[
  {
    "path": ".appveyor.yml",
    "content": "build: false\nversion: dev-{build}\nclone_folder: C:\\projects\\yii2\n\nenvironment:\n    matrix:\n      - php_ver: 7.4.0\n\ncache:\n    - '%APPDATA%\\Composer'\n    - '%LOCALAPPDATA%\\Composer'\n    - C:\\tools\\php -> .appveyor.yml\n    - C:\\tools\\composer.phar -> .appveyor.yml\n\ninit:\n    - SET PATH=C:\\tools\\php;%PATH%\n\ninstall:\n    - ps: Set-Service wuauserv -StartupType Manual\n    - IF NOT EXIST C:\\tools\\php (choco install --yes --allow-empty-checksums php --version %php_ver% --params '/InstallDir:C:\\tools\\php')\n    - cd C:\\tools\\php\n    - copy php.ini-production php.ini\n    - echo date.timezone=\"UTC\" >> php.ini\n    - echo memory_limit=512M >> php.ini\n    - echo extension_dir=ext >> php.ini\n    - echo extension=php_curl.dll >> php.ini\n    - echo extension=php_fileinfo.dll >> php.ini\n    - echo extension=php_gd2.dll >> php.ini\n    - echo extension=php_intl.dll >> php.ini\n    - echo extension=php_mbstring.dll >> php.ini\n    - echo extension=php_openssl.dll >> php.ini\n    - echo extension=php_pdo_sqlite.dll >> php.ini\n    - IF NOT EXIST C:\\tools\\composer.phar (cd C:\\tools && appveyor DownloadFile https://getcomposer.org/download/2.6.3/composer.phar)\n\nbefore_test:\n    - cd C:\\projects\\yii2\n    - php C:\\tools\\composer.phar update --no-interaction --no-progress --prefer-stable --no-ansi\n\ntest_script:\n    - cd C:\\projects\\yii2\n    - vendor\\bin\\phpunit --exclude-group mssql,mysql,pgsql,sqlite,db,oci,wincache,cubrid\n"
  },
  {
    "path": ".codecov.yml",
    "content": "ignore:\n  - \"framework/classes.php\"\n  - \"framework/views/messageConfig.php\"\n"
  },
  {
    "path": ".dockerignore",
    "content": ".git\nvendor\ndocs"
  },
  {
    "path": ".editorconfig",
    "content": "# editorconfig.org\n\nroot = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\nindent_style = space\nindent_size = 4\ntrim_trailing_whitespace = true\n\n[*.md]\ntrim_trailing_whitespace = false\n\n[*.yml]\nindent_size = 2\n"
  },
  {
    "path": ".git-blame-ignore-revs",
    "content": "# Reformat code to be PSR-2 compatible\nb5f8a4dc22d5f8188405a2099d85fc154226c9b2\n# Added php-cs-fixer coding standards validation to Travis CI\nba0ab403b52124c941dbeb46fbd9efdc12252a5d\n# Coding style fixes\n9d327baa8b2c80b53d4d405678f03e6b89ff6e38\n# Add visibility for all class elements\nc82da8dc829d557e82b12edbed9e37003dfcc9a3\n# Fix codestyle in build and tests\n909396074eef92d62dd34b4709bb7351e722bec3\n# Add void return to method in tests\nd71f7309aeec1ac0fef223840cb65bbbf96f1f99\n# Use `::class` instead of `::className()` in tests\nc960f93dfeefe760eb44bcdfa4463dcc7b29cc43\n# Replace deprecated PHPUnit mock builder `setMethods()` usage with `createPartialMock()`, `onlyMethods()` and `addMethods()` methods\n80545100b3f40423118b5cd413fcdb9c7dd7fa5e\n# Make test data providers static and declare array return types\nda20adc82aef3eb5ae0bad3889fe6695e6424f06\n# Fix codestyle in `tests`\n2f8e62d6b64324099b44cbac69395fef0e53b13e\n# Short array syntax\n1f6a8230732d829cdf2f3ca6755e4ac32f2c6f4f\n# CS fixes.\n7a7d2a9c06c099de8064729ca3fc95fb24241b75\n# Fixed error PHPCS latest version in `PHP 8.5`\nf66d5c8864712aef233b8131c2f99721101f61d0\n"
  },
  {
    "path": ".gitattributes",
    "content": "# Autodetect text files\n* text=auto eol=lf\n\n# ...Unless the name matches the following overriding patterns\n\n# Definitively text files\n*.php  text\n*.css  text\n*.js   text\n*.txt  text\n*.md   text\n*.xml  text\n*.json text\n*.bat  text\n*.sql  text\n*.yml  text\n\n# Ensure those won't be messed up with\n*.png  binary\n*.jpg  binary\n*.gif  binary\n*.ttf  binary\n\n# Ignore some meta files when creating an archive of this repository\n# We do not ignore any content, because this repo represents the\n# `yiisoft/yii2-dev` package, which is expected to ship all tests and docs.\n/.appveyor.yml              export-ignore\n/.github                    export-ignore\n/.editorconfig              export-ignore\n/.git-blame-ignore-revs     export-ignore\n/.gitattributes             export-ignore\n/.gitignore                 export-ignore\n\n# Avoid merge conflicts in CHANGELOG\n# https://about.gitlab.com/2015/02/10/gitlab-reduced-merge-conflicts-by-90-percent-with-changelog-placeholders/\n/framework/CHANGELOG.md\t\tmerge=union\n\n"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "content": "Contributing to Yii 2\n=====================\n\n- [Report an issue](../docs/internals/report-an-issue.md)\n- [Translate documentation or messages](../docs/internals/translation-workflow.md)\n- [Give us feedback or start a design discussion](https://www.yiiframework.com/forum/index.php/forum/42-general-discussions-for-yii-20/)\n- [Contribute to the core code or fix bugs](../docs/internals/git-workflow.md)\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\nopen_collective: yiisoft\ngithub: [yiisoft]\ntidelift: \"packagist/yiisoft/yii2\"\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "content": "<!--\nPlease use this issue tracker for bugs and feature requests only. In case you need support please use one of\nYii communities listed at https://github.com/yiisoft/yii2/wiki/communities\n-->\n\n### What steps will reproduce the problem?\n\n### What is the expected result?\n\n### What do you get instead?\n\n\n### Additional info\n\n| Q                | A\n| ---------------- | ---\n| Yii version      | 2.0.?\n| PHP version      | \n| Operating system |\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "| Q             | A\n| ------------- | ---\n| Is bugfix?    | ✔️/❌\n| New feature?  | ✔️/❌\n| Breaks BC?    | ✔️/❌\n| Fixed issues  | <!-- comma-separated list of tickets # fixed by the PR, if any -->\n"
  },
  {
    "path": ".github/SECURITY.md",
    "content": "# Security Policy\n\nPlease use the [security issue form](https://www.yiiframework.com/security) to report to us any security issue you find in Yii.\nDO NOT use the issue tracker or discuss it in the public forum as it will cause more damage than help.\n\nPlease note that as a non-commercial OpenSource project we are not able to pay bounties at the moment.\n"
  },
  {
    "path": ".github/actions/php-setup/action.yml",
    "content": "---\nname: PHP setup with composer.\ndescription: Setup PHP environment with composer and install dependencies.\n\ninputs:\n  composer-command:\n    description: Composer command (install or update) to run.\n    default: update\n    required: false\n    type: string\n  composer-flags:\n    description: Additional composer flags\n    default: >-\n      --prefer-dist\n      --no-interaction\n      --no-progress\n      --optimize-autoloader\n      --ansi\n    required: false\n    type: string\n  composer-version:\n    description: Composer version to use.\n    default:\n    required: false\n    type: string\n  coverage-driver:\n    description: Code coverage driver to use (pcov, xdebug).\n    default: none\n    required: false\n    type: string\n  extensions:\n    description: List of extensions to PHP.\n    default:\n    required: false\n    type: string\n  ignore-platform-reqs:\n    description: Whether to add --ignore-platform-reqs to composer command.\n    default: false\n    required: false\n    type: boolean\n  ini-values:\n    description: Initial values for PHP configuration.\n    default: date.timezone='UTC'\n    required: false\n    type: string\n  php-version:\n    description: PHP versions as a JSON array string '[\"8.4\"]'.\n    default: '[\"7.4\",\"8.0\",\"8.1\",\"8.2\",\"8.3\",\"8.4\"]'\n    required: false\n    type: string\n  tools:\n    description: Tools to test, separated by comma.\n    default: pie\n    required: false\n    type: string\n\nruns:\n  using: composite\n  steps:\n    - name: Install PHP\n      uses: shivammathur/setup-php@v2\n      with:\n        coverage: ${{ inputs.coverage-driver }}\n        extensions: ${{ inputs.extensions }}\n        ini-values: ${{ inputs.ini-values }}\n        php-version: ${{ inputs.php-version }}\n        tools: ${{ inputs.tools }}\n\n    - name: Update composer.\n      shell: bash\n      run: composer self-update ${{ inputs.composer-version }}\n\n    - name: Install dependencies with composer.\n      shell: bash\n      run: >-\n        composer\n        ${{ inputs.composer-command }}\n        ${{ inputs.composer-flags }}\n        ${{\n          inputs.ignore-platform-reqs == 'true'\n          && '--ignore-platform-reqs'\n          || ''\n        }}\n"
  },
  {
    "path": ".github/actions/phpunit/action.yml",
    "content": "---\nname: PHPUnit Test Runner.\ndescription: Run PHPUnit tests with coverage and configurable options.\n\ninputs:\n  additional-args:\n    description: Additional PHPUnit arguments.\n    default: \"--log-junit junit.xml --verbose\"\n    required: false\n    type: string\n  configuration:\n    description: PHPUnit configuration file.\n    default: \"\"\n    required: false\n    type: string\n  coverage-driver:\n    description: Code coverage driver to use (pcov, xdebug, none).\n    default: none\n    required: false\n    type: string\n  coverage-file:\n    description: Coverage output file name.\n    default: coverage.xml\n    required: false\n    type: string\n  coverage-format:\n    description: Coverage report format (clover, html, xml).\n    default: clover\n    required: false\n    type: string\n  coverage-token:\n    description: Codecov token for uploading coverage.\n    default: \"\"\n    required: false\n    type: string\n  debug:\n    description: Display warnings in phpunit.\n    default: \"\"\n    required: false\n    type: string\n  exclude-group:\n    description: Exclude group from phpunit.\n    default: \"\"\n    required: false\n    type: string\n  group:\n    description: Include specific group in phpunit.\n    default: \"\"\n    required: false\n    type: string\n  path:\n    description: Path to PHPUnit executable.\n    default: vendor/bin/phpunit\n    required: false\n    type: string\n  test-suite:\n    description: Specific test suite to run.\n    default: \"\"\n    required: false\n    type: string\n\nruns:\n  using: composite\n  steps:\n    - name: Build PHPUnit command.\n      id: build-cmd\n      shell: bash\n      run: |\n        PATH_INPUT=\"${{ inputs.path }}\"\n        CONFIG_INPUT=\"${{ inputs.configuration }}\"\n        SUITE_INPUT=\"${{ inputs.test-suite }}\"\n        GROUP_INPUT=\"${{ inputs.group }}\"\n        EXCLUDE_GROUP_INPUT=\"${{ inputs.exclude-group }}\"\n        DEBUG_INPUT=\"${{ inputs.debug }}\"\n        ADDITIONAL_ARGS=\"${{ inputs.additional-args }}\"\n        COVERAGE_DRIVER=\"${{ inputs.coverage-driver }}\"\n        COVERAGE_FORMAT=\"${{ inputs.coverage-format }}\"\n        COVERAGE_FILE=\"${{ inputs.coverage-file }}\"\n\n        PHPUNIT_CMD=\"$PATH_INPUT --colors=always\"\n\n        add_param() {\n          if [ -n \"$2\" ]; then\n            PHPUNIT_CMD=\"$PHPUNIT_CMD $1 $2\"\n          fi\n        }\n\n        if [ -n \"$COVERAGE_DRIVER\" ] && [ \"$COVERAGE_DRIVER\" != \"none\" ]; then\n          PHPUNIT_CMD=\"$PHPUNIT_CMD --coverage-$COVERAGE_FORMAT=$COVERAGE_FILE\"\n        fi\n\n        add_param \"--configuration\" \"$CONFIG_INPUT\"\n        add_param \"--testsuite\" \"$SUITE_INPUT\"\n        add_param \"--group\" \"$GROUP_INPUT\"\n        add_param \"--exclude-group\" \"$EXCLUDE_GROUP_INPUT\"\n\n        if [ -n \"$DEBUG_INPUT\" ]; then\n          PHPUNIT_CMD=\"$PHPUNIT_CMD $DEBUG_INPUT\"\n        fi\n\n        if [ -n \"$ADDITIONAL_ARGS\" ]; then\n          PHPUNIT_CMD=\"$PHPUNIT_CMD $ADDITIONAL_ARGS\"\n        fi\n\n        echo \"command=$PHPUNIT_CMD\" >> $GITHUB_OUTPUT\n        echo \"PHPUnit command: $PHPUNIT_CMD\"\n\n    - name: Run PHPUnit tests on Linux.\n      shell: bash\n      if: runner.os != 'Windows'\n      run: ${{ steps.build-cmd.outputs.command }}\n\n    - name: Run PHPUnit tests on Windows.\n      shell: pwsh\n      if: runner.os == 'Windows'\n      run: Invoke-Expression \"${{ steps.build-cmd.outputs.command }}\"\n\n    - name: Upload test results to Codecov.\n      if: ${{ !cancelled() && inputs.coverage-driver != 'none' }}\n      uses: codecov/test-results-action@v1\n      with:\n        token: ${{ inputs.coverage-token }}\n\n    - name: Upload coverage to Codecov.\n      if: ${{ !cancelled() && inputs.coverage-driver != 'none' }}\n      uses: codecov/codecov-action@v5\n      with:\n        files: ./${{ inputs.coverage-file }}\n        token: ${{ inputs.coverage-token }}\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "---\nname: build\n\npermissions:\n  contents: read\n  pull-requests: write\n\non:\n  pull_request: &ignore-paths\n    paths-ignore:\n      - \".appveyor.yml\"\n      - \".dockerignore\"\n      - \".editorconfig\"\n      - \".git-blame-ignore-revs\"\n      - \".gitattributes\"\n      - \".github/CONTRIBUTING.md\"\n      - \".github/FUNDING.yml\"\n      - \".github/ISSUE_TEMPLATE.md\"\n      - \".github/PULL_REQUEST_TEMPLATE.md\"\n      - \".github/SECURITY.md\"\n      - \".gitignore\"\n      - \".gitlab-ci.yml\"\n      - \"code-of-conduct.md\"\n      - \"docs/**\"\n      - \"framework/.gitignore\"\n      - \"framework/.phpstorm.meta.php\"\n      - \"framework/CHANGELOG.md\"\n      - \"framework/LICENSE.md\"\n      - \"framework/README.md\"\n      - \"framework/UPGRADE.md\"\n      - \"eslint.config.js\"\n      - \"LICENSE.md\"\n      - \"README.md\"\n      - \"ROADMAP.md\"\n\n  push: *ignore-paths\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\nenv:\n  PHP_EXTENSIONS: curl, dom, imagick, intl, mbstring, mcrypt, memcached\n  PHP_INI_VALUES: apc.enabled=1,apc.shm_size=32M,apc.enable_cli=1, date.timezone='UTC'\n  PHPUNIT_EXCLUDE_GROUP: db,wincache\n  XDEBUG_MODE: coverage\n\njobs:\n  phpunit:\n    name: PHP ${{ matrix.php }}\n\n    env:\n      COVERAGE_DRIVER: ${{ matrix.php < 8.1 && 'xdebug' || 'pcov' }}\n      IGNORE_PLATFORM_REQS: false\n\n    runs-on: ubuntu-22.04\n\n    services: &memcached-service\n      memcached:\n        image: memcached:latest\n        ports:\n          - 11211:11211\n        options: >-\n          --health-cmd \"timeout 5 bash -c 'cat < /dev/null > /dev/tcp/127.0.0.1/11211'\"\n          --health-interval 10s\n          --health-retries 5\n          --health-timeout 5s\n\n    strategy:\n      fail-fast: false\n      matrix:\n        php: [7.4, 8.0, 8.1, 8.2, 8.3, 8.4, 8.5]\n\n    steps: &build-steps\n      - name: Checkout.\n        uses: actions/checkout@v5\n\n      - name: Generate french locale.\n        run: sudo locale-gen fr_FR.UTF-8\n\n      - name: Setup PHP with Composer.\n        uses: ./.github/actions/php-setup\n        with:\n          coverage-driver: ${{ env.COVERAGE_DRIVER }}\n          extensions: ${{ matrix.php < 8.0 && 'apc' || 'apcu' }}, ${{ env.PHP_EXTENSIONS }}\n          ignore-platform-reqs: ${{ env.IGNORE_PLATFORM_REQS }}\n          ini-values: ${{ env.PHP_INI_VALUES }}, session.save_path=\"${{ runner.temp }}\"\n          php-version: ${{ matrix.php }}\n\n      - name: Run PHPUnit tests.\n        uses: ./.github/actions/phpunit\n        with:\n          coverage-driver: ${{ env.COVERAGE_DRIVER }}\n          coverage-token: ${{ secrets.CODECOV_TOKEN }}\n          exclude-group: ${{ env.PHPUNIT_EXCLUDE_GROUP }}\n\n  phpunit-dev:\n    name: PHP ${{ matrix.php }}\n\n    env:\n      COVERAGE_DRIVER: none\n      IGNORE_PLATFORM_REQS: true\n\n    runs-on: ubuntu-22.04\n\n    services: *memcached-service\n\n    strategy:\n      fail-fast: false\n      matrix:\n        php: [8.6]\n\n    steps: *build-steps\n"
  },
  {
    "path": ".github/workflows/ci-mariadb.yml",
    "content": "name: ci-mariadb\n\npermissions:\n  contents: read\n  pull-requests: write\n\non:\n  pull_request: &ignore-paths\n    paths-ignore:\n      - \".appveyor.yml\"\n      - \".dockerignore\"\n      - \".editorconfig\"\n      - \".git-blame-ignore-revs\"\n      - \".gitattributes\"\n      - \".github/CONTRIBUTING.md\"\n      - \".github/FUNDING.yml\"\n      - \".github/ISSUE_TEMPLATE.md\"\n      - \".github/PULL_REQUEST_TEMPLATE.md\"\n      - \".github/SECURITY.md\"\n      - \".gitignore\"\n      - \".gitlab-ci.yml\"\n      - \"code-of-conduct.md\"\n      - \"docs/**\"\n      - \"framework/.gitignore\"\n      - \"framework/.phpstorm.meta.php\"\n      - \"framework/CHANGELOG.md\"\n      - \"framework/LICENSE.md\"\n      - \"framework/README.md\"\n      - \"framework/UPGRADE.md\"\n      - \"eslint.config.js\"\n      - \"LICENSE.md\"\n      - \"README.md\"\n      - \"ROADMAP.md\"\n\n  push: *ignore-paths\n\njobs:\n  tests:\n    name: MariaDB tests with coverage\n    uses: yiisoft/yii2-actions/.github/workflows/database.yml@master\n    with:\n      concurrency-group: mariadb-${{ github.ref }}\n      coverage-driver: xdebug\n      database-env: '{\"MARIADB_ROOT_PASSWORD\":\"root\",\"MARIADB_DATABASE\":\"yiitest\"}'\n      database-health-cmd: mariadb-admin ping\n      database-image: mariadb\n      database-port: \"3306\"\n      database-type: mysql\n      database-versions: '[\"10.4\",\"latest\"]'\n      extensions: curl, intl, pdo, pdo_mysql\n      os: '[\"ubuntu-22.04\"]'\n      php-version: '[\"7.4\",\"8.5\"]'\n      phpunit-group: mysql\n    secrets:\n      CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}\n\n  tests-dev:\n    name: MariaDB tests\n    uses: yiisoft/yii2-actions/.github/workflows/database.yml@master\n    with:\n      concurrency-group: mariadb-dev-${{ github.ref }}\n      coverage-driver: none\n      database-env: '{\"MARIADB_ROOT_PASSWORD\":\"root\",\"MARIADB_DATABASE\":\"yiitest\"}'\n      database-health-cmd: mariadb-admin ping\n      database-image: mariadb\n      database-port: \"3306\"\n      database-type: mysql\n      database-versions: '[\"latest\"]'\n      extensions: curl, intl, pdo, pdo_mysql\n      os: '[\"ubuntu-22.04\"]'\n      php-version: '[\"8.0\",\"8.1\",\"8.2\",\"8.3\",\"8.4\"]'\n      phpunit-group: mysql\n"
  },
  {
    "path": ".github/workflows/ci-mssql.yml",
    "content": "name: ci-mssql\n\npermissions:\n  contents: read\n  pull-requests: write\n\non:\n  pull_request: &ignore-paths\n    paths-ignore:\n      - \".appveyor.yml\"\n      - \".dockerignore\"\n      - \".editorconfig\"\n      - \".git-blame-ignore-revs\"\n      - \".gitattributes\"\n      - \".github/CONTRIBUTING.md\"\n      - \".github/FUNDING.yml\"\n      - \".github/ISSUE_TEMPLATE.md\"\n      - \".github/PULL_REQUEST_TEMPLATE.md\"\n      - \".github/SECURITY.md\"\n      - \".gitignore\"\n      - \".gitlab-ci.yml\"\n      - \"code-of-conduct.md\"\n      - \"docs/**\"\n      - \"framework/.gitignore\"\n      - \"framework/.phpstorm.meta.php\"\n      - \"framework/CHANGELOG.md\"\n      - \"framework/LICENSE.md\"\n      - \"framework/README.md\"\n      - \"framework/UPGRADE.md\"\n      - \"eslint.config.js\"\n      - \"LICENSE.md\"\n      - \"README.md\"\n      - \"ROADMAP.md\"\n\n  push: *ignore-paths\n\njobs:\n  tests:\n    name: MSSQL tests with coverage\n    uses: yiisoft/yii2-actions/.github/workflows/database.yml@master\n    with:\n      concurrency-group: mssql-${{ github.ref }}\n      coverage-driver: xdebug\n      database-env: '{\"SA_PASSWORD\":\"YourStrong!Passw0rd\",\"ACCEPT_EULA\":\"Y\",\"MSSQL_PID\":\"Developer\"}'\n      database-health-cmd: /opt/mssql-tools18/bin/sqlcmd -C -S localhost -U SA -P 'YourStrong!Passw0rd' -Q 'SELECT 1'\n      database-image: mcr.microsoft.com/mssql/server\n      database-port: \"1433\"\n      database-type: mssql\n      database-versions: '[\"2019-latest\",\"2022-latest\"]'\n      extensions: curl, intl, pdo, pdo_sqlsrv\n      hook: |\n        sudo ACCEPT_EULA=Y apt-get install -y msodbcsql18\n        docker exec -i database /opt/mssql-tools18/bin/sqlcmd -C -S localhost -U SA -P 'YourStrong!Passw0rd' -Q \"IF DB_ID(N'yiitest') IS NULL CREATE DATABASE yiitest;\"\n      os: '[\"ubuntu-22.04\"]'\n      php-version: '[\"7.4\",\"8.5\"]'\n      phpunit-group: mssql\n    secrets:\n      CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}\n\n  tests-dev:\n    name: MSSQL tests\n    uses: yiisoft/yii2-actions/.github/workflows/database.yml@master\n    with:\n      concurrency-group: mssql-dev-${{ github.ref }}\n      coverage-driver: none\n      database-env: '{\"SA_PASSWORD\":\"YourStrong!Passw0rd\",\"ACCEPT_EULA\":\"Y\",\"MSSQL_PID\":\"Developer\"}'\n      database-health-cmd: /opt/mssql-tools18/bin/sqlcmd -C -S localhost -U SA -P 'YourStrong!Passw0rd' -Q 'SELECT 1'\n      database-image: mcr.microsoft.com/mssql/server\n      database-port: \"1433\"\n      database-type: mssql\n      database-versions: '[\"2022-latest\"]'\n      extensions: curl, intl, pdo, pdo_sqlsrv\n      hook: |\n        sudo ACCEPT_EULA=Y apt-get install -y msodbcsql18\n        docker exec -i database /opt/mssql-tools18/bin/sqlcmd -C -S localhost -U SA -P 'YourStrong!Passw0rd' -Q \"IF DB_ID(N'yiitest') IS NULL CREATE DATABASE yiitest;\"\n      os: '[\"ubuntu-22.04\"]'\n      php-version: '[\"8.0\",\"8.1\",\"8.2\",\"8.3\",\"8.4\"]'\n      phpunit-group: mssql\n"
  },
  {
    "path": ".github/workflows/ci-mysql.yml",
    "content": "name: ci-mysql\n\npermissions:\n  contents: read\n  pull-requests: write\n\non:\n  pull_request: &ignore-paths\n    paths-ignore:\n      - \".appveyor.yml\"\n      - \".dockerignore\"\n      - \".editorconfig\"\n      - \".git-blame-ignore-revs\"\n      - \".gitattributes\"\n      - \".github/CONTRIBUTING.md\"\n      - \".github/FUNDING.yml\"\n      - \".github/ISSUE_TEMPLATE.md\"\n      - \".github/PULL_REQUEST_TEMPLATE.md\"\n      - \".github/SECURITY.md\"\n      - \".gitignore\"\n      - \".gitlab-ci.yml\"\n      - \"code-of-conduct.md\"\n      - \"docs/**\"\n      - \"framework/.gitignore\"\n      - \"framework/.phpstorm.meta.php\"\n      - \"framework/CHANGELOG.md\"\n      - \"framework/LICENSE.md\"\n      - \"framework/README.md\"\n      - \"framework/UPGRADE.md\"\n      - \"eslint.config.js\"\n      - \"LICENSE.md\"\n      - \"README.md\"\n      - \"ROADMAP.md\"\n\n  push: *ignore-paths\n\njobs:\n  tests:\n    name: MySQL tests with coverage\n    uses: yiisoft/yii2-actions/.github/workflows/database.yml@master\n    with:\n      concurrency-group: mysql-${{ github.ref }}\n      coverage-driver: xdebug\n      database-env: '{\"MYSQL_ROOT_PASSWORD\":\"root\",\"MYSQL_DATABASE\":\"yiitest\"}'\n      database-health-cmd: mysqladmin ping\n      database-image: mysql\n      database-port: \"3306\"\n      database-type: mysql\n      database-versions: '[\"5.7\",\"latest\"]'\n      extensions: curl, intl, pdo, pdo_mysql\n      os: '[\"ubuntu-22.04\"]'\n      php-version: '[\"7.4\",\"8.5\"]'\n      phpunit-group: mysql\n    secrets:\n      CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}\n\n  tests-dev:\n    name: MySQL tests\n    uses: yiisoft/yii2-actions/.github/workflows/database.yml@master\n    with:\n      concurrency-group: mysql-dev-${{ github.ref }}\n      coverage-driver: none\n      database-env: '{\"MYSQL_ROOT_PASSWORD\":\"root\",\"MYSQL_DATABASE\":\"yiitest\"}'\n      database-health-cmd: mysqladmin ping\n      database-image: mysql\n      database-port: \"3306\"\n      database-type: mysql\n      database-versions: '[\"latest\"]'\n      extensions: curl, intl, pdo, pdo_mysql\n      os: '[\"ubuntu-22.04\"]'\n      php-version: '[\"8.0\",\"8.1\",\"8.2\",\"8.3\",\"8.4\"]'\n      phpunit-group: mysql\n"
  },
  {
    "path": ".github/workflows/ci-node.yml",
    "content": "name: build-node\n\non:\n  pull_request: &ignore-paths\n    paths-ignore:\n      - \".appveyor.yml\"\n      - \".dockerignore\"\n      - \".editorconfig\"\n      - \".git-blame-ignore-revs\"\n      - \".gitattributes\"\n      - \".github/CONTRIBUTING.md\"\n      - \".github/FUNDING.yml\"\n      - \".github/ISSUE_TEMPLATE.md\"\n      - \".github/PULL_REQUEST_TEMPLATE.md\"\n      - \".github/SECURITY.md\"\n      - \".gitignore\"\n      - \".gitlab-ci.yml\"\n      - \"code-of-conduct.md\"\n      - \"docs/**\"\n      - \"framework/.gitignore\"\n      - \"framework/.phpstorm.meta.php\"\n      - \"framework/CHANGELOG.md\"\n      - \"framework/LICENSE.md\"\n      - \"framework/README.md\"\n      - \"framework/UPGRADE.md\"\n      - \"LICENSE.md\"\n      - \"README.md\"\n      - \"ROADMAP.md\"\n\n  push: *ignore-paths\n\nenv:\n  DEFAULT_COMPOSER_FLAGS: \"--prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi\"\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  test:\n    name: NPM 10 on ubuntu-22.04\n\n    runs-on: ubuntu-22.04\n\n    steps:\n      - name: Monitor action permissions.\n        if: runner.os != 'Windows'\n        uses: GitHubSecurityLab/actions-permissions/monitor@v1\n\n      - name: Checkout.\n        uses: actions/checkout@v5\n\n      - name: Install dependencies.\n        run: composer update $DEFAULT_COMPOSER_FLAGS\n\n      - name: Install JQuery `3.6.*@stable` for tests.\n        run: composer require \"bower-asset/jquery:3.6.*@stable\"\n\n      - name: Install node.js.\n        uses: actions/setup-node@v4\n        with:\n          node-version: 20\n\n      - name: Tests.\n        run: |\n          npm install\n          npm run lint\n          npm test\n"
  },
  {
    "path": ".github/workflows/ci-oracle.yml",
    "content": "name: ci-oracle\n\npermissions:\n  contents: read\n  pull-requests: write\n\non:\n  pull_request: &ignore-paths\n    paths-ignore:\n      - \".appveyor.yml\"\n      - \".dockerignore\"\n      - \".editorconfig\"\n      - \".git-blame-ignore-revs\"\n      - \".gitattributes\"\n      - \".github/CONTRIBUTING.md\"\n      - \".github/FUNDING.yml\"\n      - \".github/ISSUE_TEMPLATE.md\"\n      - \".github/PULL_REQUEST_TEMPLATE.md\"\n      - \".github/SECURITY.md\"\n      - \".gitignore\"\n      - \".gitlab-ci.yml\"\n      - \"code-of-conduct.md\"\n      - \"docs/**\"\n      - \"framework/.gitignore\"\n      - \"framework/.phpstorm.meta.php\"\n      - \"framework/CHANGELOG.md\"\n      - \"framework/LICENSE.md\"\n      - \"framework/README.md\"\n      - \"framework/UPGRADE.md\"\n      - \"eslint.config.js\"\n      - \"LICENSE.md\"\n      - \"README.md\"\n      - \"ROADMAP.md\"\n\n  push: *ignore-paths\n\njobs:\n  tests:\n    name: Oracle tests with coverage\n    uses: yiisoft/yii2-actions/.github/workflows/database.yml@master\n    with:\n      concurrency-group: oracle-${{ github.ref }}\n      coverage-driver: xdebug\n      database-env: '{\"ORACLE_PASSWORD\":\"oracle\"}'\n      database-health-cmd: \"healthcheck.sh\"\n      database-health-retries: 10\n      database-image: gvenzl/oracle-free\n      database-port: \"1521\"\n      database-type: oracle\n      database-versions: '[\"slim-faststart\"]'\n      extensions: curl, intl, oci8, pdo, pdo_oci\n      hook: docker exec -i database sqlplus -s system/oracle@//localhost/FREE < tests/data/oci/optimize_for_tests.sql\n      os: '[\"ubuntu-22.04\"]'\n      php-version: '[\"7.4\",\"8.5\"]'\n      phpunit-group: oci\n    secrets:\n      CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/ci-pgsql.yml",
    "content": "name: ci-pgsql\n\npermissions:\n  contents: read\n  pull-requests: write\n\non:\n  pull_request: &ignore-paths\n    paths-ignore:\n      - \".appveyor.yml\"\n      - \".dockerignore\"\n      - \".editorconfig\"\n      - \".git-blame-ignore-revs\"\n      - \".gitattributes\"\n      - \".github/CONTRIBUTING.md\"\n      - \".github/FUNDING.yml\"\n      - \".github/ISSUE_TEMPLATE.md\"\n      - \".github/PULL_REQUEST_TEMPLATE.md\"\n      - \".github/SECURITY.md\"\n      - \".gitignore\"\n      - \".gitlab-ci.yml\"\n      - \"code-of-conduct.md\"\n      - \"docs/**\"\n      - \"framework/.gitignore\"\n      - \"framework/.phpstorm.meta.php\"\n      - \"framework/CHANGELOG.md\"\n      - \"framework/LICENSE.md\"\n      - \"framework/README.md\"\n      - \"framework/UPGRADE.md\"\n      - \"eslint.config.js\"\n      - \"LICENSE.md\"\n      - \"README.md\"\n      - \"ROADMAP.md\"\n\n  push: *ignore-paths\n\njobs:\n  tests:\n    name: PostgreSQL tests with coverage\n    uses: yiisoft/yii2-actions/.github/workflows/database.yml@master\n    with:\n      concurrency-group: pgsql-${{ github.ref }}\n      coverage-driver: xdebug\n      database-env: '{\"POSTGRES_USER\":\"postgres\",\"POSTGRES_PASSWORD\":\"postgres\",\"POSTGRES_DB\":\"yiitest\"}'\n      database-health-cmd: pg_isready\n      database-image: postgres\n      database-port: \"5432\"\n      database-type: pgsql\n      database-versions: '[\"10\",\"11\",\"12\",\"13\",\"14\",\"15\",\"16\",\"17\",\"latest\"]'\n      extensions: curl, intl, pdo, pdo_pgsql\n      os: '[\"ubuntu-22.04\"]'\n      php-version: '[\"7.4\",\"8.5\"]'\n      phpunit-group: pgsql\n    secrets:\n      CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}\n\n  tests-dev:\n    name: PostgreSQL tests\n    uses: yiisoft/yii2-actions/.github/workflows/database.yml@master\n    with:\n      concurrency-group: pgsql-dev-${{ github.ref }}\n      coverage-driver: none\n      database-env: '{\"POSTGRES_USER\":\"postgres\",\"POSTGRES_PASSWORD\":\"postgres\",\"POSTGRES_DB\":\"yiitest\"}'\n      database-health-cmd: pg_isready\n      database-image: postgres\n      database-port: \"5432\"\n      database-type: pgsql\n      database-versions: '[\"latest\"]'\n      extensions: curl, intl, pdo, pdo_pgsql\n      os: '[\"ubuntu-22.04\"]'\n      php-version: '[\"8.0\",\"8.1\",\"8.2\",\"8.3\",\"8.4\"]'\n      phpunit-group: pgsql\n"
  },
  {
    "path": ".github/workflows/ci-sqlite.yml",
    "content": "name: ci-sqlite\n\npermissions:\n  contents: read\n  pull-requests: write\n\non:\n  pull_request: &ignore-paths\n    paths-ignore:\n      - \".appveyor.yml\"\n      - \".dockerignore\"\n      - \".editorconfig\"\n      - \".git-blame-ignore-revs\"\n      - \".gitattributes\"\n      - \".github/CONTRIBUTING.md\"\n      - \".github/FUNDING.yml\"\n      - \".github/ISSUE_TEMPLATE.md\"\n      - \".github/PULL_REQUEST_TEMPLATE.md\"\n      - \".github/SECURITY.md\"\n      - \".gitignore\"\n      - \".gitlab-ci.yml\"\n      - \"code-of-conduct.md\"\n      - \"docs/**\"\n      - \"framework/.gitignore\"\n      - \"framework/.phpstorm.meta.php\"\n      - \"framework/CHANGELOG.md\"\n      - \"framework/LICENSE.md\"\n      - \"framework/README.md\"\n      - \"framework/UPGRADE.md\"\n      - \"eslint.config.js\"\n      - \"LICENSE.md\"\n      - \"README.md\"\n      - \"ROADMAP.md\"\n\n  push: *ignore-paths\n\njobs:\n  tests:\n    name: SQLite tests with coverage\n    uses: yiisoft/yii2-actions/.github/workflows/phpunit.yml@master\n    with:\n      concurrency-group: sqlite-${{ github.ref }}\n      coverage-driver: xdebug\n      enable-concurrency: true\n      extensions: curl, intl, pdo, pdo_sqlite\n      os: '[\"ubuntu-22.04\",\"windows-latest\"]'\n      php-version: '[\"7.4\",\"8.5\"]'\n      phpunit-group: sqlite\n    secrets:\n      CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}\n\n  tests-dev:\n    name: SQLite tests\n    uses: yiisoft/yii2-actions/.github/workflows/phpunit.yml@master\n    with:\n      concurrency-group: sqlite-dev-${{ github.ref }}\n      coverage-driver: none\n      enable-concurrency: true\n      extensions: curl, intl, pdo, pdo_sqlite\n      os: '[\"ubuntu-22.04\"]'\n      php-version: '[\"8.0\",\"8.1\",\"8.2\",\"8.3\",\"8.4\"]'\n      phpunit-group: sqlite\n"
  },
  {
    "path": ".github/workflows/linter.yaml",
    "content": "name: linter\n\npermissions:\n  contents: read\n  pull-requests: write\n\non:\n  pull_request: &ignore-paths\n    paths-ignore:\n      - \".appveyor.yml\"\n      - \".dockerignore\"\n      - \".editorconfig\"\n      - \".git-blame-ignore-revs\"\n      - \".gitattributes\"\n      - \".github/CONTRIBUTING.md\"\n      - \".github/FUNDING.yml\"\n      - \".github/ISSUE_TEMPLATE.md\"\n      - \".github/PULL_REQUEST_TEMPLATE.md\"\n      - \".github/SECURITY.md\"\n      - \".gitignore\"\n      - \".gitlab-ci.yml\"\n      - \"code-of-conduct.md\"\n      - \"docs/**\"\n      - \"framework/.gitignore\"\n      - \"framework/.phpstorm.meta.php\"\n      - \"framework/CHANGELOG.md\"\n      - \"framework/LICENSE.md\"\n      - \"framework/README.md\"\n      - \"framework/UPGRADE.md\"\n      - \"eslint.config.js\"\n      - \"LICENSE.md\"\n      - \"README.md\"\n      - \"ROADMAP.md\"\n\n  push: *ignore-paths\n\njobs:\n  phpcs:\n    uses: yiisoft/yii2-actions/.github/workflows/linter.yml@master\n    with:\n      php-version: '[\"7.4\", \"8.5\"]'\n"
  },
  {
    "path": ".github/workflows/static.yml",
    "content": "name: static analysis\n\npermissions:\n  contents: read\n  pull-requests: write\n\non:\n  pull_request: &ignore-paths\n    paths-ignore:\n      - \".appveyor.yml\"\n      - \".dockerignore\"\n      - \".editorconfig\"\n      - \".git-blame-ignore-revs\"\n      - \".gitattributes\"\n      - \".github/CONTRIBUTING.md\"\n      - \".github/FUNDING.yml\"\n      - \".github/ISSUE_TEMPLATE.md\"\n      - \".github/PULL_REQUEST_TEMPLATE.md\"\n      - \".github/SECURITY.md\"\n      - \".gitignore\"\n      - \".gitlab-ci.yml\"\n      - \"code-of-conduct.md\"\n      - \"docs/**\"\n      - \"framework/.gitignore\"\n      - \"framework/.phpstorm.meta.php\"\n      - \"framework/CHANGELOG.md\"\n      - \"framework/LICENSE.md\"\n      - \"framework/README.md\"\n      - \"framework/UPGRADE.md\"\n      - \"eslint.config.js\"\n      - \"LICENSE.md\"\n      - \"README.md\"\n      - \"ROADMAP.md\"\n\n  push: *ignore-paths\n\njobs:\n  phpstan:\n    uses: yiisoft/yii2-actions/.github/workflows/phpstan.yml@master\n    with:\n      configuration: phpstan.dist.neon\n      concurrency-group: phpstan-${{ github.ref }}\n      extensions: apcu, curl, dom, imagick, intl, mbstring, mcrypt, memcached\n      php-version: '[\"8.5\"]'\n\n  phpstan-7x:\n    uses: yiisoft/yii2-actions/.github/workflows/phpstan.yml@master\n    with:\n      configuration: phpstan-7x.dist.neon\n      concurrency-group: phpstan7x-${{ github.ref }}\n      extensions: apc, curl, dom, imagick, intl, mbstring, mcrypt, memcached\n      php-version: '[\"7.4\"]'\n"
  },
  {
    "path": ".gitignore",
    "content": "# phpstorm project files\n.idea\n*.iml\n\n# netbeans project files\nnbproject\n\n# zend studio for eclipse project files\n.buildpath\n.project\n.settings\n\n# sublime text project / workspace files\n*.sublime-project\n*.sublime-workspace\n\n# visual studio code project files\n.vscode\n\n# windows thumbnail cache\nThumbs.db\n\n# composer vendor dir\n/vendor\n# cubrid install dir\n/cubrid\n\n# composer itself is not needed\ncomposer.phar\n\n# composer.lock in applications is ignored since it's automatically created by composer when application is installed\n/apps/*/composer.lock\n\n# Mac DS_Store Files\n.DS_Store\n\n# phpunit itself is not needed\nphpunit.phar\n# local phpunit config\n/phpunit.xml\n.phpunit.result.cache\n\n# ignore dev installed apps and extensions\n/apps\n/extensions\n/packages\n\n# NPM packages\n/node_modules\n.env\npackage-lock.json\n\n# local phpstan config\nphpstan.neon\n"
  },
  {
    "path": ".gitlab-ci.yml",
    "content": "image: docker:latest\n\nservices:\n  - docker:dind\n\nvariables:\n  DOCKER_YII2_PHP_IMAGE: yiisoftware/yii2-php:7.4-apache\n  DOCKER_MYSQL_IMAGE: percona:5.7\n  DOCKER_POSTGRES_IMAGE: postgres:9.3\n\nbefore_script:\n  - apk add --no-cache git curl docker-compose\n  - docker info\n  - cd tests\n\nstages:\n  - travis\n  - test\n  - cleanup\n\ntest:\n  stage: test\n  script:\n    - docker-compose up --build -d\n    - docker-compose run --rm php vendor/bin/phpunit -v --exclude caching,db,data --log-junit tests/_junit/test.xml\n\ncaching:\n  stage: test\n  only:\n    - tests/caching\n    - tests/full\n  script:\n    - export COMPOSE_FILE=docker-compose.yml:docker-compose.${CI_BUILD_NAME}.yml\n    - docker-compose up --build -d\n    - docker-compose run --rm php vendor/bin/phpunit -v --group caching --exclude db\n\ndb:\n  stage: test\n  only:\n    - tests/mysql\n    - tests/full\n  script:\n    - docker-compose up --build -d\n    - docker-compose run --rm php vendor/bin/phpunit -v --group db --exclude caching,mysql,pgsql,mssql,cubrid,oci\n\n\nmysql:\n  stage: test\n  only:\n    - tests/mysql\n    - tests/full\n  script:\n    - export COMPOSE_FILE=docker-compose.yml:docker-compose.${CI_BUILD_NAME}.yml\n    - docker-compose up --build -d\n    # wait for db (retry X times)\n    - docker-compose run --rm php bash -c \"while ! curl mysql:3306; do ((c++)) && ((c==30)) && break; sleep 2; done\"\n    - docker-compose run --rm php vendor/bin/phpunit -v --group mysql\n\n\npgsql:\n  stage: test\n  only:\n    - tests/pgsql\n    - tests/full\n  script:\n    - export COMPOSE_FILE=docker-compose.yml:docker-compose.${CI_BUILD_NAME}.yml\n    - docker-compose up --build -d\n    # wait for db (retry X times)\n    - docker-compose run --rm php bash -c 'while [ true ]; do curl postgres:5432; if [ $? == 52 ]; then break; fi; ((c++)) && ((c==25)) && break; sleep 2; done'\n    - docker-compose run --rm php vendor/bin/phpunit -v --group pgsql\n\n\ncubrid:\n  stage: test\n  only:\n    - tests/cubrid\n    - tests/extra\n  script:\n    - cd cubrid\n    - docker-compose up --build -d\n    # wait for db (retry X times)\n    - docker-compose run --rm php bash -c 'while [ true ]; do curl cubrid:1523; if [ $? == 56 ]; then break; fi; ((c++)) && ((c==20)) && break; sleep 3; done'\n    - sleep 5\n    - docker-compose run --rm php /project/vendor/bin/phpunit -v --group cubrid\n\n\nmssql:\n  stage: test\n  only:\n    - tests/mssql\n    - tests/extra\n  script:\n    - cd mssql\n    - docker-compose up --build -d\n    # wait for db (retry X times)\n    - docker-compose run --rm php bash -c 'while [ true ]; do curl mssql:1433; if [ $? == 52 ]; then break; fi; ((c++)) && ((c==15)) && break; sleep 5; done'\n    - sleep 3\n    # Note: Password has to be the last parameter\n    - docker-compose run --rm sqlcmd sh -c 'sqlcmd -S mssql -U sa -Q \"CREATE DATABASE yii2test\" -P Microsoft-12345'\n    - docker-compose run --rm php vendor/bin/phpunit -v --group mssql\n\n\ntravis:\n  stage: travis\n  only:\n    - travis\n  script:\n    - export COMPOSE_FILE=docker-compose.yml:docker-compose.mysql.yml:docker-compose.pgsql.yml\n    - docker-compose up --build -d\n    # wait for dbs ...\n    - sleep 10\n    - docker-compose run --rm php vendor/bin/phpunit -v --exclude mssql,cubrid,oci,wincache,cubrid\n\n"
  },
  {
    "path": ".well-known/funding-manifest-urls",
    "content": "https://www.yiiframework.com/funding.json\n"
  },
  {
    "path": "Dockerfile",
    "content": "ARG DOCKER_YII2_PHP_IMAGE\nFROM ${DOCKER_YII2_PHP_IMAGE}\n\n# Project source-code\nWORKDIR /project\nADD composer.* /project/\n# Install packages\nRUN /usr/local/bin/composer install --prefer-dist\nADD ./ /project\nENV PATH /project/vendor/bin:${PATH}\n"
  },
  {
    "path": "LICENSE.md",
    "content": "Copyright © 2008 by Yii Software (https://www.yiiframework.com/)\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions\nare met:\n\n * Redistributions of source code must retain the above copyright\n   notice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above copyright\n   notice, this list of conditions and the following disclaimer in\n   the documentation and/or other materials provided with the\n   distribution.\n * Neither the name of Yii Software nor the names of its\n   contributors may be used to endorse or promote products derived\n   from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\nFOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\nCOPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\nINCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\nBUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\nLIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\nANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n    <a href=\"https://www.yiiframework.com/\" target=\"_blank\">\n        <picture>\n            <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://www.yiiframework.com/image/design/logo/yii3_full_for_dark.svg\">\n            <source media=\"(prefers-color-scheme: light)\" srcset=\"https://www.yiiframework.com/image/design/logo/yii3_full_for_light.svg\">\n            <img src=\"https://www.yiiframework.com/image/design/logo/yii3_full_for_light.svg\" alt=\"Yii Framework\" height=\"100\">\n        </picture>\n    </a>\n</p>\n\nYii 2 is a modern framework designed to be a solid foundation for your PHP application.\n\nIt is fast, secure and efficient and works right out of the box pre-configured with reasonable defaults.\nThe framework is easy to adjust to meet your needs, because Yii has been designed to be flexible.\n\n[![Latest Stable Version](https://img.shields.io/packagist/v/yiisoft/yii2.svg?logo=packagist&style=for-the-badge&label=Stable)](https://packagist.org/packages/yiisoft/yii2)\n[![Total Downloads](https://img.shields.io/packagist/dt/yiisoft/yii2.svg?style=for-the-badge)](https://packagist.org/packages/yiisoft/yii2)\n[![Build Status](https://img.shields.io/github/actions/workflow/status/yiisoft/yii2/build.yml?style=for-the-badge&logo=github&label=Build)](https://github.com/yiisoft/yii2/actions/workflows/build.yml)\n[![Static Analysis](https://img.shields.io/github/actions/workflow/status/yiisoft/yii2/static.yml?style=for-the-badge&label=Static&logo=github)](https://github.com/yiisoft/yii2/actions/workflows/static.yml)\n[![codecov](https://img.shields.io/codecov/c/github/yiisoft/yii2.svg?style=for-the-badge&logo=codecov&logoColor=white&label=Codecov)](https://codecov.io/gh/yiisoft/yii2)\n\nInstallation\n------------\n\n> [!IMPORTANT]\n> - The minimum required [PHP](https://www.php.net/) version of Yii is PHP `7.4`.\n> - It works best with PHP `8`.\n\n- [Follow the Definitive Guide](https://www.yiiframework.com/doc-2.0/guide-start-installation.html)\nin order to get step by step instructions.\n\nDocumentation\n-------------\n\n- A [Definitive Guide](https://www.yiiframework.com/doc/guide/2.0) and \na [Class Reference](https://www.yiiframework.com/doc/api/2.0) cover every detail\nof the framework.\n- There is a [PDF version](https://www.yiiframework.com/doc/download/yii-guide-2.0-en.pdf) of the Definitive Guide\nand a [Definitive Guide Mirror](http://stuff.cebe.cc/yii2docs/) which is updated every 15 minutes.\n- For Yii 1.1 users, there is [Upgrading from Yii 1.1](https://www.yiiframework.com/doc/guide/2.0/en/intro-upgrade-from-v1)\nto get an idea of what has changed in 2.0.\n\nVersions & PHP compatibility\n----------------------------\n\n> [!NOTE]\n> See [\"Release Cycle\" at the website](https://www.yiiframework.com/release-cycle) for detailed information about supported versions.\n\nCommunity\n---------\n\n- Participate in [discussions at forums](https://www.yiiframework.com/forum/).\n- [Community Slack](https://join.slack.com/t/yii/shared_invite/MjIxMjMxMTk5MTU1LTE1MDE3MDAwMzMtM2VkMTMyMjY1Ng) and [Chat in IRC](https://www.yiiframework.com/chat/).\n- Follow us on [Facebook](https://www.facebook.com/groups/yiitalk/), [Twitter](https://twitter.com/yiiframework)\nand [GitHub](https://github.com/yiisoft/yii2).\n- Check [other communities](https://github.com/yiisoft/yii2/wiki/communities).\n\nContributing\n------------\n\nThe framework is [Open Source](LICENSE.md) powered by [an excellent community](https://github.com/yiisoft/yii2/graphs/contributors).\n\nYou may join us and:\n\n- [Report an issue](docs/internals/report-an-issue.md)\n- [Translate documentation or messages](docs/internals/translation-workflow.md)\n- [Give us feedback or start a design discussion](https://www.yiiframework.com/forum/index.php/forum/42-general-discussions-for-yii-20/)\n- [Contribute to the core code or fix bugs](docs/internals/git-workflow.md)\n- [Become a sponsor](#sponsoring)\n\n### Reporting Security issues\n\n> [!WARNING]\n> Please do not report security vulnerabilities through public GitHub issues.\n\nPlease refer to a [special page at the website](https://www.yiiframework.com/security/)\ndescribing proper workflow for security issue reports.\n\n### Directory Structure\n\n```\nbuild/               internally used build tools\ndocs/                documentation\nframework/           core framework code\ntests/               tests of the core framework code\n```\n\n### Spreading the Word\n\nAcknowledging or citing Yii 2 is as important as direct contributions.\n\n**In presentations**\n\nIf you are giving a presentation or talk featuring work that makes use of Yii 2 and would like to acknowledge it,\nwe suggest using [our logo](https://www.yiiframework.com/logo/) on your title slide.\n\n**In projects**\n\nIf you are using Yii 2 as part of an OpenSource project, a way to acknowledge it is to\n[use a special badge](https://img.shields.io/badge/Powered_by-Yii_Framework-green.svg?style=for-the-badge&logo=yii) in your README:    \n\n[![Yii2](https://img.shields.io/badge/Powered_by-Yii_Framework-green.svg?style=for-the-badge&logo=yii)](https://www.yiiframework.com/)\n\nIf your code is hosted at GitHub, you can place the following in your README.md file to get the badge:\n\n```\n[![Yii2](https://img.shields.io/badge/Powered_by-Yii_Framework-green.svg?style=for-the-badge&logo=yii)](https://www.yiiframework.com/)\n```\n\n### Sponsoring\n\nSupport this project by becoming a sponsor or a backer. \n\n[![Open Collective sponsors](https://img.shields.io/opencollective/sponsors/yiisoft?style=for-the-badge&logo=opencollective)](https://opencollective.com/yiisoft) \n[![Open Collective backers](https://img.shields.io/opencollective/backers/yiisoft?style=for-the-badge&logo=opencollective)](https://opencollective.com/yiisoft)\n"
  },
  {
    "path": "ROADMAP.md",
    "content": "> Roadmap for Yii 3.0 and further was moved to [yiisoft/docs](https://github.com/yiisoft/docs/blob/master/003-roadmap.md).\n\n- Enhancements are not accepted for framework version 2.0.\n- Enhancements are accepted for 2.0 extensions.\n- Bug and security fixes are expected.\n- Pull requests and maintainers are very welcome.\n\nAbove would stand as it is [for two years after Yii 3.0 release](https://www.yiiframework.com/release-cycle).\n\n## Additional releases\n\nWhile we focus on 3.0, we tag 2.0 releases and extension releases [about once in a week](https://www.yiiframework.com/release-cycle).\n\n\n## 2.0.16+ (since 2019 till 3.0 release + 2 years)\n\n- Bugfixes.\n\n## 2.0.15 (2nd quarter of 2018)\n\n- Since this release main focus is bug fixing.\n- No full-branch merges into 3.0.\n- No enhancements are accepted.\n\n## 2.0.14 (1st quarter of 2018)\n\nWill be last release with features and enhancements the last one that will be merged into 3.0 directly.\n"
  },
  {
    "path": "build/.htaccess",
    "content": "deny from all\n"
  },
  {
    "path": "build/build",
    "content": "#!/usr/bin/env php\n<?php\n/**\n * build script file.\n *\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\ndefine('YII_DEBUG', true);\n\n$composerAutoload = [\n    __DIR__ . '/../vendor/autoload.php', // yii2 as the root package\n    __DIR__ . '/../../../autoload.php', // yii2-basic or yii2-advanced as the root package\n];\n\nforeach ($composerAutoload as $autoload) {\n    if (file_exists($autoload)) {\n        require $autoload;\n        $vendorPath = dirname($autoload);\n        break;\n    }\n}\n\nif (!isset($vendorPath)) {\n\techo \"composer autoloader could not be found.\\nYou should run `composer install` in repo root directory.\\n\";\n\texit(1);\n}\nrequire __DIR__ . '/../framework/Yii.php';\n\nYii::setAlias('@yii/build', __DIR__);\n\n$application = new yii\\console\\Application([\n\t'id' => 'yii-build',\n\t'basePath' => __DIR__,\n\t'controllerNamespace' => 'yii\\build\\controllers',\n\t'enableCoreCommands' => false,\n]);\n$application->run();\n"
  },
  {
    "path": "build/build.bat",
    "content": "@echo off\n\nrem -------------------------------------------------------------\nrem  build script for Windows.\nrem\nrem  This is the bootstrap script for running build on Windows.\nrem\nrem  @author Qiang Xue <qiang.xue@gmail.com>\nrem  @link https://www.yiiframework.com/\nrem  @copyright 2008 Yii Software LLC\nrem  @license https://www.yiiframework.com/license/\nrem  @version $Id$\nrem -------------------------------------------------------------\n\n@setlocal\n\nset BUILD_PATH=%~dp0\n\nif \"%PHP_COMMAND%\" == \"\" set PHP_COMMAND=php.exe\n\n%PHP_COMMAND% \"%BUILD_PATH%build\" %*\n\n@endlocal\n"
  },
  {
    "path": "build/build.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n/**\n * Phing build file for Yii 2.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @link https://www.yiiframework.com/\n * @copyright 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n-->\n<project name=\"yii\" basedir=\".\" default=\"help\">\n  <!-- task definitions -->\n  <taskdef name=\"yii-init-build\" classname=\"YiiInitTask\" classpath=\"tasks\" />\n  <!--\n  <taskdef name=\"yii-pear\" classname=\"YiiPearTask\" classpath=\"tasks\"/>\n  -->\n\n  <!-- init yii.version, yii.revision and yii.winbuild -->\n  <yii-init-build />\n\n  <!-- these are required external commands -->\n  <property name=\"php\" value=\"php\" /> <!-- PHP parser -->\n  <property name=\"composer\" value=\"composer\" /> <!-- composer -->\n  <property name=\"pdflatex\" value=\"pdflatex\" />  <!-- generates PDF from LaTex -->\n\n  <property name=\"pkgname\" value=\"${phing.project.name}-${yii.version}.${yii.revision}\"/>\n  <property name=\"docname\" value=\"${phing.project.name}-docs-${yii.version}.${yii.revision}\"/>\n  <property name=\"pearname\" value=\"${phing.project.name}-${yii.release}.tgz\" />\n\n  <!-- directory definitions -->\n  <property name=\"build.base.dir\" value=\"release\"/>\n  <property name=\"build.dist.dir\" value=\"${build.base.dir}/dist\"/>\n  <property name=\"build.src.dir\" value=\"${build.base.dir}/${pkgname}\"/>\n  <property name=\"build.pear.src.dir\" value=\"${build.src.dir}/framework\" />\n  <property name=\"build.doc.dir\" value=\"${build.base.dir}/${docname}\"/>\n  <property name=\"build.web.dir\" value=\"${build.base.dir}/web\"/>\n\n  <tstamp>\n    <format property=\"DATE\" pattern=\"%b %e %Y\" />\n  </tstamp>\n\n  <if>\n    <equals arg1=\"${yii.winbuild}\" arg2=\"true\"/>\n    <then>\n      <property name=\"build\" value=\"build\"/>\n    </then>\n    <else>\n      <property name=\"build\" value=\"php build\"/>\n    </else>\n  </if>\n\n  <!-- source files in the framework -->\n  <fileset dir=\"..\" id=\"framework\">\n    <exclude name=\"**/.gitignore\"/>\n    <exclude name=\"**/*.bak\"/>\n    <exclude name=\"**/*~\"/>\n    <include name=\"framework/**/*\"/>\n    <include name=\"requirements/**/*\"/>\n    <include name=\"demos/**/*\"/>\n    <include name=\"CHANGELOG\"/>\n    <include name=\"UPGRADE\"/>\n    <include name=\"LICENSE\"/>\n    <include name=\"README\"/>\n  </fileset>\n\n  <!-- doc files -->\n  <fileset dir=\"../docs\" id=\"docs\">\n    <exclude name=\"**/.gitignore\"/>\n    <exclude name=\"**/*.bak\"/>\n    <exclude name=\"**/*~\"/>\n    <include name=\"guide/**/*\"/>\n    <include name=\"blog/**/*\"/>\n  </fileset>\n\n  <fileset dir=\"../docs/guide\" id=\"docs-guide\">\n    <exclude name=\"**/.gitignore\"/>\n    <exclude name=\"**/*.bak\"/>\n    <exclude name=\"**/*~\"/>\n    <include name=\"**/*\"/>\n  </fileset>\n\n  <fileset dir=\"../docs/blog\" id=\"docs-blog\">\n    <exclude name=\"**/.gitignore\"/>\n    <exclude name=\"**/*.bak\"/>\n    <exclude name=\"**/*~\"/>\n    <include name=\"**/*\"/>\n  </fileset>\n\n  <fileset dir=\".\" id=\"writables\">\n    <include name=\"${build.src.dir}/**/runtime\" />\n    <include name=\"${build.src.dir}/**/assets\" />\n    <include name=\"${build.src.dir}/demos/**/data\" />\n  </fileset>\n\n  <fileset dir=\".\" id=\"executables\">\n    <include name=\"${build.src.dir}/**/yii\" />\n  </fileset>\n\n  <target name=\"src\" depends=\"sync\">\n    <echo>Building package ${pkgname}...</echo>\n    <echo>Copying files to build directory...</echo>\n    <copy todir=\"${build.src.dir}\">\n      <fileset refid=\"framework\"/>\n    </copy>\n\n    <echo>Changing file permissions...</echo>\n    <chmod mode=\"0777\">\n      <fileset refid=\"writables\" />\n    </chmod>\n    <chmod mode=\"0755\">\n      <fileset refid=\"executables\" />\n    </chmod>\n\n    <echo>Generating source release file...</echo>\n    <mkdir dir=\"${build.dist.dir}\" />\n    <if>\n      <equals arg1=\"${yii.winbuild}\" arg2=\"true\"/>\n      <then>\n        <tar destfile=\"${build.dist.dir}/${pkgname}.tar.gz\" compression=\"gzip\">\n          <fileset dir=\"${build.base.dir}\">\n            <include name=\"${pkgname}/**/*\"/>\n          </fileset>\n        </tar>\n      </then>\n      <else>\n        <exec command=\"tar czpf ${pkgname}.tar.gz ${pkgname}\" dir=\"${build.base.dir}\"/>\n        <move file=\"${build.base.dir}/${pkgname}.tar.gz\" todir=\"${build.dist.dir}\" />\n      </else>\n    </if>\n    <zip destfile=\"${build.dist.dir}/${pkgname}.zip\">\n      <fileset dir=\"${build.base.dir}\">\n        <include name=\"${pkgname}/**/*\"/>\n      </fileset>\n    </zip>\n  </target>\n\n  <target name=\"doc\" depends=\"sync\">\n    <echo>Building documentation...</echo>\n\n    <echo>Building Guide PDF...</echo>\n    <exec command=\"${build} guideLatex\" dir=\".\" passthru=\"true\" />\n    <exec command=\"${pdflatex} guide.tex -interaction=nonstopmode -max-print-line=120\" dir=\"commands/guide\" passthru=\"true\"/>\n    <exec command=\"${pdflatex} guide.tex -interaction=nonstopmode -max-print-line=120\" dir=\"commands/guide\" passthru=\"true\"/>\n    <exec command=\"${pdflatex} guide.tex -interaction=nonstopmode -max-print-line=120\" dir=\"commands/guide\" passthru=\"true\"/>\n    <move file=\"commands/guide/guide.pdf\" tofile=\"${build.doc.dir}/yii-guide-${yii.version}.pdf\" />\n\n    <echo>Building Blog PDF...</echo>\n    <exec command=\"${build} blogLatex\" dir=\".\" passthru=\"true\" />\n    <exec command=\"${pdflatex} blog.tex -interaction=nonstopmode -max-print-line=120\" dir=\"commands/blog\" passthru=\"true\"/>\n    <exec command=\"${pdflatex} blog.tex -interaction=nonstopmode -max-print-line=120\" dir=\"commands/blog\" passthru=\"true\"/>\n    <exec command=\"${pdflatex} blog.tex -interaction=nonstopmode -max-print-line=120\" dir=\"commands/blog\" passthru=\"true\"/>\n    <move file=\"commands/blog/blog.pdf\" tofile=\"${build.doc.dir}/yii-blog-${yii.version}.pdf\" />\n\n    <echo>Building API...</echo>\n    <exec command=\"${build} api ${build.doc.dir}\" dir=\".\" passthru=\"true\" />\n\n    <!--\n    <echo>Building API CHM...</echo>\n    <exec command=\"${hhc} ${build.doc.dir}/api/manual.hhp\" />\n    <move file=\"${build.doc.dir}/api/manual.chm\" tofile=\"${build.doc.dir}/yii-api-${yii.version}.chm\" />\n    <delete>\n      <fileset dir=\"${build.doc.dir}/api\">\n        <include name=\"manual.*\" />\n      </fileset>\n    </delete>\n    -->\n\n    <echo>Generating doc release file...</echo>\n    <mkdir dir=\"${build.dist.dir}\" />\n    <tar destfile=\"${build.dist.dir}/${docname}.tar.gz\" compression=\"gzip\">\n      <fileset dir=\"${build.base.dir}\">\n        <include name=\"${docname}/**/*\"/>\n      </fileset>\n    </tar>\n    <zip destfile=\"${build.dist.dir}/${docname}.zip\">\n      <fileset dir=\"${build.base.dir}\">\n        <include name=\"${docname}/**/*\"/>\n      </fileset>\n    </zip>\n  </target>\n\n  <target name=\"web\" depends=\"sync\">\n\n    <echo>Building online API...</echo>\n    <mkdir dir=\"${build.web.dir}/common/data/${yii.version}\" />\n    <exec command=\"${build} api ${build.web.dir}/common/data/${yii.version} online\" dir=\".\" passthru=\"true\" />\n\n    <echo>Copying tutorials...</echo>\n    <copy todir=\"${build.web.dir}/common/data/${yii.version}/tutorials/guide\">\n      <fileset refid=\"docs-guide\"/>\n    </copy>\n    <copy todir=\"${build.web.dir}/common/data/${yii.version}/tutorials/blog\">\n      <fileset refid=\"docs-blog\"/>\n    </copy>\n\n\t<echo>Copying release text files...</echo>\n    <mkdir dir=\"${build.web.dir}/frontend/www/files\" />\n\t<copy file=\"../CHANGELOG\" tofile=\"${build.web.dir}/frontend/www/files/CHANGELOG-${yii.version}.txt\" />\n\t<copy file=\"../UPGRADE\" tofile=\"${build.web.dir}/frontend/www/files/UPGRADE-${yii.version}.txt\" />\n\n\t<echo>\n\nFinished building Web files.\nPlease update yiisite/common/data/versions.php file with the following code:\n\n    '1.1'=>array(\n        'version'=>'${yii.version}',\n        'revision'=>'${yii.revision}',\n        'date'=>'${yii.date}',\n        'latest'=>true,\n    ),\n\n\t</echo>\n  </target>\n\n  <target name=\"sync\">\n    <echo>Synchronizing code changes for ${pkgname}...</echo>\n\n    <echo>Building autoload map...</echo>\n    <exec command=\"${build} autoload\" dir=\".\" passthru=\"true\"/>\n\n    <echo>Building yiilite.php...</echo>\n    <exec command=\"${build} lite\" dir=\".\" passthru=\"true\"/>\n  </target>\n\n  <target name=\"message\">\n    <echo>Extracting i18n messages...</echo>\n    <exec command=\"${build} message ../framework/messages/config.php\" dir=\".\" passthru=\"true\"/>\n  </target>\n\n  <!--\n  <target name=\"pear\" depends=\"clean,build\">\n    <echo>Generating pear package for ${phing.project.name}-${yii.release}</echo>\n    <mkdir dir=\"${build.dist.dir}\" />\n    <yii-pear   pkgdir=\"${build.pear.src.dir}\"\n                channel=\"pear.php.net\"\n                version=\"${yii.release}\"\n                state=\"stable\"\n                category=\"framework\"\n                package=\"${phing.project.name}\"\n                summary=\"Yii PHP Framework\"\n                pkgdescription=\"Yii PHP Framework: Best for Web 2.0 Development\"\n                notes=\"https://www.yiiframework.com/files/CHANGELOG-${yii.release}.txt\"\n                license=\"BSD\"\n                />\n    <exec command=\"pear package\" dir=\"${build.pear.src.dir}\" passthru=\"true\" />\n    <move file=\"${build.pear.src.dir}/${pearname}\" tofile=\"${build.dist.dir}/${pearname}\" />\n  </target>\n  -->\n\n  <target name=\"clean\">\n    <echo>Cleaning up the build...</echo>\n    <delete dir=\"${build.base.dir}\"/>\n  </target>\n\n  <target name=\"help\">\n    <echo>\n\n    Welcome to use Yii build script!\n    --------------------------------\n    You may use the following command format to build a target:\n\n              phing   &lt;target name&gt;\n\n    where &lt;target name&gt; can be one of the following:\n\n    - sync    : synchronize yiilite.php and BaseYii.php\n    - message : extract i18n messages of the framework\n    - src     : build source release\n    - doc     : build documentation release (Windows only)\n    - clean   : clean up the build\n\n    </echo>\n  </target>\n</project>\n"
  },
  {
    "path": "build/controllers/ClassmapController.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\build\\controllers;\n\nuse yii\\console\\Application;\nuse yii\\console\\Controller;\nuse yii\\console\\Exception;\nuse yii\\helpers\\FileHelper;\n\n/**\n * Creates a class map for the core Yii classes.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @extends Controller<Application>\n */\nclass ClassmapController extends Controller\n{\n    public $defaultAction = 'create';\n\n    /**\n     * Creates a class map for the core Yii classes.\n     * @param string $root    the root path of Yii framework. Defaults to YII2_PATH.\n     * @param string $mapFile the file to contain the class map. Defaults to YII2_PATH . '/classes.php'.\n     */\n    public function actionCreate($root = null, $mapFile = null)\n    {\n        if ($root === null) {\n            $root = YII2_PATH;\n        }\n        $root = FileHelper::normalizePath($root);\n        if ($mapFile === null) {\n            $mapFile = YII2_PATH . '/classes.php';\n        }\n        $options = [\n            'filter' => function ($path) {\n                if (is_file($path)) {\n                    $file = basename($path);\n                    if ($file[0] < 'A' || $file[0] > 'Z') {\n                        return false;\n                    }\n                }\n\n                return null;\n            },\n            'only' => ['*.php'],\n            'except' => [\n                '/Yii.php',\n                '/BaseYii.php',\n                '/console/',\n                '/requirements/',\n            ],\n        ];\n        $files = FileHelper::findFiles($root, $options);\n        $map = [];\n        foreach ($files as $file) {\n            if (strpos($file, $root) !== 0) {\n                throw new Exception(\"Something wrong: $file\\n\");\n            }\n            $path = str_replace('\\\\', '/', substr($file, \\strlen($root)));\n            $map[$path] = \"  'yii\" . substr(str_replace('/', '\\\\', $path), 0, -4) . \"' => YII2_PATH . '$path',\";\n        }\n        ksort($map);\n        $map = implode(\"\\n\", $map);\n        $output = <<<EOD\n<?php\n/**\n * Yii core class map.\n *\n * This file is automatically generated by the \"build classmap\" command under the \"build\" folder.\n * Do not modify it directly.\n *\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nreturn [\n$map\n];\n\nEOD;\n        if (is_file($mapFile) && file_get_contents($mapFile) === $output) {\n            echo \"Nothing changed.\\n\";\n        } else {\n            file_put_contents($mapFile, $output);\n            echo \"Class map saved in $mapFile\\n\";\n        }\n    }\n}\n"
  },
  {
    "path": "build/controllers/DevController.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\build\\controllers;\n\nuse Yii;\nuse yii\\base\\InvalidParamException;\nuse yii\\console\\Application;\nuse yii\\console\\Controller;\nuse yii\\console\\ExitCode;\nuse yii\\helpers\\Console;\nuse yii\\helpers\\FileHelper;\n\n/**\n * This command helps to set up a dev environment with all extensions and applications.\n *\n * It will clone an extension or app repo and link the yii2 dev installation to the contained applications/extensions vendor dirs\n * to help to work on yii using the application to test it.\n *\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0\n *\n * @extends Controller<Application>\n */\nclass DevController extends Controller\n{\n    /**\n     * {@inheritdoc}\n     */\n    public $defaultAction = 'all';\n    /**\n     * @var bool whether to use HTTP when cloning GitHub repositories\n     */\n    public $useHttp = false;\n    /**\n     * @var bool whether to use --no-progress option when running composer\n     */\n    public $composerNoProgress = false;\n    /**\n     * @var array\n     */\n    public $apps = [\n        'basic' => 'git@github.com:yiisoft/yii2-app-basic.git',\n        'advanced' => 'git@github.com:yiisoft/yii2-app-advanced.git',\n        'benchmark' => 'git@github.com:yiisoft/yii2-app-benchmark.git',\n    ];\n    /**\n     * @var array\n     */\n    public $extensions = [\n        'apidoc' => 'git@github.com:yiisoft/yii2-apidoc.git',\n        'authclient' => 'git@github.com:yiisoft/yii2-authclient.git',\n        'bootstrap' => 'git@github.com:yiisoft/yii2-bootstrap.git',\n        'bootstrap4' => 'git@github.com:yiisoft/yii2-bootstrap4.git',\n        'bootstrap5' => 'git@github.com:yiisoft/yii2-bootstrap5.git',\n        'composer' => 'git@github.com:yiisoft/yii2-composer.git',\n        'debug' => 'git@github.com:yiisoft/yii2-debug.git',\n        'elasticsearch' => 'git@github.com:yiisoft/yii2-elasticsearch.git',\n        'faker' => 'git@github.com:yiisoft/yii2-faker.git',\n        'gii' => 'git@github.com:yiisoft/yii2-gii.git',\n        'httpclient' => 'git@github.com:yiisoft/yii2-httpclient.git',\n        'imagine' => 'git@github.com:yiisoft/yii2-imagine.git',\n        'jui' => 'git@github.com:yiisoft/yii2-jui.git',\n        'mongodb' => 'git@github.com:yiisoft/yii2-mongodb.git',\n        'queue' => 'git@github.com:yiisoft/yii2-queue.git',\n        'redis' => 'git@github.com:yiisoft/yii2-redis.git',\n        'shell' => 'git@github.com:yiisoft/yii2-shell.git',\n        'smarty' => 'git@github.com:yiisoft/yii2-smarty.git',\n        'sphinx' => 'git@github.com:yiisoft/yii2-sphinx.git',\n        'swiftmailer' => 'git@github.com:yiisoft/yii2-swiftmailer.git',\n        'symfonymailer' => 'git@github.com:yiisoft/yii2-symfonymailer.git',\n        'twig' => 'git@github.com:yiisoft/yii2-twig.git',\n    ];\n\n\n    /**\n     * Install all extensions and advanced + basic app.\n     */\n    public function actionAll()\n    {\n        if (!$this->confirm('Install all applications and all extensions now?')) {\n            return 1;\n        }\n\n        foreach ($this->extensions as $ext => $repo) {\n            $ret = $this->actionExt($ext);\n            if ($ret !== 0) {\n                return $ret;\n            }\n        }\n\n        foreach ($this->apps as $app => $repo) {\n            $ret = $this->actionApp($app);\n            if ($ret !== 0) {\n                return $ret;\n            }\n        }\n\n        return 0;\n    }\n\n    /**\n     * Runs a command in all extension and application directories.\n     *\n     * Can be used to run e.g. `git pull`.\n     *\n     *     ./build/build dev/run git pull\n     *\n     * @param string $command the command to run\n     */\n    public function actionRun($command)\n    {\n        $command = implode(' ', \\func_get_args());\n\n        // root of the dev repo\n        $base = \\dirname(\\dirname(__DIR__));\n        $dirs = $this->listSubDirs(\"$base/extensions\");\n        $dirs = array_merge($dirs, $this->listSubDirs(\"$base/apps\"));\n        asort($dirs);\n\n        $oldcwd = getcwd();\n        foreach ($dirs as $dir) {\n            $displayDir = substr($dir, \\strlen($base));\n            $this->stdout(\"Running '$command' in $displayDir...\\n\", Console::BOLD);\n            chdir($dir);\n            passthru($command);\n            $this->stdout(\"done.\\n\", Console::BOLD, Console::FG_GREEN);\n        }\n        chdir($oldcwd);\n    }\n\n    /**\n     * This command installs a project template in the `apps` directory and links the framework and extensions.\n     *\n     * It basically runs the following commands in the dev repo root:\n     *\n     * - Run `composer update`\n     * - `rm -rf apps/basic/vendor/yiisoft/yii2`\n     * - `rm -rf apps/basic/vendor/yiisoft/yii2-*`\n     *\n     * And replaces them with symbolic links to the extensions and framework path in the dev repo.\n     *\n     * Extensions required by the application are automatically installed using the `ext` action.\n     *\n     * @param string $app the application name e.g. `basic` or `advanced`.\n     * @param string $repo url of the git repo to clone if it does not already exist.\n     * @return int return code\n     */\n    public function actionApp($app, $repo = null)\n    {\n        // root of the dev repo\n        $base = \\dirname(\\dirname(__DIR__));\n        $appDir = \"$base/apps/$app\";\n\n        if (!file_exists($appDir)) {\n            if (empty($repo)) {\n                if (isset($this->apps[$app])) {\n                    $repo = $this->apps[$app];\n                    if ($this->useHttp) {\n                        $repo = str_replace('git@github.com:', 'https://github.com/', $repo);\n                    }\n                } else {\n                    $this->stderr(\"Repo argument is required for app '$app'.\\n\", Console::FG_RED);\n                    return 1;\n                }\n            }\n\n            $this->stdout(\"cloning application repo '$app' from '$repo'...\\n\", Console::BOLD);\n            passthru('git clone ' . escapeshellarg($repo) . ' ' . $appDir, $returnVar);\n            if ($returnVar !== 0) {\n                $this->stdout(\"Error occurred while cloning repository.\\n\", Console::BOLD, Console::FG_RED);\n                return 1;\n            }\n            $this->stdout(\"done.\\n\", Console::BOLD, Console::FG_GREEN);\n        }\n\n        // cleanup\n        $this->stdout(\"cleaning up application '$app' vendor directory...\\n\", Console::BOLD);\n        $this->cleanupVendorDir($appDir);\n        $this->stdout(\"done.\\n\", Console::BOLD, Console::FG_GREEN);\n\n        // composer update\n        $this->stdout(\"updating composer for app '$app'...\\n\", Console::BOLD);\n        chdir($appDir);\n        $command = 'composer update --prefer-dist';\n        if ($this->composerNoProgress) {\n            $command .= ' --no-progress';\n        }\n        passthru($command);\n        $this->stdout(\"done.\\n\", Console::BOLD, Console::FG_GREEN);\n\n        // link directories\n        $this->stdout(\"linking framework and extensions to '$app' app vendor dir...\\n\", Console::BOLD);\n        $this->linkFrameworkAndExtensions($appDir, $base);\n        $this->stdout(\"done.\\n\", Console::BOLD, Console::FG_GREEN);\n\n        return 0;\n    }\n\n    /**\n     * This command installs an extension in the `extensions` directory and links the framework and other extensions.\n     *\n     * @param string $extension the application name e.g. `basic` or `advanced`.\n     * @param string $repo url of the git repo to clone if it does not already exist.\n     *\n     * @return int\n     */\n    public function actionExt($extension, $repo = null)\n    {\n        // root of the dev repo\n        $base = \\dirname(\\dirname(__DIR__));\n        $extensionDir = \"$base/extensions/$extension\";\n\n        if (!file_exists($extensionDir)) {\n            if (empty($repo)) {\n                if (isset($this->extensions[$extension])) {\n                    $repo = $this->extensions[$extension];\n                    if ($this->useHttp) {\n                        $repo = str_replace('git@github.com:', 'https://github.com/', $repo);\n                    }\n                } else {\n                    $this->stderr(\"Repo argument is required for extension '$extension'.\\n\", Console::FG_RED);\n                    return 1;\n                }\n            }\n\n            $this->stdout(\"cloning extension repo '$extension' from '$repo'...\\n\", Console::BOLD);\n            passthru('git clone ' . escapeshellarg($repo) . ' ' . $extensionDir);\n            $this->stdout(\"done.\\n\", Console::BOLD, Console::FG_GREEN);\n        }\n\n        // cleanup\n        $this->stdout(\"cleaning up extension '$extension' vendor directory...\\n\", Console::BOLD);\n        $this->cleanupVendorDir($extensionDir);\n        $this->stdout(\"done.\\n\", Console::BOLD, Console::FG_GREEN);\n\n        // composer update\n        $this->stdout(\"updating composer for extension '$extension'...\\n\", Console::BOLD);\n        chdir($extensionDir);\n        $command = 'composer update --prefer-dist';\n        if ($this->composerNoProgress) {\n            $command .= ' --no-progress';\n        }\n        passthru($command);\n        $this->stdout(\"done.\\n\", Console::BOLD, Console::FG_GREEN);\n\n        // link directories\n        $this->stdout(\"linking framework and extensions to '$extension' vendor dir...\\n\", Console::BOLD);\n        $this->linkFrameworkAndExtensions($extensionDir, $base);\n        $this->stdout(\"done.\\n\", Console::BOLD, Console::FG_GREEN);\n\n        return 0;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function options($actionID)\n    {\n        $options = parent::options($actionID);\n        if (\\in_array($actionID, ['ext', 'app', 'all'], true)) {\n            $options[] = 'useHttp';\n            $options[] = 'composerNoProgress';\n        }\n\n        return $options;\n    }\n\n\n    /**\n     * Remove all symlinks in the vendor subdirectory of the directory specified.\n     * @param string $dir base directory\n     */\n    protected function cleanupVendorDir($dir)\n    {\n        if (is_link($link = \"$dir/vendor/yiisoft/yii2\")) {\n            $this->stdout(\"Removing symlink $link.\\n\");\n            FileHelper::unlink($link);\n        }\n        $extensions = $this->findDirs(\"$dir/vendor/yiisoft\");\n        foreach ($extensions as $ext) {\n            if (is_link($link = \"$dir/vendor/yiisoft/yii2-$ext\")) {\n                $this->stdout(\"Removing symlink $link.\\n\");\n                FileHelper::unlink($link);\n            }\n        }\n    }\n\n    /**\n     * Creates symlinks to framework and extension sources for the application.\n     * @param string $dir application directory\n     * @param string $base Yii sources base directory\n     *\n     * @return int\n     */\n    protected function linkFrameworkAndExtensions($dir, $base)\n    {\n        if (is_dir($link = \"$dir/vendor/yiisoft/yii2\")) {\n            $this->stdout(\"Removing dir $link.\\n\");\n            FileHelper::removeDirectory($link);\n            $this->stdout(\"Creating symlink for $link.\\n\");\n            symlink(\"$base/framework\", $link);\n        }\n        $extensions = $this->findDirs(\"$dir/vendor/yiisoft\");\n        foreach ($extensions as $ext) {\n            if (is_dir($link = \"$dir/vendor/yiisoft/yii2-$ext\")) {\n                $this->stdout(\"Removing dir $link.\\n\");\n                FileHelper::removeDirectory($link);\n                $this->stdout(\"Creating symlink for $link.\\n\");\n                if (!file_exists(\"$base/extensions/$ext\")) {\n                    $ret = $this->actionExt($ext);\n                    if ($ret !== 0) {\n                        return $ret;\n                    }\n                }\n                symlink(\"$base/extensions/$ext\", $link);\n            }\n        }\n\n        return ExitCode::OK;\n    }\n\n    /**\n     * Get a list of subdirectories for directory specified.\n     * @param string $dir directory to read\n     *\n     * @return array list of subdirectories\n     */\n    protected function listSubDirs($dir)\n    {\n        $list = [];\n        $handle = opendir($dir);\n        if ($handle === false) {\n            throw new InvalidParamException(\"Unable to open directory: $dir\");\n        }\n        while (($file = readdir($handle)) !== false) {\n            if ($file === '.' || $file === '..') {\n                continue;\n            }\n            // ignore hidden directories\n            if (strpos($file, '.') === 0) {\n                continue;\n            }\n            if (is_dir(\"$dir/$file\")) {\n                $list[] = \"$dir/$file\";\n            }\n        }\n        closedir($handle);\n        return $list;\n    }\n\n    /**\n     * Finds linkable applications.\n     *\n     * @param string $dir directory to search in\n     * @return array list of applications command can link\n     */\n    protected function findDirs($dir)\n    {\n        $list = [];\n        $handle = @opendir($dir);\n        if ($handle === false) {\n            return [];\n        }\n        while (($file = readdir($handle)) !== false) {\n            if ($file === '.' || $file === '..') {\n                continue;\n            }\n            $path = $dir . DIRECTORY_SEPARATOR . $file;\n            if (is_dir($path) && preg_match('/^yii2-(.*)$/', $file, $matches)) {\n                $list[] = $matches[1];\n            }\n        }\n        closedir($handle);\n\n        foreach ($list as $i => $e) {\n            if ($e === 'composer') { // skip composer to not break composer update\n                unset($list[$i]);\n            }\n        }\n\n        return $list;\n    }\n}\n"
  },
  {
    "path": "build/controllers/MimeTypeController.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\build\\controllers;\n\nuse Yii;\nuse yii\\console\\Application;\nuse yii\\console\\Controller;\nuse yii\\helpers\\Console;\nuse yii\\helpers\\VarDumper;\n\n/**\n * MimeTypeController generates a map of file extensions to MIME types.\n *\n * It uses `mime.types` file from apache http located under\n * https://raw.githubusercontent.com/apache/httpd/refs/heads/trunk/docs/conf/mime.types\n *\n * This file has been placed in the public domain for unlimited redistribution,\n * so we can use it and ship it with Yii.\n *\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0\n *\n * @extends Controller<Application>\n */\nclass MimeTypeController extends Controller\n{\n    /**\n     * @var array MIME type aliases\n     */\n    private $_aliases = [\n        'text/rtf' => 'application/rtf',\n        'text/xml' => 'application/xml',\n        'image/svg' => 'image/svg+xml',\n        'image/x-bmp' => 'image/bmp',\n        'image/x-bitmap' => 'image/bmp',\n        'image/x-xbitmap' => 'image/bmp',\n        'image/x-win-bitmap' => 'image/bmp',\n        'image/x-windows-bmp' => 'image/bmp',\n        'image/ms-bmp' => 'image/bmp',\n        'image/x-ms-bmp' => 'image/bmp',\n        'application/bmp' => 'image/bmp',\n        'application/x-bmp' => 'image/bmp',\n        'application/x-win-bitmap' => 'image/bmp',\n    ];\n\n    /**\n     * @var array MIME types to add to the ones parsed from Apache files\n     */\n    private $_additionalMimeTypes = [\n        'apng' => 'image/apng',\n        'avif' => 'image/avif',\n        'jfif' => 'image/jpeg',\n        'mjs' => 'text/javascript',\n        'pjp' => 'image/jpeg',\n        'pjpeg' => 'image/jpeg',\n    ];\n\n    /**\n     * @param string $outFile the mime file to update. Defaults to @yii/helpers/mimeTypes.php\n     * @param string $aliasesOutFile the aliases file to update. Defaults to @yii/helpers/mimeAliases.php\n     */\n    public function actionIndex($outFile = null, $aliasesOutFile = null, $extensionsOutFile = null)\n    {\n        if ($outFile === null) {\n            $outFile = Yii::getAlias('@yii/helpers/mimeTypes.php');\n        }\n\n        if ($aliasesOutFile === null) {\n            $aliasesOutFile = Yii::getAlias('@yii/helpers/mimeAliases.php');\n        }\n\n        if ($extensionsOutFile === null) {\n            $extensionsOutFile = Yii::getAlias('@yii/helpers/mimeExtensions.php');\n        }\n\n        $this->stdout('Downloading mime-type file from apache httpd repository...');\n        if ($apacheMimeTypesFileContent = file_get_contents('https://raw.githubusercontent.com/apache/httpd/refs/heads/trunk/docs/conf/mime.types')) {\n            $this->stdout(\"Done.\\n\", Console::FG_GREEN);\n            $this->generateMimeTypesFile($outFile, $apacheMimeTypesFileContent);\n            $this->generateMimeAliasesFile($aliasesOutFile);\n            $this->generateMimeExtensionsFile($extensionsOutFile, $apacheMimeTypesFileContent);\n        } else {\n            $this->stderr(\"Failed to download mime.types file from apache Git.\\n\");\n        }\n    }\n\n    /**\n     * @param string $outFile\n     * @param string $content\n     */\n    private function generateMimeTypesFile($outFile, $content)\n    {\n        $this->stdout(\"Generating file $outFile...\");\n        $mimeMap = [];\n        foreach (explode(\"\\n\", $content) as $line) {\n            $line = trim($line);\n            if (empty($line) || strpos($line, '#') === 0) { // skip comments and empty lines\n                continue;\n            }\n            $parts = preg_split('/\\s+/', $line);\n            $mime = array_shift($parts);\n            foreach ($parts as $ext) {\n                if (!empty($ext)) {\n                    $mimeMap[$ext] = $mime;\n                }\n            }\n        }\n        $mimeMap = array_replace($mimeMap, $this->_additionalMimeTypes);\n        ksort($mimeMap, SORT_STRING);\n        $array = VarDumper::export($mimeMap);\n\n        $content = <<<EOD\n<?php\n/**\n * MIME types.\n *\n * This file contains most commonly used MIME types\n * according to file extension names.\n * Its content is generated from the apache http mime.types file.\n * https://raw.githubusercontent.com/apache/httpd/refs/heads/trunk/docs/conf/mime.types\n * This file has been placed in the public domain for unlimited redistribution.\n *\n * All extra changes made to this file must be committed to /build/controllers/MimeTypeController.php\n * otherwise they will be lost on next build.\n */\n\\$mimeTypes = $array;\n\n# fix for bundled libmagic bug, see also https://github.com/yiisoft/yii2/issues/19925\nif ((PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80122) || (PHP_VERSION_ID >= 80200 && PHP_VERSION_ID < 80209)) {\n    \\$mimeTypes = array_replace(\\$mimeTypes, ['xz' => 'application/octet-stream']);\n}\n\nreturn \\$mimeTypes;\n\nEOD;\n        file_put_contents($outFile, $content);\n        $this->stdout(\"done.\\n\", Console::FG_GREEN);\n    }\n\n    /**\n     * @param string $outFile\n     */\n    private function generateMimeAliasesFile($outFile)\n    {\n        $this->stdout(\"generating file $outFile...\");\n        $array = VarDumper::export($this->_aliases);\n        $content = <<<EOD\n<?php\n/**\n * MIME aliases.\n *\n * This file contains aliases for MIME types.\n *\n * All extra changes made to this file must be committed to /build/controllers/MimeTypeController.php\n * otherwise they will be lost on next build.\n */\nreturn $array;\n\nEOD;\n        file_put_contents($outFile, $content);\n        $this->stdout(\"done.\\n\", Console::FG_GREEN);\n    }\n\n    /**\n     * @param string $outFile\n     * @param string $content\n     */\n    private function generateMimeExtensionsFile($outFile, $content)\n    {\n        $this->stdout(\"Generating file $outFile...\");\n\n        $extensionMap = [];\n        foreach (explode(\"\\n\", $content) as $line) {\n            $line = trim($line);\n            if (empty($line) || strpos($line, '#') === 0) { // skip comments and empty lines\n                continue;\n            }\n            $parts = preg_split('/\\s+/', $line);\n            $mime = array_shift($parts);\n            if (!empty($parts)) {\n                $extensionMap[$mime] = [];\n                foreach ($parts as $ext) {\n                    if (!empty($ext)) {\n                        $extensionMap[$mime][] = $ext;\n                    }\n                }\n            }\n        }\n\n        foreach ($this->_additionalMimeTypes as $ext => $mime) {\n            if (!array_key_exists($mime, $extensionMap)) {\n                $extensionMap[$mime] = [];\n            }\n            $extensionMap[$mime][] = $ext;\n        }\n\n        foreach ($extensionMap as $mime => $extensions) {\n            if (count($extensions) === 1) {\n                $extensionMap[$mime] = $extensions[0];\n            }\n        }\n\n        ksort($extensionMap, SORT_STRING);\n        $array = VarDumper::export($extensionMap);\n\n        $content = <<<EOD\n<?php\n/**\n * MIME type extensions.\n *\n * This file contains most commonly used extensions for MIME types.\n * If there are multiple extensions for a singe MIME type\n * they are ordered from most to least common.\n * Its content is generated from the apache http mime.types file.\n * https://raw.githubusercontent.com/apache/httpd/refs/heads/trunk/docs/conf/mime.types\n * This file has been placed in the public domain for unlimited redistribution.\n *\n * All extra changes made to this file must be committed to /build/controllers/MimeTypeController.php\n * otherwise they will be lost on next build.\n */\nreturn $array;\n\nEOD;\n        file_put_contents($outFile, $content);\n        $this->stdout(\"done.\\n\", Console::FG_GREEN);\n    }\n}\n"
  },
  {
    "path": "build/controllers/PhpDocController.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\build\\controllers;\n\nuse Yii;\nuse yii\\base\\Model;\nuse yii\\base\\Module;\nuse yii\\console\\Application;\nuse yii\\console\\Controller as ConsoleController;\nuse yii\\db\\QueryBuilder;\nuse yii\\helpers\\Console;\nuse yii\\helpers\\FileHelper;\nuse yii\\helpers\\Json;\nuse yii\\log\\Dispatcher;\nuse yii\\log\\Target;\nuse yii\\web\\Controller as WebController;\nuse yii\\web\\Request as WebRequest;\n\n/**\n * PhpDocController is there to help to maintain PHPDoc annotation in class files.\n *\n * @author Carsten Brandt <mail@cebe.cc>\n * @author Alexander Makarov <sam@rmcreative.ru>\n * @since 2.0\n *\n * @extends ConsoleController<Application>\n */\nclass PhpDocController extends ConsoleController\n{\n    /**\n     * Manually added PHPDoc properties that do not need to be removed or changed.\n     *\n     * @var array<class-string, string[]>\n     */\n    private const MANUALLY_ADDED_PROPERTIES = [\n        WebController::class => [\n            'request',\n            'response',\n            'view',\n        ],\n        ConsoleController::class => [\n            'request',\n            'response',\n            'help',\n        ],\n        Model::class => [\n            'errors',\n        ],\n        Module::class => [\n            'aliases',\n        ],\n        Dispatcher::class => [\n            'flushInterval',\n            'logger',\n        ],\n        Target::class => [\n            'enabled',\n        ],\n        WebRequest::class => [\n            'hostInfo',\n        ],\n        QueryBuilder::class => [\n            'conditionClasses',\n        ],\n    ];\n\n    private const PROPERTIES_ENCLOSURE = \" *\\n\";\n\n    private const TYPE_REG_EXP = '\\??[\\w\\\\\\-]+(?:<(?:[^<>]+|<[^<>]*>)*>|\\{[^{}]*\\}|\\([^()]*\\)(?:\\s*:\\s*[^()\\s]+)?)?(?:\\[\\])*(?:\\s*(?:\\||&|\\?|:)\\s*\\??[\\w\\\\\\-]+(?:<[^<>]*>)?(?:\\[\\])*)*';\n\n    /**\n     * {@inheritdoc}\n     */\n    public $defaultAction = 'property';\n    /**\n     * @var bool whether to update class docs directly. Setting this to false will just output docs\n     * for copy and paste.\n     */\n    public $updateFiles = true;\n    /**\n     * @var bool whether to add copyright header to php files. This should be skipped in application code.\n     */\n    public $skipFrameworkRequirements = false;\n\n\n    /**\n     * Generates `@property` annotations in class files from getters and setters.\n     *\n     * Property description will be taken from getter or setter or from an `@property` annotation\n     * in the getters docblock if there is one defined.\n     *\n     * See https://github.com/yiisoft/yii2/wiki/Core-framework-code-style#documentation for details.\n     *\n     * @param string $root the directory to parse files from. Defaults to YII2_PATH.\n     */\n    public function actionProperty($root = null)\n    {\n        $files = $this->findFiles($root);\n\n        $nFilesTotal = 0;\n        $nFilesUpdated = 0;\n        foreach ($files as $file) {\n            $result = $this->generateClassPropertyDocs($file);\n            if ($result !== false) {\n                list($className, $phpdoc) = $result;\n                if ($this->updateFiles) {\n                    if ($this->updateClassPropertyDocs($file, $className, $phpdoc)) {\n                        $nFilesUpdated++;\n                    }\n                } elseif (!empty($phpdoc)) {\n                    $this->stdout(\"\\n[ \" . $file . \" ]\\n\\n\", Console::BOLD);\n                    $this->stdout($phpdoc);\n                }\n            }\n            $nFilesTotal++;\n        }\n\n        $this->stdout(\"\\nParsed $nFilesTotal files.\\n\");\n        $this->stdout(\"Updated $nFilesUpdated files.\\n\");\n    }\n\n    /**\n     * Fix some issues with PHPDoc in files.\n     *\n     * @param string $root the directory to parse files from. Defaults to YII2_PATH.\n     */\n    public function actionFix($root = null)\n    {\n        $files = $this->findFiles($root, false);\n\n        $nFilesTotal = 0;\n        $nFilesUpdated = 0;\n        foreach ($files as $file) {\n            $contents = file_get_contents($file);\n            $hash = $this->hash($contents);\n\n            // fix line endings\n            $lines = preg_split('/(\\r\\n|\\n|\\r)/', $contents);\n\n            if (!$this->skipFrameworkRequirements) {\n                $this->fixFileDoc($lines);\n            }\n            $this->fixDocBlockIndentation($lines);\n            $lines = array_values($this->fixLineSpacing($lines));\n\n            $newContent = implode(\"\\n\", $lines);\n            if ($hash !== $this->hash($newContent)) {\n                file_put_contents($file, $newContent);\n                $nFilesUpdated++;\n            }\n            $nFilesTotal++;\n        }\n\n        $this->stdout(\"\\nParsed $nFilesTotal files.\\n\");\n        $this->stdout(\"Updated $nFilesUpdated files.\\n\");\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function options($actionID)\n    {\n        return array_merge(parent::options($actionID), ['updateFiles', 'skipFrameworkRequirements']);\n    }\n\n    /**\n     * @param string $root\n     * @param bool $needsInclude\n     * @return array list of files.\n     */\n    protected function findFiles($root, $needsInclude = true)\n    {\n        $except = [];\n        if ($needsInclude) {\n            $extensionExcept = [\n                'apidoc' => [\n                    '/helpers/PrettyPrinter.php',\n                    '/extensions/apidoc/helpers/ApiIndexer.php',\n                    '/extensions/apidoc/helpers/ApiMarkdownLaTeX.php',\n                ],\n                'codeception' => [\n                    '/TestCase.php',\n                    '/DbTestCase.php',\n                ],\n                'gii' => [\n                    '/components/DiffRendererHtmlInline.php',\n                    '/generators/extension/default/AutoloadExample.php',\n                ],\n                'swiftmailer' => [\n                    'src/Logger.php',\n                ],\n                'twig' => [\n                    '/Extension.php',\n                    '/Optimizer.php',\n                    '/Template.php',\n                    '/TwigSimpleFileLoader.php',\n                    '/ViewRendererStaticClassProxy.php',\n                ],\n            ];\n        } else {\n            $extensionExcept = [];\n        }\n\n        if ($root === null) {\n            $root = \\dirname(YII2_PATH);\n            $extensionPath = \"$root/extensions\";\n            $this->setUpExtensionAliases($extensionPath);\n\n            $except = [\n                '/apps/',\n                '/build/',\n                '/docs/',\n                '/extensions/composer/',\n                '/framework/BaseYii.php',\n                '/framework/Yii.php',\n                'assets/',\n                'tests/',\n                'vendor/',\n            ];\n            foreach ($extensionExcept as $ext => $paths) {\n                foreach ($paths as $path) {\n                    $except[] = \"/extensions/$ext$path\";\n                }\n            }\n        } elseif (preg_match('~extensions/([\\w-]+)[\\\\\\\\/]?$~', $root, $matches)) {\n            $extensionPath = \\dirname(rtrim($root, '\\\\/'));\n            $this->setUpExtensionAliases($extensionPath);\n\n            list(, $extension) = $matches;\n            Yii::setAlias(\"@yii/$extension\", (string)$root);\n            if (is_file($autoloadFile = Yii::getAlias(\"@yii/$extension/vendor/autoload.php\"))) {\n                include $autoloadFile;\n            }\n\n            if (isset($extensionExcept[$extension])) {\n                foreach ($extensionExcept[$extension] as $path) {\n                    $except[] = $path;\n                }\n            }\n            $except[] = '/vendor/';\n            $except[] = '/tests/';\n            $except[] = '/docs/';\n\n            //            // composer extension does not contain yii code\n            //            if ($extension === 'composer') {\n            //                return [];\n            //            }\n        } elseif (preg_match('~apps/([\\w-]+)[\\\\\\\\/]?$~', $root, $matches)) {\n            $extensionPath = \\dirname(\\dirname(rtrim($root, '\\\\/'))) . '/extensions';\n            $this->setUpExtensionAliases($extensionPath);\n\n            list(, $appName) = $matches;\n            Yii::setAlias(\"@app-$appName\", (string)$root);\n            if (is_file($autoloadFile = Yii::getAlias(\"@app-$appName/vendor/autoload.php\"))) {\n                include $autoloadFile;\n            }\n\n            $except[] = '/runtime/';\n            $except[] = '/vendor/';\n            $except[] = '/tests/';\n            $except[] = '/docs/';\n        }\n        $root = FileHelper::normalizePath($root);\n        $options = [\n            'filter' => function ($path) {\n                if (is_file($path)) {\n                    $file = basename($path);\n                    if ($file[0] < 'A' || $file[0] > 'Z') {\n                        return false;\n                    }\n                }\n\n                return null;\n            },\n            'only' => ['*.php'],\n            'except' => array_merge($except, [\n                '.git/',\n                'views/',\n                'requirements/',\n                'gii/generators/',\n                'vendor/',\n                '_support/',\n            ]),\n        ];\n\n        return FileHelper::findFiles($root, $options);\n    }\n\n    /**\n     * @param string $extensionPath root path containing extension repositories.\n     */\n    private function setUpExtensionAliases($extensionPath)\n    {\n        foreach (scandir($extensionPath) as $extension) {\n            if (ctype_alpha($extension) && is_dir($extensionPath . '/' . $extension)) {\n                Yii::setAlias(\"@yii/$extension\", \"$extensionPath/$extension\");\n\n                $composerConfigFile = $extensionPath . '/' . $extension . '/composer.json';\n                if (file_exists($composerConfigFile)) {\n                    $composerConfig = Json::decode(file_get_contents($composerConfigFile));\n                    if (isset($composerConfig['autoload']['psr-4'])) {\n                        foreach ($composerConfig['autoload']['psr-4'] as $namespace => $subPath) {\n                            $alias = '@' . str_replace('\\\\', '/', $namespace);\n                            $path = rtrim(\"$extensionPath/$extension/$subPath\", '/');\n                            Yii::setAlias($alias, $path);\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    /**\n     * Fix file PHPDoc.\n     */\n    protected function fixFileDoc(&$lines)\n    {\n        // find namespace\n        $namespace = false;\n        $namespaceLine = '';\n        $contentAfterNamespace = false;\n        foreach ($lines as $i => $line) {\n            $line = trim($line);\n            if (!empty($line)) {\n                if (strncmp($line, 'namespace', 9) === 0) {\n                    $namespace = $i;\n                    $namespaceLine = $line;\n                } elseif ($namespace !== false) {\n                    $contentAfterNamespace = $i;\n                    break;\n                }\n            }\n        }\n\n        if ($namespace !== false && $contentAfterNamespace !== false) {\n            while ($contentAfterNamespace > 0) {\n                array_shift($lines);\n                $contentAfterNamespace--;\n            }\n            $lines = array_merge([\n                '<?php',\n                '/**',\n                ' * @link https://www.yiiframework.com/',\n                ' * @copyright Copyright (c) 2008 Yii Software LLC',\n                ' * @license https://www.yiiframework.com/license/',\n                ' */',\n                '',\n                $namespaceLine,\n                '',\n            ], $lines);\n        }\n    }\n\n    /**\n     * Markdown aware fix of whitespace issues in doc comments.\n     * @param array $lines\n     */\n    protected function fixDocBlockIndentation(&$lines)\n    {\n        $docBlock = false;\n        $codeBlock = false;\n        $listIndent = '';\n        $tag = false;\n        $indent = '';\n        foreach ($lines as $i => $line) {\n            if (preg_match('~^(\\s*)/\\*\\*$~', $line, $matches)) {\n                $docBlock = true;\n                $indent = $matches[1];\n            } elseif (preg_match('~^(\\s*)\\*+/~', $line)) {\n                if ($docBlock) { // could be the end of normal comment\n                    $lines[$i] = $indent . ' */';\n                }\n                $docBlock = false;\n                $codeBlock = false;\n                $listIndent = '';\n                $tag = false;\n            } elseif ($docBlock) {\n                $line = ltrim($line);\n                if (strpos($line, '*') === 0) {\n                    $line = substr($line, 1);\n                }\n                if (strpos($line, ' ') === 0) {\n                    $line = substr($line, 1);\n                }\n                $docLine = str_replace(\"\\t\", '    ', rtrim($line));\n                if (empty($docLine)) {\n                    $listIndent = '';\n                } elseif (strpos($docLine, '@') === 0) {\n                    $listIndent = '';\n                    $codeBlock = false;\n                    $tag = true;\n                    $docLine = preg_replace('/\\s+/', ' ', $docLine);\n                    $docLine = $this->fixParamTypes($docLine);\n                } elseif (preg_match('/^(~~~|```)/', $docLine)) {\n                    $codeBlock = !$codeBlock;\n                    $listIndent = '';\n                } elseif (preg_match('/^(\\s*)([0-9]+\\.|-|\\*|\\+) /', $docLine, $matches)) {\n                    $listIndent = str_repeat(' ', \\strlen($matches[0]));\n                    $tag = false;\n                    $lines[$i] = $indent . ' * ' . $docLine;\n                    continue;\n                }\n                if ($codeBlock) {\n                    $lines[$i] = rtrim($indent . ' * ' . $docLine);\n                } else {\n                    $lines[$i] = rtrim($indent . ' * ' . (empty($listIndent) && !$tag ? $docLine : ($listIndent . ltrim($docLine))));\n                }\n            }\n        }\n    }\n\n    /**\n     * @param string $line\n     * @return string\n     */\n    protected function fixParamTypes($line)\n    {\n        return preg_replace_callback('~@(param|return) ([\\w\\\\|]+)~i', function ($matches) {\n            $types = explode('|', $matches[2]);\n            foreach ($types as $i => $type) {\n                switch ($type) {\n                    case 'integer':\n                        $types[$i] = 'int';\n                        break;\n                    case 'boolean':\n                        $types[$i] = 'bool';\n                        break;\n                }\n            }\n\n            return '@' . $matches[1] . ' ' . implode('|', $types);\n        }, $line);\n    }\n\n    /**\n     * Fixes line spacing code style for properties and constants.\n     * @param string[] $lines\n     * @return string[]\n     */\n    protected function fixLineSpacing($lines)\n    {\n        $propertiesOnly = false;\n        // remove blank lines between properties\n        $skip = true;\n        $level = 0;\n        foreach ($lines as $i => $line) {\n            if (strpos($line, 'class ') !== false) {\n                $skip = false;\n            }\n            if ($skip) {\n                continue;\n            }\n\n            // keep spaces in multi line arrays\n            if (strpos($line, '*') === false && strncmp(trim($line), \"'SQLSTATE[\", 10) !== 0) {\n                $level += substr_count($line, '[') - substr_count($line, ']');\n            }\n\n            if (trim($line) === '') {\n                if ($level == 0) {\n                    unset($lines[$i]);\n                }\n            } elseif (ltrim($line)[0] !== '*' && strpos($line, 'function ') !== false) {\n                break;\n            } elseif (trim($line) === '}') {\n                $propertiesOnly = true;\n                break;\n            }\n        }\n        $lines = array_values($lines);\n\n        // add back some\n        $endofUse = false;\n        $endofConst = false;\n        $endofPublic = false;\n        $endofProtected = false;\n        $endofPrivate = false;\n        $skip = true;\n        $level = 0; // track array properties\n        $property = '';\n        foreach ($lines as $i => $line) {\n            if (strpos($line, 'class ') !== false) {\n                $skip = false;\n            }\n            if ($skip) {\n                continue;\n            }\n\n            // check for multi line array\n            if ($level > 0) {\n                ${'endof' . $property} = $i;\n            }\n\n            $line = trim($line);\n            if (strncmp($line, 'public $', 8) === 0 || strncmp($line, 'public static $', 15) === 0) {\n                $endofPublic = $i;\n                $property = 'Public';\n                $level = 0;\n            } elseif (strncmp($line, 'protected $', 11) === 0 || strncmp($line, 'protected static $', 18) === 0) {\n                $endofProtected = $i;\n                $property = 'Protected';\n                $level = 0;\n            } elseif (strncmp($line, 'private $', 9) === 0 || strncmp($line, 'private static $', 16) === 0) {\n                $endofPrivate = $i;\n                $property = 'Private';\n                $level = 0;\n            } elseif (strpos($line, 'const ') === 0) {\n                $endofConst = $i;\n                $property = false;\n            } elseif (strpos($line, 'use ') === 0) {\n                $endofUse = $i;\n                $property = false;\n            } elseif (strpos($line, '*') === 0) {\n                $property = false;\n            } elseif (strpos($line, '*') !== 0 && strpos($line, 'function ') !== false || $line === '}') {\n                break;\n            }\n\n            // check for multi line array\n            if ($property !== false && strncmp($line, \"'SQLSTATE[\", 10) !== 0) {\n                $level += substr_count($line, '[') - substr_count($line, ']');\n            }\n        }\n\n        $endofAll = false;\n        foreach (['Private', 'Protected', 'Public', 'Const', 'Use'] as $var) {\n            if (${'endof' . $var} !== false) {\n                $endofAll = ${'endof' . $var};\n                break;\n            }\n        }\n\n        //        $this->checkPropertyOrder($lineInfo);\n        $result = [];\n        foreach ($lines as $i => $line) {\n            $result[] = $line;\n            if (!($propertiesOnly && $i === $endofAll)) {\n                if (\n                    $i === $endofUse || $i === $endofConst || $i === $endofPublic ||\n                    $i === $endofProtected || $i === $endofPrivate\n                ) {\n                    $result[] = '';\n                }\n                if ($i === $endofAll) {\n                    $result[] = '';\n                }\n            }\n        }\n\n        return $result;\n    }\n\n    protected function checkPropertyOrder($lineInfo)\n    {\n        // TODO\n    }\n\n    protected function updateClassPropertyDocs($file, $className, $propertyDoc)\n    {\n        if ($this->shouldSkipClass($className)) {\n            $this->stderr(\"[INFO] Skipping class $className.\\n\", Console::FG_BLUE, Console::BOLD);\n            return false;\n        }\n\n        try {\n            $ref = new \\ReflectionClass($className);\n        } catch (\\Exception $e) {\n            $this->stderr(\"[ERR] Unable to create ReflectionClass for class '$className': \" . $e->getMessage() . \"\\n\", Console::FG_RED);\n            return false;\n        } catch (\\Error $e) {\n            $this->stderr(\"[ERR] Unable to create ReflectionClass for class '$className': \" . $e->getMessage() . \"\\n\", Console::FG_RED);\n            return false;\n        }\n        if ($ref->getFileName() != $file) {\n            $this->stderr(\"[ERR] Unable to create ReflectionClass for class: $className loaded class is not from file: $file\\n\", Console::FG_RED);\n            return false;\n        }\n\n        if ($this->isBaseObject($className, $ref)) {\n            $this->stderr(\"[INFO] Skipping class $className as it is not a subclass of yii\\\\base\\\\BaseObject.\\n\", Console::FG_BLUE, Console::BOLD);\n            return false;\n        }\n\n        if ($ref->isSubclassOf('yii\\db\\BaseActiveRecord')) {\n            $this->stderr(\"[INFO] Skipping class $className as it is an ActiveRecord class, property handling is not supported yet.\\n\", Console::FG_BLUE, Console::BOLD);\n            return false;\n        }\n\n        $oldDoc = $ref->getDocComment();\n        $newDoc = $this->cleanDocComment($this->updateDocComment($oldDoc, $propertyDoc, $className));\n\n        $seenSince = false;\n        $seenAuthor = false;\n\n        // TODO move these checks to different action\n        $lines = explode(\"\\n\", $newDoc);\n        $firstLine = trim($lines[1]);\n        if ($firstLine === '*' || strncmp($firstLine, '* @', 3) === 0) {\n            $this->stderr(\"[WARN] Class $className has no short description.\\n\", Console::FG_YELLOW, Console::BOLD);\n        }\n        foreach ($lines as $line) {\n            $line = trim($line);\n            if (strncmp($line, '* @since ', 9) === 0) {\n                $seenSince = true;\n            } elseif (strncmp($line, '* @author ', 10) === 0) {\n                $seenAuthor = true;\n            }\n        }\n\n        if (!$this->skipFrameworkRequirements) {\n            if (!$seenSince) {\n                $this->stderr(\"[ERR] No @since found in class doc in file: $file\\n\", Console::FG_RED);\n            }\n            if (!$seenAuthor) {\n                $this->stderr(\"[ERR] No @author found in class doc in file: $file\\n\", Console::FG_RED);\n            }\n        }\n\n        if (trim($oldDoc) != trim($newDoc)) {\n            $fileContent = explode(\"\\n\", file_get_contents($file));\n            $start = $ref->getStartLine() - 2;\n            $docStart = $start - \\count(explode(\"\\n\", $oldDoc)) + 1;\n\n            $newFileContent = [];\n            $n = \\count($fileContent);\n            for ($i = 0; $i < $n; $i++) {\n                if ($i > $start || $i < $docStart) {\n                    $newFileContent[] = $fileContent[$i];\n                } else {\n                    $newFileContent[] = trim($newDoc);\n                    $i = $start;\n                }\n            }\n\n            file_put_contents($file, implode(\"\\n\", $newFileContent));\n\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * remove multi empty lines and trim trailing whitespace.\n     *\n     * @param $doc\n     * @return string\n     */\n    protected function cleanDocComment($doc)\n    {\n        $lines = explode(\"\\n\", $doc);\n        $n = \\count($lines);\n        for ($i = 0; $i < $n; $i++) {\n            $lines[$i] = rtrim($lines[$i]);\n            if (trim($lines[$i]) == '*' && trim($lines[$i + 1]) == '*') {\n                unset($lines[$i]);\n            }\n        }\n\n        return implode(\"\\n\", $lines);\n    }\n\n    /**\n     * Replace property annotations in doc comment.\n     * @param $doc\n     * @param $properties\n     * @return string\n     */\n    protected function updateDocComment($doc, $properties, $className)\n    {\n        $manuallyAddedProperties = self::MANUALLY_ADDED_PROPERTIES[$className] ?? [];\n        $lines = explode(\"\\n\", $doc);\n        $propertyPart = false;\n        $propertyPosition = false;\n        $lastPropertyName = null;\n        $hasManuallyAddedProperties = false;\n\n        foreach ($lines as $i => $line) {\n            $line = trim($line);\n            if (strncmp($line, '* @property', 11) === 0) {\n                $propertyPart = true;\n            } elseif ($propertyPart && $line === '*') {\n                $propertyPosition = $i;\n                $propertyPart = false;\n            }\n            if (strncmp($line, '* @author ', 10) === 0 && $propertyPosition === false) {\n                $propertyPosition = $i - 1;\n                $propertyPart = false;\n            }\n            if ($propertyPart) {\n                preg_match(\n                    '/@property(?:-read|-write)?\\s+([\\\\\\\\\\w\\|\\[\\]]+)\\s+\\$([a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*)/',\n                    $line,\n                    $matches\n                );\n\n                if (isset($matches[2])) {\n                    $lastPropertyName = $matches[2];\n                }\n\n                if (in_array($lastPropertyName, $manuallyAddedProperties)) {\n                    $hasManuallyAddedProperties = true;\n                } else {\n                    unset($lines[$i]);\n                }\n            }\n        }\n\n        if ($properties === '') {\n            return implode(\"\\n\", $lines);\n        }\n\n        // if no properties or other tags were present add properties at the end\n        if ($propertyPosition === false) {\n            $propertyPosition = \\count($lines) - 2;\n        }\n\n        // if there are properties that were added manually, remove start enclosure\n        if ($hasManuallyAddedProperties) {\n            $properties = substr($properties, strlen(self::PROPERTIES_ENCLOSURE));\n        }\n\n        $finalDoc = '';\n        foreach ($lines as $i => $line) {\n            if (!$hasManuallyAddedProperties || $i !== $propertyPosition) {\n                $finalDoc .= $line . \"\\n\";\n            }\n\n            if ($i == $propertyPosition) {\n                $finalDoc .= $properties;\n            }\n        }\n\n        return $finalDoc;\n    }\n\n    protected function generateClassPropertyDocs($fileName)\n    {\n        $phpdoc = '';\n        $file = str_replace(\"\\r\", '', str_replace(\"\\t\", ' ', file_get_contents($fileName, true)));\n        $ns = $this->match('#\\nnamespace (?<name>[\\w\\\\\\\\]+);\\n#', $file);\n        $namespace = reset($ns);\n        if ($namespace === false) {\n            $namespace = '\\\\';\n        } else {\n            $namespace = $namespace['name'];\n        }\n        $classes = $this->match('#\\n(?:abstract )?(?:final )?class (?<name>\\w+)( extends .+)?( implements .+)?\\n\\{(?<content>.*)\\n\\}(\\n|$)#', $file);\n\n        if (\\count($classes) > 1) {\n            $this->stderr(\"[ERR] There should be only one class in a file: $fileName\\n\", Console::FG_RED);\n\n            return false;\n        }\n        if (\\count($classes) < 1) {\n            $interfaces = $this->match('#\\ninterface (?<name>\\w+)( extends .+)?\\n\\{(?<content>.*)\\n\\}(\\n|$)#', $file);\n            if (\\count($interfaces) == 1) {\n                return false;\n            }\n\n            if (\\count($interfaces) > 1) {\n                $this->stderr(\"[ERR] There should be only one interface in a file: $fileName\\n\", Console::FG_RED);\n            } else {\n                $traits = $this->match('#\\ntrait (?<name>\\w+)\\n\\{(?<content>.*)\\n\\}(\\n|$)#', $file);\n                if (\\count($traits) == 1) {\n                    return false;\n                }\n\n                if (\\count($traits) > 1) {\n                    $this->stderr(\"[ERR] There should be only one class/trait/interface in a file: $fileName\\n\", Console::FG_RED);\n                } else {\n                    $this->stderr(\"[ERR] No class in file: $fileName\\n\", Console::FG_RED);\n                }\n            }\n\n            return false;\n        }\n\n        $className = null;\n        foreach ($classes as &$class) {\n            $className = $namespace . '\\\\' . $class['name'];\n\n            $gets = $this->match(\n                '#\\* @return (?<type>' . self::TYPE_REG_EXP . ')'\n                    . '(?: (?<comment>(?:(?!\\*/|\\* @).)+?)(?:(?!\\*/).)+|[\\s\\n]*)((\\*\\n)|(\\*\\s.+))*\\*/'\n                    . '[\\s\\n]{2,}(\\#\\[\\\\\\\\*.+\\])*[\\s\\n]{2,}'\n                    . 'public function (?<kind>get)(?<name>\\w+)\\((?:,? ?\\$\\w+ ?= ?[^,]+)*\\)(\\:\\s*[\\w\\\\|\\\\\\\\\\\\[\\\\]]+)?#',\n                $class['content'],\n                true\n            );\n\n            $sets = $this->match(\n                '#\\* @param (?<type>' . self::TYPE_REG_EXP . ') \\$\\w+'\n                    . '(?: (?<comment>(?:(?!\\*/|\\* @).)+?)(?:(?!\\*/).)+|[\\s\\n]*)((\\*\\n)|(\\*\\s.+))*\\*/'\n                    . '[\\s\\n]{2,}(\\#\\[\\\\\\\\*.+\\])*[\\s\\n]{2,}'\n                    . 'public function (?<kind>set)(?<name>\\w+)\\(([\\w\\\\|\\\\\\\\\\\\[\\\\]]+\\s*)?\\$\\w+(?:, ?\\$\\w+ ?= ?[^,]+)*\\)(\\:\\s*[\\w\\\\|\\\\\\\\\\\\[\\\\]]+)?#',\n                $class['content'],\n                true\n            );\n\n            $acrs = array_merge($gets, $sets);\n            $manuallyAddedProperties = self::MANUALLY_ADDED_PROPERTIES[$className] ?? [];\n            $props = [];\n\n            foreach ($acrs as &$acr) {\n                $acr['name'] = lcfirst($acr['name']);\n                if (in_array($acr['name'], $manuallyAddedProperties)) {\n                    continue;\n                }\n\n                $acr['comment'] = trim(preg_replace('#(^|\\n)\\s+\\*\\s?#', '$1 * ', $acr['comment']));\n                $props[$acr['name']][$acr['kind']] = [\n                    'type' => $acr['type'],\n                    'comment' => $this->fixSentence($acr['comment']),\n                ];\n            }\n\n            if (\\count($props) === 0) {\n                continue;\n            }\n\n            ksort($props);\n\n            foreach ($props as $propName => &$prop) {\n                $docLine = ' * @property';\n                $note = '';\n                if (isset($prop['get'], $prop['set'])) {\n                    if ($prop['get']['type'] !== $prop['set']['type']) {\n                        $note = ' Note that the type of this property differs in getter and setter.'\n                            . ' See [[get' . ucfirst($propName) . '()]]'\n                            . ' and [[set' . ucfirst($propName) . '()]] for details.';\n                    }\n                } elseif (isset($prop['get'])) {\n                    if (!$this->hasSetterInParents($className, $propName)) {\n                        $docLine .= '-read';\n                    }\n                } elseif (isset($prop['set'])) {\n                    if (!$this->hasGetterInParents($className, $propName)) {\n                        $docLine .= '-write';\n                    }\n                } else {\n                    continue;\n                }\n                $docLine .= ' ' . $this->getPropParam($prop, 'type') . \" $$propName \";\n                $comment = explode(\"\\n\", $this->getPropParam($prop, 'comment') . $note);\n                foreach ($comment as &$cline) {\n                    $cline = ltrim(rtrim($cline), '* ');\n                }\n                $docLine = wordwrap($docLine . implode(' ', $comment), 110, \"\\n * \") . \"\\n\";\n\n                $phpdoc .= $docLine;\n            }\n        }\n\n        if ($phpdoc !== '') {\n            $phpdoc = self::PROPERTIES_ENCLOSURE . $phpdoc . self::PROPERTIES_ENCLOSURE;\n        }\n\n        return [$className, $phpdoc];\n    }\n\n    protected function match($pattern, $subject, $split = false)\n    {\n        $sets = [];\n        // split subject by double newlines because regex sometimes has problems with matching\n        // in the complete set of methods\n        // example: yii\\di\\ServiceLocator setComponents() is not recognized in the whole but in\n        // a part of the class.\n        $parts = $split ? explode(\"\\n\\n\", $subject) : [$subject];\n        foreach ($parts as $part) {\n            preg_match_all($pattern . 'suU', $part, $matches, PREG_SET_ORDER);\n            foreach ($matches as &$set) {\n                foreach ($set as $i => $match) {\n                    if (is_numeric($i) /*&& $i != 0*/) {\n                        unset($set[$i]);\n                    }\n                }\n\n                $sets[] = $set;\n            }\n        }\n\n        return $sets;\n    }\n\n    protected function fixSentence($str)\n    {\n        $str = rtrim($str, '*');\n        $str = rtrim($str);\n\n        // TODO fix word wrap\n        if ($str == '') {\n            return '';\n        }\n\n        return strtoupper(substr($str, 0, 1)) . substr($str, 1) . ($str[\\strlen($str) - 1] !== '.' ? '.' : '');\n    }\n\n    protected function getPropParam($prop, $param)\n    {\n        return isset($prop['property']) ? $prop['property'][$param] : (isset($prop['get']) ? $prop['get'][$param] : $prop['set'][$param]);\n    }\n\n    /**\n     * Generate a hash value (message digest)\n     * @param string $string message to be hashed.\n     * @return string calculated message digest.\n     */\n    private function hash($string)\n    {\n        if (!function_exists('hash')) {\n            return sha1($string);\n        }\n        return hash('sha256', $string);\n    }\n\n    /**\n     * @param string $className\n     * @param string $propName\n     * @return bool\n     */\n    protected function hasGetterInParents($className, $propName)\n    {\n        $class = $className;\n\n        try {\n            while ($parent = get_parent_class($class)) {\n                if (method_exists($parent, 'get' . ucfirst($propName))) {\n                    return true;\n                }\n                $class = $parent;\n            }\n        } catch (\\Throwable $t) {\n            $this->stderr(\"[ERR] Error when getting parents for $className\\n\", Console::FG_RED);\n            return false;\n        }\n        return false;\n    }\n\n    /**\n     * @param string $className\n     * @param string $propName\n     * @return bool\n     */\n    protected function hasSetterInParents($className, $propName)\n    {\n        $class = $className;\n\n        try {\n            while ($parent = get_parent_class($class)) {\n                if (method_exists($parent, 'set' . ucfirst($propName))) {\n                    return true;\n                }\n                $class = $parent;\n            }\n        } catch (\\Throwable $t) {\n            $this->stderr(\"[ERR] Error when getting parents for $className\\n\", Console::FG_RED);\n            return false;\n        }\n        return false;\n    }\n\n    /**\n     * @param string $className\n     * @param \\ReflectionClass<object> $ref\n     * @return bool\n     */\n    protected function isBaseObject($className, \\ReflectionClass $ref)\n    {\n        $isDeprecatedObject = false;\n        if (PHP_VERSION_ID <= 70100) {\n            $isDeprecatedObject = $ref->isSubclassOf('yii\\base\\Object') || $className === 'yii\\base\\Object';\n        }\n        return !$isDeprecatedObject && !$ref->isSubclassOf('yii\\base\\BaseObject') && $className !== 'yii\\base\\BaseObject';\n    }\n\n    private function shouldSkipClass($className)\n    {\n        if (PHP_VERSION_ID > 70100) {\n            return $className === 'yii\\base\\Object';\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "build/controllers/ReleaseController.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\build\\controllers;\n\nuse Yii;\nuse yii\\base\\Exception;\nuse yii\\console\\Application;\nuse yii\\console\\Controller;\nuse yii\\helpers\\ArrayHelper;\nuse yii\\helpers\\Console;\nuse yii\\helpers\\FileHelper;\n\n/**\n * ReleaseController is there to help to prepare releases.\n *\n * Get a version overview:\n *\n *     ./build/build release/info\n *\n * run it with `--update` to fetch tags for all repos:\n *\n *     ./build release/info --update\n *\n * Make a framework release (apps are always in line with framework):\n *\n *     ./build/build release framework\n *     ./build/build release app-basic\n *     ./build/build release app-advanced\n *\n * Make an extension release (e.g. for redis):\n *\n *     ./build/build release redis\n *\n * Be sure to check the help info for individual sub-commands:\n *\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0\n *\n * @extends Controller<Application>\n */\nclass ReleaseController extends Controller\n{\n    public $defaultAction = 'release';\n\n    /**\n     * @var string base path to use for releases.\n     */\n    public $basePath;\n    /**\n     * @var bool whether to make actual changes. If true, it will run without changing or pushing anything.\n     */\n    public $dryRun = false;\n    /**\n     * @var bool whether to fetch the latest tags.\n     */\n    public $update = false;\n    /**\n     * @var string override the default version. e.g. for major or patch releases.\n     */\n    public $version;\n\n\n    public function options($actionID)\n    {\n        $options = ['basePath'];\n        if ($actionID === 'release') {\n            $options[] = 'dryRun';\n            $options[] = 'version';\n        } elseif ($actionID === 'sort-changelog') {\n            $options[] = 'version';\n        } elseif ($actionID === 'info') {\n            $options[] = 'update';\n        }\n\n        return array_merge(parent::options($actionID), $options);\n    }\n\n\n    public function beforeAction($action)\n    {\n        if (!$this->interactive) {\n            throw new Exception('Sorry, but releases should be run interactively to ensure you actually verify what you are doing ;)');\n        }\n        if ($this->basePath === null) {\n            $this->basePath = \\dirname(\\dirname(__DIR__));\n        }\n        $this->basePath = rtrim($this->basePath, '\\\\/');\n        return parent::beforeAction($action);\n    }\n\n    /**\n     * Shows information about current framework and extension versions.\n     */\n    public function actionInfo()\n    {\n        $items = [\n            'framework',\n            'app-basic',\n            'app-advanced',\n        ];\n        $extensionPath = \"{$this->basePath}/extensions\";\n        foreach (scandir($extensionPath) as $extension) {\n            if (ctype_alpha($extension) && is_dir($extensionPath . '/' . $extension)) {\n                $items[] = $extension;\n            }\n        }\n\n        if ($this->update) {\n            foreach ($items as $item) {\n                $this->stdout(\"fetching tags for $item...\");\n                if ($item === 'framework') {\n                    $this->gitFetchTags((string)$this->basePath);\n                } elseif (strncmp('app-', $item, 4) === 0) {\n                    $this->gitFetchTags(\"{$this->basePath}/apps/\" . substr($item, 4));\n                } else {\n                    $this->gitFetchTags(\"{$this->basePath}/extensions/$item\");\n                }\n                $this->stdout(\"done.\\n\", Console::FG_GREEN, Console::BOLD);\n            }\n        } else {\n            $this->stdout(\"\\nInformation may be outdated, re-run with `--update` to fetch latest tags.\\n\\n\");\n        }\n\n        $versions = $this->getCurrentVersions($items);\n        $nextVersions = $this->getNextVersions($versions, self::PATCH);\n\n        // print version table\n        $w = $this->minWidth(array_keys($versions));\n        $this->stdout(str_repeat(' ', $w + 2) . \"Current Version  Next Version\\n\", Console::BOLD);\n        foreach ($versions as $ext => $version) {\n            $this->stdout($ext . str_repeat(' ', $w + 3 - mb_strlen($ext)) . $version . '');\n            $this->stdout(str_repeat(' ', 17 - mb_strlen($version)) . $nextVersions[$ext] . \"\\n\");\n        }\n    }\n\n    private function minWidth($a)\n    {\n        $w = 1;\n        foreach ($a as $s) {\n            if (($l = mb_strlen($s)) > $w) {\n                $w = $l;\n            }\n        }\n\n        return $w;\n    }\n\n    /**\n     * Automation tool for making Yii framework and official extension releases.\n     *\n     * Usage:\n     *\n     * To make a release, make sure your git is clean (no uncommitted changes) and run the following command in\n     * the yii dev repo root:\n     *\n     * ```\n     * ./build/build release framework\n     * ```\n     *\n     * or\n     *\n     * ```\n     * ./build/build release redis,bootstrap,apidoc\n     * ```\n     *\n     * You may use the `--dryRun` switch to test the command without changing or pushing anything:\n     *\n     * ```\n     * ./build/build release redis --dryRun\n     * ```\n     *\n     * The command will guide you through the complete release process including changing of files,\n     * committing and pushing them. Each git command must be confirmed and can be skipped individually.\n     * You may adjust changes in a separate shell or your IDE while the command is waiting for confirmation.\n     *\n     * @param array $what what do you want to release? this can either be:\n     *\n     * - an extension name such as `redis` or `bootstrap`,\n     * - an application indicated by prefix `app-`, e.g. `app-basic`,\n     * - or `framework` if you want to release a new version of the framework itself.\n     *\n     * @return int\n     */\n    public function actionRelease(array $what)\n    {\n        if (\\count($what) > 1) {\n            $this->stdout(\"Currently only one simultaneous release is supported.\\n\");\n            return 1;\n        }\n\n        $this->stdout(\"This is the Yii release manager\\n\\n\", Console::BOLD);\n\n        if ($this->dryRun) {\n            $this->stdout(\"Running in \\\"dry-run\\\" mode, nothing will actually be changed.\\n\\n\", Console::BOLD, Console::FG_GREEN);\n        }\n\n        $this->validateWhat($what);\n        $versions = $this->getCurrentVersions($what);\n\n        if ($this->version !== null) {\n            // if a version is explicitly given\n            $newVersions = [];\n            foreach ($versions as $k => $v) {\n                $newVersions[$k] = $this->version;\n            }\n        } else {\n            // otherwise, get next patch or minor\n            $newVersions = $this->getNextVersions($versions, self::PATCH);\n        }\n\n        $this->stdout(\"You are about to prepare a new release for the following things:\\n\\n\");\n        $this->printWhat($what, $newVersions, $versions);\n        $this->stdout(\"\\n\");\n\n        $this->stdout(\"Before you make a release briefly go over the changes and check if you spot obvious mistakes:\\n\\n\", Console::BOLD);\n        $gitDir = reset($what) === 'framework' ? 'framework/' : '';\n        $gitVersion = $versions[reset($what)];\n        if (strncmp('app-', reset($what), 4) !== 0) {\n            $this->stdout(\"- no accidentally added CHANGELOG lines for other versions than this one?\\n\\n    git diff $gitVersion.. {$gitDir}CHANGELOG.md\\n\\n\");\n            $this->stdout(\"- are all new `@since` tags for this release version?\\n\");\n        }\n        $this->stdout(\"- other issues with code changes?\\n\\n    git diff -w $gitVersion.. {$gitDir}\\n\\n\");\n        $travisUrl = reset($what) === 'framework' ? '' : '-' . reset($what);\n        $this->stdout(\"- are unit tests passing on travis? https://travis-ci.com/yiisoft/yii2$travisUrl/builds\\n\");\n        $this->stdout(\"- also make sure the milestone on github is complete and no issues or PRs are left open.\\n\\n\");\n        $this->printWhatUrls($what, $versions);\n        $this->stdout(\"\\n\");\n\n        if (!$this->confirm('When you continue, this tool will run cleanup jobs and update the changelog as well as other files (locally). Continue?', false)) {\n            $this->stdout(\"Canceled.\\n\");\n            return 1;\n        }\n\n        foreach ($what as $ext) {\n            if ($ext === 'framework') {\n                $this->releaseFramework(\"{$this->basePath}/framework\", $newVersions['framework']);\n            } elseif (strncmp('app-', $ext, 4) === 0) {\n                $this->releaseApplication(substr($ext, 4), \"{$this->basePath}/apps/\" . substr($ext, 4), $newVersions[$ext]);\n            } else {\n                $this->releaseExtension($ext, \"{$this->basePath}/extensions/$ext\", $newVersions[$ext]);\n            }\n        }\n\n        return 0;\n    }\n\n    /**\n     * This will generate application packages for download page.\n     *\n     * Usage:\n     *\n     * ```\n     * ./build/build release/package app-basic\n     * ```\n     *\n     * @param array $what what do you want to package? this can either be:\n     *\n     * - an application indicated by prefix `app-`, e.g. `app-basic`,\n     *\n     * @return int\n     */\n    public function actionPackage(array $what)\n    {\n        $this->validateWhat($what, ['app']);\n        $versions = $this->getCurrentVersions($what);\n\n        $this->stdout(\"You are about to generate packages for the following things:\\n\\n\");\n        foreach ($what as $ext) {\n            if (strncmp('app-', $ext, 4) === 0) {\n                $this->stdout(' - ');\n                $this->stdout(substr($ext, 4), Console::FG_RED);\n                $this->stdout(' application version ');\n            } elseif ($ext === 'framework') {\n                $this->stdout(' - Yii Framework version ');\n            } else {\n                $this->stdout(' - ');\n                $this->stdout($ext, Console::FG_RED);\n                $this->stdout(' extension version ');\n            }\n            $this->stdout($versions[$ext], Console::BOLD);\n            $this->stdout(\"\\n\");\n        }\n        $this->stdout(\"\\n\");\n\n        $packagePath = \"{$this->basePath}/packages\";\n        $this->stdout(\"Packages will be stored in $packagePath\\n\\n\");\n\n        if (!$this->confirm('Continue?', false)) {\n            $this->stdout(\"Canceled.\\n\");\n            return 1;\n        }\n\n        foreach ($what as $ext) {\n            if ($ext === 'framework') {\n                throw new Exception('Can not package framework.');\n            } elseif (strncmp('app-', $ext, 4) === 0) {\n                $this->packageApplication(substr($ext, 4), $versions[$ext], $packagePath);\n            } else {\n                throw new Exception('Can not package extension.');\n            }\n        }\n\n        $this->stdout(\"\\ndone. verify the versions composer installed above and push it to github!\\n\\n\");\n\n        return 0;\n    }\n\n    /**\n     * Sorts CHANGELOG for framework or extension.\n     *\n     * @param array $what what do you want to resort changelog for? this can either be:\n     *\n     * - an extension name such as `redis` or `bootstrap`,\n     * - or `framework` if you want to release a new version of the framework itself.\n     */\n    public function actionSortChangelog(array $what)\n    {\n        if (\\count($what) > 1) {\n            $this->stdout(\"Currently only one simultaneous release is supported.\\n\");\n            return 1;\n        }\n        $this->validateWhat($what, ['framework', 'ext'], false);\n\n        $version = $this->version ?: array_values($this->getNextVersions($this->getCurrentVersions($what), self::PATCH))[0];\n        $this->stdout('sorting CHANGELOG of ');\n        $this->stdout(reset($what), Console::BOLD);\n        $this->stdout(' for version ');\n        $this->stdout($version, Console::BOLD);\n        $this->stdout('...');\n\n        $this->resortChangelogs($what, $version);\n\n        $this->stdout(\"done.\\n\", Console::BOLD, Console::FG_GREEN);\n    }\n\n    protected function printWhat(array $what, $newVersions, $versions)\n    {\n        foreach ($what as $ext) {\n            if (strncmp('app-', $ext, 4) === 0) {\n                $this->stdout(' - ');\n                $this->stdout(substr($ext, 4), Console::FG_RED);\n                $this->stdout(' application version ');\n            } elseif ($ext === 'framework') {\n                $this->stdout(' - Yii Framework version ');\n            } else {\n                $this->stdout(' - ');\n                $this->stdout($ext, Console::FG_RED);\n                $this->stdout(' extension version ');\n            }\n            $this->stdout($newVersions[$ext], Console::BOLD);\n            $this->stdout(\", last release was {$versions[$ext]}\\n\");\n        }\n    }\n\n    protected function printWhatUrls(array $what, $oldVersions)\n    {\n        foreach ($what as $ext) {\n            if ($ext === 'framework') {\n                $this->stdout(\"framework:    https://github.com/yiisoft/yii2-framework/compare/{$oldVersions[$ext]}...master\\n\");\n                $this->stdout(\"app-basic:    https://github.com/yiisoft/yii2-app-basic/compare/{$oldVersions[$ext]}...master\\n\");\n                $this->stdout(\"app-advanced: https://github.com/yiisoft/yii2-app-advanced/compare/{$oldVersions[$ext]}...master\\n\");\n            } else {\n                $this->stdout($ext, Console::FG_RED);\n                $this->stdout(\": https://github.com/yiisoft/yii2-$ext/compare/{$oldVersions[$ext]}...master\\n\");\n            }\n        }\n    }\n\n    /**\n     * @param array $what list of items\n     * @param array $limit list of things to allow, or empty to allow any, can be `app`, `framework`, `extension`\n     * @param bool $ensureGitClean\n     * @throws \\yii\\base\\Exception\n     */\n    protected function validateWhat(array $what, $limit = [], $ensureGitClean = true)\n    {\n        foreach ($what as $w) {\n            if (strncmp('app-', $w, 4) === 0) {\n                if (!empty($limit) && !\\in_array('app', $limit)) {\n                    throw new Exception('Only the following types are allowed: ' . implode(', ', $limit) . \"\\n\");\n                }\n                if (!is_dir($appPath = \"{$this->basePath}/apps/\" . substr($w, 4))) {\n                    throw new Exception(\"Application path does not exist: \\\"{$appPath}\\\"\\n\");\n                }\n                if ($ensureGitClean) {\n                    $this->ensureGitClean($appPath);\n                }\n            } elseif ($w === 'framework') {\n                if (!empty($limit) && !\\in_array('framework', $limit)) {\n                    throw new Exception('Only the following types are allowed: ' . implode(', ', $limit) . \"\\n\");\n                }\n                if (!is_dir($fwPath = \"{$this->basePath}/framework\")) {\n                    throw new Exception(\"Framework path does not exist: \\\"{$this->basePath}/framework\\\"\\n\");\n                }\n                if ($ensureGitClean) {\n                    $this->ensureGitClean($fwPath);\n                }\n            } else {\n                if (!empty($limit) && !\\in_array('ext', $limit)) {\n                    throw new Exception('Only the following types are allowed: ' . implode(', ', $limit) . \"\\n\");\n                }\n                if (!is_dir($extPath = \"{$this->basePath}/extensions/$w\")) {\n                    throw new Exception(\"Extension path for \\\"$w\\\" does not exist: \\\"{$this->basePath}/extensions/$w\\\"\\n\");\n                }\n                if ($ensureGitClean) {\n                    $this->ensureGitClean($extPath);\n                }\n            }\n        }\n    }\n\n\n    protected function releaseFramework($frameworkPath, $version)\n    {\n        $this->stdout(\"\\n\");\n        $this->stdout($h = \"Preparing framework release version $version\", Console::BOLD);\n        $this->stdout(\"\\n\" . str_repeat('-', \\strlen($h)) . \"\\n\\n\", Console::BOLD);\n\n        if (!$this->confirm('Make sure you are on the right branch for this release and that it tracks the correct remote branch! Continue?')) {\n            exit(1);\n        }\n        $this->runGit('git pull', $frameworkPath);\n\n        // checks\n\n        $this->stdout('check if framework composer.json matches yii2-dev composer.json...');\n        $this->checkComposer($frameworkPath);\n        $this->stdout(\"done.\\n\", Console::FG_GREEN, Console::BOLD);\n\n        // adjustments\n\n        $this->stdout('prepare classmap...', Console::BOLD);\n        $this->dryRun || Yii::$app->runAction('classmap', [$frameworkPath]);\n        $this->stdout(\"done.\\n\", Console::FG_GREEN, Console::BOLD);\n\n        $this->stdout('updating mimetype magic file and mime aliases...', Console::BOLD);\n        $this->dryRun || Yii::$app->runAction('mime-type', [\"$frameworkPath/helpers/mimeTypes.php\"]);\n        $this->stdout(\"done.\\n\", Console::FG_GREEN, Console::BOLD);\n\n        $this->stdout(\"fixing various PHPDoc style issues...\\n\", Console::BOLD);\n        $this->dryRun || Yii::$app->runAction('php-doc/fix', [$frameworkPath]);\n        $this->stdout(\"done.\\n\", Console::FG_GREEN, Console::BOLD);\n\n        $this->stdout(\"updating PHPDoc @property annotations...\\n\", Console::BOLD);\n        $this->dryRun || Yii::$app->runAction('php-doc/property', [$frameworkPath]);\n        $this->stdout(\"done.\\n\", Console::FG_GREEN, Console::BOLD);\n\n        $this->stdout('sorting changelogs...', Console::BOLD);\n        $this->dryRun || $this->resortChangelogs(['framework'], $version);\n        $this->stdout(\"done.\\n\", Console::FG_GREEN, Console::BOLD);\n\n        $this->stdout('closing changelogs...', Console::BOLD);\n        $this->dryRun || $this->closeChangelogs(['framework'], $version);\n        $this->stdout(\"done.\\n\", Console::FG_GREEN, Console::BOLD);\n\n        $this->stdout('updating Yii version...');\n        $this->dryRun || $this->updateYiiVersion($frameworkPath, $version);\n        $this->stdout(\"done.\\n\", Console::FG_GREEN, Console::BOLD);\n\n        $this->stdout(\"\\nIn the following you can check the above changes using git diff.\\n\\n\");\n        do {\n            $this->runGit('git diff --color', $frameworkPath);\n            $this->stdout(\"\\n\\n\\nCheck whether the above diff is okay, if not you may change things as needed before continuing.\\n\");\n            $this->stdout(\"You may abort the program with Ctrl + C and reset the changes by running `git checkout -- .` in the repo.\\n\\n\");\n        } while (!$this->confirm('Type `yes` to continue, `no` to view git diff again. Continue?'));\n\n        $this->stdout(\"\\n\\n\");\n        $this->stdout(\"    ****          RELEASE TIME!         ****\\n\", Console::FG_YELLOW, Console::BOLD);\n        $this->stdout(\"    ****    Commit, Tag and Push it!    ****\\n\", Console::FG_YELLOW, Console::BOLD);\n        $this->stdout(\"\\n\\nHint: if you decide 'no' for any of the following, the command will not be executed. You may manually run them later if needed. E.g. try the release locally without pushing it.\\n\\n\");\n\n        $this->stdout(\"Make sure to have your git set up for GPG signing. The following tag and commit should be signed.\\n\\n\");\n\n        $this->runGit(\"git commit -S -a -m \\\"release version $version\\\"\", $frameworkPath);\n        $this->runGit(\"git tag -s $version -m \\\"version $version\\\"\", $frameworkPath);\n        $this->runGit('git push', $frameworkPath);\n        $this->runGit('git push --tags', $frameworkPath);\n\n        $this->stdout(\"\\n\\n\");\n        $this->stdout('CONGRATULATIONS! You have just released ', Console::FG_YELLOW, Console::BOLD);\n        $this->stdout('framework', Console::FG_RED, Console::BOLD);\n        $this->stdout(' version ', Console::FG_YELLOW, Console::BOLD);\n        $this->stdout($version, Console::BOLD);\n        $this->stdout(\"!\\n\\n\", Console::FG_YELLOW, Console::BOLD);\n\n        // TODO release applications\n        // $this->composerSetStability($what, $version);\n\n\n        //        $this->resortChangelogs($what, $version);\n        //        $this->closeChangelogs($what, $version);\n        //        $this->composerSetStability($what, $version);\n        //        if (in_array('framework', $what)) {\n        //            $this->updateYiiVersion($version);\n        //        }\n\n\n        // if done:\n        //     * ./build/build release/done framework 2.0.0-dev 2.0.0-rc\n        //     * ./build/build release/done redis 2.0.0-dev 2.0.0-rc\n        //            $this->openChangelogs($what, $nextVersion);\n        //            $this->composerSetStability($what, 'dev');\n        //            if (in_array('framework', $what)) {\n        //                $this->updateYiiVersion($devVersion);\n        //            }\n\n\n\n        // prepare next release\n\n        $this->stdout(\"Time to prepare the next release...\\n\\n\", Console::FG_YELLOW, Console::BOLD);\n\n        $this->stdout('opening changelogs...', Console::BOLD);\n        $nextVersion = $this->getNextVersions(['framework' => $version], self::PATCH); // TODO support other versions\n        $this->dryRun || $this->openChangelogs(['framework'], $nextVersion['framework']);\n        $this->stdout(\"done.\\n\", Console::FG_GREEN, Console::BOLD);\n\n        $this->stdout('updating Yii version...');\n        $this->dryRun || $this->updateYiiVersion($frameworkPath, $nextVersion['framework'] . '-dev');\n        $this->stdout(\"done.\\n\", Console::FG_GREEN, Console::BOLD);\n\n\n        $this->stdout(\"\\n\");\n        $this->runGit('git diff --color', $frameworkPath);\n        $this->stdout(\"\\n\\n\");\n        $this->runGit('git commit -a -m \"prepare for next release\"', $frameworkPath);\n        $this->runGit('git push', $frameworkPath);\n\n        $this->stdout(\"\\n\\nDONE!\", Console::FG_YELLOW, Console::BOLD);\n\n        $this->stdout(\"\\n\\nThe following steps are left for you to do manually:\\n\\n\");\n        $nextVersion2 = $this->getNextVersions($nextVersion, self::PATCH); // TODO support other versions\n        $this->stdout(\"- wait for your changes to be propagated to the repo and create a tag $version on  https://github.com/yiisoft/yii2-framework\\n\\n\");\n        $this->stdout(\"    git clone git@github.com:yiisoft/yii2-framework.git\\n\");\n        $this->stdout(\"    cd yii2-framework/\\n\");\n\n        $grepVersion = preg_quote($version, '~');\n        $this->stdout(\"    export RELEASECOMMIT=$(git log --oneline |grep \\\"$grepVersion\\\" | grep -Po \\\"^[0-9a-f]+\\\")\\n\");\n        $this->stdout(\"    git tag -s $version -m \\\"version $version\\\" \\$RELEASECOMMIT\\n\");\n        $this->stdout(\"    git tag --verify $version\\n\");\n        $this->stdout(\"    git push --tags\\n\\n\");\n        $this->stdout(\"- close the $version milestone on github and open new ones for {$nextVersion['framework']} and {$nextVersion2['framework']}: https://github.com/yiisoft/yii2/milestones\\n\");\n        $this->stdout(\"- create a release on github.\\n\");\n        $this->stdout(\"- release news and announcement.\\n\");\n        $this->stdout(\"- update the website (will be automated soon and is only relevant for the new website).\\n\");\n        $this->stdout(\"  https://github.com/yiisoft-contrib/yiiframework.com/blob/master/config/versions.php#L69\\n\");\n        $this->stdout(\"\\n\");\n        $this->stdout(\"- release applications: ./build/build release app-basic\\n\");\n        $this->stdout(\"- release applications: ./build/build release app-advanced\\n\");\n\n        $this->stdout(\"\\n\");\n    }\n\n    protected function releaseApplication($name, $path, $version)\n    {\n        $this->stdout(\"\\n\");\n        $this->stdout($h = \"Preparing release for application  $name  version $version\", Console::BOLD);\n        $this->stdout(\"\\n\" . str_repeat('-', \\strlen($h)) . \"\\n\\n\", Console::BOLD);\n\n        if (!$this->confirm('Make sure you are on the right branch for this release and that it tracks the correct remote branch! Continue?')) {\n            exit(1);\n        }\n        $this->runGit('git pull', $path);\n\n        // adjustments\n\n        $this->stdout(\"fixing various PHPDoc style issues...\\n\", Console::BOLD);\n        $this->setAppAliases($name, $path);\n        $this->dryRun || Yii::$app->runAction('php-doc/fix', [$path, 'skipFrameworkRequirements' => true]);\n        $this->resetAppAliases();\n        $this->stdout(\"done.\\n\", Console::FG_GREEN, Console::BOLD);\n\n        $this->stdout(\"updating PHPDoc @property annotations...\\n\", Console::BOLD);\n        $this->setAppAliases($name, $path);\n        $this->dryRun || Yii::$app->runAction('php-doc/property', [$path, 'skipFrameworkRequirements' => true]);\n        $this->resetAppAliases();\n        $this->stdout(\"done.\\n\", Console::FG_GREEN, Console::BOLD);\n\n        $this->stdout(\"updating composer stability...\\n\", Console::BOLD);\n        $this->dryRun || $this->composerSetStability([\"app-$name\"], $version);\n        $this->stdout(\"done.\\n\", Console::FG_GREEN, Console::BOLD);\n\n        $this->stdout(\"\\nIn the following you can check the above changes using git diff.\\n\\n\");\n        do {\n            $this->runGit('git diff --color', $path);\n            $this->stdout(\"\\n\\n\\nCheck whether the above diff is okay, if not you may change things as needed before continuing.\\n\");\n            $this->stdout(\"You may abort the program with Ctrl + C and reset the changes by running `git checkout -- .` in the repo.\\n\\n\");\n        } while (!$this->confirm('Type `yes` to continue, `no` to view git diff again. Continue?'));\n\n        $this->stdout(\"\\n\\n\");\n        $this->stdout(\"    ****          RELEASE TIME!         ****\\n\", Console::FG_YELLOW, Console::BOLD);\n        $this->stdout(\"    ****    Commit, Tag and Push it!    ****\\n\", Console::FG_YELLOW, Console::BOLD);\n        $this->stdout(\"\\n\\nHint: if you decide 'no' for any of the following, the command will not be executed. You may manually run them later if needed. E.g. try the release locally without pushing it.\\n\\n\");\n\n        $this->stdout(\"Make sure to have your git set up for GPG signing. The following tag and commit should be signed.\\n\\n\");\n\n        $this->runGit(\"git commit -S -a -m \\\"release version $version\\\"\", $path);\n        $this->runGit(\"git tag -s $version -m \\\"version $version\\\"\", $path);\n        $this->runGit('git push', $path);\n        $this->runGit('git push --tags', $path);\n\n        $this->stdout(\"\\n\\n\");\n        $this->stdout('CONGRATULATIONS! You have just released application ', Console::FG_YELLOW, Console::BOLD);\n        $this->stdout($name, Console::FG_RED, Console::BOLD);\n        $this->stdout(' version ', Console::FG_YELLOW, Console::BOLD);\n        $this->stdout($version, Console::BOLD);\n        $this->stdout(\"!\\n\\n\", Console::FG_YELLOW, Console::BOLD);\n\n        // prepare next release\n\n        $this->stdout(\"Time to prepare the next release...\\n\\n\", Console::FG_YELLOW, Console::BOLD);\n\n        $this->stdout(\"updating composer stability...\\n\", Console::BOLD);\n        $this->dryRun || $this->composerSetStability([\"app-$name\"], 'dev');\n        $this->stdout(\"done.\\n\", Console::FG_GREEN, Console::BOLD);\n\n        $nextVersion = $this->getNextVersions([\"app-$name\" => $version], self::PATCH); // TODO support other versions\n\n        $this->stdout(\"\\n\");\n        $this->runGit('git diff --color', $path);\n        $this->stdout(\"\\n\\n\");\n        $this->runGit('git commit -a -m \"prepare for next release\"', $path);\n        $this->runGit('git push', $path);\n\n        $this->stdout(\"\\n\\nDONE!\", Console::FG_YELLOW, Console::BOLD);\n\n        $this->stdout(\"\\n\\nThe following steps are left for you to do manually:\\n\\n\");\n        $nextVersion2 = $this->getNextVersions($nextVersion, self::PATCH); // TODO support other versions\n        $this->stdout(\"- close the $version milestone on github and open new ones for {$nextVersion[\"app-$name\"]} and {$nextVersion2[\"app-$name\"]}: https://github.com/yiisoft/yii2-app-$name/milestones\\n\");\n        $this->stdout(\"- Create Application packages and upload them to framework release at github:  ./build/build release/package app-$name\\n\");\n\n        $this->stdout(\"\\n\");\n    }\n\n    private $_oldAlias;\n\n    protected function setAppAliases($app, $path)\n    {\n        $this->_oldAlias = Yii::getAlias('@app');\n        switch ($app) {\n            case 'basic':\n                Yii::setAlias('@app', $path);\n                break;\n            case 'advanced':\n                // setup @frontend, @backend etc...\n                require \"$path/common/config/bootstrap.php\";\n                break;\n        }\n    }\n\n    protected function resetAppAliases()\n    {\n        Yii::setAlias('@app', $this->_oldAlias);\n    }\n\n    protected function packageApplication($name, $version, $packagePath)\n    {\n        FileHelper::createDirectory($packagePath);\n\n        $this->runCommand(\"composer create-project yiisoft/yii2-app-$name $name $version\", $packagePath);\n        // clear cookie validation key in basic app\n        if (is_file($configFile = \"$packagePath/$name/config/web.php\")) {\n            $this->sed(\n                \"/'cookieValidationKey' => '.*?',/\",\n                \"'cookieValidationKey' => '',\",\n                $configFile\n            );\n        }\n        $this->runCommand(\"tar zcf yii-$name-app-$version.tgz $name\", $packagePath);\n    }\n\n    protected function releaseExtension($name, $path, $version)\n    {\n        $this->stdout(\"\\n\");\n        $this->stdout($h = \"Preparing release for extension  $name  version $version\", Console::BOLD);\n        $this->stdout(\"\\n\" . str_repeat('-', \\strlen($h)) . \"\\n\\n\", Console::BOLD);\n\n        if (!$this->confirm('Make sure you are on the right branch for this release and that it tracks the correct remote branch! Continue?')) {\n            exit(1);\n        }\n        $this->runGit('git pull', $path);\n\n        // adjustments\n\n        $this->stdout(\"fixing various PHPDoc style issues...\\n\", Console::BOLD);\n        $this->dryRun || Yii::$app->runAction('php-doc/fix', [$path]);\n        $this->stdout(\"done.\\n\", Console::FG_GREEN, Console::BOLD);\n\n        $this->stdout(\"updating PHPDoc @property annotations...\\n\", Console::BOLD);\n        $this->dryRun || Yii::$app->runAction('php-doc/property', [$path]);\n        $this->stdout(\"done.\\n\", Console::FG_GREEN, Console::BOLD);\n\n        $this->stdout('sorting changelogs...', Console::BOLD);\n        $this->dryRun || $this->resortChangelogs([$name], $version);\n        $this->stdout(\"done.\\n\", Console::FG_GREEN, Console::BOLD);\n\n        $this->stdout('closing changelogs...', Console::BOLD);\n        $this->dryRun || $this->closeChangelogs([$name], $version);\n        $this->stdout(\"done.\\n\", Console::FG_GREEN, Console::BOLD);\n\n        $this->stdout(\"\\nIn the following you can check the above changes using git diff.\\n\\n\");\n        do {\n            $this->runGit('git diff --color', $path);\n            $this->stdout(\"\\n\\n\\nCheck whether the above diff is okay, if not you may change things as needed before continuing.\\n\");\n            $this->stdout(\"You may abort the program with Ctrl + C and reset the changes by running `git checkout -- .` in the repo.\\n\\n\");\n        } while (!$this->confirm('Type `yes` to continue, `no` to view git diff again. Continue?'));\n\n        $this->stdout(\"\\n\\n\");\n        $this->stdout(\"    ****          RELEASE TIME!         ****\\n\", Console::FG_YELLOW, Console::BOLD);\n        $this->stdout(\"    ****    Commit, Tag and Push it!    ****\\n\", Console::FG_YELLOW, Console::BOLD);\n        $this->stdout(\"\\n\\nHint: if you decide 'no' for any of the following, the command will not be executed. You may manually run them later if needed. E.g. try the release locally without pushing it.\\n\\n\");\n\n        $this->stdout(\"Make sure to have your git set up for GPG signing. The following tag and commit should be signed.\\n\\n\");\n\n        $this->runGit(\"git commit -S -a -m \\\"release version $version\\\"\", $path);\n        $this->runGit(\"git tag -s $version -m \\\"version $version\\\"\", $path);\n        $this->runGit('git push', $path);\n        $this->runGit('git push --tags', $path);\n\n        $this->stdout(\"\\n\\n\");\n        $this->stdout('CONGRATULATIONS! You have just released extension ', Console::FG_YELLOW, Console::BOLD);\n        $this->stdout($name, Console::FG_RED, Console::BOLD);\n        $this->stdout(' version ', Console::FG_YELLOW, Console::BOLD);\n        $this->stdout($version, Console::BOLD);\n        $this->stdout(\"!\\n\\n\", Console::FG_YELLOW, Console::BOLD);\n\n        // prepare next release\n\n        $this->stdout(\"Time to prepare the next release...\\n\\n\", Console::FG_YELLOW, Console::BOLD);\n\n        $this->stdout('opening changelogs...', Console::BOLD);\n        $nextVersion = $this->getNextVersions([$name => $version], self::PATCH); // TODO support other versions\n        $this->dryRun || $this->openChangelogs([$name], $nextVersion[$name]);\n        $this->stdout(\"done.\\n\", Console::FG_GREEN, Console::BOLD);\n\n        $this->stdout(\"\\n\");\n        $this->runGit('git diff --color', $path);\n        $this->stdout(\"\\n\\n\");\n        $this->runGit('git commit -a -m \"prepare for next release\"', $path);\n        $this->runGit('git push', $path);\n\n        $this->stdout(\"\\n\\nDONE!\", Console::FG_YELLOW, Console::BOLD);\n\n        $this->stdout(\"\\n\\nThe following steps are left for you to do manually:\\n\\n\");\n        $nextVersion2 = $this->getNextVersions($nextVersion, self::PATCH); // TODO support other versions\n        $this->stdout(\"- close the $version milestone on github and open new ones for {$nextVersion[$name]} and {$nextVersion2[$name]}: https://github.com/yiisoft/yii2-$name/milestones\\n\");\n        $this->stdout(\"- release news and announcement.\\n\");\n        $this->stdout(\"- update the website (will be automated soon and is only relevant for the new website).\\n\");\n\n        $this->stdout(\"\\n\");\n    }\n\n\n    protected function runCommand($cmd, $path)\n    {\n        $this->stdout(\"running  $cmd  ...\", Console::BOLD);\n        if ($this->dryRun) {\n            $this->stdout(\"dry run, command `$cmd` not executed.\\n\");\n            return;\n        }\n        chdir($path);\n        exec($cmd, $output, $ret);\n        if ($ret != 0) {\n            echo implode(\"\\n\", $output);\n            throw new Exception(\"Command \\\"$cmd\\\" failed with code \" . $ret);\n        }\n        $this->stdout(\"\\ndone.\\n\", Console::BOLD, Console::FG_GREEN);\n    }\n\n    protected function runGit($cmd, $path)\n    {\n        if ($this->confirm(\"Run `$cmd`?\", true)) {\n            if ($this->dryRun) {\n                $this->stdout(\"dry run, command `$cmd` not executed.\\n\");\n                return;\n            }\n            chdir($path);\n            exec($cmd, $output, $ret);\n            echo implode(\"\\n\", $output);\n            if ($ret != 0) {\n                throw new Exception(\"Command \\\"$cmd\\\" failed with code \" . $ret);\n            }\n            echo \"\\n\";\n        }\n    }\n\n    protected function ensureGitClean($path)\n    {\n        chdir($path);\n        exec('git status --porcelain -uno', $changes, $ret);\n        if ($ret != 0) {\n            throw new Exception('Command \"git status --porcelain -uno\" failed with code ' . $ret);\n        }\n        if (!empty($changes)) {\n            throw new Exception(\"You have uncommitted changes in $path: \" . print_r($changes, true));\n        }\n    }\n\n    protected function gitFetchTags($path)\n    {\n        try {\n            chdir($path);\n        } catch (\\yii\\base\\ErrorException $e) {\n            throw new Exception('Failed to fetch git tags in ' . $path . ': ' . $e->getMessage());\n        }\n        exec('git fetch --tags', $output, $ret);\n        if ($ret != 0) {\n            throw new Exception('Command \"git fetch --tags\" failed with code ' . $ret);\n        }\n    }\n\n\n    protected function checkComposer($fwPath)\n    {\n        if (!$this->confirm(\"\\nNot yet automated: Please check if composer.json dependencies in framework dir match the one in repo root. Continue?\", false)) {\n            exit;\n        }\n    }\n\n\n    protected function closeChangelogs($what, $version)\n    {\n        $v = str_replace('\\\\-', '[\\\\- ]', preg_quote($version, '/'));\n        $headline = $version . ' ' . date('F d, Y');\n        $this->sed(\n            '/' . $v . ' under development\\R(-+?)\\R/',\n            $headline . \"\\n\" . str_repeat('-', \\strlen($headline)) . \"\\n\",\n            $this->getChangelogs($what)\n        );\n    }\n\n    protected function openChangelogs($what, $version)\n    {\n        $headline = \"\\n$version under development\\n\";\n        $headline .= str_repeat('-', \\strlen($headline) - 2) . \"\\n\\n- no changes in this release.\\n\";\n        foreach ($this->getChangelogs($what) as $file) {\n            $lines = explode(\"\\n\", file_get_contents($file));\n            $hl = [\n                array_shift($lines),\n                array_shift($lines),\n            ];\n            array_unshift($lines, $headline);\n\n            file_put_contents($file, implode(\"\\n\", array_merge($hl, $lines)));\n        }\n    }\n\n    protected function resortChangelogs($what, $version)\n    {\n        foreach ($this->getChangelogs($what) as $file) {\n            // split the file into relevant parts\n            list($start, $changelog, $end) = $this->splitChangelog($file, $version);\n            $changelog = $this->resortChangelog($changelog);\n            file_put_contents($file, implode(\"\\n\", array_merge($start, $changelog, $end)));\n        }\n    }\n\n    /**\n     * Extract changelog content for a specific version.\n     * @param string $file\n     * @param string $version\n     * @return array\n     */\n    protected function splitChangelog($file, $version)\n    {\n        $lines = explode(\"\\n\", file_get_contents($file));\n\n        // split the file into relevant parts\n        $start = [];\n        $changelog = [];\n        $end = [];\n\n        $state = 'start';\n        foreach ($lines as $l => $line) {\n            // starting from the changelogs headline\n            if (\n                isset($lines[$l - 2]) && strpos($lines[$l - 2], $version) !== false &&\n                isset($lines[$l - 1]) && strncmp($lines[$l - 1], '---', 3) === 0\n            ) {\n                $state = 'changelog';\n            }\n            if ($state === 'changelog' && isset($lines[$l + 1]) && strncmp($lines[$l + 1], '---', 3) === 0) {\n                $state = 'end';\n            }\n            // add continued lines to the last item to keep them together\n            if (!empty(${$state}) && trim($line) !== '' && strncmp($line, '- ', 2) !== 0) {\n                end(${$state});\n\n                if (($k = key(${$state})) !== null) {\n                    ${$state}[$k] .= \"\\n\" . $line;\n                }\n            } else {\n                ${$state}[] = $line;\n            }\n        }\n\n        return [$start, $changelog, $end];\n    }\n\n    /**\n     * Ensure sorting of the changelog lines.\n     * @param string[] $changelog\n     * @return string[]\n     */\n    protected function resortChangelog($changelog)\n    {\n        // cleanup whitespace\n        foreach ($changelog as $i => $line) {\n            $changelog[$i] = rtrim($line);\n        }\n        $changelog = array_filter($changelog);\n\n        $i = 0;\n        ArrayHelper::multisort($changelog, function ($line) use (&$i) {\n            if (preg_match('/^- (Chg|Enh|Bug|New)( #\\d+(, #\\d+)*)?: .+/', $line, $m)) {\n                $o = ['Bug' => 'C', 'Enh' => 'D', 'Chg' => 'E', 'New' => 'F'];\n                return $o[$m[1]] . ' ' . (!empty($m[2]) ? $m[2] : 'AAAA' . $i++);\n            }\n\n            return 'B' . $i++;\n        }, SORT_ASC, SORT_NATURAL);\n\n        // re-add leading and trailing lines\n        array_unshift($changelog, '');\n        $changelog[] = '';\n        $changelog[] = '';\n\n        return $changelog;\n    }\n\n    protected function getChangelogs($what)\n    {\n        $changelogs = [];\n        if (\\in_array('framework', $what)) {\n            $changelogs[] = $this->getFrameworkChangelog();\n        }\n\n        return array_merge($changelogs, $this->getExtensionChangelogs($what));\n    }\n\n    protected function getFrameworkChangelog()\n    {\n        return $this->basePath . '/framework/CHANGELOG.md';\n    }\n\n    protected function getExtensionChangelogs($what)\n    {\n        return array_filter(glob($this->basePath . '/extensions/*/CHANGELOG.md'), function ($elem) use ($what) {\n            foreach ($what as $ext) {\n                if (strpos($elem, \"extensions/$ext/CHANGELOG.md\") !== false) {\n                    return true;\n                }\n            }\n\n            return false;\n        });\n    }\n\n    protected function composerSetStability($what, $version)\n    {\n        $apps = [];\n        if (\\in_array('app-advanced', $what)) {\n            $apps[] = $this->basePath . '/apps/advanced/composer.json';\n        }\n        if (\\in_array('app-basic', $what)) {\n            $apps[] = $this->basePath . '/apps/basic/composer.json';\n        }\n        if (\\in_array('app-benchmark', $what)) {\n            $apps[] = $this->basePath . '/apps/benchmark/composer.json';\n        }\n        if (empty($apps)) {\n            return;\n        }\n\n        $stability = 'stable';\n        if (strpos($version, 'alpha') !== false) {\n            $stability = 'alpha';\n        } elseif (strpos($version, 'beta') !== false) {\n            $stability = 'beta';\n        } elseif (strpos($version, 'rc') !== false) {\n            $stability = 'RC';\n        } elseif (strpos($version, 'dev') !== false) {\n            $stability = 'dev';\n        }\n\n        $this->sed(\n            '/\"minimum-stability\": \"(.+?)\",/',\n            '\"minimum-stability\": \"' . $stability . '\",',\n            $apps\n        );\n    }\n\n    protected function updateYiiVersion($frameworkPath, $version)\n    {\n        $this->sed(\n            '/function getVersion\\(\\)\\R {4}\\{\\R {8}return \\'(.+?)\\';/',\n            \"function getVersion()\\n    {\\n        return '$version';\",\n            $frameworkPath . '/BaseYii.php'\n        );\n    }\n\n    protected function sed($pattern, $replace, $files)\n    {\n        foreach ((array) $files as $file) {\n            file_put_contents($file, preg_replace($pattern, $replace, file_get_contents($file)));\n        }\n    }\n\n    protected function getCurrentVersions(array $what)\n    {\n        $versions = [];\n        foreach ($what as $ext) {\n            if ($ext === 'framework') {\n                chdir(\"{$this->basePath}/framework\");\n            } elseif (strncmp('app-', $ext, 4) === 0) {\n                chdir(\"{$this->basePath}/apps/\" . substr($ext, 4));\n            } else {\n                chdir(\"{$this->basePath}/extensions/$ext\");\n            }\n            $tags = [];\n            exec('git tag', $tags, $ret);\n            if ($ret != 0) {\n                throw new Exception('Command \"git tag\" failed with code ' . $ret);\n            }\n            rsort($tags, SORT_NATURAL); // TODO this can not deal with alpha/beta/rc...\n\n            // exclude 3.0.0-alpha1 tag\n            if (($key = array_search('3.0.0-alpha1', $tags, true)) !== false) {\n                unset($tags[$key]);\n            }\n\n            $versions[$ext] = reset($tags);\n        }\n\n        return $versions;\n    }\n\n    public const MINOR = 'minor';\n    public const PATCH = 'patch';\n\n    protected function getNextVersions(array $versions, $type)\n    {\n        foreach ($versions as $k => $v) {\n            if (empty($v)) {\n                $versions[$k] = '2.0.0';\n                continue;\n            }\n            $parts = explode('.', $v);\n            switch ($type) {\n                case self::MINOR:\n                    $parts[1] = (int) $parts[1] + 1;\n                    $parts[2] = 0;\n                    if (isset($parts[3])) {\n                        unset($parts[3]);\n                    }\n                    break;\n                case self::PATCH:\n                    $parts[2] = (int) $parts[2] + 1;\n                    if (isset($parts[3])) {\n                        unset($parts[3]);\n                    }\n                    break;\n                default:\n                    throw new Exception('Unknown version type.');\n            }\n            $versions[$k] = implode('.', $parts);\n        }\n\n        return $versions;\n    }\n}\n"
  },
  {
    "path": "build/controllers/TranslationController.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\build\\controllers;\n\nuse DirectoryIterator;\nuse yii\\console\\Application;\nuse yii\\console\\Controller;\nuse yii\\helpers\\Html;\n\n/**\n * TranslationController handles tasks related to framework translations.\n *\n * build translation \"../docs/guide\" \"../docs/guide-ru\" \"Russian guide translation report\" > report_guide_ru.html\n *\n * @author Alexander Makarov <sam@rmcreative.ru>\n *\n * @extends Controller<Application>\n */\nclass TranslationController extends Controller\n{\n    public $defaultAction = 'report';\n\n    /**\n     * Creates a report about documentation updates since last update of same named translations.\n     *\n     * @param string $sourcePath the directory where the original documentation files are\n     * @param string $translationPath the directory where the translated documentation files are\n     * @param string $title custom title to use for report\n     */\n    public function actionReport($sourcePath, $translationPath, $title = 'Translation report')\n    {\n        $sourcePath = trim($sourcePath, '/\\\\');\n        $translationPath = trim($translationPath, '/\\\\');\n\n        $results = [];\n\n        $dir = new DirectoryIterator($sourcePath);\n        foreach ($dir as $fileinfo) {\n            /** @var DirectoryIterator $fileinfo */\n            if (!$fileinfo->isDot() && !$fileinfo->isDir()) {\n                $translatedFilePath = $translationPath . '/' . $fileinfo->getFilename();\n                $sourceFilePath = $sourcePath . '/' . $fileinfo->getFilename();\n\n                $errors = $this->checkFiles($translatedFilePath);\n                $diff = empty($errors) ? $this->getDiff($translatedFilePath, $sourceFilePath) : '';\n                if (!empty($diff)) {\n                    $errors[] = 'Translation outdated.';\n                }\n\n                $result = [\n                    'errors' => $errors,\n                    'diff' => $diff,\n                ];\n\n                $results[$fileinfo->getFilename()] = $result;\n            }\n        }\n\n        // checking if there are obsolete translation files\n        $dir = new DirectoryIterator($translationPath);\n        foreach ($dir as $fileinfo) {\n            /** @var DirectoryIterator $fileinfo */\n            if (!$fileinfo->isDot() && !$fileinfo->isDir()) {\n                $translatedFilePath = $translationPath . '/' . $fileinfo->getFilename();\n\n                $errors = $this->checkFiles(null, $translatedFilePath);\n                if (!empty($errors)) {\n                    $results[$fileinfo->getFilename()]['errors'] = $errors;\n                }\n            }\n        }\n\n        echo $this->renderFile(__DIR__ . '/views/translation/report_html.php', [\n            'results' => $results,\n            'sourcePath' => $sourcePath,\n            'translationPath' => $translationPath,\n            'title' => $title,\n        ]);\n    }\n\n    /**\n     * Checks for files existence.\n     *\n     * @param string $translatedFilePath\n     * @param string $sourceFilePath\n     * @return array errors\n     */\n    protected function checkFiles($translatedFilePath = null, $sourceFilePath = null)\n    {\n        $errors = [];\n        if ($translatedFilePath !== null && !file_exists($translatedFilePath)) {\n            $errors[] = 'Translation does not exist.';\n        }\n\n        if ($sourceFilePath !== null && !file_exists($sourceFilePath)) {\n            $errors[] = 'Source does not exist.';\n        }\n\n        return $errors;\n    }\n\n    /**\n     * Getting DIFF from git.\n     *\n     * @param string $translatedFilePath path pointing to translated file\n     * @param string $sourceFilePath path pointing to original file\n     * @return string DIFF\n     */\n    protected function getDiff($translatedFilePath, $sourceFilePath)\n    {\n        $lastTranslationHash = shell_exec('git log -1 --format=format:\"%H\" -- ' . $translatedFilePath);\n        return shell_exec('git diff ' . $lastTranslationHash . '..HEAD -- ' . $sourceFilePath);\n    }\n\n    /**\n     * Adds all necessary HTML tags and classes to diff output.\n     *\n     * @param string $diff DIFF\n     * @return string highlighted DIFF\n     */\n    public function highlightDiff($diff)\n    {\n        $lines = explode(\"\\n\", $diff);\n        foreach ($lines as $key => $val) {\n            if (strpos($val, '@') === 0) {\n                $lines[$key] = '<span class=\"info\">' . Html::encode($val) . '</span>';\n            } elseif (strpos($val, '+') === 0) {\n                $lines[$key] = '<ins>' . Html::encode($val) . '</ins>';\n            } elseif (strpos($val, '-') === 0) {\n                $lines[$key] = '<del>' . Html::encode($val) . '</del>';\n            } else {\n                $lines[$key] = Html::encode($val);\n            }\n        }\n\n        return implode(\"\\n\", $lines);\n    }\n}\n"
  },
  {
    "path": "build/controllers/Utf8Controller.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\build\\controllers;\n\nuse yii\\console\\Application;\nuse yii\\console\\Controller;\nuse yii\\helpers\\Console;\nuse yii\\helpers\\FileHelper;\n\n/**\n * Check files for broken UTF8 and non-printable characters.\n *\n * @author Carsten Brandt <mail@cebe.cc>\n *\n * @extends Controller<Application>\n */\nclass Utf8Controller extends Controller\n{\n    public $defaultAction = 'check-guide';\n\n    /**\n     * Check guide for non-printable characters that may break docs generation.\n     *\n     * @param string $directory the directory to check. If not specified, the default\n     * guide directory will be checked.\n     */\n    public function actionCheckGuide($directory = null)\n    {\n        if ($directory === null) {\n            $directory = \\dirname(\\dirname(__DIR__)) . '/docs';\n        }\n        if (is_file($directory)) {\n            $files = [$directory];\n        } else {\n            $files = FileHelper::findFiles($directory, [\n                'only' => ['*.md'],\n            ]);\n        }\n\n        foreach ($files as $file) {\n            $content = file_get_contents($file);\n            $chars = preg_split('//u', $content, null, PREG_SPLIT_NO_EMPTY);\n\n            $line = 1;\n            $pos = 0;\n            foreach ($chars as $c) {\n                $ord = $this->unicodeOrd($c);\n\n                $pos++;\n                if ($ord == 0x000A) {\n                    $line++;\n                    $pos = 0;\n                }\n\n                if ($ord === false) {\n                    $this->found('BROKEN UTF8', $c, $line, $pos, $file);\n                    continue;\n                }\n\n                // https://unicode-table.com/en/blocks/general-punctuation/\n                if (\n                    0x2000 <= $ord && $ord <= 0x200F\n                    || 0x2028 <= $ord && $ord <= 0x202E\n                    || 0x205f <= $ord && $ord <= 0x206F\n                ) {\n                    $this->found('UNSUPPORTED SPACE CHARACTER', $c, $line, $pos, $file);\n                    continue;\n                }\n                if (\n                    $ord < 0x0020 && $ord != 0x000A && $ord != 0x0009 ||\n                    0x0080 <= $ord && $ord < 0x009F\n                ) {\n                    $this->found('CONTROL CHARACTER', $c, $line, $pos, $file);\n                    continue;\n                }\n                //                if ($ord > 0x009F) {\n                //                    $this->found(\"NON ASCII CHARACTER\", $c, $line, $pos, $file);\n                //                    continue;\n                //                }\n            }\n        }\n    }\n\n    private $_foundFiles = [];\n\n    private function found($what, $char, $line, $pos, $file)\n    {\n        if (!isset($this->_foundFiles[$file])) {\n            $this->stdout(\"$file: \\n\", Console::BOLD);\n            $this->_foundFiles[$file] = $file;\n        }\n\n        $hexcode = dechex($this->unicodeOrd($char));\n        $hexcode = str_repeat('0', max(4 - \\strlen($hexcode), 0)) . $hexcode;\n\n        $this->stdout(\"  at $line:$pos FOUND $what: 0x$hexcode '$char' https://unicode-table.com/en/$hexcode/\\n\");\n    }\n\n    /**\n     * Equivalent for ord() just for unicode.\n     *\n     * https://stackoverflow.com/questions/10333098/utf-8-safe-equivalent-of-ord-or-charcodeat-in-php/10333324#10333324\n     *\n     * @param $c\n     * @return bool|int\n     */\n    private function unicodeOrd($c)\n    {\n        $h = \\ord($c[0]);\n        if ($h <= 0x7F) {\n            return $h;\n        } elseif ($h < 0xC2) {\n            return false;\n        } elseif ($h <= 0xDF) {\n            return ($h & 0x1F) << 6 | (\\ord($c[1]) & 0x3F);\n        } elseif ($h <= 0xEF) {\n            return ($h & 0x0F) << 12 | (\\ord($c[1]) & 0x3F) << 6\n                | (\\ord($c[2]) & 0x3F);\n        } elseif ($h <= 0xF4) {\n            return ($h & 0x0F) << 18 | (\\ord($c[1]) & 0x3F) << 12\n                | (\\ord($c[2]) & 0x3F) << 6\n                | (\\ord($c[3]) & 0x3F);\n        }\n\n        return false;\n    }\n}\n"
  },
  {
    "path": "build/controllers/views/translation/report_html.php",
    "content": "<?php\n\nuse yii\\helpers\\Html;\n\n/**\n * @var \\yii\\web\\View $this\n * @var string $title\n * @var string $sourcePath\n * @var string $translationPath\n * @var array $results\n */\n\n/** @var \\yii\\build\\controllers\\TranslationController $translationController */\n$translationController = $this->context;\n\n?><!doctype html>\n<html>\n    <head>\n        <meta charset=\"utf-8\" />\n        <title>Translation report</title>\n\n        <style>\n            .diff ins {\n                background: #cfc;\n                text-decoration: none;\n            }\n\n            .diff del {\n                background: #ffe6cc;\n                text-decoration: none;\n            }\n\n            .ok {\n                color: #99cc32;\n            }\n\n            .errors {\n                color: #cc5129;\n            }\n        </style>\n    </head>\n    <body>\n        <h1><?= Html::encode($title) ?></h1>\n\n        <ul>\n            <li><strong>Source:</strong> <?= Html::encode($sourcePath) ?></li>\n            <li><strong>Translation:</strong> <?= Html::encode($translationPath) ?></li>\n        </ul>\n\n        <?php foreach ($results as $name => $result): ?>\n            <h2 class=\"<?= empty($result['errors']) ? 'ok' : 'errors' ?>\"><?= $name ?></h2>\n            <?php foreach ($result['errors'] as $error): ?>\n                <p><?= Html::encode($error) ?></p>\n            <?php endforeach ?>\n            <?php if (!empty($result['diff'])): ?>\n                <code class=\"diff\"><pre><?= $translationController->highlightDiff($result['diff']) ?></pre></code>\n            <?php endif ?>\n        <?php endforeach ?>\n    </body>\n</html>\n"
  },
  {
    "path": "code-of-conduct.md",
    "content": "Yii Contributor Code of Conduct\n=======================\n\n## Our Pledge\n\nAs contributors and maintainers of this project, and in order to keep Yii community open and welcoming, we ask to respect all community members.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment include:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\n  advances\n* Personal attacks\n* Trolling or insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing other's private information, such as physical or electronic\n  addresses, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in\n  a professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in response\nto any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or reject comments,\ncommits, code, wiki edits, issues, and other contributions that are not aligned to this\nCode of Conduct, or to ban temporarily or permanently any contributor for other behaviors\nthat they deem inappropriate, threatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces when\nan individual is representing the project or its community. Examples of representing\na project or community include posting via an official social media account,\nwithin project GitHub, official forum or acting as an appointed representative at\nan online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be reported\nby contacting core team members. All complaints will be reviewed and investigated\nand will result in a response that is deemed necessary and appropriate to the circumstances.\nThe project team is obligated to maintain confidentiality with regard to the reporter of\nan incident. Further details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good faith\nmay face temporary or permanent repercussions as determined by other members of\nthe project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 1.4.0, available at\n[https://contributor-covenant.org/version/1/4/][version]\n\n[homepage]: https://contributor-covenant.org\n[version]: https://contributor-covenant.org/version/1/4/\n"
  },
  {
    "path": "composer.json",
    "content": "{\n    \"name\": \"yiisoft/yii2-dev\",\n    \"description\": \"Yii PHP Framework Version 2 - Development Package\",\n    \"keywords\": [\n        \"yii2\",\n        \"framework\"\n    ],\n    \"homepage\": \"https://www.yiiframework.com/\",\n    \"type\": \"yii2-extension\",\n    \"license\": \"BSD-3-Clause\",\n    \"authors\": [\n        {\n            \"name\": \"Qiang Xue\",\n            \"email\": \"qiang.xue@gmail.com\",\n            \"homepage\": \"https://www.yiiframework.com/\",\n            \"role\": \"Founder and project lead\"\n        },\n        {\n            \"name\": \"Alexander Makarov\",\n            \"email\": \"sam@rmcreative.ru\",\n            \"homepage\": \"https://rmcreative.ru/\",\n            \"role\": \"Core framework development\"\n        },\n        {\n            \"name\": \"Maurizio Domba\",\n            \"homepage\": \"http://mdomba.info/\",\n            \"role\": \"Core framework development\"\n        },\n        {\n            \"name\": \"Carsten Brandt\",\n            \"email\": \"mail@cebe.cc\",\n            \"homepage\": \"https://www.cebe.cc/\",\n            \"role\": \"Core framework development\"\n        },\n        {\n            \"name\": \"Timur Ruziev\",\n            \"email\": \"resurtm@gmail.com\",\n            \"homepage\": \"http://resurtm.com/\",\n            \"role\": \"Core framework development\"\n        },\n        {\n            \"name\": \"Paul Klimov\",\n            \"email\": \"klimov.paul@gmail.com\",\n            \"role\": \"Core framework development\"\n        },\n        {\n            \"name\": \"Dmitry Naumenko\",\n            \"email\": \"d.naumenko.a@gmail.com\",\n            \"role\": \"Core framework development\"\n        },\n        {\n            \"name\": \"Boudewijn Vahrmeijer\",\n            \"email\": \"info@dynasource.eu\",\n            \"homepage\": \"http://dynasource.eu\",\n            \"role\": \"Core framework development\"\n        }\n    ],\n    \"support\": {\n        \"issues\": \"https://github.com/yiisoft/yii2/issues?state=open\",\n        \"forum\": \"https://forum.yiiframework.com/\",\n        \"wiki\": \"https://www.yiiframework.com/wiki\",\n        \"irc\": \"ircs://irc.libera.chat:6697/yii\",\n        \"source\": \"https://github.com/yiisoft/yii2\"\n    },\n    \"minimum-stability\": \"dev\",\n    \"prefer-stable\": true,\n    \"replace\": {\n        \"yiisoft/yii2\": \"self.version\"\n    },\n    \"require\": {\n        \"php\": \">=7.4.0\",\n        \"ext-mbstring\": \"*\",\n        \"ext-ctype\": \"*\",\n        \"lib-pcre\": \"*\",\n        \"yiisoft/yii2-composer\": \"~2.0.4\",\n        \"ezyang/htmlpurifier\": \"^4.17\",\n        \"cebe/markdown\": \"~1.0.0 | ~1.1.0 | ~1.2.0\",\n        \"bower-asset/jquery\": \"3.7.*@stable | 3.6.*@stable | 3.5.*@stable | 3.4.*@stable | 3.3.*@stable | 3.2.*@stable | 3.1.*@stable | 2.2.*@stable | 2.1.*@stable | 1.11.*@stable | 1.12.*@stable\",\n        \"bower-asset/inputmask\": \"^5.0.8 \",\n        \"bower-asset/punycode\": \"^2.2\",\n        \"bower-asset/yii2-pjax\": \"~2.0.1\"\n    },\n    \"require-dev\": {\n        \"cebe/indent\": \"~1.0.2\",\n        \"dealerdirect/phpcodesniffer-composer-installer\": \"*\",\n        \"dms/phpunit-arraysubset-asserts\": \"^0.5\",\n        \"phpunit/phpunit\": \"^9.6\",\n        \"yiisoft/yii2-coding-standards\": \"^3.0\",\n        \"phpstan/phpstan\": \"^2.1\",\n        \"phpstan/phpstan-phpunit\": \"^2.0\"\n    },\n    \"repositories\": [\n        {\n            \"type\": \"composer\",\n            \"url\": \"https://asset-packagist.org\"\n        }\n    ],\n    \"suggest\": {\n        \"yiisoft/yii2-coding-standards\": \"you can use this package to check for code style issues when contributing to yii\"\n    },\n    \"autoload\": {\n        \"psr-4\": {\n            \"yii\\\\\": \"framework/\"\n        }\n    },\n    \"autoload-dev\": {\n        \"psr-4\": {\n            \"yii\\\\build\\\\\": \"build/\",\n            \"yiiunit\\\\\": \"tests/\"\n        }\n    },\n    \"config\": {\n        \"allow-plugins\": {\n            \"cweagans/composer-patches\": true,\n            \"yiisoft/yii2-composer\": true,\n            \"dealerdirect/phpcodesniffer-composer-installer\": true\n        }\n    },\n    \"bin\": [\n        \"framework/yii\"\n    ],\n    \"extra\": {\n        \"branch-alias\": {\n            \"dev-master\": \"2.0.x-dev\"\n        }\n    },\n    \"scripts\": {\n        \"cs\": \"./vendor/bin/phpcs\",\n        \"cs-fix\": \"./vendor/bin/phpcbf\"\n    }\n}\n"
  },
  {
    "path": "contrib/completion/bash/yii",
    "content": "# This file implements bash completion for the ./yii command file.\n# It completes the commands available by the ./yii command.\n# See also:\n# - https://debian-administration.org/article/317/An_introduction_to_bash_completion_part_2 on how this works.\n# - https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion.html\n# - https://www.yiiframework.com/doc-2.0/guide-tutorial-console.html#bash-completion\n#\n# Usage:\n# Temporarily you can source this file in you bash by typing: source yii\n# For permanent availability, copy or link this file to /etc/bash_completion.d/\n#\n\n_yii()\n{\n    local cur opts yii command\n    COMPREPLY=()\n    cur=\"${COMP_WORDS[COMP_CWORD]}\"\n    prev=\"${COMP_WORDS[COMP_CWORD-1]}\"\n    yii=\"${COMP_WORDS[0]}\"\n\n    # exit if ./yii does not exist\n    test -f $yii || return 0\n\n    # lookup for command\n    for word in ${COMP_WORDS[@]:1}; do\n        if [[ $word != -* ]]; then\n            command=$word\n            break\n        fi\n    done\n\n    [[ $cur == $command ]] && state=\"command\"\n    [[ $cur != $command ]] && state=\"option\"\n    [[ $cur = *=* ]] && state=\"value\"\n    [[ $prev == \"help\" ]] && state=\"help\"\n\n    case $state in\n        command|help)\n            # complete command/route if not given\n            # fetch available commands from ./yii help/list command\n            opts=$($yii help/list 2> /dev/null)\n        ;;\n        option)\n            # fetch available options from ./yii help/list-action-options command\n            opts=$($yii help/list-action-options $command 2> /dev/null | grep -o '^--[a-zA-Z0-9\\-]*')\n        ;;\n        value)\n            # TODO allow normal file completion after an option, e.g. --migrationPath=...\n        ;;\n    esac\n\n    # generate completion suggestions\n    COMPREPLY=( $(compgen -W \"${opts}\" -- ${cur}) )\n    return 0\n\n}\n\n# register completion for the ./yii command\n# you may adjust this line if your command file is named differently\ncomplete -o default -F _yii ./yii yii\n"
  },
  {
    "path": "contrib/completion/zsh/_yii",
    "content": "#compdef yii\n\n_yii() {\n    local state command lastArgument commands options executive\n    lastArgument=${words[${#words[@]}]}\n    prevArgument=${words[${#words[@]}-1]}\n    executive=$words[1]\n\n    # lookup for command\n    for word in ${words[@]:1}; do\n        if [[ $word != -* ]]; then\n            command=$word\n            break\n        fi\n    done\n\n\n    [[ $lastArgument == $command ]] && state=\"command\"\n    [[ $lastArgument != $command ]] && state=\"option\"\n    [[ $prevArgument == \"help\" ]] && state=\"help\"\n\n    case $state in\n        command|help)\n            commands=(\"${(@f)$(${executive} help/list 2>/dev/null)}\")\n            _describe 'command' commands\n        ;;\n        option)\n            options=(\"${(@f)$(${executive} help/usage ${command} 2>/dev/null)}\")\n            _message -r \"$options\"\n\n            suboptions=(\"${(@f)$(${executive} help/list-action-options ${command} 2>/dev/null)}\")\n            _describe -V -o -t suboption 'action options' suboptions\n        ;;\n        *)\n    esac\n\n}\n\ncompdef _yii yii\n\n"
  },
  {
    "path": "docs/documentation_style_guide.md",
    "content": "# Yii Documentation Style Guide\n\nGuidelines to go by when writing or editing any Yii documentation.\n\n*This needs to be expanded.*\n\n## General Style\n\n* Try to use an active voice.\n* Use short, declarative sentences.\n* Demonstrate ideas using code as much as possible.\n* Never use \"we\". It's the Yii development team or the Yii core team. Better yet to put things in terms of the framework or the guide.\n* Use the Oxford comma (e.g., \"this, that, and the other\" not \"this, that and the other\").\n\n## Formatting\n\n* Use *italics* for emphasis, never capitalization, bold, or underlines.\n\n## Lists\n\n* Numeric lists should be complete sentences that end with periods.\n* Bullet lists should be fragments that end with semicolon except the last item, which should end with a period.\n\n## Blocks\n\nBlocks use the Markdown `> Type: `. There are four block types:\n\n* `Warning`, for bad security things and other problems\n* `Note`, to emphasize key concepts, things to avoid\n* `Info`, general information (an aside); not as strong as a \"Note\"\n* `Tip`, pro tips, extras, can be useful but may not be needed by everyone all the time\n\nThe sentence after the colon should begin with a capital letter.\n\nWhen translating documentation, these Block indicators should not be translated.\nKeeps them intact as they are and only translate the block content.\nFor translating the `Type` word, each guide translation should have a `blocktypes.json` file\ncontaining the translations. The following shows an example for German:\n\n```json\n{\n    \"Warning:\": \"Achtung:\",\n    \"Note:\": \"Hinweis:\",\n    \"Info:\": \"Info:\",\n    \"Tip:\": \"Tipp:\"\n}\n```\n\n## References\n\n* Yii 2.0 or Yii 2 (not Yii2 or Yii2.0)\n* Each \"page\" of the guide is referred to as a \"section\".\n* References to Code objects:\n  - Refer to classes using the full namespace: `yii\\base\\Model`\n  - Refer to class properties using the static syntax even if they are not static: `yii\\base\\Model::$validators`\n  - Refer to class methods using the static syntax even if they are not static and include parenthesis to make it clear, that it is a method: `yii\\base\\Model::validate()`\n  - references to code objects should be writting in `[[]]` to generate links to the API documentation. E.g. `[[yii\\base\\Model]]`, `[[yii\\base\\Model::$validators]]`, or `[[yii\\base\\Model::validate()]]`.\n\n## Capitalizations\n\n* Web, not web\n* the guide or this guide, not the Guide\n\n## Validating the docs\n\nThe following are some scripts that help find broken links and other issues in the guide:\n\nFind broken links (some false-positives may occur):\n\n    grep -rniP \"\\[\\[[^\\],']+?\\][^\\]]\"  docs/guide*\n    grep -rniP \"[^\\[]\\[[^\\]\\[,']+?\\]\\]\"  docs/guide*\n    \n## Attribution of Translators\n\nThe names of the translators will be listed among the guide authors in the\nrendered versions of the guide.\nTherefor in each guide directory for a different language than english a `translators.json` file\nshould be created that contains an array of names of the people who have participated in the translation.\n\n```json\n[\n  \"Jane Doe\",\n  \"John Doe\"\n]\n```\n\nIf you have contributed a significant part to the translation, feel free to send a pull request adding your name.\n"
  },
  {
    "path": "docs/guide/README.md",
    "content": "The Definitive Guide to Yii 2.0\n===============================\n\nThis tutorial is released under the [Terms of Yii Documentation](https://www.yiiframework.com/doc/terms/).\n\nAll Rights Reserved.\n\n2014 (c) Yii Software LLC.\n\n\nIntroduction\n------------\n\n* [About Yii](intro-yii.md)\n* [Upgrading from Version 1.1](intro-upgrade-from-v1.md)\n\n\nGetting Started\n---------------\n\n* [What do you need to know](start-prerequisites.md)\n* [Installing Yii](start-installation.md)\n* [Running Applications](start-workflow.md)\n* [Saying Hello](start-hello.md)\n* [Working with Forms](start-forms.md)\n* [Working with Databases](start-databases.md)\n* [Generating Code with Gii](start-gii.md)\n* [Looking Ahead](start-looking-ahead.md)\n\n\nApplication Structure\n---------------------\n\n* [Application Structure Overview](structure-overview.md)\n* [Entry Scripts](structure-entry-scripts.md)\n* [Applications](structure-applications.md)\n* [Application Components](structure-application-components.md)\n* [Controllers](structure-controllers.md)\n* [Models](structure-models.md)\n* [Views](structure-views.md)\n* [Modules](structure-modules.md)\n* [Filters](structure-filters.md)\n* [Widgets](structure-widgets.md)\n* [Assets](structure-assets.md)\n* [Extensions](structure-extensions.md)\n\n\nHandling Requests\n-----------------\n\n* [Request Handling Overview](runtime-overview.md)\n* [Bootstrapping](runtime-bootstrapping.md)\n* [Routing and URL Creation](runtime-routing.md)\n* [Requests](runtime-requests.md)\n* [Responses](runtime-responses.md)\n* [Sessions and Cookies](runtime-sessions-cookies.md)\n* [Handling Errors](runtime-handling-errors.md)\n* [Logging](runtime-logging.md)\n\n\nKey Concepts\n------------\n\n* [Components](concept-components.md)\n* [Properties](concept-properties.md)\n* [Events](concept-events.md)\n* [Behaviors](concept-behaviors.md)\n* [Configurations](concept-configurations.md)\n* [Aliases](concept-aliases.md)\n* [Class Autoloading](concept-autoloading.md)\n* [Service Locator](concept-service-locator.md)\n* [Dependency Injection Container](concept-di-container.md)\n\n\nWorking with Databases\n----------------------\n\n* [Database Access Objects](db-dao.md): Connecting to a database, basic queries, transactions, and schema manipulation\n* [Query Builder](db-query-builder.md): Querying the database using a simple abstraction layer\n* [Active Record](db-active-record.md): The Active Record ORM, retrieving and manipulating records, and defining relations\n* [Migrations](db-migrations.md): Apply version control to your databases in a team development environment\n* [Sphinx](https://www.yiiframework.com/extension/yiisoft/yii2-sphinx/doc/guide)\n* [Redis](https://www.yiiframework.com/extension/yiisoft/yii2-redis/doc/guide)\n* [MongoDB](https://www.yiiframework.com/extension/yiisoft/yii2-mongodb/doc/guide)\n* [ElasticSearch](https://www.yiiframework.com/extension/yiisoft/yii2-elasticsearch/doc/guide)\n\n\nGetting Data from Users\n-----------------------\n\n* [Creating Forms](input-forms.md)\n* [Validating Input](input-validation.md)\n* [Uploading Files](input-file-upload.md)\n* [Collecting Tabular Input](input-tabular-input.md)\n* [Getting Data for Multiple Models](input-multiple-models.md)\n* [Extending ActiveForm on the Client Side](input-form-javascript.md)\n\n\nDisplaying Data\n---------------\n\n* [Data Formatting](output-formatting.md)\n* [Pagination](output-pagination.md)\n* [Sorting](output-sorting.md)\n* [Data Providers](output-data-providers.md)\n* [Data Widgets](output-data-widgets.md)\n* [Working with Client Scripts](output-client-scripts.md)\n* [Theming](output-theming.md)\n\n\nSecurity\n--------\n\n* [Security Overview](security-overview.md)\n* [Authentication](security-authentication.md)\n* [Authorization](security-authorization.md)\n* [Working with Passwords](security-passwords.md)\n* [Cryptography](security-cryptography.md)\n* [Auth Clients](https://www.yiiframework.com/extension/yiisoft/yii2-authclient/doc/guide)\n* [Best Practices](security-best-practices.md)\n\n\nCaching\n-------\n\n* [Caching Overview](caching-overview.md)\n* [Data Caching](caching-data.md)\n* [Fragment Caching](caching-fragment.md)\n* [Page Caching](caching-page.md)\n* [HTTP Caching](caching-http.md)\n\n\nRESTful Web Services\n--------------------\n\n* [Quick Start](rest-quick-start.md)\n* [Resources](rest-resources.md)\n* [Controllers](rest-controllers.md)\n* [Filtering Collections](rest-filtering-collections.md)\n* [Routing](rest-routing.md)\n* [Response Formatting](rest-response-formatting.md)\n* [Authentication](rest-authentication.md)\n* [Rate Limiting](rest-rate-limiting.md)\n* [Versioning](rest-versioning.md)\n* [Error Handling](rest-error-handling.md)\n\n\nDevelopment Tools\n-----------------\n\n* [Debug Toolbar and Debugger](https://www.yiiframework.com/extension/yiisoft/yii2-debug/doc/guide)\n* [Generating Code using Gii](https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide)\n* [Generating API Documentation](https://www.yiiframework.com/extension/yiisoft/yii2-apidoc)\n\n\nTesting\n-------\n\n* [Testing Overview](test-overview.md)\n* [Testing environment setup](test-environment-setup.md)\n* [Unit Tests](test-unit.md)\n* [Functional Tests](test-functional.md)\n* [Acceptance Tests](test-acceptance.md)\n* [Fixtures](test-fixtures.md)\n\n\nSpecial Topics\n--------------\n\n* [Advanced Project Template](https://www.yiiframework.com/extension/yiisoft/yii2-app-advanced/doc/guide)\n* [Building Application from Scratch](tutorial-start-from-scratch.md)\n* [Console Commands](tutorial-console.md)\n* [Core Validators](tutorial-core-validators.md)\n* [Docker](tutorial-docker.md)\n* [Internationalization](tutorial-i18n.md)\n* [Mailing](tutorial-mailing.md)\n* [Performance Tuning](tutorial-performance-tuning.md)\n* [Shared Hosting Environment](tutorial-shared-hosting.md)\n* [Template Engines](tutorial-template-engines.md)\n* [Working with Third-Party Code](tutorial-yii-integration.md)\n* [Using Yii as a micro framework](tutorial-yii-as-micro-framework.md)\n\n\nWidgets\n-------\n\n* [GridView](https://www.yiiframework.com/doc-2.0/yii-grid-gridview.html)\n* [ListView](https://www.yiiframework.com/doc-2.0/yii-widgets-listview.html)\n* [DetailView](https://www.yiiframework.com/doc-2.0/yii-widgets-detailview.html)\n* [ActiveForm](https://www.yiiframework.com/doc-2.0/guide-input-forms.html#activerecord-based-forms-activeform)\n* [Pjax](https://www.yiiframework.com/doc-2.0/yii-widgets-pjax.html)\n* [Menu](https://www.yiiframework.com/doc-2.0/yii-widgets-menu.html)\n* [LinkPager](https://www.yiiframework.com/doc-2.0/yii-widgets-linkpager.html)\n* [LinkSorter](https://www.yiiframework.com/doc-2.0/yii-widgets-linksorter.html)\n* [Bootstrap Widgets](https://www.yiiframework.com/extension/yiisoft/yii2-bootstrap/doc/guide)\n* [jQuery UI Widgets](https://www.yiiframework.com/extension/yiisoft/yii2-jui/doc/guide)\n\n\nHelpers\n-------\n\n* [Helpers Overview](helper-overview.md)\n* [ArrayHelper](helper-array.md)\n* [Html](helper-html.md)\n* [Json](helper-json.md)\n* [Url](helper-url.md)\n\n"
  },
  {
    "path": "docs/guide/caching-data.md",
    "content": "Data Caching\n============\n\nData caching is about storing some PHP variables in cache and retrieving it later from cache.\nIt is also the foundation for more advanced caching features, such as [query caching](#query-caching)\nand [page caching](caching-page.md).\n\nThe following code is a typical usage pattern of data caching, where `$cache` refers to\na [cache component](#cache-components):\n\n```php\n// try retrieving $data from cache\n$data = $cache->get($key);\n\nif ($data === false) {\n    // $data is not found in cache, calculate it from scratch\n    $data = $this->calculateSomething();\n\n    // store $data in cache so that it can be retrieved next time\n    $cache->set($key, $data);\n}\n\n// $data is available here\n```\n\nSince version 2.0.11, [cache component](#cache-components) provides [[yii\\caching\\Cache::getOrSet()|getOrSet()]] method\nthat simplifies code for data getting, calculating and storing. The following code does exactly the same as the \nprevious example:\n\n```php\n$data = $cache->getOrSet($key, function () {\n    return $this->calculateSomething();\n});\n```\n\nWhen cache has data associated with the `$key`, the cached value will be returned. \nOtherwise, the passed anonymous function will be executed to calculate the value that will be cached and returned.\n\nIf the anonymous function requires some data from the outer scope, you can pass it with the `use` statement.\nFor example:\n\n```php\n$user_id = 42;\n$data = $cache->getOrSet($key, function () use ($user_id) {\n    return $this->calculateSomething($user_id);\n});\n```\n\n> Note: [[yii\\caching\\Cache::getOrSet()|getOrSet()]] method supports duration and dependencies as well. \n  See [Cache Expiration](#cache-expiration) and [Cache Dependencies](#cache-dependencies) to know more.\n  \n\n## Cache Components <span id=\"cache-components\"></span>\n\nData caching relies on the so-called *cache components* which represent various cache storage,\nsuch as memory, files, databases.\n\nCache components are usually registered as [application components](structure-application-components.md) so\nthat they can be globally configurable\nand accessible. The following code shows how to configure the `cache` application component to use\n[memcached](https://memcached.org/) with two cache servers:\n\n```php\n'components' => [\n    'cache' => [\n        'class' => 'yii\\caching\\MemCache',\n        'servers' => [\n            [\n                'host' => 'server1',\n                'port' => 11211,\n                'weight' => 100,\n            ],\n            [\n                'host' => 'server2',\n                'port' => 11211,\n                'weight' => 50,\n            ],\n        ],\n    ],\n],\n```\n\nYou can then access the above cache component using the expression `Yii::$app->cache`.\n\nIf no cache component is specified, then Yii will use [yii\\caching\\FileCache](https://www.yiiframework.com/doc/api/2.0/yii-caching-filecache) as default.\n\nBecause all cache components support the same set of APIs, you can swap the underlying cache component\nwith a different one by reconfiguring it in the application configuration without modifying the code that uses the cache.\nFor example, you can modify the above configuration to use [[yii\\caching\\ApcCache|APC cache]]:\n\n\n```php\n'components' => [\n    'cache' => [\n        'class' => 'yii\\caching\\ApcCache',\n    ],\n],\n```\n\n> Tip: You can register multiple cache application components. The component named `cache` is used\n  by default by many cache-dependent classes (e.g. [[yii\\web\\UrlManager]]).\n\n\n### Supported Cache Storage <span id=\"supported-cache-storage\"></span>\n\nYii supports a wide range of cache storage. The following is a summary:\n\n* [[yii\\caching\\ApcCache]]: uses PHP [APC](https://www.php.net/manual/en/book.apcu.php) extension. This option can be\n  considered as the fastest one when dealing with cache for a centralized thick application (e.g. one\n  server, no dedicated load balancers, etc.).\n* [[yii\\caching\\DbCache]]: uses a database table to store cached data. To use this cache, you must\n  create a table as specified in [[yii\\caching\\DbCache::cacheTable]].\n* [[yii\\caching\\ArrayCache]]: provides caching for the current request only by storing the values in an array.\n  For enhanced performance of ArrayCache, you can disable serialization of the stored data by setting\n  [[yii\\caching\\ArrayCache::$serializer]] to `false`.\n* [[yii\\caching\\DummyCache]]: serves as a cache placeholder which does no real caching.\n  The purpose of this component is to simplify the code that needs to check the availability of cache.\n  For example, during development or if the server doesn't have actual cache support, you may configure\n  a cache component to use this cache. When an actual cache support is enabled, you can switch to use\n  the corresponding cache component. In both cases, you may use the same code\n  `Yii::$app->cache->get($key)` to attempt retrieving data from the cache without worrying that\n  `Yii::$app->cache` might be `null`.\n* [[yii\\caching\\FileCache]]: uses standard files to store cached data. This is particularly suitable\n  to cache large chunk of data, such as page content.\n* [[yii\\caching\\MemCache]]: uses PHP [memcache](https://www.php.net/manual/en/book.memcache.php)\n  and [memcached](https://www.php.net/manual/en/book.memcached.php) extensions. This option can be considered as\n  the fastest one when dealing with cache in a distributed applications (e.g. with several servers, load\n  balancers, etc.)\n* [[yii\\redis\\Cache]]: implements a cache component based on [Redis](https://redis.io/) key-value store\n  (redis version 2.6.12 or higher is required).\n* [[yii\\caching\\WinCache]]: uses PHP [WinCache](https://iis.net/downloads/microsoft/wincache-extension)\n  ([see also](https://www.php.net/manual/en/book.wincache.php)) extension.\n\n> Tip: You may use different cache storage in the same application. A common strategy is to use memory-based\n  cache storage to store data that is small but constantly used (e.g. statistical data), and use file-based\n  or database-based cache storage to store data that is big and less frequently used (e.g. page content).\n\n\n## Cache APIs <span id=\"cache-apis\"></span>\n\nAll cache components have the same base class [[yii\\caching\\Cache]] and thus support the following APIs:\n\n* [[yii\\caching\\Cache::get()|get()]]: retrieves a data item from cache with a specified key. A `false`\n  value will be returned if the data item is not found in the cache or is expired/invalidated.\n* [[yii\\caching\\Cache::set()|set()]]: stores a data item identified by a key in cache.\n* [[yii\\caching\\Cache::add()|add()]]: stores a data item identified by a key in cache if the key is not found in the cache.\n* [[yii\\caching\\Cache::getOrSet()|getOrSet()]]: retrieves a data item from cache with a specified key or executes passed\n  callback, stores return of the callback in a cache by a key and returns that data.  \n* [[yii\\caching\\Cache::multiGet()|multiGet()]]: retrieves multiple data items from cache with the specified keys.\n* [[yii\\caching\\Cache::multiSet()|multiSet()]]: stores multiple data items in cache. Each item is identified by a key.\n* [[yii\\caching\\Cache::multiAdd()|multiAdd()]]: stores multiple data items in cache. Each item is identified by a key.\n  If a key already exists in the cache, the data item will be skipped.\n* [[yii\\caching\\Cache::exists()|exists()]]: returns a value indicating whether the specified key is found in the cache.\n* [[yii\\caching\\Cache::delete()|delete()]]: removes a data item identified by a key from the cache.\n* [[yii\\caching\\Cache::flush()|flush()]]: removes all data items from the cache.\n\n> Note: Do not cache a `false` boolean value directly because the [[yii\\caching\\Cache::get()|get()]] method uses\n`false` return value to indicate the data item is not found in the cache. You may put `false` in an array and cache\nthis array instead to avoid this problem. \n\nSome cache storage, such as MemCache, APC, support retrieving multiple cached values in a batch mode,\nwhich may reduce the overhead involved in retrieving cached data. The APIs [[yii\\caching\\Cache::multiGet()|multiGet()]]\nand [[yii\\caching\\Cache::multiAdd()|multiAdd()]] are provided to exploit this feature. In case the underlying cache storage\ndoes not support this feature, it will be simulated.\n\nBecause [[yii\\caching\\Cache]] implements `ArrayAccess`, a cache component can be used like an array. The following\nare some examples:\n\n```php\n$cache['var1'] = $value1;  // equivalent to: $cache->set('var1', $value1);\n$value2 = $cache['var2'];  // equivalent to: $value2 = $cache->get('var2');\n```\n\n\n### Cache Keys <span id=\"cache-keys\"></span>\n\nEach data item stored in cache is uniquely identified by a key. When you store a data item in cache,\nyou have to specify a key for it. Later when you retrieve the data item from cache, you should provide\nthe corresponding key.\n\nYou may use a string or an arbitrary value as a cache key. When a key is not a string, it will be automatically\nserialized into a string.\n\nA common strategy of defining a cache key is to include all determining factors in terms of an array.\nFor example, [[yii\\db\\Schema]] uses the following key to cache schema information about a database table:\n\n```php\n[\n    __CLASS__,              // schema class name\n    $this->db->dsn,         // DB connection data source name\n    $this->db->username,    // DB connection login user\n    $name,                  // table name\n];\n```\n\nAs you can see, the key includes all necessary information needed to uniquely specify a database table.\n\n> Note: Values stored in cache via [[yii\\caching\\Cache::multiSet()|multiSet()]] or [[yii\\caching\\Cache::multiAdd()|multiAdd()]] can\nhave only string or integer keys. If you need to set more complex key store the value separately via \n[[yii\\caching\\Cache::set()|set()]] or [[yii\\caching\\Cache::add()|add()]].\n\nWhen the same cache storage is used by different applications, you should specify a unique cache key prefix\nfor each application to avoid conflicts of cache keys. This can be done by configuring the [[yii\\caching\\Cache::keyPrefix]]\nproperty. For example, in the application configuration you can write the following code:\n\n```php\n'components' => [\n    'cache' => [\n        'class' => 'yii\\caching\\ApcCache',\n        'keyPrefix' => 'myapp',       // a unique cache key prefix\n    ],\n],\n```\n\nTo ensure interoperability, only alphanumeric characters should be used.\n\n\n### Cache Expiration <span id=\"cache-expiration\"></span>\n\nA data item stored in a cache will remain there forever unless it is removed because of some caching policy\nenforcement (e.g. caching space is full and the oldest data are removed). To change this behavior, you can provide\nan expiration parameter when calling [[yii\\caching\\Cache::set()|set()]] to store a data item. The parameter\nindicates for how many seconds the data item can remain valid in the cache. When you call\n[[yii\\caching\\Cache::get()|get()]] to retrieve the data item, if it has passed the expiration time, the method\nwill return `false`, indicating the data item is not found in the cache. For example,\n\n```php\n// keep the data in cache for at most 45 seconds\n$cache->set($key, $data, 45);\n\nsleep(50);\n\n$data = $cache->get($key);\nif ($data === false) {\n    // $data is expired or is not found in the cache\n}\n```\n\nSince 2.0.11 you may set [[yii\\caching\\Cache::$defaultDuration|defaultDuration]] value in your cache component configuration if you prefer a custom cache duration\nover the default unlimited duration.\nThis will allow you not to pass custom `duration` parameter to [[yii\\caching\\Cache::set()|set()]] each time.\n\n\n### Cache Dependencies <span id=\"cache-dependencies\"></span>\n\nBesides expiration setting, cached data item may also be invalidated by changes of the so-called *cache dependencies*.\nFor example, [[yii\\caching\\FileDependency]] represents the dependency of a file's modification time.\nWhen this dependency changes, it means the corresponding file is modified. As a result, any outdated\nfile content found in the cache should be invalidated and the [[yii\\caching\\Cache::get()|get()]] call\nshould return `false`.\n\nCache dependencies are represented as objects of [[yii\\caching\\Dependency]] descendant classes. When you call\n[[yii\\caching\\Cache::set()|set()]] to store a data item in the cache, you can pass along an associated cache\ndependency object. For example,\n\n```php\n// Create a dependency on the modification time of file example.txt.\n$dependency = new \\yii\\caching\\FileDependency(['fileName' => 'example.txt']);\n\n// The data will expire in 30 seconds.\n// It may also be invalidated earlier if example.txt is modified.\n$cache->set($key, $data, 30, $dependency);\n\n// The cache will check if the data has expired.\n// It will also check if the associated dependency was changed.\n// It will return false if any of these conditions are met.\n$data = $cache->get($key);\n```\n\nBelow is a summary of the available cache dependencies:\n\n- [[yii\\caching\\ChainedDependency]]: the dependency is changed if any of the dependencies on the chain is changed.\n- [[yii\\caching\\DbDependency]]: the dependency is changed if the query result of the specified SQL statement is changed.\n- [[yii\\caching\\ExpressionDependency]]: the dependency is changed if the result of the specified PHP expression is changed.\n- [[yii\\caching\\CallbackDependency]]: the dependency is changed if the result of the specified PHP callback is changed.\n- [[yii\\caching\\FileDependency]]: the dependency is changed if the file's last modification time is changed.\n- [[yii\\caching\\TagDependency]]: associates a cached data item with one or multiple tags. You may invalidate\n  the cached data items with the specified tag(s) by calling [[yii\\caching\\TagDependency::invalidate()]].\n\n> Note: Avoid using [[yii\\caching\\Cache::exists()|exists()]] method along with dependencies. It does not check whether\n  the dependency associated with the cached data, if there is any, has changed. So a call to\n  [[yii\\caching\\Cache::get()|get()]] may return `false` while [[yii\\caching\\Cache::exists()|exists()]] returns `true`.\n\n\n## Query Caching <span id=\"query-caching\"></span>\n\nQuery caching is a special caching feature built on top of data caching. It is provided to cache the result\nof database queries.\n\nQuery caching requires a [[yii\\db\\Connection|DB connection]] and a valid `cache` [application component](#cache-components).\nThe basic usage of query caching is as follows, assuming `$db` is a [[yii\\db\\Connection]] instance:\n\n```php\n$result = $db->cache(function ($db) {\n\n    // the result of the SQL query will be served from the cache\n    // if query caching is enabled and the query result is found in the cache\n    return $db->createCommand('SELECT * FROM customer WHERE id=1')->queryOne();\n\n});\n```\n\nQuery caching can be used for [DAO](db-dao.md) as well as [ActiveRecord](db-active-record.md):\n\n```php\n$result = Customer::getDb()->cache(function ($db) {\n    return Customer::find()->where(['id' => 1])->one();\n});\n```\n\n> Info: Some DBMS (e.g. [MySQL](https://dev.mysql.com/doc/refman/5.6/en/query-cache.html))\n  also support query caching on the DB server-side. You may choose to use either query caching mechanism.\n  The query caching described above has the advantage that you may specify flexible cache dependencies\n  and are potentially more efficient.\n\nSince 2.0.14 you can use the following shortcuts:\n\n```php\n(new Query())->cache(7200)->all();\n// and\nUser::find()->cache(7200)->all();\n```\n\n\n### Configurations <span id=\"query-caching-configs\"></span>\n\nQuery caching has three global configurable options through [[yii\\db\\Connection]]:\n\n* [[yii\\db\\Connection::enableQueryCache|enableQueryCache]]: whether to turn on or off query caching.\n  It defaults to `true`. Note that to effectively turn on query caching, you also need to have a valid\n  cache, as specified by [[yii\\db\\Connection::queryCache|queryCache]].\n* [[yii\\db\\Connection::queryCacheDuration|queryCacheDuration]]: this represents the number of seconds\n  that a query result can remain valid in the cache. You can use 0 to indicate a query result should\n  remain in the cache forever. This property is the default value used when [[yii\\db\\Connection::cache()]]\n  is called without specifying a duration.\n* [[yii\\db\\Connection::queryCache|queryCache]]: this represents the ID of the cache application component.\n  It defaults to `'cache'`. Query caching is enabled only if there is a valid cache application component.\n\n\n### Usages <span id=\"query-caching-usages\"></span>\n\nYou can use [[yii\\db\\Connection::cache()]] if you have multiple SQL queries that need to take advantage of\nquery caching. The usage is as follows,\n\n```php\n$duration = 60;     // cache query results for 60 seconds.\n$dependency = ...;  // optional dependency\n\n$result = $db->cache(function ($db) {\n\n    // ... perform SQL queries here ...\n\n    return $result;\n\n}, $duration, $dependency);\n```\n\nAny SQL queries in the anonymous function will be cached for the specified duration with the specified dependency.\nIf the result of a query is found valid in the cache, the query will be skipped and the result will be served\nfrom the cache instead. If you do not specify the `$duration` parameter, the value of\n[[yii\\db\\Connection::queryCacheDuration|queryCacheDuration]] will be used instead.\n\nSometimes within `cache()`, you may want to disable query caching for some particular queries. You can use\n[[yii\\db\\Connection::noCache()]] in this case.\n\n```php\n$result = $db->cache(function ($db) {\n\n    // SQL queries that use query caching\n\n    $db->noCache(function ($db) {\n\n        // SQL queries that do not use query caching\n\n    });\n\n    // ...\n\n    return $result;\n});\n```\n\nIf you just want to use query caching for a single query, you can call [[yii\\db\\Command::cache()]] when building\nthe command. For example,\n\n```php\n// use query caching and set query cache duration to be 60 seconds\n$customer = $db->createCommand('SELECT * FROM customer WHERE id=1')->cache(60)->queryOne();\n```\n\nYou can also use [[yii\\db\\Command::noCache()]] to disable query caching for a single command. For example,\n\n```php\n$result = $db->cache(function ($db) {\n\n    // SQL queries that use query caching\n\n    // do not use query caching for this command\n    $customer = $db->createCommand('SELECT * FROM customer WHERE id=1')->noCache()->queryOne();\n\n    // ...\n\n    return $result;\n});\n```\n\n\n### Limitations <span id=\"query-caching-limitations\"></span>\n\nQuery caching does not work with query results that contain resource handlers. For example,\nwhen using the `BLOB` column type in some DBMS, the query result will return a resource\nhandler for the column data.\n\nSome caching storage has size limitation. For example, memcache limits the maximum size\nof each entry to be 1MB. Therefore, if the size of a query result exceeds this limit,\nthe caching will fail.\n\n\n## Cache Flushing <span id=\"cache-flushing\"></span>\n\nWhen you need to invalidate all the stored cache data, you can call [[yii\\caching\\Cache::flush()]].\n\nYou can flush the cache from the console by calling `yii cache/flush` as well.\n - `yii cache`: lists the available caches in application\n - `yii cache/flush cache1 cache2`: flushes the cache components `cache1`, `cache2` (you can pass multiple component\n names separated with space)\n - `yii cache/flush-all`: flushes all cache components in the application\n - `yii cache/flush-schema db`: clears DB schema cache for a given connection component\n\n> Info: Console application uses a separate configuration file by default. Ensure, that you have the same caching\ncomponents in your web and console application configs to reach the proper effect.\n"
  },
  {
    "path": "docs/guide/caching-fragment.md",
    "content": "Fragment Caching\n================\n\nFragment caching refers to caching a fragment of a Web page. For example, if a page displays a summary of\nyearly sale in a table, you can store this table in cache to eliminate the time needed to generate this table\nfor each request. Fragment caching is built on top of [data caching](caching-data.md).\n\nTo use fragment caching, use the following construct in a [view](structure-views.md):\n\n```php\nif ($this->beginCache($id)) {\n\n    // ... generate content here ...\n\n    $this->endCache();\n}\n```\n\nThat is, enclose content generation logic in a pair of [[yii\\base\\View::beginCache()|beginCache()]] and\n[[yii\\base\\View::endCache()|endCache()]] calls. If the content is found in the cache, [[yii\\base\\View::beginCache()|beginCache()]]\nwill render the cached content and return `false`, thus skip the content generation logic.\nOtherwise, your content generation logic will be called, and when [[yii\\base\\View::endCache()|endCache()]]\nis called, the generated content will be captured and stored in the cache.\n\nLike [data caching](caching-data.md), a unique `$id` is needed to identify a content cache.\n\nTo delete fragment caching you can use\n```php\nYii::$app->cache->delete(['yii\\widgets\\FragmentCache', $id]);\n```\n\n\n## Caching Options <span id=\"caching-options\"></span>\n\nYou may specify additional options about fragment caching by passing the option array as the second\nparameter to the [[yii\\base\\View::beginCache()|beginCache()]] method. Behind the scene, this option array\nwill be used to configure a [[yii\\widgets\\FragmentCache]] widget which implements the actual fragment caching\nfunctionality.\n\n### Duration <span id=\"duration\"></span>\n\nPerhaps the most commonly used option of fragment caching is [[yii\\widgets\\FragmentCache::duration|duration]].\nIt specifies for how many seconds the content can remain valid in a cache. The following code\ncaches the content fragment for at most one hour:\n\n```php\nif ($this->beginCache($id, ['duration' => 3600])) {\n\n    // ... generate content here ...\n\n    $this->endCache();\n}\n```\n\nIf the option is not set, it will take the default value 60, which means the cached content will expire in 60 seconds.\n\n\n### Dependencies <span id=\"dependencies\"></span>\n\nLike [data caching](caching-data.md#cache-dependencies), content fragment being cached can also have dependencies.\nFor example, the content of a post being displayed depends on whether or not the post is modified.\n\nTo specify a dependency, set the [[yii\\widgets\\FragmentCache::dependency|dependency]] option, which can be\neither an [[yii\\caching\\Dependency]] object or a configuration array for creating a dependency object. The\nfollowing code specifies that the fragment content depends on the change of the `updated_at` column value:\n\n```php\n$dependency = [\n    'class' => 'yii\\caching\\DbDependency',\n    'sql' => 'SELECT MAX(updated_at) FROM post',\n];\n\nif ($this->beginCache($id, ['dependency' => $dependency])) {\n\n    // ... generate content here ...\n\n    $this->endCache();\n}\n```\n\n\n### Variations <span id=\"variations\"></span>\n\nContent being cached may be variated according to some parameters. For example, for a Web application\nsupporting multiple languages, the same piece of view code may generate the content in different languages.\nTherefore, you may want to make the cached content variated according to the current application language.\n\nTo specify cache variations, set the [[yii\\widgets\\FragmentCache::variations|variations]] option, which\nshould be an array of scalar values, each representing a particular variation factor. For example,\nto make the cached content variated by the language, you may use the following code:\n\n```php\nif ($this->beginCache($id, ['variations' => [Yii::$app->language]])) {\n\n    // ... generate content here ...\n\n    $this->endCache();\n}\n```\n\n\n### Toggling Caching <span id=\"toggling-caching\"></span>\n\nSometimes you may want to enable fragment caching only when certain conditions are met. For example, for a page\ndisplaying a form, you only want to cache the form when it is initially requested (via GET request). Any\nsubsequent display (via POST request) of the form should not be cached because the form may contain user input.\nTo do so, you may set the [[yii\\widgets\\FragmentCache::enabled|enabled]] option, like the following:\n\n```php\nif ($this->beginCache($id, ['enabled' => Yii::$app->request->isGet])) {\n\n    // ... generate content here ...\n\n    $this->endCache();\n}\n```\n\n\n## Nested Caching <span id=\"nested-caching\"></span>\n\nFragment caching can be nested. That is, a cached fragment can be enclosed within another fragment which is also cached.\nFor example, the comments are cached in an inner fragment cache, and they are cached together with the\npost content in an outer fragment cache. The following code shows how two fragment caches can be nested:\n\n```php\nif ($this->beginCache($id1)) {\n\n    // ...content generation logic...\n\n    if ($this->beginCache($id2, $options2)) {\n\n        // ...content generation logic...\n\n        $this->endCache();\n    }\n\n    // ...content generation logic...\n\n    $this->endCache();\n}\n```\n\nDifferent caching options can be set for the nested caches. For example, the inner caches and the outer caches\ncan use different cache duration values. Even when the data cached in the outer cache is invalidated, the inner\ncache may still provide the valid inner fragment. However, it is not true vice versa. If the outer cache is\nevaluated to be valid, it will continue to provide the same cached copy even after the content in the\ninner cache has been invalidated. Therefore, you must be careful in setting the durations or the dependencies\nof the nested caches, otherwise the outdated inner fragments may be kept in the outer fragment.\n\n\n## Dynamic Content <span id=\"dynamic-content\"></span>\n\nWhen using fragment caching, you may encounter the situation where a large fragment of content is relatively\nstatic except at one or a few places. For example, a page header may display the main menu bar together with\nthe name of the current user. Another problem is that the content being cached may contain PHP code that\nmust be executed for every request (e.g. the code for registering an asset bundle). Both problems can be solved\nby the so-called *dynamic content* feature.\n\nA dynamic content means a fragment of output that should not be cached even if it is enclosed within\na fragment cache. To make the content dynamic all the time, it has to be generated by executing some PHP code\nfor every request, even if the enclosing content is being served from cache.\n\nYou may call [[yii\\base\\View::renderDynamic()]] within a cached fragment to insert dynamic content\nat the desired place, like the following,\n\n```php\nif ($this->beginCache($id1)) {\n\n    // ...content generation logic...\n\n    echo $this->renderDynamic('return Yii::$app->user->identity->name;');\n\n    // ...content generation logic...\n\n    $this->endCache();\n}\n```\n\nThe [[yii\\base\\View::renderDynamic()|renderDynamic()]] method takes a piece of PHP code as its parameter.\nThe return value of the PHP code is treated as the dynamic content. The same PHP code will be executed\nfor every request, no matter the enclosing fragment is being served from cached or not.\n\n> Note: since version 2.0.14 a dynamic content API is exposed via the [[yii\\base\\DynamicContentAwareInterface]] interface and its [[yii\\base\\DynamicContentAwareTrait]] trait.\n  As an example, you may refer to the [[yii\\widgets\\FragmentCache]] class.\n"
  },
  {
    "path": "docs/guide/caching-http.md",
    "content": "HTTP Caching\n============\n\nBesides server-side caching that we have described in the previous sections, Web applications may\nalso exploit client-side caching to save the time for generating and transmitting the same page content.\n\nTo use client-side caching, you may configure [[yii\\filters\\HttpCache]] as a filter for controller\nactions whose rendering result may be cached on the client-side. [[yii\\filters\\HttpCache|HttpCache]]\nonly works for `GET` and `HEAD` requests. It can handle three kinds of cache-related HTTP headers for these requests:\n\n* [[yii\\filters\\HttpCache::lastModified|Last-Modified]]\n* [[yii\\filters\\HttpCache::etagSeed|Etag]]\n* [[yii\\filters\\HttpCache::cacheControlHeader|Cache-Control]]\n\n\n## `Last-Modified` Header <span id=\"last-modified\"></span>\n\nThe `Last-Modified` header uses a timestamp to indicate if the page has been modified since the client caches it.\n\nYou may configure the [[yii\\filters\\HttpCache::lastModified]] property to enable sending\nthe `Last-Modified` header. The property should be a PHP callable returning a UNIX timestamp about\nthe page modification time. The signature of the PHP callable should be as follows,\n\n```php\n/**\n * @param Action $action the action object that is being handled currently\n * @param array $params the value of the \"params\" property\n * @return int a UNIX timestamp representing the page modification time\n */\nfunction ($action, $params)\n```\n\nThe following is an example of making use of the `Last-Modified` header:\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\HttpCache',\n            'only' => ['index'],\n            'lastModified' => function ($action, $params) {\n                $q = new \\yii\\db\\Query();\n                return $q->from('post')->max('updated_at');\n            },\n        ],\n    ];\n}\n```\n\nThe above code states that HTTP caching should be enabled for the `index` action only. It should\ngenerate a `Last-Modified` HTTP header based on the last update time of posts. When a browser visits\nthe `index` page for the first time, the page will be generated on the server and sent to the browser;\nIf the browser visits the same page again and there is no post being modified during the period,\nthe server will not re-generate the page, and the browser will use the cached version on the client-side.\nAs a result, server-side rendering and page content transmission are both skipped.\n\n\n## `ETag` Header <span id=\"etag\"></span>\n\nThe \"Entity Tag\" (or `ETag` for short) header use a hash to represent the content of a page. If the page\nis changed, the hash will be changed as well. By comparing the hash kept on the client-side with the hash\ngenerated on the server-side, the cache may determine whether the page has been changed and should be re-transmitted.\n\nYou may configure the [[yii\\filters\\HttpCache::etagSeed]] property to enable sending the `ETag` header.\nThe property should be a PHP callable returning a seed for generating the ETag hash. The signature of the PHP callable\nshould be as follows,\n\n```php\n/**\n * @param Action $action the action object that is being handled currently\n * @param array $params the value of the \"params\" property\n * @return string a string used as the seed for generating an ETag hash\n */\nfunction ($action, $params)\n```\n\nThe following is an example of making use of the `ETag` header:\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\HttpCache',\n            'only' => ['view'],\n            'etagSeed' => function ($action, $params) {\n                $post = $this->findModel(\\Yii::$app->request->get('id'));\n                return serialize([$post->title, $post->content]);\n            },\n        ],\n    ];\n}\n```\n\nThe above code states that HTTP caching should be enabled for the `view` action only. It should\ngenerate an `ETag` HTTP header based on the title and content of the requested post. When a browser visits\nthe `view` page for the first time, the page will be generated on the server and sent to the browser;\nIf the browser visits the same page again and there is no change to the title and content of the post,\nthe server will not re-generate the page, and the browser will use the cached version on the client-side.\nAs a result, server-side rendering and page content transmission are both skipped.\n\nETags allow more complex and/or more precise caching strategies than `Last-Modified` headers.\nFor instance, an ETag can be invalidated if the site has switched to another theme.\n\nExpensive ETag generation may defeat the purpose of using `HttpCache` and introduce unnecessary overhead,\nsince they need to be re-evaluated on every request. Try to find a simple expression that invalidates\nthe cache if the page content has been modified.\n\n> Note: In compliance to [RFC 7232](https://datatracker.ietf.org/doc/html/rfc7232#section-2.4),\n  `HttpCache` will send out both `ETag` and `Last-Modified` headers if they are both configured.\n  And if the client sends both of the `If-None-Match` header and the `If-Modified-Since` header, only the former\n  will be respected.\n\n\n## `Cache-Control` Header <span id=\"cache-control\"></span>\n\nThe `Cache-Control` header specifies the general caching policy for pages. You may send it by configuring\nthe [[yii\\filters\\HttpCache::cacheControlHeader]] property with the header value. By default, the following\nheader will be sent:\n\n```\nCache-Control: public, max-age=3600\n```\n\n## Session Cache Limiter <span id=\"session-cache-limiter\"></span>\n\nWhen a page uses session, PHP will automatically send some cache-related HTTP headers as specified in\nthe `session.cache_limiter` PHP INI setting. These headers may interfere or disable the caching\nthat you want from `HttpCache`. To prevent this problem, by default `HttpCache` will disable sending\nthese headers automatically. If you want to change this behavior, you should configure the\n[[yii\\filters\\HttpCache::sessionCacheLimiter]] property. The property can take a string value, including\n`public`, `private`, `private_no_expire`, and `nocache`. Please refer to the PHP manual about\n[session_cache_limiter()](https://www.php.net/manual/en/function.session-cache-limiter.php)\nfor explanations about these values.\n\n\n## SEO Implications <span id=\"seo-implications\"></span>\n\nSearch engine bots tend to respect cache headers. Since some crawlers have a limit on how many pages\nper domain they process within a certain time span, introducing caching headers may help indexing your\nsite as they reduce the number of pages that need to be processed.\n\n"
  },
  {
    "path": "docs/guide/caching-overview.md",
    "content": "Caching\n=======\n\nCaching is a cheap and effective way to improve the performance of a Web application. By storing relatively\nstatic data in cache and serving it from cache when requested, the application saves the time that would be\nrequired to generate the data from scratch every time.\n\nCaching can occur at different levels and places in a Web application. On the server-side, at the lower level,\ncache may be used to store basic data, such as a list of most recent article information fetched from database;\nand at the higher level, cache may be used to store fragments or whole of Web pages, such as the rendering result\nof the most recent articles. On the client-side, HTTP caching may be used to keep most recently visited page content in\nthe browser cache.\n\nYii supports all these caching mechanisms:\n\n* [Data caching](caching-data.md)\n* [Fragment caching](caching-fragment.md)\n* [Page caching](caching-page.md)\n* [HTTP caching](caching-http.md)\n"
  },
  {
    "path": "docs/guide/caching-page.md",
    "content": "Page Caching\n============\n\nPage caching refers to caching the content of a whole page on the server-side. Later when the same page\nis requested again, its content will be served from the cache instead of regenerating it from scratch.\n\nPage caching is supported by [[yii\\filters\\PageCache]], an [action filter](structure-filters.md).\nIt can be used like the following in a controller class:\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\PageCache',\n            'only' => ['index'],\n            'duration' => 60,\n            'variations' => [\n                \\Yii::$app->language,\n            ],\n            'dependency' => [\n                'class' => 'yii\\caching\\DbDependency',\n                'sql' => 'SELECT COUNT(*) FROM post',\n            ],\n        ],\n    ];\n}\n```\n\nThe above code states that page caching should be used only for the `index` action. The page content should\nbe cached for at most 60 seconds and should be variated by the current application language\nand the cached page should be invalidated if the total number of posts is changed.\n\nAs you can see, page caching is very similar to [fragment caching](caching-fragment.md). They both support options such\nas `duration`, `dependencies`, `variations`, and `enabled`. Their main difference is that page caching is\nimplemented as an [action filter](structure-filters.md) while fragment caching a [widget](structure-widgets.md).\n\nYou can use [fragment caching](caching-fragment.md) as well as [dynamic content](caching-fragment.md#dynamic-content)\ntogether with page caching.\n\n"
  },
  {
    "path": "docs/guide/concept-aliases.md",
    "content": "Aliases\n=======\n\nAliases are used to represent file paths or URLs so that you don't have to hard-code absolute paths or URLs in your\nproject. An alias must start with the `@` character to be differentiated from normal file paths and URLs. Alias defined\nwithout leading `@` will be prefixed with `@` character.\n\nYii has many pre-defined aliases already available. For example, the alias `@yii` represents the installation path of\nthe Yii framework; `@web` represents the base URL for the currently running Web application.\n\nDefining Aliases <span id=\"defining-aliases\"></span>\n----------------\n\nYou can define an alias for a file path or URL by calling [[Yii::setAlias()]]:\n\n```php\n// an alias of a file path\nYii::setAlias('@foo', '/path/to/foo');\n\n// an alias of a URL\nYii::setAlias('@bar', 'https://www.example.com');\n\n// an alias of a concrete file that contains a \\foo\\Bar class\nYii::setAlias('@foo/Bar.php', '/definitely/not/foo/Bar.php');\n```\n\n> Note: The file path or URL being aliased may *not* necessarily refer to an existing file or resource.\n\nGiven a defined alias, you may derive a new alias (without the need of calling [[Yii::setAlias()]]) by appending\na slash `/` followed with one or more path segments. The aliases defined via [[Yii::setAlias()]] becomes the \n*root alias*, while aliases derived from it are *derived aliases*. For example, `@foo` is a root alias,\nwhile `@foo/bar/file.php` is a derived alias.\n\nYou can define an alias using another alias (either root or derived):\n\n```php\nYii::setAlias('@foobar', '@foo/bar');\n```\n\nRoot aliases are usually defined during the [bootstrapping](runtime-bootstrapping.md) stage.\nFor example, you may call [[Yii::setAlias()]] in the [entry script](structure-entry-scripts.md).\nFor convenience, [Application](structure-applications.md) provides a writable property named `aliases`\nthat you can configure in the application [configuration](concept-configurations.md):\n\n```php\nreturn [\n    // ...\n    'aliases' => [\n        '@foo' => '/path/to/foo',\n        '@bar' => 'https://www.example.com',\n    ],\n];\n```\n\n\nResolving Aliases <span id=\"resolving-aliases\"></span>\n-----------------\n\nYou can call [[Yii::getAlias()]] to resolve a root alias into the file path or URL it represents.\nThe same method can also resolve a derived alias into the corresponding file path or URL:\n\n```php\necho Yii::getAlias('@foo');               // displays: /path/to/foo\necho Yii::getAlias('@bar');               // displays: https://www.example.com\necho Yii::getAlias('@foo/bar/file.php');  // displays: /path/to/foo/bar/file.php\n```\n\nThe path/URL represented by a derived alias is determined by replacing the root alias part with its corresponding\npath/URL in the derived alias.\n\n> Note: The [[Yii::getAlias()]] method does not check whether the resulting path/URL refers to an existing file or resource.\n\n\nA root alias may also contain slash `/` characters. The [[Yii::getAlias()]] method\nis intelligent enough to tell which part of an alias is a root alias and thus correctly determines\nthe corresponding file path or URL:\n\n```php\nYii::setAlias('@foo', '/path/to/foo');\nYii::setAlias('@foo/bar', '/path2/bar');\nYii::getAlias('@foo/test/file.php');  // displays: /path/to/foo/test/file.php\nYii::getAlias('@foo/bar/file.php');   // displays: /path2/bar/file.php\n```\n\nIf `@foo/bar` is not defined as a root alias, the last statement would display `/path/to/foo/bar/file.php`.\n\n\nUsing Aliases <span id=\"using-aliases\"></span>\n-------------\n\nAliases are recognized in many places in Yii without needing to call [[Yii::getAlias()]] to convert\nthem into paths or URLs. For example, [[yii\\caching\\FileCache::cachePath]] can accept both a file path\nand an alias representing a file path, thanks to the `@` prefix which allows it to differentiate a file path\nfrom an alias.\n\n```php\nuse yii\\caching\\FileCache;\n\n$cache = new FileCache([\n    'cachePath' => '@runtime/cache',\n]);\n```\n\nPlease pay attention to the API documentation to see if a property or method parameter supports aliases.\n\n\nPredefined Aliases <span id=\"predefined-aliases\"></span>\n------------------\n\nYii predefines a set of aliases to easily reference commonly used file paths and URLs:\n\n- `@yii`, the directory where the `BaseYii.php` file is located (also called the framework directory).\n- `@app`, the [[yii\\base\\Application::basePath|base path]] of the currently running application.\n- `@runtime`, the [[yii\\base\\Application::runtimePath|runtime path]] of the currently running application. Defaults to `@app/runtime`.\n- `@webroot`, the Web root directory of the currently running Web application. It is determined based on the directory\n  containing the [entry script](structure-entry-scripts.md).\n- `@web`, the base URL of the currently running Web application. It has the same value as [[yii\\web\\Request::baseUrl]].\n- `@vendor`, the [[yii\\base\\Application::vendorPath|Composer vendor directory]]. Defaults to `@app/vendor`.\n- `@bower`, the root directory that contains [bower packages](https://bower.io/). Defaults to `@vendor/bower`.\n- `@npm`, the root directory that contains [npm packages](https://www.npmjs.com/). Defaults to `@vendor/npm`.\n\nThe `@yii` alias is defined when you include the `Yii.php` file in your [entry script](structure-entry-scripts.md).\nThe rest of the aliases are defined in the application constructor when applying the application\n[configuration](concept-configurations.md).\n\n> Note: `@web` and `@webroot` aliases as their descriptions indicate are defined within [[yii\\web\\Application|Web application]] and therefore are not available for [[yii\\console\\Application|Console application]] by default.\n\nExtension Aliases <span id=\"extension-aliases\"></span>\n-----------------\n\nAn alias is automatically defined for each [extension](structure-extensions.md) that is installed via Composer.\nEach alias is named after the root namespace of the extension as declared in its `composer.json` file, and each alias\nrepresents the root directory of the package. For example, if you install the `yiisoft/yii2-jui` extension,\nyou will automatically have the alias `@yii/jui` defined during the [bootstrapping](runtime-bootstrapping.md) stage, equivalent to:\n\n```php\nYii::setAlias('@yii/jui', 'VendorPath/yiisoft/yii2-jui');\n```\n"
  },
  {
    "path": "docs/guide/concept-autoloading.md",
    "content": "Class Autoloading\n=================\n\nYii relies on the [class autoloading mechanism](https://www.php.net/manual/en/language.oop5.autoload.php)\nto locate and include all required class files. It provides a high-performance class autoloader that is compliant with the\n[PSR-4 standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md).\nThe autoloader is installed when you include the `Yii.php` file.\n\n> Note: For simplicity of description, in this section we will only talk about autoloading of classes. However, keep in\n  mind that the content we are describing here applies to autoloading of interfaces and traits as well.\n\n\nUsing the Yii Autoloader <span id=\"using-yii-autoloader\"></span>\n------------------------\n\nTo make use of the Yii class autoloader, you should follow two simple rules when creating and naming your classes:\n\n* Each class must be under a [namespace](https://www.php.net/manual/en/language.namespaces.php) (e.g. `foo\\bar\\MyClass`)\n* Each class must be saved in an individual file whose path is determined by the following algorithm:\n\n```php\n// $className is a fully qualified class name without the leading backslash\n$classFile = Yii::getAlias('@' . str_replace('\\\\', '/', $className) . '.php');\n```\n\nFor example, if a class name and namespace is `foo\\bar\\MyClass`, the [alias](concept-aliases.md) for the corresponding class file path\nwould be `@foo/bar/MyClass.php`. In order for this alias to be resolvable into a file path,\neither `@foo` or `@foo/bar` must be a [root alias](concept-aliases.md#defining-aliases).\n\nWhen using the [Basic Project Template](start-installation.md), you may put your classes under the top-level\nnamespace `app` so that they can be autoloaded by Yii without the need of defining a new alias. This is because\n`@app` is a [predefined alias](concept-aliases.md#predefined-aliases), and a class name like `app\\components\\MyClass`\ncan be resolved into the class file `AppBasePath/components/MyClass.php`, according to the algorithm just described.\n\nIn the [Advanced Project Template](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md), each tier has its own root alias. For example,\nthe front-end tier has a root alias `@frontend`, while the back-end tier root alias is `@backend`. As a result,\nyou may put the front-end classes under the namespace `frontend` while the back-end classes are under `backend`. This will\nallow these classes to be autoloaded by the Yii autoloader.\n\nTo add a custom namespace to the autoloader you need to define an alias for the base directory of the namespace using [[Yii::setAlias()]].\nFor example to load classes in the `foo` namespace that are located in the `path/to/foo` directory you will call `Yii::setAlias('@foo', 'path/to/foo')`.\n\nClass Map <span id=\"class-map\"></span>\n---------\n\nThe Yii class autoloader supports the *class map* feature, which maps class names to the corresponding class file paths.\nWhen the autoloader is loading a class, it will first check if the class is found in the map. If so, the corresponding\nfile path will be included directly without further checks. This makes class autoloading super fast. In fact,\nall core Yii classes are autoloaded this way.\n\nYou may add a class to the class map, stored in `Yii::$classMap`, using:\n\n```php\nYii::$classMap['foo\\bar\\MyClass'] = 'path/to/MyClass.php';\n```\n\n[Aliases](concept-aliases.md) can be used to specify class file paths. You should set the class map in the\n[bootstrapping](runtime-bootstrapping.md) process so that the map is ready before your classes are used.\n\n\nUsing Other Autoloaders <span id=\"using-other-autoloaders\"></span>\n-----------------------\n\nBecause Yii embraces Composer as a package dependency manager, it is recommended that you also install\nthe Composer autoloader. If you are using 3rd-party libraries that have their own autoloaders, you should\nalso install those.\n\nWhen using the Yii autoloader together with other autoloaders, you should include the `Yii.php` file\n*after* all other autoloaders are installed. This will make the Yii autoloader the first one responding to\nany class autoloading request. For example, the following code is extracted from\nthe [entry script](structure-entry-scripts.md) of the [Basic Project Template](start-installation.md). The first\nline installs the Composer autoloader, while the second line installs the Yii autoloader:\n\n```php\nrequire __DIR__ . '/../vendor/autoload.php';\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n```\n\nYou may use the Composer autoloader alone without the Yii autoloader. However, by doing so, the performance\nof your class autoloading may be degraded, and you must follow the rules set by Composer in order for your classes\nto be autoloadable.\n\n> Info: If you do not want to use the Yii autoloader, you must create your own version of the `Yii.php` file\n  and include it in your [entry script](structure-entry-scripts.md).\n\n\nAutoloading Extension Classes <span id=\"autoloading-extension-classes\"></span>\n-----------------------------\n\nThe Yii autoloader is capable of autoloading [extension](structure-extensions.md) classes. The sole requirement\nis that an extension specifies the `autoload` section correctly in its `composer.json` file. Please refer to the\n[Composer documentation](https://getcomposer.org/doc/04-schema.md#autoload) for more details about specifying `autoload`.\n\nIn case you do not use the Yii autoloader, the Composer autoloader can still autoload extension classes for you.\n"
  },
  {
    "path": "docs/guide/concept-behaviors.md",
    "content": "Behaviors\n=========\n\nBehaviors are instances of [[yii\\base\\Behavior]], or of a child class. Behaviors, also known\nas [mixins](https://en.wikipedia.org/wiki/Mixin), allow you to enhance the functionality\nof an existing [[yii\\base\\Component|component]] class without needing to change the class's inheritance.\nAttaching a behavior to a component \"injects\" the behavior's methods and properties into the component, making those methods and properties accessible as if they were defined in the component class itself. Moreover, a behavior\ncan respond to the [events](concept-events.md) triggered by the component, which allows behaviors to also customize the normal\ncode execution of the component.\n\n\nDefining Behaviors <span id=\"defining-behaviors\"></span>\n------------------\n\nTo define a behavior, create a class that extends [[yii\\base\\Behavior]], or extends a child class. For example:\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Behavior;\n\nclass MyBehavior extends Behavior\n{\n    public $prop1;\n\n    private $_prop2;\n\n    public function getProp2()\n    {\n        return $this->_prop2;\n    }\n\n    public function setProp2($value)\n    {\n        $this->_prop2 = $value;\n    }\n\n    public function foo()\n    {\n        // ...\n    }\n}\n```\n\nThe above code defines the behavior class `app\\components\\MyBehavior`, with two properties\n`prop1` and `prop2` and one method `foo()`. Note that property `prop2`\nis defined via the getter `getProp2()` and the setter `setProp2()`. This is the case because [[yii\\base\\Behavior]] extends [[yii\\base\\BaseObject]] and therefore supports defining [properties](concept-properties.md) via getters and setters.\n\nBecause this class is a behavior, when it is attached to a component, that component will then also have the `prop1` and `prop2` properties and the `foo()` method.\n\n> Tip: Within a behavior, you can access the component that the behavior is attached to through the [[yii\\base\\Behavior::owner]] property.\n\n> Note: In case [[yii\\base\\Behavior::__get()]] and/or [[yii\\base\\Behavior::__set()]] method of behavior is overridden you\nneed to override [[yii\\base\\Behavior::canGetProperty()]] and/or [[yii\\base\\Behavior::canSetProperty()]] as well.\n\nHandling Component Events\n------------------\n\nIf a behavior needs to respond to the events triggered by the component it is attached to, it should override the\n[[yii\\base\\Behavior::events()]] method. For example:\n\n```php\nnamespace app\\components;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\base\\Behavior;\n\nclass MyBehavior extends Behavior\n{\n    // ...\n\n    public function events()\n    {\n        return [\n            ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',\n        ];\n    }\n\n    public function beforeValidate($event)\n    {\n        // ...\n    }\n}\n```\n\nThe [[yii\\base\\Behavior::events()|events()]] method should return a list of events and their corresponding handlers.\nThe above example declares that the [[yii\\db\\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]] event exists and defines\nits handler, `beforeValidate()`. When specifying an event handler, you may use one of the following formats:\n\n* a string that refers to the name of a method of the behavior class, like the example above\n* an array of an object or class name, and a method name as a string (without parentheses), e.g., `[$object, 'methodName']`;\n* an anonymous function\n\nThe signature of an event handler should be as follows, where `$event` refers to the event parameter. Please refer\nto the [Events](concept-events.md) section for more details about events.\n\n```php\nfunction ($event) {\n}\n```\n\nAttaching Behaviors <span id=\"attaching-behaviors\"></span>\n-------------------\n\nYou can attach a behavior to a [[yii\\base\\Component|component]] either statically or dynamically. The former is more common in practice.\n\nTo attach a behavior statically, override the [[yii\\base\\Component::behaviors()|behaviors()]] method of the component\nclass to which the behavior is being attached. The [[yii\\base\\Component::behaviors()|behaviors()]] method should return a list of behavior [configurations](concept-configurations.md).\nEach behavior configuration can be either a behavior class name or a configuration array:\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\nuse app\\components\\MyBehavior;\n\nclass User extends ActiveRecord\n{\n    public function behaviors()\n    {\n        return [\n            // anonymous behavior, behavior class name only\n            MyBehavior::class,\n\n            // named behavior, behavior class name only\n            'myBehavior2' => MyBehavior::class,\n\n            // anonymous behavior, configuration array\n            [\n                'class' => MyBehavior::class,\n                'prop1' => 'value1',\n                'prop2' => 'value2',\n            ],\n\n            // named behavior, configuration array\n            'myBehavior4' => [\n                'class' => MyBehavior::class,\n                'prop1' => 'value1',\n                'prop2' => 'value2',\n            ]\n        ];\n    }\n}\n```\n\nYou may associate a name with a behavior by specifying the array key corresponding to the behavior configuration. In this case, the behavior is called a *named behavior*. In the above example, there are two named behaviors:\n`myBehavior2` and `myBehavior4`. If a behavior is not associated with a name, it is called an *anonymous behavior*.\n\n\nTo attach a behavior dynamically, call the [[yii\\base\\Component::attachBehavior()]] method of the component to which the behavior is being attached:\n\n```php\nuse app\\components\\MyBehavior;\n\n// attach a behavior object\n$component->attachBehavior('myBehavior1', new MyBehavior());\n\n// attach a behavior class\n$component->attachBehavior('myBehavior2', MyBehavior::class);\n\n// attach a configuration array\n$component->attachBehavior('myBehavior3', [\n    'class' => MyBehavior::class,\n    'prop1' => 'value1',\n    'prop2' => 'value2',\n]);\n```\n\nYou may attach multiple behaviors at once using the [[yii\\base\\Component::attachBehaviors()]] method:\n\n```php\n$component->attachBehaviors([\n    'myBehavior1' => new MyBehavior(), // a named behavior\n    MyBehavior::class,                 // an anonymous behavior\n]);\n```\n\nYou may also attach behaviors through [configurations](concept-configurations.md) like the following: \n\n```php\n[\n    'as myBehavior2' => MyBehavior::class,\n\n    'as myBehavior3' => [\n        'class' => MyBehavior::class,\n        'prop1' => 'value1',\n        'prop2' => 'value2',\n    ],\n]\n```\n\nFor more details,\nplease refer to the [Configurations](concept-configurations.md#configuration-format) section.\n\nUsing Behaviors <span id=\"using-behaviors\"></span>\n---------------\n\nTo use a behavior, first attach it to a [[yii\\base\\Component|component]] per the instructions above. Once a behavior is attached to a component, its usage is straightforward.\n\nYou can access a *public* member variable or a [property](concept-properties.md) defined by a getter and/or a setter\nof the behavior through the component it is attached to:\n\n```php\n// \"prop1\" is a property defined in the behavior class\necho $component->prop1;\n$component->prop1 = $value;\n```\n\nYou can also call a *public* method of the behavior similarly:\n\n```php\n// foo() is a public method defined in the behavior class\n$component->foo();\n```\n\nAs you can see, although `$component` does not define `prop1` and `foo()`, they can be used as if they are part\nof the component definition due to the attached behavior.\n\nIf two behaviors define the same property or method and they are both attached to the same component,\nthe behavior that is attached to the component *first* will take precedence when the property or method is accessed.\n\nA behavior may be associated with a name when it is attached to a component. If this is the case, you may\naccess the behavior object using the name:\n\n```php\n$behavior = $component->getBehavior('myBehavior');\n```\n\nYou may also get all behaviors attached to a component:\n\n```php\n$behaviors = $component->getBehaviors();\n```\n\n\nDetaching Behaviors <span id=\"detaching-behaviors\"></span>\n-------------------\n\nTo detach a behavior, call [[yii\\base\\Component::detachBehavior()]] with the name associated with the behavior:\n\n```php\n$component->detachBehavior('myBehavior1');\n```\n\nYou may also detach *all* behaviors:\n\n```php\n$component->detachBehaviors();\n```\n\n\nUsing `TimestampBehavior` <span id=\"using-timestamp-behavior\"></span>\n-------------------------\n\nTo wrap up, let's take a look at [[yii\\behaviors\\TimestampBehavior]]. This behavior supports automatically\nupdating the timestamp attributes of an [[yii\\db\\ActiveRecord|Active Record]] model anytime the model is saved via\n`insert()`, `update()` or `save()` method.\n\nFirst, attach this behavior to the [[yii\\db\\ActiveRecord|Active Record]] class that you plan to use:\n\n```php\nnamespace app\\models\\User;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\behaviors\\TimestampBehavior;\n\nclass User extends ActiveRecord\n{\n    // ...\n\n    public function behaviors()\n    {\n        return [\n            [\n                'class' => TimestampBehavior::class,\n                'attributes' => [\n                    ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],\n                    ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'],\n                ],\n                // if you're using datetime instead of UNIX timestamp:\n                // 'value' => new Expression('NOW()'),\n            ],\n        ];\n    }\n}\n```\n\nThe behavior configuration above specifies that when the record is being:\n\n* inserted, the behavior should assign the current UNIX timestamp to\n  the `created_at` and `updated_at` attributes\n* updated, the behavior should assign the current UNIX timestamp to the `updated_at` attribute\n\n> Note: For the above implementation to work with MySQL database, please declare the columns(`created_at`, `updated_at`) as int(11) for being UNIX timestamp.\n\nWith that code in place, if you have a `User` object and try to save it, you will find its `created_at` and `updated_at` are automatically\nfilled with the current UNIX timestamp:\n\n```php\n$user = new User;\n$user->email = 'test@example.com';\n$user->save();\necho $user->created_at;  // shows the current timestamp\n```\n\nThe [[yii\\behaviors\\TimestampBehavior|TimestampBehavior]] also offers a useful method\n[[yii\\behaviors\\TimestampBehavior::touch()|touch()]], which will assign the current timestamp\nto a specified attribute and save it to the database:\n\n```php\n$user->touch('login_time');\n```\n\nOther behaviors\n---------------\n\nThere are several built-in and external behaviors available:\n\n- [[yii\\behaviors\\BlameableBehavior]] - automatically fills the specified attributes with the current user ID.\n- [[yii\\behaviors\\SluggableBehavior]] - automatically fills the specified attribute with a value that can be used\n  as a slug in a URL.\n- [[yii\\behaviors\\AttributeBehavior]] - automatically assigns a specified value to one or multiple attributes of\n  an ActiveRecord object when certain events happen.\n- [yii2tech\\ar\\softdelete\\SoftDeleteBehavior](https://github.com/yii2tech/ar-softdelete) - provides methods to soft-delete\n  and soft-restore ActiveRecord i.e. set flag or status which marks record as deleted.\n- [yii2tech\\ar\\position\\PositionBehavior](https://github.com/yii2tech/ar-position) - allows managing records order in an\n  integer field by providing reordering methods.\n\nComparing Behaviors with Traits <span id=\"comparison-with-traits\"></span>\n----------------------\n\nWhile behaviors are similar to [traits](https://www.php.net/traits) in that they both \"inject\" their\nproperties and methods to the primary class, they differ in many aspects. As explained below, they\nboth have pros and cons. They are more like complements to each other rather than alternatives.\n\n\n### Reasons to Use Behaviors <span id=\"pros-for-behaviors\"></span>\n\nBehavior classes, like normal classes, support inheritance. Traits, on the other hand,\ncan be considered as language-supported copy and paste. They do not support inheritance.\n\nBehaviors can be attached and detached to a component dynamically without requiring modification of the component class.\nTo use a trait, you must modify the code of the class using it.\n\nBehaviors are configurable while traits are not.\n\nBehaviors can customize the code execution of a component by responding to its events.\n\nWhen there can be name conflicts among different behaviors attached to the same component, the conflicts are\nautomatically resolved by prioritizing the behavior attached to the component first.\nName conflicts caused by different traits requires manual resolution by renaming the affected\nproperties or methods.\n\n\n### Reasons to Use Traits <span id=\"pros-for-traits\"></span>\n\nTraits are much more efficient than behaviors as behaviors are objects that take both time and memory.\n\nIDEs are more friendly to traits as they are a native language construct.\n\n"
  },
  {
    "path": "docs/guide/concept-components.md",
    "content": "Components\n==========\n\nComponents are the main building blocks of Yii applications. Components are instances of [[yii\\base\\Component]],\nor an extended class. The three main features that components provide to other classes are:\n\n* [Properties](concept-properties.md)\n* [Events](concept-events.md)\n* [Behaviors](concept-behaviors.md)\n \nSeparately and combined, these features make Yii classes much more customizable and easier to use. For example,\nthe included [[yii\\jui\\DatePicker|date picker widget]], a user interface component, can be used in a [view](structure-views.md)\nto generate an interactive date picker:\n\n```php\nuse yii\\jui\\DatePicker;\n\necho DatePicker::widget([\n    'language' => 'ru',\n    'name'  => 'country',\n    'clientOptions' => [\n        'dateFormat' => 'yy-mm-dd',\n    ],\n]);\n```\n\nThe widget's properties are easily writable because the class extends [[yii\\base\\Component]].\n\nWhile components are very powerful, they are a bit heavier than normal objects, due to the fact that\nit takes extra memory and CPU time to support [event](concept-events.md) and [behavior](concept-behaviors.md) functionality in particular.\nIf your components do not need these two features, you may consider extending your component class from\n[[yii\\base\\BaseObject]] instead of [[yii\\base\\Component]]. Doing so will make your components as efficient as normal PHP objects,\nbut with added support for [properties](concept-properties.md).\n\nWhen extending your class from [[yii\\base\\Component]] or [[yii\\base\\BaseObject]], it is recommended that you follow\nthese conventions:\n\n- If you override the constructor, specify a `$config` parameter as the constructor's *last* parameter, and then pass this parameter\n  to the parent constructor.\n- Always call the parent constructor *at the end* of your overriding constructor.\n- If you override the [[yii\\base\\BaseObject::init()]] method, make sure you call the parent implementation of `init()` *at the beginning* of your `init()` method.\n\nFor example:\n\n```php\n<?php\n\nnamespace yii\\components\\MyClass;\n\nuse yii\\base\\BaseObject;\n\nclass MyClass extends BaseObject\n{\n    public $prop1;\n    public $prop2;\n\n    public function __construct($param1, $param2, $config = [])\n    {\n        // ... initialization before configuration is applied\n\n        parent::__construct($config);\n    }\n\n    public function init()\n    {\n        parent::init();\n\n        // ... initialization after configuration is applied\n    }\n}\n```\n\nFollowing these guidelines will make your components [configurable](concept-configurations.md) when they are created. For example:\n\n```php\n$component = new MyClass(1, 2, ['prop1' => 3, 'prop2' => 4]);\n// alternatively\n$component = \\Yii::createObject([\n    'class' => MyClass::class,\n    'prop1' => 3,\n    'prop2' => 4,\n], [1, 2]);\n```\n\n> Info: While the approach of calling [[Yii::createObject()]] looks more complicated, it is more powerful because it is\n> implemented on top of a [dependency injection container](concept-di-container.md).\n  \n\nThe [[yii\\base\\BaseObject]] class enforces the following object lifecycle:\n\n1. Pre-initialization within the constructor. You can set default property values here.\n2. Object configuration via `$config`. The configuration may overwrite the default values set within the constructor.\n3. Post-initialization within [[yii\\base\\BaseObject::init()|init()]]. You may override this method to perform sanity checks and normalization of the properties.\n4. Object method calls.\n\nThe first three steps all happen within the object's constructor. This means that once you get a class instance (i.e., an object),\nthat object has already been initialized to a proper, reliable state.\n"
  },
  {
    "path": "docs/guide/concept-configurations.md",
    "content": "Configurations\n==============\n\nConfigurations are widely used in Yii when creating new objects or initializing existing objects.\nConfigurations usually include the class name of the object being created, and a list of initial values\nthat should be assigned to the object's [properties](concept-properties.md). Configurations may also include a list of\nhandlers that should be attached to the object's [events](concept-events.md) and/or a list of\n[behaviors](concept-behaviors.md) that should also be attached to the object.\n\nIn the following, a configuration is used to create and initialize a database connection:\n\n```php\n$config = [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n];\n\n$db = Yii::createObject($config);\n```\n\nThe [[Yii::createObject()]] method takes a configuration array as its argument, and creates an object by instantiating\nthe class named in the configuration. When the object is instantiated, the rest of the configuration\nwill be used to initialize the object's properties, event handlers, and behaviors.\n\nIf you already have an object, you may use [[Yii::configure()]] to initialize the object's properties with\na configuration array:\n\n```php\nYii::configure($object, $config);\n```\n\nNote that, in this case, the configuration array should not contain a `class` element.\n\n\n## Configuration Format <span id=\"configuration-format\"></span>\n\nThe format of a configuration can be formally described as:\n\n```php\n[\n    'class' => 'ClassName',\n    'propertyName' => 'propertyValue',\n    'on eventName' => $eventHandler,\n    'as behaviorName' => $behaviorConfig,\n]\n```\n\nwhere\n\n* The `class` element specifies a fully qualified class name for the object being created.\n* The `propertyName` elements specify the initial values for the named property. The keys are the property names, and the\n  values are the corresponding initial values. Only public member variables and [properties](concept-properties.md)\n  defined by getters/setters can be configured.\n* The `on eventName` elements specify what handlers should be attached to the object's [events](concept-events.md).\n  Notice that the array keys are formed by prefixing event names with `on `. Please refer to\n  the [Events](concept-events.md) section for supported event handler formats.\n* The `as behaviorName` elements specify what [behaviors](concept-behaviors.md) should be attached to the object.\n  Notice that the array keys are formed by prefixing behavior names with `as `; the value, `$behaviorConfig`, represents\n  the configuration for creating a behavior, like a normal configuration  described here.\n\nBelow is an example showing a configuration with initial property values, event handlers, and behaviors:\n\n```php\n[\n    'class' => 'app\\components\\SearchEngine',\n    'apiKey' => 'xxxxxxxx',\n    'on search' => function ($event) {\n        Yii::info(\"Keyword searched: \" . $event->keyword);\n    },\n    'as indexer' => [\n        'class' => 'app\\components\\IndexerBehavior',\n        // ... property init values ...\n    ],\n]\n```\n\n\n## Using Configurations <span id=\"using-configurations\"></span>\n\nConfigurations are used in many places in Yii. At the beginning of this section, we have shown how to \ncreate an object according to a configuration by using [[Yii::createObject()]]. In this subsection, we will\ndescribe application configurations and widget configurations - two major usages of configurations.\n\n\n### Application Configurations <span id=\"application-configurations\"></span>\n\nThe configuration for an [application](structure-applications.md) is probably one of the most complex arrays in Yii.\nThis is because the [[yii\\web\\Application|application]] class has a lot of configurable properties and events.\nMore importantly, its [[yii\\web\\Application::components|components]] property can receive an array of configurations\nfor creating components that are registered through the application. The following is an abstract from the application\nconfiguration file for the [Basic Project Template](start-installation.md).\n\n```php\n$config = [\n    'id' => 'basic',\n    'basePath' => dirname(__DIR__),\n    'extensions' => require __DIR__ . '/../vendor/yiisoft/extensions.php',\n    'components' => [\n        'cache' => [\n            'class' => 'yii\\caching\\FileCache',\n        ],\n        'mailer' => [\n            'class' => 'yii\\symfonymailer\\Mailer',\n        ],\n        'log' => [\n            'class' => 'yii\\log\\Dispatcher',\n            'traceLevel' => YII_DEBUG ? 3 : 0,\n            'targets' => [\n                [\n                    'class' => 'yii\\log\\FileTarget',\n                ],\n            ],\n        ],\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=stay2',\n            'username' => 'root',\n            'password' => '',\n            'charset' => 'utf8',\n        ],\n    ],\n];\n```\n\nThe configuration does not have a `class` key. This is because it is used as follows in\nan [entry script](structure-entry-scripts.md), where the class name is already given,\n\n```php\n(new yii\\web\\Application($config))->run();\n```\n\nMore details about configuring the `components` property of an application can be found\nin the [Applications](structure-applications.md) section and the [Service Locator](concept-service-locator.md) section.\n\nSince version 2.0.11, the application configuration supports [Dependency Injection Container](concept-di-container.md)\nconfiguration using `container` property. For example:\n\n```php\n$config = [\n    'id' => 'basic',\n    'basePath' => dirname(__DIR__),\n    'extensions' => require __DIR__ . '/../vendor/yiisoft/extensions.php',\n    'container' => [\n        'definitions' => [\n            'yii\\widgets\\LinkPager' => ['maxButtonCount' => 5]\n        ],\n        'singletons' => [\n            // Dependency Injection Container singletons configuration\n        ]\n    ]\n];\n```\n\nTo know more about the possible values of `definitions` and `singletons` configuration arrays and real-life examples,\nplease read [Advanced Practical Usage](concept-di-container.md#advanced-practical-usage) subsection of the\n[Dependency Injection Container](concept-di-container.md) article.\n\n### Widget Configurations <span id=\"widget-configurations\"></span>\n\nWhen using [widgets](structure-widgets.md), you often need to use configurations to customize the widget properties.\nBoth of the [[yii\\base\\Widget::widget()]] and [[yii\\base\\Widget::begin()]] methods can be used to create\na widget. They take a configuration array, like the following,\n\n```php\nuse yii\\widgets\\Menu;\n\necho Menu::widget([\n    'activateItems' => false,\n    'items' => [\n        ['label' => 'Home', 'url' => ['site/index']],\n        ['label' => 'Products', 'url' => ['product/index']],\n        ['label' => 'Login', 'url' => ['site/login'], 'visible' => Yii::$app->user->isGuest],\n    ],\n]);\n```\n\nThe above code creates a `Menu` widget and initializes its `activateItems` property to be `false`.\nThe `items` property is also configured with menu items to be displayed.\n\nNote that because the class name is already given, the configuration array should NOT have the `class` key.\n\n\n## Configuration Files <span id=\"configuration-files\"></span>\n\nWhen a configuration is very complex, a common practice is to store it in one or multiple PHP files, known as\n*configuration files*. A configuration file returns a PHP array representing the configuration.\nFor example, you may keep an application configuration in a file named `web.php`, like the following,\n\n```php\nreturn [\n    'id' => 'basic',\n    'basePath' => dirname(__DIR__),\n    'extensions' => require __DIR__ . '/../vendor/yiisoft/extensions.php',\n    'components' => require __DIR__ . '/components.php',\n];\n```\n\nBecause the `components` configuration is complex too, you store it in a separate file called `components.php`\nand \"require\" this file in `web.php` as shown above. The content of `components.php` is as follows,\n\n```php\nreturn [\n    'cache' => [\n        'class' => 'yii\\caching\\FileCache',\n    ],\n    'mailer' => [\n        'class' => 'yii\\symfonymailer\\Mailer',\n    ],\n    'log' => [\n        'class' => 'yii\\log\\Dispatcher',\n        'traceLevel' => YII_DEBUG ? 3 : 0,\n        'targets' => [\n            [\n                'class' => 'yii\\log\\FileTarget',\n            ],\n        ],\n    ],\n    'db' => [\n        'class' => 'yii\\db\\Connection',\n        'dsn' => 'mysql:host=localhost;dbname=stay2',\n        'username' => 'root',\n        'password' => '',\n        'charset' => 'utf8',\n    ],\n];\n```\n\nTo get a configuration stored in a configuration file, simply \"require\" it, like the following:\n\n```php\n$config = require 'path/to/web.php';\n(new yii\\web\\Application($config))->run();\n```\n\n\n## Default Configurations <span id=\"default-configurations\"></span>\n\nThe [[Yii::createObject()]] method is implemented based on a [dependency injection container](concept-di-container.md).\nIt allows you to specify a set of the so-called *default configurations* which will be applied to ALL instances of\nthe specified classes when they are being created using [[Yii::createObject()]]. The default configurations\ncan be specified by calling `Yii::$container->set()` in the [bootstrapping](runtime-bootstrapping.md) code.\n\nFor example, if you want to customize [[yii\\widgets\\LinkPager]] so that ALL link pagers will show at most 5 page buttons\n(the default value is 10), you may use the following code to achieve this goal:\n\n```php\n\\Yii::$container->set('yii\\widgets\\LinkPager', [\n    'maxButtonCount' => 5,\n]);\n```\n\nWithout using default configurations, you would have to configure `maxButtonCount` in every place where you use\nlink pagers.\n\n\n## Environment Constants <span id=\"environment-constants\"></span>\n\nConfigurations often vary according to the environment in which an application runs. For example,\nin development environment, you may want to use a database named `mydb_dev`, while on production server\nyou may want to use the `mydb_prod` database. To facilitate switching environments, Yii provides a constant\nnamed `YII_ENV` that you may define in the [entry script](structure-entry-scripts.md) of your application.\nFor example,\n\n```php\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n```\n\nYou may define `YII_ENV` as one of the following values:\n\n- `prod`: production environment. The constant `YII_ENV_PROD` will evaluate as `true`.\n  This is the default value of `YII_ENV` if you do not define it.\n- `dev`: development environment. The constant `YII_ENV_DEV` will evaluate as `true`.\n- `test`: testing environment. The constant `YII_ENV_TEST` will evaluate as `true`.\n\nWith these environment constants, you may specify your configurations conditionally based on\nthe current environment. For example, your application configuration may contain the following\ncode to enable the [debug toolbar and debugger](tool-debugger.md) in development environment.\n\n```php\n$config = [...];\n\nif (YII_ENV_DEV) {\n    // configuration adjustments for 'dev' environment\n    $config['bootstrap'][] = 'debug';\n    $config['modules']['debug'] = 'yii\\debug\\Module';\n}\n\nreturn $config;\n```\n"
  },
  {
    "path": "docs/guide/concept-di-container.md",
    "content": "Dependency Injection Container\n==============================\n\nA dependency injection (DI) container is an object that knows how to instantiate and configure objects and\nall their dependent objects. [Martin Fowler's article](https://martinfowler.com/articles/injection.html) has well\nexplained why DI container is useful. Here we will mainly explain the usage of the DI container provided by Yii.\n\n\nDependency Injection <span id=\"dependency-injection\"></span>\n--------------------\n\nYii provides the DI container feature through the class [[yii\\di\\Container]]. It supports the following kinds of\ndependency injection:\n\n* Constructor injection;\n* Method injection;\n* Setter and property injection;\n* PHP callable injection;\n\n\n### Constructor Injection <span id=\"constructor-injection\"></span>\n\nThe DI container supports constructor injection with the help of type hints for constructor parameters.\nThe type hints tell the container which classes or interfaces are dependent when it is used to create a new object.\nThe container will try to get the instances of the dependent classes or interfaces and then inject them\ninto the new object through the constructor. For example,\n\n```php\nclass Foo\n{\n    public function __construct(Bar $bar)\n    {\n    }\n}\n\n$foo = $container->get('Foo');\n// which is equivalent to the following:\n$bar = new Bar;\n$foo = new Foo($bar);\n```\n\n\n### Method Injection <span id=\"method-injection\"></span>\n\nUsually the dependencies of a class are passed to the constructor and are available inside the class during the whole lifecycle.\nWith Method Injection it is possible to provide a dependency that is only needed by a single method of the class\nand passing it to the constructor may not be possible or may cause too much overhead in the majority of use cases.\n\nA class method can be defined like the `doSomething()` method in the following example:\n\n```php\nclass MyClass extends \\yii\\base\\Component\n{\n    public function __construct(/*Some lightweight dependencies here*/, $config = [])\n    {\n        // ...\n    }\n\n    public function doSomething($param1, \\my\\heavy\\Dependency $something)\n    {\n        // do something with $something\n    }\n}\n```\n\nYou may call that method either by passing an instance of `\\my\\heavy\\Dependency` yourself or using [[yii\\di\\Container::invoke()]] like the following:\n\n```php\n$obj = new MyClass(/*...*/);\nYii::$container->invoke([$obj, 'doSomething'], ['param1' => 42]); // $something will be provided by the DI container\n```\n\n### Setter and Property Injection <span id=\"setter-and-property-injection\"></span>\n\nSetter and property injection is supported through [configurations](concept-configurations.md).\nWhen registering a dependency or when creating a new object, you can provide a configuration which\nwill be used by the container to inject the dependencies through the corresponding setters or properties.\nFor example,\n\n```php\nuse yii\\base\\BaseObject;\n\nclass Foo extends BaseObject\n{\n    public $bar;\n\n    private $_qux;\n\n    public function getQux()\n    {\n        return $this->_qux;\n    }\n\n    public function setQux(Qux $qux)\n    {\n        $this->_qux = $qux;\n    }\n}\n\n$container->get('Foo', [], [\n    'bar' => $container->get('Bar'),\n    'qux' => $container->get('Qux'),\n]);\n```\n\n> Info: The [[yii\\di\\Container::get()]] method takes its third parameter as a configuration array that should\n  be applied to the object being created. If the class implements the [[yii\\base\\Configurable]] interface (e.g.\n  [[yii\\base\\BaseObject]]), the configuration array will be passed as the last parameter to the class constructor;\n  otherwise, the configuration will be applied *after* the object is created.\n\n\n### PHP Callable Injection <span id=\"php-callable-injection\"></span>\n\nIn this case, the container will use a registered PHP callable to build new instances of a class.\nEach time when [[yii\\di\\Container::get()]] is called, the corresponding callable will be invoked.\nThe callable is responsible to resolve the dependencies and inject them appropriately to the newly\ncreated objects. For example,\n\n```php\n$container->set('Foo', function ($container, $params, $config) {\n    $foo = new Foo(new Bar);\n    // ... other initializations ...\n    return $foo;\n});\n\n$foo = $container->get('Foo');\n```\n\nTo hide the complex logic for building a new object, you may use a static class method as callable. For example,\n\n```php\nclass FooBuilder\n{\n    public static function build($container, $params, $config)\n    {\n        $foo = new Foo(new Bar);\n        // ... other initializations ...\n        return $foo;\n    }\n}\n\n$container->set('Foo', ['app\\helper\\FooBuilder', 'build']);\n\n$foo = $container->get('Foo');\n```\n\nBy doing so, the person who wants to configure the `Foo` class no longer needs to be aware of how it is built.\n\n\nRegistering Dependencies <span id=\"registering-dependencies\"></span>\n------------------------\n\nYou can use [[yii\\di\\Container::set()]] to register dependencies. The registration requires a dependency name\nas well as a dependency definition. A dependency name can be a class name, an interface name, or an alias name;\nand a dependency definition can be a class name, a configuration array, or a PHP callable.\n\n```php\n$container = new \\yii\\di\\Container;\n\n// register a class name as is. This can be skipped.\n$container->set('yii\\db\\Connection');\n\n// register an interface\n// When a class depends on the interface, the corresponding class\n// will be instantiated as the dependent object\n$container->set('yii\\mail\\MailInterface', 'yii\\symfonymailer\\Mailer');\n\n// register an alias name. You can use $container->get('foo')\n// to create an instance of Connection\n$container->set('foo', 'yii\\db\\Connection');\n\n// register an alias with `Instance::of`\n$container->set('bar', Instance::of('foo'));\n\n// register a class with configuration. The configuration\n// will be applied when the class is instantiated by get()\n$container->set('yii\\db\\Connection', [\n    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n]);\n\n// register an alias name with class configuration\n// In this case, a \"class\" or \"__class\" element is required to specify the class\n$container->set('db', [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n]);\n\n// register callable closure or array\n// The callable will be executed each time when $container->get('db') is called\n$container->set('db', function ($container, $params, $config) {\n    return new \\yii\\db\\Connection($config);\n});\n$container->set('db', ['app\\db\\DbFactory', 'create']);\n\n// register a component instance\n// $container->get('pageCache') will return the same instance each time it is called\n$container->set('pageCache', new FileCache);\n```\n\n> Tip: If a dependency name is the same as the corresponding dependency definition, you do not\n  need to register it with the DI container.\n\nA dependency registered via `set()` will generate an instance each time the dependency is needed.\nYou can use [[yii\\di\\Container::setSingleton()]] to register a dependency that only generates\na single instance:\n\n```php\n$container->setSingleton('yii\\db\\Connection', [\n    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n]);\n```\n\nResolving Dependencies <span id=\"resolving-dependencies\"></span>\n----------------------\n\nOnce you have registered dependencies, you can use the DI container to create new objects,\nand the container will automatically resolve dependencies by instantiating them and injecting\nthem into the newly created objects. The dependency resolution is recursive, meaning that\nif a dependency has other dependencies, those dependencies will also be resolved automatically.\n\nYou can use [[yii\\di\\Container::get()|get()]] to either create or get object instance.\nThe method takes a dependency name, which can be a class name, an interface name or an alias name. \nThe dependency name may be registered via [[yii\\di\\Container::set()|set()]] \nor [[yii\\di\\Container::setSingleton()|setSingleton()]]. You may optionally provide a list of class \nconstructor parameters and a [configuration](concept-configurations.md) to configure the newly created object. \n\nFor example:\n\n```php\n// \"db\" is a previously registered alias name\n$db = $container->get('db');\n\n// equivalent to: $engine = new \\app\\components\\SearchEngine($apiKey, $apiSecret, ['type' => 1]);\n$engine = $container->get('app\\components\\SearchEngine', [$apiKey, $apiSecret], ['type' => 1]);\n\n// equivalent to: $api = new \\app\\components\\Api($host, $apiKey);\n$api = $container->get('app\\components\\Api', ['host' => $host, 'apiKey' => $apiKey]);\n```\n\nBehind the scene, the DI container does much more work than just creating a new object.\nThe container will first inspect the class constructor to find out dependent class or interface names\nand then automatically resolve those dependencies recursively.\n\nThe following code shows a more sophisticated example. The `UserLister` class depends on an object implementing\nthe `UserFinderInterface` interface; the `UserFinder` class implements this interface and depends on\na `Connection` object. All these dependencies are declared through type hinting of the class constructor parameters.\nWith proper dependency registration, the DI container is able to resolve these dependencies automatically\nand creates a new `UserLister` instance with a simple call of `get('userLister')`.\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\BaseObject;\nuse yii\\db\\Connection;\nuse yii\\di\\Container;\n\ninterface UserFinderInterface\n{\n    function findUser();\n}\n\nclass UserFinder extends BaseObject implements UserFinderInterface\n{\n    public $db;\n\n    public function __construct(Connection $db, $config = [])\n    {\n        $this->db = $db;\n        parent::__construct($config);\n    }\n\n    public function findUser()\n    {\n    }\n}\n\nclass UserLister extends BaseObject\n{\n    public $finder;\n\n    public function __construct(UserFinderInterface $finder, $config = [])\n    {\n        $this->finder = $finder;\n        parent::__construct($config);\n    }\n}\n\n$container = new Container;\n$container->set('yii\\db\\Connection', [\n    'dsn' => '...',\n]);\n$container->set('app\\models\\UserFinderInterface', [\n    'class' => 'app\\models\\UserFinder',\n]);\n$container->set('userLister', 'app\\models\\UserLister');\n\n$lister = $container->get('userLister');\n\n// which is equivalent to:\n\n$db = new \\yii\\db\\Connection(['dsn' => '...']);\n$finder = new UserFinder($db);\n$lister = new UserLister($finder);\n```\n\n\nPractical Usage <span id=\"practical-usage\"></span>\n---------------\n\nYii creates a DI container when you include the `Yii.php` file in the [entry script](structure-entry-scripts.md)\nof your application. The DI container is accessible via [[Yii::$container]]. When you call [[Yii::createObject()]],\nthe method will actually call the container's [[yii\\di\\Container::get()|get()]] method to create a new object.\nAs aforementioned, the DI container will automatically resolve the dependencies (if any) and inject them\ninto obtained object. Because Yii uses [[Yii::createObject()]] in most of its core code to create\nnew objects, this means you can customize the objects globally by dealing with [[Yii::$container]].\n\nFor example, let's customize globally the default number of pagination buttons of [[yii\\widgets\\LinkPager]]. \n\n```php\n\\Yii::$container->set('yii\\widgets\\LinkPager', ['maxButtonCount' => 5]);\n```\n\nNow if you use the widget in a view with the following code, the `maxButtonCount` property will be initialized\nas 5 instead of the default value 10 as defined in the class.\n\n```php\necho \\yii\\widgets\\LinkPager::widget();\n```\n\nYou can still override the value set via DI container, though:\n\n```php\necho \\yii\\widgets\\LinkPager::widget(['maxButtonCount' => 20]);\n```\n\n> Note: Properties given in the widget call will always override the definition in the DI container.\n> Even if you specify an array, e.g. `'options' => ['id' => 'mypager']` these will not be merged\n> with other options but replace them.\n\nAnother example is to take advantage of the automatic constructor injection of the DI container.\nAssume your controller class depends on some other objects, such as a hotel booking service. You\ncan declare the dependency through a constructor parameter and let the DI container to resolve it for you.\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\nuse app\\components\\BookingInterface;\n\nclass HotelController extends Controller\n{\n    protected $bookingService;\n\n    public function __construct($id, $module, BookingInterface $bookingService, $config = [])\n    {\n        $this->bookingService = $bookingService;\n        parent::__construct($id, $module, $config);\n    }\n}\n```\n\nIf you access this controller from browser, you will see an error complaining the `BookingInterface`\ncannot be instantiated. This is because you need to tell the DI container how to deal with this dependency:\n\n```php\n\\Yii::$container->set('app\\components\\BookingInterface', 'app\\components\\BookingService');\n```\n\nNow if you access the controller again, an instance of `app\\components\\BookingService` will be\ncreated and injected as the 3rd parameter to the controller's constructor.\n\nSince Yii 2.0.36 when using PHP 7 action injection is available for both web and console controllers:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\nuse app\\components\\BookingInterface;\n\nclass HotelController extends Controller\n{    \n    public function actionBook($id, BookingInterface $bookingService)\n    {\n        $result = $bookingService->book($id);\n        // ...    \n    }\n}\n``` \n\nAdvanced Practical Usage <span id=\"advanced-practical-usage\"></span>\n---------------\n\nSay we work on API application and have:\n\n- `app\\components\\Request` class that extends `yii\\web\\Request` and provides additional functionality\n- `app\\components\\Response` class that extends `yii\\web\\Response` and should have `format` property \n  set to `json` on creation\n- `app\\storage\\FileStorage` and `app\\storage\\DocumentsReader` classes that implement some logic on\n  working with documents that are located in some file storage:\n  \n  ```php\n  class FileStorage\n  {\n      public function __construct($root) {\n          // whatever\n      }\n  }\n  \n  class DocumentsReader\n  {\n      public function __construct(FileStorage $fs) {\n          // whatever\n      }\n  }\n  ```\n\nIt is possible to configure multiple definitions at once, passing configuration array to\n[[yii\\di\\Container::setDefinitions()|setDefinitions()]] or [[yii\\di\\Container::setSingletons()|setSingletons()]] method.\nIterating over the configuration array, the methods will call [[yii\\di\\Container::set()|set()]]\nor [[yii\\di\\Container::setSingleton()|setSingleton()]] respectively for each item.\n\nThe configuration array format is:\n\n - `key`: class name, interface name or alias name. The key will be passed to the\n [[yii\\di\\Container::set()|set()]] method as a first argument `$class`.\n - `value`: the definition associated with `$class`. Possible values are described in [[yii\\di\\Container::set()|set()]]\n documentation for the `$definition` parameter. Will be passed to the [[set()]] method as\n the second argument `$definition`.\n\nFor example, let's configure our container to follow the aforementioned requirements:\n\n```php\n$container->setDefinitions([\n    'yii\\web\\Request' => 'app\\components\\Request',\n    'yii\\web\\Response' => [\n        'class' => 'app\\components\\Response',\n        'format' => 'json'\n    ],\n    'app\\storage\\DocumentsReader' => function ($container, $params, $config) {\n        $fs = new app\\storage\\FileStorage('/var/tempfiles');\n        return new app\\storage\\DocumentsReader($fs);\n    }\n]);\n\n$reader = $container->get('app\\storage\\DocumentsReader'); \n// Will create DocumentReader object with its dependencies as described in the config \n```\n\n> Tip: Container may be configured in declarative style using application configuration since version 2.0.11. \nCheck out the [Application Configurations](concept-configurations.md#application-configurations) subsection of\nthe [Configurations](concept-configurations.md) guide article.\n\nEverything works, but in case we need to create `DocumentWriter` class, \nwe shall copy-paste the line that creates `FileStorage` object, that is not the smartest way, obviously.\n\nAs described in the [Resolving Dependencies](#resolving-dependencies) subsection, [[yii\\di\\Container::set()|set()]]\nand [[yii\\di\\Container::setSingleton()|setSingleton()]] can optionally take dependency's constructor parameters as\na third argument. To set the constructor parameters, you may use the `__construct()` option:\n\nLet's modify our example:\n\n```php\n$container->setDefinitions([\n    'tempFileStorage' => [ // we've created an alias for convenience\n        'class' => 'app\\storage\\FileStorage',\n        '__construct()' => ['/var/tempfiles'], // could be extracted from some config files\n    ],\n    'app\\storage\\DocumentsReader' => [\n        'class' => 'app\\storage\\DocumentsReader',\n        '__construct()' => [Instance::of('tempFileStorage')],\n    ],\n    'app\\storage\\DocumentsWriter' => [\n        'class' => 'app\\storage\\DocumentsWriter',\n        '__construct()' => [Instance::of('tempFileStorage')]\n    ]\n]);\n\n$reader = $container->get('app\\storage\\DocumentsReader'); \n// Will behave exactly the same as in the previous example.\n```\n\nYou might notice `Instance::of('tempFileStorage')` notation. It means, that the [[yii\\di\\Container|Container]]\nwill implicitly provide a dependency registered with the name of `tempFileStorage` and pass it as the first argument \nof `app\\storage\\DocumentsWriter` constructor.\n\n> Note: [[yii\\di\\Container::setDefinitions()|setDefinitions()]] and [[yii\\di\\Container::setSingletons()|setSingletons()]]\n  methods are available since version 2.0.11.\n  \nAnother step on configuration optimization is to register some dependencies as singletons. \nA dependency registered via [[yii\\di\\Container::set()|set()]] will be instantiated each time it is needed.\nSome classes do not change the state during runtime, therefore they may be registered as singletons\nin order to increase the application performance. \n\nA good example could be `app\\storage\\FileStorage` class, that executes some operations on file system with a simple \nAPI (e.g. `$fs->read()`, `$fs->write()`). These operations do not change the internal class state, so we can\ncreate its instance once and use it multiple times.\n\n```php\n$container->setSingletons([\n    'tempFileStorage' => [\n        'class' => 'app\\storage\\FileStorage',\n        '__construct()' => ['/var/tempfiles']\n    ],\n]);\n\n$container->setDefinitions([\n    'app\\storage\\DocumentsReader' => [\n        'class' => 'app\\storage\\DocumentsReader',\n        '__construct()' => [Instance::of('tempFileStorage')],\n    ],\n    'app\\storage\\DocumentsWriter' => [\n        'class' => 'app\\storage\\DocumentsWriter',\n        '__construct()' => [Instance::of('tempFileStorage')],\n    ]\n]);\n\n$reader = $container->get('app\\storage\\DocumentsReader');\n```\n\nWhen to Register Dependencies <span id=\"when-to-register-dependencies\"></span>\n-----------------------------\n\nBecause dependencies are needed when new objects are being created, their registration should be done\nas early as possible. The following are the recommended practices:\n\n* If you are the developer of an application, you can register your dependencies using application configuration.\n  Please, read the [Application Configurations](concept-configurations.md#application-configurations) subsection of \n  the [Configurations](concept-configurations.md) guide article.\n* If you are the developer of a redistributable [extension](structure-extensions.md), you can register dependencies\n  in the bootstrapping class of the extension.\n\n\nSummary <span id=\"summary\"></span>\n-------\n\nBoth dependency injection and [service locator](concept-service-locator.md) are popular design patterns\nthat allow building software in a loosely-coupled and more testable fashion. We highly recommend you to read\n[Martin's article](https://martinfowler.com/articles/injection.html) to get a deeper understanding of\ndependency injection and service locator.\n\nYii implements its [service locator](concept-service-locator.md) on top of the dependency injection (DI) container.\nWhen a service locator is trying to create a new object instance, it will forward the call to the DI container.\nThe latter will resolve the dependencies automatically as described above.\n\n"
  },
  {
    "path": "docs/guide/concept-events.md",
    "content": "Events\n======\n\nEvents allow you to inject custom code into existing code at certain execution points. You can attach custom\ncode to an event so that when the event is triggered, the code gets executed automatically. For example,\na mailer object may trigger a `messageSent` event when it successfully sends a message. If you want to keep\ntrack of the messages that are successfully sent, you could then simply attach the tracking code to the `messageSent` event.\n\nYii introduces a base class called [[yii\\base\\Component]] to support events. If a class needs to trigger\nevents, it should extend from [[yii\\base\\Component]], or from a child class.\n\n\nEvent Handlers <span id=\"event-handlers\"></span>\n--------------\n\nAn event handler is a [PHP callback](https://www.php.net/manual/en/language.types.callable.php) that gets executed\nwhen the event it is attached to is triggered. You can use any of the following callbacks:\n\n- a global PHP function specified as a string (without parentheses), e.g., `'trim'`;\n- an object method specified as an array of an object and a method name as a string (without parentheses), e.g., `[$object, 'methodName']`;\n- a static class method specified as an array of a class name and a method name as a string (without parentheses), e.g., `['ClassName', 'methodName']`;\n- an anonymous function, e.g., `function ($event) { ... }`.\n\nThe signature of an event handler is:\n\n```php\nfunction ($event) {\n    // $event is an object of yii\\base\\Event or a child class\n}\n```\n\nThrough the `$event` parameter, an event handler may get the following information about the event that occurred:\n\n- [[yii\\base\\Event::name|event name]];\n- [[yii\\base\\Event::sender|event sender]]: the object whose `trigger()` method was called;\n- [[yii\\base\\Event::data|custom data]]: the data that is provided when attaching the event handler (to be explained next).\n\n\nAttaching Event Handlers <span id=\"attaching-event-handlers\"></span>\n------------------------\n\nYou can attach a handler to an event by calling the [[yii\\base\\Component::on()]] method. For example:\n\n```php\n$foo = new Foo();\n\n// this handler is a global function\n$foo->on(Foo::EVENT_HELLO, 'function_name');\n\n// this handler is an object method\n$foo->on(Foo::EVENT_HELLO, [$object, 'methodName']);\n\n// this handler is a static class method\n$foo->on(Foo::EVENT_HELLO, ['app\\components\\Bar', 'methodName']);\n\n// this handler is an anonymous function\n$foo->on(Foo::EVENT_HELLO, function ($event) {\n    // event handling logic\n});\n```\n\nYou may also attach event handlers through [configurations](concept-configurations.md). For more details, please\nrefer to the [Configurations](concept-configurations.md#configuration-format) section.\n\n\nWhen attaching an event handler, you may provide additional data as the third parameter to [[yii\\base\\Component::on()]].\nThe data will be made available to the handler when the event is triggered and the handler is called. For example:\n\n```php\n// The following code will display \"abc\" when the event is triggered\n// because $event->data contains the data passed as the 3rd argument to \"on\"\n$foo->on(Foo::EVENT_HELLO, 'function_name', 'abc');\n\nfunction function_name($event) {\n    echo $event->data;\n}\n```\n\nEvent Handler Order\n-------------------\n\nYou may attach one or more handlers to a single event. When an event is triggered, the attached handlers\nwill be called in the order that they were attached to the event. If a handler needs to stop the invocation of the\nhandlers that follow it, it may set the [[yii\\base\\Event::handled]] property of the `$event` parameter to be `true`:\n\n```php\n$foo->on(Foo::EVENT_HELLO, function ($event) {\n    $event->handled = true;\n});\n```\n\nBy default, a newly attached handler is appended to the existing handler queue for the event.\nAs a result, the handler will be called in the last place when the event is triggered.\nTo insert the new handler at the start of the handler queue so that the handler gets called first, you may call [[yii\\base\\Component::on()]], passing `false` for the fourth parameter `$append`:\n\n```php\n$foo->on(Foo::EVENT_HELLO, function ($event) {\n    // ...\n}, $data, false);\n```\n\nTriggering Events <span id=\"triggering-events\"></span>\n-----------------\n\nEvents are triggered by calling the [[yii\\base\\Component::trigger()]] method. The method requires an *event name*,\nand optionally an event object that describes the parameters to be passed to the event handlers. For example:\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Component;\nuse yii\\base\\Event;\n\nclass Foo extends Component\n{\n    const EVENT_HELLO = 'hello';\n\n    public function bar()\n    {\n        $this->trigger(self::EVENT_HELLO);\n    }\n}\n```\n\nWith the above code, any calls to `bar()` will trigger an event named `hello`.\n\n> Tip: It is recommended to use class constants to represent event names. In the above example, the constant\n  `EVENT_HELLO` represents the `hello` event. This approach has three benefits. First, it prevents typos. Second, it can make events recognizable for IDE\n  auto-completion support. Third, you can tell what events are supported in a class by simply checking its constant declarations.\n\nSometimes when triggering an event you may want to pass along additional information to the event handlers.\nFor example, a mailer may want to pass the message information to the handlers of the `messageSent` event so that the handlers\ncan know the particulars of the sent messages. To do so, you can provide an event object as the second parameter to\nthe [[yii\\base\\Component::trigger()]] method. The event object must be an instance of the [[yii\\base\\Event]] class\nor a child class. For example:\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Component;\nuse yii\\base\\Event;\n\nclass MessageEvent extends Event\n{\n    public $message;\n}\n\nclass Mailer extends Component\n{\n    const EVENT_MESSAGE_SENT = 'messageSent';\n\n    public function send($message)\n    {\n        // ...sending $message...\n\n        $event = new MessageEvent;\n        $event->message = $message;\n        $this->trigger(self::EVENT_MESSAGE_SENT, $event);\n    }\n}\n```\n\nWhen the [[yii\\base\\Component::trigger()]] method is called, it will call all handlers attached to\nthe named event.\n\n\nDetaching Event Handlers <span id=\"detaching-event-handlers\"></span>\n------------------------\n\nTo detach a handler from an event, call the [[yii\\base\\Component::off()]] method. For example:\n\n```php\n// the handler is a global function\n$foo->off(Foo::EVENT_HELLO, 'function_name');\n\n// the handler is an object method\n$foo->off(Foo::EVENT_HELLO, [$object, 'methodName']);\n\n// the handler is a static class method\n$foo->off(Foo::EVENT_HELLO, ['app\\components\\Bar', 'methodName']);\n\n// the handler is an anonymous function\n$foo->off(Foo::EVENT_HELLO, $anonymousFunction);\n```\n\nNote that in general you should not try to detach an anonymous function unless you store it\nsomewhere when it is attached to the event. In the above example, it is assumed that the anonymous\nfunction is stored as a variable `$anonymousFunction`.\n\nTo detach *all* handlers from an event, simply call [[yii\\base\\Component::off()]] without the second parameter:\n\n```php\n$foo->off(Foo::EVENT_HELLO);\n```\n\n\nClass-Level Event Handlers <span id=\"class-level-event-handlers\"></span>\n--------------------------\n\nThe above subsections described how to attach a handler to an event on an *instance level*.\nSometimes, you may want to respond to an event triggered by *every* instance of a class instead of only by\na specific instance. Instead of attaching an event handler to every instance, you may attach the handler\non the *class level* by calling the static method [[yii\\base\\Event::on()]].\n\nFor example, an [Active Record](db-active-record.md) object will trigger an [[yii\\db\\BaseActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]]\nevent whenever it inserts a new record into the database. In order to track insertions done by *every*\n[Active Record](db-active-record.md) object, you may use the following code:\n\n```php\nuse Yii;\nuse yii\\base\\Event;\nuse yii\\db\\ActiveRecord;\n\nEvent::on(ActiveRecord::class, ActiveRecord::EVENT_AFTER_INSERT, function ($event) {\n    Yii::debug(get_class($event->sender) . ' is inserted');\n});\n```\n\nThe event handler will be invoked whenever an instance of [[yii\\db\\ActiveRecord|ActiveRecord]], or one of its child classes, triggers\nthe [[yii\\db\\BaseActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]] event. In the handler, you can get the object\nthat triggered the event through `$event->sender`.\n\nWhen an object triggers an event, it will first call instance-level handlers, followed by the class-level handlers.\n\nYou may trigger a *class-level* event by calling the static method [[yii\\base\\Event::trigger()]]. A class-level\nevent is not associated with a particular object. As a result, it will cause the invocation of class-level event\nhandlers only. For example:\n\n```php\nuse yii\\base\\Event;\n\nEvent::on(Foo::class, Foo::EVENT_HELLO, function ($event) {\n    var_dump($event->sender);  // displays \"null\"\n});\n\nEvent::trigger(Foo::class, Foo::EVENT_HELLO);\n```\n\nNote that, in this case, `$event->sender` is `null` instead of an object instance.\n\n> Note: Because a class-level handler will respond to an event triggered by any instance of that class, or any child\n  classes, you should use it carefully, especially if the class is a low-level base class, such as [[yii\\base\\BaseObject]].\n\nTo detach a class-level event handler, call [[yii\\base\\Event::off()]]. For example:\n\n```php\n// detach $handler\nEvent::off(Foo::class, Foo::EVENT_HELLO, $handler);\n\n// detach all handlers of Foo::EVENT_HELLO\nEvent::off(Foo::class, Foo::EVENT_HELLO);\n```\n\n\nEvents using interfaces <span id=\"interface-level-event-handlers\"></span>\n-------------\n\nThere is even more abstract way to deal with events. You can create a separated interface for the special event and\nimplement it in classes, where you need it.\n\nFor example, we can create the following interface:\n\n```php\nnamespace app\\interfaces;\n\ninterface DanceEventInterface\n{\n    const EVENT_DANCE = 'dance';\n}\n```\n\nAnd two classes, that implement it:\n\n```php\nclass Dog extends Component implements DanceEventInterface\n{\n    public function meetBuddy()\n    {\n        echo \"Woof!\";\n        $this->trigger(DanceEventInterface::EVENT_DANCE);\n    }\n}\n\nclass Developer extends Component implements DanceEventInterface\n{\n    public function testsPassed()\n    {\n        echo \"Yay!\";\n        $this->trigger(DanceEventInterface::EVENT_DANCE);\n    }\n}\n```\n\nTo handle the `EVENT_DANCE`, triggered by any of these classes, call [[yii\\base\\Event::on()|Event::on()]] and\npass the interface class name as the first argument:\n\n```php\nEvent::on('app\\interfaces\\DanceEventInterface', DanceEventInterface::EVENT_DANCE, function ($event) {\n    Yii::debug(get_class($event->sender) . ' just danced'); // Will log that Dog or Developer danced\n});\n```\n\nYou can trigger the event of those classes:\n\n```php\n// trigger event for Dog class\nEvent::trigger(Dog::class, DanceEventInterface::EVENT_DANCE);\n\n// trigger event for Developer class\nEvent::trigger(Developer::class, DanceEventInterface::EVENT_DANCE);\n```\n\nBut please notice, that you can not trigger all the classes, that implement the interface:\n\n```php\n// DOES NOT WORK. Classes that implement this interface will NOT be triggered.\nEvent::trigger('app\\interfaces\\DanceEventInterface', DanceEventInterface::EVENT_DANCE);\n```\n\nTo detach event handler, call [[yii\\base\\Event::off()|Event::off()]]. For example:\n\n```php\n// detaches $handler\nEvent::off('app\\interfaces\\DanceEventInterface', DanceEventInterface::EVENT_DANCE, $handler);\n\n// detaches all handlers of DanceEventInterface::EVENT_DANCE\nEvent::off('app\\interfaces\\DanceEventInterface', DanceEventInterface::EVENT_DANCE);\n```\n\n\nGlobal Events <span id=\"global-events\"></span>\n-------------\n\nYii supports a so-called *global event*, which is actually a trick based on the event mechanism described above.\nThe global event requires a globally accessible Singleton, such as the [application](structure-applications.md) instance itself.\n\nTo create the global event, an event sender calls the Singleton's `trigger()` method\nto trigger the event, instead of calling the sender's own `trigger()` method. Similarly, the event handlers are attached to the event on the Singleton. For example:\n\n```php\nuse Yii;\nuse yii\\base\\Event;\nuse app\\components\\Foo;\n\nYii::$app->on('bar', function ($event) {\n    echo get_class($event->sender);  // displays \"app\\components\\Foo\"\n});\n\nYii::$app->trigger('bar', new Event(['sender' => new Foo]));\n```\n\nA benefit of using global events is that you do not need an object when attaching a handler to the event\nwhich will be triggered by the object. Instead, the handler attachment and the event triggering are both\ndone through the Singleton (e.g. the application instance).\n\nHowever, because the namespace of the global events is shared by all parties, you should name the global events\nwisely, such as introducing some sort of namespace (e.g. \"frontend.mail.sent\", \"backend.mail.sent\").\n\n\nWildcard Events <span id=\"wildcard-events\"></span>\n---------------\n\nSince 2.0.14 you can setup event handler for multiple events matching wildcard pattern.\nFor example:\n\n```php\nuse Yii;\n\n$foo = new Foo();\n\n$foo->on('foo.event.*', function ($event) {\n    // triggered for any event, which name starts on 'foo.event.'\n    Yii::debug('trigger event: ' . $event->name);\n});\n```\n\nWildcard patterns can be used for class-level events as well. For example:\n\n```php\nuse yii\\base\\Event;\nuse Yii;\n\nEvent::on('app\\models\\*', 'before*', function ($event) {\n    // triggered for any class in namespace 'app\\models' for any event, which name starts on 'before'\n    Yii::debug('trigger event: ' . $event->name . ' for class: ' . get_class($event->sender));\n});\n```\n\nThis allows you catching all application events by single handler using following code:\n\n```php\nuse yii\\base\\Event;\nuse Yii;\n\nEvent::on('*', '*', function ($event) {\n    // triggered for any event at any class\n    Yii::debug('trigger event: ' . $event->name);\n});\n```\n\n> Note: usage wildcards for event handlers setup may reduce the application performance.\n  It is better to be avoided if possible.\n\nIn order to detach event handler specified by wildcard pattern, you should repeat same pattern at\n[[yii\\base\\Component::off()]] or [[yii\\base\\Event::off()]] invocation. Keep in mind that passing wildcard\nduring detaching of event handler will detach only the handler specified for this wildcard, while handlers\nattached for regular event names will remain even if they match the pattern. For example:\n\n```php\nuse Yii;\n\n$foo = new Foo();\n\n// attach regular handler\n$foo->on('event.hello', function ($event) {\n    echo 'direct-handler'\n});\n\n// attach wildcard handler\n$foo->on('*', function ($event) {\n    echo 'wildcard-handler'\n});\n\n// detach wildcard handler only!\n$foo->off('*');\n\n$foo->trigger('event.hello'); // outputs: 'direct-handler'\n```\n"
  },
  {
    "path": "docs/guide/concept-properties.md",
    "content": "Properties\n==========\n\nIn PHP, class member variables are also called *properties*. These variables are part of the class definition, and are used\nto represent the state of a class instance (i.e., to differentiate one instance of the class from another).\nIn practice, you may often want to handle the reading or writing of properties in special ways. For example,\nyou may want to always trim a string when it is being assigned\nto a `label` property. You *could* use the following code to achieve this task:\n\n```php\n$object->label = trim($label);\n```\n\nThe drawback of the above code is that you would have to call `trim()` everywhere in your code where you might set the `label`\nproperty. If, in the future, the `label` property gets a new requirement, such as the first letter must be capitalized,\nyou would again have to modify every bit of code that assigns a value to `label`.\nThe repetition of code leads to bugs, and is a practice you want to avoid as much as possible.\n\nTo solve this problem, Yii introduces a base class called [[yii\\base\\BaseObject]] that supports defining properties\nbased on *getter* and *setter* class methods. If a class needs that functionality, it should extend from\n[[yii\\base\\BaseObject]], or from a child class.\n\n> Info: Nearly every core class in the Yii framework extends from [[yii\\base\\BaseObject]] or a child class.\n  This means, that whenever you see a getter or setter in a core class, you can use it like a property.\n\nA getter method is a method whose name starts with the word `get`; a setter method starts with `set`.\nThe name after the `get` or `set` prefix defines the name of a property. For example, a getter `getLabel()` and/or\na setter `setLabel()` defines a property named `label`, as shown in the following code:\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\BaseObject;\n\nclass Foo extends BaseObject\n{\n    private $_label;\n\n    public function getLabel()\n    {\n        return $this->_label;\n    }\n\n    public function setLabel($value)\n    {\n        $this->_label = trim($value);\n    }\n}\n```\n\nTo be clear, the getter and setter methods create the property `label`, which in this case internally refers to a private\nproperty named `_label`.\n\nProperties defined by getters and setters can be used like class member variables. The main difference is that\nwhen such property is being read, the corresponding getter method will be called;  when the property is\nbeing assigned a value, the corresponding setter method will be called. For example:\n\n```php\n// equivalent to $label = $object->getLabel();\n$label = $object->label;\n\n// equivalent to $object->setLabel('abc');\n$object->label = 'abc';\n```\n\nA property defined by a getter without a setter is *read only*. Trying to assign a value to such a property will cause\nan [[yii\\base\\InvalidCallException|InvalidCallException]]. Similarly, a property defined by a setter without a getter\nis *write only*, and trying to read such a property will also cause an exception. It is not common to have write-only\nproperties.\n\nThere are several special rules for, and limitations on, the properties defined via getters and setters:\n\n* The names of such properties are *case-insensitive*. For example, `$object->label` and `$object->Label` are the same.\n  This is because method names in PHP are case-insensitive.\n* If the name of such a property is the same as a class member variable, the latter will take precedence.\n  For example, if the above `Foo` class has a member variable `label`, then the assignment `$object->label = 'abc'`\n  will affect the *member variable* `label`; that line would not call the  `setLabel()` setter method.\n* These properties do not support visibility. It makes no difference to the defining getter or setter method if the property is public, protected or private.\n* The properties can only be defined by *non-static* getters and/or setters. Static methods will not be treated in the same manner.\n* A normal call to `property_exists()` does not work to determine magic properties. You should call [[yii\\base\\BaseObject::canGetProperty()|canGetProperty()]]\n  or [[yii\\base\\BaseObject::canSetProperty()|canSetProperty()]] respectively.\n\nReturning to the problem described at the beginning of this guide, instead of calling `trim()` everywhere a `label` value is assigned,\n`trim()` now only needs to be invoked within the setter `setLabel()`.\nAnd if a new requirement makes it necessary that the label be initially capitalized, the `setLabel()` method can quickly\nbe modified without touching any other code. The one change will universally affect every assignment to `label`.\n"
  },
  {
    "path": "docs/guide/concept-service-locator.md",
    "content": "Service Locator\n===============\n\nA service locator is an object that knows how to provide all sorts of services (or components) that an application\nmight need. Within a service locator, each component exists as only a single instance, uniquely identified by an ID.\nYou use the ID to retrieve a component from the service locator.\n\nIn Yii, a service locator is simply an instance of [[yii\\di\\ServiceLocator]] or a child class.\n\nThe most commonly used service locator in Yii is the *application* object, which can be accessed through\n`\\Yii::$app`. The services it provides are called *application components*, such as the `request`, `response`, and\n`urlManager` components. You may configure these components, or even replace them with your own implementations, easily\nthrough functionality provided by the service locator.\n\nBesides the application object, each module object is also a service locator. Modules implement [tree traversal](#tree-traversal).\n\nTo use a service locator, the first step is to register components with it. A component can be registered\nvia [[yii\\di\\ServiceLocator::set()]]. The following code shows different ways of registering components:\n\n```php\nuse yii\\di\\ServiceLocator;\nuse yii\\caching\\FileCache;\n\n$locator = new ServiceLocator;\n\n// register \"cache\" using a class name that can be used to create a component\n$locator->set('cache', 'yii\\caching\\ApcCache');\n\n// register \"db\" using a configuration array that can be used to create a component\n$locator->set('db', [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=localhost;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n]);\n\n// register \"search\" using an anonymous function that builds a component\n$locator->set('search', function () {\n    return new app\\components\\SolrService;\n});\n\n// register \"pageCache\" using a component\n$locator->set('pageCache', new FileCache);\n```\n\nOnce a component has been registered, you can access it using its ID, in one of the two following ways:\n\n```php\n$cache = $locator->get('cache');\n// or alternatively\n$cache = $locator->cache;\n```\n\nAs shown above, [[yii\\di\\ServiceLocator]] allows you to access a component like a property using the component ID.\nWhen you access a component for the first time, [[yii\\di\\ServiceLocator]] will use the component registration\ninformation to create a new instance of the component and return it. Later, if the component is accessed again,\nthe service locator will return the same instance.\n\nYou may use [[yii\\di\\ServiceLocator::has()]] to check if a component ID has already been registered.\nIf you call [[yii\\di\\ServiceLocator::get()]] with an invalid ID, an exception will be thrown.\n\n\nBecause service locators are often being created with [configurations](concept-configurations.md),\na writable property named [[yii\\di\\ServiceLocator::setComponents()|components]] is provided. This allows you \nto configure and register multiple components at once. The following code shows a configuration array\nthat can be used to configure a service locator (e.g. an [application](structure-applications.md)) with \nthe `db`, `cache`, `tz` and `search` components:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=demo',\n            'username' => 'root',\n            'password' => '',\n        ],\n        'cache' => 'yii\\caching\\ApcCache',\n        'tz' => function() {\n            return new \\DateTimeZone(Yii::$app->formatter->defaultTimeZone);\n        },\n        'search' => function () {\n            $solr = new app\\components\\SolrService('127.0.0.1');\n            // ... other initializations ...\n            return $solr;\n        },\n    ],\n];\n```\n\nIn the above, there is an alternative way to configure the `search` component. Instead of directly writing a PHP\ncallback which builds a `SolrService` instance, you can use a static class method to return such a callback, like\nshown as below:\n\n```php\nclass SolrServiceBuilder\n{\n    public static function build($ip)\n    {\n        return function () use ($ip) {\n            $solr = new app\\components\\SolrService($ip);\n            // ... other initializations ...\n            return $solr;\n        };\n    }\n}\n\nreturn [\n    // ...\n    'components' => [\n        // ...\n        'search' => SolrServiceBuilder::build('127.0.0.1'),\n    ],\n];\n```\n\nThis alternative approach is most preferable when you are releasing a Yii component which encapsulates some non-Yii\n3rd-party library. You use the static method like shown above to represent the complex logic of building the\n3rd-party object, and the user of your component only needs to call the static method to configure the component.\n\n## Tree traversal <span id=\"tree-traversal\"></span>\n\nModules allow arbitrary nesting; a Yii application is essentially a tree of modules.\nSince each of these modules is a service locator it makes sense for children to have access to their parent.\nThis allows modules to use `$this->get('db')` instead of referencing the root service locator `Yii::$app->get('db')`.\nAdded benefit is the option for a developer to override configuration in a module.\n\nAny request for a service to be retrieved from a module will be passed on to its parent in case the module is not able to satisfy it.\n\nNote that configuration from components in a module is never merged with configuration from a component in a parent module. The Service Locator pattern allows us to define named services but one cannot assume services with the same name use the same configuration parameters.\n"
  },
  {
    "path": "docs/guide/db-active-record.md",
    "content": "Active Record\n=============\n\n[Active Record](https://en.wikipedia.org/wiki/Active_record_pattern) provides an object-oriented interface\nfor accessing and manipulating data stored in databases. An Active Record class is associated with a database table,\nan Active Record instance corresponds to a row of that table, and an *attribute* of an Active Record\ninstance represents the value of a particular column in that row. Instead of writing raw SQL statements,\nyou would access Active Record attributes and call Active Record methods to access and manipulate the data stored \nin database tables.\n\nFor example, assume `Customer` is an Active Record class which is associated with the `customer` table\nand `name` is a column of the `customer` table. You can write the following code to insert a new\nrow into the `customer` table:\n\n```php\n$customer = new Customer();\n$customer->name = 'Qiang';\n$customer->save();\n```\n\nThe above code is equivalent to using the following raw SQL statement for MySQL, which is less\nintuitive, more error prone, and may even have compatibility problems if you are using a different kind of database:\n\n```php\n$db->createCommand('INSERT INTO `customer` (`name`) VALUES (:name)', [\n    ':name' => 'Qiang',\n])->execute();\n```\n\nYii provides the Active Record support for the following relational databases:\n\n* MySQL 4.1 or later: via [[yii\\db\\ActiveRecord]]\n* PostgreSQL 7.3 or later: via [[yii\\db\\ActiveRecord]]\n* SQLite 2 and 3: via [[yii\\db\\ActiveRecord]]\n* Microsoft SQL Server 2008 or later: via [[yii\\db\\ActiveRecord]]\n* Oracle: via [[yii\\db\\ActiveRecord]]\n* CUBRID 9.3 or later: via [[yii\\db\\ActiveRecord]] (Note that due to a [bug](http://jira.cubrid.org/browse/APIS-658) in\n  the cubrid PDO extension, quoting of values will not work, so you need CUBRID 9.3 as the client as well as the server)\n* Sphinx: via [[yii\\sphinx\\ActiveRecord]], requires the `yii2-sphinx` extension\n* ElasticSearch: via [[yii\\elasticsearch\\ActiveRecord]], requires the `yii2-elasticsearch` extension\n\nAdditionally, Yii also supports using Active Record with the following NoSQL databases:\n\n* Redis 2.6.12 or later: via [[yii\\redis\\ActiveRecord]], requires the `yii2-redis` extension\n* MongoDB 1.3.0 or later: via [[yii\\mongodb\\ActiveRecord]], requires the `yii2-mongodb` extension\n\nIn this tutorial, we will mainly describe the usage of Active Record for relational databases.\nHowever, most content described here are also applicable to Active Record for NoSQL databases.\n\n\n## Declaring Active Record Classes <span id=\"declaring-ar-classes\"></span>\n\nTo get started, declare an Active Record class by extending [[yii\\db\\ActiveRecord]]. \n\n### Setting a table name\n\nBy default each Active Record class is associated with its database table.\nThe [[yii\\db\\ActiveRecord::tableName()|tableName()]] method returns the table name by converting the class name via [[yii\\helpers\\Inflector::camel2id()]].\nYou may override this method if the table is not named after this convention.\n\nAlso a default [[yii\\db\\Connection::$tablePrefix|tablePrefix]] can be applied. For example if\n [[yii\\db\\Connection::$tablePrefix|tablePrefix]] is `tbl_`, `Customer` becomes `tbl_customer` and `OrderItem` becomes `tbl_order_item`. \n\nIf a table name is given as `{{%TableName}}`, then the percentage character `%` will be replaced with the table prefix. \nFor example, `{{%post}}` becomes `{{tbl_post}}`. The brackets around the table name are used for\n[quoting in an SQL query](db-dao.md#quoting-table-and-column-names).\n\nIn the following example, we declare an Active Record class named `Customer` for the `customer` database table.\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass Customer extends ActiveRecord\n{\n    const STATUS_INACTIVE = 0;\n    const STATUS_ACTIVE = 1;\n    \n    /**\n     * @return string the name of the table associated with this ActiveRecord class.\n     */\n    public static function tableName()\n    {\n        return '{{customer}}';\n    }\n}\n```\n\n### Active records are called \"models\"\n\nActive Record instances are considered as [models](structure-models.md). For this reason, we usually put Active Record\nclasses under the `app\\models` namespace (or other namespaces for keeping model classes). \n\nBecause [[yii\\db\\ActiveRecord]] extends from [[yii\\base\\Model]], it inherits *all* [model](structure-models.md) features,\nsuch as attributes, validation rules, data serialization, etc.\n\n\n## Connecting to Databases <span id=\"db-connection\"></span>\n\nBy default, Active Record uses the `db` [application component](structure-application-components.md) \nas the [[yii\\db\\Connection|DB connection]] to access and manipulate the database data. As explained in \n[Database Access Objects](db-dao.md), you can configure the `db` component in the application configuration like shown\nbelow,\n\n```php\nreturn [\n    'components' => [\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=testdb',\n            'username' => 'demo',\n            'password' => 'demo',\n        ],\n    ],\n];\n```\n\nIf you want to use a different database connection other than the `db` component, you should override \nthe [[yii\\db\\ActiveRecord::getDb()|getDb()]] method:\n\n```php\nclass Customer extends ActiveRecord\n{\n    // ...\n\n    public static function getDb()\n    {\n        // use the \"db2\" application component\n        return \\Yii::$app->db2;  \n    }\n}\n```\n\n\n## Querying Data <span id=\"querying-data\"></span>\n\nAfter declaring an Active Record class, you can use it to query data from the corresponding database table.\nThe process usually takes the following three steps:\n\n1. Create a new query object by calling the [[yii\\db\\ActiveRecord::find()]] method;\n2. Build the query object by calling [query building methods](db-query-builder.md#building-queries);\n3. Call a [query method](db-query-builder.md#query-methods) to retrieve data in terms of Active Record instances.\n\nAs you can see, this is very similar to the procedure with [query builder](db-query-builder.md). The only difference\nis that instead of using the `new` operator to create a query object, you call [[yii\\db\\ActiveRecord::find()]]\nto return a new query object which is of class [[yii\\db\\ActiveQuery]].\n\nBelow are some examples showing how to use Active Query to query data:\n\n```php\n// return a single customer whose ID is 123\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::find()\n    ->where(['id' => 123])\n    ->one();\n\n// return all active customers and order them by their IDs\n// SELECT * FROM `customer` WHERE `status` = 1 ORDER BY `id`\n$customers = Customer::find()\n    ->where(['status' => Customer::STATUS_ACTIVE])\n    ->orderBy('id')\n    ->all();\n\n// return the number of active customers\n// SELECT COUNT(*) FROM `customer` WHERE `status` = 1\n$count = Customer::find()\n    ->where(['status' => Customer::STATUS_ACTIVE])\n    ->count();\n\n// return all customers in an array indexed by customer IDs\n// SELECT * FROM `customer`\n$customers = Customer::find()\n    ->indexBy('id')\n    ->all();\n```\n\nIn the above, `$customer` is a `Customer` object while `$customers` is an array of `Customer` objects. They are\nall populated with the data retrieved from the `customer` table.\n\n> Info: Because [[yii\\db\\ActiveQuery]] extends from [[yii\\db\\Query]], you can use *all* query building methods and\n  query methods as described in the Section [Query Builder](db-query-builder.md).\n\nBecause it is a common task to query by primary key values or a set of column values, Yii provides two shortcut\nmethods for this purpose:\n\n- [[yii\\db\\ActiveRecord::findOne()]]: returns a single Active Record instance populated with the first row of the query result.\n- [[yii\\db\\ActiveRecord::findAll()]]: returns an array of Active Record instances populated with *all* query result.\n\nBoth methods can take one of the following parameter formats:\n\n- a scalar value: the value is treated as the desired primary key value to be looked for. Yii will determine \n  automatically which column is the primary key column by reading database schema information.\n- an array of scalar values: the array is treated as the desired primary key values to be looked for.\n- an associative array: the keys are column names and the values are the corresponding desired column values to \n  be looked for. Please refer to [Hash Format](db-query-builder.md#hash-format) for more details.\n  \nThe following code shows how these methods can be used:\n\n```php\n// returns a single customer whose ID is 123\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// returns customers whose ID is 100, 101, 123 or 124\n// SELECT * FROM `customer` WHERE `id` IN (100, 101, 123, 124)\n$customers = Customer::findAll([100, 101, 123, 124]);\n\n// returns an active customer whose ID is 123\n// SELECT * FROM `customer` WHERE `id` = 123 AND `status` = 1\n$customer = Customer::findOne([\n    'id' => 123,\n    'status' => Customer::STATUS_ACTIVE,\n]);\n\n// returns all inactive customers\n// SELECT * FROM `customer` WHERE `status` = 0\n$customers = Customer::findAll([\n    'status' => Customer::STATUS_INACTIVE,\n]);\n```\n\n> Warning: If you need to pass user input to these methods, make sure the input value is scalar or in case of\n> array condition, make sure the array structure can not be changed from the outside:\n>\n> ```php\n> // yii\\web\\Controller ensures that $id is scalar\n> public function actionView($id)\n> {\n>     $model = Post::findOne($id);\n>     // ...\n> }\n>\n> // explicitly specifying the column to search, passing a scalar or array here will always result in finding a single record\n> $model = Post::findOne(['id' => Yii::$app->request->get('id')]);\n>\n> // do NOT use the following code! it is possible to inject an array condition to filter by arbitrary column values!\n> $model = Post::findOne(Yii::$app->request->get('id'));\n> ```\n\n\n> Note: Neither [[yii\\db\\ActiveRecord::findOne()]] nor [[yii\\db\\ActiveQuery::one()]] will add `LIMIT 1` to \n  the generated SQL statement. If your query may return many rows of data, you should call `limit(1)` explicitly\n  to improve the performance, e.g., `Customer::find()->limit(1)->one()`.\n\nBesides using query building methods, you can also write raw SQLs to query data and populate the results into\nActive Record objects. You can do so by calling the [[yii\\db\\ActiveRecord::findBySql()]] method:\n\n```php\n// returns all inactive customers\n$sql = 'SELECT * FROM customer WHERE status=:status';\n$customers = Customer::findBySql($sql, [':status' => Customer::STATUS_INACTIVE])->all();\n```\n\nDo not call extra query building methods after calling [[yii\\db\\ActiveRecord::findBySql()|findBySql()]] as they\nwill be ignored.\n\n\n## Accessing Data <span id=\"accessing-data\"></span>\n\nAs aforementioned, the data brought back from the database are populated into Active Record instances, and\neach row of the query result corresponds to a single Active Record instance. You can access the column values\nby accessing the attributes of the Active Record instances, for example,\n\n```php\n// \"id\" and \"email\" are the names of columns in the \"customer\" table\n$customer = Customer::findOne(123);\n$id = $customer->id;\n$email = $customer->email;\n```\n\n> Note: The Active Record attributes are named after the associated table columns in a case-sensitive manner.\n  Yii automatically defines an attribute in Active Record for every column of the associated table.\n  You should NOT redeclare any of the attributes. \n\nBecause Active Record attributes are named after table columns, you may find you are writing PHP code like\n`$customer->first_name`, which uses underscores to separate words in attribute names if your table columns are\nnamed in this way. If you are concerned about code style consistency, you should rename your table columns accordingly\n(to use camelCase, for example).\n\n\n### Data Transformation <span id=\"data-transformation\"></span>\n\nIt often happens that the data being entered and/or displayed are in a format which is different from the one used in\nstoring the data in a database. For example, in the database you are storing customers' birthdays as UNIX timestamps\n(which is not a good design, though), while in most cases you would like to manipulate birthdays as strings in\nthe format of `'YYYY/MM/DD'`. To achieve this goal, you can define *data transformation* methods in the `Customer`\nActive Record class like the following:\n\n```php\nclass Customer extends ActiveRecord\n{\n    // ...\n\n    public function getBirthdayText()\n    {\n        return date('Y/m/d', $this->birthday);\n    }\n    \n    public function setBirthdayText($value)\n    {\n        $this->birthday = strtotime($value);\n    }\n}\n```\n\nNow in your PHP code, instead of accessing `$customer->birthday`, you would access `$customer->birthdayText`, which\nwill allow you to input and display customer birthdays in the format of `'YYYY/MM/DD'`.\n\n> Tip: The above example shows a generic way of transforming data in different formats. If you are working with\n> date values, you may use [DateValidator](tutorial-core-validators.md#date) and [[yii\\jui\\DatePicker|DatePicker]],\n> which is easier to use and more powerful.\n\n\n### Retrieving Data in Arrays <span id=\"data-in-arrays\"></span>\n\nWhile retrieving data in terms of Active Record objects is convenient and flexible, it is not always desirable\nwhen you have to bring back a large amount of data due to the big memory footprint. In this case, you can retrieve\ndata using PHP arrays by calling [[yii\\db\\ActiveQuery::asArray()|asArray()]] before executing a query method:\n\n```php\n// return all customers\n// each customer is returned as an associative array\n$customers = Customer::find()\n    ->asArray()\n    ->all();\n```\n\n> Note: While this method saves memory and improves performance, it is closer to the lower DB abstraction layer\n  and you will lose most of the Active Record features. A very important distinction lies in the data type of\n  the column values. When you return data in Active Record instances, column values will be automatically typecast\n  according to the actual column types; on the other hand when you return data in arrays, column values will be\n  strings (since they are the result of PDO without any processing), regardless their actual column types.\n   \n\n### Retrieving Data in Batches <span id=\"data-in-batches\"></span>\n\nIn [Query Builder](db-query-builder.md), we have explained that you may use *batch query* to minimize your memory\nusage when querying a large amount of data from the database. You may use the same technique in Active Record. For example,\n\n```php\n// fetch 10 customers at a time\nforeach (Customer::find()->batch(10) as $customers) {\n    // $customers is an array of 10 or fewer Customer objects\n}\n\n// fetch 10 customers at a time and iterate them one by one\nforeach (Customer::find()->each(10) as $customer) {\n    // $customer is a Customer object\n}\n\n// batch query with eager loading\nforeach (Customer::find()->with('orders')->each() as $customer) {\n    // $customer is a Customer object with the 'orders' relation populated\n}\n```\n\n\n## Saving Data <span id=\"inserting-updating-data\"></span>\n\nUsing Active Record, you can easily save data to the database by taking the following steps:\n\n1. Prepare an Active Record instance\n2. Assign new values to Active Record attributes\n3. Call [[yii\\db\\ActiveRecord::save()]] to save the data into database.\n\nFor example,\n\n```php\n// insert a new row of data\n$customer = new Customer();\n$customer->name = 'James';\n$customer->email = 'james@example.com';\n$customer->save();\n\n// update an existing row of data\n$customer = Customer::findOne(123);\n$customer->email = 'james@newexample.com';\n$customer->save();\n```\n\nThe [[yii\\db\\ActiveRecord::save()|save()]] method can either insert or update a row of data, depending on the state\nof the Active Record instance. If the instance is newly created via the `new` operator, calling \n[[yii\\db\\ActiveRecord::save()|save()]] will cause insertion of a new row; If the instance is the result of a query method,\ncalling [[yii\\db\\ActiveRecord::save()|save()]] will update the row associated with the instance. \n\nYou can differentiate the two states of an Active Record instance by checking its \n[[yii\\db\\ActiveRecord::isNewRecord|isNewRecord]] property value. This property is also used by \n[[yii\\db\\ActiveRecord::save()|save()]] internally as follows:\n\n```php\npublic function save($runValidation = true, $attributeNames = null)\n{\n    if ($this->getIsNewRecord()) {\n        return $this->insert($runValidation, $attributeNames);\n    } else {\n        return $this->update($runValidation, $attributeNames) !== false;\n    }\n}\n```\n\n> Tip: You can call [[yii\\db\\ActiveRecord::insert()|insert()]] or [[yii\\db\\ActiveRecord::update()|update()]]\n  directly to insert or update a row.\n  \n\n### Data Validation <span id=\"data-validation\"></span>\n\nBecause [[yii\\db\\ActiveRecord]] extends from [[yii\\base\\Model]], it shares the same [data validation](input-validation.md) feature.\nYou can declare validation rules by overriding the [[yii\\db\\ActiveRecord::rules()|rules()]] method and perform \ndata validation by calling the [[yii\\db\\ActiveRecord::validate()|validate()]] method.\n\nWhen you call [[yii\\db\\ActiveRecord::save()|save()]], by default it will call [[yii\\db\\ActiveRecord::validate()|validate()]]\nautomatically. Only when the validation passes, will it actually save the data; otherwise it will simply return `false`,\nand you can check the [[yii\\db\\ActiveRecord::errors|errors]] property to retrieve the validation error messages.  \n\n> Tip: If you are certain that your data do not need validation (e.g., the data comes from trustable sources),\n  you can call `save(false)` to skip the validation.\n\n\n### Massive Assignment <span id=\"massive-assignment\"></span>\n\nLike normal [models](structure-models.md), Active Record instances also enjoy the [massive assignment feature](structure-models.md#massive-assignment).\nUsing this feature, you can assign values to multiple attributes of an Active Record instance in a single PHP statement,\nlike shown below. Do remember that only [safe attributes](structure-models.md#safe-attributes) can be massively assigned, though.\n\n```php\n$values = [\n    'name' => 'James',\n    'email' => 'james@example.com',\n];\n\n$customer = new Customer();\n\n$customer->attributes = $values;\n$customer->save();\n```\n\n\n### Updating Counters <span id=\"updating-counters\"></span>\n\nIt is a common task to increment or decrement a column in a database table. We call these columns \"counter columns\".\nYou can use [[yii\\db\\ActiveRecord::updateCounters()|updateCounters()]] to update one or multiple counter columns.\nFor example,\n\n```php\n$post = Post::findOne(100);\n\n// UPDATE `post` SET `view_count` = `view_count` + 1 WHERE `id` = 100\n$post->updateCounters(['view_count' => 1]);\n```\n\n> Note: If you use [[yii\\db\\ActiveRecord::save()]] to update a counter column, you may end up with inaccurate result,\n  because it is likely the same counter is being saved by multiple requests which read and write the same counter value.\n\n\n### Dirty Attributes <span id=\"dirty-attributes\"></span>\n\nWhen you call [[yii\\db\\ActiveRecord::save()|save()]] to save an Active Record instance, only *dirty attributes*\nare being saved. An attribute is considered *dirty* if its value has been modified since it was loaded from DB or\nsaved to DB most recently. Note that data validation will be performed regardless if the Active Record \ninstance has dirty attributes or not.\n\nActive Record automatically maintains the list of dirty attributes. It does so by maintaining an older version of\nthe attribute values and comparing them with the latest one. You can call [[yii\\db\\ActiveRecord::getDirtyAttributes()]] \nto get the attributes that are currently dirty. You can also call [[yii\\db\\ActiveRecord::markAttributeDirty()]] \nto explicitly mark an attribute as dirty.\n\nIf you are interested in the attribute values prior to their most recent modification, you may call \n[[yii\\db\\ActiveRecord::getOldAttributes()|getOldAttributes()]] or [[yii\\db\\ActiveRecord::getOldAttribute()|getOldAttribute()]].\n\n> Note: The comparison of old and new values will be done using the `===` operator so a value will be considered dirty\n> even if it has the same value but a different type. This is often the case when the model receives user input from\n> HTML forms where every value is represented as a string.\n> To ensure the correct type for e.g. integer values you may apply a [validation filter](input-validation.md#data-filtering):\n> `['attributeName', 'filter', 'filter' => 'intval']`. This works with all the typecasting functions of PHP like\n> [intval()](https://www.php.net/manual/en/function.intval.php), [floatval()](https://www.php.net/manual/en/function.floatval.php),\n> [boolval](https://www.php.net/manual/en/function.boolval.php), etc...\n\n### Default Attribute Values <span id=\"default-attribute-values\"></span>\n\nSome of your table columns may have default values defined in the database. Sometimes, you may want to pre-populate your\nWeb form for an Active Record instance with these default values. To avoid writing the same default values again,\nyou can call [[yii\\db\\ActiveRecord::loadDefaultValues()|loadDefaultValues()]] to populate the DB-defined default values\ninto the corresponding Active Record attributes:\n\n```php\n$customer = new Customer();\n$customer->loadDefaultValues();\n// $customer->xyz will be assigned the default value declared when defining the \"xyz\" column\n```\n\n\n### Attributes Typecasting <span id=\"attributes-typecasting\"></span>\n\nBeing populated by query results, [[yii\\db\\ActiveRecord]] performs automatic typecast for its attribute values, using\ninformation from [database table schema](db-dao.md#database-schema). This allows data retrieved from table column\ndeclared as integer to be populated in ActiveRecord instance with PHP integer, boolean with boolean and so on.\nHowever, typecasting mechanism has several limitations:\n\n* Float values are not be converted and will be represented as strings, otherwise they may loose precision.\n* Conversion of the integer values depends on the integer capacity of the operation system you use. In particular:\n  values of column declared as 'unsigned integer' or 'big integer' will be converted to PHP integer only at 64-bit\n  operation system, while on 32-bit ones - they will be represented as strings.\n\nNote that attribute typecast is performed only during populating ActiveRecord instance from query result. There is no\nautomatic conversion for the values loaded from HTTP request or set directly via property access.\nThe table schema will also be used while preparing SQL statements for the ActiveRecord data saving, ensuring\nvalues are bound to the query with correct type. However, ActiveRecord instance attribute values will not be\nconverted during saving process.\n\n> Tip: you may use [[yii\\behaviors\\AttributeTypecastBehavior]] to facilitate attribute values typecasting\n  on ActiveRecord validation or saving.\n  \nSince 2.0.14, Yii ActiveRecord supports complex data types, such as JSON or multidimensional arrays.\n\n#### JSON in MySQL and PostgreSQL\n\nAfter data population, the value from JSON column will be automatically decoded from JSON according to standard JSON\ndecoding rules.\n\nTo save attribute value to a JSON column, ActiveRecord will automatically create a [[yii\\db\\JsonExpression|JsonExpression]] object\nthat will be encoded to a JSON string on [QueryBuilder](db-query-builder.md) level.\n\n#### Arrays in PostgreSQL\n\nAfter data population, the value from Array column will be automatically decoded from PgSQL notation to an [[yii\\db\\ArrayExpression|ArrayExpression]]\nobject. It implements PHP `ArrayAccess` interface, so you can use it as an array, or call `->getValue()` to get the array itself.\n\nTo save attribute value to an array column, ActiveRecord will automatically create an [[yii\\db\\ArrayExpression|ArrayExpression]] object\nthat will be encoded by [QueryBuilder](db-query-builder.md) to an PgSQL string representation of array.\n\nYou can also use conditions for JSON columns:\n\n```php\n$query->andWhere(['=', 'json', new ArrayExpression(['foo' => 'bar'])])\n```\n\nTo learn more about expressions building system read the [Query Builder – Adding custom Conditions and Expressions](db-query-builder.md#adding-custom-conditions-and-expressions)\narticle.\n\n### Updating Multiple Rows <span id=\"updating-multiple-rows\"></span>\n\nThe methods described above all work on individual Active Record instances, causing inserting or updating of individual\ntable rows. To update multiple rows simultaneously, you should call [[yii\\db\\ActiveRecord::updateAll()|updateAll()]], instead,\nwhich is a static method.\n\n```php\n// UPDATE `customer` SET `status` = 1 WHERE `email` LIKE `%@example.com%`\nCustomer::updateAll(['status' => Customer::STATUS_ACTIVE], ['like', 'email', '@example.com']);\n```\n\nSimilarly, you can call [[yii\\db\\ActiveRecord::updateAllCounters()|updateAllCounters()]] to update counter columns of\nmultiple rows at the same time.\n\n```php\n// UPDATE `customer` SET `age` = `age` + 1\nCustomer::updateAllCounters(['age' => 1]);\n```\n\n\n## Deleting Data <span id=\"deleting-data\"></span>\n\nTo delete a single row of data, first retrieve the Active Record instance corresponding to that row and then call\nthe [[yii\\db\\ActiveRecord::delete()]] method.\n\n```php\n$customer = Customer::findOne(123);\n$customer->delete();\n```\n\nYou can call [[yii\\db\\ActiveRecord::deleteAll()]] to delete multiple or all rows of data. For example,\n\n```php\nCustomer::deleteAll(['status' => Customer::STATUS_INACTIVE]);\n```\n\n> Note: Be very careful when calling [[yii\\db\\ActiveRecord::deleteAll()|deleteAll()]] because it may totally\n  erase all data from your table if you make a mistake in specifying the condition.\n\n\n## Active Record Life Cycles <span id=\"ar-life-cycles\"></span>\n\nIt is important to understand the life cycles of Active Record when it is used for different purposes.\nDuring each life cycle, a certain sequence of methods will be invoked, and you can override these methods\nto get a chance to customize the life cycle. You can also respond to certain Active Record events triggered \nduring a life cycle to inject your custom code. These events are especially useful when you are developing \nActive Record [behaviors](concept-behaviors.md) which need to customize Active Record life cycles.\n\nIn the following, we will summarize the various Active Record life cycles and the methods/events that are involved\nin the life cycles.\n\n\n### New Instance Life Cycle <span id=\"new-instance-life-cycle\"></span>\n\nWhen creating a new Active Record instance via the `new` operator, the following life cycle will happen:\n\n1. Class constructor.\n2. [[yii\\db\\ActiveRecord::init()|init()]]: triggers an [[yii\\db\\ActiveRecord::EVENT_INIT|EVENT_INIT]] event.\n\n\n### Querying Data Life Cycle <span id=\"querying-data-life-cycle\"></span>\n\nWhen querying data through one of the [querying methods](#querying-data), each newly populated Active Record will\nundergo the following life cycle:\n\n1. Class constructor.\n2. [[yii\\db\\ActiveRecord::init()|init()]]: triggers an [[yii\\db\\ActiveRecord::EVENT_INIT|EVENT_INIT]] event.\n3. [[yii\\db\\ActiveRecord::afterFind()|afterFind()]]: triggers an [[yii\\db\\ActiveRecord::EVENT_AFTER_FIND|EVENT_AFTER_FIND]] event.\n\n\n### Saving Data Life Cycle <span id=\"saving-data-life-cycle\"></span>\n\nWhen calling [[yii\\db\\ActiveRecord::save()|save()]] to insert or update an Active Record instance, the following\nlife cycle will happen:\n\n1. [[yii\\db\\ActiveRecord::beforeValidate()|beforeValidate()]]: triggers \n   an [[yii\\db\\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]] event. If the method returns `false`\n   or [[yii\\base\\ModelEvent::isValid]] is `false`, the rest of the steps will be skipped.\n2. Performs data validation. If data validation fails, the steps after Step 3 will be skipped. \n3. [[yii\\db\\ActiveRecord::afterValidate()|afterValidate()]]: triggers \n   an [[yii\\db\\ActiveRecord::EVENT_AFTER_VALIDATE|EVENT_AFTER_VALIDATE]] event.\n4. [[yii\\db\\ActiveRecord::beforeSave()|beforeSave()]]: triggers \n   an [[yii\\db\\ActiveRecord::EVENT_BEFORE_INSERT|EVENT_BEFORE_INSERT]] \n   or [[yii\\db\\ActiveRecord::EVENT_BEFORE_UPDATE|EVENT_BEFORE_UPDATE]] event. If the method returns `false`\n   or [[yii\\base\\ModelEvent::isValid]] is `false`, the rest of the steps will be skipped.\n5. Performs the actual data insertion or updating.\n6. [[yii\\db\\ActiveRecord::afterSave()|afterSave()]]: triggers\n   an [[yii\\db\\ActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]] \n   or [[yii\\db\\ActiveRecord::EVENT_AFTER_UPDATE|EVENT_AFTER_UPDATE]] event.\n   \n\n### Deleting Data Life Cycle <span id=\"deleting-data-life-cycle\"></span>\n\nWhen calling [[yii\\db\\ActiveRecord::delete()|delete()]] to delete an Active Record instance, the following\nlife cycle will happen:\n\n1. [[yii\\db\\ActiveRecord::beforeDelete()|beforeDelete()]]: triggers\n   an [[yii\\db\\ActiveRecord::EVENT_BEFORE_DELETE|EVENT_BEFORE_DELETE]] event. If the method returns `false`\n   or [[yii\\base\\ModelEvent::isValid]] is `false`, the rest of the steps will be skipped.\n2. Performs the actual data deletion.\n3. [[yii\\db\\ActiveRecord::afterDelete()|afterDelete()]]: triggers\n   an [[yii\\db\\ActiveRecord::EVENT_AFTER_DELETE|EVENT_AFTER_DELETE]] event.\n\n\n> Note: Calling any of the following methods will NOT initiate any of the above life cycles because they work on the\n> database directly and not on a record basis:\n>\n> - [[yii\\db\\ActiveRecord::updateAll()]] \n> - [[yii\\db\\ActiveRecord::deleteAll()]]\n> - [[yii\\db\\ActiveRecord::updateCounters()]] \n> - [[yii\\db\\ActiveRecord::updateAllCounters()]] \n\n> Note: DI is not supported by default due to performance concerns. You can add support if needed by overriding\n> the [[yii\\db\\ActiveRecord::instantiate()|instantiate()]] method to instantiate the class via [[Yii::createObject()]]:\n> \n> ```php\n> public static function instantiate($row)\n> {\n>     return Yii::createObject(static::class);\n> }\n> ```\n\n\n### Refreshing Data Life Cycle <span id=\"refreshing-data-life-cycle\"></span>\n\nWhen calling [[yii\\db\\ActiveRecord::refresh()|refresh()]] to refresh an Active Record instance, the\n[[yii\\db\\ActiveRecord::EVENT_AFTER_REFRESH|EVENT_AFTER_REFRESH]] event is triggered if refresh is successful and the method returns `true`.\n\n\n## Working with Transactions <span id=\"transactional-operations\"></span>\n\nThere are two ways of using [transactions](db-dao.md#performing-transactions) while working with Active Record. \n\nThe first way is to explicitly enclose Active Record method calls in a transactional block, like shown below,\n\n```php\n$customer = Customer::findOne(123);\n\nCustomer::getDb()->transaction(function($db) use ($customer) {\n    $customer->id = 200;\n    $customer->save();\n    // ...other DB operations...\n});\n\n// or alternatively\n\n$transaction = Customer::getDb()->beginTransaction();\ntry {\n    $customer->id = 200;\n    $customer->save();\n    // ...other DB operations...\n    $transaction->commit();\n} catch(\\Exception $e) {\n    $transaction->rollBack();\n    throw $e;\n} catch(\\Throwable $e) {\n    $transaction->rollBack();\n    throw $e;\n}\n```\n\n> Note: in the above code we have two catch-blocks for compatibility \n> with PHP 5.x and PHP 7.x. `\\Exception` implements the [`\\Throwable` interface](https://www.php.net/manual/en/class.throwable.php)\n> since PHP 7.0, so you can skip the part with `\\Exception` if your app uses only PHP 7.0 and higher.\n\nThe second way is to list the DB operations that require transactional support in the [[yii\\db\\ActiveRecord::transactions()]]\nmethod. For example,\n\n```php\nclass Customer extends ActiveRecord\n{\n    public function transactions()\n    {\n        return [\n            'admin' => self::OP_INSERT,\n            'api' => self::OP_INSERT | self::OP_UPDATE | self::OP_DELETE,\n            // the above is equivalent to the following:\n            // 'api' => self::OP_ALL,\n        ];\n    }\n}\n```\n\nThe [[yii\\db\\ActiveRecord::transactions()]] method should return an array whose keys are [scenario](structure-models.md#scenarios)\nnames and values are the corresponding operations that should be enclosed within transactions. You should use the following\nconstants to refer to different DB operations:\n\n* [[yii\\db\\ActiveRecord::OP_INSERT|OP_INSERT]]: insertion operation performed by [[yii\\db\\ActiveRecord::insert()|insert()]];\n* [[yii\\db\\ActiveRecord::OP_UPDATE|OP_UPDATE]]: update operation performed by [[yii\\db\\ActiveRecord::update()|update()]];\n* [[yii\\db\\ActiveRecord::OP_DELETE|OP_DELETE]]: deletion operation performed by [[yii\\db\\ActiveRecord::delete()|delete()]].\n\nUse the `|` operators to concatenate the above constants to indicate multiple operations. You may also use the shortcut\nconstant [[yii\\db\\ActiveRecord::OP_ALL|OP_ALL]] to refer to all three operations above.\n\nTransactions that are created using this method will be started before calling [[yii\\db\\ActiveRecord::beforeSave()|beforeSave()]]\nand will be committed after [[yii\\db\\ActiveRecord::afterSave()|afterSave()]] has run.\n\n## Optimistic Locks <span id=\"optimistic-locks\"></span>\n\nOptimistic locking is a way to prevent conflicts that may occur when a single row of data is being\nupdated by multiple users. For example, both user A and user B are editing the same wiki article\nat the same time. After user A saves his edits, user B clicks on the \"Save\" button in an attempt to\nsave his edits as well. Because user B was actually working on an outdated version of the article,\nit would be desirable to have a way to prevent him from saving the article and show him some hint message.\n\nOptimistic locking solves the above problem by using a column to record the version number of each row.\nWhen a row is being saved with an outdated version number, a [[yii\\db\\StaleObjectException]] exception\nwill be thrown, which prevents the row from being saved. Optimistic locking is only supported when you\nupdate or delete an existing row of data using [[yii\\db\\ActiveRecord::update()]] or [[yii\\db\\ActiveRecord::delete()]],\nrespectively.\n\nTo use optimistic locking,\n\n1. Create a column in the DB table associated with the Active Record class to store the version number of each row.\n   The column should be of big integer type (in MySQL it would be `BIGINT DEFAULT 0`).\n2. Override the [[yii\\db\\ActiveRecord::optimisticLock()]] method to return the name of this column.\n3. Implement [[\\yii\\behaviors\\OptimisticLockBehavior|OptimisticLockBehavior]] inside your model class to automatically parse its value from received requests.\n   Remove the version attribute from validation rules as [[\\yii\\behaviors\\OptimisticLockBehavior|OptimisticLockBehavior]] should handle it.\n4. In the Web form that takes user inputs, add a hidden field to store the current version number of the row being updated.\n5. In the controller action that updates the row using Active Record, try and catch the [[yii\\db\\StaleObjectException]]\n   exception. Implement necessary business logic (e.g. merging the changes, prompting staled data) to resolve the conflict.\n   \nFor example, assume the version column is named as `version`. You can implement optimistic locking with the code like\nthe following.\n\n```php\n// ------ view code -------\n\nuse yii\\helpers\\Html;\n\n// ...other input fields\necho Html::activeHiddenInput($model, 'version');\n\n\n// ------ controller code -------\n\nuse yii\\db\\StaleObjectException;\n\npublic function actionUpdate($id)\n{\n    $model = $this->findModel($id);\n\n    try {\n        if ($model->load(Yii::$app->request->post()) && $model->save()) {\n            return $this->redirect(['view', 'id' => $model->id]);\n        } else {\n            return $this->render('update', [\n                'model' => $model,\n            ]);\n        }\n    } catch (StaleObjectException $e) {\n        // logic to resolve the conflict\n    }\n}\n\n// ------ model code -------\n\nuse yii\\behaviors\\OptimisticLockBehavior;\n\npublic function behaviors()\n{\n    return [\n        OptimisticLockBehavior::class,\n    ];\n}\n\npublic function optimisticLock()\n{\n    return 'version';\n}\n\n```\n> Note: Because [[\\yii\\behaviors\\OptimisticLockBehavior|OptimisticLockBehavior]] will ensure the record is only saved\n> if user submits a valid version number by directly parsing [[\\yii\\web\\Request::getBodyParam()|getBodyParam()]], it\n> may be useful to extend your model class and do step 2 in parent model while attaching the behavior (step 3) to the child\n> class so you can have an instance dedicated to internal use while tying the other to controllers responsible of receiving \n> end user inputs. Alternatively, you can implement your own logic by configuring its [[\\yii\\behaviors\\OptimisticLockBehavior::$value|value]] property. \n\n\n## Working with Relational Data <span id=\"relational-data\"></span>\n\nBesides working with individual database tables, Active Record is also capable of bringing together related data,\nmaking them readily accessible through the primary data. For example, the customer data is related with the order\ndata because one customer may have placed one or multiple orders. With appropriate declaration of this relation,\nyou'll be able to access a customer's order information using the expression `$customer->orders` which gives\nback the customer's order information in terms of an array of `Order` Active Record instances.\n\n\n### Declaring Relations <span id=\"declaring-relations\"></span>\n\nTo work with relational data using Active Record, you first need to declare relations in Active Record classes.\nThe task is as simple as declaring a *relation method* for every interested relation, like the following,\n\n```php\nclass Customer extends ActiveRecord\n{\n    // ...\n\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n}\n\nclass Order extends ActiveRecord\n{\n    // ...\n\n    public function getCustomer()\n    {\n        return $this->hasOne(Customer::class, ['id' => 'customer_id']);\n    }\n}\n```\n\nIn the above code, we have declared an `orders` relation for the `Customer` class, and a `customer` relation\nfor the `Order` class. \n\nEach relation method must be named as `getXyz`. We call `xyz` (the first letter is in lower case) the *relation name*.\nNote that relation names are *case sensitive*.\n\nWhile declaring a relation, you should specify the following information:\n\n- the multiplicity of the relation: specified by calling either [[yii\\db\\ActiveRecord::hasMany()|hasMany()]]\n  or [[yii\\db\\ActiveRecord::hasOne()|hasOne()]]. In the above example you may easily read in the relation \n  declarations that a customer has many orders while an order only has one customer.\n- the name of the related Active Record class: specified as the first parameter to \n  either [[yii\\db\\ActiveRecord::hasMany()|hasMany()]] or [[yii\\db\\ActiveRecord::hasOne()|hasOne()]].\n  A recommended practice is to call `Xyz::class` to get the class name string so that you can receive\n  IDE auto-completion support as well as error detection at compiling stage. \n- the link between the two types of data: specifies the column(s) through which the two types of data are related.\n  The array values are the columns of the primary data (represented by the Active Record class that you are declaring\n  relations), while the array keys are the columns of the related data.\n\n  An easy rule to remember this is, as you see in the example above, you write the column that belongs to the related\n  Active Record directly next to it. You see there that `customer_id` is a property of `Order` and `id` is a property\n  of `Customer`.\n  \n> Warning: Relation name `relation` is reserved. When used it will produce `ArgumentCountError`.\n\n### Accessing Relational Data <span id=\"accessing-relational-data\"></span>\n\nAfter declaring relations, you can access relational data through relation names. This is just like accessing\nan object [property](concept-properties.md) defined by the relation method. For this reason, we call it *relation property*.\nFor example,\n\n```php\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123\n// $orders is an array of Order objects\n$orders = $customer->orders;\n```\n\n> Info: When you declare a relation named `xyz` via a getter method `getXyz()`, you will be able to access\n  `xyz` like an [object property](concept-properties.md). Note that the name is case sensitive.\n  \nIf a relation is declared with [[yii\\db\\ActiveRecord::hasMany()|hasMany()]], accessing this relation property\nwill return an array of the related Active Record instances; if a relation is declared with \n[[yii\\db\\ActiveRecord::hasOne()|hasOne()]], accessing the relation property will return the related\nActive Record instance or `null` if no related data is found.\n\nWhen you access a relation property for the first time, a SQL statement will be executed, like shown in the\nabove example. If the same property is accessed again, the previous result will be returned without re-executing\nthe SQL statement. To force re-executing the SQL statement, you should unset the relation property\nfirst: `unset($customer->orders)`.\n\n> Note: While this concept looks similar to the [object property](concept-properties.md) feature, there is an\n> important difference. For normal object properties the property value is of the same type as the defining getter method.\n> A relation method however returns an [[yii\\db\\ActiveQuery]] instance, while accessing a relation property will either\n> return a [[yii\\db\\ActiveRecord]] instance or an array of these.\n> \n> ```php\n> $customer->orders; // is an array of `Order` objects\n> $customer->getOrders(); // returns an ActiveQuery instance\n> ```\n> \n> This is useful for creating customized queries, which is described in the next section.\n\n\n### Dynamic Relational Query <span id=\"dynamic-relational-query\"></span>\n\nBecause a relation method returns an instance of [[yii\\db\\ActiveQuery]], you can further build this query\nusing query building methods before performing DB query. For example,\n\n```php\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123 AND `subtotal` > 200 ORDER BY `id`\n$orders = $customer->getOrders()\n    ->where(['>', 'subtotal', 200])\n    ->orderBy('id')\n    ->all();\n```\n\nUnlike accessing a relation property, each time you perform a dynamic relational query via a relation method, \na SQL statement will be executed, even if the same dynamic relational query was performed before.\n\nSometimes you may even want to parametrize a relation declaration so that you can more easily perform\ndynamic relational query. For example, you may declare a `bigOrders` relation as follows, \n\n```php\nclass Customer extends ActiveRecord\n{\n    public function getBigOrders($threshold = 100)\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id'])\n            ->where('subtotal > :threshold', [':threshold' => $threshold])\n            ->orderBy('id');\n    }\n}\n```\n\nThen you will be able to perform the following relational queries:\n\n```php\n// SELECT * FROM `order` WHERE `customer_id` = 123 AND `subtotal` > 200 ORDER BY `id`\n$orders = $customer->getBigOrders(200)->all();\n\n// SELECT * FROM `order` WHERE `customer_id` = 123 AND `subtotal` > 100 ORDER BY `id`\n$orders = $customer->bigOrders;\n```\n\n\n### Relations via a Junction Table <span id=\"junction-table\"></span>\n\nIn database modelling, when the multiplicity between two related tables is many-to-many, \na [junction table](https://en.wikipedia.org/wiki/Junction_table) is usually introduced. For example, the `order`\ntable and the `item` table may be related via a junction table named `order_item`. One order will then correspond\nto multiple order items, while one product item will also correspond to multiple order items.\n\nWhen declaring such relations, you would call either [[yii\\db\\ActiveQuery::via()|via()]] or [[yii\\db\\ActiveQuery::viaTable()|viaTable()]]\nto specify the junction table. The difference between [[yii\\db\\ActiveQuery::via()|via()]] and [[yii\\db\\ActiveQuery::viaTable()|viaTable()]]\nis that the former specifies the junction table in terms of an existing relation name while the latter directly uses\nthe junction table. For example,\n\n```php\nclass Order extends ActiveRecord\n{\n    public function getItems()\n    {\n        return $this->hasMany(Item::class, ['id' => 'item_id'])\n            ->viaTable('order_item', ['order_id' => 'id']);\n    }\n}\n```\n\nor alternatively,\n\n```php\nclass Order extends ActiveRecord\n{\n    public function getOrderItems()\n    {\n        return $this->hasMany(OrderItem::class, ['order_id' => 'id']);\n    }\n\n    public function getItems()\n    {\n        return $this->hasMany(Item::class, ['id' => 'item_id'])\n            ->via('orderItems');\n    }\n}\n```\n\nThe usage of relations declared with a junction table is the same as that of normal relations. For example,\n\n```php\n// SELECT * FROM `order` WHERE `id` = 100\n$order = Order::findOne(100);\n\n// SELECT * FROM `order_item` WHERE `order_id` = 100\n// SELECT * FROM `item` WHERE `item_id` IN (...)\n// returns an array of Item objects\n$items = $order->items;\n```\n\n\n### Chaining relation definitions via multiple tables <span id=\"multi-table-relations\"></span>\n\nIts further possible to define relations via multiple tables by chaining relation definitions using [[yii\\db\\ActiveQuery::via()|via()]].\nConsidering the examples above, we have classes `Customer`, `Order`, and `Item`.\nWe can add a relation to the `Customer` class that lists all items from all the orders they placed,\nand name it `getPurchasedItems()`, the chaining of relations is show in the following code example:\n\n```php\nclass Customer extends ActiveRecord\n{\n    // ...\n\n    public function getPurchasedItems()\n    {\n        // customer's items, matching 'id' column of `Item` to 'item_id' in OrderItem\n        return $this->hasMany(Item::class, ['id' => 'item_id'])\n                    ->via('orderItems');\n    }\n\n    public function getOrderItems()\n    {\n        // customer's order items, matching 'id' column of `Order` to 'order_id' in OrderItem\n        return $this->hasMany(OrderItem::class, ['order_id' => 'id'])\n                    ->via('orders');\n    }\n\n    public function getOrders()\n    {\n        // same as above\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n}\n```\n\n\n### Lazy Loading and Eager Loading <span id=\"lazy-eager-loading\"></span>\n\nIn [Accessing Relational Data](#accessing-relational-data), we explained that you can access a relation property\nof an Active Record instance like accessing a normal object property. A SQL statement will be executed only when\nyou access the relation property the first time. We call such relational data accessing method *lazy loading*.\nFor example,\n\n```php\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123\n$orders = $customer->orders;\n\n// no SQL executed\n$orders2 = $customer->orders;\n```\n\nLazy loading is very convenient to use. However, it may suffer from a performance issue when you need to access\nthe same relation property of multiple Active Record instances. Consider the following code example. How many \nSQL statements will be executed?\n\n```php\n// SELECT * FROM `customer` LIMIT 100\n$customers = Customer::find()->limit(100)->all();\n\nforeach ($customers as $customer) {\n    // SELECT * FROM `order` WHERE `customer_id` = ...\n    $orders = $customer->orders;\n}\n```\n\nAs you can see from the code comment above, there are 101 SQL statements being executed! This is because each\ntime you access the `orders` relation property of a different `Customer` object in the for-loop, a SQL statement \nwill be executed.\n\nTo solve this performance problem, you can use the so-called *eager loading* approach as shown below,\n\n```php\n// SELECT * FROM `customer` LIMIT 100;\n// SELECT * FROM `orders` WHERE `customer_id` IN (...)\n$customers = Customer::find()\n    ->with('orders')\n    ->limit(100)\n    ->all();\n\nforeach ($customers as $customer) {\n    // no SQL executed\n    $orders = $customer->orders;\n}\n```\n\nBy calling [[yii\\db\\ActiveQuery::with()]], you instruct Active Record to bring back the orders for the first 100\ncustomers in one single SQL statement. As a result, you reduce the number of the executed SQL statements from 101 to 2!\n\nYou can eagerly load one or multiple relations. You can even eagerly load *nested relations*. A nested relation is a relation\nthat is declared within a related Active Record class. For example, `Customer` is related with `Order` through the `orders`\nrelation, and `Order` is related with `Item` through the `items` relation. When querying for `Customer`, you can eagerly\nload `items` using the nested relation notation `orders.items`. \n\nThe following code shows different usage of [[yii\\db\\ActiveQuery::with()|with()]]. We assume the `Customer` class\nhas two relations `orders` and `country`, while the `Order` class has one relation `items`.\n\n```php\n// eager loading both \"orders\" and \"country\"\n$customers = Customer::find()->with('orders', 'country')->all();\n// equivalent to the array syntax below\n$customers = Customer::find()->with(['orders', 'country'])->all();\n// no SQL executed \n$orders= $customers[0]->orders;\n// no SQL executed \n$country = $customers[0]->country;\n\n// eager loading \"orders\" and the nested relation \"orders.items\"\n$customers = Customer::find()->with('orders.items')->all();\n// access the items of the first order of the first customer\n// no SQL executed\n$items = $customers[0]->orders[0]->items;\n```\n\nYou can eagerly load deeply nested relations, such as `a.b.c.d`. All parent relations will be eagerly loaded.\nThat is, when you call [[yii\\db\\ActiveQuery::with()|with()]] using `a.b.c.d`, you will eagerly load\n`a`, `a.b`, `a.b.c` and `a.b.c.d`.  \n\n> Info: In general, when eagerly loading `N` relations among which `M` relations are defined with a \n  [junction table](#junction-table), a total number of `N+M+1` SQL statements will be executed.\n  Note that a nested relation `a.b.c.d` counts as 4 relations.\n\nWhen eagerly loading a relation, you can customize the corresponding relational query using an anonymous function.\nFor example,\n\n```php\n// find customers and bring back together their country and active orders\n// SELECT * FROM `customer`\n// SELECT * FROM `country` WHERE `id` IN (...)\n// SELECT * FROM `order` WHERE `customer_id` IN (...) AND `status` = 1\n$customers = Customer::find()->with([\n    'country',\n    'orders' => function ($query) {\n        $query->andWhere(['status' => Order::STATUS_ACTIVE]);\n    },\n])->all();\n```\n\nWhen customizing the relational query for a relation, you should specify the relation name as an array key\nand use an anonymous function as the corresponding array value. The anonymous function will receive a `$query` parameter\nwhich represents the [[yii\\db\\ActiveQuery]] object used to perform the relational query for the relation.\nIn the code example above, we are modifying the relational query by appending an additional condition about order status.\n\n> Note: If you call [[yii\\db\\Query::select()|select()]] while eagerly loading relations, you have to make sure\n> the columns referenced in the relation declarations are being selected. Otherwise, the related models may not \n> be loaded properly. For example,\n>\n> ```php\n> $orders = Order::find()->select(['id', 'amount'])->with('customer')->all();\n> // $orders[0]->customer is always `null`. To fix the problem, you should do the following:\n> $orders = Order::find()->select(['id', 'amount', 'customer_id'])->with('customer')->all();\n> ```\n\n\n### Joining with Relations <span id=\"joining-with-relations\"></span>\n\n> Note: The content described in this subsection is only applicable to relational databases, such as\n  MySQL, PostgreSQL, etc.\n\nThe relational queries that we have described so far only reference the primary table columns when\nquerying for the primary data. In reality we often need to reference columns in the related tables. For example,\nwe may want to bring back the customers who have at least one active order. To solve this problem, we can\nbuild a join query like the following:\n\n```php\n// SELECT `customer`.* FROM `customer`\n// LEFT JOIN `order` ON `order`.`customer_id` = `customer`.`id`\n// WHERE `order`.`status` = 1\n// \n// SELECT * FROM `order` WHERE `customer_id` IN (...)\n$customers = Customer::find()\n    ->select('customer.*')\n    ->leftJoin('order', '`order`.`customer_id` = `customer`.`id`')\n    ->where(['order.status' => Order::STATUS_ACTIVE])\n    ->with('orders')\n    ->all();\n```\n\n> Note: It is important to disambiguate column names when building relational queries involving JOIN SQL statements.\n  A common practice is to prefix column names with their corresponding table names.\n\nHowever, a better approach is to exploit the existing relation declarations by calling [[yii\\db\\ActiveQuery::joinWith()]]:\n\n```php\n$customers = Customer::find()\n    ->joinWith('orders')\n    ->where(['order.status' => Order::STATUS_ACTIVE])\n    ->all();\n```\n\nBoth approaches execute the same set of SQL statements. The latter approach is much cleaner and drier, though. \n\nBy default, [[yii\\db\\ActiveQuery::joinWith()|joinWith()]] will use `LEFT JOIN` to join the primary table with the \nrelated table. You can specify a different join type (e.g. `RIGHT JOIN`) via its third parameter `$joinType`. If\nthe join type you want is `INNER JOIN`, you can simply call [[yii\\db\\ActiveQuery::innerJoinWith()|innerJoinWith()]], instead.\n\nCalling [[yii\\db\\ActiveQuery::joinWith()|joinWith()]] will [eagerly load](#lazy-eager-loading) the related data by default.\nIf you do not want to bring in the related data, you can specify its second parameter `$eagerLoading` as `false`. \n\n> Note: Even when using [[yii\\db\\ActiveQuery::joinWith()|joinWith()]] or [[yii\\db\\ActiveQuery::innerJoinWith()|innerJoinWith()]]\n  with eager loading enabled the related data will **not** be populated from the result of the `JOIN` query. So there's\n  still an extra query for each joined relation as explained in the section on [eager loading](#lazy-eager-loading).\n\nLike [[yii\\db\\ActiveQuery::with()|with()]], you can join with one or multiple relations; you may customize the relation\nqueries on-the-fly; you may join with nested relations; and you may mix the use of [[yii\\db\\ActiveQuery::with()|with()]]\nand [[yii\\db\\ActiveQuery::joinWith()|joinWith()]]. For example,\n\n```php\n$customers = Customer::find()->joinWith([\n    'orders' => function ($query) {\n        $query->andWhere(['>', 'subtotal', 100]);\n    },\n])->with('country')\n    ->all();\n```\n\nSometimes when joining two tables, you may need to specify some extra conditions in the `ON` part of the JOIN query.\nThis can be done by calling the [[yii\\db\\ActiveQuery::onCondition()]] method like the following:\n\n```php\n// SELECT `customer`.* FROM `customer`\n// LEFT JOIN `order` ON `order`.`customer_id` = `customer`.`id` AND `order`.`status` = 1 \n// \n// SELECT * FROM `order` WHERE `customer_id` IN (...)\n$customers = Customer::find()->joinWith([\n    'orders' => function ($query) {\n        $query->onCondition(['order.status' => Order::STATUS_ACTIVE]);\n    },\n])->all();\n```\n\nThis above query brings back *all* customers, and for each customer it brings back all active orders.\nNote that this differs from our earlier example which only brings back customers who have at least one active order.\n\n> Info: When [[yii\\db\\ActiveQuery]] is specified with a condition via [[yii\\db\\ActiveQuery::onCondition()|onCondition()]],\n  the condition will be put in the `ON` part if the query involves a JOIN query. If the query does not involve\n  JOIN, the on-condition will be automatically appended to the `WHERE` part of the query.\n  Thus it may only contain conditions including columns of the related table.\n\n#### Relation table aliases <span id=\"relation-table-aliases\"></span>\n\nAs noted before, when using JOIN in a query, we need to disambiguate column names. Therefore often an alias is\ndefined for a table. Setting an alias for the relational query would be possible by customizing the relation query in the following way:\n\n```php\n$query->joinWith([\n    'orders' => function ($q) {\n        $q->from(['o' => Order::tableName()]);\n    },\n])\n```\n\nThis however looks very complicated and involves either hardcoding the related objects table name or calling `Order::tableName()`.\nSince version 2.0.7, Yii provides a shortcut for this. You may now define and use the alias for the relation table like the following:\n\n```php\n// join the orders relation and sort the result by orders.id\n$query->joinWith(['orders o'])->orderBy('o.id');\n```\n\nThe above syntax works for simple relations. If you need an alias for an intermediate table when joining over\nnested relations, e.g. `$query->joinWith(['orders.product'])`,\nyou need to nest the joinWith calls like in the following example:\n\n```php\n$query->joinWith(['orders o' => function($q) {\n        $q->joinWith('product p');\n    }])\n    ->where('o.amount > 100');\n```\n\n### Inverse Relations <span id=\"inverse-relations\"></span>\n\nRelation declarations are often reciprocal between two Active Record classes. For example, `Customer` is related \nto `Order` via the `orders` relation, and `Order` is related back to `Customer` via the `customer` relation.\n\n```php\nclass Customer extends ActiveRecord\n{\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n}\n\nclass Order extends ActiveRecord\n{\n    public function getCustomer()\n    {\n        return $this->hasOne(Customer::class, ['id' => 'customer_id']);\n    }\n}\n```\n\nNow consider the following piece of code:\n\n```php\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123\n$order = $customer->orders[0];\n\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer2 = $order->customer;\n\n// displays \"not the same\"\necho $customer2 === $customer ? 'same' : 'not the same';\n```\n\nWe would think `$customer` and `$customer2` are the same, but they are not! Actually they do contain the same\ncustomer data, but they are different objects. When accessing `$order->customer`, an extra SQL statement\nis executed to populate a new object `$customer2`.\n\nTo avoid the redundant execution of the last SQL statement in the above example, we should tell Yii that\n`customer` is an *inverse relation* of `orders` by calling the [[yii\\db\\ActiveQuery::inverseOf()|inverseOf()]] method\nlike shown below:\n\n```php\nclass Customer extends ActiveRecord\n{\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id'])->inverseOf('customer');\n    }\n}\n```\n\nWith this modified relation declaration, we will have:\n\n```php\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123\n$order = $customer->orders[0];\n\n// No SQL will be executed\n$customer2 = $order->customer;\n\n// displays \"same\"\necho $customer2 === $customer ? 'same' : 'not the same';\n```\n\n> Note: Inverse relations cannot be defined for relations involving a [junction table](#junction-table).\n  That is, if a relation is defined with [[yii\\db\\ActiveQuery::via()|via()]] or [[yii\\db\\ActiveQuery::viaTable()|viaTable()]],\n  you should not call [[yii\\db\\ActiveQuery::inverseOf()|inverseOf()]] further.\n\n\n## Saving Relations <span id=\"saving-relations\"></span>\n\nWhen working with relational data, you often need to establish relationships between different data or destroy\nexisting relationships. This requires setting proper values for the columns that define the relations. Using Active Record,\nyou may end up writing the code like the following:\n\n```php\n$customer = Customer::findOne(123);\n$order = new Order();\n$order->subtotal = 100;\n// ...\n\n// setting the attribute that defines the \"customer\" relation in Order\n$order->customer_id = $customer->id;\n$order->save();\n```\n\nActive Record provides the [[yii\\db\\ActiveRecord::link()|link()]] method that allows you to accomplish this task more nicely:\n\n```php\n$customer = Customer::findOne(123);\n$order = new Order();\n$order->subtotal = 100;\n// ...\n\n$order->link('customer', $customer);\n```\n\nThe [[yii\\db\\ActiveRecord::link()|link()]] method requires you to specify the relation name and the target Active Record\ninstance that the relationship should be established with. The method will modify the values of the attributes that\nlink two Active Record instances and save them to the database. In the above example, it will set the `customer_id`\nattribute of the `Order` instance to be the value of the `id` attribute of the `Customer` instance and then save it\nto the database.\n\n> Note: You cannot link two newly created Active Record instances.\n\nThe benefit of using [[yii\\db\\ActiveRecord::link()|link()]] is even more obvious when a relation is defined via\na [junction table](#junction-table). For example, you may use the following code to link an `Order` instance\nwith an `Item` instance:\n\n```php\n$order->link('items', $item);\n```\n\nThe above code will automatically insert a row in the `order_item` junction table to relate the order with the item.\n\n> Info: The [[yii\\db\\ActiveRecord::link()|link()]] method will NOT perform any data validation while\n  saving the affected Active Record instance. It is your responsibility to validate any input data before\n  calling this method.\n\nThe opposite operation to [[yii\\db\\ActiveRecord::link()|link()]] is [[yii\\db\\ActiveRecord::unlink()|unlink()]]\nwhich breaks an existing relationship between two Active Record instances. For example,\n\n```php\n$customer = Customer::find()->with('orders')->where(['id' => 123])->one();\n$customer->unlink('orders', $customer->orders[0]);\n```\n\nBy default, the [[yii\\db\\ActiveRecord::unlink()|unlink()]] method will set the foreign key value(s) that specify\nthe existing relationship to be `null`. You may, however, choose to delete the table row that contains the foreign key value\nby passing the `$delete` parameter as `true` to the method.\n \nWhen a junction table is involved in a relation, calling [[yii\\db\\ActiveRecord::unlink()|unlink()]] will cause\nthe foreign keys in the junction table to be cleared, or the deletion of the corresponding row in the junction table\nif `$delete` is `true`.\n\n\n## Cross-Database Relations <span id=\"cross-database-relations\"></span> \n\nActive Record allows you to declare relations between Active Record classes that are powered by different databases.\nThe databases can be of different types (e.g. MySQL and PostgreSQL, or MS SQL and MongoDB), and they can run on \ndifferent servers. You can use the same syntax to perform relational queries. For example,\n\n```php\n// Customer is associated with the \"customer\" table in a relational database (e.g. MySQL)\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public static function tableName()\n    {\n        return 'customer';\n    }\n\n    public function getComments()\n    {\n        // a customer has many comments\n        return $this->hasMany(Comment::class, ['customer_id' => 'id']);\n    }\n}\n\n// Comment is associated with the \"comment\" collection in a MongoDB database\nclass Comment extends \\yii\\mongodb\\ActiveRecord\n{\n    public static function collectionName()\n    {\n        return 'comment';\n    }\n\n    public function getCustomer()\n    {\n        // a comment has one customer\n        return $this->hasOne(Customer::class, ['id' => 'customer_id']);\n    }\n}\n\n$customers = Customer::find()->with('comments')->all();\n```\n\nYou can use most of the relational query features that have been described in this section. \n \n> Note: Usage of [[yii\\db\\ActiveQuery::joinWith()|joinWith()]] is limited to databases that allow cross-database JOIN queries.\n  For this reason, you cannot use this method in the above example because MongoDB does not support JOIN.\n\n\n## Customizing Query Classes <span id=\"customizing-query-classes\"></span>\n\nBy default, all Active Record queries are supported by [[yii\\db\\ActiveQuery]]. To use a customized query class\nin an Active Record class, you should override the [[yii\\db\\ActiveRecord::find()]] method and return an instance\nof your customized query class. For example,\n\n```php\n// file Comment.php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass Comment extends ActiveRecord\n{\n    public static function find()\n    {\n        return new CommentQuery(get_called_class());\n    }\n}\n```\n\nNow whenever you are performing a query (e.g. `find()`, `findOne()`) or defining a relation (e.g. `hasOne()`)\nwith `Comment`, you will be calling an instance of `CommentQuery` instead of `ActiveQuery`.\n\nYou now have to define the `CommentQuery` class, which can be customized in many creative ways to improve your query building experience. For example,\n\n```php\n// file CommentQuery.php\nnamespace app\\models;\n\nuse yii\\db\\ActiveQuery;\n\nclass CommentQuery extends ActiveQuery\n{\n    // conditions appended by default (can be skipped)\n    public function init()\n    {\n        $this->andOnCondition(['deleted' => false]);\n        parent::init();\n    }\n\n    // ... add customized query methods here ...\n\n    public function active($state = true)\n    {\n        return $this->andOnCondition(['active' => $state]);\n    }\n}\n```\n\n> Note: Instead of calling [[yii\\db\\ActiveQuery::onCondition()|onCondition()]], you usually should call\n  [[yii\\db\\ActiveQuery::andOnCondition()|andOnCondition()]] or [[yii\\db\\ActiveQuery::orOnCondition()|orOnCondition()]]\n  to append additional conditions when defining new query building methods so that any existing conditions are not overwritten.\n\nThis allows you to write query building code like the following:\n\n```php\n$comments = Comment::find()->active()->all();\n$inactiveComments = Comment::find()->active(false)->all();\n```\n\n> Tip: In big projects, it is recommended that you use customized query classes to hold most query-related code\n  so that the Active Record classes can be kept clean.\n\nYou can also use the new query building methods when defining relations about `Comment` or performing relational query:\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public function getActiveComments()\n    {\n        return $this->hasMany(Comment::class, ['customer_id' => 'id'])->active();\n    }\n}\n\n$customers = Customer::find()->joinWith('activeComments')->all();\n\n// or alternatively\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public function getComments()\n    {\n        return $this->hasMany(Comment::class, ['customer_id' => 'id']);\n    }\n}\n\n$customers = Customer::find()->joinWith([\n    'comments' => function($q) {\n        $q->active();\n    }\n])->all();\n```\n\n> Info: In Yii 1.1, there is a concept called *scope*. Scope is no longer directly supported in Yii 2.0,\n  and you should use customized query classes and query methods to achieve the same goal.\n\n\n## Selecting extra fields\n\nWhen Active Record instance is populated from query results, its attributes are filled up by corresponding column\nvalues from received data set.\n\nYou are able to fetch additional columns or values from query and store it inside the Active Record.\nFor example, assume we have a table named `room`, which contains information about rooms available in the hotel.\nEach room stores information about its geometrical size using fields `length`, `width`, `height`.\nImagine we need to retrieve list of all available rooms with their volume in descending order.\nSo you can not calculate volume using PHP, because we need to sort the records by its value, but you also want `volume`\nto be displayed in the list.\nTo achieve the goal, you need to declare an extra field in your `Room` Active Record class, which will store `volume` value:\n\n```php\nclass Room extends \\yii\\db\\ActiveRecord\n{\n    public $volume;\n\n    // ...\n}\n```\n\nThen you need to compose a query, which calculates volume of the room and performs the sort:\n\n```php\n$rooms = Room::find()\n    ->select([\n        '{{room}}.*', // select all columns\n        '([[length]] * [[width]] * [[height]]) AS volume', // calculate a volume\n    ])\n    ->orderBy('volume DESC') // apply sort\n    ->all();\n\nforeach ($rooms as $room) {\n    echo $room->volume; // contains value calculated by SQL\n}\n```\n\nAbility to select extra fields can be exceptionally useful for aggregation queries.\nAssume you need to display a list of customers with the count of orders they have made.\nFirst of all, you need to declare a `Customer` class with `orders` relation and extra field for count storage:\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public $ordersCount;\n\n    // ...\n\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n}\n```\n\nThen you can compose a query, which joins the orders and calculates their count:\n\n```php\n$customers = Customer::find()\n    ->select([\n        '{{customer}}.*', // select all customer fields\n        'COUNT({{order}}.id) AS ordersCount' // calculate orders count\n    ])\n    ->joinWith('orders') // ensure table junction\n    ->groupBy('{{customer}}.id') // group the result to ensure aggregation function works\n    ->all();\n```\n\nA disadvantage of using this method would be that, if the information isn't loaded on the SQL query - it has to be calculated\nseparately. Thus, if you have found particular record via regular query without extra select statements, it\nwill be unable to return actual value for the extra field. Same will happen for the newly saved record.\n\n```php\n$room = new Room();\n$room->length = 100;\n$room->width = 50;\n$room->height = 2;\n\n$room->volume; // this value will be `null`, since it was not declared yet\n```\n\nUsing the [[yii\\db\\BaseActiveRecord::__get()|__get()]] and [[yii\\db\\BaseActiveRecord::__set()|__set()]] magic methods\nwe can emulate the behavior of a property:\n\n```php\nclass Room extends \\yii\\db\\ActiveRecord\n{\n    private $_volume;\n    \n    public function setVolume($volume)\n    {\n        $this->_volume = (float) $volume;\n    }\n    \n    public function getVolume()\n    {\n        if (empty($this->length) || empty($this->width) || empty($this->height)) {\n            return null;\n        }\n        \n        if ($this->_volume === null) {\n            $this->setVolume(\n                $this->length * $this->width * $this->height\n            );\n        }\n        \n        return $this->_volume;\n    }\n\n    // ...\n}\n```\n\nWhen the select query doesn't provide the volume, the model will be able to calculate it automatically using\nthe attributes of the model.\n\nYou can calculate the aggregation fields as well using defined relations:\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    private $_ordersCount;\n\n    public function setOrdersCount($count)\n    {\n        $this->_ordersCount = (int) $count;\n    }\n\n    public function getOrdersCount()\n    {\n        if ($this->isNewRecord) {\n            return null; // this avoid calling a query searching for null primary keys\n        }\n\n        if ($this->_ordersCount === null) {\n            $this->setOrdersCount($this->getOrders()->count()); // calculate aggregation on demand from relation\n        }\n\n        return $this->_ordersCount;\n    }\n\n    // ...\n\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n}\n```\n\nWith this code, in case 'ordersCount' is present in 'select' statement - `Customer::ordersCount` will be populated\nby query results, otherwise it will be calculated on demand using `Customer::orders` relation.\n\nThis approach can be as well used for creation of the shortcuts for some relational data, especially for the aggregation.\nFor example:\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    /**\n     * Defines read-only virtual property for aggregation data.\n     */\n    public function getOrdersCount()\n    {\n        if ($this->isNewRecord) {\n            return null; // this avoid calling a query searching for null primary keys\n        }\n        \n        return empty($this->ordersAggregation) ? 0 : $this->ordersAggregation[0]['counted'];\n    }\n\n    /**\n     * Declares normal 'orders' relation.\n     */\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n\n    /**\n     * Declares new relation based on 'orders', which provides aggregation.\n     */\n    public function getOrdersAggregation()\n    {\n        return $this->getOrders()\n            ->select(['customer_id', 'counted' => 'count(*)'])\n            ->groupBy('customer_id')\n            ->asArray(true);\n    }\n\n    // ...\n}\n\nforeach (Customer::find()->with('ordersAggregation')->all() as $customer) {\n    echo $customer->ordersCount; // outputs aggregation data from relation without extra query due to eager loading\n}\n\n$customer = Customer::findOne($pk);\n$customer->ordersCount; // output aggregation data from lazy loaded relation\n```\n"
  },
  {
    "path": "docs/guide/db-dao.md",
    "content": "Database Access Objects\n=======================\n\nBuilt on top of [PDO](https://www.php.net/manual/en/book.pdo.php), Yii DAO (Database Access Objects) provides an\nobject-oriented API for accessing relational databases. It is the foundation for other more advanced database\naccess methods, including [query builder](db-query-builder.md) and [active record](db-active-record.md).\n\nWhen using Yii DAO, you mainly need to deal with plain SQLs and PHP arrays. As a result, it is the most efficient \nway to access databases. However, because SQL syntax may vary for different databases, using Yii DAO also means \nyou have to take extra effort to create a database-agnostic application.\n\nIn Yii 2.0, DAO supports the following databases out of the box:\n\n- [MySQL](https://www.mysql.com/)\n- [MariaDB](https://mariadb.com/)\n- [SQLite](https://sqlite.org/)\n- [PostgreSQL](https://www.postgresql.org/): version 8.4 or higher\n- [CUBRID](https://www.cubrid.org/): version 9.3 or higher.\n- [Oracle](https://www.oracle.com/database/)\n- [MSSQL](https://www.microsoft.com/en-us/sqlserver/default.aspx): version 2008 or higher.\n\n> Note: New version of pdo_oci for PHP 7 currently exists only as the source code. Follow\n  [instruction provided by community](https://github.com/yiisoft/yii2/issues/10975#issuecomment-248479268)\n  to compile it or use [PDO emulation layer](https://github.com/taq/pdooci).\n\n## Creating DB Connections <span id=\"creating-db-connections\"></span>\n\nTo access a database, you first need to connect to it by creating an instance of [[yii\\db\\Connection]]:\n\n```php\n$db = new yii\\db\\Connection([\n    'dsn' => 'mysql:host=localhost;dbname=example',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n]);\n```\n\nBecause a DB connection often needs to be accessed in different places, a common practice is to configure it\nin terms of an [application component](structure-application-components.md) like the following:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        // ...\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=example',\n            'username' => 'root',\n            'password' => '',\n            'charset' => 'utf8',\n        ],\n    ],\n    // ...\n];\n```\n\nYou can then access the DB connection via the expression `Yii::$app->db`.\n\n> Tip: You can configure multiple DB application components if your application needs to access multiple databases.\n\nWhen configuring a DB connection, you should always specify its Data Source Name (DSN) via the [[yii\\db\\Connection::dsn|dsn]] \nproperty. The format of DSN varies for different databases. Please refer to the [PHP manual](https://www.php.net/manual/en/pdo.construct.php) \nfor more details. Below are some examples:\n \n* MySQL, MariaDB: `mysql:host=localhost;dbname=mydatabase`\n* SQLite: `sqlite:/path/to/database/file`\n* PostgreSQL: `pgsql:host=localhost;port=5432;dbname=mydatabase`\n* CUBRID: `cubrid:dbname=demodb;host=localhost;port=33000`\n* MS SQL Server (via sqlsrv driver): `sqlsrv:Server=localhost;Database=mydatabase`\n* MS SQL Server (via dblib driver): `dblib:host=localhost;dbname=mydatabase`\n* MS SQL Server (via mssql driver): `mssql:host=localhost;dbname=mydatabase`\n* Oracle: `oci:dbname=//localhost:1521/mydatabase`\n\nNote that if you are connecting with a database via ODBC, you should configure the [[yii\\db\\Connection::driverName]]\nproperty so that Yii can know the actual database type. For example,\n\n```php\n'db' => [\n    'class' => 'yii\\db\\Connection',\n    'driverName' => 'mysql',\n    'dsn' => 'odbc:Driver={MySQL};Server=localhost;Database=test',\n    'username' => 'root',\n    'password' => '',\n],\n```\n\nBesides the [[yii\\db\\Connection::dsn|dsn]] property, you often need to configure [[yii\\db\\Connection::username|username]]\nand [[yii\\db\\Connection::password|password]]. Please refer to [[yii\\db\\Connection]] for the full list of configurable properties. \n\n> Info: When you create a DB connection instance, the actual connection to the database is not established until\n  you execute the first SQL or you call the [[yii\\db\\Connection::open()|open()]] method explicitly.\n\n> Tip: Sometimes you may want to execute some queries right after the database connection is established to initialize\n> some environment variables (e.g., to set the timezone or character set). You can do so by registering an event handler\n> for the [[yii\\db\\Connection::EVENT_AFTER_OPEN|afterOpen]] event\n> of the database connection. You may register the handler directly in the application configuration like so:\n> \n> ```php\n> 'db' => [\n>     // ...\n>     'on afterOpen' => function($event) {\n>         // $event->sender refers to the DB connection\n>         $event->sender->createCommand(\"SET time_zone = 'UTC'\")->execute();\n>     }\n> ],\n> ```\n\nFor MS SQL Server additional connection option is needed for proper binary data handling:\n\n```php\n'db' => [\n 'class' => 'yii\\db\\Connection',\n    'dsn' => 'sqlsrv:Server=localhost;Database=mydatabase',\n    'attributes' => [\n        \\PDO::SQLSRV_ATTR_ENCODING => \\PDO::SQLSRV_ENCODING_SYSTEM\n    ]\n],\n```\n\n\n## Executing SQL Queries <span id=\"executing-sql-queries\"></span>\n\nOnce you have a database connection instance, you can execute a SQL query by taking the following steps:\n \n1. Create a [[yii\\db\\Command]] with a plain SQL query;\n2. Bind parameters (optional);\n3. Call one of the SQL execution methods in [[yii\\db\\Command]].\n\nThe following example shows various ways of fetching data from a database:\n \n```php\n// return a set of rows. each row is an associative array of column names and values.\n// an empty array is returned if the query returned no results\n$posts = Yii::$app->db->createCommand('SELECT * FROM post')\n            ->queryAll();\n\n// return a single row (the first row)\n// false is returned if the query has no result\n$post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=1')\n           ->queryOne();\n\n// return a single column (the first column)\n// an empty array is returned if the query returned no results\n$titles = Yii::$app->db->createCommand('SELECT title FROM post')\n             ->queryColumn();\n\n// return a scalar value\n// false is returned if the query has no result\n$count = Yii::$app->db->createCommand('SELECT COUNT(*) FROM post')\n             ->queryScalar();\n```\n\n> Note: To preserve precision, the data fetched from databases are all represented as strings, even if the corresponding\n  database column types are numerical.\n\n\n### Binding Parameters <span id=\"binding-parameters\"></span>\n\nWhen creating a DB command from a SQL with parameters, you should almost always use the approach of binding parameters\nto prevent SQL injection attacks. For example,\n\n```php\n$post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status')\n           ->bindValue(':id', $_GET['id'])\n           ->bindValue(':status', 1)\n           ->queryOne();\n```\n\nIn the SQL statement, you can embed one or multiple parameter placeholders (e.g. `:id` in the above example). A parameter\nplaceholder should be a string starting with a colon. You may then call one of the following parameter binding methods\nto bind the parameter values:\n\n* [[yii\\db\\Command::bindValue()|bindValue()]]: bind a single parameter value \n* [[yii\\db\\Command::bindValues()|bindValues()]]: bind multiple parameter values in one call\n* [[yii\\db\\Command::bindParam()|bindParam()]]: similar to [[yii\\db\\Command::bindValue()|bindValue()]] but also\n  support binding parameter references.\n\nThe following example shows alternative ways of binding parameters:\n\n```php\n$params = [':id' => $_GET['id'], ':status' => 1];\n\n$post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status')\n           ->bindValues($params)\n           ->queryOne();\n           \n$post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status', $params)\n           ->queryOne();\n```\n\nParameter binding is implemented via [prepared statements](https://www.php.net/manual/en/mysqli.quickstart.prepared-statements.php).\nBesides preventing SQL injection attacks, it may also improve performance by preparing a SQL statement once and\nexecuting it multiple times with different parameters. For example,\n\n```php\n$command = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id');\n\n$post1 = $command->bindValue(':id', 1)->queryOne();\n$post2 = $command->bindValue(':id', 2)->queryOne();\n// ...\n```\n\nBecause [[yii\\db\\Command::bindParam()|bindParam()]] supports binding parameters by references, the above code\ncan also be written like the following:\n\n```php\n$command = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id')\n              ->bindParam(':id', $id);\n\n$id = 1;\n$post1 = $command->queryOne();\n\n$id = 2;\n$post2 = $command->queryOne();\n// ...\n```\n\nNotice that you bind the placeholder to the `$id` variable before the execution, and then change the value of that variable \nbefore each subsequent execution (this is often done with loops). Executing queries in this manner can be vastly \nmore efficient than running a new query for every different parameter value. \n\n> Info: Parameter binding is only used in places where values need to be inserted into strings that contain plain SQL.\n> In many places in higher abstraction layers like [query builder](db-query-builder.md) and [active record](db-active-record.md)\n> you often specify an array of values which will be transformed into SQL. In these places parameter binding is done by Yii\n> internally, so there is no need to specify params manually.\n\n\n### Executing Non-SELECT Queries <span id=\"non-select-queries\"></span>\n\nThe `queryXyz()` methods introduced in the previous sections all deal with SELECT queries which fetch data from databases.\nFor queries that do not bring back data, you should call the [[yii\\db\\Command::execute()]] method instead. For example,\n\n```php\nYii::$app->db->createCommand('UPDATE post SET status=1 WHERE id=1')\n   ->execute();\n```\n\nThe [[yii\\db\\Command::execute()]] method returns the number of rows affected by the SQL execution.\n\nFor INSERT, UPDATE and DELETE queries, instead of writing plain SQLs, you may call [[yii\\db\\Command::insert()|insert()]],\n[[yii\\db\\Command::update()|update()]], [[yii\\db\\Command::delete()|delete()]], respectively, to build the corresponding\nSQLs. These methods will properly quote table and column names and bind parameter values. For example,\n\n```php\n// INSERT (table name, column values)\nYii::$app->db->createCommand()->insert('user', [\n    'name' => 'Sam',\n    'age' => 30,\n])->execute();\n\n// UPDATE (table name, column values, condition)\nYii::$app->db->createCommand()->update('user', ['status' => 1], 'age > 30')->execute();\n\n// DELETE (table name, condition)\nYii::$app->db->createCommand()->delete('user', 'status = 0')->execute();\n```\n\nYou may also call [[yii\\db\\Command::batchInsert()|batchInsert()]] to insert multiple rows in one shot, which is much\nmore efficient than inserting one row at a time:\n\n```php\n// table name, column names, column values\nYii::$app->db->createCommand()->batchInsert('user', ['name', 'age'], [\n    ['Tom', 30],\n    ['Jane', 20],\n    ['Linda', 25],\n])->execute();\n```\n\nAnother useful method is [[yii\\db\\Command::upsert()|upsert()]]. Upsert is an atomic operation that inserts rows into\na database table if they do not already exist (matching unique constraints), or update them if they do:\n\n```php\nYii::$app->db->createCommand()->upsert('pages', [\n    'name' => 'Front page',\n    'url' => 'https://example.com/', // url is unique\n    'visits' => 0,\n], [\n    'visits' => new \\yii\\db\\Expression('visits + 1'),\n], $params)->execute();\n```\n\nThe code above will either insert a new page record or increment its visit counter atomically.\n\nNote that the aforementioned methods only create the query and you always have to call [[yii\\db\\Command::execute()|execute()]]\nto actually run them.\n\n\n## Quoting Table and Column Names <span id=\"quoting-table-and-column-names\"></span>\n\nWhen writing database-agnostic code, properly quoting table and column names is often a headache because\ndifferent databases have different name quoting rules. To overcome this problem, you may use the following\nquoting syntax introduced by Yii:\n\n* `[[column name]]`: enclose a column name to be quoted in double square brackets; \n* `{{table name}}`: enclose a table name to be quoted in double curly brackets.\n\nYii DAO will automatically convert such constructs into the corresponding quoted column or table names using the\nDBMS specific syntax.\nFor example,\n\n```php\n// executes this SQL for MySQL: SELECT COUNT(`id`) FROM `employee`\n$count = Yii::$app->db->createCommand(\"SELECT COUNT([[id]]) FROM {{employee}}\")\n            ->queryScalar();\n```\n\n\n### Using Table Prefix <span id=\"using-table-prefix\"></span>\n\nIf most of your DB tables names share a common prefix, you may use the table prefix feature provided\nby Yii DAO.\n\nFirst, specify the table prefix via the [[yii\\db\\Connection::tablePrefix]] property in the application config:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        // ...\n        'db' => [\n            // ...\n            'tablePrefix' => 'tbl_',\n        ],\n    ],\n];\n```\n\nThen in your code, whenever you need to refer to a table whose name contains such a prefix, use the syntax\n`{{%table_name}}`. The percentage character will be automatically replaced with the table prefix that you have specified\nwhen configuring the DB connection. For example,\n\n```php\n// executes this SQL for MySQL: SELECT COUNT(`id`) FROM `tbl_employee`\n$count = Yii::$app->db->createCommand(\"SELECT COUNT([[id]]) FROM {{%employee}}\")\n            ->queryScalar();\n```\n\n\n## Performing Transactions <span id=\"performing-transactions\"></span>\n\nWhen running multiple related queries in a sequence, you may need to wrap them in a transaction to ensure the integrity\nand consistency of your database. If any of the queries fails, the database will be rolled back to the state as if\nnone of these queries were executed.\n \nThe following code shows a typical way of using transactions:\n\n```php\nYii::$app->db->transaction(function($db) {\n    $db->createCommand($sql1)->execute();\n    $db->createCommand($sql2)->execute();\n    // ... executing other SQL statements ...\n});\n```\n\nThe above code is equivalent to the following, which gives you more control about the error handling code:\n\n```php\n$db = Yii::$app->db;\n$transaction = $db->beginTransaction();\ntry {\n    $db->createCommand($sql1)->execute();\n    $db->createCommand($sql2)->execute();\n    // ... executing other SQL statements ...\n    \n    $transaction->commit();\n} catch(\\Exception $e) {\n    $transaction->rollBack();\n    throw $e;\n} catch(\\Throwable $e) {\n    $transaction->rollBack();\n    throw $e;\n}\n```\n\nBy calling the [[yii\\db\\Connection::beginTransaction()|beginTransaction()]] method, a new transaction is started.\nThe transaction is represented as a [[yii\\db\\Transaction]] object stored in the `$transaction` variable. Then,\nthe queries being executed are enclosed in a `try...catch...` block. If all queries are executed successfully,\nthe [[yii\\db\\Transaction::commit()|commit()]] method is called to commit the transaction. Otherwise, if an exception\nwill be triggered and caught, the [[yii\\db\\Transaction::rollBack()|rollBack()]] method is called to roll back\nthe changes made by the queries prior to that failed query in the transaction. `throw $e` will then re-throw the\nexception as if we had not caught it, so the normal error handling process will take care of it.\n\n> Note: in the above code we have two catch-blocks for compatibility \n> with PHP 5.x and PHP 7.x. `\\Exception` implements the [`\\Throwable` interface](https://www.php.net/manual/en/class.throwable.php)\n> since PHP 7.0, so you can skip the part with `\\Exception` if your app uses only PHP 7.0 and higher.\n\n\n### Specifying Isolation Levels <span id=\"specifying-isolation-levels\"></span>\n\nYii also supports setting [isolation levels] for your transactions. By default, when starting a new transaction,\nit will use the default isolation level set by your database system. You can override the default isolation level as follows,\n\n```php\n$isolationLevel = \\yii\\db\\Transaction::REPEATABLE_READ;\n\nYii::$app->db->transaction(function ($db) {\n    ....\n}, $isolationLevel);\n \n// or alternatively\n\n$transaction = Yii::$app->db->beginTransaction($isolationLevel);\n```\n\nYii provides four constants for the most common isolation levels:\n\n- [[\\yii\\db\\Transaction::READ_UNCOMMITTED]] - the weakest level, Dirty reads, non-repeatable reads and phantoms may occur.\n- [[\\yii\\db\\Transaction::READ_COMMITTED]] - avoid dirty reads.\n- [[\\yii\\db\\Transaction::REPEATABLE_READ]] - avoid dirty reads and non-repeatable reads.\n- [[\\yii\\db\\Transaction::SERIALIZABLE]] - the strongest level, avoids all of the above named problems.\n\nBesides using the above constants to specify isolation levels, you may also use strings with a valid syntax supported\nby the DBMS that you are using. For example, in PostgreSQL, you may use `\"SERIALIZABLE READ ONLY DEFERRABLE\"`.\n\nNote that some DBMS allow setting the isolation level only for the whole connection. Any subsequent transactions\nwill get the same isolation level even if you do not specify any. When using this feature\nyou may need to set the isolation level for all transactions explicitly to avoid conflicting settings.\nAt the time of this writing, only MSSQL and SQLite are affected by this limitation.\n\n> Note: SQLite only supports two isolation levels, so you can only use `READ UNCOMMITTED` and `SERIALIZABLE`.\nUsage of other levels will result in an exception being thrown.\n\n> Note: PostgreSQL does not allow setting the isolation level before the transaction starts so you can not\nspecify the isolation level directly when starting the transaction.\nYou have to call [[yii\\db\\Transaction::setIsolationLevel()]] in this case after the transaction has started.\n\n[isolation levels]: https://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels\n\n\n### Nesting Transactions <span id=\"nesting-transactions\"></span>\n\nIf your DBMS supports Savepoint, you may nest multiple transactions like the following:\n\n```php\nYii::$app->db->transaction(function ($db) {\n    // outer transaction\n    \n    $db->transaction(function ($db) {\n        // inner transaction\n    });\n});\n```\n\nOr alternatively,\n\n```php\n$db = Yii::$app->db;\n$outerTransaction = $db->beginTransaction();\ntry {\n    $db->createCommand($sql1)->execute();\n\n    $innerTransaction = $db->beginTransaction();\n    try {\n        $db->createCommand($sql2)->execute();\n        $innerTransaction->commit();\n    } catch (\\Exception $e) {\n        $innerTransaction->rollBack();\n        throw $e;\n    } catch (\\Throwable $e) {\n        $innerTransaction->rollBack();\n        throw $e;\n    }\n\n    $outerTransaction->commit();\n} catch (\\Exception $e) {\n    $outerTransaction->rollBack();\n    throw $e;\n} catch (\\Throwable $e) {\n    $outerTransaction->rollBack();\n    throw $e;\n}\n```\n\n\n## Replication and Read-Write Splitting <span id=\"read-write-splitting\"></span>\n\nMany DBMS support [database replication](https://en.wikipedia.org/wiki/Replication_(computing)#Database_replication)\nto get better database availability and faster server response time. With database replication, data are replicated\nfrom the so-called *master servers* to *slave servers*. All writes and updates must take place on the master servers,\nwhile reads may also take place on the slave servers.\n\nTo take advantage of database replication and achieve read-write splitting, you can configure a [[yii\\db\\Connection]]\ncomponent like the following:\n\n```php\n[\n    'class' => 'yii\\db\\Connection',\n\n    // configuration for the master\n    'dsn' => 'dsn for master server',\n    'username' => 'master',\n    'password' => '',\n\n    // common configuration for slaves\n    'slaveConfig' => [\n        'username' => 'slave',\n        'password' => '',\n        'attributes' => [\n            // use a smaller connection timeout\n            PDO::ATTR_TIMEOUT => 10,\n        ],\n    ],\n\n    // list of slave configurations\n    'slaves' => [\n        ['dsn' => 'dsn for slave server 1'],\n        ['dsn' => 'dsn for slave server 2'],\n        ['dsn' => 'dsn for slave server 3'],\n        ['dsn' => 'dsn for slave server 4'],\n    ],\n]\n```\n\nThe above configuration specifies a setup with a single master and multiple slaves. One of the slaves will\nbe connected and used to perform read queries, while the master will be used to perform write queries.\nSuch read-write splitting is accomplished automatically with this configuration. For example,\n\n```php\n// create a Connection instance using the above configuration\nYii::$app->db = Yii::createObject($config);\n\n// query against one of the slaves\n$rows = Yii::$app->db->createCommand('SELECT * FROM user LIMIT 10')->queryAll();\n\n// query against the master\nYii::$app->db->createCommand(\"UPDATE user SET username='demo' WHERE id=1\")->execute();\n```\n\n> Info: Queries performed by calling [[yii\\db\\Command::execute()]] are considered as write queries, while\n  all other queries done through one of the \"query\" methods of [[yii\\db\\Command]] are read queries.\n  You can get the currently active slave connection via `Yii::$app->db->slave`.\n\nThe `Connection` component supports load balancing and failover between slaves.\nWhen performing a read query for the first time, the `Connection` component will randomly pick a slave and\ntry connecting to it. If the slave is found \"dead\", it will try another one. If none of the slaves is available,\nit will connect to the master. By configuring a [[yii\\db\\Connection::serverStatusCache|server status cache]],\na \"dead\" server can be remembered so that it will not be tried again during a\n[[yii\\db\\Connection::serverRetryInterval|certain period of time]].\n\n> Info: In the above configuration, a connection timeout of 10 seconds is specified for every slave.\n  This means if a slave cannot be reached in 10 seconds, it is considered as \"dead\". You can adjust this parameter\n  based on your actual environment.\n\n\nYou can also configure multiple masters with multiple slaves. For example,\n\n\n```php\n[\n    'class' => 'yii\\db\\Connection',\n\n    // common configuration for masters\n    'masterConfig' => [\n        'username' => 'master',\n        'password' => '',\n        'attributes' => [\n            // use a smaller connection timeout\n            PDO::ATTR_TIMEOUT => 10,\n        ],\n    ],\n\n    // list of master configurations\n    'masters' => [\n        ['dsn' => 'dsn for master server 1'],\n        ['dsn' => 'dsn for master server 2'],\n    ],\n\n    // common configuration for slaves\n    'slaveConfig' => [\n        'username' => 'slave',\n        'password' => '',\n        'attributes' => [\n            // use a smaller connection timeout\n            PDO::ATTR_TIMEOUT => 10,\n        ],\n    ],\n\n    // list of slave configurations\n    'slaves' => [\n        ['dsn' => 'dsn for slave server 1'],\n        ['dsn' => 'dsn for slave server 2'],\n        ['dsn' => 'dsn for slave server 3'],\n        ['dsn' => 'dsn for slave server 4'],\n    ],\n]\n```\n\nThe above configuration specifies two masters and four slaves. The `Connection` component also supports\nload balancing and failover between masters just as it does between slaves. A difference is that when none \nof the masters are available an exception will be thrown.\n\n> Note: When you use the [[yii\\db\\Connection::masters|masters]] property to configure one or multiple\n  masters, all other properties for specifying a database connection (e.g. `dsn`, `username`, `password`)\n  with the `Connection` object itself will be ignored.\n\n\nBy default, transactions use the master connection. And within a transaction, all DB operations will use\nthe master connection. For example,\n\n```php\n$db = Yii::$app->db;\n// the transaction is started on the master connection\n$transaction = $db->beginTransaction();\n\ntry {\n    // both queries are performed against the master\n    $rows = $db->createCommand('SELECT * FROM user LIMIT 10')->queryAll();\n    $db->createCommand(\"UPDATE user SET username='demo' WHERE id=1\")->execute();\n\n    $transaction->commit();\n} catch(\\Exception $e) {\n    $transaction->rollBack();\n    throw $e;\n} catch(\\Throwable $e) {\n    $transaction->rollBack();\n    throw $e;\n}\n```\n\nIf you want to start a transaction with the slave connection, you should explicitly do so, like the following:\n\n```php\n$transaction = Yii::$app->db->slave->beginTransaction();\n```\n\nSometimes, you may want to force using the master connection to perform a read query. This can be achieved\nwith the `useMaster()` method:\n\n```php\n$rows = Yii::$app->db->useMaster(function ($db) {\n    return $db->createCommand('SELECT * FROM user LIMIT 10')->queryAll();\n});\n```\n\nYou may also directly set `Yii::$app->db->enableSlaves` to be `false` to direct all queries to the master connection.\n\n\n## Working with Database Schema <span id=\"database-schema\"></span>\n\nYii DAO provides a whole set of methods to let you manipulate the database schema, such as creating new tables,\ndropping a column from a table, etc. These methods are listed as follows:\n\n* [[yii\\db\\Command::createTable()|createTable()]]: creating a table\n* [[yii\\db\\Command::renameTable()|renameTable()]]: renaming a table\n* [[yii\\db\\Command::dropTable()|dropTable()]]: removing a table\n* [[yii\\db\\Command::truncateTable()|truncateTable()]]: removing all rows in a table\n* [[yii\\db\\Command::addColumn()|addColumn()]]: adding a column\n* [[yii\\db\\Command::renameColumn()|renameColumn()]]: renaming a column\n* [[yii\\db\\Command::dropColumn()|dropColumn()]]: removing a column\n* [[yii\\db\\Command::alterColumn()|alterColumn()]]: altering a column\n* [[yii\\db\\Command::addPrimaryKey()|addPrimaryKey()]]: adding a primary key\n* [[yii\\db\\Command::dropPrimaryKey()|dropPrimaryKey()]]: removing a primary key\n* [[yii\\db\\Command::addForeignKey()|addForeignKey()]]: adding a foreign key\n* [[yii\\db\\Command::dropForeignKey()|dropForeignKey()]]: removing a foreign key\n* [[yii\\db\\Command::createIndex()|createIndex()]]: creating an index\n* [[yii\\db\\Command::dropIndex()|dropIndex()]]: removing an index\n\nThese methods can be used like the following:\n\n```php\n// CREATE TABLE\nYii::$app->db->createCommand()->createTable('post', [\n    'id' => 'pk',\n    'title' => 'string',\n    'text' => 'text',\n]);\n```\n\nThe above array describes the name and types of the columns to be created. For the column types, Yii provides\na set of abstract data types, that allow you to define a database agnostic schema. These are converted to\nDBMS specific type definitions dependent on the database, the table is created in.\nPlease refer to the API documentation of the [[yii\\db\\Command::createTable()|createTable()]]-method for more information.\n\nBesides changing the database schema, you can also retrieve the definition information about a table through\nthe [[yii\\db\\Connection::getTableSchema()|getTableSchema()]] method of a DB connection. For example,\n\n```php\n$table = Yii::$app->db->getTableSchema('post');\n```\n\nThe method returns a [[yii\\db\\TableSchema]] object which contains the information about the table's columns,\nprimary keys, foreign keys, etc. All this information is mainly utilized by [query builder](db-query-builder.md) \nand [active record](db-active-record.md) to help you write database-agnostic code. \n"
  },
  {
    "path": "docs/guide/db-migrations.md",
    "content": "Database Migration\n==================\n\nDuring the course of developing and maintaining a database-driven application, the structure of the database\nbeing used evolves just like the source code does. For example, during the development of an application,\na new table may be found necessary; after the application is deployed to production, it may be discovered\nthat an index should be created to improve the query performance; and so on. Because a database structure change\noften requires some source code changes, Yii supports the so-called *database migration* feature that allows\nyou to keep track of database changes in terms of *database migrations* which are version-controlled together\nwith the source code.\n\nThe following steps show how database migration can be used by a team during development:\n\n1. Tim creates a new migration (e.g. creates a new table, changes a column definition, etc.).\n2. Tim commits the new migration into the source control system (e.g. Git, Mercurial).\n3. Doug updates his repository from the source control system and receives the new migration.\n4. Doug applies the migration to his local development database, thereby synchronizing his database\n   to reflect the changes that Tim has made.\n\nAnd the following steps show how to deploy a new release with database migrations to production:\n\n1. Scott creates a release tag for the project repository that contains some new database migrations.\n2. Scott updates the source code on the production server to the release tag.\n3. Scott applies any accumulated database migrations to the production database.\n\nYii provides a set of migration command line tools that allow you to:\n\n* create new migrations;\n* apply migrations;\n* revert migrations;\n* re-apply migrations;\n* show migration history and status.\n\nAll these tools are accessible through the command `yii migrate`. In this section we will describe in detail\nhow to accomplish various tasks using these tools. You may also get the usage of each tool via the help\ncommand `yii help migrate`.\n\n> Tip: Migrations could affect not only database schema but adjust existing data to fit new schema, create RBAC\n  hierarchy or clean up cache.\n\n> Note: When manipulating data using a migration you may find that using your [Active Record](db-active-record.md) classes\n> for this might be useful because some of the logic is already implemented there. Keep in mind however, that in contrast\n> to code written in the migrations, whose nature is to stay constant forever, application logic is subject to change.\n> So when using Active Record in migration code, changes to the logic in the Active Record layer may accidentally break\n> existing migrations. For this reason migration code should be kept independent of other application logic such\n> as Active Record classes.\n\n\n## Creating Migrations <span id=\"creating-migrations\"></span>\n\nTo create a new migration, run the following command:\n\n```\nyii migrate/create <name>\n```\n\nThe required `name` argument gives a brief description about the new migration. For example, if\nthe migration is about creating a new table named *news*, you may use the name `create_news_table`\nand run the following command:\n\n```\nyii migrate/create create_news_table\n```\n\n> Note: Because the `name` argument will be used as part of the generated migration class name,\n  it should only contain letters, digits, and/or underscore characters.\n\nThe above command will create a new PHP class file named `m150101_185401_create_news_table.php`\nin the `@app/migrations` directory. The file contains the following code which mainly declares\na migration class `m150101_185401_create_news_table` with the skeleton code:\n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function up()\n    {\n\n    }\n\n    public function down()\n    {\n        echo \"m101129_185401_create_news_table cannot be reverted.\\n\";\n\n        return false;\n    }\n\n    /*\n    // Use safeUp/safeDown to run migration code within a transaction\n    public function safeUp()\n    {\n    }\n\n    public function safeDown()\n    {\n    }\n    */\n}\n```\n\nEach database migration is defined as a PHP class extending from [[yii\\db\\Migration]]. The migration\nclass name is automatically generated in the format of `m<YYMMDD_HHMMSS>_<Name>`, where\n\n* `<YYMMDD_HHMMSS>` refers to the UTC datetime at which the migration creation command is executed.\n* `<Name>` is the same as the value of the `name` argument that you provide to the command.\n\nIn the migration class, you are expected to write code in the `up()` method that makes changes to the database structure.\nYou may also want to write code in the `down()` method to revert the changes made by `up()`. The `up()` method is invoked\nwhen you upgrade the database with this migration, while the `down()` method is invoked when you downgrade the database.\nThe following code shows how you may implement the migration class to create a `news` table:\n\n```php\n<?php\n\nuse yii\\db\\Schema;\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function up()\n    {\n        $this->createTable('news', [\n            'id' => Schema::TYPE_PK,\n            'title' => Schema::TYPE_STRING . ' NOT NULL',\n            'content' => Schema::TYPE_TEXT,\n        ]);\n    }\n\n    public function down()\n    {\n        $this->dropTable('news');\n    }\n}\n```\n\n> Info: Not all migrations are reversible. For example, if the `up()` method deletes a row of a table, you may\n  not be able to recover this row in the `down()` method. Sometimes, you may be just too lazy to implement\n  the `down()`, because it is not very common to revert database migrations. In this case, you should return\n  `false` in the `down()` method to indicate that the migration is not reversible.\n\nThe base migration class [[yii\\db\\Migration]] exposes a database connection via the [[yii\\db\\Migration::db|db]]\nproperty. You can use it to manipulate the database schema using the methods as described in\n[Working with Database Schema](db-dao.md#database-schema).\n\nRather than using physical types, when creating a table or column you should use *abstract types*\nso that your migrations are independent of specific DBMS. The [[yii\\db\\Schema]] class defines\na set of constants to represent the supported abstract types. These constants are named in the format\nof `TYPE_<Name>`. For example, `TYPE_PK` refers to auto-incremental primary key type; `TYPE_STRING`\nrefers to a string type. When a migration is applied to a particular database, the abstract types\nwill be translated into the corresponding physical types. In the case of MySQL, `TYPE_PK` will be turned\ninto `int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY`, while `TYPE_STRING` becomes `varchar(255)`.\n\nYou can append additional constraints when using abstract types. In the above example, ` NOT NULL` is appended\nto `Schema::TYPE_STRING` to specify that the column cannot be `null`.\n\n> Info: The mapping between abstract types and physical types is specified by\n  the [[yii\\db\\QueryBuilder::$typeMap|$typeMap]] property in each concrete `QueryBuilder` class.\n\nSince version 2.0.6, you can make use of the newly introduced schema builder which provides more convenient way of defining column schema.\nSo the migration above could be written like the following:\n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function up()\n    {\n        $this->createTable('news', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string()->notNull(),\n            'content' => $this->text(),\n        ]);\n    }\n\n    public function down()\n    {\n        $this->dropTable('news');\n    }\n}\n```\n\nA list of all available methods for defining the column types is available in the API documentation of [[yii\\db\\SchemaBuilderTrait]].\n\n> Info: The generated file permissions and ownership will be determined by the current environment. This might lead to\n  inaccessible files. This could, for example, happen when the migration is created within a docker container\n  and the files are edited on the host. In this case the `newFileMode` and/or `newFileOwnership` of the MigrateController\n  can be changed. E.g. in the application config:\n  ```php\n  <?php\n  return [\n      'controllerMap' => [\n          'migrate' => [\n              'class' => 'yii\\console\\controllers\\MigrateController',\n              'newFileOwnership' => '1000:1000', # Default WSL user id\n              'newFileMode' => 0660,\n          ],\n      ],\n  ];\n  ```\n\n## Generating Migrations <span id=\"generating-migrations\"></span>\n\nSince version 2.0.7 migration console provides a convenient way to create migrations.\n\nIf the migration name is of a special form, for example `create_xxx_table` or `drop_xxx_table` then the generated migration\nfile will contain extra code, in this case for creating/dropping tables.\nIn the following all variants of this feature are described.\n\n### Create Table\n\n```\nyii migrate/create create_post_table\n```\n\ngenerates\n\n```php\n/**\n * Handles the creation for table `post`.\n */\nclass m150811_220037_create_post_table extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey()\n        ]);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        $this->dropTable('post');\n    }\n}\n```\n\nTo create table fields right away, specify them via `--fields` option.\n\n```\nyii migrate/create create_post_table --fields=\"title:string,body:text\"\n```\n\ngenerates\n\n```php\n/**\n * Handles the creation for table `post`.\n */\nclass m150811_220037_create_post_table extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string(),\n            'body' => $this->text(),\n        ]);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        $this->dropTable('post');\n    }\n}\n\n```\n\nYou can specify more field parameters.\n\n```\nyii migrate/create create_post_table --fields=\"title:string(12):notNull:unique,body:text\"\n```\n\ngenerates\n\n```php\n/**\n * Handles the creation for table `post`.\n */\nclass m150811_220037_create_post_table extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string(12)->notNull()->unique(),\n            'body' => $this->text()\n        ]);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        $this->dropTable('post');\n    }\n}\n```\n\n> Note: Primary key is added automatically and is named `id` by default. If you want to use another name you may\n> specify it explicitly like `--fields=\"name:primaryKey\"`.\n\n#### Foreign keys\n\nSince 2.0.8 the generator supports foreign keys using the `foreignKey` keyword.\n\n```\nyii migrate/create create_post_table --fields=\"author_id:integer:notNull:foreignKey(user),category_id:integer:defaultValue(1):foreignKey,title:string,body:text\"\n```\n\ngenerates\n\n```php\n/**\n * Handles the creation for table `post`.\n * Has foreign keys to the tables:\n *\n * - `user`\n * - `category`\n */\nclass m160328_040430_create_post_table extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey(),\n            'author_id' => $this->integer()->notNull(),\n            'category_id' => $this->integer()->defaultValue(1),\n            'title' => $this->string(),\n            'body' => $this->text(),\n        ]);\n\n        // creates index for column `author_id`\n        $this->createIndex(\n            'idx-post-author_id',\n            'post',\n            'author_id'\n        );\n\n        // add foreign key for table `user`\n        $this->addForeignKey(\n            'fk-post-author_id',\n            'post',\n            'author_id',\n            'user',\n            'id',\n            'CASCADE'\n        );\n\n        // creates index for column `category_id`\n        $this->createIndex(\n            'idx-post-category_id',\n            'post',\n            'category_id'\n        );\n\n        // add foreign key for table `category`\n        $this->addForeignKey(\n            'fk-post-category_id',\n            'post',\n            'category_id',\n            'category',\n            'id',\n            'CASCADE'\n        );\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        // drops foreign key for table `user`\n        $this->dropForeignKey(\n            'fk-post-author_id',\n            'post'\n        );\n\n        // drops index for column `author_id`\n        $this->dropIndex(\n            'idx-post-author_id',\n            'post'\n        );\n\n        // drops foreign key for table `category`\n        $this->dropForeignKey(\n            'fk-post-category_id',\n            'post'\n        );\n\n        // drops index for column `category_id`\n        $this->dropIndex(\n            'idx-post-category_id',\n            'post'\n        );\n\n        $this->dropTable('post');\n    }\n}\n```\n\nThe position of the `foreignKey` keyword in the column description doesn't\nchange the generated code. That means:\n\n- `author_id:integer:notNull:foreignKey(user)`\n- `author_id:integer:foreignKey(user):notNull`\n- `author_id:foreignKey(user):integer:notNull`\n\nAll generate the same code.\n\nThe `foreignKey` keyword can take a parameter between parenthesis which will be\nthe name of the related table for the generated foreign key. If no parameter\nis passed then the table name will be deduced from the column name.\n\nIn the example above `author_id:integer:notNull:foreignKey(user)` will generate a\ncolumn named `author_id` with a foreign key to the `user` table while\n`category_id:integer:defaultValue(1):foreignKey` will generate a column\n`category_id` with a foreign key to the `category` table.\n\nSince 2.0.11, `foreignKey` keyword accepts a second parameter, separated by whitespace.\nIt accepts the name of the related column for the foreign key generated.\nIf no second parameter is passed, the column name will be fetched from table schema.\nIf no schema exists, primary key isn't set or is composite, default name `id` will be used.\n\n### Drop Table\n\n```\nyii migrate/create drop_post_table --fields=\"title:string(12):notNull:unique,body:text\"\n```\n\ngenerates\n\n```php\nclass m150811_220037_drop_post_table extends Migration\n{\n    public function up()\n    {\n        $this->dropTable('post');\n    }\n\n    public function down()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string(12)->notNull()->unique(),\n            'body' => $this->text()\n        ]);\n    }\n}\n```\n\n### Add Column\n\nIf the migration name is of the form `add_xxx_column_to_yyy_table` then the file\ncontent would contain `addColumn` and `dropColumn` statements necessary.\n\nTo add column:\n\n```\nyii migrate/create add_position_column_to_post_table --fields=\"position:integer\"\n```\n\ngenerates\n\n```php\nclass m150811_220037_add_position_column_to_post_table extends Migration\n{\n    public function up()\n    {\n        $this->addColumn('post', 'position', $this->integer());\n    }\n\n    public function down()\n    {\n        $this->dropColumn('post', 'position');\n    }\n}\n```\n\nYou can specify multiple columns as follows:\n\n```\nyii migrate/create add_xxx_column_yyy_column_to_zzz_table --fields=\"xxx:integer,yyy:text\"\n```\n\n### Drop Column\n\nIf the migration name is of the form `drop_xxx_column_from_yyy_table` then\nthe file content would contain `addColumn` and `dropColumn` statements necessary.\n\n```php\nyii migrate/create drop_position_column_from_post_table --fields=\"position:integer\"\n```\n\ngenerates\n\n```php\nclass m150811_220037_drop_position_column_from_post_table extends Migration\n{\n    public function up()\n    {\n        $this->dropColumn('post', 'position');\n    }\n\n    public function down()\n    {\n        $this->addColumn('post', 'position', $this->integer());\n    }\n}\n```\n\n### Add Junction Table\n\nIf the migration name is of the form `create_junction_table_for_xxx_and_yyy_tables` or `create_junction_xxx_and_yyy_tables`\nthen code necessary to create junction table will be generated.\n\n```\nyii migrate/create create_junction_table_for_post_and_tag_tables --fields=\"created_at:dateTime\"\n```\n\ngenerates\n\n```php\n/**\n * Handles the creation for table `post_tag`.\n * Has foreign keys to the tables:\n *\n * - `post`\n * - `tag`\n */\nclass m160328_041642_create_junction_table_for_post_and_tag_tables extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $this->createTable('post_tag', [\n            'post_id' => $this->integer(),\n            'tag_id' => $this->integer(),\n            'created_at' => $this->dateTime(),\n            'PRIMARY KEY(post_id, tag_id)',\n        ]);\n\n        // creates index for column `post_id`\n        $this->createIndex(\n            'idx-post_tag-post_id',\n            'post_tag',\n            'post_id'\n        );\n\n        // add foreign key for table `post`\n        $this->addForeignKey(\n            'fk-post_tag-post_id',\n            'post_tag',\n            'post_id',\n            'post',\n            'id',\n            'CASCADE'\n        );\n\n        // creates index for column `tag_id`\n        $this->createIndex(\n            'idx-post_tag-tag_id',\n            'post_tag',\n            'tag_id'\n        );\n\n        // add foreign key for table `tag`\n        $this->addForeignKey(\n            'fk-post_tag-tag_id',\n            'post_tag',\n            'tag_id',\n            'tag',\n            'id',\n            'CASCADE'\n        );\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        // drops foreign key for table `post`\n        $this->dropForeignKey(\n            'fk-post_tag-post_id',\n            'post_tag'\n        );\n\n        // drops index for column `post_id`\n        $this->dropIndex(\n            'idx-post_tag-post_id',\n            'post_tag'\n        );\n\n        // drops foreign key for table `tag`\n        $this->dropForeignKey(\n            'fk-post_tag-tag_id',\n            'post_tag'\n        );\n\n        // drops index for column `tag_id`\n        $this->dropIndex(\n            'idx-post_tag-tag_id',\n            'post_tag'\n        );\n\n        $this->dropTable('post_tag');\n    }\n}\n```\n\nSince 2.0.11 foreign key column names for junction tables are fetched from table schema.\nIn case table isn't defined in schema, or the primary key isn't set or is composite, default name `id` is used.\n\n### Transactional Migrations <span id=\"transactional-migrations\"></span>\n\nWhile performing complex DB migrations, it is important to ensure each migration to either succeed or fail as a whole\nso that the database can maintain integrity and consistency. To achieve this goal, it is recommended that you\nenclose the DB operations of each migration in a [transaction](db-dao.md#performing-transactions).\n\nAn even easier way of implementing transactional migrations is to put migration code in the `safeUp()` and `safeDown()`\nmethods. These two methods differ from `up()` and `down()` in that they are enclosed implicitly in a transaction.\nAs a result, if any operation in these methods fails, all prior operations will be rolled back automatically.\n\nIn the following example, besides creating the `news` table we also insert an initial row into this table.\n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function safeUp()\n    {\n        $this->createTable('news', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string()->notNull(),\n            'content' => $this->text(),\n        ]);\n\n        $this->insert('news', [\n            'title' => 'test 1',\n            'content' => 'content 1',\n        ]);\n    }\n\n    public function safeDown()\n    {\n        $this->delete('news', ['id' => 1]);\n        $this->dropTable('news');\n    }\n}\n```\n\nNote that usually when you perform multiple DB operations in `safeUp()`, you should reverse their execution order\nin `safeDown()`. In the above example we first create the table and then insert a row in `safeUp()`; while\nin `safeDown()` we first delete the row and then drop the table.\n\n> Note: Not all DBMS support transactions. And some DB queries cannot be put into a transaction. For some examples,\n  please refer to [implicit commit](https://dev.mysql.com/doc/refman/5.7/en/implicit-commit.html). If this is the case,\n  you should still implement `up()` and `down()`, instead.\n\n\n### Database Accessing Methods <span id=\"db-accessing-methods\"></span>\n\nThe base migration class [[yii\\db\\Migration]] provides a set of methods to let you access and manipulate databases.\nYou may find these methods are named similarly as the [DAO methods](db-dao.md) provided by the [[yii\\db\\Command]] class.\nFor example, the [[yii\\db\\Migration::createTable()]] method allows you to create a new table,\njust like [[yii\\db\\Command::createTable()]] does.\n\nThe benefit of using the methods provided by [[yii\\db\\Migration]] is that you do not need to explicitly\ncreate [[yii\\db\\Command]] instances and the execution of each method will automatically display useful messages\ntelling you what database operations are done and how long they take.\n\nBelow is the list of all these database accessing methods:\n\n* [[yii\\db\\Migration::execute()|execute()]]: executing a SQL statement\n* [[yii\\db\\Migration::insert()|insert()]]: inserting a single row\n* [[yii\\db\\Migration::batchInsert()|batchInsert()]]: inserting multiple rows\n* [[yii\\db\\Migration::update()|update()]]: updating rows\n* [[yii\\db\\Migration::upsert()|upsert()]]: inserting a single row or updating it if it exists (since 2.0.14)\n* [[yii\\db\\Migration::delete()|delete()]]: deleting rows\n* [[yii\\db\\Migration::createTable()|createTable()]]: creating a table\n* [[yii\\db\\Migration::renameTable()|renameTable()]]: renaming a table\n* [[yii\\db\\Migration::dropTable()|dropTable()]]: removing a table\n* [[yii\\db\\Migration::truncateTable()|truncateTable()]]: removing all rows in a table\n* [[yii\\db\\Migration::addColumn()|addColumn()]]: adding a column\n* [[yii\\db\\Migration::renameColumn()|renameColumn()]]: renaming a column\n* [[yii\\db\\Migration::dropColumn()|dropColumn()]]: removing a column\n* [[yii\\db\\Migration::alterColumn()|alterColumn()]]: altering a column\n* [[yii\\db\\Migration::addPrimaryKey()|addPrimaryKey()]]: adding a primary key\n* [[yii\\db\\Migration::dropPrimaryKey()|dropPrimaryKey()]]: removing a primary key\n* [[yii\\db\\Migration::addForeignKey()|addForeignKey()]]: adding a foreign key\n* [[yii\\db\\Migration::dropForeignKey()|dropForeignKey()]]: removing a foreign key\n* [[yii\\db\\Migration::createIndex()|createIndex()]]: creating an index\n* [[yii\\db\\Migration::dropIndex()|dropIndex()]]: removing an index\n* [[yii\\db\\Migration::addCommentOnColumn()|addCommentOnColumn()]]: adding comment to column\n* [[yii\\db\\Migration::dropCommentFromColumn()|dropCommentFromColumn()]]: dropping comment from column\n* [[yii\\db\\Migration::addCommentOnTable()|addCommentOnTable()]]: adding comment to table\n* [[yii\\db\\Migration::dropCommentFromTable()|dropCommentFromTable()]]: dropping comment from table\n\n> Info: [[yii\\db\\Migration]] does not provide a database query method. This is because you normally do not need\n> to display extra message about retrieving data from a database. It is also because you can use the powerful\n> [Query Builder](db-query-builder.md) to build and run complex queries.\n> Using Query Builder in a migration may look like this:\n>\n> ```php\n> // update status field for all users\n> foreach((new Query)->from('users')->each() as $user) {\n>     $this->update('users', ['status' => 1], ['id' => $user['id']]);\n> }\n> ```\n\n## Applying Migrations <span id=\"applying-migrations\"></span>\n\nTo upgrade a database to its latest structure, you should apply all available new migrations using the following command:\n\n```\nyii migrate\n```\n\nThis command will list all migrations that have not been applied so far. If you confirm that you want to apply\nthese migrations, it will run the `up()` or `safeUp()` method in every new migration class, one after another,\nin the order of their timestamp values. If any of the migrations fails, the command will quit without applying\nthe rest of the migrations.\n\n> Tip: In case you don't have command line at your server you may try [web shell](https://github.com/samdark/yii2-webshell)\n> extension.\n\nFor each migration that has been successfully applied, the command will insert a row into a database table named\n`migration` to record the successful application of the migration. This will allow the migration tool to identify\nwhich migrations have been applied and which have not.\n\n> Info: The migration tool will automatically create the `migration` table in the database specified by\n  the [[yii\\console\\controllers\\MigrateController::db|db]] option of the command. By default, the database\n  is specified by the `db` [application component](structure-application-components.md).\n\nSometimes, you may only want to apply one or a few new migrations, instead of all available migrations.\nYou can do so by specifying the number of migrations that you want to apply when running the command.\nFor example, the following command will try to apply the next three available migrations:\n\n```\nyii migrate 3\n```\n\nYou can also explicitly specify a particular migration to which the database should be migrated\nby using the `migrate/to` command in one of the following formats:\n\n```\nyii migrate/to 150101_185401                      # using timestamp to specify the migration\nyii migrate/to \"2015-01-01 18:54:01\"              # using a string that can be parsed by strtotime()\nyii migrate/to m150101_185401_create_news_table   # using full name\nyii migrate/to 1392853618                         # using UNIX timestamp\n```\n\nIf there are any unapplied migrations earlier than the specified one, they will all be applied before the specified\nmigration is applied.\n\nIf the specified migration has already been applied before, any later applied migrations will be reverted.\n\n\n## Reverting Migrations <span id=\"reverting-migrations\"></span>\n\nTo revert (undo) one or multiple migrations that have been applied before, you can run the following command:\n\n```\nyii migrate/down     # revert the most recently applied migration\nyii migrate/down 3   # revert the most 3 recently applied migrations\n```\n\n> Note: Not all migrations are reversible. Trying to revert such migrations will cause an error and stop the\n  entire reverting process.\n\n\n## Redoing Migrations <span id=\"redoing-migrations\"></span>\n\nRedoing migrations means first reverting the specified migrations and then applying again. This can be done\nas follows:\n\n```\nyii migrate/redo        # redo the last applied migration\nyii migrate/redo 3      # redo the last 3 applied migrations\n```\n\n> Note: If a migration is not reversible, you will not be able to redo it.\n\n## Refreshing Migrations <span id=\"refreshing-migrations\"></span>\n\nSince Yii 2.0.13 you can delete all tables and foreign keys from the database and apply all migrations from the beginning.\n\n```\nyii migrate/fresh       # truncate the database and apply all migrations from the beginning\n```\n\n## Listing Migrations <span id=\"listing-migrations\"></span>\n\nTo list which migrations have been applied and which are not, you may use the following commands:\n\n```\nyii migrate/history     # showing the last 10 applied migrations\nyii migrate/history 5   # showing the last 5 applied migrations\nyii migrate/history all # showing all applied migrations\n\nyii migrate/new         # showing the first 10 new migrations\nyii migrate/new 5       # showing the first 5 new migrations\nyii migrate/new all     # showing all new migrations\n```\n\n\n## Modifying Migration History <span id=\"modifying-migration-history\"></span>\n\nInstead of actually applying or reverting migrations, sometimes you may simply want to mark that your database\nhas been upgraded to a particular migration. This often happens when you manually change the database to a particular\nstate and you do not want the migration(s) for that change to be re-applied later. You can achieve this goal with\nthe following command:\n\n```\nyii migrate/mark 150101_185401                      # using timestamp to specify the migration\nyii migrate/mark \"2015-01-01 18:54:01\"              # using a string that can be parsed by strtotime()\nyii migrate/mark m150101_185401_create_news_table   # using full name\nyii migrate/mark 1392853618                         # using UNIX timestamp\n```\n\nThe command will modify the `migration` table by adding or deleting certain rows to indicate that the database\nhas been applied migrations to the specified one. No migrations will be applied or reverted by this command.\n\n\n## Customizing Migrations <span id=\"customizing-migrations\"></span>\n\nThere are several ways to customize the migration command.\n\n\n### Using Command Line Options <span id=\"using-command-line-options\"></span>\n\nThe migration command comes with a few command-line options that can be used to customize its behaviors:\n\n* `interactive`: boolean (defaults to `true`), specifies whether to perform migrations in an interactive mode.\n  When this is `true`, the user will be prompted before the command performs certain actions.\n  You may want to set this to `false` if the command is being used in a background process.\n\n* `migrationPath`: string|array (defaults to `@app/migrations`), specifies the directory storing all migration\n  class files. This can be specified as either a directory path or a path [alias](concept-aliases.md).\n  Note that the directory must exist, or the command may trigger an error. Since version 2.0.12 an array can be\n  specified for loading migrations from multiple sources.\n\n* `migrationTable`: string (defaults to `migration`), specifies the name of the database table for storing\n  migration history information. The table will be automatically created by the command if it does not exist.\n  You may also manually create it using the structure `version varchar(255) primary key, apply_time integer`.\n\n* `db`: string (defaults to `db`), specifies the ID of the database [application component](structure-application-components.md).\n  It represents the database that will be migrated using this command.\n\n* `templateFile`: string (defaults to `@yii/views/migration.php`), specifies the path of the template file\n  that is used for generating skeleton migration class files. This can be specified as either a file path\n  or a path [alias](concept-aliases.md). The template file is a PHP script in which you can use a predefined variable\n  named `$className` to get the migration class name.\n\n* `generatorTemplateFiles`: array (defaults to `[\n        'create_table' => '@yii/views/createTableMigration.php',\n        'drop_table' => '@yii/views/dropTableMigration.php',\n        'add_column' => '@yii/views/addColumnMigration.php',\n        'drop_column' => '@yii/views/dropColumnMigration.php',\n        'create_junction' => '@yii/views/createTableMigration.php'\n  ]`), specifies template files for generating migration code. See \"[Generating Migrations](#generating-migrations)\"\n  for more details.\n\n* `fields`: array of column definition strings used for creating migration code. Defaults to `[]`. The format of each\n  definition is `COLUMN_NAME:COLUMN_TYPE:COLUMN_DECORATOR`. For example, `--fields=name:string(12):notNull` produces\n  a string column of size 12 which is not `null`.\n\nThe following example shows how you can use these options.\n\nFor example, if we want to migrate a `forum` module whose migration files\nare located within the module's `migrations` directory, we can use the following\ncommand:\n\n```\n# migrate the migrations in a forum module non-interactively\nyii migrate --migrationPath=@app/modules/forum/migrations --interactive=0\n```\n\n\n### Configuring Command Globally <span id=\"configuring-command-globally\"></span>\n\nInstead of entering the same option values every time you run the migration command, you may configure it\nonce for all in the application configuration like shown below:\n\n```php\nreturn [\n    'controllerMap' => [\n        'migrate' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationTable' => 'backend_migration',\n        ],\n    ],\n];\n```\n\nWith the above configuration, each time you run the migration command, the `backend_migration` table\nwill be used to record the migration history. You no longer need to specify it via the `migrationTable`\ncommand-line option.\n\n\n### Namespaced Migrations <span id=\"namespaced-migrations\"></span>\n\nSince 2.0.10 you can use namespaces for the migration classes. You can specify the list of the migration namespaces via\n[[yii\\console\\controllers\\MigrateController::migrationNamespaces|migrationNamespaces]]. Using of the namespaces for\nmigration classes allows you usage of the several source locations for the migrations. For example:\n\n```php\nreturn [\n    'controllerMap' => [\n        'migrate' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationPath' => null, // disable non-namespaced migrations if app\\migrations is listed below\n            'migrationNamespaces' => [\n                'app\\migrations', // Common migrations for the whole application\n                'module\\migrations', // Migrations for the specific project's module\n                'some\\extension\\migrations', // Migrations for the specific extension\n            ],\n        ],\n    ],\n];\n```\n\n> Note: Migrations applied from different namespaces will create a **single** migration history, e.g. you might be\n  unable to apply or revert migrations from particular namespace only.\n\nWhile operating namespaced migrations: creating new, reverting and so on, you should specify full namespace before\nmigration name. Note that backslash (`\\`) symbol is usually considered a special character in the shell, so you need\nto escape it properly to avoid shell errors or incorrect behavior. For example:\n\n```\nyii migrate/create app\\\\migrations\\\\CreateUserTable\n```\n\n> Note: Migrations specified via [[yii\\console\\controllers\\MigrateController::migrationPath|migrationPath]] can not\n  contain a namespace, namespaced migration can be applied only via [[yii\\console\\controllers\\MigrateController::migrationNamespaces]]\n  property.\n\nSince version 2.0.12 the [[yii\\console\\controllers\\MigrateController::migrationPath|migrationPath]] property\nalso accepts an array for specifying multiple directories that contain migrations without a namespace.\nThis is mainly added to be used in existing projects which use migrations from different locations. These migrations mainly come\nfrom external sources, like Yii extensions developed by other developers,\nwhich can not be changed to use namespaces easily when starting to use the new approach.\n\n#### Generating namespaced migrations\n\nNamespaced migrations follow \"CamelCase\" naming pattern `M<YYMMDDHHMMSS><Name>` (for example `M190720100234CreateUserTable`). \nWhen generating such migration remember that table name will be converted from \"CamelCase\" format to \"underscored\". For \nexample:\n\n```\nyii migrate/create app\\\\migrations\\\\DropGreenHotelTable\n```\n\ngenerates migration within namespace `app\\migrations` dropping table `green_hotel` and\n\n```\nyii migrate/create app\\\\migrations\\\\CreateBANANATable\n```\n\ngenerates migration within namespace `app\\migrations` creating table `b_a_n_a_n_a`.\n\nIf table's name is mixed-cased (like `studentsExam`) you need to precede the name with underscore:\n\n```\nyii migrate/create app\\\\migrations\\\\Create_studentsExamTable\n```\n\nThis generates migration within namespace `app\\migrations` creating table `studentsExam`.\n\n### Separated Migrations <span id=\"separated-migrations\"></span>\n\nSometimes using single migration history for all project migrations is not desirable. For example: you may install some\n'blog' extension, which contains fully separated functionality and contain its own migrations, which should not affect\nthe ones dedicated to main project functionality.\n\nIf you want several migrations to be applied and tracked down completely separated from each other, you can configure multiple\nmigration commands which will use different namespaces and migration history tables:\n\n```php\nreturn [\n    'controllerMap' => [\n        // Common migrations for the whole application\n        'migrate-app' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationNamespaces' => ['app\\migrations'],\n            'migrationTable' => 'migration_app',\n            'migrationPath' => null,\n        ],\n        // Migrations for the specific project's module\n        'migrate-module' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationNamespaces' => ['module\\migrations'],\n            'migrationTable' => 'migration_module',\n            'migrationPath' => null,\n        ],\n        // Migrations for the specific extension\n        'migrate-rbac' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationPath' => '@yii/rbac/migrations',\n            'migrationTable' => 'migration_rbac',\n        ],\n    ],\n];\n```\n\nNote that to synchronize database you now need to run multiple commands instead of one:\n\n```\nyii migrate-app\nyii migrate-module\nyii migrate-rbac\n```\n\n\n## Migrating Multiple Databases <span id=\"migrating-multiple-databases\"></span>\n\nBy default, migrations are applied to the same database specified by the `db` [application component](structure-application-components.md).\nIf you want them to be applied to a different database, you may specify the `db` command-line option like shown below,\n\n```\nyii migrate --db=db2\n```\n\nThe above command will apply migrations to the `db2` database.\n\nSometimes it may happen that you want to apply *some* of the migrations to one database, while some others to another\ndatabase. To achieve this goal, when implementing a migration class you should explicitly specify the DB component\nID that the migration would use, like the following:\n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function init()\n    {\n        $this->db = 'db2';\n        parent::init();\n    }\n}\n```\n\nThe above migration will be applied to `db2`, even if you specify a different database through the `db` command-line\noption. Note that the migration history will still be recorded in the database specified by the `db` command-line option.\n\nIf you have multiple migrations that use the same database, it is recommended that you create a base migration class\nwith the above `init()` code. Then each migration class can extend from this base class.\n\n> Tip: Besides setting the [[yii\\db\\Migration::db|db]] property, you can also operate on different databases\n  by creating new database connections to them in your migration classes. You then use the [DAO methods](db-dao.md)\n  with these connections to manipulate different databases.\n\nAnother strategy that you can take to migrate multiple databases is to keep migrations for different databases in\ndifferent migration paths. Then you can migrate these databases in separate commands like the following:\n\n```\nyii migrate --migrationPath=@app/migrations/db1 --db=db1\nyii migrate --migrationPath=@app/migrations/db2 --db=db2\n...\n```\n\nThe first command will apply migrations in `@app/migrations/db1` to the `db1` database, the second command\nwill apply migrations in `@app/migrations/db2` to `db2`, and so on.\n"
  },
  {
    "path": "docs/guide/db-query-builder.md",
    "content": "Query Builder\n=============\n\nBuilt on top of [Database Access Objects](db-dao.md), query builder allows you to construct a SQL query\nin a programmatic and DBMS-agnostic way. Compared to writing raw SQL statements, using query builder will help you write \nmore readable SQL-related code and generate more secure SQL statements.  \n\nUsing query builder usually involves two steps:\n\n1. Build a [[yii\\db\\Query]] object to represent different parts (e.g. `SELECT`, `FROM`) of a SELECT SQL statement.\n2. Execute a query method (e.g. `all()`) of [[yii\\db\\Query]] to retrieve data from the database.\n\nThe following code shows a typical way of using query builder:\n\n```php\n$rows = (new \\yii\\db\\Query())\n    ->select(['id', 'email'])\n    ->from('user')\n    ->where(['last_name' => 'Smith'])\n    ->limit(10)\n    ->all();\n```\n\nThe above code generates and executes the following SQL query, where the `:last_name` parameter is bound with the\nstring `'Smith'`.\n\n```sql\nSELECT `id`, `email` \nFROM `user`\nWHERE `last_name` = :last_name\nLIMIT 10\n```\n\n> Info: You usually mainly work with [[yii\\db\\Query]] instead of [[yii\\db\\QueryBuilder]]. The latter is invoked\n  by the former implicitly when you call one of the query methods. [[yii\\db\\QueryBuilder]] is the class responsible\n  for generating DBMS-dependent SQL statements (e.g. quoting table/column names differently) from DBMS-independent\n  [[yii\\db\\Query]] objects.\n\n\n## Building Queries <span id=\"building-queries\"></span>\n\nTo build a [[yii\\db\\Query]] object, you call different query building methods to specify different parts of\na SQL query. The names of these methods resemble the SQL keywords used in the corresponding parts of the SQL\nstatement. For example, to specify the `FROM` part of a SQL query, you would call the [[yii\\db\\Query::from()|from()]] method.\nAll the query building methods return the query object itself, which allows you to chain multiple calls together.\n\nIn the following, we will describe the usage of each query building method.\n\n\n### [[yii\\db\\Query::select()|select()]] <span id=\"select\"></span>\n\nThe [[yii\\db\\Query::select()|select()]] method specifies the `SELECT` fragment of a SQL statement. You can specify \ncolumns to be selected in either an array or a string, like the following. The column names being selected will \nbe automatically quoted when the SQL statement is being generated from a query object.\n \n```php\n$query->select(['id', 'email']);\n\n// equivalent to:\n\n$query->select('id, email');\n```\n\nThe column names being selected may include table prefixes and/or column aliases, like you do when writing raw SQL queries.\nFor example,\n\n```php\n$query->select(['user.id AS user_id', 'email']);\n\n// equivalent to:\n\n$query->select('user.id AS user_id, email');\n```\n\nIf you are using the array format to specify columns, you can also use the array keys to specify the column aliases.\nFor example, the above code can be rewritten as follows,\n\n```php\n$query->select(['user_id' => 'user.id', 'email']);\n```\n\nIf you do not call the [[yii\\db\\Query::select()|select()]] method when building a query, `*` will be selected, which\nmeans selecting *all* columns.\n\nBesides column names, you can also select DB expressions. You must use the array format when selecting a DB expression\nthat contains commas to avoid incorrect automatic name quoting. For example,\n\n```php\n$query->select([\"CONCAT(first_name, ' ', last_name) AS full_name\", 'email']); \n```\n\nAs with all places where raw SQL is involved, you may use the [DBMS agnostic quoting syntax](db-dao.md#quoting-table-and-column-names)\nfor table and column names when writing DB expressions in select.\n\nStarting from version 2.0.1, you may also select sub-queries. You should specify each sub-query in terms of \na [[yii\\db\\Query]] object. For example,\n \n```php\n$subQuery = (new Query())->select('COUNT(*)')->from('user');\n\n// SELECT `id`, (SELECT COUNT(*) FROM `user`) AS `count` FROM `post`\n$query = (new Query())->select(['id', 'count' => $subQuery])->from('post');\n```\n\nTo select distinct rows, you may call [[yii\\db\\Query::distinct()|distinct()]], like the following:\n\n```php\n// SELECT DISTINCT `user_id` ...\n$query->select('user_id')->distinct();\n```\n\nYou can call [[yii\\db\\Query::addSelect()|addSelect()]] to select additional columns. For example,\n\n```php\n$query->select(['id', 'username'])\n    ->addSelect(['email']);\n```\n\n\n### [[yii\\db\\Query::from()|from()]] <span id=\"from\"></span>\n\nThe [[yii\\db\\Query::from()|from()]] method specifies the `FROM` fragment of a SQL statement. For example,\n\n```php\n// SELECT * FROM `user`\n$query->from('user');\n```\n\nYou can specify the table(s) being selected from in either a string or an array. The table names may contain\nschema prefixes and/or table aliases, like you do when writing raw SQL statements. For example,\n\n```php\n$query->from(['public.user u', 'public.post p']);\n\n// equivalent to:\n\n$query->from('public.user u, public.post p');\n```\n\nIf you are using the array format, you can also use the array keys to specify the table aliases, like the following:\n\n```php\n$query->from(['u' => 'public.user', 'p' => 'public.post']);\n```\n\nBesides table names, you can also select from sub-queries by specifying them in terms of [[yii\\db\\Query]] objects.\nFor example,\n\n```php\n$subQuery = (new Query())->select('id')->from('user')->where('status=1');\n\n// SELECT * FROM (SELECT `id` FROM `user` WHERE status=1) u \n$query->from(['u' => $subQuery]);\n```\n\n#### Prefixes\nAlso a default [[yii\\db\\Connection::$tablePrefix|tablePrefix]] can be applied. Implementation instructions\nare in the [\"Quoting Tables\" section of the \"Database Access Objects\" guide](db-dao.md#quoting-table-and-column-names).\n\n### [[yii\\db\\Query::where()|where()]] <span id=\"where\"></span>\n\nThe [[yii\\db\\Query::where()|where()]] method specifies the `WHERE` fragment of a SQL query. You can use one of\nthe four formats to specify a `WHERE` condition:\n\n- string format, e.g., `'status=1'`\n- hash format, e.g. `['status' => 1, 'type' => 2]`\n- operator format, e.g. `['like', 'name', 'test']`\n- object format, e.g. `new LikeCondition('name', 'LIKE', 'test')`\n\n#### String Format <span id=\"string-format\"></span>\n\nString format is best used to specify very simple conditions or if you need to use built-in functions of the DBMS.\nIt works as if you are writing a raw SQL. For example,\n\n```php\n$query->where('status=1');\n\n// or use parameter binding to bind dynamic parameter values\n$query->where('status=:status', [':status' => $status]);\n\n// raw SQL using MySQL YEAR() function on a date field\n$query->where('YEAR(somedate) = 2015');\n```\n\nDo NOT embed variables directly in the condition like the following, especially if the variable values come from \nend user inputs, because this will make your application subject to SQL injection attacks.\n \n```php\n// Dangerous! Do NOT do this unless you are very certain $status must be an integer.\n$query->where(\"status=$status\");\n```\n\nWhen using `parameter binding`, you may call [[yii\\db\\Query::params()|params()]] or [[yii\\db\\Query::addParams()|addParams()]]\nto specify parameters separately.\n\n```php\n$query->where('status=:status')\n    ->addParams([':status' => $status]);\n```\n\nAs with all places where raw SQL is involved, you may use the [DBMS agnostic quoting syntax](db-dao.md#quoting-table-and-column-names)\nfor table and column names when writing conditions in string format. \n\n#### Hash Format <span id=\"hash-format\"></span>\n\nHash format is best used to specify multiple `AND`-concatenated sub-conditions each being a simple equality assertion.\nIt is written as an array whose keys are column names and values the corresponding values that the columns should be.\nFor example,\n\n```php\n// ...WHERE (`status` = 10) AND (`type` IS NULL) AND (`id` IN (4, 8, 15))\n$query->where([\n    'status' => 10,\n    'type' => null,\n    'id' => [4, 8, 15],\n]);\n```\n\nAs you can see, the query builder is intelligent enough to properly handle values that are nulls or arrays.\n\nYou can also use sub-queries with hash format like the following:\n\n```php\n$userQuery = (new Query())->select('id')->from('user');\n\n// ...WHERE `id` IN (SELECT `id` FROM `user`)\n$query->where(['id' => $userQuery]);\n```\n\nUsing the Hash Format, Yii internally applies parameter binding for values, so in contrast to the [string format](#string-format),\nhere you do not have to add parameters manually. However, note that Yii never escapes column names, so if you pass\na variable obtained from user side as a column name without any additional checks, the application will become vulnerable\nto SQL injection attack. In order to keep the application secure, either do not use variables as column names or\nfilter variable against allowlist. In case you need to get column name from user, read the [Filtering Data](output-data-widgets.md#filtering-data)\nguide article. For example the following code is vulnerable:\n\n```php\n// Vulnerable code:\n$column = $request->get('column');\n$value = $request->get('value');\n$query->where([$column => $value]);\n// $value is safe, but $column name won't be encoded!\n```\n\n#### Operator Format <span id=\"operator-format\"></span>\n\nOperator format allows you to specify arbitrary conditions in a programmatic way. It takes the following format:\n\n```php\n[operator, operand1, operand2, ...]\n```\n\nwhere the operands can each be specified in string format, hash format or operator format recursively, while\nthe operator can be one of the following:\n\n- `and`: the operands should be concatenated together using `AND`. For example,\n  `['and', 'id=1', 'id=2']` will generate `id=1 AND id=2`. If an operand is an array,\n  it will be converted into a string using the rules described here. For example,\n  `['and', 'type=1', ['or', 'id=1', 'id=2']]` will generate `type=1 AND (id=1 OR id=2)`.\n  The method will NOT do any quoting or escaping.\n\n- `or`: similar to the `and` operator except that the operands are concatenated using `OR`.\n\n- `not`: requires only operand 1, which will be wrapped in `NOT()`. For example, `['not', 'id=1']` will generate `NOT (id=1)`. Operand 1 may also be an array to describe multiple expressions. For example `['not', ['status' => 'draft', 'name' => 'example']]` will generate `NOT ((status='draft') AND (name='example'))`.\n\n- `between`: operand 1 should be the column name, and operand 2 and 3 should be the\n   starting and ending values of the range that the column is in.\n   For example, `['between', 'id', 1, 10]` will generate `id BETWEEN 1 AND 10`.\n   In case you need to build a condition where value is between two columns (like `11 BETWEEN min_id AND max_id`), \n   you should use [[yii\\db\\conditions\\BetweenColumnsCondition|BetweenColumnsCondition]]. \n   See [Conditions – Object Format](#object-format) chapter to learn more about object definition of conditions.\n\n- `not between`: similar to `between` except the `BETWEEN` is replaced with `NOT BETWEEN`\n  in the generated condition.\n\n- `in`: operand 1 should be a column or DB expression. Operand 2 can be either an array or a `Query` object.\n  It will generate an `IN` condition. If Operand 2 is an array, it will represent the range of the values\n  that the column or DB expression should be; If Operand 2 is a `Query` object, a sub-query will be generated\n  and used as the range of the column or DB expression. For example,\n  `['in', 'id', [1, 2, 3]]` will generate `id IN (1, 2, 3)`.\n  The method will properly quote the column name and escape values in the range.\n  The `in` operator also supports composite columns. In this case, operand 1 should be an array of the columns,\n  while operand 2 should be an array of arrays or a `Query` object representing the range of the columns.\n  For example, `['in', ['id', 'name'], [['id' => 1, 'name' => 'oy']]]` will generate `(id, name) IN ((1, 'oy'))`.\n\n- `not in`: similar to the `in` operator except that `IN` is replaced with `NOT IN` in the generated condition.\n\n- `like`: operand 1 should be a column or DB expression, and operand 2 be a string or an array representing\n  the values that the column or DB expression should be like.\n  For example, `['like', 'name', 'tester']` will generate `name LIKE '%tester%'`.\n  When the value range is given as an array, multiple `LIKE` predicates will be generated and concatenated\n  using `AND`. For example, `['like', 'name', ['test', 'sample']]` will generate\n  `name LIKE '%test%' AND name LIKE '%sample%'`.\n  You may also provide an optional third operand to specify how to escape special characters in the values.\n  The operand should be an array of mappings from the special characters to their\n  escaped counterparts. If this operand is not provided, a default escape mapping will be used.\n  You may use `false` or an empty array to indicate the values are already escaped and no escape\n  should be applied. Note that when using an escape mapping (or the third operand is not provided),\n  the values will be automatically enclosed within a pair of percentage characters.\n\n  > Note: When using PostgreSQL you may also use [`ilike`](https://www.postgresql.org/docs/8.3/static/functions-matching.html#FUNCTIONS-LIKE)\n  > instead of `like` for case-insensitive matching.\n\n- `or like`: similar to the `like` operator except that `OR` is used to concatenate the `LIKE`\n  predicates when operand 2 is an array.\n\n- `not like`: similar to the `like` operator except that `LIKE` is replaced with `NOT LIKE`\n  in the generated condition.\n\n- `or not like`: similar to the `not like` operator except that `OR` is used to concatenate\n  the `NOT LIKE` predicates.\n\n- `exists`: requires one operand which must be an instance of [[yii\\db\\Query]] representing the sub-query.\n  It will build an `EXISTS (sub-query)` expression.\n\n- `not exists`: similar to the `exists` operator and builds a `NOT EXISTS (sub-query)` expression.\n\n- `>`, `<=`, or any other valid DB operator that takes two operands: the first operand must be a column name\n  while the second operand a value. For example, `['>', 'age', 10]` will generate `age>10`.\n\nUsing the Operator Format, Yii internally uses parameter binding for values, so in contrast to the [string format](#string-format),\nhere you do not have to add parameters manually. However, note that Yii never escapes column names, so if you pass\na variable as a column name, the application will likely become vulnerable to SQL injection attack. In order to keep\napplication secure, either do not use variables as column names or filter variable against allowlist.\nIn case you need to get column name from user, read the [Filtering Data](output-data-widgets.md#filtering-data)\nguide article. For example the following code is vulnerable:\n\n```php\n// Vulnerable code:\n$column = $request->get('column');\n$value = $request->get('value');\n$query->where(['=', $column, $value]);\n// $value is safe, but $column name won't be encoded!\n```\n\n#### Object Format <span id=\"object-format\"></span>\n\nObject Form is available since 2.0.14 and is both most powerful and most complex way to define conditions.\nYou need to follow it either if you want to build your own abstraction over query builder or if you want to implement\nyour own complex conditions.\n\nInstances of condition classes are immutable. Their only purpose is to store condition data and provide getters\nfor condition builders. Condition builder is a class that holds the logic that transforms data \nstored in condition into the SQL expression.\n\nInternally the formats described above are implicitly converted to object format prior to building raw SQL,\nso it is possible to combine formats in a single condition:\n\n```php\n$query->andWhere(new OrCondition([\n    new InCondition('type', 'in', $types),\n    ['like', 'name', '%good%'],\n    'disabled=false'\n]))\n```\n\nConversion from operator format into object format is performed according to\n[[yii\\db\\QueryBuilder::conditionClasses|QueryBuilder::conditionClasses]] property, that maps operators names\nto representative class names:\n\n- `AND`, `OR` -> `yii\\db\\conditions\\ConjunctionCondition`\n- `NOT` -> `yii\\db\\conditions\\NotCondition`\n- `IN`, `NOT IN` -> `yii\\db\\conditions\\InCondition`\n- `BETWEEN`, `NOT BETWEEN` -> `yii\\db\\conditions\\BetweenCondition`\n\nAnd so on.\n\nUsing the object format makes it possible to create your own conditions or to change the way default ones are built.\nSee [Adding Custom Conditions and Expressions](#adding-custom-conditions-and-expressions) chapter to learn more.\n\n\n#### Appending Conditions <span id=\"appending-conditions\"></span>\n\nYou can use [[yii\\db\\Query::andWhere()|andWhere()]] or [[yii\\db\\Query::orWhere()|orWhere()]] to append\nadditional conditions to an existing one. You can call them multiple times to append multiple conditions\nseparately. For example,\n\n```php\n$status = 10;\n$search = 'yii';\n\n$query->where(['status' => $status]);\n\nif (!empty($search)) {\n    $query->andWhere(['like', 'title', $search]);\n}\n```\n\nIf `$search` is not empty, the following `WHERE` condition will be generated:\n\n```sql\nWHERE (`status` = 10) AND (`title` LIKE '%yii%')\n```\n\n\n#### Filter Conditions <span id=\"filter-conditions\"></span>\n\nWhen building `WHERE` conditions based on input from end users, you usually want to ignore those input values, that are empty.\nFor example, in a search form that allows you to search by username and email, you would like to ignore the username/email\ncondition if the user does not enter anything in the username/email input field. You can achieve this goal by\nusing the [[yii\\db\\Query::filterWhere()|filterWhere()]] method:\n\n```php\n// $username and $email are from user inputs\n$query->filterWhere([\n    'username' => $username,\n    'email' => $email,\n]);\n```\n\nThe only difference between [[yii\\db\\Query::filterWhere()|filterWhere()]] and [[yii\\db\\Query::where()|where()]] \nis that the former will ignore empty values provided in the condition in [hash format](#hash-format). So if `$email`\nis empty while `$username` is not, the above code will result in the SQL condition `WHERE username=:username`.\n\n> Info: A value is considered empty if it is `null`, an empty array, an empty string or a string consisting of whitespaces only.\n\nLike [[yii\\db\\Query::andWhere()|andWhere()]] and [[yii\\db\\Query::orWhere()|orWhere()]], you can use\n[[yii\\db\\Query::andFilterWhere()|andFilterWhere()]] and [[yii\\db\\Query::orFilterWhere()|orFilterWhere()]]\nto append additional filter conditions to the existing one.\n\nAdditionally, there is [[yii\\db\\Query::andFilterCompare()]] that can intelligently determine operator based on what's\nin the value:\n\n```php\n$query->andFilterCompare('name', 'John Doe');\n$query->andFilterCompare('rating', '>9');\n$query->andFilterCompare('value', '<=100');\n```\n\nYou can also specify operator explicitly:\n\n```php\n$query->andFilterCompare('name', 'Doe', 'like');\n```\n\nSince Yii 2.0.11 there are similar methods for `HAVING` condition:\n\n- [[yii\\db\\Query::filterHaving()|filterHaving()]]\n- [[yii\\db\\Query::andFilterHaving()|andFilterHaving()]]\n- [[yii\\db\\Query::orFilterHaving()|orFilterHaving()]]\n\n### [[yii\\db\\Query::orderBy()|orderBy()]] <span id=\"order-by\"></span>\n\nThe [[yii\\db\\Query::orderBy()|orderBy()]] method specifies the `ORDER BY` fragment of a SQL query. For example,\n\n```php\n// ... ORDER BY `id` ASC, `name` DESC\n$query->orderBy([\n    'id' => SORT_ASC,\n    'name' => SORT_DESC,\n]);\n```\n \nIn the above code, the array keys are column names while the array values are the corresponding order by directions.\nThe PHP constant `SORT_ASC` specifies ascending sort and `SORT_DESC` descending sort.\n\nIf `ORDER BY` only involves simple column names, you can specify it using a string, just like you do when writing \nraw SQL statements. For example,\n\n```php\n$query->orderBy('id ASC, name DESC');\n```\n\n> Note: You should use the array format if `ORDER BY` involves some DB expression. \n\nYou can call [[yii\\db\\Query::addOrderBy()|addOrderBy()]] to add additional columns to the `ORDER BY` fragment.\nFor example,\n\n```php\n$query->orderBy('id ASC')\n    ->addOrderBy('name DESC');\n```\n\n\n### [[yii\\db\\Query::groupBy()|groupBy()]] <span id=\"group-by\"></span>\n\nThe [[yii\\db\\Query::groupBy()|groupBy()]] method specifies the `GROUP BY` fragment of a SQL query. For example,\n\n```php\n// ... GROUP BY `id`, `status`\n$query->groupBy(['id', 'status']);\n```\n\nIf `GROUP BY` only involves simple column names, you can specify it using a string, just like you do when writing \nraw SQL statements. For example,\n\n```php\n$query->groupBy('id, status');\n```\n\n> Note: You should use the array format if `GROUP BY` involves some DB expression.\n \nYou can call [[yii\\db\\Query::addGroupBy()|addGroupBy()]] to add additional columns to the `GROUP BY` fragment.\nFor example,\n\n```php\n$query->groupBy(['id', 'status'])\n    ->addGroupBy('age');\n```\n\n\n### [[yii\\db\\Query::having()|having()]] <span id=\"having\"></span>\n\nThe [[yii\\db\\Query::having()|having()]] method specifies the `HAVING` fragment of a SQL query. It takes\na condition which can be specified in the same way as that for [where()](#where). For example,\n\n```php\n// ... HAVING `status` = 1\n$query->having(['status' => 1]);\n```\n\nPlease refer to the documentation for [where()](#where) for more details about how to specify a condition.\n\nYou can call [[yii\\db\\Query::andHaving()|andHaving()]] or [[yii\\db\\Query::orHaving()|orHaving()]] to append\nadditional conditions to the `HAVING` fragment. For example,\n\n```php\n// ... HAVING (`status` = 1) AND (`age` > 30)\n$query->having(['status' => 1])\n    ->andHaving(['>', 'age', 30]);\n```\n\n\n### [[yii\\db\\Query::limit()|limit()]] and [[yii\\db\\Query::offset()|offset()]] <span id=\"limit-offset\"></span>\n\nThe [[yii\\db\\Query::limit()|limit()]] and [[yii\\db\\Query::offset()|offset()]] methods specify the `LIMIT`\nand `OFFSET` fragments of a SQL query. For example,\n\n```php\n// ... LIMIT 10 OFFSET 20\n$query->limit(10)->offset(20);\n```\n\nIf you specify an invalid limit or offset (e.g. a negative value), it will be ignored.\n\n> Info: For DBMS that do not support `LIMIT` and `OFFSET` (e.g. MSSQL), query builder will generate a SQL\n  statement that emulates the `LIMIT`/`OFFSET` behavior.\n\n\n### [[yii\\db\\Query::join()|join()]] <span id=\"join\"></span>\n\nThe [[yii\\db\\Query::join()|join()]] method specifies the `JOIN` fragment of a SQL query. For example,\n\n```php\n// ... LEFT JOIN `post` ON `post`.`user_id` = `user`.`id`\n$query->join('LEFT JOIN', 'post', 'post.user_id = user.id');\n```\n\nThe [[yii\\db\\Query::join()|join()]] method takes four parameters:\n\n- `$type`: join type, e.g., `'INNER JOIN'`, `'LEFT JOIN'`.\n- `$table`: the name of the table to be joined.\n- `$on`: optional, the join condition, i.e., the `ON` fragment. Please refer to [where()](#where) for details\n   about specifying a condition. Note, that the array syntax does **not** work for specifying a column based\n   condition, e.g. `['user.id' => 'comment.userId']` will result in a condition where the user id must be equal\n   to the string `'comment.userId'`. You should use the string syntax instead and specify the condition as\n   `'user.id = comment.userId'`.\n- `$params`: optional, the parameters to be bound to the join condition.\n\nYou can use the following shortcut methods to specify `INNER JOIN`, `LEFT JOIN` and `RIGHT JOIN`, respectively.\n\n- [[yii\\db\\Query::innerJoin()|innerJoin()]]\n- [[yii\\db\\Query::leftJoin()|leftJoin()]]\n- [[yii\\db\\Query::rightJoin()|rightJoin()]]\n\nFor example,\n\n```php\n$query->leftJoin('post', 'post.user_id = user.id');\n```\n\nTo join with multiple tables, call the above join methods multiple times, once for each table.\n\nBesides joining with tables, you can also join with sub-queries. To do so, specify the sub-queries to be joined\nas [[yii\\db\\Query]] objects. For example,\n\n```php\n$subQuery = (new \\yii\\db\\Query())->from('post');\n$query->leftJoin(['u' => $subQuery], 'u.id = author_id');\n```\n\nIn this case, you should put the sub-query in an array and use the array key to specify the alias.\n\n\n### [[yii\\db\\Query::union()|union()]] <span id=\"union\"></span>\n\nThe [[yii\\db\\Query::union()|union()]] method specifies the `UNION` fragment of a SQL query. For example,\n\n```php\n$query1 = (new \\yii\\db\\Query())\n    ->select(\"id, category_id AS type, name\")\n    ->from('post')\n    ->limit(10);\n\n$query2 = (new \\yii\\db\\Query())\n    ->select('id, type, name')\n    ->from('user')\n    ->limit(10);\n\n$query1->union($query2);\n```\n\nYou can call [[yii\\db\\Query::union()|union()]] multiple times to append more `UNION` fragments. \n\n### [[yii\\db\\Query::withQuery()|withQuery()]] <span id=\"with-query\"></span>\n\nThe [[yii\\db\\Query::withQuery()|withQuery()]] method specifies the `WITH` prefix of a SQL query. You can use it instead of subquery for more readability and some unique features (recursive CTE). Read more at [modern-sql](https://modern-sql.com/feature/with). For example, this query will select all nested permissions of `admin` with their children recursively,\n\n```php\n$initialQuery = (new \\yii\\db\\Query())\n    ->select(['parent', 'child'])\n    ->from(['aic' => 'auth_item_child'])\n    ->where(['parent' => 'admin']);\n\n$recursiveQuery = (new \\yii\\db\\Query())\n    ->select(['aic.parent', 'aic.child'])\n    ->from(['aic' => 'auth_item_child'])\n    ->innerJoin('t1', 't1.child = aic.parent');\n\n$mainQuery = (new \\yii\\db\\Query())\n    ->select(['parent', 'child'])\n    ->from('t1')\n    ->withQuery($initialQuery->union($recursiveQuery), 't1', true);\n```\n\n[[yii\\db\\Query::withQuery()|withQuery()]] can be called multiple times to prepend more CTE's to main query. Queries will be prepend in same order as they attached. If one of query is recursive then whole CTE become recursive.\n\n## Query Methods <span id=\"query-methods\"></span>\n\n[[yii\\db\\Query]] provides a whole set of methods for different query purposes:\n\n- [[yii\\db\\Query::all()|all()]]: returns an array of rows with each row being an associative array of name-value pairs.\n- [[yii\\db\\Query::one()|one()]]: returns the first row of the result.\n- [[yii\\db\\Query::column()|column()]]: returns the first column of the result.\n- [[yii\\db\\Query::scalar()|scalar()]]: returns a scalar value located at the first row and first column of the result.\n- [[yii\\db\\Query::exists()|exists()]]: returns a value indicating whether the query contains any result.\n- [[yii\\db\\Query::count()|count()]]: returns the result of a `COUNT` query.\n- Other aggregation query methods, including [[yii\\db\\Query::sum()|sum($q)]], [[yii\\db\\Query::average()|average($q)]],\n  [[yii\\db\\Query::max()|max($q)]], [[yii\\db\\Query::min()|min($q)]]. The `$q` parameter is mandatory for these methods \n  and can be either a column name or a DB expression.\n\nFor example,\n\n```php\n// SELECT `id`, `email` FROM `user`\n$rows = (new \\yii\\db\\Query())\n    ->select(['id', 'email'])\n    ->from('user')\n    ->all();\n    \n// SELECT * FROM `user` WHERE `username` LIKE `%test%`\n$row = (new \\yii\\db\\Query())\n    ->from('user')\n    ->where(['like', 'username', 'test'])\n    ->one();\n```\n\n> Note: The [[yii\\db\\Query::one()|one()]] method only returns the first row of the query result. It does NOT\n  add `LIMIT 1` to the generated SQL statement. This is fine and preferred if you know the query will return only one \n  or a few rows of data (e.g. if you are querying with some primary keys). However, if the query may potentially \n  result in many rows of data, you should call `limit(1)` explicitly to improve the performance, e.g.,\n  `(new \\yii\\db\\Query())->from('user')->limit(1)->one()`.\n\nAll these query methods take an optional `$db` parameter representing the [[yii\\db\\Connection|DB connection]] that\nshould be used to perform a DB query. If you omit this parameter, the `db` [application component](structure-application-components.md) will be used\nas the DB connection. Below is another example using the [[yii\\db\\Query::count()|count()]] query method:\n\n```php\n// executes SQL: SELECT COUNT(*) FROM `user` WHERE `last_name`=:last_name\n$count = (new \\yii\\db\\Query())\n    ->from('user')\n    ->where(['last_name' => 'Smith'])\n    ->count();\n```\n\nWhen you call a query method of [[yii\\db\\Query]], it actually does the following work internally:\n\n* Call [[yii\\db\\QueryBuilder]] to generate a SQL statement based on the current construct of [[yii\\db\\Query]];\n* Create a [[yii\\db\\Command]] object with the generated SQL statement;\n* Call a query method (e.g.  [[yii\\db\\Command::queryAll()|queryAll()]]) of [[yii\\db\\Command]] to execute the SQL statement and retrieve the data.\n\nSometimes, you may want to examine or use the SQL statement built from a [[yii\\db\\Query]] object. You can\nachieve this goal with the following code: \n\n```php\n$command = (new \\yii\\db\\Query())\n    ->select(['id', 'email'])\n    ->from('user')\n    ->where(['last_name' => 'Smith'])\n    ->limit(10)\n    ->createCommand();\n    \n// show the SQL statement\necho $command->sql;\n// show the parameters to be bound\nprint_r($command->params);\n\n// returns all rows of the query result\n$rows = $command->queryAll();\n```\n\n\n### Indexing Query Results <span id=\"indexing-query-results\"></span>\n\nWhen you call [[yii\\db\\Query::all()|all()]], it will return an array of rows which are indexed by consecutive integers.\nSometimes you may want to index them differently, such as indexing by a particular column or expression values.\nYou can achieve this goal by calling [[yii\\db\\Query::indexBy()|indexBy()]] before [[yii\\db\\Query::all()|all()]].\nFor example,\n\n```php\n// returns [100 => ['id' => 100, 'username' => '...', ...], 101 => [...], 103 => [...], ...]\n$query = (new \\yii\\db\\Query())\n    ->from('user')\n    ->limit(10)\n    ->indexBy('id')\n    ->all();\n```\n\nThe column which name is passed into [[yii\\db\\Query::indexBy()|indexBy()]] method must be present in the result set in order \nfor indexing to work - it is up to the developer to take care of it.\n\nTo index by expression values, pass an anonymous function to the [[yii\\db\\Query::indexBy()|indexBy()]] method:\n\n```php\n$query = (new \\yii\\db\\Query())\n    ->from('user')\n    ->indexBy(function ($row) {\n        return $row['id'] . $row['username'];\n    })->all();\n```\n\nThe anonymous function takes a parameter `$row` which contains the current row data and should return a scalar\nvalue which will be used as the index value for the current row.\n\n> Note: In contrast to query methods like [[yii\\db\\Query::groupBy()|groupBy()]] or [[yii\\db\\Query::orderBy()|orderBy()]]\n> which are converted to SQL and are part of the query, this method works after the data has been fetched from the database.\n> That means that only those column names can be used that have been part of SELECT in your query.\n> Also if you selected a column with table prefix, e.g. `customer.id`, the result set will only contain `id` so you have to call\n> `->indexBy('id')` without table prefix.\n\n\n### Batch Query <span id=\"batch-query\"></span>\n\nWhen working with large amounts of data, methods such as [[yii\\db\\Query::all()]] are not suitable\nbecause they require loading the whole query result into the client's memory. To solve this issue\nYii provides batch query support. The server holds the query result, and the client uses a cursor\nto iterate over the result set one batch at a time.\n\n> Warning: There are known limitations and workarounds for the MySQL implementation of batch queries. See below.\n\nBatch query can be used like the following:\n\n```php\nuse yii\\db\\Query;\n\n$query = (new Query())\n    ->from('user')\n    ->orderBy('id');\n\nforeach ($query->batch() as $users) {\n    // $users is an array of 100 or fewer rows from the user table\n}\n\n// or to iterate the row one by one\nforeach ($query->each() as $user) {\n    // data is being fetched from the server in batches of 100,\n    // but $user represents one row of data from the user table\n}\n```\n\nThe method [[yii\\db\\Query::batch()]] and [[yii\\db\\Query::each()]] return an [[yii\\db\\BatchQueryResult]] \nobject which implements the `Iterator` interface and thus can be used in the `foreach` construct.\nDuring the first iteration, a SQL query is made to the database. Data is then fetched in batches\nin the remaining iterations. By default, the batch size is 100, meaning 100 rows of data are being fetched in each batch.\nYou can change the batch size by passing the first parameter to the `batch()` or `each()` method.\n\nCompared to the [[yii\\db\\Query::all()]], the batch query only loads 100 rows of data at a time into the memory.\n\nIf you specify the query result to be indexed by some column via [[yii\\db\\Query::indexBy()]], \nthe batch query will still keep the proper index.\n\nFor example:\n\n```php\n$query = (new \\yii\\db\\Query())\n    ->from('user')\n    ->indexBy('username');\n\nforeach ($query->batch() as $users) {\n    // $users is indexed by the \"username\" column\n}\n\nforeach ($query->each() as $username => $user) {\n    // ...\n}\n```\n\n#### Limitations of batch query in MySQL <span id=\"batch-query-mysql\"></span>\n\nMySQL implementation of batch queries relies on the PDO driver library. By default, MySQL queries are \n[`buffered`](https://www.php.net/manual/en/mysqlinfo.concepts.buffering.php). This defeats the purpose \nof using the cursor to get the data, because it doesn't prevent the whole result set from being \nloaded into the client's memory by the driver.\n\n> Note: When `libmysqlclient` is used (typical of PHP5), PHP's memory limit won't count the memory \n  used for result sets. It may seem that batch queries work correctly, but in reality the whole \n  dataset is loaded into client's memory, and has the potential of using it up.\n\nTo disable buffering and reduce client memory requirements, PDO connection property \n`PDO::MYSQL_ATTR_USE_BUFFERED_QUERY` must be set to `false`. However, until the whole dataset has \nbeen retrieved, no other query can be made through the same connection. This may prevent `ActiveRecord` \nfrom making a query to get the table schema when it needs to. If this is not a problem \n(the table schema is cached already), it is possible to switch the original connection into \nunbuffered mode, and then roll back when the batch query is done.\n\n```php\nYii::$app->db->pdo->setAttribute(\\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);\n\n// Do batch query\n\nYii::$app->db->pdo->setAttribute(\\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);\n```\n\n> Note: In the case of MyISAM, for the duration of the batch query, the table may become locked, \n  delaying or denying write access for other connections. When using unbuffered queries, \n  try to keep the cursor open for as little time as possible.\n\nIf the schema is not cached, or it is necessary to run other queries while the batch query is \nbeing processed, you can create a separate unbuffered connection to the database:\n\n```php\n$unbufferedDb = new \\yii\\db\\Connection([\n    'dsn' => Yii::$app->db->dsn,\n    'username' => Yii::$app->db->username,\n    'password' => Yii::$app->db->password,\n    'charset' => Yii::$app->db->charset,\n]);\n$unbufferedDb->open();\n$unbufferedDb->pdo->setAttribute(\\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);\n```\n\nIf you want to ensure that the `$unbufferedDb` has exactly the same PDO attributes like the original \nbuffered `$db` but the `PDO::MYSQL_ATTR_USE_BUFFERED_QUERY` is `false`, \n[consider a deep copy of `$db`](https://github.com/yiisoft/yii2/issues/8420#issuecomment-301423833), \nset it to false manually.\n\nThen, queries are created normally. The new connection is used to run batch queries and retrieve \nresults either in batches or one by one:\n\n```php\n// getting data in batches of 1000\nforeach ($query->batch(1000, $unbufferedDb) as $users) {\n    // ...\n}\n\n\n// data is fetched from server in batches of 1000, but is iterated one by one \nforeach ($query->each(1000, $unbufferedDb) as $user) {\n    // ...\n}\n```\n\nWhen the connection is no longer necessary and the result set has been retrieved, it can be closed:\n\n```php\n$unbufferedDb->close();\n```\n\n> Note: unbuffered query uses less memory on the PHP-side, but can increase the load on the MySQL server. \nIt is recommended to design your own code with your production practice for extra massive data,\n[for example, divide the range for integer keys, loop them with Unbuffered Queries](https://github.com/yiisoft/yii2/issues/8420#issuecomment-296109257).\n\n### Adding custom Conditions and Expressions <span id=\"adding-custom-conditions-and-expressions\"></span>\n\nAs it was mentioned in [Conditions – Object Format](#object-format) chapter, it is possible to create custom condition\nclasses. For example, let's create a condition that will check that specific columns are less than some value.\nUsing the operator format, it would look like the following:\n\n```php\n[\n    'and',\n    ['>', 'posts', $minLimit],\n    ['>', 'comments', $minLimit],\n    ['>', 'reactions', $minLimit],\n    ['>', 'subscriptions', $minLimit]\n]\n```\n\nWhen such condition applied once, it is fine. In case it is used multiple times in a single query it can\nbe optimized a lot. Let's create a custom condition object to demonstrate it.\n\nYii has a [[yii\\db\\conditions\\ConditionInterface|ConditionInterface]], that must be used to mark classes, that represent\na condition. It requires `fromArrayDefinition()` method implementation, in order to make possible to create condition\nfrom array format. In case you don't need it, you can implement this method with exception throwing.\n\nSince we create our custom condition class, we can build API that suits our task the most. \n\n```php\nnamespace app\\db\\conditions;\n\nclass AllGreaterCondition implements \\yii\\db\\conditions\\ConditionInterface\n{\n    private $columns;\n    private $value;\n\n    /**\n     * @param string[] $columns Array of columns that must be greater, than $value\n     * @param mixed $value the value to compare each $column against.\n     */\n    public function __construct(array $columns, $value)\n    {\n        $this->columns = $columns;\n        $this->value = $value;\n    }\n    \n    public static function fromArrayDefinition($operator, $operands)\n    {\n        throw new InvalidArgumentException('Not implemented yet, but we will do it later');\n    }\n    \n    public function getColumns() { return $this->columns; }\n    public function getValue() { return $this->vaule; }\n}\n```\n\nSo we can create a condition object:\n\n```php\n$condition = new AllGreaterCondition(['col1', 'col2'], 42);\n```\n\nBut `QueryBuilder` still does not know, to make an SQL condition out of this object.\nNow we need to create a builder for this condition. It must implement [[yii\\db\\ExpressionBuilderInterface]]\nthat requires us to implement a `build()` method. \n\n```php\nnamespace app\\db\\conditions;\n\nclass AllGreaterConditionBuilder implements \\yii\\db\\ExpressionBuilderInterface\n{\n    use \\yii\\db\\ExpressionBuilderTrait; // Contains constructor and `queryBuilder` property.\n\n    /**\n     * @param ExpressionInterface $condition the condition to be built\n     * @param array $params the binding parameters.\n     * @return AllGreaterCondition\n     */ \n    public function build(ExpressionInterface $expression, array &$params = [])\n    {\n        $value = $condition->getValue();\n        \n        $conditions = [];\n        foreach ($expression->getColumns() as $column) {\n            $conditions[] = new SimpleCondition($column, '>', $value);\n        }\n\n        return $this->queryBuilder->buildCondition(new AndCondition($conditions), $params);\n    }\n}\n```\n\nThen simple let [[yii\\db\\QueryBuilder|QueryBuilder]] know about our new condition – add a mapping for it to \nthe `expressionBuilders` array. It could be done right from the application configuration:\n\n```php\n'db' => [\n    'class' => 'yii\\db\\mysql\\Connection',\n    // ...\n    'queryBuilder' => [\n        'expressionBuilders' => [\n            'app\\db\\conditions\\AllGreaterCondition' => 'app\\db\\conditions\\AllGreaterConditionBuilder',\n        ],\n    ],\n],\n```\n\nNow we can use our condition in `where()`:\n\n```php\n$query->andWhere(new AllGreaterCondition(['posts', 'comments', 'reactions', 'subscriptions'], $minValue));\n```\n\nIf we want to make it possible to create our custom condition using operator format, we should declare it in\n[[yii\\db\\QueryBuilder::conditionClasses|QueryBuilder::conditionClasses]]:\n\n```php\n'db' => [\n    'class' => 'yii\\db\\mysql\\Connection',\n    // ...\n    'queryBuilder' => [\n        'expressionBuilders' => [\n            'app\\db\\conditions\\AllGreaterCondition' => 'app\\db\\conditions\\AllGreaterConditionBuilder',\n        ],\n        'conditionClasses' => [\n            'ALL>' => 'app\\db\\conditions\\AllGreaterCondition',\n        ],\n    ],\n],\n```\n\nAnd create a real implementation of `AllGreaterCondition::fromArrayDefinition()` method \nin `app\\db\\conditions\\AllGreaterCondition`:\n\n```php\nnamespace app\\db\\conditions;\n\nclass AllGreaterCondition implements \\yii\\db\\conditions\\ConditionInterface\n{\n    // ... see the implementation above\n     \n    public static function fromArrayDefinition($operator, $operands)\n    {\n        return new static($operands[0], $operands[1]);\n    }\n}\n```\n    \nAfter that, we can create our custom condition using shorter operator format:\n\n```php\n$query->andWhere(['ALL>', ['posts', 'comments', 'reactions', 'subscriptions'], $minValue]);\n```\n\nYou might notice, that there was two concepts used: Expressions and Conditions. There is a [[yii\\db\\ExpressionInterface]]\nthat should be used to mark objects, that require an Expression Builder class, that implements\n[[yii\\db\\ExpressionBuilderInterface]] to be built. Also there is a [[yii\\db\\condition\\ConditionInterface]], that extends\n[[yii\\db\\ExpressionInterface|ExpressionInterface]] and should be used to objects, that can be created from array definition\nas it was shown above, but require builder as well.\n\nTo summarise:\n\n- Expression – is a Data Transfer Object (DTO) for a dataset, that can be somehow compiled to some SQL \nstatement (an operator, string, array, JSON, etc).\n- Condition – is an Expression superset, that aggregates multiple Expressions (or scalar values) that can be compiled\nto a single SQL condition.\n\nYou can create your own classes that implement [[yii\\db\\ExpressionInterface|ExpressionInterface]] to hide the complexity\nof transforming data to SQL statements. You will learn more about other examples of Expressions in the\n[next article](db-active-record.md);\n"
  },
  {
    "path": "docs/guide/glossary.md",
    "content": "# A\n\n## alias\n\nAlias is a string that's used by Yii to refer to the class or directory such as `@app/vendor`.\n\n## application\n\nThe application is the central object during HTTP request. It contains a number of components and with these is getting info from request and dispatching it to an appropriate controller for further processing.\n\nThe application object is instantiated as a singleton by the entry script. The application singleton can be accessed at any place via `\\Yii::$app`.\n\n## assets\n\nAsset refers to a resource file. Typically it contains JavaScript or CSS code but can be anything else that is accessed via HTTP.\n\n## attribute\n\nAn attribute is a model property (a class member variable or a magic property defined via `__get()`/`__set()`) that stores **business data**.\n\n# B\n\n## bundle\n\nBundle, known as package in Yii 1.1, refers to a number of assets and a configuration file that describes dependencies and lists assets.\n\n# C\n\n## configuration\n\nConfiguration may refer either to the process of setting properties of an object or to a configuration file that stores settings for an object or a class of objects.\n\n# E\n\n## extension\n\nExtension is a set of classes, asset bundles and configurations that adds more features to the application.\n\n# I\n\n## installation\n\nInstallation is a process of preparing something to work either by following a readme file or by executing specially prepared script. In case of Yii it's setting permissions and fulfilling software requirements.\n\n# M\n\n## module\n\nModule is a sub-application which contains MVC elements by itself, such as models, views, controllers, etc. and can be used withing the main application. Typically by forwarding requests to the module instead of handling it via controllers.\n\n# N\n\n## namespace\n\nNamespace refers to a [PHP language feature](https://www.php.net/manual/en/language.namespaces.php) which is actively used in Yii 2.\n\n# P\n\n## package\n\n[See bundle](#bundle).\n\n# V\n\n## vendor\n\nVendor is an organization or individual developer providing code in form of extensions, modules or libraries.\n"
  },
  {
    "path": "docs/guide/helper-array.md",
    "content": "ArrayHelper\n===========\n\nAdditionally to the [rich set of PHP array functions](https://www.php.net/manual/en/book.array.php), the Yii array helper provides\nextra static methods allowing you to deal with arrays more efficiently.\n\n\n## Getting Values <span id=\"getting-values\"></span>\n\nRetrieving values from an array, an object or a complex structure consisting of both using standard PHP is quite\nrepetitive. You have to check if key exists with `isset` first, then if it does you're getting it, if not,\nproviding default value:\n\n```php\nclass User\n{\n    public $name = 'Alex';\n}\n\n$array = [\n    'foo' => [\n        'bar' => new User(),\n    ]\n];\n\n$value = isset($array['foo']['bar']->name) ? $array['foo']['bar']->name : null;\n```\n\nYii provides a very convenient method to do it:\n\n```php\n$value = ArrayHelper::getValue($array, 'foo.bar.name');\n```\n\nFirst method argument is where we're getting value from. Second argument specifies how to get the data. It could be one\nof the following:\n\n- Name of array key or object property to retrieve value from.\n- Set of dot separated array keys or object property names. The one we've used in the example above.\n- A callback returning a value.\n\nThe callback should be the following:\n\n```php\n$fullName = ArrayHelper::getValue($user, function ($user, $defaultValue) {\n    return $user->firstName . ' ' . $user->lastName;\n});\n```\n\nThird optional argument is default value which is `null` if not specified. Could be used as follows:\n\n```php\n$username = ArrayHelper::getValue($comment, 'user.username', 'Unknown');\n```\n\n\n## Setting values <span id=\"setting-values\"></span>\n\n```php\n$array = [\n    'key' => [\n        'in' => ['k' => 'value']\n    ]\n];\n\nArrayHelper::setValue($array, 'key.in', ['arr' => 'val']);\n// the path to write the value in `$array` can be specified as an array\nArrayHelper::setValue($array, ['key', 'in'], ['arr' => 'val']);\n```\n\nAs a result, initial value of `$array['key']['in']` will be overwritten by new value\n\n```php\n[\n    'key' => [\n        'in' => ['arr' => 'val']\n    ]\n]\n```\n\nIf the path contains a nonexistent key, it will be created\n\n```php\n// if `$array['key']['in']['arr0']` is not empty, the value will be added to the array\nArrayHelper::setValue($array, 'key.in.arr0.arr1', 'val');\n\n// if you want to completely override the value `$array['key']['in']['arr0']`\nArrayHelper::setValue($array, 'key.in.arr0', ['arr1' => 'val']);\n```\n\nThe result will be\n\n```php\n[\n    'key' => [\n        'in' => [\n            'k' => 'value',\n            'arr0' => ['arr1' => 'val']\n        ]\n    ]\n]\n```\n\n\n## Take a value from an array <span id=\"removing-values\"></span>\n\nIn case you want to get a value and then immediately remove it from an array you can use `remove` method:\n\n```php\n$array = ['type' => 'A', 'options' => [1, 2]];\n$type = ArrayHelper::remove($array, 'type');\n```\n\nAfter executing the code `$array` will contain `['options' => [1, 2]]` and `$type` will be `A`. Note that unlike\n`getValue` method, `remove` supports simple key names only.\n\n\n## Checking Existence of Keys <span id=\"checking-existence-of-keys\"></span>\n\n`ArrayHelper::keyExists` works the same way as [array_key_exists](https://www.php.net/manual/en/function.array-key-exists.php)\nexcept that it also supports case-insensitive key comparison. For example,\n\n```php\n$data1 = [\n    'userName' => 'Alex',\n];\n\n$data2 = [\n    'username' => 'Carsten',\n];\n\nif (!ArrayHelper::keyExists('username', $data1, false) || !ArrayHelper::keyExists('username', $data2, false)) {\n    echo \"Please provide username.\";\n}\n```\n\n## Retrieving Columns <span id=\"retrieving-columns\"></span>\n\nOften you need to get a column of values from array of data rows or objects. Common example is getting a list of IDs.\n\n```php\n$array = [\n    ['id' => '123', 'data' => 'abc'],\n    ['id' => '345', 'data' => 'def'],\n];\n$ids = ArrayHelper::getColumn($array, 'id');\n```\n\nThe result will be `['123', '345']`.\n\nIf additional transformations are required or the way of getting value is complex, second argument could be specified\nas an anonymous function:\n\n```php\n$result = ArrayHelper::getColumn($array, function ($element) {\n    return $element['id'];\n});\n```\n\n\n## Re-indexing Arrays <span id=\"reindexing-arrays\"></span>\n\nIn order to index an array according to a specified key, the `index` method can be used. The input should be either\nmultidimensional array or an array of objects. The `$key` can be either a key name of the sub-array, a property name of\nobject, or an anonymous function that must return the value that will be used as a key.\n\nThe `$groups` attribute is an array of keys, that will be used to group the input array into one or more sub-arrays\nbased on keys specified.\n\nIf the `$key` attribute or its value for the particular element is `null` and `$groups` is not defined, the array\nelement will be discarded. Otherwise, if `$groups` is specified, array element will be added to the result array\nwithout any key.\n\nFor example:\n\n```php\n$array = [\n    ['id' => '123', 'data' => 'abc', 'device' => 'laptop'],\n    ['id' => '345', 'data' => 'def', 'device' => 'tablet'],\n    ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone'],\n];\n$result = ArrayHelper::index($array, 'id');\n```\n\nThe result will be an associative array, where the key is the value of `id` attribute:\n\n```php\n[\n    '123' => ['id' => '123', 'data' => 'abc', 'device' => 'laptop'],\n    '345' => ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone']\n    // The second element of an original array is overwritten by the last element because of the same id\n]\n```\n\nAnonymous function, passed as a `$key`, gives the same result:\n\n```php\n$result = ArrayHelper::index($array, function ($element) {\n    return $element['id'];\n});\n```\n\nPassing `id` as a third argument will group `$array` by `id`:\n\n```php\n$result = ArrayHelper::index($array, null, 'id');\n```\n\nThe result will be a multidimensional array grouped by `id` on the first level and not indexed on the second level:\n\n```php\n[\n    '123' => [\n        ['id' => '123', 'data' => 'abc', 'device' => 'laptop']\n    ],\n    '345' => [ // all elements with this index are present in the result array\n        ['id' => '345', 'data' => 'def', 'device' => 'tablet'],\n        ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone'],\n    ]\n]\n```\n\nAn anonymous function can be used in the grouping array as well:\n\n```php\n$result = ArrayHelper::index($array, 'data', [function ($element) {\n    return $element['id'];\n}, 'device']);\n```\n\nThe result will be a multidimensional array grouped by `id` on the first level, by `device` on the second level and\nindexed by `data` on the third level:\n\n```php\n[\n    '123' => [\n        'laptop' => [\n            'abc' => ['id' => '123', 'data' => 'abc', 'device' => 'laptop']\n        ]\n    ],\n    '345' => [\n        'tablet' => [\n            'def' => ['id' => '345', 'data' => 'def', 'device' => 'tablet']\n        ],\n        'smartphone' => [\n            'hgi' => ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone']\n        ]\n    ]\n]\n```\n\n## Building Maps <span id=\"building-maps\"></span>\n\nIn order to build a map (key-value pairs) from a multidimensional array or an array of objects you can use `map` method.\nThe `$from` and `$to` parameters specify the key names or property names to set up the map. Optionally, one can further\ngroup the map according to a grouping field `$group`. For example,\n\n```php\n$array = [\n    ['id' => '123', 'name' => 'aaa', 'class' => 'x'],\n    ['id' => '124', 'name' => 'bbb', 'class' => 'x'],\n    ['id' => '345', 'name' => 'ccc', 'class' => 'y'],\n];\n\n$result = ArrayHelper::map($array, 'id', 'name');\n// the result is:\n// [\n//     '123' => 'aaa',\n//     '124' => 'bbb',\n//     '345' => 'ccc',\n// ]\n\n$result = ArrayHelper::map($array, 'id', 'name', 'class');\n// the result is:\n// [\n//     'x' => [\n//         '123' => 'aaa',\n//         '124' => 'bbb',\n//     ],\n//     'y' => [\n//         '345' => 'ccc',\n//     ],\n// ]\n```\n\n\n## Multidimensional Sorting <span id=\"multidimensional-sorting\"></span>\n\n`multisort` method helps to sort an array of objects or nested arrays by one or several keys. For example,\n\n```php\n$data = [\n    ['age' => 30, 'name' => 'Alexander'],\n    ['age' => 30, 'name' => 'Brian'],\n    ['age' => 19, 'name' => 'Barney'],\n];\nArrayHelper::multisort($data, ['age', 'name'], [SORT_ASC, SORT_DESC]);\n```\n\nAfter sorting we'll get the following in `$data`:\n\n```php\n[\n    ['age' => 19, 'name' => 'Barney'],\n    ['age' => 30, 'name' => 'Brian'],\n    ['age' => 30, 'name' => 'Alexander'],\n];\n```\n\nSecond argument that specifies keys to sort by can be a string if it's a single key, an array in case of multiple keys\nor an anonymous function like the following one:\n\n```php\nArrayHelper::multisort($data, function($item) {\n    // sort by age if it exists or by name otherwise\n    return isset($item['age']) ? $item['age'] : $item['name'];\n});\n```\n\nThird argument is direction. In case of sorting by a single key it could be either `SORT_ASC` or\n`SORT_DESC`. If sorting by multiple values you can sort each value differently by providing an array of\nsort direction.\n\nLast argument is PHP sort flag that could take the same values as the ones passed to\nPHP [sort()](https://www.php.net/manual/en/function.sort.php).\n\n\n## Detecting Array Types <span id=\"detecting-array-types\"></span>\n\nIt is handy to know whether an array is indexed or an associative. Here's an example:\n\n```php\n// no keys specified\n$indexed = ['Qiang', 'Paul'];\necho ArrayHelper::isIndexed($indexed);\n\n// all keys are strings\n$associative = ['framework' => 'Yii', 'version' => '2.0'];\necho ArrayHelper::isAssociative($associative);\n```\n\n\n## HTML Encoding and Decoding Values <span id=\"html-encoding-values\"></span>\n\nIn order to encode or decode special characters in an array of strings into HTML entities you can use the following:\n\n```php\n$encoded = ArrayHelper::htmlEncode($data);\n$decoded = ArrayHelper::htmlDecode($data);\n```\n\nOnly values will be encoded by default. By passing second argument as `false` you can encode array's keys as well.\nEncoding will use application charset and could be changed via third argument.\n\n\n## Merging Arrays <span id=\"merging-arrays\"></span>\n\nYou can use [[yii\\helpers\\ArrayHelper::merge()|ArrayHelper::merge()]] to merge two or more arrays into one recursively.\nIf each array has an element with the same string key value, the latter will overwrite the former\n(different from [array_merge_recursive()](https://www.php.net/manual/en/function.array-merge-recursive.php)).\nRecursive merging will be conducted if both arrays have an element of array type and are having the same key.\nFor integer-keyed elements, the elements from the latter array will be appended to the former array.\nYou can use [[yii\\helpers\\UnsetArrayValue]] object to unset value from previous array or\n[[yii\\helpers\\ReplaceArrayValue]] to force replace former value instead of recursive merging.\n\nFor example:\n\n```php\n$array1 = [\n    'name' => 'Yii',\n    'version' => '1.1',\n    'ids' => [\n        1,\n    ],\n    'validDomains' => [\n        'example.com',\n        'www.example.com',\n    ],\n    'emails' => [\n        'admin' => 'admin@example.com',\n        'dev' => 'dev@example.com',\n    ],\n];\n\n$array2 = [\n    'version' => '2.0',\n    'ids' => [\n        2,\n    ],\n    'validDomains' => new \\yii\\helpers\\ReplaceArrayValue([\n        'yiiframework.com',\n        'www.yiiframework.com',\n    ]),\n    'emails' => [\n        'dev' => new \\yii\\helpers\\UnsetArrayValue(),\n    ],\n];\n\n$result = ArrayHelper::merge($array1, $array2);\n```\n\nThe result will be:\n\n```php\n[\n    'name' => 'Yii',\n    'version' => '2.0',\n    'ids' => [\n        1,\n        2,\n    ],\n    'validDomains' => [\n        'yiiframework.com',\n        'www.yiiframework.com',\n    ],\n    'emails' => [\n        'admin' => 'admin@example.com',\n    ],\n]\n```\n\n\n## Converting Objects to Arrays <span id=\"converting-objects-to-arrays\"></span>\n\nOften you need to convert an object or an array of objects into an array. The most common case is converting active record\nmodels in order to serve data arrays via REST API or use it otherwise. The following code could be used to do it:\n\n```php\n$posts = Post::find()->limit(10)->all();\n$data = ArrayHelper::toArray($posts, [\n    'app\\models\\Post' => [\n        'id',\n        'title',\n        // the key name in array result => property name\n        'createTime' => 'created_at',\n        // the key name in array result => anonymous function\n        'length' => function ($post) {\n            return strlen($post->content);\n        },\n    ],\n]);\n```\n\nThe first argument contains the data we want to convert. In our case we're converting a `Post` AR model.\n\nThe second argument is conversion mapping per class. We're setting a mapping for `Post` model.\nEach mapping array contains a set of mappings. Each mapping could be:\n\n- A field name to include as is.\n- A key-value pair of desired array key name and model column name to take value from.\n- A key-value pair of desired array key name and a callback which returns value.\n\nThe result of conversion above for single model will be:\n\n\n```php\n[\n    'id' => 123,\n    'title' => 'test',\n    'createTime' => '2013-01-01 12:00AM',\n    'length' => 301,\n]\n```\n\nIt is possible to provide default way of converting object to array for a specific class by implementing\n[[yii\\base\\Arrayable|Arrayable]] interface in that class.\n\n## Testing against Arrays <span id=\"testing-arrays\"></span>\n\nOften you need to check if an element is in an array or a set of elements is a subset of another.\nWhile PHP offers `in_array()`, this does not support subsets or `\\Traversable` objects.\n\nTo aid these kinds of tests, [[yii\\helpers\\ArrayHelper]] provides [[yii\\helpers\\ArrayHelper::isIn()|isIn()]]\nand [[yii\\helpers\\ArrayHelper::isSubset()|isSubset()]] with the same signature as\n[in_array()](https://www.php.net/manual/en/function.in-array.php).\n\n```php\n// true\nArrayHelper::isIn('a', ['a']);\n// true\nArrayHelper::isIn('a', new ArrayObject(['a']));\n\n// true \nArrayHelper::isSubset(new ArrayObject(['a', 'c']), new ArrayObject(['a', 'b', 'c']));\n```\n\n## Flattening Arrays <span id=\"flattening-arrays\"></span>\n\nThe `ArrayHelper::flatten()` method allows you to convert a multi-dimensional array into a single-dimensional array by concatenating keys.\n\n### Basic Usage\n\nTo flatten a nested array, simply pass the array to the `flatten()` method:\n\n```php\n$array = [\n    'a' => [\n        'b' => [\n            'c' => 1,\n            'd' => 2,\n        ],\n        'e' => 3,\n    ],\n    'f' => 4,\n];\n\n$flattenedArray = ArrayHelper::flatten($array);\n// Result:\n// [\n//     'a.b.c' => 1,\n//     'a.b.d' => 2,\n//     'a.e' => 3,\n//     'f' => 4,\n// ]\n```\n\n### Custom Separator\n\nYou can specify a custom separator to use when concatenating keys:\n\n```php\n$array = [\n    'a' => [\n        'b' => [\n            'c' => 1,\n            'd' => 2,\n        ],\n        'e' => 3,\n    ],\n    'f' => 4,\n];\n\n$flattenedArray = ArrayHelper::flatten($array, '_');\n// Result:\n// [\n//     'a_b_c' => 1,\n//     'a_b_d' => 2,\n//     'a_e' => 3,\n//     'f' => 4,\n// ]\n```\n\n### Handling Special Characters in Keys\n\nThe `flatten()` method can handle keys with special characters:\n\n```php\n$array = [\n    'a.b' => [\n        'c.d' => 1,\n    ],\n    'e.f' => 2,\n];\n\n$flattenedArray = ArrayHelper::flatten($array);\n// Result:\n// [\n//     'a.b.c.d' => 1,\n//     'e.f' => 2,\n// ]\n```\n\n### Mixed Data Types\n\nThe `flatten()` method works with arrays containing different data types:\n\n```php\n$array = [\n    'a' => [\n        'b' => 'string',\n        'c' => 123,\n        'd' => true,\n        'e' => null,\n    ],\n    'f' => [1, 2, 3],\n];\n\n$flattenedArray = ArrayHelper::flatten($array);\n// Result:\n// [\n//     'a.b' => 'string',\n//     'a.c' => 123,\n//     'a.d' => true,\n//     'a.e' => null,\n//     'f.0' => 1,\n//     'f.1' => 2,\n//     'f.2' => 3,\n// ]\n```\n\n### Edge Cases\n\nThe `flatten()` method handles various edge cases, such as empty arrays and non-array values:\n\n```php\n// Empty array\n$array = [];\n$flattenedArray = ArrayHelper::flatten($array);\n// Result: []\n\n// Non-array value\n$array = 'string';\n$flattenedArray = ArrayHelper::flatten($array);\n// Result:\n// yii\\base\\InvalidArgumentException: Argument $array must be an array or implement Traversable\n```\n\n### Key Collisions\n\nWhen keys collide, the `flatten()` method will overwrite the previous value:\n\n```php\n$array = [\n    'a' => [\n        'b' => 1,\n    ],\n    'a.b' => 2,\n];\n\n$flattenedArray = ArrayHelper::flatten($array);\n// Result: ['a.b' => 2]\n```\n"
  },
  {
    "path": "docs/guide/helper-html.md",
    "content": "Html helper\n===========\n\nEvery web application generates lots of HTML markup. If the markup is static, it can be done efficiently by\n[mixing PHP and HTML in a single file](https://www.php.net/manual/en/language.basic-syntax.phpmode.php), but when it is\ngenerated dynamically it starts to get tricky to handle it without extra help. Yii provides such help in the form\nof an Html helper, which provides a set of static methods for handling commonly used HTML tags, their options, and their content.\n\n> Note: If your markup is nearly static, it's better to use HTML directly. There's no need to wrap absolutely everything\n  in Html helper calls.\n\n\n## Basics <span id=\"basics\"></span>\n\nSince building dynamic HTML by string concatenation can get messy very fast, Yii provides a set of methods to\nmanipulate tag options and build tags based on these options.\n\n\n### Generating Tags <span id=\"generating-tags\"></span>\n\nThe code for generating a tag looks like the following:\n\n```php\n<?= Html::tag('p', Html::encode($user->name), ['class' => 'username']) ?>\n```\n\nThe first argument is the tag name. The second one is the content to be enclosed between the start and end tags.\nNote that we are using `Html::encode` &mdash; that's because the content isn't encoded automatically to allow using HTML when needed. \nThe third one is an array of HTML options, or in other words, tag attributes. \nIn this array the key is the name of the attribute (such as `class`, `href` or `target`), and the value is its value.\n\nThe code above will generate the following HTML:\n\n```html\n<p class=\"username\">samdark</p>\n```\n\nIn case you need just an opening or closing tag, you can use the `Html::beginTag()` and `Html::endTag()` methods.\n\nOptions are used in many methods of the Html helper and various widgets. In all these cases there is some extra handling to\nknow about:\n\n- If a value is `null`, the corresponding attribute will not be rendered.\n- Attributes whose values are of boolean type will be treated as\n  [boolean attributes](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#boolean-attributess).\n- The values of attributes will be HTML-encoded using [[yii\\helpers\\Html::encode()|Html::encode()]].\n- If the value of an attribute is an array, it will be handled as follows:\n \n   * If the attribute is a data attribute as listed in [[yii\\helpers\\Html::$dataAttributes]], such as `data` or `ng`,\n     a list of attributes will be rendered, one for each element in the value array. For example,\n     `'data' => ['id' => 1, 'name' => 'yii']` generates `data-id=\"1\" data-name=\"yii\"`; and \n     `'data' => ['params' => ['id' => 1, 'name' => 'yii'], 'status' => 'ok']` generates\n     `data-params='{\"id\":1,\"name\":\"yii\"}' data-status=\"ok\"`. Note that in the latter example JSON format is used\n     to render a sub-array.\n   * If the attribute is NOT a data attribute, the value will be JSON-encoded. For example,\n     `['params' => ['id' => 1, 'name' => 'yii']` generates `params='{\"id\":1,\"name\":\"yii\"}'`.\n\n\n### Forming CSS Classes and Styles <span id=\"forming-css\"></span>\n\nWhen building options for HTML tags we often start with defaults which we need to modify. In order to add or\nremove a CSS class you can use the following:\n\n```php\n$options = ['class' => 'btn btn-default'];\n\nif ($type === 'success') {\n    Html::removeCssClass($options, 'btn-default');\n    Html::addCssClass($options, 'btn-success');\n}\n\necho Html::tag('div', 'Pwede na', $options);\n\n// if the value of $type is 'success' it will render\n// <div class=\"btn btn-success\">Pwede na</div>\n```\n\nYou may specify multiple CSS classes using the array style as well:\n\n```php\n$options = ['class' => ['btn', 'btn-default']];\n\necho Html::tag('div', 'Save', $options);\n// renders '<div class=\"btn btn-default\">Save</div>'\n```\n\nYou may also use the array style when adding or removing classes:\n\n```php\n$options = ['class' => 'btn'];\n\nif ($type === 'success') {\n    Html::addCssClass($options, ['btn-success', 'btn-lg']);\n}\n\necho Html::tag('div', 'Save', $options);\n// renders '<div class=\"btn btn-success btn-lg\">Save</div>'\n```\n\n`Html::addCssClass()` prevents duplication, so you don't need to worry about the same class appearing twice:\n\n```php\n$options = ['class' => 'btn btn-default'];\n\nHtml::addCssClass($options, 'btn-default'); // class 'btn-default' is already present\n\necho Html::tag('div', 'Save', $options);\n// renders '<div class=\"btn btn-default\">Save</div>'\n```\n\nIf the CSS class option is specified using the array style, you may use a named key to mark the logical purpose of the class.\nIn this case, a class with the same key in the array style will be ignored in `Html::addCssClass()`:\n\n```php\n$options = [\n    'class' => [\n        'btn',\n        'theme' => 'btn-default',\n    ]\n];\n\nHtml::addCssClass($options, ['theme' => 'btn-success']); // 'theme' key is already taken\n\necho Html::tag('div', 'Save', $options);\n// renders '<div class=\"btn btn-default\">Save</div>'\n```\n\nCSS styles can be set up in similar way using the `style` attribute:\n\n```php\n$options = ['style' => ['width' => '100px', 'height' => '100px']];\n\n// gives style=\"width: 100px; height: 200px; position: absolute;\"\nHtml::addCssStyle($options, 'height: 200px; position: absolute;');\n\n// gives style=\"position: absolute;\"\nHtml::removeCssStyle($options, ['width', 'height']);\n```\n\nWhen using [[yii\\helpers\\Html::addCssStyle()|addCssStyle()]], you can specify either an array of key-value pairs,\ncorresponding to CSS property names and values, or a string such as `width: 100px; height: 200px;`. These formats\ncan be converted from one to the other using [[yii\\helpers\\Html::cssStyleFromArray()|cssStyleFromArray()]] and\n[[yii\\helpers\\Html::cssStyleToArray()|cssStyleToArray()]]. The [[yii\\helpers\\Html::removeCssStyle()|removeCssStyle()]]\nmethod accepts an array of properties to remove. If it's a single property, it can be specified as a string.\n\n\n### Encoding and Decoding Content <span id=\"encoding-and-decoding-content\"></span>\n\nIn order for content to be displayed properly and securely in HTML, special characters in the content should be encoded.\nIn PHP this is done with [htmlspecialchars](https://www.php.net/manual/en/function.htmlspecialchars.php) and\n[htmlspecialchars_decode](https://www.php.net/manual/en/function.htmlspecialchars-decode.php). The issue with using\nthese methods directly is that you have to specify encoding and extra flags all the time. Since these flags are the same\nall the time and the encoding should match the one of the application in order to prevent security issues, Yii provides two\ncompact and simple-to-use methods:\n\n```php\n$userName = Html::encode($user->name);\necho $userName;\n\n$decodedUserName = Html::decode($userName);\n```\n\n\n## Forms <span id=\"forms\"></span>\n\nDealing with form markup is quite repetitive and error prone. Because of that, there is a group of methods to help\ndealing with them.\n\n> Note: consider using [[yii\\widgets\\ActiveForm|ActiveForm]] in case you're dealing with models and need validation.\n\n\n### Creating Forms <span id=\"creating-forms\"></span>\n\nForms can be opened with [[yii\\helpers\\Html::beginForm()|beginForm()]] method like the following:\n\n```php\n<?= Html::beginForm(['order/update', 'id' => $id], 'post', ['enctype' => 'multipart/form-data']) ?>\n```\n\nThe first argument is the URL the form will be submitted to. It can be specified in the form of a Yii route and parameters accepted by [[yii\\helpers\\Url::to()|Url::to()]].\nThe second one is the method to use. `post` is the default. The third one is an array of options\nfor the form tag. In this case we're changing the encoding of the form data in the POST request to `multipart/form-data`,\nwhich is required in order to upload files.\n\nClosing the form tag is simple:\n\n```php\n<?= Html::endForm() ?>\n```\n\n\n### Buttons <span id=\"buttons\"></span>\n\nIn order to generate buttons, you can use the following code:\n\n```php\n<?= Html::button('Press me!', ['class' => 'teaser']) ?>\n<?= Html::submitButton('Submit', ['class' => 'submit']) ?>\n<?= Html::resetButton('Reset', ['class' => 'reset']) ?>\n```\n\nThe first argument for all three methods is the button title, and the second one is an array of options.\nThe title isn't encoded, so if you're displaying data from the end user, encode it with [[yii\\helpers\\Html::encode()|Html::encode()]].\n\n\n### Input Fields <span id=\"input-fields\"></span>\n\nThere are two groups of input methods. The ones starting with `active`, which are called active inputs, and the ones not starting with it.\nActive inputs take data from the model and attribute specified, while in the case of a regular input, data is specified\ndirectly.\n\nThe most generic methods are:\n\n```php\ntype, input name, input value, options\n<?= Html::input('text', 'username', $user->name, ['class' => $username]) ?>\n\ntype, model, model attribute name, options\n<?= Html::activeInput('text', $user, 'name', ['class' => $username]) ?>\n```\n\nIf you know the input type in advance, it's more convenient to use the shortcut methods:\n\n- [[yii\\helpers\\Html::buttonInput()]]\n- [[yii\\helpers\\Html::submitInput()]]\n- [[yii\\helpers\\Html::resetInput()]]\n- [[yii\\helpers\\Html::textInput()]], [[yii\\helpers\\Html::activeTextInput()]]\n- [[yii\\helpers\\Html::hiddenInput()]], [[yii\\helpers\\Html::activeHiddenInput()]]\n- [[yii\\helpers\\Html::passwordInput()]] / [[yii\\helpers\\Html::activePasswordInput()]]\n- [[yii\\helpers\\Html::fileInput()]], [[yii\\helpers\\Html::activeFileInput()]]\n- [[yii\\helpers\\Html::textarea()]], [[yii\\helpers\\Html::activeTextarea()]]\n\nRadios and checkboxes are a bit different in terms of method signature:\n\n```php\n<?= Html::radio('agree', true, ['label' => 'I agree']) ?>\n<?= Html::activeRadio($model, 'agree', ['class' => 'agreement']) ?>\n\n<?= Html::checkbox('agree', true, ['label' => 'I agree']) ?>\n<?= Html::activeCheckbox($model, 'agree', ['class' => 'agreement']) ?>\n```\n\nDropdown lists and list boxes can be rendered like the following:\n\n```php\n<?= Html::dropDownList('list', $currentUserId, ArrayHelper::map($userModels, 'id', 'name')) ?>\n<?= Html::activeDropDownList($users, 'id', ArrayHelper::map($userModels, 'id', 'name')) ?>\n\n<?= Html::listBox('list', $currentUserId, ArrayHelper::map($userModels, 'id', 'name')) ?>\n<?= Html::activeListBox($users, 'id', ArrayHelper::map($userModels, 'id', 'name')) ?>\n```\n\nThe first argument is the name of the input, the second one is the value that's currently selected, and the third one is an array of key-value pairs, where the array key is the list value and the array value is the list label.\n\nIf you want multiple choices to be selectable, you can use a checkbox list:\n\n```php\n<?= Html::checkboxList('roles', [16, 42], ArrayHelper::map($roleModels, 'id', 'name')) ?>\n<?= Html::activeCheckboxList($user, 'role', ArrayHelper::map($roleModels, 'id', 'name')) ?>\n```\n\nIf not, use radio list:\n\n```php\n<?= Html::radioList('roles', [16, 42], ArrayHelper::map($roleModels, 'id', 'name')) ?>\n<?= Html::activeRadioList($user, 'role', ArrayHelper::map($roleModels, 'id', 'name')) ?>\n```\n\n\n### Labels and Errors <span id=\"labels-and-errors\"></span>\n\nSame as inputs, there are two methods for generating form labels. Active, which takes data from the model, and non-active, which accepts data directly:\n\n```php\n<?= Html::label('User name', 'username', ['class' => 'label username']) ?>\n<?= Html::activeLabel($user, 'username', ['class' => 'label username']) ?>\n```\n\nIn order to display form errors from a model or models as a summary, you could use:\n\n```php\n<?= Html::errorSummary($posts, ['class' => 'errors']) ?>\n```\n\nTo display an individual error:\n\n```php\n<?= Html::error($post, 'title', ['class' => 'error']) ?>\n```\n\n\n### Input Names and Values <span id=\"input-names-and-values\"></span>\n\nThere are methods to get names, ids and values for input fields based on the model. These are mainly used internally,\nbut could be handy sometimes:\n\n```php\n// Post[title]\necho Html::getInputName($post, 'title');\n\n// post-title\necho Html::getInputId($post, 'title');\n\n// my first post\necho Html::getAttributeValue($post, 'title');\n\n// $post->authors[0]\necho Html::getAttributeValue($post, '[0]authors[0]');\n```\n\nIn the above, the first argument is the model, while the second one is the attribute expression. In its simplest form the expression is just an attribute name, but it can be an attribute name prefixed and/or suffixed with array indexes, which is mainly used for tabular input:\n\n- `[0]content` is used in tabular data input to represent the `content` attribute for the first model in tabular input;\n- `dates[0]` represents the first array element of the `dates` attribute;\n- `[0]dates[0]` represents the first array element of the `dates` attribute for the first model in tabular input.\n\nIn order to get the attribute name without suffixes or prefixes, one can use the following:\n\n```php\n// dates\necho Html::getAttributeName('dates[0]');\n```\n\n\n## Styles and Scripts <span id=\"styles-and-scripts\"></span>\n\nThere are two methods to generate tags wrapping embedded styles and scripts:\n\n```php\n<?= Html::style('.danger { color: #f00; }', ['media' => 'print']) ?>\n\nGives you\n\n<style media=\"print\">.danger { color: #f00; }</style>\n\n\n<?= Html::script('alert(\"Hello!\");') ?>\n\nGives you\n\n<script>alert(\"Hello!\");</script>\n```\n\nIf you want to use an external style in a CSS file:\n\n```php\n<?= Html::cssFile('@web/css/ie5.css', ['condition' => 'IE 5']) ?>\n\ngenerates\n\n<!--[if IE 5]>\n    <link href=\"https://example.com/css/ie5.css\" />\n<![endif]-->\n```\n\nThe first argument is the URL. The second one is an array of options. In addition to the regular options, you can specify:\n\n- `condition` to wrap `<link` in conditional comments with the specified condition. Hope you won't need conditional\n  comments ever ;)\n- `noscript` can be set to `true` to wrap `<link` in a `<noscript>` tag so it will be included only when there's\n  either no JavaScript support in the browser or it was disabled by the user.\n\nTo link a JavaScript file:\n\n```php\n<?= Html::jsFile('@web/js/main.js') ?>\n```\n\nSame as with CSS, the first argument specifies the URL of the file to be included. Options can be passed as the second argument.\nIn the options you can specify `condition` in the same way as in the options for `cssFile`.\n\n\n## Hyperlinks <span id=\"hyperlinks\"></span>\n\nThere's a method to generate hyperlinks conveniently:\n\n```php\n<?= Html::a('Profile', ['user/view', 'id' => $id], ['class' => 'profile-link']) ?>\n```\n\nThe first argument is the title. It's not encoded, so if you're using data entered by the user, you need to encode it with\n`Html::encode()`. The second argument is what will be in the `href` attribute of the `<a` tag.\nSee [Url::to()](helper-url.md) for details on what values it accepts.\nThe third argument is an array of tag attributes.\n\nIf you need to generate `mailto` links, you can use the following code:\n\n```php\n<?= Html::mailto('Contact us', 'admin@example.com') ?>\n```\n\n\n## Images <span id=\"images\"></span>\n\nIn order to generate an image tag, use the following:\n\n```php\n<?= Html::img('@web/images/logo.png', ['alt' => 'My logo']) ?>\n\ngenerates\n\n<img src=\"https://example.com/images/logo.png\" alt=\"My logo\" />\n```\n\nBesides [aliases](concept-aliases.md), the first argument can accept routes, parameters and URLs, in the same way [Url::to()](helper-url.md) does.\n\n\n## Lists <span id=\"lists\"></span>\n\nUnordered list can be generated like the following:\n\n```php\n<?= Html::ul($posts, ['item' => function($item, $index) {\n    return Html::tag(\n        'li',\n        $this->render('post', ['item' => $item]),\n        ['class' => 'post']\n    );\n}]) ?>\n```\n\nIn order to get ordered list, use `Html::ol()` instead.\n"
  },
  {
    "path": "docs/guide/helper-json.md",
    "content": "Json Helper\n==========\n\nJson helper provides a set of static methods for encoding and decoding JSON.\nIt handles encoding errors and the `[[yii\\helpers\\Json::encode()]]` method will not encode a JavaScript expression that is represented in \nterms of a `[[yii\\web\\JsExpression]]` object.\nBy default, encoding is done with the `JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE` options.\nPlease see [PHP:json_encode](https://www.php.net/manual/en/function.json-encode.php) for more information.\n\n## Pretty Print <span id=\"pretty-print\"></span>\n\nBy default the `[[yii\\helpers\\Json::encode()]]` method will output unformatted JSON (e.g. without whitespaces).\nTo make it more readable for humans you can turn on \"pretty printing\".\n\n> Note: Pretty Print can useful for debugging during development but is not recommended in a production environment. \n\nTo enable pretty print in a single instance you can specify it as an option. E.g.\n\n```php\n$data = ['a' => 1, 'b' => 2];\n$json = yii\\helpers\\Json::encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);\n```\nYou can also enable pretty printing of the JSON helper globally. For example in your config for or index.php: \n```php\nyii\\helpers\\Json::$prettyPrint = YII_DEBUG; // use \"pretty\" output in debug mode\n```\n"
  },
  {
    "path": "docs/guide/helper-overview.md",
    "content": "Helpers\n=======\n\n> Note: This section is under development.\n\nYii provides many classes that help simplify common coding tasks, such as string or array manipulations,\nHTML code generation, and so on. These helper classes are organized under the `yii\\helpers` namespace and\nare all static classes (meaning they contain only static properties and methods and should not be instantiated).\n\nYou use a helper class by directly calling one of its static methods, like the following:\n\n```php\nuse yii\\helpers\\Html;\n\necho Html::encode('Test > test');\n```\n\n> Note: To support [customizing helper classes](#customizing-helper-classes), Yii breaks each core helper class\n  into two classes: a base class (e.g. `BaseArrayHelper`) and a concrete class (e.g. `ArrayHelper`).\n  When you use a helper, you should only use the concrete version and never use the base class.\n\n\nCore Helper Classes\n-------------------\n\nThe following core helper classes are provided in the Yii releases:\n\n- [ArrayHelper](helper-array.md)\n- Console\n- FileHelper\n- FormatConverter\n- [Html](helper-html.md)\n- HtmlPurifier\n- Imagine (provided by yii2-imagine extension)\n- Inflector\n- [Json](helper-json.md)\n- Markdown\n- StringHelper\n- [Url](helper-url.md)\n- VarDumper\n\n\nCustomizing Helper Classes <span id=\"customizing-helper-classes\"></span>\n--------------------------\n\nTo customize a core helper class (e.g. [[yii\\helpers\\ArrayHelper]]), you should create a new class extending\nfrom the helpers corresponding base class (e.g. [[yii\\helpers\\BaseArrayHelper]]) and name your class the same\nas the corresponding concrete class (e.g. [[yii\\helpers\\ArrayHelper]]), including its namespace. This class\nwill then be set up to replace the original implementation of the framework.\n\nThe following example shows how to customize the [[yii\\helpers\\ArrayHelper::merge()|merge()]] method of the\n[[yii\\helpers\\ArrayHelper]] class:\n\n```php\n<?php\n\nnamespace yii\\helpers;\n\nclass ArrayHelper extends BaseArrayHelper\n{\n    public static function merge($a, $b)\n    {\n        // your custom implementation\n    }\n}\n```\n\nSave your class in a file named `ArrayHelper.php`. The file can be in any directory, for example `@app/components`.\n\nNext, in your application's [entry script](structure-entry-scripts.md), add the following line of code\nafter including the `yii.php` file to tell the [Yii class autoloader](concept-autoloading.md) to load your custom\nclass instead of the original helper class from the framework:\n\n```php\nYii::$classMap['yii\\helpers\\ArrayHelper'] = '@app/components/ArrayHelper.php';\n```\n\nNote that customizing of helper classes is only useful if you want to change the behavior of an existing function\nof the helpers. If you want to add additional functions to use in your application, you may be better off creating a separate\nhelper for that.\n"
  },
  {
    "path": "docs/guide/helper-url.md",
    "content": "Url Helper\n==========\n\nUrl helper provides a set of static methods for managing URLs.\n\n\n## Getting Common URLs <span id=\"getting-common-urls\"></span>\n\nThere are two methods you can use to get common URLs: home URL and base URL of the current request. In order to get\nhome URL, use the following:\n\n```php\n$relativeHomeUrl = Url::home();\n$absoluteHomeUrl = Url::home(true);\n$httpsAbsoluteHomeUrl = Url::home('https');\n```\n\nIf no parameter is passed, the generated URL is relative. You can either pass `true` to get an absolute URL for the current\nschema or specify a schema explicitly (`https`, `http`).\n\nTo get the base URL of the current request use the following:\n \n```php\n$relativeBaseUrl = Url::base();\n$absoluteBaseUrl = Url::base(true);\n$httpsAbsoluteBaseUrl = Url::base('https');\n```\n\nThe only parameter of the method works exactly the same as for `Url::home()`.\n\n\n## Creating URLs <span id=\"creating-urls\"></span>\n\nIn order to create a URL to a given route use the `Url::toRoute()` method. The method uses [[\\yii\\web\\UrlManager]] to create\na URL:\n\n```php\n$url = Url::toRoute(['product/view', 'id' => 42]);\n```\n \nYou may specify the route as a string, e.g., `site/index`. You may also use an array if you want to specify additional\nquery parameters for the URL being created. The array format must be:\n\n```php\n// generates: /index.php?r=site%2Findex&param1=value1&param2=value2\n['site/index', 'param1' => 'value1', 'param2' => 'value2']\n```\n\nIf you want to create a URL with an anchor, you can use the array format with a `#` parameter. For example,\n\n```php\n// generates: /index.php?r=site%2Findex&param1=value1#name\n['site/index', 'param1' => 'value1', '#' => 'name']\n```\n\nA route may be either absolute or relative. An absolute route has a leading slash (e.g. `/site/index`) while a relative\nroute has none (e.g. `site/index` or `index`). A relative route will be converted into an absolute one by the following rules:\n\n- If the route is an empty string, the current [[\\yii\\web\\Controller::route|route]] will be used;\n- If the route contains no slashes at all (e.g. `index`), it is considered to be an action ID of the current controller\n  and will be prepended with [[\\yii\\web\\Controller::uniqueId]];\n- If the route has no leading slash (e.g. `site/index`), it is considered to be a route relative to the current module\n  and will be prepended with the module's [[\\yii\\base\\Module::uniqueId|uniqueId]].\n  \nStarting from version 2.0.2, you may specify a route in terms of an [alias](concept-aliases.md). If this is the case,\nthe alias will first be converted into the actual route which will then be turned into an absolute route according\nto the above rules.\n\nBelow are some examples of using this method:\n\n```php\n// /index.php?r=site%2Findex\necho Url::toRoute('site/index');\n\n// /index.php?r=site%2Findex&src=ref1#name\necho Url::toRoute(['site/index', 'src' => 'ref1', '#' => 'name']);\n\n// /index.php?r=post%2Fedit&id=100     assume the alias \"@postEdit\" is defined as \"post/edit\"\necho Url::toRoute(['@postEdit', 'id' => 100]);\n\n// https://www.example.com/index.php?r=site%2Findex\necho Url::toRoute('site/index', true);\n\n// https://www.example.com/index.php?r=site%2Findex\necho Url::toRoute('site/index', 'https');\n```\n\nThere's another method `Url::to()` that is very similar to [[toRoute()]]. The only difference is that this method\nrequires a route to be specified as an array only. If a string is given, it will be treated as a URL.\n\nThe first argument could be:\n         \n- an array: [[toRoute()]] will be called to generate the URL. For example:\n  `['site/index']`, `['post/index', 'page' => 2]`. Please refer to [[toRoute()]] for more details\n  on how to specify a route.\n- a string with a leading `@`: it is treated as an alias, and the corresponding aliased string\n  will be returned.\n- an empty string: the currently requested URL will be returned;\n- a normal string: it will be returned as is.\n\nWhen `$scheme` is specified (either a string or `true`), an absolute URL with host info (obtained from\n[[\\yii\\web\\UrlManager::hostInfo]]) will be returned. If `$url` is already an absolute URL, its scheme\nwill be replaced with the specified one.\n\nBelow are some usage examples:\n\n```php\n// /index.php?r=site%2Findex\necho Url::to(['site/index']);\n\n// /index.php?r=site%2Findex&src=ref1#name\necho Url::to(['site/index', 'src' => 'ref1', '#' => 'name']);\n\n// /index.php?r=post%2Fedit&id=100     assume the alias \"@postEdit\" is defined as \"post/edit\"\necho Url::to(['@postEdit', 'id' => 100]);\n\n// the currently requested URL\necho Url::to();\n\n// /images/logo.gif\necho Url::to('@web/images/logo.gif');\n\n// images/logo.gif\necho Url::to('images/logo.gif');\n\n// https://www.example.com/images/logo.gif\necho Url::to('@web/images/logo.gif', true);\n\n// https://www.example.com/images/logo.gif\necho Url::to('@web/images/logo.gif', 'https');\n```\n\nStarting from version 2.0.3, you may use [[yii\\helpers\\Url::current()]] to create a URL based on the currently\nrequested route and GET parameters. You may modify or remove some of the GET parameters or add new ones by\npassing a `$params` parameter to the method. For example,\n\n```php\n// assume $_GET = ['id' => 123, 'src' => 'google'], current route is \"post/view\"\n\n// /index.php?r=post%2Fview&id=123&src=google\necho Url::current();\n\n// /index.php?r=post%2Fview&id=123\necho Url::current(['src' => null]);\n// /index.php?r=post%2Fview&id=100&src=google\necho Url::current(['id' => 100]);\n```\n\n\n## Remember URLs <span id=\"remember-urls\"></span>\n\nThere are cases when you need to remember URL and afterwards use it during processing of the one of sequential requests.\nIt can be achieved in the following way:\n \n```php\n// Remember current URL \nUrl::remember();\n\n// Remember URL specified. See Url::to() for argument format.\nUrl::remember(['product/view', 'id' => 42]);\n\n// Remember URL specified with a name given\nUrl::remember(['product/view', 'id' => 42], 'product');\n```\n\nIn the next request we can get URL remembered in the following way:\n\n```php\n$url = Url::previous();\n$productUrl = Url::previous('product');\n```\n                        \n## Checking Relative URLs <span id=\"checking-relative-urls\"></span>\n\nTo find out if URL is relative i.e. it doesn't have host info part, you can use the following code:\n                             \n```php\n$isRelative = Url::isRelative('test/it');\n```\n"
  },
  {
    "path": "docs/guide/images/application-lifecycle.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.13-->\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d0\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key for=\"graphml\" id=\"d7\" yfiles.type=\"resources\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d0\"/>\n    <node id=\"n0\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"571.4472707112631\" width=\"763.2772213171534\" x=\"-1269.9373595143054\" y=\"-207.17439524332679\"/>\n              <y:Fill color=\"#FFCC0024\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FFCC00\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"763.2772213171534\" x=\"0.0\" y=\"0.0\">Entry script (index.php or yii)</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"225.33495140075684\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 4</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n0:\">\n        <node id=\"n0::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"324.9258883570935\" x=\"-1249.511914911339\" y=\"-169.79793039957679\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"126.759765625\" x=\"99.08306136604676\" y=\"5.6494140625\">Load application config<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n0::n1\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d5\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"309.37646484374994\" width=\"330.35133296005984\" x=\"-1254.9373595143054\" y=\"35.272875467936274\"/>\n                  <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"330.35133296005984\" x=\"0.0\" y=\"0.0\">Create application instance</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"19\" bottomF=\"19.15489692687993\" left=\"5\" leftF=\"5.425444602966309\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 5</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n0::n1:\">\n            <node id=\"n0::n1::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"72.64934031168627\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"45.34375\" x=\"124.79106917854676\" y=\"5.6494140625\">preInit()<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n1\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"122.64524027506516\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"120.71875\" x=\"87.10356917854676\" y=\"5.6494140625\">Register error handler<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n2\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"174.96110788981125\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"173.435546875\" x=\"60.74517074104676\" y=\"5.6494140625\">Configure application properties<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n3\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"226.56779181162517\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"27.33203125\" x=\"133.79692855354676\" y=\"5.6494140625\">init()<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n4\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.9258883570935\" x=\"-1234.511914911339\" y=\"280.4944433848063\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.025390625\" x=\"116.45024886604676\" y=\"5.6494140625\">bootstrap()<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n        <node id=\"n0::n2\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d5\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"411.6943410237631\" width=\"324.9258883570935\" x=\"-846.5860265542456\" y=\"-169.79793039957679\"/>\n                  <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"324.9258883570935\" x=\"0.0\" y=\"0.0\">Run application</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"1.1368683772161603E-13\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 3</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n0::n2:\">\n            <node id=\"n0::n2::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.9258883570935\" x=\"-831.5860265542456\" y=\"-132.42146555582667\"/>\n                  <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"164.705078125\" x=\"65.11040511604676\" y=\"5.6494140625\">EVENT_BEFORE_REQUEST<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n2::n1\" yfiles.foldertype=\"group\">\n              <data key=\"d4\"/>\n              <data key=\"d5\"/>\n              <data key=\"d6\">\n                <y:ProxyAutoBoundsNode>\n                  <y:Realizers active=\"0\">\n                    <y:GroupNode>\n                      <y:Geometry height=\"204.37646484375\" width=\"294.9258883570935\" x=\"-831.5860265542456\" y=\"-78.79793039957679\"/>\n                      <y:Fill color=\"#99336635\" transparent=\"false\"/>\n                      <y:BorderStyle hasColor=\"false\" type=\"dashed\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#993366\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#FFFFFF\" visible=\"true\" width=\"294.9258883570935\" x=\"0.0\" y=\"0.0\">Handle request</y:NodeLabel>\n                      <y:Shape type=\"roundrectangle\"/>\n                      <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                      <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                      <y:BorderInsets bottom=\"8\" bottomF=\"7.929194132486941\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                    </y:GroupNode>\n                    <y:GroupNode>\n                      <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                      <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 4</y:NodeLabel>\n                      <y:Shape type=\"roundrectangle\"/>\n                      <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                      <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                      <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                    </y:GroupNode>\n                  </y:Realizers>\n                </y:ProxyAutoBoundsNode>\n              </data>\n              <graph edgedefault=\"directed\" id=\"n0::n2::n1:\">\n                <node id=\"n0::n2::n1::n0\">\n                  <data key=\"d6\">\n                    <y:ShapeNode>\n                      <y:Geometry height=\"30.0\" width=\"264.9258883570935\" x=\"-816.5860265542456\" y=\"-41.421465555826785\"/>\n                      <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"231.4609375\" x=\"16.732475428546763\" y=\"5.6494140625\">Resolve request into route and parameters<y:LabelModel>\n                          <y:SmartNodeLabelModel distance=\"4.0\"/>\n                        </y:LabelModel>\n                        <y:ModelParameter>\n                          <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                        </y:ModelParameter>\n                      </y:NodeLabel>\n                      <y:Shape type=\"rectangle\"/>\n                    </y:ShapeNode>\n                  </data>\n                </node>\n                <node id=\"n0::n2::n1::n1\">\n                  <data key=\"d6\">\n                    <y:ShapeNode>\n                      <y:Geometry height=\"30.0\" width=\"264.9258883570935\" x=\"-816.5860265542456\" y=\"18.578534444173215\"/>\n                      <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"197.44140625\" x=\"33.74224105354676\" y=\"5.6494140625\">Create module, controller and action<y:LabelModel>\n                          <y:SmartNodeLabelModel distance=\"4.0\"/>\n                        </y:LabelModel>\n                        <y:ModelParameter>\n                          <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                        </y:ModelParameter>\n                      </y:NodeLabel>\n                      <y:Shape type=\"rectangle\"/>\n                    </y:ShapeNode>\n                  </data>\n                </node>\n                <node id=\"n0::n2::n1::n2\">\n                  <data key=\"d6\">\n                    <y:ShapeNode>\n                      <y:Geometry height=\"30.0\" width=\"264.9258883570935\" x=\"-816.5860265542456\" y=\"72.64934031168627\"/>\n                      <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.369140625\" x=\"101.77837386604676\" y=\"5.649414062500057\">Run action<y:LabelModel>\n                          <y:SmartNodeLabelModel distance=\"4.0\"/>\n                        </y:LabelModel>\n                        <y:ModelParameter>\n                          <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                        </y:ModelParameter>\n                      </y:NodeLabel>\n                      <y:Shape type=\"rectangle\"/>\n                    </y:ShapeNode>\n                  </data>\n                </node>\n              </graph>\n            </node>\n            <node id=\"n0::n2::n2\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.9258883570935\" x=\"-831.5860265542456\" y=\"149.20206960042316\"/>\n                  <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"154.697265625\" x=\"70.11431136604676\" y=\"5.6494140625\">EVENT_AFTER_REQUEST<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n2::n3\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-831.5860265542456\" y=\"196.89641062418633\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"148.099609375\" x=\"73.41313949104676\" y=\"5.6494140625\">Send response to end user<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n        <node id=\"n0::n3\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"324.9258883570935\" x=\"-846.5860265542456\" y=\"319.2728754679363\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"160.08203125\" x=\"82.42192855354676\" y=\"5.6494140625\">Complete request processing<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <edge id=\"e0\" source=\"n0\" target=\"n0::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-179.83580569315376\" sy=\"-180.3944529152355\" tx=\"-13.869777491410105\" ty=\"-154.8008369539754\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::e0\" source=\"n0::n0\" target=\"n0::n1\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#99CCFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"106.052734375\" x=\"-130.76131968438426\" y=\"78.18481445312506\">Configuration array<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"77.04379339868619\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e0\" source=\"n0::n1::n0\" target=\"n0::n1::n1\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e1\" source=\"n0::n1::n1\" target=\"n0::n1::n2\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e2\" source=\"n0::n1::n2\" target=\"n0::n1::n3\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e3\" source=\"n0::n1::n3\" target=\"n0::n1::n4\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::e1\" source=\"n0::n1\" target=\"n0::n2\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"2.8060680342755404\" sy=\"-48.37646484374994\" tx=\"-162.49660512430125\" ty=\"105.53540293375653\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::e0\" source=\"n0::n2::n0\" target=\"n0::n2::n1\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::n1::e0\" source=\"n0::n2::n1::n0\" target=\"n0::n2::n1::n1\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::n1::e1\" source=\"n0::n2::n1::n1\" target=\"n0::n2::n1::n2\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::e1\" source=\"n0::n2::n1\" target=\"n0::n2::n2\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::e2\" source=\"n0::n2::n2\" target=\"n0::n2::n3\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::e2\" source=\"n0::n2\" target=\"n0::n3\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#99CCFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"59.353515625\" x=\"-93.67673227804266\" y=\"28.318773905436274\">Exit status<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"63.99999999999999\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.47945569632951074\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d7\">\n    <y:Resources/>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide/images/application-structure.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"872.1807999999999\" y=\"-14.764159999999947\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"32.265625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"69.712890625\" x=\"15.1435546875\" y=\"1.3671875\">application\ncomponent<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"-91.97375999999994\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"70.28125\" x=\"14.859375\" y=\"8.43359375\">entry script<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"-14.764159999999947\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"68.21875\" x=\"15.890625\" y=\"8.43359375\">application<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"62.44544000000004\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"60.267578125\" x=\"19.8662109375\" y=\"8.433593750000007\">controller<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"872.1807999999999\" y=\"62.44544000000005\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"31.43359375\" x=\"34.283203125\" y=\"8.43359375\">filter<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"532.664\" y=\"23.901600000000087\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"47.728515625\" x=\"26.1357421875\" y=\"8.43359375\">module<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"618.4047999999991\" y=\"139.65504000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"29.611328125\" x=\"35.1943359375\" y=\"8.43359375\">view<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"786.161599999999\" y=\"139.65504000000004\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"40.28125\" x=\"29.859375\" y=\"8.43359375\">model<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n8\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"532.664\" y=\"216.86464000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"42.923828125\" x=\"28.5380859375\" y=\"8.43359375\">widget<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n9\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"216.86464000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"77.986328125\" x=\"11.0068359375\" y=\"8.43359375\">asset bundle<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n2\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.97265625\" x=\"2.5392475585936154\" y=\"-25.635106635436955\">1:1<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.025600000000054\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.17992697197425173\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n0\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-48.97602119140629\" y=\"-23.09200633216852\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.025599999999994\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5470891790487767\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n5\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"15.193962466822086\" y=\"30.674065521861735\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"44.894496863129426\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.25416574623968574\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n3\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"2.7719538085937074\" y=\"-26.04470613037104\">1..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"15.254400000000032\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.2090245199765919\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n3\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-57.39355288912964\" y=\"-63.846685518352906\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"43.47658308018222\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.881412889692355\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n5\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"582.664\" y=\"-3.598399999999913\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-17.888505566406252\" y=\"-42.41848039245597\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"16.483200000000004\" distanceToCenter=\"true\" position=\"right\" ratio=\"5.822600000000001\" segment=\"-2\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n6\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-12.223966589163297\" y=\"-24.76668258085064\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"11.385360101663515\" distanceToCenter=\"true\" position=\"left\" ratio=\"0.10670293331985262\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e7\" source=\"n7\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-9.313146217848043\" y=\"-26.098020948340803\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.669798038586146\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.1670192242704811\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e8\" source=\"n9\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-9.797233483694527\" y=\"-25.82443358614151\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.15599378957306\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.15481212573620068\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e9\" source=\"n8\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-15.633139430038\" y=\"-24.050683308790553\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"13.24330808446041\" distanceToCenter=\"true\" position=\"left\" ratio=\"0.05506723556297327\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e10\" source=\"n4\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-47.337621191406356\" y=\"-22.682408449706998\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"13.616000000000007\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e11\" source=\"n9\" target=\"n8\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-44.265598339843905\" y=\"-19.61040553222648\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.543999999999983\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.4117077892835431\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e12\" source=\"n7\" target=\"n6\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-44.81721357421975\" y=\"-19.610410805664003\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.543999999999983\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.4531592446547025\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources/>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide/images/rbac-access-check-1.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide/images/rbac-access-check-2.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide/images/rbac-access-check-3.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide/images/rbac-hierarchy-1.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n5\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide/images/rbac-hierarchy-2.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide/images/request-lifecycle.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"70.13700103759766\" width=\"56.558998107910156\" x=\"198.38633947503078\" y=\"261.099458694458\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"26.279499053955078\" y=\"74.13700103759766\">\n            <y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"-0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"0.5\" offsetX=\"0.0\" offsetY=\"4.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"27.34375\" x=\"-31.34375\" y=\"25.717914581298828\">user<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.5\" labelRatioY=\"0.0\" nodeRatioX=\"-0.5\" nodeRatioY=\"0.0\" offsetX=\"-4.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"637.1271178722382\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"41.158203125\" y=\"13.1494140625\">model<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"46.887996673583984\" width=\"39.527000427246094\" x=\"828.8018617630005\" y=\"544.9831195354463\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"53.376953125\" x=\"-6.924976348876953\" y=\"-30.723247528076172\">database<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-12.022075653076172\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"17.763500213623047\" y=\"21.443998336791992\">\n            <y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"713.6271178722382\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"28.005859375\" x=\"45.4970703125\" y=\"13.1494140625\">view<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n4\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"377.0372841596604\" width=\"219.27949905395508\" x=\"527.4919853210449\" y=\"407.9266515731811\"/>\n              <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"219.27949905395508\" x=\"0.0\" y=\"0.0\">controller</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"2\" leftF=\"1.7335329055786133\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"412.2765645980835\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 1</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n4:\">\n        <node id=\"n4::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"445.3031164169311\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"72.70703125\" x=\"38.92598342895508\" y=\"5.6494140625\">create action<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n4::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"45.0\" width=\"159.91864204406738\" x=\"558.5326642990112\" y=\"521.8166599273682\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"77.34765625\" x=\"41.28549289703369\" y=\"13.1494140625\">perform filters<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"diamond\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n4::n2\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"162.71328270435333\" width=\"187.54596614837646\" x=\"544.2255182266235\" y=\"607.2506530284882\"/>\n                  <y:Fill color=\"#99336635\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#993366\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#FFFFFF\" visible=\"true\" width=\"187.54596614837646\" x=\"0.0\" y=\"0.0\">action</y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"4\" bottomF=\"3.8368178606033325\" left=\"4\" leftF=\"3.9869680404663086\" right=\"3\" rightF=\"3.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 3</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n4::n2:\">\n            <node id=\"n4::n2::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"644.6271178722382\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.705078125\" x=\"43.92695999145508\" y=\"5.6494140625\">load model<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n4::n2::n1\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"721.1271178722382\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"66.02734375\" x=\"42.26582717895508\" y=\"5.6494140625\">render view<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n      </graph>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"198.3863394750308\" y=\"713.6271178722382\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"116.072265625\" x=\"1.4638671875000284\" y=\"13.1494140625\">response component<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"254.50096702575684\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"106.732421875\" x=\"6.1337890625\" y=\"13.1494140625\">request component<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"144.132230758667\" width=\"219.27949905395508\" x=\"527.4919853210449\" y=\"222.86873626708984\"/>\n              <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"219.27949905395508\" x=\"0.0\" y=\"0.0\">application</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"21\" leftF=\"20.720500946044922\" right=\"18\" rightF=\"18.0\" top=\"2\" topF=\"1.7557659149169922\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 2</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n7:\">\n        <node id=\"n7::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"262.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"73.369140625\" x=\"38.59492874145508\" y=\"5.6494140625\">resolve route<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n7::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"322.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"90.0390625\" x=\"30.259967803955078\" y=\"5.6494140625\">create controller<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <node id=\"n8\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"142.37646484375\" width=\"159.91864204406738\" x=\"313.2978515625\" y=\"224.62450218200684\"/>\n              <y:Fill color=\"#FFCC0024\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FFCC00\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"159.91864204406738\" x=\"0.0\" y=\"0.0\">entry script</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"1\" leftF=\"0.9186420440673828\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"225.33495140075684\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 4</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n8:\">\n        <node id=\"n8::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"129.0\" x=\"329.2164936065674\" y=\"262.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"85.3984375\" x=\"21.80078125\" y=\"5.6494140625\">load app config<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n8::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"129.0\" x=\"329.2164936065674\" y=\"322.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"82.052734375\" x=\"23.4736328125\" y=\"5.6494140625\">run application<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <edge id=\"e0\" source=\"n2\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n5\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-31.22050094604495\" sy=\"-22.49430537223816\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"28.00000600945461\" y=\"-193.17557203769684\">\n            <y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"17.34765625\" x=\"-8.673822115545391\" y=\"-200.52615797519684\">11<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n6\" target=\"n7::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-29.525518994973595\" y=\"-10.349628448486328\">3<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"0.9990329742431641\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.25395048761528816\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n7::e0\" source=\"n7::n0\" target=\"n7::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n0\" target=\"n8\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"-79.9882439360955\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"36.76878085201736\" y=\"-9.523307113000556\">1<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n7::n1\" target=\"n4\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"2.408647025755731\" ty=\"-181.21658369302781\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-4.962140296002758\" y=\"18.61224679946895\">4<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n3\" target=\"n4::n2::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-30.100868416252297\" y=\"-9.35060429573059\">9<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.26448415477215953\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n4::n2::n1\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"-2.2737367544323206E-13\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"17.34765625\" x=\"-83.18526519030615\" y=\"-9.350604295730818\">10<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.2786124840137136\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e7\" source=\"n8::n1\" target=\"n7\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"55.750299312644984\" sy=\"0.355224609375\" tx=\"-109.63961141576266\" ty=\"42.066115379333496\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"29.326010704040527\" y=\"-9.508390885372137\">2<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e8\" source=\"n1\" target=\"n4::n2::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-31.270312699786246\" y=\"-10.35060429573059\">8<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"1.0\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.2858946861565087\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e9\" source=\"n4::n1\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-79.9882439360951\" sy=\"2.2737367544323206E-13\" tx=\"30.048898971244075\" ty=\"-1.4999999999999991\">\n            <y:Point x=\"287.93523844627487\" y=\"544.3166599273684\"/>\n          </y:Path>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-76.70009317381192\" y=\"-9.350576400756609\">6<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.23459266172695797\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::e0\" source=\"n4::n0\" target=\"n4::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFEFD6\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-5.336933135986328\" y=\"4.999985313415493\">5<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.0\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::e1\" source=\"n4::n1\" target=\"n4::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"-81.01828954355172\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFEFD6\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-5.4491733540852465\" y=\"5.039560317993164\">7<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.0\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::n2::e0\" source=\"n4::n2::n0\" target=\"n4::n2::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n8::e0\" source=\"n8::n0\" target=\"n8::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"66px\" viewBox=\"0 0 57 66\" enable-background=\"new 0 0 57 66\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3799\" y1=\"-2276.8809\" x2=\"27.6209\" y2=\"-2306.6792\" gradientTransform=\"matrix(1 0 0 -1 0.2803 -2252.9199)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_13_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t&lt;path fill=\"#2068A3\" stroke=\"#2068A3\" d=\"M28.106,33.487c-8.112,0-12.688,4.312-12.688,10.437c0,7.422,12.688,10.438,12.688,10.438\n\t\ts14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.487,28.106,33.487z M26.288,53.051c0,0-7.135-2.093-8.805-7.201\n\t\tc-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"14.2417\" cy=\"9.1006\" r=\"53.247\" gradientTransform=\"matrix(1 0 0 -1 0.04 65.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#74AEEE\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#2068A3\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#2068A3\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.022,7.807-14.022,7.807s-10.472-2.484-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path fill=\"#5491CF\" stroke=\"#2068A3\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397c-0.514,1.027-1.669,4.084-1.669,5.148\n\t\tc0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.335-2.36c-3.601-1.419-4.071-3.063-5.89-4.854\n\t\tC12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t&lt;path fill=\"#5491CF\" stroke=\"#2068A3\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617c0.516,1.025,3.617,3.693,3.617,6.617\n\t\tc0,5.186-10.27,8.576-16.698,9.145c1.429,4.938,11.372,1.293,13.804-0.313c3.563-2.354,4.563-5.133,7.854-3.705\n\t\tC47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t&lt;path fill=\"none\" stroke=\"#2068A3\" stroke-linecap=\"round\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t&lt;path fill=\"none\" stroke=\"#2068A3\" stroke-linecap=\"round\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.623\" cy=\"-2278.646\" r=\"23.425\" fx=\"23.0534\" fy=\"-2281.1357\" gradientTransform=\"matrix(1 0 0 -1 0.2803 -2252.9199)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"5761.7578\" y1=\"11330.6484\" x2=\"5785.3872\" y2=\"11424.0977\" gradientTransform=\"matrix(0.275 0 0 0.2733 -1558.9874 -3088.4209)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M27.958,6.333c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.083,13.952,36.271,6.268,27.958,6.333z\"/&gt;\n\t&lt;path id=\"Hair_Young_Brown_1_\" fill=\"#CC9869\" stroke=\"#99724F\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n\t&lt;path fill=\"#4B4B4B\" stroke=\"#4B4B4B\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" d=\"M28.105,2\n\t\tC22.464,2,20.2,4.246,18.13,5.533C29.753,2.865,41.152,10.375,44.46,20.5C44.459,16.875,44.459,2,28.105,2z\"/&gt;\n\t&lt;path fill=\"#9B9B9B\" stroke=\"#4B4B4B\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" d=\"M11.151,17.751\n\t\tC12.878,8.25,18.686,6.309,25.273,7.127C31.295,7.875,36.93,10.491,44.459,20.5C37.777,7.125,20.278-3.375,9.903,3.921\n\t\tC5.569,6.97,4.903,13.375,11.151,17.751z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\"\n\t xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\t x=\"0px\" y=\"0px\" width=\"41px\" height=\"48px\" viewBox=\"-0.875 -0.887 41 48\" enable-background=\"new -0.875 -0.887 41 48\"\n\t xml:space=\"preserve\"&gt;\n&lt;defs&gt;\n&lt;/defs&gt;\n&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-979.1445\" x2=\"682.0508\" y2=\"-979.1445\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_1_)\" d=\"M19.625,36.763C8.787,36.763,0,34.888,0,32.575v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,34.888,30.464,36.763,19.625,36.763z\"/&gt;\n&lt;linearGradient id=\"SVGID_2_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-973.1445\" x2=\"682.0508\" y2=\"-973.1445\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_2_)\" d=\"M19.625,36.763c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.927-18.396,3.927\n\tc-9.481,0-17.396-1.959-18.396-3.927l-1.229,2C0,34.888,8.787,36.763,19.625,36.763z\"/&gt;\n&lt;path fill=\"#3C89C9\" d=\"M19.625,26.468c10.16,0,19.625,2.775,19.625,2.775c-0.375,2.721-5.367,5.438-19.554,5.438\n\tc-12.125,0-18.467-2.484-19.541-4.918C-0.127,29.125,9.465,26.468,19.625,26.468z\"/&gt;\n&lt;linearGradient id=\"SVGID_3_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-965.6948\" x2=\"682.0508\" y2=\"-965.6948\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_3_)\" d=\"M19.625,23.313C8.787,23.313,0,21.438,0,19.125v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,21.438,30.464,23.313,19.625,23.313z\"/&gt;\n&lt;linearGradient id=\"SVGID_4_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-959.6948\" x2=\"682.0508\" y2=\"-959.6948\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_4_)\" d=\"M19.625,23.313c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.926-18.396,3.926\n\tc-9.481,0-17.396-1.959-18.396-3.926l-1.229,2C0,21.438,8.787,23.313,19.625,23.313z\"/&gt;\n&lt;path fill=\"#3C89C9\" d=\"M19.476,13.019c10.161,0,19.625,2.775,19.625,2.775c-0.375,2.721-5.367,5.438-19.555,5.438\n\tc-12.125,0-18.467-2.485-19.541-4.918C-0.277,15.674,9.316,13.019,19.476,13.019z\"/&gt;\n&lt;linearGradient id=\"SVGID_5_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-952.4946\" x2=\"682.0508\" y2=\"-952.4946\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_5_)\" d=\"M19.625,10.113C8.787,10.113,0,8.238,0,5.925v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,8.238,30.464,10.113,19.625,10.113z\"/&gt;\n&lt;linearGradient id=\"SVGID_6_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-946.4946\" x2=\"682.0508\" y2=\"-946.4946\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_6_)\" d=\"M19.625,10.113c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.926-18.396,3.926\n\tc-9.481,0-17.396-1.959-18.396-3.926L0,5.925C0,8.238,8.787,10.113,19.625,10.113z\"/&gt;\n&lt;linearGradient id=\"SVGID_7_\" gradientUnits=\"userSpaceOnUse\" x1=\"644.0293\" y1=\"-943.4014\" x2=\"680.8223\" y2=\"-943.4014\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;ellipse fill=\"url(#SVGID_7_)\" cx=\"19.625\" cy=\"3.926\" rx=\"18.396\" ry=\"3.926\"/&gt;\n&lt;path opacity=\"0.24\" fill=\"#FFFFFF\" enable-background=\"new    \" d=\"M31.04,45.982c0,0-4.354,0.664-7.29,0.781\n\tc-3.125,0.125-8.952,0-8.952,0l-2.384-10.292l0.044-2.108l-1.251-1.154L9.789,23.024l-0.082-0.119L9.5,20.529l-1.65-1.254\n\tL5.329,8.793c0,0,4.213,0.903,7.234,1.07s8.375,0.25,8.375,0.25l3,9.875l-0.25,1.313l1.063,2.168l2.312,9.645l-0.521,1.416\n\tl1.46,1.834L31.04,45.982z\"/&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide/input-file-upload.md",
    "content": "Uploading Files\n===============\n\nUploading files in Yii is usually done with the help of [[yii\\web\\UploadedFile]] which encapsulates each uploaded\nfile as an `UploadedFile` object. Combined with [[yii\\widgets\\ActiveForm]] and [models](structure-models.md),\nyou can easily implement a secure file uploading mechanism.\n\n\n## Creating Models <span id=\"creating-models\"></span>\n\nLike working with plain text inputs, to upload a single file you would create a model class and use an attribute\nof the model to keep the uploaded file instance. You should also declare a validation rule to validate the file upload.\nFor example,\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\nuse yii\\web\\UploadedFile;\n\nclass UploadForm extends Model\n{\n    /**\n     * @var UploadedFile\n     */\n    public $imageFile;\n\n    public function rules()\n    {\n        return [\n            [['imageFile'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg'],\n        ];\n    }\n    \n    public function upload()\n    {\n        if ($this->validate()) {\n            $this->imageFile->saveAs('uploads/' . $this->imageFile->baseName . '.' . $this->imageFile->extension);\n            return true;\n        } else {\n            return false;\n        }\n    }\n}\n```\n\nIn the code above, the `imageFile` attribute is used to keep the uploaded file instance. It is associated with\na `file` validation rule which uses [[yii\\validators\\FileValidator]] to ensure a file with extension name `png` or `jpg`\nis uploaded. The `upload()` method will perform the validation and save the uploaded file on the server.\n\nThe `file` validator allows you to check file extensions, size, MIME type, etc. Please refer to\nthe [Core Validators](tutorial-core-validators.md#file) section for more details.\n\n> Tip: If you are uploading an image, you may consider using the `image` validator instead. The `image` validator is\n  implemented via [[yii\\validators\\ImageValidator]] which verifies if an attribute has received a valid image \n  that can be then either saved or processed using the [Imagine Extension](https://github.com/yiisoft/yii2-imagine).\n\n\n## Rendering File Input <span id=\"rendering-file-input\"></span>\n\nNext, create a file input in a view:\n\n```php\n<?php\nuse yii\\widgets\\ActiveForm;\n?>\n\n<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?>\n\n    <?= $form->field($model, 'imageFile')->fileInput() ?>\n\n    <button>Submit</button>\n\n<?php ActiveForm::end() ?>\n```\n\nIt is important to remember that you add the `enctype` option to the form so that the file can be properly uploaded.\nThe `fileInput()` call will render a `<input type=\"file\">` tag which will allow users to select a file to upload.\n\n> Tip: since version 2.0.8, [[yii\\widgets\\ActiveField::fileInput|fileInput]] adds `enctype` option to the form\n  automatically when file input field is used.\n\n## Wiring Up <span id=\"wiring-up\"></span>\n\nNow in a controller action, write the code to wire up the model and the view to implement file uploading:\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\nuse app\\models\\UploadForm;\nuse yii\\web\\UploadedFile;\n\nclass SiteController extends Controller\n{\n    public function actionUpload()\n    {\n        $model = new UploadForm();\n\n        if (Yii::$app->request->isPost) {\n            $model->imageFile = UploadedFile::getInstance($model, 'imageFile');\n            if ($model->upload()) {\n                // file is uploaded successfully\n                return;\n            }\n        }\n\n        return $this->render('upload', ['model' => $model]);\n    }\n}\n```\n\nIn the above code, when the form is submitted, the [[yii\\web\\UploadedFile::getInstance()]] method is called\nto represent the uploaded file as an `UploadedFile` instance. We then rely on the model validation to make sure\nthe uploaded file is valid and save the file on the server.\n\n\n## Uploading Multiple Files <span id=\"uploading-multiple-files\"></span>\n\nYou can also upload multiple files at once, with some adjustments to the code listed in the previous subsections.\n\nFirst you should adjust the model class by adding the `maxFiles` option in the `file` validation rule to limit\nthe maximum number of files allowed to upload. Setting `maxFiles` to `0` means there is no limit on the number of files\nthat can be uploaded simultaneously. The maximum number of files allowed to be uploaded simultaneously is also limited\nwith PHP directive [`max_file_uploads`](https://www.php.net/manual/en/ini.core.php#ini.max-file-uploads),\nwhich defaults to 20. The `upload()` method should also be updated to save the uploaded files one by one.\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\nuse yii\\web\\UploadedFile;\n\nclass UploadForm extends Model\n{\n    /**\n     * @var UploadedFile[]\n     */\n    public $imageFiles;\n\n    public function rules()\n    {\n        return [\n            [['imageFiles'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg', 'maxFiles' => 4],\n        ];\n    }\n    \n    public function upload()\n    {\n        if ($this->validate()) { \n            foreach ($this->imageFiles as $file) {\n                $file->saveAs('uploads/' . $file->baseName . '.' . $file->extension);\n            }\n            return true;\n        } else {\n            return false;\n        }\n    }\n}\n```\n\nIn the view file, you should add the `multiple` option to the `fileInput()` call so that the file upload field\ncan receive multiple files. You also need to change `imageFiles` to `imageFiles[]` so that the attribute values are submitted as an array:\n \n```php\n<?php\nuse yii\\widgets\\ActiveForm;\n?>\n\n<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?>\n\n    <?= $form->field($model, 'imageFiles[]')->fileInput(['multiple' => true, 'accept' => 'image/*']) ?>\n\n    <button>Submit</button>\n\n<?php ActiveForm::end() ?>\n```\n\nAnd finally in the controller action, you should call `UploadedFile::getInstances()` instead of \n`UploadedFile::getInstance()` to assign an array of `UploadedFile` instances to `UploadForm::imageFiles`. \n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\nuse app\\models\\UploadForm;\nuse yii\\web\\UploadedFile;\n\nclass SiteController extends Controller\n{\n    public function actionUpload()\n    {\n        $model = new UploadForm();\n\n        if (Yii::$app->request->isPost) {\n            $model->imageFiles = UploadedFile::getInstances($model, 'imageFiles');\n            if ($model->upload()) {\n                // file is uploaded successfully\n                return;\n            }\n        }\n\n        return $this->render('upload', ['model' => $model]);\n    }\n}\n```\n"
  },
  {
    "path": "docs/guide/input-form-javascript.md",
    "content": "Extending ActiveForm on the Client Side\n=======================================\n\nThe [[yii\\widgets\\ActiveForm]] widget comes with a set of JavaScript methods that are used for client validation.\nIts implementation is very flexible and allows you to extend it in different ways.\nIn the following these are described.\n\n## ActiveForm events\n\nActiveForm triggers a series of dedicated events. Using the code like the following you can subscribe to these\nevents and handle them:\n\n```javascript\n$('#contact-form').on('beforeSubmit', function (e) {\n\tif (!confirm(\"Everything is correct. Submit?\")) {\n\t\treturn false;\n\t}\n\treturn true;\n});\n```\n\nIn the following we'll review events available.\n\n### `beforeValidate`\n\n`beforeValidate` is triggered before validating the whole form.\n\nThe signature of the event handler should be:\n\n```javascript\nfunction (event, messages, deferreds)\n```\n\nwhere\n\n- `event`: an Event object.\n- `messages`: an associative array with keys being attribute IDs and values being error message arrays\n   for the corresponding attributes.\n- `deferreds`: an array of Deferred objects. You can use `deferreds.add(callback)` to add a new\n   deferred validation.\n\nIf the handler returns a boolean `false`, it will stop further form validation after this event. And as\na result, `afterValidate` event will not be triggered.\n\n### `afterValidate`\n\n`afterValidate` event is triggered after validating the whole form.\n\nThe signature of the event handler should be:\n\n```javascript\nfunction (event, messages, errorAttributes)\n```\n\nwhere\n\n- `event`: an Event object.\n- `messages`: an associative array with keys being attribute IDs and values being error message arrays\n   for the corresponding attributes.\n- `errorAttributes`: an array of attributes that have validation errors. Please refer to\n  `attributeDefaults` for the structure of this parameter.\n\n### `beforeValidateAttribute`\n\n`beforeValidateAttribute` event is triggered before validating an attribute.\nThe signature of the event handler should be:\n\n```javascript\nfunction (event, attribute, messages, deferreds)\n```\n     \nwhere\n\n- `event`: an Event object.\n- `attribute`: the attribute to be validated. Please refer to `attributeDefaults` for the structure\n   of this parameter.\n- `messages`: an array to which you can add validation error messages for the specified attribute.\n- `deferreds`: an array of Deferred objects. You can use `deferreds.add(callback)` to add\n   a new deferred validation.\n\nIf the handler returns a boolean `false`, it will stop further validation of the specified attribute.\nAnd as a result, `afterValidateAttribute` event will not be triggered.\n\n### `afterValidateAttribute`\n\n`afterValidateAttribute` event is triggered after validating the whole form and each attribute.\n\nThe signature of the event handler should be:\n\n```javascript\nfunction (event, attribute, messages)\n```\n\nwhere\n\n- `event`: an Event object.\n- `attribute`: the attribute being validated. Please refer to `attributeDefaults` for the structure\n   of this parameter.\n- `messages`: an array to which you can add additional validation error messages for the specified\n   attribute.\n\n### `beforeSubmit`\n\n`beforeSubmit` event is triggered before submitting the form after all validations have passed.\n\nThe signature of the event handler should be:\n\n```javascript\nfunction (event)\n```\n\nwhere event is an Event object.\n\nIf the handler returns a boolean `false`, it will stop form submission.\n\n### `ajaxBeforeSend`\n         \n`ajaxBeforeSend` event is triggered before sending an AJAX request for AJAX-based validation.\n\nThe signature of the event handler should be:\n\n```javascript\nfunction (event, jqXHR, settings)\n```\n\nwhere\n\n- `event`: an Event object.\n- `jqXHR`: a jqXHR object\n- `settings`: the settings for the AJAX request\n\n### `ajaxComplete`\n\n`ajaxComplete` event is triggered after completing an AJAX request for AJAX-based validation.\n\nThe signature of the event handler should be:\n\n```javascript\nfunction (event, jqXHR, textStatus)\n```\n\nwhere\n\n- `event`: an Event object.\n- `jqXHR`: a jqXHR object\n- `textStatus`: the status of the request (\"success\", \"notmodified\", \"error\", \"timeout\",\n\"abort\", or \"parsererror\").\n\n## Submitting the form via AJAX\n\nWhile validation can be made on client side or via AJAX request, the form submission itself is done\nas a normal request by default. If you want the form to be submitted via AJAX, you can achieve this\nby handling the `beforeSubmit` event of the form in the following way:\n\n```javascript\nvar $form = $('#formId');\n$form.on('beforeSubmit', function() {\n    var data = $form.serialize();\n    $.ajax({\n        url: $form.attr('action'),\n        type: 'POST',\n        data: data,\n        success: function (data) {\n            // Implement successful\n        },\n        error: function(jqXHR, errMsg) {\n            alert(errMsg);\n        }\n     });\n     return false; // prevent default submit\n});\n```\n\nTo learn more about the jQuery `ajax()` function, please refer to the [jQuery documentation](https://api.jquery.com/jQuery.ajax/).\n\n\n## Adding fields dynamically\n\nIn modern web applications you often have the need of changing a form after it has been displayed to the user.\nThis can for example be the addition of new fields after click on a \"plus\"-icon.\nTo enable client validation for these fields, they have to be registered with the ActiveForm JavaScript plugin.\n\nYou have to add a field itself and then add it to validation list:\n\n```javascript\n$('#contact-form').yiiActiveForm('add', {\n    id: 'address',\n    name: 'address',\n    container: '.field-address',\n    input: '#address',\n    error: '.help-block',\n    validate:  function (attribute, value, messages, deferred, $form) {\n        yii.validation.required(value, messages, {message: \"Validation Message Here\"});\n    }\n});\n```\n\nTo remove a field from validation list so it's not validated you can do the following:\n\n```javascript\n$('#contact-form').yiiActiveForm('remove', 'address');\n```\n"
  },
  {
    "path": "docs/guide/input-forms.md",
    "content": "Creating Forms\n==============\n\nActiveRecord based forms: ActiveForm\n-----------------------\nThe primary way of using forms in Yii is through [[yii\\widgets\\ActiveForm]]. This approach should be preferred when\nthe form is based upon a model. Additionally, there are some useful methods in [[yii\\helpers\\Html]] that are typically\nused for adding buttons and help text to any form.\n\nA form, that is displayed on the client-side, will in most cases have a corresponding [model](structure-models.md) which is used\nto validate its input on the server-side (Check the [Validating Input](input-validation.md) section for more details on validation).\nWhen creating model-based forms, the first step is to define the model itself. The model can be either based upon\nan [Active Record](db-active-record.md) class, representing some data from the database, or a generic Model class\n(extending from [[yii\\base\\Model]]) to capture arbitrary input, for example a login form.\n\n> Tip: If the form fields are different from database columns or there are formatting and logic that is specific to that\n> form only, prefer creating a separate model extended from [[yii\\base\\Model]].\n\nIn the following example, we show how a generic model can be used for a login form:\n\n```php\n<?php\n\nclass LoginForm extends \\yii\\base\\Model\n{\n    public $username;\n    public $password;\n\n    public function rules()\n    {\n        return [\n            // define validation rules here\n        ];\n    }\n}\n```\n\nIn the controller, we will pass an instance of that model to the view, wherein the [[yii\\widgets\\ActiveForm|ActiveForm]]\nwidget is used to display the form:\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n$form = ActiveForm::begin([\n    'id' => 'login-form',\n    'options' => ['class' => 'form-horizontal'],\n]) ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n\n    <div class=\"form-group\">\n        <div class=\"col-lg-offset-1 col-lg-11\">\n            <?= Html::submitButton('Login', ['class' => 'btn btn-primary']) ?>\n        </div>\n    </div>\n<?php ActiveForm::end() ?>\n```\n\n### Wrapping with `begin()` and `end()` <span id=\"wrapping-with-begin-and-end\"></span>\nIn the above code, [[yii\\widgets\\ActiveForm::begin()|ActiveForm::begin()]] not only creates a form instance, but also marks the beginning of the form.\nAll of the content placed between [[yii\\widgets\\ActiveForm::begin()|ActiveForm::begin()]] and\n[[yii\\widgets\\ActiveForm::end()|ActiveForm::end()]] will be wrapped within the HTML `<form>` tag.\nAs with any widget, you can specify some options as to how the widget should be configured by passing an array to\nthe `begin` method. In this case, an extra CSS class and identifying ID are passed to be used in the opening `<form>` tag.\nFor all available options, please refer to the API documentation of [[yii\\widgets\\ActiveForm]].\n\n### ActiveField <span id=\"activefield\"></span>\nIn order to create a form element in the form, along with the element's label, and any applicable JavaScript validation,\nthe [[yii\\widgets\\ActiveForm::field()|ActiveForm::field()]] method is called, which returns an instance of [[yii\\widgets\\ActiveField]].\nWhen the result of this method is echoed directly, the result is a regular (text) input.\nTo customize the output, you can chain additional methods of [[yii\\widgets\\ActiveField|ActiveField]] to this call:\n\n```php\n// a password input\n<?= $form->field($model, 'password')->passwordInput() ?>\n// adding a hint and a customized label\n<?= $form->field($model, 'username')->textInput()->hint('Please enter your name')->label('Name') ?>\n// creating a HTML5 email input element\n<?= $form->field($model, 'email')->input('email') ?>\n```\n\nThis will create all the `<label>`, `<input>` and other tags according to the [[yii\\widgets\\ActiveField::$template|template]] defined by the form field.\nThe name of the input field is determined automatically from the model's [[yii\\base\\Model::formName()|form name]] and the attribute name.\nFor example, the name for the input field for the `username` attribute in the above example will be `LoginForm[username]`. This naming rule will result in an array\nof all attributes for the login form to be available in `$_POST['LoginForm']` on the server-side.\n\n> Tip: If you have only one model in a form and want to simplify the input names you may skip the array part by\n> overriding the [[yii\\base\\Model::formName()|formName()]] method of the model to return an empty string.\n> This can be useful for filter models used in the [GridView](output-data-widgets.md#grid-view) to create nicer URLs.\n\nSpecifying the attribute of the model can be done in more sophisticated ways. For example when an attribute may\ntake an array value when uploading multiple files or selecting multiple items you may specify it by appending `[]`\nto the attribute name:\n\n```php\n// allow multiple files to be uploaded:\necho $form->field($model, 'uploadFile[]')->fileInput(['multiple'=>'multiple']);\n\n// allow multiple items to be checked:\necho $form->field($model, 'items[]')->checkboxList(['a' => 'Item A', 'b' => 'Item B', 'c' => 'Item C']);\n```\n\nBe careful when naming form elements such as submit buttons. According to the [jQuery documentation](https://api.jquery.com/submit/) there\nare some reserved names that can cause conflicts:\n\n> Forms and their child elements should not use input names or ids that conflict with properties of a form,\n> such as `submit`, `length`, or `method`. Name conflicts can cause confusing failures.\n> For a complete list of rules and to check your markup for these problems, see [DOMLint](https://kangax.github.io/domlint/).\n\nAdditional HTML tags can be added to the form using plain HTML or using the methods from the [[yii\\helpers\\Html|Html]]-helper\nclass like it is done in the above example with [[yii\\helpers\\Html::submitButton()|Html::submitButton()]].\n\n\n> Tip: If you are using Twitter Bootstrap CSS in your application you may want to use\n> [[yii\\bootstrap\\ActiveForm]] instead of [[yii\\widgets\\ActiveForm]]. The former extends from the latter and\n> uses Bootstrap-specific styles when generating form input fields.\n\n\n> Tip: In order to style required fields with asterisks, you can use the following CSS:\n>\n> ```css\n> div.required label.control-label:after {\n>     content: \" *\";\n>     color: red;\n> }\n> ```\n\nCreating Lists <span id=\"creating-activeform-lists\"></span>\n-----------------------\n\nThere are 3 types of lists:\n* Dropdown lists \n* Radio lists\n* Checkbox lists\n\nTo create a list, you have to prepare the items. This can be done manually:\n\n```php\n$items = [\n    1 => 'item 1', \n    2 => 'item 2'\n]\n```\n\nor by retrieval from the DB:\n\n```php\n$items = Category::find()\n        ->select(['label'])\n        ->indexBy('id')\n        ->column();\n```\n\nThese `$items` have to be processed by the different list widgets.\nThe value of the form field (and the current active item) will be automatically set \nby the current value of the `$model`'s attribute. \n\n#### Creating a drop-down list <span id=\"creating-activeform-dropdownlist\"></span>\n\nWe can use ActiveField [[\\yii\\widgets\\ActiveField::dropDownList()]] method to create a drop-down list:\n\n```php\n/** @var \\yii\\widgets\\ActiveForm $form */\n\necho $form->field($model, 'category')->dropdownList([\n        1 => 'item 1', \n        2 => 'item 2'\n    ],\n    ['prompt'=>'Select Category']\n);\n```\n\n#### Creating a radio list <span id=\"creating-activeform-radioList\"></span>\n\nWe can use ActiveField [[\\yii\\widgets\\ActiveField::radioList()]] method to create a radio list:\n\n```php\n/** @var \\yii\\widgets\\ActiveForm $form */\n\necho $form->field($model, 'category')->radioList([\n    1 => 'radio 1', \n    2 => 'radio 2'\n]);\n```\n\n#### Creating a checkbox List <span id=\"creating-activeform-checkboxList\"></span>\n\nWe can use ActiveField [[\\yii\\widgets\\ActiveField::checkboxList()]] method to create a checkbox list:\n\n```php\n/** @var \\yii\\widgets\\ActiveForm $form */\n\necho $form->field($model, 'category')->checkboxList([\n    1 => 'checkbox 1', \n    2 => 'checkbox 2'\n]);\n```\n\n\nWorking with Pjax <span id=\"working-with-pjax\"></span>\n-----------------------\n\nThe [[yii\\widgets\\Pjax|Pjax]] widget allows you to update a certain section of a\npage instead of reloading the entire page. You can use it to update only the form\nand replace its contents after the submission.\n\nYou can configure [[yii\\widgets\\Pjax::$formSelector|$formSelector]] to specify\nwhich form submission may trigger pjax. If not set, all forms with `data-pjax`\nattribute within the enclosed content of Pjax will trigger pjax requests.\n\n```php\nuse yii\\widgets\\Pjax;\nuse yii\\widgets\\ActiveForm;\n\nPjax::begin([\n    // Pjax options\n]);\n    $form = ActiveForm::begin([\n        'options' => ['data' => ['pjax' => true]],\n        // more ActiveForm options\n    ]);\n\n        // ActiveForm content\n\n    ActiveForm::end();\nPjax::end();\n```\n> Tip: Be careful with the links inside the [[yii\\widgets\\Pjax|Pjax]] widget since\n> the response  will also be rendered inside the widget. To prevent this, use the\n> `data-pjax=\"0\"` HTML attribute.\n\n#### Values in Submit Buttons and File Upload\n\nThere are known issues using `jQuery.serializeArray()` when dealing with\n[files](https://github.com/jquery/jquery/issues/2321) and\n[submit button values](https://github.com/jquery/jquery/issues/2321) which\nwon't be solved and are instead deprecated in favor of the `FormData` class\nintroduced in HTML5.\n\nThat means the only official support for files and submit button values with\najax or using the [[yii\\widgets\\Pjax|Pjax]] widget depends on the\n[browser support](https://developer.mozilla.org/en-US/docs/Web/API/FormData#browser_compatibility)\nfor the `FormData` class.\n\nFurther Reading <span id=\"further-reading\"></span>\n---------------\n\nThe next section [Validating Input](input-validation.md) handles the validation of the submitted form data on the server-side as well as ajax and client-side validation.\n\nTo read about more complex usage of forms, you may want to check out the following sections:\n\n- [Collecting Tabular Input](input-tabular-input.md) for collecting data for multiple models of the same kind.\n- [Getting Data for Multiple Models](input-multiple-models.md) for handling multiple different models in the same form.\n- [Uploading Files](input-file-upload.md) on how to use forms for uploading files.\n"
  },
  {
    "path": "docs/guide/input-multiple-models.md",
    "content": "Getting Data for Multiple Models\n================================\n\nWhen dealing with some complex data, it is possible that you may need to use multiple different models to collect\nthe user input. For example, assuming the user login information is stored in the `user` table while the user profile\ninformation is stored in the `profile` table, you may want to collect the input data about a user through a `User` model \nand a `Profile` model. With the Yii model and form support, you can solve this problem in a way that is not much\ndifferent from handling a single model.\n\nIn the following, we will show how you can create a form that would allow you to collect data for both `User` and `Profile`\nmodels.\n\nFirst, the controller action for collecting the user and profile data can be written as follows, \n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\base\\Model;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\nuse app\\models\\User;\nuse app\\models\\Profile;\n\nclass UserController extends Controller\n{\n    public function actionUpdate($id)\n    {\n        $user = User::findOne($id);\n        if (!$user) {\n            throw new NotFoundHttpException(\"The user was not found.\");\n        }\n        \n        $profile = Profile::findOne($user->profile_id);\n        \n        if (!$profile) {\n            throw new NotFoundHttpException(\"The user has no profile.\");\n        }\n        \n        $user->scenario = 'update';\n        $profile->scenario = 'update';\n        \n        if ($user->load(Yii::$app->request->post()) && $profile->load(Yii::$app->request->post())) {\n            $isValid = $user->validate();\n            $isValid = $profile->validate() && $isValid;\n            if ($isValid) {\n                $user->save(false);\n                $profile->save(false);\n                return $this->redirect(['user/view', 'id' => $id]);\n            }\n        }\n        \n        return $this->render('update', [\n            'user' => $user,\n            'profile' => $profile,\n        ]);\n    }\n}\n```\n\nIn the `update` action, we first load the `$user` and `$profile` models to be updated from the database. We then call \n[[yii\\base\\Model::load()]] to populate these two models with the user input. If loading is successful, we will validate\nthe two models and then save them &mdash; please note that we use `save(false)` to skip over validations inside the models\nas the user input data have already been validated. If loading is not successful, we will render the `update` view which\nhas the following content:\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n$form = ActiveForm::begin([\n    'id' => 'user-update-form',\n    'options' => ['class' => 'form-horizontal'],\n]) ?>\n    <?= $form->field($user, 'username') ?>\n\n    ...other input fields...\n    \n    <?= $form->field($profile, 'website') ?>\n\n    <?= Html::submitButton('Update', ['class' => 'btn btn-primary']) ?>\n<?php ActiveForm::end() ?>\n```\n\nAs you can see, in the `update` view you would render input fields using two models `$user` and `$profile`.\n"
  },
  {
    "path": "docs/guide/input-tabular-input.md",
    "content": "Collecting Tabular Input\n========================\n\nSometimes you need to handle multiple models of the same kind in a single form. For example, multiple settings, where\neach setting is stored as a name-value pair and is represented by a `Setting` [active record](db-active-record.md) model.\nThis kind of form is also often referred to as \"tabular input\".\nIn contrast to this, handling different models of different kind, is handled in the section\n[Complex Forms with Multiple Models](input-multiple-models.md).\n\nThe following shows how to implement tabular input with Yii.\n\nThere are three different situations to cover, which have to be handled slightly different:\n- Updating a fixed set of records from the database\n- Creating a dynamic set of new records\n- Updating, creating and deleting of records on one page\n\nIn contrast to the single model forms explained before, we are working with an array of models now.\nThis array is passed to the view to display the input fields for each model in a table like style and we\nwill use helper methods of [[yii\\base\\Model]] that allow loading and validating multiple models at once:\n\n- [[yii\\base\\Model::loadMultiple()|Model::loadMultiple()]] load post data into an array of models.\n- [[yii\\base\\Model::validateMultiple()|Model::validateMultiple()]] validates an array of models.\n\n### Updating a fixed set of records\n\nLet's start with the controller action:\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\nuse app\\models\\Setting;\n\nclass SettingsController extends Controller\n{\n    // ...\n\n    public function actionUpdate()\n    {\n        $settings = Setting::find()->indexBy('id')->all();\n\n        if ($this->request->isPost) {\n            if (Setting::loadMultiple($settings, $this->request->post()) && Setting::validateMultiple($settings)) {\n                foreach ($settings as $setting) {\n                    $setting->save(false);\n                }\n                return $this->redirect('index');\n            }\n        }\n\n        return $this->render('update', ['settings' => $settings]);\n    }\n}\n```\n\nIn the code above we're using [[yii\\db\\ActiveQuery::indexBy()|indexBy()]] when retrieving models from the database to populate an array indexed by models primary keys.\nThese will be later used to identify form fields. [[yii\\base\\Model::loadMultiple()|Model::loadMultiple()]] fills multiple\nmodels with the form data coming from POST\nand [[yii\\base\\Model::validateMultiple()|Model::validateMultiple()]] validates all models at once.\nAs we have validated our models before, using `validateMultiple()`, we're now passing `false` as\na parameter to [[yii\\db\\ActiveRecord::save()|save()]] to not run validation twice.\n\nNow the form that's in `update` view:\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n$form = ActiveForm::begin();\n\nforeach ($settings as $id => $setting) {\n    echo $form->field($setting, \"[$id]value\")->label($setting->name);\n}\n\necho Html::submitButton('Save');\n\nActiveForm::end();\n```\n\nHere for each setting we are rendering name and an input with a value. It is important to add a proper index\nto input name since that is how [[yii\\base\\Model::loadMultiple()|Model::loadMultiple()]] determines which model to fill with which values.\n\n### Creating a dynamic set of new records\n\nCreating new records is similar to updating, except the part, where we instantiate the models:\n\n```php\npublic function actionCreate()\n{\n    $settings = [];\n    if ($this->request->isPost) {\n        $count = count($this->request->post($setting->tableName()));\n        for ($i = 0; $i < $count; $i++) {\n            $settings[$i] = new Setting();\n        }\n        if (Setting::loadMultiple($settings, $this->request->post()) && Setting::validateMultiple($settings)) {\n            foreach ($settings as $setting) {\n                $setting->save(false);\n            }\n            return $this->redirect('index');\n        }\n    }\n    $settings[] = new Setting();\n\n    return $this->render('create', ['settings' => $settings]);\n}\n```\n\nHere we create an initial `$settings` array containing one model by default so that always at least one text field will be\nvisible in the view. Additionally we add more models for each line of input we may have received.\n\nIn the view you can use JavaScript to add new input lines dynamically.\n\n### Combining Update, Create and Delete on one page\n\n> Note: This section is under development.\n>\n> It has no content yet.\n\nTBD\n"
  },
  {
    "path": "docs/guide/input-validation.md",
    "content": "Validating Input\n================\n\nAs a rule of thumb, you should never trust the data received from end users and should always validate it\nbefore putting it to good use.\n\nGiven a [model](structure-models.md) populated with user inputs, you can validate the inputs by calling the\n[[yii\\base\\Model::validate()]] method. The method will return a boolean value indicating whether the validation\nsucceeded or not. If not, you may get the error messages from the [[yii\\base\\Model::errors]] property. For example,\n\n```php\n$model = new \\app\\models\\ContactForm();\n\n// populate model attributes with user inputs\n$model->load(\\Yii::$app->request->post());\n// which is equivalent to the following:\n// $model->attributes = \\Yii::$app->request->post('ContactForm');\n\nif ($model->validate()) {\n    // all inputs are valid\n} else {\n    // validation failed: $errors is an array containing error messages\n    $errors = $model->errors;\n}\n```\n\n\n## Declaring Rules <span id=\"declaring-rules\"></span>\n\nTo make `validate()` really work, you should declare validation rules for the attributes you plan to validate.\nThis should be done by overriding the [[yii\\base\\Model::rules()]] method. The following example shows how\nthe validation rules for the `ContactForm` model are declared:\n\n```php\npublic function rules()\n{\n    return [\n        // the name, email, subject and body attributes are required\n        [['name', 'email', 'subject', 'body'], 'required'],\n\n        // the email attribute should be a valid email address\n        ['email', 'email'],\n    ];\n}\n```\n\nThe [[yii\\base\\Model::rules()|rules()]] method should return an array of rules, each of which is an array\nof the following format:\n\n```php\n[\n    // required, specifies which attributes should be validated by this rule.\n    // For a single attribute, you can use the attribute name directly\n    // without having it in an array\n    ['attribute1', 'attribute2', ...],\n\n    // required, specifies the type of this rule.\n    // It can be a class name, validator alias, or a validation method name\n    'validator',\n\n    // optional, specifies in which scenario(s) this rule should be applied\n    // if not given, it means the rule applies to all scenarios\n    // You may also configure the \"except\" option if you want to apply the rule\n    // to all scenarios except the listed ones\n    'on' => ['scenario1', 'scenario2', ...],\n\n    // optional, specifies additional configurations for the validator object\n    'property1' => 'value1', 'property2' => 'value2', ...\n]\n```\n\nFor each rule you must specify at least which attributes the rule applies to and what is the type of the rule.\nYou can specify the rule type in one of the following forms:\n\n* the alias of a core validator, such as `required`, `in`, `date`, etc. Please refer to\n  the [Core Validators](tutorial-core-validators.md) for the complete list of core validators.\n* the name of a validation method in the model class, or an anonymous function. Please refer to the\n  [Inline Validators](#inline-validators) subsection for more details.\n* a fully qualified validator class name. Please refer to the [Standalone Validators](#standalone-validators)\n  subsection for more details.\n\nA rule can be used to validate one or multiple attributes, and an attribute may be validated by one or multiple rules.\nA rule may be applied in certain [scenarios](structure-models.md#scenarios) only by specifying the `on` option.\nIf you do not specify an `on` option, it means the rule will be applied to all scenarios.\n\nWhen the `validate()` method is called, it does the following steps to perform validation:\n\n1. Determine which attributes should be validated by getting the attribute list from [[yii\\base\\Model::scenarios()]]\n   using the current [[yii\\base\\Model::scenario|scenario]]. These attributes are called *active attributes*.\n2. Determine which validation rules should be used by getting the rule list from [[yii\\base\\Model::rules()]]\n   using the current [[yii\\base\\Model::scenario|scenario]]. These rules are called *active rules*.\n3. Use each active rule to validate each active attribute which is associated with the rule.\n   The validation rules are evaluated in the order they are listed.\n\nAccording to the above validation steps, an attribute will be validated if and only if it is\nan active attribute declared in `scenarios()` and is associated with one or multiple active rules\ndeclared in `rules()`.\n\n> Note: It is handy to give names to rules i.e.\n>\n> ```php\n> public function rules()\n> {\n>     return [\n>         // ...\n>         'password' => [['password'], 'string', 'max' => 60],\n>     ];\n> }\n> ```\n>\n> You can use it in a child model:\n>\n> ```php\n> public function rules()\n> {\n>     $rules = parent::rules();\n>     unset($rules['password']);\n>     return $rules;\n> }\n\n\n### Customizing Error Messages <span id=\"customizing-error-messages\"></span>\n\nMost validators have default error messages that will be added to the model being validated when its attributes\nfail the validation. For example, the [[yii\\validators\\RequiredValidator|required]] validator will add\na message \"Username cannot be blank.\" to a model when the `username` attribute fails the rule using this validator.\n\nYou can customize the error message of a rule by specifying the `message` property when declaring the rule,\nlike the following,\n\n```php\npublic function rules()\n{\n    return [\n        ['username', 'required', 'message' => 'Please choose a username.'],\n    ];\n}\n```\n\nSome validators may support additional error messages to more precisely describe different causes of\nvalidation failures. For example, the [[yii\\validators\\NumberValidator|number]] validator supports\n[[yii\\validators\\NumberValidator::tooBig|tooBig]] and [[yii\\validators\\NumberValidator::tooSmall|tooSmall]]\nto describe the validation failure when the value being validated is too big and too small, respectively.\nYou may configure these error messages like configuring other properties of validators in a validation rule.\n\n\n### Validation Events <span id=\"validation-events\"></span>\n\nWhen [[yii\\base\\Model::validate()]] is called, it will call two methods that you may override to customize\nthe validation process:\n\n* [[yii\\base\\Model::beforeValidate()]]: the default implementation will trigger a [[yii\\base\\Model::EVENT_BEFORE_VALIDATE]]\n  event. You may either override this method or respond to this event to do some preprocessing work\n  (e.g. normalizing data inputs) before the validation occurs. The method should return a boolean value indicating\n  whether the validation should proceed or not.\n* [[yii\\base\\Model::afterValidate()]]: the default implementation will trigger a [[yii\\base\\Model::EVENT_AFTER_VALIDATE]]\n  event. You may either override this method or respond to this event to do some postprocessing work after\n  the validation is completed.\n\n\n### Conditional Validation <span id=\"conditional-validation\"></span>\n\nTo validate attributes only when certain conditions apply, e.g. the validation of one attribute depends\non the value of another attribute you can use the [[yii\\validators\\Validator::when|when]] property\nto define such conditions. For example,\n\n```php\n    ['state', 'required', 'when' => function($model) {\n        return $model->country == 'USA';\n    }]\n```\n\nThe [[yii\\validators\\Validator::when|when]] property takes a PHP callable with the following signature:\n\n```php\n/**\n * @param Model $model the model being validated\n * @param string $attribute the attribute being validated\n * @return bool whether the rule should be applied\n */\nfunction ($model, $attribute)\n```\n\nIf you also need to support client-side conditional validation, you should configure\nthe [[yii\\validators\\Validator::whenClient|whenClient]] property which takes a string representing a JavaScript\nfunction whose return value determines whether to apply the rule or not. For example,\n\n```php\n    ['state', 'required', 'when' => function ($model) {\n        return $model->country == 'USA';\n    }, 'whenClient' => \"function (attribute, value) {\n        return $('#country').val() == 'USA';\n    }\"]\n```\n\n\n### Data Filtering <span id=\"data-filtering\"></span>\n\nUser inputs often need to be filtered or preprocessed. For example, you may want to trim the spaces around the\n`username` input. You may use validation rules to achieve this goal.\n\nThe following examples shows how to trim the spaces in the inputs and turn empty inputs into nulls by using\nthe [trim](tutorial-core-validators.md#trim) and [default](tutorial-core-validators.md#default) core validators:\n\n```php\nreturn [\n    [['username', 'email'], 'trim'],\n    [['username', 'email'], 'default'],\n];\n```\n\nYou may also use the more general [filter](tutorial-core-validators.md#filter) validator to perform more complex\ndata filtering.\n\nAs you can see, these validation rules do not really validate the inputs. Instead, they will process the values\nand save them back to the attributes being validated.\n\nA complete processing of user input is shown in the following example code, which will ensure only integer\nvalues are stored in an attribute:\n\n```php\n['age', 'trim'],\n['age', 'default', 'value' => null],\n['age', 'integer', 'min' => 0],\n['age', 'filter', 'filter' => 'intval', 'skipOnEmpty' => true],\n```\n\nThe above code will perform the following operations on the input:\n\n1. Trim whitespace from the input value.\n2. Make sure empty input is stored as `null` in the database; we differentiate between a value being \"not set\"\n   and the actual value `0`. If `null` is not allowed you can set another default value here.\n3. Validate that the value is an integer greater than 0 if it is not empty. Normal validators have\n   [[yii\\validators\\Validator::$skipOnEmpty|$skipOnEmpty]] set to `true`.\n4. Make sure the value is of type integer, e.g. casting a string `'42'` to integer `42`.\n   Here we set [[yii\\validators\\FilterValidator::$skipOnEmpty|$skipOnEmpty]] to `true`, which is `false` by default\n   on the [[yii\\validators\\FilterValidator|filter]] validator.\n\n### Handling Empty Inputs <span id=\"handling-empty-inputs\"></span>\n\nWhen input data are submitted from HTML forms, you often need to assign some default values to the inputs\nif they are empty. You can do so by using the [default](tutorial-core-validators.md#default) validator. For example,\n\n```php\nreturn [\n    // set \"username\" and \"email\" as null if they are empty\n    [['username', 'email'], 'default'],\n\n    // set \"level\" to be 1 if it is empty\n    ['level', 'default', 'value' => 1],\n];\n```\n\nBy default, an input is considered empty if its value is an empty string, an empty array or a `null`.\nYou may customize the default empty detection logic by configuring the [[yii\\validators\\Validator::isEmpty]] property\nwith a PHP callable. For example,\n\n```php\n    ['agree', 'required', 'isEmpty' => function ($value) {\n        return empty($value);\n    }]\n```\n\n> Note: Most validators do not handle empty inputs if their [[yii\\validators\\Validator::skipOnEmpty]] property takes\n  the default value `true`. They will simply be skipped during validation if their associated attributes receive empty\n  inputs. Among the [core validators](tutorial-core-validators.md), only the `captcha`, `default`, `filter`,\n  `required`, and `trim` validators will handle empty inputs.\n\n\n## Ad Hoc Validation <span id=\"ad-hoc-validation\"></span>\n\nSometimes you need to do *ad hoc validation* for values that are not bound to any model.\n\nIf you only need to perform one type of validation (e.g. validating email addresses), you may call\nthe [[yii\\validators\\Validator::validate()|validate()]] method of the desired validator, like the following:\n\n```php\n$email = 'test@example.com';\n$validator = new yii\\validators\\EmailValidator();\n\nif ($validator->validate($email, $error)) {\n    echo 'Email is valid.';\n} else {\n    echo $error;\n}\n```\n\n> Note: Not all validators support this type of validation. An example is the [unique](tutorial-core-validators.md#unique)\n  core validator which is designed to work with a model only.\n\n> Note: The [[yii\\base\\Validator::skipOnEmpty]] property is used for [[yii\\base\\Model]] validation only. Using it without a model has no effect.\n\nIf you need to perform multiple validations against several values, you can use [[yii\\base\\DynamicModel]]\nwhich supports declaring both attributes and rules on the fly. Its usage is like the following:\n\n```php\npublic function actionSearch($name, $email)\n{\n    $model = DynamicModel::validateData(['name' => $name, 'email' => $email], [\n        [['name', 'email'], 'string', 'max' => 128],\n        ['email', 'email'],\n    ]);\n\n    if ($model->hasErrors()) {\n        // validation fails\n    } else {\n        // validation succeeds\n    }\n}\n```\n\nThe [[yii\\base\\DynamicModel::validateData()]] method creates an instance of `DynamicModel`, defines the attributes\nusing the given data (`name` and `email` in this example), and then calls [[yii\\base\\Model::validate()]]\nwith the given rules.\n\nAlternatively, you may use the following more \"classic\" syntax to perform ad hoc data validation:\n\n```php\npublic function actionSearch($name, $email)\n{\n    $model = new DynamicModel(['name' => $name, 'email' => $email]);\n    $model->addRule(['name', 'email'], 'string', ['max' => 128])\n        ->addRule('email', 'email')\n        ->validate();\n\n    if ($model->hasErrors()) {\n        // validation fails\n    } else {\n        // validation succeeds\n    }\n}\n```\n\nAfter validation, you can check if the validation succeeded or not by calling the\n[[yii\\base\\DynamicModel::hasErrors()|hasErrors()]] method, and then get the validation errors from the\n[[yii\\base\\DynamicModel::errors|errors]] property, like you do with a normal model.\nYou may also access the dynamic attributes defined through the model instance, e.g.,\n`$model->name` and `$model->email`.\n\n\n## Creating Validators <span id=\"creating-validators\"></span>\n\nBesides using the [core validators](tutorial-core-validators.md) included in the Yii releases, you may also\ncreate your own validators. You may create inline validators or standalone validators.\n\n\n### Inline Validators <span id=\"inline-validators\"></span>\n\nAn inline validator is one defined in terms of a model method or an anonymous function. The signature of\nthe method/function is:\n\n```php\n/**\n * @param string $attribute the attribute currently being validated\n * @param mixed $params the value of the \"params\" given in the rule\n * @param \\yii\\validators\\InlineValidator $validator related InlineValidator instance.\n * This parameter is available since version 2.0.11.\n * @param mixed $current the currently validated value of attribute.\n * This parameter is available since version 2.0.36.\n */\nfunction ($attribute, $params, $validator, $current)\n```\n\nIf an attribute fails the validation, the method/function should call [[yii\\base\\Model::addError()]] to save\nthe error message in the model so that it can be retrieved back later to present to end users.\n\nBelow are some examples:\n\n```php\nuse yii\\base\\Model;\n\nclass MyForm extends Model\n{\n    public $country;\n    public $token;\n\n    public function rules()\n    {\n        return [\n            // an inline validator defined as the model method validateCountry()\n            ['country', 'validateCountry'],\n\n            // an inline validator defined as an anonymous function\n            ['token', function ($attribute, $params, $validator) {\n                if (!ctype_alnum($this->$attribute)) {\n                    $this->addError($attribute, 'The token must contain letters or digits.');\n                }\n            }],\n        ];\n    }\n\n    public function validateCountry($attribute, $params, $validator)\n    {\n        if (!in_array($this->$attribute, ['USA', 'Indonesia'])) {\n            $this->addError($attribute, 'The country must be either \"USA\" or \"Indonesia\".');\n        }\n    }\n}\n```\n\n> Note: Since version 2.0.11 you can use [[yii\\validators\\InlineValidator::addError()]] for adding errors instead. That way the error\n> message can be formatted using [[yii\\i18n\\I18N::format()]] right away. Use `{attribute}` and `{value}` in the error\n> message to refer to an attribute label (no need to get it manually) and attribute value accordingly:\n>\n> ```php\n> $validator->addError($this, $attribute, 'The value \"{value}\" is not acceptable for {attribute}.');\n> ```\n\n> Note: By default, inline validators will not be applied if their associated attributes receive empty inputs\n  or if they have already failed some validation rules. If you want to make sure a rule is always applied,\n  you may configure the [[yii\\validators\\Validator::skipOnEmpty|skipOnEmpty]] and/or [[yii\\validators\\Validator::skipOnError|skipOnError]]\n  properties to be `false` in the rule declarations. For example:\n>\n> ```php\n> [\n>     ['country', 'validateCountry', 'skipOnEmpty' => false, 'skipOnError' => false],\n> ]\n> ```\n\n\n### Standalone Validators <span id=\"standalone-validators\"></span>\n\nA standalone validator is a class extending [[yii\\validators\\Validator]] or its child class. You may implement\nits validation logic by overriding the [[yii\\validators\\Validator::validateAttribute()]] method. If an attribute\nfails the validation, call [[yii\\base\\Model::addError()]] to save the error message in the model, like you do\nwith [inline validators](#inline-validators).\n\n\nFor example, the inline validator above could be moved into new [[components/validators/CountryValidator]] class.\nIn this case we can use [[yii\\validators\\Validator::addError()]] to set customized message for the model.\n\n```php\nnamespace app\\components;\n\nuse yii\\validators\\Validator;\n\nclass CountryValidator extends Validator\n{\n    public function validateAttribute($model, $attribute)\n    {\n        if (!in_array($model->$attribute, ['USA', 'Indonesia'])) {\n            $this->addError($model, $attribute, 'The country must be either \"{country1}\" or \"{country2}\".', ['country1' => 'USA', 'country2' => 'Indonesia']);\n        }\n    }\n}\n```\n\nIf you want your validator to support validating a value without a model, you should also override\n[[yii\\validators\\Validator::validate()]]. You may also override [[yii\\validators\\Validator::validateValue()]]\ninstead of `validateAttribute()` and `validate()` because by default the latter two methods are implemented\nby calling `validateValue()`.\n\nBelow is an example of how you could use the above validator class within your model.\n\n```php\nnamespace app\\models;\n\nuse Yii;\nuse yii\\base\\Model;\nuse app\\components\\validators\\CountryValidator;\n\nclass EntryForm extends Model\n{\n    public $name;\n    public $email;\n    public $country;\n\n    public function rules()\n    {\n        return [\n            [['name', 'email'], 'required'],\n            ['country', CountryValidator::class],\n            ['email', 'email'],\n        ];\n    }\n}\n```\n\n\n## Multiple Attributes Validation <span id=\"multiple-attributes-validation\"></span>\n\nSometimes validators involve multiple attributes. Consider the following form:\n\n```php\nclass MigrationForm extends \\yii\\base\\Model\n{\n    /**\n     * Minimal funds amount for one adult person\n     */\n    const MIN_ADULT_FUNDS = 3000;\n    /**\n     * Minimal funds amount for one child\n     */\n    const MIN_CHILD_FUNDS = 1500;\n\n    public $personalSalary;\n    public $spouseSalary;\n    public $childrenCount;\n    public $description;\n\n    public function rules()\n    {\n        return [\n            [['personalSalary', 'description'], 'required'],\n            [['personalSalary', 'spouseSalary'], 'integer', 'min' => self::MIN_ADULT_FUNDS],\n            ['childrenCount', 'integer', 'min' => 0, 'max' => 5],\n            [['spouseSalary', 'childrenCount'], 'default', 'value' => 0],\n            ['description', 'string'],\n        ];\n    }\n}\n```\n\n### Creating validator <span id=\"multiple-attributes-validator\"></span>\n\nLet's say we need to check if the family income is enough for children. We can create inline validator\n`validateChildrenFunds` for that which will run only when `childrenCount` is more than 0.\n\nNote that we can't use all validated attributes (`['personalSalary', 'spouseSalary', 'childrenCount']`) when attaching\nvalidator. This is because the same validator will run for each attribute (3 times in total) and we only need to run it\nonce for the whole attribute set.\n\nYou can use any of these attributes instead (or use what you think is the most relevant):\n\n```php\n['childrenCount', 'validateChildrenFunds', 'when' => function ($model) {\n    return $model->childrenCount > 0;\n}],\n```\n\nImplementation of `validateChildrenFunds` can be like this:\n\n```php\npublic function validateChildrenFunds($attribute, $params)\n{\n    $totalSalary = $this->personalSalary + $this->spouseSalary;\n    // Double the minimal adult funds if spouse salary is specified\n    $minAdultFunds = $this->spouseSalary ? self::MIN_ADULT_FUNDS * 2 : self::MIN_ADULT_FUNDS;\n    $childFunds = $totalSalary - $minAdultFunds;\n    if ($childFunds / $this->childrenCount < self::MIN_CHILD_FUNDS) {\n        $this->addError('childrenCount', 'Your salary is not enough for children.');\n    }\n}\n```\n\nYou can ignore `$attribute` parameter because validation is not related to just one attribute.\n\n\n### Adding errors <span id=\"multiple-attributes-errors\"></span>\n\nAdding error in case of multiple attributes can vary depending on desired form design:\n\n- Select the most relevant field in your opinion and add error to it's attribute:\n\n```php\n$this->addError('childrenCount', 'Your salary is not enough for children.');\n```\n\n- Select multiple important relevant attributes or all attributes and add the same error message to them. We can store\nmessage in separate variable before passing it to `addError` to keep code DRY.\n\n```php\n$message = 'Your salary is not enough for children.';\n$this->addError('personalSalary', $message);\n$this->addError('wifeSalary', $message);\n$this->addError('childrenCount', $message);\n```\n\nOr use a loop:\n\n```php\n$attributes = ['personalSalary', 'wifeSalary', 'childrenCount'];\nforeach ($attributes as $attribute) {\n    $this->addError($attribute, 'Your salary is not enough for children.');\n}\n```\n\n- Add a common error (not related to particular attribute). We can use the not existing attribute name for adding\nerror, for example `*`, because attribute existence is not checked at that point.\n\n```php\n$this->addError('*', 'Your salary is not enough for children.');\n```\n\nAs a result, we will not see error message near form fields. To display it, we can include the error summary in view:\n\n```php\n<?= $form->errorSummary($model) ?>\n```\n\n> Note: Creating validator which validates multiple attributes at once is well described in the [community cookbook](https://github.com/samdark/yii2-cookbook/blob/master/book/forms-validator-multiple-attributes.md).\n\n## Client-Side Validation <span id=\"client-side-validation\"></span>\n\nClient-side validation based on JavaScript is desirable when end users provide inputs via HTML forms, because\nit allows users to find out input errors faster and thus provides a better user experience. You may use or implement\na validator that supports client-side validation *in addition to* server-side validation.\n\n> Info: While client-side validation is desirable, it is not a must. Its main purpose is to provide users with a better\n  experience. Similar to input data coming from end users, you should never trust client-side validation. For this reason,\n  you should always perform server-side validation by calling [[yii\\base\\Model::validate()]], as\n  described in the previous subsections.\n\n\n### Using Client-Side Validation <span id=\"using-client-side-validation\"></span>\n\nMany [core validators](tutorial-core-validators.md) support client-side validation out-of-the-box. All you need to do\nis just use [[yii\\widgets\\ActiveForm]] to build your HTML forms. For example, `LoginForm` below declares two\nrules: one uses the [required](tutorial-core-validators.md#required) core validator which is supported on both\nclient and server-sides; the other uses the `validatePassword` inline validator which is only supported on the server\nside.\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\nuse app\\models\\User;\n\nclass LoginForm extends Model\n{\n    public $username;\n    public $password;\n\n    public function rules()\n    {\n        return [\n            // username and password are both required\n            [['username', 'password'], 'required'],\n\n            // password is validated by validatePassword()\n            ['password', 'validatePassword'],\n        ];\n    }\n\n    public function validatePassword()\n    {\n        $user = User::findByUsername($this->username);\n\n        if (!$user || !$user->validatePassword($this->password)) {\n            $this->addError('password', 'Incorrect username or password.');\n        }\n    }\n}\n```\n\nThe HTML form built by the following code contains two input fields `username` and `password`.\nIf you submit the form without entering anything, you will find the error messages requiring you\nto enter something appear right away without any communication with the server.\n\n```php\n<?php $form = yii\\widgets\\ActiveForm::begin(); ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n    <?= Html::submitButton('Login') ?>\n<?php yii\\widgets\\ActiveForm::end(); ?>\n```\n\nBehind the scene, [[yii\\widgets\\ActiveForm]] will read the validation rules declared in the model\nand generate appropriate JavaScript code for validators that support client-side validation. When a user\nchanges the value of an input field or submit the form, the client-side validation JavaScript will be triggered.\n\nIf you want to turn off client-side validation completely, you may configure the\n[[yii\\widgets\\ActiveForm::enableClientValidation]] property to be `false`. You may also turn off client-side\nvalidation of individual input fields by configuring their [[yii\\widgets\\ActiveField::enableClientValidation]]\nproperty to be false. When `enableClientValidation` is configured at both the input field level and the form level,\nthe former will take precedence.\n\n> Info: Since version 2.0.11 all validators extending from [[yii\\validators\\Validator]] receive client-side options\n> from separate method - [[yii\\validators\\Validator::getClientOptions()]]. You can use it:\n>\n> - if you want to implement your own custom client-side validation but leave the synchronization with server-side\n> validator options;\n> - to extend or customize to fit your specific needs:\n>\n> ```php\n> public function getClientOptions($model, $attribute)\n> {\n>     $options = parent::getClientOptions($model, $attribute);\n>     // Modify $options here\n>\n>     return $options;\n> }\n> ```\n\n### Implementing Client-Side Validation <span id=\"implementing-client-side-validation\"></span>\n\nTo create a validator that supports client-side validation, you should implement the\n[[yii\\validators\\Validator::clientValidateAttribute()]] method which returns a piece of JavaScript code\nthat performs the validation on the client-side. Within the JavaScript code, you may use the following\npredefined variables:\n\n- `attribute`: the name of the attribute being validated.\n- `value`: the value being validated.\n- `messages`: an array used to hold the validation error messages for the attribute.\n- `deferred`: an array which deferred objects can be pushed into (explained in the next subsection).\n\nIn the following example, we create a `StatusValidator` which validates if an input is a valid status input\nagainst the existing status data. The validator supports both server-side and client-side validation.\n\n```php\nnamespace app\\components;\n\nuse yii\\validators\\Validator;\nuse app\\models\\Status;\n\nclass StatusValidator extends Validator\n{\n    public function init()\n    {\n        parent::init();\n        $this->message = 'Invalid status input.';\n    }\n\n    public function validateAttribute($model, $attribute)\n    {\n        $value = $model->$attribute;\n        if (!Status::find()->where(['id' => $value])->exists()) {\n            $model->addError($attribute, $this->message);\n        }\n    }\n\n    public function clientValidateAttribute($model, $attribute, $view)\n    {\n        $statuses = json_encode(Status::find()->select('id')->asArray()->column());\n        $message = json_encode($this->message, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);\n        return <<<JS\nif ($.inArray(value, $statuses) === -1) {\n    messages.push($message);\n}\nJS;\n    }\n}\n```\n\n> Tip: The above code is given mainly to demonstrate how to support client-side validation. In practice,\n> you may use the [in](tutorial-core-validators.md#in) core validator to achieve the same goal. You may\n> write the validation rule like the following:\n>\n> ```php\n> [\n>     ['status', 'in', 'range' => Status::find()->select('id')->asArray()->column()],\n> ]\n> ```\n\n> Tip: If you need to work with client validation manually i.e. dynamically add fields or do some custom UI logic, refer\n> to [Working with ActiveForm via JavaScript](https://github.com/samdark/yii2-cookbook/blob/master/book/forms-activeform-js.md)\n> in Yii 2.0 Cookbook.\n\n### Deferred Validation <span id=\"deferred-validation\"></span>\n\nIf you need to perform asynchronous client-side validation, you can create [Deferred objects](https://api.jquery.com/category/deferred-object/).\nFor example, to perform a custom AJAX validation, you can use the following code:\n\n```php\npublic function clientValidateAttribute($model, $attribute, $view)\n{\n    return <<<JS\n        deferred.push($.get(\"/check\", {value: value}).done(function(data) {\n            if ('' !== data) {\n                messages.push(data);\n            }\n        }));\nJS;\n}\n```\n\nIn the above, the `deferred` variable is provided by Yii, which is an array of Deferred objects. The `$.get()`\njQuery method creates a Deferred object which is pushed to the `deferred` array.\n\nYou can also explicitly create a Deferred object and call its `resolve()` method when the asynchronous callback\nis hit. The following example shows how to validate the dimensions of an uploaded image file on the client-side.\n\n```php\npublic function clientValidateAttribute($model, $attribute, $view)\n{\n    return <<<JS\n        var def = $.Deferred();\n        var img = new Image();\n        img.onload = function() {\n            if (this.width > 150) {\n                messages.push('Image too wide!!');\n            }\n            def.resolve();\n        }\n        var reader = new FileReader();\n        reader.onloadend = function() {\n            img.src = reader.result;\n        }\n        reader.readAsDataURL(file);\n\n        deferred.push(def);\nJS;\n}\n```\n\n> Note: The `resolve()` method must be called after the attribute has been validated. Otherwise the main form\n  validation will not complete.\n\nFor simplicity, the `deferred` array is equipped with a shortcut method `add()` which automatically creates a Deferred\nobject and adds it to the `deferred` array. Using this method, you can simplify the above example as follows,\n\n```php\npublic function clientValidateAttribute($model, $attribute, $view)\n{\n    return <<<JS\n        deferred.add(function(def) {\n            var img = new Image();\n            img.onload = function() {\n                if (this.width > 150) {\n                    messages.push('Image too wide!!');\n                }\n                def.resolve();\n            }\n            var reader = new FileReader();\n            reader.onloadend = function() {\n                img.src = reader.result;\n            }\n            reader.readAsDataURL(file);\n        });\nJS;\n}\n```\n\n\n## AJAX Validation <span id=\"ajax-validation\"></span>\n\nSome validations can only be done on the server-side, because only the server has the necessary information.\nFor example, to validate if a username is unique or not, it is necessary to check the user table on the server-side.\nYou can use AJAX-based validation in this case. It will trigger an AJAX request in the background to validate the\ninput while keeping the same user experience as the regular client-side validation.\n\nTo enable AJAX validation for a single input field, configure the [[yii\\widgets\\ActiveField::enableAjaxValidation|enableAjaxValidation]]\nproperty of that field to be `true` and specify a unique form `id`:\n\n```php\nuse yii\\widgets\\ActiveForm;\n\n$form = ActiveForm::begin([\n    'id' => 'registration-form',\n]);\n\necho $form->field($model, 'username', ['enableAjaxValidation' => true]);\n\n// ...\n\nActiveForm::end();\n```\n\nTo enable AJAX validation for all inputs of the form, configure [[yii\\widgets\\ActiveForm::enableAjaxValidation|enableAjaxValidation]]\nto be `true` at the form level:\n\n```php\n$form = ActiveForm::begin([\n    'id' => 'contact-form',\n    'enableAjaxValidation' => true,\n]);\n```\n\n> Note: When the `enableAjaxValidation` property is configured at both the input field level and the form level,\n  the former will take precedence.\n\nYou also need to prepare the server so that it can handle the AJAX validation requests.\nThis can be achieved by a code snippet like the following in the controller actions:\n\n```php\nif (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {\n    Yii::$app->response->format = Response::FORMAT_JSON;\n    return ActiveForm::validate($model);\n}\n```\n\nThe above code will check whether the current request is an AJAX. If yes, it will respond to\nthis request by running the validation and returning the errors in JSON format.\n\n> Info: You can also use [Deferred Validation](#deferred-validation) to perform AJAX validation.\n  However, the AJAX validation feature described here is more systematic and requires less coding effort.\n\nWhen both `enableClientValidation` and `enableAjaxValidation` are set to `true`, AJAX validation request will be triggered\nonly after the successful client validation. Note that in case of validating a single field that happens if either\n`validateOnChange`, `validateOnBlur` or `validateOnType` is set to `true`, AJAX request will be sent when the field in\nquestion alone successfully passes client validation. \n"
  },
  {
    "path": "docs/guide/intro-upgrade-from-v1.md",
    "content": "Upgrading from Version 1.1\n==========================\n\nThere are many differences between versions 1.1 and 2.0 of Yii as the framework was completely rewritten for 2.0.\nAs a result, upgrading from version 1.1 is not as trivial as upgrading between minor versions. In this guide you'll\nfind the major differences between the two versions.\n\nIf you have not used Yii 1.1 before, you can safely skip this section and turn directly to \"[Getting started](start-installation.md)\".\n\nPlease note that Yii 2.0 introduces more new features than are covered in this summary. It is highly recommended\nthat you read through the whole definitive guide to learn about them all. Chances are that\nsome features you previously had to develop for yourself are now part of the core code.\n\n\nInstallation\n------------\n\nYii 2.0 fully embraces [Composer](https://getcomposer.org/), the de facto PHP package manager. Installation\nof the core framework, as well as extensions, are handled through Composer. Please refer to\nthe [Installing Yii](start-installation.md) section to learn how to install Yii 2.0. If you want to\ncreate new extensions, or turn your existing 1.1 extensions into 2.0-compatible extensions, please refer to\nthe [Creating Extensions](structure-extensions.md#creating-extensions) section of the guide.\n\n\nPHP Requirements\n----------------\n\nYii 2.0 requires PHP 5.4 or above, which is a huge improvement over PHP version 5.2 that is required by Yii 1.1.\nAs a result, there are many differences on the language level that you should pay attention to.\nBelow is a summary of the major changes regarding PHP:\n\n- [Namespaces](https://www.php.net/manual/en/language.namespaces.php).\n- [Anonymous functions](https://www.php.net/manual/en/functions.anonymous.php).\n- Short array syntax `[...elements...]` is used instead of `array(...elements...)`.\n- Short echo tags `<?=` are used in view files. This is safe to use starting from PHP 5.4.\n- [SPL classes and interfaces](https://www.php.net/manual/en/book.spl.php).\n- [Late Static Bindings](https://www.php.net/manual/en/language.oop5.late-static-bindings.php).\n- [Date and Time](https://www.php.net/manual/en/book.datetime.php).\n- [Traits](https://www.php.net/manual/en/language.oop5.traits.php).\n- [intl](https://www.php.net/manual/en/book.intl.php). Yii 2.0 makes use of the `intl` PHP extension\n  to support internationalization features.\n\n\nNamespace\n---------\n\nThe most obvious change in Yii 2.0 is the use of namespaces. Almost every core class\nis namespaced, e.g., `yii\\web\\Request`. The \"C\" prefix is no longer used in class names.\nThe naming scheme now follows the directory structure. For example, `yii\\web\\Request`\nindicates that the corresponding class file is `web/Request.php` under the Yii framework folder.\n\n(You can use any core class without explicitly including that class file, thanks to the Yii\nclass loader.)\n\n\nComponent and Object\n--------------------\n\nYii 2.0 breaks the `CComponent` class in 1.1 into two classes: [[yii\\base\\BaseObject]] and [[yii\\base\\Component]].\nThe [[yii\\base\\BaseObject|BaseObject]] class is a lightweight base class that allows defining [object properties](concept-properties.md)\nvia getters and setters. The [[yii\\base\\Component|Component]] class extends from [[yii\\base\\BaseObject|BaseObject]] and supports\n[events](concept-events.md) and [behaviors](concept-behaviors.md).\n\nIf your class does not need the event or behavior feature, you should consider using\n[[yii\\base\\BaseObject|BaseObject]] as the base class. This is usually the case for classes that represent basic\ndata structures.\n\n\nObject Configuration\n--------------------\n\nThe [[yii\\base\\BaseObject|BaseObject]] class introduces a uniform way of configuring objects. Any descendant class\nof [[yii\\base\\BaseObject|BaseObject]] should declare its constructor (if needed) in the following way so that\nit can be properly configured:\n\n```php\nclass MyClass extends \\yii\\base\\BaseObject\n{\n    public function __construct($param1, $param2, $config = [])\n    {\n        // ... initialization before configuration is applied\n\n        parent::__construct($config);\n    }\n\n    public function init()\n    {\n        parent::init();\n\n        // ... initialization after configuration is applied\n    }\n}\n```\n\nIn the above, the last parameter of the constructor must take a configuration array\nthat contains name-value pairs for initializing the properties at the end of the constructor.\nYou can override the [[yii\\base\\BaseObject::init()|init()]] method to do initialization work that should be done after\nthe configuration has been applied.\n\nBy following this convention, you will be able to create and configure new objects\nusing a configuration array:\n\n```php\n$object = Yii::createObject([\n    'class' => 'MyClass',\n    'property1' => 'abc',\n    'property2' => 'cde',\n], [$param1, $param2]);\n```\n\nMore details about configurations can be found in the [Configurations](concept-configurations.md) section.\n\n\nEvents\n------\n\nIn Yii 1, events were created by defining an `on`-method (e.g., `onBeforeSave`). In Yii 2, you can now use any event name. You trigger an event by calling\nthe [[yii\\base\\Component::trigger()|trigger()]] method:\n\n```php\n$event = new \\yii\\base\\Event;\n$component->trigger($eventName, $event);\n```\n\nTo attach a handler to an event, use the [[yii\\base\\Component::on()|on()]] method:\n\n```php\n$component->on($eventName, $handler);\n// To detach the handler, use:\n// $component->off($eventName, $handler);\n```\n\nThere are many enhancements to the event features. For more details, please refer to the [Events](concept-events.md) section.\n\n\nPath Aliases\n------------\n\nYii 2.0 expands the usage of path aliases to both file/directory paths and URLs. Yii 2.0 also now requires\nan alias name to start with the `@` character, to differentiate aliases from normal file/directory paths or URLs.\nFor example, the alias `@yii` refers to the Yii installation directory. Path aliases are\nsupported in most places in the Yii core code. For example, [[yii\\caching\\FileCache::cachePath]] can take\nboth a path alias and a normal directory path.\n\nA path alias is also closely related to a class namespace. It is recommended that a path\nalias be defined for each root namespace, thereby allowing you to use Yii class autoloader without\nany further configuration. For example, because `@yii` refers to the Yii installation directory,\na class like `yii\\web\\Request` can be autoloaded. If you use a third party library,\nsuch as the Zend Framework, you may define a path alias `@Zend` that refers to that framework's installation\ndirectory. Once you've done that, Yii will be able to autoload any class in that Zend Framework library, too.\n\nMore on path aliases can be found in the [Aliases](concept-aliases.md) section.\n\n\nViews\n-----\n\nThe most significant change about views in Yii 2 is that the special variable `$this` in a view no longer refers to\nthe current controller or widget. Instead, `$this` now refers to a *view* object, a new concept\nintroduced in 2.0. The *view* object is of type [[yii\\web\\View]], which represents the view part\nof the MVC pattern. If you want to access the controller or widget in a view, you can use `$this->context`.\n\nTo render a partial view within another view, you use `$this->render()`, not `$this->renderPartial()`. The call to `render` also now has to be explicitly echoed, as the `render()` method returns the rendering\nresult, rather than directly displaying it. For example:\n\n```php\necho $this->render('_item', ['item' => $item]);\n```\n\nBesides using PHP as the primary template language, Yii 2.0 is also equipped with official\nsupport for two popular template engines: Smarty and Twig. The Prado template engine is no longer supported.\nTo use these template engines, you need to configure the `view` application component by setting the\n[[yii\\base\\View::$renderers|View::$renderers]] property. Please refer to the [Template Engines](tutorial-template-engines.md)\nsection for more details.\n\n\nModels\n------\n\nYii 2.0 uses [[yii\\base\\Model]] as the base model, similar to `CModel` in 1.1.\nThe class `CFormModel` has been dropped entirely. Instead, in Yii 2 you should extend [[yii\\base\\Model]] to create a form model class.\n\nYii 2.0 introduces a new method called [[yii\\base\\Model::scenarios()|scenarios()]] to declare\nsupported scenarios, and to indicate under which scenario an attribute needs to be validated, can be considered as safe or not, etc. For example:\n\n```php\npublic function scenarios()\n{\n    return [\n        'backend' => ['email', 'role'],\n        'frontend' => ['email', '!role'],\n    ];\n}\n```\n\nIn the above, two scenarios are declared: `backend` and `frontend`. For the `backend` scenario, both the\n`email` and `role` attributes are safe, and can be massively assigned. For the `frontend` scenario,\n`email` can be massively assigned while `role` cannot. Both `email` and `role` should be validated using rules.\n\nThe [[yii\\base\\Model::rules()|rules()]] method is still used to declare the validation rules. Note that due to the introduction of [[yii\\base\\Model::scenarios()|scenarios()]], there is no longer an `unsafe` validator.\n\nIn most cases, you do not need to override [[yii\\base\\Model::scenarios()|scenarios()]]\nif the [[yii\\base\\Model::rules()|rules()]] method fully specifies the scenarios that will exist, and if there is no need to declare\n`unsafe` attributes.\n\nTo learn more details about models, please refer to the [Models](structure-models.md) section.\n\n\nControllers\n-----------\n\nYii 2.0 uses [[yii\\web\\Controller]] as the base controller class, which is similar to `CController` in Yii 1.1.\n[[yii\\base\\Action]] is the base class for action classes.\n\nThe most obvious impact of these changes on your code is that a controller action should return the content\nthat you want to render instead of echoing it:\n\n```php\npublic function actionView($id)\n{\n    $model = \\app\\models\\Post::findOne($id);\n    if ($model) {\n        return $this->render('view', ['model' => $model]);\n    } else {\n        throw new \\yii\\web\\NotFoundHttpException;\n    }\n}\n```\n\nPlease refer to the [Controllers](structure-controllers.md) section for more details about controllers.\n\n\nWidgets\n-------\n\nYii 2.0 uses [[yii\\base\\Widget]] as the base widget class, similar to `CWidget` in Yii 1.1.\n\nTo get better support for the framework in IDEs, Yii 2.0 introduces a new syntax for using widgets. The static methods\n[[yii\\base\\Widget::begin()|begin()]], [[yii\\base\\Widget::end()|end()]], and [[yii\\base\\Widget::widget()|widget()]]\nhave been introduced, to be used like so:\n\n```php\nuse yii\\widgets\\Menu;\nuse yii\\widgets\\ActiveForm;\n\n// Note that you have to \"echo\" the result to display it\necho Menu::widget(['items' => $items]);\n\n// Passing an array to initialize the object properties\n$form = ActiveForm::begin([\n    'options' => ['class' => 'form-horizontal'],\n    'fieldConfig' => ['inputOptions' => ['class' => 'input-xlarge']],\n]);\n... form input fields here ...\nActiveForm::end();\n```\n\nPlease refer to the [Widgets](structure-widgets.md) section for more details.\n\n\nThemes\n------\n\nThemes work completely differently in 2.0. They are now based on a path mapping mechanism that maps a source\nview file path to a themed view file path. For example, if the path map for a theme is\n`['/web/views' => '/web/themes/basic']`, then the themed version for the view file\n`/web/views/site/index.php` will be `/web/themes/basic/site/index.php`. For this reason, themes can now\nbe applied to any view file, even a view rendered outside the context of a controller or a widget.\n\nAlso, there is no more `CThemeManager` component. Instead, `theme` is a configurable property of the `view`\napplication component.\n\nPlease refer to the [Theming](output-theming.md) section for more details.\n\n\nConsole Applications\n--------------------\n\nConsole applications are now organized as controllers, like Web applications. Console controllers\nshould extend from [[yii\\console\\Controller]], similar to `CConsoleCommand` in 1.1.\n\nTo run a console command, use `yii <route>`, where `<route>` stands for a controller route\n(e.g. `sitemap/index`). Additional anonymous arguments are passed as the parameters to the\ncorresponding controller action method, while named arguments are parsed according to\nthe declarations in [[yii\\console\\Controller::options()]].\n\nYii 2.0 supports automatic generation of command help information from comment blocks.\n\nPlease refer to the [Console Commands](tutorial-console.md) section for more details.\n\n\nI18N\n----\n\nYii 2.0 removes the built-in date formatter and number formatter pieces in favor of the [PECL intl PHP module](https://pecl.php.net/package/intl).\n\nMessage translation is now performed via the `i18n` application component.\nThis component manages a set of message sources, which allows you to use different message\nsources based on message categories.\n\nPlease refer to the [Internationalization](tutorial-i18n.md) section for more details.\n\n\nAction Filters\n--------------\n\nAction filters are implemented via behaviors now. To define a new, custom filter, extend from [[yii\\base\\ActionFilter]]. To use a filter, attach the filter class to the controller\nas a behavior. For example, to use the [[yii\\filters\\AccessControl]] filter, you would have the following\ncode in a controller:\n\n```php\npublic function behaviors()\n{\n    return [\n        'access' => [\n            'class' => 'yii\\filters\\AccessControl',\n            'rules' => [\n                ['allow' => true, 'actions' => ['admin'], 'roles' => ['@']],\n            ],\n        ],\n    ];\n}\n```\n\nPlease refer to the [Filtering](structure-filters.md) section for more details.\n\n\nAssets\n------\n\nYii 2.0 introduces a new concept called *asset bundle* that replaces the script package concept found in Yii 1.1.\n\nAn asset bundle is a collection of asset files (e.g. JavaScript files, CSS files, image files, etc.)\nwithin a directory. Each asset bundle is represented as a class extending [[yii\\web\\AssetBundle]].\nBy registering an asset bundle via [[yii\\web\\AssetBundle::register()]], you make\nthe assets in that bundle accessible via the Web. Unlike in Yii 1, the page registering the bundle will automatically\ncontain the references to the JavaScript and CSS files specified in that bundle.\n\nPlease refer to the [Managing Assets](structure-assets.md) section for more details.\n\n\nHelpers\n-------\n\nYii 2.0 introduces many commonly used static helper classes, including.\n\n* [[yii\\helpers\\Html]]\n* [[yii\\helpers\\ArrayHelper]]\n* [[yii\\helpers\\StringHelper]]\n* [[yii\\helpers\\FileHelper]]\n* [[yii\\helpers\\Json]]\n\nPlease refer to the [Helper Overview](helper-overview.md) section for more details.\n\nForms\n-----\n\nYii 2.0 introduces the *field* concept for building a form using [[yii\\widgets\\ActiveForm]]. A field\nis a container consisting of a label, an input, an error message, and/or a hint text.\nA field is represented as an [[yii\\widgets\\ActiveField|ActiveField]] object.\nUsing fields, you can build a form more cleanly than before:\n\n```php\n<?php $form = yii\\widgets\\ActiveForm::begin(); ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n    <div class=\"form-group\">\n        <?= Html::submitButton('Login') ?>\n    </div>\n<?php yii\\widgets\\ActiveForm::end(); ?>\n```\n\nPlease refer to the [Creating Forms](input-forms.md) section for more details.\n\n\nQuery Builder\n-------------\n\nIn 1.1, query building was scattered among several classes, including `CDbCommand`,\n`CDbCriteria`, and `CDbCommandBuilder`. Yii 2.0 represents a DB query in terms of a [[yii\\db\\Query|Query]] object\nthat can be turned into a SQL statement with the help of [[yii\\db\\QueryBuilder|QueryBuilder]] behind the scene.\nFor example:\n\n```php\n$query = new \\yii\\db\\Query();\n$query->select('id, name')\n      ->from('user')\n      ->limit(10);\n\n$command = $query->createCommand();\n$sql = $command->sql;\n$rows = $command->queryAll();\n```\n\nBest of all, such query building methods can also be used when working with [Active Record](db-active-record.md).\n\nPlease refer to the [Query Builder](db-query-builder.md) section for more details.\n\n\nActive Record\n-------------\n\nYii 2.0 introduces a lot of changes to [Active Record](db-active-record.md). The two most obvious ones involve\nquery building and relational query handling.\n\nThe `CDbCriteria` class in 1.1 is replaced by [[yii\\db\\ActiveQuery]] in Yii 2. That class extends from [[yii\\db\\Query]], and thus\ninherits all query building methods. You call [[yii\\db\\ActiveRecord::find()]] to start building a query:\n\n```php\n// To retrieve all *active* customers and order them by their ID:\n$customers = Customer::find()\n    ->where(['status' => $active])\n    ->orderBy('id')\n    ->all();\n```\n\nTo declare a relation, simply define a getter method that returns an [[yii\\db\\ActiveQuery|ActiveQuery]] object.\nThe property name defined by the getter represents the relation name. For example, the following code declares\nan `orders` relation (in 1.1, you would have to declare relations in a central place `relations()`):\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public function getOrders()\n    {\n        return $this->hasMany('Order', ['customer_id' => 'id']);\n    }\n}\n```\n\nNow you can use `$customer->orders` to access a customer's orders from the related table. You can also use the following code\nto perform an on-the-fly relational query with a customized query condition:\n\n```php\n$orders = $customer->getOrders()->andWhere('status=1')->all();\n```\n\nWhen eager loading a relation, Yii 2.0 does it differently from 1.1. In particular, in 1.1 a JOIN query\nwould be created to select both the primary and the relational records. In Yii 2.0, two SQL statements are executed\nwithout using JOIN: the first statement brings back the primary records and the second brings back the relational\nrecords by filtering with the primary keys of the primary records.\n\nInstead of returning [[yii\\db\\ActiveRecord|ActiveRecord]] objects, you may chain the [[yii\\db\\ActiveQuery::asArray()|asArray()]]\nmethod when building a query to return a large number of records. This will cause the query result to be returned\nas arrays, which can significantly reduce the needed CPU time and memory if large number of records . For example:\n\n```php\n$customers = Customer::find()->asArray()->all();\n```\n\nAnother change is that you can't define attribute default values through public properties anymore.\nIf you need those, you should set them in the init method of your record class.\n\n```php\npublic function init()\n{\n    parent::init();\n    $this->status = self::STATUS_NEW;\n}\n```\n\nThere were some problems with overriding the constructor of an ActiveRecord class in 1.1. These are not present in\nversion 2.0 anymore. Note that when adding parameters to the constructor you might have to override [[yii\\db\\ActiveRecord::instantiate()]].\n\nThere are many other changes and enhancements to Active Record. Please refer to\nthe [Active Record](db-active-record.md) section for more details.\n\n\nActive Record Behaviors\n-----------------------\n\nIn 2.0, we have dropped the base behavior class `CActiveRecordBehavior`. If you want to create an Active Record Behavior,\nyou will have to extend directly from `yii\\base\\Behavior`. If the behavior class needs to respond to some events\nof the owner, you have to override the `events()` method like the following:\n\n```php\nnamespace app\\components;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\base\\Behavior;\n\nclass MyBehavior extends Behavior\n{\n    // ...\n\n    public function events()\n    {\n        return [\n            ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',\n        ];\n    }\n\n    public function beforeValidate($event)\n    {\n        // ...\n    }\n}\n```\n\n\nUser and IdentityInterface\n--------------------------\n\nThe `CWebUser` class in 1.1 is now replaced by [[yii\\web\\User]], and there is no more\n`CUserIdentity` class. Instead, you should implement the [[yii\\web\\IdentityInterface]] which\nis much more straightforward to use. The advanced project template provides such an example.\n\nPlease refer to the [Authentication](security-authentication.md), [Authorization](security-authorization.md), and [Advanced Project Template](https://www.yiiframework.com/extension/yiisoft/yii2-app-advanced/doc/guide) sections for more details.\n\n\nURL Management\n--------------\n\nURL management in Yii 2 is similar to that in 1.1. A major enhancement is that URL management now supports optional\nparameters. For example, if you have a rule declared as follows, then it will match\nboth `post/popular` and `post/1/popular`. In 1.1, you would have had to use two rules to achieve\nthe same goal.\n\n```php\n[\n    'pattern' => 'post/<page:\\d+>/<tag>',\n    'route' => 'post/index',\n    'defaults' => ['page' => 1],\n]\n```\n\nPlease refer to the [Url manager docs](runtime-routing.md) section for more details.\n\nAn important change in the naming convention for routes is that camel case names of controllers\nand actions are now converted to lower case where each word is separated by a hypen, e.g. the controller\nid for the `CamelCaseController` will be `camel-case`.\nSee the section about [controller IDs](structure-controllers.md#controller-ids) and [action IDs](structure-controllers.md#action-ids) for more details.\n\n\nUsing Yii 1.1 and 2.x together\n------------------------------\n\nIf you have legacy Yii 1.1 code that you want to use together with Yii 2.0, please refer to\nthe [Using Yii 1.1 and 2.0 Together](tutorial-yii-integration.md#using-both-yii2-yii1) section.\n\n"
  },
  {
    "path": "docs/guide/intro-yii.md",
    "content": "What is Yii\n===========\n\nYii is a high performance, component-based PHP framework for rapidly developing modern Web applications.\nThe name Yii (pronounced `Yee` or `[ji:]`) means \"simple and evolutionary\" in Chinese. It can also\nbe thought of as an acronym for **Yes It Is**!\n\n\nWhat is Yii Best for?\n---------------------\n\nYii is a generic Web programming framework, meaning that it can be used for developing all kinds\nof Web applications using PHP. Because of its component-based architecture and sophisticated caching\nsupport, it is especially suitable for developing large-scale applications such as portals, forums, content\nmanagement systems (CMS), e-commerce projects, RESTful Web services, and so on.\n\n\nHow does Yii Compare with Other Frameworks?\n-------------------------------------------\n\nIf you're already familiar with another framework, you may appreciate knowing how Yii compares:\n\n- Like most PHP frameworks, Yii implements the MVC (Model-View-Controller) architectural pattern and promotes code\n  organization based on that pattern.\n- Yii takes the philosophy that code should be written in a simple yet elegant way. Yii will never try to\n  over-design things mainly for the purpose of strictly following some design pattern.\n- Yii is a full-stack framework providing many proven and ready-to-use features: query builders\n  and ActiveRecord for both relational and NoSQL databases; RESTful API development support; multi-tier\n  caching support; and more.\n- Yii is extremely extensible. You can customize or replace nearly every piece of the core's code. You can also\n  take advantage of Yii's solid extension architecture to use or develop redistributable extensions.\n- High performance is always a primary goal of Yii.\n\nYii is not a one-man show, it is backed up by a [strong core developer team](https://www.yiiframework.com/team/), as well as a large community\nof professionals constantly contributing to Yii's development. The Yii developer team\nkeeps a close eye on the latest Web development trends and on the best practices and features\nfound in other frameworks and projects. The most relevant best practices and features found elsewhere are regularly incorporated into the core framework and exposed\nvia simple and elegant interfaces.\n\n\nYii Versions\n------------\n\nYii currently has two major versions available: 1.1 and 2.0. Version 1.1 is the old generation and is now in maintenance mode. Version 2.0 is a complete rewrite of Yii, adopting the latest\ntechnologies and protocols, including Composer, PSR, namespaces, traits, and so forth. Version 2.0 represents the current\ngeneration of the framework and will receive the main development efforts over the next few years.\nThis guide is mainly about version 2.0.\n\n\nRequirements and Prerequisites\n------------------------------\n\nYii 2.0 requires PHP 7.4.0 or above and runs best with the latest version of PHP. You can find more detailed\nrequirements for individual features by running the requirement checker included in every Yii release.\n\nUsing Yii requires basic knowledge of object-oriented programming (OOP), as Yii is a pure OOP-based framework.\nYii 2.0 also makes use of the latest features of PHP, such as [namespaces](https://www.php.net/manual/en/language.namespaces.php)\nand [traits](https://www.php.net/manual/en/language.oop5.traits.php). Understanding these concepts will help\nyou more easily pick up Yii 2.0.\n\n"
  },
  {
    "path": "docs/guide/output-client-scripts.md",
    "content": "Working with Client Scripts\n===========================\n\nModern web applications, additionally to static HTML pages that are\nrendered and sent to the browser, contain JavaScript that is used\nto modify the page in the browser by manipulating existing elements or\nloading new content via AJAX.\nThis section describes methods provided by Yii for adding JavaScript and CSS to a website as well as dynamically adjusting these.\n\n## Registering scripts <span id=\"register-scripts\"></span>\n\nWhen working with the [[yii\\web\\View]] object you can dynamically register frontend scripts.\nThere are two dedicated methods for this:\n\n- [[yii\\web\\View::registerJs()|registerJs()]] for inline scripts\n- [[yii\\web\\View::registerJsFile()|registerJsFile()]] for external scripts\n\n### Registering inline scripts <span id=\"inline-scripts\"></span>\n\nInline scripts are useful for configuration, dynamically generated code and small snippets created by reusable frontend code contained in [widgets](structure-widgets.md).\nThe [[yii\\web\\View::registerJs()|registerJs()]] method for adding these can be used as follows:\n\n```php\n$this->registerJs(\n    \"$('#myButton').on('click', function() { alert('Button clicked!'); });\",\n    View::POS_READY,\n    'my-button-handler'\n);\n```\n\nThe first argument is the actual JS code we want to insert into the page.\nIt will be wrapped into a `<script>` tag. The second argument\ndetermines at which position the script should be inserted into the page. Possible values are:\n\n- [[yii\\web\\View::POS_HEAD|View::POS_HEAD]] for head section.\n- [[yii\\web\\View::POS_BEGIN|View::POS_BEGIN]] for right after opening `<body>`.\n- [[yii\\web\\View::POS_END|View::POS_END]] for right before closing `</body>`.\n- [[yii\\web\\View::POS_READY|View::POS_READY]] for executing code on the [document `ready` event](https://learn.jquery.com/using-jquery-core/document-ready/).\n  This will automatically register [[yii\\web\\JqueryAsset|jQuery]] and wrap the code into the appropriate jQuery code. This is the default position.\n- [[yii\\web\\View::POS_LOAD|View::POS_LOAD]] for executing code on the\n  [document `load` event](https://learn.jquery.com/using-jquery-core/document-ready/). Same as the above, this will also register [[yii\\web\\JqueryAsset|jQuery]] automatically.\n\nThe last argument is a unique script ID that is used to identify the script code block and replace an existing one with the same ID\ninstead of adding a new one. If you don't provide it, the JS code itself will be used as the ID. It is used to avoid registration of the same code multiple times.\n\n### Registering script files <span id=\"script-files\"></span>\n\nThe arguments for [[yii\\web\\View::registerJsFile()|registerJsFile()]] are similar to those for\n[[yii\\web\\View::registerCssFile()|registerCssFile()]]. In the following example,\nwe register the `main.js` file with the dependency on the [[yii\\web\\JqueryAsset]]. It means that the `main.js` file\nwill be added AFTER `jquery.js`. Without such dependency specification, the relative order between\n`main.js` and `jquery.js` would be undefined and the code would not work.\n\nAn external script can be added like the following:\n\n```php\n$this->registerJsFile(\n    '@web/js/main.js',\n    ['depends' => [\\yii\\web\\JqueryAsset::class]]\n);\n```\n\nThis will add a tag for the `/js/main.js` script located under the application [base URL](concept-aliases.md#predefined-aliases).\n\nIt is highly recommended to use [asset bundles](structure-assets.md) to register external JS files rather than [[yii\\web\\View::registerJsFile()|registerJsFile()]] because these allow better flexibility and more granular dependency configuration. Also using asset bundles allows you to combine and compress\nmultiple JS files, which is desirable for high traffic websites.\n\n## Registering CSS <span id=\"register-css\"></span>\n\nSimilar to JavaScript, you can register CSS using\n[[yii\\web\\View::registerCss()|registerCss()]] or \n[[yii\\web\\View::registerCssFile()|registerCssFile()]].\nThe former registers a block of CSS code while the latter registers an external CSS file.\n\n### Registering inline CSS <span id=\"inline-css\"></span>\n\n```php\n$this->registerCss(\"body { background: #f00; }\");\n```\n\nThe code above will result in adding the following to the `<head>` section of the page:\n\n```html\n<style>\nbody { background: #f00; }\n</style>\n```\n\nIf you want to specify additional properties of the style tag, pass an array of name-values to the second argument.\nThe last argument is a unique ID that is used to identify the style block and make sure it is only added once in case the same style is registered from different places in the code.\n\n### Registering CSS files <span id=\"css-files\"></span>\n\nA CSS file can be registered using the following:\n\n```php\n$this->registerCssFile(\"@web/css/themes/black-and-white.css\", [\n    'depends' => [\\yii\\bootstrap\\BootstrapAsset::class],\n    'media' => 'print',\n], 'css-print-theme');\n```\n\nThe above code will add a link to the `/css/themes/black-and-white.css` CSS file to the `<head>` section of the page.\n\n* The first argument specifies the CSS file to be registered.\n  The `@web` in this example is an [alias for the applications base URL](concept-aliases.md#predefined-aliases).\n* The second argument specifies the HTML attributes for the resulting `<link>` tag. The option `depends`\n  is specially handled. It specifies which asset bundles this CSS file depends on. In this case, the dependent\n  asset bundle is [[yii\\bootstrap\\BootstrapAsset|BootstrapAsset]]. This means the CSS file will be added\n  *after* the CSS files from [[yii\\bootstrap\\BootstrapAsset|BootstrapAsset]].\n* The last argument specifies an ID identifying this CSS file. If it is not provided, the URL of the CSS file will be\n  used instead.\n\nIt is highly recommended to use [asset bundles](structure-assets.md) to register external CSS files rather than\n[[yii\\web\\View::registerCssFile()|registerCssFile()]]. Using asset bundles allows you to combine and compress\nmultiple CSS files, which is desirable for high traffic websites.\nIt also provides more flexibility as all asset dependencies of your application are configured in one place.\n\n\n## Registering asset bundles <span id=\"asset-bundles\"></span>\n\nAs was mentioned earlier it's recommended to use asset bundles instead of registering CSS and JavaScript files directly.\nYou can get details on how to define asset bundles in the\n[\"Assets\" section](structure-assets.md).\nAs for using already defined asset bundles, it's very straightforward:\n\n```php\n\\frontend\\assets\\AppAsset::register($this);\n```\n\nIn the above code, in the context of a view file, the `AppAsset` bundle is registered on the current view (represented by `$this`).\nWhen registering asset bundles from within a widget, you would pass the\n[[yii\\base\\Widget::$view|$view]] of the widget instead (`$this->view`).\n\n\n## Generating Dynamic Javascript <span id=\"dynamic-js\"></span>\n\nIn view files often the HTML code is not written out directly but generated\nby some PHP code dependent on the variables of the view.\nIn order for the generated HTML to be manipulated with Javascript, the JS code has to contain dynamic parts too, for example the IDs of the jQuery selectors.\n\nTo insert PHP variables into JS code, their values have to be\nescaped properly. Especially when the JS code is inserted into\nHTML instead of residing in a dedicated JS file.\nYii provides the [[yii\\helpers\\Json::htmlEncode()|htmlEncode()]] method of the [[yii\\helpers\\Json|Json]] helper for this purpose. Its usage will be shown in the following examples.\n\n### Registering a global JavaScript configuration <span id=\"js-configuration\"></span>\n\nIn this example we use an array to pass global configuration parameters from\nthe PHP part of the application to the JS frontend code.\n\n```php\n$options = [\n    'appName' => Yii::$app->name,\n    'baseUrl' => Yii::$app->request->baseUrl,\n    'language' => Yii::$app->language,\n    // ...\n];\n$this->registerJs(\n    \"var yiiOptions = \".\\yii\\helpers\\Json::htmlEncode($options).\";\",\n    View::POS_HEAD,\n    'yiiOptions'\n);\n```\n\nThe above code will register a `<script>`-tag containing the JavaScript\nvariable definition, e.g.:\n\n```javascript\nvar yiiOptions = {\"appName\":\"My Yii Application\",\"baseUrl\":\"/basic/web\",\"language\":\"en\"};\n```\n\nIn your JavaScript code you can now access these like `yiiOptions.baseUrl` or `yiiOptions.language`.\n\n### Passing translated messages <span id=\"translated-messages\"></span>\n\nYou may encounter a case where your JavaScript needs to print a message reacting to some event. In an application that works with multiple languages this string has to be translated to the current application language.\nOne way to achieve this is to use the\n[message translation feature](tutorial-i18n.md#message-translation) provided by Yii and passing the result to the JavaScript code.\n\n```php\n$message = \\yii\\helpers\\Json::htmlEncode(\n    \\Yii::t('app', 'Button clicked!')\n);\n$this->registerJs(<<<JS\n    $('#myButton').on('click', function() { alert( $message ); });\nJS\n);\n```\n\nThe above example code uses PHP\n[Heredoc syntax](https://www.php.net/manual/en/language.types.string.php#language.types.string.syntax.heredoc) for better readability. This also enables better syntax highlighting in most IDEs so it is the\npreferred way of writing inline JavaScript, especially useful for code that is longer than a single line. The variable `$message` is created in PHP and\nthanks to [[yii\\helpers\\Json::htmlEncode|Json::htmlEncode]] it contains the \nstring in valid JS syntax, which can be inserted into the JavaScript code to place the dynamic string in the function call to `alert()`.\n\n> Note: When using Heredoc, be careful with variable naming in JS code\n> as variables beginning with `$` may be interpreted as PHP variables which\n> will be replaced by their content.\n> The jQuery function in form of `$(` or `$.` is not interpreted\n> as a PHP variable and can safely be used.\n\n## The `yii.js` script <span id=\"yii.js\"></span>\n\n> Note: This section has not been written yet. It should contain explanation of the functionality provided by `yii.js`:\n> \n> - Yii JavaScript Modules\n> - CSRF param handling\n> - `data-confirm` handler\n> - `data-method` handler\n> - script filtering\n> - redirect handling\n\n"
  },
  {
    "path": "docs/guide/output-data-providers.md",
    "content": "Data Providers\n==============\n\nIn the [Pagination](output-pagination.md) and [Sorting](output-sorting.md) sections, we have described how to\nallow end users to choose a particular page of data to display and sort them by some columns. Because the task\nof paginating and sorting data is very common, Yii provides a set of *data provider* classes to encapsulate it.\n\nA data provider is a class implementing [[yii\\data\\DataProviderInterface]]. It mainly supports retrieving paginated\nand sorted data. It is usually used to work with [data widgets](output-data-widgets.md) so that end users can \ninteractively paginate and sort data. \n\nThe following data provider classes are included in the Yii releases:\n\n* [[yii\\data\\ActiveDataProvider]]: uses [[yii\\db\\Query]] or [[yii\\db\\ActiveQuery]] to query data from databases\n  and return them in terms of arrays or [Active Record](db-active-record.md) instances.\n* [[yii\\data\\SqlDataProvider]]: executes a SQL statement and returns database data as arrays.\n* [[yii\\data\\ArrayDataProvider]]: takes a big array and returns a slice of it based on the paginating and sorting\n  specifications.\n\nThe usage of all these data providers share the following common pattern:\n\n```php\n// create the data provider by configuring its pagination and sort properties\n$provider = new XyzDataProvider([\n    'pagination' => [...],\n    'sort' => [...],\n]);\n\n// retrieves paginated and sorted data\n$models = $provider->getModels();\n\n// get the number of data items in the current page\n$count = $provider->getCount();\n\n// get the total number of data items across all pages\n$totalCount = $provider->getTotalCount();\n```\n\nYou specify the pagination and sorting behaviors of a data provider by configuring its \n[[yii\\data\\BaseDataProvider::pagination|pagination]] and [[yii\\data\\BaseDataProvider::sort|sort]] properties\nwhich correspond to the configurations for [[yii\\data\\Pagination]] and [[yii\\data\\Sort]], respectively.\nYou may also configure them to be `false` to disable pagination and/or sorting features.\n\n[Data widgets](output-data-widgets.md), such as [[yii\\grid\\GridView]], have a property named `dataProvider` which\ncan take a data provider instance and display the data it provides. For example,\n\n```php\necho yii\\grid\\GridView::widget([\n    'dataProvider' => $dataProvider,\n]);\n```\n\nThese data providers mainly vary in the way how the data source is specified. In the following subsections,\nwe will explain the detailed usage of each of these data providers.\n\n\n## Active Data Provider <span id=\"active-data-provider\"></span> \n\nTo use [[yii\\data\\ActiveDataProvider]], you should configure its [[yii\\data\\ActiveDataProvider::query|query]] property.\nIt can take either a [[yii\\db\\Query]] or [[yii\\db\\ActiveQuery]] object. If the former, the data returned will be arrays;\nif the latter, the data returned can be either arrays or [Active Record](db-active-record.md) instances.\nFor example,\n\n```php\nuse yii\\data\\ActiveDataProvider;\n\n$query = Post::find()->where(['status' => 1]);\n\n$provider = new ActiveDataProvider([\n    'query' => $query,\n    'pagination' => [\n        'pageSize' => 10,\n    ],\n    'sort' => [\n        'defaultOrder' => [\n            'created_at' => SORT_DESC,\n            'title' => SORT_ASC, \n        ]\n    ],\n]);\n\n// returns an array of Post objects\n$posts = $provider->getModels();\n```\n\nIf `$query` in the above example is created using the following code, then the data provider will return raw arrays.\n\n```php\nuse yii\\db\\Query;\n\n$query = (new Query())->from('post')->where(['status' => 1]); \n```\n\n> Note: If a query already specifies the `orderBy` clause, the new ordering instructions given by end users\n  (through the `sort` configuration) will be appended to the existing `orderBy` clause. Any existing `limit`\n  and `offset` clauses will be overwritten by the pagination request from end users (through the `pagination` configuration). \n\nBy default, [[yii\\data\\ActiveDataProvider]] uses the `db` application component as the database connection. You may\nuse a different database connection by configuring the [[yii\\data\\ActiveDataProvider::db]] property.\n\n\n## SQL Data Provider <span id=\"sql-data-provider\"></span>\n\n[[yii\\data\\SqlDataProvider]] works with a raw SQL statement which is used to fetch the needed\ndata. Based on the specifications of [[yii\\data\\SqlDataProvider::sort|sort]] and \n[[yii\\data\\SqlDataProvider::pagination|pagination]], the provider will adjust the `ORDER BY` and `LIMIT`\nclauses of the SQL statement accordingly to fetch only the requested page of data in the desired order.\n\nTo use [[yii\\data\\SqlDataProvider]], you should specify the [[yii\\data\\SqlDataProvider::sql|sql]] property as well\nas the [[yii\\data\\SqlDataProvider::totalCount|totalCount]] property. For example,\n\n```php\nuse yii\\data\\SqlDataProvider;\n\n$count = Yii::$app->db->createCommand('\n    SELECT COUNT(*) FROM post WHERE status=:status\n', [':status' => 1])->queryScalar();\n\n$provider = new SqlDataProvider([\n    'sql' => 'SELECT * FROM post WHERE status=:status',\n    'params' => [':status' => 1],\n    'totalCount' => $count,\n    'pagination' => [\n        'pageSize' => 10,\n    ],\n    'sort' => [\n        'attributes' => [\n            'title',\n            'view_count',\n            'created_at',\n        ],\n    ],\n]);\n\n// returns an array of data rows\n$models = $provider->getModels();\n```\n\n> Info: The [[yii\\data\\SqlDataProvider::totalCount|totalCount]] property is required only if you need to\n  paginate the data. This is because the SQL statement specified via [[yii\\data\\SqlDataProvider::sql|sql]]\n  will be modified by the provider to return only the currently requested page of data. The provider still\n  needs to know the total number of data items in order to correctly calculate the number of pages available.\n\n\n## Array Data Provider <span id=\"array-data-provider\"></span>\n\n[[yii\\data\\ArrayDataProvider]] is best used when working with a big array. The provider allows you to return\na page of the array data sorted by one or multiple columns. To use [[yii\\data\\ArrayDataProvider]], you should\nspecify the [[yii\\data\\ArrayDataProvider::allModels|allModels]] property as the big array.\nElements in the big array can be either associative arrays\n(e.g. query results of [DAO](db-dao.md)) or objects (e.g. [Active Record](db-active-record.md) instances).\nFor example,\n\n```php\nuse yii\\data\\ArrayDataProvider;\n\n$data = [\n    ['id' => 1, 'name' => 'name 1', ...],\n    ['id' => 2, 'name' => 'name 2', ...],\n    ...\n    ['id' => 100, 'name' => 'name 100', ...],\n];\n\n$provider = new ArrayDataProvider([\n    'allModels' => $data,\n    'pagination' => [\n        'pageSize' => 10,\n    ],\n    'sort' => [\n        'attributes' => ['id', 'name'],\n    ],\n]);\n\n// get the rows in the currently requested page\n$rows = $provider->getModels();\n``` \n\n> Note: Compared to [Active Data Provider](#active-data-provider) and [SQL Data Provider](#sql-data-provider),\n  array data provider is less efficient because it requires loading *all* data into the memory.\n\n\n## Working with Data Keys <span id=\"working-with-keys\"></span>\n\nWhen using the data items returned by a data provider, you often need to identify each data item with a unique key.\nFor example, if the data items represent customer information, you may want to use the customer ID as the key\nfor each customer data. Data providers can return a list of such keys corresponding with the data items returned \nby [[yii\\data\\DataProviderInterface::getModels()]]. For example,\n\n```php\nuse yii\\data\\ActiveDataProvider;\n\n$query = Post::find()->where(['status' => 1]);\n\n$provider = new ActiveDataProvider([\n    'query' => $query,\n]);\n\n// returns an array of Post objects\n$posts = $provider->getModels();\n\n// returns the primary key values corresponding to $posts\n$ids = $provider->getKeys();\n```\n\nIn the above example, because you provide to [[yii\\data\\ActiveDataProvider]] an [[yii\\db\\ActiveQuery]] object,\nit is intelligent enough to return primary key values as the keys. You may also explicitly specify how the key\nvalues should be calculated by configuring [[yii\\data\\ActiveDataProvider::key]] with a column name or\na callable calculating key values. For example,\n\n```php\n// use \"slug\" column as key values\n$provider = new ActiveDataProvider([\n    'query' => Post::find(),\n    'key' => 'slug',\n]);\n\n// use the result of md5(id) as key values\n$provider = new ActiveDataProvider([\n    'query' => Post::find(),\n    'key' => function ($model) {\n        return md5($model->id);\n    }\n]);\n```\n\n\n## Creating Custom Data Provider <span id=\"custom-data-provider\"></span>\n\nTo create your own custom data provider classes, you should implement [[yii\\data\\DataProviderInterface]].\nAn easier way is to extend from [[yii\\data\\BaseDataProvider]] which allows you to focus on the core data provider\nlogic. In particular, you mainly need to implement the following methods:\n                                                   \n- [[yii\\data\\BaseDataProvider::prepareModels()|prepareModels()]]: prepares the data models that will be made \n  available in the current page and returns them as an array.\n- [[yii\\data\\BaseDataProvider::prepareKeys()|prepareKeys()]]: accepts an array of currently available data models\n  and returns keys associated with them.\n- [[yii\\data\\BaseDataProvider::prepareTotalCount()|prepareTotalCount]]: returns a value indicating the total number \n  of data models in the data provider.\n\nBelow is an example of a data provider that reads CSV data efficiently:\n\n```php\n<?php\nuse yii\\data\\BaseDataProvider;\n\nclass CsvDataProvider extends BaseDataProvider\n{\n    /**\n     * @var string name of the CSV file to read\n     */\n    public $filename;\n    \n    /**\n     * @var string|callable name of the key column or a callable returning it\n     */\n    public $key;\n    \n    /**\n     * @var SplFileObject\n     */\n    protected $fileObject; // SplFileObject is very convenient for seeking to particular line in a file\n    \n \n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        \n        // open file\n        $this->fileObject = new SplFileObject($this->filename);\n    }\n \n    /**\n     * {@inheritdoc}\n     */\n    protected function prepareModels()\n    {\n        $models = [];\n        $pagination = $this->getPagination();\n \n        if ($pagination === false) {\n            // in case there's no pagination, read all lines\n            while (!$this->fileObject->eof()) {\n                $models[] = $this->fileObject->fgetcsv();\n                $this->fileObject->next();\n            }\n        } else {\n            // in case there's pagination, read only a single page\n            $pagination->totalCount = $this->getTotalCount();\n            $this->fileObject->seek($pagination->getOffset());\n            $limit = $pagination->getLimit();\n \n            for ($count = 0; $count < $limit; ++$count) {\n                $models[] = $this->fileObject->fgetcsv();\n                $this->fileObject->next();\n            }\n        }\n \n        return $models;\n    }\n \n    /**\n     * {@inheritdoc}\n     */\n    protected function prepareKeys($models)\n    {\n        if ($this->key !== null) {\n            $keys = [];\n \n            foreach ($models as $model) {\n                if (is_string($this->key)) {\n                    $keys[] = $model[$this->key];\n                } else {\n                    $keys[] = call_user_func($this->key, $model);\n                }\n            }\n \n            return $keys;\n        }\n\n        return array_keys($models);\n    }\n \n    /**\n     * {@inheritdoc}\n     */\n    protected function prepareTotalCount()\n    {\n        $count = 0;\n \n        while (!$this->fileObject->eof()) {\n            $this->fileObject->next();\n            ++$count;\n        }\n \n        return $count;\n    }\n}\n```\n\n## Filtering Data Providers using Data Filters <span id=\"filtering-data-providers-using-data-filters\"></span>\n\nWhile you can build conditions for active data provider manually as described in\n[Filtering Data](output-data-widgets.md#filtering-data) and [Separate Filter Form](output-data-widgets.md#separate-filter-form)\nsections of data widgets guide, Yii has data filters that are very useful if you need flexible filter conditions.\nData filters could be used as follows:\n\n```php\n$filter = new ActiveDataFilter([\n    'searchModel' => 'app\\models\\PostSearch'\n]);\n\n$filterCondition = null;\n\n// You may load filters from any source. For example,\n// if you prefer JSON in request body,\n// use Yii::$app->request->getBodyParams() below:\nif ($filter->load(\\Yii::$app->request->get())) { \n    $filterCondition = $filter->build();\n    if ($filterCondition === false) {\n        // Serializer would get errors out of it\n        return $filter;\n    }\n}\n\n$query = Post::find();\nif ($filterCondition !== null) {\n    $query->andWhere($filterCondition);\n}\n\nreturn new ActiveDataProvider([\n    'query' => $query,\n]);\n```\n\n`PostSearch` model serves the purpose of defining which properties and values are allowed for filtering:\n\n```php\nuse yii\\base\\Model;\n\nclass PostSearch extends Model \n{\n    public $id;\n    public $title;\n    \n    public function rules()\n    {\n        return [\n            ['id', 'integer'],\n            ['title', 'string', 'min' => 2, 'max' => 200],            \n        ];\n    }\n}\n```\n\nData filters are quite flexible. You may customize how conditions are built and which operators are allowed.\nFor details check API docs on [[\\yii\\data\\DataFilter]].\n"
  },
  {
    "path": "docs/guide/output-data-widgets.md",
    "content": "Data widgets\n============\n\nYii provides a set of [widgets](structure-widgets.md) that can be used to display data.\nWhile the [DetailView](#detail-view) widget can be used to display data for a single record,\n[ListView](#list-view) and [GridView](#grid-view) can be used to display a list or table of data records\nproviding features like pagination, sorting and filtering.\n\n\nDetailView <span id=\"detail-view\"></span>\n----------\n\nThe [[yii\\widgets\\DetailView|DetailView]] widget displays the details of a single data [[yii\\widgets\\DetailView::$model|model]].\n\nIt is best used for displaying a model in a regular format (e.g. each model attribute is displayed as a row in a table).\nThe model can be either an instance or subclass of [[\\yii\\base\\Model]] such as an [active record](db-active-record.md) or an associative array.\n\nDetailView uses the [[yii\\widgets\\DetailView::$attributes|$attributes]] property to determine which model attributes should be displayed and how they\nshould be formatted. See the [formatter section](output-formatting.md) for available formatting options.\n\nA typical usage of DetailView is as follows:\n\n```php\necho DetailView::widget([\n    'model' => $model,\n    'attributes' => [\n        'title',                                           // title attribute (in plain text)\n        'description:html',                                // description attribute formatted as HTML\n        [                                                  // the owner name of the model\n            'label' => 'Owner',\n            'value' => $model->owner->name,            \n            'contentOptions' => ['class' => 'bg-red'],     // HTML attributes to customize value tag\n            'captionOptions' => ['tooltip' => 'Tooltip'],  // HTML attributes to customize label tag\n        ],\n        'created_at:datetime',                             // creation date formatted as datetime\n    ],\n]);\n```\n\nRemember that unlike [[yii\\widgets\\GridView|GridView]] which processes a set of models,\n[[yii\\widgets\\DetailView|DetailView]] processes just one. So most of the time there is no need for using closure since\n`$model` is the only one model for display and available in view as a variable.\n\nHowever some cases can make using of closure useful. For example when `visible` is specified and you want to prevent\n`value` calculations in case it evaluates to `false`:\n\n```php\necho DetailView::widget([\n    'model' => $model,\n    'attributes' => [\n        [\n            'attribute' => 'owner',\n            'value' => function ($model) {\n                return $model->owner->name;\n            },\n            'visible' => \\Yii::$app->user->can('posts.owner.view'),\n        ],\n    ],\n]);\n```\n\nListView <span id=\"list-view\"></span>\n--------\n\nThe [[yii\\widgets\\ListView|ListView]] widget is used to display data from a [data provider](output-data-providers.md).\nEach data model is rendered using the specified [[yii\\widgets\\ListView::$itemView|view file]].\nSince it provides features such as pagination, sorting and filtering out of the box, it is handy both to display\ninformation to end user and to create data managing UI.\n\nA typical usage is as follows:\n\n```php\nuse yii\\widgets\\ListView;\nuse yii\\data\\ActiveDataProvider;\n\n$dataProvider = new ActiveDataProvider([\n    'query' => Post::find(),\n    'pagination' => [\n        'pageSize' => 20,\n    ],\n]);\necho ListView::widget([\n    'dataProvider' => $dataProvider,\n    'itemView' => '_post',\n]);\n```\n\nThe `_post` view file could contain the following:\n\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\helpers\\HtmlPurifier;\n?>\n<div class=\"post\">\n    <h2><?= Html::encode($model->title) ?></h2>\n\n    <?= HtmlPurifier::process($model->text) ?>    \n</div>\n```\n\nIn the view file above, the current data model is available as `$model`. Additionally the following variables are available:\n\n- `$key`: mixed, the key value associated with the data item.\n- `$index`: integer, the zero-based index of the data item in the items array returned by the data provider.\n- `$widget`: ListView, this widget instance.\n\nIf you need to pass additional data to each view, you can use the [[yii\\widgets\\ListView::$viewParams|$viewParams]] property\nto pass key value pairs like the following:\n\n```php\necho ListView::widget([\n    'dataProvider' => $dataProvider,\n    'itemView' => '_post',\n    'viewParams' => [\n        'fullView' => true,\n        'context' => 'main-page',\n        // ...\n    ],\n]);\n```\n\nThese are then also available as variables in the view.\n\n\nGridView <span id=\"grid-view\"></span>\n--------\n\nData grid or [[yii\\grid\\GridView|GridView]] is one of the most powerful Yii widgets. It is extremely useful if you need to quickly build the admin\nsection of the system. It takes data from a [data provider](output-data-providers.md) and renders each row using a set of [[yii\\grid\\GridView::columns|columns]]\npresenting data in the form of a table.\n\nEach row of the table represents the data of a single data item, and a column usually represents an attribute of\nthe item (some columns may correspond to complex expressions of attributes or static text).\n\nThe minimal code needed to use GridView is as follows:\n\n```php\nuse yii\\grid\\GridView;\nuse yii\\data\\ActiveDataProvider;\n\n$dataProvider = new ActiveDataProvider([\n    'query' => Post::find(),\n    'pagination' => [\n        'pageSize' => 20,\n    ],\n]);\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n]);\n```\n\nThe above code first creates a data provider and then uses GridView to display every attribute in every row taken from\nthe data provider. The displayed table is equipped with sorting and pagination functionality out of the box.\n\n\n### Grid columns <span id=\"grid-columns\"></span>\n\nThe columns of the grid table are configured in terms of [[yii\\grid\\Column]] classes, which are\nconfigured in the [[yii\\grid\\GridView::columns|columns]] property of GridView configuration.\nDepending on column type and settings these are able to present data differently.\nThe default class is [[yii\\grid\\DataColumn]], which represents a model attribute and can be sorted and filtered by.\n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'columns' => [\n        ['class' => 'yii\\grid\\SerialColumn'],\n        // Simple columns defined by the data contained in $dataProvider.\n        // Data from the model's column will be used.\n        'id',\n        'username',\n        // More complex one.\n        [\n            'class' => 'yii\\grid\\DataColumn', // can be omitted, as it is the default\n            'value' => function ($data) {\n                return $data->name; // $data['name'] for array data, e.g. using SqlDataProvider.\n            },\n        ],\n    ],\n]);\n```\n\nNote that if the [[yii\\grid\\GridView::columns|columns]] part of the configuration isn't specified,\nYii tries to show all possible columns of the data provider's model.\n\n\n### Column classes <span id=\"column-classes\"></span>\n\nGrid columns could be customized by using different column classes:\n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'columns' => [\n        [\n            'class' => 'yii\\grid\\SerialColumn', // <-- here\n            // you may configure additional properties here\n        ],\n```\n\nIn addition to column classes provided by Yii that we'll review below, you can create your own column classes.\n\nEach column class extends from [[yii\\grid\\Column]] so that there are some common options you can set while configuring\ngrid columns.\n\n- [[yii\\grid\\Column::header|header]] allows to set content for header row.\n- [[yii\\grid\\Column::footer|footer]] allows to set content for footer row.\n- [[yii\\grid\\Column::visible|visible]] defines if the column should be visible.\n- [[yii\\grid\\Column::content|content]] allows you to pass a valid PHP callback that will return data for a row. The format is the following:\n\n  ```php\n  function ($model, $key, $index, $column) {\n      return 'a string';\n  }\n  ```\n\nYou may specify various container HTML options by passing arrays to:\n\n- [[yii\\grid\\Column::headerOptions|headerOptions]]\n- [[yii\\grid\\Column::footerOptions|footerOptions]]\n- [[yii\\grid\\Column::filterOptions|filterOptions]]\n- [[yii\\grid\\Column::contentOptions|contentOptions]]\n\n\n#### Data column <span id=\"data-column\"></span>\n\n[[yii\\grid\\DataColumn|Data column]] is used for displaying and sorting data. It is the default column type so the specifying class could be omitted when\nusing it.\n\nThe main setting of the data column is its [[yii\\grid\\DataColumn::format|format]] property. Its values\ncorrespond to methods in the `formatter` [application component](structure-application-components.md) that is [[\\yii\\i18n\\Formatter|Formatter]] by default:\n\n```php\necho GridView::widget([\n    'columns' => [\n        [\n            'attribute' => 'name',\n            'format' => 'text'\n        ],\n        [\n            'attribute' => 'birthday',\n            'format' => ['date', 'php:Y-m-d']\n        ],\n        'created_at:datetime', // shortcut format\n        [\n            'label' => 'Education',\n            'attribute' => 'education',\n            'filter' => ['0' => 'Elementary', '1' => 'Secondary', '2' => 'Higher'],\n            'filterInputOptions' => ['prompt' => 'All educations', 'class' => 'form-control', 'id' => null]\n        ],\n    ],\n]);\n```\n\nIn the above, `text` corresponds to [[\\yii\\i18n\\Formatter::asText()]]. The value of the column is passed as the first\nargument. In the second column definition, `date` corresponds to [[\\yii\\i18n\\Formatter::asDate()]]. The value of the\ncolumn is, again, passed as the first argument while 'php:Y-m-d' is used as the second argument value.\n\nFor a list of available formatters see the [section about Data Formatting](output-formatting.md).\n\nFor configuring data columns there is also a shortcut format which is described in the\nAPI documentation for [[yii\\grid\\GridView::columns|columns]].\n\nUse [[yii\\grid\\DataColumn::filter|filter]] and [[yii\\grid\\DataColumn::filterInputOptions|filterInputOptions]] to\ncontrol HTML for the filter input.\n\nBy default, column headers are rendered by [[yii\\data\\Sort::link]]. It could be adjusted using [[yii\\grid\\Column::header]].\nTo change header text you should set [[yii\\grid\\DataColumn::$label]] like in the example above. \nBy default the label will be populated from data model. For more details see [[yii\\grid\\DataColumn::getHeaderCellLabel]].\n\n#### Action column <span id=\"action-column\"></span>\n\n[[yii\\grid\\ActionColumn|Action column]] displays action buttons such as update or delete for each row.\n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'columns' => [\n        [\n            'class' => 'yii\\grid\\ActionColumn',\n            // you may configure additional properties here\n        ],\n```\n\nAvailable properties you can configure are:\n\n- [[yii\\grid\\ActionColumn::controller|controller]] is the ID of the controller that should handle the actions. If not set, it will use the currently active\n  controller.\n- [[yii\\grid\\ActionColumn::template|template]] defines the template used for composing each cell in the action column. Tokens enclosed within curly brackets are\n  treated as controller action IDs (also called *button names* in the context of action column). They will be replaced\n  by the corresponding button rendering callbacks specified in [[yii\\grid\\ActionColumn::$buttons|buttons]]. For example, the token `{view}` will be\n  replaced by the result of the callback `buttons['view']`. If a callback cannot be found, the token will be replaced\n  with an empty string. The default tokens are `{view} {update} {delete}`.\n- [[yii\\grid\\ActionColumn::buttons|buttons]] is an array of button rendering callbacks. The array keys are the button names (without curly brackets),\n  and the values are the corresponding button rendering callbacks. The callbacks should use the following signature:\n\n  ```php\n  function ($url, $model, $key) {\n      // return the button HTML code\n  }\n  ```\n\n  In the code above, `$url` is the URL that the column creates for the button, `$model` is the model object being\n  rendered for the current row, and `$key` is the key of the model in the data provider array.\n\n- [[yii\\grid\\ActionColumn::urlCreator|urlCreator]] is a callback that creates a button URL using the specified model information. The signature of\n  the callback should be the same as that of [[yii\\grid\\ActionColumn::createUrl()]]. If this property is not set,\n  button URLs will be created using [[yii\\grid\\ActionColumn::createUrl()]].\n- [[yii\\grid\\ActionColumn::visibleButtons|visibleButtons]] is an array of visibility conditions for each button.\n  The array keys are the button names (without curly brackets), and the values are the boolean `true`/`false` or the\n  anonymous function. When the button name is not specified in this array it will be shown by default.\n  The callbacks must use the following signature:\n\n  ```php\n  function ($model, $key, $index) {\n      return $model->status === 'editable';\n  }\n  ```\n\n  Or you can pass a boolean value:\n\n  ```php\n  [\n      'update' => \\Yii::$app->user->can('update')\n  ]\n  ```\n\n#### Checkbox column <span id=\"checkbox-column\"></span>\n\n[[yii\\grid\\CheckboxColumn|Checkbox column]] displays a column of checkboxes.\n\nTo add a CheckboxColumn to the GridView, add it to the [[yii\\grid\\GridView::$columns|columns]] configuration as follows:\n\n```php\necho GridView::widget([\n    'id' => 'grid',\n    'dataProvider' => $dataProvider,\n    'columns' => [\n        // ...\n        [\n            'class' => 'yii\\grid\\CheckboxColumn',\n            // you may configure additional properties here\n        ],\n    ],\n```\n\nUsers may click on the checkboxes to select rows of the grid. The selected rows may be obtained by calling the following\nJavaScript code:\n\n```javascript\nvar keys = $('#grid').yiiGridView('getSelectedRows');\n// keys is an array consisting of the keys associated with the selected rows\n```\n\n#### Serial column <span id=\"serial-column\"></span>\n\n[[yii\\grid\\SerialColumn|Serial column]] renders row numbers starting with `1` and going forward.\n\nUsage is as simple as the following:\n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'columns' => [\n        ['class' => 'yii\\grid\\SerialColumn'], // <-- here\n        // ...\n```\n\n\n### Sorting data <span id=\"sorting-data\"></span>\n\n> Note: This section is under development.\n>\n> - https://github.com/yiisoft/yii2/issues/1576\n\n### Filtering data <span id=\"filtering-data\"></span>\n\nFor filtering data, the GridView needs a [model](structure-models.md) that represents the search criteria which is\nusually taken from the filter fields in the GridView table.\nA common practice when using [active records](db-active-record.md) is to create a search Model class\nthat provides needed functionality (it can be generated for you by [Gii](start-gii.md)). This class defines the validation\nrules to show filter controls on the GridView table and to provide a `search()` method that will return the data \nprovider with an adjusted query that processes the search criteria.\n\nTo add the search capability for the `Post` model, we can create a `PostSearch` model like the following example:\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse Yii;\nuse yii\\base\\Model;\nuse yii\\data\\ActiveDataProvider;\n\nclass PostSearch extends Post\n{\n    public function rules()\n    { \n        // only fields in rules() are searchable\n        return [\n            [['id'], 'integer'],\n            [['title', 'creation_date'], 'safe'],\n        ];\n    }\n\n    public function scenarios()\n    {\n        // bypass scenarios() implementation in the parent class\n        return Model::scenarios();\n    }\n\n    public function search($params)\n    {\n        $query = Post::find();\n\n        $dataProvider = new ActiveDataProvider([\n            'query' => $query,\n        ]);\n\n        // load the search form data and validate\n        if (!($this->load($params) && $this->validate())) {\n            return $dataProvider;\n        }\n\n        // adjust the query by adding the filters\n        $query->andFilterWhere(['id' => $this->id]);\n        $query->andFilterWhere(['like', 'title', $this->title])\n              ->andFilterWhere(['like', 'creation_date', $this->creation_date]);\n\n        return $dataProvider;\n    }\n}\n```\n\n> Tip: See [Query Builder](db-query-builder.md) and especially [Filter Conditions](db-query-builder.md#filter-conditions)\n> to learn how to build filtering query.\n\nYou can use this function in the controller to get the dataProvider for the GridView:\n\n```php\n$searchModel = new PostSearch();\n$dataProvider = $searchModel->search(Yii::$app->request->get());\n\nreturn $this->render('myview', [\n    'dataProvider' => $dataProvider,\n    'searchModel' => $searchModel,\n]);\n```\n\nAnd in the view you then assign the `$dataProvider` and `$searchModel` to the GridView:\n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'filterModel' => $searchModel,\n    'columns' => [\n        // ...\n    ],\n]);\n```\n\n### Separate filter form <span id=\"separate-filter-form\"></span>\n\nMost of the time using GridView header filters is enough, but in case you need a separate filter form,\nyou can easily add it as well. You can create partial view `_search.php` with the following contents:\n\n```php\n<?php\n\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n/**\n * @var \\yii\\web\\View $this\n * @var \\app\\models\\PostSearch $model\n * @var \\yii\\widgets\\ActiveForm $form\n */\n?>\n\n<div class=\"post-search\">\n    <?php $form = ActiveForm::begin([\n        'action' => ['index'],\n        'method' => 'get',\n    ]); ?>\n\n    <?= $form->field($model, 'title') ?>\n\n    <?= $form->field($model, 'creation_date') ?>\n\n    <div class=\"form-group\">\n        <?= Html::submitButton('Search', ['class' => 'btn btn-primary']) ?>\n        <?= Html::submitButton('Reset', ['class' => 'btn btn-default']) ?>\n    </div>\n\n    <?php ActiveForm::end(); ?>\n</div>\n```\n\nand include it in `index.php` view like so:\n\n```php\n<?= $this->render('_search', ['model' => $searchModel]) ?>\n```\n\n> Note: if you use Gii to generate CRUD code, the separate filter form (`_search.php`) is generated by default,\nbut is commented in `index.php` view. Uncomment it and it's ready to use!\n\nSeparate filter form is useful when you need to filter by fields, that are not displayed in GridView\nor for special filtering conditions, like date range. For filtering by date range we can add non DB attributes\n`createdFrom` and `createdTo` to the search model:\n\n```php\nclass PostSearch extends Post\n{\n    /**\n     * @var string\n     */\n    public $createdFrom;\n\n    /**\n     * @var string\n     */\n    public $createdTo;\n}\n```\n\nExtend query conditions in the `search()` method like so:\n\n```php\n$query->andFilterWhere(['>=', 'creation_date', $this->createdFrom])\n      ->andFilterWhere(['<=', 'creation_date', $this->createdTo]);\n```\n\nAnd add the representative fields to the filter form:\n\n```php\n<?= $form->field($model, 'creationFrom') ?>\n\n<?= $form->field($model, 'creationTo') ?>\n```\n\n### Working with model relations <span id=\"working-with-model-relations\"></span>\n\nWhen displaying active records in a GridView you might encounter the case where you display values of related\ncolumns such as the post author's name instead of just his `id`.\nYou do this by defining the attribute name in [[yii\\grid\\GridView::$columns]] as `author.name` when the `Post` model\nhas a relation named `author` and the author model has an attribute `name`.\nThe GridView will then display the name of the author but sorting and filtering are not enabled by default.\nYou have to adjust the `PostSearch` model that has been introduced in the last section to add this functionality.\n\nTo enable sorting on a related column you have to join the related table and add the sorting rule\nto the Sort component of the data provider:\n\n```php\n$query = Post::find();\n$dataProvider = new ActiveDataProvider([\n    'query' => $query,\n]);\n\n// join with relation `author` that is a relation to the table `users`\n// and set the table alias to be `author`\n$query->joinWith(['author' => function($query) { $query->from(['author' => 'users']); }]);\n// since version 2.0.7, the above line can be simplified to $query->joinWith('author AS author');\n// enable sorting for the related column\n$dataProvider->sort->attributes['author.name'] = [\n    'asc' => ['author.name' => SORT_ASC],\n    'desc' => ['author.name' => SORT_DESC],\n];\n\n// ...\n```\n\nFiltering also needs the joinWith call as above. You also need to define the searchable column in attributes and rules like this:\n\n```php\npublic function attributes()\n{\n    // add related fields to searchable attributes\n    return array_merge(parent::attributes(), ['author.name']);\n}\n\npublic function rules()\n{\n    return [\n        [['id'], 'integer'],\n        [['title', 'creation_date', 'author.name'], 'safe'],\n    ];\n}\n```\n\nIn `search()` you then just add another filter condition with:\n\n```php\n$query->andFilterWhere(['LIKE', 'author.name', $this->getAttribute('author.name')]);\n```\n\n> Info: In the above we use the same string for the relation name and the table alias; however, when your alias and relation name\n> differ, you have to pay attention to where you use the alias and where you use the relation name.\n> A simple rule for this is to use the alias in every place that is used to build the database query and the\n> relation name in all other definitions such as `attributes()` and `rules()` etc.\n>\n> For example, if you use the alias `au` for the author relation table, the joinWith statement looks like the following:\n>\n> ```php\n> $query->joinWith(['author au']);\n> ```\n>\n> It is also possible to just call `$query->joinWith(['author']);` when the alias is defined in the relation definition.\n>\n> The alias has to be used in the filter condition but the attribute name stays the same:\n>\n> ```php\n> $query->andFilterWhere(['LIKE', 'au.name', $this->getAttribute('author.name')]);\n> ```\n>\n> The same is true for the sorting definition:\n>\n> ```php\n> $dataProvider->sort->attributes['author.name'] = [\n>      'asc' => ['au.name' => SORT_ASC],\n>      'desc' => ['au.name' => SORT_DESC],\n> ];\n> ```\n>\n> Also, when specifying the [[yii\\data\\Sort::defaultOrder|defaultOrder]] for sorting, you need to use the relation name\n> instead of the alias:\n>\n> ```php\n> $dataProvider->sort->defaultOrder = ['author.name' => SORT_ASC];\n> ```\n\n> Info: For more information on `joinWith` and the queries performed in the background, check the\n> [active record docs on joining with relations](db-active-record.md#joining-with-relations).\n\n#### Using SQL views for filtering, sorting and displaying data <span id=\"using-sql-views\"></span>\n\nThere is also another approach that can be faster and more useful - SQL views. For example, if we need to show the gridview\nwith users and their profiles, we can do so in this way:\n\n```sql\nCREATE OR REPLACE VIEW vw_user_info AS\n    SELECT user.*, user_profile.lastname, user_profile.firstname\n    FROM user, user_profile\n    WHERE user.id = user_profile.user_id\n```\n\nThen you need to create the ActiveRecord that will be representing this view:\n\n```php\n\nnamespace app\\models\\views\\grid;\n\nuse yii\\db\\ActiveRecord;\n\nclass UserView extends ActiveRecord\n{\n\n    /**\n     * {@inheritdoc}\n     */\n    public static function tableName()\n    {\n        return 'vw_user_info';\n    }\n\n    public static function primaryKey()\n    {\n        return ['id'];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function rules()\n    {\n        return [\n            // define here your rules\n        ];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function attributeLabels()\n    {\n        return [\n            // define here your attribute labels\n        ];\n    }\n\n\n}\n```\n\nAfter that you can use this UserView active record with search models, without additional specification of sorting and filtering attributes.\nAll attributes will be working out of the box. Note that this approach has several pros and cons:\n\n- you don't need to specify different sorting and filtering conditions. Everything works out of the box;\n- it can be much faster because of the data size, count of sql queries performed (for each relation you will not need any additional query);\n- since this is just a simple mapping UI on the sql view it lacks some domain logic that is in your entities, so if you have some methods like `isActive`,\n`isDeleted` or others that will influence the UI, you will need to duplicate them in this class too.\n\n\n### Multiple GridViews on one page <span id=\"multiple-gridviews\"></span>\n\nYou can use more than one GridView on a single page but some additional configuration is needed so that\nthey do not interfere with each other.\nWhen using multiple instances of GridView you have to configure different parameter names for\nthe generated sort and pagination links so that each GridView has its own individual sorting and pagination.\nYou do so by setting the [[yii\\data\\Sort::sortParam|sortParam]] and [[yii\\data\\Pagination::pageParam|pageParam]]\nof the dataProvider's [[yii\\data\\BaseDataProvider::$sort|sort]] and [[yii\\data\\BaseDataProvider::$pagination|pagination]]\ninstances.\n\nAssume we want to list the `Post` and `User` models for which we have already prepared two data providers\nin `$userProvider` and `$postProvider`:\n\n```php\nuse yii\\grid\\GridView;\n\n$userProvider->pagination->pageParam = 'user-page';\n$userProvider->sort->sortParam = 'user-sort';\n\n$postProvider->pagination->pageParam = 'post-page';\n$postProvider->sort->sortParam = 'post-sort';\n\necho '<h1>Users</h1>';\necho GridView::widget([\n    'dataProvider' => $userProvider,\n]);\n\necho '<h1>Posts</h1>';\necho GridView::widget([\n    'dataProvider' => $postProvider,\n]);\n```\n\n### Using GridView with Pjax <span id=\"using-gridview-with-pjax\"></span>\n\nThe [[yii\\widgets\\Pjax|Pjax]] widget allows you to update a certain section of a\npage instead of reloading the entire page. You can use it to update only the\n[[yii\\grid\\GridView|GridView]] content when using filters.\n\n```php\nuse yii\\widgets\\Pjax;\nuse yii\\grid\\GridView;\n\nPjax::begin([\n    // PJax options\n]);\n    Gridview::widget([\n        // GridView options\n    ]);\nPjax::end();\n```\n\nPjax also works for the links inside the [[yii\\widgets\\Pjax|Pjax]] widget and\nfor the links specified by [[yii\\widgets\\Pjax::$linkSelector|Pjax::$linkSelector]].\nBut this might be a problem for the links of an [[yii\\grid\\ActionColumn|ActionColumn]].\nTo prevent this, add the HTML attribute `data-pjax=\"0\"` to the links when you edit\nthe [[yii\\grid\\ActionColumn::$buttons|ActionColumn::$buttons]] property.\n\n#### GridView/ListView with Pjax in Gii\n\nSince 2.0.5, the CRUD generator of [Gii](start-gii.md) has an option called\n`$enablePjax` that can be used via either web interface or command line.\n\n```php\nyii gii/crud --controllerClass=\"backend\\\\controllers\\PostController\" \\\n  --modelClass=\"common\\\\models\\\\Post\" \\\n  --enablePjax=1\n```\n\nWhich generates a [[yii\\widgets\\Pjax|Pjax]] widget wrapping the\n[[yii\\grid\\GridView|GridView]] or [[yii\\widgets\\ListView|ListView]] widgets.\n\nFurther reading\n---------------\n\n- [Rendering Data in Yii 2 with GridView and ListView](https://www.sitepoint.com/rendering-data-in-yii-2-with-gridview-and-listview/) by Arno Slatius.\n"
  },
  {
    "path": "docs/guide/output-formatting.md",
    "content": "Data Formatting\n===============\n\nTo display data in a more readable format for users, you may format them using the `formatter` [application component](structure-application-components.md).\nBy default the formatter is implemented by [[yii\\i18n\\Formatter]] which provides a set of methods to format data as \ndate/time, numbers, currencies, and other commonly used formats. You can use the formatter like the following,\n\n```php\n$formatter = \\Yii::$app->formatter;\n\n// output: January 1, 2014\necho $formatter->asDate('2014-01-01', 'long');\n \n// output: 12.50%\necho $formatter->asPercent(0.125, 2);\n \n// output: <a href=\"mailto:cebe@example.com\">cebe@example.com</a>\necho $formatter->asEmail('cebe@example.com'); \n\n// output: Yes\necho $formatter->asBoolean(true); \n// it also handles display of null values:\n\n// output: (not set)\necho $formatter->asDate(null); \n```\n\nAs you can see, all these methods are named as `asXyz()`, where `Xyz` stands for a supported format. Alternatively,\nyou may format data using the generic method [[yii\\i18n\\Formatter::format()|format()]], which allows you to control\nthe desired format programmatically and is commonly used by widgets like [[yii\\grid\\GridView]] and [[yii\\widgets\\DetailView]].\nFor example,\n\n```php\n// output: January 1, 2014\necho Yii::$app->formatter->format('2014-01-01', 'date'); \n\n// you can also use an array to specify parameters for the format method:\n// `2` is the value for the $decimals parameter of the asPercent()-method.\n// output: 12.50%\necho Yii::$app->formatter->format(0.125, ['percent', 2]); \n```\n\n> Note: The formatter component is designed to format values to be displayed for the end user. If you want\n> to convert user input into machine readable format, or just format a date in a machine readable format,\n> the formatter is not the right tool for that.\n> To convert user input for date and number values you may use [[yii\\validators\\DateValidator]] and [[yii\\validators\\NumberValidator]]\n> respectively. For simple conversion between machine readable date and time formats,\n> the PHP [date()](https://www.php.net/manual/en/function.date.php)-function is enough.\n\n## Configuring Formatter <span id=\"configuring-formatter\"></span>\n\nYou may customize the formatting rules by configuring the `formatter` component in the [application configuration](concept-configurations.md#application-configurations).\nFor example,\n\n```php\nreturn [\n    'components' => [\n        'formatter' => [\n            'dateFormat' => 'dd.MM.yyyy',\n            'decimalSeparator' => ',',\n            'thousandSeparator' => ' ',\n            'currencyCode' => 'EUR',\n       ],\n    ],\n];\n```\n\nPlease refer to [[yii\\i18n\\Formatter]] for the properties that may be configured.\n\n\n## Formatting Date and Time Values <span id=\"date-and-time\"></span>\n\nThe formatter supports the following output formats that are related with date and time:\n\n- [[yii\\i18n\\Formatter::asDate()|date]]: the value is formatted as a date, e.g. `January 01, 2014`.\n- [[yii\\i18n\\Formatter::asTime()|time]]: the value is formatted as a time, e.g. `14:23`.\n- [[yii\\i18n\\Formatter::asDatetime()|datetime]]: the value is formatted as date and time, e.g. `January 01, 2014 14:23`.\n- [[yii\\i18n\\Formatter::asTimestamp()|timestamp]]: the value is formatted as a [unix timestamp](https://en.wikipedia.org/wiki/Unix_time), e.g. `1412609982`.\n- [[yii\\i18n\\Formatter::asRelativeTime()|relativeTime]]: the value is formatted as the time interval between a date\n  and now in human readable form e.g. `1 hour ago`.\n- [[yii\\i18n\\Formatter::asDuration()|duration]]: the value is formatted as a duration in human readable format. e.g. `1 day, 2 minutes`.\n\nThe default date and time formats used for the [[yii\\i18n\\Formatter::asDate()|date]], [[yii\\i18n\\Formatter::asTime()|time]],\nand [[yii\\i18n\\Formatter::asDatetime()|datetime]] methods can be customized globally by configuring  \n[[yii\\i18n\\Formatter::dateFormat|dateFormat]], [[yii\\i18n\\Formatter::timeFormat|timeFormat]], and\n[[yii\\i18n\\Formatter::datetimeFormat|datetimeFormat]].\n\nYou can specify date and time formats using the [ICU syntax](https://unicode-org.github.io/icu/userguide/format_parse/datetime/).\nYou can also use the [PHP date() syntax](https://www.php.net/manual/en/function.date.php) with a prefix `php:` to differentiate\nit from ICU syntax. For example,\n\n```php\n// ICU format\necho Yii::$app->formatter->asDate('now', 'yyyy-MM-dd'); // 2014-10-06\n\n// PHP date()-format\necho Yii::$app->formatter->asDate('now', 'php:Y-m-d'); // 2014-10-06\n```\n\n> Info: Some letters of the PHP format syntax are not supported by ICU and thus the PHP intl extension and can not be used\n> in Yii formatter. Most of these (`w`, `t`, `L`, `B`, `u`, `I`, `Z`) are not really useful for formatting dates but rather\n> used when doing date math. `S` and `U` however may be useful. Their behavior can be achieved by doing the following:\n>\n> - for `S`, which is the English ordinal suffix for the day of the month (e.g. st, nd, rd or th.), the following replacement can be used:\n>\n>   ```php\n>   $f = Yii::$app->formatter;\n>   $d = $f->asOrdinal($f->asDate('2017-05-15', 'php:j'));\n>   echo \"On the $d day of the month.\";  // prints \"On the 15th day of the month.\"\n>   ```\n>\n> - for `U`, the Unix Epoch, you can use the [[yii\\i18n\\Formatter::asTimestamp()|timestamp]] format.\n\nWhen working with applications that need to support multiple languages, you often need to specify different date\nand time formats for different locales. To simplify this task, you may use format shortcuts (e.g. `long`, `short`), instead.\nThe formatter will turn a format shortcut into an appropriate format according to the currently active [[yii\\i18n\\Formatter::locale|locale]].\nThe following format shortcuts are supported (the examples assume `en_GB` is the active locale):\n\n- `short`: will output `06/10/2014` for date and `15:58` for time;\n- `medium`: will output `6 Oct 2014` and `15:58:42`;\n- `long`: will output `6 October 2014` and `15:58:42 GMT`;\n- `full`: will output `Monday, 6 October 2014` and `15:58:42 GMT`.\n\nSince version 2.0.7 it is also possible to format dates in different calendar systems.\nPlease refer to the API documentation of the formatters [[yii\\i18n\\Formatter::$calendar|$calendar]]-property on how to set a different calendar.\n\n\n### Time Zones <span id=\"time-zones\"></span>\n\nWhen formatting date and time values, Yii will convert them to the target [[yii\\i18n\\Formatter::timeZone|time zone]].\nThe value being formatted is assumed to be in UTC, unless a time zone is explicitly given or you have configured\n[[yii\\i18n\\Formatter::defaultTimeZone]].\n\nIn the following examples, we assume the target [[yii\\i18n\\Formatter::timeZone|time zone]] is set as `Europe/Berlin`. \n\n```php\n// formatting a UNIX timestamp as a time\necho Yii::$app->formatter->asTime(1412599260); // 14:41:00\n\n// formatting a datetime string (in UTC) as a time \necho Yii::$app->formatter->asTime('2014-10-06 12:41:00'); // 14:41:00\n\n// formatting a datetime string (in CEST) as a time\necho Yii::$app->formatter->asTime('2014-10-06 14:41:00 CEST'); // 14:41:00\n```\n\nIf the [[yii\\i18n\\Formatter::timeZone|time zone]] is not set explicitly on the formatter component, the \n[[yii\\base\\Application::timeZone|time zone configured in the application]] is used, which is the same time zone\nas set in the PHP configuration.\n\n> Note: As time zones are subject to rules made by the governments around the world and may change frequently, it is\n> likely that you do not have the latest information in the time zone database installed on your system.\n> You may refer to the [ICU manual](https://unicode-org.github.io/icu/userguide/datetime/timezone/#updating-the-time-zone-data)\n> for details on updating the time zone database. Please also read\n> [Setting up your PHP environment for internationalization](tutorial-i18n.md#setup-environment).  \n\n## Formatting Numbers <span id=\"numbers\"></span>\n\nThe formatter supports the following output formats that are related with numbers:\n\n- [[yii\\i18n\\Formatter::asInteger()|integer]]: the value is formatted as an integer e.g. `42`.\n- [[yii\\i18n\\Formatter::asDecimal()|decimal]]: the value is formatted as a decimal number considering decimal and thousand\n  separators e.g. `2,542.123` or `2.542,123`.\n- [[yii\\i18n\\Formatter::asPercent()|percent]]: the value is formatted as a percent number e.g. `42%`.\n- [[yii\\i18n\\Formatter::asScientific()|scientific]]: the value is formatted as a number in scientific format e.g. `4.2E4`.\n- [[yii\\i18n\\Formatter::asCurrency()|currency]]: the value is formatted as a currency value e.g. `£420.00`.\n  Note that for this function to work properly, the locale needs to include a country part e.g. `en_GB` or `en_US` because language only\n  would be ambiguous in this case.\n- [[yii\\i18n\\Formatter::asSize()|size]]: the value that is a number of bytes is formatted as a human readable size e.g. `410 kibibytes`.\n- [[yii\\i18n\\Formatter::asShortSize()|shortSize]]: is the short version of [[yii\\i18n\\Formatter::asSize()|size]], e.g. `410 KiB`.\n\nThe format for number formatting can be adjusted using the [[yii\\i18n\\Formatter::decimalSeparator|decimalSeparator]] and\n[[yii\\i18n\\Formatter::thousandSeparator|thousandSeparator]], both of which take default values according to the \nactive [[yii\\i18n\\Formatter::locale|locale]].\n\nFor more advanced configuration, [[yii\\i18n\\Formatter::numberFormatterOptions]] and [[yii\\i18n\\Formatter::numberFormatterTextOptions]]\ncan be used to configure the [NumberFormatter class](https://www.php.net/manual/en/class.numberformatter.php) used internally\nto implement the formatter. For example, to adjust the maximum and minimum value of fraction digits, you can configure \nthe [[yii\\i18n\\Formatter::numberFormatterOptions]] property like the following:\n\n```php\n'numberFormatterOptions' => [\n    NumberFormatter::MIN_FRACTION_DIGITS => 0,\n    NumberFormatter::MAX_FRACTION_DIGITS => 2,\n]\n```\n\n\n## Other Formats <span id=\"other\"></span>\n\nBesides date/time and number formats, Yii also supports other commonly used formats, including\n\n- [[yii\\i18n\\Formatter::asRaw()|raw]]: the value is outputted as is, this is a pseudo-formatter that has no effect except that\n  `null` values will be formatted using [[nullDisplay]].\n- [[yii\\i18n\\Formatter::asText()|text]]: the value is HTML-encoded.\n  This is the default format used by the [GridView DataColumn](output-data-widgets.md#data-column).\n- [[yii\\i18n\\Formatter::asNtext()|ntext]]: the value is formatted as an HTML-encoded plain text with newlines converted\n  into line breaks.\n- [[yii\\i18n\\Formatter::asParagraphs()|paragraphs]]: the value is formatted as HTML-encoded text paragraphs wrapped\n  into `<p>` tags.\n- [[yii\\i18n\\Formatter::asHtml()|html]]: the value is purified using [[HtmlPurifier]] to avoid XSS attacks. You can\n  pass additional options such as `['html', ['Attr.AllowedFrameTargets' => ['_blank']]]`.\n- [[yii\\i18n\\Formatter::asEmail()|email]]: the value is formatted as a `mailto`-link.\n- [[yii\\i18n\\Formatter::asImage()|image]]: the value is formatted as an image tag.\n- [[yii\\i18n\\Formatter::asUrl()|url]]: the value is formatted as a hyperlink.\n- [[yii\\i18n\\Formatter::asBoolean()|boolean]]: the value is formatted as a boolean. By default `true` is rendered\n  as `Yes` and `false` as `No`, translated to the current application language. You can adjust this by configuring\n  the [[yii\\i18n\\Formatter::booleanFormat]] property.\n\n\n## Null Values <span id=\"null-values\"></span>\n\nNull values are specially formatted. Instead of displaying an empty string, the formatter will convert it into a\npreset string which defaults to `(not set)` translated into the current application language. You can configure the\n[[yii\\i18n\\Formatter::nullDisplay|nullDisplay]] property to customize this string.\n\n\n## Localizing Data Format <span id=\"localizing-data-format\"></span>\n\nAs aforementioned, the formatter may use the currently active [[yii\\i18n\\Formatter::locale|locale]] to determine how\nto format a value that is suitable in the target country/region. For example, the same date value may be formatted\ndifferently for different locales:\n\n```php\nYii::$app->formatter->locale = 'en-US';\necho Yii::$app->formatter->asDate('2014-01-01'); // output: January 1, 2014\n\nYii::$app->formatter->locale = 'de-DE';\necho Yii::$app->formatter->asDate('2014-01-01'); // output: 1. Januar 2014\n\nYii::$app->formatter->locale = 'ru-RU';\necho Yii::$app->formatter->asDate('2014-01-01'); // output: 1 января 2014 г.\n```\n\nBy default, the currently active [[yii\\i18n\\Formatter::locale|locale]] is determined by the value of \n[[yii\\base\\Application::language]]. You may override it by setting the [[yii\\i18n\\Formatter::locale]] property explicitly.\n\n> Note: The Yii formatter relies on the [PHP intl extension](https://www.php.net/manual/en/book.intl.php) to support\n> localized data formatting. Because different versions of the ICU library compiled with PHP may cause different\n> formatting results, it is recommended that you use the same ICU version for all your environments. For more details,\n> please refer to [Setting up your PHP environment for internationalization](tutorial-i18n.md#setup-environment).\n>\n> If the intl extension is not installed, the data will not be localized. \n>\n> Note that for date values that are before year 1901 or after 2038, they will not be localized on 32-bit systems, even\n> if the intl extension is installed. This is because in this case ICU is using 32-bit UNIX timestamps to date values.\n"
  },
  {
    "path": "docs/guide/output-pagination.md",
    "content": "Pagination\n==========\n\nWhen there are too much data to be displayed on a single page, a common strategy is to display them in multiple\npages and on each page only display a small portion of the data. This strategy is known as *pagination*.\n\nYii uses a [[yii\\data\\Pagination]] object to represent the information about a pagination scheme. In particular,\n\n* [[yii\\data\\Pagination::$totalCount|total count]] specifies the total number of data items. Note that this\n  is usually much more than the number of data items needed to display on a single page.\n* [[yii\\data\\Pagination::$pageSize|page size]] specifies how many data items each page contains. The default\n  value is 20.\n* [[yii\\data\\Pagination::$page|current page]] gives the current page number (zero-based). The default value is 0, meaning the first page.\n\nWith a fully specified [[yii\\data\\Pagination]] object, you can retrieve and display data partially. For example,\nif you are fetching data from a database, you can specify the `OFFSET` and `LIMIT` clause of the DB query with\nthe corresponding values provided by the pagination. Below is an example, \n\n```php\nuse yii\\data\\Pagination;\n\n// build a DB query to get all articles with status = 1\n$query = Article::find()->where(['status' => 1]);\n\n// get the total number of articles (but do not fetch the article data yet)\n$count = $query->count();\n\n// create a pagination object with the total count\n$pagination = new Pagination(['totalCount' => $count]);\n\n// limit the query using the pagination and retrieve the articles\n$articles = $query->offset($pagination->offset)\n    ->limit($pagination->limit)\n    ->all();\n```\n\nWhich page of articles will be returned in the above example? It depends on whether a query parameter named `page`\nis given. By default, the pagination will attempt to set the [[yii\\data\\Pagination::$page|current page]] to be\nthe value of the `page` parameter. If the parameter is not provided, then it will default to 0.\n\nTo facilitate building the UI element that supports pagination, Yii provides the [[yii\\widgets\\LinkPager]] widget\nthat displays a list of page buttons upon which users can click to indicate which page of data should be displayed.\nThe widget takes a pagination object so that it knows what is the current page and how many page buttons should\nbe displayed. For example,\n\n```php\nuse yii\\widgets\\LinkPager;\n\necho LinkPager::widget([\n    'pagination' => $pagination,\n]);\n```\n\nIf you want to build UI element manually, you may use [[yii\\data\\Pagination::createUrl()]] to create URLs that\nwould lead to different pages. The method requires a page parameter and will create a properly formatted URL\ncontaining the page parameter. For example,\n\n```php\n// specifies the route that the URL to be created should use\n// If you do not specify this, the currently requested route will be used\n$pagination->route = 'article/index';\n\n// displays: /index.php?r=article%2Findex&page=100\necho $pagination->createUrl(100);\n\n// displays: /index.php?r=article%2Findex&page=101\necho $pagination->createUrl(101);\n```\n\n> Tip: You can customize the name of the `page` query parameter by configuring the\n  [[yii\\data\\Pagination::pageParam|pageParam]] property when creating the pagination object.\n"
  },
  {
    "path": "docs/guide/output-sorting.md",
    "content": "Sorting\n=======\n\nWhen displaying multiple rows of data, it is often needed that the data be sorted according to some columns\nspecified by end users. Yii uses a [[yii\\data\\Sort]] object to represent the information about a sorting schema.\nIn particular, \n\n* [[yii\\data\\Sort::$attributes|attributes]] specifies the *attributes* by which the data can be sorted.\n  An attribute can be as simple as a [model attribute](structure-models.md#attributes). It can also be a composite\n  one by combining multiple model attributes or DB columns. More details will be given in the following.\n* [[yii\\data\\Sort::$attributeOrders|attributeOrders]] gives the currently requested ordering directions for \n  each attribute.\n* [[yii\\data\\Sort::$orders|orders]] gives the ordering directions in terms of the low-level columns.\n\nTo use [[yii\\data\\Sort]], first declare which attributes can be sorted. Then retrieve the currently requested\nordering information from [[yii\\data\\Sort::$attributeOrders|attributeOrders]] or [[yii\\data\\Sort::$orders|orders]]\nand use them to customize the data query. For example,\n\n```php\nuse yii\\data\\Sort;\n\n$sort = new Sort([\n    'attributes' => [\n        'age',\n        'name' => [\n            'asc' => ['first_name' => SORT_ASC, 'last_name' => SORT_ASC],\n            'desc' => ['first_name' => SORT_DESC, 'last_name' => SORT_DESC],\n            'default' => SORT_DESC,\n            'label' => 'Name',\n        ],\n    ],\n]);\n\n$articles = Article::find()\n    ->where(['status' => 1])\n    ->orderBy($sort->orders)\n    ->all();\n```\n\nIn the above example, two attributes are declared for the [[yii\\data\\Sort|Sort]] object: `age` and `name`. \n\nThe `age` attribute is a *simple* attribute corresponding to the `age` attribute of the `Article` Active Record class.\nIt is equivalent to the following declaration:\n\n```php\n'age' => [\n    'asc' => ['age' => SORT_ASC],\n    'desc' => ['age' => SORT_DESC],\n    'default' => SORT_ASC,\n    'label' => Inflector::camel2words('age'),\n]\n```\n\nThe `name` attribute is a *composite* attribute defined by `first_name` and `last_name` of `Article`. It is declared\nusing the following array structure:\n\n- The `asc` and `desc` elements specify how to sort by the attribute in ascending and descending directions, respectively.\n  Their values represent the actual columns and the directions by which the data should be sorted by. You can specify\n  one or multiple columns to indicate simple ordering or composite ordering.\n- The `default` element specifies the direction by which the attribute should be sorted when initially requested. \n  It defaults to ascending order, meaning if it is not sorted before and you request to sort by this attribute, \n  the data will be sorted by this attribute in ascending order.\n- The `label` element specifies what label should be used when calling [[yii\\data\\Sort::link()]] to create a sort link.\n  If not set, [[yii\\helpers\\Inflector::camel2words()]] will be called to generate a label from the attribute name.\n  Note that it will not be HTML-encoded.\n\n> Info: You can directly feed the value of [[yii\\data\\Sort::$orders|orders]] to the database query to build\n  its `ORDER BY` clause. Do not use [[yii\\data\\Sort::$attributeOrders|attributeOrders]] because some\n  attributes may be composite and cannot be recognized by the database query.\n\nYou can call [[yii\\data\\Sort::link()]] to generate a hyperlink upon which end users can click to request sorting\nthe data by the specified attribute. You may also call [[yii\\data\\Sort::createUrl()]] to create a sortable URL.\nFor example,\n\n```php\n// specifies the route that the URL to be created should use\n// If you do not specify this, the currently requested route will be used\n$sort->route = 'article/index';\n\n// display links leading to sort by name and age, respectively\necho $sort->link('name') . ' | ' . $sort->link('age');\n\n// displays: /index.php?r=article%2Findex&sort=age\necho $sort->createUrl('age');\n```\n\n[[yii\\data\\Sort]] checks the `sort` query parameter to determine which attributes are being requested for sorting.\nYou may specify a default ordering via [[yii\\data\\Sort::defaultOrder]] when the query parameter is not present.\nYou may also customize the name of the query parameter by configuring the [[yii\\data\\Sort::sortParam|sortParam]] property.\n"
  },
  {
    "path": "docs/guide/output-theming.md",
    "content": "Theming\n=======\n\nTheming is a way to replace a set of [views](structure-views.md) with another without the need of touching\nthe original view rendering code. You can use theming to systematically change the look and feel of an application.\n\nTo use theming, you should configure the [[yii\\base\\View::theme|theme]] property of the `view` application component.\nThe property configures a [[yii\\base\\Theme]] object which governs how view files are being replaced. You should\nmainly specify the following properties of [[yii\\base\\Theme]]:\n\n- [[yii\\base\\Theme::basePath]]: specifies the base directory that contains the themed resources (CSS, JS, images, etc.)\n- [[yii\\base\\Theme::baseUrl]]: specifies the base URL of the themed resources.\n- [[yii\\base\\Theme::pathMap]]: specifies the replacement rules of view files. More details will be given in the following\n  subsections.\n \nFor example, if you call `$this->render('about')` in `SiteController`, you will be rendering the view file\n`@app/views/site/about.php`. However, if you enable theming in the following application configuration,\nthe view file `@app/themes/basic/site/about.php` will be rendered, instead. \n\n```php\nreturn [\n    'components' => [\n        'view' => [\n            'theme' => [\n                'basePath' => '@app/themes/basic',\n                'baseUrl' => '@web/themes/basic',\n                'pathMap' => [\n                    '@app/views' => '@app/themes/basic',\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n> Info: Path aliases are supported by themes. When doing view replacement, path aliases will be turned into \n  the actual file paths or URLs.\n\nYou can access the [[yii\\base\\Theme]] object through the [[yii\\base\\View::theme]] property. For example,\nin a view file, you can write the following code because `$this` refers to the view object:\n\n```php\n$theme = $this->theme;\n\n// returns: $theme->baseUrl . '/img/logo.gif'\n$url = $theme->getUrl('img/logo.gif');\n\n// returns: $theme->basePath . '/img/logo.gif'\n$file = $theme->getPath('img/logo.gif');\n```\n\nThe [[yii\\base\\Theme::pathMap]] property governs how view files should be replaced. It takes an array of \nkey-value pairs, where the keys are the original view paths to be replaced and the values are the corresponding \nthemed view paths. The replacement is based on partial match: if a view path starts with any key in \nthe [[yii\\base\\Theme::pathMap|pathMap]] array, that matching part will be replaced with the corresponding array value.\nUsing the above configuration example, because `@app/views/site/about.php` partially matches the key\n`@app/views`, it will be replaced as `@app/themes/basic/site/about.php`.\n\n\n## Theming Modules <span id=\"theming-modules\"></span>\n\nIn order to theme modules, [[yii\\base\\Theme::pathMap]] can be configured like the following:\n\n```php\n'pathMap' => [\n    '@app/views' => '@app/themes/basic',\n    '@app/modules' => '@app/themes/basic/modules', // <-- !!!\n],\n```\n\nIt will allow you to theme `@app/modules/blog/views/comment/index.php` into `@app/themes/basic/modules/blog/views/comment/index.php`.\n\n\n## Theming Widgets <span id=\"theming-widgets\"></span>\n\nIn order to theme widgets, you can configure [[yii\\base\\Theme::pathMap]] in the following way:\n\n```php\n'pathMap' => [\n    '@app/views' => '@app/themes/basic',\n    '@app/widgets' => '@app/themes/basic/widgets', // <-- !!!\n],\n```\n\nThis will allow you to theme `@app/widgets/currency/views/index.php` into `@app/themes/basic/widgets/currency/views/index.php`.\n\n\n## Theme Inheritance <span id=\"theme-inheritance\"></span>\n\nSometimes you may want to define a basic theme which contains a basic look and feel of the application, and then\nbased on the current holiday, you may want to vary the look and feel slightly. You can achieve this goal using\ntheme inheritance which is done by mapping a single view path to multiple targets. For example,\n\n```php\n'pathMap' => [\n    '@app/views' => [\n        '@app/themes/christmas',\n        '@app/themes/basic',\n    ],\n]\n```\n\nIn this case, the view `@app/views/site/index.php` would be themed as either `@app/themes/christmas/site/index.php` \nor `@app/themes/basic/site/index.php`, depending on which themed file exists. If both themed files exist, the first\none will take precedence. In practice, you would keep most themed view files in `@app/themes/basic` and customize\nsome of them in `@app/themes/christmas`.\n"
  },
  {
    "path": "docs/guide/rest-authentication.md",
    "content": "Authentication\n==============\n\nUnlike Web applications, RESTful APIs are usually stateless, which means sessions or cookies should not\nbe used. Therefore, each request should come with some sort of authentication credentials because\nthe user authentication status may not be maintained by sessions or cookies. A common practice is\nto send a secret access token with each request to authenticate the user. Since an access token\ncan be used to uniquely identify and authenticate a user, **API requests should always be sent\nvia HTTPS to prevent man-in-the-middle (MitM) attacks**.\n\nThere are different ways to send an access token:\n\n* [HTTP Basic Auth](https://en.wikipedia.org/wiki/Basic_access_authentication): the access token\n  is sent as the username. This should only be used when an access token can be safely stored\n  on the API consumer side. For example, the API consumer is a program running on a server.\n* Query parameter: the access token is sent as a query parameter in the API URL, e.g.,\n  `https://example.com/users?access-token=xxxxxxxx`. Because most Web servers will keep query\n  parameters in server logs, this approach should be mainly used to serve `JSONP` requests which\n  cannot use HTTP headers to send access tokens.\n* [OAuth 2](https://oauth.net/2/): the access token is obtained by the consumer from an authorization\n  server and sent to the API server via [HTTP Bearer Tokens](https://datatracker.ietf.org/doc/html/rfc6750),\n  according to the OAuth2 protocol.\n\nYii supports all of the above authentication methods. You can also easily create new authentication methods.\n\nTo enable authentication for your APIs, do the following steps:\n\n1. Configure the `user` [application component](structure-application-components.md):\n   - Set the [[yii\\web\\User::enableSession|enableSession]] property to be `false`.\n   - Set the [[yii\\web\\User::loginUrl|loginUrl]] property to be `null` to show an HTTP 403 error instead of redirecting to the login page. \n2. Specify which authentication methods you plan to use by configuring the `authenticator` behavior\n   in your REST controller classes.\n3. Implement [[yii\\web\\IdentityInterface::findIdentityByAccessToken()]] in your [[yii\\web\\User::identityClass|user identity class]].\n\nStep 1 is not required but is recommended for RESTful APIs which should be stateless. When [[yii\\web\\User::enableSession|enableSession]]\nis `false`, the user authentication status will NOT be persisted across requests using sessions. Instead, authentication\nwill be performed for every request, which is accomplished by Step 2 and 3.\n\n> Tip: You may configure [[yii\\web\\User::enableSession|enableSession]] of the `user` application component\n> in application configurations if you are developing RESTful APIs in terms of an application. If you develop\n> RESTful APIs as a module, you may put the following line in the module's `init()` method, like the following:\n>\n> ```php\n> public function init()\n> {\n>     parent::init();\n>     \\Yii::$app->user->enableSession = false;\n> }\n> ```\n\nFor example, to use HTTP Basic Auth, you may configure the `authenticator` behavior as follows,\n\n```php\nuse yii\\filters\\auth\\HttpBasicAuth;\n\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n    $behaviors['authenticator'] = [\n        'class' => HttpBasicAuth::class,\n    ];\n    return $behaviors;\n}\n```\n\nIf you want to support all three authentication methods explained above, you can use `CompositeAuth` like the following,\n\n```php\nuse yii\\filters\\auth\\CompositeAuth;\nuse yii\\filters\\auth\\HttpBasicAuth;\nuse yii\\filters\\auth\\HttpBearerAuth;\nuse yii\\filters\\auth\\QueryParamAuth;\n\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n    $behaviors['authenticator'] = [\n        'class' => CompositeAuth::class,\n        'authMethods' => [\n            HttpBasicAuth::class,\n            HttpBearerAuth::class,\n            QueryParamAuth::class,\n        ],\n    ];\n    return $behaviors;\n}\n```\n\nEach element in `authMethods` should be an auth method class name or a configuration array.\n\n\nImplementation of `findIdentityByAccessToken()` is application specific. For example, in simple scenarios\nwhen each user can only have one access token, you may store the access token in an `access_token` column\nin the user table. The method can then be readily implemented in the `User` class as follows,\n\n```php\nuse yii\\db\\ActiveRecord;\nuse yii\\web\\IdentityInterface;\n\nclass User extends ActiveRecord implements IdentityInterface\n{\n    public static function findIdentityByAccessToken($token, $type = null)\n    {\n        return static::findOne(['access_token' => $token]);\n    }\n}\n```\n\nAfter authentication is enabled as described above, for every API request, the requested controller\nwill try to authenticate the user in its `beforeAction()` step.\n\nIf authentication succeeds, the controller will perform other checks (such as rate limiting, authorization)\nand then run the action. The authenticated user identity information can be retrieved via `Yii::$app->user->identity`.\n\nIf authentication fails, a response with HTTP status 401 will be sent back together with other appropriate headers\n(such as a `WWW-Authenticate` header for HTTP Basic Auth).\n\n\n## Authorization <span id=\"authorization\"></span>\n\nAfter a user is authenticated, you probably want to check if he or she has the permission to perform the requested\naction for the requested resource. This process is called *authorization* which is covered in detail in\nthe [Authorization section](security-authorization.md).\n\nIf your controllers extend from [[yii\\rest\\ActiveController]], you may override\nthe [[yii\\rest\\ActiveController::checkAccess()|checkAccess()]] method to perform authorization check. The method\nwill be called by the built-in actions provided by [[yii\\rest\\ActiveController]].\n"
  },
  {
    "path": "docs/guide/rest-controllers.md",
    "content": "Controllers\n===========\n\nAfter creating the resource classes and specifying how resource data should be formatted, the next thing\nto do is to create controller actions to expose the resources to end users through RESTful APIs.\n\nYii provides two base controller classes to simplify your work of creating RESTful actions:\n[[yii\\rest\\Controller]] and [[yii\\rest\\ActiveController]]. The difference between these two controllers\nis that the latter provides a default set of actions that are specifically designed to deal with\nresources represented as [Active Record](db-active-record.md). So if you are using [Active Record](db-active-record.md)\nand are comfortable with the provided built-in actions, you may consider extending your controller classes\nfrom [[yii\\rest\\ActiveController]], which will allow you to create powerful RESTful APIs with minimal code.\n\nBoth [[yii\\rest\\Controller]] and [[yii\\rest\\ActiveController]] provide the following features, some of which\nwill be described in detail in the next few sections:\n\n* HTTP method validation;\n* [Content negotiation and Data formatting](rest-response-formatting.md);\n* [Authentication](rest-authentication.md);\n* [Rate limiting](rest-rate-limiting.md).\n\n[[yii\\rest\\ActiveController]] in addition provides the following features:\n\n* A set of commonly needed actions: `index`, `view`, `create`, `update`, `delete`, `options`;\n* User authorization in regard to the requested action and resource.\n\n\n## Creating Controller Classes <span id=\"creating-controller\"></span>\n\nWhen creating a new controller class, a convention in naming the controller class is to use\nthe type name of the resource and use singular form. For example, to serve user information,\nthe controller may be named as `UserController`.\n\nCreating a new action is similar to creating an action for a Web application. The only difference\nis that instead of rendering the result using a view by calling the `render()` method, for RESTful actions\nyou directly return the data. The [[yii\\rest\\Controller::serializer|serializer]] and the\n[[yii\\web\\Response|response object]] will handle the conversion from the original data to the requested\nformat. For example,\n\n```php\npublic function actionView($id)\n{\n    return User::findOne($id);\n}\n```\n\n\n## Filters <span id=\"filters\"></span>\n\nMost RESTful API features provided by [[yii\\rest\\Controller]] are implemented in terms of [filters](structure-filters.md).\nIn particular, the following filters will be executed in the order they are listed:\n\n* [[yii\\filters\\ContentNegotiator|contentNegotiator]]: supports content negotiation, to be explained in\n  the [Response Formatting](rest-response-formatting.md) section;\n* [[yii\\filters\\VerbFilter|verbFilter]]: supports HTTP method validation;\n* [[yii\\filters\\auth\\AuthMethod|authenticator]]: supports user authentication, to be explained in\n  the [Authentication](rest-authentication.md) section;\n* [[yii\\filters\\RateLimiter|rateLimiter]]: supports rate limiting, to be explained in\n  the [Rate Limiting](rest-rate-limiting.md) section.\n\nThese named filters are declared in the [[yii\\rest\\Controller::behaviors()|behaviors()]] method.\nYou may override this method to configure individual filters, disable some of them, or add your own filters.\nFor example, if you only want to use HTTP basic authentication, you may write the following code:\n\n```php\nuse yii\\filters\\auth\\HttpBasicAuth;\n\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n    $behaviors['authenticator'] = [\n        'class' => HttpBasicAuth::class,\n    ];\n    return $behaviors;\n}\n```\n\n### CORS <span id=\"cors\"></span>\n\nAdding the [Cross-Origin Resource Sharing](structure-filters.md#cors) filter to a controller is a bit more complicated\nthan adding other filters described above, because the CORS filter has to be applied before authentication methods\nand thus needs a slightly different approach compared to other filters. Also authentication has to be disabled for the\n[CORS Preflight requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#preflighted_requests)\nso that a browser can safely determine whether a request can be made beforehand without the need for sending\nauthentication credentials. The following shows the code that is needed to add the [[yii\\filters\\Cors]] filter\nto an existing controller that extends from [[yii\\rest\\ActiveController]]:\n\n```php\nuse yii\\filters\\auth\\HttpBasicAuth;\n\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n\n    // remove authentication filter\n    $auth = $behaviors['authenticator'];\n    unset($behaviors['authenticator']);\n    \n    // add CORS filter\n    $behaviors['corsFilter'] = [\n        'class' => \\yii\\filters\\Cors::class,\n    ];\n    \n    // re-add authentication filter\n    $behaviors['authenticator'] = $auth;\n    // avoid authentication on CORS-pre-flight requests (HTTP OPTIONS method)\n    $behaviors['authenticator']['except'] = ['options'];\n\n    return $behaviors;\n}\n```\n\n\n## Extending `ActiveController` <span id=\"extending-active-controller\"></span>\n\nIf your controller class extends from [[yii\\rest\\ActiveController]], you should set\nits [[yii\\rest\\ActiveController::modelClass|modelClass]] property to be the name of the resource class\nthat you plan to serve through this controller. The class must extend from [[yii\\db\\ActiveRecord]].\n\n\n### Customizing Actions <span id=\"customizing-actions\"></span>\n\nBy default, [[yii\\rest\\ActiveController]] provides the following actions:\n\n* [[yii\\rest\\IndexAction|index]]: list resources page by page;\n* [[yii\\rest\\ViewAction|view]]: return the details of a specified resource;\n* [[yii\\rest\\CreateAction|create]]: create a new resource;\n* [[yii\\rest\\UpdateAction|update]]: update an existing resource;\n* [[yii\\rest\\DeleteAction|delete]]: delete the specified resource;\n* [[yii\\rest\\OptionsAction|options]]: return the supported HTTP methods.\n\nAll these actions are declared through the [[yii\\rest\\ActiveController::actions()|actions()]] method.\nYou may configure these actions or disable some of them by overriding the `actions()` method, like shown the following,\n\n```php\npublic function actions()\n{\n    $actions = parent::actions();\n\n    // disable the \"delete\" and \"create\" actions\n    unset($actions['delete'], $actions['create']);\n\n    // customize the data provider preparation with the \"prepareDataProvider()\" method\n    $actions['index']['prepareDataProvider'] = [$this, 'prepareDataProvider'];\n\n    return $actions;\n}\n\npublic function prepareDataProvider()\n{\n    // prepare and return a data provider for the \"index\" action\n}\n```\n\nPlease refer to the class references for individual action classes to learn what configuration options are available.\n\n\n### Performing Access Check <span id=\"performing-access-check\"></span>\n\nWhen exposing resources through RESTful APIs, you often need to check if the current user has the permission\nto access and manipulate the requested resource(s). With [[yii\\rest\\ActiveController]], this can be done\nby overriding the [[yii\\rest\\ActiveController::checkAccess()|checkAccess()]] method like the following,\n\n```php\n/**\n * Checks the privilege of the current user.\n *\n * This method should be overridden to check whether the current user has the privilege\n * to run the specified action against the specified data model.\n * If the user does not have access, a [[ForbiddenHttpException]] should be thrown.\n *\n * @param string $action the ID of the action to be executed\n * @param \\yii\\base\\Model $model the model to be accessed. If `null`, it means no specific model is being accessed.\n * @param array $params additional parameters\n * @throws ForbiddenHttpException if the user does not have access\n */\npublic function checkAccess($action, $model = null, $params = [])\n{\n    // check if the user can access $action and $model\n    // throw ForbiddenHttpException if access should be denied\n    if ($action === 'update' || $action === 'delete') {\n        if ($model->author_id !== \\Yii::$app->user->id)\n            throw new \\yii\\web\\ForbiddenHttpException(sprintf('You can only %s articles that you\\'ve created.', $action));\n    }\n}\n```\n\nThe `checkAccess()` method will be called by the default actions of [[yii\\rest\\ActiveController]]. If you create\nnew actions and also want to perform access check, you should call this method explicitly in the new actions.\n\n> Tip: You may implement `checkAccess()` by using the [Role-Based Access Control (RBAC) component](security-authorization.md).\n"
  },
  {
    "path": "docs/guide/rest-error-handling.md",
    "content": "Error Handling\n==============\n\nWhen handling a RESTful API request, if there is an error in the user request or if something unexpected\nhappens on the server, you may simply throw an exception to notify the user that something went wrong.\nIf you can identify the cause of the error (e.g., the requested resource does not exist), you should\nconsider throwing an exception along with a proper HTTP status code (e.g., [[yii\\web\\NotFoundHttpException]]\nrepresents a 404 status code). Yii will send the response along with the corresponding HTTP status\ncode and text. Yii will also include the serialized representation of the\nexception in the response body. For example:\n\n```\nHTTP/1.1 404 Not Found\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"name\": \"Not Found Exception\",\n    \"message\": \"The requested resource was not found.\",\n    \"code\": 0,\n    \"status\": 404\n}\n```\n\nThe following list summarizes the HTTP status codes that are used by the Yii REST framework:\n\n* `200`: OK. Everything worked as expected.\n* `201`: A resource was successfully created in response to a `POST` request. The `Location` header\n   contains the URL pointing to the newly created resource.\n* `204`: The request was handled successfully and the response contains no body content (like a `DELETE` request).\n* `304`: The resource was not modified. You can use the cached version.\n* `400`: Bad request. This could be caused by various actions by the user, such as providing invalid JSON\n   data in the request body, providing invalid action parameters, etc.\n* `401`: Authentication failed.\n* `403`: The authenticated user is not allowed to access the specified API endpoint.\n* `404`: The requested resource does not exist.\n* `405`: Method not allowed. Please check the `Allow` header for the allowed HTTP methods.\n* `415`: Unsupported media type. The requested content type or version number is invalid.\n* `422`: Data validation failed (in response to a `POST` request, for example). Please check the response body for detailed error messages.\n* `429`: Too many requests. The request was rejected due to rate limiting.\n* `500`: Internal server error. This could be caused by internal program errors.\n\n\n## Customizing Error Response <span id=\"customizing-error-response\"></span>\n\nSometimes you may want to customize the default error response format. For example, instead of relying on\nusing different HTTP statuses to indicate different errors, you would like to always use 200 as HTTP status\nand enclose the actual HTTP status code as part of the JSON structure in the response, like shown in the following,\n\n```\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"success\": false,\n    \"data\": {\n        \"name\": \"Not Found Exception\",\n        \"message\": \"The requested resource was not found.\",\n        \"code\": 0,\n        \"status\": 404\n    }\n}\n```\n\nTo achieve this goal, you can respond to the `beforeSend` event of the `response` component in the application configuration:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'response' => [\n            'class' => 'yii\\web\\Response',\n            'on beforeSend' => function ($event) {\n                $response = $event->sender;\n                if ($response->data !== null && Yii::$app->request->get('suppress_response_code')) {\n                    $response->data = [\n                        'success' => $response->isSuccessful,\n                        'data' => $response->data,\n                    ];\n                    $response->statusCode = 200;\n                }\n            },\n        ],\n    ],\n];\n```\n\nThe above code will reformat the response (for both successful and failed responses) as explained when\n`suppress_response_code` is passed as a `GET` parameter.\n"
  },
  {
    "path": "docs/guide/rest-filtering-collections.md",
    "content": "Filtering Collections\n=====================\n\nResource collection can be filtered using [[yii\\data\\DataFilter]] component since 2.0.13. It allows validating and \nbuilding the filter conditions passed via request, and, with the help of its extended version [[yii\\data\\ActiveDataFilter]], \nusing them in a format suitable for [[yii\\db\\QueryInterface::where()]].\n\n\n## Configuring Data Provider For Filtering <span id=\"configuring-data-provider-for-filtering\"></span>\n\nAs mentioned in the [Collections](rest-resources.md#collections) section, we can use \n[Data Provider](output-data-providers#data-providers) to output sorted and paginated list of resources. We can also use \nit to filter that list.\n\n```php\n$filter = new ActiveDataFilter([\n    'searchModel' => 'app\\models\\PostSearch',\n]);\n\n$filterCondition = null;\n// You may load filters from any source. For example,\n// if you prefer JSON in request body,\n// use Yii::$app->request->getBodyParams() below:\nif ($filter->load(Yii::$app->request->get())) { \n    $filterCondition = $filter->build();\n    if ($filterCondition === false) {\n        // Serializer would get errors out of it\n        return $filter;\n    }\n}\n\n$query = Post::find();\nif ($filterCondition !== null) {\n    $query->andWhere($filterCondition);\n}\n\nreturn new ActiveDataProvider([\n    'query' => $query,\n]);\n```\n\n`PostSearch` model serves the purpose of defining which properties and values are allowed for filtering:\n\n```php\nuse yii\\base\\Model;\n\nclass PostSearch extends Model \n{\n    public $id;\n    public $title;\n    \n    public function rules()\n    {\n        return [\n            ['id', 'integer'],\n            ['title', 'string', 'min' => 2, 'max' => 200],            \n        ];\n    }\n}\n```\n\nInstead of preparing the standalone model for search rules you can use [[yii\\base\\DynamicModel]] if you don't need any \nspecial business logic there.\n\n```php\n$filter = new ActiveDataFilter([\n    'searchModel' => (new DynamicModel(['id', 'title']))\n        ->addRule(['id'], 'integer')\n        ->addRule(['title'], 'string', ['min' => 2, 'max' => 200]),\n]);\n```\n\nDefining `searchModel` is required in order to control the filter conditions allowed to the end user.\n\n\n## Filtering Request <span id=\"filtering-request\"></span>\n\nEnd user is usually expected to provide optional filtering conditions in the request by one or more of the allowed \nmethods (which should be explicitly stated in the API documentation). For example, if filtering is handled via POST \nmethod using JSON it can be something similar to:\n\n```json\n{\n    \"filter\": {\n        \"id\": {\"in\": [2, 5, 9]},\n        \"title\": {\"like\": \"cheese\"}\n    }\n}\n```\n\nThe above conditions are:\n- `id` must be either 2, 5, or 9 **AND**\n- `title` must contain the word `cheese`.\n\nThe same conditions sent as a part of GET query are:\n\n```\n?filter[id][in][]=2&filter[id][in][]=5&filter[id][in][]=9&filter[title][like]=cheese\n```\n\nYou can change the default `filter` key word by setting [[yii\\data\\DataFilter::$filterAttributeName]].\n\n\n## Filter Control Keywords <span id=\"filter-control-keywords\"></span>\n\nThe default list of allowed filter control keywords is as the following:\n\n| filter control | translates to |\n|:--------------:|:-------------:|\n|     `and`      |     `AND`     |\n|      `or`      |     `OR`      |\n|     `not`      |     `NOT`     |\n|      `lt`      |      `<`      |\n|      `gt`      |      `>`      |\n|     `lte`      |     `<=`      |\n|     `gte`      |     `>=`      |\n|      `eq`      |      `=`      |\n|     `neq`      |     `!=`      |\n|      `in`      |     `IN`      |\n|     `nin`      |   `NOT IN`    |\n|     `like`     |    `LIKE`     |\n\nYou can expand that list by expanding option [[yii\\data\\DataFilter::$filterControls]], for example you could provide\nseveral keywords for the same filter build key, creating multiple aliases like:\n\n```php\n[\n    'eq' => '=',\n    '=' => '=',\n    '==' => '=',\n    '===' => '=',\n    // ...\n]\n```\n\nKeep in mind that any unspecified keyword will not be recognized as a filter control and will be treated as an attribute \nname - you should avoid conflicts between control keywords and attribute names (for example: in case you have control \nkeyword `like` and an attribute named `like`, specifying condition for such attribute will be impossible).\n\n> Note: while specifying filter controls take actual data exchange format, which your API uses, in mind.\n  Make sure each specified control keyword is valid for the format. For example, in XML tag name can start\n  only with a letter character, thus controls like `>`, `=`, or `$gt` will break the XML schema.\n\n> Note: When adding new filter control word make sure to check whether you need also to update \n  [[yii\\data\\DataFilter::$conditionValidators]] and/or [[yii\\data\\DataFilter::$operatorTypes]] in order to achieve\n  expected query result based on the complication of the operator and the way it should work.\n\n\n## Handling The Null Values <span id=\"handling-the-null-values\"></span>\n\nWhile it is easy to use `null` inside the JSON statement, it is not possible to send it using the GET query without \nconfusing the literal `null` with the string `\"null\"`. Since 2.0.40 you can use [[yii\\data\\DataFilter::$nullValue]] \noption to configure the word that will be used as a replacement for literal `null` (by default it's `\"NULL\"`).\n\n\n## Aliasing Attributes <span id=\"aliasing-attributes\"></span>\n\nWhether you want to alias the attribute with another name or to filter the joined DB table you can use\n[[yii\\data\\DataFilter::$attributeMap]] to set the map of aliases:\n\n```php\n[\n    'carPart' => 'car_part', // carPart will be used to filter car_part property\n    'authorName' => '{{author}}.[[name]]', // authorName will be used to filter name property of joined author table\n]\n```\n\n## Configuring Filters For `ActiveController` <span id=\"configuring-filters-for-activecontroller\"></span>\n\n[[yii\\rest\\ActiveController]] comes with the handy set of common REST actions that you can easily configure to use \nfilters as well through [[yii\\rest\\IndexAction::$dataFilter]] property. One of the possible ways of doing so is to use\n[[yii\\rest\\ActiveController::actions()]]:\n\n```php\npublic function actions()\n{\n    $actions = parent::actions();\n    \n    $actions['index']['dataFilter'] = [\n        'class' => \\yii\\data\\ActiveDataFilter::class,\n        'attributeMap' => [\n            'clockIn' => 'clock_in',\n        ],\n        'searchModel' => (new DynamicModel(['id', 'clockIn']))->addRule(['id', 'clockIn'], 'integer', ['min' => 1]),\n    ];\n    \n    return $actions;\n}\n```\n\nNow your collection (accessed through `index` action) can be filtered by `id` and `clockIn` properties.\n"
  },
  {
    "path": "docs/guide/rest-quick-start.md",
    "content": "Quick Start\n===========\n\nYii provides a whole set of tools to simplify the task of implementing RESTful Web Service APIs.\nIn particular, Yii supports the following features about RESTful APIs:\n\n* Quick prototyping with support for common APIs for [Active Record](db-active-record.md);\n* Response format negotiation (supporting JSON and XML by default);\n* Customizable object serialization with support for selectable output fields;\n* Proper formatting of collection data and validation errors;\n* Collection pagination, filtering and sorting;\n* Support for [HATEOAS](https://en.wikipedia.org/wiki/HATEOAS);\n* Efficient routing with proper HTTP verb check;\n* Built-in support for the `OPTIONS` and `HEAD` verbs;\n* Authentication and authorization;\n* Data caching and HTTP caching;\n* Rate limiting;\n\n\nIn the following, we use an example to illustrate how you can build a set of RESTful APIs with some minimal coding effort.\n\nAssume you want to expose the user data via RESTful APIs. The user data are stored in the `user` DB table,\nand you have already created the [active record](db-active-record.md) class `app\\models\\User` to access the user data.\n\n\n## Creating a Controller <span id=\"creating-controller\"></span>\n\nFirst, create a [controller](structure-controllers.md) class `app\\controllers\\UserController` as follows:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\rest\\ActiveController;\n\nclass UserController extends ActiveController\n{\n    public $modelClass = 'app\\models\\User';\n}\n```\n\nThe controller class extends from [[yii\\rest\\ActiveController]], which implements a common set of RESTful actions.\nBy specifying [[yii\\rest\\ActiveController::modelClass|modelClass]]\nas `app\\models\\User`, the controller knows which model can be used for fetching and manipulating data.\n\n\n## Configuring URL Rules <span id=\"configuring-url-rules\"></span>\n\nThen, modify the configuration of the `urlManager` component in your application configuration:\n\n```php\n'urlManager' => [\n    'enablePrettyUrl' => true,\n    'enableStrictParsing' => true,\n    'showScriptName' => false,\n    'rules' => [\n        ['class' => 'yii\\rest\\UrlRule', 'controller' => 'user'],\n    ],\n]\n```\n\nThe above configuration mainly adds a URL rule for the `user` controller so that the user data\ncan be accessed and manipulated with pretty URLs and meaningful HTTP verbs.\n\n> Note: Yii will automatically pluralize controller names for use in endpoints (see [Trying it Out](#trying-it-out) section below).\n> You can configure this using the [[yii\\rest\\UrlRule::$pluralize]] property.\n\n\n## Enabling JSON Input <span id=\"enabling-json-input\"></span>\n\nTo let the API accept input data in JSON format, configure the [[yii\\web\\Request::$parsers|parsers]] property of\nthe `request` [application component](structure-application-components.md) to use the [[yii\\web\\JsonParser]] for JSON input:\n\n```php\n'request' => [\n    'parsers' => [\n        'application/json' => 'yii\\web\\JsonParser',\n    ]\n]\n```\n\n> Info: The above configuration is optional. Without the above configuration, the API would only recognize \n  `application/x-www-form-urlencoded` and `multipart/form-data` input formats.\n\n\n## Trying it Out <span id=\"trying-it-out\"></span>\n\nWith the above minimal amount of effort, you have already finished your task of creating the RESTful APIs\nfor accessing the user data. The APIs you have created include:\n\n* `GET /users`: list all users page by page;\n* `HEAD /users`: show the overview information of user listing;\n* `POST /users`: create a new user;\n* `GET /users/123`: return the details of the user 123;\n* `HEAD /users/123`: show the overview information of user 123;\n* `PATCH /users/123` and `PUT /users/123`: update the user 123;\n* `DELETE /users/123`: delete the user 123;\n* `OPTIONS /users`: show the supported verbs regarding endpoint `/users`;\n* `OPTIONS /users/123`: show the supported verbs regarding endpoint `/users/123`.\n\nYou may access your APIs with the `curl` command like the following,\n\n```\n$ curl -i -H \"Accept:application/json\" \"http://localhost/users\"\n\nHTTP/1.1 200 OK\n...\nX-Pagination-Total-Count: 1000\nX-Pagination-Page-Count: 50\nX-Pagination-Current-Page: 1\nX-Pagination-Per-Page: 20\nLink: <http://localhost/users?page=1>; rel=self, \n      <http://localhost/users?page=2>; rel=next, \n      <http://localhost/users?page=50>; rel=last\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n[\n    {\n        \"id\": 1,\n        ...\n    },\n    {\n        \"id\": 2,\n        ...\n    },\n    ...\n]\n```\n\nTry changing the acceptable content type to be `application/xml`, and you will see the result\nis returned in XML format:\n\n```\n$ curl -i -H \"Accept:application/xml\" \"http://localhost/users\"\n\nHTTP/1.1 200 OK\n...\nX-Pagination-Total-Count: 1000\nX-Pagination-Page-Count: 50\nX-Pagination-Current-Page: 1\nX-Pagination-Per-Page: 20\nLink: <http://localhost/users?page=1>; rel=self, \n      <http://localhost/users?page=2>; rel=next, \n      <http://localhost/users?page=50>; rel=last\nTransfer-Encoding: chunked\nContent-Type: application/xml\n\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<response>\n    <item>\n        <id>1</id>\n        ...\n    </item>\n    <item>\n        <id>2</id>\n        ...\n    </item>\n    ...\n</response>\n```\n\nThe following command will create a new user by sending a POST request with the user data in JSON format:\n\n```\n$ curl -i -H \"Accept:application/json\" -H \"Content-Type:application/json\" \\\n    -XPOST \"http://localhost/users\" \\\n    -d '{\"username\": \"example\", \"email\": \"user@example.com\"}'\n\nHTTP/1.1 201 Created\n...\nLocation: http://localhost/users/1\nContent-Length: 99\nContent-Type: application/json; charset=UTF-8\n\n{\"id\":1,\"username\":\"example\",\"email\":\"user@example.com\",\"created_at\":1414674789,\"updated_at\":1414674789}\n```\n\n> Tip: You may also access your APIs via Web browser by entering the URL `http://localhost/users`.\n  However, you may need some browser plugins to send specific request headers.\n\nAs you can see, in the response headers, there is information about the total count, page count, etc.\nThere are also links that allow you to navigate to other pages of data. For example, `http://localhost/users?page=2`\nwould give you the next page of the user data.\n\nUsing the `fields` and `expand` parameters, you may also specify which fields should be included in the result.\nFor example, the URL `http://localhost/users?fields=id,email` will only return the `id` and `email` fields.\n\n\n> Info: You may have noticed that the result of `http://localhost/users` includes some sensitive fields,\n> such as `password_hash`, `auth_key`. You certainly do not want these to appear in your API result.\n> You can and should remove these fields from result as described in the [Resources](rest-resources.md) section.\n\nAdditionally, you can sort collections like `http://localhost/users?sort=email` or\n`http://localhost/users?sort=-email`. Filtering collections like `http://localhost/users?filter[id]=10` or\n`http://localhost/users?filter[email][like]=gmail.com` could be implemented using\ndata filters. See [Filtering Collections](rest-filtering-collections.md) section for details.\n\n## Customizing Pagination and Sorting in the list<span id=\"customizing-pagination-and-sorting\"></span>\n\nIn order to change the default [pagination](output-pagination.md) and [sorting](output-sorting.md) of the model list\nyou can configure the [[yii\\rest\\IndexAction]] in your controller. For example:\n\n```php\n<?php\nnamespace app\\controllers;\n\nuse yii\\rest\\ActiveController;\nuse yii\\helpers\\ArrayHelper;\n\nclass UserController extends ActiveController\n{\n    public $modelClass = 'app\\models\\User';\n    \n    public function actions()\n    {\n        return ArrayHelper::merge(parent::actions(), [\n            'index' => [\n                'pagination' => [\n                    'pageSize' => 10,\n                ],\n                'sort' => [\n                    'defaultOrder' => [\n                        'created_at' => SORT_DESC,\n                    ],\n                ],\n            ],\n        ]);\n    }\n}\n```\n\nPlease see [Extending ActiveController](rest-controllers#extending-active-controller) for more information on how to \nconfigure actions of the ActiveController.\n\n## Summary <span id=\"summary\"></span>\n\nUsing the Yii RESTful API framework, you implement an API endpoint in terms of a controller action, and you use\na controller to organize the actions that implement the endpoints for a single type of resource.\n\nResources are represented as data models which extend from the [[yii\\base\\Model]] class.\nIf you are working with databases (relational or NoSQL), it is recommended you use [[yii\\db\\ActiveRecord|ActiveRecord]]\nto represent resources.\n\nYou may use [[yii\\rest\\UrlRule]] to simplify the routing to your API endpoints.\n\nWhile not required, it is recommended that you develop your RESTful APIs as a separate application, different from\nyour Web front end and back end for easier maintenance.\n"
  },
  {
    "path": "docs/guide/rest-rate-limiting.md",
    "content": "Rate Limiting\n=============\n\nTo prevent abuse, you should consider adding *rate limiting* to your APIs. For example, you may want to limit the API usage\nof each user to be at most 100 API calls within a period of 10 minutes. If too many requests are received from a user\nwithin the stated period of the time, a response with status code 429 (meaning \"Too Many Requests\") should be returned.\n\nTo enable rate limiting, the [[yii\\web\\User::identityClass|user identity class]] should implement [[yii\\filters\\RateLimitInterface]].\nThis interface requires implementation of three methods:\n\n* `getRateLimit()`: returns the maximum number of allowed requests and the time period (e.g., `[100, 600]` means there can be at most 100 API calls within 600 seconds).\n* `loadAllowance()`: returns the number of remaining requests allowed and the corresponding UNIX timestamp\n  when the rate limit was last checked.\n* `saveAllowance()`: saves both the number of remaining requests allowed and the current UNIX timestamp.\n\nYou may want to use two columns in the user table to record the allowance and timestamp information. With those defined,\nthen `loadAllowance()` and `saveAllowance()` can be implemented to read and save the values\nof the two columns corresponding to the current authenticated user. To improve performance, you may also\nconsider storing these pieces of information in a cache or NoSQL storage.\n\nImplementation in the `User` model could look like the following:\n\n```php\npublic function getRateLimit($request, $action)\n{\n    return [$this->rateLimit, 1]; // $rateLimit requests per second\n}\n\npublic function loadAllowance($request, $action)\n{\n    return [$this->allowance, $this->allowance_updated_at];\n}\n\npublic function saveAllowance($request, $action, $allowance, $timestamp)\n{\n    $this->allowance = $allowance;\n    $this->allowance_updated_at = $timestamp;\n    $this->save();\n}\n```\n\nOnce the identity class implements the required interface, Yii will automatically use [[yii\\filters\\RateLimiter]]\nconfigured as an action filter for [[yii\\rest\\Controller]] to perform rate limiting check. The rate limiter\nwill throw a [[yii\\web\\TooManyRequestsHttpException]] when the rate limit is exceeded. \n\nYou may configure the rate limiter\nas follows in your REST controller classes:\n\n```php\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n    $behaviors['rateLimiter']['enableRateLimitHeaders'] = false;\n    return $behaviors;\n}\n```\n\nWhen rate limiting is enabled, by default every response will be sent with the following HTTP headers containing\nthe current rate limiting information:\n\n* `X-Rate-Limit-Limit`, the maximum number of requests allowed with a time period\n* `X-Rate-Limit-Remaining`, the number of remaining requests in the current time period\n* `X-Rate-Limit-Reset`, the number of seconds to wait in order to get the maximum number of allowed requests\n\nYou may disable these headers by configuring [[yii\\filters\\RateLimiter::enableRateLimitHeaders]] to be `false`,\nas shown in the above code example.\n"
  },
  {
    "path": "docs/guide/rest-resources.md",
    "content": "Resources\n=========\n\nRESTful APIs are all about accessing and manipulating *resources*. You may view resources as\n[models](structure-models.md) in the MVC paradigm.\n\nWhile there is no restriction in how to represent a resource, in Yii you usually would represent resources\nin terms of objects of [[yii\\base\\Model]] or its child classes (e.g. [[yii\\db\\ActiveRecord]]), for the\nfollowing reasons:\n\n* [[yii\\base\\Model]] implements the [[yii\\base\\Arrayable]] interface, which allows you to\n  customize how you want to expose resource data through RESTful APIs.\n* [[yii\\base\\Model]] supports [input validation](input-validation.md), which is useful if your RESTful APIs\n  need to support data input.\n* [[yii\\db\\ActiveRecord]] provides powerful DB data access and manipulation support, which makes it\n  a perfect fit if your resource data is stored in databases.\n\nIn this section, we will mainly describe how a resource class extending from [[yii\\base\\Model]] (or its child classes)\ncan specify what data may be returned via RESTful APIs. If the resource class does not extend from [[yii\\base\\Model]],\nthen all its public member variables will be returned.\n\n\n## Fields <span id=\"fields\"></span>\n\nWhen including a resource in a RESTful API response, the resource needs to be serialized into a string.\nYii breaks this process into two steps. First, the resource is converted into an array by [[yii\\rest\\Serializer]].\nSecond, the array is serialized into a string in a requested format (e.g. JSON, XML) by\n[[yii\\web\\ResponseFormatterInterface|response formatters]]. The first step is what you should mainly focus when\ndeveloping a resource class.\n\nBy overriding [[yii\\base\\Model::fields()|fields()]] and/or [[yii\\base\\Model::extraFields()|extraFields()]],\nyou may specify what data, called *fields*, in the resource can be put into its array representation.\nThe difference between these two methods is that the former specifies the default set of fields which should\nbe included in the array representation, while the latter specifies additional fields which may be included\nin the array if an end user requests for them via the `expand` query parameter. For example,\n\n```\n// returns all fields as declared in fields()\nhttp://localhost/users\n\n// only returns \"id\" and \"email\" fields, provided they are declared in fields()\nhttp://localhost/users?fields=id,email\n\n// returns all fields in fields() and field \"profile\" if it is in extraFields()\nhttp://localhost/users?expand=profile\n\n// returns all fields in fields() and \"author\" from post if\n// it is in extraFields() of post model\nhttp://localhost/comments?expand=post.author\n\n// only returns \"id\" and \"email\" provided they are in fields() and \"profile\" if it is in extraFields()\nhttp://localhost/users?fields=id,email&expand=profile\n```\n\n\n### Overriding `fields()` <span id=\"overriding-fields\"></span>\n\nBy default, [[yii\\base\\Model::fields()]] returns all model attributes as fields, while\n[[yii\\db\\ActiveRecord::fields()]] only returns the attributes which have been populated from DB.\n\nYou can override `fields()` to add, remove, rename or redefine fields. The return value of `fields()`\nshould be an array. The array keys are the field names, and the array values are the corresponding\nfield definitions which can be either property/attribute names or anonymous functions returning the\ncorresponding field values. In the special case when a field name is the same as its defining attribute\nname, you can omit the array key. For example,\n\n```php\n// explicitly list every field, best used when you want to make sure the changes\n// in your DB table or model attributes do not cause your field changes (to keep API backward compatibility).\npublic function fields()\n{\n    return [\n        // field name is the same as the attribute name\n        'id',\n        // field name is \"email\", the corresponding attribute name is \"email_address\"\n        'email' => 'email_address',\n        // field name is \"name\", its value is defined by a PHP callback\n        'name' => function ($model) {\n            return $model->first_name . ' ' . $model->last_name;\n        },\n    ];\n}\n\n// filter out some fields, best used when you want to inherit the parent implementation\n// and exclude some sensitive fields.\npublic function fields()\n{\n    $fields = parent::fields();\n\n    // remove fields that contain sensitive information\n    unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);\n\n    return $fields;\n}\n```\n\n> Warning: Because by default all attributes of a model will be included in the API result, you should\n> examine your data to make sure they do not contain sensitive information. If there is such information,\n> you should override `fields()` to filter them out. In the above example, we choose\n> to filter out `auth_key`, `password_hash` and `password_reset_token`.\n\n\n### Overriding `extraFields()` <span id=\"overriding-extra-fields\"></span>\n\nBy default, [[yii\\base\\Model::extraFields()]] returns an empty array, while [[yii\\db\\ActiveRecord::extraFields()]]\nreturns the names of the relations that have been populated from DB.\n\nThe return data format of `extraFields()` is the same as that of `fields()`. Usually, `extraFields()`\nis mainly used to specify fields whose values are objects. For example, given the following field\ndeclaration,\n\n```php\npublic function fields()\n{\n    return ['id', 'email'];\n}\n\npublic function extraFields()\n{\n    return ['profile'];\n}\n```\n\nthe request with `http://localhost/users?fields=id,email&expand=profile` may return the following JSON data:\n\n```php\n[\n    {\n        \"id\": 100,\n        \"email\": \"100@example.com\",\n        \"profile\": {\n            \"id\": 100,\n            \"age\": 30,\n        }\n    },\n    ...\n]\n```\n\n\n## Links <span id=\"links\"></span>\n\n[HATEOAS](https://en.wikipedia.org/wiki/HATEOAS), an abbreviation for Hypermedia as the Engine of Application State,\npromotes that RESTful APIs should return information that allows clients to discover actions supported for the returned\nresources. The key of HATEOAS is to return a set of hyperlinks with relation information when resource data are served\nby the APIs.\n\nYour resource classes may support HATEOAS by implementing the [[yii\\web\\Linkable]] interface. The interface\ncontains a single method [[yii\\web\\Linkable::getLinks()|getLinks()]] which should return a list of [[yii\\web\\Link|links]].\nTypically, you should return at least the `self` link representing the URL to the resource object itself. For example,\n\n```php\nuse yii\\base\\Model;\nuse yii\\web\\Link; // represents a link object as defined in JSON Hypermedia API Language.\nuse yii\\web\\Linkable;\nuse yii\\helpers\\Url;\n\nclass UserResource extends Model implements Linkable\n{\n    public $id;\n    public $email;\n\n    //...\n\n    public function fields()\n    {\n        return ['id', 'email'];\n    }\n\n    public function extraFields()\n    {\n        return ['profile'];\n    }\n\n    public function getLinks()\n    {\n        return [\n            Link::REL_SELF => Url::to(['user/view', 'id' => $this->id], true),\n            'edit' => Url::to(['user/view', 'id' => $this->id], true),\n            'profile' => Url::to(['user/profile/view', 'id' => $this->id], true),\n            'index' => Url::to(['users'], true),\n        ];\n    }\n}\n```\n\nWhen a `UserResource` object is returned in a response, it will contain a `_links` element representing the links related\nto the user, for example,\n\n```\n{\n    \"id\": 100,\n    \"email\": \"user@example.com\",\n    // ...\n    \"_links\" => {\n        \"self\": {\n            \"href\": \"https://example.com/users/100\"\n        },\n        \"edit\": {\n            \"href\": \"https://example.com/users/100\"\n        },\n        \"profile\": {\n            \"href\": \"https://example.com/users/profile/100\"\n        },\n        \"index\": {\n            \"href\": \"https://example.com/users\"\n        }\n    }\n}\n```\n\n\n## Collections <span id=\"collections\"></span>\n\nResource objects can be grouped into *collections*. Each collection contains a list of resource objects\nof the same type.\n\nWhile collections can be represented as arrays, it is usually more desirable to represent them\nas [data providers](output-data-providers.md). This is because data providers support sorting and pagination\nof resources, which is a commonly needed feature for RESTful APIs returning collections. For example,\nthe following action returns a data provider about the post resources:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\rest\\Controller;\nuse yii\\data\\ActiveDataProvider;\nuse app\\models\\Post;\n\nclass PostController extends Controller\n{\n    public function actionIndex()\n    {\n        return new ActiveDataProvider([\n            'query' => Post::find(),\n        ]);\n    }\n}\n```\n\nWhen a data provider is being sent in a RESTful API response, [[yii\\rest\\Serializer]] will take out the current\npage of resources and serialize them as an array of resource objects. Additionally, [[yii\\rest\\Serializer]]\nwill also include the pagination information by the following HTTP headers:\n\n* `X-Pagination-Total-Count`: The total number of resources;\n* `X-Pagination-Page-Count`: The number of pages;\n* `X-Pagination-Current-Page`: The current page (1-based);\n* `X-Pagination-Per-Page`: The number of resources in each page;\n* `Link`: A set of navigational links allowing client to traverse the resources page by page.\n\nSince collection in REST APIs is a data provider, it shares all data provider features i.e. pagination and sorting.\n\nAn example may be found in the [Quick Start](rest-quick-start.md#trying-it-out) section.\n"
  },
  {
    "path": "docs/guide/rest-response-formatting.md",
    "content": "Response Formatting\n===================\n\nWhen handling a RESTful API request, an application usually takes the following steps that are related\nwith response formatting:\n\n1. Determine various factors that may affect the response format, such as media type, language, version, etc.\n   This process is also known as [content negotiation](https://en.wikipedia.org/wiki/Content_negotiation).\n2. Convert resource objects into arrays, as described in the [Resources](rest-resources.md) section.\n   This is done by [[yii\\rest\\Serializer]].\n3. Convert arrays into a string in the format as determined by the content negotiation step. This is\n   done by [[yii\\web\\ResponseFormatterInterface|response formatters]] registered with\n   the [[yii\\web\\Response::formatters|formatters]] property of the\n   `response` [application component](structure-application-components.md).\n\n\n## Content Negotiation <span id=\"content-negotiation\"></span>\n\nYii supports content negotiation via the [[yii\\filters\\ContentNegotiator]] filter. The RESTful API base\ncontroller class [[yii\\rest\\Controller]] is equipped with this filter under the name of `contentNegotiator`.\nThe filter provides response format negotiation as well as language negotiation. For example, if a RESTful\nAPI request contains the following header,\n\n```\nAccept: application/json; q=1.0, */*; q=0.1\n```\n\nit will get a response in JSON format, like the following:\n\n```\n$ curl -i -H \"Accept: application/json; q=1.0, */*; q=0.1\" \"http://localhost/users\"\n\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nX-Powered-By: PHP/5.4.20\nX-Pagination-Total-Count: 1000\nX-Pagination-Page-Count: 50\nX-Pagination-Current-Page: 1\nX-Pagination-Per-Page: 20\nLink: <http://localhost/users?page=1>; rel=self,\n      <http://localhost/users?page=2>; rel=next,\n      <http://localhost/users?page=50>; rel=last\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n[\n    {\n        \"id\": 1,\n        ...\n    },\n    {\n        \"id\": 2,\n        ...\n    },\n    ...\n]\n```\n\nBehind the scene, before a RESTful API controller action is executed, the [[yii\\filters\\ContentNegotiator]]\nfilter will check the `Accept` HTTP header in the request and set the [[yii\\web\\Response::format|response format]]\nto be `'json'`. After the action is executed and returns the resulting resource object or collection,\n[[yii\\rest\\Serializer]] will convert the result into an array. And finally, [[yii\\web\\JsonResponseFormatter]]\nwill serialize the array into a JSON string and include it in the response body.\n\nBy default, RESTful APIs support both JSON and XML formats. To support a new format, you should configure\nthe [[yii\\filters\\ContentNegotiator::formats|formats]] property of the `contentNegotiator` filter like\nthe following in your API controller classes:\n\n```php\nuse yii\\web\\Response;\n\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n    $behaviors['contentNegotiator']['formats']['text/html'] = Response::FORMAT_HTML;\n    return $behaviors;\n}\n```\n\nThe keys of the `formats` property are the supported MIME types, while the values are the corresponding\nresponse format names which must be supported in [[yii\\web\\Response::formatters]].\n\n\n## Data Serializing <span id=\"data-serializing\"></span>\n\nAs we have described above, [[yii\\rest\\Serializer]] is the central piece responsible for converting resource\nobjects or collections into arrays. It recognizes objects implementing [[yii\\base\\Arrayable]] as\nwell as [[yii\\data\\DataProviderInterface]]. The former is mainly implemented by resource objects, while\nthe latter resource collections.\n\nYou may configure the serializer by setting the [[yii\\rest\\Controller::serializer]] property with a configuration array.\nFor example, sometimes you may want to help simplify the client development work by including pagination information\ndirectly in the response body. To do so, configure the [[yii\\rest\\Serializer::collectionEnvelope]] property\nas follows:\n\n```php\nuse yii\\rest\\ActiveController;\n\nclass UserController extends ActiveController\n{\n    public $modelClass = 'app\\models\\User';\n    public $serializer = [\n        'class' => 'yii\\rest\\Serializer',\n        'collectionEnvelope' => 'items',\n    ];\n}\n```\n\nYou may then get the following response for request `http://localhost/users`:\n\n```\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nX-Powered-By: PHP/5.4.20\nX-Pagination-Total-Count: 1000\nX-Pagination-Page-Count: 50\nX-Pagination-Current-Page: 1\nX-Pagination-Per-Page: 20\nLink: <http://localhost/users?page=1>; rel=self,\n      <http://localhost/users?page=2>; rel=next,\n      <http://localhost/users?page=50>; rel=last\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"items\": [\n        {\n            \"id\": 1,\n            ...\n        },\n        {\n            \"id\": 2,\n            ...\n        },\n        ...\n    ],\n    \"_links\": {\n        \"self\": {\n            \"href\": \"http://localhost/users?page=1\"\n        },\n        \"next\": {\n            \"href\": \"http://localhost/users?page=2\"\n        },\n        \"last\": {\n            \"href\": \"http://localhost/users?page=50\"\n        }\n    },\n    \"_meta\": {\n        \"totalCount\": 1000,\n        \"pageCount\": 50,\n        \"currentPage\": 1,\n        \"perPage\": 20\n    }\n}\n```\n\n### Controlling JSON output\n\nThe JSON response is generated by the [[yii\\web\\JsonResponseFormatter|JsonResponseFormatter]] class which will\nuse the [[yii\\helpers\\Json|JSON helper]] internally. This formatter can be configured with different options like\nfor example the [[yii\\web\\JsonResponseFormatter::$prettyPrint|$prettyPrint]] option, which is useful on development for\nbetter readable responses, or [[yii\\web\\JsonResponseFormatter::$encodeOptions|$encodeOptions]] to control the output\nof the JSON encoding.\n\nThe formatter can be configured in the [[yii\\web\\Response::formatters|formatters]] property of the `response` application\ncomponent in the application [configuration](concept-configurations.md) like the following:\n\n```php\n'response' => [\n    // ...\n    'formatters' => [\n        \\yii\\web\\Response::FORMAT_JSON => [\n            'class' => 'yii\\web\\JsonResponseFormatter',\n            'prettyPrint' => YII_DEBUG, // use \"pretty\" output in debug mode\n            'encodeOptions' => JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE,\n            // ...\n        ],\n    ],\n],\n```\n\nWhen returning data from a database using the [DAO](db-dao.md) database layer all data will be represented\nas strings, which is not always the expected result especially numeric values should be represented as\nnumbers in JSON. When using the ActiveRecord layer for retrieving data from the database, the values for numeric\ncolumns will be converted to integers when data is fetched from the database in [[yii\\db\\ActiveRecord::populateRecord()]].\n"
  },
  {
    "path": "docs/guide/rest-routing.md",
    "content": "Routing\n=======\n\nWith resource and controller classes ready, you can access the resources using the URL like\n`http://localhost/index.php?r=user/create`, similar to what you can do with normal Web applications.\n\nIn practice, you usually want to enable pretty URLs and take advantage of HTTP verbs.\nFor example, a request `POST /users` would mean accessing the `user/create` action.\nThis can be done easily by configuring the `urlManager` [application component](structure-application-components.md)\nin the application configuration like the following:\n\n```php\n'urlManager' => [\n    'enablePrettyUrl' => true,\n    'enableStrictParsing' => true,\n    'showScriptName' => false,\n    'rules' => [\n        ['class' => 'yii\\rest\\UrlRule', 'controller' => 'user'],\n    ],\n]\n```\n\nCompared to the URL management for Web applications, the main new thing above is the use of\n[[yii\\rest\\UrlRule]] for routing RESTful API requests. This special URL rule class will\ncreate a whole set of child URL rules to support routing and URL creation for the specified controller(s).\nFor example, the above code is roughly equivalent to the following rules:\n\n```php\n[\n    'PUT,PATCH users/<id>' => 'user/update',\n    'DELETE users/<id>' => 'user/delete',\n    'GET,HEAD users/<id>' => 'user/view',\n    'POST users' => 'user/create',\n    'GET,HEAD users' => 'user/index',\n    'users/<id>' => 'user/options',\n    'users' => 'user/options',\n]\n```\n\nAnd the following API endpoints are supported by this rule:\n\n* `GET /users`: list all users page by page;\n* `HEAD /users`: show the overview information of user listing;\n* `POST /users`: create a new user;\n* `GET /users/123`: return the details of the user 123;\n* `HEAD /users/123`: show the overview information of user 123;\n* `PATCH /users/123` and `PUT /users/123`: update the user 123;\n* `DELETE /users/123`: delete the user 123;\n* `OPTIONS /users`: show the supported verbs regarding endpoint `/users`;\n* `OPTIONS /users/123`: show the supported verbs regarding endpoint `/users/123`.\n\nYou may configure the `only` and `except` options to explicitly list which actions to support or which\nactions should be disabled, respectively. For example,\n\n```php\n[\n    'class' => 'yii\\rest\\UrlRule',\n    'controller' => 'user',\n    'except' => ['delete', 'create', 'update'],\n],\n```\n\nYou may also configure `patterns` or `extraPatterns` to redefine existing patterns or add new patterns supported by this rule.\nFor example, to support a new action `search` by the endpoint `GET /users/search`, configure the `extraPatterns` option as follows,\n\n```php\n[\n    'class' => 'yii\\rest\\UrlRule',\n    'controller' => 'user',\n    'extraPatterns' => [\n        'GET search' => 'search',\n    ],\n]\n```\n\nYou may have noticed that the controller ID `user` appears in plural form as `users` in the endpoint URLs.\nThis is because [[yii\\rest\\UrlRule]] automatically pluralizes controller IDs when creating child URL rules.\nYou may disable this behavior by setting [[yii\\rest\\UrlRule::pluralize]] to be `false`. \n\n> Info: The pluralization of controller IDs is done by [[yii\\helpers\\Inflector::pluralize()]]. The method respects\n  special pluralization rules. For example, the word `box` will be pluralized as `boxes` instead of `boxs`.\n\nIn case when the automatic pluralization does not meet your requirement, you may also configure the \n[[yii\\rest\\UrlRule::controller]] property to explicitly specify how to map a name used in endpoint URLs to \na controller ID. For example, the following code maps the name `u` to the controller ID `user`.  \n \n```php\n[\n    'class' => 'yii\\rest\\UrlRule',\n    'controller' => ['u' => 'user'],\n]\n```\n\n## Extra configuration for contained rules\n\nIt could be useful to specify extra configuration that is applied to each rule contained within [[yii\\rest\\UrlRule]].\nA good example would be specifying defaults for `expand` parameter:\n\n```php\n[\n    'class' => 'yii\\rest\\UrlRule',\n    'controller' => ['user'],\n    'ruleConfig' => [\n        'class' => 'yii\\web\\UrlRule',\n        'defaults' => [\n            'expand' => 'profile',\n        ]\n    ],\n],\n```\n"
  },
  {
    "path": "docs/guide/rest-versioning.md",
    "content": "Versioning\n==========\n\nA good API is *versioned*: changes and new features are implemented in new versions of the API instead of continually altering just one version. Unlike Web applications, with which you have full control of both the client-side and server-side\ncode, APIs are meant to be used by clients beyond your control. For this reason, backward\ncompatibility (BC) of the APIs should be maintained whenever possible. If a change that may break BC is necessary, you should introduce it in new version of the API, and bump up the version number. Existing clients can continue to use the old, working version of the API; and new or upgraded clients can get the new functionality in the new API version. \n\n> Tip: Refer to [Semantic Versioning](https://semver.org/)\nfor more information on designing API version numbers.\n\nOne common way to implement API versioning is to embed the version number in the API URLs.\nFor example, `https://example.com/v1/users` stands for the `/users` endpoint of API version 1. \n\nAnother method of API versioning,\nwhich has gained momentum recently, is to put the version number in the HTTP request headers. This is typically done through the `Accept` header:\n\n```\n// via a parameter\nAccept: application/json; version=v1\n// via a vendor content type\nAccept: application/vnd.company.myapp-v1+json\n```\n\nBoth methods have their pros and cons, and there are a lot of debates about each approach. Below you'll see a practical strategy\nfor API versioning that is a mix of these two methods:\n\n* Put each major version of API implementation in a separate module whose ID is the major version number (e.g. `v1`, `v2`).\n  Naturally, the API URLs will contain major version numbers.\n* Within each major version (and thus within the corresponding module), use the `Accept` HTTP request header\n  to determine the minor version number and write conditional code to respond to the minor versions accordingly.\n\nFor each module serving a major version, the module should include the resource and controller classes\nserving that specific version. To better separate code responsibility, you may keep a common set of\nbase resource and controller classes, and subclass them in each individual version module. Within the subclasses,\nimplement the concrete code such as `Model::fields()`.\n\nYour code may be organized like the following:\n\n```\napi/\n    common/\n        controllers/\n            UserController.php\n            PostController.php\n        models/\n            User.php\n            Post.php\n    modules/\n        v1/\n            controllers/\n                UserController.php\n                PostController.php\n            models/\n                User.php\n                Post.php\n            Module.php\n        v2/\n            controllers/\n                UserController.php\n                PostController.php\n            models/\n                User.php\n                Post.php\n            Module.php\n```\n\nYour application configuration would look like:\n\n```php\nreturn [\n    'modules' => [\n        'v1' => [\n            'class' => 'app\\modules\\v1\\Module',\n        ],\n        'v2' => [\n            'class' => 'app\\modules\\v2\\Module',\n        ],\n    ],\n    'components' => [\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            'enableStrictParsing' => true,\n            'showScriptName' => false,\n            'rules' => [\n                ['class' => 'yii\\rest\\UrlRule', 'controller' => ['v1/user', 'v1/post']],\n                ['class' => 'yii\\rest\\UrlRule', 'controller' => ['v2/user', 'v2/post']],\n            ],\n        ],\n    ],\n];\n```\n\nAs a result of the above code, `https://example.com/v1/users` will return the list of users in version 1, while\n`https://example.com/v2/users` will return version 2 users.\n\nThanks to modules, the code for different major versions can be well isolated. But modules make it still possible\nto reuse code across the modules via common base classes and other shared resources.\n\nTo deal with minor version numbers, you may take advantage of the content negotiation\nfeature provided by the [[yii\\filters\\ContentNegotiator|contentNegotiator]] behavior. The `contentNegotiator`\nbehavior will set the [[yii\\web\\Response::acceptParams]] property when it determines which\ncontent type to support.\n\nFor example, if a request is sent with the HTTP header `Accept: application/json; version=v1`,\nafter content negotiation, [[yii\\web\\Response::acceptParams]] will contain the value `['version' => 'v1']`.\n\nBased on the version information in `acceptParams`, you may write conditional code in places\nsuch as actions, resource classes, serializers, etc. to provide the appropriate functionality.\n\nSince minor versions by definition require maintaining backward compatibility, hopefully there would not be many\nversion checks in your code. Otherwise, chances are that you may need to create a new major version.\n"
  },
  {
    "path": "docs/guide/runtime-bootstrapping.md",
    "content": "Bootstrapping\n=============\n\nBootstrapping refers to the process of preparing the environment before an application starts\nto resolve and process an incoming request. Bootstrapping is done in two places:\nthe [entry script](structure-entry-scripts.md) and the [application](structure-applications.md).\n\nIn the [entry script](structure-entry-scripts.md), class autoloaders for different libraries are\nregistered. This includes the Composer autoloader through its `autoload.php` file and the Yii\nautoloader through its `Yii` class file. The entry script then loads the application\n[configuration](concept-configurations.md) and creates an [application](structure-applications.md) instance.\n\nIn the constructor of the application, the following bootstrapping work is done:\n\n1. [[yii\\base\\Application::preInit()|preInit()]] is called, which configures some high priority\n   application properties, such as [[yii\\base\\Application::basePath|basePath]].\n2. Register the [[yii\\base\\Application::errorHandler|error handler]].\n3. Initialize application properties using the given application configuration.\n4. [[yii\\base\\Application::init()|init()]] is called which in turn calls\n   [[yii\\base\\Application::bootstrap()|bootstrap()]] to run bootstrapping components.\n   - Include the extension manifest file `vendor/yiisoft/extensions.php`.\n   - Create and run [bootstrap components](structure-extensions.md#bootstrapping-classes)\n     declared by extensions.\n   - Create and run [application components](structure-application-components.md) and/or\n     [modules](structure-modules.md) that are declared in the application's\n     [bootstrap property](structure-applications.md#bootstrap).\n\nBecause the bootstrapping work has to be done before handling *every* request, it is very important\nto keep this process light and optimize it as much as possible.\n\nTry not to register too many bootstrapping components. A bootstrapping component is needed only\nif it wants to participate the whole life cycle of requesting handling. For example, if a module\nneeds to register additional URL parsing rules, it should be listed in the\n[bootstrap property](structure-applications.md#bootstrap) so that the new URL rules can take effect\nbefore they are used to resolve requests.\n\nIn production mode, enable a bytecode cache, such as [PHP OPcache] or [APC], to minimize the time needed for including\nand parsing PHP files.\n\n[PHP OPcache]: https://www.php.net/manual/en/intro.opcache.php\n[APC]: https://www.php.net/manual/en/book.apcu.php\n\nSome large applications have very complex application [configurations](concept-configurations.md)\nwhich are divided into many smaller configuration files. If this is the case, consider caching\nthe whole configuration array and loading it directly from cache before creating the application instance\nin the entry script.\n"
  },
  {
    "path": "docs/guide/runtime-handling-errors.md",
    "content": "Handling Errors\n===============\n\nYii includes a built-in [[yii\\web\\ErrorHandler|error handler]] which makes error handling a much more pleasant\nexperience than before. In particular, the Yii error handler does the following to improve error handling:\n\n* All non-fatal PHP errors (e.g. warnings, notices) are converted into catchable exceptions.\n* Exceptions and fatal PHP errors are displayed with detailed call stack information and source code lines\n  in debug mode.\n* Supports using a dedicated [controller action](structure-controllers.md#actions) to display errors.\n* Supports different error response formats.\n\nThe [[yii\\web\\ErrorHandler|error handler]] is enabled by default. You may disable it by defining the constant\n`YII_ENABLE_ERROR_HANDLER` to be `false` in the [entry script](structure-entry-scripts.md) of your application.\n\n\n## Using Error Handler <span id=\"using-error-handler\"></span>\n\nThe [[yii\\web\\ErrorHandler|error handler]] is registered as an [application component](structure-application-components.md) named `errorHandler`.\nYou may configure it in the application configuration like the following:\n\n```php\nreturn [\n    'components' => [\n        'errorHandler' => [\n            'maxSourceLines' => 20,\n        ],\n    ],\n];\n```\n\nWith the above configuration, the number of source code lines to be displayed in exception pages will be up to 20.\n\nAs aforementioned, the error handler turns all non-fatal PHP errors into catchable exceptions. This means you can\nuse the following code to deal with PHP errors:\n\n```php\nuse Yii;\nuse yii\\base\\ErrorException;\n\ntry {\n    10/0;\n} catch (ErrorException $e) {\n    Yii::warning(\"Division by zero.\");\n}\n\n// execution continues...\n```\n\nIf you want to show an error page telling the user that his request is invalid or unexpected, you may simply\nthrow an [[yii\\web\\HttpException|HTTP exception]], such as [[yii\\web\\NotFoundHttpException]]. The error handler\nwill correctly set the HTTP status code of the response and use an appropriate error view to display the error\nmessage.\n\n```php\nuse yii\\web\\NotFoundHttpException;\n\nthrow new NotFoundHttpException();\n```\n\n\n## Customizing Error Display <span id=\"customizing-error-display\"></span>\n\nThe [[yii\\web\\ErrorHandler|error handler]] adjusts the error display according to the value of the constant `YII_DEBUG`.\nWhen `YII_DEBUG` is `true` (meaning in debug mode), the error handler will display exceptions with detailed call\nstack information and source code lines to help easier debugging. And when `YII_DEBUG` is `false`, only the error\nmessage will be displayed to prevent revealing sensitive information about the application.\n\n> Info: If an exception is a descendant of [[yii\\base\\UserException]], no call stack will be displayed regardless\nthe value of `YII_DEBUG`. This is because such exceptions are considered to be caused by user mistakes and the\ndevelopers do not need to fix anything.\n\nBy default, the [[yii\\web\\ErrorHandler|error handler]] displays errors using two [views](structure-views.md):\n\n* `@yii/views/errorHandler/error.php`: used when errors should be displayed WITHOUT call stack information.\n  When `YII_DEBUG` is `false`, this is the only error view to be displayed.\n* `@yii/views/errorHandler/exception.php`: used when errors should be displayed WITH call stack information.\n\nYou can configure the [[yii\\web\\ErrorHandler::errorView|errorView]] and [[yii\\web\\ErrorHandler::exceptionView|exceptionView]]\nproperties of the error handler to use your own views to customize the error display.\n\n\n### Using Error Actions <span id=\"using-error-actions\"></span>\n\nA better way of customizing the error display is to use dedicated error [actions](structure-controllers.md).\nTo do so, first configure the [[yii\\web\\ErrorHandler::errorAction|errorAction]] property of the `errorHandler`\ncomponent like the following:\n\n```php\nreturn [\n    'components' => [\n        'errorHandler' => [\n            'errorAction' => 'site/error',\n        ],\n    ]\n];\n```\n\nThe [[yii\\web\\ErrorHandler::errorAction|errorAction]] property takes a [route](structure-controllers.md#routes)\nto an action. The above configuration states that when an error needs to be displayed without call stack information,\nthe `site/error` action should be executed.\n\nYou can create the `site/error` action as follows,\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actions()\n    {\n        return [\n            'error' => [\n                'class' => 'yii\\web\\ErrorAction',\n            ],\n        ];\n    }\n}\n```\n\nThe above code defines the `error` action using the [[yii\\web\\ErrorAction]] class which renders an error\nusing a view named `error`.\n\nBesides using [[yii\\web\\ErrorAction]], you may also define the `error` action using an action method like the following,\n\n```php\npublic function actionError()\n{\n    $exception = Yii::$app->errorHandler->exception;\n    if ($exception !== null) {\n        return $this->render('error', ['exception' => $exception]);\n    }\n}\n```\n\nYou should now create a view file located at `views/site/error.php`. In this view file, you can access\nthe following variables if the error action is defined as [[yii\\web\\ErrorAction]]:\n\n* `name`: the name of the error;\n* `message`: the error message;\n* `exception`: the exception object through which you can retrieve more useful information, such as HTTP status code,\n  error code, error call stack, etc.\n\n> Info: If you are using the [basic project template](start-installation.md) or the [advanced project template](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md),\nthe error action and the error view are already defined for you.\n\n> Note: If you need to redirect in an error handler, do it the following way:\n>\n> ```php\n> Yii::$app->getResponse()->redirect($url)->send();\n> return;\n> ```\n\n\n### Customizing Error Response Format <span id=\"error-format\"></span>\n\nThe error handler displays errors according to the format setting of the [response](runtime-responses.md).\nIf the [[yii\\web\\Response::format|response format]] is `html`, it will use the error or exception view\nto display errors, as described in the last subsection. For other response formats, the error handler will\nassign the array representation of the exception to the [[yii\\web\\Response::data]] property which will then\nbe converted to different formats accordingly. For example, if the response format is `json`, you may see\nthe following response:\n\n```\nHTTP/1.1 404 Not Found\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"name\": \"Not Found Exception\",\n    \"message\": \"The requested resource was not found.\",\n    \"code\": 0,\n    \"status\": 404\n}\n```\n\nYou may customize the error response format by responding to the `beforeSend` event of the `response` component\nin the application configuration:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'response' => [\n            'class' => 'yii\\web\\Response',\n            'on beforeSend' => function ($event) {\n                $response = $event->sender;\n                if ($response->data !== null) {\n                    $response->data = [\n                        'success' => $response->isSuccessful,\n                        'data' => $response->data,\n                    ];\n                    $response->statusCode = 200;\n                }\n            },\n        ],\n    ],\n];\n```\n\nThe above code will reformat the error response like the following:\n\n```\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"success\": false,\n    \"data\": {\n        \"name\": \"Not Found Exception\",\n        \"message\": \"The requested resource was not found.\",\n        \"code\": 0,\n        \"status\": 404\n    }\n}\n```\n"
  },
  {
    "path": "docs/guide/runtime-logging.md",
    "content": "Logging\n=======\n\nYii provides a powerful logging framework that is highly customizable and extensible. Using this framework, you\ncan easily log various types of messages, filter them, and gather them at different targets, such as files, databases,\nemails. \n\nUsing the Yii logging framework involves the following steps:\n \n* Record [log messages](#log-messages) at various places in your code;\n* Configure [log targets](#log-targets) in the application configuration to filter and export log messages;\n* Examine the filtered logged messages exported by different targets (e.g. the [Yii debugger](tool-debugger.md)).\n\nIn this section, we will mainly describe the first two steps.\n\n\n## Log Messages <span id=\"log-messages\"></span>\n\nRecording log messages is as simple as calling one of the following logging methods:\n\n* [[Yii::debug()]]: record a message to trace how a piece of code runs. This is mainly for development use.\n* [[Yii::info()]]: record a message that conveys some useful information.\n* [[Yii::warning()]]: record a warning message that indicates something unexpected has happened.\n* [[Yii::error()]]: record a fatal error that should be investigated as soon as possible.\n\nThese logging methods record log messages at various *severity levels* and *categories*. They share\nthe same function signature `function ($message, $category = 'application')`, where `$message` stands for\nthe log message to be recorded, while `$category` is the category of the log message. The code in the following\nexample records a trace message under the default category `application`:\n\n```php\nYii::debug('start calculating average revenue');\n```\n\n> Info: Log messages can be strings as well as complex data, such as arrays or objects. It is the responsibility\nof [log targets](#log-targets) to properly deal with log messages. By default, if a log message is not a string,\nit will be exported as a string by calling [[yii\\helpers\\VarDumper::export()]].\n\nTo better organize and filter log messages, it is recommended that you specify an appropriate category for each\nlog message. You may choose a hierarchical naming scheme for categories, which will make it easier for \n[log targets](#log-targets) to filter messages based on their categories. A simple yet effective naming scheme\nis to use the PHP magic constant `__METHOD__` for the category names. This is also the approach used in the core \nYii framework code. For example,\n\n```php\nYii::debug('start calculating average revenue', __METHOD__);\n```\n\nThe `__METHOD__` constant evaluates as the name of the method (prefixed with the fully qualified class name) where \nthe constant appears. For example, it is equal to the string `'app\\controllers\\RevenueController::calculate'` if \nthe above line of code is called within this method.\n\n> Info: The logging methods described above are actually shortcuts to the [[yii\\log\\Logger::log()|log()]] method \nof the [[yii\\log\\Logger|logger object]] which is a singleton accessible through the expression `Yii::getLogger()`. When\nenough messages are logged or when the application ends, the logger object will call a \n[[yii\\log\\Dispatcher|message dispatcher]] to send recorded log messages to the registered [log targets](#log-targets).\n\n\n## Log Targets <span id=\"log-targets\"></span>\n\nA log target is an instance of the [[yii\\log\\Target]] class or its child class. It filters the log messages by their\nseverity levels and categories and then exports them to some medium. For example, a [[yii\\log\\DbTarget|database target]]\nexports the filtered log messages to a database table, while an [[yii\\log\\EmailTarget|email target]] exports\nthe log messages to specified email addresses.\n\nYou can register multiple log targets in an application by configuring them through the `log` [application component](structure-application-components.md)\nin the application configuration, like the following:\n\n```php\nreturn [\n    // the \"log\" component must be loaded during bootstrapping time\n    'bootstrap' => ['log'],\n    // the \"log\" component process messages with timestamp. Set PHP timezone to create correct timestamp\n    'timeZone' => 'America/Los_Angeles',\n    'components' => [\n        'log' => [\n            'targets' => [\n                [\n                    'class' => 'yii\\log\\DbTarget',\n                    'levels' => ['error', 'warning'],\n                ],\n                [\n                    'class' => 'yii\\log\\EmailTarget',\n                    'levels' => ['error'],\n                    'categories' => ['yii\\db\\*'],\n                    'message' => [\n                       'from' => ['log@example.com'],\n                       'to' => ['admin@example.com', 'developer@example.com'],\n                       'subject' => 'Database errors at example.com',\n                    ],\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n> Note: The `log` component must be loaded during [bootstrapping](runtime-bootstrapping.md) time so that\nit can dispatch log messages to targets promptly. That is why it is listed in the `bootstrap` array as shown above.\n\nIn the above code, two log targets are registered in the [[yii\\log\\Dispatcher::targets]] property: \n\n* the first target selects error and warning messages and saves them in a database table;\n* the second target selects error messages under the categories whose names start with `yii\\db\\`, and sends\n  them in an email to both `admin@example.com` and `developer@example.com`.\n\nYii comes with the following built-in log targets. Please refer to the API documentation about these classes to \nlearn how to configure and use them. \n\n* [[yii\\log\\DbTarget]]: stores log messages in a database table.\n* [[yii\\log\\EmailTarget]]: sends log messages to pre-specified email addresses.\n* [[yii\\log\\FileTarget]]: saves log messages in files.\n* [[yii\\log\\SyslogTarget]]: saves log messages to syslog by calling the PHP function `syslog()`.\n\nIn the following, we will describe the features common to all log targets.\n\n  \n### Message Filtering <span id=\"message-filtering\"></span>\n\nFor each log target, you can configure its [[yii\\log\\Target::levels|levels]] and \n[[yii\\log\\Target::categories|categories]] properties to specify which severity levels and categories of the messages the target should process.\n\nThe [[yii\\log\\Target::levels|levels]] property takes an array consisting of one or several of the following values:\n\n* `error`: corresponding to messages logged by [[Yii::error()]].\n* `warning`: corresponding to messages logged by [[Yii::warning()]].\n* `info`: corresponding to messages logged by [[Yii::info()]].\n* `trace`: corresponding to messages logged by [[Yii::debug()]].\n* `profile`: corresponding to messages logged by [[Yii::beginProfile()]] and [[Yii::endProfile()]], which will\nbe explained in more details in the [Profiling](#performance-profiling) subsection.\n\nIf you do not specify the [[yii\\log\\Target::levels|levels]] property, it means the target will process messages\nof *any* severity level.\n\nThe [[yii\\log\\Target::categories|categories]] property takes an array consisting of message category names or patterns.\nA target will only process messages whose category can be found or match one of the patterns in this array.\nA category pattern is a category name prefix with an asterisk `*` at its end. A category name matches a category pattern\nif it starts with the same prefix of the pattern. For example, `yii\\db\\Command::execute` and `yii\\db\\Command::query`\nare used as category names for the log messages recorded in the [[yii\\db\\Command]] class. They both match\nthe pattern `yii\\db\\*`.\n\nIf you do not specify the [[yii\\log\\Target::categories|categories]] property, it means the target will process\nmessages of *any* category.\n\nIn addition to specifying allowed categories using the [[yii\\log\\Target::categories|categories]] property, you may also\nexclude certain categories by the [[yii\\log\\Target::except|except]] property. If the category of a message\nis found or matches one of the patterns in this property, it will NOT be processed by the target.\n \nThe following target configuration specifies that the target should only process error and warning messages\nunder the categories whose names match either `yii\\db\\*` or `yii\\web\\HttpException:*`, but not `yii\\web\\HttpException:404`.\n\n```php\n[\n    'class' => 'yii\\log\\FileTarget',\n    'levels' => ['error', 'warning'],\n    'categories' => [\n        'yii\\db\\*',\n        'yii\\web\\HttpException:*',\n    ],\n    'except' => [\n        'yii\\web\\HttpException:404',\n    ],\n]\n```\n\n> Info: When an HTTP exception is caught by the [error handler](runtime-handling-errors.md), an error message\n  will be logged with the category name in the format of `yii\\web\\HttpException:ErrorCode`. For example,\n  the [[yii\\web\\NotFoundHttpException]] will cause an error message of category `yii\\web\\HttpException:404`.\n\n\n### Message Formatting <span id=\"message-formatting\"></span>\n\nLog targets export the filtered log messages in a certain format. For example, if you install\na log target of the class [[yii\\log\\FileTarget]], you may find a log message similar to the following in the\n`runtime/log/app.log` file:\n\n```\n2014-10-04 18:10:15 [::1][][-][trace][yii\\base\\Module::getModule] Loading module: debug\n```\n\nBy default, log messages will be formatted as follows by the [[yii\\log\\Target::formatMessage()]]:\n\n```\nTimestamp [IP address][User ID][Session ID][Severity Level][Category] Message Text\n```\n\nYou may customize this format by configuring the [[yii\\log\\Target::prefix]] property which takes a PHP callable\nreturning a customized message prefix. For example, the following code configures a log target to prefix each\nlog message with the current user ID (IP address and Session ID are removed for privacy reasons).\n\n```php\n[\n    'class' => 'yii\\log\\FileTarget',\n    'prefix' => function ($message) {\n        $user = Yii::$app->has('user', true) ? Yii::$app->get('user') : null;\n        $userID = $user ? $user->getId(false) : '-';\n        return \"[$userID]\";\n    }\n]\n```\n\nBesides message prefixes, log targets also append some context information to each batch of log messages.\nBy default, the values of these global PHP variables are included: `$_GET`, `$_POST`, `$_FILES`, `$_COOKIE`,\n`$_SESSION` and `$_SERVER`. You may adjust this behavior by configuring the [[yii\\log\\Target::logVars]] property\nwith the names of the global variables that you want to include by the log target. For example, the following\nlog target configuration specifies that only the value of the `$_SERVER` variable will be appended to the log messages.\n\n```php\n[\n    'class' => 'yii\\log\\FileTarget',\n    'logVars' => ['_SERVER'],\n]\n```\n\nYou may configure `logVars` to be an empty array to totally disable the inclusion of context information.\nOr if you want to implement your own way of providing context information, you may override the\n[[yii\\log\\Target::getContextMessage()]] method.\n\nIn case some of your request fields contain sensitive information you would not like to log (e.g. passwords, access tokens),\nyou may additionally configure `maskVars` property, which can contain both exact values and (case-insensitive) patterns. By default,\nthe following request parameters will be masked with `***`:\n`$_SERVER[HTTP_AUTHORIZATION]`, `$_SERVER[PHP_AUTH_USER]`, `$_SERVER[PHP_AUTH_PW]`, but you can set your own. For example:\n\n```php\n[\n    'class' => 'yii\\log\\FileTarget',\n    'logVars' => ['_SERVER'],\n    'maskVars' => [\n        '_SERVER.HTTP_X_PASSWORD',\n        '_SERVER.*_SECRET', // matches all ending with \"_SECRET\"\n        '_SERVER.SECRET_*', // matches all starting with \"SECRET_\"\n        '_SERVER.*SECRET*', // matches all containing \"SECRET\"\n    ]\n]\n```\n\n### Message Trace Level <span id=\"trace-level\"></span>\n\nDuring development, it is often desirable to see where each log message is coming from. This can be achieved by\nconfiguring the [[yii\\log\\Dispatcher::traceLevel|traceLevel]] property of the `log` component like the following:\n\n```php\nreturn [\n    'bootstrap' => ['log'],\n    'components' => [\n        'log' => [\n            'traceLevel' => YII_DEBUG ? 3 : 0,\n            'targets' => [...],\n        ],\n    ],\n];\n```\n\nThe above application configuration sets [[yii\\log\\Dispatcher::traceLevel|traceLevel]] to be 3 if `YII_DEBUG` is on\nand 0 if `YII_DEBUG` is off. This means, if `YII_DEBUG` is on, each log message will be appended with at most 3\nlevels of the call stack at which the log message is recorded; and if `YII_DEBUG` is off, no call stack information\nwill be included.\n\n> Info: Getting call stack information is not trivial. Therefore, you should only use this feature during development\nor when debugging an application.\n\n\n### Message Flushing and Exporting <span id=\"flushing-exporting\"></span>\n\nAs aforementioned, log messages are maintained in an array by the [[yii\\log\\Logger|logger object]]. To limit the\nmemory consumption by this array, the logger will flush the recorded messages to the [log targets](#log-targets)\neach time the array accumulates a certain number of log messages. You can customize this number by configuring\nthe [[yii\\log\\Dispatcher::flushInterval|flushInterval]] property of the `log` component:\n\n\n```php\nreturn [\n    'bootstrap' => ['log'],\n    'components' => [\n        'log' => [\n            'flushInterval' => 100,   // default is 1000\n            'targets' => [...],\n        ],\n    ],\n];\n```\n\n> Info: Message flushing also occurs when the application ends, which ensures log targets can receive complete log messages.\n\nWhen the [[yii\\log\\Logger|logger object]] flushes log messages to [log targets](#log-targets), they do not get exported\nimmediately. Instead, the message exporting only occurs when a log target accumulates certain number of the filtered\nmessages. You can customize this number by configuring the [[yii\\log\\Target::exportInterval|exportInterval]]\nproperty of individual [log targets](#log-targets), like the following,\n\n```php\n[\n    'class' => 'yii\\log\\FileTarget',\n    'exportInterval' => 100,  // default is 1000\n]\n```\n\nBecause of the flushing and exporting level setting, by default when you call `Yii::debug()` or any other logging\nmethod, you will NOT see the log message immediately in the log targets. This could be a problem for some long-running\nconsole applications. To make each log message appear immediately in the log targets, you should set both\n[[yii\\log\\Dispatcher::flushInterval|flushInterval]] and [[yii\\log\\Target::exportInterval|exportInterval]] to be 1,\nas shown below:\n\n```php\nreturn [\n    'bootstrap' => ['log'],\n    'components' => [\n        'log' => [\n            'flushInterval' => 1,\n            'targets' => [\n                [\n                    'class' => 'yii\\log\\FileTarget',\n                    'exportInterval' => 1,\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n> Note: Frequent message flushing and exporting will degrade the performance of your application.\n\n\n### Toggling Log Targets <span id=\"toggling-log-targets\"></span>\n\nYou can enable or disable a log target by configuring its [[yii\\log\\Target::enabled|enabled]] property.\nYou may do so via the log target configuration or by the following PHP statement in your code:\n\n```php\nYii::$app->log->targets['file']->enabled = false;\n```\n\nThe above code requires you to name a target as `file`, as shown below by using string keys in the\n`targets` array:\n\n```php\nreturn [\n    'bootstrap' => ['log'],\n    'components' => [\n        'log' => [\n            'targets' => [\n                'file' => [\n                    'class' => 'yii\\log\\FileTarget',\n                ],\n                'db' => [\n                    'class' => 'yii\\log\\DbTarget',\n                ],\n            ],\n        ],\n    ],\n];\n```\n\nSince version 2.0.13, you may configure [[yii\\log\\Target::enabled|enabled]] with a callable to\ndefine a dynamic condition for whether the log target should be enabled or not.\nSee the documentation of [[yii\\log\\Target::setEnabled()]] for an example.\n\n### Creating New Targets <span id=\"new-targets\"></span>\n\nCreating a new log target class is very simple. You mainly need to implement the [[yii\\log\\Target::export()]] method\nsending the content of the [[yii\\log\\Target::messages]] array to a designated medium. You may call the\n[[yii\\log\\Target::formatMessage()]] method to format each message. For more details, you may refer to any of the\nlog target classes included in the Yii release.\n\n> Tip: Instead of creating your own loggers you may try any PSR-3 compatible logger such\n  as [Monolog](https://github.com/Seldaek/monolog) by using\n  [PSR log target extension](https://github.com/samdark/yii2-psr-log-target).\n\n## Performance Profiling <span id=\"performance-profiling\"></span>\n\nPerformance profiling is a special type of message logging that is used to measure the time taken by certain\ncode blocks and find out what are the performance bottlenecks. For example, the [[yii\\db\\Command]] class uses\nperformance profiling to find out the time taken by each DB query.\n\nTo use performance profiling, first identify the code blocks that need to be profiled. Then enclose each\ncode block like the following:\n\n```php\n\\Yii::beginProfile('myBenchmark');\n\n...code block being profiled...\n\n\\Yii::endProfile('myBenchmark');\n```\n\nwhere `myBenchmark` stands for a unique token identifying a code block. Later when you examine the profiling\nresult, you will use this token to locate the time spent by the corresponding code block.\n\nIt is important to make sure that the pairs of `beginProfile` and `endProfile` are properly nested.\nFor example,\n\n```php\n\\Yii::beginProfile('block1');\n\n    // some code to be profiled\n\n    \\Yii::beginProfile('block2');\n        // some other code to be profiled\n    \\Yii::endProfile('block2');\n\n\\Yii::endProfile('block1');\n```\n\nIf you miss `\\Yii::endProfile('block1')` or switch the order of `\\Yii::endProfile('block1')` and\n`\\Yii::endProfile('block2')`, the performance profiling will not work.\n\nFor each code block being profiled, a log message with the severity level `profile` is recorded. You can configure\na [log target](#log-targets) to collect such messages and export them. The [Yii debugger](tool-debugger.md) has\na built-in performance profiling panel showing the profiling results.\n"
  },
  {
    "path": "docs/guide/runtime-overview.md",
    "content": "Overview\n========\n\nEach time when a Yii application handles a request, it undergoes a similar workflow.\n\n1. A user makes a request to the [entry script](structure-entry-scripts.md) `web/index.php`.\n2. The entry script loads the application [configuration](concept-configurations.md) and creates\n   an [application](structure-applications.md) instance to handle the request.\n3. The application resolves the requested [route](runtime-routing.md) with the help of\n   the [request](runtime-requests.md) application component.\n4. The application creates a [controller](structure-controllers.md) instance to handle the request.\n5. The controller creates an [action](structure-controllers.md) instance and performs the filters for the action.\n6. If any [filter](structure-filters.md) fails, the action is cancelled.\n7. If all filters pass, the action is executed.\n8. The action loads a data [model](structure-models.md), possibly from a database.\n9. The action renders a [view](structure-views.md), providing it with the data model.\n10. The rendered result is returned to the [response](runtime-responses.md) application component.\n11. The response component sends the rendered result to the user's browser.\n\nThe following diagram shows how an application handles a request.\n\n![Request Lifecycle](images/request-lifecycle.png)\n\nIn this section, we will describe in detail how some of these steps work.\n"
  },
  {
    "path": "docs/guide/runtime-requests.md",
    "content": "Requests\n========\n\nRequests made to an application are represented in terms of [[yii\\web\\Request]] objects which provide information\nsuch as request parameters, HTTP headers, cookies, etc. For a given request, you can get access to the corresponding\nrequest object via the `request` [application component](structure-application-components.md) which is an instance\nof [[yii\\web\\Request]], by default. In this section, we will describe how you can make use of this component in your applications.\n\n\n## Request Parameters <span id=\"request-parameters\"></span>\n\nTo get request parameters, you can call [[yii\\web\\Request::get()|get()]] and [[yii\\web\\Request::post()|post()]] methods\nof the `request` component. They return the values of `$_GET` and `$_POST`, respectively. For example,\n\n```php\n$request = Yii::$app->request;\n\n$get = $request->get();\n// equivalent to: $get = $_GET;\n\n$id = $request->get('id');\n// equivalent to: $id = isset($_GET['id']) ? $_GET['id'] : null;\n\n$id = $request->get('id', 1);\n// equivalent to: $id = isset($_GET['id']) ? $_GET['id'] : 1;\n\n$post = $request->post();\n// equivalent to: $post = $_POST;\n\n$name = $request->post('name');\n// equivalent to: $name = isset($_POST['name']) ? $_POST['name'] : null;\n\n$name = $request->post('name', '');\n// equivalent to: $name = isset($_POST['name']) ? $_POST['name'] : '';\n```\n\n> Info: Instead of directly accessing `$_GET` and `$_POST` to retrieve the request parameters, it is recommended\n  that you get them via the `request` component as shown above. This will make writing tests easier because\n  you can create a mock request component with faked request data.\n\nWhen implementing [RESTful APIs](rest-quick-start.md), you often need to retrieve parameters that are submitted\nvia PUT, PATCH or other [request methods](#request-methods). You can get these parameters by calling\nthe [[yii\\web\\Request::getBodyParam()]] methods. For example,\n\n```php\n$request = Yii::$app->request;\n\n// returns all parameters\n$params = $request->bodyParams;\n\n// returns the parameter \"id\"\n$param = $request->getBodyParam('id');\n```\n\n> Info: Unlike `GET` parameters, parameters submitted via `POST`, `PUT`, `PATCH` etc. are sent in the request body.\n  The `request` component will parse these parameters when you access them through the methods described above.\n  You can customize the way how these parameters are parsed by configuring the [[yii\\web\\Request::parsers]] property.\n  \n\n## Request Methods <span id=\"request-methods\"></span>\n\nYou can get the HTTP method used by the current request via the expression `Yii::$app->request->method`.\nA whole set of boolean properties is also provided for you to check if the current method is of certain type.\nFor example,\n\n```php\n$request = Yii::$app->request;\n\nif ($request->isAjax) { /* the request is an AJAX request */ }\nif ($request->isGet)  { /* the request method is GET */ }\nif ($request->isPost) { /* the request method is POST */ }\nif ($request->isPut)  { /* the request method is PUT */ }\n```\n\n## Request URLs <span id=\"request-urls\"></span>\n\nThe `request` component provides many ways of inspecting the currently requested URL. \n\nAssuming the URL being requested is `https://example.com/admin/index.php/product?id=100`, you can get various\nparts of this URL as summarized in the following:\n\n* [[yii\\web\\Request::url|url]]: returns `/admin/index.php/product?id=100`, which is the URL without the host info part. \n* [[yii\\web\\Request::absoluteUrl|absoluteUrl]]: returns `https://example.com/admin/index.php/product?id=100`,\n  which is the whole URL including the host info part.\n* [[yii\\web\\Request::hostInfo|hostInfo]]: returns `https://example.com`, which is the host info part of the URL.\n* [[yii\\web\\Request::pathInfo|pathInfo]]: returns `/product`, which is the part after the entry script and \n  before the question mark (query string).\n* [[yii\\web\\Request::queryString|queryString]]: returns `id=100`, which is the part after the question mark. \n* [[yii\\web\\Request::baseUrl|baseUrl]]: returns `/admin`, which is the part after the host info and before\n  the entry script name.\n* [[yii\\web\\Request::scriptUrl|scriptUrl]]: returns `/admin/index.php`, which is the URL without path info and query string.\n* [[yii\\web\\Request::serverName|serverName]]: returns `example.com`, which is the host name in the URL.\n* [[yii\\web\\Request::serverPort|serverPort]]: returns 80, which is the port used by the Web server.\n\n\n## HTTP Headers <span id=\"http-headers\"></span> \n\nYou can get the HTTP header information through the [[yii\\web\\HeaderCollection|header collection]] returned \nby the [[yii\\web\\Request::headers]] property. For example,\n\n```php\n// $headers is an object of yii\\web\\HeaderCollection \n$headers = Yii::$app->request->headers;\n\n// returns the Accept header value\n$accept = $headers->get('Accept');\n\nif ($headers->has('User-Agent')) { /* there is User-Agent header */ }\n```\n\nThe `request` component also provides support for quickly accessing some commonly used headers, including:\n\n* [[yii\\web\\Request::userAgent|userAgent]]: returns the value of the `User-Agent` header.\n* [[yii\\web\\Request::contentType|contentType]]: returns the value of the `Content-Type` header which indicates\n  the MIME type of the data in the request body.\n* [[yii\\web\\Request::acceptableContentTypes|acceptableContentTypes]]: returns the content MIME types acceptable by users.\n  The returned types are ordered by their quality score. Types with the highest scores will be returned first.\n* [[yii\\web\\Request::acceptableLanguages|acceptableLanguages]]: returns the languages acceptable by users.\n  The returned languages are ordered by their preference level. The first element represents the most preferred language.\n\nIf your application supports multiple languages and you want to display pages in the language that is the most preferred\nby the end user, you may use the language negotiation method [[yii\\web\\Request::getPreferredLanguage()]].\nThis method takes a list of languages supported by your application, compares them with [[yii\\web\\Request::acceptableLanguages|acceptableLanguages]],\nand returns the most appropriate language.\n\n> Tip: You may also use the [[yii\\filters\\ContentNegotiator|ContentNegotiator]] filter to dynamically determine \n  what content type and language should be used in the response. The filter implements the content negotiation\n  on top of the properties and methods described above.\n\n\n## Client Information <span id=\"client-information\"></span>\n\nYou can get the host name and IP address\nof the client machine through [[yii\\web\\Request::userHost|userHost]]\nand [[yii\\web\\Request::userIP|userIP]], respectively. For example,\n\n```php\n$userHost = Yii::$app->request->userHost;\n$userIP = Yii::$app->request->userIP;\n```\n\n## Trusted proxies and headers <span id=\"trusted-proxies\"></span>\n\nIn the previous section you have seen how to get user information like host and IP address.\nThis will work out of the box in a normal setup where a single webserver is used to serve the website.\nIf your Yii application however runs behind a reverse proxy, you need to add additional configuration\nto retrieve this information as the direct client is now the proxy and the user IP address is passed to\nthe Yii application by a header set by the proxy.\n\nYou should not blindly trust headers provided by proxies unless you explicitly trust the proxy.\nSince 2.0.13 Yii supports configuring trusted proxies via the \n[[yii\\web\\Request::trustedHosts|trustedHosts]],\n[[yii\\web\\Request::secureHeaders|secureHeaders]], \n[[yii\\web\\Request::ipHeaders|ipHeaders]],\n[[yii\\web\\Request::secureProtocolHeaders|secureProtocolHeaders]] and\n[[yii\\web\\Request::portHeaders|portHeaders]] (since 2.0.46)\nproperties of the `request` component.\n\nThe following is a request config for an application that runs behind an array of reverse proxies,\nwhich are located in the `10.0.2.0/24` IP network:\n\n```php\n'request' => [\n    // ...\n    'trustedHosts' => [\n        '10.0.2.0/24',\n    ],\n],\n```\n\nThe IP is sent by the proxy in the `X-Forwarded-For` header by default, and the protocol (`http` or `https`) is sent in `X-Forwarded-Proto`.\n\nIn case your proxies are using different headers you can use the request configuration to adjust these, e.g.:\n\n```php\n'request' => [\n    // ...\n    'trustedHosts' => [\n        '10.0.2.0/24' => [\n            'X-ProxyUser-Ip',\n            'Front-End-Https',\n        ],\n    ],\n    'secureHeaders' => [\n        'X-Forwarded-For',\n        'X-Forwarded-Host',\n        'X-Forwarded-Proto',\n        'X-Forwarded-Port',\n        'X-Proxy-User-Ip',\n        'Front-End-Https',\n    ],\n    'ipHeaders' => [\n        'X-Proxy-User-Ip',\n    ],\n    'secureProtocolHeaders' => [\n        'Front-End-Https' => ['on']\n    ],\n],\n```\n\nWith the above configuration, all headers listed in `secureHeaders` are filtered from the request,\nexcept the `X-ProxyUser-Ip` and `Front-End-Https` headers in case the request is made by the proxy.\nIn that case the former is used to retrieve the user IP as configured in `ipHeaders` and the latter\nwill be used to determine the result of [[yii\\web\\Request::getIsSecureConnection()]].\n\nSince 2.0.31 [RFC 7239](https://datatracker.ietf.org/doc/html/rfc7239) `Forwarded` header is supported. In order to enable\nit you need to add header name to `secureHeaders`. Make sure your proxy is setting it, otherwise end user would be\nable to spoof IP and protocol.\n\n### Already resolved user IP <span id=\"already-respolved-user-ip\"></span>\n\nIf the user's IP address is resolved before the Yii application (e.g. `ngx_http_realip_module` or similar),\nthe `request` component will work correctly with the following configuration:\n\n```php\n'request' => [\n    // ...\n    'trustedHosts' => [\n        '0.0.0.0/0',\n    ],\n    'ipHeaders' => [], \n],\n```\n\nIn this case, the value of [[yii\\web\\Request::userIP|userIP]] will be equal to `$_SERVER['REMOTE_ADDR']`.\nAlso, properties that are resolved from HTTP headers will work correctly (e.g. [[yii\\web\\Request::getIsSecureConnection()]]).\n\n> Warning: The `trustedHosts=['0.0.0.0/0']` setting assumes that all IPs are trusted.\n"
  },
  {
    "path": "docs/guide/runtime-responses.md",
    "content": "Responses\n=========\n\nWhen an application finishes handling a [request](runtime-requests.md), it generates a [[yii\\web\\Response|response]] object\nand sends it to the end user. The response object contains information such as the HTTP status code, HTTP headers and body.\nThe ultimate goal of Web application development is essentially to build such response objects upon various requests.\n\nIn most cases you should mainly deal with the `response` [application component](structure-application-components.md)\nwhich is an instance of [[yii\\web\\Response]], by default. However, Yii also allows you to create your own response\nobjects and send them to end users as we will explain in the following.\n\nIn this section, we will describe how to compose and send responses to end users. \n\n\n## Status Code <span id=\"status-code\"></span>\n\nOne of the first things you would do when building a response is to state whether the request is successfully handled.\nThis is done by setting the [[yii\\web\\Response::statusCode]] property which can take one of the valid\n[HTTP status codes](https://tools.ietf.org/html/rfc2616#section-10). For example, to indicate the request\nis successfully handled, you may set the status code to be 200, like the following:\n\n```php\nYii::$app->response->statusCode = 200;\n```\n\nHowever, in most cases you do not need to explicitly set the status code. This is because the default value\nof [[yii\\web\\Response::statusCode]] is 200. And if you want to indicate the request is unsuccessful, you may\nthrow an appropriate HTTP exception like the following:\n\n```php\nthrow new \\yii\\web\\NotFoundHttpException;\n```\n\nWhen the [error handler](runtime-handling-errors.md) catches an exception, it will extract the status code \nfrom the exception and assign it to the response. For the [[yii\\web\\NotFoundHttpException]] above, it is\nassociated with the HTTP status 404. The following HTTP exceptions are predefined in Yii:\n\n* [[yii\\web\\BadRequestHttpException]]: status code 400.\n* [[yii\\web\\ConflictHttpException]]: status code 409.\n* [[yii\\web\\ForbiddenHttpException]]: status code 403.\n* [[yii\\web\\GoneHttpException]]: status code 410.\n* [[yii\\web\\MethodNotAllowedHttpException]]: status code 405.\n* [[yii\\web\\NotAcceptableHttpException]]: status code 406. \n* [[yii\\web\\NotFoundHttpException]]: status code 404.\n* [[yii\\web\\ServerErrorHttpException]]: status code 500.\n* [[yii\\web\\TooManyRequestsHttpException]]: status code 429.\n* [[yii\\web\\UnauthorizedHttpException]]: status code 401.\n* [[yii\\web\\UnsupportedMediaTypeHttpException]]: status code 415.\n\nIf the exception that you want to throw is not among the above list, you may create one by extending\nfrom [[yii\\web\\HttpException]], or directly throw it with a status code, for example,\n \n```php\nthrow new \\yii\\web\\HttpException(402);\n```\n\n\n## HTTP Headers <span id=\"http-headers\"></span> \n\nYou can send HTTP headers by manipulating the [[yii\\web\\Response::headers|header collection]] in the `response` component.\nFor example,\n\n```php\n$headers = Yii::$app->response->headers;\n\n// add a Pragma header. Existing Pragma headers will NOT be overwritten.\n$headers->add('Pragma', 'no-cache');\n\n// set a Pragma header. Any existing Pragma headers will be discarded.\n$headers->set('Pragma', 'no-cache');\n\n// remove Pragma header(s) and return the removed Pragma header values in an array\n$values = $headers->remove('Pragma');\n```\n\n> Info: Header names are case insensitive. And the newly registered headers are not sent to the user until\n  the [[yii\\web\\Response::send()]] method is called.\n\n\n## Response Body <span id=\"response-body\"></span>\n\nMost responses should have a body which gives the content that you want to show to end users.\n\nIf you already have a formatted body string, you may assign it to the [[yii\\web\\Response::content]] property\nof the response. For example,\n\n```php\nYii::$app->response->content = 'hello world!';\n```\n\nIf your data needs to be formatted before sending it to end users, you should set both of the\n[[yii\\web\\Response::format|format]] and [[yii\\web\\Response::data|data]] properties. The [[yii\\web\\Response::format|format]]\nproperty specifies in which format the [[yii\\web\\Response::data|data]] should be formatted. For example,\n\n```php\n$response = Yii::$app->response;\n$response->format = \\yii\\web\\Response::FORMAT_JSON;\n$response->data = ['message' => 'hello world'];\n```\n\nYii supports the following formats out of the box, each implemented by a [[yii\\web\\ResponseFormatterInterface|formatter]] class.\nYou can customize these formatters or add new ones by configuring the [[yii\\web\\Response::formatters]] property.\n\n* [[yii\\web\\Response::FORMAT_HTML|HTML]]: implemented by [[yii\\web\\HtmlResponseFormatter]].\n* [[yii\\web\\Response::FORMAT_XML|XML]]: implemented by [[yii\\web\\XmlResponseFormatter]].\n* [[yii\\web\\Response::FORMAT_JSON|JSON]]: implemented by [[yii\\web\\JsonResponseFormatter]].\n* [[yii\\web\\Response::FORMAT_JSONP|JSONP]]: implemented by [[yii\\web\\JsonResponseFormatter]].\n* [[yii\\web\\Response::FORMAT_RAW|RAW]]: use this format if you want to send the response directly without applying any formatting.\n\nWhile the response body can be set explicitly as shown above, in most cases you may set it implicitly by the return value\nof [action](structure-controllers.md) methods. A common use case is like the following:\n \n```php\npublic function actionIndex()\n{\n    return $this->render('index');\n}\n```\n\nThe `index` action above returns the rendering result of the `index` view. The return value will be taken\nby the `response` component, formatted and then sent to end users.\n\nBecause by default the response format is [[yii\\web\\Response::FORMAT_HTML|HTML]], you should only return a string\nin an action method. If you want to use a different response format, you should set it first before returning the data.\nFor example,\n\n```php\npublic function actionInfo()\n{\n    \\Yii::$app->response->format = \\yii\\web\\Response::FORMAT_JSON;\n    return [\n        'message' => 'hello world',\n        'code' => 100,\n    ];\n}\n```\n\nAs aforementioned, besides using the default `response` application component, you can also create your own\nresponse objects and send them to end users. You can do so by returning such object in an action method, like the following,\n\n```php\npublic function actionInfo()\n{\n    return \\Yii::createObject([\n        'class' => 'yii\\web\\Response',\n        'format' => \\yii\\web\\Response::FORMAT_JSON,\n        'data' => [\n            'message' => 'hello world',\n            'code' => 100,\n        ],\n    ]);\n}\n```\n\n> Note: If you are creating your own response objects, you will not be able to take advantage of the configurations\n  that you set for the `response` component in the application configuration. You can, however, use \n  [dependency injection](concept-di-container.md) to apply a common configuration to your new response objects.\n\n\n## Browser Redirection <span id=\"browser-redirection\"></span>\n\nBrowser redirection relies on sending a `Location` HTTP header. Because this feature is commonly used, Yii provides\nsome special support for it.\n\nYou can redirect the user browser to a URL by calling the [[yii\\web\\Response::redirect()]] method. The method\nsets the appropriate `Location` header with the given URL and returns the response object itself. In an action method,\nyou can call its shortcut version [[yii\\web\\Controller::redirect()]]. For example,\n\n```php\npublic function actionOld()\n{\n    return $this->redirect('https://example.com/new', 301);\n}\n```\n\nIn the above code, the action method returns the result of the `redirect()` method. As explained before, the response\nobject returned by an action method will be used as the response sending to end users.\n\nIn places other than an action method, you should call [[yii\\web\\Response::redirect()]] directly followed by \na chained call to the [[yii\\web\\Response::send()]] method to ensure no extra content will be appended to the response.\n\n```php\n\\Yii::$app->response->redirect('https://example.com/new', 301)->send();\n```\n\n> Info: By default, the [[yii\\web\\Response::redirect()]] method sets the response status code to be 302 which instructs\n  the browser that the resource being requested is *temporarily* located in a different URI. You can pass in a status\n  code 301 to tell the browser that the resource has been *permanently* relocated.\n\nWhen the current request is an AJAX request, sending a `Location` header will not automatically cause the browser\nto redirect. To solve this problem, the [[yii\\web\\Response::redirect()]] method sets an `X-Redirect` header with \nthe redirection URL as its value. On the client-side, you may write JavaScript code to read this header value and\nredirect the browser accordingly.\n\n> Info: Yii comes with a `yii.js` JavaScript file which provides a set of commonly used JavaScript utilities,\n  including browser redirection based on the `X-Redirect` header. Therefore, if you are using this JavaScript file\n  (by registering the [[yii\\web\\YiiAsset]] asset bundle), you do not need to write anything to support AJAX redirection.\n  More information about `yii.js` can be found in the [Client Scripts Section](output-client-scripts.md#yii.js).\n\n## Sending Files <span id=\"sending-files\"></span>\n\nLike browser redirection, file sending is another feature that relies on specific HTTP headers. Yii provides\na set of methods to support various file sending needs. They all have built-in support for the HTTP range header.\n\n* [[yii\\web\\Response::sendFile()]]: sends an existing file to a client.\n* [[yii\\web\\Response::sendContentAsFile()]]: sends a text string as a file to a client.\n* [[yii\\web\\Response::sendStreamAsFile()]]: sends an existing file stream as a file to a client. \n\nThese methods have the same method signature with the response object as the return value. If the file\nto be sent is very big, you should consider using [[yii\\web\\Response::sendStreamAsFile()]] because it is more\nmemory efficient. The following example shows how to send a file in a controller action:\n\n```php\npublic function actionDownload()\n{\n    return \\Yii::$app->response->sendFile('path/to/file.txt');\n}\n```\n\nIf you are calling the file sending method in places other than an action method, you should also call\nthe [[yii\\web\\Response::send()]] method afterwards to ensure no extra content will be appended to the response.\n\n```php\n\\Yii::$app->response->sendFile('path/to/file.txt')->send();\n```\n\nSome Web servers have a special file sending support called *X-Sendfile*. The idea is to redirect the\nrequest for a file to the Web server which will directly serve the file. As a result, the Web application\ncan terminate earlier while the Web server is sending the file. To use this feature, you may call\nthe [[yii\\web\\Response::xSendFile()]]. The following list summarizes how to enable the `X-Sendfile` feature\nfor some popular Web servers:\n\n- Apache: [X-Sendfile](https://tn123.org/mod_xsendfile)\n- Lighttpd v1.4: [X-LIGHTTPD-send-file](https://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file)\n- Lighttpd v1.5: [X-Sendfile](https://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file)\n- Nginx: [X-Accel-Redirect](https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile/)\n- Cherokee: [X-Sendfile and X-Accel-Redirect](https://www.cherokee-project.com/doc/other_goodies.html#x-sendfile)\n\n\n## Sending Response <span id=\"sending-response\"></span>\n\nThe content in a response is not sent to the user until the [[yii\\web\\Response::send()]] method is called.\nBy default, this method will be called automatically at the end of [[yii\\base\\Application::run()]]. You can, however,\nexplicitly call this method to force sending out the response immediately.\n\nThe [[yii\\web\\Response::send()]] method takes the following steps to send out a response:\n\n1. Trigger the [[yii\\web\\Response::EVENT_BEFORE_SEND]] event.\n2. Call [[yii\\web\\Response::prepare()]] to format [[yii\\web\\Response::data|response data]] into \n   [[yii\\web\\Response::content|response content]].\n3. Trigger the [[yii\\web\\Response::EVENT_AFTER_PREPARE]] event.\n4. Call [[yii\\web\\Response::sendHeaders()]] to send out the registered HTTP headers.\n5. Call [[yii\\web\\Response::sendContent()]] to send out the response body content.\n6. Trigger the [[yii\\web\\Response::EVENT_AFTER_SEND]] event.\n\nAfter the [[yii\\web\\Response::send()]] method is called once, any further call to this method will be ignored.\nThis means once the response is sent out, you will not be able to append more content to it.\n\nAs you can see, the [[yii\\web\\Response::send()]] method triggers several useful events. By responding to\nthese events, it is possible to adjust or decorate the response.\n"
  },
  {
    "path": "docs/guide/runtime-routing.md",
    "content": "Routing and URL Creation\n========================\n\nWhen a Yii application starts processing a requested URL, the first step it takes is to parse the URL\ninto a [route](structure-controllers.md#routes). The route is then used to instantiate the corresponding\n[controller action](structure-controllers.md) to handle the request. This whole process is called *routing*.\n\nThe reverse process of routing is called *URL creation*, which creates a URL from a given route\nand the associated query parameters. When the created URL is later requested, the routing process can resolve it\nback into the original route and query parameters.\n\nThe central piece responsible for routing and URL creation is the [[yii\\web\\UrlManager|URL manager]],\nwhich is registered as the `urlManager` [application component](structure-application-components.md). The [[yii\\web\\UrlManager|URL manager]]\nprovides the [[yii\\web\\UrlManager::parseRequest()|parseRequest()]] method to parse an incoming request into\na route and the associated query parameters and the [[yii\\web\\UrlManager::createUrl()|createUrl()]] method to\ncreate a URL from a given route and its associated query parameters.\n\nBy configuring the `urlManager` component in the application configuration, you can let your application\nrecognize arbitrary URL formats without modifying your existing application code. For example, you can\nuse the following code to create a URL for the `post/view` action:\n\n```php\nuse yii\\helpers\\Url;\n\n// Url::to() calls UrlManager::createUrl() to create a URL\n$url = Url::to(['post/view', 'id' => 100]);\n```\n\nDepending on the `urlManager` configuration, the created URL may look like one of the following (or other format).\nAnd if the created URL is requested later, it will still be parsed back into the original route and query parameter value.\n\n```\n/index.php?r=post%2Fview&id=100\n/index.php/post/100\n/posts/100\n```\n\n\n## URL Formats <span id=\"url-formats\"></span>\n\nThe [[yii\\web\\UrlManager|URL manager]] supports two URL formats:\n\n- the default URL format;\n- the pretty URL format.\n\nThe default URL format uses a [[yii\\web\\UrlManager::$routeParam|query parameter]] named `r` to represent the route and normal query parameters\nto represent the query parameters associated with the route. For example, the URL `/index.php?r=post/view&id=100` represents\nthe route `post/view` and the `id` query parameter `100`. The default URL format does not require any configuration of\nthe [[yii\\web\\UrlManager|URL manager]] and works in any Web server setup.\n\nThe pretty URL format uses the extra path following the entry script name to represent the route and the associated\nquery parameters. For example, the extra path in the URL `/index.php/post/100` is `/post/100` which may represent\nthe route `post/view` and the `id` query parameter `100` with a proper [[yii\\web\\UrlManager::rules|URL rule]]. To use\nthe pretty URL format, you will need to design a set of [[yii\\web\\UrlManager::rules|URL rules]] according to the actual\nrequirement about how the URLs should look like.\n\nYou may switch between the two URL formats by toggling the [[yii\\web\\UrlManager::enablePrettyUrl|enablePrettyUrl]]\nproperty of the [[yii\\web\\UrlManager|URL manager]] without changing any other application code.\n\n\n## Routing <span id=\"routing\"></span>\n\nRouting involves two steps:\n\n- the incoming request is parsed into a route and the associated query parameters;\n- a [controller action](structure-controllers.md#actions) corresponding to the parsed route\nis created to handle the request.\n\nWhen using the default URL format, parsing a request into a route is as simple as getting the value of a `GET`\nquery parameter named `r`.\n\nWhen using the pretty URL format, the [[yii\\web\\UrlManager|URL manager]] will examine the registered\n[[yii\\web\\UrlManager::rules|URL rules]] to find matching one that can resolve the request into a route.\nIf such a rule cannot be found, a [[yii\\web\\NotFoundHttpException]] exception will be thrown.\n\nOnce the request is parsed into a route, it is time to create the controller action identified by the route.\nThe route is broken down into multiple parts by the slashes in it. For example, `site/index` will be\nbroken into `site` and `index`. Each part is an ID which may refer to a module, a controller or an action.\nStarting from the first part in the route, the application takes the following steps to create modules (if any),\ncontroller and action:\n\n1. Set the application as the current module.\n2. Check if the [[yii\\base\\Module::controllerMap|controller map]] of the current module contains the current ID.\n   If so, a controller object will be created according to the controller configuration found in the map,\n   and Step 5 will be taken to handle the rest part of the route.\n3. Check if the ID refers to a module listed in the [[yii\\base\\Module::modules|modules]] property of\n   the current module. If so, a module is created according to the configuration found in the module list,\n   and Step 2 will be taken to handle the next part of the route under the context of the newly created module.\n4. Treat the ID as a [controller ID](structure-controllers.md#controller-ids) and create a controller object. Do the next step with the rest part of\n   the route.\n5. The controller looks for the current ID in its [[yii\\base\\Controller::actions()|action map]]. If found,\n   it creates an action according to the configuration found in the map. Otherwise, the controller will\n   attempt to create an inline action which is defined by an action method corresponding to the current [action ID](structure-controllers.md#action-ids).\n\nAmong the above steps, if any error occurs, a [[yii\\web\\NotFoundHttpException]] will be thrown, indicating\nthe failure of the routing process.\n\n\n### Default Route <span id=\"default-route\"></span>\n\nWhen a request is parsed into an empty route, the so-called *default route* will be used, instead. By default,\nthe default route is `site/index`,  which refers to the `index` action of the `site` controller. You may\ncustomize it by configuring the [[yii\\web\\Application::defaultRoute|defaultRoute]] property of the application\nin the application configuration like the following:\n\n```php\n[\n    // ...\n    'defaultRoute' => 'main/index',\n];\n```\n\nSimilar to the default route of the application, there is also a default route for modules, so for example if there\nis a `user` module and the request is parsed into the route `user` the module's [[yii\\base\\Module::defaultRoute|defaultRoute]]\nis used to determine the controller. By default the controller name is `default`. If no action is specified in [[yii\\base\\Module::defaultRoute|defaultRoute]],\nthe [[yii\\base\\Controller::defaultAction|defaultAction]] property of the controller is used to determine the action.\nIn this example, the full route would be `user/default/index`.\n\n\n### `catchAll` Route <span id=\"catchall-route\"></span>\n\nSometimes, you may want to put your Web application in maintenance mode temporarily and display the same\ninformational page for all requests. There are many ways to accomplish this goal. But one of the simplest\nways is to configure the [[yii\\web\\Application::catchAll]] property like the following in the application configuration:\n\n```php\n[\n    // ...\n    'catchAll' => ['site/offline'],\n];\n```\n\nWith the above configuration, the `site/offline` action will be used to handle all incoming requests.\n\nThe `catchAll` property should take an array whose first element specifies a route, and\nthe rest of the elements (name-value pairs) specify the parameters to be [bound to the action](structure-controllers.md#action-parameters).\n\n> Info: The [debug toolbar](https://github.com/yiisoft/yii2-debug/blob/master/docs/guide/README.md) in development environment\n> will not work when this property is enabled.\n\n\n## Creating URLs <span id=\"creating-urls\"></span>\n\nYii provides a helper method [[yii\\helpers\\Url::to()]] to create various kinds of URLs from given routes and\ntheir associated query parameters. For example,\n\n```php\nuse yii\\helpers\\Url;\n\n// creates a URL to a route: /index.php?r=post%2Findex\necho Url::to(['post/index']);\n\n// creates a URL to a route with parameters: /index.php?r=post%2Fview&id=100\necho Url::to(['post/view', 'id' => 100]);\n\n// creates an anchored URL: /index.php?r=post%2Fview&id=100#content\necho Url::to(['post/view', 'id' => 100, '#' => 'content']);\n\n// creates an absolute URL: https://www.example.com/index.php?r=post%2Findex\necho Url::to(['post/index'], true);\n\n// creates an absolute URL using the https scheme: https://www.example.com/index.php?r=post%2Findex\necho Url::to(['post/index'], 'https');\n```\n\nNote that in the above example, we assume the default URL format is being used. If the pretty URL format is enabled,\nthe created URLs will be different, according to the [[yii\\web\\UrlManager::rules|URL rules]] in use.\n\nThe route passed to the [[yii\\helpers\\Url::to()]] method is context sensitive. It can be either a *relative* route\nor an *absolute* route which will be normalized according to the following rules:\n\n- If the route is an empty string, the currently requested [[yii\\web\\Controller::route|route]] will be used;\n- If the route contains no slashes at all, it is considered to be an action ID of the current controller\n  and will be prepended with the [[\\yii\\web\\Controller::uniqueId|uniqueId]] value of the current controller;\n- If the route has no leading slash, it is considered to be a route relative to the current module and\n  will be prepended with the [[\\yii\\base\\Module::uniqueId|uniqueId]] value of the current module.\n\nStarting from version 2.0.2, you may specify a route in terms of an [alias](concept-aliases.md). If this is the case,\nthe alias will first be converted into the actual route which will then be turned into an absolute route according\nto the above rules.\n\nFor example, assume the current module is `admin` and the current controller is `post`,\n\n```php\nuse yii\\helpers\\Url;\n\n// currently requested route: /index.php?r=admin%2Fpost%2Findex\necho Url::to(['']);\n\n// a relative route with action ID only: /index.php?r=admin%2Fpost%2Findex\necho Url::to(['index']);\n\n// a relative route: /index.php?r=admin%2Fpost%2Findex\necho Url::to(['post/index']);\n\n// an absolute route: /index.php?r=post%2Findex\necho Url::to(['/post/index']);\n\n// using an alias \"@posts\", which is defined as \"/post/index\": /index.php?r=post%2Findex\necho Url::to(['@posts']);\n```\n\nThe [[yii\\helpers\\Url::to()]] method is implemented by calling the [[yii\\web\\UrlManager::createUrl()|createUrl()]]\nand [[yii\\web\\UrlManager::createAbsoluteUrl()|createAbsoluteUrl()]] methods of the [[yii\\web\\UrlManager|URL manager]].\nIn the next few subsections, we will explain how to configure the [[yii\\web\\UrlManager|URL manager]] to customize\nthe format of the created URLs.\n\nThe [[yii\\helpers\\Url::to()]] method also supports creating URLs that are **not** related with particular routes.\nInstead of passing an array as its first parameter, you should pass a string in this case. For example,\n\n```php\nuse yii\\helpers\\Url;\n\n// currently requested URL: /index.php?r=admin%2Fpost%2Findex\necho Url::to();\n\n// an aliased URL: https://example.com\nYii::setAlias('@example', 'https://example.com/');\necho Url::to('@example');\n\n// an absolute URL: https://example.com/images/logo.gif\necho Url::to('/images/logo.gif', true);\n```\n\nBesides the `to()` method, the [[yii\\helpers\\Url]] helper class also provides several other convenient URL creation\nmethods. For example,\n\n```php\nuse yii\\helpers\\Url;\n\n// home page URL: /index.php?r=site%2Findex\necho Url::home();\n\n// the base URL, useful if the application is deployed in a sub-folder of the Web root\necho Url::base();\n\n// the canonical URL of the currently requested URL\n// see https://en.wikipedia.org/wiki/Canonical_link_element\necho Url::canonical();\n\n// remember the currently requested URL and retrieve it back in later requests\nUrl::remember();\necho Url::previous();\n```\n\n\n## Using Pretty URLs <span id=\"using-pretty-urls\"></span>\n\nTo use pretty URLs, configure the `urlManager` component in the application configuration like the following:\n\n```php\n[\n    'components' => [\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            'showScriptName' => false,\n            'enableStrictParsing' => false,\n            'rules' => [\n                // ...\n            ],\n        ],\n    ],\n]\n```\n\nThe [[yii\\web\\UrlManager::enablePrettyUrl|enablePrettyUrl]] property is mandatory as it toggles the pretty URL format.\nThe rest of the properties are optional. However, their configuration shown above is most commonly used.\n\n* [[yii\\web\\UrlManager::showScriptName|showScriptName]]: this property determines whether the entry script\n  should be included in the created URLs. For example, instead of creating a URL `/index.php/post/100`,\n  by setting this property to be `false`, a URL `/post/100` will be generated.\n* [[yii\\web\\UrlManager::enableStrictParsing|enableStrictParsing]]: this property determines whether to enable\n  strict request parsing. If strict parsing is enabled, the incoming requested URL must match at least one of\n  the [[yii\\web\\UrlManager::rules|rules]] in order to be treated as a valid request, otherwise a [[yii\\web\\NotFoundHttpException]]\n  will be thrown. If strict parsing is disabled, when none of the [[yii\\web\\UrlManager::rules|rules]] matches\n  the requested URL, the path info part of the URL will be treated as the requested route.\n* [[yii\\web\\UrlManager::rules|rules]]: this property contains a list of rules specifying how to parse and create\n  URLs. It is the main property that you should work with in order to create URLs whose format satisfies your\n  particular application requirement.\n\n> Note: In order to hide the entry script name in the created URLs, besides setting\n  [[yii\\web\\UrlManager::showScriptName|showScriptName]] to be `false`, you may also need to configure your Web server\n  so that it can correctly identify which PHP script should be executed when a requested URL does not explicitly\n  specify one. If you are using Apache or nginx Web server, you may refer to the recommended configuration as described in the\n  [Installation](start-installation.md#recommended-apache-configuration) section.\n\n\n### URL Rules <span id=\"url-rules\"></span>\n\nA URL rule is a class implementing the [[yii\\web\\UrlRuleInterface]], usually [[yii\\web\\UrlRule]]. Each URL rule consists of a pattern used\nfor matching the path info part of URLs, a route, and a few query parameters. A URL rule can be used to parse a request\nif its pattern matches the requested URL. A URL rule can be used to create a URL if its route and query parameter\nnames match those that are given.\n\nWhen the pretty URL format is enabled, the [[yii\\web\\UrlManager|URL manager]] uses the URL rules declared in its\n[[yii\\web\\UrlManager::rules|rules]] property to parse incoming requests and create URLs. In particular,\nto parse an incoming request, the [[yii\\web\\UrlManager|URL manager]] examines the rules in the order they are\ndeclared and looks for the *first* rule that matches the requested URL. The matching rule is then used to\nparse the URL into a route and its associated parameters. Similarly, to create a URL, the [[yii\\web\\UrlManager|URL manager]]\nlooks for the first rule that matches the given route and parameters and uses that to create a URL.\n\nYou can configure [[yii\\web\\UrlManager::rules]] as an array with keys being the [[yii\\web\\UrlRule::$pattern|patterns]] and values the corresponding\n[[yii\\web\\UrlRule::$route|routes]]. Each pattern-route pair constructs a URL rule. For example, the following [[yii\\web\\UrlManager::rules|rules]]\nconfiguration declares two URL rules. The first rule matches a URL `posts` and maps it into the route `post/index`.\nThe second rule matches a URL matching the regular expression `post/(\\d+)` and maps it into the route `post/view` and\ndefines a query parameter named `id`.\n\n```php\n'rules' => [\n    'posts' => 'post/index',\n    'post/<id:\\d+>' => 'post/view',\n]\n```\n\n> Info: The pattern in a rule is used to match the path info part of a URL. For example, the path info of\n  `/index.php/post/100?source=ad` is `post/100` (the leading and ending slashes are ignored) which matches\n  the pattern `post/(\\d+)`.\n\nBesides declaring URL rules as pattern-route pairs, you may also declare them as configuration arrays. Each configuration\narray is used to configure a single URL rule object. This is often needed when you want to configure other\nproperties of a URL rule. For example,\n\n```php\n'rules' => [\n    // ...other url rules...\n    [\n        'pattern' => 'posts',\n        'route' => 'post/index',\n        'suffix' => '.json',\n    ],\n]\n```\n\nBy default if you do not specify the `class` option for a rule configuration, it will take the default\nclass [[yii\\web\\UrlRule]], which is the default value defined in\n[[yii\\web\\UrlManager::$ruleConfig]].\n\n\n### Named Parameters <span id=\"named-parameters\"></span>\n\nA URL rule can be associated with named query parameters which are specified in the pattern in the format\nof `<ParamName:RegExp>`, where `ParamName` specifies the parameter name and `RegExp` is an optional regular\nexpression used to match parameter values. If `RegExp` is not specified, it means the parameter value should be\na string without any slash.\n\n> Note: You can only use regular expressions inside of parameters. The rest of a pattern is considered plain text.\n\nWhen a rule is used to parse a URL, it will fill the associated parameters with values matching the corresponding\nparts of the URL, and these parameters will be made available in `$_GET` later by the `request` application component.\nWhen the rule is used to create a URL, it will take the values of the provided parameters and insert them at the\nplaces where the parameters are declared.\n\nLet's use some examples to illustrate how named parameters work. Assume we have declared the following three URL rules:\n\n```php\n'rules' => [\n    'posts/<year:\\d{4}>/<category>' => 'post/index',\n    'posts' => 'post/index',\n    'post/<id:\\d+>' => 'post/view',\n]\n```\n\nWhen the rules are used to parse URLs:\n\n- `/index.php/posts` is parsed into the route `post/index` using the second rule;\n- `/index.php/posts/2014/php` is parsed into the route `post/index`, the `year` parameter whose value is 2014\n  and the `category` parameter whose value is `php` using the first rule;\n- `/index.php/post/100` is parsed into the route `post/view` and the `id` parameter whose value is 100 using\n  the third rule;\n- `/index.php/posts/php` will cause a [[yii\\web\\NotFoundHttpException]] when [[yii\\web\\UrlManager::enableStrictParsing]]\n  is `true`, because it matches none of the patterns. If [[yii\\web\\UrlManager::enableStrictParsing]] is `false` (the\n  default value), the path info part `posts/php` will be returned as the route. This will either execute the corresponding action if it exists or throw a [[yii\\web\\NotFoundHttpException]] otherwise.\n\nAnd when the rules are used to create URLs:\n\n- `Url::to(['post/index'])` creates `/index.php/posts` using the second rule;\n- `Url::to(['post/index', 'year' => 2014, 'category' => 'php'])` creates `/index.php/posts/2014/php` using the first rule;\n- `Url::to(['post/view', 'id' => 100])` creates `/index.php/post/100` using the third rule;\n- `Url::to(['post/view', 'id' => 100, 'source' => 'ad'])` creates `/index.php/post/100?source=ad` using the third rule.\n  Because the `source` parameter is not specified in the rule, it is appended as a query parameter in the created URL.\n- `Url::to(['post/index', 'category' => 'php'])` creates `/index.php/post/index?category=php` using none of the rules.\n  Note that since none of the rules applies, the URL is created by simply appending the route as the path info\n  and all parameters as the query string part.\n\n\n### Parameterizing Routes <span id=\"parameterizing-routes\"></span>\n\nYou can embed parameter names in the route of a URL rule. This allows a URL rule to be used for matching multiple\nroutes. For example, the following rules embed `controller` and `action` parameters in the routes.\n\n```php\n'rules' => [\n    '<controller:(post|comment)>/create' => '<controller>/create',\n    '<controller:(post|comment)>/<id:\\d+>/<action:(update|delete)>' => '<controller>/<action>',\n    '<controller:(post|comment)>/<id:\\d+>' => '<controller>/view',\n    '<controller:(post|comment)>s' => '<controller>/index',\n]\n```\n\nTo parse a URL `/index.php/comment/100/update`, the second rule will apply, which sets the `controller` parameter to\nbe `comment` and `action` parameter to be `update`. The route `<controller>/<action>` is thus resolved as `comment/update`.\n\nSimilarly, to create a URL for the route `comment/index`, the last rule will apply, which creates a URL `/index.php/comments`.\n\n> Info: By parameterizing routes, it is possible to greatly reduce the number of URL rules, which can significantly\n  improve the performance of [[yii\\web\\UrlManager|URL manager]].\n\n### Default Parameter Values <span id=\"default-parameter-values\"></span>\n\nBy default, all parameters declared in a rule are required. If a requested URL does not contain a particular parameter,\nor if a URL is being created without a particular parameter, the rule will not apply. To make some of the parameters\noptional, you can configure the [[yii\\web\\UrlRule::defaults|defaults]] property of a rule. Parameters listed in this\nproperty are optional and will take the specified values when they are not provided.\n\nIn the following rule declaration, the `page` and `tag` parameters are both optional and will take the value of 1 and\nempty string, respectively, when they are not provided.\n\n```php\n'rules' => [\n    // ...other rules...\n    [\n        'pattern' => 'posts/<page:\\d+>/<tag>',\n        'route' => 'post/index',\n        'defaults' => ['page' => 1, 'tag' => ''],\n    ],\n]\n```\n\nThe above rule can be used to parse or create any of the following URLs:\n\n* `/index.php/posts`: `page` is 1, `tag` is ''.\n* `/index.php/posts/2`: `page` is 2, `tag` is ''.\n* `/index.php/posts/2/news`: `page` is 2, `tag` is `'news'`.\n* `/index.php/posts/news`: `page` is 1, `tag` is `'news'`.\n\nWithout using optional parameters, you would have to create 4 rules to achieve the same result.\n\n> Note: If [[yii\\web\\UrlRule::$pattern|pattern]] contains only optional parameters and slashes, first parameter could be omitted \n  only if all other parameters are omitted.\n\n\n### Rules with Server Names <span id=\"rules-with-server-names\"></span>\n\nIt is possible to include Web server names in the patterns of URL rules. This is mainly useful when your application\nshould behave differently for different Web server names. For example, the following rules will parse the URL\n`https://admin.example.com/login` into the route `admin/user/login` and `https://www.example.com/login` into `site/login`.\n\n```php\n'rules' => [\n    'https://admin.example.com/login' => 'admin/user/login',\n    'https://www.example.com/login' => 'site/login',\n]\n```\n\nYou can also embed parameters in the server names to extract dynamic information from them. For example, the following rule\nwill parse the URL `https://en.example.com/posts` into the route `post/index` and the parameter `language=en`.\n\n```php\n'rules' => [\n    'https://<language:\\w+>.example.com/posts' => 'post/index',\n]\n```\n\nSince version 2.0.11, you may also use protocol relative patterns that work for both, `http` and `https`.\nThe syntax is the same as above but skipping the `http:` part, e.g.: `'//www.example.com/login' => 'site/login'`.\n\n> Note: Rules with server names should **not** include the subfolder of the entry script in their patterns. For example, if the applications entry script is at `https://www.example.com/sandbox/blog/index.php`, then you should use the pattern\n  `https://www.example.com/posts` instead of `https://www.example.com/sandbox/blog/posts`. This will allow your application\n  to be deployed under any directory without the need to change your url rules. Yii will automatically detect the base url of the application.\n\n\n### URL Suffixes <span id=\"url-suffixes\"></span>\n\nYou may want to add suffixes to the URLs for various purposes. For example, you may add `.html` to the URLs so that they\nlook like URLs for static HTML pages; you may also add `.json` to the URLs to indicate the expected content type\nof the response. You can achieve this goal by configuring the [[yii\\web\\UrlManager::suffix]] property like\nthe following in the application configuration:\n\n```php\n[\n    // ...\n    'components' => [\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            // ...\n            'suffix' => '.html',\n            'rules' => [\n                // ...\n            ],\n        ],\n    ],\n]\n```\n\nThe above configuration will allow the [[yii\\web\\UrlManager|URL manager]] to recognize requested URLs and also create\nURLs with `.html` as their suffix.\n\n> Tip: You may set `/` as the URL suffix so that the URLs all end with a slash.\n\n> Note: When you configure a URL suffix, if a requested URL does not have the suffix, it will be considered as\n  an unrecognized URL. This is a recommended practice for SEO (search engine optimization) to avoid duplicate content on different URLs.\n\nSometimes you may want to use different suffixes for different URLs. This can be achieved by configuring the\n[[yii\\web\\UrlRule::suffix|suffix]] property of individual URL rules. When a URL rule has this property set, it will\noverride the suffix setting at the [[yii\\web\\UrlManager|URL manager]] level. For example, the following configuration\ncontains a customized URL rule which uses `.json` as its suffix instead of the global `.html` suffix.\n\n```php\n[\n    'components' => [\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            // ...\n            'suffix' => '.html',\n            'rules' => [\n                // ...\n                [\n                    'pattern' => 'posts',\n                    'route' => 'post/index',\n                    'suffix' => '.json',\n                ],\n            ],\n        ],\n    ],\n]\n```\n\n### HTTP Methods <span id=\"http-methods\"></span>\n\nWhen implementing RESTful APIs, it is commonly needed that the same URL be parsed into different routes according to\nthe HTTP methods being used. This can be easily achieved by prefixing the supported HTTP methods to the patterns of\nthe rules. If a rule supports multiple HTTP methods, separate the method names with commas. For example, the following\nrules have the same pattern `post/<id:\\d+>` with different HTTP method support. A request for `PUT post/100` will\nbe parsed into `post/update`, while a request for `GET post/100` will be parsed into `post/view`.\n\n```php\n'rules' => [\n    'PUT,POST post/<id:\\d+>' => 'post/update',\n    'DELETE post/<id:\\d+>' => 'post/delete',\n    'post/<id:\\d+>' => 'post/view',\n]\n```\n\n> Note: If a URL rule contains HTTP method(s) in its pattern, the rule will only be used for parsing purpose unless `GET` is among the specified verbs.\n  It will be skipped when the [[yii\\web\\UrlManager|URL manager]] is called to create URLs.\n\n> Tip: To simplify the routing of RESTful APIs, Yii provides a special URL rule class [[yii\\rest\\UrlRule]]\n  which is very efficient and supports some fancy features such as automatic pluralization of controller IDs.\n  For more details, please refer to the [Routing](rest-routing.md) section in the RESTful APIs chapter.\n\n\n### Adding Rules Dynamically <span id=\"adding-rules\"></span>\n\nURL rules can be dynamically added to the [[yii\\web\\UrlManager|URL manager]]. This is often needed by redistributable\n[modules](structure-modules.md) which want to manage their own URL rules. In order for the dynamically added rules\nto take effect during the routing process, you should add them during the [bootstrapping](runtime-bootstrapping.md)\nstage of the application. For modules, this means they should implement [[yii\\base\\BootstrapInterface]] and add the rules in the\n[[yii\\base\\BootstrapInterface::bootstrap()|bootstrap()]] method like the following:\n\n```php\npublic function bootstrap($app)\n{\n    $app->getUrlManager()->addRules([\n        // rule declarations here\n    ], false);\n}\n```\n\nNote that you should also list these modules in [[yii\\web\\Application::bootstrap]] so that they can participate the\n[bootstrapping](runtime-bootstrapping.md) process.\n\n\n### Creating Rule Classes <span id=\"creating-rules\"></span>\n\nDespite the fact that the default [[yii\\web\\UrlRule]] class is flexible enough for the majority of projects, there\nare situations when you have to create your own rule classes. For example, in a car dealer Web site, you may want\nto support the URL format like `/Manufacturer/Model`, where both `Manufacturer` and `Model` must match some data\nstored in a database table. The default rule class will not work here because it relies on statically declared patterns.\n\nWe can create the following URL rule class to solve this problem.\n\n```php\n<?php\n\nnamespace app\\components;\n\nuse yii\\web\\UrlRuleInterface;\nuse yii\\base\\BaseObject;\n\nclass CarUrlRule extends BaseObject implements UrlRuleInterface\n{\n    public function createUrl($manager, $route, $params)\n    {\n        if ($route === 'car/index') {\n            if (isset($params['manufacturer'], $params['model'])) {\n                return $params['manufacturer'] . '/' . $params['model'];\n            } elseif (isset($params['manufacturer'])) {\n                return $params['manufacturer'];\n            }\n        }\n        return false; // this rule does not apply\n    }\n\n    public function parseRequest($manager, $request)\n    {\n        $pathInfo = $request->getPathInfo();\n        if (preg_match('%^(\\w+)(/(\\w+))?$%', $pathInfo, $matches)) {\n            // check $matches[1] and $matches[3] to see\n            // if they match a manufacturer and a model in the database.\n            // If so, set $params['manufacturer'] and/or $params['model']\n            // and return ['car/index', $params]\n        }\n        return false; // this rule does not apply\n    }\n}\n```\n\nAnd use the new rule class in the [[yii\\web\\UrlManager::rules]] configuration:\n\n```php\n'rules' => [\n    // ...other rules...\n    [\n        'class' => 'app\\components\\CarUrlRule',\n        // ...configure other properties...\n    ],\n]\n```\n\n\n## URL normalization <span id=\"url-normalization\"></span>\n\nSince version 2.0.10 [[yii\\web\\UrlManager|UrlManager]] can be configured to use [[yii\\web\\UrlNormalizer|UrlNormalizer]] for dealing\nwith variations of the same URL, for example with and without a trailing slash. Because technically `https://example.com/path`\nand `https://example.com/path/` are different URLs, serving the same content for both of them can degrade SEO ranking.\nBy default normalizer collapses consecutive slashes, adds or removes trailing slashes depending on whether the\nsuffix has a trailing slash or not, and redirects to the normalized version of the URL using [permanent redirection](https://en.wikipedia.org/wiki/HTTP_301).\nThe normalizer can be configured globally for the URL manager or individually for each rule - by default each rule will use the normalizer\nfrom URL manager. You can set [[yii\\web\\UrlRule::$normalizer|UrlRule::$normalizer]] to `false` to disable normalization\nfor particular URL rule.\n\nThe following shows an example configuration for the [[yii\\web\\UrlNormalizer|UrlNormalizer]]:\n\n```php\n'urlManager' => [\n    'enablePrettyUrl' => true,\n    'showScriptName' => false,\n    'enableStrictParsing' => true,\n    'suffix' => '.html',\n    'normalizer' => [\n        'class' => 'yii\\web\\UrlNormalizer',\n        // use temporary redirection instead of permanent for debugging\n        'action' => UrlNormalizer::ACTION_REDIRECT_TEMPORARY,\n    ],\n    'rules' => [\n        // ...other rules...\n        [\n            'pattern' => 'posts',\n            'route' => 'post/index',\n            'suffix' => '/',\n            'normalizer' => false, // disable normalizer for this rule\n        ],\n        [\n            'pattern' => 'tags',\n            'route' => 'tag/index',\n            'normalizer' => [\n                // do not collapse consecutive slashes for this rule\n                'collapseSlashes' => false,\n            ],\n        ],\n    ],\n]\n```\n\n> Note: by default [[yii\\web\\UrlManager::$normalizer|UrlManager::$normalizer]] is disabled. You need to explicitly\n  configure it in order to enable URL normalization.\n\n\n\n## Performance Considerations <span id=\"performance-consideration\"></span>\n\nWhen developing a complex Web application, it is important to optimize URL rules so that it takes less time to parse\nrequests and create URLs.\n\nBy using parameterized routes, you may reduce the number of URL rules, which can significantly improve performance.\n\nWhen parsing or creating URLs, [[yii\\web\\UrlManager|URL manager]] examines URL rules in the order they are declared.\nTherefore, you may consider adjusting the order of the URL rules so that more specific and/or more commonly used rules are placed before less used ones.\n\nIf some URL rules share the same prefix in their patterns or routes, you may consider using [[yii\\web\\GroupUrlRule]]\nso that they can be more efficiently examined by [[yii\\web\\UrlManager|URL manager]] as a group. This is often the case\nwhen your application is composed by modules, each having its own set of URL rules with module ID as their common prefixes.\n"
  },
  {
    "path": "docs/guide/runtime-sessions-cookies.md",
    "content": "Sessions and Cookies\n====================\n\nSessions and cookies allow data to be persisted across multiple user requests. In plain PHP you may access them\nthrough the global variables `$_SESSION` and `$_COOKIE`, respectively. Yii encapsulates sessions and cookies as objects\nand thus allows you to access them in an object-oriented fashion with additional useful enhancements.\n\n\n## Sessions <span id=\"sessions\"></span>\n\nLike [requests](runtime-requests.md) and [responses](runtime-responses.md), you can get access to sessions via\nthe `session` [application component](structure-application-components.md) which is an instance of [[yii\\web\\Session]],\nby default.\n\n\n### Opening and Closing Sessions <span id=\"opening-closing-sessions\"></span>\n\nTo open and close a session, you can do the following:\n\n```php\n$session = Yii::$app->session;\n\n// check if a session is already open\nif ($session->isActive) ...\n\n// open a session\n$session->open();\n\n// close a session\n$session->close();\n\n// destroys all data registered to a session.\n$session->destroy();\n```\n\nYou can call [[yii\\web\\Session::open()|open()]] and [[yii\\web\\Session::close()|close()]] multiple times\nwithout causing errors; internally the methods will first check if the session is already open.\n\n\n### Accessing Session Data <span id=\"access-session-data\"></span>\n\nTo access the data stored in session, you can do the following:\n\n```php\n$session = Yii::$app->session;\n\n// get a session variable. The following usages are equivalent:\n$language = $session->get('language');\n$language = $session['language'];\n$language = isset($_SESSION['language']) ? $_SESSION['language'] : null;\n\n// set a session variable. The following usages are equivalent:\n$session->set('language', 'en-US');\n$session['language'] = 'en-US';\n$_SESSION['language'] = 'en-US';\n\n// remove a session variable. The following usages are equivalent:\n$session->remove('language');\nunset($session['language']);\nunset($_SESSION['language']);\n\n// check if a session variable exists. The following usages are equivalent:\nif ($session->has('language')) ...\nif (isset($session['language'])) ...\nif (isset($_SESSION['language'])) ...\n\n// traverse all session variables. The following usages are equivalent:\nforeach ($session as $name => $value) ...\nforeach ($_SESSION as $name => $value) ...\n```\n\n> Info: When you access session data through the `session` component, a session will be automatically opened\nif it has not been done so before. This is different from accessing session data through `$_SESSION`, which requires\nan explicit call of `session_start()`.\n\nWhen working with session data that are arrays, the `session` component has a limitation which prevents you from\ndirectly modifying an array element. For example,\n\n```php\n$session = Yii::$app->session;\n\n// the following code will NOT work\n$session['captcha']['number'] = 5;\n$session['captcha']['lifetime'] = 3600;\n\n// the following code works:\n$session['captcha'] = [\n    'number' => 5,\n    'lifetime' => 3600,\n];\n\n// the following code also works:\necho $session['captcha']['lifetime'];\n```\n\nYou can use one of the following workarounds to solve this problem:\n\n```php\n$session = Yii::$app->session;\n\n// directly use $_SESSION (make sure Yii::$app->session->open() has been called)\n$_SESSION['captcha']['number'] = 5;\n$_SESSION['captcha']['lifetime'] = 3600;\n\n// get the whole array first, modify it and then save it back\n$captcha = $session['captcha'];\n$captcha['number'] = 5;\n$captcha['lifetime'] = 3600;\n$session['captcha'] = $captcha;\n\n// use ArrayObject instead of array\n$session['captcha'] = new \\ArrayObject;\n...\n$session['captcha']['number'] = 5;\n$session['captcha']['lifetime'] = 3600;\n\n// store array data by keys with a common prefix\n$session['captcha.number'] = 5;\n$session['captcha.lifetime'] = 3600;\n```\n\nFor better performance and code readability, we recommend the last workaround. That is, instead of storing\nan array as a single session variable, you store each array element as a session variable which shares the same\nkey prefix with other array elements.\n\n\n### Custom Session Storage <span id=\"custom-session-storage\"></span>\n\nThe default [[yii\\web\\Session]] class stores session data as files on the server. Yii also provides the following\nsession classes implementing different session storage:\n\n* [[yii\\web\\DbSession]]: stores session data in a database table.\n* [[yii\\web\\CacheSession]]: stores session data in a cache with the help of a configured [cache component](caching-data.md#cache-components).\n* [[yii\\redis\\Session]]: stores session data using [redis](https://redis.io/) as the storage medium.\n* [[yii\\mongodb\\Session]]: stores session data in a [MongoDB](https://www.mongodb.com/).\n\nAll these session classes support the same set of API methods. As a result, you can switch to a different\nsession storage class without the need to modify your application code that uses sessions.\n\n> Note: If you want to access session data via `$_SESSION` while using custom session storage, you must make\n  sure that the session has already been started by [[yii\\web\\Session::open()]]. This is because custom session storage\n  handlers are registered within this method.\n\n> Note: If you use a custom session storage you may need to configure the session garbage collector explicitly.\n  Some installations of PHP (e.g. Debian) use a garbage collector probability of 0 and clean session files\n  offline in a cronjob. This process does not apply to your custom storage so you need to configure\n  [[yii\\web\\Session::$GCProbability]] to use a non-zero value.\n\nTo learn how to configure and use these component classes, please refer to their API documentation. Below is\nan example showing how to configure [[yii\\web\\DbSession]] in the application configuration to use a database table\nfor session storage:\n\n```php\nreturn [\n    'components' => [\n        'session' => [\n            'class' => 'yii\\web\\DbSession',\n            // 'db' => 'mydb',  // the application component ID of the DB connection. Defaults to 'db'.\n            // 'sessionTable' => 'my_session', // session table name. Defaults to 'session'.\n        ],\n    ],\n];\n```\n\nYou also need to create the following database table to store session data:\n\n```sql\nCREATE TABLE session\n(\n    id CHAR(40) NOT NULL PRIMARY KEY,\n    expire INTEGER,\n    data BLOB\n)\n```\n\nwhere 'BLOB' refers to the BLOB-type of your preferred DBMS. Below are the BLOB types that can be used for some popular DBMS:\n\n- MySQL: LONGBLOB\n- PostgreSQL: BYTEA\n- MSSQL: BLOB\n\n> Note: According to the php.ini setting of `session.hash_function`, you may need to adjust\n  the length of the `id` column. For example, if `session.hash_function=sha256`, you should use a\n  length 64 instead of 40.\n\nAlternatively, this can be accomplished with the following migration:\n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m170529_050554_create_table_session extends Migration\n{\n    public function up()\n    {\n        $this->createTable('{{%session}}', [\n            'id' => $this->char(64)->notNull(),\n            'expire' => $this->integer(),\n            'data' => $this->binary()\n        ]);\n        $this->addPrimaryKey('pk-id', '{{%session}}', 'id');\n    }\n\n    public function down()\n    {\n        $this->dropTable('{{%session}}');\n    }\n}\n```\n\n### Flash Data <span id=\"flash-data\"></span>\n\nFlash data is a special kind of session data which, once set in one request, will only be available during\nthe next request and will be automatically deleted afterwards. Flash data is most commonly used to implement\nmessages that should only be displayed to end users once, such as a confirmation message displayed after\na user successfully submits a form.\n\nYou can set and access flash data through the `session` application component. For example,\n\n```php\n$session = Yii::$app->session;\n\n// Request #1\n// set a flash message named as \"postDeleted\"\n$session->setFlash('postDeleted', 'You have successfully deleted your post.');\n\n// Request #2\n// display the flash message named \"postDeleted\"\necho $session->getFlash('postDeleted');\n\n// Request #3\n// $result will be false since the flash message was automatically deleted\n$result = $session->hasFlash('postDeleted');\n```\n\nLike regular session data, you can store arbitrary data as flash data.\n\nWhen you call [[yii\\web\\Session::setFlash()]], it will overwrite any existing flash data that has the same name.\nTo append new flash data to an existing message of the same name, you may call [[yii\\web\\Session::addFlash()]] instead.\nFor example:\n\n```php\n$session = Yii::$app->session;\n\n// Request #1\n// add a few flash messages under the name of \"alerts\"\n$session->addFlash('alerts', 'You have successfully deleted your post.');\n$session->addFlash('alerts', 'You have successfully added a new friend.');\n$session->addFlash('alerts', 'You are promoted.');\n\n// Request #2\n// $alerts is an array of the flash messages under the name of \"alerts\"\n$alerts = $session->getFlash('alerts');\n```\n\n> Note: Try not to use [[yii\\web\\Session::setFlash()]] together with [[yii\\web\\Session::addFlash()]] for flash data\n  of the same name. This is because the latter method will automatically turn the flash data into an array so that it\n  can append new flash data of the same name. As a result, when you call [[yii\\web\\Session::getFlash()]], you may\n  find sometimes you are getting an array while sometimes you are getting a string, depending on the order of\n  the invocation of these two methods.\n\n> Tip: For displaying Flash messages you can use [[yii\\bootstrap\\Alert|bootstrap Alert]] widget in the following way:\n>\n> ```php\n> echo Alert::widget([\n>    'options' => ['class' => 'alert-info'],\n>    'body' => Yii::$app->session->getFlash('postDeleted'),\n> ]);\n> ```\n\n\n## Cookies <span id=\"cookies\"></span>\n\nYii represents each cookie as an object of [[yii\\web\\Cookie]]. Both [[yii\\web\\Request]] and [[yii\\web\\Response]]\nmaintain a collection of cookies via the property named `cookies`. The cookie collection in the former represents\nthe cookies submitted in a request, while the cookie collection in the latter represents the cookies that are to\nbe sent to the user.\n\nThe part of the application dealing with request and response directly is controller. Therefore, cookies should be\nread and sent in controller.\n\n### Reading Cookies <span id=\"reading-cookies\"></span>\n\nYou can get the cookies in the current request using the following code:\n\n```php\n// get the cookie collection (yii\\web\\CookieCollection) from the \"request\" component\n$cookies = Yii::$app->request->cookies;\n\n// get the \"language\" cookie value. If the cookie does not exist, return \"en\" as the default value.\n$language = $cookies->getValue('language', 'en');\n\n// an alternative way of getting the \"language\" cookie value\nif (($cookie = $cookies->get('language')) !== null) {\n    $language = $cookie->value;\n}\n\n// you may also use $cookies like an array\nif (isset($cookies['language'])) {\n    $language = $cookies['language']->value;\n}\n\n// check if there is a \"language\" cookie\nif ($cookies->has('language')) ...\nif (isset($cookies['language'])) ...\n```\n\n\n### Sending Cookies <span id=\"sending-cookies\"></span>\n\nYou can send cookies to end users using the following code:\n\n```php\n// get the cookie collection (yii\\web\\CookieCollection) from the \"response\" component\n$cookies = Yii::$app->response->cookies;\n\n// add a new cookie to the response to be sent\n$cookies->add(new \\yii\\web\\Cookie([\n    'name' => 'language',\n    'value' => 'zh-CN',\n]));\n\n// remove a cookie\n$cookies->remove('language');\n// equivalent to the following\nunset($cookies['language']);\n```\n\nBesides the [[yii\\web\\Cookie::name|name]], [[yii\\web\\Cookie::value|value]] properties shown in the above\nexamples, the [[yii\\web\\Cookie]] class also defines other properties to fully represent all available cookie \ninformation, such as [[yii\\web\\Cookie::domain|domain]], [[yii\\web\\Cookie::expire|expire]]. You may configure these\nproperties as needed to prepare a cookie and then add it to the response's cookie collection.\n\n### Cookie Validation <span id=\"cookie-validation\"></span>\n\nWhen you are reading and sending cookies through the `request` and `response` components as shown in the last\ntwo subsections, you enjoy the added security of cookie validation which protects cookies from being modified\non the client-side. This is achieved by signing each cookie with a hash string, which allows the application to\ntell if a cookie has been modified on the client-side. If so, the cookie will NOT be accessible through the\n[[yii\\web\\Request::cookies|cookie collection]] of the `request` component.\n\n> Note: Cookie validation only protects cookie values from being modified. If a cookie fails the validation, \nyou may still access it through `$_COOKIE`. This is because third-party libraries may manipulate cookies \nin their own way, which does not involve cookie validation.\n\nCookie validation is enabled by default. You can disable it by setting the [[yii\\web\\Request::enableCookieValidation]]\nproperty to be `false`, although we strongly recommend you do not do so.\n\n> Note: Cookies that are directly read/sent via `$_COOKIE` and `setcookie()` will NOT be validated.\n\nWhen using cookie validation, you must specify a [[yii\\web\\Request::cookieValidationKey]] that will be used to generate\nthe aforementioned hash strings. You can do so by configuring the `request` component in the application configuration:\n\n```php\nreturn [\n    'components' => [\n        'request' => [\n            'cookieValidationKey' => 'fill in a secret key here',\n        ],\n    ],\n];\n```\n\n> Info: [[yii\\web\\Request::cookieValidationKey|cookieValidationKey]] is critical to your application's security.\n  It should only be known to people you trust. Do not store it in the version control system.\n  \n## Security settings\n\nBoth [[yii\\web\\Cookie]] and [[yii\\web\\Session]] support the following security flags:\n\n### httpOnly\n\nFor better security, the default value of [[yii\\web\\Cookie::httpOnly]] and the 'httponly' parameter of \n[[yii\\web\\Session::cookieParams]] is set to `true`. This helps mitigate the risk of a client-side script accessing \nthe protected cookie (if the browser supports it).\nYou may read the [HttpOnly wiki article](https://owasp.org/www-community/HttpOnly) for more details.\n\n### secure\n\nThe purpose of the secure flag is to prevent cookies from being sent in clear text. If the browser supports the\nsecure flag it will only include the cookie when the request is sent over a secure (TLS) connection.\nYou may read the [SecureFlag wiki article](https://owasp.org/www-community/controls/SecureCookieAttribute) for more details.\n\n### sameSite\n\nStarting with Yii 2.0.21 the [[yii\\web\\Cookie::sameSite]] setting is supported. It requires PHP version 7.4.0 or higher.\nThe purpose of the `sameSite` setting is to prevent CSRF (Cross-Site Request Forgery) attacks.\nIf the browser supports the `sameSite` setting it will only include the cookie according to the specified policy ('Lax' or 'Strict').\nYou may read the [SameSite wiki article](https://owasp.org/www-community/SameSite) for more details.\nFor better security, an exception will be thrown if `sameSite` is used with an unsupported version of PHP.\nTo use this feature across different PHP versions check the version first. E.g.\n\n```php\n[\n    'sameSite' => PHP_VERSION_ID >= 70300 ? yii\\web\\Cookie::SAME_SITE_LAX : null,\n]\n```\n\n> Note: Since not all browsers support the `sameSite` setting yet, it is still strongly recommended to also include\n  [additional CSRF protection](security-best-practices.md#avoiding-csrf).\n\n## Session php.ini settings\n\nAs [noted in PHP manual](https://www.php.net/manual/en/session.security.ini.php), `php.ini` has important\nsession security settings. Please ensure recommended settings are applied. Especially `session.use_strict_mode`\nthat is not enabled by default in PHP installations.\nThis setting can also be set with [[yii\\web\\Session::useStrictMode]].\n"
  },
  {
    "path": "docs/guide/security-authentication.md",
    "content": "Authentication\n==============\n\nAuthentication is the process of verifying the identity of a user. It usually uses an identifier\n(e.g. a username or an email address) and a secret token (e.g. a password or an access token) to judge\nif the user is the one whom he claims as. Authentication is the basis of the login feature.\n\nYii provides an authentication framework which wires up various components to support login. To use this framework,\nyou mainly need to do the following work:\n\n* Configure the [[yii\\web\\User|user]] application component;\n* Create a class that implements the [[yii\\web\\IdentityInterface]] interface.\n\n\n## Configuring [[yii\\web\\User]] <span id=\"configuring-user\"></span>\n\nThe [[yii\\web\\User|user]] application component manages the user authentication status. It requires you to\nspecify an [[yii\\web\\User::identityClass|identity class]] which contains the actual authentication logic.\nIn the following application configuration, the [[yii\\web\\User::identityClass|identity class]] for\n[[yii\\web\\User|user]] is configured to be `app\\models\\User` whose implementation is explained in\nthe next subsection:\n\n```php\nreturn [\n    'components' => [\n        'user' => [\n            'identityClass' => 'app\\models\\User',\n        ],\n    ],\n];\n```\n\n\n## Implementing [[yii\\web\\IdentityInterface]] <span id=\"implementing-identity\"></span>\n\nThe [[yii\\web\\User::identityClass|identity class]] must implement the [[yii\\web\\IdentityInterface]] which contains\nthe following methods:\n\n* [[yii\\web\\IdentityInterface::findIdentity()|findIdentity()]]: it looks for an instance of the identity\n  class using the specified user ID. This method is used when you need to maintain the login status via session.\n* [[yii\\web\\IdentityInterface::findIdentityByAccessToken()|findIdentityByAccessToken()]]: it looks for\n  an instance of the identity class using the specified access token. This method is used when you need\n  to authenticate a user by a single secret token (e.g. in a stateless RESTful application).\n* [[yii\\web\\IdentityInterface::getId()|getId()]]: it returns the ID of the user represented by this identity instance.\n* [[yii\\web\\IdentityInterface::getAuthKey()|getAuthKey()]]: it returns a key used to validate session and auto-login in\n  case it is enabled.\n* [[yii\\web\\IdentityInterface::validateAuthKey()|validateAuthKey()]]: it implements the logic for verifying\n  authentication key.\n\nIf a particular method is not needed, you may implement it with an empty body. For example, if your application\nis a pure stateless RESTful application, you would only need to implement [[yii\\web\\IdentityInterface::findIdentityByAccessToken()|findIdentityByAccessToken()]]\nand [[yii\\web\\IdentityInterface::getId()|getId()]] while leaving all other methods with an empty body. Or if your \napplication uses session only authentication, you would need to implement all the methods except\n[[yii\\web\\IdentityInterface::findIdentityByAccessToken()|findIdentityByAccessToken()]].\n\nIn the following example, an [[yii\\web\\User::identityClass|identity class]] is implemented as\nan [Active Record](db-active-record.md) class associated with the `user` database table.\n\n```php\n<?php\n\nuse yii\\db\\ActiveRecord;\nuse yii\\web\\IdentityInterface;\n\nclass User extends ActiveRecord implements IdentityInterface\n{\n    public static function tableName()\n    {\n        return 'user';\n    }\n\n    /**\n     * Finds an identity by the given ID.\n     *\n     * @param string|int $id the ID to be looked for\n     * @return IdentityInterface|null the identity object that matches the given ID.\n     */\n    public static function findIdentity($id)\n    {\n        return static::findOne($id);\n    }\n\n    /**\n     * Finds an identity by the given token.\n     *\n     * @param string $token the token to be looked for\n     * @return IdentityInterface|null the identity object that matches the given token.\n     */\n    public static function findIdentityByAccessToken($token, $type = null)\n    {\n        return static::findOne(['access_token' => $token]);\n    }\n\n    /**\n     * @return int|string current user ID\n     */\n    public function getId()\n    {\n        return $this->id;\n    }\n\n    /**\n     * @return string|null current user auth key\n     */\n    public function getAuthKey()\n    {\n        return $this->auth_key;\n    }\n\n    /**\n     * @param string $authKey\n     * @return bool|null if auth key is valid for current user\n     */\n    public function validateAuthKey($authKey)\n    {\n        return $this->getAuthKey() === $authKey;\n    }\n}\n```\n\nYou may use the following code to generate an auth key for each\nuser and then store it in the `user` table:\n\n```php\nclass User extends ActiveRecord implements IdentityInterface\n{\n    ......\n\n    public function beforeSave($insert)\n    {\n        if (parent::beforeSave($insert)) {\n            if ($this->isNewRecord) {\n                $this->auth_key = \\Yii::$app->security->generateRandomString();\n            }\n            return true;\n        }\n        return false;\n    }\n}\n```\n\n> Note: Do not confuse the `User` identity class with [[yii\\web\\User]]. The former is the class implementing\n  the authentication logic. It is often implemented as an [Active Record](db-active-record.md) class associated\n  with some persistent storage for storing the user credential information. The latter is an application component\n  class responsible for managing the user authentication state.\n\n\n## Using [[yii\\web\\User]] <span id=\"using-user\"></span>\n\nYou mainly use [[yii\\web\\User]] in terms of the `user` application component.\n\nYou can detect the identity of the current user using the expression `Yii::$app->user->identity`. It returns\nan instance of the [[yii\\web\\User::identityClass|identity class]] representing the currently logged-in user,\nor `null` if the current user is not authenticated (meaning a guest). The following code shows how to retrieve\nother authentication-related information from [[yii\\web\\User]]:\n\n```php\n// the current user identity. `null` if the user is not authenticated.\n$identity = Yii::$app->user->identity;\n\n// the ID of the current user. `null` if the user not authenticated.\n$id = Yii::$app->user->id;\n\n// whether the current user is a guest (not authenticated)\n$isGuest = Yii::$app->user->isGuest;\n```\n\nTo login a user, you may use the following code:\n\n```php\n// find a user identity with the specified username.\n// note that you may want to check the password if needed\n$identity = User::findOne(['username' => $username]);\n\n// logs in the user\nYii::$app->user->login($identity);\n```\n\nThe [[yii\\web\\User::login()]] method sets the identity of the current user to the [[yii\\web\\User]]. If session is\n[[yii\\web\\User::enableSession|enabled]], it will keep the identity in the session so that the user\nauthentication status is maintained throughout the whole session. If cookie-based login (i.e. \"remember me\" login)\nis [[yii\\web\\User::enableAutoLogin|enabled]], it will also save the identity in a cookie so that\nthe user authentication status can be recovered from the cookie as long as the cookie remains valid.\n\nIn order to enable cookie-based login, you need to configure [[yii\\web\\User::enableAutoLogin]] to be\n`true` in the application configuration. You also need to provide a duration time parameter when calling\nthe [[yii\\web\\User::login()]] method.\n\nTo logout a user, simply call\n\n```php\nYii::$app->user->logout();\n```\n\nNote that logging out a user is only meaningful when session is enabled. The method will clean up\nthe user authentication status from both memory and session. And by default, it will also destroy *all*\nuser session data. If you want to keep the session data, you should call `Yii::$app->user->logout(false)`, instead.\n\n\n## Authentication Events <span id=\"auth-events\"></span>\n\nThe [[yii\\web\\User]] class raises a few events during the login and logout processes.\n\n* [[yii\\web\\User::EVENT_BEFORE_LOGIN|EVENT_BEFORE_LOGIN]]: raised at the beginning of [[yii\\web\\User::login()]].\n  If the event handler sets the [[yii\\web\\UserEvent::isValid|isValid]] property of the event object to be `false`,\n  the login process will be cancelled.\n* [[yii\\web\\User::EVENT_AFTER_LOGIN|EVENT_AFTER_LOGIN]]: raised after a successful login.\n* [[yii\\web\\User::EVENT_BEFORE_LOGOUT|EVENT_BEFORE_LOGOUT]]: raised at the beginning of [[yii\\web\\User::logout()]].\n  If the event handler sets the [[yii\\web\\UserEvent::isValid|isValid]] property of the event object to be `false`,\n  the logout process will be cancelled.\n* [[yii\\web\\User::EVENT_AFTER_LOGOUT|EVENT_AFTER_LOGOUT]]: raised after a successful logout.\n\nYou may respond to these events to implement features such as login audit, online user statistics. For example,\nin the handler for [[yii\\web\\User::EVENT_AFTER_LOGIN|EVENT_AFTER_LOGIN]], you may record the login time and IP\naddress in the `user` table.\n"
  },
  {
    "path": "docs/guide/security-authorization.md",
    "content": "Authorization\n=============\n\nAuthorization is the process of verifying that a user has enough permission to do something. Yii provides two authorization\nmethods: Access Control Filter (ACF) and Role-Based Access Control (RBAC).\n\n\n## Access Control Filter <span id=\"access-control-filter\"></span>\n\nAccess Control Filter (ACF) is a simple authorization method implemented as [[yii\\filters\\AccessControl]] which\nis best used by applications that only need some simple access control. As its name indicates, ACF is\nan action [filter](structure-filters.md) that can be used in a controller or a module. While a user is requesting\nto execute an action, ACF will check a list of [[yii\\filters\\AccessControl::rules|access rules]]\nto determine if the user is allowed to access the requested action.\n\nThe code below shows how to use ACF in the `site` controller:\n\n```php\nuse yii\\web\\Controller;\nuse yii\\filters\\AccessControl;\n\nclass SiteController extends Controller\n{\n    public function behaviors()\n    {\n        return [\n            'access' => [\n                'class' => AccessControl::class,\n                'only' => ['login', 'logout', 'signup'],\n                'rules' => [\n                    [\n                        'allow' => true,\n                        'actions' => ['login', 'signup'],\n                        'roles' => ['?'],\n                    ],\n                    [\n                        'allow' => true,\n                        'actions' => ['logout'],\n                        'roles' => ['@'],\n                    ],\n                ],\n            ],\n        ];\n    }\n    // ...\n}\n```\n\nIn the code above ACF is attached to the `site` controller as a behavior. This is the typical way of using an action\nfilter. The `only` option specifies that the ACF should only be applied to the `login`, `logout` and `signup` actions.\nAll other actions in the `site` controller are not subject to the access control. The `rules` option lists\nthe [[yii\\filters\\AccessRule|access rules]], which reads as follows:\n\n- Allow all guest (not yet authenticated) users to access the `login` and `signup` actions. The `roles` option\n  contains a question mark `?` which is a special token representing \"guest users\".\n- Allow authenticated users to access the `logout` action. The `@` character is another special token representing\n  \"authenticated users\".\n\nACF performs the authorization check by examining the access rules one by one from top to bottom until it finds\na rule that matches the current execution context. The `allow` value of the matching rule will then be used to\njudge if the user is authorized or not. If none of the rules matches, it means the user is NOT authorized,\nand ACF will stop further action execution.\n\nWhen ACF determines a user is not authorized to access the current action, it takes the following measure by default:\n\n* If the user is a guest, it will call [[yii\\web\\User::loginRequired()]] to redirect the user browser to the login page.\n* If the user is already authenticated, it will throw a [[yii\\web\\ForbiddenHttpException]].\n\nYou may customize this behavior by configuring the [[yii\\filters\\AccessControl::denyCallback]] property like the following:\n\n```php\n[\n    'class' => AccessControl::class,\n    ...\n    'denyCallback' => function ($rule, $action) {\n        throw new \\Exception('You are not allowed to access this page');\n    }\n]\n```\n\n[[yii\\filters\\AccessRule|Access rules]] support many options. Below is a summary of the supported options.\nYou may also extend [[yii\\filters\\AccessRule]] to create your own customized access rule classes.\n\n * [[yii\\filters\\AccessRule::allow|allow]]: specifies whether this is an \"allow\" or \"deny\" rule.\n\n * [[yii\\filters\\AccessRule::actions|actions]]: specifies which actions this rule matches. This should\nbe an array of action IDs. The comparison is case-sensitive. If this option is empty or not set,\nit means the rule applies to all actions.\n\n * [[yii\\filters\\AccessRule::controllers|controllers]]: specifies which controllers this rule\nmatches. This should be an array of controller IDs. Each controller ID is prefixed with the module ID (if any).\nThe comparison is case-sensitive. If this option is empty or not set, it means the rule applies to all controllers.\n\n * [[yii\\filters\\AccessRule::roles|roles]]: specifies which user roles that this rule matches.\n   Two special roles are recognized, and they are checked via [[yii\\web\\User::isGuest]]:\n\n     - `?`: matches a guest user (not authenticated yet)\n     - `@`: matches an authenticated user\n\n   Using other role names will trigger the invocation of [[yii\\web\\User::can()]], which requires enabling RBAC\n   (to be described in the next subsection). If this option is empty or not set, it means this rule applies to all roles.\n\n * [[yii\\filters\\AccessRule::roleParams|roleParams]]: specifies the parameters that will be passed to [[yii\\web\\User::can()]].\n   See the section below describing RBAC rules to see how it can be used. If this option is empty or not set, then no parameters will be passed.\n\n * [[yii\\filters\\AccessRule::ips|ips]]: specifies which [[yii\\web\\Request::userIP|client IP addresses]] this rule matches.\nAn IP address can contain the wildcard `*` at the end so that it matches IP addresses with the same prefix.\nFor example, '192.168.*' matches all IP addresses in the segment '192.168.'. If this option is empty or not set,\nit means this rule applies to all IP addresses.\n\n * [[yii\\filters\\AccessRule::verbs|verbs]]: specifies which request method (e.g. `GET`, `POST`) this rule matches.\nThe comparison is case-insensitive.\n\n * [[yii\\filters\\AccessRule::matchCallback|matchCallback]]: specifies a PHP callable that should be called to determine\nif this rule should be applied.\n\n * [[yii\\filters\\AccessRule::denyCallback|denyCallback]]: specifies a PHP callable that should be called when this rule\nwill deny the access.\n\nBelow is an example showing how to make use of the `matchCallback` option, which allows you to write arbitrary access\ncheck logic:\n\n```php\nuse yii\\filters\\AccessControl;\n\nclass SiteController extends Controller\n{\n    public function behaviors()\n    {\n        return [\n            'access' => [\n                'class' => AccessControl::class,\n                'only' => ['special-callback'],\n                'rules' => [\n                    [\n                        'actions' => ['special-callback'],\n                        'allow' => true,\n                        'matchCallback' => function ($rule, $action) {\n                            return date('d-m') === '31-10';\n                        }\n                    ],\n                ],\n            ],\n        ];\n    }\n\n    // Match callback called! This page can be accessed only each October 31st\n    public function actionSpecialCallback()\n    {\n        return $this->render('happy-halloween');\n    }\n}\n```\n\n\n## Role Based Access Control (RBAC) <span id=\"rbac\"></span>\n\nRole-Based Access Control (RBAC) provides a simple yet powerful centralized access control. Please refer to\nthe [Wikipedia](https://en.wikipedia.org/wiki/Role-based_access_control) for details about comparing RBAC\nwith other more traditional access control schemes.\n\nYii implements a General Hierarchical RBAC, following the [NIST RBAC model](https://csrc.nist.gov/CSRC/media/Publications/conference-paper/1992/10/13/role-based-access-controls/documents/ferraiolo-kuhn-92.pdf).\nIt provides the RBAC functionality through the [[yii\\rbac\\ManagerInterface|authManager]] [application component](structure-application-components.md).\n\nUsing RBAC involves two parts of work. The first part is to build up the RBAC authorization data, and the second\npart is to use the authorization data to perform access check in places where it is needed.\n\nTo facilitate our description next, we will first introduce some basic RBAC concepts.\n\n\n### Basic Concepts <span id=\"basic-concepts\"></span>\n\nA role represents a collection of *permissions* (e.g. creating posts, updating posts). A role may be assigned\nto one or multiple users. To check if a user has a specified permission, we may check if the user is assigned\nwith a role that contains that permission.\n\nAssociated with each role or permission, there may be a *rule*. A rule represents a piece of code that will be\nexecuted during access check to determine if the corresponding role or permission applies to the current user.\nFor example, the \"update post\" permission may have a rule that checks if the current user is the post creator.\nDuring access checking, if the user is NOT the post creator, he/she will be considered not having the \"update post\" permission.\n\nBoth roles and permissions can be organized in a hierarchy. In particular, a role may consist of other roles or permissions;\nand a permission may consist of other permissions. Yii implements a *partial order* hierarchy which includes the\nmore special *tree* hierarchy. While a role can contain a permission, it is not `true` vice versa.\n\n\n### Configuring RBAC <span id=\"configuring-rbac\"></span>\n\nBefore we set off to define authorization data and perform access checking, we need to configure the\n[[yii\\base\\Application::authManager|authManager]] application component. Yii provides two types of authorization managers:\n[[yii\\rbac\\PhpManager]] and [[yii\\rbac\\DbManager]]. The former uses a PHP script file to store authorization\ndata, while the latter stores authorization data in a database. You may consider using the former if your application\ndoes not require very dynamic role and permission management.\n\n\n#### Using `PhpManager` <span id=\"using-php-manager\"></span>\n\nThe following code shows how to configure the `authManager` in the application configuration using the [[yii\\rbac\\PhpManager]] class:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'authManager' => [\n            'class' => 'yii\\rbac\\PhpManager',\n        ],\n        // ...\n    ],\n];\n```\n\nThe `authManager` can now be accessed via `\\Yii::$app->authManager`.\n\nBy default, [[yii\\rbac\\PhpManager]] stores RBAC data in files under `@app/rbac` directory. Make sure the directory\nand all the files in it are writable by the Web server process if permissions hierarchy needs to be changed online.\n\n\n#### Using `DbManager` <span id=\"using-db-manager\"></span>\n\nThe following code shows how to configure the `authManager` in the application configuration using the [[yii\\rbac\\DbManager]] class:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'authManager' => [\n            'class' => 'yii\\rbac\\DbManager',\n            // uncomment if you want to cache RBAC items hierarchy\n            // 'cache' => 'cache',\n        ],\n        // ...\n    ],\n];\n```\n> Note: If you are using yii2-basic-app template, there is a `config/console.php` configuration file where the\n  `authManager` needs to be declared additionally to `config/web.php`.\n> In case of yii2-advanced-app the `authManager` should be declared only once in `common/config/main.php`.\n\n`DbManager` uses four database tables to store its data:\n\n- [[yii\\rbac\\DbManager::$itemTable|itemTable]]: the table for storing authorization items. Defaults to \"auth_item\".\n- [[yii\\rbac\\DbManager::$itemChildTable|itemChildTable]]: the table for storing authorization item hierarchy. Defaults to \"auth_item_child\".\n- [[yii\\rbac\\DbManager::$assignmentTable|assignmentTable]]: the table for storing authorization item assignments. Defaults to \"auth_assignment\".\n- [[yii\\rbac\\DbManager::$ruleTable|ruleTable]]: the table for storing rules. Defaults to \"auth_rule\".\n\nBefore you can go on you need to create those tables in the database. To do this, you can use the migration stored in `@yii/rbac/migrations`:\n\n`yii migrate --migrationPath=@yii/rbac/migrations`\n\nRead more about working with migrations from different namespaces in\n[Separated Migrations](db-migrations.md#separated-migrations) section.\n\nThe `authManager` can now be accessed via `\\Yii::$app->authManager`.\n\n\n### Building Authorization Data <span id=\"generating-rbac-data\"></span>\n\nBuilding authorization data is all about the following tasks:\n\n- defining roles and permissions;\n- establishing relations among roles and permissions;\n- defining rules;\n- associating rules with roles and permissions;\n- assigning roles to users.\n\nDepending on authorization flexibility requirements the tasks above could be done in different ways.\nIf your permissions hierarchy is meant to be changed by developers only, you can use either migrations\nor a console command. Migration pro is that it could be executed along with other migrations. Console\ncommand pro is that you have a good overview of the hierarchy in the code rather than it being scattered\namong multiple migrations.\n\nEither way in the end you'll get the following RBAC hierarchy:\n\n![Simple RBAC hierarchy](images/rbac-hierarchy-1.png \"Simple RBAC hierarchy\")\n\nIn case you need permissions hierarchy to be formed dynamically you need a UI or a console command. API used to\nbuild the hierarchy itself won't be different.\n\n#### Using migrations\n\nYou can use [migrations](db-migrations.md)\nto initialize and modify hierarchy via APIs offered by `authManager`.\n\nCreate new migration using `./yii migrate/create init_rbac` then implement creating a hierarchy:\n\n```php\n<?php\nuse yii\\db\\Migration;\n\nclass m170124_084304_init_rbac extends Migration\n{\n    public function up()\n    {\n        $auth = Yii::$app->authManager;\n\n        // add \"createPost\" permission\n        $createPost = $auth->createPermission('createPost');\n        $createPost->description = 'Create a post';\n        $auth->add($createPost);\n\n        // add \"updatePost\" permission\n        $updatePost = $auth->createPermission('updatePost');\n        $updatePost->description = 'Update post';\n        $auth->add($updatePost);\n\n        // add \"author\" role and give this role the \"createPost\" permission\n        $author = $auth->createRole('author');\n        $auth->add($author);\n        $auth->addChild($author, $createPost);\n\n        // add \"admin\" role and give this role the \"updatePost\" permission\n        // as well as the permissions of the \"author\" role\n        $admin = $auth->createRole('admin');\n        $auth->add($admin);\n        $auth->addChild($admin, $updatePost);\n        $auth->addChild($admin, $author);\n\n        // Assign roles to users. 1 and 2 are IDs returned by IdentityInterface::getId()\n        // usually implemented in your User model.\n        $auth->assign($author, 2);\n        $auth->assign($admin, 1);\n    }\n    \n    public function down()\n    {\n        $auth = Yii::$app->authManager;\n\n        $auth->removeAll();\n    }\n}\n```\n\n> If you don't want to hardcode which users have certain roles, don't put `->assign()` calls in migrations. Instead,\n  create either UI or console command to manage assignments.\n\nMigration could be applied by using `yii migrate`.\n\n### Using console command\n\nIf your permissions hierarchy doesn't change at all and you have a fixed number of users you can create a\n-[console command](tutorial-console.md#create-command) that will initialize authorization data once via\nAPIs offered by `authManager`:\n\n```php\n<?php\nnamespace app\\commands;\n\nuse Yii;\nuse yii\\console\\Controller;\n\nclass RbacController extends Controller\n{\n    public function actionInit()\n    {\n        $auth = Yii::$app->authManager;\n        $auth->removeAll();\n        \n        // add \"createPost\" permission\n        $createPost = $auth->createPermission('createPost');\n        $createPost->description = 'Create a post';\n        $auth->add($createPost);\n\n        // add \"updatePost\" permission\n        $updatePost = $auth->createPermission('updatePost');\n        $updatePost->description = 'Update post';\n        $auth->add($updatePost);\n\n        // add \"author\" role and give this role the \"createPost\" permission\n        $author = $auth->createRole('author');\n        $auth->add($author);\n        $auth->addChild($author, $createPost);\n\n        // add \"admin\" role and give this role the \"updatePost\" permission\n        // as well as the permissions of the \"author\" role\n        $admin = $auth->createRole('admin');\n        $auth->add($admin);\n        $auth->addChild($admin, $updatePost);\n        $auth->addChild($admin, $author);\n\n        // Assign roles to users. 1 and 2 are IDs returned by IdentityInterface::getId()\n        // usually implemented in your User model.\n        $auth->assign($author, 2);\n        $auth->assign($admin, 1);\n    }\n}\n```\n\n> Note: If you are using advanced template, you need to put your `RbacController` inside `console/controllers` directory\n  and change namespace to `console\\controllers`.\n  \nThe command above could be executed from console the following way:\n\n```\nyii rbac/init\n```\n\n> If you don't want to hardcode what users have certain roles, don't put `->assign()` calls into the command. Instead,\n  create either UI or console command to manage assignments.\n\n## Assigning roles to users\n\nAuthor can create post, admin can update post and do everything author can.\n\nIf your application allows user signup you need to assign roles to these new users once. For example, in order for all\nsigned up users to become authors in your advanced project template you need to modify `frontend\\models\\SignupForm::signup()`\nas follows:\n\n```php\npublic function signup()\n{\n    if ($this->validate()) {\n        $user = new User();\n        $user->username = $this->username;\n        $user->email = $this->email;\n        $user->setPassword($this->password);\n        $user->generateAuthKey();\n        $user->save(false);\n\n        // the following three lines were added:\n        $auth = \\Yii::$app->authManager;\n        $authorRole = $auth->getRole('author');\n        $auth->assign($authorRole, $user->getId());\n\n        return $user;\n    }\n\n    return null;\n}\n```\n\nFor applications that require complex access control with dynamically updated authorization data, special user interfaces\n(i.e. admin panel) may need to be developed using APIs offered by `authManager`.\n\n\n### Using Rules <span id=\"using-rules\"></span>\n\nAs aforementioned, rules add additional constraint to roles and permissions. A rule is a class extending\nfrom [[yii\\rbac\\Rule]]. It must implement the [[yii\\rbac\\Rule::execute()|execute()]] method. In the hierarchy we've\ncreated previously author cannot edit his own post. Let's fix it. First we need a rule to verify that the user is the post author:\n\n```php\nnamespace app\\rbac;\n\nuse yii\\rbac\\Rule;\nuse app\\models\\Post;\n\n/**\n * Checks if authorID matches user passed via params\n */\nclass AuthorRule extends Rule\n{\n    public $name = 'isAuthor';\n\n    /**\n     * @param string|int $user the user ID.\n     * @param Item $item the role or permission that this rule is associated with\n     * @param array $params parameters passed to ManagerInterface::checkAccess().\n     * @return bool a value indicating whether the rule permits the role or permission it is associated with.\n     */\n    public function execute($user, $item, $params)\n    {\n        return isset($params['post']) ? $params['post']->createdBy == $user : false;\n    }\n}\n```\n\nThe rule above checks if the `post` is created by `$user`. We'll create a special permission `updateOwnPost` in the\ncommand we've used previously:\n\n```php\n$auth = Yii::$app->authManager;\n\n// add the rule\n$rule = new \\app\\rbac\\AuthorRule;\n$auth->add($rule);\n\n// add the \"updateOwnPost\" permission and associate the rule with it.\n$updateOwnPost = $auth->createPermission('updateOwnPost');\n$updateOwnPost->description = 'Update own post';\n$updateOwnPost->ruleName = $rule->name;\n$auth->add($updateOwnPost);\n\n// \"updateOwnPost\" will be used from \"updatePost\"\n$auth->addChild($updateOwnPost, $updatePost);\n\n// allow \"author\" to update their own posts\n$auth->addChild($author, $updateOwnPost);\n```\n\nNow we have got the following hierarchy:\n\n![RBAC hierarchy with a rule](images/rbac-hierarchy-2.png \"RBAC hierarchy with a rule\")\n\n\n### Access Check <span id=\"access-check\"></span>\n\nWith the authorization data ready, access check is as simple as a call to the [[yii\\rbac\\ManagerInterface::checkAccess()]]\nmethod. Because most access check is about the current user, for convenience Yii provides a shortcut method\n[[yii\\web\\User::can()]], which can be used like the following:\n\n```php\nif (\\Yii::$app->user->can('createPost')) {\n    // create post\n}\n```\n\nIf the current user is Jane with `ID=1` we are starting at `createPost` and trying to get to `Jane`:\n\n![Access check](images/rbac-access-check-1.png \"Access check\")\n\nIn order to check if a user can update a post, we need to pass an extra parameter that is required by `AuthorRule` described before:\n\n```php\nif (\\Yii::$app->user->can('updatePost', ['post' => $post])) {\n    // update post\n}\n```\n\nHere is what happens if the current user is John:\n\n\n![Access check](images/rbac-access-check-2.png \"Access check\")\n\nWe are starting with the `updatePost` and going through `updateOwnPost`. In order to pass the access check, `AuthorRule`\nshould return `true` from its `execute()` method. The method receives its `$params` from the `can()` method call so the value is\n`['post' => $post]`. If everything is fine, we will get to `author` which is assigned to John.\n\nIn case of Jane it is a bit simpler since she is an admin:\n\n![Access check](images/rbac-access-check-3.png \"Access check\")\n\nInside your controller there are a few ways to implement authorization. If you want granular permissions that\nseparate access to adding and deleting, then you need to check access for each action. You can either use the\nabove condition in each action method, or use [[yii\\filters\\AccessControl]]:\n\n```php\npublic function behaviors()\n{\n    return [\n        'access' => [\n            'class' => AccessControl::class,\n            'rules' => [\n                [\n                    'allow' => true,\n                    'actions' => ['index'],\n                    'roles' => ['managePost'],\n                ],\n                [\n                    'allow' => true,\n                    'actions' => ['view'],\n                    'roles' => ['viewPost'],\n                ],\n                [\n                    'allow' => true,\n                    'actions' => ['create'],\n                    'roles' => ['createPost'],\n                ],\n                [\n                    'allow' => true,\n                    'actions' => ['update'],\n                    'roles' => ['updatePost'],\n                ],\n                [\n                    'allow' => true,\n                    'actions' => ['delete'],\n                    'roles' => ['deletePost'],\n                ],\n            ],\n        ],\n    ];\n}\n```\n\nIf all the CRUD operations are managed together then it's a good idea to use a single permission, like `managePost`, and\ncheck it in [[yii\\web\\Controller::beforeAction()]].\n\nIn the above example, no parameters are passed with the roles specified for accessing an action, but in case of the\n`updatePost` permission, we need to pass a `post` parameter for it to work properly.\nYou can pass parameters to [[yii\\web\\User::can()]] by specifying [[yii\\filters\\AccessRule::roleParams|roleParams]] on\nthe access rule:\n\n```php\n[\n    'allow' => true,\n    'actions' => ['update'],\n    'roles' => ['updatePost'],\n    'roleParams' => function() {\n        return ['post' => Post::findOne(['id' => Yii::$app->request->get('id')])];\n    },\n],\n```\n\nIn the above example, [[yii\\filters\\AccessRule::roleParams|roleParams]] is a Closure that will be evaluated when\nthe access rule is checked, so the model will only be loaded when needed.\nIf the creation of role parameters is a simple operation, you may just specify an array, like so:\n\n```php\n[\n    'allow' => true,\n    'actions' => ['update'],\n    'roles' => ['updatePost'],\n    'roleParams' => ['postId' => Yii::$app->request->get('id')],\n],\n```\n\n### Using Default Roles <span id=\"using-default-roles\"></span>\n\nA default role is a role that is *implicitly* assigned to *all* users. The call to [[yii\\rbac\\ManagerInterface::assign()]]\nis not needed, and the authorization data does not contain its assignment information.\n\nA default role is usually associated with a rule which determines if the role applies to the user being checked.\n\nDefault roles are often used in applications which already have some sort of role assignment. For example, an application\nmay have a \"group\" column in its user table to represent which privilege group each user belongs to.\nIf each privilege group can be mapped to an RBAC role, you can use the default role feature to automatically\nassign each user to an RBAC role. Let's use an example to show how this can be done.\n\nAssume in the user table, you have a `group` column which uses 1 to represent the administrator group and 2 the author group.\nYou plan to have two RBAC roles `admin` and `author` to represent the permissions for these two groups, respectively.\nYou can set up the RBAC data as follows, first create a class:\n\n\n```php\nnamespace app\\rbac;\n\nuse Yii;\nuse yii\\rbac\\Rule;\n\n/**\n * Checks if user group matches\n */\nclass UserGroupRule extends Rule\n{\n    public $name = 'userGroup';\n\n    public function execute($user, $item, $params)\n    {\n        if (!Yii::$app->user->isGuest) {\n            $group = Yii::$app->user->identity->group;\n            if ($item->name === 'admin') {\n                return $group == 1;\n            } elseif ($item->name === 'author') {\n                return $group == 1 || $group == 2;\n            }\n        }\n        return false;\n    }\n}\n```\n\nThen create your own command/migration as explained [in the previous section](#generating-rbac-data):\n\n```php\n$auth = Yii::$app->authManager;\n\n$rule = new \\app\\rbac\\UserGroupRule;\n$auth->add($rule);\n\n$author = $auth->createRole('author');\n$author->ruleName = $rule->name;\n$auth->add($author);\n// ... add permissions as children of $author ...\n\n$admin = $auth->createRole('admin');\n$admin->ruleName = $rule->name;\n$auth->add($admin);\n$auth->addChild($admin, $author);\n// ... add permissions as children of $admin ...\n```\n\nNote that in the above, because \"author\" is added as a child of \"admin\", when you implement the `execute()` method\nof the rule class, you need to respect this hierarchy as well. That is why when the role name is \"author\",\nthe `execute()` method will return `true` if the user group is either 1 or 2 (meaning the user is in either \"admin\"\ngroup or \"author\" group).\n\nNext, configure `authManager` by listing the two roles in [[yii\\rbac\\BaseManager::$defaultRoles]]:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'authManager' => [\n            'class' => 'yii\\rbac\\PhpManager',\n            'defaultRoles' => ['admin', 'author'],\n        ],\n        // ...\n    ],\n];\n```\n\nNow if you perform an access check, both of the `admin` and `author` roles will be checked by evaluating\nthe rules associated with them. If the rule returns `true`, it means the role applies to the current user.\nBased on the above rule implementation, this means if the `group` value of a user is 1, the `admin` role\nwould apply to the user; and if the `group` value is 2, the `author` role would apply.\n"
  },
  {
    "path": "docs/guide/security-best-practices.md",
    "content": "Security best practices\n=======================\n\nBelow we'll review common security principles and describe how to avoid threats when developing applications using Yii.\nMost of these principles are not unique to Yii alone but apply to website or software development in general,\nso you will also find links for further reading on the general ideas behind these.\n\n\nBasic principles\n----------------\n\nThere are two main principles when it comes to security no matter which application is being developed:\n\n1. Filter input.\n2. Escape output.\n\n\n### Filter input\n\nFilter input means that input should never be considered safe and you should always check if the value you've got is\nactually among allowed ones. For example, if we know that sorting could be done by three fields `title`, `created_at` and `status`\nand the field could be supplied via user input, it's better to check the value we've got right where we're receiving it.\nIn terms of basic PHP that would look like the following:\n\n```php\n$sortBy = $_GET['sort'];\nif (!in_array($sortBy, ['title', 'created_at', 'status'])) {\n\tthrow new Exception('Invalid sort value.');\n}\n```\n\nIn Yii, most probably you'll use [form validation](input-validation.md) to do alike checks.\n\nFurther reading on the topic:\n\n- <https://owasp.org/www-community/vulnerabilities/Improper_Data_Validation>\n- <https://www.owasp.org/index.php/Input_Validation_Cheat_Sheet>\n\n\n### Escape output\n\nEscape output means that depending on context where we're using data it should be escaped i.e. in context of HTML you\nshould escape `<`, `>` and alike special characters. In context of JavaScript or SQL it will be different set of characters.\nSince it's error-prone to escape everything manually Yii provides various tools to perform escaping for different\ncontexts.\n\nFurther reading on the topic:\n\n- <https://owasp.org/www-community/attacks/Command_Injection>\n- <https://owasp.org/www-community/attacks/Code_Injection>\n- <https://owasp.org/www-community/attacks/xss/>\n\n\nAvoiding SQL injections\n-----------------------\n\nSQL injection happens when query text is formed by concatenating unescaped strings such as the following:\n\n```php\n$username = $_GET['username'];\n$sql = \"SELECT * FROM user WHERE username = '$username'\";\n```\n\nInstead of supplying correct username attacker could give your applications something like `'; DROP TABLE user; --`.\nResulting SQL will be the following:\n\n```sql\nSELECT * FROM user WHERE username = ''; DROP TABLE user; --'\n```\n\nThis is valid query that will search for users with empty username and then will drop `user` table most probably\nresulting in broken website and data loss (you've set up regular backups, right?).\n\nIn Yii most database querying happens via [Active Record](db-active-record.md) which properly uses PDO prepared\nstatements internally. In case of prepared statements it's not possible to manipulate query as was demonstrated above.\n\nStill, sometimes you need [raw queries](db-dao.md) or [query builder](db-query-builder.md). In this case you should use\nsafe ways of passing data. If data is used for column values it's preferred to use prepared statements:\n\n```php\n// query builder\n$userIDs = (new Query())\n    ->select('id')\n    ->from('user')\n    ->where('status=:status', [':status' => $status])\n    ->all();\n\n// DAO\n$userIDs = $connection\n    ->createCommand('SELECT id FROM user where status=:status')\n    ->bindValues([':status' => $status])\n    ->queryColumn();\n```\n\nIf data is used to specify column names or table names the best thing to do is to allow only predefined set of values:\n \n```php\nfunction actionList($orderBy = null)\n{\n    if (!in_array($orderBy, ['name', 'status'])) {\n        throw new BadRequestHttpException('Only name and status are allowed to order by.')\n    }\n    \n    // ...\n}\n```\n\nIn case it's not possible, table and column names should be escaped. Yii has special syntax for such escaping\nwhich allows doing it the same way for all databases it supports:\n\n```php\n$sql = \"SELECT COUNT([[$column]]) FROM {{table}}\";\n$rowCount = $connection->createCommand($sql)->queryScalar();\n```\n\nYou can get details about the syntax in [Quoting Table and Column Names](db-dao.md#quoting-table-and-column-names).\n\nFurther reading on the topic:\n\n- <https://owasp.org/www-community/attacks/SQL_Injection>\n\n\nAvoiding XSS\n------------\n\nXSS or cross-site scripting happens when output isn't escaped properly when outputting HTML to the browser. For example,\nif user can enter his name and instead of `Alexander` he enters `<script>alert('Hello!');</script>`, every page that\noutputs user name without escaping it will execute JavaScript `alert('Hello!');` resulting in alert box popping up\nin a browser. Depending on website instead of innocent alert such script could send messages using your name or even\nperform bank transactions.\n\nAvoiding XSS is quite easy in Yii. There are generally two cases:\n\n1. You want data to be outputted as plain text.\n2. You want data to be outputted as HTML.\n\nIf all you need is plain text then escaping is as easy as the following:\n\n\n```php\n<?= \\yii\\helpers\\Html::encode($username) ?>\n```\n\nIf it should be HTML we could get some help from HtmlPurifier:\n\n```php\n<?= \\yii\\helpers\\HtmlPurifier::process($description) ?>\n```\n\nNote that HtmlPurifier processing is quite heavy so consider adding caching.\n\nFurther reading on the topic:\n\n- <https://owasp.org/www-community/attacks/xss/>\n\n\nAvoiding CSRF\n-------------\n\nCSRF is an abbreviation for cross-site request forgery. The idea is that many applications assume that requests coming\nfrom a user browser are made by the user themselves. This assumption could be false.\n\nFor example, the website `an.example.com` has a `/logout` URL that, when accessed using a simple GET request, logs the user out. As long\nas it's requested by the user themselves everything is OK, but one day bad guys are somehow posting\n`<img src=\"https://an.example.com/logout\">` on a forum the user visits frequently. The browser doesn't make any difference between\nrequesting an image or requesting a page so when the user opens a page with such a manipulated `<img>` tag,\nthe browser will send the GET request to that URL and the user will be logged out from `an.example.com`.\n\nThat's the basic idea of how a CSRF attack works. One can say that logging out a user is not a serious thing,\nhowever this was just an example, there are much more things one could do using this approach, for example triggering payments\nor changing data. Imagine that some website has an URL\n`https://an.example.com/purse/transfer?to=anotherUser&amount=2000`. Accessing it using GET request, causes transfer of $2000\nfrom authorized user account to user `anotherUser`. We know, that the browser will always send GET request to load an image,\nso we can modify code to accept only POST requests on that URL. Unfortunately, this will not save us, because an attacker\ncan put some JavaScript code instead of `<img>` tag, which allows to send POST requests to that URL as well.\n\nFor this reason, Yii applies additional mechanisms to protect against CSRF attacks.\n\nIn order to avoid CSRF you should always:\n\n1. Follow HTTP specification i.e. GET should not change application state.\n   See [RFC2616](https://www.rfc-editor.org/rfc/rfc9110.html#name-method-definitions) for more details.\n2. Keep Yii CSRF protection enabled.\n\nSometimes you need to disable CSRF validation per controller and/or action. It could be achieved by setting its property:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public $enableCsrfValidation = false;\n\n    public function actionIndex()\n    {\n        // CSRF validation will not be applied to this and other actions\n    }\n\n}\n```\n\nTo disable CSRF validation per custom actions you can do:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function beforeAction($action)\n    {\n        // ...set `$this->enableCsrfValidation` here based on some conditions...\n        // call parent method that will check CSRF if such property is `true`.\n        return parent::beforeAction($action);\n    }\n}\n```\n\nDisabling CSRF validation in [standalone actions](structure-controllers.md#standalone-actions) must be done in `init()`\nmethod. Do not place this code into `beforeRun()` method because it won't have effect.\n\n```php\n<?php\n\nnamespace app\\components;\n\nuse yii\\base\\Action;\n\nclass ContactAction extends Action\n{\n    public function init()\n    {\n        parent::init();\n        $this->controller->enableCsrfValidation = false;\n    }\n\n    public function run()\n    {\n          $model = new ContactForm();\n          $request = Yii::$app->request;\n          if ($request->referrer === 'yiipowered.com'\n              && $model->load($request->post())\n              && $model->validate()\n          ) {\n              $model->sendEmail();\n          }\n    }\n}\n```\n\n> Warning: Disabling CSRF will allow any site to send POST requests to your site. It is important to implement extra validation such as checking an IP address or a secret token in this case.\n\n> Note: Since version 2.0.21 Yii supports the `sameSite` cookie setting (requires PHP version 7.3.0 or higher).\n  Setting the `sameSite` cookie setting does not make the above obsolete since not all browsers support the setting yet.\n  See the [Sessions and Cookies sameSite option](runtime-sessions-cookies.md#samesite) for more information.\n\nFurther reading on the topic:\n\n- <https://owasp.org/www-community/attacks/csrf>\n- <https://owasp.org/www-community/SameSite>\n\n\nAvoiding arbitrary object instantiations\n----------------------------------------\n\nYii [configurations](concept-configurations.md) are associative arrays used by the framework to instantiate new objects through `Yii::createObject($config)`. These arrays specify the class name for instantiation, and it is important to ensure that this class name does not originate from untrusted sources. Otherwise, it can lead to Unsafe Reflection, a vulnerability that allows the execution of malicious code by exploiting the loading of specific classes. Additionally, when you need to dynamically add keys to an object derived from a framework class, such as the base `Component` class, it's essential to validate these dynamic properties using a whitelist approach. This precaution is necessary because the framework might employ `Yii::createObject($config)` within the `__set()` magic method.\n\n\nAvoiding file exposure\n----------------------\n\nBy default server webroot is meant to be pointed to `web` directory where `index.php` is. In case of shared hosting\nenvironments it could be impossible to achieve so we'll end up with all the code, configs and logs in server webroot.\n\nIf it's the case don't forget to deny access to everything except `web`. If it can't be done consider hosting your\napplication elsewhere.\n\n\nAvoiding debug info and tools in production\n-------------------------------------------\n\nIn debug mode Yii shows quite verbose errors which are certainly helpful for development. The thing is that these\nverbose errors are handy for attacker as well since these could reveal database structure, configuration values and\nparts of your code. Never run production applications with `YII_DEBUG` set to `true` in your `index.php`.\n\nYou should never enable Gii or the Debug toolbar in production. It could be used to get information about database structure, code and to\nsimply rewrite code with what's generated by Gii.\n\nDebug toolbar should be avoided at production unless really necessary. It exposes all the application and config\ndetails possible. If you absolutely need it check twice that access is properly restricted to your IP only.\n\nFurther reading on the topic:\n\n- <https://owasp.org/www-project-.net/articles/Exception_Handling.md>\n- <https://owasp.org/www-pdf-archive/OWASP_Top_10_2007.pdf> (A6 - Information Leakage and Improper Error Handling)\n\n\nUsing secure connection over TLS\n--------------------------------\n\nYii provides features that rely on cookies and/or PHP sessions. These can be vulnerable in case your connection is\ncompromised. The risk is reduced if the app uses secure connection via TLS (often referred to as [SSL](https://en.wikipedia.org/wiki/Transport_Layer_Security)).\n\nPlease refer to your webserver documentation for instructions on how to configure it. You may also check example configs\nprovided by the H5BP project:\n\n- [Nginx](https://github.com/h5bp/server-configs-nginx)\n- [Apache](https://github.com/h5bp/server-configs-apache).\n- [IIS](https://github.com/h5bp/server-configs-iis).\n- [Lighttpd](https://github.com/h5bp/server-configs-lighttpd).\n\n> Note: When TLS is configured it is recommended that (session) cookies are sent over TLS exclusively.\n  This is achieved by setting the `secure` flag for sessions and/or cookies.\n  See the [Sessions and Cookies secure flag](runtime-sessions-cookies.md#secure) for more information.\n\n\nSecure Server configuration\n---------------------------\n\nThe purpose of this section is to highlight risks that need to be considered when creating a\nserver configuration for serving a Yii based website. Besides the points covered here there may\nbe other security related configuration options to be considered, so do not consider this section to\nbe complete.\n\n### Avoiding `Host`-header attacks\n\nClasses like [[yii\\web\\UrlManager]] and [[yii\\helpers\\Url]] may use the [[yii\\web\\Request::getHostInfo()|currently requested host name]]\nfor generating links.\nIf the webserver is configured to serve the same site independent of the value of the `Host` header, this information may not be reliable\nand [may be faked by the user sending the HTTP request](https://www.acunetix.com/vulnerabilities/web/host-header-attack).\nIn such situations you should either fix your webserver configuration to serve the site only for specified host names\nor explicitly set or filter the value by setting the [[yii\\web\\Request::setHostInfo()|hostInfo]] property of the `request` application component.\n\nFor more information about the server configuration, please refer to the documentation of your webserver:\n\n- Apache 2: <https://httpd.apache.org/docs/trunk/vhosts/examples.html#defaultallports>\n- Nginx: <https://www.nginx.com/resources/wiki/start/topics/examples/server_blocks/>\n\nIf you don't have access to the server configuration, you can setup [[yii\\filters\\HostControl]] filter at\napplication level in order to protect against such kind of attack:\n\n```php\n// Web Application configuration file\nreturn [\n    'as hostControl' => [\n        'class' => 'yii\\filters\\HostControl',\n        'allowedHosts' => [\n            'example.com',\n            '*.example.com',\n        ],\n        'fallbackHostInfo' => 'https://example.com',\n    ],\n    // ...\n];\n```\n\n> Note: you should always prefer web server configuration for 'host header attack' protection instead of the filter usage.\n  [[yii\\filters\\HostControl]] should be used only if server configuration setup is unavailable.\n\n### Configuring SSL peer validation\n\nThere is a typical misconception about how to solve SSL certificate validation issues such as:\n\n```\ncURL error 60: SSL certificate problem: unable to get local issuer certificate\n```\n\nor\n\n```\nstream_socket_enable_crypto(): SSL operation failed with code 1. OpenSSL Error messages: error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed\n```\n\nMany sources wrongly suggest disabling SSL peer verification. That should not be ever done since it enables\nman-in-the middle type of attacks. Instead, PHP should be configured properly:\n\n1. Download [https://curl.haxx.se/ca/cacert.pem](https://curl.haxx.se/ca/cacert.pem).\n2. Add the following to your php.ini:\n  ```\n  openssl.cafile=\"/path/to/cacert.pem\"\n  curl.cainfo=\"/path/to/cacert.pem\".\n  ```\n\nNote that the `cacert.pem` file should be kept up to date.\n"
  },
  {
    "path": "docs/guide/security-cryptography.md",
    "content": "Cryptography\n============\n\nIn this section we'll review the following security aspects:\n\n- Generating random data\n- Encryption and Decryption\n- Confirming Data Integrity\n\nGenerating Pseudorandom Data\n----------------------------\n\nPseudorandom data is useful in many situations. For example when resetting a password via email you need to generate a\ntoken, save it to the database, and send it via email to end user which in turn will allow them to prove ownership of\nthat account. It is very important that this token be unique and hard to guess, else there is a possibility that attacker\ncan predict the token's value and reset the user's password.\n\nYii security helper makes generating pseudorandom data simple:\n\n\n```php\n$key = Yii::$app->getSecurity()->generateRandomString();\n```\n\nEncryption and Decryption\n-------------------------\n\nYii provides convenient helper functions that allow you to encrypt/decrypt data using a secret key. The data is passed through the encryption function so that only the person which has the secret key will be able to decrypt it.\nFor example, we need to store some information in our database but we need to make sure only the user who has the secret key can view it (even if the application database is compromised):\n\n\n```php\n// $data and $secretKey are obtained from the form\n$encryptedData = Yii::$app->getSecurity()->encryptByPassword($data, $secretKey);\n// store $encryptedData to database\n```\n\nSubsequently when user wants to read the data:\n\n```php\n// $secretKey is obtained from user input, $encryptedData is from the database\n$data = Yii::$app->getSecurity()->decryptByPassword($encryptedData, $secretKey);\n```\n\nIt's also possible to use key instead of password via [[\\yii\\base\\Security::encryptByKey()]] and\n[[\\yii\\base\\Security::decryptByKey()]].\n\nConfirming Data Integrity\n-------------------------\n\nThere are situations in which you need to verify that your data hasn't been tampered with by a third party or even corrupted in some way. Yii provides an easy way to confirm data integrity in the form of two helper functions.\n\nPrefix the data with a hash generated from the secret key and data\n\n\n```php\n// $secretKey our application or user secret, $genuineData obtained from a reliable source\n$data = Yii::$app->getSecurity()->hashData($genuineData, $secretKey);\n```\n\nChecks if the data integrity has been compromised\n\n```php\n// $secretKey our application or user secret, $data obtained from an unreliable source\n$data = Yii::$app->getSecurity()->validateData($data, $secretKey);\n```\n"
  },
  {
    "path": "docs/guide/security-overview.md",
    "content": "Security\n========\n\nGood security is vital to the health and success of any application. Unfortunately, many developers cut corners when it\ncomes to security, either due to a lack of understanding or because implementation is too much of a hurdle. To make your\nYii powered application as secure as possible, Yii has included several excellent and easy to use security features.\n\n* [Authentication](security-authentication.md)\n* [Authorization](security-authorization.md)\n* [Working with Passwords](security-passwords.md)\n* [Cryptography](security-cryptography.md)\n* [Views security](structure-views.md#security)\n* [Auth Clients](https://github.com/yiisoft/yii2-authclient/blob/master/docs/guide/README.md)\n* [Best Practices](security-best-practices.md)\n* [Trusted proxies and headers](runtime-requests.md#trusted-proxies)\n"
  },
  {
    "path": "docs/guide/security-passwords.md",
    "content": "Working with Passwords\n======================\n\nMost developers know that passwords cannot be stored in plain text, but many developers believe it's still safe to hash\npasswords using `md5` or `sha1`. There was a time when using the aforementioned hashing algorithms was sufficient,\nbut modern hardware makes it possible to crack such hashes and even stronger ones very quickly using brute force attacks.\n\nIn order to provide increased security for user passwords, even in the worst case scenario (your application is breached),\nyou need to use a hashing algorithm that is resilient against brute force attacks. The best current choice is `bcrypt`.\nIn PHP, you can create a `bcrypt` hash using the [crypt function](https://www.php.net/manual/en/function.crypt.php). Yii provides\ntwo helper functions which make using `crypt` to securely generate and verify hashes easier.\n\nWhen a user provides a password for the first time (e.g., upon registration), the password needs to be hashed:\n\n\n```php\n$hash = Yii::$app->getSecurity()->generatePasswordHash($password);\n```\n\nThe hash can then be associated with the corresponding model attribute, so it can be stored in the database for later use.\n\nWhen a user attempts to log in, the submitted password must be verified against the previously hashed and stored password:\n\n\n```php\nif (Yii::$app->getSecurity()->validatePassword($password, $hash)) {\n    // all good, logging user in\n} else {\n    // wrong password\n}\n```\n"
  },
  {
    "path": "docs/guide/start-databases.md",
    "content": "Working with Databases\n======================\n\nThis section will describe how to create a new page that displays country data fetched from\na database table named `country`. To achieve this goal, you will configure a database connection,\ncreate an [Active Record](db-active-record.md) class, define an [action](structure-controllers.md),\nand create a [view](structure-views.md).\n\nThrough this tutorial, you will learn how to:\n\n* configure a DB connection,\n* define an Active Record class,\n* query data using the Active Record class,\n* display data in a view in a paginated fashion.\n\nNote that in order to finish this section, you should have basic knowledge and experience using databases.\nIn particular, you should know how to create a database, and how to execute SQL statements using a DB client tool.\n\n\nPreparing the Database <span id=\"preparing-database\"></span>\n----------------------\n\nTo begin, create a database named `yii2basic`, from which you will fetch data in your application.\nYou may create an SQLite, MySQL, PostgreSQL, MSSQL or Oracle database, as Yii has built-in support for many database applications. For simplicity, MySQL will be assumed in the following description.\n\n> Info: While MariaDB used to be a drop-in replacement for MySQL this is no longer fully true. In case you wish to use advanced features like `JSON` support in MariaDB, please check the MariaDB extension listed below.\n\nNext, create a table named `country` in the database, and insert some sample data. You may run the following SQL statements to do so:\n\n```sql\nCREATE TABLE `country` (\n  `code` CHAR(2) NOT NULL PRIMARY KEY,\n  `name` CHAR(52) NOT NULL,\n  `population` INT(11) NOT NULL DEFAULT '0'\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nINSERT INTO `country` VALUES ('AU','Australia',24016400);\nINSERT INTO `country` VALUES ('BR','Brazil',205722000);\nINSERT INTO `country` VALUES ('CA','Canada',35985751);\nINSERT INTO `country` VALUES ('CN','China',1375210000);\nINSERT INTO `country` VALUES ('DE','Germany',81459000);\nINSERT INTO `country` VALUES ('FR','France',64513242);\nINSERT INTO `country` VALUES ('GB','United Kingdom',65097000);\nINSERT INTO `country` VALUES ('IN','India',1285400000);\nINSERT INTO `country` VALUES ('RU','Russia',146519759);\nINSERT INTO `country` VALUES ('US','United States',322976000);\n```\n\nAt this point, you have a database named `yii2basic`, and within it a `country` table with three columns, containing ten rows of data.\n\nConfiguring a DB Connection <span id=\"configuring-db-connection\"></span>\n---------------------------\n\nBefore proceeding, make sure you have installed both the [PDO](https://www.php.net/manual/en/book.pdo.php) PHP extension and\nthe PDO driver for the database you are using (e.g. `pdo_mysql` for MySQL). This is a basic requirement\nif your application uses a relational database.\n\nWith those installed, open the file `config/db.php` and change the parameters to be correct for your database. By default,\nthe file contains the following:\n\n```php\n<?php\n\nreturn [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=localhost;dbname=yii2basic',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n];\n```\n\nThe `config/db.php` file is a typical file-based [configuration](concept-configurations.md) tool. This particular configuration file specifies the parameters\nneeded to create and initialize a [[yii\\db\\Connection]] instance through which you can make SQL queries\nagainst the underlying database.\n\nThe DB connection configured above can be accessed in the application code via the expression `Yii::$app->db`.\n\n> Info: The `config/db.php` file will be included by the main application configuration `config/web.php`, \n  which specifies how the [application](structure-applications.md) instance should be initialized.\n  For more information, please refer to the [Configurations](concept-configurations.md) section.\n\nIf you need to work with databases support for which isn't bundled with Yii, check the following extensions:\n\n- [Informix](https://github.com/edgardmessias/yii2-informix)\n- [IBM DB2](https://github.com/edgardmessias/yii2-ibm-db2)\n- [Firebird](https://github.com/edgardmessias/yii2-firebird)\n- [MariaDB](https://github.com/sam-it/yii2-mariadb)\n\n\nCreating an Active Record <span id=\"creating-active-record\"></span>\n-------------------------\n\nTo represent and fetch the data in the `country` table, create an [Active Record](db-active-record.md)-derived\nclass named `Country`, and save it in the file `models/Country.php`.\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass Country extends ActiveRecord\n{\n}\n```\n\nThe `Country` class extends from [[yii\\db\\ActiveRecord]]. You do not need to write any code inside it! With just the above code, \nYii will guess the associated table name from the class name. \n\n> Info: If no direct match can be made from the class name to the table name, you can\noverride the [[yii\\db\\ActiveRecord::tableName()]] method to explicitly specify the associated table name.\n\nUsing the `Country` class, you can easily manipulate data in the `country` table, as shown in these snippets:\n\n```php\nuse app\\models\\Country;\n\n// get all rows from the country table and order them by \"name\"\n$countries = Country::find()->orderBy('name')->all();\n\n// get the row whose primary key is \"US\"\n$country = Country::findOne('US');\n\n// displays \"United States\"\necho $country->name;\n\n// modifies the country name to be \"U.S.A.\" and save it to database\n$country->name = 'U.S.A.';\n$country->save();\n```\n\n> Info: Active Record is a powerful way to access and manipulate database data in an object-oriented fashion.\nYou may find more detailed information in the [Active Record](db-active-record.md) section. Alternatively, you may also interact with a database using a lower-level data accessing method called [Database Access Objects](db-dao.md).\n\n\nCreating an Action <span id=\"creating-action\"></span>\n------------------\n\nTo expose the country data to end users, you need to create a new action. Instead of placing the new action in the `site`\ncontroller, like you did in the previous sections, it makes more sense to create a new controller specifically\nfor all actions related to the country data. Name this new controller  `CountryController`, and create\nan `index` action in it, as shown in the following.\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\nuse yii\\data\\Pagination;\nuse app\\models\\Country;\n\nclass CountryController extends Controller\n{\n    public function actionIndex()\n    {\n        $query = Country::find();\n\n        $pagination = new Pagination([\n            'defaultPageSize' => 5,\n            'totalCount' => $query->count(),\n        ]);\n\n        $countries = $query->orderBy('name')\n            ->offset($pagination->offset)\n            ->limit($pagination->limit)\n            ->all();\n\n        return $this->render('index', [\n            'countries' => $countries,\n            'pagination' => $pagination,\n        ]);\n    }\n}\n```\n\nSave the above code in the file `controllers/CountryController.php`.\n\nFirst, The `index` action calls `Country::find()`. This [find()](https://www.yiiframework.com/doc/api/2.0/yii-db-activerecord#find()-detail) method creates a [ActiveQuery](https://www.yiiframework.com/doc/api/2.0/yii-db-activequery) query object, which provides methods to access data from the `country` table.\n\nTo limit the number of countries returned in each request, the query object is paginated with the help of a\n[[yii\\data\\Pagination]] object. The `Pagination` object serves two purposes:\n\n* Sets the `offset` and `limit` clauses for the SQL statement represented by the query object so that it only\n  returns a single page of data at a time (at most 5 rows in a page).\n* It's used in the view to display a pager consisting of a list of page buttons, as will be explained in\n  the next subsection.\n  \nNext, [all()](https://www.yiiframework.com/doc/api/2.0/yii-db-activequery#all()-detail) returns all `country` records based on the query results.\n\nAt the end of the code, the `index` action renders a view named `index`, and passes the returned country data as well as the pagination\ninformation to it.\n\n\nCreating a View <span id=\"creating-view\"></span>\n---------------\n\nUnder the `views` directory, first create a sub-directory named `country`. This folder will be used to hold all the\nviews rendered by the `country` controller. Within the `views/country` directory, create a file named `index.php`\ncontaining the following:\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\LinkPager;\n?>\n<h1>Countries</h1>\n<ul>\n<?php foreach ($countries as $country): ?>\n    <li>\n        <?= Html::encode(\"{$country->code} ({$country->name})\") ?>:\n        <?= $country->population ?>\n    </li>\n<?php endforeach; ?>\n</ul>\n\n<?= LinkPager::widget(['pagination' => $pagination]) ?>\n```\n\nThe view has two sections relative to displaying the country data. In the first part, the provided country data is traversed and rendered as an unordered HTML list.\nIn the second part, a [[yii\\widgets\\LinkPager]] widget is rendered using the pagination information passed from the action.\nThe `LinkPager` widget displays a list of page buttons. Clicking on any of them will refresh the country data\nin the corresponding page.\n\n\nTrying it Out <span id=\"trying-it-out\"></span>\n-------------\n\nTo see how all of the above code works, use your browser to access the following URL:\n\n```\nhttps://hostname/index.php?r=country%2Findex\n```\n\n![Country List](images/start-country-list.png)\n\nAt first, you will see a page showing five countries. Below the countries, you will see a pager with four buttons.\nIf you click on the button \"2\", you will see the page display another five countries in the database: the second page of records.\nObserve more carefully and you will find that the URL in the browser also changes to\n\n```\nhttps://hostname/index.php?r=country%2Findex&page=2\n```\n\nBehind the scenes, [[yii\\data\\Pagination|Pagination]] is providing all of the necessary functionality to paginate a data set:\n\n* Initially, [[yii\\data\\Pagination|Pagination]] represents the first page, which reflects the country SELECT query\n  with the clause `LIMIT 5 OFFSET 0`. As a result, the first five countries will be fetched and displayed.\n* The [[yii\\widgets\\LinkPager|LinkPager]] widget renders the page buttons using the URLs\n  created by [[yii\\data\\Pagination::createUrl()|Pagination]]. The URLs will contain the query parameter `page`, which \n  represents the different page numbers.\n* If you click the page button \"2\", a new request for the route `country/index` will be triggered and handled.\n  [[yii\\data\\Pagination|Pagination]] reads the `page` query parameter from the URL and sets the current page number to 2.\n  The new country query will thus have the clause `LIMIT 5 OFFSET 5` and return  the next five countries\n  for display.\n\n\nSummary <span id=\"summary\"></span>\n-------\n\nIn this section, you learned how to work with a database. You also learned how to fetch and display\ndata in pages with the help of [[yii\\data\\Pagination]] and [[yii\\widgets\\LinkPager]].\n\nIn the next section, you will learn how to use the powerful code generation tool, called [Gii](https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide),\nto help you rapidly implement some commonly required features, such as the Create-Read-Update-Delete (CRUD)\noperations for working with the data in a database table. As a matter of fact, the code you have just written can all\nbe automatically generated in Yii using the Gii tool.\n"
  },
  {
    "path": "docs/guide/start-forms.md",
    "content": "Working with Forms\n==================\n\nThis section describes how to create a new page with a form for getting data from users.\nThe page will display a form with a name input field and an email input field.\nAfter getting those two pieces of information from the user, the page will echo the entered values back for confirmation.\n\nTo achieve this goal, besides creating an [action](structure-controllers.md) and\ntwo [views](structure-views.md), you will also create a [model](structure-models.md).\n\nThrough this tutorial, you will learn how to:\n\n* create a [model](structure-models.md) to represent the data entered by a user through a form,\n* declare rules to validate the data entered,\n* build an HTML form in a [view](structure-views.md).\n\n\nCreating a Model <span id=\"creating-model\"></span>\n----------------\n\nThe data to be requested from the user will be represented by an `EntryForm` model class as shown below and\nsaved in the file `models/EntryForm.php`. Please refer to the [Class Autoloading](concept-autoloading.md)\nsection for more details about the class file naming convention.\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse Yii;\nuse yii\\base\\Model;\n\nclass EntryForm extends Model\n{\n    public $name;\n    public $email;\n\n    public function rules()\n    {\n        return [\n            [['name', 'email'], 'required'],\n            ['email', 'email'],\n        ];\n    }\n}\n```\n\nThe class extends from [[yii\\base\\Model]], a base class provided by Yii, commonly used to\nrepresent form data.\n\n> Info: [[yii\\base\\Model]] is used as a parent for model classes *not* associated with database tables.\n[[yii\\db\\ActiveRecord]] is normally the parent for model classes that do correspond to database tables.\n\nThe `EntryForm` class contains two public members, `name` and `email`, which are used to store\nthe data entered by the user. It also contains a method named `rules()`, which returns a set\nof rules for validating the data. The validation rules declared above state that\n\n* both the `name` and `email` values are required\n* the `email` data must be a syntactically valid email address\n\nIf you have an `EntryForm` object populated with the data entered by a user, you may call\nits [[yii\\base\\Model::validate()|validate()]] method to trigger the data validation routines. A data validation\nfailure will set the [[yii\\base\\Model::hasErrors|hasErrors]] property to `true`, and you may learn what validation\nerrors occurred through [[yii\\base\\Model::getErrors|errors]].\n\n```php\n<?php\n$model = new EntryForm();\n$model->name = 'Qiang';\n$model->email = 'bad';\nif ($model->validate()) {\n    // Good!\n} else {\n    // Failure!\n    // Use $model->getErrors()\n}\n```\n\n\nCreating an Action <span id=\"creating-action\"></span>\n------------------\n\nNext, you'll need to create an `entry` action in the `site` controller that will use the new model. The process\nof creating and using actions was explained in the [Saying Hello](start-hello.md) section.\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\nuse app\\models\\EntryForm;\n\nclass SiteController extends Controller\n{\n    // ...existing code...\n\n    public function actionEntry()\n    {\n        $model = new EntryForm();\n\n        if ($model->load(Yii::$app->request->post()) && $model->validate()) {\n            // valid data received in $model\n\n            // do something meaningful here about $model ...\n\n            return $this->render('entry-confirm', ['model' => $model]);\n        } else {\n            // either the page is initially displayed or there is some validation error\n            return $this->render('entry', ['model' => $model]);\n        }\n    }\n}\n```\n\nThe action first creates an `EntryForm` object. It then tries to populate the model\nwith the data from `$_POST`, provided in Yii by [[yii\\web\\Request::post()]].\nIf the model is successfully populated (i.e., if the user has submitted the HTML form), the action will call\n[[yii\\base\\Model::validate()|validate()]] to make sure the values entered are valid.\n\n> Info: The expression `Yii::$app` represents the [application](structure-applications.md) instance,\n  which is a globally accessible singleton. It is also a [service locator](concept-service-locator.md) that\n  provides components such as `request`, `response`, `db`, etc. to support specific functionality.\n  In the above code, the `request` component of the application instance is used to access the `$_POST` data.\n\nIf everything is fine, the action will render a view named `entry-confirm` to confirm the successful submission\nof the data to the user. If no data is submitted or the data contains errors, the `entry` view will\nbe rendered, wherein the HTML form will be shown, along with any validation error messages.\n\n> Note: In this very simple example we just render the confirmation page upon valid data submission. In practice,\n  you should consider using [[yii\\web\\Controller::refresh()|refresh()]] or [[yii\\web\\Controller::redirect()|redirect()]]\n  to avoid [form resubmission problems](https://en.wikipedia.org/wiki/Post/Redirect/Get).\n\n\nCreating Views <span id=\"creating-views\"></span>\n--------------\n\nFinally, create two view files named `entry-confirm` and `entry`. These will be rendered by the `entry` action,\nas just described.\n\nThe `entry-confirm` view simply displays the name and email data. It should be stored in the file `views/site/entry-confirm.php`.\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n<p>You have entered the following information:</p>\n\n<ul>\n    <li><label>Name</label>: <?= Html::encode($model->name) ?></li>\n    <li><label>Email</label>: <?= Html::encode($model->email) ?></li>\n</ul>\n```\n\nThe `entry` view displays an HTML form. It should be stored in the file `views/site/entry.php`.\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n?>\n<?php $form = ActiveForm::begin(); ?>\n\n    <?= $form->field($model, 'name') ?>\n\n    <?= $form->field($model, 'email') ?>\n\n    <div class=\"form-group\">\n        <?= Html::submitButton('Submit', ['class' => 'btn btn-primary']) ?>\n    </div>\n\n<?php ActiveForm::end(); ?>\n```\n\nThe view uses a powerful [widget](structure-widgets.md) called [[yii\\widgets\\ActiveForm|ActiveForm]] to\nbuild the HTML form. The `begin()` and `end()` methods of the widget render the opening and closing\nform tags, respectively. Between the two method calls, input fields are created by the\n[[yii\\widgets\\ActiveForm::field()|field()]] method. The first input field is for the \"name\" data,\nand the second for the \"email\" data. After the input fields, the [[yii\\helpers\\Html::submitButton()]] method\nis called to generate a submit button.\n\n\nTrying it Out <span id=\"trying-it-out\"></span>\n-------------\n\nTo see how it works, use your browser to access the following URL:\n\n```\nhttps://hostname/index.php?r=site%2Fentry\n```\n\nYou will see a page displaying a form with two input fields. In front of each input field, a label indicates what data is to be entered. If you click the submit button without\nentering anything, or if you do not provide a valid email address, you will see an error message displayed next to each problematic input field.\n\n![Form with Validation Errors](images/start-form-validation.png)\n\nAfter entering a valid name and email address and clicking the submit button, you will see a new page\ndisplaying the data that you just entered.\n\n![Confirmation of Data Entry](images/start-entry-confirmation.png)\n\n\n\n### Magic Explained <span id=\"magic-explained\"></span>\n\nYou may wonder how the HTML form works behind the scene, because it seems almost magical that it can\ndisplay a label for each input field and show error messages if you do not enter the data correctly\nwithout reloading the page.\n\nYes, the data validation is initially done on the client-side using JavaScript, and secondarily performed on the server-side via PHP.\n[[yii\\widgets\\ActiveForm]] is smart enough to extract the validation rules that you have declared in `EntryForm`,\nturn them into executable JavaScript code, and use the JavaScript to perform data validation. In case you have disabled\nJavaScript on your browser, the validation will still be performed on the server-side, as shown in\nthe `actionEntry()` method. This ensures data validity in all circumstances.\n\n> Warning: Client-side validation is a convenience that provides for a better user experience. Server-side validation\n  is always required, whether or not client-side validation is in place.\n\nThe labels for input fields are generated by the `field()` method, using the property names from the model.\nFor example, the label `Name` will be generated for the `name` property. \n\nYou may customize a label within a view using \nthe following code:\n\n```php\n<?= $form->field($model, 'name')->label('Your Name') ?>\n<?= $form->field($model, 'email')->label('Your Email') ?>\n```\n\n> Info: Yii provides many such widgets to help you quickly build complex and dynamic views.\n  As you will learn later, writing a new widget is also extremely easy. You may want to turn much of your\n  view code into reusable widgets to simplify view development in future.\n\n\nSummary <span id=\"summary\"></span>\n-------\n\nIn this section of the guide, you have touched every part in the MVC architectural pattern. You have learned how\nto create a model class to represent the user data and validate said data.\n\nYou have also learned how to get data from users and how to display data back in the browser. This is a task that\ncould take you a lot of time when developing an application, but Yii provides powerful widgets\nto make this task very easy.\n\nIn the next section, you will learn how to work with databases, which are needed in nearly every application.\n"
  },
  {
    "path": "docs/guide/start-gii.md",
    "content": "Generating Code with Gii\n========================\n\nThis section will describe how to use [Gii](https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide) to automatically generate code\nthat implements some common Web site features. Using Gii to auto-generate code is simply a matter of entering the right information per the instructions shown on the Gii Web pages.\n\nThrough this tutorial, you will learn how to:\n\n* enable Gii in your application,\n* use Gii to generate an Active Record class,\n* use Gii to generate the code implementing the CRUD operations for a DB table,\n* customize the code generated by Gii.\n\n\nStarting Gii <span id=\"starting-gii\"></span>\n------------\n\n[Gii](https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide) is provided in Yii as a [module](structure-modules.md). You can enable Gii\nby configuring it in the [[yii\\base\\Application::modules|modules]] property of the application. Depending upon how you created your application, you may find the following code is already provided in the `config/web.php` configuration file:\n\n```php\n$config = [ ... ];\n\nif (YII_ENV_DEV) {\n    $config['bootstrap'][] = 'gii';\n    $config['modules']['gii'] = [\n        'class' => 'yii\\gii\\Module',\n    ];\n}\n```\n\nThe above configuration states that when in [development environment](concept-configurations.md#environment-constants),\nthe application should include a module named `gii`, which is of class [[yii\\gii\\Module]].\n\nIf you check the [entry script](structure-entry-scripts.md) `web/index.php` of your application, you will\nfind the following line, which essentially makes `YII_ENV_DEV` to be `true`.\n\n```php\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n```\n\nThanks to that line, your application is in development mode, and will have already enabled Gii, per the above configuration. You can now access Gii via the following URL:\n\n```\nhttps://hostname/index.php?r=gii\n```\n\n> Note: If you are accessing Gii from a machine other than localhost, the access will be denied by default\n> for security purpose. You can configure Gii to add the allowed IP addresses as follows,\n>\n```php\n'gii' => [\n    'class' => 'yii\\gii\\Module',\n    'allowedIPs' => ['127.0.0.1', '::1', '192.168.0.*', '192.168.178.20'] // adjust this to your needs\n],\n```\n\n![Gii](images/start-gii.png)\n\n\nGenerating an Active Record Class <span id=\"generating-ar\"></span>\n---------------------------------\n\nTo use Gii to generate an Active Record class, select the \"Model Generator\" (by clicking the link on the Gii index page). Then fill out the form as follows:\n\n* Table Name: `country`\n* Model Class: `Country`\n\n![Model Generator](images/start-gii-model.png)\n\nNext, click on the \"Preview\" button. You will see `models/Country.php` is listed in the resulting class file to be created. You may click on the name of the class file to preview its content.\n\nWhen using Gii, if you have already created the same file and would be overwriting it, click\nthe `diff` button next to the file name to see the differences between the code to be generated\nand the existing version.\n\n![Model Generator Preview](images/start-gii-model-preview.png)\n\nWhen overwriting an existing file, check the box next to \"overwrite\" and then click  the \"Generate\" button. If creating a new file, you can just click \"Generate\". \n\nNext, you will see\na confirmation page indicating the code has been successfully generated. If you had an existing file, you'll also see a message indicating that it was overwritten with the newly generated code.\n\n\nGenerating CRUD Code <span id=\"generating-crud\"></span>\n--------------------\n\nCRUD stands for Create, Read, Update, and Delete, representing the four common tasks taken with data on most Web sites. To create CRUD functionality using Gii, select the \"CRUD Generator\" (by clicking the link on the Gii index page). For the \"country\" example, fill out the resulting form as follows:\n\n* Model Class: `app\\models\\Country`\n* Search Model Class: `app\\models\\CountrySearch`\n* Controller Class: `app\\controllers\\CountryController`\n\n![CRUD Generator](images/start-gii-crud.png)\n\nNext, click on the \"Preview\" button. You will see a list of files to be generated, as shown below.\n\n![CRUD Generator Preview](images/start-gii-crud-preview.png)\n\nIf you previously created the `controllers/CountryController.php` and\n`views/country/index.php` files (in the databases section of the guide), check the \"overwrite\" box to replace them. (The previous versions did not have full CRUD support.)\n\n\nTrying it Out <span id=\"trying-it-out\"></span>\n-------------\n\nTo see how it works, use your browser to access the following URL:\n\n```\nhttps://hostname/index.php?r=country%2Findex\n```\n\nYou will see a data grid showing the countries from the database table. You may sort the grid,\nor filter it by entering filter conditions in the column headers.\n\nFor each country displayed in the grid, you may choose to view its details, update it, or delete it.\nYou may also click on the \"Create Country\" button on top of the grid to be provided with a form for creating a new country.\n\n![Data Grid of Countries](images/start-gii-country-grid.png)\n\n![Updating a Country](images/start-gii-country-update.png)\n\nThe following is the list of the files generated by Gii, in case you want to investigate how these features are implemented,\nor to customize them:\n\n* Controller: `controllers/CountryController.php`\n* Models: `models/Country.php` and `models/CountrySearch.php`\n* Views: `views/country/*.php`\n\n> Info: Gii is designed to be a highly customizable and extensible code generation tool. Using it wisely\n  can greatly accelerate your application development speed. For more details, please refer to\n  the [Gii](https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide) section.\n\n\nSummary <span id=\"summary\"></span>\n-------\n\nIn this section, you have learned how to use Gii to generate the code that implements complete\nCRUD functionality for content stored in a database table.\n"
  },
  {
    "path": "docs/guide/start-hello.md",
    "content": "Saying Hello\n============\n\nThis section describes how to create a new \"Hello\" page in your application.\nTo achieve this goal, you will create an [action](structure-controllers.md#creating-actions) and\na [view](structure-views.md):\n\n* The application will dispatch the page request to the action\n* and the action will in turn render the view that shows the word \"Hello\" to the end user.\n\nThrough this tutorial, you will learn three things:\n\n1. how to create an [action](structure-controllers.md#creating-actions) to respond to requests,\n2. how to create a [view](structure-views.md) to compose the response's content, and\n3. how an application dispatches requests to [actions](structure-controllers.md#creating-actions).\n\n\nCreating an Action <span id=\"creating-action\"></span>\n------------------\n\nFor the \"Hello\" task, you will create a `say` [action](structure-controllers.md#creating-actions) that reads\na `message` parameter from the request and displays that message back to the user. If the request\ndoes not provide a `message` parameter, the action will display the default \"Hello\" message.\n\n> Info: [Actions](structure-controllers.md#creating-actions) are the objects that end users can directly refer to for\n  execution. Actions are grouped by [controllers](structure-controllers.md). The execution result of\n  an action is the response that an end user will receive.\n\nActions must be declared in [controllers](structure-controllers.md). For simplicity, you may\ndeclare the `say` action in the existing  `SiteController`. This controller is defined\nin the class file `controllers/SiteController.php`. Here is the start of the new action:\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    // ...existing code...\n\n    public function actionSay($message = 'Hello')\n    {\n        return $this->render('say', ['message' => $message]);\n    }\n}\n```\n\nIn the above code, the `say` action is defined as a method named `actionSay` in the `SiteController` class.\nYii uses the prefix `action` to differentiate action methods from non-action methods in a controller class.\nThe name after the `action` prefix maps to the action's ID.\n\nWhen it comes to naming your actions, you should understand how Yii treats action IDs. Action IDs are always\nreferenced in lower case. If an action ID requires multiple words, they will be concatenated by dashes\n(e.g., `create-comment`). Action method IDs are mapped to action names by removing any dashes from the IDs,\ncapitalizing the first letter in each word, and prefixing the resulting string with `action`. For example,\nthe action ID `create-comment` corresponds to the action method name `actionCreateComment`.\n\nThe action method in our example takes a parameter `$message`, whose value defaults to `\"Hello\"` (in exactly\nthe same way you set a default value for any function or method argument in PHP). When the application\nreceives a request and determines that the `say` action is responsible for handling said request, the application will\npopulate this parameter with the same named parameter found in the request. In other words, if the request includes\na `message` parameter with a value of `\"Goodbye\"`, the `$message` variable within the action will be assigned that value.\n\nWithin the action method, [[yii\\web\\Controller::render()|render()]] is called to render\na [view](structure-views.md) file named `say`. The `message` parameter is also passed to the view\nso that it can be used there. The rendering result is returned by the action method. That result will be received\nby the application and displayed to the end user in the browser (as part of a complete HTML page).\n\n\nCreating a View <span id=\"creating-view\"></span>\n---------------\n\n[Views](structure-views.md) are scripts you write to generate a response's content.\nFor the \"Hello\" task, you will create a `say` view that prints the `message` parameter received from the action method:\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n<?= Html::encode($message) ?>\n```\n\nThe `say` view should be saved in the file `views/site/say.php`. When the method [[yii\\web\\Controller::render()|render()]]\nis called in an action, it will look for a PHP file named as `views/ControllerID/ViewName.php`.\n\nNote that in the above code, the `message` parameter is [[yii\\helpers\\Html::encode()|HTML-encoded]]\nbefore being printed. This is necessary as the parameter comes from an end user, making it vulnerable to\n[cross-site scripting (XSS) attacks](https://en.wikipedia.org/wiki/Cross-site_scripting) by embedding\nmalicious JavaScript code in the parameter.\n\nNaturally, you may put more content in the `say` view. The content can consist of HTML tags, plain text, and even PHP statements.\nIn fact, the `say` view is just a PHP script that is executed by the [[yii\\web\\Controller::render()|render()]] method.\nThe content printed by the view script will be returned to the application as the response's result. The application will in turn output this result to the end user.\n\n\nTrying it Out <span id=\"trying-it-out\"></span>\n-------------\n\nAfter creating the action and the view, you may access the new page by accessing the following URL:\n\n```\nhttps://hostname/index.php?r=site%2Fsay&message=Hello+World\n```\n\n![Hello World](images/start-hello-world.png)\n\nThis URL will result in a page displaying \"Hello World\". The page shares the same header and footer as the other application pages.\n\nIf you omit the `message` parameter in the URL, you would see the page display just \"Hello\". This is because `message` is passed as a parameter to the `actionSay()` method, and when it is omitted,\nthe default value of `\"Hello\"` will be used instead.\n\n> Info: The new page shares the same header and footer as other pages because the [[yii\\web\\Controller::render()|render()]]\n  method will automatically embed the result of the `say` view in a so-called [layout](structure-views.md#layouts) which in this\n  case is located at `views/layouts/main.php`.\n\nThe `r` parameter in the above URL requires more explanation. It stands for [route](runtime-routing.md), an application wide unique ID\nthat refers to an action. The route's format is `ControllerID/ActionID`. When the application receives\na request, it will check this parameter, using the `ControllerID` part to determine which controller\nclass should be instantiated to handle the request. Then, the controller will use the `ActionID` part\nto determine which action should be instantiated to do the real work. In this example case, the route `site/say`\nwill be resolved to the `SiteController` controller class and the `say` action. As a result,\nthe `SiteController::actionSay()` method will be called to handle the request.\n\n> Info: Like actions, controllers also have IDs that uniquely identify them in an application.\n  Controller IDs use the same naming rules as action IDs. Controller class names are derived from\n  controller IDs by removing dashes from the IDs, capitalizing the first letter in each word,\n  and suffixing the resulting string with the word `Controller`. For example, the controller ID `post-comment` corresponds\n  to the controller class name `PostCommentController`.\n\n\nSummary <span id=\"summary\"></span>\n-------\n\nIn this section, you have touched the controller and view parts of the MVC architectural pattern.\nYou created an action as part of a controller to handle a specific request. And you also created a view\nto compose the response's content. In this simple example, no model was involved as the only data used was the `message` parameter.\n\nYou have also learned about routes in Yii, which act as the bridge between user requests and controller actions.\n\nIn the next section, you will learn how to create a model, and add a new page containing an HTML form.\n"
  },
  {
    "path": "docs/guide/start-installation.md",
    "content": "Installing Yii\n==============\n\nYou can install Yii in two ways, using the [Composer](https://getcomposer.org/) package manager or by downloading an archive file.\nThe former is the preferred way, as it allows you to install new [extensions](structure-extensions.md) or update Yii by simply running a single command.\n\nStandard installations of Yii result in both the framework and a project template being downloaded and installed.\nA project template is a working Yii project implementing some basic features, such as login, contact form, etc.\nIts code is organized in a recommended way. Therefore, it can serve as a good starting point for your projects.\n    \nIn this and the next few sections, we will describe how to install Yii with the so-called *Basic Project Template* and\nhow to implement new features on top of this template. Yii also provides another template called\nthe [Advanced Project Template](https://www.yiiframework.com/extension/yiisoft/yii2-app-advanced/doc/guide) which is better used in a team development environment\nto develop applications with multiple tiers.\n\n> Info: The Basic Project Template is suitable for developing 90 percent of Web applications. It differs\n  from the Advanced Project Template mainly in how their code is organized. If you are new to Yii, we strongly\n  recommend you stick to the Basic Project Template for its simplicity yet sufficient functionalities.\n\n\nInstalling via Composer <span id=\"installing-via-composer\"></span>\n-----------------------\n\n### Installing Composer\n\nIf you do not already have Composer installed, you may do so by following the instructions at\n[getcomposer.org](https://getcomposer.org/download/). On Linux and Mac OS X, you'll run the following commands:\n\n```bash\ncurl -sS https://getcomposer.org/installer | php\nsudo mv composer.phar /usr/local/bin/composer\n```\n\nOn Windows, you'll download and run [Composer-Setup.exe](https://getcomposer.org/Composer-Setup.exe).\n\nPlease refer to the [Troubleshooting section of the Composer Documentation](https://getcomposer.org/doc/articles/troubleshooting.md)\nif you encounter any problems.\nIf you are new to Composer, we also recommend to read at least the [Basic usage section](https://getcomposer.org/doc/01-basic-usage.md)\nof the Composer documentation.\n\nIn this guide all composer commands assume you have installed composer [globally](https://getcomposer.org/doc/00-intro.md#globally)\nso that it is available as the `composer` command. If you are using the `composer.phar` in the local directory instead,\nyou have to adjust the example commands accordingly.\n\nIf you had Composer already installed before, make sure you use an up to date version. You can update Composer\nby running `composer self-update`.\n\n> Note: During the installation of Yii, Composer will need to request a lot of information from the Github API.\n> The number of requests depends on the number of dependencies your application has and may be bigger than the\n> **Github API rate limit**. If you hit this limit, Composer may ask for your Github login credentials to obtain\n> a Github API access token. On fast connections you may hit this limit earlier than Composer can handle so we\n> recommend to configure the access token before installing Yii.\n> Please refer to the [Composer documentation about Github API tokens](https://getcomposer.org/doc/articles/troubleshooting.md#api-rate-limit-and-oauth-tokens)\n> for instructions on how to do this.\n\n### Installing Yii <span id=\"installing-from-composer\"></span>\n\nWith Composer installed, you can install Yii application template by running the following command\nunder a Web-accessible folder:\n\n```bash\ncomposer create-project --prefer-dist yiisoft/yii2-app-basic basic\n```\n\nThis will install the latest stable version of Yii application template in a directory named `basic`.\nYou can choose a different directory name if you want.\n\n> Info: If the `composer create-project` command fails you may also refer to the \n> [Troubleshooting section of the Composer Documentation](https://getcomposer.org/doc/articles/troubleshooting.md)\n> for common errors. When you have fixed the error, you can resume the aborted installation\n> by running `composer update` inside of the `basic` directory.\n\n> Tip: If you want to install the latest development version of Yii, you may use the following command instead,\n> which adds a [stability option](https://getcomposer.org/doc/04-schema.md#minimum-stability):\n>\n> ```bash\n> composer create-project --prefer-dist --stability=dev yiisoft/yii2-app-basic basic\n> ```\n>\n> Note that the development version of Yii should not be used for production as it may break your running code.\n\n\nInstalling from an Archive File <span id=\"installing-from-archive-file\"></span>\n-------------------------------\n\nInstalling Yii from an archive file involves three steps:\n\n1. Download the archive file from [yiiframework.com](https://www.yiiframework.com/download/).\n2. Unpack the downloaded file to a Web-accessible folder.\n3. Modify the `config/web.php` file by entering a secret key for the `cookieValidationKey` configuration item\n   (this is done automatically if you are installing Yii using Composer):\n\n   ```php\n   // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation\n   'cookieValidationKey' => 'enter your secret key here',\n   ```\n\n\nOther Installation Options <span id=\"other-installation-options\"></span>\n--------------------------\n\nThe above installation instructions show how to install Yii, which also creates a basic Web application that works out of the box.\nThis approach is a good starting point for most projects, either small or big. It is especially suitable if you just\nstart learning Yii.\n\nBut there are other installation options available:\n\n* If you only want to install the core framework and would like to build an entire  application from scratch,\n  you may follow the instructions as explained in [Building Application from Scratch](tutorial-start-from-scratch.md).\n* If you want to start with a more sophisticated application, better suited to team development environments,\n  you may consider installing the [Advanced Project Template](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md).\n\n\nInstalling Assets <span id=\"installing-assets\"></span>\n-----------------\n\nYii relies on [Bower](https://bower.io/) and/or [NPM](https://www.npmjs.com/) packages for the asset (CSS and JavaScript) libraries installation.\nIt uses Composer to obtain these libraries, allowing PHP and CSS/JavaScript package versions to resolve at the same time.\nThis can be achieved either by usage of [asset-packagist.org](https://asset-packagist.org) or [composer asset plugin](https://github.com/fxpio/composer-asset-plugin).\nPlease refer to [Assets documentation](structure-assets.md) for more details.\n\nYou may want to either manage your assets via native Bower/NPM client, use CDN or avoid assets installation entirely.\nIn order to prevent assets installation via Composer, add the following lines to your 'composer.json':\n\n```json\n\"replace\": {\n    \"bower-asset/jquery\": \">=1.11.0\",\n    \"bower-asset/inputmask\": \">=3.2.0\",\n    \"bower-asset/punycode\": \">=1.3.0\",\n    \"bower-asset/yii2-pjax\": \">=2.0.0\"\n},\n```\n\n> Note: in case of bypassing asset installation via Composer, you are responsible for the assets installation and resolving\n> version collisions. Be prepared for possible inconsistencies among asset files from different extensions.\n\n\nVerifying the Installation <span id=\"verifying-installation\"></span>\n--------------------------\n\nAfter installation is done, either configure your web server (see next section) or use the\n[built-in PHP web server](https://www.php.net/manual/en/features.commandline.webserver.php) by running the following\nconsole command while in the project root directory:\n \n```bash\nphp yii serve\n```\n\n> Note: By default the HTTP-server will listen to port 8080. However if that port is already in use or you wish to \nserve multiple applications this way, you might want to specify what port to use. Just add the --port argument:\n\n```bash\nphp yii serve --port=8888\n```\n\nYou can use your browser to access the installed Yii application with the following URL:\n\n```\nhttp://localhost:8080/\n```\n\n![Successful Installation of Yii](images/start-app-installed.png)\n\nYou should see the above \"Congratulations!\" page in your browser. If not, please check if your PHP installation satisfies\nYii's requirements. You can check if the minimum requirements are met using one of the following approaches:\n\n* Copy `/requirements.php` to `/web/requirements.php` and then use a browser to access it via `http://localhost/requirements.php`\n* Run the following commands:\n\n  ```bash\n  cd basic\n  php requirements.php\n  ```\n\nYou should configure your PHP installation so that it meets the minimum requirements of Yii. Most importantly, you\nshould have PHP 5.4 or above. Ideally latest PHP 7. You should also install the [PDO PHP Extension](https://www.php.net/manual/en/pdo.installation.php)\nand a corresponding database driver (such as `pdo_mysql` for MySQL databases), if your application needs a database.\n\n\nConfiguring Web Servers <span id=\"configuring-web-servers\"></span>\n-----------------------\n\n> Info: You may skip this subsection for now if you are just test driving Yii with no intention\n  of deploying it to a production server.\n\nThe application installed according to the above instructions should work out of box with either\nan [Apache HTTP server](https://httpd.apache.org/) or an [Nginx HTTP server](https://nginx.org/), on\nWindows, Mac OS X, or Linux running PHP 5.4 or higher. Yii 2.0 is also compatible with facebook's\n[HHVM](https://hhvm.com/). However, there are some edge cases where HHVM behaves different than native\nPHP, so you have to take some extra care when using HHVM.\n\nOn a production server, you may want to configure your Web server so that the application can be accessed\nvia the URL `https://www.example.com/index.php` instead of `https://www.example.com/basic/web/index.php`. Such configuration\nrequires pointing the document root of your Web server to the `basic/web` folder. You may also\nwant to hide `index.php` from the URL, as described in the [Routing and URL Creation](runtime-routing.md) section.\nIn this subsection, you'll learn how to configure your Apache or Nginx server to achieve these goals.\n\n> Info: By setting `basic/web` as the document root, you also prevent end users from accessing\nyour private application code and sensitive data files that are stored in the sibling directories\nof `basic/web`. Denying access to those other folders is a security improvement.\n\n> Info: If your application will run in a shared hosting environment where you do not have permission\nto modify its Web server configuration, you may still adjust the structure of your application for better security. Please refer to\nthe [Shared Hosting Environment](tutorial-shared-hosting.md) section for more details.\n\n> Info: If you are running your Yii application behind a reverse proxy, you might need to configure\n> [Trusted proxies and headers](runtime-requests.md#trusted-proxies) in the request component.\n\n### Recommended Apache Configuration <span id=\"recommended-apache-configuration\"></span>\n\nUse the following configuration in Apache's `httpd.conf` file or within a virtual host configuration. Note that you\nshould replace `path/to/basic/web` with the actual path for `basic/web`.\n\n```apache\n# Set document root to be \"basic/web\"\nDocumentRoot \"path/to/basic/web\"\n\n<Directory \"path/to/basic/web\">\n    # use mod_rewrite for pretty URL support\n    RewriteEngine on\n    \n    # if $showScriptName is false in UrlManager, do not allow accessing URLs with script name\n    RewriteRule ^index.php/ - [L,R=404]\n    \n    # If a directory or a file exists, use the request directly\n    RewriteCond %{REQUEST_FILENAME} !-f\n    RewriteCond %{REQUEST_FILENAME} !-d\n    \n    # Otherwise forward the request to index.php\n    RewriteRule . index.php\n\n    # ...other settings...\n</Directory>\n```\n\n\n### Recommended Nginx Configuration <span id=\"recommended-nginx-configuration\"></span>\n\nTo use [Nginx](https://wiki.nginx.org/), you should install PHP as an [FPM SAPI](https://www.php.net/install.fpm).\nYou may use the following Nginx configuration, replacing `path/to/basic/web` with the actual path for \n`basic/web` and `mysite.test` with the actual hostname to serve.\n\n```nginx\nserver {\n    charset utf-8;\n    client_max_body_size 128M;\n\n    listen 80; ## listen for ipv4\n    #listen [::]:80 default_server ipv6only=on; ## listen for ipv6\n\n    server_name mysite.test;\n    root        /path/to/basic/web;\n    index       index.php;\n\n    access_log  /path/to/basic/log/access.log;\n    error_log   /path/to/basic/log/error.log;\n\n    location / {\n        # Redirect everything that isn't a real file to index.php\n        try_files $uri $uri/ /index.php$is_args$args;\n    }\n\n    # uncomment to avoid processing of calls to non-existing static files by Yii\n    #location ~ \\.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ {\n    #    try_files $uri =404;\n    #}\n    #error_page 404 /404.html;\n\n    # deny accessing php files for the /assets directory\n    location ~ ^/assets/.*\\.php$ {\n        deny all;\n    }\n\n    location ~ \\.php$ {\n        include fastcgi_params;\n        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;\n        fastcgi_pass 127.0.0.1:9000;\n        #fastcgi_pass unix:/var/run/php5-fpm.sock;\n        try_files $uri =404;\n    }\n\n    location ~* /\\. {\n        deny all;\n    }\n}\n```\n\nWhen using this configuration, you should also set `cgi.fix_pathinfo=0` in the `php.ini` file\nin order to avoid many unnecessary system `stat()` calls.\n\nAlso note that when running an HTTPS server, you need to add `fastcgi_param HTTPS on;` so that Yii\ncan properly detect if a connection is secure.\n\n### Recommended NGINX Unit Configuration <span id=\"recommended-nginx-unit-configuration\"></span>\n\nYou can run Yii-based apps using [NGINX Unit](https://unit.nginx.org/) with a PHP language module.\nHere is a sample configuration.\n\n```json\n{\n    \"listeners\": {\n        \"*:80\": {\n            \"pass\": \"routes/yii\"\n        }\n    },\n\n    \"routes\": {\n        \"yii\": [\n            {\n                \"match\": {\n                    \"uri\": [\n                        \"!/assets/*\",\n                        \"*.php\",\n                        \"*.php/*\"\n                    ]\n                },\n\n                \"action\": {\n                    \"pass\": \"applications/yii/direct\"\n                }\n            },\n            {\n                \"action\": {\n                    \"share\": \"/path/to/app/web/\",\n                    \"fallback\": {\n                        \"pass\": \"applications/yii/index\"\n                    }\n                }\n            }\n        ]\n    },\n\n    \"applications\": {\n        \"yii\": {\n            \"type\": \"php\",\n            \"user\": \"www-data\",\n            \"targets\": {\n                \"direct\": {\n                    \"root\": \"/path/to/app/web/\"\n                },\n\n                \"index\": {\n                    \"root\": \"/path/to/app/web/\",\n                    \"script\": \"index.php\"\n                }\n            }\n        }\n    }\n}\n```\n\nYou can also [set up](https://unit.nginx.org/configuration/#php) your PHP environment or supply a custom `php.ini` in the same configuration.\n\n### IIS Configuration <span id=\"iis-configuration\"></span>\n\nIt's recommended to host the application in a virtual host (Web site) where document root points to `path/to/app/web` folder and that Web site is configured to run PHP. In that `web` folder you have to place a file named `web.config` i.e. `path/to/app/web/web.config`. Content of the file should be the following:\n\n```xml\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration>\n<system.webServer>\n<directoryBrowse enabled=\"false\" />\n  <rewrite>\n    <rules>\n      <rule name=\"Hide Yii Index\" stopProcessing=\"true\">\n        <match url=\".\" ignoreCase=\"false\" />\n        <conditions>\n        <add input=\"{REQUEST_FILENAME}\" matchType=\"IsFile\" \n              ignoreCase=\"false\" negate=\"true\" />\n        <add input=\"{REQUEST_FILENAME}\" matchType=\"IsDirectory\" \n              ignoreCase=\"false\" negate=\"true\" />\n        </conditions>\n        <action type=\"Rewrite\" url=\"index.php\" appendQueryString=\"true\" />\n      </rule> \n    </rules>\n  </rewrite>\n</system.webServer>\n</configuration>\n```\nAlso the following list of Microsoft's official resources could be useful in order to configure PHP on IIS:\n 1. [How to set up your first IIS Web site](https://docs.microsoft.com/en-us/iis/manage/creating-websites/scenario-build-a-static-website-on-iis)\n 2. [Configure a PHP Website on IIS](https://docs.microsoft.com/en-us/iis/application-frameworks/scenario-build-a-php-website-on-iis/configure-a-php-website-on-iis)\n"
  },
  {
    "path": "docs/guide/start-looking-ahead.md",
    "content": "Looking Ahead\n=============\n\nIf you've read through the entire \"Getting Started\" chapter, you have now created a complete Yii application. In the process, you have learned how to implement some commonly\nneeded features, such as getting data from users via an HTML form, fetching data from a database, and\ndisplaying data in a paginated fashion. You have also learned how to use [Gii](https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide) to generate\ncode automatically. Using Gii for code generation turns the bulk of your Web development process into a task as simple as just filling out some forms. \n\nThis section will summarize the Yii resources available to help you be more productive when using the framework.\n\n* Documentation\n    - [The Definitive Guide](https://www.yiiframework.com/doc-2.0/guide-README.html):\n      As the name indicates, the guide precisely defines how Yii should work and provides general guidance\n      about using Yii. It is the single most important Yii tutorial, and one that you should read \n      before writing any Yii code.\n    - [The Class Reference](https://www.yiiframework.com/doc-2.0/index.html):\n      This specifies the usage of every class provided by Yii. It should be mainly used when you are writing\n      code and want to understand the usage of a particular class, method, property. Usage of the class reference is best only after a contextual understanding of the entire framework.\n    - [The Wiki Articles](https://www.yiiframework.com/wiki/?tag=yii2):\n      The wiki articles are written by Yii users based on their own experiences. Most of them are written\n      like cookbook recipes, and show how to solve particular problems using Yii. While the quality of these\n      articles may not be as good as the Definitive Guide, they are useful in that they cover broader topics\n      and can often provide ready-to-use solutions.\n    - [Books](https://www.yiiframework.com/books)\n* [Extensions](https://www.yiiframework.com/extensions/):\n  Yii boasts a library of thousands of user-contributed extensions that can be easily plugged into your applications, thereby making your application development even faster and easier.\n* Community\n    - Forum: <https://forum.yiiframework.com/>\n    - IRC chat: The #yii channel on the Libera (<ircs://irc.libera.chat:6697/yii>)\n    - Slack chanel: <https://join.slack.com/t/yii/shared_invite/enQtMzQ4MDExMDcyNTk2LTc0NDQ2ZTZhNjkzZDgwYjE4YjZlNGQxZjFmZDBjZTU3NjViMDE4ZTMxNDRkZjVlNmM1ZTA1ODVmZGUwY2U3NDA>\n    - Gitter chat: <https://gitter.im/yiisoft/yii2>\n    - GitHub: <https://github.com/yiisoft/yii2>\n    - Facebook: <https://www.facebook.com/groups/yiitalk/>\n    - Twitter: <https://twitter.com/yiiframework>\n    - LinkedIn: <https://www.linkedin.com/groups/yii-framework-1483367>\n    - Stackoverflow: <https://stackoverflow.com/questions/tagged/yii2>\n"
  },
  {
    "path": "docs/guide/start-prerequisites.md",
    "content": "# What do you need to know\n\nThe Yii learning curve is not as steep as other PHP frameworks but still there are some things you should learn before starting with Yii.\n\n## PHP\n\nYii is a PHP framework so make sure you [read and understand language reference](https://www.php.net/manual/en/langref.php).\nWhen developing with Yii you will be writing code in an object-oriented fashion, so make sure you are familiar with [Classes and Objects](https://www.php.net/manual/en/language.oop5.basic.php) as well as [namespaces](https://www.php.net/manual/en/language.namespaces.php).\n\n## Object oriented programming\n\nBasic understanding of object-oriented programming is required. If you're not familiar with it, check one of the many\ntutorials available such as [the one from tuts+](https://code.tutsplus.com/tutorials/object-oriented-php-for-beginners--net-12762).\n\nNote that the more complicated your application is the more advanced OOP concepts you should learn in order to successfully\nmanage that complexity.\n\n## Command line and composer\n\nYii extensively uses de-facto standard PHP package manager, [Composer](https://getcomposer.org/) so make sure you read\nand understand its [guide](https://getcomposer.org/doc/01-basic-usage.md). If you are not familiar with using command line it is time to start trying. Once you\nlearn the basics you'll never want to work without it.\n\n"
  },
  {
    "path": "docs/guide/start-workflow.md",
    "content": "Running Applications\n====================\n\nAfter installing Yii, you have a working Yii application that can be accessed via\nthe URL `https://hostname/basic/web/index.php` or `https://hostname/index.php`, depending\nupon your configuration. This section will introduce the application's built-in functionality,\nhow the code is organized, and how the application handles requests in general.\n\n> Info: For simplicity, throughout this \"Getting Started\" tutorial, it's assumed that you have set `basic/web`\n  as the document root of your Web server, and configured the URL for accessing\n  your application to be `https://hostname/index.php` or something similar.\n  For your needs, please adjust the URLs in our descriptions accordingly.\n  \nNote that unlike framework itself, after project template is installed it's all yours. You're free to add or delete\ncode and overall modify it as you need.\n\n\nFunctionality <span id=\"functionality\"></span>\n-------------\n\nThe basic application installed contains four pages:\n\n* the homepage, displayed when you access the URL `https://hostname/index.php`,\n* the \"About\" page,\n* the \"Contact\" page, which displays a contact form that allows end users to contact you via email,\n* and the \"Login\" page, which displays a login form that can be used to authenticate end users. Try logging in\n  with \"admin/admin\", and you will find the \"Login\" main menu item will change to \"Logout\".\n\nThese pages share a common header and footer. The header contains a main menu bar to allow navigation\namong different pages.\n\nYou should also see a toolbar at the bottom of the browser window.\nThis is a useful [debugger tool](https://github.com/yiisoft/yii2-debug/blob/master/docs/guide/README.md) provided by Yii to record and display a lot of debugging information, such as log messages, response statuses, the database queries run, and so on.\n\nAdditionally to the web application, there is a console script called `yii`, which is located in the applications base directory.\nThis script can be used to run background and maintenance tasks for the application, which are described\nin the [Console Application Section](tutorial-console.md).\n\n\nApplication Structure <span id=\"application-structure\"></span>\n---------------------\n\nThe most important directories and files in your application are (assuming the application's root directory is `basic`):\n\n```\nbasic/                  application base path\n    composer.json       used by Composer, describes package information\n    config/             contains application and other configurations\n        console.php     the console application configuration\n        web.php         the Web application configuration\n    commands/           contains console command classes\n    controllers/        contains controller classes\n    models/             contains model classes\n    runtime/            contains files generated by Yii during runtime, such as logs and cache files\n    vendor/             contains the installed Composer packages, including the Yii framework itself\n    views/              contains view files\n    web/                application Web root, contains Web accessible files\n        assets/         contains published asset files (javascript and css) by Yii\n        index.php       the entry (or bootstrap) script for the application\n    yii                 the Yii console command execution script\n```\n\nIn general, the files in the application can be divided into two types: those under `basic/web` and those\nunder other directories. The former can be directly accessed via HTTP (i.e., in a browser), while the latter can not and should not be.\n\nYii implements the [model-view-controller (MVC)](https://wikipedia.org/wiki/Model-view-controller) architectural pattern,\nwhich is reflected in the above directory organization. The `models` directory contains all [model classes](structure-models.md),\nthe `views` directory contains all [view scripts](structure-views.md), and the `controllers` directory contains\nall [controller classes](structure-controllers.md).\n\nThe following diagram shows the static structure of an application.\n\n![Static Structure of Application](images/application-structure.png)\n\nEach application has an entry script `web/index.php` which is the only Web accessible PHP script in the application.\nThe entry script takes an incoming request and creates an [application](structure-applications.md) instance to handle it.\nThe [application](structure-applications.md) resolves the request with the help of its [components](concept-components.md),\nand dispatches the request to the MVC elements. [Widgets](structure-widgets.md) are used in the [views](structure-views.md)\nto help build complex and dynamic user interface elements.\n\n\nRequest Lifecycle <span id=\"request-lifecycle\"></span>\n-----------------\n\nThe following diagram shows how an application handles a request.\n\n![Request Lifecycle](images/request-lifecycle.png)\n\n1. A user makes a request to the [entry script](structure-entry-scripts.md) `web/index.php`.\n2. The entry script loads the application [configuration](concept-configurations.md) and creates\n   an [application](structure-applications.md) instance to handle the request.\n3. The application resolves the requested [route](runtime-routing.md) with the help of\n   the [request](runtime-requests.md) application component.\n4. The application creates a [controller](structure-controllers.md) instance to handle the request.\n5. The controller creates an [action](structure-controllers.md) instance and performs the filters for the action.\n6. If any filter fails, the action is cancelled.\n7. If all filters pass, the action is executed.\n8. The action loads some data models, possibly from a database.\n9. The action renders a view, providing it with the data models.\n10. The rendered result is returned to the [response](runtime-responses.md) application component.\n11. The response component sends the rendered result to the user's browser.\n\n"
  },
  {
    "path": "docs/guide/structure-application-components.md",
    "content": "Application Components\n======================\n\nApplications are [service locators](concept-service-locator.md). They host a set of the so-called\n*application components* that provide different services for processing requests. For example,\nthe `urlManager` component is responsible for routing Web requests to appropriate controllers;\nthe `db` component provides DB-related services; and so on.\n\nEach application component has an ID that uniquely identifies itself among other application components\nin the same application. You can access an application component through the expression:\n\n```php\n\\Yii::$app->componentID\n```\n\nFor example, you can use `\\Yii::$app->db` to get the [[yii\\db\\Connection|DB connection]],\nand `\\Yii::$app->cache` to get the [[yii\\caching\\Cache|primary cache]] registered with the application.\n\nAn application component is created the first time it is accessed through the above expression. Any\nfurther accesses will return the same component instance.\n\nApplication components can be any objects. You can register them by configuring\nthe [[yii\\base\\Application::components]] property in [application configurations](structure-applications.md#application-configurations).\nFor example,\n\n```php\n[\n    'components' => [\n        // register \"cache\" component using a class name\n        'cache' => 'yii\\caching\\ApcCache',\n\n        // register \"db\" component using a configuration array\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=demo',\n            'username' => 'root',\n            'password' => '',\n        ],\n\n        // register \"search\" component using an anonymous function\n        'search' => function () {\n            return new app\\components\\SolrService;\n        },\n    ],\n]\n```\n\n> Info: While you can register as many application components as you want, you should do this judiciously.\n  Application components are like global variables. Using too many application components can potentially\n  make your code harder to test and maintain. In many cases, you can simply create a local component\n  and use it when needed.\n\n\n## Bootstrapping Components <span id=\"bootstrapping-components\"></span>\n\nAs mentioned above, an application component will only be instantiated when it is being accessed the first time.\nIf it is not accessed at all during a request, it will not be instantiated. Sometimes, however, you may want\nto instantiate an application component for every request, even if it is not explicitly accessed.\nTo do so, you may list its ID in the [[yii\\base\\Application::bootstrap|bootstrap]] property of the application.\n\nYou can also use Closures to bootstrap customized components. Returning an instantiated component is not \nrequired. A Closure can also be used simply for running code after [[yii\\base\\Application]] instantiation.\n\nFor example, the following application configuration makes sure the `log` component is always loaded:\n\n```php\n[\n    'bootstrap' => [\n        'log',\n        function($app){\n            return new ComponentX();\n        },\n        function($app){\n            // some code\n           return;\n        }\n    ],\n    'components' => [\n        'log' => [\n            // configuration for \"log\" component\n        ],\n    ],\n]\n```\n\n\n## Core Application Components <span id=\"core-application-components\"></span>\n\nYii defines a set of *core* application components with fixed IDs and default configurations. For example,\nthe [[yii\\web\\Application::request|request]] component is used to collect information about\na user request and resolve it into a [route](runtime-routing.md); the [[yii\\base\\Application::db|db]]\ncomponent represents a database connection through which you can perform database queries.\nIt is with help of these core application components that Yii applications are able to handle user requests.\n\nBelow is the list of the predefined core application components. You may configure and customize them\nlike you do with normal application components. When you are configuring a core application component,\nif you do not specify its class, the default one will be used.\n\n* [[yii\\web\\AssetManager|assetManager]]: manages asset bundles and asset publishing.\n  Please refer to the [Assets](structure-assets.md) section for more details.\n* [[yii\\db\\Connection|db]]: represents a database connection through which you can perform DB queries.\n  Note that when you configure this component, you must specify the component class as well as other required\n  component properties, such as [[yii\\db\\Connection::dsn]].\n  Please refer to the [Database Access Objects](db-dao.md) section for more details.\n* [[yii\\base\\Application::errorHandler|errorHandler]]: handles PHP errors and exceptions.\n  Please refer to the [Handling Errors](runtime-handling-errors.md) section for more details.\n* [[yii\\i18n\\Formatter|formatter]]: formats data when they are displayed to end users. For example, a number\n  may be displayed with thousand separator, a date may be formatted in long format.\n  Please refer to the [Data Formatting](output-formatting.md) section for more details.\n* [[yii\\i18n\\I18N|i18n]]: supports message translation and formatting.\n  Please refer to the [Internationalization](tutorial-i18n.md) section for more details.\n* [[yii\\log\\Dispatcher|log]]: manages log targets.\n  Please refer to the [Logging](runtime-logging.md) section for more details.\n* [[yii\\swiftmailer\\Mailer|mailer]]: supports mail composing and sending.\n  Please refer to the [Mailing](tutorial-mailing.md) section for more details.\n* [[yii\\base\\Application::response|response]]: represents the response being sent to end users.\n  Please refer to the [Responses](runtime-responses.md) section for more details.\n* [[yii\\base\\Application::request|request]]: represents the request received from end users.\n  Please refer to the [Requests](runtime-requests.md) section for more details.\n* [[yii\\web\\Session|session]]: represents the session information. This component is only available\n  in [[yii\\web\\Application|Web applications]].\n  Please refer to the [Sessions and Cookies](runtime-sessions-cookies.md) section for more details.\n* [[yii\\web\\UrlManager|urlManager]]: supports URL parsing and creation.\n  Please refer to the [Routing and URL Creation](runtime-routing.md) section for more details.\n* [[yii\\web\\User|user]]: represents the user authentication information. This component is only available\n  in [[yii\\web\\Application|Web applications]].\n  Please refer to the [Authentication](security-authentication.md) section for more details.\n* [[yii\\web\\View|view]]: supports view rendering.\n  Please refer to the [Views](structure-views.md) section for more details.\n"
  },
  {
    "path": "docs/guide/structure-applications.md",
    "content": "Applications\n============\n\nApplications are objects that govern the overall structure and lifecycle of Yii application systems.\nEach Yii application system contains a single application object which is created in\nthe [entry script](structure-entry-scripts.md) and is globally accessible through the expression `\\Yii::$app`.\n\n> Info: Depending on the context, when we say \"an application\", it can mean either an application\n  object or an application system.\n\nThere are two types of applications: [[yii\\web\\Application|Web applications]] and\n[[yii\\console\\Application|console applications]]. As the names indicate, the former mainly handles\nWeb requests, while the latter handles console command requests.\n\n\n## Application Configurations <span id=\"application-configurations\"></span>\n\nWhen an [entry script](structure-entry-scripts.md) creates an application, it will load\na [configuration](concept-configurations.md) and apply it to the application, as follows:\n\n```php\nrequire __DIR__ . '/../vendor/autoload.php';\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n\n// load application configuration\n$config = require __DIR__ . '/../config/web.php';\n\n// instantiate and configure the application\n(new yii\\web\\Application($config))->run();\n```\n\nLike normal [configurations](concept-configurations.md), application configurations specify how\nto initialize properties of application objects. Because application configurations are often\nvery complex, they are usually kept in [configuration files](concept-configurations.md#configuration-files),\nlike the `web.php` file in the above example.\n\n\n## Application Properties <span id=\"application-properties\"></span>\n\nThere are many important application properties that you should configure in application configurations.\nThese properties typically describe the environment that applications are running in.\nFor example, applications need to know how to load [controllers](structure-controllers.md),\nwhere to store temporary files, etc. In the following, we will summarize these properties.\n\n\n### Required Properties <span id=\"required-properties\"></span>\n\nIn any application, you should at least configure two properties: [[yii\\base\\Application::id|id]]\nand [[yii\\base\\Application::basePath|basePath]].\n\n\n#### [[yii\\base\\Application::id|id]] <span id=\"id\"></span>\n\nThe [[yii\\base\\Application::id|id]] property specifies a unique ID that differentiates an application\nfrom others. It is mainly used programmatically. Although not a requirement, for best interoperability\nit is recommended that you use only alphanumeric characters when specifying an application ID.\n\n\n#### [[yii\\base\\Application::basePath|basePath]] <span id=\"basePath\"></span>\n\nThe [[yii\\base\\Application::basePath|basePath]] property specifies the root directory of an application.\nIt is the directory that contains all protected source code of an application system. Under this directory,\nyou normally will see sub-directories such as `models`, `views`, and `controllers`, which contain source code\ncorresponding to the MVC pattern.\n\nYou may configure the [[yii\\base\\Application::basePath|basePath]] property using a directory path\nor a [path alias](concept-aliases.md). In both forms, the corresponding directory must exist, or an exception\nwill be thrown. The path will be normalized by calling the `realpath()` function.\n\nThe [[yii\\base\\Application::basePath|basePath]] property is often used to derive other important\npaths (e.g. the runtime path). For this reason, a path alias named `@app` is predefined to represent this\npath. Derived paths may then be formed using this alias (e.g. `@app/runtime` to refer to the runtime directory).\n\n\n### Important Properties <span id=\"important-properties\"></span>\n\nThe properties described in this subsection often need to be configured because they differ across\ndifferent applications.\n\n\n#### [[yii\\base\\Application::aliases|aliases]] <span id=\"aliases\"></span>\n\nThis property allows you to define a set of [aliases](concept-aliases.md) in terms of an array.\nThe array keys are alias names, and the array values are the corresponding path definitions.\nFor example:\n\n```php\n[\n    'aliases' => [\n        '@name1' => 'path/to/path1',\n        '@name2' => 'path/to/path2',\n    ],\n]\n```\n\nThis property is provided so that you can define aliases in terms of application configurations instead of\nby calling the [[Yii::setAlias()]] method.\n\n\n#### [[yii\\base\\Application::bootstrap|bootstrap]] <span id=\"bootstrap\"></span>\n\nThis is a very useful property. It allows you to specify an array of components that should\nbe run during the application [[yii\\base\\Application::bootstrap()|bootstrapping process]].\nFor example, if you want a [module](structure-modules.md) to customize the [URL rules](runtime-routing.md),\nyou may list its ID as an element in this property.\n\nEach component listed in this property may be specified in one of the following formats:\n\n- an application component ID as specified via [components](#components),\n- a module ID as specified via [modules](#modules),\n- a class name,\n- a configuration array,\n- an anonymous function that creates and returns a component.\n\nFor example:\n\n```php\n[\n    'bootstrap' => [\n        // an application component ID or module ID\n        'demo',\n\n        // a class name\n        'app\\components\\Profiler',\n\n        // a configuration array\n        [\n            'class' => 'app\\components\\Profiler',\n            'level' => 3,\n        ],\n\n        // an anonymous function\n        function () {\n            return new app\\components\\Profiler();\n        }\n    ],\n]\n```\n\n> Info: If a module ID is the same as an application component ID, the application component will be used during\n> the bootstrapping process. If you want to use the module instead, you may specify it using an anonymous function\n> like the following:\n>\n> ```php\n> [\n>     function () {\n>         return Yii::$app->getModule('user');\n>     },\n> ]\n> ```\n\n\nDuring the bootstrapping process, each component will be instantiated. If the component class\nimplements [[yii\\base\\BootstrapInterface]], its [[yii\\base\\BootstrapInterface::bootstrap()|bootstrap()]] method\nwill also be called.\n\nAnother practical example is in the application configuration for the [Basic Project Template](start-installation.md),\nwhere the `debug` and `gii` modules are configured as bootstrapping components when the application is running\nin the development environment:\n\n```php\nif (YII_ENV_DEV) {\n    // configuration adjustments for 'dev' environment\n    $config['bootstrap'][] = 'debug';\n    $config['modules']['debug'] = 'yii\\debug\\Module';\n\n    $config['bootstrap'][] = 'gii';\n    $config['modules']['gii'] = 'yii\\gii\\Module';\n}\n```\n\n> Note: Putting too many components in `bootstrap` will degrade the performance of your application because\n  for each request, the same set of components need to be run. So use bootstrapping components judiciously.\n\n\n#### [[yii\\web\\Application::catchAll|catchAll]] <span id=\"catchAll\"></span>\n\nThis property is supported by [[yii\\web\\Application|Web applications]] only. It specifies\na [controller action](structure-controllers.md) which should handle all user requests. This is mainly\nused when the application is in maintenance mode and needs to handle all incoming requests via a single action.\n\nThe configuration is an array whose first element specifies the route of the action.\nThe rest of the array elements (key-value pairs) specify the parameters to be bound to the action. For example:\n\n```php\n[\n    'catchAll' => [\n        'offline/notice',\n        'param1' => 'value1',\n        'param2' => 'value2',\n    ],\n]\n```\n\n> Info: Debug panel on development environment will not work when this property is enabled.\n\n#### [[yii\\base\\Application::components|components]] <span id=\"components\"></span>\n\nThis is the single most important property. It allows you to register a list of named components\ncalled [application components](structure-application-components.md) that you can use in other places. For example:\n\n```php\n[\n    'components' => [\n        'cache' => [\n            'class' => 'yii\\caching\\FileCache',\n        ],\n        'user' => [\n            'identityClass' => 'app\\models\\User',\n            'enableAutoLogin' => true,\n        ],\n    ],\n]\n```\n\nEach application component is specified as a key-value pair in the array. The key represents the component ID,\nwhile the value represents the component class name or [configuration](concept-configurations.md).\n\nYou can register any component with an application, and the component can later be accessed globally\nusing the expression `\\Yii::$app->componentID`.\n\nPlease read the [Application Components](structure-application-components.md) section for details.\n\n\n#### [[yii\\base\\Application::controllerMap|controllerMap]] <span id=\"controllerMap\"></span>\n\nThis property allows you to map a controller ID to an arbitrary controller class. By default, Yii maps\ncontroller IDs to controller classes based on a [convention](#controllerNamespace) (e.g. the ID `post` would be mapped\nto `app\\controllers\\PostController`). By configuring this property, you can break the convention for\nspecific controllers. In the following example, `account` will be mapped to\n`app\\controllers\\UserController`, while `article` will be mapped to `app\\controllers\\PostController`.\n\n```php\n[\n    'controllerMap' => [\n        'account' => 'app\\controllers\\UserController',\n        'article' => [\n            'class' => 'app\\controllers\\PostController',\n            'enableCsrfValidation' => false,\n        ],\n    ],\n]\n```\n\nThe array keys of this property represent the controller IDs, while the array values represent the corresponding\ncontroller class names or [configurations](concept-configurations.md).\n\n\n#### [[yii\\base\\Application::controllerNamespace|controllerNamespace]] <span id=\"controllerNamespace\"></span>\n\nThis property specifies the default namespace under which controller classes should be located. It defaults to\n`app\\controllers`. If a controller ID is `post`, by convention the corresponding controller class name (without\nnamespace) would be `PostController`, and the fully qualified class name would be `app\\controllers\\PostController`.\n\nController classes may also be located under sub-directories of the directory corresponding to this namespace.\nFor example, given a controller ID `admin/post`, the corresponding fully qualified controller class would\nbe `app\\controllers\\admin\\PostController`.\n\nIt is important that the fully qualified controller classes should be [autoloadable](concept-autoloading.md)\nand the actual namespace of your controller classes match the value of this property. Otherwise,\nyou will receive a \"Page Not Found\" error when accessing the application.\n\nIn case you want to break the convention as described above, you may configure the [controllerMap](#controllerMap)\nproperty.\n\n\n#### [[yii\\base\\Application::language|language]] <span id=\"language\"></span>\n\nThis property specifies the language in which the application should display content to end users.\nThe default value of this property is `en`, meaning English. You should configure this property\nif your application needs to support multiple languages.\n\nThe value of this property determines various [internationalization](tutorial-i18n.md) aspects,\nincluding message translation, date formatting, number formatting, etc. For example, the [[yii\\jui\\DatePicker]] widget\nwill use this property value by default to determine in which language the calendar should be displayed and how\nthe date should be formatted.\n\nIt is recommended that you specify a language in terms of an [IETF language tag](https://en.wikipedia.org/wiki/IETF_language_tag).\nFor example, `en` stands for English, while `en-US` stands for English (United States).\n\nMore details about this property can be found in the [Internationalization](tutorial-i18n.md) section.\n\n\n#### [[yii\\base\\Application::modules|modules]] <span id=\"modules\"></span>\n\nThis property specifies the [modules](structure-modules.md) that the application contains.\n\nThe property takes an array of module classes or [configurations](concept-configurations.md) with the array keys\nbeing the module IDs. For example:\n\n```php\n[\n    'modules' => [\n        // a \"booking\" module specified with the module class\n        'booking' => 'app\\modules\\booking\\BookingModule',\n\n        // a \"comment\" module specified with a configuration array\n        'comment' => [\n            'class' => 'app\\modules\\comment\\CommentModule',\n            'db' => 'db',\n        ],\n    ],\n]\n```\n\nPlease refer to the [Modules](structure-modules.md) section for more details.\n\n\n#### [[yii\\base\\Application::name|name]] <span id=\"name\"></span>\n\nThis property specifies the application name that may be displayed to end users. Unlike the\n[[yii\\base\\Application::id|id]] property, which should take a unique value, the value of this property is mainly for\ndisplay purposes; it does not need to be unique.\n\nYou do not always need to configure this property if none of your code is using it.\n\n\n#### [[yii\\base\\Application::params|params]] <span id=\"params\"></span>\n\nThis property specifies an array of globally accessible application parameters. Instead of using hardcoded\nnumbers and strings everywhere in your code, it is a good practice to define them as application parameters\nin a single place and use the parameters in places where needed. For example, you may define the thumbnail\nimage size as a parameter like the following:\n\n```php\n[\n    'params' => [\n        'thumbnail.size' => [128, 128],\n    ],\n]\n```\n\nThen in your code where you need to use the size value, you can simply use code like the following:\n\n```php\n$size = \\Yii::$app->params['thumbnail.size'];\n$width = \\Yii::$app->params['thumbnail.size'][0];\n```\n\nLater if you decide to change the thumbnail size, you only need to modify it in the application configuration;\nyou don't need to touch any dependent code.\n\n\n#### [[yii\\base\\Application::sourceLanguage|sourceLanguage]] <span id=\"sourceLanguage\"></span>\n\nThis property specifies the language that the application code is written in. The default value is `'en-US'`,\nmeaning English (United States). You should configure this property if the text content in your code is not in English.\n\nLike the [language](#language) property, you should configure this property in terms of\nan [IETF language tag](https://en.wikipedia.org/wiki/IETF_language_tag). For example, `en` stands for English,\nwhile `en-US` stands for English (United States).\n\nMore details about this property can be found in the [Internationalization](tutorial-i18n.md) section.\n\n\n#### [[yii\\base\\Application::timeZone|timeZone]] <span id=\"timeZone\"></span>\n\nThis property is provided as an alternative way of setting the default time zone of the PHP runtime.\nBy configuring this property, you are essentially calling the PHP function\n[date_default_timezone_set()](https://www.php.net/manual/en/function.date-default-timezone-set.php). For example:\n\n```php\n[\n    'timeZone' => 'America/Los_Angeles',\n]\n```\n\nFor more details on the implications of setting the time zone, please check the [section on date formatting](output-formatting.md#time-zones).\n\n#### [[yii\\base\\Application::version|version]] <span id=\"version\"></span>\n\nThis property specifies the version of the application. It defaults to `'1.0'`. You do not need to configure\nthis property if none of your code is using it.\n\n\n### Useful Properties <span id=\"useful-properties\"></span>\n\nThe properties described in this subsection are not commonly configured because their default values\nderive from common conventions. However, you may still configure them in case you want to break the conventions.\n\n\n#### [[yii\\base\\Application::charset|charset]] <span id=\"charset\"></span>\n\nThis property specifies the charset that the application uses. The default value is `'UTF-8'`, which should\nbe kept as-is for most applications unless you are working with a legacy system that uses a lot of non-Unicode data.\n\n\n#### [[yii\\base\\Application::defaultRoute|defaultRoute]] <span id=\"defaultRoute\"></span>\n\nThis property specifies the [route](runtime-routing.md) that an application should use when a request\ndoes not specify one. The route may consist of a child module ID, a controller ID, and/or an action ID.\nFor example, `help`, `post/create`, or `admin/post/create`. If an action ID is not given, this property will take\nthe default value specified in [[yii\\base\\Controller::defaultAction]].\n\nFor [[yii\\web\\Application|Web applications]], the default value of this property is `'site'`, which means\nthe `SiteController` controller and its default action should be used. As a result, if you access\nthe application without specifying a route, it will show the result of `app\\controllers\\SiteController::actionIndex()`.\n\nFor [[yii\\console\\Application|console applications]], the default value is `'help'`, which means the core command\n[[yii\\console\\controllers\\HelpController::actionIndex()]] should be used. As a result, if you run the command `yii`\nwithout providing any arguments, it will display the help information.\n\n\n#### [[yii\\base\\Application::extensions|extensions]] <span id=\"extensions\"></span>\n\nThis property specifies the list of [extensions](structure-extensions.md) that are installed and used by the application.\nBy default, it will take the array returned by the file `@vendor/yiisoft/extensions.php`. The `extensions.php` file\nis generated and maintained automatically when you use [Composer](https://getcomposer.org) to install extensions.\nSo in most cases, you do not need to configure this property.\n\nIn the special case when you want to maintain extensions manually, you may configure this property as follows:\n\n```php\n[\n    'extensions' => [\n        [\n            'name' => 'extension name',\n            'version' => 'version number',\n            'bootstrap' => 'BootstrapClassName',  // optional, may also be a configuration array\n            'alias' => [  // optional\n                '@alias1' => 'to/path1',\n                '@alias2' => 'to/path2',\n            ],\n        ],\n\n        // ... more extensions like the above ...\n\n    ],\n]\n```\n\nAs you can see, the property takes an array of extension specifications. Each extension is specified with an array\nconsisting of `name` and `version` elements. If an extension needs to run during the [bootstrap](runtime-bootstrapping.md)\nprocess, a `bootstrap` element may be specified with a bootstrapping class name or a [configuration](concept-configurations.md)\narray. An extension may also define a few [aliases](concept-aliases.md).\n\n\n#### [[yii\\base\\Application::layout|layout]] <span id=\"layout\"></span>\n\nThis property specifies the name of the default layout that should be used when rendering a [view](structure-views.md).\nThe default value is `'main'`, meaning the layout file `main.php` under the [layout path](#layoutPath) should be used.\nIf both of the [layout path](#layoutPath) and the [view path](#viewPath) are taking the default values,\nthe default layout file can be represented as the path alias `@app/views/layouts/main.php`.\n\nYou may configure this property to be `false` if you want to disable layout by default, although this is very rare.\n\n\n#### [[yii\\base\\Application::layoutPath|layoutPath]] <span id=\"layoutPath\"></span>\n\nThis property specifies the path where layout files should be looked for. The default value is\nthe `layouts` sub-directory under the [view path](#viewPath). If the [view path](#viewPath) is taking\nits default value, the default layout path can be represented as the path alias `@app/views/layouts`.\n\nYou may configure it as a directory or a path [alias](concept-aliases.md).\n\n\n#### [[yii\\base\\Application::runtimePath|runtimePath]] <span id=\"runtimePath\"></span>\n\nThis property specifies the path where temporary files, such as log files and cache files, can be generated.\nThe default value is the directory represented by the alias `@app/runtime`.\n\nYou may configure it as a directory or a path [alias](concept-aliases.md). Note that the runtime path must\nbe writable by the process running the application. And the path should be protected from being accessed\nby end users, because the temporary files under it may contain sensitive information.\n\nTo simplify access to this path, Yii has predefined a path alias named `@runtime` for it.\n\n\n#### [[yii\\base\\Application::viewPath|viewPath]] <span id=\"viewPath\"></span>\n\nThis property specifies the root directory where view files are located. The default value is the directory\nrepresented by the alias `@app/views`. You may configure it as a directory or a path [alias](concept-aliases.md).\n\n\n#### [[yii\\base\\Application::vendorPath|vendorPath]] <span id=\"vendorPath\"></span>\n\nThis property specifies the vendor directory managed by [Composer](https://getcomposer.org). It contains\nall third party libraries used by your application, including the Yii framework. The default value is\nthe directory represented by the alias `@app/vendor`.\n\nYou may configure this property as a directory or a path [alias](concept-aliases.md). When you modify\nthis property, make sure you also adjust the Composer configuration accordingly.\n\nTo simplify access to this path, Yii has predefined a path alias named `@vendor` for it.\n\n\n#### [[yii\\console\\Application::enableCoreCommands|enableCoreCommands]] <span id=\"enableCoreCommands\"></span>\n\nThis property is supported by [[yii\\console\\Application|console applications]] only. It specifies\nwhether the core commands included in the Yii release should be enabled. The default value is `true`.\n\n\n## Application Events <span id=\"application-events\"></span>\n\nAn application triggers several events during the lifecycle of handling a request. You may attach event\nhandlers to these events in application configurations as follows:\n\n```php\n[\n    'on beforeRequest' => function ($event) {\n        // ...\n    },\n]\n```\n\nThe use of the `on eventName` syntax is described in the [Configurations](concept-configurations.md#configuration-format)\nsection.\n\nAlternatively, you may attach event handlers during the [bootstrapping process](runtime-bootstrapping.md)\nafter the application instance is created. For example:\n\n```php\n\\Yii::$app->on(\\yii\\base\\Application::EVENT_BEFORE_REQUEST, function ($event) {\n    // ...\n});\n```\n\n### [[yii\\base\\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_REQUEST]] <span id=\"beforeRequest\"></span>\n\nThis event is triggered *before* an application handles a request. The actual event name is `beforeRequest`.\n\nWhen this event is triggered, the application instance has been configured and initialized. So it is a good place\nto insert your custom code via the event mechanism to intercept the request handling process. For example,\nin the event handler, you may dynamically set the [[yii\\base\\Application::language]] property based on some parameters.\n\n\n### [[yii\\base\\Application::EVENT_AFTER_REQUEST|EVENT_AFTER_REQUEST]] <span id=\"afterRequest\"></span>\n\nThis event is triggered *after* an application finishes handling a request but *before* sending the response.\nThe actual event name is `afterRequest`.\n\nWhen this event is triggered, the request handling is completed and you may take this chance to do some postprocessing\nof the request or customize the response.\n\nNote that the [[yii\\web\\Response|response]] component also triggers some events while it is sending out\nresponse content to end users. Those events are triggered *after* this event.\n\n\n### [[yii\\base\\Application::EVENT_BEFORE_ACTION|EVENT_BEFORE_ACTION]] <span id=\"beforeAction\"></span>\n\nThis event is triggered *before* running every [controller action](structure-controllers.md).\nThe actual event name is `beforeAction`.\n\nThe event parameter is an instance of [[yii\\base\\ActionEvent]]. An event handler may set\nthe [[yii\\base\\ActionEvent::isValid]] property to be `false` to stop running the action.\nFor example:\n\n```php\n[\n    'on beforeAction' => function ($event) {\n        if (some condition) {\n            $event->isValid = false;\n        } else {\n        }\n    },\n]\n```\n\nNote that the same `beforeAction` event is also triggered by [modules](structure-modules.md)\nand [controllers](structure-controllers.md). Application objects are the first ones\ntriggering this event, followed by modules (if any), and finally controllers. If an event handler\nsets [[yii\\base\\ActionEvent::isValid]] to be `false`, all of the subsequent events will NOT be triggered.\n\n\n### [[yii\\base\\Application::EVENT_AFTER_ACTION|EVENT_AFTER_ACTION]] <span id=\"afterAction\"></span>\n\nThis event is triggered *after* running every [controller action](structure-controllers.md).\nThe actual event name is `afterAction`.\n\nThe event parameter is an instance of [[yii\\base\\ActionEvent]]. Through\nthe [[yii\\base\\ActionEvent::result]] property, an event handler may access or modify the action result.\nFor example:\n\n```php\n[\n    'on afterAction' => function ($event) {\n        if (some condition) {\n            // modify $event->result\n        } else {\n        }\n    },\n]\n```\n\nNote that the same `afterAction` event is also triggered by [modules](structure-modules.md)\nand [controllers](structure-controllers.md). These objects trigger this event in the reverse order\nas for that of `beforeAction`. That is, controllers are the first objects triggering this event,\nfollowed by modules (if any), and finally applications.\n\n\n## Application Lifecycle <span id=\"application-lifecycle\"></span>\n\n![Application Lifecycle](images/application-lifecycle.png)\n\nWhen an [entry script](structure-entry-scripts.md) is being executed to handle a request,\nan application will undergo the following lifecycle:\n\n1. The entry script loads the application configuration as an array.\n2. The entry script creates a new instance of the application:\n  * [[yii\\base\\Application::preInit()|preInit()]] is called, which configures some high priority\n    application properties, such as [[yii\\base\\Application::basePath|basePath]].\n  * Register the [[yii\\base\\Application::errorHandler|error handler]].\n  * Configure application properties.\n  * [[yii\\base\\Application::init()|init()]] is called which further calls\n    [[yii\\base\\Application::bootstrap()|bootstrap()]] to run bootstrapping components.\n3. The entry script calls [[yii\\base\\Application::run()]] to run the application:\n  * Trigger the [[yii\\base\\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_REQUEST]] event.\n  * Handle the request: resolve the request into a [route](runtime-routing.md) and the associated parameters;\n    create the module, controller, and action objects as specified by the route; and run the action.\n  * Trigger the [[yii\\base\\Application::EVENT_AFTER_REQUEST|EVENT_AFTER_REQUEST]] event.\n  * Send response to the end user.\n4. The entry script receives the exit status from the application and completes the request processing.\n"
  },
  {
    "path": "docs/guide/structure-assets.md",
    "content": "Assets\n======\n\nAn asset in Yii is a file that may be referenced in a Web page. It can be a CSS file, a JavaScript file, an image\nor video file, etc. Assets are located in Web-accessible directories and are directly served by Web servers.\n\nIt is often preferable to manage assets programmatically. For example, when you use the [[yii\\jui\\DatePicker]] widget\nin a page, it will automatically include the required CSS and JavaScript files, instead of asking you to manually\nfind these files and include them. And when you upgrade the widget to a new version, it will automatically use\nthe new version of the asset files. In this tutorial, we will describe the powerful asset management capability\nprovided in Yii.\n\n\n## Asset Bundles <span id=\"asset-bundles\"></span>\n\nYii manages assets in the unit of *asset bundle*. An asset bundle is simply a collection of assets located\nin a directory. When you register an asset bundle in a [view](structure-views.md), it will include the CSS and\nJavaScript files in the bundle in the rendered Web page.\n\n\n## Defining Asset Bundles <span id=\"defining-asset-bundles\"></span>\n\nAsset bundles are specified as PHP classes extending from [[yii\\web\\AssetBundle]]. The name of a bundle is simply\nits corresponding fully qualified PHP class name (without the leading backslash). An asset bundle class should\nbe [autoloadable](concept-autoloading.md). It usually specifies where the assets are located, what CSS and \nJavaScript files the bundle contains, and how the bundle depends on other bundles.\n\nThe following code defines the main asset bundle used by [the basic project template](start-installation.md):\n\n```php\n<?php\n\nnamespace app\\assets;\n\nuse yii\\web\\AssetBundle;\n\nclass AppAsset extends AssetBundle\n{\n    public $basePath = '@webroot';\n    public $baseUrl = '@web';\n    public $css = [\n        'css/site.css',\n        ['css/print.css', 'media' => 'print'],\n    ];\n    public $js = [\n    ];\n    public $depends = [\n        'yii\\web\\YiiAsset',\n        'yii\\bootstrap\\BootstrapAsset',\n    ];\n}\n```\n\nThe above `AppAsset` class specifies that the asset files are located under the `@webroot` directory which\ncorresponds to the URL `@web`; the bundle contains a single CSS file `css/site.css` and no JavaScript file;\nthe bundle depends on two other bundles: [[yii\\web\\YiiAsset]] and [[yii\\bootstrap\\BootstrapAsset]]. More detailed\nexplanation about the properties of [[yii\\web\\AssetBundle]] can be found in the following:\n\n* [[yii\\web\\AssetBundle::sourcePath|sourcePath]]: specifies the root directory that contains the asset files in\n  this bundle. This property should be set if the root directory is not Web accessible. Otherwise, you should\n  set the [[yii\\web\\AssetBundle::basePath|basePath]] property and [[yii\\web\\AssetBundle::baseUrl|baseUrl]], instead.\n  [Path aliases](concept-aliases.md) can be used here.\n* [[yii\\web\\AssetBundle::basePath|basePath]]: specifies a Web-accessible directory that contains the asset files in\n  this bundle. When you specify the [[yii\\web\\AssetBundle::sourcePath|sourcePath]] property,\n  the [asset manager](#asset-manager) will publish the assets in this bundle to a Web-accessible directory\n  and overwrite this property accordingly. You should set this property if your asset files are already in\n  a Web-accessible directory and do not need asset publishing. [Path aliases](concept-aliases.md) can be used here.\n* [[yii\\web\\AssetBundle::baseUrl|baseUrl]]: specifies the URL corresponding to the directory\n  [[yii\\web\\AssetBundle::basePath|basePath]]. Like [[yii\\web\\AssetBundle::basePath|basePath]],\n  if you specify the [[yii\\web\\AssetBundle::sourcePath|sourcePath]] property, the [asset manager](#asset-manager)\n  will publish the assets and overwrite this property accordingly. [Path aliases](concept-aliases.md) can be used here.\n* [[yii\\web\\AssetBundle::css|css]]: an array listing the CSS files contained in this bundle. Note that only forward slash \"/\"\n  should be used as directory separators. Each file can be specified on its own as a string or in an array together with\n  attribute tags and their values.\n* [[yii\\web\\AssetBundle::js|js]]: an array listing the JavaScript files contained in this bundle. The format of this array\n  is the same as that of [[yii\\web\\AssetBundle::css|css]]. Each JavaScript file can be specified in one of the following two\n  formats:\n  - a relative path representing a local JavaScript file (e.g. `js/main.js`). The actual path of the file\n    can be determined by prepending [[yii\\web\\AssetManager::basePath]] to the relative path, and the actual URL\n    of the file can be determined by prepending [[yii\\web\\AssetManager::baseUrl]] to the relative path.\n  - an absolute URL representing an external JavaScript file. For example,\n    `https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js` or\n    `//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js`.\n* [[yii\\web\\AssetBundle::depends|depends]]: an array listing the names of the asset bundles that this bundle depends on\n  (to be explained shortly).\n* [[yii\\web\\AssetBundle::jsOptions|jsOptions]]: specifies the options that will be passed to the\n  [[yii\\web\\View::registerJsFile()]] method when it is called to register *every* JavaScript file in this bundle.\n* [[yii\\web\\AssetBundle::cssOptions|cssOptions]]: specifies the options that will be passed to the\n  [[yii\\web\\View::registerCssFile()]] method when it is called to register *every* CSS file in this bundle.\n* [[yii\\web\\AssetBundle::publishOptions|publishOptions]]: specifies the options that will be passed to the\n  [[yii\\web\\AssetManager::publish()]] method when it is called to publish source asset files to a Web directory.\n  This is only used if you specify the [[yii\\web\\AssetBundle::sourcePath|sourcePath]] property.\n\n\n### Asset Locations <span id=\"asset-locations\"></span>\n\nAssets, based on their location, can be classified as:\n\n* source assets: the asset files are located together with PHP source code which cannot be directly accessed via Web.\n  In order to use source assets in a page, they should be copied to a Web directory and turned into the so-called\n  published assets. This process is called *asset publishing* which will be described in detail shortly.\n* published assets: the asset files are located in a Web directory and can thus be directly accessed via Web.\n* external assets: the asset files are located on a Web server that is different from the one hosting your Web\n  application.\n\nWhen defining an asset bundle class, if you specify the [[yii\\web\\AssetBundle::sourcePath|sourcePath]] property,\nit means any assets listed using relative paths will be considered as source assets. If you do not specify this property,\nit means those assets are published assets (you should therefore specify [[yii\\web\\AssetBundle::basePath|basePath]] and\n[[yii\\web\\AssetBundle::baseUrl|baseUrl]] to let Yii know where they are located).\n\nIt is recommended that you place assets belonging to an application in a Web directory to avoid the unnecessary asset\npublishing process. This is why `AppAsset` in the prior example specifies [[yii\\web\\AssetBundle::basePath|basePath]]\ninstead of [[yii\\web\\AssetBundle::sourcePath|sourcePath]].\n\nFor [extensions](structure-extensions.md), because their assets are located together with their source code\nin directories that are not Web accessible, you have to specify the [[yii\\web\\AssetBundle::sourcePath|sourcePath]]\nproperty when defining asset bundle classes for them.\n\n> Note: Do not use `@webroot/assets` as the [[yii\\web\\AssetBundle::sourcePath|source path]].\n  This directory is used by default by the [[yii\\web\\AssetManager|asset manager]] to save the asset files\n  published from their source location. Any content in this directory is considered temporarily and may be subject\n  to removal.\n\n\n### Asset Dependencies <span id=\"asset-dependencies\"></span>\n\nWhen you include multiple CSS or JavaScript files in a Web page, they have to follow a certain order to avoid\noverriding issues. For example, if you are using a jQuery UI widget in a Web page, you have to make sure\nthe jQuery JavaScript file is included before the jQuery UI JavaScript file. We call such ordering the dependencies\namong assets.\n\nAsset dependencies are mainly specified through the [[yii\\web\\AssetBundle::depends]] property.\nIn the `AppAsset` example, the asset bundle depends on two other asset bundles: [[yii\\web\\YiiAsset]] and\n[[yii\\bootstrap\\BootstrapAsset]], which means the CSS and JavaScript files in `AppAsset` will be included *after*\nthose files in the two dependent bundles.\n\nAsset dependencies are transitive. This means if bundle A depends on B which depends on C, A will depend on C, too.\n\n\n### Asset Options <span id=\"asset-options\"></span>\n\nYou can specify the [[yii\\web\\AssetBundle::cssOptions|cssOptions]] and [[yii\\web\\AssetBundle::jsOptions|jsOptions]]\nproperties to customize the way that CSS and JavaScript files are included in a page. The values of these properties\nwill be passed to the [[yii\\web\\View::registerCssFile()]] and [[yii\\web\\View::registerJsFile()]] methods, respectively, when\nthey are called by the [view](structure-views.md) to include CSS and JavaScript files.\n\n> Note: The options you set in a bundle class apply to *every* CSS/JavaScript file in the bundle. If you want to\n  use different options for different files, you should use the format mentioned [[yii\\web\\AssetBundle::css|above]] or create\n  separate asset bundles, and use one set of options in each bundle.\n\nFor example, to conditionally include a CSS file for browsers that are IE9 or below, you can use the following option:\n\n```php\npublic $cssOptions = ['condition' => 'lte IE9'];\n```\n\nThis will cause a CSS file in the bundle to be included using the following HTML tags:\n\n```html\n<!--[if lte IE9]>\n<link rel=\"stylesheet\" href=\"path/to/foo.css\">\n<![endif]-->\n```\n\nTo wrap the generated CSS link tags within `<noscript>`, you can configure `cssOptions` as follows,\n\n```php\npublic $cssOptions = ['noscript' => true];\n```\n\nTo include a JavaScript file in the head section of a page (by default, JavaScript files are included at the end\nof the body section), use the following option:\n\n```php\npublic $jsOptions = ['position' => \\yii\\web\\View::POS_HEAD];\n```\n\nBy default, when an asset bundle is being published, all contents in the directory specified by [[yii\\web\\AssetBundle::sourcePath]]\nwill be published. You can customize this behavior by configuring the [[yii\\web\\AssetBundle::publishOptions|publishOptions]] \nproperty. For example, to publish only one or a few subdirectories of [[yii\\web\\AssetBundle::sourcePath]], \nyou can do the following in the asset bundle class:\n\n```php\n<?php\nnamespace app\\assets;\n\nuse yii\\web\\AssetBundle;\n\nclass FontAwesomeAsset extends AssetBundle \n{\n    public $sourcePath = '@bower/font-awesome'; \n    public $css = [ \n        'css/font-awesome.min.css', \n    ];\n    public $publishOptions = [\n        'only' => [\n            'fonts/*',\n            'css/*',\n        ]\n    ];\n}  \n```\n\nThe above example defines an asset bundle for the [\"fontawesome\" package](https://fontawesome.com/). By specifying \nthe `only` publishing option, only the `fonts` and `css` subdirectories will be published.\n\n\n### Bower and NPM Assets installation <span id=\"bower-npm-assets\"></span>\n\nMost JavaScript/CSS packages are managed by [Bower](https://bower.io/) and/or [NPM](https://www.npmjs.com/) package \nmanagers. In PHP world we have Composer, that manages PHP dependencies, but it is possible to load\nboth Bower and NPM packages using `composer.json` just as PHP packages.\n\nTo achieve this, we should configure our composer a bit. There are two options to do that:\n\n___\n\n#### Using asset-packagist repository\n\nThis way will satisfy requirements of the majority of projects, that need NPM or Bower packages.\n\n> Note: Since 2.0.13 both Basic and Advanced application templates are pre-configured to use asset-packagist\n by default, so you can skip this section.\n\nIn the `composer.json` of your project, add the following lines:\n\n```json\n\"repositories\": [\n    {\n        \"type\": \"composer\",\n        \"url\": \"https://asset-packagist.org\"\n    }\n]\n```\n\nAdjust `@npm` and `@bower` [aliases](concept-aliases.md) in you [application configuration](concept-configurations.md):\n\n```php\n$config = [\n    ...\n    'aliases' => [\n        '@bower' => '@vendor/bower-asset',\n        '@npm'   => '@vendor/npm-asset',\n    ],\n    ...\n];\n```\n\nVisit [asset-packagist.org](https://asset-packagist.org) to know, how it works.\n\n#### Using fxp/composer-asset-plugin\n\nCompared to asset-packagist, composer-asset-plugin does not require any changes to application config. Instead, it\nrequires global installation of a special Composer plugin by running the following command:\n\n```bash\ncomposer global require \"fxp/composer-asset-plugin:^1.4.1\"\n```\n\nThis command installs [composer asset plugin](https://github.com/fxpio/composer-asset-plugin/) globally\nwhich allows managing Bower and NPM package dependencies through Composer. After the plugin installation, \nevery single project on your computer will support Bower and NPM packages through `composer.json`. \n\nAdd the following lines to `composer.json` of your project to adjust directories where the installed packages \nwill be placed, if you want to publish them using Yii:\n\n```json\n\"config\": {\n    \"fxp-asset\": {\n        \"installer-paths\": {\n            \"npm-asset-library\": \"vendor/npm\",\n            \"bower-asset-library\": \"vendor/bower\"\n        }\n    }\n}\n```\n\n> Note: `fxp/composer-asset-plugin` significantly slows down the `composer update` command in comparison\n  to asset-packagist.\n \n____\n \nAfter configuring Composer to support Bower and NPM:\n\n1. Modify the `composer.json` file of your application or extension and list the package in the `require` entry.\n   You should use `bower-asset/PackageName` (for Bower packages) or `npm-asset/PackageName` (for NPM packages)\n   to refer to the library.\n2. Run `composer update`\n3. Create an asset bundle class and list the JavaScript/CSS files that you plan to use in your application or extension.\n   You should specify the [[yii\\web\\AssetBundle::sourcePath|sourcePath]] property as `@bower/PackageName` or `@npm/PackageName`.\n   This is because Composer will install the Bower or NPM package in the directory corresponding to this alias.\n\n> Note: Some packages may put all their distributed files in a subdirectory. If this is the case, you should specify\n  the subdirectory as the value of [[yii\\web\\AssetBundle::sourcePath|sourcePath]]. For example, [[yii\\web\\JqueryAsset]]\n  uses `@bower/jquery/dist` instead of `@bower/jquery`.\n\n\n## Using Asset Bundles <span id=\"using-asset-bundles\"></span>\n\nTo use an asset bundle, register it with a [view](structure-views.md) by calling the [[yii\\web\\AssetBundle::register()]]\nmethod. For example, in a view template you can register an asset bundle like the following:\n\n```php\nuse app\\assets\\AppAsset;\nAppAsset::register($this);  // $this represents the view object\n```\n\n> Info: The [[yii\\web\\AssetBundle::register()]] method returns an asset bundle object containing the information\n  about the published assets, such as [[yii\\web\\AssetBundle::basePath|basePath]] or [[yii\\web\\AssetBundle::baseUrl|baseUrl]].\n\nIf you are registering an asset bundle in other places, you should provide the needed view object. For example,\nto register an asset bundle in a [widget](structure-widgets.md) class, you can get the view object by `$this->view`.\n\nWhen an asset bundle is registered with a view, behind the scenes Yii will register all its dependent asset bundles.\nAnd if an asset bundle is located in a directory inaccessible through the Web, it will be published to a Web directory.\nLater, when the view renders a page, it will generate `<link>` and `<script>` tags for the CSS and JavaScript files\nlisted in the registered bundles. The order of these tags is determined by the dependencies among\nthe registered bundles and the order of the assets listed in the [[yii\\web\\AssetBundle::css]] and [[yii\\web\\AssetBundle::js]]\nproperties.\n\n\n### Dynamic Asset Bundles <span id=\"dynamic-asset-bundles\"></span>\n\nBeing a regular PHP class asset bundle can bear some extra logic related to it and may adjust its internal parameters dynamically.\nFor example: you may use some sophisticated JavaScript library, which provides some internationalization packed in separated\nsource files: each per each supported language. Thus you will need to add particular '.js' file to your page in order to\nmake library translation work. This can be achieved overriding [[yii\\web\\AssetBundle::init()]] method:\n\n```php\nnamespace app\\assets;\n\nuse yii\\web\\AssetBundle;\nuse Yii;\n\nclass SophisticatedAssetBundle extends AssetBundle\n{\n    public $sourcePath = '/path/to/sophisticated/src';\n    public $js = [\n        'sophisticated.js' // file, which is always used\n    ];\n\n    public function init()\n    {\n        parent::init();\n        $this->js[] = 'i18n/' . Yii::$app->language . '.js'; // dynamic file added\n    }\n}\n```\n\nParticular asset bundle can also be adjusted via its instance returned by [[yii\\web\\AssetBundle::register()]].\nFor example:\n\n```php\nuse app\\assets\\SophisticatedAssetBundle;\nuse Yii;\n\n$bundle = SophisticatedAssetBundle::register(Yii::$app->view);\n$bundle->js[] = 'i18n/' . Yii::$app->language . '.js'; // dynamic file added\n```\n\n> Note: although dynamic adjustment of the asset bundles is supported, it is a **bad** practice, which may lead to\n  unexpected side effects, and should be avoided if possible.\n\n\n### Customizing Asset Bundles <span id=\"customizing-asset-bundles\"></span>\n\nYii manages asset bundles through an application component named `assetManager` which is implemented by [[yii\\web\\AssetManager]].\nBy configuring the [[yii\\web\\AssetManager::bundles]] property, it is possible to customize the behavior of an asset bundle.\nFor example, the default [[yii\\web\\JqueryAsset]] asset bundle uses the `jquery.js` file from the installed\njquery Bower package. To improve the availability and performance, you may want to use a version hosted by Google.\nThis can be achieved by configuring `assetManager` in the application configuration like the following:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'bundles' => [\n                'yii\\web\\JqueryAsset' => [\n                    'sourcePath' => null,   // do not publish the bundle\n                    'js' => [\n                        '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js',\n                    ]\n                ],\n            ],\n        ],\n    ],\n];\n```\n\nYou can configure multiple asset bundles similarly through [[yii\\web\\AssetManager::bundles]]. The array keys\nshould be the class names (without the leading backslash) of the asset bundles, and the array values should\nbe the corresponding [configuration arrays](concept-configurations.md).\n\n> Tip: You can conditionally choose which assets to use in an asset bundle. The following example shows how\n> to use `jquery.js` in the development environment and `jquery.min.js` otherwise:\n>\n> ```php\n> 'yii\\web\\JqueryAsset' => [\n>     'js' => [\n>         YII_ENV_DEV ? 'jquery.js' : 'jquery.min.js'\n>     ]\n> ],\n> ```\n\nYou can disable one or multiple asset bundles by associating `false` with the names of the asset bundles\nthat you want to disable. When you register a disabled asset bundle with a view, none of its dependent bundles\nwill be registered, and the view also will not include any of the assets in the bundle in the page it renders.\nFor example, to disable [[yii\\web\\JqueryAsset]], you can use the following configuration:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'bundles' => [\n                'yii\\web\\JqueryAsset' => false,\n            ],\n        ],\n    ],\n];\n```\n\nYou can also disable *all* asset bundles by setting [[yii\\web\\AssetManager::bundles]] as `false`.\n\nKeep in mind that customization made via [[yii\\web\\AssetManager::bundles]] is applied at the creation of the asset bundle, e.g.\nat object constructor stage. Thus any adjustments made to the bundle object after that will override the mapping setup at [[yii\\web\\AssetManager::bundles]] level.\nIn particular: adjustments made inside [[yii\\web\\AssetBundle::init()]]\nmethod or over the registered bundle object will take precedence over `AssetManager` configuration.\nHere are the examples, where mapping set via [[yii\\web\\AssetManager::bundles]] makes no effect:\n\n```php\n// Program source code:\n\nnamespace app\\assets;\n\nuse yii\\web\\AssetBundle;\nuse Yii;\n\nclass LanguageAssetBundle extends AssetBundle\n{\n    // ...\n\n    public function init()\n    {\n        parent::init();\n        $this->baseUrl = '@web/i18n/' . Yii::$app->language; // can NOT be handled by `AssetManager`!\n    }\n}\n// ...\n\n$bundle = \\app\\assets\\LargeFileAssetBundle::register(Yii::$app->view);\n$bundle->baseUrl = YII_DEBUG ? '@web/large-files': '@web/large-files/minified'; // can NOT be handled by `AssetManager`!\n\n\n// Application config :\n\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'bundles' => [\n                'app\\assets\\LanguageAssetBundle' => [\n                    'baseUrl' => 'https://some.cdn.com/files/i18n/en' // makes NO effect!\n                ],\n                'app\\assets\\LargeFileAssetBundle' => [\n                    'baseUrl' => 'https://some.cdn.com/files/large-files' // makes NO effect!\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n\n### Asset Mapping <span id=\"asset-mapping\"></span>\n\nSometimes you may want to \"fix\" incorrect/incompatible asset file paths used in multiple asset bundles. For example,\nbundle A uses `jquery.min.js` version 1.11.1, and bundle B uses `jquery.js` version 2.1.1. While you can\nfix the problem by customizing each bundle, an easier way is to use the *asset map* feature to map incorrect assets\nto the desired ones. To do so, configure the [[yii\\web\\AssetManager::assetMap]] property like the following:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'assetMap' => [\n                'jquery.js' => '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js',\n            ],\n        ],\n    ],\n];\n```\n\nThe keys of [[yii\\web\\AssetManager::assetMap|assetMap]] are the asset names that you want to fix, and the values\nare the desired asset paths. When you register an asset bundle with a view, each relative asset file in its\n[[yii\\web\\AssetBundle::css|css]] and [[yii\\web\\AssetBundle::js|js]] arrays will be examined against this map.\nIf any of the keys are found to be the last part of an asset file (which is prefixed with [[yii\\web\\AssetBundle::sourcePath]]\nif available), the corresponding value will replace the asset and be registered with the view.\nFor example, the asset file `my/path/to/jquery.js` matches the key `jquery.js`.\n\n> Note: Only assets specified using relative paths are subject to asset mapping. The target asset paths\n  should be either absolute URLs or paths relative to [[yii\\web\\AssetManager::basePath]].\n\n\n### Asset Publishing <span id=\"asset-publishing\"></span>\n\nAs aforementioned, if an asset bundle is located in a directory that is not Web accessible, its assets will be copied\nto a Web directory when the bundle is being registered with a view. This process is called *asset publishing*, and is done\nautomatically by the [[yii\\web\\AssetManager|asset manager]].\n\nBy default, assets are published to the directory `@webroot/assets` which corresponds to the URL `@web/assets`.\nYou may customize this location by configuring the [[yii\\web\\AssetManager::basePath|basePath]] and\n[[yii\\web\\AssetManager::baseUrl|baseUrl]] properties.\n\nInstead of publishing assets by file copying, you may consider using symbolic links, if your OS and Web server allow.\nThis feature can be enabled by setting [[yii\\web\\AssetManager::linkAssets|linkAssets]] to be `true`.\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'linkAssets' => true,\n        ],\n    ],\n];\n```\n\nWith the above configuration, the asset manager will create a symbolic link to the source path of an asset bundle\nwhen it is being published. This is faster than file copying and can also ensure that the published assets are\nalways up-to-date.\n\n\n### Cache Busting <span id=\"cache-busting\"></span>\n\nFor Web application running in production mode, it is a common practice to enable HTTP caching for assets and other\nstatic resources. A drawback of this practice is that whenever you modify an asset and deploy it to production, a user\nclient may still use the old version due to the HTTP caching. To overcome this drawback, you may use the cache busting\nfeature, which was introduced in version 2.0.3, by configuring [[yii\\web\\AssetManager]] like the following:\n  \n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'appendTimestamp' => true,\n        ],\n    ],\n];\n```\n\nBy doing so, the URL of every published asset will be appended with its last modification timestamp. For example,\nthe URL to `yii.js` may look like `/assets/5515a87c/yii.js?v=1423448645\"`, where the parameter `v` represents the\nlast modification timestamp of the `yii.js` file. Now if you modify an asset, its URL will be changed, too, which causes\nthe client to fetch the latest version of the asset.\n\n\n## Commonly Used Asset Bundles <span id=\"common-asset-bundles\"></span>\n\nThe core Yii code has defined many asset bundles. Among them, the following bundles are commonly used and may\nbe referenced in your application or extension code.\n\n- [[yii\\web\\YiiAsset]]: It mainly includes the `yii.js` file which implements a mechanism of organizing JavaScript code\n  in modules. It also provides special support for `data-method` and `data-confirm` attributes and other useful features.\n  More information about `yii.js` can be found in the [Client Scripts Section](output-client-scripts.md#yii.js).\n- [[yii\\web\\JqueryAsset]]: It includes the `jquery.js` file from the jQuery Bower package.\n- [[yii\\bootstrap\\BootstrapAsset]]: It includes the CSS file from the Twitter Bootstrap framework.\n- [[yii\\bootstrap\\BootstrapPluginAsset]]: It includes the JavaScript file from the Twitter Bootstrap framework for\n  supporting Bootstrap JavaScript plugins.\n- [[yii\\jui\\JuiAsset]]: It includes the CSS and JavaScript files from the jQuery UI library.\n\nIf your code depends on jQuery, jQuery UI or Bootstrap, you should use these predefined asset bundles rather than\ncreating your own versions. If the default setting of these bundles do not satisfy your needs, you may customize them \nas described in the [Customizing Asset Bundle](#customizing-asset-bundles) subsection. \n\n\n## Asset Conversion <span id=\"asset-conversion\"></span>\n\nInstead of directly writing CSS and/or JavaScript code, developers often write them in some extended syntax and\nuse special tools to convert it into CSS/JavaScript. For example, for CSS code you may use [LESS](https://lesscss.org/)\nor [SCSS](https://sass-lang.com/); and for JavaScript you may use [TypeScript](https://www.typescriptlang.org/).\n\nYou can list the asset files in extended syntax in the [[yii\\web\\AssetBundle::css|css]] and [[yii\\web\\AssetBundle::js|js]] properties of an asset bundle. For example,\n\n```php\nclass AppAsset extends AssetBundle\n{\n    public $basePath = '@webroot';\n    public $baseUrl = '@web';\n    public $css = [\n        'css/site.less',\n    ];\n    public $js = [\n        'js/site.ts',\n    ];\n    public $depends = [\n        'yii\\web\\YiiAsset',\n        'yii\\bootstrap\\BootstrapAsset',\n    ];\n}\n```\n\nWhen you register such an asset bundle with a view, the [[yii\\web\\AssetManager|asset manager]] will automatically\nrun the pre-processor tools to convert assets in recognized extended syntax into CSS/JavaScript. When the view\nfinally renders a page, it will include the CSS/JavaScript files in the page, instead of the original assets\nin extended syntax.\n\nYii uses the file name extensions to identify which extended syntax an asset is in. By default it recognizes\nthe following syntax and file name extensions:\n\n- [LESS](https://lesscss.org/): `.less`\n- [SCSS](https://sass-lang.com/): `.scss`\n- [Stylus](https://stylus-lang.com/): `.styl`\n- [CoffeeScript](https://coffeescript.org/): `.coffee`\n- [TypeScript](https://www.typescriptlang.org/): `.ts`\n\nYii relies on the installed pre-processor tools to convert assets. For example, to use [LESS](https://lesscss.org/)\nyou should install the `lessc` pre-processor command.\n\nYou can customize the pre-processor commands and the supported extended syntax by configuring\n[[yii\\web\\AssetManager::converter]] like the following:\n\n```php\nreturn [\n    'components' => [\n        'assetManager' => [\n            'converter' => [\n                'class' => 'yii\\web\\AssetConverter',\n                'commands' => [\n                    'less' => ['css', 'lessc {from} {to} --no-color'],\n                    'ts' => ['js', 'tsc --out {to} {from}'],\n                ],\n            ],\n        ],\n    ],\n];\n```\n\nIn the above, we specify the supported extended syntax via the [[yii\\web\\AssetConverter::commands]] property.\nThe array keys are the file extension names (without leading dot), and the array values are the resulting\nasset file extension names and the commands for performing the asset conversion. The tokens `{from}` and `{to}`\nin the commands will be replaced with the source asset file paths and the target asset file paths.\n\n> Info: There are other ways of working with assets in extended syntax, besides the one described above.\n  For example, you can use build tools such as [grunt](https://gruntjs.com/) to monitor and automatically\n  convert assets in extended syntax. In this case, you should list the resulting CSS/JavaScript files in\n  asset bundles rather than the original files.\n\n\n## Combining and Compressing Assets <span id=\"combining-compressing-assets\"></span>\n\nA Web page can include many CSS and/or JavaScript files. To reduce the number of HTTP requests and the overall\ndownload size of these files, a common practice is to combine and compress multiple CSS/JavaScript files into \none or very few files, and then include these compressed files instead of the original ones in the Web pages.  \n \n> Info: Combining and compressing assets are usually needed when an application is in production mode. \n  In development mode, using the original CSS/JavaScript files is often more convenient for debugging purposes.\n\nIn the following, we introduce an approach to combine and compress asset files without the need to modify\nyour existing application code.\n\n1. Find all the asset bundles in your application that you plan to combine and compress.\n2. Divide these bundles into one or a few groups. Note that each bundle can only belong to a single group.\n3. Combine/compress the CSS files in each group into a single file. Do this similarly for the JavaScript files.\n4. Define a new asset bundle for each group:\n   * Set the [[yii\\web\\AssetBundle::css|css]] and [[yii\\web\\AssetBundle::js|js]] properties to be\n     the combined CSS and JavaScript files, respectively.\n   * Customize the asset bundles in each group by setting their [[yii\\web\\AssetBundle::css|css]] and \n     [[yii\\web\\AssetBundle::js|js]] properties to be empty, and setting their [[yii\\web\\AssetBundle::depends|depends]]\n     property to be the new asset bundle created for the group.\n\nUsing this approach, when you register an asset bundle in a view, it causes the automatic registration of\nthe new asset bundle for the group that the original bundle belongs to. And as a result, the combined/compressed \nasset files are included in the page, instead of the original ones.\n\n\n### An Example <span id=\"example\"></span>\n\nLet's use an example to further explain the above approach. \n\nAssume your application has two pages, X and Y. Page X uses asset bundles A, B and C, while Page Y uses asset bundles B, C and D. \n\nYou have two ways to divide these asset bundles. One is to use a single group to include all asset bundles, the\nother is to put A in Group X, D in Group Y, and (B, C) in Group S. Which one is better? It depends. The first way\nhas the advantage that both pages share the same combined CSS and JavaScript files, which makes HTTP caching\nmore effective. On the other hand, because the single group contains all bundles, the size of the combined CSS and \nJavaScript files will be bigger and thus increase the initial file transmission time. For simplicity in this example, \nwe will use the first way, i.e., use a single group to contain all bundles.\n\n> Info: Dividing asset bundles into groups is not trivial task. It usually requires analysis about the real world\n  traffic data of various assets on different pages. At the beginning, you may start with a single group for simplicity. \n\nUse existing tools (e.g. [Closure Compiler](https://developers.google.com/closure/compiler/), \n[YUI Compressor](https://github.com/yui/yuicompressor/)) to combine and compress CSS and JavaScript files in \nall the bundles. Note that the files should be combined in the order that satisfies the dependencies among the bundles. \nFor example, if Bundle A depends on B which depends on both C and D, then you should list the asset files starting \nfrom C and D, followed by B and finally A. \n\nAfter combining and compressing, we get one CSS file and one JavaScript file. Assume they are named as \n`all-xyz.css` and `all-xyz.js`, where `xyz` stands for a timestamp or a hash that is used to make the file name unique\nto avoid HTTP caching problems.\n \nWe are at the last step now. Configure the [[yii\\web\\AssetManager|asset manager]] as follows in the application\nconfiguration:\n\n```php\nreturn [\n    'components' => [\n        'assetManager' => [\n            'bundles' => [\n                'all' => [\n                    'class' => 'yii\\web\\AssetBundle',\n                    'basePath' => '@webroot/assets',\n                    'baseUrl' => '@web/assets',\n                    'css' => ['all-xyz.css'],\n                    'js' => ['all-xyz.js'],\n                ],\n                'A' => ['css' => [], 'js' => [], 'depends' => ['all']],\n                'B' => ['css' => [], 'js' => [], 'depends' => ['all']],\n                'C' => ['css' => [], 'js' => [], 'depends' => ['all']],\n                'D' => ['css' => [], 'js' => [], 'depends' => ['all']],\n            ],\n        ],\n    ],\n];\n```\n\nAs explained in the [Customizing Asset Bundles](#customizing-asset-bundles) subsection, the above configuration\nchanges the default behavior of each bundle. In particular, Bundle A, B, C and D no longer have any asset files.\nThey now all depend on the `all` bundle which contains the combined `all-xyz.css` and `all-xyz.js` files.\nConsequently, for Page X, instead of including the original source files from Bundle A, B and C, only these\ntwo combined files will be included; the same thing happens to Page Y.\n\nThere is one final trick to make the above approach work more smoothly. Instead of directly modifying the\napplication configuration file, you may put the bundle customization array in a separate file and conditionally\ninclude this file in the application configuration. For example,\n\n```php\nreturn [\n    'components' => [\n        'assetManager' => [\n            'bundles' => require __DIR__ . '/' . (YII_ENV_PROD ? 'assets-prod.php' : 'assets-dev.php'),  \n        ],\n    ],\n];\n```\n\nThat is, the asset bundle configuration array is saved in `assets-prod.php` for production mode, and\n`assets-dev.php` for non-production mode.\n\n> Note: this asset combining mechanism is based on the ability of [[yii\\web\\AssetManager::bundles]] to override the properties\n  of the registered asset bundles. However, as it already has been said above, this ability does not cover asset bundle\n  adjustments, which are performed at [[yii\\web\\AssetBundle::init()]] method or after bundle is registered. You should\n  avoid usage of such dynamic bundles during the asset combining.\n\n\n### Using the `asset` Command <span id=\"using-asset-command\"></span>\n\nYii provides a console command named `asset` to automate the approach that we just described.\n\nTo use this command, you should first create a configuration file to describe what asset bundles should\nbe combined and how they should be grouped. You can use the `asset/template` sub-command to generate\na template first and then modify it to fit for your needs.\n\n```\nyii asset/template assets.php\n```\n\nThe command generates a file named `assets.php` in the current directory. The content of this file looks like the following:\n\n```php\n<?php\n/**\n * Configuration file for the \"yii asset\" console command.\n * Note that in the console environment, some path aliases like '@webroot' and '@web' may not exist.\n * Please define these missing path aliases.\n */\nreturn [\n    // Adjust command/callback for JavaScript files compressing:\n    'jsCompressor' => 'java -jar compiler.jar --js {from} --js_output_file {to}',\n    // Adjust command/callback for CSS files compressing:\n    'cssCompressor' => 'java -jar yuicompressor.jar --type css {from} -o {to}',\n    // Whether to delete asset source after compression:\n    'deleteSource' => false,\n    // The list of asset bundles to compress:\n    'bundles' => [\n        // 'yii\\web\\YiiAsset',\n        // 'yii\\web\\JqueryAsset',\n    ],\n    // Asset bundle for compression output:\n    'targets' => [\n        'all' => [\n            'class' => 'yii\\web\\AssetBundle',\n            'basePath' => '@webroot/assets',\n            'baseUrl' => '@web/assets',\n            'js' => 'js/all-{hash}.js',\n            'css' => 'css/all-{hash}.css',\n        ],\n    ],\n    // Asset manager configuration:\n    'assetManager' => [\n    ],\n];\n```\n\nYou should modify this file and specify which bundles you plan to combine in the `bundles` option. In the `targets` \noption you should specify how the bundles should be divided into groups. You can specify one or multiple groups, \nas aforementioned.\n\n> Note: Because the alias `@webroot` and `@web` are not available in the console application, you should\n  explicitly define them in the configuration.\n\nJavaScript files are combined, compressed and written to `js/all-{hash}.js` where {hash} is replaced with the hash of\nthe resulting file.\n\nThe `jsCompressor` and `cssCompressor` options specify the console commands or PHP callbacks for performing\nJavaScript and CSS combining/compressing. By default, Yii uses [Closure Compiler](https://developers.google.com/closure/compiler/) \nfor combining JavaScript files and [YUI Compressor](https://github.com/yui/yuicompressor/) for combining CSS files. \nYou should install those tools manually or adjust these options to use your favorite tools.\n\n\nWith the configuration file, you can run the `asset` command to combine and compress the asset files\nand then generate a new asset bundle configuration file `assets-prod.php`:\n \n```\nyii asset assets.php config/assets-prod.php\n```\n\nThe generated configuration file can be included in the application configuration, like described in\nthe last subsection.\n\n> Note: in case you customize asset bundles for your application via [[yii\\web\\AssetManager::bundles]] or\n  [[yii\\web\\AssetManager::assetMap]] and want this customization to be applied for the compression source files,\n  you should include these options to the `assetManager` section inside asset command configuration file.\n\n> Note: while specifying the compression source, you should avoid the use of asset bundles whose parameters may be\n  adjusted dynamically (e.g. at `init()` method or after registration), since they may work incorrectly after compression.\n\n\n> Info: Using the `asset` command is not the only option to automate the asset combining and compressing process.\n  You can use the excellent task runner tool [grunt](https://gruntjs.com/) to achieve the same goal.\n\n\n### Grouping Asset Bundles <span id=\"grouping-asset-bundles\"></span>\n\nIn the last subsection, we have explained how to combine all asset bundles into a single one in order to minimize\nthe HTTP requests for asset files referenced in an application. This is not always desirable in practice. For example,\nimagine your application has a \"front end\" as well as a \"back end\", each of which uses a different set of JavaScript \nand CSS files. In this case, combining all asset bundles from both ends into a single one does not make sense, \nbecause the asset bundles for the \"front end\" are not used by the \"back end\" and it would be a waste of network\nbandwidth to send the \"back end\" assets when a \"front end\" page is requested.\n \nTo solve the above problem, you can divide asset bundles into groups and combine asset bundles for each group.\nThe following configuration shows how you can group asset bundles: \n\n```php\nreturn [\n    ...\n    // Specify output bundles with groups:\n    'targets' => [\n        'allShared' => [\n            'js' => 'js/all-shared-{hash}.js',\n            'css' => 'css/all-shared-{hash}.css',\n            'depends' => [\n                // Include all assets shared between 'backend' and 'frontend'\n                'yii\\web\\YiiAsset',\n                'app\\assets\\SharedAsset',\n            ],\n        ],\n        'allBackEnd' => [\n            'js' => 'js/all-{hash}.js',\n            'css' => 'css/all-{hash}.css',\n            'depends' => [\n                // Include only 'backend' assets:\n                'app\\assets\\AdminAsset'\n            ],\n        ],\n        'allFrontEnd' => [\n            'js' => 'js/all-{hash}.js',\n            'css' => 'css/all-{hash}.css',\n            'depends' => [], // Include all remaining assets\n        ],\n    ],\n    ...\n];\n```\n\nAs you can see, the asset bundles are divided into three groups: `allShared`, `allBackEnd` and `allFrontEnd`.\nThey each depends on an appropriate set of asset bundles. For example, `allBackEnd` depends on `app\\assets\\AdminAsset`.\nWhen running `asset` command with this configuration, it will combine asset bundles according to the above specification.\n\n> Info: You may leave the `depends` configuration empty for one of the target bundle. By doing so, that particular\n  asset bundle will depend on all of the remaining asset bundles that other target bundles do not depend on.\n"
  },
  {
    "path": "docs/guide/structure-controllers.md",
    "content": "Controllers\n===========\n\nControllers are part of the [MVC](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) architecture.\nThey are objects of classes extending from [[yii\\base\\Controller]] and are responsible for processing requests and\ngenerating responses. In particular, after taking over the control from [applications](structure-applications.md),\ncontrollers will analyze incoming request data, pass them to [models](structure-models.md), inject model results\ninto [views](structure-views.md), and finally generate outgoing responses.\n\n\n## Actions <span id=\"actions\"></span>\n\nControllers are composed of *actions* which are the most basic units that end users can address and request for\nexecution. A controller can have one or multiple actions.\n\nThe following example shows a `post` controller with two actions: `view` and `create`:\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse app\\models\\Post;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\n\nclass PostController extends Controller\n{\n    public function actionView($id)\n    {\n        $model = Post::findOne($id);\n        if ($model === null) {\n            throw new NotFoundHttpException;\n        }\n\n        return $this->render('view', [\n            'model' => $model,\n        ]);\n    }\n\n    public function actionCreate()\n    {\n        $model = new Post;\n\n        if ($model->load(Yii::$app->request->post()) && $model->save()) {\n            return $this->redirect(['view', 'id' => $model->id]);\n        } else {\n            return $this->render('create', [\n                'model' => $model,\n            ]);\n        }\n    }\n}\n```\n\nIn the `view` action (defined by the `actionView()` method), the code first loads the [model](structure-models.md)\naccording to the requested model ID; If the model is loaded successfully, it will display it using\na [view](structure-views.md) named `view`. Otherwise, it will throw an exception.\n\nIn the `create` action (defined by the `actionCreate()` method), the code is similar. It first tries to populate\na new instance of the [model](structure-models.md) using the request data and save the model. If both succeed it\nwill redirect the browser to the `view` action with the ID of the newly created model. Otherwise it will display\nthe `create` view through which users can provide the needed input.\n\n\n## Routes <span id=\"routes\"></span>\n\nEnd users address actions through the so-called *routes*. A route is a string that consists of the following parts:\n\n* a module ID: this exists only if the controller belongs to a non-application [module](structure-modules.md);\n* a [controller ID](#controller-ids): a string that uniquely identifies the controller among all controllers within the same application\n  (or the same module if the controller belongs to a module);\n* an [action ID](#action-ids): a string that uniquely identifies the action among all actions within the same controller.\n\nRoutes take the following format:\n\n```\nControllerID/ActionID\n```\n\nor the following format if the controller belongs to a module:\n\n```php\nModuleID/ControllerID/ActionID\n```\n\nSo if a user requests with the URL `https://hostname/index.php?r=site/index`, the `index` action in the `site` controller\nwill be executed. For more details on how routes are resolved into actions, please refer to\nthe [Routing and URL Creation](runtime-routing.md) section.\n\n\n## Creating Controllers <span id=\"creating-controllers\"></span>\n\nIn [[yii\\web\\Application|Web applications]], controllers should extend from [[yii\\web\\Controller]] or its\nchild classes. Similarly in [[yii\\console\\Application|console applications]], controllers should extend from\n[[yii\\console\\Controller]] or its child classes. The following code defines a `site` controller:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n}\n```\n\n\n### Controller IDs <span id=\"controller-ids\"></span>\n\nUsually, a controller is designed to handle the requests regarding a particular type of resource.\nFor this reason, controller IDs are often nouns referring to the types of the resources that they are handling.\nFor example, you may use `article` as the ID of a controller that handles article data.\n\nBy default, controller IDs should contain these characters only: English letters in lower case, digits,\nunderscores, hyphens, and forward slashes. For example, `article` and `post-comment` are both valid controller IDs,\nwhile `article?`, `PostComment`, `admin\\post` are not.\n\nA controller ID may also contain a subdirectory prefix. For example, `admin/article` stands for an `article` controller\nin the `admin` subdirectory under the [[yii\\base\\Application::controllerNamespace|controller namespace]].\nValid characters for subdirectory prefixes include: English letters in lower and upper cases, digits, underscores, and\nforward slashes, where forward slashes are used as separators for multi-level subdirectories (e.g. `panels/admin`).\n\n\n### Controller Class Naming <span id=\"controller-class-naming\"></span>\n\nController class names can be derived from controller IDs according to the following procedure:\n\n1. Turn the first letter in each word separated by hyphens into upper case. Note that if the controller ID\n  contains slashes, this rule only applies to the part after the last slash in the ID.\n2. Remove hyphens and replace any forward slashes with backward slashes.\n3. Append the suffix `Controller`.\n4. Prepend the [[yii\\base\\Application::controllerNamespace|controller namespace]].\n\nThe following are some examples, assuming the [[yii\\base\\Application::controllerNamespace|controller namespace]]\ntakes the default value `app\\controllers`:\n\n* `article` becomes `app\\controllers\\ArticleController`;\n* `post-comment` becomes `app\\controllers\\PostCommentController`;\n* `admin/post-comment` becomes `app\\controllers\\admin\\PostCommentController`;\n* `adminPanels/post-comment` becomes `app\\controllers\\adminPanels\\PostCommentController`.\n\nController classes must be [autoloadable](concept-autoloading.md). For this reason, in the above examples,\nthe `article` controller class should be saved in the file whose [alias](concept-aliases.md)\nis `@app/controllers/ArticleController.php`; while the `admin/post-comment` controller should be\nin `@app/controllers/admin/PostCommentController.php`.\n\n> Info: The last example `admin/post-comment` shows how you can put a controller under a sub-directory\n  of the [[yii\\base\\Application::controllerNamespace|controller namespace]]. This is useful when you want\n  to organize your controllers into several categories and you do not want to use [modules](structure-modules.md).\n\n\n### Controller Map <span id=\"controller-map\"></span>\n\nYou can configure the [[yii\\base\\Application::controllerMap|controller map]] to overcome the constraints\nof the controller IDs and class names described above. This is mainly useful when you are using\nthird-party controllers and you do not have control over their class names.\n\nYou may configure the [[yii\\base\\Application::controllerMap|controller map]] in the\n[application configuration](structure-applications.md#application-configurations). For example:\n\n```php\n[\n    'controllerMap' => [\n        // declares \"account\" controller using a class name\n        'account' => 'app\\controllers\\UserController',\n\n        // declares \"article\" controller using a configuration array\n        'article' => [\n            'class' => 'app\\controllers\\PostController',\n            'enableCsrfValidation' => false,\n        ],\n    ],\n]\n```\n\n\n### Default Controller <span id=\"default-controller\"></span>\n\nEach application has a default controller specified via the [[yii\\base\\Application::defaultRoute]] property.\nWhen a request does not specify a [route](#routes), the route specified by this property will be used.\nFor [[yii\\web\\Application|Web applications]], its value is `'site'`, while for [[yii\\console\\Application|console applications]],\nit is `help`. Therefore, if a URL is `https://hostname/index.php`, then the `site` controller will handle the request.\n\nYou may change the default controller with the following [application configuration](structure-applications.md#application-configurations):\n\n```php\n[\n    'defaultRoute' => 'main',\n]\n```\n\n\n## Creating Actions <span id=\"creating-actions\"></span>\n\nCreating actions can be as simple as defining the so-called *action methods* in a controller class. An action method is\na *public* method whose name starts with the word `action`. The return value of an action method represents\nthe response data to be sent to end users. The following code defines two actions, `index` and `hello-world`:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actionIndex()\n    {\n        return $this->render('index');\n    }\n\n    public function actionHelloWorld()\n    {\n        return 'Hello World';\n    }\n}\n```\n\n\n### Action IDs <span id=\"action-ids\"></span>\n\nAn action is often designed to perform a particular manipulation of a resource. For this reason,\naction IDs are usually verbs, such as `view`, `update`, etc.\n\nBy default, action IDs should contain these characters only: English letters in lower case, digits,\nunderscores, and hyphens (you can use hyphens to separate words). For example,\n`view`, `update2`, and `comment-post` are all valid action IDs, while `view?` and `Update` are not.\n\nYou can create actions in two ways: inline actions and standalone actions. An inline action is\ndefined as a method in the controller class, while a standalone action is a class extending\n[[yii\\base\\Action]] or its child classes. Inline actions take less effort to create and are often preferred\nif you have no intention to reuse these actions. Standalone actions, on the other hand, are mainly\ncreated to be used in different controllers or be redistributed as [extensions](structure-extensions.md).\n\n\n### Inline Actions <span id=\"inline-actions\"></span>\n\nInline actions refer to the actions that are defined in terms of action methods as we just described.\n\nThe names of the action methods are derived from action IDs according to the following procedure:\n\n1. Turn the first letter in each word of the action ID into upper case.\n2. Remove hyphens.\n3. Prepend the prefix `action`.\n\nFor example, `index` becomes `actionIndex`, and `hello-world` becomes `actionHelloWorld`.\n\n> Note: The names of the action methods are *case-sensitive*. If you have a method named `ActionIndex`,\n  it will not be considered as an action method, and as a result, the request for the `index` action\n  will result in an exception. Also note that action methods must be public. A private or protected\n  method does NOT define an inline action.\n\n\nInline actions are the most commonly defined actions because they take little effort to create. However,\nif you plan to reuse the same action in different places, or if you want to redistribute an action,\nyou should consider defining it as a *standalone action*.\n\n\n### Standalone Actions <span id=\"standalone-actions\"></span>\n\nStandalone actions are defined in terms of action classes extending [[yii\\base\\Action]] or its child classes.\nFor example, in the Yii releases, there are [[yii\\web\\ViewAction]] and [[yii\\web\\ErrorAction]], both of which\nare standalone actions.\n\nTo use a standalone action, you should declare it in the *action map* by overriding the\n[[yii\\base\\Controller::actions()]] method in your controller classes like the following:\n\n```php\npublic function actions()\n{\n    return [\n        // declares \"error\" action using a class name\n        'error' => 'yii\\web\\ErrorAction',\n\n        // declares \"view\" action using a configuration array\n        'view' => [\n            'class' => 'yii\\web\\ViewAction',\n            'viewPrefix' => '',\n        ],\n    ];\n}\n```\n\nAs you can see, the `actions()` method should return an array whose keys are action IDs and values the corresponding\naction class names or [configurations](concept-configurations.md). Unlike inline actions, action IDs for standalone\nactions can contain arbitrary characters, as long as they are declared in the `actions()` method.\n\nTo create a standalone action class, you should extend [[yii\\base\\Action]] or a child class, and implement\na public method named `run()`. The role of the `run()` method is similar to that of an action method. For example,\n\n```php\n<?php\nnamespace app\\components;\n\nuse yii\\base\\Action;\n\nclass HelloWorldAction extends Action\n{\n    public function run()\n    {\n        return \"Hello World\";\n    }\n}\n```\n\n\n### Action Results <span id=\"action-results\"></span>\n\nThe return value of an action method or of the `run()` method of a standalone action is significant. It stands\nfor the result of the corresponding action.\n\nThe return value can be a [response](runtime-responses.md) object which will be sent to the end user as the response.\n\n* For [[yii\\web\\Application|Web applications]], the return value can also be some arbitrary data which will\n  be assigned to [[yii\\web\\Response::data]] and be further converted into a string representing the response body.\n* For [[yii\\console\\Application|console applications]], the return value can also be an integer representing\n  the [[yii\\console\\Response::exitStatus|exit status]] of the command execution.\n\nIn the examples shown above, the action results are all strings which will be treated as the response body\nto be sent to end users. The following example shows how an action can redirect the user browser to a new URL\nby returning a response object (because the [[yii\\web\\Controller::redirect()|redirect()]] method returns\na response object):\n\n```php\npublic function actionForward()\n{\n    // redirect the user browser to https://example.com\n    return $this->redirect('https://example.com');\n}\n```\n\n\n### Action Parameters <span id=\"action-parameters\"></span>\n\nThe action methods for inline actions and the `run()` methods for standalone actions can take parameters,\ncalled *action parameters*. Their values are obtained from requests. For [[yii\\web\\Application|Web applications]],\nthe value of each action parameter is retrieved from `$_GET` using the parameter name as the key;\nfor [[yii\\console\\Application|console applications]], they correspond to the command line arguments.\n\nIn the following example, the `view` action (an inline action) has declared two parameters: `$id` and `$version`.\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass PostController extends Controller\n{\n    public function actionView($id, $version = null)\n    {\n        // ...\n    }\n}\n```\n\nThe action parameters will be populated as follows for different requests:\n\n* `https://hostname/index.php?r=post/view&id=123`: the `$id` parameter will be filled with the value\n  `'123'`,  while `$version` is still `null` because there is no `version` query parameter.\n* `https://hostname/index.php?r=post/view&id=123&version=2`: the `$id` and `$version` parameters will\n  be filled with `'123'` and `'2'`, respectively.\n* `https://hostname/index.php?r=post/view`: a [[yii\\web\\BadRequestHttpException]] exception will be thrown\n  because the required `$id` parameter is not provided in the request.\n* `https://hostname/index.php?r=post/view&id[]=123`: a [[yii\\web\\BadRequestHttpException]] exception will be thrown\n  because `$id` parameter is receiving an unexpected array value `['123']`.\n\nIf you want an action parameter to accept array values, you should type-hint it with `array`, like the following:\n\n```php\npublic function actionView(array $id, $version = null)\n{\n    // ...\n}\n```\n\nNow if the request is `https://hostname/index.php?r=post/view&id[]=123`, the `$id` parameter will take the value\nof `['123']`. If the request is `https://hostname/index.php?r=post/view&id=123`, the `$id` parameter will still\nreceive the same array value because the scalar value `'123'` will be automatically turned into an array.\n\nThe above examples mainly show how action parameters work for Web applications. For console applications,\nplease refer to the [Console Commands](tutorial-console.md) section for more details.\n\n\n### Default Action <span id=\"default-action\"></span>\n\nEach controller has a default action specified via the [[yii\\base\\Controller::defaultAction]] property.\nWhen a [route](#routes) contains the controller ID only, it implies that the default action of\nthe specified controller is requested.\n\nBy default, the default action is set as `index`. If you want to change the default value, simply override\nthis property in the controller class, like the following:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public $defaultAction = 'home';\n\n    public function actionHome()\n    {\n        return $this->render('home');\n    }\n}\n```\n\n\n## Controller Lifecycle <span id=\"controller-lifecycle\"></span>\n\nWhen processing a request, an [application](structure-applications.md) will create a controller\nbased on the requested [route](#routes). The controller will then undergo the following lifecycle\nto fulfill the request:\n\n1. The [[yii\\base\\Controller::init()]] method is called after the controller is created and configured.\n2. The controller creates an action object based on the requested action ID:\n   * If the action ID is not specified, the [[yii\\base\\Controller::defaultAction|default action ID]] will be used.\n   * If the action ID is found in the [[yii\\base\\Controller::actions()|action map]], a standalone action\n     will be created;\n   * If the action ID is found to match an action method, an inline action will be created;\n   * Otherwise an [[yii\\base\\InvalidRouteException]] exception will be thrown.\n3. The controller sequentially calls the `beforeAction()` method of the application, the module (if the controller\n   belongs to a module), and the controller.\n   * If one of the calls returns `false`, the rest of the uncalled `beforeAction()` methods will be skipped and the\n     action execution will be cancelled.\n   * By default, each `beforeAction()` method call will trigger a `beforeAction` event to which you can attach a handler.\n4. The controller runs the action.\n   * The action parameters will be analyzed and populated from the request data.\n5. The controller sequentially calls the `afterAction()` method of the controller, the module (if the controller\n   belongs to a module), and the application.\n   * By default, each `afterAction()` method call will trigger an `afterAction` event to which you can attach a handler.\n6. The application will take the action result and assign it to the [response](runtime-responses.md).\n\n\n## Best Practices <span id=\"best-practices\"></span>\n\nIn a well-designed application, controllers are often very thin, with each action containing only a few lines of code.\nIf your controller is rather complicated, it usually indicates that you should refactor it and move some code\nto other classes.\n\nHere are some specific best practices. Controllers\n\n* may access the [request](runtime-requests.md) data;\n* may call methods of [models](structure-models.md) and other service components with request data;\n* may use [views](structure-views.md) to compose responses;\n* should NOT process the request data - this should be done in [the model layer](structure-models.md);\n* should avoid embedding HTML or other presentational code - this is better done in [views](structure-views.md).\n"
  },
  {
    "path": "docs/guide/structure-entry-scripts.md",
    "content": "Entry Scripts\n=============\n\nEntry scripts are the first step in the application bootstrapping process. An application (either\nWeb application or console application) has a single entry script. End users make requests to\nentry scripts which instantiate application instances and forward the requests to them.\n\nEntry scripts for Web applications must be stored under Web accessible directories so that they\ncan be accessed by end users. They are often named as `index.php`, but can also use any other names,\nprovided Web servers can locate them.\n\nEntry scripts for console applications are usually stored under the [base path](structure-applications.md)\nof applications and are named as `yii` (with the `.php` suffix). They should be made executable\nso that users can run console applications through the command `./yii <route> [arguments] [options]`.\n\nEntry scripts mainly do the following work:\n\n* Define global constants;\n* Register [Composer autoloader](https://getcomposer.org/doc/01-basic-usage.md#autoloading);\n* Include the [[Yii]] class file;\n* Load application configuration;\n* Create and configure an [application](structure-applications.md) instance;\n* Call [[yii\\base\\Application::run()]] to process the incoming request.\n\n\n## Web Applications <span id=\"web-applications\"></span>\n\nThe following is the code in the entry script for the [Basic Web Project Template](start-installation.md).\n\n```php\n<?php\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n\n// register Composer autoloader\nrequire __DIR__ . '/../vendor/autoload.php';\n\n// include Yii class file\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n\n// load application configuration\n$config = require __DIR__ . '/../config/web.php';\n\n// create, configure and run application\n(new yii\\web\\Application($config))->run();\n```\n\n\n## Console Applications <span id=\"console-applications\"></span>\n\nSimilarly, the following is the code for the entry script of a console application:\n\n```php\n#!/usr/bin/env php\n<?php\n/**\n * Yii console bootstrap file.\n *\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n\n// register Composer autoloader\nrequire __DIR__ . '/vendor/autoload.php';\n\n// include Yii class file\nrequire __DIR__ . '/vendor/yiisoft/yii2/Yii.php';\n\n// load application configuration\n$config = require __DIR__ . '/config/console.php';\n\n$application = new yii\\console\\Application($config);\n$exitCode = $application->run();\nexit($exitCode);\n```\n\n\n## Defining Constants <span id=\"defining-constants\"></span>\n\nEntry scripts are the best place for defining global constants. Yii supports the following three constants:\n\n* `YII_DEBUG`: specifies whether the application is running in debug mode. When in debug mode, an application\n  will keep more log information, and will reveal detailed error call stacks if exceptions are thrown. For this\n  reason, debug mode should be used mainly during development. The default value of `YII_DEBUG` is `false`.\n* `YII_ENV`: specifies which environment the application is running in. This will be described in\n  more detail in the [Configurations](concept-configurations.md#environment-constants) section.\n  The default value of `YII_ENV` is `'prod'`, meaning the application is running in production environment.\n* `YII_ENABLE_ERROR_HANDLER`: specifies whether to enable the error handler provided by Yii. The default\n  value of this constant is `true`.\n\nWhen defining a constant, we often use the code like the following:\n\n```php\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\n```\n\nwhich is equivalent to the following code:\n\n```php\nif (!defined('YII_DEBUG')) {\n    define('YII_DEBUG', true);\n}\n```\n\nClearly the former is more succinct and easier to understand.\n\nConstant definitions should be done at the very beginning of an entry script so that they can take effect\nwhen other PHP files are being included.\n"
  },
  {
    "path": "docs/guide/structure-extensions.md",
    "content": "Extensions\n==========\n\nExtensions are redistributable software packages specifically designed to be used in Yii applications and provide\nready-to-use features. For example, the [yiisoft/yii2-debug](https://github.com/yiisoft/yii2-debug) extension adds a handy debug toolbar\nat the bottom of every page in your application to help you more easily grasp how the pages are generated. You can\nuse extensions to accelerate your development process. You can also package your code as extensions to share with\nother people your great work.\n\n> Info: We use the term \"extension\" to refer to Yii-specific software packages. For general purpose software packages\n  that can be used without Yii, we will refer to them using the term \"package\" or \"library\".\n\n\n## Using Extensions <span id=\"using-extensions\"></span>\n\nTo use an extension, you need to install it first. Most extensions are distributed as [Composer](https://getcomposer.org/)\npackages which can be installed by taking the following two simple steps:\n\n1. modify the `composer.json` file of your application and specify which extensions (Composer packages) you want to install.\n2. run `composer install` to install the specified extensions.\n\nNote that you may need to install [Composer](https://getcomposer.org/) if you do not have it.\n\nBy default, Composer installs packages registered on [Packagist](https://packagist.org/) - the biggest repository\nfor open source Composer packages. You can look for extensions on Packagist. You may also\n[create your own repository](https://getcomposer.org/doc/05-repositories.md#repository) and configure Composer\nto use it. This is useful if you are developing private extensions that you want to share within your projects only.\n\nExtensions installed by Composer are stored in the `BasePath/vendor` directory, where `BasePath` refers to the\napplication's [base path](structure-applications.md#basePath).  Because Composer is a dependency manager, when\nit installs a package, it will also install all its dependent packages.\n\nFor example, to install the `yiisoft/yii2-imagine` extension, modify your `composer.json` like the following:\n\n```json\n{\n    // ...\n\n    \"require\": {\n        // ... other dependencies\n\n        \"yiisoft/yii2-imagine\": \"*\"\n    }\n}\n```\n\nAfter the installation, you should see the directory `yiisoft/yii2-imagine` under `BasePath/vendor`. You should\nalso see another directory `imagine/imagine` which contains the installed dependent package.\n\n> Info: The `yiisoft/yii2-imagine` is a core extension developed and maintained by the Yii developer team. All\n  core extensions are hosted on [Packagist](https://packagist.org/) and named like `yiisoft/yii2-xyz`, where `xyz`\n  varies for different extensions.\n\nNow you can use the installed extensions like they are part of your application. The following example shows\nhow you can use the `yii\\imagine\\Image` class provided by the `yiisoft/yii2-imagine` extension:\n\n```php\nuse Yii;\nuse yii\\imagine\\Image;\n\n// generate a thumbnail image\nImage::thumbnail('@webroot/img/test-image.jpg', 120, 120)\n    ->save(Yii::getAlias('@runtime/thumb-test-image.jpg'), ['quality' => 50]);\n```\n\n> Info: Extension classes are autoloaded by the [Yii class autoloader](concept-autoloading.md).\n\n\n### Installing Extensions Manually <span id=\"installing-extensions-manually\"></span>\n\nIn some rare occasions, you may want to install some or all extensions manually, rather than relying on Composer.\nTo do so, you should:\n\n1. download the extension archive files and unpack them in the `vendor` directory.\n2. install the class autoloaders provided by the extensions, if any.\n3. download and install all dependent extensions as instructed.\n\nIf an extension does not have a class autoloader but follows the [PSR-4 standard](https://www.php-fig.org/psr/psr-4/),\nyou may use the class autoloader provided by Yii to autoload the extension classes. All you need to do is just to\ndeclare a [root alias](concept-aliases.md#defining-aliases) for the extension root directory. For example,\nassuming you have installed an extension in the directory `vendor/mycompany/myext`, and the extension classes\nare under the `myext` namespace, then you can include the following code in your application configuration:\n\n```php\n[\n    'aliases' => [\n        '@myext' => '@vendor/mycompany/myext',\n    ],\n]\n```\n\n\n## Creating Extensions <span id=\"creating-extensions\"></span>\n\nYou may consider creating an extension when you feel the need to share with other people your great code.\nAn extension can contain any code you like, such as a helper class, a widget, a module, etc.\n\nIt is recommended that you create an extension in terms of a [Composer package](https://getcomposer.org/) so that\nit can be more easily installed and used by other users, as described in the last subsection.\n\nBelow are the basic steps you may follow to create an extension as a Composer package.\n\n1. Create a project for your extension and host it on a VCS repository, such as [github.com](https://github.com).\n   The development and maintenance work for the extension should be done on this repository.\n2. Under the root directory of the project, create a file named `composer.json` as required by Composer. Please\n   refer to the next subsection for more details.\n3. Register your extension with a Composer repository, such as [Packagist](https://packagist.org/), so that\n   other users can find and install your extension using Composer.\n\n\n### `composer.json` <span id=\"composer-json\"></span>\n\nEach Composer package must have a `composer.json` file in its root directory. The file contains the metadata about\nthe package. You may find complete specification about this file in the [Composer Manual](https://getcomposer.org/doc/01-basic-usage.md#composer-json-project-setup).\nThe following example shows the `composer.json` file for the `yiisoft/yii2-imagine` extension:\n\n```json\n{\n    // package name\n    \"name\": \"yiisoft/yii2-imagine\",\n\n    // package type\n    \"type\": \"yii2-extension\",\n\n    \"description\": \"The Imagine integration for the Yii framework\",\n    \"keywords\": [\"yii2\", \"imagine\", \"image\", \"helper\"],\n    \"license\": \"BSD-3-Clause\",\n    \"support\": {\n        \"issues\": \"https://github.com/yiisoft/yii2/issues?labels=ext%3Aimagine\",\n        \"forum\": \"https://forum.yiiframework.com/\",\n        \"wiki\": \"https://www.yiiframework.com/wiki/\",\n        \"irc\": \"ircs://irc.libera.chat:6697/yii\",\n        \"source\": \"https://github.com/yiisoft/yii2\"\n    },\n    \"authors\": [\n        {\n            \"name\": \"Antonio Ramirez\",\n            \"email\": \"amigo.cobos@gmail.com\"\n        }\n    ],\n\n    // package dependencies\n    \"require\": {\n        \"yiisoft/yii2\": \"~2.0.0\",\n        \"imagine/imagine\": \"v0.5.0\"\n    },\n\n    // class autoloading specs\n    \"autoload\": {\n        \"psr-4\": {\n            \"yii\\\\imagine\\\\\": \"\"\n        }\n    }\n}\n```\n\n\n#### Package Name <span id=\"package-name\"></span>\n\nEach Composer package should have a package name which uniquely identifies the package among all others.\nThe format of package names is `vendorName/projectName`. For example, in the package name `yiisoft/yii2-imagine`,\nthe vendor name and the project name are `yiisoft` and `yii2-imagine`, respectively.\n\nDo NOT use `yiisoft` as your vendor name as it is reserved for use by the Yii core code.\n\nWe recommend you prefix `yii2-` to the project name for packages representing Yii 2 extensions, for example,\n`myname/yii2-mywidget`. This will allow users to more easily tell whether a package is a Yii 2 extension.\n\n\n#### Package Type <span id=\"package-type\"></span>\n\nIt is important that you specify the package type of your extension as `yii2-extension` so that the package can\nbe recognized as a Yii extension when being installed.\n\nWhen a user runs `composer install` to install an extension, the file `vendor/yiisoft/extensions.php`\nwill be automatically updated to include the information about the new extension. From this file, Yii applications\ncan know which extensions are installed (the information can be accessed via [[yii\\base\\Application::extensions]]).\n\n\n#### Dependencies <span id=\"dependencies\"></span>\n\nYour extension depends on Yii (of course). So you should list it (`yiisoft/yii2`) in the `require` entry in `composer.json`.\nIf your extension also depends on other extensions or third-party libraries, you should list them as well.\nMake sure you also list appropriate version constraints (e.g. `1.*`, `@stable`) for each dependent package. Use stable\ndependencies when your extension is released in a stable version.\n\nMost JavaScript/CSS packages are managed using [Bower](https://bower.io/) and/or [NPM](https://www.npmjs.com/),\ninstead of Composer. Yii uses the [Composer asset plugin](https://github.com/fxpio/composer-asset-plugin)\nto enable managing these kinds of packages through Composer. If your extension depends on a Bower package, you can\nsimply list the dependency in `composer.json` like the following:\n\n```json\n{\n    // package dependencies\n    \"require\": {\n        \"bower-asset/jquery\": \">=1.11.*\"\n    }\n}\n```\n\nThe above code states that the extension depends on the `jquery` Bower package. In general, you can use\n`bower-asset/PackageName` to refer to a Bower package in `composer.json`, and use `npm-asset/PackageName`\nto refer to a NPM package. When Composer installs a Bower or NPM package, by default the package content will be\ninstalled under the `@vendor/bower/PackageName` and `@vendor/npm/Packages` directories, respectively.\nThese two directories can also be referred to using the shorter aliases `@bower/PackageName` and `@npm/PackageName`.\n\nFor more details about asset management, please refer to the [Assets](structure-assets.md#bower-npm-assets) section.\n\n\n#### Class Autoloading <span id=\"class-autoloading\"></span>\n\nIn order for your classes to be autoloaded by the Yii class autoloader or the Composer class autoloader,\nyou should specify the `autoload` entry in the `composer.json` file, like shown below:\n\n```json\n{\n    // ....\n\n    \"autoload\": {\n        \"psr-4\": {\n            \"yii\\\\imagine\\\\\": \"\"\n        }\n    }\n}\n```\n\nYou may list one or multiple root namespaces and their corresponding file paths.\n\nWhen the extension is installed in an application, Yii will create for each listed root namespace\nan [alias](concept-aliases.md#extension-aliases) that refers to the directory corresponding to the namespace.\nFor example, the above `autoload` declaration will correspond to an alias named `@yii/imagine`.\n\n\n### Recommended Practices <span id=\"recommended-practices\"></span>\n\nBecause extensions are meant to be used by other people, you often need to make an extra effort during development. Below\nwe introduce some common and recommended practices in creating high quality extensions.\n\n\n#### Namespaces <span id=\"namespaces\"></span>\n\nTo avoid name collisions and make the classes in your extension autoloadable, you should use namespaces and\nname the classes in your extension by following the [PSR-4 standard](https://www.php-fig.org/psr/psr-4/) or\n[PSR-0 standard](https://www.php-fig.org/psr/psr-0/).\n\nYour class namespaces should start with `vendorName\\extensionName`, where `extensionName` is similar to the project name\nin the package name except that it should not contain the `yii2-` prefix. For example, for the `yiisoft/yii2-imagine`\nextension, we use `yii\\imagine` as the namespace for its classes.\n\nDo not use `yii`, `yii2` or `yiisoft` as your vendor name. These names are reserved for use by the Yii core code.\n\n\n#### Bootstrapping Classes <span id=\"bootstrapping-classes\"></span>\n\nSometimes, you may want your extension to execute some code during the [bootstrapping process](runtime-bootstrapping.md)\nstage of an application. For example, your extension may want to respond to the application's `beginRequest` event\nto adjust some environment settings. While you can instruct users of the extension to explicitly attach your event\nhandler in the extension to the `beginRequest` event, a better way is to do this automatically.\n\nTo achieve this goal, you can create a so-called *bootstrapping class* by implementing [[yii\\base\\BootstrapInterface]].\nFor example,\n\n```php\nnamespace myname\\mywidget;\n\nuse yii\\base\\BootstrapInterface;\nuse yii\\base\\Application;\n\nclass MyBootstrapClass implements BootstrapInterface\n{\n    public function bootstrap($app)\n    {\n        $app->on(Application::EVENT_BEFORE_REQUEST, function () {\n             // do something here\n        });\n    }\n}\n```\n\nYou then list this class in the `composer.json` file of your extension like follows,\n\n```json\n{\n    // ...\n\n    \"extra\": {\n        \"bootstrap\": \"myname\\\\mywidget\\\\MyBootstrapClass\"\n    }\n}\n```\n\nWhen the extension is installed in an application, Yii will automatically instantiate the bootstrapping class\nand call its [[yii\\base\\BootstrapInterface::bootstrap()|bootstrap()]] method during the bootstrapping process for\nevery request.\n\n\n#### Working with Databases <span id=\"working-with-databases\"></span>\n\nYour extension may need to access databases. Do not assume that the applications that use your extension will always\nuse `Yii::$db` as the DB connection. Instead, you should declare a `db` property for the classes that require DB access.\nThe property will allow users of your extension to customize which DB connection they would like your extension to use.\nAs an example, you may refer to the [[yii\\caching\\DbCache]] class and see how it declares and uses the `db` property.\n\nIf your extension needs to create specific DB tables or make changes to DB schema, you should\n\n- provide [migrations](db-migrations.md) to manipulate DB schema, rather than using plain SQL files;\n- try to make the migrations applicable to different DBMS;\n- avoid using [Active Record](db-active-record.md) in the migrations.\n\n\n#### Using Assets <span id=\"using-assets\"></span>\n\nIf your extension is a widget or a module, chances are that it may require some [assets](structure-assets.md) to work.\nFor example, a module may display some pages which contain images, JavaScript, and CSS. Because the files of an\nextension are all under the same directory which is not Web accessible when installed in an application, you have\ntwo choices to make the asset files directly accessible via Web:\n\n- ask users of the extension to manually copy the asset files to a specific Web-accessible folder;\n- declare an [asset bundle](structure-assets.md) and rely on the asset publishing mechanism to automatically\n  copy the files listed in the asset bundle to a Web-accessible folder.\n\nWe recommend you use the second approach so that your extension can be more easily used by other people.\nPlease refer to the [Assets](structure-assets.md) section for more details about how to work with assets in general.\n\n\n#### Internationalization and Localization <span id=\"i18n-l10n\"></span>\n\nYour extension may be used by applications supporting different languages! Therefore, if your extension displays\ncontent to end users, you should try to [internationalize and localize](tutorial-i18n.md) it. In particular,\n\n- If the extension displays messages intended for end users, the messages should be wrapped into `Yii::t()`\n  so that they can be translated. Messages meant for developers (such as internal exception messages) do not need\n  to be translated.\n- If the extension displays numbers, dates, etc., they should be formatted using [[yii\\i18n\\Formatter]] with\n  appropriate formatting rules.\n\nFor more details, please refer to the [Internationalization](tutorial-i18n.md) section.\n\n\n#### Testing <span id=\"testing\"></span>\n\nYou want your extension to run flawlessly without bringing problems to other people. To reach this goal, you should\ntest your extension before releasing it to public.\n\nIt is recommended that you create various test cases to cover your extension code rather than relying on manual tests.\nEach time before you release a new version of your extension, you may simply run these test cases to make sure\neverything is in good shape. Yii provides testing support, which can help you to more easily write unit tests,\nacceptance tests and functionality tests. For more details, please refer to the [Testing](test-overview.md) section.\n\n\n#### Versioning <span id=\"versioning\"></span>\n\nYou should give each release of your extension a version number (e.g. `1.0.1`). We recommend you follow the\n[semantic versioning](https://semver.org) practice when determining what version numbers should be used.\n\n\n#### Releasing <span id=\"releasing\"></span>\n\nTo let other people know about your extension, you need to release it to the public.\n\nIf it is the first time you are releasing an extension, you should register it on a Composer repository, such as\n[Packagist](https://packagist.org/). After that, all you need to do is simply create a release tag (e.g. `v1.0.1`)\non the VCS repository of your extension and notify the Composer repository about the new release. People will\nthen be able to find the new release, and install or update the extension through the Composer repository.\n\nIn the releases of your extension, in addition to code files, you should also consider including the following to\nhelp other people learn about and use your extension:\n\n* A readme file in the package root directory: it describes what your extension does and how to install and use it.\n  We recommend you write it in [Markdown](https://daringfireball.net/projects/markdown/) format and name the file\n  as `readme.md`.\n* A changelog file in the package root directory: it lists what changes are made in each release. The file\n  may be written in Markdown format and named as `changelog.md`.\n* An upgrade file in the package root directory: it gives the instructions on how to upgrade from older releases\n  of the extension. The file may be written in Markdown format and named as `upgrade.md`.\n* Tutorials, demos, screenshots, etc.: these are needed if your extension provides many features that cannot be\n  fully covered in the readme file.\n* API documentation: your code should be well documented to allow other people to more easily read and understand it.\n  You may refer to the [BaseObject class file](https://github.com/yiisoft/yii2/blob/master/framework/base/BaseObject.php)\n  to learn how to document your code.\n\n> Info: Your code comments can be written in Markdown format. The `yiisoft/yii2-apidoc` extension provides a tool\n  for you to generate pretty API documentation based on your code comments.\n\n> Info: While not a requirement, we suggest your extension adhere to certain coding styles. You may refer to\n  the [core framework code style](https://github.com/yiisoft/yii2/blob/master/docs/internals/core-code-style.md).\n\n\n## Core Extensions <span id=\"core-extensions\"></span>\n\nYii provides the following core extensions (or [\"Official Extensions\"](https://www.yiiframework.com/extensions/official)) that are developed and maintained by the Yii developer team. They are all\nregistered on [Packagist](https://packagist.org/) and can be easily installed as described in the\n[Using Extensions](#using-extensions) subsection.\n\n- [yiisoft/yii2-apidoc](https://www.yiiframework.com/extension/yiisoft/yii2-apidoc):\n  provides an extensible and high-performance API documentation generator. It is also used to generate the core\n  framework API documentation.\n- [yiisoft/yii2-authclient](https://www.yiiframework.com/extension/yiisoft/yii2-authclient):\n  provides a set of commonly used auth clients, such as Facebook OAuth2 client, GitHub OAuth2 client.\n- [yiisoft/yii2-bootstrap](https://www.yiiframework.com/extension/yiisoft/yii2-bootstrap):\n  provides a set of widgets that encapsulate the [Bootstrap](https://getbootstrap.com/) components and plugins.\n- [yiisoft/yii2-debug](https://www.yiiframework.com/extension/yiisoft/yii2-debug):\n  provides debugging support for Yii applications. When this extension is used, a debugger toolbar will appear\n  at the bottom of every page. The extension also provides a set of standalone pages to display more detailed\n  debug information.\n- [yiisoft/yii2-elasticsearch](https://www.yiiframework.com/extension/yiisoft/yii2-elasticsearch):\n  provides the support for using [Elasticsearch](https://www.elastic.co/). It includes basic querying/search\n  support and also implements the [Active Record](db-active-record.md) pattern that allows you to store active records\n  in Elasticsearch.\n- [yiisoft/yii2-faker](https://www.yiiframework.com/extension/yiisoft/yii2-faker):\n  provides the support for using [Faker](https://www.yiiframework.com/extension/fzaninotto/Faker) to generate fake data for you.\n- [yiisoft/yii2-gii](https://www.yiiframework.com/extension/yiisoft/yii2-gii):\n  provides a Web-based code generator that is highly extensible and can be used to quickly generate models,\n  forms, modules, CRUD, etc.\n- [yiisoft/yii2-httpclient](https://www.yiiframework.com/extension/yiisoft/yii2-httpclient):\n  provides an HTTP client.\n- [yiisoft/yii2-imagine](https://www.yiiframework.com/extension/yiisoft/yii2-imagine):\n  provides commonly used image manipulation functions based on [Imagine](https://imagine.readthedocs.org/).\n- [yiisoft/yii2-jui](https://www.yiiframework.com/extension/yiisoft/yii2-jui):\n  provides a set of widgets that encapsulate the [JQuery UI](https://jqueryui.com/) interactions and widgets.\n- [yiisoft/yii2-mongodb](https://www.yiiframework.com/extension/yiisoft/yii2-mongodb):\n  provides the support for using [MongoDB](https://www.mongodb.com/). It includes features such as basic query,\n  Active Record, migrations, caching, code generation, etc.\n- [yiisoft/yii2-queue](https://www.yiiframework.com/extension/yiisoft/yii2-queue):\n  provides the supports for running tasks asynchronously via queues.\n  It supports queues based on DB, Redis, RabbitMQ, AMQP, Beanstalk and Gearman.\n- [yiisoft/yii2-redis](https://www.yiiframework.com/extension/yiisoft/yii2-redis):\n  provides the support for using [redis](https://redis.io/). It includes features such as basic query,\n  Active Record, caching, etc.\n- [yiisoft/yii2-shell](https://www.yiiframework.com/extension/yiisoft/yii2-shell):\n  provides an interactive shell based on [psysh](https://psysh.org/).\n- [yiisoft/yii2-smarty](https://www.yiiframework.com/extension/yiisoft/yii2-smarty):\n  provides a template engine based on [Smarty](https://www.smarty.net/).\n- [yiisoft/yii2-sphinx](https://www.yiiframework.com/extension/yiisoft/yii2-sphinx):\n  provides the support for using [Sphinx](https://sphinxsearch.com/). It includes features such as basic query,\n  Active Record, code generation, etc.\n- [yiisoft/yii2-symfonymailer](https://www.yiiframework.com/extension/yiisoft/yii2-symfonymailer):\n  provides email sending features based on [Symfony Mailer](https://symfony.com/doc/current/mailer.html).\n- [yiisoft/yii2-twig](https://www.yiiframework.com/extension/yiisoft/yii2-twig):\n  provides a template engine based on [Twig](https://twig.symfony.com/).\n\nThe following official extensions are for Yii 2.1 and above.\nYou don't need to install them for Yii 2.0, since they are included in the core framework.\n\n- [yiisoft/yii2-captcha](https://www.yiiframework.com/extension/yiisoft/yii2-captcha):\n  provides an CAPTCHA.\n- [yiisoft/yii2-jquery](https://www.yiiframework.com/extension/yiisoft/yii2-jquery):\n  provides a support for [jQuery](https://jquery.com/).\n- [yiisoft/yii2-maskedinput](https://www.yiiframework.com/extension/yiisoft/yii2-maskedinput):\n  provides a masked input widget based on [jQuery Input Mask plugin](https://robinherbots.github.io/Inputmask/).\n- [yiisoft/yii2-mssql](https://www.yiiframework.com/extension/yiisoft/yii2-mssql):\n  provides the support for using [MSSQL](https://www.microsoft.com/sql-server/).\n- [yiisoft/yii2-oracle](https://www.yiiframework.com/extension/yiisoft/yii2-oracle):\n  provides the support for using [Oracle](https://www.oracle.com/).\n- [yiisoft/yii2-rest](https://www.yiiframework.com/extension/yiisoft/yii2-rest):\n  provides a support for the REST API.\n"
  },
  {
    "path": "docs/guide/structure-filters.md",
    "content": "Filters\n=======\n\nFilters are objects that run before and/or after [controller actions](structure-controllers.md#actions). For example,\nan access control filter may run before actions to ensure that they are allowed to be accessed by particular end users;\na content compression filter may run after actions to compress the response content before sending them out to end users.\n\nA filter may consist of a pre-filter (filtering logic applied *before* actions) and/or a post-filter (logic applied\n*after* actions).\n\n\n## Using Filters <span id=\"using-filters\"></span>\n\nFilters are essentially a special kind of [behaviors](concept-behaviors.md). Therefore, using filters is the same\nas [using behaviors](concept-behaviors.md#attaching-behaviors). You can declare filters in a controller class\nby overriding its [[yii\\base\\Controller::behaviors()|behaviors()]] method like the following:\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\HttpCache',\n            'only' => ['index', 'view'],\n            'lastModified' => function ($action, $params) {\n                $q = new \\yii\\db\\Query();\n                return $q->from('user')->max('updated_at');\n            },\n        ],\n    ];\n}\n```\n\nBy default, filters declared in a controller class will be applied to *all* actions in that controller. You can,\nhowever, explicitly specify which actions the filter should be applied to by configuring the\n[[yii\\base\\ActionFilter::only|only]] property. In the above example, the `HttpCache` filter only applies to the\n`index` and `view` actions. You can also configure the [[yii\\base\\ActionFilter::except|except]] property to prevent\nsome actions from being filtered.\n\nBesides controllers, you can also declare filters in a [module](structure-modules.md) or [application](structure-applications.md).\nWhen you do so, the filters will be applied to *all* controller actions belonging to that module or application,\nunless you configure the filters' [[yii\\base\\ActionFilter::only|only]] and [[yii\\base\\ActionFilter::except|except]]\nproperties like described above.\n\n> Note: When declaring filters in modules or applications, you should use [routes](structure-controllers.md#routes)\n  instead of action IDs in the [[yii\\base\\ActionFilter::only|only]] and [[yii\\base\\ActionFilter::except|except]] properties.\n  This is because action IDs alone cannot fully specify actions within the scope of a module or application.\n\nWhen multiple filters are configured for a single action, they are applied according to the rules described below:\n\n* Pre-filtering\n    - Apply filters declared in the application in the order they are listed in `behaviors()`.\n    - Apply filters declared in the module in the order they are listed in `behaviors()`.\n    - Apply filters declared in the controller in the order they are listed in `behaviors()`.\n    - If any of the filters cancel the action execution, the filters (both pre-filters and post-filters) after it will\n      not be applied.\n* Running the action if it passes the pre-filtering.\n* Post-filtering\n    - Apply filters declared in the controller in the reverse order they are listed in `behaviors()`.\n    - Apply filters declared in the module in the reverse order they are listed in `behaviors()`.\n    - Apply filters declared in the application in the reverse order they are listed in `behaviors()`.\n\n\n## Creating Filters <span id=\"creating-filters\"></span>\n\nTo create a new action filter, extend from [[yii\\base\\ActionFilter]] and override the\n[[yii\\base\\ActionFilter::beforeAction()|beforeAction()]] and/or [[yii\\base\\ActionFilter::afterAction()|afterAction()]]\nmethods. The former will be executed before an action runs while the latter after an action runs.\nThe return value of [[yii\\base\\ActionFilter::beforeAction()|beforeAction()]] determines whether an action should\nbe executed or not. If it is `false`, the filters after this one will be skipped and the action will not be executed.\n\nThe following example shows a filter that logs the action execution time:\n\n```php\nnamespace app\\components;\n\nuse Yii;\nuse yii\\base\\ActionFilter;\n\nclass ActionTimeFilter extends ActionFilter\n{\n    private $_startTime;\n\n    public function beforeAction($action)\n    {\n        $this->_startTime = microtime(true);\n        return parent::beforeAction($action);\n    }\n\n    public function afterAction($action, $result)\n    {\n        $time = microtime(true) - $this->_startTime;\n        Yii::debug(\"Action '{$action->uniqueId}' spent $time second.\");\n        return parent::afterAction($action, $result);\n    }\n}\n```\n\n\n## Core Filters <span id=\"core-filters\"></span>\n\nYii provides a set of commonly used filters, found primarily under the `yii\\filters` namespace. In the following,\nwe will briefly introduce these filters.\n\n\n### [[yii\\filters\\AccessControl|AccessControl]] <span id=\"access-control\"></span>\n\nAccessControl provides simple access control based on a set of [[yii\\filters\\AccessControl::rules|rules]].\nIn particular, before an action is executed, AccessControl will examine the listed rules and find the first one\nthat matches the current context variables (such as user IP address, user login status, etc.) The matching\nrule will dictate whether to allow or deny the execution of the requested action. If no rule matches, the access\nwill be denied.\n\nThe following example shows how to allow authenticated users to access the `create` and `update` actions\nwhile denying all other users from accessing these two actions.\n\n```php\nuse yii\\filters\\AccessControl;\n\npublic function behaviors()\n{\n    return [\n        'access' => [\n            'class' => AccessControl::class,\n            'only' => ['create', 'update'],\n            'rules' => [\n                // allow authenticated users\n                [\n                    'allow' => true,\n                    'roles' => ['@'],\n                ],\n                // everything else is denied by default\n            ],\n        ],\n    ];\n}\n```\n\nFor more details about access control in general, please refer to the [Authorization](security-authorization.md) section.\n\n\n### Authentication Method Filters <span id=\"auth-method-filters\"></span>\n\nAuthentication method filters are used to authenticate a user using various methods, such as\n[HTTP Basic Auth](https://en.wikipedia.org/wiki/Basic_access_authentication), [OAuth 2](https://oauth.net/2/).\nThese filter classes are all under the `yii\\filters\\auth` namespace.\n\nThe following example shows how you can use [[yii\\filters\\auth\\HttpBasicAuth]] to authenticate a user using\nan access token based on HTTP Basic Auth method. Note that in order for this to work, your\n[[yii\\web\\User::identityClass|user identity class]] must implement the [[yii\\web\\IdentityInterface::findIdentityByAccessToken()|findIdentityByAccessToken()]]\nmethod.\n\n```php\nuse yii\\filters\\auth\\HttpBasicAuth;\n\npublic function behaviors()\n{\n    return [\n        'basicAuth' => [\n            'class' => HttpBasicAuth::class,\n        ],\n    ];\n}\n```\n\nAuthentication method filters are commonly used in implementing RESTful APIs. For more details, please refer to the\nRESTful [Authentication](rest-authentication.md) section.\n\n\n### [[yii\\filters\\ContentNegotiator|ContentNegotiator]] <span id=\"content-negotiator\"></span>\n\nContentNegotiator supports response format negotiation and application language negotiation. It will try to\ndetermine the response format and/or language by examining `GET` parameters and `Accept` HTTP header.\n\nIn the following example, ContentNegotiator is configured to support JSON and XML response formats, and\nEnglish (United States) and German languages.\n\n```php\nuse yii\\filters\\ContentNegotiator;\nuse yii\\web\\Response;\n\npublic function behaviors()\n{\n    return [\n        [\n            'class' => ContentNegotiator::class,\n            'formats' => [\n                'application/json' => Response::FORMAT_JSON,\n                'application/xml' => Response::FORMAT_XML,\n            ],\n            'languages' => [\n                'en-US',\n                'de',\n            ],\n        ],\n    ];\n}\n```\n\nResponse formats and languages often need to be determined much earlier during\nthe [application lifecycle](structure-applications.md#application-lifecycle). For this reason, ContentNegotiator\nis designed in a way such that it can also be used as a [bootstrapping component](structure-applications.md#bootstrap)\nbesides being used as a filter. For example, you may configure it in the [application configuration](structure-applications.md#application-configurations)\nlike the following:\n\n```php\nuse yii\\filters\\ContentNegotiator;\nuse yii\\web\\Response;\n\n[\n    'bootstrap' => [\n        [\n            'class' => ContentNegotiator::class,\n            'formats' => [\n                'application/json' => Response::FORMAT_JSON,\n                'application/xml' => Response::FORMAT_XML,\n            ],\n            'languages' => [\n                'en-US',\n                'de',\n            ],\n        ],\n    ],\n];\n```\n\n> Info: In case the preferred content type and language cannot be determined from a request, the first format and\n  language listed in [[formats]] and [[languages]] will be used.\n\n\n\n### [[yii\\filters\\HttpCache|HttpCache]] <span id=\"http-cache\"></span>\n\nHttpCache implements client-side caching by utilizing the `Last-Modified` and `Etag` HTTP headers.\nFor example,\n\n```php\nuse yii\\filters\\HttpCache;\n\npublic function behaviors()\n{\n    return [\n        [\n            'class' => HttpCache::class,\n            'only' => ['index'],\n            'lastModified' => function ($action, $params) {\n                $q = new \\yii\\db\\Query();\n                return $q->from('user')->max('updated_at');\n            },\n        ],\n    ];\n}\n```\n\nPlease refer to the [HTTP Caching](caching-http.md) section for more details about using HttpCache.\n\n\n### [[yii\\filters\\PageCache|PageCache]] <span id=\"page-cache\"></span>\n\nPageCache implements server-side caching of whole pages. In the following example, PageCache is applied\nto the `index` action to cache the whole page for maximum 60 seconds or until the count of entries in the `post`\ntable changes. It also stores different versions of the page depending on the chosen application language.\n\n```php\nuse yii\\filters\\PageCache;\nuse yii\\caching\\DbDependency;\n\npublic function behaviors()\n{\n    return [\n        'pageCache' => [\n            'class' => PageCache::class,\n            'only' => ['index'],\n            'duration' => 60,\n            'dependency' => [\n                'class' => DbDependency::class,\n                'sql' => 'SELECT COUNT(*) FROM post',\n            ],\n            'variations' => [\n                \\Yii::$app->language,\n            ]\n        ],\n    ];\n}\n```\n\nPlease refer to the [Page Caching](caching-page.md) section for more details about using PageCache.\n\n\n### [[yii\\filters\\RateLimiter|RateLimiter]] <span id=\"rate-limiter\"></span>\n\nRateLimiter implements a rate limiting algorithm based on the [leaky bucket algorithm](https://en.wikipedia.org/wiki/Leaky_bucket).\nIt is primarily used in implementing RESTful APIs. Please refer to the [Rate Limiting](rest-rate-limiting.md) section\nfor details about using this filter.\n\n\n### [[yii\\filters\\VerbFilter|VerbFilter]] <span id=\"verb-filter\"></span>\n\nVerbFilter checks if the HTTP request methods are allowed by the requested actions. If not allowed, it will\nthrow an HTTP 405 exception. In the following example, VerbFilter is declared to specify a typical set of allowed\nrequest methods for CRUD actions.\n\n```php\nuse yii\\filters\\VerbFilter;\n\npublic function behaviors()\n{\n    return [\n        'verbs' => [\n            'class' => VerbFilter::class,\n            'actions' => [\n                'index'  => ['get'],\n                'view'   => ['get'],\n                'create' => ['get', 'post'],\n                'update' => ['get', 'put', 'post'],\n                'delete' => ['post', 'delete'],\n            ],\n        ],\n    ];\n}\n```\n\n### [[yii\\filters\\Cors|Cors]] <span id=\"cors\"></span>\n\nCross-origin resource sharing [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) is a mechanism that allows many resources (e.g. fonts, JavaScript, etc.)\non a Web page to be requested from another domain outside the domain the resource originated from.\nIn particular, JavaScript's AJAX calls can use the XMLHttpRequest mechanism. Such \"cross-domain\" requests would\notherwise be forbidden by Web browsers, per the same origin security policy.\nCORS defines a way in which the browser and the server can interact to determine whether or not to allow the cross-origin request.\n\nThe [[yii\\filters\\Cors|Cors filter]] should be defined before Authentication / Authorization filters to make sure the CORS headers\nwill always be sent.\n\n```php\nuse yii\\filters\\Cors;\nuse yii\\helpers\\ArrayHelper;\n\npublic function behaviors()\n{\n    return ArrayHelper::merge([\n        [\n            'class' => Cors::class,\n        ],\n    ], parent::behaviors());\n}\n```\n\nAlso check the section on [REST Controllers](rest-controllers.md#cors) if you want to add the CORS filter to an\n[[yii\\rest\\ActiveController]] class in your API.\n\nThe Cors filtering could be tuned using the [[yii\\filters\\Cors::$cors|$cors]] property.\n\n* `cors['Origin']`: array used to define allowed origins. Can be `['*']` (everyone) or `['https://www.myserver.net', 'https://www.myotherserver.com']`. Default to `['*']`.\n* `cors['Access-Control-Request-Method']`: array of allowed verbs like `['GET', 'OPTIONS', 'HEAD']`.  Default to `['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']`.\n* `cors['Access-Control-Request-Headers']`: array of allowed headers. Can be `['*']` all headers or specific ones `['X-Request-With']`. Default to `['*']`.\n* `cors['Access-Control-Allow-Credentials']`: define if current request can be made using credentials. Can be `true`, `false` or `null` (not set). Default to `null`.\n* `cors['Access-Control-Max-Age']`: define lifetime of pre-flight request. Default to `86400`.\n\nFor example, allowing CORS for origin : `https://www.myserver.net` with method `GET`, `HEAD` and `OPTIONS` :\n\n```php\nuse yii\\filters\\Cors;\nuse yii\\helpers\\ArrayHelper;\n\npublic function behaviors()\n{\n    return ArrayHelper::merge([\n        [\n            'class' => Cors::class,\n            'cors' => [\n                'Origin' => ['https://www.myserver.net'],\n                'Access-Control-Request-Method' => ['GET', 'HEAD', 'OPTIONS'],\n            ],\n        ],\n    ], parent::behaviors());\n}\n```\n\nYou may tune the CORS headers by overriding default parameters on a per action basis.\nFor example adding the `Access-Control-Allow-Credentials` for the `login` action could be done like this :\n\n```php\nuse yii\\filters\\Cors;\nuse yii\\helpers\\ArrayHelper;\n\npublic function behaviors()\n{\n    return ArrayHelper::merge([\n        [\n            'class' => Cors::class,\n            'cors' => [\n                'Origin' => ['https://www.myserver.net'],\n                'Access-Control-Request-Method' => ['GET', 'HEAD', 'OPTIONS'],\n            ],\n            'actions' => [\n                'login' => [\n                    'Access-Control-Allow-Credentials' => true,\n                ]\n            ]\n        ],\n    ], parent::behaviors());\n}\n```\n"
  },
  {
    "path": "docs/guide/structure-models.md",
    "content": "Models\n======\n\nModels are part of the [MVC](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) architecture.\nThey are objects representing business data, rules and logic.\n\nYou can create model classes by extending [[yii\\base\\Model]] or its child classes. The base class\n[[yii\\base\\Model]] supports many useful features:\n\n* [Attributes](#attributes): represent the business data and can be accessed like normal object properties\n  or array elements;\n* [Attribute labels](#attribute-labels): specify the display labels for attributes;\n* [Massive assignment](#massive-assignment): supports populating multiple attributes in a single step;\n* [Validation rules](#validation-rules): ensures input data based on the declared validation rules;\n* [Data Exporting](#data-exporting): allows model data to be exported in terms of arrays with customizable formats.\n\nThe `Model` class is also the base class for more advanced models, such as [Active Record](db-active-record.md).\nPlease refer to the relevant documentation for more details about these advanced models.\n\n> Info: You are not required to base your model classes on [[yii\\base\\Model]]. However, because there are many Yii\n  components built to support [[yii\\base\\Model]], it is usually the preferable base class for a model.\n\n\n## Attributes <span id=\"attributes\"></span>\n\nModels represent business data in terms of *attributes*. Each attribute is like a publicly accessible property\nof a model. The method [[yii\\base\\Model::attributes()]] specifies what attributes a model class has.\n\nYou can access an attribute like accessing a normal object property:\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// \"name\" is an attribute of ContactForm\n$model->name = 'example';\necho $model->name;\n```\n\nYou can also access attributes like accessing array elements, thanks to the support for\n[ArrayAccess](https://www.php.net/manual/en/class.arrayaccess.php) and [Traversable](https://www.php.net/manual/en/class.traversable.php)\nby [[yii\\base\\Model]]:\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// accessing attributes like array elements\n$model['name'] = 'example';\necho $model['name'];\n\n// Model is traversable using foreach.\nforeach ($model as $name => $value) {\n    echo \"$name: $value\\n\";\n}\n```\n\n\n### Defining Attributes <span id=\"defining-attributes\"></span>\n\nBy default, if your model class extends directly from [[yii\\base\\Model]], all its *non-static public* member\nvariables are attributes. For example, the `ContactForm` model class below has four attributes: `name`, `email`,\n`subject` and `body`. The `ContactForm` model is used to represent the input data received from an HTML form.\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\n\nclass ContactForm extends Model\n{\n    public $name;\n    public $email;\n    public $subject;\n    public $body;\n}\n```\n\n\nYou may override [[yii\\base\\Model::attributes()]] to define attributes in a different way. The method should\nreturn the names of the attributes in a model. For example, [[yii\\db\\ActiveRecord]] does so by returning\nthe column names of the associated database table as its attribute names. Note that you may also need to\noverride the magic methods such as `__get()`, `__set()` so that the attributes can be accessed like\nnormal object properties.\n\n\n### Attribute Labels <span id=\"attribute-labels\"></span>\n\nWhen displaying values or getting input for attributes, you often need to display some labels associated\nwith attributes. For example, given an attribute named `firstName`, you may want to display a label `First Name`\nwhich is more user-friendly when displayed to end users in places such as form inputs and error messages.\n\nYou can get the label of an attribute by calling [[yii\\base\\Model::getAttributeLabel()]]. For example,\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// displays \"Name\"\necho $model->getAttributeLabel('name');\n```\n\nBy default, attribute labels are automatically generated from attribute names. The generation is done by\nthe method [[yii\\base\\Model::generateAttributeLabel()]]. It will turn camel-case variable names into\nmultiple words with the first letter in each word in upper case. For example, `username` becomes `Username`,\nand `firstName` becomes `First Name`.\n\nIf you do not want to use automatically generated labels, you may override [[yii\\base\\Model::attributeLabels()]]\nto explicitly declare attribute labels. For example,\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\n\nclass ContactForm extends Model\n{\n    public $name;\n    public $email;\n    public $subject;\n    public $body;\n\n    public function attributeLabels()\n    {\n        return [\n            'name' => 'Your name',\n            'email' => 'Your email address',\n            'subject' => 'Subject',\n            'body' => 'Content',\n        ];\n    }\n}\n```\n\nFor applications supporting multiple languages, you may want to translate attribute labels. This can be done\nin the [[yii\\base\\Model::attributeLabels()|attributeLabels()]] method as well, like the following:\n\n```php\npublic function attributeLabels()\n{\n    return [\n        'name' => \\Yii::t('app', 'Your name'),\n        'email' => \\Yii::t('app', 'Your email address'),\n        'subject' => \\Yii::t('app', 'Subject'),\n        'body' => \\Yii::t('app', 'Content'),\n    ];\n}\n```\n\nYou may even conditionally define attribute labels. For example, based on the [scenario](#scenarios) the model\nis being used in, you may return different labels for the same attribute.\n\n> Info: Strictly speaking, attribute labels are part of [views](structure-views.md). But declaring labels\n  in models is often very convenient and can result in very clean and reusable code.\n\n\n## Scenarios <span id=\"scenarios\"></span>\n\nA model may be used in different *scenarios*. For example, a `User` model may be used to collect user login inputs,\nbut it may also be used for the user registration purpose. In different scenarios, a model may use different\nbusiness rules and logic. For example, the `email` attribute may be required during user registration,\nbut not so during user login.\n\nA model uses the [[yii\\base\\Model::scenario]] property to keep track of the scenario it is being used in.\nBy default, a model supports only a single scenario named `default`. The following code shows two ways of\nsetting the scenario of a model:\n\n```php\n// scenario is set as a property\n$model = new User;\n$model->scenario = User::SCENARIO_LOGIN;\n\n// scenario is set through configuration\n$model = new User(['scenario' => User::SCENARIO_LOGIN]);\n```\n\nBy default, the scenarios supported by a model are determined by the [validation rules](#validation-rules) declared\nin the model. However, you can customize this behavior by overriding the [[yii\\base\\Model::scenarios()]] method,\nlike the following:\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass User extends ActiveRecord\n{\n    const SCENARIO_LOGIN = 'login';\n    const SCENARIO_REGISTER = 'register';\n\n    public function scenarios()\n    {\n        return [\n            self::SCENARIO_LOGIN => ['username', 'password'],\n            self::SCENARIO_REGISTER => ['username', 'email', 'password'],\n        ];\n    }\n}\n```\n\n> Info: In the above and following examples, the model classes are extending from [[yii\\db\\ActiveRecord]]\n  because the usage of multiple scenarios usually happens to [Active Record](db-active-record.md) classes.\n\nThe `scenarios()` method returns an array whose keys are the scenario names and values the corresponding\n*active attributes*. An active attribute can be [massively assigned](#massive-assignment) and is subject\nto [validation](#validation-rules). In the above example, the `username` and `password` attributes are active\nin the `login` scenario; while in the `register` scenario, `email` is also active besides `username` and `password`.\n\nThe default implementation of `scenarios()` will return all scenarios found in the validation rule declaration\nmethod [[yii\\base\\Model::rules()]]. When overriding `scenarios()`, if you want to introduce new scenarios\nin addition to the default ones, you may write code like the following:\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass User extends ActiveRecord\n{\n    const SCENARIO_LOGIN = 'login';\n    const SCENARIO_REGISTER = 'register';\n\n    public function scenarios()\n    {\n        $scenarios = parent::scenarios();\n        $scenarios[self::SCENARIO_LOGIN] = ['username', 'password'];\n        $scenarios[self::SCENARIO_REGISTER] = ['username', 'email', 'password'];\n        return $scenarios;\n    }\n}\n```\n\nThe scenario feature is primarily used by [validation](#validation-rules) and [massive attribute assignment](#massive-assignment).\nYou can, however, use it for other purposes. For example, you may declare [attribute labels](#attribute-labels)\ndifferently based on the current scenario.\n\n\n## Validation Rules <span id=\"validation-rules\"></span>\n\nWhen the data for a model is received from end users, it should be validated to make sure it satisfies\ncertain rules (called *validation rules*, also known as *business rules*). For example, given a `ContactForm` model,\nyou may want to make sure all attributes are not empty and the `email` attribute contains a valid email address.\nIf the values for some attributes do not satisfy the corresponding business rules, appropriate error messages\nshould be displayed to help the user to fix the errors.\n\nYou may call [[yii\\base\\Model::validate()]] to validate the received data. The method will use\nthe validation rules declared in [[yii\\base\\Model::rules()]] to validate every relevant attribute. If no error\nis found, it will return `true`. Otherwise, it will keep the errors in the [[yii\\base\\Model::errors]] property\nand return `false`. For example,\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// populate model attributes with user inputs\n$model->attributes = \\Yii::$app->request->post('ContactForm');\n\nif ($model->validate()) {\n    // all inputs are valid\n} else {\n    // validation failed: $errors is an array containing error messages\n    $errors = $model->errors;\n}\n```\n\n\nTo declare validation rules associated with a model, override the [[yii\\base\\Model::rules()]] method by returning\nthe rules that the model attributes should satisfy. The following example shows the validation rules declared\nfor the `ContactForm` model:\n\n```php\npublic function rules()\n{\n    return [\n        // the name, email, subject and body attributes are required\n        [['name', 'email', 'subject', 'body'], 'required'],\n\n        // the email attribute should be a valid email address\n        ['email', 'email'],\n    ];\n}\n```\n\nA rule can be used to validate one or multiple attributes, and an attribute may be validated by one or multiple rules.\nPlease refer to the [Validating Input](input-validation.md) section for more details on how to declare\nvalidation rules.\n\nSometimes, you may want a rule to be applied only in certain [scenarios](#scenarios). To do so, you can\nspecify the `on` property of a rule, like the following:\n\n```php\npublic function rules()\n{\n    return [\n        // username, email and password are all required in \"register\" scenario\n        [['username', 'email', 'password'], 'required', 'on' => self::SCENARIO_REGISTER],\n\n        // username and password are required in \"login\" scenario\n        [['username', 'password'], 'required', 'on' => self::SCENARIO_LOGIN],\n        \n        [['username'], 'string'], // username must always be a string, this rule applies to all scenarios\n    ];\n}\n```\n\nIf you do not specify the `on` property, the rule would be applied in all scenarios. A rule is called\nan *active rule* if it can be applied in the current [[yii\\base\\Model::scenario|scenario]].\n\nAn attribute will be validated if and only if it is an active attribute declared in `scenarios()` and\nis associated with one or multiple active rules declared in `rules()`.\n\n\n## Massive Assignment <span id=\"massive-assignment\"></span>\n\nMassive assignment is a convenient way of populating a model with user inputs using a single line of code.\nIt populates the attributes of a model by assigning the input data directly to the [[yii\\base\\Model::$attributes]]\nproperty. The following two pieces of code are equivalent, both trying to assign the form data submitted by end users\nto the attributes of the `ContactForm` model. Clearly, the former, which uses massive assignment, is much cleaner\nand less error prone than the latter:\n\n```php\n$model = new \\app\\models\\ContactForm;\n$model->attributes = \\Yii::$app->request->post('ContactForm');\n```\n\n```php\n$model = new \\app\\models\\ContactForm;\n$data = \\Yii::$app->request->post('ContactForm', []);\n$model->name = isset($data['name']) ? $data['name'] : null;\n$model->email = isset($data['email']) ? $data['email'] : null;\n$model->subject = isset($data['subject']) ? $data['subject'] : null;\n$model->body = isset($data['body']) ? $data['body'] : null;\n```\n\n\n### Safe Attributes <span id=\"safe-attributes\"></span>\n\nMassive assignment only applies to the so-called *safe attributes* which are the attributes listed in\n[[yii\\base\\Model::scenarios()]] for the current [[yii\\base\\Model::scenario|scenario]] of a model.\nFor example, if the `User` model has the following scenario declaration, then when the current scenario\nis `login`, only the `username` and `password` can be massively assigned. Any other attributes will\nbe kept untouched.\n\n```php\npublic function scenarios()\n{\n    return [\n        self::SCENARIO_LOGIN => ['username', 'password'],\n        self::SCENARIO_REGISTER => ['username', 'email', 'password'],\n    ];\n}\n```\n\n> Info: The reason that massive assignment only applies to safe attributes is because you want to\n  control which attributes can be modified by end user data. For example, if the `User` model\n  has a `permission` attribute which determines the permission assigned to the user, you would\n  like this attribute to be modifiable by administrators through a backend interface only.\n\nBecause the default implementation of [[yii\\base\\Model::scenarios()]] will return all scenarios and attributes\nfound in [[yii\\base\\Model::rules()]], if you do not override this method, it means an attribute is safe as long\nas it appears in one of the active validation rules.\n\nFor this reason, a special validator aliased `safe` is provided so that you can declare an attribute\nto be safe without actually validating it. For example, the following rules declare that both `title`\nand `description` are safe attributes.\n\n```php\npublic function rules()\n{\n    return [\n        [['title', 'description'], 'safe'],\n    ];\n}\n```\n\n\n### Unsafe Attributes <span id=\"unsafe-attributes\"></span>\n\nAs described above, the [[yii\\base\\Model::scenarios()]] method serves for two purposes: determining which attributes\nshould be validated, and determining which attributes are safe. In some rare cases, you may want to validate\nan attribute but do not want to mark it safe. You can do so by prefixing an exclamation mark `!` to the attribute\nname when declaring it in `scenarios()`, like the `secret` attribute in the following:\n\n```php\npublic function scenarios()\n{\n    return [\n        self::SCENARIO_LOGIN => ['username', 'password', '!secret'],\n    ];\n}\n```\n\nWhen the model is in the `login` scenario, all three attributes will be validated. However, only the `username`\nand `password` attributes can be massively assigned. To assign an input value to the `secret` attribute, you\nhave to do it explicitly as follows,\n\n```php\n$model->secret = $secret;\n```\n\nThe same can be done in `rules()` method:\n\n```php\npublic function rules()\n{\n    return [\n        [['username', 'password', '!secret'], 'required', 'on' => 'login']\n    ];\n}\n```\n\nIn this case attributes `username`, `password` and `secret` are required, but `secret` must be assigned explicitly.\n\n\n## Data Exporting <span id=\"data-exporting\"></span>\n\nModels often need to be exported in different formats. For example, you may want to convert a collection of\nmodels into JSON or Excel format. The exporting process can be broken down into two independent steps:\n\n- models are converted into arrays;\n- the arrays are converted into target formats.\n\nYou may just focus on the first step, because the second step can be achieved by generic\ndata formatters, such as [[yii\\web\\JsonResponseFormatter]].\n\nThe simplest way of converting a model into an array is to use the [[yii\\base\\Model::$attributes]] property.\nFor example,\n\n```php\n$post = \\app\\models\\Post::findOne(100);\n$array = $post->attributes;\n```\n\nBy default, the [[yii\\base\\Model::$attributes]] property will return the values of *all* attributes\ndeclared in [[yii\\base\\Model::attributes()]].\n\nA more flexible and powerful way of converting a model into an array is to use the [[yii\\base\\Model::toArray()]]\nmethod. Its default behavior is the same as that of [[yii\\base\\Model::$attributes]]. However, it allows you\nto choose which data items, called *fields*, to be put in the resulting array and how they should be formatted.\nIn fact, it is the default way of exporting models in RESTful Web service development, as described in\nthe [Response Formatting](rest-response-formatting.md).\n\n\n### Fields <span id=\"fields\"></span>\n\nA field is simply a named element in the array that is obtained by calling the [[yii\\base\\Model::toArray()]] method\nof a model.\n\nBy default, field names are equivalent to attribute names. However, you can change this behavior by overriding\nthe [[yii\\base\\Model::fields()|fields()]] and/or [[yii\\base\\Model::extraFields()|extraFields()]] methods. Both methods\nshould return a list of field definitions. The fields defined by `fields()` are default fields, meaning that\n`toArray()` will return these fields by default. The `extraFields()` method defines additionally available fields\nwhich can also be returned by `toArray()` as long as you specify them via the `$expand` parameter. For example,\nthe following code will return all fields defined in `fields()` and the `prettyName` and `fullAddress` fields\nif they are defined in `extraFields()`.\n\n```php\n$array = $model->toArray([], ['prettyName', 'fullAddress']);\n```\n\nYou can override `fields()` to add, remove, rename or redefine fields. The return value of `fields()`\nshould be an array. The array keys are the field names, and the array values are the corresponding\nfield definitions which can be either property/attribute names or anonymous functions returning the\ncorresponding field values. In the special case when a field name is the same as its defining attribute\nname, you can omit the array key. For example,\n\n```php\n// explicitly list every field, best used when you want to make sure the changes\n// in your DB table or model attributes do not cause your field changes (to keep API backward compatibility).\npublic function fields()\n{\n    return [\n        // field name is the same as the attribute name\n        'id',\n\n        // field name is \"email\", the corresponding attribute name is \"email_address\"\n        'email' => 'email_address',\n\n        // field name is \"name\", its value is defined by a PHP callback\n        'name' => function () {\n            return $this->first_name . ' ' . $this->last_name;\n        },\n    ];\n}\n\n// filter out some fields, best used when you want to inherit the parent implementation\n// and exclude some sensitive fields.\npublic function fields()\n{\n    $fields = parent::fields();\n\n    // remove fields that contain sensitive information\n    unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);\n\n    return $fields;\n}\n```\n\n> Warning: Because by default all attributes of a model will be included in the exported array, you should\n> examine your data to make sure they do not contain sensitive information. If there is such information,\n> you should override `fields()` to filter them out. In the above example, we choose\n> to filter out `auth_key`, `password_hash` and `password_reset_token`.\n\n\n## Best Practices <span id=\"best-practices\"></span>\n\nModels are the central places to represent business data, rules and logic. They often need to be reused\nin different places. In a well-designed application, models are usually much fatter than\n[controllers](structure-controllers.md).\n\nIn summary, models\n\n* may contain attributes to represent business data;\n* may contain validation rules to ensure the data validity and integrity;\n* may contain methods implementing business logic;\n* should NOT directly access request, session, or any other environmental data. These data should be injected\n  by [controllers](structure-controllers.md) into models;\n* should avoid embedding HTML or other presentational code - this is better done in [views](structure-views.md);\n* avoid having too many [scenarios](#scenarios) in a single model.\n\nYou may usually consider the last recommendation above when you are developing large complex systems.\nIn these systems, models could be very fat because they are used in many places and may thus contain many sets\nof rules and business logic. This often ends up in a nightmare in maintaining the model code\nbecause a single touch of the code could affect several different places. To make the model code more maintainable,\nyou may take the following strategy:\n\n* Define a set of base model classes that are shared by different [applications](structure-applications.md) or\n  [modules](structure-modules.md). These model classes should contain minimal sets of rules and logic that\n  are common among all their usages.\n* In each [application](structure-applications.md) or [module](structure-modules.md) that uses a model,\n  define a concrete model class by extending from the corresponding base model class. The concrete model classes\n  should contain rules and logic that are specific for that application or module.\n\nFor example, in the [Advanced Project Template](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md), you may define a base model\nclass `common\\models\\Post`. Then for the front end application, you define and use a concrete model class\n`frontend\\models\\Post` which extends from `common\\models\\Post`. And similarly for the back end application,\nyou define `backend\\models\\Post`. With this strategy, you will be sure that the code in `frontend\\models\\Post`\nis only specific to the front end application, and if you make any change to it, you do not need to worry if\nthe change may break the back end application.\n"
  },
  {
    "path": "docs/guide/structure-modules.md",
    "content": "Modules\n=======\n\nModules are self-contained software units that consist of [models](structure-models.md), [views](structure-views.md),\n[controllers](structure-controllers.md), and other supporting components. End users can access the controllers\nof a module when it is installed in [application](structure-applications.md). For these reasons, modules are\noften viewed as mini-applications. Modules differ from [applications](structure-applications.md) in that\nmodules cannot be deployed alone and must reside within applications.\n\n\n## Creating Modules <span id=\"creating-modules\"></span>\n\nA module is organized as a directory which is called the [[yii\\base\\Module::basePath|base path]] of the module.\nWithin the directory, there are sub-directories, such as `controllers`, `models`, `views`, which hold controllers,\nmodels, views, and other code, just like in an application. The following example shows the content within a module:\n\n```\nforum/\n    Module.php                   the module class file\n    controllers/                 containing controller class files\n        DefaultController.php    the default controller class file\n    models/                      containing model class files\n    views/                       containing controller view and layout files\n        layouts/                 containing layout view files\n        default/                 containing view files for DefaultController\n            index.php            the index view file\n```\n\n\n### Module Classes <span id=\"module-classes\"></span>\n\nEach module should have a unique module class which extends from [[yii\\base\\Module]]. The class should be located\ndirectly under the module's [[yii\\base\\Module::basePath|base path]] and should be [autoloadable](concept-autoloading.md).\nWhen a module is being accessed, a single instance of the corresponding module class will be created.\nLike [application instances](structure-applications.md), module instances are used to share data and components\nfor code within modules.\n\nThe following is an example how a module class may look like:\n\n```php\nnamespace app\\modules\\forum;\n\nclass Module extends \\yii\\base\\Module\n{\n    public function init()\n    {\n        parent::init();\n\n        $this->params['foo'] = 'bar';\n        // ...  other initialization code ...\n    }\n}\n```\n\nIf the `init()` method contains a lot of code initializing the module's properties, you may also save them in terms\nof a [configuration](concept-configurations.md) and load it with the following code in `init()`:\n\n```php\npublic function init()\n{\n    parent::init();\n    // initialize the module with the configuration loaded from config.php\n    \\Yii::configure($this, require __DIR__ . '/config.php');\n}\n```\n\nwhere the configuration file `config.php` may contain the following content, similar to that in an\n[application configuration](structure-applications.md#application-configurations).\n\n```php\n<?php\nreturn [\n    'components' => [\n        // list of component configurations\n    ],\n    'params' => [\n        // list of parameters\n    ],\n];\n```\n\n\n### Controllers in Modules <span id=\"controllers-in-modules\"></span>\n\nWhen creating controllers in a module, a convention is to put the controller classes under the `controllers`\nsub-namespace of the namespace of the module class. This also means the controller class files should be\nput in the `controllers` directory within the module's [[yii\\base\\Module::basePath|base path]].\nFor example, to create a `post` controller in the `forum` module shown in the last subsection, you should\ndeclare the controller class like the following:\n\n```php\nnamespace app\\modules\\forum\\controllers;\n\nuse yii\\web\\Controller;\n\nclass PostController extends Controller\n{\n    // ...\n}\n```\n\nYou may customize the namespace of controller classes by configuring the [[yii\\base\\Module::controllerNamespace]]\nproperty. In case some of the controllers are outside of this namespace, you may make them accessible\nby configuring the [[yii\\base\\Module::controllerMap]] property, similar to [what you do in an application](structure-applications.md#controller-map).\n\n\n### Views in Modules <span id=\"views-in-modules\"></span>\n\nViews in a module should be put in the `views` directory within the module's [[yii\\base\\Module::basePath|base path]].\nFor views rendered by a controller in the module, they should be put under the directory `views/ControllerID`,\nwhere `ControllerID` refers to the [controller ID](structure-controllers.md#routes). For example, if\nthe controller class is `PostController`, the directory would be `views/post` within the module's\n[[yii\\base\\Module::basePath|base path]].\n\nA module can specify a [layout](structure-views.md#layouts) that is applied to the views rendered by the module's\ncontrollers. The layout should be put in the `views/layouts` directory by default, and you should configure\nthe [[yii\\base\\Module::layout]] property to point to the layout name. If you do not configure the `layout` property,\nthe application's layout will be used instead.\n\n\n### Console commands in Modules <span id=\"console-commands-in-modules\"></span>\n\nYour module may also declare commands, that will be available through the [Console](tutorial-console.md) mode.\n\nIn order for the command line utility to see your commands, you will need to change the [[yii\\base\\Module::controllerNamespace]]\nproperty, when Yii is executed in the console mode, and point it to your commands namespace.\n\nOne way to achieve that is to test the instance type of the Yii application in the module's `init()` method:\n\n```php\npublic function init()\n{\n    parent::init();\n    if (Yii::$app instanceof \\yii\\console\\Application) {\n        $this->controllerNamespace = 'app\\modules\\forum\\commands';\n    }\n}\n```\n\nYour commands will then be available from the command line using the following route:\n\n```\nyii <module_id>/<command>/<sub_command>\n```\n\n## Using Modules <span id=\"using-modules\"></span>\n\nTo use a module in an application, simply configure the application by listing the module in\nthe [[yii\\base\\Application::modules|modules]] property of the application. The following code in the\n[application configuration](structure-applications.md#application-configurations) uses the `forum` module:\n\n```php\n[\n    'modules' => [\n        'forum' => [\n            'class' => 'app\\modules\\forum\\Module',\n            // ... other configurations for the module ...\n        ],\n    ],\n]\n```\n\n> Info: To connect console commands of your module,\n> you also need to include it in the [console application configuration](tutorial-console.md#configuration)\n\nThe [[yii\\base\\Application::modules|modules]] property takes an array of module configurations. Each array key\nrepresents a *module ID* which uniquely identifies the module among all modules in the application, and the corresponding\narray value is a [configuration](concept-configurations.md) for creating the module.\n\n\n### Routes <span id=\"routes\"></span>\n\nLike accessing controllers in an application, [routes](structure-controllers.md#routes) are used to address\ncontrollers in a module. A route for a controller within a module must begin with the module ID followed by\nthe [controller ID](structure-controllers.md#controller-ids) and [action ID](structure-controllers.md#action-ids).\nFor example, if an application uses a module named `forum`, then the route\n`forum/post/index` would represent the `index` action of the `post` controller in the module. If the route\nonly contains the module ID, then the [[yii\\base\\Module::defaultRoute]] property, which defaults to `default`,\nwill determine which controller/action should be used. This means a route `forum` would represent the `default`\ncontroller in the `forum` module.\n\nThe URL manager rules for the modules should be added before [[yii\\web\\UrlManager::parseRequest()]] is fired. That means doing it \nin module's `init()` won't work because module will be initialized when routes were already processed. Thus, the rules\nshould be added at [bootstrap stage](structure-extensions.md#bootstrapping-classes). It is a also a good practice\nto wrap module's URL rules with [[\\yii\\web\\GroupUrlRule]].  \n\nIn case a module is used to [version API](rest-versioning.md), its URL rules should be added directly in `urlManager` \nsection of the application config.\n\n\n### Accessing Modules <span id=\"accessing-modules\"></span>\n\nWithin a module, you may often need to get the instance of the [module class](#module-classes) so that you can\naccess the module ID, module parameters, module components, etc. You can do so by using the following statement:\n\n```php\n$module = MyModuleClass::getInstance();\n```\n\nwhere `MyModuleClass` refers to the name of the module class that you are interested in. The `getInstance()` method\nwill return the currently requested instance of the module class. If the module is not requested, the method will\nreturn `null`. Note that you do not want to manually create a new instance of the module class because it will be\ndifferent from the one created by Yii in response to a request.\n\n> Info: When developing a module, you should not assume the module will use a fixed ID. This is because a module\n  can be associated with an arbitrary ID when used in an application or within another module. In order to get\n  the module ID, you should use the above approach to get the module instance first, and then get the ID via\n  `$module->id`.\n\nYou may also access the instance of a module using the following approaches:\n\n```php\n// get the child module whose ID is \"forum\"\n$module = \\Yii::$app->getModule('forum');\n\n// get the module to which the currently requested controller belongs\n$module = \\Yii::$app->controller->module;\n```\n\nThe first approach is only useful when you know the module ID, while the second approach is best used when you\nknow about the controllers being requested.\n\nOnce you have the module instance, you can access parameters and components registered with the module. For example,\n\n```php\n$maxPostCount = $module->params['maxPostCount'];\n```\n\n\n### Bootstrapping Modules <span id=\"bootstrapping-modules\"></span>\n\nSome modules may need to be run for every request. The [[yii\\debug\\Module|debug]] module is such an example.\nTo do so, list the IDs of such modules in the [[yii\\base\\Application::bootstrap|bootstrap]] property of the application.\n\nFor example, the following application configuration makes sure the `debug` module is always loaded:\n\n```php\n[\n    'bootstrap' => [\n        'debug',\n    ],\n\n    'modules' => [\n        'debug' => 'yii\\debug\\Module',\n    ],\n]\n```\n\n\n## Nested Modules <span id=\"nested-modules\"></span>\n\nModules can be nested in unlimited levels. That is, a module can contain another module which can contain yet\nanother module. We call the former *parent module* while the latter *child module*. Child modules must be declared\nin the [[yii\\base\\Module::modules|modules]] property of their parent modules. For example,\n\n```php\nnamespace app\\modules\\forum;\n\nclass Module extends \\yii\\base\\Module\n{\n    public function init()\n    {\n        parent::init();\n\n        $this->modules = [\n            'admin' => [\n                // you should consider using a shorter namespace here!\n                'class' => 'app\\modules\\forum\\modules\\admin\\Module',\n            ],\n        ];\n    }\n}\n```\n\nFor a controller within a nested module, its route should include the IDs of all its ancestor modules.\nFor example, the route `forum/admin/dashboard/index` represents the `index` action of the `dashboard` controller\nin the `admin` module which is a child module of the `forum` module.\n\n> Info: The [[yii\\base\\Module::getModule()|getModule()]] method only returns the child module directly belonging\nto its parent. The [[yii\\base\\Application::loadedModules]] property keeps a list of loaded modules, including both\ndirect children and nested ones, indexed by their class names.\n\n## Accessing components from within modules\n\nSince version 2.0.13 modules support [tree traversal](concept-service-locator.md#tree-traversal). This allows module \ndevelopers to reference (application) components via the service locator that is their module.\nThis means that it is preferable to use `$module->get('db')` over `Yii::$app->get('db')`.\nThe user of a module is able to specify a specific component to be used for the module in case a different component\n(configuration) is required.\n\nFor example consider partial this application configuration:\n\n```php\n'components' => [\n    'db' => [\n        'tablePrefix' => 'main_',\n        'class' => Connection::class,\n        'enableQueryCache' => false\n    ],\n],\n'modules' => [\n    'mymodule' => [\n        'components' => [\n            'db' => [\n                'tablePrefix' => 'module_',\n                'class' => Connection::class\n            ],\n        ],\n    ],\n],\n```\n\nThe application database tables will be prefixed with `main_`, while all module tables will be prefixed with `module_`.\nNote that configuration above is not merged; the modules' component for example will have the query cache enabled since that is the default value.\n\n## Best Practices <span id=\"best-practices\"></span>\n\nModules are best used in large applications whose features can be divided into several groups, each consisting of\na set of closely related features. Each such feature group can be developed as a module which is developed and\nmaintained by a specific developer or team.\n\nModules are also a good way of reusing code at the feature group level. Some commonly used features, such as\nuser management, comment management, can all be developed in terms of modules so that they can be reused easily\nin future projects.\n"
  },
  {
    "path": "docs/guide/structure-overview.md",
    "content": "Overview\n========\n\nYii applications are organized according to the [model-view-controller (MVC)](https://wikipedia.org/wiki/Model-view-controller)\narchitectural pattern. [Models](structure-models.md) represent data, business logic and rules; [views](structure-views.md)\nare output representation of models; and [controllers](structure-controllers.md) take input and convert\nit to commands for [models](structure-models.md) and [views](structure-views.md).\n\nBesides MVC, Yii applications also have the following entities:\n\n* [entry scripts](structure-entry-scripts.md): they are PHP scripts that are directly accessible by end users.\n  They are responsible for starting a request handling cycle.\n* [applications](structure-applications.md): they are globally accessible objects that manage application components\n  and coordinate them to fulfill requests.\n* [application components](structure-application-components.md): they are objects registered with applications and\n  provide various services for fulfilling requests.\n* [modules](structure-modules.md): they are self-contained packages that contain complete MVC by themselves.\n  An application can be organized in terms of multiple modules.\n* [filters](structure-filters.md): they represent code that need to be invoked before and after the actual\n  handling of each request by controllers.\n* [widgets](structure-widgets.md): they are objects that can be embedded in [views](structure-views.md). They\n  may contain controller logic and can be reused in different views.\n\nThe following diagram shows the static structure of an application:\n\n![Static Structure of Application](images/application-structure.png)\n"
  },
  {
    "path": "docs/guide/structure-views.md",
    "content": "Views\n=====\n\nViews are part of the [MVC](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) architecture.\nThey are code responsible for presenting data to end users. In a Web application, views are usually created\nin terms of *view templates* which are PHP script files containing mainly HTML code and presentational PHP code.\nThey are managed by the [[yii\\web\\View|view]] [application component](structure-application-components.md) which provides commonly used methods\nto facilitate view composition and rendering. For simplicity, we often call view templates or view template files\nas views.\n\n\n## Creating Views <span id=\"creating-views\"></span>\n\nAs aforementioned, a view is simply a PHP script mixed with HTML and PHP code. The following is the view\nthat presents a login form. As you can see, PHP code is used to generate the dynamic content, such as the\npage title and the form, while HTML code organizes them into a presentable HTML page.\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n/**\n * @var \\yii\\web\\View $this\n * @var \\yii\\widgets\\ActiveForm $form\n * @var \\app\\models\\LoginForm $model\n */\n\n$this->title = 'Login';\n?>\n<h1><?= Html::encode($this->title) ?></h1>\n\n<p>Please fill out the following fields to login:</p>\n\n<?php $form = ActiveForm::begin(); ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n    <?= Html::submitButton('Login') ?>\n<?php ActiveForm::end(); ?>\n```\n\nWithin a view, you can access `$this` which refers to the [[yii\\web\\View|view component]] managing\nand rendering this view template.\n\nBesides `$this`, there may be other predefined variables in a view, such as `$model` in the above\nexample. These variables represent the data that are *pushed* into the view by [controllers](structure-controllers.md)\nor other objects which trigger the [view rendering](#rendering-views).\n\n> Tip: The predefined variables are listed in a comment block at beginning of a view so that they can\n  be recognized by IDEs. It is also a good way of documenting your views.\n\n\n### Security <span id=\"security\"></span>\n\nWhen creating views that generate HTML pages, it is important that you encode and/or filter the data coming\nfrom end users before presenting them. Otherwise, your application may be subject to\n[cross-site scripting](https://en.wikipedia.org/wiki/Cross-site_scripting) attacks.\n\nTo display a plain text, encode it first by calling [[yii\\helpers\\Html::encode()]]. For example, the following code\nencodes the user name before displaying it:\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n\n<div class=\"username\">\n    <?= Html::encode($user->name) ?>\n</div>\n```\n\nTo display HTML content, use [[yii\\helpers\\HtmlPurifier]] to filter the content first. For example, the following\ncode filters the post content before displaying it:\n\n```php\n<?php\nuse yii\\helpers\\HtmlPurifier;\n?>\n\n<div class=\"post\">\n    <?= HtmlPurifier::process($post->text) ?>\n</div>\n```\n\n> Tip: While HTMLPurifier does excellent job in making output safe, it is not fast. You should consider\n  [caching](caching-overview.md) the filtering result if your application requires high performance.\n\n\n### Organizing Views <span id=\"organizing-views\"></span>\n\nLike [controllers](structure-controllers.md) and [models](structure-models.md), there are conventions to organize views.\n\n* For views rendered by a controller, they should be put under the directory `@app/views/ControllerID` by default,\n  where `ControllerID` refers to the [controller ID](structure-controllers.md#routes). For example, if\n  the controller class is `PostController`, the directory would be `@app/views/post`; if it is `PostCommentController`,\n  the directory would be `@app/views/post-comment`. In case the controller belongs to a module, the directory\n  would be `views/ControllerID` under the [[yii\\base\\Module::basePath|module directory]].\n* For views rendered in a [widget](structure-widgets.md), they should be put under the `WidgetPath/views` directory by\n  default, where `WidgetPath` stands for the directory containing the widget class file.\n* For views rendered by other objects, it is recommended that you follow the similar convention as that for widgets.\n\nYou may customize these default view directories by overriding the [[yii\\base\\ViewContextInterface::getViewPath()]]\nmethod of controllers or widgets.\n\n\n## Rendering Views <span id=\"rendering-views\"></span>\n\nYou can render views in [controllers](structure-controllers.md), [widgets](structure-widgets.md), or any\nother places by calling view rendering methods. These methods share a similar signature shown as follows,\n\n```\n/**\n * @param string $view view name or file path, depending on the actual rendering method\n * @param array $params the data to be passed to the view\n * @return string rendering result\n */\nmethodName($view, $params = [])\n```\n\n\n### Rendering in Controllers <span id=\"rendering-in-controllers\"></span>\n\nWithin [controllers](structure-controllers.md), you may call the following controller methods to render views:\n\n* [[yii\\base\\Controller::render()|render()]]: renders a [named view](#named-views) and applies a [layout](#layouts)\n  to the rendering result.\n* [[yii\\base\\Controller::renderPartial()|renderPartial()]]: renders a [named view](#named-views) without any layout.\n* [[yii\\web\\Controller::renderAjax()|renderAjax()]]: renders a [named view](#named-views) without any layout,\n  and injects all registered JS/CSS scripts and files. It is usually used in response to AJAX Web requests.\n* [[yii\\base\\Controller::renderFile()|renderFile()]]: renders a view specified in terms of a view file path or\n  [alias](concept-aliases.md).\n* [[yii\\base\\Controller::renderContent()|renderContent()]]: renders a static string by embedding it into\n  the currently applicable [layout](#layouts). This method is available since version 2.0.1.\n\nFor example,\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse app\\models\\Post;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\n\nclass PostController extends Controller\n{\n    public function actionView($id)\n    {\n        $model = Post::findOne($id);\n        if ($model === null) {\n            throw new NotFoundHttpException;\n        }\n\n        // renders a view named \"view\" and applies a layout to it\n        return $this->render('view', [\n            'model' => $model,\n        ]);\n    }\n}\n```\n\n\n### Rendering in Widgets <span id=\"rendering-in-widgets\"></span>\n\nWithin [widgets](structure-widgets.md), you may call the following widget methods to render views.\n\n* [[yii\\base\\Widget::render()|render()]]: renders a [named view](#named-views).\n* [[yii\\base\\Widget::renderFile()|renderFile()]]: renders a view specified in terms of a view file path or\n  [alias](concept-aliases.md).\n\nFor example,\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Widget;\nuse yii\\helpers\\Html;\n\nclass ListWidget extends Widget\n{\n    public $items = [];\n\n    public function run()\n    {\n        // renders a view named \"list\"\n        return $this->render('list', [\n            'items' => $this->items,\n        ]);\n    }\n}\n```\n\n\n### Rendering in Views <span id=\"rendering-in-views\"></span>\n\nYou can render a view within another view by calling one of the following methods provided by the [[yii\\base\\View|view component]]:\n\n* [[yii\\base\\View::render()|render()]]: renders a [named view](#named-views).\n* [[yii\\web\\View::renderAjax()|renderAjax()]]: renders a [named view](#named-views) and injects all registered\n  JS/CSS scripts and files. It is usually used in response to AJAX Web requests.\n* [[yii\\base\\View::renderFile()|renderFile()]]: renders a view specified in terms of a view file path or\n  [alias](concept-aliases.md).\n\nFor example, the following code in a view renders the `_overview.php` view file which is in the same directory\nas the view being currently rendered. Remember that `$this` in a view refers to the [[yii\\base\\View|view]] component:\n\n```php\n<?= $this->render('_overview') ?>\n```\n\n\n### Rendering in Other Places <span id=\"rendering-in-other-places\"></span>\n\nIn any place, you can get access to the [[yii\\base\\View|view]] application component by the expression\n`Yii::$app->view` and then call its aforementioned methods to render a view. For example,\n\n```php\n// displays the view file \"@app/views/site/license.php\"\necho \\Yii::$app->view->renderFile('@app/views/site/license.php');\n```\n\n\n### Named Views <span id=\"named-views\"></span>\n\nWhen you render a view, you can specify the view using either a view name or a view file path/alias. In most cases,\nyou would use the former because it is more concise and flexible. We call views specified using names as *named views*.\n\nA view name is resolved into the corresponding view file path according to the following rules:\n\n* A view name may omit the file extension name. In this case, `.php` will be used as the extension. For example,\n  the view name `about` corresponds to the file name `about.php`.\n* If the view name starts with double slashes `//`, the corresponding view file path would be `@app/views/ViewName`.\n  That is, the view is looked for under the [[yii\\base\\Application::viewPath|application's view path]].\n  For example, `//site/about` will be resolved into `@app/views/site/about.php`.\n* If the view name starts with a single slash `/`, the view file path is formed by prefixing the view name\n  with the [[yii\\base\\Module::viewPath|view path]] of the currently active [module](structure-modules.md).\n  If there is no active module, `@app/views/ViewName` will be used. For example, `/user/create` will be resolved into\n  `@app/modules/user/views/user/create.php`, if the currently active module is `user`. If there is no active module,\n  the view file path would be `@app/views/user/create.php`.\n* If the view is rendered with a [[yii\\base\\View::context|context]] and the context implements [[yii\\base\\ViewContextInterface]],\n  the view file path is formed by prefixing the [[yii\\base\\ViewContextInterface::getViewPath()|view path]] of the\n  context to the view name. This mainly applies to the views rendered within controllers and widgets. For example,\n  `about` will be resolved into `@app/views/site/about.php` if the context is the controller `SiteController`.\n* If a view is rendered within another view, the directory containing the other view file will be prefixed to\n  the new view name to form the actual view file path. For example, `item` will be resolved into `@app/views/post/item.php`\n  if it is being rendered in the view `@app/views/post/index.php`.\n\nAccording to the above rules, calling `$this->render('view')` in a controller `app\\controllers\\PostController` will\nactually render the view file `@app/views/post/view.php`, while calling `$this->render('_overview')` in that view\nwill render the view file `@app/views/post/_overview.php`.\n\n\n### Accessing Data in Views <span id=\"accessing-data-in-views\"></span>\n\nThere are two approaches to access data within a view: push and pull.\n\nBy passing the data as the second parameter to the view rendering methods, you are using the push approach.\nThe data should be represented as an array of name-value pairs. When the view is being rendered, the PHP\n`extract()` function will be called on this array so that the array is extracted into variables in the view.\nFor example, the following view rendering code in a controller will push two variables to the `report` view:\n`$foo = 1` and `$bar = 2`.\n\n```php\necho $this->render('report', [\n    'foo' => 1,\n    'bar' => 2,\n]);\n```\n\nThe pull approach actively retrieves data from the [[yii\\base\\View|view component]] or other objects accessible\nin views (e.g. `Yii::$app`). Using the code below as an example, within the view you can get the controller object\nby the expression `$this->context`. And as a result, it is possible for you to access any properties or methods\nof the controller in the `report` view, such as the controller ID shown in the following:\n\n```php\nThe controller ID is: <?= $this->context->id ?>\n```\n\nThe push approach is usually the preferred way of accessing data in views, because it makes views less dependent\non context objects. Its drawback is that you need to manually build the data array all the time, which could\nbecome tedious and error prone if a view is shared and rendered in different places.\n\n\n### Sharing Data among Views <span id=\"sharing-data-among-views\"></span>\n\nThe [[yii\\base\\View|view component]] provides the [[yii\\base\\View::params|params]] property that you can use\nto share data among views.\n\nFor example, in an `about` view, you can have the following code which specifies the current segment of the\nbreadcrumbs.\n\n```php\n$this->params['breadcrumbs'][] = 'About Us';\n```\n\nThen, in the [layout](#layouts) file, which is also a view, you can display the breadcrumbs using the data\npassed along [[yii\\base\\View::params|params]]:\n\n```php\n<?= yii\\widgets\\Breadcrumbs::widget([\n    'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],\n]) ?>\n```\n\n\n## Layouts <span id=\"layouts\"></span>\n\nLayouts are a special type of views that represent the common parts of multiple views. For example, the pages\nfor most Web applications share the same page header and footer. While you can repeat the same page header and footer\nin every view, a better way is to do this once in a layout and embed the rendering result of a content view at\nan appropriate place in the layout.\n\n\n### Creating Layouts <span id=\"creating-layouts\"></span>\n\nBecause layouts are also views, they can be created in the similar way as normal views. By default, layouts\nare stored in the directory `@app/views/layouts`. For layouts used within a [module](structure-modules.md),\nthey should be stored in the `views/layouts` directory under the [[yii\\base\\Module::basePath|module directory]].\nYou may customize the default layout directory by configuring the [[yii\\base\\Module::layoutPath]] property of\nthe application or modules.\n\nThe following example shows how a layout looks like. Note that for illustrative purpose, we have greatly simplified\nthe code in the layout. In practice, you may want to add more content to it, such as head tags, main menu, etc.\n\n```php\n<?php\nuse yii\\helpers\\Html;\n\n/**\n * @var \\yii\\web\\View $this\n * @var string $content\n */\n?>\n<?php $this->beginPage() ?>\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\"/>\n    <?= Html::csrfMetaTags() ?>\n    <title><?= Html::encode($this->title) ?></title>\n    <?php $this->head() ?>\n</head>\n<body>\n<?php $this->beginBody() ?>\n    <header>My Company</header>\n    <?= $content ?>\n    <footer>&copy; 2014 by My Company</footer>\n<?php $this->endBody() ?>\n</body>\n</html>\n<?php $this->endPage() ?>\n```\n\nAs you can see, the layout generates the HTML tags that are common to all pages. Within the `<body>` section,\nthe layout echoes the `$content` variable which represents the rendering result of content views and is pushed\ninto the layout when [[yii\\base\\Controller::render()]] is called.\n\nMost layouts should call the following methods like shown in the above code. These methods mainly trigger events\nabout the rendering process so that scripts and tags registered in other places can be properly injected into\nthe places where these methods are called.\n\n- [[yii\\base\\View::beginPage()|beginPage()]]: This method should be called at the very beginning of the layout.\n  It triggers the [[yii\\base\\View::EVENT_BEGIN_PAGE|EVENT_BEGIN_PAGE]] event which indicates the beginning of a page.\n- [[yii\\base\\View::endPage()|endPage()]]: This method should be called at the end of the layout.\n  It triggers the [[yii\\base\\View::EVENT_END_PAGE|EVENT_END_PAGE]] event which indicates the end of a page.\n- [[yii\\web\\View::head()|head()]]: This method should be called within the `<head>` section of an HTML page.\n  It generates a placeholder which will be replaced with the registered head HTML code (e.g. link tags, meta tags)\n  when a page finishes rendering.\n- [[yii\\web\\View::beginBody()|beginBody()]]: This method should be called at the beginning of the `<body>` section.\n  It triggers the [[yii\\web\\View::EVENT_BEGIN_BODY|EVENT_BEGIN_BODY]] event and generates a placeholder which will\n  be replaced by the registered HTML code (e.g. JavaScript) targeted at the body begin position.\n- [[yii\\web\\View::endBody()|endBody()]]: This method should be called at the end of the `<body>` section.\n  It triggers the [[yii\\web\\View::EVENT_END_BODY|EVENT_END_BODY]] event and generates a placeholder which will\n  be replaced by the registered HTML code (e.g. JavaScript) targeted at the body end position.\n\n\n### Accessing Data in Layouts <span id=\"accessing-data-in-layouts\"></span>\n\nWithin a layout, you have access to two predefined variables: `$this` and `$content`. The former refers to\nthe [[yii\\base\\View|view]] component, like in normal views, while the latter contains the rendering result of a content\nview which is rendered by calling the [[yii\\base\\Controller::render()|render()]] method in controllers.\n\nIf you want to access other data in layouts, you have to use the pull method as described in\nthe [Accessing Data in Views](#accessing-data-in-views) subsection. If you want to pass data from a content view\nto a layout, you may use the method described in the [Sharing Data among Views](#sharing-data-among-views) subsection.\n\n\n### Using Layouts <span id=\"using-layouts\"></span>\n\nAs described in the [Rendering in Controllers](#rendering-in-controllers) subsection, when you render a view\nby calling the [[yii\\base\\Controller::render()|render()]] method in a controller, a layout will be applied\nto the rendering result. By default, the layout `@app/views/layouts/main.php` will be used. \n\nYou may use a different layout by configuring either [[yii\\base\\Application::layout]] or [[yii\\base\\Controller::layout]].\nThe former governs the layout used by all controllers, while the latter overrides the former for individual controllers.\nFor example, the following code makes the `post` controller to use `@app/views/layouts/post.php` as the layout\nwhen rendering its views. Other controllers, assuming their `layout` property is untouched, will still use the default\n`@app/views/layouts/main.php` as the layout.\n \n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass PostController extends Controller\n{\n    public $layout = 'post';\n    \n    // ...\n}\n```\n\nFor controllers belonging to a module, you may also configure the module's [[yii\\base\\Module::layout|layout]] property to\nuse a particular layout for these controllers. \n\nBecause the `layout` property may be configured at different levels (controllers, modules, application),\nbehind the scene Yii takes two steps to determine what is the actual layout file being used for a particular controller.\n\nIn the first step, it determines the layout value and the context module:\n\n- If the [[yii\\base\\Controller::layout]] property of the controller is not `null`, use it as the layout value and\n  the [[yii\\base\\Controller::module|module]] of the controller as the context module.\n- If the [[yii\\base\\Controller::layout]] property of the controller is `null`, search through all ancestor modules (including the application itself) of the controller and \n  find the first module whose [[yii\\base\\Module::layout|layout]] property is not `null`. Use that module and\n  its [[yii\\base\\Module::layout|layout]] value as the context module and the chosen layout value.\n  If such a module cannot be found, it means no layout will be applied.\n  \nIn the second step, it determines the actual layout file according to the layout value and the context module\ndetermined in the first step. The layout value can be:\n\n- a path alias (e.g. `@app/views/layouts/main`).\n- an absolute path (e.g. `/main`): the layout value starts with a slash. The actual layout file will be\n  looked for under the application's [[yii\\base\\Application::layoutPath|layout path]] which defaults to\n  `@app/views/layouts`.\n- a relative path (e.g. `main`): the actual layout file will be looked for under the context module's\n  [[yii\\base\\Module::layoutPath|layout path]] which defaults to the `views/layouts` directory under the\n  [[yii\\base\\Module::basePath|module directory]].\n- the boolean value `false`: no layout will be applied.\n\nIf the layout value does not contain a file extension, it will use the default one `.php`.\n\n\n### Nested Layouts <span id=\"nested-layouts\"></span>\n\nSometimes you may want to nest one layout in another. For example, in different sections of a Web site, you\nwant to use different layouts, while all these layouts share the same basic layout that generates the overall\nHTML5 page structure. You can achieve this goal by calling [[yii\\base\\View::beginContent()|beginContent()]] and\n[[yii\\base\\View::endContent()|endContent()]] in the child layouts like the following:\n\n```php\n<?php $this->beginContent('@app/views/layouts/base.php'); ?>\n\n...child layout content here...\n\n<?php $this->endContent(); ?>\n```\n\nAs shown above, the child layout content should be enclosed within [[yii\\base\\View::beginContent()|beginContent()]] and\n[[yii\\base\\View::endContent()|endContent()]]. The parameter passed to [[yii\\base\\View::beginContent()|beginContent()]]\nspecifies what is the parent layout. It can be either a layout file or alias.\n\nUsing the above approach, you can nest layouts in more than one levels.\n\n\n### Using Blocks <span id=\"using-blocks\"></span>\n\nBlocks allow you to specify the view content in one place while displaying it in another. They are often used together\nwith layouts. For example, you can define a block in a content view and display it in the layout.\n\nYou call [[yii\\base\\View::beginBlock()|beginBlock()]] and [[yii\\base\\View::endBlock()|endBlock()]] to define a block.\nThe block can then be accessed via `$view->blocks[$blockID]`, where `$blockID` stands for a unique ID that you assign\nto the block when defining it.\n\nThe following example shows how you can use blocks to customize specific parts of a layout in a content view.\n\nFirst, in a content view, define one or multiple blocks:\n\n```php\n...\n\n<?php $this->beginBlock('block1'); ?>\n\n...content of block1...\n\n<?php $this->endBlock(); ?>\n\n...\n\n<?php $this->beginBlock('block3'); ?>\n\n...content of block3...\n\n<?php $this->endBlock(); ?>\n```\n\nThen, in the layout view, render the blocks if they are available, or display some default content if a block is\nnot defined.\n\n```php\n...\n<?php if (isset($this->blocks['block1'])): ?>\n    <?= $this->blocks['block1'] ?>\n<?php else: ?>\n    ... default content for block1 ...\n<?php endif; ?>\n\n...\n\n<?php if (isset($this->blocks['block2'])): ?>\n    <?= $this->blocks['block2'] ?>\n<?php else: ?>\n    ... default content for block2 ...\n<?php endif; ?>\n\n...\n\n<?php if (isset($this->blocks['block3'])): ?>\n    <?= $this->blocks['block3'] ?>\n<?php else: ?>\n    ... default content for block3 ...\n<?php endif; ?>\n...\n```\n\n\n## Using View Components <span id=\"using-view-components\"></span>\n\n[[yii\\base\\View|View components]] provides many view-related features. While you can get view components\nby creating individual instances of [[yii\\base\\View]] or its child class, in most cases you will mainly use\nthe `view` application component. You can configure this component in [application configurations](structure-applications.md#application-configurations)\nlike the following:\n\n```php\n[\n    // ...\n    'components' => [\n        'view' => [\n            'class' => 'app\\components\\View',\n        ],\n        // ...\n    ],\n]\n```\n\nView components provide the following useful view-related features, each described in more details in a separate section:\n\n* [theming](output-theming.md): allows you to develop and change the theme for your Web site.\n* [fragment caching](caching-fragment.md): allows you to cache a fragment within a Web page.\n* [client script handling](output-client-scripts.md): supports CSS and JavaScript registration and rendering.\n* [asset bundle handling](structure-assets.md): supports registering and rendering of [asset bundles](structure-assets.md).\n* [alternative template engines](tutorial-template-engines.md): allows you to use other template engines, such as\n  [Twig](https://twig.symfony.com/), [Smarty](https://www.smarty.net/).\n\nYou may also frequently use the following minor yet useful features when you are developing Web pages.\n\n\n### Setting Page Titles <span id=\"setting-page-titles\"></span>\n\nEvery Web page should have a title. Normally the title tag is being displayed in a [layout](#layouts). However, in practice\nthe title is often determined in content views rather than layouts. To solve this problem, [[yii\\web\\View]] provides\nthe [[yii\\web\\View::title|title]] property for you to pass the title information from content views to layouts.\n\nTo make use of this feature, in each content view, you can set the page title like the following:\n\n```php\n<?php\n$this->title = 'My page title';\n?>\n```\n\nThen in the layout, make sure you have the following code in the `<head>` section:\n\n```php\n<title><?= Html::encode($this->title) ?></title>\n```\n\n\n### Registering Meta Tags <span id=\"registering-meta-tags\"></span>\n\nWeb pages usually need to generate various meta tags needed by different parties. Like page titles, meta tags\nappear in the `<head>` section and are usually generated in layouts.\n\nIf you want to specify what meta tags to generate in content views, you can call [[yii\\web\\View::registerMetaTag()]]\nin a content view, like the following:\n\n```php\n<?php\n$this->registerMetaTag(['name' => 'keywords', 'content' => 'yii, framework, php']);\n?>\n```\n\nThe above code will register a \"keywords\" meta tag with the view component. The registered meta tag is\nrendered after the layout finishes rendering. The following HTML code will be generated and inserted\nat the place where you call [[yii\\web\\View::head()]] in the layout:\n\n```php\n<meta name=\"keywords\" content=\"yii, framework, php\">\n```\n\nNote that if you call [[yii\\web\\View::registerMetaTag()]] multiple times, it will register multiple meta tags,\nregardless whether the meta tags are the same or not.\n\nTo make sure there is only a single instance of a meta tag type, you can specify a key as a second parameter when calling the method.\nFor example, the following code registers two \"description\" meta tags. However, only the second one will be rendered.\n\n```php\n$this->registerMetaTag(['name' => 'description', 'content' => 'This is my cool website made with Yii!'], 'description');\n$this->registerMetaTag(['name' => 'description', 'content' => 'This website is about funny raccoons.'], 'description');\n```\n\n\n### Registering Link Tags <span id=\"registering-link-tags\"></span>\n\nLike [meta tags](#registering-meta-tags), link tags are useful in many cases, such as customizing favicon, pointing to\nRSS feed or delegating OpenID to another server. You can work with link tags in the similar way as meta tags\nby using [[yii\\web\\View::registerLinkTag()]]. For example, in a content view, you can register a link tag like follows,\n\n```php\n$this->registerLinkTag([\n    'title' => 'Live News for Yii',\n    'rel' => 'alternate',\n    'type' => 'application/rss+xml',\n    'href' => 'https://www.yiiframework.com/rss.xml/',\n]);\n```\n\nThe code above will result in\n\n```html\n<link title=\"Live News for Yii\" rel=\"alternate\" type=\"application/rss+xml\" href=\"https://www.yiiframework.com/rss.xml/\">\n```\n\nSimilar as [[yii\\web\\View::registerMetaTag()|registerMetaTag()]], you can specify a key when calling\n[[yii\\web\\View::registerLinkTag()|registerLinkTag()]] to avoid generating repeated link tags.\n\n\n## View Events <span id=\"view-events\"></span>\n\n[[yii\\base\\View|View components]] trigger several events during the view rendering process. You may respond\nto these events to inject content into views or process the rendering results before they are sent to end users.\n\n- [[yii\\base\\View::EVENT_BEFORE_RENDER|EVENT_BEFORE_RENDER]]: triggered at the beginning of rendering a file\n  in a controller. Handlers of this event may set [[yii\\base\\ViewEvent::isValid]] to be `false` to cancel the rendering process.\n- [[yii\\base\\View::EVENT_AFTER_RENDER|EVENT_AFTER_RENDER]]: triggered after rendering a file by the call of [[yii\\base\\View::afterRender()]].\n  Handlers of this event may obtain the rendering result through [[yii\\base\\ViewEvent::output]] and may modify\n  this property to change the rendering result.\n- [[yii\\base\\View::EVENT_BEGIN_PAGE|EVENT_BEGIN_PAGE]]: triggered by the call of [[yii\\base\\View::beginPage()]] in layouts.\n- [[yii\\base\\View::EVENT_END_PAGE|EVENT_END_PAGE]]: triggered by the call of [[yii\\base\\View::endPage()]] in layouts.\n- [[yii\\web\\View::EVENT_BEGIN_BODY|EVENT_BEGIN_BODY]]: triggered by the call of [[yii\\web\\View::beginBody()]] in layouts.\n- [[yii\\web\\View::EVENT_END_BODY|EVENT_END_BODY]]: triggered by the call of [[yii\\web\\View::endBody()]] in layouts.\n\nFor example, the following code injects the current date at the end of the page body:\n\n```php\n\\Yii::$app->view->on(View::EVENT_END_BODY, function () {\n    echo date('Y-m-d');\n});\n```\n\n\n## Rendering Static Pages <span id=\"rendering-static-pages\"></span>\n\nStatic pages refer to those Web pages whose main content are mostly static without the need of accessing\ndynamic data pushed from controllers.\n\nYou can output static pages by putting their code in the view, and then using the code like the following in a controller:\n\n```php\npublic function actionAbout()\n{\n    return $this->render('about');\n}\n```\n\nIf a Web site contains many static pages, it would be very tedious repeating the similar code many times.\nTo solve this problem, you may introduce a [standalone action](structure-controllers.md#standalone-actions)\ncalled [[yii\\web\\ViewAction]] in a controller. For example,\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actions()\n    {\n        return [\n            'page' => [\n                'class' => 'yii\\web\\ViewAction',\n            ],\n        ];\n    }\n}\n```\n\nNow if you create a view named `about` under the directory `@app/views/site/pages`, you will be able to\ndisplay this view by the following URL:\n\n```\nhttp://localhost/index.php?r=site%2Fpage&view=about\n```\n\nThe `GET` parameter `view` tells [[yii\\web\\ViewAction]] which view is requested. The action will then look\nfor this view under the directory `@app/views/site/pages`. You may configure [[yii\\web\\ViewAction::viewPrefix]]\nto change the directory for searching these views.\n\n\n## Best Practices <span id=\"best-practices\"></span>\n\nViews are responsible for presenting models in the format that end users desire. In general, views\n\n* should mainly contain presentational code, such as HTML, and simple PHP code to traverse, format and render data.\n* should not contain code that performs DB queries. Such code should be done in models.\n* should avoid direct access to request data, such as `$_GET`, `$_POST`. This belongs to controllers.\n  If request data is needed, they should be pushed into views by controllers.\n* may read model properties, but should not modify them.\n\nTo make views more manageable, avoid creating views that are too complex or contain too much redundant code.\nYou may use the following techniques to achieve this goal:\n\n* use [layouts](#layouts) to represent common presentational sections (e.g. page header, footer).\n* divide a complicated view into several smaller ones. The smaller views can be rendered and assembled into a bigger\n  one using the rendering methods that we have described.\n* create and use [widgets](structure-widgets.md) as building blocks of views.\n* create and use helper classes to transform and format data in views.\n\n"
  },
  {
    "path": "docs/guide/structure-widgets.md",
    "content": "Widgets\n=======\n\nWidgets are reusable building blocks used in [views](structure-views.md) to create complex and configurable user\ninterface elements in an object-oriented fashion. For example, a date picker widget may generate a fancy date picker\nthat allows users to pick a date as their input. All you need to do is just to insert the code in a view\nlike the following:\n\n```php\n<?php\nuse yii\\jui\\DatePicker;\n?>\n<?= DatePicker::widget(['name' => 'date']) ?>\n```\n\nThere are a good number of widgets bundled with Yii, such as [[yii\\widgets\\ActiveForm|active form]],\n[[yii\\widgets\\Menu|menu]], [jQuery UI widgets](https://www.yiiframework.com/extension/yiisoft/yii2-jui), [Twitter Bootstrap widgets](https://www.yiiframework.com/extension/yiisoft/yii2-bootstrap).\nIn the following, we will introduce the basic knowledge about widgets. Please refer to the class API documentation\nif you want to learn about the usage of a particular widget.\n\n\n## Using Widgets <span id=\"using-widgets\"></span>\n\nWidgets are primarily used in [views](structure-views.md). You can call the [[yii\\base\\Widget::widget()]] method\nto use a widget in a view. The method takes a [configuration](concept-configurations.md) array for initializing\nthe widget and returns the rendering result of the widget. For example, the following code inserts a date picker\nwidget which is configured to use the Russian language and keep the input in the `from_date` attribute of `$model`.\n\n```php\n<?php\nuse yii\\jui\\DatePicker;\n?>\n<?= DatePicker::widget([\n    'model' => $model,\n    'attribute' => 'from_date',\n    'language' => 'ru',\n    'dateFormat' => 'php:Y-m-d',\n]) ?>\n```\n\nSome widgets can take a block of content which should be enclosed between the invocation of\n[[yii\\base\\Widget::begin()]] and [[yii\\base\\Widget::end()]]. For example, the following code uses the\n[[yii\\widgets\\ActiveForm]] widget to generate a login form. The widget will generate the opening and closing\n`<form>` tags at the place where `begin()` and `end()` are called, respectively. Anything in between will be\nrendered as is.\n\n```php\n<?php\nuse yii\\widgets\\ActiveForm;\nuse yii\\helpers\\Html;\n?>\n\n<?php $form = ActiveForm::begin(['id' => 'login-form']); ?>\n\n    <?= $form->field($model, 'username') ?>\n\n    <?= $form->field($model, 'password')->passwordInput() ?>\n\n    <div class=\"form-group\">\n        <?= Html::submitButton('Login') ?>\n    </div>\n\n<?php ActiveForm::end(); ?>\n```\n\nNote that unlike [[yii\\base\\Widget::widget()]] which returns the rendering result of a widget, the method\n[[yii\\base\\Widget::begin()]] returns an instance of the widget which you can use to build the widget content.\n\n> Note: Some widgets will use [output buffering](https://www.php.net/manual/en/book.outcontrol.php) to adjust the enclosed\n> content when [[yii\\base\\Widget::end()]] is called. For this reason calling [[yii\\base\\Widget::begin()]] and\n> [[yii\\base\\Widget::end()]] is expected to happen in the same view file.\n> Not following this rule may result in unexpected output.\n\n\n### Configuring global defaults\n\nGlobal defaults for a widget type could be configured via DI container:\n\n```php\n\\Yii::$container->set('yii\\widgets\\LinkPager', ['maxButtonCount' => 5]);\n```\n\nSee [\"Practical Usage\" section in Dependency Injection Container guide](concept-di-container.md#practical-usage) for\ndetails.\n\n\n## Creating Widgets <span id=\"creating-widgets\"></span>\n\nWidget can be created in either of two different ways depending on the requirement.\n\n### 1: Utilizing `widget()` method\n\nTo create a widget, extend from [[yii\\base\\Widget]] and override the [[yii\\base\\Widget::init()]] and/or\n[[yii\\base\\Widget::run()]] methods. Usually, the `init()` method should contain the code that initializes the widget\nproperties, while the `run()` method should contain the code that generates the rendering result of the widget.\nThe rendering result may be directly \"echoed\" or returned as a string by `run()`.\n\nIn the following example, `HelloWidget` HTML-encodes and displays the content assigned to its `message` property.\nIf the property is not set, it will display \"Hello World\" by default.\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Widget;\nuse yii\\helpers\\Html;\n\nclass HelloWidget extends Widget\n{\n    public $message;\n\n    public function init()\n    {\n        parent::init();\n        if ($this->message === null) {\n            $this->message = 'Hello World';\n        }\n    }\n\n    public function run()\n    {\n        return Html::encode($this->message);\n    }\n}\n```\n\nTo use this widget, simply insert the following code in a view:\n\n```php\n<?php\nuse app\\components\\HelloWidget;\n?>\n<?= HelloWidget::widget(['message' => 'Good morning']) ?>\n```\n\n\nSometimes, a widget may need to render a big chunk of content. While you can embed the content within the `run()`\nmethod, a better approach is to put it in a [view](structure-views.md) and call [[yii\\base\\Widget::render()]] to\nrender it. For example,\n\n```php\npublic function run()\n{\n    return $this->render('hello');\n}\n```\n\n### 2: Utilizing `begin()` and `end()` methods\n\nThis is similar to above one with minor difference.\nBelow is a variant of `HelloWidget` which takes the content enclosed within the `begin()` and `end()` calls,\nHTML-encodes it and then displays it.\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Widget;\nuse yii\\helpers\\Html;\n\nclass HelloWidget extends Widget\n{\n    public function init()\n    {\n        parent::init();\n        ob_start();\n    }\n\n    public function run()\n    {\n        $content = ob_get_clean();\n        return Html::encode($content);\n    }\n}\n```\n\nAs you can see, PHP's output buffer is started in `init()` so that any output between the calls of `init()` and `run()`\ncan be captured, processed and returned in `run()`.\n\n> Info: When you call [[yii\\base\\Widget::begin()]], a new instance of the widget will be created and the `init()` method\n  will be called at the end of the widget constructor. When you call [[yii\\base\\Widget::end()]], the `run()` method\n  will be called whose return result will be echoed by `end()`.\n\nThe following code shows how to use this new variant of `HelloWidget`:\n\n```php\n<?php\nuse app\\components\\HelloWidget;\n?>\n<?php HelloWidget::begin(); ?>\n\n    sample content that may contain one or more <strong>HTML</strong> <pre>tags</pre>\n\n    If this content grows too big, use sub views\n\n    For e.g.\n\n    <?php echo $this->render('viewfile'); // Note: here render() method is of class \\yii\\base\\View as this part of code is within view file and not in Widget class file ?>\n\n<?php HelloWidget::end(); ?>\n```\n\nBy default, views for a widget should be stored in files in the `WidgetPath/views` directory, where `WidgetPath`\nstands for the directory containing the widget class file. Therefore, the above example will render the view file\n`@app/components/views/hello.php`, assuming the widget class is located under `@app/components`. You may override\nthe [[yii\\base\\Widget::getViewPath()]] method to customize the directory containing the widget view files.\n\n\n## Best Practices <span id=\"best-practices\"></span>\n\nWidgets are an object-oriented way of reusing view code.\n\nWhen creating widgets, you should still follow the MVC pattern. In general, you should keep logic in widget\nclasses and keep presentation in [views](structure-views.md).\n\nWidgets should be designed to be self-contained. That is, when using a widget, you should be able to just drop\nit in a view without doing anything else. This could be tricky if a widget requires external resources, such as\nCSS, JavaScript, images, etc. Fortunately, Yii provides the support for [asset bundles](structure-assets.md),\nwhich can be utilized to solve the problem.\n\nWhen a widget contains view code only, it is very similar to a [view](structure-views.md). In fact, in this case,\ntheir only difference is that a widget is a redistributable class, while a view is just a plain PHP script\nthat you would prefer to keep within your application.\n\n"
  },
  {
    "path": "docs/guide/test-acceptance.md",
    "content": "Acceptance Tests\n================\n\nAcceptance test verifies scenarios from a user's perspective. The application tested is accessed via either PhpBrowser or\na real browser. In both cases the browsers are communicating via HTTP so application should be served via web server.\n\nAcceptance testing is implemented with the help of Codeception framework which has a nice documentation about it:\n\n- [Codeception for Yii framework](https://codeception.com/for/yii)\n- [Codeception Acceptance Tests](https://codeception.com/docs/03-AcceptanceTests)\n\n## Running basic and advanced template tests\n\nIf you've started with advanced template, please refer to [\"testing\" guide](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/start-testing.md)\nfor more details about running tests.  \n\nIf you've started with basic template, check its [README \"testing\" section](https://github.com/yiisoft/yii2-app-basic/blob/master/README.md#testing).\n"
  },
  {
    "path": "docs/guide/test-environment-setup.md",
    "content": "Testing environment setup\n======================\n\nYii 2 has officially maintained integration with [`Codeception`](https://github.com/Codeception/Codeception) testing\nframework that allows you to create the following test types:\n\n- [Unit](test-unit.md) - verifies that a single unit of code is working as expected;\n- [Functional](test-functional.md) - verifies scenarios from a user's perspective via browser emulation;\n- [Acceptance](test-acceptance.md) - verifies scenarios from a user's perspective in a browser.\n\nYii provides ready to use test sets for all three test types in both\n[`yii2-basic`](https://github.com/yiisoft/yii2-app-basic) and\n[`yii2-advanced`](https://github.com/yiisoft/yii2-app-advanced) project templates.\n\nCodeception comes preinstalled with both basic and advanced project templates.\nIn case you are not using one of these templates, Codeception could be installed\nby issuing the following console commands:\n\n```\ncomposer require --dev codeception/codeception\ncomposer require --dev codeception/specify\ncomposer require --dev codeception/verify\n```\n"
  },
  {
    "path": "docs/guide/test-fixtures.md",
    "content": "Fixtures\n========\n\nFixtures are an important part of testing. Their main purpose is to set up the environment in a fixed/known state\nso that your tests are repeatable and run in an expected way. Yii provides a fixture framework that allows\nyou to define your fixtures precisely and use them easily both when running your tests with Codeception\nand independently.\n\nA key concept in the Yii fixture framework is the so-called *fixture object*. A fixture object represents\na particular aspect of a test environment and is an instance of [[yii\\test\\Fixture]] or its child class. For example,\nyou may use `UserFixture` to make sure the user DB table contains a fixed set of data. You load one or multiple\nfixture objects before running a test and unload them when finishing.\n\nA fixture may depend on other fixtures, specified via its [[yii\\test\\Fixture::depends]] property.\nWhen a fixture is being loaded, the fixtures it depends on will be automatically loaded BEFORE the fixture;\nand when the fixture is being unloaded, the dependent fixtures will be unloaded AFTER the fixture.\n\n\n## Defining a Fixture\n\nTo define a fixture, create a new class by extending [[yii\\test\\Fixture]] or [[yii\\test\\ActiveFixture]].\nThe former is best suited for general purpose fixtures, while the latter has enhanced features specifically\ndesigned to work with database and ActiveRecord.\n\nThe following code defines a fixture about the `User` ActiveRecord and the corresponding user table.\n\n```php\n<?php\nnamespace app\\tests\\fixtures;\n\nuse yii\\test\\ActiveFixture;\n\nclass UserFixture extends ActiveFixture\n{\n    public $modelClass = 'app\\models\\User';\n}\n```\n\n> Tip: Each `ActiveFixture` is about preparing a DB table for testing purpose. You may specify the table\n> by setting either the [[yii\\test\\ActiveFixture::tableName]] property or the [[yii\\test\\ActiveFixture::modelClass]]\n> property. If the latter, the table name will be taken from the `ActiveRecord` class specified by `modelClass`.\n\n> Note: [[yii\\test\\ActiveFixture]] is only suited for SQL databases. For NoSQL databases, Yii provides the following\n> `ActiveFixture` classes:\n>\n> - Mongo DB: [[yii\\mongodb\\ActiveFixture]]\n> - Elasticsearch: [[yii\\elasticsearch\\ActiveFixture]] (since version 2.0.2)\n\n\nThe fixture data for an `ActiveFixture` fixture is usually provided in a file located at `fixturepath/data/tablename.php`,\nwhere `fixturepath` stands for the directory containing the fixture class file, and `tablename`\nis the name of the table associated with the fixture. In the example above, the file should be\n`@app/tests/fixtures/data/user.php`. The data file should return an array of data rows\nto be inserted into the user table. For example,\n\n```php\n<?php\nreturn [\n    'user1' => [\n        'username' => 'lmayert',\n        'email' => 'strosin.vernice@jerde.com',\n        'auth_key' => 'K3nF70it7tzNsHddEiq0BZ0i-OU8S3xV',\n        'password' => '$2y$13$WSyE5hHsG1rWN2jV8LRHzubilrCLI5Ev/iK0r3jRuwQEs2ldRu.a2',\n    ],\n    'user2' => [\n        'username' => 'napoleon69',\n        'email' => 'aileen.barton@heaneyschumm.com',\n        'auth_key' => 'dZlXsVnIDgIzFgX4EduAqkEPuphhOh9q',\n        'password' => '$2y$13$kkgpvJ8lnjKo8RuoR30ay.RjDf15bMcHIF7Vz1zz/6viYG5xJExU6',\n    ],\n];\n```\n\nYou may give an alias to a row so that later in your test, you may refer to the row via the alias. In the above example,\nthe two rows are aliased as `user1` and `user2`, respectively.\n\nAlso, you do not need to specify the data for auto-incremental columns. Yii will automatically fill the actual\nvalues into the rows when the fixture is being loaded.\n\n> Tip: You may customize the location of the data file by setting the [[yii\\test\\ActiveFixture::dataFile]] property.\n> You may also override [[yii\\test\\ActiveFixture::getData()]] to provide the data.\n\nAs we described earlier, a fixture may depend on other fixtures. For example, a `UserProfileFixture` may need to depend on `UserFixture`\nbecause the user profile table contains a foreign key pointing to the user table.\nThe dependency is specified via the [[yii\\test\\Fixture::depends]] property, like the following,\n\n```php\nnamespace app\\tests\\fixtures;\n\nuse yii\\test\\ActiveFixture;\n\nclass UserProfileFixture extends ActiveFixture\n{\n    public $modelClass = 'app\\models\\UserProfile';\n    public $depends = ['app\\tests\\fixtures\\UserFixture'];\n}\n```\n\nThe dependency also ensures, that the fixtures are loaded and unloaded in a well-defined order. In the above example `UserFixture` will\nalways be loaded before `UserProfileFixture` to ensure all foreign key references exist and will be unloaded after `UserProfileFixture`\nhas been unloaded for the same reason.\n\nIn the above, we have shown how to define a fixture about a DB table. To define a fixture not related with DB\n(e.g. a fixture about certain files and directories), you may extend from the more general base class\n[[yii\\test\\Fixture]] and override the [[yii\\test\\Fixture::load()|load()]] and [[yii\\test\\Fixture::unload()|unload()]] methods.\n\n\n## Using Fixtures\n\nIf you are using [Codeception](https://codeception.com/) to test your code, you can use the built-in support for loading\nand accessing fixtures.\n\nIf you are using other testing frameworks, you may use [[yii\\test\\FixtureTrait]] in your\ntest cases to achieve the same goal.\n\nIn the following we will describe how to write a `UserProfile` unit test class using Codeception.\n\nIn your unit test class extending `\\Codeception\\Test\\Unit` either declare fixtures you want to use in the\n`_fixtures()` method or use `haveFixtures()` method of an actor directly. For example,\n\n```php\nnamespace app\\tests\\unit\\models;\n\n\nuse app\\tests\\fixtures\\UserProfileFixture;\n\nclass UserProfileTest extends \\Codeception\\Test\\Unit\n{   \n    public function _fixtures()\n    {\n        return [\n            'profiles' => [\n                'class' => UserProfileFixture::class,\n                // fixture data located in tests/_data/user.php\n                'dataFile' => codecept_data_dir() . 'user.php'\n            ],\n        ];\n    }\n\n    // ...test methods...\n}\n```\n\nThe fixtures listed in the `_fixtures()` method will be automatically loaded before a test is executed. And as we\ndescribed before, when a fixture is being loaded, all its dependent fixtures will be automatically loaded first.\nIn the above example, because `UserProfileFixture` depends on `UserFixture`, when running any test method in the test\nclass, two fixtures will be loaded sequentially: `UserFixture` and `UserProfileFixture`.\n\nWhen specifying fixtures for both `_fixtures()` and `haveFixtures()`, you may use either a class name\nor a configuration array to refer to a fixture. The configuration array will let you customize\nthe fixture properties when the fixture is loaded.\n\nYou may also assign an alias to a fixture. In the above example, the `UserProfileFixture` is aliased as `profiles`.\nIn the test methods, you may then access a fixture object using its alias in `grabFixture()` method. For example,\n\n```php\n$profile = $I->grabFixture('profiles');\n```\n\nwill return the `UserProfileFixture` object.\n\nBecause `UserProfileFixture` extends from `ActiveFixture`, you may further use the following syntax to access\nthe data provided by the fixture:\n\n```php\n// returns the UserProfile model corresponding to the data row aliased as 'user1'\n$profile = $I->grabFixture('profiles', 'user1');\n// traverse data in the fixture\nforeach ($I->grabFixture('profiles') as $profile) ...\n```\n\n## Organizing Fixture Classes and Data Files\n\nBy default, fixture classes look for the corresponding data files under the `data` folder which is a sub-folder\nof the folder containing the fixture class files. You can follow this convention when working with simple projects.\nFor big projects, chances are that you often need to switch different data files for the same fixture class for\ndifferent tests. We thus recommend that you organize the data files in a hierarchical way that is similar to\nyour class namespaces. For example,\n\n```\n# under folder tests\\unit\\fixtures\n\ndata\\\n    components\\\n        fixture_data_file1.php\n        fixture_data_file2.php\n        ...\n        fixture_data_fileN.php\n    models\\\n        fixture_data_file1.php\n        fixture_data_file2.php\n        ...\n        fixture_data_fileN.php\n# and so on\n```\n\nIn this way you will avoid collision of fixture data files between tests and use them as you need.\n\n> Note: In the example above fixture files are named only for example purpose. In real life you should name them\n> according to which fixture class your fixture classes are extending from. For example, if you are extending\n> from [[yii\\test\\ActiveFixture]] for DB fixtures, you should use DB table names as the fixture data file names;\n> If you are extending from [[yii\\mongodb\\ActiveFixture]] for MongoDB fixtures, you should use collection names as the file names.\n\nA similar hierarchy can be used to organize fixture class files. Instead of using `data` as the root directory, you may\nwant to use `fixtures` as the root directory to avoid conflict with the data files.\n\n## Managing fixtures with `yii fixture`\n\nYii supports fixtures via the `yii fixture` command line tool. This tool supports:\n\n* Loading fixtures to different storage such as: RDBMS, NoSQL, etc;\n* Unloading fixtures in different ways (usually it is clearing storage);\n* Auto-generating fixtures and populating it with random data.\n\n### Fixtures data format\n\nLets assume we have fixtures data to load:\n\n```\n#users.php file under fixtures data path, by default @tests\\unit\\fixtures\\data\n\nreturn [\n    [\n        'name' => 'Chase',\n        'login' => 'lmayert',\n        'email' => 'strosin.vernice@jerde.com',\n        'auth_key' => 'K3nF70it7tzNsHddEiq0BZ0i-OU8S3xV',\n        'password' => '$2y$13$WSyE5hHsG1rWN2jV8LRHzubilrCLI5Ev/iK0r3jRuwQEs2ldRu.a2',\n    ],\n    [\n        'name' => 'Celestine',\n        'login' => 'napoleon69',\n        'email' => 'aileen.barton@heaneyschumm.com',\n        'auth_key' => 'dZlXsVnIDgIzFgX4EduAqkEPuphhOh9q',\n        'password' => '$2y$13$kkgpvJ8lnjKo8RuoR30ay.RjDf15bMcHIF7Vz1zz/6viYG5xJExU6',\n    ],\n];\n```\nIf we are using fixture that loads data into database then these rows will be applied to `users` table. If we are using nosql fixtures, for example `mongodb`\nfixture, then this data will be applied to `users` mongodb collection. In order to learn about implementing various loading strategies and more, refer to official [documentation](https://github.com/yiisoft/yii2/blob/master/docs/guide/test-fixtures.md).\nAbove fixture example was auto-generated by `yii2-faker` extension, read more about it in these [section](#auto-generating-fixtures).\nFixture classes name should not be plural.\n\n### Loading fixtures\n\nFixture classes should be suffixed by `Fixture`. By default, fixtures will be searched under `tests\\unit\\fixtures` namespace, you can\nchange this behavior with config or command options. You can exclude some fixtures due load or unload by specifying `-` before its name like `-User`.\n\nTo load fixture, run the following command:\n\n> Note: Prior to loading data unload sequence is executed. Usually that results in cleaning up all the existing data inserted by previous fixture executions.\n\n```\nyii fixture/load <fixture_name>\n```\n\nThe required `fixture_name` parameter specifies a fixture name which data will be loaded. You can load several fixtures at once.\nBelow are correct formats of this command:\n\n```\n// load `User` fixture\nyii fixture/load User\n\n// same as above, because default action of \"fixture\" command is \"load\"\nyii fixture User\n\n// load several fixtures\nyii fixture \"User, UserProfile\"\n\n// load all fixtures\nyii fixture/load \"*\"\n\n// same as above\nyii fixture \"*\"\n\n// load all fixtures except ones\nyii fixture \"*, -DoNotLoadThisOne\"\n\n// load fixtures, but search them in different namespace. By default namespace is: tests\\unit\\fixtures.\nyii fixture User --namespace='alias\\my\\custom\\namespace'\n\n// load global fixture `some\\name\\space\\CustomFixture` before other fixtures will be loaded.\n// By default this option is set to `InitDbFixture` to disable/enable integrity checks. You can specify several\n// global fixtures separated by comma.\nyii fixture User --globalFixtures='some\\name\\space\\Custom'\n```\n\n### Unloading fixtures\n\nTo unload fixture, run the following command:\n\n```\n// unload Users fixture, by default it will clear fixture storage (for example \"users\" table, or \"users\" collection if this is mongodb fixture).\nyii fixture/unload User\n\n// Unload several fixtures\nyii fixture/unload \"User, UserProfile\"\n\n// unload all fixtures\nyii fixture/unload \"*\"\n\n// unload all fixtures except ones\nyii fixture/unload \"*, -DoNotUnloadThisOne\"\n\n```\n\nSame command options like: `namespace`, `globalFixtures` also can be applied to this command.\n\n### Configure Command Globally\n\nWhile command line options allow us to configure the fixture command\non-the-fly, sometimes we may want to configure the command once for all. For example, you can configure\ndifferent fixture path as follows:\n\n```\n'controllerMap' => [\n    'fixture' => [\n        'class' => 'yii\\console\\controllers\\FixtureController',\n        'namespace' => 'myalias\\some\\custom\\namespace',\n        'globalFixtures' => [\n            'some\\name\\space\\Foo',\n            'other\\name\\space\\Bar'\n        ],\n    ],\n]\n```\n\n### Auto-generating fixtures\n\nYii also can auto-generate fixtures for you based on some template. You can generate your fixtures with different data on different languages and formats.\nThis feature is done by [Faker](https://github.com/fzaninotto/Faker) library and `yii2-faker` extension.\nSee extension [guide](https://github.com/yiisoft/yii2-faker) for more docs.\n\n## Summary\n\nIn the above, we have described how to define and use fixtures. Below we summarize the typical workflow\nof running DB related unit tests:\n\n1. Use the `yii migrate` tool to upgrade your test database to the latest version;\n2. Run a test case:\n   - Load fixtures: clean up the relevant DB tables and populate them with fixture data;\n   - Perform the actual test;\n   - Unload fixtures.\n3. Repeat Step 2 until all tests finish.\n"
  },
  {
    "path": "docs/guide/test-functional.md",
    "content": "Functional Tests\n================\n\nFunctional test verifies scenarios from a user's perspective. It is similar to [acceptance test](test-acceptance.md)\nbut instead of communicating via HTTP it is filling up environment such as POST and GET parameters and then executes\napplication instance right from the code.\n\nFunctional tests are generally faster than acceptance tests and are providing detailed stack traces on failures.\nAs a rule of thumb, they should be preferred unless you have a special web server setup or complex UI powered by\nJavaScript.\n\nFunctional testing is implemented with the help of Codeception framework which has a nice documentation about it:\n\n- [Codeception for Yii framework](https://codeception.com/for/yii)\n- [Codeception Functional Tests](https://codeception.com/docs/04-FunctionalTests)\n\n## Running basic and advanced template tests\n\nIf you've started with advanced template, please refer to [\"testing\" guide](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/start-testing.md)\nfor more details about running tests.  \n\nIf you've started with basic template, check its [README \"testing\" section](https://github.com/yiisoft/yii2-app-basic/blob/master/README.md#testing).\n"
  },
  {
    "path": "docs/guide/test-overview.md",
    "content": "Testing\n=======\n\nTesting is an important part of software development. Whether we are aware of it or not, we conduct testing continuously.\nFor example, when we write a class in PHP, we may debug it step by step or simply use `echo` or `die` statements to verify\nthe implementation works according to our initial plan. In the case of a web application, we're entering some test data\nin forms to ensure the page interacts with us as expected.\n\nThe testing process could be automated so that each time when we need to verify something, we just need to call up\nthe code that does it for us. The code that verifies the result matches what we've planned is called *test* and\nthe process of its creation and further execution is known as *automated testing*, which is the main topic of these\ntesting chapters.\n\n\n## Developing with tests\n\nTest-Driven Development (TDD) and Behavior-Driven Development (BDD) are approaches of developing\nsoftware by describing behavior of a piece of code or the whole feature as a set of scenarios or tests before\nwriting actual code and only then creating the implementation that allows these tests to pass verifying that intended\nbehavior is achieved.\n\nThe process of developing a feature is the following:\n\n- Create a new test that describes a feature to be implemented.\n- Run the new test and make sure it fails. It is expected since there's no implementation yet.\n- Write simple code to make the new test pass.\n- Run all tests and make sure they all pass.\n- Improve code and make sure tests are still OK.\n\nAfter it's done the process is repeated again for another feature or improvement. If the existing feature is to be changed,\ntests should be changed as well.\n\n> Tip: If you feel that you are losing time doing a lot of small and simple iterations, try covering more by your\n> test scenario so you do more before executing tests again. If you're debugging too much, try doing the opposite.\n\nThe reason to create tests before doing any implementation is that it allows us to focus on what we want to achieve\nand fully dive into \"how to do it\" afterwards. Usually it leads to better abstractions and easier test maintenance when\nit comes to feature adjustments or less coupled components.\n\nSo to sum up the advantages of such approach are the following:\n\n- Keeps you focused on one thing at a time which results in improved planning and implementation.\n- Results in test-covering more features in greater detail i.e. if tests are OK most likely nothing's broken.\n\nIn the long term it usually gives you a good time-saving effect.\n\n## When and how to test\n\nWhile the test first approach described above makes sense for long term and relatively complex projects it could be overkill\nfor simpler ones. There are some indicators of when it's appropriate:\n\n- Project is already large and complex.\n- Project requirements are starting to get complex. Project grows constantly.\n- Project is meant to be long term.\n- The cost of the failure is too high.\n\nThere's nothing wrong in creating tests covering behavior of existing implementation.\n\n- Project is a legacy one to be gradually renewed.\n- You've got a project to work on and it has no tests.\n\nIn some cases any form of automated testing could be overkill:\n\n- Project is simple and isn't getting any more complex.\n- It's a one-time project that will no longer be worked on.\n\nStill if you have time it's good to automate testing in these cases as well.\n\n## Further reading\n\n- Test Driven Development: By Example / Kent Beck. ISBN: 0321146530.\n"
  },
  {
    "path": "docs/guide/test-unit.md",
    "content": "Unit Tests\n==========\n\nA unit test verifies that a single unit of code is working as expected. That is, given different input parameters,\nthe test verifies the class method returns expected results. Unit tests are usually developed by people who write the\nclasses being tested.\n\nUnit testing in Yii is built on top of PHPUnit and, optionally, Codeception so it's recommended to go through their docs:\n\n- [Codeception for Yii framework](https://codeception.com/for/yii)\n- [Codeception Unit Tests](https://codeception.com/docs/05-UnitTests)\n- [PHPUnit docs starting from chapter 2](https://phpunit.de/manual/current/en/writing-tests-for-phpunit.html)\n\n## Running basic and advanced template tests\n\nIf you've started with advanced template, please refer to [\"testing\" guide](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/start-testing.md)\nfor more details about running tests.  \n\nIf you've started with basic template, check its [README \"testing\" section](https://github.com/yiisoft/yii2-app-basic/blob/master/README.md#testing).\n\n## Framework unit tests\n\nIf you want to run unit tests for Yii framework itself follow\n\"[Getting started with Yii 2 development](https://github.com/yiisoft/yii2/blob/master/docs/internals/getting-started.md)\".\n"
  },
  {
    "path": "docs/guide/tutorial-console.md",
    "content": "Console applications\n====================\n\nBesides, the rich features for building web applications, Yii also has full-featured support for console applications\nwhich are mainly used to create background and maintenance tasks that need to be performed for a website.\n\nThe structure of console applications is very similar to a Yii web application. It consists of one\nor more [[yii\\console\\Controller]] classes, which are often referred to as *commands* in the console environment.\nEach controller can also have one or more actions, just like web controllers.\n\nBoth project templates already have a console application with them.\nYou can run it by calling the `yii` script, which is located in the base directory of the repository. \nThis will give you a list of available commands when you run it without any further parameters:\n\n![Running ./yii command for help output](images/tutorial-console-help.png)\n\nAs you can see in the screenshot, Yii has already defined a set of commands that are available by default:\n\n- [[yii\\console\\controllers\\AssetController|AssetController]] - Allows you to combine and compress your JavaScript and CSS files.\n  You can learn more about this command in the [Assets Section](structure-assets.md#using-asset-bundles).\n- [[yii\\console\\controllers\\CacheController|CacheController]] - Allows you to flush application caches.\n- [[yii\\console\\controllers\\FixtureController|FixtureController]] - Manages fixture data loading and unloading for testing purposes.\n  This command is described in more detail in the [Testing Section about Fixtures](test-fixtures.md#managing-fixtures).\n- [[yii\\console\\controllers\\HelpController|HelpController]] - Provides help information about console commands, this is the default command\n  and prints what you have seen in the above output.\n- [[yii\\console\\controllers\\MessageController|MessageController]] - Extracts messages to be translated from source files.\n  To learn more about this command, please refer to the [I18N Section](tutorial-i18n.md#message-command).\n- [[yii\\console\\controllers\\MigrateController|MigrateController]] - Manages application migrations.\n  Database migrations are described in more detail in the [Database Migration Section](db-migrations.md).\n- [[yii\\console\\controllers\\ServeController|ServeController]] - Allows you run PHP built-in web server.\n\n\nUsage <span id=\"usage\"></span>\n-----\n\nYou execute a console controller action using the following syntax:\n\n```\nyii <route> [--option1=value1 ... argument1 argument2 ... --option2=value2]\n```\n\nOptions could be specified in any position.\n\nIn the above, `<route>` refers to the route to the controller action. The options will populate the class\nproperties and arguments are the parameters of the action method.\n\nFor example, the [[yii\\console\\controllers\\MigrateController::actionUp()|MigrateController::actionUp()]]\nwith [[yii\\console\\controllers\\MigrateController::$migrationTable|MigrateController::$migrationTable]] set to `migrations`\nand a limit of 5 migrations can be called like so:\n\n```\nyii migrate/up 5 --migrationTable=migrations\n```\n\n> Note: When using `*` in console, don't forget to quote it as `\"*\"` in order to avoid executing it as a shell\n> glob that will be replaced by all file names of the current directory.\n\n\nThe entry script <span id=\"entry-script\"></span>\n----------------\n\nThe console application entry script is equivalent to the `index.php` bootstrap file used for the web application.\nThe console entry script is typically called `yii`, and located in your application's root directory.\nIt contains code like the following:\n\n```php\n#!/usr/bin/env php\n<?php\n/**\n * Yii console bootstrap file.\n */\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n\nrequire __DIR__ . '/vendor/autoload.php';\nrequire __DIR__ . '/vendor/yiisoft/yii2/Yii.php';\n\n$config = require __DIR__ . '/config/console.php';\n\n$application = new yii\\console\\Application($config);\n$exitCode = $application->run();\nexit($exitCode);\n```\n\nThis script will be created as part of your application; you're free to edit it to suit your needs. The `YII_DEBUG` constant can be set to `false` if you do\nnot want to see a stack trace on error, and/or if you want to improve the overall performance. In both basic and advanced application\ntemplates, the console application entry script has debugging enabled by default to provide a more developer-friendly environment.\n\n\nConfiguration <span id=\"configuration\"></span>\n-------------\n\nAs can be seen in the code above, the console application uses its own configuration file, named `console.php`. In this file\nyou should configure various [application components](structure-application-components.md) and properties for the console application in particular.\n\nIf your web application and console application share a lot of configuration parameters and values, you may consider moving the common\nparts into a separate file, and including this file in both of the application configurations (web and console).\nYou can see an example of this in the advanced project template.\n\n> Tip: Sometimes, you may want to run a console command using an application configuration that is different\n> from the one specified in the entry script. For example, you may want to use the `yii migrate` command to\n> upgrade your test databases, which are configured in each individual test suite. To change the configuration\n> dynamically, simply specify a custom application configuration\n> file via the `appconfig` option when executing the command:\n> \n> ```\n> yii <route> --appconfig=path/to/config.php ...\n> ```\n\n\nConsole command completion <span id=\"console-command-completion\"></span>\n---------------\n\nAuto-completion of command arguments is a useful thing when working with the shell. \nSince version 2.0.11, the `./yii` command provides auto-completion for the Bash and ZSH out of the box. \n\n### Bash completion\n\nMake sure bash completion is installed. For most of the installations it is available by default.\n\nPlace the completion script in `/etc/bash_completion.d/`:\n\n     curl -L https://raw.githubusercontent.com/yiisoft/yii2/master/contrib/completion/bash/yii -o /etc/bash_completion.d/yii\n\nFor temporary usage you can put the file into the current directory and include it in the current session via `source yii`.\nIf globally installed you may need to restart the terminal or `source ~/.bashrc` to activate it.\n\nCheck the [Bash Manual](https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion.html) for\nother ways of including completion script to your environment.\n\n### ZSH completion\n\nPut the completion script in directory for completions, using e.g. `~/.zsh/completion/`\n\n```\nmkdir -p ~/.zsh/completion\ncurl -L https://raw.githubusercontent.com/yiisoft/yii2/master/contrib/completion/zsh/_yii -o ~/.zsh/completion/_yii\n```\n\nInclude the directory in the `$fpath`, e.g. by adding it to `~/.zshrc`\n\n```\nfpath=(~/.zsh/completion $fpath)\n```\n\nMake sure `compinit` is loaded or do it by adding in `~/.zshrc`\n\n```\nautoload -Uz compinit && compinit -i\n```\n\nThen reload your shell\n\n```\nexec $SHELL -l\n```\n\nCreating your own console commands <span id=\"create-command\"></span>\n----------------------------------\n\n### Console Controller and Action\n\nA console command is defined as a controller class extending from [[yii\\console\\Controller]]. In the controller class,\nyou define one or more actions that correspond to sub-commands of the controller. Within each action, you write code that implements the appropriate tasks for that particular sub-command.\n\nWhen running a command, you need to specify the route to the  controller action. For example,\nthe route `migrate/create` invokes the sub-command that corresponds to the\n[[yii\\console\\controllers\\MigrateController::actionCreate()|MigrateController::actionCreate()]] action method.\nIf a route offered during execution does not contain an action ID, the default action will be executed (as with a web controller).\n\n### Options\n\nBy overriding the [[yii\\console\\Controller::options()]] method, you can specify options that are available\nto a console command (controller/actionID). The method should return a list of the controller class's public properties.\nWhen running a command, you may specify the value of an option using the syntax `--optionName=optionValue`.\nThis will assign `optionValue` to the `optionName` property of the controller class.\n\nIf the default value of an option is of an array type and you set this option while running the command,\nthe option value will be converted into an array by splitting the input string on any commas.\n\n### Options Aliases\n\nSince version 2.0.8 console command provides [[yii\\console\\Controller::optionAliases()]] method to add\naliases for options.\n\nTo define an alias, override [[yii\\console\\Controller::optionAliases()]] in your controller, for example:\n\n```php\nnamespace app\\commands;\n\nuse yii\\console\\Controller;\n\nclass HelloController extends Controller\n{\n    public $message;\n    \n    public function options($actionID)\n    {\n        return ['message'];\n    }\n    \n    public function optionAliases()\n    {\n        return ['m' => 'message'];\n    }\n    \n    public function actionIndex()\n    {\n        echo $this->message . \"\\n\";\n    }\n}\n```\n\nNow, you can use the following syntax to run the command:\n\n```\nyii hello -m=hello\n```\n\n### Arguments\n\nBesides options, a command can also receive arguments. The arguments will be passed as the parameters to the action\nmethod corresponding to the requested sub-command. The first argument corresponds to the first parameter, the second\ncorresponds to the second, and so on. If not enough arguments are provided when the command is called, the corresponding parameters\nwill take the declared default values, if defined. If no default value is set, and no value is provided at runtime, the command will exit with an error.\n\nYou may use the `array` type hint to indicate that an argument should be treated as an array. The array will be generated\nby splitting the input string on commas.\n\nThe following example shows how to declare arguments:\n\n```php\nclass ExampleController extends \\yii\\console\\Controller\n{\n    // The command \"yii example/create test\" will call \"actionCreate('test')\"\n    public function actionCreate($name) { ... }\n\n    // The command \"yii example/index city\" will call \"actionIndex('city', 'name')\"\n    // The command \"yii example/index city id\" will call \"actionIndex('city', 'id')\"\n    public function actionIndex($category, $order = 'name') { ... }\n\n    // The command \"yii example/add test\" will call \"actionAdd(['test'])\"\n    // The command \"yii example/add test1,test2\" will call \"actionAdd(['test1', 'test2'])\"\n    public function actionAdd(array $name) { ... }\n}\n```\n\n\n### Exit Code\n\nUsing exit codes is a best practice for console application development. Conventionally, a command returns `0` to indicate that\neverything is OK. If the command returns a number greater than zero, that's considered to be indicative of an error. The number returned will be the error\ncode, potentially usable to find out details about the error.\nFor example `1` could stand generally for an unknown error and all codes above would be reserved for specific cases: input errors, missing files, and so forth.\n\nTo have your console command return an exit code, simply return an integer in the controller action\nmethod:\n\n```php\npublic function actionIndex()\n{\n    if (/* some problem */) {\n        echo \"A problem occurred!\\n\";\n        return 1;\n    }\n    // do something\n    return 0;\n}\n```\n\nThere are some predefined constants you can use. These are defined in the [[yii\\console\\ExitCode]] class:\n\n```php\npublic function actionIndex()\n{\n    if (/* some problem */) {\n        echo \"A problem occurred!\\n\";\n        return ExitCode::UNSPECIFIED_ERROR;\n    }\n    // do something\n    return ExitCode::OK;\n}\n```\n\nIt's a good practice to define meaningful constants for your controller in case you have more specific error code types.\n\n### Formatting and colors\n\nYii console supports formatted output that is automatically degraded to non-formatted one if it's not supported\nby terminal running the command.\n\nOutputting formatted strings is simple. Here's how to output some bold text:\n\n```php\n$this->stdout(\"Hello?\\n\", Console::BOLD);\n```\n\nIf you need to build string dynamically combining multiple styles it's better to use [[yii\\helpers\\Console::ansiFormat()|ansiFormat()]]:\n\n```php\n$name = $this->ansiFormat('Alex', Console::FG_YELLOW);\necho \"Hello, my name is $name.\";\n```\n\n### Tables\n\nSince version 2.0.13 there is a widget that allows you to format table data in console. It could be used as the following:\n\n```php\necho Table::widget([\n    'headers' => ['Project', 'Status', 'Participant'],\n    'rows' => [\n        ['Yii', 'OK', '@samdark'],\n        ['Yii', 'OK', '@cebe'],\n    ],\n]);\n```\n\nFor details please refer to [[yii\\console\\widgets\\Table|API documentation]].\n"
  },
  {
    "path": "docs/guide/tutorial-core-validators.md",
    "content": "Core Validators\n===============\n\nYii provides a set of commonly used core validators, found primarily under the `yii\\validators` namespace.\nInstead of using lengthy validator class names, you may use *aliases* to specify the use of these core\nvalidators. For example, you can use the alias `required` to refer to the [[yii\\validators\\RequiredValidator]] class:\n\n```php\npublic function rules()\n{\n    return [\n        [['email', 'password'], 'required'],\n    ];\n}\n```\n\nThe [[yii\\validators\\Validator::builtInValidators]] property declares all supported validator aliases.\n\nIn the following, we will describe the main usage and properties of every core validator.\n\n\n## [[yii\\validators\\BooleanValidator|boolean]] <span id=\"boolean\"></span>\n\n```php\n[\n    // checks if \"selected\" is either 0 or 1, regardless of data type\n    ['selected', 'boolean'],\n\n    // checks if \"deleted\" is of boolean type, either true or false\n    ['deleted', 'boolean', 'trueValue' => true, 'falseValue' => false, 'strict' => true],\n]\n```\n\nThis validator checks if the input value is a boolean.\n\n- `trueValue`: the value representing `true`. Defaults to `'1'`.\n- `falseValue`: the value representing `false`. Defaults to `'0'`.\n- `strict`: whether the type of the input value should match that of `trueValue` and `falseValue`. Defaults to `false`.\n\n\n> Note: Because data input submitted via HTML forms are all strings, you normally should leave the\n  [[yii\\validators\\BooleanValidator::strict|strict]] property as `false`.\n\n\n## [[yii\\captcha\\CaptchaValidator|captcha]] <span id=\"captcha\"></span>\n\n```php\n[\n    ['verificationCode', 'captcha'],\n]\n```\n\nThis validator is usually used together with [[yii\\captcha\\CaptchaAction]] and [[yii\\captcha\\Captcha]]\nto make sure an input is the same as the verification code displayed by [[yii\\captcha\\Captcha|CAPTCHA]] widget.\n\n- `caseSensitive`: whether the comparison of the verification code is case-sensitive. Defaults to `false`.\n- `captchaAction`: the [route](structure-controllers.md#routes) corresponding to the\n  [[yii\\captcha\\CaptchaAction|CAPTCHA action]] that renders the CAPTCHA image. Defaults to `'site/captcha'`.\n- `skipOnEmpty`: whether the validation can be skipped if the input is empty. Defaults to `false`,\n  which means the input is required.\n\n\n## [[yii\\validators\\CompareValidator|compare]] <span id=\"compare\"></span>\n\n```php\n[\n    // validates if the value of \"password\" attribute equals to that of \"password_repeat\"\n    ['password', 'compare'],\n\n    // same as above but with explicitly specifying the attribute to compare with\n    ['password', 'compare', 'compareAttribute' => 'password_repeat'],\n\n    // validates if age is greater than or equal to 30\n    ['age', 'compare', 'compareValue' => 30, 'operator' => '>=', 'type' => 'number'],\n]\n```\n\nThis validator compares the specified input value with another one and make sure if their relationship\nis as specified by the `operator` property.\n\n- `compareAttribute`: the name of the attribute whose value should be compared with. When the validator\n  is being used to validate an attribute, the default value of this property would be the name of\n  the attribute suffixed with `_repeat`. For example, if the attribute being validated is `password`,\n  then this property will default to `password_repeat`.\n- `compareValue`: a constant value (or a closure returning a value) that the input value should be compared with. When both\n  of this property and `compareAttribute` are specified, this property will take precedence.\n- `operator`: the comparison operator. Defaults to `==`, meaning checking if the input value is equal\n  to that of `compareAttribute` or `compareValue`. The following operators are supported:\n     * `==`: check if two values are equal. The comparison is done is non-strict mode.\n     * `===`: check if two values are equal. The comparison is done is strict mode.\n     * `!=`: check if two values are NOT equal. The comparison is done is non-strict mode.\n     * `!==`: check if two values are NOT equal. The comparison is done is strict mode.\n     * `>`: check if value being validated is greater than the value being compared with.\n     * `>=`: check if value being validated is greater than or equal to the value being compared with.\n     * `<`: check if value being validated is less than the value being compared with.\n     * `<=`: check if value being validated is less than or equal to the value being compared with.\n- `type`: The default comparison type is '[[yii\\validators\\CompareValidator::TYPE_STRING|string]]', which means the values are\n  compared byte by byte. When comparing numbers, make sure to set the [[yii\\validators\\CompareValidator::$type|$type]]\n  to '[[yii\\validators\\CompareValidator::TYPE_NUMBER|number]]' to enable numeric comparison.\n\n### Comparing date values\n\nThe compare validator can only be used to compare strings and numbers. If you need to compare values\nlike dates you have two options. For comparing a date against a fixed value, you can simply use the\n[[yii\\validators\\DateValidator|date]] validator and specify its\n[[yii\\validators\\DateValidator::$min|$min]] or [[yii\\validators\\DateValidator::$max|$max]] property.\nIf you need to compare two dates entered in the form, e.g. a `fromDate` and a `toDate` field,\nyou can use a combination of compare and date validator like the following:\n\n```php\n['fromDate', 'date', 'timestampAttribute' => 'fromDate'],\n['toDate', 'date', 'timestampAttribute' => 'toDate'],\n['fromDate', 'compare', 'compareAttribute' => 'toDate', 'operator' => '<', 'enableClientValidation' => false],\n```\n\nAs validators are executed in the order they are specified this will first validate that the values entered in\n`fromDate` and `toDate` are valid date values and if so, they will be converted into a machine-readable format.\nAfterwards these two values are compared with the compare validator.\nClient validation is not enabled as this will only work on the server-side because the date validator currently does not\nprovide client validation, so [[yii\\validators\\CompareValidator::$enableClientValidation|$enableClientValidation]]\nis set to `false` on the compare validator too.\n\n\n## [[yii\\validators\\DateValidator|date]] <span id=\"date\"></span>\n\nThe [[yii\\validators\\DateValidator|date]] validator comes with three different\nshortcuts:\n\n```php\n[\n    [['from_date', 'to_date'], 'date'],\n    [['from_datetime', 'to_datetime'], 'datetime'],\n    [['some_time'], 'time'],\n]\n```\n\nThis validator checks if the input value is a date, time or datetime in a proper format.\nOptionally, it can convert the input value into a UNIX timestamp or other machine readable format and store it in an attribute\nspecified via [[yii\\validators\\DateValidator::timestampAttribute|timestampAttribute]].\n\n- `format`: the date/time format that the value being validated should be in.\n   This can be a date time pattern as described in the [ICU manual](https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax).\n   Alternatively this can be a string prefixed with `php:` representing a format that can be recognized by the PHP\n   `Datetime` class. Please refer to <https://www.php.net/manual/en/datetime.createfromformat.php> on supported formats.\n   If this is not set, it will take the value of `Yii::$app->formatter->dateFormat`.\n   See the [[yii\\validators\\DateValidator::$format|API documentation]] for more details.\n\n- `timestampAttribute`: the name of the attribute to which this validator may assign the UNIX timestamp\n  converted from the input date/time. This can be the same attribute as the one being validated. If this is the case,\n  the original value will be overwritten with the timestamp value after validation.\n  See [\"Handling date input with the DatePicker\"](https://github.com/yiisoft/yii2-jui/blob/master/docs/guide/topics-date-picker.md) for a usage example.\n\n  Since version 2.0.4, a format and timezone can be specified for this attribute using\n  [[yii\\validators\\DateValidator::$timestampAttributeFormat|$timestampAttributeFormat]] and\n  [[yii\\validators\\DateValidator::$timestampAttributeTimeZone|$timestampAttributeTimeZone]].\n  \n  Note, that when using `timestampAttribute`, the input value will be converted to a unix timestamp, which by definition is in UTC, so\n  a conversion from the [[yii\\validators\\DateValidator::timeZone|input time zone]] to UTC will be performed (this behavior \n  can be changed by setting [[yii\\validators\\DateValidator::$defaultTimeZone|$defaultTimeZone]] since 2.0.39).\n\n- Since version 2.0.4 it is also possible to specify a [[yii\\validators\\DateValidator::$min|minimum]] or\n  [[yii\\validators\\DateValidator::$max|maximum]] timestamp.\n\nIn case the input is optional you may also want to add a [default value filter](#default) in addition to the date validator\nto ensure empty input is stored as `null`. Otherwise, you may end up with dates like `0000-00-00` in your database\nor `1970-01-01` in the input field of a date picker.\n\n```php\n[\n    [['from_date', 'to_date'], 'default', 'value' => null],\n    [['from_date', 'to_date'], 'date'],\n],\n```\n\n## [[yii\\validators\\DefaultValueValidator|default]] <span id=\"default\"></span>\n\n```php\n[\n    // set \"age\" to be null if it is empty\n    ['age', 'default', 'value' => null],\n\n    // set \"country\" to be \"USA\" if it is empty\n    ['country', 'default', 'value' => 'USA'],\n\n    // assign \"from\" and \"to\" with a date 3 days and 6 days from today, if they are empty\n    [['from', 'to'], 'default', 'value' => function ($model, $attribute) {\n        return date('Y-m-d', strtotime($attribute === 'to' ? '+3 days' : '+6 days'));\n    }],\n]\n```\n\nThis validator does not validate data. Instead, it assigns a default value to the attributes being validated\nif the attributes are empty.\n\n- `value`: the default value or a closure as callback that returns the default value which will be assigned to\n  the attributes being validated if they are empty. The signature of the closure should be as follows,\n\n```php\nfunction foo($model, $attribute) {\n    // ... compute $value ...\n    return $value;\n}\n```\n\n> Info: How to determine if a value is empty or not is a separate topic covered\n  in the [Empty Values](input-validation.md#handling-empty-inputs) section. Default value from database\n  schema could be loaded via [loadDefaultValues()](db-active-record.md#default-attribute-values) method of the model.\n\n\n## [[yii\\validators\\NumberValidator|double]] <span id=\"double\"></span>\n\n```php\n[\n    // checks if \"salary\" is a double number\n    ['salary', 'double'],\n]\n```\n\nThis validator checks if the input value is a double number. It is equivalent to the [number](#number) validator.\n\n- `max`: the upper limit (inclusive) of the value. If not set, it means the validator does not check the upper limit.\n- `min`: the lower limit (inclusive) of the value. If not set, it means the validator does not check the lower limit.\n\n\n## [[yii\\validators\\EachValidator|each]] <span id=\"each\"></span>\n\n> Info: This validator has been available since version 2.0.4.\n\n```php\n[\n    // checks if every category ID is an integer\n    ['categoryIDs', 'each', 'rule' => ['integer']],\n]\n```\n\nThis validator only works with an array attribute. It validates if *every* element of the array can be successfully\nvalidated by a specified validation rule. In the above example, the `categoryIDs` attribute must take an array value\nand each array element will be validated by the `integer` validation rule.\n\n- `rule`: an array specifying a validation rule. The first element in the array specifies the class name or\n  the alias of the validator. The rest of the name-value pairs in the array are used to configure the validator object.\n- `allowMessageFromRule`: whether to use the error message returned by the embedded validation rule. Defaults to `true`.\n  If `false`, it will use `message` as the error message.\n\n> Note: If the attribute value is not an array, it is considered validation fails and the `message` will be returned\n  as the error message.\n\n\n## [[yii\\validators\\EmailValidator|email]] <span id=\"email\"></span>\n\n```php\n[\n    // checks if \"email\" is a valid email address\n    ['email', 'email'],\n]\n```\n\nThis validator checks if the input value is a valid email address.\n\n- `allowName`: whether to allow name in the email address (e.g. `John Smith <john.smith@example.com>`). Defaults to `false`.\n- `checkDNS`, whether to check whether the email's domain exists and has either an A or MX record.\n  Be aware that this check may fail due to temporary DNS problems, even if the email address is actually valid.\n  Defaults to `false`.\n- `enableIDN`, whether the validation process should take into account IDN (internationalized domain names).\n  Defaults to `false`. Note that in order to use IDN validation you have to install and enable the `intl` PHP extension,\n  or an exception would be thrown.\n\n\n## [[yii\\validators\\ExistValidator|exist]] <span id=\"exist\"></span>\n\n```php\n[\n    // a1 needs to exist in the column represented by the \"a1\" attribute\n    // i.e. a1 = 1, valid if there is value 1 in column \"a1\"\n    ['a1', 'exist'],\n    // equivalent of\n    ['a1', 'exist', 'targetAttribute' => 'a1'],\n    ['a1', 'exist', 'targetAttribute' => ['a1' => 'a1']],\n\n    // a1 needs to exist, but its value will use a2 to check for the existence\n    // i.e. a1 = 2, valid if there is value 2 in column \"a2\"\n    ['a1', 'exist', 'targetAttribute' => 'a2'],\n    // equivalent of\n    ['a1', 'exist', 'targetAttribute' => ['a1' => 'a2']],\n    \n    // a2 needs to exist, its value will use a2 to check for the existence, a1 will receive error message\n    // i.e. a2 = 2, valid if there is value 2 in column \"a2\"\n    ['a1', 'exist', 'targetAttribute' => ['a2']],\n    // equivalent of\n    ['a1', 'exist', 'targetAttribute' => ['a2' => 'a2']],\n\n    // a1 and a2 need to exist together, and the first attribute without errors will receive error message\n    // i.e. a1 = 3, a2 = 4, valid if there is value 3 in column \"a1\" and value 4 in column \"a2\"\n    [['a1', 'a2'], 'exist', 'targetAttribute' => ['a1', 'a2']],\n    // equivalent of\n    [['a1', 'a2'], 'exist', 'targetAttribute' => ['a1' => 'a1', 'a2' => 'a2']],\n\n    // a1 and a2 need to exist together, only a1 will receive error message\n    // i.e. a1 = 5, a2 = 6, valid if there is value 5 in column \"a1\" and value 6 in column \"a2\"\n    ['a1', 'exist', 'targetAttribute' => ['a1', 'a2']],\n    // equivalent of\n    ['a1', 'exist', 'targetAttribute' => ['a1' => 'a1', 'a2' => 'a2']],\n\n    // a1 needs to exist by checking the existence of both a2 and a3 (using a1 value)\n    // i.e. a1 = 7, a2 = 8, valid if there is value 7 in column \"a3\" and value 8 in column \"a2\"\n    ['a1', 'exist', 'targetAttribute' => ['a2', 'a1' => 'a3']],\n    // equivalent of\n    ['a1', 'exist', 'targetAttribute' => ['a2' => 'a2', 'a1' => 'a3']],\n\n    // a1 needs to exist. If a1 is an array, then every element of it must exist.\n    // i.e. a1 = 9, valid if there is value 9 in column \"a1\"\n    //      a1 = [9, 10], valid if there are values 9 and 10 in column \"a1\"\n    ['a1', 'exist', 'allowArray' => true],\n    \n    // type_id needs to exist in the column \"id\" in the table defined in ProductType class\n    // i.e. type_id = 1, valid if there is value 1 in column \"id\" of ProductType's table\n    ['type_id', 'exist', 'targetClass' => ProductType::class, 'targetAttribute' => ['type_id' => 'id']],    \n    \n    // the same as the previous, but using already defined relation \"type\"\n    ['type_id', 'exist', 'targetRelation' => 'type'],\n]\n```\n\nThis validator checks if the input value can be found in a table column represented by\nan [Active Record](db-active-record.md) attribute. You can use `targetAttribute` to specify the\n[Active Record](db-active-record.md) attribute and `targetClass` the corresponding [Active Record](db-active-record.md)\nclass. If you do not specify them, they will take the values of the attribute and the model class being validated.\n\nYou can use this validator to validate against a single column or multiple columns (i.e., the combination of\nmultiple attribute values should exist). In case of validation fail on the multiple columns checked at the same time \n(like `['a1', 'a2']` in the examples) and `skipOnError` set to `true`, only the first attribute without any previous \nerrors will receive a new error message.\n\n- `targetClass`: the name of the [Active Record](db-active-record.md) class that should be used\n  to look for the input value being validated. If not set, the class of the model currently being validated will be used.\n- `targetAttribute`: the name of the attribute in `targetClass` that should be used to validate the existence\n  of the input value. If not set, it will use the name of the attribute currently being validated.\n  You may use an array to validate the existence of multiple columns at the same time. The array values\n  are the attributes that will be used to validate the existence, while the array keys are the attributes\n  whose values are to be validated. If the key, and the value are the same, you can just specify the value.  \n  Assuming we have ModelA to be validated and ModelB set as the target class the following `targetAttribute`'s \n  configurations are taken as:\n    - `null` => value of a currently validated attribute of ModelA will be checked against stored values of ModelB's attribute with the same name\n    - `'a'` => value of a currently validated attribute of ModelA will be checked against stored values of attribute \"a\" of ModelB\n    - `['a']` => value of attribute \"a\" of ModelA will be checked against stored values of attribute \"a\" of ModelB\n    - `['a' => 'a']` => the same as above\n    - `['a', 'b']` => value of attribute \"a\" of ModelA will be checked against stored values of attribute \"a\" of ModelB and \n      at the same time value of attribute \"b\" of ModelA will be checked against stored values of attribute \"b\" of ModelB\n    - `['a' => 'b']` => value of attribute \"a\" of ModelA will be checked against stored values of attribute \"b\" of ModelB\n- `targetRelation`: since version 2.0.14 you can use convenient attribute `targetRelation`, which overrides the `targetClass` and `targetAttribute` attributes using specs from the requested relation.  \n- `filter`: an additional filter to be applied to the DB query used to check the existence of the input value.\n  This can be a string, or an array representing the additional query condition (refer to [[yii\\db\\Query::where()]]\n  on the format of query condition), or an anonymous function with the signature `function ($query)`, where `$query`\n  is the [[yii\\db\\Query|Query]] object that you can modify in the function.\n- `allowArray`: whether to allow the input value to be an array. Defaults to `false`. If this property is `true`\n  and the input is an array, then every element of the array must exist in the target column. Note that\n  this property cannot be set `true` if you are validating against multiple columns by setting `targetAttribute` as an array.\n\n\n## [[yii\\validators\\FileValidator|file]] <span id=\"file\"></span>\n\n```php\n[\n    // checks if \"primaryImage\" is an uploaded image file in PNG, JPG or GIF format.\n    // the file size must be less than 1MB\n    ['primaryImage', 'file', 'extensions' => ['png', 'jpg', 'gif'], 'maxSize' => 1024*1024],\n]\n```\n\nThis validator checks if the input is a valid uploaded file.\n\n- `extensions`: a list of file name extensions that are allowed to be uploaded. This can be either\n  an array or a string consisting of file extension names separated by space or comma (e.g. \"gif, jpg\").\n  Extension names are case-insensitive. Defaults to `null`, meaning all file name\n  extensions are allowed.\n- `mimeTypes`: a list of file MIME types that are allowed to be uploaded. This can be either an array\n  or a string consisting of file MIME types separated by space or comma (e.g. \"image/jpeg, image/png\").\n  The wildcard mask with the special character `*` can be used to match groups of mime types.\n  For example `image/*` will pass all mime types, that begin with `image/` (e.g. `image/jpeg`, `image/png`).\n  Mime type names are case-insensitive. Defaults to `null`, meaning all MIME types are allowed.\n  For more details, please refer to [common media types](https://en.wikipedia.org/wiki/Media_type).\n- `minSize`: the minimum number of bytes required for the uploaded file. Defaults to `null`, meaning no lower limit.\n- `maxSize`: the maximum number of bytes allowed for the uploaded file. Defaults to `null`, meaning no upper limit.\n- `maxFiles`: the maximum number of files that the given attribute can hold. Defaults to 1, meaning\n  the input must be a single uploaded file. If it is greater than 1, then the input must be an array\n  consisting of at most `maxFiles` number of uploaded files.\n- `checkExtensionByMimeType`: whether to check the file extension by the file's MIME type. If the extension produced by\n  MIME type check differs from the uploaded file extension, the file will be considered as invalid. Defaults to `true`,\n  meaning perform such check.\n\n`FileValidator` is used together with [[yii\\web\\UploadedFile]]. Please refer to the [Uploading Files](input-file-upload.md)\nsection for complete coverage about uploading files and performing validation about the uploaded files.\n\n\n## [[yii\\validators\\FilterValidator|filter]] <span id=\"filter\"></span>\n\n```php\n[\n    // trim \"username\" and \"email\" inputs\n    [['username', 'email'], 'filter', 'filter' => 'trim', 'skipOnArray' => true],\n\n    // normalize \"phone\" input\n    ['phone', 'filter', 'filter' => function ($value) {\n        // normalize phone input here\n        return $value;\n    }],\n    \n    // normalize \"phone\" using the function \"normalizePhone\"\n    ['phone', 'filter', 'filter' => [$this, 'normalizePhone']],\n    \n    public function normalizePhone($value) {\n        return $value;\n    }\n]\n```\n\nThis validator does not validate data. Instead, it applies a filter on the input value and assigns it\nback to the attribute being validated.\n\n- `filter`: a PHP callback that defines a filter. This can be a global function name, an anonymous function, etc.\n  The function signature must be `function ($value) { return $newValue; }`. This property must be set.\n- `skipOnArray`: whether to skip the filter if the input value is an array. Defaults to `false`.\n  Note that if the filter cannot handle array input, you should set this property to be `true`. Otherwise, some\n  PHP error might occur.\n\n> Tip: If you want to trim input values, you may directly use the [trim](#trim) validator.\n\n> Tip: There are many PHP functions that have the signature expected for the `filter` callback.\n> For example to apply type casting (using e.g. [intval](https://www.php.net/manual/en/function.intval.php),\n> [boolval](https://www.php.net/manual/en/function.boolval.php), ...) to ensure a specific type for an attribute,\n> you can simply specify the function names of the filter without the need to wrap them in a closure:\n>\n> ```php\n> ['property', 'filter', 'filter' => 'boolval'],\n> ['property', 'filter', 'filter' => 'intval'],\n> ```\n\n\n## [[yii\\validators\\ImageValidator|image]] <span id=\"image\"></span>\n\n```php\n[\n    // checks if \"primaryImage\" is a valid image with proper size\n    ['primaryImage', 'image', 'extensions' => 'png, jpg',\n        'minWidth' => 100, 'maxWidth' => 1000,\n        'minHeight' => 100, 'maxHeight' => 1000,\n    ],\n]\n```\n\nThis validator checks if the input value represents a valid image file. It extends from the [file](#file) validator\nand thus inherits all its properties. Besides, it supports the following additional properties specific for image\nvalidation purpose:\n\n- `minWidth`: the minimum width of the image. Defaults to `null`, meaning no lower limit.\n- `maxWidth`: the maximum width of the image. Defaults to `null`, meaning no upper limit.\n- `minHeight`: the minimum height of the image. Defaults to `null`, meaning no lower limit.\n- `maxHeight`: the maximum height of the image. Defaults to `null`, meaning no upper limit.\n\n## [[yii\\validators\\IpValidator|ip]] <span id=\"ip\"></span>\n```php\n[\n    // checks if \"ip_address\" is a valid IPv4 or IPv6 address\n    ['ip_address', 'ip'],\n\n    // checks if \"ip_address\" is a valid IPv6 address or subnet,\n    // value will be expanded to full IPv6 notation.\n    ['ip_address', 'ip', 'ipv4' => false, 'subnet' => null, 'expandIPv6' => true],\n\n    // checks if \"ip_address\" is a valid IPv4 or IPv6 address,\n    // allows negation character `!` at the beginning\n    ['ip_address', 'ip', 'negation' => true],\n]\n```\n\nThe validator checks if the attribute value is a valid IPv4/IPv6 address or subnet.\nIt also may change attribute's value if normalization or IPv6 expansion is enabled.\n\nThe validator has such configuration options:\n\n- `ipv4`: whether the validating value can be an IPv4 address. Defaults to `true`.\n- `ipv6`: whether the validating value can be an IPv6 address. Defaults to `true`.\n- `subnet`: whether the address can be an IP with CIDR subnet, like `192.168.10.0/24`\n     * `true` - the subnet is required, addresses without CIDR will be rejected\n     * `false` - the address can not have the CIDR\n     * `null` - the CIDR is optional\n\n    Defaults to `false`.\n- `normalize`: whether to add the CIDR prefix with the smallest length (32 for IPv4 and 128 for IPv6) to an\naddress without it. Works only when `subnet` is not `false`. For example:\n    * `10.0.1.5` will normalized to `10.0.1.5/32`\n    * `2008:db0::1` will be normalized to `2008:db0::1/128`\n\n    Defaults to `false`.\n- `negation`: whether the validation address can have a negation character `!` at the beginning. Defaults to `false`.\n- `expandIPv6`: whether to expand an IPv6 address to the full notation format.\nFor example, `2008:db0::1` will be expanded to `2008:0db0:0000:0000:0000:0000:0000:0001`. Defaults to `false`.\n- `ranges`: array of IPv4 or IPv6 ranges that are allowed or forbidden.\n\n    When the array is empty, or the option is not set, all the IP addresses are allowed.\n    Otherwise, the rules are checked sequentially until the first match is found.\n    IP address is forbidden, when it has not matched any of the rules.\n    \n    For example:\n    ```php\n    [\n         'client_ip', 'ip', 'ranges' => [\n             '192.168.10.128'\n             '!192.168.10.0/24',\n             'any' // allows any other IP addresses\n         ]\n    ]\n    ```\nIn this example, access is allowed for all the IPv4 and IPv6 addresses excluding `192.168.10.0/24` subnet.\nIPv4 address `192.168.10.128` is also allowed, because it is listed before the restriction.\n- `networks`: array of network aliases, that can be used in `ranges`. Format of array:\n    * key - alias name\n    * value - array of strings. String can be a range, IP address or another alias. String can be\n    negated with `!` (independent of `negation` option).\n\n    The following aliases are defined by default:\n    \n    * `*`: `any`\n    * `any`: `0.0.0.0/0, ::/0`\n    * `private`: `10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fd00::/8`\n    * `multicast`: `224.0.0.0/4, ff00::/8`\n    * `linklocal`: `169.254.0.0/16, fe80::/10`\n    * `localhost`: `127.0.0.0/8', ::1`\n    * `documentation`: `192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24, 2001:db8::/32`\n    * `system`: `multicast, linklocal, localhost, documentation`\n\n> Info: This validator has been available since version 2.0.7.\n\n## [[yii\\validators\\RangeValidator|in]] <span id=\"in\"></span>\n\n```php\n[\n    // checks if \"level\" is 1, 2 or 3\n    ['level', 'in', 'range' => [1, 2, 3]],\n]\n```\n\nThis validator checks if the input value can be found among the given list of values.\n\n- `range`: a list of given values within which the input value should be looked for.\n- `strict`: whether the comparison between the input value and the given values should be strict\n  (both the type and value must be the same). Defaults to `false`.\n- `not`: whether the validation result should be inverted. Defaults to `false`. When this property is set `true`,\n  the validator checks if the input value is NOT among the given list of values.\n- `allowArray`: whether to allow the input value to be an array. When this is `true` and the input value is an array,\n  every element in the array must be found in the given list of values, or the validation would fail.\n\n\n## [[yii\\validators\\NumberValidator|integer]] <span id=\"integer\"></span>\n\n```php\n[\n    // checks if \"age\" is an integer\n    ['age', 'integer'],\n]\n```\n\nThis validator checks if the input value is an integer.\n\n- `max`: the upper limit (inclusive) of the value. If not set, it means the validator does not check the upper limit.\n- `min`: the lower limit (inclusive) of the value. If not set, it means the validator does not check the lower limit.\n\n\n## [[yii\\validators\\RegularExpressionValidator|match]] <span id=\"match\"></span>\n\n```php\n[\n    // checks if \"username\" starts with a letter and contains only word characters\n    ['username', 'match', 'pattern' => '/^[a-z]\\w*$/i']\n]\n```\n\nThis validator checks if the input value matches the specified regular expression.\n\n- `pattern`: the regular expression that the input value should match. This property must be set,\n  or an exception will be thrown.\n- `not`: whether to invert the validation result. Defaults to `false`, meaning the validation succeeds\n   only if the input value matches the pattern. If this is set `true`, the validation is considered\n   successful only if the input value does NOT match the pattern.\n\n\n## [[yii\\validators\\NumberValidator|number]] <span id=\"number\"></span>\n\n```php\n[\n    // checks if \"salary\" is a number\n    ['salary', 'number'],\n]\n```\n\nThis validator checks if the input value is a number. It is equivalent to the [double](#double) validator.\n\n- `max`: the upper limit (inclusive) of the value. If not set, it means the validator does not check the upper limit.\n- `min`: the lower limit (inclusive) of the value. If not set, it means the validator does not check the lower limit.\n\n\n## [[yii\\validators\\RequiredValidator|required]] <span id=\"required\"></span>\n\n```php\n[\n    // checks if both \"username\" and \"password\" are not empty\n    [['username', 'password'], 'required'],\n]\n```\n\nThis validator checks if the input value is provided and not empty.\n\n- `requiredValue`: the desired value that the input should be. If not set, it means the input should not be empty.\n- `strict`: whether to check data types when validating a value. Defaults to `false`.\n  When `requiredValue` is not set, if this property is `true`, the validator will check if the input value is\n  not strictly `null`; If this property is `false`, the validator will use a loose rule to determine a value is empty or not.\n  When `requiredValue` is set, the comparison between the input and `requiredValue` will also check data types\n  if this property is `true`.\n\n> Info: How to determine if a value is empty or not is a separate topic covered\n  in the [Empty Values](input-validation.md#handling-empty-inputs) section.\n\n\n## [[yii\\validators\\SafeValidator|safe]] <span id=\"safe\"></span>\n\n```php\n[\n    // marks \"description\" to be a safe attribute\n    ['description', 'safe'],\n]\n```\n\nThis validator does not perform data validation. Instead, it is used to mark an attribute to be\na [safe attribute](structure-models.md#safe-attributes).\n\n\n## [[yii\\validators\\StringValidator|string]] <span id=\"string\"></span>\n\n```php\n[\n    // checks if \"username\" is a string whose length is between 4 and 24\n    ['username', 'string', 'length' => [4, 24]],\n]\n```\n\nThis validator checks if the input value is a valid string with certain length.\n\n- `length`: specifies the length limit of the input string being validated. This can be specified\n   in one of the following forms:\n     * an integer: the exact length that the string should be of;\n     * an array of one element: the minimum length of the input string (e.g. `[8]`). This will overwrite `min`.\n     * an array of two elements: the minimum and maximum lengths of the input string (e.g. `[8, 128]`).\n     This will overwrite both `min` and `max`.\n- `min`: the minimum length of the input string. If not set, it means no minimum length limit.\n- `max`: the maximum length of the input string. If not set, it means no maximum length limit.\n- `encoding`: the encoding of the input string to be validated. If not set, it will use the application's\n  [[yii\\base\\Application::charset|charset]] value which defaults to `UTF-8`.\n\n\n## [[yii\\validators\\FilterValidator|trim]] <span id=\"trim\"></span>\n\n```php\n[\n    // trims the white spaces surrounding \"username\" and \"email\"\n    [['username', 'email'], 'trim'],\n]\n```\n\nThis validator does not perform data validation. Instead, it will trim the surrounding white spaces around\nthe input value. Note that if the input value is an array, it will be ignored by this validator.\n\n\n## [[yii\\validators\\UniqueValidator|unique]] <span id=\"unique\"></span>\n\n```php\n[\n    // a1 needs to be unique in the column represented by the \"a1\" attribute\n    // i.e. a1 = 1, valid if there is no value 1 in column \"a1\"\n    ['a1', 'unique'],\n    // equivalent of\n    ['a1', 'unique', 'targetAttribute' => 'a1'],\n    ['a1', 'unique', 'targetAttribute' => ['a1' => 'a1']],\n\n    // a1 needs to be unique, but column a2 will be used to check the uniqueness of the a1 value\n    // i.e. a1 = 2, valid if there is no value 2 in column \"a2\"\n    ['a1', 'unique', 'targetAttribute' => 'a2'],\n    // equivalent of\n    ['a1', 'unique', 'targetAttribute' => ['a1' => 'a2']],\n\n    // a1 and a2 need to be unique together, and the first attribute without errors will receive error message\n    // i.e. a1 = 3, a2 = 4, valid if there is no value 3 in column \"a1\" and at the same time no value 4 in column \"a2\"\n    [['a1', 'a2'], 'unique', 'targetAttribute' => ['a1', 'a2']],\n    // equivalent of\n    [['a1', 'a2'], 'unique', 'targetAttribute' => ['a1' => 'a1', 'a2' => 'a2']],\n\n    // a1 and a2 need to be unique together, only a1 will receive error message\n    ['a1', 'unique', 'targetAttribute' => ['a1', 'a2']],\n\n    // a1 needs to be unique by checking the uniqueness of both a2 and a3 (using a1 value)\n    // i.e. a1 = 5, a2 = 6, valid if there is no value 5 in column \"a3\" and at the same time no value 6 in column \"a2\"\n    ['a1', 'unique', 'targetAttribute' => ['a2', 'a1' => 'a3']],\n    \n    // type_id needs to be unique in the column \"id\" in the table defined in ProductType class\n    // i.e. type_id = 1, valid if there is no value 1 in column \"id\" of ProductType's table\n    ['type_id', 'unique', 'targetClass' => ProductType::class, 'targetAttribute' => 'id'],    \n]\n```\n\nThis validator checks if the input value is unique in a table column. It only works with [Active Record](db-active-record.md) \nmodel attributes. It supports validation against either a single column or multiple columns. In case of validation fail \non the multiple columns checked at the same time (like `['a1', 'a2']` in the examples) and `skipOnError` set to `true`, \nonly the first attribute without any previous errors will receive a new error message.\n\n- `targetClass`: the name of the [Active Record](db-active-record.md) class that should be used\n  to look for the input value being validated. If not set, the class of the model currently being validated will be used.\n- `targetAttribute`: the name of the attribute in `targetClass` that should be used to validate the uniqueness\n  of the input value. If not set, it will use the name of the attribute currently being validated.\n  You may use an array to validate the uniqueness of multiple columns at the same time. The array values\n  are the attributes that will be used to validate the uniqueness, while the array keys are the attributes\n  whose values are to be validated. If the key, and the value are the same, you can just specify the value.  \n  Assuming we have ModelA to be validated and ModelB set as the target class the following `targetAttribute`'s\n  configurations are taken as:\n    - `null` => value of a currently validated attribute of ModelA will be checked against stored values of ModelB's attribute with the same name\n    - `'a'` => value of a currently validated attribute of ModelA will be checked against stored values of attribute \"a\" of ModelB\n    - `['a']` => value of attribute \"a\" of ModelA will be checked against stored values of attribute \"a\" of ModelB\n    - `['a' => 'a']` => the same as above\n    - `['a', 'b']` => value of attribute \"a\" of ModelA will be checked against stored values of attribute \"a\" of ModelB and\n      at the same time value of attribute \"b\" of ModelA will be checked against stored values of attribute \"b\" of ModelB\n    - `['a' => 'b']` => value of attribute \"a\" of ModelA will be checked against stored values of attribute \"b\" of ModelB\n- `filter`: an additional filter to be applied to the DB query used to check the uniqueness of the input value.\n  This can be a string, or an array representing the additional query condition (refer to [[yii\\db\\Query::where()]]\n  on the format of query condition), or an anonymous function with the signature `function ($query)`, where `$query`\n  is the [[yii\\db\\Query|Query]] object that you can modify in the function.\n\n\n## [[yii\\validators\\UrlValidator|url]] <span id=\"url\"></span>\n\n```php\n[\n    // checks if \"website\" is a valid URL. Prepend \"http://\" to the \"website\" attribute\n    // if it does not have a URI scheme\n    ['website', 'url', 'defaultScheme' => 'http'],\n]\n```\n\nThis validator checks if the input value is a valid URL.\n\n- `validSchemes`: an array specifying the URI schemes that should be considered valid. Defaults to `['http', 'https']`,\n  meaning both `http` and `https` URLs are considered to be valid.\n- `defaultScheme`: the default URI scheme to be prepended to the input if it does not have the scheme part.\n  Defaults to `null`, meaning do not modify the input value.\n- `enableIDN`: whether the validator should take into account IDN (internationalized domain names).\n  Defaults to `false`. Note that in order to use IDN validation you have to install and enable the `intl` PHP\n  extension, otherwise an exception would be thrown.\n\n> Note: The validator checks that URL scheme and host part is correct. It does NOT check the remaining parts of a URL\nand is NOT designed to protect against XSS or any other attacks. See [Security best practices](security-best-practices.md)\narticle to learn more about threats prevention when developing applications.\n"
  },
  {
    "path": "docs/guide/tutorial-docker.md",
    "content": "Yii and Docker\n==============\n\nFor development and deployments Yii applications can be run as Docker containers. A container is like a lightweight isolated virtual machine that maps its services to host's ports, i.e. a webserver in a container on port 80 is available on port 8888 on your (local)host. \n\nContainers can solve many issues such as having identical software versions at developer's computer and the server, fast deployments or simulating multi-server architecture while developing.\n\nYou can read more about Docker containers on [docker.com](https://www.docker.com/why-docker).\n\n## Requirements\n\n- `docker`\n- `docker-compose`\n\nVisit the [download page](https://www.docker.com/products/container-runtime) to get the Docker tooling.\n\n## Installation\n\nAfter installation, you should be able to run `docker ps` and see an output similar to\n\n```\nCONTAINER ID   IMAGE   COMMAND   CREATED   STATUS   PORTS\n```\n\nThis means your Docker daemon is up and running.\n\nAdditionally, run `docker-compose version`, your output should look like this\n\n```\ndocker-compose version 1.20.0, build unknown\ndocker-py version: 3.1.3\nCPython version: 3.6.4\nOpenSSL version: OpenSSL 1.1.0g  2 Nov 2017\n```\n\nWith Compose you can configure manage all services required for your application, such as databases and caching.\n\n## Resources\n\n- PHP-base images for Yii can be found at [yii2-docker](https://github.com/yiisoft/yii2-docker)\n- Docker support for [yii2-app-basic](https://github.com/yiisoft/yii2-app-basic#install-with-docker)\n- Docker support for [yii2-app-advanced](https://github.com/yiisoft/yii2-app-advanced/pull/347) is in development\n\n## Usage\n\nBasic commands for Docker are\n\n    docker-compose up -d\n    \nto start all services in your stack, in the background\n\n    docker-compose ps\n    \nto list running services\n\n    docker-compose logs -f\n    \nto view logs for all services, continuously\n\n    docker-compose stop\n    \nto stop all services in your stack, gracefully\n\n    docker-compose kill\n    \nto stop all services in your stack, immediately\n\n    docker-compose down -v\n    \nto stop and remove all services, **be aware of data loss when not using host-volumes**\n\nTo run commands in a container\n\n    docker-compose run --rm php composer install\n    \nruns composer installation in a new container\n\n    docker-compose exec php bash\n    \nexecutes a bash in a *running* `php` service\n\n\n## Advanced topics\n\n### Yii framework tests\n\nYou can run the dockerized framework tests for Yii itself as described [here](https://github.com/yiisoft/yii2/blob/master/tests/README.md#dockerized-testing).\n\n### Database administration tools\n\nWhen running MySQL as (`mysql`), you can add phpMyAdmin container to your stack like the following:\n\n```\n    phpmyadmin:\n        image: phpmyadmin/phpmyadmin\n        ports:\n            - '8888:80'\n        environment:\n            - PMA_ARBITRARY=1\n            - PMA_HOST=mysql\n        depends_on:\n            - mysql\n```\n"
  },
  {
    "path": "docs/guide/tutorial-i18n.md",
    "content": "Internationalization\n====================\n\nInternationalization (I18N) refers to the process of designing a software application so that it can be adapted to\nvarious languages and regions without engineering changes. For Web applications, this is of particular importance\nbecause the potential users may be worldwide. Yii offers a full spectrum of I18N features that support message\ntranslation, view translation, date and number formatting.\n\n\n## Locale and Language <span id=\"locale-language\"></span>\n\n### Locale\n\nLocale is a set of parameters that defines the user's language, country and any special variant preferences\nthat the user wants to see in their user interface. It is usually identified by an ID consisting of a language\nID and a region ID. \n\nFor example, the ID `en-US` stands for the locale of \"English and the United States\".\n\nFor consistency reasons, all locale IDs used in Yii applications should be canonicalized to the format of\n`ll-CC`, where `ll` is a two- or three-letter lowercase language code according to\n[ISO-639](https://www.loc.gov/standards/iso639-2/) and `CC` is a two-letter country code according to\n[ISO-3166](https://en.wikipedia.org/wiki/ISO_3166-1#Current_codes).\nMore details about locale can be found in the\n[documentation of the ICU project](https://unicode-org.github.io/icu/userguide/locale/#the-locale-concept).\n\n### Language\n\nIn Yii, we often use the term \"language\" to refer to a locale.\n\nA Yii application uses two kinds of languages: \n* [[yii\\base\\Application::$sourceLanguage|source language]]: This refers to the language in which the text messages in the source code are written.\n* [[yii\\base\\Application::$language|target language]]: This is the language that should be used to display content to end users.\n\nThe so-called message translation service mainly translates a text message from source language to target language.\n\n### Configuration\nYou can configure application languages in the \"application configuration\" like the following:\n\n```php\nreturn [\n    // set target language to be Russian\n    'language' => 'ru-RU',\n    \n    // set source language to be English\n    'sourceLanguage' => 'en-US',\n    \n    ......\n];\n```\n\nThe default value for the [[yii\\base\\Application::$sourceLanguage|source language]] is `en-US`, meaning\nUS English. It is **recommended** that you keep this default value unchanged. Usually it is much easier\nto find people who can translate from \"English to other languages\" than from \"non-English to non-English\".\n\nYou often need to set the [[yii\\base\\Application::$language|target language]] dynamically based on different \nfactors, such as the language preference of end users. Instead of configuring it in the application configuration,\nyou can use the following statement to change the target language:\n\n```php\n// change target language to Chinese\n\\Yii::$app->language = 'zh-CN';\n```\n\n> Tip: If your source language varies among different parts of your code, you can\n> override the source language for different message sources, which are described in the next section.\n\n## Message Translation <span id=\"message-translation\"></span>\n\n### From source language to target language\nThe message translation service translates a text message from one language (usually the [[yii\\base\\Application::$sourceLanguage|source language]])\nto another (usually the [[yii\\base\\Application::$language|target language]]). \n\nIt does the translation by looking up the message to be translated in a message source which stores the original messages and the translated messages. If the message is found, the corresponding translated message will be returned; otherwise the original message will be \nreturned untranslated.\n\n### How to implement\nTo use the message translation service, you mainly need to do the following work:\n\n1. Wrap every text message that needs to be translated in a call to the [[Yii::t()]] method.\n2. Configure one or multiple message sources in which the message translation service can look for translated messages.\n3. Let the translators translate messages and store them in the message source(s).\n\n\n#### 1. Wrap a text message\nThe method [[Yii::t()]] can be used like the following,\n\n```php\necho \\Yii::t('app', 'This is a string to translate!');\n```\n\nwhere the second parameter refers to the text message to be translated, while the first parameter refers to \nthe name of the category which is used to categorize the message. \n\n#### 2. Configure one or multiple message sources\nThe [[Yii::t()]] method will call the `i18n` [application component](structure-application-components.md) `translate`\nmethod to perform the actual translation work. The component can be configured in the application configuration as follows,\n\n```php\n'components' => [\n    // ...\n    'i18n' => [\n        'translations' => [\n            'app*' => [\n                'class' => 'yii\\i18n\\PhpMessageSource',\n                //'basePath' => '@app/messages',\n                //'sourceLanguage' => 'en-US',\n                'fileMap' => [\n                    'app' => 'app.php',\n                    'app/error' => 'error.php',\n                ],\n            ],\n        ],\n    ],\n],\n```\n\nIn the above code, a message source supported by [[yii\\i18n\\PhpMessageSource]] is being configured. \n\n##### Category wildcards with `*` symbol\n\nThe pattern `app*` indicates that all message categories whose names start with `app` should be translated using this\nmessage source. \n\n#### 3. Let the translators translate messages and store them in the message source(s)\n\nThe [[yii\\i18n\\PhpMessageSource]] class uses PHP files with a simple PHP array to store message translations. \nThese files contain a map of the messages in `source language` to the translation in the `target language`.\n\n> Info: You can automatically generate these PHP files by using the [`message` command](#message-command),\n> which will be introduced later in this chapter.\n\nEach PHP file corresponds to the messages of a single category. By default, the file name should be the same as\nthe category name. Example for `app/messages/nl-NL/main.php:`\n\n```php\n<?php\n\n/**\n* Translation map for nl-NL\n*/\nreturn [\n    'welcome' => 'welkom'\n];\n\n```\n\n\n##### File mapping\n\nYou may configure [[yii\\i18n\\PhpMessageSource::fileMap|fileMap]] to map a category to a PHP file with a different naming approach. \n\nIn the above example, the category `app/error` is mapped to the PHP file `@app/messages/ru-RU/error.php` \n(assuming `ru-RU` is the target language). \nHowever, without this configuration the category would be mapped to `@app/messages/ru-RU/app/error.php` instead.\n\n#####  Other storage types\n\nBesides, storing the messages in PHP files, you may also use the following message sources to store translated messages\nin different storage:\n\n- [[yii\\i18n\\GettextMessageSource]] uses GNU Gettext MO or PO files to maintain translated messages.\n- [[yii\\i18n\\DbMessageSource]] uses a database table to store translated messages.\n\n\n## Message Formatting <span id=\"message-formatting\"></span>\n\nWhen translating a message, you can embed some placeholders and have them replaced by dynamic parameter values.\nYou can even use special placeholder syntax to have the parameter values formatted according to the target language.\nIn this subsection, we will describe different ways of formatting messages.\n\n### Message Parameters <span id=\"message-parameters\"></span>\n\nIn a message to be translated, you can embed one or multiple parameters (also called placeholders) so that they can be\nreplaced by the given values. By giving different sets of values, you can variate the translated message dynamically.\nIn the following example, the placeholder `{username}` in the message `'Hello, {username}!'` will be replaced\nby `'Alexander'` and `'Qiang'`, respectively.\n\n```php\n$username = 'Alexander';\n// display a translated message with username being \"Alexander\"\necho \\Yii::t('app', 'Hello, {username}!', [\n    'username' => $username,\n]);\n\n$username = 'Qiang';\n// display a translated message with username being \"Qiang\"\necho \\Yii::t('app', 'Hello, {username}!', [\n    'username' => $username,\n]);\n```\n\nWhile translating a message containing placeholders, you should leave the placeholders as is. This is because the placeholders\nwill be replaced with the actual values when you call `Yii::t()` to translate a message.\n\nYou can use either *named placeholders* or *positional placeholders*, but not both, in a single message.\n \nThe previous example shows how you can use named placeholders. That is, each placeholder is written in the format of \n`{name}`, and you provide an associative array whose keys are the placeholder names\n(without the curly brackets) and whose values are the corresponding values' placeholder to be replaced with.\n\nPositional placeholders use zero-based integer sequence as names which are replaced by the provided values\naccording to their positions in the call of `Yii::t()`. In the following example, the positional placeholders\n`{0}`, `{1}` and `{2}` will be replaced by the values of `$price`, `$count` and `$subtotal`, respectively.\n\n```php\n$price = 100;\n$count = 2;\n$subtotal = 200;\necho \\Yii::t('app', 'Price: {0}, Count: {1}, Subtotal: {2}', [$price, $count, $subtotal]);\n```\n\nIn case of a single positional parameter its value could be specified without wrapping it into array:\n\n```php\necho \\Yii::t('app', 'Price: {0}', $price);\n```\n\n> Tip: In most cases you should use named placeholders. This is because the names will make the translators\n> understand better the whole messages being translated.\n\n\n### Parameter Formatting <span id=\"parameter-formatting\"></span>\n\nYou can specify additional formatting rules in the placeholders of a message so that the parameter values can be \nformatted properly before they replace the placeholders. In the following example, the price parameter value will be\ntreated as a number and formatted as a currency value:\n\n```php\n$price = 100;\necho \\Yii::t('app', 'Price: {0,number,currency}', $price);\n```\n\n> Note: Parameter formatting requires the installation of the [intl PHP extension](https://www.php.net/manual/en/intro.intl.php).\n\nYou can use either the short form or the full form to specify a placeholder with formatting:\n\n```\nshort form: {name,type}\nfull form: {name,type,style}\n```\n\n> Note: If you need to use special characters such as `{`, `}`, `'`, `#`, wrap them in `'`:\n> \n```php\necho Yii::t('app', \"Example of string with ''-escaped characters'': '{' '}' '{test}' {count,plural,other{''count'' value is # '#{}'}}\", ['count' => 3]);\n```\n\nComplete format is described in the [ICU documentation](https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classMessageFormat.html).\nIn the following we will show some common usages.\n\n\n#### Number <span id=\"number\"></span>\n\nThe parameter value is treated as a number. For example,\n\n```php\n$sum = 42;\necho \\Yii::t('app', 'Balance: {0,number}', $sum);\n```\n\nYou can specify an optional parameter style as `integer`, `currency`, or `percent`:\n\n```php\n$sum = 42;\necho \\Yii::t('app', 'Balance: {0,number,currency}', $sum);\n```\n\nYou can also specify a custom pattern to format the number. For example,\n\n```php\n$sum = 42;\necho \\Yii::t('app', 'Balance: {0,number,,000,000000}', $sum);\n```\n\nCharacters used in the custom format could be found in\n[ICU API reference](https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classDecimalFormat.html) under \"Special Pattern Characters\"\nsection.\n \n \nThe value is always formatted according to the locale you are translating to i.e. you cannot change decimal or thousands\nseparators, currency symbol etc. without changing translation locale. If you need to customize these you can\nuse [[yii\\i18n\\Formatter::asDecimal()]] and [[yii\\i18n\\Formatter::asCurrency()]].\n\n#### Date <span id=\"date\"></span>\n\nThe parameter value should be formatted as a date. For example,\n\n```php\necho \\Yii::t('app', 'Today is {0,date}', time());\n```\n\nYou can specify an optional parameter style as `short`, `medium`, `long`, or `full`:\n\n```php\necho \\Yii::t('app', 'Today is {0,date,short}', time());\n```\n\nYou can also specify a custom pattern to format the date value:\n\n```php\necho \\Yii::t('app', 'Today is {0,date,yyyy-MM-dd}', time());\n```\n\n[Formatting reference](https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1SimpleDateFormat.html#details).\n\n\n#### Time <span id=\"time\"></span>\n\nThe parameter value should be formatted as a time. For example,\n\n```php\necho \\Yii::t('app', 'It is {0,time}', time());\n```\n\nYou can specify an optional parameter style as `short`, `medium`, `long`, or `full`:\n\n```php\necho \\Yii::t('app', 'It is {0,time,short}', time());\n```\n\nYou can also specify a custom pattern to format the time value:\n\n```php\necho \\Yii::t('app', 'It is {0,date,HH:mm}', time());\n```\n\n[Formatting reference](https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1SimpleDateFormat.html#details).\n\n\n#### Spellout <span id=\"spellout\"></span>\n\nThe parameter value should be treated as a number and formatted as a spellout. For example,\n\n```php\n// may produce \"42 is spelled as forty-two\"\necho \\Yii::t('app', '{n,number} is spelled as {n,spellout}', ['n' => 42]);\n```\n\nBy default, the number is spelled out as cardinal. It could be changed:\n\n```php\n// may produce \"I am forty-seventh agent\"\necho \\Yii::t('app', 'I am {n,spellout,%spellout-ordinal} agent', ['n' => 47]);\n```\n\nNote that there should be no space after `spellout,` and before `%`.\n\nTo get a list of options available for locale you're using check \n\"Numbering schemas, Spellout\" at [https://intl.rmcreative.ru/](https://intl.rmcreative.ru/).\n\n#### Ordinal <span id=\"ordinal\"></span>\n\nThe parameter value should be treated as a number and formatted as an ordinal name. For example,\n\n```php\n// may produce \"You are the 42nd visitor here!\"\necho \\Yii::t('app', 'You are the {n,ordinal} visitor here!', ['n' => 42]);\n```\n\nOrdinal supports more ways of formatting for languages such as Spanish:\n\n```php\n// may produce 471ª\necho \\Yii::t('app', '{n,ordinal,%digits-ordinal-feminine}', ['n' => 471]);\n```\n\nNote that there should be no space after `ordinal,` and before `%`.\n\nTo get a list of options available for locale you're using check \n\"Numbering schemas, Ordinal\" at [https://intl.rmcreative.ru/](https://intl.rmcreative.ru/).\n\n#### Duration <span id=\"duration\"></span>\n\nThe parameter value should be treated as the number of seconds and formatted as a time duration string. For example,\n\n```php\n// may produce \"You are here for 47 sec. already!\"\necho \\Yii::t('app', 'You are here for {n,duration} already!', ['n' => 47]);\n```\n\nDuration supports more ways of formatting:\n\n```php\n// may produce 130:53:47\necho \\Yii::t('app', '{n,duration,%in-numerals}', ['n' => 471227]);\n```\n\nNote that there should be no space after `duration,` and before `%`.\n\nTo get a list of options available for locale you're using check \n\"Numbering schemas, Duration\" at [https://intl.rmcreative.ru/](https://intl.rmcreative.ru/).\n\n#### Plural <span id=\"plural\"></span>\n\nDifferent languages have different ways to inflect plurals. Yii provides a convenient way for translating messages in\ndifferent plural forms that works well even for very complex rules. Instead of dealing with the inflection rules directly,\nit is sufficient to provide the translation of inflected words in certain situations only. For example,\n\n```php\n// When $n = 0, it may produce \"There are no cats!\"\n// When $n = 1, it may produce \"There is one cat!\"\n// When $n = 42, it may produce \"There are 42 cats!\"\necho \\Yii::t('app', 'There {n,plural,=0{are no cats} =1{is one cat} other{are # cats}}!', ['n' => $n]);\n```\n\nIn the plural rule arguments above, `=` means explicit value. So `=0` means exactly zero, `=1` means exactly one.\n`other` stands for any other value. `#` is replaced with the value of `n` formatted according to target language.\n\nPlural forms can be very complicated in some languages. In the following Russian example, `=1` matches exactly `n = 1` \nwhile `one` matches `21` or `101`:\n\n```\nЗдесь {n,plural,=0{котов нет} =1{есть один кот} one{# кот} few{# кота} many{# котов} other{# кота}}!\n```\n\nThese `other`, `few`, `many` and other special argument names vary depending on language. To learn which ones you should\nspecify for a particular locale, please refer to \"Plural Rules, Cardinal\" at [https://intl.rmcreative.ru/](https://intl.rmcreative.ru/). \nAlternatively you can refer to [rules reference at unicode.org](https://cldr.unicode.org/index/cldr-spec/plural-rules).\n\n> Note: The above example Russian message is mainly used as a translated message, not an original message, unless you set\n> the [[yii\\base\\Application::$sourceLanguage|source language]] of your application as `ru-RU` and translating from Russian.\n>\n> When a translation is not found for an original message specified in `Yii::t()` call, the plural rules for the\n> [[yii\\base\\Application::$sourceLanguage|source language]] will be applied to the original message.\n\nThere's an `offset` parameter for the cases when the string is like the following:\n \n```php\n$likeCount = 2;\necho Yii::t('app', 'You {likeCount,plural,\n    offset: 1\n    =0{did not like this}\n    =1{liked this}\n    one{and one other person liked this}\n    other{and # others liked this}\n}', [\n    'likeCount' => $likeCount\n]);\n\n// You and one other person liked this\n```\n\n#### Ordinal selection <span id=\"ordinal-selection\"></span>\n\nThe parameter type of `selectordinal` is meant to choose a string based on language rules for ordinals for the\nlocale you are translating to:\n\n```php\n$n = 3;\necho Yii::t('app', 'You are the {n,selectordinal,one{#st} two{#nd} few{#rd} other{#th}} visitor', ['n' => $n]);\n// For English it outputs:\n// You are the 3rd visitor\n\n// Translation\n'You are the {n,selectordinal,one{#st} two{#nd} few{#rd} other{#th}} visitor' => 'Вы {n,selectordinal,other{#-й}} посетитель',\n\n// For Russian translation it outputs:\n// Вы 3-й посетитель\n```\n\nThe format is very close to what's used for plurals. To learn which arguments you should specify for a particular locale,\nplease refer to \"Plural Rules, Ordinal\" at [https://intl.rmcreative.ru/](https://intl.rmcreative.ru/). \nAlternatively you can refer to [rules reference at unicode.org](https://unicode-org.github.io/cldr-staging/charts/37/supplemental/language_plural_rules.html).\n\n#### Selection <span id=\"selection\"></span>\n\nYou can use the `select` parameter type to choose a phrase based on the parameter value. For example, \n\n```php\n// It may produce \"Snoopy is a dog and it loves Yii!\"\necho \\Yii::t('app', '{name} is a {gender} and {gender,select,female{she} male{he} other{it}} loves Yii!', [\n    'name' => 'Snoopy',\n    'gender' => 'dog',\n]);\n```\n\nIn the expression above, both `female` and `male` are possible parameter values, while `other` handles values that \ndo not match either one of them. Following each possible parameter value, you should specify a phrase and enclose\nit in a pair of curly brackets.\n\n\n### Specifying default message source <span id=\"default-message-source\"></span>\n\nYou can specify default message source that will be used as a fallback for category that doesn't match any\nconfigured category. You can do that by configuring a wildcard category `*`. In order to do that, add the following\nto the application config:\n\n```php\n//configure i18n component\n\n'i18n' => [\n    'translations' => [\n        '*' => [\n            'class' => 'yii\\i18n\\PhpMessageSource'\n        ],\n    ],\n],\n```\n\nNow you can use categories without configuring each one, which is similar to Yii 1.1 behavior.\nMessages for the category will be loaded from a file under the default translation `basePath` that is `@app/messages`:\n\n```php\necho Yii::t('not_specified_category', 'message from unspecified category');\n```\n\nThe message will be loaded from `@app/messages/<LanguageCode>/not_specified_category.php`.\n\n### Translating module messages <span id=\"module-translation\"></span>\n\nIf you want to translate the messages for a module and avoid using a single translation file for all the messages, you can do it like the following:\n\n```php\n<?php\n\nnamespace app\\modules\\users;\n\nuse Yii;\n\nclass Module extends \\yii\\base\\Module\n{\n    public $controllerNamespace = 'app\\modules\\users\\controllers';\n\n    public function init()\n    {\n        parent::init();\n        $this->registerTranslations();\n    }\n\n    public function registerTranslations()\n    {\n        Yii::$app->i18n->translations['modules/users/*'] = [\n            'class' => 'yii\\i18n\\PhpMessageSource',\n            'sourceLanguage' => 'en-US',\n            'basePath' => '@app/modules/users/messages',\n            'fileMap' => [\n                'modules/users/validation' => 'validation.php',\n                'modules/users/form' => 'form.php',\n                ...\n            ],\n        ];\n    }\n\n    public static function t($category, $message, $params = [], $language = null)\n    {\n        return Yii::t('modules/users/' . $category, $message, $params, $language);\n    }\n\n}\n```\n\nIn the example above we are using wildcard for matching and then filtering each category per needed file. Instead of using `fileMap`, you can simply\nuse the convention of the category mapping to the same named file.\nNow you can use `Module::t('validation', 'your custom validation message')` or `Module::t('form', 'some form label')` directly.\n\n### Translating widgets messages <span id=\"widget-translation\"></span>\n\nThe same rule as applied for Modules above can be applied for widgets too, for example:\n\n```php\n<?php\n\nnamespace app\\widgets\\menu;\n\nuse yii\\base\\Widget;\nuse Yii;\n\nclass Menu extends Widget\n{\n\n    public function init()\n    {\n        parent::init();\n        $this->registerTranslations();\n    }\n\n    public function registerTranslations()\n    {\n        $i18n = Yii::$app->i18n;\n        $i18n->translations['widgets/menu/*'] = [\n            'class' => 'yii\\i18n\\PhpMessageSource',\n            'sourceLanguage' => 'en-US',\n            'basePath' => '@app/widgets/menu/messages',\n            'fileMap' => [\n                'widgets/menu/messages' => 'messages.php',\n            ],\n        ];\n    }\n\n    public function run()\n    {\n        echo $this->render('index');\n    }\n\n    public static function t($category, $message, $params = [], $language = null)\n    {\n        return Yii::t('widgets/menu/' . $category, $message, $params, $language);\n    }\n\n}\n```\n\nInstead of using `fileMap` you can simply use the convention of the category mapping to the same named file.\nNow you can use `Menu::t('messages', 'new messages {messages}', ['{messages}' => 10])` directly.\n\n> Note: For widgets you also can use i18n views, with the same rules as for controllers being applied to them too.\n\n\n### Translating framework messages <span id=\"framework-translation\"></span>\n\nYii comes with the default translation messages for validation errors and some other strings. These messages are all\nin the category `yii`. Sometimes you want to correct the default framework message translation for your application.\nIn order to do so, configure the `i18n` [application component](structure-application-components.md) like the following:\n\n```php\n'i18n' => [\n    'translations' => [\n        'yii' => [\n            'class' => 'yii\\i18n\\PhpMessageSource',\n            'sourceLanguage' => 'en-US',\n            'basePath' => '@app/messages'\n        ],\n    ],\n],\n```\n\nNow you can place your adjusted translations to `@app/messages/<language>/yii.php`.\n\n### Handling missing translations <span id=\"missing-translations\"></span>\n\nEven if the translation is missing from the source, Yii will display the requested message content. Such behavior is very convenient\nin case your raw message is a valid verbose text. However, sometimes it is not enough.\nYou may need to perform some custom processing of the situation, when the requested translation is missing from the source.\nThis can be achieved using the [[yii\\i18n\\MessageSource::EVENT_MISSING_TRANSLATION|missingTranslation]]-event of [[yii\\i18n\\MessageSource]].\n\nFor example, you may want to mark all the missing translations with something notable, so that they can be easily found at the page.\nFirst you need to setup an event handler. This can be done in the application configuration:\n\n```php\n'components' => [\n    // ...\n    'i18n' => [\n        'translations' => [\n            'app*' => [\n                'class' => 'yii\\i18n\\PhpMessageSource',\n                'fileMap' => [\n                    'app' => 'app.php',\n                    'app/error' => 'error.php',\n                ],\n                'on missingTranslation' => ['app\\components\\TranslationEventHandler', 'handleMissingTranslation']\n            ],\n        ],\n    ],\n],\n```\n\nNow you need to implement your own event handler:\n\n```php\n<?php\n\nnamespace app\\components;\n\nuse yii\\i18n\\MissingTranslationEvent;\n\nclass TranslationEventHandler\n{\n    public static function handleMissingTranslation(MissingTranslationEvent $event)\n    {\n        $event->translatedMessage = \"@MISSING: {$event->category}.{$event->message} FOR LANGUAGE {$event->language} @\";\n    }\n}\n```\n\nIf [[yii\\i18n\\MissingTranslationEvent::translatedMessage]] is set by the event handler it will be displayed as the translation result.\n\n> Note: each message source handles its missing translations separately. If you are using several message sources\n> and wish them to treat the missing translations in the same way, you should assign the corresponding event handler to each of them.\n\n\n### Using the `message` command <span id=\"message-command\"></span>\n\nTranslations can be stored in [[yii\\i18n\\PhpMessageSource|php files]], [[yii\\i18n\\GettextMessageSource|.po files]] or in a [[yii\\i18n\\DbMessageSource|database]]. See specific classes for additional options.\n\nFirst you need to create a configuration file. Decide where you want to store it and then issue the command \n\n```bash\n./yii message/config-template path/to/config.php\n```\n\nOpen the created file and adjust the parameters to fit your needs. Pay special attention to:\n\n* `languages`: an array representing what languages your app should be translated to;\n* `messagePath`: path where to store message files, which should match the `i18n`'s `basePath` parameter stated in config.\n\nYou may also use './yii message/config' command to dynamically generate configuration file with specified options via cli.\nFor example, you can set `languages` and `messagePath` parameters like the following:\n\n```bash\n./yii message/config --languages=de,ja --messagePath=messages path/to/config.php\n```\n\nTo get list of available options execute next command:\n\n```bash\n./yii help message/config\n```\n\nOnce you're done with the configuration file you can finally extract your messages with the command:\n\n```bash\n./yii message path/to/config.php\n```\n\nAlso, you may use options to dynamically change parameters for extraction.\n\nYou will then find your files (if you've chosen file based translations) in your `messagePath` directory.\n\n\n## View Translation <span id=\"view-translation\"></span>\n\nInstead of translating individual text messages, sometimes you may want to translate a whole view script.\nTo achieve this goal, simply translate the view and save it under a subdirectory whose name is the same as \ntarget language. For example, if you want to translate the view script `views/site/index.php` and the target\nlanguage is `ru-RU`, you may translate the view and save it as  the file `views/site/ru-RU/index.php`. Now\nwhenever you call [[yii\\base\\View::renderFile()]] or any method that invoke this method (e.g. [[yii\\base\\Controller::render()]])\nto render the view `views/site/index.php`, it will end up rendering the translated view `views/site/ru-RU/index.php`, instead. \n\n> Note: If the [[yii\\base\\Application::$language|target language]] is the same as [[yii\\base\\Application::$sourceLanguage|source language]]\n> original view will be rendered regardless of presence of translated view.\n\n\n## Formatting Date and Number Values <span id=\"date-number\"></span>\n\nSee the [Data Formatting](output-formatting.md) section for details.\n\n\n## Setting Up PHP Environment <span id=\"setup-environment\"></span>\n\nYii uses the [PHP intl extension](https://www.php.net/manual/en/book.intl.php) to provide most of its I18N features,\nsuch as the date and number formatting of the [[yii\\i18n\\Formatter]] class and the message formatting using [[yii\\i18n\\MessageFormatter]].\nBoth classes provide a fallback mechanism when the `intl` extension is not installed. However, the fallback implementation\nonly works well for English target language. So it is highly recommended that you install `intl` when I18N is needed.\n\nThe [PHP intl extension](https://www.php.net/manual/en/book.intl.php) is based on the [ICU library](https://icu.unicode.org/) which\nprovides the knowledge and formatting rules for all different locales. Different versions of ICU may produce different\nformatting result of date and number values. To ensure your website produces the same results across all environments,\nit is recommended that you install the same version of the `intl` extension (and thus the same version of ICU)\nin all environments.\n\nTo find out which version of ICU is used by PHP, you can run the following script, which will give you the PHP and ICU version being used.\n\n```php\n<?php\necho \"PHP: \" . PHP_VERSION . \"\\n\";\necho \"ICU: \" . INTL_ICU_VERSION . \"\\n\";\necho \"ICU Data: \" . INTL_ICU_DATA_VERSION . \"\\n\";\n```\n\nIt is also recommended that you use an ICU version equal or greater than version 49. This will ensure you can use all the features\ndescribed in this document. For example, an ICU version below 49 does not support using `#` placeholders in plural rules.\nPlease refer to <https://icu.unicode.org/download> for a complete list of available ICU versions. Note that the version \nnumbering has changed after the 4.8 release (e.g., ICU 4.8, ICU 49, ICU 50, etc.)\n\nAdditionally the information in the time zone database shipped with the ICU library may be outdated. Please refer\nto the [ICU manual](https://unicode-org.github.io/icu/userguide/datetime/timezone/#updating-the-time-zone-data) for details\non updating the time zone database. While for output formatting the ICU timezone database is used, the time zone database\nused by PHP may be relevant too. You can update it by installing the latest version of the [pecl package `timezonedb`](https://pecl.php.net/package/timezonedb).\n"
  },
  {
    "path": "docs/guide/tutorial-mailing.md",
    "content": "Mailing\n=======\n\n> Note: This section is under development.\n\nYii supports composition and sending of the email messages. However, the framework core provides\nonly the content composition functionality and basic interface. Actual mail sending mechanism should\nbe provided by the extension, because different projects may require its different implementation and\nit usually depends on the external services and libraries.\n\nFor the most common cases you can use [yii2-symfonymailer](https://www.yiiframework.com/extension/yiisoft/yii2-symfonymailer) official extension.\n\n\nConfiguration\n-------------\n\nMail component configuration depends on the extension you have chosen.\nIn general your application configuration should look like:\n\n```php\nreturn [\n    //....\n    'components' => [\n        'mailer' => [\n            'class' => 'yii\\symfonymailer\\Mailer',\n            'useFileTransport' => false,\n            'transport' => [\n                'dsn' => 'smtp://user:pass@smtp.example.com:465',\n            ],\n        ],\n    ],\n];\n```\n\n\nBasic usage\n-----------\n\nOnce the `mailer` component is configured, you can use the following code to send an email message:\n\n```php\nYii::$app->mailer->compose()\n    ->setFrom('from@domain.com')\n    ->setTo('to@domain.com')\n    ->setSubject('Message subject')\n    ->setTextBody('Plain text content')\n    ->setHtmlBody('<b>HTML content</b>')\n    ->send();\n```\n\nIn the above example the method `compose()` creates an instance of the mail message, which then is populated and sent.\nYou may put more complex logic in this process if needed:\n\n```php\n$message = Yii::$app->mailer->compose();\nif (Yii::$app->user->isGuest) {\n    $message->setFrom('from@domain.com');\n} else {\n    $message->setFrom(Yii::$app->user->identity->email);\n}\n$message->setTo(Yii::$app->params['adminEmail'])\n    ->setSubject('Message subject')\n    ->setTextBody('Plain text content')\n    ->send();\n```\n\n> Note: each `mailer` extension comes in 2 major classes: `Mailer` and `Message`. `Mailer` always knows\n  the class name and specific of the `Message`. Do not attempt to instantiate `Message` object directly —\n  always use `compose()` method for it.\n\nYou may also send several messages at once:\n\n```php\n$messages = [];\nforeach ($users as $user) {\n    $messages[] = Yii::$app->mailer->compose()\n        // ...\n        ->setTo($user->email);\n}\nYii::$app->mailer->sendMultiple($messages);\n```\n\nSome particular mail extensions may benefit from this approach, using single network message etc.\n\n\nComposing mail content\n----------------------\n\nYii allows composition of the actual mail messages content via special view files.\nBy default these files should be located at `@app/mail` path.\n\nExample mail view file content:\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\helpers\\Url;\n\n/**\n * @var \\yii\\web\\View $this view component instance\n * @var \\yii\\mail\\BaseMessage $message instance of newly created mail message\n */\n\n?>\n<h2>This message allows you to visit our site home page by one click</h2>\n<?= Html::a('Go to home page', Url::home('http')) ?>\n```\n\nIn order to compose message content via view file simply pass view name to the `compose()` method:\n\n```php\nYii::$app->mailer->compose('home-link') // a view rendering result becomes the message body here\n    ->setFrom('from@domain.com')\n    ->setTo('to@domain.com')\n    ->setSubject('Message subject')\n    ->send();\n```\n\nYou may pass additional view parameters to `compose()` method, which will be available inside the view files:\n\n```php\nYii::$app->mailer->compose('greetings', [\n    'user' => Yii::$app->user->identity,\n    'advertisement' => $adContent,\n]);\n```\n\nYou can specify different view files for HTML and plain text message contents:\n\n```php\nYii::$app->mailer->compose([\n    'html' => 'contact-html',\n    'text' => 'contact-text',\n]);\n```\n\nIf you specify view name as a scalar string, its rendering result will be used as HTML body, while\nplain text body will be composed by removing all HTML entities from HTML one.\n\nView rendering result can be wrapped into the layout, which can be setup using [[yii\\mail\\BaseMailer::htmlLayout]]\nand [[yii\\mail\\BaseMailer::textLayout]]. It will work the same way like layouts in regular web application.\nLayout can be used to setup mail CSS styles or other shared content:\n\n```php\n<?php\nuse yii\\helpers\\Html;\n\n/**\n * @var \\yii\\web\\View $this view component instance\n * @var \\yii\\mail\\MessageInterface $message the message being composed\n * @var string $content main view render result\n */\n?>\n<?php $this->beginPage() ?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n<head>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=<?= Yii::$app->charset ?>\" />\n    <style type=\"text/css\">\n        .heading {...}\n        .list {...}\n        .footer {...}\n    </style>\n    <?php $this->head() ?>\n</head>\n<body>\n    <?php $this->beginBody() ?>\n    <?= $content ?>\n    <div class=\"footer\">With kind regards, <?= Yii::$app->name ?> team</div>\n    <?php $this->endBody() ?>\n</body>\n</html>\n<?php $this->endPage() ?>\n```\n\n\nFile attachment\n---------------\n\nYou can add attachments to message using methods `attach()` and `attachContent()`:\n\n```php\n$message = Yii::$app->mailer->compose();\n\n// attach file from local file system\n$message->attach('/path/to/source/file.pdf');\n\n// create attachment on-the-fly\n$message->attachContent('Attachment content', ['fileName' => 'attach.txt', 'contentType' => 'text/plain']);\n```\n\n\nEmbedding images\n----------------\n\nYou can embed images into the message content using `embed()` method. This method returns the attachment id,\nwhich should be then used at `img` tag.\nThis method is easy to use when composing message content via view file:\n\n```php\nYii::$app->mailer->compose('embed-email', ['imageFileName' => '/path/to/image.jpg'])\n    // ...\n    ->send();\n```\n\nThen inside the view file you can use the following code:\n\n```php\n<img src=\"<?= $message->embed($imageFileName); ?>\">\n```\n\n\nTesting and debugging\n---------------------\n\nA developer often has to check, what actual emails are sent by the application, what was their content and so on.\nSuch ability is granted by Yii via `yii\\mail\\BaseMailer::useFileTransport`. If enabled, this option enforces\nsaving mail message data into the local files instead of regular sending. These files will be saved under\n`yii\\mail\\BaseMailer::fileTransportPath`, which is `@runtime/mail` by default.\n\n> Note: you can either save the messages to the files or send them to the actual recipients, but can not do both simultaneously.\n\nA mail message file can be opened by a regular text file editor, so you can browse the actual message headers, content and so on.\nThis mechanism may prove itself, while debugging application or running unit test.\n\n> Note: the mail message file content is composed via `\\yii\\mail\\MessageInterface::toString()`, so it depends on the actual\n  mail extension you are using in your application.\n\n\nCreating your own mail solution\n-------------------------------\n\nIn order to create your own custom mail solution, you need to create 2 classes: one for the `Mailer` and\nanother one for the `Message`.\nYou can use `yii\\mail\\BaseMailer` and `yii\\mail\\BaseMessage` as the base classes for your solution. These classes\nalready contain the basic logic, which is described in this guide. However, their usage is not mandatory, it is enough\nto implement `yii\\mail\\MailerInterface` and `yii\\mail\\MessageInterface` interfaces.\nThen you need to implement all the abstract methods to build your solution.\n"
  },
  {
    "path": "docs/guide/tutorial-performance-tuning.md",
    "content": "Performance Tuning\n==================\n\nThere are many factors affecting the performance of your Web application. Some are environmental, some are related \nwith your code, while some others are related with Yii itself. In this section, we will enumerate most of these\nfactors and explain how you can improve your application performance by adjusting these factors.\n\n\n## Optimizing your PHP Environment <span id=\"optimizing-php\"></span>\n\nA well configured PHP environment is very important. In order to get maximum performance,\n\n- Use the latest stable PHP version. Major releases of PHP may bring significant performance improvements.\n- Enable bytecode caching with [Opcache](https://www.php.net/manual/en/book.opcache.php) (PHP 5.5 or later) or [APC](https://www.php.net/manual/en/book.apcu.php) \n  (PHP 5.4). Bytecode caching avoids the time spent in parsing and including PHP scripts for every\n  incoming request.\n- [Tune `realpath()` cache](https://github.com/samdark/realpath_cache_tuner).\n\n\n## Disabling Debug Mode <span id=\"disable-debug\"></span>\n\nWhen running an application in production, you should disable debug mode. Yii uses the value of a constant\nnamed `YII_DEBUG` to indicate whether debug mode should be enabled. When debug mode is enabled, Yii\nwill take extra time to generate and record debugging information.\n\nYou may place the following line of code at the beginning of the [entry script](structure-entry-scripts.md) to\ndisable debug mode:\n\n```php\ndefined('YII_DEBUG') or define('YII_DEBUG', false);\n```\n\n> Info: The default value of `YII_DEBUG` is `false`. So if you are certain that you do not change its default\n  value somewhere else in your application code, you may simply remove the above line to disable debug mode. \n  \n\n## Using Caching Techniques <span id=\"using-caching\"></span>\n\nYou can use various caching techniques to significantly improve the performance of your application. For example,\nif your application allows users to enter text in Markdown format, you may consider caching the parsed Markdown\ncontent to avoid parsing the same Markdown text repeatedly in every request. Please refer to \nthe [Caching](caching-overview.md) section to learn about the caching support provided by Yii.\n\n\n## Enabling Schema Caching <span id=\"enable-schema-caching\"></span>\n\nSchema caching is a special caching feature that should be enabled whenever you are using [Active Record](db-active-record.md).\nAs you know, Active Record is intelligent enough to detect schema information (e.g. column names, column types, constraints)\nabout a DB table without requiring you to manually describe them. Active Record obtains this information by executing \nextra SQL queries. By enabling schema caching, the retrieved schema information will be saved in the cache and reused\nin future requests.\n\nTo enable schema caching, configure a `cache` [application component](structure-application-components.md) to store\nthe schema information and set [[yii\\db\\Connection::enableSchemaCache]] to be `true` in the [application configuration](concept-configurations.md):\n\n```php\nreturn [\n    // ...\n    'components' => [\n        // ...\n        'cache' => [\n            'class' => 'yii\\caching\\FileCache',\n        ],\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=mydatabase',\n            'username' => 'root',\n            'password' => '',\n            'enableSchemaCache' => true,\n\n            // Duration of schema cache.\n            'schemaCacheDuration' => 3600,\n\n            // Name of the cache component used to store schema information\n            'schemaCache' => 'cache',\n        ],\n    ],\n];\n```\n\n\n## Combining and Minimizing Assets <span id=\"optimizing-assets\"></span>\n\nA complex Web page often includes many CSS and/or JavaScript asset files. To reduce the number of HTTP requests \nand the overall download size of these assets, you should consider combining them into one single file and\ncompressing it. This may greatly improve the page loading time and reduce the server load. For more details,\nplease refer to the [Assets](structure-assets.md) section.\n\n\n## Optimizing Session Storage <span id=\"optimizing-session\"></span>\n\nBy default session data are stored in files. The implementation is locking a file from opening a session to the point it's\nclosed either by `session_write_close()` (in Yii it could be done as `Yii::$app->session->close()`) or at the end of request.\nWhile session file is locked all other requests which are trying to use the same session are blocked i.e. waiting for the\ninitial request to release session file. This is fine for development and probably small projects. But when it comes \nto handling massive concurrent requests, it is better to use more sophisticated storage, such as database. Yii supports\na variety of session storage out of box. You can use these storage by configuring the `session` component in the\n[application configuration](concept-configurations.md) like the following,\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'session' => [\n            'class' => 'yii\\web\\DbSession',\n\n            // Set the following if you want to use DB component other than\n            // default 'db'.\n            // 'db' => 'mydb',\n\n            // To override default session table, set the following\n            // 'sessionTable' => 'my_session',\n        ],\n    ],\n];\n```\n\nThe above configuration uses a database table to store session data. By default, it will use the `db` application\ncomponent as the database connection and store the session data in the `session` table. You do have to create the\n`session` table as follows in advance, though,\n\n```sql\nCREATE TABLE session (\n    id CHAR(40) NOT NULL PRIMARY KEY,\n    expire INTEGER,\n    data BLOB\n)\n```\n\nYou may also store session data in a cache by using [[yii\\web\\CacheSession]]. In theory, you can use any supported\n[cache storage](caching-data.md#supported-cache-storage). Note, however, that some cache storage may flush cached data\nwhen the storage limit is reached. For this reason, you should mainly use those cache storage that do not enforce\nstorage limit.\n\nIf you have [Redis](https://redis.io/) on your server, it is highly recommended you use it as session storage by using\n[[yii\\redis\\Session]].\n\n\n## Optimizing Databases <span id=\"optimizing-databases\"></span>\n\nExecuting DB queries and fetching data from databases are often the main performance bottleneck in\na Web application. Although using [data caching](caching-data.md) techniques may alleviate the performance hit,\nit does not fully solve the problem. When the database contains enormous amounts of data and the cached data is invalid, \nfetching the latest data could be prohibitively expensive without proper database and query design.\n\nA general technique to improve the performance of DB queries is to create indices for table columns that\nneed to be filtered by. For example, if you need to look for a user record by `username`, you should create an index\non `username`. Note that while indexing can make SELECT queries much faster, it will slow down INSERT, UPDATE and DELETE queries.\n\nFor complex DB queries, it is recommended that you create database views to save the query parsing and preparation time.\n\nLast but not least, use `LIMIT` in your `SELECT` queries. This avoids fetching an overwhelming amount of data from the database\nand exhausting the memory allocated to PHP.\n\n\n## Using Plain Arrays <span id=\"using-arrays\"></span>\n\nAlthough [Active Record](db-active-record.md) is very convenient to use, it is not as efficient as using plain arrays\nwhen you need to retrieve a large amount of data from database. In this case, you may consider calling `asArray()`\nwhile using Active Record to query data so that the retrieved data is represented as arrays instead of bulky Active\nRecord objects. For example,\n\n```php\nclass PostController extends Controller\n{\n    public function actionIndex()\n    {\n        $posts = Post::find()->limit(100)->asArray()->all();\n        \n        return $this->render('index', ['posts' => $posts]);\n    }\n}\n```\n\nIn the above code, `$posts` will be populated as an array of table rows. Each row is a plain array. To access\nthe `title` column of the i-th row, you may use the expression `$posts[$i]['title']`.\n\nYou may also use [DAO](db-dao.md) to build queries and retrieve data in plain arrays. \n\n\n## Optimizing Composer Autoloader <span id=\"optimizing-autoloader\"></span>\n\nBecause Composer autoloader is used to include most third-party class files, you should consider optimizing it\nby executing the following command:\n\n```\ncomposer dumpautoload -o\n```\n\nAdditionally you may consider using\n[authoritative class maps](https://getcomposer.org/doc/articles/autoloader-optimization.md#optimization-level-2-a-authoritative-class-maps)\nand [APCu cache](https://getcomposer.org/doc/articles/autoloader-optimization.md#optimization-level-2-b-apcu-cache).\nNote that both optimizations may or may not be suitable for your particular case.\n\n\n## Processing Data Offline <span id=\"processing-data-offline\"></span>\n\nWhen a request involves some resource intensive operations, you should think of ways to perform those operations\nin offline mode without having users wait for them to finish.\n\nThere are two methods to process data offline: pull and push. \n\nIn the pull method, whenever a request involves some complex operation, you create a task and save it in a persistent \nstorage, such as database. You then use a separate process (such as a cron job) to pull the tasks and process them.\nThis method is easy to implement, but it has some drawbacks. For example, the task process needs to periodically pull\nfrom the task storage. If the pull frequency is too low, the tasks may be processed with great delay, but if the frequency\nis too high, it will introduce high overhead.\n\nIn the push method, you would use a message queue (e.g. RabbitMQ, ActiveMQ, Amazon SQS, etc.) to manage the tasks. \nWhenever a new task is put on the queue, it will initiate or notify the task handling process to trigger the task processing.\n\n\n## Performance Profiling <span id=\"performance-profiling\"></span>\n\nYou should profile your code to find out the performance bottlenecks and take appropriate measures accordingly.\nThe following profiling tools may be useful:\n\n- [Yii debug toolbar and debugger](https://github.com/yiisoft/yii2-debug/blob/master/docs/guide/README.md)\n- [Blackfire](https://blackfire.io/)\n- [XHProf](https://www.php.net/manual/en/book.xhprof.php)\n- [XDebug profiler](https://xdebug.org/docs/profiler)\n\n## Prepare application for scaling\n\nWhen nothing helps you may try making your application scalable. A good introduction is provided in [Configuring a Yii 2 Application for an Autoscaling Stack](https://github.com/samdark/yii2-cookbook/blob/master/book/scaling.md).\n"
  },
  {
    "path": "docs/guide/tutorial-shared-hosting.md",
    "content": "Shared Hosting Environment\n==========================\n\nShared hosting environments are often quite limited about configuration and directory structure. Still in most cases you\ncan run Yii 2.0 on a shared hosting environment with a few adjustments.\n\n## Deploying a basic project template\n\nSince in a shared hosting environment there's typically only one webroot, use the basic project template if you can.\nRefer to the [Installing Yii chapter](start-installation.md) and install the basic project template locally.\nAfter you have the application working locally, we'll make some adjustments so it can be hosted on your shared hosting\nserver.\n\n### Renaming webroot <span id=\"renaming-webroot\"></span>\n\nConnect to your shared host using FTP or by other means. You will probably see something like the following.\n \n```\nconfig\nlogs\nwww\n```\n\nIn the above, `www` is your webserver webroot directory. It could be named differently. Common names are: `www`,\n`htdocs`, and `public_html`.\n\nThe webroot in our basic project template is named `web`. Before uploading the application to your webserver rename\nyour local webroot to match your server, i.e., from `web` to `www`, `public_html` or whatever the name of your hosting\nwebroot.\n\n### FTP root directory is writeable\n\nIf you can write to the root level directory i.e. where `config`, `logs` and `www` are, then upload `assets`, `commands`\netc. as is to the root level directory.\n\n### Add extras for webserver <span id=\"add-extras-for-webserver\"></span>\n\nIf your webserver is Apache you'll need to add an `.htaccess` file with the following content to `web`\n(or `public_html` or whatever) (where the `index.php` file is located):\n\n```\nOptions +FollowSymLinks\nIndexIgnore */*\n\nRewriteEngine on\n\n# if a directory or a file exists, use it directly\nRewriteCond %{REQUEST_FILENAME} !-f\nRewriteCond %{REQUEST_FILENAME} !-d\n\n# otherwise forward it to index.php\nRewriteRule . index.php\n```\n\nIn case of nginx you should not need any extra config files.\n\n### Check requirements\n\nIn order to run Yii, your webserver must meet its requirements. The very minimum requirement is PHP 5.4. In order to\ncheck the requirements copy `requirements.php` from your root directory into the webroot directory and run it via\nbrowser using `https://example.com/requirements.php` URL. Don't forget to delete the file afterwards.\n\n## Deploying an advanced project template\n\nDeploying an advanced application to shared hosting is a bit trickier than a basic application but it could be achieved.\nFollow instructions described in\n[advanced project template documentation](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/topic-shared-hosting.md).\n"
  },
  {
    "path": "docs/guide/tutorial-start-from-scratch.md",
    "content": "Creating your own Application structure\n=======================================\n\n> Note: This section is under development.\n\nWhile the [basic](https://github.com/yiisoft/yii2-app-basic) and [advanced](https://github.com/yiisoft/yii2-app-advanced)\nproject templates are great for most of your needs, you may want to create your own project template with which\nto start your projects.\n\nProject templates in Yii are simply repositories containing a `composer.json` file, and registered as a Composer package.\nAny repository can be identified as a Composer package, making it installable via `create-project` Composer command.\n\nSince it's a bit too much to start building your entire template from scratch, it is better to use one of the built-in\ntemplates as a base. Let's use the basic template here.\n\nClone the Basic Template\n----------------------------------------\n\nThe first step is to clone the basic Yii template's Git repository:\n\n```bash\ngit clone git@github.com:yiisoft/yii2-app-basic.git\n```\n\nThen wait for the repository to be downloaded to your computer. Since the changes made to the template won't be pushed \nback, you can delete the `.git` directory and all of its contents from the download.\n\nModify the Files\n------------\n\nNext, you'll want to modify the `composer.json` to reflect your template. Change the `name`, `description`, `keywords`, \n`homepage`, `license`, and `support` values to describe your new template. Also adjust the `require`, `require-dev`, \n`suggest`, and other options to match your template's requirements.\n\n> Note: In the `composer.json` file, use the `writable` parameter under `extra` to specify\n> per file permissions to be set after an application is created using the template.\n\nNext, actually modify the structure and contents of the application as you would like the default to be. \nFinally, update the README file to be applicable to your template.\n\nMake a Package\n--------------\n\nWith the template defined, create a Git repository from it, and push your files there. If you're going to open source \nyour template, [Github](https://github.com) is the best place to host it. If you intend to keep your template \nnon-collaborative, any Git repository site will do.\n\nNext, you need to register your package for Composer's sake. For public templates, the package should be registered \nat [Packagist](https://packagist.org/). For private templates, it is a bit more tricky to register the package. For \ninstructions, see the [Composer documentation](https://getcomposer.org/doc/05-repositories.md#hosting-your-own).\n\nUse the Template\n------\n\nThat's all that's required to create a new Yii project template. Now you can create projects using your template:\n\n```\ncomposer create-project --prefer-dist --stability=dev mysoft/yii2-app-coolone new-project\n```\n"
  },
  {
    "path": "docs/guide/tutorial-template-engines.md",
    "content": "Using template engines\n======================\n\nBy default, Yii uses PHP as its template language, but you can configure Yii to support other rendering engines, such as\n[Twig](https://twig.symfony.com/) or [Smarty](https://www.smarty.net/) available as extensions.\n\nThe `view` component is responsible for rendering views. You can add a custom template engine by reconfiguring this\ncomponent's behavior:\n\n```php\n[\n    'components' => [\n        'view' => [\n            'class' => 'yii\\web\\View',\n            'renderers' => [\n                'tpl' => [\n                    'class' => 'yii\\smarty\\ViewRenderer',\n                    //'cachePath' => '@runtime/Smarty/cache',\n                ],\n                'twig' => [\n                    'class' => 'yii\\twig\\ViewRenderer',\n                    'cachePath' => '@runtime/Twig/cache',\n                    // Array of twig options:\n                    'options' => [\n                        'auto_reload' => true,\n                    ],\n                    'globals' => ['html' => '\\yii\\helpers\\Html'],\n                    'uses' => ['yii\\bootstrap'],\n                ],\n                // ...\n            ],\n        ],\n    ],\n]\n```\n\nIn the code above, both Smarty and Twig are configured to be usable by the view files. But in order to get these extensions into your project, you need to also modify\nyour `composer.json` file to include them, too:\n\n```\n\"yiisoft/yii2-smarty\": \"~2.0.0\",\n\"yiisoft/yii2-twig\": \"~2.0.0\",\n```\nThat code would be added to the `require` section of `composer.json`. After making that change and saving the file, you can install the extensions by running `composer update --prefer-dist` in the command-line.\n\nFor details about using concrete template engine please refer to its documentation:\n\n- [Twig guide](https://www.yiiframework.com/extension/yiisoft/yii2-twig/doc/guide/)\n- [Smarty guide](https://www.yiiframework.com/extension/yiisoft/yii2-smarty/doc/guide/)\n"
  },
  {
    "path": "docs/guide/tutorial-yii-as-micro-framework.md",
    "content": "# Using Yii as a Micro-framework\n\nYii can be easily used without the features included in basic and advanced templates. In other words, Yii is already a micro-framework. It is not required to have the directory structure provided by templates to work with Yii.\n\nThis is especially handy when you do not need all the pre-defined template code like assets or views. One of such cases is building a JSON API. In the following sections will show how to do that.\n\n## Installing Yii\n\nCreate a directory for your project files and change working directory to that path. Commands used in examples are Unix-based but similar commands exist in Windows.\n\n```bash\nmkdir micro-app\ncd micro-app\n```\n\n> Note: A little bit of Composer knowledge is required to continue. If you don't know how to use composer yet, please take time to read [Composer Guide](https://getcomposer.org/doc/00-intro.md).\n\nCreate file `composer.json` under the `micro-app` directory using your favorite editor and add the following:\n\n```json\n{\n    \"require\": {\n        \"yiisoft/yii2\": \"~2.0.0\"\n    },\n    \"repositories\": [\n        {\n            \"type\": \"composer\",\n            \"url\": \"https://asset-packagist.org\"\n        }\n    ]\n}\n```\n\nSave the file and run the `composer install` command. This will install the framework with all its dependencies.\n\n## Creating the Project Structure\n\nAfter you have installed the framework, it's time to create an [entry point](structure-entry-scripts.md) for the application. Entry point is the very first file that will be executed when you try to open your application. For the security reasons, it is recommended to put the entrypoint file in a separate directory and make it a web root.\n\nCreate a `web` directory and put `index.php` inside with the following content:\n\n```php \n<?php\n\n// comment out the following two lines when deployed to production\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n\nrequire(__DIR__ . '/../vendor/autoload.php');\nrequire(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');\n\n$config = require __DIR__ . '/../config.php';\n(new yii\\web\\Application($config))->run();\n```\n\nAlso create a file named `config.php` which will contain all application configuration:\n\n```php\n<?php\nreturn [\n    'id' => 'micro-app',\n    // the basePath of the application will be the `micro-app` directory\n    'basePath' => __DIR__,\n    // this is where the application will find all controllers\n    'controllerNamespace' => 'micro\\controllers',\n    // set an alias to enable autoloading of classes from the 'micro' namespace\n    'aliases' => [\n        '@micro' => __DIR__,\n    ],\n];\n```\n\n> Info: Even though the configuration could be kept in the `index.php` file it is recommended\n> to have it separately. This way it can be used for console application also as it is shown below.\n\nYour project is now ready for coding. Although it's up to you to decide the project directory structure, as long as you observe namespaces.\n\n## Creating the first Controller\n\nCreate a `controllers` directory and add a file `SiteController.php`, which is the default\ncontroller that will handle a request with no path info.\n\n```php\n<?php\n\nnamespace micro\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actionIndex()\n    {\n        return 'Hello World!';\n    }\n}\n```\n\nIf you want to use a different name for this controller you can change it and configure [[yii\\base\\Application::$defaultRoute]] accordingly.\nFor example, for a `DefaultController` that would be `'defaultRoute' => 'default/index'`.\n\nAt this point the project structure should look like this:\n\n```\nmicro-app/\n├── composer.json\n├── config.php\n├── web/\n    └── index.php\n└── controllers/\n    └── SiteController.php\n```\n\nIf you have not set up the web server yet, you may want to take a look at [web server configuration file examples](start-installation.md#configuring-web-servers).\nAnother options is to use the `yii serve` command which will use the PHP build-in web server. You can run\nit from the `micro-app/` directory via:\n\n    vendor/bin/yii serve --docroot=./web\n\nOpening the application URL in a browser should now print \"Hello World!\" which has been returned in the `SiteController::actionIndex()`.\n\n> Info: In our example, we have changed default application namespace `app` to `micro` to demonstrate\n> that you are not tied to that name (in case you thought you were), then adjusted\n> [[yii\\base\\Application::$controllerNamespace|controllers namespace]] and set the correct alias.\n\n\n## Creating a REST API\n\nIn order to demonstrate the usage of our \"micro framework\", we will create a simple REST API for posts.\n\nFor this API to serve some data, we need a database first. Add the database connection configuration\nto the application configuration:\n\n```php\n'components' => [\n    'db' => [\n        'class' => 'yii\\db\\Connection',\n        'dsn' => 'sqlite:@micro/database.sqlite',\n    ],\n],\n```\n\n> Info: We use an sqlite database here for simplicity. Please refer to the [Database guide](db-dao.md) for more options.\n\nNext we create a [database migration](db-migrations.md) to create a post table.\nMake sure you have a separate configuration file as explained above, we need it to run the console commands below.\nRunning the following commands will\ncreate a database migration file and apply the migration to the database:\n\n    vendor/bin/yii migrate/create --appconfig=config.php create_post_table --fields=\"title:string,body:text\"\n    vendor/bin/yii migrate/up --appconfig=config.php\n\nCreate directory `models` and a `Post.php` file in that directory. This is the code for the model:\n\n```php\n<?php\n\nnamespace micro\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass Post extends ActiveRecord\n{ \n    public static function tableName()\n    {\n        return '{{post}}';\n    }\n}\n```\n\n> Info: The model created here is an ActiveRecord class, which represents the data from the `post` table.\n> Please refer to the [active record guide](db-active-record.md) for more information.\n\nTo serve posts on our API, add the `PostController` in `controllers`:\n\n```php\n<?php\n\nnamespace micro\\controllers;\n\nuse yii\\rest\\ActiveController;\n\nclass PostController extends ActiveController\n{\n    public $modelClass = 'micro\\models\\Post';\n\n    public function behaviors()\n    {\n        // remove rateLimiter which requires an authenticated user to work\n        $behaviors = parent::behaviors();\n        unset($behaviors['rateLimiter']);\n        return $behaviors;\n    }\n}\n```\n\nAt this point our API will provide the following URLs:\n\n- `/index.php?r=post` - list all posts\n- `/index.php?r=post/view&id=1` - show post with ID 1\n- `/index.php?r=post/create` - create a post\n- `/index.php?r=post/update&id=1` - update post with ID 1\n- `/index.php?r=post/delete&id=1` - delete post with ID 1\n\nStarting from Here you may want to look at the following guides to further develop your application:\n\n- The API currently only understands urlencoded form data as input, to make it a real JSON API, you\n  need to configure [[yii\\web\\JsonParser]].\n- To make the URLs more friendly you need to configure routing.\n  See [guide on REST routing](rest-routing.md) on how to do this.\n- Please also refer to the [Looking Ahead](start-looking-ahead.md) section for further references.\n"
  },
  {
    "path": "docs/guide/tutorial-yii-integration.md",
    "content": "Working with Third-Party Code\n=============================\n\nFrom time to time, you may need to use some third-party code in your Yii applications. Or you may want to\nuse Yii as a library in some third-party systems. In this section, we will show how to achieve these goals.\n\n\nUsing Third-Party Libraries in Yii <span id=\"using-libs-in-yii\"></span>\n----------------------------------\n\nTo use a third-party library in a Yii application, you mainly need to make sure the classes in the library\nare properly included or can be autoloaded.\n\n### Using Composer Packages <span id=\"using-composer-packages\"></span>\n\nMany third-party libraries are released in terms of [Composer](https://getcomposer.org/) packages.\nYou can install such libraries by taking the following two simple steps:\n\n1. modify the `composer.json` file of your application and specify which Composer packages you want to install.\n2. run `composer install` to install the specified packages.\n\nThe classes in the installed Composer packages can be autoloaded using the Composer autoloader. Make sure\nthe [entry script](structure-entry-scripts.md) of your application contains the following lines to install\nthe Composer autoloader:\n\n```php\n// install Composer autoloader\nrequire __DIR__ . '/../vendor/autoload.php';\n\n// include Yii class file\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n```\n\n### Using Downloaded Libraries <span id=\"using-downloaded-libs\"></span>\n\nIf a library is not released as a Composer package, you should follow its installation instructions to install it.\nIn most cases, you will need to download a release file manually and unpack it in the `BasePath/vendor` directory,\nwhere `BasePath` represents the [base path](structure-applications.md#basePath) of your application.\n\nIf a library carries its own class autoloader, you may install it in the [entry script](structure-entry-scripts.md)\nof your application. It is recommended the installation is done before you include the `Yii.php` file so that\nthe Yii class autoloader can take precedence in autoloading classes.\n\nIf a library does not provide a class autoloader, but its class naming follows [PSR-4](https://www.php-fig.org/psr/psr-4/),\nyou may use the Yii class autoloader to autoload the classes. All you need to do is just to declare a\n[root alias](concept-aliases.md#defining-aliases) for each root namespace used in its classes. For example,\nassume you have installed a library in the directory `vendor/foo/bar`, and the library classes are under\nthe `xyz` root namespace. You can include the following code in your application configuration:\n\n```php\n[\n    'aliases' => [\n        '@xyz' => '@vendor/foo/bar',\n    ],\n]\n```\n\nIf neither of the above is the case, it is likely that the library relies on PHP include path configuration to\ncorrectly locate and include class files. Simply follow its instruction on how to configure the PHP include path.\n\nIn the worst case when the library requires explicitly including every class file, you can use the following method\nto include the classes on demand:\n\n* Identify which classes the library contains.\n* List the classes and the corresponding file paths in `Yii::$classMap` in the [entry script](structure-entry-scripts.md)\n  of the application. For example,\n```php\nYii::$classMap['Class1'] = 'path/to/Class1.php';\nYii::$classMap['Class2'] = 'path/to/Class2.php';\n```\n\n\nUsing Yii in Third-Party Systems <span id=\"using-yii-in-others\"></span>\n--------------------------------\n\nBecause Yii provides many excellent features, sometimes you may want to use some of its features to support\ndeveloping or enhancing 3rd-party systems, such as WordPress, Joomla, or applications developed using other PHP\nframeworks. For example, you may want to use the [[yii\\helpers\\ArrayHelper]] class or use the\n[Active Record](db-active-record.md) feature in a third-party system. To achieve this goal, you mainly need to\ntake two steps: install Yii, and bootstrap Yii.\n\nIf the third-party system uses Composer to manage its dependencies, run the following command to add Yii\nto the project requirements:\n\n```bash\ncomposer require yiisoft/yii2\n```\n\nIn case you would like to use only the database abstraction layer or other non-asset related features of Yii,\nyou should require a special composer package that prevent Bower and NPM packages installation. See \n[cebe/assetfree-yii2](https://github.com/cebe/assetfree-yii2) for details.\n\nSee also the general [section about installing Yii](start-installation.md#installing-via-composer) for more information\non Composer and solution to possible issues popping up during the installation.\n\nOtherwise, you can [download](https://www.yiiframework.com/download/) the Yii release file and unpack it in\nthe `BasePath/vendor` directory.\n\nNext, you should modify the entry script of the 3rd-party system by including the following code at the beginning:\n\n```php\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n\n$yiiConfig = require __DIR__ . '/../config/yii/web.php';\nnew yii\\web\\Application($yiiConfig); // Do NOT call run() here\n```\n\nAs you can see, the code above is very similar to that in the [entry script](structure-entry-scripts.md) of\na typical Yii application. The only difference is that after the application instance is created, the `run()` method\nis not called. This is because by calling `run()`, Yii will take over the control of the request handling workflow\nwhich is not needed in this case and already handled by the existing application.\n\nLike in a Yii application, you should configure the application instance based on the environment running\nthe third-party system. For example, to use the [Active Record](db-active-record.md) feature, you need to configure\nthe `db` [application component](structure-application-components.md) with the DB connection setting used by the third-party system.\n\nNow you can use most features provided by Yii. For example, you can create Active Record classes and use them\nto work with databases.\n\n\nUsing Yii 2 with Yii 1 <span id=\"using-both-yii2-yii1\"></span>\n----------------------\n\nIf you were using Yii 1 previously, it is likely you have a running Yii 1 application. Instead of rewriting\nthe whole application in Yii 2, you may just want to enhance it using some of the features only available in Yii 2.\nThis can be achieved as described below.\n\n> Note: Yii 2 requires PHP 5.4 or above. You should make sure that both your server and the existing application\n> support this.\n\nFirst, install Yii 2 in your existing application by following the instructions given in the [last subsection](#using-yii-in-others).\n\nSecond, modify the entry script of the application as follows,\n\n```php\n// include the customized Yii class described below\nrequire __DIR__ . '/../components/Yii.php';\n\n// configuration for Yii 2 application\n$yii2Config = require __DIR__ . '/../config/yii2/web.php';\nnew yii\\web\\Application($yii2Config); // Do NOT call run(), yii2 app is only used as service locator\n\n// configuration for Yii 1 application\n$yii1Config = require __DIR__ . '/../config/yii1/main.php';\nYii::createWebApplication($yii1Config)->run();\n```\n\nBecause both Yii 1 and Yii 2 have the `Yii` class, you should create a customized version to combine them.\nThe above code includes the customized `Yii` class file, which can be created as follows.\n\n```php\n$yii2path = '/path/to/yii2';\nrequire $yii2path . '/BaseYii.php'; // Yii 2.x\n\n$yii1path = '/path/to/yii1';\nrequire $yii1path . '/YiiBase.php'; // Yii 1.x\n\nclass Yii extends \\yii\\BaseYii\n{\n    // copy-paste the code from YiiBase (1.x) here\n}\n\nspl_autoload_unregister(array('YiiBase','autoload'));\nspl_autoload_register(array('Yii','autoload'));\n\nYii::$classMap = include($yii2path . '/classes.php');\n// register Yii 2 autoloader via Yii 1\nYii::registerAutoloader(['yii\\BaseYii', 'autoload']);\n// create the dependency injection container\nYii::$container = new yii\\di\\Container;\n```\n\nThat's all! Now in any part of your code, you can use `Yii::$app` to access the Yii 2 application instance, while\n`Yii::app()` will give you the Yii 1 application instance:\n\n```php\necho get_class(Yii::app()); // outputs 'CWebApplication'\necho get_class(Yii::$app);  // outputs 'yii\\web\\Application'\n```\n"
  },
  {
    "path": "docs/guide-ar/README.md",
    "content": " Yii 2.0 الدليل التقني الخاص ببيئة العمل\n===============================\n\nتم تحرير هذا الملف اعتمادا على [الشروط الخاصة بتوثيف ال Yii](https://www.yiiframework.com/doc/terms/).\n\nجميع الحقوق محفوظة\n\n2014 (c) Yii Software LLC.\n\n\nالمقدمة\n------------\n\n* [عن بيئة العمل Yii](intro-yii.md)\n* [التحديث من الإصدار 1.1](../guide/intro-upgrade-from-v1.md)\n\n\nالبداية من هنا\n---------------\n\n* [ماذا يجب أن تعرف عن بيئة العمل](start-prerequisites.md)\n* [تثبيت ال Yii](start-installation.md)\n* [تشغيل التطبيقات - Running Applications](start-workflow.md)\n* [قل مرحبا - المشروع الأول](start-hello.md)\n* [التعامل مع ال forms](start-forms.md)\n* [التعامل مع قواعد البيانات](start-databases.md)\n* [إنشاء الشيفرة البرمجية من خلال ال gii](start-gii.md)\n* [ماذا الآن - الخطوة القادمة](start-looking-ahead.md)\n\n\nالهيكلية الخاصة بالتطبيق (Application Structure)\n---------------------\n\n* [نظرة عامة عن الهيكلية الخاصة بالتطبيق](../guide/structure-overview.md)\n* [Entry Scripts](../guide/structure-entry-scripts.md)\n* [التطبيقات](../guide/structure-applications.md)\n* [مكونات التطبيقات](../guide/structure-application-components.md)\n* [Controllers](../guide/structure-controllers.md)\n* [Models](../guide/structure-models.md)\n* [Views](../guide/structure-views.md)\n* [Modules](../guide/structure-modules.md)\n* [Filters](../guide/structure-filters.md)\n* [Widgets](../guide/structure-widgets.md)\n* [Assets](../guide/structure-assets.md)\n* [Extensions](../guide/structure-extensions.md)\n\n\nالتعامل مع ال requests\n-----------------\n\n* [نظرة عامة عن التعامل مع ال requests](../guide/runtime-overview.md)\n* [Bootstrapping](../guide/runtime-bootstrapping.md)\n* [Routing and URL Creation](../guide/runtime-routing.md)\n* [Requests](../guide/runtime-requests.md)\n* [Responses](../guide/runtime-responses.md)\n* [Sessions and Cookies](../guide/runtime-sessions-cookies.md)\n* [Handling Errors - التحكم بالأخطاء](../guide/runtime-handling-errors.md)\n* [Logging - تسجيل الحركات](../guide/runtime-logging.md)\n\n\nالمفاهيم الرئيسية (Key Concepts)\n------------\n\n* [Components](../guide/concept-components.md)\n* [Properties](../guide/concept-properties.md)\n* [Events](../guide/concept-events.md)\n* [Behaviors](../guide/concept-behaviors.md)\n* [Configurations](../guide/concept-configurations.md)\n* [Aliases](../guide/concept-aliases.md)\n* [Class Autoloading](../guide/concept-autoloading.md)\n* [Service Locator](../guide/concept-service-locator.md)\n* [Dependency Injection Container](../guide/concept-di-container.md)\n\n\nالتعامل مع قواعد البيانات\n----------------------\n\n* [Database Access Objects](../guide/db-dao.md): Connecting to a database, basic queries, transactions, and schema manipulation\n* [Query Builder](../guide/db-query-builder.md): Querying the database using a simple abstraction layer\n* [Active Record](../guide/db-active-record.md): The Active Record ORM, retrieving and manipulating records, and defining relations\n* [Migrations](../guide/db-migrations.md): Apply version control to your databases in a team development environment\n* [Sphinx](https://www.yiiframework.com/extension/yiisoft/yii2-sphinx/doc/guide)\n* [Redis](https://www.yiiframework.com/extension/yiisoft/yii2-redis/doc/guide)\n* [MongoDB](https://www.yiiframework.com/extension/yiisoft/yii2-mongodb/doc/guide)\n* [ElasticSearch](https://www.yiiframework.com/extension/yiisoft/yii2-elasticsearch/doc/guide)\n\n\nالحصول على البيانات من خلال المستخدمين\n-----------------------\n\n* [Creating Forms](../guide/input-forms.md)\n* [Validating Input](../guide/input-validation.md)\n* [Uploading Files](../guide/input-file-upload.md)\n* [Collecting Tabular Input](../guide/input-tabular-input.md)\n* [Getting Data for Multiple Models](../guide/input-multiple-models.md)\n* [Extending ActiveForm on the Client Side](../guide/input-form-javascript.md)\n\n\nعرض البيانات\n---------------\n\n* [Data Formatting](../guide/output-formatting.md)\n* [Pagination](../guide/output-pagination.md)\n* [Sorting](../guide/output-sorting.md)\n* [Data Providers](../guide/output-data-providers.md)\n* [Data Widgets](../guide/output-data-widgets.md)\n* [Working with Client Scripts](../guide/output-client-scripts.md)\n* [Theming](../guide/output-theming.md)\n\n\nالامان والحماية\n--------\n\n* [Security Overview](../guide/security-overview.md)\n* [Authentication](../guide/security-authentication.md)\n* [Authorization](../guide/security-authorization.md)\n* [Working with Passwords](../guide/security-passwords.md)\n* [Cryptography](../guide/security-cryptography.md)\n* [Auth Clients](https://www.yiiframework.com/extension/yiisoft/yii2-authclient/doc/guide)\n* [Best Practices](../guide/security-best-practices.md)\n\n\nCaching التخزين المؤقت\n-------\n\n* [Caching Overview](../guide/caching-overview.md)\n* [Data Caching](../guide/caching-data.md)\n* [Fragment Caching](../guide/caching-fragment.md)\n* [Page Caching](../guide/caching-page.md)\n* [HTTP Caching](../guide/caching-http.md)\n\n\nRESTful Web Services\n--------------------\n\n* [Quick Start](../guide/rest-quick-start.md)\n* [Resources](../guide/rest-resources.md)\n* [Controllers](../guide/rest-controllers.md)\n* [Routing](../guide/rest-routing.md)\n* [Response Formatting](../guide/rest-response-formatting.md)\n* [Authentication](../guide/rest-authentication.md)\n* [Rate Limiting](../guide/rest-rate-limiting.md)\n* [Versioning](../guide/rest-versioning.md)\n* [Error Handling](../guide/rest-error-handling.md)\n\n\nالأدوات المساعدة أثناء تطوير التطبيقات\n-----------------\n\n* [Debug Toolbar and Debugger](https://www.yiiframework.com/extension/yiisoft/yii2-debug/doc/guide)\n* [Generating Code using Gii](https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide)\n* [Generating API Documentation](https://www.yiiframework.com/extension/yiisoft/yii2-apidoc)\n\n\nفحص واختبار التطبيقات\n-------\n\n* [Testing Overview](../guide/test-overview.md)\n* [Testing environment setup](../guide/test-environment-setup.md)\n* [Unit Tests](../guide/test-unit.md)\n* [Functional Tests](../guide/test-functional.md)\n* [Acceptance Tests](../guide/test-acceptance.md)\n* [Fixtures](../guide/test-fixtures.md)\n\n\nمواضيع وعناوين مميزة\n--------------\n\n* [Advanced Project Template](https://www.yiiframework.com/extension/yiisoft/yii2-app-advanced/doc/guide)\n* [Building Application from Scratch](../guide/tutorial-start-from-scratch.md)\n* [Console Commands](../guide/tutorial-console.md)\n* [Core Validators](../guide/tutorial-core-validators.md)\n* [Docker](../guide/tutorial-docker.md)\n* [Internationalization](../guide/tutorial-i18n.md)\n* [Mailing](../guide/tutorial-mailing.md)\n* [Performance Tuning](../guide/tutorial-performance-tuning.md)\n* [Shared Hosting Environment](../guide/tutorial-shared-hosting.md)\n* [Template Engines](../guide/tutorial-template-engines.md)\n* [Working with Third-Party Code](../guide/tutorial-yii-integration.md)\n* [Using Yii as a micro framework](../guide/tutorial-yii-as-micro-framework.md)\n\n\nWidgets\n-------\n\n* [GridView](https://www.yiiframework.com/doc-2.0/yii-grid-gridview.html)\n* [ListView](https://www.yiiframework.com/doc-2.0/yii-widgets-listview.html)\n* [DetailView](https://www.yiiframework.com/doc-2.0/yii-widgets-detailview.html)\n* [ActiveForm](https://www.yiiframework.com/doc-2.0/guide-input-forms.html#activerecord-based-forms-activeform)\n* [Pjax](https://www.yiiframework.com/doc-2.0/yii-widgets-pjax.html)\n* [Menu](https://www.yiiframework.com/doc-2.0/yii-widgets-menu.html)\n* [LinkPager](https://www.yiiframework.com/doc-2.0/yii-widgets-linkpager.html)\n* [LinkSorter](https://www.yiiframework.com/doc-2.0/yii-widgets-linksorter.html)\n* [Bootstrap Widgets](https://www.yiiframework.com/extension/yiisoft/yii2-bootstrap/doc/guide)\n* [jQuery UI Widgets](https://www.yiiframework.com/extension/yiisoft/yii2-jui/doc/guide)\n\n\nHelpers\n-------\n\n* [Helpers Overview](../guide/helper-overview.md)\n* [ArrayHelper](../guide/helper-array.md)\n* [Html](../guide/helper-html.md)\n* [Url](../guide/helper-url.md)\n\n"
  },
  {
    "path": "docs/guide-ar/intro-yii.md",
    "content": "# <div dir=\"rtl\">ما هي بيئة العمل Yii</div>\n\n<p dir=\"rtl\">Yii هو إطار PHP عالي الأداء يعتمد على المكونات لتطوير تطبيقات الويب الحديثة بسرعة.\nإن الاسم \"Yii\" (يُنطق بـ \"يي\" أو \"[جي:]\" يعني \"بسيطًا وتطوريًا\" باللغة الصينية. ومن الممكن ايضا\n    اعتباره اختصارًا لـ <b>Yes It Is</b>!</p>\n\n\n# <div dir=\"rtl\">ما هي أفضل التطبيقات أو البرمجيات التي يمكن برمجتها وتتناسب مع ال Yii</div>\n\n<p dir=\"rtl\">\nYii هو إطار عام لبرمجة الويب ، مما يعني أنه يمكن استخدامه لتطوير كافة أنواع\nتطبيقات الويب باستخدام PHP. وذلك بسبب البنية القائمة على  البنية التركيبة لبيئة العمل وترابطها مع المكونات والتخزين المؤقت، وهو مناسب بشكل خاص لتطوير portals, forums, content management systems (CMS), e-commerce projects, RESTful Web services. وما إلى ذلك.\n</p>\n\n\n# <div dir=\"rtl\">كيف يمكن مقارنة بيئة العمل الخاصة بال Yii مع الأطر أو بيئات العمل الأخرى؟</div>\n\n<p dir=\"rtl\">\n    إذا كنت بالفعل على دراية بإطار العمل الأخرى ، فيمكنك معرفة كيف تتم مقارنة ال Yii:\n</p>\n\n<ul  dir=\"rtl\">\n    <li> مثل معظم أطر عمل ال PHP ، يطبق Yii النمط المعماري MVC (Model-View-Controller).</li>\n<li> ال Yii يتبنى الفلسفة التي تقول أن الشيفرة البرمجية يجب أن تكتب بأسهل طريقها وادقها، ولكنها بذات الوقت يجب أن تكون أنيقة الكتابة مظهرا ومضمونا (شكلا وتطيبقا).</li>\n<li> ال Yii هو إطار متكامل  (full stack) يوفر العديد من الميزات الجاهزة للإستخدام والمعدة مسبقا، مثل ال query builders وال ActiveRecord لقواعد البيانات العلاقئية (relational) وغير العلائقية (Nosql)، بالإضافة الى دعم وتجهيز ال RESTful API  والتخزين المؤقت (caching) وغيرها الكثير. </li> \n<li> من مميزات ال Yii إمكانية التعديل (استبدال جزء معين أو تخيصيص وإضافة) جزء معين على أغلب ال Yii core code، وبالإضافة الى هذا، يمكنك بناء ملحقات برمجية اعتمادا على ال core code، ومن ثم نشر هذه الشيفرة وتوزيعها واستخدامها دون وجود أي مشاكل أو صعوبة تذكر.</li>\n<li> الأداء العالي هو الهدف الأساسي من ال Yii.</li>\n    </ul>\n\n<p dir=\"rtl\">\nال Yii إطار عمل صمم من قبل فريق برمجي متكامل، فهو ليس مجرد عمل فردي ،  بل يتكون من <a href=\"https://www.yiiframework.com/team\">فريق تطوير أساسي وقوي</a> ، بالإضافة إلى منتدى كبير\nمن المهنيين الذين يساهمون باستمرار في تطوير هذا الإطار. فريق المطورين الخاص بال Yii\nيراقب عن كثب أحدث اتجاهات تطوير الويب وأفضل الممارسات والمميزات التي\nوجدت في الأطر والمشاريع الأخرى. وتدرج بانتظام بإضافة أفضل الممارسات والميزات الى ال Yii عبر واجهات بسيطة وأنيقة.\n</p>\n\n\n\n# <div dir=\"rtl\">الإصدارات الخاصة بال Yii</div>\n\n<p dir=\"rtl\">\n    يتوفر لدى Yii حاليًا إصداران رئيسيان: 1.1 و 2.0. الإصدار 1.1 هو الجيل القديم وهو الآن في وضع الصيانة. الإصدار 2.0 هو إعادة كتابة وهيكلة كاملة لل Yii، تم  اعتماد أحدث التقنيات والبروتوكولات فيها مثل  including Composer, PSR, namespaces, traits والكثير من الأمور الأخرى، وفي هذه الإرشادات، سيكون الكلام كله موجها الى الإصدار الثاني من بيئة العمل ال Yii.\n</p>\n\n# <div dir=\"rtl\">المتطلبات الأساسية للعمل على إطار ال Yii</div>\n\n<ul dir=\"rtl\">\n    <li>الإصدار PHP 7.4.0 أو أكثر</li>\n    <li>المعرفة الأساسية بمفاهيم البرمجة كائنية التوجه OOP</li>\n    <li>المعرفة بآخر وأحدث التقنيات الموجودة بال php مثل ال namespaces, traits، الفهم لهذه المفاهيم سيسهل عليك العمل كثيرا</li>\n</ul>\n\n<p dir=\"rtl\">\n    ملاحظة: يمكن التحقق من توافق المتطلبات الخاصة بك مع ال yii من خلال الدخول الى الصفحة requirement الموجودة بال yii\n    </p>\n"
  },
  {
    "path": "docs/guide-ar/start-databases.md",
    "content": "# <div dir=\"rtl\">التعامل مع قواعد البيانات</div>\n\n<p dir=\"rtl\">\n    في هذا الجزء التعليمي ستتعلم آلية إنشاء صفحة جديدة تعرض بيانات يتم جلبها من قاعدة البيانات  -في هذا المثال، البيانات تخص ال country-، هذه البيانات سيتم جلبها من جدول موجود في قاعدة البيانات يسمى ب <code>country</code>. لتحقيق هذا المهمة، ستقوم بعمل ال config الخاص بالإتصال بقاعدة بيانات، بالإضافة لإنشاء ال <a href=\"../guide/db-active-record.md\">Active Record</a> class، وتعريف ال <a href=\"../guide/structure-controllers.md\">action</a>، وإنشاء <a href=\"../guide/structure-views.md\">view</a> لهذه الصفحة. \n</p>\n\n\n<p dir=\"rtl\">\n في هذا الشرح ستتعلم كيف يمكنك القيام بما يلي: \n</p>\n\n<ul dir=\"rtl\">\n    <li>إعداد ال connection الخاص بقاعدة البيانات</li>\n    <li> التعرف على ال active record.</li>\n    <li>إنشاء جمل إستعلام عن البياتات بإستخدام ال active record class</li>\n    <li>عرض البيانات داخل ال view من خلال ال paginated fashion.</li>\n</ul>\n\n<p dir=\"rtl\">\n    ملاحظة: من أجل الانتهاء من هذا الجزء التعليمي، يجب أن يكون لديك المعرفة الأساسية والخبرة باستخدام قواعد البيانات. وعلى وجه الخصوص، يجب أن تعرف كيفية إنشاء قواعد البيانات، وكيفية تنفيذ ال statements SQL باستخدام أي DB client tool.\n</p>\n\n## <div dir=\"rtl\">إعداد قاعدة البيانات</div> <span id=\"preparing-database\"></span>\n\n<p dir=\"rtl\">\n    في البداية، عليك إنشاء قاعدة بيانات تسمى ب <code>yii2basic</code>، والتي ستستخدم لجلب البيانات الخاصة بالتطبيق، ويمكنك إستخدام أي من ال SQLite, MySql, PostgreSQL, MSSQL or Oracle database, ال Yii بشكل افتراضي بدعم العديد من قواعد البيانات والتي يمكنك إستخدامها مباشرة في التطبيق الخاص بك، ولتبسيط الأمور، ال MySql هي التي سيتم إستخدامها في في هذا الشرح. \n</p>\n\n<blockquote class=\"info\"><p dir=\"rtl\">\n    معلومة: إذا كنت ترغب بالحصول على خيارات متقدمة  مثل دعم ال <code>JSON</code> الموجود داخل MariaDB، فيمكنك من إستخدام أحد ال Extension المذكوره بالأسفل للقيام بهذه المهمة بدلا من الإستغناء عن ال MySql، فإستخدام MariaDB بدلا عن ال MySql لم يعد صحيحا تماما. \n</p></blockquote>\n\n<p dir=\"rtl\">\n    بعد قيامك بإنشاء قاعدة البيانات، سنقوم بإنشاء جدول إسمه <code>country</code>، ومن ثم سنقوم بإدخال بعض البيانات كعينة للإختيار، وللقيام بذلك، قم بتنفيذ الأوامر التالية: \n</p>\n\n```sql\nCREATE TABLE `country` (\n  `code` CHAR(2) NOT NULL PRIMARY KEY,\n  `name` CHAR(52) NOT NULL,\n  `population` INT(11) NOT NULL DEFAULT '0'\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nINSERT INTO `country` VALUES ('AU','Australia',24016400);\nINSERT INTO `country` VALUES ('BR','Brazil',205722000);\nINSERT INTO `country` VALUES ('CA','Canada',35985751);\nINSERT INTO `country` VALUES ('CN','China',1375210000);\nINSERT INTO `country` VALUES ('DE','Germany',81459000);\nINSERT INTO `country` VALUES ('FR','France',64513242);\nINSERT INTO `country` VALUES ('GB','United Kingdom',65097000);\nINSERT INTO `country` VALUES ('IN','India',1285400000);\nINSERT INTO `country` VALUES ('RU','Russia',146519759);\nINSERT INTO `country` VALUES ('US','United States',322976000);\n```\n\n<p dir=\"rtl\">\n    الآن، أصبح لديك قاعدة بيانات إسمها <code>yii2basic</code>، وتحوي بداخلها جدول بثلاث أعمدة يسمى ب <code>country</code>، وفيه 10 صفوف من البيانات.     \n</p>\n\n## <div dir=\"rtl\">إعدادات الإتصال الخاصة بقواعد البيانات - Configuring a DB Connection</div> <span id=\"configuring-db-connection\"></span>\n\n<p dir=\"rtl\">\n    قبل أن تكمل الشرح، تأكد من تثبيت ال PHP <a href=\"https://www.php.net/manual/en/book.pdo.php\">PDO</a> وال PDO driver، بالنسبة لهذا المثال، فإننا سنستخدم ال driver الخاص بال MySql وهو ال <code>pdo_mysql</code>، وهذه هي المتطلبات الأساسية لبناء أي التطبيق اذا كان التطبيق يستخدم ال relational database. \n</p>\n\n<blockquote class=\"note\"><p dir=\"rtl\">\n   ملاحظة: يمكنك تقعيل ال PDO مباشرة من خلال الدخول الى php.ini ومن ثم حذف الفاصلة المنقوطة قبل السطر التالي: <code>extension=php_pdo.dll</code>\n    كما يمكنك تفعيل ال driver المطلوب عن طريق حذف الفاصلة المنقوطة قبل ال driver المقصود مثل: \n<code>extension=php_pdo_mysql.dll</code>\n    ويمكنك الإطلاع على المزيد من هنا: \n<a href=\"https://www.php.net/manual/en/pdo.installation.php\">pdo installation</a>    \n</p></blockquote>\n\n<p dir=\"rtl\">\n    بعد إتمام ما سبق، قم بفتح الملف <code>config/db.php</code> ومن ثم قم بتعديل ال parameters لتكون الإعدادات الخاصة بقاعدة البيانات صحيحة -الإعدادت الخاصة بك-، بشكل افتراضي، يحتوي الملف على ما يلي: \n</p>\n    \n\n```php\n<?php\n\nreturn [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=localhost;dbname=yii2basic',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n];\n```\n\n<p dir=\"rtl\">\n    يمثل ملف ال <code>config/db.php</code> أداة نموذجية تعتمد على الملفات للقيام بال <a href=\"../guide/concept-configurations.md\">configuration</a>. يقوم ملف ال configuration بتحديد ال parameters المطلوبة لإنشاء وإعداد ال instance الخاص بال <code>[[yii\\db\\Connection]]</code>، ومن خلالها يمكنك إجراء عمليات الإستعلام على قاعدة البيانات.\n</p>\n\n<p dir=\"rtl\">\n الإعدادات الخاصة بالإتصال بقاعدة البيانات والمذكورة في الملف أعلاه يمكن الوصول اليها من خلال التطبيق عن طريق تنفيذ الأمر التالي\n    <code>Yii::$app->db</code>\n</p>\n\n<blockquote class=\"info\"><p dir=\"rtl\">\n    معلومة: سيتم تضمين ملف ال <code>config/db.php</code> من خلال  ال main application configuration والذي يتمثل بالملف <code>config/web.php</code>، والذي يقوم بدوره بتحديد كيف يمكن تهيئة ال instance الخاص <a href=\"../guide/concept-configurations.md\">بالتطبيق</a>، لمزيد من المعلومات، يرجى الإطلاع على قسم ال <a href=\"../guide/concept-configurations.md\">Configurations</a>.\n</p></blockquote>\n\n<p dir=\"rtl\">\n    إذا كنت بحاجة إلى العمل مع إحدى قواعد البيانات الغير مدعومة بشكل إفتراضي من ال Yii، فيمكنك التحقق من الإضافات التالية:\n</p>\n\n<ul dir=\"rtl\">\n    <li><a href=\"https://github.com/edgardmessias/yii2-informix\">Informix</a></li>\n<li> <a href=\"https://github.com/edgardmessias/yii2-ibm-db2\">IBM DB2</a></li>\n<li> <a href=\"https://github.com/edgardmessias/yii2-firebird\">Firebird</a></li>\n<li> <a href=\"https://github.com/sam-it/yii2-mariadb\">MariaDB</a></li>\n</ul>\n\n## <div dir=\"rtl\">إنشاء ال Active Record</div> <span id=\"creating-active-record\"></span>\n\n<p dir=\"rtl\">\n    لجلب البيانات وعرضها من جدول ال <code>country</code>، سنقوم بإضافة ال <a href=\"../guide/db-active-record.md\">Active Record</a> الى ال class المسمى ب <code>country</code>، والموجود في المسار <code>models/Country.php</code>.\n</p>\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass Country extends ActiveRecord\n{\n}\n```\n<p dir=\"rtl\">\n    يرث ال <code>Country</code> Class ال [[yii\\db\\ActiveRecord]]، ولذلك، أنت لست بحاجة لكتابة أي شيفرة برمجية بداخله، فقط الشيفرة التي تشاهدها بالأعلى. سيقوم ال Yii بشكل تلقائي بالحصول على إسم الجدول في قاعدة البيانات من خلال إسم ال Class. \n</p>\n\n<blockquote class=\"info\"><p dir=\"rtl\">\nمعلومة: إذا لم يكن من الممكن إجراء مطابقة مباشرة بين اسم ال class واسم الجدول، فيمكنك تجاوز هذه المشكلة من خلال إستخدام الدالة  [[yii\\db\\ActiveRecord::tableName()]] ، والتي ستقوم بعمل override على اسم الجدول. \n</p></blockquote>\n\n<p dir=\"rtl\">\n    من خلال إستخدام ال <code>Country</code> class، يمكنك التحكم بكل سهولة بالبيانات الموجودة داخل جدول ال <code>country</code>، شاهد هذه الشيفرة البرمجية لتبسيط الفكرة:  \n</p>\n\n```php\nuse app\\models\\Country;\n\n// get all rows from the country table and order them by \"name\"\n$countries = Country::find()->orderBy('name')->all();\n\n// get the row whose primary key is \"US\"\n$country = Country::findOne('US');\n\n// displays \"United States\"\necho $country->name;\n\n// modifies the country name to be \"U.S.A.\" and save it to database\n$country->name = 'U.S.A.';\n$country->save();\n```\n\n<blockquote class=\"info\"><p dir=\"rtl\">\n    معلومة: يعتبر ال Active Record وسيلة قوية للوصول إلى بيانات قاعدة البيانات والتعامل معها بطريقة ال object oriented.\n    ستجد معلومات أكثر تفصيلاً في الجزء الخاص بال <a href=\"../guide/db-active-record.md\">Active Record</a>. بالإضافة الى ذلك، يمكنك التفاعل مباشرة مع قاعدة البيانات باستخدام lower-level data accessing والتي تسمى ب <a href=\"../guide/db-dao.md\">Database Access Objects</a>.\n</p></blockquote>\n\n## <div dir=\"rtl\">إنشاء ال Action</div> <span id=\"creating-action\"></span>\n\n<p dir=\"rtl\">\nلعرض بيانات ال country للمستخدمين، يلزمك إنشاء action جديد، وبدلاً من وضع ال action الجديد في ال <code>site</code> controller كما فعلنا في المرات السابقة، سنقوم بإنشاء controller جديد، ومن ثم سنقوم بوضع ال action بداخله، والسبب المنطقي لهذا العمل أنك ستقوم بتجميع الشيفرة البرمجية المسؤولة عن أداء وظائف معينة في مكان واحد، وبهذا فإن جميع الإجرائات التي تخص ال country من المنطقي أن تكون موجودة داخل ال <code>CountryController</code>، والآن لنقم بإنشاء هذا ال controller الجديد، وال action الجديد وسيكون باسم <code>index</code>، كما هو موضح أدناه: \n</p>\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\nuse yii\\data\\Pagination;\nuse app\\models\\Country;\n\nclass CountryController extends Controller\n{\n    public function actionIndex()\n    {\n        $query = Country::find();\n\n        $pagination = new Pagination([\n            'defaultPageSize' => 5,\n            'totalCount' => $query->count(),\n        ]);\n\n        $countries = $query->orderBy('name')\n            ->offset($pagination->offset)\n            ->limit($pagination->limit)\n            ->all();\n\n        return $this->render('index', [\n            'countries' => $countries,\n            'pagination' => $pagination,\n        ]);\n    }\n}\n```\n<p dir=\"rtl\">\n    قم بحفظ الشيفرة البرمجية التي بالأعلى داخل هذا الملف <code>controllers/CountryController.php</code>\n</p>\n\n<p dir=\"rtl\">\n    يقوم ال <code>index</code> action باستخدام ال <code>Country::find()</code>، وهذه الدالة موجودة من ضمن ال <code>active record</code>، وتقوم هذه الدالة على بناء الإستعلام الخاص بقاعدة البيانات مما يسمح باسترجاع جميع البيانات الموجودة بداخل جدول ال country، ولتحديد الحد الأعلى المسموح إرجاعه في كل request، يمكنك إستخدام ال <code>[[yii\\data\\Pagination]]</code> object كوسيلة مساعدة، ويقدم هذا ال object غرضين أساسيين وهما: \n</p>\n\n<ul dir=\"rtl\">\n    <li>كتابة جملة استعلام محددة بعدد صفوف معين، وتبدأ من مكان معين، ويتم ذلك من خلال ال <code>offset</code> وال <code>limit</code>، وبهذا التعيين فأنت ستقوم بإرجاع صفحة واحدة من البيانات وفي كل مرة سيتم عرض 5 صفوف على الأكثر. </li>\n    <li>كما يستخدم في صفحة ال view لعرض ال pager مع قائمة بال page buttons، وذلك كما ستشاهد في الجزء التالي من هذا الموضوع. </li>\n</ul>\n\n<p dir=\"rtl\">\n    في نهاية الشيفرة البرمجية الموجودة في ال <code>index</code> action، نقوم باستدعاء صفحة ال view المسمية ب <code>index</code>، ومن ثم نقوم بإرسال معلومات ال country المنظمة على شكل صفحات (pagination) ومن ثم عرضها. \n</p>\n\n\n## <div dir=\"rtl\"> إنشاء View </div> <span id=\"creating-view\"></span>\n\n<p dir=\"rtl\">\n    الآن، سنقوم بإنشاء صفحة ال view والتي ستقوم بعرض المعلومات الخاصة بال country والقادمة من ال index action، ولذلك توجه الى المسار <code>views</code>, ومن ثم قم بإنشاء مجلد جديد بداخله باسم <code>country</code>، هذا المجلد سيحوي جمع ال views التي سيتم إستدعائها من ال <code>country</code> controller>. الآن، قم بإنشاء الصفحة index.php بداخل المسار <code>views/country</code>، ومن ثم قم بكتابة هذه الشيفرة البرمجية بداخلها: \n</p>\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\LinkPager;\n?>\n<h1>Countries</h1>\n<ul>\n<?php foreach ($countries as $country): ?>\n    <li>\n        <?= Html::encode(\"{$country->code} ({$country->name})\") ?>:\n        <?= $country->population ?>\n    </li>\n<?php endforeach; ?>\n</ul>\n\n<?= LinkPager::widget(['pagination' => $pagination]) ?>\n```\n\n<p dir=\"rtl\">\n    في هذه الصفحة، هناك جزئيين رئيسيين، الجزء الأول هو المسؤول عن طباعة المعلومات الخاصة بال country داخل قائمة من نوع ul، والجزء الثاني عبارة عن widget يستخدم المعلومات الخاصة بال pagination والتي تم إرسالها من ال action، ليقوم بعد ذلك بعرض قائمة من ال page buttons، والتي بدورها ستقوم بعمل تحديث للمعلومات الموجودة بالصفحة بنائا على الصفحة المطلوبة، هذا ال widget يسمى ب <code>LinkPager -[[yii\\widgets\\LinkPager]]-</code>.\n</p>\n\n## <div dir=\"rtl\">لنجرب المثال</div> <span id=\"trying-it-out\"></span>\n\n<p dir=\"rtl\">\n    لتشاهد آلية العمل لهذا المثال، والنتائج المتعلقة به، يمكنك إستخدام المتصفح والدخول الى الرابط التالي:\n</p>\n\n```\nhttps://hostname/index.php?r=country%2Findex\n```\n\n![Country List](../guide/images/start-country-list.png)\n\n<p dir=\"rtl\">\nفي البداية، ستشاهد البيانات الخاصة بخمسة دول، وأسفل هذه الدول، ستشاهد pager فيه 4 أزرار، اذا قمت بالضغط على الزر رقم 2، فإنك ستشاهد المعلومات الخاصة بخمسة دول أخرى، والتي تمثل صفوف أخرى من البيانات تم جلبها من قاعدة البيانات. \n<br />\nتابع بعناية أكبر وستجد أن عنوان ال URL في المتصفح قد تغير أيضًا:\n</p>\n\n```\nhttps://hostname/index.php?r=country%2Findex&page=2\n```\n\n<p dir=\"rtl\">\n    بالخفاء، يقوم ال <code>[[yii\\data\\Pagination|Pagination]]</code> بتقديم الدعم اللازم لكل الوظائف المطلوب تنفيذها لعمل الترقيم الخاص بال pagination: \n</p>\n\n<ul dir=\"rtl\">\n    <li>في البداية، يمثل ال  [[yii\\data\\Pagination|Pagination]] الصفحة الأولى التي تعكس جملة الإستعلام SELECT مع الجدول country مع العبارة <code>LIMIT 5 OFFSET 0</code>. ونتيجة لذلك، سيتم جلب البلدان الخمسة الأولى وعرضها.</li>\n    <li>ال [[yii\\widgets\\LinkPager|LinkPager]] widget يقوم على إستدعاء الأزرار الخاصة بأرقام الصفحات والتي يتم إنشاء ال Url الخاص بها من خلال ال [[yii\\data\\Pagination::createUrl()|Pagination]]، هذه ال Url تحتوي على parameter يسمى ب <code>page</code>، والذي يمثل بدورة مجموعة من الأرقام المختلفة والتي تمثل صفحات تعرض بيانات مختلفة كما هو في مثالنا عندما قمنا بالضغط على رقم 2.</li>\n    <li>عند قيامك بالنقر على الزر رقم 2، سينطلق request جديد الى ال <code>country/index</code> route، وحينها يقوم ال [[yii\\data\\Pagination|Pagination]] على قرائة ال <code>page</code> parameter من ال URL ,من ثم يقوم بتغيير رقم الصفحة الحالية الى الرقم الجديد، وهنا الرقم الجديد هو 2 بدلا من 1. كما أن جملة الإستعلام الجديدة ستحوي الشرط التالي <code>LIMIT 5 OFFSET 5</code> لتقم بذلك بجلب الصفوف الخمسة الأخرى من البلدان لعرضها.</li>\n</ul>\n\n## <div dir=\"rtl\">الخلاصة</div> <span id=\"summary\"></span>\n\n<p dir=\"rtl\">\n    في هذا الجزء من التوثيق، لقد تعلمنا كيف يمكننا التعامل مع قواعد البيانات، وكيف يمكننا جلب البيانات وعرضها وتجزئتها  لصفحات من خلال إستخدام ال [[yii\\data\\Pagination]] وال [[yii\\widgets\\LinkPager]].\n</p>\n\n<p dir=\"rtl\">\n  في الجزء القادم من هذا التوثيق، ستتعلم كيف يمكنك إستخدام إخدى الوسائل المميزة والموجودة في ال Yii لإنشاء الشيفرة البرمجية الخاصة بك، والتي يطلق عليها اسم <a href=\"https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide\">Gii</a>، هذه الأداة ستساعدك على إنشاء وتنفيذ الشيفرات البرمجية التي يكثر كتابتها وإستخدامها بشكل دوري، مثل مجموعة العمليات Create-Read-Update-Delete، والتي يتم اختصارها ب CRUD، هذه الأداة ستقوم بالتعامل مع البيانات الموجدة بقاعدة البيانات، وفي الحقيقة، الشيفرة البرمجية الذي قمت بكتابته بالأعلى، يمكنك إنشائه بسهولة من خلال ال Gii بنقرة زر، لذلك فلننطلق الى الجزء التالي بأسرع وقت.  \n</p>\n"
  },
  {
    "path": "docs/guide-ar/start-forms.md",
    "content": "# <div dir=\"rtl\">العمل مع ال Forms</div>\n\n<p dir=\"rtl\">\nفي هذا الموضوع سنتعلم كيفية إنشاء صفحة تحتوي على form للحصول على البيانات من خلال المستخدمين، وستعرض هذه الصفحة form يحتوي على حقل لإدخال الإسم وحقل إدخال للبريد الإلكتروني.\nوبعد الحصول على المعلومات الخاصة بهذه الحقول من المستخدم، ستقوم الصفحة بطباعة القيم التي تم إدخالها. \n</p>\n\n<p dir=\"rtl\">\n    في هذا الشرح، ستقوم بإضافة <a href=\"../guide/structure-controllers.md\">action</a> وصحفتين <a href=\"../guide/structure-views.md\">views</a>، وستتعرف أيضا على طريقة إنشاء ال <a href=\"../guide/structure-models.md\">model</a>.\n</p>\n\n<p dir=\"rtl\">\nمن خلال هذا البرنامج التعليمي ، ستتعلم كيفية:\n</p>\n\n<ul dir=\"rtl\">\n    <li>إنشاء model لتمثيل البيانات التي تم إدخالها من خلال المستخدم عن طريق ال form.</li>\n    <li>إنشاء rules للتحقق من صحة البيانات التي تم إدخالها.</li>\n    <li>بناء html form داخل صفحة ال view.</li>\n</ul>\n\n## <div dir=\"rtl\">إنشاء ال Model</div> <span id=\"creating-model\"></span>\n\n<p dir=\"rtl\">\n    يتم تمثيل البيانات التي يتم طلبها من خلال المستخدم عن طريق ال <code>EntryForm</code> model class  كما هو موضح أدناه، ويتم حفظ هذا الملف داخل المسار models، ويكون إسم ال model ومساره في مثالنا هذا هو  <code>models/EntryForm.php</code>. يرجى الرجوع إلى صفحة ال <a href=\"../guide/concept-autoloading.md\">Class Autoloading</a> للحصول على مزيد من التفاصيل حول طريقة التعامل مع التسمية الخاصة بال class في Yii. \n</p>\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse Yii;\nuse yii\\base\\Model;\n\nclass EntryForm extends Model\n{\n    public $name;\n    public $email;\n\n    public function rules()\n    {\n        return [\n            [['name', 'email'], 'required'],\n            ['email', 'email'],\n        ];\n    }\n}\n```\n\n<p dir=\"rtl\">\n    هذا ال class يرث ال [[yii\\base\\Model]], وهو base class تم تصميمه من خلال ال Yii, وبشكل عام وظيفته هي تثمثيل البيانات الخاصة بأي نموذج.\n</p>\n\n<blockquote class=\"info\"><p dir=\"rtl\">\nمعلومة: يتم إستخدام ال  [[yii\\base\\Model]] كأصل لل model class <b>ولا</b> يرتبط بجداول قواعد البيانات. ويستخدم ال  [[yii\\db\\ActiveRecord]]  بالشكل الإعتيادي ليكون هو الأصل الذي من خلاله يتم الإرتباط بجداول بقواعد البيانات. \n</p></blockquote>\n\n<p dir=\"rtl\">\n    يحتوي class ال <code>EntryForm</code> على متغيرين إثنين من نوع Public، هما <code>name</code> و <code>email</code>، واللذان يستخدمان في تخزين البيانات التي أدخلها المستخدم. كما يحتوي أيضًا على method باسم <code>rules()</code>، والتي تُرجع مجموعة\nالشروط الخاصة بالبيانات للتحقق من صحتها. والشيفرة البرمجية الموجودة داخل ال rules method تعني: \n</p>\n\n<ul dir=\"rtl\">\n    <li>كل من ال <code>name</code> وال <code>email</code> حقول الزامية (required).</li>\n    <li>ال <code>email</code> حقل يجب أن يحتوي بداخله قيمة صحيحة تعبر عن البريد الإلكتروني (القواعد النحوية لكتابة البريد الإلكتروني).</li>\n</ul>\n\n<p dir=\"rtl\">\n    إذا كان لديك object من ال  <code>EntryForm</code> ويحتوي على البيانات التي أدخلها المستخدم،  فيمكنك حينها إستدعاء الدالة  [[yii\\base\\Model::validate()|validate()]] للتحقق من صحة البيانات. اذا فشلت عملية التحقق من صحة البيانات، فسيؤدي ذلك إلى تغيير قيمة ال  [[yii\\base\\Model::hasErrors|hasErrors]] إلى <code>true</code> ، بالإضافة الى ذلك يمكنك التعرف الى الأخطاء المتعلقة بهذه البيانات من خلال الدالة [[yii\\base\\Model::getErrors|errors]].\n</p>\n\n```php\n<?php\n$model = new EntryForm();\n$model->name = 'Qiang';\n$model->email = 'bad';\nif ($model->validate()) {\n    // Good!\n} else {\n    // Failure!\n    // Use $model->getErrors()\n}\n```\n\n\n## <div dir=\"rtl\">إنشاء Action</div> <span id=\"creating-action\"></span>\n\n<p dir=\"rtl\">\n    الآن، ستحتاج إلى إنشاء <code>action</code> جديد في ال <code>site</code> controller وليكن إسمه <code>entry</code>، والذي سيقوم بدوره باستخدام ال model الجديد الذي قمنا بإنشائه. هذه العملية تم شرحها سابقا في الجزء التالي من التوثيق <a href=\"start-hello.md\">Saying Hello - قل مرحبا</a>.\n</p>\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\nuse app\\models\\EntryForm;\n\nclass SiteController extends Controller\n{\n    // ...existing code...\n\n    public function actionEntry()\n    {\n        $model = new EntryForm();\n\n        if ($model->load(Yii::$app->request->post()) && $model->validate()) {\n            // valid data received in $model\n\n            // do something meaningful here about $model ...\n\n            return $this->render('entry-confirm', ['model' => $model]);\n        } else {\n            // either the page is initially displayed or there is some validation error\n            return $this->render('entry', ['model' => $model]);\n        }\n    }\n}\n```\n<p dir=\"rtl\">\n    أولا، يقوم ال action بإنشاء object من ال  <code>EntryForm</code>. ثم يحاول تعبئة البيانات لل object من خلال ال <code>$ _POST</code>، والتي يتم تقديمها في ال Yii من خلال ال [[yii\\web\\Request::post()]].\nإذا تم ملء ال object بنجاح (على سبيل المثال، إذا قام المستخدم بإدخال البيانات داخل ال form ومن ثم قام بإرسالها(submitted html form))، فسيتم استدعاء ال [[yii\\base\\Model::validate()|validate()]] من خلال ال action للتأكد من صلاحية القيم المدخلة.\n</p>\n\n\n<blockquote class=\"info\"><p dir=\"rtl\">\n    معلومة: يمثل التعبير Yii::$app ال  <a href=\"../guide/structure-applications.md\">Application</a> instance الذي يمكن الوصول اليه من خلال ال singleton <br />(singleton globally accessible). وهو أيضا  <a href=\"../guide/concept-service-locator.md\">service locator</a>  بحيث يوفر الدعم لل components مثل ال request, response, db..الخ، لدعم وظائف محددة. مثلا في المثال الموجود في الأعلى، فإن ال request هو component من ال application instance والذي يستخدم للوصول الى البيانات الموجودة داخل ال $_POST. \n</p></blockquote>\n\n<p dir=\"rtl\">\n    إذا كان كل شيء على ما يرام، فسوف يقوم ال action بجلب ال view التالية: <code>entry-confirm</code>، وذلك لتأكيد أن العملية قد تمت بنجاح بالنسبة للمستخدم، أما إن كانت البيانات غير صحيحة، أو لم يتم إرسال أي بيانات، فإن ال view <code>entry</code> هي التي سيتم جلبها وعرضها للمستخدم، حيث يتم عرض ال Html form، مع أي رسائل تحذير بخصوص الأخطاء التي تم العثور عليها من عملية التحقق.\n</p>\n\n<blockquote class=\"note\"><p dir=\"rtl\">\nملاحظة: في هذا المثال البسيط، نعرض صفحة التأكيد فقط عند إرسال البيانات بشكل صحيح. عند الممارسة العملية، يجب عليك استخدام [[yii\\web\\Controller::refresh()|refresh()]] أو [[yii\\web\\Controller::redirect()|redirect()]] لتجنب أي مشكلة تحصل عن طريق ال resubmission والتي تندرج تحت العنوان <a href=\"https://en.wikipedia.org/wiki/Post/Redirect/Get\">form resubmission problems</a>.\n</p></blockquote>\n\n## <div dir=\"rtl\">إنشاء ال views</div> <span id=\"creating-views\"></span>\n\n<p dir=\"rtl\">\n    أخيرا، سنقوم بإنشاء صفحتين لل views الأولى بإسم <code>entry-confirm</code> والثانية <code>entry</code>. وهاتين الصفحتين سيتم جلبهم من خلال ال <code>entry</code> action. \n</p>\n\n<p dir=\"rtl\">\n    ال <code>entry-confirm</code> ستقوم بكل بساطة بعرض الإسم والبريد الإلكتروني الذي تم إدخالهم من قبل المستخدم. ويجب حفظ هذه الصفحة بالمسار التالي: <code>views/site/entry-confirm.php</code> \n</p>\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n<p>You have entered the following information:</p>\n\n<ul>\n    <li><label>Name</label>: <?= Html::encode($model->name) ?></li>\n    <li><label>Email</label>: <?= Html::encode($model->email) ?></li>\n</ul>\n```\n<p dir=\"rtl\">\n   صفحة ال <code>entry</code> ستقوم بعرض ال HTML form. هذه الصفحة يجب أن يتم حفظها داخل المسار التالي: <code>views/site/entry.php</code>\n</p>\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n?>\n<?php $form = ActiveForm::begin(); ?>\n\n    <?= $form->field($model, 'name') ?>\n\n    <?= $form->field($model, 'email') ?>\n\n    <div class=\"form-group\">\n        <?= Html::submitButton('Submit', ['class' => 'btn btn-primary']) ?>\n    </div>\n\n<?php ActiveForm::end(); ?>\n```\n<p dir=\"rtl\">\n    تستخدم ال view أسلوب مميز لبناء ال Forms، وذلك عن طريق ال <a href=\"../guide/structure-widgets.md\">widget</a> الذي يسمى ب  [[yii\\widgets\\ActiveForm|ActiveForm]]. إن الأسلوب المستخدم في هذا ال widget يقوم على إستخدام كل من الدالة <code>begin()</code> و <code>end()</code>  لجلب ال opening  وال closing form tags على التوالي (فتحة ال tag، ثم الإغلاق الخاص بهذا ال tag)، وبين الفتحة والإغلاق يمكنك إنشاء الحقول عن طريق إستخدام الدالة [[yii\\widgets\\ActiveForm::field()|field()]]. في هذا المثال كان الحقل الأول في ال form يشير الى name data، والثاني يشير الى ال email data، وبعد هذه الحقول ستجد الدالة المستخدمة لإنشاء ال Submit button وهي [[yii\\helpers\\Html::submitButton()]].\n</p>\n\n## <div dir=\"rtl\">لنجرب المثال</div> <span id=\"trying-it-out\"></span>\n\n<p dir=\"rtl\">\n لتشاهد آلية العمل لهذا المثال، والنتائج المتعلقة به، يمكنك إستخدام المتصفح والدخول الى الرابط التالي:\n</p>\n\n```\nhttps://hostname/index.php?r=site%2Fentry\n```\n\n<p dir=\"rtl\">\nعند دخولك الى الرابط السابق، سترى صفحة تعرض Html form يحتوي على حقلين لإدخال المعلومات. أمام كل حقل إدخال ستجد label يشير إلى البيانات المطلوب إدخالها. إذا قمت بالنقر فوق الزر \"submit\" بدون\nأي إدخال، أو إذا لم تقم بكتابة عنوان البريد الإلكتروني بشكل صحيح، فستظهر لك رسالة خطأ بجوار الحقل المقصود.\n</p>\n\n![Form with Validation Errors](../guide/images/start-form-validation.png)\n\n<p dir=\"rtl\">\n    بعد إدخالك لإسم وبريد الكتروني صحيح، وقيامك بالنقر على زر submit، فإنك ستشاهد صفحة جديدة تقوم بعرض البيانات التي قمت بإدخالها.\n</p>\n\n![Confirmation of Data Entry](../guide/images/start-entry-confirmation.png)\n\n## <div dir=\"rtl\">كيف ظهر الخطأ؟ هل هو سحر؟!</div> <span id=\"magic-explained\"></span>\n\n<p dir=\"rtl\">\nقد تتساءل كيف يعمل ال Html form بالخفاء، وقد يبدو ذلك سحرا للوهلة الأولى، فهو يعرض ال label لكل حقل إدخال، ويعرض رسائل الخطأ إذا لم تقم بإدخال البيانات بشكل صحيح، وكل ذلك دون الحاجة لإعادة تحميل الصفحة.\n</p>\n\n<p dir=\"rtl\">\n    إن السحر الموجود لدينا هنا، هو كيفية العمل الخاصة بالشيفرة البرمجية لل form،  والتي تعمل بالخفاء، إن إجراء التحقق عن صحة البيانات يتم في البداية من جانب العميل -client side- وذلك باستخدام الجافا سكربت، ومن ثم -بعد تجاوز التحقق الخاص بالجافا سكربت- بتم تنفيذ التحقق من جانب ال server-side عبر ال PHP. ال  [[yii\\widgets\\ActiveForm]] ذكية بما فيه الكفاية لاستخراج ال rule الخاصة بالتحقق والتي قمت بإنشائها وتعريفها داخل ال <code>EntryForm</code>، ومن ثم تحويل هذه القواعد إلى شيفرة برمجية بالجافا سكربت قابلة للتنفيذ، ومن ثم استخدام هذه الشيفرة من قبل الجافا سكربت لإجراء التحقق من صحة البيانات. في حال قمت بإيقاف الجافا سكربت في المتصفح الخاص بك، سوف يستمر إجراء التحقق من جانب الخادم -server side-، كما هو موضح في ال action المسمى <code>actionEntry()</code>. وهذا يضمن صحة البيانات في جميع الظروف.\n</p>\n\n<blockquote class=\"warning\"><p dir=\"rtl\">\n    تحذير: التحقق من جانب العميل -client side-  يوفر تجربة أفضل للمستخدم، لكن يجب الأخذ بعين الإعتبار أن التحقق من جانب الخادم -server- مطلوب دائمًا، سواء تم التحقق من جانب العميل أم لا.\n</p></blockquote>\n\n<p dir=\"rtl\">\n    يتم إنشاء ال labels الخاصة بحقول الإدخال بواسطة الدالة <code>field()</code>، وذلك من خلال إستخدام أسماء ال property الموجودة داخل ال model. على سبيل المثال، سيتم إنشاء ال label التالي <code>Name</code> للproperty التالية: <code>name</code>.\n</p>\n\n<p dir=\"rtl\">\n    كما يمكنك تعديل ال label الإفتراضي لأي حقل من خلال الشيفرة البرمجية التالية:\n</p>\n\n```php\n<?= $form->field($model, 'name')->label('Your Name') ?>\n<?= $form->field($model, 'email')->label('Your Email') ?>\n```\n\n<blockquote class=\"info\"><p dir=\"rtl\">\n    معلومة: يوفر ال Yii العديد من ال widgets لمساعدتك في إنشاء views معقدة وديناميكية بسرعة. كما أنك ستتعلم في وقت لاحق كيف يمكنك إنشاء widget جديد، وستكتشف أن الموضوع سهل وبسيط، مما سيدفعك إلى كتابة الشيفرة البرمجية الخاصة بك داخل ال widget، والذي بدوره سيجعل من هذه الشيفرة قابلة للتطوير والإستخدام في أكثر من مكان في المستقبل. \n</p></blockquote>\n\n## <div dir=\"rtl\">الخلاصة</div> <span id=\"summary\"></span>\n\n<p dir=\"rtl\">\n    في هذا الجزء من التوثيق، تحدثنا عن كل جزء في ال MVC architectural pattern، لقد تعلمت الآن كيف يمكنك إنشاء model class ليقوم بتمثيل البيانات الخاصة بالمستخدمين، ومن ثم التحقق منها.   \n</p>\n\n<p dir=\"rtl\">\n    لقد تعلمت أيضًا كيفية الحصول على البيانات من المستخدمين، وكيفية عرض البيانات مرة أخرى في المتصفح. هذه المهمة يمكن أن تأخذ الكثير من الوقت عند تطوير أي تطبيق، ولكن، يوفر ال Yii العديد من ال widgets القوية، والتي تجعل من هذه المهمة أمرا سهلا للغاية.\n</p>\n\n<p dir=\"rtl\">\n    في الجزء القادم من هذا التوثيق، ستتعلم كيف يمكنك التعامل مع قواعد البيانات، والتي سنحتاجها -غالبا- مع كل تطبيق ستعمل عليه تقريبا. \n</p>\n"
  },
  {
    "path": "docs/guide-ar/start-gii.md",
    "content": "# <div dir=\"rtl\">إنشاء الشيفرة البرمجية من خلال ال gii</div>\n\n<p dir=\"rtl\">\n    في هذا الجزء التعليمي سنتعرف على آلية التعامل مع ال <a href=\"https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide\">Gii</a>، والذي يستخدم لإنتاج الشيفرة البرمجية الخاصة بمعظم الميزات والخصائص المشتركة في أغلب المواقع بشكل تلقائي، بالإضافة الى ذلك، فإن استخدام ال Gii لإنشاء الشيفرة البرمجية بشكل تلقائي يمثل مجموعة من المعلومات الصحيحة التي بتم إدخالها إعتمادا على التعليمات الموجودة في ال Gii Web Pages.\n</p>\n\n<p dir=\"rtl\">\n    من خلال هذا البرنامج التعليمي، ستتعلم كيفية:\n</p>\n\n<ul dir=\"rtl\">\n    <li>تفعيل ال Gii داخل التطبيق الخاص بك</li>\n    <li>إستخدام ال Gii لإنشاء ال Active Record class</li>\n    <li>إستخدام ال Gii لإنشاء الشيفرة البرمجية الخاصة بال CRUD إعتمادا على الجداول الموجودة في قاعدة البيانات</li>\n    <li>تخصيص (customize) الشيفرة البرمجية التي سيتم إنتاجها من خلال ال Gii.</li>\n</ul>\n\n## <div dir=\"rtl\">البدء باستخدام ال Gii</div> <span id=\"starting-gii\"></span>\n\n<p dir=\"rtl\">\n    يتم تقديم ال <a href=\"https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide\">Gii</a> داخل على ال Yii على أنه <a href=\"../guide/structure-modules.md\">module</a>، ويمكنك تفعيله من خلال الإعدادات الخاصة به والتي تجدها داخل ال application، وبالتحديد داخل ال property التالية [[yii\\base\\Application::modules|modules]]، واعتمادا على كيفية إنشائك للمشروع، فيمكنك إيجاد الشيفرة البرمجية التالية موجودة بشكل مسبق داخل ال <code>config/web.php</code>:  \n</p>\n\n```php\n$config = [ ... ];\n\nif (YII_ENV_DEV) {\n    $config['bootstrap'][] = 'gii';\n    $config['modules']['gii'] = [\n        'class' => 'yii\\gii\\Module',\n    ];\n}\n```\n\n<p dir=\"rtl\">\n    في الإعدادت الموجودة في الأعلى، فإن التطبيق سيقوم بتضمين وتفعيل ال gii في حال كانت الحالة الخاصة بالتطبيق هي <a href=\"../guide/concept-configurations.md#environment-constants\">development enviroment</a>، بالإضافة الى ذلك، فإنه يجب تضمين واستخدام ال module <code>gii</code>، والموجود ضمن ال class التالي [[yii\\gii\\Module]]. \n</p>\n\n\n<p dir=\"rtl\">\n    اذا قمت بالتحقق من ال <a href=\"../guide/structure-entry-scripts.md\">entry script</a> وبالتحديد صفحة ال <code>web/index.php</code> في التطبيق الخاص بك، ستجد هذه الأسطر، والتي يجب أن تجعل من ال <code>YII_ENV_DEV</code> ذات قيمة <code>true</code>.\n</p>\n\n```php\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n```\n\n<p dir=\"rtl\">\n    كل الشكر لهذا السطر البرمجي، التطبيق الآن أصبح بحالة ال development mode، وأصبح لديك ال Gii enabled بالفعل، والآن، يمكنك الوصول الى ال Gii من خلال عنوان ال URL التالي:     \n</p>\n\n```\nhttps://hostname/index.php?r=gii\n```\n\n<blockquote class=\"note\"><p dir=\"rtl\">\n    ملاحظة: إذا كنت تحاول الوصول إلى Gii  من جهاز آخر غير ال localhost، فسيتم رفض الوصول افتراضيًا لأغراض أمنية، ولكن، يمكنك إعداد ال Gii لإضافة مجموعة من ال IP Addresses المسموح لها بالوصول وذلك من خلال: \n</p></blockquote>\n\n```php\n'gii' => [\n    'class' => 'yii\\gii\\Module',\n    'allowedIPs' => ['127.0.0.1', '::1', '192.168.0.*', '192.168.178.20'] // عدل هذه حسب إحتياجاتك\n],\n```\n\n![Gii](../guide/images/start-gii.png)\n\n\n## <div dir=\"rtl\">إنشاء ال Active Record Class من خلال ال Gii</div> <span id=\"generating-ar\"></span>\n\n<p dir=\"rtl\">\n    لإستخدام ال Gii لإنشاء ال Active Record class, قم باختيار ال \"Model Generator\" (من خلال النقر على الرابط الموجود بالصفحة الرئيسية لل Gii)، ومن ثم قم بتعبئة ال form كما يلي: \n</p>\n\n<ul dir=\"rtl\">\n    <li>إسم الجدول: <code>country</code></li>\n    <li><code>Country</code> :Model Class</li>\n</ul>\n\n![Model Generator](../guide/images/start-gii-model.png)\n\n<p dir=\"rtl\">\n    والآن، قم بالنقر على الزر \"Preview\"، ستشاهد الآن <code>models/Country.php</code> قد تم إنشائها وإضافتها الى قائمة النتائج، اذا قمت بالنقر على إسم ال class، فإن المحتوى الخاص بهذا ال class سيتم عرضه.\n</p>\n\n<p dir=\"rtl\">\n    عند استخدام ال Gii، إذا كنت قد قمت بالفعل بإنشاء نفس الملف وستقوم بعمل overwriting عليه، فيمكنك النقر على زر <code>diff</code> الموجود بعد إسم ال class، لتشاهد الفرق بين الشيفرة البرمجية الحالية، والشيفرة البرمجية الجديدة. \n</p>\n\n![Model Generator Preview](../guide/images/start-gii-model-preview.png)\n\n<p dir=\"rtl\">\n    عند قيامك بعمل overwriting على ملف موجود، قم بالضغط على ال ckeckbox الموجودة بجانب كلمة overwrite، ومن ثم قم بالنقر على زر \"Generate\", اذا كان هذا الملف جديد، وغير موجود مسبقا، فيمكنك النقر مباشرة على \"Generate\"، بعد ذلك ستشاهد صفحة ال confirmation والتي تبين الشيفرات البرمجية التي تم إنشائها بنجاح.\n</p>\n\n<p dir=\"rtl\">\n    بعد فيامك بالنقر على زر Generate، فإنك ستشاهد صفحة ال confirmation page، والتي تقوم بدورها بتوضيح الشيفرات البرمجية التي تم إنشائها بنجاح، واذا كان الملف موجود، فإنك ستشاهد أيضا رسالة تعلمك بأن الملف قد تم تعديله وتمت إضافة الشيفرة الجديدة مكان القديمة.\n</p>\n\n## <div dir=\"rtl\">إنشاء ال CRUD Code</div> <span id=\"generating-crud\"></span>\n\n<p dir=\"rtl\">\n    ال CRUD هي اختصار ل Create, Read, Update, And Delete (إنشاء، وقرائة، وتحديث، وحذف)، والتي تمثل أكثر المهمات المطلوبة للتعامل مع البيانات على مواقع الويب. ولإنشاء ال CRUD باستخدام ال Gii، قم باختيار ال \"CRUD Generator\" (من خلال النقر على الرابط الموجود بالصفحة الرئيسية لل Gii)، وهنا وبالبنسبة للمثال الخاص بال \"country\"، يمكنك تعبئة ال from بما يلي:\n</p>\n\n* Model Class: `app\\models\\Country`\n* Search Model Class: `app\\models\\CountrySearch`\n* Controller Class: `app\\controllers\\CountryController`\n\n![CRUD Generator](../guide/images/start-gii-crud.png)\n\n<p dir=\"rtl\">\n    بعد ذلك، قم بالنقر على زر ال \"Preivew\"، وستشاهد قائمة بالملفات التي سيتم إنشائها كما في الصورة أدناه.\n</p>\n\n![CRUD Generator Preview](../guide/images/start-gii-crud-preview.png)\n\n<p dir=\"rtl\">\n    اذا قمت إنشاء الصفحتين <code>controllers/CountryController.php</code> و <code>views/country/index.php</code> عند حديثنا سابقا عند موضوع (التعامل مع قواعد البيانات)، فقم بالضغط على ال \"overwrite\" ليتم إستبدالهم. (الصفحات القديمة لا توجد فيها كل الخصائص التي سيتم إنتاجها من خلال ال Gii CRUD).\n</p>\n\n## <div dir=\"rtl\"> لنجرب المثال </div> <span id=\"trying-it-out\"></span>\n\n<p dir=\"rtl\">\nلتشاهد آلية العمل لهذا المثال، والنتائج المتعلقة به، يمكنك إستخدام المتصفح والدخول الى الرابط التالي:\n</p>\n\n```\nhttps://hostname/index.php?r=country%2Findex\n```\n\n<p dir=\"rtl\">\n    عند دخولك إلى الرابط أعلاه، ستشاهد مجموعة الدول التي تم إستدعائها من جدول ال country من قاعدة البيانات، ويمكنك التعامل مع هذا ال grid من حيث الترتيب أو التصفية بنائا على الشروط التي ستقوم بإدخالها في مربعات النص أعلى الأعمدة.\n</p>\n\n<p dir=\"rtl\">\nلكل دولة تم جلبها وعرضها داخل ال Grid، هناك مجموعة من الخيارات التي يمكنك التعامل معها بشكل إفتراضي، مثل ال view لعرض التفاصيل الخاصة بالدولة المختارة، أو تحديث المعلومات، الخاصة بالدولة، أو حذف هذه الدولة، بالإضافة إلى ذلك يمكنك النقر على \"Create Country\" الموجودة في أعلى ال Grid والتي ستأخذك بدورها الى صفحة تحتوي form لإنشاء ال country.\n</p>\n\n![Data Grid of Countries](../guide/images/start-gii-country-grid.png)\n\n![Updating a Country](../guide/images/start-gii-country-update.png)\n\n<p dir=\"rtl\">\n    فيما يلي قائمة بالملفات التي تم إنشاؤها من خلال ال Gii، في حالة رغبتك في التحقق من كيفية عمل هذه الميزات والإطلاع على الشيفرة البرمجية وتخصيصها حسب الرغبة:\n</p>\n\n* Controller: `controllers/CountryController.php`\n* Models: `models/Country.php` and `models/CountrySearch.php`\n* Views: `views/country/*.php`\n\n<blockquote class=\"info\"><p dir=\"rtl\">\nمعلومة: تم تصميم ال Gii لتكون أداة إنشاء شيفرات برمجية قابلة للتخصيص بشكل كبير للغاية. اذا قمت باستخدامه بحكمة،فإنك ستقوم بتسريع وتيرة التطوير الخاصة بالتطبيق الخاص بك. لمزيد من التفاصيل، يرجى الذهاب إلى الجزء الخاص بال  <a href=\"https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide\">Gii</a>. \n</p></blockquote>\n\n## <div dir=\"rtl\">الخلاصة</div> <span id=\"summary\"></span>\n\n<p dir=\"rtl\">\nفي هذا الجزء من التوثيق، لقد تعلمنا آلية استخدام ال Gii لإنشاء الشيفرة البرمجية الخاصة بال CRUD، وتحدثنا عن الوظائف التي تقوم فيها، وكيف يمكننا من خلالها إتمام العمليات الخاصة بالبيانات من إدخال وتحديث وحذف وعرض للبيانات من قاعدة البيانات.\n</p>\n"
  },
  {
    "path": "docs/guide-ar/start-hello.md",
    "content": "# <div dir=\"rtl\">قل مرحبا - Saying Hello</div>\n\n<p dir=\"rtl\">\n    في هذا الموضوع سنتعرف على كيفية إنشاء صفحة \"Hello\" جديدة في التطبيق الذي قمت بتثبيته، ولتحقيق ذلك، يجب عليك القيام بإنشاء <a href=\"../guide/structure-controllers.md#creating-actions\">action</a> و <a href=\"../guide/structure-views.md\">view</a> لهذه الصفحة:\n</p>\n\n<ul dir=\"rtl\">\n    <li>سيقوم التطبيق بإرسال ال request الخاص بالصفحة إلى ال action.</li>\n    <li>وسيقوم ال action بدوره في جلب ال view التي تعرض كلمة \"Hello\" إلى المستخدم النهائي.</li>\n</ul>\n\n<p dir=\"rtl\">\n    من خلال هذا البرنامج التعليمي ، ستتعلم ثلاثة أشياء: \n</p>\n\n<ol dir=\"rtl\">\n    <li>كيفية إنشاء <a href=\"../guide/structure-controllers.md#creating-actions\">action</a> ليقوم بإستقبال ال request ومن ثم الرد (respond) عليها.</li>\n    <li>كيفية إنشاء <a href=\"../guide/structure-views.md\">view</a> وإضافة المحتوى الى ال respond.</li>\n    <li>و كيفية إنشاء التطبيق لل requests التي يوجهها لل <a href=\"../guide/structure-controllers.md#creating-actions\">actions</a>. </li>\n</ol>\n\n##  <div dir=\"rtl\">إنشاء ال Action</div> <span id=\"creating-action\"></span>\n\n<p dir=\"rtl\">\n    لإنشاء صفحة \"Hello\"، ستقوم بإنشاء <code>say</code> <a href=\"../guide/structure-controllers.md#creating-actions\">action</a> والذي بدوره سيقوم  بقراءة ال <code>message</code> parameter من ال request، ومن ثم عرض ال <code>message</code> مرة أخرى إلى المستخدم. إذا كان ال request لا يحمل معه ال message parameter فإن ال action سيقوم بطباعة message إفتراضية وهي \"Hello\".\n</p>\n\n<blockquote class=\"info\"><p dir=\"rtl\">\n    معلومة: ال <a href=\"../guide/structure-controllers.md#creating-actions\">Actions</a> هي الكائنات(objects) التي يمكن للمستخدمين من الوصول اليها وتنفيذ ما في بداخلها بشكل مباشر.  يتم تجميع هذه ال Actions بواسطة ال <a href=\"../guide/structure-controllers.md\">controllers</a>. ونتيجة لذلك فإن ال response الراجعة للمستخدم ستكون هي نتيجة التنفيذ الخاصة بال action. \n</p></blockquote>\n\n<p dir=\"rtl\">\n    يجب تعريف ال actions داخل ال <a href=\"../guide/structure-controllers.md\">controller</a>، ولتبسيط الفكرة، سنقوم بتعريف ال <code>say</code> action داخل أحد ال controller الموجود مسبقا وهو ال <code>siteController</code>. هذا ال controller ستجده داخل المسار <code>controllers/siteController.php</code>. ومن هنا سنبدأ بإضافة ال action الجديد: \n</p>\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    // ...existing code...\n\n    public function actionSay($message = 'Hello')\n    {\n        return $this->render('say', ['message' => $message]);\n    }\n}\n```\n\n<p dir=\"rtl\">\n    في الشيفرة البرمجية السابقة ، تم تعريف ال <code>say</code> action من خلال إنشاء الدالة <code>actionSay</code> داخل الكلاس  <code>siteController</code>. يستخدم ال Yii كلمة action ك prefix للدوال للتميز بين الدوال ال action و ال non-action في ال controller، كما يستخدم الإسم الخاص بال action مباشرة بعد ال prefix، ويتم عمل mapping بين ال action method name وال action id ليستطيع المستخدم من الوصول للدالة المنشئة من خلال ال action id. \n</p>\n\n<p dir=\"rtl\">\n    عندما يتعلق الأمر بتسمية ال action الخاصة بك، يجب عليك  أن تفهم كيف يقوم ال Yii بالتعامل مع ال action id، ال action id يجب أن يكون دائما أحرف lower case، وإذا إحتوى ال action id على عدة كلمات فإن الفاصل بينهم سيكون الداش (dashes)  مثل  <code>create-comment</code>، ويتم ربط ال action id بال action method name من  خلال حذف ال dashes من ال IDs، ويتم عمل capitalizing لأول خرف من كل كلمة، وإضافة ال prefix الى الناتج السابق من التحويل. مثال: ال action id المسمى ب <code>create-comment</code> سيتم تحويله الى ال action method name التالي: <code>actionCreateComment</code>. \n</p>\n\n<p dir=\"rtl\">\n    في المثال السابق، يقوم ال action method على إستقبال parameter يسمى ب <code>$message</code>، والذي يملك قيمة إفتراضية وهي <code>\"Hello\"</code> (وهي مشابة تماما لطريقة وضع القيم الإفتراضية لل argument في ال PHP). عندما يستقبل التطبيق ال request ويحدد أن ال action المطلوب للتعامل مع الطلب (request) هو <code>say</code>، فإن التطبيق سيقوم بإسناد القيمة الموجودة بال request الى ال parameter الموجود بال action بشرط أن يكون الإسم الموجود بال request هو نفسه الموجود في ال action. ولتبسيط الموضوع يمكن القول أن ال request اذا احتوى على كلمة <code>message</code> وقيمة هذا ال parameter هي <code>\"GoodBye\"</code>، فإن ال <code>$message</code> الموجودة في ال action ستصبح قيمتها <code>\"GoodBye\"</code>.  \n</p>\n\n\n<p dir=\"rtl\">\n    من خلال ال action method، يتم استدعاء ال  [[yii\\web\\Controller::render()|render()]] لتقديم\nالملف الخاص بال view والمسمى هنا ب <code>say</code>. أيضا فإن ال <code>message</code> يتم تمرريرها الى ال view مما يسمح لك باستخدام هذا ال parameter داخل ال view. النتيجة المرجعة لل view تتم معالجتها وإرجاعها من خلال ال action method، وهذه النتائج سيتم إستقبالها من خلال المتصفح الخاص بالمستخدم ليتم عرضها وكأنها (جزء من صفحة Html كاملة). \n</p>\n\n## <div dir=\"rtl\">إنشاء ال View</div> <span id=\"creating-view\"></span>\n\n<p dir=\"rtl\">\n    ال <a href=\"../guide/structure-views.md\">Views</a> هي شيفرات برمجية كتبت ﻹنشاء المحتوى المناسب بنائا على ال response الراجع اليها من خلال ال action. \n    بالنسبة إلى مثال \"Hello\" ، الآن سنقوم بإنشاء view مسمى ب <code>say</code>، والذي سيقوم بدوره بطباعة ال <code>message</code> التي تم إستلامها من ال action، شاهد المثال: \n</p>\n\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n<?= Html::encode($message) ?>\n```\n\n<p dir=\"rtl\">\n    صفحة ال view <code>say</code> يجب أن يتم حفظها داخل المسار التالي: <code>views/site/say.php</code>. عندما يتم إستدعاء الدالة [[yii\\web\\Controller::render()|render()]] من قبل ال action، فإنه سينظر للمسار على الشكل التالي: <code>views/ControllerID/ViewName.php</code>، بحيث يكون في مثالنا السابق ال ContollerID هو ال <code>site</code> وال viewName هو <code>say</code>.\n</p>\n\n<p dir=\"rtl\">\n    ملاحظة:  في الشيفرة البرمجية أعلاه، تكون ال <code>message</code> مضمنة داخل ال  [[yii\\helpers\\Html::encode()]] قبل أن يتم طباعتها، هذا الأمر ضروري لأن ال parameter التي تأتي من المستخدم النهائي لا يجب الوثوق بها،  فهي يمكن أن تحتوي على شيفرات برمجية تستغل الضعف الحاص بك بموضوع الأمان مثل <a href=\"https://en.wikipedia.org/wiki/Cross-site_scripting\">vulnerable to XSS attack</a> عن طريق دمج JS code مع ال parameter. \n</p>\n\n<p dir=\"rtl\">\n    وبطبيعة الحال، يمكنك وضع المزيد من المحتوى في صفحة ال <code>say</code> view. ويمكن أن يتكون هذا المحتوى من HTML tag و plain text وحتى PHP statements.\nفي الواقع، تعد ال <code>say</code> view مجرد شيفرة برمجية بلغة ال PHP والتي يتم تنفيذها بواسطة [[yii\\web\\Controller::render()|render()]].\nالمحتوى الذي سيتم طباعته من خلال ال view سيتم إرجاعه الى التطبيق من خلال ال response، وسيقوم التطبيق بدوره بإخراج هذه النتيجة إلى المستخدم النهائي.\n</p>\n\n\n## <div dir=\"rtl\">تطبيق المثال</div> <span id=\"trying-it-out\"></span>\n-------------\n\n<p dir=\"rtl\">\nبعد إنشاء ال action وصفحة ال view،  يمكنك الوصول إلى الصفحة الجديدة عن طريق  عنوان URL التالي:    \n</p>\n\n```\nhttps://hostname/index.php?r=site%2Fsay&message=Hello+World\n```\n\n![Hello World](../guide/images/start-hello-world.png)\n\n<p dir=\"rtl\">\n    سينتج عن هذا ال URL صفحة تعرض \"Hello World\". هذه الصفحة لديها نفس ال Header و ال Footer لصفحات التطبيق الأخرى.\n</p>\n\n<p dir=\"rtl\">\n    إذا قمت بحذف ال <code>message</code> parameter من ال URL ، فسترى الصفحة تعرض كلمة <code>\" Hello \"</code> فقط. ويرجع ذلك إلى أن \"message\" يتم تمريرها ك parameter إلى ال  <code>actionSay()</code>، وعندما يتم حذفها، سيتم استخدام القيمة الافتراضية وهي <code>\"Hello\"</code> بدلاً من ذلك.\n</p>\n\n<blockquote class=\"info\"><p dir=\"rtl\">\n    معلومة: تتشارك الصفحة الجديدة مع الصفحات الأخرى بالتطبيق بنفس ال Header وال Footer وذلك بسبب الدالة  [[yii\\web\\Controller::render() | render ()]] والتي ستقوم بشكل تلقائي بتضمين النتيجة الخاصة بصفحة ال view <code>say</code> مع صفحة ال <a href=\"../guide/structure-views.md#layouts\">Layout</a>، والتي يمكنك أن تجدها داخل المسار التالي: <code>views/layouts/main.php</code>\n</p></blockquote>\n\n<p dir=\"rtl\">\n    ال <code>r</code> الموجودة في ال URL أعلاه يتطلب الكثير من الشرح. ولكن باختصار يمكن القوم أنها اختصار ل <a href=\"../guide/runtime-routing.md\">route</a> ، وهو معرف فريد من نوعه(unique ID) للتطبيق بحيث يقوم بالتأشير على ال action، والصيغة الخاصة بال route هي <code>ControllerID/ActionID</code>. عندما يتلقى التطبيق request، فإنه سيتم التحقق من <code>r</code> parameter، فيقوم باستخدام الجزء <code>ControllerID</code> لتحديد ال controller class المطلوب ليقوم بعمل instance منه، ومن ثم يقوم ال controller باستخدام ال <code>\"ActionID\"</code> لتحديد ال action المطلوب والذي سيقوم بالعمل الفعلي.\n في المثال الخاص بنا، فإن ال route هو  <code>\"site/say\"</code>، وهذا ال route يتم معالجته ليستدعي ال controller class المسمى ب <code>SiteController</code> و ال action المسمى ب <code>actionSay</code> داخل هذا ال controller، ونتيجة لذلك سيتم فعليا إستدعاء الدالة  <code>SiteController::actionSay()</code> لتقوم بمعالجة ال request كما يلزم. \n</p>\n\n<blockquote><p dir=\"rtl\">\n    معلومة: مثل ال actions، تحتوي ال controllers أيضًا على Uniquely IDs يتم تعريفها واستخدامها من خلال التطبيق. تستخدم ال Controllers IDs نفس قواعد التسمية الخاصة بال action IDs، ويتم ذلك من خلال حذف ال dashes و capitalizing أول حرف من كل كلمة، ثم إضافة كلمة <code>Controller</code> الى الإسم الناتج -وهنا الإختلاف عن ال action ID-. مثال: ال controller ID المسمى ب <code>\"post-comment\"</code> ستم معالجته ليصبح <code>PostCommentController</code>. \n</p></blockquote>\n\n\n## <div dir=\"rtl\">الخلاصة</div> <span id=\"summary\"></span>\n\n<p dir=\"rtl\">\n    في هذا الموضوع، قمنا بالتعرف على ال controller وال view كأجزاء من MVC architectural pattern، كما قمنا بإنشاء action داخل controller موجود ليستقبل  specific request ويتحكم فيه، وقمنا أيضا بإنشاء view لعرض المحتوى. في هذا المثال البسيط، لم نتطرق الى ال model، وقمنا فقط باستخدام  ال data بشكل مباشر من خلال ال <code>message</code> parameter. \n</p>\n\n<p dir=\"rtl\">\n    كما تعرفنا أيضا على ال routes في ال Yii، والتي تعمل بدورها كجسر بين ال user request وال controller actions. \n</p>\n\n<p dir=\"rtl\">\n    في الموضوع القادم، ستتعلم كيف يمكنك إنشاء model وكيف يمكنك إنشاء صفحة جديدة تحتوي على Html form.\n</p>\n"
  },
  {
    "path": "docs/guide-ar/start-installation.md",
    "content": "# <div dir=\"rtl\">تثبيت ال Yii</div>\n\n<p dir=\"rtl\">يمكنك تثبيت ال Yii بطريقتين ، الأولى باستخدام مدير الحزم <a href=\"https://getcomposer.org\">Composer</a> أو عن طريق تنزيل Archive File. الطريقة الأولى هي الطريقة المفضلة للعمل، ، لأنها تتيح لك تثبيت [<a href=\"../guide/structure-extensions.md\">extensions</a> - ملحقات أو اضافات] جديدة، أو تحديث إطار العمل Yii ببساطة عن طريق تشغيل أمر واحد فقط.\n</p>\n\n<p dir=\"rtl\">\n    التثبيت الإفتراضي لل Yii ينتج عنه بنية تركيبة منظمة ومرتبة للمجلدات والملفات التي بداخلها، ويوفر هذا الكلام بعض المميزات التي يتم إضافتها وإنشائها بشكل تلقائي مثل صفحة تسجيل الدخول، ونموذج اتصل بنا...الخ، هذا الأمر سيشكل نقطة إنطلاق جيدة لبدء العمل على أي مشروع.\n</p>\n    \n<p dir=\"rtl\">\n    في هذه الصفحة من التوثيق سنقوم بشرح ووصف كيف يمكن تثبيت إطار العمل Yii وبالتحديد Yii2 Basic Project Template.\n    هناك Template آخر موجود بإطار العمل Yii وهو <a href=\"https://www.yiiframework.com/extension/yiisoft/yii2-app-advanced/doc/guide\">Yii2 Advanced Project Template</a>، وهو الأفضل للعمل وإنشاء المشاريع لفريق عمل برمجي، ولتطوير المشاريع متعددة الطبقات(multiple tires). \n</p>\n\n<blockquote class=\"info\"><p dir=\"rtl\">\nمعلومة: قالب المشروع الأساسي (Basic) مناسب لتطوير 90% من تطبيقات الويب. ويختلف القالب المتقدم (Advanced Template) عن القالب الأساسي في كيفية تنظيم وهيكلة الشيفرة البرمجية.\nاذا كنت جديدا في عالم تطوير تطبيقات الويب باستخدام ال Yii، فإننا نوصيك بقوة بأن تستخدم القالب الأساسي في بناء المشروع الخاص بك.\n</p></blockquote>\n\n\n## <div dir=\"rtl\">تثبيت ال Yii من خلال (Composer)</div> <span id=\"installing-via-composer\"></span>\n\n### <div dir=\"rtl\">تثبيت ال Composer</div>\n\n<p dir=\"rtl\">\nإن لم يكن لديك Composer مثبت مسبقا، فيمكنك السير بخطوات تثبيته من خلال الدخول الى هذا الرابط <a href=\"https://getcomposer.org/download/\">https://getcomposer.org/download/</a>.\nلتثبيت ال Composer في كل من نظامي Linux و Max OS X، يمكنك تنفيذ الأوامر التالية: \n</p>\n\n```bash\ncurl -sS https://getcomposer.org/installer | php\nsudo mv composer.phar /usr/local/bin/composer\n```\n<p dir=\"rtl\">\n    ولنظام ويندوز يمكنك تثبيت ال <a href=\"https://getcomposer.org/Composer-Setup.exe\">Composer-Setup.exe</a> ومن ثم عمل run \n</p>\n\n<p dir=\"rtl\">\nيرجى الدخول الى <a href=\"https://getcomposer.org/doc/articles/troubleshooting.md\">Troubleshooting section of the Composer Documentation</a> في حال واجهتك أي مشاكل متعلقة بال composer, وإذا كنت مستخدمًا جديدًا لل composer، ننصحك أيضًا بقراءة <a href=\"https://getcomposer.org/doc/01-basic-usage.md\">قسم الاستخدام الأساسي</a> على الأقل من التوثيف الخاص بال composer. \n</p>\n\n<p dir=\"rtl\">\n    في هذا الدليل ، نفترض أنك قمت بتثبيت ال composer على مستوى جميع المشاريع (<a href=\"https://getcomposer.org/doc/00-intro.md#globally\">globally</a>)  بحيث تكون أوامر ال composer متاحة لجميع المشاريع من أي مكان. أما إذا كنت تستخدم ال composer.phar لمسار محدد فقط(local directory)،  فيجب عليك ضبط  الأومر وفقًا لذلك.\n</p>\n\n<p dir=\"rtl\">\nإذا كان ال composer مثبتًا من قبل، فتأكد من استخدام إصدار حديث. يمكنك تحديث ال composer عن طريق تنفيذ الأمر التالي <code>composer self-update</code>\n</p>\n\n<blockquote class=\"note\"><p dir=\"rtl\">\n  ملاحظة مهمة: أثناء تثبيت ال Yii ، سيحتاج ال composer إلى طلب(request) الكثير من المعلومات من ال Github Api. يعتمد عدد الطلبات على عدد dependencies التي يمتلكها التطبيق الخاص بك، وقد يكون هذا العدد أكبر من الحد المسموح به من قبل ال Github Api <b>(Github API rate limit)</b>. إذا وصلت الى الحد الأعلى المسموح به من الطلبات، فقد يطلب منك ال composer بيانات تسجيل الدخول إلى Github، وذلك للحصول على رمز (token) للدخول إلى Github Api. اذا كانت عمليات الإتصال سريعة، فقد تصل إلى هذا الحد(limit) قبل أن يتمكن ال composer من التعامل معه ، لذالك نوصي بتكوين رمز الدخول(access token) قبل تثبيت ال Yii. يرجى الرجوع إلى التوثيق الخاص بال Composer والإطلاع على التعليمات الخاصة <a href=\"https://getcomposer.org/doc/articles/troubleshooting.md#api-rate-limit-and-oauth-tokens\">Github API tokens</a> للحصول على الإرشادات اللازمة للقيام بذلك. \n</p></blockquote>\n\n### <div dir=\"rtl\">تثبيت ال Yii</div> <span id=\"installing-from-composer\"></span>\n\n<p dir=\"rtl\">\n    من خلال ال Composer، يمكنك الآن تثبيت ال Yii من خلال تنفيذ سطر الأوامر التالي داخل أي مسار يمكن الوصول اليه من قبل الويب\n</p>\n\n```bash\ncomposer create-project --prefer-dist yiisoft/yii2-app-basic basic\n```\n<p dir=\"rtl\">\n      سطر الأوامر السابق سيقوم بتثبيت أحدث نسخة مستقرة(stable) من إطار العمل Yii داخل مسار جديد اسمه basic، ويمكنك التعديل على سطر الأوامر السابق لتغيير اسم المشروع لأي اسم ترغب فيه.  \n</p>\n\n<blockquote class=\"info\"><p dir=\"rtl\">\nمعلومة: اذا واجهتك أي مشكلة عند تنفيذ السطر `composer create-project` فيمكنك الذهاب إلى <a href=\"https://getcomposer.org/doc/articles/troubleshooting.md\">قسم استكشاف الأخطاء في ال composer</a>.\nفي معظم الأخطاء الشائعة، وعند حل المشكلة أو الخطأ، يمكنك إكمال التثبيت من خلال الدخول الى المسار `basic` ومن ثم تنفيذ الأمر التالي: `composer update`.\n</p></blockquote>\n\n<blockquote class=\"tip\"><p dir=\"rtl\">\n    تلميح: اذا كنت ترغب بتثبيت أحدث نسخة خاصة بالمطورين من ال Yii، فيمكنك ذلك من خلال إضافة الخيار <a href=\"https://getcomposer.org/doc/04-schema.md#minimum-stability\">stability</a> وذلك من خلال سطر الأوامر التالي:\n</p></blockquote>  \n\n```bash\n    composer create-project --prefer-dist --stability=dev yiisoft/yii2-app-basic basic\n```\n<blockquote class=\"note\"><p dir=\"rtl\">\n    ملاحظة: نسخة المطورين من ال Yii يجب أن يتم إستخدامها للمواقع الإلكترونية التي لن تصدر كنسخة نهائية للمستخدم(Not for production) لأن ذلك يمكن أن يسبب بإيقاف المشروع أو الشيفرة البرمجية الخاصة بك. \n</p></blockquote>\n\n### <div dir=\"rtl\">تثبيت ال Yii من خلال ال Archive File</div> <span id=\"installing-from-archive-file\"></span>\n--------------------------\n\n<p dir=\"rtl\">\nيتضمن تثبيت Yii من ملف أرشيف ثلاث خطوات وهي:\n</p>\n<ol dir=\"rtl\">\n<li> تثبت الملف من خلال الموقع الرسمي <a href=\"https://www.yiiframework.com/download/\">yiiframework.com</a>.</li>\n<li> قم بفك ضغط الملف الذي تم تنزيله إلى مجلد يمكن الوصول إليه عبر الويب.</li>\n<li> قم بتعديل ملف <code>config/web.php</code> عن طريق إدخال secret key ل<code> cookieValidationKey</code>\n(يتم ذلك تلقائيًا إذا قمت بتثبيت ال Yii باستخدام Composer): </li>\n</ol>\n\n   ```php\n   // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation\n   'cookieValidationKey' => 'enter your secret key here',\n   ```\n\n\n### <div dir=\"rtl\">خيارات تثبيت أخرى</div> <span id=\"other-installation-options\"></span>\n--------------------------\n\n<p dir=\"rtl\">\nتوضح تعليمات التثبيت أعلاه كيفية تثبيت ال Yii ، والذي يقوم أيضًا بإنشاء تطبيق ويب أساسي(basic).\nهذا النهج هو نقطة انطلاق جيدة لمعظم المشاريع، صغيرة كانت أو كبيرة. خصوصا اذا كنت قد بدأت تعلم ال Yii من وقت قريب.\n<br /><br />\nلكن، هناك خيارات أخرى متاحة لتثبيت ال Yii وهي: \n</p>\n\n<ul dir=\"rtl\">\n<li> إذا كنت ترغب فقط في تثبيت ال core لإطار العمل Yii، وترغب ببناء المكونات الخاصة بإطار العمل كما ترغب أنت وبطريقتك أنت، يمكنك اتباع التعليمات كما هو موضح في هذه الصفحة <a href=\"../guide/tutorial-start-from-scratch.md\"> Building Application from Scratch</a>.</li>\n<li> إذا كنت تريد البدء بتطبيق أكثر تعقيدًا وأكثر إحترافية، ويتناسب بشكل أفضل مع وجود فريق عمل تقني،\nفأنت اذا سترغب بتثبيت ال <a href=\"https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md\">Advanced Project Template</a>\n</li>\n</ul>\n\n\n### <div dir=\"rtl\">تثبيت ال Assets</div> <span id=\"installing-assets\"></span>\n--------------------------\n\n<p dir=\"rtl\">\n    تعتمد ال Yii على حزم <a href=\"https://bower.io/\">Bower</a> و/أو <a href=\"https://www.npmjs.com/\">NPM</a> لتثبيت مكتبات ال (CSS و JavaScript). ويستخدم ال composer للحصول على هذه المكتبات ، مما يسمح بالحصول على إصدارات ال PHP و CSS/JavaScript في نفس الوقت. ويمكن تحقيق ذلك إما عن طريق استخدام <a href=\"https://asset-packagist.org/\">asset-packagist.org</a> أو من خلال ال <a href=\"https://github.com/fxpio/composer-asset-plugin\">composer asset plugin</a>، يرجى الرجوع إلى <a href=\"../guide/structure-assets.md\">Assets documentation</a> لمزيد من التفاصيل.\n<br /><br />\nقد ترغب في إدارة ال assets عبر  ال native Bower/NPM أو استخدام ال CDN أو تجنب تثبيت ال assets بالكامل من حلال ال Composer ، ويمكن ذلك من خلال إضافة الأسطر التالية إلى \"composer.json\":\n</p>\n\n```json\n\"replace\": {\n    \"bower-asset/jquery\": \">=1.11.0\",\n    \"bower-asset/inputmask\": \">=3.2.0\",\n    \"bower-asset/punycode\": \">=1.3.0\",\n    \"bower-asset/yii2-pjax\": \">=2.0.0\"\n},\n```\n\n<blockquote class=\"note\"><p dir=\"rtl\">\nملاحظة: في حالة تجاوز تثبيت ال assets عبر ال Composer، فأنت المسؤول عن تثبيت ال assets وحل مشكلات التعارض بين الإصدارات والمكتبات المختلفة. وكن مستعدًا لعدم تناسق محتمل بين ملفات ال asstes والإضافات المختلفة.\n</p></blockquote>\n\n### <div dir=\"rtl\">التحقق من التثبيت</div> <span id=\"verifying-installation\"></span>\n--------------------------\n\n<p dir=\"rtl\">\n    بعد الانتهاء من التثبيت، ستحتاج الى القيام بإعداد خادم الويب الخاص بك(your web server) (انظر القسم التالي) أو قم باستخدام <a href=\"https://www.php.net/manual/en/features.commandline.webserver.php\">built-in PHP web server</a> عن طريق تنفيذ الأمر التالي داخل المسار web في المشروع الخاص بك: \n</p>\n \n```bash\nphp yii serve\n```\n\n<blockquote class=\"note\"><p dir=\"rtl\">\nملاحظة: افتراضيًا ال HTTP-server يعمل على البورت 8080. ومع ذلك ، إذا كان هذا البورت قيد الاستخدام بالفعل أو كنت ترغب في تشغيل أكثر من تطبيق بهذه الطريقة، حينها سيلزمك تحديد البورت الذي يجب استخدامه. ما عليك سوى إضافة --port:\n</p></blockquote>\n\n```bash\nphp yii serve --port=8888\n```\n<p dir=\"rtl\">\n    يمكنك استخدام الرابط الموجود في الأسفل للوصول الى تطبيق ال Yii الذي قمت بتثبيته وتنفيذ الأوامر السابقة عليه.\n</p>\n\n```\nhttp://localhost:8080/\n```\n\n![Successful Installation of Yii](../guide/images/start-app-installed.png)\n\n<p dir=\"rtl\">\n    اذا كانت كل الإعدادات السابقة تعمل بشكل صحيح، فيجب أن ترى الصورة الموجودة بالأعلى \"Congratulations!\" على المتصفح. إذا لم يكن كذلك، يرجى التحقق مما إذا كان تثبيت الPHP الخاص بك متوافق مع متطلبات ال Yii. يمكنك التحقق من ذلك باستخدام أحد الأساليب التالية:\n</p>\n\n<ul dir=\"rtl\">\n    <li>قم بنسخ الملف <code>/requirements.php</code> الى المسار <code>/web/requirements.php</code> بحيث يمكنك الوصول الى الصفحة من خلال الرابط التالي: <code>http://localhost/requirements.php</code></li>\n    <li>قم بتنفيذ الأوامر التالية: <br /><code>\n        cd basic\n        </code><br /><code>php requirements.php</code></li>\n</ul>\n\n<p dir=\"rtl\">\n    يجب عليك أن تقوم بتثبيت وإعداد ال PHP الخاص بك بحيث تلبي الحد الأدنى من متطلبات ال Yii. الأهم من ذلك يجب أن يكون الإصدار الخاص بال PHP أعلى أو يساوي 5.4. من الناحية المثالية أحدث إصدار  يعمل مع ال Yii هو ال PHP 7. يجب عليك أيضًا تثبيت ال <a href=\"https://www.php.net/manual/en/pdo.installation.php\">PDO PHP Extension</a>.\n</p>\n\n\n### <div dir=\"rtl\">إعداد ال Web Servers</div> <span id=\"configuring-web-servers\"></span>\n-----------------------\n\n<blockquote class=\"info\"><p dir=\"rtl\">\nمعلومة: يمكنك تخطي هذا الجزء الآن إذا كنت تختبر فقط إطار العمل Yii دون أي نية لنشر هذا التطبيق على الويب(بدون رفع التطبيق على production server).\n</p></blockquote>\n\n<p dir=\"rtl\">\n    يجب أن يعمل التطبيق الذي تم تثبيته وفقًا للتعليمات المذكورة أعلاه مع أي من الخوادم ال <a href=\"https://httpd.apache.org/\">Apache HTTP</a> أو ال <a href=\"https://nginx.org/\">Nginx HTTP</a> في كل من أنظمة التشغيل Windows, Mac OS X أو Linux ممن لديها إصدار أعلى أو يساوي PHP 5.4، كما أن ال Yii 2.0 متوافق مع ال Facebook <a href=\"https://hhvm.com/\">HHVM</a>، لكن، يجب أن تأخذ بعين الإعتبار أن ال HHVM يسلك في بعض الأحيان بطريقة مختلفة عن ال Native PHP، لذلك يجب أن تأخذ عناية إضافية عندما تعمل على ال HHVM.\n</p>\n\n<p dir=\"rtl\">\n    على ال production server، قد ترغب في إعداد خادم الويب الخاص بك بحيث يمكن الوصول إلى التطبيق\nالخاص بك عبر ال URL التالي <code>https://www.example.com/index.php</code> بدلاً من <code>https://www.example.com/basic/web/index.php</code>. هذا الكلام يتطلب إنشاء إعداد يقوم بتوجيه ال document root الموجود على ال web server الى مجلد ال basic/web، كما قد ترغب أيضا بإخفاء ال <code>index.php</code> من ال URL كما هو موضح في ال <a href=\"../guide/runtime-routing.md\">Routing and URL Creation</a>. في هذا الموضوع ستتعلم كيف يمكنك إعداد ال Apache أو ال Nginx server لتحقيق هذه الأهداف. \n</p>\n\n<blockquote class=\"info\"><p dir=\"rtl\">\n    معلومة: من خلال تعيين ال <code>basic/web</code> ك <code>document root</code>، فإنك بذلك تمنع أيضًا المستخدمين النهائيين من الوصول الى الشيفرة البرمجية الخاصة بالتطبيق الخاص بك، وتمنعهم من الوصول الى الملفات الحساسة والمهمة والمخزنة في sibling directories من <code>basic/web</code>، ويعبر رفض الوصول الى المجلدات الأخرى تحسينا أمنيا مهما، يساعد في الحفاظ على مستوى أعلى من الحماية.\n</p></blockquote>\n\n<blockquote class=\"info\"><p dir=\"rtl\">\nمعلومة: إذا كان سيتم تشغيل التطبيق الخاص بك في بيئة استضافة مشتركة(shared hosting) حيث ليس لديك الصلاحية لتعديل الإعدادات الخاصة بال web server، ستحتاج حينها الى تعديل في البنية الخاصة بالمشروع للحصول على أفضل أمان ممكن. يرجى الرجوع إلى <a href=\"../guide/tutorial-shared-hosting.md\">Shared Hosting Environment</a> لمزيد من المعلومات. \n</p></blockquote>\n\n<blockquote class=\"info\"><p dir=\"rtl\">\n    معلومة: إذا كنت تقوم بتشغيل تطبيق ال Yii بوجود ال proxy، فقد تحتاج إلى إعداد التطبيق ليكون ضمن ال <a href=\"../guide/runtime-requests.md#trusted-proxies\">trusted proxies and header</a>.\n</p></blockquote>\n\n### <div dir=\"rtl\">الإعدادات الموصى بها لل Apache</div> <span id=\"recommended-apache-configuration\"></span>\n-----------------------\n\n<p dir=\"rtl\">\n    استخدم الإعدادات التالية في ملف ال <code>httpd.conf</code> في Apache أو ضمن إعدادات ال virtual host.\n    ملاحظة:  يجب عليك استبدال المسار التالي <code>path/to/basic/web</code> بالمسار الفعلي للتطبيق الخاص بك وصولا الى ال <code>basic/web</code>.\n</p>\n\n```apache\n# Set document root to be \"basic/web\"\nDocumentRoot \"path/to/basic/web\"\n\n<Directory \"path/to/basic/web\">\n    # use mod_rewrite for pretty URL support\n    RewriteEngine on\n    # If a directory or a file exists, use the request directly\n    RewriteCond %{REQUEST_FILENAME} !-f\n    RewriteCond %{REQUEST_FILENAME} !-d\n    # Otherwise forward the request to index.php\n    RewriteRule . index.php\n\n    # if $showScriptName is false in UrlManager, do not allow accessing URLs with script name\n    RewriteRule ^index.php/ - [L,R=404]\n\n    # ...other settings...\n</Directory>\n```\n\n\n### <div dir=\"rtl\">الإعدادات الموصى بها لل Nginx</div> <span id=\"recommended-nginx-configuration\"></span>\n-----------------------\n\n<p dir=\"rtl\">\n    لاستخدام <a href=\"https://wiki.nginx.org/\">Nginx</a>، يجب تثبيت PHP على أنه <a href=\"https://www.php.net/install.fpm\">FPM SAPI</a>، ويمكنك استخدام إعدادات ال Nginx التالية، مع الإنتباه على استبدال المسار من  <code>path/to/basic/web</code> الى المسار الفعلي  وصولا إلى <code>basic/web</code> بالإضافة الى إستبدال <code>mysite.test</code> إلى ال hostname الخاص بالتطبيق.\n</p>\n\n\n```nginx\nserver {\n    charset utf-8;\n    client_max_body_size 128M;\n\n    listen 80; ## listen for ipv4\n    #listen [::]:80 default_server ipv6only=on; ## listen for ipv6\n\n    server_name mysite.test;\n    root        /path/to/basic/web;\n    index       index.php;\n\n    access_log  /path/to/basic/log/access.log;\n    error_log   /path/to/basic/log/error.log;\n\n    location / {\n        # Redirect everything that isn't a real file to index.php\n        try_files $uri $uri/ /index.php$is_args$args;\n    }\n\n    # uncomment to avoid processing of calls to non-existing static files by Yii\n    #location ~ \\.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ {\n    #    try_files $uri =404;\n    #}\n    #error_page 404 /404.html;\n\n    # deny accessing php files for the /assets directory\n    location ~ ^/assets/.*\\.php$ {\n        deny all;\n    }\n\n    location ~ \\.php$ {\n        include fastcgi_params;\n        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;\n        fastcgi_pass 127.0.0.1:9000;\n        #fastcgi_pass unix:/var/run/php5-fpm.sock;\n        try_files $uri =404;\n    }\n\n    location ~* /\\. {\n        deny all;\n    }\n}\n```\n<p dir=\"rtl\">\n    عند استخدامك لهذا الإعداد، يجب عليك أيضًا تعيين <code>cgi.fix_pathinfo = 0</code> في ملف php.ini\n    من أجل تجنب العديد من طلبات ال <code>stat()</code>  الغير الضرورية للنظام.\n</p>\n\n<p dir=\"rtl\">\n    لاحظ أيضًا أنه عند تشغيل خادم HTTPS، تحتاج إلى إضافة  <code>fastcgi_param HTTPS on;</code>\nبحيث يمكنك إكتشاف إذا ما كان الاتصال آمنًا أم لا.\n</p>\n"
  },
  {
    "path": "docs/guide-ar/start-looking-ahead.md",
    "content": "# <div dir=\"rtl\">ماذا الآن - الخطوة القادمة</div>\n\n<p dir=\"rtl\">\nإذا قمت بقرائة الفصل الخاص ب \"البداية من هنا\"، فأنت الآن قادر على بناء تطبيق Yii متكامل، لقد تعلمت كيف يمكنك تنفيذ وإستخدام أكثر الخصائص والمميزات المشتركة، مثل جلب البيانات من قاعدة البيانات، وأخذ البيانات من المستخدمين، ومن ثم عرضها، كما تعلمت كيف يمكنك تقسيم البيانات الى صفحات، وقمنا باستخدام ال <a href=\"https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide\">Gii</a> وتعلمنا كيف يمكننا إنشاء شيفرة برمجية من خلاله بشكل تلقائي، ومن الممارسة تعلمنا أن إنشاء الشيفرة البرمجية من خلال ال Gii يجعل من عملية تطوير المواقع والوظائف المطلوبة أمرا بسيطا وسهلا للغابة، كل ما عليك القيام به هو تعبئة ال forms.\n</p>\n\n<p dir=\"rtl\">\n    في هذا الجزء من التوثيق سنعرض ملخص للمصادر المتاحة لل Yii، والتي ستساعدك في تحسين إنتاجيتك عند إستخدامك لبيئة العمل Yii. \n</p>\n\n<ul dir=\"rtl\">\n    <li>\n        التوثيق\n        <ul>\n            <li><a href=\"https://www.yiiframework.com/doc-2.0/guide-README.html\">The Definitive Guide - الدليل الشامل</a>: كما يشير الإسم، فإن هذا الدليل يحدد آلية عمل ال Yii بدقة عالية، ويوفر إرشادات حول كيفية إستخدام ال Yii، هذا الجزء الأكثر أهمية في ال Yii، والذي يجب عليك قرائته قبل كتابة أي Yii code (ملاحظة: جزء البداية من هنا، والذي قمنا بدراسته هو أحد هذه الأجزاء، ومن أهمها للبدء بإنشاء التطبيقات من خلال ال Yii).</li>\n            <li><a href=\"https://www.yiiframework.com/doc-2.0/index.html\">The Class Reference - المرجع الخاص بال Class</a> في هذا الجزء يتم تحديد الية إستخدام كل Class يقدمه ال Yii، في العادة يتم إستخدام هذا المرجع عند كتابة شيفرة برمجية وأنت ترغب في فهم آلية العمل ل Class معين، أو Method, او فهم Proporty معينة...الخ، من الأفضل إستخدام المرجع الخاص بال Class فقط عند فهم آلية العمل لل Yii. </li>\n            <li><a href=\"https://www.yiiframework.com/wiki/?tag=yii2\">The Wiki Articles - مقالات الويكي</a> مقالات الويكي هي مجموعة من الخبرات العملية للمستخدمين، تمت كتابتها ونشرها على شكل مقالات لمشاركة الخبرات، ومعظم هذه الكتابات تكون مثل الوصفات الخاصة بالطبخ، موجودة لخدمة هدف معين، وحل مشكلة محددة باستخدام ال Yii، وبالرغم من أن هذه الكتابات قد لا تكون بجودة ودقة الدليل الشامل، الا أنها قد تغطي مواضيع أكثر، وتطرح أيضا حلولا مباشرة للإستخدام.</li>\n            <li><a href=\"https://www.yiiframework.com/doc/\">الكتب</a></li>\n        </ul>\n    </li>\n    <li><a href=\"https://www.yiiframework.com/extensions/\">Extensions - الملحقات</a>: تفتخر ال Yii بوجود مكتبة ضخمة من الملحقات التي تمت برمجتها وإضافتها من قبل المستخدمين المتطوعين الذين شاركوا أعمالهم وطورها لتجعل مهمة المطورين الآخرين أسهل وأسرع في تطوير التطبيفات المبنية بواسطة ال Yii.</li>\n    <li>\n        المجتمع\n        <ul>\n            <li>المنتدى: <a href=\"https://forum.yiiframework.com/\">https://forum.yiiframework.com/</a></li>\n            <li>IRC chat: The #yii channel on the Libera (ircs://irc.libera.chat:6697/yii)</li>\n            <li>Slack chanel: <a href=\"https://yii.slack.com\">https://yii.slack.com</a></li>\n            <li>Gitter chat: <a href=\"https://gitter.im/yiisoft/yii2\">https://gitter.im/yiisoft/yii2</a></li>\n            <li>GitHub: <a href=\"https://github.com/yiisoft/yii2\">https://github.com/yiisoft/yii2</a></li>\n            <li>Facebook: <a href=\"https://www.facebook.com/groups/yiitalk/\">https://www.facebook.com/groups/yiitalk/</a></li>\n            <li>Twitter: <a href=\"https://twitter.com/yiiframework\">https://twitter.com/yiiframework</a></li>\n            <li>LinkedIn: <a href=\"https://www.linkedin.com/groups/yii-framework-1483367\">https://www.linkedin.com/groups/yii-framework-1483367</a></li>\n            <li>Stackoverflow: <a href=\"https://stackoverflow.com/questions/tagged/yii2\">https://stackoverflow.com/questions/tagged/yii2</a></li>\n        </ul>\n    </li>\n</ul>\n"
  },
  {
    "path": "docs/guide-ar/start-prerequisites.md",
    "content": "# <div dir=\"rtl\">ماذا يجب أن تعرف قبل البدء بال Yii</div>\n\n<p dir=\"rtl\">\n    منحنى التعلم الخاص بال Yii ليس حادًا مثل أطر PHP الأخرى، ولكن لا يزال هناك بعض الأشياء التي يجب أن تتعلمها قبل البدء بـال Yii.\n</p>\n\n## <div dir=\"rtl\">PHP</div>\n\n<p dir=\"rtl\">\n    ال Yii هو إطار عمل PHP، لذا تأكد من قراءة وفهم المرجع الرسمي الخاص بلغة ال <a href=\"https://www.php.net/manual/en/langref.php\">PHP</a>. عند البدء بتطوير المشاريع أو التطبيقات باستخدام ال Yii ، ستكتب التعليمات البرمجية بطريقة كائنية التوجه OOP، لذا تأكد من أنك على دراية بـمفاهيم ال <a href=\"https://www.php.net/manual/en/language.oop5.basic.php\">OOP</a> وكذلك ال <a href=\"https://www.php.net/manual/en/language.namespaces.php\">namespaces</a>.\n</p>\n\n## <div dir=\"rtl\">البرمجة كائنية التوجه object oriented programming</div>\n\n<p dir=\"rtl\">\n    كمبرمج أو مطور يرغب بالعمل على ال Yii، يجب عليك أن تمتلك المعرفة الأساسية للبرمجة كائنية التوجه OOP. إذا لم تكن على دراية بها ، فيمكنك تعلم ذلك من خلال واحدة من هذه الدورات المنتشرة مثل هذه الدورة من <a href=\"https://code.tutsplus.com/tutorials/object-oriented-php-for-beginners--net-12762\">tuts+</a>.<br />\nملاحظة: كلما زاد تعقيد التطبيق أو المشروع الذي تعمل عليه، كلما احتجت الى مستوى أعلى وإحترافي أكثر من مفاهيم ال OOP لحل وإدارة التعقديات التي ستترب على مثل هذه المشاريع.\n</p>\n\n## <div dir=\"rtl\">Command line and composer</div>\n\n<p dir=\"rtl\">تستخدم ال Yii بشكل كبير de-facto standard PHP package manager، ال <a href=\"https://getcomposer.org/\">Composer</a>، لذلك تأكد من قرائتك وفهمك لهذا الموضوع قبل أن تبدء. بالإضافة الى ذلك إذا لم تكن على دراية باستخدام سطر الأوامر (command line) ، فقد حان الوقت لبدء المحاولة. بمجرد تعلم الأساسيات ، لن ترغب في العمل بدون إستخدام سطر الأوامر.<br />\nال composer: ويترجم حرفيا الى كلمة \"الملحن\"، وهي عبارة عن أداة لإدارة المشاريع البرمجية والتي تسمح لك بتحديث وتنزيل المكتبات البرمجية المطلوبة للمشروع الخاص بك.  \n</p>\n\n"
  },
  {
    "path": "docs/guide-ar/start-workflow.md",
    "content": "# <div dir=\"rtl\">تشغيل التطبيقات</div>\n\n<p dir=\"rtl\">\nبعد تثبيت ال Yii، سيكون لديك تطبيق Yii جاهز للعمل عليه ويمكن الوصول إليه عبر\nالرابط التالي:  <code>https://hostname/basic/web/index.php</code>  أو <code>https://hostname/index.php</code> إعتمادا على الإعدادات\nالخاصة بك (إعدادت ال web server). في هذا الجزء سنستعرض الوظائف ال built-in الموجودة في التطبيق الإفتراضي لإطار العمل Yii، وكيف يقوم بتنظيم الشيفرة البرمجية، وكيف يعالج (handling) هذا التطبيق الطلبات (requests) بشكل عام.\n</p>\n\n<blockquote class=\"info\"><p dir=\"rtl\">\n    معلومة: من أجل تبسيط الطرح، ومن خلال هذا البرنامج التعليمي \" Getting Started - البداية من هنا\"، من المفترض أنك قمت بتعيين <code>basic/web</code> ك <code>document root</code> لل Web server، وقد قمت أيضا بإعداد ال Url الذي يسمح لك بالوصول الى التطبيق المثبت من خلاله ليكون على الشكل التالي: <code>https://hostname/index.php</code> أو ما شابه ذلك.\nاذا لم تقم بذلك، ولتلبية إحتياجاتك في هذا البرنامج التعليمي، يرجى ضبط ال Url كما هو موضح في هذه الصفحة.\nيمكنك معرفة الضبط الخاص بال Web server من هنا:  <a href=\"start-installation.md\">تثبيت ال Yii </a>\n</p></blockquote>\n\n<p dir=\"rtl\">\nملاحظة: بخلاف إطار العمل نفسه(Yii framework)، بعد تثبيت ال template الخاص بالمشروع، يكون كل شيء في هذا التطبيق يخصك أنت، بحيث تملك الحرية في إضافة أو حذف أو تعديل كل ما تحتاج اليه.\n</p>\n\n\n## <div dir=\"rtl\">خصائص / وظائف التطبيق المثبت - Functionality</div> <span id=\"functionality\"></span>\n\n<p dir=\"rtl\">\n    يحتوي ال Basic ِApplication Template الذي قمنا بتثبيته على أربع صفحات:\n</p>\n\n<ul dir=\"rtl\">\n    <li>الصفحة الرئيسية(Homepage): يتم عرض هذه الصفحة من خلال الرابط التالي <code>https://hostname/index.php</code> </li>\n    <li>صفحة من نحن(About)</li>\n    <li>صفحة اتصل بنا (Contact): في هذه الصفحة يتم عرض form يسمح للأعشاء بالإتصال بك من خلال البريد الإلكتروني.</li>\n    <li>صفحة تسجيل الدخول (Login): في هذه الصفحة يتم عرض form يسمح للأعضاء بالحصول على الإذن لإستخدام الخصائص التي لا يجوز لغيرهم من الوصول اليها، قم بتجربة تسجيل الدخول من خلال استخدام <code>admin/admin</code> ولاحظ أن كلمة \"Login\" ستختفي من القائمة الرئيسية وستظهر محلها الكلمة \"Logout\"</li>\n</ul>\n\n<p dir=\"rtl\">\nهذه الصفحات تشترك بامتلاكها common header and footer -الترويسة أعلى الصفحة، والذيل أسفل الصفحة-. ويحتوي ال header على القائمة الرئيسية (main menu) والتي بدورها تسمح لك بالتنقل بين الصفحات المختلفة.\n</p>\n\n<p dir=\"rtl\">\n    أيضا، يجب عليك أن تنظر الى ال toolbar الموجود في أسفل نافذة المتصفح. ال <a href=\"https://github.com/yiisoft/yii2-debug/blob/master/docs/guide/README.md\">debugger tool</a> هذه تعتبر كأداة مفيدة مقدمة من ال Yii لتسجيل وعرض الكثير من المعلومات وتصحيح الأخطاء، مثل  log messages, response statuses, the database queries run وما إلى ذلك.\n</p>\n\n<p dir=\"rtl\">\n    بالإضافة إلى ال  web application، يوجد هناك \"console script\" يسمى ب <code>yii</code>، والذي ستجده في المسار الرئيسي للتطبيق. هذا السكربت يمكن استخدامه لتشغيل المهام التي تعمل في الخفاء (background)  أو لتنفيذ مهام الصيانة (ال maintenance). <br /> ستجد الوصف الخاص بهذا السكربت  \n    داخل هذه الصفحة <a href=\"tutorial-console.md\">Console Application Section</a>.\n</p>\n\n\n\n## <div dir=\"rtl\">هيكلية التطبيق - Application Structure</div> <span id=\"application-structure\"></span>\n\n<p dir=\"rtl\">\n    أكثر المسارات والملفات أهمية الموجودة داخل التطبيق (بافتراض أن ال application's root directory هو <code>basic</code>) هي:  \n</p>\n\n```\nbasic/                  application base path\n    composer.json       used by Composer, describes package information\n    config/             contains application and other configurations\n        console.php     the console application configuration\n        web.php         the Web application configuration\n    commands/           contains console command classes\n    controllers/        contains controller classes\n    models/             contains model classes\n    runtime/            contains files generated by Yii during runtime, such as logs and cache files\n    vendor/             contains the installed Composer packages, including the Yii framework itself\n    views/              contains view files\n    web/                application Web root, contains Web accessible files\n        assets/         contains published asset files (javascript and css) by Yii\n        index.php       the entry (or bootstrap) script for the application\n    yii                 the Yii console command execution script\n```\n\n<p dir=\"rtl\">\nبشكل عام، يمكن تقسيم الملفات داخل التطبيق إلى نوعين: الاول تجده تحت المسار التالي: <code>basic/web</code> والثاني تجده بالمسارات الأخرى.<br />\nوبنائا على ذلك، فإنه من الممكن الوصول إلى النوع الأول مباشرة عبر ال HTTP (أي من خلال المتصفح) ، بينما لا يمكن أن يكون ذلك للنوع الثاني.\n</p>\n\n<p dir=\"rtl\">\n    يعتمد ال Yii على إستخدام ال MVC، وال MVC هو أحد ال Architectural Pattern، وهي اختصار ل <a href=\"https://wikipedia.org/wiki/Model-view-controller\">model-view-controller</a>،\nهذا الأسلوب ينعكس في تنظيم المسارات الخاصة بالملفات كما في الشكل أعلاه. يحتوي المسار <code>models</code> على جميع الكلاس <a href=\"../guide/structure-models.md\">(model classes)</a> ، ويحتوي مسار ال <code>views</code> على جميع الصفحات التي ستستخدم في العرض <a href=\"../guide/structure-controllers.md\">(view scripts)</a>، ويحتوي مسار ال<code>controllers</code> على\n    جميع <a href=\"../guide/structure-views.md\">(controller classes)</a> \n</p>\n\n<p dir=\"rtl\">\n    يوضح المخطط التالي ال static structure للتطبيق. \n</p>\n\n![Static Structure of Application](../guide/images/application-structure.png)\n\n<p dir=\"rtl\">\n    يحتوي كل تطبيق على نص برمجي يستخدم للدخول الى التطبيق (كبوابة بعدها يظهر التطبيق للناظر)، ويسمى هذا الجزء بال entry script، وهو يمثل الصفحة <code>web/index.php</code>، ويعد هذا المدخل النص البرمجي الوحيد الذي يمكن الوصول إليه من خلال ال PHP في التطبيق، ويعمل هذا ال entry script على أخذ ال request ومن ثم إنشاء instance خاص بالتطبيق ليستطيع التعامل معه (التعامل مع التطبيق ومكوناته).\n    يقوم <a href=\"../guide/structure-applications.md\">application</a> على معالجة ال request بمساعدة من ال <a href=\"../guide/concept-components.md\">components</a>، ومن ثم بقوم التطبيق بإرسال ال request الى عناصر ال MVC، كما يتم استخدام ال <a href=\"../guide/structure-widgets.md\">Widgets</a> في ال <a href=\"../guide/structure-views.md\">views</a> للمساعدة في إنشاء العناصر المعقدة والمتغيرة (Complex &amp; Dynamic user interface) لواجهة المستخدم.\n</p>\n\n## <div dir=\"rtl\">دورة الحياة الخاصة بال request</div> <span id=\"request-lifecycle\"></span>\n\n<p dir=\"rtl\">\nيوضح المخطط التالي كيفية معالجة التطبيق ل request معين.\n</p>\n\n![Request Lifecycle](../guide/images/request-lifecycle.png)\n\n<ol dir=\"rtl\">\n    <li>يقوم المستخدم بعمل request لل <a href=\"../guide/structure-entry-scripts.md\">entry script</a> <code>web/index.php</code>.</li>\n    <li>يقوم ال entry script على جلب <a href=\"../guide/concept-configurations.md\">الإعدادات</a> الخاصة بالتطبيق ومن ثم إنشاء ال instance الخاص بالتطبيق ليستطيع التحكم ب request وإدارتها.</li>\n    <li>يقوم التطبيق بمعالجة ال <a href=\"../guide/runtime-routing.md\">requested route</a> بمساعدة من ال <a href=\"../guide/runtime-requests.md\">request</a> application component.</li>\n    <li>يقوم التطبيق على إنشاء instance من ال <a href=\"../guide/structure-controllers.md\">controller</a> للتحكم بال request.</li>\n    <li>يقوم ال controller على إنشاء <a href=\"../guide/structure-controllers.md\">action</a> instance مع مجموعة من الفلاتر(المرشحات) الخاصة بهذا ال action.</li>\n    <li>في حالة فشل أي فلتر، يتم إلغاء الإجراء.</li>\n    <li>في حال نجاح جميع الفلاتر ، يتم تنفيذ الإجراء.</li>\n    <li>يقوم ال action بجلب بعض البيانات الخاصة بال models, وفي الغالب ستكون من قاعدة البيانات إن أمكن ذلك.</li>\n    <li>سيقوم ال action بجلب ال view ليقوم بتقديم البيانات التي تم جلبها لل view.</li>\n    <li>عملية الجلب السابقة ستقوم على إرجاع النتائج الى <a href=\"../guide/runtime-responses.md\">response</a> application component</li>\n    <li>بعد ذلك سيقوم ال response component بإرسال النتيجة النهائية الى المتصفح الخاص بالمستخدم. </li>\n</ol>\n"
  },
  {
    "path": "docs/guide-de/README.md",
    "content": "Das umfassende Handbuch für Yii 2.0\n===================================\n\nDieses Tutorial wurde unter den [Bedingungen der Yii-Dokumentation](https://www.yiiframework.com/doc/terms/) veröffentlicht.\n\nAlle Rechte vorbehalten.\n\n2014 (c) Yii Software LLC.\n\n\nEinführung\n----------\n\n* [Über Yii](intro-yii.md)\n* [Upgrade von Version 1.1](intro-upgrade-from-v1.md)\n\n\nEinstieg\n--------\n\n* [Yii installieren](start-installation.md)\n* [Running Applications](start-workflow.md)\n* [Hallo sagen](start-hello.md)\n* [Arbeiten mit Formularen](start-forms.md)\n* [Arbeiten mit Datenbanken](start-databases.md)\n* [Code generieren mit Gii](start-gii.md)\n* [Ausblick](start-looking-ahead.md)\n\n\nApplication Struktur\n--------------------\n\n* [Überblick](structure-overview.md)\n* [Entry Scripts](structure-entry-scripts.md)\n* [Applications](structure-applications.md)\n* [Application Komponenten](structure-application-components.md)\n* [Controller](structure-controllers.md)\n* [Model](structure-models.md)\n* [View](structure-views.md)\n* **TBD** [Filters](structure-filters.md)\n* **TBD** [Widgets](structure-widgets.md)\n* **TBD** [Modules](structure-modules.md)\n* [Assets](structure-assets.md)\n* **TBD** [Extensions](structure-extensions.md)\n\n\nAnfragen bearbeiten\n-------------------\n\n* **TBD** [Bootstrapping](runtime-bootstrapping.md)\n* **TBD** [Routing](runtime-routing.md)\n* **TBD** [Requests](runtime-requests.md)\n* **TBD** [Responses](runtime-responses.md)\n* **TBD** [Sessions and Cookies](runtime-sessions-cookies.md)\n* [Parsen und Generieren von URLs](runtime-url-handling.md)\n* [Fehlerbehandlung](runtime-handling-errors.md)\n* [Logging](runtime-logging.md)\n\n\nKern-Konzepte\n-------------\n\n* [Komponenten](concept-components.md)\n* [Eigenschaften](concept-properties.md)\n* [Events (Ereignisse)](concept-events.md)\n* [Behaviors](concept-behaviors.md)\n* [Konfiguration](concept-configurations.md)\n* [Aliase](concept-aliases.md)\n* [Class Autoloading](concept-autoloading.md)\n* [Service Locator](concept-service-locator.md)\n* [Dependency Injection Container](concept-di-container.md)\n\n\nArbeiten mit Datenbanken\n------------------------\n\n* [Data Access Objects](db-dao.md) - Connecting to a database, basic queries, transactions and schema manipulation\n* [Query Builder](db-query-builder.md) - Querying the database using a simple abstraction layer\n* [Active Record](db-active-record.md) - The active record ORM, retrieving and manipulating records and defining relations\n* [Migrations](db-migrations.md) - Version control your databases in a team development environment\n* **TBD** [Sphinx](db-sphinx.md)\n* **TBD** [Redis](db-redis.md)\n* **TBD** [MongoDB](db-mongodb.md)\n* **TBD** [ElasticSearch](db-elastic-search.md)\n\n\nEingabedaten verarbeiten\n------------------------\n\n* [Creating Forms](input-forms.md)\n* [Validating Input](input-validation.md)\n* **TBD** [Uploading Files](input-file-upload.md)\n* **TBD** [Getting Data for Multiple Models](input-multiple-models.md)\n\n\nAusgabe von Daten\n-----------------\n\n* **TBD** [Data Formatting](output-formatting.md)\n* **TBD** [Pagination](output-pagination.md)\n* **TBD** [Sorting](output-sorting.md)\n* [Data Providers](output-data-providers.md)\n* [Data Widgets](output-data-widgets.md)\n* [Theming](output-theming.md)\n\n\nSicherheit\n----------\n\n* [Authentication](security-authentication.md)\n* [Authorization](security-authorization.md)\n* [Working with Passwords](security-passwords.md)\n* **TBD** [Auth Clients](security-auth-clients.md)\n* **TBD** [Best Practices](security-best-practices.md)\n\n\nCaching\n-------\n\n* [Overview](caching-overview.md)\n* [Data Caching](caching-data.md)\n* [Fragment Caching](caching-fragment.md)\n* [Page Caching](caching-page.md)\n* [HTTP Caching](caching-http.md)\n\n\nRESTful Web Services\n--------------------\n\n* [Quick Start](rest-quick-start.md)\n* [Resources](rest-resources.md)\n* [Controllers](rest-controllers.md)\n* [Routing](rest-routing.md)\n* [Response Formatting](rest-response-formatting.md)\n* [Authentication](rest-authentication.md)\n* [Rate Limiting](rest-rate-limiting.md)\n* [Versioning](rest-versioning.md)\n* [Error Handling](rest-error-handling.md)\n\n\nDevelopment Tools\n-----------------\n\n* [Debug Toolbar and Debugger](tool-debugger.md)\n* [Generating Code using Gii](tool-gii.md)\n* **TBD** [Generating API Documentation](tool-api-doc.md)\n\n\nTesting\n-------\n\n* [Overview](test-overview.md)\n* **TBD** [Unit Tests](test-unit.md)\n* **TBD** [Functional Tests](test-functional.md)\n* **TBD** [Acceptance Tests](test-acceptance.md)\n* [Fixtures](test-fixtures.md)\n\n\nYii erweitern\n-------------\n\n* [Creating Extensions](extend-creating-extensions.md)\n* [Customizing Core Code](extend-customizing-core.md)\n* [Using 3rd-Party Libraries](extend-using-libs.md)\n* **TBD** [Using Yii in 3rd-Party Systems](extend-embedding-in-others.md)\n* **TBD** [Using Yii 1.1 and 2.0 Together](extend-using-v1-v2.md)\n* [Using Composer](extend-using-composer.md)\n\n\nWeitere Themen\n--------------\n\n* [Advanced Application Template](tutorial-advanced-app.md)\n* [Building Application from Scratch](tutorial-start-from-scratch.md)\n* [Console Commands](tutorial-console.md)\n* [Core Validators](tutorial-core-validators.md)\n* [Internationalization](tutorial-i18n.md)\n* [Mailing](tutorial-mailing.md)\n* [Performance Tuning](tutorial-performance-tuning.md)\n* **TBD** [Shared Hosting Environment](tutorial-shared-hosting.md)\n* [Template Engines](tutorial-template-engines.md)\n\n\nWidgets\n-------\n\n* GridView: link to demo page\n* ListView: link to demo page\n* DetailView: link to demo page\n* ActiveForm: link to demo page\n* Pjax: link to demo page\n* Menu: link to demo page\n* LinkPager: link to demo page\n* LinkSorter: link to demo page\n* [Bootstrap Widgets](bootstrap-widgets.md)\n* [Jquery UI Widgets](jui-widgets.md)\n\n\nHelfer-Klassen\n--------------\n\n* [Overview](helper-overview.md)\n* **TBD** [ArrayHelper](helper-array.md)\n* **TBD** [Html](helper-html.md)\n* **TBD** [Url](helper-url.md)\n* **TBD** [Security](helper-security.md)\n\n"
  },
  {
    "path": "docs/guide-de/blocktypes.json",
    "content": "{\n    \"Warning:\": \"Achtung:\",\n    \"Note:\": \"Hinweis:\",\n    \"Info:\": \"Info:\",\n    \"Tip:\": \"Tipp:\"\n}"
  },
  {
    "path": "docs/guide-de/translators.json",
    "content": "[\n  \"Carsten Brandt\"\n]"
  },
  {
    "path": "docs/guide-es/README.md",
    "content": "Guía Definitiva de Yii 2.0\n==========================\n\nEste tutorial se publica bajo los [Términos de Documentación Yii](https://www.yiiframework.com/doc/terms/).\n\nTodos los derechos reservados.\n\n2014 (c) Yii Software LLC.\n\n\nIntroducción\n------------\n\n* [Acerca de Yii](intro-yii.md)\n* [Actualizar desde Yii 1.1](intro-upgrade-from-v1.md)\n\n\nPrimeros pasos\n--------------\n\n* [Qué necesita saber](start-prerequisites.md)\n* [Instalar Yii](start-installation.md)\n* [Funcionamiento de aplicaciones](start-workflow.md)\n* [Hola a todos](start-hello.md)\n* [Trabajar con formularios](start-forms.md)\n* [Trabajar con bases de datos](start-databases.md)\n* [Generar códigos con Gii](start-gii.md)\n* [Adentrarse en Yii](start-looking-ahead.md)\n\n\nEstructura de una aplicación\n----------------------------\n\n* [Información general](structure-overview.md)\n* [Script de entrada](structure-entry-scripts.md)\n* [Aplicaciones](structure-applications.md)\n* [Componentes de una aplicación](structure-application-components.md)\n* [Controladores](structure-controllers.md)\n* [Modelos](structure-models.md)\n* [Vistas](structure-views.md)\n* [Filtros](structure-filters.md)\n* [Widgets](structure-widgets.md)\n* [Módulos](structure-modules.md)\n* [Assets](structure-assets.md)\n* [Extensiones](structure-extensions.md)\n\n\nGestión de las peticiones\n-------------------------\n\n* [Información general](runtime-overview.md)\n* [Bootstrapping](runtime-bootstrapping.md)\n* [Routing y Creación de las URL](runtime-routing.md)\n* [Peticiones (Requests)](runtime-requests.md)\n* [Respuestas (Responses)](runtime-responses.md)\n* [Sesiones (Sessions) y Cookies](runtime-sessions-cookies.md)\n* [Gestión de errores](runtime-handling-errors.md)\n* [Registro de anotaciones](runtime-logging.md)\n\n\nConceptos clave\n---------------\n\n* [Componentes](concept-components.md)\n* [Propiedades](concept-properties.md)\n* [Eventos](concept-events.md)\n* [Comportamientos (Behaviors)](concept-behaviors.md)\n* [Configuraciones](concept-configurations.md)\n* [Alias](concept-aliases.md)\n* [Autocarga de clases](concept-autoloading.md)\n* [Localizador de servicios (Service Locator)](concept-service-locator.md)\n* [Contenedor de inyección de dependencia](concept-di-container.md)\n\n\nTrabajar con bases de datos\n---------------------------\n\n* [Objeto de acceso a datos](db-dao.md) - Conexión a una base de datos, consultas básicas, transacciones y\n  manipulación de esquemas\n* [Constructor de consultas](db-query-builder.md) - Consulta de la base de datos utilizando una simple capa de\n  abstracción\n* **TBD** [Active Record](db-active-record.md) - ORM Active Record, recuperación y manipulación de registros y\n  definición de relaciones\n* **TBD** [Migraciones](db-migrations.md) - Control de versiones de bases de datos en el entorno de desarrollo en\n  equipo\n* **TBD** [Sphinx](db-sphinx.md)\n* **TBD** [Redis](db-redis.md)\n* **TBD** [MongoDB](db-mongodb.md)\n* **TBD** [ElasticSearch](db-elastic-search.md)\n\n\nObtener datos de los usuarios\n-----------------------------\n\n* **TBD** [Crear formularios](input-forms.md)\n* **TBD** [Validar datos](input-validation.md)\n* **TBD** [Subir archivos](input-file-upload.md)\n* **TBD** [Recogida de tabular input](input-tabular-input.md)\n* **TBD** [Obtener datos para múltiples modelos](input-multiple-models.md)\n\n\nVisualizar datos\n----------------\n\n* **TBD** [Formato de datos](output-formatting.md)\n* **TBD** [Paginación](output-pagination.md)\n* **TBD** [Ordenación](output-sorting.md)\n* **TBD** [Proveedores de datos](output-data-providers.md)\n* **TBD** [Widgets de datos](output-data-widgets.md)\n* **TBD** [Trabajar con scripts de cliente](output-client-scripts.md)\n* [Temas](output-theming.md)\n\n\nSeguridad\n---------\n\n* **TBD** [Autenticación](security-authentication.md)\n* **TBD** [Autorización](security-authorization.md)\n* **TBD** [Trabajar con contraseñas](security-passwords.md)\n* [Autenticar Clientes](https://www.yiiframework.com/extension/yiisoft/yii2-authclient/doc/guide)\n* **TBD** [Buenas prácticas](security-best-practices.md)\n\n\nCaché\n-----\n\n* [Información general](caching-overview.md)\n* [Caché de datos](caching-data.md)\n* [Caché de fragmentos](caching-fragment.md)\n* [Caché de páginas](caching-page.md)\n* [Caché HTTP](caching-http.md)\n\n\nServicios Web RESTful\n---------------------\n\n* [Guía breve](rest-quick-start.md)\n* [Recursos (Resources)](rest-resources.md)\n* [Controladores](rest-controllers.md)\n* [Gestión de rutas](rest-routing.md)\n* [Formateo de respuestas](rest-response-formatting.md)\n* [Autenticación](rest-authentication.md)\n* [Límite de Rango](rest-rate-limiting.md)\n* [Gestión de versiones](rest-versioning.md)\n* [Gestión de errores](rest-error-handling.md)\n\n\nHerramientas de Desarrollo\n--------------------------\n\n* [Depurador y Barra de Herramientas de Depuración](https://github.com/yiisoft/yii2-debug/blob/master/docs/guide-es/README.md)\n* **TBD** [Generación de códigos con Gii](tool-gii.md)\n* **TBD** [Generación de documentación de API](tool-api-doc.md)\n\n\nPruebas\n------\n\n* **TBD** [Información general](test-overview.md)\n* **TBD** [Configuración del entorno de pruebas](test-environment-setup.md)\n* **TBD** [Pruebas unitarias](test-unit.md)\n* **TBD** [Pruebas funcionales](test-functional.md)\n* **TBD** [Pruebas de aceptación](test-acceptance.md)\n* **TBD** [Fixtures](test-fixtures.md)\n\n\nTemas especiales\n----------------\n\n* **TBD** [Plantilla aplicación avanzada](tutorial-advanced-app.md)\n* **TBD** [Creación de una aplicación desde cero](tutorial-start-from-scratch.md)\n* **TBD** [Comandos de consola](tutorial-console.md)\n* [Validadores del núcleo](tutorial-core-validators.md)\n* **TBD** [Internacionalización](tutorial-i18n.md)\n* **TBD** [Envío de correos electrónicos](tutorial-mailing.md)\n* **TBD** [Mejora del rendimiento](tutorial-performance-tuning.md)\n* **TBD** [Entorno de alojamiento compartido](tutorial-shared-hosting.md)\n* **TBD** [Motores de plantillas](tutorial-template-engines.md)\n* **TBD** [Trabajar con Código de Terceros](tutorial-yii-integration.md)\n\n\nWidgets\n-------\n\n* GridView: **TBD** link to demo page\n* ListView: **TBD** link to demo page\n* DetailView: **TBD** link to demo page\n* ActiveForm: **TBD** link to demo page\n* Pjax: **TBD** link to demo page\n* Menu: **TBD** link to demo page\n* LinkPager: **TBD** link to demo page\n* LinkSorter: **TBD** link to demo page\n* [Bootstrap Widgets](https://github.com/yiisoft/yii2-bootstrap/blob/master/docs/guide-es/README.md)\n* [Jquery UI Widgets](https://github.com/yiisoft/yii2-jui/blob/master/docs/guide-es/README.md)\n\n\nClases auxiliares\n-----------------\n\n* [Información general](helper-overview.md)\n* [ArrayHelper](helper-array.md)\n* [Html](helper-html.md)\n* [Url](helper-url.md)\n"
  },
  {
    "path": "docs/guide-es/blocktypes.json",
    "content": "{\n    \"Warning:\": \"Aviso:\",\n    \"Note:\": \"Nota:\",\n    \"Info:\": \"Información:\",\n    \"Tip:\": \"Consejo:\"\n}\n"
  },
  {
    "path": "docs/guide-es/caching-data.md",
    "content": "Almacenamiento de Datos en Caché\n================================\n\nEl almacenamiento de datos en caché trata del almacenamiento de alguna variable PHP en caché y recuperarla más tarde del mismo. También es la base de algunas de las características avanzadas de almacenamiento en caché, tales como [el almacenamiento en caché de consultas a la base de datos](#query-caching) y [el almacenamiento en caché de contenido](caching-page.md).\n\nEl siguiente código muestra el típico patrón de uso para el almacenamiento en caché, donde la variable `$cache` se refiere al [componente caché](#cache-components):\n\n```php\n// intenta recuperar $data de la caché\n$data = $cache->get($key);\n\nif ($data === false) {\n\n    // $data no ha sido encontrada en la caché, calcularla desde cero\n\n    // guardar $data en caché para así recuperarla la próxima vez\n    $cache->set($key, $data);\n}\n\n// $data está disponible aquí\n```\n\n\n## Componentes de Caché <span id=\"cache-components\"></span>\n\nEl almacenamiento de datos en caché depende de los llamados *cache components* (componentes de caché) los cuales\nrepresentan diferentes tipos de almacenamiento en caché, como por ejemplo en memoria, en archivos o en base de datos.\n\nLos Componentes de Caché están normalmente registrados como [componentes de la aplicación](structure-application-components.md) para que de esta forma puedan\nser configurados y accesibles globalmente. El siguiente código muestra cómo configurar el componente de aplicación\n`cache` para usar [memcached](https://memcached.org/) con dos servidores caché:\n\n```php\n'components' => [\n    'cache' => [\n        'class' => 'yii\\caching\\MemCache',\n        'servers' => [\n            [\n                'host' => 'server1',\n                'port' => 11211,\n                'weight' => 100,\n            ],\n            [\n                'host' => 'server2',\n                'port' => 11211,\n                'weight' => 50,\n            ],\n        ],\n    ],\n],\n```\n\nPuedes acceder al componente de caché usando la expresión  `Yii::$app->cache`.\n\nDebido a que todos los componentes de caché soportan el mismo conjunto de APIs, podrías cambiar el componente de caché\nsubyacente por otro diferente mediante su reconfiguración en la configuración de la aplicación sin tener que modificar\nel código que utiliza la caché. Por ejemplo, podrías modificar la configuración anterior para usar [[yii\\caching\\ApcCache|APC cache]]:\n\n```php\n'components' => [\n    'cache' => [\n        'class' => 'yii\\caching\\ApcCache',\n    ],\n],\n```\n\n> Tip: Puedes registrar múltiples componentes de aplicación de caché. El componente llamado `cache` es usado por defecto por muchas clases caché-dependiente (ej. [[yii\\web\\UrlManager]]).\n\n\n### Almacenamientos de Caché Soportados <span id=\"supported-cache-storage\"></span>\n\nYii proporciona varios componentes de caché que pueden almacenar datos en diferentes medios. A continuación\nse muestra un listado con los componentes de caché disponibles:\n\n* [[yii\\caching\\ApcCache]]: utiliza la extensión de PHP [APC](https://www.php.net/manual/es/book.apc.php). Esta opción puede ser considerada como la más rápida de entre todas las disponibles para una aplicación centralizada. (ej. un servidor, no dedicado al balance de carga, etc).\n* [[yii\\caching\\DbCache]]: utiliza una tabla de base de datos para almacenar los datos. Por defecto, se creará y usará como base de datos [SQLite3](https://sqlite.org/) en el directorio runtime. Se puede especificar explícitamente que base de datos va a ser utilizada configurando la propiedad `db`.\n* [[yii\\caching\\DummyCache]]: dummy cache (caché tonta) que no almacena en caché nada. El propósito de este componente es simplificar el código necesario para chequear la disponibilidad de caché. Por ejemplo, durante el desarrollo o si el servidor no tiene soporte de caché actualmente, puede utilizarse este componente de caché. Cuando este disponible un soporte en caché, puede cambiarse el componente correspondiente. En ambos casos, puede utilizarse el mismo código `Yii::$app->cache->get($key)` para recuperar un dato sin la preocupación de que `Yii::$app->cache` pueda ser `null`.\n* [[yii\\caching\\FileCache]]: utiliza un fichero estándar para almacenar los datos. Esto es adecuado para almacenar grandes bloques de datos (como páginas).\n* [[yii\\caching\\MemCache]]: utiliza las extensiones de PHP [memcache](https://www.php.net/manual/es/book.memcache.php) y [memcached](https://www.php.net/manual/es/book.memcached.php). Esta opción puede ser considerada como la más rápida cuando la caché es manejada en una aplicación distribuida (ej. con varios servidores, con balance de carga, etc..)\n* [[yii\\redis\\Cache]]: implementa un componente de caché basado en [Redis](https://redis.io/) que almacenan pares clave-valor (requiere la versión 2.6.12 de redis).\n* [[yii\\caching\\WinCache]]: utiliza la extensión de PHP [WinCache](https://iis.net/downloads/microsoft/wincache-extension) ([ver también](https://www.php.net/manual/es/book.wincache.php)).\n\n> Tip: Puedes utilizar diferentes tipos de almacenamiento de caché en la misma aplicación. Una estrategia común es la de usar almacenamiento de caché en memoria para almacenar datos que son pequeños pero que son utilizados constantemente (ej. datos estadísticos), y utilizar el almacenamiento de caché en archivos o en base de datos para guardar datos que son grandes y utilizados con menor frecuencia (ej. contenido de página).\n\n\n## API de Caché <span id=\"cache-apis\"></span>\n\nTodos los componentes de almacenamiento de caché provienen de la misma clase \"padre\" [[yii\\caching\\Cache]] y por lo tanto soportan la siguiente API:\n\n* [[yii\\caching\\Cache::get()|get()]]: recupera un elemento de datos de la memoria caché con una clave especificada.\n  Un valor nulo será devuelto si el elemento de datos no ha sido encontrado en la memoria caché o si ha expirado o ha sido invalidado.\n* [[yii\\caching\\Cache::set()|set()]]: almacena un elemento de datos identificado por una clave en la memoria caché.\n* [[yii\\caching\\Cache::add()|add()]]: almacena un elemento de datos identificado por una clave en la memoria caché si la clave no se encuentra en la memoria caché.\n* [[yii\\caching\\Cache::mget()|mget()]]: recupera varios elementos de datos de la memoria caché con las claves especificadas.\n* [[yii\\caching\\Cache::mset()|mset()]]: almacena múltiples elementos de datos en la memoria caché. Cada elemento se identifica por una clave.\n* [[yii\\caching\\Cache::madd()|madd()]]: almacena múltiples elementos de datos en la memoria caché. Cada elemento se identifica con una clave. Si una clave ya existe en la caché, el elemento será omitido.\n* [[yii\\caching\\Cache::exists()|exists()]]: devuelve un valor que indica si la clave especificada se encuentra en la memoria caché.\n* [[yii\\caching\\Cache::delete()|delete()]]: elimina un elemento de datos identificado por una clave de la caché.\n* [[yii\\caching\\Cache::flush()|flush()]]: elimina todos los elementos de datos de la cache.\n\n> Note: No Almacenes el valor boolean `false` en caché directamente porque el método [[yii\\caching\\Cache::get()|get()]] devuelve\nel valor `false` para indicar que el dato no ha sido encontrado en la caché. Puedes poner `false` dentro de un array y cachear\neste array para evitar este problema.\n\nAlgunos sistemas de almacenamiento de caché, como por ejemplo MemCache, APC, pueden recuperar múltiples valores almacenados en modo de lote (batch), lo que puede reducir considerablemente la sobrecarga que implica la recuperación de datos almacenados en la caché. Las API [[yii\\caching\\Cache::mget()|mget()]] y  [[yii\\caching\\Cache::madd()|madd()]]\nse proporcionan para utilizar esta característica. En el caso de que el sistema de memoria caché no lo soportara, ésta sería simulada.\n\nPuesto que [[yii\\caching\\Cache]] implementa `ArrayAccess`, un componente de caché puede ser usado como un array.\nEl siguiente código muestra unos ejemplos:\n\n```php\n$cache['var1'] = $value1;  // equivalente a: $cache->set('var1', $value1);\n$value2 = $cache['var2'];  // equivalente a: $value2 = $cache->get('var2');\n```\n\n\n### Claves de Caché <span id=\"cache-keys\"></span>\n\nCada elemento de datos almacenado en caché se identifica por una clave. Cuando se almacena un elemento de datos en la memoria caché, se debe especificar una clave. Más tarde, cuando se recupera el elemento de datos de la memoria caché, se debe proporcionar la clave correspondiente.\n\nPuedes utilizar una cadena o un valor arbitrario como una clave de caché. Cuando una clave no es una cadena de texto, ésta será automáticamente serializada en una cadena.\n\nUna estrategia común para definir una clave de caché es incluir en ella todos los factores determinantes en términos de un array. Por ejemplo, [[yii\\db\\Schema]] utiliza la siguiente clave para almacenar en caché la información del esquema de una tabla de base de datos:\n\n```php\n[\n    __CLASS__,              // nombre de la clase del esquema\n    $this->db->dsn,         // nombre del origen de datos de la conexión BD\n    $this->db->username,    // usuario para la conexión BD\n    $name,                  // nombre de la tabla\n];\n```\n\nComo puedes ver, la clave incluye toda la información necesaria para especificar de una forma exclusiva una tabla de base de datos.\n\nCuando en un mismo almacenamiento en caché es utilizado por diferentes aplicaciones, se debería especificar un prefijo único para las claves de la caché por cada una de las aplicaciones para así evitar conflictos. Esto puede hacerse mediante la configuración de la propiedad [[yii\\caching\\Cache::keyPrefix]]. Por ejemplo, en la configuración de la aplicación podrías escribir el siguiente código:\n\n```php\n'components' => [\n    'cache' => [\n        'class' => 'yii\\caching\\ApcCache',\n        'keyPrefix' => 'myapp',       // un prefijo de clave de caché único\n    ],\n],\n```\n\nPara garantizar la interoperabilidad, deberían utilizarse sólo caracteres alfanuméricos.\n\n\n### Caducidad de Caché <span id=\"cache-expiration\"></span>\n\nUn elemento de datos almacenado en la memoria caché permanecerá en ella para siempre, a menos que sea removida de alguna manera debido a alguna directiva de caché (ej. el espacio de almacenamiento en caché está lleno y los datos más antiguos se eliminan). Para cambiar este comportamiento, podrías proporcionar un parámetro de caducidad al llamar [[yii\\caching\\Cache::set()|set()]] para guardar el elemento de datos. El parámetro nos indica por cuántos segundos el elemento se mantendrá válido en memoria caché. Cuando llames [[yii\\caching\\Cache::get()|get()]] para recuperar el elemento, si el tiempo de caducidad ha pasado, el método devolverá `false`, indicando que el elemento de datos no ha sido encontrado en la memoria caché. Por ejemplo,\n\n```php\n// guardar los datos en memoria caché al menos 45 segundos\n$cache->set($key, $data, 45);\n\nsleep(50);\n\n$data = $cache->get($key);\nif ($data === false) {\n    // $data ha caducado o no ha sido encontrado en la memoria caché\n}\n```\n\n\n### Dependencias de Caché <span id=\"cache-dependencies\"></span>\n\nAdemás de configurar el tiempo de caducidad, los datos almacenados en caché pueden también ser invalidados conforme a algunos cambios en la caché de dependencias. Por ejemplo, [[yii\\caching\\FileDependency]] representa la dependencia del tiempo de modificación del archivo. Cuando esta dependencia cambia, significa que el archivo correspondiente ha cambiado. Como resultado, cualquier contenido anticuado que sea encontrado en la caché debería ser invalidado y la llamada a [[yii\\caching\\Cache::get()|get()]] debería retornar falso.\n\nUna dependencia es representada como una instancia de [[yii\\caching\\Dependency]] o su clase hija. Cuando llamas [[yii\\caching\\Cache::set()|set()]] para almacenar un elemento de datos en la caché, puedes pasar el objeto de dependencia asociado. Por ejemplo,\n\n```php\n// Crear una dependencia sobre el tiempo de modificación del archivo example.txt.\n$dependency = new \\yii\\caching\\FileDependency(['fileName' => 'example.txt']);\n\n// Los datos expirarán en 30 segundos.\n// También podría ser invalidada antes si example.txt es modificado.\n$cache->set($key, $data, 30, $dependency);\n\n// La caché comprobará si los datos han expirado.\n// También comprobará si la dependencia ha cambiado.\n// Devolverá `false` si se encuentran algunas de esas condiciones.\n$data = $cache->get($key);\n```\n\nAquí abajo se muestra un sumario de las dependencias disponibles:\n\n- [[yii\\caching\\ChainedDependency]]: la dependencia cambia si cualquiera de las dependencias en la cadena cambia.\n- [[yii\\caching\\DbDependency]]: la dependencia cambia si el resultado de la consulta de la sentencia SQL especificada cambia.\n- [[yii\\caching\\ExpressionDependency]]: la dependencia cambia si el resultado de la expresión de PHP especificada cambia.\n- [[yii\\caching\\CallbackDependency]]: la dipendenza viene modificata se il risultato della callback PHP specificata cambia.\n- [[yii\\caching\\FileDependency]]:  la dependencia cambia si se modifica la última fecha de modificación del archivo.\n- [[yii\\caching\\TagDependency]]: marca un elemento de datos en caché con un nombre de grupo. Puedes invalidar los elementos de datos almacenados en caché\n  con el mismo nombre del grupo a la vez llamando a [[yii\\caching\\TagDependency::invalidate()]].\n\n\n## Consultas en Caché <span id=\"query-caching\"></span>\n\nLas consultas en caché es una característica especial de caché construido sobre el almacenamiento de caché de datos. Se\nproporciona para almacenar en caché el resultado de consultas a la base de datos.\n\nLas consultas en caché requieren una [[yii\\db\\Connection|DB connection]] y un componente de aplicación caché válido. El uso básico de las consultas en memoria caché es el siguiente, asumiendo que `db` es una instancia de [[yii\\db\\Connection]]:\n\n```php\n$result = $db->cache(function ($db) {\n\n    // el resultado de la consulta SQL será servida de la caché\n    // si el cacheo de consultas está habilitado y el resultado de la consulta se encuentra en la caché\n    return $db->createCommand('SELECT * FROM customer WHERE id=1')->queryOne();\n\n});\n```\n\nEl cacheo de consultas puede ser usado tanto para [DAO](db-dao.md) como para [ActiveRecord](db-active-record.md):\n\n```php\n$result = Customer::getDb()->cache(function ($db) {\n    return Customer::find()->where(['id' => 1])->one();\n});\n```\n\n> Note: Algunos DBMS (ej. [MySQL](https://dev.mysql.com/doc/refman/5.6/en/query-cache.html)) también soporta el almacenamiento en caché desde el mismo servidor de la BD. Puedes optar por utilizar cualquiera de los mecanismos de memoria caché. El almacenamiento en caché de consultas previamente descrito tiene la ventaja que de que se puede especificar dependencias de caché de una forma flexible y son potencialmente mucho más eficientes.\n\n\n### Configuraciones <span id=\"query-caching-configs\"></span>\n\nLas consultas en caché tienen tres opciones configurables globales a través de [[yii\\db\\Connection]]:\n\n* [[yii\\db\\Connection::enableQueryCache|enableQueryCache]]: activa o desactiva el cacheo de consultas.\n  Por defecto es `true`. Tenga en cuenta que para activar el cacheo de consultas, también necesitas tener una caché válida, especificada por [[yii\\db\\Connection::queryCache|queryCache]].\n* [[yii\\db\\Connection::queryCacheDuration|queryCacheDuration]]: representa el número de segundos que un resultado de la consulta permanecerá válida en la memoria caché. Puedes usar 0 para indicar que el resultado de la consulta debe permanecer en la caché para siempre. Esta propiedad es el valor usado por defecto cuando [[yii\\db\\Connection::cache()]] es llamada sin especificar una duración.\n* [[yii\\db\\Connection::queryCache|queryCache]]: representa el ID del componente de aplicación de caché.\n  Por defecto es `'cache'`. El almacenamiento en caché de consultas se habilita sólo si hay un componente de la aplicación de caché válida.\n\n\n### Usos <span id=\"query-caching-usages\"></span>\n\nPuedes usar [[yii\\db\\Connection::cache()]] si tienes multiples consultas SQL que necesitas a aprovechar el cacheo de consultas. El uso es de la siguiente manera,\n\n```php\n$duration = 60;     // resultado de la consulta de caché durante 60 segundos.\n$dependency = ...;  // dependencia opcional\n\n$result = $db->cache(function ($db) {\n\n    // ... realiza consultas SQL aquí ...\n\n    return $result;\n\n}, $duration, $dependency);\n```\n\nCualquier consulta SQL en una función anónima será cacheada durante el tiempo indicado con la dependencia especificada.\nSi el resultado de la consulta se encuentra válida en la caché, la consulta se omitirá y el resultado se servirá de la caché en su lugar. Si no especificar el parámetro `$duration`, el valor [[yii\\db\\Connection::queryCacheDuration|queryCacheDuration]] será usado en su lugar.\n\nA veces dentro de `cache()`, puedes querer desactivar el cacheo de consultas para algunas consultas especificas. Puedes usar [[yii\\db\\Connection::noCache()]] en este caso.\n\n```php\n$result = $db->cache(function ($db) {\n\n    // consultas SQL que usan el cacheo de consultas\n\n    $db->noCache(function ($db) {\n\n        // consultas SQL que no usan el cacheo de consultas\n\n    });\n\n    // ...\n\n    return $result;\n});\n```\n\nSi lo deseas puedes usar el cacheo de consultas para una simple consulta, puedes llamar a [[yii\\db\\Command::cache()]] cuando construyas el comando. Por ejemplo,\n\n```php\n// usa el cacheo de consultas y asigna la duración de la consulta de caché por 60 segundos\n$customer = $db->createCommand('SELECT * FROM customer WHERE id=1')->cache(60)->queryOne();\n```\n\nTambién puedes usar [[yii\\db\\Command::noCache()]] para desactivar el cacheo de consultas de un simple comando. Por ejemplo,\n\n```php\n$result = $db->cache(function ($db) {\n\n    // consultas SQL que usan cacheo de consultas\n\n    // no usa cacheo de consultas para este comando\n    $customer = $db->createCommand('SELECT * FROM customer WHERE id=1')->noCache()->queryOne();\n\n    // ...\n\n    return $result;\n});\n```\n\n\n### Limitaciones <span id=\"query-caching-limitations\"></span>\n\nEl almacenamiento en caché de consultas no funciona con los resultados de consulta que contienen controladores de recursos.\nPor ejemplo, cuando se utiliza el tipo de columna `BLOB` en algunos DBMS, el resultado de la consulta devolverá un recurso para manejar los datos de la columna.\n\nAlgunos sistemas de almacenamiento caché tienen limitación de tamaño. Por ejemplo, memcache limita el tamaño máximo de cada entrada a 1MB. Por lo tanto, si el tamaño de un resultado de la consulta excede ese límite, el almacenamiento en caché fallará.\n"
  },
  {
    "path": "docs/guide-es/caching-fragment.md",
    "content": "Caché de Fragmentos\n===================\n\nLa Caché de Fragmentos se refiere al almacenamiento en caché de un fragmento, o sección, de una página Web. Por ejemplo, si\nuna página muestra un sumario de las ventas anuales en una tabla, podrías guardar esta tabla en memoria caché para\neliminar el tiempo necesario para generar esta tabla en cada petición (request). La caché de fragmentos está construido\nsobre la [caché de datos](caching-data.md).\n\nPara usar la caché de fragmentos, utiliza el siguiente código en tu [vista (view)](structure-views.md):\n\n\n```php\nif ($this->beginCache($id)) {\n\n    // ... generar contenido aquí ...\n\n    $this->endCache();\n}\n```\n\nEs decir, encierra la lógica de la generación del contenido entre las llamadas [[yii\\base\\View::beginCache()|beginCache()]] y\n[[yii\\base\\View::endCache()|endCache()]]. Si el contenido se encuentra en la memoria caché, [[yii\\base\\View::beginCache()|beginCache()]]\nmostrará el contenido y devolverá `false`, saltandose así la lógica de generación del contenido. De lo contrario, el\ncódigo de generación se ejecutaría y al alcanzar la llamada [[yii\\base\\View::endCache()|endCache()]], el contenido\ngenerado será capturado y almacenado en la memoria caché.\n\nComo en la [caché de datos](caching-data.md), un `$id` (clave) único es necesario para identificar un contenido guardado en\ncaché.\n\n\n## Opciones de Caché <span id=\"caching-options\"></span>\n\nPuedes especificar opciones adicionales para la caché de fragmentos pasando el array de opciones como segundo\nparametro del método [[yii\\base\\View::beginCache()|beginCache()]]. Entre bastidores, este array de opciones se utiliza\npara configurar el widget [[yii\\widgets\\FragmentCache]] que es en realidad el que implementa la funcionalidad de la caché\nde fragmentos.\n\n### Duración <span id=\"duration\"></span>\n\nQuizás la opción más utilizada en la caché de fragmentos es [[yii\\widgets\\FragmentCache::duration|duración]]. Ésta\nespecifica cuántos segundos el contenido puede permanecer como válido en la memoria caché. El siguiente código almacena\nen la caché el fragmento de contenido para una hora a lo sumo:\n\n```php\nif ($this->beginCache($id, ['duration' => 3600])) {\n\n    // ... generar contenido aquí ...\n\n    $this->endCache();\n}\n```\n\nSi la opción no está activada, se tomará el valor por defecto 60, lo que significa que el contenido almacenado en caché expirará en 60 segundos.\n\n\n### Dependencias <span id=\"dependencies\"></span>\n\nComo en la [caché de datos](caching-data.md#cache-dependencies), el fragmento de contenido que está siendo almacenado en caché\ntambién puede tener dependencias. Por ejemplo, el contenido de un artículo que se muestre depende de si el mensaje se\nmodifica o no.\n\nPara especificar una dependencia, activa la opción [[yii\\widgets\\FragmentCache::dependency|dependencia]] (dependency),\nque puede ser un objecto [[yii\\caching\\Dependency]] o un array de configuración para crear un objecto `Dependency`. El\nsiguiente código especifica que la caché de fragmento depende del cambio del valor de la columna `updated_at`:\n\n```php\n$dependency = [\n    'class' => 'yii\\caching\\DbDependency',\n    'sql' => 'SELECT MAX(updated_at) FROM post',\n];\n\nif ($this->beginCache($id, ['dependency' => $dependency])) {\n\n    // ... generar contenido aquí ...\n\n    $this->endCache();\n}\n```\n\n\n### Variaciones <span id=\"variations\"></span>\n\nEl contenido almacenado en caché puede variar de acuerdo a ciertos parámetros. Por ejemplo, para una aplicación Web que\nsoporte multiples idiomas, la misma pieza del código de la vista puede generar el contenido almacenado en caché\nen diferentes idiomas. Por lo tanto, es posible que desees hacer variaciones del mismo contenido almacenado en caché de\nacuerdo con la actual selección del idioma en la aplicación.\n\nPara especificar variaciones en la memoria caché, configura la opción [[yii\\widgets\\FragmentCache::variations|variaciones]]\n(variations), la cual deberá ser un array de valores escalares, cada uno de ellos representando un factor de variación.\nPor ejemplo, para hacer que el contenido almacenado en la caché varíe por lenguaje, podrías usar el siguiente código:\n\n```php\nif ($this->beginCache($id, ['variations' => [Yii::$app->language]])) {\n\n    // ... generar código aquí ...\n\n    $this->endCache();\n}\n```\n\n\n### Alternando el Almacenamiento en Caché <span id=\"toggling-caching\"></span>\n\nPuede que a veces quieras habilitar la caché de fragmentos únicamente cuando ciertas condiciones se cumplan. Por ejemplo,\npara una página que muestra un formulario, tal vez quieras guardarlo en la caché cuando es inicialmente solicitado (a\ntravés de una petición GET). Cualquier muestra posterior (a través de una petición POST) del formulario no debería ser\nalmacenada en caché ya que el formulario puede que contenga entradas del usuario. Para hacerlo, podrías configurar la\nopción de [[yii\\widgets\\FragmentCache::enabled|activado]] (enabled), de la siguiente manera:\n\n```php\nif ($this->beginCache($id, ['enabled' => Yii::$app->request->isGet])) {\n\n    // ... generar contenido aquí ...\n\n    $this->endCache();\n}\n```\n\n\n## Almacenamiento en Caché Anidada <span id=\"nested-caching\"></span>\n\nEl almacenamiento en caché de fragmentos se puede anidar. Es decir, un fragmento de caché puede ser encerrado dentro de\notro fragmento que también se almacena en caché. Por ejemplo, los comentarios se almacenan en una caché de fragmento\ninterno, y se almacenan conjuntamente con el contenido del artículo en un fragmento de caché exterior. El siguiente\ncódigo muestra cómo dos fragmentos de caché pueden ser anidados:\n\n```php\nif ($this->beginCache($id1)) {\n\n    // ... lógica de generación de contenido externa ...\n\n    if ($this->beginCache($id2, $options2)) {\n\n        // ... lógica de generación de contenido anidada ...\n\n        $this->endCache();\n    }\n\n    // ... lógica de generación de contenido externa ...\n\n    $this->endCache();\n}\n```\n\nExisten diferentes opciones de configuración para las cachés anidadas. Por ejemplo, las cachés internas y las cachés\nexternas pueden usar diferentes valores de duración. Aún cuando los datos almacenados en la caché externa sean invalidados,\nla caché interna puede todavía proporcionar un fragmento válido. Sin embargo, al revés no es cierto. Si la caché externa\nes evaluada como válida, seguiría proporcionando la misma copia en caché incluso después de que el contenido  en la\ncaché interna haya sido invalidada. Por lo tanto, hay que tener mucho cuidado al configurar el tiempo de duración o las\ndependencias de las cachés anidadas, de lo contrario los fragmentos internos que ya estén obsoletos se pueden seguir\nmanteniendo en el fragmento externo.\n\n\n## Contenido Dinámico <span id=\"dynamic-content\"></span>\n\nCuando se usa la caché de fragmentos, podrías encontrarte en la situación que un fragmento grande de contenido es\nrelavitamente estático excepto en uno u otro lugar. Por ejemplo, la cabeza de una página (header) puede que muestre el\nmenú principal junto al nombre del usuario actual. Otro problema es que el contenido que está siendo almacenado en caché\npuede que contenga código PHP que debe ser ejecutado en cada petición (por ejemplo, el código para registrar\nun paquete de recursos (asset bundle)). En ambos casos, podríamos resolver el problema con lo que llamamos la\ncaracterística de *contenido dinámico*.\n\nEntendemos *contenido dinámico* como un fragmento de salida que no debería ser guardado en caché incluso si está\nencerrado dentro de un fragmento de caché. Para hacer el contenido dinámico todo el tiempo, éste ha de ser generado ejecutando\ncierto código PHP en cada petición, incluso si el contenido está siendo mostrado desde la caché.\n\nPuedes llamar a [[yii\\base\\View::renderDynamic()]] dentro de un fragmento almacenado en caché para insertar código\ndinámico en el lugar deseado como, por ejemplo, de la siguiente manera,\n\n```php\nif ($this->beginCache($id1)) {\n\n    // ... lógica de generación de contenido ...\n\n    echo $this->renderDynamic('return Yii::$app->user->identity->name;');\n\n    // ... lógica de generación de contenido ...\n\n    $this->endCache();\n}\n```\n\nEl método [[yii\\base\\View::renderDynamic()|renderDynamic()]] toma una pieza de código PHP como su parámetro. El valor\ndevuelto del código PHP se trata como contenido dinámico. El mismo código PHP será ejecutado en cada petición,\nsin importar que esté dentro de un fragmento que está siendo servido desde la caché o no.\n"
  },
  {
    "path": "docs/guide-es/caching-http.md",
    "content": "Caché HTTP\n==========\n\nAdemás del almacenamiento de caché en el servidor que hemos descrito en secciones anteriores, las aplicaciones Web\npueden hacer uso de la caché en el lado del cliente para así ahorrar tiempo y recursos para generar y transmitir el\nmismo contenido una y otra vez.\n\nPara usar la caché del lado del cliente, puedes configurar [[yii\\filters\\HttpCache]] como un filtro en el controlador\npara aquellas acciones cuyo resultado deba estar almacenado en la caché en el lado del cliente. [[yii\\filters\\HttpCache|HttpCache]]\nsolo funciona en peticiones `GET` y `HEAD`. Puede manejar tres tipos de cabeceras (headers) HTTP relacionadas en este tipo de\nconsultas:\n\n* [[yii\\filters\\HttpCache::lastModified|Last-Modified]]\n* [[yii\\filters\\HttpCache::etagSeed|Etag]]\n* [[yii\\filters\\HttpCache::cacheControlHeader|Cache-Control]]\n\n\n## La Cabecera `Last-Modified` <span id=\"last-modified\"></span>\n\nLa cabecera `Last-Modified` usa un sello de tiempo para indicar si la página ha sido modificada desde que el cliente la\nalmacena en la caché.\n\nPuedes configurar la propiedad [[yii\\filters\\HttpCache::lastModified]] para activar el envío de la cabecera `Last-Modified`.\nLa propiedad debe ser una llamada de retorno (callable) PHP que devuelva un timestamp UNIX sobre el tiempo de modificación de\nla página. El formato de la función de llamada de retorno debe ser el siguiente,\n\n```php\n/**\n * @param Action $action el objeto acción que se está controlando actualmente\n * @param array $params el valor de la propiedad \"params\"\n * @return int un sello de tiempo UNIX que representa el tiempo de modificación de la página\n */\nfunction ($action, $params)\n```\n\nEl siguiente es un ejemplo haciendo uso de la cabecera `Last-Modified`:\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\HttpCache',\n            'only' => ['index'],\n            'lastModified' => function ($action, $params) {\n                $q = new \\yii\\db\\Query();\n                return $q->from('post')->max('updated_at');\n            },\n        ],\n    ];\n}\n```\n\nEl código anterior establece que la memoria caché HTTP debe ser habilitada únicamente por la acción `index`. Se debe\ngenerar una cabecera HTTP `Last-Modified` basado en el último tiempo de actualización de los artículos. Cuando un\nnavegador visita la página `index` la primera vez, la página será generada en el servidor y enviada al navegador; Si el\nnavegador visita la misma página de nuevo y no ningún artículo modificado durante el período, el servidor no volverá a\nregenerar la página, y el navegador usará la versión caché del lado del cliente. Como resultado, la representación del\nlado del servidor y la transmisión del contenido de la página son ambos omitidos.\n\n\n## La Cabecera `ETag` <span id=\"etag\"></span>\n\nLa cabecera \"Entity Tag\" (o para abreviar `ETag`) usa un hash para representar el contenido de una página. Si la página\nha sido cambiada, el hash también cambiará. Al comparar el hash guardado en el lado del cliente con el hash generado en\nel servidor, la caché puede determinar si la página ha cambiado y deber ser retransmitida.\n\nPuedes configurar la propiedad [[yii\\filters\\HttpCache::etagSeed]] para activar el envío de la cabecera `ETag`.\nLa propiedad debe ser una función de retorno (callable) PHP que devuelva una semilla para la generación del hash de `ETag`.\nEl formato de la función de retorno es el siguiente:\n\n```php\n/**\n * @param Action $action el objeto acción que se está controlando actualmente\n * @param array $params el valor de la propiedad \"params\"\n * @return string una cadena usada como semilla para la generación del hash de ETag\n */\nfunction ($action, $params)\n```\n\nEl siguiente es un ejemplo de cómo usar la cabecera `ETag`:\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\HttpCache',\n            'only' => ['view'],\n            'etagSeed' => function ($action, $params) {\n                $post = $this->findModel(\\Yii::$app->request->get('id'));\n                return serialize([$post->title, $post->content]);\n            },\n        ],\n    ];\n}\n```\n\nEl código anterior establece que la caché HTTP debe ser activada únicamente para la acción `view`. Debería generar una\ncabecera HTTP `ETag` basándose en el título y contenido del artículo consultado. Cuando un navegador visita la página\n`view` por primera vez, la página se generará en el servidor y será enviada al navegador; Si el navegador visita la\nmisma página de nuevo y no ha ocurrido un cambio en el título o contenido del artículo, el servidor no volverá a generar\nla página, y el navegador usará la versión guardada en la caché del lado del cliente. Como resultado, la representación del\nlado del servidor y la transmisión del contenido de la página son ambos omitidos.\n\nETags permiten estrategias de almacenamiento de caché más complejas y/o mucho más precisas que las cabeceras `Last-Modified`.\nPor ejemplo, un ETag puede ser invalidado si el sitio Web ha cambiado de tema (theme).\n\nLa generación de un ETag que requiera muchos recursos puede echar por tierra el propósito de estar usando `HttpCache` e\nintroducir una sobrecarga innecesaria, ya que debe ser re-evaluada en cada solicitud (request). Trata de encontrar una\nexpresión sencilla para invalidar la caché si la página ha sido modificada.\n\n> Note: En cumplimiento con [RFC 7232](https://datatracker.ietf.org/doc/html/rfc7232#section-2.4),\n  `HttpCache` enviará ambas cabeceras `ETag` y `Last-Modified` si ambas están configuradas. Y si el clientes envía tanto la cabecera `If-None-Match` como la cabecera `If-Modified-Since`, solo la primera será respetada.\n\n## La Cabecera `Cache-Control` <span id=\"cache-control\"></span>\n\nLa cabecera `Cache-Control` especifica la directiva general de la caché para páginas. Puedes enviarla configurando la\npropiedad [[yii\\filters\\HttpCache::cacheControlHeader]] con el valor de la cabecera. Por defecto, la siguiente cabecera\nserá enviada:\n\n```\nCache-Control: public, max-age=3600\n```\n\n## Limitador de la Sesión de Caché <span id=\"session-cache-limiter\"></span>\n\nCuando una página utiliza la sesión, PHP enviará automáticamente cabeceras HTTP relacionadas con la caché tal y como se\nespecifican en `session.cache_limiter` de la configuración INI de PHP. Estas cabeceras pueden interferir o deshabilitar\nel almacenamiento de caché que desees de `HttpCache`. Para evitar este problema, por defecto `HttpCache` deshabilitará\nautomáticamente el envío de estas cabeceras. Si deseas modificar este comportamiento, tienes que configurar la propiedad\n[[yii\\filters\\HttpCache::sessionCacheLimiter]]. La propiedad puede tomar un valor de cadena, incluyendo `public`, `private`,\n`private_no_expire`, and `nocache`. Por favor, consulta el manual PHP acerca de [session_cache_limiter()](https://www.php.net/manual/es/function.session-cache-limiter.php)\npara una mejor explicación sobre esos valores.\n\n\n## Implicaciones SEO <span id=\"seo-implications\"></span>\n\nLos robots de motores de búsqueda tienden a respetar las cabeceras de caché. Dado que algunos `crawlers` tienen limitado\nel número de páginas que pueden rastrear por dominios dentro de un cierto período de tiempo, la introducción de cabeceras\nde caché pueden ayudar a la indexación del sitio Web y reducir el número de páginas que deben ser procesadas.\n\n"
  },
  {
    "path": "docs/guide-es/caching-overview.md",
    "content": "El Almacenamiento en Caché\n==========================\n\nEl almacenamiento en caché es una forma económica y eficaz para mejorar el rendimiento de una aplicación web. Mediante\nel almacenamiento de datos relativamente estáticos en la memoria caché y su correspondiente recuperación cuando éstos sean\nsolicidatos, la aplicación salvaría todo ese tiempo y recursos necesarios para volver a generarlos cada vez desde cero.\n\nEl almacenamiento en caché se puede usar en diferentes niveles y lugares en una aplicación web. En el lado del servidor, al más bajo nivel,\nla caché puede ser usada para almacenar datos básicos, tales como una una lista de los artículos más recientes obtenidos de una base de datos;\ny en el más alto nivel, la caché puede ser usada para almacenar fragmentos o la totalidad de las páginas web, tales como el resultado del renderizado de los artículos más recientes. En el lado del cliente, el almacenamiento en caché HTTP puede ser utilizado para mantener\nel contenido de la página que ha sido visitada más recientemente en el caché del navegador.\n\nYii soporta los siguientes mecanismos de almacenamiento de caché:\n\n* [Caché de datos](caching-data.md)\n* [Caché de fragmentos](caching-fragment.md)\n* [Caché de páginas](caching-page.md)\n* [Caché HTTP](caching-http.md)\n"
  },
  {
    "path": "docs/guide-es/caching-page.md",
    "content": "Caché de Páginas\n================\n\nLa caché de páginas se refiere a guardar el contenido de toda una página en el almacenamiento de caché del servidor.\nPosteriormente, cuando la misma página sea requerida de nuevo, su contenido será devuelto desde la caché en vez de\nvolver a generarlo desde cero.\n\nEl almacenamiento en caché de páginas está soportado por [[yii\\filters\\PageCache]], un [filtro de acción](structure-filters.md).\nPuede ser utilizado de la siguiente forma en un controlador:\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\PageCache',\n            'only' => ['index'],\n            'duration' => 60,\n            'variations' => [\n                \\Yii::$app->language,\n            ],\n            'dependency' => [\n                'class' => 'yii\\caching\\DbDependency',\n                'sql' => 'SELECT COUNT(*) FROM post',\n            ],\n        ],\n    ];\n}\n```\n\nEl código anterior establece que el almacenamiento de páginas en caché debe ser utilizado sólo en la acción `index`; el\ncontenido de la página debería almacenarse durante un máximo de 60 segundos y ser variado por el idioma actual de la\naplicación; además, el almacenamiento de la página en caché debería ser invalidado si el número total de\nartículos ha cambiado.\n\nComo puedes ver, la caché de páginas es muy similar a la [caché de fragmentos](caching-fragment.md). Ambos soportan opciones\ntales como `duration`, `dependencies`, `variations`, y `enabled`. Su principal diferencia es que la caché de páginas está\nimplementado como un [filtro de acción](structure-filters.md) mientras que la caché de fragmentos se hace en un [widget](structure-widgets.md).\n\nPuedes usar la [caché de fragmentos](caching-fragment.md) así como el [contenido dinámico](caching-fragment.md#dynamic-content)\njunto con la caché de páginas.\n"
  },
  {
    "path": "docs/guide-es/concept-aliases.md",
    "content": "Alias\n=====\n\nLoa alias son utilizados para representar rutas o URLs de manera que no tengas que escribir explícitamente rutas absolutas o URLs en tu\nproyecto. Un alias debe comenzar con el signo `@` para ser diferenciado de una ruta normal de archivo y de URLs. Los alias definidos\nsin el `@` del principio, serán prefijados con el signo `@`.\n\nYii trae disponibles varios alias predefinidos. Por ejemplo, el alias `@yii` representa la ruta de instalación del\nframework Yii; `@web` representa la URL base para la aplicación Web ejecutándose.\n\nDefinir Alias <span id=\"defining-aliases\"></span>\n-------------\n\nPara definir un alias puedes llamar a [[Yii::setAlias()]] para una determinada ruta de archivo o URL. Por ejemplo,\n\n```php\n// un alias de una ruta de archivos\nYii::setAlias('@foo', '/path/to/foo');\n\n// una alias de un URL\nYii::setAlias('@bar', 'https://www.example.com');\n```\n\n> Note: Una ruta de archivo o URL en alias NO debe necesariamente referirse a un archivo o recurso existente.\n\nDado un alias, puedes derivar un nuevo alias (sin necesidad de llamar [[Yii::setAlias()]]) anexando una barra diagonal `/`\nseguida por uno o varios segmentos de la ruta. Llamamos los alias definidos a través de [[Yii::setAlias()]]\n*alias de raíz* (root alias), mientras que los alias derivados de ellos *alias derivados* (derived aliases). Por ejemplo,\n`@foo` es un alias de raíz, mientras que `@foo/bar/file.php` es un alias derivado.\n\nPuedes definir un alias usando otro alias (ya sea un alias de raíz o derivado):\n\n```php\nYii::setAlias('@foobar', '@foo/bar');\n```\n\nLos alias de raíz están usualmente definidos durante la etapa [bootstrapping](runtime-bootstrapping.md) de la aplicación.\nPor ejemplo, puedes llamar a [[Yii::setAlias()]] en el [script de entrada](structure-entry-scripts.md).\nPor conveniencia, [Application](structure-applications.md) provee una propiedad modificable llamada `aliases` que puedes\nconfigurar en la [configuración](concept-configurations.md) de la aplicación, como por ejemplo,\n\n```php\nreturn [\n    // ...\n    'aliases' => [\n        '@foo' => '/path/to/foo',\n        '@bar' => 'https://www.example.com',\n    ],\n];\n```\n\n\nResolución de Alias <span id=\"resolving-aliases\"></span>\n-------------------\n\nPuedes llamar [[Yii::getAlias()]] para resolver un alias de raíz en la ruta o URL que representa. El mismo método puede\nademás resolver un alias derivado en su correspondiente ruta de archivo o URL. Por ejemplo,\n\n```php\necho Yii::getAlias('@foo');               // muestra: /path/to/foo\necho Yii::getAlias('@bar');               // muestra: https://www.example.com\necho Yii::getAlias('@foo/bar/file.php');  // muestra: /path/to/foo/bar/file.php\n```\n\nLa ruta de archivo/URL representado por un alias derivado está determinado por la sustitución de la parte de su alias raíz\ncon su correspondiente ruta/Url en el alias derivado.\n\n> Note: El método [[Yii::getAlias()]] no comprueba si la ruta/URL resultante hacer referencia a un archivo o recurso existente.\n\n\nUn alias de raíz puede contener carácteres `/`. El método [[Yii::getAlias()]] es lo suficientemente inteligente para saber\nqué parte de un alias es un alias de raíz y por lo tanto determinar correctamente la correspondiente ruta de archivo o URL.\nPor ejemplo,\n\n```php\nYii::setAlias('@foo', '/path/to/foo');\nYii::setAlias('@foo/bar', '/path2/bar');\nYii::getAlias('@foo/test/file.php');  // muestra: /path/to/foo/test/file.php\nYii::getAlias('@foo/bar/file.php');   // muestra: /path2/bar/file.php\n```\n\nSi `@foo/bar` no está definido como un alias de raíz, la última declaración mostraría `/path/to/foo/bar/file.php`.\n\n\nUsando Alias <span id=\"using-aliases\"></span>\n------------\n\nLos alias son utilizados en muchos lugares en Yii sin necesidad de llamar [[Yii::getAlias()]] para convertirlos\nen rutas/URLs. Por ejemplo, [[yii\\caching\\FileCache::cachePath]] puede aceptar tanto una ruta de archivo como un alias\nque represente la ruta de archivo, gracias al prefijo `@` el cual permite diferenciar una ruta de archivo\nde un alias.\n\n```php\nuse yii\\caching\\FileCache;\n\n$cache = new FileCache([\n    'cachePath' => '@runtime/cache',\n]);\n```\n\nPor favor, presta atención a la documentación API para ver si una propiedad o el parámetro de un método soporta alias.\n\n\nAlias Predefinidos <span id=\"predefined-aliases\"></span>\n------------------\n\nYii predefine un conjunto de alias para aliviar la necesidad de hacer referencia a rutas de archivo o URLs que son\nutilizadas regularmente. La siguiente es la lista de alias predefinidos por Yii:\n\n- `@yii`: el directorio donde el archivo `BaseYii.php` se encuentra (también llamado el directorio del framework).\n- `@app`: la [[yii\\base\\Application::basePath|ruta base]] de la aplicación que se está ejecutando actualmente.\n- `@runtime`: la [[yii\\base\\Application::runtimePath|ruta de ejecución]] de la aplicación en ejecución. Por defecto `@app/runtime`.\n- `@webroot`: el directorio raíz Web de la aplicación Web se está ejecutando actualmente.\n- `@web`: la URL base de la aplicación web se ejecuta actualmente. Tiene el mismo valor que [[yii\\web\\Request::baseUrl]].\n- `@vendor`: el [[yii\\base\\Application::vendorPath|directorio vendor de Composer]]. Por defecto `@app/vendor`.\n- `@bower`, el directorio raíz que contiene [paquetes bower](https://bower.io/). Por defecto `@vendor/bower`.\n- `@npm`, el directorio raíz que contiene [paquetes npm](https://www.npmjs.com/). Por defecto `@vendor/npm`.\n\nEl alias `@yii` se define cuando incluyes el archivo `Yii.php` en tu [script de entrada](structure-entry-scripts.md),\nmientras que el resto de los alias están definidos en el constructor de la aplicación cuando se aplica la\n[configuración](concept-configurations.md) de la aplicación.\n\n\nAlias en Extensiones <span id=\"extension-aliases\"></span>\n--------------------\n\nUn alias se define automáticamente por cada [extensión](structure-extensions.md) que ha sido instalada a través de Composer.\nEl alias es nombrado tras el `namespace` de raíz de la extensión instalada tal y como está declarada en su archivo `composer.json`,\ny representa el directorio raíz de la extensión. Por ejemplo, si instalas la extensión `yiisoft/yii2-jui`, tendrás\nautomáticamente definido el alias `@yii/jui` durante la etapa [bootstrapping](runtime-bootstrapping.md) de la aplicación:\n\n```php\nYii::setAlias('@yii/jui', 'VendorPath/yiisoft/yii2-jui');\n```\n"
  },
  {
    "path": "docs/guide-es/concept-autoloading.md",
    "content": "Autocarga de clases\n===================\n\nYii depende del [mecanismo de autocarga de clases](https://www.php.net/manual/es/language.oop5.autoload.php) para localizar\ne incluir los archivos de las clases requiridas. Proporciona un cargador de clases de alto rendimiento que cumple con el\n[estandard PSR-4](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md).\nEl cargador se instala cuando incluyes el archivo `Yii.php`.\n\n> Note: Para simplificar la descripción, en esta sección sólo hablaremos de la carga automática de clases. Sin embargo,\n  ten en cuenta que el contenido que describimos aquí también se aplica a la autocarga de interfaces y rasgos (Traits).\n\n\nUsando el Autocargador de Yii <span id=\"using-yii-autoloader\"></span>\n-----------------------------\n\nPara utilizar el cargador automático de clases de Yii, deberías seguir dos reglas básicas cuando desarrolles y nombres tus\nclases:\n\n* Cada clase debe estar bajo un espacio de nombre (namespace). Por ejemplo `foo\\bar\\MyClass`.\n* Cada clase debe estar guardada en un archivo individual cuya ruta está determinada por el siguiente algoritmo:\n\n```php\n// $className es un nombre completo de clase con las iniciales barras invertidas.\n$classFile = Yii::getAlias('@' . str_replace('\\\\', '/', $className) . '.php');\n```\n\nPor ejemplo, si el nombre de una clase es `foo\\bar\\MyClass`, el [alias](concept-aliases.md) la correspondiente ruta de\narchivo de la clase sería `@foo/bar/MyClass.php`. Para que este sea capaz de ser resuelto como una ruta de archivo, ya sea\n`@foo` o `@foo/bar` debe ser un [alias de raíz](concept-aliases.md#defining-aliases) (root alias).\n\nCuando utilizas la [Plantilla de Aplicación Básica](start-installation.md), puede que pongas tus clases bajo el nivel superior\nde espacio de nombres `app` para que de esta manera pueda ser automáticamente cargado por Yii sin tener la necesidad de\ndefinir un nuevo alias. Esto es porque `@app` es un [alias predefinido](concept-aliases.md#predefined-aliases), y el\nnombre de una clase tal como `app\\components\\MyClass` puede ser resuelto en el archivo de la clase `AppBasePath/components/MyClass.php`,\nde acuerdo con el algoritmo previamente descrito.\n\nEn la [Plantilla de Aplicación Avanzada](tutorial-advanced-app.md), cada nivel tiene su propio alias. Por ejemplo, el nivel\n`front-end` tiene un alias de raíz `@frontend` mientras que el nivel `back-end` tiene `@backend`. Como resultado, es posible\nponer las clases `front-end` bajo el espacio de nombres `frontend` mientras que las clases `back-end` pueden hacerlo bajo\n`backend`. Esto permitirá que estas clases sean automaticamente cargadas por el autocargador de Yii.\n\n\nMapa de Clases <span id=\"class-map\"></span>\n--------------\n\nEl autocargador de clases de Yii soporta el *mapa de clases*, que mapea nombres de clases to sus correpondientes rutas de\narchvios. Cuando el autocargador esta cargando una clase, primero chequeará si la clase se encuentra en el mapa. Si es así,\nel correspondiente archivo será incluido directamente sin más comprobación. Esto hace que la clase se cargue muy rápidamente.\nDe hecho, todas las clases de Yii son autocargadas de esta manera.\n\nPuedes añadir una clase al mapa de clases `Yii::$classMap` de la siguiente forma,\n\n```php\nYii::$classMap['foo\\bar\\MyClass'] = 'path/to/MyClass.php';\n```\n\n[Alias](concept-aliases.md) puede ser usado para especificar la ruta de archivos de clases. Deberías iniciar el mapeo de\nclases en el proceso [bootstrapping](runtime-bootstrapping.md) de la aplicación para que de esta manera el mapa esté listo\nantes de que tus clases sean usadas.\n\n\nUsando otros Autocargadores <span id=\"using-other-autoloaders\"></span>\n---------------------------\n\nDebido a que Yii incluye Composer como un gestor de dependencias y extensions, es recomendado que también instales el\nautocargador de Composer. Si estás usando alguna librería externa que requiere sus autocargadores, también deberías\ninstalarlos.\n\nCuando se utiliza el cargador de clases automático de Yii conjuntamente con otros autocargadores, deberías incluir el\narchivo `Yii.php` *después* de que todos los demás autocargadores se hayan instalado. Esto hará que el autocargador de\nYii sea el primero en responder a cualquier petición de carga automática de clases. Por ejemplo, el siguiente código ha\nsido extraido del [script de entrada](structure-entry-scripts.md) de la [Plantilla de Aplicación Básica](start-installation.md).\nLa primera línea instala el autocargador de Composer, mientras que la segunda línea instala el autocargador de Yii.\n\n```php\nrequire __DIR__ . '/../vendor/autoload.php';\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n```\n\nPuedes usar el autocargador de Composer sin el autocargador de Yii. Sin embargo, al hacerlo, la eficacia de la carga de\ntus clases puede que se degrade, y además deberías seguir las reglas establecidas por Composer para que tus clases pudieran\nser autocargables.\n\n> Note: Si no deseas utilizar el autocargador de Yii, tendrás que crear tu propia versión del archivo `Yii.php` e\n  incluirlo en tu [script de entrada](structure-entry-scripts.md).\n\n\nCarga Automática de Clases de Extensiones <span id=\"autoloading-extension-classes\"></span>\n-----------------------------------------\n\nEl autocargador de Yii es capaz de autocargar clases de [extensiones](structure-extensions.md). El único requirimiento es\nque la extensión especifique correctamente la sección de `autoload` (autocarga) en su archivo `composer.json`. Por favor,\nconsulta la [documentación de Composer](https://getcomposer.org/doc/04-schema.md#autoload) para más detalles acerca de la\nespecificación `autoload`.\n\nEn el caso de que no quieras usar el autocargador de Yii, el autocargador de Composer podría cargar las clases de extensiones\npor tí.\n"
  },
  {
    "path": "docs/guide-es/concept-behaviors.md",
    "content": "Comportamientos\n===============\n\nComportamientos son instancias de [[yii\\base\\Behavior]] o sus clases \"hija\". Comportamientos, también conocido como\n[mixins](https://es.wikipedia.org/wiki/Mixin), te permiten mejorar la funcionalidad de un [[yii\\base\\Component|componente]]\nexistente sin necesidad de modificar su herencia de clases.\nCuando un comportamiento se une a un componente, \"inyectará\" sus métodos y propiedades dentro del componente, y podrás\nacceder a esos métodos y propiedades como si hubieran estado definidos por la clase de componente. Además, un\ncomportamiento puede responder a [eventos](concept-events.md) disparados por el componente de modo que se pueda personalizar\no adaptar a la ejecución normal del código del componente.\n\n\nDefiniendo comportamientos <span id=\"defining-behaviors\"></span>\n--------------------------\n\nPara definir un comportamiento, se debe crear una clase que exiende [[yii\\base\\Behavior]], o se extiende una clase hija. Por ejemplo:\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Behavior;\n\nclass MyBehavior extends Behavior\n{\n    public $prop1;\n\n    private $_prop2;\n\n    public function getProp2()\n    {\n        return $this->_prop2;\n    }\n\n    public function setProp2($value)\n    {\n        $this->_prop2 = $value;\n    }\n\n    public function foo()\n    {\n        // ...\n    }\n}\n```\nEl código anterior define la clase de comportamiento (behavior) app\\components\\MyBehavior`, con dos propiedades --\n`prop1` y `prop2`--y un método `foo()`. Tenga en cuenta que la propiedad `prop2`\nse define a través de la getter `getProp2()` y el setter `setProp2()`. Este caso es porque [[yii\\base\\Behavior]] extiende [[yii\\base\\BaseObject]] y por lo tanto se apoya en la definición de [propiedades](concept-properties.md) via getters y setters.\n\nDebido a que esta clase es un comportamiento, cuando está unido a un componente, el componente también tienen la propiedad `prop1` y `prop2` y el método `foo()`.\n\n> Tip: Dentro de un comportamiento, puede acceder al componente que el comportamiento está unido a través de la propiedad [[yii\\base\\Behavior::owner]].\n\n\nGestión de eventos de componentes\n---------------------------------\n\nSi un comportamiento necesita responder a los acontecimientos desencadenados por el componente al que está unido, se debe reemplazar el método [[yii\\base\\Behavior::events()]]. Por ejemplo:\n\n```php\nnamespace app\\components;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\base\\Behavior;\n\nclass MyBehavior extends Behavior\n{\n    // ...\n\n    public function events()\n    {\n        return [\n            ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',\n        ];\n    }\n\n    public function beforeValidate($event)\n    {\n        // ...\n    }\n}\n```\n\nEl método [[yii\\base\\Behavior::events()|events()]] debe devolver una lista de eventos y sus correspondientes controladores.\nEl ejemplo anterior declara que el evento [[yii\\db\\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]] existe y esta  exists y define su controlador, `beforeValidate()`. Al especificar un controlador de eventos, puede utilizar uno de los siguientes formatos:\n\n* una cadena que se refiere al nombre de un método de la clase del comportamiento, como el ejemplo anterior\n* un arreglo de objeto o nombre de clase, y un nombre de método como una cadena (sin paréntesis), ej., `[$object, 'methodName']`;\n* una función anónima \n\nLa firma de un controlador de eventos debe ser la siguiente, donde `$ event` refiere al parámetro de evento. Por favor, consulte la sección [Eventos](concept-events.md) para más detalles sobre los eventos.\n\n```php\nfunction ($event) {\n}\n```\n\n\nVinculando Comportamientos <span id=\"attaching-behaviors\"></span>\n--------------------------\n\nPuedes vincular un comportamiento a un [[yii\\base\\Component|componente]] ya sea estática o dinámicamente. La primera forma\nes la más comúnmente utilizada en la práctica.\n\nPara unir un comportamiento estáticamente, reemplaza el método [[yii\\base\\Component::behaviors()|behaviors()]] dde la clase de componente a la que se une el comportamiento. El método [[yii\\base\\Component::behaviors()|behaviors()]]  debe devolver una lista de comportamiento [configuraciones](concept-configurations.md).\nCada configuración de comportamiento puede ser un nombre de clase de comportamiento o un arreglo de configuración:\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\nuse app\\components\\MyBehavior;\n\nclass User extends ActiveRecord\n{\n    public function behaviors()\n    {\n        return [\n            // anonymous behavior, behavior class name only\n            MyBehavior::class,\n\n            // named behavior, behavior class name only\n            'myBehavior2' => MyBehavior::class,\n\n            // anonymous behavior, configuration array\n            [\n                'class' => MyBehavior::class,\n                'prop1' => 'value1',\n                'prop2' => 'value2',\n            ],\n\n            // named behavior, configuration array\n            'myBehavior4' => [\n                'class' => MyBehavior::class,\n                'prop1' => 'value1',\n                'prop2' => 'value2',\n            ]\n        ];\n    }\n}\n```\n\nPuedes asociciar un nombre a un comportamiento especificándolo en la clave de la matriz correspondiente a la configuración\ndel comportamiento. En este caso, el comportamiento puede ser llamado un *comportamiento nombrado* (named behavior). En\nel ejemplo anterior, hay dos tipos de comportamientos nombrados: `myBehavior2` y `myBehavior4`. Si un comportamiento\nno está asociado con un nombre, se le llama *comportamiento anónimo* (anonymous behavior).\n\nPara vincular un comportamiento dinámicamente, llama al método [[yii\\base\\Component::attachBehavior()]] desde el componente al\nque se le va a unir el comportamiento:\n\n```php\nuse app\\components\\MyBehavior;\n\n// vincular un objeto comportamiento \"behavior\"\n$component->attachBehavior('myBehavior1', new MyBehavior());\n\n// vincular una clase comportamiento\n$component->attachBehavior('myBehavior2', MyBehavior::class);\n\n// asociar una matriz de configuración\n$component->attachBehavior('myBehavior3', [\n    'class' => MyBehavior::class,\n    'prop1' => 'value1',\n    'prop2' => 'value2',\n]);\n```\nPuede vincular múltiples comportamientos a la vez mediante el uso del método [[yii\\base\\Component::attachBehaviors()]]. Por ejemplo,\n\n```php\n$component->attachBehaviors([\n    'myBehavior1' => new MyBehavior(), // un comportamiento nombrado\n    MyBehavior::class,                 // un comportamiento anónimo\n]);\n```\n\nTambién puedes asociar comportamientos a traves de [configuraciones](concept-configurations.md) como el siguiente:\n\n```php\n[\n    'as myBehavior2' => MyBehavior::class,\n\n    'as myBehavior3' => [\n        'class' => MyBehavior::class,\n        'prop1' => 'value1',\n        'prop2' => 'value2',\n    ],\n]\n```\n\nPara más detalles, por favor visita la sección [Configuraciones](concept-configurations.md#configuration-format).\n\n\nUsando comportamientos <span id=\"using-behaviors\"></span>\n----------------------\n\nPara poder utilizar un comportamiento, primero tienes que unirlo a un [[yii\\base\\Component|componente]] según las instrucciones anteriores. Una vez que un comportamiento ha sido vinculado a un componente, su uso es sencillo.\n\nPuedes usar a una variable *pública* o a una [propiedad](concept-properties.md) definida por un `getter` y/o un `setter`\ndel comportamiento a través del componente con el que se ha vinculado:\n\n```php\n// \"prop1\" es una propiedad definida en la clase comportamiento\necho $component->prop1;\n$component->prop1 = $value;\n```\n\nTambién puedes llamar métodos *públicos* del comportamiento de una forma similar:\n\n```php\n// foo() es un método público definido dentro de la clase comportamiento\n$component->foo();\n```\n\nComo puedes ver, aunque `$component` no tiene definida `prop1` y `bar()`, que se pueden utilizar como si son parte\nde la definición de componentes debido al comportamiento vinculado.\n\nSi dos comportamientos definen la misma propiedad o método y ambos están vinculados con el mismo componente, el\ncomportamiento que ha sido vinculado *primero* tendrá preferencia cuando se esté accediendo a la propiedad o método.\n\nUn comportamiento puede estar asociado con un nombre cuando se une a un componente. Si este es el caso, es posible\nacceder al objeto de comportamiento mediante el nombre, como se muestra a continuación,\n\n```php\n$behavior = $component->getBehavior('myBehavior');\n```\n\nTambién puedes acceder a todos los comportamientos vinculados al componente:\n\n```php\n$behaviors = $component->getBehaviors();\n```\n\n\nDesasociar Comportamientos <span id=\"detaching-behaviors\"></span>\n--------------------------\n\nPara desasociar un comportamiento, puedes llamar el método [[yii\\base\\Component::detachBehavior()]] con el nombre con el\nque se le asoció:\n\n```php\n$component->detachBehavior('myBehavior1');\n```\n\nTambién puedes desvincular *todos* los comportamientos:\n\n```php\n$component->detachBehaviors();\n```\n\n\nUtilizando `TimestampBehavior` <span id=\"using-timestamp-behavior\"></span>\n-----------------------------\n\nPara terminar, vamos a echar un vistazo a [[yii\\behaviors\\TimestampBehavior]]. Este comportamiento soporta de forma\nautomática la actualización de atributos timestamp de un modelo [[yii\\db\\ActiveRecord|Registro Activo]]\n(Active Record) en cualquier momento donde se guarda el modelo (ej., en la inserción o actualización).\n\nPrimero, vincula este comportamiento a la clase [[yii\\db\\ActiveRecord|Active Record]] que desees utilizar.\n\n```php\nnamespace app\\models\\User;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\behaviors\\TimestampBehavior;\n\nclass User extends ActiveRecord\n{\n    // ...\n\n    public function behaviors()\n    {\n        return [\n            [\n                'class' => TimestampBehavior::class,\n                'attributes' => [\n                    ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],\n                    ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'],\n                ],\n            ],\n        ];\n    }\n}\n```\n\nLa configuración del comportamiento anterior especifica que\n\n* cuando el registro está siendo insertado, el comportamiento debe asignar el sello de tiempo actual a los atributos\n  `created_at` y `updated_at`;\n* cuando el registro está siendo actualizado, el comportamiento debe asignar el sello de tiempo actual al atributo\n  `updated_at`.\n\nAhora si tienes un objeto `User` e intentas guardarlo, descubrirás que sus campos `created_at` y `updated_at` están\nautomáticamente actualizados con el sello de tiempo actual:\n\n```php\n$user = new User;\n$user->email = 'test@example.com';\n$user->save();\necho $user->created_at;  // muestra el sello tiempo actual (timestamp)\n```\n\nEl comportamiento [[yii\\behaviors\\TimestampBehavior|TimestampBehavior]] también ofrece un método muy útil llamado\n[[yii\\behaviors\\TimestampBehavior::touch()|touch()]], que asigna el sello de tiempo actual a un atributo especificado y lo guarda automáticamente en la base de datos:\n\n```php\n$user->touch('login_time');\n```\n\n\nComparación con Traits <span id=\"comparison-with-traits\"></span>\n----------------------\n\nMientras que los comportamientos son similares a [traits](https://www.php.net/manual/es/language.oop5.traits.php) en cuanto que ambos \"inyectan\" sus\nmétodos  y propiedades a la clase primaria, son diferentes en muchos aspectos. Tal y como se describe abajo, los dos\ntienen sus ventajas y desventajas. Son más como complementos el uno al otro en lugar de alternativas.\n\n\n### Razones para utilizar comportamientos <span id=\"pros-for-behaviors\"></span>\n\nLas clases de comportamientos, como todas las clases, soportan herencias. Traits, por otro lado, pueden ser\nconsiderados como un copia-y-pega de PHP. Ellos no soportan la herencia de clases.\n\nLos comportamientos pueden ser asociados y desasociados a un componente dinámicamente sin necesidad de que la clase del\ncomponente sea modificada. Para usar un trait, debes modificar la clase que la usa.\n\nLos comportamientos son configurables mientras que los traits no.\n\nLos comportamientos pueden personalizar la ejecución de un componente al responder a sus eventos.\n\nCuando hay un conflicto de nombre entre los diferentes comportamientos vinculados a un mismo componente, el conflicto es\nautomáticamente resuelto respetando al que ha sido asociado primero.\nEl conflicto de nombres en traits requiere que manualmente sean resueltos cambiando el nombre de las propiedades o métodos afectados.\n\n\n### Razones para utilizar los Traits <span id=\"pros-for-traits\"></span>\n\nLos Traits son mucho más eficientes que los comportamientos debido a que los últimos son objetos que consumen tiempo y\nmemoria.\n\nLos IDEs (Programas de desarrollo) son más amigables con traits ya que son una construcción del lenguaje nativo.\n\n"
  },
  {
    "path": "docs/guide-es/concept-components.md",
    "content": "Componentes\n===========\n\nLos componentes son los principales bloques de construcción de las aplicaciones Yii. Los componentes son instancias de [[yii\\base\\Component]] o de una clase extendida. Las tres características principales que los componentes proporcionan\na las otras clases son:\n\n* [Propiedades](concept-properties.md)\n* [Eventos](concept-events.md)\n* [Comportamientos](concept-behaviors.md)\n\nPor separado y combinadas, estas características hacen que las clases Yii sean mucho mas personalizables y sean mucho más fáciles de usar. Por ejemplo, el incluido [[yii\\jui\\DatePicker|widget de selección de fecha]], un componente de la interfaz de usuario, puede ser utilizado en una [vista](structure-view.md) para generar un DatePicker interactivo:\n\n```php\nuse yii\\jui\\DatePicker;\n\necho DatePicker::widget([\n    'language' => 'ru',\n    'name'  => 'country',\n    'clientOptions' => [\n        'dateFormat' => 'yy-mm-dd',\n    ],\n]);\n```\n\nLas propiedades del widget son fácilmente modificables porque la clase se extiende de [[yii\\base\\Component]].\n\nMientras que los componentes son muy potentes, son un poco más pesados que los objetos normales, debido al hecho de que necesitan más memoria y tiempo de CPU para poder soportar [eventos](concept-events.md) y [comportamientos](concept-behaviors.md) en particular.\nSi tus componentes no necesitan estas dos características, deberías considerar extender tu componente directamente de [[yii\\base\\BaseObject]] en vez de [[yii\\base\\Component]]. De esta manera harás que tus componentes sean mucho más eficientes que objetos PHP normales, pero con el añadido soporte para [propiedades](concept-properties.md).\n\nCuando extiendes tu clase de [[yii\\base\\Component]] o [[yii\\base\\BaseObject]], se recomienda que sigas las siguientes convenciones:\n\n- Si sobrescribes el constructor, especifica un parámetro `$config` como el *último* parámetro del constructor, y después pasa este parámetro al constructor padre.\n- Siempre llama al constructor padre al *final* de su propio constructor.\n- Si sobrescribes el método [[yii\\base\\BaseObject::init()]], asegúrese de llamar la implementación padre de `init` * al principio * de su método` init`.\n\nPor ejemplo:\n\n```php\nnamespace yii\\components\\MyClass;\n\nuse yii\\base\\BaseObject;\n\nclass MyClass extends BaseObject\n{\n    public $prop1;\n    public $prop2;\n\n    public function __construct($param1, $param2, $config = [])\n    {\n        // ... inicialización antes de la configuración está siendo aplicada\n\n        parent::__construct($config);\n    }\n\n    public function init()\n    {\n        parent::init();\n\n        // ... inicialización después de la configuración esta siendo aplicada\n    }\n}\n```\n\nSiguiendo esas directrices hará que tus componentes sean [configurables](concept-configurations.md) cuando son creados. Por ejemplo:\n\n```php\n$component = new MyClass(1, 2, ['prop1' => 3, 'prop2' => 4]);\n// alternativamente\n$component = \\Yii::createObject([\n    'class' => MyClass::class,\n    'prop1' => 3,\n    'prop2' => 4,\n], [1, 2]);\n```\n\n> Info: Mientras que el enfoque de llamar [[Yii::createObject()]] parece mucho más complicado, es mucho más potente debido al hecho de que se implementa en la parte superior de un [contenedor de inyección de dependencia](concept-di-container.md).\n  \n\nLa clase [[yii\\base\\BaseObject]] hace cumplir el siguiente ciclo de vida del objeto:\n\n1. Pre-inicialización en el constructor. Puedes establecer los valores predeterminados de propiedades aquí.\n2. Configuración del objeto a través de `$config`. La configuración puede sobrescribir los valores prdeterminados dentro del constructor.\n3. Post-inicialización dentro de [[yii\\base\\BaseObject::init()|init()]]. Puedes sobrescribir este método para realizar comprobaciones de validez y normalización de las propiedades.\n4. Llamadas a métodos del objeto.\n\nLos tres primeros pasos ocurren dentro del constructor del objeto. Esto significa que una vez obtengas la instancia de un objeto, ésta ha sido inicializada para que puedas utilizarla adecuadamente.\n"
  },
  {
    "path": "docs/guide-es/concept-configurations.md",
    "content": "Configuración\n==============\n\nLas configuraciones se utilizan ampliamente en Yii al crear nuevos objetos o inicializar los objetos existentes. Las configuraciones por lo general incluyen el nombre de la clase del objeto que se está creando, y una lista de los valores iniciales que deberían ser asignadas a las del [propiedades](concept-properties.md) objeto. Las configuraciones también pueden incluir una lista de manipuladores que deban imponerse a del objeto [eventos](concept-events.md) y/o una lista de [comportamientos](concept-behaviors.md) que también ha de atribuirse al objeto.\n\nA continuación, una configuración que se utiliza para crear e inicializar una conexión a base de datos:\n\n```php\n$config = [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n];\n\n$db = Yii::createObject($config);\n```\nEl método [[Yii::createObject()]] toma una matriz de configuración como su argumento, y crea un objeto intanciando la clase llamada en la configuración. Cuando se crea una instancia del objeto, el resto de la configuración se utilizará para inicializar las propiedades del objeto, controladores de eventos y comportamientos.\n\nSi usted ya tiene un objeto, puede usar [[Yii::configure()]] para inicializar las propiedades del objeto con una matriz de configuración:\n\n```php\nYii::configure($object, $config);\n```\n\nTenga en cuenta que en este caso, la matriz de configuración no debe contener un elemento `class`.\n\n## Formato de Configuración <span id=\"configuration-format\"></span>\n\nEl formato de una configuración se puede describir formalmente como:\n\n```php\n[\n    'class' => 'ClassName',\n    'propertyName' => 'propertyValue',\n    'on eventName' => $eventHandler,\n    'as behaviorName' => $behaviorConfig,\n]\n```\n\ndonde\n\n* El elemento `class` especifica un nombre de clase completo para el objeto que se está creando.\n* Los elementos `propertyName` especifica los valores iniciales de la propiedad con nombre. Las claves son los nombres de las propiedades y los valores son los valores iniciales correspondientes. Sólo los miembros de variables públicas y [propiedades](concept-properties.md) definidas por getters/setters se pueden configurar.\n* Los elementos `on eventName` especifican qué manipuladores deberán adjuntarse al del objeto [eventos](concept-events.md). Observe que las claves de matriz se forman prefijando nombres de eventos con `on`. Por favor, consulte la sección [Eventos](concept-events.md) para los formatos de controlador de eventos compatibles.\n* Los elementos `as behaviorName` especifican qué [comportamientos](concept-behaviors.md) deben adjuntarse al objeto. Observe que las claves de matriz se forman prefijando nombres de comportamiento con `as`; el valor, `$behaviorConfig`, representa la configuración para la creación de un comportamiento, como una configuración normal descrita aquí.\n\nA continuación se muestra un ejemplo de una configuración con los valores de propiedad iniciales, controladores de eventos y comportamientos:\n\n```php\n[\n    'class' => 'app\\components\\SearchEngine',\n    'apiKey' => 'xxxxxxxx',\n    'on search' => function ($event) {\n        Yii::info(\"Keyword searched: \" . $event->keyword);\n    },\n    'as indexer' => [\n        'class' => 'app\\components\\IndexerBehavior',\n        // ... property init values ...\n    ],\n]\n```\n\n\n## Usando Configuraciones <span id=\"using-configurations\"></span>\n\nLas configuraciones se utilizan en muchos lugares en Yii. Al comienzo de esta sección, hemos demostrado cómo crear un objeto según una configuración mediante el uso de [[Yii::createObject()]]. En este apartado, vamos a describir configuraciones de aplicaciones y configuraciones widget - dos principales usos de configuraciones.\n\n\n### Configuraciones de aplicación <span id=\"application-configurations\"></span>\n\nConfiguración para una [aplicación](structure-applications.md) es probablemente una de las configuraciones más complejas. Esto se debe a que la clase [[yii\\web\\Application|aplicación]] tiene un montón de propiedades y eventos configurables. Más importante aún, su propiedad [[yii\\web\\Application::components|componentes]] que puede recibir una gran variedad de configuraciones para crear componentes que se registran a través de la aplicación. Lo siguiente es un resumen del archivo de configuración de la aplicación para la [plantilla básica de la aplicación](start-installation.md).\n\n```php\n$config = [\n    'id' => 'basic',\n    'basePath' => dirname(__DIR__),\n    'extensions' => require __DIR__ . '/../vendor/yiisoft/extensions.php',\n    'components' => [\n        'cache' => [\n            'class' => 'yii\\caching\\FileCache',\n        ],\n        'mailer' => [\n            'class' => 'yii\\swiftmailer\\Mailer',\n        ],\n        'log' => [\n            'class' => 'yii\\log\\Dispatcher',\n            'traceLevel' => YII_DEBUG ? 3 : 0,\n            'targets' => [\n                [\n                    'class' => 'yii\\log\\FileTarget',\n                ],\n            ],\n        ],\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=stay2',\n            'username' => 'root',\n            'password' => '',\n            'charset' => 'utf8',\n        ],\n    ],\n];\n```\n\nLa configuración no tiene una clave `class`. Esto es porque se utiliza como sigue en un [script de entrada](structure-entry-scripts.md), donde ya se le da el nombre de la clase,\n\n```php\n(new yii\\web\\Application($config))->run();\n```\n\nPara más detalles sobre la configuración de la propiedad `components` de una aplicación se puede encontrar en la sección [Aplicación](structure-applications.md) y la sección [Localizador de Servicio](concept-service-locator.md).\n\n\n### Configuración Widget <span id=\"widget-configurations\"></span>\n\nCuando se utiliza [widgets](structure-widgets.md), a menudo es necesario utilizar las configuraciones para personalizar las propiedades de widgets. Tanto los metodos [[yii\\base\\Widget::widget()]] y [[yii\\base\\Widget::begin()]] pueden usarse para crear un widget. Toman un arreglo de configuración, como el siguiente,\n\n```php\nuse yii\\widgets\\Menu;\n\necho Menu::widget([\n    'activateItems' => false,\n    'items' => [\n        ['label' => 'Home', 'url' => ['site/index']],\n        ['label' => 'Products', 'url' => ['product/index']],\n        ['label' => 'Login', 'url' => ['site/login'], 'visible' => Yii::$app->user->isGuest],\n    ],\n]);\n```\n\nEl código anterior crea un widget `Menu` e inicializa su propiedad `activeItems` en falsa. La propiedad `items` también se configura con elementos de menú que se muestran.\n\nTenga en cuenta que debido a que el nombre de la clase ya está dado, la matriz de configuración no deben tener la clave `class`.\n\n\n## Archivos de Configuración <span id=\"configuration-files\"></span>\n\nCuando una configuración es muy compleja, una práctica común es almacenarla en uno o múltiples archivos PHP, conocidos como *archivos de configuración*. Un archivo de configuración devuelve un array de PHP que representa la configuración. Por ejemplo, es posible mantener una configuración de la aplicación en un archivo llamado `web.php`, como el siguiente,\n\n```php\nreturn [\n    'id' => 'basic',\n    'basePath' => dirname(__DIR__),\n    'extensions' => require __DIR__ . '/../vendor/yiisoft/extensions.php',\n    'components' => require __DIR__ . '/components.php',\n];\n```\n\nDebido a que la configuración `componentes` es compleja también, se guarda en un archivo separado llamado `components.php` y \"requerir\" este archivo en `web.php` como se muestra arriba. El contenido de `components.php` es el siguiente,\n\n```php\nreturn [\n    'cache' => [\n        'class' => 'yii\\caching\\FileCache',\n    ],\n    'mailer' => [\n        'class' => 'yii\\swiftmailer\\Mailer',\n    ],\n    'log' => [\n        'class' => 'yii\\log\\Dispatcher',\n        'traceLevel' => YII_DEBUG ? 3 : 0,\n        'targets' => [\n            [\n                'class' => 'yii\\log\\FileTarget',\n            ],\n        ],\n    ],\n    'db' => [\n        'class' => 'yii\\db\\Connection',\n        'dsn' => 'mysql:host=localhost;dbname=stay2',\n        'username' => 'root',\n        'password' => '',\n        'charset' => 'utf8',\n    ],\n];\n```\n\nPara obtener una configuración almacenada en un archivo de configuración, simplemente \"requerir\" este, como el siguiente:\n\n```php\n$config = require 'path/to/web.php';\n(new yii\\web\\Application($config))->run();\n```\n\n\n## Configuraciones por Defecto <span id=\"default-configurations\"></span>\n\nEl método [[Yii::createObject()]] es implementado en base a [contenedor de inyección de dependencia](concept-di-container.md). Le permite especificar un conjunto de los llamados *configuraciones predeterminadas* que se aplicarán a todos los casos de las clases especificadas cuando se crean utilizando [[Yii::createObject()]]. Las configuraciones por defecto se puede especificar llamando `Yii::$container->set()` en el código [bootstrapping](runtime-bootstrapping.md).\n\nPor ejemplo, si desea personalizar [[yii\\widgets\\LinkPager]] para que TODO enlace de búsqueda muestre como máximo 5 botones de página (el valor por defecto es 10), puede utilizar el siguiente código para lograr este objetivo,\n\n```php\n\\Yii::$container->set('yii\\widgets\\LinkPager', [\n    'maxButtonCount' => 5,\n]);\n```\n\nSin utilizar las configuraciones predeterminadas, usted tendría que configurar `maxButtonCount` en cada lugar en el que utiliza enlace paginador.\n\n## Constantes de Entorno <span id=\"environment-constants\"></span>\n\nLas configuraciones a menudo varían de acuerdo al entorno en que se ejecuta una aplicación. Por ejemplo, en el entorno de desarrollo, es posible que desee utilizar una base de datos llamada `mydb_dev`, mientras que en servidor de producción es posible que desee utilizar la base de datos `mydb_prod`. Para facilitar la conmutación de entornos, Yii proporciona una constante llamado `YII_ENV` que se puede definir en el [script de entrada](structure-entry-scripts.md) de su aplicación. Por ejemplo,\n\n```php\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n```\n\nUsted puede definir `YII_ENV` como uno de los valores siguientes:\n\n- `prod`: entorno de producción. La constante `YII_ENV_PROD` evaluará como verdadero. \nEste es el valor por defecto de `YII_ENV` si no esta definida.\n- `dev`: entorno de desarrollo. La constante `YII_ENV_DEV` evaluará como verdadero.\n- `test`: entorno de pruebas. La constante `YII_ENV_TEST` evaluará como verdadero.\n\nCon estas constantes de entorno, puede especificar sus configuraciones condicionales basado en el entorno actual. Por ejemplo, la configuración de la aplicación puede contener el siguiente código para permitir que el [depurador y barra de herramientas de depuración](tool-debugger.md) en el entorno de desarrollo.\n\n```php\n$config = [...];\n\nif (YII_ENV_DEV) {\n    // configuration adjustments for 'dev' environment\n    $config['bootstrap'][] = 'debug';\n    $config['modules']['debug'] = 'yii\\debug\\Module';\n}\n\nreturn $config;\n```\n"
  },
  {
    "path": "docs/guide-es/concept-di-container.md",
    "content": "Contenedor de Inyección de Dependencias\n=======================================\n\nUn contenedor de Inyección de Dependencias (ID), es un objeto que sabe como instancias y configurar objetos y sus \nobjetos dependientes. El [articulo de Martin](https://martinfowler.com/articles/injection.html) contiene una buena \nexplicación de porque son útiles los contenedores de ID. A continuación explicaremos como usar el contenedor de ID que \nproporciona Yii.\n\nInyección de Dependencias <span id=\"dependency-injection\"></span>\n-------------------------\n\nYii proporciona la función de contenedor de ID mediante la clase [[yii\\di\\Container]]. Soporta los siguientes tipos \nde ID:\n\n* Inyección de constructores;\n* Inyección de setters y propiedades;\n* Inyección de [llamadas de retorno PHP](https://www.php.net/manual/es/language.types.callable.php);\n\n### Inyección de Constructores <span id=\"constructor-injection\"></span>\n\nEl contenedor de ID soporta inyección de constructores con la ayuda de los indicios (hint) de tipo para los parámetros del \nconstructor. Los indicios de tipo le proporcionan información al contenedor para saber cuáles son las clases o \ninterfaces dependientes al usarse para crear un nuevo objeto. El contenedor intentara obtener las instancias de las \nclases o interfaces dependientes y las inyectará dentro del nuevo objeto mediante el constructor. Por ejemplo,\n\n```php\nclass Foo\n{\n    public function __construct(Bar $bar)\n    {\n    }\n}\n\n$foo = $container->get('Foo');\n// que es equivalente a:\n$bar = new Bar;\n$foo = new Foo($bar);\n```\n\n### Inyección de Setters y Propiedades <span id=\"setter-and-property-injection\"></span>\n\nLa inyección de setters y propiedades se admite a través de [configuraciones](concept-configurations.md). Cuando se \nregistra una dependencia o se crea un nuevo objeto, se puede proporcionar una configuración que usará el contenedor \npara inyectar las dependencias a través de sus correspondientes setters y propiedades. Por ejemplo,\n\n```php\nuse yii\\base\\BaseObject;\n\nclass Foo extends BaseObject\n{\n    public $bar;\n\n    private $_qux;\n\n    public function getQux()\n    {\n        return $this->_qux;\n    }\n\n    public function setQux(Qux $qux)\n    {\n        $this->_qux = $qux;\n    }\n}\n\n$container->get('Foo', [], [\n    'bar' => $container->get('Bar'),\n    'qux' => $container->get('Qux'),\n]);\n```\n\n### Inyección de Llamadas de retorno PHP <span id=\"php-callable-injection\"></span>\n\nEn este caso, el contenedor usará una llamada de retorno PHP registrada para construir una nueva instancia de una \nclase. La llamada de retorno se responsabiliza de que dependencias debe inyectar al nuevo objeto creado. Por ejemplo,\n\n```php\n$container->set('Foo', function ($container, $params, $config) {\n    return new Foo(new Bar);\n});\n\n$foo = $container->get('Foo');\n```\n\nRegistro de dependencias <span id=\"registering-dependencies\"></span>\n------------------------\n\nSe puede usar [[yii\\di\\Container::set()]] para registrar dependencias. El registro requiere un nombre de dependencia \nasí como una definición de dependencia. Un nombre de dependencia puede ser un nombre de clase, un nombre de interfaz, \no un nombre de alias; y una definición de dependencia puede ser un nombre de clase, un array de configuración, o una \nllamada de retorno PHP.\n\n```php\n$container = new \\yii\\di\\Container;\n\n// registra un nombre de clase como tal. Puede se omitido.\n$container->set('yii\\db\\Connection');\n\n// registra una interfaz\n// Cuando una clase depende de una interfaz, la clase correspondiente\n// se instanciará como un objeto dependiente\n$container->set('yii\\mail\\MailInterface', 'yii\\swiftmailer\\Mailer');\n\n// registra un nombre de alias. Se puede usar $container->get('foo')\n// para crear una instancia de Connection\n$container->set('foo', 'yii\\db\\Connection');\n\n// registrar una clase con configuración. La configuración\n// se aplicara cuando la clase se instancie por get()\n$container->set('yii\\db\\Connection', [\n    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n]);\n\n// registra un nombre de alias con configuración de clase\n// En este caso, se requiere un elemento \"clase\" para especificar la clase\n$container->set('db', [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n]);\n\n// registra una llamada de retorno de PHP\n// La llamada de retorno sera ejecutada cada vez que se ejecute $container->get('db') \n$container->set('db', function ($container, $params, $config) {\n    return new \\yii\\db\\Connection($config);\n});\n\n// registra un componente instancia\n// $container->get('pageCache') devolverá la misma instancia cada vez que se ejecute\n$container->set('pageCache', new FileCache);\n```\n\n> Tip: Si un nombre de dependencia es el mismo que la definición de dependencia, no es necesario registrarlo con \n  el contenedor de ID.\n\nUna dependencia registrada mediante `set()` generará una instancia cada vez que se necesite la dependencia. Se puede \nusar [[yii\\di\\Container::setSingleton()]] para registrar una dependencia que genere una única instancia:\n\n```php\n$container->setSingleton('yii\\db\\Connection', [\n    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n]);\n```\n\nResolución de Dependencias <span id=\"resolving-dependencies\"></span>\n--------------------------\n\nUna ves se hayan registrado las dependencias, se puede usar el contenedor de ID para crear nuevos objetos, y el \ncontenedor resolverá automáticamente las dependencias instanciándolas e inyectándolas dentro de los nuevos objetos \ncreados. La resolución de dependencias es recursiva, esto significa que si una dependencia tiene otras dependencias, \nestas dependencias también se resolverán automáticamente.\n\nSe puede usar [[yii\\di\\Container::get()]] para crear nuevos objetos. El método obtiene el nombre de dependencia, que \npuede ser un nombre de clase, un nombre de interfaz o un nombre de alias. El nombre de dependencia puede estar \nregistrado o no mediante `set()` o `setSingleton()`. Se puede proporcionar opcionalmente un listado de los parámetros \ndel constructor de clase y una [configuración](concept-configurations.md) para configurar los nuevos objetos creados. \nPor ejemplo,\n\n```php\n// \"db\" ha sido registrado anteriormente como nombre de alias\n$db = $container->get('db');\n\n// equivalente a: $engine = new \\app\\components\\SearchEngine($apiKey, ['type' => 1]);\n$engine = $container->get('app\\components\\SearchEngine', [$apiKey], ['type' => 1]);\n```\n\nPor detrás, el contenedor de ID efectúa mucho más trabajo la creación de un nuevo objeto. El contenedor primero \ninspeccionará la clase constructora para encontrar los nombres de clase o interfaces dependientes y después \nautomáticamente resolverá estas dependencias recursivamente.\n\nEl siguiente código muestra un ejemplo más sofisticado. La clase `UserLister` depende del un objeto que implementa la \ninterfaz `UserFinderInterface`; la clase `UserFinder` implementa la interfaz y depende del objeto `Connection`.  Todas \nestas dependencias se declaran a través de insinuaciones (hinting) de los parámetros del constructor de clase. Con el \nregistro de dependencia de propiedades, el contenedor de ID puede resolver las dependencias automáticamente y crear \nuna nueva instancia de `UserLister` con una simple llamada a `get('userLister')`.\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\BaseObject;\nuse yii\\db\\Connection;\nuse yii\\di\\Container;\n\ninterface UserFinderInterface\n{\n    function findUser();\n}\n\nclass UserFinder extends BaseObject implements UserFinderInterface\n{\n    public $db;\n\n    public function __construct(Connection $db, $config = [])\n    {\n        $this->db = $db;\n        parent::__construct($config);\n    }\n\n    public function findUser()\n    {\n    }\n}\n\nclass UserLister extends BaseObject\n{\n    public $finder;\n\n    public function __construct(UserFinderInterface $finder, $config = [])\n    {\n        $this->finder = $finder;\n        parent::__construct($config);\n    }\n}\n\n$container = new Container;\n$container->set('yii\\db\\Connection', [\n    'dsn' => '...',\n]);\n$container->set('app\\models\\UserFinderInterface', [\n    'class' => 'app\\models\\UserFinder',\n]);\n$container->set('userLister', 'app\\models\\UserLister');\n\n$lister = $container->get('userLister');\n\n// que es equivalente a:\n\n$db = new \\yii\\db\\Connection(['dsn' => '...']);\n$finder = new UserFinder($db);\n$lister = new UserLister($finder);\n```\n\nUso Practico <span id=\"practical-usage\"></span>\n------------\n\nYii crea un contenedor de ID cuando se incluye el archivo `Yii.php` en el \n[script de entrada](structure-entry-scripts.md) de la aplicación. Cuando se llama a [[Yii::createObject()]] el método \nrealmente llama al contenedor del método [[yii\\di\\Container::get()|get()]] para crear un nuevo objeto. Como se ha \ncomentado anteriormente, el contenedor de ID resolverá automáticamente las dependencias (si las hay) y las inyectará \ndentro del nuevo objeto creado. Debido a que Yii utiliza [[Yii::createObject()]] en la mayor parte del núcleo (core) \npara crear nuevo objetos, podemos personalizar los objetos globalmente para que puedan tratar con [[Yii::$container]].\n\nPor ejemplo, se puede personalizar globalmenete el numero predeterminado de números de botones de paginación de \n[[yii\\widgets\\LinkPager]]:\n\n```php\n\\Yii::$container->set('yii\\widgets\\LinkPager', ['maxButtonCount' => 5]);\n```\n\nAhora si se usa el widget en una vista con el siguiente código, la propiedad `maxButtonCount` será inicializada con \nvalor 5 en lugar de 10 que es el valor predeterminado definido en la clase.\n\n```php\necho \\yii\\widgets\\LinkPager::widget();\n```\n\nSe puede sobrescribir el valor establecido mediante el contenedor de ID, como a continuación:\n\n```php\necho \\yii\\widgets\\LinkPager::widget(['maxButtonCount' => 20]);\n```\n\nOtro ejemplo es aprovechar la ventaja de la inyección automática de constructores de contenedores de ID. Asumiendo que \nla clase controlador depende de otros objetos, tales como un servicio de reservas de hotel. Se puede declarar una \ndependencia a través de un parámetro del constructor y permitir al contenedor de ID resolverla por nosotros.\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\nuse app\\components\\BookingInterface;\n\nclass HotelController extends Controller\n{\n    protected $bookingService;\n\n    public function __construct($id, $module, BookingInterface $bookingService, $config = [])\n    {\n        $this->bookingService = $bookingService;\n        parent::__construct($id, $module, $config);\n    }\n}\n```\n\nSi se accede al controlador desde el navegador, veremos un error advirtiendo que `BookingInterface` no puede ser \ninstanciada. Esto se debe a que necesitamos indicar al contenedor de ID como tratar con esta dependencia:\n\n```php\n\\Yii::$container->set('app\\components\\BookingInterface', 'app\\components\\BookingService');\n```\n\nAhora si se accede al contenedor nuevamente, se creará una instancia de `app\\components\\BookingService` y se inyectará \na como tercer parámetro al constructor del controlador.\n\nCuando Registrar Dependencias <span id=\"when-to-register-dependencies\"></span>\n-----------------------------\n\nEl registro de dependencias debe hacerse lo antes posible debido a que las dependencias se necesitan cuando se crean \nnuevos objetos. A continuación se listan practicas recomendadas:\n\n* Siendo desarrolladores de una aplicación, podemos registrar dependencias en el \n  [script de entrada](structure-entry-scripts.md) o en un script incluido en el script de entrada.\n* Siendo desarrolladores de una [extension](structure-extensions.md) redistribuible, podemos registrar dependencias en \n  la clase de boostraping de la extensión.\n\nResumen <span id=\"summary\"></span>\n-------\n\nTanto la inyección de dependencias como el [localizador de servicios](concept-service-locator.md) son patrones de \ndiseño populares que permiten construir software con acoplamiento flexible y más fácil de testear. Se recomienda \nencarecida la lectura del articulo de [Martin](https://martinfowler.com/articles/injection.html) para obtener una mejor \ncomprensión de la inyección de dependencias y de la localización de servicios.\n\nYii implementa su propio [localizador de servicios](concept-service-locator.md) por encima del contenedor de ID. \nCuando un localizador de servicios intenta crear una nueva instancia de objeto, se desviará la llamada al contenedor \nde ID. Este último resolverá las dependencias automáticamente como se ha descrito anteriormente.\n"
  },
  {
    "path": "docs/guide-es/concept-events.md",
    "content": "Eventos\n=======\n\nLos eventos permiten inyectar código dentro de otro código existente en ciertos puntos de ejecución. Se pueden adjuntar\ncódigo personalizado a un evento, cuando se lance (triggered), el código se ejecutará automáticamente. Por ejemplo, un\nobjeto mailer puede lanzar el evento `messageSent` cuando se envía un mensaje correctamente. Si se quiere rastrear\nel correcto envío del mensaje, se puede, simplemente, añadir un código de seguimiento al evento `messageSent`.\n\nYii introduce una clase base [[yii\\base\\Component]] para soportar eventos. Si una clase necesita lanzar un evento,\neste debe extender a [[yii\\base\\Component]] o a una clase hija.\n\nGestor de Eventos <span id=\"event-handlers\"></span>\n-----------------\n\nUn gestor de eventos es una\n[llamada de retorno PHP (PHP callback)](https://www.php.net/manual/es/language.types.callable.php) que se ejecuta cuando se\nlanza el evento al que corresponde. Se puede usar cualquier llamada de retorno de las enumeradas a continuación:\n\n- una función de PHP global especificada como una cadena de texto (sin paréntesis), ej. `'trim'`;\n- un método de objeto especificado como un array de un objeto y un nombre de método como una cadena de texto\n  (sin paréntesis), ej. `[$object, 'methodNAme']`;\n- un método de clase estático especificado como un array de un nombre de clase y un método como una cadena de texto\n  (sin paréntesis), ej. `[$class, 'methodName']`;\n- una función anónima, ej. `function ($event) { ... }`.\n\nLa firma de un gestor de eventos es:\n\n```php\nfunction ($event) {\n    // $event es un objeto de yii\\base\\Event o de una clase hija\n}\n```\n\nUn gestor de eventos puede obtener la siguiente información acerca de un evento ya sucedido mediante el parámetro\n`$event`:\n\n- [[yii\\base\\Event::name|nombre del evento]]\n- [[yii\\base\\Event::sender|evento enviando]]: el objeto desde el que se ha ejecutado `trigger()`\n- [[yii\\base\\Event::data|custom data]]: los datos que se proporcionan al adjuntar el gestor de eventos\n  (se explicará más adelante)\n\n\nAñadir Gestores de Eventos <span id=\"attaching-event-handlers\"></span>\n--------------------------\n\nSe puede añadir un gestor a un evento llamando al método [[yii\\base\\Component::on()]]. Por ejemplo:\n\n```php\n$foo = new Foo;\n\n// este gestor es una función global\n$foo->on(Foo::EVENT_HELLO, 'function_name');\n\n// este gestor es un método de objeto\n$foo->on(Foo::EVENT_HELLO, [$object, 'methodName']);\n\n// este gestor es un método de clase estática\n$foo->on(Foo::EVENT_HELLO, ['app\\components\\Bar', 'methodName']);\n\n// este gestor es una función anónima\n$foo->on(Foo::EVENT_HELLO, function ($event) {\n    // event handling logic\n});\n```\n\nTambién se pueden adjuntar gestores de eventos mediante [configuraciones](concept-configurations.md). Se pueden\nencontrar más de talles en la sección [Configuraciones](concept-configurations.md#configuration-format).\n\nCuando se adjunta un gestor de eventos, se pueden proporcionar datos adicionales como tercer parámetro de\n[[yii\\base\\Component::on()]]. El gestor podrá acceder a los datos cuando se lance el evento y se ejecute el gestor.\nPor ejemplo:\n\n```php\n// El siguiente código muestra \"abc\" cuando se lanza el evento\n// ya que $event->data contiene los datos enviados en el tercer parámetro de \"on\"\n$foo->on(Foo::EVENT_HELLO, 'function_name', 'abc');\n\nfunction function_name($event) {\n    echo $event->data;\n}\n```\n\nOrdenación de Gestores de Eventos\n---------------------------------\n\nSe puede adjuntar uno o más gestores a un único evento. Cuando se lanza un evento, se ejecutarán los gestores adjuntos\nen el orden que se hayan añadido al evento. Si un gestor necesita parar la invocación de los gestores que le siguen,\nse puede establecer la propiedad [[yii\\base\\Event::handled]] del parámetro `$event` para que sea `true`:\n\n```php\n$foo->on(Foo::EVENT_HELLO, function ($event) {\n    $event->handled = true;\n});\n```\n\nDe forma predeterminada, cada nuevo gestor añadido se pone a la cola de la lista de gestores del evento. Por lo tanto,\nel gestor se ejecutará en el último lugar cuando se lance el evento. Para insertar un nuevo gestor al principio de la\ncola de gestores para que sea ejecutado primero, se debe llamar a [[yii\\base\\Component::on()]], pasando al cuarto\nparámetro `$append` el valor `false`:\n\n```php\n$foo->on(Foo::EVENT_HELLO, function ($event) {\n    // ...\n}, $data, false);\n```\n\nLanzamiento de Eventos <span id=\"triggering-events\"></span>\n----------------------\n\nLos eventos se lanzan llamando al método [[yii\\base\\Component::trigger()]]. El método requiere un *nombre de evento*,\ny de forma opcional un objeto de evento que describa los parámetros que se enviarán a los gestores de eventos. Por\nejemplo:\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Component;\nuse yii\\base\\Event;\n\nclass Foo extends Component\n{\n    const EVENT_HELLO = 'hello';\n\n    public function bar()\n    {\n        $this->trigger(self::EVENT_HELLO);\n    }\n}\n```\n\nCon el código anterior, cada llamada a `bar()` lanzará un evento llamado `hello`\n\n> Tip: Se recomienda usar las constantes de clase para representar nombres de eventos. En el anterior ejemplo, la\n  constante `EVENT_HELLO` representa el evento `hello`. Este enfoque proporciona tres beneficios. Primero, previene\n  errores tipográficos. Segundo, puede hacer que los IDEs reconozcan los eventos en las funciones de auto-completado.\n  Tercero, se puede ver que eventos soporta una clase simplemente revisando la declaración de constantes.\n\nA veces cuando se lanza un evento se puede querer pasar información adicional al gestor de eventos. Por ejemplo, un\nmailer puede querer enviar la información del mensaje para que los gestores del evento `messageSent` para que los\ngestores puedan saber las particularidades del mensaje enviado. Para hacerlo, se puede proporcionar un objeto de tipo\nevento como segundo parámetro al método [[yii\\base\\Component::trigger()]]. El objeto de tipo evento debe ser una\ninstancia de la clase [[yii\\base\\Event]] o de su clase hija. Por ejemplo:\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Component;\nuse yii\\base\\Event;\n\nclass MessageEvent extends Event\n{\n    public $message;\n}\n\nclass Mailer extends Component\n{\n    const EVENT_MESSAGE_SENT = 'messageSent';\n\n    public function send($message)\n    {\n        // ...enviando $message...\n\n        $event = new MessageEvent;\n        $event->message = $message;\n        $this->trigger(self::EVENT_MESSAGE_SENT, $event);\n    }\n}\n```\n\nCuando se lanza el método [[yii\\base\\Component::trigger()]], se ejecutarán todos los gestores adjuntos al evento.\n\nDesadjuntar Gestores de Evento <span id=\"detaching-event-handlers\"></span>\n------------------------------\n\nPara desadjuntar un gestor de un evento, se puede ejecutar el método [[yii\\base\\Component::off()]]. Por ejemplo:\n\n```php\n// el gestor es una función global\n$foo->off(Foo::EVENT_HELLO, 'function_name');\n\n// el gestor es un método de objeto\n$foo->off(Foo::EVENT_HELLO, [$object, 'methodName']);\n\n// el gestor es un método estático de clase\n$foo->off(Foo::EVENT_HELLO, ['app\\components\\Bar', 'methodName']);\n\n// el gestor es una función anónima\n$foo->off(Foo::EVENT_HELLO, $anonymousFunction);\n```\n\nTenga en cuenta que en general no se debe intentar desadjuntar las funciones anónimas a no ser que se almacene donde\nse ha adjuntado al evento. En el anterior ejemplo, se asume que la función anónima se almacena como variable\n`$anonymousFunction`.\n\nPara desadjuntar TODOS los gestores de un evento, se puede llamar [[yii\\base\\Component::off()]] sin el segundo\nparámetro:\n\n```php\n$foo->off(Foo::EVENT_HELLO);\n```\n\nNivel de Clase (Class-Level) Gestores de Eventos <span id=\"class-level-event-handlers\"></span>\n------------------------------------------------\n\nEn las subsecciones anteriores se ha descrito como adjuntar un gestor a un evento a *nivel de instancia*. A veces, se\npuede querer que un gestor responda todos los eventos de *todos* las instancias de una clase en lugar de una instancia\nespecifica. En lugar de adjuntar un gestor de eventos a una instancia, se puede adjuntar un gestor a *nivel de clase*\nllamando al método estático [[yii\\base\\Event::on()]].\n\nPor ejemplo, un objeto de tipo [Active Record](db-active-record.md) lanzará un evento\n[[yii\\db\\BaseActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]] cada vez que inserte un nuevo registro en la base\nde datos. Para poder registrar las inserciones efectuadas por *todos* los objetos\n[Active Record](db-active-record.md), se puede usar el siguiente código:\n\n```php\nuse Yii;\nuse yii\\base\\Event;\nuse yii\\db\\ActiveRecord;\n\nEvent::on(ActiveRecord::class, ActiveRecord::EVENT_AFTER_INSERT, function ($event) {\n    Yii::debug(get_class($event->sender) . ' is inserted');\n});\n```\n\nSe invocará al gestor de eventos cada vez que una instancia de [[yii\\db\\ActiveRecord|ActiveRecord]], o de uno de sus\nclases hijas, lance un evento de tipo [[yii\\db\\BaseActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]]. Se puede\nobtener el objeto que ha lanzado el evento mediante `$event->sender` en el gestor.\n\nCuando un objeto lanza un evento, primero llamará los gestores a nivel de instancia, y a continuación los gestores a\nnivel de clase.\n\nSe puede lanzar un evento de tipo *nivel de clase* llamando al método estático [[yii\\base\\Event::trigger()]]. Un\nevento de nivel de clase no se asocia a un objeto en particular. Como resultado, esto provocará solamente la\ninvocación de los gestores de eventos a nivel de clase.\n\n```php\nuse yii\\base\\Event;\n\nEvent::on(Foo::class, Foo::EVENT_HELLO, function ($event) {\n    var_dump($event->sender);  // displays \"null\"\n});\n\nEvent::trigger(Foo::class, Foo::EVENT_HELLO);\n```\n\nTenga en cuenta que en este caso, el `$event->sender` hace referencia al nombre de la clase que lanza el evento en\nlugar de a la instancia del objeto.\n\n> Note: Debido a que los gestores a nivel de clase responderán a los eventos lanzados por cualquier instancia de la\nclase, o cualquier clase hija, se debe usar con cuidado, especialmente en las clases de bajo nivel (low-level), tales\ncomo [[yii\\base\\BaseObject]].\n\nPara desadjuntar un gestor de eventos a nivel de clase, se tiene que llamar a [[yii\\base\\Event::off()]]. Por ejemplo:\n\n```php\n// desadjunta $handler\nEvent::off(Foo::class, Foo::EVENT_HELLO, $handler);\n\n// desadjunta todos los gestores de Foo::EVENT_HELLO\nEvent::off(Foo::class, Foo::EVENT_HELLO);\n```\n\nEventos Globales <span id=\"global-events\"></span>\n----------------\n\nYii soporta los llamados *eventos globales*, que en realidad es un truco basado en el gestor de eventos descrito\nanteriormente. El evento global requiere un Singleton globalmente accesible, tal como la instancia de\n[aplicación](structure-applications.md) en si misma.\n\nPara crear un evento global, un evento remitente (event sender) llama al método `trigger()` del Singleton para lanzar\nel evento, en lugar de llamar al propio método `trigger()` del remitente. De forma similar, los gestores de eventos se\nadjuntan al evento del Singleton. Por ejemplo:\n\n```php\nuse Yii;\nuse yii\\base\\Event;\nuse app\\components\\Foo;\n\nYii::$app->on('bar', function ($event) {\n    echo get_class($event->sender);  // muestra \"app\\components\\Foo\"\n});\n\nYii::$app->trigger('bar', new Event(['sender' => new Foo]));\n```\n\nUn beneficio de usar eventos globales es que no se necesita un objeto cuando se adjuntan gestores a un evento para que\nsean lanzados por el objeto. En su lugar, los gestores adjuntos y el lanzamiento de eventos se efectúan en el\nSingleton (ej. la instancia de la aplicación).\n\nSin embargo, debido a que los `namespaces` de los eventos globales son compartidos por todas partes, se les deben\nasignar nombres bien pensados, como puede ser la introducción de algún `namespace`\n(ej. \"frontend.mail.sent\", \"backend.mail.sent\").\n"
  },
  {
    "path": "docs/guide-es/concept-properties.md",
    "content": "Propiedades\n===========\n\nEn PHP, las variables miembro de clases también llamadas *propiedades*, son parte de la definición de la clase, y se\nusan para representar el estado de una instancia de la clase (ej. para diferenciar una instancia de clase de otra).\nA la práctica, a menudo, se puede querer gestionar la lectura o escritura de las propiedades de algunos momentos. Por\nejemplo, se puede querer eliminar los espacios en blanco (trim) de una cadena de texto cada vez que esta se asigne a\nuna propiedad de tipo `label`. Se *podría* usar el siguiente código para realizar esta tarea:\n\n```php\n$object->label = trim($label);\n```\n\nLa desventaja del código anterior es que se tendría que ejecutar `trim()` en todas las partes del código que pudieran\nestablecer la propiedad `label`. Si en el futuro, la propiedad `label` tiene que seguir otro funcionamiento, como por\nejemplo que la primera letra tiene que estar en mayúsculas, se tendrán que modificar todas las secciones de código que\nasignen el valor a la propiedad `label`. La repetición de código conlleva a bugs, y es una practica que se tiene que\nevitar en la medida de lo posible.\n\nPara solventar este problema, Yii introduce la clase base llamada [[yii\\base\\BaseObject]] que da soporte a la definición\nde propiedades basada en los métodos de clase *getter* y *setter*. Si una clase necesita más funcionalidad, debe\nextender a la clase [[yii\\base\\BaseObject]] o a alguna de sus hijas.\n\n> Info: Casi todas las clases del núcleo (core) en el framework Yii extienden a [[yii\\base\\BaseObject]] o a una de\n  sus clases hijas. Esto significa que siempre que se encuentre un getter o un setter en una clase del núcleo, se\n  puede utilizar como una propiedad.\n\nUn método getter es un método cuyo nombre empieza por la palabra `get`: un metodo setter empieza por `set`. El nombre\nañadido detrás del prefijo `get` o `set` define el nombre de la propiedad. Por ejemplo, un getter `getLabel()` y/o un\nsetter `setLabel()` definen la propiedad `label`, como se muestra a continuación:\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\BaseObject;\n\nclass Foo extends BaseObject\n{\n    private $_label;\n\n    public function getLabel()\n    {\n        return $this->_label;\n    }\n\n    public function setLabel($value)\n    {\n        $this->_label = trim($value);\n    }\n}\n```\n\n(Para ser claros, los métodos getter y setter crean la propiedad `label`, que en este caso hace una referencia interna\nal nombre de atributo privado `_label`.)\n\nLas propiedades definidas por los getter y los setters se pueden usar como variables de clase miembro. La principal\ndiferencia radica en que cuando esta propiedad se lea, se ejecutará su correspondiente método getter; cuando se asigne\nun valor a la propiedad, se ejecutará el correspondiente método setter. Por ejemplo:\n\n```php\n// equivalente a $label = $object->getLabel();\n$label = $object->label;\n\n// equivalente a $object->setLabel('abc');\n$object->label = 'abc';\n```\n\nUna propiedad definida por un getter sin un setter es de tipo *sólo lectura*. Si se intenta asignar un valor a esta\npropiedad se producirá una excepción de tipo [[yii\\base\\InvalidCallException|InvalidCallException]]. Del mismo modo\nque una propiedad definida con un setter pero sin getter será de tipo *sólo escritura*, cualquier intento de lectura\nde esta propiedad producirá una excepción. No es común tener variables de tipo sólo escritura.\n\nHay varias reglas especiales y limitaciones en las propiedades definidas mediante getters y setters:\n\n* Los nombres de estas propiedades son *case-insensitive*. Por ejemplo, `$object->label` y `$object->Label` son la\n  misma. Esto se debe a que los nombres de los métodos en PHP son case-insensitive.\n* Si el nombre de una propiedad de este tipo es igual al de una variable miembro de la clase, la segunda tendrá\n  prioridad. Por ejemplo, si la anterior clase `Foo` tiene la variable miembro `label`, entonces la asignación\n  `$object->label = 'abc'` afectará a la *variable miembro* 'label'; no se ejecutará el método setter `setLabel()`.\n* Estas variables no soportan la visibilidad. No hay diferencia en definir los métodos getter o setter en una\n  propiedad public, protected, o private.\n* Las propiedades sólo se pueden definir por getters y setters *no estáticos*. Los métodos estáticos no se tratarán de\n  la misma manera.\n\nVolviendo de nuevo al problema descrito al principio de la guía, en lugar de ejecutar `trim()` cada vez que se asigne\nun valor a `label`, ahora `trim()` sólo necesita ser invocado dentro del setter `setLabel()`. I si se tiene que añadir\nun nuevo requerimiento, para que `label` empiece con una letra mayúscula, se puede modificar rápidamente el método `\nsetLabel()` sin tener que modificar más secciones de código. El cambio afectará a cada asignación de `label`.\n"
  },
  {
    "path": "docs/guide-es/concept-service-locator.md",
    "content": "Localizador de Servicios\n========================\n\nUn localizador de servicios es un objeto que sabe cómo proporcionar todo tipo de servicios (o componentes) que puede necesitar una aplicación. Dentro de un localizador de servicios, existe en cada componente como una única instancia, únicamente identificado por un ID. Se utiliza el ID para recuperar un componente desde el localizador de servicios.\n\nEn Yii, un localizador de servicio es simplemente una instancia de [[yii\\di\\ServiceLocator]], o de una clase hija.\n\nEl localizador de servicio más utilizado en Yii es el objeto *aplicación*, que se puede acceder a través de `\\Yii::$app`. Los servicios que prestá son llamadas *componentes de la aplicación*, como los componentes `request`, `response`, and `urlManager`. Usted puede configurar estos componentes, o incluso cambiarlos por sus propias implementaciones fácilmente a través de la funcionalidad proporcionada por el localizador de servicios.\n\nAdemás del objeto de aplicación, cada objeto módulo es también un localizador de servicios.\n\nPara utilizar un localizador de servicios, el primer paso es registrar los componentes de la misma. Un componente se puede registrar a través de [[yii\\di\\ServiceLocator::set()]]. El código siguiente muestra diferentes maneras de registrarse componentes:\n\n```php\nuse yii\\di\\ServiceLocator;\nuse yii\\caching\\FileCache;\n\n$locator = new ServiceLocator;\n\n// register \"cache\" using a class name that can be used to create a component\n$locator->set('cache', 'yii\\caching\\ApcCache');\n\n// register \"db\" using a configuration array that can be used to create a component\n$locator->set('db', [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=localhost;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n]);\n\n// register \"search\" using an anonymous function that builds a component\n$locator->set('search', function () {\n    return new app\\components\\SolrService;\n});\n\n// register \"pageCache\" using a component\n$locator->set('pageCache', new FileCache);\n```\n\nUna vez que el componente se ha registrado, usted puede acceder a él utilizando su ID, en una de las dos formas siguientes:\n\n```php\n$cache = $locator->get('cache');\n// or alternatively\n$cache = $locator->cache;\n```\n\nComo puede observarse, [[yii\\di\\ServiceLocator]] le permite acceder a un componente como una propiedad utilizando el ID de componente. Cuando acceda a un componente, por primera vez, [[yii\\di\\ServiceLocator]] utilizará la información de registro de componente para crear una nueva instancia del componente y devolverlo. Más tarde, si se accede de nuevo al componente, el localizador de servicio devolverá la misma instancia.\n\nUsted puede utilizar [[yii\\di\\ServiceLocator::has()]] para comprobar si un ID de componente ya ha sido registrada. \nSi llama [[yii\\di\\ServiceLocator::get()]] con una identificación válida, se produce una excepción.\n\nDebido a que los localizadores de servicios a menudo se crean con [configuraciones](concept-configurations.md), se proporciona una propiedad que puede escribir el nombre [[yii\\di\\ServiceLocator::setComponents()|components]]. Esto le permite configurar y registrar varios componentes a la vez. El siguiente código muestra un arreglo de configuración que se puede utilizar para configurar una aplicación, al mismo tiempo que el registro de la \"db\", \"cache\" y \"buscar\" componentes:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=demo',\n            'username' => 'root',\n            'password' => '',\n        ],\n        'cache' => 'yii\\caching\\ApcCache',\n        'search' => function () {\n            return new app\\components\\SolrService;\n        },\n    ],\n];\n```\n"
  },
  {
    "path": "docs/guide-es/db-dao.md",
    "content": "Objetos de Acceso a Bases de Datos\n==================================\n\nConstruido sobre [PDO](https://www.php.net/manual/es/book.pdo.php), Yii DAO (Objetos de Acceso a Bases de Datos) proporciona una\nAPI orientada a objetos para el acceso a bases de datos relacionales. Es el fundamento para otros métodos de acceso a bases de datos\nmás avanzados, incluyendo el [constructor de consultas](db-query-builder.md) y [active record](db-active-record.md).\n\nAl utilizar Yii DAO, principalmente vas a tratar con SQLs planos y arrays PHP. Como resultado, esta es la manera más eficiente\nde acceder a las bases de datos. Sin embargo, como la sintaxis puede variar para las diferentes bases de datos, utilizando\nYii DAO también significa que tienes que tienes que tomar un esfuerzo adicional para crear una aplicación de database-agnostic.\n\nYii DAO soporta las siguientes bases de datos:\n\n- [MySQL](https://www.mysql.com/)\n- [MariaDB](https://mariadb.com/)\n- [SQLite](https://sqlite.org/)\n- [PostgreSQL](https://www.postgresql.org/): versión 8.4 o superior.\n- [CUBRID](https://www.cubrid.org/): versión 9.3 o superior.\n- [Oracle](https://www.oracle.com/database/)\n- [MSSQL](https://www.microsoft.com/en-us/sqlserver/default.aspx): versión 2008 o superior.\n\n## Creando Conexiones DB <span id=\"creating-db-connections\"></span>\n\nPara acceder a una base de datos, primero necesitas conectarte a tu bases de datos mediante la creación\nde una instancia de [[yii\\db\\Connection]]:\n\n```php\n$db = new yii\\db\\Connection([\n    'dsn' => 'mysql:host=localhost;dbname=example',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n]);\n```\n\nDebido a una conexión DB a menudo necesita ser accedido en diferentes lugares, una práctica común es\nconfigurarlo en términos de un [componente de aplicación](structure-application-components.md) como\nse muestra a continuación:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        // ...\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=example',\n            'username' => 'root',\n            'password' => '',\n            'charset' => 'utf8',\n        ],\n    ],\n    // ...\n];\n```\n\nPuedes acceder a la conexión DB mediante la expresión `Yii::$app->db`.\n\n> Tip: Puedes configurar múltiples componentes de aplicación DB si tu aplicación necesita acceder a múltiples bases de datos.\n\nCuando configuras una conexión DB, deberías siempre especificar el Nombre de Origen de Datos (DSN) mediante la\npropiedad [[yii\\db\\Connection::dsn|dsn]]. El formato del DSN varia para cada diferente base de datos. Por favor consulte el\n[manual de PHP](https://www.php.net/manual/es/function.PDO-construct.php) para más detalles. Abajo están algunos ejemplos:\n\n* MySQL, MariaDB: `mysql:host=localhost;dbname=mydatabase`\n* SQLite: `sqlite:/path/to/database/file`\n* PostgreSQL: `pgsql:host=localhost;port=5432;dbname=mydatabase`\n* CUBRID: `cubrid:dbname=demodb;host=localhost;port=33000`\n* MS SQL Server (mediante sqlsrv driver): `sqlsrv:Server=localhost;Database=mydatabase`\n* MS SQL Server (mediante dblib driver): `dblib:host=localhost;dbname=mydatabase`\n* MS SQL Server (mediante mssql driver): `mssql:host=localhost;dbname=mydatabase`\n* Oracle: `oci:dbname=//localhost:1521/mydatabase`\n\nNota que si estás conectándote con una base de datos mediante ODBC, deberías configurar la propiedad [[yii\\db\\Connection::driverName]]\npara que Yii pueda conocer el tipo de base de datos actual. Por ejemplo,\n\n```php\n'db' => [\n    'class' => 'yii\\db\\Connection',\n    'driverName' => 'mysql',\n    'dsn' => 'odbc:Driver={MySQL};Server=localhost;Database=test',\n    'username' => 'root',\n    'password' => '',\n],\n```\n\nAdemás de la propiedad [[yii\\db\\Connection::dsn|dsn]], a menudo es necesario configurar el [[yii\\db\\Connection::username|username]]\ny [[yii\\db\\Connection::password|password]]. Por favor consulta [[yii\\db\\Connection]] para ver la lista completa de propiedades configurables.\n\n> Info: Cuando se crea una instancia de conexión DB, la conexión actual a la base de datos no se establece hasta que\n  ejecutes el primer SQL o llames explícitamente al método [[yii\\db\\Connection::open()|open()]].\n\n\n## Ejecutando Consultas SQL <span id=\"executing-sql-queries\"></span>\n\nUna vez tienes instanciada una conexión a la base de datos, se pueden ejecutar consultas SQL tomando\nlos siguientes pasos:\n\n1. Crea un [[yii\\db\\Command]] con SQL plano;\n2. Vincula parámetros (opcional);\n3. Llama a uno de los métodos de ejecución SQL con [[yii\\db\\Command]].\n\nEl siguiente ejemplo muestra varias maneras de obtener datos de una base de datos:\n\n```php\n$db = new yii\\db\\Connection(...);\n\n// retorna un conjunto de filas. Cada fila es un array asociativo de columnas de nombres y valores.\n// un array vacío es retornado si no hay resultados\n$posts = $db->createCommand('SELECT * FROM post')\n            ->queryAll();\n\n// retorna una sola fila (la primera fila)\n// `false` es retornado si no hay resultados\n$post = $db->createCommand('SELECT * FROM post WHERE id=1')\n           ->queryOne();\n\n// retorna una sola columna (la primera columna)\n// un array vacío es retornado si no hay resultados\n$titles = $db->createCommand('SELECT title FROM post')\n             ->queryColumn();\n\n// retorna un escalar\n// `false` es retornado si no hay resultados\n$count = $db->createCommand('SELECT COUNT(*) FROM post')\n             ->queryScalar();\n```\n\n> Note: Para preservar la precisión, los datos obtenidos de las bases de datos son todos representados como cadenas, incluso si el tipo de columna correspondiente\na la base de datos es numérico.\n\n> Tip: Si necesitas ejecutar una consulta SQL inmediatamente después de establecer una conexión (ej., para establecer una zona horaria o un conjunto de caracteres),\n> puedes hacerlo con el evento [[yii\\db\\Connection::EVENT_AFTER_OPEN]]. Por ejemplo,\n>\n```php\nreturn [\n    // ...\n    'components' => [\n        // ...\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            // ...\n            'on afterOpen' => function($event) {\n                // $event->sender se refiere a la conexión DB\n                $event->sender->createCommand(\"SET time_zone = 'UTC'\")->execute();\n            }\n        ],\n    ],\n    // ...\n];\n```\n\n\n### Parámetros Vinculados (Binding Parameters) <span id=\"binding-parameters\"></span>\n\nCuando creamos un comando DB para un SQL con parámetros, nosotros deberíamos casi siempre aprovechar el uso de los parámetros vinculados\npara prevenir los ataques de inyección de SQL. Por ejemplo,\n\n```php\n$post = $db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status')\n           ->bindValue(':id', $_GET['id'])\n           ->bindValue(':status', 1)\n           ->queryOne();\n```\n\nEn la sentencia SQL, puedes incrustar uno o múltiples parámetros placeholders (ej. `:id` en el ejemplo anterior). Un parámetro\nplaceholder debería ser una cadena que empiece con dos puntos. A continuación puedes llamar a uno de los siguientes métodos para\nunir los valores de los parámetros vinculados:\n\n* [[yii\\db\\Command::bindValue()|bindValue()]]: une un solo parámetro\n* [[yii\\db\\Command::bindValues()|bindValues()]]: une múltiples parámetros en una sola llamada\n* [[yii\\db\\Command::bindParam()|bindParam()]]: similar a [[yii\\db\\Command::bindValue()|bindValue()]] pero también\n  soporta las referencias de parámetros vinculados.\n\nEl siguiente ejemplo muestra formas alternativas de vincular parámetros:\n\n```php\n$params = [':id' => $_GET['id'], ':status' => 1];\n\n$post = $db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status')\n           ->bindValues($params)\n           ->queryOne();\n\n$post = $db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status', $params)\n           ->queryOne();\n```\n\nLa vinculación parámetros es implementada mediante [sentencias preparadas (prepared statements)](https://www.php.net/manual/es/mysqli.quickstart.prepared-statements.php).\nAdemás de prevenir ataques de inyección de SQL, también puede mejorar el rendimiento preparando una sola vez una sentencia SQL y ejecutándola múltiples veces con diferentes\nparámetros. Por ejemplo,\n\n```php\n$command = $db->createCommand('SELECT * FROM post WHERE id=:id');\n\n$post1 = $command->bindValue(':id', 1)->queryOne();\n$post2 = $command->bindValue(':id', 2)->queryOne();\n```\n\nPorque [[yii\\db\\Command::bindParam()|bindParam()]] soporta parámetros vinculados por referencias, el código de arriba también\npuede ser escrito como lo siguiente:\n\n```php\n$command = $db->createCommand('SELECT * FROM post WHERE id=:id')\n              ->bindParam(':id', $id);\n\n$id = 1;\n$post1 = $command->queryOne();\n\n$id = 2;\n$post2 = $command->queryOne();\n```\n\nObserve que vincula el placeholder a la variable `$id` antes de la ejecución, y entonces cambia el valor de esa variable\nantes de cada subsiguiente ejecución (esto se hace a menudo con bucles). Ejecutando consultas de esta manera puede ser\nbastante más eficiente que ejecutar una nueva consulta para cada valor diferente del parámetro.\n\n\n### Ejecutando Consultas Non-SELECT <span id=\"non-select-queries\"></span>\n\nEl método `queryXyz()` introducidos en las secciones previas todos tratan con consultas SELECT los cuales recogen los\ndatos de la base de datos. Para las consultas que no devuelven datos, deberías llamar a el método [[yii\\db\\Command::execute()]]\nen su lugar. Por ejemplo,\n\n```php\n$db->createCommand('UPDATE post SET status=1 WHERE id=1')\n   ->execute();\n```\n\nEl método [[yii\\db\\Command::execute()]] retorna el número de filas afectadas por la ejecución SQL.\n\nPara consultas INSERT, UPDATE y DELETE, en vez de escribir SQLs planos, puedes llamar a [[yii\\db\\Command::insert()|insert()]],\n[[yii\\db\\Command::update()|update()]], [[yii\\db\\Command::delete()|delete()]], respectivamente, construyen los correspondientes\nSQLs. Estos métodos entrecomillan adecuadamente las tablas y los nombres de columnas y los valores de los parámetros vinculados.\nPor ejemplo,\n\n```php\n// INSERT (table name, column values)\n$db->createCommand()->insert('user', [\n    'name' => 'Sam',\n    'age' => 30,\n])->execute();\n\n// UPDATE (table name, column values, condition)\n$db->createCommand()->update('user', ['status' => 1], 'age > 30')->execute();\n\n// DELETE (table name, condition)\n$db->createCommand()->delete('user', 'status = 0')->execute();\n```\n\nPuedes también llamar a [[yii\\db\\Command::batchInsert()|batchInsert()]] para insertar múltiples filas de una sola vez,\nque es mucho más eficiente que insertar una fila de cada vez:\n\n```php\n// table name, column names, column values\n$db->createCommand()->batchInsert('user', ['name', 'age'], [\n    ['Tom', 30],\n    ['Jane', 20],\n    ['Linda', 25],\n])->execute();\n```\n\n\n## Entrecomillado de Tablas y Nombres de Columna <span id=\"quoting-table-and-column-names\"></span>\n\nAl escribir código de database-agnostic, entrecomillar correctamente los nombres de las tablas y las columnas es a menudo\nun dolor de cabeza porque las diferentes bases de datos tienen diferentes reglas para entrecomillar los nombres. Para\nsolventar este problema, puedes usar la siguiente sintaxis de entrecomillado introducido por Yii:\n\n* `[[column name]]`: encierra con dobles corchetes el nombre de una columna que debe ser entrecomillado;\n* `{{table name}}`: encierra con dobles llaves el nombre de una tabla que debe ser entrecomillado.\n\nYii DAO automáticamente convertirá tales construcciones en un SQL con los correspondientes entrecomillados de los nombres de las columnas o tablas.\nPor ejemplo,\n\n```php\n// ejecuta esta SQL para MySQL: SELECT COUNT(`id`) FROM `employee`\n$count = $db->createCommand(\"SELECT COUNT([[id]]) FROM {{employee}}\")\n            ->queryScalar();\n```\n\n\n### Usando Prefijos de Tabla <span id=\"using-table-prefix\"></span>\n\nSi la mayoría de tus tablas de BD utilizan algún prefijo común en sus tablas, puedes usar la función de prefijo de tabla soportado\npor Yii DAO.\n\nPrimero, especifica el prefijo de tabla mediante la propiedad [[yii\\db\\Connection::tablePrefix]]:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        // ...\n        'db' => [\n            // ...\n            'tablePrefix' => 'tbl_',\n        ],\n    ],\n];\n```\n\nLuego en tu código, siempre que lo necesites para hacer referencia a una tabla cuyo nombre tiene un prefijo, utiliza la sintaxis\n`{{%table name}}`. El carácter porcentaje se sustituye con el prefijo de la tabla que has especificado en la configuración de\nla conexión DB. Por ejemplo,\n\n```php\n// ejecuta esta SQL para MySQL: SELECT COUNT(`id`) FROM `tbl_employee`\n$count = $db->createCommand(\"SELECT COUNT([[id]]) FROM {{%employee}}\")\n            ->queryScalar();\n```\n\n\n## Realización de Transacciones <span id=\"performing-transactions\"></span>\n\nCuando se ejecutan múltiples consultas relacionadas en una secuencia, puede que se tengan que envolver en una\ntransacción para asegurar la integridad de los datos y la consistencia de tu base de datos. Si cualquiera de las consultas\nfalla, la base de datos debe ser revertida al estado anterior como si ninguna de estas consultas se haya ejecutado.\n\nEl siguiente código muestra una manera típica de usar transacciones:\n\n```php\n$db->transaction(function($db) {\n    $db->createCommand($sql1)->execute();\n    $db->createCommand($sql2)->execute();\n    // ... ejecutando otras sentencias SQL\n});\n```\n\nEl código de arriba es equivalente a lo siguiente:\n\n```php\n$transaction = $db->beginTransaction();\n\ntry {\n    $db->createCommand($sql1)->execute();\n    $db->createCommand($sql2)->execute();\n    // ... ejecutando otras sentencias SQL\n\n    $transaction->commit();\n\n} catch(\\Exception $e) {\n\n    $transaction->rollBack();\n\n    throw $e;\n}\n```\n\nAl llamar al método [[yii\\db\\Connection::beginTransaction()|beginTransaction()]], se inicia una nueva transacción.\nLa transacción se representa como un objeto [[yii\\db\\Transaction]] almacenado en la variable `$transaction`. Luego,\nlas consultas que se ejecutan están encerrados en un bloque `try...catch...`. Si todas las consultas son ejecutadas satisfactoriamente,\nel método [[yii\\db\\Transaction::commit()|commit()]] es llamado para confirmar la transacción. De lo contrario, una excepción\nse disparará y se capturará, y el método [[yii\\db\\Transaction::rollBack()|rollBack()]] es llamado para revertir\nlos cambios hechos por las consultas antes de que fallara la consulta en la transacción.\n\n\n### Especificando los Niveles de Aislamiento <span id=\"specifying-isolation-levels\"></span>\n\nYii también soporta la configuración de [niveles de aislamiento] para tus transacciones. Por defecto, cuando comienza una  nueva transacción,\nutilizará el nivel de aislamiento definido por tu sistema de base de datos. Se puede sobrescribir el nivel de aislamiento por defecto de la\nsiguiente manera,\n\n```php\n$isolationLevel = \\yii\\db\\Transaction::REPEATABLE_READ;\n\n$db->transaction(function ($db) {\n    ....\n}, $isolationLevel);\n\n// or alternatively\n\n$transaction = $db->beginTransaction($isolationLevel);\n```\n\nYii proporciona cuatro constantes para los niveles de aislamiento más comunes:\n\n- [[\\yii\\db\\Transaction::READ_UNCOMMITTED]] - el nivel más bajo, pueden ocurrir lecturas Dirty, lecturas\n  Non-repeatable y Phantoms.\n- [[\\yii\\db\\Transaction::READ_COMMITTED]] - evita lecturas Dirty.\n- [[\\yii\\db\\Transaction::REPEATABLE_READ]] - evita lecturas Dirty y lecturas Non-repeatable.\n- [[\\yii\\db\\Transaction::SERIALIZABLE]] - el nivel más fuerte, evita todos los problemas nombrados anteriormente.\n\nAdemás de usar las constantes de arriba para especificar los niveles de aislamiento, puedes también usar cadenas con\nuna sintaxis valida soportada por el DBMS que estés usando. Por ejemplo, en PostgreSQL, puedes utilizar `SERIALIZABLE READ ONLY DEFERRABLE`.\n\nTenga en cuenta que algunos DBMS permiten configuraciones de niveles de aislamiento solo a nivel de conexión. Las transacciones subsiguientes\nrecibirá el mismo nivel de aislamiento , incluso si no se especifica ninguna. Al utilizar esta característica\nes posible que necesites ajustar el nivel de aislamiento para todas las transacciones de forma explícitamente para evitar conflictos\nen las configuraciones.\nEn el momento de escribir esto, solo MSSQL y SQLite serán afectadas.\n\n> Note: SQLite solo soporta dos niveles de aislamiento, por lo que solo se puede usar `READ UNCOMMITTED` y\n`SERIALIZABLE`. El uso de otros niveles causará el lanzamiento de una excepción.\n\n> Note: PostgreSQL no permite configurar el nivel de aislamiento antes que la transacción empiece por lo que no se\n  puede especificar el nivel de aislamiento directamente cuando empieza la transacción. Se tiene que llamar a\n  [[yii\\db\\Transaction::setIsolationLevel()]] después de que la transacción haya empezado.\n\n[isolation levels]: https://es.wikipedia.org/wiki/Aislamiento_(ACID)#Niveles_de_aislamiento\n\n\n### Transacciones Anidadas <span id=\"nesting-transactions\"></span>\n\nSi tu DBMS soporta Savepoint, puedes anidar múltiples transacciones como a continuación:\n\n```php\n$db->transaction(function ($db) {\n    // outer transaction\n\n    $db->transaction(function ($db) {\n        // inner transaction\n    });\n});\n```\n\nO alternativamente,\n\n```php\n$outerTransaction = $db->beginTransaction();\ntry {\n    $db->createCommand($sql1)->execute();\n\n    $innerTransaction = $db->beginTransaction();\n    try {\n        $db->createCommand($sql2)->execute();\n        $innerTransaction->commit();\n    } catch (Exception $e) {\n        $innerTransaction->rollBack();\n    }\n\n    $outerTransaction->commit();\n} catch (Exception $e) {\n    $outerTransaction->rollBack();\n}\n```\n\n\n## Replicación y División Lectura-Escritura <span id=\"read-write-splitting\"></span>\n\nMuchos DBMS soportan [replicación de bases de datos](https://es.wikipedia.org/wiki/Replicaci%C3%B3n_(inform%C3%A1tica)) para tener\nuna mejor disponibilidad de la base de datos y un mejor tiempo de respuesta del servidor. Con la replicación de bases\nde datos, los datos están replicados en los llamados *servidores maestros* (master servers) y *servidores esclavos*\n(slave servers). Todas las escrituras y actualizaciones deben hacerse en el servidor maestro, mientras que las lecturas\nse efectuarán en los servidores esclavos.\n\nPara aprovechar las ventajas de la replicación de la base de datos y lograr una división de lecuta-escritura, se puede configurar\nel componente [[yii\\db\\Connection]] como se muestra a continuación:\n\n```php\n[\n    'class' => 'yii\\db\\Connection',\n\n    // configuración para el maestro\n    'dsn' => 'dsn for master server',\n    'username' => 'master',\n    'password' => '',\n\n    // configuración para los esclavos\n    'slaveConfig' => [\n        'username' => 'slave',\n        'password' => '',\n        'attributes' => [\n            // utiliza un tiempo de espera de conexión más pequeña\n            PDO::ATTR_TIMEOUT => 10,\n        ],\n    ],\n\n    // listado de configuraciones de esclavos\n    'slaves' => [\n        ['dsn' => 'dsn for slave server 1'],\n        ['dsn' => 'dsn for slave server 2'],\n        ['dsn' => 'dsn for slave server 3'],\n        ['dsn' => 'dsn for slave server 4'],\n    ],\n]\n```\n\nLa configuración anterior especifica una configuración con un único maestro y múltiples esclavos. Uno de los esclavos\nse conectará y se usará para ejecutar consultas de lectura, mientras que el maestro se usará para realizar consultas de\nescritura. De este modo la división de lectura-escritura se logra automáticamente con esta configuración, Por ejemplo,\n\n```php\n// crea una instancia de Connection usando la configuración anterior\n$db = Yii::createObject($config);\n\n// consulta contra uno de los esclavos\n$rows = $db->createCommand('SELECT * FROM user LIMIT 10')->queryAll();\n\n// consulta contra el maestro\n$db->createCommand(\"UPDATE user SET username='demo' WHERE id=1\")->execute();\n```\n\n> Info: Las consultas realizadas llamando a [[yii\\db\\Command::execute()]] se consideran consultas de escritura,\n  mientras que todas las demás se ejecutan mediante alguno de los métodos \"query\" de [[yii\\db\\Command]] son consultas\n  de lectura. Se puede obtener la conexión de esclavo activa mediante `$db->slave`.\n\nEl componente `Connection` soporta el balanceo de carga y la conmutación de errores entre esclavos. Cuando se realiza\nuna consulta de lectura por primera vez, el componente `Connection` elegirá un esclavo aleatorio e intentará realizar\nuna conexión a este. Si el esclavo se encuentra \"muerto\", se intentará con otro. Si no está disponible ningún esclavo, se conectará al maestro. Configurando una [[yii\\db\\Connection::serverStatusCache|server status cache]], se recordarán los servidores\n\"muertos\" por lo que no se intentará volver a conectar a ellos durante\n[[yii\\db\\Connection::serverRetryInterval|certain period of time]].\n\n> Info: En la configuración anterior, se especifica un tiempo de espera (timeout) de conexión de 10 segundos\n  para cada esclavo. Esto significa que si no se puede conectar a un esclavo en 10 segundos, este será considerado\n  como \"muerto\". Se puede ajustar el parámetro basado en el entorno actual.\n\nTambién se pueden configurar múltiples maestros con múltiples esclavos. Por ejemplo,\n\n```php\n[\n    'class' => 'yii\\db\\Connection',\n\n    // configuracion habitual para los maestros\n    'masterConfig' => [\n        'username' => 'master',\n        'password' => '',\n        'attributes' => [\n            // utilizar un tiempo de espera de conexión más pequeña\n            PDO::ATTR_TIMEOUT => 10,\n        ],\n    ],\n\n    // listado de configuraciones de maestros\n    'masters' => [\n        ['dsn' => 'dsn for master server 1'],\n        ['dsn' => 'dsn for master server 2'],\n    ],\n\n    // configuración habitual para esclavos\n    'slaveConfig' => [\n        'username' => 'slave',\n        'password' => '',\n        'attributes' => [\n            // utilizar un tiempo de espera de conexión más pequeña\n            PDO::ATTR_TIMEOUT => 10,\n        ],\n    ],\n\n    // listado de configuración de esclavos\n    'slaves' => [\n        ['dsn' => 'dsn for slave server 1'],\n        ['dsn' => 'dsn for slave server 2'],\n        ['dsn' => 'dsn for slave server 3'],\n        ['dsn' => 'dsn for slave server 4'],\n    ],\n]\n```\n\nLa configuración anterior especifica dos maestros y cuatro esclavos. El componente `Connection` también da soporte al\nbalanceo de carga y la conmutación de errores entre maestros igual que hace con los esclavos. La diferencia es que\ncuando no se encuentra ningún maestro disponible se lanza una excepción.\n\n> Note: cuando se usa la propiedad [[yii\\db\\Connection::masters|masters]] para configurar uno o múltiples maestros, se\n  ignorarán todas las otras propiedades que especifiquen una conexión de base de datos\n  (ej. `dsn`, `username`, `password`), junto con el mismo objeto `Connection`.\n\nPor defecto. las transacciones usan la conexión del maestro. Y dentro de una transacción, todas las operaciones de DB usarán\nla conexión del maestro. Por ejemplo,\n\n```php\n// la transacción empieza con la conexión al maestro\n$transaction = $db->beginTransaction();\n\ntry {\n    // las dos consultas se ejecutan contra el maestro\n    $rows = $db->createCommand('SELECT * FROM user LIMIT 10')->queryAll();\n    $db->createCommand(\"UPDATE user SET username='demo' WHERE id=1\")->execute();\n\n    $transaction->commit();\n} catch(\\Exception $e) {\n    $transaction->rollBack();\n    throw $e;\n}\n```\n\nSi se quiere empezar la transacción con una conexión a un esclavo, se debe hacer explícitamente como se muestra a\ncontinuación:\n\n```php\n$transaction = $db->slave->beginTransaction();\n```\n\nA veces, se puede querer forzar el uso de una conexión maestra para realizar una consulta de lectura. Se puede lograr\nusando el método `useMaster()`:\n\n```php\n$rows = $db->useMaster(function ($db) {\n    return $db->createCommand('SELECT * FROM user LIMIT 10')->queryAll();\n});\n```\n\nTambién se puede utilizar directamente estableciendo `$db->enableSlaves` a `false` para que se redirijan todas las\nconsultas a la conexión del maestro.\n\n## Trabajando con Esquemas de Bases de Datos <span id=\"database-schema\"></span>\n\nYii DAO proporciona todo un conjunto de métodos que permites manipular el esquema de tu base de datos, tal como\ncrear nuevas tablas, borrar una columna de una tabla, etc. Estos métodos son listados a continuación:\n\n* [[yii\\db\\Command::createTable()|createTable()]]: crea una tabla\n* [[yii\\db\\Command::renameTable()|renameTable()]]: renombra una tabla\n* [[yii\\db\\Command::dropTable()|dropTable()]]: remueve una tabla\n* [[yii\\db\\Command::truncateTable()|truncateTable()]]: remueve todas las filas de una tabla\n* [[yii\\db\\Command::addColumn()|addColumn()]]: añade una columna\n* [[yii\\db\\Command::renameColumn()|renameColumn()]]: renombra una columna\n* [[yii\\db\\Command::dropColumn()|dropColumn()]]: remueve una columna\n* [[yii\\db\\Command::alterColumn()|alterColumn()]]: altera una columna\n* [[yii\\db\\Command::addPrimaryKey()|addPrimaryKey()]]: añade una clave primaria\n* [[yii\\db\\Command::dropPrimaryKey()|dropPrimaryKey()]]: remueve una clave primaria\n* [[yii\\db\\Command::addForeignKey()|addForeignKey()]]: añade una clave ajena\n* [[yii\\db\\Command::dropForeignKey()|dropForeignKey()]]: remueve una clave ajena\n* [[yii\\db\\Command::createIndex()|createIndex()]]: crea un indice\n* [[yii\\db\\Command::dropIndex()|dropIndex()]]: remueve un indice\n\nEstos métodos puedes ser usados como se muestra a continuación:\n\n```php\n// CREATE TABLE\n$db->createCommand()->createTable('post', [\n    'id' => 'pk',\n    'title' => 'string',\n    'text' => 'text',\n]);\n```\n\nTambién puedes recuperar la información de definición de una tabla a través\ndel método [[yii\\db\\Connection::getTableSchema()|getTableSchema()]] de una conexión DB. Por ejemplo,\n\n```php\n$table = $db->getTableSchema('post');\n```\n\nEl método retorna un objeto [[yii\\db\\TableSchema]] que contiene la información sobre las columnas de las tablas,\nclaves primarias, claves ajenas, etc. Toda esta información principalmente es utilizada por el\n[constructor de consultas](db-query-builder.md) y [active record](db-active-record.md) para ayudar a\nescribir código database-agnostic.\n"
  },
  {
    "path": "docs/guide-es/db-migrations.md",
    "content": "Migración de Base de Datos\n==========================\n\nDurante el curso de desarrollo y mantenimiento de una aplicación con base de datos, la estructura de dicha base de datos\nevoluciona tanto como el código fuente. Por ejemplo, durante el desarrollo de una aplicación,\nuna nueva tabla podría ser necesaria; una vez que la aplicación se encuentra en producción, podría descrubrirse\nque debería crearse un índice para mejorar el tiempo de ejecución de una consulta; y así sucesivamente. Debido a los cambios en la estructura de la base de datos\na menudo se requieren cambios en el código, Yii soporta la característica llamada *migración de base de datos*, la cual permite\ntener un seguimiento de esos cambios en término de *migración de base de datos*, cuyo versionado es controlado\njunto al del código fuente.\n\nLos siguientes pasos muestran cómo una migración puede ser utilizada por un equipo durante el desarrollo:\n\n1. Tim crea una nueva migración (por ej. crea una nueva table, cambia la definición de una columna, etc.).\n2. Tim hace un commit con la nueva migración al sistema de control de versiones (por ej. Git, Mercurial).\n3. Doug actualiza su repositorio desde el sistema de control de versiones y recibe la nueva migración.\n4. Doug aplica dicha migración a su base de datos local de desarrollo, de ese modo sincronizando su base de datos\n   y reflejando los cambios que hizo Tim.\n\nLos siguientes pasos muestran cómo hacer una puesta en producción con una migración de base de datos:\n\n1. Scott crea un tag de lanzamiento en el repositorio del proyecto que contiene algunas migraciones de base de datos.\n2. Scott actualiza el código fuente en el servidor de producción con el tag de lanzamiento.\n3. Scott aplica cualquier migración de base de datos acumulada a la base de datos de producción.\n\nYii provee un grupo de herramientas de línea de comandos que te permite:\n\n* crear nuevas migraciones;\n* aplicar migraciones;\n* revertir migraciones;\n* re-aplicar migraciones;\n* mostrar el historial y estado de migraciones.\n\nTodas esas herramientas son accesibles a través del comando `yii migrate`. En esta sección describiremos en detalle\ncómo lograr varias tareas utilizando dichas herramientas. Puedes a su vez ver el uso de cada herramienta a través del comando\nde ayuda `yii help migrate`.\n\n> Tip: las migraciones pueden no sólo afectar un esquema de base de datos sino también ajustar datos existentes para que encajen en el nuevo esquema, crear herencia RBAC\n  o también limpiar el cache.\n\n\n## Creando Migraciones <span id=\"creating-migrations\"></span>\n\nPara crear una nueva migración, ejecuta el siguiente comando:\n\n```\nyii migrate/create <name>\n```\n\nEl argumento requerido `name` da una pequeña descripción de la nueva migración. Por ejemplo, si\nla migración se trata acerca de crear una nueva tabla llamada *news*, podrías utilizar el nombre `create_news_table`\ny ejecutar el siguiente comando:\n\n```\nyii migrate/create create_news_table\n```\n\n> Note: Debido a que el argumento `name` será utilizado como parte del nombre de clase de la migración generada,\n  sólo debería contener letras, dígitos, y/o guines bajos.\n\nEl comando anterior un nuevo archivo de clase PHP llamado `m150101_185401_create_news_table.php`\nen el directorio `@app/migrations`. El archivo contendrá el siguiente código, que principalmente declara\nuna clase de tipo migración `m150101_185401_create_news_table` con el siguiente esqueleto de código:\n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function up()\n    {\n\n    }\n\n    public function down()\n    {\n        echo \"m101129_185401_create_news_table cannot be reverted.\\n\";\n\n        return false;\n    }\n\n    /*\n    // Use safeUp/safeDown to run migration code within a transaction\n    public function safeUp()\n    {\n    }\n\n    public function safeDown()\n    {\n    }\n    */\n}\n```\n\nCada migración de base de datos es definida como una clase PHP que extiende de [[yii\\db\\Migration]]. La nombre de clase\nde la migración es generado automáticamente en el formato `m<YYMMDD_HHMMSS>_<Name>`, donde\n\n* `<YYMMDD_HHMMSS>` se refiere a la marca de tiempo UTC en la cual el comando de migración fue ejecutado.\n* `<Name>` es el mismo valor del argumento `name` provisto al ejecutar el comando.\n\nEn la clase de la migración, se espera que tu escribas código en el método `up()`, que realiza los cambios en la base de datos.\nPodrías también querer introducir código en el método `down()`, que debería revertir los cambios realizados por `up()`. El método `up()` es llamado\ncuando actualizas la base de datos con esta migración, mientras que el método `down()` es llamado cuando reviertes dicha migración.\nEl siguiente código muestra cómo podrías implementar la clase de migración para crear la tabla `news`:\n\n```php\n<?php\n\nuse yii\\db\\Schema;\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function up()\n    {\n        $this->createTable('news', [\n            'id' => Schema::TYPE_PK,\n            'title' => Schema::TYPE_STRING . ' NOT NULL',\n            'content' => Schema::TYPE_TEXT,\n        ]);\n    }\n\n    public function down()\n    {\n        $this->dropTable('news');\n    }\n}\n```\n\n> Info: No todas las migraciones son reversibles. Por ejemplo, si el método `up()` elimina un registro en una tabla, podrías \n  no ser capáz de recuperarla en el método `down()`. A veces, podrías ser simplemente demasiado perezoso para implementar\n  el método `down()`, debido a que no es muy común revertir migraciones de base de datos. En este caso, deberías devolver\n  `false` en el método `down()` para indicar que dicha migración no es reversible.\n\nLa clase de migración de base de datos [[yii\\db\\Migration]] expone una conexión a la base de datos mediante la propiedad [[yii\\db\\Migration::db|db]].\nPuedes utilizar esto para manipular el esquema de la base de datos utilizando métodos como se describen en\n[Trabajando con Esquemas de Base de Datos](db-dao.md#database-schema).\n\nEn vez de utilizar tipos físicos, al crear tablas o columnas deberías utilizar los *tipos abstractos*\nasí las migraciones son independientes de algún DBMS específico. La clase [[yii\\db\\Schema]] define\nun grupo de constantes que representan los tipos abstractos soportados. Dichas constantes son llamadas utilizando el formato\nde `TYPE_<Name>`. Por ejemplo, `TYPE_PK` se refiere al tipo clave primaria auto-incremental; `TYPE_STRING`\nse refiere al tipo string. Cuando se aplica una migración a una base de datos en particular, los tipos abstractos\nserán traducidos a los tipos físicos correspondientes. En el caso de MySQL, `TYPE_PK` será transformado\nen `int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY`, mientras `TYPE_STRING` se vuelve `varchar(255)`.\n\nPuedes agregar restricciones adicionales al utilizar tipos abstractos. En el ejemplo anterior, ` NOT NULL` es agregado\na `Schema::TYPE_STRING` para especificar que la columna no puede ser `null`.\n\n> Info: El mapeo entre tipos abstractos y tipos físicos es especificado en\n  la propiedad [[yii\\db\\QueryBuilder::$typeMap|$typeMap]] en cada clase concreta `QueryBuilder`.\n\nDesde la versión 2.0.6, puedes hacer uso del recientemente introducido generador de esquemas, el cual provee una forma más conveniente de definir las columnas.\nDe esta manera, la migración anterior podría ser escrita así:\n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function up()\n    {\n        $this->createTable('news', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string()->notNull(),\n            'content' => $this->text(),\n        ]);\n    }\n\n    public function down()\n    {\n        $this->dropTable('news');\n    }\n}\n```\n\nExiste una lista de todos los métodos disponibles para la definición de tipos de columna en la API de la documentación de [[yii\\db\\SchemaBuilderTrait]].\n\n\n## Generar Migraciones <span id=\"generating-migrations\"></span>\n\nDesde la versión 2.0.7 la consola provee una manera muy conveniente de generar migraciones.\n\nSi el nombre de la migración tiene una forma especial, por ejemplo `create_xxx_table` o `drop_xxx_table` entonces el archivo de la migración generada\ncontendrá código extra, en este caso para crear/eliminar tablas.\nA continuación se describen todas estas variantes.\n\n### Crear Tabla\n\n```php\nyii migrate/create create_post_table\n```\n\nesto genera\n\n```php\n/**\n * Handles the creation for table `post`.\n */\nclass m150811_220037_create_post_table extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey()\n        ]);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        $this->dropTable('post');\n    }\n}\n```\n\nPara crear las columnas en ese momento, las puedes especificar vía la opción `--fields`.\n\n```php\nyii migrate/create create_post_table --fields=\"title:string,body:text\"\n```\n\ngenera\n\n```php\n/**\n * Handles the creation for table `post`.\n */\nclass m150811_220037_create_post_table extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string(),\n            'body' => $this->text(),\n        ]);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        $this->dropTable('post');\n    }\n}\n\n```\n\nPuedes especificar más parámetros para las columnas.\n\n```php\nyii migrate/create create_post_table --fields=\"title:string(12):notNull:unique,body:text\"\n```\n\ngenera\n\n```php\n/**\n * Handles the creation for table `post`.\n */\nclass m150811_220037_create_post_table extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string(12)->notNull()->unique(),\n            'body' => $this->text()\n        ]);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        $this->dropTable('post');\n    }\n}\n```\n\n> Note: la clave primaria es automáticamente agragada y llamada `id` por defecto. Si quieres utilizar otro nombre puedes\n> especificarlo así `--fields=\"name:primaryKey\"`.\n\n#### Claves Foráneas\n\nDesde 2.0.8 el generador soporta claves foráneas utilizando la palabra clave `foreignKey`.\n\n```php\nyii migrate/create create_post_table --fields=\"author_id:integer:notNull:foreignKey(user),category_id:integer:defaultValue(1):foreignKey,title:string,body:text\"\n```\n\ngenera\n\n```php\n/**\n * Handles the creation for table `post`.\n * Has foreign keys to the tables:\n *\n * - `user`\n * - `category`\n */\nclass m160328_040430_create_post_table extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey(),\n            'author_id' => $this->integer()->notNull(),\n            'category_id' => $this->integer()->defaultValue(1),\n            'title' => $this->string(),\n            'body' => $this->text(),\n        ]);\n\n        // creates index for column `author_id`\n        $this->createIndex(\n            'idx-post-author_id',\n            'post',\n            'author_id'\n        );\n\n        // add foreign key for table `user`\n        $this->addForeignKey(\n            'fk-post-author_id',\n            'post',\n            'author_id',\n            'user',\n            'id',\n            'CASCADE'\n        );\n\n        // creates index for column `category_id`\n        $this->createIndex(\n            'idx-post-category_id',\n            'post',\n            'category_id'\n        );\n\n        // add foreign key for table `category`\n        $this->addForeignKey(\n            'fk-post-category_id',\n            'post',\n            'category_id',\n            'category',\n            'id',\n            'CASCADE'\n        );\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        // drops foreign key for table `user`\n        $this->dropForeignKey(\n            'fk-post-author_id',\n            'post'\n        );\n\n        // drops index for column `author_id`\n        $this->dropIndex(\n            'idx-post-author_id',\n            'post'\n        );\n\n        // drops foreign key for table `category`\n        $this->dropForeignKey(\n            'fk-post-category_id',\n            'post'\n        );\n\n        // drops index for column `category_id`\n        $this->dropIndex(\n            'idx-post-category_id',\n            'post'\n        );\n\n        $this->dropTable('post');\n    }\n}\n```\n\nLa posición de la palabra clave `foreignKey` en la descripción de la columna\nno cambia el código generado. Esto significa:\n\n- `author_id:integer:notNull:foreignKey(user)`\n- `author_id:integer:foreignKey(user):notNull`\n- `author_id:foreignKey(user):integer:notNull`\n\nTodas generan el mismo código.\n\nLa palabra clave `foreignKey` puede tomar un parámetro entre paréntesis el cual\nserá el nombre de la tabla relacionada por la clave foránea generada. Si no se pasa ningún parámetro\nel nombre de la tabla será deducido en base al nombre de la columna.\n\nEn el ejemplo anterior `author_id:integer:notNull:foreignKey(user)` generará\nuna columna llamada `author_id` con una clave foránea a la tabla `user` mientras\n`category_id:integer:defaultValue(1):foreignKey` generará\n`category_id` con una clave foránea a la tabla `category`.\n\n### Eliminar Tabla\n\n```php\nyii migrate/create drop_post_table --fields=\"title:string(12):notNull:unique,body:text\"\n```\n\ngenera\n\n```php\nclass m150811_220037_drop_post_table extends Migration\n{\n    public function up()\n    {\n        $this->dropTable('post');\n    }\n\n    public function down()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string(12)->notNull()->unique(),\n            'body' => $this->text()\n        ]);\n    }\n}\n```\n\n### Agregar Columna\n\nSi el nombre de la migración está en la forma `add_xxx_column_to_yyy_table` entonces el archivo generado contendrá\nlas declaraciones `addColumn` y `dropColumn` necesarias.\n\nPara agregar una columna:\n\n```php\nyii migrate/create add_position_column_to_post_table --fields=\"position:integer\"\n```\n\ngenera\n\n```php\nclass m150811_220037_add_position_column_to_post_table extends Migration\n{\n    public function up()\n    {\n        $this->addColumn('post', 'position', $this->integer());\n    }\n\n    public function down()\n    {\n        $this->dropColumn('post', 'position');\n    }\n}\n```\n\n### Eliminar Columna\n\nSi el nombre de la migración está en la forma `drop_xxx_column_from_yyy_table` entonces el archivo generado contendrá\nlas declaraciones `addColumn` y `dropColumn` necesarias.\n\n```php\nyii migrate/create drop_position_column_from_post_table --fields=\"position:integer\"\n```\n\ngenera\n\n```php\nclass m150811_220037_drop_position_column_from_post_table extends Migration\n{\n    public function up()\n    {\n        $this->dropColumn('post', 'position');\n    }\n\n    public function down()\n    {\n        $this->addColumn('post', 'position', $this->integer());\n    }\n}\n```\n\n### Agregar Tabla de Unión\n\nSi el nombre de la migración está en la forma `create_junction_table_for_xxx_and_yyy_tables` entonces se generará el código necesario\npara una tabla de unión.\n\n```php\nyii migrate/create create_junction_table_for_post_and_tag_tables --fields=\"created_at:dateTime\"\n```\n\ngenera\n\n```php\n/**\n * Handles the creation for table `post_tag`.\n * Has foreign keys to the tables:\n *\n * - `post`\n * - `tag`\n */\nclass m160328_041642_create_junction_table_for_post_and_tag_tables extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $this->createTable('post_tag', [\n            'post_id' => $this->integer(),\n            'tag_id' => $this->integer(),\n            'created_at' => $this->dateTime(),\n            'PRIMARY KEY(post_id, tag_id)',\n        ]);\n\n        // creates index for column `post_id`\n        $this->createIndex(\n            'idx-post_tag-post_id',\n            'post_tag',\n            'post_id'\n        );\n\n        // add foreign key for table `post`\n        $this->addForeignKey(\n            'fk-post_tag-post_id',\n            'post_tag',\n            'post_id',\n            'post',\n            'id',\n            'CASCADE'\n        );\n\n        // creates index for column `tag_id`\n        $this->createIndex(\n            'idx-post_tag-tag_id',\n            'post_tag',\n            'tag_id'\n        );\n\n        // add foreign key for table `tag`\n        $this->addForeignKey(\n            'fk-post_tag-tag_id',\n            'post_tag',\n            'tag_id',\n            'tag',\n            'id',\n            'CASCADE'\n        );\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        // drops foreign key for table `post`\n        $this->dropForeignKey(\n            'fk-post_tag-post_id',\n            'post_tag'\n        );\n\n        // drops index for column `post_id`\n        $this->dropIndex(\n            'idx-post_tag-post_id',\n            'post_tag'\n        );\n\n        // drops foreign key for table `tag`\n        $this->dropForeignKey(\n            'fk-post_tag-tag_id',\n            'post_tag'\n        );\n\n        // drops index for column `tag_id`\n        $this->dropIndex(\n            'idx-post_tag-tag_id',\n            'post_tag'\n        );\n\n        $this->dropTable('post_tag');\n    }\n}\n```\n\n### Migraciones Transaccionales <span id=\"transactional-migrations\"></span>\n\nAl ejecutar migraciones complejas de BD, es importante asegurarse que todas las migraciones funcionen o fallen como una unidad\nasí la base de datos puede mantener integridad y consistencia. Para alcanzar este objetivo, se recomienda que\nencierres las operación de la BD de cada migración en una [transacción](db-dao.md#performing-transactions).\n\nUna manera simple de implementar migraciones transaccionales es poniendo el código de las migraciones en los métodos `safeUp()` y `safeDown()`.\nEstos métodos se diferencias con `up()` y `down()` en que son encerrados implícitamente en una transacción.\nComo resultado, si alguna de las operaciones dentro de estos métodos falla, todas las operaciones previas son automáticamente revertidas.\n\nEn el siguiente ejemplo, además de crear la tabla `news` también insertamos un registro inicial dentro de la dicha tabla.\n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function safeUp()\n    {\n        $this->createTable('news', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string()->notNull(),\n            'content' => $this->text(),\n        ]);\n\n        $this->insert('news', [\n            'title' => 'test 1',\n            'content' => 'content 1',\n        ]);\n    }\n\n    public function safeDown()\n    {\n        $this->delete('news', ['id' => 1]);\n        $this->dropTable('news');\n    }\n}\n```\n\nTen en cuenta que usualmente cuando ejecutas múltiples operaciones en la BD en `safeUp()`, deberías revertir su orden de ejecución\nen `safeDown()`. En el ejemplo anterior primero creamos la tabla y luego insertamos la finla en `safeUp()`; mientras\nque en `safeDown()` primero eliminamos el registro y posteriormente eliminamos la tabla.\n\n> Note: No todos los DBMS soportan transacciones. Y algunas consultas a la BD no pueden ser puestas en transacciones. Para algunos ejemplos,\n  por favor lee acerca de [commits implícitos](https://dev.mysql.com/doc/refman/5.7/en/implicit-commit.html). En estos casos,\n  deberías igualmente implementar `up()` y `down()`.\n\n\n### Métodos de Acceso a la Base de Datos <span id=\"db-accessing-methods\"></span>\n\nLa clase base [[yii\\db\\Migration]] provee un grupo de métodos que te permiten acceder y manipular bases de datos.\nPodrías encontrar que estos métodos son nombrados de forma similar a los [métodos DAO](db-dao.md) provistos por la clase [[yii\\db\\Command]].\nPor ejemplo, el método [[yii\\db\\Migration::createTable()]] te permite crear una nueva tabla,\ntal como lo hace [[yii\\db\\Command::createTable()]].\n\nEl beneficio de utilizar lo métodos provistos por [[yii\\db\\Migration]] es que no necesitas explícitamente\ncrear instancias de [[yii\\db\\Command]], y la ejecución de cada método mostrará automáticamente mensajes útiles\ndiciéndote qué operaciones de la base de datos se realizaron y cuánto tiempo tomaron.\n\nDebajo hay una lista de todos los métodos de acceso a la base de datos:\n\n* [[yii\\db\\Migration::execute()|execute()]]: ejecuta una declaración SQL\n* [[yii\\db\\Migration::insert()|insert()]]: inserta un único registro\n* [[yii\\db\\Migration::batchInsert()|batchInsert()]]: inserta múltiples registros\n* [[yii\\db\\Migration::update()|update()]]: actualiza registros\n* [[yii\\db\\Migration::delete()|delete()]]: elimina registros\n* [[yii\\db\\Migration::createTable()|createTable()]]: crea una nueva tabla\n* [[yii\\db\\Migration::renameTable()|renameTable()]]: renombra una tabla\n* [[yii\\db\\Migration::dropTable()|dropTable()]]: elimina una tabla\n* [[yii\\db\\Migration::truncateTable()|truncateTable()]]: elimina todos los registros de una tabla\n* [[yii\\db\\Migration::addColumn()|addColumn()]]: agrega una columna\n* [[yii\\db\\Migration::renameColumn()|renameColumn()]]: renombra una columna\n* [[yii\\db\\Migration::dropColumn()|dropColumn()]]: elimina una columna\n* [[yii\\db\\Migration::alterColumn()|alterColumn()]]: modifica una columna\n* [[yii\\db\\Migration::addPrimaryKey()|addPrimaryKey()]]: agrega una clave primaria\n* [[yii\\db\\Migration::dropPrimaryKey()|dropPrimaryKey()]]: elimina una clave primaria\n* [[yii\\db\\Migration::addForeignKey()|addForeignKey()]]: agrega una clave foránea\n* [[yii\\db\\Migration::dropForeignKey()|dropForeignKey()]]: elimina una clave foránea\n* [[yii\\db\\Migration::createIndex()|createIndex()]]: crea un índice\n* [[yii\\db\\Migration::dropIndex()|dropIndex()]]: elimina un índice\n* [[yii\\db\\Migration::addCommentOnColumn()|addCommentOnColumn()]]: agrega un comentario a una columna\n* [[yii\\db\\Migration::dropCommentFromColumn()|dropCommentFromColumn()]]: elimina un comentario de una columna\n* [[yii\\db\\Migration::addCommentOnTable()|addCommentOnTable()]]: agrega un comentario a una tabla\n* [[yii\\db\\Migration::dropCommentFromTable()|dropCommentFromTable()]]: elimina un comentario de una tabla\n\n> Info: [[yii\\db\\Migration]] no provee un método de consulta a la base de datos. Esto es porque normalmente no necesitas\n  mostrar mensajes detallados al traer datos de una base de datos. También se debe a que puedes utilizar el poderoso\n  [Query Builder](db-query-builder.md) para generar y ejecutar consultas complejas.\n\n> Note: Al manipular datos utilizando una migración podrías encontrar que utilizando tus clases [Active Record](db-active-record.md)\n> para esto podría ser útil ya que algo de la lógica ya está implementada ahí. Ten en cuenta de todos modos, que en contraste con\n> el código escrito en las migraciones, cuya naturaleza es permanecer constante por siempre, la lógica de la aplicación está sujeta a cambios.\n> Entonces al utilizar Active Record en migraciones, los cambios en la lógica en la capa Active Record podrían accidentalmente romper\n> migraciones existentes. Por esta razón, el código de las migraciones debería permanecer independiente de determinada lógica de la aplicación\n> tal como clases Active Record.\n\n\n## Aplicar Migraciones <span id=\"applying-migrations\"></span>\n\nTo upgrade a database to its latest structure, you should apply all available new migrations using the following command:\nPara actualizar una base de datos a su última estructura, deberías aplicar todas las nuevas migraciones utilizando el siguiente comando:\n\n```\nyii migrate\n```\n\nEste comando listará todas las migraciones que no han sido aplicadas hasta el momento. Si confirmas que quieres aplicar\ndichas migraciones, se correrá el método `up()` o `safeUp()` en cada clase de migración nueva, una tras otra,\nen el orden de su valor de marca temporal. Si alguna de las migraciones falla, el comando terminará su ejecución sin aplicar\nel resto de las migraciones.\n\n> Tip: En caso de no disponer de la línea de comandos en el servidor, podrías intentar utilizar\n> la extensión [web shell](https://github.com/samdark/yii2-webshell).\n\nPor cada migración aplicada correctamente, el comando insertará un registro en la base de datos, en la tabla llamada\n`migration` para registrar la correcta aplicación de la migración. Esto permitirá a la herramienta de migración identificar\ncuáles migraciones han sido aplicadas y cuáles no.\n\n> Info: La herramienta de migración creará automáticamente la tabla  `migration` en la base de datos especificada\n  en la opción [[yii\\console\\controllers\\MigrateController::db|db]] del comando. Por defecto, la base de datos\n  es especificada en el [componente de aplicación](structure-application-components.md) `db`.\n\nA veces, podrías sólo querer aplicar una o algunas pocas migraciones, en vez de todas las migraciones disponibles.\nPuedes hacer esto el número de migraciones que quieres aplicar al ejecutar el comando.\nPor ejemplo, el siguiente comando intentará aplicar las tres siguientes migraciones disponibles:\n\n```\nyii migrate 3\n```\n\nPuedes además explícitamente especificar una migración en particular a la cual la base de datos debería migrar\nutilizando el comando `migrate/to` de acuerdo a uno de los siguientes formatos:\n\n```\nyii migrate/to 150101_185401                      # utiliza la marca temporal para especificar la migración\nyii migrate/to \"2015-01-01 18:54:01\"              # utiliza un string que puede ser analizado por strtotime()\nyii migrate/to m150101_185401_create_news_table   # utiliza el nombre completo\nyii migrate/to 1392853618                         # utiliza el tiempo UNIX\n```\n\nSi hubiera migraciones previas a la especificada sin aplicar, estas serán aplicadas antes de que la migración especificada\nsea aplicada.\n\nSi la migración especificada ha sido aplicada previamente, cualquier migración aplicada posteriormente será revertida.\n\n\n## Revertir Migraciones <span id=\"reverting-migrations\"></span>\n\nPara revertir (deshacer) una o varias migraciones ya aplicadas, puedes ejecutar el siguiente comando:\n\n```\nyii migrate/down     # revierte la más reciente migración aplicada\nyii migrate/down 3   # revierte las 3 últimas migraciones aplicadas\n```\n\n> Note: No todas las migraciones son reversibles. Intentar revertir tales migraciones producirá un error y detendrá\n  completamente el proceso de reversión.\n\n\n## Rehacer Migraciones <span id=\"redoing-migrations\"></span>\n\nRehacer (re-ejecutar) migraciones significa primero revertir las migraciones especificadas y luego aplicarlas nuevamente. Esto puede hacerse\nde esta manera:\n\n```\nyii migrate/redo        # rehace la más reciente migración aplicada\nyii migrate/redo 3      # rehace las 3 últimas migraciones aplicadas\n```\n\n> Note: Si una migración no es reversible, no tendrás posibilidades de rehacerla.\n\n\n## Listar Migraciones <span id=\"listing-migrations\"></span>\n\nPara listar cuáles migraciones han sido aplicadas y cuáles no, puedes utilizar los siguientes comandos:\n\n```\nyii migrate/history     # muestra las últimas 10 migraciones aplicadas\nyii migrate/history 5   # muestra las últimas 5 migraciones aplicadas\nyii migrate/history all # muestra todas las migraciones aplicadas\n\nyii migrate/new         # muestra las primeras 10 nuevas migraciones\nyii migrate/new 5       # muestra las primeras 5 nuevas migraciones\nyii migrate/new all     # muestra todas las nuevas migraciones\n```\n\n\n## Modificar el Historial de Migraciones <span id=\"modifying-migration-history\"></span>\n\nEn vez de aplicar o revertir migraciones, a veces simplemente quieres marcar que tu base de datos\nha sido actualizada a una migración en particular. Esto sucede normalmente cuando cambias manualmente la base de datos\na un estado particular y no quieres que la/s migración/es de ese cambio sean re-aplicadas posteriormente. Puedes alcanzar este objetivo\ncon el siguiente comando:\n\n```\nyii migrate/mark 150101_185401                      # utiliza la marca temporal para especificar la migración\nyii migrate/mark \"2015-01-01 18:54:01\"              # utiliza un string que puede ser analizado por strtotime()\nyii migrate/mark m150101_185401_create_news_table   # utiliza el nombre completo\nyii migrate/mark 1392853618                         # utiliza el tiempo UNIX\n```\n\nEl comando modificará la tabla `migration` agregando o eliminado ciertos registros para indicar que en la base de datos\nhan sido aplicadas las migraciones hasta la especificada. Ninguna migración será aplicada ni revertida por este comando.\n\n\n## Personalizar Migraciones <span id=\"customizing-migrations\"></span>\n\nHay varias maneras de personalizar el comando de migración.\n\n\n### Utilizar Opciones de la Línea de Comandos <span id=\"using-command-line-options\"></span>\n\nEl comando de migración trae algunas opciones de línea de comandos que pueden ser utilizadas para personalizar su comportamiento:\n\n* `interactive`: boolean (por defecto `true`), especificar si se debe ejecutar la migración en modo interactivo.\n  Cuando se indica `true`, se le pedirá confirmación al usuario antes de ejecutar ciertas acciones.\n  Puedes querer definirlo como `false` si el comando está siendo utilizado como un proceso de fondo.\n\n* `migrationPath`: string (por defecto `@app/migrations`), especifica el directorio que contiene todos los archivos\n  de clase de las migraciones. Este puede ser especificado tanto como una ruta a un directorio un [alias](concept-aliases.md) de ruta.\n  Ten en cuenta que el directorio debe existir, o el comando disparará un error.\n\n* `migrationTable`: string (por defecto `migration`), especifica el nombre de la tabla de la base de datos que almacena\n  información del historial de migraciones. Dicha tabla será creada por el comando en caso de que no exista.\n  Puedes también crearla manualmente utilizando la estructura `version varchar(255) primary key, apply_time integer`.\n\n* `db`: string (por defecto `db`), especifica el ID del [componente de aplicación](structure-application-components.md) de la base de datos.\n  Esto representa la base de datos que será migrada en este comando.\n\n* `templateFile`: string (por defecto `@yii/views/migration.php`), especifica la ruta al template\n  utilizado para generar el esqueleto de los archivos de clases de migración. Puede ser especificado tanto como una ruta a un archivo\n  como una [alias](concept-aliases.md) de una ruta. El template es un archivo PHP en el cual puedes utilizar una variable predefinida\n  llamada `$className` para obtener el nombre de clase de la migración.\n\n* `generatorTemplateFiles`: array (por defecto `[\n        'create_table' => '@yii/views/createTableMigration.php',\n        'drop_table' => '@yii/views/dropTableMigration.php',\n        'add_column' => '@yii/views/addColumnMigration.php',\n        'drop_column' => '@yii/views/dropColumnMigration.php',\n        'create_junction' => '@yii/views/createTableMigration.php'\n  ]`), especifica los templates utilizados para generar las migraciones. Ver \"[Generar Migraciones](#generating-migrations)\"\n  para más detalles.\n\n* `fields`: array de strings de definiciones de columna utilizado por el código de migración. Por defecto `[]`. El formato de cada\n  definición es `COLUMN_NAME:COLUMN_TYPE:COLUMN_DECORATOR`. Por ejemplo, `--fields=name:string(12):notNull` produce\n  una columna string de tamaño 12 que es not `null`.\n\nEl siguiente ejemplo muestra cómo se pueden utilizar estas opciones.\n\nPor ejemplo, si queremos migrar un módulo `forum` cuyos arhivos de migración\nestán ubicados dentro del directorio `migrations` del módulo, podemos utilizar el siguientedocs/guide-es/db-migrations.md\ncomando:\n\n```\n# realiza las migraciones de un módulo forum sin interacción del usuario\nyii migrate --migrationPath=@app/modules/forum/migrations --interactive=0\n```\n\n\n### Configurar el Comando Globalmente <span id=\"configuring-command-globally\"></span>\n\nEn vez de introducir los valores de las opciones cada vez que ejecutas un comandod e migración, podrías configurarlos\nde una vez por todas en la configuración de la aplicación como se muestra a continuación:\n\n```php\nreturn [\n    'controllerMap' => [\n        'migrate' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationTable' => 'backend_migration',\n        ],\n    ],\n];\n```\n\nCon esta configuración, cada vez que ejecutes un comando de migración, la tabla `backend_migration`\nserá utilizada para registrar el historial de migraciones. No necesitarás volver a especificarla con la opción `migrationTable`\nde la línea de comandos.\n\n\n## Migrar Múltiples Bases de Datos <span id=\"migrating-multiple-databases\"></span>\n\nPor defecto, las migraciones son aplicadas en la misma base de datos especificada en el [componente de aplicación](structure-application-components.md) `db`.\nSi quieres que sean aplicadas en una base de datos diferente, puedes especificar la opción `db` como se muestra a continuación,\n\n```\nyii migrate --db=db2\n```\n\nEl comando anterior aplicará las migraciones en la base de datos `db2`.\n\nA veces puede suceder que quieras aplicar *algunas* de las migraciones a una base de datos, mientras algunas otras\na una base de datos distinta. Para lograr esto, al implementar una clase de migración debes especificar explícitamente el ID del componente DB\nque la migración debe utilizar, como a continuación:\n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function init()\n    {\n        $this->db = 'db2';\n        parent::init();\n    }\n}\n```\n\nLa migración anterior se aplicará a `db2`, incluso si especificas una base de datos diferente en la opción `db` de la\nlínea de comandos. Ten en cuenta que el historial aún será registrado in la base de datos especificada en la opción `db` de la línea de comandos.\n\nSi tienes múltiples migraciones que utilizan la misma base de datos, es recomandable que crees una clase base de migración \ncon el código `init()` mostrado. Entonces cada clase de migración puede extender de esa clase base.\n\n> Tip: Aparte de definir la propiedad [[yii\\db\\Migration::db|db]], puedes también operar en diferentes bases de datos\n  creando nuevas conexiones de base de datos en tus clases de migración. También puedes utilizar [métodos DAO](db-dao.md)\n  con esas conexiones para manipular diferentes bases de datos.\n\nAnother strategy that you can take to migrate multiple databases is to keep migrations for different databases in\ndifferent migration paths. Then you can migrate these databases in separate commands like the following:\nOtra estrategia que puedes seguir para migrar múltiples bases de datos es mantener las migraciones para diferentes bases de datos en\ndistintas rutas de migración. Entonces podrías migrar esas bases de datos en comandos separados como a continuación:\n\n```\nyii migrate --migrationPath=@app/migrations/db1 --db=db1\nyii migrate --migrationPath=@app/migrations/db2 --db=db2\n...\n```\n\nEl primer comando aplicará las migraciones que se encuentran en `@app/migrations/db1` en la base de datos `db1`, el segundo comando\naplicará las migraciones que se encuentran en `@app/migrations/db2` en `db2`, y así sucesivamente.\n"
  },
  {
    "path": "docs/guide-es/db-query-builder.md",
    "content": "Constructor de Consultas\n========================\n\n> Note: Esta sección está en desarrollo.\n\nYii proporciona una capa de acceso básico a bases de datos como se describe en la sección\n[Objetos de Acceso a Bases de Datos](db-dao.md). La capa de acceso a bases de datos proporciona un método de bajo\nnivel (low-level) para interaccionar con la base de datos. Aunque a veces puede ser útil la escritura de sentencias\nSQLs puras, en otras situaciones puede ser pesado y propenso a errores. Otra manera de tratar con bases de datos puede\nser el uso de Constructores de Consultas (Query Builder). El Constructor de Consultas proporciona un medio orientado a\nobjetos para generar las consultas que se ejecutarán.\n\nUn uso típico de Constructor de Consultas puede ser el siguiente:\n\n```php\n$rows = (new \\yii\\db\\Query())\n    ->select('id, name')\n    ->from('user')\n    ->limit(10)\n    ->all();\n\n// que es equivalente al siguiente código:\n\n$query = (new \\yii\\db\\Query())\n    ->select('id, name')\n    ->from('user')\n    ->limit(10);\n\n//  Crear un comando. Se puede obtener la consulta SQL actual utilizando $command->sql\n$command = $query->createCommand();\n\n// Ejecutar el comando:\n$rows = $command->queryAll();\n```\n\nMétodos de Consulta\n-------------------\n\nComo se puede observar, primero se debe tratar con [[yii\\db\\Query]]. En realidad, `Query` sólo se encarga de\nrepresentar diversa información de la consulta. La lógica para generar la consulta se efectúa mediante\n[[yii\\db\\QueryBuilder]] cuando se llama al método `createCommand()`, y la ejecución de la consulta la efectúa\n[[yii\\db\\Command]].\n\nSe ha establecido, por convenio, que [[yii\\db\\Query]] proporcione un conjunto de métodos de consulta comunes que\nconstruirán la consulta, la ejecutarán, y devolverán el resultado. Por ejemplo:\n\n- [[yii\\db\\Query::all()|all()]]: construye la consulta, la ejecuta y devuelve todos los resultados en formato de array.\n- [[yii\\db\\Query::one()|one()]]: devuelve la primera fila del resultado.\n- [[yii\\db\\Query::column()|column()]]: devuelve la primera columna del resultado.\n- [[yii\\db\\Query::scalar()|scalar()]]: devuelve la primera columna en la primera fila del resultado.\n- [[yii\\db\\Query::exists()|exists()]]: devuelve un valor indicando si la el resultado devuelve algo.\n- [[yii\\db\\Query::count()|count()]]: devuelve el resultado de la consulta `COUNT`. Otros métodos similares incluidos\n  son `sum($q)`, `average($q)`, `max($q)`, `min($q)`, que soportan las llamadas funciones de agregación. El parámetro\n  `$q` es obligatorio en estos métodos y puede ser el nombre de la columna o expresión.\n\nConstrucción de Consultas\n-------------------------\n\nA continuación se explicará como construir una sentencia SQL que incluya varias clausulas. Para simplificarlo, usamos\n`$query` para representar el objeto [[yii\\db\\Query]]:\n\n### `SELECT`\n\nPara formar una consulta `SELECT` básica, se necesita especificar que columnas y de que tablas se seleccionarán:\n\n```php\n$query->select('id, name')\n    ->from('user');\n```\n\nLas opciones de select se pueden especificar como una cadena de texto (string) separada por comas o como un array. La\nsintaxis del array es especialmente útil cuando se forma la selección dinámicamente.\n\n```php\n$query->select(['id', 'name'])\n    ->from('user');\n```\n\n> Info: Se debe usar siempre el formato array si la clausula `SELECT` contiene expresiones SQL. Esto se debe a\n  que una expresión SQL como `CONCAT(first_name, last_name) AS full_name` puede contener comas. Si se junta con otra\n  cadena de texto de otra columna, puede ser que la expresión se divida en varias partes por comas, esto puede\n  conllevar a errores.\n\nCuando se especifican columnas, se pueden incluir los prefijos de las tablas o alias de columnas, ej.  `user.id`,\n`user.id AS user_id`. Si se usa un array para especificar las columnas, también se pueden usar las claves del array\npara especificar los alias de columna, ej. `['user_id' => 'user.id', 'user_name' => 'user.name']`.\n\nA partir de la versión 2.0.1, también se pueden seleccionar subconsultas como columnas. Por ejemplo:\n\n```php\n$subQuery = (new Query)->select('COUNT(*)')->from('user');\n$query = (new Query)->select(['id', 'count' => $subQuery])->from('post');\n// $query representa la siguiente sentencia SQL:\n// SELECT `id`, (SELECT COUNT(*) FROM `user`) AS `count` FROM `post`\n```\n\nPara seleccionar filas distintas, se puede llamar a `distinct()`, como se muestra a continuación:\n\n```php\n$query->select('user_id')->distinct()->from('post');\n```\n\n### `FROM`\n\nPara especificar de que tabla(s) se quieren seleccionar los datos, se llama a `from()`:\n\n```php\n$query->select('*')->from('user');\n```\n\nSe pueden especificar múltiples tablas usando una cadena de texto separado por comas o un array. Los nombres de tablas\npueden contener prefijos de esquema (ej. `'public.user'`) y/o alias de tablas (ej. `'user u'). El método\nentrecomillara automáticamente los nombres de tablas a menos que contengan algún paréntesis (que significa que se\nproporciona la tabla como una subconsulta o una expresión de BD). Por ejemplo:\n\n```php\n$query->select('u.*, p.*')->from(['user u', 'post p']);\n```\n\nCuando se especifican las tablas como un array, también se pueden usar las claves de los arrays como alias de tablas\n(si una tabla no necesita alias, no se usa una clave en formato texto). Por ejemplo:\n\n```php\n$query->select('u.*, p.*')->from(['u' => 'user', 'p' => 'post']);\n```\n\nSe puede especificar una subconsulta usando un objeto `Query`. En este caso, la clave del array correspondiente se\nusará como alias para la subconsulta.\n\n```php\n$subQuery = (new Query())->select('id')->from('user')->where('status=1');\n$query->select('*')->from(['u' => $subQuery]);\n```\n\n### `WHERE`\n\nHabitualmente se seleccionan los datos basándose en ciertos criterios. El Constructor de Consultas tiene algunos\nmétodos útiles para especificarlos, el más poderoso de estos es `where`, y se puede usar de múltiples formas.\n\nLa manera más simple para aplicar una condición es usar una cadena de texto:\n\n```php\n$query->where('status=:status', [':status' => $status]);\n```\n\nCuando se usan cadenas de texto, hay que asegurarse que se unen los parámetros de la consulta, no crear una consulta\nmediante concatenación de cadenas de texto. El enfoque anterior es seguro, el que se muestra a continuación, no lo es:\n\n```php\n$query->where(\"status=$status\"); // Peligroso!\n```\n\nEn lugar de enlazar los valores de estado inmediatamente, se puede hacer usando `params` o `addParams`:\n\n```php\n$query->where('status=:status');\n$query->addParams([':status' => $status]);\n```\n\nSe pueden establecer múltiples condiciones en `where` usando el *formato hash*.\n\n```php\n$query->where([\n    'status' => 10,\n    'type' => 2,\n    'id' => [4, 8, 15, 16, 23, 42],\n]);\n```\n\nEl código generará la el siguiente SQL:\n\n```sql\nWHERE (`status` = 10) AND (`type` = 2) AND (`id` IN (4, 8, 15, 16, 23, 42))\n```\n\nEl valor NULO es un valor especial en las bases de datos, y el Constructor de Consultas lo gestiona inteligentemente.\nEste código:\n\n```php\n$query->where(['status' => null]);\n```\n\nda como resultado la siguiente cláusula WHERE:\n\n```sql\nWHERE (`status` IS NULL)\n```\n\nTambién se pueden crear subconsultas con objetos de tipo `Query` como en el siguiente ejemplo:\n\n```php\n$userQuery = (new Query)->select('id')->from('user');\n$query->where(['id' => $userQuery]);\n```\n\nque generará el siguiente código SQL:\n\n```sql\nWHERE `id` IN (SELECT `id` FROM `user`)\n```\n\nOtra manera de usar el método es el formato de operando que es `[operator, operand1, operand2, ...]`.\n\nEl operando puede ser uno de los siguientes (ver también [[yii\\db\\QueryInterface::where()]]):\n\n- `and`: los operandos deben concatenerase usando `AND`. por ejemplo, `['and', 'id=1', 'id=2']` generará\n  `id=1 AND id=2`. Si el operando es un array, se convertirá en una cadena de texto usando las reglas aquí descritas.\n  Por ejemplo, `['and', 'type=1', ['or', 'id=1', 'id=2']]` generará `type=1 AND (id=1 OR id=2)`. El método no\n  ejecutará ningún filtrado ni entrecomillado.\n\n- `or`: similar al operando `and` exceptuando que los operando son concatenados usando `OR`.\n\n- `between`: el operando 1 debe ser el nombre de columna, y los operandos 2 y 3 deben ser los valores iniciales y\n  finales del rango en el que se encuentra la columna. Por ejemplo, `['between', 'id', 1, 10]` generará\n  `id BETWEEN 1 AND 10`.\n\n- `not between`: similar a `between` exceptuando que  `BETWEEN` se reemplaza por `NOT BETWEEN` en la condición\n  generada.\n\n- `in`: el operando 1 debe ser una columna o una expresión de BD. El operando 2 puede ser un array o un objeto de tipo\n  `Query`. Generará una condición `IN`. Si el operando 2 es un array, representará el rango de valores que puede\n  albergar la columna o la expresión de BD; Si el operando 2 es un objeto de tipo `Query`, se generará una subconsulta\n  y se usará como rango de la columna o de la expresión de BD. Por ejemplo, `['in', 'id', [1, 2, 3]]` generará\n  `id IN (1, 2, 3)`. El método entrecomillará adecuadamente el nombre de columna y filtrará los valores del rango. El\n  operando `in` también soporta columnas compuestas. En este caso, el operando 1 debe se un array de columnas,\n  mientras que el operando 2 debe ser un array de arrays o un objeto de tipo `Query` que represente el rango de las\n  columnas.\n\n- `not in`: similar que el operando `in` exceptuando que `IN` se reemplaza por `NOT IN` en la condición generada.\n\n- `like`: el operando 1 debe ser una columna o una expresión de BD, y el operando 2 debe ser una cadena de texto o un\n  array que represente los valores a los que tienen que asemejarse la columna o la expresión de BD.Por ejemplo,\n  `['like', 'name', 'tester']` generará `name LIKE '%tester%'`. Cuando se da el valor rango como un array, se\n  generarán múltiples predicados `LIKE` y se concatenaran usando `AND`. Por ejemplo,\n  `['like', 'name', ['test', 'sample']]` generará `name LIKE '%test%' AND name LIKE '%sample%'`. También se puede\n  proporcionar un tercer operando opcional para especificar como deben filtrarse los caracteres especiales en los\n  valores. El operando debe se un array que mapeen los caracteres especiales a sus caracteres filtrados asociados. Si\n  no se proporciona este operando, se aplicará el mapeo de filtrado predeterminado. Se puede usar `false` o un array\n  vacío para indicar que los valores ya están filtrados y no se necesita aplicar ningún filtro. Hay que tener en\n  cuenta que cuando se usa un el mapeo de filtrado (o no se especifica el tercer operando), los valores se encerraran\n  automáticamente entre un par de caracteres de porcentaje.\n\n> Note: Cuando se usa PostgreSQL también se puede usar\n[`ilike`](https://www.postgresql.org/docs/8.3/static/functions-matching.html#FUNCTIONS-LIKE) en lugar de `like` para\nfiltrar resultados insensibles a mayúsculas (case-insensitive).\n\n- `or like`: similar al operando `like` exceptuando que se usa `OR` para concatenar los predicados `LIKE` cuando haya\n  un segundo operando en un array.\n\n- `not like`: similar al operando `like` exceptuando que se usa `LIKE` en lugar de `NOT LIKE` en las condiciones\n  generadas.\n\n- `or not like`: similar al operando `not like` exceptuando que se usa `OR` para concatenar los predicados `NOT LIKE`.\n\n- `exists`: requiere un operando que debe ser una instancia de [[yii\\db\\Query]] que represente la subconsulta. Esto\n  generará una expresión `EXISTS (sub-query)`.\n\n- `not exists`: similar al operando `exists` y genera una expresión `NOT EXISTS (sub-query)`.\n\nAdicionalmente se puede especificar cualquier cosa como operando:\n\n```php\n$query->select('id')\n    ->from('user')\n    ->where(['>=', 'id', 10]);\n```\n\nCuyo resultado será:\n\n```sql\nSELECT id FROM user WHERE id >= 10;\n```\n\nSi se construyen partes de una condición dinámicamente, es muy convenientes usar `andWhere()` y `orWhere()`:\n\n```php\n$status = 10;\n$search = 'yii';\n\n$query->where(['status' => $status]);\nif (!empty($search)) {\n    $query->andWhere(['like', 'title', $search]);\n}\n```\n\nEn el caso que `$search` no este vacío, se generará el siguiente código SQL:\n\n```sql\nWHERE (`status` = 10) AND (`title` LIKE '%yii%')\n```\n\n#### Construcción de Condiciones de Filtro\n\nCuando se generan condiciones de filtro basadas en datos recibidos de usuarios (inputs), a menudo se quieren gestionar\nde forma especial las \"datos vacíos\" para ignorarlos en los filtros. Por ejemplo, teniendo un formulario HTML que\nobtiene el nombre de usuario y la dirección de correo electrónico. Si el usuario solo rellena el campo de nombre de\nusuario, se puede querer generar una consulta para saber si el nombre de usuario recibido es valido. Se puede usar\n`filterWhere()` para conseguirlo:\n\n```php\n// $username y $email son campos de formulario rellenados por usuarios\n$query->filterWhere([\n    'username' => $username,\n    'email' => $email,\n]);\n```\n\nEl método `filterWhere()` es muy similar al método `where()`. La principal diferencia es que el `filterWhere()`\neliminará los valores vacíos de las condiciones proporcionadas. Por lo tanto si `$email` es \"vació\", la consulta\nresultante será `...WHERE username=:username`; y si tanto `$username` como `$email` son \"vacías\", la consulta no\ntendrá `WHERE`.\n\nDecimos que un valor es *vacío* si es nulo, una cadena de texto vacía, una cadena de texto que consista en espacios en\nblanco o un array vacío.\n\nTambién se pueden usar `andFilterWhere()` y `orFilterWhere()` para añadir más condiciones de filtro.\n\n### `ORDER BY`\n\nSe pueden usar `orderBy` y `addOrderBy` para ordenar resultados:\n\n```php\n$query->orderBy([\n    'id' => SORT_ASC,\n    'name' => SORT_DESC,\n]);\n```\n\nAquí estamos ordenando por `id` ascendente y después por `name` descendente.\n\n### `GROUP BY` and `HAVING`\n\nPara añadir `GROUP BY` al SQL generado se puede usar el siguiente código:\n\n```php\n$query->groupBy('id, status');\n```\n\nSi se quieren añadir otro campo después de usar `groupBy`:\n\n```php\n$query->addGroupBy(['created_at', 'updated_at']);\n```\n\nPara añadir la condición `HAVING` se pueden usar los métodos `having` y `andHaving` y `orHaving`. Los parámetros para\nellos son similares a los del grupo de métodos `where`:\n\n```php\n$query->having(['status' => $status]);\n```\n\n### `LIMIT` and `OFFSET`\n\nPara limitar el resultado a 10 filas se puede usar `limit`:\n\n```php\n$query->limit(10);\n```\n\nPara saltarse las 100 primeras filas, se puede usar:\n\n```php\n$query->offset(100);\n```\n\n### `JOIN`\n\nLas clausulas `JOIN` se generan en el Constructor de Consultas usando el método join aplicable:\n\n- `innerJoin()`\n- `leftJoin()`\n- `rightJoin()`\n\nEste left join selecciona los datos desde dos tablas relacionadas en una consulta:\n\n```php\n$query->select(['user.name AS author', 'post.title as title'])\n    ->from('user')\n    ->leftJoin('post', 'post.user_id = user.id');\n```\n\nEn el código, el primer parámetro del método `leftjoin` especifica la tabla a la que aplicar el join. El segundo\nparámetro, define la condición del join.\n\nSi la aplicación de bases de datos soporta otros tipos de joins, se pueden usar mediante el método `join` genérico:\n\n```php\n$query->join('FULL OUTER JOIN', 'post', 'post.user_id = user.id');\n```\n\nEl primer argumento es el tipo de join a realizar. El segundo es la tabla a la que aplicar el join, y el tercero es la condición:\n\nComo en `FROM`, también se pueden efectuar joins con subconsultas. Para hacerlo, se debe especificar la subconsulta\ncomo un array que tiene que contener un elemento. El valor del array tiene que ser un objeto de tipo `Query` que\nrepresente la subconsulta, mientras que la clave del array es el alias de la subconsulta. Por ejemplo:\n\n```php\n$query->leftJoin(['u' => $subQuery], 'u.id=author_id');\n```\n\n### `UNION`\n\nEn SQL `UNION` agrega resultados de una consulta a otra consulta. Las columnas devueltas por ambas consultas deben\ncoincidir. En Yii para construirla, primero se pueden formar dos objetos de tipo query y después usar el método\n`union`:\n\n```php\n$query = new Query();\n$query->select(\"id, category_id as type, name\")->from('post')->limit(10);\n\n$anotherQuery = new Query();\n$anotherQuery->select('id, type, name')->from('user')->limit(10);\n\n$query->union($anotherQuery);\n```\n\nConsulta por Lotes\n---------------\n\nCuando se trabaja con grandes cantidades de datos, los métodos como [[yii\\db\\Query::all()]] no son adecuados ya que\nrequieren la carga de todos los datos en memoria. Para mantener los requerimientos de memoria reducidos, Yii\nproporciona soporte a las llamadas consultas por lotes (batch query). Una consulta por lotes usa un cursor de datos y\nrecupera los datos en bloques.\n\nLas consultas por lotes se pueden usar del siguiente modo:\n\n```php\nuse yii\\db\\Query;\n\n$query = (new Query())\n    ->from('user')\n    ->orderBy('id');\n\nforeach ($query->batch() as $users) {\n    // $users is an array of 100 or fewer rows from the user table\n}\n\n// o si se quieren iterar las filas una a una\nforeach ($query->each() as $user) {\n    // $user representa uno fila de datos de la tabla user\n}\n```\n\nLos métodos [[yii\\db\\Query::batch()]] y [[yii\\db\\Query::each()]] devuelven un objeto [[yii\\db\\BatchQueryResult]] que\nimplementa una interfaz `Iterator` y así se puede usar en el constructor `foreach`. Durante la primera iteración, se\nefectúa una consulta SQL a la base de datos. Desde entonces, los datos se recuperan por lotes en las iteraciones. El\ntamaño predeterminado de los lotes es 100, que significa que se recuperan 100 filas de datos en cada lote. Se puede\nmodificar el tamaño de los lotes pasando pasando un primer parámetro a los métodos `batch()` o `each()`.\n\nEn comparación con [[yii\\db\\Query::all()]], las consultas por lotes sólo cargan 100 filas de datos en memoria cada\nvez. Si el procesan los datos y después se descartan inmediatamente, las consultas por lotes, pueden ayudar a mantener\nel uso de memora bajo un limite.\n\nSi se especifica que el resultado de la consulta tiene que ser indexado por alguna columna mediante\n[[yii\\db\\Query::indexBy()]], las consultas por lotes seguirán manteniendo el indice adecuado. Por ejemplo,\n\n```php\nuse yii\\db\\Query;\n\n$query = (new Query())\n    ->from('user')\n    ->indexBy('username');\n\nforeach ($query->batch() as $users) {\n    // $users esta indexado en la columna \"username\"\n}\n\nforeach ($query->each() as $username => $user) {\n}\n```\n"
  },
  {
    "path": "docs/guide-es/glossary.md",
    "content": "# A\n\n## alias\n\nAlias es un string utilizado por Yii para referirse a una clase o directorio tal como `@app/vendor`.\n\n## aplicación\n\nLa aplicación es el objeto central durante la solicitud HTTP. Contiene un número de componentes con los que toma información de la solicitud y la envía al controlador apropiado para posterior procesamiento.\n\nEl objeto de la aplicación es instanciado como un singleton por el script de entrada. El singleton de la aplicación puede ser accedido desde cualquier lugar a través de `\\Yii::$app`.\n\n## assets\n\nAsset se refiere a un archivo de recurso. Típicamente contiene JavaScript o CSS pero puede ser cualquier otra cosa que sea accesible vía HTTP.\n\n## atributo\n\nUn atributo es una propiedad de un modelo (una variable miembro de clase o una propiedad mágica definida vía `__get()`/`__set()`) que almacena **datos de negocio**.\n\n# B\n\n## bundle\n\nBundle, conocido como paquete en Yii 1.1, se refiere a un número de recursos y un archivo de configuración que describe dependencias y lista recursos.\n\n# C\n\n## configuración\n\nConfiguración puede referirse tanto al proceso de establecer propiedades de un objeto como a un archivo de configuración que almacena la definición de propiedades para un objeto o clase de objetos.\n\n# E\n\n## extensión\n\nExtensión es un grupo de clases, paquete de recursos y configuraciones que agrega más características a la aplicación.\n\n# I\n\n## instalación\n\nInstalación es el proceso de preparar algo para trabajar, desde seguir un archivo léame hasta ejecutar un script preparado especialmente para tal fin. En el caso de Yii, define permisos y chequea los requerimientos para el funcionamiento del software.\n\n# M\n\n## módulo\n\nMódulo es una sub-aplicación que contiene elementos MVC en sí mismo, como modelos, vistas, controladores, etc. y puede ser utilizado dentro de la aplicación principal. Típicamente remitiendo las solicitudes al módulo en vez de manejándolo desde controladores.\n\n# N\n\n## namespace\n\nNamespace (espacio de nombres) se refiere a una [característica de PHP](https://www.php.net/manual/es/language.namespaces.php) activamente utilizada en Yii 2.\n\n# P\n\n## paquete\n\n[Ver bundle](#bundle).\n\n# V\n\n## vendor\n\nVendor (proveedor) es una organización o un desarrollador individual que provee código en forma de extensiones, módulos o librerías.\n"
  },
  {
    "path": "docs/guide-es/helper-array.md",
    "content": "ArrayHelper\n===========\n\nAdicionalmente al [rico conjunto de funciones para arrays de PHP](https://www.php.net/manual/es/book.array.php), el array helper de Yii proporciona\nmétodos estáticos adicionales permitiendo trabajar con arrays de manera más eficiente.\n\n\n## Devolviendo Valores <span id=\"getting-values\"></span>\n\nRecuperar valores de un array, un objeto o una estructura compleja usando PHP estándar es bastante\nrepetitivo. Tienes que comprobar primero si una clave existe con `isset`, después devolver el valor si existe, si no,\ndevolver un valor por defecto:\n\n```php\nclass User\n{\n    public $name = 'Alex';\n}\n\n$array = [\n    'foo' => [\n        'bar' => new User(),\n    ]\n];\n\n$value = isset($array['foo']['bar']->name) ? $array['foo']['bar']->name : null;\n```\n\nYii proviene de un método muy conveniente para hacerlo:\n\n```php\n$value = ArrayHelper::getValue($array, 'foo.bar.name');\n```\n\nEl primer argumento del método es de donde vamos a obtener el valor. El segundo argumento especifica como devolver el dato. Puede ser\nde la siguiente manera:\n\n- Nombre de la clave del array o de la propiedad del objeto para recuperar el valor.\n- Conjunto de puntos separados por las claves del array o los nombres de las propiedades del objeto. Esto se ha usado en el ejemplo anterior.\n- Un callback que devuelve un valor.\n\nEl callback se debería usar de la siguiente manera:\n\n```php\n$fullName = ArrayHelper::getValue($user, function ($user, $defaultValue) {\n    return $user->firstName . ' ' . $user->lastName;\n});\n```\n\nEl tercer argumento opcional es el valor por defecto el cual es `null` si no se especifica. Podría ser utilizado de la siguiente manera:\n\n```php\n$username = ArrayHelper::getValue($comment, 'user.username', 'Unknown');\n```\n\nEn caso de que quieras coger un valor y luego removerlo inmediatamente del array puedes usar el método `remove`:\n\n```php\n$array = ['type' => 'A', 'options' => [1, 2]];\n$type = ArrayHelper::remove($array, 'type');\n```\n\nDespués de ejecutar el código el `$array` contendrá `['options' => [1, 2]]` y `$type` debe ser `A`. Tenga en cuenta que a diferencia del método\n`getValue`, `remove` solo soporta nombres clave simples.\n\n\n## Comprobando la Existencia de Claves <span id=\"checking-existence-of-keys\"></span>\n\n`ArrayHelper::keyExists` funciona de la misma manera que [array_key_exists](https://www.php.net/manual/es/function.array-key-exists.php)\nexcepto que también soporta case-insensitive para la comparación de claves. Por ejemplo,\n\n```php\n$data1 = [\n    'userName' => 'Alex',\n];\n\n$data2 = [\n    'username' => 'Carsten',\n];\n\nif (!ArrayHelper::keyExists('username', $data1, false) || !ArrayHelper::keyExists('username', $data2, false)) {\n    echo \"Please provide username.\";\n}\n```\n\n## Recuperando Columnas <span id=\"retrieving-columns\"></span>\n\nA menudo necesitas obtener unos valores de una columna de las filas de datos u objetos de un array. Un ejemplo común es obtener una lista de IDs.\n\n```php\n$data = [\n    ['id' => '123', 'data' => 'abc'],\n    ['id' => '345', 'data' => 'def'],\n];\n$ids = ArrayHelper::getColumn($array, 'id');\n```\n\nEl resultado será `['123', '345']`.\n\nSi se requieren transformaciones adicionales o la manera de obtener el valor es complejo, se podría especificar como segundo argumento\nuna función anónima :\n\n```php\n$result = ArrayHelper::getColumn($array, function ($element) {\n    return $element['id'];\n});\n```\n\n\n## Re-indexar Arrays <span id=\"reindexing-arrays\"></span>\n\nCon el fin de indexar un array según una clave especificada, se puede usar el método `index`. La entrada debería ser\nun array multidimensional o un array de objetos. `$key` puede ser tanto una clave del sub-array, un nombre de una propiedad\ndel objeto, o una función anónima que debe devolver el valor que será utilizado como clave.\n\nEl atributo `$groups` es un array de claves, que será utilizado para agrupar el array de entrada en uno o más sub-arrays\nbasado en la clave especificada.\n\nSi el atributo `$key` o su valor por el elemento en particular es `null` y `$groups` no está definido, dicho elemento del array\nserá descartado. De otro modo, si `$groups` es especificado, el elemento del array será agregado al array resultante\nsin una clave.\n\nPor ejemplo:\n\n```php\n$array = [\n    ['id' => '123', 'data' => 'abc', 'device' => 'laptop'],\n    ['id' => '345', 'data' => 'def', 'device' => 'tablet'],\n    ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone'],\n];\n$result = ArrayHelper::index($array, 'id');');\n```\n\nEl resultado será un array asociativo, donde la clave es el valor del atributo `id`\n\n```php\n[\n    '123' => ['id' => '123', 'data' => 'abc', 'device' => 'laptop'],\n    '345' => ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone']\n    // El segundo elemento del array original es sobrescrito por el último elemento debido a que tiene el mismo id\n]\n```\n\nPasando una función anónima en `$key`, da el mismo resultado.\n\n```php\n$result = ArrayHelper::index($array, function ($element) {\n    return $element['id'];\n});\n```\n\nPasando `id` como tercer argumento, agrupará `$array` mediante `id`:\n\n```php\n$result = ArrayHelper::index($array, null, 'id');\n```\n\nEl resultado será un array multidimensional agrupado por `id` en su primer nivel y no indexado en su segundo nivel:\n\n```php\n[\n    '123' => [\n        ['id' => '123', 'data' => 'abc', 'device' => 'laptop']\n    ],\n    '345' => [ // todos los elementos con este índice están presentes en el array resultante\n        ['id' => '345', 'data' => 'def', 'device' => 'tablet'],\n        ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone'],\n    ]\n]\n```\n\nUna función anónima puede ser usada también en el array agrupador:\n\n```php\n$result = ArrayHelper::index($array, 'data', [function ($element) {\n    return $element['id'];\n}, 'device']);\n```\n\nEl resultado será un array multidimensional agrupado por `id` en su primer nivel, por `device` en su segundo nivel e\nindexado por `data` en su tercer nivel:\n\n```php\n[\n    '123' => [\n        'laptop' => [\n            'abc' => ['id' => '123', 'data' => 'abc', 'device' => 'laptop']\n        ]\n    ],\n    '345' => [\n        'tablet' => [\n            'def' => ['id' => '345', 'data' => 'def', 'device' => 'tablet']\n        ],\n        'smartphone' => [\n            'hgi' => ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone']\n        ]\n    ]\n]\n```\n\n## Construyendo Mapas (Maps) <span id=\"building-maps\"></span>\n\nCon el fin de construir un mapa (pareja clave-valor) de un array multidimensional o un array de objetos puedes usar el método `map`.\nLos parámetros `$from` y `$to`  especifican los nombres de las claves o los nombres de las propiedades que serán configuradas en el mapa. Opcionalmente, se puede\nagrupar en el mapa de acuerdo al campo de agrupamiento `$group`. Por ejemplo,\n\n```php\n$array = [\n    ['id' => '123', 'name' => 'aaa', 'class' => 'x'],\n    ['id' => '124', 'name' => 'bbb', 'class' => 'x'],\n    ['id' => '345', 'name' => 'ccc', 'class' => 'y'],\n);\n\n$result = ArrayHelper::map($array, 'id', 'name');\n// el resultado es:\n// [\n//     '123' => 'aaa',\n//     '124' => 'bbb',\n//     '345' => 'ccc',\n// ]\n\n$result = ArrayHelper::map($array, 'id', 'name', 'class');\n// el resultado es:\n// [\n//     'x' => [\n//         '123' => 'aaa',\n//         '124' => 'bbb',\n//     ],\n//     'y' => [\n//         '345' => 'ccc',\n//     ],\n// ]\n```\n\n\n## Ordenamiento Multidimensional <span id=\"multidimensional-sorting\"></span>\n\nEl método `multisort` ayuda a ordenar un array de objetos o arrays anidados por una o varias claves. Por ejemplo,\n\n```php\n$data = [\n    ['age' => 30, 'name' => 'Alexander'],\n    ['age' => 30, 'name' => 'Brian'],\n    ['age' => 19, 'name' => 'Barney'],\n];\nArrayHelper::multisort($data, ['age', 'name'], [SORT_ASC, SORT_DESC]);\n```\n\nDespués del ordenado obtendremos lo siguiente en `$data`:\n\n```php\n[\n    ['age' => 19, 'name' => 'Barney'],\n    ['age' => 30, 'name' => 'Brian'],\n    ['age' => 30, 'name' => 'Alexander'],\n];\n```\n\nEl segundo argumento que especifica las claves para ordenar puede ser una cadena si se trata de una clave, un array en caso de que tenga múltiples claves\no una función anónima como la siguiente\n\n```php\nArrayHelper::multisort($data, function($item) {\n    return isset($item['age']) ? ['age', 'name'] : 'name';\n});\n```\n\nEl tercer argumento es la dirección. En caso de ordenar por una clave podría ser `SORT_ASC` o\n`SORT_DESC`. Si ordenas por múltiples valores puedes ordenar cada valor diferentemente proporcionando un array de\ndirecciones de ordenación.\n\nEl último argumento es un PHP sort flag que toma los mismos valores que los pasados a\nPHP [sort()](https://www.php.net/manual/es/function.sort.php).\n\n\n## Detectando Tipos de Array <span id=\"detecting-array-types\"></span>\n\nEs muy útil saber si un array es indexado o asociativo. He aquí un ejemplo:\n\n```php\n// sin claves especificadas\n$indexed = ['Qiang', 'Paul'];\necho ArrayHelper::isIndexed($indexed);\n\n// todas las claves son strings\n$associative = ['framework' => 'Yii', 'version' => '2.0'];\necho ArrayHelper::isAssociative($associative);\n```\n\n\n## Codificación y Decodificación de Valores HTML <span id=\"html-encoding-values\"></span>\n\nCon el fin de codificar o decodificar caracteres especiales en un array de strings con entidades HTML puedes usar lo siguiente:\n\n```php\n$encoded = ArrayHelper::htmlEncode($data);\n$decoded = ArrayHelper::htmlDecode($data);\n```\n\nSolo los valores se codifican por defecto. Pasando como segundo argumento `false` puedes codificar un array de claves también.\nLa codificación utilizará el charset de la aplicación y podría ser cambiado pasandole un tercer argumento.\n\n\n## Fusionando Arrays <span id=\"merging-arrays\"></span>\n\n```php\n  /**\n    * Fusiona recursivamente dos o más arrays en uno.\n    * Si cada array tiene un elemento con el mismo valor string de clave, el último\n    * sobrescribirá el anterior (difiere de array_merge_recursive).\n    * Se llegará a una fusión recursiva si ambos arrays tienen un elemento tipo array\n    * y comparten la misma clave.\n    * Para elementos cuyas claves son enteros, los elementos del array final\n    * serán agregados al array anterior.\n    * @param array $a array al que se va a fusionar\n    * @param array $b array desde el cual fusionar. Puedes especificar\n    * arrays adicionales mediante el tercer argumento, cuarto argumento, etc.\n    * @return array el array fusionado (los arrays originales no sufren cambios)\n    */\n    public static function merge($a, $b)\n```\n\n\n## Convirtiendo Objetos a Arrays <span id=\"converting-objects-to-arrays\"></span>\n\nA menudo necesitas convertir un objeto o un array de objetos a un array. El caso más común es convertir los modelos de active record\ncon el fin de servir los arrays de datos vía API REST o utilizarlos de otra manera. El siguiente código se podría utilizar para hacerlo:\n\n```php\n$posts = Post::find()->limit(10)->all();\n$data = ArrayHelper::toArray($posts, [\n    'app\\models\\Post' => [\n        'id',\n        'title',\n        // el nombre de la clave del resultado del array => nombre de la propiedad\n        'createTime' => 'created_at',\n        // el nombre de la clave del resultado del array => función anónima\n        'length' => function ($post) {\n            return strlen($post->content);\n        },\n    ],\n]);\n```\n\nEl primer argumento contiene el dato que queremos convertir. En nuestro caso queremos convertir un modelo AR `Post`.\n\nEl segundo argumento es el mapeo de conversión por clase. Estamos configurando un mapeo para el modelo `Post`.\nCada array de mapeo contiene un conjunto de mapeos. Cada mapeo podría ser:\n\n- Un campo nombre para incluir como está.\n- Un par clave-valor del array deseado con un nombre clave y el nombre de la columna del modelo que tomará el valor.\n- Un par clave-valor del array deseado con un nombre clave y una función anónima que retorne el valor.\n\nEl resultado de la conversión anterior será:\n\n\n```php\n[\n    'id' => 123,\n    'title' => 'test',\n    'createTime' => '2013-01-01 12:00AM',\n    'length' => 301,\n]\n```\n\nEs posible proporcionar una manera predeterminada de convertir un objeto a un array para una clase especifica\nmediante la implementación de la interfaz [[yii\\base\\Arrayable|Arrayable]] en esa clase.\n\n## Haciendo pruebas con Arrays <span id=\"testing-arrays\"></span>\n\nA menudo necesitarás comprobar está en un array o un grupo de elementos es un sub-grupo de otro.\nA pesar de que PHP ofrece `in_array()`, este no soporta sub-grupos u objetos de tipo `\\Traversable`.\n\nPara ayudar en este tipo de pruebas, [[yii\\helpers\\ArrayHelper]] provee [[yii\\helpers\\ArrayHelper::isIn()|isIn()]]\ny [[yii\\helpers\\ArrayHelper::isSubset()|isSubset()]] con la misma firma del método\n[in_array()](https://www.php.net/manual/es/function.in-array.php).\n\n```php\n// true\nArrayHelper::isIn('a', ['a']);\n// true\nArrayHelper::isIn('a', new(ArrayObject['a']));\n\n// true \nArrayHelper::isSubset(new(ArrayObject['a', 'c']), new(ArrayObject['a', 'b', 'c'])\n\n```\n"
  },
  {
    "path": "docs/guide-es/helper-html.md",
    "content": "Clase auxiliar Html (Html helper)\n=================================\n\nTodas las aplicaciones web generan grandes cantidades de marcado HTML (HTML markup). Si el marcado es estático, se\npuede realizar de forma efectiva\n[mezclando PHP y HTML en un mismo archivo](https://www.php.net/manual/es/language.basic-syntax.phpmode.php) pero cuando se\ngeneran dinámicamente empieza a complicarse su gestión sin ayuda extra. Yii ofrece esta ayuda en forma de una clase auxiliar Html\nque proporciona un conjunto de métodos estáticos para gestionar las etiquetas HTML más comúnmente usadas, sus opciones y contenidos.\n\n> Note: Si el marcado es casi estático, es preferible usar HTML directamente. No es necesario encapsularlo todo con\nllamadas a la clase auxiliar Html.\n\n## Lo fundamental <span id=\"basics\"></span>\n\n\nTeniendo en cuenta que la construcción de HTML dinámico mediante la concatenación de cadenas de texto se complica\nrápidamente, Yii proporciona un conjunto de métodos para manipular las opciones de etiquetas y la construcción de las\nmismas basadas en estas opciones.\n\n### Generación de etiquetas <span id=\"generating-tags\"></span>\n\nEl código de generación de etiquetas es similar al siguiente:\n\n```php\n<?= Html::tag('p', Html::encode($user->name), ['class' => 'username']) ?>\n```\n\nEl primer argumento es el nombre de la etiqueta. El segundo es el contenido que se ubicará entre la etiqueta de\napertura y la de cierre. Hay que tener en cuenta que estamos usando `Html::encode`. Esto es debido a que el contenido\nno se codifica automáticamente para permitir usar HTML cuando se necesite. La tercera opción es un array de opciones\nHTML o, en otras palabras, los atributos de las etiquetas. En este array la clave representa el nombre del atributo\ncomo podría ser `class`, `href` o `target` y el valor es su valor.\n\nEl código anterior generará el siguiente HTML:\n\n```html\n<p class=\"username\">samdark</p>\n```\n\nSi se necesita solo la apertura o el cierre de una etiqueta, se pueden usar los métodos `Html::beginTag()` y\n`Html::endTag()`.\n\nLas opciones se usan en muchos métodos de la clase auxiliar Html y en varios widgets. En todos estos casos hay cierta\ngestión adicional que se debe conocer:\n\n- Si un valor es `null`, el correspondiente atributo no se renderizará.\n- Los atributos cuyos valores son de tipo booleano serán tratados como\n  [atributos booleanos](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#boolean-attributes).\n- Los valores de los atributos se codificarán en HTML usando [[yii\\helpers\\Html::encode()|Html::encode()]].\n- El atributo \"data\" puede recibir un array. En este caso, se \"expandirá\" y se renderizará una lista de atributos\n  `data` ej. `'data' => ['id' => 1, 'name' => 'yii']` se convierte en `data-id=\"1\" data-name=\"yii\"`.\n- El atributo \"data\" puede recibir un JSON. Se gestionará de la misma manera que un array ej.\n  `'data' => ['params' => ['id' => 1, 'name' => 'yii'], 'status' => 'ok']` se convierte en\n  `data-params='{\"id\":1,\"name\":\"yii\"}' data-status=\"ok\"`.\n\n### Formación de clases y estilos dinámicamente <span id=\"forming-css\"></span>\n\nCuando se construyen opciones para etiquetas HTML, a menudo nos encontramos con valores predeterminados que hay que\nmodificar. Para añadir o eliminar clases CSS se puede usar el siguiente ejemplo:\n\n```php\n$options = ['class' => 'btn btn-default'];\n\nif ($type === 'success') {\n    Html::removeCssClass($options, 'btn-default');\n    Html::addCssClass($options, 'btn-success');\n}\n\necho Html::tag('div', 'Pwede na', $options);\n\n// cuando $type sea 'success' se renderizará\n// <div class=\"btn btn-success\">Pwede na</div>\n```\n\nPara hacer lo mismo con los estilos para el atributo `style`:\n\n```php\n$options = ['style' => ['width' => '100px', 'height' => '100px']];\n\n// devuelve style=\"width: 100px; height: 200px; position: absolute;\"\nHtml::addCssStyle($options, 'height: 200px; positon: absolute;');\n\n// devuelve style=\"position: absolute;\"\nHtml::removeCssStyle($options, ['width', 'height']);\n```\n\nCuando se usa [[yii\\helpers\\Html::addCssStyle()|addCssStyle()]] se puede especificar si un array de pares clave-valor\ncorresponde a nombres y valores de la propiedad CSS correspondiente o a una cadena de texto como por ejemplo\n`width: 100px; height: 200px;`. Estos formatos se pueden \"hacer\" y \"deshacer\" usando\n[[yii\\helpers\\Html::cssStyleFromArray()|cssStyleFromArray()]] y\n[[yii\\helpers\\Html::cssStyleToArray()|cssStyleToArray()]]. El método\n[[yii\\helpers\\Html::removeCssStyle()|removeCssStyle()]] acepta un array de propiedades que se eliminarán. Si sólo se\neliminara una propiedad, se puede especificar como una cadena de texto.\n\n## Codificación y Decodificación del contenido <span id=\"encoding-and-decoding-content\"></span>\n\n\nPara que el contenido se muestre correctamente y de forma segura con caracteres especiales HTML el contenido debe ser\ncodificado. En PHP esto se hace con [htmlspecialchars](https://www.php.net/manual/es/function.htmlspecialchars.php) y\n[htmlspecialchars_decode](https://www.php.net/manual/es/function.htmlspecialchars-decode.php). El problema con el uso\nde estos métodos directamente es que se tiene que especificar la codificación y opciones extra cada vez. Ya que las\nopciones siempre son las mismas y la codificación debe coincidir con la de la aplicación para prevenir problemas de\nseguridad, Yii proporciona dos métodos simples y compactos:\n\n```php\n$userName = Html::encode($user->name);\necho $userName;\n\n$decodedUserName = Html::decode($userName);\n```\n\n## Formularios <span id=\"forms\"></span>\n\n\nEl trato con el marcado de formularios es una tarea repetitiva y propensa a errores. Por esto hay un grupo de métodos\npara ayudar a gestionarlos.\n\n> Note: hay que considerar la opción de usar [[yii\\widgets\\ActiveForm|ActiveForm]] en caso de que se gestionen\nformularios que requieran validaciones.\n\n### Creando formularios <span id=\"creating-forms\"></span>\n\nSe puede abrir un formulario con el método [[yii\\helpers\\Html::beginForm()|beginForm()]] como se muestra a\ncontinuación:\n\n```php\n<?= Html::beginForm(['order/update', 'id' => $id], 'post', ['enctype' => 'multipart/form-data']) ?>\n```\n\nEl primer argumento es la URL a la que se enviarán los datos del formulario. Se puede especificar en formato de ruta\nde Yii con los parámetros aceptados por [[yii\\helpers\\Url::to()|Url::to()]]. El segundo es el método que se usará.\n`post` es el método predeterminado. El tercero es un array de opciones para la etiqueta `form`. En este caso cambiamos\nel método de codificación del formulario de `data` en una petición POST a `multipart/form-data`. Esto se requiere\ncuando se quieren subir archivos.\n\nEl cierre de la etiqueta `form` es simple:\n\n```php\n<?= Html::endForm() ?>\n```\n\n### Botones <span id=\"buttons\"></span>\n\nPara generar botones se puede usar el siguiente código:\n\n```php\n<?= Html::button('Press me!', ['class' => 'teaser']) ?>\n<?= Html::submitButton('Submit', ['class' => 'submit']) ?>\n<?= Html::resetButton('Reset', ['class' => 'reset']) ?>\n```\n\nEl primer argumento para los tres métodos es el título del botón y el segundo son las opciones. El título no está\ncodificado pero si se usan datos recibidos por el usuario, deben codificarse mediante\n[[yii\\helpers\\Html::encode()|Html::encode()]].\n\n### Inputs <span id=\"input-fields\"></span>\n\nHay dos grupos en los métodos input. Unos empiezan con `active` y se llaman inputs activos y los otros no empiezan\nasí. Los inputs activos obtienen datos del modelo y del atributo especificado y los datos de los inputs normales se\nespecifica directamente.\n\nLos métodos más genéricos son:\n\n```php\ntype, input name, input value, options\n<?= Html::input('text', 'username', $user->name, ['class' => $username]) ?>\n\ntype, model, model attribute name, options\n<?= Html::activeInput('text', $user, 'name', ['class' => $username]) ?>\n```\n\nSi se conoce el tipo de input de antemano, es conveniente usar los atajos de los métodos:\n\n- [[yii\\helpers\\Html::buttonInput()]]\n- [[yii\\helpers\\Html::submitInput()]]\n- [[yii\\helpers\\Html::resetInput()]]\n- [[yii\\helpers\\Html::textInput()]], [[yii\\helpers\\Html::activeTextInput()]]\n- [[yii\\helpers\\Html::hiddenInput()]], [[yii\\helpers\\Html::activeHiddenInput()]]\n- [[yii\\helpers\\Html::passwordInput()]] / [[yii\\helpers\\Html::activePasswordInput()]]\n- [[yii\\helpers\\Html::fileInput()]], [[yii\\helpers\\Html::activeFileInput()]]\n- [[yii\\helpers\\Html::textarea()]], [[yii\\helpers\\Html::activeTextarea()]]\n\nLos botones de opción (Radios) y las casillas de verificación (checkboxes) se especifican de forma un poco diferente:\n\n```php\n<?= Html::radio('agree', true, ['label' => 'I agree']);\n<?= Html::activeRadio($model, 'agree', ['class' => 'agreement'])\n\n<?= Html::checkbox('agree', true, ['label' => 'I agree']);\n<?= Html::activeCheckbox($model, 'agree', ['class' => 'agreement'])\n```\n\nLas listas desplegables (dropdown list) se pueden renderizar como se muestra a continuación:\n\n```php\n<?= Html::dropDownList('list', $currentUserId, ArrayHelper::map($userModels, 'id', 'name')) ?>\n<?= Html::activeDropDownList($users, 'id', ArrayHelper::map($userModels, 'id', 'name')) ?>\n\n<?= Html::listBox('list', $currentUserId, ArrayHelper::map($userModels, 'id', 'name')) ?>\n<?= Html::activeListBox($users, 'id', ArrayHelper::map($userModels, 'id', 'name')) ?>\n```\n\nEl primer argumento es el nombre del input, el segundo es el valor seleccionado actualmente y el tercero es el array\nde pares clave-valor donde la clave es la lista de valores y el valor del array es la lista a mostrar.\n\nSi se quiere habilitar la selección múltiple, se puede usar la lista seleccionable (checkbox list):\n\n```php\n<?= Html::checkboxList('roles', [16, 42], ArrayHelper::map($roleModels, 'id', 'name')) ?>\n<?= Html::activeCheckboxList($user, 'role', ArrayHelper::map($roleModels, 'id', 'name')) ?>\n```\n\nSi no, se puede usar la lista de opciones (radio list):\n\n```php\n<?= Html::radioList('roles', [16, 42], ArrayHelper::map($roleModels, 'id', 'name')) ?>\n<?= Html::activeRadioList($user, 'role', ArrayHelper::map($roleModels, 'id', 'name')) ?>\n```\n\n### Etiquetas y Errores <span id=\"labels-and-errors\"></span>\n\nDe forma parecida que en los inputs hay dos métodos para generar etiquetas. El activo que obtiene los datos del modelo y\nel no-activo que acepta los datos directamente:\n\n```php\n<?= Html::label('User name', 'username', ['class' => 'label username']) ?>\n<?= Html::activeLabel($user, 'username', ['class' => 'label username'])\n```\n\nPara mostrar los errores del formulario de un modelo o modelos a modo de resumen puedes usar:\n\n```php\n<?= Html::errorSummary($posts, ['class' => 'errors']) ?>\n```\n\nPara mostrar un error individual:\n\n```php\n<?= Html::error($post, 'title', ['class' => 'error']) ?>\n```\n\n### Input Names y Values <span id=\"input-names-and-values\"></span>\n\nExisten métodos para obtener names, IDs y values para los campos de entrada (inputs) basados en el modelo. Estos se\nusan principalmente internamente pero a veces pueden resultar prácticos:\n\n```php\n// Post[title]\necho Html::getInputName($post, 'title');\n\n// post-title\necho Html::getInputId($post, 'title');\n\n// mi primer post\necho Html::getAttributeValue($post, 'title');\n\n// $post->authors[0]\necho Html::getAttributeValue($post, '[0]authors[0]');\n```\n\nEn el ejemplo anterior, el primer argumento es el modelo y el segundo es un atributo de expresión. En su forma más\nsimple es su nombre de atributo pero podría ser un nombre de atributo prefijado y/o añadido como sufijo con los\nindices de un array, esto se usa principalmente para mostrar inputs en formatos de tablas:\n\n- `[0]content` se usa en campos de entrada de datos en formato de tablas para representar el atributo \"content\" para\n  el primer modelo del input en formato de tabla;\n- `dates[0]` representa el primer elemento del array del atributo \"dates\";\n- `[0]dates[0]` representa el primer elemento del array del atributo \"dates\" para el primer modelo en formato de tabla.\n\nPara obtener el nombre de atributo sin sufijos o prefijos se puede usar el siguiente código:\n\n```php\n// dates\necho Html::getAttributeName('dates[0]');\n```\n\n## Estilos y scripts <span id=\"styles-and-scripts\"></span>\n\n\nExisten dos métodos para generar etiquetas que envuelvan estilos y scripts incrustados (embebbed):\n\n```php\n<?= Html::style('.danger { color: #f00; }') ?>\n\nGenera\n\n<style>.danger { color: #f00; }</style>\n\n<?= Html::script('alert(\"Hello!\");', ['defer' => true]);\n\nGenera\n\n<script defer>alert(\"Hello!\");</script>\n```\n\nSi se quiere enlazar un estilo externo desde un archivo CSS:\n\n```php\n<?= Html::cssFile('@web/css/ie5.css', ['condition' => 'IE 5']) ?>\n\ngenera\n\n<!--[if IE 5]>\n    <link href=\"https://example.com/css/ie5.css\" />\n<![endif]-->\n```\n\nEl primer argumento es la URL. El segundo es un array de opciones. Adicionalmente, para regular las opciones se puede\nespecificar:\n\n- `condition` para envolver `<link` con los comentarios condicionales con condiciones especificas. Esperamos que sean\n  necesarios los comentarios condicionales ;)\n- `noscript` se puede establecer como `true` para envolver `<link` con la etiqueta `<noscript>` por lo que el sólo se\n  incluirá si el navegador no soporta JavaScript o si lo ha deshabilitado el usuario.\n\nPara enlazar un archivo JavaScript:\n\n```php\n<?= Html::jsFile('@web/js/main.js') ?>\n```\n\nEs igual que con las CSS, el primer argumento especifica el enlace al fichero que se quiere incluir. Las opciones se\npueden pasar como segundo argumento. En las opciones se puede especificar `condition` del mismo modo que se puede usar\npara `cssFile`.\n\n## Enlaces <span id=\"hyperlinks\"></span>\n\n\nExiste un método para generar hipervínculos a conveniencia:\n\n```php\n<?= Html::a('Profile', ['user/view', 'id' => $id], ['class' => 'profile-link']) ?>\n```\n\nEl primer argumento es el título. No está codificado por lo que si se usan datos enviados por el usuario se tienen que\ncodificar usando `Html::encode()`. El segundo argumento es el que se introducirá en `href` de la etiqueta `<a`. Se\npuede consultar [Url::to()](helper-url.md) para obtener más detalles de los valores que acepta. El tercer argumento es\nun array de las propiedades de la etiqueta.\n\nSi se requiere generar enlaces de tipo `mailto` se puede usar el siguiente código:\n\n```php\n<?= Html::mailto('Contact us', 'admin@example.com') ?>\n```\n\n## Imagenes <span id=\"images\"></span>\n\n\nPara generar una etiqueta de tipo imagen se puede usar el siguiente ejemplo:\n\n```php\n<?= Html::img('@web/images/logo.png', ['alt' => 'My logo']) ?>\n\ngenera\n\n<img src=\"https://example.com/images/logo.png\" alt=\"My logo\" />\n```\n\nAparte de los [alias](concept-aliases.md) el primer argumento puede aceptar rutas, parámetros y URLs. Del mismo modo\nque [Url::to()](helper-url.md).\n\n## Listas <span id=\"lists\"></span>\n\n\nLas listas desordenadas se puede generar como se muestra a continuación:\n\n```php\n<?= Html::ul($posts, ['item' => function($item, $index) {\n    return Html::tag(\n        'li',\n        $this->render('post', ['item' => $item]),\n        ['class' => 'post']\n    );\n}]) ?>\n```\n\nPara generar listas ordenadas se puede usar `Html::ol()` en su lugar.\n"
  },
  {
    "path": "docs/guide-es/helper-overview.md",
    "content": "Helpers\n=======\n\n> Note: Esta sección está en desarrollo.\n\nYii ofrece muchas clases que ayudan a simplificar las tareas comunes de codificación, como manipulación de string o array,\ngeneración de código HTML, y más. Estas clases helper están organizadas bajo el namespace `yii\\helpers` y\nson todo clases estáticas (lo que significa que sólo contienen propiedades y métodos estáticos y no deben ser instanciadas).\n\nPuedes usar una clase helper directamente llamando a uno de sus métodos estáticos, como a continuación:\n\n```php\nuse yii\\helpers\\Html;\n\necho Html::encode('Test > test');\n```\n\n> Note: Para soportar la [personalización de clases helper](#customizing-helper-classes), Yii separa cada clase helper del núcleo\n  en dos clases: una clase base (ej. `BaseArrayHelper`) y una clase concreta (ej. `ArrayHelper`).\n  Cuando uses un helper, deberías sólo usar la versión concreta y nunca usar la clase base.\n\n\nClases Helper del núcleo\n------------------------\n\nLas siguientes clases helper del núcleo son proporcionadas en los releases de Yii:\n\n- [ArrayHelper](helper-array.md)\n- Console\n- FileHelper\n- [Html](helper-html.md)\n- HtmlPurifier\n- Image\n- Inflector\n- Json\n- Markdown\n- Security\n- StringHelper\n- [Url](helper-url.md)\n- VarDumper\n\n\nPersonalizando Las Clases Helper <span id=\"customizing-helper-classes\"></span>\n--------------------------------\n\nPara personalizar una clase helper del núcleo (ej. [[yii\\helpers\\ArrayHelper]]), deberías crear una nueva clase extendiendo\nde los helpers correspondientes a la clase base (ej. [[yii\\helpers\\BaseArrayHelper]]), incluyendo su namespace. Esta clase\nserá creada para remplazar la implementación original del framework.\n\nEl siguiente ejemplo muestra como personalizar el método [[yii\\helpers\\ArrayHelper::merge()|merge()]] de la clase\n[[yii\\helpers\\ArrayHelper]]:\n\n```php\n<?php\n\nnamespace yii\\helpers;\n\nclass ArrayHelper extends BaseArrayHelper\n{\n    public static function merge($a, $b)\n    {\n        // tu implementación personalizada\n    }\n}\n```\n\nGuarda tu clase en un fichero nombrado `ArrayHelper.php`. El fichero puede estar en cualquier directorio, por ejemplo `@app/components`.\n\nA continuación, en tu [script de entrada](structure-entry-scripts.md) de la aplicación, añade las siguientes lineas de código\ndespués de incluir el fichero `yii.php` para decirle a la [clase autoloader de Yii](concept-autoloading.md) que cargue tu\nclase personalizada en vez de la clase helper original del framework:\n\n```php\nYii::$classMap['yii\\helpers\\ArrayHelper'] = '@app/components/ArrayHelper.php';\n```\n\nNota que la personalización de clases helper sólo es útil si quieres cambiar el comportamiento de una función\nexistente de los helpers. Si quieres añadir funciones adicionales para usar en tu aplicación puedes mejor crear un helper\npor separado para eso.\n"
  },
  {
    "path": "docs/guide-es/helper-url.md",
    "content": "Clase Auxiliar URL (URL Helper)\n===============================\n\nLa clase auxiliar URL proporciona un conjunto de métodos estáticos para gestionar URLs.\n\n\n## Obtener URLs comúnes <span id=\"getting-common-urls\"></span>\n\nSe pueden usar dos métodos para obtener URLs comunes: URL de inicio (home URL) y URL base (base URL) de la petición\n(request) actual. Para obtener la URL de inicio se puede usar el siguiente código:\n\n```php\n$relativeHomeUrl = Url::home();\n$absoluteHomeUrl = Url::home(true);\n$httpsAbsoluteHomeUrl = Url::home('https');\n```\n\nSi no se pasan parámetros, la URL generada es relativa. Se puede pasar `true`para obtener la URL absoluta del\nesquema actual o especificar el esquema explícitamente (`https`, `http`).\n\nPara obtener la URL base de la petición actual, se puede usar el siguiente código:\n\n```php\n$relativeBaseUrl = Url::base();\n$absoluteBaseUrl = Url::base(true);\n$httpsAbsoluteBaseUrl = Url::base('https');\n```\n\nEl único parámetro del método funciona exactamente igual que para `Url::home()`.\n\n\n## Creación de URLs <span id=\"creating-urls\"></span>\n\nPara crear una URL para una ruta determinada se puede usar `Url::toRoute()`. El método utiliza [[\\yii\\web\\UrlManager]]\npara crear la URL:\n\n```php\n$url = Url::toRoute(['product/view', 'id' => 42]);\n```\n\nSe puede especificar la ruta como una cadena de texto, ej. `site/index`. También se puede usar un array si se\nquieren especificar parámetros para la URL que se esta generando. El formato del array debe ser:\n\n```php\n// genera: /index.php?r=site%2Findex&param1=value1&param2=value2\n['site/index', 'param1' => 'value1', 'param2' => 'value2']\n```\n\nSi se quiere crear una URL con un enlace, se puede usar el formato de array con el parámetro `#`. Por ejemplo,\n\n```php\n// genera: /index.php?r=site/index&param1=value1#name\n['site/index', 'param1' => 'value1', '#' => 'name']\n```\n\nUna ruta puede ser absoluta o relativa. Una ruta absoluta tiene una barra al principio (ej. `/site/index`), mientras que una ruta relativa\nno la tiene (ej. `site/index` o `index`). Una ruta relativa se convertirá en una ruta absoluta siguiendo las siguientes reglas:\n\n- Si la ruta es una cadena vacía, se usará la [[\\yii\\web\\Controller::route|route]] actual;\n- Si la ruta no contiene barras (ej. `index`), se considerará que es el ID de una acción del controlador actual y\n  se antepondrá con [[\\yii\\web\\Controller::uniqueId]];\n- Si la ruta no tiene barra inicial (ej. `site/index`), se considerará que es una ruta relativa del modulo actual y\n  se le antepondrá el [[\\yii\\base\\Module::uniqueId|uniqueId]] del modulo.\n\nDesde la versión 2.0.2, puedes especificar una ruta en términos de [alias](concept-aliases.md). Si este es el caso,\nel alias será convertido primero en la ruta real, la cual será entonces transformada en una ruta absoluta de acuerdo\na las reglas mostradas arriba.\n\nA continuación se muestran varios ejemplos del uso de este método:\n\n```php\n// /index.php?r=site%2Findex\necho Url::toRoute('site/index');\n\n// /index.php?r=site%2Findex&src=ref1#name\necho Url::toRoute(['site/index', 'src' => 'ref1', '#' => 'name']);\n\n// /index.php?r=post%2Fedit&id=100     asume que el alias \"@postEdit\" se definió como \"post/edit\"\necho Url::toRoute(['@postEdit', 'id' => 100]);\n\n// https://www.example.com/index.php?r=site%2Findex\necho Url::toRoute('site/index', true);\n\n// https://www.example.com/index.php?r=site%2Findex\necho Url::toRoute('site/index', 'https');\n```\n\nEl otro método `Url::to()` es muy similar a [[toRoute()]]. La única diferencia es que este método requiere que la ruta\nespecificada sea un array. Si se pasa una cadena de texto, se tratara como una URL.\n\nEl primer argumento puede ser:\n\n- un array: se llamará a [[toRoute()]] para generar la URL. Por ejemplo: `['site/index']`,\n  `['post/index', 'page' => 2]`. Se puede revisar [[toRoute()]] para obtener más detalles acerca de como especificar\n  una ruta.\n- una cadena que empiece por `@`: se tratará como un alias, y se devolverá la cadena correspondiente asociada a este\n  alias.\n- una cadena vacía: se devolverá la URL de la petición actual;\n- una cadena de texto: se devolverá sin alteraciones.\n\nCuando se especifique `$schema` (tanto una cadena de text como `true`), se devolverá una URL con información del host\n(obtenida mediante [[\\yii\\web\\UrlManager::hostInfo]]). Si `$url` ya es una URL absoluta, su esquema se reemplazará con\nel especificado.\n\nA continuación se muestran algunos ejemplos de uso:\n\n```php\n// /index.php?r=site%2Findex\necho Url::to(['site/index']);\n\n// /index.php?r=site%2Findex&src=ref1#name\necho Url::to(['site/index', 'src' => 'ref1', '#' => 'name']);\n\n// /index.php?r=post%2Fedit&id=100     asume que el alias \"@postEdit\" se definió como \"post/edit\"\necho Url::to(['@postEdit', 'id' => 100]);\n\n// the currently requested URL\necho Url::to();\n\n// /images/logo.gif\necho Url::to('@web/images/logo.gif');\n\n// images/logo.gif\necho Url::to('images/logo.gif');\n\n// https://www.example.com/images/logo.gif\necho Url::to('@web/images/logo.gif', true);\n\n// https://www.example.com/images/logo.gif\necho Url::to('@web/images/logo.gif', 'https');\n```\n\nDesde la versión 2.0.3, puedes utilizar [[yii\\helpers\\Url::current()]] para crear una URL a partir de la ruta\nsolicitada y los parámetros GET. Puedes modificar o eliminar algunos de los parámetros GET, o también agregar nuevos\npasando un parámetro `$params` al método. Por ejemplo,\n\n```php\n// asume que $_GET = ['id' => 123, 'src' => 'google'], la ruta actual es \"post/view\"\n\n// /index.php?r=post%2Fview&id=123&src=google\necho Url::current();\n\n// /index.php?r=post%2Fview&id=123\necho Url::current(['src' => null]);\n// /index.php?r=post%2Fview&id=100&src=google\necho Url::current(['id' => 100]);\n```\n\n\n## Recordar URLs <span id=\"remember-urls\"></span>\n\nHay casos en que se necesita recordar la URL y después usarla durante el procesamiento de una de las peticiones\nsecuenciales. Se puede logar de la siguiente manera:\n\n```php\n// Recuerda la URL actual\nUrl::remember();\n\n// Recuerda la URL especificada. Revisar Url::to() para ver formatos de argumentos.\nUrl::remember(['product/view', 'id' => 42]);\n\n// Recuerda la URL especificada con un nombre asignado\nUrl::remember(['product/view', 'id' => 42], 'product');\n```\n\nEn la siguiente petición se puede obtener la URL memorizada de la siguiente manera:\n\n```php\n$url = Url::previous();\n$productUrl = Url::previous('product');\n```\n\n## Chequear URLs relativas <span id=\"checking-relative-urls\"></span>\n\nPara descubrir si una URL es relativa, es decir, que no contenga información del host, se puede utilizar el siguiente código:\n\n```php\n$isRelative = Url::isRelative('test/it');\n```\n"
  },
  {
    "path": "docs/guide-es/images/application-lifecycle.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.13-->\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d0\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key for=\"graphml\" id=\"d7\" yfiles.type=\"resources\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d0\"/>\n    <node id=\"n0\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"571.4472707112631\" width=\"763.2772213171534\" x=\"-1269.9373595143054\" y=\"-207.17439524332679\"/>\n              <y:Fill color=\"#FFCC0024\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FFCC00\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"763.2772213171534\" x=\"0.0\" y=\"0.0\">Entry script (index.php or yii)</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"225.33495140075684\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 4</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n0:\">\n        <node id=\"n0::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"324.9258883570935\" x=\"-1249.511914911339\" y=\"-169.79793039957679\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"126.759765625\" x=\"99.08306136604676\" y=\"5.6494140625\">Load application config<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n0::n1\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d5\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"309.37646484374994\" width=\"330.35133296005984\" x=\"-1254.9373595143054\" y=\"35.272875467936274\"/>\n                  <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"330.35133296005984\" x=\"0.0\" y=\"0.0\">Create application instance</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"19\" bottomF=\"19.15489692687993\" left=\"5\" leftF=\"5.425444602966309\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 5</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n0::n1:\">\n            <node id=\"n0::n1::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"72.64934031168627\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"45.34375\" x=\"124.79106917854676\" y=\"5.6494140625\">preInit()<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n1\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"122.64524027506516\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"120.71875\" x=\"87.10356917854676\" y=\"5.6494140625\">Register error handler<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n2\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"174.96110788981125\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"173.435546875\" x=\"60.74517074104676\" y=\"5.6494140625\">Configure application properties<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n3\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"226.56779181162517\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"27.33203125\" x=\"133.79692855354676\" y=\"5.6494140625\">init()<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n4\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.9258883570935\" x=\"-1234.511914911339\" y=\"280.4944433848063\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.025390625\" x=\"116.45024886604676\" y=\"5.6494140625\">bootstrap()<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n        <node id=\"n0::n2\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d5\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"411.6943410237631\" width=\"324.9258883570935\" x=\"-846.5860265542456\" y=\"-169.79793039957679\"/>\n                  <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"324.9258883570935\" x=\"0.0\" y=\"0.0\">Run application</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"1.1368683772161603E-13\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 3</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n0::n2:\">\n            <node id=\"n0::n2::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.9258883570935\" x=\"-831.5860265542456\" y=\"-132.42146555582667\"/>\n                  <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"164.705078125\" x=\"65.11040511604676\" y=\"5.6494140625\">EVENT_BEFORE_REQUEST<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n2::n1\" yfiles.foldertype=\"group\">\n              <data key=\"d4\"/>\n              <data key=\"d5\"/>\n              <data key=\"d6\">\n                <y:ProxyAutoBoundsNode>\n                  <y:Realizers active=\"0\">\n                    <y:GroupNode>\n                      <y:Geometry height=\"204.37646484375\" width=\"294.9258883570935\" x=\"-831.5860265542456\" y=\"-78.79793039957679\"/>\n                      <y:Fill color=\"#99336635\" transparent=\"false\"/>\n                      <y:BorderStyle hasColor=\"false\" type=\"dashed\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#993366\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#FFFFFF\" visible=\"true\" width=\"294.9258883570935\" x=\"0.0\" y=\"0.0\">Handle request</y:NodeLabel>\n                      <y:Shape type=\"roundrectangle\"/>\n                      <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                      <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                      <y:BorderInsets bottom=\"8\" bottomF=\"7.929194132486941\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                    </y:GroupNode>\n                    <y:GroupNode>\n                      <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                      <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 4</y:NodeLabel>\n                      <y:Shape type=\"roundrectangle\"/>\n                      <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                      <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                      <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                    </y:GroupNode>\n                  </y:Realizers>\n                </y:ProxyAutoBoundsNode>\n              </data>\n              <graph edgedefault=\"directed\" id=\"n0::n2::n1:\">\n                <node id=\"n0::n2::n1::n0\">\n                  <data key=\"d6\">\n                    <y:ShapeNode>\n                      <y:Geometry height=\"30.0\" width=\"264.9258883570935\" x=\"-816.5860265542456\" y=\"-41.421465555826785\"/>\n                      <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"231.4609375\" x=\"16.732475428546763\" y=\"5.6494140625\">Resolve request into route and parameters<y:LabelModel>\n                          <y:SmartNodeLabelModel distance=\"4.0\"/>\n                        </y:LabelModel>\n                        <y:ModelParameter>\n                          <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                        </y:ModelParameter>\n                      </y:NodeLabel>\n                      <y:Shape type=\"rectangle\"/>\n                    </y:ShapeNode>\n                  </data>\n                </node>\n                <node id=\"n0::n2::n1::n1\">\n                  <data key=\"d6\">\n                    <y:ShapeNode>\n                      <y:Geometry height=\"30.0\" width=\"264.9258883570935\" x=\"-816.5860265542456\" y=\"18.578534444173215\"/>\n                      <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"197.44140625\" x=\"33.74224105354676\" y=\"5.6494140625\">Create module, controller and action<y:LabelModel>\n                          <y:SmartNodeLabelModel distance=\"4.0\"/>\n                        </y:LabelModel>\n                        <y:ModelParameter>\n                          <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                        </y:ModelParameter>\n                      </y:NodeLabel>\n                      <y:Shape type=\"rectangle\"/>\n                    </y:ShapeNode>\n                  </data>\n                </node>\n                <node id=\"n0::n2::n1::n2\">\n                  <data key=\"d6\">\n                    <y:ShapeNode>\n                      <y:Geometry height=\"30.0\" width=\"264.9258883570935\" x=\"-816.5860265542456\" y=\"72.64934031168627\"/>\n                      <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.369140625\" x=\"101.77837386604676\" y=\"5.649414062500057\">Run action<y:LabelModel>\n                          <y:SmartNodeLabelModel distance=\"4.0\"/>\n                        </y:LabelModel>\n                        <y:ModelParameter>\n                          <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                        </y:ModelParameter>\n                      </y:NodeLabel>\n                      <y:Shape type=\"rectangle\"/>\n                    </y:ShapeNode>\n                  </data>\n                </node>\n              </graph>\n            </node>\n            <node id=\"n0::n2::n2\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.9258883570935\" x=\"-831.5860265542456\" y=\"149.20206960042316\"/>\n                  <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"154.697265625\" x=\"70.11431136604676\" y=\"5.6494140625\">EVENT_AFTER_REQUEST<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n2::n3\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-831.5860265542456\" y=\"196.89641062418633\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"148.099609375\" x=\"73.41313949104676\" y=\"5.6494140625\">Send response to end user<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n        <node id=\"n0::n3\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"324.9258883570935\" x=\"-846.5860265542456\" y=\"319.2728754679363\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"160.08203125\" x=\"82.42192855354676\" y=\"5.6494140625\">Complete request processing<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <edge id=\"e0\" source=\"n0\" target=\"n0::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-179.83580569315376\" sy=\"-180.3944529152355\" tx=\"-13.869777491410105\" ty=\"-154.8008369539754\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::e0\" source=\"n0::n0\" target=\"n0::n1\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#99CCFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"106.052734375\" x=\"-130.76131968438426\" y=\"78.18481445312506\">Configuration array<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"77.04379339868619\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e0\" source=\"n0::n1::n0\" target=\"n0::n1::n1\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e1\" source=\"n0::n1::n1\" target=\"n0::n1::n2\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e2\" source=\"n0::n1::n2\" target=\"n0::n1::n3\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e3\" source=\"n0::n1::n3\" target=\"n0::n1::n4\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::e1\" source=\"n0::n1\" target=\"n0::n2\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"2.8060680342755404\" sy=\"-48.37646484374994\" tx=\"-162.49660512430125\" ty=\"105.53540293375653\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::e0\" source=\"n0::n2::n0\" target=\"n0::n2::n1\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::n1::e0\" source=\"n0::n2::n1::n0\" target=\"n0::n2::n1::n1\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::n1::e1\" source=\"n0::n2::n1::n1\" target=\"n0::n2::n1::n2\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::e1\" source=\"n0::n2::n1\" target=\"n0::n2::n2\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::e2\" source=\"n0::n2::n2\" target=\"n0::n2::n3\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::e2\" source=\"n0::n2\" target=\"n0::n3\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#99CCFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"59.353515625\" x=\"-93.67673227804266\" y=\"28.318773905436274\">Exit status<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"63.99999999999999\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.47945569632951074\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d7\">\n    <y:Resources/>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-es/images/application-structure.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.13-->\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d0\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key for=\"graphml\" id=\"d7\" yfiles.type=\"resources\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d0\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"56.0\" width=\"100.0\" x=\"872.1807999999999\" y=\"-25.264159999999947\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"45.90625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"83.083984375\" x=\"8.4580078125\" y=\"5.046875\">componente \nde\naplicación<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"133.0\" x=\"685.9223999999999\" y=\"-91.97375999999994\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"107.01953125\" x=\"12.990234375\" y=\"8.515625\">script de entrada<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"-14.764159999999947\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.46875\" x=\"17.765625\" y=\"8.515625\">aplicación<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"62.44544000000004\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"73.10546875\" x=\"13.447265625\" y=\"8.515625000000007\">controlador<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"872.1807999999999\" y=\"62.44544000000005\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"31.873046875\" x=\"34.0634765625\" y=\"8.515625\">filtro<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"532.664\" y=\"23.901600000000087\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"48.9296875\" x=\"25.53515625\" y=\"8.515625\">módulo<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"618.4047999999991\" y=\"139.65504000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"32.74609375\" x=\"33.626953125\" y=\"8.515625\">vista<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"786.161599999999\" y=\"139.65504000000004\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"48.70703125\" x=\"25.646484375\" y=\"8.515625\">modelo<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n8\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"532.664\" y=\"216.86464000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"44.470703125\" x=\"27.7646484375\" y=\"8.515625\">widget<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n9\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"216.86464000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"55.298828125\" x=\"22.3505859375\" y=\"8.515625\">recursos<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n2\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"23.3125\" x=\"2.369325683593729\" y=\"-25.50056340427648\">1:1<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.025600000000054\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.17992697197425173\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n0\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-49.11136368282337\" y=\"-23.00997508216852\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.025599999999994\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5470891790487767\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n5\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"15.0445484043222\" y=\"30.756096771861735\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"44.894496863129426\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.25416574623968574\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n3\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"2.6225397460937074\" y=\"-25.9149367156797\">1..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"15.254400000000032\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.2090245199765919\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n3\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-57.41127147104078\" y=\"-63.734752657990555\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"43.47658308018222\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.881412889692355\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n5\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"582.664\" y=\"-3.598399999999913\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-18.037919628906252\" y=\"-42.33644914245597\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"16.483200000000004\" distanceToCenter=\"true\" position=\"right\" ratio=\"5.822600000000001\" segment=\"-2\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n6\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-12.453120279866653\" y=\"-24.611373055849555\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"11.385360101663515\" distanceToCenter=\"true\" position=\"left\" ratio=\"0.10670293331985262\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e7\" source=\"n7\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-9.3884511336189\" y=\"-25.947659244081734\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.669798038586146\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.1670192242704811\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e8\" source=\"n9\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-9.871202356268668\" y=\"-25.673070518330803\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.15599378957306\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.15481212573620068\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e9\" source=\"n8\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-15.868632344503453\" y=\"-23.891138042957834\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"13.24330808446041\" distanceToCenter=\"true\" position=\"left\" ratio=\"0.05506723556297327\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e10\" source=\"n4\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-47.487035253906356\" y=\"-22.600377199706998\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"13.616000000000007\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e11\" source=\"n9\" target=\"n8\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-44.441396598124356\" y=\"-19.52837428222651\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.543999999999983\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.4117077892835431\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e12\" source=\"n7\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-44.9806249718132\" y=\"-19.528379555664003\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.543999999999983\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.4531592446547025\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d7\">\n    <y:Resources/>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-es/images/rbac-access-check-1.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-es/images/rbac-access-check-2.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-es/images/rbac-access-check-3.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-es/images/rbac-hierarchy-1.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n5\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-es/images/rbac-hierarchy-2.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-es/images/request-lifecycle.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.13-->\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d0\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key for=\"graphml\" id=\"d7\" yfiles.type=\"resources\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d0\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"70.13700103759766\" width=\"56.558998107910156\" x=\"198.38633947503078\" y=\"261.099458694458\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"26.279499053955078\" y=\"74.13700103759766\">\n            <y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"-0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"0.5\" offsetX=\"0.0\" offsetY=\"4.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"48.42578125\" x=\"-52.42578125\" y=\"26.084125518798828\">usuario<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.5\" labelRatioY=\"0.0\" nodeRatioX=\"-0.5\" nodeRatioY=\"0.0\" offsetX=\"-4.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"863.0653619766235\" y=\"637.1271178722382\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"48.70703125\" x=\"35.146484375\" y=\"13.515625\">modelo<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"46.887996673583984\" width=\"39.527000427246094\" x=\"902.8018617630005\" y=\"544.9831195354463\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"88.50390625\" x=\"-24.488452911376953\" y=\"-29.990825653076172\">base de datos<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-12.022075653076172\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"17.763500213623047\" y=\"21.443998336791992\">\n            <y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"863.0653619766235\" y=\"713.6271178722382\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"32.74609375\" x=\"43.126953125\" y=\"13.515625\">vista<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n4\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"376.1217568159104\" width=\"219.27949905395508\" x=\"601.4919853210449\" y=\"408.8421789169311\"/>\n              <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.4609375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"219.27949905395508\" x=\"0.0\" y=\"0.0\">controlador</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"2\" leftF=\"1.7335329055786133\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"412.2765645980835\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.4609375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"65.201171875\" x=\"-7.6005859375\" y=\"0.0\">Folder 1</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n4:\">\n        <node id=\"n4::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"637.2124862670898\" y=\"445.3031164169311\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"77.845703125\" x=\"36.35664749145508\" y=\"6.015625\">crear acción<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n4::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"45.0\" width=\"159.91864204406738\" x=\"632.5326642990112\" y=\"521.8166599273682\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"91.234375\" x=\"34.34213352203369\" y=\"13.515625\">ejecutar filtros<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"diamond\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n4::n2\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"161.79775536060333\" width=\"187.54596614837646\" x=\"618.2255182266235\" y=\"608.1661803722382\"/>\n                  <y:Fill color=\"#99336635\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#993366\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.4609375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#FFFFFF\" visible=\"true\" width=\"187.54596614837646\" x=\"0.0\" y=\"0.0\">acción</y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"4\" bottomF=\"3.8368178606033325\" left=\"4\" leftF=\"3.9869680404663086\" right=\"3\" rightF=\"3.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.4609375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"65.201171875\" x=\"-7.6005859375\" y=\"0.0\">Folder 3</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n4::n2:\">\n            <node id=\"n4::n2::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"637.2124862670898\" y=\"644.6271178722382\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"91.310546875\" x=\"29.624225616455078\" y=\"6.015625\">cargar modelo<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n4::n2::n1\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"637.2124862670898\" y=\"721.1271178722382\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"98.3359375\" x=\"26.111530303955078\" y=\"6.015625\">renderizar vista<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n      </graph>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"96.0\" x=\"189.47636032104495\" y=\"713.6271178722382\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"63.484375\" x=\"16.2578125\" y=\"13.515625\">respuesta<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"863.0653619766235\" y=\"254.50096702575684\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"51.91796875\" x=\"33.541015625\" y=\"13.515625\">petición<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"143.216703414917\" width=\"219.27949905395508\" x=\"601.4919853210449\" y=\"223.78426361083984\"/>\n              <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.4609375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"219.27949905395508\" x=\"0.0\" y=\"0.0\">aplicación</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"21\" leftF=\"20.720500946044922\" right=\"18\" rightF=\"18.0\" top=\"2\" topF=\"1.7557659149169922\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.4609375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"65.201171875\" x=\"-7.6005859375\" y=\"0.0\">Folder 2</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n7:\">\n        <node id=\"n7::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"637.2124862670898\" y=\"262.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"81.07421875\" x=\"34.74238967895508\" y=\"6.015625\">resolver ruta<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n7::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"637.2124862670898\" y=\"322.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"108.12109375\" x=\"21.218952178955078\" y=\"6.015625\">crear controlador<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <node id=\"n8\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"141.4609375\" width=\"267.1120014190674\" x=\"272.201171875\" y=\"225.54002952575684\"/>\n              <y:Fill color=\"#FFCC0024\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FFCC00\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.4609375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"267.1120014190674\" x=\"0.0\" y=\"0.0\">script de entrada</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"2\" leftF=\"1.7358226776123047\" right=\"16\" rightF=\"16.0966796875\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"225.33495140075684\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.4609375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"65.201171875\" x=\"-7.6005859375\" y=\"0.0\">Folder 4</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n8:\">\n        <node id=\"n8::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"219.27949905395508\" x=\"288.9369945526123\" y=\"262.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"211.193359375\" x=\"4.043069839477539\" y=\"6.015625\">cargar configuración de aplicación<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n8::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"129.0\" x=\"334.07674407958984\" y=\"322.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"117.578125\" x=\"5.7109375\" y=\"6.015625\">ejecutar aplicación<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <edge id=\"e0\" source=\"n2\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n5\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-10.81052179205907\" sy=\"-22.46484374999998\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"28.000006009454637\" y=\"-193.20499098300934\">\n            <y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"19.26953125\" x=\"-9.634759615545363\" y=\"-200.18936598300934\">11<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n6\" target=\"n7::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.634765625\" x=\"-30.242425948280925\" y=\"-9.983417510986328\">3<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"0.9990329742431641\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.25395048761528816\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n7::e0\" source=\"n7::n0\" target=\"n7::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n0\" target=\"n8\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"-79.9882439360955\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.634765625\" x=\"17.21815192227234\" y=\"-8.9605422404872\">1<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n7::n1\" target=\"n4\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"2.408647025755731\" ty=\"-181.21658369302781\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.634765625\" x=\"-5.4386998761101495\" y=\"19.43622140884395\">4<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n3\" target=\"n4::n2::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.634765625\" x=\"-30.80765317377586\" y=\"-8.98439335823059\">9<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.26448415477215953\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n4::n2::n1\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"-2.2737367544323206E-13\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"19.26953125\" x=\"-114.09832302464997\" y=\"-8.984393358230705\">10<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.2786124840137136\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e7\" source=\"n8::n1\" target=\"n7\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"55.750299312644984\" sy=\"0.355224609375\" tx=\"-109.63961141576266\" ty=\"42.066115379333496\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.634765625\" x=\"63.40921211242676\" y=\"-8.936139821377537\">2<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e8\" source=\"n1\" target=\"n4::n2::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.634765625\" x=\"-31.95652327480775\" y=\"-9.98439335823059\">8<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"1.0\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.2858946861565087\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e9\" source=\"n4::n1\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-79.9882439360951\" sy=\"2.2737367544323206E-13\" tx=\"24.24112858184396\" ty=\"-1.4999999999999991\">\n            <y:Point x=\"261.71748890288893\" y=\"544.3166599273684\"/>\n          </y:Path>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.634765625\" x=\"-101.0586648485413\" y=\"-8.984365463256609\">6<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.23459266172695797\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::e0\" source=\"n4::n0\" target=\"n4::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFEFD6\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.634765625\" x=\"-5.817401885986328\" y=\"4.999985313415493\">5<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.0\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::e1\" source=\"n4::n1\" target=\"n4::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"-81.01828954355172\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFEFD6\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.634765625\" x=\"-5.926145785867902\" y=\"5.063791275024414\">7<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.0\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::n2::e0\" source=\"n4::n2::n0\" target=\"n4::n2::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n8::e0\" source=\"n8::n0\" target=\"n8::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d7\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"66px\" viewBox=\"0 0 57 66\" enable-background=\"new 0 0 57 66\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3799\" y1=\"-2276.8809\" x2=\"27.6209\" y2=\"-2306.6792\" gradientTransform=\"matrix(1 0 0 -1 0.2803 -2252.9199)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_13_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t&lt;path fill=\"#2068A3\" stroke=\"#2068A3\" d=\"M28.106,33.487c-8.112,0-12.688,4.312-12.688,10.437c0,7.422,12.688,10.438,12.688,10.438\n\t\ts14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.487,28.106,33.487z M26.288,53.051c0,0-7.135-2.093-8.805-7.201\n\t\tc-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"14.2417\" cy=\"9.1006\" r=\"53.247\" gradientTransform=\"matrix(1 0 0 -1 0.04 65.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#74AEEE\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#2068A3\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#2068A3\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.022,7.807-14.022,7.807s-10.472-2.484-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path fill=\"#5491CF\" stroke=\"#2068A3\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397c-0.514,1.027-1.669,4.084-1.669,5.148\n\t\tc0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.335-2.36c-3.601-1.419-4.071-3.063-5.89-4.854\n\t\tC12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t&lt;path fill=\"#5491CF\" stroke=\"#2068A3\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617c0.516,1.025,3.617,3.693,3.617,6.617\n\t\tc0,5.186-10.27,8.576-16.698,9.145c1.429,4.938,11.372,1.293,13.804-0.313c3.563-2.354,4.563-5.133,7.854-3.705\n\t\tC47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t&lt;path fill=\"none\" stroke=\"#2068A3\" stroke-linecap=\"round\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t&lt;path fill=\"none\" stroke=\"#2068A3\" stroke-linecap=\"round\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.623\" cy=\"-2278.646\" r=\"23.425\" fx=\"23.0534\" fy=\"-2281.1357\" gradientTransform=\"matrix(1 0 0 -1 0.2803 -2252.9199)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"5761.7578\" y1=\"11330.6484\" x2=\"5785.3872\" y2=\"11424.0977\" gradientTransform=\"matrix(0.275 0 0 0.2733 -1558.9874 -3088.4209)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M27.958,6.333c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.083,13.952,36.271,6.268,27.958,6.333z\"/&gt;\n\t&lt;path id=\"Hair_Young_Brown_1_\" fill=\"#CC9869\" stroke=\"#99724F\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n\t&lt;path fill=\"#4B4B4B\" stroke=\"#4B4B4B\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" d=\"M28.105,2\n\t\tC22.464,2,20.2,4.246,18.13,5.533C29.753,2.865,41.152,10.375,44.46,20.5C44.459,16.875,44.459,2,28.105,2z\"/&gt;\n\t&lt;path fill=\"#9B9B9B\" stroke=\"#4B4B4B\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" d=\"M11.151,17.751\n\t\tC12.878,8.25,18.686,6.309,25.273,7.127C31.295,7.875,36.93,10.491,44.459,20.5C37.777,7.125,20.278-3.375,9.903,3.921\n\t\tC5.569,6.97,4.903,13.375,11.151,17.751z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\"\n\t xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\t x=\"0px\" y=\"0px\" width=\"41px\" height=\"48px\" viewBox=\"-0.875 -0.887 41 48\" enable-background=\"new -0.875 -0.887 41 48\"\n\t xml:space=\"preserve\"&gt;\n&lt;defs&gt;\n&lt;/defs&gt;\n&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-979.1445\" x2=\"682.0508\" y2=\"-979.1445\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_1_)\" d=\"M19.625,36.763C8.787,36.763,0,34.888,0,32.575v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,34.888,30.464,36.763,19.625,36.763z\"/&gt;\n&lt;linearGradient id=\"SVGID_2_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-973.1445\" x2=\"682.0508\" y2=\"-973.1445\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_2_)\" d=\"M19.625,36.763c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.927-18.396,3.927\n\tc-9.481,0-17.396-1.959-18.396-3.927l-1.229,2C0,34.888,8.787,36.763,19.625,36.763z\"/&gt;\n&lt;path fill=\"#3C89C9\" d=\"M19.625,26.468c10.16,0,19.625,2.775,19.625,2.775c-0.375,2.721-5.367,5.438-19.554,5.438\n\tc-12.125,0-18.467-2.484-19.541-4.918C-0.127,29.125,9.465,26.468,19.625,26.468z\"/&gt;\n&lt;linearGradient id=\"SVGID_3_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-965.6948\" x2=\"682.0508\" y2=\"-965.6948\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_3_)\" d=\"M19.625,23.313C8.787,23.313,0,21.438,0,19.125v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,21.438,30.464,23.313,19.625,23.313z\"/&gt;\n&lt;linearGradient id=\"SVGID_4_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-959.6948\" x2=\"682.0508\" y2=\"-959.6948\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_4_)\" d=\"M19.625,23.313c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.926-18.396,3.926\n\tc-9.481,0-17.396-1.959-18.396-3.926l-1.229,2C0,21.438,8.787,23.313,19.625,23.313z\"/&gt;\n&lt;path fill=\"#3C89C9\" d=\"M19.476,13.019c10.161,0,19.625,2.775,19.625,2.775c-0.375,2.721-5.367,5.438-19.555,5.438\n\tc-12.125,0-18.467-2.485-19.541-4.918C-0.277,15.674,9.316,13.019,19.476,13.019z\"/&gt;\n&lt;linearGradient id=\"SVGID_5_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-952.4946\" x2=\"682.0508\" y2=\"-952.4946\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_5_)\" d=\"M19.625,10.113C8.787,10.113,0,8.238,0,5.925v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,8.238,30.464,10.113,19.625,10.113z\"/&gt;\n&lt;linearGradient id=\"SVGID_6_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-946.4946\" x2=\"682.0508\" y2=\"-946.4946\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_6_)\" d=\"M19.625,10.113c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.926-18.396,3.926\n\tc-9.481,0-17.396-1.959-18.396-3.926L0,5.925C0,8.238,8.787,10.113,19.625,10.113z\"/&gt;\n&lt;linearGradient id=\"SVGID_7_\" gradientUnits=\"userSpaceOnUse\" x1=\"644.0293\" y1=\"-943.4014\" x2=\"680.8223\" y2=\"-943.4014\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;ellipse fill=\"url(#SVGID_7_)\" cx=\"19.625\" cy=\"3.926\" rx=\"18.396\" ry=\"3.926\"/&gt;\n&lt;path opacity=\"0.24\" fill=\"#FFFFFF\" enable-background=\"new    \" d=\"M31.04,45.982c0,0-4.354,0.664-7.29,0.781\n\tc-3.125,0.125-8.952,0-8.952,0l-2.384-10.292l0.044-2.108l-1.251-1.154L9.789,23.024l-0.082-0.119L9.5,20.529l-1.65-1.254\n\tL5.329,8.793c0,0,4.213,0.903,7.234,1.07s8.375,0.25,8.375,0.25l3,9.875l-0.25,1.313l1.063,2.168l2.312,9.645l-0.521,1.416\n\tl1.46,1.834L31.04,45.982z\"/&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-es/input-file-upload.md",
    "content": "Subir Archivos\n==============\n\nSubir archivos en Yii es normalmente realizado con la ayuda de [[yii\\web\\UploadedFile]], que encapsula cada archivo subido\nen un objeto `UploadedFile`. Combinado con [[yii\\widgets\\ActiveForm]] y [modelos](structure-models.md),\npuedes fácilmente implementar un mecanismo seguro de subida de archivos.\n\n\n## Crear Modelos <span id=\"creating-models\"></span>\n\nAl igual que al trabajar con entradas de texto plano, para subir un archivo debes crear una clase de modelo y utilizar un atributo \nde dicho modelo para mantener la instancia del archivo subido. Debes también declarar una regla para validar la subida del archivo.\nPor ejemplo,\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\nuse yii\\web\\UploadedFile;\n\nclass UploadForm extends Model\n{\n    /**\n     * @var UploadedFile\n     */\n    public $imageFile;\n\n    public function rules()\n    {\n        return [\n            [['imageFile'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg'],\n        ];\n    }\n    \n    public function upload()\n    {\n        if ($this->validate()) {\n            $this->imageFile->saveAs('uploads/' . $this->imageFile->baseName . '.' . $this->imageFile->extension);\n            return true;\n        } else {\n            return false;\n        }\n    }\n}\n```\n\nEn el código anterior, el atributo `imageFile` es utilizado para mantener una instancia del archivo subido. Este está asociado con\nuna regla de validación `file`, que utiliza [[yii\\validators\\FileValidator]] para asegurarse que el archivo a subir tenga extensión `png` o `jpg`.\nEl método `upload()` realizará la validación y guardará el archivo subido en el servidor.\n\nEl validador `file` te permite chequear las extensiones, el tamaño, el tipo MIME, etc. Por favor consulta\nla sección [Validadores del Framework](tutorial-core-validators.md#file) para más detalles.\n\n> Tip: Si estás subiendo una imagen, podrías considerar el utilizar el validador `image`. El validador `image` es\n  implementado a través de [[yii\\validators\\ImageValidator]], que verifica que un atributo haya recibido una imagen válida \n  que pueda ser tanto guardada como procesada utilizando la [Extensión Imagine](https://github.com/yiisoft/yii2-imagine).\n\n\n## Renderizar Campos de Subida de Archivos <span id=\"rendering-file-input\"></span>\n\nA continuación, crea un campo de subida de archivo en la vista:\n\n```php\n<?php\nuse yii\\widgets\\ActiveForm;\n?>\n\n<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?>\n\n    <?= $form->field($model, 'imageFile')->fileInput() ?>\n\n    <button>Enviar</button>\n\n<?php ActiveForm::end() ?>\n```\n\nEs importante recordad que agregues la opción `enctype` al formulario para que el archivo pueda ser subido apropiadamente.\nLa llamada a `fileInput()` renderizará un tag `<input type=\"file\">` que le permitirá al usuario seleccionar el archivo a subir.\n\n> Tip: desde la versión 2.0.8, [[yii\\widgets\\ActiveField::fileInput|fileInput]] agrega la opción `enctype` al formulario\n  automáticamente cuando se utiliza una campo de subida de archivo.\n\n## Uniendo Todo <span id=\"wiring-up\"></span>\n\nAhora, en una acción del controlador, escribe el código que una el modelo y la vista para implementar la subida de archivos:\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\nuse app\\models\\UploadForm;\nuse yii\\web\\UploadedFile;\n\nclass SiteController extends Controller\n{\n    public function actionUpload()\n    {\n        $model = new UploadForm();\n\n        if (Yii::$app->request->isPost) {\n            $model->imageFile = UploadedFile::getInstance($model, 'imageFile');\n            if ($model->upload()) {\n                // el archivo se subió exitosamente\n                return;\n            }\n        }\n\n        return $this->render('upload', ['model' => $model]);\n    }\n}\n```\n\nEn el código anterior, cuando se envía el formulario, el método [[yii\\web\\UploadedFile::getInstance()]] es llamado\npara representar el archivo subido como una instancia de `UploadedFile`. Entonces dependemos de la validación del modelo\npara asegurarnos que el archivo subido es válido y entonces subirlo al servidor.\n\n\n## Uploading Multiple Files <span id=\"uploading-multiple-files\"></span>\n\nTambién puedes subir varios archivos a la vez, con algunos ajustes en el código de las subsecciones previas.\n\nPrimero debes ajustar la clase del modelo, agregando la opción `maxFiles` en la regla de validación `file` para limitar\nel número máximo de archivos a subir. Definir `maxFiles` como `0` significa que no hay límite en el número de archivos\na subir simultáneamente. El número máximo de archivos permitidos para subir simultáneamente está también limitado\npor la directiva PHP [`max_file_uploads`](https://www.php.net/manual/es/ini.core.php#ini.max-file-uploads),\ncuyo valor por defecto es 20. El método `upload()` debería también ser modificado para guardar los archivos uno a uno.\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\nuse yii\\web\\UploadedFile;\n\nclass UploadForm extends Model\n{\n    /**\n     * @var UploadedFile[]\n     */\n    public $imageFiles;\n\n    public function rules()\n    {\n        return [\n            [['imageFiles'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg', 'maxFiles' => 4],\n        ];\n    }\n    \n    public function upload()\n    {\n        if ($this->validate()) { \n            foreach ($this->imageFiles as $file) {\n                $file->saveAs('uploads/' . $file->baseName . '.' . $file->extension);\n            }\n            return true;\n        } else {\n            return false;\n        }\n    }\n}\n```\n\nEn el archivo de la vista, debes agregar la opción `multiple` en la llamada a `fileInput()` de manera que el campo\npueda recibir varios archivos:\n \n```php\n<?php\nuse yii\\widgets\\ActiveForm;\n?>\n\n<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?>\n\n    <?= $form->field($model, 'imageFiles[]')->fileInput(['multiple' => true, 'accept' => 'image/*']) ?>\n\n    <button>Enviar</button>\n\n<?php ActiveForm::end() ?>\n```\n\nY finalmente en la acción del controlador, debes llamar `UploadedFile::getInstances()` en vez de\n`UploadedFile::getInstance()` para asignar un array de instancias `UploadedFile` a `UploadForm::imageFiles`. \n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\nuse app\\models\\UploadForm;\nuse yii\\web\\UploadedFile;\n\nclass SiteController extends Controller\n{\n    public function actionUpload()\n    {\n        $model = new UploadForm();\n\n        if (Yii::$app->request->isPost) {\n            $model->imageFiles = UploadedFile::getInstances($model, 'imageFiles');\n            if ($model->upload()) {\n                // el archivo fue subido exitosamente\n                return;\n            }\n        }\n\n        return $this->render('upload', ['model' => $model]);\n    }\n}\n```\n"
  },
  {
    "path": "docs/guide-es/input-multiple-models.md",
    "content": "Obtención de datos para los modelos de múltiples\n================================\n\nCuando se trata de algunos datos complejos, es posible que puede que tenga que utilizar varios modelos diferentes para recopilar\nla entrada del usuario. Por ejemplo, suponiendo que la información de inicio de sesión del usuario se almacena en la tabla `user`, \nmientras que el perfil de usuario la información se almacena en la tabla `Profile`, es posible que desee para recoger los datos\nde entrada sobre un usuario a través de un modelo `User` y un modelo `Profile`. Con el modelo de Yii y apoyo formulario, \npuede solucionar este problema de una manera que no es mucho diferente de la manipulación de un solo modelo.\n\nEn lo que sigue, vamos a mostrar cómo se puede crear un formulario que permitirá recoger datos tanto para los modelos `User` y \n`Profile`.\n\nEn primer lugar, la acción del controlador para la recogida de los datos del usuario y del perfil se puede escribir de la \nsiguiente manera,\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\base\\Model;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\nuse app\\models\\User;\nuse app\\models\\Profile;\n\nclass UserController extends Controller\n{\n    public function actionUpdate($id)\n    {\n        $user = User::findOne($id);\n        if (!$user) {\n            throw new NotFoundHttpException(\"The user was not found.\");\n        }\n        \n        $profile = Profile::findOne($user->profile_id);\n        \n        if (!$profile) {\n            throw new NotFoundHttpException(\"The user has no profile.\");\n        }\n        \n        $user->scenario = 'update';\n        $profile->scenario = 'update';\n        \n        if ($user->load(Yii::$app->request->post()) && $profile->load(Yii::$app->request->post())) {\n            $isValid = $user->validate();\n            $isValid = $profile->validate() && $isValid;\n            if ($isValid) {\n                $user->save(false);\n                $profile->save(false);\n                return $this->redirect(['user/view', 'id' => $id]);\n            }\n        }\n        \n        return $this->render('update', [\n            'user' => $user,\n            'profile' => $profile,\n        ]);\n    }\n}\n```\n\nEn la acción `update`, primero cargamos los modelos `User` y `Profile` que se actualicen desde la base de datos. Luego llamamos\n[[yii\\base\\Model::load()]] para llenar estos dos modelos con la entrada del usuario. Si tiene éxito, se validará\nlos dos modelos y guardarlos. De lo contrario vamos a renderizar la vista `update` que tiene el siguiente contenido:\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n$form = ActiveForm::begin([\n    'id' => 'user-update-form',\n    'options' => ['class' => 'form-horizontal'],\n]) ?>\n    <?= $form->field($user, 'username') ?>\n\n    ...other input fields...\n    \n    <?= $form->field($profile, 'website') ?>\n\n    <?= Html::submitButton('Update', ['class' => 'btn btn-primary']) ?>\n<?php ActiveForm::end() ?>\n```\n\nComo se puede ver, en el `update` vista que haría que los campos de entrada utilizando dos modelos `User` y `Profile`.\n"
  },
  {
    "path": "docs/guide-es/input-validation.md",
    "content": "Validación de Entrada\n=====================\n\nComo regla básica, nunca debes confiar en los datos recibidos de un usuario final y deberías validarlo siempre\nantes de ponerlo en uso.\n\nDado un [modelo](structure-models.md) poblado con entradas de usuarios, puedes validar esas entradas llamando al\nmétodo [[yii\\base\\Model::validate()]]. Dicho método devolverá un valor booleano indicando si la validación\ntuvo éxito o no. En caso de que no, puedes obtener los mensajes de error de la propiedad [[yii\\base\\Model::errors]]. Por ejemplo,\n\n```php\n$model = new \\app\\models\\ContactForm();\n\n// poblar los atributos del modelo desde la entrada del usuario\n$model->load(\\Yii::$app->request->post());\n// lo que es equivalente a:\n// $model->attributes = \\Yii::$app->request->post('ContactForm');\n\nif ($model->validate()) {\n    // toda la entrada es válida\n} else {\n    // la validación falló: $errors es un array que contienen los mensajes de error\n    $errors = $model->errors;\n}\n```\n\n\n## Declarar Reglas <span id=\"declaring-rules\"></span>\n\nPara hacer que `validate()` realmente funcione, debes declarar reglas de validación para los atributos que planeas validar.\nEsto debería hacerse sobrescribiendo el método [[yii\\base\\Model::rules()]]. El siguiente ejemplo muestra cómo\nson declaradas las reglas de validación para el modelo `ContactForm`:\n\n```php\npublic function rules()\n{\n    return [\n        // los atributos name, email, subject y body son obligatorios\n        [['name', 'email', 'subject', 'body'], 'required'],\n\n        // el atributo email debe ser una dirección de email válida\n        ['email', 'email'],\n    ];\n}\n```\n\nEl método [[yii\\base\\Model::rules()|rules()]] debe devolver un array de reglas, la cual cada una\ntiene el siguiente formato:\n\n```php\n[\n    // requerido, especifica qué atributos deben ser validados por esta regla.\n    // Para un sólo atributo, puedes utilizar su nombre directamente\n    // sin tenerlo dentro de un array\n    ['attribute1', 'attribute2', ...],\n\n    // requerido, especifica de qué tipo es la regla.\n    // Puede ser un nombre de clase, un alias de validador, o el nombre de un método de validación\n    'validator',\n\n    // opcional, especifica en qué escenario/s esta regla debe aplicarse\n    // si no se especifica, significa que la regla se aplica en todos los escenarios\n    // Puedes también configurar la opción \"except\" en caso de que quieras aplicar la regla\n    // en todos los escenarios salvo los listados\n    'on' => ['scenario1', 'scenario2', ...],\n\n    // opcional, especifica atributos adicionales para el objeto validador\n    'property1' => 'value1', 'property2' => 'value2', ...\n]\n```\n\nPor cada regla debes especificar al menos a cuáles atributos aplica la regla y cuál es el tipo de la regla.\nPuedes especificar el tipo de regla de las siguientes maneras:\n\n* el alias de un validador propio del framework, tal como `required`, `in`, `date`, etc. Por favor consulta\n  [Validadores del núcleo](tutorial-core-validators.md) para la lista completa de todos los validadores incluidos.\n* el nombre de un método de validación en la clase del modelo, o una función anónima. Consulta la\n  subsección [Validadores en Línea](#inline-validators) para más detalles.\n* el nombre completo de una clase de validador. Por favor consulta la subsección [Validadores Independientes](#standalone-validators)\n  para más detalles.\n\nUna regla puede ser utilizada para validar uno o varios atributos, y un atributo puede ser validado por una o varias reglas.\nUna regla puede ser aplicada en ciertos [escenarios](structure-models.md#scenarios) con tan sólo especificando la opción `on`.\nSi no especificas una opción `on`, significa que la regla se aplicará en todos los escenarios.\n\nCuando el método `validate()` es llamado, este sigue los siguientes pasos para realiza la validación:\n\n1. Determina cuáles atributos deberían ser validados obteniendo la lista de atributos de [[yii\\base\\Model::scenarios()]]\n   utilizando el [[yii\\base\\Model::scenario|scenario]] actual. Estos atributos son llamados *atributos activos*.\n2. Determina cuáles reglas de validación deberían ser validados obteniendo la lista de reglas de [[yii\\base\\Model::rules()]]\n   utilizando el [[yii\\base\\Model::scenario|scenario]] actual. Estas reglas son llamadas *reglas activas*.\n3. Utiliza cada regla activa para validar cada atributo activo que esté asociado a la regla.\n   Las reglas de validación son evaluadas en el orden en que están listadas.\n\nDe acuerdo a los pasos de validación mostrados arriba, un atributo será validado si y sólo si\nes un atributo activo declarado en `scenarios()` y está asociado a una o varias reglas activas\ndeclaradas en `rules()`.\n\n> Note: Es práctico darle nombre a las reglas, por ej:\n>\n> ```php\n> public function rules()\n> {\n>     return [\n>         // ...\n>         'password' => [['password'], 'string', 'max' => 60],\n>     ];\n> }\n> ```\n>\n> Puedes utilizarlas en una subclase del modelo:\n>\n> ```php\n> public function rules()\n> {\n>     $rules = parent::rules();\n>     unset($rules['password']);\n>     return $rules;\n> }\n\n\n### Personalizar Mensajes de Error <span id=\"customizing-error-messages\"></span>\n\nLa mayoría de los validadores tienen mensajes de error por defecto que serán agregados al modelo siendo validado cuando sus atributos\nfallan la validación. Por ejemplo, el validador [[yii\\validators\\RequiredValidator|required]] agregará\nel mensaje \"Username no puede estar vacío.\" a un modelo cuando falla la validación del atributo `username` al utilizar esta regla.\n\nPuedes especificar el mensaje de error de una regla especificado la propiedad `message` al declarar la regla,\ncomo a continuación,\n\n```php\npublic function rules()\n{\n    return [\n        ['username', 'required', 'message' => 'Por favor escoge un nombre de usuario.'],\n    ];\n}\n```\n\nAlgunos validadores pueden soportar mensajes de error adicionales para describir más precisamente las causas\ndel fallo de validación. Por ejemplo, el validador [[yii\\validators\\NumberValidator|number]] soporta\n[[yii\\validators\\NumberValidator::tooBig|tooBig]] y [[yii\\validators\\NumberValidator::tooSmall|tooSmall]]\npara describir si el fallo de validación es porque el valor siendo validado es demasiado grande o demasiado pequeño, respectivamente.\nPuedes configurar estos mensajes de error tal como cualquier otroa propiedad del validador en una regla de validación.\n\n\n### Eventos de Validación <span id=\"validation-events\"></span>\n\nCuando el método [[yii\\base\\Model::validate()]] es llamado, este llamará a dos métodos que puedes sobrescribir para personalizar\nel proceso de validación:\n\n* [[yii\\base\\Model::beforeValidate()]]: la implementación por defecto lanzará un evento [[yii\\base\\Model::EVENT_BEFORE_VALIDATE]].\n  Puedes tanto sobrescribir este método o responder a este evento para realizar algún trabajo de pre procesamiento\n  (por ej. normalizar datos de entrada) antes de que ocurra la validación en sí. El método debe devolver un booleano que indique\n  si la validación debe continuar o no.\n* [[yii\\base\\Model::afterValidate()]]: la implementación por defecto lanzará un evento [[yii\\base\\Model::EVENT_AFTER_VALIDATE]].\n  uedes tanto sobrescribir este método o responder a este evento para realizar algún trabajo de post procesamiento después\n  de completada la validación.\n\n\n### Validación Condicional <span id=\"conditional-validation\"></span>\n\nPara validar atributos sólo en determinadas condiciones, por ej. la validación de un atributo depende\ndel valor de otro atributo puedes utilizar la propiedad [[yii\\validators\\Validator::when|when]]\npara definir la condición. Por ejemplo,\n\n```php\n    ['state', 'required', 'when' => function($model) {\n        return $model->country == 'USA';\n    }]\n```\n\nLa propiedad [[yii\\validators\\Validator::when|when]] toma un método invocable PHP con la siguiente firma:\n\n```php\n/**\n * @param Model $model el modelo siendo validado\n * @param string $attribute al atributo siendo validado\n * @return bool si la regla debe ser aplicada o no\n */\nfunction ($model, $attribute)\n```\n\nSi también necesitas soportar validación condicional del lado del cliente, debes configurar\nla propiedad [[yii\\validators\\Validator::whenClient|whenClient]], que toma un string que representa una función JavaScript\ncuyo valor de retorno determina si debe aplicarse la regla o no. Por ejemplo,\n\n```php\n    ['state', 'required', 'when' => function ($model) {\n        return $model->country == 'USA';\n    }, 'whenClient' => \"function (attribute, value) {\n        return $('#country').val() == 'USA';\n    }\"]\n```\n\n\n### Filtro de Datos <span id=\"data-filtering\"></span>\n\nLa entrada del usuario a menudo debe ser filtrada o pre procesada. Por ejemplo, podrías querer eliminar los espacions alrededor\nde la entrada `username`. Puedes utilizar reglas de validación para lograrlo.\n\nLos siguientes ejemplos muestran cómo eliminar esos espacios en la entrada y cómo transformar entradas vacías en `null` utilizando\nlos validadores del framework [trim](tutorial-core-validators.md#trim) y [default](tutorial-core-validators.md#default):\n\n```php\nreturn [\n    [['username', 'email'], 'trim'],\n    [['username', 'email'], 'default'],\n];\n```\n\nTambién puedes utilizar el validador más general [filter](tutorial-core-validators.md#filter) para realizar filtros\nde datos más complejos.\n\nComo puedes ver, estas reglas de validación no validan la entrada realmente. En cambio, procesan los valores\ny los guardan en el atributo siendo validado.\n\n\n### Manejando Entradas Vacías <span id=\"handling-empty-inputs\"></span>\n\nCuando los datos de entrada son enviados desde formularios HTML, a menudo necesitas asignar algunos valores por defecto a las entradas\nsi estas están vacías. Puedes hacerlo utilizando el validador [default](tutorial-core-validators.md#default). Por ejemplo,\n\n```php\nreturn [\n    // convierte \"username\" y \"email\" en `null` si estos están vacíos\n    [['username', 'email'], 'default'],\n\n    // convierte \"level\" a 1 si está vacío\n    ['level', 'default', 'value' => 1],\n];\n```\n\nPor defecto, una entrada se considera vacía si su valor es un string vacío, un array vacío o `null`.\nPuedes personalizar la lógica de detección de valores vacíos configurando la propiedad [[yii\\validators\\Validator::isEmpty]]\ncon una función PHP invocable. Por ejemplo,\n\n```php\n    ['agree', 'required', 'isEmpty' => function ($value) {\n        return empty($value);\n    }]\n```\n\n> Note: La mayoría de los validadores no manejan entradas vacías si su propiedad [[yii\\validators\\Validator::skipOnEmpty]] toma\n  el valor por defecto `true`. Estas serán simplemente salteadas durante la validación si sus atributos asociados reciben una entrada vacía.\n  Entre los [validadores del framework](tutorial-core-validators.md), sólo `captcha`, `default`, `filter`,\n  `required`, y `trim` manejarán entradas vacías.\n\n\n## Validación Ad Hoc <span id=\"ad-hoc-validation\"></span>\n\nA veces necesitas realizar *validación ad hoc* para valores que no están ligados a ningún modelo.\n\nSi sólo necesitas realizar un tipo de validación (por ej: validar direcciones de email), podrías llamar\nal método [[yii\\validators\\Validator::validate()|validate()]] de los validadores deseados, como a continuación:\n\n```php\n$email = 'test@example.com';\n$validator = new yii\\validators\\EmailValidator();\n\nif ($validator->validate($email, $error)) {\n    echo 'Email válido.';\n} else {\n    echo $error;\n}\n```\n\n> Note: No todos los validadores soportan este tipo de validación. Un ejemplo es el validador del framework [unique](tutorial-core-validators.md#unique),\n  que está diseñado para trabajar sólo con un modelo.\n\nSi necesitas realizar varias validaciones contro varios valores, puedes utilizar [[yii\\base\\DynamicModel]],\nque soporta declarar tanto los atributos como las reglas sobre la marcha. Su uso es como a continuación:\n\n```php\npublic function actionSearch($name, $email)\n{\n    $model = DynamicModel::validateData(compact('name', 'email'), [\n        [['name', 'email'], 'string', 'max' => 128],\n        ['email', 'email'],\n    ]);\n\n    if ($model->hasErrors()) {\n        // validación fallida\n    } else {\n        // validación exitosa\n    }\n}\n```\n\nEl método [[yii\\base\\DynamicModel::validateData()]] crea una instancia de `DynamicModel`, define los atributos\nutilizando los datos provistos (`name` e `email` en este ejemplo), y entonces llama a [[yii\\base\\Model::validate()]]\ncon las reglas provistas.\n\nAlternativamente, puedes utilizar la sintaxis más \"clásica\" para realizar la validación ad hoc:\n\n```php\npublic function actionSearch($name, $email)\n{\n    $model = new DynamicModel(compact('name', 'email'));\n    $model->addRule(['name', 'email'], 'string', ['max' => 128])\n        ->addRule('email', 'email')\n        ->validate();\n\n    if ($model->hasErrors()) {\n        // validación fallida\n    } else {\n        // validación exitosa\n    }\n}\n```\n\nDespués de la validación, puedes verificar si la validación tuvo éxito o no llamando al\nmétodo [[yii\\base\\DynamicModel::hasErrors()|hasErrors()]], obteniendo así los errores de validación de la\npropiedad [[yii\\base\\DynamicModel::errors|errors]], como haces con un modelo normal.\nPuedes también acceder a los atributos dinámicos definidos a través de la instancia del modelo, por ej.,\n`$model->name` y `$model->email`.\n\n\n## Crear Validadores <span id=\"creating-validators\"></span>\n\nAdemás de los [validadores del framework](tutorial-core-validators.md) incluidos en los lanzamientos de Yii, puedes también\ncrear tus propios validadores. Puedes crear validadores en línea o validadores independientes.\n\n\n### Validadores en Línea <span id=\"inline-validators\"></span>\n\nUn validador en línea es uno definido en términos del método de un modelo o una función anónima. La firma\ndel método/función es:\n\n```php\n/**\n * @param string $attribute el atributo siendo validado actualmente\n * @param mixed $params el valor de los \"parámetros\" dados en la regla\n */\nfunction ($attribute, $params)\n```\n\nSi falla la validación de un atributo, el método/función debería llamar a [[yii\\base\\Model::addError()]] para guardar\nel mensaje de error en el modelo de manera que pueda ser recuperado más tarde y presentado a los usuarios finales.\n\nDebajo hay algunos ejemplos:\n\n```php\nuse yii\\base\\Model;\n\nclass MyForm extends Model\n{\n    public $country;\n    public $token;\n\n    public function rules()\n    {\n        return [\n            // un validador en línea definido como el método del modelo validateCountry()\n            ['country', 'validateCountry'],\n\n            // un validador en línea definido como una función anónima\n            ['token', function ($attribute, $params) {\n                if (!ctype_alnum($this->$attribute)) {\n                    $this->addError($attribute, 'El token debe contener letras y dígitos.');\n                }\n            }],\n        ];\n    }\n\n    public function validateCountry($attribute, $params)\n    {\n        if (!in_array($this->$attribute, ['USA', 'Web'])) {\n            $this->addError($attribute, 'El país debe ser \"USA\" o \"Web\".');\n        }\n    }\n}\n```\n\n> Note: Por defecto, los validadores en línea no serán aplicados si sus atributos asociados reciben entradas vacías\n  o si alguna de sus reglas de validación ya falló. Si quieres asegurarte de que una regla siempre sea aplicada,\n  puedes configurar las reglas [[yii\\validators\\Validator::skipOnEmpty|skipOnEmpty]] y/o [[yii\\validators\\Validator::skipOnError|skipOnError]]\n  como `false` en las declaraciones de las reglas. Por ejemplo:\n>\n> ```php\n> [\n>     ['country', 'validateCountry', 'skipOnEmpty' => false, 'skipOnError' => false],\n> ]\n> ```\n\n\n### Validadores Independientes <span id=\"standalone-validators\"></span>\n\nUn validador independiente es una clase que extiende de [[yii\\validators\\Validator]] o sus sub clases. Puedes implementar\nsu lógica de validación sobrescribiendo el método [[yii\\validators\\Validator::validateAttribute()]]. Si falla la validación\nde un atributo, llama a [[yii\\base\\Model::addError()]] para guardar el mensaje de error en el modelo, tal como haces\ncon los [validadores en línea](#inline-validators).\n\n\nPor ejemplo, el validador en línea de arriba podría ser movida a una nueva clase [[components/validators/CountryValidator]].\n\n```php\nnamespace app\\components;\n\nuse yii\\validators\\Validator;\n\nclass CountryValidator extends Validator\n{\n    public function validateAttribute($model, $attribute)\n    {\n        if (!in_array($model->$attribute, ['USA', 'Web'])) {\n            $this->addError($model, $attribute, 'El país debe ser \"USA\" o \"Web\".');\n        }\n    }\n}\n```\n\nSi quieres que tu validador soporte la validación de un valor sin modelo, deberías también sobrescribir\nel método[[yii\\validators\\Validator::validate()]]. Puedes también sobrescribir [[yii\\validators\\Validator::validateValue()]]\nen vez de `validateAttribute()` y `validate()` porque por defecto los últimos dos métodos son implementados\nllamando a `validateValue()`.\n\nDebajo hay un ejemplo de cómo podrías utilizar la clase del validador de arriba dentro de tu modelo.\n\n```php\nnamespace app\\models;\n\nuse Yii;\nuse yii\\base\\Model;\nuse app\\components\\validators\\CountryValidator;\n\nclass EntryForm extends Model\n{\n    public $name;\n    public $email;\n    public $country;\n\n    public function rules()\n    {\n        return [\n            [['name', 'email'], 'required'],\n            ['country', CountryValidator::class],\n            ['email', 'email'],\n        ];\n    }\n}\n```\n\n\n## Validación del Lado del Cliente <span id=\"client-side-validation\"></span>\n\nLa validación del lado del cliente basada en JavaScript es deseable cuando la entrada del usuario proviene de formularios HTML, dado que\npermite a los usuarios encontrar errores más rápido y por lo tanto provee una mejor experiencia. Puedes utilizar o implementar\nun validador que soporte validación del lado del cliente *en adición a* validación del lado del servidor.\n\n> Info: Si bien la validación del lado del cliente es deseable, no es una necesidad. Su principal propósito es proveer al usuario una mejor\n  experiencia. Al igual que datos de entrada que vienen del los usuarios finales, nunca deberías confiar en la validación del lado del cliente. Por esta razón,\n  deberías realizar siempre la validación del lado del servidor llamando a [[yii\\base\\Model::validate()]], como\n  se describió en las subsecciones previas.\n\n\n### Utilizar Validación del Lado del Cliente <span id=\"using-client-side-validation\"></span>\n\nVarios [validadores del framework](tutorial-core-validators.md) incluyen validación del lado del cliente. Todo lo que necesitas hacer\nes solamente utilizar [[yii\\widgets\\ActiveForm]] para construir tus formularios HTML. Por ejemplo, `LoginForm` mostrado abajo declara dos\nreglas: una utiliza el validador del framework [required](tutorial-core-validators.md#required), el cual es soportado tanto en\nlado del cliente como del servidor; y el otro usa el validador en línea `validatePassword`, que es sólo soportado de lado\ndel servidor.\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\nuse app\\models\\User;\n\nclass LoginForm extends Model\n{\n    public $username;\n    public $password;\n\n    public function rules()\n    {\n        return [\n            // username y password son ambos requeridos\n            [['username', 'password'], 'required'],\n\n            // password es validado por validatePassword()\n            ['password', 'validatePassword'],\n        ];\n    }\n\n    public function validatePassword()\n    {\n        $user = User::findByUsername($this->username);\n\n        if (!$user || !$user->validatePassword($this->password)) {\n            $this->addError('password', 'Username o password incorrecto.');\n        }\n    }\n}\n```\n\nEl formulario HTML creado en el siguiente código contiene dos campos de entrada: `username` y `password`.\nSi envias el formulario sin escribir nada, encontrarás que los mensajes de error requiriendo que\nescribas algo aparecen sin que haya comunicación alguna con el servidor.\n\n```php\n<?php $form = yii\\widgets\\ActiveForm::begin(); ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n    <?= Html::submitButton('Login') ?>\n<?php yii\\widgets\\ActiveForm::end(); ?>\n```\n\nDetrás de escena, [[yii\\widgets\\ActiveForm]] leerá las reglas de validación declaradas en el modelo\ny generará el código JavaScript apropiado para los validadores que soportan validación del lado del cliente. Cuando un usuario\ncambia el valor de un campo o envia el formulario, se lanzará la validación JavaScript del lado del cliente.\n\nSi quieres deshabilitar la validación del lado del cliente completamente, puedes configurar\nla propiedad [[yii\\widgets\\ActiveForm::enableClientValidation]] como `false`. También puedes deshabilitar la validación\ndel lado del cliente de campos individuales configurando su propiedad [[yii\\widgets\\ActiveField::enableClientValidation]]\ncomo `false`. Cuando `enableClientValidation` es configurado tanto a nivel de campo como a nivel de formulario,\ntendrá prioridad la primera.\n\n### Implementar Validación del Lado del Cliente <span id=\"implementing-client-side-validation\"></span>\n\n\nPara crear validadores que soportan validación del lado del cliente, debes implementar\nel método [[yii\\validators\\Validator::clientValidateAttribute()]], que devuelve una pieza de código JavaScript\nque realiza dicha validación. Dentro del código JavaScript, puedes utilizar las siguientes\nvariables predefinidas:\n\n- `attribute`: el nombre del atributo siendo validado.\n- `value`: el valor siendo validado.\n- `messages`: un array utilizado para contener los mensajes de error de validación para el atributo.\n- `deferred`: un array con objetos diferidos puede ser insertado (explicado en la subsección siguiente).\n\nEn el siguiente ejemplo, creamos un `StatusValidator` que valida si la entrada es un status válido\ncontra datos de status existentes. El validador soporta tato tanto validación del lado del servidor como del lado del cliente.\n\n```php\nnamespace app\\components;\n\nuse yii\\validators\\Validator;\nuse app\\models\\Status;\n\nclass StatusValidator extends Validator\n{\n    public function init()\n    {\n        parent::init();\n        $this->message = 'Entrada de Status Inválida.';\n    }\n\n    public function validateAttribute($model, $attribute)\n    {\n        $value = $model->$attribute;\n        if (!Status::find()->where(['id' => $value])->exists()) {\n            $model->addError($attribute, $this->message);\n        }\n    }\n\n    public function clientValidateAttribute($model, $attribute, $view)\n    {\n        $statuses = json_encode(Status::find()->select('id')->asArray()->column());\n        $message = json_encode($this->message, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);\n        return <<<JS\nif ($.inArray(value, $statuses) === -1) {\n    messages.push($message);\n}\nJS;\n    }\n}\n```\n\n> Tip: El código de arriba muestra principalmente cómo soportar validación del lado del cliente. En la práctica,\n> puedes utilizar el validador del framework [in](tutorial-core-validators.md#in) para alcanzar el mismo objetivo. Puedes\n> escribir la regla de validación como a como a continuación:\n>\n> ```php\n> [\n>     ['status', 'in', 'range' => Status::find()->select('id')->asArray()->column()],\n> ]\n> ```\n\n> Tip: Si necesitas trabajar con validación del lado del cliente manualmente, por ejemplo, agregar campos dinámicamente o realizar alguna lógica de UI,\n> consulta [Trabajar con ActiveForm vía JavaScript](https://github.com/samdark/yii2-cookbook/blob/master/book/forms-activeform-js.md)\n> en el Yii 2.0 Cookbook.\n\n### Validación Diferida <span id=\"deferred-validation\"></span>\n\nSi necesitas realizar validación del lado del cliente asincrónica, puedes crear [Objetos Diferidos](https://api.jquery.com/category/deferred-object/).\nPor ejemplo, para realizar validación AJAX personalizada, puedes utilizar el siguiente código:\n\n```php\npublic function clientValidateAttribute($model, $attribute, $view)\n{\n    return <<<JS\n        deferred.push($.get(\"/check\", {value: value}).done(function(data) {\n            if ('' !== data) {\n                messages.push(data);\n            }\n        }));\nJS;\n}\n```\n\nArriba, la variable `deferred` es provista por Yii, y es un array de Objetos Diferidos. El método `$.get()`\nde jQuery crea un Objeto Diferido, el cual es insertado en el array `deferred`.\n\nPuedes también crear un Objeto Diferito explícitamente y llamar a su método `resolve()` cuando la llamada asincrónica\ntiene lugar. El siguiente ejemplo muestra cómo validar las dimensiones de un archivo de imagen del lado del cliente.\n\n```php\npublic function clientValidateAttribute($model, $attribute, $view)\n{\n    return <<<JS\n        var def = $.Deferred();\n        var img = new Image();\n        img.onload = function() {\n            if (this.width > 150) {\n                messages.push('Imagen demasiado ancha!!');\n            }\n            def.resolve();\n        }\n        var reader = new FileReader();\n        reader.onloadend = function() {\n            img.src = reader.result;\n        }\n        reader.readAsDataURL(file);\n\n        deferred.push(def);\nJS;\n}\n```\n\n> Note: El método `resolve()` debe ser llamado después de que el atributo ha sido validado. De otra manera la validación\n  principal del formulario no será completada.\n\nPor simplicidad, el array `deferred` está equipado con un método de atajo, `add()`, que automáticamente crea un\nObjeto Diferido y lo agrega al array `deferred`. Utilizando este método, puedes simplificar el ejemplo de arriba de esta manera,\n\n```php\npublic function clientValidateAttribute($model, $attribute, $view)\n{\n    return <<<JS\n        deferred.add(function(def) {\n            var img = new Image();\n            img.onload = function() {\n                if (this.width > 150) {\n                    messages.push('Imagen demasiado ancha!!');\n                }\n                def.resolve();\n            }\n            var reader = new FileReader();\n            reader.onloadend = function() {\n                img.src = reader.result;\n            }\n            reader.readAsDataURL(file);\n        });\nJS;\n}\n```\n\n\n### Validación AJAX <span id=\"ajax-validation\"></span>\n\nAlgunas validaciones sólo pueden realizarse del lado del servidor, debido a que sólo el servidor tiene la información necesaria.\nPor ejemplo, para validar si un nombre de usuario es único o no, es necesario revisar la tabla de usuarios del lado del servidor.\nPuedes utilizar validación basada en AJAX en este caso. Esta lanzará una petición AJAX de fondo para validar\nla entrada mientras se mantiene la misma experiencia de usuario como en una validación del lado del cliente regular.\n\nPara habilitar la validación AJAX individualmente un campo de entrada, configura la propiedad [[yii\\widgets\\ActiveField::enableAjaxValidation|enableAjaxValidation]]\nde ese campo como `true` y especifica un único `id` de formulario:\n\n```php\nuse yii\\widgets\\ActiveForm;\n\n$form = ActiveForm::begin([\n    'id' => 'registration-form',\n]);\n\necho $form->field($model, 'username', ['enableAjaxValidation' => true]);\n\n// ...\n\nActiveForm::end();\n```\n\nPara habiliar la validación AJAX en el formulario entero, configura [[yii\\widgets\\ActiveForm::enableAjaxValidation|enableAjaxValidation]]\ncomo `true` a nivel del formulario:\n\n```php\n$form = ActiveForm::begin([\n    'id' => 'contact-form',\n    'enableAjaxValidation' => true,\n]);\n```\n\n> Note: Cuando la propiedad `enableAjaxValidation` es configurada tanto a nivel de campo como a nivel de formulario,\n  la primera tendrá prioridad.\n\nNecesitas también preparar el servidor para que pueda manejar las peticiones AJAX.\nEsto puede alcanzarse con una porción de código como la siguiente en las acciones del controlador:\n\n```php\nif (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {\n    Yii::$app->response->format = Response::FORMAT_JSON;\n    return ActiveForm::validate($model);\n}\n```\n\nEl código de arriba chequeará si la petición actual es AJAX o no. Si lo es, responderá\nesta petición ejecutando la validación y devolviendo los errores en formato JSON.\n\n> Info: Puedes también utilizar [Validación Diferida](#deferred-validation) para realizar validación AJAX.\n  De todos modos, la característica de validación AJAX descrita aquí es más sistemática y requiere menos esfuerzo de escritura de código.\n\nCuando tanto `enableClientValidation` como `enableAjaxValidation` son definidas como `true`, la petición de validación AJAX será lanzada\nsólo después de una validación del lado del cliente exitosa.\n"
  },
  {
    "path": "docs/guide-es/intro-upgrade-from-v1.md",
    "content": "Actualizar desde Yii 1.1\n========================\n\nExisten muchas diferencias entre las versiones 1.1 y 2.0 de Yii ya que el framework fue completamente reescrito\nen su segunda versión.\nComo resultado, actualizar desde la versión 1.1 no es tan trivial como actualizar entre versiones menores. En esta\nguía encontrarás las diferencias más grandes entre estas dos versiones.\n\nSi no has utilizado Yii 1.1 antes, puedes saltarte con seguridad esta sección e ir directamente a \"[Comenzando con Yii](start-installation.md)\".\n\nEs importante anotar que Yii 2.0 introduce más características de las que van a ser cubiertas en este resumen. Es altamente recomendado\nque leas a través de toda la guía definitiva para aprender acerca de todas ellas. Hay muchas posibilidades de que algo que hayas desarrollado anteriormente para extender Yii, sea ahora parte del núcleo de la librería.\n\n\nInstalación\n------------\n\nYii 2.0 adopta íntegramente [Composer](https://getcomposer.org/), el administrador de paquetes de facto de PHP.\nTanto la instalación del núcleo del framework como las extensiones se manejan a través de Composer. Por favor consulta\nla sección [Comenzando con la Aplicación Básica](start-installation.md) para aprender a instalar Yii 2.0. Si quieres crear extensiones\no transformar extensiones de Yii 1.1 para que sean compatibles con Yii 2.0, consulta\nla sección [Creando Extensiones](structure-extensions.md#creating-extensions) de la guía.\n\n\nRequerimientos de PHP\n---------------------\n\nYii 2.0 requiere PHP 5.4 o mayor, lo que es un gran progreso ya que Yii 1.1 funcionaba con PHP 5.2.\nComo resultado, hay muchas diferencias a nivel del lenguaje a las que deberías prestar atención.\nAbajo hay un resumen de los mayores cambios en relación a PHP:\n\n- [Namespaces](https://www.php.net/manual/es/language.namespaces.php).\n- [Funciones anónimas](https://www.php.net/manual/es/functions.anonymous.php).\n- La sintaxis corta de Arrays `[...elementos...]` es utilizada en vez de `array(...elementos...)`.\n- Etiquetas cortas de `echo`. Ahora en las vistas se usa `<?=`. Esto se puede utilizar desde PHP 5.4.\n- [SPL - Biblioteca estándar de PHP](https://www.php.net/manual/es/book.spl.php).\n- [Enlace estático en tiempo de ejecución](https://www.php.net/manual/es/language.oop5.late-static-bindings.php).\n- [Fecha y Hora](https://www.php.net/manual/es/book.datetime.php).\n- [Traits](https://www.php.net/manual/es/language.oop5.traits.php).\n- [intl](https://www.php.net/manual/es/book.intl.php). Yii 2.0 utiliza la extensión `intl` de PHP\n  como soporte para internacionalización.\n\n\nNamespace\n---------\n\nEl cambio más obvio en Yii 2.0 es el uso de namespaces. Casi todas las clases del núcleo\nutilizan namespaces, ej., `yii\\web\\Request`. El prefijo \"C\" no se utiliza más en los nombre de clases.\nEl esquema de nombres sigue la estructura de directorios. Por ejemplo, `yii\\web\\Request`\nindica que el archivo de la clase correspondiente `web/Request.php` está bajo el directorio de Yii framework.\n\n(Puedes utilizar cualquier clase del núcleo sin necesidad de incluir el archivo que la contiene, gracias\nal autoloader de Yii.)\n\n\nComponentes y Objetos\n----------------------\n\nYii 2.0 parte la clase `CComponent` de 1.1 en dos clases: [[yii\\base\\BaseObject]] y [[yii\\base\\Component]].\nLa clase [[yii\\base\\BaseObject|BaseObject]] es una clase base que permite definir [propiedades de object](concept-properties.md)\na través de getters y setters. La clase [[yii\\base\\Component|Component]] extiende de [[yii\\base\\BaseObject|BaseObject]] y soporta\n[eventos](concept-events.md) y [comportamientos](concept-behaviors.md).\n\nSi tu clase no necesita utilizar las características de eventos o comportamientos, puedes considerar usar\n[[yii\\base\\BaseObject|BaseObject]] como clase base. Esto es frecuente en el caso de que las clases que representan sean\nestructuras de datos básicas.\n\n\nConfiguración de objetos\n------------------------\n\nLa clase [[yii\\base\\BaseObject|BaseObject]] introduce una manera uniforme de configurar objetos. Cualquier clase descendiente\nde [[yii\\base\\BaseObject|BaseObject]] debería declarar su constructor (si fuera necesario) de la siguiente manera para que\npuede ser adecuadamente configurado:\n\n```php\nclass MyClass extends \\yii\\base\\BaseObject\n{\n    public function __construct($param1, $param2, $config = [])\n    {\n        // ... se aplica la inicialización antes de la configuración\n\n        parent::__construct($config);\n    }\n\n    public function init()\n    {\n        parent::init();\n\n        // ... se aplica la inicialización después de la configuración\n    }\n}\n```\n\nEn el ejemplo de arriba, el último parámetro del constructor debe tomar un array de configuración que\ncontiene pares clave-valor para la inicialización de las propiedades al final del mismo.\nPuedes sobrescribir el método [[yii\\base\\BaseObject::init()|init()]] para realizar el trabajo de inicialización\nque debe ser hecho después de que la configuración haya sido aplicada.\n\nSiguiendo esa convención, podrás crear y configurar nuevos objetos utilizando\nun array de configuración:\n\n```php\n$object = Yii::createObject([\n    'class' => 'MyClass',\n    'property1' => 'abc',\n    'property2' => 'cde',\n], [$param1, $param2]);\n```\n\nSe puede encontrar más detalles acerca del tema en la sección [Configuración](concept-configurations.md).\n\n\nEventos\n-------\n\nEn Yii 1, los eventos eran creados definiendo un método `on` (ej., `onBeforeSave`). En Yii 2, puedes utilizar cualquier nombre de evento.\nAhora puedes disparar un evento utilizando el método [[yii\\base\\Component::trigger()|trigger()]]:\n\n```php\n$event = new \\yii\\base\\Event;\n$component->trigger($eventName, $event);\n```\n\nPara conectar un manejador a un evento, utiliza el método [[yii\\base\\Component::on()|on()]]:\n\n```php\n$component->on($eventName, $handler);\n// Para desconectar el manejador, utiliza:\n// $component->off($eventName, $handler);\n```\n\nHay muchas mejoras en lo que respecta a eventos. Para más detalles, consulta la sección [Eventos](concept-events.md).\n\n\nAlias\n-----\n\nYii 2.0 extiende el uso de alias tanto para archivos/directorios como URLs. Yii 2.0 ahora requiere que cada\nalias comience con el carácter `@`, para diferenciarlos de rutas o URLs normales.\nPor ejemplo, el alias `@yii` corresponde al directorio donde Yii se encuentra instalado. Los alias\nestán soportados en la mayor parte del núcleo. Por ejemplo, [[yii\\caching\\FileCache::cachePath]] puede tomar tanto\nuna ruta de directorios normal como un alias.\n\nUn alias está estrechamente relacionado con un namespace de la clase. Se recomienda definir un alias\npor cada namespace raíz, y así poder utilizar el autoloader de Yii sin otra configuración.\nPor ejemplo, debido a que `@yii` se refiere al directorio de instalación, una clase\ncomo `yii\\web\\Request` puede ser auto-cargada. Si estás utilizando una librería de terceros,\ncomo Zend Framework, puedes definir un alias `@Zend` que se refiera al directorio de instalación\nde ese framework. Una vez realizado esto, Yii será capaz de auto-cargar cualquier clase de Zend Framework también.\n\nSe puede encontrar más detalles del tema en la sección [Alias](concept-aliases.md).\n\n\nVistas\n------\n\nEl cambio más significativo con respecto a las vistas en Yii 2 es que la variable especial `$this` dentro de una vista\nya no se refiere al controlador o widget actual. En vez de eso, `$this` ahora se refiere al objeto de la *vista*, un concepto nuevo\nintroducido en Yii 2.0. El objeto *vista* es del tipo [[yii\\web\\View]], que representa la parte de las vistas\nen el patrón MVC. Si quieres acceder al controlador o al widget correspondiente desde la propia vista, puedes utilizar `$this->context`.\n\nPara renderizar una vista parcial (partial) dentro de otra vista, se utiliza `$this->render()`, no `$this->renderPartial()`. La llamada a `render` además tiene que ser mostrada explícitamente a través de `echo`,\nya que el método `render()` devuelve el resultado de la renderización en vez de mostrarlo directamente. Por ejemplo:\n\n```php\necho $this->render('_item', ['item' => $item]);\n```\n\nAdemás de utilizar PHP como el lenguaje principal de plantillas (templates), Yii 2.0 está también equipado con soporte\noficial de otros dos motores de plantillas populares: Smarty y Twig. El motor de plantillas de Prado ya no está soportado.\nPara utilizar esos motores, necesitas configurar el componente `view` de la aplicación, definiendo la propiedad [[yii\\base\\View::$renderers|View::$renderers]].\nPor favor consulta la sección [Motores de Plantillas](tutorial-template-engines.md)\npara más detalles.\n\n\nModelos\n-------\n\nYii 2.0 utiliza [[yii\\base\\Model]] como modelo base, algo similar a `CModel` en 1.1.\nLa clase `CFormModel` ha sido descartada por completo. Ahora, en Yii 2 debes extender de [[yii\\base\\Model]] para crear clases de modelos basados en formularios.\n\nYii 2.0 introduce un nuevo método llamado [[yii\\base\\Model::scenarios()|scenarios()]] para declarar escenarios soportados,\ny para indicar bajo que escenario un atributo necesita ser validado, puede ser considerado seguro o no, etc. Por ejemplo:\n\n```php\npublic function scenarios()\n{\n    return [\n        'backend' => ['email', 'role'],\n        'frontend' => ['email', '!role'],\n    ];\n}\n```\n\nEn el ejemplo anterior, se declaran dos escenarios: `backend` y `frontend`. Para el escenario `backend` son considerados seguros\nambos atributos, `email` y `role`, y pueden ser asignados masivamente. Para el escenario `frontend`, `email` puede ser asignado\nmasivamente mientras `role` no. Tanto `email` como `role` deben ser validados utilizando reglas (rules).\n\nEl método [[yii\\base\\Model::rules()|rules()]] aún es utilizado para declara reglas de validación.\nTen en cuenta que dada la introducción de [[yii\\base\\Model::scenarios()|scenarios()]], ya no existe el validador `unsafe`.\n\nEn la mayoría de los casos, no necesitas sobrescribir [[yii\\base\\Model::scenarios()|scenarios()]] si el método [[yii\\base\\Model::rules()|rules()]]\nespecifica completamente los escenarios que existirán, y si no hay necesidad de declarar atributos inseguros (`unsafe`).\n\nPara aprender más detalles de modelos, consulta la sección [Modelos](structure-models.md).\n\n\nControladores\n-------------\n\nYii 2.0 utiliza [[yii\\web\\Controller]] como controlador base, similar a `CWebController` en Yii 1.1.\n[[yii\\base\\Action]] es la clase base para clases de acciones.\n\nEl impacto más obvio de estos cambios en tu código es que que cada acción del controlador debe devolver el contenido\nque quieres mostrar en vez de mostrarlo directamente:\n\n```php\npublic function actionView($id)\n{\n    $model = \\app\\models\\Post::findOne($id);\n    if ($model) {\n        return $this->render('view', ['model' => $model]);\n    } else {\n        throw new \\yii\\web\\NotFoundHttpException;\n    }\n}\n```\n\nPor favor, consulta la sección [Controladores](structure-controllers.md) para más detalles acerca de los controladores.\n\n\nWidgets\n-------\n\nYii 2.0 utiliza [[yii\\base\\Widget]] como clase base de los widgets, similar a `CWidget` en Yii 1.1.\n\nPara obtener mejor soporte del framework en IDEs, Yii 2.0 introduce una nueva sintaxis para utilizar widgets.\nLos métodos estáticos [[yii\\base\\Widget::begin()|begin()]], [[yii\\base\\Widget::end()|end()]], y [[yii\\base\\Widget::widget()|widget()]]\nfueron incorporados, y deben utilizarse así:\n\n```php\nuse yii\\widgets\\Menu;\nuse yii\\widgets\\ActiveForm;\n\n// Ten en cuenta que debes pasar el resultado a \"echo\" para mostrarlo\necho Menu::widget(['items' => $items]);\n\n// Pasando un array para inicializar las propiedades del objeto\n$form = ActiveForm::begin([\n    'options' => ['class' => 'form-horizontal'],\n    'fieldConfig' => ['inputOptions' => ['class' => 'input-xlarge']],\n]);\n... campos del formulario aquí ...\nActiveForm::end();\n```\n\nConsulta la sección [Widgets](structure-widgets.md) para más detalles.\n\n\nTemas\n------\n\nLos temas funcionan completamente diferente en Yii 2.0. Ahora están basados en un mecanismo de mapeo de rutas,\nque mapea la ruta de un archivo de la vista de origen a uno con un tema aplicado. Por ejemplo, si el mapeo de ruta de un tema es\n`['/web/views' => '/web/themes/basic']`, entonces la versión con el tema aplicado del archivo\n`/web/views/site/index.php` será `/web/themes/basic/site/index.php`. Por esta razón, ahora los temas pueden ser\naplicados a cualquier archivo de la vista, incluso una vista renderizada fuera del contexto de un controlador o widget.\n\nAdemás, el componente `CThemeManager` ya no existe. En cambio, `theme` es una propiedad configurable del componente `view`\nde la aplicación.\n\nConsulta la sección [Temas](output-theming.md) para más detalles.\n\n\nAplicaciones de Consola\n-----------------------\n\nLas aplicaciones de consola ahora están organizadas en controladores, tal como aplicaciones Web. Estos controladores\ndeben extender de [[yii\\console\\Controller]], similar a `CConsoleCommand` en 1.1.\n\nPara correr un comando de consola, utiliza `yii <ruta>`, donde `<ruta>` se refiere a la ruta del controlador\n(ej. `sitemap/index`). Los argumentos anónimos adicionales son pasados como parámetros al método de la acción correspondiente\ndel controlador, mientras que los argumentos especificados son pasados de acuerdo a las declaraciones en [[yii\\console\\Controller::options()]].\n\nYii 2.0 soporta la generación automática de información de ayuda de los comandos a través de los bloques de comentarios del archivo.\n\nPor favor consulta la sección [Comandos de Consola](tutorial-console.md) para más detalles.\n\n\nI18N\n----\n\nYii 2.0 remueve el formateador de fecha y números previamente incluido en favor del [módulo de PHP PECL intl](https://pecl.php.net/package/intl).\n\nLa traducción de mensajes ahora es ejecutada vía el componente `i18n` de la aplicación.\nEste componente maneja un grupo de mensajes origen, lo que te permite utilizar diferentes mensajes\nbasados en categorías.\n\nPor favor, consulta la sección [Internacionalización](tutorial-i18n.md) para más información.\n\n\nFiltros de Acciones\n-------------------\n\nLos filtros de acciones son implementados a través de comportamientos. Para definir un\nnuevo filtro personalizado, se debe extender de [[yii\\base\\ActionFilter]]. Para utilizar el filtro, conecta la clase del filtro\nal controlador como un comportamiento. Por ejemplo, para utilizar el filtro [[yii\\filters\\AccessControl]], deberías tener\nel siguiente código en el controlador:\n\n```php\npublic function behaviors()\n{\n    return [\n        'access' => [\n            'class' => 'yii\\filters\\AccessControl',\n            'rules' => [\n                ['allow' => true, 'actions' => ['admin'], 'roles' => ['@']],\n            ],\n        ],\n    ];\n}\n```\n\nConsulta la sección [Filtrando](structure-filters.md) para una mayor información acerca del tema.\n\n\nAssets\n------\n\nYii 2.0 introduce un nuevo concepto llamado *asset bundle* que reemplaza el concepto de script package encontrado en Yii 1.1.\n\nUn asset bundle es una colección de archivos assets (ej. archivos JavaScript, archivos CSS, imágenes, etc.) dentro de un directorio.\nCada asset bundle está representado por una clase que extiende de [[yii\\web\\AssetBundle]].\nAl registrar un asset bundle a través de [[yii\\web\\AssetBundle::register()]], haces que los assets de dicho bundle sean accesibles\nvía Web. A diferencia de Yii 1, la página que registra el bundle contendrá automáticamente las referencias a los archivos\nJavaScript y CSS especificados en el bundle.\n\nPor favor, consulta la sección [Manejando Assets](structure-assets.md) para más detalles.\n\n\nHelpers\n-------\n\nYii 2.0 introduce muchos helpers estáticos comúnmente utilizados, incluyendo:\n\n* [[yii\\helpers\\Html]]\n* [[yii\\helpers\\ArrayHelper]]\n* [[yii\\helpers\\StringHelper]]\n* [[yii\\helpers\\FileHelper]]\n* [[yii\\helpers\\Json]]\n\nPor favor, consulta la sección [Información General de Helpers](helper-overview.md) para más detalles.\n\nFormularios\n-----------\n\nYii 2.0 introduce el concepto de *campo* (field) para construir formularios utilizando [[yii\\widgets\\ActiveForm]]. Un campo\nes un contenedor que consiste en una etiqueta, un input, un mensaje de error y/o texto de ayuda.\nUn campo es representado como un objeto [[yii\\widgets\\ActiveField|ActiveField]].\nUtilizando estos campos, puedes crear formularios más legibles que antes:\n\n```php\n<?php $form = yii\\widgets\\ActiveForm::begin(); ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n    <div class=\"form-group\">\n        <?= Html::submitButton('Login') ?>\n    </div>\n<?php yii\\widgets\\ActiveForm::end(); ?>\n```\n\nPor favor, consulta la sección [Creando Formularios](input-forms.md) para más detalles.\n\n\nConstructor de Consultas\n------------------------\n\nEn Yii 1.1, la generación de consultas a la base de datos estaba dividida en varias clases, incluyendo `CDbCommand`,\n`CDbCriteria`, y `CDbCommandBuilder`. Yii 2.0 representa una consulta a la base de datos en términos de un objeto [[yii\\db\\Query|Query]]\nque puede ser convertido en una declaración SQL con la ayuda de [[yii\\db\\QueryBuilder|QueryBuilder]] detrás de la escena.\nPor ejemplo:\n\n```php\n$query = new \\yii\\db\\Query();\n$query->select('id, name')\n      ->from('user')\n      ->limit(10);\n\n$command = $query->createCommand();\n$sql = $command->sql;\n$rows = $command->queryAll();\n```\n\nLo mejor de todo, dichos métodos de generación de consultas pueden ser también utilizados mientras se trabaja con [Active Record](db-active-record.md).\n\nConsulta la sección [Constructor de Consultas](db-query-builder.md) para más detalles.\n\n\nActive Record\n-------------\n\nYii 2.0 introduce muchísimos cambios con respecto a [Active Record](db-active-record.md). Los dos más obvios se relacionan a\nla generación de consultas y al manejo de relaciones.\n\nLa clase de Yii 1.1 `CDbCriteria` es reemplazada por [[yii\\db\\ActiveQuery]] en Yii 2. Esta clase extiende de [[yii\\db\\Query]],\ny por lo tanto hereda todos los métodos de generación de consultas.\nPara comenzar a generar una consulta, llamas al método [[yii\\db\\ActiveRecord::find()]]:\n\n```php\n// Recibe todos los clientes *activos* y ordenados por su ID:\n$customers = Customer::find()\n    ->where(['status' => $active])\n    ->orderBy('id')\n    ->all();\n```\n\nPara declarar una relación, simplemente define un método getter que devuelva un objeto [[yii\\db\\ActiveQuery|ActiveQuery]].\nEl nombre de la propiedad definida en el getter representa el nombre de la relación. Por ejemplo, el siguiente código declara\nuna relación `orders` (en Yii 1.1, las relaciones se declaraban centralmente en el método `relations()`):\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public function getOrders()\n    {\n        return $this->hasMany('Order', ['customer_id' => 'id']);\n    }\n}\n```\n\nAhora puedes utilizar `$customer->orders` para acceder a las órdenes de la tabla relacionada. También puedes utilizar el siguiente\ncódigo para realizar una consulta relacional 'sobre la marcha' con una condición personalizada:\n\n```php\n$orders = $customer->getOrders()->andWhere('status=1')->all();\n```\n\nCuando se utiliza la carga temprana (eager loading) de la relación, Yii 2.0 lo hace diferente de 1.1. En particular, en 1.1 una declaración JOIN\nsería creada para seleccionar tanto los registros de la tabla primaria como los relacionados. En Yii 2.0, dos declaraciones SQL son ejecutadas\nsin utilizar un JOIN: la primera trae todos los modelos primarios, mientras que la segunda trae los registros relacionados\nutilizando como condición la clave primaria de los primarios.\n\nEn vez de devolver objetos [[yii\\db\\ActiveRecord|ActiveRecord]], puedes conectar el método [[yii\\db\\ActiveQuery::asArray()|asArray()]]\nmientras generas una consulta que devuelve un gran número de registros. Esto causará que el resultado de la consulta sea devuelto como\narrays, lo que puede reducir significativamente la necesidad de tiempo de CPU y memoria si el número de registros es grande.\nPor ejemplo:\n\n```php\n$customers = Customer::find()->asArray()->all();\n```\n\nOtro cambio es que ya no puedes definir valores por defecto a los atributos a través de propiedades públicas.\nSi lo necesitaras, debes definirlo en el método `init` de la clase del registro en cuestión.\n\n```php\npublic function init()\n{\n    parent::init();\n    $this->status = self::STATUS_NEW;\n}\n```\n\nAnteriormente, solía haber algunos problemas al sobrescribir el constructor de una clase ActiveRecord en 1.1. Estos ya no están presentes en\nYii 2.0. Ten en cuenta que al agregar parámetros al constructor podrías llegar a tener que sobrescribir [[yii\\db\\ActiveRecord::instantiate()]].\n\nHay muchos otros cambios y mejoras con respecto a ActiveRecord. Por favor, consulta\nla sección [Active Record](db-active-record.md) para más detalles.\n\n\nActive Record Behaviors\n-----------------------\n\nEn 2.0, hemos eliminado la clase del comportamiento base `CActiveRecordBehavior`. Si desea crear un comportamiento Active Record, usted tendrá que extender directamente de `yii\\base\\Behavior`. Si la clase de comportamiento debe responder a algunos eventos propios, usted tiene que sobrescribir los métodos `events()` como se muestra a continuación,\n\n```php\nnamespace app\\components;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\base\\Behavior;\n\nclass MyBehavior extends Behavior\n{\n    // ...\n\n    public function events()\n    {\n        return [\n            ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',\n        ];\n    }\n\n    public function beforeValidate($event)\n    {\n        // ...\n    }\n}\n```\n\n\nUser e IdentityInterface\n------------------------\n\nLa clase `CWebUser` de 1.1 es reemplazada por [[yii\\web\\User]], y la clase `CUserIdentity` ha dejado de existir.\nEn cambio, ahora debes implementar [[yii\\web\\IdentityInterface]] el cual es mucho más directo de usar.\nEl template de proyecto avanzado provee un ejemplo así.\n\nConsulta las secciones [Autenticación](security-authentication.md), [Autorización](security-authorization.md), y [Template de Proyecto Avanzado](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide-es/README.md) para más detalles.\n\n\n\nManejo de URLs\n--------------\n\nEl manejo de URLs en Yii 2 es similar al de 1.1. Una mejora mayor es que el manejador actual ahora soporta parámetros opcionales.\nPor ejemplo, si tienes una regla declarada como a continuación, entonces coincidirá tanto con `post/popular` como con `post/1/popular`.\nEn 1.1, tendrías que haber creado dos reglas diferentes para obtener el mismo resultado\n\n```php\n[\n    'pattern' => 'post/<page:\\d+>/<tag>',\n    'route' => 'post/index',\n    'defaults' => ['page' => 1],\n]\n```\n\nPor favor, consulta la sección [Documentación del Manejo de URLs](runtime-routing.md) para más detalles.\n\nUn cambio importante en la convención de nombres para rutas es que los nombres en CamelCase de controladores\ny acciones ahora son convertidos a minúsculas y cada palabra separada por un guión, por ejemplo el id del controlador\n`CamelCaseController` será `camel-case`.\nConsulta la sección acerca de [IDs de controladores](structure-controllers.md#controller-ids) y [IDs de acciones](structure-controllers.md#action-ids) para más detalles.\n\n\nUtilizar Yii 1.1 y 2.x juntos\n-----------------------------\n\nSi tienes código en Yii 1.1 que quisieras utilizar junto con Yii 2.0, por favor consulta\nla sección [Utilizando Yii 1.1 y 2.0 juntos](tutorial-yii-integration.md).\n\n"
  },
  {
    "path": "docs/guide-es/intro-yii.md",
    "content": "¿Qué es Yii?\n============\n\nYii es un framework de PHP de alto rendimiento, basado en componentes para desarrollar aplicaciones web\nmodernas en poco tiempo. El nombre Yii significa \"simple y evolutivo\" en chino. También se puede considerar como el acrónimo\nde _**Yes It Is**_ (que en inglés significa _**Sí, lo es**_)!\n\n\n¿En qué es mejor Yii?\n-----------------------\n\nYii es un framework genérico de programación web, lo que significa que se puede utilizar para desarrollar todo tipo de aplicaciones web en PHP.\nDebido a su arquitectura basada en componentes y a su sofisticada compatibilidad de caché, es especialmente apropiado para el desarrollo\nde aplicaciones de gran envergadura, como páginas web, foros, sistemas de gestión de contenidos (CMS), proyectos de comercio electrónico,\nservicios web compatibles con la arquitectura REST y muchos más.\n\n\n¿Cómo se compara Yii con otros frameworks?\n--------------------------------------\n\nSi estás ya familiarizado con otros framework, puedes apreciar como se compara Yii con ellos:\n\n- Como la mayoría de los framework de PHP, Yii implementa el patrón de diseño MVC (Modelo-Vista-Controlador) y\n promueve la organización de código basada en este patrón.\n- La filosofía de Yii consiste en escribir el código de manera simple y elegante, sin sobrediseñar nunca por el\n mero hecho de seguir un patrón de diseño determinado.\n- Yii es un framework completo (full stack) que provee muchas características probadas y listas para usar, como los \nconstructores de consultas y la clase ActiveRecord para las bases de datos relacionales y NoSQL, \nla compatibilidad con la arquitectura REST para desarrollar API, la compatibilidad de caché en varios niveles \ny muchas más.\n- Yii es extremadamente extensible. Puedes personalizar o reemplazar prácticamente cualquier pieza de código de base, \ncomo se puede también aprovechar su sólida arquitectura de extensiones para utilizar o desarrollar extensiones distribuibles.\n- El alto rendimiento es siempre la meta principal de Yii.\n\nYii no es un proyecto de un sola persona, detrás de Yii hay un [sólido equipo de desarrollo](https://www.yiiframework.com/team/), \nasí como una gran comunidad en la que numerosos profesionales contribuyen constantemente a su desarrollo.\nEl equipo de desarrollo de Yii se mantiene atento a las últimas tendencias de desarrollo web, así como a las mejores prácticas y características de otros frameworks y proyectos.\nLas buenas prácticas y características más relevantes de otros proyectos se incorporan regularmente a la base del framework y se exponen a través de interfaces simples y elegantes.\n\n[about_yii]: https://www.yiiframework.com/about/\n\nVersiones de Yii\n----------------\n\nActualmente existen dos versiones principales de Yii: la versión 1.1 y la versión 2.0. Para la versión 1.1, que es de la generación anterior, actualmente solo se ofrece mantenimiento.\nLa versión 2.0 está completamente reescrita y adopta las últimas tecnologías y protocolos, incluidos Composer, PSR, namespaces, traits, etc.\nLa versión 2.0 representa la actual generación del framework y su desarrollo recibirá el principal esfuerzo en los próximos años.\nEsta guía está basada principalmente en la versión 2.0. del framework.\n\n\nRequisitos y Prerequisitos\n--------------------------\n\nYii 2.0 requiere PHP 7.4.0 o una versión posterior y corre de mejor manera en la última versión de PHP. Se pueden encontrar requisitos más detallados de características individuales\nejecutando el script de comprobación incluido en cada lanzamiento de Yii.\n\nPara utilizar Yii se requieren conocimientos básicos de programación orientada a objetos (POO), porque el framework Yii se basa íntegramente en esta tecnología.\nYii 2.0 hace uso también de las últimas características de PHP, como [namespaces](https://www.php.net/manual/es/language.namespaces.php)\ny [traits](https://www.php.net/manual/es/language.oop5.traits.php). Comprender estos conceptos te ayudará a entender mejor Yii 2.0.\n\n"
  },
  {
    "path": "docs/guide-es/output-client-scripts.md",
    "content": "Trabajar con Scripts del Cliente\n================================\n\n> Note: Esta sección se encuentra en desarrollo.\n\n### Registrar scripts\n\nCon el objeto [[yii\\web\\View]] puedes registrar scripts. Hay dos métodos dedicados a esto:\n[[yii\\web\\View::registerJs()|registerJs()]] para scripts en línea\n[[yii\\web\\View::registerJsFile()|registerJsFile()]] para scripts externos.\nLos scripts en línea son útiles para configuración y código generado dinámicamente.\nEl método para agregarlos puede ser utilizado así:\n\n```php\n$this->registerJs(\"var options = \".json_encode($options).\";\", View::POS_END, 'my-options');\n```\n\nEl primer argumento es el código JS real que queremos insertar en la página. El segundo argumento\ndetermina en qué parte de la página debería ser insertado el script. Los valores posibles son:\n\n- [[yii\\web\\View::POS_HEAD|View::POS_HEAD]] para la sección head.\n- [[yii\\web\\View::POS_BEGIN|View::POS_BEGIN]] justo después de la etiqueta `<body>`.\n- [[yii\\web\\View::POS_END|View::POS_END]] justo antes de cerrar la etiqueta `</body>`.\n- [[yii\\web\\View::POS_READY|View::POS_READY]] para ejecutar código en el evento `ready` del documento. Esto registrará [[yii\\web\\JqueryAsset|jQuery]] automáticamente.\n- [[yii\\web\\View::POS_LOAD|View::POS_LOAD]] para ejecutar código en el evento `load` del documento. Esto registrará [[yii\\web\\JqueryAsset|jQuery]] automáticamente.\n\nEl último argumento es un ID único del script, utilizado para identificar el bloque de código y reemplazar otro con el mismo ID\nen vez de agregar uno nuevo. En caso de no proveerlo, el código JS en sí será utilizado como ID.\n\nUn script externo puede ser agregado de esta manera:\n\n```php\n$this->registerJsFile('https://example.com/js/main.js', ['depends' => [\\yii\\web\\JqueryAsset::class]]);\n```\n\nLos argumentos para [[yii\\web\\View::registerJsFile()|registerJsFile()]] son similares a los de\n[[yii\\web\\View::registerCssFile()|registerCssFile()]]. En el ejemplo anterior,\nregistramos el archivo `main.js` con dependencia de `JqueryAsset`. Esto quiere decir que el archivo `main.js`\nserá agregado DESPUÉS de `jquery.js`. Si esta especificación de dependencia, el orden relativo entre\n`main.js` y `jquery.js` sería indefinido.\n\nComo para [[yii\\web\\View::registerCssFile()|registerCssFile()]], es altamente recomendable que utilices\n[asset bundles](structure-assets.md) para registrar archivos JS externos más que utilizar [[yii\\web\\View::registerJsFile()|registerJsFile()]].\n\n\n### Registrar asset bundles\n\nComo mencionamos anteriormente, es preferible utilizar asset bundles en vez de usar CSS y JavaScript directamente. Puedes obtener detalles\nde cómo definir asset bundles en la sección [gestor de assets](structure-assets.md) de esta guía. Utilizar asset bundles\nya definidos es muy sencillo:\n\n```php\n\\frontend\\assets\\AppAsset::register($this);\n```\n\n\n\n### Registrar CSS\n\nPuedes registrar CSS utilizando [[yii\\web\\View::registerCss()|registerCss()]] o [[yii\\web\\View::registerCssFile()|registerCssFile()]].\nEl primero registra un bloque de código CSS mientras que el segundo registra un archivo CSS externo. Por ejemplo,\n\n```php\n$this->registerCss(\"body { background: #f00; }\");\n```\n\nEl código anterior dará como resultado que se agregue lo siguiente a la sección head de la página:\n\n```html\n<style>\nbody { background: #f00; }\n</style>\n```\n\nSi quieres especificar propiedades adicionales a la etiqueta style, pasa un array de claves-valores como tercer argumento.\nSi necesitas asegurarte que haya sólo una etiqueta style utiliza el cuarto argumento como fue mencionado en las descripciones de meta etiquetas.\n\n```php\n$this->registerCssFile(\"https://example.com/css/themes/black-and-white.css\", [\n    'depends' => [BootstrapAsset::class],\n    'media' => 'print',\n], 'css-print-theme');\n```\n\nEl código de arriba agregará un link al archivo CSS en la sección head de la página.\n\n* El primer argumento especifica el archivo CSS a ser registrado.\n* El segundo argumento especifica los atributos HTML de la etiqueta `<link>` resultante. La opción `depends`\n  es especialmente tratada. Esta especifica de qué asset bundles depende este archivo CSS. En este caso, depende\n  del asset bundle [[yii\\bootstrap\\BootstrapAsset|BootstrapAsset]]. Esto significa que el archivo CSS será agregado\n  *después* de los archivos CSS de [[yii\\bootstrap\\BootstrapAsset|BootstrapAsset]].\n* El último argumento especifica un ID que identifica al archivo CSS. Si no es provisto, se utilizará la URL\n  del archivo.\n\n\nEs altamente recomendable que ustilices [asset bundles](structure-assets.md) para registrar archivos CSS en vez de\nutilizar [[yii\\web\\View::registerCssFile()|registerCssFile()]]. Utilizar asset bundles te permite combinar y comprimir\nvarios archivos CSS, deseable en sitios web de tráfico alto.\n"
  },
  {
    "path": "docs/guide-es/output-data-providers.md",
    "content": "Proveedores de datos\n====================\n\nEn las secciones sobre [paginación](output-pagination.md) y [ordenación](output-sorting.md) se\ndescribe como permitir a los usuarios finales elegir que se muestre una página de datos en\nparticular, y ordenar los datos por algunas columnas.  Como la tarea de paginar y ordenar datos\nes muy común, Yii proporciona un conjunto de clases *proveedoras de datos* para encapsularla.\n\nUn proveedor de datos es una clase que implementa la interfaz [[yii\\data\\DataProviderInterface]].\nBásicamente se encarga de obtener datos paginados y ordenados.  Normalmente se usa junto con\n[_widgets_ de datos](output-data-widgets.md) para que los usuarios finales puedan paginar y\nordenar datos de forma interactiva.\n\nYii incluye las siguientes clases proveedoras de datos:\n\n* [[yii\\data\\ActiveDataProvider]]: usa [[yii\\db\\Query]] o [[yii\\db\\ActiveQuery]] para consultar datos de bases de datos y devolverlos como _arrays_ o instancias [Active Record](db-active-record.md).\n* [[yii\\data\\SqlDataProvider]]: ejecuta una sentencia SQL y devuelve los datos de la base de datos como _arrays_.\n* [[yii\\data\\ArrayDataProvider]]: toma un _array_ grande y devuelve una rodaja de él basándose en las especificaciones de paginación y ordenación.\n\nEl uso de todos estos proveedores de datos comparte el siguiente patrón común:\n\n```php\n// Crear el proveedor de datos configurando sus propiedades de paginación y ordenación\n$provider = new XyzDataProvider([\n    'pagination' => [...],\n    'sort' => [...],\n]);\n\n// Obtener los datos paginados y ordenados\n$models = $provider->getModels();\n\n// Obtener el número de elementos de la página actual\n$count = $provider->getCount();\n\n// Obtener el número total de elementos entre todas las páginas\n$totalCount = $provider->getTotalCount();\n```\n\nSe puede especificar los comportamientos de paginación y ordenación de un proveedor de datos\nconfigurando sus propiedades [[yii\\data\\BaseDataProvider::pagination|pagination]] y\n[[yii\\data\\BaseDataProvider::sort|sort]], que corresponden a las configuraciones para\n[[yii\\data\\Pagination]] y [[yii\\data\\Sort]] respectivamente.  También se pueden configurar a\n`false` para inhabilitar las funciones de paginación y/u ordenación.\n\nLos [_widgets_ de datos](output-data-widgets.md), como [[yii\\grid\\GridView]], tienen una\npropiedad llamada `dataProvider` que puede tomar una instancia de un proveedor de datos y\nmostrar los datos que proporciona.  Por ejemplo,\n\n```php\necho yii\\grid\\GridView::widget([\n    'dataProvider' => $dataProvider,\n]);\n```\n\nEstos proveedores de datos varían principalmente en la manera en que se especifica la fuente de\ndatos.  En las siguientes secciones se explica el uso detallado de cada uno de estos proveedores\nde datos.\n\n\n## Proveedor de datos activo <span id=\"active-data-provider\"></span>\n\nPara usar [[yii\\data\\ActiveDataProvider]], hay que configurar su propiedad\n[[yii\\data\\ActiveDataProvider::query|query]].\nPuede tomar un objeto [[yii\\db\\Query] o [[yii\\db\\ActiveQuery]].  En el primer caso, los datos\ndevueltos serán _arrays_.  En el segundo, los datos devueltos pueden ser _arrays_ o instancias de\n[Active Record](db-active-record.md).  Por ejemplo:\n\n\n```php\nuse yii\\data\\ActiveDataProvider;\n\n$query = Post::find()->where(['state_id' => 1]);\n\n$provider = new ActiveDataProvider([\n    'query' => $query,\n    'pagination' => [\n        'pageSize' => 10,\n    ],\n    'sort' => [\n        'defaultOrder' => [\n            'created_at' => SORT_DESC,\n            'title' => SORT_ASC,\n        ]\n    ],\n]);\n\n// Devuelve un array de objetos Post\n$posts = $provider->getModels();\n```\n\nEn el ejemplo anterior, si `$query` se crea el siguiente código, el proveedor de datos\ndevolverá _arrays_ en bruto.\n\n```php\nuse yii\\db\\Query;\n\n$query = (new Query())->from('post')->where(['state' => 1]);\n```\n\n> Note: Si una consulta ya tiene la cláusula `orderBy`, las nuevas instrucciones de ordenación\n  dadas por los usuarios finales (mediante la configuración de `sort`) se añadirán a la cláusula\n  `orderBy` previa.  Las cláusulas `limit` y `offset` que pueda haber se sobrescribirán por la\n  petición de paginación de los usuarios finales (mediante la configuración de `pagination`).\n\nPor omisión, [[yii\\data\\ActiveDataProvider]] usa el componente `db` de la aplicación como\nconexión con la base de datos.  Se puede indicar una conexión con base de datos diferente\nconfigurando la propiedad [[yii\\data\\ActiveDataProvider::db]].\n\n\n## Proveedor de datos SQL <span id=\"sql-data-provider\"></span>\n\n[[yii\\data\\SqlDataProvider]] funciona con una sentencia SQL en bruto, que se usa para obtener\nlos datos requeridos.\nBasándose en las especificaciones de [[yii\\data\\SqlDataProvider::sort|sort]] y\n[[yii\\data\\SqlDataProvider::pagination|pagination]], el proveedor ajustará las cláusulas\n`ORDER BY` y `LIMIT` de la sentencia SQL acordemente para obtener sólo la página de datos\nsolicitados en el orden deseado.\n\nPara usar [[yii\\data\\SqlDataProvider]], hay que especificar las propiedades\n[[yii\\data\\SqlDataProvider::sql|sql]] y [[yii\\data\\SqlDataProvider::totalCount|totalCount]].\nPor ejemplo:\n\n```php\nuse yii\\data\\SqlDataProvider;\n\n$count = Yii::$app->db->createCommand('\n    SELECT COUNT(*) FROM post WHERE status=:status\n', [':status' => 1])->queryScalar();\n\n$provider = new SqlDataProvider([\n    'sql' => 'SELECT * FROM post WHERE status=:status',\n    'params' => [':status' => 1],\n    'totalCount' => $count,\n    'pagination' => [\n        'pageSize' => 10,\n    ],\n    'sort' => [\n        'attributes' => [\n            'title',\n            'view_count',\n            'created_at',\n        ],\n    ],\n]);\n\n// Devuelve un array de filas de datos\n$models = $provider->getModels();\n```\n\n> Info: La propiedad [[yii\\data\\SqlDataProvider::totalCount|totalCount]] se requiere sólo si se\n  necesita paginar los datos.  Esto es porque el proveedor modificará la sentencia SQL\n  especificada vía [[yii\\data\\SqlDataProvider::sql|sql]] para que devuelva sólo la pagina de\n  datos solicitada.  El proveedor sigue necesitando saber el número total de elementos de datos\n  para calcular correctamente el número de páginas.\n\n\n## Proveedor de datos de _arrays_ <span id=\"array-data-provider\"></span>\n\nSe recomienda usar [[yii\\data\\ArrayDataProvider]] cuando se trabaja con un _array_ grande.\nEl proveedor permite devolver una página de los datos del _array_ ordenados por una o varias\ncolumnas.  Para usar [[yii\\data\\ArrayDataProvider]], hay que especificar la propiedad\n[[yii\\data\\ArrayDataProvider::allModels|allModels]] como el _array_ grande.  Los elementos\ndel _array_ grande pueden ser _arrays_ asociativos (por ejemplo resultados de consultas de\n[DAO](db-dao.md) u objetos (por ejemplo instancias de [Active Record](db-active-record.md).\nPor ejemplo:\n\n```php\nuse yii\\data\\ArrayDataProvider;\n\n$data = [\n    ['id' => 1, 'name' => 'name 1', ...],\n    ['id' => 2, 'name' => 'name 2', ...],\n    ...\n    ['id' => 100, 'name' => 'name 100', ...],\n];\n\n$provider = new ArrayDataProvider([\n    'allModels' => $data,\n    'pagination' => [\n        'pageSize' => 10,\n    ],\n    'sort' => [\n        'attributes' => ['id', 'name'],\n    ],\n]);\n\n// Obtener las filas de la página solicitada\n$rows = $provider->getModels();\n```\n\n> Note: En comparación con [Active Data Provider](#active-data-provider) y\n  [SQL Data Provider](#sql-data-provider), Array Data Provider es menos eficiente porque\n  requiere cargar *todos* los datos en memoria.\n\n\n## Trabajar con las claves de los datos <span id=\"working-with-keys\"></span>\n\nAl utilizar los elementos de datos devueltos por un proveedor de datos, con frecuencia\nnecesita identificar cada elemento de datos con una clave única.\nPor ejemplo, si los elementos de datos representan información de los clientes, puede querer\nusar el ID de cliente como la clave de cada conjunto de datos de un cliente.\nLos proveedores de datos pueden devolver una lista de estas claves correspondientes a los\nelementos de datos devueltos por [[yii\\data\\DataProviderInterface::getModels()]].\nPor ejemplo:\n\n```php\nuse yii\\data\\ActiveDataProvider;\n\n$query = Post::find()->where(['status' => 1]);\n\n$provider = new ActiveDataProvider([\n    'query' => $query,\n]);\n\n// Devuelve un array de objetos Post\n$posts = $provider->getModels();\n\n// Devuelve los valores de las claves primarias correspondientes a $posts\n$ids = $provider->getKeys();\n```\n\nEn el ejemplo superior, como se le proporciona a [[yii\\data\\ActiveDataProvider]] un objeto\n[[yii\\db\\ActiveQuery]], es lo suficientemente inteligente como para devolver los valores de\nlas claves primarias como las claves.  También puede indicar explícitamente cómo se deben\ncalcular los valores de la clave configurando [[yii\\data\\ActiveDataProvider::key]] con un\nnombre de columna o un invocable que calcule los valores de la clave.  Por ejemplo:\n\n```php\n// Utiliza la columna «slug» como valores de la clave\n$provider = new ActiveDataProvider([\n    'query' => Post::find(),\n    'key' => 'slug',\n]);\n\n// Utiliza el resultado de md5(id) como valores de la clave\n$provider = new ActiveDataProvider([\n    'query' => Post::find(),\n    'key' => function ($model) {\n        return md5($model->id);\n    }\n]);\n```\n\n\n## Creación de un proveedor de datos personalizado <span id=\"custom-data-provider\"></span>\n\nPara crear su propio proveedor de datos personalizado, debe implementar\n[[yii\\data\\DataProviderInterface]].\nUna manera más fácil es extender [[yii\\data\\BaseDataProvider]], que le permite centrarse\nen la lógica central del proveedor de datos.  En particular, esencialmente necesita\nimplementar los siguientes métodos:\n\n- [[yii\\data\\BaseDataProvider::prepareModels()|prepareModels()]]: prepara los modelos\n  de datos que estarán disponibles en la página actual y los devuelve como un _array_.\n- [[yii\\data\\BaseDataProvider::prepareKeys()|prepareKeys()]]: acepta un _array_ de\n  modelos de datos disponibles actualmente y devuelve las claves asociadas a ellos.\n- [[yii\\data\\BaseDataProvider::prepareTotalCount()|prepareTotalCount]]: devuelve un valor\n  que indica el número total de modelos de datos en el proveedor de datos.\n\nDebajo se muestra un ejemplo de un proveedor de datos que lee datos CSV eficientemente:\n\n```php\n<?php\nuse yii\\data\\BaseDataProvider;\n\nclass CsvDataProvider extends BaseDataProvider\n{\n    /**\n     * @var string nombre del fichero CSV a leer\n     */\n    public $filename;\n\n    /**\n     * @var string|callable nombre de la columna clave o un invocable que la devuelva\n     */\n    public $key;\n\n    /**\n     * @var SplFileObject\n     */\n    protected $fileObject;  // SplFileObject es muy práctico para buscar una línea concreta en un fichero\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n\n        // Abrir el fichero\n        $this->fileObject = new SplFileObject($this->filename);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function prepareModels()\n    {\n        $models = [];\n        $pagination = $this->getPagination();\n\n        if ($pagination === false) {\n            // En caso de que no haya paginación, leer todas las líneas\n            while (!$this->fileObject->eof()) {\n                $models[] = $this->fileObject->fgetcsv();\n                $this->fileObject->next();\n            }\n        } else {\n            // En caso de que haya paginación, leer sólo una única página\n            $pagination->totalCount = $this->getTotalCount();\n            $this->fileObject->seek($pagination->getOffset());\n            $limit = $pagination->getLimit();\n\n            for ($count = 0; $count < $limit; ++$count) {\n                $models[] = $this->fileObject->fgetcsv();\n                $this->fileObject->next();\n            }\n        }\n\n        return $models;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function prepareKeys($models)\n    {\n        if ($this->key !== null) {\n            $keys = [];\n\n            foreach ($models as $model) {\n                if (is_string($this->key)) {\n                    $keys[] = $model[$this->key];\n                } else {\n                    $keys[] = call_user_func($this->key, $model);\n                }\n            }\n\n            return $keys;\n        }\n\n        return array_keys($models);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function prepareTotalCount()\n    {\n        $count = 0;\n\n        while (!$this->fileObject->eof()) {\n            $this->fileObject->next();\n            ++$count;\n        }\n\n        return $count;\n    }\n}\n```\n\n## Filtrar proveedores de datos usando filtros de datos <span id=\"filtering-data-providers-using-data-filters\"></span>\n\nSi bien puede construir condiciones para un proveedor de datos activo manualmente tal\ny como se describe en las secciones [Filtering Data](output-data-widgets.md#filtering-data)\ny [Separate Filter Form](output-data-widgets.md#separate-filter-form) de la guía de\n_widgets_ de datos, Yii tiene filtros de datos que son muy útiles si necesita\ncondiciones de filtro flexibles.  Los filtros de datos se pueden usar así:\n\n```php\n$filter = new ActiveDataFilter([\n    'searchModel' => 'app\\models\\PostSearch'\n]);\n\n$filterCondition = null;\n\n// Puede cargar los filtros de datos de cualquier fuente.\n// Por ejemplo, si prefiere JSON en el cuerpo de la petición,\n// use Yii::$app->request->getBodyParams() aquí abajo:\nif ($filter->load(\\Yii::$app->request->get())) {\n    $filterCondition = $filter->build();\n    if ($filterCondition === false) {\n        // Serializer recibiría errores\n        return $filter;\n    }\n}\n\n$query = Post::find();\nif ($filterCondition !== null) {\n    $query->andWhere($filterCondition);\n}\n\nreturn new ActiveDataProvider([\n    'query' => $query,\n]);\n```\n\nEl propósito del modelo `PostSearch` es definir por qué propiedades y valores se permite filtrar:\n\n```php\nuse yii\\base\\Model;\n\nclass PostSearch extends Model\n{\n    public $id;\n    public $title;\n\n    public function rules()\n    {\n        return [\n            ['id', 'integer'],\n            ['title', 'string', 'min' => 2, 'max' => 200],\n        ];\n    }\n}\n```\n\nLos filtros de datos son bastante flexibles.  Puede personalizar cómo se construyen\nlas condiciones y qué operadores se permiten.\nPara más detalles consulte la documentación de la API en [[\\yii\\data\\DataFilter]].\n"
  },
  {
    "path": "docs/guide-es/output-data-widgets.md",
    "content": "Widgets de datos\n================\n\nYii proporciona un conjunto de [widgets](structure-widgets.md) que se pueden usar para mostrar datos.\nMientras que el _widget_ [DetailView](#detail-view) se puede usar para mostrar los datos de un único\nregistro, [ListView](#list-view) y [GridView](#grid-view) se pueden usar para mostrar una lista o\ntabla de registros de datos proporcionando funcionalidades como paginación, ordenación y filtro.\n\n\nDetailView <span id=\"detail-view\"></span>\n----------\n\nEl _widget_ [[yii\\widgets\\DetailView|DetailView]] muestra los detalles de un único\n[[yii\\widgets\\DetailView::$model|modelo]] de datos.\n\nSe recomienda su uso para mostrar un modelo en un formato estándar (por ejemplo, cada atributo del\nmodelo se muestra como una fila en una tabla).  El modelo puede ser tanto una instancia o subclase\nde [[\\yii\\base\\Model]] como un [active record](db-active-record.md) o un _array_ asociativo.\n\nDetailView usa la propiedad [[yii\\widgets\\DetailView::$attributes|$attributes]] para determinar\nqué atributos del modelo se deben mostrar y cómo se deben formatear.\nEn la [sección sobre formateadores](output-formatting.html) se pueden ver las opciones de formato\ndisponibles.\n\nUn uso típico de DetailView sería así:\n\n```php\necho DetailView::widget([\n    'model' => $model,\n    'attributes' => [\n        'title',                                           // atributo title (en texto plano)\n        'description:html',                                // atributo description formateado como HTML\n        [                                                  // nombre del propietario del modelo\n            'label' => 'Owner',\n            'value' => $model->owner->name,\n            'contentOptions' => ['class' => 'bg-red'],     // atributos HTML para personalizar el valor\n            'captionOptions' => ['tooltip' => 'Tooltip'],  // atributos HTML para personalizar la etiqueta\n        ],\n        'created_at:datetime',                             // fecha de creación formateada como datetime\n    ],\n]);\n```\n\nRecuerde que a diferencia de [[yii\\widgets\\GridView|GridView]], que procesa un conjunto de modelos,\n[[yii\\widgets\\DetailView|DetailView]] sólo procesa uno.  Así que la mayoría de las veces no hay\nnecesidad de usar funciones anónimas ya que `$model` es el único modelo a mostrar y está disponible\nen la vista como una variable.\n\nSin embargo, en algunos casos el uso de una función anónima puede ser útil.  Por ejemplo cuando\n`visible` está especificado y se desea impedir el cálculo de `value` en case de que evalúe a `false`:\n\n```php\necho DetailView::widget([\n    'model' => $model,\n    'attributes' => [\n        [\n            'attribute' => 'owner',\n            'value' => function ($model) {\n                return $model->owner->name;\n            },\n            'visible' => \\Yii::$app->user->can('posts.owner.view'),\n        ],\n    ],\n]);\n```\n\n\nListView <span id=\"list-view\"></span>\n--------\n\nEl _widget_ [[yii\\widgets\\ListView|ListView]] se usa para mostrar datos de un\n[proveedor de datos](output-data-providers.md).\nCada modelo de datos se representa usando el [[yii\\widgets\\ListView::$itemView|fichero de vista]]\nindicado.\nComo proporciona de serie funcionalidades tales como paginación, ordenación y filtro,\nes útil tanto para mostrar información al usuario final como para crear una interfaz\nde usuario de gestión de datos.\n\nUn uso típico es el siguiente:\n\n```php\nuse yii\\widgets\\ListView;\nuse yii\\data\\ActiveDataProvider;\n\n$dataProvider = new ActiveDataProvider([\n    'query' => Post::find(),\n    'pagination' => [\n        'pageSize' => 20,\n    ],\n]);\n\necho ListView::widget([\n    'dataProvider' => $dataProvider,\n    'itemView' => '_post',\n]);\n```\n\nEl fichero de vista `_post` podría contener lo siguiente:\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\helpers\\HtmlPurifier;\n?>\n<div class=\"tarea\">\n    <h2><?= Html::encode($model->title) ?></h2>\n\n    <?= HtmlPurifier::process($model->text) ?>\n</div>\n```\n\nEn el fichero de vista anterior, el modelo de datos actual está disponible como `$model`.\nAdemás están disponibles las siguientes variables:\n\n- `$key`: mixto, el valor de la clave asociada a este elemento de datos.\n- `$index`: entero, el índice empezando por cero del elemento de datos en el array de elementos devuelto por el proveedor de datos.\n- `$widget`: ListView, esta instancia del _widget_.\n\nSi se necesita pasar datos adicionales a cada vista, se puede usar la propiedad\n[[yii\\widgets\\ListView::$viewParams|$viewParams]] para pasar parejas clave-valor como las siguientes:\n\n```php\necho ListView::widget([\n    'dataProvider' => $dataProvider,\n    'itemView' => '_post',\n    'viewParams' => [\n        'fullView' => true,\n        'context' => 'main-page',\n        // ...\n    ],\n]);\n```\n\nEntonces éstas también estarán disponibles en la vista como variables.\n\n\nGridView <span id=\"grid-view\"></span>\n--------\n\nLa cuadrícula de datos o [[yii\\grid\\GridView|GridView]] es uno de los _widgets_ de Yii\nmás potentes.  Es extremadamente útil si necesita construir rápidamente la sección de\nadministración del sistema.  Recibe los datos de un [proveedor de datos](output-data-providers.md)\ny representa cada fila usando un conjunto de [[yii\\grid\\GridView::columns|columnas]]\nque presentan los datos en forma de tabla.\n\nCada fila de la tabla representa los datos de un único elemento de datos, y una columna\nnormalmente representa un atributo del elemento (algunas columnas pueden corresponder a\nexpresiones complejas de los atributos o a un texto estático).\n\nEl mínimo código necesario para usar GridView es como sigue:\n\n```php\nuse yii\\grid\\GridView;\nuse yii\\data\\ActiveDataProvider;\n\n$dataProvider = new ActiveDataProvider([\n    'query' => Post::find(),\n    'pagination' => [\n        'pageSize' => 20,\n    ],\n]);\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n]);\n```\n\nEl código anterior primero crea un proveedor de datos y a continuación usa GridView\npara mostrar cada atributo en cada fila tomados del proveedor de datos.  La tabla\nmostrada está equipada de serie con las funcionalidades de ordenación y paginación.\n\n\n### Columnas de la cuadrícula <span id=\"grid-columns\"></span>\n\nLas columnas de la tabla se configuran en términos de clase [[yii\\grid\\Column]], que\nse configuran en la propiedad [[yii\\grid\\GridView::columns|columns]] de la configuración\ndel GridView.\nDependiendo del tipo y ajustes de las columnas éstas pueden presentar los datos de\ndiferentes maneras.\nLa clase predefinida es [[yii\\grid\\DataColumn]], que representa un atributo del modelo\npor el que se puede ordenar y filtrar.\n\n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'columns' => [\n        ['class' => 'yii\\grid\\SerialColumn'],\n        // Columnas sencillas definidas por los datos contenidos en $dataProvider.\n        // Se usarán los datos de la columna del modelo.\n        'id',\n        'username',\n        // Un ejemplo más complejo.\n        [\n            'class' => 'yii\\grid\\DataColumn',  // Se puede omitir, ya que es la predefinida.\n            'value' => function ($data) {\n                return $data->name;  // $data['name'] para datos de un array, por ejemplo al usar SqlDataProvider.\n            },\n        ],\n    ],\n]);\n```\n\nObserve que si no se especifica la parte [[yii\\grid\\GridView::columns|columns]] de la\nconfiguración, Yii intenta mostrar todas las columnas posibles del modelo del proveedor\nde datos.\n\n\n### Clases de columna <span id=\"column-classes\"></span>\n\nLas columnas de la cuadrícula se pueden personalizar usando diferentes clases de columna:\n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'columns' => [\n        [\n            'class' => 'yii\\grid\\SerialColumn', // <-- aquí\n            // puede configurar propiedades adicionales aquí\n        ],\n```\n\nAdemás de las clases de columna proporcionadas por Yii que se revisarán más abajo,\npuede crear sus propias clases de columna.\n\nCada clase de columna extiende [[yii\\grid\\Column]] de modo que hay algunas opciones\ncomunes que puede establecer al configurar las columnas de una cuadrícula.\n\n- [[yii\\grid\\Column::header|header]] permite establecer el contenida para la fila cabecera\n- [[yii\\grid\\Column::footer|footer]] permite establece el contenido de la fila al pie\n- [[yii\\grid\\Column::visible|visible]] define si la columna debería ser visible.\n- [[yii\\grid\\Column::content|content]] le permite pasar una función PHP válida que devuelva datos para una fila.  El formato es el siguiente:\n\n  ```php\n  function ($model, $key, $index, $column) {\n      return 'una cadena';\n  }\n  ```\n\nPuede indicar varias opciones HTML del contenedor pasando _arrays_ a:\n\n- [[yii\\grid\\Column::headerOptions|headerOptions]]\n- [[yii\\grid\\Column::footerOptions|footerOptions]]\n- [[yii\\grid\\Column::filterOptions|filterOptions]]\n- [[yii\\grid\\Column::contentOptions|contentOptions]]\n\n"
  },
  {
    "path": "docs/guide-es/output-pagination.md",
    "content": "Paginación\n==========\n\nCuando hay muchos datos a mostrar en una sola página, una estrategia común es mostrarlos en varias\npáginas y en cada una de ellas mostrar sólo una pequeña porción de datos. Esta estrategia es conocida como *paginación*.\n\nYii utiliza el objeto [[yii\\data\\Pagination]] para representar la información acerca del esquema de paginación. En particular,\n\n* [[yii\\data\\Pagination::$totalCount|cuenta total]] especifica el número total de ítems de datos. Ten en cuenta que\n  este es normalmente un número mucho mayor que el número de ítems necesarios a mostrar en una simple página.\n* [[yii\\data\\Pagination::$pageSize|tamaño de página]] especifica cuántos ítems de datos contiene cada página. El valor\n  por defecto es 20.\n* [[yii\\data\\Pagination::$page|página actual]] da el número de la página actual (comenzando desde 0). El valor\n  por defecto es 0, lo que sería la primera página.\n\nCon un objeto [[yii\\data\\Pagination]] totalmente especificado, puedes obtener y mostrar datos en partes. Por ejemplo,\nsi estás recuperando datos de una base de datos, puedes especificar las cláusulas `OFFSET` y `LIMIT` de la consulta a la BD\ncorrespondientes a los valores provistos por la paginación. A continuación hay un ejemplo, \n\n```php\nuse yii\\data\\Pagination;\n\n// construye una consulta a la BD para obtener todos los artículos con status = 1\n$query = Article::find()->where(['status' => 1]);\n\n// obtiene el número total de artículos (pero no recupera los datos de los artículos todavía)\n$count = $query->count();\n\n// crea un objeto paginación con dicho total\n$pagination = new Pagination(['totalCount' => $count]);\n\n// limita la consulta utilizando la paginación y recupera los artículos\n$articles = $query->offset($pagination->offset)\n    ->limit($pagination->limit)\n    ->all();\n```\n\n¿Qué página de artículos devolverá el ejemplo de arriba? Depende de si se le es pasado un parámetro  llamado `page`.\nPor defecto, la paginación intentará definir la [[yii\\data\\Pagination::$page|página actual]] con\nel valor del parámetro `page`. Si el parámetro no es provisto, entonces tomará por defecto el valor 0.\n\nPara facilitar la construcción de elementos UI que soporten paginación, Yii provee el widget [[yii\\widgets\\LinkPager]],\nque muestra una lista de botones de navegación que el usuario puede presionar para indicar qué página de datos debería mostrarse.\nEl widget toma un objeto de paginación y tal manera conoce cuál es la página actual y cuántos botones\ndebe mostrar. Por ejemplo,\n\n```php\nuse yii\\widgets\\LinkPager;\n\necho LinkPager::widget([\n    'pagination' => $pagination,\n]);\n```\n\nSi quieres construir los elementos de UI manualmente, puedes utilizar [[yii\\data\\Pagination::createUrl()]] para generar URLs que\ndirigirán a las distintas páginas. El método requiere un parámetro de página y generará una URL apropiadamente formada\ncontieniendo el parámetro de página. Por ejemplo,\n\n```php\n// especifica la ruta que la URL generada debería utilizar\n// Si no lo especificas, se utilizará la ruta de la petición actual\n$pagination->route = 'article/index';\n\n// muestra: /index.php?r=article%2Findex&page=100\necho $pagination->createUrl(100);\n\n// muestra: /index.php?r=article%2Findex&page=101\necho $pagination->createUrl(101);\n```\n\n> Tip: puedes personalizar el parámetro `page` de la consulta configurando\n  la propiedad [[yii\\data\\Pagination::pageParam|pageParam]] al crear el objeto de la paginación.\n"
  },
  {
    "path": "docs/guide-es/output-theming.md",
    "content": "Temas\n=====\n\n> Note: Esta sección está en desarrollo.\n\nUn tema (theme) es un directorio de archivos y de vistas (views) y layouts. Cada archivo de este directorio \nsobrescribe el archivo correspondiente de una aplicación cuando se renderiza. Una única aplicación puede usar \nmúltiples temas para que pueden proporcionar experiencias totalmente diferentes. Solo se puede haber un único tema \nactivo.\n\n> Note: Los temas no están destinados a ser redistribuidos ya que están demasiado ligados a la aplicación. Si se \n  quiere redistribuir una apariencia personalizada, se puede considerar la opción de \n  [asset bundles](structure-assets.md) de archivos CSS y Javascript.\n\nConfiguración de un Tema\n------------------------\n\nLa configuración de un tema se especifica a través del componente `view` de la aplicación. Para establecer que un tema \ntrabaje con vistas de aplicación básicas, la configuración de la aplicación debe contener lo siguiente:\n\n```php\n'components' => [\n    'view' => [\n        'theme' => [\n            'pathMap' => ['@app/views' => '@app/themes/basic'],\n            'baseUrl' => '@web/themes/basic',\n        ],\n    ],\n],\n```\n\nEn el ejemplo anterior, el `pathMap` define un mapa (map) de las rutas a las que se aplicará el tema mientras que \n`baseUrl` define la URL base para los recursos a los que hacen referencia los archivos del tema.\n\nEn nuestro caso `pathMap` es `['@app/views' => '@app/themes/basic']`. Esto significa que cada vista de `@app/views` \nprimero se buscará en `@app/themes/basic` y si existe, se usará la vista del directorio del tema en lugar de la vista \noriginal.\n\nPor ejemplo, con la configuración anterior, la versión del tema para la vista `@app/views/site/index.php` será \n`@app/themes/basic/site/index.php`. Básicamente se reemplaza `@app/views` en `@app/views/site/index.php` por \n`@app/themes/basic`.\n\n### Temas para Módulos\n\nPara utilizar temas en los módulos, el `pathMap` debe ser similar al siguiente:\n\n```php\n'components' => [\n    'view' => [\n        'theme' => [\n            'pathMap' => [\n                '@app/views' => '@app/themes/basic',\n                '@app/modules' => '@app/themes/basic/modules', // <-- !!!\n            ],\n        ],\n    ],\n],\n```\n\nEsto permite aplicar el tema a `@app/modules/blog/views/comment/index.php` con la vista \n`@app/themes/basic/modules/blog/views/comment/index.php`.\n\n### Temas para Widgets\n\nPara utilizar un tema en una vista que se encuentre en `@app/widgets/currency/views/index.php`, se debe aplicar la \nsiguiente configuración para el componente vista, tema:\n\n```php\n'components' => [\n    'view' => [\n        'theme' => [\n            'pathMap' => ['@app/widgets' => '@app/themes/basic/widgets'],\n        ],\n    ],\n],\n```\n\nCon la configuración anterior, se puede crear una versión de la vista `@app/widgets/currency/index.php` para que se \naplique el tema en `@app/themes/basic/widgets/currency/views/index.php`.\n\nUso de Multiples Rutas\n----------------------\n\nEs posible mapear una única ruta a múltiples rutas de temas. Por ejemplo:\n\n```php\n'pathMap' => [\n    '@app/views' => [\n        '@app/themes/christmas',\n        '@app/themes/basic',\n    ],\n]\n```\n\nEn este caso, primero se buscara la vista en `@app/themes/christmas/site/index.php`, si no se encuentra, se intentará \nen `@app/themes/basic/site/index.php`. Si la vista no se encuentra en ninguna de rutas especificadas, se usará la \nvista de aplicación.\n\nEsta capacidad es especialmente útil si se quieren sobrescribir algunas rutas temporal o condicionalmente.\n"
  },
  {
    "path": "docs/guide-es/rest-authentication.md",
    "content": "Autenticación\n=============\n\nA diferencia de las aplicaciones Web, las API RESTful son usualmente sin estado (stateless), lo que permite que las sesiones o las cookies\nno sean usadas. Por lo tanto, cada petición debe llevar alguna suerte de credenciales de autenticación,\nporque la autenticación del usuario no puede ser mantenida por las sesiones o las cookies. Una práctica común\nes enviar una pieza (token) secreta de acceso con cada petición para autenticar al usuario. Dado que una pieza de autenticación\npuede ser usada para identificar y autenticar solamente a un usuario, **la API de peticiones tiene que ser siempre enviado\nvía HTTPS para prevenir ataques tipo \"man-in-the-middle\" (MitM) **.\n\nHay muchas maneras de enviar una token (pieza) de acceso:\n\n* [Autenticación Básica HTTP](https://es.wikipedia.org/wiki/Autenticaci%C3%B3n_de_acceso_b%C3%A1sica): la pieza de acceso\n  es enviada como nombre de usuario. Esto sólo debe de ser usado cuando la pieza de acceso puede ser guardada\n  de forma segura en la parte del API del consumidor. Por ejemplo, el API del consumidor es un programa ejecutándose en un servidor.\n* Parámetro de la consulta: la pieza de acceso es enviada como un parámetro de la consulta en la URL de la API, p.e.,\n  `https://example.com/users?access-token=xxxxxxxx`. Debido que muchos servidores dejan los parámetros de consulta en los logs del servidor,\n  esta aproximación suele ser usada principalmente para servir peticiones `JSONP`\n  que no usen las cabeceras HTTP para enviar piezas de acceso.\n* [OAuth 2](https://oauth.net/2/): la pieza de acceso es obtenida por el consumidor por medio de una autorización del servidor\n  y enviada al API del servidor según el protocolo\n  OAuth 2 [tokens HTTP del portador](https://datatracker.ietf.org/doc/html/rfc6750).\n\nYii soporta todos los métodos anteriores de autenticación. Puedes crear nuevos métodos de autenticación de una forma fácil.\n\nPara activar la autenticación para tus APIs, sigue los pasos siguientes:\n\n1. Configura el componente `user` de la aplicación:\n   - Define la propiedad [[yii\\web\\User::enableSession|enableSession]] como `false`.\n   - Define la propiedad [[yii\\web\\User::loginUrl|loginUrl]] como `null` para mostrar un error HTTP 403 en vez de redireccionar a la pantalla de login. \n2. Especifica cuál método de autenticación planeas usar configurando el comportamiento (behavior) `authenticator` en tus\n   clases de controladores REST.\n3. Implementa [[yii\\web\\IdentityInterface::findIdentityByAccessToken()]] en tu [[yii\\web\\User::identityClass|clase de identidad de usuarios]].\n\nEl paso 1 no es necesario pero sí recomendable para las APIs RESTful, pues son sin estado (stateless).\nCuando [[yii\\web\\User::enableSession|enableSession]] es `false`, el estado de autenticación del usuario puede NO persistir entre peticiones usando sesiones.\nSi embargo, la autenticación será realizada para cada petición, lo que se consigue en los pasos 2 y 3.\n\n> Tip:Puedes configurar [[yii\\web\\User::enableSession|enableSession]] del componente de la aplicación `user` en la configuración\n> de las aplicaciones si estás desarrollando APIs RESTful en términos de un aplicación. Si desarrollas un módulo de las APIs RESTful,\n> puedes poner la siguiente línea en el método del módulo `init()`, tal y como sigue:\n>\n> ```php\n> public function init()\n> {\n>     parent::init();\n>     \\Yii::$app->user->enableSession = false;\n> }\n> ```\n\nPor ejemplo, para usar HTTP Basic Auth, puedes configurar el comportamiento (behavior) `authenticator` como sigue,\n\n```php\nuse yii\\filters\\auth\\HttpBasicAuth;\n\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n    $behaviors['authenticator'] = [\n        'class' => HttpBasicAuth::class,\n    ];\n    return $behaviors;\n}\n```\n\nSi quieres implementar los tres métodos de autenticación explicados antes, puedes usar `CompositeAuth` de la siguiente manera,\n\n```php\nuse yii\\filters\\auth\\CompositeAuth;\nuse yii\\filters\\auth\\HttpBasicAuth;\nuse yii\\filters\\auth\\HttpBearerAuth;\nuse yii\\filters\\auth\\QueryParamAuth;\n\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n    $behaviors['authenticator'] = [\n        'class' => CompositeAuth::class,\n        'authMethods' => [\n            HttpBasicAuth::class,\n            HttpBearerAuth::class,\n            QueryParamAuth::class,\n        ],\n    ];\n    return $behaviors;\n}\n```\n\nCada elemento en `authMethods` debe de ser el nombre de una clase de método de autenticación o un array de configuración.\n\n\nLa implementación de `findIdentityByAccessToken()` es específico de la aplicación. Por ejemplo, en escenarios simples\ncuando cada usuario sólo puede tener un token de acceso, puedes almacenar este token en la columna `access_token`\nen la tabla de usuario. El método debe de ser inmediatamente implementado en la clase  `User` como sigue,\n\n```php\nuse yii\\db\\ActiveRecord;\nuse yii\\web\\IdentityInterface;\n\nclass User extends ActiveRecord implements IdentityInterface\n{\n    public static function findIdentityByAccessToken($token, $type = null)\n    {\n        return static::findOne(['access_token' => $token]);\n    }\n}\n```\n\nDespués que la autenticación es activada, tal y como se describe arriba, para cada petición de la API, el controlador solicitado\npuede intentar autenticar al usuario en su evento `beforeAction()`.\n\nSi la autenticación tiene éxito, el controlador realizará otras comprobaciones (como son límite del ratio, autorización)\ny entonces ejecutar la acción. La identidad del usuario autenticado puede ser recuperada via `Yii::$app->user->identity`.\n\nSi la autenticación falla, una respuesta con estado HTTP 401 será devuelta junto con otras cabeceras apropiadas\n(tal como la cabecera para autenticación básica HTTP `WWW-Authenticate`).\n\n\n## Autorización <span id=\"authorization\"></span>\n\nDespués de que un usuario se ha autenticado, probablementer querrás comprobar si él o ella tiene los permisos para realizar\nla acción solicitada. Este proceso es llamado *autorización (authorization)* y está cubierto en detalle\nen la [Sección de Autorización](security-authorization.md).\n\nSi tus controladores extienden de [[yii\\rest\\ActiveController]], puedes sobreescribir\nel método [[yii\\rest\\ActiveController::checkAccess()|checkAccess()]] para realizar la comprobación de la autorización.\nEl método será llamado por las acciones contenidas en [[yii\\rest\\ActiveController]].\n"
  },
  {
    "path": "docs/guide-es/rest-controllers.md",
    "content": "Controladores\n=============\n\nDespués de crear las clases de recursos y especificar cómo debe ser el formato de datos de recursos, el siguiente paso\nes crear acciones del controlador para exponer los recursos a los usuarios finales a través de las APIs RESTful.\n\nYii ofrece dos clases controlador base para simplificar tu trabajo de crear acciones REST:\n[[yii\\rest\\Controller]] y [[yii\\rest\\ActiveController]]. La diferencia entre estos dos controladores\nes que este último proporciona un conjunto predeterminado de acciones que están específicamente diseñado para trabajar con\nlos recursos representados como [Active Record](db-active-record.md). Así que si estás utilizando [Active Record](db-active-record.md)\ny te sientes cómodo con las acciones integradas provistas, podrías considerar extender tus controladores\nde [[yii\\rest\\ActiveController]], lo que te permitirá crear potentes APIs RESTful con un mínimo de código.\n\nAmbos [[yii\\rest\\Controller]] y [[yii\\rest\\ActiveController]] proporcionan las siguientes características,\nalgunas de las cuales se describen en detalle en las siguientes secciones:\n\n* Método de Validación HTTP;\n* [Negociación de contenido y formato de datos](rest-response-formatting.md);\n* [Autenticación](rest-authentication.md);\n* [Límite de Rango](rest-rate-limiting.md).\n\n[[yii\\rest\\ActiveController]] además provee de las siguientes características:\n\n* Un conjunto de acciones comunes necesarias: `index`, `view`, `create`, `update`, `delete`, `options`;\n* La autorización del usuario de acuerdo a la acción y recurso solicitado.\n\n\n## Creando Clases de Controlador <span id=\"creating-controller\"></span>\n\nAl crear una nueva clase de controlador, una convención para nombrar la clase del controlador es utilizar\nel nombre del tipo de recurso en singular. Por ejemplo, para servir información de usuario,\nel controlador puede ser nombrado como `UserController`.\n\nCrear una nueva acción es similar a crear una acción para una aplicación Web. La única diferencia\nes que en lugar de renderizar el resultado utilizando una vista llamando al método `render()`, para las acciones REST\nregresas directamente los datos. El [[yii\\rest\\Controller::serializer|serializer]] y el\n[[yii\\web\\Response|response object]] se encargarán de la conversión de los datos originales\nal formato solicitado. Por ejemplo,\n\n```php\npublic function actionView($id)\n{\n    return User::findOne($id);\n}\n```\n\n\n## Filtros <span id=\"filters\"></span>\n\nLa mayoría de las características API REST son proporcionadas por [[yii\\rest\\Controller]] son implementadas en los términos de [filtros](structure-filters.md).\nEn particular, los siguientes filtros se ejecutarán en el orden en que aparecen:\n\n* [[yii\\filters\\ContentNegotiator|contentNegotiator]]: soporta la negociación de contenido, que se explica en\n  la sección [Formateo de respuestas](rest-response-formatting.md);\n* [[yii\\filters\\VerbFilter|verbFilter]]: soporta métodos de validación HTTP;\n* [[yii\\filters\\auth\\AuthMethod|authenticator]]: soporta la autenticación de usuarios, que se explica en\n  la sección [Autenticación](rest-authentication.md);\n* [[yii\\filters\\RateLimiter|rateLimiter]]: soporta la limitación de rango, que se explica en\n  la sección [Límite de Rango](rest-rate-limiting.md).\n\nEstos filtros se declaran nombrándolos en el método [[yii\\rest\\Controller::behaviors()|behaviors()]].\nPuede sobrescribir este método para configurar filtros individuales, desactivar algunos de ellos, o añadir los tuyos.\nPor ejemplo, si sólo deseas utilizar la autenticación básica HTTP, puede escribir el siguiente código:\n\n```php\nuse yii\\filters\\auth\\HttpBasicAuth;\n\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n    $behaviors['authenticator'] = [\n        'class' => HttpBasicAuth::class,\n    ];\n    return $behaviors;\n}\n```\n\n\n## Extendiendo `ActiveController` <span id=\"extending-active-controller\"></span>\n\nSi tu clase controlador extiende de [[yii\\rest\\ActiveController]], debe establecer\nsu propiedad [[yii\\rest\\ActiveController::modelClass|modelClass]] con el nombre de la clase del recurso\nque planeas servir a través de este controlador. La clase debe extender de [[yii\\db\\ActiveRecord]].\n\n\n### Personalizando Acciones <span id=\"customizing-actions\"></span>\n\nPor defecto, [[yii\\rest\\ActiveController]] provee de las siguientes acciones:\n\n* [[yii\\rest\\IndexAction|index]]: listar recursos página por página;\n* [[yii\\rest\\ViewAction|view]]: devolver el detalle de un recurso específico;\n* [[yii\\rest\\CreateAction|create]]: crear un nuevo recurso;\n* [[yii\\rest\\UpdateAction|update]]: actualizar un recurso existente;\n* [[yii\\rest\\DeleteAction|delete]]: eliminar un recurso específico;\n* [[yii\\rest\\OptionsAction|options]]: devolver los métodos HTTP soportados.\n\nTodas esta acciones se declaran a través de método [[yii\\rest\\ActiveController::actions()|actions()]].\nPuedes configurar estas acciones o desactivar alguna de ellas sobrescribiendo el método `actions()`, como se muestra a continuación,\n\n```php\npublic function actions()\n{\n    $actions = parent::actions();\n\n    // disable the \"delete\" and \"create\" actions\n    unset($actions['delete'], $actions['create']);\n\n    // customize the data provider preparation with the \"prepareDataProvider()\" method\n    $actions['index']['prepareDataProvider'] = [$this, 'prepareDataProvider'];\n\n    return $actions;\n}\n\npublic function prepareDataProvider()\n{\n    // prepare and return a data provider for the \"index\" action\n}\n```\n\nPor favor, consulta las referencias de clases de acciones individuales para aprender las opciones de configuración disponibles para cada una.\n\n\n### Realizando Comprobación de Acceso <span id=\"performing-access-check\"></span>\n\nAl exponer los recursos a través de RESTful APIs, a menudo es necesario comprobar si el usuario actual tiene permiso\npara acceder y manipular el/los recurso solicitado/s. Con [[yii\\rest\\ActiveController]], esto puede lograrse\nsobrescribiendo el método [[yii\\rest\\ActiveController::checkAccess()|checkAccess()]] como a continuación, \n\n```php\n/**\n * Checks the privilege of the current user.\n *\n * This method should be overridden to check whether the current user has the privilege\n * to run the specified action against the specified data model.\n * If the user does not have access, a [[ForbiddenHttpException]] should be thrown.\n *\n * @param string $action the ID of the action to be executed\n * @param \\yii\\base\\Model $model the model to be accessed. If `null`, it means no specific model is being accessed.\n * @param array $params additional parameters\n * @throws ForbiddenHttpException if the user does not have access\n */\npublic function checkAccess($action, $model = null, $params = [])\n{\n    // check if the user can access $action and $model\n    // throw ForbiddenHttpException if access should be denied\n    if ($action === 'update' || $action === 'delete') {\n        if ($model->author_id !== \\Yii::$app->user->id)\n            throw new \\yii\\web\\ForbiddenHttpException(sprintf('You can only %s articles that you\\'ve created.', $action));\n    }\n}\n```\n\nEl método `checkAccess()` será llamado por defecto en las acciones predeterminadas de [[yii\\rest\\ActiveController]]. Si creas\nnuevas acciones y también deseas llevar a cabo la comprobación de acceso, debe llamar a este método de forma explícita en las nuevas acciones.\n\n> Tip: Puedes implementar `checkAccess()` mediante el uso del [Componente Role-Based Access Control (RBAC)](security-authorization.md).\n"
  },
  {
    "path": "docs/guide-es/rest-error-handling.md",
    "content": "Manejo de errores\n=================\n\nCuando se maneja una petición de API RESTful, si ocurre un error en la petición del usuario o si algo inesperado\nocurre en el servidor, simplemente puedes lanzar una excepción para notificar al usuario que algo erróneo ocurrió.\nSi puedes identificar la causa del error (p.e., el recurso solicitado no existe), debes considerar lanzar una excepción\ncon el código HTTP de estado apropiado (p.e., [[yii\\web\\NotFoundHttpException]] representa un código de estado 404).\nYii enviará la respuesta a continuación con el correspondiente código de estado HTTP y el texto. Yii puede incluir también\nla representación serializada de la excepción en el cuerpo de la respuesta.\nPor ejemplo:\n\n```\nHTTP/1.1 404 Not Found\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"name\": \"Not Found Exception\",\n    \"message\": \"The requested resource was not found.\",\n    \"code\": 0,\n    \"status\": 404\n}\n```\n\nLa siguiente lista sumariza los códigos de estado HTTP que son usados por el framework REST:\n\n* `200`: OK. Todo ha funcionado como se esperaba.\n* `201`: El recurso ha creado con éxito en respuesta a la petición `POST`. La cabecera de situación `Location`\n   contiene la URL apuntando al nuevo recurso creado.\n* `204`: La petición ha sido manejada con éxito y el cuerpo de la respuesta no tiene contenido (como una petición `DELETE`).\n* `304`: El recurso no ha sido modificado. Puede usar la versión en caché.\n* `400`: Petición errónea. Esto puede estar causado por varias acciones de el usuario, como proveer un JSON no válido\n   en el cuerpo de la petición, proveyendo parámetros de acción no válidos, etc.\n* `401`: Autenticación fallida.\n* `403`: El usuario autenticado no tiene permitido acceder a la API final.\n* `404`: El recurso pedido no existe.\n* `405`: Método no permitido. Por favor comprueba la cabecera `Allow` por los métodos HTTP permitidos.\n* `415`: Tipo de medio no soportado. El tipo de contenido pedido o el número de versión no es válido.\n* `422`: La validación de datos ha fallado (en respuesta a una petición `POST` , por ejemplo). Por favor, comprueba en el cuerpo de la respuesta el mensaje detallado.\n* `429`: Demasiadas peticiones. La petición ha sido rechazada debido a un limitación de rango.\n* `500`: Error interno del servidor. Esto puede estar causado por errores internos del programa.\n\n\n## Personalizar la Respuesta al Error <span id=\"customizing-error-response\"></span>\n\nA veces puedes querer personalizar el formato de la respuesta del error por defecto . Por ejemplo, en lugar de depender\ndel uso de diferentes estados HTTP para indicar los diferentes errores, puedes querer usar siempre el estado HTTP 200\ny encapsular el código de estado HTTP real como parte de una estructura JSON en la respuesta, como se muestra a continuación,\n\n```\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"success\": false,\n    \"data\": {\n        \"name\": \"Not Found Exception\",\n        \"message\": \"The requested resource was not found.\",\n        \"code\": 0,\n        \"status\": 404\n    }\n}\n```\n\nPara lograrlo, puedes responder al evento `beforeSend` del componente `response` en la configuración de la aplicación:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'response' => [\n            'class' => 'yii\\web\\Response',\n            'on beforeSend' => function ($event) {\n                $response = $event->sender;\n                if ($response->data !== null && Yii::$app->request->get('suppress_response_code')) {\n                    $response->data = [\n                        'success' => $response->isSuccessful,\n                        'data' => $response->data,\n                    ];\n                    $response->statusCode = 200;\n                }\n            },\n        ],\n    ],\n];\n```\n\nEl anterior código reformateará la respuesta (sea exitosa o fallida) como se explicó cuando\n`suppress_response_code` es pasado como un parámetro `GET`.\n"
  },
  {
    "path": "docs/guide-es/rest-quick-start.md",
    "content": "Guía Breve\n==========\n\nYii ofrece todo un conjunto de herramientas para simplificar la tarea de implementar un\nservicio web APIs RESTful.\nEn particular, Yii soporta las siguientes características sobre APIs RESTful;\n\n* Prototipado rápido con soporte para APIs comunes para [Active Record](db-active-record.md);\n* Formato de respuesta de negocio (soporta JSON y XML por defecto);\n* Personalización de objetos serializados con soporte para campos de salida seleccionables;\n* Formateo apropiado de colecciones de datos y validación de errores;\n* Soporte para [HATEOAS](https://es.wikipedia.org/wiki/HATEOAS);\n* Eficiente enrutamiento con una adecuada comprobación del verbo(verb) HTTP;\n* Incorporado soporte para las `OPTIONS` y `HEAD` verbos;\n* Autenticación y autorización;\n* Cacheo de datos y cacheo HTTP;\n* Limitación de rango;\n\n\nA continuación, utilizamos un ejemplo para ilustrar como se puede construir un conjunto de APIs RESTful con un esfuerzo mínimo de codificación.\n\nSupongamos que deseas exponer los datos de los usuarios vía APIs RESTful. Los datos de usuario son almacenados en la tabla DB `user`,\ny ya tienes creado la clase [[yii\\db\\ActiveRecord|ActiveRecord]] `app\\models\\User` para acceder a los datos del usuario.\n\n\n## Creando un controlador <span id=\"creating-controller\"></span>\n\nPrimero, crea una clase controladora `app\\controllers\\UserController` como la siguiente,\n\n```php\nnamespace app\\controllers;\n\nuse yii\\rest\\ActiveController;\n\nclass UserController extends ActiveController\n{\n    public $modelClass = 'app\\models\\User';\n}\n```\n\nLa clase controladora extiende de [[yii\\rest\\ActiveController]]. Especificado por [[yii\\rest\\ActiveController::modelClass|modelClass]]\ncomo `app\\models\\User`, el controlador sabe que modelo puede ser usado para recoger y manipular sus datos.\n\n\n## Configurando las reglas de las URL <span id=\"configuring-url-rules\"></span>\n\nA continuación, modifica la configuración del componente `urlManager` en la configuración de tu aplicación:\n\n```php\n'urlManager' => [\n    'enablePrettyUrl' => true,\n    'enableStrictParsing' => true,\n    'showScriptName' => false,\n    'rules' => [\n        ['class' => 'yii\\rest\\UrlRule', 'controller' => 'user'],\n    ],\n]\n```\n\nLa configuración anterior principalmente añade una regla URL para el controlador `user` de manera\nque los datos de user pueden ser accedidos y manipulados con URLs amigables y verbos HTTP significativos.\n\n\n## Habilitando entradas JSON <span id=\"enabling-json-input\"></span>\n\nPara permitir que la API acepte datos de entrada con formato JSON, configura la propiedad [[yii\\web\\Request::$parsers|parsers]]\ndel componente de aplicación `request` para usar [[yii\\web\\JsonParser]] para entradas JSON:\n\n```php\n'request' => [\n    'parsers' => [\n        'application/json' => 'yii\\web\\JsonParser',\n    ]\n]\n```\n\n> Tip: La configuración anterior es opcional. Sin la configuración anterior, la API sólo reconocería\n  `application/x-www-form-urlencoded` y `multipart/form-data` como formatos de entrada.\n\n\n## Probándolo <span id=\"trying-it-out\"></span>\n\nCon la mínima cantidad de esfuerzo, tienes ya finalizado tu tarea de crear las APIs RESTful\npara acceder a los datos de user. Las APIs que tienes creado incluyen:\n\n* `GET /users`: una lista de todos los usuarios página por página;\n* `HEAD /users`: muestra la información general de la lista de usuarios;\n* `POST /users`: crea un nuevo usuario;\n* `GET /users/123`: devuelve los detalles del usuario 123;\n* `HEAD /users/123`: muestra la información general del usuario 123;\n* `PATCH /users/123` y `PUT /users/123`: actualiza el usuario 123;\n* `DELETE /users/123`: elimina el usuario 123;\n* `OPTIONS /users`: muestra los verbos compatibles respecto al punto final `/users`;\n* `OPTIONS /users/123`: muestra los verbos compatibles respecto al punto final `/users/123`.\n\n> Info: Yii automáticamente pluraliza los nombres de los controladores para usarlo en los puntos finales.\n> Puedes configurar esto usando la propiedad [[yii\\rest\\UrlRule::$pluralize]].\n\nPuedes acceder a tus APIs con el comando `curl` de la siguiente manera,\n\n```\n$ curl -i -H \"Accept:application/json\" \"http://localhost/users\"\n\nHTTP/1.1 200 OK\n...\nX-Pagination-Total-Count: 1000\nX-Pagination-Page-Count: 50\nX-Pagination-Current-Page: 1\nX-Pagination-Per-Page: 20\nLink: <http://localhost/users?page=1>; rel=self,\n      <http://localhost/users?page=2>; rel=next,\n      <http://localhost/users?page=50>; rel=last\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n[\n    {\n        \"id\": 1,\n        ...\n    },\n    {\n        \"id\": 2,\n        ...\n    },\n    ...\n]\n```\n\nIntenta cambiar el tipo de contenido aceptado para ser `application/xml`, y verá que el resultado\nse devuelve en formato XML:\n\n```\n$ curl -i -H \"Accept:application/xml\" \"http://localhost/users\"\n\nHTTP/1.1 200 OK\n...\nX-Pagination-Total-Count: 1000\nX-Pagination-Page-Count: 50\nX-Pagination-Current-Page: 1\nX-Pagination-Per-Page: 20\nLink: <http://localhost/users?page=1>; rel=self,\n      <http://localhost/users?page=2>; rel=next,\n      <http://localhost/users?page=50>; rel=last\nTransfer-Encoding: chunked\nContent-Type: application/xml\n\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<response>\n    <item>\n        <id>1</id>\n        ...\n    </item>\n    <item>\n        <id>2</id>\n        ...\n    </item>\n    ...\n</response>\n```\n\nEl siguiente comando creará un nuevo usuario mediante el envío de una petición POST con los datos del usuario en formato JSON:\n\n```\n$ curl -i -H \"Accept:application/json\" -H \"Content-Type:application/json\" -XPOST \"http://localhost/users\" -d '{\"username\": \"example\", \"email\": \"user@example.com\"}'\n\nHTTP/1.1 201 Created\n...\nLocation: http://localhost/users/1\nContent-Length: 99\nContent-Type: application/json; charset=UTF-8\n\n{\"id\":1,\"username\":\"example\",\"email\":\"user@example.com\",\"created_at\":1414674789,\"updated_at\":1414674789}\n```\n\n> Tip: También puedes acceder a tus APIs a través del navegador web  introduciendo la URL `http://localhost/users`.\n  Sin embargo, es posible que necesites algunos plugins para el navegador para enviar cabeceras especificas en la petición.\n\nComo se puede ver, en las cabeceras de la respuesta, hay información sobre la cuenta total, número de páginas, etc.\nTambién hay enlaces que permiten navegar por otras páginas de datos. Por ejemplo, `http://localhost/users?page=2`\nle daría la página siguiente de los datos de usuario.\n\nUtilizando los parámetros `fields` y `expand`, puedes también especificar que campos deberían ser incluidos en el resultado.\nPor ejemplo, la URL `http://localhost/users?fields=id,email` sólo devolverá los campos `id` y `email`.\n\n\n> Info: Puedes haber notado que el resultado de `http://localhost/users` incluye algunos campos sensibles,\n> tal como `password_hash`, `auth_key`. Seguramente no quieras que éstos aparecieran en el resultado de tu API.\n> Puedes y deberías filtrar estos campos como se describe en la sección [Response Formatting](rest-response-formatting.md).\n\n\n## Resumen <span id=\"summary\"></span>\n\nUtilizando el framework Yii API RESTful, implementa un punto final API en términos de una acción de un controlador, y utiliza\nun controlador para organizar las acciones que implementan los puntos finales para un sólo tipo de recurso.\n\nLos recursos son representados como modelos de datos que extienden de la clase [[yii\\base\\Model]].\nSi estás trabajando con bases de datos (relacionales o NoSQL), es recomendable utilizar [[yii\\db\\ActiveRecord|ActiveRecord]]\npara representar los recursos.\n\nPuedes utilizar [[yii\\rest\\UrlRule]] para simplificar el enrutamiento de los puntos finales de tu API.\n\nAunque no es obligatorio, es recomendable que desarrolles tus APIs RESTful como una aplicación separada, diferente de\ntu WEB front end y tu back end para facilitar el mantenimiento.\n"
  },
  {
    "path": "docs/guide-es/rest-rate-limiting.md",
    "content": "Limitando el rango (rate)\n=========================\n\nPara prevenir el abuso, puedes considerar añadir un *límitación del rango (rate limiting)* para tus APIs. Por ejemplo,\npuedes querer limitar el uso del API de cada usuario a un máximo de 100 llamadas al API dentro de un periodo de 10 minutos.\nSi se reciben demasiadas peticiones de un usuario dentro del periodo de tiempo declarado, deveríá devolverse una respuesta con código de estado 429 (que significa \"Demasiadas peticiones\").\n\nPara activar la limitación de rango, la clase [[yii\\web\\User::identityClass|user identity class]] debe implementar [[yii\\filters\\RateLimitInterface]].\nEste interface requiere la implementación de tres métodos:\n\n* `getRateLimit()`: devuelve el número máximo de peticiones permitidas y el periodo de tiempo (p.e., `[100, 600]` significa que como mucho puede haber 100 llamadas al API dentro de 600 segundos).\n* `loadAllowance()`: devuelve el número de peticiones que quedan permitidas y el tiempo (fecha/hora) UNIX\n  con el último límite del rango que ha sido comprobado.\n* `saveAllowance()`: guarda ambos, el número restante de peticiones permitidas y el tiempo actual (fecha/hora) UNIX .\n\nPuedes usar dos columnas en la tabla de usuario para guardar la información de lo permitido y la fecha/hora (timestamp). Con ambas definidas,\nentonces `loadAllowance()` y `saveAllowance()` pueden ser utilizados para leer y guardar los valores de las dos columnas correspondientes al actual usuario autenticado.\nPara mejorar el desempeño, también puedes considerar almacenar esas piezas de información en caché o almacenamiento NoSQL.\n\nUna vez que la clase de identidad implementa la interfaz requerida, Yii utilizará automáticamente [[yii\\filters\\RateLimiter]]\nconfigurado como un filtro de acción para que [[yii\\rest\\Controller]] compruebe el límite de rango. El limitador de rango\nlanzará una excepeción [[yii\\web\\TooManyRequestsHttpException]] cuando el límite del rango sea excedido.\n\nPuedes configurar el limitador de rango\nen tu clase controlador REST como sigue:\n\n```php\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n    $behaviors['rateLimiter']['enableRateLimitHeaders'] = false;\n    return $behaviors;\n}\n```\n\nCuando se activa el límite de rango, por defecto todas las respuestas serán enviadas con la siguiente cabecera HTTP conteniendo\ninformación sobre el límite actual de rango:\n\n* `X-Rate-Limit-Limit`, el máximo número de peticiones permitidas en un periodo de tiempo\n* `X-Rate-Limit-Remaining`, el número de peticiones restantes en el periodo de tiempo actual\n* `X-Rate-Limit-Reset`, el número de segundos a esperar para pedir el máximo número de peticiones permitidas\n\nPuedes desactivar estas cabeceras configurando [[yii\\filters\\RateLimiter::enableRateLimitHeaders]] a `false`,\ntal y como en el anterior ejemplo.\n"
  },
  {
    "path": "docs/guide-es/rest-resources.md",
    "content": "Recursos\n=========\n\nLas APIs RESTful lo son todos para acceder y manipular *recursos (resources)*. Puedes observar los recursos en el paradigma MVC en [Modelos (models)](structure-models.md) .\n\nMientras que no hay restricción a cómo representar un recurso, en YII usualmente, puedes representar recursos como objetos de la clase [[yii\\base\\Model]] o sus clases hijas (p.e. [[yii\\db\\ActiveRecord]]), por las siguientes razones:\n\n* [[yii\\base\\Model]] implementa el interface [[yii\\base\\Arrayable]] , el cual te permite personalizar como exponer los datos de los recursos a travès de las APIs RESTful.\n* [[yii\\base\\Model]] soporta [Validación de entrada (input validation)](input-validation.md), lo cual es muy usado en las APIs RESTful para soportar la entrada de datos.\n* [[yii\\db\\ActiveRecord]] provee un poderoso soporte para el acceso a datos en bases de datos y su manipulación, lo que lo le hace servir perfectamente si sus recursos de datos están en bases de datos.\n\nEn esta sección, vamos principalmente a describir como la clase con recursos que extiende de [[yii\\base\\Model]] (o sus clases hijas) puede especificar qué datos puede ser devueltos vía las APIs RESTful. Si la clase de los recursos no extiende de [[yii\\base\\Model]], entonces todas las variables públicas miembro serán devueltas.\n\n\n## Campos (fields) <span id=\"fields\"></span>\n\nCuando incluimos un recurso en una respuesta de la API RESTful, el recurso necesita ser serializado en una cadena.\nYii divide este proceso en dos pasos. Primero, el recurso es convertido en un array por [[yii\\rest\\Serializer]].\nSegundo, el array es serializado en una cadena en  el formato requerido (p.e. JSON, XML) por [[yii\\web\\ResponseFormatterInterface|response formatters]]. El primer paso es en el que debes de concentrarte principalmente cuando desarrolles una clase de un recurso.\n\nSobreescribiendo [[yii\\base\\Model::fields()|fields()]] y/o [[yii\\base\\Model::extraFields()|extraFields()]],\npuedes especificar qué datos, llamados *fields*, en el recursos, pueden ser colocados en el array que le representa.\nLa diferencia entre estos dos métodos es que el primero especifica el conjunto de campos por defecto que deben ser incluidos en el array que los representa, mientras que el último especifica campos adicionales que deben de ser incluidos en el array si una petición del usuario final para ellos vía el parámetro de consulta `expand`. Por ejemplo,\n\n```\n// devuelve todos los campos declarados en fields()\nhttp://localhost/users\n\n// sólo devuelve los campos id y email, provistos por su declaración en fields()\nhttp://localhost/users?fields=id,email\n\n// devuelve todos los campos en fields() y el campo profile siempre y cuando esté declarado en extraFields()\nhttp://localhost/users?expand=profile\n\n// sólo devuelve los campos id, email y profile, siempre y cuando ellos estén declarados en fields() y extraFields()\nhttp://localhost/users?fields=id,email&expand=profile\n```\n\n\n### Sobreescribiendo `fields()` <span id=\"overriding-fields\"></span>\n\nPor defecto, [[yii\\base\\Model::fields()]] devuelve todos los atributos de los modelos como si fueran campos, mientras [[yii\\db\\ActiveRecord::fields()]] sólo devuelve los atributos que tengan datos en la base de datos.\n\nPuedes sobreescribir `fields()` para añadir, quitar, renombrar o redefinir campos. El valor de retorno de `fields()` ha de estar en un array. Las claves del array son los nombres de los campos y los valores del array son las correspondientes definiciones de los campos que pueden ser tanto nombres de propiedades/atributos o funciones anónimas que devuelven los correspondientes valores del los campos. En el caso especial de que el nombre de un campo sea el mismo que su definición puedes omitir la clave en el array. Por ejemplo,\n\n```php\n// explícitamente lista cada campo, siendo mejor usarlo cuando quieras asegurarte que los cambios\n// en una tabla de la base de datos o en un atributo del modelo no provoque el cambio de tu campo (para mantener la compatibilidad anterior).\npublic function fields()\n{\n    return [\n        // el nombre de campo es el mismo nombre del atributo\n        'id',\n        // el nombre del campo es \"email\", su atributo se denomina \"email_address\"\n        'email' => 'email_address',\n        // el nombre del campo es \"name\", su valor es definido está definido por una función anónima de retrollamada (callback)\n        'name' => function () {\n            return $this->first_name . ' ' . $this->last_name;\n        },\n    ];\n}\n\n// el ignorar algunos campos, es mejor usarlo cuando heredas de una implementación padre\n// y pones en la lista negra (blacklist) algunos campos sensibles\npublic function fields()\n{\n    $fields = parent::fields();\n\n    // quita los campos con información sensible\n    unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);\n\n    return $fields;\n}\n```\n\n> Warning: Dado que, por defecto, todos los atributos de un modelo pueden ser incluidos en la devolución del API, debes\n> examinar tus datos para estar seguro de que no contiene información sensible. Si se da este tipo de información,\n> debes sobreescribir `fields()` para filtrarlos. En el ejemplo anterior, escogemos\n> quitar `auth_key`, `password_hash` y `password_reset_token`.\n\n\n### Sobreescribiendo `extraFields()` <span id=\"overriding-extra-fields\"></span>\n\nPor defecto, [[yii\\base\\Model::extraFields()]] no devuelve nada, mientras que [[yii\\db\\ActiveRecord::extraFields()]] devuelve los nombres de las relaciones que tienen datos (populated) obtenidos de la base de datos.\n\nEl formato de devolución de los datos de `extraFields()` es el mismo que el de `fields()`. Usualmente, `extraFields()` es principalmente usado para especificar campos cuyos valores sean objetos. Por ejemplo, dado la siguiente declaración de campo,\n\n```php\npublic function fields()\n{\n    return ['id', 'email'];\n}\n\npublic function extraFields()\n{\n    return ['profile'];\n}\n```\n\nla petición `http://localhost/users?fields=id,email&expand=profile` puede devolver los siguientes datos en formato JSON :\n\n```php\n[\n    {\n        \"id\": 100,\n        \"email\": \"100@example.com\",\n        \"profile\": {\n            \"id\": 100,\n            \"age\": 30,\n        }\n    },\n    ...\n]\n```\n\n\n## Enlaces (Links) <span id=\"links\"></span>\n\n[HATEOAS](https://es.wikipedia.org/wiki/HATEOAS), es una abreviación de Hipermedia es el Motor del Estado de la Aplicación (Hypermedia as the Engine of Application State), promueve que las APIs RESTfull devuelvan información que permita a los clientes descubrir las acciones que soportan los recursos devueltos. El sentido de HATEOAS es devolver un conjunto de hiperenlaces con relación a la información, cuando los datos de los recursos son servidos por las APIs.\n\nLas clases con recursos pueden soportar HATEOAS implementando el interfaz [[yii\\web\\Linkable]] . El interfaz contiene sólo un método [[yii\\web\\Linkable::getLinks()|getLinks()]] el cual debe de de devolver una lista de  [[yii\\web\\Link|links]].\nTípicamente, debes devolver al menos un enlace `self` representando la URL al mismo recurso objeto. Por ejemplo,\n\n```php\nuse yii\\db\\ActiveRecord;\nuse yii\\web\\Link;\nuse yii\\web\\Linkable;\nuse yii\\helpers\\Url;\n\nclass User extends ActiveRecord implements Linkable\n{\n    public function getLinks()\n    {\n        return [\n            Link::REL_SELF => Url::to(['user/view', 'id' => $this->id], true),\n        ];\n    }\n}\n```\n\nCuando un objeto `User` es devuelto en una respuesta, puede contener un elemento  `_links` representando los enlaces relacionados con el usuario, por ejemplo,\n\n```\n{\n    \"id\": 100,\n    \"email\": \"user@example.com\",\n    // ...\n    \"_links\" => {\n        \"self\": {\n            \"href\": \"https://example.com/users/100\"\n        }\n    }\n}\n```\n\n\n## Colecciones <span id=\"collections\"></span>\n\nLos objetos de los recursos pueden ser agrupados en  *collections*. Cada colección contiene una lista de recursos objeto del mismo tipo.\n\nLas colecciones pueden ser representadas como arrays pero, es usualmente más deseable representarlas como [proveedores de datos (data providers)](output-data-providers.md). Esto es así porque los proveedores de datos soportan paginación y ordenación de los recursos, lo cual es comunmente necesario en las colecciones devueltas con las APIs RESTful. Por ejemplo, la siguiente acción devuelve un proveedor de datos sobre los recursos post:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\rest\\Controller;\nuse yii\\data\\ActiveDataProvider;\nuse app\\models\\Post;\n\nclass PostController extends Controller\n{\n    public function actionIndex()\n    {\n        return new ActiveDataProvider([\n            'query' => Post::find(),\n        ]);\n    }\n}\n```\n\nCuando un proveedor de datos está enviando una respuesta con el API RESTful, [[yii\\rest\\Serializer]] llevará la actual página de los recursos y los serializa como un array de recursos objeto. Adicionalmente, [[yii\\rest\\Serializer]]\npuede incluir también la información de paginación a través de las cabeceras HTTP siguientes:\n\n* `X-Pagination-Total-Count`: Número total de recursos;\n* `X-Pagination-Page-Count`: Número de páginas;\n* `X-Pagination-Current-Page`: Página actual (iniciando en 1);\n* `X-Pagination-Per-Page`: Número de recursos por página;\n* `Link`: Un conjunto de enlaces de navegación permitiendo al cliente recorrer los recursos página a página.\n\nUn ejemplo se puede ver en la sección [Inicio rápido (Quick Start)](rest-quick-start.md#trying-it-out).\n"
  },
  {
    "path": "docs/guide-es/rest-response-formatting.md",
    "content": "Formato de Respuesta\n====================\n\nCuando se maneja una petición al API RESTful, una aplicación realiza usualmente los siguientes pasos que están relacionados\ncon el formato de la respuesta:\n\n1. Determinar varios factores que pueden afectar al formato de la respuesta, como son el tipo de medio, lenguaje, versión, etc.\n   Este proceso es también conocido como [negociación de contenido (content negotiation)](https://es.wikipedia.org/wiki/Negociaci%C3%B3n_de_contenido).\n2. La conversión de objetos recurso en arrays, como está descrito en la sección [Recursos (Resources)](rest-resources.md).\n   Esto es realizado por la clase [[yii\\rest\\Serializer]].\n3. La conversión de arrays en cadenas con el formato determinado por el paso de negociación de contenido. Esto es\n   realizado por los [[yii\\web\\ResponseFormatterInterface|formatos de respuesta]] registrados\n   con la propiedad [[yii\\web\\Response::formatters|formatters]] del\n   [componente de la aplicación](structure-application-components.md) `response`.\n\n\n## Negociación de contenido (Content Negotiation) <span id=\"content-negotiation\"></span>\n\nYii soporta la negociación de contenido a través del filtro [[yii\\filters\\ContentNegotiator]]. La clase de controlador base\ndel API RESTful [[yii\\rest\\Controller]] está equipada con este filtro bajo el nombre `contentNegotiator`.\nEl filtro provee tanto un formato de respuesta de negociación como una negociación de lenguaje. Por ejemplo, si la petición API RESTful\ncontiene la siguiente cabecera,\n\n```\nAccept: application/json; q=1.0, */*; q=0.1\n```\n\npuede obtener una respuesta en formato JSON, como a continuación:\n\n```\n$ curl -i -H \"Accept: application/json; q=1.0, */*; q=0.1\" \"http://localhost/users\"\n\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nX-Powered-By: PHP/5.4.20\nX-Pagination-Total-Count: 1000\nX-Pagination-Page-Count: 50\nX-Pagination-Current-Page: 1\nX-Pagination-Per-Page: 20\nLink: <http://localhost/users?page=1>; rel=self,\n      <http://localhost/users?page=2>; rel=next,\n      <http://localhost/users?page=50>; rel=last\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n[\n    {\n        \"id\": 1,\n        ...\n    },\n    {\n        \"id\": 2,\n        ...\n    },\n    ...\n]\n```\n\nDetrás de escena, antes de que sea ejecutada una acción del controlador del API RESTful, el filtro [[yii\\filters\\ContentNegotiator]]\ncomprobará la cabecera HTTP `Accept` de la petición y definirá el [[yii\\web\\Response::format|response format]]\ncomo `'json'`. Después de que la acción sea ejecutada y devuelva el objeto recurso o la colección resultante,\n[[yii\\rest\\Serializer]] convertirá el resultado en un array. Y finalmente, [[yii\\web\\JsonResponseFormatter]]\nserializará el array en una cadena JSON incluyéndola en el cuerpo de la respuesta.\n\nPor defecto, el API RESTful soporta tanto el formato JSON como el XML. Para soportar un nuevo formato, debes configurar\nla propiedad [[yii\\filters\\ContentNegotiator::formats|formats]] del filtro `contentNegotiator` tal y como sigue,\nen las clases del controlador del API:\n\n```php\nuse yii\\web\\Response;\n\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n    $behaviors['contentNegotiator']['formats']['text/html'] = Response::FORMAT_HTML;\n    return $behaviors;\n}\n```\n\nLas claves de la propiedad `formats` son los tipos MIME soportados, mientras que los valores son los nombres de formato de respuesta correspondientes,\nlos cuales deben ser soportados en [[yii\\web\\Response::formatters]].\n\n\n## Serialización de Datos <span id=\"data-serializing\"></span>\n\nComo hemos descrito antes, [[yii\\rest\\Serializer]] es la pieza central responsable de convertir\nobjetos recurso o colecciones en arrays. Reconoce objetos tanto implementando [[yii\\base\\ArrayableInterface]]\ncomo [[yii\\data\\DataProviderInterface]]. El primer formateador es implementado principalmente para objetos recursos,\nmientras que el segundo para recursos collección.\n\nPuedes configurar el serializador definiendo la propiedad [[yii\\rest\\Controller::serializer]] con un array de configuración.\nPor ejemplo, a veces puedes querer ayudar a simplificar el trabajo de desarrollo del cliente incluyendo información de la paginación\ndirectamente en el cuerpo de la respuesta. Para hacer esto, configura la propiedad [[yii\\rest\\Serializer::collectionEnvelope]]\ncomo sigue:\n\n```php\nuse yii\\rest\\ActiveController;\n\nclass UserController extends ActiveController\n{\n    public $modelClass = 'app\\models\\User';\n    public $serializer = [\n        'class' => 'yii\\rest\\Serializer',\n        'collectionEnvelope' => 'items',\n    ];\n}\n```\n\nPuedes obtener la respuesta que sigue para la petición `http://localhost/users`:\n\n```\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nX-Powered-By: PHP/5.4.20\nX-Pagination-Total-Count: 1000\nX-Pagination-Page-Count: 50\nX-Pagination-Current-Page: 1\nX-Pagination-Per-Page: 20\nLink: <http://localhost/users?page=1>; rel=self,\n      <http://localhost/users?page=2>; rel=next,\n      <http://localhost/users?page=50>; rel=last\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"items\": [\n        {\n            \"id\": 1,\n            ...\n        },\n        {\n            \"id\": 2,\n            ...\n        },\n        ...\n    ],\n    \"_links\": {\n        \"self\": {\n            \"href\": \"http://localhost/users?page=1\"\n        },\n        \"next\": {\n            \"href\": \"http://localhost/users?page=2\"\n        },\n        \"last\": {\n            \"href\": \"http://localhost/users?page=50\"\n        }\n    },\n    \"_meta\": {\n        \"totalCount\": 1000,\n        \"pageCount\": 50,\n        \"currentPage\": 1,\n        \"perPage\": 20\n    }\n}\n```\n"
  },
  {
    "path": "docs/guide-es/rest-routing.md",
    "content": "Enrutamiento\n============\n\nCon las clases de controlador y recurso preparadas, puedes acceder a los recursos usando una URL como\n`http://localhost/index.php?r=user/create`, parecida a la que usas con aplicaciones Web normales.\n\nEn la práctica, querrás usualmente usar URLs limpias y obtener ventajas de los verbos HTTP.\nPor ejemplo, una petición `POST /users` significaría acceder a la acción `user/create`.\nEsto puede realizarse fácilmente configurando el componente de la aplicación `urlManager`\ncomo sigue:\n\n```php\n'urlManager' => [\n    'enablePrettyUrl' => true,\n    'enableStrictParsing' => true,\n    'showScriptName' => false,\n    'rules' => [\n        ['class' => 'yii\\rest\\UrlRule', 'controller' => 'user'],\n    ],\n]\n```\n\nEn comparación con la gestión de URL en las aplicaciones Web, lo principalmente nuevo de lo anterior es el uso de\n[[yii\\rest\\UrlRule]] para el enrutamiento de las peticiones con el API RESTful. Esta clase especial de regla URL creará\nun conjunto completo de reglas URL hijas para soportar el enrutamiento y creación de URL para el/los controlador/es especificados.\nPor ejemplo, el código anterior es equivalente a las siguientes reglas:\n\n```php\n[\n    'PUT,PATCH users/<id>' => 'user/update',\n    'DELETE users/<id>' => 'user/delete',\n    'GET,HEAD users/<id>' => 'user/view',\n    'POST users' => 'user/create',\n    'GET,HEAD users' => 'user/index',\n    'users/<id>' => 'user/options',\n    'users' => 'user/options',\n]\n```\n\nY los siguientes puntos finales del API son mantenidos por esta regla:\n\n* `GET /users`: lista de todos los usuarios página a página;\n* `HEAD /users`: muestra ĺa información resumen del usuario listado;\n* `POST /users`: crea un nuevo usuario;\n* `GET /users/123`: devuelve los detalles del usuario 123;\n* `HEAD /users/123`: muestra la información resumen del usuario 123;\n* `PATCH /users/123` y `PUT /users/123`: actualizan al usuario 123;\n* `DELETE /users/123`: borra el usuario 123;\n* `OPTIONS /users`: muestra los verbos soportados de acuerdo al punto final `/users`;\n* `OPTIONS /users/123`: muestra los verbos soportados de acuerdo al punto final `/users/123`.\n\nPuedes configurar las opciones `only` y `except` para explícitamente listar cuáles acciones soportar o cuáles\ndeshabilitar, respectivamente. Por ejemplo,\n\n```php\n[\n    'class' => 'yii\\rest\\UrlRule',\n    'controller' => 'user',\n    'except' => ['delete', 'create', 'update'],\n],\n```\n\nTambién puedes configurar las propiedades `patterns` o `extraPatterns` para redefinir patrones existentes o añadir nuevos soportados por esta regla.\nPor ejemplo, para soportar una nueva acción `search` para el punto final `GET /users/search`, configura la opción `extraPatterns` como sigue,\n\n```php\n[\n    'class' => 'yii\\rest\\UrlRule',\n    'controller' => 'user',\n    'extraPatterns' => [\n        'GET search' => 'search',\n    ],\n]\n```\n\nPuedes haber notado que el ID del controlador `user` aparece en formato plural `users` en los puntos finales de las URLs.\nEsto se debe a que [[yii\\rest\\UrlRule]] automáticamente pluraliza los IDs de los controladores al crear reglas URL hijas.\nPuedes desactivar este comportamiento definiendo la propiedad [[yii\\rest\\UrlRule::pluralize]] como `false`. \n\n> Info: La pluralización de los IDs de los controladores es realizada por [[yii\\helpers\\Inflector::pluralize()]]. Este método respeta\n  reglas especiales de pluralización. Por ejemplo, la palabra `box` (caja) será pluralizada como `boxes` en vez de `boxs`.\n\nEn caso de que la pluralización automática no encaje en tus requerimientos, puedes además configurar la propiedad \n[[yii\\rest\\UrlRule::controller]] para especificar explícitamente cómo mapear un nombre utilizado en un punto final URL\na un ID de controlador. Por ejemplo, el siguiente código mapea el nombre `u` al ID del controlador `user`.  \n \n```php\n[\n    'class' => 'yii\\rest\\UrlRule',\n    'controller' => ['u' => 'user'],\n]\n```\n"
  },
  {
    "path": "docs/guide-es/rest-versioning.md",
    "content": "Versionado\n==========\n\nUna buena API ha de ser *versionada*: los cambios y las nuevas características son implementadas en las nuevas versiones del API, en vez de estar continuamente modificando sólo una versión. Al contrario que en las aplicaciones Web, en las cuales tienes total control del código de ambas partes lado del cliente y lado del servidor,\nlas APIs están destinadas a ser usadas por los clientes fuera de tu control. Por esta razón, compatibilidad hacia atrás (BC Backward compatibility)\nde las APIs ha de ser mantenida siempre que sea posible. Si es necesario un cambio que puede romper la BC, debes de introducirla en la nueva versión del API, e incrementar el número de versión. Los clientes que la usan pueden continuar usando la antigua versión de trabajo del API; los nuevos y actualizados clientes pueden obtener la nueva funcionalidad de la nueva versión del API.\n\n> Tip: referirse a [Semántica del versionado](https://semver.org/)\npara más información en el diseño del número de versión del API.\n\nUna manera común de implementar el versionado de la API es embeber el número de versión en las URLs de la  API.\nPor ejemplo, `https://example.com/v1/users` se refiere al punto final `/users` de la versión 1 de la API. \n\nOtro método de versionado de la API,\nla cual está ganando predominancia recientemente, es poner el número de versión en las cabeceras de la petición HTTP. Esto se suele hacer típicamente a través la cabecera `Accept`:\n\n```\n// vía parámetros\nAccept: application/json; version=v1\n// vía de el tipo de contenido del proveedor\nAccept: application/vnd.company.myapp-v1+json\n```\n\nAmbos métodos tienen sus pros y sus contras, y hay gran cantidad de debates sobre cada uno. Debajo puedes ver una estrategia\npráctica para el versionado de la API que es una mezcla de estos dos métodos:\n\n* Pon cada versión superior de la implementación de la API en un módulo separado cuyo ID es el número de la versión mayor (p.e. `v1`, `v2`).\n  Naturalmente, las URLs de la API contendrán números de versión mayores.\n* Dentro de cada versión mayor (y por lo tanto, dentro del correspondiente módulo), usa la cabecera de HTTP `Accept`\n  para determinar el número de la versión menor y escribe código condicional para responder a la menor versión como corresponde.\n\nPara cada módulo sirviendo una versión mayor, el módulo debe incluir las clases de recursos y y controladores\nque especifican la versión. Para separar mejor la responsabilidad del código, puedes conservar un conjunto común de\nclases base de recursos y controladores, y hacer subclases de ellas en cada versión individual del módulo. Dentro de las subclases,\nimpementa el código concreto como por ejemplo `Model::fields()`.\n\nTu código puede estar organizado como lo que sigue:\n\n```\napi/\n    common/\n        controllers/\n            UserController.php\n            PostController.php\n        models/\n            User.php\n            Post.php\n    modules/\n        v1/\n            controllers/\n                UserController.php\n                PostController.php\n            models/\n                User.php\n                Post.php\n        v2/\n            controllers/\n                UserController.php\n                PostController.php\n            models/\n                User.php\n                Post.php\n```\n\nLa configuración de tu aplicación puede tener este aspecto:\n\n```php\nreturn [\n    'modules' => [\n        'v1' => [\n            'basePath' => '@app/modules/v1',\n            'controllerNamespace' => 'app\\modules\\v1\\controllers',\n        ],\n        'v2' => [\n            'basePath' => '@app/modules/v2',\n            'controllerNamespace' => 'app\\modules\\v2\\controllers',\n        ],\n    ],\n    'components' => [\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            'enableStrictParsing' => true,\n            'showScriptName' => false,\n            'rules' => [\n                ['class' => 'yii\\rest\\UrlRule', 'controller' => ['v1/user', 'v1/post']],\n                ['class' => 'yii\\rest\\UrlRule', 'controller' => ['v2/user', 'v2/post']],\n            ],\n        ],\n    ],\n];\n```\n\nComo consecuencia del código anterior, `https://example.com/v1/users` devolverá la lista de usuarios en la versión 1, mientras\n`https://example.com/v2/users` devolverá la versión 2 de los usuarios.\n\nGracias a los módulos, el código de las diferentes principales versiones puede ser aislado. Pero los módulos hacen posible\nreutilizar el código a través de los módulos vía clases base comunes y otros recursos compartidos.\n\nPara tratar con versiones menores, puedes tomar ventaja de la característica de negociación de contenido\nprovista por el comportamiento (behavior) [[yii\\filters\\ContentNegotiator|contentNegotiator]]. El comportamiento `contentNegotiator`\ndefinirá la propiedad [[yii\\web\\Response::acceptParams]] cuando determina qué tipo\nde contenido soportar.\n\nPor ejemplo, si una petición es enviada con la cabecera HTTP `Accept: application/json; version=v1`,\ndespués de la negociación de contenido, [[yii\\web\\Response::acceptParams]] contendrá el valor `['version' => 'v1']`.\n\nBasado en la información de versión contenida en `acceptParams`, puedes escribir código condicional en lugares\ncomo acciones, clases de recursos, serializadores, etc. para proveer la funcionalidad apropiada.\n\nDado que por definición las versiones menores requireren mantener la compatibilidad hacia atrás, con suerte no tendrás demasiadas\ncomprobaciones de versión en tu código. De otra manera, probablemente puede ocurrir que necesites crear una versión mayor.\n"
  },
  {
    "path": "docs/guide-es/runtime-bootstrapping.md",
    "content": "Bootstrapping\n=============\n\nEl Bootstrapping hace referencia al proceso de preparar el entorno antes de que una aplicación se inicie para resolver y procesar una petición entrante. El se ejecuta en dos lugares: el [script de entrada](structure-entry-scripts.md) y la [aplicación](structure-applications.md).\n\nEn el [script de entrada](structure-entry-scripts.md), se registran los cargadores automáticos de clase para diferentes librerías. Esto incluye el cargador automático de Composer a través de su fichero ‘autoload.php’ y del cargador automático de Yii a través del fichero de clase ‘Yii’. El script de entrada después carga la [configuración](concept-configurations.md) de la aplicación y crea una instancia de la [aplicación](structure-applications.md).\n\nEl constructor de la aplicación, ejecuta el siguiente trabajo de bootstrapping:\n\nLlama a [[yii\\base\\Application::preInit()|preInit()]], que configura algunas propiedades de alta prioridad de la aplicación, como [[yii\\base\\Application::basePath|basePath]].\nRegistra el [[yii\\base\\Application::errorHandler|error handler]].\nInicializa las propiedades de aplicación usando la configuración de la aplicación dada.\nLlama a [[yii\\base\\Application::init()|init()]] que a su vez llama a [[yii\\base\\Application::bootstrap()|bootstrap()]] para ejecutar componentes de bootstrapping.\nIncluye el archivo de manifiesto de extensiones ‘vendor/yiisoft/extensions.php’\nCrea y ejecuta [componentes de bootstrap](structure-extensions.md#bootstrapping-classes) declarados por las extensiones. \nCrea y ejecuta [componentes de aplicación](structure-application-components.md) y/o [módulos](structure-modules.md) que se declaran en la [propiedad bootstrap](structure-applications.md#bootstrap) de la aplicación.\n\nDebido a que el trabajo de bootstrapping se tiene que ejecutar antes de gestionar *todas* las peticiones, es muy importante mantener este proceso ligero y optimizado lo máximo que sea posible.\n\nIntenta no registrar demasiados componentes de bootstrapping. Un componente de bootstrapping sólo es necesario si tiene que interaccionar en todo el ciclo de vida de la gestión de la petición. Por ejemplo, si un modulo necesita registrar reglas de análisis de URL adicionales, se debe incluirse en la [propiedad bootstrap](structure-applications.md#bootstrap) para que la nueva regla de URL tenga efecto antes de que sea utilizada para resolver peticiones.\n\nEn modo de producción, hay que habilitar la cache bytecode, así como [APC](https://www.php.net/manual/es/book.apc.php), para minimizar el tiempo necesario para incluir y analizar archivos PHP.\n\nAlgunas grandes aplicaciones tienen [configuraciones](concept-configurations.md) de aplicación muy complejas que están dividida en muchos archivos de configuración más pequeños.\n"
  },
  {
    "path": "docs/guide-es/runtime-handling-errors.md",
    "content": "Gestión de Errores\n==================\n\nYii incluye un [[yii\\web\\ErrorHandler|error handler]] que permite una gestión de errores mucho más práctica que\nanteriormente. En particular, el gestor de errores de Yii hace lo siguiente para mejorar la gestión de errores:\n\n* Todos los errores no fatales (ej. advertencias (warning), avisos (notices)) se convierten en excepciones capturables.\n* Las excepciones y los errores fatales de PHP se muestran con una pila de llamadas (call stack) de información\n  detallada y lineas de código fuente.\n* Soporta el uso de [acciones de controlador](structure-controllers.md#actions) dedicadas para mostrar errores.\n* Soporta diferentes formatos de respuesta (response) de errores.\n\nEl [[yii\\web\\ErrorHandler|error handler]] esta habilitado de forma predeterminada. Se puede deshabilitar definiendo la\nconstante `YII_ENABLE_ERROR_HANDLER` con valor `false` en el [script de entrada (entry script)](structure-entry-scripts.md) de la aplicación.\n\n\n## Uso del Gestor de Errores <span id=\"using-error-handler\"></span>\n\nEl [[yii\\web\\ErrorHandler|error handler]] se registra como un [componente de aplicación](structure-application-components.md) llamado `errorHandler`.\nSe puede configurar en la configuración de la aplicación como en el siguiente ejemplo:\n\n```php\nreturn [\n    'components' => [\n        'errorHandler' => [\n            'maxSourceLines' => 20,\n        ],\n    ],\n];\n```\n\nCon la anterior configuración, el numero del lineas de código fuente que se mostrará en las páginas de excepciones será como máximo de 20.\n\nComo se ha mencionado, el gestor de errores convierte todos los errores de PHP no fatales en excepciones capturables.\nEsto significa que se puede usar el siguiente código para tratar los errores PHP:\n\n```php\nuse Yii;\nuse yii\\base\\ErrorException;\n\ntry {\n    10/0;\n} catch (ErrorException $e) {\n    Yii::warning(\"Division by zero.\");\n}\n\n// la ejecución continua ...\n```\n\nSi se quiere mostrar una página de error que muestra al usuario que su petición no es válida o no es la esperada, se\npuede simplemente lanzar una excepción de tipo [[yii\\web\\HttpException|HTTP exception]], como podría ser\n[[yii\\web\\NotFoundHttpException]]. El gestor de errores establecerá correctamente el código de estado HTTP de la\nrespuesta y usará la vista de error apropiada para mostrar el mensaje.\n\n```php\nuse yii\\web\\NotFoundHttpException;\n\nthrow new NotFoundHttpException();\n```\n\n\n## Personalizar la Visualización de Errores <span id=\"customizing-error-display\"></span>\n\nEl [[yii\\web\\ErrorHandler|error handler]] ajusta la visualización del error conforme al valor de la constante `YII_DEBUG`.\nCuando `YII_DEBUG` es `true` (es decir, en modo depuración (debug)), el gestor de errores mostrara las\nexcepciones con una pila detallada de información y con lineas de código fuente para ayudar a depurar. Y cuando la variable `YII_DEBUG` es `false`,\nsolo se mostrará el mensaje de error para prevenir la revelación de información sensible de la aplicación.\n\n> Info: Si una excepción es descendiente de [[yii\\base\\UserException]], no se mostrará la pila de llamadas\nindependientemente del valor de `YII_DEBUG`. Esto es debido a que se considera que estas excepciones se deben a\nerrores cometidos por los usuarios y los desarrolladores no necesitan corregirlas.\n\nDe forma predeterminada, el [[yii\\web\\ErrorHandler|error handler]] muestra los errores usando dos [vistas](structure-views.md):\n\n* `@yii/views/errorHandler/error.php`: se usa cuando deben mostrarse los errores SIN la información de la pila de\n  llamadas. Cuando `YII_DEBUG` es falos, este es el único error que se mostrara.\n* `@yii/views/errorHandler/exception.php`: se usa cuando los errores deben mostrarse CON la información de la pila de llamadas.\n\nSe pueden configurar las propiedades [[yii\\web\\ErrorHandler::errorView|errorView]] y [[yii\\web\\ErrorHandler::exceptionView|exceptionView]]\nel gestor de errores para usar nuestros propias vistas para personalizar la visualización de los errores.\n\n\n### Uso de Acciones de Error <span id=\"using-error-actions\"></span>\n\nUna mejor manera de personalizar la visualización de errores es usar un [acción](structure-controllers.md) de error\ndedicada. Para hacerlo, primero se debe configurar la propiedad [[yii\\web\\ErrorHandler::errorAction|errorAction]] del\ncomponente `errorHandler` como en el siguiente ejemplo:\n\n```php\nreturn [\n    'components' => [\n        'errorHandler' => [\n            'errorAction' => 'site/error',\n        ],\n    ]\n];\n```\n\nLa propiedad [[yii\\web\\ErrorHandler::errorAction|errorAction]] vincula una [ruta](structure-controllers.md#routes) a\nuna acción. La configuración anterior declara que cuando un error tiene que mostrarse sin la pila de información de\nllamadas, se debe ejecutar la acción `site/error`.\n\nSe puede crear una acción `site/error` como se hace a continuación,\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actions()\n    {\n        return [\n            'error' => [\n                'class' => 'yii\\web\\ErrorAction',\n            ],\n        ];\n    }\n}\n```\n\nEl código anterior define la acción `error` usando la clase [[yii\\web\\ErrorAction]] que renderiza un error usando la\nvista llamada `error`.\n\nAdemás, usando [[yii\\web\\ErrorAction]], también se puede definir la acción `error` usando un método de acción como en el siguiente ejemplo,\n\n```php\npublic function actionError()\n{\n    $exception = Yii::$app->errorHandler->exception;\n    if ($exception !== null) {\n        return $this->render('error', ['exception' => $exception]);\n    }\n}\n```\n\nAhora se debe crear un archivo de vista ubicado en `views/sites/error.php`. En este archivo de vista, se puede acceder\na las siguientes variables si se define el error como un [[yii\\web\\ErrorAction]]:\n\n* `name`: el nombre del error;\n* `message`: el mensaje del error;\n* `exception`: el objeto de excepción a través del cual se puede obtener más información útil, tal como el código de\n  estado HTTP, el código de error, la pila de llamadas del error, etc.\n\n> Info: Tanto la [plantilla de aplicación básica](start-installation.md) como la [plantilla de aplicación avanzada](tutorial-advanced-app.md),\nya incorporan la acción de error y la vista de error.\n\n> Note: Si necesitas redireccionar en un gestor de error, hazlo de la siguiente manera:\n> ```php\n> Yii::$app->getResponse()->redirect($url)->send();\n> return;\n> ```\n\n\n### Personalizar el Formato de Respuesta de Error <span id=\"error-format\"></span>\n\nEl gestor de errores muestra los errores de siguiente la configuración del formato de las\n[respuestas](runtime-responses.md). Si el [[yii\\web\\Response::format response format]] es `html`, se usará la vista de\nerror o excepción para mostrar los errores tal y como se ha descrito en la anterior subsección. Para otros tipos de\nformatos de respuesta, el gestor de errores asignara la representación del array de la excepción a la propiedad\n[[yii\\web\\Response::data]] que posteriormente podrá convertirse al formato deseado. Por ejemplo, si el formato de\nrespuesta es `json`, obtendremos la siguiente respuesta:\n\n```\nHTTP/1.1 404 Not Found\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"name\": \"Not Found Exception\",\n    \"message\": \"The requested resource was not found.\",\n    \"code\": 0,\n    \"status\": 404\n}\n```\n\nSe puede personalizar el formato de respuestas de error respondiendo al evento `beforeSend` del componente `response`\nen la configuración de la aplicación:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'response' => [\n            'class' => 'yii\\web\\Response',\n            'on beforeSend' => function ($event) {\n                $response = $event->sender;\n                if ($response->data !== null) {\n                    $response->data = [\n                        'success' => $response->isSuccessful,\n                        'data' => $response->data,\n                    ];\n                    $response->statusCode = 200;\n                }\n            },\n        ],\n    ],\n];\n```\n\nEl código anterior reformateará la respuesta de error como en el siguiente ejemplo:\n\n```\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"success\": false,\n    \"data\": {\n        \"name\": \"Not Found Exception\",\n        \"message\": \"The requested resource was not found.\",\n        \"code\": 0,\n        \"status\": 404\n    }\n}\n```\n"
  },
  {
    "path": "docs/guide-es/runtime-logging.md",
    "content": "Registro de anotaciones\n=======================\n\nYii proporciona un poderoso framework dedicado al registro de anotaciones (logging) que es altamente personalizable y\nextensible. Usando este framework se pueden guardar fácilmente anotaciones (logs) de varios tipos de mensajes,\nfiltrarlos, y unificarlos en diferentes destinos que pueden ser archivos, bases de datos o emails.\n\n\nUsar el framework de registro de anotaciones de Yii involucra los siguientes pasos:\n\n* Registrar [mensajes de las anotaciones](#log-messages) en distintos lugares del código;\n* Configurar los [destinos de las anotaciones](#log-targets) en la configuración de la aplicación para filtrar y\n  exportar los mensajes de las anotaciones;\n* Examinar los mensajes filtrados de los las anotaciones exportadas para diferentes destinos\n  (ej. [Yii debugger](tool-debugger.md)).\n\nEn esta sección, se describirán principalmente los dos primeros pasos.\n\n## Anotación de Messages <span id=\"log-messages\"></span>\n\nRegistrar mensajes de anotación es tan simple como llamar a uno de los siguientes métodos de registro de anotaciones.\n\n* [[Yii::debug()]]: registra un mensaje para trazar el funcionamiento de una sección de código. Se usa principalmente\n  para tareas de desarrollo.\n* [[Yii::info()]]: registra un mensaje que transmite información útil.\n* [[Yii::warning()]]: registra un mensaje de advertencia que indica que ha sucedido algo inesperado.\n* [[Yii::error()]]: registra un error fatal que debe ser investigado tan pronto como sea posible.\n\nEstos métodos registran mensajes de varios *niveles de severidad* y *categorías*. Comparten el mismo registro de\nfunción `function ($message, $category = 'application')`, donde `$message` representa el mensaje del registro que\ntiene que ser registrado, mientras que `$category` es la categoría del registro de mensaje. El código del siguiente\nejemplo registra la huella del mensaje para la categoría `application`:\n\n```php\nYii::debug('start calculating average revenue');\n```\n\n> Info: Los mensajes de registro pueden ser tanto cadenas de texto como datos complejos, como arrays u objetos.\n  Es responsabilidad de los [destinos de registros](#log-targets) tratar los mensajes de registro de manera apropiada.\n  De forma predeterminada, si un mensaje de registro no es una cadena de texto, se exporta como si fuera un string\n  llamando a [[yii\\helpers\\VarDumper::export()]].\n\nPara organizar mejor y filtrar los mensajes de registro, se recomienda especificar una categoría apropiada para cada\nmensaje de registro. Se puede elegir un sistema de nombres jerárquicos por categorías que facilite a los\n[destino de registros](#log-targets) el filtrado de mensajes basándose en categorías. Una manera simple pero\nefectiva de organizarlos es usar la constante predefinida (magic constant) de PHP `__METHOD__` como nombre de\ncategoría. Además este es el enfoque que se usa en el código del núcleo (core) del framework Yii. Por ejemplo,\n\n```php\nYii::debug('start calculating average revenue', __METHOD__);\n```\n\nLa constante `__METHOD__` equivale al nombre del método (con el prefijo del nombre completo del nombre de clase) donde\nse encuentra la constante. Por ejemplo, es igual a la cadena `'app\\controllers\\RevenueController::calculate'` si la\nlinea anterior de código se llamara dentro de este método.\n\n> Info: Los métodos de registro de anotaciones descritos anteriormente en realidad son accesos directos al\n  método [[yii\\log\\Logger::log()|log()]] del [[yii\\log\\Logger|logger object]] que es un singleton accesible a través\n  de la expresión `Yii::getLogger()`. Cuando se hayan registrado suficientes mensajes o cuando la aplicación haya\n  finalizado, el objeto de registro llamará [[yii\\log\\Dispatcher|message dispatcher]] para enviar los mensajes de\n  registro registrados a los [destiinos de registros](#log-targets).\n\n## Destino de Registros <span id=\"log-targets\"></span>\n\nUn destino de registro es una instancia de la clase [[yii\\log\\Target]] o de una clase hija. Este filtra los\nmensajes de registro por sus niveles de severidad y sus categorías y después los exporta a algún medio. Por ejemplo,\nun [[yii\\log\\DbTarget|database target]] exporta los mensajes de registro filtrados a una tabla de base de datos,\nmientras que un [[yii\\log\\EmailTarget|email target]] exporta los mensajes de registro a una dirección de correo\nelectrónico específica.\n\nSe pueden registrar múltiples destinos de registros en una aplicación configurándolos en la\n[aplicación de componente](structure-application-components.md) `log` dentro de la configuración de aplicación, como\nen el siguiente ejemplo:\n\n```php\nreturn [\n    // el componente log tiene que cargarse durante el proceso de bootstrapping\n    'bootstrap' => ['log'],\n\n    'components' => [\n        'log' => [\n            'targets' => [\n                [\n                    'class' => 'yii\\log\\DbTarget',\n                    'levels' => ['error', 'warning'],\n                ],\n                [\n                    'class' => 'yii\\log\\EmailTarget',\n                    'levels' => ['error'],\n                    'categories' => ['yii\\db\\*'],\n                    'message' => [\n                       'from' => ['log@example.com'],\n                       'to' => ['admin@example.com', 'developer@example.com'],\n                       'subject' => 'Database errors at example.com',\n                    ],\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n> Note: El componente `log` debe cargarse durante el proceso de [bootstrapping](runtime-bootstrapping.md) para que\npueda enviar los mensajes de registro a los destinos inmediatamente. Este es el motivo por el que se lista en el\narray `bootstrap` como se muestra más arriba.\n\nEn el anterior código, se registran dos destinos de registros en la propiedad [[yii\\log\\Dispatcher::targets]]\n\n* el primer destino gestiona los errores y las advertencias y las guarda en una tabla de la base de datos;\n* el segundo destino gestiona mensajes los mensajes de error de las categorías cuyos nombres empiecen por\n  `yii\\db\\` y los envía por email a las direcciones `admin@example.com` y `developer@example.com`.\n\nYii incluye los siguientes destinos. En la API de documentación se pueden referencias a estas clases e\ninformación de configuración y uso.\n\n* [[yii\\log\\DbTarget]]: almacena los mensajes de registro en una tabla de la base de datos.\n* [[yii\\log\\EmailTarget]]: envía los mensajes de registro a direcciones de correo preestablecidas.\n* [[yii\\log\\FileTarget]]: guarda los menajes de registro en archivos.\n* [[yii\\log\\SyslogTarget]]: guarda los mensajes de registro en el syslog llamando a la función PHP `syslog()`.\n\nA continuación, se describirá las características más comunes de todos los destinos de registros.\n\n### Filtrado de Mensajes <span id=\"message-filtering\"></span>\n\nSe pueden configurar las propiedades [[yii\\log\\Target::levels|levels]] y [[yii\\log\\Target::categories|categories]]\npara cada destino de registros, con estas se especifican los niveles de severidad y las categorías de mensajes que\ndeberán procesar sus destinos.\n\nLa propiedad [[yii\\log\\Target::levels|levels]] es un array que consta de uno o varios de los siguientes valores:\n\n* `error`: correspondiente a los mensajes registrados por [[Yii::error()]].\n* `warning`: correspondiente a los mensajes registrados por [[Yii::warning()]].\n* `info`: correspondiente a los mensajes registrados por [[Yii::info()]].\n* `trace`: correspondiente a los mensajes registrados por [[Yii::debug()]].\n* `profile`: correspondiente a los mensajes registrados por [[Yii::beginProfile()]] y [[Yii::endProfile()]], que se\n  explicará más detalladamente en la subsección [Perfiles](#performance-profiling).\n\nSi no se especifica la propiedad [[yii\\log\\Target::levels|levels]], significa que el destino procesará los\nmensajes de *cualquier* nivel de severidad.\n\nLa propiedad [[yii\\log\\Target::categories|categories]] es un array que consta de categorías de mensaje o patrones. El\ndestino sólo procesará mensajes de las categorías que se puedan encontrar o si coinciden con algún patrón listado\nen el array. Un patrón de categoría es un nombre de categoría al que se le añade un asterisco `*` al final. Un nombre\nde categoría coincide con un patrón si empieza por el mismo prefijo que el patrón. Por ejemplo,\n`yii\\db\\Command::execute` y `yii\\db\\Command::query` que se usan como nombres de categoría para los mensajes\nregistrados en la clase [[yii\\db\\Command]], coinciden con el patrón `yii\\db\\*`.\n\nSi no se especifica la propiedad [[yii\\log\\Target::categories|categories]], significa que el destino procesará\nlos mensajes de *todas* las categorías.\n\nAdemás añadiendo las categorías en listas blancas (whitelisting) mediante la propiedad\n[[yii\\log\\Target::categories|categories]], también se pueden añadir ciertas categorías en listas negras (blacklist)\nconfigurando la propiedad [[yii\\log\\Target::except|except]]. Si se encuentra la categoría de un mensaje o coincide\nalgún patrón con esta propiedad, NO será procesada por el destino.\n\nLa siguiente configuración de destinos especifica que el destino solo debe procesar los mensajes de error y\nde advertencia de las categorías que coincidan con alguno de los siguientes patrones `yii\\db\\*` o\n`yii\\web\\HttpException:*`, pero no con `yii\\web\\HttpException:404`.\n\n```php\n[\n    'class' => 'yii\\log\\FileTarget',\n    'levels' => ['error', 'warning'],\n    'categories' => [\n        'yii\\db\\*',\n        'yii\\web\\HttpException:*',\n    ],\n    'except' => [\n        'yii\\web\\HttpException:404',\n    ],\n]\n```\n\n> Info: Cuando se captura una excepción de tipo HTTP por el [gestor de errores](runtime-handling-errors.md), se\n  registrará un mensaje de error con el nombre de categoría con formato `yii\\web\\HttpException:ErrorCode`. Por\n  ejemplo, la excepción [[yii\\web\\NotFoundHttpException]] causará un mensaje de error del tipo\n  `yii\\web\\HttpException:404`.\n\n### Formato de los Mensajes <span id=\"message-formatting\"></span>\n\nLos destinos exportan los mensajes de registro filtrados en cierto formato. Por ejemplo, is se instala un\ndestino de registros de la calse [[yii\\log\\FileTarget]], encontraremos un registro similar en el archivo de\nregistro `runtime/log/app.log`:\n\n```\n2014-10-04 18:10:15 [::1][][-][trace][yii\\base\\Module::getModule] Loading module: debug\n```\n\nDe forma predeterminada los mensajes de registro se formatearan por [[yii\\log\\Target::formatMessage()]] como en el\nsiguiente ejemplo:\n\n```\nTimestamp [IP address][User ID][Session ID][Severity Level][Category] Message Text\n```\n\nSe puede personalizar el formato configurando la propiedad [[yii\\log\\Target::prefix]] que es un PHP ejecutable y\ndevuelve un prefijo de mensaje personalizado. Por ejemplo, el siguiente código configura un destino de registro\nanteponiendo a cada mensaje de registro el ID de usuario (se eliminan la dirección IP y el ID por razones de\nprivacidad).\n\n```php\n[\n    'class' => 'yii\\log\\FileTarget',\n    'prefix' => function ($message) {\n        $user = Yii::$app->has('user', true) ? Yii::$app->get('user') : null;\n        $userID = $user ? $user->getId(false) : '-';\n        return \"[$userID]\";\n    }\n]\n```\n\nAdemás de prefijos de mensaje, destinos de registros también añaden alguna información de contexto en cada lote\nde mensajes de registro. De forma predeterminada, se incluyen los valores de las siguientes variables globales de\nPHP: `$_GET`, `$_POST`, `$_FILES`, `$_COOKIE`, `$_SESSION` y `$_SERVER`. Se puede ajustar el comportamiento\nconfigurando la propiedad [[yii\\log\\Target::logVars]] con los nombres de las variables globales que se quieran incluir\ncon el destino del registro. Por ejemplo, la siguiente configuración de destino de registros especifica que\nsólo se añadirá al mensaje de registro el valor de la variable `$_SERVER`.\n\n```php\n[\n    'class' => 'yii\\log\\FileTarget',\n    'logVars' => ['_SERVER'],\n]\n```\n\nSe puede configurar `logVars` para que sea un array vacío para deshabilitar totalmente la inclusión de información de\ncontexto. O si se desea implementar un método propio de proporcionar información de contexto se puede sobrescribir el\nmétodo [[yii\\log\\Target::getContextMessage()]].\n\n### Nivel de Seguimiento de Mensajes <span id=\"trace-level\"></span>\n\nDurante el desarrollo, a veces se quiere visualizar de donde proviene cada mensaje de registro. Se puede lograr\nconfigurando la propiedad [[yii\\log\\Dispatcher::traceLevel|traceLevel]] del componente `log` como en el siguiente\nejemplo:\n\n```php\nreturn [\n    'bootstrap' => ['log'],\n    'components' => [\n        'log' => [\n            'traceLevel' => YII_DEBUG ? 3 : 0,\n            'targets' => [...],\n        ],\n    ],\n];\n```\n\nLa configuración de aplicación anterior establece el [[yii\\log\\Dispatcher::traceLevel|traceLevel]] para que sea 3 si\n`YII_DEBUG` esta habilitado y 0 si esta deshabilitado. Esto significa que si `YII_DEBUG` esta habilitado, a cada\nmensaje de registro se le añadirán como mucho 3 niveles de la pila de llamadas del mensaje que se este registrando; y\nsi `YII_DEBUG` está deshabilitado, no se incluirá información de la pila de llamadas.\n\n> Info: Obtener información de la pila de llamadas no es trivial. Por lo tanto, sólo se debe usar esta\n  característica durante el desarrollo o cuando se depura la aplicación.\n\n### Liberación (Flushing) y Exportación de Mensajes <span id=\"flushing-exporting\"></span>\n\nComo se ha comentado anteriormente, los mensajes de registro se mantienen en un array por el\n[[yii\\log\\Logger|logger object]]. Para limitar el consumo de memoria de este array, el componente encargado del\nregistro de mensajes enviará los mensajes registrados a los [destinos de registros](#log-targets) cada vez que el\narray acumule un cierto número de mensajes de registro. Se puede personalizar el número configurando la propiedad\n[[yii\\log\\Dispatcher::flushInterval|flushInterval]] del componente `log`:\n\n```php\nreturn [\n    'bootstrap' => ['log'],\n    'components' => [\n        'log' => [\n            'flushInterval' => 100,   // el valor predeterminado es 1000\n            'targets' => [...],\n        ],\n    ],\n];\n```\n\n> Info: También se produce la liberación de mensajes cuando la aplicación finaliza, esto asegura que los\n  destinos de los registros reciban los mensajes de registro.\n\nCuando el [[yii\\log\\Logger|logger object]] libera los mensajes de registro enviándolos a los\n[destinos de registros](#log-targets), estos no se exportan inmediatamente. La exportación de mensajes solo se\nproduce cuando un destino de registros acumula un cierto número de mensajes filtrados. Se puede personalizar este\nnúmero configurando la propiedad [[yii\\log\\Target::exportInterval|exportInterval]] de un\n[destinos de registros](#log-targets) individual, como se muestra a continuación,\n\n```php\n[\n    'class' => 'yii\\log\\FileTarget',\n    'exportInterval' => 100,  // el valor predeterminado es 1000\n]\n```\n\nDebido al nivel de configuración de la liberación y exportación de mensajes, de forma predeterminada cuando se llama a\n`Yii::debug()` o cualquier otro método de registro de mensajes, NO veremos el registro de mensaje inmediatamente en\nlos destinos de registros. Esto podría ser un problema para algunas aplicaciones de consola de ejecución\nprolongada (long-running). Para hacer que los mensajes de registro aparezcan inmediatamente en los destinos de\nregistro se deben establecer [[yii\\log\\Dispatcher::flushInterval|flushInterval]] y\n[[yii\\log\\Target::exportInterval|exportInterval]] para que tengan valor 1 como se muestra a continuación:\n\n```php\nreturn [\n    'bootstrap' => ['log'],\n    'components' => [\n        'log' => [\n            'flushInterval' => 1,\n            'targets' => [\n                [\n                    'class' => 'yii\\log\\FileTarget',\n                    'exportInterval' => 1,\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n> Note: El uso frecuente de liberación y exportación puede degradar el rendimiento de la aplicación.\n\n### Conmutación de Destinos de Registros <span id=\"toggling-log-targets\"></span>\n\nSe puede habilitar o deshabilitar un destino de registro configuración su propiedad\n[[yii\\log\\Target::enabled|enabled]]. Esto se puede llevar a cabo a mediante la configuración del destino de\nregistros o con la siguiente declaración PHP de código:\n\n```php\nYii::$app->log->targets['file']->enabled = false;\n```\n\nEl código anterior requiere que se asocie un destino como `file`, como se muestra a continuación usando las\nclaves de texto en el array `targets`:\n\n```php\nreturn [\n    'bootstrap' => ['log'],\n    'components' => [\n        'log' => [\n            'targets' => [\n                'file' => [\n                    'class' => 'yii\\log\\FileTarget',\n                ],\n                'db' => [\n                    'class' => 'yii\\log\\DbTarget',\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n### Creación de Nuevos Destinos <span id=\"new-targets\"></span>\n\nLa creación de nuevas clases de destinos de registro es muy simple. Se necesita implementar el método\n[[yii\\log\\Target::export()]] enviando el contenido del array [[yii\\log\\Target::messages]] al medio designado. Se puede\nllamar al método [[yii\\log\\Target::formatMessage()]] para formatear los mensajes. Se pueden encontrar más detalles de\ndestinos de registros en las clases incluidas en la distribución de Yii.\n\n## Perfilado de Rendimiento <span id=\"performance-profiling\"></span>\n\nEl Perfilado de rendimiento es un tipo especial de registro de mensajes que se usa para medir el tiempo que tardan en\nejecutarse ciertos bloques de código y encontrar donde están los cuellos de botella de rendimiento. Por ejemplo, la\nclase [[yii\\db\\Command]] utiliza el perfilado de rendimiento para encontrar conocer el tiempo que tarda cada consulta\na la base de datos.\n\nPara usar el perfilado de rendimiento, primero debemos identificar los bloques de código que tienen que ser\nperfilados, para poder enmarcar su contenido como en el siguiente ejemplo:\n\n```php\n\\Yii::beginProfile('myBenchmark');\n\n... Empieza el perfilado del bloque de código ...\n\n\\Yii::endProfile('myBenchmark');\n```\n\nDonde `myBenchmark` representa un token único para identificar el bloque de código. Después cuando se examine el\nresulte del perfilado, se podrá usar este token para encontrar el tiempo que ha necesitado el correspondiente bloque\nde código.\n\nEs importante asegurarse de que los pares de `beginProfile` y `endProfile` estén bien anidados. Por ejemplo,\n\n```php\n\\Yii::beginProfile('block1');\n\n    // código que será perfilado\n\n    \\Yii::beginProfile('block2');\n        // más código para perfilar\n    \\Yii::endProfile('block2');\n\n\\Yii::endProfile('block1');\n```\n\nSi nos dejamos el `\\Yii::endProfile('block1')` o lo intercambiamos `\\Yii::endProfile('block1')` con\n`\\Yii::endProfile('block2')`, el perfilado de rendimiento no funcionará.\n\nSe registra un mensaje de registro con el nivel de severidad `profile` para cada bloque de código que se haya\nperfilado. Se puede configurar el [destino del registro](#log-targets) para reunir todos los mensajes y exportarlos.\nEl [depurador de Yii](tool-debugger.md) incluye un panel de perfilado de rendimiento que muestra los resultados de\nperfilado.\n"
  },
  {
    "path": "docs/guide-es/runtime-overview.md",
    "content": "Información General\n===============\n\nCada vez que una aplicación Yii gestiona una petición, se somete a un flujo de trabajo similar.\n\n1. Un usuario hace una petición al [script de entrada](structure-entry-scripts.md) ‘web/index.php’.\n2. El script de entrada carga la [configuración](concept-configurations.md) y crea una instancia de la \n   [aplicación](structure-applications.md) para gestionar la petición.\n3. La aplicación resuelve la [ruta](runtime-routing.md) solicitada con la ayuda del componente \n   [petición](runtime-requests.md) de la aplicación.\n4. La aplicación crea una instancia del [controlador](structure-controllers.md) para gestionar la petición. \n5. El controlador crea una instancia de la [acción](structure-controllers.md) y ejecuta los filtros para la acción.\n6. Si algún filtro falla, se cancela la acción.\n7. Si pasa todos los filtros, se ejecuta la acción.\n8. La acción carga un modelo de datos, posiblemente de la base de datos.\n9. La acción renderiza una vista, proporcionándole el modelo de datos.\n10. El resultado renderizado se devuelve al componente [respuesta](runtime-responses.md) de la aplicación.\n11. El componente respuesta envía el resultado renderizado al navegador del usuario.\n\nEl siguiente diagrama muestra como una aplicación gestiona una petición.\n\n![Request Lifecycle](images/request-lifecycle.png)\n\nEn esta sección, se describirá en detalle cómo funcionan algunos de estos pasos.\n"
  },
  {
    "path": "docs/guide-es/runtime-requests.md",
    "content": "Peticiones\n==========\n\nLas peticiones (requests) hechas a una aplicación son representadas como objetos [[yii\\web\\Request]] que proporcionan \ninformación como parámetros de la petición, cabeceras HTTP, cookies, etc. Dada una petición, se puede acceder al \nobjeto request correspondiente a través del [componente de aplicación](structure-application-components.md) `request` \nque, por defecto, es una instancia de [[yii\\web\\Request]]. En esta sección se describirá como hacer uso de este \ncomponente en las aplicaciones.\n\n## Parámetros de Request <span id=\"request-parameters\"></span>\n\nPara obtener los parámetros de la petición, se puede llamar a los métodos [[yii\\web\\Request::get()|get()]] y \n[[yii\\web\\Request::post()|post()]] del componente `request`. Estos devuelven los valores de `$_GET` y `$_POST`, \nrespectivamente. Por ejemplo:\n\n```php\n$request = Yii::$app->request;\n\n$get = $request->get(); \n// equivalente a: $get = $_GET;\n\n$id = $request->get('id');\n// equivalente a: $id = isset($_GET['id']) ? $_GET['id'] : null;\n\n$id = $request->get('id', 1);\n// equivalente a: $id = isset($_GET['id']) ? $_GET['id'] : 1;\n\n$post = $request->post(); \n// equivalente a: $post = $_POST;\n\n$name = $request->post('name');\n// equivalente a: $name = isset($_POST['name']) ? $_POST['name'] : null;\n\n$name = $request->post('name', '');\n// equivalente a: $name = isset($_POST['name']) ? $_POST['name'] : '';\n```\n\n> Info: En lugar de acceder directamente a `$_GET` y `$_POST` para obtener los parámetros de la petición, es \n  recomendable que se obtengan mediante el componente `request` como en el ejemplo anterior. Esto facilitará la \n  creación de tests ya que se puede simular una componente de request con datos de peticiones personalizados.\n\nCuando se implementan [APIs RESTful](rest-quick-start.md), a menudo se necesita obtener parámetros enviados desde el \nformulario a través de PUT, PATCH u otros [métodos de request](runtime-requests.md#request-methods). Se pueden obtener \nestos parámetros llamando a los métodos [[yii\\web\\Request::getBodyParam()]]. Por ejemplo:\n\n```php\n$request = Yii::$app->request;\n\n// devuelve todos los parámetros\n$params = $request->bodyParams;\n\n// devuelve el parámetro \"id\"\n$param = $request->getBodyParam('id');\n```\n\n> Info: A diferencia de los parámetros `GET`, los parámetros enviados desde el formulario a través de `POST`, \n  `PUT`, `PATCH`, etc. se envían en el cuerpo de la petición. El componente `request` convierte los parámetros cuando \n  se acceda a él a través de los métodos descritos anteriormente. Se puede personalizar la manera en como los \n  parámetros se convierten configurando la propiedad [[yii\\web\\Request::parsers]].\n\n## Métodos de Request <span id=\"request-methods\"></span>\n\nSe puede obtener el método HTTP usado por la petición actual a través de la expresión `Yii::$app->request->method`. Se \nproporcionan un conjunto de propiedades booleanas para comprobar si el método actual es de un cierto tipo. Por ejemplo:\n\n```php\n$request = Yii::$app->request;\n\nif ($request->isAjax) { // la request es una request AJAX }\nif ($request->isGet)  { // el método de la request es GET }\nif ($request->isPost) { // el método de la request es POST }\nif ($request->isPut)  { // el método de la request es PUT }\n```\n\n## URLs de Request <span id=\"request-urls\"></span>\n\nEl componente `request` proporciona muchas maneras de inspeccionar la URL solicitada actualmente.\n\nAsumiendo que la URL que se está solicitando es `https://example.com/admin/index.php/product?id=100`, se pueden obtener \nvarias partes de la URL explicadas en los siguientes puntos:\n\n* [[yii\\web\\Request::url|url]]: devuelve `/admin/index.php/product?id=100`, que es la URL sin la parte de información \n  del host.\n* [[yii\\web\\Request::absoluteUrl|absoluteUrl]]: devuelve `https://example.com/admin/index.php/product?id=100`, que es \n  la URL entera, incluyendo la parte de información del host.\n* [[yii\\web\\Request::hostInfo|hostInfo]]: devuelve `https://example.com`, que es la parte de información del host \n  dentro de la URL.\n* [[yii\\web\\Request::pathInfo|pathInfo]]: devuelve `/product`, que es la parte posterior al script de entrada y \n  anterior al interrogante (query string)\n* [[yii\\web\\Request::queryString|queryString]]: devuelve `id=100`, que es la parte posterior al interrogante.\n* [[yii\\web\\Request::baseUrl|baseUrl]]: devuelve `/admin`, que es la parte posterior a la información del host y \n  anterior al nombre de script de entrada.\n* [[yii\\web\\Request::scriptUrl|scriptUrl]]: devuelve `/admin/index.php`, que es la URL sin la información del la ruta \n  ni la query string.\n* [[yii\\web\\Request::serverName|serverName]]: devuelve `example.com`, que es el nombre del host dentro de la URL.\n* [[yii\\web\\Request::serverPort|serverPort]]: devuelve 80, que es el puerto que usa el servidor web.\n\n## Cabeceras HTTP <span id=\"http-headers\"></span> \n\nSe pueden obtener la información de las cabeceras HTTP a través de [[yii\\web\\HeaderCollection|header collection]] \ndevueltas por la propiedad [[yii\\web\\Request::headers]]. Por ejemplo:\n\n```php\n// $headers es un objeto de yii\\web\\HeaderCollection \n$headers = Yii::$app->request->headers;\n\n// devuelve el valor Accept de la cabecera\n$accept = $headers->get('Accept');\n\nif ($headers->has('User-Agent')) { // la cabecera contiene un User-Agent }\n```\n\nEl componente `request` también proporciona soporte para acceder rápidamente a las cabeceras usadas más comúnmente, \nincluyendo:\n\n* [[yii\\web\\Request::userAgent|userAgent]]: devuelve el valor de la cabecera `User-Agen`.\n* [[yii\\web\\Request::contentType|contentType]]: devuelve el valor de la cabecera `Content-Type` que indica el tipo \n  MIME de los datos del cuerpo de la petición.\n* [[yii\\web\\Request::acceptableContentTypes|acceptableContentTypes]]: devuelve los tipos de contenido MIME aceptado \n  por los usuarios, ordenados por puntuación de calidad. Los que tienen mejor puntuación, se devolverán primero.\n* [[yii\\web\\Request::acceptableLanguages|acceptableLanguages]]: devuelve los idiomas aceptados por el usuario. Los \n  idiomas devueltos son ordenados según su orden de preferencia. El primer elemento representa el idioma preferido.\n\nSi la aplicación soporta múltiples idiomas y se quiere mostrar las páginas en el idioma preferido por el usuario, se \npuede usar el método de negociación de idioma [[yii\\web\\Request::getPreferredLanguage()]]. Este método obtiene una \nlista de idiomas soportados por la aplicación, comparados con \n[[yii\\web\\Request::acceptableLanguages|acceptableLanguages]], y devuelve el idioma más apropiado.\n\n> Tip: También se puede usar el filtro [[yii\\filters\\ContentNegotiator|ContentNegotiator]] para determinar \ndiatónicamente el content type y el idioma que debe usarse en la respuesta. El filtro implementa la negociación de \ncontenido en la parte superior de las propiedades y métodos descritos anteriormente.\n\n## Información del cliente <span id=\"client-information\"></span>\n\nSe puede obtener el nombre del host y la dirección IP de la máquina cliente a través de \n[[yii\\web\\Request::userHost|userHost]] y [[yii\\web\\Request::userIP|userIP]], respectivamente. Por ejemplo:\n\n```php\n$userHost = Yii::$app->request->userHost;\n$userIP = Yii::$app->request->userIP;\n```\n"
  },
  {
    "path": "docs/guide-es/runtime-responses.md",
    "content": "Respuestas\n==========\n\nCuando una aplicación finaliza la gestión de una [petición (request)](runtime-requests.md), genera un objeto \n[[yii\\web\\Response|response]] y lo envía al usuario final. El objeto response contiene información tal como el código \nde estado (status code) HTTP, las cabeceras (headers) HTTP y el cuerpo (body). El objetivo final del desarrollo de una \naplicación Web es esencialmente construir objetos response para varias peticiones.\n\nEn la mayoría de casos principalmente se debe tratar con \n[componentes de aplicación](structure-application-components.md) de tipo `response` que, por defecto, son una \ninstancia de [[yii\\web\\Response]]. Sin embargo, Yii permite crear sus propios objetos `response` y enviarlos al \nusuario final tal y como se explica a continuación.\n\nEn esta sección, se describirá como generar y enviar respuestas a usuarios finales.\n\n## Códigos de Estado <span id=\"status-code\"></span>\n\nUna de las primeras cosas que debería hacerse cuando se genera una respuesta es indicar si la petición se ha \ngestionado correctamente. Esto se indica asignando la propiedad [[yii\\web\\Response::statusCode]] a la que se le puede \nasignar cualquier valor válido dentro de los \n[códigos de estado HTTP](https://tools.ietf.org/html/rfc2616#section-10). Por ejemplo, para indicar que la \npetición se ha gestionado correctamente, se puede asignar el código de estado a 200, como en el siguiente ejemplo:\n\n```php\nYii::$app->response->statusCode = 200;\n```\n\nSin embargo, en la mayoría de casos nos es necesario asignar explícitamente el código de estado. Esto se debe a que el \nvalor por defecto de [[yii\\web\\Response::statusCode]] es 200. Y si se quiere indicar que la petición ha fallado, se \npuede lanzar una excepción HTTP apropiada como en el siguiente ejemplo:\n\n```php\nthrow new \\yii\\web\\NotFoundHttpException;\n```\n\nCuando el [error handler](runtime-handling-errors.md) captura una excepción, obtendrá el código de estado de la \nexcepción y lo asignará a la respuesta. En el caso anterior, la excepción [[yii\\web\\NotFoundHttpException]] está \nasociada al estado HTTP 404. En Yii existen las siguientes excepciones predefinidas.\n\n* [[yii\\web\\BadRequestHttpException]]: código de estado 400.\n* [[yii\\web\\ConflictHttpException]]: código de estado 409.\n* [[yii\\web\\ForbiddenHttpException]]: código de estado 403.\n* [[yii\\web\\GoneHttpException]]: código de estado 410.\n* [[yii\\web\\MethodNotAllowedHttpException]]: código de estado 405.\n* [[yii\\web\\NotAcceptableHttpException]]: código de estado 406. \n* [[yii\\web\\NotFoundHttpException]]: código de estado 404.\n* [[yii\\web\\ServerErrorHttpException]]: código de estado 500.\n* [[yii\\web\\TooManyRequestsHttpException]]: código de estado 429.\n* [[yii\\web\\UnauthorizedHttpException]]: código de estado 401.\n* [[yii\\web\\UnsupportedMediaTypeHttpException]]: código de estado 415.\n\nSi la excepción que se quiere lanzar no se encuentra en la lista anterior, se puede crear una extendiendo \n[[yii\\web\\HttpException]], o directamente lanzando un código de estado, por ejemplo:\n\n```php\nthrow new \\yii\\web\\HttpException(402);\n```\n\n## Cabeceras HTTP <span id=\"http-headers\"></span> \n\nSe puede enviar cabeceras HTTP modificando el [[yii\\web\\Response::headers|header collection]] en el componente \n`response`. Por ejemplo:\n\n```php\n$headers = Yii::$app->response->headers;\n\n// añade una cabecera Pragma. Las cabeceras Pragma existentes NO se sobrescribirán.\n$headers->add('Pragma', 'no-cache');\n\n// asigna una cabecera Pragma. Cualquier cabecera Pragma existente será descartada.\n$headers->set('Pragma', 'no-cache');\n\n// Elimina las cabeceras Pragma y devuelve los valores de las eliminadas en un array\n$values = $headers->remove('Pragma');\n```\n\n> Info: Los nombres de las cabeceras case insensitive, es decir, no discriminan entre mayúsculas y minúsculas. \nAdemás, las nuevas cabeceras registradas no se enviarán al usuario hasta que se llame al método \n[[yii\\web\\Response::send()]].\n\n## Cuerpo de la Respuesta<span id=\"response-body\"></span>\n\nLa mayoría de las respuestas deben tener un cuerpo que contenga el contenido que se quiere mostrar a los usuarios \nfinales.\n\nSi ya se tiene un texto de cuerpo con formato, se puede asignar a la propiedad [[yii\\web\\Response::content]] del \nresponse. Por ejemplo:\n\n```php\nYii::$app->response->content = 'hello world!';\n```\n\nSi se tiene que dar formato a los datos antes de enviarlo al usuario final, se deben asignar las propiedades \n[[yii\\web\\Response::format|format]] y [[yii\\web\\Response::data|data]]. La propiedad [[yii\\web\\Response::format|format]]\n especifica que formato debe tener [[yii\\web\\Response::data|data]]. Por ejemplo:\n\n```php\n$response = Yii::$app->response;\n$response->format = \\yii\\web\\Response::FORMAT_JSON;\n$response->data = ['message' => 'hello world'];\n```\n\nYii soporta a los siguientes formatos de forma predeterminada, cada uno de ellos implementado por una clase \n[[yii\\web\\ResponseFormatterInterface|formatter]]. Se pueden personalizar los formatos o añadir nuevos sobrescribiendo \nla propiedad [[yii\\web\\Response::formatters]].\n\n* [[yii\\web\\Response::FORMAT_HTML|HTML]]: implementado por [[yii\\web\\HtmlResponseFormatter]].\n* [[yii\\web\\Response::FORMAT_XML|XML]]: implementado por [[yii\\web\\XmlResponseFormatter]].\n* [[yii\\web\\Response::FORMAT_JSON|JSON]]: implementado por [[yii\\web\\JsonResponseFormatter]].\n* [[yii\\web\\Response::FORMAT_JSONP|JSONP]]: implementado por [[yii\\web\\JsonResponseFormatter]].\n\nMientras el cuerpo de la respuesta puede ser mostrado de forma explicita como se muestra a en el anterior ejemplo, en \nla mayoría de casos se puede asignar implícitamente por el valor de retorno de los métodos de \n[acción](structure-controllers.md). El siguiente, es un ejemplo de uso común:\n\n```php\npublic function actionIndex()\n{\n    return $this->render('index');\n}\n```\n\nLa acción `index` anterior, devuelve el resultado renderizado de la vista `index`. El valor devuelto será recogido por \nel componente `response`, se le aplicará formato y se enviará al usuario final.\n\nPor defecto, el formato de respuesta es [[yii\\web\\Response::FORMAT_HTML|HTML]], sólo se debe devolver un string en un \nmétodo de acción.  Si se quiere usar un formato de respuesta diferente, se debe asignar antes de devolver los datos. \nPor ejemplo:\n\n```php\npublic function actionInfo()\n{\n    \\Yii::$app->response->format = \\yii\\web\\Response::FORMAT_JSON;\n    return [\n        'message' => 'hello world',\n        'code' => 100,\n    ];\n}\n```\n\nComo se ha mencionado, además de usar el componente de aplicación `response` predeterminado, también se pueden crear \nobjetos response propios y enviarlos a los usuarios finales. Se puede hacer retornando un objeto en el método de \nacción, como en el siguiente ejemplo:\n\n```php\npublic function actionInfo()\n{\n    return \\Yii::createObject([\n        'class' => 'yii\\web\\Response',\n        'format' => \\yii\\web\\Response::FORMAT_JSON,\n        'data' => [\n            'message' => 'hello world',\n            'code' => 100,\n        ],\n    ]);\n}\n```\n\n> Note: Si se crea un objeto response propio, no se podrán aprovechar las configuraciones asignadas para el componente \n`response` en la configuración de la aplicación. Sin embargo, se puede usar la \n[inyección de dependencias](concept-di-container.md) para aplicar la configuración común al nuevo objeto response.\n\n## Redirección del Navegador <span id=\"browser-redirection\"></span>\n\nLa redirección del navegador se basa en el envío de la cabecera HTTP `Location`. Debido a que esta característica se \nusa comúnmente, Yii proporciona soporte especial para ello.\n\nSe puede redirigir el navegador a una URL llamando al método [[yii\\web\\Response::redirect()]]. El método asigna la \ncabecera de `Location` apropiada con la URL proporcionada y devuelve el objeto response él mismo. En un método de \nacción, se puede acceder a él mediante el acceso directo [[yii\\web\\Controller::redirect()]] como en el siguiente \nejemplo:\n\n```php\npublic function actionOld()\n{\n    return $this->redirect('https://example.com/new', 301);\n}\n```\n\nEn el ejemplo anterior, el método de acción devuelve el resultado del método `redirect()`. Como se ha explicado antes, \nel objeto response devuelto por el método de acción se usará como respuesta enviándola al usuario final.\n\nEn otros sitios que no sean los métodos de acción, se puede llamar a [[yii\\web\\Response::redirect()]] directamente \nseguido por una llamada al método [[yii\\web\\Response::send()]] para asegurar que no habrá contenido extra en la \nrespuesta.\n\n```php\n\\Yii::$app->response->redirect('https://example.com/new', 301)->send();\n```\n\n> Info: De forma predeterminada, el método [[yii\\web\\Response::redirect()]] asigna el estado de respuesta al \ncódigo de estado 302 que indica al navegador que recurso solicitado está *temporalmente* alojado en una URI diferente. \nSe puede enviar un código de estado 301 para expresar que el recurso se ha movido de forma *permanente*.\n\nCuando la petición actual es de una petición AJAX, el hecho de enviar una cabecera `Location` no causará una \nredirección del navegador automática. Para resolver este problema, el método [[yii\\web\\Response::redirect()]] asigna \nuna cabecera `X-Redirect` con el valor de la URL de redirección. En el lado del cliente se puede escribir código \nJavaScript para leer la esta cabecera y redireccionar el navegador como corresponda.\n\n> Info: Yii contiene el archivo JavaScript `yii.js` que proporciona un conjunto de utilidades comunes de \nJavaScript, incluyendo la redirección de navegador basada en la cabecera `X-Redirect`. Por tanto, si se usa este \nfichero JavaScript (registrándolo *asset bundle* [[yii\\web\\YiiAsset]]), no se necesitará escribir nada más para tener \nsoporte en redirecciones AJAX.\n\n## Enviar Archivos <span id=\"sending-files\"></span>\n\nIgual que con la redirección, el envío de archivos es otra característica que se basa en cabeceras HTTP especificas. \nYii proporciona un conjunto de métodos para dar soporte a varias necesidades del envío de ficheros. Todos ellos \nincorporan soporte para el rango de cabeceras HTTP.\n\n* [[yii\\web\\Response::sendFile()]]: envía un fichero existente al cliente.\n* [[yii\\web\\Response::sendContentAsFile()]]: envía un string al cliente como si fuera un archivo.\n* [[yii\\web\\Response::sendStreamAsFile()]]: envía un *file stream* existente al cliente como si fuera un archivo.\n\nEstos métodos tienen la misma firma de método con el objeto response como valor de retorno. Si el archivo que se envía \nes muy grande, se debe considerar usar [[yii\\web\\Response::sendStreamAsFile()]] porque es más efectivo en términos de \nmemoria. El siguiente ejemplo muestra como enviar un archivo en una acción de controlador.\n\n```php\npublic function actionDownload()\n{\n    return \\Yii::$app->response->sendFile('ruta/del/fichero.txt');\n}\n```\n\nSi se llama al método de envío de ficheros fuera de un método de acción, también se debe llamar al método \n[[yii\\web\\Response::send()]] después para asegurar que no se añada contenido extra a la respuesta.\n\n```php\n\\Yii::$app->response->sendFile('ruta/del/fichero.txt')->send();\n```\n\nAlgunos servidores Web tienen un soporte especial para enviar ficheros llamado *X-Sendfile*. La idea es redireccionar \nla petición para un fichero a un servidor Web que servirá el fichero directamente. Como resultado, la aplicación Web \npuede terminar antes mientras el servidor Web envía el fichero. Para usar esta funcionalidad, se puede llamar a \n[[yii\\web\\Response::xSendFile()]]. La siguiente lista resume como habilitar la característica `X-Sendfile` para \nalgunos servidores Web populares.\n\n- Apache: [X-Sendfile](https://tn123.org/mod_xsendfile)\n- Lighttpd v1.4: [X-LIGHTTPD-send-file](https://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file)\n- Lighttpd v1.5: [X-Sendfile](https://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file)\n- Nginx: [X-Accel-Redirect](https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile/)\n- Cherokee: [X-Sendfile and X-Accel-Redirect](https://www.cherokee-project.com/doc/other_goodies.html#x-sendfile)\n\n## Enviar la Respuesta <span id=\"sending-response\"></span>\n\nEl contenido en una respuesta no se envía al usuario hasta que se llama al método [[yii\\web\\Response::send()]]. De \nforma predeterminada, se llama a este método automáticamente al final de [[yii\\base\\Application::run()]]. Sin embargo, \nse puede llamar explícitamente a este método forzando el envío de la respuesta inmediatamente.\n\nEl método [[yii\\web\\Response::send()]] sigue los siguientes pasos para enviar una respuesta:\n\n1. Lanza el evento [[yii\\web\\Response::EVENT_BEFORE_SEND]].\n2. Llama a [[yii\\web\\Response::prepare()]] para convertir el [[yii\\web\\Response::data|response data]] en \n   [[yii\\web\\Response::content|response content]].\n3. Lanza el evento [[yii\\web\\Response::EVENT_AFTER_PREPARE]].\n4. Llama a [[yii\\web\\Response::sendHeaders()]] para enviar las cabeceras HTTP registradas.\n5. Llama a [[yii\\web\\Response::sendContent()]] para enviar el contenido del cuerpo de la respuesta.\n6. Lanza el evento [[yii\\web\\Response::EVENT_AFTER_SEND]].\n\nDespués de llamar a [[yii\\web\\Response::send()]] por primera vez, cualquier llamada a este método será ignorada. Esto \nsignifica que una vez se envíe una respuesta, no se le podrá añadir más contenido.\n\nComo se puede observar, el método [[yii\\web\\Response::send()]] lanza varios eventos útiles. Al responder a estos \neventos, es posible ajustar o decorar la respuesta.\n"
  },
  {
    "path": "docs/guide-es/runtime-routing.md",
    "content": "Enrutamiento y Creación de URLS\n===============================\n\nCuando una aplicación Yii empieza a procesar una URL solicitada, lo primero que hace es convertir la URL en una \n[ruta](structure-controllers.md#routes). Luego se usa la ruta para instanciar la \n[acción de controlador](structure-controllers.md) correspondiente para gestionar la petición. A este proceso se \nle llama *enrutamiento*.\n\nEl proceso inverso se llama *creación de URLs*, y crea una URL a partir de una ruta dada y unos parámetros de consulta (query) asociados. Cuando posteriormente se solicita la URL creada, el proceso de enrutamiento puede resolverla y \nconvertirla en la ruta original con los parámetros asociados.\n\nLa principal pieza encargada del enrutamiento y de la creación de URLs es [[yii\\web\\UrlManager|URL manager]], que se \nregistra como el componente de aplicación `urlManager`. El [[yii\\web\\UrlManager|URL manager]] proporciona el método \n[[yii\\web\\UrlManager::parseRequest()|parseRequest()]] para convertir una petición entrante en una ruta y sus \nparámetros asociados y el método [[yii\\web\\UrlManager::createUrl()|createUrl()]] para crear una URL a partir de una \nruta dada y sus parámetros asociados.\n\nConfigurando el componente `urlManager` en la configuración de la aplicación, se puede dotar a la aplicación de \nreconocimiento arbitrario de formatos de URL sin modificar el código de la aplicación existente.  Por ejemplo, se \npuede usar el siguiente código para crear una URL para la acción `post/view`:\n\n```php\nuse yii\\helpers\\Url;\n\n// Url::to() llama a UrlManager::createUrl() para crear una URL\n$url = Url::to(['post/view', 'id' => 100]);\n```\n\nDependiendo de la configuración de `urlManager`, la URL generada puede asemejarse a alguno de los siguientes (u otro) \nformato. Y si la URL creada se solicita posteriormente, se seguirá convirtiendo en la ruta original y los valores de \nlos parámetros.\n\n```\n/index.php?r=post/view&id=100\n/index.php/post/100\n/posts/100\n```\n\n\n## Formatos de URL <span id=\"url-formats\"></span>\n\nEl [[yii\\web\\UrlManager|URL manager]] soporta dos formatos de URL: el formato predeterminado de URL y el formato URL \namigable (pretty URL).\n\nEl formato de URL predeterminado utiliza un parámetro de consulta llamado `r` para representar la ruta y los \nparámetros normales de la petición para representar los parámetros asociados con la ruta. Por ejemplo, la URL \n`/index.php?r=post/view&id=100` representa la ruta `post/view` y 100 es el valor del parámetro `id` de la consulta. \nEl formato predeterminado de URL no requiere ningún tipo de configuración para [[yii\\web\\UrlManager|URL manager]] y \nfunciona en cualquier configuración de servidor Web.\n\nEl formato de URL amigable utiliza la ruta adicional a continuación del nombre del script de entrada (entry script) \npara representar la ruta y los parámetros de consulta. Por ejemplo, La ruta en la URL `/index.php/post/100` es \n`/post/100` que puede representar la ruta `post/view` y el parámetro de consulta `id` 100 con una \n[[yii\\web\\UrlManager::rules|URL rule]] apropiada. Para poder utilizar el formato de URL amigable, se tendrán que \ndiseñar una serie de [[yii\\web\\UrlManager::rules|URL rules]] de acuerdo con el requerimiento actual acerca de como \ndeben mostrarse las URLs.\n\nSe puede cambiar entre los dos formatos de URL conmutando la propiedad \n[[yii\\web\\UrlManager::enablePrettyUrl|enablePrettyUrl]] del [[yii\\web\\UrlManager|URL manager]] sin cambiar ningún \notro código de aplicación.\n\n## Enrutamiento <span id=\"routing\"></span>\n\nEl Enrutamiento involucra dos pasos. El primero, la petición (request) entrante se convierte en una ruta y sus \nparámetros de consulta asociados. En el segundo paso, se crea la correspondiente \n[acción de controlador](structure-controllers.md) para la ruta convertida para que gestione la petición.\n\nCuando se usa el formato predefinido de URL, convertir una petición en una ruta es tan simple como obtener los valores \ndel parámetro de consulta `GET` llamado `r`.\n\nCuando se usa el formato de URL amigable, el [[yii\\web\\UrlManager|URL manager]] examinará las \n[[yii\\web\\UrlManager::rules|URL rules]] registradas para encontrar alguna que pueda convertir la petición en una ruta. \nSi no se encuentra tal regla, se lanzará una excepción de tipo [[yii\\web\\NotFoundHttpException]].\n\nUna vez que la petición se ha convertido en una ruta, es el momento de crear la acción de controlador identificada \npor la ruta. La ruta se desglosa en múltiples partes a partir de las barras que contenga. Por ejemplo, `site/index` \nserá desglosado en `site` e `index`. Cada parte is un ID que puede hacer referencia a un modulo, un controlador o una \nacción. Empezando por la primera parte de la ruta, la aplicación, sigue los siguientes pasos para generar \n(si los hay), controladores y acciones:\n\n1. Establece la aplicación como el modulo actual.\n2. Comprueba si el [[yii\\base\\Module::controllerMap|controller map]] del modulo actual contiene un ID actual. Si lo \n   tiene, se creará un objeto controlador de acuerdo con la configuración del controlador encontrado en el mapa, y \n   se seguirá el Paso 5 para gestionar la parte restante de la ruta. \n3. Comprueba si el ID hace referencia a un modulo listado en la propiedad [[yii\\base\\Module::modules|modules]] del \n   módulo actual. Si está listado, se crea un modulo de acuerdo con la configuración encontrada en el listado de \n   módulos, y se seguirá el Paso 2 para gestionar la siguiente parte de la ruta bajo el contexto de la creación de un \n   nuevo módulo.\n4. Trata el ID como si se tratara de un ID de controlador y crea un objeto controlador. Sigue el siguiente paso con la    parte restante de la ruta.\n5. El controlador busca el ID en su [[yii\\base\\Controller::actions()|action map]]. Si lo encuentra, crea una acción de    acuerdo con la configuración encontrada en el mapa. De otra forma, el controlador intenta crear una acción en linea    definida por un método de acción correspondiente al ID actual.\n\nSi ocurre algún error entre alguno de los pasos anteriores, se lanzará una excepción de tipo \n[[yii\\web\\NotFoundHttpException]], indicando el fallo de proceso de enrutamiento.\n\n### Ruta Predeterminada <span id=\"default-route\"></span>\n\nCuando una petición se convierte en una ruta vacía, se usa la llamada *ruta predeterminada*. Por defecto, la ruta \npredeterminada es `site/index`, que hace referencia a la acción `index` del controlador `site`. Se puede personalizar \nconfigurando la propiedad [[yii\\web\\Application::defaultRoute|defaultRoute]] de la aplicación en la configuración de \naplicación como en el siguiente ejemplo:\n\n```php\n[\n    // ...\n    'defaultRoute' => 'main/index',\n];\n```\n\n\n### Ruta `catchAll` <span id=\"catchall-route\"></span>\n\nA veces, se puede querer poner la aplicación Web en modo de mantenimiento temporalmente y mostrar la misma pagina de \ninformación para todas las peticiones. Hay varias maneras de lograr este objetivo. Pero una de las maneras más simples \nes configurando la propiedad [[yii\\web\\Application::catchAll]] como en el siguiente ejemplo de configuración de \naplicación:\n\n```php\n[\n    // ...\n\t'catchAll' => ['site/offline'],\n];\n```\n\nCon la anterior configuración, se usar la acción `site/offline` para gestionar todas las peticiones entrantes.\n\nLa propiedad `catchAll` debe tener un array cuyo primer elemento especifique una ruta, y el resto de elementos \n(pares nombre-valor) especifiquen los parámetros [ligados a la acción](structure-controllers.md#action-parameters).\n\n## Creación de URLs <span id=\"creating-urls\"></span>\n\nYii proporciona un método auxiliar (helper method) [[yii\\helpers\\Url::to()]] para crear varios tipos de URLs a partir \nde las rutas dadas y sus parámetros de consulta asociados. Por ejemplo, \n\n```php\nuse yii\\helpers\\Url;\n\n// crea una URL para la ruta: /index.php?r=post/index\necho Url::to(['post/index']);\n\n// crea una URL para la ruta con parámetros: /index.php?r=post/view&id=100\necho Url::to(['post/view', 'id' => 100]);\n\n// crea una URL interna: /index.php?r=post/view&id=100#contentecho \nUrl::to(['post/view', 'id' => 100, '#' => 'content']);\n\n// crea una URL absoluta: https://www.example.com/index.php?r=post/index\necho Url::to(['post/index'], true);\n\n// crea una URL absoluta usando el esquema https: https://www.example.com/index.php?r=post/index\necho Url::to(['post/index'], 'https');\n```\n\nHay que tener en cuenta que en el anterior ejemplo, asumimos que se está usando el formato de URL predeterminado. \nSi habilita el formato de URL amigable, las URLs creadas serán diferentes, de acuerdo con las \n[[yii\\web\\UrlManager::rules|URL rules]] que se usen.\n\nLa ruta que se pasa al método [[yii\\helpers\\Url::to()]] es context sensitive. Esto quiere decir que puede ser una ruta \n*relativa* o una ruta *absoluta* que serán tipificadas de acuerdo con las siguientes reglas:\n\n- Si una ruta es una cadena vacía, se usará la [[yii\\web\\Controller::route|route]] solicitada actualmente. \n- Si la ruta no contiene ninguna barra `/`, se considerará que se trata de un ID de acción del controlador actual y se \n  le antepondrá el valor [[\\yii\\web\\Controller::uniqueId|uniqueId]] del controlador actual. \n- Si la ruta no tiene barra inicial, se considerará que se trata de una ruta relativa al modulo actual y se le \n  antepondrá el valor [[\\yii\\base\\Module::uniqueId|uniqueId]] del modulo actual.\n\nPor ejemplo, asumiendo que el modulo actual es `admin` y el controlador actual es `post`,\n\n```php\nuse yii\\helpers\\Url;\n\n// la ruta solicitada: /index.php?r=admin/post/index\necho Url::to(['']);\n\n// una ruta relativa solo con ID de acción: /index.php?r=admin/post/index\necho Url::to(['index']);\n\n// una ruta relativa: /index.php?r=admin/post/index\necho Url::to(['post/index']);\n\n// una ruta absoluta: /index.php?r=post/index\necho Url::to(['/post/index']);\n```\n\nEl método [[yii\\helpers\\Url::to()]] se implementa llamando a los métodos \n[[yii\\web\\UrlManager::createUrl()|createUrl()]] y [[yii\\web\\UrlManager::createAbsoluteUrl()|createAbsoluteUrl()]] del \n[[yii\\web\\UrlManager|URL manager]]. En las próximas sub-secciones, explicaremos como configurar el \n[[yii\\web\\UrlManager|URL manager]] para personalizar el formato de las URLs generadas.\n\nEl método [[yii\\helpers\\Url::to()]] también soporta la creación de URLs NO relacionadas con rutas particulares. \nEn lugar de pasar un array como su primer paramento, se debe pasar una cadena de texto. Por ejemplo,\n\n```php\nuse yii\\helpers\\Url;\n\n// la URL solicitada actualmente: /index.php?r=admin/post/index\necho Url::to();\n\n// una URL con alias: https://example.comYii::setAlias('@example', 'https://example.com/');\necho Url::to('@example');\n\n// una URL absoluta: https://example.com/images/logo.gif\necho Url::to('/images/logo.gif', true);```\n```\n\nAdemás del método `to()`, la clase auxiliar [[yii\\helpers\\Url]] también proporciona algunos otros métodos de creación \nde URLs. Por ejemplo,\n\n```php\nuse yii\\helpers\\Url;\n\n// URL de la página inicial: /index.php?r=site/index\necho Url::home();\n\n// la URL base, útil si la aplicación se desarrolla en una sub-carpeta de la carpeta raíz (root) Web\necho Url::base();\n\n// la URL canónica de la actual URL solicitada// visitar https://en.wikipedia.org/wiki/Canonical_link_element\necho Url::canonical();\n\n// recuerda la actual URL solicitada y la recupera más tarde requestsUrl::remember();\necho Url::previous();\n```\n\n\n## Uso de URLs Amigables <span id=\"using-pretty-urls\"></span>\n\nPara utilizar URLs amigables, hay que configurar el componente `ulrManager` en la configuración de la aplicación como \nen el siguiente ejemplo:\n\n```php\n[\n    'components' => [\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            'showScriptName' => false,\n            'enableStrictParsing' => true,\n            'rules' => [\n                // ...\n            ],\n        ],\n    ],\n]\n```\n\nLa propiedad [[yii\\web\\UrlManager::enablePrettyUrl|enablePrettyUrl]] es obligatoria ya que alterna el formato de URL \namigable. El resto de propiedades son opcionales. Sin embargo, la anterior configuración es la más común.\n\n* [[yii\\web\\UrlManager::showScriptName|showScriptName]]: esta propiedad determina si el script de entrada debe ser \n  incluido en las URLs generadas. Por ejemplo, en lugar de crear una URL `/index.php/post/100`, estableciendo la \n  propiedad con valor `true`, la URL que se generará sera `/post/100`.\n* [[yii\\web\\UrlManager::enableStrictParsing|enableStrictParsing]]: esta propiedad determina si se habilita la \n  conversión de petición estricta, si se habilita, la URL solicitada tiene que encajar al menos con uno de las \n  [[yii\\web\\UrlManager::rules|rules]] para poder ser tratada como una petición valida, o se lanzará una \n  [[yii\\web\\NotFoundHttpException]]. Si la conversión estricta esta deshabilitada, cuando ninguna de las \n  [[yii\\web\\UrlManager::rules|rules]] coincida con la URL solicitada, la parte de información de la URL se tratará \n  como si fuera la ruta solicitada.\n* [[yii\\web\\UrlManager::rules|rules]]: esta propiedad contiene una lista de las reglas que especifican como convertir \n  y crear URLs. Esta es la propiedad principal con la que se debe trabajar para crear URLs que satisfagan el formato \n  de un requerimiento particular de la aplicación.\n\n> Note: Para ocultar el nombre del script de entrada en las URLs generadas, además de establecer el \n  [[yii\\web\\UrlManager::showScriptName|showScriptName]] a falso, puede ser necesaria la configuración del servidor Web \n  para que identifique correctamente que script PHP debe ejecutarse cuando se solicita una URL que no lo especifique. \n  Si se usa el servidor Web Apache, se puede utilizar la configuración recomendada descrita en la sección de \n  [Instalación](start-installation.md#recommended-apache-configuration).\n\n\n### Reglas de URL <span id=\"url-rules\"></span>\n\nUna regla de URL es una instancia de [[yii\\web\\UrlRule]] o de una clase hija. Cada URL consiste en un patrón utilizado \npara cotejar la parte de información de ruta de las URLs, una ruta, y algunos parámetros de consulta. Una URL puede \nusarse para convertir una petición si su patrón coincide con la URL solicitada y una regla de URL pude usarse para \ncrear una URL si su ruta y sus nombres de parámetros coinciden con los que se hayan dado.\n\nCuando el formato de URL amigables está habilitado, el [[yii\\web\\UrlManager|URL manager]] utiliza las reglas de URL \ndeclaradas en su propiedad [[yii\\web\\UrlManager::rules|rules]] para convertir las peticiones entrantes y crear URLs. \nEn particular, para convertir una petición entrante, el [[yii\\web\\UrlManager|URL manager]] examina las reglas en el \norden que se han declarado y busca la *primera* regla que coincida con la URL solicitada. La regla que coincide es la \nque se usa para convertir la URL en una ruta y sus parámetros asociados. De igual modo, para crear una URL, el \n[[yii\\web\\UrlManager|URL manager]] busca la primera regla que coincida con la ruta dad y los parámetros y la utiliza \npara crear una URL.\n\nSe pueden configurar las [[yii\\web\\UrlManager::rules]] como un array con claves, siendo los patrones y las reglas sus \ncorrespondientes rutas. Cada pareja patrón-ruta construye una regla de URL. Por ejemplo, la siguiente configuración de \nconfiguración de [[yii\\web\\UrlManager::rules|rules]] declara dos reglas de URL. La primera regla coincide con una URL \n`posts` y la mapea a la ruta `post/index`. La segunda regla coincide con una URL que coincida con la expresión regular \n`post/(\\d+)` y la mapea a la ruta `post/view` y el parámetro llamado `id`.\n\n```php\n[\n    'posts' => 'post/index', \n    'post/<id:\\d+>' => 'post/view',\n]\n```\n\n> Información; El patrón en una regla se usa para encontrar coincidencias en la parte de información de la URL. \n  Por ejemplo, la parte de información de la ruta `/index.php/post/100?source=ad` es `post/100` \n  (la primera barra y la ultima son ignoradas) que coincide con el patrón `post/(\\d+)`.\n\nEntre la declaración de reglas de URL como pares de patrón-ruta, también se pueden declarar como arrays de \nconfiguración. Cada array de configuración se usa para configurar un único objeto de tipo regla de URL. Este proceso \nse necesita a menudo cuando se quieren configurar otras propiedades de la regla de URL. Por ejemplo,\n\n```php\n[\n    // ... otras reglas de URL ...\n    \n    [\n        'pattern' => 'posts',\n        'route' => 'post/index',\n        'suffix' => '.json',\n    ],\n]\n```\n\nDe forma predeterminada si no se especifica la opción `class` en la configuración de una regla, se utilizará la clase \npredeterminada [[yii\\web\\UrlRule]].\n\n\n### Parameters Asociativos <span id=\"named-parameters\"></span>\n\nUna regla de URL puede asociarse a una determinado grupo de parámetros de consulta que se hayan sido especificados en \nel patrón con el formato `<ParamName:RegExp>`, donde `ParamName` especifica el nombre del parámetro y `RegExp` es una \nexpresión regular opcional que se usa para encontrar los valores de los parámetros. Si no se especifica `RegExp` \nsignifica que el parámetro debe ser una cadena de texto sin ninguna barra.\n\n> Note: Solo se pueden especificar expresiones regulares para los parámetros. La parte restante del patrón se \n  considera texto plano.\n\nCuando se usa una regla para convertir una URL, esta rellenara los parámetros asociados con los valores que coincidan \ncon las partes correspondientes de la URL, y estos parámetros serán accesibles posteriormente mediante `$_GET` por el \ncomponente de aplicación `request`. Cuando se usa una regla para crear una URL, esta obtendrá los valores de los \nparámetros proporcionados y los insertara donde se hayan declarado los parámetros.\n\nVamos a utilizar algunos ejemplos para ilustrar como funcionan los parámetros asociativos. Asumiendo que hemos \ndeclarado las siguientes tres URLs:\n\n```php\n[\n    'posts' => 'post/index',\n    'post/<id:\\d+>' => 'post/view',\n    'posts/<year:\\d{4}>/<category>' => 'post/index',\n]\n```\n\nCuando se usen las reglas para convertir URLs:\n\n- `/index.php/posts` se convierte en la ruta `post/index` usando la primera regla;\n- `/index.php/posts/2014/php` se convierte en la ruta `post/index`, el parámetro `year` cuyo valor es 2014 y el \n  parámetro `category` cuyo valor es `php` usando la tercera regla;\n- `/index.php/post/100` se convierte en la ruta `post/view` y el parámetro `id` cuyo valor es 100 usando la segunda \n  regla;\n- `/index.php/posts/php` provocara una [[yii\\web\\NotFoundHttpException]] cuando \n  [[yii\\web\\UrlManager::enableStrictParsing]] sea `true`, ya que no coincide ninguno de los parámetros . Si \n  [[yii\\web\\UrlManager::enableStrictParsing]] es `false` (valor predeterminado), se devolverá como ruta la parte de \n  información `posts/php`.\n\nY cuando las se usen las reglas para crear URLs:\n\n- `Url::to(['post/index'])` genera `/index.php/posts` usando la primera regla;\n- `Url::to(['post/index', 'year' => 2014, 'category' => 'php'])` genera `/index.php/posts/2014/php` usando la tercera \n  regla;\n- `Url::to(['post/view', 'id' => 100])` genera `/index.php/post/100` usando la segunda regla;\n- `Url::to(['post/view', 'id' => 100, 'source' => 'ad'])` genera `/index.php/post/100?source=ad` usando la segunda \n   regla. Debido a que el parámetro `source` no se especifica en la regla, se añade como un parámetro de consulta en \n   la URL generada.\n- `Url::to(['post/index', 'category' => 'php'])` genera `/index.php/post/index?category=php` no usa ninguna de las \n   reglas. Hay que tener en cuenta que si no se aplica ninguna de las reglas, la URL se genera simplemente añadiendo \n   la parte de información de la ruta y todos los parámetros como parte de la consulta.\n\n\n### Parametrización de Rutas <span id=\"parameterizing-routes\"></span>\n\nSe pueden incrustar nombres de parámetros en la ruta de una regla de URL. Esto permite a la regla de URL poder ser \nusada para que coincida con varias rutas. Por ejemplo, la siguiente regla incrusta los parámetros `controller` y \n`action` en las rutas.\n\n```php\n[\n    '<controller:(post|comment)>/<id:\\d+>/<action:(create|update|delete)>' => '<controller>/<action>',\n    '<controller:(post|comment)>/<id:\\d+>' => '<controller>/view',\n    '<controller:(post|comment)>s' => '<controller>/index',\n]\n```\n\nPara convertir una URL `index.php/comment/100/create`, se aplicará la primera regla, que establece el parámetro \n`controller` a `comment` y el parámetro `action` a `create`. Por lo tanto la ruta `<controller>/<action>` se resuelve \ncomo `comment/create`.\n\nDel mismo modo, para crear una URL para una ruta `comment/index`, se aplicará la tercera regla, que crea una URL \n`/index.php/comments`.\n\n> Info: Mediante la parametrización de rutas es posible reducir el numero de reglas de URL e incrementar \n  significativamente el rendimiento del [[yii\\web\\UrlManager|URL manager]].\n\nDe forma predeterminada, todos los parámetros declarados en una regla son requeridos. Si una URL solicitada no \ncontiene un parámetro en particular, o si se esta creando una URL sin un parámetro en particular, la regla no se \naplicará. Para establecer algunos parámetros como opcionales, se puede configurar la propiedad de \n[[yii\\web\\UrlRule::defaults|defaults]] de una regla. Los parámetros listados en esta propiedad son opcionales y se \nusarán los parámetros especificados cuando estos no se proporcionen.\n\nEn la siguiente declaración de reglas, los parámetros `page` y `tag` son opcionales y cuando no se proporcionen, se \nusarán los valores 1 y cadena vacía respectivamente.\n\n```php\n[\n    // ... otras reglas ...\n    [\n        'pattern' => 'posts/<page:\\d+>/<tag>',\n        'route' => 'post/index',\n        'defaults' => ['page' => 1, 'tag' => ''],\n    ],\n]\n```\n\nLa regla anterior puede usarse para convertir o crear cualquiera de las siguientes URLs:\n\n* `/index.php/posts`: `page` es 1, `tag` es ''.\n* `/index.php/posts/2`: `page` es 2, `tag` es ''.\n* `/index.php/posts/2/news`: `page` es 2, `tag` es `'news'`.\n* `/index.php/posts/news`: `page` es 1, `tag` es `'news'`.\n\nSin usar ningún parámetro opcional, se tendrían que crear 4 reglas para lograr el mismo resultado.\n\n\n### Reglas con Nombres de Servidor <span id=\"rules-with-server-names\"></span>\n\nEs posible incluir nombres de servidores Web en los parámetros de las URLs. Esto es practico principalmente cuando una \naplicación debe tener distintos comportamientos paro diferentes nombres de servidores Web. Por ejemplo, las siguientes \nreglas convertirán la URL `https://admin.example.com/login` en la ruta `admin/user/login` y \n`https://www.example.com/login` en `site/login`.\n\n```php\n[\n    'https://admin.example.com/login' => 'admin/user/login',\n    'https://www.example.com/login' => 'site/login',\n]\n```\n\nTambién se pueden incrustar parámetros en los nombres de servidor para extraer información dinámica de ellas. Por \nejemplo, la siguiente regla convertirá la URL `https://en.example.com/posts` en la ruta `post/index` y el parámetro \n`language=en`.\n\n```php\n[\n    'http://<language:\\w+>.example.com/posts' => 'post/index',\n]\n```\n\n> Note: Las reglas con nombres de servidor NO deben incluir el subdirectorio del script de entrada (entry script) en \n  sus patrones. Por ejemplo, is la aplicación se encuentra en `https://www.example.com/sandbox/blog`, entonces se debe \n  usar el patrón `https://www.example.com/posts` en lugar de `https://www.example.com/sandbox/blog/posts`. Esto \n  permitirá que la aplicación se pueda desarrollar en cualquier directorio sin la necesidad de cambiar el código de la \n  aplicación.\n\n### Sufijos de URL <span id=\"url-suffixes\"></span>\n\nSe puede querer añadir sufijos a las URLs para varios propósitos. Por ejemplo, se puede añadir `.html`a las URLs para \nque parezcan URLs para paginas HTML estáticas; también se puede querer añadir `.json` a las URLs para indicar el tipo \nde contenido que se espera encontrar en una respuesta (response). Se puede lograr este objetivo configurando la \npropiedad [[yii\\web\\UrlManager::suffix]] como en el siguiente ejemplo de configuración de aplicación:\n\n```php\n[\n    'components' => [\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            'showScriptName' => false,\n            'enableStrictParsing' => true,\n            'suffix' => '.html',\n            'rules' => [\n                // ...\n            ],\n        ],\n    ],\n]\n```\n\nLa configuración anterior permitirá al [[yii\\web\\UrlManager|URL manager]] reconocer las URLs solicitadas y a su vez \ncrear URLs con el sufijo `.html`.\n\n> Tip: Se puede establecer `/` como el prefijo de URL para que las URLs finalicen con una barra.\n\n> Note: Cuando se configura un sufijo de URL, si una URL solicitada no tiene el sufijo, se considerará como una URL \n  desconocida. Esta es una practica recomendada para SEO (optimización en motores de búsqueda).\n\nA veces, se pueden querer usar sufijos diferentes para URLs diferentes. Esto se puede conseguir configurando la \npropiedad [[yii\\web\\UrlRule::suffix|suffix]] de una regla de URL individual. Cuando una regla de URL tiene la \npropiedad establecida, anulará el sufijo estableciendo a nivel de [[yii\\web\\UrlManager|URL manager]]. Por ejemplo, la \nsiguiente configuración contiene una regla de URL personalizada que usa el sufijo `.json` en lugar del sufijo global \n`.html`.\n\n```php\n[\n    'components' => [\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            'showScriptName' => false,\n            'enableStrictParsing' => true,\n            'suffix' => '.html',\n            'rules' => [\n                // ...\n                [\n                    'pattern' => 'posts',\n                    'route' => 'post/index',\n                    'suffix' => '.json',\n                ],\n            ],\n        ],\n    ],\n]\n```\n\n\n### Métodos HTTP <span id=\"http-methods\"></span>\n\nCuando se implementan APIs RESTful, normalmente se necesita que ciertas URLs se conviertan en otras de acuerdo con el \nmétodo HTTP que se esté usando. Esto se puede hacer fácilmente prefijando los métodos HTTP soportados como los \npatrones de las reglas. Si una regla soporta múltiples métodos HTTP, hay que separar los nombres de los métodos con \ncomas. Por ejemplo, la siguiente regla usa el mismo patrón `post/<id:\\d+>` para dar soporte a diferentes métodos HTTP. \nUna petición para `PUT post/100` se convertirá en `post/create`, mientras que una petición `GET post/100` se \nconvertirá en `post/view`.\n\n```php\n[\n    'PUT,POST post/<id:\\d+>' => 'post/create',\n    'DELETE post/<id:\\d+>' => 'post/delete',\n    'post/<id:\\d+>' => 'post/view',\n]\n```\n\n> Note: Si una regla de URL contiene algún método HTTP en su patrón, la regla solo se usará para aplicar conversiones. \n  Se omitirá cuando se llame a [[yii\\web\\UrlManager|URL manager]] para crear URLs.\n\n> Tip: Para simplificar el enrutamiento en APIs RESTful, Yii proporciona una clase de reglas de URL \n  [[yii\\rest\\UrlRule]] especial que es bastante eficiente y soporta ciertas características como pluralización de IDs \n  de controladores. Para conocer más detalles, se puede visitar la sección [Enrutamiento](rest-routing.md) acerca de \n  el desarrollo de APIs RESTful.\n\n\n### Personalización de Reglas <span id=\"customizing-rules\"></span>\n\nEn los anteriores ejemplos, las reglas de URL se han declarado principalmente en términos de pares de patrón-ruta. \nEste es un método de acceso directo que se usa a menudo. En algunos escenarios, se puede querer personalizar la regla \nde URL configurando sus otras propiedades, tales como [[yii\\web\\UrlRule::suffix]]. Esto se puede hacer usando una \narray completo de configuración para especificar una regla. El siguiente ejemplo se ha extraído de la subsección \n[Sufijos de URL](#url-suffixes).\n\n```php\n[\n    // ... otras reglas de URL ...\n    \n    [\n        'pattern' => 'posts',\n        'route' => 'post/index',\n        'suffix' => '.json',\n    ],\n]\n```\n\n> Info: De forma predeterminada si no se especifica una opción `class` para una configuración de regla, se \n  usará la clase predeterminada [[yii\\web\\UrlRule]].\n\n\n### Adición de Reglas Dinámicamente <span id=\"adding-rules\"></span>\n\nLas reglas de URL se pueden añadir dinámicamente en el [[yii\\web\\UrlManager|URL manager]]. A menudo se necesita por \n[módulos](structure-modules.md) redistribubles que se encargan de gestionar sus propias reglas de URL. Para que las \nreglas añadidas dinámicamente tenga efecto durante el proceso de enrutamiento, se deben añadir durante la etapa \n[bootstrapping](runtime-bootstrapping.md). Para los módulos, esto significa que deben implementar \n[[yii\\base\\BootstrapInterface]] y añadir las reglas en el método \n[[yii\\base\\BootstrapInterface::bootstrap()|bootstrap()]] como en el siguiente ejemplo:\n\n```php\npublic function bootstrap($app)\n{\n    $app->getUrlManager()->addRules([\n        // declaraciones de reglas aquí\n    ], false);\n}\n```\n\nHay que tener en cuenta se deben añadir estos módulos en [[yii\\web\\Application::bootstrap]] para que puedan participar \nen el proceso de [bootstrapping](runtime-bootstrapping.md)\n\n### Creación de Clases de Reglas <span id=\"creating-rules\"></span>\n\nA pesar del hecho de que de forma predeterminada la clase [[yii\\web\\UrlRule]] lo suficientemente flexible para la \nmayoría de proyectos, hay situaciones en las que se tiene que crear una clase de reglas propia. Por ejemplo, en un \nsitio Web de un concesionario de coches, se puede querer dar soporte a las URL con el siguiente formato \n`/Manufacturer/Model`, donde tanto `Manufacturer` como `Model` tengan que coincidir con algún dato almacenado una \ntabla de la base de datos. De forma predeterminada, la clase regla no puede gestionar estas reglas ya que se base en \npatrones estáticos declarados.\n\nPodemos crear la siguiente clase de reglas de URL para solucionar el problema.\n\n```php\nnamespace app\\components;\n\nuse yii\\web\\UrlRuleInterface;\nuse yii\\base\\BaseObject;\n\nclass CarUrlRule extends BaseObject implements UrlRuleInterface\n{\n\n    public function createUrl($manager, $route, $params)\n    {\n        if ($route === 'car/index') {\n            if (isset($params['manufacturer'], $params['model'])) {\n                return $params['manufacturer'] . '/' . $params['model'];\n            } elseif (isset($params['manufacturer'])) {\n                return $params['manufacturer'];\n            }\n        }\n        return false;  // no se aplica esta regla\n    }\n\n    public function parseRequest($manager, $request)\n    {\n        $pathInfo = $request->getPathInfo();\n        if (preg_match('%^(\\w+)(/(\\w+))?$%', $pathInfo, $matches)) {\n            // comprueba $matches[1] y $matches[3] para ver\n            // si coincide con un *manufacturer* y un *model* en la base de datos\n            // Si coinciden, establece $params['manufacturer'] y/o $params['model']\n            // y devuelve ['car/index', $params]\n        }\n        return false;  // no se aplica la regla\n    }\n}\n```\n\nY usa la nueva clase de regla en la configuración de [[yii\\web\\UrlManager::rules]]:\n\n```php\n[\n    // ... otras reglas ...\n    \n    [\n        'class' => 'app\\components\\CarUrlRule', \n        // ... configura otras propiedades ...\n    ],\n]\n```\n\n## Consideración del Rendimiento <span id=\"performance-consideration\"></span>\n\nCuando se desarrolla una aplicación Web compleja, es importante optimizar las reglas de URL para que tarden el mínimo \ntiempo posible en convertir las peticiones y crear URLs.\n\nUsando rutas parametrizadas se puede reducir el numero de reglas de URL que a su vez significa una mejor en el \nrendimiento.\n\nCuando se convierten o crean URLs, el [[yii\\web\\UrlManager|URL manager]] examina las reglas de URL en el orden en que \nhan sido declaradas. Por lo tanto, se debe tener en cuenta el orden de las reglas de URL y anteponer las reglas más \nespecificas y/o las que se usen más a menudo.\n\nSi algunas URLs comparten el mismo prefijo en sus patrones o rutas, se puede considerar usar [[yii\\web\\GroupUrlRule]] \nya que puede ser más eficiente al ser examinado por [[yii\\web\\UrlManager|URL manager]] como un grupo. Este suele ser \nel caso cuando una aplicación se compone por módulos, y cada uno tiene su propio conjunto de reglas con un ID de \nmódulo para sus prefijos más comunes.\n"
  },
  {
    "path": "docs/guide-es/runtime-sessions-cookies.md",
    "content": "Sesiones (Sessions) y Cookies\n=============================\n\nLas sesiones y las cookies permiten la persistencia de datos a través de múltiples peticiones de usuario. En PHP plano, debes acceder a ellos a través de las variables globales `$_SESSION` y `$_COOKIE`, respectivamente. Yii encapsula las sesiones y las cookies como objetos y por lo tanto te permite acceder a ellos de manera orientada a objetos con estupendas mejoras adicionales.\n\n\n## Sesiones <span id=\"sessions\"></span>\n\nComo las [peticiones](runtime-requests.md) y las [respuestas](runtime-responses.md), puedes acceder a las sesiones vía el [componente de la aplicación](structure-application-components.md) `session`  el cual es una instancia de [[yii\\web\\Session]], por defecto.\n\n\n### Abriendo y cerrando sesiones <span id=\"opening-closing-sessions\"></span>\n\nPara abrir y cerrar una sesión, puedes hacer lo siguiente:\n\n```php\n$session = Yii::$app->session;\n\n// comprueba si una sesión está ya abierta\nif ($session->isActive) ...\n\n// abre una sesión\n$session->open();\n\n// cierra una sesión\n$session->close();\n\n// destruye todos los datos registrados por la sesión.\n$session->destroy();\n```\n\nPuedes llamar a [[yii\\web\\Session::open()|open()]] y [[yii\\web\\Session::close()|close()]] múltiples veces sin causar errores. Esto ocurre porque internamente los métodos verificarán primero si la sesión está ya abierta.\n\n\n### Accediendo a los datos de sesión <span id=\"access-session-data\"></span>\n\nPara acceder a los datos almacenados en sesión, puedes hacer lo siguiente:\n\n```php\n$session = Yii::$app->session;\n\n// devuelve una variable de sesión. Los siguientes usos son equivalentes:\n$language = $session->get('language');\n$language = $session['language'];\n$language = isset($_SESSION['language']) ? $_SESSION['language'] : null;\n\n// inicializa una variable de sesión. Los siguientes usos son equivalentes:\n$session->set('language', 'en-US');\n$session['language'] = 'en-US';\n$_SESSION['language'] = 'en-US';\n\n// remueve la variable de sesión. Los siguientes usos son equivalentes:\n$session->remove('language');\nunset($session['language']);\nunset($_SESSION['language']);\n\n// comprueba si una variable de sesión existe. Los siguientes usos son equivalentes:\nif ($session->has('language')) ...\nif (isset($session['language'])) ...\nif (isset($_SESSION['language'])) ...\n\n// recorre todas las variables de sesión. Los siguientes usos son equivalentes:\nforeach ($session as $name => $value) ...\nforeach ($_SESSION as $name => $value) ...\n```\n\n> Info: Cuando accedas a los datos de sesión a través del componente `session`, una sesión será automáticamente abierta si no lo estaba antes. Esto es diferente accediendo a los datos de sesión a través de `$_SESSION`, el cual requiere llamar explícitamente a `session_start()`.\n\nCuando trabajas con datos de sesiones que son arrays, el componte `session` tiene una limitación que te previene directamente de modificar un elemento del array. Por ejemplo,\n\n```php\n$session = Yii::$app->session;\n\n// el siguiente código no funciona\n$session['captcha']['number'] = 5;\n$session['captcha']['lifetime'] = 3600;\n\n// el siguiente código funciona:\n$session['captcha'] = [\n    'number' => 5,\n    'lifetime' => 3600,\n];\n\n// el siguiente código también funciona:\necho $session['captcha']['lifetime'];\n```\n\nPuedes usar las siguientes soluciones para arreglar este problema:\n\n```php\n$session = Yii::$app->session;\n\n// directamente usando $_SESSION (asegura te de que Yii::$app->session->open() ha sido llamado)\n$_SESSION['captcha']['number'] = 5;\n$_SESSION['captcha']['lifetime'] = 3600;\n\n// devuelve el valor del array, lo modifica y a continuación lo guarda\n$captcha = $session['captcha'];\n$captcha['number'] = 5;\n$captcha['lifetime'] = 3600;\n$session['captcha'] = $captcha;\n\n// usa un ArrayObject en vez de un array\n$session['captcha'] = new \\ArrayObject;\n...\n$session['captcha']['number'] = 5;\n$session['captcha']['lifetime'] = 3600;\n\n// almacena los datos en un array con un prefijo común para las claves\n$session['captcha.number'] = 5;\n$session['captcha.lifetime'] = 3600;\n```\n\nPara un mejor rendimiento y legibilidad del código, recomendamos la última solución. Es decir, en vez de almacenar un array como una única variable de sesión, almacena cada elemento del array como una variable de sesión que comparta el mismo prefijo clave con otros elementos del array.\n\n\n### Personalizar el almacenamiento de sesión <span id=\"custom-session-storage\"></span>\n\nPor defecto la clase [[yii\\web\\Session]] almacena los datos de sesión como ficheros en el servidor. Yii también provee de las siguientes clases de sesión que implementan diferentes almacenamientos de sesión:\n\n* [[yii\\web\\DbSession]]: almacena los datos de sesión en una tabla en la base de datos.\n* [[yii\\web\\CacheSession]]: almacena los datos de sesión en una caché con la ayuda de la configuración del [componente caché](caching-data.md#cache-components).\n* [[yii\\redis\\Session]]: almacena los datos de sesión usando [redis](https://redis.io/) como medio de almacenamiento.\n* [[yii\\mongodb\\Session]]: almacena los datos de sesión en [MongoDB](https://www.mongodb.com/).\n\nTodas estas clases de sesión soportan los mismos métodos de la API. Como consecuencia, puedes cambiar el uso de diferentes almacenamientos de sesión sin la necesidad de modificar el código de tu aplicación que usa sesiones.\n\n> Note: si quieres acceder a los datos de sesión vía `$_SESSION` mientras estás usando un almacenamiento de sesión personalizado, debes asegurar te que la sesión está ya empezada por [[yii\\web\\Session::open()]]. Esto ocurre porque los manipuladores de almacenamiento de sesión personalizado son registrados sin este método.\n\nPara aprender como configurar y usar estas clases de componentes, por favor consulte la documentación de la API. Abajo está un ejemplo que muestra como configurar [[yii\\web\\DbSession]] en la configuración de la aplicación para usar una tabla en la base de datos como almacenamiento de sesión:\n\n```php\nreturn [\n    'components' => [\n        'session' => [\n            'class' => 'yii\\web\\DbSession',\n            // 'db' => 'mydb',  // el identificador del componente de aplicación DB connection. Por defecto'db'.\n            // 'sessionTable' => 'my_session', // nombre de la tabla de sesión. Por defecto 'session'.\n        ],\n    ],\n];\n```\n\nTambién es necesario crear la siguiente tabla de la base de datos para almacenar los datos de sesión:\n\n```sql\nCREATE TABLE session\n(\n    id CHAR(40) NOT NULL PRIMARY KEY,\n    expire INTEGER,\n    data BLOB\n)\n```\n\ndonde 'BLOB' se refiere al BLOB-type de tu DBMS preferida. Abajo está el tipo BLOB que puedes usar para algunos DBMS populares:\n\n- MySQL: LONGBLOB\n- PostgreSQL: BYTEA\n- MSSQL: BLOB\n\n> Note: De acuerdo con la configuración de php.ini `session.hash_function`, puedes necesitar ajustar el tamaño de la columna `id`. Por ejemplo, si `session.hash_function=sha256`, deberías usar el tamaño 64 en vez de 40.\n\n\n### Flash Data <span id=\"flash-data\"></span>\n\nFlash data es una clase especial de datos de sesión que, una vez se inicialice en la primera petición, estará sólo disponible durante la siguiente petición y automáticamente se borrará después. Flash data es comúnmente usado para implementar mensajes que deberían ser mostrados una vez a usuarios finales, tal como mostrar un mensaje de confirmación después de que un usuario envíe un formulario con éxito.\n\nPuedes inicializar y acceder a flash data a través del componente de aplicación `session`. Por ejemplo,\n\n```php\n$session = Yii::$app->session;\n\n// Petición #1\n// inicializa el mensaje flash nombrado como \"postDeleted\"\n$session->setFlash('postDeleted', 'You have successfully deleted your post.');\n\n// Petición #2\n// muestra el mensaje flash nombrado \"postDeleted\"\necho $session->getFlash('postDeleted');\n\n// Petición #3\n// $result será `false` ya que el mensaje flash ha sido borrado automáticamente\n$result = $session->hasFlash('postDeleted');\n```\n\nAl igual que los datos de sesión regulares, puede almacenar datos arbitrarios como flash data.\n\nCuando llamas a [[yii\\web\\Session::setFlash()]], sobrescribirá cualquier Flash data que tenga el mismo nombre.\nPara añadir un nuevo flash data a el/los existes con el mismo nombre, puedes llamar a [[yii\\web\\Session::addFlash()]].\nPor ejemplo:\n\n```php\n$session = Yii::$app->session;\n\n// Petición #1\n// añade un pequeño mensaje flash bajo el nombre de \"alerts\"\n$session->addFlash('alerts', 'You have successfully deleted your post.');\n$session->addFlash('alerts', 'You have successfully added a new friend.');\n$session->addFlash('alerts', 'You are promoted.');\n\n// Petición #2\n// $alerts es un array de mensajes flash bajo el nombre de \"alerts\"\n$alerts = $session->getFlash('alerts');\n```\n\n> Note: Intenta no usar a la vez [[yii\\web\\Session::setFlash()]] con [[yii\\web\\Session::addFlash()]] para flash data\n  del mismo nombre. Esto ocurre porque el último método elimina el flash data dentro del array así que puedes añadir un nuevo flash data con el mismo nombre. Como resultado, cuando llamas a [[yii\\web\\Session::getFlash()]], puedes encontrarte algunas veces que te está devolviendo un array mientras que otras veces te está devolviendo un string, esto depende del orden que invoques a estos dos métodos.\n\n\n## Cookies <span id=\"cookies\"></span>\n\nYii representa cada cookie como un objeto de [[yii\\web\\Cookie]]. Tanto [[yii\\web\\Request]] como [[yii\\web\\Response]]\nmantienen una colección de cookies vía la propiedad de llamada `cookies`. La colección de cookie en la antigua representación son enviadas en una petición, mientras la colección de cookie en esta última representa las cookies que van a ser enviadas al usuario.\n\n\n### Leyendo Cookies <span id=\"reading-cookies\"></span>\n\nPuedes recuperar las cookies en la petición actual usando el siguiente código:\n\n```php\n// devuelve la colección de cookie (yii\\web\\CookieCollection) del componente \"request\"\n$cookies = Yii::$app->request->cookies;\n\n// devuelve el valor \"language\" de la cookie. Si la cookie no existe, retorna \"en\" como valor por defecto.\n$language = $cookies->getValue('language', 'en');\n\n// una manera alternativa de devolver el valor \"language\" de la cookie\nif (($cookie = $cookies->get('language')) !== null) {\n    $language = $cookie->value;\n}\n\n// puedes también usar $cookies como un array\nif (isset($cookies['language'])) {\n    $language = $cookies['language']->value;\n}\n\n// comprueba si hay una cookie con el valor \"language\"\nif ($cookies->has('language')) ...\nif (isset($cookies['language'])) ...\n```\n\n\n### Enviando Cookies <span id=\"sending-cookies\"></span>\n\nPuedes enviar cookies a usuarios finales usando el siguiente código:\n\n```php\n// devuelve la colección de cookie (yii\\web\\CookieCollection) del componente \"response\"\n$cookies = Yii::$app->response->cookies;\n\n// añade una nueva cookie a la respuesta que se enviará\n$cookies->add(new \\yii\\web\\Cookie([\n    'name' => 'language',\n    'value' => 'zh-CN',\n]));\n\n// remueve una cookie\n$cookies->remove('language');\n// equivalente a lo siguiente\nunset($cookies['language']);\n```\n\nAdemás de [[yii\\web\\Cookie::name|name]], [[yii\\web\\Cookie::value|value]] las propiedades que se muestran en los anteriores ejemplos, la clase [[yii\\web\\Cookie]] también define otras propiedades para representar toda la información posible de las cookies, tal como [[yii\\web\\Cookie::domain|domain]], [[yii\\web\\Cookie::expire|expire]]. Puedes configurar estas propiedades según sea necesario para preparar una cookie y luego añadirlo a la colección de cookies de la respuesta.\n\n> Note: Para mayor seguridad, el valor por defecto de [[yii\\web\\Cookie::httpOnly]] es `true`. Esto ayuda a mitigar el riesgo del acceso a la cookie protegida por script desde el lado del cliente (si el navegador lo soporta). Puedes leer el [httpOnly wiki article](https://owasp.org/www-community/HttpOnly) para más detalles.\n\n\n### Validación de la Cookie <span id=\"cookie-validation\"></span>\n\nCuando estás leyendo y enviando cookies a través de los componentes `request` y `response` como mostramos en las dos últimas subsecciones, cuentas con el añadido de seguridad de la validación de cookies el cual protege las cookies de ser modificadas en el lado del cliente. Esto se consigue con la firma de cada cookie con una cadena hash, el cual permite a la aplicación saber si una cookie ha sido modificada en el lado del cliente o no. Si es así, la cookie no será accesible a través de [[yii\\web\\Request::cookies|cookie collection]] del componente `request`.\n\n> Info: Si falla la validación de una cookie, aún puedes acceder a la misma a través de `$_COOKIE`. Esto sucede porque librerías de terceros pueden manipular de forma propia las cookies, lo cual no implica la validación de las mismas.\n\nLa validación de cookies es habilitada por defecto. Puedes desactivar lo ajustando la propiedad [[yii\\web\\Request::enableCookieValidation]] a `false`, aunque se recomienda encarecidamente que no lo haga.\n\n> Note: Las cookies que son directamente leídas/enviadas vía `$_COOKIE` y `setcookie()` no serán validadas.\n\nCuando estás usando la validación de cookie, puedes especificar una [[yii\\web\\Request::cookieValidationKey]] el cual se usará para generar los strings hash mencionados anteriormente. Puedes hacerlo mediante la configuración del componente `request` en la configuración de la aplicación:\n\n```php\nreturn [\n    'components' => [\n        'request' => [\n            'cookieValidationKey' => 'fill in a secret key here',\n        ],\n    ],\n];\n```\n\n> Info: [[yii\\web\\Request::cookieValidationKey|cookieValidationKey]] es crítico para la seguridad de tu aplicación.\n  Sólo debería ser conocido por personas de confianza. No lo guardes en sistemas de control de versiones.\n"
  },
  {
    "path": "docs/guide-es/security-authentication.md",
    "content": "Authentication\n==============\n\nLa autenticación es el proceso de verificar la identidad de un usuario. Usualmente se usa un identificador (ej. un `username` o una dirección de correo electrónico) y una token secreto (ej. una contraseña o un token de acceso) para juzgar si el usuario es quien dice ser. La autenticación es la base de la función de inicio de sesión.\n\nYii proporciona un marco de autenticación que conecta varios componentes para soportar el inicio de sesión. Para utilizar este marco, usted necesita principalmente hacer el siguiente trabajo:\n\n* Configurar el componente de la aplicación [[yii\\web\\User|user]];\n* Crear una clase que implemente la interfaz [[yii\\web\\IdentityInterface]].\n\n## Configurando [[yii\\web\\User]] <span id=\"configuring-user\"></span>\n\nEl componente [[yii\\web\\User|user]] gestiona el estado de autenticación del usuario. Requiere que especifiques una [[yii\\web\\User::identityClass|clase de identidad]] la cual contiene la lógica de autenticación. En la siguiente configuración de la aplicación, la [[yii\\web\\User::identityClass|clase identity]] para [[yii\\web\\User|user]] está configurada para ser `app\\models\\User` cuya implementación se explica en la siguiente subsección:\n\n```php\nreturn [\n    'components' => [\n        'user' => [\n            'identityClass' => 'app\\models\\User',\n        ],\n    ],\n];\n```\n\n## Implementando [[yii\\web\\IdentityInterface]] <span id=\"implementing-identity\"></span>\nLa [[yii\\web\\User::identityClass|clase identity]] debe implementar la [[yii\\web\\IdentityInterface]] que contiene los siguientes métodos:\n* [[yii\\web\\IdentityInterface::findIdentity()|findIdentity()]]: busca una instancia de la clase identidad usando el ID de usuario especificado. Este método se utiliza cuando se necesita mantener el estado de inicio de sesión (login) a través de la sesión.\n\n* [[yii\\web\\IdentityInterface::findIdentityByAccessToken()|findIdentityByAccessToken()]]: busca una instancia de la clase de identidad usando el token de acceso especificado. Este método se utiliza cuando se necesita autenticar a un usuario mediante un único token secreto (ej. en una aplicación RESTful sin estado).\n* [[yii\\web\\IdentityInterface::getId()|getId()]]: devuelve el ID del usuario representado por esta instancia de identidad.\n* [[yii\\web\\IdentityInterface::getAuthKey()|getAuthKey()]]: devuelve una clave utilizada para validar la sesión y el auto-login en caso de que esté habilitado.\n* [[yii\\web\\IdentityInterface::validateAuthKey()|validateAuthKey()]]: implementa la lógica para verificar la clave de autenticación.\n\nSi no se necesita un método en particular, se podría implementar con un cuerpo vacío, Por ejemplo, Si un método en particular no es necesario, puedes implementarlo con un cuerpo vacío. Por ejemplo, si tu aplicación es una aplicación RESTful pura sin estado, sólo necesitarás implementar [[yii\\web\\IdentityInterface::findIdentityByAccessToken()|findIdentityByAccessToken()]] y [[yii\\web\\IdentityInterface::getId()|getId()]] dejando el resto de métodos con un cuerpo vacío. O si tu aplicación utiliza autenticación sólo de sesión, necesitarías implementar todos los métodos excepto findIdentityByAccessToken().\n\nEn el siguiente ejemplo, una clase [[yii\\web\\User::identityClass|identity]] es implementada como una clase [Active Record](db-active-record.md) asociada con la tabla de base de datos `user`.\n\n```php\n<?php\n\nuse yii\\db\\ActiveRecord;\nuse yii\\web\\IdentityInterface;\n\nclass User extends ActiveRecord implements IdentityInterface\n{\n    public static function tableName()\n    {\n        return 'user';\n    }\n\n    /**\n     * Buscar una identidad por el ID dado.\n     *\n     * @param string|int $id ID que debe buscarse\n     * @return IdentityInterface|null objeto de identidad que coincide con el ID dado.\n     */\n    public static function findIdentity($id)\n    {\n        return static::findOne($id);\n    }\n\n    /**\n     * Buscar una identidad por el token dado..\n     *\n     * @param string $token token que debe buscarse\n     * @return IdentityInterface|null objeto de identidad que coincide con el token dado.\n     */\n    public static function findIdentityByAccessToken($token, $type = null)\n    {\n        return static::findOne(['access_token' => $token]);\n    }\n\n    /**\n     * @return int|string ID del usuario actual\n     */\n    public function getId()\n    {\n        return $this->id;\n    }\n\n    /**\n     * @return string|null llave de autenticación del usuario actual\n     */\n    public function getAuthKey()\n    {\n        return $this->auth_key;\n    }\n\n    /**\n     * @param string $authKey\n     * @return bool|null si la llave de autenticación es válida para el usuario actual\n     */\n    public function validateAuthKey($authKey)\n    {\n        return $this->getAuthKey() === $authKey;\n    }\n}\n```\n\nPuede utilizar el siguiente código para generar una clave de autenticación para cada usuario y almacenarla en la tabla `user`:\n\n```php\nclass User extends ActiveRecord implements IdentityInterface\n{\n    ......\n\n    public function beforeSave($insert)\n    {\n        if (parent::beforeSave($insert)) {\n            if ($this->isNewRecord) {\n                $this->auth_key = \\Yii::$app->security->generateRandomString();\n            }\n            return true;\n        }\n        return false;\n    }\n}\n```\n\n> Nota: No confundas la clase de identidad `User` con [[yii\\web\\User]]. La primera es la clase que implementa la lógica de autenticación. Suele implementarse como una clase [Active Record](db-active-record.md) asociada a algún almacenamiento persistente para guardar la información de las credenciales del usuario. Esta última es una clase de componente de aplicación responsable de gestionar el estado de autenticación del usuario.\n\n## Usando [[yii\\web\\User]] <span id=\"using-user\"></span>\nPrincipalmente se usa [[yii\\web\\User]] en términos del componente de aplicación `user`.\n\nPuede detectar la identidad del usuario actual usando la expresión `Yii::$app->user->identity`. Devuelve una instancia de la clase [[yii\\web\\User::identityClass|identity]] que representa al usuario actualmente conectado, o `null` si el usuario actual no está autenticado (es decir, es un invitado). El siguiente código muestra como recuperar otra información relacionada con la autenticación desde [[yii\\web\\User]]:\n\n```php\n// El usuario actual identificado. `null` si el usuario no esta autenticado.\n$identity = Yii::$app->user->identity;\n\n// El ID del usuario actual. `null` si el usuario no esta autenticado.\n$id = Yii::$app->user->id;\n\n// si el usuario actual es un invitado (No autenticado)\n$isGuest = Yii::$app->user->isGuest;\n```\n\nPara acceder a un usuario, puede utilizar el siguiente código:\n\n```php\n// encontrar una identidad de usuario con el nombre de usuario especificado.\n// tenga en cuenta que es posible que desee comprobar la contraseña si es necesario\n$identity = User::findOne(['username' => $username]);\n\n// inicia la sesión del usuario.\nYii::$app->user->login($identity);\n```\n\nEl método [[yii\\web\\User::login()]] establece la identidad del usuario actual a [[yii\\web\\User]]. Si la sesión es [[yii\\web\\User::enableSession|habilitada]], mantendrá la identidad en la sesión para que el estado de autenticación del usuario se mantenga durante toda la sesión. Si el login basado en cookies (es decir, inicio de sesión \"recordarme\") está [[yii\\web\\User::enableAutoLogin|habilitado]], también guardará la identidad en una cookie para que el estado de autenticación del usuario pueda ser recuperado desde la cookie mientras la cookie permanezca válida.\n\nPara habilitar el login basado en cookies, necesita configurar [[yii\\web\\User::enableAutoLogin]] como `true` en la configuración de la aplicación. También necesita proporcionar un parámetro de tiempo de duración cuando llame al método [[yii\\web\\User::login()]].\n\nPara cerrar la sesión de un usuario, basta con llamar a:\n\n```php\nYii::$app->user->logout();\n```\n\nTenga en cuenta que cerrar la sesión de un usuario sólo tiene sentido cuando la sesión está activada. El método limpiará el estado de autenticación del usuario tanto de la memoria como de la sesión. Y por defecto, también destruirá *todos* los datos de sesión del usuario. Si desea mantener los datos de sesión, debe llamar a `Yii::$app->user->logout(false)`, en su lugar.\n\n## Eventos de Autenticación <span id=\"auth-events\"></span>\nLa clase [[yii\\web\\User]] genera algunos eventos durante los procesos de inicio y cierre de sesión.\n* [[yii\\web\\User::EVENT_BEFORE_LOGIN|EVENT_BEFORE_LOGIN]]: levantado al comienzo de [[yii\\web\\User::login()]]. Si el manejador del evento establece la propiedad [[yii\\web\\UserEvent::isValid|isValid]] del objeto evento a `false`, el proceso de inicio de sesión será cancelado.\n* [[yii\\web\\User::EVENT_AFTER_LOGIN|EVENT_AFTER_LOGIN]]: se produce después de un inicio de sesión exitoso.\n* [[yii\\web\\User::EVENT_BEFORE_LOGOUT|EVENT_BEFORE_LOGOUT]]: levantado al comienzo de [[yii\\web\\User::logout()]]. Si el manejador del evento establece la propiedad [[yii\\web\\UserEvent::isValid|isValid]] del objeto evento a `false`, el proceso de cierre de sesión será cancelado.\n* [[yii\\web\\User::EVENT_AFTER_LOGOUT|EVENT_AFTER_LOGOUT]]: se produce después de un cierre de sesión exitoso.\nUsted puede responder a estos eventos para implementar características como auditoria de inicio de sesión, estadísticas de usuarios en línea. Por ejemplo, en el manejador para [[yii\\web\\User::EVENT_AFTER_LOGIN|EVENT_AFTER_LOGIN]], puede registrar la hora de inicio de sesión y la dirección IP en la tabla `user`.\n"
  },
  {
    "path": "docs/guide-es/security-authorization.md",
    "content": "Autorización\n============\n\nAutorización esl el proceso de verificación de que un usuario tenga sugifientes permisos para realizar algo. Yii provee\ndos métodos de autorización: Filtro de Control de Acceso y Control Basado en Roles (ACF y RBAC por sus siglas en inglés).\n\n\n## Filtro de Control de Acceso <span id=\"access-control-filter\"></span>\n\nFiltro de Control de Acceso (ACF) es un único método de autorización implementado como [[yii\\filters\\AccessControl]], el cual\nes mejor utilizado por aplicaciones que sólo requieran un control de acceso simple. Como su nombre lo indica, ACF es\nun [filtro](structure-filters.md) de acción que puede ser utilizado en un controlador o en un módulo. Cuando un usuario solicita\nla ejecución de una acción, ACF comprobará una lista de [[yii\\filters\\AccessControl::rules|reglas de acceso]]\npara determinar si el usuario tiene permitido acceder a dicha acción.\n\nEl siguiente código muestra cómo utilizar ACF en el controlador `site`:\n\n```php\nuse yii\\web\\Controller;\nuse yii\\filters\\AccessControl;\n\nclass SiteController extends Controller\n{\n    public function behaviors()\n    {\n        return [\n            'access' => [\n                'class' => AccessControl::class,\n                'only' => ['login', 'logout', 'signup'],\n                'rules' => [\n                    [\n                        'allow' => true,\n                        'actions' => ['login', 'signup'],\n                        'roles' => ['?'],\n                    ],\n                    [\n                        'allow' => true,\n                        'actions' => ['logout'],\n                        'roles' => ['@'],\n                    ],\n                ],\n            ],\n        ];\n    }\n    // ...\n}\n```\n\nEn el código anterior, ACF es adjuntado al controlador `site` en forma de behavior (comportamiento). Esta es la forma típica de utilizar\nun filtro de acción. La opción `only` especifica que el ACF debe ser aplicado solamente a las acciones `login`, `logout` y `signup`.\nLas acciones restantes en el controlador `site` no están sujetas al control de acceso. La opción `rules` lista\nlas [[yii\\filters\\AccessRule|reglas de acceso]], y se lee como a continuación:\n\n- Permite a todos los usuarios invitados (sin autenticar) acceder a las acciones `login` y `signup`. La opción `roles`\n  contiene el signo de interrogación `?`, que es un código especial para representar a los \"invitados\".\n- Permite a los usuarios autenticados acceder a la acción `logout`. El signo `@` es otro código especial que representa\n  a los \"usuarios autenticados\".\n\nACF ejecuta la comprobación de autorización examinando las reglas de acceso una a una desde arriba hacia abajo hasta que encuentra\nuna regla que aplique al contexto de ejecución actual. El valor `allow` de la regla que coincida será entonces utilizado\npara juzgar si el usuario está autorizado o no. Si ninguna de las reglas coincide, significa que el usuario NO está autorizado,\ny el ACF detendrá la ejecución de la acción.\n\nCuando el ACF determina que un usuario no está autorizado a acceder a la acción actual, toma las siguientes medidas por defecto:\n\n* Si el usuario es un invitado, llamará a [[yii\\web\\User::loginRequired()]] para redireccionar el navegador a la pantalla de login.\n* Si el usuario está autenticado, lanzará una excepeción [[yii\\web\\ForbiddenHttpException]].\n\nPuedes personalizar este comportamiento configurando la propiedad [[yii\\filters\\AccessControl::denyCallback]] como a continuación:\n\n```php\n[\n    'class' => AccessControl::class,\n    ...\n    'denyCallback' => function ($rule, $action) {\n        throw new \\Exception('No tienes los suficientes permisos para acceder a esta página');\n    }\n]\n```\n\nLas [[yii\\filters\\AccessRule|Reglas de Acceso]] soportan varias opciones. Abajo hay un resumen de las mismas.\nTambién puedes extender de [[yii\\filters\\AccessRule]] para crear tus propias clases de reglas de acceso personalizadas.\n\n * [[yii\\filters\\AccessRule::allow|allow]]: especifica si la regla es de tipo \"allow\" (permitir) o \"deny\" (denegar).\n\n * [[yii\\filters\\AccessRule::actions|actions]]: especifica con qué acciones coinciden con esta regla. Esta debería ser\nun array de IDs de acciones. La comparación es sensible a mayúsculas. Si la opción está vacía o no definida,\nsignifica que la regla se aplica a todas las acciones.\n\n * [[yii\\filters\\AccessRule::controllers|controllers]]: especifica con qué controladores coincide\nesta regla. Esta debería ser un array de IDs de controladores. Cada ID de controlador es prefijado con el ID del módulo (si existe).\nLa comparación es sensible a mayúsculas. Si la opción está vacía o no definida, significa que la regla se aplica a todos los controladores.\n\n * [[yii\\filters\\AccessRule::roles|roles]]: especifica con qué roles de usuarios coincide esta regla.\n   Son reconocidos dos roles especiales, y son comprobados vía [[yii\\web\\User::isGuest]]:\n\n     - `?`: coincide con el usuario invitado (sin autenticar)\n     - `@`: coincide con el usuario autenticado\n\n   El utilizar otro nombre de rol invocará una llamada a [[yii\\web\\User::can()]], que requiere habilitar RBAC\n   (a ser descrito en la próxima subsección). Si la opción está vacía o no definida, significa que la regla se aplica a todos los roles.\n\n * [[yii\\filters\\AccessRule::ips|ips]]: especifica con qué [[yii\\web\\Request::userIP|dirección IP del cliente]] coincide esta regla.\nUna dirección IP puede contener el caracter especial `*` al final de manera que coincidan todas las IPs que comiencen igual.\nPor ejemplo, '192.168.*' coincide con las direcciones IP en el segmento '192.168.'. Si la opción está vacía o no definida,\nsignifica que la regla se aplica a todas las direcciones IP.\n\n * [[yii\\filters\\AccessRule::verbs|verbs]]: especifica con qué método de la solicitud (por ej. `GET`, `POST`) coincide esta regla.\nLa comparación no distingue minúsculas de mayúsculas.\n\n * [[yii\\filters\\AccessRule::matchCallback|matchCallback]]: especifica una función PHP invocable que debe ser llamada para determinar\nsi la regla debe ser aplicada.\n\n * [[yii\\filters\\AccessRule::denyCallback|denyCallback]]: especifica una función PHP invocable que debe ser llamada cuando esta regla\ndeniegue el acceso.\n\nDebajo hay un ejemplo que muestra cómo utilizar la opción `matchCallback`, que te permite escribir lógica de comprabación de acceso\narbitraria:\n\n```php\nuse yii\\filters\\AccessControl;\n\nclass SiteController extends Controller\n{\n    public function behaviors()\n    {\n        return [\n            'access' => [\n                'class' => AccessControl::class,\n                'only' => ['special-callback'],\n                'rules' => [\n                    [\n                        'actions' => ['special-callback'],\n                        'allow' => true,\n                        'matchCallback' => function ($rule, $action) {\n                            return date('d-m') === '31-10';\n                        }\n                    ],\n                ],\n            ],\n        ];\n    }\n\n    // Callback coincidente llamado! Esta página sólo puede ser accedida cada 31 de Octubre\n    public function actionSpecialCallback()\n    {\n        return $this->render('happy-halloween');\n    }\n}\n```\n\n\n## Control de Acceso Basado en Roles (RBAC) <span id=\"rbac\"></span>\n\nEl Control de Acceso Basado en Roles (RBAC) provee una simple pero poderosa manera centralizada de control de acceso. Por favos consulta\nla [Wikipedia](https://en.wikipedia.org/wiki/Role-based_access_control) para más detalles sobre comparar RBAC\ncon otros mecanismos de control de acceso más tradicionales.\n\nYii implementa una Jerarquía General RBAC, siguiendo el [modelo NIST RBAC](https://csrc.nist.gov/CSRC/media/Publications/conference-paper/1992/10/13/role-based-access-controls/documents/ferraiolo-kuhn-92.pdf).\nEsto provee la funcionalidad RBAC a través de [componente de la aplicación](structure-application-components.md) [[yii\\rbac\\ManagerInterface|authManager]].\n\nUtilizar RBAC envuelve dos cosas. La primera es construir los datos de autorización RBAC, y la segunda\nes utilizar esos datos de autorización para comprobar el acceso en los lugares donde se necesite.\n\nPara facilitar la próxima descripción, necesitamos primero instroducir algunos conceptos RBAC básicos.\n\n\n### Conceptos Básicos <span id=\"basic-concepts\"></span>\n\nUn rol representa una colección de *permisos* (por ej. crear posts, actualizar posts). Un rol puede ser asignado\na uno o varios usuarios. Para comprobar que un usuario cuenta con determinado permiso, podemos comprobar si el usuario tiene asignado\nun rol que cuente con dicho permiso.\n\nAsociado a cada rol o permiso, puede puede haber una *regla*. Una regla representa una porción de código que será\nejecutada durante la comprobación de acceso para determinar si el rol o permiso correspondiente aplica al usuario actual.\nPor ejemplo, el permiso \"actualizar post\" puede tener una regla que compruebe que el usuario actual es el autor del post.\nDurante la comprobación de acceso, si el usuario NO es el autor del post, se considerará que el/ella no cuenta con el permiso \"actualizar post\".\n\nTanto los roles como los permisos pueden ser organizados en una jerarquía. En particular, un rol puede consistir en otros roles o permisos;\ny un permiso puede consistir en otros permisos. Yii implementa una jerarquía de *orden parcial*, que incluye\nuna jerarquía de *árbol* especial. Mientras que un rol puede contener un permiso, esto no sucede al revés.\n\n\n### Configurar RBAC <span id=\"configuring-rbac\"></span>\n\nAntes de definir todos los datos de autorización y ejecutar la comprobación de acceso, necesitamos configurar el\ncomponente de la aplicación [[yii\\base\\Application::authManager|authManager]]. Yii provee dos tipos de administradores de autorización:\n[[yii\\rbac\\PhpManager]] y [[yii\\rbac\\DbManager]]. El primero utiliza un archivo PHP para almacenar los datos\nde autorización, mientras que el segundo almacena dichos datos en una base de datos. Puedes considerar utilizar el primero si tu aplicación\nno requiere una administración de permisos y roles muy dinámica.\n\n\n#### Utilizar `PhpManager` <span id=\"using-php-manager\"></span>\n\nEl siguiente código muestra cómo configurar `authManager` en la configuración de nuestra aplicación utilizando la clase [[yii\\rbac\\PhpManager]]:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'authManager' => [\n            'class' => 'yii\\rbac\\PhpManager',\n        ],\n        // ...\n    ],\n];\n```\n\nEl `authManager` ahora puede ser accedido vía `\\Yii::$app->authManager`.\n\nPor defecto, [[yii\\rbac\\PhpManager]] almacena datos RBAC en archivos bajo el directorio `@app/rbac`. Asegúrate de que el directorio\ny todos sus archivos son tienen permiso de escritura para el proceso del servidor Web si la jerarquía de permisos necesita ser modoficada en línea.\n\n\n#### Utilizar `DbManager` <span id=\"using-db-manager\"></span>\n\nEl sigiente código muestra cómo configurar `authManager` en la configuración de la aplicación utilizando la clase [[yii\\rbac\\DbManager]]:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'authManager' => [\n            'class' => 'yii\\rbac\\DbManager',\n        ],\n        // ...\n    ],\n];\n```\n> Note: si estás utilizando el template yii2-basic-app, existe el archivo de configuración `config/console.php` donde\n  necesita declararse `authManager` adicionalmente a `config/web.php`.\n> En el caso de yii2-advanced-app, `authManager` sólo debe declararse en `common/config/main.php`.\n\n`DbManager` utiliza cuatro tablas de la BD para almacenar los datos:\n\n- [[yii\\rbac\\DbManager::$itemTable|itemTable]]: la tabla para almacenar los ítems de autorización. Por defecto \"auth_item\".\n- [[yii\\rbac\\DbManager::$itemChildTable|itemChildTable]]: la tabla para almacentar la jerarquía de los ítems de autorización. Por defecto \"auth_item_child\".\n- [[yii\\rbac\\DbManager::$assignmentTable|assignmentTable]]: la tabla para almacenar las asignaciones de los ítems de autorización. Por defecto \"auth_assignment\".\n- [[yii\\rbac\\DbManager::$ruleTable|ruleTable]]: la tabla para almacenar las reglas. Por defecto \"auth_rule\".\n\nAntes de continuar, necesitas crear las tablas respectivas en la base de datos. Para hacerlo, puedes utilizar las migraciones contenidas en `@yii/rbac/migrations`:\n\n`yii migrate --migrationPath=@yii/rbac/migrations`\n\nEl `authManager` puede ahora ser accedido vía `\\Yii::$app->authManager`.\n\n\n### Construir los Datos de Autorización <span id=\"generating-rbac-data\"></span>\n\nConstruir los datos de autorización implica las siguientes tareas:\n\n- definir roles y permisos;\n- establecer relaciones entre roles y permisos;\n- definir reglas;\n- asociar reglas con roles y permisos;\n- asignar roles a usuarios.\n\nDependiendo de los requerimientos de flexibilidad en la autorización, las tareas se pueden lograr de diferentes maneras.\n\nSi la jerarquía de permisos no cambia en absoluto y tienes un número fijo de usuarios puede crear un\n[comando de consola](tutorial-console.md#create-command) que va a inicializar los datos de autorización una vez a través de las API que ofrece por `authManager`:\n\n```php\n<?php\nnamespace app\\commands;\n\nuse Yii;\nuse yii\\console\\Controller;\n\nclass RbacController extends Controller\n{\n    public function actionInit()\n    {\n        $auth = Yii::$app->authManager;\n\n        // agrega el permiso \"createPost\"\n        $createPost = $auth->createPermission('createPost');\n        $createPost->description = 'Create a post';\n        $auth->add($createPost);\n\n        // agrega el permiso \"updatePost\"\n        $updatePost = $auth->createPermission('updatePost');\n        $updatePost->description = 'Update post';\n        $auth->add($updatePost);\n\n        // agrega el rol \"author\" y le asigna el permiso \"createPost\"\n        $author = $auth->createRole('author');\n        $auth->add($author);\n        $auth->addChild($author, $createPost);\n\n        // agrega el rol \"admin\" y le asigna el permiso \"updatePost\"\n        // más los permisos del rol \"author\"\n        $admin = $auth->createRole('admin');\n        $auth->add($admin);\n        $auth->addChild($admin, $updatePost);\n        $auth->addChild($admin, $author);\n\n        // asigna roles a usuarios. 1 y 2 son IDs devueltos por IdentityInterface::getId()\n        // usualmente implementado en tu modelo User.\n        $auth->assign($author, 2);\n        $auth->assign($admin, 1);\n    }\n}\n```\n\n> Note: Si estas utilizando el template avanzado, necesitas poner tu `RbacController` dentro del directorio `console/controllers`\n  y cambiar el espacio de nombres a `console\\controllers`.\n\nDespués de ejecutar el comando `yii rbac/init`, obtendremos la siguiente jerarquía:\n\n![Simple RBAC hierarchy](images/rbac-hierarchy-1.png \"Simple RBAC hierarchy\")\n\n\"Author\" puede crear un post, \"admin\" puede actualizar posts y hacer todo lo que puede hacer \"author\".\n\nSi tu aplicación permite el registro de usuarios, necesitas asignar los roles necesarios para cada usuario nuevo. Por ejemplo, para que todos\nlos usuarios registrados tengan el rol \"author\", en el template de aplicación avanzada debes modificar `frontend\\models\\SignupForm::signup()`\ncomo a continuación:\n\n```php\npublic function signup()\n{\n    if ($this->validate()) {\n        $user = new User();\n        $user->username = $this->username;\n        $user->email = $this->email;\n        $user->setPassword($this->password);\n        $user->generateAuthKey();\n        $user->save(false);\n\n        // las siguientes tres líneas fueron agregadas\n        $auth = Yii::$app->authManager;\n        $authorRole = $auth->getRole('author');\n        $auth->assign($authorRole, $user->getId());\n\n        return $user;\n    }\n\n    return null;\n}\n```\n\nPara aplicaciones que requieren un control de acceso complejo con una actualización constante en los datos de autorización, puede ser necesario\ndesarrollar una interfaz especial (por ej. un panel de administración) utilizando las APIs ofrecidas por `authManager`.\n\n\n### Utilizar Reglas <span id=\"using-rules\"></span>\n\nComo se había mencionado, las reglas agregan restricciones adicionales a los roles y permisos. Una regla es una clase extendida\nde [[yii\\rbac\\Rule]]. Debe implementar al método [[yii\\rbac\\Rule::execute()|execute()]]. En la jerarquía que creamos\npreviamente, \"author\" no puede editar su propio post. Vamos a arreglarlo. Primero necesitamos una regla para comprobar que el usuario actual es el autor del post:\n\n```php\nnamespace app\\rbac;\n\nuse yii\\rbac\\Rule;\n\n/**\n * Comprueba si authorID coincide con el usuario pasado como parámetro\n */\nclass AuthorRule extends Rule\n{\n    public $name = 'isAuthor';\n\n    /**\n     * @param string|int $user el ID de usuario.\n     * @param Item $item el rol o permiso asociado a la regla\n     * @param array $params parámetros pasados a ManagerInterface::checkAccess().\n     * @return bool un valor indicando si la regla permite al rol o permiso con el que está asociado.\n     */\n    public function execute($user, $item, $params)\n    {\n        return isset($params['post']) ? $params['post']->createdBy == $user : false;\n    }\n}\n```\n\nLa regla anterior comprueba si el `post` fue creado por `$user`. Crearemos un permiso especial, `updateOwnPost`, en el comando que hemos utilizado\nanteriormente:\n\n```php\n$auth = Yii::$app->authManager;\n\n// agrega la regla\n$rule = new \\app\\rbac\\AuthorRule;\n$auth->add($rule);\n\n// agrega el permiso \"updateOwnPost\" y le asocia la regla.\n$updateOwnPost = $auth->createPermission('updateOwnPost');\n$updateOwnPost->description = 'Update own post';\n$updateOwnPost->ruleName = $rule->name;\n$auth->add($updateOwnPost);\n\n// \"updateOwnPost\" será utilizado desde \"updatePost\"\n$auth->addChild($updateOwnPost, $updatePost);\n\n// permite a \"author\" editar sus propios posts\n$auth->addChild($author, $updateOwnPost);\n```\n\nAhora tenemos la siguiente jerarquía:\n\n![RBAC hierarchy with a rule](images/rbac-hierarchy-2.png \"RBAC hierarchy with a rule\")\n\n\n### Comprobación de Acceso <span id=\"access-check\"></span>\n\nCon los datos de autorización listos, la comprobación de acceso se hace con una simple llamada al método [[yii\\rbac\\ManagerInterface::checkAccess()]].\nDado que la mayoría de la comprobación de acceso se hace sobre el usuario actual, para mayor comodidad Yii proporciona el atajo\n[[yii\\web\\User::can()]], que puede ser utilizado como a continuación:\n\n```php\nif (\\Yii::$app->user->can('createPost')) {\n    // crear el post\n}\n```\n\nSi el usuario actual es Jane con `ID=1`, comenzamos desde `createPost` y tratamos de alcanzar a `Jane`:\n\n![Access check](images/rbac-access-check-1.png \"Access check\")\n\nCon el fin de comprobar si un usuario puede actualizar un post, necesitamos pasarle un parámetro adicional requerido por `AuthorRule`, descrito antes:\n\n```php\nif (\\Yii::$app->user->can('updatePost', ['post' => $post])) {\n    // actualizar post\n}\n```\n\nAquí es lo que sucede si el usuario actual es John:\n\n\n![Access check](images/rbac-access-check-2.png \"Access check\")\n\nComenzamos desde `updatePost` y pasamos por `updateOwnPost`. Con el fin de pasar la comprobación de acceso, `AuthorRule`\ndebe devolver `true` desde su método `execute()`. El método recive `$params` desde la llamada al método `can()`, cuyo valor es\n`['post' => $post]`. Si todo está bien, vamos a obtener `author`, el cual es asignado a John.\n\nEn caso de Jane es un poco más simple, ya que ella es un \"admin\":\n\n![Access check](images/rbac-access-check-3.png \"Access check\")\n\n\n### Utilizar Roles por Defecto <span id=\"using-default-roles\"></span>\n\nUn rol por defecto es un rol que esta asignado *implícitamente* a *todos* los usuarios. La llamada a [[yii\\rbac\\ManagerInterface::assign()]]\nno es necesaria, y los datos de autorización no contienen su información de asignación.\n\nUn rol por defecto es usualmente asociado con una regla que determina si el rol aplica al usuario siendo verificado.\n\nLos roles por defecto se utilizan a menudo en aplicaciones que ya tienen algún tipo de asignación de roles. Por ejemplo, una aplicación\npuede tener una columna \"grupo\" en su tabla de usuario para representar a qué grupo de privilegio pertenece cada usuario.\nSi cada grupo privilegio puede ser conectado a un rol de RBAC, se puede utilizar la función de rol por defecto para asignar\ncada usuario a un rol RBAC automáticamente. Usemos un ejemplo para mostrar cómo se puede hacer esto.\n\nSuponga que en la tabla de usuario, usted tiene una columna `group` que utiliza 1 para representar el grupo administrador y 2 al grupo autor.\nPlaneas tener dos roles RBAC, `admin` y `author`, para representar los permisos de estos dos grupos, respectivamente.\nPuede configurar los datos RBAC de la siguiente manera,\n\n\n```php\nnamespace app\\rbac;\n\nuse Yii;\nuse yii\\rbac\\Rule;\n\n/**\n * Comprueba si el grupo coincide\n */\nclass UserGroupRule extends Rule\n{\n    public $name = 'userGroup';\n\n    public function execute($user, $item, $params)\n    {\n        if (!Yii::$app->user->isGuest) {\n            $group = Yii::$app->user->identity->group;\n            if ($item->name === 'admin') {\n                return $group == 1;\n            } elseif ($item->name === 'author') {\n                return $group == 1 || $group == 2;\n            }\n        }\n        return false;\n    }\n}\n\n$auth = Yii::$app->authManager;\n\n$rule = new \\app\\rbac\\UserGroupRule;\n$auth->add($rule);\n\n$author = $auth->createRole('author');\n$author->ruleName = $rule->name;\n$auth->add($author);\n// ... agrega permisos hijos a $author ...\n\n$admin = $auth->createRole('admin');\n$admin->ruleName = $rule->name;\n$auth->add($admin);\n$auth->addChild($admin, $author);\n// ... agrega permisos hijos a $admin ...\n```\n\nTenga en cuenta que en el ejemplo anterior, dado que \"author\" es agregado como hijo de \"admin\", cuando implementes el método `execute()`\nde la clase de la regla, necesitas respetar esta jerarquía. Esto se debe a que cuando el nombre del rol es \"author\",\nel método `execute()` devolverá `true` si el grupo de usuario es tanto 1 como 2 (lo que significa que el usuario se encuentra en\ncualquiera de los dos grupos, \"admin\" o \"author\").\n\nLuego, configura `authManager` enumerando los dos roles en [[yii\\rbac\\BaseManager::$defaultRoles]]:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'authManager' => [\n            'class' => 'yii\\rbac\\PhpManager',\n            'defaultRoles' => ['admin', 'author'],\n        ],\n        // ...\n    ],\n];\n```\n\nAhora si realizas una comprobación de acceso, tanto el rol `admin` y como el rol `author` serán comprobados evaluando\nlas reglas asociadas con ellos. Si la regla devuelve `true`, significa que la regla aplica al usuario actual.\nBasado en la implementación de la regla anterior, esto significa que si el valor `group` en un usuario es 1, el rol `admin`\nse aplicaría al usuario; y si el valor de `group` es 2, se le aplicaría el rol `author`.\n"
  },
  {
    "path": "docs/guide-es/security-passwords.md",
    "content": "Trabajar con Passwords\n======================\n\nLa mayoría de los desarrolladores saben que los passwords no deben ser guardados en texto plano, pero muchos desarrolladores aún creen\nque es seguro aplicar a los passowrds hash `md5` o `sha1`. Hubo un tiempo cuando utilizar esos algoritmos de hash mencionados era suficiente,\npero el hardware moderno hace posible que ese tipo de hash e incluso más fuertes, puedan revertirse rápidamente utilizando ataques de fuerza bruta.\n\nPara poder proveer de una seguridad mayor para los passwords de los usuarios, incluso en el peor de los escenarios (tu aplicación sufre una brecha de seguridad),\nnecesitas utilizar un algoritmo que resista los ataques de fuerza bruta. La mejor elección actualmente es `bcrypt`.\nEn PHP, puedes generar un hash `bcrypt` utilizando la [función crypt](https://www.php.net/manual/en/function.crypt.php). Yii provee\ndos funciones auxiliares que hacen que `crypt` genere y verifique los hash más fácilmente.\n\nCuando un usuario provee un password por primera vez (por ej., en la registración), dicho password necesita ser pasado por un hash:\n\n\n```php\n$hash = Yii::$app->getSecurity()->generatePasswordHash($password);\n```\n\nEl hash puede estar asociado con el atributo del model correspondiente, de manera que pueda ser almacenado en la base de datos para uso posterior.\n\nCuando un usuario intenta ingresar al sistema, el password enviado debe ser verificado con el password con hash almacenado previamente:\n\n\n```php\nif (Yii::$app->getSecurity()->validatePassword($password, $hash)) {\n    // todo en orden, dejar ingresar al usuario\n} else {\n    // password erróneo\n}\n```\n"
  },
  {
    "path": "docs/guide-es/start-databases.md",
    "content": "Trabajar con Bases de Datos\n===========================\n\nEn esta sección, explicaremos cómo crear una nueva página para mostrar datos de países traídos de una tabla de la\nbase de datos llamada `country`. Para lograr este objetivo, configurarás una conexión a la base de datos,\ncrearás una clase [Active Record](db-active-record.md), una [acción](structure-controllers.md)\ny una [vista](structure-views.md).\n\nA lo largo de este tutorial, aprenderás a\n\n* configurar una conexión a la base de datos;\n* definir una clase Active Record;\n* realizar consultas a la base de datos utilizando la clase Active Record;\n* mostrar datos en una vista con paginación incluida.\n\nTen en cuenta que para finalizar esta sección, deberás tener al menos conocimientos básicos y experiencia con bases de datos.\nEn particular, deberás ser capaz de crear una base de datos y saber ejecutar consultas SQL usando alguna herramienta de cliente de base de datos.\n\n\nPreparar una Base de Datos <span id=\"preparing-database\"></span>\n--------------------------\n\nPara empezar, crea una base de datos llamada `yii2basic` de la cual tomarás los datos en la aplicación.\nPuedes elegir entre una base de datos SQLite, MySQL, PostgreSQL, MSSQL u Oracle, dado que Yii incluye soporte para varios motores. Por simplicidad, usaremos MySQL en la siguiente descripción.\n\nA continuación, crea una tabla llamada `country` e inserta algunos datos de ejemplo. Puedes utilizar las siguientes declaraciones SQL.\n\n```sql\nCREATE TABLE `country` (\n  `code` CHAR(2) NOT NULL PRIMARY KEY,\n  `name` CHAR(52) NOT NULL,\n  `population` INT(11) NOT NULL DEFAULT '0'\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nINSERT INTO `country` VALUES ('AU','Australia',24016400);\nINSERT INTO `country` VALUES ('BR','Brazil',205722000);\nINSERT INTO `country` VALUES ('CA','Canada',35985751);\nINSERT INTO `country` VALUES ('CN','China',1375210000);\nINSERT INTO `country` VALUES ('DE','Germany',81459000);\nINSERT INTO `country` VALUES ('FR','France',64513242);\nINSERT INTO `country` VALUES ('GB','United Kingdom',65097000);\nINSERT INTO `country` VALUES ('IN','India',1285400000);\nINSERT INTO `country` VALUES ('RU','Russia',146519759);\nINSERT INTO `country` VALUES ('US','United States',322976000);\n```\n\nAl final, tendrás una base de datos llamada `yii2basic`, y dentro de esta, una tabla llamada `country` con diez registros en ella.\n\nConfigurar una conexión a la Base de Datos <span id=\"configuring-db-connection\"></span>\n------------------------------------------\n\nAsegúrate de tener instalado la extensión de PHP [PDO](https://www.php.net/manual/es/book.pdo.php) y el driver\nde PDO para el motor que estés utilizando (ej. `pdo_mysql` para MySQL). Este es un requisito básico si tu aplicación\nva a utilizar bases de datos relacionales.\n\nAbre el archivo `config/db.php` y ajusta el contenido dependiendo de la configuración a tu base de datos. Por defecto,\nel archivo contiene el siguiente contenido:\n\n```php\n<?php\n\nreturn [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=localhost;dbname=yii2basic',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n];\n```\n\nEl archivo `config/db.php` representa la típica [configuración](concept-configurations.md) basada en archivos. Este archivo de configuración en particular\nespecifica los parámetros necesarios para crear e inicializar una instancia de [[yii\\db\\Connection]] a través de la cual puedes realizar\nconsultas SQL contra la base de datos subyacente.\n\nLa conexión a la base de datos realizada anteriormente puede ser accedida mediante `Yii::$app->db`.\n\n> Info: El archivo `config/db.php` será incluido en el archivo principal de configuración `config/web.php`,\n  el cual especifica cómo la instancia de la [aplicación](structure-applications.md) debe ser inicializada.\n  Para más información, consulta la sección [Configuraciones](concept-configurations.md).\n\nSi necesitas trabajar con bases de datos cuyo soporte no está incluído en Yii, revisa las siguientes extensiones:\n\n- [Informix](https://github.com/edgardmessias/yii2-informix)\n- [IBM DB2](https://github.com/edgardmessias/yii2-ibm-db2)\n- [Firebird](https://github.com/edgardmessias/yii2-firebird)\n\n\nCrear un Active Record <span id=\"creating-active-record\"></span>\n----------------------\n\nPara representar y extraer datos de la tabla `country`, crea una clase [Active Record](db-active-record.md)\nllamada `Country` y guárdala en el archivo `models/Country.php`.\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass Country extends ActiveRecord\n{\n}\n```\n\nLa clase `Country` extiende de [[yii\\db\\ActiveRecord]]. No necesitas escribir ningún código dentro de ella! Con tan sólo el código de arriba,\nYii adivinará la tabla correspondiente a la clase desde su nombre.\n\n> Info: Si no se puede realizar un emparejamiento entre el nombre de la clase y la tabla, puedes\nsobrescribir el método [[yii\\db\\ActiveRecord::tableName()]] para especificar explícitamente el nombre de la tabla asiciada.\n\nUtilizando la clase `Country`, puedes manipular los datos de la tabla `country` fácilmente, como se muestra en los siguiente ejemplos:\n\n```php\nuse app\\models\\Country;\n\n// obtiene todos los registros de la tabla country ordenándolos por \"name\"\n$countries = Country::find()->orderBy('name')->all();\n\n// obtiene el registro cuya clave primaria es \"US\"\n$country = Country::findOne('US');\n\n// muestra \"United States\"\necho $country->name;\n\n// cambia el nombre del país a \"U.S.A.\" y lo guarda en la base de datos\n$country->name = 'U.S.A.';\n$country->save();\n```\n\n> Info: Active Record es una potente forma de acceder y manipular datos de una base de datos de una manera orientada a objetos.\nPuedes encontrar información más detallada acerca de [Active Record](db-active-record.md). Además de Active Record, puedes utilizar un método de acceso de bajo nivel llamado [Data Access Objects](db-dao.md).\n\n\nCrear una Acción <span id=\"creating-action\"></span>\n----------------\n\nPara mostrar el país a los usuarios, necesitas crear una acción. En vez de hacerlo en el controlador `site`\ncomo lo hiciste en las secciones previas, tiene más sentido crear un nuevo controlador que englobe todas las\nacciones de manipulación de datos de la tabla country. Llama a este nuevo controlador `CountryController` y define\nuna acción `index` en él, como se muestra a continuación:\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\nuse yii\\data\\Pagination;\nuse app\\models\\Country;\n\nclass CountryController extends Controller\n{\n    public function actionIndex()\n    {\n        $query = Country::find();\n\n        $pagination = new Pagination([\n            'defaultPageSize' => 5,\n            'totalCount' => $query->count(),\n        ]);\n\n        $countries = $query->orderBy('name')\n            ->offset($pagination->offset)\n            ->limit($pagination->limit)\n            ->all();\n\n        return $this->render('index', [\n            'countries' => $countries,\n            'pagination' => $pagination,\n        ]);\n    }\n}\n```\n\nGuarda el código anterior en el archivo `controllers/CountryController.php`.\n\nLa acción `index` llama a `Country::find()` para generar una consulta a la base de datos y traer todos los datos de la tabla `country`.\nPara limitar la cantidad de registros traídos en cada petición, la consulta es paginada con la ayuda de un objeto\n[[yii\\data\\Pagination]]. El objeto `Pagination` sirve para dos propósitos:\n\n* Define las cláusulas `offset` y `limit` de la consulta SQL para así sólo devolver una sola página de datos\n  (5 registros por página como máximo).\n* Es utilizado en la vista para mostrar un paginador que consiste en una lista de botones que representan a cada página,\n  tal como será explicado en la siguiente sub-sección.\n\nAl final, la acción `index` renderiza una vista llamada `index` y le pasa los datos de países así como la información\nde paginación relacionada.\n\n\nCrear una Vista <span id=\"creating-view\"></span>\n---------------\n\nBajo el directorio `views`, crea primero un sub-directorio llamado `country`. Este será usado para contener\ntodas las vistas renderizadas por el controlador `country`. \nDentro del directorio `views/country`, crea un archivo llamado `index.php` con el siguiente contenido:\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\LinkPager;\n?>\n<h1>Países</h1>\n<ul>\n<?php foreach ($countries as $country): ?>\n    <li>\n        <?= Html::encode(\"{$country->name} ({$country->code})\") ?>:\n        <?= $country->population ?>\n    </li>\n<?php endforeach; ?>\n</ul>\n\n<?= LinkPager::widget(['pagination' => $pagination]) ?>\n```\n\nLa vista consiste en dos partes. En la primera, los datos de países son recorridos y renderizados como una lista HTML.\nEn la segunda parte, un widget [[yii\\widgets\\LinkPager]] es renderizado usando la información de paginación pasada desde la acción.\nEl widget `LinkPager` muestra una lista de botones que representan las páginas disponibles. Haciendo click en cualquiera\nde ellas mostrará los datos de países de la página correspondiente.\n\n\nProbándolo <span id=\"trying-it-out\"></span>\n----------\n\nPara ver cómo funciona, utiliza a la siguiente URL en tu navegador:\n\n```\nhttps://hostname/index.php?r=country%2Findex\n```\n\n![Lista de Países](images/start-country-list.png)\n\nVerás una página que muestra cinco países. Y debajo de todos los países, verás un paginador con cuatro botones.\nSi haces click en el botón \"2\", verás que la página muestra otros cinco países de la base de datos.\nObserva más cuidadosamente y verás que la URL en el navegador cambia a\n\n```\nhttps://hostname/index.php?r=country%2Findex&page=2\n```\n\nEntre bastidores, [[yii\\data\\Pagination|Pagination]] está realizando su magia.\n\n* Inicialmente, [[yii\\data\\Pagination|Pagination]] representa la primera página, que agrega a la consulta SQL\n  a la base de datos con la cláusula `LIMIT 5 OFFSET 0`. Como resultado, los primeros cinco países serán traídos y mostrados.\n* El widget [[yii\\widgets\\LinkPager|LinkPager]] renderiza los botones de páginas usando las URLs\n  creadas por [[yii\\data\\Pagination::createUrl()|Pagination]]. Las URLs contendrán el parámetro `page`\n  representando los números de páginas.\n* Si haces click en el botón \"2\", se lanza y maneja una nueva petición a la ruta `country/index`.\n  [[yii\\data\\Pagination|Pagination]] lee el parámetro `page` y define el número de página actual como \"2\".\n  Por consiguiente, la consulta a la base de datos tendrá la cláusula `LIMIT 5 OFFSET 5` y devolverá los\n  siguientes cinco países para mostrar.\n\n\nResumen <span id=\"summary\"></span>\n-------\n\nEn esta sección has aprendido cómo trabajar con una base de datos. También has aprendido cómo traer y mostrar\ndatos paginados con la ayuda de [[yii\\data\\Pagination]] y [[yii\\widgets\\LinkPager]].\n\nEn la siguiente sección, aprenderás a utilizar la poderosa herramienta de generación de código llamada [Gii](tool-gii.md),\npara ayudarte a implementar rápidamente algunas características comunes, como crear operaciones de Alta-Baja-Modificación\n(ABM, o CRUD en inglés) de los datos guardados en la base de datos. De hecho, el código que acabas de escribir fue \ngenerado automáticamente a través de esta herramienta.\n"
  },
  {
    "path": "docs/guide-es/start-forms.md",
    "content": "Trabajando con Formularios\n==========================\n\nEn esta sección, describiremos como crear una nueva página para solicitar información de los usuarios.\nLa página mostrará un formulario con un campo de input para el nombre y un campo de input para el email.\nDespués de recibir estos datos del usuario, la página le mostrará la información de vuelta al usuario para la confirmación.\n\nPara lograr este objetivo, además de crear una [acción](structure-controllers.md) y\ndos [vistas](structure-views.md), también crearás un [modelo](structure-models.md).\n\nA través de este tutorial, aprenderás\n\n* Cómo crear un [modelo](structure-models.md) para representar los datos ingresados por un usuario;\n* Cómo declarar reglas para validar los datos ingresado por los usuarios;\n* Cómo construir un formulario HTML en una [vista](structure-views.md).\n\n\nCreando un Modelo <span id=\"creating-model\"></span>\n-----------------\n\nPara representar los datos ingresados por un usuario, crea una clase modelo `EntryForm` cómo se muestra abajo y\nguarda la clase en el archivo `models/EntryForm.php`. Por favor, visita la sección [Autocargando Clases](concept-autoloading.md)\npara obtener más detalles acerca de la convención de nombres de los archivos de clase.\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse yii\\base\\Model;\n\nclass EntryForm extends Model\n{\n    public $name;\n    public $email;\n\n    public function rules()\n    {\n        return [\n            [['name', 'email'], 'required'],\n            ['email', 'email'],\n        ];\n    }\n}\n```\n\nLa clase se extiende a partir de [[yii\\base\\Model]], que es una clase base que provee Yii y es comúnmente utilizada\npara representar datos de formularios.\n\nLa clase contiene dos miembros públicos, `name` y `email`, que son utilizas para mantener\nlos datos ingresados por el usuario. También contiene el método llamado `rules()` que regresa un conjunto\nde reglas utilizadas para validar los datos. Las reglas de validación declaradas arriba indican que\n\n* ambos datos, tanto el `name` como el `email`, son requeridos;\n* el dato `email` debe ser una dirección de correo válida.\n\nSi tienes un objeto `EntryForm` llenado con los datos ingresados por el usuario, puedes llamar\nsu [[yii\\base\\Model::validate()|validate()]] para disparar (trigger) la validación de los datos. Un fallo en la validación\nde los datos se mostrará en la propiedad [[yii\\base\\Model::hasErrors|hasErrors]], y a través de\n[[yii\\base\\Model::getErrors|errors]] puedes aprender cuales son los errores de validación que tiene el modelo.\n\n\nCreando una Acción <span id=\"creating-action\"></span>\n------------------\n\nLuego, crea una acción `entry` en el controlador `site`, como lo hiciste en la sección anterior.\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\nuse app\\models\\EntryForm;\n\nclass SiteController extends Controller\n{\n    // ...código existente...\n\n    public function actionEntry()\n    {\n        $model = new EntryForm;\n\n        if ($model->load(Yii::$app->request->post()) && $model->validate()) {\n            // validar los datos recibidos en el modelo\n\n            // aquí haz algo significativo con el modelo ...\n\n            return $this->render('entry-confirm', ['model' => $model]);\n        } else {\n            // la página es mostrada inicialmente o hay algún error de validación\n            return $this->render('entry', ['model' => $model]);\n        }\n    }\n}\n```\n\nLa acción primero crea un objeto `EntryForm`. Luego intenta poblar el modelo\ncon los datos del `$_POST` que es proporcionado por Yii a través de [[yii\\web\\Request::post()]].\nSi el modelo es llenado satisfactoriamente (ej., el usuario ha enviado el formulario HTML),\nllamará a [[yii\\base\\Model::validate()|validate()]] para asegurarse que los datos ingresados\nson válidos.\n\nSi todo está bien, la acción mostrará una vista llamada `entry-confirm` para confirmar\ncon el usuario que acepta los datos que ha ingresado. De otra manera, la vista `entry` será\nmostrada, y mostrará el formulario HTML junto con los mensajes de error de validación (si es que hay alguno).\n\n> Info: La expresión `Yii::$app` representa la instancia de la [aplicación](structure-applications.md)\n  que es un singleton globalmente accesible. También es un [service locator](concept-service-locator.md) (localizador de servicio)\n  que provee los componentes, tales como `request`, `response`, `db`, etc. para soportar funcionalidades específicas.\n  En el código de arriba, el componente `request` es utilizado para acceder los datos `$_POST`.\n\n\nCreando Vistas <span id=\"creating-views\"></span>\n--------------\n\nFinalmente, crea dos vistas llamadas `entry-confirm` y `entry` que sean mostradas por la acción `entry`,\ntal y como fue descrito en la última sub-sección.\n\nLa vista `entry-confirm` simplemente muestra los datos de name y email. Ésta debe ser guardada como el archivo `views/site/entry-confirm.php`.\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n<p>You have entered the following information:</p>\n\n<ul>\n    <li><label>Name</label>: <?= Html::encode($model->name) ?></li>\n    <li><label>Email</label>: <?= Html::encode($model->email) ?></li>\n</ul>\n```\n\nLa vista `entry` muestra un formulario HTML. Debe ser guardado como el archivo `views/site/entry.php`.\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n?>\n<?php $form = ActiveForm::begin(); ?>\n\n    <?= $form->field($model, 'name') ?>\n\n    <?= $form->field($model, 'email') ?>\n\n    <div class=\"form-group\">\n        <?= Html::submitButton('Submit', ['class' => 'btn btn-primary']) ?>\n    </div>\n\n<?php ActiveForm::end(); ?>\n```\n\nLa vista utiliza un poderoso [widget](structure-widgets.md) llamado [[yii\\widgets\\ActiveForm|ActiveForm]] para\nconstruir el formulario HTML. Los métodos `begin()` y `end()` del widget muestran, respectivamente, las etiquetas de \napertura y cierre del formulario. Entre las llamadas de los dos métodos, los campos de input son creados por el\nmétodo [[yii\\widgets\\ActiveForm::field()|field()]]. El primer campo input es del dato \"name\",\ny el segundo del dato \"email\". Después de los campos de input, el método [[yii\\helpers\\Html::submitButton()]] \nes llamado para general el botón de submit (enviar).\n\n\nProbándolo <span id=\"trying-it-out\"></span>\n----------\n\nPara ver cómo funciona, utiliza tu navegador para ir al siguiente URL:\n\n```\nhttps://hostname/index.php?r=site/entry\n```\n\nVerás una página que muestra un formulario con dos campos de input. Adelante de cada campo de input, será mostrada también \nuna etiqueta indicando que dato necesitas ingresar. Si haces click en el botón de envío (Submit) sin ingresar nada, \no si ingresas una dirección de correo inválida, verás un mensaje de error que se mostrará al lado del campo que tiene problemas.\n\n![Formulario con Errores de Validación](images/start-form-validation.png)\n\nDespués de ingresar un nombre y dirección de correo válidos y haciendo click en el botón de envío (Submit), verás una nueva página\nmostrando los datos que acabas de ingresar.\n\n![Confirmación de los Datos de Entrada](images/start-entry-confirmation.png)\n\n\n\n### Magia Explicada <span id=\"magic-explained\"></span>\n\nTe estarás preguntando cómo funciona toda esa automatización del formulario HTML, porque parece casi mágico que pueda \nmostrar una etiqueta para cada campo de input y mostrar los mensajes de error si no ingresas los datos correctamente\nsin recargar la página.\n\nSi, la validación de los datos se realiza en el lado del cliente utilizando JavaScript así como también en el lado del servidor.\n[[yii\\widgets\\ActiveForm]] es lo suficientemente inteligente como para extraer las reglas de validación que has declarado en `EntryForm`,\nconvertirlas en código Javascript, y utilizar el JavaScript para realizar la validación de los datos. En caso de que hayas deshabilitado \nJavaScript en tu navegador, la validación se realizará igualmente en el lado del servidor, como se muestra en \nel método `actionEntry()`. Esto garantiza la validez de los datos en cualquier circunstancias.\n\nLas etiquetas de los campos de input son generados por el método `field()` basado en los nombres de las propiedades del modelo.\nPor ejemplo, la etiqueta `Name` será generada de la propiedad `name`. Puedes personalizar una etiqueta con\nel siguiente código:\n\n```php\n<?= $form->field($model, 'name')->label('Tu Nombre') ?>\n<?= $form->field($model, 'email')->label('Tu Email') ?>\n```\n\n> Info: Yii provee muchos widgets para ayudarte a construir rápidamente vistas complejas y dinámicas.\n  Como aprenderás más adelante, escribir un nuevo  widget es extremadamente fácil. Puedes convertir mucho del\n  código de tus vistas en widgets reutilizables para simplificar el desarrollo de las vistas en un futuro.\n\n\nResumen <span id=\"summary\"></span>\n-------\n\nEn esta sección, has tocado cada parte del patrón de diseño MVC. Ahora has aprendido\na crear una clase modelo para representar los datos del usuario y validarlos.\n\nTambién has aprendido como obtener datos de los usuarios y como mostrarlos de vuelta. Esta es una tarea que\npuede tomarte mucho tiempo cuando estás desarrollando una aplicación. Yii provee poderosos widgets \npara hacer muy fácil esta tarea.\n\nEn la próxima sección, aprenderás como trabajar con bases de datos que son necesarias en casi cualquier aplicación.\n"
  },
  {
    "path": "docs/guide-es/start-gii.md",
    "content": "Generando Código con Gii\n========================\n\nEn esta sección, explicaremos cómo utilizar [Gii](tool-gii.md) para generar código que automáticamente\nimplementa algunas de las características más comunes de una aplicación. Para lograrlo, todo lo que tienes que hacer es\ningresar la información de acuerdo a las instrucciones mostradas en la páginas web de Gii.\n\nA lo largo de este tutorial, aprenderás\n\n* Cómo activar Gii en tu aplicación;\n* Cómo utilizar Gii para generar una clase Active Record;\n* Cómo utilizar Gii para generar el código que implementa las operaciones ABM de una tabla de la base de datos.\n* Cómo personalizar el código generado por Gii.\n\n\nComenzando con Gii <span id=\"starting-gii\"></span>\n------------------\n\n[Gii](tool-gii.md) está provisto por Yii en forma de [módulo](structure-modules.md). Puedes habilitar Gii\nconfigurándolo en la propiedad [[yii\\base\\Application::modules|modules]] de la aplicación. Dependiendo de cómo hayas creado tu aplicación, podrás encontrar que el siguiente código ha sido ya incluido en el archivo de configuración `config/web.php`:\n\n```php\n$config = [ ... ];\n\nif (YII_ENV_DEV) {\n    $config['bootstrap'][] = 'gii';\n    $config['modules']['gii'] = [\n        'class' => 'yii\\gii\\Module',\n    ];\n}\n```\n\nLa configuración dice que al estar en el [entorno de desarrollo](concept-configurations.md#environment-constants),\nla aplicación debe incluir el módulo llamado `gii`, cuya clase es [[yii\\gii\\Module]].\n\nSi chequeas el [script de entrada](structure-entry-scripts.md) `web/index.php` de tu aplicación, encontrarás la línea\nque esencialmente define la constante `YII_ENV_DEV` como verdadera -`true`.\n\n```php\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n```\n\nDe esta manera, tu aplicación habrá habilitado Gii, y puedes acceder al módulo a través de la siguiente URL:\n\n```\nhttps://hostname/index.php?r=gii\n```\n\n![Gii](images/start-gii.png)\n\n\nGenerando una Clase Active Record <span id=\"generating-ar\"></span>\n---------------------------------\n\nPara poder generar una clase Active Record con Gii, selecciona \"Model Generator\" (haciendo click en el vínculo que existe en la página inicial del modulo Gii). Después, completa el formulario de la siguiente manera,\n\n* Table Name: `country`\n* Model Class: `Country`\n\n![Model Generator](images/start-gii-model.png)\n\nHaz click el el botón \"Preview\". Verás que `models/Country.php` está mostrado listado como la clase resultante que ha de ser creada. Puedes hacer click en el nombre de la clase para previsualizar su contenido.\n\nAl utilizar Gii, si habías creado previamente el mismo archivo y puede ser sobrescrito, si haces click\nen el botón `diff` cercano al nombre del archivo, verás las diferencias entre el código a ser generado\ny la versión existente del mismo.\n\n![Previsualización del Model Generator](images/start-gii-model-preview.png)\n\nPara sobrescribir un archivo existente, marca el checkbox que se encuentra al lado de \"overwrite\" y posteriormente haz click en el botón \"Generate\".\n\nDespués, verás una página de confirmación indicando que el código ha sido generado correctamente y tu archivo `models/Country.php`\nha sido sobrescrito con el nuevo código generado.\n\n\nGenerando código de ABM (CRUD en inglés) <span id=\"generating-crud\"></span>\n----------------------------------------\n\nEn computación, CRUD es el acrónimo de Crear, Obtener, Actualizar y Borrar (del inglés: Create, Read, Update y Delete) \nrepresentando la cuatro funciones con datos más comunes en la mayoría de sitios Web. \nEl acrónimo ABM es Altas, Bajas y Modificaciones. Para generar un ABM, selecciona \"CRUD Generator\" y completa el formulario de esta manera:\n\n* Model Class: `app\\models\\Country`\n* Search Model Class: `app\\models\\CountrySearch`\n* Controller Class: `app\\controllers\\CountryController`\n\n![Generador de ABM](images/start-gii-crud.png)\n\nAl hacer click en el botón \"Preview\" verás la lista de archivos a ser generados.\n\nSi has creado previamente los archivos `controllers/CountryController.php` y\n`views/country/index.php` (en la sección sobre bases de datos de esta guía), asegúrate de seleccionar el checkbox \"overwrite\" para reemplazarlos. (Las versiones anteriores no disponían de un soporte ABM (CRUD) completo.)\n\n\nProbándolo <span id=\"trying-it-out\"></span>\n----------\n\nPara ver cómo funciona, accede desde tu navegador a la siguiente URL:\n\n```\nhttps://hostname/index.php?r=country/index\n```\n\nVerás una grilla de datos mostrando los países de la base de datos. Puedes ordenar la grilla\no filtrar los resultados escribiendo alguna condición en los encabezados de las columnas.\n\nPor cada país mostrado en la grilla, puedes elegir entre visualizar el registro, actualizarlo o eliminarlo.\nPuedes incluso hacer click en el botón \"Create Country\" que se encuentra sobre la grilla y así cargar\nun nuevo país en la base de datos.\n\n![Grilla de Países](images/start-gii-country-grid.png)\n\n![Actualizando un País](images/start-gii-country-update.png)\n\nLa siguiente es la lista de archivos generados por Gii, en el caso de que quieras inspeccionar cómo el ABM ha sido generado,\no por si desearas personalizarlos:\n\n* Controlador: `controllers/CountryController.php`\n* Modelos: `models/Country.php` y `models/CountrySearch.php`\n* Vistas: `views/country/*.php`\n\n> Info: Gii está diseñado para ser una herramienta altamente configurable. Utilizándola con sabiduría\n  puede acelerar enormemente la velocidad de desarrollo de tu aplicación. Para más detalles, consulta la\n  sección [Gii](tool-gii.md).\n\n\nResumen <span id=\"summary\"></span>\n-------\n\nEn esta sección, has aprendido a utilizar Gii para generar el código que implementa completamente las características\nde un ABM de acuerdo a una determinada tabla de la base de datos.\n"
  },
  {
    "path": "docs/guide-es/start-hello.md",
    "content": "Diciendo Hola\n=============\n\nEsta sección describe cómo crear la típica página \"Hola Mundo\" (Hello World en inglés) en tu aplicación.\nPara lograr este objetivo, vas a crear una [acción](structure-controllers.md#creating-actions) y \nuna [vista](structure-views.md):\n\n* La aplicación enviará la petición de la página a la acción\n* y la acción regresará el render de la vista que muestra la palabra \"Hola\" al usuario final.\n\nA lo largo de este tutorial, aprenderás tres cosas:\n\n1. Cómo crear una [acción](structure-controllers.md) para responder peticiones (request),\n2. Cómo crear una [vista](structure-views.md) para armar el contenido de la respuesta, y\n3. Cómo una aplicación envía peticiones a las [acciones](structure-controllers.md#creating-actions).\n\n\nCreando una Acción <span id=\"creating-action\"></span>\n------------------\n\nPara la tarea \"Hola\", crearás una [acción](structure-controllers.md#creating-actions) `say` que lee\nun parámetro `message` de la petición y muestra este mensaje de vuelta al usuario. Si la petición\nno provee un parámetro `message`, la acción mostrará el mensaje por defecto \"Hola\".\n\n> Info: Las [acciones](structure-controllers.md#creating-actions) son objetos que los usuarios finales pueden utilizar directamente para\n  su ejecución. Las acciones están agrupadas por [controladores](structure-controllers.md) (controllers). El resultado de la ejecución de\n  una acción es la respuesta que el usuario final recibirá.\n\nLas acciones deben ser declaradas en [controladores](structure-controllers.md). Para simplificar, puedes\ndeclarar la acción `say` en el controlador `SiteController` existente. Este controlador está definido\nen el archivo de clase `controllers/SiteController.php`. Aquí está el inicio de la nueva acción:\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    // ...código existente...\n\n    public function actionSay($message = 'Hola')\n    {\n        return $this->render('say', ['message' => $message]);\n    }\n}\n```\n\nEn el código de arriba, la acción `say` está definida por un método llamado `actionSay` en la clase `SiteController`.\nYii utiliza el prefijo `action` para diferenciar los métodos de acciones de otros métodos en las clases de los controladores.\nEl nombre que le sigue al prefijo `action` se mapea al ID de la acción.\n\nCuando se trata de nombrar las acciones, debes entender como Yii trata los ID de las acciones. Los ID de las acciones siempre son \nreferenciados en minúscula. Si un ID de acción requiere múltiples palabras, estas serán concatenadas con guiones\n(ej., `crear-comentario`). Los nombres de los métodos de las acciones son mapeados a los ID de las acciones \nremoviendo los guiones, colocando en mayúscula la primera letra de cada palabra, y colocando el prefijo `action` al resultado. Por ejemplo,\nel ID de la acción `crear-comentario` corresponde al nombre de método de acción `actionCrearComentario`.\n\nEl método de acción en nuestro ejemplo toma un parámetro `$message`, el cual tiene como valor por defecto `\"Hola\"` (de la misma manera \nque se coloca un valor por defecto a un argumento en cualquier función o método en PHP). Cuando una aplicación \nrecibe una petición y determina que la acción `say` es responsable de manejar dicha petición, la aplicación llenará \nel parámetro con el parámetro que tenga el mismo nombre en la petición. En otras palabras, si la petición incluye un \nparámetro `message` con el valor de `\"Adios\"`, la variable `$message` dentro de la acción será sustituida por este valor.\n\nDentro del método de acción, [[yii\\web\\Controller::render()|render()]] es llamado para hacer render (mostrar o visualizar) un \narchivo [vista](structure-views.md) (template) llamado `say`. El parámetro `message` tambien es pasado al  view para que pueda ser utilizado ahí. \nEl resultado es devuelto al método de la acción. Ese resultado será recibido por la aplicación y mostrado al usuario final en el\nnavegador (como parte de una página HTML completa).\n\n\nCreando una Vista <span id=\"creating-view\"></span>\n-----------------\n\nLas [vistas](structure-views.md) son scripts que escribes para generar una respuesta de contenido.\nPara la tarea \"Hola\", vas a crear una vista `say` que imprime el parámetro `message` recibido desde el método action, y pasado por la acción a la vista:\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n<?= Html::encode($message) ?>\n```\n\nLa vista `say` debe ser guardada en el archivo `views/site/say.php`. Cuando el método [[yii\\web\\Controller::render()|render()]]\nes llamado en una acción, buscará un archivo PHP llamado `views/ControllerID/NombreVista.php`.\n\nNota que en el código de arriba, el parámetro `message` es procesado por [[yii\\helpers\\Html::encode()|HTML-encoded]]\nantes de ser impreso. Esto es necesario ya que el parámetro viene de un usuario final, haciéndolo vulnerable a \n[ataques cross-site scripting (XSS)](https://es.wikipedia.org/wiki/Cross-site_scripting) pudiendo inyectar código de Javascript malicioso dentro del parámetro.\n\nNaturalmente, puedes colocar mas contenido en la vista `say`. El contenido puede consistir de etiquetas HTML, texto plano, e inclusive código PHP.\nDe hecho, la vista `say` es sólo un script PHP que es ejecutado por el método [[yii\\web\\Controller::render()|render()]].\nEl contenido impreso por el script de la vista será regresado a la aplicación como la respuesta del resultado. La aplicación a cambio mostrará el resultado al usuario final.\n\n\nProbándolo <span id=\"trying-it-out\"></span>\n----------\n\nDespués de crear la acción y la vista, puedes acceder a la nueva página abriendo el siguiente URL:\n\n```\nhttps://hostname/index.php?r=site%2Fsay&message=Hello+World\n```\n\n![Hello World](images/start-hello-world.png)\n\nEsta URL resultará en una página mostrando \"Hello World\". La página comparte el mismo encabezado y pie de página de las otras páginas de la aplicación. \n\nSi omites el parámetro `message` en el URL, verás que la página muestra sólo \"Hola\". Esto es porque `message` es pasado como un parámetro al método `actionSay()`, \ny cuando es omitido, el valor por defecto `\"Hola\"` será utilizado.\n\n> Info: La nueva página comparte el mismo encabezado y pie de página que otras páginas porque el método [[yii\\web\\Controller::render()|render()]]\n  automáticamente inyectará el resultado de la vista `say` en el [layout](structure-views.md#layouts), que en este \n  caso está localizada en `views/layouts/main.php`.\n\nEl parámetro `r` en el URL de arriba requiere más explicación. Se refierea a [route](runtime-routing.md) (ruta), y es el ID amplio y único de una aplicación\nque refiere a una acción. El formato de las rutas es `ControllerID/ActionID`. Cuando la aplicación recibe una petición, revisará este parámetro,\nutilizando la parte del `ControllerID` para determinar cual clase de controlador será inicializado para manejar la petición. Entonces, el controlador utilizará\nla parte `ActionID` para determinar cual acción debe ser inizializada para hacer realmente el trabajo. \nEn este ejemplo, la ruta  `site/say` será respondida por la clase controlador `SiteController` y la acción `say`. Como resultado, \nel método `SiteController::actionSay()` será llamado para manejar el requerimiento.\n\n> Info: Al igual que las acciones, los controladores tambien tienen ID únicos que los identifican en una aplicación.\n  Los ID de los Controladores utilizan las mismas reglas de nombrado que los ID de las acciones. Los nombres de las clases de los controladores son derivados de los ID de los controladores removiendo los guiones de los ID, colocando la primera letra en mayúscula en cada palabra, y colocando el sufijo `Controller` al resultado. Por ejemplo, el ID del controlador `post-comentario` corresponde\n  al nombre de clase del controlador `PostComentarioController`.\n\n\nResumen <span id=\"summary\"></span>\n-------\n\nEn esta sección, has tocado las partes del controlador y la vista del patrón de diseño MVC.\nHas creado una acción como parte de un controlador para manejar una petición específica. Y también has creado una vista para armar el contenido de la respuesta. \nEn este simple ejemplo, ningún modelo ha sido involucrado ya que el único dato que fue utilizado fue el parámetro `message`.\n\nTambién has aprendido acerca de las rutas en Yii, que actúan como puentes entre la petición del usuario y las acciones del controlador.\n\nEn la próxima sección, aprenderás como crear un modelo, y agregar una nueva página que contenga un formulario HTML.\n"
  },
  {
    "path": "docs/guide-es/start-installation.md",
    "content": "Instalar Yii\n============\n\nPuedes instalar Yii de dos maneras, utilizando el administrador de paquetes [Composer](https://getcomposer.org/) o descargando un archivo comprimido.\nLa forma recomendada es la primera, ya que te permite instalar nuevas [extensions](structure-extensions.md) o actualizar Yii con sólo ejecutar un comando.\n\nLa instalación estándar de Yii cuenta tanto con el framework como un template de proyecto instalados.\nUn template de proyecto es un proyecto Yii funcional que implementa algunas características básicas como: login, formulario de contacto, etc. \nEl código está organizado de una forma recomendada. Por lo tanto, puede servir como un buen punto de partida para tus proyectos.\n    \nEn esta y en las próximas secciones, describiremos cómo instalar Yii con el llamado *Template de Proyecto Básico*\ny cómo implementar nuevas características por encima del template. Yii también provee otro template llamado\n[Template de Proyecto Avanzado](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md) qué es mejor para desarrollar aplicaciones con varios niveles\nen el entorno de un equipo de desarrollo.\n\n> Info: El Template de Proyecto Básico es adecuado para desarrollar el 90 porciento de las aplicaciones Web. Difiere del\n  Template de Proyecto Avanzado principalmente en cómo está organizado el código. Si eres nuevo en Yii, te recomendamos\n  utilizar el Template de Proyecto Básico por su simplicidad pero funcionalidad suficiente.\n\n\nInstalando via Composer <span id=\"installing-via-composer\"></span>\n-------------------------------\n\nSi aún no tienes Composer instalado, puedes hacerlo siguiendo las instrucciones que se encuentran en\n[getcomposer.org](https://getcomposer.org/download/). En Linux y Mac OS X, se ejecutan los siguientes comandos:\n\n```bash\ncurl -sS https://getcomposer.org/installer | php\nmv composer.phar /usr/local/bin/composer\n```\n\nEn Windows, tendrás que descargar y ejecutar [Composer-Setup.exe](https://getcomposer.org/Composer-Setup.exe).\n\nPor favor, consulta la [Documentación de Composer](https://getcomposer.org/doc/) si encuentras algún problema\no deseas obtener un conocimiento más profundo sobre su utilización.\n\nSi ya tienes composer instalado, asegúrate de tener una versión actualizada. Puedes actualizar Composer\nejecutando el comando `composer self-update`\n\nTeniendo Composer instalado, puedes instalar Yii ejecutando los siguientes comandos en un directorio accesible vía Web:\n\n```bash\ncomposer global require \"fxp/composer-asset-plugin:^1.4.1\"\ncomposer create-project --prefer-dist yiisoft/yii2-app-basic basic\n```\n\nEl primer comando instala [composer asset plugin](https://github.com/fxpio/composer-asset-plugin),\nque permite administrar dependencias de paquetes bower y npm a través de Composer. Sólo necesitas ejecutar este comando\nuna vez. El segundo comando instala Yii en un directorio llamado `basic`. Puedes elegir un nombre de directorio diferente si así lo deseas.\n\n> Note: Durante la instalación, Composer puede preguntar por tus credenciales de acceso de Github. Esto es normal ya que Composer \n> necesita obtener suficiente límite de acceso de la API para traer la información de dependencias de Github. Para más detalles, \n> consulta la [documentación de Composer](https://getcomposer.org/doc/articles/troubleshooting.md#api-rate-limit-and-oauth-tokens).\n\n> Tip: Si quieres instalar la última versión de desarrollo de Yii, puedes utilizar uno de los siguientes comandos,\n> que agregan una [opción de estabilidad](https://getcomposer.org/doc/04-schema.md#minimum-stability):\n>\n> ```bash\n> composer create-project --prefer-dist --stability=dev yiisoft/yii2-app-basic basic\n> ```\n>\n> Ten en cuenta que la versión de desarrollo de Yii no debería ser utilizada en producción ya que podría romper tu código actual.\n\n\nInstalar desde un Archivo Comprimido <span id=\"installing-from-archive-file\"></span>\n------------------------------------\n\nInstalar Yii desde un archivo comprimido involucra tres pasos:\n\n1. Descargar el archivo desde [yiiframework.com](https://www.yiiframework.com/download/yii2-basic).\n2. Descomprimirlo en un directorio accesible vía Web.\n3. Modificar el archivo `config/web.php` introduciendo una clave secreta para el ítem de configuración `cookieValidationKey`\n   (esto se realiza automáticamente si estás instalando Yii a través de Composer):\n\n   ```php\n   // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation\n   'cookieValidationKey' => 'enter your secret key here',\n   ```\n\n\nOtras Opciones de Instalación <span id=\"other-installation-options\"></span>\n-----------------------------\n\nLas instrucciones anteriores muestran cómo instalar Yii, lo que también crea una aplicación Web lista para ser usada.\nEste es un buen punto de partida para la mayoría de proyectos, tanto grandes como pequeños. Es especialmente adecuado si recién\nestás aprendiendo a utilizar Yii.\n\nPero también hay otras opciones de instalación disponibles:\n\n* Si sólo quieres instalar el núcleo del framework y entonces crear una nueva aplicación desde cero,\n  puedes seguir las instrucciones explicadas en [Generando una Aplicación desde Cero](tutorial-start-from-scratch.md).\n* Si quisieras comenzar con una aplicación más avanzada, más adecuada para un entorno de desarrollo de equipo,\n  deberías considerar instalar el [Template de Aplicación Avanzada](tutorial-advanced-app.md).\n\n\nVerificando las Instalación <span id=\"verifying-installation\"></span>\n---------------------------\n\nUna vez finalizada la instalación, o bien configura tu servidor web (mira la sección siguiente) o utiliza\nel [servidor web incluido en PHP](https://www.php.net/manual/es/features.commandline.webserver.php) ejecutando el siguiente\ncomando de consola estando parado en el directorio `web` de la aplicación:\n \n```bash\nphp yii serve\n```\n\n> Note: Por defecto el servidor HTTP escuchará en el puerto 8080. De cualquier modo, si el puerto está en uso o deseas \nservir varias aplicaciones de esta manera, podrías querer especificar qué puerto utilizar. Sólo agrega el argumento --port:\n\n```bash\nphp yii serve --port=8888\n```\n\nPuedes utilizar tu navegador para acceder a la aplicación instalada de Yii en la siguiente URL:\n\n```\nhttp://localhost:8080/.\n```\n\n![Instalación Correcta de Yii](images/start-app-installed.png)\n\nDeberías ver la página mostrando \"Congratulations!\" en tu navegador. Si no ocurriera, por favor chequea que la instalación\nde PHP satisfaga los requerimientos de Yii. Esto puedes hacerlo usando cualquiera de los siguientes procedimientos:\n\n* Copiando `/requirements.php` a `/web/requirements.php` y visitando la URL `http://localhost/basic/requirements.php` en tu navegador\n* Corriendo los siguientes comandos:\n\n  ```bash\n  cd basic\n  php requirements.php\n  ```\n  \nDeberías configurar tu instalación de PHP para que satisfaga los requisitos mínimos de Yii. Lo que es más importante,\ndebes tener PHP 5.4 o mayor. También deberías instalar la [Extensión de PHP PDO](https://www.php.net/manual/es/pdo.installation.php)\ny el correspondiente driver de base de datos (como `pdo_mysql` para bases de datos MySQL), si tu aplicación lo necesitara.\n\n\nConfigurar Servidores Web <span id=\"configuring-web-servers\"></span>\n-------------------------\n\n> Info: Puedes saltear esta sección por ahora si sólo estás probando Yii sin intención\n  de poner la aplicación en un servidor de producción.\n\nLa aplicación instalada siguiendo las instrucciones mencionadas debería estar lista para usar tanto\ncon un [servidor HTTP Apache](https://httpd.apache.org/) como con un [servidor HTTP Nginx](https://nginx.org/),\nen Windows, Mac OS X, o Linux utilizando PHP 5.4 o mayor. Yii 2.0 también es compatible con [HHVM](https://hhvm.com/)\nde Facebook. De todos modos, hay algunos casos donde HHVM se comporta diferente del\nPHP oficial, por lo que tendrás que tener cuidados extra al utilizarlo.\n\nEn un servidor de producción, podrías querer configurar el servidor Web para que la aplicación sea accedida\na través de la URL `https://www.example.com/index.php` en vez de `https://www.example.com/basic/web/index.php`. Tal configuración\nrequire apuntar el document root de tu servidor Web a la carpeta `basic/web`. También podrías\nquerer ocultar `index.php` de la URL, como se describe en la sección [Parseo y Generación de URLs](runtime-url-handling.md).\nEn esta sub-sección, aprenderás a configurar tu servidor Apache o Nginx para alcanzar estos objetivos.\n\n> Info: Al definir `basic/web` como document root, también previenes que los usuarios finales accedan\nal código privado o archivos con información sensible de tu aplicación que están incluidos en los directorios del mismo nivel\nque `basic/web`. Denegando el acceso es una importante mejora en la seguridad.\n\n> Info: En caso de que tu aplicación corra en un entorno de hosting compartido donde no tienes permisos para modificar\nla configuración del servidor Web, aún puedes ajustar la estructura de la aplicación para mayor seguridad. Por favor consulta\nla sección [Entorno de Hosting Compartido](tutorial-shared-hosting.md) para más detalles.\n\n\n### Configuración Recomendada de Apache <span id=\"recommended-apache-configuration\"></span>\n\nUtiliza la siguiente configuración del archivo `httpd.conf` de Apache dentro de la configuración del virtual host. Ten en cuenta\nque deberás reemplazar `path/to/basic/web` con la ruta real a `basic/web`.\n\n```apache\n# Definir el document root como \"basic/web\"\nDocumentRoot \"path/to/basic/web\"\n\n<Directory \"path/to/basic/web\">\n    # utiliza mod_rewrite para soporte de URLs amigables\n    RewriteEngine on\n    # Si el directorio o archivo existe, utiliza la petición directamente\n    RewriteCond %{REQUEST_FILENAME} !-f\n    RewriteCond %{REQUEST_FILENAME} !-d\n    # Sino, redirige la petición a index.php\n    RewriteRule . index.php\n\n    # ...otras configuraciones...\n</Directory>\n```\n\n\n### Configuración Recomendada de Nginx <span id=\"recommended-nginx-configuration\"></span>\n\nPara utilizar [Nginx](https://wiki.nginx.org/), debes instalar PHP como un [FPM SAPI](https://www.php.net/manual/es/install.fpm.php).\nUtiliza la siguiente configuración de Nginx, reemplazando `path/to/basic/web` con la ruta real a\n`basic/web` y `mysite.test` con el hostname real a servir.\n\n```\nserver {\n    charset utf-8;\n    client_max_body_size 128M;\n\n    listen 80; ## listen for ipv4\n    #listen [::]:80 default_server ipv6only=on; ## listen for ipv6\n\n    server_name mysite.test;\n    root        /path/to/basic/web;\n    index       index.php;\n\n    access_log  /path/to/basic/log/access.log;\n    error_log   /path/to/basic/log/error.log;\n\n    location / {\n        # Redireccionar a index.php todo lo que no sea un archivo real\n        try_files $uri $uri/ /index.php$is_args$args;\n    }\n\n    # descomentar para evitar el procesamiento de llamadas de Yii a archivos estáticos no existente\n    #location ~ \\.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ {\n    #    try_files $uri =404;\n    #}\n    #error_page 404 /404.html;\n\n    location ~ \\.php$ {\n        include fastcgi_params;\n        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;\n        fastcgi_pass   127.0.0.1:9000;\n        #fastcgi_pass unix:/var/run/php5-fpm.sock;\n        try_files $uri =404;\n    }\n\n    location ~ /\\.(ht|svn|git) {\n        deny all;\n    }\n}\n```\n\nAl utilizar esta configuración, también deberías definir `cgi.fix_pathinfo=0` en el archivo `php.ini`, y así\nevitar muchas llamadas innecesarias del sistema a `stat()`.\n\nTen en cuenta también que al correr un servidor HTTPS, deberás agregar `fastcgi_param HTTPS on;` así Yii puede\ndetectar propiamente si la conexión es segura.\n"
  },
  {
    "path": "docs/guide-es/start-looking-ahead.md",
    "content": "Mirando Hacia Adelante\n======================\n\nSi has leído el capítulo \"Comenzando con Yii\" completo, has creado una aplicación completa en Yii. En el proceso, has aprendido cómo implementar algunas\ncaracterísticas comúnmente necesitadas, tales como obtener datos del usuario a través de formularios HTML, traer datos desde la base de datos,\ny mostrar datos utilizando paginación. También has aprendido a utilizar [Gii](https://github.com/yiisoft/yii2-gii/blob/master/docs/guide/README.md) para generar\ncódigo automáticamente. Utilizar Gii para la generación de código transforma la carga en el proceso de tu desarrollo Web en una tarea tan simple como solamente completar unos formularios. \n\nEsta sección resumirá los recursos disponibles de Yii que te ayudarán a ser más productivo al utilizar el framework.\n\n* Documentación\n    - [La Guía Definitiva](https://www.yiiframework.com/doc-2.0/guide-README.html):\n      Como su nombre lo indica, la guía define precisamente cómo debería trabajar Yii y provee guías generales\n      acerca de su utilización. Es el tutorial más importante de Yii, y el que deberías leer \n      antes de escribir cualquier código en Yii.\n    - [La Referencia de Clases](https://www.yiiframework.com/doc-2.0/index.html):\n      Esta especifica el uso de cada clase provista por Yii. Debería ser utilizada principalmente cuando estás escribiendo\n      código y deseas entender el uso de una clase, método o propiedad en particular. El uso de la referencia de clases es mejor luego de un entendimiento contextual del framework.\n    - [Los Artículos de la Wiki](https://www.yiiframework.com/wiki/?tag=yii2):\n      Los artículos de la wiki son escritos por usuarios de Yii basados en sus propias experiencias. La mayoría de ellos están escritos\n      como recetas de cocina, y muestran cómo resolver problemas particulares utilizando Yii. Si bien la calidad de estos\n      puede no ser tan buena como la de la Guía Definitiva, son útiles ya que cubren un espectro muy amplio\n      de temas y puede proveer a menudo soluciones listas para usar.\n    - [Libros](https://www.yiiframework.com/books)\n* [Extensiones](https://www.yiiframework.com/extensions/):\n  Yii puede hacer alarde de una librería de miles de extensiones contribuidas por usuarios, que pueden fácilmente conectadas a tu aplicación, haciendo que el desarrollo de la misma sea todavía más fácil y rápido.\n* Comunidad\n    - Foro: <https://forum.yiiframework.com/>\n    - Chat IRC: El canal #yii en la red Libera (<ircs://irc.libera.chat:6697/yii>)\n    - Chat Gitter: <https://gitter.im/yiisoft/yii2>\n    - GitHub: <https://github.com/yiisoft/yii2>\n    - Facebook: <https://www.facebook.com/groups/yiitalk/>\n    - Twitter: <https://twitter.com/yiiframework>\n    - LinkedIn: <https://www.linkedin.com/groups/yii-framework-1483367>\n    - Stackoverflow: <https://stackoverflow.com/questions/tagged/yii2>\n"
  },
  {
    "path": "docs/guide-es/start-prerequisites.md",
    "content": "# Qué necesita saber\n\nLa curva de aprendizaje de Yii no es tan empinada como en otros _frameworks_ en PHP,\npero todavía hay algunas cosas que debería aprender antes de empezar con Yii.\n\n## PHP\n\nYii es un _framework_ (base estructurada de desarrollo) en PHP, así que asegúrese de\n[leer y comprender la referencia del lenguaje](https://www.php.net/manual/es/langref.php).\nAl desarrollar con Yii deberá escribir código de manera orientada a objetos, así que\nasegúrese de estar familiarizado con\n[clases y objetos](https://www.php.net/manual/es/language.oop5.basic.php) así como con\n[espacios de nombres](https://www.php.net/manual/es/language.namespaces.php).\n\n## Programación orientada a objetos\n\nSe requiere una comprensión básica de la programación orientada a objetos.  Si no está\nfamiliarizado con ella, diríjase a alguno de los muchos tutoriales disponibles, como\n[el de tuts+](https://code.tutsplus.com/tutorials/object-oriented-php-for-beginners--net-12762).\n\nObserve que cuanto más complicada sea su aplicación, más conceptos avanzados de la\nPOO deberá aprender para gestionar con éxito esa complejidad.\n\n## Línea de órdenes y composer\n\nYii usa profusamente el gestor de paquetes _de facto_ de PHP, [Composer](https://getcomposer.org/),\nasí que asegúrese de leer y comprender su [guía](https://getcomposer.org/doc/01-basic-usage.md).\nSi no está familiarizado con el uso de la línea de órdenes, es hora de empezar a probarla.\nUna vez que aprenda los fundamentos, nunca querrá trabajar sin ella.\n"
  },
  {
    "path": "docs/guide-es/start-workflow.md",
    "content": "Corriendo Aplicaciones\n======================\n\nDespués de haber instalado Yii, tienes una aplicación totalmente funcional a la que se puede acceder a través de\nla URL `https://hostname/basic/web/index.php` o `https://hostname/index.php`, dependiendo de tu configuración.\nEsta sección será una introducción a la funcionalidad incluida de la aplicación, cómo se organiza el código,\ny cómo la aplicación maneja los requests en general.\n\n> Info: Por simplicidad, en el transcurso de este tutorial \"Para Empezar\", se asume que has definido `basic/web`\n  como el document root de tu servidor Web, y configurado la URL de acceso a tu aplicación para que sea `https://hostname/index.php`\n  o similar.\n  Dependiendo de tus necesidades, por favor ajusta dichas URLs.\n  \nTen en cuenta que a diferencia del framework en sí, después de que el template de proyecto es instalado, este es todo tuyo. Eres libre de agregar o eliminar\ncódigo modificar todo según tu necesidad.\n\n\nFuncionalidad <span id=\"functionality\"></span>\n-------------\n\nLa aplicación básica contiene 4 páginas:\n\n* página principal, mostrada cuando se accede a la URL `https://hostname/index.php`,\n* página \"Acerca de (About)\",\n* la página \"Contacto (Contact)\", que muestra un formulario de contacto que permite a los usuarios finales contactarse vía email,\n* y la página \"Login\", que muestra un formulario para loguearse que puede usarse para autenticar usuarios. \n  Intenta loguearte con \"admin/admin\", y verás que el elemento \"Login\" del menú principal cambiará a \"Logout\".\n\nEstas páginas comparten un encabezado y un pie. El encabezado contiene una barra con el menú principal que permite\nla navegación entre las diferentes páginas.\n\nTambién deberías ver una barra en la parte inferior de la ventana del navegador.\nEsta es la útil [herramienta de depuración](tool-debugger.md) provista por Yii para registrar y mostrar mucha información de depuración, tal como los mensajes de log, response status, las consultas ejecutadas a la base de datos, y más.\n\nAdicionalmente a la aplicación web, hay un script de consola llamado `yii`, localizado en el directorio base de la aplicación.\nEl script puede ser utilizado para ejecutar tareas de fondo y tareas de mantenimiento de la aplicación, las cuales son descritas\nen la [Sección de Aplicación de Consola](tutorial-console.md).\n\n  \nEstructura de la aplicación <span id=\"application-structure\"></span>\n---------------------------\n\nLos archivos y directorios más importantes en tu aplicación son (asumiendo que la raíz de la aplicación es `basic`):\n\n```\nbasic/                  base path de la aplicación\n    composer.json       archivo utilizado por Composer, describe información de sus paquetes y librerías\n    config/             contiene la configuración de las aplicaciones (y otras)\n        console.php     configuración de la aplicación de consola\n        web.php         configuración de la aplicación web\n    commands/           contiene las clases de comandos de consola\n    controllers/        contiene las clases de los controladores\n    models/             contienes las clases del modelo\n    runtime/            contiene archivos generados por Yii en tiempo de ejecución, como archivos de log y cache\n    vendor/             contiene los paquetes y librerías instalados por Composer, incluyendo el propio núcleo de Yii\n    views/              contiene los archivos de vistas (templates)\n    web/                raíz web de la aplicación, contiene los archivos accesibles vía Web\n        assets/         contiene los assets publicados (javascript y css) por Yii\n        index.php       el script de entrada (o bootstrap) de la aplicación\n    yii                 el script de ejecución de los comandos de consola de Yii\n```\n\nEn general, los archivos de la aplicación pueden ser divididos en dos: aquellos bajo `basic/web` y aquellos bajo otros directorios.\nLos primeros pueden accederse directo por HTTP (ej., en un navegador), mientras que los últimos no pueden ni deben ser accedidos así.\n\nYii implementa el patrón de diseño [modelo-vista-controlador (MVC)](https://wikipedia.org/wiki/Model-view-controller),\nque es reflejado en la estructura de directorios utilizada. El directorio `models` contiene todas las [clases del modelo](structure-models.md),\nel directorio `views` contiene todas las [vistas (templates)](structure-views.md), y el directorio `controllers` contiene\ntodas las [clases de controladores](structure-controllers.md).\n\nEl siguiente diagrama muestra la estructura estática de una aplicación.\n\n![Estructura Estática de una Aplicación](images/application-structure.png)\n\nCada aplicación tiene un script de entrada `web/index.php` que es el único script PHP accesible vía web.\nEl script de entrada toma una petición (request) entrante y crea una instancia de una [aplicación](structure-applications.md) para manejarlo.\nLa [aplicación](structure-applications.md) resuelve la petición (request) con la ayuda de sus [componentes](concept-components.md),\ny la envía al resto de los elementos MVC. Los [widgets](structure-widgets.md) son usados en las [vistas](structure-views.md)\npara ayudar a construir elementos de interfaz complejos y dinámicos.\n\n\nCiclo de Vida de una Petición (Request) <span id=\"request-lifecycle\"></span>\n---------------------------------------\n\nEl siguiente diagrama muestra cómo una aplicación maneja una petición.\n\n![Ciclo de Vida de un Request](images/request-lifecycle.png)\n\n1. Un usuario realiza una petición al [script de entrada](structure-entry-scripts.md) `web/index.php`.\n2. El script de entrada carga la [configuración](concept-configurations.md) de la aplicación y crea\n   una instancia de la [aplicación](structure-applications.md) para manejar la consulta.\n3. La aplicación resuelve la [ruta](runtime-routing.md) solicitada con la ayuda del\n   componente [request](runtime-requests.md) de la aplicación.\n4. La aplicación crea una instancia de un [controlador](structure-controllers.md) para manejar la petición.\n5. El controlador crea una instancia de una [acción](structure-controllers.md) y ejecuta los filtros de dicha acción.\n6. Si alguno de los filtros falla, la acción es cancelada.\n7. Si todos los filtros pasan, la acción es ejecutada.\n8. La acción carga datos del modelo, posiblemente de la base de datos.\n9. La acción renderiza una vista, pasándole los datos del modelo cargado.\n10. El resultado de la renderización es pasado al componente [response](runtime-responses.md) de la aplicación.\n11. El componente response envía el resultado de la renderización al navegador del usuario.\n\n"
  },
  {
    "path": "docs/guide-es/structure-application-components.md",
    "content": "Componentes de la Aplicación\n============================\n\nLas aplicaciones son [service locators](concept-service-locators.md) (localizadores de servicios). Ellas albergan\nun grupo de los llamados *componentes de aplicación* que proveen diferentes servicios para procesar el `request` (petición).\nPor ejemplo, el componente `urlManager` es responsable por rutear Web `requests` (peticiones) a los controladores apropiados;\nel componente `db` provee servicios relacionados a base de datos; y así sucesivamente.\n\nCada componente de la aplicación tiene un ID que lo identifica de forma inequívoca de otros en la misma aplicación.\nPuedes acceder a un componente de la aplicación con la siguiente expresión:\n\n```php\n\\Yii::$app->ComponentID\n```\n\nPor ejemplo, puedes utilizar `\\Yii::$app->db` para obtener la [[yii\\db\\Connection|conexión a la base de datos]],\ny `\\Yii::$app->cache` para obtener el [[yii\\caching\\Cache|cache primario]] registrado con la aplicación.\n\nEstos componentes pueden ser cualquier objeto. Puedes registrarlos configurando la propiedad [[yii\\base\\Application::components]]\nen las [configuraciones de la aplicación](structure-applications.md#application-configurations).\nPor ejemplo:\n\n```php\n[\n    'components' => [\n        // registra el componente \"cache\" utilizando el nombre de clase\n        'cache' => 'yii\\caching\\ApcCache',\n\n        // registra el componente \"db\" utilizando un array de configuración\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=demo',\n            'username' => 'root',\n            'password' => '',\n        ],\n\n        // registra el componente \"search\" utilizando una función anónima\n        'search' => function () {\n            return new app\\components\\SolrService;\n        },\n    ],\n]\n```\n\n> Info: A pesar de que puedes registrar tantos componentes como desees, deberías hacerlo con criterio.\n  Los componente de la aplicación son como variables globales. Abusando demasiado de ellos puede resultar en\n  un código más difícil de mantener y testear. En muchos casos, puedes simplemente crear un componente local\n  y utilizarlo únicamente cuando sea necesario.\n\n\n## Componentes del Núcleo de la Aplicación <span id=\"core-application-components\"></span>\n\nYii define un grupo de componentes del *núcleo* con IDs fijos y configuraciones por defecto. Por ejemplo,\nel componente [[yii\\web\\Application::request|request]] es utilizado para recolectar información acerca\ndel `request` del usuario y resolverlo en una [ruta](runtime-routing.md); el componente [[yii\\base\\Application::db|db]]\nrepresenta una conexión a la base de datos a través del cual realizar consultas a la misma.\nEs con ayuda de estos componentes del núcleo que Yii puede manejar los `request` del usuario.\n\nA continuación, hay una lista de componentes predefinidos en el núcleo. Puedes configurarlos y personalizarlos\ncomo lo haces con componentes normales de la aplicación. Cuando configuras un componente del núcleo,\nsi no especificas su nombre de clase, la clase por defecto será utilizada.\n\n* [[yii\\web\\AssetManager|assetManager]]: maneja los `assets bundles` y su publicación.\n  Consulta la sección [Menajando Assets](output-assets.md) para más detalles.\n* [[yii\\db\\Connection|db]]: representa una conexión a la base de datos a través de la cual puedes realizar consultas a la misma.\n  Ten en cuenta que cuando configuras este componente, debes especificar el nombre de clase así como otras\n  propiedades requeridas por el mismo, como [[yii\\db\\Connection::dsn]].\n  Por favor consulta la sección [Data Access Objects](db-dao.md) para más detalles.\n* [[yii\\base\\Application::errorHandler|errorHandler]]: maneja errores y excepciones de PHP.\n  Por favor consulta la sección [Handling Errors](tutorial-handling-errors.md) para más detalles.\n* [[yii\\base\\Formatter|formatter]]: da formato a los datos cuando son mostrados a los usuarios. Por ejemplo, un número\n  puede ser mostrado usando un separador de miles, una fecha en una forma extensa.\n  Por favor consulta la sección [Formato de Datos](output-formatting.md) para más detalles.\n* [[yii\\i18n\\I18N|i18n]]: soporta traducción y formato de mensajes.\n  Por favor consulta la sección [Internacionalización](tutorial-i18n.md) para más detalles.\n* [[yii\\log\\Dispatcher|log]]: maneja a dónde dirigir los logs.\n  Por favor consulta la sección [Logging](tutorial-logging.md) para más detalles.\n* [[yii\\swiftmailer\\Mailer|mail]]: soporta construcción y envío de emails.\n  Por favor consulta la sección [Enviando Emails](tutorial-mailing.md) para más detalles.\n* [[yii\\base\\Application::response|response]]: representa la respuesta enviada a los usuarios.\n  Por favor consulta la sección [Responses](runtime-responses.md) para más detalles.\n* [[yii\\base\\Application::request|request]]: representa el `request` recibido de los usuarios.\n  Por favor consulta la sección [Requests](runtime-requests.md) para más detalles.\n* [[yii\\web\\Session|session]]: representa la información de sesión. Este componente sólo está disponible\n  en [[yii\\web\\Application|alpicaciones Web]].\n  Por favor consulta la sección [Sessions and Cookies](runtime-sessions-cookies.md) para más detalles.\n* [[yii\\web\\UrlManager|urlManager]]: soporta el parseo y generación de URLs.\n  Por favor consulta la sección [URL Parsing and Generation](runtime-url-handling.md) para más detalles.\n* [[yii\\web\\User|user]]: representa la información e autenticación del usuario. Este componente sólo está disponible\n  en [[yii\\web\\Application|aplicaciones Web]]\n  Por favor consulta la sección [Autenticación](security-authentication.md) para más detalles.\n* [[yii\\web\\View|view]]: soporta el renderizado de las vistas.\n  Por favor consulta la sección [Vistas](structure-views.md) para más detalles.\n"
  },
  {
    "path": "docs/guide-es/structure-applications.md",
    "content": "Aplicaciones\n============\n\nLas `Applications` (aplicaciones) son objetos que gobiernan la estructura total y el ciclo de vida de las aplicaciones\nhechas en Yii.\nCada aplicación Yii contiene un objeto `Application` que es creado en el [script de entrada](structure-entry-scripts.md)\ny es globalmente accesible a través de la expresión `\\Yii::$app`.\n\n> Info: Dependiendo del contexto, cuando decimos \"una aplicación\", puede significar tanto un objeto Application\n  o un sistema desarrollado en Yii.\n\nHay dos tipos de aplicaciones: [[yii\\web\\Application|aplicaciones Web]] y\n[[yii\\console\\Application|aplicaciones de consola]]. Como el nombre lo indica, la primera maneja principalmente\nWeb requests mientras que la última maneja requests (peticiones) de la línea de comandos.\n\n\n## Configuraciones de las Aplicaciones <span id=\"application-configurations\"></span>\n\nCuando un [script de entrada](structure-entry-scripts.md) crea una aplicación, cargará\nuna [configuración](concept-configurations.md) y la aplicará a la aplicación, como se muestra a continuación:\n\n```php\nrequire __DIR__ . '/../vendor/autoload.php';\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n\n// carga la configuración de la aplicación\n$config = require __DIR__ . '/../config/web.php';\n\n// instancia y configura la aplicación\n(new yii\\web\\Application($config))->run();\n```\n\nPrincipalmente, las [configuraciones](concept-configurations.md) de una aplicación especifican\ncomo inicializar las propiedades de un objeto `application`. Debido a que estas configuraciones\nsuelen ser complejas, son usualmente guardadas en [archivos de configuración](concept-configurations.md#configuration-files),\ncomo en el archivo `web.php` del ejemplo anterior.\n\n\n## Propiedades de la Aplicación <span id=\"application-properties\"></span>\n\nHay muchas propiedades importantes en la aplicación que deberían configurarse en en la configuración de la aplicación.\nEstas propiedades suelen describir el entorno en el cual la aplicación está corriendo.\nPor ejemplo, las aplicaciones necesitan saber cómo cargar [controladores](structure-controllers.md),\ndónde guardar archivos temporales, etc. A continuación, resumiremos esas propiedades.\n\n\n### Propiedades Requeridas <span id=\"required-properties\"></span>\n\nEn cualquier aplicación, debes configurar al menos dos propiedades: [[yii\\base\\Application::id|id]]\ny [[yii\\base\\Application::basePath|basePath]].\n\n\n#### [[yii\\base\\Application::id|id]] <span id=\"id\"></span>\n\nLa propiedad [[yii\\base\\Application::id|id]] especifica un ID único que diferencia una aplicación de otras.\nEs mayormente utilizada a nivel programación. A pesar de que no es un requerimiento, para una mejor interoperabilidad,\nse recomienda utilizar sólo caracteres alfanuméricos.\n\n\n#### [[yii\\base\\Application::basePath|basePath]] <span id=\"basePath\"></span>\n\nLa propiedad [[yii\\base\\Application::basePath|basePath]] especifica el directorio raíz de una aplicación.\nEs el directorio que alberga todos los archivos protegidos de un sistema. Bajo este directorio,\ntendrás normalmente sub-directorios como `models`, `views`, `controllers`, que contienen el código fuente\ncorrespondiente al patrón MVC.\n\nPuedes configurar la propiedad [[yii\\base\\Application::basePath|basePath]] usando la ruta a un directorio\no un [alias](concept-aliases.md). En ambas formas, el directorio debe existir, o se lanzará una excepción.\nLa ruta será normalizada utilizando la función `realpath()`.\n\nLa propiedad [[yii\\base\\Application::basePath|basePath]] es utilizada a menudo derivando otras rutas\n(ej. la ruta `runtime`). Por esta razón, un alias llamado `@app` está predefinido para representar esta ruta.\nRutas derivadas pueden ser entonces creadas a partir de este alias (ej. `@app/runtime` para referirse al directorio `runtime`).\n\n\n### Propiedades Importantes <span id=\"important-properties\"></span>\n\nLas propiedades descritas en esta subsección a menudo necesita ser configurada porque difieren entre las\ndiferentes aplicaciones.\n\n\n#### [[yii\\base\\Application::aliases|aliases]] <span id=\"aliases\"></span>\n\nEsta propiedad te permite definir un grupo de [alias](concept-aliases.md) en términos de un array (matriz).\nLas claves del array son los nombres de los alias, y los valores su correspondiente definición.\nPor ejemplo:\n\n```php\n[\n    'aliases' => [\n        '@name1' => 'path/to/path1',\n        '@name2' => 'path/to/path2',\n    ],\n]\n```\n\nEsta propiedad está provista de tal manera que puedas definir alias en términos de configuraciones de la aplicación\nen vez de llamadas al método [[Yii::setAlias()]].\n\n\n#### [[yii\\base\\Application::bootstrap|bootstrap]] <span id=\"bootstrap\"></span>\n\nEsta es una propiedad importante. Te permite definir un array de los componentes que deben ejecutarse\ndurante el [[yii\\base\\Application::bootstrap()|proceso de `bootstrapping`]] de la aplicación.\nPor ejemplo, si quieres personalizar las [reglas de URL](runtime-url-handling.md) de un [módulo](structure-modules.md),\npodrías listar su ID como un elemento de este array.\n\nCada componente listado en esta propiedad puede ser especificado en cualquiera de los siguientes formatos:\n\n- el ID de un componente como está especificado vía [`components`](#components).\n- el ID de un módulo como está especificado vía [`modules`](#modules).\n- un nombre de clase.\n- un array de configuración.\n\nPor ejemplo:\n\n```php\n[\n    'bootstrap' => [\n        // un ID de componente o de módulo\n        'demo',\n\n        // un nombre de clase\n        'app\\components\\TrafficMonitor',\n\n        // un array de configuración\n        [\n            'class' => 'app\\components\\Profiler',\n            'level' => 3,\n        ]\n    ],\n]\n```\n\nDurante el proceso de `bootstrapping`, cada componente será instanciado. Si la clase del componente\nimplementa [[yii\\base\\BootstrapInterface]], también se llamará a su método [[yii\\base\\BootstrapInterface::bootstrap()|bootstrap()]].\n\nOtro ejemplo práctico se encuentra en la configuración del [Template de Aplicación Básica](start-installation.md),\ndonde los módulos `debug` y `gii` son configurados como componentes `bootstrap` cuando la aplicación está\ncorriendo en un entorno de desarrollo,\n\n```php\nif (YII_ENV_DEV) {\n    // ajustes en la configuración del entorno 'dev' (desarrollo)\n    $config['bootstrap'][] = 'debug';\n    $config['modules']['debug'] = 'yii\\debug\\Module';\n\n    $config['bootstrap'][] = 'gii';\n    $config['modules']['gii'] = 'yii\\gii\\Module';\n}\n```\n\n> Note: Agregar demasiados componentes `bootstrap` degradará la performance de tu aplicación debido a que\n  por cada request, se necesita correr el mismo grupo de componentes. Por lo tanto, utiliza componentes `bootstrap` con criterio.\n\n\n#### [[yii\\web\\Application::catchAll|catchAll]] <span id=\"catchAll\"></span>\n\nEsta propiedad está solamente soportada por [[yii\\web\\Application|aplicaciones Web]]. Especifica\nla [acción de controlador](structure-controllers.md) que debería manejar todos los requests (peticiones) del usuario.\nEs mayormente utilizada cuando una aplicación está en \"modo de mantenimiento\" y necesita que todas las peticiones\nsean capturadas por una sola acción.\n\nLa configuración es un array cuyo primer elemento especifica la ruta de la acción.\nEl resto de los elementos del array (pares clave-valor) especifica los parámetros a ser enviados a la acción.\nPor ejemplo:\n\n```php\n[\n    'catchAll' => [\n        'offline/notice',\n        'param1' => 'value1',\n        'param2' => 'value2',\n    ],\n]\n```\n\n\n#### [[yii\\base\\Application::components|components]] <span id=\"components\"></span>\n\nEsta es la propiedad más importante. Te permite registrar una lista de componentes llamados [componentes de aplicación](#structure-application-components.md)\nque puedes utilizar en otras partes de tu aplicación. Por ejemplo:\n\n```php\n[\n    'components' => [\n        'cache' => [\n            'class' => 'yii\\caching\\FileCache',\n        ],\n        'user' => [\n            'identityClass' => 'app\\models\\User',\n            'enableAutoLogin' => true,\n        ],\n    ],\n]\n```\n\nCada componente de la aplicación es un par clave-valor del array. La clave representa el ID del componente,\nmientras que el valor representa el nombre de la clase del componente o una [configuración](concept-configurations.md).\n\nPuedes registrar cualquier componente en una aplicación, y el componente puede ser globalmente accedido utilizando\nla expresión `\\Yii::$app->ComponentID`.\n\nPor favor, lee la sección [Componentes de la Aplicación](structure-application-components.md) para mayor detalle.\n\n\n#### [[yii\\base\\Application::controllerMap|controllerMap]] <span id=\"controllerMap\"></span>\n\nEsta propiedad te permite mapear un ID de controlador a una clase de controlador arbitraria. Por defecto, Yii mapea\nID de controladores a clases de controladores basado en una [convención](#controllerNamespace) (ej. el ID `post` será mapeado\na `app\\controllers\\PostController`). Configurando esta propiedad, puedes saltear esa convención\npara controladores específicos. En el siguiente ejemplo, `account` será mapeado a\n`app\\controllers\\UserController`, mientras que `article` será mapeado a `app\\controllers\\PostController`.\n\n```php\n[\n    'controllerMap' => [\n        'account' => 'app\\controllers\\UserController',\n        'article' => [\n            'class' => 'app\\controllers\\PostController',\n            'enableCsrfValidation' => false,\n        ],\n    ],\n]\n```\n\nLas claves de este array representan los ID de los controladores, mientras que los valores representan\nlos nombres de clase de dichos controladores o una [configuración](concept-configurations.md).\n\n\n#### [[yii\\base\\Application::controllerNamespace|controllerNamespace]] <span id=\"controllerNamespace\"></span>\n\nEsta propiedad especifica el `namespace` bajo el cual las clases de los controladores deben ser ubicados. Por defecto es\n`app\\controllers`. Si el ID es `post`, por convención el controlador correspondiente (sin\n`namespace`) será `PostController`, y el nombre completo (cualificado) de la clase `app\\controllers\\PostController`.\n\nLas clases de controladores pueden ser ubicados también en sub-directorios del directorio correspondiente a este `namespace`.\nPor ejemplo, dado el ID de controlador `admin/post`, el nombre completo de la clase sería `app\\controllers\\admin\\PostController`.\n\nEs importante que el nombre completo de la clase del controlador sea [auto-cargable](concept-autoloading.md)\ny el `namespace` actual de la clase coincida con este valor. De otro modo, recibirás\nun error \"Page Not Found\" (\"Página no Encontrada\") cuando accedas a la aplicación.\n\nEn caso de que quieras romper con la convención cómo se comenta arriba, puedes configurar la propiedad [controllerMap](#controllerMap).\n\n\n#### [[yii\\base\\Application::language|language]] <span id=\"language\"></span>\n\nEsta propiedad especifica el idioma en el cual la aplicación debería mostrar el contenido a los usuarios.\nEl valor por defecto de esta propiedad es `en`, referido a English. Deberías configurar esta propiedad\nsi tu aplicación necesita soporte multi-idioma.\n\nEl valor de esta propiedad determina varios aspectos de la [internacionalización](tutorial-i18n.md),\nincluido la traducción de mensajes, formato de fecha y números, etc. Por ejemplo, el widget [[yii\\jui\\DatePicker]]\nutilizará el valor de esta propiedad para determinar en qué idioma el calendario debe ser mostrado y cómo dar formato\na la fecha.\n\nSe recomienda que especifiques el idioma en términos de una [Código de idioma IETF](https://es.wikipedia.org/wiki/Código_de_idioma_IETF).\nPor ejemplo, `en` se refiere a English, mientras que `en-US` se refiere a English (United States).\n\nSe pueden encontrar más detalles de este aspecto en la sección [Internacionalización](tutorial-i18n.md).\n\n\n#### [[yii\\base\\Application::modules|modules]] <span id=\"modules\"></span>\n\nEsta propiedad especifica los [módulos](structure-modules.md) que contiene la aplicación.\n\nEsta propiedad toma un array con los nombre de clases de los módulos o [configuraciones](concept-configurations.md) con las claves siendo\nlos IDs de los módulos. Por ejemplo:\n\n```php\n[\n    'modules' => [\n        // módulo \"booking\" especificado con la clase del módulo\n        'booking' => 'app\\modules\\booking\\BookingModule',\n\n        // módulo \"comment\" especificado usando un array de configuración\n        'comment' => [\n            'class' => 'app\\modules\\comment\\CommentModule',\n            'db' => 'db',\n        ],\n    ],\n]\n```\n\nPor favor consulta la sección [Módulos](structure-modules.md) para más detalles.\n\n\n#### [[yii\\base\\Application::name|name]] <span id=\"name\"></span>\n\nEsta propiedad especifica el nombre de la aplicación que será mostrado a los usuarios. Al contrario de\n[[yii\\base\\Application::id|id]], que debe tomar un valor único, el valor de esta propiedad existe principalmente\npara propósito de visualización y no tiene porqué ser única.\n\nNo siempre necesitas configurar esta propiedad si en tu aplicación no va a ser utilizada.\n\n\n#### [[yii\\base\\Application::params|params]] <span id=\"params\"></span>\n\nEsta propiedad especifica un array con parámetros accesibles desde cualquier lugar de tu aplicación.\nEn vez de usar números y cadenas fijas por todos lados en tu código, es una buena práctica definirlos como\nparámetros de la aplicación en un solo lugar y luego utilizarlos donde los necesites. Por ejemplo, podrías definir el tamaño\nde las imágenes en miniatura de la siguiente manera:\n\n```php\n[\n    'params' => [\n        'thumbnail.size' => [128, 128],\n    ],\n]\n```\n\nEntonces, cuando necesites acceder a esa configuración en tu aplicación, podrías hacerlo utilizando el código siguiente:\n\n```php\n$size = \\Yii::$app->params['thumbnail.size'];\n$width = \\Yii::$app->params['thumbnail.size'][0];\n```\n\nMás adelante, si decides cambiar el tamaño de las miniaturas, sólo necesitas modificarlo en la configuración de la aplicación\nsin necesidad de tocar el código que lo utiliza.\n\n\n#### [[yii\\base\\Application::sourceLanguage|sourceLanguage]] <span id=\"sourceLanguage\"></span>\n\nEsta propiedad especifica el idioma en el cual la aplicación está escrita. El valor por defecto es `'en-US'`,\nreferido a English (United States). Deberías configurar esta propiedad si el contenido de texto en tu código no está en inglés.\n\nComo la propiedad [language](#language), deberías configurar esta propiedad siguiendo el [Código de idioma IETF](https://es.wikipedia.org/wiki/Código_de_idioma_IETF).\nPor ejemplo, `en` se refiere a English, mientras que `en-US` se refiere a English (United States).\n\nPuedes encontrar más detalles de esta propiedad en la sección [Internacionalización](tutorial-i18n.md).\n\n\n#### [[yii\\base\\Application::timeZone|timeZone]] <span id=\"timeZone\"></span>\n\nEsta propiedad es provista como una forma alternativa de definir el `time zone` de PHP por defecto en tiempo de ejecución.\nConfigurando esta propiedad, escencialmente estás llamando a la función de PHP [date_default_timezone_set()](https://www.php.net/manual/es/function.date-default-timezone-set.php).\nPor ejemplo:\n\n```php\n[\n    'timeZone' => 'America/Los_Angeles',\n]\n```\n\n\n#### [[yii\\base\\Application::version|version]] <span id=\"version\"></span>\n\nEsta propiedad especifica la versión de la aplicación. Es por defecto `'1.0'`. No hay total necesidad de configurarla\nsi tu no la usarás en tu código.\n\n\n### Propiedades Útiles <span id=\"useful-properties\"></span>\n\nLas propiedades especificadas en esta sub-sección no son configuradas normalmente ya que sus valores por defecto\nestipulan convenciones comunes. De cualquier modo, aún puedes configurarlas en caso de que quieras romper con la convención.\n\n\n#### [[yii\\base\\Application::charset|charset]] <span id=\"charset\"></span>\n\nEsta propiedad especifica el `charset` que la aplicación utiliza. El valor por defecto es `'UTF-8'`, que debería ser mantenido\ntal cual para la mayoría de las aplicaciones a menos que estés trabajando con sistemas legados que utilizan muchos datos no-unicode.\n\n\n#### [[yii\\base\\Application::defaultRoute|defaultRoute]] <span id=\"defaultRoute\"></span>\n\nEsta propiedad especifica la [ruta](runtime-routing.md) que una aplicación debería utilizar si el `request`\nno especifica una. La ruta puede consistir el ID de un sub-módulo, el ID de un controlador, y/o el ID de una acción.\nPor ejemplo, `help`, `post/create`, `admin/post/create`. Si el ID de la acción no se especifica, tomará el valor por defecto\nespecificado en [[yii\\base\\Controller::defaultAction]].\n\nPara [[yii\\web\\Application|aplicaciones Web]], el valor por defecto de esta propiedad es `'site'`, lo que significa que el\ncontrolador `SiteController` y su acción por defecto serán usados. Como resultado, si accedes a la aplicación sin\nespecificar una ruta, mostrará el resultado de `app\\controllers\\SiteController::actionIndex()`.\n\nPara [[yii\\console\\Application|aplicaciones de consola]], el valor por defecto es `'help'`, lo que significa que el comando\n[[yii\\console\\controllers\\HelpController::actionIndex()]] debería ser utilizado. Como resultado, si corres el comando `yii`\nsin proveer ningún argumento, mostrará la información de ayuda.\n\n\n#### [[yii\\base\\Application::extensions|extensions]] <span id=\"extensions\"></span>\n\nEsta propiedad especifica la lista de [extensiones](structure-extensions.md) que se encuentran instaladas y son utilizadas\npor la aplicación.\nPor defecto, tomará el array devuelto por el archivo `@vendor/yiisoft/extensions.php`. El archivo `extensions.php`\nes generado y mantenido automáticamente cuando utilizas [Composer](https://getcomposer.org) para instalar extensiones.\nPor lo tanto, en la mayoría de los casos no necesitas configurarla.\n\nEn el caso especial de que quieras mantener las extensiones a mano, puedes configurar la propiedad como se muestra a continuación:\n\n```php\n[\n    'extensions' => [\n        [\n            'name' => 'nombre de la extensión',\n            'version' => 'número de versión',\n            'bootstrap' => 'BootstrapClassName',  // opcional, puede ser también un array de configuración\n            'alias' => [  // opcional\n                '@alias1' => 'to/path1',\n                '@alias2' => 'to/path2',\n            ],\n        ],\n\n        // ... más extensiones como las de arriba ...\n\n    ],\n]\n```\n\nComo puedes ver, la propiedad toma un array de especificaciones de extensiones. Cada extensión es especificada mediante un array\nque consiste en los elementos `name` y `version`. Si una extensión necesita ser ejecutada durante el proceso de [`bootstrap`](runtime-bootstrapping.md),\nun elemento `bootstrap` puede ser especificado con un nombre de clase o un array de [configuración](concept-configurations.md).\nUna extensión también puede definir algunos [alias](concept-aliases.md).\n\n\n#### [[yii\\base\\Application::layout|layout]] <span id=\"layout\"></span>\n\nEsta propiedad especifica el valor del `layout` por defecto que será utilizado al renderizar una [vista](structure-views.md).\nEl valor por defecto es `'main'`, y se refiere al archivo `main.php` bajo el [`layout path`](#layoutPath) definido.\nSi tanto el [`layout path`](#layoutPath) y el [`view path`](#viewPath) están utilizando los valores por defecto,\nel archivo `layout` puede ser representado con el alias `@app/views/layouts/main.php`.\n\nPuedes configurar esta propiedad con el valor `false` si quieres desactivar el `layout` por defecto, aunque esto sería un\ncaso muy raro.\n\n\n#### [[yii\\base\\Application::layoutPath|layoutPath]] <span id=\"layoutPath\"></span>\n\nEsta propiedad especifica el lugar por defecto donde deben buscarse los archivos `layout`. El valor por defecto\nes el sub-directorio `layouts` bajo el [`view path`](#viewPath). Si el [`view path`](#viewPath) usa su valor por defecto,\nel `layout path` puede ser representado con el alias `@app/views/layouts`.\n\nPuedes configurarlo como un directorio o utilizar un [alias](concept-aliases.md).\n\n\n#### [[yii\\base\\Application::runtimePath|runtimePath]] <span id=\"runtimePath\"></span>\n\nEsta propiedad especifica dónde serán guardados los archivos temporales, como archivos de log y de cache, pueden ser generados.\nEl valor por defecto de esta propiedad es el alias `@app/runtime`.\n\nPuedes configurarlo como un directorio o utilizar un [alias](concept-aliases.md). Ten en cuenta que el\ndirectorio debe tener permisos de escritura por el proceso que corre la aplicación. También este directorio debe estar protegido\nde ser accedido por usuarios finales, ya que los archivos generados pueden tener información sensible.\n\nPara simplificar el acceso a este directorio, Yii trae predefinido el alias `@runtime` para él.\n\n\n#### [[yii\\base\\Application::viewPath|viewPath]] <span id=\"viewPath\"></span>\n\nEsta propiedad especifica dónde están ubicados los archivos de la vista. El valor por defecto de esta propiedad está\nrepresentado por el alias `@app/views`. Puedes configurarlo como un directorio o utilizar un [alias](concept-aliases.md).\n\n\n#### [[yii\\base\\Application::vendorPath|vendorPath]] <span id=\"vendorPath\"></span>\n\nEsta propiedad especifica el directorio `vendor` que maneja [Composer](https://getcomposer.org). Contiene\ntodas las librerías de terceros utilizadas por tu aplicación, incluyendo el núcleo de Yii. Su valor por defecto\nestá representado por el alias `@app/vendor`.\n\nPuedes configurarlo como un directorio o utilizar un [alias](concept-aliases.md). Cuando modificas esta propiedad,\nasegúrate de ajustar la configuración de Composer en concordancia.\n\nPara simplificar el acceso a esta ruta, Yii trae predefinido el alias `@vendor`.\n\n\n#### [[yii\\console\\Application::enableCoreCommands|enableCoreCommands]] <span id=\"enableCoreCommands\"></span>\n\nEsta propiedad está sólo soportada por [[yii\\console\\Application|aplicaciones de consola]].\nEspecifica si los comandos de consola incluidos en Yii deberían estar habilitados o no.\nPor defecto está definido como `true`.\n\n\n## Eventos de la Aplicación <span id=\"application-events\"></span>\n\nUna aplicación dispara varios eventos durante su ciclo de vida al manejar un `request`. Puedes conectar\nmanejadores a dichos eventos en la configuración de la aplicación como se muestra a continuación:\n\n```php\n[\n    'on beforeRequest' => function ($event) {\n        // ...\n    },\n]\n```\n\nEl uso de la sintáxis `on nombreEvento` es descrita en la sección [Configuraciones](concept-configurations.md#configuration-format).\n\nAlternativamente, puedes conectar manejadores de eventos durante el [`proceso de bootstrapping`](runtime-bootstrapping.md)\ndespués de que la instancia de la aplicación es creada. Por ejemplo:\n\n```php\n\\Yii::$app->on(\\yii\\base\\Application::EVENT_BEFORE_REQUEST, function ($event) {\n    // ...\n});\n```\n\n### [[yii\\base\\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_REQUEST]] <span id=\"beforeRequest\"></span>\n\nEste evento es disparado *before* (antes) de que la aplicación maneje el `request`. El nombre del evento es `beforeRequest`.\n\nCuando este evento es disparado, la instancia de la aplicación ha sido configurada e inicializada. Por lo tanto es un\nbuen lugar para insertar código personalizado vía el mecanismo de eventos para interceptar dicho manejo del `request`.\nPor ejemplo, en el manejador del evento, podrías definir dinámicamente la propiedad [[yii\\base\\Application::language]]\nbasada en algunos parámetros.\n\n\n### [[yii\\base\\Application::EVENT_BEFORE_REQUEST|EVENT_AFTER_REQUEST]] <span id=\"afterRequest\"></span>\n\nEste evento es disparado *after* (después) de que una aplicación finaliza el manejo de un `request` pero *before* (antes) de enviar el `response` (respuesta).\nEl nombre del evento es `afterRequest`.\n\nCuando este evento es disparado, el manejo del `request` está finalizado y puedes aprovechar para realizar algún\npost-proceso del mismo o personalizar el `response` (respuesta).\n\nTen en cuenta que el componente [[yii\\web\\Response|response]] también dispara algunos eventos mientras está enviando el contenido\na los usuarios finales. Estos eventos son disparados *after* (después) de este evento.\n\n\n### [[yii\\base\\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_ACTION]] <span id=\"beforeAction\"></span>\n\nEste evento es disparado *before* (antes) de ejecutar cualquier [acción de controlador](structure-controllers.md).\nEl nombre de este evento es `beforeAction`.\n\nEl parámetro evento es una instancia de [[yii\\base\\ActionEvent]]. Un manejador de eventos puede definir\nla propiedad [[yii\\base\\ActionEvent::isValid]] como `false` para detener la ejecución de una acción.\nPor ejemplo:\n\n```php\n[\n    'on beforeAction' => function ($event) {\n        if (..alguna condición..) {\n            $event->isValid = false;\n        } else {\n        }\n    },\n]\n```\n\nTen en cuenta que el mismo evento `beforeAction` también es disparado por [módulos](structure-modules.md)\ny [controladores)(structure-controllers.md). Los objectos aplicación son los primeros en disparar este evento,\nseguidos por módulos (si los hubiera), y finalmente controladores. Si un manejador de eventos define [[yii\\base\\ActionEvent::isValid]]\ncomo `false`, todos los eventos siguientes NO serán disparados.\n\n\n### [[yii\\base\\Application::EVENT_BEFORE_REQUEST|EVENT_AFTER_ACTION]] <span id=\"afterAction\"></span>\n\nEste evento es disparado *after* (después) de ejecutar cualquier [acción de controlador](structure-controllers.md).\nEl nombre de este evento es `afterAction`.\n\nEl parámetro evento es una instancia de [[yii\\base\\ActionEvent]]. A través de la\npropiedad [[yii\\base\\ActionEvent::result]], un manejador de eventos puede acceder o modificar el resultado de una acción.\nPor ejemplo:\n\n```php\n[\n    'on afterAction' => function ($event) {\n        if (..alguna condición...) {\n            // modificar $event->result\n        } else {\n        }\n    },\n]\n```\n\nTen en cuenta que el mismo evento `afterAction` también es disparado por [módulo](structure-modules.md)\ny [controladores)(structure-controllers.md). Estos objetos disparan el evento en orden inverso\nque los de `beforeAction`. Esto quiere decir que los controladores son los primeros en dispararlo,\nseguido por módulos (si los hubiera), y finalmente aplicaciones.\n\n\n## Ciclo de Vida de una Aplicación <span id=\"application-lifecycle\"></span>\n\nCuando un [script de entrada](structure-entry-scripts.md) está siendo ejecutado para manejar un `request`,\nuna aplicación experimenta el siguiente ciclo de vida:\n\n1. El script de entrada carga el array de configuración de la aplicación.\n2. El script de entrada crea una nueva instancia de la aplicación:\n  * Se llama a [[yii\\base\\Application::preInit()|preInit()]], que configura algunas propiedades\n    de alta prioridad de la aplicación, como [[yii\\base\\Application::basePath|basePath]].\n  * Registra el [[yii\\base\\Application::errorHandler|manejador de errores]].\n  * Configura las propiedades de la aplicación.\n  * Se llama a [[yii\\base\\Application::init()|init()]] con la subsiguiente llamada a\n    [[yii\\base\\Application::bootstrap()|bootstrap()]] para correr componentes `bootstrap`.\n3. El script de entrada llama a [[yii\\base\\Application::run()]] para correr la aplicación:\n  * Dispara el evento [[yii\\base\\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_REQUEST]].\n  * Maneja el `request`: lo resuelve en una [route (ruta)](runtime-routing.md) con los parámetros asociados;\n    crea el módulo, controlador y objetos acción como se especifica en dicha ruta; y entonces ejecuta la acción.\n  * Dispara el evento [[yii\\base\\Application::EVENT_AFTER_REQUEST|EVENT_AFTER_REQUEST]].\n  * Envía el `response` (respuesta) al usuario.\n4. El script de entrada recibe el estado de salida de la aplicación y completa el proceso del `request`.\n"
  },
  {
    "path": "docs/guide-es/structure-assets.md",
    "content": "Assets\n======\n\nUn asset en Yii es un archivo al que se puede hacer referencia en una página Web. Puede ser un archivo CSS, un archivo\nJavaScript, una imagen o un archivo de video, etc. Los assets se encuentran en los directorios públicos de la web y se\nsirven directamente por los servidores Web.\n\nA menudo es preferible gestionar los assets mediante programación. Por ejemplo, cuando se usa el widget\n[[yii\\jui\\DatePicker]] en una página, éste incluirá automáticamente los archivos CSS y JavaScript requeridos, en vez\nde tener que buscar los archivos e incluirlos manualmente. Y cuando se actualice el widget a una nueva versión, ésta\nusará de forma automática la nueva versión de los archivos asset.\nEn este tutorial, se describirá la poderosa capacidad que proporciona la gestión de assets en Yii.\n\n## Asset Bundles <span id=\"asset-bundles\"></span>\n\nYii gestiona los assets en unidades de *asset bundle*. Un asset bundle es simplemente un conjunto de assets\nlocalizados en un directorio. Cuando se registra un asset bundle en una [vista](structure-views.md), éste incluirá los\narchivos CSS y JavaScript del bundle en la página Web renderizada.\n\n## Definición de Asset Bundles <span id=\"defining-asset-bundles\"></span>\n\nLos asset bundles son descritos como clases PHP que extienden a [[yii\\web\\AssetBundle]]. El nombre del bundle es\nsimplemente su correspondiente nombre de la classe PHP que debe ser [autocargable](concept-autoloading.md). En una\nclase asset bundle, lo más habitual es especificar donde se encuentran los archivos asset, que archivos CSS y\nJavaScript contiene el bundle, y como depende este bundle de otros bundles.\n\nEl siguiente código define el asset bundle principal que se usa en\n[la plantilla de aplicación básica](start-installation.md):\n\n```php\n<?php\n\nnamespace app\\assets;\n\nuse yii\\web\\AssetBundle;\n\nclass AppAsset extends AssetBundle\n{\n    public $basePath = '@webroot';\n    public $baseUrl = '@web';\n    public $css = [\n        'css/site.css',\n    ];\n    public $js = [\n    ];\n    public $depends = [\n        'yii\\web\\YiiAsset',\n        'yii\\bootstrap\\BootstrapAsset',\n    ];\n}\n```\n\nLa anterior clase `AppAsset` especifica que los archivos asset se encuentran en el directorio `@webroot` que\ncorresponde a la URL `@web`; el bundle contiene un único archivo CSS `css/site.css` y ningún archivo JavaScript;\nel bundle depende de otros dos bundles: [[yii\\web\\YiiAsset]] y [[yii\\bootstrap\\BootstrapAsset]].\nA continuación se explicarán más detalladamente las propiedades del [[yii\\web\\AssetBundle]]:\n\n* [[yii\\web\\AssetBundle::sourcePath|sourcePath]]: especifica el directorio raíz que contiene los archivos asset en el\n  bundle.  Si no, se deben especificar las propiedades [[yii\\web\\AssetBundle::basePath|basePath]] y\n  [[yii\\web\\AssetBundle::baseUrl|baseUrl]], en su lugar. Se pueden usar [alias de ruta](concept-aliases.md).\n* [[yii\\web\\AssetBundle::basePath|basePath]]: especifica el directorio Web público que contiene los archivos assets de\n  este bundle. Cuando se especifica la propiedad [[yii\\web\\AssetBundle::sourcePath|sourcePath]], el [gestor de\n  assets](#asset-manager) publicará los assets de este bundle en un directorio  Web público  y sobrescribirá la\n  propiedad en consecuencia. Se debe establecer esta propiedad si los archivos asset ya se encuentran en un directorio\n  Web público y no necesitan ser publicados. Se pueden usar [alias de ruta](concept-aliases.md).\n* [[yii\\web\\AssetBundle::baseUrl|baseUrl]]: especifica la URL correspondiente al directorio\n  [[yii\\web\\AssetBundle::basePath|basePath]]. Como en [[yii\\web\\AssetBundle::basePath|basePath]], si se especifica la\n  propiedad [[yii\\web\\AssetBundle::sourcePath|sourcePath]], el [gestor de assets](#asset-manager) publicara los assets\n  y sobrescribirá esta propiedad en consecuencia. Se pueden usar [alias de ruta](concept-aliases.md).\n* [[yii\\web\\AssetBundle::js|js]]: un array lista los archivos JavaScript que contiene este bundle. Tenga en cuenta que\n  solo deben usarse las barras invertidas \"/\" como separadores de directorios. Cada archivo Javascript se puede\n  especificar en uno de los siguientes formatos:\n    - una ruta relativa que represente un archivo local JavaScript (ej. `js/main.js`). La ruta actual del fichero\n      se puede determinar anteponiendo [[yii\\web\\AssetManager::basePath]] a la ruta relativa, y la URL actual de un\n      archivo puede ser determinada anteponiendo [[yii\\web\\AssetManager::baseUrl]] a la ruta relativa.\n    - una URL absoluta que represente un archivo JavaScript externo. Por ejemplo,\n    `https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js` o\n    `//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js`.\n* [[yii\\web\\AssetBundle::css|css]]: un array que lista los archivos CSS que contiene este bundle. El formato de este\n  array es el mismo que el de [[yii\\web\\AssetBundle::js|js]].\n* [[yii\\web\\AssetBundle::depends|depends]]: un array que lista los nombres de los asset bundles de los que depende este\n  asset bundle (para explicarlo brevemente).\n* [[yii\\web\\AssetBundle::jsOptions|jsOptions]]: especifica las opciones que se enviarán al método\n  [[yii\\web\\View::registerJsFile()]] cuando se le llame para registrar *todos* los archivos JavaScript de este bundle.\n* [[yii\\web\\AssetBundle::cssOptions|cssOptions]]: especifica las opciones que se enviarán al método\n  [[yii\\web\\View::registerCssFile()]] cuando se le llame para registrar *todos* los archivos CSS de este bundle.\n* [[yii\\web\\AssetBundle::publishOptions|publishOptions]]: especifica las opciones que se enviarán al método\n  [[yii\\web\\AssetManager::publish()]] cuando se le llame para publicar los archivos de los assets fuente a un\n  directorio Web.\nSolo se usa si se especifica la propiedad [[yii\\web\\AssetBundle::sourcePath|sourcePath]].\n\n### Ubicación de los Assets <span id=\"asset-locations\"></span>\n\nSegún la localización de los assets, se pueden clasificar como:\n\n* assets fuente (source assets): los assets se encuentran junto con el código fuente PHP, al que no se puede acceder\n  directamente a través de la Web. Para usar los assets fuente en una página, deben ser copiados en un directorio\n  público y transformados en los llamados assets publicados. El proceso se llama *publicación de assets* que será\n  descrito a continuación.\n* assets publicados (published assets): los archivos assets se encuentran en el directorio Web y son accesibles vía Web.\n* assets externos (external assets): los archivos assets se encuentran en un servidor Web diferente al de la aplicación.\n\nCuando se define una clase asset bundle, si se especifica la propiedad [[yii\\web\\AssetBundle::sourcePath|sourcePath]],\nsignifica que cualquier asset listado que use rutas relativas será considerado como un asset fuente. Si no se\nespecifica la propiedad, significa que los assets son assets publicados (se deben especificar\n[[yii\\web\\AssetBundle::basePath|basePath]] y\n[[yii\\web\\AssetBundle::baseUrl|baseUrl]] para hacerle saber a Yii dónde se encuentran.)\n\nSe recomienda ubicar los assets que correspondan a la aplicación en un directorio Web para evitar publicaciones de\nassets innecesarias. Por esto en el anterior ejemplo `AppAsset` especifica [[yii\\web\\AssetBundle::basePath|basePath]]\nen vez de [[yii\\web\\AssetBundle::sourcePath|sourcePath]].\n\nPara las [extensiones](structure-extensions.md), por el hecho de que sus assets se encuentran junto con el código\nfuente, en directorios que no son accesibles para la Web, se tiene que especificar la propiedad\n[[yii\\web\\AssetBundle::sourcePath|sourcePath]] cuando se definan clases asset bundle para ellas.\n\n> Note: No se debe usar `@webroot/assets` como [[yii\\web\\AssetBundle::sourcePath|source path]]. Este directorio se usa\n  por defecto por el [[yii\\web\\AssetManager|asset manager]] para guardar los archivos asset publicados temporalmente y\n  pueden ser eliminados.\n\n### Dependencias de los Asset <span id=\"asset-dependencies\"></span>\n\nCuando se incluyen múltiples archivos CSS o JavaScript en una página Web, tienen que cumplir ciertas órdenes para\nevitar problemas de sobrescritura. Por ejemplo, si se usa un widget jQuery UI en una página Web, tenemos que\nasegurarnos de que el archivo JavaScript jQuery se incluya antes que el archivo JavaScript jQuery UI. A esto se le\nllama ordenar las dependencias entre archivos.\n\nLas dependencias de los assets se especifican principalmente a través de la propiedad [[yii\\AssetBundle::depends]].\nEn el ejemplo `AppAsset`, el asset bundle depende de otros dos asset bundles [[yii\\web\\YiiAsset]] y\n[[yii\\bootstrap\\BootstrapAsset]], que significa que los archivos CSS y JavaScript en `AppAsset` se incluirán *después*\nque los archivos de los dos bundles dependientes.\n\nLas dependencias son transitivas. Esto significa, que si un bundle A depende de un bundle B que depende de C, A\ndependerá de C, también.\n\n### Opciones de los Assets <span id=\"asset-options\"></span>\n\nSe pueden especificar las propiedades [[yii\\web\\AssetBundle::cssOptions|cssOptions]] y\n[[yii\\web\\AssetBundle::jsOptions|jsOptions]] para personalizar la forma en que los archivos CSS y JavaScript serán\nincluidos en una página. Los valores de estas propiedades serán enviadas a los métodos\n[[yii\\web\\View::registerCssFile()]] y [[yii\\web\\View::registerJsFile()]], respectivamente cuando las\n[vistas](structure-views.md) los llamen para incluir los archivos CSS y JavaScript.\n\n> Note: Las opciones que se especifican en una clase bundle se aplican a *todos* los archivos CSS/JavaScript de un\n  bundle. Si se quiere usar diferentes opciones para diferentes archivos, se deben crear assets bundles separados y\n  usar un conjunto de opciones para cada bundle.\n\nPor ejemplo, para incluir una archivo CSS condicionalmente para navegadores que como IE9 o anteriores, se puede usar la\n siguiente opción:\n\n```php\npublic $cssOptions = ['condition' => 'lte IE9'];\n```\n\nEsto provoca que un archivo CSS dentro de un bundle sea incluido usando los siguientes tags HTML:\n\n```html\n<!--[if lte IE9]>\n<link rel=\"stylesheet\" href=\"path/to/foo.css\">\n<![endif]-->\n```\n\nPara envolver el tag del enlace con `<noscript>` se puede usar el siguiente código:\n\n```php\npublic $cssOptions = ['noscript' => true];\n```\n\nPara incluir un archivo JavaScript en la sección cabecera (head) de una página (por defecto, los archivos JavaScript se\n incluyen al final de la sección cuerpo(body)), se puede usar el siguiente código:\n\n```php\npublic $jsOptions = ['position' => \\yii\\web\\View::POS_HEAD];\n```\n\nPor defecto, cuando un asset bundle está siendo publicado, todos los contenidos del directorio especificado por [[yii\\web\\AssetBundle::sourcePath]]\nserán publicados. Puedes personalizar este comportamiento configurando la propiedad [[yii\\web\\AssetBundle::publishOptions|publishOptions]]. Por\nejemplo, públicar solo uno o unos pocos subdirectorios de [[yii\\web\\AssetBundle::sourcePath]], puedes hacerlo de la siguiente manera en la clase\nasset bundle:\n\n```php\n<?php\nnamespace app\\assets;\n\nuse yii\\web\\AssetBundle;\n\nclass FontAwesomeAsset extends AssetBundle\n{\n    public $sourcePath = '@bower/font-awesome';\n    public $css = [\n        'css/font-awesome.min.css',\n    ];\n\n    public function init()\n    {\n        parent::init();\n        $this->publishOptions['beforeCopy'] = function ($from, $to) {\n            $dirname = basename(dirname($from));\n            return $dirname === 'fonts' || $dirname === 'css';\n        };\n    }\n}\n```\n\nEl ejemplo anterior define un asset bundle para el [\"fontawesome\" package](https://fontawesome.com/). Especificando\nla opción de publicación `beforeCopy`, solo los subdirectorios `fonts` y `css` serán publicados.\n\n### Bower y NPM Assets <span id=\"bower-npm-assets\"></span>\n\nLa mayoría de paquetes JavaScript/CSS se gestionan con [Bower](https://bower.io/) y/o [NPM](https://www.npmjs.com/).\nSi tu aplicación o extensión usa estos paquetes, se recomienda seguir los siguientes pasos para gestionar los assets en\n la librería:\n\n1. Modificar el archivo `composer.json` de tu aplicación o extensión e introducir el paquete en la lista `require`.\n   Se debe usar `bower-asset/PackageName` (para paquetes Bower) o `npm-asset/PackageName` (para paquetes NPM) para\n   referenciar la librería.\n2. Crear una clase asset bundle y listar los archivos JavaScript/CSS que se planea usar en la aplicación o extensión.\n   Se debe especificar la propiedad [[yii\\web\\AssetBundle::sourcePath|sourcePath]] como `@bower\\PackageName` o\n   `@npm\\PackageName`. Esto se debe a que Composer instalará el paquete Bower o NPM en el correspondiente directorio de\n    este alias.\n\n> Note: Algunos paquetes pueden distribuir sus archivos en subdirectorios. Si es el caso, se debe especificar el\n  subdirectorio como valor del [[yii\\web\\AssetBundle::sourcePath|sourcePath]]. Por ejemplo, [[yii\\web\\JqueryAsset]]\n  usa `@bower/jquery/dist` en vez de `@bower/jquery`.\n\n## Uso de Asset Bundles <span id=\"using-asset-bundles\"></span>\n\nPara usar un asset bundle, debe registrarse con una [vista](structure-views.md) llamando al método\n[[yii\\web\\AssetBundle::register()]]. Por ejemplo, en plantilla de vista se puede registrar un asset bundle como en el\nsiguiente ejemplo:\n\n```php\nuse app\\assets\\AppAsset;\nAppAsset::register($this);  // $this representa el objeto vista\n```\n\n> Info: El método [[yii\\web\\AssetBundle::register()]] devuelve un objeto asset bundle que contiene la\n  información acerca de los assets publicados, tales como [[yii\\web\\AssetBundle::basePath|basePath]] o\n  [[yii\\web\\AssetBundle::baseUrl|baseUrl]].\n\nSi se registra un asset bundle en otro lugar, se debe proporcionar la vista necesaria al objeto. Por ejemplo, para\nregistrar un asset bundle en una clase [widget](structure-widgets.md), se puede obtener el objeto vista mediante\n`$this->view`.\n\nCuando se registra un asset bundle con una vista, por detrás, Yii registrará todos sus asset bundles dependientes.\nY si un asset bundle se encuentra en un directorio inaccesible por la Web, éste será publicado a un directorio Web\npúblico. Después cuando la vista renderice una página, se generarán las etiquetas (tags) `<link>` y `<script>`  para\nlos archivos CSS y JavaScript listados en los bundles registrados. El orden de estas etiquetas será determinado por\nlas dependencias entre los bundles registrados y los otros assets listados en las propiedades\n[[yii\\web\\AssetBundle::css]] y [[yii\\web\\AssetBundle::js]].\n\n### Personalización de Asset Bundles <span id=\"customizing-asset-bundles\"></span>\n\nYii gestiona los asset bundles a través de un componente de aplicación llamado `assetManager` que está implementado\npor [[yii\\web\\AssetManager]]. Configurando la propiedad [[yii\\web\\AssetManager::bundles]], se puede personalizar el\ncomportamiento (behavior) de un asset bundle. Por ejemplo, de forma predeterminada, el asset bundle [[yii\\web\\Jquery]]\n, utiliza el archivo `jquery.js` desde el paquete Bower instalado. Para mejorar la disponibilidad y el rendimiento se\npuede querer usar la versión alojada por Google. Ésta puede ser obtenida configurando `assetManager` en la\nconfiguración de la aplicación como en el siguiente ejemplo:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'bundles' => [\n                'yii\\web\\JqueryAsset' => [\n                    'sourcePath' => null,   // no publicar el bundle\n                    'js' => [\n                        '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js',\n                    ]\n                ],\n            ],\n        ],\n    ],\n];\n```\n\nDel mismo modo, se pueden configurar múltiples asset bundles a través de [[yii\\web\\AssetManager::bundles]]. Las claves\ndel array deben ser los nombres de clase (sin la primera barra invertida) de los asset bundles, y los valores del array\n deben ser las correspondientes [configuraciones de arrays](concept-configurations.md).\n\n> Tip: Se puede elegir condicionalmente que assets se van a usar en un asset bundle. El siguiente ejemplo\nmuestra como usar `jquery.js` en el entorno de desarrollo y `jquery.min.js` en los otros casos:\n>\n> ```php\n> 'yii\\web\\JqueryAsset' => [\n>     'js' => [\n>         YII_ENV_DEV ? 'jquery.js' : 'jquery.min.js'\n>     ]\n> ],\n> ```\n\nSe puede deshabilitar uno o más asset bundles asociando `false` a los nombres de los asset bundles que se quieran\ndeshabilitar. Cuando se registra un asset bundle deshabilitado con una vista, ninguno de sus bundles dependientes será\nregistrado, y la vista tampoco incluirá ningún asset del bundle en la página que se renderice.\nPor ejemplo, para deshabilitar [[yii\\web\\JqueryAsset]], se puede usar la siguiente configuración:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'bundles' => [\n                'yii\\web\\JqueryAsset' => false,\n            ],\n        ],\n    ],\n];\n```\n\nAdemás se pueden deshabilitar *todos* los asset bundles asignando `false` a [[yii\\web\\AssetManager::bundles]].\n\n### Mapeo de Assets (Asset Mapping) <span id=\"asset-mapping\"></span>\n\nA veces se puede querer \"arreglar\" rutas de archivos incorrectos/incompatibles usadas en múltiples asset bundles.\nPor ejemplo, el bundle A usa `jquery.min.js` con versión 1.11.1, y el bundle B usa `jquery.js` con versión 2.11.1.\nMientras que se puede solucionar el problema personalizando cada bundle, una forma más fácil, es usar la\ncaracterística *asset map* para mapear los assets incorrectos a los deseados. Para hacerlo, se tiene que configurar la\npropiedad [[yii\\web\\AssetManager::assetMap]] como en el siguiente ejemplo:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'assetMap' => [\n                'jquery.js' => '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js',\n            ],\n        ],\n    ],\n];\n```\n\nLas claves de [[yii\\web\\AssetManager::assetMap|assetmMap]] son los nombres de los assets que se quieren corregir,\ny los valores son las rutas de los assets deseados. Cuando se registra un asset bundle con una vista, cada archivo de\nasset relativo de [[yii\\web\\AssetBundle::css|css]] y [[yii\\web\\AssetBundle::js|js]] serán contrastados con este mapa.\nSi se detecta que alguna de estas claves es la última parte de un archivo asset (prefijado con\n[[yii\\web\\AssetBundle::sourcePath]], si esta disponible), el correspondiente valor reemplazará el asset y será\nregistrado con la vista.\nPor ejemplo, un archivo asset `mi/ruta/a/jquery.js` concuerda con la clave `jquery.js`.\n\n> Note: Sólo los assets especificados usando rutas relativas están sujetos al mapeo de assets. Y las rutas de los\nassets destino deben ser tanto URLs absolutas o rutas relativas a [[yii\\web\\AssetManager::basePath]].\n\n### Publicación de Asset <span id=\"asset-publishing\"></span>\n\nComo se ha comentado anteriormente, si un asset bundle se encuentra en un directorio que no es accesible por la Web,\neste asset será copiado a un directorio Web cuando se registre el bundle con una vista. Este proceso se llama\n*publicación de assets*, y se efectúa automáticamente por el [[yii\\web\\AssetManager|asset manager]].\n\nDe forma predeterminada, los assets se publican en el directorio `@webroot/assets` cuando corresponden a la URL\n`@web\\assets`. Se puede personalizar esta ubicación configurando las propiedades\n[[yii\\web\\AssetManager::basePath|basePath]] y [[yii\\web\\AssetManager::baseUrl|baseUrl]].\n\nEn lugar de publicar los assets copiando archivos, se puede considerar usar enlaces simbólicos, si tu\nSO (sistema operativo) y servidor Web lo permiten. Esta característica se puede habilitar estableciendo el valor de\n[[yii\\web\\AssetManager::linkAssets|linkAssets]] en `true`.\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'linkAssets' => true,\n        ],\n    ],\n];\n```\n\nCon la anterior configuración, el gestor de assets creará un enlace simbólico a la ruta de origen del asset bundle\ncuando éste sea publicado. Esto es más rápido que copiar archivos y también asegura que siempre estén actualizados.\n\n## Los Asset Bundles más Comunes <span id=\"common-asset-bundles\"></span>\n\nEl código del núcleo de Yii tiene definidos varios asset bundles. Entre ellos, los siguientes bundles son los más\nusados y pueden referenciarse en códigos de aplicaciones o extensiones.\n\n- [[yii\\web\\YiiAsset]]: Principalmente incluye el archivo `yii.js` que implementa un mecanismo de organización de\n  código JavaScript en los módulos. También proporciona soporte especial para los atributos `data-method` y\n  `data-confirm` y otras característica útiles.\n- [[yii\\web\\JqueryAsset]]: Incluye el archivo `jquery.js` desde el paquete Bower jQuery.\n- [[yii\\bootstrap\\BootstrapAsset]]: Incluye el archivo CSS desde el framework Twitter Bootstrap.\n- [[yii\\bootstrap\\BootstrapPluginAsset]]: Incluye el archivo JavaScript desde el framework Twitter Bootstrap para dar\n  soporte a los plugins JavaScript de Bootstrap.\n- [[yii\\jui\\JuiAsset]]: Incluye los archivos CSS y JavaScript desde la librería jQuery UI.\n\nSi el código depende de jQuery, jQuery UI o Bootstrap, se pueden usar estos asset bundles predefinidos en lugar de\ncrear versiones propias. Si la configuración predeterminada de estos bundles no satisface las necesidades, se puede\npersonalizar como se describe en la subsección [Personalización de Asset Bundles](#customizing-asset-bundles).\n\n## Conversión de Assets <span id=\"asset-conversion\"></span>\n\nEn lugar de escribir código CSS y/o JavaScript directamente, los desarrolladores a menudo escriben código usando una\nsintaxis extendida y usan herramientas especiales para convertirlos en CSS/JavaScript. Por ejemplo, para código CSS se\npuede usar [LESS](https://lesscss.org) o [SCSS](https://sass-lang.com/); y para JavaScript se puede usar\n[TypeScript](https://www.typescriptlang.org/).\n\nSe pueden listar los archivos asset con sintaxis extendida (extended syntax) en [[yii\\web\\AssetBundle::css|css]] y\n[[yii\\web\\AssetBundle::js|js]] en un asset bundle. Por ejemplo:\n\n```php\nclass AppAsset extends AssetBundle\n{\n    public $basePath = '@webroot';\n    public $baseUrl = '@web';\n    public $css = [\n        'css/site.less',\n    ];\n    public $js = [\n        'js/site.ts',\n    ];\n    public $depends = [\n        'yii\\web\\YiiAsset',\n        'yii\\bootstrap\\BootstrapAsset',\n    ];\n}\n```\n\nCuando se registra uno de estos asset bundles en una vista, el [[yii\\web\\AssetManager|asset manager]] ejecutará\nautomáticamente las herramientas pre-procesadoras para convertir los assets de sintaxis extendidas reconocidas en\nCSS/JavaScript. Cuando la vista renderice finalmente una página, se incluirán los archivos CSS/JavaScript\nen la página, en lugar de los assets originales en sintaxis extendidas.\n\nYii usa las extensiones de archivo para identificar que sintaxis extendida se está usando. De forma predeterminada se\nreconocen las siguientes sintaxis y extensiones de archivo.\n\n- [LESS](https://lesscss.org/): `.less`\n- [SCSS](https://sass-lang.com/): `.scss`\n- [Stylus](https://stylus-lang.com/): `.styl`\n- [CoffeeScript](https://coffeescript.org/): `.coffee`\n- [TypeScript](https://www.typescriptlang.org/): `.ts`\n\nYii se basa en las herramientas pre-procesadoras instalada para convertir los assets. Por ejemplo, para usar\n[LESS](https://lesscss.org/) se debe instalar el comando pre-procesador `lessc`.\n\nSe pueden personalizar los comandos de los pre-procesadores y las sintaxis extendidas soportadas configurando\n[[yii\\web\\AssetManager::converter]] como en el siguiente ejemplo:\n\n```php\nreturn [\n    'components' => [\n        'assetManager' => [\n            'converter' => [\n                'class' => 'yii\\web\\AssetConverter',\n                'commands' => [\n                    'less' => ['css', 'lessc {from} {to} --no-color'],\n                    'ts' => ['js', 'tsc --out {to} {from}'],\n                ],\n            ],\n        ],\n    ],\n];\n```\n\nEn el anterior ejemplo se especifican las sintaxis extendidas soportadas a través de la propiedad\n[[yii\\web\\AssetConverter::commands]]. Las claves del array son los nombres de extensión de archivo (sin el punto), y\nlos valores del array las extensiones de archivo resultantes y los comandos para realizar la conversión de assets.\nLos tokens `{from}` y `{to}` en los comandos se reemplazarán por las rutas de origen de los archivos asset y las rutas\nde destino de los archivos asset.\n\n> Info: Hay otras maneras de trabajar con las assets de sintaxis extendidas, además de la descrita\n  anteriormente. Por ejemplo, se pueden usar herramientas generadoras tales como [grunt](https://gruntjs.com/) para\n  monitorear y convertir automáticamente los assets de sintaxis extendidas. En este caso, se deben listar los archivos\n  CSS/JavaScript resultantes en lugar de los archivos de originales.\n\n## Combinación y Compresión de Assets <span id=\"combining-compressing-assets\"></span>\n\nUna página web puede incluir muchos archivos CSS y/o JavaScript. Para reducir el número de peticiones (requests)\nHTTP y el tamaño total de descarga de estos archivos, una práctica común es combinar y comprimir uno o\nvarios archivos, y después incluir los archivos comprimidos en las páginas Web.\n\n>Información: La combinación y compresión de assets es habitualmente necesario cuando una aplicación se encuentra en\nmodo de producción. En modo de desarrollo, es más conveniente usar los archivos CSS/JavaScript originales por temas\nrelacionados con el debugging.\n\nEn el siguiente ejemplo, se muestra una propuesta para combinar y comprimir archivos asset sin necesidad de modificar\nel código de la aplicación.\n\n1. Buscar todos los asset bundles en la aplicación que se quieran combinar y comprimir.\n2. Dividir estos bundles en uno o más grupos. Tenga en cuenta que cada bundle solo puede pertenecer a un único grupo.\n3. Combina/Comprime los archivos CSS de cada grupo en un único archivo. Hace lo mismo para los archivos JavaScript.\n4. Define un nuevo asset bundle para cada grupo:\n    * Establece las propiedades [[yii\\web\\AssetBundle::css|css]] y [[yii\\web\\AssetBundle::js|js]] para que sean los\n      archivos CSS y JavaScript combinados, respectivamente.\n    * Personaliza los asset bundles en cada grupo configurando sus propiedades [[yii\\web\\AssetBundle::css|css]] y\n      [[yii\\web\\AssetBundle::js|js]] para que sean el nuevo asset bundle creado para el grupo.\n\nUsando este propuesta, cuando se registre un asset bundle en una vista, se genera un registro automático del nuevo\nasset bundle para el grupo al que pertenece el bundle original. Y como resultado, los archivos combinados/comprimidos\nse incluyen en la página, en lugar de los originales.\n\n### Un Example <span id=\"example\"></span>\n\nVamos a usar un ejemplo para explicar la propuesta anterior.\n\nAsumiendo que la aplicación tenga dos páginas X e Y. La página X utiliza el asset bundle A, B y C mientras que la\npágina Y usa los asset bundles B, C y D.\n\nHay dos maneras de dividir estos asset bundles. Uno es usar un único grupo que incluye todos los asset bundles,\nel otro es poner (A, B y C) en el Grupo X, y (B, C, D) en el grupo Y. ¿Cuál es mejor? El primero tiene la ventaja\nde que las dos páginas comparten los mismos archivos CSS y JavaScript combinados, que producen una caché HTTP más\nefectiva. Por otra parte, por el hecho de que un único grupo contenga todos los bundles, los archivos JavaScript serán\nmás grandes y por tanto incrementan el tiempo de transmisión del archivo inicial. En este ejemplo, se usará la primera\nopción, ej., usar un único grupo que contenga todos los bundles.\n\n> Info: Dividiendo los asset bundles en grupos no es una tarea trivial. Normalmente requiere un análisis de los\n  datos del tráfico real de varios assets en diferentes páginas. Al principio, se puede empezar con un\n  único grupo para simplificar.\n\nSe pueden usar herramientas existentes (ej. [Closure Compiler](https://developers.google.com/closure/compiler/),\n[YUI Compressor](https://github.com/yui/yuicompressor/)) para combinar y comprimir todos los bundles. Hay que tener en\ncuenta que los archivos deben ser combinados en el orden que satisfaga las dependencias entre los bundles.\nPor ejemplo, si el Bundle A depende del B que depende a su vez de C y D, entonces, se deben listar los archivos asset\nempezando por C y D, seguidos por B y finalmente A.\n\nDespués de combinar y comprimir obtendremos un archivo CSS y un archivo JavaScript. Supongamos que se llaman\n`all-xyz.css` y `all-xyz.js`, donde `xyz` representa un timestamp o un hash que se usa para generar un nombre de\narchivo único para evitar problemas con la caché HTTP.\n\nAhora estamos en el último paso. Configurar el [[yii\\web\\AssetManager|asset manager]] como en el siguiente ejemplo en\nla configuración de la aplicación:\n\n```php\nreturn [\n    'components' => [\n        'assetManager' => [\n            'bundles' => [\n                'all' => [\n                    'class' => 'yii\\web\\AssetBundle',\n                    'basePath' => '@webroot/assets',\n                    'baseUrl' => '@web/assets',\n                    'css' => ['all-xyz.css'],\n                    'js' => ['all-xyz.js'],\n                ],\n                'A' => ['css' => [], 'js' => [], 'depends' => ['all']],\n                'B' => ['css' => [], 'js' => [], 'depends' => ['all']],\n                'C' => ['css' => [], 'js' => [], 'depends' => ['all']],\n                'D' => ['css' => [], 'js' => [], 'depends' => ['all']],\n            ],\n        ],\n    ],\n];\n```\n\nComo se ha explicado en la subsección [Personalización de Asset Bundles](#customizing-asset-bundles), la anterior\nconfiguración modifica el comportamiento predeterminado de cada bundle. En particular, el Bundle A, B, C y D ya no\ntendrán ningún archivo asset. Ahora todos dependen del bundle `all` que contiene los archivos combinados `all-xyz.css`\ny `all-xyz.js`. Por consiguiente, para la Página X, en lugar de incluir los archivos originales desde los bundles A, B\ny C, solo se incluirán los dos archivos combinados; pasa lo mismo con la Página Y.\n\nHay un último truco para hacer que el enfoque anterior se adapte mejor. En lugar de modificar directamente el archivo\nde configuración de la aplicación, se puede poner el array del personalización del bundle en un archivo separado y que\nse incluya condicionalmente este archivo en la configuración de la aplicación. Por ejemplo:\n\n```php\nreturn [\n    'components' => [\n        'assetManager' => [\n            'bundles' => require __DIR__ . '/' . (YII_ENV_PROD ? 'assets-prod.php' : 'assets-dev.php'),\n        ],\n    ],\n];\n```\n\nEs decir, el array de configuración del asset bundle se guarda en `asset-prod.php` para el modo de producción, y\n`assets-del.php` para los otros modos.\n\n### Uso del Comando `asset` <span id=\"using-asset-command\"></span>\n\nYii proporciona un comando de consola llamado `asset` para automatizar el enfoque descrito.\n\nPara usar este comando, primero se debe crear un archivo de configuración para describir que asset bundle se deben\ncombinar y cómo se deben agrupar. Se puede usar el sub-comando `asset/template` para generar una plantilla primero y\ndespués modificarla para que se adapte a nuestras necesidades.\n\n```\nyii asset/template assets.php\n```\n\nEl comando genera un archivo llamado `assets.php` en el directorio actual. El contenido de este archivo es similar al\nsiguiente código:\n\n```php\n<?php\n/**\n * Configuration file for the \"yii asset\" console command.\n * Note that in the console environment, some path aliases like '@webroot' and '@web' may not exist.\n * Please define these missing path aliases.\n */\nreturn [\n    // Ajustar comando/callback para comprimir los ficheros JavaScript:\n    'jsCompressor' => 'java -jar compiler.jar --js {from} --js_output_file {to}',\n    // Ajustar comando/callback para comprimir los ficheros CSS:\n    'cssCompressor' => 'java -jar yuicompressor.jar --type css {from} -o {to}',\n    // La lista de assets bundles para comprimir:\n    'bundles' => [\n        // 'yii\\web\\YiiAsset',\n        // 'yii\\web\\JqueryAsset',\n    ],\n    // Asset bundle para la salida de compresión:\n    'targets' => [\n        'all' => [\n            'class' => 'yii\\web\\AssetBundle',\n            'basePath' => '@webroot/assets',\n            'baseUrl' => '@web/assets',\n            'js' => 'js/all-{hash}.js',\n            'css' => 'css/all-{hash}.css',\n        ],\n    ],\n    // Configuración del Asset manager:\n    'assetManager' => [\n    ],\n];\n```\n\nSe debe modificar este archivo para especificar que bundles plantea combinar en la opción `bundles`. En la opción\n`targets` se debe especificar como se deben dividir entre los grupos. Se puede especificar uno o más grupos,\ncomo se ha comentado.\n\n> Note: Debido a que los alias `@webroot` y `@web` no están disponibles en la aplicación de consola, se deben definir\n  explícitamente en la configuración.\n\nLos archivos JavaScript se combinan, comprimen y guardan en `js/all-{hash}.js` donde {hash} se reemplaza con el hash\ndel archivo resultante.\n\nLas opciones `jsCompressor` y `cssCompressor` especifican los comandos de consola o llamadas PHP (PHP callbacks) para\nrealizar la combinación/compresión de JavaScript y CSS. De forma predeterminada Yii usa\n[Closure Compiler](https://developers.google.com/closure/compiler/) para combinar los archivos JavaScript y\n[YUI Compressor](https://github.com/yui/yuicompressor/) para combinar archivos CSS. Se deben instalar las herramientas\nmanualmente o ajustar sus configuraciones para usar nuestras favoritas.\n\nCon el archivo de configuración, se puede ejecutar el comando `asset` para combinar y comprimir los archivos asset y\ndespués generar un nuevo archivo de configuración de asset bundles `asset-prod.php`:\n\n```\nyii asset assets.php config/assets-prod.php\n```\n\nEl archivo de configuración generado se puede incluir en la configuración de la aplicación, como se ha descrito en la\nanterior subsección.\n\n> Info: Usar el comando `asset` no es la única opción de automatizar el proceso de combinación y compresión.\n  Se puede usar la excelente herramienta de ejecución de tareas [grunt](https://gruntjs.com/) para lograr el mismo\n  objetivo.\n"
  },
  {
    "path": "docs/guide-es/structure-controllers.md",
    "content": "Controladores\n=============\n\nLos controladores son parte del patrón o arquitectura [MVC](https://es.wikipedia.org/wiki/Modelo%E2%80%93vista%E2%80%93controlador).\nSon objetos que extienden de [[yii\\base\\Controller]] y se encargan de procesar los `requests` (consultas)\ngenerando `responses` (respuestas). Particularmente, después de tomar el control desde las [aplicaciones](structure-applications.md),\nlos controladores analizarán los datos que entran en el `request`, los pasan a los [modelos](structure-models.md), inyectan los\nmodelos resultantes a las [vistas](structure-views.md), y finalmente generan los `responses` (respuestas) de salida.\n\n\n## Acciones <span id=\"actions\"></span>\n\nLos Controladores están compuestos por *acciones* que son las unidades más básicas a las que los usuarios pueden\ndirigirse y solicitar ejecución. Un controlador puede tener una o múltiples acciones.\n\nEl siguiente ejemplo muestra un controlador `post` con dos acciones: `view` y `create`:\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse app\\models\\Post;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\n\nclass PostController extends Controller\n{\n    public function actionView($id)\n    {\n        $model = Post::findOne($id);\n        if ($model === null) {\n            throw new NotFoundHttpException;\n        }\n\n        return $this->render('view', [\n            'model' => $model,\n        ]);\n    }\n\n    public function actionCreate()\n    {\n        $model = new Post;\n\n        if ($model->load(Yii::$app->request->post()) && $model->save()) {\n            return $this->redirect(['view', 'id' => $model->id]);\n        } else {\n            return $this->render('create', [\n                'model' => $model,\n            ]);\n        }\n    }\n}\n```\n\nEn la acción `view` (definida en el método `actionView()`), el código primero carga el [modelo](structure-models.md)\nde acuerdo el ID del modelo solicitado; Si el modelo es cargado satisfactoriamente, lo mostrará usando una [vista](structure-views.md)\nllamada `view`. Si no, arrojará una excepción.\n\nEn la acción `create` (definida por el método `actionCreate()`), el código es similar. Primero intenta poblar \nel [modelo](structure-models.md) usando datos del `request` y guardarlo. Si ambas cosas suceden correctamente, se redireccionará\nel navegador a la acción `view` con el ID del modelo recientemente creado. De otro modo mostrará\nla vista `create` a través de la cual el usuario puede completar los campos necesarios.\n\n\n## Routes <span id=\"routes\"></span>\n\nLos usuarios ejecutan las acciones a través de las llamadas *routes* (rutas). una ruta es una cadena que consiste en las siguientes partes:\n\n* un ID de módulo: este existe solamente si el controlador pertenece a un [módulo](structure-modules.md) que no es de la aplicación;\n* un ID de controlador: una cadena que identifica exclusivamente al controlador entre todos los controladores dentro de la misma aplicación\n  (o el mismo módulo si el controlador pertenece a uno);\n* un ID de acción: una cadena que identifica exclusivamente a la acción entre todas las acciones del mismo controlador.\n\nLas rutas pueden usar el siguiente formato:\n\n```\nControllerID/ActionID\n```\n\no el siguiente formato si el controlador pertenece a un módulo:\n\n```php\nModuleID/ControllerID/ActionID\n```\n\nEntonces si un usuario solicita la URL `https://hostname/index.php?r=site/index`, la acción `index` del controlador `site`\nserá ejecutado. Para más detalles acerca de cómo las son resueltas en acciones, por favor consulta\nla sección [Routing](runtime-routing.md).\n\n\n## Creando Controladores <span id=\"creating-controllers\"></span>\n\nEn [[yii\\web\\Application|aplicaciones Web]], los controladores deben extender de [[yii\\web\\Controller]] o cualquier\nclase hija. De forma similar los controladores de [[yii\\console\\Application|aplicaciones de consola]], deben extender\nde [[yii\\console\\Controller]] o cualquier clase hija de esta. El siguiente código define un controlador `site`:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n}\n```\n\n\n### IDs de Controladores <span id=\"controller-ids\"></span>\n\nNormalmente, un controlador está diseñado para manejar los `requests` de acuerdo a un tipo de recurso.\nPor esta razón, los IDs de controladores son a menudo sustantivos de los tipos de recurso que están manejando.\nPor ejemplo, podrías utilizar `article` como el ID de un controlador que maneja datos de artículos.\n\nPor defecto, los IDs de controladores deberían contener sólo estos caracteres: letras del Inglés en minúscula, dígitos,\nguiones bajos y medios, y barras. Por ejemplo, `article`, `post-comment`, `admin/post-comment` son todos\nIDs de controladores válidos, mientras que `article?`, `PostComment`, `admin\\post` no lo son.\n\nLos guiones en un ID de controlador son utilizados para separar palabras, mientras que las barras diagonales lo son para\norganizar los controladores en sub-directorios.\n\n\n### Nombres de Clases de Controladores <span id=\"controller-class-naming\"></span>\n\nLos nombres de clases de controladores pueden ser derivados de los IDs de acuerdo a las siguientes reglas:\n\n* Transforma la primera letra de cada palabra separada por guiones en mayúscula. Nota que si el ID del controlador\n  contiene barras, esta regla sólo aplica a la porción después de la última barra dentro del ID.\n* Elimina guiones y reemplaza cualquier barra diagonal por barras invertidas.\n* Agrega el sufijo `Controller`.\n* Agrega al principio el [[yii\\base\\Application::controllerNamespace|controller namespace]].\n\nA continuación mostramos algunos ejemplos, asumiendo que el [[yii\\base\\Application::controllerNamespace|controller namespace]]\ntoma el valor por defecto: `app\\controllers`:\n\n* `article` deriva en `app\\controllers\\ArticleController`;\n* `post-comment` deriva en `app\\controllers\\PostCommentController`;\n* `admin/post-comment` deriva en `app\\controllers\\admin\\PostCommentController`.\n\nLas clases de controladores deben ser [autocargables](concept-autoloading.md). Por esta razón, en los ejemplos anteriores,\nla clase del controlador `article` debe ser guardada en un archivo cuyo alias [alias](concept-aliases.md)\nes `@app/controllers/ArticleController.php`; mientras que el controlador `admin/post-comment` debería estar\nen `@app/controllers/admin/PostCommentController.php`.\n\n> Info: En el último ejemplo, `admin/post-comment`, demuestra cómo puedes poner un controlador bajo un sub-directorio\n  del [[yii\\base\\Application::controllerNamespace|controller namespace]]. Esto es útil cuando quieres organizar\n  tus controladores en varias categorías pero sin utilizar [módulos](structure-modules.md).\n\n\n### Controller Map <span id=\"controller-map\"></span>\n\nPuedes configurar [[yii\\base\\Application::controllerMap|controller map]] (mapeo de controladores) para superar las restricciones\nde los IDs de controladores y sus nombres de clase descritos arriba. Esto es principalmente útil cuando estás utilizando un\ncontrolador de terceros del cual no tienes control alguno sobre sus nombres de clase.\n\nPuedes configurar [[yii\\base\\Application::controllerMap|controller map]] en la\n[configuración de la aplicación](structure-applications.md#application-configurations) de la siguiente manera:\n\n```php\n[\n    'controllerMap' => [\n        [\n            // declara el controlador \"account\" usando un nombre de clase\n            'account' => 'app\\controllers\\UserController',\n\n            // declara el controlador \"article\" utilizando un array de configuración\n            'article' => [\n                'class' => 'app\\controllers\\PostController',\n                'enableCsrfValidation' => false,\n            ],\n        ],\n    ],\n]\n```\n\n\n### Controller por Defecto <span id=\"default-controller\"></span>\n\nCada aplicación tiene un controlador por defecto especificado a través de la propiedad [[yii\\base\\Application::defaultRoute]].\nCuando un `request` no especifica una [ruta](#ids-routes), se utilizará la ruta especificada en esta propiedad.\nPara [[yii\\web\\Application|aplicaciones Web]], el valor es `'site'`, mientras que para [[yii\\console\\Application|aplicaciones de consola]]\nes `help`. Por lo tanto, si la URL es `https://hostname/index.php`, significa que el `request` será manejado por el controlador `site`.\n\nPuedes cambiar el controlador por defecto con la siguiente [configuración de la aplicación](structure-applications.md#application-configurations):\n\n```php\n[\n    'defaultRoute' => 'main',\n]\n```\n\n\n## Creando Acciones <span id=\"creating-actions\"></span>\n\nCrear acciones puede ser tan simple como definir un llamado *método de acción* en una clase controlador. Un método de acción es\nun método *public* cuyo nombre comienza con la palabra `action`. El valor de retorno de uno de estos métodos representa\nlos datos de respuesta (response) a ser enviado a los usuarios. El siguiente código define dos acciones: `index` y `hello-world`:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actionIndex()\n    {\n        return $this->render('index');\n    }\n\n    public function actionHelloWorld()\n    {\n        return 'Hola Mundo!';\n    }\n}\n```\n\n\n### IDs de Acciones <span id=\"action-ids\"></span>\n\nUna acción está a menudo diseñada para realizar una manipulación particular de un recurso. Por esta razón,\nlos IDs de acciones son usualmente verbos, como `view` (ver), `update` (actualizar), etc.\n\nPor defecto, los IDs de acciones deberían contener estos caracteres solamente: letras en Inglés en minúsculas, dígitos,\nguiones bajos y barras. Los guiones en un ID de acción son utilizados para separar palabras. Por ejemplo,\n`view`, `update2`, `comment-post` son todos IDs válidos, mientras que `view?` y `Update` no lo son.\n\nPuedes crear acciones de dos maneras: acciones en línea (inline) o acciones independientes (standalone). Una acción en línea\nes definida como un método en la clase del controlador, mientras que una acción independiente es una clase que extiende\n[[yii\\base\\Action]] o sus clases hijas. Las acciones en línea son más fáciles de crear y por lo tanto preferidas\nsi no tienes intenciones de volver a utilizarlas. Las acciones independientes, por otro lado, son principalmente\ncreadas para ser reutilizadas en otros controladores o para ser redistribuidas como [extensiones](structure-extensions.md).\n\n\n### Acciones en Línea <span id=\"inline-actions\"></span>\n\nComo acciones en línea nos referimos a acciones que son definidas en términos de métodos como acabamos de describir.\n\nLos nombre de métodos de acciones derivan de los IDs de acuerdo al siguiente criterio:\n\n* Transforma la primera letra de cada palabra del ID de la acción a mayúscula;\n* Elimina guiones;\n* Prefija la palabra `action`.\n\nPor ejemplo, `index` se vuelve `actionIndex`, y `hello-world` se vuelve `actionHelloWorld`.\n\n> Note: Los nombres de los métodos de acción son *case-sensitive* (distinguen entre minúsculas y mayúsculas). Si tienes un\n  método llamado `ActionIndex`, no será considerado como un método de acción, y como resultado, solicitar la acción `index`\n  resultará en una excepción. También ten en cuenta que los métodos de acción deben ser `public`. Un método `private` o `protected`\n  NO define un método de acción.\n\n\nLas acciones en línea son las más comúnmente definidas ya que requieren muy poco esfuerzo de creación. De todos modos,\nsi planeas reutilizar la misma acción en diferentes lugares, o quieres redistribuir una acción,\ndeberías considerar definirla como un *acción independiente*.\n\n\n### Acciones Independientes <span id=\"standalone-actions\"></span>\n\nLas acciones independientes son acciones definidas en términos de clases de acción que extienden de [[yii\\base\\Action]] o cualquiera de sus clases hijas.\nPor ejemplo, en Yii se encuentran las clases [[yii\\web\\ViewAction]] y [[yii\\web\\ErrorAction]], de las cuales ambas son acciones independientes.\n\nPara utilizar una acción independiente, debes declararla en el *action map* (mapeo de acciones) sobrescribiendo el método\n[[yii\\base\\Controller::actions()]] en tu controlador de la siguiente manera:\n\n```php\npublic function actions()\n{\n    return [\n        // declara la acción \"error\" utilizando un nombre de clase\n        'error' => 'yii\\web\\ErrorAction',\n\n        // declara la acción \"view\" utilizando un array de configuración\n        'view' => [\n            'class' => 'yii\\web\\ViewAction',\n            'viewPrefix' => '',\n        ],\n    ];\n}\n```\n\nComo puedes ver, el método `actions()` debe devolver un array cuyas claves son los IDs de acciones y sus valores los nombres\nde clases de acciones o [configuraciones](concept-configurations.md). Al contrario de acciones en línea, los IDs de acciones independientes\npueden contener caracteres arbitrarios, mientras sean declarados en el método `actions()`.\n\n\nPara crear una acción independiente, debes extender de [[yii\\base\\Action]] o una clase hija, e implementar un\nmétodo `public` llamado `run()`. El rol del método `run()` es similar al de un método de acción. Por ejemplo:\n\n```php\n<?php\nnamespace app\\components;\n\nuse yii\\base\\Action;\n\nclass HelloWorldAction extends Action\n{\n    public function run()\n    {\n        return \"Hola Mundo!\";\n    }\n}\n```\n\n\n### Resultados de Acción <span id=\"action-results\"></span>\n\nEl valor de retorno de una método de acción o del método `run()` de una acción independiente son significativos. Este se refiere\nal resultado de la acción correspondiente.\n\nEl valor devuelto puede ser un objeto [response](runtime-responses.md) que será enviado como respuesta a\nlos usuarios.\n\n* Para [[yii\\web\\Application|aplicaciones Web]], el valor de retorno pueden ser también datos arbitrarios que serán\n  asignados a [[yii\\web\\Response::data]] y más adelante convertidos a una cadena representando el cuerpo de la respuesta.\n* Para [[yii\\console\\Application|aplicaciones de consola]], el valor de retorno puede ser también un entero representando\n  el [[yii\\console\\Response::exitStatus|status de salida]] de la ejecución del comando.\n\nEn los ejemplos mostrados arriba, los resultados de las acciones son todas cadenas que serán tratadas como el cuerpo de la respuesta\na ser enviado a los usuarios. El siguiente ejemplo demuestra cómo una acción puede redirigir el navegador del usuario a una nueva URL\ndevolviendo un objeto `response` (debido a que el método [[yii\\web\\Controller::redirect()|redirect()]] devuelve\nun objeto `response`):\n\n```php\npublic function actionForward()\n{\n    // redirige el navegador del usuario a https://example.com\n    return $this->redirect('https://example.com');\n}\n```\n\n\n### Parámetros de Acción <span id=\"action-parameters\"></span>\n\nLos métodos de acción para acciones en línea y el método `run()` de acciones independientes pueden tomar parámetros,\nllamados *parámetros de acción*. Sus valores son obtenidos del `request`. Para [[yii\\web\\Application|aplicaciones Web]],\nel valor de cada parámetro de acción es tomado desde `$_GET` usando el nombre del parámetro como su clave;\npara [[yii\\console\\Application|aplicaciones de consola]], estos corresponden a los argumentos de la línea de comandos.\n\nEn el siguiente ejemplo, la acción `view` (una acción en línea) declara dos parámetros: `$id` y `$version`.\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass PostController extends Controller\n{\n    public function actionView($id, $version = null)\n    {\n        // ...\n    }\n}\n```\n\nLos parámetros de acción serán poblados como se muestra a continuación para distintos `requests`:\n\n* `https://hostname/index.php?r=post/view&id=123`: el parámetro `$id` tomará el valor\n  `'123'`,  mientras que `$version` queda como `null` debido a que no hay un parámetro `version` en la URL.\n* `https://hostname/index.php?r=post/view&id=123&version=2`: los parámetros `$id` y `$version` serán llenados con\n  `'123'` y `'2'`, respectivamente.\n* `https://hostname/index.php?r=post/view`: se lanzará una excepción [[yii\\web\\BadRequestHttpException]]\n  dado que el parámetro `$id` es requerido pero no es provisto en el `request`.\n* `https://hostname/index.php?r=post/view&id[]=123`: una excepción [[yii\\web\\BadRequestHttpException]] será lanzada\n  porque el parámetro `$id` está recibiendo un valor inesperado, el array `['123']`.\n\nSi quieres que un parámetro de acción acepte un array como valor, deberías utilizar el `type-hinting` (especificación de tipo) `array`,\ncomo a continuación:\n\n```php\npublic function actionView(array $id, $version = null)\n{\n    // ...\n}\n```\n\nAhora si el `request` es `https://hostname/index.php?r=post/view&id[]=123`, el parámetro `$id` tomará el valor\nde `['123']`. Si el `request` es `https://hostname/index.php?r=post/view&id=123`, el parámetro `$id` recibirá aún\nel mismo array como valor ya que el valor escalar `'123'` será convertido automáticamente en array.\n\nLos ejemplos de arriba muestran principalmente como funcionan los parámetros de acción de una aplicación Web. Para aplicaciones de consola,\npor favor consulta la sección [Comandos de Consola](tutorial-console.md) para más detalles.\n\n\n### Acción por Defecto <span id=\"default-action\"></span>\n\nCada controlador tiene una acción por defecto especificada a través de la propiedad [[yii\\base\\Controller::defaultAction]].\nCuando una [ruta](#ids-routes) contiene sólo el ID del controlador, implica que se está solicitando la acción por defecto\ndel controlador especificado.\n\nPor defecto, la acción por defecto (valga la redundancia) definida es `index`. Si quieres cambiar dicho valor, simplemente sobrescribe\nesta propiedad en la clase del controlador, como se muestra a continuación:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public $defaultAction = 'home';\n\n    public function actionHome()\n    {\n        return $this->render('home');\n    }\n}\n```\n\n\n## Ciclo de Vida del Controlador <span id=\"controller-lifecycle\"></span>\n\nCuando se procesa un `request`, la [aplicación](structure-applications.md) creará un controlador\nbasado en la [ruta](#routes) solicitada. El controlador entonces irá a través del siguiente ciclo de vida\npara completar el `request`:\n\n1. El método [[yii\\base\\Controller::init()]] es llamado después de que el controlador es creado y configurado.\n2. El controlador crea un objecto `action` basado en el ID de acción solicitado:\n   * Si el ID de la acción no es especificado, el [[yii\\base\\Controller::defaultAction|ID de la acción por defecto]] será utilizado.\n   * Si el ID de la acción es encontrado en el [[yii\\base\\Controller::actions()|mapeo de acciones]], una acción independiente\n     será creada;\n   * Si el ID de la acción es coincide con un método de acción, una acción en línea será creada;\n   * De otra manera, se lanzará una excepción [[yii\\base\\InvalidRouteException]].\n3. El controlador llama secuencialmente al método `beforeAction()` de la aplicación, al del módulo (si el controlador\n   pertenece a uno) y al del controlador.\n   * Si alguna de las llamadas devuelve `false`, el resto de los llamados subsiguientes a `beforeAction()` serán saltados y\n     la ejecución de la acción será cancelada.\n   * Por defecto, cada llamada al método `beforeAction()` lanzará un evento `beforeAction` al cual le puedes conectar un manejador.\n4. El controlador ejecuta la acción:\n   * Los parámetros de la acción serán analizados y poblados con los datos del `request`;\n5. El controlador llama secuencialmente al método `afterAction()` del controlador, del módulo (si el controlador\n   pertenece a uno) y de la aplicación.\n   * Por defecto, cada llamada al método `afterAction()` lanzará un evento `afterAction` al cual le puedes conectar un manejador.\n6. La aplicación tomará el resultado de la acción y lo asignará al [response](runtime-responses.md).\n\n\n## Buenas Prácticas <span id=\"best-practices\"></span>\n\nEn una aplicación bien diseñada, los controladores son a menudo muy pequeños con cada acción conteniendo unas pocas líneas de código.\nSi tu controlador se torna muy complejo, es usualmente un indicador de que deberías realizar una refactorización y mover algo de\ncódigo a otras clases.\n\nEn resumen, los controladores\n\n* pueden acceder a los datos del [request](runtime-requests.md);\n* puede llamar a métodos del [modelo](structure-models.md) y otros componentes con data del `request`;\n* pueden utilizar [vistas](structure-views.md) para componer `responses`;\n* NO debe procesar datos del `request - esto debe ser realizado en los [modelos](structure-models.md);\n* deberían evitar insertar HTML o cualquier código de presentación - para esto están las [vistas](structure-views.md).\n"
  },
  {
    "path": "docs/guide-es/structure-entry-scripts.md",
    "content": "Scripts de Entrada\n==================\n\nLos scripts de entrada son el primer eslabón en el proceso de arranque de la aplicación. Una aplicación (ya sea una \naplicación Web o una aplicación de consola) tiene un único script de entrada. Los usuarios finales hacen peticiones al \nscript de entrada que instancia instancias de aplicación y remite la petición a estos.\n\nLos scripts de entrada para aplicaciones Web tiene que estar alojado bajo niveles de directorios accesibles para la Web \nde manera que puedan ser accesibles para los usuarios finales. Normalmente se nombra como `index.php`, pero también se \npueden usar cualquier otro nombre, los servidores Web proporcionados pueden localizarlo.\n\nEl script de entrada para aplicaciones de consola normalmente está alojado bajo la \n[ruta base](structure-applications.md) de las aplicaciones y es nombrado como `yii` (con el sufijo `.php`). Estos \ndeberían ser ejecutables para que los usuarios puedan ejecutar las aplicaciones de consola a través del comando \n`./yii <ruta> [argumentos] [opciones]`.\n\nEl script de entrada principalmente hace los siguientes trabajos:\n\n* Definir las constantes globales;\n* Registrar el [cargador automático de Composer](https://getcomposer.org/doc/01-basic-usage.md#autoloading);\n* Incluir el archivo de clase [[Yii]];\n* Cargar la configuración de la aplicación;\n* Crear y configurar una instancia de [aplicación](structure-applications.md);\n* Llamar a [[yii\\base\\Application::run()]] para procesar la petición entrante.\n\n## Aplicaciones Web <span id=\"web-applications\"></span>\n\nEl siguiente código es el script de entrada para la [Plantilla de Aplicación web Básica](start-installation.md).\n\n```php\n<?php\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n\n// registrar el cargador automático de Composer\nrequire __DIR__ . '/../vendor/autoload.php';\n\n// incluir el fichero de clase Yii\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n\n// cargar la configuración de la aplicación\n$config = require __DIR__ . '/../config/web.php';\n\n// crear, configurar y ejecutar la aplicación\n(new yii\\web\\Application($config))->run();\n```\n\n## Aplicaciones de consola <span id=\"console-applications\"></span>\n\nDe la misma manera, el siguiente código es el script de entrada para la [aplicación de consola](tutorial-console.md):\n\n```php\n#!/usr/bin/env php\n<?php\n/**\n * Yii console bootstrap file.\n *\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\n\n// registrar el cargador automático de Composer\nrequire __DIR__ . '/vendor/autoload.php';\n\n// incluir el fichero de clase Yii\nrequire __DIR__ . '/vendor/yiisoft/yii2/Yii.php';\n\n// cargar la configuración de la aplicación\n$config = require __DIR__ . '/config/console.php';\n\n$application = new yii\\console\\Application($config);\n$exitCode = $application->run();\nexit($exitCode);\n```\n\n## Definición de Constantes <span id=\"defining-constants\"></span>\n\nEl script de entrada es el mejor lugar para definir constantes globales. Yii soporta las siguientes tres constantes:\n\n* `YII_DEBUG`: especifica si la aplicación se está ejecutando en modo depuración. Cuando esta en modo depuración, una \naplicación mantendrá más información de registro, y revelará detalladas pilas de errores si se lanza una excepción. Por \nesta razón, el modo depuración debería ser usado principalmente durante el desarrollo. El valor por defecto de \n'YII_DEBUG' es falso.\n* `YII_ENV`: especifica en que entorno se esta ejecutando la aplicación. Se puede encontrar una descripción más \ndetallada en la sección [Configuraciones](concept-configurations.md#environment-constants).\nEl Valor por defecto de `YII_ENV` es `'prod'`, que significa que la aplicación se esta ejecutando en el entorno de \nproducción.\n* `YII_ENABLE_ERROR_HANDLER`: especifica si se habilita el gestor de errores proporcionado por Yii. El valor \npredeterminado de esta constante es verdadero.\n\nCuando se define una constante, a menudo se usa código como el siguiente:\n\n```php\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\n```\n\nque es equivalente al siguiente código:\n\n```php\nif (!defined('YII_DEBUG')) {\n    define('YII_DEBUG', true);\n}\n```\n\nClaramente el primero es más breve y fácil de entender.\n\nLa definición de constantes debería hacerse al principio del script de entrada para que pueda tener efecto cuando se \nincluyan otros archivos PHP.\n"
  },
  {
    "path": "docs/guide-es/structure-extensions.md",
    "content": "Extensiones\n===========\n\nLas extensiones son paquetes de software redistribuibles diseñados especialmente para ser usados en aplicaciones Yii y\nproporcionar características listas para ser usadas. Por ejemplo, la extensión [yiisoft/yii2-debug](tool-debugger.md)\nañade una practica barra de herramientas de depuración (debug toolbar) al final de cada página de la aplicación para\nayudar a comprender más fácilmente como se han generado las páginas. Se pueden usar extensiones para acelerar el\nproceso de desarrollo. También se puede empaquetar código propio para compartir nuestro trabajo con otra gente.\n\n> Info: Usamos el termino \"extensión\" para referirnos a los paquetes específicos de software Yii. Para\n  propósitos generales los paquetes de software pueden usarse sin Yii, nos referiremos a ellos usando los términos\n  \"paquetes\" (package) o \"librerías\" (library).\n\n## Uso de Extensiones <span id=\"using-extensions\"></span>\n\nPara usar una extension, primero tenemos que instalarla. La mayoría de extensiones se usan como paquetes\n[Composer](https://getcomposer.org/) que se pueden instalar mediante los dos simples siguientes pasos:\n\n1. modificar el archivo `composer.json` de la aplicación y especificar que extensiones (paquetes Composer) se quieren\n   instalar.\n2. ejecutar `composer install` para instalar las extensiones especificadas.\n\nHay que tener en cuenta que es necesaria la instalación de [Composer](https://getcomposer.org/) si no la tenemos\ninstalada.\n\nDe forma predeterminada, Composer instala los paquetes registrados en [Packagist](https://packagist.org/) que es el\nrepositorio más grande de paquetes Composer de código abierto (open source). Se pueden buscar extensiones en\nPackagist. También se puede crear un [repositorio propio](https://getcomposer.org/doc/05-repositories.md#repository) y\nconfigurar Composer para que lo use. Esto es práctico cuando se desarrollan extensiones privadas que se quieran\ncompartir a través de otros proyectos.\n\nLas extensiones instaladas por Composer se almacenan en el directorio `BasePath/vendor`, donde `BasePath` hace\nreferencia a la [ruta base (base path)](structure-applications.md#basePath) de la aplicación. Ya que Composer es un\ngestor de dependencias, cuando se instala un paquete, también se instalarán todos los paquetes de los que dependa.\n\nPor ejemplo, para instalar la extensión `yiisoft/yii2-imagine`, modificamos el archivo `composer.json` como se muestra\na continuación:\n\n```json\n{\n    // ...\n\n    \"require\": {\n        // ... otras dependencias\n\n        \"yiisoft/yii2-imagine\": \"~2.0.0\"\n    }\n}\n```\n\nDespués de la instalación, debemos encontrar el directorio `yiisoft/yii2-imagine` dentro del directorio\n`BasePath/vendor`. También debemos encontrar el directorio `imagine/imagine` que contiene sus paquetes dependientes\ninstalados.\n\n> Info: La extensión `yiisoft/yii2-imagine` es una extensión del núcleo (core) desarrollada y mantenida por el\n  equipo de desarrollo de Yii. Todas las extensiones del núcleo se hospedan en [Packagist](https://packagist.org/) y\n  son nombradas como `yiisoft/yii2-xyz`, donde `zyz` varia según la extensión.\n\nAhora ya podemos usar las extensiones instaladas como si fueran parte de nuestra aplicación. El siguiente ejemplo\nmuestra como se puede usar la clase `yii\\imagine\\Image` proporcionada por la extensión `yiisoft/yii2-imagine`:\n\n```php\nuse Yii;\nuse yii\\imagine\\Image;\n\n// genera una miniatura (thumbnail) de la imagen\nImage::thumbnail('@webroot/img/test-image.jpg', 120, 120)\n    ->save(Yii::getAlias('@runtime/thumb-test-image.jpg'), ['quality' => 50]);\n```\n\n> Info: Las clases de extensiones se cargan automáticamente gracias a\n  [autocarga de clases de Yii](concept-autoloading.md).\n\n### Instalación Manual de Extensiones <span id=\"installing-extensions-manually\"></span>\n\nEn algunas ocasiones excepcionales es posible que tengamos que instalar alguna o todas las extensiones manualmente, en lugar de utilizar Composer. Para lograrlo, debemos:\n\n\n1. descargar los archivos de la extensión y descomprimirlos en la carpeta `vendor`.\n2. instalar la clase de autocarga proporcionada por las extensiones, si existe.\n3. descargar e instalar todas las extensiones dependientes como siguiendo estas mismas instrucciones.\n\nSi una extensión no proporciona clase de autocarga pero sigue el estándar\n[PSR-4](https://www.php-fig.org/psr/psr-4/),  se puede usar la clase de autocarga proporcionada por Yii para cargar\nautomáticamente las clases de las extensiones. Todo lo que se tiene que hacer es declarar un\n[alias de raíz (root)](concept-aliases.md#defining-aliases)  para las extensiones del directorio raíz. Por ejemplo,\nasumiendo que tenemos instalada una extensión en el directorio `vendor/mycompany/myext`, y las clases de extensión se\nencuentran en el namespace `myext`, entonces podemos incluir el siguiente código en nuestra configuración de\naplicación:\n\n```php\n[\n    'aliases' => [\n        '@myext' => '@vendor/mycompany/myext',\n    ],\n]\n```\n\n## Creación de Extensiones <span id=\"creating-extensions\"></span>\n\nPodemos considerar la creación de una extensión cuando tengamos la necesidad de compartir nuestro código. Cada\nextensión puede contener el código que se desee, puede ser una clase de ayuda (helper class), un widget, un módulo,\netc.\n\nSe recomienda crear una extensión como [paquetes de Composer](https://getcomposer.org/) para que sea se pueda\ninstalarse más fácilmente por los otros usuarios, como se ha descrito en la anterior subsección.\n\nMás adelante se encuentran los pasos básicos que deben seguirse para crear una extensión como paquete Composer.\n\n1. Crear un proyecto para la extensión y alojarlo en un repositorio con VCS (Sistema de Control de Versiones), como\n   puede ser [github.com](https://github.com). El trabajo de desarrollo y el mantenimiento debe efectuarse en este\n   repositorio.\n2. En el directorio raíz del repositorio debe encontrarse el archivo `composer.json` que es requerido por Composer. Se\n   pueden encontrar más detalles en la siguiente subsección.\n3. Registrar la extensión en un repositorio de Composer como puede ser [Packagist](https://packagist.org/), para que\n   los otros usuarios puedan encontrarlo e instalarla mediante Composer.\n\n### `composer.json` <span id=\"composer-json\"></span>\n\nCada paquete de Composer tiene que tener un archivo `composer.json` en su directorio raíz. El archivo contiene los\nmetadatos relacionados con el paquete. Se pueden encontrar especificaciones completas acerca de este fichero en el\n[Manual de Composer](https://getcomposer.org/doc/01-basic-usage.md#composer-json-project-setup). El siguiente ejemplo\nmuestra el archivo `composer.json` para la extensión `yiisoft/yii2-imagine`:\n\n```json\n{\n    // nombre del paquete\n    \"name\": \"yiisoft/yii2-imagine\",\n\n    // tipo de paquete\n    \"type\": \"yii2-extension\",\n\n    \"description\": \"The Imagine integration for the Yii framework\",\n    \"keywords\": [\"yii2\", \"imagine\", \"image\", \"helper\"],\n    \"license\": \"BSD-3-Clause\",\n    \"support\": {\n        \"issues\": \"https://github.com/yiisoft/yii2/issues?labels=ext%3Aimagine\",\n        \"forum\": \"https://forum.yiiframework.com/\",\n        \"wiki\": \"https://www.yiiframework.com/wiki/\",\n        \"irc\": \"ircs://irc.libera.chat:6697/yii\",\n        \"source\": \"https://github.com/yiisoft/yii2\"\n    },\n    \"authors\": [\n        {\n            \"name\": \"Antonio Ramirez\",\n            \"email\": \"amigo.cobos@gmail.com\"\n        }\n    ],\n\n    // dependencias del paquete\n    \"require\": {\n        \"yiisoft/yii2\": \"~2.0.0\",\n        \"imagine/imagine\": \"v0.5.0\"\n    },\n\n    // especificaciones de la autocarga de clases\n    \"autoload\": {\n        \"psr-4\": {\n            \"yii\\\\imagine\\\\\": \"\"\n        }\n    }\n}\n```\n\n#### Nombre del Paquete<span id=\"package-name\"></span>\n\nCada paquete Composer debe tener un nombre de paquete que identifique de entre todos los otros paquetes. El formato\ndel nombre del paquete es `nombreProveedor/nombreProyecto`. Por ejemplo, el nombre de paquete `yiisoft/yii2-imagine`,\nel nombre del proveedor es `yiisoft` y el nombre del proyecto es `yii2-imagine`.\n\nNO se puede usar el nombre de proveedor `yiisoft` ya que está reservado para el usarse para el código del núcleo\n(core) de Yii.\n\nRecomendamos usar el prefijo `yii2-` al nombre del proyecto para paquetes que representen extensiones de Yii 2, por\nejemplo, `minombre/yii2-miwidget`. Esto permite ver a los usuarios más fácilmente si un paquete es una extensión de\nYii 2.\n\n#### Tipo de Paquete <span id=\"package-type\"></span>\n\nEs importante que se especifique el tipo del paquete de la extensión como `yii2-extension` para que el paquete pueda\nser reconocido como una extensión de Yii cuando se esté instalando.\n\nCuando un usuario ejecuta `composer install` para instalar una extensión, el archivo `vendor/yiisoft/extensions.php`\nse actualizará automáticamente para incluir la información acerca de la nueva extensión. Desde este archivo, las\naplicaciones Yii pueden saber que extensiones están instaladas. (se puede acceder a esta información mediante\n[[yii\\base\\Application::extensions]]).\n\n#### Dependencias <span id=\"dependencies\"></span>\n\nLa extensión depende de Yii (por supuesto). Por ello se debe añadir (`yiisoft/yii2`) a la lista en la entrada\n`required` del archivo `composer.json`. Si la extensión también depende de otras extensiones o de terceras\n(third-party) librerías, también se deberán listar. Debemos asegurarnos de anotar las restricciones de versión\napropiadas (ej. `1.*`, `@stable`) para cada paquete dependiente. Se deben usar dependencias estables en versiones\nestables de nuestras extensiones.\n\nLa mayoría de paquetes JavaScript/CSS se gestionan usando [Bower](https://bower.io/) y/o [NPM](https://www.npmjs.com/),\nen lugar de Composer. Yii utiliza el [Composer asset plugin](https://github.com/fxpio/composer-asset-plugin)\n para habilitar la gestión de estos tipos de paquetes a través de Composer. Si la extensión depende de un paquete\n Bower, se puede, simplemente, añadir la dependencia de el archivo `composer.json` como se muestra a continuación:\n\n```json\n{\n    // dependencias del paquete\n    \"require\": {\n        \"bower-asset/jquery\": \">=1.11.*\"\n    }\n}\n```\n\nEl código anterior declara que tela extensión depende del paquete Bower `jquery`. En general, se puede usar\n`bower-asset/NombrePaquete` para referirse al paquete en `composer.json`, y usar `npm-asset/NombrePaquete` para\nreferirse a paquetes NPM. Cuando Composer instala un paquete Bower o NPM, de forma predeterminada los contenidos de\nlos paquetes se instalarán en `@vendor/bower/NombrePaquete` y `@vendor/npm/Packages` respectivamente. Podemos hacer\nreferencia a estos dos directorios usando los alias `@bower/NombrePaquete` and `@npm/NombrePaquete`.\n\nPara obtener más detalles acerca de la gestión de assets, puede hacerse referencia a la sección\n[Assets](structure-assets.md#bower-npm-assets).\n\n#### Autocarga de Clases <span id=\"class-autoloading\"></span>\n\nPara que se aplique la autocarga a clases propias mediante la autocarga de clases de Yii o la autocarga de clases de\nComposer, debemos especificar la entrada `autoload` en el archivo `composer.json` como se puede ver a continuación:\n\n```json\n{\n    // ....\n\n    \"autoload\": {\n        \"psr-4\": {\n            \"yii\\\\imagine\\\\\": \"\"\n        }\n    }\n}\n```\n\nSe pueden añadir una o más namespaces raíz y sus correspondientes rutas de archivo.\n\nCuando se instala la extensión en una aplicación, Yii creara un [alias](concept-aliases.md#extension-aliases) para\ntodos los namespaces raíz, que harán referencia al directorio correspondiente del namespace. Por ejemplo, la anterior\ndeclaración `autoload` corresponderá a un alias llamado `@yii/imagine`.\n\n### Prácticas Recomendadas <span id=\"recommended-practices\"></span>\n\nDado que las extensiones están destinadas a ser utilizadas por otras personas, a menudo es necesario hacer un esfuerzo\nextra durante el desarrollo. A continuación presentaremos algunas practicas comunes y recomendadas para la creación de\nextensiones de alta calidad.\n\n#### Namespaces <span id=\"namespaces\"></span>\n\nPara evitar colisiones de nombres y permitir que las clases usen la autocarga en extensiones propias, se deben usar\nnamespaces y nombres de clase siguiendo el [estándar PSR-4](https://www.php-fig.org/psr/psr-4/) o el\n[estándar PSR-0](https://www.php-fig.org/psr/psr-0/).\n\nLos namespaces de clases propias deben empezar por `NombreProveedor\\NombreExtension` donde `NombreExtension` es\nsimilar al nombre del paquete pero este no debe contener el prefijo `yii2-`. Por ejemplo, para la extensión\n`yiisoft/yii2-imagine`, usamos `yii\\imagine` como namespace para sus clases.\n\nNo se puede usar `yii`, `yii2` o `yiisoft` como nombre de proveedor. Estos nombres están reservados para usarse en el\ncódigo del núcleo de Yii.\n\n#### Clases de Bootstrapping <span id=\"bootstrapping-classes\"></span>\n\nA veces, se puede querer que nuestras extensiones ejecuten algo de código durante el\n[proceso de bootstrapping](runtime-bootstrapping.md) de una aplicación. Por ejemplo, queremos que nuestra extensión\nresponda a un evento `beginRequest` de la aplicación para ajustar alguna configuración de entorno. Aunque podemos\nindicar a los usuarios de la extensión que añadan nuestro gestor de eventos para que capture `beginRequest`, es mejor\nhacerlo automáticamente.\n\nPara llevarlo a cabo, podemos crear una *clase de bootstrpping* para implementar [[yii\\base\\BootstrapInterface]]. Por\nejemplo,\n\n```php\nnamespace myname\\mywidget;\n\nuse yii\\base\\BootstrapInterface;\nuse yii\\base\\Application;\n\nclass MyBootstrapClass implements BootstrapInterface\n{\n    public function bootstrap($app)\n    {\n        $app->on(Application::EVENT_BEFORE_REQUEST, function () {\n             // do something here\n        });\n    }\n}\n```\n\nEntonces se tiene que añadir esta clase en la lista del archivo `composer.json` de la extensión propia como se muestra\na continuación,\n\n```json\n{\n    // ...\n\n    \"extra\": {\n        \"bootstrap\": \"myname\\\\mywidget\\\\MyBootstrapClass\"\n    }\n}\n```\n\nCuando se instala la extensión en la aplicación, Yii automáticamente instancia la clase de bootstrapping y llama a su\nmétodo [[yii\\base\\BootstrapInterface::bootstrap()|bootstrap()]] durante el proceso de bootstrapping para cada petición.\n\n#### Trabajar con Bases de Datos<span id=\"working-with-databases\"></span>\n\nPuede darse el caso en que la extensión necesite acceso a bases de datos. No se debe asumir que las aplicaciones que\nusen la extensión siempre usarán `Yii::$db` como conexión de BBDD. Se debe crear una propiedad `db` para las clases\nque requieran acceso a BBDD. La propiedad permitirá a los usuarios de nuestra extensión elegir que conexión quieren\nque use nuestra extensión. Como ejemplo, se puede hacer referencia a la clase [[yii\\caching\\DbCache]] y observar como\ndeclara y utiliza la propiedad `db`.\n\nSi nuestra extensión necesita crear tablas especificas en la BBDD o hacer cambios en el esquema de la BBDD, debemos:\n\n- proporcionar [migraciones](db-migrations.md) para manipular el esquema de la BBDD, en lugar de utilizar archivos con\n  sentencias SQL;\n- intentar hacer las migraciones aplicables a varios Sistemas de Gestión de BBDD;\n- evitar usar [Active Record](db-active-record.md) en las migraciones.\n\n#### Uso de Assets <span id=\"using-assets\"></span>\n\nSi nuestra aplicación es un widget o un módulo, hay posibilidades de que requiera [assets](structure-assets.md) para\npoder funcionar. Por ejemplo, un modulo puede mostrar algunas páginas de que contengan archivos JavaScript y/o CSS.\nDebido a que los archivos de las extensiones se encuentran en la misma ubicación y no son accesibles por la Web cuando\nse instalan en una aplicación, hay dos maneras de hacer los assets accesibles vía Web:\n\n- pedir a los usuarios que copien manualmente los archivos assets en un directorio público de la Web.\n- declarar un [asset bundle](structure-assets.md) dejar que el mecanismo de publicación se encargue automáticamente de\n  copiar los archivos que se encuentren en el asset bundle a un directorio Web público.\n\nRecomendamos el uso de la segunda propuesta para que la extensión sea más fácil de usar para usuarios. Se puede hacer\nreferencia a la sección [Assets](structure-assets.md) para encontrar más detalles acerca de como trabajar con ellos.\n\n#### Internacionalización y Localización <span id=\"i18n-l10n\"></span>\n\nPuede que las extensiones propias se usen en aplicaciones que den soporte a diferentes idiomas! Por ello, si nuestra\nextensión muestra contenido a los usuarios finales, se debe intentar [internacionalizar y localizar](tutorial-i18n.md)\nla extensión. En particular,\n\n- Si la extensión muestra mensajes destinados a usuarios finales, los mensajes deben mostrarse usando `Yii::t()` para\n  que puedan ser traducidos. Los mensajes dirigidos a desarrolladores (como mensajes de excepciones internas) no\n  necesitan ser traducidos.\n- Si la extensión muestra números, fechas, etc., deben ser formateados usando [[yii\\i18n\\Formatter]] siguiendo las\n  reglas de formato adecuadas.\n\nSe pueden encontrar más detalles en la sección [internacionalización](tutorial-i18n.md).\n\n#### Testing <span id=\"testing\"></span>\n\nPara conseguir que las aplicaciones propias se ejecuten sin problemas y no causen problemas a otros usuarios, deben\nejecutarse test a las extensiones antes de ser publicadas al público.\n\nSe recomienda crear varios casos de prueba (test cases) para probar el código de nuestra extensión en lugar de\nejecutar pruebas manuales. Cada vez que se vaya a lanzar una nueva versión, simplemente podemos ejecutar estos casos\nde prueba para asegurarnos de que todo está correcto. Yii proporciona soporte para testing que puede ayudar a escribir\npruebas unitarias (unit tests), pruebas de aceptación (acceptance tests) y pruebas de funcionalidad\n(functionality tests), más fácilmente. Se pueden encontrar más detalles en la sección [Testing](test-overview.md).\n\n#### Versiones <span id=\"versioning\"></span>\n\nSe debe asignar un número de versión cada vez que se lance una nueva distribución. (ej. `1.0.1`). Recomendamos\nseguir la práctica [Versionamiento Semántico](https://semver.org/lang/es/) para determinar que números se deben usar.\n\n#### Lanzamientos <span id=\"releasing\"></span>\n\nPara dar a conocer nuestra extensión a terceras personas, debemos lanzara al público.\n\nSi es la primera vez que se realiza un lanzamiento de una extensión, debemos registrarla en un repositorio Composer\ncomo puede ser [Packagist](https://packagist.org/). Después de estos, todo lo que tenemos que hacer es crear una\netiqueta (tag) (ej. `v1.0.1`) en un repositorio con VCS (Sistema de Control de Versiones) y notificarle al\nrepositorio Composer el nuevo lanzamiento. Entonces la gente podrá encontrar el nuevo lanzamiento y instalar o\nactualizar la extensión a mediante el repositorio Composer.\n\nEn los lanzamientos de una extensión, además de archivos de código, también se debe considerar la inclusión los puntos\nmencionados a continuación para facilitar a otra gente el uso de nuestra extensión:\n\n* Un archivo léame (readme) en el directorio raíz: describe que hace la extensión y como instalarla y utilizarla.\n  Recomendamos que se escriba en formato [Markdown](https://daringfireball.net/projects/markdown/) y llamarlo\n  `readme.md`.\n* Un archivo de registro de cambios (changelog) en el directorio raíz: enumera que cambios se realizan en cada\n  lanzamiento. El archivo puede escribirse en formato Markdown y llamarlo `changelog.md`.\n* Un archivo de actualización (upgrade) en el directorio raíz: da instrucciones de como actualizar desde lanzamientos\n  antiguos de la extensión. El archivo puede escribirse en formato Markdown y llamarlo `upgrade.md`.\n* Tutoriales, demostraciones, capturas de pantalla, etc: son necesarios si nuestra extensión proporciona muchas\n  características que no pueden ser detalladas completamente en el archivo `readme`.\n* Documentación de API: el código debe documentarse debidamente para que otras personas puedan leerlo y entenderlo\n  fácilmente. Más información acerca de documentación de código en\n  [archivo de Objetos de clase](https://github.com/yiisoft/yii2/blob/master/framework/base/BaseObject.php)\n\n> Info: Los comentarios de código pueden ser escritos en formato Markdown. La extensión `yiisoft/yii2-apidoc`\n  proporciona una herramienta para generar buena documentación de API basándose en los comentarios del código.\n\n> Info: Aunque no es un requerimiento, se recomienda que la extensión se adhiera a ciertos estilos de\n  codificación. Se puede hacer referencia a\n  [estilo de código del núcleo del framework](https://github.com/yiisoft/yii2/blob/master/docs/internals/core-code-style.md) para\n  obtener más detalles.\n\n## Extensiones del Núcleo <span id=\"core-extensions\"></span>\n\nYii proporciona las siguientes extensiones del núcleo que son desarrolladas y mantenidas por el equipo de desarrollo\nde Yii. Todas ellas están registradas en [Packagist](https://packagist.org/) y pueden ser instaladas fácilmente como\nse describe en la subsección [Uso de Extensiones](#using-extensions)\n\n- [yiisoft/yii2-apidoc](https://github.com/yiisoft/yii2-apidoc):proporciona un generador de documentación de APIs\n  extensible y de de alto rendimiento.\n- [yiisoft/yii2-authclient](https://github.com/yiisoft/yii2-authclient):proporciona un conjunto de clientes de\n  autorización tales como el cliente OAuth2 de Facebook, el cliente GitHub OAuth2.\n- [yiisoft/yii2-bootstrap](https://github.com/yiisoft/yii2-bootstrap): proporciona un conjunto de widgets que\n  encapsulan los componentes y plugins de [Bootstrap](https://getbootstrap.com/).\n- [yiisoft/yii2-debug](https://github.com/yiisoft/yii2-debug): proporciona soporte de depuración para aplicaciones\n  Yii. Cuando se usa esta extensión, aparece una barra de herramientas de depuración en la parte inferior de cada\n  página. La extensión también proporciona un conjunto de páginas para mostrar información detallada de depuración.\n- [yiisoft/yii2-elasticsearch](https://github.com/yiisoft/yii2-elasticsearch): proporciona soporte para usar\n  [Elasticsearch](https://www.elastic.co/). Incluye soporte básico para realizar consultas/búsquedas y también\n  implementa patrones de [Active Record](db-active-record.md) que permiten y permite guardar los `active records` en\n  Elasticsearch.\n- [yiisoft/yii2-faker](https://github.com/yiisoft/yii2-faker): proporciona soporte para usar\n  [Faker](https://github.com/fzaninotto/Faker) y generar datos automáticamente.\n- [yiisoft/yii2-gii](https://github.com/yiisoft/yii2-gii): proporciona un generador de código basado den Web altamente\n  extensible y que puede usarse para generar modelos, formularios, módulos, CRUD, etc. rápidamente.\n- [yiisoft/yii2-httpclient](https://github.com/yiisoft/yii2-httpclient):\n  provides an HTTP client.\n- [yiisoft/yii2-imagine](https://github.com/yiisoft/yii2-imagine): proporciona funciones comunes de manipulación de\n  imágenes basadas en [Imagine](https://imagine.readthedocs.org/).\n- [yiisoft/yii2-jui](https://github.com/yiisoft/yii2-jui): proporciona un conjunto de widgets que encapsulan las\n  iteraciones y widgets de [JQuery UI](https://jqueryui.com/).\n- [yiisoft/yii2-mongodb](https://github.com/yiisoft/yii2-mongodb): proporciona soporte para utilizar\n  [MongoDB](https://www.mongodb.com/). incluye características como consultas básicas, Active Record, migraciones,\n  caching, generación de código, etc.\n- [yiisoft/yii2-redis](https://github.com/yiisoft/yii2-redis): proporciona soporte para utilizar\n  [redis](https://redis.io/). incluye características como consultas básicas, Active Record, caching, etc.\n- [yiisoft/yii2-smarty](https://github.com/yiisoft/yii2-smarty): proporciona un motor de plantillas basado en\n  [Smarty](https://www.smarty.net/).\n- [yiisoft/yii2-sphinx](https://github.com/yiisoft/yii2-sphinx): proporciona soporte para utilizar\n  [Sphinx](https://sphinxsearch.com). incluye características como consultas básicas, Active Record, code generation,\n  etc.\n- [yiisoft/yii2-swiftmailer](https://github.com/yiisoft/yii2-swiftmailer): proporciona características de envío de\n  correos electrónicos basadas en [swiftmailer](https://swiftmailer.symfony.com/).\n- [yiisoft/yii2-twig](https://github.com/yiisoft/yii2-twig): proporciona un motor de plantillas basado en\n  [Twig](https://twig.symfony.com/).\n"
  },
  {
    "path": "docs/guide-es/structure-filters.md",
    "content": "Filtros\n=======\n\nLos Filtros (filters) son objetos que se ejecutan antes y/o después de las \n[acciones de controlador](structure-controllers.md#actions). Por ejemplo, un filtro de control de acceso puede \nejecutarse antes de las acciones para asegurar que un usuario final tiene permitido acceder a estas; un filtro de \ncompresión de contenido puede ejecutarse después de las acciones para comprimir el contenido de la respuesta antes de \nser enviado al usuario final.\n\nUn filtro puede consistir en un pre-filtro (lógica de filtrado aplicada *antes* de las acciones) y/o un post-filtro \n(lógica de filtro aplicada *después* de las acciones).\n\n## Uso de Filtros <span id=\"using-filters\"></span>\n\nLos filtros son esencialmente un tipo especial de [comportamientos (behaviors)](concept-behaviors.md).\nPor lo tanto, usar filtros es lo mismo que [uso de comportamientos](concept-behaviors.md#attaching-behaviors). Se \npueden declarar los filtros en una clase controlador sobrescribiendo el método \n[[yii\\base\\Controller::behaviors()|behaviors()]] como en el siguiente ejemplo:\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\HttpCache',\n            'only' => ['index', 'view'],\n            'lastModified' => function ($action, $params) {\n                $q = new \\yii\\db\\Query();\n                return $q->from('user')->max('updated_at');\n            },\n        ],\n    ];\n}\n```\n\nPor defecto, los filtros declarados en una clase controlador, serán aplicados en *todas* las acciones de este \ncontrolador. Sin embargo, se puede especificar explícitamente en que acciones serán aplicadas configurando la \npropiedad [[yii\\base\\ActionFilter::only|only]]. En el anterior ejemplo, el filtro 'HttpCache' solo se aplica a las \nacciones 'index' y 'view'. También se puede configurar la propiedad [[yii\\base\\ActionFilter::except|except]] para \nprevenir que ciertas acciones sean filtradas.\n\nAdemás de en los controladores, se pueden declarar filtros en [módulos](structure-modules.md) o \n[aplicaciones](structure-applications.md).\nUna vez hecho, los filtros serán aplicados a *todas* las acciones de controlador que pertenezcan a ese modulo o \naplicación, a menos que las propiedades [[yii\\base\\ActionFilter::only|only]] y [[yii\\base\\ActionFilter::except|except]]\n sean configuradas como se ha descrito anteriormente.\n\n> Note: Cuando se declaran filtros en módulos o aplicaciones, deben usarse [rutas](structure-controllers.md#routes) en \n  lugar de IDs de acciones en las propiedades [[yii\\base\\ActionFilter::only|only]] y \n  [[yii\\base\\ActionFilter::except|except]]. Esto es debido a que los IDs de acciones no pueden especificar acciones \n  dentro del ámbito de un modulo o una aplicación por si mismos.\n\nCuando se configuran múltiples filtros para una misma acción, se aplican de acuerdo a las siguientes reglas:\n\n* Pre-filtrado\n    - Aplica filtros declarados en la aplicación en orden de aparición en `behaviors()`.\n    - Aplica filtros declarados en el modulo en orden de aparición en `behaviors()`.\n    - Aplica filtros declarados en el controlador en orden de aparición en `behaviors()`.\n    - Si hay algún filtro que cancele la ejecución de la acción, los filtros(tanto pre-filtros como post-filtros) \n      posteriores a este no serán aplicados.\n* Ejecución de la acción si pasa el pre-filtro.\n* Post-filtrado\n    - Aplica los filtros declarados en el controlador en el controlador en orden inverso al de aparición en \n      `behaviors()`.\n    - Aplica los filtros declarados en el modulo en orden inverso al de aparición en `behaviors()`.\n    - Aplica los filtros declarados en la aplicación en orden inverso al de aparición en `behaviors()`.\n\n##Creación de Filtros <span id=\"creating-filters\"></span>\n\nPara crear un nuevo filtro de acción, hay que extender a [[yii\\base\\ActionFilter]] y sobrescribir los métodos \n[[yii\\base\\ActionFilter::beforeAction()|beforeAction()]] y/o [[yii\\base\\ActionFilter::afterAction()|afterAction()]]. \nEl primero será ejecutado antes de la acción mientras que el segundo lo hará una vez ejecutada la acción.\nEl valor devuelto por [[yii\\base\\ActionFilter::beforeAction()|beforeAction()]] determina si una acción debe ejecutarse \no no. Si el valor es falso, los filtros posteriores a este serán omitidos y la acción no será ejecutada.\n\nEl siguiente ejemplo muestra un filtro que registra el tiempo de ejecución de una acción:\n\n```php\nnamespace app\\components;\n\nuse Yii;\nuse yii\\base\\ActionFilter;\n\nclass ActionTimeFilter extends ActionFilter\n{\n    private $_startTime;\n\n    public function beforeAction($action)\n    {\n        $this->_startTime = microtime(true);\n        return parent::beforeAction($action);\n    }\n\n    public function afterAction($action, $result)\n    {\n        $time = microtime(true) - $this->_startTime;\n        Yii::debug(\"Action '{$action->uniqueId}' spent $time second.\");\n        return parent::afterAction($action, $result);\n    }\n}\n```\n\n## Filtros del Núcleo <span id=\"core-filters\"></span>\n\nYii proporciona una serie de filtros de uso general, que se encuentran principalmente en `yii\\filters` namespace. En \nadelante introduciremos estos filtros brevemente.\n\n### [[yii\\filters\\AccessControl|AccessControl]] <span id=\"access-control\"></span>\n\nAccessControl proporciona control de acceso simple basado en un conjunto de [[yii\\filters\\AccessControl::rules|rules]].\nEn concreto, antes de ejecutar una acción, AccessControl examinará la lista de reglas y encontrará la primera que \nconcuerde con las actuales variables de contexto(tales como dirección IP de usuario, estado de inicio de sesión del \nusuario, etc.). La regla que concuerde dictara si se permite o deniega la ejecución de la acción solicitada. Si \nninguna regla concuerda, el acceso será denegado.\n\nEl siguiente ejemplo muestra como habilitar el acceso a los usuarios autenticados a las acciones 'create' y 'update' \nmientras deniega a todos los otros usuarios el acceso a estas dos acciones.\n\n\n```php\nuse yii\\filters\\AccessControl;\n\npublic function behaviors()\n{\n    return [\n        'access' => [\n            'class' => AccessControl::class,\n            'only' => ['create', 'update'],\n            'rules' => [\n                // permitido para usuarios autenticados\n                [\n                    'allow' => true,\n                    'roles' => ['@'],\n                ],\n                // todo lo demás se deniega por defecto\n            ],\n        ],\n    ];\n}\n```\n\nPara conocer más detalles acerca del control de acceso en general, refiérase a la sección de \n[Autorización](security-authorization.md)\n\n### Filtros del Método de Autenticación <span id=\"auth-method-filters\"></span>\n\nLos filtros del método de autenticación se usan para autenticar a un usuario utilizando varios métodos, tales como la \n[Autenticación de acceso básico HTTP](https://es.wikipedia.org/wiki/Autenticaci%C3%B3n_de_acceso_b%C3%A1sica), \n[Oauth 2](https://oauth.net/2/). Estas clases de filtros se encuentran en el espacio de nombres `yii\\filters\\auth`.\n\nEl siguiente ejemplo muestra como usar [[yii\\filters\\auth\\HttpBasicAuth]] para autenticar un usuario usando un token \nde acceso basado en el método de Autenticación de acceso básico HTTP. Tenga en cuenta que para que esto funcione, la \nclase [[yii\\web\\User::identityClass|user identity class]] debe implementar el método \n[[yii\\web\\IdentityInterface::findIdentityByAccessToken()|findIdentityByAccessToken()]].\n\n```php\nuse yii\\filters\\auth\\HttpBasicAuth;\n\npublic function behaviors()\n{\n    return [\n        'basicAuth' => [\n            'class' => HttpBasicAuth::class,\n        ],\n    ];\n}\n```\n\nLos filtros del método de autenticación se usan a menudo para implementar APIs RESTful. Para más detalles, por favor \nrefiérase a la sección [Autenticación RESTful](rest-authentication.md).\n\n[[yii\\filters\\ContentNegotiator|ContentNegotiator]] \nEl filtro ContentNegotiator da soporte a la negociación del formato de respuesta y a la negociación del idioma de la \naplicación. Este determinara el formato de respuesta y/o el idioma examinando los parámetros 'GET' y 'Accept' del \nencabezado HTTP.\n\nEn el siguiente ejemplo, el filtro ContentNegotiator se configura para soportar los formatos de respuesta 'JSON' y \n'XML', y los idiomas Ingles(Estados Unidos) y Alemán.\n\n```php\nuse yii\\filters\\ContentNegotiator;\nuse yii\\web\\Response;\n\npublic function behaviors()\n{\n    return [\n        [\n            'class' => ContentNegotiator::class,\n            'formats' => [\n                'application/json' => Response::FORMAT_JSON,\n                'application/xml' => Response::FORMAT_XML,\n            ],\n            'languages' => [\n                'en-US',\n                'de',\n            ],\n        ],\n    ];\n}\n```\n\nLos formatos de respuesta y los idiomas a menudo precisan ser determinados mucho antes durante el \n[ciclo de vida de la aplicación](structure-applications.md#application-lifecycle). Por esta razón, ContentNegotiator \nesta diseñado de tal manera que se pueda usar como componente de [bootstrapping](structure-applications.md#bootstrap) \nasí como de filtro. Por ejemplo, ContentNegotiator se puede configurar en la configuración de la aplicación como en el \nsiguiente ejemplo:\n\n```php\nuse yii\\filters\\ContentNegotiator;\nuse yii\\web\\Response;\n\n[\n    'bootstrap' => [\n        [\n            'class' => ContentNegotiator::class,\n            'formats' => [\n                'application/json' => Response::FORMAT_JSON,\n                'application/xml' => Response::FORMAT_XML,\n            ],\n            'languages' => [\n                'en-US',\n                'de',\n            ],\n        ],\n    ],\n];\n```\n\n> Info: En el caso que el tipo preferido de contenido y el idioma no puedan ser determinados por una petición, \n  será utilizando el primer elemento de formato e idioma de la lista [[formats]] y [[lenguages]].\n\n\n### [[yii\\filters\\HttpCache|HttpCache]] <span id=\"http-cache\"></span>\n\nHttpCache implementa un almacenamiento caché del lado del cliente utilizando las cabeceras HTTP 'Last-Modified' y \n'Etag'. Por ejemplo:\n\n```php\nuse yii\\filters\\HttpCache;\n\npublic function behaviors()\n{\n    return [\n        [\n            'class' => HttpCache::class,\n            'only' => ['index'],\n            'lastModified' => function ($action, $params) {\n                $q = new \\yii\\db\\Query();\n                return $q->from('user')->max('updated_at');\n            },\n        ],\n    ];\n}\n```\n\nPara conocer más detalles acerca de HttpCache refiérase a la sección [almacenamiento caché HTTP](caching-http.md).\n\n### [[yii\\filters\\PageCache|PageCache]] <span id=\"page-cache\"></span>\n\nPageCache implementa una caché por parte del servidor de paginas enteras. En el siguiente ejemplo, se aplica PageCache \na la acción 'index' para generar una cache de la pagina entera durante 60 segundos como máximo o hasta que el contador \nde entradas de la tabla 'post' varíe. También se encarga de almacenar diferentes versiones de la pagina dependiendo \ndel idioma de la aplicación seleccionado.\n\n```php\nuse yii\\filters\\PageCache;\nuse yii\\caching\\DbDependency;\n\npublic function behaviors()\n{\n    return [\n        'pageCache' => [\n            'class' => PageCache::class,\n            'only' => ['index'],\n            'duration' => 60,\n            'dependency' => [\n                'class' => DbDependency::class,\n                'sql' => 'SELECT COUNT(*) FROM post',\n            ],\n            'variations' => [\n                \\Yii::$app->language,\n            ]\n        ],\n    ];\n}\n```\n\nPor favor refiérase a [Caché de Páginas](caching-page.md) para obtener más detalles acerca de como usar PageCache.\n\n### [[yii\\filters\\RateLimiter|RateLimiter]] <span id=\"rate-limiter\"></span>\n\nRateLimiter implementa un algoritmo de para limitar la tasa de descarga basándose en \n[leaky bucket algorithm](https://en.wikipedia.org/wiki/Leaky_bucket). Este se utiliza sobre todo en la implementación \nde APIs RESTful. Por favor, refiérase a la sección [limite de tasa](rest-rate-limiting.md) para obtener más detalles \nacerca de el uso de este filtro.\n\n### [[yii\\filters\\VerbFilter|VerbFilter]] <span id=\"verb-filter\"></span>\n\nVerbFilter comprueba que los métodos de las peticiones HTTP estén permitidas para las acciones solicitadas. Si no \nestán permitidas, lanzara una excepción de tipo HTTP 405. En el siguiente ejemplo, se declara VerbFilter para \nespecificar el conjunto típico métodos de petición permitidos para acciones CRUD.\n\n```php\nuse yii\\filters\\VerbFilter;\n\npublic function behaviors()\n{\n    return [\n        'verbs' => [\n            'class' => VerbFilter::class,\n            'actions' => [\n                'index'  => ['get'],\n                'view'   => ['get'],\n                'create' => ['get', 'post'],\n                'update' => ['get', 'put', 'post'],\n                'delete' => ['post', 'delete'],\n            ],\n        ],\n    ];\n}\n```\n\n### [[yii\\filters\\Cors|Cors]] <span id=\"cors\"></span>\n\n[CORS](https://developer.mozilla.org/es/docs/Web/HTTP/CORS) es un mecanismo que permite a diferentes \nrecursos (por ejemplo: fuentes, JavaScript, etc) de una pagina Web ser solicitados por otro dominio diferente al \ndominio que esta haciendo la petición. En particular las llamadas AJAX de JavaScript pueden utilizar el mecanismo \nXMLHttpRequest. De otro modo esta petición de dominio cruzado seria prohibida por los navegadores Web, por la misma \npollita de seguridad de origen. CORS establece la manera en que el navegador y el servidor pueden interaccionar para \ndeterminar si se permite o no la petición de dominio cruzado. El filtro [[yii\\filters\\Cors|Cors filter]] puede ser \ndefinido antes de los filtros Autenticación / Autorización para asegurar que las cabeceras de CORS siempre serán \nenviadas.\n\n```php\nuse yii\\filters\\Cors;\nuse yii\\helpers\\ArrayHelper;\n\npublic function behaviors()\n{\n    return ArrayHelper::merge([\n        [\n            'class' => Cors::class,\n        ],\n    ], parent::behaviors());\n}\n```\n\nEl filtrado CORS puede ser ajustado utilizando la propiedad 'cors'.\n\n* `cors['Origin']`: array utilizado para definir los orígenes permitidos. Puede ser `['*']` (everyone) o \n  `['https://www.myserver.net', 'https://www.myotherserver.com']`. Por defecto `['*']`.\n* `cors['Access-Control-Request-Method']`: array de los verbos permitidos como `['GET', 'OPTIONS', 'HEAD']`.  Por \n  defecto `['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']`.\n* `cors['Access-Control-Request-Headers']`: array de las cabeceras permitidas. Puede ser `['*']` todas las cabeceras o \n  algunas especificas `['X-Request-With']`. Por defecto `['*']`.\n* `cors['Access-Control-Allow-Credentials']`: define si la petición actual puede hacer uso de credenciales. Puede ser \n  `true`, `false` o `null` (not set). Por defecto `null`.\n* `cors['Access-Control-Max-Age']`: define el tiempo de vida del la petición pref-flight. Por defecto `86400`. Por \n  ejemplo, habilitar CORS para el origen: `https://www.myserver.net` con métodos `GET`, `HEAD` y `OPTIONS`:\n\n```php\nuse yii\\filters\\Cors;\nuse yii\\helpers\\ArrayHelper;\n\npublic function behaviors()\n{\n    return ArrayHelper::merge([\n        [\n            'class' => Cors::class,\n            'cors' => [\n                'Origin' => ['https://www.myserver.net'],\n                'Access-Control-Request-Method' => ['GET', 'HEAD', 'OPTIONS'],\n            ],\n        ],\n    ], parent::behaviors());\n}\n```\n\nSe pueden ajustar las cabeceras de CORS sobrescribiendo los parámetros por defecto de una acción. Por ejemplo añadir \n`Access-Control-Allow-Credentials` a la acción 'login', se podría hacer así:\n\n```php\nuse yii\\filters\\Cors;\nuse yii\\helpers\\ArrayHelper;\n\npublic function behaviors()\n{\n    return ArrayHelper::merge([\n        [\n            'class' => Cors::class,\n            'cors' => [\n                'Origin' => ['https://www.myserver.net'],\n                'Access-Control-Request-Method' => ['GET', 'HEAD', 'OPTIONS'],\n            ],\n            'actions' => [\n                'login' => [\n                    'Access-Control-Allow-Credentials' => true,\n                ]\n            ]\n        ],\n    ], parent::behaviors());\n}\n```\n"
  },
  {
    "path": "docs/guide-es/structure-models.md",
    "content": "Modelos\n=======\n\nLos modelos forman parte de la arquitectura \n[MVC](https://es.wikipedia.org/wiki/Modelo%E2%80%93vista%E2%80%93controlador). Son objetos que representan datos de \nnegocio, reglas y lógica.\n\nSe pueden crear clases modelo extendiendo a [[yii\\base\\Model]] o a sus clases hijas. La clase base [[yii\\base\\Model]] \nsoporta muchas características útiles:\n\n* [Atributos](#attributes): representan los datos de negocio y se puede acceder a ellos como propiedades normales de \n  un objeto o como elementos de un array;\n* [Etiquetas de atributo](#attribute-labels): especifica la etiqueta a mostrar para los atributos;\n* [Asignación masiva](#massive-assignment): soporta la asignación múltiple de atributos en un único paso;\n* [validación](#validation-rules): asegura la validez de los datos de entrada basándose en reglas declaradas;\n* [Exportación de datos](#data-exporting): permite que los datos del modelo sean exportados en términos de arrays con \n  formatos personalizables.\n\nLa clase 'modelo' también es una base para modelos más avanzados, tales como [Active Records](db-active-record.md).\n\n> Info: No es obligatorio basar las clases modelo en [[yii\\base\\Model]]. Sin embargo, debido a que hay muchos \n  componentes de Yii construidos para dar soporte a [[yii\\base\\Model]], por lo general, es la clase base preferible \n  para un modelo.\n\n### Atributos <span id=\"attributes\"></span>\n\nLos modelos representan los datos de negocio en términos de *atributos*. Cada atributos es como una propiedad \npúblicamente accesible de un modelo. El método [[yii\\base\\Model::attributes()]] especifica qué atributos tiene la \nclase modelo.\n\nSe puede acceder a un atributo como se accede a una propiedad de un objeto normal.\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// \"name\" es un atributo de ContactForm\n$model->name = 'example';\necho $model->name;\n```\n\nTambién se puede acceder a los atributos como se accede a los elementos de un array, gracias al soporte para \n[ArrayAccess](https://www.php.net/manual/es/class.arrayaccess.php) y \n[ArrayIterator](https://www.php.net/manual/es/class.arrayiterator.php) que brinda [[yii\\base\\Model]]:\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// acceder a atributos como elementos de array\n$model['name'] = 'example';\necho $model['name'];\n\n// iterar entre atributos\nforeach ($model as $name => $value) {\n    echo \"$name: $value\\n\";\n}\n```\n\n### Definir Atributos <span id=\"defining-attributes\"></span>\n\nPor defecto, si un modelo extiende directamente a [[yii\\base\\Model]], todas sus variables miembro no estáticas son \natributos. Por ejemplo, la siguiente clase modelo 'ContactForm' tiene cuatro atributos: 'name', 'email', 'subject', \n'body'. El modelo 'ContactForm' se usa para representar los datos de entrada recibidos desde un formulario HTML.\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\n\nclass ContactForm extends Model\n{\n    public $name;\n    public $email;\n    public $subject;\n    public $body;\n}\n```\n\nSe puede sobrescribir [[yii\\base\\Model::attributes()]] para definir los atributos de diferente manera. El método debe \ndevolver los nombres de los atributos de un modelo. Por ejemplo [[yii\\db\\ActiveRecord]] lo hace devolviendo el nombre \nde las columnas de la tabla de la base de datos asociada como el nombre de sus atributos. Hay que tener en cuenta que \ntambién puede necesitar sobrescribir los métodos mágicos como `__get()`, `__set()` de modo que se puede acceder a los \natributos como a propiedades de objetos normales.\n\n### Etiquetas de atributo <span id=\"attribute-labels\"></span>\n\nCuando se muestran valores o se obtienen entradas para atributos, normalmente se necesita mostrar etiquetas asociadas \na los atributos. Por ejemplo, dado un atributo con nombre 'segundoApellido', es posible que se quiera mostrar la \netiqueta 'Segundo Apellido' ya que es más fácil de interpretar por el usuario final en lugares como campos de \nformularios y en mensajes de error.\n\nSe puede obtener la etiqueta de un atributo llamando a [[yii\\base\\Model::getAttributeLabel()]]. Por ejemplo:\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// muestra \"Name\"\necho $model->getAttributeLabel('name');\n```\n\nPor defecto, una etiqueta de atributo se genera automáticamente a partir del nombre de atributo. La generación se hace \ncon el método [[yii\\base\\Model::generateAttributeLabel()]]. Este convertirá los nombres de variables de tipo \ncamel-case en múltiples palabras con la primera letra de cada palabra en mayúsculas. Por ejemplo 'usuario' se \nconvertirá en 'Nombre', y 'primerApellido' se convertirá en 'Primer Apellido'.\nSi no se quieren usar las etiquetas generadas automáticamente, se puede sobrescribir \n[[yii\\base\\Model::attributeLabels()]] a una declaración de etiquetas de atributo especifica. Por ejemplo:\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\n\nclass ContactForm extends Model\n{\n    public $name;\n    public $email;\n    public $subject;\n    public $body;\n\n    public function attributeLabels()\n    {\n        return [\n            'name' => 'Your name',\n            'email' => 'Your email address',\n            'subject' => 'Subject',\n            'body' => 'Content',\n        ];\n    }\n}\n```\n\nPara aplicaciones con soporte para múltiples idiomas, se puede querer traducir las etiquetas de los atributos. Esto se \npuede hacer en el método [[yii\\base\\Model::attributeLabels()|attributeLabels()]], como en el siguiente ejemplo:\n\n```php\npublic function attributeLabels()\n{\n    return [\n        'name' => \\Yii::t('app', 'Your name'),\n        'email' => \\Yii::t('app', 'Your email address'),\n        'subject' => \\Yii::t('app', 'Subject'),\n        'body' => \\Yii::t('app', 'Content'),\n    ];\n}\n```\n\nIncluso se puede definir etiquetas de atributo condicionales. Por ejemplo, basándose en el [escenario](#scenarios) en \nque se esta usando el modelo, se pueden devolver diferentes etiquetas para un mismo atributo.\n\n> Info: Estrictamente hablando, los atributos son parte de las [vistas](structure-views.md). Pero declarar las \n  etiquetas en los modelos, a menudo, es muy conveniente y puede generar a un código muy limpio y reutilizable.\n\n## Escenarios <span id=\"scenarios\"></span>\n\nUn modelo puede usarse en diferentes *escenarios*. Por ejemplo, un modelo 'Usuario', puede ser utilizado para recoger \nentradas de inicio de sesión de usuarios, pero también puede usarse para generar usuarios. En diferentes escenarios, \nun modelo puede usar diferentes reglas de negocio y lógica. Por ejemplo, un atributo 'email' puede ser requerido \ndurante un registro de usuario, pero no ser necesario durante el inicio de sesión del mismo.\n\nUn modelo utiliza la propiedad [[yii\\base\\Model::scenario]] para mantener saber en qué escenario se esta usando. Por \ndefecto, un modelo soporta sólo un escenario llamado 'default'. El siguiente código muestra dos maneras de establecer \nel escenario en un modelo.\n\n```php\n// el escenario se establece como una propiedad\n$model = new User;\n$model->scenario = 'login';\n\n// el escenario se establece mediante configuración\n$model = new User(['scenario' => 'login']);\n```\n\nPor defecto, los escenarios soportados por un modelo se determinan por las [reglas de validación](#validation-rules) \ndeclaradas en el modelo. Sin embargo, se puede personalizar este comportamiento sobrescribiendo el método \n[[yii\\base\\Model::scenarios()]], como en el siguiente ejemplo:\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass User extends ActiveRecord\n{\n    public function scenarios()\n    {\n        return [\n            'login' => ['username', 'password'],\n            'register' => ['username', 'email', 'password'],\n        ];\n    }\n}\n```\n\n> Info: En el anterior y en los siguientes ejemplos, las clases modelo extienden a [[yii\\db\\ActiveRecord]] \n  porque el uso de múltiples escenarios normalmente sucede con clases de [Active Records](db-active-record.md).\n\nEl método 'scenarios()' devuelve un array cuyas claves son el nombre de escenario y los valores correspondientes a los \n*atributos activos*. Un atributo activo puede ser [asignado masivamente](#massive-assignment) y esta sujeto a \n[validación](#validation-rules). En el anterior ejemplo, los atributos 'username' y 'password' están activados en el \nescenario 'login'; mientras que en el escenario 'register', el atributo 'email' esta activado junto con 'username' y \n'password'.\n\nLa implementación por defecto de los 'scenarios()' devolverá todos los escenarios encontrados en el método de \ndeclaración de las reglas de validación [[yii\\base\\Model::rules()]]. Cuando se sobrescribe 'scenarios()', si se quiere \nintroducir nuevos escenarios además de los predeterminados, se puede hacer como en el siguiente ejemplo:\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass User extends ActiveRecord\n{\n    public function scenarios()\n    {\n        $scenarios = parent::scenarios();\n        $scenarios['login'] = ['username', 'password'];\n        $scenarios['register'] = ['username', 'email', 'password'];\n        return $scenarios;\n    }\n}\n```\n\nLa característica escenario se usa principalmente en las [validaciones](#validation-rules) y por la \n[asignación masiva de atributos](#massive-assignment). Aunque también se puede usar para otros propósitos. Por \nejemplo, se pueden declarar [etiquetas de atributo](#attribute-labels) diferentes basándose en el escenario actual.\n\n## Reglas de Validación <span id=\"validation-rules\"></span>\n\nCuando un modelo recibe datos del usuario final, estos deben ser validados para asegurar que cumplan ciertas reglas \n(llamadas *reglas de validación*, también conocidas como *reglas de negocio*). Por ejemplo, dado un modelo \n'ContactForm', se puede querer asegurar que ningún atributo este vacío y que el atributo 'email' contenga una \ndirección de correo válida. Si algún valor no cumple con las reglas, se debe mostrar el mensaje de error apropiado \npara ayudar al usuario a corregir estos errores.\n\nSe puede llamar a [[yii\\base\\Model::validate()]] para validar los datos recibidos. El método se usará para validar las \nreglas declaradas en [[yii\\base\\Model::rules()]] para validar cada atributo relevante. Si no se encuentran errores, se \ndevolverá `true`. De otro modo, este almacenará los errores en la propiedad [[yii\\base\\Model::errors]] y devolverá falso.\n Por ejemplo:\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// establece los atributos del modelo con la entrada de usuario\n$model->attributes = \\Yii::$app->request->post('ContactForm');\n\nif ($model->validate()) {\n    // todas las entradas validadas\n} else {\n    // validación fallida: $errors es un array que contiene los mensajes de error\n    $errors = $model->errors;\n}\n```\n\nPara declarar reglas de validación asociadas a un modelo, se tiene que sobrescribir el método \n[[yii\\base\\Model::rules()]] para que devuelva las reglas que los atributos del modelo deben satisfacer. El siguiente \nejemplo muestra las reglas de validación declaradas para el modelo 'ContactForm'.\n\n```php\npublic function rules()\n{\n    return [\n        // name, email, subject y body son atributos requeridos\n        [['name', 'email', 'subject', 'body'], 'required'],\n\n        // el atribuido email debe ser una dirección de correo electrónico válida\n        ['email', 'email'],\n    ];\n}\n```\n\nUna regla puede usarse para validar uno o más atributos, y un atributo puede validarse por una o múltiples reglas. Por \nfavor refiérase a la sección [Validación de entrada](input-validation.md) para obtener más detalles sobre cómo \ndeclarar reglas de validación.\n\nA veces, solamente se quiere aplicar una regla en ciertos [escenarios](#scenarios). Para hacerlo, se puede especificar \nla propiedad 'on' de una regla, como en el siguiente ejemplo:\n\n```php\npublic function rules()\n{\n    return [\n        // username, email y password son obligatorios en el escenario “register”\n        [['username', 'email', 'password'], 'required', 'on' => 'register'],\n\n        // username y password son obligatorios en el escenario “login”\n        [['username', 'password'], 'required', 'on' => 'login'],\n    ];\n}\n```\n\nSi no se especifica la propiedad 'on', la regla se aplicará en todos los escenarios. Se llama a una regla \n*regla activa* si esta puede aplicarse en el [[yii\\base\\Model::scenario|scenario]] actual.\n\nUn atributo será validado si y sólo si es un atributo activo declarado en 'scenarios()' y esta asociado con una o más \nreglas activas declaradas en 'rules()'.\n\n## Asignación Masiva <span id=\"massive-assignment\"></span>\n\nLa asignación masiva es una buena forma de rellenar los atributos de un modelo con las entradas de usuario en una \núnica línea de código. Rellena los atributos de un modelo asignando los datos de entrada directamente a las \npropiedades de [[yii\\base\\Model::$attributes]]. Los siguientes dos ejemplos son equivalentes, ambos intentan asignar \nlos datos enviados por el usuario final a través de un formulario a los atributos del modelo 'ContactForm'. \nClaramente, el primero, que usa la asignación masiva, es más claro y menos propenso a errores que el segundo:\n\n```php\n$model = new \\app\\models\\ContactForm;\n$model->attributes = \\Yii::$app->request->post('ContactForm');\n```\n\n```php\n$model = new \\app\\models\\ContactForm;\n$data = \\Yii::$app->request->post('ContactForm', []);\n$model->name = isset($data['name']) ? $data['name'] : null;\n$model->email = isset($data['email']) ? $data['email'] : null;\n$model->subject = isset($data['subject']) ? $data['subject'] : null;\n$model->body = isset($data['body']) ? $data['body'] : null;\n```\n\n### Atributos Seguros <span id=\"safe-attributes\"></span>\n\nLa asignación masiva sólo se aplica a los llamados *atributos seguros* qué son los atributos listados en \n[[yii\\base\\Model::scenarios()]] para el actual [[yii\\base\\Model::scenario|scenario]] del modelo. Por ejemplo, si en el \nmodelo 'User' tenemos la siguiente declaración de escenario, entonces cuando el escenario actual sea 'login', sólo los \natributos 'username' y 'password' podrán ser asignados masivamente. Cualquier otro atributo permanecerá intacto \n\n```php\npublic function scenarios()\n{\n    return [\n        'login' => ['username', 'password'],\n        'register' => ['username', 'email', 'password'],\n    ];\n}\n```\n\n> Info: La razón de que la asignación masiva sólo se aplique a los atributos seguros es debida a que se quiere \ncontrolar qué atributos pueden ser modificados por los datos del usuario final. Por ejemplo, si el modelo 'User' tiene \nun atributo 'permission' que determina los permisos asignados al usuario, se quiere que estos atributos sólo sean \nmodificados por administradores desde la interfaz backend.\n\nDebido a que la implementación predeterminada de [[yii\\base\\Model::scenarios()]] devolverá todos los escenarios y \natributos encontrados en [[yii\\base\\Model::rules()]], si no se sobrescribe este método, significa que un atributo es \nseguro mientras aparezca en una de las reglas de validación activas.\n\nPor esta razón, se proporciona un validador especial con alias 'safe' con el que se puede declarar un atributo como \nseguro sin llegar a validarlo. Por ejemplo, las siguientes reglas declaran que los atributos 'title' y 'description' \nson atributos seguros.\n\n```php\npublic function rules()\n{\n    return [\n        [['title', 'description'], 'safe'],\n    ];\n}\n```\n\n### Atributos Inseguros <span id=\"unsafe-attributes\"></span>\n\nComo se ha descrito anteriormente, el método [[yii\\base\\Model::scenarios()]] sirve para dos propósitos: determinar qué \natributos deben ser validados y determinar qué atributos son seguros. En situaciones poco comunes, se puede querer \nvalidar un atributo pero sin marcarlo como seguro. Se puede hacer prefijando el signo de exclamación '!' delante del \nnombre del atributo cuando se declaran en 'scenarios()', como el atributo 'secret' del siguiente ejemplo:\n\n```php\npublic function scenarios()\n{\n    return [\n        'login' => ['username', 'password', '!secret'],\n    ];\n}\n```\n\nCuando el modelo esté en el escenario 'login', los tres atributos serán validados. Sin embargo, sólo los atributos \n'username' y 'password' se asignarán masivamente. Para asignar un valor de entrada al atribuido 'secret', se tendrá \nque hacer explícitamente como en el ejemplo:\n\n```php\n$model->secret = $secret;\n```\n\n## Exportación de Datos <span id=\"data-exporting\"></span>\n\nA menudo necesitamos exportar modelos a diferentes formatos. Por ejemplo, se puede querer convertir un conjunto de \nmodelos a formato JSON o Excel. El proceso de exportación se puede dividir en dos pasos independientes. En el primer \npaso, se convierten los modelos en arrays; en el segundo paso, los arrays se convierten a los formatos deseados. Nos \npuede interesar fijarnos en el primer paso, ya que el segundo paso se puede lograr mediante un formateador de datos \ngenérico, tal como [[yii\\web\\JsonResponseFormatter]].\nLa manera más simple de convertir un modelo en un array es usar la propiedad [[yii\\base\\Model::$attributes]]. Por \nejemplo:\n\n```php\n$post = \\app\\models\\Post::findOne(100);\n$array = $post->attributes;\n```\n\nPor defecto, la propiedad [[yii\\base\\Model::$attributes]] devolverá los valores de *todos* los atributos declarados en \n[[yii\\base\\Model::attributes()]].\n\nUna manera más flexible y potente de convertir un modelo en un array es usar el método [[yii\\base\\Model::toArray()]]. \nSu funcionamiento general es el mismo que el de [[yii\\base\\Model::$attributes]]. Sin embargo, este permite elegir que \nelementos de datos, llamados *campos*, queremos poner en el array resultante y elegir como debe ser formateado. De \nhecho, es la manera por defecto de exportar modelos en desarrollo de servicios Web RESTful, tal y como se describe en \n[Formatos de Respuesta](rest-response-formatting.md).\n\n### Campos <span id=\"fields\"></span>\n\nUn campo es simplemente un elemento nombrado en el array resultante de ejecutar el método [[yii\\base\\Model::toArray()]]\n de un modelo.\nPor defecto, los nombres de los campos son equivalentes a los nombres de los atributos. Sin embargo, se puede \nmodificar este comportamiento sobrescribiendo el método [[yii\\base\\Model::fields()|fields()]] y/o el método \n[[yii\\base\\Model::extraFields()|extraFields()]]. Ambos métodos deben devolver una lista de las definiciones de los \ncampos. Los campos definidos mediante 'fields()' son los campos por defecto, esto significa que 'toArray()' devolverá \nestos campos por defecto. El método 'extraFields()' define campos adicionalmente disponibles que también pueden \ndevolverse mediante 'toArray()' siempre y cuando se especifiquen a través del parámetro '$expand'. Por ejemplo, el \nsiguiente código devolverá todos los campos definidos en 'fields()' y los campos 'prettyName' y 'fullAdress' si estos \nestán definidos en 'extraFields()'.\n\n```php\n$array = $model->toArray([], ['prettyName', 'fullAddress']);\n```\n\nSe puede sobrescribir 'fields()' para añadir, eliminar, renombrar o redefinir campos. El valor devuelto por 'fields()' \ndebe se un array. Las claves del array son los nombres de los campos, y los valores son las correspondientes \ndefiniciones de los campos que pueden ser nombres de propiedades/atributos o funciones anónimas que devuelvan los \ncorrespondientes valores de campo. En el caso especial en que un nombre de un campo es el mismo a su definición de \nnombre de atributo, se puede omitir la clave del array. Por ejemplo:\n\n```php\n// lista explícitamente cada campo, es mejor usarlo cuando nos queremos asegurar \n// de que los cambios en la tabla de la base de datos o los atributos del modelo \n// no modifiquen los campos(para asegurar compatibilidades para versiones anteriores de API)\npublic function fields()\n{\n    return [\n        // el nombre del campo es el mismo que el nombre de atributo\n        'id',\n\n        // el nombre del campo es “email”, el nombre de atributo correspondiente es “email_address”\n        'email' => 'email_address',\n\n        // El nombre del campo es “name”, su valor esta definido por una llamada de retorno PHP\n        'name' => function () {\n            return $this->first_name . ' ' . $this->last_name;\n        },\n    ];\n}\n\n// filtrar algunos campos, es mejor usarlo cuando se quiere heredar la implementación del padre\n// y discriminar algunos campos sensibles.\npublic function fields()\n{\n    $fields = parent::fields();\n\n    // elimina campos que contengan información sensible.\n    unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);\n\n    return $fields;\n}\n```\n\n> Warning: debido a que por defecto todos los atributos de un modelo serán incluidos en el array exportado, se debe \nexaminar los datos para asegurar que no contienen información sensible. Si existe dicha información, se debe \nsobrescribir 'fields()' para filtrarla. En el anterior ejemplo, se filtra 'aut_key', 'password_hash' y \n'password_reset_token'.\n\n## Mejores Prácticas <span id=\"best-practices\"></span>\n\nLos modelos son los lugares centrales para representar datos de negocio, reglas y lógica. Estos a menudo necesitan ser \nreutilizados en diferentes lugares. En una aplicación bien diseñada, los modelos normalmente son más grandes que los \n[controladores](structure-controllers.md).\n\nEn resumen, los modelos:\n* pueden contener atributos para representar los datos de negocio;\n* pueden contener reglas de validación para asegurar la validez e integridad de los datos;\n* pueden contener métodos que para implementar la lógica de negocio;\n* NO deben acceder directamente a peticiones, sesiones, u otro tipo de datos de entorno. Estos datos deben ser \n  inyectados por los [controladores](structure-controllers.md) en los modelos.\n* deben evitar embeber HTML u otro código de presentación – esto es mejor hacerlo en las [vistas](structure-views.md);\n* evitar tener demasiados [escenarios](#scenarios) en un mismo modelo.\n\nGeneralmente se puede considerar la última recomendación cuando se estén desarrollando grandes sistemas complejos. En \nestos sistemas, los modelos podrían ser muy grandes debido a que podrían ser usados en muchos lugares y por tanto \ncontener muchos conjuntos de reglas y lógicas de negocio. A menudo esto desemboca en un código muy difícil de mantener \nya que una simple modificación en el código puede afectar a muchos sitios diferentes. Para mantener el código más \nfácil de mantener, se puede seguir la siguiente estrategia:\n\n* Definir un conjunto de clases modelo base que sean compartidas por diferentes \n  [aplicaciones](structure-applications.md) o [módulos](structure-modules.md). Estas clases modelo deben contener el \n  conjunto mínimo de reglas y lógica que sean comunes para todos sus usos.\n* En cada [aplicación](structure-applications.md) o [módulo](structure-modules.md) que use un modelo, definir una \n  clase modelo concreta que extienda a la correspondiente clase modelo base. La clase modelo concreta debe contener \n  reglas y lógica que sean específicas para esa aplicación o módulo.\n\nPor ejemplo, en la [Plantilla de Aplicación Avanzada](tutorial-advanced-app.md), definiendo una clase modelo base \n'common\\models\\Post'. Después en la aplicación front end, definiendo y usando una clase modelo concreta \n'frontend\\models\\Post' que extienda a 'common\\models\\Post'. Y de forma similar en la aplicación back end, definiendo \n'backend\\models\\Post'. Con esta estrategia, nos aseguramos que el código de 'frontend\\models\\Post' es específico para \nla aplicación front end, y si se efectúa algún cambio en el, no nos tenemos que preocupar de si el cambio afectará a \nla aplicación back end.\n"
  },
  {
    "path": "docs/guide-es/structure-modules.md",
    "content": "Módulos\n=======\nLos módulos son unidades de software independientes que consisten en [modelos](structure-models.md), \n[vistas](structure-views.md), [controladores](structure-controllers.md), y otros componentes de apoyo. Los usuarios \nfinales pueden acceder a los controladores de un módulo cuando éste está instalado en la \n[aplicación](structure-applications.md). Por éstas razones, los módulos a menudo se considerados como \nmini-aplicaciones. Los módulos difieren de las [aplicaciones](structure-applications.md) en que los módulos no pueden \nser desplegados solos y tienen que residir dentro de aplicaciones.\n\n## Creación de Módulos<span id=\"creating-modules\"></span>\n\nUn módulo está organizado de tal manera que contiene un directorio llamado [[yii\\base\\Module::basePath|base path]] del \nmódulo. Dentro de este directorio, hay subdirectorios tales como 'controllers', 'models', 'views', que contienen \ncontroladores, modelos, vistas y otro código, exactamente como una aplicación. El siguiente ejemplo muestra el \ncontenido dentro de un módulo:\n\n```\nforum/\n    Module.php                   archivo clase módulo\n    controllers/                 contiene archivos de la clase controlador\n        DefaultController.php    archivo clase controlador por defecto\n    models/                      contiene los archivos de clase modelo\n    views/                       contiene las vistas de controlador y los archivos de diseño\n        layouts/                 contiene los archivos de diseño de las vistas\n        default/                 contiene los archivos de vista del DefaultController\n            index.php            archivo de vista del index\n```\n\n### Clases Módulo <span id=\"module-classes\"></span>\n\nCada módulo debe tener una única clase módulo que extiende a [[yii\\base\\Module]]. La clase debe encontrarse \ndirectamente debajo del [[yii\\base\\Module::basePath|base path]] y debe ser [autocargable](concept-autoloading.md). \nCuando se está accediendo a un módulo, se creará una única instancia de la clase módulo correspondiente. Como en las \n[instancias de aplicación](structure-applications.md), las instancias de módulo se utilizan para compartir datos y \ncomponentes de código dentro de los módulos.\n\nEl siguiente ejemplo muestra como podría ser una clase módulo.\n\n```php\nnamespace app\\modules\\forum;\n\nclass Module extends \\yii\\base\\Module\n{\n    public function init()\n    {\n        parent::init();\n\n        $this->params['foo'] = 'bar';\n        // ...  otro código de inicialización ...\n    }\n}\n```\n\nSi el método 'init()' contiene mucho código de inicialización de las propiedades del módulo, también se puede guardar \nen términos de configuración y cargarlo con el siguiente código ‘init()’:\n\n```php\npublic function init()\n{\n    parent::init();\n    // inicializa el módulo con la configuración cargada desde config.php\n    \\Yii::configure($this, require __DIR__ . '/config.php');\n}\n```\n\ndonde el archivo de configuración ‘config.php’ puede contener el siguiente contenido, similar al de \n[configuraciones de aplicación](structure-applications.md#application-configurations).\n\n```php\n<?php\nreturn [\n    'components' => [\n        // lista de configuraciones de componente\n    ],\n    'params' => [\n        // lista de parámetros\n    ],\n];\n```\n\n### Controladores en Módulos <span id=\"controllers-in-modules\"></span>\n\nCuando se crean controladores en un modelo, una convención es poner las clases controlador debajo del sub-espacio de \nnombres de ‘controllers’ del espacio de nombres de la clase módulo. Esto también significa que los archivos de la \nclase controlador deben ponerse en el directorio ‘controllers’ dentro del [[yii\\base\\Module::basePath|base path]] del \nmódulo. Por ejemplo, para crear un controlador ‘post’ en el módulo ‘forum’ mostrado en la última subdivisión, se debe \ndeclarar la clase controlador de la siguiente manera:\n\n```php\nnamespace app\\modules\\forum\\controllers;\n\nuse yii\\web\\Controller;\n\nclass PostController extends Controller\n{\n    // ...\n}\n```\n\nSe puede personalizar el espacio de nombres de las clases controlador configurando la propiedad \n[[yii\\base\\Module::controllerNamespace]]. En el caso que alguno de los controladores esté fuera del espacio de \nnombres, se puede hacer accesible configurando la propiedad [[yii\\base\\Module::controllerMap]], similar a \n[como se hace en una aplicación](structure-applications.md#controller-map).\n\n### Vistas en Módulos <span id=\"views-in-modules\"></span>\n\nLas vistas en un módulo deben alojarse en el directorio ‘views’ dentro del módulo del \n[[yii\\base\\Module::basePath|base path]]. Las vistas renderizadas por un controlador en el módulo, deben alojarse en el \ndirectorio ‘views/ControllerID’, donde el ‘ControllerID’ hace referencia al \n[ID del controlador](structure-controllers.md#routes). Por ejemplo, si la clase controlador es ‘PostController’, el \ndirectorio sería ‘views/post’ dentro del [[yii\\base\\Module::basePath|base path]] del módulo.\n\nUn modulo puede especificar un [layout](structure-views.md#layouts) que se aplica a las vistas renderizadas por los \ncontroladores del módulo. El layout debe alojarse en el directorio ‘views/layouts’ por defecto, y se puede configurar \nla propiedad [[yii\\base\\Module::layout]] para apuntar al nombre del layout. Si no se configura la propiedad ‘layout’, \nse usar el layout de la aplicación.\n\n## Uso de los Módulos <span id=\"using-modules\"></span>\n\nPara usar un módulo en una aplicación, simplemente se tiene que configurar la aplicación añadiendo el módulo en la \npropiedad [[yii\\base\\Application::modules|modules]] de la aplicación. El siguiente ejemplo de la \n[configuración de la aplicación](structure-applications.md#application-configurations) usa el modelo ‘forum’:\n\n```php\n[\n    'modules' => [\n        'forum' => [\n            'class' => 'app\\modules\\forum\\Module',\n            // ... otras configuraciones para el módulo ...\n        ],\n    ],\n]\n```\n\nLa propiedad [[yii\\base\\Application::modules|modules]] contiene un array de configuraciones de módulo.  Cada clave del \narray representa un *ID de módulo* que identifica de forma única el módulo de entre todos los módulos de la \naplicación, y el correspondiente valor del array es la [configuración](concept-configurations.md) para crear el módulo.\n\n### Rutas <span id=\"routes\"></span>\n\nDe Igual manera que el acceso a los controladores en una aplicación, las [rutas](structure-controllers.md#routes) se \nutiliza para dirigirse a los controladores en un módulo. Una ruta para un controlador dentro de un módulo debe empezar \ncon el ID del módulo seguido por el ID del controlador y el ID de la acción. Por ejemplo, si una aplicación usa un \nmódulo llamado ‘forum’, la ruta ‘forum/post/index’ representaría la acción ‘index’ del controlador ‘post’ en el \nmódulo. Si la ruta sólo contiene el ID del módulo, entonces la propiedad [[yii\\base\\Module::defaultRoute]] que por \ndefecto es ‘default’, determinara que controlador/acción debe usarse. Esto significa que la ruta ‘forum’ representaría \nel controlador ‘default’ en el módulo ‘forum’.\n\n### Acceder a los Módulos <span id=\"accessing-modules\"></span>\n\nDentro de un módulo, se puede necesitar obtener la instancia de la [clase módulo](#module-classes) para poder acceder \nal ID del módulo, componentes del módulo, etc. Se puede hacer usando la siguiente declaración:\n\n```php\n$module = MyModuleClass::getInstance();\n```\n\nDónde ‘MyModuleClass’ hace referencia al nombre de la clase módulo en la que estemos interesados. El método \n‘getInstance()’ devolverá la instancia actualmente solicitada de la clase módulo. Si no se solicita el módulo, el \nmétodo devolverá nulo. Hay que tener en cuenta que si se crea una nueva instancia del módulo, esta será diferente a la \ncreada por Yii en respuesta a la solicitud.\n\n> Info: Cuando se desarrolla un módulo, no se debe dar por sentado que el módulo usará un ID fijo. Esto se debe \n  a que un módulo puede asociarse a un ID arbitrario cuando se usa en una aplicación o dentro de otro módulo. Para \n  obtener el ID del módulo, primero se debe usar el código del anterior ejemplo para obtener la instancia y luego el \n  ID mediante ‘$modeule->id’.\n\nTambién se puede acceder a la instancia de un módulo usando las siguientes declaraciones:\n\n```php\n// obtiene el modulo hijo cuyo ID es “forum”\n$module = \\Yii::$app->getModule('forum');\n\n// obtiene el módulo al que pertenece la petición actual\n$module = \\Yii::$app->controller->module;\n```\n\nEl primer ejemplo sólo es útil cuando conocemos el ID del módulo, mientras que el segundo es mejor usarlo cuando \nconocemos los controladores que se están solicitando.\n\nUna vez obtenida la instancia del módulo, se puede acceder a parámetros o componentes registrados con el módulo. Por \nejemplo:\n\n```php\n$maxPostCount = $module->params['maxPostCount'];\n```\n\n### Bootstrapping Módulos <span id=\"bootstrapping-modules\"></span>\n\nPuede darse el caso en que necesitemos que un módulo se ejecute en cada petición. El módulo [[yii\\debug\\Module|debug]] \nes un ejemplo. Para hacerlo, tenemos que listar los IDs de los módulos en la propiedad \n[[yii\\base\\Application::bootstrap|bootstrap]] de la aplicación.\n\nPor ejemplo, la siguiente configuración de aplicación se asegura de que el módulo ‘debug’ siempre se cargue:\n\n```php\n[\n    'bootstrap' => [\n        'debug',\n    ],\n\n    'modules' => [\n        'debug' => 'yii\\debug\\Module',\n    ],\n]\n```\n\n## Módulos anidados <span id=\"nested-modules\"></span>\n\nLos módulos pueden ser anidados sin límite de niveles. Es decir, un módulo puede contener un módulo y éste a la vez \ncontener otro módulo. Nombramos *padre* al primero mientras que al segundo lo nombramos *hijo*. Los módulos hijo se \ntienen que declarar en la propiedad [[yii\\base\\Module::modules|modules]] de sus módulos padre. Por ejemplo:\n\n```php\nnamespace app\\modules\\forum;\n\nclass Module extends \\yii\\base\\Module\n{\n    public function init()\n    {\n        parent::init();\n\n        $this->modules = [\n            'admin' => [\n                // debe considerarse usar un nombre de espacios más corto!\n                'class' => 'app\\modules\\forum\\modules\\admin\\Module',\n            ],\n        ];\n    }\n}\n```\n\nEn un controlador dentro de un módulo anidado, la ruta debe incluir el ID de todos los módulos antecesores. Por \nejemplo, la ruta ‘forum/admin/dashboard/index’ representa la acción ‘index’ del controlador ‘dashboard’ en el módulo \n‘admin’ que es el módulo hijo del módulo ‘forum’. \n\n> Info: El método [[yii\\base\\Module::getModule()|getModule()]] sólo devuelve el módulo hijo que pertenece \ndirectamente a su padre. La propiedad [[yii\\base\\Application::loadedModules]] contiene una lista de los módulos \ncargados, incluyendo los hijos directos y los anidados, indexados por sus nombres de clase.\n\n## Mejores Prácticas <span id=\"best-practices\"></span>\n\nEs mejor usar los módulos en grandes aplicaciones en las que sus funcionalidades puedan ser divididas en diferentes \ngrupos, cada uno compuesto por funcionalidades directamente relacionadas. Cada grupo de funcionalidades se puede \ndesarrollar como un módulo que puede ser desarrollado y mantenido por un programador o equipo específico.\n\nLos módulos también son una buena manera de reutilizar código a nivel de grupo de funcionalidades. Algunas \nfuncionalidades de uso común, tales como la gestión de usuarios o la gestión de comentarios, pueden ser desarrollados \ncomo módulos para que puedan ser fácilmente reutilizados en futuros proyectos.\n"
  },
  {
    "path": "docs/guide-es/structure-overview.md",
    "content": "Información general\n===================\n\nLas aplicaciones realizadas con Yii están organizadas de acuerdo al patrón de diseño [modelo-vista-controlador (MVC)](https://es.wikipedia.org/wiki/Modelo%E2%80%93vista%E2%80%93controlador). Los\n[modelos](structure-models.md) representan datos, la lógica de negocios y sus reglas; las [vistas](structure-views.md) \nson la representación de salida de los modelos; y finalmente, los [controladores](structure-controllers.md) que toman datos de entrada y los convierten en instrucciones para los [modelos](structure-models.md) y [vistas](structure-views.md). \n\nAdemás de MVC, las aplicaciones Yii también tienen las siguientes entidades:\n\n* [scripts de entrada](structure-entry-scripts.md): Existen scripts PHP directamente accesibles a los usuarios finales.\n  Son los responsables de comenzar el ciclo de manejo de una solicitud.\n* [aplicaciones](structure-applications.md): Son objetos accesibles globalmente que gestionan y coordinan los componentes \n  de la aplicación con el fin de atender las diferentes solicitudes. \n* [componentes de la aplicación](structure-application-components.md): Son los objetos registrados con la aplicación, y\n  proporcionan varios servicios para cumplir las solicitudes.\n* [módulos](structure-modules.md): Son paquetes auto-contenidos los cuales por si solos poseen estructura MVC.\n  Una aplicación puede estar organizada en términos de múltiples módulos.\n* [filtros](structure-filters.md): Representan el código que debe ser invocado antes y despues de la ejecución de cada\n  solicitud por los controladores.\n* [widgets](structure-widgets.md): Son objetos que pueden ser embebidos en las [Vistas](structure-views.md). Pueden\n  contener lógica del controlador y ser reutilizados en múltiples vistas.\n\nEl siguiente esquema muestra la estructura estática de una aplicación:\n\n![Estructura estática de una aplicación](images/application-structure.png)\n"
  },
  {
    "path": "docs/guide-es/structure-views.md",
    "content": "Vistas\n======\n\nLas Vistas (views) son una parte de la arquitectura [MVC](https://es.wikipedia.org/wiki/Modelo%E2%80%93vista%E2%80%93controlador).\nEstas son el código responsable de presentar los datos al usuario final. En una aplicación Web, las vistas son usualmente creadas\nen términos de *templates* que son archivos PHP que contienen principalmente HTML y PHP.\nEstas son manejadas por el [componente de la aplicación](structure-application-components.md) [[yii\\web\\View|view]], el cual provee los métodos comúnmente utilizados\npara facilitar la composición y renderizado. Por simplicidad, a menudo nos referimos a los templates de vistas o archivos de templates\ncomo vistas.\n\n\n## Crear Vistas <span id=\"creating-views\"></span>\n\nComo fue mencionado, una vista es simplemente un archivo PHP que mezcla código PHP y HTML. La siguiente es una vista\nque muestra un formulario de login. Como puedes ver, el código PHP utilizado es para generar contenido dinámico, como el\ntítulo de la página y el formulario mismo, mientras que el código HTML organiza estos elementos en una página HTML mostrable.\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n/**\n * @var \\yii\\web\\View $this\n * @var \\yii\\widgets\\ActiveForm $form\n * @var \\app\\models\\LoginForm $model\n */\n\n$this->title = 'Login';\n?>\n<h1><?= Html::encode($this->title) ?></h1>\n\n<p>Por favor completa los siguientes campos para loguearte:</p>\n\n<?php $form = ActiveForm::begin(); ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n    <?= Html::submitButton('Login') ?>\n<?php ActiveForm::end(); ?>\n```\n\nDentro de una vista, puedes acceder a la variable `$this` referida al [[yii\\web\\View|componente view]]\nque maneja y renderiza la vista actual.\n\nAdemás de `$this`, puede haber otras variables predefinidas en una vista, como `$form` y `$model` en el\nejemplo anterior. Estas variables representan los datos que son *inyectados* a la vista desde el [controlador](structure-controllers.md)\no algún otro objeto que dispara la [renderización de la vista](#rendering-views).\n\n> Tip: La lista de variables predefinidas están listadas en un bloque de comentario al principio de la vista así\n  pueden ser reconocidas por las IDEs. Esto es también una buena manera de documentar tus propias vistas.\n\n\n### Seguridad <span id=\"security\"></span>\n\nAl crear vistas que generan páginas HTML, es importante que codifiques (encode) y/o filtres los datos\nprovenientes de los usuarios antes de mostrarlos. De otro modo, tu aplicación puede estar expuesta\na ataques tipo [cross-site scripting](https://es.wikipedia.org/wiki/Cross-site_scripting).\n\nPara mostrar un texto plano, codifícalos previamente utilizando [[yii\\helpers\\Html::encode()]]. Por ejemplo, el siguiente código aplica\nuna codificación del nombre de usuario antes de mostrarlo:\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n\n<div class=\"username\">\n    <?= Html::encode($user->name) ?>\n</div>\n```\n\nPara mostrar contenido HTML, utiliza [[yii\\helpers\\HtmlPurifier]] para filtrarlo antes. Por ejemplo, el siguiente código\nfiltra el contenido del post antes de mostrarlo en pantalla:\n\n```php\n<?php\nuse yii\\helpers\\HtmlPurifier;\n?>\n\n<div class=\"post\">\n    <?= HtmlPurifier::process($post->text) ?>\n</div>\n```\n\n> Tip: Aunque HTMLPurifier hace un excelente trabajo al hacer la salida más segura, no es rápido. Deberías considerar\nel aplicar un [caching](caching-overview.md) al resultado de aplicar el filtro si tu aplicación requiere un gran desempeño (performance).\n\n\n### Organizar las Vistas <span id=\"organizing-views\"></span>\n\nAsí como en [controladores](structure-controllers.md) y [modelos](structure-models.md), existen convenciones para organizar las vistas.\n\n* Para vistas renderizadas por controladores, deberían colocarse en un directorio tipo `@app/views/ControllerID` por defecto,\n  donde `ControllerID` se refiere al [ID del controlador](structure-controllers.md#routes). Por ejemplo,\n  si la clase del controlador es `PostController`, el directorio sería `@app/views/post`; Si fuera `PostCommentController`,\n  el directorio sería `@app/views/post-comment`. En caso de que el controlador pertenezca a un módulo,\n  el directorio sería `views/ControllerID` bajo el [[yii\\base\\Module::basePath|directorio del módulo]].\n* Para vistas renderizadas por un [widget](structure-widgets.md), deberían ser puestas en un directorio\n  tipo `WidgetPath/views` por defecto, donde `WidgetPath` se refiere al directorio que contiene a la clase del widget.\n* Para vistas renderizadas por otros objetos, se recomienda seguir una convención similar a la utilizada con los widgets.\n\nPuedes personalizar estos directorios por defecto sobrescribiendo el método [[yii\\base\\ViewContextInterface::getViewPath()]]\nen el controlador o widget necesario.\n\n\n## Renderizando Vistas <span id=\"rendering-views\"></span>\n\nPuedes renderizar vistas desde [controllers](structure-controllers.md), [widgets](structure-widgets.md), o cualquier otro lugar\nllamando a los métodos de renderización de vistas. Estos métodos comparten una firma similar, como se muestra a continuación:\n\n```\n/**\n * @param string $view nombre de la vista o ruta al archivo, dependiendo del método de renderización utilizado\n * @param array $params los datos pasados a la vista\n * @return string el resultado de la renderización\n */\nmethodName($view, $params = [])\n```\n\n\n### Renderizando en Controladores <span id=\"rendering-in-controllers\"></span>\n\nDentro de los [controladores](structure-controllers.md), puedes llamar al siguiente método del controlador para renderizar una vista:\n\n* [[yii\\base\\Controller::render()|render()]]: renderiza la [vista nombrada](#named-views) y aplica un [layout](#layouts)\n  al resultado de la renderización.\n* [[yii\\base\\Controller::renderPartial()|renderPartial()]]: renderiza la [vista nombrada](#named-views) sin ningún layout aplicado.\n* [[yii\\web\\Controller::renderAjax()|renderAjax()]]: renderiza la [vista nombrada](#named-views) sin layout,\n  e inyecta todos los scripts y archivos JS/CSS registrados. Esto sucede usualmente en respuestas a peticiones AJAX.\n* [[yii\\base\\Controller::renderFile()|renderFile()]]: renderiza la vista especificada en términos de la ruta al archivo o\n  [alias](concept-aliases.md).\n* [[yii\\base\\Controller::renderContent()|renderContent()]]: renderiza un string fijo, inscrustándolo en\n  el [layout](#layouts) actualmente aplicable. Este método está disponible desde la versión 2.0.1.\n\nPor ejemplo:\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse app\\models\\Post;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\n\nclass PostController extends Controller\n{\n    public function actionView($id)\n    {\n        $model = Post::findOne($id);\n        if ($model === null) {\n            throw new NotFoundHttpException;\n        }\n\n        // renderiza una vista llamada \"view\" y le aplica el layout\n        return $this->render('view', [\n            'model' => $model,\n        ]);\n    }\n}\n```\n\n\n### Renderizando en Widgets <span id=\"rendering-in-widgets\"></span>\n\nDentro de [widgets](structure-widgets.md), puedes llamar a cualquier de los siguientes métodos de widget para renderizar una vista.\n\n* [[yii\\base\\Widget::render()|render()]]: renderiza la [vista nombrada](#named-views).\n* [[yii\\base\\Widget::renderFile()|renderFile()]]: renderiza la vista especificada en términos de ruta al archivo\n  o [alias](concept-aliases.md).\n\nPor ejemplo:\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Widget;\nuse yii\\helpers\\Html;\n\nclass ListWidget extends Widget\n{\n    public $items = [];\n\n    public function run()\n    {\n        // renderiza una vista llamada \"list\"\n        return $this->render('list', [\n            'items' => $this->items,\n        ]);\n    }\n}\n```\n\n\n### Renderizar en Vistas <span id=\"rendering-in-views\"></span>\n\nPuedes renderizar una vista dentro de otra vista llamando a algunos de los siguientes métodos provistos por el [[yii\\base\\View|componente view]]:\n\n* [[yii\\base\\View::render()|render()]]: renderiza la [vista nombrada](#named-views).\n* [[yii\\web\\View::renderAjax()|renderAjax()]]: renderiza la [vista nombrada](#named-views) e inyecta\n  todos los archivos y scripts JS/CSS. Esto sucede usualmente en respuestas a las peticiones AJAX.\n* [[yii\\base\\View::renderFile()|renderFile()]]: renderiza la vista especificada en términos de ruta al archivo\n  o [alias](concept-aliases.md).\n\nPor ejemplo, el siguiente código en una vista renderiza el template `_overview.php` encontrado en el mismo directorio\nde la vista renderizada actualmente. Recuerda que la variable `$this` en una vista se refiere al componente [[yii\\base\\View|view]]:\n\n```php\n<?= $this->render('_overview') ?>\n```\n\n\n### Renderizar en Otros Lugares <span id=\"rendering-in-other-places\"></span>\n\nEn cualquier lugar, puedes tener acceso al componente [[yii\\base\\View|view]] utilizando la expresión\n`Yii::$app->view` y entonces llamar a los métodos previamente mencionados para renderizar una vista. Por ejemplo:\n\n```php\n// muestra el template \"@app/views/site/license.php\"\necho \\Yii::$app->view->renderFile('@app/views/site/license.php');\n```\n\n\n### Vistas Nombradas <span id=\"named-views\"></span>\n\nCuando renderizas una vista, puedes especificar el template utilizando tanto el nombre de la vista o la ruta/alias al archivo. En la mayoría de los casos,\nutilizarías la primera porque es más concisa y flexible. Las *vistas nombradas* son vistas especificadas mediante un nombre en vez de una ruta al archivo o alias.\n\nUn nombre de vista es resuelto a su correspondiente ruta de archivo siguiendo las siguientes reglas:\n\n* Un nombre de vista puede omitir la extensión del archivo. En estos casos se utilizará `.php` como extensión del archivo. Por ejemplo,\n  el nombre de vista `about` corresponde al archivo `about.php`.\n* Si el nombre de la vista comienza con doble barra (`//`), la ruta al archivo correspondiente será `@app/views/ViewName`.\n  Esto quiere decir que la vista es buscada bajo el [[yii\\base\\Application::viewPath|ruta de vistas de la aplicación]].\n  Por ejemplo, `//site/about` será resuelto como `@app/views/site/about.php`.\n* Si el nombre de la vista comienza con una barra simple `/`, la ruta al archivo de la vista utilizará como prefijo el nombre de la vista\n  con el [[yii\\base\\Module::viewPath|view path]] del [módulo](structure-modules.md) utilizado actualmente.\n  Si no hubiera módulo activo se utilizará `@app/views/ViewName`. Por ejemplo, `/user/create` será resuelto como\n  `@app/modules/user/views/user/create.php` si el módulo activo es `user`. Si no hubiera módulo activo,\n  la ruta al archivo será `@app/views/user/create.php`.\n* Si la vista es renderizada con un [[yii\\base\\View::context|context]] y dicho contexto implementa [[yii\\base\\ViewContextInterface]],\n  la ruta al archivo se forma utilizando como prefijo la [[yii\\base\\ViewContextInterface::getViewPath()|ruta de vistas]] del contexto\n  de la vista. Esto principalmente aplica a vistas renderizadas en controladores y widgets. Por ejemplo,\n  `about` será resuelto como `@app/views/site/about.php` si el contexto es el controlador `SiteController`.\n* Si la vista es renderizada dentro de otra vista, el directorio que contiene la otra vista será prefijado\n  al nuevo nombre de la vista para formar la ruta a la vista. Por ejemplo, `item` sera resuelto como `@app/views/post/item`\n  si está siendo renderizado desde la vista `@app/views/post/index.php`.\n\nDe acuerdo a las reglas mencionadas, al llamar a `$this->render('view')` en el controlador `app\\controllers\\PostController`\nse renderizará el template `@app/views/post/view.php`, mientras que llamando a `$this->render('_overview')` en la vista\nrenderizará el template `@app/views/post/_overview.php`.\n\n\n### Acceder a Datos en la Vista <span id=\"accessing-data-in-views\"></span>\n\nHay dos modos posibles de acceder a los datos en la vista: push (inyectar) y pull (traer).\n\nAl pasar los datos como segundo parámetro en algún método de renderización, estás utilizando el modo push.\nLos datos deberían ser representados como un array de pares clave-valor. Cuando la vista está siendo renderizada, la función PHP `extract()`\nserá llamada sobre este array así se extraen las variables que contiene a la vista actual.\nPor ejemplo, el siguiente código de renderización en un controlador inyectará dos variables a la vista `report`:\n`$foo = 1` y `$bar = 2`.\n\n```php\necho $this->render('report', [\n    'foo' => 1,\n    'bar' => 2,\n]);\n```\n\nEl modo pull obtiene los datos del [[yii\\base\\View|componente view]] u otros objetos accesibles\nen las vistas (ej. `Yii::$app`). Utilizando el código anterior como ejemplo, dentro de una vista puedes acceder al objeto del controlador\na través de la expresión `$this->context`. Como resultado, te es posible acceder a cualquier propiedad o método\ndel controlador en la vista `report`, tal como el ID del controlador como se muestra a continuación:\n\n```php\nEl ID del controlador es: <?= $this->context->id ?>\n```\n\nPara acceder a datos en la vista, normalmente se prefiere el modo push, ya que hace a la vista menos dependiente\nde los objetos del contexto. La contra es que tienes que construir el array manualmente cada vez, lo que podría\nvolverse tedioso y propenso al error si la misma vista es compartida y renderizada desde diferentes lugares.\n\n\n### Compartir Datos Entre las Vistas <span id=\"sharing-data-among-views\"></span>\n\nEl [[yii\\base\\View|componente view]] provee la propiedad [[yii\\base\\View::params|params]] para que puedas compartir datos\nentre diferentes vistas.\n\nPor ejemplo, en una vista `about`, podrías tener el siguiente código que especifica el segmento actual\ndel breadcrumbs (migas de pan).\n\n```php\n$this->params['breadcrumbs'][] = 'Acerca de Nosotros';\n```\n\nEntonces, en el archivo del [layout](#layouts), que es también una vista, puedes mostrar el breadcrumbs utilizando los datos\npasados a través de [[yii\\base\\View::params|params]]:\n\n```php\n<?= yii\\widgets\\Breadcrumbs::widget([\n    'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],\n]) ?>\n```\n\n\n## Layouts <span id=\"layouts\"></span>\n\nLos layouts son un tipo especial de vista que representan partes comunes de otras múltiples vistas. Por ejemplo, las páginas\nde la mayoría de las aplicaciones Web comparten el mismo encabezado y pie de página. Aunque puedes repetirlos en todas y cada una de las vistas,\nuna mejor forma es hacerlo sólo en el layout e incrustar el resultado de la renderización de la vista\nen un lugar apropiado del mismo.\n\n\n### Crear Layouts <span id=\"creating-layouts\"></span>\n\nDado que los layouts son también vistas, pueden ser creados de manera similar a las vistas comunes. Por defecto, los layouts\nson guardados en el directorio `@app/views/layouts`. Para layouts utilizados dentro de un [módulo](structure-modules.md), deberían ser guardados\nen el directorio `views/layouts` bajo el [[yii\\base\\Module::basePath|directorio del módulo]].\nPuedes personalizar el directorio de layouts por defecto configurando la propiedad [[yii\\base\\Module::layoutPath]]\nde la aplicación o módulos.\n\nEl siguiente ejemplo muestra cómo debe verse un layout. Ten en cuenta que por motivos ilustrativos, hemos simplificado\nbastante el código del layout. En la práctica, probablemente le agregues más contenido, como tags en el `head`, un menú principal, etc.\n\n```php\n<?php\nuse yii\\helpers\\Html;\n\n/**\n * @var \\yii\\web\\View $this\n * @var string $content\n */\n?>\n<?php $this->beginPage() ?>\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\"/>\n    <?= Html::csrfMetaTags() ?>\n    <title><?= Html::encode($this->title) ?></title>\n    <?php $this->head() ?>\n</head>\n<body>\n<?php $this->beginBody() ?>\n    <header>Mi Compañía</header>\n    <?= $content ?>\n    <footer>&copy; 2014 - Mi Compañía</footer>\n<?php $this->endBody() ?>\n</body>\n</html>\n<?php $this->endPage() ?>\n```\n\nComo puedes ver, el layout genera los tags HTML comunes a todas las páginas. Dentro de la sección `<body>`,\nel layout imprime la variable `$content`, que representa el resultado de la renderización del contenido de cada vista\ny es incrustado dentro del layout cuando se llama al método [[yii\\base\\Controller::render()]].\n\nLa mayoría de layouts deberían llamar a los siguientes métodos (como fue mostrado recién). Estos métodos principalmente disparan eventos\nacerca del proceso de renderizado así los scripts y tags registrados en otros lugares pueden ser propiamente inyectados\nen los lugares donde los métodos son llamados.\n\n- [[yii\\base\\View::beginPage()|beginPage()]]: Este método debería ser llamado bien al principio del layout.\n  Esto dispara el evento [[yii\\base\\View::EVENT_BEGIN_PAGE|EVENT_BEGIN_PAGE]], el cual indica el comienzo de la página.\n- [[yii\\base\\View::endPage()|endPage()]]: Este método debería ser llamado al final del layout.\n  Esto dispara el evento [[yii\\base\\View::EVENT_END_PAGE|EVENT_END_PAGE]], indicando el final de la página.\n- [[yii\\web\\View::head()|head()]]: Este método debería llamarse dentro de la sección `<head>` de una página HTML.\n  Esto genera un espacio vacío que será reemplazado con el código del head HTML registrado (ej. link tags, meta tags)\n  cuando una página finaliza el renderizado.\n- [[yii\\base\\View::beginBody()|beginBody()]]: Este método debería llamarse al principio de la sección `<body>`.\n  Esto dispara el evento [[yii\\web\\View::EVENT_BEGIN_BODY|EVENT_BEGIN_BODY]] y genera un espacio vacío que será reemplazado\n  con el código HTML registrado (ej. JavaScript) que apunta al principio del body.\n- [[yii\\base\\View::endBody()|endBody()]]: Este método debería llamarse al final de la sección `<body>`.\n  Esto dispara el evento [[yii\\web\\View::EVENT_END_BODY|EVENT_END_BODY]], que genera un espacio vacío a ser reemplazado\n  por el código HTML registrado (ej. JavaScript) que apunta al final del body.\n\n\n### Acceder a Datos en Layouts <span id=\"accessing-data-in-layouts\"></span>\n\nDentro de un layout, tienes acceso a dos variables predefinidas: `$this` y `$content`. La primera se refiere al componente [[yii\\base\\View|view]],\ncomo en cualquier vista, mientras que la última contiene el resultado de la renderización del contenido de la vista que está siendo renderizada\nal llamar al método [[yii\\base\\Controller::render()|render()]] en los controladores.\n\nSi quieres acceder a otros datos en los layouts, debes utilizar el modo pull que fue descrito en la sub-sección [Accediendo a Datos en la Vista](#accessing-data-in-views).\nSi quieres pasar datos desde al contenido de la vista a un layout, puedes utilizar el método descrito en la\nsub-sección [Compartiendo Datos Entre las Vistas](#sharing-data-among-views).\n\n\n### Utilizar Layouts <span id=\"using-layouts\"></span>\n\nComo se describe en la sub-sección [Renderizando en Controllers](#rendering-in-controllers), cuando renderizas una vista\nllamando al método [[yii\\base\\Controller::render()|render()]] en un controlador, al resultado de dicha renderización le será aplicado un layout.\nPor defecto, el layout `@app/views/layouts/main.php` será el utilizado. \n\nPuedes utilizar un layout diferente configurando la propiedad [[yii\\base\\Application::layout]] o [[yii\\base\\Controller::layout]]. El primero\nse refiere al layout utilizado por todos los controladores, mientras que el último sobrescribe el layout en controladores individuales.\nPor ejemplo, el siguiente código hace que el controlador `post` utilice `@app/views/layouts/post.php` como layout al renderizar sus vistas.\nOtros controladores, asumiendo que su propiedad `layout` no fue modificada,\nutilizarán `@app/views/layouts/main.php` como layout.\n \n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass PostController extends Controller\n{\n    public $layout = 'post';\n    \n    // ...\n}\n```\n\nPara controladores que pertencen a un módulo, puedes también configurar la propiedad [[yii\\base\\Module::layout|layout]] y así utilizar un layout\nen particular para esos controladores. \n\nDado que la propiedad `layout` puede ser configurada en diferentes niveles (controladores, módulos, aplicación), detrás de escena\nYii realiza dos pasos para determinar cuál es el archivo de layout siendo utilizado para un controlador en particular.\n\nEn el primer paso, determina el valor del layout y el módulo de contexto:\n\n- Si la propiedad [[yii\\base\\Controller::layout]] no es `null`, la utiliza como valor del layout y el [[yii\\base\\Controller::module|módulo]]\n  del controlador como el módulo de contexto.\n- Si [[yii\\base\\Controller::layout|layout]] es `null`, busca a través de todos los módulos ancestros del controlador\n  y encuentra el primer módulo cuya propiedad [[yii\\base\\Module::layout|layout]] no es `null`.\n  Utiliza ese módulo y su valor de [[yii\\base\\Module::layout|layout]] como módulo de contexto y como layout seleccionado.\n  Si tal módulo no puede ser encontrado, significa que no se aplicará ningún layout.\n  \nEn el segundo paso, se determina el archivo de layout actual de acuerdo al valor de layout y el módulo de contexto determinado en el primer paso.\nEl valor de layout puede ser:\n\n- un alias de ruta (ej. `@app/views/layouts/main`).\n- una ruta absoluta (ej. `/main`): el valor del layout comienza con una barra. El archivo de layout actual será buscado\n  bajo el [[yii\\base\\Application::layoutPath|layout path]] de la aplicación,\n  que es por defecto `@app/views/layouts`.\n- una ruta relativa (ej. `main`): El archivo de layout actual será buscado bajo el [[yii\\base\\Module::layoutPath|layout path]]\n  del módulo de contexto, que es por defecto el directorio `views/layouts`\n  bajo el [[yii\\base\\Module::basePath|directorio del módulo]].\n- el valor booleano `false`: no se aplicará ningún layout.\n\nSi el valor de layout no contiene una extensión de tipo de archivo, utilizará por defecto `.php`.\n\n\n### Layouts Anidados <span id=\"nested-layouts\"></span>\n\nA veces podrías querer anidar un layout dentro de otro. Por ejemplo, en diferentes secciones de un sitio Web,\npodrías querer utilizar layouts diferentes, mientras que todos esos layouts comparten el mismo layout básico que genera\nla estructura general de la página en HTML5. Esto es posible llamando a los métodos\n[[yii\\base\\View::beginContent()|beginContent()]] y [[yii\\base\\View::endContent()|endContent()]] en los layouts hijos como se muestra a continuación:\n\n```php\n<?php $this->beginContent('@app/views/layouts/base.php'); ?>\n\n...contenido del layout hijo aquí...\n\n<?php $this->endContent(); ?>\n```\n\nComo se acaba de mostrar, el contenido del layout hijo debe ser encerrado dentro de [[yii\\base\\View::beginContent()|beginContent()]]\ny [[yii\\base\\View::endContent()|endContent()]]. El parámetro pasado a [[yii\\base\\View::beginContent()|beginContent()]]\nespecifica cuál es el módulo padre. Este puede ser tanto un archivo layout como un alias.\n\nUtilizando la forma recién mencionada, puedes anidar layouts en más de un nivel.\n\n\n### Utilizar Blocks <span id=\"using-blocks\"></span>\n\nLos bloques te permiten especificar el contenido de la vista en un lugar y mostrarlo en otro. Estos son a menudo utilizados junto a\nlos layouts. Por ejemplo, puedes definir un bloque un una vista de contenido y mostrarla en el layout.\n\nPara definir un bloque, llamas a [[yii\\base\\View::beginBlock()|beginBlock()]] y [[yii\\base\\View::endBlock()|endBlock()]].\nEl bloque puede ser accedido vía `$view->blocks[$blockID]`, donde `$blockID` se refiere al ID único que le asignas\nal bloque cuando lo defines.\n\nEl siguiente ejemplo muestra cómo utilizar bloques para personalizar partes especificas del layout in una vista.\n\nPrimero, en una vista, define uno o varios bloques:\n\n```php\n...\n\n<?php $this->beginBlock('block1'); ?>\n\n...contenido de block1...\n\n<?php $this->endBlock(); ?>\n\n...\n\n<?php $this->beginBlock('block3'); ?>\n\n...contenido de block3...\n\n<?php $this->endBlock(); ?>\n```\n\nEntonces, en la vista del layout, renderiza los bloques si están disponibles, o muestra un contenido por defecto si el bloque\nno está definido.\n\n```php\n...\n<?php if (isset($this->blocks['block1'])): ?>\n    <?= $this->blocks['block1'] ?>\n<?php else: ?>\n    ... contenido por defecto de block1 ...\n<?php endif; ?>\n\n...\n\n<?php if (isset($this->blocks['block2'])): ?>\n    <?= $this->blocks['block2'] ?>\n<?php else: ?>\n    ... contenido por defecto de block2 ...\n<?php endif; ?>\n\n...\n\n<?php if (isset($this->blocks['block3'])): ?>\n    <?= $this->blocks['block3'] ?>\n<?php else: ?>\n    ... contenido por defecto de block3 ...\n<?php endif; ?>\n...\n```\n\n\n## Utilizar Componentes de Vista <span id=\"using-view-components\"></span>\n\nLos [[yii\\base\\View|componentes de vista]] proveen características relacionadas a las vistas. Aunque puedes obtener componentes de vista\ncreando instancias individuales de [[yii\\base\\View]] o sus clases hijas, en la mayoría de los casos utilizarías el componente `view` del a aplicación.\nPuedes configurar este componente en la [configuración de la aplicación](structure-applications.md#application-configurations)\ncomo a continuación:\n\n```php\n[\n    // ...\n    'components' => [\n        'view' => [\n            'class' => 'app\\components\\View',\n        ],\n        // ...\n    ],\n]\n```\n\nLos componentes de vista proveen las siguientes características útiles, cada una descrita en mayor detalle en su propia sección:\n\n* [temas](output-theming.md): te permite desarrollar y cambiar el tema (theme) de tu sitio Web.\n* [caché de fragmentos](caching-fragment.md): te permite guardar en cache un fragmento de una página Web.\n* [manejo de scripts del cliente](output-client-scripts.md): soporte para registro y renderización de CSS y JavaScript.\n* [manejo de asset bundle](structure-assets.md): soporte de registro y renderización de [asset bundles](structure-assets.md).\n* [motores de template alternativos](tutorial-template-engines.md): te permite utilizar otros motores de templates, como\n  [Twig](https://twig.symfony.com/) o [Smarty](https://www.smarty.net/).\n\nPuedes también utilizar frecuentemente el siguiente menor pero útil grupo de características al desarrollar páginas Web.\n\n\n### Definiendo Títulos de Página <span id=\"setting-page-titles\"></span>\n\nToda página Web debería tener un título. Normalmente el tag de título es generado en [layout](#layouts). De todos modos, en la práctica\nel título es determinado en el contenido de las vistas más que en layouts. Para resolver este problema, [[yii\\web\\View]] provee\nla propiedad [[yii\\web\\View::title|title]] para que puedas pasar información del título desde el contenido de la vista a los layouts.\n\nPara utilizar esta característica, en cada contenido de la vista, puedes definir el título de la siguiente manera:\n\n```php\n<?php\n$this->title = 'Título de mi página';\n?>\n```\n\nEntonces en el layout, asegúrate de tener el siguiente código en la sección `<head>` de la página:\n\n```php\n<title><?= Html::encode($this->title) ?></title>\n```\n\n\n### Registrar Meta Tags <span id=\"registering-meta-tags\"></span>\n\nLas páginas Web usualmente necesitan generar varios meta tags necesarios para diferentes grupos. Cómo los títulos de página, los meta tags\naparecen en la sección `<head>` y son usualmente generado en los layouts.\n\nSi quieres especificar cuáles meta tags generar en las vistas, puedes llamar a [[yii\\web\\View::registerMetaTag()]]\ndentro de una de ellas, como se muestra a continuación:\n\n```php\n<?php\n$this->registerMetaTag(['name' => 'keywords', 'content' => 'yii, framework, php']);\n?>\n```\n\nEl código anterior registrará el meta tag \"keywords\" a través del componente view. El meta tag registrado\nno se renderiza hasta que finaliza el renderizado del layout. Para entonces, el siguiente código HTML será insertado\nen el lugar donde llamas a [[yii\\web\\View::head()]] en el layout, generando el siguiente HTML:\n\n```php\n<meta name=\"keywords\" content=\"yii, framework, php\">\n```\n\nTen en cuenta que si llamas a [[yii\\web\\View::registerMetaTag()]] varias veces, esto registrará varios meta tags,\nsin tener en cuenta si los meta tags son los mismo o no.\n\nPara asegurarte de que sólo haya una instancia de cierto tipo de meta tag, puedes especificar una clave al llamar al método.\nPor ejemplo, el siguiente código registra dos meta tags \"description\", aunque sólo el segundo será renderizado.\n\n```php\n$this->registerMetaTag(['name' => 'description', 'content' => 'Este es mi sitio Web cool hecho con Yii!'], 'description');\n$this->registerMetaTag(['name' => 'description', 'content' => 'Este sitio Web es sobre mapaches graciosos.'], 'description');\n```\n\n\n### Registrar Link Tags <span id=\"registering-link-tags\"></span>\n\nTal como los [meta tags](#adding-meta-tags), los link tags son útiles en muchos casos, como personalizar el ícono (favicon) del sitio,\napuntar a una fuente de RSS o delegar OpenID a otro servidor. Puedes trabajar con link tags, al igual que con meta tags,\nutilizando [[yii\\web\\View::registerLinkTag()]]. Por ejemplo, en el contenido de una vista, puedes registrar un link tag como se muestra a continuación:\n\n```php\n$this->registerLinkTag([\n    'title' => 'Noticias en Vivo de Yii',\n    'rel' => 'alternate',\n    'type' => 'application/rss+xml',\n    'href' => 'https://www.yiiframework.com/rss.xml/',\n]);\n```\n\nEl resultado del código es el siguiente:\n\n```html\n<link title=\"Noticias en Vivo de Yii\" rel=\"alternate\" type=\"application/rss+xml\" href=\"https://www.yiiframework.com/rss.xml/\">\n```\n\nAl igual que con [[yii\\web\\View::registerMetaTag()|registerMetaTags()]], puedes especificar una clave al llamar\na [[yii\\web\\View::registerLinkTag()|registerLinkTag()]] para evitar registrar link tags repetidos.\n\n\n## Eventos de Vistas <span id=\"view-events\"></span>\n\nLos [[yii\\base\\View|componentes de vistas]] disparan varios eventos durante el proceso de renderizado de la vista. Puedes responder\na estos eventos para inyectar contenido a la vista o procesar el resultado de la renderización antes de que sea enviada al usuario final.\n\n- [[yii\\base\\View::EVENT_BEFORE_RENDER|EVENT_BEFORE_RENDER]]: disparado al principio del renderizado de un archivo\n  en un controlador. Los manejadores de este evento pueden definir [[yii\\base\\ViewEvent::isValid]] como `false` para cancelar el proceso de renderizado.\n- [[yii\\base\\View::EVENT_AFTER_RENDER|EVENT_AFTER_RENDER]]: disparado luego de renderizar un archivo con la llamada de [[yii\\base\\View::afterRender()]].\n  Los manejadores de este evento pueden obtener el resultado del renderizado a través de [[yii\\base\\ViewEvent::output]] y modificar\n  esta propiedad para cambiar dicho resultado.\n- [[yii\\base\\View::EVENT_BEGIN_PAGE|EVENT_BEGIN_PAGE]]: disparado por la llamada a [[yii\\base\\View::beginPage()]] en layouts.\n- [[yii\\base\\View::EVENT_END_PAGE|EVENT_END_PAGE]]: disparado por la llamada a [[yii\\base\\View::endPage()]] en layouts.\n- [[yii\\web\\View::EVENT_BEGIN_BODY|EVENT_BEGIN_BODY]]: disparado por la llamada a [[yii\\web\\View::beginBody()]] en layouts.\n- [[yii\\web\\View::EVENT_END_BODY|EVENT_END_BODY]]: disparado por la llamada a [[yii\\web\\View::endBody()]] en layouts.\n\nPor ejemplo, el siguiente código inyecta la fecha actual al final del body de la página:\n\n```php\n\\Yii::$app->view->on(View::EVENT_END_BODY, function () {\n    echo date('Y-m-d');\n});\n```\n\n\n## Renderizar Páginas Estáticas <span id=\"rendering-static-pages\"></span>\n\nCon páginas estáticas nos referimos a esas páginas cuyo contenido es mayormente estático y sin necesidad de acceso\na datos dinámicos enviados desde los controladores.\n\nPuedes generar páginas estáticas utilizando un código como el que sigue dentro de un controlador:\n\n```php\npublic function actionAbout()\n{\n    return $this->render('about');\n}\n```\n\nSi un sitio Web contiene muchas páginas estáticas, resultaría tedioso repetir el mismo código en muchos lados.\nPara resolver este problema, puedes introducir una [acción independiente](structure-controllers.md#standalone-actions)\nllamada [[yii\\web\\ViewAction]] en el controlador. Por ejemplo,\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actions()\n    {\n        return [\n            'page' => [\n                'class' => 'yii\\web\\ViewAction',\n            ],\n        ];\n    }\n}\n```\n\nAhora, si creamos una vista llamada `about` bajo el directorio `@app/views/site/pages`, serás capáz de mostrarla\nen la siguiente URL:\n\n```\nhttp://localhost/index.php?r=site%2Fpage&view=about\n```\n\nEl parámetro `GET` `view` le comunica a [[yii\\web\\ViewAction]] cuál es la vista solicitada. La acción entonces buscará\nesta vista dentro de `@app/views/site/pages`. Puedes configurar la propiedad [[yii\\web\\ViewAction::viewPrefix]]\npara cambiar el directorio en el que se buscarán dichas páginas.\n\n\n## Buenas Prácticas <span id=\"best-practices\"></span>\n\nLas vistas son responsables de la presentación de modelos en el formato que el usuario final desea. En general, las vistas\n\n* deberían contener principalmente sólo código de presentación, como HTML, y PHP simple para recorrer, dar formato y renderizar datos.\n* no deberían contener código que realiza consultas a la base de datos. Ese tipo de código debe ir en los modelos.\n* deberían evitar el acceso directo a datos del `request`, como `$_GET` y/o `$_POST`. Esto es una responsabilidad de los controladores.\n  Si se necesitan datos del `request`, deben ser inyectados a la vista desde el controlador.\n* pueden leer propiedades del modelo, pero no debería modificarlas.\n\nPara hacer las vistas más manejables, evita crear vistas que son demasiado complejas o que contengan código redundante.\nPuedes utilizar estas técnicas para alcanzar dicha meta:\n\n* utiliza [layouts](#layouts) para representar secciones comunes (ej. encabezado y footer de la página).\n* divide una vista compleja en varias más simples. Las vistas pequeñas pueden ser renderizadas y unidas una mayor\n  utilizando los métodos de renderización antes descritos.\n* crea y utiliza [widgets](structure-widgets.md) como bloques de construcción de la vista.\n* crea y utilizar helpers para transformar y dar formato a los datos en la vista.\n\n"
  },
  {
    "path": "docs/guide-es/structure-widgets.md",
    "content": "Widgets\n=======\n\nLos _widgets_ son bloques de código reutilizables que se usan en las [vistas](structure-views.md)\npara crear elementos de interfaz de usuario complejos y configurables, de forma orientada a objetos.\nPor ejemplo, un _widget_ de selección de fecha puede generar un selector de fechas bonito que\npermita a los usuarios seleccionar una fecha.  Todo lo que hay que hacer es insertar el siguiente\ncódigo en una vista:\n\n```php\n<?php\nuse yii\\jui\\DatePicker;\n?>\n<?= DatePicker::widget(['name' => 'date']) ?>\n```\n\nYii incluye un buen número de _widgets_, tales como\n[[yii\\widgets\\ActiveForm|formulario activo]],\n[[yii\\widgets\\Menu|menú]],\n[_widgets_ de jQuery UI](https://www.yiiframework.com/extension/yiisoft/yii2-jui), y\n[_widgets_ de Twitter Bootstrap](https://www.yiiframework.com/extension/yiisoft/yii2-bootstrap).\nA continuación presentaremos las nociones básicas de de los _widgets_.  Por favor, refiérase a la\ndocumentación de la API de clases si quiere aprender más acerca del uso de un _widget_ en particular.\n\n\n## Uso de los _widgets_ <span id=\"using-widgets\"></span>\n\nLos _widgets_ se usan principalmente en las [vistas](structure-views.md).  Se puede llamar al método\n[[yii\\base\\Widget::widget()]] para usar un _widget_ en una vista.  El método toma un _array_ de\n[configuración](concept-configurations.md) para inicializar el _widget_ y devuelve la representación\nresultante del _widget_.  Por ejemplo, el siguiente código inserta un _widget_ de selección de fecha\nconfigurado para usar el idioma ruso y guardar la selección en el atributo `from_date` de `$model`.\n\n```php\n<?php\nuse yii\\jui\\DatePicker;\n?>\n<?= DatePicker::widget([\n    'model' => $model,\n    'attribute' => 'from_date',\n    'language' => 'ru',\n    'dateFormat' => 'php:Y-m-d',\n]) ?>\n```\n\nAlgunos _widgets_ pueden coger un bloque de contenido que debería encontrarse entre la invocación de\n[[yii\\base\\Widget::begin()]] y [[yii\\base\\Widget::end()]].  Por ejemplo, el siguiente código usa el\n_widget_ [[yii\\widgets\\ActiveForm]] para generar un formulario de inicio de sesión.  El _widget_\ngenerará las etiquetas `<form>` de apertura y cierre donde se llame a `begin()` y `end()`\nrespectivamente. Cualquier cosa que este en medio se representará tal cual.\n\n```php\n<?php\nuse yii\\widgets\\ActiveForm;\nuse yii\\helpers\\Html;\n?>\n\n<?php $form = ActiveForm::begin(['id' => 'login-form']); ?>\n\n    <?= $form->field($model, 'username') ?>\n\n    <?= $form->field($model, 'password')->passwordInput() ?>\n\n    <div class=\"form-group\">\n        <?= Html::submitButton('Login') ?>\n    </div>\n\n<?php ActiveForm::end(); ?>\n```\n\nHay que tener en cuenta que, a diferencia de [[yii\\base\\Widget::widget()]] que devuelve la\nrepresentación resultante del _widget_, el método [[yii\\base\\Widget::begin()]] devuelve una\ninstancia del _widget_, que se puede usar para generar el contenido del _widget_.\n\n> Nota: Algunos _widgets_ utilizan un [búfer de salida](https://www.php.net/manual/es/book.outcontrol.php)\n> para ajustar el contenido rodeado al invocar [[yii\\base\\Widget::end()]].  Por este motivo se espera\n> que las llamadas a [[yii\\base\\Widget::begin()]] y [[yii\\base\\Widget::end()]] tengan lugar en el\n> mismo fichero de vista.\n> No seguir esta regla puede desembocar en una salida distinta a la esperada.\n\n\n### Configuración de las variables globales predefinidas\n\nLas variables globales predefinidas de un _widget_ se pueden configurar por medio del contenedor\nde inyección de dependencias:\n\n```php\n\\Yii::$container->set('yii\\widgets\\LinkPager', ['maxButtonCount' => 5]);\n```\n\nConsulte la [sección \"Uso práctico\" de la Guía del contenedor de inyección de dependencias](concept-di-container.md#practical-usage) para más detalles.\n\n\n## Creación de _widgets_ <span id=\"creating-widgets\"></span>\n\nPara crear un _widget_, extienda la clase [[yii\\base\\Widget]] y sobrescriba los métodos\n[[yii\\base\\Widget::init()]] y/o [[yii\\base\\Widget::run()]].  Normalmente el método `init()` debería\ncontener el código que inicializa las propiedades del _widget_, mientras que el método `run()`\ndebería contener el código que genera la representación resultante del _widget_.  La representación\nresultante del método `run()` puede pasarse directamente a `echo` o devolverse como una cadena.\n\nEn el siguiente ejemplo, `HelloWidget` codifica en HTML y muestra el contenido asignado a su\npropiedad `message`.  Si la propiedad no está establecida, mostrará «Hello World» por omisión.\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Widget;\nuse yii\\helpers\\Html;\n\nclass HelloWidget extends Widget\n{\n    public $message;\n\n    public function init()\n    {\n        parent::init();\n        if ($this->message === null) {\n            $this->message = 'Hello World';\n        }\n    }\n\n    public function run()\n    {\n        return Html::encode($this->message);\n    }\n}\n```\n\nPara usar este _widget_, simplemente inserte el siguiente código en una vista:\n\n```php\n<?php\nuse app\\components\\HelloWidget;\n?>\n<?= HelloWidget::widget(['message' => 'Good morning']) ?>\n```\n\nAbajo se muestra una variante de `HelloWidget` que toma el contenido insertado entre las llamadas a\n`begin()` y `end()`, lo codifica en HTML y posteriormente lo muestra.\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Widget;\nuse yii\\helpers\\Html;\n\nclass HelloWidget extends Widget\n{\n    public function init()\n    {\n        parent::init();\n        ob_start();\n    }\n\n    public function run()\n    {\n        $content = ob_get_clean();\n        return Html::encode($content);\n    }\n}\n```\n\nComo se puede observar, el búfer de salida de PHP es iniciado en `init()` para que toda salida\nentre las llamadas de `init()` y `run()` puede ser capturada, procesada y devuelta en `run()`.\n\n> Info: Cuando llame a [[yii\\base\\Widget::begin()]], se creará una nueva instancia del _widget_ y se\n> llamará a su método `init()` al final del constructor del _widget_.  Cuando llame a\n> [[yii\\base\\Widget::end()]], se invocará el método `run()` y el resultado que devuelva será pasado\n> a `echo` por `end()`.\n\nEl siguiente código muestra cómo usar esta nueva variante de `HelloWidget`:\n\n```php\n<?php\nuse app\\components\\HelloWidget;\n?>\n<?php HelloWidget::begin(); ?>\n\n    contenido que puede contener <etiqueta>s\n\n<?php HelloWidget::end(); ?>\n```\n\nA veces, un _widget_ puede necesitar representar un gran bloque de contenido.  Aunque que se\npodría incrustar el contenido dentro del método `run()`, es preferible ponerlo dentro de una\n[vista](structure-views.md) y llamar al método [[yii\\base\\Widget::render()]] para representarlo.\nPor ejemplo:\n\n```php\npublic function run()\n{\n    return $this->render('hello');\n}\n```\n\nPor omisión, las vistas para un _widget_ deberían encontrarse en ficheros dentro del directorio\n`WidgetPath/views`, donde `WidgetPath` representa el directorio que contiene el fichero de clase\ndel _widget_.  Por lo tanto, el ejemplo anterior representará el fichero de vista\n`@app/components/views/hello.php`, suponiendo que la clase del _widget_ se encuentre en\n`@app/components`.  Se puede sobrescribir el método [[yii\\base\\Widget::getViewPath()]] para\npersonalizar el directorio que contiene los ficheros de vista del _widget_.\n\n\n## Buenas prácticas <span id=\"best-practices\"></span>\n\nLos _widgets_ son una manera orientada a objetos de reutilizar código de las vistas.\n\nAl crear _widgets_, debería continuar suguiendo el patrón MVC.  En general, se debería mantener la\nlógica en las clases del widget y la presentación en las [vistas](structure-views.md).\n\nLos _widgets_ deberían diseñarse para ser autosuficientes.  Es decir, cuando se use un _widget_, se\ndebería poder ponerlo en una vista sin hacer nada más.  Esto puede resultar complicado si un\n_widget_ requiere recursos externos, tales como CSS, JavaScript, imágenes, etc.  Afortunadamente\nYii proporciona soporte para [paquetes de recursos](structure-asset-bundles.md) (_asset bundles_)\nque se pueden utilizar para resolver este problema.\n\nCuando un _widget_ sólo contiene código de vista, es muy similar a una [vista](structure-views.md).\nDe hecho, en este caso, su única diferencia es que un _widget_ es una clase redistribuible, mientras\nque una vista es sólo un simple script PHP que prefiere mantener dentro de su aplicación.\n"
  },
  {
    "path": "docs/guide-es/test-acceptance.md",
    "content": "Tests de aceptación\n===================\n\nUn test de aceptación verifica escenarios desde la perspectiva de un usuario.\nSe accede a la aplicación testeada por medio de PhpBrowser o de un navegador de verdad.\nEn ambos casos los navegadores se comunican vía HTTP así que la aplicación debe ser\nservida por un servidor web.\n\nLos tests de aceptación se implementan con ayuda del _framework_ Codeception, que tiene\nuna buena documentación:\n\n- [Codeception para el _framework_ Yii](https://codeception.com/for/yii)\n- [Tests funcionales de Codeception](https://codeception.com/docs/04-FunctionalTests)\n\n## Ejecución de tests en las plantillas básica y avanzada\n\nSi ha empezado con la plantilla avanzada, consulte la [guía de testeo](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/start-testing.md)\npara más detalles sobre la ejecución de tests.\n\nSi ha empezado con la plantilla básica, consulte la [sección sobre testeo de su README](https://github.com/yiisoft/yii2-app-basic/blob/master/README.md#testing).\n"
  },
  {
    "path": "docs/guide-es/test-environment-setup.md",
    "content": "Preparación del entorno de pruebas\n==================================\n\nYii 2 ha mantenido oficialmente integración con el _framework_ de testeo [`Codeception`](https://github.com/Codeception/Codeception),\nque le permite crear los siguientes tipos de tests:\n\n- [Unitarias](test-unit.md) - verifica que una unidad simple de código funciona como se espera;\n- [Funcional](test-functional.md) - verifica escenarios desde la perspectiva de un usuario a través de la emulación de un navegador;\n- [De aceptación](test-acceptance.md) - verifica escenarios desde la perspectiva de un usuario en un navegador.\n\nYii provee grupos de pruebas listos para utilizar para los tres tipos de test, tanto en la plantilla de proyecto\n[`yii2-basic`](https://github.com/yiisoft/yii2-app-basic) como en\n[`yii2-advanced`](https://github.com/yiisoft/yii2-app-advanced).\n\nCodeception viene preinstalado tanto en la plantilla de proyecto básica como en la avanzada.\nEn caso de que no use una de estas plantillas, puede instalar Codeception ejecutando\nlas siguientes órdenes de consola:\n\n```\ncomposer require codeception/codeception\ncomposer require codeception/specify\ncomposer require codeception/verify\n```\n"
  },
  {
    "path": "docs/guide-es/test-fixtures.md",
    "content": "Fixtures\n========\n\nLos fixtures son una parte importante de los tests. Su propósito principal es el de preparar el entorno en una estado fijado/conocido\nde manera que los tests sean repetibles y corran de la manera esperada. Yii provee un framework de fixtures que te permite\ndichos fixtures de manera precisa y usarlo de forma simple.\n\nUn concepto clave en el framework de fixtures de Yii es el llamado *objeto fixture*. Un objeto fixture representa\nun aspecto particular de un entorno de pruebas y es una instancia de [[yii\\test\\Fixture]] o heredada de esta. Por ejemplo,\npuedes utilizar `UserFixture` para asegurarte de que la tabla de usuarios de la BD contiene un grupo de datos fijos. Entonces cargas uno o varios\nobjetos fixture antes de correr un test y lo descargas cuando el test ha concluido.\n\nUn fixture puede depender de otros fixtures, especificándolo en su propiedad [[yii\\test\\Fixture::depends]].\nCuando un fixture está siendo cargado, los fixtures de los que depende serán cargados automáticamente ANTES que él;\ny cuando el fixture está siendo descargado, los fixtures dependientes serán descargados DESPUÉS de él.\n\n\nDefinir un Fixture\n------------------\n\nPara definir un fixture, crea una nueva clase que extienda de [[yii\\test\\Fixture]] o [[yii\\test\\ActiveFixture]].\nEl primero es más adecuado para fixtures de propósito general, mientras que el último tiene características mejoradas específicamente\ndiseñadas para trabajar con base de datos y ActiveRecord.\n\nEl siguiente código define un fixture acerca del ActiveRecord `User` y su correspondiente tabla user.\n\n```php\n<?php\nnamespace app\\tests\\fixtures;\n\nuse yii\\test\\ActiveFixture;\n\nclass UserFixture extends ActiveFixture\n{\n    public $modelClass = 'app\\models\\User';\n}\n```\n\n> Tip: Cada `ActiveFixture` se encarga de preparar la tabla de la DB para los tests. Puedes especificar la tabla\n> definiendo tanto la propiedad [[yii\\test\\ActiveFixture::tableName]] o la propiedad [[yii\\test\\ActiveFixture::modelClass]].\n> Haciéndolo como el último, el nombre de la tabla será tomado de la clase `ActiveRecord` especificada en `modelClass`.\n\n> Note: [[yii\\test\\ActiveFixture]] es sólo adecualdo para bases de datos SQL. Para bases de datos NoSQL, Yii provee\n> las siguientes clases `ActiveFixture`:\n>\n> - Mongo DB: [[yii\\mongodb\\ActiveFixture]]\n> - Elasticsearch: [[yii\\elasticsearch\\ActiveFixture]] (desde la versión 2.0.2)\n\n\nLos datos para un fixture `ActiveFixture` son usualmente provistos en un archivo ubicado en `FixturePath/data/TableName.php`,\ndonde `FixturePath` corresponde al directorio conteniendo el archivo de clase del fixture, y `TableName`\nes el nombre de la tabla asociada al fixture. En el ejemplo anterior, el archivo debería ser\n`@app/tests/fixtures/data/user.php`. El archivo de datos debe devolver un array de registros\na ser insertados en la tabla user. Por ejemplo,\n\n```php\n<?php\nreturn [\n    'user1' => [\n        'username' => 'lmayert',\n        'email' => 'strosin.vernice@jerde.com',\n        'auth_key' => 'K3nF70it7tzNsHddEiq0BZ0i-OU8S3xV',\n        'password' => '$2y$13$WSyE5hHsG1rWN2jV8LRHzubilrCLI5Ev/iK0r3jRuwQEs2ldRu.a2',\n    ],\n    'user2' => [\n        'username' => 'napoleon69',\n        'email' => 'aileen.barton@heaneyschumm.com',\n        'auth_key' => 'dZlXsVnIDgIzFgX4EduAqkEPuphhOh9q',\n        'password' => '$2y$13$kkgpvJ8lnjKo8RuoR30ay.RjDf15bMcHIF7Vz1zz/6viYG5xJExU6',\n    ],\n];\n```\n\nPuedes dar un alias al registro tal que más tarde en tu test, puedas referirte a ese registra a través de dicho alias. En el ejemplo anterior,\nlos dos registros tienen como alias `user1` y `user2`, respectivamente.\n\nAdemás, no necesitas especificar los datos de columnas auto-incrementales. Yii automáticamente llenará esos valores\ndentro de los registros cuando el fixture está siendo cargado.\n\n> Tip: Puedes personalizar la ubicación del archivo de datos definiendo la propiedad [[yii\\test\\ActiveFixture::dataFile]].\n> Puedes también sobrescribir [[yii\\test\\ActiveFixture::getData()]] para obtener los datos.\n\nComo se describió anteriormente, un fixture puede depender de otros fixtures. Por ejemplo, un `UserProfileFixture` puede necesitar depender de `UserFixture`\nporque la table de perfiles de usuarios contiene una clave foránea a la tabla user.\nLa dependencia es especificada vía la propiedad [[yii\\test\\Fixture::depends]], como a continuación,\n\n```php\nnamespace app\\tests\\fixtures;\n\nuse yii\\test\\ActiveFixture;\n\nclass UserProfileFixture extends ActiveFixture\n{\n    public $modelClass = 'app\\models\\UserProfile';\n    public $depends = ['app\\tests\\fixtures\\UserFixture'];\n}\n```\n\nLa dependencia también asegura que los fixtures son cargados y descargados en un orden bien definido. En el ejemplo `UserFixture`\nserá siempre cargado antes de `UserProfileFixture` para asegurar que todas las referencias de las claves foráneas existan y será siempre descargado después de `UserProfileFixture`\npor la misma razón.\n\nArriba te mostramos cómo definir un fixture de BD. Para definir un fixture no relacionado a BD\n(por ej. un fixture acerca de archivos y directorios), puedes extender de la clase base más general\n[[yii\\test\\Fixture]] y sobrescribir los métodos [[yii\\test\\Fixture::load()|load()]] y [[yii\\test\\Fixture::unload()|unload()]].\n\n\nUtilizar Fixtures\n-----------------\n\nSi estás utilizando [Codeception](https://codeception.com/) para hacer tests de tu código, deberías considerar el utilizar\nla extensión `yii2-codeception`, que tiene soporte incorporado para la carga y acceso a fixtures.\nEn caso de que utilices otros frameworks de testing, puedes usar [[yii\\test\\FixtureTrait]] en tus casos de tests\npara alcanzar el mismo objetivo.\n\nA continuación describiremos cómo escribir una clase de test de unidad `UserProfile` utilizando `yii2-codeception`.\n\nEn tu clase de test de unidad que extiende de [[yii\\codeception\\DbTestCase]] o [[yii\\codeception\\TestCase]],\nindica cuáles fixtures quieres utilizar en el método [[yii\\test\\FixtureTrait::fixtures()|fixtures()]]. Por ejemplo,\n\n```php\nnamespace app\\tests\\unit\\models;\n\nuse yii\\codeception\\DbTestCase;\nuse app\\tests\\fixtures\\UserProfileFixture;\n\nclass UserProfileTest extends DbTestCase\n{\n    public function fixtures()\n    {\n        return [\n            'profiles' => UserProfileFixture::class,\n        ];\n    }\n\n    // ...métodos de test...\n}\n```\n\nLos fixtures listados en el método `fixtures()` serán automáticamente cargados antes de correr cada método de test\nen el caso de test y descargado al finalizar cada uno. También, como describimos antes, cuando un fixture está\nsiendo cargado, todos sus fixtures dependientes serán cargados primero. En el ejemplo de arriba, debido a que\n`UserProfileFixture` depende de `UserFixture`, cuando ejecutas cualquier método de test en la clase,\ndos fixtures serán cargados secuencialmente: `UserFixture` y `UserProfileFixture`.\n\nAl especificar fixtures en `fixtures()`, puedes utilizar tanto un nombre de clase o un array de configuración para referirte a\nun fixture. El array de configuración te permitirá personalizar las propiedades del fixture cuando este es cargado.\n\nPuedes también asignarles alias a los fixtures. En el ejemplo anterior, el `UserProfileFixture` tiene como alias `profiles`.\nEn los métodos de test, puedes acceder a un objeto fixture utilizando su alias. Por ejemplo, `$this->profiles`\ndevolverá el objeto `UserProfileFixture`.\n\nDado que `UserProfileFixture` extiende de `ActiveFixture`, puedes por lo tanto usar la siguiente sintáxis para acceder\na los datos provistos por el fixture:\n\n```php\n// devuelve el registro del fixture cuyo alias es 'user1'\n$row = $this->profiles['user1'];\n// devuelve el modelo UserProfile correspondiente al registro cuyo alias es 'user1'\n$profile = $this->profiles('user1');\n// recorre cada registro en el fixture\nforeach ($this->profiles as $row) ...\n```\n\n> Info: `$this->profiles` es todavía del tipo `UserProfileFixture`. Las características de acceso mostradas arriba son implementadas\n> a través de métodos mágicos de PHP.\n\n\nDefinir y Utilizar Fixtures Globales\n------------------------------------\n\nLos fixtures descritos arriba son principalmente utilizados para casos de tests individuales. En la mayoría de los casos, puedes necesitar algunos\nfixtures globales que sean aplicados a TODOS o muchos casos de test. Un ejemplo sería [[yii\\test\\InitDbFixture]], que hace\ndos cosas:\n\n* Realiza alguna tarea de inicialización común al ejectutar un script ubicado en `@app/tests/fixtures/initdb.php`;\n* Deshabilita la comprobación de integridad antes de cargar otros fixtures de BD, y la rehabilita después de que todos los fixtures son descargados.\n\nUtilizar fixtures globales es similar a utilizar los no-globales. La única diferencia es que declaras estos fixtures\nen [[yii\\codeception\\TestCase::globalFixtures()]] en vez de en `fixtures()`. Cuando un caso de test carga fixtures,\nprimero carga los globales y luego los no-globales.\n\nPor defecto, [[yii\\codeception\\DbTestCase]] ya declara `InitDbFixture` en su método `globalFixtures()`.\nEsto significa que sólo necesitas trabajar con `@app/tests/fixtures/initdb.php` si quieres realizar algún trabajo de inicialización\nantes de cada test. Sino puedes simplemente enfocarte en desarrollar cada caso de test individual y sus fixtures correspondientes.\n\n\nOrganizar Clases de Fixtures y Archivos de Datos\n------------------------------------------------\n\nPor defecto, las clases de fixtures busca los archivos de datos correspondientes dentro de la carpeta `data`, que es una subcarpeta\nde la carpeta conteniendo los archivos de clases de fixtures. Puedes seguir esta convención al trabajar en proyectos simples.\nPara proyectos más grandes, es probable que a menudo necesites intercambiar entre diferentes archivos de datos para la misma clase de fixture\nen diferentes tests. Recomendamos que organices los archivos de datos en forma jerárquica similar\na tus espacios de nombre de clases. Por ejemplo,\n\n```\n# bajo la carpeta tests\\unit\\fixtures\n\ndata\\\n    components\\\n        fixture_data_file1.php\n        fixture_data_file2.php\n        ...\n        fixture_data_fileN.php\n    models\\\n        fixture_data_file1.php\n        fixture_data_file2.php\n        ...\n        fixture_data_fileN.php\n# y así sucesivamente\n```\n\nDe esta manera evitarás la colisión de archivos de datos de fixtures entre tests y podrás utlilizarlos como necesites.\n\n> Note: En el ejemplo de arriba los archivos de fixtures son nombrados así sólo como ejemplo. En la vida real deberías nombrarlos\n> de acuerdo a qué clase de fixture extienden tus clases de fixtures. Por ejemplo, si estás extendiendo\n> de [[yii\\test\\ActiveFixture]] para fixtures de BD, deberías utilizar nombres de tabla de la BD como nombres de los archivos de fixtures;\n> Si estás extendiendo de [[yii\\mongodb\\ActiveFixture]] para fixtures de MongoDB, deberías utilizar nombres de colecciones para los nombres de archivo.\n\nSe puede utilizar una jerarquía similar para organizar archivos de clases de fixtures. En vez de utilizar `data` como directorio raíz, podrías\nquerer utilizar `fixtures` como directorio raíz para evitar conflictos con los archivos de datos.\n\n\nResumen\n-------\n\n> Note: Esta sección se encuentra en desarrollo.\n\nArriba, definimos cómo definir y utilizar fixtures. Abajo resumiremos el típico flujo de trabajo\nde correr tests de unidad relacionados a BD:\n\n1. Usa la herramienta `yii migrate` para actualizar tu base de datos de prueba a la última versión;\n2. Corre el caso de test:\n   - Carga los fixtures: limpia las tablas de la BD relevantes y cargala con los datos de los fixtures;\n   - Realiza el test en sí;\n   - Descarga los fixtures.\n3. Repite el Paso 2 hasta que todos los tests terminen.\n\n\n**Lo siguiente, a ser limpiado**\n\nAdministrar Fixtures\n====================\n\n> Note: Esta sección está en desarrollo.\n>\n> todo: este tutorial podría ser unificado con la parte de arriba en test-fixtures.md\n\nLos fixtures son una parte importante del testing. Su principal propósito es el de poblarte con datos necesarios para el test\nde diferentes casos. Con estos datos. utilizar tests se vuelve más eficiente y útil.\n\nYii soporta fixtures a través de la herramienta de línea de comandos `yii fixture`. Esta herramienta soporta:\n\n* Cargar fixtures a diferentes almacenamientos: RDBMS, NoSQL, etc;\n* Descargar fixtures de diferentes maneras (usualmente limpiando el almacenamiento);\n* Auto-generar fixtures y poblarlos con datos al azar.\n\nFormato de Fixtures\n-------------------\n\nLos fixtures son objetos con diferentes métodos y configuraciones, inspecciónalos en la [documentación oficial](https://github.com/yiisoft/yii2/blob/master/docs/guide-es/test-fixtures.md).\nAsumamos que tenemos datos de fixtures a cargar:\n\n```\n#archivo users.php bajo la ruta de los fixtures, por defecto @tests\\unit\\fixtures\\data\n\nreturn [\n    [\n        'name' => 'Chase',\n        'login' => 'lmayert',\n        'email' => 'strosin.vernice@jerde.com',\n        'auth_key' => 'K3nF70it7tzNsHddEiq0BZ0i-OU8S3xV',\n        'password' => '$2y$13$WSyE5hHsG1rWN2jV8LRHzubilrCLI5Ev/iK0r3jRuwQEs2ldRu.a2',\n    ],\n    [\n        'name' => 'Celestine',\n        'login' => 'napoleon69',\n        'email' => 'aileen.barton@heaneyschumm.com',\n        'auth_key' => 'dZlXsVnIDgIzFgX4EduAqkEPuphhOh9q',\n        'password' => '$2y$13$kkgpvJ8lnjKo8RuoR30ay.RjDf15bMcHIF7Vz1zz/6viYG5xJExU6',\n    ],\n];\n```\nSi estamos utilizando un fixture que carga datos en la base de datos, entonces esos registros serán insertados en la tabla `users`. Si estamos utilizando fixtures no sql, por ejemplo de `mongodb`,\nentonces estos datos serán aplicados a la colección mongodb `users`. Para aprender cómo implementar varias estrategias de carga y más, visita la [documentación oficial](https://github.com/yiisoft/yii2/blob/master/docs/guide-es/test-fixtures.md).\nEl fixture de ejemplo de arriba fue autogenerado por la extensión `yii2-faker`, lee más acerca de esto en su [sección](#auto-generating-fixtures).\nLos nombres de clase de fixtures no deberían ser en plural.\n\nCargar fixtures\n----------------\n\nLas clases de fixture deberían tener el prefijo `Fixture`. Por defecto los fixtures serán buscados bajo el espacio de nombre `tests\\unit\\fixtures`, puedes\nmodificar este comportamiento con opciones de comando o configuración. Puedes excluir algunos fixtures para carga o descarga especificando `-` antes de su nombre, por ejemplo `-User`.\n\nPara cargar un fixture, ejecuta el siguiente comando:\n\n```\nyii fixture/load <fixture_name>\n```\n\nEl parámetro requerido `fixture_name` especifica un nombre de fixture cuyos datos serán cargados. Puedes cargar varios fixtures de una sola vez.\nAbajo se muestran formatos correctos de este comando:\n\n```\n// carga el fixture `User`\nyii fixture/load User\n\n// lo mismo que arriba, dado que la acción por defecto del comando \"fixture\" es \"load\"\nyii fixture User\n\n// carga varios fixtures\nyii fixture \"User, UserProfile\"\n\n// carga todos los fixtures\nyii fixture/load \"*\"\n\n// lo mismo que arriba\nyii fixture \"*\"\n\n// carga todos los fixtures excepto uno\nyii fixture \"*, -DoNotLoadThisOne\"\n\n// carga fixtures, pero los busca en diferente espacio de nombre. El espacio de nombre por defecto es: tests\\unit\\fixtures.\nyii fixture User --namespace='alias\\my\\custom\\namespace'\n\n// carga el fixture global `some\\name\\space\\CustomFixture` antes de que otros fixtures sean cargados.\n// Por defecto está opción se define como `InitDbFixture` para habilitar/deshabilitar la comprobación de integridad. Puedes especificar varios\n// fixtures globales separados por coma.\nyii fixture User --globalFixtures='some\\name\\space\\Custom'\n```\n\nDescargar fixtures\n------------------\n\nPara descargar un fixture, ejecuta el siguiente comando:\n\n```\n// descarga el fixture Users, por defecto limpiará el almacenamiento del fixture (por ejemplo la tabla \"users\", o la colección \"users\" si es un fixture mongodb).\nyii fixture/unload User\n\n// descarga varios fixtures\nyii fixture/unload \"User, UserProfile\"\n\n// descarga todos los fixtures\nyii fixture/unload \"*\"\n\n// descarga todos los fixtures excepto uno\nyii fixture/unload \"*, -DoNotUnloadThisOne\"\n\n```\n\nOpciones de comando similares como: `namespace`, `globalFixtures` también pueden ser aplicadas a este comando.\n\nConfigurar el Comando Globalmente\n---------------------------------\nMientras que las opciones de línea de comandos nos permiten configurar el comando de migración\nen el momento, a veces queremos configurar el comando de una vez y para siempre. Por ejemplo puedes configurar\ndiferentes rutas de migración como a continuación:\n\n```\n'controllerMap' => [\n    'fixture' => [\n        'class' => 'yii\\console\\controllers\\FixtureController',\n        'namespace' => 'myalias\\some\\custom\\namespace',\n        'globalFixtures' => [\n            'some\\name\\space\\Foo',\n            'other\\name\\space\\Bar'\n        ],\n    ],\n]\n```\n\nAutogenerando fixtures\n----------------------\n\nYii puede también autogenerar fixtures por tí basándose en algún template. Puedes generar tus fixtures con distintos datos en diferentes lenguajes y formatos.\nEsta característica es realizada por la librería [Faker](https://github.com/fzaninotto/Faker) y la extensión `yii2-faker`.\nVisita la [guía de la extensión](https://github.com/yiisoft/yii2-faker) para mayor documentación.\n"
  },
  {
    "path": "docs/guide-es/test-functional.md",
    "content": "Tests funcionales\n=================\n\nLos tests funcionales verifican escenarios desde la perspectiva de un usuario.\nSon similares a los [tests de aceptación](test-acceptance.md) pero en lugar de\ncomunicarse vía HTTP rellena el entorno como parámetros POST y GET y después ejecuta\nuna instancia de la aplicación directamente desde el código.\n\nLos tests funcionales son generalmente más rápidos que los tests de aceptación y\nproporcionan _stack traces_ detalladas en los fallos.\nComo regla general, debería preferirlos salvo que tenga una configuración de servidor\nweb especial o una interfaz de usuario compleja en Javascript.\n\nLas pruebas funcionales se implementan con ayuda del _framework_ Codeception, que tiene\nuna buena documentación:\n\n- [Codeception para el _framework_ Yii](https://codeception.com/for/yii)\n- [Tests funcionales de Codeception](https://codeception.com/docs/04-FunctionalTests)\n\n## Ejecución de tests en las plantillas básica y avanzada\n\nSi ha empezado con la plantilla avanzada, consulte la [guía de testeo](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/start-testing.md)\npara más detalles sobre la ejecución de tests.\n\nSi ha empezado con la plantilla básica, consulte la [sección sobre testeo de su README](https://github.com/yiisoft/yii2-app-basic/blob/master/README.md#testing).\n"
  },
  {
    "path": "docs/guide-es/test-overview.md",
    "content": "Tests\n=====\n\nLas pruebas son una parte importante del desarrollo de software.  Seamos conscientes\nde ello o no, ralizamos pruebas contínuamente.\nPor ejemplo, cuando escribimos una clase en PHP, podemos depurarla paso a paso o\nsimplemente usar declaraciones `echo` o `die` para verificar que la implementación\nfunciona conforme a nuestro plan inicial.  En el caso de una aplicación web, introducimos\nalgunos datos de prueba en los formularios para asegurarnos de que la página interactúa\ncon nosotros como esperábamos.\n\nEl proceso de testeo se puede automatizar para que cada vez que necesitemos verificar\nalgo, solamente necesitemos invocar el código que lo hace por nosotros.  El código que\nverifica que el restulado coincide con lo que habíamos planeado se llama *test* y el proceso\nde su creación y posterior ejecución es conocido como *testeo automatizado*, que es el\nprincipal tema de estos capítulos sobre testeo.\n\n\n## Desarrollo con tests\n\nEl Desarrollo Dirigido por Pruebas (_Test-Driven Development_ o TDD) y el Desarrollo\nDirigido por Corpotamientos (_Behavior-Driven Development_ o BDD) son enfoques para\ndesarrollar software, en los que se describe el comportamiento de un trozo de código\no de toda la funcionalidad como un conjunto de escenarios o pruebas antes de escribir\nel código real y sólo entonces crear la implementación que permite pasar esos tests\nverificando que se ha logrado el comportamiento pretendido.\n\nEl proceso de desarrollo de una funcionalidad es el siguiente:\n\n- Crear un nuevo test que describe una funcionalidad a implementar.\n- Ejecutar el nuevo test y asegurarse de que falla.  Esto es lo esperado, dado que todavía no hay ninguna implementación.\n- Escribir un código sencillo para superar el nuevo test.\n- Ejecutar todos los tests y asegurarse de que se pasan todos.\n- Mejorar el código y asegurarse de que los tests siguen superándose.\n\nUna vez hecho, se repite el proceso de neuvo para otra funcionalidad o mejora.\nSi se va a cambiar la funcionalidad existente, también hay que cambiar los tests.\n\n> Tip: Si siente que está perdiendo tiempo haciendo un montón de iteraciones pequeñas\n> y simples, intente cubrir más por cada escenario de test, de modo que haga más cosas antes\n> de ejecutar los tests de nuevo.  Si está depurando demasiado, intente hacer lo contrario.\n\nLa razón para crear los tests antes de hacer ninguna implementación es que eso nos permite\ncentrarnos en lo que queremos alcanzar y sumergirnos totalmente en «cómo hacerlo» después.\nNormalmente conduce a mejores abstracciones y a un más fácil mantenimiento de los tests\ncuando toque hacer ajustes a las funcionalidades o componentes menos acoplados.\n\nPara resumir, las ventajas de este enfoque son las siguientes:\n\n- Le mantiene centrado en una sola cosa en cada momento, lo que resulta en una mejor planificación e implementación.\n- Resulta en más funcionalidades cubiertas por tests, y en mayor detalle.  Es decir, si se superan los tests, lo más problable es que no haya nada roto.\n\nA largo plazo normalmente tiene como efecto un buen ahorro de tiempo.\n\n## Qué y cómo probar\n\nAunque el enfoque de primero los tests descrito arriba tiene sentido para el largo plazo\ny proyectos relativamente complejos, sería excesivo para proyectos más simples.\nHay algunas indicaciones de cuándo es apropiado:\n\n- El proyecto ya es grande y complejo.\n- Los requisitos del proyecto están empezando a hacerse complejos.  El proyecto crece constantemente.\n- El proyecto pretende a ser a largo plazo.\n- El coste de fallar es demasiado alto.\n\nNo hay nada malo en crear tests que cubran el comportamiento de una implementación existente.\n\n- Es un proyecto legado que se va a renovar gradualmente.\n- Le han dado un proyecto sobre el que trabajar y no tiene tests.\n\nEn algunos casos cualquier forma de testo automatizado sería exagerada:\n\n- El proyecto es sencillo y no se va a volver más complejo.\n- Es un proyecto puntual en el que no se seguirá trabajando.\n\nDe todas formas, si dispone de tiempo, es bueno automatizar las pruebas también en esos casos.\n\n## Más lecturas\n\n- Test Driven Development: By Example / Kent Beck. ISBN: 0321146530.\n"
  },
  {
    "path": "docs/guide-es/test-unit.md",
    "content": "Pruebas unitarias\n=================\n\nUn test unitario se encarga de verificar que una unidad simple de código funcione como se espera.\nEsto decir, dados diferentes parámetros de entrada, el test verifica que el método\nde la clase devuelve el resultado esperado.\nNormalmente los tests unitarios son desarrollados por la persona que escribe las clases testeadas.\n\nLos tests unitarios en Yii están construidos en base a PHPUnit y, opcionalmente, Codeception, por lo que se recomienda consultar su respectiva documentación:\n\n- [Codeception para el _framework_ Yii](https://codeception.com/for/yii)\n- [Tests unitarios con Codeception](https://codeception.com/docs/05-UnitTests)\n- [Documentación de PHPUnit, empezando por el capítulo 2](https://phpunit.de/manual/current/en/writing-tests-for-phpunit.html)\n\n## Ejecución de tests en las plantillas básica y avanzada\n\nSi ha empezado con la plantilla avanzada, consulte la [guía de testeo](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/start-testing.md)\npara más detalles sobre la ejecución de tests.\n\nSi ha empezado con la plantilla básica, consulte la [sección sobre testeo de su README](https://github.com/yiisoft/yii2-app-basic/blob/master/README.md#testing).\n\n##Tests unitarios del framework\n\nSi desea ejecutar tests unitarios para el framework Yii en sí, consulte\n«[Comenzando con el desarrollo de Yii 2](https://github.com/yiisoft/yii2/blob/master/docs/internals/getting-started.md)».\n"
  },
  {
    "path": "docs/guide-es/translators.json",
    "content": "[\n  \"Antonio Ramirez\",\n  \"Daniel Gómez Pan\",\n  \"Enrique Matías Sánchez (Quique)\",\n  \"'larnu'\",\n  \"Luciano Baraglia\"\n]\n"
  },
  {
    "path": "docs/guide-es/tutorial-core-validators.md",
    "content": "Validadores del framework\n=========================\n\nYii provee en su núcleo un conjunto de validadores de uso común, que se pueden encontrar principalmente bajo el espacio de nombres (namespace) `yii\\validators`.\nEn vez de utilizar interminables nombres de clases para los validadores, puedes usar *alias* para especificar el uso de esos validadores del núcleo. Por ejemplo, puedes usar el alias `required` para referirte a la clase [[yii\\validators\\RequiredValidator]] :\n\n```php\npublic function rules()\n{\n    return [\n        [['email', 'password'], 'required'],\n    ];\n}\n```\n\nLa propiedad [[yii\\validators\\Validator::builtInValidators]] declara todos los aliases de los validadores soportados.\n\nA continuación, vamos a describir el uso principal y las propiedades de cada validador del núcleo.\n\n\n## [[yii\\validators\\BooleanValidator|boolean]] <span id=\"boolean\"></span>\n\n```php\n[\n    // comprueba si \"selected\" es 0 o 1, sin mirar el tipo de dato\n    ['selected', 'boolean'],\n\n    // comprueba si \"deleted\" es del tipo booleano, alguno entre `true` o `false`\n    ['deleted', 'boolean', 'trueValue' => true, 'falseValue' => false, 'strict' => true],\n]\n```\n\nEste validador comprueba si el valor de la entrada (input) es booleano.\n\n- `trueValue`: El valor representando `true`. Valor por defecto a `'1'`.\n- `falseValue`: El valor representando `false`. Valor por defecto a `'0'`.\n- `strict`: Si el tipo del valor de la entrada (input) debe corresponder con `trueValue` y `falseValue`. Valor por defecto a `false`.\n\n\n> Note: Ya que los datos enviados con la entrada, vía formularios HTML,son todos cadenas (strings), usted debe normalmente dejar la propiedad  [[yii\\validators\\BooleanValidator::strict|strict]] a `false`.\n\n\n## [[yii\\captcha\\CaptchaValidator|captcha]] <span id=\"captcha\"></span>\n\n```php\n[\n    ['verificationCode', 'captcha'],\n]\n```\n\nEste validador es usualmente usado junto con [[yii\\captcha\\CaptchaAction]] y [[yii\\captcha\\Captcha]] para asegurarse que una entrada es la misma que lo es el código de verificación que enseña el widget [[yii\\captcha\\Captcha|CAPTCHA]].\n\n- `caseSensitive`: cuando la comparación del código de verificación depende de que sean mayúsculas y minúsculas (case sensitive). Por defecto a `false`.\n- `captchaAction`: la [ruta](structure-controllers.md#routes) correspondiente a\n  [[yii\\captcha\\CaptchaAction|CAPTCHA action]] que representa (render) la imagen CAPTCHA. Por defecto`'site/captcha'`.\n- `skipOnEmpty`: cuando la validación puede saltarse si la entrada está vacía. Por defecto a `false`, lo caul permite que la entrada sea necesaria (required).\n  \n\n## [[yii\\validators\\CompareValidator|compare]] <span id=\"compare\"></span>\n\n```php\n[\n    // valida si el valor del atributo \"password\" es igual al  \"password_repeat\"\n    ['password', 'compare'],\n\n    // valida si la edad es mayor que o igual que 30\n    ['age', 'compare', 'compareValue' => 30, 'operator' => '>='],\n]\n```\n\nEste validador compara el valor especificado por la entrada con otro valor y, se asegura si su relación es la especificada por la propiedad `operator`.\n\n- `compareAttribute`: El nombre del valor del atributo con el cual debe compararse. Cuando el validador está siendo usado para validar un atributo, el valor por defecto de esta propiedad debe de ser el nombre de el atributo con el sufijo `_repeat`. Por  ejemplo, si el atributo a ser validado es `password`, entonces esta propiedad contiene por defecto `password_repeat`.\n- `compareValue`: un valor constante con el que el valor de entrada debe ser comparado. Cuando ambos, esta propiedad y `compareAttribute` son especificados, esta preferencia tiene precedencia.\n- `operator`: el operador de comparación. Por defecto vale `==`, permitiendo comprobar si el valor de entrada es igual al de `compareAttribute` o `compareValue`. Los siguientes operadores son soportados:\n     * `==`: comprueba si dos valores son iguales. La comparación se realiza en modo no estricto.\n     * `===`: comprueba si dos valores son iguales. La comparación se realiza en modo estricto.\n     * `!=`: comprueba si dos valores NO son iguales. La comparación se realiza en modo no estricto.\n     * `!==`: comprueba si dos valores NO son iguales. La comparación se realiza en modo estricto.\n     * `>`: comprueba si el valor siendo validado es mayor que el valor con el que se compara.\n     * `>=`: comprueba si el valor siendo validado es mayor o igual que el valor con el que se compara\n     * `<`: comprueba si el valor siendo validado es menor que el valor con el que se compara\n     * `<=`: comprueba si el valor siendo validado es menor o igual que el valor con el que se compara\n\n\n## [[yii\\validators\\DateValidator|date]] <span id=\"date\"></span>\n\n```php\n[\n    [['from', 'to'], 'date'],\n]\n```\n\nEste validador comprueba si el valor de entrada es una fecha, tiempo or fecha/tiempo y tiempo en el formato correcto.\nOpcionalmente, puede convertir el valor de entrada en una fecha/tiempo UNIX y almacenarla en un atributo especificado vía [[yii\\validators\\DateValidator::timestampAttribute|timestampAttribute]].\n\n- `format`: el formato fecha/tiempo en el que debe estar el valor a ser validado. \n   Esto tiene que ser un patrón fecha/tiempo descrito en [manual ICU](https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax).\n   Alternativamente tiene que ser una cadena con el prefijo `php:` representando un formato que ha de ser reconocido por la clase `Datetime` de PHP. Por favor, refiérase a <https://www.php.net/manual/es/datetime.createfromformat.php> sobre los formatos soportados.\n   Si no tiene ningún valor, ha de coger el valor de `Yii::$app->formatter->dateFormat`.\n- `timestampAttribute`: el nombre del atributo al cual este validador puede asignar el fecha/hora UNIX convertida desde la entrada fecha/hora.\n\n\n## [[yii\\validators\\DefaultValueValidator|default]] <span id=\"default\"></span>\n\n```php\n[\n    // pone el valor de \"age\" a null si está vacío\n    ['age', 'default', 'value' => null],\n\n    // pone el valor de \"country\" a \"USA\" si está vacío\n    ['country', 'default', 'value' => 'USA'],\n\n    // asigna \"from\" y \"to\" con una fecha 3 días y 6 días a partir de hoy, si está vacía\n    [['from', 'to'], 'default', 'value' => function ($model, $attribute) {\n        return date('Y-m-d', strtotime($attribute === 'to' ? '+3 days' : '+6 days'));\n    }],\n]\n```\n\nEste validador no valida datos. En cambio, asigna un valor por defecto a los atributos siendo validados, si los atributos están vacíos.\n\n- `value`: el valor por defecto o un elemento llamable de PHP que devuelva el valor por defecto, el cual, va a ser asignado a los atributos siendo validados, si estos están vacíos. La signatura de la función PHP tiene que ser como sigue,\n\n```php\nfunction foo($model, $attribute) {\n    // ... calcula $value ...\n    return $value;\n}\n```\n\n> Info: Cómo determinar si un valor está vacío o no, es un tópico separado cubierto en la sección [Valores Vacíos](input-validation.md#handling-empty-inputs) .\n\n\n## [[yii\\validators\\NumberValidator|double]] <span id=\"double\"></span>\n\n```php\n[\n    // comprueba si  \"salary\" es un número de tipo doble\n    ['salary', 'double'],\n]\n```\n\nEsta validador comprueba si el valor de entrada es un número de tipo doble. Es equivalente a el validador [Número](#number) .\n\n- `max`: el valor límite superior (incluido) de el valor. Si no tiene valor, significa que no se comprueba el valor superior.\n- `min`: el valor límite inferior (incluido) de el valor. Si no tiene valor, significa que no se comprueba el valor inferior.\n\n\n## [[yii\\validators\\EmailValidator|email]] <span id=\"email\"></span>\n\n```php\n[\n    // comprueba si \"email\" es una dirección válida de email\n    ['email', 'email'],\n]\n```\n\nEste validador comprueba si el valor de entrada es una dirección válida de email.\n\n- `allowName`: indica cuando permitir el nombre en la dirección de email (p.e. `John Smith <john.smith@example.com>`). Por defecto a `false`.\n- `checkDNS`, comprobar cuando el dominio del email existe y tiene cualquier registro  A o MX.\n  Es necesario ser consciente que esta comprobación puede fallar debido a problemas temporales de  DNS, incluso si el la dirección es válida actualmente.\n  Por defecto a `false`.\n- `enableIDN`, indica cuando el proceso de validación debe tener en cuenta el informe de IDN (internationalized domain names).\n  Por defecto a `false`. Dese cuenta que para poder usar la validación de IDN has de instalar y activar la extensión de PHP `intl`,  o será lanzada una excepción.\n\n\n## [[yii\\validators\\ExistValidator|exist]] <span id=\"exist\"></span>\n\n```php\n[\n    // a1 necesita que exista una columna con el atributo \"a1\" \n    ['a1', 'exist'],\n\n    // a1 necesita existir,pero su valor puede usar a2 para comprobar la existencia\n    ['a1', 'exist', 'targetAttribute' => 'a2'],\n\n    // a1 y a2 necesitan existir ambos, y ambos pueden recibir un mensaje de error\n    [['a1', 'a2'], 'exist', 'targetAttribute' => ['a1', 'a2']],\n\n    // a1 y a2 necesitan existir ambos, sólo a1 puede recibir el mensaje de error\n    ['a1', 'exist', 'targetAttribute' => ['a1', 'a2']],\n\n    // a1 necesita existir comprobando la existencia ambos a2 y a3 (usando el valor a1)\n    ['a1', 'exist', 'targetAttribute' => ['a2', 'a1' => 'a3']],\n\n    // a1 necesita existir. Si a1 es un array, cada elemento de él tiene que existir.\n    ['a1', 'exist', 'allowArray' => true],\n]\n```\n\nEste validador comprueba si el valor de entrada puede ser encontrado en una columna de una tabla. Sólo funciona con los atributos del modelo [Registro Activo (Active Record)](db-active-record.md). Soporta validación tanto con una simple columna o múltiples columnas.\n\n- `targetClass`: el nombre de la clase [Registro Activo (Active Record)](db-active-record.md) debe de ser usada para mirar por el valor de entrada siendo validado. Si no tiene valor, la clase del modelo actualmente siendo validado puede ser usada.\n- `targetAttribute`: el nombre del atributo en `targetClass` que debe de ser usado para validar la existencia del valor de entrada. Si no tiene valor, puede usar el nombra del atributoactualmente siendo validado.\n  Puede usar una array para validar la existencia de múltiples columnas al mismo tiempo. El array de valores son los atributos que pueden ser usados para validar la existencia, mientras que las claves del array son los atributos a ser validados. Si la clave y el valor son los mismos, solo en ese momento puedes especificar el valor.\n- `filter`: filtro adicional a aplicar a la consulta de la base de datos usado para comprobar la existencia de una valor de entrada.\n  Esto puede ser una cadena o un array representando la condición de la consulta (referirse a [[yii\\db\\Query::where()]] sobre el formato de la condición de consulta), o una función anónima con la signatura `function ($query)`, donde `$query` es el objeto [[yii\\db\\Query|Query]] que puedes modificar en la función.\n- `allowArray`: indica cuando permitir que el valor de entrada sea un array. Por defecto a `false`.Si la propiedad es `true` y la entrada es un array, cada elemento del array debe existir en la columna destino. Nota que esta propiedad no puede ser `true` si estás validando, por el contrario, múltiple columnas poniendo el valor del atributo `targetAttribute` como que es un array.\n\n\n## [[yii\\validators\\FileValidator|file]] <span id=\"file\"></span>\n\n```php\n[\n    // comprueba si \"primaryImage\" es un fichero mde imagen en formato PNG, JPG o GIF.\n    // el tamaño del fichero ha de ser menor de 1MB\n    ['primaryImage', 'file', 'extensions' => ['png', 'jpg', 'gif'], 'maxSize' => 1024*1024*1024],\n]\n```\n\nEste validador comprueba que el fichero subido es el adecuado.\n\n- `extensions`: una lista de extensiones de ficheros que pueden ser subidos. Esto puede ser tanto un array o una cadena conteniendo nombres de extensiones de ficheros separados por un espacio o coma (p.e. \"gif, jpg\").\n  Los nombres de las extensiones no diferencian mayúsculas de minúsculas (case-insensitive). Por defecto a `null`, permitiendo todas los nombres de extensiones de fichero.\n- `mimeTypes`: una lista de tipos de ficheros MIME  que están permitidos subir. Esto puede ser tanto un array como una cadena conteniendo tipos de fichero MIME separados por un espacio o una coma (p.e. \"image/jpeg, image/png\").\n  Los tipos Mime no diferencian mayúsculas de minúsculas (case-insensitive). Por defecto a `null`, permitiendo todos los tipos MIME.\n- `minSize`: el número de bytes mínimo requerido para el fichero subido. El tamaño del fichero ha de ser superior a este valor. Por defecto a `null`, lo que significa sin límite inferior. \n- `maxSize`: El número máximo de bytes del fichero a subir. El tamaño del fichero ha de ser inferior a este valor. Por defecto a `null`, significando no tener límite superior.\n- `maxFiles`: el máximo número de ficheros que determinado atributo puede manejar. Por defecto a 1, lo que significa que la entrada debe de ser sólo un fichero. Si es mayor que 1, entonces la entrada tiene que ser un array conteniendo como máximo el número `maxFiles` de elementos que representan los ficheros a subir.\n- `checkExtensionByMimeType`: cuando comprobar la extensión del fichero por el tipo  MIME. Si la extensión producida por la comprobación del tipo MIME difiere la extensión del fichero subido, el fichero será considerado como no válido. Por defecto a `true`, significando que realiza este tipo de comprobación.\n\n`FileValidator` es usado con [[yii\\web\\UploadedFile]]. Por favor, refiérase a la sección [Subida de ficheros](input-file-upload.md) para una completa cobertura sobre la subida de ficheros y llevar a cabo la validación de los ficheros subidos.\n\n\n## [[yii\\validators\\FilterValidator|filter]] <span id=\"filter\"></span>\n\n```php\n[\n    // recorta (trim) las entradas \"username\" y \"email\"\n    [['username', 'email'], 'filter', 'filter' => 'trim', 'skipOnArray' => true],\n\n    // normaliza la entrada de  \"phone\"\n    ['phone', 'filter', 'filter' => function ($value) {\n        // normaliza la entrada del teléfono aquí\n        return $value;\n    }],\n]\n```\n\nEste validador no valida datos. En su lugar, aplica un filtro sobre el valor de entrada y le asigna de nuevo el atributo siendo validado.\n\n- `filter`: una retrollamada (callback) de PHP que define un filtro. Tiene que ser un nombre de función global, una función anónima, etc.\n  La forma de la función ha de ser `function ($value) { return $newValue; }`. Tiene que contener un valor esta propiedad.\n- `skipOnArray`: cuando evitar el filtro si el valor de la entrada es un array. Por defecto a `false`.\n  A tener en cuenta que si el filtro no puede manejar una entrada de un array, debes poner esta propiedad a `true`. En otro caso algún error PHP puede ocurrir.\n\n> Consejo (Tip): Si quieres recortar los valores de entrada, puedes usar directamente el validador [Recorte (trim)](#trim).\n\n\n## [[yii\\validators\\ImageValidator|image]] <span id=\"image\"></span>\n\n```php\n[\n    // comprueba si \"primaryImage\"  es una imágen vaĺida con el tamaño adecuado\n    ['primaryImage', 'image', 'extensions' => 'png, jpg',\n        'minWidth' => 100, 'maxWidth' => 1000,\n        'minHeight' => 100, 'maxHeight' => 1000,\n    ],\n]\n```\n\nEste validador comprueba si el valor de entrada representa un fichero de imagen válido. Extiende al validador [Fichero (file)](#file) y, por lo tanto, hereda todas sus propiedades. Además, soporta las siguientes propiedades adicionales específicas para la validación de imágenes:\n\n- `minWidth`: el mínimo ancho de la imagen. Por defecto a `null`, indicando que no hay límite inferior.\n- `maxWidth`: el máximo ancho de la imagen. Por defecto a `null`, indicando que no hay límite superior.\n- `minHeight`: el mínimo alto de la imagen. Por defecto a `null`, indicando que no hay límite inferior.\n- `maxHeight`: el máximo alto de la imagen. Por defecto a `null`, indicando que no hay límite superior.\n\n\n## [[yii\\validators\\RangeValidator|in]] <span id=\"in\"></span>\n\n```php\n[\n    // comprueba si \"level\" es 1, 2 o 3\n    ['level', 'in', 'range' => [1, 2, 3]],\n]\n```\n\nEste validador comprueba si el valor de entrada puede encontrarse entre determinada lista de valores.\n\n- `range`: una lista de determinados valores dentro de los cuales el valor de entrada debe de ser mirado.\n- `strict`: cuando la comparación entre el valor de entrada y los valores determinados debe de ser estricta (ambos el tipo y el valor han de ser iguales). Por defecto a `false`.\n- `not`: cuando el resultado de la validación debe de ser invertido. Por defecto a `false`. Cuando esta propiedad está a `true`, el validador comprueba que el valor de entrada NO ESTÁ en la determinada lista de valores.\n- `allowArray`: si se permite que el valor de entrada sea un array. Cuando es `true` y el valor de entrada es un array, cada elemento en el array debe de ser encontrado en la lista de valores determinada,o la validación fallará.\n\n\n## [[yii\\validators\\NumberValidator|integer]] <span id=\"integer\"></span>\n\n```php\n[\n    // comrpueba si \"age\" es un entero\n    ['age', 'integer'],\n]\n```\n\nEsta validador comprueba si el valor de entrada es un entero.\n\n- `max`: el valor superior  (incluido) . Si no tiene valor, significa que el validador no comprueba el límite superior.\n- `min`: el valor inferior (incluido). Si no tiene valor, significa que el validador no comprueba el límite inferior.\n\n\n## [[yii\\validators\\RegularExpressionValidator|match]] <span id=\"match\"></span>\n\n```php\n[\n    // comprueba si \"username\" comienza con una letra y contiene solamente caracteres en sus palabras\n    ['username', 'match', 'pattern' => '/^[a-z]\\w*$/i']\n]\n```\n\nEste validador comprueba si el valor de entrada coincide con la expresión regular especificada.\n\n- `pattern`: la expresión regular conla que el valor de entrada debe coincidir. Esta propiedad no puede estar vacía, o se lanzará una excepción.\n- `not`: indica cuando invertir el resultado de la validación. Por defecto a `false`, significando que la validación es exitosa solamente si el valor de entrada coincide con el patrón. Si esta propiedad está a `true`, la validación es exitosa solamente si el valor de entrada NO coincide con el patrón.\n\n\n## [[yii\\validators\\NumberValidator|number]] <span id=\"number\"></span>\n\n```php\n[\n    // comprueba si \"salary\" es un número\n    ['salary', 'number'],\n]\n```\n\nEste validador comprueba si el valor de entrada es un número. Es equivalente al validador [Doble precisión (double)](#double).\n\n- `max`: el valor superior límite (incluido) . Si no tiene valor, significa que el validador no comprueba el valor límite superior.\n- `min`: el valor inferior límite (incluido) . Si no tiene valor, significa que el validador no comprueba el valor límite inferior.\n\n\n## [[yii\\validators\\RequiredValidator|required]] <span id=\"required\"></span>\n\n```php\n[\n    // comprueba si ambos \"username\" y \"password\" no están vacíos\n    [['username', 'password'], 'required'],\n]\n```\n\nEl validador comprueba si el valor de entrada es provisto y no está vacío.\n\n- `requiredValue`: el valor deseado que la entrada debería tener. Si no tiene valor, significa que la entrada no puede estar vacía.\n- `strict`: indica como comprobar los tipos de los datos al validar un valor. Por defecto a `false`.\n  Cuando `requiredValue` no tiene valor, si esta propiedad es `true`, el validador comprueba si el valor de entrada no es estrictamente `null`; si la propiedad es `false`, el validador puede usar una regla suelta para determinar si el valor está vacío o no.\n  Cuando `requiredValue` tiene valor, la comparación entre la entrada y  `requiredValue` comprobará tambien los tipos de los datos si esta propiedad es `true`.\n\n> Info: Como determinar si un valor está vacío o no es un tópico separado cubierto en la sección [Valores vacíos](input-validation.md#handling-empty-inputs).\n\n\n## [[yii\\validators\\SafeValidator|safe]] <span id=\"safe\"></span>\n\n```php\n[\n    // marca  \"description\" como un atributo seguro\n    ['description', 'safe'],\n]\n```\n\nEste validador no realiza validación de datos. En lugar de ello, es usado para marcar un atributo como seguro [atributos seguros](structure-models.md#safe-attributes).\n\n\n## [[yii\\validators\\StringValidator|string]] <span id=\"string\"></span>\n\n```php\n[\n    // comprueba si \"username\" es una cadena cuya longitud está entre 4 Y 24\n    ['username', 'string', 'length' => [4, 24]],\n]\n```\n\nEste validador comprueba si el valor de entrada es una cadena válida con determinada longitud.\n\n- `length`: especifica la longitud límite de la cadena de entrada a validar. Esto tiene que ser especificado del las siguientes formas:\n     * un entero: la longitud exacta que la cadena debe de tener;\n     * un array de un elemento: la longitud mínima de la cadena de entrada (p.e.`[8]`). Esto puede sobre escribir `min`.\n     * un array de dos elementos: las longitudes mínima y mmáxima de la cadena de entrada (p.e. `[8, 128]`).\n     Esto sobreescribe ambos valores de `min` y `max`.\n- `min`: el mínimo valor de longitud de la cadena de entrada. Si no tiene valor, significa que no hay límite para longitud mínima.\n- `max`: el máximo valor de longitud de la cadena de entrada. Si no tiene valor, significa que no hay límite para longitud máxima.\n- `encoding`: la codificación de la cadena de entrada a ser validada. Si no tiene valor, usará el valor de la aplicación [[yii\\base\\Application::charset|charset]]  que por defecto es `UTF-8`.\n\n\n## [[yii\\validators\\FilterValidator|trim]] <span id=\"trim\"></span>\n\n```php\n[\n    // recorta (trim) los espacios en blanco que rodean a \"username\" y \"email\"\n    [['username', 'email'], 'trim'],\n]\n```\n\nEste validador no realiza validación de datos. En cambio, recorta los espacios que rodean el valor de entrada. Nota que si el valor de entrada es un array, se ignorará este validador.\n\n\n## [[yii\\validators\\UniqueValidator|unique]] <span id=\"unique\"></span>\n\n```php\n[\n    // a1 necesita ser único en la columna representada por el atributo \"a1\"\n    ['a1', 'unique'],\n\n    // a1 necesita ser único, pero la columna a2 puede ser usado para comprobar la unicidad del valor a1\n    ['a1', 'unique', 'targetAttribute' => 'a2'],\n\n    // a1 y a2 necesitan ambos ser únicos, y ambospueden recibir el mensaje de error\n    [['a1', 'a2'], 'unique', 'targetAttribute' => ['a1', 'a2']],\n\n    // a1 y a2 necesitan ser unicos ambos, solamente uno recibirá el mensaje de error\n    ['a1', 'unique', 'targetAttribute' => ['a1', 'a2']],\n\n    // a1 necesita ser único comprobando la unicidad de ambos a2 y a3 (usando el valor)\n    ['a1', 'unique', 'targetAttribute' => ['a2', 'a1' => 'a3']],\n]\n```\n\nEste validador comprueba si el valor de entrada es único en una columna de una tabla. Solo funciona con los atributos del modelo [Registro Activo (Active Record)](db-active-record.md). Soporta validación contra cualquiera de los casos, una columna o múltiples columnas.\n\n- `targetClass`: el nombre de la clase [Registro Activo (Active Record)](db-active-record.md) que debe de ser usada para mirar por el valor de entrada que está siendo validado. Si no tiene valor, la clase del modelo actualmente validado será usada.\n- `targetAttribute`: el nombre de el atributo en `targetClass`que debe de ser usado para validar la unicidad de el valor de entrada. Si no tiene valor, puede usar el nombre del atributo actualmente siendo validado.\n  Puedes usar un array para validar la unicidad de múltiples columnas al mismo tiempo. Los valores del array son atributos que pueden ser usados para validar la unicidad, mientras que las claves del array son los atributos que cuyos valores van a ser validados. Si la clave y el valor son el mismo, entonces puedes especificar el valor.\n- `filter`: filtro adicional puede ser aplicado a la consulta de la base de datos usado para comprobar la unicidad del valor de entrada.\n  Esto puede ser una cadena o un array representando la condición adicional a la consulta (Referirse a [[yii\\db\\Query::where()]] para el formato de la condición de la consulta), o una función anónima de la forma  `function ($query)`, donde `$query` es el objeto [[yii\\db\\Query|Query]] que puedes modificar en la función.\n\n\n## [[yii\\validators\\UrlValidator|url]] <span id=\"url\"></span>\n\n```php\n[\n    // comprueba si \"website\" es una URL válida. Prefija con \"http://\" al atributo  \"website\"\n    // si no tiene un esquema URI\n    ['website', 'url', 'defaultScheme' => 'http'],\n]\n```\n\nEste validador comprueba si el valor de entrada es una URL válida.\n\n- `validSchemes`: un array especificando el esquema URI que debe ser considerado válido. Por defecto contiene `['http', 'https']`, significando que ambas URLS `http` y `https` son consideradas válidas.\n- `defaultScheme`: el esquema de URI a poner como prefijo a la entrada si no tiene la parte del esquema.\n  Por defecto a `null`, significando que no modifica el valor de entrada.\n- `enableIDN`: Si el validador debe formar parte del registro IDN (internationalized domain names).\n  Por defecto a `false`. Nota que para usar la validación IDN tienes que instalar y activar la extensión PHP `intl`, en otro caso una excepción será lanzada.\n\n"
  },
  {
    "path": "docs/guide-es/tutorial-mailing.md",
    "content": "Envío de Emails\n===============\n\n> Note: Esta sección se encuentra en desarrollo.\n\nYii soporta composición y envío de emails. De cualquier modo, el núcleo del framework provee\nsólo la funcionalidad de composición y una interfaz básica. En mecanismo de envío en sí debería\nser provisto por la extensión, dado que diferentes proyectos pueden requerir diferente implementación y esto\nusualmente depende de servicios y librerías externas.\n\nPara la mayoría de los casos, puedes utilizar la extensión oficial [yii2-swiftmailer](https://github.com/yiisoft/yii2-swiftmailer).\n\n\nConfiguración\n-------------\n\nLa configuración del componente Mail depende de la extensión que hayas elegido.\nEn general, la configuración de tu aplicación debería verse así:\n\n```php\nreturn [\n    //....\n    'components' => [\n        'mailer' => [\n            'class' => 'yii\\swiftmailer\\Mailer',\n        ],\n    ],\n];\n```\n\n\nUso Básico\n----------\n\nUna vez configurado el componente 'mailer', puedes utilizar el siguiente código para enviar un correo electrónico:\n\n```php\nYii::$app->mailer->compose()\n    ->setFrom('from@domain.com')\n    ->setTo('to@domain.com')\n    ->setSubject('Asunto del mensaje')\n    ->setTextBody('Contenido en texto plano')\n    ->setHtmlBody('<b>Contenido HTML</b>')\n    ->send();\n```\n\nEn el ejemplo anterior, el método `compose()` crea una instancia del mensaje de correo, el cual puede ser llenado y enviado.\nEn caso de ser necesario, puedes agregar una lógica más compleja en el proceso:\n\n```php\n$message = Yii::$app->mailer->compose();\nif (Yii::$app->user->isGuest) {\n    $message->setFrom('from@domain.com')\n} else {\n    $message->setFrom(Yii::$app->user->identity->email)\n}\n$message->setTo(Yii::$app->params['adminEmail'])\n    ->setSubject('Asunto del mensaje')\n    ->setTextBody('Contenido en texto plano')\n    ->send();\n```\n\n> Note: cada extensión 'mailer' viene en dos grandes clases: 'Mailer' y 'Message'. 'Mailer' siempre conoce\n  el nombre de clase especifico de 'Message'. No intentes instanciar el objeto 'Message' directamente -\n  siempre utiliza el método `compose()` para ello.\n\nPuedes también enviar varios mensajes al mismo tiempo:\n\n```php\n$messages = [];\nforeach ($users as $user) {\n    $messages[] = Yii::$app->mailer->compose()\n        // ...\n        ->setTo($user->email);\n}\nYii::$app->mailer->sendMultiple($messages);\n```\n\nAlgunas extensiones en particular pueden beneficiarse de este enfoque, utilizando mensaje simple de red, etc.\n\n\nComponer el contenido del mensaje\n---------------------------------\n\nYii permite componer el contenido de los mensajes de correo a través de archivos de vista especiales.\nPor defecto, estos archivos deben estar ubicados en la ruta '@app/mail'.\n\nEjemplo de archivo de contenido de correo:\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\helpers\\Url;\n\n\n/** \n * @var \\yii\\web\\View $this instancia del componente view\n * @var \\yii\\mail\\BaseMessage $message instancia del mensaje de correo recién creado\n */\n\n?>\n<h2>Este mensaje te permite visitar nuestro sitio con un sólo click</h2>\n<?= Html::a('Ve a la página principal', Url::home('http')) ?>\n```\n\nPara componer el contenido del mensaje utilizando un archivo, simplemente pasa el nombre de la vista al método `compose()`:\n\n```php\nYii::$app->mailer->compose('home-link') // el resultado del renderizado de la vista se transforma en el cuerpo del mensaje aquí\n    ->setFrom('from@domain.com')\n    ->setTo('to@domain.com')\n    ->setSubject('Asunto del mensaje')\n    ->send();\n```\n\nPuedes pasarle parámetros adicionales a la vista en el método `compose()`, los cuales estarán disponibles dentro de las vistas:\n\n```php\nYii::$app->mailer->compose('greetings', [\n    'user' => Yii::$app->user->identity,\n    'advertisement' => $adContent,\n]);\n```\n\nPuedes especificar diferentes archivos de vista para el contenido del mensaje en HTML y texto plano:\n\n```php\nYii::$app->mailer->compose([\n    'html' => 'contact-html',\n    'text' => 'contact-text',\n]);\n```\n\nSi especificas el nombre de la vista como un string, el resultado de su renderización será utilizado como cuerpo HTML, mientras\nque el cuerpo en texto plano será compuesto removiendo todas las entidades HTML del anterior.\n\nEl resultado de la renderización de la vista puede ser envuelta en el layout, que puede ser definido utiliazando [[yii\\mail\\BaseMailer::htmlLayout]]\ny [[yii\\mail\\BaseMailer::textLayout]]. Esto funciona igual a como funcionan los layouts en una aplicación web normal.\nEl layout puede utilizar estilos CSS u otros contenidos compartidos:\n\n```php\n<?php\nuse yii\\helpers\\Html;\n\n/**\n * @var \\yii\\web\\View $this instancia del componente view\n * @var \\yii\\mail\\MessageInterface $message el mensaje siendo compuesto\n * @var string $content el resultado de la renderización de la vista principal\n */\n?>\n<?php $this->beginPage() ?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n<head>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=<?= Yii::$app->charset ?>\" />\n    <style type=\"text/css\">\n        .heading {...}\n        .list {...}\n        .footer {...}\n    </style>\n    <?php $this->head() ?>\n</head>\n<body>\n    <?php $this->beginBody() ?>\n    <?= $content ?>\n    <div class=\"footer\">Saludos cordiales, el equipo de<?= Yii::$app->name ?></div>\n    <?php $this->endBody() ?>\n</body>\n</html>\n<?php $this->endPage() ?>\n```\n\n\nAdjuntar archivos\n-----------------\n\nPuedes adjuntar archivos al mensaje utilizando los métodos `attach()` y `attachContent()`:\n\n```php\n$message = Yii::$app->mailer->compose();\n\n// Adjunta un archivo del sistema local de archivos:\n$message->attach('/path/to/file.pdf');\n\n// Crear adjuntos sobre la marcha\n$message->attachContent('Contenido adjunto', ['fileName' => 'attach.txt', 'contentType' => 'text/plain']);\n```\n\n\nIncrustar imágenes\n------------------\n\nPuedes incrustar imágenes en el mensaje utilizando el método `embed()`. Este método devuelve el id del adjunto,\nque debería ser utilizado como tag 'img'.\nEste método es fácil de utilizar al componer mensajes a través de un archivo de vista:\n\n```php\nYii::$app->mailer->compose('embed-email', ['imageFileName' => '/path/to/image.jpg'])\n    // ...\n    ->send();\n```\n\nEntonces, dentro de tu archivo de vista, puedes utilizar el siguiente código:\n\n```php\n<img src=\"<?= $message->embed($imageFileName); ?>\">\n```\n\n\nTestear y depurar\n-----------------\n\nUn desarrollador a menudo necesita comprobar qué emails están siendo enviados por la aplicación, cuál es su contenido y otras cosas.\nYii concede dicha habilidad vía `yii\\mail\\BaseMailer::useFileTransport`. Si se habilita, esta opción hace que\nlos datos del mensaje sean guardados en archivos locales en vez de enviados. Esos archivos serán guardados bajo\n`yii\\mail\\BaseMailer::fileTransportPath`, que por defecto es '@runtime/mail'.\n\n> Note: puedes o bien guardar los mensajes en archivos, o enviarlos a sus receptores correspondientes, pero no puedes hacer las dos cosas al mismo tiempo.\n\nUn archivo de mensaje puede ser abierto por un editor de texto común, de modo que puedas ver sus cabeceras, su contenido y demás.\nEste mecanismo en sí puede comprobarse al depurar la aplicación o al ejecutar un test de unidad.\n\n> Note: el archivo de contenido de mensaje es compuesto vía `\\yii\\mail\\MessageInterface::toString()`, por lo que depende de la extensión\n  actual de correo utilizada en tu aplicación.\n\n\nCrear tu solución personalizada de correo\n-----------------------------------------\n\nPara crear tu propia solución de correo, necesitas crear 2 clases: una para 'Mailer' y\notra para 'Message'.\nPuedes utilizar `yii\\mail\\BaseMailer` y `yii\\mail\\BaseMessage` como clases base de tu solución. Estas clases\nya contienen un lógica básica, la cual se describe en esta guía. De cualquier modo, su utilización no es obligatoria, es suficiente\ncon implementar las interfaces `yii\\mail\\MailerInterface` y `yii\\mail\\MessageInterface`.\nLuego necesitas implementar todos los métodos abstractos para construir tu solución.\n"
  },
  {
    "path": "docs/guide-es/tutorial-start-from-scratch.md",
    "content": "Crear tu propia estructura de Aplicación\n========================================\n\n> Note: Esta sección se encuentra en desarrollo.\n\nMientras que los templates de proyectos [basic](https://github.com/yiisoft/yii2-app-basic) y [advanced](https://github.com/yiisoft/yii2-app-advanced)\nson grandiosos para la mayoría de tus necesidades, podrías querer crear tu propio template de proyecto del cual\npartir todos tus proyectos.\n\nLos templates de proyectos en Yii son simplemente repositorios conteniendo un archivo `composer.json`, y registrado como un paquete de Composer.\nCualquier repositorio puede ser identificado como paquete Composer, haciéndolo instalable a través del comando de Composer `create-project`.\n\nDado que es un poco demasiado comenzar tu template de proyecto desde cero, es mejor utilizar uno de los\ntemplates incorporados como una base. Utilicemos el template básico aquí.\n\nClonar el Template Básico\n-------------------------\n\nEl primer paso es clonar el template básico de Yii desde su repositorio Git:\n\n```bash\ngit clone git@github.com:yiisoft/yii2-app-basic.git\n```\n\nEntonces espera que el repositorio sea descargado a tu computadora. Dado que los cambios realizados al template no serán enviados al repositorio, puedes eliminar el directorio `.git`\ny todo su contenido de la descarga.\n\nModificar los Archivos\n----------------------\n\nA continuación, querrás modificar el archivo `composer.json` para que refleje tu template. Cambia los valores de `name`, `description`, `keywords`, `homepage`, `license`, y `support`\nde forma que describa tu nuevo template. También ajusta las opciones `require`, `require-dev`, `suggest`, y demás para que encajen con los requerimientos de tu template.\n\n> Note: En el archivo `composer.json`, utiliza el parámetro `writable` (bajo `extra`) para especificar\n> permisos-por-archivo a ser definidos después de que la aplicación es creada a partir del template.\n\nLuego, pasa a modificar la estructura y contenido de la aplicación como te gustaría que sea por defecto. Finalmente, actualiza el archivo README para que sea aplicable a tu template.\n\nHacer un Paquete\n----------------\n\nCon el template definido, crea un repositorio Git a partir de él, y sube tus archivos ahí. Si tu template va a ser de código abierto, [Github](https://github.com) es el mejor lugar para alojarlo. Si tu intención es que el template no sea colaborativo, cualquier sitio de repositorios Git servirá.\n\nAhora, necesitas registrar tu paquete para Composer. Para templates públicos, el paquete debe ser registrado en [Packagist](https://packagist.org/).\nPara templates privados, es un poco más complicado registrarlo. Puedes ver instrucciones para hacerlo en la [documentación de Composer](https://getcomposer.org/doc/05-repositories.md#hosting-your-own).\n\nUtilizar el Template\n--------------------\n\nEso es todo lo que se necesita para crear un nuevo template de proyecto Yii. Ahora puedes crear tus propios proyectos a partir de este template:\n\n```\ncomposer global require \"fxp/composer-asset-plugin:^1.4.1\"\ncomposer create-project --prefer-dist --stability=dev mysoft/yii2-app-coolone new-project\n```\n"
  },
  {
    "path": "docs/guide-es/tutorial-template-engines.md",
    "content": "Usar motores de plantillas\n==========================\n\nPor defecto, Yii utiliza PHP como su lenguaje de plantilla, pero puedes configurar Yii para que soporte otros motores de renderizado, tal como\n[Twig](https://twig.symfony.com/) o [Smarty](https://www.smarty.net/), disponibles como extensiones.\n\nEl componente `view` es el responsable de renderizar las vistas. Puedes agregar un motor de plantillas personalizado reconfigurando\nel comportamiento (behavior) de este componente:\n\n```php\n[\n    'components' => [\n        'view' => [\n            'class' => 'yii\\web\\View',\n            'renderers' => [\n                'tpl' => [\n                    'class' => 'yii\\smarty\\ViewRenderer',\n                    //'cachePath' => '@runtime/Smarty/cache',\n                ],\n                'twig' => [\n                    'class' => 'yii\\twig\\ViewRenderer',\n                    'cachePath' => '@runtime/Twig/cache',\n                    // Array de opciones de Twig:\n                    'options' => [\n                        'auto_reload' => true,\n                    ],\n                    'globals' => ['html' => '\\yii\\helpers\\Html'],\n                    'uses' => ['yii\\bootstrap'],\n                ],\n                // ...\n            ],\n        ],\n    ],\n]\n```\n\nEn el código de arriba, tanto Smarty como Twig son configurados para ser utilizables por los archivos de vista. Pero para tener ambas extensiones en tu proyecto, también necesitas modificar\ntu archivo `composer.json` para incluirlos:\n\n```\n\"yiisoft/yii2-smarty\": \"~2.0.0\",\n\"yiisoft/yii2-twig\": \"~2.0.0\",\n```\nEse código será agregado a la sección `require` de `composer.json`. Después de realizar ese cambio y guardar el archivo, puedes instalar estas extensiones ejecutando `composer update --prefer-dist` en la línea de comandos.\n\nPara más detalles acerca del uso concreto de cada motor de plantillas, visita su documentación:\n\n- [Guía de Twig](https://github.com/yiisoft/yii2-twig/tree/master/docs/guide)\n- [Guía de Smarty](https://github.com/yiisoft/yii2-smarty/tree/master/docs/guide)\n"
  },
  {
    "path": "docs/guide-es/tutorial-yii-integration.md",
    "content": "Trabajar con código de terceros\n===============================\n\nDe tiempo en tiempo, puede necesitar usar algún código de terceros en sus aplicaciones Yii. O puedes querer\nutilizar Yii como una librería en otros sistemas de terceros. En esta sección, te enseñaremos cómo conseguir estos objetivos.\n\n\nUtilizar librerías de terceros en Yii <span id=\"using-libs-in-yii\"></span>\n-------------------------------------\n\nPara usar una librería en una aplicación Yii, primeramente debes de asegurarte que las clases en la librería\nson incluidas adecuadamente o pueden ser cargadas de forma automática.\n\n### Usando Paquetes de Composer <span id=\"using-composer-packages\"></span>\n\nMuchas librerías de terceros son liberadas en términos de paquetes [Composer](https://getcomposer.org/).\nPuedes instalar este tipo de librerías siguiendo dos sencillos pasos:\n\n1. modificar el fichero `composer.json` de tu aplicación y especificar que paquetes Composer quieres instalar.\n2. ejecuta `composer install` para instalar los paquetes especificados.\n\nLas clases en los paquetes Composer instalados pueden ser autocargados usando el cargador automatizado de Composer autoloader.\nAsegúrate que el fichero [script de entrada](structure-entry-scripts.md) de tu aplicación contiene las siguientes líneas\npara instalar el cargador automático de Composer:\n\n```php\n// instalar el cargador automático de  Composer\nrequire __DIR__ . '/../vendor/autoload.php';\n\n// incluir rl fichero de la clase Yii\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n```\n\n### Usando librerías Descargadas <span id=\"using-downloaded-libs\"></span>\n\nSi la librería no es liberada como un paquete de Composer, debes de seguir sus instrucciones de instalación para instalarla.\nEn muchos casos, puedes necesitar descargar manualmente el fichero de la versión y desempaquetarlo en el directorio `BasePath/vendor`,\ndonde `BasePath` representa el [camino base (base path)](structure-applications.md#basePath) de tu aplicación.\n\nSi la librería lleva su propio cargador automático (autoloader), puedes instalarlo en [script de entrada](structure-entry-scripts.md) de tu aplicación.\nEs recomendable que la instalación se  termine antes de incluir el fichero `Yii.php` de forma que el cargador automático tenga precedencia al cargar\nde forma automática las clases.\n\nSi la librería no provee un cargador automático de clases, pero la denominación de sus clases sigue el [PSR-4](https://www.php-fig.org/psr/psr-4/),\npuedes usar el cargador automático de Yii para cargar de forma automática las clases. Todo lo que necesitas\nes declarar un [alias raíz](concept-aliases.md#defining-aliases) para cada espacio de nombres (namespace) raiz usado en sus clases. Por ejemplo,\nasume que has instalado una librería en el directorio `vendor/foo/bar`, y que las clases de la librería están bajo el espacio de nombres raiz `xyz`.\nPuedes incluir el siguiente código en la configuración de tu aplicación:\n\n```php\n[\n    'aliases' => [\n        '@xyz' => '@vendor/foo/bar',\n    ],\n]\n```\n\nSi ninguno de lo anterior es el caso, estaría bien que la librería dependa del camino de inclusión (include path) de configuración de PHP\npara localizar correctamente e incluir los ficheros  de las clases. Simplemente siguiendo estas instrucciones de cómo configurar el camino de inclusión de PHP.\n\nEn el caso más grave en el que la librería necesite incluir cada uno de sus ficheros de clases, puedes usar el siguiente método\npara incluir las clases según se pidan:\n\n* Identificar que clases contiene la librería.\n* Listar las clases y el camino a los archivos correspondientes en `Yii::$classMap`  en el script de entrada [script de entrada](structure-entry-scripts.md)\n  de la aplicación. Por ejemplo,\n```php\nYii::$classMap['Class1'] = 'path/to/Class1.php';\nYii::$classMap['Class2'] = 'path/to/Class2.php';\n```\n\n\nUtilizar Yii en Sistemas de Terceros <span id=\"using-yii-in-others\"></span>\n------------------------------------\n\nDebido a que Yii provee muchas posibilidades excelentes, a veces puedes querer usar alguna de sus características para permitir\nel desarrollo o mejora de sistemas de terceros, como es WordPress, Joomla, o aplicaciones desarrolladas usando otros frameworks de PHP.\nPor ejemplo, puedes querer utilizar la clase [[yii\\helpers\\ArrayHelper]] o usar la característica [Active Record](db-active-record.md)\nen un sistema de terceros. Para lograr este objetivo, principalmente necesitas realizar dos pasos:\ninstalar Yii , e iniciar  Yii.\n\nSi el sistema de terceros usa Composer para manejar sus dependencias, simplemente ejecuta estos comandos\npara instalar Yii:\n\n    composer global require \"fxp/composer-asset-plugin:^1.4.1\"\n    composer require yiisoft/yii2\n    composer install\n\nEl primer comando instala el [composer asset plugin](https://github.com/fxpio/composer-asset-plugin),\nque permite administrar paquetes bower y npm a través de Composer. Incluso si sólo quieres utilizar la capa de base de datos\nu otra característica de Yii no relacionada a assets, requiere que instales el paquete composer de Yii.\n\nSi quieres utilizar la [publicación de Assets de Yii](structure-assets.md) deberías agregar también la siguiente configuración\na la sección `extra` de tu `composer.json`:\n\n```json\n{\n    ...\n    \"extra\": {\n        \"asset-installer-paths\": {\n            \"npm-asset-library\": \"vendor/npm\",\n            \"bower-asset-library\": \"vendor/bower\"\n        }\n    }\n}\n```\n\nVisita también la [sección de cómo instalar Yii](start-installation.md#installing-via-composer) para más información\nsobre Composer y sobre cómo solucionar posibles problemas que surjan durante la instalación.\n\nEn otro caso, puedes [descargar](https://www.yiiframework.com/download/) el archivo de la edición de Yii\ny desempaquetarla en el directorio `BasePath/vendor`.\n\nDespués, debes de modificar el script de entrada de sistema de terceros para incluir el siguiente código al principio:\n\n```php\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n\n$yiiConfig = require __DIR__ . '/../config/yii/web.php';\nnew yii\\web\\Application($yiiConfig); // No ejecutes run() aquí\n```\n\nComo puedes ver, el código anterior es muy similar al que puedes ver en [script de entrada](structure-entry-scripts.md)\nde una aplicación típica. La única diferencia es que después de que se crea la instancia de la aplicación, el método `run()` no es llamado.\nEsto es así porque llamando a `run()`, Yii se haría cargo del control del flujo de trabajo del manejo de las peticiones,\nlo cual no es necesario en este caso por estar ya es manejado por la aplicación existente.\n\nComo en una aplicación Yii, debes configurar la instancia de la aplicación basándose en el entorno que se está\nejecutando del sistema de terceros. Por ejemplo, para usar la característica [Active Record](db-active-record.md), necesitas configurar\nel [componente de la aplicación](structure-application-components.md) `db` con los parámetros de la conexión a la BD del sistema de terceros.\n\nAhora puedes usar muchas características provistas por Yii. Por ejemplo, puedes crear clases Active Record y usarlas\npara trabajar con bases de datos.\n\n\nUtilizar Yii 2 con Yii 1 <span id=\"using-both-yii2-yii1\"></span>\n------------------------\n\nSi estaba usando Yii 1 previamente, es como si tuvieras una aplicación Yii 1 funcionando. En vez de reescribir\ntoda la aplicación en Yii 2, puedes solamente mejorarla usando alguna de las características sólo disponibles en Yii 2.\nEsto se puede lograr tal y como se describe abajo.\n\n> Note: Yii 2 requiere PHP 5.4 o superior. Debes de estar seguro que tanto tu servidor como la aplicación\n> existente lo soportan.\n\nPrimero, instala Yii 2 en tu aplicación siguiendo las instrucciones descritas en la [última subsección](#using-yii-in-others).\n\nSegundo, modifica el script de entrada de la aplicación como sigue,\n\n```php\n// incluir la clase Yii personalizada descrita debajo\nrequire __DIR__ . '/../components/Yii.php';\n\n// configuración para la aplicación Yii 2\n$yii2Config = require __DIR__ . '/../config/yii2/web.php';\nnew yii\\web\\Application($yii2Config); // No llamar a run()\n\n// configuración para la aplicación Yii 1\n$yii1Config = require __DIR__ . '/../config/yii1/main.php';\nYii::createWebApplication($yii1Config)->run();\n```\n\nDebido a que ambos Yii 1 y Yii 2 tiene la clase `Yii` , debes crear una versión personalizada para combinarlas.\nEl código anterior incluye el fichero con la clase `Yii` personalizada, que tiene que ser creada como sigue.\n\n```php\n$yii2path = '/path/to/yii2';\nrequire $yii2path . '/BaseYii.php'; // Yii 2.x\n\n$yii1path = '/path/to/yii1';\nrequire $yii1path . '/YiiBase.php'; // Yii 1.x\n\nclass Yii extends \\yii\\BaseYii\n{\n    // copy-paste the code from YiiBase (1.x) here\n}\n\nYii::$classMap = include($yii2path . '/classes.php');\n// registrar el autoloader de Yii 2 vía Yii 1\nYii::registerAutoloader(['Yii', 'autoload']);\n// crear el contenedor de inyección de dependencia\nYii::$container = new yii\\di\\Container;\n```\n\n¡Esto es todo!. Ahora, en cualquier parte de tu código, puedes usar `Yii::$app` para acceder a la instancia de la aplicación de Yii 2,\nmientras `Yii::app()` proporciona la instancia de la aplicación de  Yii 1 :\n\n```php\necho get_class(Yii::app()); // genera 'CWebApplication'\necho get_class(Yii::$app);  // genera 'yii\\web\\Application'\n```\n\n"
  },
  {
    "path": "docs/guide-fr/README.md",
    "content": "Guide définitif pour Yii 2.0\n============================\n\nCe guide est soumis aux [Conditions de la Documentation de Yii](https://www.yiiframework.com/doc/terms/).\n\nTous droits réservés.\n\n2014 (c) Yii Software LLC.\n\n\nIntroduction\n------------\n\n* [A propos de Yii](intro-yii.md)\n* [Mise à jour depuis la version 1.1](intro-upgrade-from-v1.md)\n\n\nMise en Route\n-------------\n\n* [Installer Yii](start-installation.md)\n* [Fonctionnement des applications](start-workflow.md)\n* [Hello World](start-hello.md)\n* [Travailler avec les formulaires](start-forms.md)\n* [Travailler avec les bases de données](start-databases.md)\n* [Générer du code avec Gii](start-gii.md)\n* [En savoir plus](start-looking-ahead.md)\n\n\nStructure Application\n---------------------\n\n* [Vue d'ensemble](structure-overview.md)\n* [Scripts d'entrée](structure-entry-scripts.md)\n* [Applications](structure-applications.md)\n* [Composants application](structure-application-components.md)\n* [Contrôleurs](structure-controllers.md)\n* [Modèles](structure-models.md)\n* [Vues](structure-views.md)\n* **TBD** [Filtres](structure-filters.md)\n* **TBD** [Widgets](structure-widgets.md)\n* **TBD** [Modules](structure-modules.md)\n* [Assets](structure-assets.md)\n* **TBD** [Extensions](structure-extensions.md)\n\n\nGérer les Requêtes\n------------------\n\n* **TBD** [Amorçage (Bootstrapping)](runtime-bootstrapping.md)\n* **TBD** [Routes](runtime-routing.md)\n* **TBD** [Requêtes](runtime-requests.md)\n* **TBD** [Réponses](runtime-responses.md)\n* **TBD** [Sessions et Cookies](runtime-sessions-cookies.md)\n* [Génération et traitement des URL](runtime-url-handling.md)\n* [Gestion des erreurs](runtime-handling-errors.md)\n* [Journalisation](runtime-logging.md)\n\n\nConcepts Clés\n-------------\n\n* [Composants](concept-components.md)\n* [Propriétés](concept-properties.md)\n* [Evénements](concept-events.md)\n* [Comportements](concept-behaviors.md)\n* [Configurations](concept-configurations.md)\n* [Alias](concept-aliases.md)\n* [Auto-chargement de classes](concept-autoloading.md)\n* [Annuaire de services](concept-service-locator.md)\n* [Conteneur d'injection de dépendance](concept-di-container.md)\n\n\nTravailler avec les Bases de Données\n------------------------------------\n\n* [Objet d'accès aux données (DAO)](db-dao.md) - Connexion à une base de données, requêtes basiques, transactions et manipulation de schéma\n* [Constructeur de requête](db-query-builder.md) - Interrogation de base de données en utilisant une couche d'abstraction simple\n* [Active Record](db-active-record.md) - Active Record ORM, récupération et manipulation d'enregistrements et définition des relations\n* [Migrations](db-migrations.md) - Contrôle de version de vos bases de données dans un environnement de développement en équipe\n* **TBD** [Sphinx](db-sphinx.md)\n* **TBD** [Redis](db-redis.md)\n* **TBD** [MongoDB](db-mongodb.md)\n* **TBD** [ElasticSearch](db-elastic-search.md)\n\n\nGetting Data from Users\n-----------------------\n\n* [Créer des formulaires](input-forms.md)\n* [Valider les entrées](input-validation.md)\n* **TBD** [Télécharger des fichiers](input-file-upload.md)\n* **TBD** [Récupération de données provenant de plusieurs modèles](input-multiple-models.md)\n\n\nAfficher les données\n--------------------\n\n* **TBD** [Formattage](output-formatting.md)\n* **TBD** [Pagination](output-pagination.md)\n* **TBD** [Tri](output-sorting.md)\n* [Fournisseurs de données](output-data-providers.md)\n* [Widgets pour afficher des données](output-data-widgets.md)\n* [Thématisation](output-theming.md)\n\n\nSecurité\n--------\n\n* [Authentification](security-authentication.md)\n* [Autorisation](security-authorization.md)\n* [Gestion des mots de passe](security-passwords.md)\n* **TBD** [Clients authentification](security-auth-clients.md)\n* **TBD** [Meilleures pratiques](security-best-practices.md)\n\n\nCache\n-----\n\n* [Vue d'ensemble](caching-overview.md)\n* [Cache de données](caching-data.md)\n* [Cache de fragment](caching-fragment.md)\n* [Cache de page](caching-page.md)\n* [Cache HTTP](caching-http.md)\n\n\nServices Web RESTful\n--------------------\n\n* [Démarrage rapide](rest-quick-start.md)\n* [Ressources](rest-resources.md)\n* [Contrôleurs](rest-controllers.md)\n* [Gestion des routes](rest-routing.md)\n* [Formattage des réponses](rest-response-formatting.md)\n* [Authentification](rest-authentication.md)\n* [Limiter le taux d'utilisation](rest-rate-limiting.md)\n* [Gestion des versions](rest-versioning.md)\n* [Gestion des erreurs](rest-error-handling.md)\n\n\nOutils de développement\n-----------------------\n\n* [Barre de débogage, et débogueur](tool-debugger.md)\n* [Générer du code avec Gii](tool-gii.md)\n* **TBD** [Générer une documentation API](tool-api-doc.md)\n\n\nTests\n-----\n\n* [Vue d'ensemble](test-overview.md)\n* **TBD** [Tests unitaires](test-unit.md)\n* **TBD** [tests fonctionnels](test-functional.md)\n* **TBD** [Tests d'acceptation](test-acceptance.md)\n* [Fixtures](test-fixtures.md)\n\n\nEtendre Yii\n-----------\n\n* [Créer des extensions](extend-creating-extensions.md)\n* [Personnalisation du code du noyau](extend-customizing-core.md)\n* [Utiliser des libraires tierces](extend-using-libs.md)\n* **TBD** [Utiliser Yii dans d'autres systèmes](extend-embedding-in-others.md)\n* **TBD** [Utiliser Yii 1.1 et 2.0 ensemble](extend-using-v1-v2.md)\n* [Utiliser Composer](extend-using-composer.md)\n\n\nSujets avancés\n--------------\n\n* [Modèle application avancée](tutorial-advanced-app.md)\n* [Créer une application à partir de zéro](tutorial-start-from-scratch.md)\n* [Commandes console](tutorial-console.md)\n* [Validateurs de base](tutorial-core-validators.md)\n* [Internationalisation](tutorial-i18n.md)\n* [Envoyer des courriels](tutorial-mailing.md)\n* [Amélioration des performances](tutorial-performance-tuning.md)\n* **TBD** [Environnement d'hébergement mutualisé](tutorial-shared-hosting.md)\n* [Moteur de gabarit](tutorial-template-engines.md)\n\n\nWidgets\n-------\n\n* GridView: link to demo page\n* ListView: link to demo page\n* DetailView: link to demo page\n* ActiveForm: link to demo page\n* Pjax: link to demo page\n* Menu: link to demo page\n* LinkPager: link to demo page\n* LinkSorter: link to demo page\n* [Widgets Bootstrap](bootstrap-widgets.md)\n* **TBD** [Widgets Jquery UI](jui-widgets.md)\n\n\nAssistants\n----------\n\n* [Vue d'ensemble](helper-overview.md)\n* **TBD** [ArrayHelper](helper-array.md)\n* **TBD** [Html](helper-html.md)\n* **TBD** [Url](helper-url.md)\n* **TBD** [Security](helper-security.md)\n\n"
  },
  {
    "path": "docs/guide-fr/blocktypes.json",
    "content": "{\n  \"Warning:\": \"Attention :\",\n  \"Note:\": \"Note :\",\n  \"Info:\": \"Info :\",\n  \"Tip:\": \"Conseil :\"\n}\n"
  },
  {
    "path": "docs/guide-fr/caching-data.md",
    "content": "Mise en cache de données\n========================\n\nLa mise en cache de données consiste à stocker quelques variables PHP dans un cache et à les y retrouver plus tard. \nC'est également la base pour des fonctionnalités de mise en cache plus avancées, comme la [mise en cache de requêtes](#query-caching) et la [mise en cache de pages](caching-page.md).\n\nLe code qui suit est un modèle d'utilisation typique de la mise en cache de données, dans lequel `cache` fait référence à un [composant de mise en cache](#cache-components) :\n\n\n```php\n// tente de retrouver la donnée $data dans le cache\n$data = $cache->get($key);\n\nif ($data === false) {\n    // la donnée $data n'a pas été trouvée dans le cache, on la recalcule\n    $data = $this->calculateSomething();\n\n    // stocke la donnée $data dans le cache de façon à la retrouver la prochaine fois\n    $cache->set($key, $data);\n}\n\n// la donnée $data est disponible ici\n```\n\nDepuis la version 2.0.11, le [composant de mise en cache](#cache-components) fournit la méthode [[yii\\caching\\Cache::getOrSet()|getOrSet()]] qui simplifie le code pour l'obtention, le calcul et le stockage des données. Le code qui suit fait exactement la même chose que l'exemple précédent :\n\n```php\n$data = $cache->getOrSet($key, function () {\n    return $this->calculateSomething();\n});\n```\n\nLorsque le cache possède une donnée associée à la clé `$key`, la valeur en cache est retournée. Autrement, la fonction anonyme passée est exécutée pour calculer cette valeur qui est mise en cache et retournée.\n\nSi la fonction anonyme a besoin de quelques données en dehors de la portée courante, vous pouvez les passer en utilisant l'instruction `use`. Par exemple :\n\n```php\n$user_id = 42;\n$data = $cache->getOrSet($key, function () use ($user_id) {\n    return $this->calculateSomething($user_id);\n});\n```\n\n> Note : la méthode [[yii\\caching\\Cache::getOrSet()|getOrSet()]] prend également en charge la durée et les dépendances.\n  Reportez-vous à [Expiration de la mise en cache](#cache-expiration) et à [Dépendances de mise en cache](#cache-dependencies) pour en savoir plus.\n  \n\n## Composants de mise en cache <span id=\"cache-components\"></span>\n\nLa mise en cache s'appuie sur ce qu'on appelle les *composants de mise en cache* qui représentent des supports de mise en cache tels que les mémoires, les fichiers et les bases de données.\n\nLes composants de mise en cache sont généralement enregistrés en tant que [composants d'application](structure-application-components.md) de façon à ce qu'ils puissent être configurables et accessibles globalement. Le code qui suit montre comment configurer le composant d'application `cache` pour qu'il utilise [memcached](https://memcached.org/) avec deux serveurs de cache :\n\n```php\n'components' => [\n    'cache' => [\n        'class' => 'yii\\caching\\MemCache',\n        'servers' => [\n            [\n                'host' => 'server1',\n                'port' => 11211,\n                'weight' => 100,\n            ],\n            [\n                'host' => 'server2',\n                'port' => 11211,\n                'weight' => 50,\n            ],\n        ],\n    ],\n],\n```\n\nVous pouvez accéder au composant de mise en cache configuré ci-dessus en utilisant l'expression `Yii::$app->cache`.\n\nComme tous les composants de mise en cache prennent en charge le même jeux d'API, vous pouvez remplacer le composant de mise en cache sous-jacent par un autre en le reconfigurant  dans la configuration de l'application, cela sans modifier le code qui utilise le cache. Par exemple, vous pouvez modifier le code ci-dessus pour utiliser [[yii\\caching\\ApcCache|APC cache]] :\n\n\n```php\n'components' => [\n    'cache' => [\n        'class' => 'yii\\caching\\ApcCache',\n    ],\n],\n```\n\n> Tip: vous pouvez enregistrer de multiples composants d'application de mise en cache. Le composant nommé `cache` est utilisé par défaut par de nombreuses classes dépendantes d'un cache (p. ex.[[yii\\web\\UrlManager]]). \n\n\n### Supports de stockage pour cache pris en charge <span id=\"supported-cache-storage\"></span>\n\nYii prend en charge un large panel de supports de stockage pour cache. Ce qui suit est un résumé :\n\n* [[yii\\caching\\ApcCache]]: utilise l'extension PHP [APC](https://www.php.net/manual/fr/book.apcu.php). Cette option peut être considérée comme la plus rapide lorsqu'on utilise un cache pour une grosse application centralisée (p. ex. un serveur, pas d'équilibrage de charge dédié, etc.).\n* [[yii\\caching\\DbCache]]: utilise une table de base de données pour stocker les données en cache. Pour utiliser ce cache, vous devez créer une table comme spécifié dans [[yii\\caching\\DbCache::cacheTable]].\n* [[yii\\caching\\DummyCache]]: tient lieu de cache à remplacer qui n'assure pas de mise en cache réelle. Le but de ce composant est de simplifier le code qui a besoin de vérifier la disponibilité du cache. Par exemple, lors du développement ou si le serveur ne dispose pas de la prise en charge d'un cache, vous pouvez configurer un composant de mise en cache pour qu'il utilise ce cache. Lorsque la prise en charge réelle de la mise en cache est activée, vous pouvez basculer sur le composant de mise en cache correspondant. Dans les deux cas, vous pouvez utiliser le même code `Yii::$app->cache->get($key)` pour essayer de retrouver les données du cache sans vous préoccuper du fait que `Yii::$app->cache` puisse être `null`.\n* [[yii\\caching\\FileCache]]: utilise des fichiers standards pour stocker les données en cache. Cela est particulièrement adapté à la mise en cache de gros blocs de données, comme le contenu d'une page.\n* [[yii\\caching\\MemCache]]: utilise le [memcache](https://www.php.net/manual/fr/book.memcache.php) PHP et l'extension [memcached](https://www.php.net/manual/fr/book.memcached.php). Cette option peut être considérée comme la plus rapide lorsqu'on utilise un cache dans des applications distribuées (p. ex. avec plusieurs serveurs, l'équilibrage de charge, etc.).\n* [[yii\\redis\\Cache]]: met en œuvre un composant de mise en cache basé sur un stockage clé-valeur [Redis](https://redis.io/) \n  (une version de redis égale ou supérieure à 2.6.12 est nécessaire).\n* [[yii\\caching\\WinCache]]: utilise le [WinCache](https://iis.net/downloads/microsoft/wincache-extension) PHP\n  ([voir aussi l'extension](https://www.php.net/manual/fr/book.wincache.php)).\n\n\n> Tip: vous pouvez utiliser différents supports de stockage pour cache dans la même application. Une stratégie courante est d'utiliser un support de stockage pour cache basé sur la mémoire pour stocker des données de petite taille mais d'usage constant (p. ex. des données statistiques), et d'utiliser des supports de stockage pour cache basés sur des fichiers ou des bases de données pour stocker des données volumineuses et utilisées moins souvent (p. ex. des contenus de pages).\n\n\n## Les API Cache <span id=\"cache-apis\"></span>\n\nTous les composants de mise en cache dérivent de la même classe de base [[yii\\caching\\Cache]] et par conséquent prennent en charge les API suivantes :\n\n* [[yii\\caching\\Cache::get()|get()]]: retrouve une donnée dans le cache identifiée par une clé spécifiée. Une valeur `false` (faux) est retournée si la donnée n'est pas trouvée dans le cache ou si elle a expiré ou été invalidée.\n* [[yii\\caching\\Cache::set()|set()]]: stocke une donnée sous une clé dans le cache.\n* [[yii\\caching\\Cache::add()|add()]]: stocke une donnée identifiée par une clé dans le cache si la clé n'existe pas déjà dans le cache.\n* [[yii\\caching\\Cache::getOrSet()|getOrSet()]]: retrouve une donnée dans le cache identifiée par une clé spécifiée ou exécute la fonction de rappel passée, stocke la valeur retournée par cette fonction dans le cache sous cette clé et retourne la donnée. \n* [[yii\\caching\\Cache::multiGet()|multiGet()]]: retrouve de multiples données dans le cache identifiées par les clés spécifiées.\n* [[yii\\caching\\Cache::multiSet()|multiSet()]]: stocke de multiples données dans le cache. Chaque donnée est identifiée par une clé.\n* [[yii\\caching\\Cache::multiAdd()|multiAdd()]]: stocke de multiples données dans le cache. Chaque donnée est identifiée par une clé. Si la clé existe déjà dans le cache, la donnée est ignorée.\n* [[yii\\caching\\Cache::exists()|exists()]]: retourne une valeur indiquant si la clé spécifiée existe dans le cache.\n* [[yii\\caching\\Cache::delete()|delete()]]: retire du cache une donnée identifiée par une clé.\n* [[yii\\caching\\Cache::flush()|flush()]]: retire toutes les données du cache.\n\n> Note : ne mettez pas directement en cache une valeur booléenne `false` parce que la méthode [[yii\\caching\\Cache::get()|get()]] utilise  la valeur `false` pour indiquer que la donnée n'a pas été trouvée dans le cache. Au lieu de cela, vous pouvez placer cette donnée dans un tableau et mettre ce tableau en cache pour éviter le problème.\n\nQuelques supports de cache, tels que MemCache, APC, prennent en charge la récupération de multiples valeurs en cache en mode « batch » (lot), ce qui réduit la surcharge occasionnée par la récupération des données en cache. Les API [[yii\\caching\\Cache::multiGet()|multiGet()]] et [[yii\\caching\\Cache::multiAdd()|multiAdd()]] sont fournies pour exploiter cette fonctionnalité. Dans le cas où le support de cache sous-jacent ne prend pas en charge cette fonctionnalité, elle est simulée.\nComme [[yii\\caching\\Cache]] implémente `ArrayAccess`, un composant de mise en cache peut être utilisé comme un tableau. En voici quelques exemples :\n\n```php\n$cache['var1'] = $value1;  // équivalent à : $cache->set('var1', $value1);\n$value2 = $cache['var2'];  // équivalent à : $value2 = $cache->get('var2');\n```\n\n\n### Clés de cache <span id=\"cache-keys\"></span>\n\nChacune des données stockée dans le cache est identifiée de manière unique par une clé. Lorsque vous stockez une donnée dans le cache, vous devez spécifier une clé qui lui est attribuée. Plus tard, pour récupérer la donnée, vous devez fournir cette clé.\n\nVous pouvez utiliser une chaîne de caractères ou une valeur arbitraire en tant que clé de cache. Lorsqu'il ne s'agit pas d'une chaîne de caractères, elle est automatiquement sérialisée sous forme de chaîne de caractères.\n\nUne stratégie courante pour définir une clé de cache consiste à inclure tous les facteurs déterminants sous forme de tableau. Par exemple,[[yii\\db\\Schema]] utilise la clé suivante par mettre en cache les informations de schéma d'une table de base de données :\n\n```php\n[\n    __CLASS__,              // schema class name\n    $this->db->dsn,         // DB connection data source name\n    $this->db->username,    // DB connection login user\n    $name,                  // table name\n];\n```\n\nComme vous le constatez, la clé inclut toutes les informations nécessaires pour spécifier de manière unique une table de base de données. \n\n> Note : les valeurs stockées dans le cache via [[yii\\caching\\Cache::multiSet()|multiSet()]] ou [[yii\\caching\\Cache::multiAdd()|multiAdd()]] peuvent n'avoir que des clés sous forme de chaînes de caractères ou de nombres entiers. Si vous avez besoin de définir des clés plus complexes, stockez la valeur séparément via [[yii\\caching\\Cache::set()|set()]] ou [[yii\\caching\\Cache::add()|add()]].\n\nLorsque le même support de cache est utilisé par différentes applications, vous devriez spécifier un préfixe de clé de cache pour chacune des applications afin d'éviter les conflits de clés de cache. Cela peut être fait en configurant la propriété [[yii\\caching\\Cache::keyPrefix]]. Par exemple, dans la configuration de l'application vous pouvez entrer le code suivant :\n\n```php\n'components' => [\n    'cache' => [\n        'class' => 'yii\\caching\\ApcCache',\n        'keyPrefix' => 'myapp',       // a unique cache key prefix\n    ],\n],\n```\n\nPour garantir l'interopérabilité, vous ne devez utiliser que des caractères alpha-numériques.\n\n\n### Expiration de la mise en cache <span id=\"cache-expiration\"></span>\n\nUne donnée stockée dans le cache y restera à jamais sauf si elle en est retirée par l'application d'une quelconque politique de mise en cache (p. ex. l'espace de mise en cache est plein et les données les plus anciennes sont retirées). Pour modifier ce comportement, vous pouvez fournir un paramètre d'expiration lors de l'appel de la fonction [[yii\\caching\\Cache::set()|set()]] pour stocker une donnée. Le paramètre indique pour combien de secondes la donnée restera valide dans le cache. Lorsque vous appelez la fonction [[yii\\caching\\Cache::get()|get()]] pour récupérer une donnée, si cette dernière a expiré, la méthode retourne `false`, pour indiquer que la donnée n'a pas été trouvée dans le cache. Par exemple, \n\n```php\n// conserve la donnée dans le cache pour un maximum de 45 secondes\n$cache->set($key, $data, 45);\n\nsleep(50);\n\n$data = $cache->get($key);\nif ($data === false) {\n    // $data a expiré ou n'a pas été trouvée dans le cache\n}\n```\n\nDepuis la version 2.0.11, vous pouvez définir une valeur [[yii\\caching\\Cache::$defaultDuration|defaultDuration]] dans la configuration de votre composant de mise en cache si vous préférez utiliser une durée de mise en cache personnalisée au lieu de la durée illimitée par défaut. Cela vous évitera d'avoir à passer la durée personnalisée à la fonction [[yii\\caching\\Cache::set()|set()]] à chaque fois.\n\n\n### Dépendances de mise en cache <span id=\"cache-dependencies\"></span>\n\nEn plus de la définition du temps d'expiration, les données mises en cache peuvent également être invalidées par modification de ce qu'on appelle les *dépendances de mise en cache*. \nPar exemple, [[yii\\caching\\FileDependency]] représente la dépendance à la date de modification d'un fichier. \nLorsque cette dépendance est modifiée, cela signifie que le fichier correspondant est modifié. En conséquence, tout contenu de fichier périmé trouvé dans le cache devrait être invalidé et l'appel de la fonction [[yii\\caching\\Cache::get()|get()]] devrait retourner `false`.\n\n\nLes dépendances de mise en cache sont représentées sous forme d'objets dérivés de [[yii\\caching\\Dependency]]. Lorsque vous appelez la fonction [[yii\\caching\\Cache::set()|set()]] pour stocker une donnée dans le cache, vous pouvez lui passer un objet de dépendance (« Dependency ») associé. Par exemple,\n\n```php\n// Crée une dépendance à la date de modification du fichier example.txt\n$dependency = new \\yii\\caching\\FileDependency(['fileName' => 'example.txt']);\n\n// La donnée expirera dans 30 secondes.\n// Elle sera également invalidée plus tôt si le fichier example.txt est modifié.\n$cache->set($key, $data, 30, $dependency);\n\n// Le cache vérifiera si la donnée a expiré.\n// Il vérifiera également si la dépendance associée a été modifiée. \n// Il retournera `false` si l'une de ces conditions est vérifiée.\n$data = $cache->get($key);\n```\n\nCi-dessous nous présentons un résumé des dépendances de mise en cache disponibles :\n\n- [[yii\\caching\\ChainedDependency]]: la dépendance est modifiée si l'une des dépendances de la chaîne est modifiée.\n- [[yii\\caching\\DbDependency]]: la dépendance est modifiée si le résultat de le requête de l'instruction SQL spécifiée est modifié.\n- [[yii\\caching\\ExpressionDependency]]: la dépendance est modifiée si le résultat de l'expression PHP spécifiée est modifié.\n- [[yii\\caching\\CallbackDependency]]: la dépendance est modifiée si le résultat du rappel PHP spécifié est modifié.\n- [[yii\\caching\\FileDependency]]: la dépendance est modifiée si la date de dernière modification du fichier est modifiée.\n- [[yii\\caching\\TagDependency]]: associe une donnée mise en cache à une ou plusieurs balises. Vous pouvez invalider la donnée mise en cache associée à la balise spécifiée en appelant [[yii\\caching\\TagDependency::invalidate()]].\n\n> Note : évitez d'utiliser la méthode [[yii\\caching\\Cache::exists()|exists()]] avec des dépendances. Cela ne vérifie pas si la dépendance associée à la donnée mise en cache, s'il en existe une, a changé. Ainsi, un appel de la fonction [[yii\\caching\\Cache::get()|get()]] peut retourner `false` alors que l'appel de la fonction [[yii\\caching\\Cache::exists()|exists()]] retourne `true`.\n\n\n## Mise en cache de requêtes <span id=\"query-caching\"></span>\n\nLa mise en cache de requêtes est une fonctionnalité spéciale de la mise en cache construite sur la base de la mise en cache de données. Elle est fournie pour permettre la mise en cache du résultat de requêtes de base de données.\n\nLa mise en cache de requêtes nécessite une [[yii\\db\\Connection|connexion à une base de données]] et un  [composant d'application](#cache-components)`cache` valide.\nL'utilisation de base de la mise en cache de requêtes est la suivante, en supposant que `$db` est une instance de [[yii\\db\\Connection]] :\n\n```php\n$result = $db->cache(function ($db) {\n\n    // le résultat d'une requête SQL sera servi à partir du cache\n    // si la mise en cache de requêtes est activée et si le résultat de la requête est trouvé dans le cache\n    return $db->createCommand('SELECT * FROM customer WHERE id=1')->queryOne();\n\n});\n```\n\nLa mise en cache de requêtes peut être utilisée pour des [DAO](db-dao.md) ainsi que pour des [enregistrements actifs](db-active-record.md):\n\n```php\n$result = Customer::getDb()->cache(function ($db) {\n    return Customer::find()->where(['id' => 1])->one();\n});\n```\n\n> Info : quelques systèmes de gestion de bases de données (DBMS) (p. ex. [MySQL](https://dev.mysql.com/doc/refman/5.6/en/query-cache.html))\n  prennent également en charge la mise en cache de requêtes du côté serveur de base de données. Vous pouvez choisir d'utiliser l'un ou l'autre des ces mécanismes de mise en cache de requêtes. Les mises en cache de requêtes décrites ci-dessus offrent l'avantage de pouvoir spécifier des dépendances de mise en cache flexibles et sont potentiellement plus efficaces.\n\n\n### Vidage du cache <span id=\"cache-flushing\"></span>\n\nLorsque vous avez besoin d'invalider toutes les données stockées dans le cache, vous pouvez appeler [[yii\\caching\\Cache::flush()]].\n\nVous pouvez aussi vider le cache depuis la console en appelant `yii cache/flush`.\n - `yii cache`: liste les caches disponibles dans une application\n - `yii cache/flush cache1 cache2`: vide les composants de mise en cache `cache1`, `cache2` (vous pouvez passer de multiples composants en les séparant par une virgule)\n - `yii cache/flush-all`: vide tous les composants de mise en cache de l'application\n\n> Info : les applications en console utilisent un fichier de configuration séparé par défaut. Assurez-vous que vous utilisez le même composant de mise en cache dans les configurations de vos application web et console pour obtenir l'effet correct. \n\n\n### Configurations <span id=\"query-caching-configs\"></span>\n\nLa mise en cache de requêtes dispose de trois options globales configurables via [[yii\\db\\Connection]] :\n\n* [[yii\\db\\Connection::enableQueryCache|enableQueryCache]] : pour activer ou désactiver la mise en cache de requêtes.\n  Valeur par défaut : `true`. Notez que pour activer effectivement la mise en cache de requêtes, vous devez également disposer d'un cache valide, tel que spécifié par [[yii\\db\\Connection::queryCache|queryCache]].\n* [[yii\\db\\Connection::queryCacheDuration|queryCacheDuration]] : ceci représente le nombre de secondes durant lesquelles le résultat d'une requête reste valide dans le cache. Vous pouvez utiliser 0 pour indiquer que le résultat de la requête doit rester valide indéfiniment dans le cache. Cette propriété est la valeur par défaut utilisée lors de l'appel [[yii\\db\\Connection::cache()]] sans spécifier de durée.\n* [[yii\\db\\Connection::queryCache|queryCache]] : ceci représente l'identifiant du composant d'application de mise en cache.\n  Sa valeur par défaut est : `'cache'`. La mise en cache de requêtes est activée seulement s'il existe un composant d'application de mise en cache valide.\n\n\n### Utilisations <span id=\"query-caching-usages\"></span>\n\nVous pouvez utiliser [[yii\\db\\Connection::cache()]] si vous avez de multiples requêtes SQL qui doivent bénéficier de la mise en cache de requêtes. On l'utilise comme suit :\n\n```php\n$duration = 60;     // mettre le résultat de la requête en cache durant 60 secondes.\n$dependency = ...;  // dépendance optionnelle\n\n$result = $db->cache(function ($db) {\n\n    // ... effectuer les requêtes SQL ici ...\n\n    return $result;\n\n}, $duration, $dependency);\n```\n\nToutes les requêtes SQL dans la fonction anonyme sont mises en cache pour la durée spécifiée avec la dépendance spécifiée. Si le résultat d'une requête est trouvé valide dans le cache, la requête est ignorée et, à la place, le résultat est servi à partir du cache. Si vous ne spécifiez pas le paramètre `$duration`, la valeur de [[yii\\db\\Connection::queryCacheDuration|queryCacheDuration]] est utilisée en remplacement.\n\nParfois, dans `cache()`, il se peut que vous vouliez désactiver la mise en cache de requêtes pour des requêtes particulières. Dans un tel cas, vous pouvez utiliser [[yii\\db\\Connection::noCache()]].\n\n```php\n$result = $db->cache(function ($db) {\n\n    // requêtes SQL qui utilisent la mise en cache de requêtes\n\n    $db->noCache(function ($db) {\n\n        // requêtes qui n'utilisent pas la mise en cache de requêtes\n\n    });\n\n    // ...\n\n    return $result;\n});\n```\n\nSi vous voulez seulement utiliser la mise en cache de requêtes pour une requête unique, vous pouvez appeler la fonction [[yii\\db\\Command::cache()]] lors de la construction de la commande. Par exemple :\n\n```php\n// utilise la mise en cache de requêtes et définit la durée de mise en cache de la requête à 60 secondes\n$customer = $db->createCommand('SELECT * FROM customer WHERE id=1')->cache(60)->queryOne();\n```\n\nVous pouvez aussi utiliser la fonction [[yii\\db\\Command::noCache()]] pour désactiver la mise en cache de requêtes pour une commande unique. Par exemple :\n\n```php\n$result = $db->cache(function ($db) {\n\n    // requêtes SQL qui utilisent la mise en cache de requêtes\n\n    // ne pas utiliser la mise en cache de requêtes pour cette commande\n    $customer = $db->createCommand('SELECT * FROM customer WHERE id=1')->noCache()->queryOne();\n\n    // ...\n\n    return $result;\n});\n```\n\n\n### Limitations <span id=\"query-caching-limitations\"></span>\n\nLa mise en cache de requêtes ne fonctionne pas avec des résultats de requêtes qui contiennent des gestionnaires de ressources. Par exemple, lorsque vous utilisez de type de colonne `BLOB` dans certains systèmes de gestion de bases de données (DBMS), la requête retourne un gestionnaire de ressources pour la donnée de la colonne.\n\nQuelques supports de stockage pour cache sont limités en taille. Par exemple, avec memcache, chaque entrée est limitée en taille à 1 MO. En conséquence, si le résultat d'une requête dépasse cette taille, la mise en cache échoue.\n"
  },
  {
    "path": "docs/guide-fr/caching-fragment.md",
    "content": "Mise en cache de fragments\n==========================\n\nLa mise en cache de fragments fait référence à la mise en cache de fragments de pages Web. Par exemple, si une page affiche un résumé des ventes annuelles dans un tableau, vous pouvez stocker ce tableau en cache pour éliminer le temps nécessaire à sa génération à chacune des requêtes. La mise en cache de fragments est construite au-dessus de la [mise en cache de données](caching-data.md).\n\nPour utiliser la mise en cache de fragments, utilisez la construction qui suit dans une [vue](structure-views.md):\n\n```php\nif ($this->beginCache($id)) {\n\n    // ... générez le contenu ici ...\n\n    $this->endCache();\n}\n```\n\nC'est à dire, insérez la logique de génération du contenu entre les appels [[yii\\base\\View::beginCache()|beginCache()]] et\n[[yii\\base\\View::endCache()|endCache()]]. Si le contenu est trouvé dans le cache, [[yii\\base\\View::beginCache()|beginCache()]]\nrendra le contenu en cache et retournera `false` (faux), ignorant la logique de génération de contenu.\nAutrement, votre logique de génération de contenu sera appelée, et quand [[yii\\base\\View::endCache()|endCache()]] sera appelée, le contenu généré sera capturé et stocké dans le cache.\n\nComme pour la [mise en cache de données](caching-data.md), un `$id` (identifiant) unique est nécessaire pour identifier un cache de contenu.\n\n\n## Options de mise en cache <span id=\"caching-options\"></span>\n\nVous pouvez spécifier des options additionnelles sur la mise en cache de fragments en passant le tableau d'options comme second paramètre à la méthode [[yii\\base\\View::beginCache()|beginCache()]]. En arrière plan, ce tableau d'options est utilisé pour configurer un composant graphique [[yii\\widgets\\FragmentCache]] qui met en œuvre la fonctionnalité réelle de mise en cache de fragments.\n\n### Durée <span id=\"duration\"></span>\n\nL'option [[yii\\widgets\\FragmentCache::duration|duration]] (durée) est peut-être l'option de la mise en cache de fragments la plus couramment utilisée. Elle spécifie pour combien de secondes le contenu peut demeurer valide dans le cache. Le code qui suit met le fragment de contenu en cache pour au maximum une heure :\n\n```php\nif ($this->beginCache($id, ['duration' => 3600])) {\n\n    // ... générez le contenu ici...\n\n    $this->endCache();\n}\n```\n\nSi cette option n'est pas définie, la valeur utilisée par défaut est 60, ce qui veut dire que le contenu mise en cache expirera au bout de 60 secondes.\n\n\n### Dépendances <span id=\"dependencies\"></span>\n\nComme pour la [mise en cache de données](caching-data.md#cache-dependencies), le fragment de contenu mis en cache peut aussi avoir des dépendances. Par exemple, le contenu d'un article affiché dépend du fait que l'article a été modifié ou pas.\n\nPour spécifier une dépendance, définissez l'option [[yii\\widgets\\FragmentCache::dependency|dependency]], soit sous forme d'objet [[yii\\caching\\Dependency]], soit sous forme d'un tableau de configuration pour créer un objet [[yii\\caching\\Dependency]]. Le code qui suit spécifie que le fragment de contenu dépend du changement de la valeur de la colonne `updated_at` (mis à jour le) : \n\n```php\n$dependency = [\n    'class' => 'yii\\caching\\DbDependency',\n    'sql' => 'SELECT MAX(updated_at) FROM post',\n];\n\nif ($this->beginCache($id, ['dependency' => $dependency])) {\n\n    // ... générez le contenu ici ...\n\n    $this->endCache();\n}\n```\n\n\n### Variations <span id=\"variations\"></span>\n\nLe contenu mise en cache peut connaître quelques variations selon certains paramètres. Par exemple, pour une application Web prenant en charge plusieurs langues, le même morceau de code d'une vue peut générer le contenu dans différentes langues. Par conséquent, vous pouvez souhaitez que le contenu mis en cache varie selon la langue courante de l'application.\n\nPour spécifier des variations de mise en cache, définissez l'option [[yii\\widgets\\FragmentCache::variations|variations]], qui doit être un tableau de valeurs scalaires, représentant chacune un facteur de variation particulier. Par exemple, pour que le contenu mis en cache varie selon la langue, vous pouvez utiliser le code suivant :\n\n```php\nif ($this->beginCache($id, ['variations' => [Yii::$app->language]])) {\n\n    // ... générez le contenu ici ...\n\n    $this->endCache();\n}\n```\n\n\n### Activation désactivation de la mise en cache <span id=\"toggling-caching\"></span>\n\nParfois, vous désirez activer la mise en cache de fragments seulement lorsque certaines conditions sont rencontrées. Par exemple, pour une page qui affiche un formulaire, vous désirez seulement mettre le formulaire en cache lorsqu'il est initialement demandé (via une requête GET). Tout affichage subséquent du formulaire (via des requêtes POST) ne devrait pas être mise en cache car il contient des données entrées par l'utilisateur. Pour mettre en œuvre ce mécanisme, vous pouvez définir l'option [[yii\\widgets\\FragmentCache::enabled|enabled]], comme suit :\n\n```php\nif ($this->beginCache($id, ['enabled' => Yii::$app->request->isGet])) {\n\n    // ... générez le contenu ici ...\n\n    $this->endCache();\n}\n```\n\n\n## Mises en cache imbriquées <span id=\"nested-caching\"></span>\n\nLa mise en cache de fragments peut être imbriquée. C'est à dire qu'un fragment mis en cache peut être contenu dans un autre fragment lui aussi mis en cache.\nPar exemple, les commentaires sont mis en cache dans un cache de fragment interne, et sont mis en cache en même temps et avec le contenu de l'article dans un cache de fragment externe. Le code qui suit montre comment deux caches de fragment peuvent être imbriqués :\n\n```php\nif ($this->beginCache($id1)) {\n\n    // ...logique de génération du contenu ...\n\n    if ($this->beginCache($id2, $options2)) {\n\n        // ...logique de génération du contenu...\n\n        $this->endCache();\n    }\n\n    // ... logique de génération de contenu ...\n\n    $this->endCache();\n}\n```\n\nDifférentes options de mise en cache peuvent être définies pour les caches imbriqués. Par exemple, les caches internes et les caches externes peuvent utiliser des valeurs de durée différentes. Même lorsque les données mises en cache dans le cache externe sont invalidées, le cache interne peut continuer à fournir un fragment interne valide. Néanmoins, le réciproque n'est pas vraie ; si le cache externe est évalué comme valide, il continue à fournir la même copie mise en cache après que le contenu du cache interne a été invalidé. Par conséquent, vous devez être prudent lors de la définition des durées ou des dépendances des caches imbriqués, autrement des fragments internes périmés peuvent subsister dans le fragment externe.\n\n\n## Contenu dynamique <span id=\"dynamic-content\"></span>\n\nLors de l'utilisation de la mise en cache de fragments, vous pouvez rencontrer une situation dans laquelle un gros fragment de contenu est relativement statique en dehors de quelques endroits particuliers. Par exemple, l'entête d'une page peut afficher le menu principal avec le nom de l'utilisateur courant. Un autre problème se rencontre lorsque le contenu mis en cache, contient du code PHP qui doit être exécuté à chacune des requêtes (p. ex. le code pour enregistrer un paquet de ressources). Ces deux problèmes peuvent être résolus par une fonctionnalité qu'on appelle *contenu dynamique*.\n\nUn contenu dynamique signifie un fragment de sortie qui ne doit jamais être mis en cache même s'il est contenu dans un  fragment mis en cache. Pour faire en sorte que le contenu soit dynamique en permanence, il doit être généré en exécutant un code PHP à chaque requête, même si le contenu l'englobant est servi à partir du cache.\n\nVous pouvez appeler la fonction [[yii\\base\\View::renderDynamic()]] dans un fragment mis en cache pour y insérer un contenu dynamique à l'endroit désiré, comme ceci :\n\n```php\nif ($this->beginCache($id1)) {\n\n    // ... logique de génération de contenu ...\n\n    echo $this->renderDynamic('return Yii::$app->user->identity->name;');\n\n    // ... logique de génération de contenu ...\n\n    $this->endCache();\n}\n```\n\nLa méthode [[yii\\base\\View::renderDynamic()|renderDynamic()]] accepte un morceau de code PHP en paramètre. La valeur retournée est traitée comme un contenu dynamique. Le même code PHP est exécuté à chacune des requêtes, peu importe que le fragment englobant soit servi à partir du cache ou pas. \n"
  },
  {
    "path": "docs/guide-fr/caching-http.md",
    "content": "Mise en cache HTTP\n============\n\nEn plus de la mise en cache côté serveur que nous avons décrite dans les sections précédentes, les applications Web peuvent aussi exploiter la mise en cache côté client pour économiser le temps de génération et de transfert d'un contenu de page inchangé.\n\nPour utiliser la mise en cache côté client, vous pouvez configurer [[yii\\filters\\HttpCache]] comme un filtre pour des actions de contrôleur dont le résultat rendu peut être mis en cache du côté du client. [[yii\\filters\\HttpCache|HttpCache]]\nne fonctionne que pour les requêtes `GET` et `HEAD`. Il peut gérer trois sortes d'entêtes HTTP relatifs à la mise en cache pour ces requêtes :\n\n* [[yii\\filters\\HttpCache::lastModified|Last-Modified]]\n* [[yii\\filters\\HttpCache::etagSeed|Etag]]\n* [[yii\\filters\\HttpCache::cacheControlHeader|Cache-Control]]\n\n\n## Entête `Last-Modified` <span id=\"last-modified\"></span>\n\nL'entête `Last-Modified` (dernière modification) utilise un horodatage pour indiquer si la page a été modifiée depuis sa mise en cache par le client.\n\nVous pouvez configurer la propriété [[yii\\filters\\HttpCache::lastModified]] pour activer l'envoi de l'entête `Last-modified`. La propriété doit être une fonction de rappel PHP qui retourne un horodatage UNIX concernant la modification de la page. La signature de la fonction de rappel PHP doit être comme suit :\n\n```php\n/**\n * @param Action $action l'objet action qui est actuellement géré\n * @param array $params la valeur de la propriété \"params\"\n * @return int un horodatage UNIX représentant l'instant de modification de la page\n */\nfunction ($action, $params)\n```\n\nCe qui suit est un exemple d'utilisation de l'entête `Last-Modified` :\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\HttpCache',\n            'only' => ['index'],\n            'lastModified' => function ($action, $params) {\n                $q = new \\yii\\db\\Query();\n                return $q->from('post')->max('updated_at');\n            },\n        ],\n    ];\n}\n```\n\nLe code précédent établit que la mise en cache HTTP doit être activée pour l'action `index` seulement. Il doit générer un entête HTTP `Last-Modified` basé sur l'instant de la dernière mise à jour d'articles (posts). Lorsque le navigateur visite la page `index` pour la première fois, la page est générée par le serveur et envoyée au navigateur. Si le navigateur visite à nouveau la même page, et qu'aucun article n'a été modifié, le serveur ne régénère par la page, et le navigateur utilise la version mise en cache du côté du client. En conséquence, le rendu côté serveur et la transmission de la page sont tous deux évités. \n\n\n## Entête `ETag` <span id=\"etag\"></span>\n\nL'entête \"Entity Tag\" (or `ETag` en raccourci) utilise une valeur de hachage pour représenter le contenu d'une page. Si la page est modifiée, la valeur de hachage change également. En comparant la valeur de hachage conservée sur le client avec la valeur de hachage générée côté serveur, le cache peut déterminer si la page a été modifiée et doit être retransmise.\n\nVous pouvez configurer la propriété [[yii\\filters\\HttpCache::etagSeed]] pour activer l'envoi de l'entête `ETag`. La propriété doit être une fonction de rappel PHP qui retourne un nonce (sel) pour la génération de la valeur de hachage Etag. La signature de la fonction de rappel PHP doit être comme suit :\n\n```php\n/**\n * @param Action $action l'objet action qui est actuellement géré\n * @param array $params la valeur de la propriété \"params\"\n * @return string une chaîne de caractères à utiliser comme nonce (sel) pour la génération d'une valeur de hachage ETag \n */\nfunction ($action, $params)\n```\n\nCe qui suit est un exemple d'utilisation de l'entête `ETag` :\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\HttpCache',\n            'only' => ['view'],\n            'etagSeed' => function ($action, $params) {\n                $post = $this->findModel(\\Yii::$app->request->get('id'));\n                return serialize([$post->title, $post->content]);\n            },\n        ],\n    ];\n}\n```\n\nLe code ci-dessus établit que la mise en cache HTTP doit être activée pour l'action `view` seulement. Il doit générer un entête HTTP `ETag` basé sur le titre et le contenu de l'article demandé. Lorsque le navigateur visite la page pour la première fois, la page est générée par le serveur et envoyée au navigateur. Si le navigateur visite à nouveau la même page et que ni le titre, ni le contenu de l'article n'ont changé, le serveur ne régénère pas la page et le navigateur utilise la version mise en cache côté client. En conséquence, le rendu par le serveur et la transmission de la page sont tous deux évités. \n\nETags vous autorise des stratégies de mises en cache plus complexes et/ou plus précises que l'entête `Last-Modified`. Par exemple, un ETag peut être invalidé si on a commuté le site sur un nouveau thème. \n\nDes génération coûteuses d'ETag peuvent contrecarrer l'objectif poursuivi en utilisant `HttpCache` et introduire une surcharge inutile, car il faut les réévaluer à chacune des requêtes. Essayez de trouver une expression simple qui invalide le cache si le contenu de la page a été modifié. \n\n> Note : en conformité avec la norme [RFC 7232](https://datatracker.ietf.org/doc/html/rfc7232#section-2.4),\n  `HttpCache` envoie les entêtes `ETag` et `Last-Modified` à la fois si ils sont tous deux configurés. Et si le client envoie les entêtes `If-None-Match` et `If-Modified-Since` à la fois, seul le premier est respecté. \n\n\n## Entête `Cache-Control` <span id=\"cache-control\"></span>\n\nL'entête `Cache-Control` spécifie la politique de mise en cache générale pour les pages. Vous pouvez l'envoyer en configurant la propriété [[yii\\filters\\HttpCache::cacheControlHeader]] avec la valeur de l'entête. Par défaut, l'entête suivant est envoyé :\n\n```\nCache-Control: public, max-age=3600\n```\n\n## Propriété \"Session Cache Limiter\" <span id=\"session-cache-limiter\"></span>\n\nLorsqu'une page utilise une session, PHP envoie automatiquement quelques entêtes HTTP relatifs à la mise en cache comme spécifié dans la propriété `session.cache_limiter` de PHP INI. Ces entêtes peuvent interférer ou désactiver la mise en cache que vous voulez obtenir de `HttpCache`. Pour éviter ce problème, par défaut, `HttpCache` désactive l'envoi de ces entêtes automatiquement. Si vous désirez modifier ce comportement, vous devez configurer la propriété [[yii\\filters\\HttpCache::sessionCacheLimiter]]. Cette propriété accepte une chaîne de caractères parmi `public`, `private`, `private_no_expire` et `nocache`. Reportez-vous au manuel de PHP à propos de [session_cache_limiter()](https://www.php.net/manual/fr/function.session-cache-limiter.php) pour des explications sur ces valeurs.\n\n\n## Implications SEO <span id=\"seo-implications\"></span>\n\nLes robots moteurs de recherche ont tendance à respecter les entêtes de mise en cache. Comme certains moteurs d'indexation du Web sont limités quant aux nombre de pages par domaine qu'ils sont à même de traiter dans un certain laps de temps, l'introduction d'entêtes de mise en cache peut aider à l'indexation de votre site car ils limitent le nombre de pages qui ont besoin d'être traitées.\n"
  },
  {
    "path": "docs/guide-fr/caching-overview.md",
    "content": "Mise en cache\n=============\n\nLa mise en cache est un moyen peu coûteux et efficace d'améliorer la performance d'une application Web. En stockant des données relativement statiques en cache et en les servant à partir de ce cache lorsqu'elles sont demandées, l'application économise le temps qu'il aurait fallu pour générer ces données à partir de rien à chaque demande.\n\nLa mise en cache se produit à différents endroits et à différents niveaux dans une application Web. Du côté du serveur, au niveau le plus bas, le cache peut être utilisé pour stocker des données de base, telles qu'une liste des informations sur des articles recherchée dans une base de données ; et à un niveau plus élevé, il peut être utilisé pour stocker des fragments ou l'intégralité de pages Web, telles que le rendu des articles les plus récents. \n\nDu côté client, la mise en cache HTTP peut être utilisée pour conserver le contenu des pages visitées les plus récentes dans le cache du navigateur.\n\nYii prend en charge tous ces mécanismes de mise en cache :\n\n* [Mise en cache de données](caching-data.md)\n* [Mise en cache de fragments](caching-fragment.md)\n* [Mise en cache de pages](caching-page.md)\n* [Mise en cache HTTP](caching-http.md)\n"
  },
  {
    "path": "docs/guide-fr/caching-page.md",
    "content": "Mise en cache de pages\n======================\n\nLa mise en cache de pages fait référence à la mise en cache du contenu d'une page entière du côté serveur. Plus tard, lorsque la même page est demandée à nouveau, son contenu est servi à partir du cache plutôt que d'être régénéré entièrement.\n\nLa mise en cache de pages est prise en charge par [[yii\\filters\\PageCache]], un [filtre d'action](structure-filters.md). On peut l'utiliser de la manière suivante dans une classe contrôleur :\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\PageCache',\n            'only' => ['index'],\n            'duration' => 60,\n            'variations' => [\n                \\Yii::$app->language,\n            ],\n            'dependency' => [\n                'class' => 'yii\\caching\\DbDependency',\n                'sql' => 'SELECT COUNT(*) FROM post',\n            ],\n        ],\n    ];\n}\n```\n\nLe code ci-dessus établit que la mise en cache de pages doit être utilisée uniquement pour l'action `index`. Le contenu de la page doit être mis en cache pour au plus 60 secondes et doit varier selon la langue courante de l'application. De plus, le contenu de la page mis en cache doit être invalidé si le nombre total d'articles (post) change. \n\nComme vous pouvez le constater, la mise en cache de pages est très similaire à la [mise en cache de fragments](caching-fragment.md). Les deux prennent en charge les options telles que `duration`, `dependencies`, `variations` et `enabled`. La différence principale est que la mise en cache de pages est mis en œuvre comme un [filtre d'action](structure-filters.md) alors que la mise en cache de framgents l'est comme un [composant graphique](structure-widgets.md).\n\nVous pouvez utiliser la [mise en cache de fragments](caching-fragment.md) ainsi que le [contenu dynamique](caching-fragment.md#dynamic-content) en simultanéité avec la mise en cache de pages.\n\n"
  },
  {
    "path": "docs/guide-fr/concept-aliases.md",
    "content": "Alias\n=====\n\nLes alias sont utilisés  pour représenter des chemins de fichier ou des URL de façon à ce que vous n'ayez pas besoin d'écrire ces chemins ou ces URL en entier dans votre code. Un alias doit commencer par le caractère arobase `@` pour être différentié des chemins de fichier et des URL normaux. Les alias définis sans ce caractère de tête `@` sont automatiquement préfixés avec ce dernier.\n\nYii possèdent de nombreux alias pré-définis déjà disponibles. Par exemple, l'alias `@yii` représente le chemin d'installation de la base structurée de développement PHP (*framework*), Yii ; L'alias `@web` représente l'URL de base de l'application Web en cours d'exécution.\n\nDéfinition des alias <span id=\"defining-aliases\"></span>\n--------------------\n\nVous pouvez définir un alias pour un chemin de fichier ou pour une URL en appelant [[Yii::setAlias()]]:\n\n```php\n// un alias pour un chemin de fichier\nYii::setAlias('@foo', '/path/to/foo');\n\n// un alias pour une URL\nYii::setAlias('@bar', 'https://www.example.com');\n\n\n// un alias de fichier concrêt qui contient une classe  \\foo\\Bar\nYii::setAlias('@foo/Bar.php', '/definitely/not/foo/Bar.php');\n```\n\n> Note: le chemin de fichier ou l'URL pour qui un alias est créé peut *ne pas* nécessairement faire référence à un fichier ou une ressource existante.\n\nÉtant donné un alias, vous pouvez dériver un autre alias – sans faire appel à [[Yii::setAlias()]]) – en y ajoutant une barre oblique de division `/` suivi d'un ou plusieurs segments de chemin. Les alias définis via [[Yii::setAlias()]] sont des *alias racines*, tandis que les alias qui en dérivent sont des *alias dérivés*. Par exemple, `@foo` est un alias racine, alors que `@foo/bar/file.php` est un alias dérivé.\n\nVous pouvez définir un alias en utilisant un autre alias (qu'il soit racine ou dérivé) :\n\n```php\nYii::setAlias('@foobar', '@foo/bar');\n```\n\nLes alias racines sont ordinairement définis pendant l'étape d'[amorçage](runtime-bootstrapping.md). Par exemple, vous pouvez appeler [[Yii::setAlias()]] dans le [script d'entrée](structure-entry-scripts.md). Pour commodité, la classe [Application](structure-applications.md) fournit une propriété nommée `aliases` que vous pouvez configurer dans la [configuration](concept-configurations.md) de l'application :\n\n```php\nreturn [\n    // ...\n    'aliases' => [\n        '@foo' => '/path/to/foo',\n        '@bar' => 'https://www.example.com',\n    ],\n];\n```\n\n\nRésolution des alias <span id=\"resolving-aliases\"></span>\n--------------------\n\nVous pouvez appeler [[Yii::getAlias()]] pour résoudre un alias racine en le chemin de fichier ou l'URL qu'il représente. La même méthode peut aussi résoudre un alias dérivé en le chemin de fichier ou l'URL correspondant :\n\n```php\necho Yii::getAlias('@foo');               // affiche : /path/to/foo\necho Yii::getAlias('@bar');               // affiche : https://www.example.com\necho Yii::getAlias('@foo/bar/file.php');  // affiche : /path/to/foo/bar/file.php\n```\n\nLe chemin ou l'URL que représente un alias dérivé est déterminé en remplaçant l'alias racine par le chemin ou l'URL qui lui correspond dans l'alias dérivé.\n\n> Note: la méthode [[Yii::getAlias()]] ne vérifie pas que le chemin ou l'URL qui en résulte fait référence à un fichier existant ou à une ressource existante.\n\n\nUn alias racine peut également contenir des barres obliques de division `/`. La méthode [[Yii::getAlias()]] est suffisamment intelligente pour dire quelle partie d'un alias est un alias racine et, par conséquent, déterminer correctement le chemin de fichier ou l'URL qui correspond : \n\n```php\nYii::setAlias('@foo', '/path/to/foo');\nYii::setAlias('@foo/bar', '/path2/bar');\nYii::getAlias('@foo/test/file.php');  // affiche : /path/to/foo/test/file.php\nYii::getAlias('@foo/bar/file.php');   // affiche : /path2/bar/file.php\n```\n\nSi `@foo/bar` n'est pas défini en tant qu'alias racine, la dernière instruction affiche `/path/to/foo/bar/file.php`.\n\n\nUtilisation des alias <span id=\"using-aliases\"></span>\n---------------------\n\nLes alias sont reconnus en différents endroits dans Yii sans avoir besoin d'appeler [[Yii::getAlias()]] pour les convertir en chemin ou URL. Par exemple, [[yii\\caching\\FileCache::cachePath]] accepte soit un chemin de fichier, soit un alias représentant un chemin de fichier, grâce au préfixe `@` qui permet de différentier un chemin de fichier d'un alias.\n\n```php\nuse yii\\caching\\FileCache;\n\n$cache = new FileCache([\n    'cachePath' => '@runtime/cache',\n]);\n```\n\nReportez-vous à la documentation de l'API pour savoir si une propriété ou une méthode prend en charge les alias.\n\n\nAlias prédéfinis  <span id=\"predefined-aliases\"></span>\n----------------\n\nYii prédéfinit un jeu d'alias pour faire référence à des chemins de fichier ou à des URL d'utilisation courante :\n\n- `@yii`, le dossier où le fichier `BaseYii.php` se trouve – aussi appelé dossier de la base structurée de développement PHP (*framework*).\n- `@app`, le  [[yii\\base\\Application::basePath|chemin de base]] de l'application en cours d'exécution. \n- `@runtime`, le [[yii\\base\\Application::runtimePath|chemin du dossier runtime]] de l'application en cours d'exécution. Valeur par défaut `@app/runtime`.\n- `@webroot`, le dossier Web racine de l'application en cours d'exécution. Il est déterminé en se basant sur le dossier qui contient le [script d'entrée](structure-entry-scripts.md).\n- `@web`, l'URL de base de l'application en cours d'exécution. Cet alias a la même valeur que  [[yii\\web\\Request::baseUrl]].\n- `@vendor`, le [[yii\\base\\Application::vendorPath|dossier vendor de Composer]]. Valeur par défaut `@app/vendor`.\n- `@bower`, le dossier racine des [paquets bower](https://bower.io/). Valeur par défaut `@vendor/bower`.\n- `@npm`, le dossier racine des [paquets npm](https://www.npmjs.com/). Valeur par défaut `@vendor/npm`.\n\nL'alias `@yii` est défini lorsque vous incluez le fichier `Yii.php` dans votre [script d'entrée](structure-entry-scripts.md). Les alias restants sont définis dans le constructeur de l'application au moment où la [configuration](concept-configurations.md) de l'application est appliquée.\n .\n\n\nAlias d'extension <span id=\"extension-aliases\"></span>\n-----------------\n\nUn alias est automatiquement défini par chacune des [extensions](structure-extensions.md) qui sont installées par Composer. Chaque alias est nommé d'après le nom de l'extension déclaré dans le fichier `composer.json`. Chaque alias représente le dossier racine du paquet. Par exemple, si vous installez l'extension `yiisoft/yii2-jui`, vous obtiendrez automatiquement l'alias `@yii/jui` défini durant l'étape d'[amorçage](runtime-bootstrapping.md), et équivalent à :\n\n```php\nYii::setAlias('@yii/jui', 'VendorPath/yiisoft/yii2-jui');\n```\n"
  },
  {
    "path": "docs/guide-fr/concept-autoloading.md",
    "content": "Chargement automatique des classes\n==================================\n\nYii compte sur le [mécanisme de chargement automatique des classes](https://www.php.net/manual/fr/language.oop5.autoload.php) pour localiser et inclure tous les fichiers de classes requis. Il fournit un chargeur automatique de classes de haute performance qui est conforme à la [norme PSR-4](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md). Le chargeur automatique est installé lorsque vous incluez le fichier `Yii.php`.\n\n> Note: pour simplifier la description, dans cette section, nous ne parlerons que du chargement automatique des classes. Néanmoins, gardez présent à l'esprit que le contenu que nous décrivons ici s'applique aussi au chargement automatique des interfaces et des traits. \n\n\nUtilisation du chargeur automatique de Yii <span id=\"using-yii-autoloader\"></span>\n------------------------------------------\n\nPour utiliser le chargeur automatique de classes de Yii, vous devez suivre deux règles simples lorsque vous créez et nommez vos classes :\n\n* Chaque classe doit être placée sous un [espace de noms](https://www.php.net/manual/fr/language.namespaces.php) (p. ex. `foo\\bar\\MyClass`)\n* Chaque classe doit être sauvegardée sous forme d'un fichier individuel dont le chemin est déterminé par l'algorithme suivant : \n\n```php\n// $className est un nom de classe pleinement qualifié sans la barre oblique inversée de tête\n$classFile = Yii::getAlias('@' . str_replace('\\\\', '/', $className) . '.php');\n```\n\nFor exemple, si le nom de classe et l'espace de noms sont `foo\\bar\\MyClass`, l'[alias](concept-aliases.md) pour le chemin du fichier de classe correspondant est `@foo/bar/MyClass.php`. Pour que cet alias puisse être résolu en un chemin de fichier, soit `@foo`, soit `@foo/bar` doit être un [alias racine](concept-aliases.md#defining-aliases).\n\nLorsque vous utilisez le [modèle de projet *basic*](start-installation.md), vous pouvez placer vos classes sous l'espace de noms de niveau le plus haut `app` afin qu'elles puissent être chargées automatiquement par Yii sans avoir besoin de définir un nouvel alias. Cela est dû au fait que `@app` est un [alias prédéfini](concept-aliases.md#predefined-aliases), et qu'un nom de classe comme `app\\components\\MyClass` peut être résolu en le fichier de classe `AppBasePath/components/MyClass.php`, en appliquant l'algorithme précédemment décrit. \n\nDans le [modèle de projet avancé](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md), chaque niveau possède son propre alias. Par exemple, le niveau « interface utilisateur » a l'alias `@frontend`, tandis que le niveau « interface d'administration » a l'alias `@backend`. En conséquence, vous pouvez mettre les classes de l'interface utilisateur sous l'espace de noms `frontend`, tandis que les classes de l'interface d'administration sont sous l'espace de noms `backend`. Cela permet à ces classes d'être chargées automatiquement par le chargeur automatique de Yii. \n\nPour ajouter un espace de noms personnalisé au chargeur automatique, vous devez définir un alias pour le dossier de base de l'espace de noms en utilisant  [[Yii::setAlias()]].\nPar exemple, pour charger des classes de l'espace de noms `foo` qui se trouvent dans le dossier `path/to/foo`, vous appelez `Yii::setAlias('@foo', 'path/to/foo')`.\n\n\nTable de mise en correspondance des classes <span id=\"class-map\"></span>\n-------------------------------------------\n\nLe chargeur automatique de classes de Yii prend en charge la fonctionnalité *table de mise en correspondance des classes*, qui met en correspondance les noms de classe avec les chemins de classe de fichiers. Lorsque le chargeur automatique charge une classe, il commence par chercher si la classe existe dans la table de mise en correspondance. Si c'est le cas, le chemin de fichier correspondant est inclus directement sans plus de recherche. Cela rend le chargement des classes très rapide. En fait, toutes les classes du noyau de Yii sont chargées de cette manière. \n\nVous pouvez ajouter une classe à la table de mise en correspondance des classes, stockée dans `Yii::$classMap`, avec l'instruction :\n\n```php\nYii::$classMap['foo\\bar\\MyClass'] = 'path/to/MyClass.php';\n```\n\nLes [alias](concept-aliases.md) peuvent être utilisés pour spécifier des chemins de fichier de classe. Vous devez définir la table de mise en correspondance dans le processus d'[amorçage](runtime-bootstrapping.md) afin qu'elle soit prête avant l'utilisation de vos classes. \n\n\nUtilisation d'autres chargeurs automatiques <span id=\"using-other-autoloaders\"></span>\n-------------------------------------------\n\nComme Yii utilise Composer comme gestionnaire de dépendances de paquets, il est recommandé que vous installiez aussi le chargeur automatique de Composer. Si vous utilisez des bibliothèques de tierces parties qui ont besoin de leurs propres chargeurs, vous devez installer ces chargeurs également. \n\nLors de l'utilisation conjointe du chargeur automatique de Yii et d'autres chargeurs automatiques, vous devez inclure le fichier `Yii.php` *après* que tous les autres chargeurs automatiques sont installés. Cela fait du chargeur automatique de Yii le premier à répondre à une requête de chargement automatique de classe. Par exemple, le code suivant est extrait du [script d'entrée](structure-entry-scripts.md) du [modèle de projet *basic*](start-installation.md). La première ligne installe le chargeur automatique de Composer, tandis que la seconde installe le chargeur automatique de Yii :\n\n```php\nrequire __DIR__ . '/../vendor/autoload.php';\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n```\n\nVous pouvez utiliser le chargeur automatique de Composer seul sans celui de Yii. Néanmoins, en faisant de cette manière, la performance de chargement de vos classes est dégradée et vous devez appliquer les règles de Composer pour que vos classes puissent être chargées automatiquement. \n\n> Info: si vous voulez ne pas utiliser le chargeur automatique de Yii, vous devez créer votre propre version du fichier `Yii.php` et l'inclure dans votre [script d'entrée](structure-entry-scripts.md).\n\n\nChargement automatique des classes d'extension <span id=\"autoloading-extension-classes\"></span>\n----------------------------------------------\n\nLe chargeur automatique de Yii est capable de charger automatiquement des classes d'[extension](structure-extensions.md). La seule exigence est que cette extension spécifie la section `autoload` correctement dans son fichier `composer.json`. Reportez-vous à la [documentation de Composer](https://getcomposer.org/doc/04-schema.md#autoload) pour plus de détails sur la manière de spécifier `autoload`.\n\nDans le cas où vous n'utilisez pas le chargeur automatique de Yii, le chargeur automatique de Composer peut toujours charger les classes d'extensions pour vous. \n"
  },
  {
    "path": "docs/guide-fr/concept-behaviors.md",
    "content": "Comportements\n=============\n\nLes comportements (*behaviors* sont des instances de la classe [[yii\\base\\Behavior]], ou de ses classes filles. Les comportements, aussi connus sous le nom de [mixins](https://fr.wikipedia.org/wiki/Mixin), vous permettent d'améliorer les fonctionnalités d'une classe de [[yii\\base\\Component|composant]] existante sans avoir à modifier les héritages de cette classe. Le fait d'attacher un comportement à un composant injecte les méthodes et les propriétés de ce comportement dans le composant, rendant ces méthodes et ces propriétés accessibles comme si elles avaient été définies dans la classe du composant lui-même. En outre, un comportement peut répondre aux [événements](concept-events.md) déclenchés par le composant, ce qui permet aux comportements de personnaliser l'exécution normale du code du composant.\n\n\nDéfinition des comportements <span id=\"defining-behaviors\"></span>\n---------------------------\n\nPour définir un comportement, vous devez créer une classe qui étend la classe  [[yii\\base\\Behavior]], ou une des ses classes filles. Par exemple :\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Behavior;\n\nclass MyBehavior extends Behavior\n{\n    public $prop1;\n\n    private $_prop2;\n\n    public function getProp2()\n    {\n        return $this->_prop2;\n    }\n\n    public function setProp2($value)\n    {\n        $this->_prop2 = $value;\n    }\n\n    public function foo()\n    {\n        // ...\n    }\n}\n```\n\nLe code ci-dessus définit la classe de comportement `app\\components\\MyBehavior` avec deux propriété — `prop1` et `prop2` — et une méthode `foo()`. Notez que la propriété `prop2` est définie via la méthode d'obtention `getProp2` et la méthode d'assignation `setProp2`. Cela est le cas parce que la classe  [[yii\\base\\Behavior]] étend la classe [[yii\\base\\BaseObject]] et, par conséquent, prend en charge la définition des [propriétés](concept-properties.md) via les méthodes d'obtention et d'assignation. \n\nComme cette classe est un comportement, lorsqu'elle est attachée à un composant, ce composant acquiert alors les propriétés  `prop1` et `prop2`, ainsi que la méthode `foo()`.\n\n> Tip: depuis l'intérieur d'un comportement, vous avez accès au composant auquel le comportement est attaché via la propriété [[yii\\base\\Behavior::owner]].\n\n> Note: dans le cas où les méthodes  [[yii\\base\\Behavior::__get()]] et/ou [[yii\\base\\Behavior::__set()]] du comportement sont redéfinies, vous devez redéfinir les méthodes [[yii\\base\\Behavior::canGetProperty()]] et/ou [[yii\\base\\Behavior::canSetProperty()]] également.\n\nGestion des événements du composant\n-----------------------------------\n\nSi un comportement a besoin de répondre aux événements déclenchés par le composant auquel il est attaché, il doit redéfinir la méthode [[yii\\base\\Behavior::events()]]. Par exemple:\n\n```php\nnamespace app\\components;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\base\\Behavior;\n\nclass MyBehavior extends Behavior\n{\n    // ...\n\n    public function events()\n    {\n        return [\n            ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',\n        ];\n    }\n\n    public function beforeValidate($event)\n    {\n        // ...\n    }\n}\n```\n\nLa méthode [[yii\\base\\Behavior::events()|events()]] doit retourner une liste d'événements avec leur gestionnaire correspondant. L'exemple ci-dessus déclare que l'événement [[yii\\db\\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]] existe et définit son gestionnaire `beforeValidate()`. En spécifiant un gestionnaire d'événement, vous pouvez utiliser un des formats suivants :\n \n* une chaîne de caractères qui fait référence au nom d'une méthode de la classe du comportement, comme dans l'exemple ci-dessus ;\n* un tableau constitué d'un nom d'objet ou de classe et d'un nom de méthode sous forme de chaîne de caractères (sans les parenthèses), p. ex. `[$object, 'methodName']`;\n* une fonction anonyme.\n\nLa signature d'un gestionnaire d'événement doit être similaire à ce qui suit, où `event` fait référence au paramètre événement. Reportez-vous à la section [Événements](concept-events.md) pour plus de détail sur les événements.\n\n```php\nfunction ($event) {\n}\n```\n\nAttacher des comportements <span id=\"attaching-behaviors\"></span>\n-----------------------------\n\nVous pouvez attacher un comportement à un [[yii\\base\\Component|composant]] soit de manière statique, soit de manière dynamique. Le première manière est une pratique plus habituelle.\n\nPour attacher un comportement de manière statique, redéfinissez la méthode [[yii\\base\\Component::behaviors()|behaviors()]] de la classe du composant auquel le comportement va être attaché. La méthode [[yii\\base\\Component::behaviors()|behaviors()]] doit retourner une liste de [configurations](concept-configurations.md) de comportements. Chaque comportement peut être soit un nom de classe de comportement, soit un tableau de configuration :\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\nuse app\\components\\MyBehavior;\n\nclass User extends ActiveRecord\n{\n    public function behaviors()\n    {\n        return [\n            // comportement anonyme, nom de la classe de comportement seulement\n            MyBehavior::class,\n\n            // comportement nommé, nom de classe de comportement seulement\n            'myBehavior2' => MyBehavior::class,\n\n            // comportement anonyme, tableau de configuration\n            [\n                'class' => MyBehavior::class,\n                'prop1' => 'value1',\n                'prop2' => 'value2',\n            ],\n\n            // comportement nommé, tableau de configuration\n            'myBehavior4' => [\n                'class' => MyBehavior::class,\n                'prop1' => 'value1',\n                'prop2' => 'value2',\n            ]\n        ];\n    }\n}\n```\n\nVous pouvez associer un nom au comportement en spécifiant la clé de tableau correspondant à la configuration du comportement. Dans ce cas, le comportement est appelé *comportement nommé*. Dans l'exemple ci-dessus, il y a deux comportements nommés : `myBehavior2` et `myBehavior4`. Si un comportement n'est pas associé à un nom, il est appelé *comportement anonyme*.\n\n\nPour attacher un comportement de manière dynamique, appelez la méthode [[yii\\base\\Component::attachBehavior()]] du composant auquel le comportement va être attaché : \n\n```php\nuse app\\components\\MyBehavior;\n\n// attache un objet comportement \n$component->attachBehavior('myBehavior1', new MyBehavior());\n\n// attache un classe de comportement\n$component->attachBehavior('myBehavior2', MyBehavior::class);\n\n// attache un tableau de configuration \n$component->attachBehavior('myBehavior3', [\n    'class' => MyBehavior::class,\n    'prop1' => 'value1',\n    'prop2' => 'value2',\n]);\n```\n\nVous pouvez attacher plusieurs comportements à la fois en utilisant la méthode  [[yii\\base\\Component::attachBehaviors()]] :\n\n```php\n$component->attachBehaviors([\n    'myBehavior1' => new MyBehavior(), // un comportement nommé\n    MyBehavior::class,                 // un comportement anonyme\n]);\n```\n\nVous pouvez aussi attacher des comportements via les [configurations](concept-configurations.md) comme ceci :\n\n```php\n[\n    'as myBehavior2' => MyBehavior::class,\n\n    'as myBehavior3' => [\n        'class' => MyBehavior::class,\n        'prop1' => 'value1',\n        'prop2' => 'value2',\n    ],\n]\n```\n\nPour plus de détails, reportez-vous à la section  [Configurations](concept-configurations.md#configuration-format).\n\nUtilisation des comportements <span id=\"using-behaviors\"></span>\n-----------------------------\n\nPour utiliser un comportement, commencez par l'attacher à un [[yii\\base\\Component|composant]] en suivant les instructions données ci-dessus. Une fois le comportement attaché au composant, son utilisation est évidente.\n\nVous pouvez accéder à une variable membre *publique*, ou à une  [propriété](concept-properties.md) définie par une méthode d'obtention et/ou une méthode d'assignation (*getter* et *setter*), du comportement, via le composant auquel ce comportement est attaché : \n\n```php\n// \"prop1\" est une propriété définie dans la classe du comportement\necho $component->prop1;\n$component->prop1 = $value;\n```\n\nVous pouvez aussi appeler une méthode *publique* du comportement de façon similaire :\n\n```php\n// foo() est une méthode publique définie dans la classe du comportement\n$component->foo();\n```\n\nComme vous pouvez le voir, bien que le composant `$component` ne définissent pas `prop1` et`foo()`, elles peuvent être utilisées comme si elles faisaient partie de la définition du composant grâce au comportement attaché. \n\nSi deux comportement définissent la même propriété ou la même méthode, et que ces deux comportement sont attachés au même composant, le comportement qui a été attaché le *premier* prévaut lorsque la propriété ou la méthode est accédée.\n\nUn comportement peut être associé à un nom lorsqu'il est attaché à un composant. Dans un tel cas, vous pouvez accéder à l'objet comportement en utilisant ce nom :\n\n```php\n$behavior = $component->getBehavior('myBehavior');\n```\n\nVous pouvez aussi obtenir tous les comportements attachés au composant :\n\n```php\n$behaviors = $component->getBehaviors();\n```\n\n\nDétacher des comportements <span id=\"detaching-behaviors\"></span>\n--------------------------\n\nPour détacher un comportement, appelez [[yii\\base\\Component::detachBehavior()]] avec le nom associé au comportement :\n\n```php\n$component->detachBehavior('myBehavior1');\n```\n\nVous pouvez aussi détacher *tous les* comportements : \n\n```php\n$component->detachBehaviors();\n```\n\n\nUtilisation de  `TimestampBehavior` <span id=\"using-timestamp-behavior\"></span>\n-------------------------\n\nPour aller à l'essentiel, jetons un coup d'œil à [[yii\\behaviors\\TimestampBehavior]]. Ce comportement prend automatiquement en charge la mise à jour de l'attribut *timestamp* (horodate) d'un modèle [[yii\\db\\ActiveRecord|enregistrement actif]] à chaque fois qu'il est sauvegardé via les méthodes `insert()`, `update()` ou `save()`.\n\nTout d'abord, attachez ce comportement à la classe [[yii\\db\\ActiveRecord|Active Record (enregistrement actif)]] que vous envisagez d'utiliser :\n\n```php\nnamespace app\\models\\User;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\behaviors\\TimestampBehavior;\n\nclass User extends ActiveRecord\n{\n    // ...\n\n    public function behaviors()\n    {\n        return [\n            [\n                'class' => TimestampBehavior::class,\n                'attributes' => [\n                    ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],\n                    ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'],\n                ],\n                // si vous utilisez datetime au lieur de l'UNIX timestamp:\n                // 'value' => new Expression('NOW()'),\n            ],\n        ];\n    }\n}\n```\n\nLe comportement ci-dessus spécifie que lorsque l'enregistrement est : \n\n* inséré, le comportement doit assigner l'horodate UNIX courante aux attributs `created_at` (créé le)  et `updated_at` (mis à jour le) ;\n* mis à jour, le comportement doit assigner l'horodate UNIX courante à l'attribut `updated_at` ;\n\n> Note: pour que la mise en œuvre ci-dessus fonctionne avec une base de données MySQL, vous devez déclarer les colonnes (`created_at`, `updated_at`) en tant que `int(11)` pour qu'elles puissent représenter des horodates UNIX. \n\nAvec ce code en place, si vous avez un objet `User` (utilisateur) et que vous essayez de le sauvegarder, il verra ses attributs `created_at` et `updated_at` automatiquement remplis avec l'horodate UNIX :\n\n```php\n$user = new User;\n$user->email = 'test@example.com';\n$user->save();\necho $user->created_at;  // affiche l'horodate courante\n```\n\nLe comportement [[yii\\behaviors\\TimestampBehavior|TimestampBehavior]] offre également une méthode utile [[yii\\behaviors\\TimestampBehavior::touch()|touch()]], qui assigne l'horodate courante à un attribut spécifié et le sauvegarde dans la base de données : \n\n```php\n$user->touch('login_time');\n```\n\nAutres comportements\n--------------------\n\nIl existe plusieurs comportements pré-inclus et extérieurs disponibles :\n\n- [[yii\\behaviors\\BlameableBehavior]] – remplit automatiquement les attributs spécifiés avec l'identifiant de l'utilisateur courant. \n- [[yii\\behaviors\\SluggableBehavior]] – remplit automatiquement l'attribut spécifié avec une valeur utilisable en tant que chaîne purement ASCII (*slug*) dans une URL. \n- [[yii\\behaviors\\AttributeBehavior]] – assigne automatiquement une valeur spécifiée à un ou plusieurs attributs d'un objet enregistrement actif lorsque certains événements se produisent. \n- [yii2tech\\ar\\softdelete\\SoftDeleteBehavior](https://github.com/yii2tech/ar-softdelete) – fournit des méthodes pour une suppression douce et une restauration douce d'un enregistrement actif c.-à-d. positionne un drapeau ou un état qui marque l'enregistrement comme étant effacé.\n- [yii2tech\\ar\\position\\PositionBehavior](https://github.com/yii2tech/ar-position) – permet la gestion de l'ordre des enregistrements dans un champ entier (*integer*) en fournissant les méthodes de remise dans l'ordre.\n\nComparaison des comportement et des traits <span id=\"comparison-with-traits\"></span>\n------------------------------------------\n\nBien que les comportements  soient similaires aux [traits](https://www.php.net/manual/fr/language.oop5.traits.php) par le fait qu'ils *injectent* tous deux leurs propriétés et leurs méthodes dans la classe primaire, ils diffèrent par de nombreux aspects. Comme nous l'expliquons ci-dessous, ils ont chacun leurs avantages et leurs inconvénients. Ils sont plus des compléments l'un envers l'autre, que des alternatives. \n\n\n### Raisons d'utiliser des comportements <span id=\"pros-for-behaviors\"></span>\n\nLes classes de comportement, comme les classes normales, prennent en charge l'héritage. Les traits, par contre, peuvent être considérés comme des copier coller pris en charge par le langage. Ils ne prennent pas en charge l'héritage. \n\nLes comportements peuvent être attachés et détachés à un composant de manière dynamique sans qu'une modification de la classe du composant soit nécessaire. Pour utiliser un trait, vous devez modifier le code de la classe qui l'utilise. \n\nLes comportements sont configurables mais les traits ne le sont pas. \n\nLes comportement peuvent personnaliser l'exécution du code d'un composant en répondant à ses événements.\n\nLorsqu'il se produit des conflits de noms entre les différents comportements attachés à un même composant, les conflits sont automatiquement résolus  en donnant priorité au comportement attaché le premier. Les conflits de noms causés par différents traits nécessitent une résolution manuelle en renommant les propriétés et méthodes concernées. \n\n\n### Raisons d'utiliser des traits <span id=\"pros-for-traits\"></span>\n\nLes traits sont beaucoup plus efficaces que les comportements car les comportements sont des objets qui requièrent plus de temps du processeur et plus de mémoire. \n\nLes environnement de développement intégrés (EDI) sont plus conviviaux avec les traits car ces derniers sont des constructions natives du langage. \n"
  },
  {
    "path": "docs/guide-fr/concept-components.md",
    "content": "Composants\n==========\n\nLes composants sont les blocs de constructions principaux de vos applications Yii. Les composants sont des instances de la classe [[yii\\base\\Component]],\nou de ses classes filles. Les trois fonctionnalités principales fournies par les composants aux autres classes sont :\n\n* [Les propriétés](concept-properties.md) ;\n* [Les événements](concept-events.md) ;\n* [Les comportements](concept-behaviors.md).\n \nSéparément et en combinaisons, ces fonctionnalités rendent les classes de Yii beaucoup plus personnalisables et faciles à utiliser. Par exemple, l'[[yii\\jui\\DatePicker|objet graphique de sélection de date]] inclus, un composant d'interface utilisateur, peut être utilisé dans une [vue](structure-views.md) pour générer un sélecteur de date interactif :\n\n```php\nuse yii\\jui\\DatePicker;\n\necho DatePicker::widget([\n    'language' => 'ru',\n    'name'  => 'country',\n    'clientOptions' => [\n        'dateFormat' => 'yy-mm-dd',\n    ],\n]);\n```\nLes propriétés de l'objet graphique sont faciles à écrire car la classe étend [[yii\\base\\Component]].\n\nTandis que les composants sont très puissants, ils sont un peu plus lourds que les objets normaux. Cela est dû au fait que, en particulier,  la prise en charge des fonctionnalités [event](concept-events.md) et [behavior](concept-behaviors.md) requiert un peu plus de mémoire et de temps du processeur. Si vos composants n'ont pas besoin de ces deux fonctionnalités, vous devriez envisager d'étendre la classe [[yii\\base\\BaseObject]] au lieu de la classe [[yii\\base\\Component]]. Ce faisant, votre composant sera aussi efficace que les objets PHP normaux, mais avec la prise en charge des [propriétés](concept-properties.md).\n\nLorsque votre classe étend la classe [[yii\\base\\Component]] ou [[yii\\base\\BaseObject]], il est recommandé que suiviez ces conventions :\n\n- Si vous redéfinissez le constructeur, spécifiez un paramètre `$config` en tant que *dernier* paramètre du constructeur et passez le au constructeur du parent. \n- Appelez toujours le constructeur du parent *à la fin* de votre constructeur redéfini.\n- Si vous redéfinissez la méthode [[yii\\base\\BaseObject::init()]], assurez-vous que vous appelez la méthode `init()` mise en œuvre par le parent *au début* de votre méthodes `init()`.\n\nPar exemple :\n\n```php\n<?php\n\nnamespace yii\\components\\MyClass;\n\nuse yii\\base\\BaseObject;\n\nclass MyClass extends BaseObject\n{\n    public $prop1;\n    public $prop2;\n\n    public function __construct($param1, $param2, $config = [])\n    {\n        // ... initialisation avant l'application de la configuration\n\n        parent::__construct($config);\n    }\n\n    public function init()\n    {\n        parent::init();\n\n        // ... initialization après l'application de la configuration\n    }\n}\n```\n\nLe respect de ces conseils rend vos composants  [configurables](concept-configurations.md) lors de leur création. Par exemple :\n\n```php\n$component = new MyClass(1, 2, ['prop1' => 3, 'prop2' => 4]);\n// alternatively\n$component = \\Yii::createObject([\n    'class' => MyClass::class,\n    'prop1' => 3,\n    'prop2' => 4,\n], [1, 2]);\n```\n\n> Info: bien que l'approche qui consiste à appeler la méthode [[Yii::createObject()]] semble plus compliquée, elle est plus puissante car elle est mise en œuvre sur un [conteneur d'injection de dépendances](concept-di-container.md).\n  \n\nLa classe [[yii\\base\\BaseObject]] fait appliquer le cycle de vie suivant de l'objet :\n\n1. Pré-initialisation dans le constructeur. Vous pouvez définir les propriétés par défaut à cet endroit.\n2. Configuration de l'objet via `$config`. La configuration peut écraser les valeurs par défaut définies dans le constructeur.\n3. Post-initialisation dans la méthode [[yii\\base\\BaseObject::init()|init()]]. Vous pouvez redéfinir cette méthode pour effectuer des tests sanitaires et normaliser les propriétés.\n4. Appel des méthodes de l'objet.\n\nLes trois premières étapes arrivent toutes durant la construction de l'objet. Cela signifie qu'une fois que vous avez obtenu une instance de la classe (c.-à-d. un objet), cet objet a déjà été initialisé dans un état propre et fiable. \n"
  },
  {
    "path": "docs/guide-fr/concept-configurations.md",
    "content": "Configurations\n==============\n\nLes configurations sont très largement utilisées dans Yii lors de la création d'objets ou l'initialisation d'objets existants. Les configurations contiennent généralement le nom de la classe de l'objet en cours de création, et une liste de valeurs initiales qui doivent être assignées aux [propriétés](concept-properties.md) de l'objet. Elles peuvent aussi comprendre une liste de gestionnaires qui doivent être attachés aux [événements](concept-events.md) de l'objet et/ou une liste de [comportements](concept-behaviors.md) qui doivent être attachés à l'objet. \n\nDans ce qui suit, une configuration est utilisée pour créer et initialiser une connexion à une base de données :\n\n```php\n$config = [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n];\n\n$db = Yii::createObject($config);\n```\n\nLa méthode [[Yii::createObject()]] prend un tableau de configuration en tant qu'argument et crée un objet en instanciant la classe nommée dans la configuration. Lorsque l'objet est instancié, le reste de la configuration est utilisé pour initialiser les propriétés de l'objet, ses gestionnaires d'événement et ses comportements. \n\nSi vous disposez déjà d'un objet, vous pouvez utiliser la méthode [[Yii::configure()]] pour initialiser les propriétés de l'objet avec un tableau de configuration :\n\n```php\nYii::configure($object, $config);\n```\n\nNotez bien que dans ce cas, le tableau de configuration ne doit pas contenir d'élément `class`.\n\n\n## Format d'une configuration <span id=\"configuration-format\"></span>\n\nLe format d'une configuration peut être formellement décrit comme suit :\n\n```php\n[\n    'class' => 'ClassName',\n    'propertyName' => 'propertyValue',\n    'on eventName' => $eventHandler,\n    'as behaviorName' => $behaviorConfig,\n]\n```\n\noù\n\n* L'élément `class` spécifie un nom de classe pleinement qualifié pour l'objet à créer.\n* L'élément `propertyName` spécifie les valeurs initiales d'une propriété nommé property. Les clés sont les noms de propriété et les valeurs correspondantes les valeurs initiales. Seules les variables membres publiques et les [propriétés](concept-properties.md) définies par des méthodes d'obtention (*getters*) et/ou des méthodes d'assignation (*setters*) peuvent être configurées.\n* Les éléments `on eventName` spécifient quels gestionnaires doivent être attachés aux [événements](concept-events.md) de l'objet. Notez que les clés du tableau sont formées en préfixant les noms d'événement par `on`. Reportez-vous à la section [événements](concept-events.md) pour connaître les formats des gestionnaires d'événement pris en charge.\n* L'élément `as behaviorName` spécifie quels [comportements](concept-behaviors.md) doivent être attachés à l'objet. Notez que les clés du tableau sont formées en préfixant les noms de comportement par `as ` ; la valeur `$behaviorConfig` représente la configuration pour la création du comportement, comme une configuration normale décrite ici. \n\nCi-dessous, nous présentons un exemple montrant une configuration avec des valeurs initiales de propriétés, des gestionnaires d'événement et des comportements.\n\n```php\n[\n    'class' => 'app\\components\\SearchEngine',\n    'apiKey' => 'xxxxxxxx',\n    'on search' => function ($event) {\n        Yii::info(\"Keyword searched: \" . $event->keyword);\n    },\n    'as indexer' => [\n        'class' => 'app\\components\\IndexerBehavior',\n        // ... property init values ...\n    ],\n]\n```\n\n\n## Utilisation des configurations <span id=\"using-configurations\"></span>\n\nLes configurations sont utilisées en de nombreux endroits dans Yii. Au début de cette section, nous avons montré comment créer un objet obéissant à une configuration en utilisant la méthode [[Yii::createObject()]]. Dans cette sous-section, nous allons décrire les configurations d'applications et les configurations d'objets graphiques (*widget*) – deux utilisations majeures des configurations.\n\n\n### Configurations d'applications <span id=\"application-configurations\"></span>\n\nLa configuration d'une [application](structure-applications.md) est probablement l'un des tableaux les plus complexes dans Yii. Cela est dû au fait que la classe [[yii\\web\\Application|application]] dispose d'un grand nombre de propriétés et événements configurables. De première importance, se trouve sa propriété [[yii\\web\\Application::components|components]] qui peut recevoir un tableau de configurations pour créer des composants qui sont enregistrés durant l'exécution de l'application. Ce qui suit est un résumé de la configuration de l'application du [modèle de projet *basic*](start-installation.md).\n\n```php\n$config = [\n    'id' => 'basic',\n    'basePath' => dirname(__DIR__),\n    'extensions' => require __DIR__ . '/../vendor/yiisoft/extensions.php',\n    'components' => [\n        'cache' => [\n            'class' => 'yii\\caching\\FileCache',\n        ],\n        'mailer' => [\n            'class' => 'yii\\swiftmailer\\Mailer',\n        ],\n        'log' => [\n            'class' => 'yii\\log\\Dispatcher',\n            'traceLevel' => YII_DEBUG ? 3 : 0,\n            'targets' => [\n                [\n                    'class' => 'yii\\log\\FileTarget',\n                ],\n            ],\n        ],\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=stay2',\n            'username' => 'root',\n            'password' => '',\n            'charset' => 'utf8',\n        ],\n    ],\n];\n```\n\nLa configuration n'a pas de clé `class`. Cela tient au fait qu'elle est utilisée comme indiqué ci-dessous dans un [script d'entrée](structure-entry-scripts.md), dans lequel le nom de la classe est déjà donné :\n\n```php\n(new yii\\web\\Application($config))->run();\n```\n\nPlus de détails sur la configuration de la propriété `components` d'une application sont donnés dans la section [Applications](structure-applications.md) et dans la section [Localisateur de services](concept-service-locator.md).\n\nDepuis la version 2.0.11, la configuration de l'application prend en charge la configuration du [Conteneur d'injection de dépendances](concept-di-container.md)\nvia la propriété `container`. Par exemple :\n\n```php\n$config = [\n    'id' => 'basic',\n    'basePath' => dirname(__DIR__),\n    'extensions' => require __DIR__ . '/../vendor/yiisoft/extensions.php',\n    'container' => [\n        'definitions' => [\n            'yii\\widgets\\LinkPager' => ['maxButtonCount' => 5]\n        ],\n        'singletons' => [\n            // Configuration du singleton Dependency Injection Container\n        ]\n    ]\n];\n```\n\nPour en savoir plus sur les valeurs possibles des tableaux de configuration de   `definitions` et `singletons`  et avoir des exemples de la vie réelle, reportez-vous à la sous-section [Utilisation pratique avancée](concept-di-container.md#advanced-practical-usage) de l'article \n[Conteneur d'injection de dépendances](concept-di-container.md).\n\n### Configurations des objets graphiques <span id=\"widget-configurations\"></span>\n\nLorsque vous utilisez des [objets graphiques](structure-widgets.md), vous avez souvent besoin d'utiliser des configurations pour personnaliser les propriétés de ces objets graphiques. Les méthodes [[yii\\base\\Widget::widget()]] et [[yii\\base\\Widget::begin()]] peuvent toutes deux être utilisées pour créer un objet graphique. Elles acceptent un tableau de configuration, comme celui qui suit : \n\n```php\nuse yii\\widgets\\Menu;\n\necho Menu::widget([\n    'activateItems' => false,\n    'items' => [\n        ['label' => 'Home', 'url' => ['site/index']],\n        ['label' => 'Products', 'url' => ['product/index']],\n        ['label' => 'Login', 'url' => ['site/login'], 'visible' => Yii::$app->user->isGuest],\n    ],\n]);\n```\n\nLa configuration ci-dessus crée un objet graphique nommé `Menu` et initialise sa propriété `activateItems` à `false` (faux). La propriété `items` est également configurée avec les items de menu à afficher.\n\nNotez que, comme le nom de classe est déjà donné, le tableau de configuration ne doit PAS contenir de clé `class`. \n\n## Fichiers de configuration <span id=\"configuration-files\"></span>\n\nLorsqu'une configuration est très complexe, une pratique courante est de la stocker dans un ou plusieurs fichiers PHP appelés *fichiers de configuration*. Un fichier de configuration retourne un tableau PHP représentant la configuration. Par exemple, vous pouvez conserver une configuration d'application dans un fichier nommé `web.php`, comme celui qui suit :\n\n```php\nreturn [\n    'id' => 'basic',\n    'basePath' => dirname(__DIR__),\n    'extensions' => require __DIR__ . '/../vendor/yiisoft/extensions.php',\n    'components' => require __DIR__ . '/components.php',\n];\n```\n\nParce que la configuration `components` et elle aussi complexe, vous pouvez la stocker dans un fichier séparé appelé `components.php` et requérir ce fichier dans `web.php` comme c'est montré ci-dessus. Le contenu de `components.php` ressemble à ceci :\n\n```php\nreturn [\n    'cache' => [\n        'class' => 'yii\\caching\\FileCache',\n    ],\n    'mailer' => [\n        'class' => 'yii\\swiftmailer\\Mailer',\n    ],\n    'log' => [\n        'class' => 'yii\\log\\Dispatcher',\n        'traceLevel' => YII_DEBUG ? 3 : 0,\n        'targets' => [\n            [\n                'class' => 'yii\\log\\FileTarget',\n            ],\n        ],\n    ],\n    'db' => [\n        'class' => 'yii\\db\\Connection',\n        'dsn' => 'mysql:host=localhost;dbname=stay2',\n        'username' => 'root',\n        'password' => '',\n        'charset' => 'utf8',\n    ],\n];\n```\n\nPour obtenir une configuration stockée dans un fichier de configuration, il vous suffit requérir ce fichier avec \"require\", comme ceci :\n\n```php\n$config = require 'path/to/web.php';\n(new yii\\web\\Application($config))->run();\n```\n\n\n## Configurations par défaut <span id=\"default-configurations\"></span>\n\nLa méthode [[Yii::createObject()]] est implémentée sur la base du [conteneur d'injection de dépendances](concept-di-container.md). Cela vous permet de spécifier un jeu de configurations dites *configurations par défaut* qui seront appliquées à TOUTES les instances des classes spécifiées lors de leur création en utilisant [[Yii::createObject()]]. Les configurations par défaut peuvent être spécifiées en appelant `Yii::$container->set()` dans le code d'[amorçage](runtime-bootstrapping.md).\n\nPar exemple, si vous voulez personnaliser l'objet graphique [[yii\\widgets\\LinkPager]] de façon à ce que TOUS les fonctions de mise en page (pagers) affichent au plus 5 boutons de page (la valeur par défaut est 10), vous pouvez utiliser le code suivant pour atteindre ce but : \n\n```php\n\\Yii::$container->set('yii\\widgets\\LinkPager', [\n   'maxButtonCount' => 5,\n]);\n```\n\nSans les configurations par défaut, vous devez configurer la propriété `maxButtonCount` partout où vous utilisez un pagineur.\n\n\n## Constantes d'environment <span id=\"environment-constants\"></span>\n\nLes configurations varient souvent en fonction de l'environnement dans lequel les applications s'exécutent. Par exemple, dans l'environnement de développement, vous désirez peut être utiliser la base de données nommée `mydb_dev`, tandis que sur un serveur en production, vous désirez utiliser la base de données nommée `mydb_prod`. Pour faciliter le changement d'environnement, Yii fournit une constante nommée `YII_ENV` que vous pouvez définir dans le [script d'entrée](structure-entry-scripts.md) de votre application. Par exemple :\n\n```php\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n```\n\nVous pouvez assigner à `YII_ENV` une des valeurs suivantes :\n\n- `prod`: environnement de production. La constante `YII_ENV_PROD` est évaluée comme étant `true` (vrai). C'est la valeur par défaut de `YII_ENV`.\n- `dev`: environnement de développement. La constante `YII_ENV_DEV` est évaluée comme étant `true` (vrai).\n- `test`: environnement de test. La constante `YII_ENV_TEST` est évaluée comme étant `true` (vrai).\n\nAvec ces constantes d'environnement, vous pouvez spécifier les configurations en fonction de l'environnement courant. Par exemple, votre configuration d'application peut contenir le code suivant pour activer la [barre de débogage et le module de débogage](tool-debugger.md) dans l'environnement de développement seulement :\n\n```php\n$config = [...];\n\nif (YII_ENV_DEV) {\n    // ajustement de la configuration pour l'environnement 'dev'\n    $config['bootstrap'][] = 'debug';\n    $config['modules']['debug'] = 'yii\\debug\\Module';\n}\n\nreturn $config;\n```\n"
  },
  {
    "path": "docs/guide-fr/concept-di-container.md",
    "content": "Conteneur d'injection de dépendances\n====================================\n\nUn conteneur d'injection de dépendances (DI container) est un objet qui sait comment instancier et configurer des objets et tous leurs objets dépendants. [Cet article de Martin Fowler](https://martinfowler.com/articles/injection.html) explique très bien en quoi un conteneur d'injection de dépendances est utile. Ici nous expliquons essentiellement l'utilisation qui est faite du conteneur d'injection de dépendances que fournit Yii.\n\n\nInjection de dépendances <span id=\"dependency-injection\"></span>\n------------------------\n\nYii fournit la fonctionnalité conteneur d'injection de dépendances via la classe [[yii\\di\\Container]]. Elle prend en charge les sortes d'injections de dépendance suivantes :\n\n* Injection par le constructeur ;\n* Injection par les méthodes ;\n* Injection par les méthodes d'assignation et les propriétés ;\n* Injection par une méthode de rappel PHP ;\n\n\n### Injection par le constructeur <span id=\"constructor-injection\"></span>\n\nLe conteneur d'injection de dépendances prend en charge l'injection dans le constructeur grâce à l'allusion au type pour les paramètres du constructeur. L'allusion au type indique au conteneur de quelles classes ou de quelles interfaces dépend l'objet concerné par la construction. Le conteneur essaye de trouver les instances des classes dont l'objet dépend pour les injecter dans le nouvel objet via le constructeur. Par exemple :\n\n```php\nclass Foo\n{\n    public function __construct(Bar $bar)\n    {\n    }\n}\n\n$foo = $container->get('Foo');\n// qui est équivalent à ce qui suit\n$bar = new Bar;\n$foo = new Foo($bar);\n```\n\n\n### Injection par les méthodes <span id=\"method-injection\"></span>\n\nOrdinairement, les classes dont une classe dépend sont passées à son constructeur et sont disponibles dans la classe durant tout son cycle de vie. Avec l'injection par les méthodes, il est possible de fournir une classe qui est seulement nécessaire à une unique méthode de la classe, et qu'il est impossible de passer au constructeur, ou qui pourrait entraîner trop de surplus de travail dans la majorité des classes qui l'utilisent. \n\nUne méthode de classe peut être définie comme la méthode `doSomething()` de l'exemple suivant :\n\n```php\nclass MyClass extends \\yii\\base\\Component\n{\n    public function __construct(/*ici, quelques classes légères dont la classe dépend*/, $config = [])\n    {\n        // ...\n    }\n\n    public function doSomething($param1, \\ma\\dependance\\Lourde $something)\n    {\n        // faire quelque chose avec $something\n    }\n}\n```\n\nVous pouvez appeler la méthode, soit en passant une instance de `\\ma\\dependance\\Lourde` vous-même, soit en utilisant [[yii\\di\\Container::invoke()]] comme ceci :\n\n```php\n$obj = new MyClass(/*...*/);\nYii::$container->invoke([$obj, 'doSomething'], ['param1' => 42]); // $something est fournie par le conteneur d'injection de dépendances\n```\n\n### Injection par les méthodes d'assignation et les propriétés <span id=\"setter-and-property-injection\"></span>\n\nL'injection par les méthodes d'assignation et les propriétés est prise en charge via les [configurations](concept-configurations.md). Lors de l'enregistrement d'une dépendance ou lors de la création d'un nouvel objet, vous pouvez fournir une configuration qui est utilisée par le conteneur pour injecter les dépendances via les méthodes d'assignation ou les propriétés correspondantes. Par exemple :\n\n```php\nuse yii\\base\\BaseObject;\n\nclass Foo extends BaseObject\n{\n    public $bar;\n\n    private $_qux;\n\n    public function getQux()\n    {\n        return $this->_qux;\n    }\n\n    public function setQux(Qux $qux)\n    {\n        $this->_qux = $qux;\n    }\n}\n\n$container->get('Foo', [], [\n    'bar' => $container->get('Bar'),\n    'qux' => $container->get('Qux'),\n]);\n```\n\n> Info: la méthode [[yii\\di\\Container::get()]] accepte un tableau de configurations qui peut être appliqué à l'objet en création comme troisième paramètre. Si la classe implémente l'interface [[yii\\base\\Configurable]] (p. ex. [[yii\\base\\BaseObject]]), le tableau de configuration est passé en tant que dernier paramètre du constructeur de la classe ; autrement le tableau de configuration serait appliqué *après* la création de l'objet. \n\n### Injection par une méthode de rappel PHP <span id=\"php-callable-injection\"></span>\n\nDans ce cas, le conteneur utilise une fonction de rappel PRP enregistrée pour construire de nouvelles instances d'une classe. À chaque fois que [[yii\\di\\Container::get()]] est appelée, la fonction de rappel correspondante est invoquée. Cette fonction de rappel est chargée de la résolution des dépendances et de leur injection appropriée dans les objets nouvellement créés. Par exemple :\n\n```php\n$container->set('Foo', function ($container, $params, $config) {\n    $foo = new Foo(new Bar);\n    // ... autres initialisations ...\n    return $foo;\n});\n\n$foo = $container->get('Foo');\n```\n\nPour cacher la logique complexe de construction des nouveaux objets, vous pouvez utiliser un méthode de classe statique en tant que fonction de rappel. Par exemple :\n\n```php\nclass FooBuilder\n{\n    public static function build($container, $params, $config)\n    {\n        $foo = new Foo(new Bar);\n        // ... autres initialisations ...\n        return $foo;\n    }\n}\n\n$container->set('Foo', ['app\\helper\\FooBuilder', 'build']);\n\n$foo = $container->get('Foo');\n```\n\nEn procédant de cette manière, la personne qui désire configurer la classe `Foo` n'a plus besoin de savoir comment elle est construite.\n\n\nEnregistrement des dépendances <span id=\"registering-dependencies\"></span>\n------------------------------\n\nVous pouvez utiliser [[yii\\di\\Container::set()]] pour enregistrer les dépendances. L'enregistrement requiert un nom de dépendance et une définition de dépendance. Un nom de dépendance peut être un nom de classe, un nom d'interface, ou un nom d'alias ; et une définition de dépendance peut être une nom de classe, un tableau de configuration, ou une fonction de rappel PHP.\n\n```php\n$container = new \\yii\\di\\Container;\n\n// enregistre un nom de classe tel quel. Cela peut être sauté. \n$container->set('yii\\db\\Connection');\n\n// enregistre une interface\n// Lorsqu'une classe dépend d'une interface, la classe correspondante\n// est instanciée en tant qu'objet dépendant\n$container->set('yii\\mail\\MailInterface', 'yii\\swiftmailer\\Mailer');\n\n// enregistre un nom d'alias. Vous pouvez utiliser $container->get('foo')\n// pour créer une instance de Connection\n$container->set('foo', 'yii\\db\\Connection');\n\n// enregistre une classe avec une configuration. La configuration\n// est appliquée lorsque la classe est instanciée par  get()\n$container->set('yii\\db\\Connection', [\n    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n]);\n\n// enregistre un nom d'alias avec une configuration de classe\n// Dans ce cas, un élément \"class\" est requis pour spécifier la classe\n$container->set('db', [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n]);\n\n// enregistre une fonction de rappel PHP \n// La fonction de rappel est exécutée à chaque fois que $container->get('db') est appelée\n$container->set('db', function ($container, $params, $config) {\n    return new \\yii\\db\\Connection($config);\n});\n\n// enregistre une interface de composant \n// $container->get('pageCache') retourne la même instance à chaque fois qu'elle est appelée\n$container->set('pageCache', new FileCache);\n```\n\n> Tip: si un nom de dépendance est identique à celui de la définition de dépendance correspondante, vous n'avez pas besoin de l'enregistrer dans le conteneur d'injection de dépendances.\n\nUne dépendance enregistrée via `set()` génère une instance à chaque fois que la dépendance est nécessaire. Vous pouvez utiliser [[yii\\di\\Container::setSingleton()]] pour enregistrer une dépendance qui ne génère qu'une seule instance :\n\n```php\n$container->setSingleton('yii\\db\\Connection', [\n    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n]);\n```\n\n\nRésolution des dépendances <span id=\"resolving-dependencies\"></span>\n--------------------------\n\nUne fois que vous avez enregistré des dépendances, vous pouvez utiliser le conteneur d'injection de dépendances pour créer de nouveau objets, et le conteneur résout automatiquement les dépendances en les instanciant et en les injectant dans les nouveaux objets. Le résolution des dépendances est récursive, ce qui signifie que si une dépendance a d'autres dépendances, ces dépendances sont aussi résolue automatiquement. \n\nVous pouvez utiliser [[yii\\di\\Container::get()]] soit pour créer, soit pour obtenir une instance d'un objet. La méthode accepte un nom de dépendance qui peut être un nom de classe, un nom d'interface ou un nom d'alias. Le nom de dépendance, peut être enregistré [[yii\\di\\Container::set()|set()]] \nou [[yii\\di\\Container::setSingleton()|setSingleton()]]. En option, vous pouvez fournir une liste de paramètres du constructeur de la classe et une [configuration](concept-configurations.md) pour configurer l'objet nouvellement créé. Par exemple :\n\n```php\n// \"db\" est un nom d'alias enregistré préalablement\n$db = $container->get('db');\n\n// équivalent à : $engine = new \\app\\components\\SearchEngine($apiKey, $apiSecret, ['type' => 1]);\n$engine = $container->get('app\\components\\SearchEngine', [$apiKey, $apiSecret], ['type' => 1]);\n```\n\nEn coulisses, le conteneur d'injection de dépendances ne fait rien de plus que de créer l'objet. Le conteneur inspecte d'abord le constructeur de la classe pour trouver les classes dépendantes ou les noms d'interface et résout ensuite ces dépendances récursivement. \n\nLe code suivant montre un exemple plus sophistiqué. La classe `UserLister` dépend d'un objet implémentant l'interface `UserFinderInterface` ; la classe `UserFinder` implémente cet interface et dépend de l'objet `Connection`. Toutes ces dépendances sont déclarées via l'allusion au type des paramètres du constructeur de la classe. Avec l'enregistrement des dépendances de propriétés, le conteneur d'injection de dépendances est capable de résoudre ces dépendances automatiquement et de créer une nouvelle instance de `UserLister` par un simple appel à `get('userLister')`.\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\BaseObject;\nuse yii\\db\\Connection;\nuse yii\\di\\Container;\n\ninterface UserFinderInterface\n{\n    function findUser();\n}\n\nclass UserFinder extends BaseObject implements UserFinderInterface\n{\n    public $db;\n\n    public function __construct(Connection $db, $config = [])\n    {\n        $this->db = $db;\n        parent::__construct($config);\n    }\n\n    public function findUser()\n    {\n    }\n}\n\nclass UserLister extends BaseObject\n{\n    public $finder;\n\n    public function __construct(UserFinderInterface $finder, $config = [])\n    {\n        $this->finder = $finder;\n        parent::__construct($config);\n    }\n}\n\n$container = new Container;\n$container->set('yii\\db\\Connection', [\n    'dsn' => '...',\n]);\n$container->set('app\\models\\UserFinderInterface', [\n    'class' => 'app\\models\\UserFinder',\n]);\n$container->set('userLister', 'app\\models\\UserLister');\n\n$lister = $container->get('userLister');\n\n// qui est équivalent à :\n\n$db = new \\yii\\db\\Connection(['dsn' => '...']);\n$finder = new UserFinder($db);\n$lister = new UserLister($finder);\n```\n\n\nUtilisation pratique <span id=\"practical-usage\"></span>\n--------------------\n\nYii crée un conteneur d'injection de dépendances lorsque vous incluez le fichier `Yii.php` dans le [script d'entrée](structure-entry-scripts.md) de votre application. Le conteneur d'injection de dépendances est accessible via [[Yii::$container]]. Lorsque vous appelez [[Yii::createObject()]], la méthode appelle en réalité la méthode [[yii\\di\\Container::get()|get()]] du conteneur pour créer le nouvel objet. Comme c'est dit plus haut, le conteneur d'injection de dépendances résout automatiquement les dépendances (s'il en existe) et les injecte dans l'objet obtenu. Parce que Yii utilise [[Yii::createObject()]] dans la plus grande partie du code de son noyau pour créer de nouveaux objets, cela signifie que vous pouvez personnaliser ces objets globalement en utilisant [[Yii::$container]].\n\nPar exemple, personnalisons globalement le nombre de boutons de pagination par défaut de l'objet graphique [[yii\\widgets\\LinkPager]] :\n\n```php\n\\Yii::$container->set('yii\\widgets\\LinkPager', ['maxButtonCount' => 5]);\n```\n\nMaintenant, si vous utilisez l'objet graphique dans une vue avec le code suivant, la propriété `maxButtonCount` est initialisée à la valeur 5 au lieu de la valeur par défaut 10 qui est définie dans la classe.\n```php\necho \\yii\\widgets\\LinkPager::widget();\n```\n\nVous pouvez encore redéfinir la valeur définie par le conteneur d'injection de dépendances via :\n\n```php\necho \\yii\\widgets\\LinkPager::widget(['maxButtonCount' => 20]);\n```\n\n> Tip: peu importe de quel type de valeur il s'agit, elle est redéfinie, c'est pourquoi vous devez vous montrer prudent avec les tableaux d'options. Ils ne sont pas fusionnés. \n\nUn autre exemple est de profiter de l'injection automatique par le constructeur du conteneur d'injection de dépendances. Supposons que votre classe de contrôleur dépende de quelques autres objets, comme un service de réservation d'hôtel. Vous pouvez déclarer la dépendance via un paramètre de constructeur et laisser le conteneur d'injection de dépendances la résoudre pour vous. \n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\nuse app\\components\\BookingInterface;\n\nclass HotelController extends Controller\n{\n    protected $bookingService;\n\n    public function __construct($id, $module, BookingInterface $bookingService, $config = [])\n    {\n        $this->bookingService = $bookingService;\n        parent::__construct($id, $module, $config);\n    }\n}\n```\n\nSi vous accédez au contrôleur à partir du navigateur, vous verrez un message d'erreur se plaignant que l'interface `BookingInterface` ne peut pas être instanciée. Cela est dû au fait que vous devez dire au conteneur d'injection de dépendances comment s'y prendre avec cette dépendance :\n\n```php\n\\Yii::$container->set('app\\components\\BookingInterface', 'app\\components\\BookingService');\n```\n\nMaintenant, si vous accédez à nouveau au contrôleur, une instance de `app\\components\\BookingService` est créée et injectée en tant que troisième paramètre du constructeur. \n\nUtilisation pratique avancée <span id=\"advanced-practical-usage\"></span>\n---------------\n\nSupposons que nous travaillions sur l'API de l'application et ayons :S\n\n- la classe `app\\components\\Request` qui étende `yii\\web\\Request` et fournisse une fonctionnalité additionnelle, \n- la classe `app\\components\\Response` qui étende `yii\\web\\Response` et devrait avoir une propriété `format` définie à `json` à la création,\n- des classes `app\\storage\\FileStorage` et `app\\storage\\DocumentsReader` qui mettent en œuvre une certaine logique pour travailler sur des documents qui seraient situés dans un dossier :\n  \n  \n  ```php\n  class FileStorage\n  {\n      public function __construct($root) {\n          // whatever\n      }\n  }\n  \n  class DocumentsReader\n  {\n      public function __construct(FileStorage $fs) {\n          // whatever\n      }\n  }\n  ```\n\nIl est possible de configurer de multiples définitions à la fois, en passant un tableau de configurations à la méthode  \n[[yii\\di\\Container::setDefinitions()|setDefinitions()]] ou à la méthode [[yii\\di\\Container::setSingletons()|setSingletons()]].\nEn itérant sur le tableau de configuration, les méthodes appellent [[yii\\di\\Container::set()|set()]]\nou [[yii\\di\\Container::setSingleton()|setSingleton()]] respectivement pour chacun des items.\n\nLe format du tableau de  configurations est :\n\n - `key`: nom de classe, nom d'interface ou alias. La clé est passée à la méthode\n [[yii\\di\\Container::set()|set()]] comme premier argument `$class`.\n - `value`: la définition associée à `$class`. Les valeurs possibles sont décrites dans la documentation [[yii\\di\\Container::set()|set()]]\n du paramètre `$definition`. Est passé à la méthode [[set()]] comme deuxième argument `$definition`.\n\nPar exemple, configurons notre conteneur pour répondre aux exigences mentionnées précédemment :\n\n```php\n$container->setDefinitions([\n    'yii\\web\\Request' => 'app\\components\\Request',\n    'yii\\web\\Response' => [\n        'class' => 'app\\components\\Response',\n        'format' => 'json'\n    ],\n    'app\\storage\\DocumentsReader' => function ($container, $params, $config) {\n        $fs = new app\\storage\\FileStorage('/var/tempfiles');\n        return new app\\storage\\DocumentsReader($fs);\n    }\n]);\n\n$reader = $container->get('app\\storage\\DocumentsReader'); \n// Crée un objet DocumentReader avec ses dépendances tel que décrit dans la configuration.\n```\n\n> Tip: le conteneur peut être configuré dans le style déclaratif en utilisant la configuration de l'application depuis la version 2.0.11. \nConsultez la sous-section [Configurations des applications](concept-configurations.md#application-configurations) de l'article du guide  [Configurations](concept-configurations.md).\n\nTout fonctionne, mais au cas où, nous devons créer une classe  `DocumentWriter`, nous devons copier-coller la ligne qui crée un objet  `FileStorage`, ce qui n'est pas la manière la plus élégante, c'est évident. \n\n\nComme cela est décrit à la sous-section [Résolution des dépendances](#resolving-dependencies) subsection, [[yii\\di\\Container::set()|set()]]\net [[yii\\di\\Container::setSingleton()|setSingleton()]] peuvent facultativement des paramètres du constructeur de dépendances en tant que troisième argument. Pour définir les paramètres du constructeur, vous pouvez utiliser le format de tableau de configuration suivant :\n\n - `key`: nom de classe, nom d'interface ou alias. La clé est passée à la méthode \n [[yii\\di\\Container::set()|set()]] comme premier argument `$class`.\n - `value`: un tableau de deux éléments. Le premier élément est passé à la méthode [[yii\\di\\Container::set()|set()]] comme deuxième argument `$definition`, le second — comme `$params`.\n\nModifions notre exemple :\n\n```php\n$container->setDefinitions([\n    'tempFileStorage' => [ // we've created an alias for convenience\n        ['class' => 'app\\storage\\FileStorage'],\n        ['/var/tempfiles'] // pourrait être extrait de certains fichiers de configuration\n    ],\n    'app\\storage\\DocumentsReader' => [\n        ['class' => 'app\\storage\\DocumentsReader'],\n        [Instance::of('tempFileStorage')]\n    ],\n    'app\\storage\\DocumentsWriter' => [\n        ['class' => 'app\\storage\\DocumentsWriter'],\n        [Instance::of('tempFileStorage')]\n    ]\n]);\n\n$reader = $container->get('app\\storage\\DocumentsReader'); \n// Se comporte exactement comme l'exemple précédent\n```\n\nVous noterez la notation `Instance::of('tempFileStorage')`. cela siginifie que  le [[yii\\di\\Container|Container]] fournit implicitement une dépendance enregistrée avec le nom de  `tempFileStorage` et la passe en tant que premier argument du constructeur\nof `app\\storage\\DocumentsWriter`.\n\n> Note: [[yii\\di\\Container::setDefinitions()|setDefinitions()]] and [[yii\\di\\Container::setSingletons()|setSingletons()]]\n  methods are available since version 2.0.11.\n  \nUne autre étape de l'optimisation de la configuration est d'enregistrer certaines dépendances  sous forme de singletons. \nUne dépendance enregistrée via [[yii\\di\\Container::set()|set()]] est instanciée à chaque fois qu'on en a besoin.\nCertaines classes ne changent pas l'état au moment de l'exécution, par conséquent elles peuvent être enregistrées sous forme de singletons afin d'augmenter la performance de l'application.\n\nUn bon exemple serait la classe `app\\storage\\FileStorage`, qui effectue certaines opérations sur le système de fichiers avec une API simple (p. ex. `$fs->read()`, `$fs->write()`). Ces opération ne changent pas l'état interne de la classe, c'est pourquoi nous pouvons créer son instance une seule fois et l'utiliser de multiples fois.\n\n```php\n$container->setSingletons([\n    'tempFileStorage' => [\n        ['class' => 'app\\storage\\FileStorage'],\n        ['/var/tempfiles']\n    ],\n]);\n\n$container->setDefinitions([\n    'app\\storage\\DocumentsReader' => [\n        ['class' => 'app\\storage\\DocumentsReader'],\n        [Instance::of('tempFileStorage')]\n    ],\n    'app\\storage\\DocumentsWriter' => [\n        ['class' => 'app\\storage\\DocumentsWriter'],\n        [Instance::of('tempFileStorage')]\n    ]\n]);\n\n$reader = $container->get('app\\storage\\DocumentsReader');\n```\n\n\nÀ quel moment enregistrer les dépendances <span id=\"when-to-register-dependencies\"></span>\n-----------------------------------------\n\nComme les dépendances sont nécessaires lorsque de nouveaux objets sont créés, leur enregistrement doit être fait aussi tôt que possible. Les pratiques recommandées sont : \n\n* Si vous êtes le développeur d'une application, vous pouvez enregistrer les dépendances dans le [script d'entrée](structure-entry-scripts.md) de votre application ou dans un script qui est inclus par le script d'entrée. \n* Si vous êtes le développeur d'une [extension](structure-extensions.md) distribuable, vous pouvez enregistrer les dépendances dans la classe d'amorçage de l'extension. \n\n\nRésumé <span id=\"summary\"></span>\n-------\n\nL'injection de dépendances et le [localisateur de services](concept-service-locator.md) sont tous deux des modèles de conception populaires qui permettent des construire des logiciels d'une manière faiblement couplée et plus testable. Nous vous recommandons fortement de lire [l'article de Martin](https://martinfowler.com/articles/injection.html) pour acquérir une compréhension plus profonde de l'injection de dépendances et du localisateur de services. \n\nYii implémente son [localisateur de services](concept-service-locator.md) par dessus le conteneur d'injection de dépendances. Lorsqu'un localisateur de services essaye de créer une nouvelle instance d'un objet, il appelle le conteneur d'injection de dépendances. Ce dernier résout les dépendances automatiquement comme c'est expliqué plus haut.\n\n"
  },
  {
    "path": "docs/guide-fr/concept-events.md",
    "content": "Événements\n==========\n\nLes événement vous permettent d'injecter du code personnalisé dans le code existant à des points précis de son exécution. Vous pouvez attacher du code personnalisé à un événement de façon à ce que, lorsque l'événement est déclenché, le code s'exécute automatiquement. Par exemple, un objet serveur de courriel  peut déclencher un événement `messageSent` (message envoyé) quand il réussit à envoyer un message. Si vous voulez conserver une trace des messages dont l'envoi a réussi, vous pouvez simplement attacher le code de conservation de la trace à l'événement  `messageSent`.\n\nYii introduit une classe de base appelée [[yii\\base\\Component]] pour prendre en charge les événements. Si une classe a besoin de déclencher des événements, elle doit étendre la classe [[yii\\base\\Component]], ou une de ses classes filles. \n\n\nGestionnaires d'événements <span id=\"event-handlers\"></span>\n--------------------------\n\nUn gestionnaire d'événement est une  [fonction de rappel PHP](https://www.php.net/manual/fr/language.types.callable.php) qui est exécutée lorsque l'événement à laquelle elle est attachée est déclenché. Vous pouvez utiliser n'importe laquelle des fonctions de rappel suivantes :\n\n- une fonction PHP globale spécifiée sous forme de chaîne de caractères (sans les parenthèses) p. ex., `'trim'` ;\n- une méthode d'objet spécifiée sous forme de tableau constitué d'un nom d'objet et d'un nom de méthode sous forme de chaîne de caractères (sans les parenthèses), p. ex., `[$object, 'methodName']`;\n- une méthode d'une classe statique spécifiée sous forme de tableau constitué d'un nom de classe et d'un nom de méthode sous forme de chaîne de caractères (sans les parenthèses), p. ex., `['ClassName', 'methodName']`;\n- une fonction anonyme p. ex., `function ($event) { ... }`.\n\nLa signature d'un gestionnaire d'événement est :\n\n```php\nfunction ($event) {\n    // $event est un objet de la classe  yii\\base\\Event ou des ses classes filles\n}\n```\n\nVia le paramètre `$event`, un gestionnaire d'événement peut obtenir l'information suivante sur l'événement qui vient de se produire : \n\n- le [[yii\\base\\Event::name|nom de l'événement]];\n- l'[[yii\\base\\Event::sender|émetteur de l'événement]]: l'objet dont la méthode  `trigger()` a été appelée ;\n- les [[yii\\base\\Event::data|données personnalisées]]: les données fournies lorsque le gestionnaire d'événement est attaché (les explications arrivent bientôt).\n\n\nAttacher des gestionnaires d'événements <span id=\"attaching-event-handlers\"></span>\n---------------------------------------\n\nVous pouvez attacher un gestionnaire d'événement en appelant la méthode [[yii\\base\\Component::on()]] du composant. Par exemple :\n\n```php\n$foo = new Foo();\n\n// le gestionnaire est une fonction globale\n$foo->on(Foo::EVENT_HELLO, 'function_name');\n\n// le gestionnaire est une méthode d'objet\n$foo->on(Foo::EVENT_HELLO, [$object, 'methodName']);\n\n// le gestionnaire est une méthode d'une classe statique\n$foo->on(Foo::EVENT_HELLO, ['app\\components\\Bar', 'methodName']);\n\n// le gestionnaire est un fonction anonyme\n$foo->on(Foo::EVENT_HELLO, function ($event) {\n    // event handling logic\n});\n```\n\nVous pouvez aussi attacher des gestionnaires d'événements via les [configurations](concept-configurations.md). Pour plus de détails, reportez-vous à la section [Configurations](concept-configurations.md#configuration-format).\n\n\nLorsque vous attachez un gestionnaire d'événement, vous pouvez fournir des données additionnelles telles que le troisième paramètre de [[yii\\base\\Component::on()]]. Les données sont rendues disponibles au gestionnaire lorsque l'événement est déclenché et que le gestionnaire est appelé. Par exemple :\n\n```php\n// Le code suivant affiche  \"abc\" lorsque l'événement est déclenché\n// parce que  $event->data contient les données passées en tant que troisième argument à la méthode \"on\"\n$foo->on(Foo::EVENT_HELLO, 'function_name', 'abc');\n\nfunction function_name($event) {\n    echo $event->data;\n}\n```\n\nOrdre des gestionnaires d'événements\n------------------------------------\n\nVous pouvez attacher un ou plusieurs gestionnaires à un seul événement. Lorsqu'un événement est déclenché, les gestionnaires attachés sont appelés dans l'ordre dans lequel ils ont été attachés à l'événement. Si un gestionnaire a besoin d'arrêter l'appel des gestionnaires qui viennent après lui, il doit définir la propriété [[yii\\base\\Event::handled (géré)]] du paramètre `$event` à `true`:\n\n```php\n$foo->on(Foo::EVENT_HELLO, function ($event) {\n    $event->handled = true;\n});\n```\n\nPar défaut, un gestionnaire nouvellement attaché est ajouté à la file des gestionnaires de l'événement. En conséquence, le gestionnaire est appelé en dernier lorsque l'événement est déclenché. Pour insérer un événement nouvellement attaché en tête de file pour qu'il soit appelé le premier, vous devez appeler [[yii\\base\\Component::on()]], en lui passant `false` pour le quatrième paramètre `$append`:\n\n```php\n$foo->on(Foo::EVENT_HELLO, function ($event) {\n    // ...\n}, $data, false);\n```\n\nDéclenchement des événements <span id=\"triggering-events\"></span>\n----------------------------\n\nLes événements sont déclenchés en appelant la méthode [[yii\\base\\Component::trigger()]]. La méthode requiert un  *nom d'événement* et, en option, un objet événement qui décrit les paramètres à passer aux gestionnaires de cet événement. Par exemple :\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Component;\nuse yii\\base\\Event;\n\nclass Foo extends Component\n{\n    const EVENT_HELLO = 'hello';\n\n    public function bar()\n    {\n        $this->trigger(self::EVENT_HELLO);\n    }\n}\n```\nAvec le code précédent, tout appel à  `bar()` déclenche un événement nommé `hello`.\n\n> Tip: il est recommandé d'utiliser des constantes de classe pour représenter les noms d'événement. Dans l'exemple qui précède, la constante `EVENT_HELLO` représente l'événement `hello`. Cette approche procure trois avantages. Primo, elle évite les erreurs de frappe. Secundo, elle permet aux événements d'être reconnus par le mécanisme d'auto-complètement des EDI. Tertio, vous pouvez dire quels événements sont pris en charge par une classe en vérifiant la déclaration de ses constantes. \n\nParfois, lors du déclenchement d'un événement, vous désirez passer des informations additionnelles aux gestionnaires de cet événement. Par exemple, un serveur de courriels peut souhaiter passer les informations sur le message aux gestionnaires de l'événement `messageSent` pour que ces derniers soient informés de certaines particularités des messages envoyés. Pour ce faire, vous pouvez fournir un objet événement comme deuxième paramètre de la méthode [[yii\\base\\Component::trigger()]]. L'objet événement doit simplement être une instance de la classe [[yii\\base\\Event]] ou d'une de ses classes filles. Par exemple :\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Component;\nuse yii\\base\\Event;\n\nclass MessageEvent extends Event\n{\n    public $message;\n}\n\nclass Mailer extends Component\n{\n    const EVENT_MESSAGE_SENT = 'messageSent';\n\n    public function send($message)\n    {\n        // ...sending $message...\n\n        $event = new MessageEvent;\n        $event->message = $message;\n        $this->trigger(self::EVENT_MESSAGE_SENT, $event);\n    }\n}\n```\n\nLorsque la méthode [[yii\\base\\Component::trigger()]] est appelée, elle appelle tous les gestionnaires attachés à l'événement nommé. \n\n\nDétacher des gestionnaires d'événements <span id=\"detaching-event-handlers\"></span>\n---------------------------------------\n\nPour détacher un gestionnaire d'un événement, appelez la méthode [[yii\\base\\Component::off()]]. Par exemple :\n\n```php\n// le gestionnaire est une fonction globale\n$foo->off(Foo::EVENT_HELLO, 'function_name');\n\n// le gestionnaire est une méthode d'objet\n$foo->off(Foo::EVENT_HELLO, [$object, 'methodName']);\n\n// le gestionnaire est une méthode d'une classe statique \n$foo->off(Foo::EVENT_HELLO, ['app\\components\\Bar', 'methodName']);\n\n// le gestionnaire est une fonction anonyme\n$foo->off(Foo::EVENT_HELLO, $anonymousFunction);\n```\n\nNotez qu'en général, vous ne devez pas essayer de détacher une fonction anonyme sauf si vous l'avez stockée quelque part lorsque vous l'avez attachée à un événement. Dans l'exemple ci-dessus, on suppose que la fonctions anonyme est stockée dans une variable nommée  `$anonymousFunction`.\n\nPour détacher *tous* les gestionnaires d'un événement, appelez simplement la méthode [[yii\\base\\Component::off()]] sans le deuxième paramètre :\n\n```php\n$foo->off(Foo::EVENT_HELLO);\n```\n\n\nGestionnaire d'événement au niveau de la classe <span id=\"class-level-event-handlers\"></span>\n-----------------------------------------------\n\nLes sections précédent décrivent comment attacher un gestionnaire à un événement au *niveau d'une instance*. Parfois, vous désirez répondre à un événement déclenché par *chacune des* instances d'une classe plutôt que par une instance spécifique. Au lieu d'attacher l'événement à chacune des instances, vous pouvez attacher le gestionnaire au *niveau de la classe* en appelant la méthode statique [[yii\\base\\Event::on()]].\n\nPar exemple, un objet [Active Record](db-active-record.md) déclenche un événement  [[yii\\db\\BaseActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]]\nà chaque fois qu'il insère un nouvel enregistrement dans la base de données. Afin de suivre les insertions faites par tous les objets [Active Record](db-active-record.md), vous pouvez utiliser le code suivant :\n\n```php\nuse Yii;\nuse yii\\base\\Event;\nuse yii\\db\\ActiveRecord;\n\nEvent::on(ActiveRecord::class, ActiveRecord::EVENT_AFTER_INSERT, function ($event) {\n    Yii::debug(get_class($event->sender) . ' is inserted');\n});\n```\n\nLe gestionnaire d'événement est invoqué à chaque fois qu'une instance de la classe [[yii\\db\\ActiveRecord|ActiveRecord]], ou d'une de ses classes filles, déclenche l'événement [[yii\\db\\BaseActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]]. Dans le gestionnaire, vous pouvez obtenir l'objet qui a déclenché l'événement via `$event->sender`.\n\nLorsqu'un objet déclenche un événement, il commence par appeler les gestionnaires attachés au niveau de l'instance, puis les gestionnaires attachés au niveau de la classe. \n\nVous pouvez déclencher un événement au *niveau de la classe* en appelant la méthode statique [[yii\\base\\Event::trigger()]]. Un événement déclenché au niveau de la classe n'est associé à aucun objet en particulier. En conséquence, il provoque l'appel des gestionnaires attachés au niveau de la classe seulement. Par exemple :\n\n```php\nuse yii\\base\\Event;\n\nEvent::on(Foo::class, Foo::EVENT_HELLO, function ($event) {\n    var_dump($event->sender);  // displays \"null\"\n});\n\nEvent::trigger(Foo::class, Foo::EVENT_HELLO);\n```\n\nNotez que, dans ce cas, `$event->sender` fait référence au nom de la classe qui a déclenché l'événement plutôt qu'à une instance de classe.\n\n> Note: comme les gestionnaires attachés au niveau de la classe répondent aux événements déclenchés par n'importe quelle instance de cette classe, ou de ses classes filles, vous devez utiliser cette fonctionnalité avec précaution, en particulier si la classe est une classe de bas niveau comme la classe [[yii\\base\\BaseObject]].\n\nPour détacher un gestionnaire attaché au niveau de la classe, appelez  [[yii\\base\\Event::off()]]. Par exemple :\n\n```php\n// détache $handler\nEvent::off(Foo::class, Foo::EVENT_HELLO, $handler);\n\n// détache tous les gestionnaires de Foo::EVENT_HELLO\nEvent::off(Foo::class, Foo::EVENT_HELLO);\n```\n\n\nÉvénement utilisant des interfaces <span id=\"interface-level-event-handlers\"></span>\n----------------------------------\n\nIl y a encore une manière plus abstraite d'utiliser les événements. Vous pouvez créer une interface séparée pour un événement particulier et l'implémenter dans des classes dans lesquelles vous en avez besoin. \n\nPar exemple, vous pouvez créer l'interface suivante :\n\n```php\nnamespace app\\interfaces;\n\ninterface DanceEventInterface\n{\n    const EVENT_DANCE = 'dance';\n}\n```\n\nEt ajouter deux classes qui l'implémente :\n\n```php\nclass Dog extends Component implements DanceEventInterface\n{\n    public function meetBuddy()\n    {\n        echo \"Woof!\";\n        $this->trigger(DanceEventInterface::EVENT_DANCE);\n    }\n}\n\nclass Developer extends Component implements DanceEventInterface\n{\n    public function testsPassed()\n    {\n        echo \"Yay!\";\n        $this->trigger(DanceEventInterface::EVENT_DANCE);\n    }\n}\n```\n\nPour gérer l'événement `EVENT_DANCE` déclenché par n'importe laquelle de ces classes, appelez [[yii\\base\\Event::on()|Event::on()]] et passez-lui le nom de l'interface comme premier argument :\n\n```php\nEvent::on('app\\interfaces\\DanceEventInterface', DanceEventInterface::EVENT_DANCE, function ($event) {\n    Yii::debug(get_class($event->sender) . ' just danced'); // Will log that Dog or Developer danced\n});\n```\n\nVous pouvez déclencher l'événement de ces classes :\n\n```php\n// trigger event for Dog class\nEvent::trigger(Dog::class, DanceEventInterface::EVENT_DANCE);\n\n// trigger event for Developer class\nEvent::trigger(Developer::class, DanceEventInterface::EVENT_DANCE);\n```\n\nNotez bien que vous ne pouvez pas déclencher l'événement de toutes les classes qui implémentent l'interface :,\n\n```php\n// NE FONCTIONNE PAS\nEvent::trigger('app\\interfaces\\DanceEventInterface', DanceEventInterface::EVENT_DANCE);\n```\n\nPour détacher le gestionnaire d'événement, appelez [[yii\\base\\Event::off()|Event::off()]]. Par exemple :\n\n```php\n// détache $handler\nEvent::off('app\\interfaces\\DanceEventInterface', DanceEventInterface::EVENT_DANCE, $handler);\n\n// détache tous les gestionnaires de DanceEventInterface::EVENT_DANCE\nEvent::off('app\\interfaces\\DanceEventInterface', DanceEventInterface::EVENT_DANCE);\n```\n\n\nÉvénements globaux <span id=\"global-events\"></span>\n------------------\n\nYii prend en charge ce qu'on appelle les *événements globaux*, qui est une astuce basée sur le mécanisme des événements décrit ci-dessus. L'événement global requiert un singleton accessible globalement tel que l'instance de l'[application](structure-applications.md) elle-même.\n\nPour créer l'événement global, un émetteur d'événement appelle la méthode `trigger()`  du singleton pour déclencher l'événement au lieu d'appeler la méthode `trigger()` propre à l'émetteur. De façon similaire, les gestionnaires d'événement sont attachés à l'événement sur le singleton. Par exemple :\n\n```php\nuse Yii;\nuse yii\\base\\Event;\nuse app\\components\\Foo;\n\nYii::$app->on('bar', function ($event) {\n    echo get_class($event->sender);  // affiche \"app\\components\\Foo\"\n});\n\nYii::$app->trigger('bar', new Event(['sender' => new Foo]));\n```\n\nUn avantage de l'utilisation d'événement globaux est que vous n'avez pas besoin d'un objet lorsque vous attachez un gestionnaire à l'événement qui est déclenché par l'objet. Au lieu de cela, vous attachez le gestionnaire et déclenchez l'événement via le singleton (p. ex. l'instance d'application). \n\nNéanmoins, parce que l'espace de noms des événements globaux est partagé par toutes les parties, vous devez nommer les événements globaux avec prudence, par exemple en introduisant une sorte d'espace de noms (p. ex. \"frontend.mail.sent\", \"backend.mail.sent\").\n\nÉvénements génériques <span id=\"wildcard-events\"></span>\n---------------------\n\nDepuis la version 2.0.14, vous pouvez définir un gestionnaire d'événement pour de multiples événement correspondant à un motif générique. \nPar exemple:\n\n```php\nuse Yii;\n\n$foo = new Foo();\n\n$foo->on('foo.event.*', function ($event) {\n    // déclenché pour tout événement dont le nom commence par 'foo.event.'\n    Yii::debug('trigger event: ' . $event->name);\n});\n```\n\nLes motifs génériques peuvent être utilisés pour des événements au niveau de la classe. Par exemple : \n\n```php\nuse yii\\base\\Event;\nuse Yii;\n\nEvent::on('app\\models\\*', 'before*', function ($event) {\n    // déclenché pour toute classe de l'espace de noms  'app\\models' pour tout événement dont le nom commence par 'before'\n    Yii::debug('trigger event: ' . $event->name . ' for class: ' . get_class($event->sender));\n});\n```\n\nCela vous permet d'attraper tous les événement de l'application par un unique gestionnaire en utilisant le code suivant :\n\n```php\nuse yii\\base\\Event;\nuse Yii;\n\nEvent::on('*', '*', function ($event) {\n    // déclenché pour tout événement de n'importe quelle classe\n    Yii::debug('trigger event: ' . $event->name);\n});\n```\n\n> Note: l'utilisation de motifs génériques pour la définition des gestionnaires d'événement peut réduire la performance de l'application . Il vaut mieux l'éviter si possible.\n\nAfin de détacher un gestionnaire d'événement spécifié par un motif générique, vous devez répéter le même motif en invoquant \n[[yii\\base\\Component::off()]] ou [[yii\\base\\Event::off()]]. Soyez conscient que le passage d'un motif générique lors du détachement d'un gestionnaire d'événement ne détache que le gestionnaire attaché avec ce motif, tandis que les gestionnaires attachés par des noms réguliers d'événement resteront attachés même si leur nom correspond au motif. Par exemple :\n\n```php\nuse Yii;\n\n$foo = new Foo();\n\n// attache un gestionnaire de façon régulière\n$foo->on('event.hello', function ($event) {\n    echo 'direct-handler'\n});\n\n// attache un gestionnaire par un motif générique\n$foo->on('*', function ($event) {\n    echo 'wildcard-handler'\n});\n\n// ne détache que le gestionnaire attaché par le motif générique\n$foo->off('*');\n\n$foo->trigger('event.hello'); // outputs: 'direct-handler'\n```\n"
  },
  {
    "path": "docs/guide-fr/concept-properties.md",
    "content": "Propriétés\n==========\n\nEn PHP, les variables membres des classes sont aussi appelées *propriétés*. Ces variables font partie de la définition de la classe  et sont utilisées pour représenter l'état d'une instance de cette classe (c.-à-d. à différentier une instance de la classe d'une autre). En pratique, vous désirez souvent gérer la lecture et l'écriture de ces propriété d'une manière particulière. Par exemple, vous pouvez désirer qu'une chaîne de caractères soit toujours nettoyée avant de l'assigner à une propriété `label`. Vous *pouvez* utiliser le code suivant pour arriver à cette fin :\n\n```php\n$object->label = trim($label);\n```\n\nLe revers du code ci-dessus est que vous devez appeler `trim()` partout ou vous voulez définir la propriété `label`. Si, plus tard, la propriété `label` devient sujette à de nouvelles exigences, telles que la première lettre doit être une capitale, vous auriez à modifier toutes les parties de code  qui assignent une valeur à la propriété `label`. La répétition de code conduit à des bogues, et c'est une pratique courante de l'éviter autant que faire se peut.\n\nPour résoudre ce problème, Yii introduit une classe de base nommée [[yii\\base\\BaseObject]] qui prend en charge la définition de propriétés sur la base de méthodes d'obtention (*getter*) et de méthode d'assignation (*setters*). Si une classe a besoin de cette fonctionnalité, il suffit qu'elle étende la classe[[yii\\base\\BaseObject]], ou une de ses classes filles.\n\n> Info: presque toutes les classes du noyau du framework Yii étendent la classe [[yii\\base\\BaseObject]] ou une de ses classes filles. Cela veut dire, que chaque fois que vous trouvez une méthode d'obtention ou d'assignation dans une classe du noyau, vous pouvez l'utiliser comme une propriété. \n \nUne méthode d'obtention est une méthode dont le nom commence par le mot `get` (obtenir) et une méthode d'assignation est une méthode dont le nom commence par le mot `set` (assigner, définir).  Le nom après les mots préfixes `get` ou `set` définit le nom d'une propriété. Par exemple, une méthode d'obtention `getLabel` et/ou une méthode d'assignation `setLabel` obtient et assigne, respectivement, une propriété nommée `label`, comme le montre le code suivant :\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\BaseObject;\n\nclass Foo extends BaseObject\n{\n    private $_label;\n\n    public function getLabel()\n    {\n        return $this->_label;\n    }\n\n    public function setLabel($value)\n    {\n        $this->_label = trim($value);\n    }\n}\n```\n\nPour être tout à fait exact, les méthodes d'obtention et d'assignation créent la propriété `label`, qui dans ce cas fait référence en interne à une propriété privée nommée `_label`.\n\nLes propriétés définies par les méthodes d'obtention et d'assignation peuvent être utilisées comme des variables membres de la classe. La différence principale est que, lorsqu'une telle propriété est lue, la méthode d'obtention correspondante est appelée ; lorsqu'une valeur est assignée à la propriété, la méthode d'assignation correspondante est appelée. Par exemple :\n\n```php\n// équivalent à $label = $object->getLabel();\n$label = $object->label;\n\n// équivalent à $object->setLabel('abc');\n$object->label = 'abc';\n```\n\nUne propriété définie par une méthode d'obtention (*getter*) sans méthode d'assignation (*setter*) est une propriété *en lecture seule*. Essayer d'assigner une valeur à une telle propriété provoque une exception [[yii\\base\\InvalidCallException|InvalidCallException]]. De façon similaire, une propriété définie par une méthode d'assignation sans méthode d'obtention est *en écriture seule*. Essayer de lire une telle propriété provoque une exception. Il n'est pas courant d'avoir des propriétés *en écriture seule*. \n\nIl existe plusieurs règles spéciales pour les propriétés définies via des méthodes d'obtention et d'assignation, ainsi que certaines limitations sur elles.\n\n* Le nom de telles propriétés sont *insensibles à la casse*. Par exemple,  `$object->label` et `$object->Label` sont identiques. Cela est dû au fait que le nom des méthodes dans PHP est insensible à la casse.\n* Si le nom d'une telle propriété est identique à celui d'une variable membre de la classe, le dernier prévaut. Par exemple, si la classe ci-dessus `Foo` possède une variable momée `label`, alors l'assignation `$object->label = 'abc'` affecte la *variable membre* `label` ; cette ligne ne fait pas appel à la méthode d'assignation `setLabel()`.\n* Ces propriétés ne prennent pas en charge la visibilité. Cela ne fait aucune différence pour les méthodes d'obtention et d'assignation qui définissent une propriété, que cette propriété soit publique, protégée ou privée.\n* Les propriétés peuvent uniquement être définies par des méthodes d'obtention et d'assignation *non-statiques*. Les méthodes statiques ne sont pas traitées de la même manière. \n* Un appel normal à la méthode `property_exists()` ne fonctionne pas pour déterminer des propriétés magiques. Vous devez appeler  [[yii\\base\\BaseObject::canGetProperty()|canGetProperty()]] ou [[yii\\base\\BaseObject::canSetProperty()|canSetProperty()]] respectivement.\n\nEn revenant au problème évoqué au début de ce guide, au lieu d'appeler `trim()` partout où une valeur est assignée à `label`, vous pouvez vous contenter d'appeler `trim()` dans la méthode d'assignation `setLabel()`. Et si une nouvelle exigence apparaît – comme celle de mettre la première lettre en capitale – la méthode  `setLabel()` peut être rapidement modifiée sans avoir à toucher à d'autres parties du code. Cet unique modification affecte l'ensemble des assignation de `label`.\n"
  },
  {
    "path": "docs/guide-fr/concept-service-locator.md",
    "content": "Localisateur de services\n========================\n\nUn localisateur de services est un objet que sait comment fournir toutes sortes de services (ou composants) dont une application peut avoir besoin. Dans le localisateur de services, chaque composant existe seulement sous forme d'une unique instance, identifiée de manière unique par un identifiant. Vous utilisez l'identifiant pour retrouver un composant du localisateur de services. \n\nDans Yii, un localisateur de service est simplement une instance de [[yii\\di\\ServiceLocator]] ou d'une de ses classes filles.\n\nLe localisateur de service le plus couramment utilisé dans Yii est l'objet *application*, auquel vous avez accès via `\\Yii::$app`. Les services qu'il procure, tels les composants `request`, `response` et `urlManager`,  sont appelés *composants d'application*. Vous pouvez configurer ces trois composants, ou même les remplacer facilement avec votre propre implémentation, en utilisant les fonctionnalités procurées par le localisateur de services. \n\nEn plus de l'objet application, chaque objet module est aussi un localisateur de services.\n\nPour utiliser un localisateur de service, la première étape est d'enregistrer le composant auprès de lui. Un composant peut être enregistré via la méthode [[yii\\di\\ServiceLocator::set()]]. Le code suivant montre différentes manières d'enregistrer des composants :\n\n```php\nuse yii\\di\\ServiceLocator;\nuse yii\\caching\\FileCache;\n\n$locator = new ServiceLocator;\n\n// enregistre \"cache\" en utilisant un nom de classe qui peut être utilisé pour créer un composant\n$locator->set('cache', 'yii\\caching\\ApcCache');\n\n// enregistre \"db\" en utilisant un tableau de configuration qui peut être utilisé pour créer un composant\n$locator->set('db', [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=localhost;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n]);\n\n// enregistre \"search\" en utilisant une fonction anonyme qui construit un composant\n$locator->set('search', function () {\n    return new app\\components\\SolrService;\n});\n\n// enregistre \"pageCache\" en utilisant un composant\n$locator->set('pageCache', new FileCache);\n```\n\nUne fois qu'un composant a été enregistré, vous pouvez y accéder via son identifiant, d'une des deux manières suivantes : \n\n```php\n$cache = $locator->get('cache');\n// ou en alternative \n$cache = $locator->cache;\n```\n\nComme montré ci-dessus, [[yii\\di\\ServiceLocator]] vous permet d'accéder à un composant comme à une propriété en utilisant l'identifiant du composant.\n\nLorsque vous accédez à un composant pour la première fois, [[yii\\di\\ServiceLocator]] utilise l'information d'enregistrement du composant pour créer une nouvelle instance du composant et la retourner. Par la suite, si on accède à nouveau au composant, le localisateur de service retourne la même instance.\n\nVous pouvez utiliser [[yii\\di\\ServiceLocator::has()]] pour savoir si un identifiant de composant a déjà été enregistré. Si vous appelez [[yii\\di\\ServiceLocator::get()]] avec un identifiant invalide, une exception est levée. \n\n\nComme les localisateurs de services sont souvent créés avec des [configurations](concept-configurations.md), une propriété accessible en écriture, et nommée [[yii\\di\\ServiceLocator::setComponents()|components]], est fournie. Cela vous permet de configurer et d'enregistrer plusieurs composants à la fois. Le code suivant montre un tableau de configuration qui peut être utilisé pour configurer un localisateur de services (p. ex. une [application](structure-applications.md)) avec les composants `db`, `cache`, `tz` et `search` :\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=demo',\n            'username' => 'root',\n            'password' => '',\n        ],\n        'cache' => 'yii\\caching\\ApcCache',\n        'tz' => function() {\n            return new \\DateTimeZone(Yii::$app->formatter->defaultTimeZone);\n        },\n        'search' => function () {\n            $solr = new app\\components\\SolrService('127.0.0.1');\n            // ... other initializations ...\n            return $solr;\n        },\n    ],\n];\n```\n\nDans ce qui précède, il y a une façon alternative de configurer le composant `search`. Au lieu d'écrire directement une fonction de rappel PHP qui construit une instance de `SolrService`, vous pouvez utiliser une méthode de classe statique pour retourner une telle fonction de rappel, comme c'est montré ci-dessous :\n\n```php\nclass SolrServiceBuilder\n{\n    public static function build($ip)\n    {\n        return function () use ($ip) {\n            $solr = new app\\components\\SolrService($ip);\n            // ... autres initialisations ...\n            return $solr;\n        };\n    }\n}\n\nreturn [\n    // ...\n    'components' => [\n        // ...\n        'search' => SolrServiceBuilder::build('127.0.0.1'),\n    ],\n];\n```\n\nCette approche alternative est à utiliser de préférence lorsque vous publiez une composant Yii qui encapsule quelques bibliothèques de tierces parties. Vous utilisez la méthode statique comme c'est montré ci-dessus pour représenter la logique complexe de construction de l'objet de tierce partie, et l'utilisateur de votre composant doit seulement appeler la méthode statique pour configurer le composant.\n\n## Parcours d'un arbre <span id=\"tree-traversal\"></span>\n\nLes modules acceptent les inclusions arbitraires; une application Yii est essentiellement un arbre de modules. Comme chacun de ces modules est un localisateur de services, cela a du sens pour les enfants d'accéder à leur parent. \nCela permet aux modules d'utiliser `$this->get('db')` au lieu de faire référence au localisateur de services racine `Yii::$app->get('db')`.\nUn bénéficie supplémentaire pour le développeur est de pouvoir redéfinir la configuration dans un module.\n\nToute requête d'un service à l'intérieur d'un module est passée à son parent dans le cas où le module lui-même est incapable  de la satisfaire.\n\nNotez que la configuration depuis des composants dans un module n'est jamais fusionnée avec celle depuis un composant du module parent. Le modèle de localisateur de services nous permet de définir des services nommés mais on ne peut supposer que des services du même nom utilisent les mêmes paramètres de configuration.\n"
  },
  {
    "path": "docs/guide-fr/db-active-record.md",
    "content": "Enregistrement actif (*Active Record*)\n===================================== \n\nL'[enregistrement actif](https://fr.wikipedia.org/wiki/Active_record) fournit une interface orientée objet pour accéder aux données stockées dans une base de données et les manipuler. \nUne classe d'enregistrement actif (ActiveRecord) est associée à une table de base de données, une instance de cette classe représente une ligne de cette table, et un *attribut* d'une instance d'enregistrement actif représente la valeur d'une colonne particulière dans cette ligne. \nAu lieu d'écrire des instructions SQL brutes, \nvous pouvez accéder aux attributs de l'objet enregistrement actif et appeler ses méthodes pour accéder aux données stockées dans les tables de la base de données et les manipuler. \n\n\n\nPar exemple, supposons que `Customer` soit une classe d'enregistrement actif associée à la table `customer` et que `name` soit une colonne de la table `customer`. \nVous pouvez écrire le code suivant pour insérer une nouvelle ligne dans la table `customer` :\n\n\n```php\n$customer = new Customer();\n$customer->name = 'Qiang';\n$customer->save();\n```\n\nLe code ci-dessus est équivalent à l'utilisation de l'instruction SQL brute suivante pour MySQL, qui est moins intuitive, plus propice aux erreurs, et peut même poser des problèmes de compatibilité sur vous utilisez un système de gestion de base données différent. \n\n\n```php\n$db->createCommand('INSERT INTO `customer` (`name`) VALUES (:name)', [\n    ':name' => 'Qiang',\n])->execute();\n```\n\nYii assure la prise en charge de l'enregistrement actif (*Active Record*) pour les bases de données relationnelles suivantes :\n\n* MySQL 4.1 ou versions postérieures : via [[yii\\db\\ActiveRecord]]\n* PostgreSQL 7.3 ou versions postérieures : via [[yii\\db\\ActiveRecord]]\n* SQLite 2 et 3 : via [[yii\\db\\ActiveRecord]]\n* Microsoft SQL Server 2008 ou versions postérieures : via [[yii\\db\\ActiveRecord]]\n* Oracle : via [[yii\\db\\ActiveRecord]]\n* CUBRID 9.3 ou versions postérieures : via [[yii\\db\\ActiveRecord]] \n  (Notez que, à cause d'un [bogue](https://jira.cubrid.org/browse/APIS-658) dans l'extension CUBRID 9.3, l'entourage des valeurs par des marques de citation ne fonctionne pas, c'est pourquoi vous avez besoin de CUBRID 9.3 à la fois comme client et comme serveur)\n* Sphinx : via [[yii\\sphinx\\ActiveRecord]], requiert l'extension `yii2-sphinx`\n* ElasticSearch : via [[yii\\elasticsearch\\ActiveRecord]], requiert l'extension `yii2-elasticsearch`\n\nDe plus, Yii prend aussi en charge l'enregistrement actif (*Active Record*) avec les bases de données non SQL suivantes :\n\n* Redis 2.6.12 ou versions postérieures : via [[yii\\redis\\ActiveRecord]], requiert l'extension `yii2-redis`\n* MongoDB 1.3.0 ou versions postérieures: via [[yii\\mongodb\\ActiveRecord]], requiert l'extension `yii2-mongodb`\n\nDans ce tutoriel, nous décrivons essentiellement l'utilisation de l'enregistrement actif pour des bases de données relationnelles. \nCependant, la majeure partie du contenu décrit ici est aussi applicable aux bases de données non SQL.\n\n\n## Déclaration des classes d'enregistrement actif (*Active Record*) <span id=\"declaring-ar-classes\"></span>\n\nPour commencer, déclarez une classe d'enregistrement actif en étendant la classe [[yii\\db\\ActiveRecord]]. \n\n### Définir un nom de table\n\nPar défaut, chacune des classes d'enregistrement actif est associée à une table de la base de données.\nLa méthode [[yii\\db\\ActiveRecord::tableName()|tableName()]] retourne le nom de la table en convertissant le nom via [[yii\\helpers\\Inflector::camel2id()]].\nVous pouvez redéfinir cette méthode si le nom de la table ne suit pas cette convention.\n\nUn [[yii\\db\\Connection::$tablePrefix|préfixe de table]] par défaut peut également être appliqué.  \nPar exemple, si le [[yii\\db\\Connection::$tablePrefix|préfixe de table]] est `tbl_`, `Customer` devient `tbl_customer` et `OrderItem` devient `tbl_order_item`. \n\nSi un nom de table est fourni sous la forme `{{%TableName}}`, alors le caractère `%` est remplacé par le préfixe de table.\nPar exemple, `{{%post}}` devient `{{tbl_post}}`. Les accolades autour du nom de table sont utilisées pour \n[l'entourage par des marques de citation dans une requête SQL ](db-dao.md#quoting-table-and-column-names).\n\nDans l'exemple suivant, nous déclarons une classe  d'enregistrement actif nommée `Customer` pour la table de base de données `customer`. \n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass Customer extends ActiveRecord\n{\n    const STATUS_INACTIVE = 0;\n    const STATUS_ACTIVE = 1;\n    \n    /**\n     * @return string le nom de la table associée à cette classe d'enregistrement actif.\n     */\n    public static function tableName()\n    {\n        return 'customer';\n    }\n}\n```\n\n### Les enregistrements actifs sont appelés \"modèles\"\n\nLes instances d'une classe d'enregistrement actif (*Active Record*) sont considérées comme des [modèles](structure-models.md).\n Pour cette raison, nous plaçons les classes d'enregistrement actif dans l'espace de noms `app\\models`(ou autres espaces de noms prévus pour contenir des classes de modèles). \n\nComme la classe [[yii\\db\\ActiveRecord]] étend la classe [[yii\\base\\Model]], \nelle hérite de *toutes* les fonctionnalités d'un [modèle](structure-models.md), comme les attributs, les règles de validation, la sérialisation des données, etc.\n\n\n## Connexion aux bases de données <span id=\"db-connection\"></span>\n\nPar défaut, l'enregistrement actif utilise le [composant d'application](structure-application-components.md) `db` en tant que [[yii\\db\\Connection|connexion à une base de données]] \npour accéder aux données de la base de données et les manipuler. \nComme expliqué dans la section [Objets d'accès aux bases de données](db-dao.md), vous pouvez configurer le composant `db` dans la configuration de l'application comme montré ci-dessous :\n\n\n```php\nreturn [\n    'components' => [\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=testdb',\n            'username' => 'demo',\n            'password' => 'demo',\n        ],\n    ],\n];\n```\n\nSi vous voulez utiliser une connexion de base de données autre que le composant `db`, vous devez redéfinir la méthode [[yii\\db\\ActiveRecord::getDb()|getDb()]] :\n\n\n```php\nclass Customer extends ActiveRecord\n{\n    // ...\n\n    public static function getDb()\n    {\n        // utilise le composant d'application \"db2\"\n        return \\Yii::$app->db2;\n    }\n}\n```\n\n\n## Requête de données <span id=\"querying-data\"></span>\n\nAprès avoir déclaré une classe d'enregistrement actif, vous pouvez l'utiliser pour faire une requête de données de la table correspondante dans la base de données. Ce processus s'accomplit en général en trois étapes :\n\n1. Créer un nouvel objet *query* (requête) en appelant la méthode [[yii\\db\\ActiveRecord::find()]] ;\n2. Construire l'objet *query* en appelant des [méthodes de construction de requête](db-query-builder.md#building-queries);\n3. Appeler une [méthode de requête](db-query-builder.md#query-methods) pour retrouver les données en terme d'instances d'enregistrement actif. \n\nComme vous pouvez le voir, cela est très similaire à la procédure avec le [constructeur de requêtes](db-query-builder.md). \nLa seule différence est que, au lieu d'utiliser l'opérateur `new` pour créer un objet *query* (requête), vous appelez la méthode [[yii\\db\\ActiveRecord::find()]] pour retourner un nouvel objet *query* qui est de la classe [[yii\\db\\ActiveQuery]].\n\n\n\nCe-dessous, nous donnons quelques exemples qui montrent comment utiliser l'*Active Query* (requête active) pour demander des données : \n\n```php\n// retourne un client (*customer*) unique dont l'identifiant est 123\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::find()\n    ->where(['id' => 123])\n    ->one();\n\n// retourne tous les clients actifs et les classe par leur identifiant\n// SELECT * FROM `customer` WHERE `status` = 1 ORDER BY `id`\n$customers = Customer::find()\n    ->where(['status' => Customer::STATUS_ACTIVE])\n    ->orderBy('id')\n    ->all();\n\n// retourne le nombre de clients actifs \n// SELECT COUNT(*) FROM `customer` WHERE `status` = 1\n$count = Customer::find()\n    ->where(['status' => Customer::STATUS_ACTIVE])\n    ->count();\n\n// retourne tous les clients dans un tableau indexé par l'identifiant du client \n// SELECT * FROM `customer`\n$customers = Customer::find()\n    ->indexBy('id')\n    ->all();\n```\n\nDans le code ci-dessus, `$customer` est un objet `Customer` tandis que `$customers` est un tableau d'objets `Customer`. \nIls sont tous remplis par les données retrouvées dans la table `customer`.\n\n> Info: comme la classe [[yii\\db\\ActiveQuery]] étend la classe [[yii\\db\\Query]], \nvous pouvez utiliser *toutes* les méthodes de construction et de requête comme décrit dans la section sur le [constructeur de requête](db-query-builder.md).\n\nParce que faire une requête de données par les valeurs de clés primaires ou par jeu de valeurs de colonne est une tâche assez courante, Yii fournit une prise en charge de méthodes raccourcis pour cela :\n\n\n- [[yii\\db\\ActiveRecord::findOne()]]: retourne une instance d'enregistrement actif remplie avec la première ligne du résultat de la requête.\n- [[yii\\db\\ActiveRecord::findAll()]]: retourne un tableau d'instances d'enregistrement actif rempli avec *tous* les résultats de la requête.\n\nLes deux méthodes acceptent un des formats de paramètres suivants :\n\n- une valeur scalaire : la valeur est traitée comme la valeur de la clé primaire à rechercher. Yii détermine automatiquement quelle colonne est la colonne de clé primaire en lisant les informations du schéma de la base de données.\n- un tableau de valeurs scalaires : le tableau est traité comme les valeurs de clé primaire désirées à rechercher.\n- un tableau associatif : les clés sont les noms de colonne et les valeurs sont les valeurs de colonne désirées à rechercher. \nReportez-vous au [format haché](db-query-builder.md#hash-format) pour plus de détails.\n  \n\nLe code qui suit montre comment ces méthodes peuvent être utilisées :\n\n```php\n// retourne un client unique dont l'identifiant est 123\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// retourne les clients dont les identifiants sont 100, 101, 123 ou 124\n// SELECT * FROM `customer` WHERE `id` IN (100, 101, 123, 124)\n$customers = Customer::findAll([100, 101, 123, 124]);\n\n// retourne un client actif dont l'identifiant est 123\n// SELECT * FROM `customer` WHERE `id` = 123 AND `status` = 1\n$customer = Customer::findOne([\n    'id' => 123,\n    'status' => Customer::STATUS_ACTIVE,\n]);\n\n// retourne tous les clients inactifs \n// SELECT * FROM `customer` WHERE `status` = 0\n$customers = Customer::findAll([\n    'status' => Customer::STATUS_INACTIVE,\n]);\n```\n\n> Attention : si vous avez besoin de passer des saisies utilisateur à \nces méthodes, assurez-vous que la valeurs saisie est un scalaire ou dans le cas d'une\n> condition tableau, assurez-vous que la structure du tableau ne peut pas être changée depuis l'extérieur :\n>\n> ```php\n> // yii\\web\\Controller garantit que $id est un scalaire\n> public function actionView($id)\n> {\n>     $model = Post::findOne($id);\n>     // ...\n> }\n>\n> // spécifier explicitement la colonne à chercher, passer un scalaire ou un tableau ici, aboutit à retrouver un enregistrement unique\n> $model = Post::findOne(['id' => Yii::$app->request->get('id')]);\n>\n> // n'utilisez PAS le code suivant si possible ! Il est possible d'injecter une condition tableau pour filtrer par des valeurs de colonne arbitraires !\n> $model = Post::findOne(Yii::$app->request->get('id'));\n> ```\n\n> Note: ni [[yii\\db\\ActiveRecord::findOne()]], ni [[yii\\db\\ActiveQuery::one()]] n'ajoutent `LIMIT 1` à l'instruction SQL générée. Si votre requête peut retourner plusieurs lignes de données, vous devez appeler `limit(1)` explicitement pour améliorer la performance, p. ex., `Customer::find()->limit(1)->one()`.\n\nEn plus d'utiliser les méthodes de construction de requête, vous pouvez aussi écrire du SQL brut pour effectuer une requête de données et vous servir des résultats pour remplir des objets enregistrements actifs. \nVous pouvez le faire en appelant la méthode [[yii\\db\\ActiveRecord::findBySql()]] :\n\n\n\n```php\n// retourne tous les clients inactifs\n$sql = 'SELECT * FROM customer WHERE status=:status';\n$customers = Customer::findBySql($sql, [':status' => Customer::STATUS_INACTIVE])->all();\n```\n\nN'appelez pas de méthodes de construction de requêtes supplémentaires après avoir appelé \n[[yii\\db\\ActiveRecord::findBySql()|findBySql()]] \ncar elles seront ignorées. \n\n## Accès aux données <span id=\"accessing-data\"></span>\n\n\nComme nous l'avons mentionné plus haut, les données extraites de la base de données servent à remplir des instances de la classe d'enregistrement actif et chacune des lignes du résultat de la requête correspond à une instance unique de la classe d'enregistrement actif. \nVous pouvez accéder accéder aux valeurs des colonnes en accédant aux attributs des instances de la classe d'enregistrement actif, par exemple : \n\n```php\n// \"id\" et \"email\" sont les noms des colonnes de la table \"customer\"\n$customer = Customer::findOne(123);\n$id = $customer->id;\n$email = $customer->email;\n```\n\n> Note: les attributs de l'instance de la classe d'enregistrement actif sont nommés d'après les noms des colonnes de la table associée en restant sensibles à la casse. \nYii définit automatiquement un attribut dans l'objet enregistrement actif pour chacune des colonnes de la table associée. \nVous ne devez PAS déclarer à nouveau l'un quelconque des ces attributs. \n\nComme les attributs de l'instance d'enregistrement actif sont nommés d'après le nom des colonnes, \nvous pouvez vous retrouver en train d'écrire du code PHP tel que `$customer->first_name`, qui utilise le caractère (_) *souligné* pour séparer les mots dans les noms d'attributs si vos colonnes de table sont nommées de cette manière. \nSi vous êtes attaché à la cohérence du style de codage, vous devriez renommer vos colonnes de tables en conséquence (p. ex. en utilisant la notation en dos de chameau). \n\n\n\n### Transformation des données <span id=\"data-transformation\"></span>\n\nIl arrive souvent que les données entrées et/ou affichées soient dans un format qui diffère de celui utilisé pour stocker les données dans la base. \nPar exemple, dans la base de données, vous stockez la date d'anniversaire des clients sous la forme de horodates UNIX (bien que ce soit pas une conception des meilleures), \ntandis que dans la plupart des cas, vous avez envie de manipuler les dates d'anniversaire sous la forme de chaînes de caractères dans le format `'YYYY/MM/DD'`. \nPour le faire, vous pouvez définir des méthodes de *transformation de données* dans la classe d'enregistrement actif comme ceci : \n\n\n```php\nclass Customer extends ActiveRecord\n{\n    // ...\n\n    public function getBirthdayText()\n    {\n        return date('Y/m/d', $this->birthday);\n    }\n    \n    public function setBirthdayText($value)\n    {\n        $this->birthday = strtotime($value);\n    }\n}\n```\n\nDésormais, dans votre code PHP, au lieu d'accéder à `$customer->birthday`, vous devez accéder à `$customer->birthdayText`, ce qui vous permet d'entrer et d'afficher les dates d'anniversaire dans le format `'YYYY/MM/DD'`.\n\n\n> Tip: l'exemple qui précède montre une manière générique de transformer des données dans différents formats. Si vous travaillez avec des valeurs de dates, vous pouvez utiliser [DateValidator](tutorial-core-validators.md#date) et [[yii\\jui\\DatePicker|DatePicker]], qui sont plus faciles à utiliser et plus puissantes.\n\n\n\n\n### Retrouver des données dans des tableaux <span id=\"data-in-arrays\"></span>\n\nAlors que retrouver des données en termes d'objets enregistrements actifs est souple et pratique, cela n'est pas toujours souhaitable lorsque vous devez extraire une grande quantité de données à cause de l'empreinte mémoire très importante. \nDans ce cas, vous pouvez retrouver les données en utilisant des tableaux PHP en appelant [[yii\\db\\ActiveQuery::asArray()|asArray()]] avant d'exécuter une méthode de requête :\n\n\n```php\n// retourne tous les clients\n// chacun des clients est retourné sous forme de tableau associatif\n$customers = Customer::find()\n    ->asArray()\n    ->all();\n```\n\n> Note: bien que cette méthode économise de la mémoire et améliore la performance, elle est plus proche de la couche d'abstraction basse de la base de données et perd la plupart des fonctionnalités de l'objet enregistrement actif. Une distinction très importante réside dans le type de données des valeurs de colonne. \n  Lorsque vous retournez des données dans une instance d'enregistrement actif, les valeurs des colonnes sont automatiquement typées en fonction du type réel des colonnes ;\n  par contre, lorsque vous retournez des données dans des tableaux, les valeurs des colonnes sont des chaînes de caractères (parce qu'elles résultent de PDO sans aucun traitement), indépendamment du type réel de ces colonnes.\n\n\n\n\n### Retrouver des données dans des lots <span id=\"data-in-batches\"></span>\n\nDans la section sur le [constructeur de requêtes](db-query-builder.md), nous avons expliqué que vous pouvez utiliser des *requêtes par lots* pour minimiser l'utilisation de la mémoire lorsque vous demandez de grandes quantités de données de la base de données. \nVous pouvez utiliser la même technique avec l'enregistrement actif. Par exemple :\n\n```php\n// va chercher 10 clients (customer) à la fois\nforeach (Customer::find()->batch(10) as $customers) {\n    // $customers est un tableau de 10 (ou moins) objets Customer \n}\n\n// va chercher 10 clients (customers) à la fois et itère sur chacun d'eux \nforeach (Customer::find()->each(10) as $customer) {\n    // $customer est un objet Customer \n}\n\n// requête par lots avec chargement précoce \nforeach (Customer::find()->with('orders')->each() as $customer) {\n    // $customer est un objet Customer avec la relation 'orders' remplie\n}\n```\n\n\n## Sauvegarde des données <span id=\"inserting-updating-data\"></span>\n\nEn utilisant l'enregistrement actif, vous pouvez sauvegarder facilement les données dans la base de données en suivant les étapes suivantes :\n\n1. Préparer une instance de la classe d'enregistrement actif\n2. Assigner de nouvelles valeurs aux attributs de cette instance\n3. Appeler [[yii\\db\\ActiveRecord::save()]] pour sauvegarder les données dans la base de données.\n\nPar exemple :\n\n```php\n// insère une nouvelle ligne de données\n$customer = new Customer();\n$customer->name = 'James';\n$customer->email = 'james@example.com';\n$customer->save();\n\n// met à jour une ligne de données existante\n$customer = Customer::findOne(123);\n$customer->email = 'james@newexample.com';\n$customer->save();\n```\n\nLa méthode [[yii\\db\\ActiveRecord::save()|save()]] peut soit insérer, soit mettre à jour une ligne de données, selon l'état de l'instance de l'enregistrement actif. \nSi l'instance est en train d'être créée via l'opérateur `new`, appeler [[yii\\db\\ActiveRecord::save()|save()]] \nprovoque l'insertion d'une nouvelle ligne de données ; \nsi l'instance est le résultat d'une méthode de requête, appeler [[yii\\db\\ActiveRecord::save()|save()]] met à jour la ligne associée à l'instance. \n\nVous pouvez différentier les deux états d'une instance d'enregistrement actif en testant la valeur de sa propriété [[yii\\db\\ActiveRecord::isNewRecord|isNewRecord]]. \nCette propriété est aussi utilisée par [[yii\\db\\ActiveRecord::save()|save()]] \nen interne, comme ceci :\n\n```php\npublic function save($runValidation = true, $attributeNames = null)\n{\n    if ($this->getIsNewRecord()) {\n        return $this->insert($runValidation, $attributeNames);\n    } else {\n        return $this->update($runValidation, $attributeNames) !== false;\n    }\n}\n```\n\n> Astuce: vous pouvez appeler [[yii\\db\\ActiveRecord::insert()|insert()]] ou [[yii\\db\\ActiveRecord::update()|update()]] \ndirectement pour insérer ou mettre à jour une ligne.\n  \n\n### Validation des données <span id=\"data-validation\"></span>\n\nComme la classe [[yii\\db\\ActiveRecord]] étend la classe [[yii\\base\\Model]], \nelle partage la même fonctionnalité de [validation des données](input-validation.md). \nVous pouvez déclarer les règles de validation en redéfinissant la méthode [[yii\\db\\ActiveRecord::rules()|rules()]] et effectuer la validation des données en appelant la méthode [[yii\\db\\ActiveRecord::validate()|validate()]].\n\nLorsque vous appelez la méthode [[yii\\db\\ActiveRecord::save()|save()]], par défaut, elle appelle automatiquement la méthode [[yii\\db\\ActiveRecord::validate()|validate()]]. \nC'est seulement si la validation réussit, que les données sont effectivement sauvegardées ; \nautrement elle retourne simplement `false`, et vous pouvez tester la propriété [[yii\\db\\ActiveRecord::errors|errors]] pour retrouver les messages d'erreurs de validation.\n\n> Astuce: si vous avez la certitude que vos données n'ont pas besoin d'être validées (p. ex. vos données proviennent de sources fiables), vous pouvez appeler `save(false)` pour omettre la validation.\n\n\n\n### Assignation massive <span id=\"massive-assignment\"></span>\n\nComme les [modèles](structure-models.md) habituels, les instances d'enregistrement actif profitent de la [fonctionnalité d'assignation massive](structure-models.md#massive-assignment). \nL'utilisation de cette fonctionnalité vous permet d'assigner plusieurs attributs d'un enregistrement actif en une seule instruction PHP, comme c'est montré ci-dessous. \nN'oubliez cependant pas que, seuls les [attributs sûrs](structure-models.md#safe-attributes) sont assignables en masse. \n\n```php\n$values = [\n    'name' => 'James',\n    'email' => 'james@example.com',\n];\n\n$customer = new Customer();\n\n$customer->attributes = $values;\n$customer->save();\n```\n\n\n### Mise à jour des compteurs <span id=\"updating-counters\"></span>\n\nC'est une tâche courante que d'incrémenter ou décrémenter une colonne dans une table de base de données. \nNous appelons ces colonnes « colonnes compteurs*. Vous pouvez utiliser la méthode [[yii\\db\\ActiveRecord::updateCounters()|updateCounters()]] pour mettre à jour une ou plusieurs colonnes de comptage. \nPar exemple : \n\n```php\n$post = Post::findOne(100);\n\n// UPDATE `post` SET `view_count` = `view_count` + 1 WHERE `id` = 100\n$post->updateCounters(['view_count' => 1]);\n```\n\n> Note: si vous utilisez la méthode [[yii\\db\\ActiveRecord::save()]] pour mettre à jour une colonne compteur, vous pouvez vous retrouver avec un résultat erroné car il est probable que le même compteur soit sauvegardé par de multiples requêtes qui lisent et écrivent la même valeur de compteur.\n\n\n\n### Attributs sales (*Dirty Attributes*) <span id=\"dirty-attributes\"></span>\n\nLorsque vous appelez la méthode [[yii\\db\\ActiveRecord::save()|save()]] pour sauvegarder une instance d'enregistrement actif, seuls les attributs dit *attributs sales* sont sauvegardés. \nUn attribut est considéré comme *sale* si sa valeur a été modifiée depuis qu'il a été chargé depuis la base de données ou sauvegardé dans la base de données le plus récemment. \nNotez que la validation des données est assurée sans se préoccuper de savoir si l'instance d'enregistrement actif possède des attributs sales ou pas. \n\n\nL'enregistrement actif tient à jour la liste des attributs sales. Il le fait en conservant une version antérieure des valeurs d'attribut et en les comparant avec les dernières. \nVous pouvez appeler la méthode [[yii\\db\\ActiveRecord::getDirtyAttributes()]] pour obtenir les attributs qui sont couramment sales. \nVous pouvez aussi appeler la méthode [[yii\\db\\ActiveRecord::markAttributeDirty()]] pour marquer explicitement un attribut comme sale. \n\n\nSi vous êtes intéressé par les valeurs d'attribut antérieurs à leur plus récente modification, vous pouvez appeler la méthode [[yii\\db\\ActiveRecord::getOldAttributes()|getOldAttributes()]] \nou la méthode [[yii\\db\\ActiveRecord::getOldAttribute()|getOldAttribute()]].\n\n> Note: la comparaison entre les anciennes et les nouvelles valeurs est faite en utilisant l'opérateur `===` , ainsi une valeur est considérée comme sale si le type est différent même si la valeur reste la même. \nCela est souvent le cas lorsque le modèle reçoit des entrées utilisateur de formulaires HTML ou chacune des valeurs est représentée par une chaîne de caractères. \nPour garantir le type correct pour p. ex. des valeurs entières, vous devez appliquer un [filtre de validation](input-validation.md#data-filtering):\n> `['attributeName', 'filter', 'filter' => 'intval']`. \nCela fonctionne pour toutes les fonctions de transformation de type de PHP comme [intval()](https://www.php.net/manual/fr/function.intval.php), \n[floatval()](https://www.php.net/manual/fr/function.floatval.php), \n[boolval](https://www.php.net/manual/fr/function.boolval.php), etc...\n\n### Valeurs d'attribut par défaut <span id=\"default-attribute-values\"></span>\n\nQuelques unes de vos colonnes de tables peuvent avoir des valeurs par défaut définies dans la base de données. \nParfois, vous voulez peut-être pré-remplir votre formulaire Web pour un enregistrement actif à partir des valeurs par défaut. \nPour éviter d'écrire les mêmes valeurs par défaut à nouveau, vous pouvez appeler la méthode [[yii\\db\\ActiveRecord::loadDefaultValues()|loadDefaultValues()]] pour remplir les attributs de l'enregistrement actif avec les valeurs par défaut prédéfinies dans la base de données :\n\n\n```php\n$customer = new Customer();\n$customer->loadDefaultValues();\n// $customer->xyz recevra la valeur par défaut déclarée lors de la définition de la colonne « xyz » column\n```\n\n\n### Conversion de type d'attributs <span id=\"attributes-typecasting\"></span>\n\nÉtant peuplé par les résultats des requêtes, l'[[yii\\db\\ActiveRecord|enregistrement actif]] effectue des conversions automatiques de type pour ses valeurs d'attribut, en utilisant les informations du [schéma des tables de base de données](db-dao.md#database-schema). \nCela permet aux données retrouvées dans les colonnes de la table et déclarées comme entiers de peupler une instance d'enregistrement actif avec des entiers PHP, les valeurs booléennes avec des valeurs booléennes, et ainsi de suite.\nNéanmoins, le mécanisme de conversion de type souffre de plusieurs limitation :\n\n* Les valeurs flottantes (Float) ne sont pas converties et sont représentées par des chaînes de caractères, autrement elles pourraient perdre de la précision. \n* La conversion des valeurs entières dépend de la capacité du système d'exploitation utilisé. \nEn particulier, les valeurs de colonne déclarée comme « entier non signé »,  ou  « grand entier » (big integer) sont converties en entier PHP seulement pour les système d'exploitation 64 bits, tandis que sur les systèmes 32 bits, elles sont représentées par des chaînes de caractères.\n\n\n\nNotez que la conversion de type des attributs n'est effectuée que lors du peuplement d'une instance d'enregistrement actif par les résultats d'une requête.\nIl n'y a pas de conversion automatique pour les valeurs chargées par une requête HTTP ou définies directement par accès à des propriétés. \nLe schéma de table est aussi utilisé lors de la préparation des instructions SQL pour la sauvegarde de l'enregistrement actif, garantissant ainsi que les valeurs sont liées à la requête avec le type correct.\nCependant, les valeurs d'attribut d'une instance d'enregistrement actif ne sont pas converties durant le processus de sauvegarde. \n\n\n> Astuce : vous pouvez utiliser  [[yii\\behaviors\\AttributeTypecastBehavior]] pour faciliter la conversion de type des valeurs d'attribut lors de la validation ou la sauvegarde d'un enregistrement actif.\n  \n\nDepuis la version 2.0.14, la classe  ActiveRecord de Yii prend en charge des types de données complexe tels que JSON ou les tableaux multi-dimensionnels. \n\n#### JSON dans MySQL et PostgreSQL\n\nAprès le peuplement par les données, la valeur d'une colonne JSON est automatiquement décodée selon les règles de décodage standard de JSON.\n\n\nPour sauvegarder une valeur d'attribut dans une colonne de type JSON, la classe ActiveRecord crée automatiquement un objet  [[yii\\db\\JsonExpression|JsonExpression]] \nqui est encodé en une chaîne JSON au niveau du [constructeur de requête](db-query-builder.md).\n\n#### Tableaux dans PostgreSQL\n\nAprès le peuplement par les données, les valeurs issues de colonnes de type tableau sont automatiquement décodée de la notation PgSQL en un objet  [[yii\\db\\ArrayExpression|ArrayExpression]]. \nIl met en œuvre l'interface `ArrayAccess`, ainsi pouvez-vous l'utiliser comme un tableau, ou appeler `->getValue()` pour obtenir le tableau lui-même.\n\nPour sauvegarder une valeur d'attribut dans une colonne de type tableau,la classe ActiveRecord crée automatiquement un objet [[yii\\db\\ArrayExpression|ArrayExpression]] qui est encodé par le [constructeur de requête](db-query-builder.md) en une chaîne PgSQL représentant le tableau.\n\n\nVous pouvez aussi utiliser des conditions pour les colonnes de type JSON :\n\n```php\n$query->andWhere(['=', 'json', new ArrayExpression(['foo' => 'bar'])\n```\n\nPour en apprendre plus sur les le système de construction d'expressions, reportez-vous à l'article  [Constructeur de requêtes – Ajout de conditions et d'expressions personnalisées](db-query-builder.md#adding-custom-conditions-and-expressions).\n\n### Mise à jour de plusieurs lignes <span id=\"updating-multiple-rows\"></span>\n\nLes méthodes décrites ci-dessus fonctionnent toutes sur des instances individuelles d'enregistrement actif pour insérer ou mettre à jour des lignes individuelles de table. \nPour mettre à jour plusieurs lignes à la fois, vous devez appeler la méthode statique [[yii\\db\\ActiveRecord::updateAll()|updateAll()]].\n\n\n```php\n// UPDATE `customer` SET `status` = 1 WHERE `email` LIKE `%@example.com%`\nCustomer::updateAll(['status' => Customer::STATUS_ACTIVE], ['like', 'email', '@example.com']);\n```\n\nDe façon similaire, vous pouvez appeler [[yii\\db\\ActiveRecord::updateAllCounters()|updateAllCounters()]] pour mettre à jour les colonnes compteurs de plusieurs lignes à la fois.\n\n```php\n// UPDATE `customer` SET `age` = `age` + 1\nCustomer::updateAllCounters(['age' => 1]);\n```\n\n\n## Suppression de données <span id=\"deleting-data\"></span>\n\nPour supprimer une ligne unique de données, commencez par retrouver l'instance d'enregistrement actif correspondant à cette ligne et appelez la méthode [[yii\\db\\ActiveRecord::delete()]].\n\n\n\n```php\n$customer = Customer::findOne(123);\n$customer->delete();\n```\n\nVous pouvez appeler [[yii\\db\\ActiveRecord::deleteAll()]] pour effacer plusieurs ou toutes les lignes de données. Par exemple :\n\n```php\nCustomer::deleteAll(['status' => Customer::STATUS_INACTIVE]);\n```\n\n> Note : agissez avec prudence lorsque vous appelez [[yii\\db\\ActiveRecord::deleteAll()|deleteAll()]] parce que cela peut effacer totalement toutes les données de votre table si vous faites une erreur en spécifiant la condition. \n\n\n## Cycles de vie de l'enregistrement actif <span id=\"ar-life-cycles\"></span>\n\nIl est important que vous compreniez les cycles de vie d'un enregistrement actif lorsqu'il est utilisé à des fins différentes. \nLors de chaque cycle de vie, une certaine séquence d'invocation de méthodes a lieu, et vous pouvez redéfinir ces méthodes pour avoir une chance de personnaliser le cycle de vie. \nVous pouvez également répondre à certains événements de l'enregistrement actif déclenchés durant un cycle de vie pour injecter votre code personnalisé. \nCes événements sont particulièrement utiles lorsque vous développez des [comportements](concept-behaviors.md) d'enregistrement actif qui ont besoin de personnaliser les cycles de vie d'enregistrement actifs. \n\n\n\nDans l'exemple suivant, nous résumons les différents cycles de vie d'enregistrement actif et les méthodes/événements à qui il est fait appel dans ces cycles.\n\n\n\n### Cycle de vie d'une nouvelle instance <span id=\"new-instance-life-cycle\"></span>\n\nLorsque vous créez un nouvel enregistrement actif via l'opérateur `new`, le cycle suivant se réalise :\n\n1. Construction de la classe.\n2. [[yii\\db\\ActiveRecord::init()|init()]]: déclenche un événement [[yii\\db\\ActiveRecord::EVENT_INIT|EVENT_INIT]].\n\n\n### Cycle de vie lors d'une requête de données <span id=\"querying-data-life-cycle\"></span>\n\nLorsque vous effectuez une requête de données via l'une des [méthodes de requête](#querying-data), chacun des enregistrements actifs nouvellement rempli entreprend le cycle suivant :\n\n\n1. Construction de la classe.\n2. [[yii\\db\\ActiveRecord::init()|init()]]: déclenche un événement [[yii\\db\\ActiveRecord::EVENT_INIT|EVENT_INIT]].\n3. [[yii\\db\\ActiveRecord::afterFind()|afterFind()]]: déclenche un événement [[yii\\db\\ActiveRecord::EVENT_AFTER_FIND|EVENT_AFTER_FIND]].\n\n\n### Cycle de vie lors d'une sauvegarde de données <span id=\"saving-data-life-cycle\"></span>\n\nEn appelant [[yii\\db\\ActiveRecord::save()|save()]] pour insérer ou mettre à jour une instance d'enregistrement actif, le cycle de vie suivant se réalise :\n\n\n1. [[yii\\db\\ActiveRecord::beforeValidate()|beforeValidate()]]: déclenche un événement [[yii\\db\\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]] . \n   Si la méthode retourne `false` (faux), ou si [[yii\\base\\ModelEvent::isValid]] est `false`, les étapes suivantes sont sautées.\n2. Effectue la validation des données. \n   Si la validation échoue, les étapes après l'étape 3 saut sautées. \n3. [[yii\\db\\ActiveRecord::afterValidate()|afterValidate()]]: \n   déclenche un événement [[yii\\db\\ActiveRecord::EVENT_AFTER_VALIDATE|EVENT_AFTER_VALIDATE]].\n4. [[yii\\db\\ActiveRecord::beforeSave()|beforeSave()]]: \n   déclenche un événement [[yii\\db\\ActiveRecord::EVENT_BEFORE_INSERT|EVENT_BEFORE_INSERT]] \n   ou un événement [[yii\\db\\ActiveRecord::EVENT_BEFORE_UPDATE|EVENT_BEFORE_UPDATE]]. \n   Si la méthode retourne `false` ou si [[yii\\base\\ModelEvent::isValid]] est `false`, les étapes suivantes sont sautées. \n5. Effectue l'insertion ou la mise à jour réelle.\n6. [[yii\\db\\ActiveRecord::afterSave()|afterSave()]]: \n   déclenche un événement [[yii\\db\\ActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]] \n   ou un événement [[yii\\db\\ActiveRecord::EVENT_AFTER_UPDATE|EVENT_AFTER_UPDATE]].\n   \n\n### Cycle de vie lors d'une suppression de données <span id=\"deleting-data-life-cycle\"></span>\n\nEn appelant [[yii\\db\\ActiveRecord::delete()|delete()]] pour supprimer une instance d'enregistrement actif, le cycle suivant se déroule :\n\n\n1. [[yii\\db\\ActiveRecord::beforeDelete()|beforeDelete()]]: \n   déclenche un événement [[yii\\db\\ActiveRecord::EVENT_BEFORE_DELETE|EVENT_BEFORE_DELETE]]. \n   Si la méthode retourne `false` ou si [[yii\\base\\ModelEvent::isValid]] est `false`, les étapes suivantes sont sautées. \n2. Effectue la suppression réelle des données.\n3. [[yii\\db\\ActiveRecord::afterDelete()|afterDelete()]]: \n   déclenche un événement [[yii\\db\\ActiveRecord::EVENT_AFTER_DELETE|EVENT_AFTER_DELETE]].\n\n\n> Note : l'appel de l'une des méthodes suivantes n'initie AUCUN des cycles vus ci-dessus parce qu'elles travaillent directement sur la base de données et pas sur la base d'un enregistrement actif :\n>\n\n> - [[yii\\db\\ActiveRecord::updateAll()]] \n> - [[yii\\db\\ActiveRecord::deleteAll()]]\n> - [[yii\\db\\ActiveRecord::updateCounters()]] \n> - [[yii\\db\\ActiveRecord::updateAllCounters()]] \n\n### Cycle de vie lors du rafraîchissement des données <span id=\"refreshing-data-life-cycle\"></span>\n\nEn appelant [[yii\\db\\ActiveRecord::refresh()|refresh()]] pour rafraîchir une instance d'enregistrement actif, l'événement [[yii\\db\\ActiveRecord::EVENT_AFTER_REFRESH|EVENT_AFTER_REFRESH]] \nest déclenché si le rafraîchissement réussit et si la méthode retourne `true`.\n\n\n## Travail avec des transactions <span id=\"transactional-operations\"></span>\n\nIl y a deux façons d'utiliser les [transactions](db-dao.md#performing-transactions) lorsque l'on travaille avec un enregistrement actif. \n\nLa première façon consiste à enfermer explicitement les appels des différents méthodes dans un bloc transactionnel, comme ci-dessous :\n\n```php\n$customer = Customer::findOne(123);\n\nCustomer::getDb()->transaction(function($db) use ($customer) {\n    $customer->id = 200;\n    $customer->save();\n    // ...autres opérations de base de données...\n});\n\n// ou en alternative\n\n$transaction = Customer::getDb()->beginTransaction();\ntry {\n    $customer->id = 200;\n    $customer->save();\n    // ...other DB operations...\n    $transaction->commit();\n} catch(\\Exception $e) {\n    $transaction->rollBack();\n    throw $e;\n} catch(\\Throwable $e) {\n    $transaction->rollBack();\n    throw $e;\n}\n```\n\n> Note : dans le code précédent, nous utilisons deux blocs de capture pour être compatible avec PHP 5.x et PHP 7.x. \n`\\Exception` met en œuvre l'[interface `\\Throwable`](https://www.php.net/manual/fr/class.throwable.php)\n> à partir de  PHP 7.0, c'est pourquoi vous pouvez sauter la partie avec `\\Exception` si votre application utilise PHP 7.0 ou une version plus récente.\n\nLa deuxième façon consiste à lister les opérations de base de données qui nécessitent une prise en charge transactionnelle dans la méthode [[yii\\db\\ActiveRecord::transactions()]]. \nPar exemple :\n\n```php\nclass Customer extends ActiveRecord\n{\n    public function transactions()\n    {\n        return [\n            'admin' => self::OP_INSERT,\n            'api' => self::OP_INSERT | self::OP_UPDATE | self::OP_DELETE,\n            // ce qui précède est équivalent à ce qui suit :\n            // 'api' => self::OP_ALL,\n        ];\n    }\n}\n```\n\nLa méthode [[yii\\db\\ActiveRecord::transactions()]] \ndoit retourner un tableau dont les clés sont les noms de [scénario](structure-models.md#scenarios) et les valeurs les opérations correspondantes qui doivent être enfermées dans des transactions. \nVous devez utiliser les constantes suivantes pour faire référence aux différentes opérations de base de données :\n\n* [[yii\\db\\ActiveRecord::OP_INSERT|OP_INSERT]]: opération d'insertion réalisée par [[yii\\db\\ActiveRecord::insert()|insert()]];\n* [[yii\\db\\ActiveRecord::OP_UPDATE|OP_UPDATE]]: opération de mise à jour réalisée par [[yii\\db\\ActiveRecord::update()|update()]];\n* [[yii\\db\\ActiveRecord::OP_DELETE|OP_DELETE]]: opération de suppression réalisée par [[yii\\db\\ActiveRecord::delete()|delete()]].\n\nUtilisez l'opérateur `|` pour concaténer les constantes précédentes pour indiquer de multiples opérations. \nVous pouvez également utiliser la constante raccourci [[yii\\db\\ActiveRecord::OP_ALL|OP_ALL]] pour faire référence à l'ensemble des trois opération ci-dessus.\n\nLes transactions qui sont créées en utilisant cette méthode sont démarrées avant d'appeler [[yii\\db\\ActiveRecord::beforeSave()|beforeSave()]] et sont entérinées après que la méthode [[yii\\db\\ActiveRecord::afterSave()|afterSave()]] a été exécutée.\n\n\n## Verrous optimistes <span id=\"optimistic-locks\"></span>\n\nLe verrouillage optimiste est une manière d'empêcher les conflits qui peuvent survenir lorsqu'une même ligne de données est mise à jour par plusieurs utilisateurs. \nPar exemple, les utilisateurs A et B sont tous deux, simultanément, en train de modifier le même article de wiki. \nAprès que l'utilisateur A a sauvegardé ses modifications, l'utilisateur B clique sur le bouton « Sauvegarder » dans le but de sauvegarder ses modifications lui aussi. \nComme l'utilisateur B est en train de travailler sur une version périmée de l'article, il serait souhaitable de disposer d'un moyen de l'empêcher de sauvegarder sa version de l'article et de lui montrer un message d'explication.\n\n\nLe verrouillage optimiste résout le problème évoqué ci-dessus en utilisant une colonne pour enregistrer le numéro de version de chacune des lignes. \nLorsqu'une ligne est sauvegardée avec un numéro de version périmée, une exception [[yii\\db\\StaleObjectException]] est levée, ce qui empêche la sauvegarde de la ligne. \nLe verrouillage optimiste, n'est seulement pris en charge que lorsque vous mettez à jour ou supprimez une ligne de données existante en utilisant les méthodes [[yii\\db\\ActiveRecord::update()]] \nou [[yii\\db\\ActiveRecord::delete()]],respectivement.\n\nPour utiliser le verrouillage optimiste :\n\n1. Créez une colonne dans la table de base de données associée à la classe d'enregistrement actif pour stocker le numéro de version de chacune des lignes. \nLa colonne doit être du type *big integer* \n(dans MySQL ce doit être `BIGINT DEFAULT 0`).\n2. Redéfinissez la méthode [[yii\\db\\ActiveRecord::optimisticLock()]] pour qu'elle retourne le nom de cette colonne.\n3. Dans la classe de votre modèle, mettez en œuvre  [[\\yii\\behaviors\\OptimisticLockBehavior|OptimisticLockBehavior]] pour analyser automatiquement sa valeur des requêtes reçues.\n4. Dans le formulaire Web qui reçoit les entrées de l'utilisateur, ajoutez un champ caché pour stocker le numéro de version courant de la ligne en modification. \n   Retirez l'attribut version des règles de validation étant donné que [[\\yii\\behaviors\\OptimisticLockBehavior|OptimisticLockBehavior]] s'en charge.\n5. Dans l'action de contrôleur qui met la ligne à jour en utilisant l'enregistrement actif, utiliser une structure *try-catch* pour l'exception [[yii\\db\\StaleObjectException]]. \n   Mettez en œuvre la logique requise (p. ex. fusionner les modifications, avertir des données douteuses) pour résoudre le conflit.\nPar exemple, supposons que la colonne du numéro de version est nommée `version`. \n   Vous pouvez mettre en œuvre le verrouillage optimiste avec un code similaire au suivant :\n\n\n```php\n// ------ view code -------\n\nuse yii\\helpers\\Html;\n\n// ...other input fields\necho Html::activeHiddenInput($model, 'version');\n\n\n// ------ controller code -------\n\nuse yii\\db\\StaleObjectException;\n\npublic function actionUpdate($id)\n{\n    $model = $this->findModel($id);\n\n    try {\n        if ($model->load(Yii::$app->request->post()) && $model->save()) {\n            return $this->redirect(['view', 'id' => $model->id]);\n        } else {\n            return $this->render('update', [\n                'model' => $model,\n            ]);\n        }\n    } catch (StaleObjectException $e) {\n        // logique pour résoudre le  conflict\n    }\n}\n\n// ------ model code -------\n\nuse yii\\behaviors\\OptimisticLockBehavior;\n\npublic function behaviors()\n{\n    return [\n        OptimisticLockBehavior::class,\n    ];\n}\n\npublic function optimisticLock()\n{\n    return 'version';\n}\n\n```\n> Note : comme [[\\yii\\behaviors\\OptimisticLockBehavior|OptimisticLockBehavior]] garantit que l'enregistrement n'est sauvegardé que \n> si l'utilisateur soumet un numéro de version valide en analysant directement [[\\yii\\web\\Request::getBodyParam()|getBodyParam()]], \n> il peut être utile d'étendre votre classe de modèle et de réaliser l'étape 2 dans le modèle du parent lors de l'attachement du comportement (étape 3) à la classe enfant;\n> ainsi vous pouvez disposer d'une instance dédiée à l'usage interne tout en liant l'autre aux contrôleurs chargés de recevoir \n> les entrées de l'utilisateur final. En alternative, vous pouvez mettre en œuvre votre propre logique en configurant sa propriété [[\\yii\\behaviors\\OptimisticLockBehavior::$value|value]]. \n\n\n## Travail avec des données relationnelles <span id=\"relational-data\"></span>\n\nEn plus de travailler avec des tables de base de données individuelles, l'enregistrement actif permet aussi de rassembler des données en relation, les rendant ainsi immédiatement accessibles via les données primaires. \nPar exemple, la donnée client est en relation avec les données commandes parce qu'un client peut avoir passé une ou plusieurs commandes. \nAvec les déclarations appropriées de cette relation, vous serez capable d'accéder aux commandes d'un client en utilisant l'expression `$customer->orders` qui vous renvoie les informations sur les commandes du client en terme de tableau d'instances `Order` (Commande) d'enregistrement actif.\n\n\n\n\n### Déclaration de relations <span id=\"declaring-relations\"></span>\n\nPour travailler avec des données relationnelles en utilisant l'enregistrement actif, vous devez d'abord déclarer les relations dans les classes d'enregistrement actif. \nLa tâche est aussi simple que de déclarer une *méthode de relation* pour chacune des relations concernées, comme ceci :\n\n```php\nclass Customer extends ActiveRecord\n{\n    // ...\n\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n}\n\nclass Order extends ActiveRecord\n{\n    // ...\n\n    public function getCustomer()\n    {\n        return $this->hasOne(Customer::class, ['id' => 'customer_id']);\n    }\n}\n```\n\nDans le code ci-dessus, nous avons déclaré une relation `orders` (commandes) pour la classe `Customer` (client), et une relation `customer` (client) pour la classe `Order` (commande). \n\n\nChacune des méthodes de relation doit être nommée sous la forme `getXyz`. Nous appelons `xyz` (la première lettre est en bas de casse) le *nom de la relation*. \nNotez que les noms de relation sont *sensibles à la casse*.\n\nEn déclarant une relation, vous devez spécifier les informations suivantes :\n\n- la multiplicité de la relation : spécifiée en appelant soit la méthode [[yii\\db\\ActiveRecord::hasMany()|hasMany()]], \nsoit la méthode [[yii\\db\\ActiveRecord::hasOne()|hasOne()]]. \nDans l'exemple ci-dessus vous pouvez facilement déduire en lisant la déclaration des relations qu'un client a beaucoup de commandes, tandis qu'une commande n'a qu'un client.\n- le nom de la classe d'enregistrement actif : spécifié comme le premier paramètre de [[yii\\db\\ActiveRecord::hasMany()|hasMany()]] ou de [[yii\\db\\ActiveRecord::hasOne()|hasOne()]]. \nUne pratique conseillée est d'appeler `Xyz::class` \npour obtenir la chaîne de caractères représentant le nom de la classe de manière à bénéficier \nde l'auto-complètement de l'EDI et de la détection d'erreur dans l'étape de compilation. \n- Le lien entre les deux types de données : spécifie le(s) colonne(s) via lesquelles les deux types de données sont en relation. \nLes valeurs du tableau sont les colonnes des données primaires (représentées par la classe d'enregistrement actif dont vous déclarez les relations), tandis que les clés sont les colonnes des données en relation. \n\n\nUne règle simple pour vous rappeler cela est, comme vous le voyez dans l'exemple ci-dessus, d'écrire la colonne qui appartient à l'enregistrement actif en relation juste à coté de lui. \nVous voyez là que l'identifiant du client (`customer_id`) est une propriété de `Order` et `id` est une propriété de `Customer`.\n\n\n\n### Accès aux données relationnelles  <span id=\"accessing-relational-data\"></span>\n\nAprès avoir déclaré des relations, vous pouvez accéder aux données relationnelles via le nom des relations. \nTout se passe comme si vous accédiez à une [propriété](concept-properties.md) d'un objet défini par la méthode de relation. Pour cette raison, nous appelons cette propriété *propriété de relation*. \nPar exemple :\n\n```php\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123\n// $orders est un tableau d'objets Order \n$orders = $customer->orders;\n```\n\n> Info : lorsque vous déclarez une relation nommée `xyz` via une méthode d'obtention `getXyz()`, vous êtes capable d'accéder à `xyz` comme à un [objet property](concept-properties.md). \n   Notez que le nom est sensible à la casse. \n\nSi une relation est déclarée avec la méthode [[yii\\db\\ActiveRecord::hasMany()|hasMany()]], l'accès à cette propriété de relation retourne un tableau des instances de l'enregistrement actif en relation ; \nsi une relation est déclarée avec la méthode [[yii\\db\\ActiveRecord::hasOne()|hasOne()]], l'accès à la propriété de relation retourne l'instance de l'enregistrement actif en relation, ou `null` si aucune donnée en relation n'est trouvée. \n\n\n\nLorsque vous accédez à une propriété de relation pour la première fois, une instruction SQL est exécutée comme le montre l'exemple précédent. \nSi la même propriété fait l'objet d'un nouvel accès, le résultat précédent est retourné sans exécuter à nouveau l'instruction SQL. \nPour forcer l'exécution à nouveau de l'instruction SQL, vous devez d'abord annuler la définition de la propriété de relation : `unset($customer->orders)`.\n\n\n> Note : bien que ce concept semble similaire à la fonctionnalité [propriété d'objet](concept-properties.md), il y a une différence importante. \n   Pour les propriétés normales d'objet, la valeur est du même type que la méthode d'obtention de définition. \n   Une méthode de relation cependant retourne toujours une instance d'[[yii\\db\\ActiveRecord]] ou un tableau de telles instances.\n> \n>\n> ```php\n> $customer->orders; // est un tableau d'objets `Order` \n> $customer->getOrders(); // retourne une instance d'ActiveQuery\n> ```\n> \n> Cela est utile for créer des requêtes personnalisées, ce qui est décrit dans la section suivante. \n\n\n### Requête relationnelle dynamique <span id=\"dynamic-relational-query\"></span>\n\nParce qu'une méthode de relation retourne une instance d'[[yii\\db\\ActiveQuery]], vous pouvez continuer à construire cette requête en utilisant les méthodes de construction avant de l'exécuter. \nPar exemple :\n\n```php\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123 AND `subtotal` > 200 ORDER BY `id`\n$orders = $customer->getOrders()\n    ->where(['>', 'subtotal', 200])\n    ->orderBy('id')\n    ->all();\n```\n\nContrairement à l'accès à une propriété de relation, chaque fois que vous effectuez une requête relationnelle dynamique via une méthode de relation, une instruction SQL est exécutée, même si la même requête relationnelle dynamique a été effectuée auparavant.\n\n\nParfois, vous voulez peut-être paramétrer une déclaration de relation de manière à ce que vous puissiez effectuer des requêtes relationnelles dynamiques plus facilement. \nPar exemple, vous pouvez déclarer une relation `bigOrders` comme ceci :, \n\n```php\nclass Customer extends ActiveRecord\n{\n    public function getBigOrders($threshold = 100)\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id'])\n            ->where('subtotal > :threshold', [':threshold' => $threshold])\n            ->orderBy('id');\n    }\n}\n```\n\nPar la suite, vous serez en mesure d'effectuer les requêtes relationnelles suivantes : \n\n```php\n// SELECT * FROM `order` WHERE `customer_id` = 123 AND `subtotal` > 200 ORDER BY `id`\n$orders = $customer->getBigOrders(200)->all();\n\n// SELECT * FROM `order` WHERE `customer_id` = 123 AND `subtotal` > 100 ORDER BY `id`\n$orders = $customer->bigOrders;\n```\n\n\n### Relations via une table de jointure <span id=\"junction-table\"></span>\n\nDans la modélisation de base de données, lorsque la multiplicité entre deux tables en relation est *many-to-many* (de plusieurs à plusieurs), une [table de jointure](https://en.wikipedia.org/wiki/Junction_table) est en général introduite. \nPar exemple, la table `order` (commande) et la table `item` peuvent être en relation via une table de jointure nommée `order_item` (item_de_commande). \nUne commande correspond ensuite à de multiples items de commande, tandis qu'un item de produit correspond lui-aussi à de multiples items de commande (*order items*). \n\n\nLors de la déclaration de telles relations, vous devez appeler soit [[yii\\db\\ActiveQuery::via()|via()]], \nsoit [[yii\\db\\ActiveQuery::viaTable()|viaTable()]], pour spécifier la table de jointure. \nLa différence entre [[yii\\db\\ActiveQuery::via()|via()]] et [[yii\\db\\ActiveQuery::viaTable()|viaTable()]] est que la première spécifie la table de jointure en termes de noms de relation existante, tandis que la deuxième utilise directement la table de jointure. \nPar exemple : \n\n```php\nclass Order extends ActiveRecord\n{\n    public function getItems()\n    {\n        return $this->hasMany(Item::class, ['id' => 'item_id'])\n            ->viaTable('order_item', ['order_id' => 'id']);\n    }\n}\n```\n\nou autrement,\n\n```php\nclass Order extends ActiveRecord\n{\n    public function getOrderItems()\n    {\n        return $this->hasMany(OrderItem::class, ['order_id' => 'id']);\n    }\n\n    public function getItems()\n    {\n        return $this->hasMany(Item::class, ['id' => 'item_id'])\n            ->via('orderItems');\n    }\n}\n```\n\nL'utilisation de relations déclarées avec une table de jointure est la même que celle de relations normales. Par exemple :\n\n```php\n// SELECT * FROM `order` WHERE `id` = 100\n$order = Order::findOne(100);\n\n// SELECT * FROM `order_item` WHERE `order_id` = 100\n// SELECT * FROM `item` WHERE `item_id` IN (...)\n// retourne un tableau d'objets Item \n$items = $order->items;\n```\n\n\n### Chaînage de définitions de relation via de multiples tables <span id=\"multi-table-relations\"></span>\n\nIl est de plus possible de définir des relations via de multiples tables en chaînant les définitions de relation en utilisant [[yii\\db\\ActiveQuery::via()|via()]].\nEn reprenant l'exemple ci-dessus, nous avons les classes  `Customer`, `Order` et `Item`.\nNous pouvons ajouter une relation à la classe `Customer` qui liste tous les items de tous les commandes qu'ils ont passées,\net la nommer `getPurchasedItems()`, le chaînage de relations est présenté dans l'exemple de code suivant :\n\n```php\nclass Customer extends ActiveRecord\n{\n    // ...\n\n    public function getPurchasedItems()\n    {\n        // items de clients pour lesquels la colonne 'id' de `Item` correspond à  'item_id' dans OrderItem\n        return $this->hasMany(Item::class, ['id' => 'item_id'])\n                    ->via('orderItems');\n    }\n\n    public function getOrderItems()\n    {\n        // items de commandes clients pour lesquels, la colonne 'id' de `Order` correspond à 'order_id' dans OrderItem\n        return $this->hasMany(OrderItem::class, ['order_id' => 'id'])\n                    ->via('orders');\n    }\n\n    public function getOrders()\n    {\n        // idem à ci-dessus\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n}\n```\n\n\n### Chargement paresseux et chargement précoce <span id=\"lazy-eager-loading\"></span>\n\nDans la sous-section [Accès aux données relationnelles](#accessing-relational-data), nous avons expliqué que vous pouvez accéder à une propriété de relation d'une instance d'enregistrement actif comme si vous accédiez à une propriété normale d'objet. \nUne instruction SQL est exécutée seulement lorsque vous accédez à cette propriété pour la première fois. \nNous appelons une telle méthode d'accès à des données relationnelles, *chargement paresseux*. \nPar exemple :\n\n```php\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123\n$orders = $customer->orders;\n\n// pas de SQL exécuté\n$orders2 = $customer->orders;\n```\n\nLe chargement paresseux est très pratique à utiliser. \nNéanmoins, il peut souffrir d'un problème de performance lorsque vous avez besoin d'accéder à la même propriété de relation sur de multiples instances d'enregistrement actif. \nExaminons l'exemple de code suivant. Combien d'instruction SQL sont-elles exécutées ?\n\n```php\n// SELECT * FROM `customer` LIMIT 100\n$customers = Customer::find()->limit(100)->all();\n\nforeach ($customers as $customer) {\n    // SELECT * FROM `order` WHERE `customer_id` = ...\n    $orders = $customer->orders;\n}\n```\n\nComme vous pouvez le constater dans le fragment de code ci-dessus, 101 instruction SQL sont exécutées ! \nCela tient au fait que, à chaque fois que vous accédez à la propriété de relation `orders` d'un objet client différent dans la boucle for, une instruction SQL est exécutée. \n\n\nPour résoudre ce problème de performance, vous pouvez utiliser ce qu'on appelle le *chargement précoce* comme montré ci-dessous :\n\n```php\n// SELECT * FROM `customer` LIMIT 100;\n// SELECT * FROM `orders` WHERE `customer_id` IN (...)\n$customers = Customer::find()\n    ->with('orders')\n    ->limit(100)\n    ->all();\n\nforeach ($customers as $customer) {\n    // aucune instruction SQL exécutée\n    $orders = $customer->orders;\n}\n```\n\nEn appelant [[yii\\db\\ActiveQuery::with()]], vous donner comme instruction à l'enregistrement actif de rapporter les commandes (*orders*) pour les 100 premiers clients (*customers*) en une seule instruction SQL. \nEn conséquence, vous réduisez le nombre d'instructions SQL de 101 à 2 !\n\nVous pouvez charger précocement une ou plusieurs relations. Vous pouvez même charger précocement des *relations imbriquées*. \nUne relation imbriquée est une relation qui est déclarée dans une classe d'enregistrement actif. \nPar exemple, `Customer` est en relation avec `Order` via la relation `orders`, et `Order` est en relation avec `Item` via la relation `items`. \nLorsque vous effectuez une requête pour `Customer`, vous pouvez charger précocement `items` en utilisant la notation de relation imbriquée `orders.items`. \n\nLe code suivant montre différentes utilisations de [[yii\\db\\ActiveQuery::with()|with()]]. \nNous supposons que la classe `Customer` possède deux relations `orders` (commandes) et `country` (pays), tandis que la classe `Order` possède une relation `items`.\n\n```php\n// chargement précoce à la fois de \"orders\" et de \"country\"\n$customers = Customer::find()->with('orders', 'country')->all();\n// équivalent au tableau de syntaxe ci-dessous\n$customers = Customer::find()->with(['orders', 'country'])->all();\n// aucune instruction SQL exécutée \n$orders= $customers[0]->orders;\n// aucune instruction SQL exécutée\n$country = $customers[0]->country;\n\n// chargement précoce de \"orders\" et de la relation imbriquée \"orders.items\"\n$customers = Customer::find()->with('orders.items')->all();\n// accés aux items de la première commande du premier client\n// aucune instruction SQL exécutée\n$items = $customers[0]->orders[0]->items;\n```\n\nVous pouvez charger précocement des relations imbriquées en profondeur, telles que `a.b.c.d`. Toutes les relations parentes sont chargées précocement. \nC'est à dire, que lorsque vous appelez [[yii\\db\\ActiveQuery::with()|with()]] en utilisant `a.b.c.d`, vous chargez précocement `a`, `a.b`, `a.b.c` et `a.b.c.d`.\n\n\n> Info : en général, lors du chargement précoce de `N` relations parmi lesquelles `M` relations sont définies par une [table de jointure](#junction-table), `N+M+1` instructions SQL sont exécutées au total. \nNotez qu'une relation imbriquée `a.b.c.d` possède 4 relations.\n\n\nLorsque vous chargez précocement une relation, vous pouvez personnaliser le requête relationnelle correspondante en utilisant une fonction anonyme. \nPar exemple :\n\n```php\n// trouve les clients et rapporte leur pays et leurs commandes actives \n// SELECT * FROM `customer`\n// SELECT * FROM `country` WHERE `id` IN (...)\n// SELECT * FROM `order` WHERE `customer_id` IN (...) AND `status` = 1\n$customers = Customer::find()->with([\n    'country',\n    'orders' => function ($query) {\n        $query->andWhere(['status' => Order::STATUS_ACTIVE]);\n    },\n])->all();\n```\n\nLors de la personnalisation de la requête relationnelle pour une relation, vous devez spécifier le nom de la relation comme une clé de tableau et utiliser une fonction anonyme comme valeur de tableau correspondante. \nLa fonction anonyme accepte une paramètre `$query` qui représente l'objet [[yii\\db\\ActiveQuery]] utilisé pour effectuer la requête relationnelle pour la relation. \nDans le code ci-dessus, nous modifions la requête relationnelle en ajoutant une condition additionnelle à propos de l'état de la commande (*order*).\n\n\n> Note : si vous appelez [[yii\\db\\Query::select()|select()]] tout en chargeant précocement les relations, vous devez vous assurer que les colonnes référencées dans la déclaration de la relation sont sélectionnées. \n> Autrement, les modèles en relation peuvent ne pas être chargés correctement. \n> Par exemple :\n>\n> ```php\n> $orders = Order::find()->select(['id', 'amount'])->with('customer')->all();\n> // $orders[0]->customer est toujours nul. Pour régler le problème, vous devez faire ce qui suit :\n> $orders = Order::find()->select(['id', 'amount', 'customer_id'])->with('customer')->all();\n> ```\n\n\n### Jointure avec des relations <span id=\"joining-with-relations\"></span>\n\n> Note : le contenu décrit dans cette sous-section ne s'applique qu'aux bases de données relationnelles, telles que MySQL, PostgreSQL, etc.\n\n\nLes requêtes relationnelles que nous avons décrites jusqu'à présent ne font référence qu'aux colonnes de table primaires lorsque nous faisons une requête des données primaires. \nEn réalité, nous avons souvent besoin de faire référence à des colonnes dans les tables en relation. \nPar exemple, vous désirez peut-être rapporter les clients qui ont au moins une commande active. \nPour résoudre ce problème, nous pouvons construire une requête avec jointure comme suit :\n\n```php\n// SELECT `customer`.* FROM `customer`\n// LEFT JOIN `order` ON `order`.`customer_id` = `customer`.`id`\n// WHERE `order`.`status` = 1\n// \n// SELECT * FROM `order` WHERE `customer_id` IN (...)\n$customers = Customer::find()\n    ->select('customer.*')\n    ->leftJoin('order', '`order`.`customer_id` = `customer`.`id`')\n    ->where(['order.status' => Order::STATUS_ACTIVE])\n    ->with('orders')\n    ->all();\n```\n\n> Note : il est important de supprimer les ambiguïtés sur les noms de colonnes lorsque vous construisez les requêtes relationnelles faisant appel à des instructions SQL JOIN. \n> Une pratique courante est de préfixer les noms de colonnes par le nom des tables correspondantes. \n\nNéanmoins, une meilleure approche consiste à exploiter les déclarations de relations existantes en appelant [[yii\\db\\ActiveQuery::joinWith()]] :\n\n```php\n$customers = Customer::find()\n    ->joinWith('orders')\n    ->where(['order.status' => Order::STATUS_ACTIVE])\n    ->all();\n```\n\nLes deux approches exécutent le même jeu d'instructions SQL. La deuxième approche est plus propre et plus légère cependant. \n\nPar défaut, [[yii\\db\\ActiveQuery::joinWith()|joinWith()]] utilise `LEFT JOIN` pour joindre la table primaire avec les tables en relation. \nVous pouvez spécifier une jointure différente (p .ex. `RIGHT JOIN`) via son troisième paramètre `$joinType`. \nSi le type de jointure que vous désirez est `INNER JOIN`, vous pouvez simplement appeler [[yii\\db\\ActiveQuery::innerJoinWith()|innerJoinWith()]], à la place.\n\nL'appel de [[yii\\db\\ActiveQuery::joinWith()|joinWith()]] [charge précocement](#lazy-eager-loading) les données en relation par défaut. \nSi vous ne voulez pas charger les données en relation, vous pouvez spécifier son deuxième paramètre `$eagerLoading` comme étant `false`. \n\n> Note : même en utilisant [[yii\\db\\ActiveQuery::joinWith()|joinWith()]] ou [[yii\\db\\ActiveQuery::innerJoinWith()|innerJoinWith()]]\n  avec le chargement précoce activé les données en relation ne sont **pas** peuplées à partir du résultat de la requête `JOIN`. C'est pourquoi il y a\n  toujours une requête supplémetaire pour chacune des relations jointes comme expliqué à la section [chargement précoce](#lazy-eager-loading).\n\nComme avec [[yii\\db\\ActiveQuery::with()|with()]], vous pouvez joindre une ou plusieurs relations ; vous pouvez personnaliser les requêtes de relation à la volée ; vous pouvez joindre des relations imbriquées ; et vous pouvez mélanger l'utilisation de [[yii\\db\\ActiveQuery::with()|with()]] et celle de [[yii\\db\\ActiveQuery::joinWith()|joinWith()]]. \nPar exemple :\n\n```php\n$customers = Customer::find()->joinWith([\n    'orders' => function ($query) {\n        $query->andWhere(['>', 'subtotal', 100]);\n    },\n])->with('country')\n    ->all();\n```\n\nParfois, en joignant deux tables, vous désirez peut-être spécifier quelques conditions supplémentaires dans la partie `ON` de la requête JOIN. \nCela peut être réalisé en appelant la méthode [[yii\\db\\ActiveQuery::onCondition()]] \ncomme ceci :\n\n```php\n// SELECT `customer`.* FROM `customer`\n// LEFT JOIN `order` ON `order`.`customer_id` = `customer`.`id` AND `order`.`status` = 1 \n// \n// SELECT * FROM `order` WHERE `customer_id` IN (...)\n$customers = Customer::find()->joinWith([\n    'orders' => function ($query) {\n        $query->onCondition(['order.status' => Order::STATUS_ACTIVE]);\n    },\n])->all();\n```\n\nLa requête ci-dessus retourne *tous* les clients, et pour chacun des clients, toutes les commandes actives. \nNotez que cela est différent de notre exemple précédent qui ne retourne que les clients qui ont au moins une commande active. \n\n> Info : quand [[yii\\db\\ActiveQuery]] est spécifiée avec une condition via une jointure [[yii\\db\\ActiveQuery::onCondition()|onCondition()]], la condition est placée dans la partie `ON` si la requête fait appel à une requête JOIN. \nSi la requête ne fait pas appel à JOIN, la *on-condition* est automatiquement ajoutée à la partie `WHERE` de la requête. \nPar conséquent elle peut ne contenir que des conditions incluant des colonnes de la table en relation. \n\n\n#### Alias de table de relation <span id=\"relation-table-aliases\"></span>\n\nComme noté auparavant, lorsque vous utilisez une requête JOIN, vous devez lever les ambiguïtés sur le nom des colonnes. \nPour cela, un alias est souvent défini pour une table. Définir un alias pour la requête relationnelle serait possible en personnalisant le requête de relation de la manière suivante :\n\n```php\n$query->joinWith([\n    'orders' => function ($q) {\n        $q->from(['o' => Order::tableName()]);\n    },\n])\n```\n\nCela paraît cependant très compliqué et implique soit de coder en dur les noms de tables des objets en relation, soit d'appeler `Order::tableName()`. \nDepuis la version 2.0.7, Yii fournit un raccourci pour cela. Vous pouvez maintenant définir et utiliser l'alias pour la table de relation comme ceci :\n\n```php\n// joint la relation orders et trie les résultats par orders.id\n$query->joinWith(['orders o'])->orderBy('o.id');\n```\n\nLa syntaxe ci-dessus ne fonctionne que pour des relations simples. Si vous avez besoin d'un alias pour une table intermédiaire lors de la jointure via des relations imbriquées, p. ex. `$query->joinWith(['orders.product'])`,\nvous devez imbriquer les appels joinWith comme le montre l'exemple suivant :\n\n```php\n$query->joinWith(['orders o' => function($q) {\n        $q->joinWith('product p');\n    }])\n    ->where('o.amount > 100');\n```\n\n\n### Relations inverses <span id=\"inverse-relations\"></span>\n\nLes déclarations de relations sont souvent réciproques entre deux classes d'enregistrement actif. \nPar exemple, `Customer` est en relation avec `Order` via la relation `orders`, et `Order` est en relation inverse avec `Customer` via la relation `customer`.\n\n```php\nclass Customer extends ActiveRecord\n{\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n}\n\nclass Order extends ActiveRecord\n{\n    public function getCustomer()\n    {\n        return $this->hasOne(Customer::class, ['id' => 'customer_id']);\n    }\n}\n```\n\nConsidérons maintenant ce fragment de code :\n\n```php\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123\n$order = $customer->orders[0];\n\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer2 = $order->customer;\n\n// displays \"not the same\"\necho $customer2 === $customer ? 'same' : 'not the same';\n```\n\nOn aurait tendance à penser que `$customer` et `$customer2` sont identiques, mais ils ne le sont pas ! \nEn réalité, ils contiennent les mêmes données de client, mais ce sont des objets différents. \nEn accédant à `$order->customer`, une instruction SQL supplémentaire est exécutée pour remplir un nouvel objet `$customer2`.\n\nPour éviter l'exécution redondante de la dernière instruction SQL dans l'exemple ci-dessus, nous devons dire à Yii que `customer` est une  *relation inverse* de `orders` en appelant la méthode [[yii\\db\\ActiveQuery::inverseOf()|inverseOf()]] comme ci-après :\n\n\n\n```php\nclass Customer extends ActiveRecord\n{\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id'])->inverseOf('customer');\n    }\n}\n```\n\nAvec cette déclaration de relation modifiée, nous avons :\n\n```php\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123\n$order = $customer->orders[0];\n\n// aucune instruction SQL n'est exécutée\n$customer2 = $order->customer;\n\n// affiche \"same\"\necho $customer2 === $customer ? 'same' : 'not the same';\n```\n\n> Note : les relations inverses ne peuvent être définies pour des relations faisant appel à une [table de jointure](#junction-table). \nC'est à dire que, si une relation est définie avec [[yii\\db\\ActiveQuery::via()|via()]] ou avec [[yii\\db\\ActiveQuery::viaTable()|viaTable()]], vous ne devez pas appeler [[yii\\db\\ActiveQuery::inverseOf()|inverseOf()]] ensuite.\n\n\n\n## Sauvegarde des relations <span id=\"saving-relations\"></span>\n\nEn travaillant avec des données relationnelles, vous avez souvent besoin d'établir de créer des relations entre différentes données ou de supprimer des relations existantes. \nCela requiert de définir les valeurs appropriées pour les colonnes qui définissent ces relations.\nEn utilisant l'enregistrement actif, vous pouvez vous retrouver en train d'écrire le code de la façon suivante :\n\n```php\n$customer = Customer::findOne(123);\n$order = new Order();\n$order->subtotal = 100;\n// ...\n\n// défninition de l'attribut qui définit la relation \"customer\" dans Order\n$order->customer_id = $customer->id;\n$order->save();\n```\n\nL'enregistrement actif fournit la méthode [[yii\\db\\ActiveRecord::link()|link()]] qui vous permet d'accomplir cette tâche plus élégamment :\n\n```php\n$customer = Customer::findOne(123);\n$order = new Order();\n$order->subtotal = 100;\n// ...\n\n$order->link('customer', $customer);\n```\n\nLa méthode [[yii\\db\\ActiveRecord::link()|link()]] requiert que vous spécifiiez le nom de la relation et l'instance d'enregistrement actif cible avec laquelle le relation doit être établie. \nLa méthode modifie les valeurs des attributs qui lient deux instances d'enregistrement actif et les sauvegardes dans la base de données. \nDans l'exemple ci-dessus, elle définit l'attribut `customer_id` de l'instance `Order` comme étant la valeur de l'attribut `id` de l'instance `Customer` et le sauvegarde ensuite dans la base de données.\n\n\n\n> Note : vous ne pouvez pas lier deux instances d'enregistrement actif nouvellement créées. \n\nL'avantage d'utiliser [[yii\\db\\ActiveRecord::link()|link()]] \nest même plus évident lorsqu'une relation est définie via une [table de jointure](#junction-table). \nPar exemple, vous pouvez utiliser le code suivant pour lier une instance de `Order` à une instance de `Item` :\n\n```php\n$order->link('items', $item);\n```\n\nLe code ci-dessus insère automatiquement une ligne dans la table de jointure `order_item` pour mettre la commande en relation avec l'item. \n\n> Info : la méthode [[yii\\db\\ActiveRecord::link()|link()]] n'effectue AUCUNE validation de données lors de la sauvegarde de l'instance d'enregistrement actif affectée. \n> Il est de votre responsabilité de valider toutes les données entrées avant d'appeler cette méthode. \n\n\nL'opération opposée à [[yii\\db\\ActiveRecord::link()|link()]] est [[yii\\db\\ActiveRecord::unlink()|unlink()]] \nqui casse une relation existante entre deux instances d'enregistrement actif. Par exemple :\n\n```php\n$customer = Customer::find()->with('orders')->where(['id' => 123])->one();\n$customer->unlink('orders', $customer->orders[0]);\n```\n\nPar défaut, la méthode [[yii\\db\\ActiveRecord::unlink()|unlink()]] définit la valeur de la (des) clé(s) étrangères qui spécifie(nt) la relation existante à `null`. \nVous pouvez cependant, choisir de supprimer la ligne de la table qui contient la valeur de clé étrangère en passant à la méthode la valeur `true` pour le paramètre `$delete`. \n \n\nLorsqu'une table de jointure est impliquée dans une relation, appeler [[yii\\db\\ActiveRecord::unlink()|unlink()]] provoque l'effacement des clés étrangères dans la table de jointure, ou l'effacement de la ligne correspondante dans la table de jointure si `#delete` vaut `true`.\n\n\n\n\n## Relations inter bases de données <span id=\"cross-database-relations\"></span> \n\nL'enregistrement actif vous permet de déclarer des relations entre les classes d'enregistrement actif qui sont mise en œuvre par différentes bases de données. \nLes bases de données peuvent être de types différents (p. ex. MySQL and PostgreSQL, ou MS SQL et MongoDB), et elles peuvent s'exécuter sur des serveurs différents. \nVous pouvez utiliser la même syntaxe pour effectuer des requêtes relationnelles. Par exemple :\n\n```php\n// Customer est associé à la table \"customer\" dans la base de données relationnelle (e.g. MySQL)\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public static function tableName()\n    {\n        return 'customer';\n    }\n\n    public function getComments()\n    {\n        // a customer has many comments\n        return $this->hasMany(Comment::class, ['customer_id' => 'id']);\n    }\n}\n\n// Comment est associé à la collection \"comment\" dans une base de données MongoDB\nclass Comment extends \\yii\\mongodb\\ActiveRecord\n{\n    public static function collectionName()\n    {\n        return 'comment';\n    }\n\n    public function getCustomer()\n    {\n        // un commentaire (comment) a un client (customer)\n        return $this->hasOne(Customer::class, ['id' => 'customer_id']);\n    }\n}\n\n$customers = Customer::find()->with('comments')->all();\n```\n\nVous pouvez utiliser la plupart des fonctionnalités de requêtes relationnelles qui ont été décrites dans cette section.\n \n> Note : l'utilisation de [[yii\\db\\ActiveQuery::joinWith()|joinWith()]] est limitée aux bases de données qui permettent les requête JOIN inter bases. \nPour cette raison, vous ne pouvez pas utiliser cette méthode dans l'exemple ci-dessus car MongoDB ne prend pas JOIN en charge. \n\n\n## Personnalisation des classes de requête <span id=\"customizing-query-classes\"></span>\n\nPar défaut, toutes les requêtes d'enregistrement actif sont prises en charge par [[yii\\db\\ActiveQuery]]. \nPour utiliser une classe de requête personnalisée dans une classe d'enregistrement actif, vous devez redéfinir la méthode [[yii\\db\\ActiveRecord::find()]] et retourner une instance de votre classe de requête personnalisée .\nPar exemple :\n \n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\db\\ActiveQuery;\n\nclass Comment extends ActiveRecord\n{\n    public static function find()\n    {\n        return new CommentQuery(get_called_class());\n    }\n}\n```\n\nDésormais, à chaque fois que vous effectuez une requête (p. ex. `find()`, `findOne()`) ou définissez une relation (p. ex. `hasOne()`) avec `Comment`, vous travaillez avec une instance de `CommentQuery` au lieu d'une instance d'`ActiveQuery`.\n\n\nVous devez maintenant définir la classe `CommentQuery`, qui peut être personnalisée de maintes manières créatives pour améliorer votre expérience de la construction de requête. Par exemple :\n\n```php\n// fichier CommentQuery.php\nnamespace app\\models;\n\nuse yii\\db\\ActiveQuery;\n\nclass CommentQuery extends ActiveQuery\n{\n    // conditions ajoutées par défaut (peut être sauté)\n    public function init()\n    {\n        $this->andOnCondition(['deleted' => false]);\n        parent::init();\n    }\n\n    // ... ajoutez vos méthodes de requêtes personnalisées ici ...\n\n    public function active($state = true)\n    {\n        return $this->andOnCondition(['active' => $state]);\n    }\n}\n```\n\n> Note : au lieu d'appeler [[yii\\db\\ActiveQuery::onCondition()|onCondition()]], vous devez généralement appeler\n  [[yii\\db\\ActiveQuery::andOnCondition()|andOnCondition()]] ou [[yii\\db\\ActiveQuery::orOnCondition()|orOnCondition()]]\n  pour ajouter des conditions supplémentaires lors de la définition de nouvelles méthodes de requête de façon à ce que aucune condition existante en soit redéfinie.\n\nCela vous permet d'écrire le code de construction de requête comme suit :\n\n```php\n$comments = Comment::find()->active()->all();\n$inactiveComments = Comment::find()->active(false)->all();\n```\n\n> Astuce : dans les gros projets, il est recommandé que vous utilisiez des classes de requête personnalisées pour contenir la majeure partie de code relatif aux requêtes de manière à ce que les classe d'enregistrement actif puissent être maintenues propres. \n\n\nVous pouvez aussi utiliser les méthodes de construction de requêtes en définissant des relations avec `Comment` ou en effectuant une requête relationnelle : \n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public function getActiveComments()\n    {\n        return $this->hasMany(Comment::class, ['customer_id' => 'id'])->active();\n    }\n}\n\n$customers = Customer::find()->joinWith('activeComments')->all();\n\n// ou en alternative\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public function getComments()\n    {\n        return $this->hasMany(Comment::class, ['customer_id' => 'id']);\n    }\n}\n\n$customers = Customer::find()->joinWith([\n    'comments' => function($q) {\n        $q->active();\n    }\n])->all();\n```\n\n> Info : dans Yii 1.1, il existe un concept appelé *scope*. \n> Scope n'est plus pris en charge directement par Yii 2.0, et vous devez utiliser des classes de requête personnalisées et des méthodes de requêtes pour remplir le même objectif. \n\n\n## Sélection de champs supplémentaires\n\nQuand un enregistrement actif est rempli avec les résultats d'une requête, ses attributs sont remplis par les valeurs des colonnes correspondantes du jeu de données reçu. \n\n\nIl vous est possible d'aller chercher des colonnes ou des valeurs additionnelles à partir d'une requête et des les stocker dans l'enregistrement actif. \nPar exemple, supposons que nous ayons une table nommée `room`, qui contient des informations sur les chambres (rooms) disponibles dans l'hôtel. \nChacune des chambres stocke des informations sur ses dimensions géométriques en utilisant des champs `length` (longueur), `width` (largeur) , `height` (hauteur). \nImaginons que vous ayez besoin de retrouver une liste des chambres disponibles en les classant par volume décroissant. \nVous ne pouvez pas calculer le volume en PHP parce que vous avez besoin de trier les enregistrements par cette valeur, mais vous voulez peut-être aussi que `volume` soit affiché dans la liste. \nPour atteindre ce but, vous devez déclarer un champ supplémentaire dans la classe d'enregistrement actif `Room` qui contiendra la valeur de `volume` :\n\n\n```php\nclass Room extends \\yii\\db\\ActiveRecord\n{\n    public $volume;\n\n    // ...\n}\n```\n\nEnsuite, vous devez composer une requête qui calcule le volume de la chambre et effectue le tri : \n\n```php\n$rooms = Room::find()\n    ->select([\n        '{{room}}.*', // selectionne toutes les colonnes \n        '([[length]] * [[width]] * [[height]]) AS volume', // calcule un volume\n    ])\n    ->orderBy('volume DESC') // applique le tri\n    ->all();\n\nforeach ($rooms as $room) {\n    echo $room->volume; // contient la valeur calculée par SQL\n}\n```\n\nLa possibilité de sélectionner des champs supplémentaires peut être exceptionnellement utile pour l'agrégation de requêtes. \nSupposons que vous ayez besoin d'afficher une liste des clients avec le nombre total de commandes qu'ils ont passées. \nTout d'abord, vous devez déclarer une classe `Customer` avec une relation `orders` et un champ supplémentaire pour le stockage du nombre de commandes :\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public $ordersCount;\n\n    // ...\n\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n}\n```\n\nEnsuite vous pouvez composer une requête qui joint les commandes et calcule leur nombre :\n\n```php\n$customers = Customer::find()\n    ->select([\n        '{{customer}}.*', // selectionne tous les champs de customer \n        'COUNT({{order}}.id) AS ordersCount' // calcule le nombre de commandes (orders)\n    ])\n    ->joinWith('orders') // garantit la jointure de la table\n    ->groupBy('{{customer}}.id') // groupe les résultats pour garantir que la fonction d'agrégation fonctionne \n    ->all();\n```\n\nUn inconvénient de l'utilisation de cette méthode est que si l'information n'est pas chargée dans la requête SQL, elle doit être calculée séparément.\nPar conséquent, si vous avez trouvé un enregistrement particulier via une requête régulière sans instruction de sélection supplémentaire, il ne pourra retourner la valeur réelle du champ supplémentaire. \nLa même chose se produit pour l'enregistrement nouvellement sauvegardé. \n\n```php\n$room = new Room();\n$room->length = 100;\n$room->width = 50;\n$room->height = 2;\n\n$room->volume; // cette valeur est `null` puisqu'elle n'a pas encore été déclarée\n```\n\nEn utilisant les méthodes magiques [[yii\\db\\BaseActiveRecord::__get()|__get()]] et [[yii\\db\\BaseActiveRecord::__set()|__set()]] nous pouvons émuler le comportement d'une propriété :\n\n\n```php\nclass Room extends \\yii\\db\\ActiveRecord\n{\n    private $_volume;\n    \n    public function setVolume($volume)\n    {\n        $this->_volume = (float) $volume;\n    }\n    \n    public function getVolume()\n    {\n        if (empty($this->length) || empty($this->width) || empty($this->height)) {\n            return null;\n        }\n        \n        if ($this->_volume === null) {\n            $this->setVolume(\n                $this->length * $this->width * $this->height\n            );\n        }\n        \n        return $this->_volume;\n    }\n\n    // ...\n}\n```\n\nLorsque la requête *select* ne fournit pas le volume, le modèle est capable de le calculer automatiquement en utilisant les attributs du modèle. \n\n\nVous pouvez aussi bien calculer les champs agrégés en utilisant les relations définies :\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    private $_ordersCount;\n    \n    public function setOrdersCount($count)\n    {\n        $this->_ordersCount = (int) $count;\n    }\n    \n    public function getOrdersCount()\n    {\n        if ($this->isNewRecord) {\n            return null; // cela évite d'appeler une requête pour chercher une clé primaire nulle \n        }\n        \n        if ($this->_ordersCount === null) {\n            $this->setOrdersCount($this->getOrders()->count()); // calcule l'aggrégation à la demande à partir de la relation\n        }\n\n        return $this->_ordersCount;\n    }\n\n    // ...\n\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n}\n```\n\nAvec ce code, dans le cas où 'ordersCount' est présent dans l'instruction 'select'  - `Customer::ordersCount` est peuplé\npar les résultats de la requête, autrement il est calculé à la demande en utilisant la relation  `Customer::orders`.\n\nCette approche peut aussi bien être utilisée pour la création de raccourcis pour certaines données relationnelles, en particulier pour l'aggrégation. \nPar exemple :\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    /**\n     * Definit une propriété en lecture seule pour les données agrégées.\n     */\n    public function getOrdersCount()\n    {\n        if ($this->isNewRecord) {\n            return null; // ceci évite l'appel d'une requête pour cherche une clé primaire nulle\n        }\n        \n        return empty($this->ordersAggregation) ? 0 : $this->ordersAggregation[0]['counted'];\n    }\n\n    /**\n     * Déclere une relation 'orders' normale.\n     */\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n\n    /**\n     * Déclare une nouvelle relation basée sur 'orders', qui fournit l'agrégation.\n     */\n    public function getOrdersAggregation()\n    {\n        return $this->getOrders()\n            ->select(['customer_id', 'counted' => 'count(*)'])\n            ->groupBy('customer_id')\n            ->asArray(true);\n    }\n\n    // ...\n}\n\nforeach (Customer::find()->with('ordersAggregation')->all() as $customer) {\n    echo $customer->ordersCount; // fournit les données agrégées à partir de la relation sans requête supplémentaire grâce au chargement précoce. \n}\n\n$customer = Customer::findOne($pk);\n$customer->ordersCount; // fournit les données agrégées à partir de la relation paresseuse chargée\n\n"
  },
  {
    "path": "docs/guide-fr/db-dao.md",
    "content": "Objets d'accès aux bases de données\n===================================\n\nConstruits au-dessus des [objets de bases de données PHP (PDO – PHP Data Objects)](https://www.php.net/manual/fr/book.pdo.php), les objets d'accès aux bases de données de Yii (DAO – Database Access Objects) fournissent une API orientée objets pour accéder à des bases de données relationnelles. C'est la fondation pour d'autres méthodes d'accès aux bases de données plus avancées qui incluent le [constructeur de requêtes (*query builder*)](db-query-builder.md) et l'[enregistrement actif (*active record*)](db-active-record.md).\n\nLorsque vous utilisez les objets d'accès aux bases de données de Yii, vous manipulez des requêtes SQL et des tableaux PHP. En conséquence, cela reste le moyen le plus efficace pour accéder aux bases de données. Néanmoins, étant donné que la syntaxe du langage SQL varie selon le type de base de données, l'utilisation des objets d'accès aux bases de données de Yii signifie également que vous avez à faire un travail supplémentaire pour créer une application indifférente au type de base de données.\n\nDans Yii 2.0, les objets d'accès aux bases de données prennent en charge les bases de données suivantes sans configuration supplémentaire :\n\n- [MySQL](https://www.mysql.com/)\n- [MariaDB](https://mariadb.com/)\n- [SQLite](https://sqlite.org/)\n- [PostgreSQL](https://www.postgresql.org/): version 8.4 ou plus récente.\n- [CUBRID](https://www.cubrid.org/): version 9.3 ou plus récente.\n- [Oracle](https://www.oracle.com/database/)\n- [MSSQL](https://www.microsoft.com/en-us/sqlserver/default.aspx): version 2008 ou plus récente.\n\n> Info: depuis Yii 2.1, la prise en charge des objets d'accès aux bases de données pour CUBRID, Oracle et MSSQL n'est plus fournie en tant que composants du noyau. Cette prise en charge nécessite l'installation d'[extensions](structure-extensions.md) séparées.\nParmi les [extensions officielles](https://www.yiiframework.com/extensions/official), on trouve [yiisoft/yii2-oracle](https://www.yiiframework.com/extension/yiisoft/yii2-oracle) et\n[yiisoft/yii2-mssql](https://www.yiiframework.com/extension/yiisoft/yii2-mssql).\n\n> Note: la nouvelle version de pdo_oci pour PHP 7 n'existe pour le moment que sous forme de code source. Suivez les [instructions de la communauté](https://github.com/yiisoft/yii2/issues/10975#issuecomment-248479268)\npour la compiler ou utilisez [la couche d'émulation de PDO](https://github.com/taq/pdooci).\n\n\n## Création de connexions à une base de données <span id=\"creating-db-connections\"></span>\n\nPour accéder à une base de données, vous devez d'abord vous y connecter en créant une instance de la classe [[yii\\db\\Connection]] :\n\n```php\n$db = new yii\\db\\Connection([\n    'dsn' => 'mysql:host=localhost;dbname=example',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n]);\n```\n\nComme souvent vous devez accéder à une base de données en plusieurs endroits, une pratique commune est de la configurer en terme de [composant d'application ](structure-application-components.md) comme ci-après :\n\n```php\nreturn [\n    // ...\n    'components' => [\n        // ...\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=example',\n            'username' => 'root',\n            'password' => '',\n            'charset' => 'utf8',\n        ],\n    ],\n    // ...\n];\n```\n\nVous pouvez ensuite accéder à la base de données via l'expression `Yii::$app->db`.\n\n> Tip: vous pouvez configurer plusieurs composants d'application « base de données » si votre application a besoin d'accéder à plusieurs bases de données.\n\nLorsque vous configurez une connexion à une base de données, vous devez toujours spécifier le nom de sa source de données (DSN – Data Source Name) via la propriété [[yii\\db\\Connection::dsn|dsn]]. Les formats des noms de source de données varient selon le type de base de données. Reportez-vous au [manuel de PHP](https://www.php.net/manual/fr/pdo.construct.php) pour plus de détails. Ci-dessous, nous donnons quelques exemples :\n\n* MySQL, MariaDB: `mysql:host=localhost;dbname=mydatabase`\n* SQLite: `sqlite:/path/to/database/file`\n* PostgreSQL: `pgsql:host=localhost;port=5432;dbname=mydatabase`\n* CUBRID: `cubrid:dbname=demodb;host=localhost;port=33000`\n* MS SQL Server (via sqlsrv driver): `sqlsrv:Server=localhost;Database=mydatabase`\n* MS SQL Server (via dblib driver): `dblib:host=localhost;dbname=mydatabase`\n* MS SQL Server (via mssql driver): `mssql:host=localhost;dbname=mydatabase`\n* Oracle: `oci:dbname=//localhost:1521/mydatabase`\n\nNotez que si vous vous connectez à une base de données en utilisant ODBC (Open Database Connectivity), vous devez configurer la propriété [[yii\\db\\Connection::driverName]] afin que Yii connaisse le type réel de base de données. Par exemple :\n\n```php\n'db' => [\n    'class' => 'yii\\db\\Connection',\n    'driverName' => 'mysql',\n    'dsn' => 'odbc:Driver={MySQL};Server=localhost;Database=test',\n    'username' => 'root',\n    'password' => '',\n],\n```\n\nEn plus de la propriété [[yii\\db\\Connection::dsn|dsn]], vous devez souvent configurer les propriétés [[yii\\db\\Connection::username|username (nom d'utilisateur)]] et [[yii\\db\\Connection::password|password (mot de passe)]]. Reportez-vous à [[yii\\db\\Connection]] pour une liste exhaustive des propriétés configurables.\n\n> Info: lorsque vous créez une instance de connexion à une base de données, la connexion réelle à la base de données n'est pas établie tant que vous n'avez pas exécuté la première requête SQL ou appelé la méthode [[yii\\db\\Connection::open()|open()]] explicitement.\n\n> Tip: parfois, vous désirez effectuer quelques requêtes juste après l'établissement de la connexion à la base de données pour initialiser quelques variables d'environnement (p. ex. pour définir le fuseau horaire ou le jeu de caractères). Vous pouvez le faire en enregistrant un gestionnaire d'événement pour l'événement [[yii\\db\\Connection::EVENT_AFTER_OPEN|afterOpen]] de la connexion à la base de données. Vous pouvez enregistrer le gestionnaire directement dans la configuration de l'application comme ceci :\n>\n> ```php\n> 'db' => [\n>     // ...\n>     'on afterOpen' => function($event) {\n>         // $event->sender refers to the DB connection\n>         $event->sender->createCommand(\"SET time_zone = 'UTC'\")->execute();\n>     }\n> ],\n> ```\n\n\n## Execution de requêtes SQL <span id=\"executing-sql-queries\"></span>\n\nUne fois que vous avez une instance de connexion à la base de données, vous pouvez exécuter une requête SQL en suivant les étapes suivantes :\n\n1. Créer une [[yii\\db\\Command|commande]] avec une requête SQL simple ;\n2. Lier les paramètres (facultatif);\n3. Appeler l'une des méthodes d'exécution SQL dans la [[yii\\db\\Command|commande]].\n\nL'exemple qui suit montre différentes façons d'aller chercher des données dans une base de données :\n\n```php\n// retourne un jeu de lignes. Chaque ligne est un tableau associatif (couples clé-valeur) dont les clés sont des noms de colonnes \n// retourne un tableau vide si la requête ne retourne aucun résultat\n$posts = Yii::$app->db->createCommand('SELECT * FROM post')\n            ->queryAll();\n\n// retourne une ligne unique (la première ligne) \n// retourne false si la requête ne retourne aucun résultat\n$post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=1')\n           ->queryOne();\n\n// retourne une colonne unique (la première colonne) \n//retourne un tableau vide si la requête ne retourne aucun résultat\n$titles = Yii::$app->db->createCommand('SELECT title FROM post')\n             ->queryColumn();\n\n// retourne une valeur scalaire\n// retourne false si la requête ne retourne aucun résultat\n$count = Yii::$app->db->createCommand('SELECT COUNT(*) FROM post')\n             ->queryScalar();\n```\n\n> Note: pour préserver la précision, les données extraites des bases de données sont toutes représentées sous forme de chaînes de caractères, même si les colonnes sont de type numérique.\n\n\n### Liaison des paramètres <span id=\"binding-parameters\"></span>\n\nLorsque vous créez une commande de base de données à partir d'une requête SQL avec des paramètres, vous devriez presque toujours utiliser l'approche de liaison des paramètres pour éviter les attaques par injection SQL. Par exemple :\n\n```php\n$post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status')\n           ->bindValue(':id', $_GET['id'])\n           ->bindValue(':status', 1)\n           ->queryOne();\n```\n\nDans l'instruction SQL, vous pouvez incorporer une ou plusieurs valeurs à remplacer pour les paramètres (p. ex. `:id` dans l'exemple ci-dessus). Une valeur à remplacer pour un paramètre doit être une chaîne de caractères commençant par le caractère deux-points `:`. Vous pouvez ensuite appeler l'une des méthodes de liaison de paramètres suivantes pour lier les valeurs de paramètre :\n\n* [[yii\\db\\Command::bindValue()|bindValue()]]: lie une unique valeur de paramètre\n* [[yii\\db\\Command::bindValues()|bindValues()]]: lie plusieurs valeurs de paramètre en un seul appel\n* [[yii\\db\\Command::bindParam()|bindParam()]]: similaire à [[yii\\db\\Command::bindValue()|bindValue()]] mais prend aussi en charge la liaison de références à des paramètres\n\nL'exemple suivant montre les manières alternatives de lier des paramètres :\n\n```php\n$params = [':id' => $_GET['id'], ':status' => 1];\n\n$post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status')\n           ->bindValues($params)\n           ->queryOne();\n           \n$post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status', $params)\n           ->queryOne();\n```\n\nLa liaison des paramètres est implémentée via des [instructions préparées](https://www.php.net/manual/fr/mysqli.quickstart.prepared-statements.php). En plus d'empêcher les attaques par injection SQL, cela peut aussi améliorer la performance en préparant l'instruction SQL une seule fois et l'exécutant de multiples fois avec des paramètres différents. Par exemple :\n\n```php\n$command = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id');\n\n$post1 = $command->bindValue(':id', 1)->queryOne();\n$post2 = $command->bindValue(':id', 2)->queryOne();\n// ...\n```\n\nComme la méthode [[yii\\db\\Command::bindParam()|bindParam()]] prend en charge la liaison des paramètres par référence, le code ci-dessus peut aussi être écrit comme suit :\n\n```php\n$command = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id')\n              ->bindParam(':id', $id);\n\n$id = 1;\n$post1 = $command->queryOne();\n\n$id = 2;\n$post2 = $command->queryOne();\n// ...\n```\n\nNotez que vous devez lier la valeur à remplacer à la variable `$id` avant l'exécution, et ensuite changer la valeur de cette variable avant chacune des exécutions subséquentes (cela est souvent réalisé dans une boucle). L'exécution de requêtes de cette façon peut être largement plus efficace que d'exécuter une nouvelle requête pour chacune des valeurs du paramètre).\n\n> Info: la liaison de paramètres n'est utilisée qu'en des endroits où les valeurs doivent être insérées dans des chaînes de caractères qui contiennent du langage SQL.\n> Dans de nombreux endroits dans des couches plus abstraites comme le [query builder](db-query-builder.md) (constructeur de requêtes) et [active record](db-active-record.md) (enregistrement actif)\n> vous spécifiez souvent un tableau de valeurs qui est transformé en SQL. À ces endroits, la liaison de paramètres est assurée par Yii en interne. Il n'est donc pas nécessaire de spécifier ces paramètres manuellement.\n\n\n### Exécution de requête sans sélection <span id=\"non-select-queries\"></span>\n\nLes méthodes `queryXyz()` introduites dans les sections précédentes concernent toutes des requêtes SELECT qui retournent des données de la base de données. Pour les instructions qui ne retournent pas de donnée, vous devez appeler la méthode [[yii\\db\\Command::execute()]] à la place. Par exemple :\n\n```php\nYii::$app->db->createCommand('UPDATE post SET status=1 WHERE id=1')\n   ->execute();\n```\n\nLa méthode [[yii\\db\\Command::execute()]] exécute retourne le nombre de lignes affectées par l'exécution de la requête SQL.\n\nPour les requeêtes INSERT, UPDATE et DELETE, au lieu d'écrire des instructions SQL simples, vous pouvez appeler les méthodes [[yii\\db\\Command::insert()|insert()]], [[yii\\db\\Command::update()|update()]] ou [[yii\\db\\Command::delete()|delete()]], respectivement, pour construire les instructions SQL correspondantes. Ces méthodes entourent correctement les noms de tables et de colonnes par des marques de citation et lient les paramètres. Par exemple :\n\n```php\n// INSERT (table name, column values)\nYii::$app->db->createCommand()->insert('user', [\n    'name' => 'Sam',\n    'age' => 30,\n])->execute();\n\n// UPDATE (table name, column values, condition)\nYii::$app->db->createCommand()->update('user', ['status' => 1], 'age > 30')->execute();\n\n// DELETE (table name, condition)\nYii::$app->db->createCommand()->delete('user', 'status = 0')->execute();\n```\n\nVous pouvez aussi appeler [[yii\\db\\Command::batchInsert()|batchInsert()]] pour insérer plusieurs lignes en un seul coup, ce qui est bien plus efficace que d'insérer une ligne à la fois :\n\n```php\n// noms de table, noms de colonne, valeurs de colonne\nYii::$app->db->createCommand()->batchinsère('user', ['name', 'age'], [\n    ['Tom', 30],\n    ['Jane', 20],\n    ['Linda', 25],\n])->execute();\n```\nUne autre méthode utile est [[yii\\db\\Command::upsert()|upsert()]]. Upsert est une opération atomique qui insère des lignes dans une table de base de données si elles n'existent pas déjà (répondant à une contrainte unique), ou les mets à jour si elles existent :\n\n```php\nYii::$app->db->createCommand()->upsert('pages', [\n    'name' => 'Front page',\n    'url' => 'https://example.com/', // url is unique\n    'visits' => 0,\n], [\n    'visits' => new \\yii\\db\\Expression('visits + 1'),\n], $params)->execute();\n```\n\nLe code ci-dessus, soit insère un enregistrement pour une nouvelle page, soit incrémente sont compteur de visite automatiquement.\n\nNotez que les méthodes mentionnées ci-dessus ne font que créer les requêtes, vous devez toujours appeler [[yii\\db\\Command::execute()|execute()]] pour les exécuter réellement.\n\n\n## Entourage de noms de table et de colonne par des marque de citation <span id=\"quoting-table-and-column-names\"></span>\n\nLorsque l'on écrit du code indifférent au type de base de données, entourer correctement les noms table et de colonne avec des marques de citation (p. ex. guillemets ou simple apostrophe) et souvent un casse-tête parce que les différentes base de données utilisent des règles de marquage de citation différentes. Pour vous affranchir de cette difficulté, vous pouvez utiliser la syntaxe de citation introduite par Yii :\n\n* `[[column name]]`: entourez un nom de colonne qui doit recevoir les marques de citation par des doubles crochets ;\n* `{{table name}}`: entourez un nom de table qui doit recevoir les marques de citation par des doubles accolades ;\n\nLes objets d'accès aux base de données de Yii convertissent automatiquement de telles constructions en les noms de colonne ou de table correspondants en utilisant la syntaxe spécifique au système de gestion de la base de données. Par exemple :\n\n```php\n// exécute cette instruction SQL pour MySQL: SELECT COUNT(`id`) FROM `employee`\n$count = Yii::$app->db->createCommand(\"SELECT COUNT([[id]]) FROM {{employee}}\")\n            ->queryScalar();\n```\n\n\n### Utilisation des préfixes de table <span id=\"using-table-prefix\"></span>\n\nLa plupart des noms de table de base de données partagent un préfixe commun. Vous pouvez utiliser la fonctionnalité de gestion du préfixe de noms de table procurée par les objets d'accès aux bases de données de Yii.\n\nTout d'abord, spécifiez un préfixe de nom de table via la propriété [[yii\\db\\Connection::tablePrefix]] dans la configuration de l'application :\n\n```php\nreturn [\n    // ...\n    'components' => [\n        // ...\n        'db' => [\n            // ...\n            'tablePrefix' => 'tbl_',\n        ],\n    ],\n];\n```\n\nEnsuite dans votre code, à chaque fois que vous faites référence à une table dont le nom commence par ce préfixe, utilisez la syntaxe `{{%table_name}}`. Le caractère pourcentage `%` est automatiquement remplacé par le préfixe que vous avez spécifié dans la configuration de la connexion à la base de données. Par exemple :\n\n```php\n// exécute cette instruction SQL pour MySQL: SELECT COUNT(`id`) FROM `tbl_employee`\n$count = Yii::$app->db->createCommand(\"SELECT COUNT([[id]]) FROM {{%employee}}\")\n            ->queryScalar();\n```\n\n\n## Réalisation de transactions <span id=\"performing-transactions\"></span>\n\nLorsque vous exécutez plusieurs requêtes liées en séquence, il arrive que vous ayez besoin de les envelopper dans une transactions pour garantir l'intégrité et la cohérence de votre base de données. Si une des requêtes échoue, la base de données est ramenée en arrière dans l'état dans lequel elle se trouvait avant qu'aucune de ces requêtes n'ait été exécutée.\n\nLe code suivant montre une façon typique d'utiliser les transactions :\n\n```php\nYii::$app->db->transaction(function($db) {\n    $db->createCommand($sql1)->execute();\n    $db->createCommand($sql2)->execute();\n    // ... exécution des autres instruction SQL ...\n});\n```\n\nLe code précédent est équivalent à celui qui suit, et qui vous donne plus de contrôle sur le code de gestion des erreurs :\n\n```php\n$db = Yii::$app->db;\n$transaction = $db->beginTransaction();\ntry {\n    $db->createCommand($sql1)->execute();\n    $db->createCommand($sql2)->execute();\n    // ... exécutions des autres instructions SQL  ...\n    \n    $transaction->commit();\n} catch(\\Exception $e) {\n    $transaction->rollBack();\n    throw $e;\n} catch(\\Throwable $e) {\n    $transaction->rollBack();\n    throw $e;\n}\n```\n\nEn appelant la méthode [[yii\\db\\Connection::beginTransaction()|beginTransaction()]], une nouvelle transaction est démarrée. La transaction est représentée sous forme d'objet [[yii\\db\\Transaction]] stocké dans la variable `$transaction`. Ensuite, les requêtes à exécuter sont placées dans un bloc `try...catch...`. Si toutes les requêtes réussissent, la méthode [[yii\\db\\Transaction::commit()|commit()]] est appelée pour entériner la transaction. Autrement, si une exception a été levée et capturée, la méthode [[yii\\db\\Transaction::rollBack()|rollBack()]] est appelée pour défaire les changements faits par les requêtes de la transaction antérieures à celle qui a échoué. `throw $e` est alors à nouveau exécutée comme si l'exception n'avait jamais été capturée, ce qui permet au processus normal de gestion des erreurs de s'en occuper.\n\n> Note: dans le code précédent nous avons deux blocs « catch » pour compatibilité\n> avec PHP 5.x et PHP 7.x. `\\Exception` met en œuvre l'[interface `\\Throwable`](https://www.php.net/manual/fr/class.throwable.php)\n> depuis PHP 7.0, ainsi vous pouvez sauter la partie avec `\\Exception` si votre application utilise seulement PHP 7.0 et plus récent.\n\n### Spécification de niveaux d'isolation <span id=\"specifying-isolation-levels\"></span>\n\nYii prend aussi en charge la définition de [niveaux d'isolation] pour vos transactions. Par défaut, lors du démarrage d'une nouvelle transaction, il utilise le niveau d'isolation par défaut défini par votre système de base de données. Vous pouvez redéfinir le niveau d'isolation comme indiqué ci-après :\n\n```php\n$isolationLevel = \\yii\\db\\Transaction::REPEATABLE_READ;\n\nYii::$app->db->transaction(function ($db) {\n    ....\n}, $isolationLevel);\n \n// ou alternativement\n\n$transaction = Yii::$app->db->beginTransaction($isolationLevel);\n```\n\nYii fournit quatre constantes pour les niveaux d'isolation les plus courants :\n\n- [[\\yii\\db\\Transaction::READ_UNCOMMITTED]] – le niveau le plus faible, des lectures sales (*dirty reads*) , des lectures non répétables) (*non-repeatable reads*) et des lectures fantômes (*phantoms*) peuvent se produire.\n- [[\\yii\\db\\Transaction::READ_COMMITTED]] – évite les lectures sales.\n- [[\\yii\\db\\Transaction::REPEATABLE_READ]] – évite les lectures sales et les lectures non répétables.\n- [[\\yii\\db\\Transaction::SERIALIZABLE]] – le niveau le plus élevé, évite tous les problèmes évoqués ci-dessus.\n\n\n\nEn plus de l'utilisation des constantes présentées ci-dessus pour spécifier un niveau d'isolation, vous pouvez également utiliser des chaînes de caractères avec une syntaxe valide prise en charges par le système de gestion de base de données que vous utilisez. Par exemple, dans PostgreSQL, vous pouvez utiliser `\"SERIALIZABLE READ ONLY DEFERRABLE\"`.\n\nNotez que quelques systèmes de gestion de base de données autorisent la définition des niveaux d'isolation uniquement au niveau de la connexion tout entière. Toutes les transactions subséquentes auront donc le même niveau d'isolation même si vous n'en spécifiez aucun. En utilisant cette fonctionnalité, vous avez peut-être besoin de spécifier le niveau d'isolation de manière explicite pour éviter les conflits de définition. Au moment d'écrire ces lignes, seules MSSQL et SQLite sont affectées par cette limitation.\n\n> Note: SQLite ne prend en charge que deux niveaux d'isolation, c'est pourquoi vous ne pouvez utiliser que `READ UNCOMMITTED` et `SERIALIZABLE`. L'utilisation d'autres niveaux provoque la levée d'une exception.\n\n> Note: PostgreSQL n'autorise pas la définition du niveau d'isolation tant que la transaction n'a pas démarré, aussi ne pouvez-vous pas spécifier le niveau d'isolation directement en démarrant la transaction. Dans ce cas, vous devez appeler [[yii\\db\\Transaction::setIsolationLevel()]] après que la transaction a démarré.\n\n[isolation levels]: https://fr.wikipedia.org/wiki/Isolation_(informatique)\n\n\n### Imbrication des transactions <span id=\"nesting-transactions\"></span>\n\nSi votre système de gestion de base de données prend en charge Savepoint, vous pouvez imbriquer plusieurs transactions comme montré ci-dessous :\n\n```php\nYii::$app->db->transaction(function ($db) {\n    //  transaction extérieure\n    \n    $db->transaction(function ($db) {\n        //  transaction intérieure\n    });\n});\n```\n\nOu en alternative,\n```php\n$db = Yii::$app->db;\n$outerTransaction = $db->beginTransaction();\ntry {\n    $db->createCommand($sql1)->execute();\n\n    $innerTransaction = $db->beginTransaction();\n    try {\n        $db->createCommand($sql2)->execute();\n        $innerTransaction->commit();\n    } catch (\\Exception $e) {\n        $innerTransaction->rollBack();\n        throw $e;\n    } catch (\\Throwable $e) {\n        $innerTransaction->rollBack();\n        throw $e;\n    }\n\n    $outerTransaction->commit();\n} catch (\\Exception $e) {\n    $outerTransaction->rollBack();\n    throw $e;\n} catch (\\Throwable $e) {\n    $outerTransaction->rollBack();\n    throw $e;\n}\n```\n\n\n## Réplication et éclatement lecture-écriture <span id=\"read-write-splitting\"></span>\n\nBeaucoup de systèmes de gestion de bases de données prennent en charge la [réplication de la base de données](https://fr.wikipedia.org/wiki/R%C3%A9plication_(informatique)) pour obtenir une meilleure disponibilité et des temps de réponse de serveur plus courts. Avec la réplication de la base de données, les données sont répliquées depuis les serveurs dits *serveurs maîtres* vers les serveurs dit *serveurs esclaves*. Toutes les écritures et les mises à jour ont lieu sur les serveurs maîtres, tandis que les lectures ont lieu sur les serveurs esclaves.\n\nPour tirer parti de la réplication des bases de données et réaliser l'éclatement lecture-écriture, vous pouvez configurer un composant [[yii\\db\\Connection]] comme le suivant :\n\n```php\n[\n    'class' => 'yii\\db\\Connection',\n\n    // configuration pour le maître\n    'dsn' => 'dsn pour le serveur maître',\n    'username' => 'master',\n    'password' => '',\n\n    //  configuration commune pour les esclaves\n    'slaveConfig' => [\n        'username' => 'slave',\n        'password' => '',\n        'attributes' => [\n            // utilise un temps d'attente de connexion plus court\n            PDO::ATTR_TIMEOUT => 10,\n        ],\n    ],\n\n    // liste des configurations d'esclave\n    'slaves' => [\n        ['dsn' => 'dsn pour le serveur esclave 1'],\n        ['dsn' => 'dsn pour le serveur esclave 2'],\n        ['dsn' => 'dsn pour le serveur esclave 3'],\n        ['dsn' => 'dsn pour le serveur esclave 4'],\n    ],\n]\n```\n\nLa configuration ci-dessus spécifie une configuration avec un unique maître et de multiples esclaves. L'un des esclaves est connecté et utilisé pour effectuer des requêtes en lecture, tandis que le maître est utilisé pour effectuer les requêtes en écriture. Un tel éclatement lecture-écriture est accompli automatiquement avec cette configuration. Par exemple :\n\n```php\n// crée une instance de Connection en utilisant la configuration ci-dessus\nYii::$app->db = Yii::createObject($config);\n\n// effectue une requête auprès d'un des esclaves\n$rows = Yii::$app->db->createCommand('SELECT * FROM user LIMIT 10')->queryAll();\n\n// effectue une requête auprès du maître\nYii::$app->db->createCommand(\"UPDATE user SET username='demo' WHERE id=1\")->execute();\n```\n\n> Info: les requêtes effectuées en appelant [[yii\\db\\Command::execute()]] sont considérées comme des requêtes en écriture, tandis que toutes les autres requêtes faites via l'une des méthodes « *query* » sont des requêtes en lecture. Vous pouvez obtenir la connexion couramment active à un des esclaves via `Yii::$app->db->slave`.\n\nLe composant `Connection` prend en charge l'équilibrage de charge et de basculement entre esclaves. Lorsque vous effectuez une requête en lecture par la première fois, le composant `Connection` sélectionne un esclave de façon aléatoire et essaye de s'y connecter. Si l'esclave set trouvé « mort », il en essaye un autre. Si aucun des esclaves n'est disponible, il se connecte au maître. En configurant un [[yii\\db\\Connection::serverStatusCache|cache d'état du serveur]], le composant mémorise le serveur « mort » et ainsi, pendant un [[yii\\db\\Connection::serverRetryInterval|certain intervalle de temps]], n'essaye plus de s'y connecter.\n\n> Info: dans la configuration précédente, un temps d'attente de connexion de 10 secondes est spécifié pour chacun des esclaves. Cela signifie que, si un esclave ne peut être atteint pendant ces 10 secondes, il est considéré comme « mort ». Vous pouvez ajuster ce paramètre en fonction de votre environnement réel.\n\n\nVous pouvez aussi configurer plusieurs maîtres avec plusieurs esclaves. Par exemple :\n\n\n```php\n[\n    'class' => 'yii\\db\\Connection',\n\n    // configuration commune pour les maîtres\n    'masterConfig' => [\n        'username' => 'master',\n        'password' => '',\n        'attributes' => [\n            // utilise un temps d'attente de connexion plus court \n            PDO::ATTR_TIMEOUT => 10,\n        ],\n    ],\n\n    // liste des configurations de maître\n    'masters' => [\n        ['dsn' => 'dsn for master server 1'],\n        ['dsn' => 'dsn for master server 2'],\n    ],\n\n    // configuration commune pour les esclaves\n    'slaveConfig' => [\n        'username' => 'slave',\n        'password' => '',\n        'attributes' => [\n            // use a smaller connection timeout\n            PDO::ATTR_TIMEOUT => 10,\n        ],\n    ],\n\n    // liste des configurations d'esclave \n    'slaves' => [\n        ['dsn' => 'dsn for slave server 1'],\n        ['dsn' => 'dsn for slave server 2'],\n        ['dsn' => 'dsn for slave server 3'],\n        ['dsn' => 'dsn for slave server 4'],\n    ],\n]\n```\n\nLa configuration ci-dessus spécifie deux maîtres et quatre esclaves. Le composant `Connection` prend aussi en charge l'équilibrage de charge et le basculement entre maîtres juste comme il le fait pour les esclaves. Une différence est que, si aucun des maîtres n'est disponible, une exception est levée.\n\n> Note: lorsque vous utilisez la propriété [[yii\\db\\Connection::masters|masters]] pour configurer un ou plusieurs maîtres, toutes les autres propriétés pour spécifier une connexion à une base de données (p. ex. `dsn`, `username`, `password`) avec l'objet `Connection` lui-même sont ignorées.\n\n\nPar défaut, les transactions utilisent la connexion au maître. De plus, dans une transaction, toutes les opérations de base de données utilisent la connexion au maître. Par exemple :\n\n```php\n$db = Yii::$app->db;\n// la transaction est démarrée sur la connexion au maître\n$transaction = $db->beginTransaction();\n\ntry {\n    //  les deux requêtes sont effectuées auprès du maître\n    $rows = $db->createCommand('SELECT * FROM user LIMIT 10')->queryAll();\n    $db->createCommand(\"UPDATE user SET username='demo' WHERE id=1\")->execute();\n\n    $transaction->commit();\n} catch(\\Exception $e) {\n    $transaction->rollBack();\n    throw $e;\n}\n```\n\nSi vous voulez démarrer une transaction avec une connexion à un esclave, vous devez le faire explicitement, comme ceci :\n\n```php\n$transaction = Yii::$app->db->slave->beginTransaction();\n```\n\nParfois, vous désirez forcer l'utilisation de la connexion au maître pour effectuer une requête en lecture . Cela est possible avec la méthode `useMaster()` :\n\n```php\n$rows = Yii::$app->db->useMaster(function ($db) {\n    return $db->createCommand('SELECT * FROM user LIMIT 10')->queryAll();\n});\n```\n\nVous pouvez aussi définir directement `Yii::$app->db->enableSlaves` à `false` (faux) pour rediriger toutes les requêtes vers la connexion au maître.\n\n\n## Travail avec le schéma de la base de données <span id=\"database-schema\"></span>\n\nLes objets d'accès aux bases de données de Yii DAO fournissent un jeu complet de méthodes pour vous permettre de manipuler le schéma de la base de données, comme créer de nouvelles tables, supprimer une colonne d'une table, etc. Ces méthodes sont listées ci-après :\n\n* [[yii\\db\\Command::createTable()|createTable()]]: crée une table\n* [[yii\\db\\Command::renameTable()|renameTable()]]: renomme une table\n* [[yii\\db\\Command::dropTable()|dropTable()]]: supprime une table\n* [[yii\\db\\Command::truncateTable()|truncateTable()]]: supprime toutes les lignes dans une table\n* [[yii\\db\\Command::addColumn()|addColumn()]]: ajoute une colonne\n* [[yii\\db\\Command::renameColumn()|renameColumn()]]: renomme une colonne\n* [[yii\\db\\Command::dropColumn()|dropColumn()]]: supprime une colonne\n* [[yii\\db\\Command::alterColumn()|alterColumn()]]: modifie une colonne\n* [[yii\\db\\Command::addPrimaryKey()|addPrimaryKey()]]: ajoute une clé primaire\n* [[yii\\db\\Command::dropPrimaryKey()|dropPrimaryKey()]]: supprime une clé primaire\n* [[yii\\db\\Command::addForeignKey()|addForeignKey()]]: ajoute un clé étrangère\n* [[yii\\db\\Command::dropForeignKey()|dropForeignKey()]]: supprime une clé étrangère\n* [[yii\\db\\Command::createIndex()|createIndex()]]: crée un index\n* [[yii\\db\\Command::dropIndex()|dropIndex()]]: supprime un index\n\nCes méthodes peuvent être utilisées comme suit :\n\n```php\n// CREATE TABLE\nYii::$app->db->createCommand()->createTable('post', [\n    'id' => 'pk',\n    'title' => 'string',\n    'text' => 'text',\n]);\n```\n\nLe tableau ci-dessus décrit le nom et le type des colonnes à créer. Pour les types de colonne, Yii fournit un jeu de types de donnée abstraits, qui permettent de définir un schéma de base de données indifférent au type de base de données. Ces types sont convertis en définition de types spécifiques au système de gestion de base de données qui dépendent de la base de données dans laquelle la table est créée. Reportez-vous à la documentation de l'API de la méthode [[yii\\db\\Command::createTable()|createTable()]] pour plus d'informations.\n\nEn plus de changer le schéma de la base de données, vous pouvez aussi retrouver les informations de définition d'une table via la méthode [[yii\\db\\Connection::getTableSchema()|getTableSchema()]] d'une connexion à une base de données. Par exemple :\n\n```php\n$table = Yii::$app->db->getTableSchema('post');\n```\n\nLa méthode retourne un objet [[yii\\db\\TableSchema]] qui contient les information sur les colonnes de la table, les clés primaires, les clés étrangères, etc. Toutes ces informations sont essentiellement utilisées par le [constructeur de requêtes](db-query-builder.md) et par l'[enregistrement actif](db-active-record.md) pour vous aider à écrire du code indifférent au type de la base de données.\n"
  },
  {
    "path": "docs/guide-fr/db-migrations.md",
    "content": "Migrations de base de données\n=============================\n\nDurant la période de développement et de maintenance d'une application s'appuyant sur une base de données, la structure de la base de données évolue tout comme le code source. Par exemple, durant développement une nouvelle table peut devenir nécessaire ; après que l'application est déployée en production, on peut s'apercevoir qu'un index doit être créé pour améliorer la performance des requêtes ; et ainsi de suite. Comme un changement dans la base de données nécessite souvent des changements dans le code, Yii prend en charge une fonctionnalité qu'on appelle *migrations de base de données*. Cette fonctionnalité permet de conserver la trace des changements de la base de données en termes de *migrations de base de données* dont les versions sont contrôlées avec celles du code.\n\nLes étapes suivantes montrent comment des migrations de base de données peuvent être utilisées par une équipe durant la phase de développement :\n\n1. Tim crée une nouvelle migration (p. ex. créer une nouvelle table, changer la définition d'une colonne, etc.).\n2. Tim entérine (commit) la nouvelle migration dans le système de contrôle de version (p. ex. Git, Mercurial).\n3. Doug met à jour son dépôt depuis le système de contrôle de version et reçoit la nouvelle migration. \n4. Doug applique la migration à sa base de données de développement locale, et ce faisant synchronise sa base de données pour refléter les changements que Tim a faits.\n\nLes étapes suivantes montrent comment déployer une nouvelle version avec les migrations de base de données en production :\n\n1. Scott crée une balise de version pour le dépôt du projet qui contient quelques nouvelles migrations de base de données.\n2. Scott met à jour le code source sur le serveur de production à la version balisée. \n3. Scott applique toutes les migrations accumulées à la base de données de production.\n\nYii fournit un jeu de commandes de migration en ligne de commande qui vous permet de :\n\n* créer de nouvelles migrations;\n* appliquer les migrations;\n* défaire les migrations;\n* ré-appliquer les migrations;\n* montrer l'historique de l'état des migrations.\n\nTous ces outils sont accessibles via la commande `yii migrate`. Dans cette section nous décrivons en détails comment accomplir des tâches variées en utilisant ces outils. Vous pouvez aussi obtenir les conseils d'utilisation de chacun des outils via la commande d'aide `yii help migrate`.\n\n> Astuce : les migrations peuvent non seulement affecter le schéma de base de données mais aussi ajuster les données existantes pour s'adapter au nouveau schéma, créer la hiérarchie RBAC (Role Based Acces Control - Contrôle d'accès basé sur les rôles), ou vider le cache.\n\n> Note : lors de la manipulation de données utilisant une migration, vous pouvez trouver qu'utiliser vos classes  [Active Record](db-active-record.md) \n> pour cela peut s'avérer utile parce qu'une partie de la logique y est déjà mise en œuvre. Soyez cependant conscient que, contrairement\n> au code écrit dans les migrations, dont la nature est de rester constant à jamais, la logique d'application est sujette à modification.\n> C'est pourquoi, lorsque vous utilisez des classes ActiveRecord dans le code d'une migration, des modifications de la logique de l'ActiveRecord peuvent accidentellement casser\n> des migrations existantes. Pour cette raison, le code des migrations devrait être conservé indépendant d'autres logiques d'application telles que celles des classes ActiveRecord.\n\n## Création de migrations <span id=\"creating-migrations\"></span>\n\nPour créer une nouvelle migration, exécutez la commande suivante : \n\n```\nyii migrate/create <name>\n```\n\nL'argument `name` requis donne une brève description de la nouvelle migration. Par exemple, si la création concerne la création d'une nouvelle table nommée *news*, vous pouvez utiliser le nom `create_news_table` et exécuter la commande suivante :\n\n```\nyii migrate/create create_news_table\n```\n\n> Note: comme l'argument `name` est utilisé comme partie du nom de la classe migration générée, il ne doit contenir que des lettres, des chiffre et/ou des caractères *souligné*. \n\nLa commande ci-dessus crée une nouvelle classe PHP nommée `m150101_185401_create_news_table.php` dans le dossier `@app/migrations`. Le fichier contient le code suivant qui déclare principalement une classe de migration `m150101_185401_create_news_table` avec le squelette de code suivant :\n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function up()\n    {\n\n    }\n\n    public function down()\n    {\n        echo \"m101129_185401_create_news_table cannot be reverted.\\n\";\n\n        return false;\n    }\n\n    /*\n    // Use safeUp/safeDown to run migration code within a transaction\n    public function safeUp()\n    {\n    }\n\n    public function safeDown()\n    {\n    }\n    */\n}\n```\n\nChaque migration de base de données est définie sous forme de classe PHP étendant la classe [[yii\\db\\Migration]]. Le nom de la classe de migration est généré automatiquement dans le format `m<YYMMDD_HHMMSS>_<Name>`, dans lequel :\n\n* `<YYMMDD_HHMMSS>` fait référence à l'horodate UTC à laquelle la commande de création de la migration a été exécutée.\n* `<Name>` est le même que la valeur que vous donnez à l'argument `name` dans la commande.\n\nDans la classe de migration, vous devez écrire du code dans la méthode `up()` qui effectue les modifications dans la structure de la base de données. Vous désirez peut-être écrire du code dans la méthode `down()` pour défaire les changements apportés par `up()`. La méthode `up()` est invoquée lorsque vous mettez à jour la base de données avec la migration, tandis que la méthode `down()` est invoquée lorsque vous ramenez la base de données à l'état antérieur. Le code qui suit montre comment mettre en œuvre la classe de migration pour créer une table `news` :\n\n```php\n<?php\n\nuse yii\\db\\Schema;\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function up()\n    {\n        $this->createTable('news', [\n            'id' => Schema::TYPE_PK,\n            'title' => Schema::TYPE_STRING . ' NOT NULL',\n            'content' => Schema::TYPE_TEXT,\n        ]);\n    }\n\n    public function down()\n    {\n        $this->dropTable('news');\n    }\n}\n```\n\n> Info: toutes les migrations ne sont pas réversibles. Par exemple, si la méthode `up()` supprime une ligne dans une table, il se peut que vous soyez incapable de récupérer cette ligne dans la méthode `down()`. Parfois, vous pouvez simplement être trop paresseux pour implémenter la méthode `down`, parce que défaire une migration de base de données n'est pas chose courante. Dans ce cas, vous devriez retourner `false` dans la méthode `down()` pour indiquer que la migration n'est pas réversible. \n\nLa classe de migration de base [[yii\\db\\Migration]] expose une connexion à une base de données via la propriété [[yii\\db\\Migration::db|db]]. Vous pouvez utiliser cette connexion pour manipuler le schéma en utilisant les méthodes décrites dans la sous-section [Travail avec le schéma de base de données](db-dao.md#database-schema).\n\nPlutôt que d'utiliser des types physiques, lors de la création d'une table ou d'une colonne, vous devez utiliser des *types abstraits* afin que vos migrations soient indépendantes d'un système de gestion de base de données en particulier. La classe [[yii\\db\\Schema]] définit une jeu de constantes pour représenter les types abstraits pris en charge. Ces constantes sont nommées dans le format `TYPE_<Name>`. Par exemple, `TYPE_PK` fait référence au type clé primaire à auto-incrémentation ; `TYPE_STRING` fait référence au type chaîne de caractères. Lorsqu'une migration est appliquée à une base de données particulière, le type abstrait est converti dans le type physique correspondant. Dans le cas de MySQL, `TYPE_PK` est transformé en `int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY`, tandis que `TYPE_STRING` est transformé en `varchar(255)`.\n\nVous pouvez ajouter des contraintes additionnelles lors de l'utilisation des types abstraits. Dans l'exemple ci-dessus,` NOT NULL` est ajouté à `Schema::TYPE_STRING` pour spécifier que la colonne ne peut être `null` (nulle).\n\n> Info: la mise en correspondance entre les types abstraits et les types physiques est spécifiée par la propriété [[yii\\db\\QueryBuilder::$typeMap|$typeMap]] dans chacune des classes `QueryBuilder` concrètes.\n\nDepuis la version 2.0.6, vous pouvez utiliser le constructeur de schéma récemment introduit qui procure un moyen plus pratique de définir le schéma d'une colonne. Ainsi, la migration ci-dessus pourrait s'écrire comme ceci :\n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function up()\n    {\n        $this->createTable('news', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string()->notNull(),\n            'content' => $this->text(),\n        ]);\n    }\n\n    public function down()\n    {\n        $this->dropTable('news');\n    }\n}\n```\n\nUne liste de toutes les méthodes disponibles pour définir les types de colonne est disponible dans la documentation de l'API de [[yii\\db\\SchemaBuilderTrait]].\n\n\n## Génération des migrations <span id=\"generating-migrations\"></span>\n\nDepuis la version 2.0.7, la commande de migration procure un moyen pratique de créer des migrations. \n\nSi le nom de la migration est d'une forme spéciale, par exemple, `create_xxx_table` ou `drop_xxx_table` alors le fichier de la migration générée contient du code supplémentaire, dans ce cas pour créer/supprimer des tables. Dans ce qui suit, toutes les variantes de cette fonctionnalité sont décrites. \n\n### Création d'une table\n\n```php\nyii migrate/create create_post_table\n```\n\ngénère\n\n```php\n/**\n * prend en charge la création de la table `post`.\n */\nclass m150811_220037_create_post_table extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey()\n        ]);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        $this->dropTable('post');\n    }\n}\n```\n\nPour créer les champs de table tout de suite, spécifiez les via l'option `--fields`.\n\n```php\nyii migrate/create create_post_table --fields=\"title:string,body:text\"\n```\n\ngénère\n\n```php\n/**\n * prend en charge la création de la table `post`.\n */\nclass m150811_220037_create_post_table extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string(),\n            'body' => $this->text(),\n        ]);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        $this->dropTable('post');\n    }\n}\n\n```\n\nVous pouvez spécifier plus de paramètres de champs.\n\n```php\nyii migrate/create create_post_table --fields=\"title:string(12):notNull:unique,body:text\"\n```\n\ngénère\n\n```php\n/**\n * prend en charge la création de la table `post`.\n */\nclass m150811_220037_create_post_table extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string(12)->notNull()->unique(),\n            'body' => $this->text()\n        ]);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        $this->dropTable('post');\n    }\n}\n```\n\n> Note: par défaut, une clé primaire nommée `id` est ajoutée automatiquement. Si vous voulez utiliser un autre nom, vous devez le spécifier explicitement comme dans `--fields=\"name:primaryKey\"`.\n\n#### Clés étrangères\n\nDepuis 2.0.8 le générateur prend en charge les clés étrangères en utilisant le mot clé `foreignKey`.\n\n```php\nyii migrate/create create_post_table --fields=\"author_id:integer:notNull:foreignKey(user),category_id:integer:defaultValue(1):foreignKey,title:string,body:text\"\n```\n\ngénère\n\n```php\n/**\n * prend en charge la création de la table `post`.\n * possède des clés étrangères vers les tables\n *\n * - `user`\n * - `category`\n */\nclass m160328_040430_create_post_table extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey(),\n            'author_id' => $this->integer()->notNull(),\n            'category_id' => $this->integer()->defaultValue(1),\n            'title' => $this->string(),\n            'body' => $this->text(),\n        ]);\n\n        // crée un index pour la colonne `author_id`\n        $this->createIndex(\n            'idx-post-author_id',\n            'post',\n            'author_id'\n        );\n\n        // ajoute une clé étrangère vers la table `user`\n        $this->addForeignKey(\n            'fk-post-author_id',\n            'post',\n            'author_id',\n            'user',\n            'id',\n            'CASCADE'\n        );\n\n        // crée un index pour la colonne `category_id`\n        $this->createIndex(\n            'idx-post-category_id',\n            'post',\n            'category_id'\n        );\n\n        // ajoute une clé étrangère vers la table `category`\n        $this->addForeignKey(\n            'fk-post-category_id',\n            'post',\n            'category_id',\n            'category',\n            'id',\n            'CASCADE'\n        );\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        // supprime la clé étrangère vers la table `user`\n        $this->dropForeignKey(\n            'fk-post-author_id',\n            'post'\n        );\n\n        // supprime l'index pour la colonne `author_id`\n        $this->dropIndex(\n            'idx-post-author_id',\n            'post'\n        );\n\n        // supprime la clé étrangère vers la table `category`\n        $this->dropForeignKey(\n            'fk-post-category_id',\n            'post'\n        );\n\n        // supprime l'index pour la colonne `category_id`\n        $this->dropIndex(\n            'idx-post-category_id',\n            'post'\n        );\n\n        $this->dropTable('post');\n    }\n}\n```\n\nLa position du mot clé `foreignKey` dans la description de la colonne ne change pas le code généré. Ce qui signifie que les expressions :\n\n- `author_id:integer:notNull:foreignKey(user)`\n- `author_id:integer:foreignKey(user):notNull`\n- `author_id:foreignKey(user):integer:notNull`\n\ngénèrent toutes le même code.\n\nLe mot clé `foreignKey` accepte un paramètre entre parenthèses qui est le nom de la table en relation pour la clé étrangère générée. Si aucun paramètre n'est passé, le nom de table est déduit du nom de la colonne. \n\nDans l'exemple ci-dessus `author_id:integer:notNull:foreignKey(user)` génère une colonne nommée `author_id` avec une clé étrangère pointant sur la table `user`, tandis que `category_id:integer:defaultValue(1):foreignKey` génère une colonne `category_id` avec une clé étrangère pointant sur la table `category`.\n\nDepuis la version 2.0.11, le mot clé `foreignKey` accepte un second paramètre, séparé par une espace. \nIl accepte le nom de la colonne en relation pour la clé étrangère générée. \nSi aucun second paramètre n'est passé, le nom de la colonne est retrouvé dans le schéma de table.\nSi aucun schéma n'existe, la clé primaire n'est pas définie ou est composite, le nom par défaut `id` est utilisé.\n\n\n\n### Suppression de tables\n\n```php\nyii migrate/create drop_post_table --fields=\"title:string(12):notNull:unique,body:text\"\n```\n\ngénère\n\n```php\nclass m150811_220037_drop_post_table extends Migration\n{\n    public function up()\n    {\n        $this->dropTable('post');\n    }\n\n    public function down()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string(12)->notNull()->unique(),\n            'body' => $this->text()\n        ]);\n    }\n}\n```\n\n### Ajout de colonnes\n\nSi le nom de la migration est de la forme `add_xxx_column_to_yyy_table` alors le fichier doit contenir les instructions `addColumn` et `dropColumn` nécessaires.\n\nPour ajouter une colonne :\n\n```php\nyii migrate/create add_position_column_to_post_table --fields=\"position:integer\"\n```\n\ngénère\n\n```php\nclass m150811_220037_add_position_column_to_post_table extends Migration\n{\n    public function up()\n    {\n        $this->addColumn('post', 'position', $this->integer());\n    }\n\n    public function down()\n    {\n        $this->dropColumn('post', 'position');\n    }\n}\n```\nVous pouvez spécifier de multiples colonnes comme suit :\n\n```\nyii migrate/create add_xxx_column_yyy_column_to_zzz_table --fields=\"xxx:integer,yyy:text\"\n```\n\n### Supprimer une colonne\n\nSi le nom de la migration est de la forme `drop_xxx_column_from_yyy_table` alors le fichier doit contenir les instructions `addColumn` et `dropColumn` néessaires.\n\n```php\nyii migrate/create drop_position_column_from_post_table --fields=\"position:integer\"\n```\n\ngénère\n\n```php\nclass m150811_220037_drop_position_column_from_post_table extends Migration\n{\n    public function up()\n    {\n        $this->dropColumn('post', 'position');\n    }\n\n    public function down()\n    {\n        $this->addColumn('post', 'position', $this->integer());\n    }\n}\n```\n\n### Ajout d'une table de jointure\n\nSi le nom de la migration est de la forme `create_junction_table_for_xxx_and_yyy_tables` ou `create_junction_xxx_and_yyy_tables`, alors le code nécessaire à la création de la table de jointure est généré.\n\n```php\nyii migrate/create create_junction_table_for_post_and_tag_tables --fields=\"created_at:dateTime\"\n```\n\ngénère\n\n```php\n/**\n * prend en charge la création de la table `post_tag`.\n * possède des clés étrangères vers les tables:\n *\n * - `post`\n * - `tag`\n */\nclass m160328_041642_create_junction_table_for_post_and_tag_tables extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $this->createTable('post_tag', [\n            'post_id' => $this->integer(),\n            'tag_id' => $this->integer(),\n            'created_at' => $this->dateTime(),\n            'PRIMARY KEY(post_id, tag_id)',\n        ]);\n\n        // crée un index pour la colonne`post_id`\n        $this->createIndex(\n            'idx-post_tag-post_id',\n            'post_tag',\n            'post_id'\n        );\n\n        // ajoute un clé étrangère vers la table `post`\n        $this->addForeignKey(\n            'fk-post_tag-post_id',\n            'post_tag',\n            'post_id',\n            'post',\n            'id',\n            'CASCADE'\n        );\n\n        // crée un index pour la colonne `tag_id`\n        $this->createIndex(\n            'idx-post_tag-tag_id',\n            'post_tag',\n            'tag_id'\n        );\n\n        // ajoute une clé étrangère vers la table `tag`\n        $this->addForeignKey(\n            'fk-post_tag-tag_id',\n            'post_tag',\n            'tag_id',\n            'tag',\n            'id',\n            'CASCADE'\n        );\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        // supprime la clé étrangère vers la table `post`\n        $this->dropForeignKey(\n            'fk-post_tag-post_id',\n            'post_tag'\n        );\n\n        // supprime l'index pour la colonne `post_id`\n        $this->dropIndex(\n            'idx-post_tag-post_id',\n            'post_tag'\n        );\n\n        // supprime la clé étrangère vers la table `tag`\n        $this->dropForeignKey(\n            'fk-post_tag-tag_id',\n            'post_tag'\n        );\n\n        // supprime l'index pour la column `tag_id`\n        $this->dropIndex(\n            'idx-post_tag-tag_id',\n            'post_tag'\n        );\n\n        $this->dropTable('post_tag');\n    }\n}\n```\n\nDepuis la version 2.0.1, les noms de colonne des clés étrangères pour les tables de jonction sont recherchées dans le schéma de table.\nDans le cas où la table n'est pas définie dans le schéma, ou quand la clé primaire n'est pas définie ou est composite, le nom par défaut `id` est utilisé.\n\n### Migrations transactionnelles <span id=\"transactional-migrations\"></span>\n\nEn effectuant des migration de base de données complexes, il est important de garantir que chacune des migrations soit réussisse, soit échoue dans son ensemble, de manière à ce que la base de données reste cohérente et intègre. Pour atteindre ce but, il est recommandé que vous englobiez les opérations de base de données de chacune des migrations dans une [transaction](db-dao.md#performing-transactions).\n\nUne manière encore plus aisée pour mettre en œuvre des migrations transactionnelles est de placer le code de migration dans les méthodes `safeUp()` et `safeDown()`. Ces deux méthodes diffèrent de `up()` et `down()` par le fait qu'elles sont implicitement englobées dans une transaction. En conséquence, si n'importe quelle opération de ces méthodes échoue, toutes les opérations antérieures à elle sont automatiquement défaites. \n\nDans l'exemple suivant, en plus de créer la table `news`, nous insérons une ligne initiale dans cette table. \n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function safeUp()\n    {\n        $this->createTable('news', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string()->notNull(),\n            'content' => $this->text(),\n        ]);\n\n        $this->insert('news', [\n            'title' => 'test 1',\n            'content' => 'content 1',\n        ]);\n    }\n\n    public function safeDown()\n    {\n        $this->delete('news', ['id' => 1]);\n        $this->dropTable('news');\n    }\n}\n```\n\nNotez que, généralement, si vous effectuez de multiples opérations de base de données dans `safeUp()`, vous devriez les défaire dans `safeDown()`. Dans l'exemple ci-dessus, dans `safeUp()`, nous créons d'abord la table puis nous insérons une ligne, tandis que, dans `safeDown`, nous commençons par supprimer la ligne, puis nous supprimons la table. \n\n> Note: tous les systèmes de gestion de bases de données NE prennent PAS en charge les transactions. De plus, quelques requêtes de base de données ne peuvent être placées dans une transaction. Pour quelques exemples, reportez-vous à [entérinement implicite](https://dev.mysql.com/doc/refman/5.7/en/implicit-commit.html). Si c'est le cas, vous devez simplement mettre en œuvre `up()` et`down()`, à la place.\n\n\n### Méthodes d'accès aux bases de données <span id=\"db-accessing-methods\"></span>\n\nLa classe de base de migration [[yii\\db\\Migration]] fournit un jeu de méthodes pour vous permettre d'accéder aux bases de données et de les manipuler. Vous vous apercevrez que ces méthodes sont nommées de façon similaires aux [méthodes d'objets d'accès aux données](db-dao.md) fournies par la classe [[yii\\db\\Command]]. Par exemple, la méthode [[yii\\db\\Migration::createTable()]] vous permet de créer une nouvelle table, tout comme [[yii\\db\\Command::createTable()]].\n\nL'avantage d'utiliser les méthodes fournies par [[yii\\db\\Migration]] est que vous n'avez pas besoin de créer explicitement des instances de [[yii\\db\\Command]] et que l'exécution de chacune des méthodes affiche automatiquement des messages utiles vous indiquant que les opérations de base de données sont effectuées et combien de temps ces opérations ont pris. \n\nCi-dessous, nous présentons la liste de toutes les méthodes d'accès aux bases de données : \n\n* [[yii\\db\\Migration::execute()|execute()]]: exécute une instruction SQL\n* [[yii\\db\\Migration::insert()|insert()]]: insère une unique ligne\n* [[yii\\db\\Migration::batchInsert()|batchInsert()]]: insère de multiples lignes\n* [[yii\\db\\Migration::update()|update()]]: met à jour des lignes\n* [[yii\\db\\Migration::delete()|delete()]]: supprime des lignes\n* [[yii\\db\\Migration::createTable()|createTable()]]: crée une table\n* [[yii\\db\\Migration::renameTable()|renameTable()]]: renomme une table\n* [[yii\\db\\Migration::dropTable()|dropTable()]]: supprime une table\n* [[yii\\db\\Migration::truncateTable()|truncateTable()]]: supprime toutes les lignes d'une table\n* [[yii\\db\\Migration::addColumn()|addColumn()]]: ajoute une colonne\n* [[yii\\db\\Migration::renameColumn()|renameColumn()]]: renomme une colonne\n* [[yii\\db\\Migration::dropColumn()|dropColumn()]]: supprime une colonne\n* [[yii\\db\\Migration::alterColumn()|alterColumn()]]: modifie une colonne\n* [[yii\\db\\Migration::addPrimaryKey()|addPrimaryKey()]]: ajoute une clé primaire\n* [[yii\\db\\Migration::dropPrimaryKey()|dropPrimaryKey()]]: supprime une clé primaire\n* [[yii\\db\\Migration::addForeignKey()|addForeignKey()]]: ajoute une clé étrangère\n* [[yii\\db\\Migration::dropForeignKey()|dropForeignKey()]]: supprime une clé étrangère\n* [[yii\\db\\Migration::createIndex()|createIndex()]]: crée un index\n* [[yii\\db\\Migration::dropIndex()|dropIndex()]]: supprime un index\n* [[yii\\db\\Migration::addCommentOnColumn()|addCommentOnColumn()]]: ajoute un commentaire à une colonne\n* [[yii\\db\\Migration::dropCommentFromColumn()|dropCommentFromColumn()]]: supprime un commentaire d'une colonne\n* [[yii\\db\\Migration::addCommentOnTable()|addCommentOnTable()]]: ajoute un commentaire à une table\n* [[yii\\db\\Migration::dropCommentFromTable()|dropCommentFromTable()]]: supprime un commentaire d'une table\n\n> Info: [[yii\\db\\Migration]] \n> ne fournit pas une méthode de requête de base de données. C'est parce que, normalement, vous n'avez pas besoin d'afficher de messages supplémentaire à propos de l'extraction de données dans une base de données. C'est aussi parce que vous pouvez utiliser le puissant [constructeur de requêtes](db-query-builder.md) pour construire et exécuter des requêtes complexes. \n> L'utilisation du constructeur de requêtes dans une migration ressemble à ceci :\n>\n> ```php\n> // update status field for all users\n> foreach((new Query)->from('users')->each() as $user) {\n>     $this->update('users', ['status' => 1], ['id' => $user['id']]);\n> }\n> ```\n\n## Application des migrations <span id=\"applying-migrations\"></span>\n\nPour mettre une base de données à jour à sa dernière structure, vous devez appliquer toutes les nouvelles migrations disponibles en utilisant la commande suivante :\n\n```\nyii migrate\n```\n\nCette commande liste toutes les migrations qui n'ont pas encore été appliquées. Si vous confirmez que vous voulez appliquer ces migrations, cela provoque l'exécution des méthodes `up()` ou `safeUp()` de chacune des nouvelles migrations, l'une après l'autre, dans l'ordre de leur horodate. Si l'une de ces migrations échoue, la commande se termine sans appliquer les migrations qui restent. \n\n> Astuce : dans le cas où votre serveur ne vous offre pas de ligne de commande, vous pouvez essayer [Web shell](https://github.com/samdark/yii2-webshell).\n\nPour chaque migration qui n'a pas été appliqué avec succès, la commande insère une ligne dans une table de base de données nommée `migration` pour enregistrer les applications réussies de la migration. Cela permet à l'outil de migration d'identifier les migrations qui ont été appliquées et celles qui ne l'ont pas été. \n\n> Info: l'outil de migration crée automatiquement la table `migration` dans la base de données spécifiée par l'option [[yii\\console\\controllers\\MigrateController::db|db]] de la commande. Par défaut, la base de données est spécifiée dans le [composant d'application](structure-application-components.md) `db`.\n\nParfois, vous désirez peut-être appliquer une ou quelques migrations plutôt que toutes les migrations disponibles. Vous pouvez le faire en spécifiant le nombre de migrations que vous voulez appliquer en exécutant la commande. Par exemple, la commande suivante essaye d'appliquer les trois prochaines migrations disponibles :\n\n```\nyii migrate 3\n```\n\nVous pouvez également spécifier explicitement une migration particulière à laquelle la base de données doit être amenée en utilisant la commande `migrate/to` dans l'un des formats suivants :\n\n```\nyii migrate/to 150101_185401                      # utiliser l'horodatage pour spécifier la migration\nyii migrate/to \"2015-01-01 18:54:01\"              # utilise une chaîne de caractères qui peut être analysée par strtotime()\nyii migrate/to m150101_185401_create_news_table   # utilise le nom complet \nyii migrate/to 1392853618                         # utilise un horodatage UNIX\n```\n\nS'il existe des migrations non appliquée antérieures à celle spécifiée, elles sont toutes appliquées avant que la migration spécifiée ne le soit.\n\nSi la migration spécifiée a déjà été appliquée auparavant, toutes les migrations postérieures qui ont été appliquées sont défaites. \n\n\n## Défaire des migrations <span id=\"reverting-migrations\"></span>\n\nPour défaire une ou plusieurs migrations que ont été appliquées auparavant, vous pouvez exécuter la commande suivante : \n\n```\nyii migrate/down     # défait la migration appliquée le plus récemment\nyii migrate/down 3   # défait les 3 migrations appliquées le plus récemment \n\n```\n\n> Note: toutes les migrations ne sont PAS réversibles. Essayer de défaire de telles migrations provoque une erreur et arrête tout le processus de retour à l'état initial.\n\n\n## Refaire des migrations <span id=\"redoing-migrations\"></span>\n\nRefaire (ré-appliquer) des migrations signifie d'abord défaire les migrations spécifiées puis les appliquer à nouveau. Cela peut être fait comme suit :\n\n```\nyii migrate/redo        # refait la dernière migration appliquée \nyii migrate/redo 3      # refait les 3 dernière migrations appliquées\n\n```\n\n> Note: si une  migration n'est pas réversible, vous ne serez pas en mesure de la refaire.\n\n## Rafraîchir des Migrations <span id=\"refreshing-migrations\"></span>\n\nDeepuis la version 2.0.13, vous pouvez supprimer toutes les tables et clés étrangères de la base de données et ré-appliquer toutes les migrations depuis le début. \n```\nyii migrate/fresh       # Tronçonne la base de données et applique toutes les migrations depuis le début\n\n```\n\n## Lister des migrations <span id=\"listing-migrations\"></span>\n\nPour lister quelles migrations ont été appliquées et quelles migrations ne l'ont pas été, vous pouvez utiliser les commandes suivantes : \n\n```\nyii migrate/history     # montre les 10 dernières migrations appliquées\nyii migrate/history 5   # montre les 5 dernières migrations appliquées\nyii migrate/history all # montre toutes les migrations appliquées\n\nyii migrate/new         # montre les 10 premières nouvelles migrations \nyii migrate/new 5       # montre les 5 premières nouvelles migrations\nyii migrate/new all     # montre toutes les nouvelles migrations\n```\n\n\n## Modification de l'historique des migrations <span id=\"modifying-migration-history\"></span>\n\nAu lieu d'appliquer ou défaire réellement des migrations, parfois, vous voulez peut-être simplement marquer que votre base de données a été portée à une certaine migration. Cela arrive souvent lorsque vous changer manuellement la base de données pour l'amener à un état particulier et que vous ne voulez pas que la migration correspondant à ce changement soit appliquée de nouveau par la suite. Vous pouvez faire cela avec la commande suivante :\n```\nyii migrate/mark 150101_185401                      # utilise un horodatage pour spécifier la migration \nyii migrate/mark \"2015-01-01 18:54:01\"              # utilise une chaîne de caractères qui peut être analysée par strtotime()\nyii migrate/mark m150101_185401_create_news_table   # utilise le nom complet\nyii migrate/mark 1392853618                         # utilise un horodatage UNIX\n```\n\nLa commande modifie la table `migration` en ajoutant ou en supprimant certaines lignes pour indiquer que la base de données s'est vue appliquer toutes les migrations jusqu'à celle spécifiée. Aucune migration n'est appliquée ou défaite par cette commande. \n\n## Personnalisation des migrations <span id=\"customizing-migrations\"></span>\n\nIl y a plusieurs manières de personnaliser la commande de migration.\n\n\n### Utilisation des options de ligne de commande <span id=\"using-command-line-options\"></span>\n\nLa commande de migration possède quelques options en ligne de commande qui peuvent être utilisées pour personnaliser son comportement :\n\n* `interactive`: boolean (valeur par défaut `true`), spécifie si la migration doit être effectuées en mode interactif. Lorsque cette option est `true`, l'utilisateur reçoit un message avant que la commande n'effectue certaines actions. Vous désirez peut-être définir cette valeur à `false` si la commande s'exécute en arrière plan. \n\n* `migrationPath`: string (valeur par défaut `@app/migrations`), spécifie le dossier qui stocke tous les fichiers de classe de  migration. Cela peut être spécifié soit comme un chemin de dossier, soit comme un [alias](concept-aliases.md) de chemin. Notez que le dossier doit exister sinon la commande déclenche une erreur.\n\n* `migrationTable`: string (valeur par défaut `migration`), spécifie le nom de la table de base de données pour stocker l'historique de migration. La table est créée automatiquement par la commande si elle n'existe pas encore. Vous pouvez aussi la créer à la main en utilisant la structure `version varchar(255) primary key, apply_time integer`.\n\n* `db`: string (valeur par défaut `db`), spécifie l'identifiant du [composant d'application](structure-application-components.md) base de données. Il représente la base de données à laquelle les migrations sont appliquées avec cette commande. \n\n* `templateFile`: string (valeur par défaut `@yii/views/migration.php`), spécifie le chemin vers le fichier modèle qui est utilisé pour générer le squelette des fichiers de classe de migration. Cela peut être spécifié soit sous forme de chemin de fichier, soit sous forme d'[alias](concept-aliases.md) de chemin. Le fichier modèle est un script PHP dans lequel vous pouvez utiliser une variable prédéfinie nommée `$className` pour obtenir le nom de la classe de migration. \n\n* `generatorTemplateFiles`: array (valeur par défaut `[\n        'create_table' => '@yii/views/createTableMigration.php',\n        'drop_table' => '@yii/views/dropTableMigration.php',\n        'add_column' => '@yii/views/addColumnMigration.php',\n        'drop_column' => '@yii/views/dropColumnMigration.php',\n        'create_junction' => '@yii/views/createTableMigration.php'\n  ]`), spécifie les fichiers modèles pour générer le code de migration. Voir \"[Génération des migrations](#generating-migrations)\" pour plus de détails.\n\n* `fields`: array (tableau) de chaîne de caractères de définition de colonnes utilisées pour créer le code de migration. Valeur par défaut `[]`. Le format de chacune des définitions est `COLUMN_NAME:COLUMN_TYPE:COLUMN_DECORATOR`. Par exemple, `--fields=name:string(12):notNull` produit une colonne chaîne de caractères de taille 12 qui n'est pas `null` (nulle).\n\nL'exemple suivant montre comment vous pouvez utiliser ces options. \n\nPar exemple, si vous voulez appliquer des migrations à un module `forum` dont les fichiers de migration sont situés dans le dossier `migrations` du module, vous pouvez utiliser la commande suivante :\n\n```\n# Appliquer les migrations d'un module forum sans interactivité\nyii migrate --migrationPath=@app/modules/forum/migrations --interactive=0\n```\n\n\n### Configuration globale des commandes <span id=\"configuring-command-globally\"></span>\n\nAu lieu de répéter les mêmes valeurs d'option à chaque fois que vous exécutez une commande de migration, vous pouvez la configurer une fois pour toute dans la configuration de l'application comme c'est montré ci-après : \n\n```php\nreturn [\n    'controllerMap' => [\n        'migrate' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationTable' => 'backend_migration',\n        ],\n    ],\n];\n```\n\nAvec la configuration ci-dessus, à chaque fois que vous exécutez la commande de migration, la table `backend_migration` est utilisée pour enregistrer l'historique de migration. Vous n'avez plus besoin de le spécifier via l'option en ligne de commande `migrationTable`.\n\n### Migrations avec espaces de noms <span id=\"namespaced-migrations\"></span>\n\nDepuis la version 2.0.10, vous pouvez utiliser les espaces de noms pour les classes de migration. Vous pouvez spécifier la liste des espaces de noms des migrations via \n[[yii\\console\\controllers\\MigrateController::migrationNamespaces|migrationNamespaces]]. L'utilisation des espaces de noms pour les classes de migration vous permet l'utilisation de plusieurs emplacement pour les sources des migrations. Par exemple :\n\n```php\nreturn [\n    'controllerMap' => [\n        'migrate' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationPath' => null, // désactive les migration sans espace de noms si app\\migrations est listé ci-dessous\n            'migrationNamespaces' => [\n                'app\\migrations', // Migration ordinaires pour l'ensemble de l'application\n                'module\\migrations', // Migrations pour le module de projet spécifique\n                'some\\extension\\migrations', // Migrations pour l'extension spécifique \n            ],\n        ],\n    ],\n];\n```\n\n> Note : les migrations appliquées appartenant à des espaces de noms différent créent un historique de migration **unique**, p. ex. vous pouvez être incapable d'appliquer ou d'inverser des migrations d'un espace de noms particulier seulement.\n\nLors des opérations sur les migrations avec espaces de noms : la création, l'inversion, etc. vous devez spécifier l'espace de nom complet avant le nom de la migration.\nNotez que le caractère barre oblique inversée (`\\`) est en général considéré comme un caractère spécial dans l'interprète de commandes, c'est pourquoi vous devez l'échapper correctement pour éviter des erreurs d'interprète de commandes ou des comportements incorrects. Par exemple :\n\n```\nyii migrate/create 'app\\\\migrations\\\\createUserTable'\n```\n\n> Note : les migrations spécifiées via [[yii\\console\\controllers\\MigrateController::migrationPath|migrationPath]] \nne peuvent pas contenir un espace de noms, les migrations avec espaces de noms peuvent être appliquée via la propriété [[yii\\console\\controllers\\MigrateController::migrationNamespaces]].\n\nDepuis la version 2.0.12, la propriété [[yii\\console\\controllers\\MigrateController::migrationPath|migrationPath]] \naccepte également un tableau pour spécifier de multiples dossiers contenant des migrations sans espaces de noms.\nCela a été ajouté principalement pour être utilisé dans des projets existants qui utilisent des migrations provenant de différents emplacements. Ces migrations viennent principalement de\nsources externes, comme les extensions à Yii développées par d'autres développeurs,\nqui ne peuvent être facilement modifiées pour utiliser les espaces de noms lors du démarrage avec la nouvelle approche.\n\n\n### Migrations séparées <span id=\"separated-migrations\"></span>\n\nParfois, l'utilisation d'un historique unique de migration pour toutes les migrations du projet n'est pas souhaité. Par exemple : vous pouvez installer une extension 'blog', qui contient des fonctionnalités complètement séparées et contient ses propres migrations, qui ne devraient pas affecter celles dédiées aux fonctionnalités principales du projet.\nSi vous voulez qui plusieurs migrations soient appliquées et complétement tracées séparément l'une de l'autre, vous pouvez configurer de multiples commandes de migration qui utilisent des espaces de noms différents et des tables d'historique de migration différentes :\n\n\n```php\nreturn [\n    'controllerMap' => [\n        // Common migrations for the whole application\n        'migrate-app' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationNamespaces' => ['app\\migrations'],\n            'migrationTable' => 'migration_app',\n            'migrationPath' => null,\n        ],\n        // Migrations for the specific project's module\n        'migrate-module' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationNamespaces' => ['module\\migrations'],\n            'migrationTable' => 'migration_module',\n            'migrationPath' => null,\n        ],\n        // Migrations for the specific extension\n        'migrate-rbac' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationPath' => '@yii/rbac/migrations',\n            'migrationTable' => 'migration_rbac',\n        ],\n    ],\n];\n```\n\nNotez que pour synchroniser la base de données vous devez maintenant exécuter plusieurs commandes au lieu d'une seule : \n\n```\nyii migrate-app\nyii migrate-module\nyii migrate-rbac\n```\n## Migration de multiples base de données <span id=\"migrating-multiple-databases\"></span>\n\nPar défaut, les migrations sont appliquées à la même base de données spécifiée par le [composant d'application](structure-application-components.md) `db`. Si vous voulez que celles-ci soient appliquées à des bases de données différentes, vous pouvez spécifier l'option en ligne de commande `db` comme indiqué ci-dessous :\n\n```\nyii migrate --db=db2\n```\n\nLa commande ci-dessus applique les migration à la base de données `db2`.\n\nParfois, il est possible que vous vouliez appliquer *quelques unes* des migrations à une base de données, et *quelques autres* à une autre base de données. Pour y parvenir, lorsque vous implémentez une classe de migration, vous devez spécifier explicitement l'identifiant du composant base de données que la migration doit utiliser, comme ceci : \n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function init()\n    {\n        $this->db = 'db2';\n        parent::init();\n    }\n}\n```\n\nLa migration ci-dessus est appliquée à `db2`, même si vous spécifiez une autre base via l'option en ligne de commande `db`. Notez que l'historique de migration est toujours enregistré dans la base de données spécifiée par l'option en ligne de commande `db`.\n\nSi vous avez de multiples migrations qui utilisent la même base de données, il est recommandé que vous créiez une classe de migration de base avec le code `init()` ci-dessus. Ensuite, chaque classe de migration peut étendre cette classe de base.\n\n> Astuce : en plus de définir la propriété [[yii\\db\\Migration::db|db]], vous pouvez aussi opérer sur différentes bases de données en créant de nouvelles connexions à ces bases de données dans vos classes de migration. Ensuite,vous utilisez les [méthodes des objets d'accès aux bases de données](db-dao.md) avec ces connexions pour manipuler différentes bases de données.\n\nUne autre stratégie que vous pouvez adopter pour appliquer des migrations à de multiples bases de données est de tenir ces migrations de différentes bases de données dans des chemins différents. Ensuite vous pouvez appliquer les migrations à ces bases de données dans des commandes séparées comme ceci :\n\n```\nyii migrate --migrationPath=@app/migrations/db1 --db=db1\nyii migrate --migrationPath=@app/migrations/db2 --db=db2\n...\n```\n\nLa première commande applique les migrations dans `@app/migrations/db1` à la base de données `db1`, la seconde commande applique les migrations dans `@app/migrations/db2` à `db2`, et ainsi de suite.\n"
  },
  {
    "path": "docs/guide-fr/db-query-builder.md",
    "content": "Le constructeur de requêtes\n===========================\n\nConstruit sur la base des [objets d'accès aux bases de données (DAO)](db-dao.md), le constructeur de requêtes vous permet de construire des requêtes SQL par programme qui sont indifférentes au système de gestion de base de données utilisé. Comparé à l'écriture d'instructions SQL brutes, l'utilisation du constructeur de requêtes vous aide à écrire du code relatif à SQL plus lisible et à générer des instructions SQL plus sûres. \n\nL'utilisation du constructeur de requêtes comprend ordinairement deux étapes :\n\n1. Construire un objet [[yii\\db\\Query]] pour représenter différentes parties (p. ex. `SELECT`, `FROM`) d'une instruction SQL. \n2. Exécuter une méthode de requête (p. ex. `all()`) de [[yii\\db\\Query]] pour retrouver des données dans la base de données. \n\n\n\nLe code suivant montre une manière typique d'utiliser le constructeur de requêtes. \n\n```php\n$rows = (new \\yii\\db\\Query())\n    ->select(['id', 'email'])\n    ->from('user')\n    ->where(['last_name' => 'Smith'])\n    ->limit(10)\n    ->all();\n```\n\nLe code ci-dessus génère et exécute la requête SQL suivante, dans laquelle le paramètre `:last_name` est lié à la chaîne de caractères `'Smith'`.\n\n```sql\nSELECT `id`, `email` \nFROM `user`\nWHERE `last_name` = :last_name\nLIMIT 10\n```\n\n\n> Info: génélalement vous travaillez essentiellement avec [[yii\\db\\Query]] plutôt qu'avec [[yii\\db\\QueryBuilder]].\n Le dernier est implicitement invoqué par le premier lorsque vous appelez une des méthodes de requête. [[yii\\db\\QueryBuilder]] est la classe en charge de la génération des instructions SQL dépendantes du système de gestion de base de données (p. ex. entourer les noms de table/colonne par des marques de citation différemment) à partir d'objets [[yii\\db\\Query]] indifférents au système de gestion de base de données.\n\n\n\n\n## Construction des requêtes <span id=\"building-queries\"></span>\n\nPour construire un objet [[yii\\db\\Query]], vous appelez différentes méthodes de construction de requêtes pour spécifier différentes parties de la requête SQL.\n Les noms de ces méthodes ressemblent aux mots clés de SQL utilisés dans les parties correspondantes de l'instruction SQL.\nPar exemple, pour spécifier la partie `FROM` d'une requête SQL, vous appelez la méthode [[yii\\db\\Query::from()|from()]].\n Toutes les méthodes de construction de requêtes retournent l'objet *query* lui-même, ce qui vous permet d'enchaîner plusieurs appels.\n\nDans ce qui suit, nous décrivons l'utilisation de chacune des méthodes de requête.\n\n\n### [[yii\\db\\Query::select()|select()]] <span id=\"select\"></span>\n\nLa méthode [[yii\\db\\Query::select()|select()]] spécifie le fragment `SELECT` d'une instruction SQL. Vous pouvez spécifier les colonnes à sélectionner soit sous forme de chaînes de caractères, soit sous forme de tableaux, comme ci-après. \nLes noms des colonnes sélectionnées sont automatiquement entourés des marques de citation lorsque l'instruction SQL\n est générée à partir de l'objet *query* (requête). \n \n```php\n$query->select(['id', 'email']);\n\n// équivalent à:\n\n$query->select('id, email');\n```\n\nLes noms des colonnes sélectionnées peuvent inclure des préfixes de table et/ou des alias de colonne, comme vous le faites en écrivant une requête SQL brute.\nPar exemple :\n\n```php\n$query->select(['user.id AS user_id', 'email']);\n\n// équivalent à:\n\n$query->select('user.id AS user_id, email');\n```\n\nSi vous utilisez le format tableau pour spécifier les colonnes, vous pouvez aussi utiliser les clés du tableau pour spécifier les alias de colonne. \nPar exemple, le code ci-dessus peut être réécrit comme ceci : \n\n```php\n$query->select(['user_id' => 'user.id', 'email']);\n```\n\nSi vous n'appelez pas la méthode [[yii\\db\\Query::select()|select()]] en construisant une requête, `*` est sélectionné, \nce qui signifie la sélection de *toutes* les colonnes. \n\nEn plus des noms de colonne, vous pouvez aussi sélectionner des expression de base de données. Vous devez utiliser le format tableau en sélectionnant une expression de base de données qui contient des virgules pour éviter des entourages automatiques incorrects des noms par des marques de citation. \nPar exemple :\n\n```php\n$query->select([\"CONCAT(first_name, ' ', last_name) AS full_name\", 'email']); \n```\n\nComme en tout lieu où il est fait appel à du SQL brut, vous devez utiliser la [syntaxe des marques de citation indifférentes au système de gestion de base de données](db-dao.md#quoting-table-and-column-names) pour les noms de table et \nde colonne lorsque vous écrivez les expressions de base de données dans `select`. \n\nDepuis la version 2.0.1, vous pouvez aussi sélectionner des sous-requêtes. Vous devez spécifier chacune des sous-requêtes en termes d'objet [[yii\\db\\Query]]. \nPar exemple :\n \n```php\n$subQuery = (new Query())->select('COUNT(*)')->from('user');\n\n// SELECT `id`, (SELECT COUNT(*) FROM `user`) AS `count` FROM `post`\n$query = (new Query())->select(['id', 'count' => $subQuery])->from('post');\n```\n\nPour sélectionner des lignes distinctes, vous pouvez appeler [[yii\\db\\Query::distinct()|distinct()]], comme ceci :\n\n```php\n// SELECT DISTINCT `user_id` ...\n$query->select('user_id')->distinct();\n```\n\nVous pouvez appeler [[yii\\db\\Query::addSelect()|addSelect()]] pour sélectionner des colonnes additionnelles. Par exemple :\n\n```php\n$query->select(['id', 'username'])\n    ->addSelect(['email']);\n```\n\n\n### [[yii\\db\\Query::from()|from()]] <span id=\"from\"></span>\n\nLa méthode [[yii\\db\\Query::from()|from()]] spécifie le fragment `FROM`d'une instruction. Par exemple :\n\n```php\n// SELECT * FROM `user`\n$query->from('user');\n```\n\nVous pouvez spécifier les tables à sélectionner soit sous forme de chaînes de caractères, soit sous forme de tableaux. Les noms de table peuvent contenir des préfixes et/ou des alias de table. \nPar exemple :\n\n```php\n$query->from(['public.user u', 'public.post p']);\n\n// équivalent à :\n\n$query->from('public.user u, public.post p');\n```\n\nSi vous utilisez le format tableau, vous pouvez aussi utiliser les clés du tableau pour spécifier les alias de table, comme suit : \n\n```php\n$query->from(['u' => 'public.user', 'p' => 'public.post']);\n```\n\nEn plus des noms de table, vous pouvez aussi sélectionner à partir de sous-requêtes en les spécifiant en termes d'objets [[yii\\db\\Query]].\nPar exemple :\n\n```php\n$subQuery = (new Query())->select('id')->from('user')->where('status=1');\n\n// SELECT * FROM (SELECT `id` FROM `user` WHERE status=1) u \n$query->from(['u' => $subQuery]);\n```\n\n#### Préfixes\nUn [[yii\\db\\Connection::$tablePrefix|préfixe de table]] peut aussi être appliqué. Les instructions de mise en œuvre sont données à la section \n[\"Entourage des noms de table et de colonne par des marques de citation\" du guide sur les objets d'accès aux bases de données\" ](db-dao.md#quoting-table-and-column-names).\n\n### [[yii\\db\\Query::where()|where()]] <span id=\"where\"></span>\nLa méthode [[yii\\db\\Query::where()|where()]] spécifie le fragment `WHERE`d'une requête SQL. Vous pouvez utiliser un des quatre formats suivants pour spécifier une condition `WHERE` :\n\n- format chaîne de caractères, p. ex. `'status=1'`\n- format haché, p. ex. `['status' => 1, 'type' => 2]`\n- format opérateur, p. ex. `['like', 'name', 'test']`\n- format objet, p. ex. `new LikeCondition('name', 'LIKE', 'test')`\n\n\n#### Format chaîne de caractères <span id=\"string-format\"></span>\n\nLe format chaîne de caractères est celui qui convient le mieux pour spécifier des conditions très simples ou si vous avez besoin d'utiliser les fonctions incorporées au système de gestion de base de données. \nIl fonctionne comme si vous écriviez une requête SQL brute. Par exemple : \n\n```php\n$query->where('status=1');\n\n// ou utilisez la liaison des paramètres pour lier des valeurs dynamiques des paramètres.\n$query->where('status=:status', [':status' => $status]);\n\n//  SQL brute utilisant la fonction MySQL YEAR() sur un champ de date\n$query->where('YEAR(somedate) = 2015');\n```\n\nN'imbriquez PAS les variables directement dans la condition comme ce qui suit, spécialement si les valeurs des variables proviennent d'entrées utilisateur, parce que cela rendrait votre application SQL sujette aux attaques par injections SQL.\n \n\n```php\n// Dangereux! Ne faites PAS cela sauf si vous êtes tout à fait sûr que $status est un entier\n$query->where(\"status=$status\");\n```\n\nLorsque vous utilisez la `liaison des paramètres`, vous pouvez appeler [[yii\\db\\Query::params()|params()]] ou [[yii\\db\\Query::addParams()|addParams()]] pour spécifier les paramètres séparément.\n\n\n```php\n$query->where('status=:status')\n    ->addParams([':status' => $status]);\n```\n\nComme dans tous les endroits ou il est fait appel à du SQL, vous pouvez utiliser la [syntaxe d'entourage par des marques de citation indifférente au système de gestion de base de données](db-dao.md#quoting-table-and-column-names) pour les noms de table et de colonne lorsque vous écrivez les conditions au format chaîne de caractères.\n\n\n#### Format haché <span id=\"hash-format\"></span>\n\nLe format valeur de hachage convient le mieux pour spécifier de multiples sous-conditions concaténées par `AND`, chacune étant une simple assertion d'égalité. \nIl se présente sous forme de tableau dont les clés sont les noms des colonnes et les valeurs les valeurs correspondantes que les valeurs des colonnes devraient avoir. \nPar exemple : \n\n```php\n// ...WHERE (`status` = 10) AND (`type` IS NULL) AND (`id` IN (4, 8, 15))\n$query->where([\n    'status' => 10,\n    'type' => null,\n    'id' => [4, 8, 15],\n]);\n```\n\nComme vous pouvez le voir, le constructeur de requêtes est assez intelligent pour manipuler correctement les valeurs qui sont soit nulles, soit des tableaux.\n\nVous pouvez utiliser aussi des sous-requêtes avec le format haché comme suit :\n\n```php\n$userQuery = (new Query())->select('id')->from('user');\n\n// ...WHERE `id` IN (SELECT `id` FROM `user`)\n$query->where(['id' => $userQuery]);\n```\n\nEn utilisant le format haché, Yii, en interne, utilise la liaison des paramètres pour les valeurs de façon à ce que, contrairement au [format chaîne de caractères](#string-format), vous n'ayez pas à ajouter les paramètres à la main.\nCependant, notez que Yii ne procède pas à l'échappement des noms de colonne, c'est pourquoi si vous passez un nom de variable obtenu de l'utilisateur en tant que nom de colonne sans vérification, l'application devient vulnérable à l'injection SQL. \nAfin de maintenir l'application sûre, soit n'utilisez pas de variables comme nom de colonne, soit filtrez les variables par une liste blanche. \nDans le cas où vous avez besoin d'obtenir un nom de colonne de l'utilisateur, lisez \nl'article du guide [Filtrage des données](output-data-widgets.md#filtering-data).\nAinsi l'exemple de code suivant est vulnérable :\n\n```php\n// Vulnerable code:\n$column = $request->get('column');\n$value = $request->get('value');\n$query->where([$column => $value]);\n// $value est sûre, mais le nom de colonne n'est pas encodé!\n```\n\n#### Format opérateur <span id=\"operator-format\"></span>\n\nLe format opérateur vous permet de spécifier des conditions arbitraires par programmation. Il accepte les formats suivants : \n\n```php\n[operator, operand1, operand2, ...]\n```\n\ndans lequel chacun des opérandes peut être spécifié au format chaîne de caractères, au format haché ou au format opérateur de façon récursive, tandis que l'opérateur peut être un de ceux qui suivent : \n\n- `and`: les opérandes doivent être concaténés en utilisant `AND`. Par exemple, `['and', 'id=1', 'id=2']` génère `id=1 AND id=2`. \n  Si un opérande est un tableau, il est converti en une chaîne de caractères en utilisant les règles décrites ici. \n  Par exemple, `['and', 'type=1', ['or', 'id=1', 'id=2']]` génère `type=1 AND (id=1 OR id=2)`. \n  La méthode ne procède à aucun entourage par des marques de citation, ni à aucun échappement.\n\n\n\n- `or`: similaire à l'opérateur `and` sauf que les opérandes sont concaténés en utilisant `OR`.\n\n- `not`: ne réclame que l'opérande 1, qui est emballé dans  `NOT()`. Par exemple, `['not', 'id=1']` génère `NOT (id=1)`. L'opérande 1 peut aussi être un tableau pour décrire des expressions multiples. Par exemple `['not', ['status' => 'draft', 'name' => 'example']]` génère `NOT ((status='draft') AND (name='example'))`.\n\n- `between`: l'opérande 1 doit être le nom de la colonne, et les opérandes 2 et 3 doivent être les valeurs de départ et de fin de la plage dans laquelle la colonne doit être. Par exemple, `['between', 'id', 1, 10]` génère `id BETWEEN 1 AND 10`.\n  Dans le cas où vous avez besoin de construire une expression dans laquelle la valeur est entre deux colonnes (telle que `11 BETWEEN min_id AND max_id`), \n  vous devez utiliser  [[yii\\db\\conditions\\BetweenColumnsCondition|BetweenColumnsCondition]]. \n  Reportez-vous au chapitre [Conditions – Format d'objet](#object-format) pour en savoir plus sur la définition des conditions d'objet.\n\n\n\n- `not between`: similaire à `between` sauf que  `BETWEEN` est remplacé par `NOT BETWEEN` \n  dans la condition générée.\n\n- `in`: l'opérande 1 doit être une colonne ou une expression de base de données. \nL'opérande 2 peut être soit un tableau, soit un objet `Query`. \n  Il génère une condition `IN`. Si l'opérande 2 est un tableau, il représente la plage des valeurs que la colonne ou l'expression de base de données peut prendre. \n  Si l'opérande 2 est un objet `Query`, une sous-requête est générée et utilisée comme plage pour la colonne ou l'expression de base de données. \n  Par exemple, `['in', 'id', [1, 2, 3]]` génère `id IN (1, 2, 3)`. \n  La méthode assure correctement l'entourage des noms de colonnes par des marques de citation et l'échappement des valeurs de la plage. \n  L'opérateur `in` prend aussi en charge les colonnes composites. \n  Dans ce cas, l'opérande 1 doit être un tableau des colonnes, tandis que l'opérateur 2 doit être un tableau de tableaux, ou un objet `Query` représentant la plage de colonnes.\n\n- `not in`: similaire à l'opérateur `in` sauf que `IN` est remplacé par `NOT IN` dans la condition générée.\n\n- `like`: l'opérande 1 doit être une colonne ou une expression de base de données, tandis que l'opérande 2 doit être une chaîne de caractères ou \n  un tableau représentant les valeurs que cette colonne ou cette expression de base de données peuvent être. \n  Par exemple, `['like', 'name', 'tester']` génère `name LIKE '%tester%'`. \n  Lorsque la plage de valeurs est donnée sous forme de tableau, de multiples prédicats `LIKE` sont générés et concaténés \n  en utilisant `AND`. \n  Par exemple, `['like', 'name', ['test', 'sample']]` génère `name LIKE '%test%' AND name LIKE '%sample%'`. \n  Vous pouvez également fournir un troisième paramètre facultatif pour spécifier comment échapper les caractères spéciaux dans les valeurs. \n  Les opérandes doivent être un tableau de correspondance \n  entre les caractères spéciaux et les contre-parties échappées. \n  Si cet opérande n'est pas fourni, une mise en correspondance par défaut est utilisée. \n  Vous pouvez utiliser `false` ou un tableau vide pour indiquer que les valeurs sont déjà échappées et qu'aucun échappement ne doit être appliqué. \n  Notez que lorsqu'un tableau de mise en correspondance pour l'échappement est utilisé (ou quand le troisième opérande n'est pas fourni), les valeurs sont automatiquement entourées par une paire de caractères `%`. \n\n  > Note: lors de l'utilisation de PostgreSQL vous pouvez aussi utiliser [`ilike`](https://www.postgresql.org/docs/8.3/static/functions-matching.html#FUNCTIONS-LIKE)   \n  >à la place de `like` pour une mise en correspondance insensible à la casse.\n\n- `or like`: similaire à l'opérateur `like` sauf que `OR`est utilisé pour concaténer les prédicats `LIKE` \n  quand l'opérande 2 est un tableau. \n\n- `not like`: similaire à l'opérateur `like` sauf que `LIKE` est remplacé par `NOT LIKE` \n  dans la condition générée.\n\n- `or not like`: similaire à l'opérateur `not like` sauf que `OR` est utilisé pour concaténer \n  les prédicats `NOT LIKE`.\n\n- `exists`: requiert un opérande que doit être une instance de [[yii\\db\\Query]] représentant la sous-requête.  \n  Il construit une expression `EXISTS (sub-query)`.\n\n- `not exists`: similaire à l'opérateur `exists` et construit une expression `NOT EXISTS (sub-query)`.\n\n- `>`, `<=`, ou tout autre opérateur de base de données valide qui accepte deux opérandes : \n  le premier opérande doit être un nom de colonne, tandis que le second doit être une valeur. Par exemple, `['>', 'age', 10]` génère `age>10`.\n\nEn utilisant le format opérateur, Yii, en interne, utilise la liaison des paramètres afin, que contrairement au [format chaîne de caractères](#string-format), ici, vous n'avez pas besoin d'ajouter les paramètres à la main. \nCependant, notez que Yii ne procède pas à l'échappement des noms de colonne, c'est pourquoi si vous passez un nom de variable obtenu de l'utilisateur en tant que nom de colonne sans vérification, l'application devient vulnérable à l'injection SQL. \nAfin de maintenir l'application sûre, soit n'utilisez pas de variables comme nom de colonne, soit filtrez les variables par une liste blanche. \nDans le cas où vous avez besoin d'obtenir un nom de colonne de l'utilisateur, lisez \nl'article du guide [Filtrage des données](output-data-widgets.md#filtering-data).\nAinsi l'exemple de code suivant est vulnérable :\n\n```php\n// Code vulnérable:\n$column = $request->get('column');\n$value = $request->get('value');\n$query->where(['=', $column, $value]);\n// $value est sûre, mais le nom  $column n'est pas encodé !\n```\n\n#### Format objet <span id=\"object-format\"></span>\n\nLe format objet est disponible depuis 2.0.14 et est à la fois le moyen plus puissant et le plus complexe pour définir des conditions.\nVous devez le suivre si vous voulez construire votre propre abstraction au-dessus du constructeur de requêtes (query builder) ou si vous voulez mettre en œuvre vos propres conditions complexes.\n\n\nLes instances de classes de condition sont immuables. \nLe seul but est de stocker des données de condition et de fournir des obtenteurs (getters) pour les constructeurs de conditions. \nLa classe « constructeur de condition » (condition builder) est une classe qui contient la logique qui transforme les données stockées en condition dans une expression SQL.\n\nEn interne, les formats décrits plus haut sont implicitement convertis en format objet avant de construire le SQL brut, \n aussi est-il possible de combiner les formats en une condition unique :\n\n```php\n$query->andWhere(new OrCondition([\n    new InCondition('type', 'in', $types),\n    ['like', 'name', '%good%'],\n    'disabled=false'\n]))\n```\n\nLa conversion du format opérateur au format objet est accomplie en fonction de la propriété\n[[yii\\db\\QueryBuilder::conditionClasses|QueryBuilder::conditionClasses]] , \nqui fait correspondre des noms d'opérateurs à des nom de classe représentatives :\n\n- `AND`, `OR` -> `yii\\db\\conditions\\ConjunctionCondition`\n- `NOT` -> `yii\\db\\conditions\\NotCondition`\n- `IN`, `NOT IN` -> `yii\\db\\conditions\\InCondition`\n- `BETWEEN`, `NOT BETWEEN` -> `yii\\db\\conditions\\BetweenCondition`\n\nEt ainsi de suite.\n\nL'utilisation du format objet rend possible de créer vos propres conditions ou de changer la manière dont celles par défaut sont construites.\nReportez-vous au chapitre  [Ajout de conditions et d'expressions personnalisées](#adding-custom-conditions-and-expressions) pour en savoir plus.\n\n\n#### Ajout de conditions <span id=\"appending-conditions\"></span>\n\nVous pouvez utiliser [[yii\\db\\Query::andWhere()|andWhere()]] ou [[yii\\db\\Query::orWhere()|orWhere()]] pour ajouter des conditions supplémentaires à une condition existante. \nVous pouvez les appeler plusieurs fois pour ajouter plusieurs conditions séparément. \nPar exemple :\n\n```php\n$status = 10;\n$search = 'yii';\n\n$query->where(['status' => $status]);\n\nif (!empty($search)) {\n   $query->andWhere(['like', 'title', $search]);\n}\n```\n\nSi `$search` n'est pas vide, la condition `WHERE` suivante est générée :\n\n```sql\nWHERE (`status` = 10) AND (`title` LIKE '%yii%')\n```\n\n\n#### Conditions de filtrage <span id=\"filter-conditions\"></span>\n\nLors de la construction de conditions  `WHERE` basées sur des entrées de l'utilisateur final, \nvous voulez généralement ignorer les valeurs entrées qui sont vides. \nPar exemple, dans un formulaire de recherche par nom d'utilisateur ou par adresse de courriel, vous aimeriez ignorer la condition nom d'utilisateur/adresse de courriel si l'utilisateur n'a rien saisi dans les champs correspondants. \nVous pouvez faire cela en utilisant la méthode [[yii\\db\\Query::filterWhere()|filterWhere()]] :\n\n```php\n// $username et $email sont entrées par l'utilisateur\n$query->filterWhere([\n    'username' => $username,\n    'email' => $email,\n]);\n```\n\nLa seule différence entre [[yii\\db\\Query::filterWhere()|filterWhere()]] et [[yii\\db\\Query::where()|where()]] est que la première ignore les valeurs vides fournies dans la condition au [format haché](#hash-format). \nAinsi si `$email` est vide alors que `$username` ne l'est pas, \nle code ci dessus produit la condition SQL `WHERE username=:username`.\n\n> Info: une valeur est considérée comme vide si elle est nulle, un tableau vide, ou un chaîne de caractères vide, ou un chaîne de caractères constituée d'espaces uniquement.\n\nComme avec [[yii\\db\\Query::andWhere()|andWhere()]] et [[yii\\db\\Query::orWhere()|orWhere()]], \nvous pouvez utiliser [[yii\\db\\Query::andFilterWhere()|andFilterWhere()]] et [[yii\\db\\Query::orFilterWhere()|orFilterWhere()]] \npour ajouter des conditions de filtrage supplémentaires à une condition existante.\n\nEn outre, il y a [[yii\\db\\Query::andFilterCompare()]] qui peut déterminer intelligemment l'opérateur \nen se basant sur ce qu'il y a dans les valeurs : \n\n```php\n$query->andFilterCompare('name', 'John Doe');\n$query->andFilterCompare('rating', '>9');\n$query->andFilterCompare('value', '<=100');\n```\n\nVous pouvez aussi utiliser un opérateur explicitement :\n\n```php\n$query->andFilterCompare('name', 'Doe', 'like');\n```\n\nDepuis Yii 2.0.1, il existe des méthodes similaires pour la condition  `HAVING` :\n\n- [[yii\\db\\Query::filterHaving()|filterHaving()]]\n- [[yii\\db\\Query::andFilterHaving()|andFilterHaving()]]\n- [[yii\\db\\Query::orFilterHaving()|orFilterHaving()]]\n\n### [[yii\\db\\Query::orderBy()|orderBy()]] <span id=\"order-by\"></span>\n\nLa méthode [[yii\\db\\Query::orderBy()|orderBy()]] spécifie le fragment `ORDER BY` d'une requête SQL. Par exemple :\n\n```php\n// ... ORDER BY `id` ASC, `name` DESC\n$query->orderBy([\n    'id' => SORT_ASC,\n    'name' => SORT_DESC,\n]);\n```\n \nDans le code ci-dessus, les clés du tableau sont des noms de colonnes, tandis que les valeurs sont les instructions de direction de tri. \nLa constante PHP `SORT_ASC` spécifie un tri ascendant et `SORT_DESC`, un tri descendant.\n\nSi `ORDER BY` ne fait appel qu'à des noms de colonnes simples, vous pouvez le spécifier en utilisant une chaîne de caractères, juste comme vous le faites en écrivant des instructions SQL brutes. \nPar exemple :\n\n```php\n$query->orderBy('id ASC, name DESC');\n```\n\n> Note: vous devez utiliser le format tableau si `ORDER BY` fait appel à une expression de base de données.\n\nVous pouvez appeler [[yii\\db\\Query::addOrderBy()|addOrderBy()]] pour ajouter des colonnes supplémentaires au fragment `ORDER BY`. \nPar exemple :\n\n```php\n$query->orderBy('id ASC')\n    ->addOrderBy('name DESC');\n```\n\n\n### [[yii\\db\\Query::groupBy()|groupBy()]] <span id=\"group-by\"></span>\n\nLa méthode [[yii\\db\\Query::groupBy()|groupBy()]] spécifie le fragment `GROUP BY` d'une requête SQL. Par exemple :\n\n```php\n// ... GROUP BY `id`, `status`\n$query->groupBy(['id', 'status']);\n```\n\nSi `GROUP BY` ne fait appel qu'à des noms de colonnes simples, vous pouvez le spécifier en utilisant un chaîne de caractères, juste comme vous le faîtes en écrivant des instructions SQL brutes. \nPar exemple :\n\n```php\n$query->groupBy('id, status');\n```\n\n> Note: vous devez utiliser le format tableau si `GROUP BY` fait appel à une expression de base de données.\n \nVous pouvez appeler [[yii\\db\\Query::addGroupBy()|addGroupBy()]] pour ajouter des colonnes au fragment `GROUP BY`. \nPar exemple :\n\n```php\n$query->groupBy(['id', 'status'])\n    ->addGroupBy('age');\n```\n\n\n### [[yii\\db\\Query::having()|having()]] <span id=\"having\"></span>\n\nLa méthode [[yii\\db\\Query::having()|having()]] \nspécifie le fragment `HAVING` d'un requête SQL. Elle accepte une condition qui peut être spécifiée de la même manière que celle pour [where()](#where). Par exemple :\n\n```php\n// ... HAVING `status` = 1\n$query->having(['status' => 1]);\n```\n\nReportez-vous à la documentation de [where()](#where) pour plus de détails sur la manière de spécifier une condition. \n\nVous pouvez appeler [[yii\\db\\Query::andHaving()|andHaving()]] ou [[yii\\db\\Query::orHaving()|orHaving()]] pour ajouter des conditions supplémentaires au fragment `HAVING` fragment. \nPar exemple :\n\n```php\n// ... HAVING (`status` = 1) AND (`age` > 30)\n$query->having(['status' => 1])\n    ->andHaving(['>', 'age', 30]);\n```\n\n\n### [[yii\\db\\Query::limit()|limit()]] et [[yii\\db\\Query::offset()|offset()]] <span id=\"limit-offset\"></span>\n\nLes méthodes [[yii\\db\\Query::limit()|limit()]] et [[yii\\db\\Query::offset()|offset()]] spécifient les fragments `LIMIT` et `OFFSET` d'une requête SQL. \nPar exemple : \n\n```php\n// ... LIMIT 10 OFFSET 20\n$query->limit(10)->offset(20);\n```\n\nSi vous spécifiez une limite ou un décalage (p. ex. une valeur négative), il est ignoré. \n\n> Info: pour les systèmes de gestion de base de données qui ne prennent pas en charge `LIMIT` et `OFFSET` (p. ex. MSSQL), \nle constructeur de requêtes génère une instruction SQL qui émule le comportement `LIMIT`/`OFFSET`.\n\n\n### [[yii\\db\\Query::join()|join()]] <span id=\"join\"></span>\n\nLa méthode [[yii\\db\\Query::join()|join()]] spécifie le fragment `JOIN` d'une requête SQL. Par exemple :\n\n```php\n// ... LEFT JOIN `post` ON `post`.`user_id` = `user`.`id`\n$query->join('LEFT JOIN', 'post', 'post.user_id = user.id');\n```\n\nLa méthode [[yii\\db\\Query::join()|join()]] accepte quatre paramètres :\n\n- `$type`: type de jointure , p. ex. `'INNER JOIN'`, `'LEFT JOIN'`.\n- `$table`: le nom de la table à joindre.\n- `$on`: facultatif, la condition de jointure, c.-à-d. le fragment `ON`. \n  Reportez-vous à [where()](#where) pour des détails sur la manière de spécifier une condition. \n  Notez, que la syntaxe tableau ne fonctionne **PAS** pour spécifier une condition basée sur une colonne, p. ex. `['user.id' => 'comment.userId']` conduit à une condition \n  où l'identifiant utilisateur doit être égal à la chaîne de caractères `'comment.userId'`. \n  Vous devez utiliser la syntaxe chaîne de caractères à la place et spécifier la condition `'user.id = comment.userId'`.\n- `$params`: facultatif, les paramètres à lier à la condition de jointure. \n\nVous pouvez utiliser les méthodes raccourcies suivantes pour spécifier `INNER JOIN`, `LEFT JOIN` et `RIGHT JOIN`, respectivement.\n\n- [[yii\\db\\Query::innerJoin()|innerJoin()]]\n- [[yii\\db\\Query::leftJoin()|leftJoin()]]\n- [[yii\\db\\Query::rightJoin()|rightJoin()]]\n\nPar exemple :\n\n```php\n$query->leftJoin('post', 'post.user_id = user.id');\n```\n\nPour joindre plusieurs tables, appelez les méthodes join ci-dessus plusieurs fois, une fois pour chacune des tables.\n\nEn plus de joindre des tables, vous pouvez aussi joindre des sous-requêtes. Pour faire cela, spécifiez les sous-requêtes à joindre sous forme d'objets [[yii\\db\\Query]]. \nPar exemple :\n\n```php\n$subQuery = (new \\yii\\db\\Query())->from('post');\n$query->leftJoin(['u' => $subQuery], 'u.id = author_id');\n```\n\nDans ce cas, vous devez mettre la sous-requête dans un tableau et utiliser les clés du tableau pour spécifier les alias.\n\n\n### [[yii\\db\\Query::union()|union()]] <span id=\"union\"></span>\n\nLa méthode [[yii\\db\\Query::union()|union()]] spécifie le fragment `UNION` d'une requête SQL. Par exemple :\n\n```php\n$query1 = (new \\yii\\db\\Query())\n    ->select(\"id, category_id AS type, name\")\n    ->from('post')\n    ->limit(10);\n\n$query2 = (new \\yii\\db\\Query())\n    ->select('id, type, name')\n    ->from('user')\n    ->limit(10);\n\n$query1->union($query2);\n```\n\nVous pouvez appeler [[yii\\db\\Query::union()|union()]] plusieurs fois pour ajouter plus de fragments `UNION`. \n\n\n## Méthodes de requête <span id=\"query-methods\"></span>\n\nL'objet [[yii\\db\\Query]] fournit un jeu complet de méthodes pour différents objectifs de requêtes :\n\n- [[yii\\db\\Query::all()|all()]]: retourne un tableau de lignes dont chacune des lignes est un tableau associatif de paires clé-valeur.\n- [[yii\\db\\Query::one()|one()]]: retourne la première ligne du résultat. \n- [[yii\\db\\Query::column()|column()]]: retourne la première colonne du résultat. \n- [[yii\\db\\Query::scalar()|scalar()]]: retourne une valeur scalaire située au croisement de la première ligne et de la première colonne du résultat.\n- [[yii\\db\\Query::exists()|exists()]]: retourne une valeur précisant si le résultat de la requête contient un résultat.\n- [[yii\\db\\Query::count()|count()]]: retourne le résultat d'une requête `COUNT`..\n- D'autres méthodes d'agrégation de requêtes, y compris [[yii\\db\\Query::sum()|sum($q)]], [[yii\\db\\Query::average()|average($q)]], [[yii\\db\\Query::max()|max($q)]], [[yii\\db\\Query::min()|min($q)]]. \n  Le paramètre `$q` est obligatoire pour ces méthodes \n  et peut être soit un nom de colonne, soit une expression de base de données.\n\nPar exemple :\n\n```php\n// SELECT `id`, `email` FROM `user`\n$rows = (new \\yii\\db\\Query())\n    ->select(['id', 'email'])\n    ->from('user')\n    ->all();\n    \n// SELECT * FROM `user` WHERE `username` LIKE `%test%`\n$row = (new \\yii\\db\\Query())\n    ->from('user')\n    ->where(['like', 'username', 'test'])\n    ->one();\n```\n\n> Note: la méthode [[yii\\db\\Query::one()|one()]] retourne seulement la première ligne du résultat de la requête. \n  Elle n'ajoute PAS `LIMIT 1` à l'instruction SQL générée. \n  Cela est bon et préférable si vous savez que la requête ne retourne qu'une seule \n  ou quelques lignes de données (p. ex. si vous effectuez une requête avec quelques clés primaires). \n  Néanmoins, si la requête peut potentiellement retourner de nombreuses lignes de données, vous devriez appeler `limit(1)` explicitement pour améliorer la performance, p. ex. `(new \\yii\\db\\Query())->from('user')->limit(1)->one()`.\n\nToutes ces méthodes de requête acceptent un paramètre supplémentaire `$db` représentant la [[yii\\db\\Connection|connexion à la base de données]] \nqui doit être utilisée pour effectuer la requête. \nSi vous omettez ce paramètre, le [composant d'application](structure-application-components.md) `db` est utilisé en tant que connexion à la base de données. Ci-dessous, nous présentons un autre exemple utilisant la méthode [[yii\\db\\Query::count()|count()]] :\n\n```php\n// exécute SQL: SELECT COUNT(*) FROM `user` WHERE `last_name`=:last_name\n$count = (new \\yii\\db\\Query())\n    ->from('user')\n    ->where(['last_name' => 'Smith'])\n    ->count();\n```\n\nLorsque vous appelez une méthode de requête de [[yii\\db\\Query]], elle effectue réellement le travail suivant en interne :\n\n* Appelle [[yii\\db\\QueryBuilder]] pour générer une instruction SQL basée sur la construction courante de [[yii\\db\\Query]] ;\n* Crée un objet [[yii\\db\\Command]] avec l'instruction SQL générée ;\n* Appelle une méthode de requête (p. ex. [[yii\\db\\Command::queryAll()|queryAll()]]) de [[yii\\db\\Command]] pour exécuter une instruction SQL et retrouver les données.\n\nParfois, vous voulez peut-être examiner ou utiliser une instruction SQL construite à partir d'un objet [[yii\\db\\Query]]. Vous pouvez faire cela avec le code suivant :\n\n\n```php\n$command = (new \\yii\\db\\Query())\n    ->select(['id', 'email'])\n    ->from('user')\n    ->where(['last_name' => 'Smith'])\n    ->limit(10)\n    ->createCommand();\n    \n// affiche l'instruction SQL\necho $command->sql;\n// affiche les paramètres à lier \nprint_r($command->params);\n\n// retourne toutes les lignes du résultat de la requête \n$rows = $command->queryAll();\n```\n\n\n### Indexation des résultats de la requête <span id=\"indexing-query-results\"></span>\n\nLorsque vous appelez [[yii\\db\\Query::all()|all()]], elle retourne un tableau de lignes qui sont indexées par des entiers consécutifs. \nParfois, vous désirez peut-être les indexer différemment, comme les indexer par une colonne particulière ou par des expressions donnant une valeur. \nVous pouvez le faire en appelant [[yii\\db\\Query::indexBy()|indexBy()]] avant [[yii\\db\\Query::all()|all()]]. \nPar exemple :\n\n```php\n// retourne [100 => ['id' => 100, 'username' => '...', ...], 101 => [...], 103 => [...], ...]\n$query = (new \\yii\\db\\Query())\n    ->from('user')\n    ->limit(10)\n    ->indexBy('id')\n    ->all();\n```\n\nPour indexer par des valeurs d'expressions, passez une fonction anonyme à la méthode [[yii\\db\\Query::indexBy()|indexBy()]] :\n\n```php\n$query = (new \\yii\\db\\Query())\n    ->from('user')\n    ->indexBy(function ($row) {\n        return $row['id'] . $row['username'];\n    })->all();\n```\n\nLe fonction anonyme accepte un paramètre `$row` qui contient les données de la ligne courante \net retourne une valeur scalaire qui est utilisée comme la valeur d'index de la ligne courante.\n\n> Note: contrairement aux méthodes de requête telles que [[yii\\db\\Query::groupBy()|groupBy()]] ou [[yii\\db\\Query::orderBy()|orderBy()]] \nqui sont converties en SQL et font partie de la requête, cette méthode ne fait son travail qu'après que les données ont été retrouvées dans la base de données. \n> Cela signifie que seuls les noms de colonne qui on fait partie du fragment SELECT dans votre requête peuvent être utilisés.\n> De plus, si vous avez sélectionné une colonne avec un préfixe de table, p. ex. `customer.id`, le jeu de résultats ne contient que `id` c'est pourquoi vous devez appeler\n> `->indexBy('id')` sans  préfixe de table.\n\n\n### Requêtes par lots <span id=\"batch-query\"></span>\n\nLorsque vous travaillez sur de grandes quantités de données, des méthodes telles que [[yii\\db\\Query::all()]] ne conviennent pas  car elles requièrent le chargement de toutes les données en mémoire du client.\n Pour résoudre cet problème Yii assure la prise en charge de requêtes par lots. \nLe serveur conserve les résultats de la requête, et le client utilise un curseur  pour itérer sur le jeu de résultats un lot à la fois. \n\n\n> Attention : il existe des limitations connues et des solutions de contournement pour la mise en œuvre des requêtes par lots par MySQL.\n\nLes requêtes par lots peuvent être utilisées comme suit :\n\n```php\nuse yii\\db\\Query;\n\n$query = (new Query())\n    ->from('user')\n    ->orderBy('id');\n\nforeach ($query->batch() as $users) {\n    // $users est dans un tableau de 100 ou moins lignes de la table user. \n}\n\n// ou si vous voulez itérer les lignes une par une \nforeach ($query->each() as $user) {\n     //les données sont retrouvées du serveur en lots de 100,\n    // $user représente une ligne de données de la table user.\n}\n```\n\nLes méthodes [[yii\\db\\Query::batch()]] et [[yii\\db\\Query::each()]] retournent un objet [[yii\\db\\BatchQueryResult]] \nqui implémente l'interface `Iterator` et qui, par conséquent, peut être utilisé dans une construction `foreach`.\nDurant la première itération, une requête SQL est faite à la base de données. Les données sont retrouvées en lots dans les itérations suivantes. \nPar défaut, la taille du lot est 100, ce qui signifie que 100 lignes sont retrouvées dans chacun des lots. \nVous pouvez changer la taille du lot en passant le premier paramètre des méthodes `batch()` ou `each()`.\n\nComparée à la requête [[yii\\db\\Query::all()]], la requête par lots ne charge que 100 lignes de données à la fois en mémoire. Si vous traitez les données et les détruisez tout de suite, la requête par lots réduit l'utilisation de la mémoire. \n\nSi vous spécifiez l'indexation du résultat de la requête par une colonne via [[yii\\db\\Query::indexBy()]], \nla requête par lots conserve l'index approprié. Par exemple :\n\nPar exemple :\n\n```php\n$query = (new \\yii\\db\\Query())\n    ->from('user')\n    ->indexBy('username');\n\nforeach ($query->batch() as $users) {\n    // $users est indexé par la colonne \"username\"\n}\n\nforeach ($query->each() as $username => $user) {\n    // ...\n}\n```\n\n#### Limitations des requêtes par lots dans MySQL <span id=\"batch-query-mysql\"></span>\n\nLa mise en œuvre des requêtes par lots de MySQL s'appuie sur la bibliothèque du pilote PDO. Par défaut, les requêtes MySQL sont \n[`mises en tampon`](https://www.php.net/manual/fr/mysqlinfo.concepts.buffering.php). \nCela empêche d'utiliser le curseur pour obtenir les données, parce que cela n'empêche pas le jeu résultant complet d'être chargé dans la mémoire du client par le pilote.\n\n\n> Note: lorsque `libmysqlclient` est utilisé (typique de PHP5), la limite mémoire de  PHP ne compte pas la mémoire utilisée par les jeux de résultats. \nIl peut sembler que les requêtes par lot fonctionnent correctement, mais en réalité l'intégralité du jeu de données est chargé dans la mémoire du client.\n \n\nPour désactiver la mise en tampon et réduire les exigences en mémoire client, la propriété connexion à PDOT\n`PDO::MYSQL_ATTR_USE_BUFFERED_QUERY` doit être définie à `false`. Cependant, jusqu'à ce que l'intégralité du jeu de données ait été retrouvé, aucune autre requête ne peut être faite via la même connexion. Cela peut empêcher  `ActiveRecord` \nd'effectuer une requête pour obtenir le schéma de table lorsqu'il le doit. \nSi cela n'est pas un problème (le schéma de table est déjà mis en cache), il est possible de commuter la connexion originale en mode sans mise en tampon, \net de revenir en arrière lorsque la requête par lots est terminée.\n\n\n```php\nYii::$app->db->pdo->setAttribute(\\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);\n\n// Effectue la requête par lots\n\nYii::$app->db->pdo->setAttribute(\\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);\n```\n\n> Note: dans le cas de  MyISAM, pour toute la durée de la requête par lots, la table peut devenir verrouillée, retardant ainsi ou refusant l'accès en écriture pour une autre connexion. Lors de l'utilisation de requêtes sans mise en tampon, essayez de conserver le curseur ouvert pour un temps aussi court que possible.  \n\nSi le schéma n'est pas mis en cache, ou s'il est nécessaire d'effectuer d'autres requêtes alors que la requête par lots est en cours de traitement, vous pouvez créer une connexion à la base de données séparée sans mise en tampon : \n\n```php\n$unbufferedDb = new \\yii\\db\\Connection([\n    'dsn' => Yii::$app->db->dsn,\n    'username' => Yii::$app->db->username,\n    'password' => Yii::$app->db->password,\n    'charset' => Yii::$app->db->charset,\n]);\n$unbufferedDb->open();\n$unbufferedDb->pdo->setAttribute(\\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);\n```\n\nSi vous voulez garantir que la `$unbufferedDb` a exactement les mêmes attributs PDO gue la `$db` originale avec mise en tampon mais que `PDO::MYSQL_ATTR_USE_BUFFERED_QUERY` est `false`, \n[envisagez une copie profonde de `$db`](https://github.com/yiisoft/yii2/issues/8420#issuecomment-301423833), \ndéfinissez le à `false` manuellement.\n\nEnsuite, les requêtes sont créées normalement. La nouvelle connexion est utilisée pour exécuter les requêtes par lots et retrouver des résultats soit par lots, soit un par un :\n\n```php\n// obtention des données par lots de 1000\nforeach ($query->batch(1000, $unbufferedDb) as $users) {\n    // ...\n}\n\n\n// les données sont retrouvées dans le serveur par lots de 1000, mais elles sont itérées une à une \nforeach ($query->each(1000, $unbufferedDb) as $user) {\n    // ...\n}\n```\n\nLorsque la connexion n'est plus nécessaire et que le jeu de résultats a été retrouvé, on peut le fermer :\n\n```php\n$unbufferedDb->close();\n```\n\n> Note: une requête sans mise en tampon utilise moinsde mémoire du côté PHP, mais peut augmenter la charge du serveur MySQL. \n> Il est recommandé de concevoir votre propre code avec votre pratique en production pour des données massives supplémentaires,\n> [par exemple, divisez la plage pour les clés entières, itérez sur elles avec des requêtes sans mise en tampon](https://github.com/yiisoft/yii2/issues/8420#issuecomment-296109257).\n\n\n\n\n\n\n### Ajout de conditions et expressions personnalisées <span id=\"adding-custom-conditions-and-expressions\"></span>\n\nComme cela a été mentionné au chapitre [Conditions – Format Object](#object-format), il est possible de créer des classe de condition personnalisées.\nPour l'exemple, créons une condition qui vérifie que des colonnes spécifiques sont inférieures à une valeur donnée.\nEn utilisant le format opérateur, ça devrait ressembler à ce qui suit : \n\n```php\n[\n    'and',\n    '>', 'posts', $minLimit,\n    '>', 'comments', $minLimit,\n    '>', 'reactions', $minLimit,\n    '>', 'subscriptions', $minLimit\n]\n```\n\nLorsqu'une telle condition est utilisée une seule fois, tout va bien. Dans le cas où elle est utilisée de multiples fois dans une unique requête, cela peut être grandement optimisé. \nCréons un objet condition personnalisé pour le démontrer.\n\nYii dispose d'une classe [[yii\\db\\conditions\\ConditionInterface|ConditionInterface]], qui peut être utilisée pour marquer des classes qui représentent une condition. \nElle nécessite la mise en œuvre de la méthode `fromArrayDefinition()`, afin de rendre possible la création d'une condition à partir du format tableau. \nDans le cas où vous n'en n'avez pas besoin, vous pouvez mettre cette méthode en œuvre avec lancement d'une exception. \n\nComme nous créons notre classe de condition personnalisée, nous pouvons construire une API qui s'adapte au mieux à notre tâche.\n\n```php\nnamespace app\\db\\conditions;\n\nclass AllGreaterCondition implements \\yii\\db\\conditions\\ConditionInterface\n{\n    private $columns;\n    private $value;\n\n    /**\n     * @param string[] $columns tableau de colonnes qui doivent être plus grande que $value\n     * @param mixed $value la valeur à laquelle comparer chaque $column\n     */\n    public function __construct(array $columns, $value)\n    {\n        $this->columns = $columns;\n        $this->value = $value;\n    }\n    \n    public static function fromArrayDefinition($operator, $operands)\n    {\n        throw new InvalidArgumentException('Not implemented yet, but we will do it later');\n    }\n    \n    public function getColumns() { return $this->columns; }\n    public function getValue() { return $this->vaule; }\n}\n```\n\nAinsi nous pouvons créer un objet condition :\n\n```php\n$conditon = new AllGreaterCondition(['col1', 'col2'], 42);\n```\n\nMais `QueryBuilder` (le constructeur de requêtes) ne sait toujours pas comment élaborer une condition SQL à partir de cet objet. \nMaintenant nous devons créer un constructeur pour cette condition. Il doit mettre en œuvre une méthode `build()`. \n\n```php\nnamespace app\\db\\conditions;\n\nclass AllGreaterConditionBuilder implements \\yii\\db\\ExpressionBuilderInterface\n{\n    use \\yii\\db\\Condition\\ExpressionBuilderTrait; // Contient le constructeur et la propriété `queryBuilder`.\n\n    /**\n     * @param AllGreaterCondition $condition la condition à élaborer\n     * @param array $params les paramètres de liaison.\n     */ \n    public function build(ConditionInterface $condition, &$params)\n    {\n        $value = $condition->getValue();\n        \n        $conditions = [];\n        foreach ($condition->getColumns() as $column) {\n            $conditions[] = new SimpleCondition($column, '>', $value);\n        }\n\n        return $this->queryBuider->buildCondition(new AndCondition($conditions), $params);\n    }\n}\n```\n\nEnsuite, laissons simplement  [[yii\\db\\QueryBuilder|QueryBuilder]] prendre connaissance de notre nouvelle condition — établissons une correspondance entre lui et notre tableau  `expressionBuilders`. Cela peut se faire directement à partir de la configuration de l'application :\n\n```php\n'db' => [\n    'class' => 'yii\\db\\mysql\\Connection',\n    // ...\n    'queryBuilder' => [\n        'expressionBuilders' => [\n            'app\\db\\conditions\\AllGreaterCondition' => 'app\\db\\conditions\\AllGreaterConditionBuilder',\n        ],\n    ],\n],\n```\n\nMaintenant nous sommes en mesure d'utiliser notre condition dans `where()`:\n\n```php\n$query->andWhere(new AllGreaterCondition(['posts', 'comments', 'reactions', 'subscriptions'], $minValue));\n```\n\nSi nous voulons rendre possible la création  de notre condition personnalisée en utilisant le format opérateur, nous devons le déclarer dans \n[[yii\\db\\QueryBuilder::conditionClasses|QueryBuilder::conditionClasses]]:\n\n```php\n'db' => [\n    'class' => 'yii\\db\\mysql\\Connection',\n    // ...\n    'queryBuilder' => [\n        'expressionBuilders' => [\n            'app\\db\\conditions\\AllGreaterCondition' => 'app\\db\\conditions\\AllGreaterConditionBuilder',\n        ],\n        'conditionClasses' => [\n            'ALL>' => 'app\\db\\conditions\\AllGreaterCondition',\n        ],\n    ],\n],\n```\n\nEt créer une mise en œuvre réelle de la méthode  `AllGreaterCondition::fromArrayDefinition()`  \ndans `app\\db\\conditions\\AllGreaterCondition`:\n\n```php\nnamespace app\\db\\conditions;\n\nclass AllGreaterCondition implements \\yii\\db\\conditions\\ConditionInterface\n{\n    // ... see the implementation above\n     \n    public static function fromArrayDefinition($operator, $operands)\n    {\n        return new static($operands[0], $operands[1]);\n    }\n}\n```\n    \nÀ la suite de cela, nous pouvons créer notre condition personnalisée en utilisant un format opérateur plus court :\n\n```php\n$query->andWhere(['ALL>', ['posts', 'comments', 'reactions', 'subscriptions'], $minValue]);\n```\n\nVous pouvez noter que deux conceptes ont été utilisés : Expressions et Conditions. Il y a une  [[yii\\db\\ExpressionInterface]] qui doit être utilisée pour marquer les objets qui requièrent une classe constructrice d'expression qui met en œuvre \n[[yii\\db\\ExpressionBuilderInterface]] pour être construite. Il existe également une  [[yii\\db\\condition\\ConditionInterface]], qui étend \n[[yii\\db\\ExpressionInterface|ExpressionInterface]] et doit être utilisée pour des objets qui peuvent être créés à partir d'un tableau de définition comme cela a été expliqué plus haut, mais qui peuvent aussi bien nécessiter le constructeur.\n\nPour résumer:\n\n- Expression – est un objet de transfert de donnèes — Data Transfer Object (DTO) — pour un jeu de données, qui peut être compilé en une instruction SQL  (un opérateur, une chaîne de caractères, un tableau, JSON, etc).\n- Condition – est un super jeu d'expressions, qui agrège de multiples expressions (ou valeurs scalaires) qui peut être compilé en une unique condition SQL.\n\nVous pouvez créer votre propre classe qui met en œuvre l'interface  [[yii\\db\\ExpressionInterface|ExpressionInterface]] pour cacher la complexité de la transformation de données en instructions SQL. Vous en apprendrez plus sur d'autres exemples d'expressions dans le \n[prochain article](db-active-record.md);\n"
  },
  {
    "path": "docs/guide-fr/helper-array.md",
    "content": "Classe assistante ArrayHelper\n=============================\n\nEn plus du jeu riche de [fonctions de tableaux](https://www.php.net/manual/fr/book.array.php) qu'offre PHP, la classe assistante traitant les tableaux dans Yii fournit des méthodes statiques supplémentaires qui vous permettent de traiter les tableaux avec plus d'efficacité.\n\n\n## Obtention de valeurs <span id=\"getting-values\"></span>\n\nRécupérer des valeurs d'un tableau ou d'un objet ou une structure complexe écrits tous deux en PHP standard est un processus assez répétitif. Vous devez d'abord vérifier que la clé existe avec `isset`, puis si c'est le cas, vous récupérez la valeur associée, sinon il vous faut fournir une valeur par défaut : \n\n```php\nclass User\n{\n    public $name = 'Alex';\n}\n\n$array = [\n    'foo' => [\n        'bar' => new User(),\n    ]\n];\n\n$value = isset($array['foo']['bar']->name) ? $array['foo']['bar']->name : null;\n```\n\nYii fournit une méthode très pratique pour faire cela :\n\n```php\n$value = ArrayHelper::getValue($array, 'foo.bar.name');\n```\n\nLe premier argument de la méthode indique de quelle source nous voulons récupérer une valeur. Le deuxième spécifie comment récupérer la donnée. Il peut s'agir d'un des éléments suivants :\n\n- Nom d'une clé de tableau ou de la propriété d'un objet de laquelle récupérer une valeur.\n- Un jeu de noms de clé de tableau ou de propriétés d'objet séparées par des points, comme dans l'exemple que nous venons de présenter ci-dessus.\n- Une fonction de rappel qui retourne une valeur.\n\nLe fonction de rappel doit être la suivante :\n\n```php\n$fullName = ArrayHelper::getValue($user, function ($user, $defaultValue) {\n    return $user->firstName . ' ' . $user->lastName;\n});\n```\n\nLe troisième argument facultatif est la valeur par défaut qui est `null` si on ne la spécifie pas. Il peut être utilisé comme ceci :\n\n```php\n$username = ArrayHelper::getValue($comment, 'user.username', 'Unknown');\n```\n\nDans le cas où vous voulez récupérer la valeur tout en la retirant immédiatement du tableau, vous pouvez utiliser la méthode `remove` :\n\n```php\n$array = ['type' => 'A', 'options' => [1, 2]];\n$type = ArrayHelper::remove($array, 'type');\n```\n\nAprès exécution du code, `$array` contiendra `['options' => [1, 2]]` et `$type` sera `A`. Notez que contrairement à la méthode `getValue`, `remove` accepte seulement les noms de clé.\n\n\n## Tester l'existence des clés <span id=\"checking-existence-of-keys\"></span>\n\n`ArrayHelper::keyExists` fonctionne comme [array_key_exists](https://www.php.net/manual/fr/function.array-key-exists.php) sauf qu'elle prend également en charge la comparaison de clés insensible à la casse. Par exemple,\n\n```php\n$data1 = [\n    'userName' => 'Alex',\n];\n\n$data2 = [\n    'username' => 'Carsten',\n];\n\nif (!ArrayHelper::keyExists('username', $data1, false) || !ArrayHelper::keyExists('username', $data2, false)) {\n    echo \"Veuillez fournir un nom d'utilisateur (username).\";\n}\n```\n\n## Récupération de colonnes <span id=\"retrieving-columns\"></span>\n\nIl arrive souvent que vous ayez à récupérer une colonne de valeurs d'un tableau de lignes de données ou d'objets. Un exemple courant est l'obtention d'une liste d'identifiants. \n\n```php\n$array = [\n    ['id' => '123', 'data' => 'abc'],\n    ['id' => '345', 'data' => 'def'],\n];\n$ids = ArrayHelper::getColumn($array, 'id');\n```\n\nLe résultat sera `['123', '345']`.\n\nSi des transformations supplémentaires sont nécessaires ou si la manière de récupérer les valeurs est complexe, le second argument peut être formulé sous forme de fonction anonyme :\n\n```php\n$result = ArrayHelper::getColumn($array, function ($element) {\n    return $element['id'];\n});\n```\n\n\n## Réindexation de tableaux <span id=\"reindexing-arrays\"></span>\n\nLa méthode `index` peut être utilisées pour indexer un tableau selon une clé spécifiée. L'entrée doit être soit un tableau multidimensionnel, soit un tableau d'objets. `$key` peut être un nom de clé du sous-tableau, un nom de propriété d'objet ou une fonction anonyme qui doit retourner la valeur à utiliser comme clé. \n\nL'attribut `$groups` est un tableau de clés qui est utilisé pour regrouper le tableau d'entrée en un ou plusieurs sous-tableaux basés sur les clés spécifiées. \n\nSi l'argument `$key` ou sa valeur pour l'élément particulier est `null` alors que `$groups` n'est pas défini, l'élément du tableau est écarté. Autrement, si `$groups` est spécifié, l'élément du tableau est ajouté au tableau résultant sans aucune clé.\n\nPar exemple :\n\n```php\n$array = [\n    ['id' => '123', 'data' => 'abc', 'device' => 'laptop'],\n    ['id' => '345', 'data' => 'def', 'device' => 'tablet'],\n    ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone'],\n];\n$result = ArrayHelper::index($array, 'id');\n```\n\nLe résultat est un tableau associatif, dans lequel la clé est la valeur de l'attribut `id` : \n\n```php\n[\n    '123' => ['id' => '123', 'data' => 'abc', 'device' => 'laptop'],\n    '345' => ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone']\n    // Le second élément du tableau d'origine est écrasé par le dernier élément parce que les identifiants sont identiques. \n]\n```\n\nUne fonction anonyme passée en tant que `$key`, conduit au même résultat :\n\n```php\n$result = ArrayHelper::index($array, function ($element) {\n    return $element['id'];\n});\n```\n\nPasser `id` comme troisième argument regroupe `$array` par `id`:\n\n```php\n$result = ArrayHelper::index($array, null, 'id');\n```\n\nLe résultat est un tableau multidimensionnel regroupé par `id` au premier niveau et non indexé au deuxième niveau : \n\n```php\n[\n    '123' => [\n        ['id' => '123', 'data' => 'abc', 'device' => 'laptop']\n    ],\n    '345' => [ // all elements with this index are present in the result array\n        ['id' => '345', 'data' => 'def', 'device' => 'tablet'],\n        ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone'],\n    ]\n]\n```\n\nUne fonction anonyme peut également être utilisée dans le tableau de regroupement : \n\n```php\n$result = ArrayHelper::index($array, 'data', [function ($element) {\n    return $element['id'];\n}, 'device']);\n```\n\nLe résultat est un tableau multidimensionnel regroupé par `id` au premier niveau, par `device` au deuxième niveau et par `data` au troisième niveau :\n\n```php\n[\n    '123' => [\n        'laptop' => [\n            'abc' => ['id' => '123', 'data' => 'abc', 'device' => 'laptop']\n        ]\n    ],\n    '345' => [\n        'tablet' => [\n            'def' => ['id' => '345', 'data' => 'def', 'device' => 'tablet']\n        ],\n        'smartphone' => [\n            'hgi' => ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone']\n        ]\n    ]\n]\n```\n\n## Construction de tableaux de mise en correspondance <span id=\"building-maps\"></span>\n\nAfin de construire un tableau de mise en correspondance (paires clé-valeur) sur la base d'un tableau multidimensionnel ou d'un tableau d'objets, vous pouvez utiliser la méthode `map`. \nLes paramètres `$from` et `$to` spécifient les noms de clé ou les noms des propriétés pour construire le tableau de mise en correspondance. Le paramètre facultatif `$group` est un nom de clé ou de propriété qui permet de regrouper les éléments du tableau au premier niveau. Par exemple :\n\n```php\n$array = [\n    ['id' => '123', 'name' => 'aaa', 'class' => 'x'],\n    ['id' => '124', 'name' => 'bbb', 'class' => 'x'],\n    ['id' => '345', 'name' => 'ccc', 'class' => 'y'],\n];\n\n$result = ArrayHelper::map($array, 'id', 'name');\n// le résultat est :\n// [\n//     '123' => 'aaa',\n//     '124' => 'bbb',\n//     '345' => 'ccc',\n// ]\n\n$result = ArrayHelper::map($array, 'id', 'name', 'class');\n// le résultat est :\n// [\n//     'x' => [\n//         '123' => 'aaa',\n//         '124' => 'bbb',\n//     ],\n//     'y' => [\n//         '345' => 'ccc',\n//     ],\n// ]\n```\n\n\n## Tri multidimensionnel <span id=\"multidimensional-sorting\"></span>\n\nLa méthode `multisort` facilite le tri d'un tableau d'objets ou de tableaux imbriqués selon une ou plusieurs clés. Par exemple :\n\n```php\n$data = [\n    ['age' => 30, 'name' => 'Alexander'],\n    ['age' => 30, 'name' => 'Brian'],\n    ['age' => 19, 'name' => 'Barney'],\n];\nArrayHelper::multisort($data, ['age', 'name'], [SORT_ASC, SORT_DESC]);\n```\n\nAprès le tri, `data` contient ce qui suit :\n\n```php\n[\n    ['age' => 19, 'name' => 'Barney'],\n    ['age' => 30, 'name' => 'Brian'],\n    ['age' => 30, 'name' => 'Alexander'],\n];\n```\n\nLe deuxième argument, qui spécifie les clés de tri peut être une chaîne de caractères si la clé est unique, un tableau dans le cas de clés multiples, ou une fonction anonyme telle que celle qui suit :\n\n```php\nArrayHelper::multisort($data, function($item) {\n    return isset($item['age']) ? ['age', 'name'] : 'name';\n});\n```\n\nLe troisième argument précise la direction. Dans le cas d'un tri selon une clé unique, il s'agit soit de `SORT_ASC`, soit de `SORT_DESC`. Si le tri se fait selon des valeurs multiples, vous pouvez préciser des directions de tri différentes pour chacune des clés en présentant ces directions sous forme de tableau.\n\nLe dernier argument est une option de tri de PHP qui peut prendre les mêmes valeurs que celles acceptées par la fonction [sort()](https://www.php.net/manual/fr/function.sort.php) de PHP.\n\n\n## Détection des types de tableau <span id=\"detecting-array-types\"></span>\n\nIl est pratique de savoir si un tableau est indexé ou associatif. Voici un exemple :\n\n```php\n// aucune clé spécifiée\n$indexed = ['Qiang', 'Paul'];\necho ArrayHelper::isIndexed($indexed);\n\n// toutes les clés sont des chaînes de caractères\n$associative = ['framework' => 'Yii', 'version' => '2.0'];\necho ArrayHelper::isAssociative($associative);\n```\n\n\n## Encodage et décodage de valeurs HTML <span id=\"html-encoding-values\"></span>\n\nAfin d'encoder ou décoder des caractères spéciaux dans un tableau de chaînes de caractères en/depuis des entités HTML, vous pouvez utiliser les fonctions suivantes :\n\n```php\n$encoded = ArrayHelper::htmlEncode($data);\n$decoded = ArrayHelper::htmlDecode($data);\n```\n\nSeules les valeurs sont encodées par défaut. En passant un deuxième argument comme `false` vous pouvez également encoder les clés d'un tableau. L'encodage utilise le jeu de caractères de l'application et on peut le changer via un troisième argument.\n\n\n## Fusion de tableaux <span id=\"merging-arrays\"></span>\n\nLa fonction [[yii\\helpers\\ArrayHelper::merge()|ArrayHelper::merge()]] vous permet de fusionner deux, ou plus, tableaux en un seul de manière récursive. Si chacun des tableaux possède un élément avec la même chaîne clé valeur, le dernier écrase le premier (ce qui est un fonctionnement différent de [array_merge_recursive()](https://www.php.net/manual/fr/function.array-merge-recursive.php)).\nLa fusion récursive est entreprise si les deux tableaux possèdent un élément de type tableau avec la même clé. Pour des éléments dont la clé est un entier, les éléments du deuxième tableau sont ajoutés aux éléments du premier tableau. Vous pouvez utiliser l'objet [[yii\\helpers\\UnsetArrayValue]] pour supprimer la valeur du premier tableau ou [[yii\\helpers\\ReplaceArrayValue]] pour forcer le remplacement de la première valeur au lieu de la fusion récursive. \n\nPar exemple :\n\n```php\n$array1 = [\n    'name' => 'Yii',\n    'version' => '1.1',\n    'ids' => [\n        1,\n    ],\n    'validDomains' => [\n        'example.com',\n        'www.example.com',\n    ],\n    'emails' => [\n        'admin' => 'admin@example.com',\n        'dev' => 'dev@example.com',\n    ],\n];\n\n$array2 = [\n    'version' => '2.0',\n    'ids' => [\n        2,\n    ],\n    'validDomains' => new \\yii\\helpers\\ReplaceArrayValue([\n        'yiiframework.com',\n        'www.yiiframework.com',\n    ]),\n    'emails' => [\n        'dev' => new \\yii\\helpers\\UnsetArrayValue(),\n    ],\n];\n\n$result = ArrayHelper::merge($array1, $array2);\n```\n\nLe résultat est :\n\n```php\n[\n    'name' => 'Yii',\n    'version' => '2.0',\n    'ids' => [\n        1,\n        2,\n    ],\n    'validDomains' => [\n        'yiiframework.com',\n        'www.yiiframework.com',\n    ],\n    'emails' => [\n        'admin' => 'admin@example.com',\n    ],\n]\n```\n\n\n## Conversion d'objets en tableaux <span id=\"converting-objects-to-arrays\"></span>\n\nIl arrive souvent que vous ayez besoin de convertir un objet, ou un tableau d'objets, en tableau. Le cas le plus courant est la conversion de modèles d'enregistrements actifs afin de servir des tableaux de données via une API REST ou pour un autre usage. Le code suivant peut alors être utilisé :\n\n```php\n$posts = Post::find()->limit(10)->all();\n$data = ArrayHelper::toArray($posts, [\n    'app\\models\\Post' => [\n        'id',\n        'title',\n        // the key name in array result => property name\n        'createTime' => 'created_at',\n        // the key name in array result => anonymous function\n        'length' => function ($post) {\n            return strlen($post->content);\n        },\n    ],\n]);\n```\n\nLe premier argument contient les données à convertir. Dans notre cas, nous convertissons un modèle d'enregistrements actifs `Post`. \n\nThe second argument est un tableau de mise en correspondance de conversions par classe. Nous définissons une mise en correspondance pour le modèle `Post`. Chaque tableau de mise en correspondance contient un jeu de mise en correspondance. Chaque mise en correspondance peut être :\n\n- Un nom de champ à inclure tel quel.\n- Une paire clé-valeur dans laquelle la clé est donnée sous forme de chaîne de caractères et la valeur sous forme du nom de la colonne dont on doit prendre la valeur. \n- Une paire clé-valeur dans laquelle la clé est donnée sous forme de chaîne de caractères et la valeur sous forme de fonction de rappel qui la retourne. \n\nLe résultat de la conversion ci-dessus pour un modèle unique est :\n\n\n```php\n[\n    'id' => 123,\n    'title' => 'test',\n    'createTime' => '2013-01-01 12:00AM',\n    'length' => 301,\n]\n```\n\nIl est possible de fournir une manière par défaut de convertir un objet en tableau pour une classe spécifique en implémentant l'interface [[yii\\base\\Arrayable|Arrayable]] dans cette classe.\n\n## Test de l'appartenance à un tableau <span id=\"testing-arrays\"></span>\n\nSouvent, vous devez savoir si un élément se trouve dans un tableau ou si un jeu d'éléments est un sous-ensemble d'un autre. Bien que PHP offre la fonction `in_array()`, cette dernière ne prend pas en charge les sous-ensembles ou les objets `\\Traversable`.\n\nPour faciliter ce genre de tests, [[yii\\helpers\\ArrayHelper]] fournit les méthodes [[yii\\helpers\\ArrayHelper::isIn()|isIn()]]\net [[yii\\helpers\\ArrayHelper::isSubset()|isSubset()]] avec la même signature que [in_array()](https://www.php.net/manual/fr/function.in-array.php).\n\n```php\n// true\nArrayHelper::isIn('a', ['a']);\n// true\nArrayHelper::isIn('a', new ArrayObject(['a']));\n\n// true \nArrayHelper::isSubset(new ArrayObject(['a', 'c']), new ArrayObject(['a', 'b', 'c']));\n```\n"
  },
  {
    "path": "docs/guide-fr/helper-html.md",
    "content": "Classe assistante Html\n======================\n\nToutes les applications Web génèrent un grand nombre de balises HTML. Si le code HTML est statique, il peut être créé efficacement sous forme de [mélange de code PHP et de code HTML dans un seul fichier](https://www.php.net/manual/fr/language.basic-syntax.phpmode.php), mais lorsqu'il est généré dynamiquement, cela commence à être compliqué à gérer sans une aide supplémentaire. Yii fournit une telle aide sous la forme de la classe assistante Html, qui offre un jeu de méthodes statiques pour manipuler les balises Html les plus courantes, leurs options et leur contenu.\n\n> Note: si votre code HTML est presque statique, il vaut mieux utiliser HTML directement. Il n'est pas nécessaire d'envelopper tout dans des appels aux méthodes de la classe assistante Html.\n\n\n## Les bases <span id=\"basics\"></span>\n\nComme la construction de code HTML dynamique en concaténant des chaînes de caractère peut très vite tourner à la confusion, Yii fournit un jeu de méthodes pour manipuler les options de balises et construire des balises s'appuyant sur ces options. \n\n\n### Génération de balises <span id=\"generating-tags\"></span>\n\nLe code pour générer une balise ressemble à ceci :\n\n```php\n<?= Html::tag('p', Html::encode($user->name), ['class' => 'username']) ?>\n```\n\nLe premier argument est le nom de la balise. Le deuxième est le contenu qui apparaît entre l'ouverture de la balise et sa fermeture. \nNotez que nous utilisons `Html::encode` – c'est parce que le contenu n'est pas encodé automatiquement pour permetre l'utilisation de HTML quand c'est nécessaire.\nLe troisième est un tableau d'options HTML ou, en d'autres mots, les attributs de la balise.\nDans ce tableau, la clé est le nom de l'attribut (comme `class`, `href` ou `target`) et la valeur est sa valeur.\n\nLe code ci-dessus génère le code HTML suivant : \n\n```html\n<p class=\"username\">samdark</p>\n```\n\nDans le cas où vous avez simplement besoin d'ouvrir ou de fermer la balise, vous pouvez utiliser les méthodes `Html::beginTag()` et `Html::endTag()`.\n\nDes options sont utilisées dans de nombreuses méthodes de la classe assistante Html et de nombreux composants graphiques (widgets). Dans tous ces cas, il y a quelques manipulations supplémentaires à connaître :\n\n- Si une valeur est `null`, l'attribut correspondant n'est pas rendu.\n- Les attributs du type booléen sont traités comme des \n  [attributs booléens ](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#boolean-attributes).\n- Les valeurs des attributs sont encodés HTML à l'aide de la méthode [[yii\\helpers\\Html::encode()|Html::encode()]].\n- Si la valeur d'un attribut est un tableau, il est géré comme suit :\n \n   * Si l'attribut est un attribut de donnée tel que listé dans [[yii\\helpers\\Html::$dataAttributes]], tel que `data` ou `ng`,\n     une liste d'attributs est rendue, un pour chacun des élément dans le tableau de valeurs. Par exemple, \n     `'data' => ['id' => 1, 'name' => 'yii']` génère `data-id=\"1\" data-name=\"yii\"`; et \n     `'data' => ['params' => ['id' => 1, 'name' => 'yii'], 'status' => 'ok']` génère\n     `data-params='{\"id\":1,\"name\":\"yii\"}' data-status=\"ok\"`. Notez que dans le dernier exemple le format JSON est utilisé pour rendre le sous-tableau.\n   * Si l'attribut n'est PAS un attribut de donnée, la valeur est encodée JSON. Par exemple,\n     `['params' => ['id' => 1, 'name' => 'yii']` génère `params='{\"id\":1,\"name\":\"yii\"}'`.\n\n\n### Formation des classes et des styles CSS <span id=\"forming-css\"></span>\n\nLors de la construction des options pour des balises HTML, nous démarrons souvent avec des valeurs par défaut qu'il faut modifier. Afin d'ajouter ou de retirer une classe, vous pouvez utiliser ce qui suit : \n\n```php\n$options = ['class' => 'btn btn-default'];\n\nif ($type === 'success') {\n    Html::removeCssClass($options, 'btn-default');\n    Html::addCssClass($options, 'btn-success');\n}\n\necho Html::tag('div', 'Pwede na', $options);\n\n// si la valeur de $type est 'success' le rendu sera\n// <div class=\"btn btn-success\">Pwede na</div>\n```\n\nVous pouvez spécifier de multiples classe CSS en utilisant le tableau de styles également :\n\n```php\n$options = ['class' => ['btn', 'btn-default']];\n\necho Html::tag('div', 'Save', $options);\n// rend '<div class=\"btn btn-default\">Save</div>'\n```\n\nVous pouvez aussi utiliser le tableau de styles pour ajouter ou retirer des classes :\n\n```php\n$options = ['class' => 'btn'];\n\nif ($type === 'success') {\n    Html::addCssClass($options, ['btn-success', 'btn-lg']);\n}\n\necho Html::tag('div', 'Save', $options);\n// rend '<div class=\"btn btn-success btn-lg\">Save</div>'\n```\n\n`Html::addCssClass()` empêche la duplication, vous n'avez donc pas à vous préoccuper de savoir si une classe apparaît deux fois :\n\n```php\n$options = ['class' => 'btn btn-default'];\n\nHtml::addCssClass($options, 'btn-default'); // class 'btn-default' is already present\n\necho Html::tag('div', 'Save', $options);\n// rend '<div class=\"btn btn-default\">Save</div>'\n```\n\nSi l'option classe CSS est spécifiée en utilisant le tableau de styles, vous pouvez utiliser une clé nommée pour indiquer le but logique de la classe. Dans ce cas, une classe utilisant la même clé dans le tableau de styles passé à `Html::addClass()` est ignorée :\n\n```php\n$options = [\n    'class' => [\n        'btn',\n        'theme' => 'btn-default',\n    ]\n];\n\nHtml::addCssClass($options, ['theme' => 'btn-success']); // la clé 'theme' est déjà utilisée\n\necho Html::tag('div', 'Save', $options);\n// rend '<div class=\"btn btn-default\">Save</div>'\n```\n\nLes styles CSS peuvent être définis d'une façon similaire en utilisant l'attribut `style` :\n\n```php\n$options = ['style' => ['width' => '100px', 'height' => '100px']];\n\n// donne style=\"width: 100px; height: 200px; position: absolute;\"\nHtml::addCssStyle($options, 'height: 200px; position: absolute;');\n\n// gives style=\"position: absolute;\"\nHtml::removeCssStyle($options, ['width', 'height']);\n```\n\nLors de l'utilisation de  [[yii\\helpers\\Html::addCssStyle()|addCssStyle()]], vous pouvez spécifier soit un tableau de paires clé-valeur qui correspond aux propriétés CSS noms et valeurs, soit une chaîne de caractères telle que `width: 100px; height: 200px;`. Ces formats peuvent être convertis de l'un en l'autre en utilisant les méthodes [[yii\\helpers\\Html::cssStyleFromArray()|cssStyleFromArray()]] et\n[[yii\\helpers\\Html::cssStyleToArray()|cssStyleToArray()]]. La méthode [[yii\\helpers\\Html::removeCssStyle()|removeCssStyle()]]\naccepte un tableau de propriétés à retirer. S'il s'agit d'une propriété unique, elle peut être spécifiée sous forme de chaîne de caractères. \n\n### Encodage et décodage du contenu <span id=\"encoding-and-decoding-content\"></span>\n\nPour que le contenu puisse être affiché en HTML de manière propre et en toute sécurité, les caractères spéciaux du contenu doivent être encodés. En PHP, cela s'obtient avec [htmlspecialchars](https://www.php.net/manual/fr/function.htmlspecialchars.php) et \n[htmlspecialchars_decode](https://www.php.net/manual/fr/function.htmlspecialchars-decode.php). Le problème rencontré en utilisant ces méthodes directement est que vous devez spécifier l'encodage et des options supplémentaires tout le temps. Comme ces options restent toujours les mêmes et que l'encodage doit correspondre à celui de l'application pour éviter les problèmes de sécurité, Yii fournit deux méthodes compactes et faciles à utiliser :\n\n```php\n$userName = Html::encode($user->name);\necho $userName;\n\n$decodedUserName = Html::decode($userName);\n```\n\n\n## Formulaires <span id=\"forms\"></span>\n\nManipuler des formulaires dans le code HTML est tout à fait répétitif et sujet à erreurs. À cause de cela, il existe un groupe de méthodes pour aider à les manipuler.\n\n> Note : envisagez d'utiliser [[yii\\widgets\\ActiveForm|ActiveForm]] dans le cas où vous avez affaire à des modèles et que ces derniers doivent être validés. \n\n### Création de formulaires <span id=\"creating-forms\"></span>\n\nLes formulaires peut être ouverts avec la méthode [[yii\\helpers\\Html::beginForm()|beginForm()]] comme ceci :\n\n```php\n<?= Html::beginForm(['order/update', 'id' => $id], 'post', ['enctype' => 'multipart/form-data']) ?>\n```\n\nLe premier argument est l'URL à laquelle le formulaire sera soumis. Il peut être spécifié sous la forme d'une route Yii et de paramètres acceptés par [[yii\\helpers\\Url::to()|Url::to()]].\nLe deuxième est la méthode à utiliser. `post` est la méthode par défaut. Le troisième est un tableau d'options pour la balise form. Dans ce cas, nous modifions l'encodage des données du formulaire dans la requête POST en `multipart/form-data`, ce qui est requis pour envoyer des fichiers.\n\nLa fermeture du formulaire se fait simplement par :\n\n```php\n<?= Html::endForm() ?>\n```\n\n\n### Boutons <span id=\"buttons\"></span>\n\nPour générer des boutons, vous pouvez utiliser le code suivant :\n\n```php\n<?= Html::button('Pressez-mo!', ['class' => 'teaser']) ?>\n<?= Html::submitButton('Envoyer', ['class' => 'submit']) ?>\n<?= Html::resetButton('Ré-initialiser', ['class' => 'reset']) ?>\n```\n\nLe premier argument pour les trois méthodes est l'intitulé du bouton, le deuxième est un tableau d'options. \nL'intitulé n'est pas encodé, mais si vous affichez des données en provenance de l'utilisateur, encodez les avec [[yii\\helpers\\Html::encode()|Html::encode()]].\n\n\n### Champs d'entrée <span id=\"input-fields\"></span>\n\nIl y a deux groupes de méthodes d'entrée de données. Celles qui commencent par `active`, est qui sont appelées entrées actives, et celles qui ne commencent pas par ce mot. Les entrées actives prennent leurs données dans le modèle à partir des attributs spécifiés, tandis que pour les entrées régulières, les données sont spécifiées directement.\n\nLes méthodes les plus génériques sont :\n\n```php\ntype, nom de l'entrée, valeur de l'entrée, options\n<?= Html::input('text', 'username', $user->name, ['class' => $username]) ?>\n\ntype, modèle, nom de l'attribut du modèle, options\n<?= Html::activeInput('text', $user, 'name', ['class' => $username]) ?>\n```\n\nSi vous connaissez le type de l'entrée à l'avance, il est plus commode d'utiliser les méthodes raccourcis :\n\n- [[yii\\helpers\\Html::buttonInput()]]\n- [[yii\\helpers\\Html::submitInput()]]\n- [[yii\\helpers\\Html::resetInput()]]\n- [[yii\\helpers\\Html::textInput()]], [[yii\\helpers\\Html::activeTextInput()]]\n- [[yii\\helpers\\Html::hiddenInput()]], [[yii\\helpers\\Html::activeHiddenInput()]]\n- [[yii\\helpers\\Html::passwordInput()]] / [[yii\\helpers\\Html::activePasswordInput()]]\n- [[yii\\helpers\\Html::fileInput()]], [[yii\\helpers\\Html::activeFileInput()]]\n- [[yii\\helpers\\Html::textarea()]], [[yii\\helpers\\Html::activeTextarea()]]\n\nLes listes radio et les boîtes à cocher sont un peu différentes en matière de signature de méthode :\n\n```php\n<?= Html::radio('agree', true, ['label' => 'I agree']);\n<?= Html::activeRadio($model, 'agree', ['class' => 'agreement'])\n\n<?= Html::checkbox('agree', true, ['label' => 'I agree']);\n<?= Html::activeCheckbox($model, 'agree', ['class' => 'agreement'])\n```\n\nLes listes déroulantes et les boîtes listes peuvent être rendues comme suit :\n\n```php\n<?= Html::dropDownList('list', $currentUserId, ArrayHelper::map($userModels, 'id', 'name')) ?>\n<?= Html::activeDropDownList($users, 'id', ArrayHelper::map($userModels, 'id', 'name')) ?>\n\n<?= Html::listBox('list', $currentUserId, ArrayHelper::map($userModels, 'id', 'name')) ?>\n<?= Html::activeListBox($users, 'id', ArrayHelper::map($userModels, 'id', 'name')) ?>\n```\n\nLe premier argument est le nom de l'entrée, le deuxième est la valeur sélectionnée actuelle et le troisième est un tableau de paires clé-valeur, dans lequel la clé est la valeur d'entrée dans la liste et la valeur est l'étiquette qui correspond à cette valeur dans la liste.\n\nSi vous désirez que des choix multiples soient sélectionnables, vous pouvez utiliser la liste à sélection multiples (checkbox list) :\n\n```php\n<?= Html::checkboxList('roles', [16, 42], ArrayHelper::map($roleModels, 'id', 'name')) ?>\n<?= Html::activeCheckboxList($user, 'role', ArrayHelper::map($roleModels, 'id', 'name')) ?>\n```\n\nSinon utilisez la liste radio :\n\n```php\n<?= Html::radioList('roles', [16, 42], ArrayHelper::map($roleModels, 'id', 'name')) ?>\n<?= Html::activeRadioList($user, 'role', ArrayHelper::map($roleModels, 'id', 'name')) ?>\n```\n\n\n### Étiquettes et erreurs <span id=\"labels-and-errors\"></span>\n\nComme pour les entrées, il existe deux méthodes pour générer les étiquettes de formulaire. Celles pour les entrées « actives » qui prennent leurs étiquettes dans le modèle, et celles « non actives » qui sont étiquetées directement :\n\n```php\n<?= Html::label('User name', 'username', ['class' => 'label username']) ?>\n<?= Html::activeLabel($user, 'username', ['class' => 'label username']) ?>\n```\n\nPour afficher les erreurs de formulaire à partir d'un modèle ou sous forme de résumé pour un modèle, vous pouvez utiliser :\n\n```php\n<?= Html::errorSummary($posts, ['class' => 'errors']) ?>\n```\n\nPour afficher une erreur individuellement :\n\n```php\n<?= Html::error($post, 'title', ['class' => 'error']) ?>\n```\n\n\n### Nom et valeur des entrées  <span id=\"input-names-and-values\"></span>\n\nIl existe deux méthodes pour obtenir des noms, des identifiants et des valeurs pour des champs d'entrée basés sur un modèle. Elles sont essentiellement utilisées en interne, mais peuvent être pratiques quelques fois :\n\n```php\n// Post[title]\necho Html::getInputName($post, 'title');\n\n// post-title\necho Html::getInputId($post, 'title');\n\n// my first post\necho Html::getAttributeValue($post, 'title');\n\n// $post->authors[0]\necho Html::getAttributeValue($post, '[0]authors[0]');\n```\n\nDans ce qui précède, le premier argument est le modèle, tandis que le deuxième est l'expression d'attribut. Dans sa forme la plus simple, l'expression est juste un nom d'attribut, mais il peut aussi s'agir d'un nom d'attribut préfixé et-ou suffixé par des index de tableau, ce qui est essentiellement le cas pour des entrées tabulaires : \n\n- `[0]content` est utilisé dans des entrées de données tabulaires pour représenter l'attribut `content` pour le premier modèle des entrées tabulaires ;\n- `dates[0]` représente le premier élément du tableau de l'attribut `dates` ; \n- `[0]dates[0]` représente le premier élément du tableau de l'attribut `dates` pour le premier modèle des entrées tabulaires.  \n\nAfin d'obtenir le nom de l'attribut sans suffixe ou préfixe, vous pouvez utiliser ce qui suit :\n\n```php\n// dates\necho Html::getAttributeName('dates[0]');\n```\n\n\n## Styles et scripts <span id=\"styles-and-scripts\"></span>\n\nIl existe deux méthodes pour générer les balises enveloppes des styles et des scripts :\n\n```php\n<?= Html::style('.danger { color: #f00; }') ?>\n\nProduit\n\n<style>.danger { color: #f00; }</style>\n\n\n<?= Html::script('alert(\"Hello!\");', ['defer' => true]);\n\nProduit\n\n<script defer>alert(\"Hello!\");</script>\n```\n\nSi vous désirez utiliser utiliser un style externe d'un fichier CSS :\n\n```php\n<?= Html::cssFile('@web/css/ie5.css', ['condition' => 'IE 5']) ?>\n\ngénère\n\n<!--[if IE 5]>\n    <link href=\"https://example.com/css/ie5.css\" />\n<![endif]-->\n```\n\nLe premier argument est l'URL. Le deuxième est un tableau d'options. En plus des options normales, vous pouvez spécifier :\n\n- `condition` pour envelopper `<link` dans des commentaires conditionnels avec la condition spécifiée. Nous espérons que vous n'aurez jamais besoin de commentaires conditionnels ;\n- `noscript` peut être défini à `true` pour envelopper `<link` dans une balise `<noscript>` de façon à ce qu'elle soit incluse seulement si le navigateur ne prend pas en charge JavaScript ou si l'utilisateur l'a désactivé. \n\nPour lier un fichier JavaScript :\n\n```php\n<?= Html::jsFile('@web/js/main.js') ?>\n```\n\nSe passe comme avec CSS, le premier argument spécifie l'URL du fichier à inclure. Les options sont passées via le deuxième argument. Dans les options vous pouvez spécifier `condition` de la même manière que dans les options pour un fichier CSS (méthode `cssFile`). \n\n\n## Hyperliens <span id=\"hyperlinks\"></span>\n\nIl y a une méthode commode pour générer les hyperliens :\n\n```php\n<?= Html::a('Profile', ['user/view', 'id' => $id], ['class' => 'profile-link']) ?>\n```\n\nLe premier argument est le titre. Il n'est pas encodé, mais si vous utilisez des données entrées par l'utilisateur, vous devez les encoder avec `Html::encode()`. Le deuxième argument est ce qui se retrouvera dans l'attribut `href` de la balise `<a`.\n\nVoir [Url::to()](helper-url.md) pour les détails sur les valeurs acceptées. \nLe troisième argument est un tableau pour les attributs de la balise.\n\nSi vous devez générer des liens  `mailto`, vous pouvez utiliser le code suivant :\n\n```php\n<?= Html::mailto('Contact us', 'admin@example.com') ?>\n```\n\n\n## Images <span id=\"images\"></span>\n\nPour générer une balise image, utilisez le code suivant :\n\n```php\n<?= Html::img('@web/images/logo.png', ['alt' => 'My logo']) ?>\n\nqui génère\n\n<img src=\"https://example.com/images/logo.png\" alt=\"My logo\" />\n```\n\nEn plus des [alias](concept-aliases.md), le premier argument accepte les routes, les paramètres et les URL, tout comme [Url::to()](helper-url.md).\n\n\n## Listes <span id=\"lists\"></span>\n\nLes listes non ordonnées peuvent être générées comme suit :\n\n```php\n<?= Html::ul($posts, ['item' => function($item, $index) {\n    return Html::tag(\n        'li',\n        $this->render('post', ['item' => $item]),\n        ['class' => 'post']\n    );\n}]) ?>\n```\n\nPour une liste ordonnée, utilisez plutôt `Html::ol()`.\n"
  },
  {
    "path": "docs/guide-fr/helper-overview.md",
    "content": "Classes assistantes\n===================\n\n> Note: cette section est en cours de développement.\n\nYii procure de nombreuses classes qui vous aident à simplifier le code de tâches courantes, telles que la manipulation de chaînes de caractères ou de tableaux, la génération de code HTML, et ainsi de suite. Ces classes assistantes sont organisées dans l'espace de noms `yii\\helpers` et sont toutes des classes statiques (ce qui signifie qu'elles ne contiennent que des propriétés et des méthodes statiques et ne doivent jamais être instanciées).\n\nVous utilisez une classe assistante en appelant directement une de ses méthodes statiques, comme ceci :\n\n```php\nuse yii\\helpers\\Html;\n\necho Html::encode('Test > test');\n```\n\n> Note: pour prendre en charge la [personnalisation des classes assistantes](#customizing-helper-classes), Yii éclate chacune des classes assistantes du noyau en deux classes : une classe de base (p. ex. `BaseArrayHelper`) et une classe concrète (p. ex. `ArrayHelper`). Lorsque vous utilisez une classe assistante, vous devez utiliser la version concrète uniquement et ne jamais utiliser la classe de base.\n\n\nClasses assistantes du noyau\n----------------------------\n\nLes versions de Yii fournissent les classes assistantes du noyau suivantes :\n\n- [ArrayHelper](helper-array.md)\n- Console\n- FileHelper\n- FormatConverter\n- [Html](helper-html.md)\n- HtmlPurifier\n- Imagine (provided by yii2-imagine extension)\n- Inflector\n- Json\n- Markdown\n- StringHelper\n- [Url](helper-url.md)\n- VarDumper\n\n\nPersonnalisation des classes assistantes <span id=\"customizing-helper-classes\"></span>\n----------------------------------------\n\nPour personnaliser une classe assistante du noyau (p. ex. [[yii\\helpers\\ArrayHelper]]), vous devez créer une nouvelle classe qui étend la classe de base correspondant à la classe assistante (p. ex. [[yii\\helpers\\ArrayHelper]]), y compris son espace de noms. Cette classe sera ensuite configurée pour remplacer l'implémentation originale de Yii.\n\nL'exemple qui suit montre comment personnaliser la méthode [[yii\\helpers\\ArrayHelper::merge()|merge()]] de la classe [[yii\\helpers\\ArrayHelper]] :\n\n```php\n<?php\n\nnamespace yii\\helpers;\n\nclass ArrayHelper extends BaseArrayHelper\n{\n    public static function merge($a, $b)\n    {\n        // votre implémentation personnalisée\n    }\n}\n```\n\nSauvegardez votre classe dans un fichier nommé `ArrayHelper.php`. Le fichier peut se trouver dans n'importe quel dossier, par exemple, `@app/components`.\n\nEnsuite, dans le [script d'entrée](structure-entry-scripts.md) de votre application, ajoutez la ligne de code suivante, après l'inclusion du fichier `yii.php` pour dire à la [classe autoloader de Yii](concept-autoloading.md) de charger votre classe personnalisée au lieu de la classe assistance originale de Yii. \n\n```php\nYii::$classMap['yii\\helpers\\ArrayHelper'] = '@app/components/ArrayHelper.php';\n```\n\nNotez que la personnalisation d'une classe assistante n'est utile que si vous désirez changer le comportement d'une fonction existante de la classe assistante. Si vous désirez ajouter une fonction additionnelle à utiliser dans votre application, le mieux est de créer une classe assistante séparée pour cela.\n"
  },
  {
    "path": "docs/guide-fr/helper-url.md",
    "content": "Classe assistante Url\n=====================\n\nLa classe assistante Url fournit un jeu de méthodes statiques pour gérer les URL.\n\n\n## Obtenir des URL communes <span id=\"getting-common-urls\"></span>\n\nVous pouvez utiliser deux méthodes pour obtenir des URL communes : l'URL de la page d'accueil et l'URL de base de la requête courante. Pour obtenir l'URL de la page d'accueil, utilisez ce qui suit :\n\n```php\n$relativeHomeUrl = Url::home();\n$absoluteHomeUrl = Url::home(true);\n$httpsAbsoluteHomeUrl = Url::home('https');\n```\n\nSi aucun paramètre n'est passé, l'URL générée est relative. Vous pouvez passer `true` pour obtenir une URL absolue pour le schéma courant ou spécifier un schéma explicitement (`https`, `http`).\n\nPour obtenir l'URL de base de la requête courante utilisez ceci :\n \n```php\n$relativeBaseUrl = Url::base();\n$absoluteBaseUrl = Url::base(true);\n$httpsAbsoluteBaseUrl = Url::base('https');\n```\n\nL'unique paramètre de la méthode fonctionne comme pour `Url::home()`.\n\n\n## Création d'URL <span id=\"creating-urls\"></span>\n\nEn vue de créer une URL pour une route donnée, utilisez la méthode `Url::toRoute()`. La méthode utilise [[\\yii\\web\\UrlManager]] pour créer une URL :\n\n```php\n$url = Url::toRoute(['product/view', 'id' => 42]);\n```\n \nVous pouvez spécifier la route sous forme de chaîne de caractère, p. ex. `site/index`. Vous pouvez également utiliser un tableau si vous désirez spécifier des paramètres de requête supplémentaires pour l'URL créée. Le format du tableau doit être :\n\n```php\n// génère : /index.php?r=site%2Findex&param1=value1&param2=value2\n['site/index', 'param1' => 'value1', 'param2' => 'value2']\n```\n\nSi vous voulez créer une URL avec une ancre, vous pouvez utiliser le format de tableau avec un paramètre `#`. Par exemple :\n\n```php\n// génère: /index.php?r=site%2Findex&param1=value1#name\n['site/index', 'param1' => 'value1', '#' => 'name']\n```\n\nUne route peut être ,soit absolue, soit relative. Une route absolue commence par une barre oblique de division (p. ex. `/site/index`) tandis que route relative commence sans ce caractère (p. ex. `site/index` ou `index`). Une route relative peut être convertie en une route absolue en utilisant une des règles suivantes :\n- Si la route est une chaîne de caractères vide, la [[\\yii\\web\\Controller::route|route]] est utilisée ;\n- Si la route ne contient aucune barre oblique de division (p. ex. `index`), elle est considérée être un identifiant d'action dans le contrôleur courant et sera préfixée par l'identifiant du contrôleur ([[\\yii\\web\\Controller::uniqueId]]);\n- Si la route ne commence pas par une barre oblique de division (p. ex. `site/index`), elle est considérée être une route relative au module courant et sera préfixée par l'identifiant du module ([[\\yii\\base\\Module::uniqueId|uniqueId]]).\n  \nDepuis la version 2.0.2, vous pouvez spécifier une route sous forme d'[alias](concept-aliases.md). Si c'est le cas, l'alias sera d'abord converti en la route réelle puis transformé en une route absolue en respectant les règles ci-dessus.\n\nVoci quelques exemple d'utilisation de cette méthode :\n\n```php\n// /index.php?r=site%2Findex\necho Url::toRoute('site/index');\n\n// /index.php?r=site%2Findex&src=ref1#name\necho Url::toRoute(['site/index', 'src' => 'ref1', '#' => 'name']);\n\n// /index.php?r=post%2Fedit&id=100     assume the alias \"@postEdit\" is defined as \"post/edit\"\necho Url::toRoute(['@postEdit', 'id' => 100]);\n\n// https://www.example.com/index.php?r=site%2Findex\necho Url::toRoute('site/index', true);\n\n// https://www.example.com/index.php?r=site%2Findex\necho Url::toRoute('site/index', 'https');\n```\n\nIl existe une autre méthode `Url::to()` très similaire à  [[toRoute()]]. La seule différence est que cette méthode requiert la spécification d'une route sous forme de tableau seulement. Si une chaîne de caractères est données, elle est traitée comme une URL.\n\nLe premier argument peut être :\n         \n- un tableau : [[toRoute()]] sera appelée pour générer l'URL. Par exemple :\n  `['site/index']`, `['post/index', 'page' => 2]`. Reportez-vous à la méthode [[toRoute()]] pour plus de détails sur la manière de spécifier une route.\n- une chaîne de caractères commençant par `@`: elle est traitée commme un alias, et la chaine aliasée correspondante est retournée ;\n- une chaîne de caractères vide : l'URL couramment requise est retournée ;\n- une chaîne de caractères normale : elle est retournée telle que.\n\nLorsque `$scheme` est spécifié (soit une chaîne de caractères, soit `true`), une URL absolue avec l'information hôte tirée de [[\\yii\\web\\UrlManager::hostInfo]]) est retournée. Si`$url` est déjà une URL absolue, son schéma est remplacé par celui qui est spécifié. \n\nVoici quelques exemples d'utilisation :\n\n```php\n// /index.php?r=site%2Findex\necho Url::to(['site/index']);\n\n// /index.php?r=site%2Findex&src=ref1#name\necho Url::to(['site/index', 'src' => 'ref1', '#' => 'name']);\n\n// /index.php?r=post%2Fedit&id=100     assume the alias \"@postEdit\" is defined as \"post/edit\"\necho Url::to(['@postEdit', 'id' => 100]);\n\n// l'URL couramment requise\necho Url::to();\n\n// /images/logo.gif\necho Url::to('@web/images/logo.gif');\n\n// images/logo.gif\necho Url::to('images/logo.gif');\n\n// https://www.example.com/images/logo.gif\necho Url::to('@web/images/logo.gif', true);\n\n// https://www.example.com/images/logo.gif\necho Url::to('@web/images/logo.gif', 'https');\n```\n\nDepuis la version 2.0.3, vous pouvez utiliser [[yii\\helpers\\Url::current()]] pour créer une URL basée sur la route couramment requise et sur les paramètres de la méthode GET. Vous pouvez modifier ou retirer quelques uns des paramètres GET et en ajouter d'autres en passant le paramètre `$params` à la méthode. Par exemple :\n\n```php\n// suppose que $_GET = ['id' => 123, 'src' => 'google'],et que la route courante est \"post/view\"\n\n// /index.php?r=post%2Fview&id=123&src=google\necho Url::current();\n\n// /index.php?r=post%2Fview&id=123\necho Url::current(['src' => null]);\n// /index.php?r=post%2Fview&id=100&src=google\necho Url::current(['id' => 100]);\n```\n\n\n## Se souvenir d'URL <span id=\"remember-urls\"></span>\n\nIl y a des cas dans lesquels vous avez besoin de mémoriser une URL et ensuite de l'utiliser durant le traitement d'une des requêtes séquentielles. Cela peut être fait comme suit :\n\n```php\n// se souvenir de l'URL courante \nUrl::remember();\n\n// Se souvenir de l'URL spécifiée. Voir Url::to() pour le format des arguments.\nUrl::remember(['product/view', 'id' => 42]);\n\n// Se souvenir de  l'URL spécifiée avec un nom\nUrl::remember(['product/view', 'id' => 42], 'product');\n```\n\nDans la prochaine requête, vous pouvez récupérer l'URL mémorisée comme ceci :\n\n```php\n$url = Url::previous();\n$productUrl = Url::previous('product');\n```\n                        \n## Vérification des URL relatives <span id=\"checking-relative-urls\"></span>\n\nPour savoir si une URL est relative, c.-à-d. n'a pas de partie « hôte », vous pouvez utiliser le code suivant : \n                             \n```php\n$isRelative = Url::isRelative('test/it');\n```\n"
  },
  {
    "path": "docs/guide-fr/images/advanced-app-configs.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yFiles for Java 2.11-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Beschreibung\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"409.0\" width=\"148.0\" x=\"290.953299818952\" y=\"148.6669921875\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"148.0\" x=\"0.0\" y=\"0.0\">console</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 4</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n0:\"/>\n    </node>\n    <node id=\"n1\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"409.0\" width=\"148.0\" x=\"310.2342794220951\" y=\"167.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"148.0\" x=\"0.0\" y=\"0.0\">backend</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"310.2342794220951\" y=\"167.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 3</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n1:\"/>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"62.0\" width=\"118.0\" x=\"348.0\" y=\"628.9999999999999\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"32.6875\" x=\"42.65625\" y=\"21.6494140625\">index<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"rectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n3\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"402.37646484375\" width=\"148.0\" x=\"333.0\" y=\"188.62353515625\"/>\n              <y:Fill color=\"#DDFFDD\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#DDFFDD\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"148.0\" x=\"0.0\" y=\"0.0\">frontend</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 1</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n3:\">\n        <node id=\"n3::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"348.0\" y=\"226.0\"/>\n              <y:Fill color=\"#FFCCCC\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"44.013671875\" x=\"36.9931640625\" y=\"21.6494140625\">params<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n3::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"348.0\" y=\"322.0\"/>\n              <y:Fill color=\"#FFCCCC\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"72.689453125\" x=\"22.6552734375\" y=\"21.6494140625\">params-local<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n3::n2\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"348.0\" y=\"418.0\"/>\n              <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"30.009765625\" x=\"43.9951171875\" y=\"21.6494140625\">main<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n3::n3\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"348.0\" y=\"514.0\"/>\n              <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"58.685546875\" x=\"29.6572265625\" y=\"21.6494140625\">main-local<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"62.0\" width=\"223.21580824827026\" x=\"7.0\" y=\"628.9999999999999\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"41.353515625\" x=\"90.93114631163513\" y=\"21.6494140625\">aliases<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"rectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"306.37646484375\" width=\"311.0\" x=\"7.0\" y=\"188.62353515625\"/>\n              <y:Fill color=\"#DDFFDD\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#DDFFDD\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"311.0\" x=\"0.0\" y=\"0.0\">common</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"-8.0\" y=\"189.333984375\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 2</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n5:\">\n        <node id=\"n5::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"185.0\" y=\"226.0\"/>\n              <y:Fill color=\"#FFCCCC\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"72.689453125\" x=\"22.6552734375\" y=\"21.6494140625\">params-local<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n5::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"185.0\" y=\"418.0\"/>\n              <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"58.685546875\" x=\"29.6572265625\" y=\"21.6494140625\">main-local<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n5::n2\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"22.0\" y=\"226.0\"/>\n              <y:Fill color=\"#FFCCCC\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"44.013671875\" x=\"36.9931640625\" y=\"21.6494140625\">params<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n5::n3\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"22.0\" y=\"418.0\"/>\n              <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"30.009765625\" x=\"43.9951171875\" y=\"21.6494140625\">main<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <edge id=\"e0\" source=\"n3::n3\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"2.0\" y=\"10.125\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-111.48005839096935\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"3.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.25\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"33.47449351530429\" y=\"-2.0000000000001137\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n5::n1\" target=\"n3::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"5.6843418860808015E-14\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"218.8251533742331\" y=\"449.00000000000006\"/>\n            <y:Point x=\"218.8251533742331\" y=\"449.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"10.089752197265625\" y=\"-6.0\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n5::e0\" source=\"n5::n2\" target=\"n5::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"10.125\" y=\"-6.0\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n5::e1\" source=\"n5::n3\" target=\"n5::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"10.125\" y=\"-6.0\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n3::e0\" source=\"n3::n0\" target=\"n3::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"2.0\" y=\"10.125\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n3::e1\" source=\"n3::n1\" target=\"n3::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"2.0\" y=\"10.125\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n3::e2\" source=\"n3::n2\" target=\"n3::n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"2.0\" y=\"10.125\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n5::n0\" target=\"n3::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"-5.6843418860808015E-14\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"218.8251533742331\" y=\"256.99999999999994\"/>\n            <y:Point x=\"218.8251533742331\" y=\"257.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"10.089752197265625\" y=\"-6.0\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources/>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-fr/images/application-lifecycle.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.13-->\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d0\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key for=\"graphml\" id=\"d7\" yfiles.type=\"resources\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d0\"/>\n    <node id=\"n0\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"571.4472707112631\" width=\"763.2772213171534\" x=\"-1269.9373595143054\" y=\"-207.17439524332679\"/>\n              <y:Fill color=\"#FFCC0024\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FFCC00\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"763.2772213171534\" x=\"0.0\" y=\"0.0\">Entry script (index.php or yii)</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"225.33495140075684\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 4</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n0:\">\n        <node id=\"n0::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"324.9258883570935\" x=\"-1249.511914911339\" y=\"-169.79793039957679\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"126.759765625\" x=\"99.08306136604676\" y=\"5.6494140625\">Load application config<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n0::n1\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d5\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"309.37646484374994\" width=\"330.35133296005984\" x=\"-1254.9373595143054\" y=\"35.272875467936274\"/>\n                  <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"330.35133296005984\" x=\"0.0\" y=\"0.0\">Create application instance</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"19\" bottomF=\"19.15489692687993\" left=\"5\" leftF=\"5.425444602966309\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 5</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n0::n1:\">\n            <node id=\"n0::n1::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"72.64934031168627\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"45.34375\" x=\"124.79106917854676\" y=\"5.6494140625\">preInit()<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n1\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"122.64524027506516\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"120.71875\" x=\"87.10356917854676\" y=\"5.6494140625\">Register error handler<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n2\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"174.96110788981125\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"173.435546875\" x=\"60.74517074104676\" y=\"5.6494140625\">Configure application properties<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n3\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"226.56779181162517\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"27.33203125\" x=\"133.79692855354676\" y=\"5.6494140625\">init()<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n4\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.9258883570935\" x=\"-1234.511914911339\" y=\"280.4944433848063\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.025390625\" x=\"116.45024886604676\" y=\"5.6494140625\">bootstrap()<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n        <node id=\"n0::n2\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d5\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"411.6943410237631\" width=\"324.9258883570935\" x=\"-846.5860265542456\" y=\"-169.79793039957679\"/>\n                  <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"324.9258883570935\" x=\"0.0\" y=\"0.0\">Run application</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"1.1368683772161603E-13\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 3</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n0::n2:\">\n            <node id=\"n0::n2::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.9258883570935\" x=\"-831.5860265542456\" y=\"-132.42146555582667\"/>\n                  <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"164.705078125\" x=\"65.11040511604676\" y=\"5.6494140625\">EVENT_BEFORE_REQUEST<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n2::n1\" yfiles.foldertype=\"group\">\n              <data key=\"d4\"/>\n              <data key=\"d5\"/>\n              <data key=\"d6\">\n                <y:ProxyAutoBoundsNode>\n                  <y:Realizers active=\"0\">\n                    <y:GroupNode>\n                      <y:Geometry height=\"204.37646484375\" width=\"294.9258883570935\" x=\"-831.5860265542456\" y=\"-78.79793039957679\"/>\n                      <y:Fill color=\"#99336635\" transparent=\"false\"/>\n                      <y:BorderStyle hasColor=\"false\" type=\"dashed\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#993366\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#FFFFFF\" visible=\"true\" width=\"294.9258883570935\" x=\"0.0\" y=\"0.0\">Handle request</y:NodeLabel>\n                      <y:Shape type=\"roundrectangle\"/>\n                      <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                      <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                      <y:BorderInsets bottom=\"8\" bottomF=\"7.929194132486941\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                    </y:GroupNode>\n                    <y:GroupNode>\n                      <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                      <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 4</y:NodeLabel>\n                      <y:Shape type=\"roundrectangle\"/>\n                      <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                      <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                      <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                    </y:GroupNode>\n                  </y:Realizers>\n                </y:ProxyAutoBoundsNode>\n              </data>\n              <graph edgedefault=\"directed\" id=\"n0::n2::n1:\">\n                <node id=\"n0::n2::n1::n0\">\n                  <data key=\"d6\">\n                    <y:ShapeNode>\n                      <y:Geometry height=\"30.0\" width=\"264.9258883570935\" x=\"-816.5860265542456\" y=\"-41.421465555826785\"/>\n                      <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"231.4609375\" x=\"16.732475428546763\" y=\"5.6494140625\">Resolve request into route and parameters<y:LabelModel>\n                          <y:SmartNodeLabelModel distance=\"4.0\"/>\n                        </y:LabelModel>\n                        <y:ModelParameter>\n                          <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                        </y:ModelParameter>\n                      </y:NodeLabel>\n                      <y:Shape type=\"rectangle\"/>\n                    </y:ShapeNode>\n                  </data>\n                </node>\n                <node id=\"n0::n2::n1::n1\">\n                  <data key=\"d6\">\n                    <y:ShapeNode>\n                      <y:Geometry height=\"30.0\" width=\"264.9258883570935\" x=\"-816.5860265542456\" y=\"18.578534444173215\"/>\n                      <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"197.44140625\" x=\"33.74224105354676\" y=\"5.6494140625\">Create module, controller and action<y:LabelModel>\n                          <y:SmartNodeLabelModel distance=\"4.0\"/>\n                        </y:LabelModel>\n                        <y:ModelParameter>\n                          <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                        </y:ModelParameter>\n                      </y:NodeLabel>\n                      <y:Shape type=\"rectangle\"/>\n                    </y:ShapeNode>\n                  </data>\n                </node>\n                <node id=\"n0::n2::n1::n2\">\n                  <data key=\"d6\">\n                    <y:ShapeNode>\n                      <y:Geometry height=\"30.0\" width=\"264.9258883570935\" x=\"-816.5860265542456\" y=\"72.64934031168627\"/>\n                      <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.369140625\" x=\"101.77837386604676\" y=\"5.649414062500057\">Run action<y:LabelModel>\n                          <y:SmartNodeLabelModel distance=\"4.0\"/>\n                        </y:LabelModel>\n                        <y:ModelParameter>\n                          <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                        </y:ModelParameter>\n                      </y:NodeLabel>\n                      <y:Shape type=\"rectangle\"/>\n                    </y:ShapeNode>\n                  </data>\n                </node>\n              </graph>\n            </node>\n            <node id=\"n0::n2::n2\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.9258883570935\" x=\"-831.5860265542456\" y=\"149.20206960042316\"/>\n                  <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"154.697265625\" x=\"70.11431136604676\" y=\"5.6494140625\">EVENT_AFTER_REQUEST<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n2::n3\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-831.5860265542456\" y=\"196.89641062418633\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"148.099609375\" x=\"73.41313949104676\" y=\"5.6494140625\">Send response to end user<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n        <node id=\"n0::n3\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"324.9258883570935\" x=\"-846.5860265542456\" y=\"319.2728754679363\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"160.08203125\" x=\"82.42192855354676\" y=\"5.6494140625\">Complete request processing<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <edge id=\"e0\" source=\"n0\" target=\"n0::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-179.83580569315376\" sy=\"-180.3944529152355\" tx=\"-13.869777491410105\" ty=\"-154.8008369539754\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::e0\" source=\"n0::n0\" target=\"n0::n1\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#99CCFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"106.052734375\" x=\"-130.76131968438426\" y=\"78.18481445312506\">Configuration array<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"77.04379339868619\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e0\" source=\"n0::n1::n0\" target=\"n0::n1::n1\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e1\" source=\"n0::n1::n1\" target=\"n0::n1::n2\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e2\" source=\"n0::n1::n2\" target=\"n0::n1::n3\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e3\" source=\"n0::n1::n3\" target=\"n0::n1::n4\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::e1\" source=\"n0::n1\" target=\"n0::n2\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"2.8060680342755404\" sy=\"-48.37646484374994\" tx=\"-162.49660512430125\" ty=\"105.53540293375653\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::e0\" source=\"n0::n2::n0\" target=\"n0::n2::n1\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::n1::e0\" source=\"n0::n2::n1::n0\" target=\"n0::n2::n1::n1\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::n1::e1\" source=\"n0::n2::n1::n1\" target=\"n0::n2::n1::n2\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::e1\" source=\"n0::n2::n1\" target=\"n0::n2::n2\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::e2\" source=\"n0::n2::n2\" target=\"n0::n2::n3\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::e2\" source=\"n0::n2\" target=\"n0::n3\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#99CCFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"59.353515625\" x=\"-93.67673227804266\" y=\"28.318773905436274\">Exit status<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"63.99999999999999\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.47945569632951074\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d7\">\n    <y:Resources/>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-fr/images/application-structure.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"872.1807999999999\" y=\"-14.764159999999947\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"32.265625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"69.712890625\" x=\"15.1435546875\" y=\"1.3671875\">composant\nd'application<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"-91.97375999999994\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"70.28125\" x=\"14.859375\" y=\"8.43359375\">script de\ndémarrage<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"-14.764159999999947\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"68.21875\" x=\"15.890625\" y=\"8.43359375\">application<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"62.44544000000004\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"60.267578125\" x=\"19.8662109375\" y=\"8.433593750000007\">contrôleur<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"872.1807999999999\" y=\"62.44544000000005\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"31.43359375\" x=\"34.283203125\" y=\"8.43359375\">filtre<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"532.664\" y=\"23.901600000000087\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"47.728515625\" x=\"26.1357421875\" y=\"8.43359375\">module<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"618.4047999999991\" y=\"139.65504000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"29.611328125\" x=\"35.1943359375\" y=\"8.43359375\">vue<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"786.161599999999\" y=\"139.65504000000004\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"40.28125\" x=\"29.859375\" y=\"8.43359375\">modèle<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n8\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"532.664\" y=\"216.86464000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"42.923828125\" x=\"28.5380859375\" y=\"8.43359375\">widget<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n9\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"216.86464000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"77.986328125\" x=\"11.0068359375\" y=\"8.43359375\">asset bundle<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n2\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.97265625\" x=\"2.5392475585936154\" y=\"-25.635106635436955\">1:1<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.025600000000054\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.17992697197425173\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n0\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-48.97602119140629\" y=\"-23.09200633216852\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.025599999999994\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5470891790487767\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n5\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"15.193962466822086\" y=\"30.674065521861735\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"44.894496863129426\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.25416574623968574\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n3\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"2.7719538085937074\" y=\"-26.04470613037104\">1..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"15.254400000000032\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.2090245199765919\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n3\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-57.39355288912964\" y=\"-63.846685518352906\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"43.47658308018222\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.881412889692355\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n5\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"582.664\" y=\"-3.598399999999913\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-17.888505566406252\" y=\"-42.41848039245597\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"16.483200000000004\" distanceToCenter=\"true\" position=\"right\" ratio=\"5.822600000000001\" segment=\"-2\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n6\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-12.223966589163297\" y=\"-24.76668258085064\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"11.385360101663515\" distanceToCenter=\"true\" position=\"left\" ratio=\"0.10670293331985262\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e7\" source=\"n7\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-9.313146217848043\" y=\"-26.098020948340803\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.669798038586146\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.1670192242704811\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e8\" source=\"n9\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-9.797233483694527\" y=\"-25.82443358614151\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.15599378957306\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.15481212573620068\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e9\" source=\"n8\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-15.633139430038\" y=\"-24.050683308790553\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"13.24330808446041\" distanceToCenter=\"true\" position=\"left\" ratio=\"0.05506723556297327\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e10\" source=\"n4\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-47.337621191406356\" y=\"-22.682408449706998\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"13.616000000000007\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e11\" source=\"n9\" target=\"n8\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-44.265598339843905\" y=\"-19.61040553222648\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.543999999999983\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.4117077892835431\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e12\" source=\"n7\" target=\"n6\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-44.81721357421975\" y=\"-19.610410805664003\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.543999999999983\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.4531592446547025\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources/>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-fr/images/rbac-access-check-1.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-fr/images/rbac-access-check-2.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-fr/images/rbac-access-check-3.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-fr/images/rbac-hierarchy-1.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n5\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-fr/images/rbac-hierarchy-2.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-fr/images/request-lifecycle.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"70.13700103759766\" width=\"56.558998107910156\" x=\"198.38633947503078\" y=\"261.099458694458\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"26.279499053955078\" y=\"74.13700103759766\">\n            <y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"-0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"0.5\" offsetX=\"0.0\" offsetY=\"4.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"29.16015625\" x=\"-33.16015625\" y=\"26.002094268798828\">utilisateur<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.5\" labelRatioY=\"0.0\" nodeRatioX=\"-0.5\" nodeRatioY=\"0.0\" offsetX=\"-4.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"637.1271178722382\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"40.28125\" x=\"39.359375\" y=\"13.43359375\">modèle<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"46.887996673583984\" width=\"39.527000427246094\" x=\"828.8018617630005\" y=\"544.9831195354463\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"56.27734375\" x=\"-8.375171661376953\" y=\"-30.154888153076172\">base de donnée<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-12.022075653076172\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"17.763500213623047\" y=\"21.443998336791992\">\n            <y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"713.6271178722382\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"29.611328125\" x=\"44.6943359375\" y=\"13.43359375\">vue<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n4\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"376.3268349409104\" width=\"219.27949905395508\" x=\"527.4919853210449\" y=\"408.6371007919311\"/>\n              <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.666015625\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"219.27949905395508\" x=\"0.0\" y=\"0.0\">contrôleur</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"2\" leftF=\"1.7335329055786133\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"412.2765645980835\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.666015625\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"63.75830078125\" x=\"-6.879150390625\" y=\"0.0\">Folder 1</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n4:\">\n        <node id=\"n4::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"445.3031164169311\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"78.888671875\" x=\"35.83516311645508\" y=\"5.93359375\">créer l'action<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n4::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"45.0\" width=\"159.91864204406738\" x=\"558.5326642990112\" y=\"521.8166599273682\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"88.392578125\" x=\"35.76303195953369\" y=\"13.43359375\">effectue \nles filtres<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"diamond\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n4::n2\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"162.00283348560333\" width=\"187.54596614837646\" x=\"544.2255182266235\" y=\"607.9611022472382\"/>\n                  <y:Fill color=\"#99336635\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#993366\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.666015625\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#FFFFFF\" visible=\"true\" width=\"187.54596614837646\" x=\"0.0\" y=\"0.0\">action</y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"4\" bottomF=\"3.8368178606033325\" left=\"4\" leftF=\"3.9869680404663086\" right=\"3\" rightF=\"3.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.666015625\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"63.75830078125\" x=\"-6.879150390625\" y=\"0.0\">Folder 3</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n4::n2:\">\n            <node id=\"n4::n2::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"644.6271178722382\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"69.09765625\" x=\"40.73067092895508\" y=\"5.93359375\">charge un modèle<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n4::n2::n1\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"721.1271178722382\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"71.599609375\" x=\"39.47969436645508\" y=\"5.93359375\">génère une vue<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n      </graph>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"96.0\" x=\"189.47636032104495\" y=\"713.6271178722382\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"56.88671875\" x=\"19.556640625\" y=\"13.43359375\">réponse<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"254.50096702575684\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"47.88671875\" x=\"35.556640625\" y=\"13.43359375\">requête<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"143.421781539917\" width=\"219.27949905395508\" x=\"527.4919853210449\" y=\"223.57918548583984\"/>\n              <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.666015625\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"219.27949905395508\" x=\"0.0\" y=\"0.0\">application</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"21\" leftF=\"20.720500946044922\" right=\"18\" rightF=\"18.0\" top=\"2\" topF=\"1.7557659149169922\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.666015625\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"63.75830078125\" x=\"-6.879150390625\" y=\"0.0\">Folder 2</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n7:\">\n        <node id=\"n7::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"262.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"80.1484375\" x=\"35.20528030395508\" y=\"5.93359375\">résout la route<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n7::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"322.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"99.607421875\" x=\"25.475788116455078\" y=\"5.93359375\">crée une instance \nde contrôleur<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <node id=\"n8\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"141.666015625\" width=\"159.91864204406738\" x=\"313.2978515625\" y=\"225.33495140075684\"/>\n              <y:Fill color=\"#FFCC0024\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FFCC00\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.666015625\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"159.91864204406738\" x=\"0.0\" y=\"0.0\">script de démarrage</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"1\" leftF=\"0.9186420440673828\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"225.33495140075684\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.666015625\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"63.75830078125\" x=\"-6.879150390625\" y=\"0.0\">Folder 4</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n8:\">\n        <node id=\"n8::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"129.0\" x=\"329.2164936065674\" y=\"262.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"94.673828125\" x=\"17.1630859375\" y=\"5.93359375\">charge la configuration \nde l'application<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n8::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"129.0\" x=\"329.2164936065674\" y=\"322.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"91.8203125\" x=\"18.58984375\" y=\"5.93359375\">créé une instance \nd'application<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <edge id=\"e0\" source=\"n2\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n5\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-10.81052179205907\" sy=\"-22.46484374999998\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"28.000006009454637\" y=\"-193.20499098300934\">\n            <y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"19.17578125\" x=\"-9.587884615545363\" y=\"-200.27139723300934\">11<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n6\" target=\"n7::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.587890625\" x=\"-30.207454877387818\" y=\"-10.065448760986328\">3<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"0.9990329742431641\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.25395048761528816\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n7::e0\" source=\"n7::n0\" target=\"n7::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n0\" target=\"n8\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"-79.9882439360955\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.587890625\" x=\"37.24206271236892\" y=\"-9.066415786743164\">1<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n7::n1\" target=\"n4\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"2.408647025755731\" ty=\"-181.21658369302781\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.587890625\" x=\"-5.416119621118469\" y=\"19.25165109634395\">4<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n3\" target=\"n4::n2::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.587890625\" x=\"-30.77317586853087\" y=\"-9.06642460823059\">9<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.26448415477215953\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n4::n2::n1\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"-2.2737367544323206E-13\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"19.17578125\" x=\"-93.46793455769887\" y=\"-9.066424608230705\">10<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.2786124840137136\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e7\" source=\"n8::n1\" target=\"n7\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"55.750299312644984\" sy=\"0.355224609375\" tx=\"-109.63961141576266\" ty=\"42.066115379333496\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.587890625\" x=\"28.868979454040527\" y=\"-9.066415786743164\">2<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e8\" source=\"n1\" target=\"n4::n2::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.587890625\" x=\"-31.923049588221375\" y=\"-10.06642460823059\">8<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"1.0\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.2858946861565087\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e9\" source=\"n4::n1\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-79.9882439360951\" sy=\"2.2737367544323206E-13\" tx=\"24.24112858184396\" ty=\"-1.4999999999999991\">\n            <y:Point x=\"261.71748890288893\" y=\"544.3166599273684\"/>\n          </y:Path>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.587890625\" x=\"-83.6574311462557\" y=\"-9.066396713256609\">6<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.23459266172695797\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::e0\" source=\"n4::n0\" target=\"n4::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFEFD6\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.587890625\" x=\"-5.793964385986328\" y=\"4.999985313415493\">5<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.0\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::e1\" source=\"n4::n1\" target=\"n4::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"-81.01828954355172\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFEFD6\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.587890625\" x=\"-5.903311505104057\" y=\"5.037729263305664\">7<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.0\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::n2::e0\" source=\"n4::n2::n0\" target=\"n4::n2::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n8::e0\" source=\"n8::n0\" target=\"n8::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"66px\" viewBox=\"0 0 57 66\" enable-background=\"new 0 0 57 66\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3799\" y1=\"-2276.8809\" x2=\"27.6209\" y2=\"-2306.6792\" gradientTransform=\"matrix(1 0 0 -1 0.2803 -2252.9199)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_13_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t&lt;path fill=\"#2068A3\" stroke=\"#2068A3\" d=\"M28.106,33.487c-8.112,0-12.688,4.312-12.688,10.437c0,7.422,12.688,10.438,12.688,10.438\n\t\ts14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.487,28.106,33.487z M26.288,53.051c0,0-7.135-2.093-8.805-7.201\n\t\tc-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"14.2417\" cy=\"9.1006\" r=\"53.247\" gradientTransform=\"matrix(1 0 0 -1 0.04 65.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#74AEEE\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#2068A3\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#2068A3\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.022,7.807-14.022,7.807s-10.472-2.484-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path fill=\"#5491CF\" stroke=\"#2068A3\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397c-0.514,1.027-1.669,4.084-1.669,5.148\n\t\tc0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.335-2.36c-3.601-1.419-4.071-3.063-5.89-4.854\n\t\tC12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t&lt;path fill=\"#5491CF\" stroke=\"#2068A3\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617c0.516,1.025,3.617,3.693,3.617,6.617\n\t\tc0,5.186-10.27,8.576-16.698,9.145c1.429,4.938,11.372,1.293,13.804-0.313c3.563-2.354,4.563-5.133,7.854-3.705\n\t\tC47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t&lt;path fill=\"none\" stroke=\"#2068A3\" stroke-linecap=\"round\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t&lt;path fill=\"none\" stroke=\"#2068A3\" stroke-linecap=\"round\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.623\" cy=\"-2278.646\" r=\"23.425\" fx=\"23.0534\" fy=\"-2281.1357\" gradientTransform=\"matrix(1 0 0 -1 0.2803 -2252.9199)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"5761.7578\" y1=\"11330.6484\" x2=\"5785.3872\" y2=\"11424.0977\" gradientTransform=\"matrix(0.275 0 0 0.2733 -1558.9874 -3088.4209)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M27.958,6.333c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.083,13.952,36.271,6.268,27.958,6.333z\"/&gt;\n\t&lt;path id=\"Hair_Young_Brown_1_\" fill=\"#CC9869\" stroke=\"#99724F\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n\t&lt;path fill=\"#4B4B4B\" stroke=\"#4B4B4B\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" d=\"M28.105,2\n\t\tC22.464,2,20.2,4.246,18.13,5.533C29.753,2.865,41.152,10.375,44.46,20.5C44.459,16.875,44.459,2,28.105,2z\"/&gt;\n\t&lt;path fill=\"#9B9B9B\" stroke=\"#4B4B4B\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" d=\"M11.151,17.751\n\t\tC12.878,8.25,18.686,6.309,25.273,7.127C31.295,7.875,36.93,10.491,44.459,20.5C37.777,7.125,20.278-3.375,9.903,3.921\n\t\tC5.569,6.97,4.903,13.375,11.151,17.751z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\"\n\t xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\t x=\"0px\" y=\"0px\" width=\"41px\" height=\"48px\" viewBox=\"-0.875 -0.887 41 48\" enable-background=\"new -0.875 -0.887 41 48\"\n\t xml:space=\"preserve\"&gt;\n&lt;defs&gt;\n&lt;/defs&gt;\n&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-979.1445\" x2=\"682.0508\" y2=\"-979.1445\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_1_)\" d=\"M19.625,36.763C8.787,36.763,0,34.888,0,32.575v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,34.888,30.464,36.763,19.625,36.763z\"/&gt;\n&lt;linearGradient id=\"SVGID_2_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-973.1445\" x2=\"682.0508\" y2=\"-973.1445\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_2_)\" d=\"M19.625,36.763c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.927-18.396,3.927\n\tc-9.481,0-17.396-1.959-18.396-3.927l-1.229,2C0,34.888,8.787,36.763,19.625,36.763z\"/&gt;\n&lt;path fill=\"#3C89C9\" d=\"M19.625,26.468c10.16,0,19.625,2.775,19.625,2.775c-0.375,2.721-5.367,5.438-19.554,5.438\n\tc-12.125,0-18.467-2.484-19.541-4.918C-0.127,29.125,9.465,26.468,19.625,26.468z\"/&gt;\n&lt;linearGradient id=\"SVGID_3_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-965.6948\" x2=\"682.0508\" y2=\"-965.6948\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_3_)\" d=\"M19.625,23.313C8.787,23.313,0,21.438,0,19.125v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,21.438,30.464,23.313,19.625,23.313z\"/&gt;\n&lt;linearGradient id=\"SVGID_4_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-959.6948\" x2=\"682.0508\" y2=\"-959.6948\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_4_)\" d=\"M19.625,23.313c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.926-18.396,3.926\n\tc-9.481,0-17.396-1.959-18.396-3.926l-1.229,2C0,21.438,8.787,23.313,19.625,23.313z\"/&gt;\n&lt;path fill=\"#3C89C9\" d=\"M19.476,13.019c10.161,0,19.625,2.775,19.625,2.775c-0.375,2.721-5.367,5.438-19.555,5.438\n\tc-12.125,0-18.467-2.485-19.541-4.918C-0.277,15.674,9.316,13.019,19.476,13.019z\"/&gt;\n&lt;linearGradient id=\"SVGID_5_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-952.4946\" x2=\"682.0508\" y2=\"-952.4946\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_5_)\" d=\"M19.625,10.113C8.787,10.113,0,8.238,0,5.925v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,8.238,30.464,10.113,19.625,10.113z\"/&gt;\n&lt;linearGradient id=\"SVGID_6_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-946.4946\" x2=\"682.0508\" y2=\"-946.4946\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_6_)\" d=\"M19.625,10.113c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.926-18.396,3.926\n\tc-9.481,0-17.396-1.959-18.396-3.926L0,5.925C0,8.238,8.787,10.113,19.625,10.113z\"/&gt;\n&lt;linearGradient id=\"SVGID_7_\" gradientUnits=\"userSpaceOnUse\" x1=\"644.0293\" y1=\"-943.4014\" x2=\"680.8223\" y2=\"-943.4014\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;ellipse fill=\"url(#SVGID_7_)\" cx=\"19.625\" cy=\"3.926\" rx=\"18.396\" ry=\"3.926\"/&gt;\n&lt;path opacity=\"0.24\" fill=\"#FFFFFF\" enable-background=\"new    \" d=\"M31.04,45.982c0,0-4.354,0.664-7.29,0.781\n\tc-3.125,0.125-8.952,0-8.952,0l-2.384-10.292l0.044-2.108l-1.251-1.154L9.789,23.024l-0.082-0.119L9.5,20.529l-1.65-1.254\n\tL5.329,8.793c0,0,4.213,0.903,7.234,1.07s8.375,0.25,8.375,0.25l3,9.875l-0.25,1.313l1.063,2.168l2.312,9.645l-0.521,1.416\n\tl1.46,1.834L31.04,45.982z\"/&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-fr/input-file-upload.md",
    "content": "Chargement de fichiers sur le serveur\n=====================================\n\nLe chargement de fichiers sur le serveur dans Yii est ordinairement effectué avec l'aide de [[yii\\web\\UploadedFile]] qui encapsule chaque fichier chargé dans un objet `UploadedFile`. Combiné avec les [[yii\\widgets\\ActiveForm]] et les [modèles](structure-models.md), vous pouvez aisément mettre en œuvre un mécanisme sûr de chargement de fichiers sur le serveur.\n\n\n## Création de modèles <span id=\"creating-models\"></span>\n\nComme on le ferait avec des entrées de texte simple, pour charger un unique fichier sur le serveur, vous devez créer une classe de modèle et utliser un attribut du modèle pour conserver un instance du fichier chargé. Vous devez également déclarer une règle de validation pour valider le fichier chargé. Par exemple : \n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\nuse yii\\web\\UploadedFile;\n\nclass UploadForm extends Model\n{\n    /**\n     * @var UploadedFile\n     */\n    public $imageFile;\n\n    public function rules()\n    {\n        return [\n            [['imageFile'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg'],\n        ];\n    }\n    \n    public function upload()\n    {\n        if ($this->validate()) {\n            $this->imageFile->saveAs('uploads/' . $this->imageFile->baseName . '.' . $this->imageFile->extension);\n            return true;\n        } else {\n            return false;\n        }\n    }\n}\n```\n\nDans le code ci-dessus, l'attribut `imageFile` est utilisé pur conserver une instance du fichier chargé. Il est associé à une règle de validation de fichier (`file`) qui utilise [[yii\\validators\\FileValidator]] pour garantir que l'extension du nom de fichier chargé est `png` ou `jpg`. La méthode `upload()` effectue l'examen de validation et sauvegarde le fichier sur le serveur.\n\nLe validateur `file` vous permet de vérifier l'extension du fichier, sa taille, son type MIME, etc. Reportez-vous à la section [Validateurs de noyau](tutorial-core-validators.md#file) pour plus de détails.\n\n> Tip: si vous chargez une image sur le serveur, vous pouvez envisager l'utilisation du validateur `image` au lieu de `file`. Le validateur `image` est mis en œuvre via [[yii\\validators\\ImageValidator]] qui vérifie si un attribut a reçu une image valide qui peut être, soit sauvegardée, soit traitée en utilisant l'[extension Imagine](https://github.com/yiisoft/yii2-imagine).\n\n\n## Rendu d'une entrée de fichier <span id=\"rendering-file-input\"></span>\n\nEnsuite, créez une entrée de fichier dans une vue :\n\n```php\n<?php\nuse yii\\widgets\\ActiveForm;\n?>\n\n<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?>\n\n    <?= $form->field($model, 'imageFile')->fileInput() ?>\n\n    <button>Submit</button>\n\n<?php ActiveForm::end() ?>\n```\n\nIl est important de se rappeler que vous devez ajouter l'option `enctype` au formulaire afin que le fichier soit proprement chargé sur le serveur. L'appel de `fileInput()` rend une balise `<input type=\"file\">` qui permet à l'utilisateur de sélectionner un fichier à charger sur le serveur.\n\n> Tip: depuis la version 2.0.8, [[yii\\widgets\\ActiveField::fileInput|fileInput]] ajoute l'option `enctype` au formulaire automatiquement lorsqu'un champ d'entrée de fichier est utilisé.\n\n## Câblage <span id=\"wiring-up\"></span>\n\nMaintenant dans une action de contrôleur, écrivez le code de câblage entre le modèle et la vue pour mettre en œuvre le chargement sur le serveur :\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\nuse app\\models\\UploadForm;\nuse yii\\web\\UploadedFile;\n\nclass SiteController extends Controller\n{\n    public function actionUpload()\n    {\n        $model = new UploadForm();\n\n        if (Yii::$app->request->isPost) {\n            $model->imageFile = UploadedFile::getInstance($model, 'imageFile');\n            if ($model->upload()) {\n                // le fichier a été chargé avec succès sur le serveur\n                return;\n            }\n        }\n\n        return $this->render('upload', ['model' => $model]);\n    }\n}\n```\n\nDans le code ci-dessus, lorsque le formulaire est soumis, la méthode [[yii\\web\\UploadedFile::getInstance()]] est appelée pour représenter le fichier chargé sous forme d'instance de `UploadedFile`. Nous comptons ensuite sur la validation du modèle pour garantir que le fichier chargé est valide et le sauvegarder sur le serveur.\n\n\n## Chargement sur le serveur de plusieurs fichiers  <span id=\"uploading-multiple-files\"></span>\n\nVous pouvez également charger sur le serveur plusieurs fichiers à la fois, avec quelques ajustements au code présenté dans les sous-sections précédentes.\n\nTout d'abord, vous devez ajuster la classe du modèle en ajoutant l'option `maxFiles` dans la règle de validation de `file` pour limiter le nombre maximum de fichiers à charger simultanément. Définir `maxFiles` à `0` signifie que ce nombre n'est pas limité. Le nombre maximal de fichiers que l'on peut charger simultanément est aussi limité par la directive PHP [`max_file_uploads`](https://www.php.net/manual/fr/ini.core.php#ini.max-file-uploads), dont la valeur par défaut est 20. La méthode `upload()` doit aussi être modifiée pour permettre la sauvegarde des fichiers un à un.\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\nuse yii\\web\\UploadedFile;\n\nclass UploadForm extends Model\n{\n    /**\n     * @var UploadedFile[]\n     */\n    public $imageFiles;\n\n    public function rules()\n    {\n        return [\n            [['imageFiles'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg', 'maxFiles' => 4],\n        ];\n    }\n    \n    public function upload()\n    {\n        if ($this->validate()) { \n            foreach ($this->imageFiles as $file) {\n                $file->saveAs('uploads/' . $file->baseName . '.' . $file->extension);\n            }\n            return true;\n        } else {\n            return false;\n        }\n    }\n}\n```\n\nDans le fichier de vue, vous devez ajouter l'option `multiple` à l'appel de `fileInput()` afin que le champ d'entrée puisse recevoir plusieurs fichiers :\n \n```php\n<?php\nuse yii\\widgets\\ActiveForm;\n?>\n\n<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?>\n\n    <?= $form->field($model, 'imageFiles[]')->fileInput(['multiple' => true, 'accept' => 'image/*']) ?>\n\n    <button>Submit</button>\n\n<?php ActiveForm::end() ?>\n```\n\nPour finir, dans l'action du contrôleur, vous devez appeler `UploadedFile::getInstances()` au lieu de `UploadedFile::getInstance()` pour assigner un tableau d'instances de `UploadedFile` à `UploadForm::imageFiles`. \n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\nuse app\\models\\UploadForm;\nuse yii\\web\\UploadedFile;\n\nclass SiteController extends Controller\n{\n    public function actionUpload()\n    {\n        $model = new UploadForm();\n\n        if (Yii::$app->request->isPost) {\n            $model->imageFiles = UploadedFile::getInstances($model, 'imageFiles');\n            if ($model->upload()) {\n                // file is uploaded successfully\n                return;\n            }\n        }\n\n        return $this->render('upload', ['model' => $model]);\n    }\n}\n```\n"
  },
  {
    "path": "docs/guide-fr/input-forms.md",
    "content": "Création de formulaires\n=======================\n\nLa manière primaire d'utiliser des formulaires dans Yii de faire appel aux [[yii\\widgets\\ActiveForm]]. Cette approche doit être privilégiée lorsque le formulaire est basé sur un modèle. En plus, il existe quelques méthodes utiles dans [[yii\\helpers\\Html]] qui sont typiquement utilisées pour ajouter des boutons et des textes d'aides de toute forme.\n\nUn formulaire, qui est affiché du côté client, possède dans la plupart des cas, un [modèle](structure-models.md) correspondant qui est utilisé pour valider ses entrées du côté serveur (lisez la section [Validation des entrées](input-validation.md) pour plus de détails sur la validation). Lors de la création de formulaires basés sur un modèle, la première étape est de définir le modèle lui-même. Le modèle peut être soit basé sur une classe d'[enregistrement actif](db-active-record.md) représentant quelques données de la base de données, soit sur une classe de modèle générique qui étend la classe [[yii\\base\\Model]]) pour capturer des entrées arbitraires, par exemple un formulaire de connexion. Dans l'exemple suivant, nous montrons comment utiliser un modèle générique pour un formulaire de connexion :\n\n```php\n<?php\n\nclass LoginForm extends \\yii\\base\\Model\n{\n    public $username;\n    public $password;\n\n    public function rules()\n    {\n        return [\n            // les règles de validation sont définies ici\n        ];\n    }\n}\n```\n\nDans le contrôleur, nous passons une instance de ce modèle à la vue, dans laquelle le composant graphique [[yii\\widgets\\ActiveForm|ActiveForm]] est utilisé pour afficher le formulaire :\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n$form = ActiveForm::begin([\n    'id' => 'login-form',\n    'options' => ['class' => 'form-horizontal'],\n]) ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n\n    <div class=\"form-group\">\n        <div class=\"col-lg-offset-1 col-lg-11\">\n            <?= Html::submitButton('Login', ['class' => 'btn btn-primary']) ?>\n        </div>\n    </div>\n<?php ActiveForm::end() ?>\n```\n\nDans le code précédent, [[yii\\widgets\\ActiveForm::begin()|ActiveForm::begin()]] ne crée pas seulement une instance de formulaire, mais il marque également le début du formulaire. Tout le contenu placé entre [[yii\\widgets\\ActiveForm::begin()|ActiveForm::begin()]] et [[yii\\widgets\\ActiveForm::end()|ActiveForm::end()]] sera enveloppé dans la balise HTML `<form>`. Comme avec tout composant graphique, vous pouvez spécifier quelques options sur la façon dont le composant graphique est configuré en passant un tableau à la méthode `begin`. Dans ce cas précis, une classe CSS supplémentaire et un identifiant sont passés pour être utilisés dans l'ouverture de balise `<form>`. Pour connaître toutes les options disponibles, reportez-vous à la documentation de l'API de [[yii\\widgets\\ActiveForm]].\n\nAfin de créer un élément *form* dans le formulaire, avec l'élément *label* et toute validation JavaScript applicable, la méthode [[yii\\widgets\\ActiveForm::field()|ActiveForm::field()]] est appelée. Elle retourne une instance de [[yii\\widgets\\ActiveField]]. Lorsque le résultat de cette méthode est renvoyé en écho directement, le résultat est un champ de saisie de texte régulier. Pour personnaliser la sortie, vous pouvez enchaîner des méthodes additionnelles de [[yii\\widgets\\ActiveField|ActiveField]] à cet appel :\n\n```php\n// un champ de saisie du mot de passe\n<?= $form->field($model, 'password')->passwordInput() ?>\n// ajoute une invite et une étiquette personnalisée\n<?= $form->field($model, 'username')->textInput()->hint('Please enter your name')->label('Name') ?>\n// crée un élément HTML5 de saisie d'une adresse de courriel\n<?= $form->field($model, 'email')->input('email') ?>\n```\n\nCela crée toutes les balises `<label>`, `<input>` et autres, selon le [[yii\\widgets\\ActiveField::$template|modèle]] défini par le champ de formulaire. Le nom du champ de saisie est déterminé automatiquement à partir du [[yii\\base\\Model::formName()|nom de formulaire]] du modèle et du nom d'attribut. Par exemple, le nom du champ de saisie de l'attribut `username` dans l'exemple ci-dessus est `LoginForm[username]`. Cette règle de nommage aboutit à un tableau de tous les attributs du formulaire de connexion dans `$_POST['LoginForm']` côté serveur.\n\n> Tip: si vous avez seulement un modèle dans un formulaire et que vous voulez simplifier le nom des champs de saisie, vous pouvez sauter la partie tableau en redéfinissant la méthode [[yii\\base\\Model::formName()|formName()]] du modèle pour qu'elle retourne une chaîne vide. Cela peut s'avérer utile pour les modèles de filtres utilisés dans le composant graphique [GridView](output-data-widgets.md#grid-view) pour créer des URL plus élégantes.\n\nSpécifier l'attribut de modèle peut se faire de façon plus sophistiquée. Par exemple, lorsqu'un attribut peut prendre une valeur de tableau lors du chargement sur le serveur de multiples fichiers ou lors de la sélection de multiples items, vous pouvez le spécifier en ajoutant `[]` au nom d'attribut : \n\n```php\n// permet à de multiples fichiers d'être chargés sur le serveur :\necho $form->field($model, 'uploadFile[]')->fileInput(['multiple'=>'multiple']);\n\n// permet à de multiples items d'être cochés :\necho $form->field($model, 'items[]')->checkboxList(['a' => 'Item A', 'b' => 'Item B', 'c' => 'Item C']);\n```\n\nSoyez prudent lorsque vous nommez des éléments de formulaire tels que des boutons de soumission. Selon la [documentation de jQuery](https://api.jquery.com/submit/), certains noms sont réservés car ils peuvent créer des conflits :\n\n> Les éléments *forms* et leurs éléments enfants ne devraient par utiliser des noms de champ de saisie, ou des identifiants que entrent en conflit avec les propriétés d'un élément de *form*, tels que `submit`, `length`, ou `method`. Les conflits de noms peuvent créer des échecs troublants. Pour une liste complètes des règles et pour vérifier votre code HTML à propos de ces problèmes, reportez-vous à [DOMLint](https://kangax.github.io/domlint/).\n\nDes balises additionnelles HTML peuvent être ajoutées au formulaire en utilisant du HTML simple ou en utilisant les méthodes de la classe [[yii\\helpers\\Html|Html]]-helper comme cela est fait dans l'exemple ci-dessus avec le [[yii\\helpers\\Html::submitButton()|bouton de soumission]].\n\n\n> Tip: si vous utilisez la base structurée *Twitter Bootstrap CSS* dans votre application, vous désirez peut-être utiliser [[yii\\bootstrap\\ActiveForm]] à la place de [[yii\\widgets\\ActiveForm]]. La première étend la deuxième et utilise les styles propres à Bootstrap lors de la génération des champs de saisie du formulaire.\n\n\n> Tip: afin de styler les champs requis avec une astérisque, vous pouvez utiliser le CSS suivant :\n>\n> ```css\n> div.required label.control-label:after {\n>     content: \" *\";\n>     color: red;\n> }\n> ```\n\nCréation d'une liste déroulante <span id=\"creating-activeform-dropdownlist\"></span>\n-------------------------------\n\nVous pouvez utiliser la méthode [dropDownList()](https://www.yiiframework.com/doc-2.0/yii-widgets-activefield.html#dropDownList()-detail) de ActiveForm pour créer une liste déroulante :\n\n\n```php\nuse app\\models\\ProductCategory;\n\n/**\n * @var \\yii\\web\\View $this\n * @var \\yii\\widgets\\ActiveForm $form\n * @var \\app\\models\\Product $model\n */\n\necho $form->field($model, 'product_category')->dropdownList(\n    ProductCategory::find()->select(['category_name', 'id'])->indexBy('id')->column(),\n    ['prompt'=>'Select Category']\n);\n```\n\nLa valeur du champ de saisie de votre modèle est automatiquement pré-selectionnée \n\nTravail avec Pjax <span id=\"working-with-pjax\"></span>\n-----------------------\n\nLe composant graphique [[yii\\widgets\\Pjax|Pjax]] vous permet de mettre à jour une certaine section d'une page plutôt que de recharger la page entière. Vous pouvez l'utiliser pour mettre à jour seulement le formulaire et remplacer son contenu après la soumission.\n\nVous pouvez configurer [[yii\\widgets\\Pjax::$formSelector|$formSelector]] pour spécifier quelles soumissions de formulaire peuvent déclencher pjax. Si cette propriété n'est pas définie, tous les formulaires avec l'attribut `data-pjax` dans le contenu englobé par Pjax déclenchent des requêtes pjax. \n\n```php\nuse yii\\widgets\\Pjax;\nuse yii\\widgets\\ActiveForm;\n\nPjax::begin([\n    // Pjax options\n]);\n    $form = ActiveForm::begin([\n        'options' => ['data' => ['pjax' => true]],\n        // plus d'options d'ActiveForm\n    ]);\n\n        // contenu de ActiveForm\n\n    ActiveForm::end();\nPjax::end();\n```\n> Tip: soyez prudent avec les liens à l'intérieur du composant graphique [[yii\\widgets\\Pjax|Pjax]] car la réponse est également rendue dans le composant graphique. Pour éviter cela, utilisez l'attribut HTML `data-pjax=\"0\"`.\n\n#### Valeurs dans les boutons de soumission et dans les chargement de fichiers sur le serveur\n\nIl y a des problèmes connus avec l'utilisation de `jQuery.serializeArray()` lorsqu'on manipule des [fichiers](https://github.com/jquery/jquery/issues/2321) et des [valeurs de boutons de soumission](https://github.com/jquery/jquery/issues/2321) qui ne peuvent être résolus et sont plutôt rendus obsolète en faveur de la classe `FormData` introduite en HTML5. \n\nCela siginifie que la seule prise en charge officielle pour les fichiers et les valeurs de boutons de soumission avec ajax, ou en utilisant le composant graphique  [[yii\\widgets\\Pjax|Pjax]], dépend de la [prise en charge par le navigateur](https://developer.mozilla.org/fr/docs/Web/API/FormData#compatibilit%C3%A9_des_navigateurs) de la classe `FormData`.\n\nLectures d'approfondissement <span id=\"further-reading\"></span>\n----------------------------\n\nLa section suivante, [Validation des entrées](input-validation.md) prend en charge la validation des données soumises par le formulaire du côté serveur ainsi que la validation ajax et du côté client. \n\nPour en apprendre plus sur les utilisations complexes de formulaires, vous pouvez lire les sections suivantes :\n\n- [Collecte des champs de saisie tabulaires](input-tabular-input.md), pour collecter des données à destination de multiples modèles du même genre.\n- [Obtention de données pour de multiples modèles](input-multiple-models.md), pour manipuler plusieurs modèles différents dans le même formulaire.\n- [Chargement de fichiers sur le serveur](input-file-upload.md), sur la manière d'utiliser les formulaires pour charger des fichiers sur le serveur.\n"
  },
  {
    "path": "docs/guide-fr/input-multiple-models.md",
    "content": "Obtenir des données pour plusieurs modèles\n==========================================\n\nLorsque vous avez affaire à des données complexes, il est possible que vous ayez besoin d'utiliser plusieurs modèles différents pour collecter des saisies de l'utilisateur. Par exemple, en supposant que les informations de connexion de l'utilisateur sont stockées dans la table `user` tandis que les informations de son profil sont stockées dans la table `profil`, vous désirez peut-être collecter les données de l'utilisateur via un modèle `User` et un modèle `Profile`. Avec la prise en charge par Yii des modèles et des formulaires, vous pouvez résoudre ce problème d'une façon qui ne diffère qu'assez peu de celle consistant à utiliser un modèle unique. \n\nDans ce qui suit, nous montrons comment créer un formulaire que permet la collecte de données pour les deux modèles, `User` et `Profile`, à la fois.\n\nTout d'abord, l'action de contrôleur pour la collecte des données de connexion (user) et des données de profil, peut être écrite comme suit :\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\base\\Model;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\nuse app\\models\\User;\nuse app\\models\\Profile;\n\nclass UserController extends Controller\n{\n    public function actionUpdate($id)\n    {\n        $user = User::findOne($id);\n        if (!$user) {\n            throw new NotFoundHttpException(\"The user was not found.\");\n        }\n        \n        $profile = Profile::findOne($user->profile_id);\n        \n        if (!$profile) {\n            throw new NotFoundHttpException(\"The user has no profile.\");\n        }\n        \n        $user->scenario = 'update';\n        $profile->scenario = 'update';\n        \n        if ($user->load(Yii::$app->request->post()) && $profile->load(Yii::$app->request->post())) {\n            $isValid = $user->validate();\n            $isValid = $profile->validate() && $isValid;\n            if ($isValid) {\n                $user->save(false);\n                $profile->save(false);\n                return $this->redirect(['user/view', 'id' => $id]);\n            }\n        }\n        \n        return $this->render('update', [\n            'user' => $user,\n            'profile' => $profile,\n        ]);\n    }\n}\n```\n\nDans l'action `update`, nous commençons par charger les données des modèles, `$user` et `$profile`, à mettre à jour dans la base de données. Puis nous appelons [[yii\\base\\Model::load()]] pour remplir les deux modèles avec les entrées de l'utilisateur. Si tout se passe bien, nous validons les deux modèles et les sauvegardons. Autrement, nous rendons la vue `update` avec le contenu suivant :\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n$form = ActiveForm::begin([\n    'id' => 'user-update-form',\n    'options' => ['class' => 'form-horizontal'],\n]) ?>\n    <?= $form->field($user, 'username') ?>\n\n    ...autres champs de saisie...\n    \n    <?= $form->field($profile, 'website') ?>\n\n    <?= Html::submitButton('Update', ['class' => 'btn btn-primary']) ?>\n<?php ActiveForm::end() ?>\n```\n\nComme vous le voyez, la vue `update` rend les champs de saisie de deux modèles `$user` et `$profile`.\n"
  },
  {
    "path": "docs/guide-fr/input-tabular-input.md",
    "content": "Collecte d'entrées tabulaires\n=============================\n\nIl arrive parfois que vous ayez besoin de manipuler plusieurs modèles de même sorte dans un formulaire unique. Par exemple, de multiples réglages où chacun des réglages est stocké sous forme de paire nom-valeur et est représenté par un modèle d'[enregistrement actif](db-active-record.md) `Setting`. Cette sorte de formulaire est aussi appelé « entrées tabulaires ». Par opposition à cela, la manipulation des modèles de différente sortes, est traitée dans la section [Formulaires complexes avec plusieurs modèles](input-multiple-models.md).\n\nCe qui suit montre comment mettre en œuvre les entrées tabulaires avec Yii. \n\nIl y a trois situations différentes à couvrir, qui doivent être traitées avec de légères différences :\n- La mise à jour d'un jeu fixe d'enregistrement de la base de données.\n- La création d'un jeu dynamique d'enregistrements.\n- La mise à jour, création et suppression d'enregistrements sur une page.\n\nPar contraste avec les formulaires de modèle unique expliqué précédemment, nous travaillons maintenant sur un tableau de modèles. Ce tableau est passé à la vue pour afficher les champs de saisie de chacun des modèles sous une forme ressemblant à un tableau. Nous allons utiliser les méthodes d'aide de [[yii\\base\\Model]] qui nous permettent le chargement et la validation de plusieurs modèles à la fois :\n\n- [[yii\\base\\Model::loadMultiple()|Model::loadMultiple()]] charge les données d'une requête `POST` dans un tableau de modèles.\n- [[yii\\base\\Model::validateMultiple()|Model::validateMultiple()]] valide un tableau de modèles.\n\n### Mise à jour d'un jeu fixe d'enregistrements\n\nCommençons par l'action du contrôleur :\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\base\\Model;\nuse yii\\web\\Controller;\nuse app\\models\\Setting;\n\nclass SettingsController extends Controller\n{\n    // ...\n\n    public function actionUpdate()\n    {\n        $settings = Setting::find()->indexBy('id')->all();\n\n        if (Model::loadMultiple($settings, Yii::$app->request->post()) && Model::validateMultiple($settings)) {\n            foreach ($settings as $setting) {\n                $setting->save(false);\n            }\n            return $this->redirect('index');\n        }\n\n        return $this->render('update', ['settings' => $settings]);\n    }\n}\n```\n\nDans le code ci-dessus, nous utilisons [[yii\\db\\ActiveQuery::indexBy()|indexBy()]] lors de l'extraction de modèles depuis la base de données pour remplir un tableau indexé par les clés primaires des modèles. Celles-ci seront utilisées plus tard pour identifier les champs de formulaires. [[yii\\base\\Model::loadMultiple()|Model::loadMultiple()]] remplit de multiples modèles avec les données du formulaire issues de la méthode `POST` et [[yii\\base\\Model::validateMultiple()|Model::validateMultiple()]] valide tous les modèles en une seule fois. Comme nous avons validé auparavant, nous passons maintenant `false` en paramètre à [[yii\\db\\ActiveRecord::save()|save()]] pour ne pas exécuter la validation deux fois. \n\nMaintenant, voyons le formulaire qui se trouve dans la vue `update` (mise à jour) :\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n$form = ActiveForm::begin();\n\nforeach ($settings as $index => $setting) {\n    echo $form->field($setting, \"[$index]value\")->label($setting->name);\n}\n\nActiveForm::end();\n```\n\nIci, pour chaque réglage, nous rendons un champ de saisie avec un nom indexé. Il est important d'ajouter un index approprié au nom d'un champ de saisie car c'est avec cela que [[yii\\base\\Model::loadMultiple()|Model::loadMultiple()]] détermine à quel modèle attribuer telles valeurs. \n\n### Création d'un jeu dynamique d'enregistrements\n\nLa création d'enregistrements est similaire à leur mise à jour, sauf que nous avons a instancier les modèles :\n\n```php\npublic function actionCreate()\n{\n    $count = count(Yii::$app->request->post('Setting', []));\n    $settings = [new Setting()];\n    for($i = 1; $i < $count; $i++) {\n        $settings[] = new Setting();\n    }\n\n    // ...\n}\n```\n\nIci nous créons un tableau initial de `$settings` (réglages) contenant un modèle par défaut de façon à ce qu'au moins un champ de texte soit visible dans la vue. De plus, nous ajoutons un modèle pour chacune des lignes d'entrée que nous recevons.\n\nDans la vue, nous pouvons utiliser JavaScript pour ajouter de nouvelles lignes dynamiquement. \n\n### Combinaison, mise à jour, création et suppression sur une page \n\n> Note: Cette section est en cours de création\n>\n> Elle est vide pour le moment.\n\nTBD\n"
  },
  {
    "path": "docs/guide-fr/input-validation.md",
    "content": "Validation des entrées utilisateur\n==================================\n\nEn général, vous ne devriez jamais faire confiance aux données entrées par l'utilisateur et devriez toujours les valider avant de les utiliser.,\n\nÉtant donné un [modèle](structure-models.md) rempli par les données entrées par l'utilisateur, il est possible de valider ces entrées en appelant la méthode [[yii\\base\\Model::validate()]]. La méthode retourne une valeur booléenne qui indique si la validation a réussi ou pas. Si ce n'est pas le cas, vous pouvez obtenir les messages d'erreur depuis la propriété [[yii\\base\\Model::errors]]. Par exemple :\n\n```php\n$model = new \\app\\models\\ContactForm();\n\n// remplit les attributs du modèle avec les entrées de l'utilisateur\n$model->load(\\Yii::$app->request->post());\n// ce qui est équivalent à :\n// $model->attributes = \\Yii::$app->request->post('ContactForm');\n\nif ($model->validate()) {\n    // toutes les entrées sont valides\n} else {\n    // la validation a échoué: $errors est un tableau contenant les messages d'erreur\n    $errors = $model->errors;\n}\n```\n\n\n## Déclaration de règles <span id=\"declaring-rules\"></span>\n\nPour que `validate()` fonctionne réellement, vous devez déclarer des règles de validation pour les attributs que vous envisagez de valider. Cela peut être réalisé en redéfinissant la méthode [[yii\\base\\Model::rules()]]. L'exemple suivant montre comment les règles de validation pour le modèle `ContactForm` sont déclarées :\n\n```php\npublic function rules()\n{\n    return [\n        // les attributs  name, email, subject et  body sont à saisir obligatoirement\n        [['name', 'email', 'subject', 'body'], 'required'],\n\n        // l'attribut email doit être une adresse de courriel valide\n        ['email', 'email'],\n    ];\n}\n```\n\nLa méthode [[yii\\base\\Model::rules()|rules()]] doit retourner un tableau de règles, dont chacune est un tableau dans le format suivant :\n\n\n```php\n[\n    // obligatoire, spécifie quels attributs doivent être validés par cette règle.\n    // Pour un attribut unique, vous pouvez utiliser le nom de l'attribut directement\n    // sans le mettre dans un tableau\n    ['attribute1', 'attribute2', ...],\n\n    // obligatoire, spécifier le type de cette règle.\n    // Il peut s'agir d'un nom de classe, d'un alias de validateur ou du nom d'une méthode de validation\n    'validator',\n\n    // facultatif, spécifie dans quel(s) scénario(s) cette règle doit être appliquée\n    // si absent, cela signifie que la règle s'applique à tous les scénarios\n    // Vous pouvez aussi configurer l'option \"except\" si vous voulez que la règle\n    // s'applique à tous les scénarios sauf à ceux qui sont listés\n    'on' => ['scenario1', 'scenario2', ...],\n\n    // facultatif, spécifie des configurations additionnelles pour l'objet validateur\n    'property1' => 'value1', 'property2' => 'value2', ...\n]\n```\n\nPour chacune des règles vous devez spécifier au moins à quels attributs la règle s'applique et quel est le type de cette règle. Vous pouvez spécifier le type de la règle sous l'une des formes suivantes :\n\n* l'alias d'un validateur du noyau, comme `required`, `in`, `date`, etc. Reportez-vous à la sous-section [Validateurs du noyau](tutorial-core-validators.md) pour une liste complète des validateurs du noyau.\n* le nom d'une méthode de validation dans la classe du modèle, ou une fonction anonyme. Reportez-vous à la sous-section [Inline Validators](#inline-validators) pour plus de détails.\n* un nom de classe de validateur pleinement qualifié. Reportez-vous à la sous-section [Validateurs autonomes](#standalone-validators) pour plus de détails.\n\nUne règle peut être utilisée pour valider un ou plusieurs attributs, et un attribut peut être validé par une ou plusieurs règles. Une règle peut s'appliquer dans certains [scenarios](structure-models.md#scenarios) seulement en spécifiant l'option `on`. Si vous ne spécifiez pas l'option `on`, la règle s'applique à tous les scénarios.\n\nQuand la méthode `validate()` est appelée, elle suit les étapes suivantes pour effectuer l'examen de validation :\n\n1. Détermine quels attributs doivent être validés en obtenant la liste des attributs de [[yii\\base\\Model::scenarios()]] en utilisant le [[yii\\base\\Model::scenario|scenario]] courant. Ces attributs sont appelés *attributs actifs*.\n2. Détermine quelles règles de validation doivent être appliquées en obtenant la liste des règles de [[yii\\base\\Model::rules()]] en utilisant le [[yii\\base\\Model::scenario|scenario]] courant. Ces règles sont appelées *règles actives*.\n3. Utilise chacune des règles actives pour valider chacun des attributs qui sont associés à cette règle. Les règles sont évaluées dans l'ordre dans lequel elles sont listées.\n\nSelon les étapes de validation décrites ci-dessus, un attribut est validé si, et seulement si, il est un attribut actif déclaré dans `scenarios()` et est associé à une ou plusieurs règles actives déclarées dans `rules()`.\n\n> Note: il est pratique le nommer les règles, c.-à-d.\n>\n> ```php\n> public function rules()\n> {\n>     return [\n>         // ...\n>         'password' => [['password'], 'string', 'max' => 60],\n>     ];\n> }\n> ```\n>\n> Vous pouvez l'utiliser dans un modèle enfant :\n>\n> ```php\n> public function rules()\n> {\n>     $rules = parent::rules();\n>     unset($rules['password']);\n>     return $rules;\n> }\n\n\n### Personnalisation des messages d'erreur <span id=\"customizing-error-messages\"></span>\n\nLa plupart des validateurs possèdent des messages d'erreurs qui sont ajoutés au modèle en cours de validation lorsque ses attributs ne passent pas la validation. Par exemple, le validateur [[yii\\validators\\RequiredValidator|required]] ajoute le message \"Username cannot be blank.\" (Le nom d'utilisateur ne peut être vide.) au modèle lorsque l'attribut `username` ne passe pas la règle de validation utilisant ce validateur.\n\nVous pouvez personnaliser le message d'erreur d'une règle en spécifiant la propriété `message` lors de la déclaration de la règle, comme ceci :\n\n```php\npublic function rules()\n{\n    return [\n        ['username', 'required', 'message' => 'Please choose a username.'],\n    ];\n}\n```\n\nQuelques validateurs peuvent prendre en charge des messages d'erreur additionnels pour décrire précisément les différentes causes de non validation. Par exemple, le validateur [[yii\\validators\\NumberValidator|number]] prend en charge[[yii\\validators\\NumberValidator::tooBig|tooBig (trop grand)]] et [[yii\\validators\\NumberValidator::tooSmall|tooSmall (trop petit)]] pour décrire la cause de non validation lorsque la valeur à valider est trop grande ou trop petite, respectivement. Vous pouvez configurer ces messages d'erreur comme vous configureriez d'autres propriétés de validateurs dans une règle de validation.\n\n\n### Événement de validation <span id=\"validation-events\"></span>\n\nLosque la méthode [[yii\\base\\Model::validate()]] est appelée, elle appelle deux méthodes que vous pouvez redéfinir pour personnaliser le processus de validation :\n\n* [[yii\\base\\Model::beforeValidate()]]: la mise en œuvre par défaut déclenche un événement [[yii\\base\\Model::EVENT_BEFORE_VALIDATE]]. Vous pouvez, soit redéfinir cette méthode, soit répondre à cet événement pour accomplir un travail de pré-traitement (p. ex. normaliser les données entrées) avant que l'examen de validation n'ait lieu. La méthode retourne une valeur booléenne indiquant si l'examen de validation doit avoir lieu ou pas.\n\n* [[yii\\base\\Model::afterValidate()]]: la mise en œuvre par défaut déclenche un événement [[yii\\base\\Model::EVENT_AFTER_VALIDATE]]. Vous pouvez, soit redéfinir cette méthode, soit répondre à cet événement pour accomplir un travail de post-traitement après que l'examen de validation a eu lieu.\n\n### Validation conditionnelle <span id=\"conditional-validation\"></span>\n\nPour valider des attributs seulement lorsque certaines conditions sont réalisées, p. ex. la validation d'un attribut dépend de la valeur d'un autre attribut, vous pouvez utiliser la propriété [[yii\\validators\\Validator::when|when]] pour définir de telles conditions. Par exemple :\n\n```php\n    ['state', 'required', 'when' => function($model) {\n        return $model->country == 'USA';\n    }]\n```\n\nLa propriété [[yii\\validators\\Validator::when|when]] accepte une fonction de rappel PHP avec la signature suivante :\n\n```php\n/**\n * @param Model $model le modèle en cours de validation\n * @param string $attribute l'attribut en cours de validation\n * @return bool `true` si la règle doit être appliqué, `false` si non\n */\nfunction ($model, $attribute)\n```\n\nSi vous avez aussi besoin de la prise en charge côté client de la validation conditionnelle, vous devez configurer la propriété [[yii\\validators\\Validator::whenClient|whenClient]] qui accepte une chaîne de caractères représentant une fonction JavaScript dont la valeur de retour détermine si la règles doit être appliquée ou pas. Par exemple :\n\n```php\n    ['state', 'required', 'when' => function ($model) {\n        return $model->country == 'USA';\n    }, 'whenClient' => \"function (attribute, value) {\n        return $('#country').val() == 'USA';\n    }\"]\n```\n\n\n### Filtrage des données <span id=\"data-filtering\"></span>\n\nLes entrées utilisateur nécessitent souvent d'être filtrées ou pré-traitées. Par exemple, vous désirez peut-être vous débarrasser des espaces devant et derrière l'entrée `username`. Vous pouvez utiliser les règles de validation pour le faire.\n\nLes exemples suivants montrent comment se débarrasser des espaces dans les entrées et transformer des entrées vides en `nulls` en utilisant les validateurs du noyau [trim](tutorial-core-validators.md#trim) et [default](tutorial-core-validators.md#default) :\n\n```php\nreturn [\n    [['username', 'email'], 'trim'],\n    [['username', 'email'], 'default'],\n];\n```\n\nVous pouvez également utiliser le validateur plus général [filter](tutorial-core-validators.md#filter) pour accomplir un filtrage plus complexe des données.\n\nComme vous le voyez, ces règles de validation ne pratiquent pas un examen de validation proprement dit. Plus exactement, elles traitent les valeurs et les sauvegardent dans les attributs en cours de validation.\n\n\n### Gestion des entrées vides <span id=\"handling-empty-inputs\"></span>\n\nLorsque les entrées sont soumises par des formulaires HTML, vous devez souvent assigner des valeurs par défaut aux entrées si elles restent vides. Vous pouvez le faire en utilisant le validateur [default](tutorial-core-validators.md#default). Par exemple :\n\n```php\nreturn [\n    // définit \"username\" et \"email\" comme *null* si elles sont vides\n    [['username', 'email'], 'default'],\n\n    // définit \"level\" à 1 si elle est vide\n    ['level', 'default', 'value' => 1],\n];\n```\n\nPar défaut, une entrée est considérée vide si sa valeur est une chaîne de caractères vide, un tableau vide ou un `null`. Vous pouvez personnaliser la logique de détection de vide en configurant la propriété [[yii\\validators\\Validator::isEmpty]] avec une fonction de rappel PHP. Par exemple :\n\n```php\n    ['agree', 'required', 'isEmpty' => function ($value) {\n        return empty($value);\n    }]\n```\n\n> Note: la plupart des validateurs ne traitent pas les entrées vides si leur propriété [[yii\\validators\\Validator::skipOnEmpty]] prend la valeur par défaut `true` (vrai). Ils sont simplement sautés lors de l'examen de validation si leurs attributs associés reçoivent des entrées vides. Parmi les [validateurs de noyau](tutorial-core-validators.md), seuls les validateurs `captcha`, `default`, `filter`, `required`, et `trim` traitent les entrées vides.\n\n\n## Validation ad hoc <span id=\"ad-hoc-validation\"></span>\n\nParfois vous avez besoin de faire une *validation ad hoc* pour des valeurs qui ne sont pas liées à un modèle.\n\nSi vous n'avez besoin d'effectuer qu'un seul type de validation (p. ex. valider une adresse de courriel), vous pouvez appeler la méthode [[yii\\validators\\Validator::validate()|validate()]] du validateur désiré, comme ceci :\n\n```php\n$email = 'test@example.com';\n$validator = new yii\\validators\\EmailValidator();\n\nif ($validator->validate($email, $error)) {\n    echo 'Email is valid.';\n} else {\n    echo $error;\n}\n```\n\n> Note: tous les validateurs ne prennent pas en charge ce type de validation. Le validateur du noyau [unique](tutorial-core-validators.md#unique), qui est conçu pour travailler avec un modèle uniquement, en est un exemple.\n\nSi vous avez besoin de validations multiples pour plusieurs valeurs, vous pouvez utiliser [[yii\\base\\DynamicModel]] qui prend en charge, à la fois les attributs et les règles à la volée. Son utilisation ressemble à ce qui suit :\n\n```php\npublic function actionSearch($name, $email)\n{\n    $model = DynamicModel::validateData(compact('name', 'email'), [\n        [['name', 'email'], 'string', 'max' => 128],\n        ['email', 'email'],\n    ]);\n\n    if ($model->hasErrors()) {\n        // validation fails\n    } else {\n        // validation succeeds\n    }\n}\n```\n\nLa méthode [[yii\\base\\DynamicModel::validateData()]] crée une instance de `DynamicModel`, définit les attributs utilisant les données fournies (`name` et `email` dans cet exemple), puis appelle [[yii\\base\\Model::validate()]] avec les règles données.\n\nEn alternative, vous pouvez utiliser la syntaxe plus *classique* suivante pour effectuer la validation ad hoc :\n\n```php\npublic function actionSearch($name, $email)\n{\n    $model = new DynamicModel(compact('name', 'email'));\n    $model->addRule(['name', 'email'], 'string', ['max' => 128])\n        ->addRule('email', 'email')\n        ->validate();\n\n    if ($model->hasErrors()) {\n        // la validation a échoué\n    } else {\n        // la validation a réussi\n    }\n}\n```\n\nAprès l'examen de validation, vous pouvez vérifier si la validation a réussi ou pas en appelant la méthode [[yii\\base\\DynamicModel::hasErrors()|hasErrors()]] et obtenir les erreurs de validation de la propriété [[yii\\base\\DynamicModel::errors|errors]], comme vous le feriez avec un modèle normal. Vous pouvez aussi accéder aux attributs dynamiques définis via l'instance de modèle, p. ex. `$model->name` et `$model->email`.\n\n\n## Création de validateurs <span id=\"creating-validators\"></span>\n\nEn plus de pouvoir utiliser les [validateurs du noyau](tutorial-core-validators.md) inclus dans les versions publiées de Yii, vous pouvez également créer vos propres validateurs. Vous pouvez créer des validateurs en ligne et des validateurs autonomes.\n\n\n### Validateurs en ligne <span id=\"inline-validators\"></span>\n\nUn validateur en ligne est un validateur défini sous forme de méthode de modèle ou de fonction anonyme. La signature de la méthode/fonction est :\n\n```php\n/**\n * @param string $attribute l'attribut en cours de validation\n * @param mixed $params la valeur des *paramètres* donnés dans la règle\n */\nfunction ($attribute, $params)\n```\n\nSi un attribut ne réussit pas l'examen de validation, la méthode/fonction doit appeler [[yii\\base\\Model::addError()]] pour sauvegarder le message d'erreur dans le modèle de manière à ce qu'il puisse être retrouvé plus tard pour être présenté à l'utilisateur.\n\nVoici quelques exemples :\n\n```php\nuse yii\\base\\Model;\n\nclass MyForm extends Model\n{\n    public $country;\n    public $token;\n\n    public function rules()\n    {\n        return [\n            // un validateur en ligne défini sous forme de méthode de modèle validateCountry()\n            ['country', 'validateCountry'],\n\n            // un validateur en ligne défini sous forme de fonction anonyme\n            ['token', function ($attribute, $params) {\n                if (!ctype_alnum($this->$attribute)) {\n                    $this->addError($attribute, 'The token must contain letters or digits.');\n                }\n            }],\n        ];\n    }\n\n    public function validateCountry($attribute, $params)\n    {\n        if (!in_array($this->$attribute, ['USA', 'Web'])) {\n            $this->addError($attribute, 'The country must be either \"USA\" or \"Web\".');\n        }\n    }\n}\n```\n\n> Note: Par défaut, les validateurs en ligne ne sont pas appliqués si leurs attributs associés reçoivent des entrées vides ou s'ils ont déjà échoué à des examen de validation selon certaines règles. Si vous voulez être sûr qu'une règle sera toujours appliquée, vous devez configurer les propriétés [[yii\\validators\\Validator::skipOnEmpty|skipOnEmpty]] et/ou [[yii\\validators\\Validator::skipOnError|skipOnError]] à `false` (faux) dans les déclarations des règles. Par exemple :\n>\n> ```php\n> [\n>     ['country', 'validateCountry', 'skipOnEmpty' => false, 'skipOnError' => false],\n> ]\n> ```\n\n\n### Validateurs autonomes <span id=\"standalone-validators\"></span>\n\nUn validateur autonome est une classe qui étend la classe [[yii\\validators\\Validator]] ou une de ses classe filles. Vous pouvez mettre en œuvre sa logique de validation en redéfinissant la méthode [[yii\\validators\\Validator::validateAttribute()]]. Si un attribut ne réussit pas l'exament de validation, appellez [[yii\\base\\Model::addError()]] pour sauvegarder le message d'erreur dans le modèle, comme vous le feriez avec des [validateurs en ligne](#inline-validators).\n\n\nPar exemple, le validateur en ligne ci-dessus peut être transformé en une nouvelle classe [[components/validators/CountryValidator]].\n\n```php\nnamespace app\\components;\n\nuse yii\\validators\\Validator;\n\nclass CountryValidator extends Validator\n{\n    public function validateAttribute($model, $attribute)\n    {\n        if (!in_array($model->$attribute, ['USA', 'Web'])) {\n            $this->addError($model, $attribute, 'The country must be either \"USA\" or \"Web\".');\n        }\n    }\n}\n```\n\nSi vous voulez que votre validateur prennent en charge la validation d'une valeur sans modèle, vous devez redéfinir la méthode [[yii\\validators\\Validator::validate()]]. Vous pouvez aussi redéfinir [[yii\\validators\\Validator::validateValue()]] au lieu de `validateAttribute()` et `validate()`, parce que, par défaut, les deux dernières méthodes sont appelées en appelant `validateValue()`.\n\nCi-dessous, nous présentons un exemple de comment utiliser la classe de validateur précédente dans votre modèle.\n\n```php\nnamespace app\\models;\n\nuse Yii;\nuse yii\\base\\Model;\nuse app\\components\\validators\\CountryValidator;\n\nclass EntryForm extends Model\n{\n    public $name;\n    public $email;\n    public $country;\n\n    public function rules()\n    {\n        return [\n            [['name', 'email'], 'required'],\n            ['country', CountryValidator::class],\n            ['email', 'email'],\n        ];\n    }\n}\n```\n\n\n## Validation côté client <span id=\"client-side-validation\"></span>\n\nLa validation côté client basée sur JavaScript est souhaitable lorsque l'utilisateur fournit les entrées via des formulaires HTML, parce que cela permet à l'utilisateur de détecter plus vite les erreurs et lui apporte ainsi un meilleur ressenti. Vous pouvez utiliser ou implémenter un validateur qui prend en charge la validation côté client *en plus* de la validation côté serveur.\n\n> Info: bien que la validation côté client soit souhaitable, ce n'est pas une obligation. Son but principal est d'apporter un meilleur ressenti à l'utilisateur. Comme pour les données venant de l'utilisateur, vous ne devriez jamais faire confiance à la validation côté client. Pour cette raison, vous devez toujours effectuer la validation côté serveur en appelant [[yii\\base\\Model::validate()]], comme nous l'avons décrit dans les sous-sections précédentes.\n\n### Utilisation de la validation côté client <span id=\"using-client-side-validation\"></span>\n\nBeaucoup de  [validateurs du noyau](tutorial-core-validators.md) prennent en charge la validation côté client directement. Tout ce que vous avez à faire c'est utiliser [[yii\\widgets\\ActiveForm]] pour construire vos formulaires HTML. Par exemple, `LoginForm` ci-dessous déclare deux règles : l'une utilise le validateur du noyau [required](tutorial-core-validators.md#required) qui est pris en charge à la fois côté serveur et côté client ; l'autre utilise le validateur en ligne `validatePassword` qui ne prend pas en charge la validation côté client.\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\nuse app\\models\\User;\n\nclass LoginForm extends Model\n{\n    public $username;\n    public $password;\n\n    public function rules()\n    {\n        return [\n            // username et password sont tous deux obligatoires\n            [['username', 'password'], 'required'],\n\n            // password est validé par validatePassword()\n            ['password', 'validatePassword'],\n        ];\n    }\n\n    public function validatePassword()\n    {\n        $user = User::findByUsername($this->username);\n\n        if (!$user || !$user->validatePassword($this->password)) {\n            $this->addError('password', 'Incorrect username or password.');\n        }\n    }\n}\n```\n\nLe formulaire HTML construit par le code suivant contient deux champs de saisie `username` et `password`. Si vous soumettez le formulaire sans rien saisir, vous recevrez directement les messages d'erreur vous demandant d'entrer quelque chose sans qu'aucune communication avec le serveur n'ait lieu.\n\n```php\n<?php $form = yii\\widgets\\ActiveForm::begin(); ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n    <?= Html::submitButton('Login') ?>\n<?php yii\\widgets\\ActiveForm::end(); ?>\n```\n\nEn arrière plan, [[yii\\widgets\\ActiveForm]] lit les règles de validation déclarées dans le modèle et génère le code JavaScript approprié pour la prise en charge de la validation côté client. Lorsqu'un utilisateur modifie la valeur d'un champ de saisie ou soumet le formulaire, le code JavaScript est appelé.\n\nSi vous désirez inhiber la validation côté client complètement, vous pouvez configurer la propriété [[yii\\widgets\\ActiveForm::enableClientValidation]] à `false` (faux). Vous pouvez aussi inhiber la validation côté client pour des champs de saisie individuels en configurant leur propriété [[yii\\widgets\\ActiveField::enableClientValidation]] à `false`. Lorsque `enableClientValidation` est configurée à la fois au niveau du champ et au niveau du formulaire, c'est la première configuration qui prévaut.\n\n\n### Mise en œuvre de la validation côté client <span id=\"implementing-client-side-validation\"></span>\n\nPour créer un validateur qui prend en charge la validation côté client, vous devez implémenter la méthode [[yii\\validators\\Validator::clientValidateAttribute()]] qui retourne un morceau de code JavaScript propre à effectuer l'examen de validation côté client. Dans ce code JavaScript, vous pouvez utiliser les variables prédéfinies suivantes :\n\n- `attribute`: le nom de l'attribut en cours de validation ;\n- `value`: la valeur en cours de validation ;\n- `messages`: un tableau utilisé pour contenir les messages d'erreurs pour l'attribut ;\n- `deferred`: un tableau dans lequel les objets différés peuvent être poussés (explication dans la prochaine sous-section).\n\nDans l'exemple suivant, nous créons un `StatusValidator` qui valide une entrée si elle représente l'identifiant d'une donnée existante ayant un état valide. Le validateur prend en charge à la fois la validation côté serveur et la validation côté client.\n\n```php\nnamespace app\\components;\n\nuse yii\\validators\\Validator;\nuse app\\models\\Status;\n\nclass StatusValidator extends Validator\n{\n    public function init()\n    {\n        parent::init();\n        $this->message = 'Invalid status input.';\n    }\n\n    public function validateAttribute($model, $attribute)\n    {\n        $value = $model->$attribute;\n        if (!Status::find()->where(['id' => $value])->exists()) {\n            $model->addError($attribute, $this->message);\n        }\n    }\n\n    public function clientValidateAttribute($model, $attribute, $view)\n    {\n        $statuses = json_encode(Status::find()->select('id')->asArray()->column());\n        $message = json_encode($this->message, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);\n        return <<<JS\nif ($.inArray(value, $statuses) === -1) {\n    messages.push($message);\n}\nJS;\n    }\n}\n```\n\n> Tip: le code ci-dessus est donné essentiellement pour démontrer comment prendre en charge la validation côté client. En pratique, vous pouvez utiliser le validateur du noyau [in](tutorial-core-validators.md#in) pour arriver au même résultat. Vous pouvez écrire la règle de validation comme suit :\n>\n> ```php\n> [\n>     ['status', 'in', 'range' => Status::find()->select('id')->asArray()->column()],\n> ]\n> ```\n\n> Tip: si vous avez besoin de travailler à la main avec la validation côté client, c.-à-d. ajouter des champs dynamiquement ou effectuer quelque logique d'interface utilisateur, reportez-vous à [Travail avec ActiveForm via JavaScript](https://github.com/samdark/yii2-cookbook/blob/master/book/forms-activeform-js.md) dans le *Cookbook* de Yii 2.0 .\n\n### Validation différée <span id=\"deferred-validation\"></span>\n\nSi vous devez effectuer une validation asynchrone côté client, vous pouvez créer des [objets différés](https://api.jquery.com/category/deferred-object/). Par exemple, pour effectuer une validation AJAX personnalisée, vous pouvez utiliser le code suivant :\n\n```php\npublic function clientValidateAttribute($model, $attribute, $view)\n{\n    return <<<JS\n        deferred.push($.get(\"/check\", {value: value}).done(function(data) {\n            if ('' !== data) {\n                messages.push(data);\n            }\n        }));\nJS;\n}\n```\n\nDans ce qui précède, la variable `deferred` est fournie par Yii, et représente un tableau de d'objets différés. La méthode `$.get()` crée un objet différé qui est poussé dans le tableau `deferred`.\n\nVous pouvez aussi créer explicitement un objet différé et appeler sa méthode `resolve()` lorsque la fonction de rappel asynchrone est activée . L'exemple suivant montre comment valider les dimensions d'une image à charger sur le serveur du côté client.\n\n```php\npublic function clientValidateAttribute($model, $attribute, $view)\n{\n    return <<<JS\n        var def = $.Deferred();\n        var img = new Image();\n        img.onload = function() {\n            if (this.width > 150) {\n                messages.push('Image too wide!!');\n            }\n            def.resolve();\n        }\n        var reader = new FileReader();\n        reader.onloadend = function() {\n            img.src = reader.result;\n        }\n        reader.readAsDataURL(file);\n\n        deferred.push(def);\nJS;\n}\n```\n\n> Note: La méthode `resolve()` doit être appelée après que l'attribut a été validé. Autrement la validation principale du formulaire ne se terminera pas.\n\nPour faire simple, le tableau `deferred` est doté d'une méthode raccourci `add()` qui crée automatiquement un objet différé et l'ajoute au tableau `deferred`. En utilisant cette méthode, vous pouvez simplifier l'exemple ci-dessus comme suit :\n\n```php\npublic function clientValidateAttribute($model, $attribute, $view)\n{\n    return <<<JS\n        deferred.add(function(def) {\n            var img = new Image();\n            img.onload = function() {\n                if (this.width > 150) {\n                    messages.push('Image too wide!!');\n                }\n                def.resolve();\n            }\n            var reader = new FileReader();\n            reader.onloadend = function() {\n                img.src = reader.result;\n            }\n            reader.readAsDataURL(file);\n        });\nJS;\n}\n```\n\n\n### Validation AJAX <span id=\"ajax-validation\"></span>\n\nQuelques validations ne peuvent avoir lieu que côté serveur, parce que seul le serveur dispose des informations nécessaires. Par exemple, pour valider l'unicité d'un nom d'utilisateur, il est nécessaire de consulter la table des utilisateurs côté serveur. Vous pouvez utiliser la validation basée sur AJAX dans ce cas. Elle provoquera une requête AJAX en arrière plan pour exécuter l'examen de validation tout en laissant à l'utilisateur le même ressenti que lors d'une validation côté client normale.\n\nPour activer la validation AJAX pour un unique champ de saisie, configurez la propriété [[yii\\widgets\\ActiveField::enableAjaxValidation|enableAjaxValidation]] de ce champ à `true` et spécifiez un `identifiant` unique de formulaire :\n\n```php\nuse yii\\widgets\\ActiveForm;\n\n$form = ActiveForm::begin([\n    'id' => 'registration-form',\n]);\n\necho $form->field($model, 'username', ['enableAjaxValidation' => true]);\n\n// ...\n\nActiveForm::end();\n```\n\nPour étendre la validation AJAX à tout le formulaire, configurez la propriété [[yii\\widgets\\ActiveForm::enableAjaxValidation|enableAjaxValidation]] à `true` au niveau du formulaire :\n\n```php\n$form = ActiveForm::begin([\n    'id' => 'contact-form',\n    'enableAjaxValidation' => true,\n]);\n```\n\n> Note: lorsque la propriété `enableAjaxValidation` est configurée à la fois au niveau du champ et au niveau du formulaire, la première configuration prévaut.\n\nVous devez aussi préparer le serveur de façon à ce qu'il puisse prendre en charge les requêtes de validation AJAX . Cela peut se faire à l'aide d'un fragment de code comme celui qui suit dans les actions de contrôleur :\n\n```php\nif (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {\n    Yii::$app->response->format = Response::FORMAT_JSON;\n    return ActiveForm::validate($model);\n}\n```\n\nLe code ci-dessus vérifie si la requête courante est une requête AJAX. Si oui, il répond à la requête en exécutant l'examen de validation et en retournant les erreurs au format JSON.\n\n> Info: vous pouvez aussi utiliser la [validation différée](#deferred-validation) pour effectuer une validation AJAX. Néanmoins, la fonctionnalité de validation AJAX décrite ici est plus systématique et nécessite moins de codage.\n\nQuand, à la fois `enableClientValidation` et `enableAjaxValidation` sont définies à  `true`, la requête de validation AJAX est déclenchée seulement après une validation réussie côté client.\n"
  },
  {
    "path": "docs/guide-fr/intro-upgrade-from-v1.md",
    "content": "Mise à jour depuis la version 1.1\n=================================\n\nIl y a beaucoup de différences entre les versions 1.1 et 2.0 de Yii, le framework ayant été complètement réécrit pour\nla 2.0. En conséquence, la mise à jour depuis la version 1.1 n'est pas aussi triviale que la mise à jour entre deux\nversions mineures. Dans ce guide, vous trouverez les principales différences entre les deux versions.\n\nSi vous n'avez pas utilisé Yii 1.1 avant, vous pouvez ignorer cette section et passer directement à la section\n\"[Mise en route](start-installation.md)\".\n\nMerci de noter que Yii 2.0 introduit plus de nouvelles fonctionnalités que celles abordées ici. Il est fortement\nrecommandé de lire tout le guide de référence pour en apprendre davantage. Il y a des chances que certaines\nfonctionnalités, que vous aviez préalablement développées pour vous, fassent désormais partie du code du noyau.\n\n\nInstallation\n------------\n\nYii 2.0 exploite pleinement [Composer](https://getcomposer.org/), le gestionnaire de paquet PHP. L'installation\ndu framework, ainsi que des extensions, sont gérées par Composer. Reportez-vous à la section\n[Installer Yii](start-installation.md) pour apprendre comment installer Yii 2.0. Si vous voulez\ncréer de nouvelles extensions, ou rendre vos extensions existantes 1.1 compatibles 2.0, reportez-vous à\nla section [Créer des extensions](extend-creating-extensions.md) de ce guide.\n\n\nPré-requis PHP\n--------------\n\nYii 2.0 requiert PHP 5.4 ou plus, ce qui est une grosse amélioration par rapport à PHP 5.2 qui était requis pour Yii 1.1.\n\nPar conséquent, il y a beaucoup de différences au niveau du langage auxquelles vous devriez prêter attention.\nVoici un résumé des principaux changements concernant PHP:\n\n- [Espaces de noms](https://www.php.net/manual/fr/language.namespaces.php).\n- [Fonctions anonymes](https://www.php.net/manual/fr/functions.anonymous.php).\n- Syntaxe courte pour les tableaux : `[...éléments...]` est utilisé au lieu de `array(...éléments...)`.\n- Syntaxe courte pour echo : `<?=` est utilisé dans les vues. Cela ne pose aucun problème à partir de PHP 5.4.\n- [Classes SPL et interfaces](https://www.php.net/manual/fr/book.spl.php).\n- [Late Static Bindings (résolution statique à la volée)](https://www.php.net/manual/fr/language.oop5.late-static-bindings.php).\n- [Date et heure](https://www.php.net/manual/fr/book.datetime.php).\n- [Traits](https://www.php.net/manual/fr/language.oop5.traits.php).\n- [intl](https://www.php.net/manual/fr/book.intl.php). Yii 2.0 utilise l'extension PHP `intl` pour les fonctionnalités\n  d'internationalisation.\n\n\nEspaces de noms\n---------------\n\nLe changement le plus évident dans Yii 2.0 est l'utilisation des espaces de noms. La majorité des classes du noyau\nutilise les espace de noms, par exemple, `yii\\web\\Request`. Le préfixe «C» n'est plus utilisé dans les noms de classe.\nLe schéma de nommage suit maintenant la structure des répertoires. Par exemple, `yii\\web\\Request`\nindique que le fichier de classe correspondant est `web/Request.php` dans le dossier du framework.\n\n(Vous pouvez utiliser n'importe quelle classe du noyau sans inclure explicitement le fichier correspondant, grâce au\nchargeur de classe de Yii.)\n\n\nComposants et objets\n--------------------\n\nYii 2.0 décompose la classe `CComponent` 1.1 en deux classes: [[yii\\base\\BaseObject]] et [[yii\\base\\Component]].\nLe classe [[yii\\base\\BaseObject|BaseObject]] est une classe de base légère qui permet de définir les\n[Propriétés de l'objet](concept-properties.md) via des accesseurs. La classe [[yii\\base\\Component|Component]] est une\nsous classe de [[yii\\base\\BaseObject|BaseObject]] et prend en charge les [Événements](concept events.md) et les\n[Comportements](concept-behaviors.md).\n\nSi votre classe n'a pas besoin des événements et des comportements, vous devriez envisager d'utiliser\n[[yii\\base\\BaseObject|BaseObject]] comme classe de base. C'est généralement le cas pour les classes qui représentent\nune structure de données basique.\n\n\nConfiguration d'objets\n---------------------\n\nLa classe [[yii\\base\\BaseObject|BaseObject]] introduit une manière uniforme de configurer les objets. Toute sous-classe\nde [[yii\\base\\BaseObject|BaseObject]] doit déclarer son constructeur (si besoin) de la manière suivante afin qu'elle\npuisse être configurée correctement :\n\n```php\nclass MyClass extends \\yii\\base\\BaseObject\n{\n    public function __construct($param1, $param2, $config = [])\n    {\n        // ... initialisation avant que la configuration ne soit appliquée\n\n        parent::__construct($config);\n    }\n\n    public function init()\n    {\n        parent::init();\n\n        // ... initialisation après que la configuration est appliquée\n    }\n}\n```\n\nDans ce qui précède, le dernier paramètre du constructeur doit être un tableau de configuration\nqui contient des entrées nom-valeur pour initialiser les propriétés à la fin du constructeur.\nVous pouvez remplacer la méthode [[yii\\base\\BaseObject::init()|init()]] pour le travail d'initialisation qui doit être fait\naprès que la configuration a été appliquée.\n\nEn suivant cette convention, vous serez en mesure de créer et de configurer de nouveaux objets en utilisant un tableau\nde configuration :\n\n```php\n$object = Yii::createObject([\n    'class' => 'MyClass',\n    'property1' => 'abc',\n    'property2' => 'cde',\n], [$param1, $param2]);\n```\n\nPlus de détails sur les configurations peuvent être trouvés dans la section\n[Configurations](concept-configurations.md).\n\n\nÉvénements\n----------\n\nAvec Yii 1, les événements étaient créés par la définition d'une  méthode `on` (par exemple `onBeforeSave`). Avec Yii 2,\nvous pouvez maintenant utiliser n'importe quel nom de l'événement. Vous déclenchez un événement en appelant\nla méthode [[yii\\base\\Component::trigger()|trigger()]] :\n\n```php\n$event = new \\yii\\base\\Event;\n$component->trigger($eventName, $event);\n```\n\nPour attacher un gestionnaire à un événement, utilisez la méthode [[yii\\base\\Component::on()|on()]]:\n\n```php\n$component->on($eventName, $handler);\n// Pour détacher le gestionnaire, utilisez :\n// $component->off($eventName, $handler);\n```\nIl y a de nombreuses améliorations dans la gestion des événements. Pour plus de détails, reportez-vous à la section [Événements](concept-events.md).\n\n\nAlias\n-----\n\nYii 2.0 étend l'utilisation des alias aux fichiers/répertoires et aux URL. Yii 2.0 impose maintenant\naux alias de commencer par le caractère `@`, pour différencier les alias de fichiers/répertoires ou URL.\nPar exemple, l'alias `@yii` fait référence au répertoire d'installation de Yii. Les alias ??sont\nsupportés dans la plupart du code de Yii. Par exemple, [[yii\\caching\\FileCache::cachePath]] peut prendre\nà la fois un alias et un chemin de répertoire normal.\n\nUn alias est aussi étroitement lié aux espaces de noms des classes. Il est recommandé de définir\nun alias pour chaque espace de noms racine, ce qui vous permet d'utiliser le chargeur automatique de classe de Yii sans\nsans devoir en faire d'avantage. Par exemple, vu que `@yii` fait référence au dossier d'installation de Yii,\nune classe comme `yii\\web\\Request` peut être chargée automatiquement. Si vous utilisez une librairie tierce,\ntelle que Zend Framework, vous pouvez définir un alias de chemin `@Zend` qui fera référence au dossier\nd'installation de Zend Framework. Une fois que vous avez fait cela, Yii sera aussi en mesure de charger automatiquement une classe de ce framework.\n\nPour en savoir plus, consultez la section [Alias](concept-aliases.md).\n\n\nVues\n----\n\nLe changement le plus significatif à propos des vues dans Yii 2 est que la variable spéciale `$this` dans une vue ne fait plus référence au\ncontrôleur ou à l'objet graphique. Au lieu de cela, `$this` correspond maintenant à un objet *vue*, un nouveau concept\nintroduit dans la version 2.0. L'objet *vue* est de type [[yii\\web\\View]], et représente la partie vue\ndu modèle MVC. Si vous souhaitez accéder au contrôleur ou  à l'objet graphique dans une vue, vous pouvez utiliser `$this->context`.\n\nPour afficher une vue depuis une autre vue, utilisez `$this->render()`, et non `$this->renderPartial()`. Le résultat retourné par la méthode `render()` doit être explicitement envoyé à la sortie, en effet `render()` retournera la vue au lieu de l'afficher. Par exemple :\n\n```php\necho $this->render('_item', ['item' => $item]);\n```\n\nOutre l'utilisation de PHP comme langage principal de gabarit, Yii 2.0 prend également en charge\ndeux moteurs de gabarit populaires : Smarty et Twig. Le moteur de gabarit Prado n'est plus pris en charge.\nPour utiliser ces moteurs de gabarit, vous devez configurer le composant `view` de l'application en définissant la propriété\n[[yii\\base\\View::$renderers|View::$renderers]]. Reportez-vous à la section [Moteur de gabarit](tutorial-template-engines.md) pour en savoir plus.\n\n\nModèles\n-------\n\nYii 2.0 utilise la classe [[yii\\base\\Model]] comme modèle de base, similaire à la classe `CModel` dans la version 1.1.\nLa classe `CFormModel` a été supprimée. Vous pouvez, à la place, étendre la classe [[yii\\base\\Model]] afin de créer une classe modèle pour un formulaire.\n\nYii 2.0 introduit une nouvelle méthode appelée [[yii\\base\\Model::scenarios()|scenarios()]] pour déclarer\nles scénarios pris en charge, indiquer dans quel scénario un attribut doit être validé et si cet attribut peut être considéré comme sûr ou non, etc. Par exemple:\n\n```php\npublic function scenarios()\n{\n    return [\n        'backend' => ['email', 'role'],\n        'frontend' => ['email', '!name'],\n    ];\n}\n```\n\nDans ce qui précède, deux scénarios sont déclarés: `backend` et `frontend`. Pour le scénario `backend` les\nattribut `email` et `role` sont sûrs et peuvent être affectés massivement. Pour le scénario `frontend`,\n`email` peut être affecté massivement tandis que `role` ne le peut pas. `email` et `rôle` doivent être validées en utilisant des règles.\n\nLa méthode [[yii\\base\\Model::rules()|rules()]] est toujours utilisée pour déclarer les règles de validation. Remarque : suite à l'introduction de la méthode [[yii\\base\\Model::scenarios()|scenarios()]], le validateur `unsafe` n'as plus de raison d'être.\n\nDans la plupart des cas, vous n'avez pas besoin de surcharger la méthode [[yii\\base\\Model::scenarios()|scenarios()]]\nlorsque les scénarios existants sont déclarés via la méthode [[yii\\base\\Model::rules()|rules()]], et il n'y a pas besoin de déclarer de propriétés `unsafe`.\n\nPour en savoir plus sur les modèles, reportez-vous à la section [Modèles](structure-models.md).\n\n\nContrôleurs\n-----------\n\nYii 2.0 utilise la classe [[yii\\web\\Controller]] comme classe de base des contrôleurs, similaire à la classe `CController` dans la version Yii 1.1.\n[[yii\\base\\Action]] est la classe de base pour les actions.\n\nL'impact le plus évident de ces changements sur votre code est qu'une action de contrôleur doit retourner le contenu\nque vous voulez afficher au lieu de l'envoyer vers la sortie :\n\n```php\npublic function actionView($id)\n{\n    $model = \\app\\models\\Post::findOne($id);\n    if ($model) {\n        return $this->render('view', ['model' => $model]);\n    } else {\n        throw new \\yii\\web\\NotFoundHttpException;\n    }\n}\n```\n\nReportez-vous à la section [Contrôleurs](structure-controllers.md) pour plus de détails.\n\n\nObjets graphiques\n-----------------\n\nYii 2.0 utilise la classe [[yii\\base\\Widget]] comme classe de base pour les objets graphiques, similaire à la classe `CWidget` de Yii 1.1.\n\nPour avoir une meilleure prise en charge du framework dans les EDI, Yii 2 introduit une nouvelle syntaxe pour utiliser les objets graphiques. Les méthodes statiques\n[[yii\\base\\Widget::begin()|begin()]], [[yii\\base\\Widget::end()|end()]], et [[yii\\base\\Widget::widget()|widget()]]\nont été créées et sont utilisables comme suit :\n\n```php\nuse yii\\widgets\\Menu;\nuse yii\\widgets\\ActiveForm;\n\n// Remarque : vous devez utiliser echo pour afficher le résultat\necho Menu::widget(['items' => $items]);\n\n// Utilisation d'un tableau pour initialiser les propriétés de l'objet\n$form = ActiveForm::begin([\n    'options' => ['class' => 'form-horizontal'],\n    'fieldConfig' => ['inputOptions' => ['class' => 'input-xlarge']],\n]);\n... champs du formulaire ici ...\nActiveForm::end();\n```\n\nReportez-vous à la section [Objets graphiques](structure-widgets.md) pour en savoir plus.\n\n\nThèmes\n------\n\nLes thèmes fonctionnent tout à fait différemment dans la version 2.0. Ils sont maintenant basés sur un mécanisme de mise en correspondance de chemin qui met un chemin\nde fichier de vue en correspondance avec un chemin de fichier de vue thématisée. Par exemple, si la mise en correspondance  pour un thème est\n`['/web/views' => '/web/themes/basic']`, alors la version thématisée du fichier de vue\n`/web/views/site/index.php` sera `/web/themes/basic/site/index.php`. Pour cette raison, les thèmes peuvent maintenant\nêtre appliqués à n'importe quel fichier de vue, même une vue utilisée en dehors du contexte d'un contrôleur ou d'un objet graphique.\n\nEn outre, il n'y a plus de composant `CThemeManager`. A la place, `theme` est une propriété configurable du composant `view`\nde l'application.\n\nReportez-vous à la section [Thématisation](tutorial-theming.md) pour plus de détails.\n\n\nApplications en ligne de commande\n---------------------------------\n\nLes applications en ligne de commande (console) sont désormais organisées en contrôleurs, comme les applications Web. ces contrôleurs\ndoivent étendre la classe [[yii\\console\\Controller]], similaire à la classe `CConsoleCommand` de la version 1.1.\n\nPour exécuter une commande console, utilisez `yii <route>`, où `<route>` correspond à une route vers un contrôleur\n(par exemple `sitemap/index`). Les arguments anonymes supplémentaires sont passés comme paramètres à\nl'action du contrôleur correspondant, alors que les arguments nommés sont analysés selon\nles options déclarées dans la méthode [[yii\\console\\Controller::options()]].\n\nYii 2.0 prend en charge la génération automatique d'aide à partir des blocs de commentaire.\n\nReportez-vous à la section [Commandes console](tutorial-console.md) pour plus de détails.\n\n\nI18N\n----\n\nYii 2.0 supprime les fonctionnalités internes de formatage des dates et des nombres, en faveur du [module PHP PECL intl](https://pecl.php.net/package/intl).\n\nLa traduction des messages est désormais effectuée via le composant d'application `i18n`.\nCe composant gère un ensemble de sources de messages, ce qui vous permet d'utiliser différentes\nsources de messages en fonction de catégories.\n\nReportez-vous à la section [Internationalisation](tutorial-i18n.md) pour plus de détails.\n\n\nFiltres d'action\n----------------\n\nLes filtres d'action sont maintenant implémentés comme des comportements. Pour définir un nouveau filtre personnalisé, étendez la classe [[yii\\base\\ActionFilter]]. Pour utiliser un filtre, déclarez le\ncomme un comportement du contrôleur.  Par exemple, pour utiliser le filtre [[yii\\filters\\AccessControl]], vous aurez le code suivant dans le contrôleur :\n\n```php\npublic function behaviors()\n{\n    return [\n        'access' => [\n            'class' => 'yii\\filters\\AccessControl',\n            'rules' => [\n                ['allow' => true, 'actions' => ['admin'], 'roles' => ['@']],\n            ],\n        ],\n    ];\n}\n```\n\nReportez-vous à la section [Filtres](structure-filters.md) pour plus de détails.\n\n\nRessources\n----------\n\nYii 2.0 introduit un nouveau concept de paquet de ressources (*asset bundle*) qui remplace le concept de gestionnaire de ressources (*asset manager*) de la version 1.1.\n\nUn paquet de ressources est une collection de fichiers (par exemple : fichier JavaScript, CSS, image, etc.)\ndans un dossier. Chaque paquet est représenté par une classe étendant [[yii\\web\\AssetBundle]].\nEn *enregistrant* un paquet de ressources via [[yii\\web\\AssetBundle::register()]], vous rendez les ressources du paquet accessibles via le Web. Contrairement à Yii 1.1, la page *enregistrant* le paquet\ncontiendra automatiquement les références vers les fichiers déclarés dans le paquet.\n\nReportez-vous à la section [Assets](structure-assets.md) pour plus de détails.\n\n\nAssistants\n----------\n\nYii 2.0 introduit de nombreux assistants couramment utilisés, sous la forme de classes statiques, y compris :\n\n* [[yii\\helpers\\Html]]\n* [[yii\\helpers\\ArrayHelper]]\n* [[yii\\helpers\\StringHelper]]\n* [[yii\\helpers\\FileHelper]]\n* [[yii\\helpers\\Json]]\n\nReportez-vous à la section [Assistants](helper-overview.md) pour plus de détails.\n\n\nFormulaires\n-----------\n\nYii 2.0 introduit le concept de *champ* pour la construction d'un formulaire à l'aide de la classe [[yii\\widgets\\ActiveForm]]. Un champ\nest un conteneur constitué d'une étiquette, d'une entrée, d'un message d'erreur, et/ou d'un texte d'aide.\nUn champ est représenté comme un objet de la classe [[yii\\widgets\\ActiveField|ActiveField]].\nEn utilisant des champs, vous pouvez construire un formulaire plus proprement qu'avant:\n\n```php\n<?php $form = yii\\widgets\\ActiveForm::begin(); ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n    <div class=\"form-group\">\n        <?= Html::submitButton('Login') ?>\n    </div>\n<?php yii\\widgets\\ActiveForm::end(); ?>\n```\n\nReportez-vous à la section [Créer des formulaires](input-forms.md) pour plus de détails.\n\n\nConstructeur de requêtes\n-----------------------\n\nDans la version 1.1, la construction des requêtes était dispersée dans plusieurs classes, y compris `CDbCommand`,\n`CDbCriteria` et `CDbCommandBuilder`. Avec Yii 2.0, une requête de base de données est représentée par un objet de la classe [[yii\\db\\Query|Query]]\nqui peut être transformé en une instruction SQL à l'aide de la classe [[yii\\db\\QueryBuilder|QueryBuilder]].\nPar exemple:\n\n```php\n$query = new \\yii\\db\\Query();\n$query->select('id, name')\n      ->from('user')\n      ->limit(10);\n\n$command = $query->createCommand();\n$sql = $command->sql;\n$rows = $command->queryAll();\n```\n\nDe plus, ces méthodes de construction de requête peuvent également être utilisées lorsque vous travaillez avec [Active Record](db-active-record.md).\n\nReportez-vous à la section [Constructeur de requête](db-query-builder.md) pour plus de détails.\n\n\nActive Record\n-------------\n\nYii 2.0 introduit beaucoup de modifications au modèle [Active Record](db-active-record.md). Les deux plus évidentes concernent la construction des requêtes et la manipulation de requêtes relationnelles.\n\nLa classe `CDbCriteria` en 1.1 est remplacée par [[yii\\db\\ActiveQuery]] dans Yii 2. Cette classe étend [[yii\\db\\Query]],\net hérite donc de toutes les méthodes de construction de requête. Pour commencer à construire une requête il suffit d'utiliser [[yii\\db\\ActiveRecord::find()]] :\n\n```php\n// Pour récupérer tous les clients *actifs* et les trier selon leur identifiant\n$customers = Customer::find()\n    ->where(['status' => $active])\n    ->orderBy('id')\n    ->all();\n```\n\nPour déclarer une relation, il suffit de définir un accesseur qui renvoie un objet [[yii\\db\\ActiveQuery|ActiveQuery]].\nLe nom de la propriété définie par l'accesseur représente le nom de la relation. Par exemple, le code suivant déclare\nune relation `orders` (en 1.1, vous aviez à déclarer les relations dans la méthode `relations()`):\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public function getOrders()\n    {\n        return $this->hasMany('Order', ['customer_id' => 'id']);\n    }\n}\n```\n\nMaintenant vous pouvez utiliser `$customer->orders` pour accéder aux commandes du client depuis la table liée.\nVous pouvez aussi utiliser le code suivant pour effectuer une requête relationnelle à la volée avec une condition\npersonnalisée :\n\n```php\n$orders = $customer->getOrders()->andWhere('status=1')->all();\n```\n\nLors du chargement anticipé (*eager loading*) d'une relation, Yii 2.0 fonctionne différemment de la version 1.1.\nEn particulier avec Yii 1.1, une jointure était créée pour sélectionner à la fois l'enregistrement principal et les\nenregistrements liés. Avec Yii 2.0, deux instructions SQL sont exécutées sans utiliser de jointure : la première\nrécupère les enregistrements principaux et la seconde récupère les enregistrements liés en filtrant selon les clés\nprimaires des enregistrements principaux.\n\nAu lieu de retourner des objets [[yii\\db\\ActiveRecord|ActiveRecord]], vous pouvez utiliser la méthode\n[[yii\\db\\ActiveQuery::asArray()|asArray()]] lors de la construction d'une requête pour renvoyer un grand nombre\nd'enregistrements. Ainsi le résultat  sera retourné sous forme de tableaux, ce qui peut réduire considérablement le temps de calcul et la mémoire nécessaires dans le cas d'un grand nombre d'enregistrements. Par exemple:\n\n```php\n$customers = Customer::find()->asArray()->all();\n```\n\nUn autre changement fait que vous ne pouvez plus définir les valeurs par défaut des attributs en utilisant des propriétés\npubliques. Si vous en avez besoin, vous devez utiliser la méthode `init` de la classe de votre modèle.\n\n```php\npublic function init()\n{\n    parent::init();\n    $this->status = self::STATUS_NEW;\n}\n```\n\nIl y avait des problèmes de surcharge du constructeur de la classe ActiveRecord 1.1. Ces problèmes n'existent plus dans\nla version 2.0. Notez que lorsque vous ajoutez des paramètres au constructeur, vous avez éventuellement à surcharger\nla méthode [[yii\\db\\ActiveRecord::instantiate()]].\n\nIl y a beaucoup d'autres modifications et améliorations à Active Record.\nReportez-vous à la section [Active Record](db-active-record.md) pour en savoir plus.\n\nComportement des Enregistrements actifs)\n---------------------------------------------------------\n\nDans la version 2.0, nous avons la classe de base des  *behaviors* (comportements)  `CActiveRecordBehavior`. Si vous voulez créer une classe de comportement d'enregistrement actif (Active Record), vous devez étendre directement la classe `yii\\base\\Behavior`. Si la classe de comportement doit réagir à certains événements du propriétaire, vous devez redéfinir les méthodes `events()` comme suit :\n\n```php\nnamespace app\\components;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\base\\Behavior;\n\nclass MyBehavior extends Behavior\n{\n    // ...\n\n    public function events()\n    {\n        return [\n            ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',\n        ];\n    }\n\n    public function beforeValidate($event)\n    {\n        // ...\n    }\n}\n```\nUser et IdentityInterface\n-------------------------\n\nLa classe `CWebUser` 1.1 est maintenant remplacée par [[yii\\web\\User]], et il n'y a plus de classe `CUserIdentity`.\nAu lieu de cela, vous devez implémenter [[yii\\web\\IdentityInterface]] qui est beaucoup plus simple à utiliser.\nLe modèle de projet avancé fournit un exemple.\n\nReportez-vous aux sections [Authentification](security-authentication.md), [Authorisation](security-authorization.md), et\n[Modèle de projet avancé](https://www.yiiframework.com/extension/yiisoft/yii2-app-advanced/doc/guide) pour en savoir plus.\n\n\nGestion des URL\n---------------\n\nLa gestion des URL dans Yii 2 est similaire à celle disponible dans la version 1.1. Une amélioration majeure est que la\ngestion des URL prend désormais en charge les paramètres optionnels. Par exemple, si vous avez une règle déclarée comme suit,\ncela fera correspondre `post/popular` et `post/1/popular`. Dans la version 1.1, il fallait utiliser deux règles pour\natteindre le même objectif.\n\n```php\n[\n    'pattern' => 'post/<page:\\d+>/<tag>',\n    'route' => 'post/index',\n    'defaults' => ['page' => 1],\n]\n```\n\nReportez-vous à la section [Documentation de la gestion des URL](runtime-routing.md) pour en savoir plus.\n\n\nUn changement important dans la convention de nommage pour les routes est que les noms utilisant la *casse en dos de chameau*  des contrôleurs et des actions utilisent désormais uniquement des mots en bas de casse séparés par des tirets, p. ex. l'identifiant du  contrôleur  Reportez-vous à la section traitant des  [Identifiants de contrôleur](structure-controllers.md#controller-ids) et  [Identifiants d'action](structure-controllers.md#action-ids) pour plus de détails.\n\nUtiliser Yii 1.1 et 2.x ensemble\n--------------------------------\n\nSi vous avez du code Yii 1.1 que vous souhaitez réutiliser avec Yii 2, reportez-vous à la section [Utiliser Yii 1.1 et 2.0 ensemble](tutorial-yii-integration.md).\n\n"
  },
  {
    "path": "docs/guide-fr/intro-yii.md",
    "content": "Qu'est ce que Yii ?\n=================\n\nYii est un framework PHP hautes performances à base de composants qui permet de développer rapidement des applications Web modernes.\nLe nom Yii (prononcer `Yee` ou `[ji:]`) signifie \"simple et évolutif\" en Chinois. Il peut également \nêtre considéré comme un acronyme de **Yes It Is**!\n\n\nPour quel usage Yii est il optimal ?\n-----------------------------------\n\nYii est un framework Web générique, c'est à dire qu'il peut être utilisé pour développer tous types\nd'applications Web basées sur PHP. De par son architecture à base de composants et son système de cache sophistiqué,\nil est particulièrement adapté au développement d'applications à forte audience telles que des portails, des forums,\ndes systèmes de gestion de contenu (CMS), des sites e-commerce, des services Web RESTFul, etc.\n\n\nComment se positionne Yii vis-à-vis des autres Frameworks ? \n----------------------------------------------------------\n\n- Comme la plupart des frameworks PHP, Yii est basé sur le modèle de conception MVC (Modèle-Vue-Contrôleur) et encourage à une\norganisation du code basée sur ce modèle.\n- Yii repose sur l'idée que le code doit être écrit de façon simple et élégante. Il ne sera jamais question de\ncompliquer le code de Yii uniquement pour respecter un modèle de conception.\n- Yii est un framework complet offrant de nombreuses caractéristiques éprouvées et prêtes à l'emploi, telles que:\nconstructeur de requêtes et ActiveRecord, à la fois pour les bases de données relationnelles et NoSQL; prise en charge RESTful API;\nprise en charge de caches multi-niveaux; et plus. \n- Yii est extrêmement flexible. Vous pouvez personnaliser ou remplacer presque chaque partie du code du noyau. Vous pouvez également \nprofiter de son architecture extensible solide, afin d'utiliser ou développer des extensions distribuables. \n- La haute performance est toujours un des principaux objectifs de Yii.\n\nYii n'est pas un one-man show, il est soutenu par une [solide équipe de développement du noyau][yii_team] ainsi que d'une grande communauté \navec de nombreux professionnels qui contribuent constamment au développement de Yii. L'équipe de développeurs de Yii \ngarde un œil attentif sur les dernières tendances en développement Web, et sur les meilleures pratiques et caractéristiques \ntrouvées dans d'autres frameworks ou projets. Les meilleures pratiques et caractéristiques les plus pertinentes trouvées ailleurs sont régulièrement intégrées dans le code du noyau et utilisables\nvia des interfaces simples et élégantes.\n\n[yii_team]: https://www.yiiframework.com/team\n\nVersions de Yii\n---------------\n\nYii est actuellement disponible en deux versions majeures : 1.1 et 2.0. La version 1.1, l'ancienne génération, est désormais en mode maintenance. La version 2.0 est une réécriture complète de Yii, adoptant les dernières technologies et protocoles, y compris Composer, PSR, les espaces de noms, les traits, et ainsi de suite. La version 2.0 est la dernière génération du framework et recevra nos principaux efforts de développement dans les prochaines années. \nCe guide est principalement pour la version 2.0.\n\n\nConfiguration nécessaire\n------------------------\n\nYii 2.0 nécessite PHP 7.4.0 ou plus. Vous pouvez trouver plus de détails sur la configuration requise pour chaque fonctionnalité\nen utilisant le script de test de la configuration inclus dans chaque distribution de Yii.\n\nUtiliser Yii requiert des connaissances de base sur la programmation objet (OOP), en effet Yii est un framework basé sur ce type de programmation.\nYii 2.0 utilise aussi des fonctionnalités récentes de PHP, telles que les [espaces de noms](https://www.php.net/manual/fr/language.namespaces.php) et les [traits](https://www.php.net/manual/fr/language.oop5.traits.php).\nComprendre ces concepts vous aidera à mieux prendre en main Yii.\n\n"
  },
  {
    "path": "docs/guide-fr/output-client-scripts.md",
    "content": "Travail avec des scripts clients\n===================================\n\n> Note: cette section est encore en développement.\n\n### Enregistrement des scripts\n\nAvec l'objet [[yii\\web\\View]] vous êtes en mesure d'enregistrer des scripts. Il existe deux méthodes dédiées pour cela :\n[[yii\\web\\View::registerJs()|registerJs()]] pour les scripts en ligne et [[yii\\web\\View::registerJsFile()|registerJsFile()]] pour les scripts externes.\n\n\nLes scripts en ligne sont utiles pour la configuration et le code généré dynamiquement. La méthode pour les ajouter est la suivante :\n\n\n```php\n$this->registerJs(\"var options = \".json_encode($options).\";\", View::POS_END, 'my-options');\n```\n\nLe premier argument est le code JS réel à insérer dans la page. Le deuxième argument détermine à quel endroit le script doit être inséré dans la page. Les valeurs possibles sont : \n- [[yii\\web\\View::POS_HEAD|View::POS_HEAD]] pour le placer dans la section d'entête (`<head></head>`).\n- [[yii\\web\\View::POS_BEGIN|View::POS_BEGIN]] pour le placer juste après la balise d'ouverture du corps de la page (`<body>`).\n- [[yii\\web\\View::POS_END|View::POS_END]] pour le placer juste avant la balise de fermeture du corps de la page (`</body>`).\n- [[yii\\web\\View::POS_READY|View::POS_READY]] pour l'exéuter sur l'événement  « document `ready` ». Cela enregistre [[yii\\web\\JqueryAsset|jQuery]] automatiquement.\n- [[yii\\web\\View::POS_LOAD|View::POS_LOAD]] pour l'exécuter sur l'événement « document `load` » . Cela enregistre [[yii\\web\\JqueryAsset|jQuery]] automatiquement.\n\nLe dernier argument est un identifiant unique du script utilisé pour identifier le bloc de code et remplacer un bloc existant de même identifiant au lieu de simplement l'ajouter. Si vous ne le fournissez pas, le code JS lui-même est utilisé en tant qu'identifiant. \n\nUn script externe peut être ajouté comme expliqué ci-dessous : \n\n```php\n$this->registerJsFile('https://example.com/js/main.js', ['depends' => [\\yii\\web\\JqueryAsset::class]]);\n```\n\nLes arguments pour  [[yii\\web\\View::registerJsFile()|registerJsFile()]] sont semblables à ceux utilisés pour [[yii\\web\\View::registerCssFile()|registerCssFile()]]. Dans l'exemple précédent, nous enregistrons le fichier `main.js` avec une dépendance sur `JqueryAsset`. Cela siginifie que le fichier `main.js` sera ajouté APRÈS `jquery.js`. Sans la spécification de cette dépendance, l'ordre relatif entre `main.js` et `jquery.js` resterait indéfini.\n\nComme pour [[yii\\web\\View::registerCssFile()|registerCssFile()]], il est également fortement recommandé que vous utilisiez les [paquets de ressources](structure-assets.md) (asset bundles) pour enregistrer des fichiers JS externes plutôt que d'utiliser [[yii\\web\\View::registerJsFile()|registerJsFile()]].\n\n\n### Enregistrement de paquets de ressources\n\nComme cela a été mentionné plus tôt, il est préférable d'utiliser des paquets de ressources plutôt que d'utiliser CSS et JavaScript directement. Vous pouvez obtenir des détails sur les paquets de ressources dans la section [Ressources](structure-assets.md) de ce guide. Comme lors de l'utilisation des paquets de ressources déjà définis, c'est très simple :\n\n```php\n\\frontend\\assets\\AppAsset::register($this);\n```\n\n\n### Enregistrement des CSS\n\nVous pouvez enregistrer les CSS en utilisant [[yii\\web\\View::registerCss()|registerCss()]] ou [[yii\\web\\View::registerCssFile()|registerCssFile()]]. Le premier enregistre un bloc de code CSS tandis que le second enregistre un fichier CSS externe. Par exemple :\n\n```php\n$this->registerCss(\"body { background: #f00; }\");\n```\n\nLe code ci-dessus provoque l'ajout de ce qui suit à la section « head » :\n\n```html\n<style>\nbody { background: #f00; }\n</style>\n```\n\nSi vous désirez spécifier des propriétés additionnelles du style balise, passez un tableau des paires nom-valeur en tant que troisième argument. Si vous avez besoin de vous assurer qu'il y a seulement une balise style unique, utilisez un quatrième argument comme cela a été mentionné dans la description des balises méta. \n\n```php\n$this->registerCssFile(\"https://example.com/css/themes/black-and-white.css\", [\n    'depends' => [BootstrapAsset::class],\n    'media' => 'print',\n], 'css-print-theme');\n```\n\nLe code ci-dessus provoque l'ajout d'un lien vers un fichier CSS à la section « head » de la page.  \n\n* Le premier argument spécifie le fichier CSS à enregistrer. \n* Le deuxième argument spécifie l'attribut HTML pour la balise `<link>` résultant. L'option `depends` fait l'objet d'une interprétation particulière. Elle spécifie de quel paquet de ressources  ce fichier dépend. Dans ce cas, le paquet de ressources dont le ficher dépend est [[yii\\bootstrap\\BootstrapAsset|BootstrapAsset]]. Cela veut dire que le fichier CSS sera ajouté *après* les fichiers CSS contenus dans [[yii\\bootstrap\\BootstrapAsset|BootstrapAsset]].\n* Le dernier argument spécifie un identifiant pour ce fichier CSS. S'il n'est pas fourni, l'URL du fichier CSS est utilisée à sa place. \n\n\nIl est fortement recommandé que vous utilisiez des [paquets de ressources](structure-assets.md) pour enregistrer des fichers CSS externes plutôt que [[yii\\web\\View::registerCssFile()|registerCssFile()]]. L'utilisation des paquets de ressources vous permet de combiner et de comprimer plusieurs fichiers CSS, ce qui est souhaitable pour les sites Web à trafic intense.\n"
  },
  {
    "path": "docs/guide-fr/output-data-providers.md",
    "content": "Fournisseurs de données\n=======================\n\nDans les sections [Pagination](output-pagination.md) et [Tri](output-sorting.md), nous avons décrit comment permettre à l'utilisateur de choisir une page particulière de données à afficher et de trier ces données en fonction de certaines colonnes. Comme les tâches de pagination et de tri sont très courantes, Yii met à votre disposition un jeu de classes *fournisseurs de données* pour les encapsuler.\n\nUn fournisseur de données est une classe qui implémente l'interface [[yii\\data\\DataProviderInterface]]. Il prend en essentiellement en charge l'extraction de données paginées et triées. Il fonctionne ordinairement avec des [composants graphiques de données](output-data-widgets.md) pour que l'utilisateur final puisse paginer et trier les données de manière interactive.\n\nLes classes fournisseurs de données suivantes sont incluses dans les versions publiées de Yii :\n\n* [[yii\\data\\ActiveDataProvider]]: utilise [[yii\\db\\Query]] ou [[yii\\db\\ActiveQuery]] pour demander des données à des bases de données et les retourner sous forme de tableaux ou d'instances d'[enregistrement actif](db-active-record.md).\n* [[yii\\data\\SqlDataProvider]]: exécute une instruction SQL et retourne les données sous forme de tableaux. \n* [[yii\\data\\ArrayDataProvider]]: prend un gros tableau et en retourne une tranche en se basant sur les spécifications de pagination et de tri.\n\nTous ces fournisseurs de données sont utilisés selon un schéma commun :\n\n```php\n// créer le fournisseur de données en configurant ses propriétés de pagination et de tri \n$provider = new XyzDataProvider([\n    'pagination' => [...],\n    'sort' => [...],\n]);\n\n// retrouver les données paginées et triées\n$models = $provider->getModels();\n\n// obtenir le nombre d'items de données dans la page courante\n$count = $provider->getCount();\n\n// obtenir le nombre total d'items de données de l'ensemble des pages \n$totalCount = $provider->getTotalCount();\n```\n\nVous spécifiez les comportements de pagination et de tri d'un fournisseur de données en configurant ses propriétés [[yii\\data\\BaseDataProvider::pagination|pagination]] et [[yii\\data\\BaseDataProvider::sort|sort (tri)]] qui correspondent aux configurations de [[yii\\data\\Pagination]] et [[yii\\data\\Sort]], respectivement. Vous pouvez également les configurer à `false` pour désactiver la pagination et/ou le tri.\n\nLes [composants graphiques de données](output-data-widgets.md), tels que [[yii\\grid\\GridView]], disposent d'une propriété nommée `dataProvider` qui accepte une instance de fournisseur de données et affiche les données qu'il fournit. Par exemple :\n\n```php\necho yii\\grid\\GridView::widget([\n    'dataProvider' => $dataProvider,\n]);\n```\n\nCes fournisseurs de données varient essentiellement en fonction de la manière dont la source de données est spécifiée. Dans les sections qui suivent, nous expliquons l'utilisation détaillée de chacun des ces fournisseurs de données. \n\n\n## Fournisseur de données actif <span id=\"active-data-provider\"></span> \n\nPour utiliser le [[yii\\data\\ActiveDataProvider|fournisseur de données actif (classe *ActiveDataProvider*)]], vous devez configurer sa propriété [[yii\\data\\ActiveDataProvider::query|query]]. Elle accepte soit un objet [[yii\\db\\Query]], soit un objet [[yii\\db\\ActiveQuery]]. Avec le premier, les données peuvent être soit des tableaux, soit des instances d'[enregistrement actif](db-active-record.md). Par exemple :\n\n\n```php\nuse yii\\data\\ActiveDataProvider;\n\n$query = Post::find()->where(['status' => 1]);\n\n$provider = new ActiveDataProvider([\n    'query' => $query,\n    'pagination' => [\n        'pageSize' => 10,\n    ],\n    'sort' => [\n        'defaultOrder' => [\n            'created_at' => SORT_DESC,\n            'title' => SORT_ASC, \n        ]\n    ],\n]);\n\n// retourne un tableau d'objets Post \n$posts = $provider->getModels();\n```\n\nSi la requête `$query` de l'exemple ci-dessus est créée en utilisant le code suivant, alors le fournisseur de données retourne des tableaux bruts.\n\n```php\nuse yii\\db\\Query;\n\n$query = (new Query())->from('post')->where(['status' => 1]); \n```\n\n> Note: si une requête spécifie déjà la clause `orderBy`, les nouvelles instructions de tri données par l'utilisateur final (via la configuration `sort`) sont ajoutées à la clause `orderBy` existante. Toute clause `limit` et `offset` existante est écrasée par la requête de pagination de l'utilisateur final (via la configuration `pagination`).\nPar défaut, [[yii\\data\\ActiveDataProvider]] utilise le composant d'application `db` comme connexion à la base de données. Vous pouvez utiliser une connexion différente en configurant la propriété [[yii\\data\\ActiveDataProvider::db]].\n\n\n## Fournisseur de données SQL <span id=\"sql-data-provider\"></span>\n\n[[yii\\data\\SqlDataProvider]] travaille avec des instructions SQL brutes pour aller chercher les données. Selon les spécifications de [[yii\\data\\SqlDataProvider::sort|sort]] et de [[yii\\data\\SqlDataProvider::pagination|pagination]], le fournisseur ajuste les clauses `ORDER BY` et `LIMIT` de l'instruction SQL en conséquence pour n'aller chercher que la page de données requise dans l'ordre désiré.\n\nPour utiliser [[yii\\data\\SqlDataProvider]], vous devez spécifier la propriété [[yii\\data\\SqlDataProvider::sql|sql]], ainsi que la propriété [[yii\\data\\SqlDataProvider::totalCount|totalCount]]. Par exemple :\n\n```php\nuse yii\\data\\SqlDataProvider;\n\n$count = Yii::$app->db->createCommand('\n    SELECT COUNT(*) FROM post WHERE status=:status\n', [':status' => 1])->queryScalar();\n\n$provider = new SqlDataProvider([\n    'sql' => 'SELECT * FROM post WHERE status=:status',\n    'params' => [':status' => 1],\n    'totalCount' => $count,\n    'pagination' => [\n        'pageSize' => 10,\n    ],\n    'sort' => [\n        'attributes' => [\n            'title',\n            'view_count',\n            'created_at',\n        ],\n    ],\n]);\n\n// retourne un tableau de lignes de données\n$models = $provider->getModels();\n```\n\n> Info: la propriété [[yii\\data\\SqlDataProvider::totalCount|totalCount]] est requise seulement si vous avez besoin de paginer les données.Cela est dû au fait que l'instruction SQL spécifiée via [[yii\\data\\SqlDataProvider::sql|sql]] est modifiée par le fournisseur pour ne retourner que la page de données couramment requise. Le fournisseur a donc besoin de connaître le nombre total d'items de données pour calculer correctement le nombre de pages disponibles.\n\n\n## Fournisseur de données tableau <span id=\"array-data-provider\"></span>\n\nL'utilisation de [[yii\\data\\ArrayDataProvider]] est préférable lorsque vous travaillez avec un grand tableau. Le fournisseur vous permet de retourner une page des données du tableau, triées selon une ou plusieurs colonnes. Pour utiliser [[yii\\data\\ArrayDataProvider]], vous devez spécifier la propriété [[yii\\data\\ArrayDataProvider::allModels|*allModels* (tous les modèles)]] comme un grand tableau. Les éléments dans le grand tableau peuvent être, soit des tableaux associatifs (p. ex. des résultats de requête d'[objets d'accès aux données (DAO)](db-dao.md)) ou des objets (p. ex. les instances d'[Active Record](db-active-record.md). Par exemple :\n\n```php\nuse yii\\data\\ArrayDataProvider;\n\n$data = [\n    ['id' => 1, 'name' => 'name 1', ...],\n    ['id' => 2, 'name' => 'name 2', ...],\n    ...\n    ['id' => 100, 'name' => 'name 100', ...],\n];\n\n$provider = new ArrayDataProvider([\n    'allModels' => $data,\n    'pagination' => [\n        'pageSize' => 10,\n    ],\n    'sort' => [\n        'attributes' => ['id', 'name'],\n    ],\n]);\n\n// obtient les lignes de la page couramment requise\n$rows = $provider->getModels();\n``` \n\n> Note: comparé au [fournisseur de données actif](#active-data-provider) et au fournisseur de données SQL](#sql-data-provider), le fournisseur de données tableau est moins efficient car il requiert de charger *toutes* les données en mémoire.\n\n\n## Travail avec les clés de données <span id=\"working-with-keys\"></span>\n\nLorsque vous utilisez les items de données retournés par le fournisseur de données, vous avez souvent besoin d'identifier chacun des items de données par une clé unique. Par exemple, si les items de donnés représentent des informations sur un client, vous désirez peut-être utiliser l'identifiant du client en tant que clé pour chacun de lots d'informations sur un client. Les fournisseurs de données peuvent retourner une liste de telles clés correspondant aux items de données retournés par [[yii\\data\\DataProviderInterface::getModels()]]. Par exemple :\n\n```php\nuse yii\\data\\ActiveDataProvider;\n\n$query = Post::find()->where(['status' => 1]);\n\n$provider = new ActiveDataProvider([\n    'query' => $query,\n]);\n\n// retourne un tableau d'objets Post\n$posts = $provider->getModels();\n\n// retourne les valeurs des clés primaires correspondant à $posts\n```\n\nDans l'exemple ci-dessus, comme vous fournissez un objet [[yii\\db\\ActiveQuery]] à [[yii\\data\\ActiveDataProvider]]. Il est suffisamment intelligent pour retourner les valeurs de la clé primaire en tant que clés. Vous pouvez aussi spécifier comment les valeurs de la clé sont calculées en configurant [[yii\\data\\ActiveDataProvider::key]] avec un nom de colonne ou une fonction de rappel qui calcule les valeurs de la clé. Par exemple :\n\n```php\n// utilise la colonne \"slug\" comme valeurs de la clé\n$provider = new ActiveDataProvider([\n    'query' => Post::find(),\n    'key' => 'slug',\n]);\n\n// utilise le résultat de md5(id) comme valeurs de la clé\n$provider = new ActiveDataProvider([\n    'query' => Post::find(),\n    'key' => function ($model) {\n        return md5($model->id);\n    }\n]);\n```\n\n\n## Création d'un fournisseur de données personnalisé <span id=\"custom-data-provider\"></span>\n\nPour créer votre fournisseur de données personnalisé, vous devez implémenter [[yii\\data\\DataProviderInterface]]. Une manière plus facile est d'étendre [[yii\\data\\BaseDataProvider]],ce qui vous permet de vous concentrer sur la logique centrale du fournisseur de données. En particulier, vous devez essentiellement implémenter les méthodes suivantes :\n \n- [[yii\\data\\BaseDataProvider::prepareModels()|prepareModels()]]: prépare les modèles de données qui seront disponibles dans la page courante et les retourne sous forme de tableau. \n- [[yii\\data\\BaseDataProvider::prepareKeys()|prepareKeys()]]: accepte un tableau de modèles de données couramment disponibles et retourne les clés qui leur sont associés.\n- [[yii\\data\\BaseDataProvider::prepareTotalCount()|prepareTotalCount]]: retourne une valeur indiquant le nombre total de modèles de données dans le fournisseur.\n\nNous présentons ci-dessous un exemple de fournisseur de données que lit des données CSV efficacement :\n\n```php\n<?php\nuse yii\\data\\BaseDataProvider;\n\nclass CsvDataProvider extends BaseDataProvider\n{\n    /**\n     * @var string name of the CSV file to read\n     */\n    public $filename;\n    \n    /**\n     * @var string|callable nom de la colonne clé ou fonction de rappel la retournant\n     */\n    public $key;\n    \n    /**\n     * @var SplFileObject\n     */\n    protected $fileObject; // SplFileObject est très pratique pour rechercher une ligne particulière dans un fichier\n    \n \n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        \n        // open file\n        $this->fileObject = new SplFileObject($this->filename);\n    }\n \n    /**\n     * {@inheritdoc}\n     */\n    protected function prepareModels()\n    {\n        $models = [];\n        $pagination = $this->getPagination();\n \n        if ($pagination === false) {\n            // dans le cas où il n'y a pas de pagination, lit toutes les lignes\n            while (!$this->fileObject->eof()) {\n                $models[] = $this->fileObject->fgetcsv();\n                $this->fileObject->next();\n            }\n        } else {\n            // s'il y a une pagination, ne lit qu'une seule page\n            $pagination->totalCount = $this->getTotalCount();\n            $this->fileObject->seek($pagination->getOffset());\n            $limit = $pagination->getLimit();\n \n            for ($count = 0; $count < $limit; ++$count) {\n                $models[] = $this->fileObject->fgetcsv();\n                $this->fileObject->next();\n            }\n        }\n \n        return $models;\n    }\n \n    /**\n     * {@inheritdoc}\n     */\n    protected function prepareKeys($models)\n    {\n        if ($this->key !== null) {\n            $keys = [];\n \n            foreach ($models as $model) {\n                if (is_string($this->key)) {\n                    $keys[] = $model[$this->key];\n                } else {\n                    $keys[] = call_user_func($this->key, $model);\n                }\n            }\n \n            return $keys;\n        } else {\n            return array_keys($models);\n        }\n    }\n \n    /**\n     * {@inheritdoc}\n     */\n    protected function prepareTotalCount()\n    {\n        $count = 0;\n \n        while (!$this->fileObject->eof()) {\n            $this->fileObject->next();\n            ++$count;\n        }\n \n        return $count;\n    }\n}\n```\n"
  },
  {
    "path": "docs/guide-fr/output-data-widgets.md",
    "content": "Composants graphiques d'affichage de données\n============================================\n\nYii fournit un jeu de [composants graphiques](structure-widgets.md) utilisables pour afficher des données. Tandis que le componsant graphique [DetailView](#detail-view) (vue détaillée) peut être utilisé pour afficher un enregistrement unique, les composants graphiques [ListView](#list-view) (vue en liste) et [GridView](#grid-view) (vue en grille) peuvent être utilisés pour afficher plusieurs enregistrements en liste ou en grille assortis de fonctionnalités telles que la pagination, le tri et le filtrage.\n\n\nVue détaillée (classe *DetailView*) <span id=\"detail-view\"></span>\n----------------------------------\n\nLe composant graphique [[yii\\widgets\\DetailView|DetailView]] (vue détaillée) affiche les détails d'un [[yii\\widgets\\DetailView::$model|modèle]] de données unique.\n\nIl est le plus adapté à l'affichage d'un modèle dans un format courant (p. ex. chacun des attributs du modèle est affiché en tant que ligne d'une grille). Le modèle peut être, soit une instance, ou une classe fille, de [[\\yii\\base\\Model]] telle que la classe [ActiveRecord](db-active-record.md), soit un tableau associatif.\n\n*DetailView* utilise la propriété [[yii\\widgets\\DetailView::$attributes|$attributes]] pour déterminer quels attributs du modèle doivent être affichés et comment ils doivent être formatés. Reportez-vous à la section [formatage des données](output-formatting.md) pour des informations sur les options de formatage.\n\nUne utilisation typique de *DetailView* ressemble à ce qui suit : \n\n```php\necho DetailView::widget([\n    'model' => $model,\n    'attributes' => [\n        'title',               // attribut title (en texte simple)\n        'description:html',    // attribut description formaté en HTML\n        [                      // le nom du propriétaire du modèle\n            'label' => 'Owner',\n            'value' => $model->owner->name,\n        ],\n        'created_at:datetime', // date de création formaté comme une date/temps\n    ],\n]);\n```\n\nVue en liste (class *ListView*)<span id=\"list-view\"></span>\n------------------------------\n\nLe composant graphique [[yii\\widgets\\ListView|ListView]] (vue en liste) est utilisé pour afficher des données issues d'un [fournisseur de données](output-data-providers.md). Chacun des modèles est rendu en utilisant le composant [[yii\\widgets\\ListView::$itemView|ListView]] (vue en liste) spécifié. Comme ce composant fournit des fonctionnalités telles que la pagination, le tri et le filtrage de base, il est pratique, à la fois pour afficher des informations et pour créer des interfaces utilisateur de gestion des données.\n\nTypiquement, on l'utilise comme ceci :\n\n```php\nuse yii\\widgets\\ListView;\nuse yii\\data\\ActiveDataProvider;\n\n$dataProvider = new ActiveDataProvider([\n    'query' => Post::find(),\n    'pagination' => [\n        'pageSize' => 20,\n    ],\n]);\necho ListView::widget([\n    'dataProvider' => $dataProvider,\n    'itemView' => '_post',\n]);\n```\n\nLe fichier de vue,  `_post`, contient ce qui suit : \n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\helpers\\HtmlPurifier;\n?>\n<div class=\"post\">\n    <h2><?= Html::encode($model->title) ?></h2>\n\n    <?= HtmlPurifier::process($model->text) ?>\n</div>\n```\n\nDans le fichier ci-dessus, le modèle de données courant est disponible comme `$model`. En outre, les variables suivantes sont disponibles :\n\n- `$key`: mixed, la valeur de la clé associée à l'item de données\n- `$index`: integer, l'index commençant à zéro de l'item de données dans le tableau d'items retourné par le fournisseur de données.\n- `$widget`: ListView, l'instance de ce composant graphique.\n\nSi vous avez besoin de passer des données additionnelles à chacune des vues, vous pouvez utiliser la propriété [[yii\\widgets\\ListView::$viewParams|$viewParams]] pour passer des paires clé valeur, comme ceci :\n\n```php\necho ListView::widget([\n    'dataProvider' => $dataProvider,\n    'itemView' => '_post',\n    'viewParams' => [\n        'fullView' => true,\n        'context' => 'main-page',\n        // ...\n    ],\n]);\n```\n\nCelles-ci sont alors disponibles aussi dans la vue en tant que variables. \n\n\nVue en grille (classe *GridView*)<span id=\"grid-view\"></span>\n--------------------------------\n\nLa vue en grille, ou composant [[yii\\grid\\GridView|GridView]], est un des composants les plus puissants de Yii. Ce composant est extrêmement utile si vous devez rapidement construire l'interface d'administration du système. Il accepte des données d'un [fournisseur de données](output-data-providers.md) et rend chacune des lignes en utilisant un jeu de [[yii\\grid\\GridView::columns|columns]] (colonnes), présentant ainsi l'ensemble des données sous forme d'une grille.\n\nChacune des lignes de la grille représente un item unique de données, et une colonne représente ordinairement un attribut de l'item (quelques colonnes peuvent correspondre à des expressions complexes utilisant les attributs ou à un texte statique).\n\nLe code minimal pour utiliser le composant *GridView* se présente comme suit :\n\n```php\nuse yii\\grid\\GridView;\nuse yii\\data\\ActiveDataProvider;\n\n$dataProvider = new ActiveDataProvider([\n    'query' => Post::find(),\n    'pagination' => [\n        'pageSize' => 20,\n    ],\n]);\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n]);\n```\n\nLe code précédent crée un fournisseur de données, puis utilise le composant *GridView* pour afficher chacun des attributs dans une ligne en le prélevant dans le fournisseur de données. La grille affichée est doté de fonctionnalités de pagination et de tri sans autre intervention. \n\n\n### Colonnes de la grille\n\nLes colonnes de la grille sont exprimées en terme de classe [[yii\\grid\\Column]], qui sont configurées dans la propriété [[yii\\grid\\GridView::columns|columns]] (colonnes) de la configuration du composant *GridView*. En fonction du type de colonne et des réglages, celles-ci sont en mesure de présenter les données différemment. La classe par défaut est [[yii\\grid\\DataColumn]] (colonne de données), qui représente un attribut de modèle et peut être triée et filtrée. \n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'columns' => [\n        ['class' => 'yii\\grid\\SerialColumn'],\n        // colonnes simples définies par les données contenues dans le fournisseur de données\n        // les données de la colonne du modèle sont utilisées\n        'id',\n        'username',\n        // un exemple plus complexe\n        [\n            'class' => 'yii\\grid\\DataColumn', // peut être omis car c'est la valeur par défaut\n            'value' => function ($data) {\n                return $data->name; // $data['name'] pour une donnée tableau p. ex. en utilisant SqlDataProvider.\n            },\n        ],\n    ],\n]);\n```\n\nNotez que si la partie [[yii\\grid\\GridView::columns|columns]] de la configuration n'est pas spécifiée, Yii essaye de montrer toutes les colonnes possibles du modèle du fournisseur de données.\n\n\n### Classes de colonne\n\nLes colonnes du composant *GridView* peuvent être personnalisées en utilisant différentes classes de colonnes : \n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'columns' => [\n        [\n            'class' => 'yii\\grid\\SerialColumn', // <-- ici\n            // vous pouvez configurer des propriété additionnelles ici\n        ],\n```\n\nEn plus des classes de colonne fournies par Yii que nous allons passer en revue ci-après, vous pouvez créer vos propres classes de colonne. \n\nChacune des classes de colonne étend la classe [[yii\\grid\\Column]] afin que quelques options communes soient disponibles lors de la configuration des colonnes.\n\n- [[yii\\grid\\Column::header|header]] permet de définir une ligne d'entête\n- [[yii\\grid\\Column::footer|footer]] permet de définir le contenu d'une ligne de pied de grille\n- [[yii\\grid\\Column::visible|visible]] définit si la colonne doit être visible.\n- [[yii\\grid\\Column::content|content]] vous permet de passer une fonction de rappel PHP valide qui retourne les données d'une ligne. Le format est le suivant :\n\n  ```php\n  function ($model, $key, $index, $column) {\n      return 'a string';\n  }\n  ```\n\nVous pouvez spécifier différentes options HTML de conteneurs en passant des tableaux à :\n\n- [[yii\\grid\\Column::headerOptions|headerOptions]]\n- [[yii\\grid\\Column::footerOptions|footerOptions]]\n- [[yii\\grid\\Column::filterOptions|filterOptions]]\n- [[yii\\grid\\Column::contentOptions|contentOptions]]\n\n\n#### Colonne de données (*DataColumn*) <span id=\"data-column\"></span>\n\nLa classe [[yii\\grid\\DataColumn|DataColumn]] (colonne de données) est utilisée pour afficher et trier des données. C'est le type de colonne par défaut, c'est pourquoi la spécification de la classe peut être omise.\n\nLe réglage principal de la colonne de données est celui de sa propriété [[yii\\grid\\DataColumn::format|format]]. Ses valeurs correspondent aux méthodes du [composant d'application](structure-application-components.md) `formatter` qui est de classe [[\\yii\\i18n\\Formatter|Formatter]] par défaut :\n\n```php\necho GridView::widget([\n    'columns' => [\n        [\n            'attribute' => 'name',\n            'format' => 'text'\n        ],\n        [\n            'attribute' => 'birthday',\n            'format' => ['date', 'php:Y-m-d']\n        ],\n    ],\n]);La valeur de la colonne est passée en tant que premier argument\n```\n\nDans cet exemple, `text` correspond à la méthode [[\\yii\\i18n\\Formatter::asText()]]. La valeur de la colonne est passée en tant que premier argument. Dans la deuxième définition de colonne, `date` correspond à la méthode [[\\yii\\i18n\\Formatter::asDate()]]. La valeur de la colonne est passée en tant que premier argument tandis que 'php:Y-m-d' est utilisé en tant que valeur du deuxième argument.\n\nPour une liste complète de tous les formateurs, reportez-vous à la section [Formatage des données](output-formatting.md).\n\nPour configurer des colonnes de données, il y a aussi un format raccourci qui est décrit dans la documentation de l'API de [[yii\\grid\\GridView::columns|columns]].\n\n\n#### Colonne d'actions (*ActionColumn*)\n\nLa classe [[yii\\grid\\ActionColumn|ActionColumn]] (colonne d'action) affiche des boutons d'action tels que mise à jour ou supprimer pour chacune des lignes. \n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'columns' => [\n        [\n            'class' => 'yii\\grid\\ActionColumn',\n            // vous pouvez configurer des propriétés additionnelles ici\n        ],\n```\n\nLes propriétés additionnelles configurables sont :\n\n- [[yii\\grid\\ActionColumn::controller|controller]] qui est l'identifiant du contrôleur qui prend en charge l'action. Si cette propriété n'est pas définie, le contrôleur courant est utilisé. \n- [[yii\\grid\\ActionColumn::template|template]] qui définit le modèle utilisé pour composer chacune des cellules dans la colonne d'actions. Les marqueurs (textes à l'intérieur d'accolades) sont traités comme des identifiants d'action (aussi appelé *noms de bouton* dans le contexte d'une colonne d'actions. Il sont remplacés par les fonctions de rappel correspondantes spécifiées dans la propriété [[yii\\grid\\ActionColumn::$buttons|buttons]]. Par exemple, le marqueur `{view}` sera remplacé par le résultat de la fonction de rappel `buttons['view']`. Si une fonction de rappel n'est pas trouvée, le texte est remplacé par une chaîne vide. Les marqueurs par défaut sont `{view} {update} et {delete}`.\n- [[yii\\grid\\ActionColumn::buttons|buttons]] est un tableau de fonctions de rappel pour le rendu des boutons. Les clés du tableau sont les noms des boutons (sans les accolades), et les valeurs sont les fonctions de rappel de rendu des boutons. Les fonctions de rappel ont la signature suivante :\n\n  ```php\n  function ($url, $model, $key) {\n      // retourne le code HTML du bouton\n  }\n  ```\n\n  dans le code qui précède, `$url` est l'URL que la colonne crée pour le bouton, `$model` est l'objet modèle qui est en train d'être rendu pour la ligne courante, et `$key` est la clé du modèle dans le tableau du fournisseur de données.\n\n- [[yii\\grid\\ActionColumn::urlCreator|urlCreator]] est une fonction de rappel qui crée une URL de bouton en utilisant les informations spécifiées sur le modèle. La signature de la fonction de rappel doit être le même que celle de [[yii\\grid\\ActionColumn::createUrl()]]. Si cette propriété n'est pas définie, les URL de bouton sont créées en utilisant [[yii\\grid\\ActionColumn::createUrl()]].\n- [[yii\\grid\\ActionColumn::visibleButtons|visibleButtons]] est un tableau des conditions de visibilité pour chacun des boutons. Les clés du tableau sont les noms des boutons (sans les accolades), et les valeurs sont les valeurs booleénnes `true` ou `false` (vrai ou faux) ou la fonction anonyme. Lorsque le nom du bouton n'est pas spécifié dans ce tableau, il est montré par défaut. Les fonctions de rappel utilisent la signature suivante :\n\n  ```php\n  function ($model, $key, $index) {\n      return $model->status === 'editable';\n  }\n  ```\n\n  Ou vous pouvez passer une valeur booléenne :\n\n  ```php\n  [\n      'update' => \\Yii::$app->user->can('update')\n  ]\n  ```\n\n#### Colonne boîte à cocher (*CheckboxColumn*)\n\nLa classe [[yii\\grid\\CheckboxColumn|CheckboxColumn]] (colonne de boîtes à cocher) affiche une colonne de boîtes à cocher.\n\nPour ajouter une colonne de boîtes à cocher à la vue en grille (*GridView*), ajoutez la configuration de [[yii\\grid\\GridView::$columns|columns]] comme ceci :\n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'columns' => [\n        // ...\n        [\n            'class' => 'yii\\grid\\CheckboxColumn',\n            // vous pouvez configurer des propriétés additionnelles ici\n        ],\n    ],\n```\n\nL'utilisateur peut cliquer sur les boîtes à cocher pour sélectionner des lignes dans la grille. Les lignes sélectionnées peuvent être obtenues en appelant le code JavaScript suivant :\n\n```javascript\nvar keys = $('#grid').yiiGridView('getSelectedRows');\n// keys est un tableau constitué des clés associées aux lignes sélectionnées. \n\n```\n\n#### Colonne série (*SerialColumn*)\n\nLa classe [[yii\\grid\\SerialColumn|SerialColumn]] (colonne série) rend les numéros de ligne en commençant à `1` et en continuant.\n\nL'utilisation est aussi simple que ce que nous présentons ci-après :\n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'columns' => [\n        ['class' => 'yii\\grid\\SerialColumn'], // <-- ici\n        // ...\n```\n\n\n### Tri des données\n\n> Note: cette section est en cours de développement. \n>\n> - https://github.com/yiisoft/yii2/issues/1576\n\n### Filtrage des données\n\nPour filtrer les données, la vue en grille (*GridView*) requiert un [modèle](structure-models.md) qui représente le critère de recherche qui est ordinairement pris dans les champs du filtre dans la vue en grille. Une pratique courante lorsqu'on utilise des [enregistrements actifs](db-active-record.md) est de créer une classe modèle de recherche qui fournit les fonctionnalités nécessaires (elle peut être générée pour vous par [Gii](start-gii.md)). Cette classe définit les règles de validation pour la recherche et fournit une méthode `search()` (recherche) qui retourne le fournisseur de données avec une requête ajustée qui respecte les critères de recherche.\n\nPour ajouter la fonctionnalité de recherche au modèle `Post`, nous pouvons créer un modèle `PostSearch` comme celui de l'exemple suivant :\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse Yii;\nuse yii\\base\\Model;\nuse yii\\data\\ActiveDataProvider;\n\nclass PostSearch extends Post\n{\n    public function rules()\n    {\n        // seuls les champs dans rules() peuvent être recherchés\n        return [\n            [['id'], 'integer'],\n            [['title', 'creation_date'], 'safe'],\n        ];\n    }\n\n    public function scenarios()\n    {\n        // bypasse l'implémentation de scenarios() dans la classe parent\n        return Model::scenarios();\n    }\n\n    public function search($params)\n    {\n        $query = Post::find();\n\n        $dataProvider = new ActiveDataProvider([\n            'query' => $query,\n        ]);\n\n        // charge les données du formulaire de recherche et valide \n        if (!($this->load($params) && $this->validate())) {\n            return $dataProvider;\n        }\n\n        // ajuste la requête en ajoutant les filtres \n        $query->andFilterWhere(['id' => $this->id]);\n        $query->andFilterWhere(['like', 'title', $this->title])\n              ->andFilterWhere(['like', 'creation_date', $this->creation_date]);\n\n        return $dataProvider;\n    }\n}\n```\n\n> Tip: reportez-vous au [Constructeur de requêtes ](db-query-builder.md) (*Query Builder*) et en particulier aux [conditions de filtrage](db-query-builder.md#filter-conditions) pour savoir comment construire la requête de filtrage.\n\nVous pouvez utiliser cette fonction dans le contrôleur pour obtenir le fournisseur de données de la vue en grille :\n\n```php\n$searchModel = new PostSearch();\n$dataProvider = $searchModel->search(Yii::$app->request->get());\n\nreturn $this->render('myview', [\n    'dataProvider' => $dataProvider,\n    'searchModel' => $searchModel,\n]);\n```\n\nEt dans la vue, vous assignez ensuite le fournisseur de données (`$dataProvider`) et le modèle de recherche (`$searchModel`) à la vue en grille (*GridView*) :\n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'filterModel' => $searchModel,\n    'columns' => [\n        // ...\n    ],\n]);\n```\n\n### Formulaire de filtrage séparé\n\nLa plupart du temps, utiliser les filtres de l'entête de la vue en grille suffit, mais dans le cas où vous avez besoin d'un formulaire de filtrage séparé, vous pouvez facilement l'ajouter aussi. Vous pouvez créer une vue partielle `_search.php` avec le contenu suivant : \n\n```php\n<?php\n\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n/**\n * @var \\yii\\web\\View $this\n * @var \\app\\models\\PostSearch $model\n * @var \\yii\\widgets\\ActiveForm $form\n */\n?>\n\n<div class=\"post-search\">\n    <?php $form = ActiveForm::begin([\n        'action' => ['index'],\n        'method' => 'get',\n    ]); ?>\n\n    <?= $form->field($model, 'title') ?>\n\n    <?= $form->field($model, 'creation_date') ?>\n\n    <div class=\"form-group\">\n        <?= Html::submitButton('Search', ['class' => 'btn btn-primary']) ?>\n        <?= Html::submitButton('Reset', ['class' => 'btn btn-default']) ?>\n    </div>\n\n    <?php ActiveForm::end(); ?>\n</div>\n```\n\net l'inclure dans la vue `index.php`, ainsi :\n\n```php\n<?= $this->render('_search', ['model' => $searchModel]) ?>\n```\n\n> Note: si vous utilisez Gii pour générer le code des méthodes CRUD, le formulaire de filtrage séparé (`_search.php`) est généré par défaut, mais est commenté dans la vue `index.php`. Il vous suffit de supprimer la marque du commentaire pour l'utiliser !\n\nUn formulaire de filtrage séparé est utile quand vous avez besoin de filtrer selon des champs qui ne sont pas visibles dans la vue en grille, ou pour des conditions particulières de filtrage, telles qu'une plage de dates. Pour filtrer selon une plage de dates, nous pouvons ajouter les attributs non DB `createdFrom` et`createdTo` au modèle de recherche :\n\n```php\nclass PostSearch extends Post\n{\n    /**\n     * @var string\n     */\n    public $createdFrom;\n\n    /**\n     * @var string\n     */\n    public $createdTo;\n}\n```\n\nÉtendez les conditions de la requête dans la méthode `search()` comme ceci :\n\n```php\n$query->andFilterWhere(['>=', 'creation_date', $this->createdFrom])\n      ->andFilterWhere(['<=', 'creation_date', $this->createdTo]);\n```\n\nEt ajoutez les champs représentatifs au formulaire de filtrage : \n\n```php\n<?= $form->field($model, 'creationFrom') ?>\n\n<?= $form->field($model, 'creationTo') ?>\n```\n\n### Travail avec des relations entre modèles\n\nLorsque vous affichez des enregistrements actifs dans la vue en grille, vous pouvez rencontrer le cas où vous affichez des valeurs de colonne en relation telles que le nom de l'auteur de l'article (post) au lieu d'afficher simplement son identifiant (`id`). Vous pouvez le faire en définissant le nom de l'attribut dans [[yii\\grid\\GridView::$columns]] comme étant `author.name` lorsque le modèle de l'article (`Post`) possède une relation nommée `author` (auteur) et que le modèle possède un attribut nommé `name` (nom). La vue en grille affiche alors le nom de l'auteur mais le tri et le filtrage ne sont pas actifs par défaut. Vous devez ajuster le modèle `PostSearch` que nous avons introduit dans la section précédente pour y ajouter cette fonctionnalité.\n\nPour activer le tri sur une colonne en relation, vous devez joindre la table en relation et ajouter la règle de tri au composant *Sort* du fournisseur de données :\n\n```php\n$query = Post::find();\n$dataProvider = new ActiveDataProvider([\n    'query' => $query,\n]);\n\n// joingnez avec la relation nommée `author` qui est une relation avec la table `users`\n// et définissez l'alias à `author`\n$query->joinWith(['author' => function($query) { $query->from(['author' => 'users']); }]);\n// depuis la version 2.0.7, l'écriture ci-dessus peut être simplifiée en $query->joinWith('author AS author');\n// active le tri pour la colonne en relation \n$dataProvider->sort->attributes['author.name'] = [\n    'asc' => ['author.name' => SORT_ASC],\n    'desc' => ['author.name' => SORT_DESC],\n];\n\n// ...\n```\n\nLe filtrage nécessite aussi l'appel de la fonction *joinWith* ci-dessus. Vous devez également autoriser la recherche sur la colonne dans les attributs et les règles comme ceci :\n\n```php\npublic function attributes()\n{\n    // ajoute les champs en relation avec les attributs susceptibles d'être cherchés\n    return array_merge(parent::attributes(), ['author.name']);\n}\n\npublic function rules()\n{\n    return [\n        [['id'], 'integer'],\n        [['title', 'creation_date', 'author.name'], 'safe'],\n    ];\n}\n```\n\nDans `search()`, il vous suffit ensuite d'ajouter une autre condition de filtrage avec :\n\n```php\n$query->andFilterWhere(['LIKE', 'author.name', $this->getAttribute('author.name')]);\n```\n\n> Info: dans ce qui précède, nous utilisons la même chaîne de caractères pour le nom de la relation et pour l'alias de table ; cependant, lorsque votre nom de relation et votre alias diffèrent, vous devez faire attention aux endroits où vous utilisez l'alias et à ceux où vous utilisez le nom de la relation. Une règle simple pour cela est d'utiliser l'alias partout où cela sert à construire le requête de base de données et le nom de la relation dans toutes les autres définitions telles que `attributes()` et `rules()` etc.\n>\n> Par exemple, si vous utilisez l'alias `au` pour la table auteur en relation, l'instruction *joinWith* ressemble à ceci : \n>\n> ```php\n> $query->joinWith(['author au']);\n> ```\n>\n> Il est également possible d'appeler simplement `$query->joinWith(['author']);` lorsque l'alias est défini dans la définition de la relation. \n>\n> L'alias doit être utilisé dans la condition de filtrage mais le nom d'attribut reste le même :\n>\n> ```php\n> $query->andFilterWhere(['LIKE', 'au.name', $this->getAttribute('author.name')]);\n> ```\n>\n> La même chose est vraie pour la définition du tri :\n>\n> ```php\n> $dataProvider->sort->attributes['author.name'] = [\n>      'asc' => ['au.name' => SORT_ASC],\n>      'desc' => ['au.name' => SORT_DESC],\n> ];\n> ```\n>\n> Également, lorsque vous spécifiez la propriété [[yii\\data\\Sort::defaultOrder|defaultOrder]] (ordre de tri par défaut) pour le tri, vous avez besoin d'utiliser le nom de la relation au lieu de l'alias :\n>\n> ```php\n> $dataProvider->sort->defaultOrder = ['author.name' => SORT_ASC];\n> ```\n\n> Info: pour plus d'informations sur `joinWith` et sur les requêtes effectuées en arrière-plan, consultez la documentation sur l'enregistrement actif à la section [Jointure avec des relations](db-active-record.md#joining-with-relations).\n\n#### Utilisation de vues SQL pour le filtrage, le tri et l'affichage des données\n\nIl existe une autre approche qui peut être plus rapide et plus utile – les vues SQL. Par exemple, si vous avez besoin d'afficher la vue en grille avec des utilisateurs et leur profil, vous pouvez le faire de cette manière :\n\n```sql\nCREATE OR REPLACE VIEW vw_user_info AS\n    SELECT user.*, user_profile.lastname, user_profile.firstname\n    FROM user, user_profile\n    WHERE user.id = user_profile.user_id\n```\n\nEnsuite vous devez créer l'enregistrement actif qui représente cette vue : \n\n```php\n\nnamespace app\\models\\views\\grid;\n\nuse yii\\db\\ActiveRecord;\n\nclass UserView extends ActiveRecord\n{\n\n    /**\n     * {@inheritdoc}\n     */\n    public static function tableName()\n    {\n        return 'vw_user_info';\n    }\n\n    public static function primaryKey()\n    {\n        return ['id'];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function rules()\n    {\n        return [\n            // définissez vos règle ici\n        ];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function attributeLabels()\n    {\n        return [\n            // définissez vos étiquettes d'attribut ici\n        ];\n    }\n\n\n}\n```\n\nAprès cela, vous pouvez utiliser l'enregistrement actif *UserView* dans vos modèle de recherche, sans spécification additionnelle d'attribut de tri et de filtrage. Tous les attributs fonctionneront directement. Notez que cette approche a ses avantages et ses inconvénients :\n\n- vous n'avez pas besoin de spécifier des conditions de tri et de filtrage. Tout fonctionne d'emblée ;\n- cela peut être beaucoup plus rapide à cause de la taille des données et du nombre de requêtes SQL effectuées (pour chacune des relations vous n'avez pas besoin de requête supplémentaire) ;\n- comme cela n'est qu'une simple mise en relation de l'interface utilisateur avec la vue SQL, il lui manque un peu de la logique qui apparaît dans vos entités, ainsi, si vous avez des méthodes comme `isActive`, `isDeleted` ou autres qui influencent l'interface utilisateur, vous devez les dupliquer dans cette classe également.\n\n\n### Plusieurs vues en grille par page\n\nVous pouvez utiliser plus d'une vue en grille sur une page unique mais quelques éléments de configuration additionnels sont nécessaires afin qu'elles n'entrent pas en interférence entre elles. Lorsque vous utilisez plusieurs instances de la vue en grille, vous devez configurer des noms de paramètre différents pour les liens de tri et de pagination générés de manière à ce que chacune des vues en grille possède ses propres liens de tri et de pagination. Vous faites cela en définissant les paramètres [[yii\\data\\Sort::sortParam|sortParam]] (tri) et [[yii\\data\\Pagination::pageParam|pageParam]] (page) des instances [[yii\\data\\BaseDataProvider::$sort|sort]] et [[yii\\data\\BaseDataProvider::$pagination|pagination]] du fournisseur de données.\n\nSupposez que vous vouliez lister les modèles `Post` et `User` pour lesquels vous avez déjà préparé deux fournisseurs de données `$userProvider` et `$postProvider`:\n\n```php\nuse yii\\grid\\GridView;\n\n$userProvider->pagination->pageParam = 'user-page';\n$userProvider->sort->sortParam = 'user-sort';\n\n$postProvider->pagination->pageParam = 'post-page';\n$postProvider->sort->sortParam = 'post-sort';\n\necho '<h1>Users</h1>';\necho GridView::widget([\n    'dataProvider' => $userProvider,\n]);\n\necho '<h1>Posts</h1>';\necho GridView::widget([\n    'dataProvider' => $postProvider,\n]);\n```\n\n### Utilisation de la vue en grille avec Pjax\n\nLe composant graphique [[yii\\widgets\\Pjax|Pjax]] vous permet de mettre à jour une certaine section de votre page plutôt que d'avoir à recharger la page toute entière. Vous pouvez l'utiliser pour mettre uniquement à jour le contenu de la [[yii\\grid\\GridView|GridView]] (vue en grille) lors de l'utilisation de filtres.\n\n```php\nuse yii\\widgets\\Pjax;\nuse yii\\grid\\GridView;\n\nPjax::begin([\n    // PJax options\n]);\n    Gridview::widget([\n        // GridView options\n    ]);\nPjax::end();\n```\n\nPjax fonctionne également pour les liens à l'intérieur du composant graphique [[yii\\widgets\\Pjax|Pjax]] et pour les liens spécifiés par [[yii\\widgets\\Pjax::$linkSelector|Pjax::$linkSelector]]. Mais cela peut être un problème pour les liens d'une [[yii\\grid\\ActionColumn|ActionColumn]] (colonne d'action). Pour empêcher cela, ajoutez l'attribut HTML `data-pjax=\"0\"` aux liens lorsque vous définissez la propriété [[yii\\grid\\ActionColumn::$buttons|ActionColumn::$buttons]].\n\n#### Vue en grille et vue en liste avec Pjax dans Gii\n\nDepuis la version 2.0.5, le générateur d'actions CRUD de [Gii](start-gii.md) dispose d'une option appelée `$enablePjax` qui peut être utilisée, soit via l'interface web, soit en ligne de commande.\n\n```php\nyii gii/crud --controllerClass=\"backend\\\\controllers\\PostController\" \\\n  --modelClass=\"common\\\\models\\\\Post\" \\\n  --enablePjax=1\n```\n\nQui génère un composant graphique [[yii\\widgets\\Pjax|Pjax]] enveloppant les composants graphiques [[yii\\grid\\GridView|GridView]] ou [[yii\\widgets\\ListView|ListView]].\n\nLectures complémentaires\n------------------------\n\n- [Rendering Data in Yii 2 with GridView and ListView](https://www.sitepoint.com/rendering-data-in-yii-2-with-gridview-and-listview/) d'Arno Slatius.\n"
  },
  {
    "path": "docs/guide-fr/output-formatting.md",
    "content": "Formatage des données\n=====================\n\nPour afficher des données dans un format plus facile à lire par les utilisateurs, vous pouvez les formater en utilisant le [composant d'application](structure-application-components.md) `formatter`. Par défaut, le formateur est mis en œuvre par [[yii\\i18n\\Formatter]] qui fournit un jeu de méthodes pour formater des données telles que des dates, des temps, des nombres, des monnaies et autres données couramment utilisées. Vous pouvez utiliser le formateur de la manière indiquée ci-dessous :\n\n```php\n$formatter = \\Yii::$app->formatter;\n\n// affiche : January 1, 2014\necho $formatter->asDate('2014-01-01', 'long');\n \n// affiche : 12.50%\necho $formatter->asPercent(0.125, 2);\n \n// affiche : <a href=\"mailto:cebe@example.com\">cebe@example.com</a>\necho $formatter->asEmail('cebe@example.com'); \n\n// affiche : Yes\necho $formatter->asBoolean(true); \n// il prend aussi en charge l'affichage de valeurs nulles :\n\n// affiche : (Not set)\necho $formatter->asDate(null); \n```\n\nComme vous pouvez le voir, ces trois méthodes sont nommées selon le format suivant `asXyz()`, où `Xyz` représente un format pris en charge. En alternative, vous pouvez formater les données en utilisant la méthode générique [[yii\\i18n\\Formatter::format()|format()]], qui vous permet de contrôler le format désiré par programmation et qui est communément utilisé par les composants graphiques tels que [[yii\\grid\\GridView]] et [[yii\\widgets\\DetailView]]. Par exemple :\n\n```php\n// affiche : January 1, 2014\necho Yii::$app->formatter->format('2014-01-01', 'date'); \n\n// vous pouvez aussi utiliser un tableau pour spécifier les paramètres de votre méthode de formatage :\n// `2` est la valeur du paramètre `$decimals` (nombre de décimales) pour la méthode asPercent().\n// affiche : 12.50%\necho Yii::$app->formatter->format(0.125, ['percent', 2]); \n```\n\n> Note: le composant de formatage est conçu pour formater des valeurs à présenter à l'utilisateur. Si vous voulez convertir des entrées utilisateur en un format lisible par la machine, ou simplement formater une date dans un format lisible par la machine, le formateur n'est pas l'outil adapté à cela. Pour convertir une entrée utilisateur pour une date et un temps, vous pouvez utiliser [[yii\\validators\\DateValidator]] et [[yii\\validators\\NumberValidator]] respectivement. Pour une simple conversion entre les formats lisibles par la machine de date et de temps, la fonction PHP [date()](https://www.php.net/manual/fr/function.date.php) suffit.\n\n## Configuration du formateur <span id=\"configuring-formatter\"></span>\n\nVous pouvez configurer les règles de formatage en configurant le composant `formatter` dans la [configuration de l'application](concept-configurations.md#application-configurations). Par exemple :\n\n```php\nreturn [\n    'components' => [\n        'formatter' => [\n            'dateFormat' => 'dd.MM.yyyy',\n            'decimalSeparator' => ',',\n            'thousandSeparator' => ' ',\n            'currencyCode' => 'EUR',\n       ],\n    ],\n];\n```\n\nReportez-vous à la classe [[yii\\i18n\\Formatter]] pour connaître les propriétés qui peuvent être configurées.\n\n\n## Formatage de valeurs de dates et de temps <span id=\"date-and-time\"></span>\n\nLe formateur prend en charge les formats de sortie suivants en relation avec les dates et les temps : \n\n- [[yii\\i18n\\Formatter::asDate()|date]]: la valeur est formatée sous la forme d'une date, p. ex. `January 01, 2014`.\n- [[yii\\i18n\\Formatter::asTime()|time]]: la valeur est formatée sous la forme d'un temps, p. ex. `14:23`.\n- [[yii\\i18n\\Formatter::asDatetime()|datetime]]: la valeur est formatée sous la forme d'une date et d'un temps, p. ex. `January 01, 2014 14:23`.\n- [[yii\\i18n\\Formatter::asTimestamp()|timestamp]]: la valeur est formatée sous la forme d'un [horodatage unix ](https://fr.wikipedia.org/wiki/Heure_Unix), p. ex. `1412609982`.\n- [[yii\\i18n\\Formatter::asRelativeTime()|relativeTime]]: la valeur est formatée sous la forme d'un intervalle de temps entre un temps et le temps actuel dans une forme lisible par l'homme, p.ex. `1 hour ago`.\n- [[yii\\i18n\\Formatter::asDuration()|duration]]: la valeur est formatée comme une durée dans un format lisible par l'homme, p. ex. `1 day, 2 minutes`.\n\nLes formats par défaut pour les dates et les temps utilisés pour les méthodes [[yii\\i18n\\Formatter::asDate()|date]], [[yii\\i18n\\Formatter::asTime()|time]],\net [[yii\\i18n\\Formatter::asDatetime()|datetime]] peuvent être configurés globalement en configurant [[yii\\i18n\\Formatter::dateFormat|dateFormat]], [[yii\\i18n\\Formatter::timeFormat|timeFormat]], et [[yii\\i18n\\Formatter::datetimeFormat|datetimeFormat]].\n\nVous pouvez spécifier les formats de date et de temps en utilisant la [syntaxe ICU](https://unicode-org.github.io/icu/userguide/format_parse/datetime/). Vous pouvez aussi utiliser la [syntaxe date() de PHP](https://www.php.net/manual/fr/function.date.php) avec le préfixe `php:` pour la différentier de la syntaxe ICU. Par exemple :\n\n```php\n// format ICU\necho Yii::$app->formatter->asDate('now', 'yyyy-MM-dd'); // 2014-10-06\n\n// format date() de PHP\necho Yii::$app->formatter->asDate('now', 'php:Y-m-d'); // 2014-10-06\n```\n\nLorsque vous travaillez avec des applications qui requièrent une prise en charge de plusieurs langues, vous devez souvent spécifier différents formats de dates et de temps pour différentes locales. Pour simplifier cette tâche, vous pouvez utiliser les raccourcis de formats (p. ex. `long`, `short`), à la place. Le formateur transforme un raccourci de formats en un format approprié en prenant en compte la [[yii\\i18n\\Formatter::locale|locale]] courante. Les raccourcis de formats suivants sont pris en charge (les exemples supposent que `en_GB` est la locale courante) :\n\n- `short`: affiche `06/10/2014` pour une date et `15:58` pour un temps;\n- `medium`: affiche `6 Oct 2014` et `15:58:42`;\n- `long`: affiche `6 October 2014` et `15:58:42 GMT`;\n- `full`: affiche `Monday, 6 October 2014` et `15:58:42 GMT`.\n\nDepuis la version 2.0.7, il est aussi possible de formater les dates dans différents systèmes calendaires. Reportez-vous à la documentation de l'API pour la propriété [[yii\\i18n\\Formatter::$calendar|$calendar]] des formateurs pour savoir comment définir un autre système calendaire. \n\n### Fuseaux horaires <span id=\"time-zones\"></span>\n\nLors du formatage des dates et des temps, Yii les convertit dans le [[yii\\i18n\\Formatter::timeZone|fuseau horaire]] cible. La valeur à formater est supposée être donnée en UTC, sauf si un fuseau horaire est explicitement défini ou si vous avez configuré [[yii\\i18n\\Formatter::defaultTimeZone]].\n\nDans les exemples qui suivent, nous supposons que la cible [[yii\\i18n\\Formatter::timeZone|fuseau horaire]] est définie à `Europe/Berlin`. \n\n```php\n// formatage d'un horodatage UNIX comme un temps\necho Yii::$app->formatter->asTime(1412599260); // 14:41:00\n\n// formatage d'une chaîne de caractère date-temps (en UTC) comme un temps \necho Yii::$app->formatter->asTime('2014-10-06 12:41:00'); // 14:41:00\n\n// formatage d'une chaîne de caractères date-temps (en CEST) comme un temps\necho Yii::$app->formatter->asTime('2014-10-06 14:41:00 CEST'); // 14:41:00\n```\n\n> Note: comme les fuseaux horaires sont assujettis à des règles fixées par les gouvernements du monde entier, et que ces règles peuvent varier fréquemment, il est vraisemblable que vous n'ayez pas la dernière information dans la base de données des fuseaux horaires installée sur votre système. Vous pouvez vous reporter au [manuel d'ICU](https://unicode-org.github.io/icu/userguide/datetime/timezone/#updating-the-time-zone-data) pour des informations sur la manière de mettre cette base de données à jour. Reportez-vous aussi au tutoriel [Configurer votre environnement PHP pour l'internationalisation](tutorial-i18n.md#setup-environment).\n\n\n## Formatage des nombres <span id=\"numbers\"></span>\n\nPour les nombres, le formateur prend en charge les formats de sortie suivants :\n\n- [[yii\\i18n\\Formatter::asInteger()|integer]]: la valeur est formatée comme un entier, p. ex. `42`.\n- [[yii\\i18n\\Formatter::asDecimal()|decimal]]: la valeur est formatée comme un nombre décimal en portant attention aux décimales et aux séparateurs de milliers, p. ex. `2,542.123` ou `2.542,123`.\n- [[yii\\i18n\\Formatter::asPercent()|percent]]: la valeur est formatée comme un pourcentage p. ex. `42%`.\n- [[yii\\i18n\\Formatter::asScientific()|scientific]]: la valeur est formatée comme un nombre dans le format scientifique p. ex. `4.2E4`.\n- [[yii\\i18n\\Formatter::asCurrency()|currency]]: la valeur est formatée comme une valeur monétaire, p. ex. `£420.00`. Notez que pour que cette fonction fonctionne correctement, la locale doit inclure la partie correspondant au pays p. ex. `en_GB` ou `en_US` parce que la partie langue seulement reste ambigüe dans ce cas. \n- [[yii\\i18n\\Formatter::asSize()|size]]: la valeur, qui est un nombre d'octets est formatée sous une forme lisible par l'homme, p. ex. `410 kibibytes`.\n- [[yii\\i18n\\Formatter::asShortSize()|shortSize]]: est la version courte de [[yii\\i18n\\Formatter::asSize()|size]], e.g. `410 KiB`.\n\nLe format pour un nombre peut être ajusté en utilisant [[yii\\i18n\\Formatter::decimalSeparator|decimalSeparator (séparateur de décimales)]] et\n[[yii\\i18n\\Formatter::thousandSeparator|thousandSeparator (séparateur de milliers) ]], qui prennent tous les deux les valeurs par défaut déterminées par la [[yii\\i18n\\Formatter::locale|locale]] courante.\n\nPour une configuration plus avancée, [[yii\\i18n\\Formatter::numberFormatterOptions]] et [[yii\\i18n\\Formatter::numberFormatterTextOptions]] peuvent être utilisés pour configurer la classe [NumberFormater (formateur de nombres)](https://www.php.net/manual/fr/class.numberformatter.php) utilisée en interne pour implémenter le formateur. Par exemple, pour ajuster la valeur minimum et maximum des chiffres fractionnaires, vous pouvez configurer la propriété [[yii\\i18n\\Formatter::numberFormatterOptions]] comme ceci :\n\n```php\n'numberFormatterOptions' => [\n    NumberFormatter::MIN_FRACTION_DIGITS => 0,\n    NumberFormatter::MAX_FRACTION_DIGITS => 2,\n]\n```\n\n\n## Autres formats <span id=\"other\"></span>\n\nEn plus des formats de date, temps et nombre, Yii prend aussi en charge les autres formats communément utilisés, y compris :\n\n- [[yii\\i18n\\Formatter::asRaw()|raw]]: la valeur est affichée telle quelle, il s'agit d'un pseudo-formateur qui n'a pas d'effet, à l'exception des valeurs `null` qui sont affichées en utilisant la propriété [[nullDisplay]].\n- [[yii\\i18n\\Formatter::asText()|text]]: la valeur est encodée HTML. C'est le format par défaut utilisé par les [données des colonnes du widget GridView](output-data-widgets.md#data-column).\n- [[yii\\i18n\\Formatter::asNtext()|ntext]]: la valeur est formatée comme un texte simple encodé HTML avec conversion des retours à la ligne en balise break.\n- [[yii\\i18n\\Formatter::asParagraphs()|paragraphs]]: la valeur est formatée comme un paragraphe de texte encodé HTML à l'intérieur d'une balise `<p>`.\n- [[yii\\i18n\\Formatter::asHtml()|html]]: la valeur est purifiée en utilisant [[HtmlPurifier]] pour éviter les attaques XSS. Vous pouvez passer les options additionnelles telles que `['html', ['Attr.AllowedFrameTargets' => ['_blank']]]`.\n- [[yii\\i18n\\Formatter::asEmail()|email]]: la valeur est encodé comme un lien `mailto`.\n- [[yii\\i18n\\Formatter::asImage()|image]]: la valeur est formatée comme une balise image.\n- [[yii\\i18n\\Formatter::asUrl()|url]]: la valeur est formatée comme un hyperlien.\n- [[yii\\i18n\\Formatter::asBoolean()|boolean]]: la valeur est formatée comme une valeur booléenne. Par défaut `true` est rendu par `Yes` et `false` par `No`, traduit dans la langue courante de l'application. Vous pouvez ajuster cela en configurant la propriété [[yii\\i18n\\Formatter::booleanFormat]].\n\n\n## Valeurs nulles (null) <span id=\"null-values\"></span>\n\nLes valeurs *null* sont formatées spécialement. Au lieu d'afficher une chaîne de caractères vide, le formateur la convertit en une chaîne de caractères prédéfinie dont la valeur par défaut est `(not set)` traduite dans la langue courante de l'application. Vous pouvez configurer la propriété [[yii\\i18n\\Formatter::nullDisplay|nullDisplay]] pour personnaliser cette chaîne de caractères.\n\n\n## Localisation des formats de données <span id=\"localizing-data-format\"></span>\n\nComme nous l'avons mentionné précédemment, le formateur utilise la [[yii\\i18n\\Formatter::locale|locale]] courante pour déterminer comment formater une valeur qui soit convenable dans la cible pays/région. Par exemple, la même valeur de date est formatée différemment pour différentes locales :\n\n```php\nYii::$app->formatter->locale = 'en-US';\necho Yii::$app->formatter->asDate('2014-01-01'); // affiche : January 1, 2014\n\nYii::$app->formatter->locale = 'de-DE';\necho Yii::$app->formatter->asDate('2014-01-01'); // affiche : 1. Januar 2014\n\nYii::$app->formatter->locale = 'ru-RU';\necho Yii::$app->formatter->asDate('2014-01-01'); // affiche : 1 января 2014 г.\n```\n\nPar défaut, la [[yii\\i18n\\Formatter::locale|locale]] est déterminée par la valeur de [[yii\\base\\Application::language]]. Vous pouvez la redéfinir en définissant la propriété [[yii\\i18n\\Formatter::locale]] explicitement.\n\n> Note: le formateur de Yii a besoin de l'[extension intl de PHP](https://www.php.net/manual/fr/book.intl.php) pour prendre en charge la localisation des formats de données. Parce que différentes versions de la bibliothèque ICU compilées par PHP produisent des résultats de formatage différents, il est recommandé que vous utilisiez la même version de la bibliothèque ICU pour tous vos environnements. Pour plus de détails, reportez-vous au tutoriel [Configuration de votre environnement PHP pour l'internationalisation](tutorial-i18n.md#setup-environment).\n>\n> Si l'extension intl extension n'est pas installée, les données ne sont pas localisées. \n>\n> Notez que pour les valeurs de dates qui sont antérieures à l'année 1901, ou postérieures à 2038, la localisation n'est pas faite sur les systèmes 32 bits, même si l'extension intl est installée. Cela est dû au fait que, dans ce cas, ICU utilise des horodatages UNIX 32 bits pour les valeurs de date. \n"
  },
  {
    "path": "docs/guide-fr/output-pagination.md",
    "content": "Pagination\n==========\n\nLorsqu'il y a trop de données à afficher sur une seule page, une stratégie courante consiste à les afficher en de multiples pages, et sur chacune des pages, à n'afficher qu'une fraction réduite des données. Cette stratégie est connue sous le nom de *pagination*.\n\nYii utilise un objet [[yii\\data\\Pagination]] pour représenter les informations d'un schéma de pagination. En particulier :\n\n* [[yii\\data\\Pagination::$totalCount|nombre total (*total count*)]] spécifie le nombre total d'items de données. Notez que cela est ordinairement beaucoup plus élevé que le nombre d'items de données que l'on a besoin d'afficher sur une unique page.\n* [[yii\\data\\Pagination::$pageSize|taille de la page (*page size*)]] spécifie combien d'items de données chaque page contient. La valeur par défaut est 20.\n* [[yii\\data\\Pagination::$page|page courante (*current page*)]] donne la numéro de la page courante (qui commence à zéro). La valeur par défaut est 0, ce qui indique la première page. \n\nAvec un objet [[yii\\data\\Pagination]] pleinement spécifié, vous pouvez retrouver et afficher partiellement des données. Par exemple, si vous allez chercher des données dans une base de données, vous pouvez spécifier les clauses `OFFSET` et `LIMIT` de la requête de base de données avec les valeurs correspondantes fournies par l'objet pagination. Un exemple est présenté ci-dessous. \n\n```php\nuse yii\\data\\Pagination;\n\n// construit une requêt de base de données pour obtenir tous les articles dont le *status* vaut 1\n$query = Article::find()->where(['status' => 1]);\n\n// obtient le nombre total d'articles (mais ne va pas chercher les données articles pour le moment)\n$count = $query->count();\n\n// crée un objet pagination en lui passant le nombre total d'items\n$pagination = new Pagination(['totalCount' => $count]);\n\n// limite la requête en utilisant l'objet pagination et va chercher les articles\n$articles = $query->offset($pagination->offset)\n    ->limit($pagination->limit)\n    ->all();\n```\n\nMais quelle page d'article est retournée par l'exemple ci-dessus ? Cela dépend d'un paramètre de la requête nommé `page`. Par défaut, l'objet pagination essaye de définir le paramètre `page` avec la valeur de la [[yii\\data\\Pagination::$page|page courante (*current page*)]]. Si le paramètre n'est pas fourni, il prend la valeur par défaut `0`.\n\nPour faciliter la construction des élément de l'interface utilisateur qui prennent en charge la pagination, Yii fournit le composant graphique  [[yii\\widgets\\LinkPager]] qui affiche une liste de boutons de page sur lesquels l'utilisateur peut cliquer pour préciser quelle page de données doit être affichée. Ce composant graphique accepte en paramètre un objet pagination afin de savoir quelle est la page courante et combien de boutons de page afficher. Par exemple :\n\n```php\nuse yii\\widgets\\LinkPager;\n\necho LinkPager::widget([\n    'pagination' => $pagination,\n]);\n```\n\nSi vous voulez construire des éléments d'interface graphique à la main, vous pouvez utiliser [[yii\\data\\Pagination::createUrl()]] pour créer des URL qui conduisent à différentes pages. La méthode requiert un paramètre de page et crée une URL formatée correctement qui contient le paramètre de page. Par exemple :\n\n```php\n// spécifie la route que l'URL à créer doit utiliser,\n// si vous ne la spécifiez pas, la route actuellement requise est utilisée\n$pagination->route = 'article/index';\n\n// affiche : /index.php?r=article%2Findex&page=100\necho $pagination->createUrl(100);\n\n// affiche : /index.php?r=article%2Findex&page=101\necho $pagination->createUrl(101);\n```\n\n> Tip: vous pouvez personnaliser le nom du paramètre de requête `page` en configurant la propriété [[yii\\data\\Pagination::pageParam|pageParam]] lors de la création de l'objet pagination. \n"
  },
  {
    "path": "docs/guide-fr/output-sorting.md",
    "content": "Tri\n===\n\nLors de l'affichage de multiples lignes de données, on a souvent besoin des trier les données en fonction des valeurs de certaines colonnes spécifiées par l'utilisateur. Yii utilise l'objet [[yii\\data\\Sort]] pour représenter les information sur le schéma de triage. En particulier :\n\n* [[yii\\data\\Sort::$attributes|attributes]] spécifie les *attributs* grâce auxquels les données peuvent être triées. Un attribut peut être aussi simple qu'un [attribut de modèle](structure-models.md#attributes). Il peut aussi être un composite combinant le multiples attributs de modèles ou de colonnes de base de données. Nous apportons des informations plus détaillées dans la suite de cette page.\n* [[yii\\data\\Sort::$attributeOrders|attributeOrders]] fournit la direction de l'ordre de tri pour chacun des attributs.\n* [[yii\\data\\Sort::$orders|orders]] fournit les directions de tri en terme de colonnes de bas niveau. \n\nPour utiliser [[yii\\data\\Sort]], commencez par déclarer quels attributs peuvent être triés. Puis retrouvez les informations d'ordre de tri courantes de [[yii\\data\\Sort::$attributeOrders|attributeOrders]] ou [[yii\\data\\Sort::$orders|orders]], et utilisez-les pour personnaliser votre requête de données. Par exemple :\n\n```php\nuse yii\\data\\Sort;\n\n$sort = new Sort([\n    'attributes' => [\n        'age',\n        'name' => [\n            'asc' => ['first_name' => SORT_ASC, 'last_name' => SORT_ASC],\n            'desc' => ['first_name' => SORT_DESC, 'last_name' => SORT_DESC],\n            'default' => SORT_DESC,\n            'label' => 'Name',\n        ],\n    ],\n]);\n\n$articles = Article::find()\n    ->where(['status' => 1])\n    ->orderBy($sort->orders)\n    ->all();\n```\n\nDans l'exemple qui précède, deux attributs sont déclarés pour l'objet [[yii\\data\\Sort|Sort]] : `age` et `name`. \n\nL'attribut `age` est un attribut *simple* correspondant à l'attribut `age` de la classe d'enregistrement actif `Article`. Il équivaut à la déclaration suivante :\n\n```php\n'age' => [\n    'asc' => ['age' => SORT_ASC],\n    'desc' => ['age' => SORT_DESC],\n    'default' => SORT_ASC,\n    'label' => Inflector::camel2words('age'),\n]\n```\n\nL'attribut `name` est un attribut *composite* défini par `first_name` et `last_name` de la classe `Article`. Il est déclaré en utilisant la structure de tableau suivante :\n\n- Les éléments `asc` et `desc` spécifient comment trier selon l'attribut dans la direction croissante et décroissante, respectivement. Leurs valeurs représentent les colonnes réelles et les directions dans lesquelles les données sont triées. Vous pouvez spécifier une ou plusieurs colonnes pour préciser un tri simple ou un tri composite.\n- L'élément `default` spécifie la direction dans laquelle l'attribut doit être trié lorsqu'il est initialement requis. Sa valeur par défaut est l'ordre croissant, ce qui signifie que si les données n'ont pas été triées auparavant et que vous demandez leur tri par cet attribut, elles sont triées par cette attribut dans la direction croissante.\n- L'élément `label` spécifie quelle étiquette doit être utilisée lors de l'appel de [[yii\\data\\Sort::link()]] pour créer un lien de tri. Si cet élément n'est pas spécifié, la fonction [[yii\\helpers\\Inflector::camel2words()]] est appelée pour générer une étiquette à partir du nom de l'attribut. Notez que cette étiquette n'est pas encodée HTML.\n\n> Info: vous pouvez fournir la valeur de [[yii\\data\\Sort::$orders|orders]] à la requête de base de données pour construire sa clause `ORDER BY`. N'utilisez pas [[yii\\data\\Sort::$attributeOrders|attributeOrders]], parce que certains attributs peuvent être composites et ne peuvent pas être reconnus par la requête de base de données.\n\nVous pouvez appeler [[yii\\data\\Sort::link()]] pour générer un hyperlien sur lequel l'utilisateur peut cliquer pour demander le tri des données selon l'attribut spécifié. Vous pouvez aussi appeler [[yii\\data\\Sort::createUrl()]] pour créer une URL susceptible d'être triée. Par exemple :\n\n```php\n// spécifie la route que l'URL à créer doit utiliser,\n// si vous ne la spécifiez pas, la route couramment requise est utilisée \n$sort->route = 'article/index';\n\n// affiche des liens conduisant à trier par *name* (nom) et *age*, respectivement\necho $sort->link('name') . ' | ' . $sort->link('age');\n\n// affiche : /index.php?r=article%2Findex&sort=age\necho $sort->createUrl('age');\n```\n\n[[yii\\data\\Sort]] vérifie le paramètre `sort` pour savoir quels attributs sont requis pour le tri. Vous pouvez spécifier un ordre de tri par défaut via [[yii\\data\\Sort::defaultOrder]] lorsque le paramètre de requête est absent. Vous pouvez aussi personnaliser le nom du paramètre de requête en configurant la porpriété [[yii\\data\\Sort::sortParam|sortParam]].\n"
  },
  {
    "path": "docs/guide-fr/runtime-bootstrapping.md",
    "content": "Amorçage\n=============\n\nL'amorçage fait référence au processus de préparation de l'environnement avant qu'une application ne démarre, pour résoudre et traiter une requête d'entrée. L'amorçage se fait en deux endroits : le  [script d'entrée](structure-entry-scripts.md) et l'[application](structure-applications.md).\n\nDans le [script d'entrée](structure-entry-scripts.md), les classes de chargement automatique (*autoloaders*) pour différentes bibliothèques sont enregistrées. Cela inclut la classe de chargement automatique de Composer via son fichier `autoload.php` et la classe de chargement automatique de Yii via son fichier de classe `Yii`. Ensuite, le script d'entrée charge la [configuration](concept-configurations.md) de l'application et crée une instance d'[application](structure-applications.md).\n\nDans le constructeur de l'application, le travail d'amorçage suivant est effectué :\n\n1. La méthode [[yii\\base\\Application::preInit()|preInit()]] est appelée. Elle configure quelques propriétés de haute priorité de l'application, comme  [[yii\\base\\Application::basePath|le chemin de base (*basePath*)]].\n2. Le [[yii\\base\\Application::errorHandler|gestionnaire d'erreurs]] est enregistré.\n3. Les propriétés qui utilisent la configuration de l'application sont initialisées.\n4. La méthode [[yii\\base\\Application::init()|init()]] est appelée. À son tour elle appelle la méthode [[yii\\base\\Application::bootstrap()|bootstrap()]] pour exécuter les composants d'amorçage.\n   - Le fichier de manifeste des extensions `vendor/yiisoft/extensions.php` est inclus.\n   - Les[composants d'amorçage](structure-extensions.md#bootstrapping-classes) déclarés par les extensions sont créés et exécutés\n   - Les [composants d'application(structure-application-components.md) et/ou les [modules](structure-modules.md) déclarés dans la [propriété bootstrap](structure-applications.md#bootstrap) de l'application sont créés et exécutés.\n\nComme le travail d'amorçage doit être fait avant *chacune* des requêtes, il est très important de conserver ce processus aussi léger et optimisé que possible. \n\nÉvitez d'enregistrer trop de composants d'amorçage. Un composant d'amorçage est seulement nécessaire s'il doit participer à tout le cycle de vie de la prise en charge des requêtes. Par exemple,si un module a besoin d'enregistrer des règles d'analyse additionnelles, il doit être listé dans la [propriété bootstrap](structure-applications.md#bootstrap) afin que les nouvelles règles d'URL prennent effet avant qu'elles ne soient utilisées pour résoudre des requêtes.\n\nDans le mode production, activez un cache bytecode, tel que [PHP OPcache] ou [APC], pour minimiser le temps nécessaire à l'inclusion et à l'analyse des fichiers PHP.\n\n[PHP OPcache]: https://www.php.net/manual/fr/book.opcache.php\n[APC]: https://www.php.net/manual/fr/book.apcu.php\n\nQuelques applications volumineuses ont des [configurations](concept-configurations.md) d'application très complexes qui sont divisées en fichiers de configuration plus petits. Si c'est le cas, envisagez de mettre tout le tableau de configuration en cache et de le charger directement à partir cache avant la création de l'instance d'application dans le script d'entrée. \n"
  },
  {
    "path": "docs/guide-fr/runtime-handling-errors.md",
    "content": "Gestion des erreurs\n===================\n\nYii inclut un [[yii\\web\\ErrorHandler|gestionnaire d'erreur]] pré-construit qui rend la gestion des erreurs bien plus agréable qu'auparavant. En particulier, le gestionnaire d'erreurs de Yii possède les fonctionnalités suivantes pour améliorer la gestion des erreurs.\n\n* Toutes les erreurs PHP non fatales (p. ex. avertissements, notifications) sont converties en exceptions susceptibles d'être interceptées. \n* Les exceptions et les erreurs fatales sont affichées avec les informations détaillées de la pile des appels et les lignes de code source en mode *debug*.\n* Prise en charge de l'utilisation d'une  [action de contrôleur](structure-controllers.md#actions) dédiée à l'affichage des erreurs.\n* Prise en charge de différents formats de réponse d'erreur. \n\nLe  [[yii\\web\\ErrorHandler|gestionnaire d'erreur]] est activé par défaut. Vous pouvez le désactiver en définissant la constante `YII_ENABLE_ERROR_HANDLER` à `false` (faux) dans le [script d'entrée](structure-entry-scripts.md) de votre application.\n\n\n## Utilisation du gestionnaire d'erreurs <span id=\"using-error-handler\"></span>\n\nLe [[yii\\web\\ErrorHandler|gestionnaire d'erreurs]] est enregistré en tant que [composant d'application](structure-application-components.md) nommé `errorHandler`. Vous pouvez le configurer dans la configuration de l'application comme indiqué ci-dessous : \n\n```php\nreturn [\n    'components' => [\n        'errorHandler' => [\n            'maxSourceLines' => 20,\n        ],\n    ],\n];\n```\n\nAvec la configuration qui précède, le nombre de lignes de code source à afficher dans les pages d'exception est limité à 20. \n\nComme cela a déjà été dit, le gestionnaire d'erreur transforme toutes les erreurs PHP non fatales en exception susceptibles d'être interceptées. Cela signifie que vous pouvez utiliser le code suivant pour vous servir de cette gestion d'erreurs :\n\n```php\nuse Yii;\nuse yii\\base\\ErrorException;\n\ntry {\n    10/0;\n} catch (ErrorException $e) {\n    Yii::warning(\"Division by zero.\");\n}\n\n// l'exécution continue...\n```\n\nSi vous désirez afficher une page d'erreur disant à l'utilisateur que sa requête est invalide ou inattendue, vous pouvez simplement lever une [[yii\\web\\HttpException|exception HTTP]], comme l'exception [[yii\\web\\NotFoundHttpException]]. Le gestionnaire d'erreurs définit alors correctement le code d'état HTTP de la réponse et utilise une vue d'erreur appropriée pour afficher le message d'erreur. \n\n```php\nuse yii\\web\\NotFoundHttpException;\n\nthrow new NotFoundHttpException();\n```\n\n\n## Personnalisation de l'affichage des erreurs <span id=\"customizing-error-display\"></span>\n\nLe [[yii\\web\\ErrorHandler|gestionnaire d'erreurs]] ajuste l'affichage de l'erreur en tenant compte de la valeur de la constante  `YII_DEBUG`. Quand `YII_DEBUG` est égale à `true` (vrai) (ce qui signifie que le mode *debug* est activé), le gestionnaire d'erreurs affiche les exceptions avec le détail de la pile des appels et les lignes de code apportant de l'aide au débogage. Quand  `YII_DEBUG` est égale à `false` (faux), seule le message d'erreur est affiché pour ne pas révéler des informations sensibles sur l'application.\n\n> Info: si une exception est un descendant de la classe [[yii\\base\\UserException]], aucune pile des appels n'est affichée, et ceci indépendamment de la valeur `YII_DEBUG`. Cela tient au fait que de telles exceptions résultent d'erreurs commises par l'utilisateur et que les développeurs n'ont rien à corriger. \n\nPar défaut, le [[yii\\web\\ErrorHandler|gestionnaire d'erreurs]] affiche les erreurs en utilisant deux [vues](structure-views.md):\n\n* `@yii/views/errorHandler/error.php`: utilisée lorsque les erreurs doivent être affichées SANS les informations sur la pile des appels. Quand `YII_DEBUG` est égale à `false`, c'est la seule vue d'erreur à afficher.\n* `@yii/views/errorHandler/exception.php`: utilisée lorsque les erreurs doivent être affichées AVEC les informations sur la pile des appels. \n\nVous pouvez configurer les propriétés [[yii\\web\\ErrorHandler::errorView|errorView]] et [[yii\\web\\ErrorHandler::exceptionView|exceptionView]] du gestionnaire d'erreur pour utiliser vos propres vues afin de personnaliser l'affichage des erreurs. \n\n\n### Utilisation des actions d'erreurs <span id=\"using-error-actions\"></span>\n\nUne meilleure manière de personnaliser l'affichage des erreurs est d'utiliser des [actions](structure-controllers.md) d'erreur dédiées. Pour cela, commencez par configurer la propriété [[yii\\web\\ErrorHandler::errorAction|errorAction]] du composant `errorHandler` comme indiqué ci-après :\n\n```php\nreturn [\n    'components' => [\n        'errorHandler' => [\n            'errorAction' => 'site/error',\n        ],\n    ]\n];\n```\n\nLa propriété [[yii\\web\\ErrorHandler::errorAction|errorAction]] accepte une [route](structure-controllers.md#routes) vers une action. La configuration ci-dessus établit que lorsqu'une erreur doit être affichée sans information de la pile des appels, l'action `site/error` doit être exécutée.\n\nVous pouvez créer une action `site/error` comme ceci :\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actions()\n    {\n        return [\n            'error' => [\n                'class' => 'yii\\web\\ErrorAction',\n            ],\n        ];\n    }\n}\n```\n\nLe code ci-dessus définit l'action `error` en utilisant la classe [[yii\\web\\ErrorAction]] qui rend une erreur en utilisant une vue nommée `error`.\n\nEn plus d'utiliser  [[yii\\web\\ErrorAction]], vous pouvez aussi définir l'action `error` en utilisant une méthode d'action similaire à la suivante :\n\n```php\npublic function actionError()\n{\n    $exception = Yii::$app->errorHandler->exception;\n    if ($exception !== null) {\n        return $this->render('error', ['exception' => $exception]);\n    }\n}\n```\n\nVous devez maintenant créer un fichier de vue `views/site/error.php`. Dans ce fichier de vue, vous pouvez accéder aux variables suivantes si l'action d'erreur est définie en tant que [[yii\\web\\ErrorAction]]:\n\n* `name`: le nom de l'erreur ;\n* `message`: le message d'erreur ;\n* `exception`: l'objet exception via lequel vous pouvez retrouver encore plus d'informations utiles telles que le code d'état HTTP, le code d'erreur, la pile des appels de l'erreur, etc. \n\n> Info: si vous êtes en train d'utiliser le [modèle de projet *basic*](start-installation.md) ou le [modèle de projet avancé](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md), l'action d'erreur est la vue d'erreur sont déjà définies pour vous. \n\n> Note: si vous avez besoin de rediriger dans un gestionnaire d'erreur, faites-le de la manière suivante :\n>\n> ```php\n> Yii::$app->getResponse()->redirect($url)->send();\n> return;\n> ```\n\n\n### Personnalisation du format de la réponse d'erreur  <span id=\"error-format\"></span>\n\nLe gestionnaire d'erreurs affiche les erreurs en respectant le réglage de format de la [réponse](runtime-responses.md). Si le [[yii\\web\\Response::format|format de la réponse]] est `html`, il utilise la vue d'erreur ou d'exception pour afficher les erreurs, comme c'est expliqué dans la sous-section précédente. Pour les autres formats de réponse, le gestionnaire d'erreurs assigne la représentation de l'erreur sous forme de tableau à la propriété [[yii\\web\\Response::data]] qui est ensuite convertie dans le format désiré. Par exemple, si le format de la réponse est `json`, vous pourriez voir une réponse similaire à la suivante : \n\n```\nHTTP/1.1 404 Not Found\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"name\": \"Not Found Exception\",\n    \"message\": \"The requested resource was not found.\",\n    \"code\": 0,\n    \"status\": 404\n}\n```\n\nVous pouvez personnaliser le format de réponse d'erreur en répondant à l'événement `beforeSend` du composant `response` dans la configuration de l'application :\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'response' => [\n            'class' => 'yii\\web\\Response',\n            'on beforeSend' => function ($event) {\n                $response = $event->sender;\n                if ($response->data !== null) {\n                    $response->data = [\n                        'success' => $response->isSuccessful,\n                        'data' => $response->data,\n                    ];\n                    $response->statusCode = 200;\n                }\n            },\n        ],\n    ],\n];\n```\n\nLe code précédent formate la réponse d'erreur comme suit : \n\n```\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"success\": false,\n    \"data\": {\n        \"name\": \"Not Found Exception\",\n        \"message\": \"The requested resource was not found.\",\n        \"code\": 0,\n        \"status\": 404\n    }\n}\n```\n"
  },
  {
    "path": "docs/guide-fr/runtime-logging.md",
    "content": "Enregistrements des messages\n============================\n\nYii fournit une puissante base structurée (framework) d'enregistrement des messages qui est très personnalisable et extensible. En utilisant cette base structurée, vous pouvez facilement enregistrer des types variés de messages, les filtrer et les rassembler dans différentes cibles comme les bases de données et les courriels. \n\nL'utilisation de la base structurée d'enregistrement des messages de Yii nécessite de suivre les étapes suivantes :\n \n* Enregistrer les  [messages](#log-messages) à différents endroits de votre code ;\n* Configurer [cibles d'enregistrement](#log-targets) dans la configuration de l'application pour filtrer et exporter les messages enregistrés ; \n* Examiner les messages enregistrés, filtrés et exportés par les différentes cibles (p. ex. [débogueur de Yii](tool-debugger.md)).\n\nDans cette section, nous décrivons principalement les deux premières étapes. \n\n\n## Messages enregistrés <span id=\"log-messages\"></span>\n\nEnregistrer des messages est aussi simple que d'appeler une des méthodes suivantes :\n\n* [[Yii::debug()]]: enregistre un message pour garder une trace de comment un morceau de code fonctionne. Cela est utilisé principalement en développement.\n* [[Yii::info()]]: enregistre un message qui contient quelques informations utiles.\n* [[Yii::warning()]]: enregistre un message d'avertissement qui indique que quelque chose d'inattendu s'est produit.\n* [[Yii::error()]]: enregistre une erreur fatale qui doit être analysée dès que possible. \n\nCes méthodes enregistrent les messages à différents niveaux de sévérité et dans différentes catégories. Elles partagent la même signature `function ($message, $category = 'application')`, où `$message` représente le message à enregistrer, tandis que `$category` est la catégorie de ce message. Le code de l'exemple qui suit enregistre un message de trace dans la catégorie `application`:\n\n```php\nYii::debug('start calculating average revenue');\n```\n\n> Info: les messages enregistrés peuvent être des chaînes de caractères aussi bien que des données complexes telles que des tableaux ou des objets. Il est de la responsabilité des [cibles d'enregistrement](#log-targets) de traiter correctement ces messages. Par défaut, si un message enregistré n'est pas un chaîne de caractères, il est exporté comme une chaîne de caractères en appelant la méthode [[yii\\helpers\\VarDumper::export()]].\n\nPour mieux organiser et filtrer les messages enregistrés, il est recommandé que vous spécifiiez une catégorie appropriée pour chacun des messages. Vous pouvez choisir une schéma de nommage hiérarchisé pour les catégories, ce qui facilitera le filtrage des messages par les [cibles d'enregistrement](#log-targets)  sur la base de ces catégories. Un schéma de nommage simple et efficace est d'utiliser la constante magique `__METHOD__` de PHP dans les noms de catégorie. Par exemple :\n\n```php\nYii::debug('start calculating average revenue', __METHOD__);\n```\n\nLa constante magique `__METHOD__` est évaluée comme le nom de la méthode (préfixée par le nom pleinement qualifié de la classe), là où la constante apparaît. Par exemple, elle est égale à `'app\\controllers\\RevenueController::calculate'` si la ligne suivante est utilisée dans cette méthode. \n\n> Info: les méthodes d'enregistrement décrites plus haut sont en fait des raccourcis pour la méthode  [[yii\\log\\Logger::log()|log()]] de l'[[yii\\log\\Logger|objet logger]] qui est un singleton accessible via l'expression `Yii::getLogger()`. Lorsque suffisamment de messages ont été enregistrés, ou quand l'application se termine, l'objet *logger* appelle un [[yii\\log\\Dispatcher|distributeur de messages]] pour envoyer les messages enregistrés aux [cibles d'enregistrement](#log-targets).\n\n\n## Cibles d'enregistrement <span id=\"log-targets\"></span>\n\nUne cible d'enregistrement est une instance de la classe [[yii\\log\\Target]] ou d'une de ses classe filles. Elle filtre les messages enregistrés selon leur degré de sévérité et leur catégorie et les exporte vers un média donné. Par exemple, une [[yii\\log\\DbTarget|cible base données]] exporte les messages enregistrés et filtrés vers une base de données, tandis qu'une [[yii\\log\\EmailTarget|cible courriel]] exporte les messages vers l'adresse de courriel spécifiée.\n\nVous pouvez enregistrer plusieurs cibles d'enregistrement dans votre application en les configurant, via le [composant d'application](structure-application-components.md)`log` dans la configuration de l'application, de la manière suivante :\n\n```php\nreturn [\n    // le composant \"log\" doit être chargé lors de la période d'amorçage\n    'bootstrap' => ['log'],\n    // le composant \"log\" traite les messages avec un horodatage (timestamp). Définissez le fuseau horaire  PHP pour créer l'horodate correcte :\n    'timeZone' => 'America/Los_Angeles',\n    \n    'components' => [\n        'log' => [\n            'targets' => [\n                [\n                    'class' => 'yii\\log\\DbTarget',\n                    'levels' => ['error', 'warning'],\n                ],\n                [\n                    'class' => 'yii\\log\\EmailTarget',\n                    'levels' => ['error'],\n                    'categories' => ['yii\\db\\*'],\n                    'message' => [\n                       'from' => ['log@example.com'],\n                       'to' => ['admin@example.com', 'developer@example.com'],\n                       'subject' => 'Database errors at example.com',\n                    ],\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n> Note: le composant `log` doit être chargé durant le  [processus d'amorçage](runtime-bootstrapping.md) afin qu'il puisse distribuer les messages enregistrés aux cibles rapidement. C'est pourquoi il est listé dans le tableau `bootstrap` comme nous le montrons ci-dessus.\n\nDans le code précédent, deux cibles d'enregistrement sont enregistrées dan la propriété [[yii\\log\\Dispatcher::targets]] : \n\n* la première cible sélectionne les messages d'erreurs et les avertissements et les sauvegarde dans une table de base de données ; \n* la deuxième cible sélectionne les messages d'erreur dont le nom de la catégorie commence par `yii\\db\\`, et les envoie dans un courriel à la fois à `admin@example.com` et à `developer@example.com`.\n\nYii est fourni avec les cibles pré-construites suivantes. Reportez-vous à la documentation de l'API pour en savoir plus sur ces classes, en particulier comment les configurer et les utiliser. \n\n* [[yii\\log\\DbTarget]]: stocke les messages enregistrés dans une table de base de données.\n* [[yii\\log\\EmailTarget]]: envoie les messages enregistrés vers une adresse de courriel spécifiée préalablement. \n* [[yii\\log\\FileTarget]]: sauvegarde les messages enregistrés dans des fichiers. \n* [[yii\\log\\SyslogTarget]]: sauvegarde les messages enregistrés vers *syslog* en appelant la fonction PHP `syslog()`.\n\nDans la suite de ce document, nous décrivons les fonctionnalités communes à toutes les cibles d'enregistrement. \n\n  \n### Filtrage des messages <span id=\"message-filtering\"></span>\n\nVous pouvez configurer les propriétés [[yii\\log\\Target::levels|levels]] et [[yii\\log\\Target::categories|categories]] de chacune des cibles d'enregistrement pour spécifier les niveaux de sévérité et les catégories que la cible doit traiter. \n\nLa propriété [[yii\\log\\Target::levels|levels]] accepte un tableau constitué d'une ou plusieurs des valeurs suivantes :\n\n* `error`: correspondant aux messages enregistrés par [[Yii::error()]].\n* `warning`: correspondant aux messages enregistrés par [[Yii::warning()]].\n* `info`: correspondant aux messages enregistrés par [[Yii::info()]].\n* `trace`: correspondant aux messages enregistrés par [[Yii::debug()]].\n* `profile`: correspondant aux messages enregistrés par [[Yii::beginProfile()]] et [[Yii::endProfile()]], et qui sera expliqué en détails dans la sous-section [Profilage de la performance](#performance-profiling).\n\nSi vous ne spécifiez pas la propriété [[yii\\log\\Target::levels|levels]], cela signifie que la cible traitera les messages de *n'importe quel* niveau de sévérité. \n\nLa propriété [[yii\\log\\Target::categories|categories]] accepte un tableau constitué de noms ou de motifs de noms de catégorie de messages. Une cible ne traite \nque les messages dont la catégorie est trouvée ou correspond aux motifs de ce tableau. Un motif de nom de catégorie est un préfixe de nom de catégorie \nsuivi d'une astérisque `*`. Un nom de catégorie correspond à un motif de nom de catégorie s'il commence par le préfixe du motif. \nPar exemple, `yii\\db\\Command::execute` et `yii\\db\\Command::query` sont utilisés comme noms de catégorie pour les messages enregistrés dans la classe [[yii\\db\\Command]]. Ils correspondent tous deux au motif `yii\\db\\*`.\n\nSi vous ne spécifiez pas la propriété [[yii\\log\\Target::categories|categories]], cela signifie que le cible traite les messages de *n'importe quelle* catégorie. \n\nEn plus d'inscrire des catégories en liste blanche via la propriété [[yii\\log\\Target::categories|categories]], vous pouvez également inscrire certaines catégories\nen liste noire via la propriété [[yii\\log\\Target::except|except]]. Si la catégorie d'un message est trouvée ou correspond à un des motifs de cette propriété, ce message n'est PAS traité par la cible. \n \nLa configuration suivante de cible spécifie que la cible  traitera les messages d'erreur ou d'avertissement des catégories dont le nom correspond soit à `yii\\db\\*`, soit `yii\\web\\HttpException:*`, mais pas `yii\\web\\HttpException:404`.\n\n```php\n[\n    'class' => 'yii\\log\\FileTarget',\n    'levels' => ['error', 'warning'],\n    'categories' => [\n        'yii\\db\\*',\n        'yii\\web\\HttpException:*',\n    ],\n    'except' => [\n        'yii\\web\\HttpException:404',\n    ],\n]\n```\n\n> Info: lorsqu'une exception HTTP est capturée par le [gestionnaire d'erreur](runtime-handling-errors.md), un message d'erreur est enregistré avec un non de catégorie dont le format est `yii\\web\\HttpException:ErrorCode`. Par exemple, l'exception [[yii\\web\\NotFoundHttpException]] provoque un message d'erreur de catégorie `yii\\web\\HttpException:404`.\n\n\n### Formatage des messages <span id=\"message-formatting\"></span>\n\nLes cibles d'enregistrement exportent les messages enregistrés et filtrés dans un certain format. Par exemple, si vous installez une cible d'enregistrement de classe [[yii\\log\\FileTarget]], vous pouvez trouver un message enregistré similaire au suivant dans le fichier `runtime/log/app.log` file:\n\n```\n2014-10-04 18:10:15 [::1][][-][trace][yii\\base\\Module::getModule] Loading module: debug\n```\n\nPar défaut, les messages enregistrés sont formatés comme suit par la méthode [[yii\\log\\Target::formatMessage()]]:\n\n```\nHorodate [adresse IP][identifiant utilisateur][identifiant de session][niveau de sévérité][catégorie] Texte du message\n```\n\nVous pouvez personnaliser ce format en configurant la propriété [[yii\\log\\Target::prefix]] qui accepte une fonction PHP appelable qui retourne un message de préfixe personnalisé. Par exemple, le code suivant configure une cible d'enregistrement pour qu'elle préfixe chaque message enregistré avec l'identifiant de l'utilisateur courant (l'adresse IP et l'identifiant de session étant retirés pour des raisons de protection de la vie privée).\n\n```php\n[\n    'class' => 'yii\\log\\FileTarget',\n    'prefix' => function ($message) {\n        $user = Yii::$app->has('user', true) ? Yii::$app->get('user') : null;\n        $userID = $user ? $user->getId(false) : '-';\n        return \"[$userID]\";\n    }\n]\n```\n\nEn plus des préfixes de messages, les cibles d'enregistrement ajoutent aussi quelques informations de contexte à chaque lot de messages enregistrés. \nPar défaut, les valeurs de ces variables PHP globales sont incluses : `$_GET`, `$_POST`, `$_FILES`, `$_COOKIE`, `$_SESSION` et `$_SERVER`. Vous pouvez ajuster ce comportement en configurant la propriété [[yii\\log\\Target::logVars]] avec les noms des variables globales que vous voulez que la cible d'enregistrement inclue. Par exemple, la cible d'enregistrement suivante spécifie que seules les valeurs de la variable `$_SERVER` seront ajoutées aux messages enregistrés.\n```php\n[\n    'class' => 'yii\\log\\FileTarget',\n    'logVars' => ['_SERVER'],\n]\n```\n\nVous pouvez configurer  `logVars` comme un tableau vide pour désactiver totalement l'inclusion d'informations de contexte. Ou si vous voulez mettre en œuvre votre propre façon de fournir les informations contextuelles, vous pouvez redéfinir la méthode [[yii\\log\\Target::getContextMessage()]].\n\n\n### Niveaux de la trace de message <span id=\"trace-level\"></span>\n\nLors du développement, vous cherchez souvent à voir d'où provient chacun des messages enregistrés. Cela est possible en configurant la propriété [[yii\\log\\Dispatcher::traceLevel|traceLevel]] du composant`log` de la façon suivante :\n\n```php\nreturn [\n    'bootstrap' => ['log'],\n    'components' => [\n        'log' => [\n            'traceLevel' => YII_DEBUG ? 3 : 0,\n            'targets' => [...],\n        ],\n    ],\n];\n```\n\nLa configuration de l'application ci-dessus statue que le [[yii\\log\\Dispatcher::traceLevel|niveau de trace ]] sera  3 si `YII_DEBUG` est activé et 0 si `YII_DEBUG` est désactivé. Cela veut dire que, si `YII_DEBUG` est activé, au plus trois niveaux de la pile des appels seront ajoutés à chaque message enregistré, là où le messages est enregistré ; et, si  `YII_DEBUG`  est désactivé, aucune information de la pile des appels ne sera incluse. \n\n> Info: obtenir les informations de la pile des appels n'a rien de trivial. En conséquence, vous ne devriez utiliser cette fonctionnalité que durant le développement ou le débogage d'une application. \n\n\n### Purge et exportation des messages <span id=\"flushing-exporting\"></span>\n\nComme nous l'avons dit plus haut, les messages enregistrés sont conservés dans un tableau par l'[[yii\\log\\Logger|objet *logger*]]. Pour limiter la consommation de mémoire par ce tableau, l'objet *logger* purge les messages enregistrés vers les [cibles d'enregistrement](#log-targets) chaque fois que leur nombre atteint une certaine valeur. Vous pouvez personnaliser ce nombre en configurant la propriété [[yii\\log\\Dispatcher::flushInterval|flushInterval]] du composant `log` :\n\n\n```php\nreturn [\n    'bootstrap' => ['log'],\n    'components' => [\n        'log' => [\n            'flushInterval' => 100,   // default is 1000\n            'targets' => [...],\n        ],\n    ],\n];\n```\n\n> Info: la purge des messages intervient aussi lorsque l'application se termine, ce qui garantit que les cibles d'enregistrement reçoivent des messages enregistrés complets. \n\nLorsque l'[[yii\\log\\Logger|objet *logger*]] purge les messages enregistrés vers les [cibles d'enregistrement](#log-targets), ils ne sont pas exportés immédiatement. Au lieu de cela, l'exportation des messages ne se produit que lorsque la cible d'enregistrement a accumulé un certain nombre de messages filtrés. Vous pouvez personnaliser ce nombre en configurant la propriété [[yii\\log\\Target::exportInterval|exportInterval]] de chacune des [cibles d'enregistrement](#log-targets), comme ceci :\n\n```php\n[\n    'class' => 'yii\\log\\FileTarget',\n    'exportInterval' => 100,  // default is 1000\n]\n```\n\nÀ cause des niveaux de purge et d'exportation, par défaut, lorsque vous appelez `Yii::debug()` ou toute autre méthode d'enregistrement, vous ne voyez PAS immédiatement le message enregistré dans la cible. Cela peut représenter un problème pour pour certaines applications de console qui durent longtemps. Pour faire en sorte que les messages apparaissent immédiatement dans les cibles d'enregistrement, vous devriez définir les propriétés [[yii\\log\\Dispatcher::flushInterval|flushInterval]] et [[yii\\log\\Target::exportInterval|exportInterval]] toutes deux à 1, comme montré ci-après :\n\n```php\nreturn [\n    'bootstrap' => ['log'],\n    'components' => [\n        'log' => [\n            'flushInterval' => 1,\n            'targets' => [\n                [\n                    'class' => 'yii\\log\\FileTarget',\n                    'exportInterval' => 1,\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n> Note: la purge et l'exportation fréquentes de vos messages dégradent la performance de votre application. \n\n\n### Activation, désactivation des cibles d'enregistrement <span id=\"toggling-log-targets\"></span>\n\nVous pouvez activer ou désactiver une cible d'enregistrement en configurant sa propriété [[yii\\log\\Target::enabled|enabled]]. Vous pouvez le faire via la configuration de la cible d'enregistrement ou en utilisant l'instruction suivante dans votre code PHP :\n\n```php\nYii::$app->log->targets['file']->enabled = false;\n```\n\nLe code ci-dessus, nécessite que nommiez une cible `file`, comme montré ci-dessous en utilisant des clés sous forme de chaînes de caractères  dans le tableau `targets` :\n\n```php\nreturn [\n    'bootstrap' => ['log'],\n    'components' => [\n        'log' => [\n            'targets' => [\n                'file' => [\n                    'class' => 'yii\\log\\FileTarget',\n                ],\n                'db' => [\n                    'class' => 'yii\\log\\DbTarget',\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n\n### Création d'une cible d'enregistrement <span id=\"new-targets\"></span>\n\nLa création d'une classe de cible d'enregistrement est très simple. Vous devez essentiellement implémenter [[yii\\log\\Target::export()]] en envoyant le contenu du tableau des [[yii\\log\\Target::messages]] vers un média désigné. Vous pouvez appeler la méthode [[yii\\log\\Target::formatMessage()]] pour formater chacun des messages. Pour plus de détails, reportez-vous à n'importe quelle classe de cible de messages incluse dans la version de Yii. \n\n> Tip: au lieu de créer vos propres journaliseurs, vous pouvez essayez n'importe quel journaliseur compatible PSR-3 tel que [Monolog](https://github.com/Seldaek/monolog) en utilisant \n  [PSR log target extension](https://github.com/samdark/yii2-psr-log-target).\n\n## Profilage de la performance <span id=\"performance-profiling\"></span>\n\nLe profilage de la performance est un type particulier d'enregistrement de messages qui est utilisé pour mesurer le temps d'exécution de certains blocs de code et pour déterminer les goulots d'étranglement. Par exemple, la classe [[yii\\db\\Command]] utilise le profilage de performance pour connaître le temps d'exécution de chacune des requêtes de base de données. \n\nPour utiliser le profilage de la  performance, commencez par identifier les blocs de code qui ont besoin d'être profilés. Puis, entourez-les de la manière suivante :\n\n```php\n\\Yii::beginProfile('myBenchmark');\n\n...le bloc de code à profiler...\n\n\\Yii::endProfile('myBenchmark');\n```\n\noù `myBenchmark` représente un jeton unique identifiant un bloc de code. Plus tard, lorsque vous examinez le résultat du profilage, vous pouvez utiliser ce jeton pour localiser le temps d'exécution du bloc correspondant. \n\nIl est important de vous assurer que les paires  `beginProfile` et `endProfile` sont correctement imbriquées.\nPar exemple,\n\n```php\n\\Yii::beginProfile('block1');\n\n    // du code à profiler\n\n    \\Yii::beginProfile('block2');\n        // un autre bloc de code à profiler\n    \\Yii::endProfile('block2');\n\n\\Yii::endProfile('block1');\n```\n\nSi vous omettez `\\Yii::endProfile('block1')` ou inversez l'ordre de `\\Yii::endProfile('block1')` et de `\\Yii::endProfile('block2')`, le profilage de performance ne fonctionnera pas. \n\nPour chaque bloc de code profilé, un message est enregistré avec le niveau de sévérité `profile`. Vous pouvez configurer une [cible d'enregistrement](#log-targets) pour collecter de tels messages et les exporter. L'outil [*Yii debugger*](tool-debugger.md) comporte un panneau d'affichage pré-construit des résultats de profilage.\n"
  },
  {
    "path": "docs/guide-fr/runtime-overview.md",
    "content": "Vue d'ensemble\n==============\n\nÀ chaque fois qu'une application Yii prend en charge une requête, elle entreprend un flux de travail similaire. \n\n1. Un utilisateur effectue une requête auprès du [script d'entrée](structure-entry-scripts.md) `web/index.php`.\n2. Le script d'entrée charge la [configuration](concept-configurations.md) de l'application et crée une instance d'[application](structure-applications.md) pour prendre en charge la requête.\n3. L'application résoud la [route](runtime-routing.md) requise avec l'aide du composant d'application [request](runtime-requests.md).\n4. L'application crée une instance de [contrôleur](structure-controllers.md) pour prendre en charge le requête.\n5. Le contrôleur crée une instance d'[action](structure-controllers.md) et exécute les filtres de l'action.\n6. Si un [filtre](structure-filters.md) échoue, l'exécution de l'action est annulée.\n7. Si tous les filtres réussissent l'action est exécutée.\n8. L'action charge un [modèle](structure-models.md) de données, possiblement à partir d'une base de données.\n9. L'action rend une [vue](structure-views.md), en lui passant le modèle de données.\n10. Le résultat rendu est retourné au composant d'application [response](runtime-responses.md).\n11. Le composant *response* envoye le résultat rendu au navigateur de l'utilisateur. \nLe diagramme ci-dessous illustre comment une application prend une requête en charge. \n\n![Cycle de vie d'une requête](images/request-lifecycle.png)\n\nDans cette section, nous décrivons en détails comment se déroulent quelques unes de ces étapes. \n"
  },
  {
    "path": "docs/guide-fr/runtime-requests.md",
    "content": "Requêtes\n========\n\nLes requêtes faites à l'application sont représentées en terme d'objets [[yii\\web\\Request]] qui fournissent des informations telles que les paramètres de requête, les entêtes HTTP, les cookies, etc. \nPour une requête donnée, vous avez accès au [composant d'application](structure-application-components.md)`request` qui, par défaut,  est une instance de [[yii\\web\\Request]]. \nDans cette section, nous décrivons comment utiliser ce composant dans vos applications.\n\n\n\n## Paramètres de requête <span id=\"request-parameters\"></span>\n\nPour obtenir les paramètres de requête, vous pouvez appeler les méthodes  [[yii\\web\\Request::get()|get()]] et [[yii\\web\\Request::post()|post()]] du composant `request` component. \nElles retournent les valeurs de `$_GET` et `$_POST`, respectivement. Pas exemple :\n\n```php\n$request = Yii::$app->request;\n\n$get = $request->get(); \n// équivalent à : $get = $_GET;\n\n$id = $request->get('id');   \n// équivalent à : $id = isset($_GET['id']) ? $_GET['id'] : null;\n\n$id = $request->get('id', 1);   \n// équivalent à : $id = isset($_GET['id']) ? $_GET['id'] : 1;\n\n$post = $request->post(); \n// équivalent à : $post = $_POST;\n\n$name = $request->post('name');   \n// equivalent to: $name = isset($_POST['name']) ? $_POST['name'] : null;\n\n$name = $request->post('name', '');   \n// équivalent à : $name = isset($_POST['name']) ? $_POST['name'] : '';\n```\n\n> Info: plutôt que d'accéder directement à `$_GET` et `$_POST` pour récupérer les paramètres de requête, il est recommandé de les obtenir via le composant `request` comme indiqué ci-dessus. \nCela rend l'écriture des tests plus facile parce que vous pouvez créer un simulacre de composant 'request' avec des données de requête factices.  \n\n\nLorsque vous mettez en œuvre des [API pleinement REST](rest-quick-start.md), vous avez souvent besoin de récupérer les paramètres qui sont soumis via les [méthodes de requête](#request-methods) PUT, PATCH ou autre . \nVous pouvez obtenir ces paramètres en appelant la méthode [[yii\\web\\Request::getBodyParam()]]. par exemple : \n\n\n```php\n$request = Yii::$app->request;\n\n// retourne tous les paramètres\n$params = $request->bodyParams;\n\n// retourne le paramètre  \"id\"\n$param = $request->getBodyParam('id');\n```\n\n> Info: à la différence des paramètres  de `GET`, les paramètres soumis via `POST`, `PUT`, `PATCH` etc. sont envoyés dans le corps de la requête. \nLe composant `request` analyse ces paramètres lorsque vous y accédez via les méthodes décrites ci-dessus. \nVous pouvez personnaliser la manière dont ces paramètres sont analysés en configurant la propriété [[yii\\web\\Request::parsers]].\n  \n\n## Méthodes de requête <span id=\"request-methods\"></span>\n\nVous pouvez obtenir la méthode HTTP utilisée par la requête courante via l'expression `Yii::$app->request->method`. \nUn jeu entier de propriétés booléennes est également fourni pour que vous puissiez déterminer le type de la méthode courante. Par exemple :\n\n\n```php\n$request = Yii::$app->request;\n\nif ($request->isAjax) { /* la méthode de requête est requête AJAX */ }\nif ($request->isGet)  { /* la méthode de requête est requête GET */ }\nif ($request->isPost) { /* la méthode de requête est requête POST */ }\nif ($request->isPut)  { /* la méthode de requête est requête PUT */ }\n```\n\n## URL de requête <span id=\"request-urls\"></span>\n\nLe composant `request` fournit plusieurs manières d'inspecter l'URL couramment requise.\n\nEn supposant que l'URL requise soit `https://example.com/admin/index.php/product?id=100`, vous pouvez obtenir différentes parties de cette URL comme c'est résumé ci-dessous :\n\n\n* [[yii\\web\\Request::url|url]]: retourne`/admin/index.php/product?id=100`, qui est l'URL sans la partie hôte. \n* [[yii\\web\\Request::absoluteUrl|absoluteUrl]]: retourne `https://example.com/admin/index.php/product?id=100`, qui est l'URL complète y compris la partie hôte.\n* [[yii\\web\\Request::hostInfo|hostInfo]]: retourne `https://example.com`, qui est la partie hôte de l'URL.\n* [[yii\\web\\Request::pathInfo|pathInfo]]: retourne `/product`, qui est la partie après le script d'entrée \net avant le point d'interrogation (chaîne de requête).\n* [[yii\\web\\Request::queryString|queryString]]: retourne `id=100`, qui est la partie après le point d'interrogation.\n* [[yii\\web\\Request::baseUrl|baseUrl]]: retourne `/admin`, qui est la partie après l'hôte \net avant le nom du script d'entrée. \n* [[yii\\web\\Request::scriptUrl|scriptUrl]]: retourne `/admin/index.php`, qui set l'URL sans le chemin et la chaîne de requête. \n* [[yii\\web\\Request::serverName|serverName]]: retourne `example.com`, qui est le nom d'hôte dans l'URL.\n* [[yii\\web\\Request::serverPort|serverPort]]: retourne 80, qui est le numéro de port utilisé par le serveur  Web.\n\n\n\n## Entêtes HTTP  <span id=\"http-headers\"></span> \n\nVous pouvez obtenir les entêtes HTTP via la [[yii\\web\\HeaderCollection|collection d'entêtes]] qui est retournée par la propriété [[yii\\web\\Request::headers]]. Par exemple :\n\n```php\n// $headers est un objet   yii\\web\\HeaderCollection \n$headers = Yii::$app->request->headers;\n\n// retourne la valeur de l'entête  Accept\n$accept = $headers->get('Accept');\n\nif ($headers->has('User-Agent')) { /* il existe un entête User-Agent  */ }\n```\n\n\nLe composant `request` fournit aussi la prise en charge de l'accès rapide à quelques entêtes couramment utilisés. Cela inclut :\n\n* [[yii\\web\\Request::userAgent|userAgent]]: retourne la valeur de l'entête  `User-Agent`.\n* [[yii\\web\\Request::contentType|contentType]]: retourne la valeur de l'entête `Content-Type` qui indique le type MIME des données dans le corps de la requête. \n* [[yii\\web\\Request::acceptableContentTypes|acceptableContentTypes]]: retourne les types MIME acceptés par l'utilisateur. \nLes types retournés sont classés par ordre de score de qualité. Les types avec les plus hauts scores sont retournés en premier. \n* [[yii\\web\\Request::acceptableLanguages|acceptableLanguages]]: retourne les langues acceptées par l'utilisateur.\nLes langues retournées sont classées par niveau de préférence. \nLe premier élément représente la langue préférée. Si votre application prend en charge plusieurs langues et que vous voulez afficher des pages dans la langue préférée de l'utilisateur, vous pouvez utiliser la méthode de négociation de la langue [[yii\\web\\Request::getPreferredLanguage()]].\nCette méthode accepte une liste des langues prises en charge par votre application, la compare avec les [[yii\\web\\Request::acceptableLanguages (langues acceptées)|acceptableLanguages]],\n et retourne la langue la plus appropriée. \n\n\n> Tip: vous pouvez également utiliser le filtre [[yii\\filters\\ContentNegotiator|ContentNegotiator]] pour déterminer dynamiquement quel type de contenu \net quelle langue utiliser dans la réponse. \nLe filtre met en œuvre la négociation de contenu en plus des propriétés \net  méthodes décrites ci-dessus. \n\n\n\n## Informations sur le client <span id=\"client-information\"></span>\n\nVous pouvez obtenir le nom d'hôte et l'adresse IP de la machine cliente via  [[yii\\web\\Request::userHost|userHost]] \net [[yii\\web\\Request::userIP|userIP]], respectivement. \nPar exemple :\n\n\n```php\n$userHost = Yii::$app->request->userHost;\n$userIP = Yii::$app->request->userIP;\n\n## Mandataires de confiance et entêtes <span id=\"trusted-proxies\"></span>\n\nDans la section précédente, vous avez vu comment obtenir des informations sur l'utilisateur comme le nom d'hôte et l'adresse IP.\nCela fonctionne sans aucune configuration complémentaire dans une configuration normale dans laquelle une unique serveur Web est utilisé pour servir le site.\nCependant, si votre application s'exécute derrière un mandataire inverse, vous devez compléter la configuration pour retrouver ces informations car le client direct est désormais le mandataire \net l'adresse IP de l'utilisateur est passée à l'application Yii par une entête établie par le mandataire. \n\n\nVous ne devez pas faire confiance aveuglément aux entêtes fournies par un mandataire sauf si vous faites explicitement confiance à ce mandataire.\nDepuis sa version 2.0.13,  Yii prend en charge la configuration des mandataires de confiance via les propriétés \n[[yii\\web\\Request::trustedHosts|trustedHosts]],\n[[yii\\web\\Request::secureHeaders|secureHeaders]], \n[[yii\\web\\Request::ipHeaders|ipHeaders]] and\n[[yii\\web\\Request::secureProtocolHeaders|secureProtocolHeaders]]\ndu composant `request`.\n\nCe qui suit est la configuration d'une requête pour une application qui s'exécute derrière une tableau de mandataires inverses \nsitués dans le réseau IP `10.0.2.0/24` IP network:\n\n```php\n'request' => [\n    // ...\n    'trustedHosts' => [\n        '10.0.2.0/24',\n    ],\n],\n```\n\nL'adresse IP est envoyée par défaut par le mandataire dans l'entête `X-Forwarded-For` , et le protocole (`http` ou `https`) est envoyé dans `X-Forwarded-Proto`.\n\nDans le cas où vos mandataires utilisent différentes entêtes, vous pouvez utiliser la configuration de la requête  pour les ajuster, p. ex. :\n\n```php\n'request' => [\n    // ...\n    'trustedHosts' => [\n        '10.0.2.0/24' => [\n            'X-ProxyUser-Ip',\n            'Front-End-Https',\n        ],\n    ],\n    'secureHeaders' => [\n        'X-Forwarded-For',\n        'X-Forwarded-Host',\n        'X-Forwarded-Proto',\n        'X-Proxy-User-Ip',\n        'Front-End-Https',\n    ],\n    'ipHeaders' => [\n        'X-Proxy-User-Ip',\n    ],\n    'secureProtocolHeaders' => [\n        'Front-End-Https' => ['on']\n    ],\n],\n```\n\nAvec la configuration précédente, toutes les entêtes listées dans  `secureHeaders` sont filtrées de la requête à l'exception des entêtes `X-ProxyUser-Ip` et `Front-End-Https` pour le cas où la requête est élaborée par le mandataire.\nDans un tel cas, le précédent est utilisé pour retrouver l'adresse IP de l'utilisateur comme configuré dans  `ipHeaders` et le dernier est utilisé pour déterminer le résultat de [[yii\\web\\Request::getIsSecureConnection()]].\n\n```\n"
  },
  {
    "path": "docs/guide-fr/runtime-responses.md",
    "content": "Réponses\n=========\n\nQuand une application a terminé la prise en charge d'une [requête](runtime-requests.md), elle génère un objet [[yii\\web\\Response|response]] et l'envoie à l'utilisateur final. L'objet `response` contient des informations telles que le code d'état HTTP, les entêtes HTTP et le corps. Le but ultime du développement d'applications Web est essentiellement du construire de tels objets `response` pour des requêtes variées. \n\nDans la plupart des cas, vous devez travailler avec le [composant d'application](structure-application-components.md) `response` qui, par défaut, est une instance de [[yii\\web\\Response]]. Néanmoins, Yii vous permet également de créer vos propres objets `response` et de les envoyer à l'utilisateur final comme nous l'expliquons dans ce qui suit.\n\nDans cette section, nous décrivons comment composer et envoyer des réponses à l'utilisateur final. \n\n\n## Code d'état <span id=\"status-code\"></span>\n\nUne des premières choses que vous devez faire lorsque vous construisez une réponse est de déclarer si la requête a été correctement prise en charge ou pas. Cela se fait en définissant la propriété  \n[[yii\\web\\Response::statusCode|code d'état]]\nqui peut prendre un des [codes d'état HTTP](https://tools.ietf.org/html/rfc2616#section-10) valides. Par exemple, pour indiquer que la requête a été prise en charge avec succès, vous pouvez définir le code à 200, comme ceci :\n\n```php\nYii::$app->response->statusCode = 200;\n```\n\nNéanmoins, dans la plupart des cas, vous n'avez pas besoin de définir ce code explicitement. Cela tient au fait que la valeur par défaut de [[yii\\web\\Response::statusCode]] est 200. Et, si vous voulez indiquer que la prise en charge de la requête a échoué vous pouvez lever une exception appropriée comme ceci :\n\n```php\nthrow new \\yii\\web\\NotFoundHttpException;\n```\n\nLorsque le  [gestionnaire d'erreurs](runtime-handling-errors.md) intercepte l'exception, il extraie le code d'état de l'exception et l'assigne à la réponse. Concernant l'exception [[yii\\web\\NotFoundHttpException]] ci-dessus, elle est associée au code d'état HTTP 404. Les exception HTTP suivantes sont prédéfinies dans Yii :\n\n* [[yii\\web\\BadRequestHttpException]]: code d'état 400.\n* [[yii\\web\\ConflictHttpException]]: code d'état 409.\n* [[yii\\web\\ForbiddenHttpException]]: code d'état 403.\n* [[yii\\web\\GoneHttpException]]: code d'état 410.\n* [[yii\\web\\MethodNotAllowedHttpException]]: code d'état 405.\n* [[yii\\web\\NotAcceptableHttpException]]: code d'état 406. \n* [[yii\\web\\NotFoundHttpException]]: code d'état 404.\n* [[yii\\web\\ServerErrorHttpException]]: code d'état 500.\n* [[yii\\web\\TooManyRequestsHttpException]]: code d'état 429.\n* [[yii\\web\\UnauthorizedHttpException]]: code d'état 401.\n* [[yii\\web\\UnsupportedMediaTypeHttpException]]: code d'état 415.\n\nSi l'exception que vous voulez lever ne fait pas partie de cette liste, vous pouvez en créer une en étendant la classe [[yii\\web\\HttpException]], ou en en levant une à laquelle vous passez directement le code d'état. Par exemple :\n \n```php\nthrow new \\yii\\web\\HttpException(402);\n```\n\n\n## Entêtes HTTP  <span id=\"http-headers\"></span> \n\nVous pouvez envoyer les entêtes HTTP en manipulant la [[yii\\web\\Response::headers|collection d'entêtes]] dans le composant `response`. Par exemple :\n\n```php\n$headers = Yii::$app->response->headers;\n\n// ajoute un entête  Pragma . L'entête Pragma existant n'est PAS écrasé.\n$headers->add('Pragma', 'no-cache');\n\n// définit un entête Pragma. Tout entête Pragma existant est supprimé.\n$headers->set('Pragma', 'no-cache');\n\n// retire un (des) entêtes Pragma et retourne les valeurs de l'entête Pragma retiré dans un tableau\n$values = $headers->remove('Pragma');\n```\n\n> Info: les noms d'entête ne sont pas sensibles à la casse. Les nouveaux entêtes enregistrés ne sont pas envoyés à l'utilisateur tant que la méthode [[yii\\web\\Response::send()]] n'est pas appelée.\n\n\n## Corps de la réponse <span id=\"response-body\"></span>\n\nLa plupart des réponses doivent avoir un corps qui transporte le contenu que vous voulez montrer à l'utilisateur final. \n\nSi vous disposez déjà d'une chaîne de caractères formatée pour le corps, vous pouvez l'assigner à la propriété [[yii\\web\\Response::content]] de la réponse. Par exemple :\n\n```php\nYii::$app->response->content = 'hello world!';\n```\n\nSi vos données doivent être formatées avant l'envoi à l'utilisateur final, vous devez définir les propriétés [[yii\\web\\Response::format|format]] et [[yii\\web\\Response::data|data]]. La propriété [[yii\\web\\Response::format|format]] spécifie dans quel format les  [[yii\\web\\Response::data|données]] doivent être formatées. Par exemple :\n\n```php\n$response = Yii::$app->response;\n$response->format = \\yii\\web\\Response::FORMAT_JSON;\n$response->data = ['message' => 'hello world'];\n```\n\nDe base, Yii prend en charge les formats suivants, chacun mis en œuvre par une classe [[yii\\web\\ResponseFormatterInterface|formatter]]. Vous pouvez personnaliser les formateurs ou en ajouter de nouveaux en configurant la propriété [[yii\\web\\Response::formatters]].\n\n* [[yii\\web\\Response::FORMAT_HTML|HTML]]: mise en œuvre par [[yii\\web\\HtmlResponseFormatter]].\n* [[yii\\web\\Response::FORMAT_XML|XML]]: mise en œuvre par [[yii\\web\\XmlResponseFormatter]].\n* [[yii\\web\\Response::FORMAT_JSON|JSON]]: mise en œuvre par [[yii\\web\\JsonResponseFormatter]].\n* [[yii\\web\\Response::FORMAT_JSONP|JSONP]]: mise en œuvre par [[yii\\web\\JsonResponseFormatter]].\n* [[yii\\web\\Response::FORMAT_RAW|RAW]]: utilisez ce format si vous voulez envoyer la réponse directement sans lui appliquer aucun formatage. \n\nBien que le corps de la réponse puisse être défini explicitement comme montré ci-dessus, dans la plupart des cas, vous pouvez le définir implicitement en utilisant la valeur retournée par les méthodes d'[action](structure-controllers.md). Un cas d'usage courant ressemble à ceci :\n \n```php\npublic function actionIndex()\n{\n    return $this->render('index');\n}\n```\n\nL'action `index` ci-dessus retourne le résultat du rendu de la vue `index`. La valeur de retour est interceptée par le composant  `response`, formatée et envoyée à l'utilisateur final.\n\nParce que le format par défaut de la réponse est [[yii\\web\\Response::FORMAT_HTML|HTML]], vous devez seulement retourner un chaîne de caractères dans une méthode d'action. Si vous utilisez un format de réponse différent, vous devez le définir avant de retourner les donnés. Par exemple :\n\n```php\npublic function actionInfo()\n{\n    \\Yii::$app->response->format = \\yii\\web\\Response::FORMAT_JSON;\n    return [\n        'message' => 'hello world',\n        'code' => 100,\n    ];\n}\n```\n\nComme mentionné plus haut, en plus d'utiliser le composant d'application `response`, vous pouvez également créer vos propres objets `response` et les envoyer à l'utilisateur final. Vous pouvez faire cela en retournant un tel objet dans une méthode d'action, comme le montre l'exemple suivant :\n\n```php\npublic function actionInfo()\n{\n    return \\Yii::createObject([\n        'class' => 'yii\\web\\Response',\n        'format' => \\yii\\web\\Response::FORMAT_JSON,\n        'data' => [\n            'message' => 'hello world',\n            'code' => 100,\n        ],\n    ]);\n}\n```\n\n> Note : si vous êtes en train de créer vos propres objets `response`, vous ne pourrez par bénéficier des configurations que vous avez établies pour le composant `response` dans la configuration de l'application. Vous pouvez néanmoins, utiliser l'[injection de dépendances](concept-di-container.md) pour appliquer une configuration commune à vos nouveaux objets `response`. \n\n\n## Redirection du navigateur <span id=\"browser-redirection\"></span>\n\nLa redirection du navigateur s'appuie sur l'envoi d'un entête HTTP `Location`. Comme cette fonctionnalité est couramment utilisée, Yii fournit une prise en charge spéciale pour cela.\n\nVous pouvez rediriger le navigateur sur une URL en appelant la méthode [[yii\\web\\Response::redirect()]]. Cette méthode définit l'entête `Location` approprié avec l'URL donnée et retourne l'objet `response` lui-même. Dans une méthode d'action vous pouvez appeler sa version abrégée [[yii\\web\\Controller::redirect()]]. Par exemple :\n\n```php\npublic function actionOld()\n{\n    return $this->redirect('https://example.com/new', 301);\n}\n```\n\nDans le code précédent, la méthode d'action retourne le résultat de la méthode `redirect()`. Comme expliqué ci-dessus, l'objet `response` retourné par une méthode d'action est utilisé en tant que réponse à envoyer à l'utilisateur final.\n\nDans des endroits autres que les méthodes d'action, vous devez appeler la méthode [[yii\\web\\Response::redirect()]] directement, suivi d'un appel chaîné à la méthode [[yii\\web\\Response::send()]] pour garantir qu'aucun contenu supplémentaire ne sera ajouté à la réponse. \n\n```php\n\\Yii::$app->response->redirect('https://example.com/new', 301)->send();\n```\n\n> Info: par défaut la méthode [[yii\\web\\Response::redirect()]] définit le code d'état à 302 pour indiquer au navigateur que la ressource requise est *temporairement* située sous un URI différent. Vous pouvez passer un code 301 pour dire au navigateur que la ressource a été déplacée *de manière permanente*.\n\nLorsque la requête courante est une requête AJAX, l'envoi d'un entête `Location` ne provoque pas automatiquement une redirection du navigateur. Pour pallier ce problème, la méthode [[yii\\web\\Response::redirect()]] définit un entête  `X-Redirect` avec l'URL de redirection comme valeur. Du côté client, vous pouvez écrire un code JavaScript pour lire l'entête et rediriger le navigateur sur l'URL transmise. \n\n> Info: Yii est fourni avec un fichier JavaScript `yii.js` qui fournit un jeu d'utilitaires JavaScript, y compris l'utilitaire de redirection basé sur l'entête `X-Redirect`. Par conséquent, si vous utilisez ce fichier JavaScript (en enregistrant le paquet de ressources [[yii\\web\\YiiAsset]] ), vous n'avez rien à écrire pour prendre en charge la redirection AJAX. \nDe l'information complémentaire sur  `yii.js` est disponible à la [section Scripts client](output-client-scripts.md#yii.js).\n\n## Envoi de fichiers <span id=\"sending-files\"></span>\n\nComme la redirection du navigateur, l'envoi de fichiers est une autre fonctionnalité qui s'appuie sur les entêtes HTTP spécifiques. Yii fournit un jeu de méthodes pour prendre en charge différents besoins d'envoi de fichiers. Elles assurent toutes la prise en charge de la plage d'entêtes HTTP. \n\n* [[yii\\web\\Response::sendFile()]]: envoie un fichier existant à un client.\n* [[yii\\web\\Response::sendContentAsFile()]]: envoie un chaîne de caractères en tant que fichier à un client.\n* [[yii\\web\\Response::sendStreamAsFile()]]: envoie un flux de fichier existant en tant que fichier à un client. \n\nCes méthodes ont la même signature avec l'objet `response` comme valeur de retour. Si le fichier à envoyer est trop gros, vous devez envisager d'utiliser [[yii\\web\\Response::sendStreamAsFile()]] parce qu'elle fait un usage plus efficace de la mémoire. L'exemple qui suit montre comment envoyer un fichier dans une action de contrôleur. \n\n```php\npublic function actionDownload()\n{\n    return \\Yii::$app->response->sendFile('path/to/file.txt');\n}\n```\n\nSi vous appelez la méthode d'envoi de fichiers dans des endroits autres qu'une méthode d'action, vous devez aussi appeler la méthode  [[yii\\web\\Response::send()]] immédiatement après pour garantir qu'aucun contenu supplémentaire ne sera ajouté à la réponse. \n\n```php\n\\Yii::$app->response->sendFile('path/to/file.txt')->send();\n```\n\nQuelques serveurs Web assurent une prise en charge spéciale de l'envoi de fichiers appelée *X-Sendfile*. L'idée est de rediriger la requête d'un fichier sur le serveur Web qui sert directement le fichier. En conséquence, l'application Web peut terminer plus rapidement tandis que le serveur Web est en train d'envoyer le fichier. Pour utiliser cette fonctionnalité, vous pouvez appeler la méthode [[yii\\web\\Response::xSendFile()]]. La liste suivante résume, comment activer la fonctionnalité `X-Sendfile` pour quelques serveurs Web populaires :\n\n- Apache: [X-Sendfile](https://tn123.org/mod_xsendfile)\n- Lighttpd v1.4: [X-LIGHTTPD-send-file](https://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file)\n- Lighttpd v1.5: [X-Sendfile](https://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file)\n- Nginx: [X-Accel-Redirect](https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile/)\n- Cherokee: [X-Sendfile and X-Accel-Redirect](https://www.cherokee-project.com/doc/other_goodies.html#x-sendfile)\n\n\n## Envoi de la réponse <span id=\"sending-response\"></span>\n\nLe contenu d'une réponse n'est pas envoyé à l'utilisateur tant que la méthode [[yii\\web\\Response::send()]] n'est pas appelée. Par défaut, cette méthode est appelée automatiquement à la fin de [[yii\\base\\Application::run()]]. Vous pouvez néanmoins appeler cette méthode explicitement pour forcer l'envoi de la réponse immédiatement. \n\nLa méthode [[yii\\web\\Response::send()]] entreprend les étapes suivantes pour envoyer la réponse :\n\n1. Elle déclenche l'événement  [[yii\\web\\Response::EVENT_BEFORE_SEND]].\n2. Elle appelle [[yii\\web\\Response::prepare()]] pour formater [[yii\\web\\Response::data|les données de la réponse]] du [[yii\\web\\Response::content|contenu de la réponse]].\n3. Elle déclenche l'événement  [[yii\\web\\Response::EVENT_AFTER_PREPARE]].\n4. Elle appelle la méthode [[yii\\web\\Response::sendHeaders()]] pour envoyer les entêtes HTTP enregistrés. \n5. Elle appelle la méthode [[yii\\web\\Response::sendContent()]] pour envoyer le corps de la réponse.\n6. Elle déclenche l'événement [[yii\\web\\Response::EVENT_AFTER_SEND]].\n\nAprès que la méthode [[yii\\web\\Response::send()]] est appelée une fois, tout appel suivant de cette méthode est ignoré. Cela signifie qu'une fois la réponse expédiée, vous ne pouvez lui ajouter aucun contenu. \n\nComme vous pouvez le voir, la méthode [[yii\\web\\Response::send()]] déclenche plusieurs événements utiles. En répondant à ces événements, il est possible d'ajuster ou d'enjoliver la réponse. \n"
  },
  {
    "path": "docs/guide-fr/runtime-routing.md",
    "content": "Routage et création d'URL \n=========================\n\nLorsqu'une application Yii commence à traiter une URL objet d'une requête, sa première étape consiste à analyser cette URL \npour la résoudre en une [route](structure-controllers.md#routes). \nLa route est ensuite utilisée pour instancier l'[action de contrôleur](structure-controllers.md) correspondante pour la prise en charge de la requête. Ce processus est appelé *routage*.\n \nLe processus inverse du routage, qui consiste à créer une URL à partir d'une route et des paramètres associés de la requête,  \nest appelé *création d'URL*. Lorsque l'URL créée est ensuite requise, le processus de routage est capable de la résoudre en la route originale\n avec les paramètres de requête. \n  \nL'élément central en charge du routage et de la création d'URL est le [[yii\\web\\UrlManager|gestionnaire d'URL]], qui est enregistré en tant que  [composant d'application](structure-application-components.md) sous le nom `urlManager`. \nLe [[yii\\web\\UrlManager|gestionnaire d'URL]] fournit la méthode [[yii\\web\\UrlManager::parseRequest()|parseRequest()]] pour analyser une requête entrante et la résoudre en \nune route et les paramètres de requête associés, \net la méthode [[yii\\web\\UrlManager::createUrl()|createUrl()]] pour \ncréer une URL en partant d'une route avec ses paramètres de requête associés. \n \nEn configurant le composant  `urlManager` dans la configuration de l'application, vous pouvez laisser votre application reconnaître les formats d'URL arbitraires sans modifier le code existant de votre application. \nPar exemple, vous pouvez utiliser le code suivant pour créer une URL pour l'action `post/view` :\n\n\n```php\nuse yii\\helpers\\Url;\n\n// Url::to() appelle UrlManager::createUrl() pour créer une URL\n$url = Url::to(['post/view', 'id' => 100]);\n```\n\nSelon la configuration de `urlManager`, l'URL créée peut ressembler à l'une des URL suivantes (ou autre formats). \nEt si l'URL est requise plus tard, elle sera toujours analysée pour revenir à la route originale et aux valeurs des paramètres de la requête.\n\n```\n/index.php?r=post%2Fview&id=100\n/index.php/post/100\n/posts/100\n```\n\n\n## Formats d'URL  <span id=\"url-formats\"></span>\n\nLe [[yii\\web\\UrlManager|gestionnaire d'URL]] prend en charge deux formats d'URL : \n\n- le format d'URL par défaut,\n- le format d'URL élégantes.\n\nLe format d'URL par défaut utilise un [[yii\\web\\UrlManager::$routeParam|paramètre de requête]] nommé `r` qui représente la route et les paramètres de requête normaux associés à la route. \nPar exemple, l'URL `/index.php?r=post/view&id=100` represente la route `post/view` et le paramètre de requête `id` dont la valeur est 100. \nLe format d'URL par défaut ne requiert aucune configuration du [[yii\\web\\UrlManager|gestionnaire d'URL] \net fonctionne dans toutes les configurations de serveur Web. \n\nLe format d'URL élégantes utilise le chemin additionnel qui suit le nom du script d'entrée pour représenter la route et les paramètres de requête associés. \nPar exemple, le chemin additionnel dans l'URL `/index.php/post/100` est `/post/100` qui, avec une [[yii\\web\\UrlManager::rules|règle d'URL]] appropriée, peut représenter la route `post/view` et le paramètre des requête  `id` avec une valeur de 100 . \nPour utiliser le format d'URL élégantes, \nvous devez définir un jeu de [[yii\\web\\UrlManager::rules|règles d'URL]] \nen cohérence avec les exigences réelles sur la présentation d'une URL. \n \nVous pouvez passer d'un format d'URL à l'autre en inversant la propriété [[yii\\web\\UrlManager::enablePrettyUrl|enablePrettyUrl]] du  [[yii\\web\\UrlManager|gestionnaire d'URL]] \nsans changer quoi que ce soit au code de votre application. \n\n\n## Routage <span id=\"routing\"></span>\n\nLe routage se fait en deux étapes :\n\n- La requête entrante est analysée et résolue en une route et les paramètres de requête associés. \n- L'[action de contrôleur](structure-controllers.md#actions) \ncorrespondant à la route analysée est créée pour prendre la requête en charge. \n\nLors de l'utilisation du format d'URL par défaut, \nla résolution d'une requête en route est aussi simple que d'obtenir le paramètre nommé `r` de la méthode `GET`.\n\nLors de l'utilisation du format d'URL élégantes, le [[yii\\web\\UrlManager|gestionnaire d'URL] examine les [[yii\\web\\UrlManager::rules|règles d'URL]] enregistrées pour trouver une règle qui correspond et résoudre la requête en une route. \nSi une telle règle n'est pas trouvée, une exception  [[yii\\web\\NotFoundHttpException]] \nest levée. \n\nUne fois que la requête est résolue en une route, il est temps de créer l'action de contrôleur identifiée par la route. \nLa route est éclatée en de multiples parties par des barres obliques de division. \nPar exemple, `site/index` est éclatée en  `site` et `index`. \nChacune des parties est considérée comme un identifiant qui peut faire référence à un module, un contrôleur ou une action. \nEn partant de la première partie dans la route, l'application entreprend les étapes suivantes pour créer un module (s'il en existe un), un contrôleur et une action :\n\n1. Définit l'application comme étant le module courant.\n2. Vérifie si la [[yii\\base\\Module::controllerMap|table de mise en correspondance des contrôleurs]] du module courant contient l'identifiant courant. \nSi c'est le cas, un objet *controller* est créé en respectant la configuration du contrôleur trouvé dans la table de mise en correspondance, \net on passe à l'étape 5 pour prendre en compte le reste de la route. \n3. Vérifie si l'identifiant fait référence à un module listé dans la propriété [[yii\\base\\Module::modules|modules]] du module courant. \nSi c'est le cas, un module est créé en respectant la configuration trouvée dans la liste des modules et on passe à l'étape 2 \npour prendre en compte le reste de la route dans le contexte du nouveau module. \n4. Traite l'identifiant comme un [identifiant de contrôleur](structure-controllers.md#controller-ids), crée un objet *controller* \net passe à l'étape suivante avec le reste de la route. \n5. Le contrôleur recherche l'identifiant courant dans sa [[yii\\base\\Controller::actions()|table de mise en correspondance des actions]]. S'il le trouve, il crée une action respectant la configuration trouvée dans la table de mise en correspondance. \nAutrement, le contrôleur essaye de créer une action en ligne dont le nom de méthode correspond à l' [identifiant d'action](structure-controllers.md#action-ids) courant.\n\n\nSi une erreur se produit dans l'une des étapes décrites ci-dessus, une exception  [[yii\\web\\NotFoundHttpException]] est levée, indiquant l'échec du processus de routage. \n\n\n\n### Route par défaut <span id=\"default-route\"></span>\n\nQuand une requête est analysée et résolue en une route vide, la route dite *route par défaut* est utilisée à sa place. \nPar défaut, la route par défaut est `site/index`,  qui fait référence à l'action `index` du contrôleur `site`. \nVous pouvez la personnaliser en configurant la propriété [[yii\\web\\Application::defaultRoute|defaultRoute]] de l'application dans la configuration de l'application comme indiqué ci-dessous :\n\n\n```php\n[\n    // ...\n    'defaultRoute' => 'main/index',\n];\n```\n\nDe façon similaire à la route par défaut de l'application, il existe aussi une route par défaut pour les modules. \nAinsi s'il existe un module `user` (utilisateur) et que la requête est résolue en la route `user`, la propriété [[yii\\base\\Module::defaultRoute|defaultRoute]] du module est utilisée pour déterminer le contrôleur.\nPar défaut, le nom du contrôleur est `default`. Si aucune action n'est spécifiée dans la propriété [[yii\\base\\Module::defaultRoute|defaultRoute]],\nla propriété [[yii\\base\\Controller::defaultAction|defaultAction]] du contrôleur est utilisée pour déterminer l'action.\nDans cet exemple, la route complète serait `user/default/index`.\n\n\n### La route `attrape-tout` <span id=\"catchall-route\"></span>\n\nParfois, vous désirez mettre votre application Web en mode maintenance temporairement \net afficher la même page d'information pour toutes les requêtes. Il y a plusieurs moyens de faire cela. \nL'une des manières les plus simples est de configurer la propriété [[yii\\web\\Application::catchAll]] dans la configuration de l'application comme indiqué ci-dessous :\n\n```php\n[\n    // ...\n    'catchAll' => ['site/offline'],\n];\n```\n\nAvec la configuration ci-dessus, l'action `site/offline` est utilisée pour prendre toutes les requêtes entrantes en charge. \n\nLa propriété  `catchAll` accepte un tableau dont le premier élément spécifie une route \net le reste des éléments des couples clé-valeur pour les paramètres  [liés à l'action](structure-controllers.md#action-parameters).\n\n> Info: le [panneau de débogage] ](https://github.com/yiisoft/yii2-debug/blob/master/docs/guide/README.md) de l'environnement de développement\n ne fonctionne pas lorsque cette propriété est activée.\n\n\n## Création d'URL <span id=\"creating-urls\"></span>\n\nYii fournit une méthode d'aide [[yii\\helpers\\Url::to()]] pour créer différentes sortes d'URL à partir de routes données et de leurs paramètres de requête associés. \nPar exemple : \n\n```php\nuse yii\\helpers\\Url;\n\n// crée une URL d'une route: /index.php?r=post%2Findex\necho Url::to(['post/index']);\n\n// crée une URL d'une route avec paramètres : /index.php?r=post%2Fview&id=100\necho Url::to(['post/view', 'id' => 100]);\n\n// crée une  URL avec ancre : /index.php?r=post%2Fview&id=100#content\necho Url::to(['post/view', 'id' => 100, '#' => 'content']);\n\n// crée une URL absolue : https://www.example.com/index.php?r=post%2Findex\necho Url::to(['post/index'], true);\n\n// crée une URL absolue en utilisant le schéma https : https://www.example.com/index.php?r=post%2Findex\necho Url::to(['post/index'], 'https');\n```\n\nNotez que dans l'exemple ci-dessus, nous supposons que le format d'URL est le format par défaut. \nSi le format d'URL élégantes est activé, les URL créées sont différentes et respectent les [[yii\\web\\UrlManager::rules|règles d'URL]] en cours d'utilisation. \n\nLa route passée à la méthode [[yii\\helpers\\Url::to()]] est sensible au contexte. \nElle peut être soit *relative*, soit *absolue* et normalisée en respect des règles suivantes :\n\n- Si la route est une chaîne vide, la [[yii\\web\\Controller::route|route]] couramment requise est utilisée ;\n- Si la route ne contient aucune barre oblique de division, elle est considérée comme un identifiant d'action du contrôleur courant \net est préfixée par la valeur de l'identifiant [[\\yii\\web\\Controller::uniqueId|uniqueId]] du contrôleur courant ;\n- Si la route n'a pas de barre oblique de division en tête, elle est considérée comme une route relative au module courant \net préfixée par la valeur de l'identifiant [[\\yii\\base\\Module::uniqueId|uniqueId]] du module courant.\n\nÀ partir de la version 2.0.2, vous pouvez spécifier une route en terme d'[alias](concept-aliases.md). Si c'est le cas, \nl'alias est d'abord converti en la route réelle qui est ensuite transformée en route absolue dans le respect des règles précédentes. \n\n\nPar exemple, en supposant que le module courant est `admin` et que le contrôleur courant est `post`,\n\n```php\nuse yii\\helpers\\Url;\n\n// route couramment requise : /index.php?r=admin%2Fpost%2Findex\necho Url::to(['']);\n\n// une route relative avec un identifiant d'action seulement : /index.php?r=admin%2Fpost%2Findex\necho Url::to(['index']);\n\n// une route relative : /index.php?r=admin%2Fpost%2Findex\necho Url::to(['post/index']);\n\n// une route absoulue : /index.php?r=post%2Findex\necho Url::to(['/post/index']);\n\n// /index.php?r=post%2Findex     suppose que l'alias \"@posts\" est défini comme  \"/post/index\"\necho Url::to(['@posts']);\n```\n\nLa méthode [[yii\\helpers\\Url::to()]] est mise en œuvre en appelant les méthodes [[yii\\web\\UrlManager::createUrl()|createUrl()]] \net  [[yii\\web\\UrlManager::createAbsoluteUrl()|createAbsoluteUrl()]] \ndu [[yii\\web\\UrlManager|gestionnaire d'URL]]. \nDans les quelques sous-sections suivantes, nous expliquons comment configurer le [[yii\\web\\UrlManager|gestionnaire d'URL]] pour personnaliser le format des URL créées. \n\nLa méthode [[yii\\helpers\\Url::to()]] prend aussi en charge la création d'URL qui n'ont **pas** de relation avec des routes particulières. \nAu lieu de passer un tableau comme premier paramètre, vous devez, dans ce cas,  passer une chaîne de caractères. Par exemple :\n \n```php\nuse yii\\helpers\\Url;\n\n// URL couramment requise : /index.php?r=admin%2Fpost%2Findex\necho Url::to();\n\n// un alias d'URL: https://example.com\nYii::setAlias('@example', 'https://example.com/');\necho Url::to('@example');\n\n// une URL absolue : https://example.com/images/logo.gif\necho Url::to('/images/logo.gif', true);\n```\n\nEn plus de la méthode `to()`, la classe d'aide [[yii\\helpers\\Url]] fournit aussi plusieurs méthode pratiques de création d'URL. \nPar exemple :\n\n```php\nuse yii\\helpers\\Url;\n\n// URL de page d'accueil: /index.php?r=site%2Findex\necho Url::home();\n\n// URL de base, utile si l'application est déployée dans un sous-dossier du dossier Web racine\necho Url::base();\n\n// l'URL canonique de l'URL couramment requise \n// voir https://fr.wikipedia.org/wiki/%C3%89l%C3%A9ment_de_lien_canonique\necho Url::canonical();\n\n// mémorise l'URL couramment requise et la retrouve dans les requêtes subséquentes\nUrl::remember();\necho Url::previous();\n```\n\n\n## Utilisation des URL élégantes <span id=\"using-pretty-urls\"></span>\n\nPour utiliser les URL élégantes, configurez le composant `urlManager` dans la configuration de l'application comme indiqué ci-dessous :\n\n```php\n[\n    'components' => [\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            'showScriptName' => false,\n            'enableStrictParsing' => false,\n            'rules' => [\n                // ...\n            ],\n        ],\n    ],\n]\n```\n\nLa propriété [[yii\\web\\UrlManager::enablePrettyUrl|enablePrettyUrl]] est obligatoire car elle active/désactive le format d'URL élégantes. Le reste des propriétés est facultatif. \nNéanmoins, leur configuration montrée plus haut est couramment utilisée. \n\n* [[yii\\web\\UrlManager::showScriptName|showScriptName]]: cette propriété détermine si le script d'entrée doit être inclus dans l'URL créée. \nPar exemple, au lieu de créer une URL `/index.php/post/100`, \nen définissant cette propriété à `false`, l'URL `/post/100` est générée. \n* [[yii\\web\\UrlManager::enableStrictParsing|enableStrictParsing]]: cette propriété détermine si l'analyse stricte est activée . \nSi c'est le cas, l'URL entrante doit correspondre à au moins une des [[yii\\web\\UrlManager::rules|règles]] afin d'être traitée comme une requête valide, sinon une exception [[yii\\web\\NotFoundHttpException]] est levée. \nSi l'analyse stricte est désactivée, lorsqu'aucune  \n[[yii\\web\\UrlManager::rules|règle]] ne correspond à l'URL requise, \nla partie chemin de l'URL est considérée comme étant la route requise. \n* [[yii\\web\\UrlManager::rules|rules]]: cette propriété contient une liste de règles spécifiant comme analyser et créer des URL. \nC'est la propriété principale avec laquelle vous devez travailler afin de créer des URL dont le format satisfait les exigences particulières de votre application. \n\n\n> Note: afin de cacher le nom du script d'entrée dans l'URL créée, en plus de définir la propriété \n[[yii\\web\\UrlManager::showScriptName|showScriptName]] \nà `false`, vous pouvez aussi configurer votre serveur Web de manière à ce qu'il puisse identifier correctement quel script PHP doit être exécuté lorsqu'une URL requise n'en précise aucun explicitement. \nSi vous utilisez un serveur Apache ou nginx, vous pouvez vous reporter à la configuration recommandée décrite dans la section [Installation](start-installation.md#recommended-apache-configuration).\n\n\n\n### Règles d'URL  <span id=\"url-rules\"></span>\n\nUne règle d'URL est une classe mettant en œuvre l'interface [[yii\\web\\UrlRuleInterface]], généralement une instance de la classe [[yii\\web\\UrlRule]]. \nChaque règle d'URL consiste en un motif utilisé pour être mis en correspondance avec la partie chemin de l'URL, une route, et quelques paramètres de requête. \nUne règle d'URL peut être utilisée pour analyser une requête si son motif correspond à l'URL requise. \nUne règle d'URL peut être utilisée pour créer une URL si sa route et le nom de ses paramètres de requête correspondent à ceux qui sont fournis. \n\nQuand le format d'URL élégantes est activé, le [[yii\\web\\UrlManager|gestionnaire d'URL]] utilise les règles d'URL déclarées dans sa propriété \n[[yii\\web\\UrlManager::rules|rules]] pour analyser les requêtes entrantes et créer des URL. \nEn particulier, pour analyser une requête entrante, le [[yii\\web\\UrlManager|gestionnaire d'URL]] examine les règles dans l'ordre de leur déclaration et cherche la *première* règle qui correspond à l'URL requise. \nLa règle correspondante est ensuite utilisée pour analyser l'URL et la résoudre en une route et ses paramètres de requête associés. \nDe façon similaire, pour créer une URL, le [[yii\\web\\UrlManager|gestionnaire d'URL]] cherche la première règle qui correspond à la route donnée et aux paramètres et l'utilise pour créer l'URL. \n\n\nVous pouvez configurer la propriété [[yii\\web\\UrlManager::rules]] sous forme de tableau dont les clés sont les [[yii\\web\\UrlRule::$pattern|motifs]] \net les valeurs, les [[yii\\web\\UrlRule::$route|routes]] correspondantes. \nChacune des paires motif-route construit une règle d'URL. \nPar exemple,  la configuration des [[yii\\web\\UrlManager::rules|règles]] suivante déclare deux règles d'URL. \nLa première correspond à l'URL `posts` et la met en correspondance avec la route   `post/index`. La seconde correspond à une URL qui correspond à l'expression régulière  `post/(\\d+)` et la met en correspondance avec la route `post/view` et le paramètre nommé `id`.\n\n```php\n[\n    'posts' => 'post/index', \n    'post/<id:\\d+>' => 'post/view',\n]\n```\n\n> Info: le motif dans une règle est utilisé pour correspondre à la partie chemin d'une URL.  \nPar exemple, la partie chemin de `/index.php/post/100?source=ad` est `post/100` (les barres obliques de division de début et de fin sont ignorées) et correspond au motif `post/(\\d+)`.\n\n\nEn plus de déclarer des règles d'URL sous forme de paires motif-route, vous pouvez aussi les déclarer  sous forme de tableaux de configuration. \nChacun des tableaux de configuration est utilisé pour configurer un simple objet règle d'URL. \nC'est souvent nécessaire lorsque vous voulez configurer d'autres propriétés d'une règle d'URL. Par exemple :\n\n```php\n'rules' => [\n    // ...autres règles d'URL...\n    [\n        'pattern' => 'posts',\n        'route' => 'post/index',\n        'suffix' => '.json',\n    ],\n]\n```\n\nPar défaut, si vous ne spécifiez pas l'option `class` pour une configuration de règle, \nelle prend la valeur par défaut [[yii\\web\\UrlRule]] qui est la valeur par défaut définie dans \n[[yii\\web\\UrlManager::$ruleConfig]].\n\n\n### Paramètres nommés <span id=\"named-parameters\"></span>\n\nUne règle d'URL peut être associée à quelques paramètres de requête nommés qui sont spécifiés dans le motif et respectent le format `<ParamName:RegExp>`, \ndans lequel  `ParamName` spécifie le nom du paramètre et  `RegExp` est une expression régulière facultative utilisée pour établir la correspondance avec une valeur de paramètre. \nSi `RegExp` n'est pas spécifié, cela signifie que la valeur du paramètre doit être une chaîne de caractères sans aucune barre oblique de division. \n\n\n> Note: vous pouvez seulement spécifier des expressions régulières pour les paramètres. La partie restante du motif est considérée être du texte simple.\n\nLorsqu'une règle est utilisée pour analyser une URL, \nelle remplit les paramètres associés avec les valeurs des parties de l'URL qui leur correspondent, \net ces paramètres sont rendus disponibles dans `$_GET` et plus tard dans le composant d'application `request`. \nLorsque la règle est utilisée pour créer une URL, elle prend les valeurs des paramètres fournis et les insère à l'endroit où ces paramètres sont déclarés.\n\nPrenons quelques exemples pour illustrer comment les paramètres nommés fonctionnent. Supposons que nous ayons déclaré les règles d'URL suivantes :\n\n```php\n[\n    'posts/<year:\\d{4}>/<category>' => 'post/index',\n    'posts' => 'post/index',\n    'post/<id:\\d+>' => 'post/view',\n]\n```\n\nLorsque les règles sont utilisées pour analyser des URL :\n\n- `/index.php/posts` est analysée et résolue en la route `post/index` en utilisant la deuxième règle ;\n- `/index.php/posts/2014/php` est analysée et résolue en la route `post/index`, le paramètre  `year` dont la valeur est  2014 \net le paramètre `category` dont la valeur est  `php` en utilisant la première règle ;\n- `/index.php/post/100` est analysée et résolue en la route `post/view` \net le paramètre `id` dont la valeur est 100 en utilisant la troisième règle ;\n- `/index.php/posts/php` provoque la levée d'une exception [[yii\\web\\NotFoundHttpException]] quand la propriété [[yii\\web\\UrlManager::enableStrictParsing]]\n  est définie à `true`, parce qu'elle ne correspond à aucun des motifs. \nSi  [[yii\\web\\UrlManager::enableStrictParsing]] est définie à  `false` (la valeur par défaut), la partie chemin `posts/php` est retournée en tant que route. Cela provoque l'exécution de l'action correspondante si elle existe, ou lève une exception [[yii\\web\\NotFoundHttpException]] autrement.\n \nEt quand les règles sont utilisées pour créer des URL : \n\n- `Url::to(['post/index'])` crée `/index.php/posts` en utilisant la deuxième règle ;\n- `Url::to(['post/index', 'year' => 2014, 'category' => 'php'])` crée `/index.php/posts/2014/php` en utilisant la première règle ;\n- `Url::to(['post/view', 'id' => 100])` crée `/index.php/post/100` en utilisant la troisième règle ;\n- `Url::to(['post/view', 'id' => 100, 'source' => 'ad'])` crée `/index.php/post/100?source=ad` en utilisant la troisième règle.\n  Comme le paramètre `source` n'est pas spécifié dans la règle, il est ajouté en tant que paramètre de requête à l'URL créée.\n- `Url::to(['post/index', 'category' => 'php'])` crée `/index.php/post/index?category=php` en utilisant aucune des règles.\n  Notez que, aucune des règles n'étant utilisée, l'URL est créée en ajoutant simplement la route en tant que partie chemin \net tous les paramètres en tant que partie de la chaîne de requête.\n\n\n### Paramétrage des routes <span id=\"parameterizing-routes\"></span>\n\nVous pouvez inclure les noms des paramètres dans la route d'une règle d'URL. Cela permet à une règle d'URL d'être utilisée pour correspondre à de multiples routes. \nPar exemple, les règles suivantes incluent les paramètres `controller` et `action` dans les routes.\n\n```php\n'rules' => [\n    '<controller:(post|comment)>/create' => '<controller>/create',\n    '<controller:(post|comment)>/<id:\\d+>/<action:(update|delete)>' => '<controller>/<action>',\n    '<controller:(post|comment)>/<id:\\d+>' => '<controller>/view',\n    '<controller:(post|comment)>s' => '<controller>/index',\n]\n```\n\nPour analyser l'URL `/index.php/comment/100/update`, la deuxième règle s'applique et définit le paramètre `controller`  \ncomme étant `comment` et le paramètre  `action` comme étant `create`. La route `<controller>/<action>` est par conséquent résolue comme `comment/update`.\n \nDe façon similaire, pour créer une URL à partir de la route `comment/index`, la dernière règle s'applique, ce qui donne l'URL `/index.php/comments`.\n\n> Info: en paramétrant les routes, il est possible de réduire grandement le nombre de règles d'URL, \nce qui peut accroître significativement la performance du  [[yii\\web\\UrlManager|gestionnaire d'URL]]. \n  \n### Valeur par défaut des paramètres <span id=\"default-parameter-values\"></span>\n\nPar défaut, tous les paramètres déclarés dans une règle sont requis. \nSi une URL requise ne contient pas un paramètre particulier, ou si une URL est créée sans un paramètre particulier, la règle ne s'applique pas. \nPour rendre certains paramètres facultatifs, vous pouvez configurer la propriété [[yii\\web\\UrlRule::defaults|defaults]] de la règle. \nLes paramètres listés dans cette propriété sont facultatifs et prennent les valeurs spécifiées lorsqu'elles ne sont pas fournies.\n\nDans la déclaration suivante d'une règle, les paramètres `page` et `tag` sont tous les deux facultatifs \net prennent la valeur 1 et vide, respectivement quand ils ne sont pas fournis. \n\n```php\n[\n    // ...autres règles...\n    [\n        'pattern' => 'posts/<page:\\d+>/<tag>',\n        'route' => 'post/index',\n        'defaults' => ['page' => 1, 'tag' => ''],\n    ],\n]\n```\n\nLa règle ci-dessus peut être utilisée pour analyser ou créer l'une quelconque des URL suivantes : \n\n* `/index.php/posts`: `page` est 1, `tag` est ''.\n* `/index.php/posts/2`: `page` est 2, `tag` est ''.\n* `/index.php/posts/2/news`: `page` est 2, `tag` est `'news'`.\n* `/index.php/posts/news`: `page` est 1, `tag` est `'news'`.\n\nSans les paramètres facultatifs, vous devriez créer quatre règles pour arriver au même résultat.\n\n> Note: si [[yii\\web\\UrlRule::$pattern|pattern]] (motif) ne contient que des paramètres facultatifs et des barres obliques de division, \nle premier paramètre peut être omis seulement si tous les autres paramètres le sont.\n\n\n### Règles avec des noms de serveur <span id=\"rules-with-server-names\"></span>\n\nIl est possible d'inclure des noms de serveur Web dans le motif d'une règle d'URL. Cela est principalement utilisé lorsque votre application doit se comporter différemment selon le nom du serveur Web. \nPar exemple, les règles suivantes analysent et résolvent l'URL `https://admin.example.com/login` en la route `admin/user/login` \net `https://www.example.com/login` en la route `site/login`.\n\n```php\n[\n    'https://admin.example.com/login' => 'admin/user/login',\n    'https://www.example.com/login' => 'site/login',\n]\n```\n\nVous pouvez aussi inclure des paramètres  dans les noms de serveurs pour en extraire de l'information dynamique. \nPar exemple, la règle suivante analyse et résout l'URL `https://en.example.com/posts` en la route `post/index` et le paramètre  `language=en`.\n\n```php\n[\n    'http://<language:\\w+>.example.com/posts' => 'post/index',\n]\n```\n\nDepuis la version 2.0.11, vous pouvez également utiliser des motifs relatifs au protocole qui marchent à la fois pour `http` et `https`.\nLa syntaxe est la même que ci-dessus mais en sautant la partie `http`, p. ex. `'//www.example.com/login' => 'site/login'`.\n\n> Note: les règles avec des noms de serveur ne doivent **pas** comprendre le sous-dossier du script d'entrée dans leur motif. \nPar exemple, si l'application est sous  `https://www.example.com/sandbox/blog`, alors vous devez utiliser le motif `https://www.example.com/posts` au lieu de  `https://www.example.com/sandbox/blog/posts`. \nCela permet à votre application d'être déployée sous n'importe quel dossier sans avoir à changer son code. \nYii détecte automatiquement l'URL de base de l'application.\n\n### Suffixes d'URL  <span id=\"url-suffixes\"></span>\n\nVous désirez peut-être ajouter des suffixes aux URL pour des raisons variées. \nPar exemple, vous pouvez ajouter `.html` aux URL de manière à ce qu'elles ressemblent à des URL de pages HTML statiques. \nVous pouvez aussi y ajouter `.json` pour indiquer le type de contenu attendu  pour la réponse. \nVous pouvez faire cela en configurant la propriété [[yii\\web\\UrlManager::suffix]] dans la configuration de l'application comme ceci :\n\n```php\n[\n    // ...\n    'components' => [\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            // ...\n            'suffix' => '.html',\n            'rules' => [\n                // ...\n            ],\n        ],\n    ],\n]\n```\n\nLa configuration ci-dessus permet au [[yii\\web\\UrlManager|gestionnaire d'URL]] de reconnaître les URL requises \net aussi de créer des URL avec le suffixe `.html`.\n\n> Tip: vous pouvez définir `/` en tant que suffixe des URL de manière à ce que tous les URL se terminent par la barre oblique de division. \n\n> Note: lorsque vous configurez un suffixe d'URL, si une URL requise ne contient pas ce suffixe, elle est considérée comme une URL non reconnue. \nCela est une pratique recommandée pour l'optimisation des moteurs de recherche (SE0 – Search Engine Optimization). \n  \nParfois vous désirez utiliser des suffixes différents pour différentes URL. \nCela peut être fait en configurant la propriété [[yii\\web\\UrlRule::suffix|suffix]] des règles d'URL individuelles. \nLorsqu'une URL a cette propriété définie, elle écrase la valeur définie au niveau du [[yii\\web\\UrlManager|gestionnaire d'URL]]. \nPar exemple, la configuration suivante contient une règle d'URL personnalisée  qui utilise  `.json` en tant que suffixe à la place du suffixe défini globalement `.html`.\n\n```php\n[\n    'components' => [\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            // ...\n            'suffix' => '.html',\n            'rules' => [\n                // ...\n                [\n                    'pattern' => 'posts',\n                    'route' => 'post/index',\n                    'suffix' => '.json',\n                ],\n            ],\n        ],\n    ],\n]\n```\n\n### Méthodes HTTP  <span id=\"http-methods\"></span>\n\nEn mettant en œuvre des API pleinement REST, il est couramment nécessaire que la même URL puisse être résolue en différentes routes selon la méthode HTTP utilisée par la requête. \nCela peut être fait facilement en préfixant les motifs des règles avec les méthodes HTTP prises en charge. \nSi une règle prend en charge plusieurs méthodes HTTP, il faut séparer les noms de méthode par une virgule. \nPar exemple, les règles suivantes ont le même motif `post/<id:\\d+>` mais des méthodes HTTP différentes. \nUne requête de `PUT post/100` est résolue en la route `post/update`, tandis que la requête de `GET post/100` en la route `post/view`.\n\n```php\n'rules' => [\n    'PUT,POST post/<id:\\d+>' => 'post/update',\n    'DELETE post/<id:\\d+>' => 'post/delete',\n    'post/<id:\\d+>' => 'post/view',\n]\n```\n\n> Note: si une règle d'URL contient des méthodes  HTTP dans son motif, la règle n'est utilisée qu'à des fins d'analyse sauf si `GET` fait partie des verbes spécifiés. \nElle est ignorée quand le [[yii\\web\\UrlManager|gestionnaire d'URL]]  est sollicité pour créer une URL.\n\n> Tip: pour simplifier le routage des API pleinement REST, Yii fournit la classe spéciale de règle d'URL [[yii\\rest\\UrlRule]] \nqui est très efficace et prend en charge quelques fonctionnalités originales comme la pluralisation automatique des identifiants de contrôleur. \nPour plus de détails, reportez-vous à la section [Routage](rest-routing.md) dans le chapitre API pleinement REST. \n\n\n### Ajout dynamique de règles <span id=\"adding-rules\"></span>\n\nDes règles d'URL peuvent être ajoutées dynamiquement au [[yii\\web\\UrlManager|gestionnaire d'URL]]. \nCela est souvent nécessaire pour les [modules](structure-modules.md) distribuables qui veulent gérer leurs propres règles d'URL. \nPour que les règles ajoutées dynamiquement prennent effet dans de processus de routage, vous devez les ajouter dans l'étape d'[amorçage](runtime-bootstrapping.md). \nPour les modules, cela signifie qu'ils doivent implémenter l'interface  [[yii\\base\\BootstrapInterface]] \net ajouter les règles dans leur méthode [[yii\\base\\BootstrapInterface::bootstrap()|bootstrap()]] comme l'exemple suivant le montre :\n\n```php\npublic function bootstrap($app)\n{\n    $app->getUrlManager()->addRules([\n        // rule declarations here\n    ], false);\n}\n```\n\nNotez que vous devez également lister ces modules dans la propriété [[yii\\web\\Application::bootstrap]] \nafin qu'ils puissent participer au processus d'[amorçage](runtime-bootstrapping.md).\n\n\n### Création des classes règles <span id=\"creating-rules\"></span>\n\nEn dépit du fait que la classe par défaut [[yii\\web\\UrlRule]] est suffisamment flexible pour la majorité des projets, \nil y a des situations dans lesquelles vous devez créer votre propres classes de règle. \nPar exemple, dans un site Web de vendeur de voitures, vous désirerez peut-être prendre en charge des formats d'URL du type `/Manufacturer/Model`, où `Manufacturer` et `Model` doivent correspondre à quelques données stockées dans une base de données. \nLa classe de règle par défaut ne fonctionne pas dans ce cas car elle s'appuie sur des motifs déclarés de manière statique. \n\nVous pouvez créer les classes de règle d'URL suivantes pour résoudre ce problème : \n\n```php\n<?php\n\nnamespace app\\components;\n\nuse yii\\web\\UrlRuleInterface;\nuse yii\\base\\BaseObject;\n\nclass CarUrlRule extends BaseObject implements UrlRuleInterface\n{\n    public function createUrl($manager, $route, $params)\n    {\n        if ($route === 'car/index') {\n            if (isset($params['manufacturer'], $params['model'])) {\n                return $params['manufacturer'] . '/' . $params['model'];\n            } elseif (isset($params['manufacturer'])) {\n                return $params['manufacturer'];\n            }\n        }\n        return false;  // this rule does not apply\n    }\n\n    public function parseRequest($manager, $request)\n    {\n        $pathInfo = $request->getPathInfo();\n        if (preg_match('%^(\\w+)(/(\\w+))?$%', $pathInfo, $matches)) {\n            // vérifie  $matches[1] et $matches[3] pour voir si\n            // elles correspondent à un  manufacturer et à un model dans la base de données\n            // si oui, définit $params['manufacturer'] et/ou $params['model']\n            // et retourne  ['car/index', $params]\n        }\n        return false;  // cette règle ne s'applique pas\n    }\n}\n```\n\nEt utilisez la nouvelle classe de règle dans la configuration de [[yii\\web\\UrlManager::rules]] :\n\n```php\n[\n    // ...other rules...\n    \n    [\n        'class' => 'app\\components\\CarUrlRule', \n        // ...configure d'autres propriétés...\n    ],\n]\n```\n\n## Normalisation d'URL <span id=\"url-normalization\"></span>\n\nDepuis la version 2.0.10, le [[yii\\web\\UrlManager|gestionnaire d'URL]] peut être configuré pour utiliser le [[yii\\web\\UrlNormalizer|normalisateur d'URL]] pour prendre en compte les variations de la même URL, p. ex. avec et sans la barre oblique de division de fin.\n\n\nParce que, techniquement,  `https://example.com/path`\net `https://example.com/path/` sont des URL différentes, servir le même  contenu pour chacune d'elles peut dégrader le classement SEO.\nPar défaut, le normalisateur fusionne les barres obliques de division consécutives, ajoute ou retire des barres de division de fin selon que le suffixe comporte une barre de division de fin ou pas, et redirige vers la version normalisée de l'URL en utilisant la [redirection permanente](https://fr.wikipedia.org/wiki/HTTP_301).\nLe normalisateur peut être configuré globalement pour le gestionnaire d'URL ou individuellement pour chacune des règles — par défaut, chacune des règles utilise le normalisateur du gestionnaire d'URL. \nVous pouvez définir [[yii\\web\\UrlRule::$normalizer|UrlRule::$normalizer]] à `false` pour désactiver la normalisation pour une règle d'URL particulière.\n\nCe qui suit est un exemple de configuration pour le  [[yii\\web\\UrlNormalizer|normalisateur d'URL]]:\n\n```php\n'urlManager' => [\n    'enablePrettyUrl' => true,\n    'showScriptName' => false,\n    'enableStrictParsing' => true,\n    'suffix' => '.html',\n    'normalizer' => [\n        'class' => 'yii\\web\\UrlNormalizer',\n        // utilise la redirection temporaire au lieu de la redirection permanente pour le débogage\n        'action' => UrlNormalizer::ACTION_REDIRECT_TEMPORARY,\n    ],\n    'rules' => [\n        // ...autres règles...\n        [\n            'pattern' => 'posts',\n            'route' => 'post/index',\n            'suffix' => '/',\n            'normalizer' => false, // désactive le normalisateur pour cette règle\n        ],\n        [\n            'pattern' => 'tags',\n            'route' => 'tag/index',\n            'normalizer' => [\n                // ne fusionne pas les barres obliques de division consécutives pour cette règle\n                'collapseSlashes' => false,\n            ],\n        ],\n    ],\n]\n```\n\n> Note: par défaut [[yii\\web\\UrlManager::$normalizer|UrlManager::$normalizer]] est désactivé. \nVous devez le configure explicitement pour activer la normalisation d'URL.\n\n\n\n## Considérations de performance  <span id=\"performance-consideration\"></span>\n\nLors du développement d'une application Web complexe, il est important d'optimiser les règles d'URL  afin que l'analyse des requêtes et la création d'URL prennent moins de temps.\n\n\nEn utilisant les routes paramétrées, vous pouvez réduire le nombre de règles d'URL, ce qui accroît significativement la performance. \n\nLors de l'analyse d'URL ou de la création d'URL, le [[yii\\web\\UrlManager|gestionnaire d'URL]] examine les règles d'URL dans l'ordre de leur déclaration. En conséquence, vous devez envisager d'ajuster cet ordre afin que les règles les plus spécifiques et/ou utilisées couramment soient placées avant les règles les moins utilisées. \n\n\nSi quelques règles d'URL partagent le même préfixe dans leur motif ou dans leur route, vous pouvez envisager d'utiliser [[yii\\web\\GroupUrlRule]] pour qu'elles puissent être examinées plus efficacement par le [[yii\\web\\UrlManager|gestionnaire d'URL]] en tant que groupe. \nCela est souvent le cas quand votre application est composée de modules, chacun ayant son propre jeu de règles d'URL avec l'identifiant de module comme préfixe commun. \n"
  },
  {
    "path": "docs/guide-fr/runtime-sessions-cookies.md",
    "content": "Sessions et témoins de connexion\n================================\n\nLes sessions et les témoins de connexion permettent à des données d'être conservées à travers des requêtes multiples. Avec le langage PHP simple, vous pouvez y accéder via les variables globales `$_SESSION` et `$_COOKIE`, respectivement. Yii encapsule les sessions et les témoins de connexion sous forme d'objets et, par conséquent, vous permet d'y accéder d'une manière orientée objet avec des améliorations utiles. \n\n## Sessions <span id=\"sessions\"></span>\n\nComme pour les [requêtes](runtime-requests.md) et les [réponses](runtime-responses.md), vous pouvez accéder aux sessions via le [composant d'application](structure-application-components.md) `session` qui, par défaut, est une instance de la classe [[yii\\web\\Session]].\n\n\n### Ouverture et fermeture d'une session <span id=\"opening-closing-sessions\"></span>\n\nPour ouvrir et fermer une session, vous pouvez procéder comme suit :\n\n```php\n$session = Yii::$app->session;\n\n// vérifie si une session est déjà ouverte\nif ($session->isActive) ...\n\n// ouvre une session\n$session->open();\n\n// ferme une session\n$session->close();\n\n// détruit toutes les données enregistrées dans une session.\n$session->destroy();\n```\n\nVous pouvez appeler les méthodes  [[yii\\web\\Session::open()|open()]] et [[yii\\web\\Session::close()|close()]] plusieurs fois sans causer d'erreur ; en interne les méthodes commencent par vérifier si la session n'est pas déjà ouverte. \n\n\n### Accès aux données de  session <span id=\"access-session-data\"></span>\n\nPour accéder aux données stockées dans une session, vous pouvez procéder comme indiqué ci-après :\n\n```php\n$session = Yii::$app->session;\n\n// obtient une variable de session. Les utilisations suivantes sont équivalentes :\n$language = $session->get('language');\n$language = $session['language'];\n$language = isset($_SESSION['language']) ? $_SESSION['language'] : null;\n\n// définit une variable de session variable. Les utilisations suivantes sont équivalentes :\n$session->set('language', 'en-US');\n$session['language'] = 'en-US';\n$_SESSION['language'] = 'en-US';\n\n// supprime une variable session. Les utilisations suivantes sont équivalentes :\n$session->remove('language');\nunset($session['language']);\nunset($_SESSION['language']);\n\n// vérifie si une session possède la variable 'language'. Les utilisations suivantes sont équivalentes :\nif ($session->has('language')) ...\nif (isset($session['language'])) ...\nif (isset($_SESSION['language'])) ...\n\n// boucle sur toutes les sessions. Les utilisations suivantes sont équivalentes :\nforeach ($session as $name => $value) ...\nforeach ($_SESSION as $name => $value) ...\n```\n\n> Info: lorsque vous accédez aux données d'une session via le composant  `session`, une session est automatiquement ouverte si elle ne l'a pas déjà été.  Cela est différent de l'accès aux données via la variable globale `$_SESSION`, qui réclame un appel préalable  explicite de `session_start()`.\n\nLorsque vous travaillez avec les données de session qui sont des tableaux, le composant `session` possède une limitation qui vous empêche de modifier directement un des élément de ces tableaux. Par exemple :\n\n```php\n$session = Yii::$app->session;\n\n// le code suivant ne fonctionne PAS\n$session['captcha']['number'] = 5;\n$session['captcha']['lifetime'] = 3600;\n\n// le code suivant fonctionne :\n$session['captcha'] = [\n    'number' => 5,\n    'lifetime' => 3600,\n];\n\n// le code suivant fonctionne également :\necho $session['captcha']['lifetime'];\n```\n\nVous pouvez utiliser une des solutions de contournement suivantes pour résoudre ce problème :\n\n\n```php\n$session = Yii::$app->session;\n\n// utiliser directement  $_SESSION (assurez-vous que  Yii::$app->session->open() a été appelée)\n$_SESSION['captcha']['number'] = 5;\n$_SESSION['captcha']['lifetime'] = 3600;\n\n// obtenir le tableau complet d'abord, le modifier et le sauvegarder\n$captcha = $session['captcha'];\n$captcha['number'] = 5;\n$captcha['lifetime'] = 3600;\n$session['captcha'] = $captcha;\n\n// utiliser un  ArrayObject au lieu d'un tableau\n$session['captcha'] = new \\ArrayObject;\n...\n$session['captcha']['number'] = 5;\n$session['captcha']['lifetime'] = 3600;\n\n// stocker les données du tableau par une clé avec un préfixe commun \n$session['captcha.number'] = 5;\n$session['captcha.lifetime'] = 3600;\n```\n\nPour une meilleure performance et une meilleure lisibilité du code, nous recommandons la dernière solution de contournement. Elle consiste, au lieu de stocker un tableau comme une donnée de session unique, à stocker chacun des éléments du tableau comme une variable de session qui partage le même préfixe de clé avec le reste des éléments de ce tableau.\n\n\n### Stockage de session personnalisé <span id=\"custom-session-storage\"></span>\n\nLa classe par défaut [[yii\\web\\Session]] stocke les données de session sous forme de fichiers sur le serveur. Yii fournit également des classes de session qui mettent en œuvre des procédés de stockage différents. En voici la liste :\n\n* [[yii\\web\\DbSession]]: stocke les données de session dans une base de données. \n* [[yii\\web\\CacheSession]]: stocke les données de session dans un cache avec l'aide d'un [composant cache](caching-data.md#cache-components) configuré.\n* [[yii\\redis\\Session]]: stocke les données de session en utilisant le médium de stockage [redis](https://redis.io/) as the storage medium.\n* [[yii\\mongodb\\Session]]: stocke les données de session dans une base de données de documents [MongoDB](https://www.mongodb.com/).\n\nToutes ces classes de session prennent en charge le même jeu de méthodes d'API. En conséquence, vous pouvez changer de support de stockage sans avoir à modifier le code de votre application qui utilise ces sessions. \n\n> Note: si vous voulez accéder aux données de session via `$_SESSION` quand vous êtes en train d'utiliser une session à stockage personnalisé, vous devez vous assurer que cette session a été préalablement démarrée via  [[yii\\web\\Session::open()]]. Cela est dû au fait que les gestionnaires de stockage des sessions personnalisées sont enregistrés à l'intérieur de cette méthode. \n\n> Note : si vous utilisez un stockage de session personnalisé, vous devez configurer le collecteur de déchets de session explicitement. \nQuelques installations de PHP (p. ex. Debian) utilisent une probabilité de collecteur de déchets de 0 et nettoient les fichiers de session hors ligne dans une tâche de cron. Ce processus ne s'applique pas à votre stockage personnalisé, c'est pourquoi vous devez configurer  \n  [[yii\\web\\Session::$GCProbability]] pour utiliser une valeur non nulle.\n\nPour savoir comment configurer et utiliser ces classes de composant, reportez-vous à leur documentation d'API. Ci-dessous, nous présentons un exemple de configuration de [[yii\\web\\DbSession]] dans la configuration de l'application pour utiliser une base de données en tant que support de stockage d'une session :\n\n\n```php\nreturn [\n    'components' => [\n        'session' => [\n            'class' => 'yii\\web\\DbSession',\n            // 'db' => 'mydb',  // l'identifiant du composant d'application de la connexion à la base de données. Valeur par défaut : 'db'.\n            // 'sessionTable' => 'my_session', // nom de la table 'session' . Valeur par défaut : 'session'.\n        ],\n    ],\n];\n```\n\nVous devez aussi créer la base de données suivante pour stocker les données de session :\n\n```sql\nCREATE TABLE session\n(\n    id CHAR(40) NOT NULL PRIMARY KEY,\n    expire INTEGER,\n    data BLOB\n)\n```\n\noù 'BLOB' fait référence au type « grand objet binaire » (binary large objet — BLOB) de votre système de gestion de base de données (DBMS) préféré. Ci-dessous, vous trouverez les types de BLOB qui peuvent être utilisés par quelques DBMS populaires :\n- MySQL: LONGBLOB\n- PostgreSQL: BYTEA\n- MSSQL: BLOB\n\n> Note: en fonction des réglages de `session.hash_function` dans votre fichier php.ini, vous devez peut-être ajuster la longueur de la colonne `id`. Par exemple, si  `session.hash_function=sha256`, vous devez utiliser une longueur de 64 au lieu de 40. \n\nCela peut être accompli d'une façon alternative avec la migration suivante :\n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m170529_050554_create_table_session extends Migration\n{\n    public function up()\n    {\n        $this->createTable('{{%session}}', [\n            'id' => $this->char(64)->notNull(),\n            'expire' => $this->integer(),\n            'data' => $this->binary()\n        ]);\n        $this->addPrimaryKey('pk-id', '{{%session}}', 'id');\n    }\n\n    public function down()\n    {\n        $this->dropTable('{{%session}}');\n    }\n}\n```\n  \n\n### Donnés flash <span id=\"flash-data\"></span>\n\nLes données flash sont une sorte de données de session spéciale qui, une fois définies dans une requête, ne restent disponibles que durant la requête suivante et sont détruites automatiquement ensuite. Les données flash sont le plus communément utilisées pour mettre en œuvre des messages qui doivent être présentés  une seule  fois, comme les messages de confirmation affichés après une soumission réussie de formulaire. \n\nVous pouvez définir des données flash et y accéder via le composant d'application `session`. Par exemple :\n\n```php\n$session = Yii::$app->session;\n\n// Request #1\n// définit un message flash nommé \"commentDeleted\"\n$session->setFlash('commentDeleted', 'Vous avez réussi la suppression de votre commentaire.');\n\n// Request #2\n// affiche le message  flash nommé \"commentDeleted\"\necho $session->getFlash('commentDeleted');\n\n// Request #3\n// $result est faux puisque le message flash a été automatiquement supprimé\n$result = $session->hasFlash('commentDeleted');\n```\n\nComme les données de session ordinaires, vous pouvez stocker des données arbitraires sous forme de données flash.\n\nVous pouvez appeler [[yii\\web\\Session::setFlash()]], cela écrase toute donnée flash préexistante qui a le même nom. Pour ajouter une nouvelle donnée flash à un message existant, vous pouvez utiliser [[yii\\web\\Session::addFlash()]] à la place. Par exemple :\n\n\n```php\n$session = Yii::$app->session;\n\n// Request #1\n// ajoute un message flash nommé  \"alerts\"\n$session->addFlash('alerts', 'Vous avez réussi la suppression de votre commentaire');\n$session->addFlash('alerts', 'Vous avez réussi l'ajout d'un ami.');\n$session->addFlash('alerts', 'Vous êtes promu.');\n\n// Request #2\n// $alerts est un tableau de messages flash nommé \"alerts\"\n$alerts = $session->getFlash('alerts');\n```\n\n> Note: évitez d'utiliser [[yii\\web\\Session::setFlash()]] en même temps que  [[yii\\web\\Session::addFlash()]] pour des données flash de même nom. C'est parce que la deuxième méthode transforme automatiquement les données flash en tableau pour pouvoir y ajouter des données. En conséquence, quand vous appelez [[yii\\web\\Session::getFlash()]], vous pouvez parfois recevoir un tableau ou une chaîne de caractères selon l'ordre dans lequel ces méthodes ont été appelées. \n\n> Tip: pour afficher des messages Flash vous pouvez utiliser l'objet graphique  [[yii\\bootstrap\\Alert|bootstrap Alert]] de la manière suivante :\n>\n> ```php\n> echo Alert::widget([\n>    'options' => ['class' => 'alert-info'],\n>    'body' => Yii::$app->session->getFlash('postDeleted'),\n> ]);\n> ```\n\n\n## Témoins de connexion <span id=\"cookies\"></span>\n\nYii représente chacun des témoins de connexion sous forme d'objet de classe [[yii\\web\\Cookie]]. Les objets [[yii\\web\\Request]] et [[yii\\web\\Response]] contiennent une collection de témoins de connexion via la propriété nommée  `cookies`. La collection de témoins de connexion dans le premier de ces objets est celle soumise dans une requête, tandis que celle du deuxième objet représente les témoins de connexion envoyés à l'utilisateur. \n\nLa partie de l'application qui traite la requête et la réponse directement est le contrôleur. Par conséquent, les témoins de connexion doivent être lus et envoyés dans le contrôleur. \n\n### Lecture des  témoins de connexion <span id=\"reading-cookies\"></span>\n\nVous pouvez obtenir les témoins de connexion de la requête courante en utilisant le code suivant :\n\n```php\n// obtient la collection de témoins de connexion (yii\\web\\CookieCollection) du composant \"request\" \n$cookies = Yii::$app->request->cookies;\n\n// obtient la valeur du témoin de connexion  \"language\". Si le témoin de connexion n'existe pas, retourne  \"en\" par défaut.\n$language = $cookies->getValue('language', 'en');\n\n// une façon alternative d'obtenir la valeur du témoin de connexion \"language\" \nif (($cookie = $cookies->get('language')) !== null) {\n    $language = $cookie->value;\n}\n\n// vous pouvez aussi utiliser  $cookies comme un tableau\nif (isset($cookies['language'])) {\n    $language = $cookies['language']->value;\n}\n\n// vérifie si un témoin de connexion \"language\" existe\nif ($cookies->has('language')) ...\nif (isset($cookies['language'])) ...\n```\n\n\n### Envoi de  témoins de connexion <span id=\"sending-cookies\"></span>\n\nVous pouvez envoyer des témoins de connexion à l'utilisateur final avec le code suivant :\n\n```php\n// obtient la collection de témoins de connexion (yii\\web\\CookieCollection) du composant \"response\" \n$cookies = Yii::$app->response->cookies;\n\n// ajoute un témoin de connexion à la réponse à envoyer\n$cookies->add(new \\yii\\web\\Cookie([\n    'name' => 'language',\n    'value' => 'zh-CN',\n]));\n\n// supprime un cookie\n$cookies->remove('language');\n// équivalent à\nunset($cookies['language']);\n```\n\nEn plus des propriétés  [[yii\\web\\Cookie::name|name (nom)]], [[yii\\web\\Cookie::value|value (valeur)]] montrées dans les exemples ci-dessus, la classe  [[yii\\web\\Cookie]] définit également d'autres propriétés pour représenter complètement toutes les informations de témoin de connexion disponibles, comme les propriétés [[yii\\web\\Cookie::domain|domain (domaine)]], [[yii\\web\\Cookie::expire|expire (date d'expiration)]]. Vous pouvez configurer ces propriété selon vos besoins pour préparer un témoin de connexion et ensuite l'ajouter à la collection de témoins de connexion de la réponse.\n\n> Note: pour une meilleure sécurité, la valeur par défaut de la propriété [[yii\\web\\Cookie::httpOnly]] est définie à `true`. Cela permet de limiter le risque qu'un script client n'accède à un témoin de connexion protégé (si le navigateur le prend en charge). Reportez-vous à l'[article de wiki httpOnly](https://owasp.org/www-community/HttpOnly) pour plus de détails.\n\n\n### Validation des témoins de connexion <span id=\"cookie-validation\"></span>\n\nLorsque vous lisez ou envoyez des témoins de connexion via les composants `request` et `response` comme expliqué dans les sous-sections qui précèdent, vous appréciez la sécurité additionnelle de validation des témoins de connexion qui protège vos témoins de connexion de la modification côté client. Cela est réalisé en signant chacun des témoins de connexion  avec une valeur de hachage qui permet à l'application de dire si un témoin de connexion a été modifié ou pas du côté client. Si c'est le cas, le témoin de connexion n'est PLUS accessible via la [[yii\\web\\Request::cookies|collection de témoins de connexion]] du composant  `request`.\n\n> Note: la validation des témoins de connexion ne protège que contre les effets de la modification des valeurs de témoins de connexion. Néanmoins, si un témoin de connexion ne peut être validé, vous pouvez continuer à y accéder via la variable globale `$_COOKIE`. Ceci est dû au fait que les bibliothèques de tierces parties peuvent manipuler les témoins de connexion d'une façon qui leur est propre, sans forcément impliquer la validation des témoins de connexion. \n\nLa validation des témoins de connexion est activée par défaut. Vous pouvez la désactiver en définissant la propriété  [[yii\\web\\Request::enableCookieValidation]] à `false` (faux) mais nous vous recommandons fortement de ne pas le faire. \n\n> Note: les témoins de connexion qui sont lus/écrits directement via `$_COOKIE` et `setcookie()` ne seront PAS validés.\n\nQuand vous utilisez la validation des témoins de connexion, vous devez spécifier une  [[yii\\web\\Request::cookieValidationKey |clé de validation des témoins de connexion]] gui sera utilisée pour générer la valeur de hachage dont nous avons parlé plus haut. Vous pouvez faire ça en configurant le composant  `request` dans la configuration de l'application configuration comme indiqué ci-après :\n\n```php\nreturn [\n    'components' => [\n        'request' => [\n            'cookieValidationKey' => 'entrez une clé secrète ici',\n        ],\n    ],\n];\n```\n\n> Info: la [[yii\\web\\Request::cookieValidationKey|clé de validation des témoins de connexion (cookieValidationKey)]] est un élément critique de la sécurité de votre application. Elle ne devrait être connue que des personnes à qui vous faites confiance. Ne le stockez pas dans le système de gestion des version. \n"
  },
  {
    "path": "docs/guide-fr/security-authentication.md",
    "content": "Authentification\n================\n\nL'authentification est le processus qui consiste à vérifier l'identité d'un utilisateur. Elle utilise ordinairement un identifiant (p. ex. un nom d'utilisateur ou une adresse de courriels) et un jeton secret (p. ex. un mot de passe ou un jeton d'accès) pour juger si l'utilisateur est bien qui il prétend être. L'authentification est à la base de la fonctionnalité de connexion.\n\nYii fournit une base structurée d'authentification  qui interconnecte des composants variés pour prendre en charge la connexion. Pour utiliser cette base structurée, vous devez essentiellement accomplir les tâches suivantes :\n\n* Configurer le composant d'application [[yii\\web\\User|user]] ;\n* Créer une classe qui implémente l'interface [[yii\\web\\IdentityInterface]].\n\n\n## Configuration de  [[yii\\web\\User]] <span id=\"configuring-user\"></span>\n\nLe composant d'application [[yii\\web\\User|user]] gère l'état d'authentification de l'utilisateur. Il requiert que vous spécifiiez une [[yii\\web\\User::identityClass|classe d'identité]] contenant la logique réelle d'authentification.\nDans la configuration suivante de l'application, la [[yii\\web\\User::identityClass|classe d'identité]] pour [[yii\\web\\User|user]] est configurée sous le nom `app\\models\\User` dont la mise en œuvre est expliquée dans la sous-section suivante :\n\n```php\nreturn [\n    'components' => [\n        'user' => [\n            'identityClass' => 'app\\models\\User',\n        ],\n    ],\n];\n```\n\n\n## Mise en œuvre de [[yii\\web\\IdentityInterface]] <span id=\"implementing-identity\"></span>\n\nLa  [[yii\\web\\User::identityClass|classe d'identité]] doit implémenter l'interface [[yii\\web\\IdentityInterface]] qui comprend les méthodes suivantes :\n\n* [[yii\\web\\IdentityInterface::findIdentity()|findIdentity()]]: cette méthode recherche une instance de la classe d'identité à partir de l'identifiant utilisateur spécifié. Elle est utilisée lorsque vous devez conserver l'état de connexion via et durant la session.\n* [[yii\\web\\IdentityInterface::findIdentityByAccessToken()|findIdentityByAccessToken()]]: cette méthode recherche une instance de la classe d'identité à partir du jeton d'accès spécifié. Elle est utilisée lorsque vous avez besoin d'authentifier un utilisateur par un jeton secret (p. ex. dans une application pleinement REST sans état).\n* [[yii\\web\\IdentityInterface::getId()|getId()]]: cette méthode retourne l'identifiant de l'utilisateur que cette instance de la classe d'identité représente.\n* [[yii\\web\\IdentityInterface::getAuthKey()|getAuthKey()]]: cette méthode retourne une clé utilisée pour vérifier la connexion basée sur les témoins de connexion (*cookies*). La clé est stockée dans le témoin de connexion `login` et est ensuite comparée avec la version côté serveur pour s'assurer que le témoin de connexion est valide.\n* [[yii\\web\\IdentityInterface::validateAuthKey()|validateAuthKey()]]: cette méthode met en œuvre la logique de vérification de la clé de connexion basée sur les témoins de connexion.\n\nSi une méthode particulière n'est pas nécessaire, vous devez l'implémenter avec un corps vide. Par exemple, si votre application est une application sans état et pleinement REST, vous devez seulement implémenter [[yii\\web\\IdentityInterface::findIdentityByAccessToken()|findIdentityByAccessToken()]] et [[yii\\web\\IdentityInterface::getId()|getId()]] et laisser toutes les autres méthodes avec un corps vide.\n\nDans l'exemple qui suit, une [[yii\\web\\User::identityClass|classe d'identité]] est mise en œuvre en tant que classe [Active Record](db-active-record.md) associée à la table de base de données  `user`.\n\n```php\n<?php\n\nuse yii\\db\\ActiveRecord;\nuse yii\\web\\IdentityInterface;\n\nclass User extends ActiveRecord implements IdentityInterface\n{\n    public static function tableName()\n    {\n        return 'user';\n    }\n\n    /**\n     * Trouve une identité à partir de l'identifiant donné.\n     *\n     * @param string|int $id l'identifiant à rechercher\n     * @return IdentityInterface|null l'objet identité qui correspond à l'identifiant donné\n     */\n    public static function findIdentity($id)\n    {\n        return static::findOne($id);\n    }\n\n    /**\n     * Trouve une identité à partir du jeton donné\n     *\n     * @param string $token le jeton à rechercher\n     * @return IdentityInterface|null l'objet identité qui correspond au jeton donné\n     */\n    public static function findIdentityByAccessToken($token, $type = null)\n    {\n        return static::findOne(['access_token' => $token]);\n    }\n\n    /**\n     * @return int|string l'identifiant de l'utilisateur courant\n     */\n    public function getId()\n    {\n        return $this->id;\n    }\n\n    /**\n     * @return string la clé d'authentification de l'utilisateur courant\n     */\n    public function getAuthKey()\n    {\n        return $this->auth_key;\n    }\n\n    /**\n     * @param string $authKey\n     * @return bool si la clé d'authentification est valide pour l'utilisateur courant\n     */\n    public function validateAuthKey($authKey)\n    {\n        return $this->getAuthKey() === $authKey;\n    }\n}\n```\n\nComme nous l'avons expliqué précédemment, vous devez seulement implémenter `getAuthKey()` et `validateAuthKey()` si votre application utilise la fonctionnalité de connexion basée sur les témoins de connexion. Dans ce cas, vous devez utiliser le code suivant pour générer une clé d'authentification pour chacun des utilisateurs et la stocker dans la table `user` :\n\n```php\nclass User extends ActiveRecord implements IdentityInterface\n{\n    ......\n\n    public function beforeSave($insert)\n    {\n        if (parent::beforeSave($insert)) {\n            if ($this->isNewRecord) {\n                $this->auth_key = \\Yii::$app->security->generateRandomString();\n            }\n            return true;\n        }\n        return false;\n    }\n}\n```\n\n> Note: ne confondez pas la classe d'identité `User` avec la classe [[yii\\web\\User]]. La première est la classe mettant en œuvre la logique d'authentification. Elle est souvent mise en œuvre sous forme de classe [Active Record](db-active-record.md) associée à un moyen de stockage persistant pour conserver les éléments d'authentification de l'utilisateur. La deuxième est une classe de composant d'application qui gère l'état d'authentification de l'utilisateur.\n\n\n## Utilisation de [[yii\\web\\User]] <span id=\"using-user\"></span>\n\nVous utilisez  [[yii\\web\\User]] essentiellement en terme de composant d'application `user`.\n\nVous pouvez détecter l'identité de l'utilisateur courant en utilisant l'expression `Yii::$app->user->identity`. Elle retourne une instance de la [[yii\\web\\User::identityClass|classe d'identité]] représentant l'utilisateur connecté actuellement ou `null` si l'utilisateur courant n'est pas authentifié (soit un simple visiteur). Le code suivant montre comment retrouver les autres informations relatives à l'authentification à partir de [[yii\\web\\User]]:\n\n```php\n// l'identité de l'utilisateur courant. Null si l'utilisateur n'est pas authentifié.\n$identity = Yii::$app->user->identity;\n\n// l'identifiant de l'utilisateur courant. Null si l'utilisateur n'est pas authentifié.\n$id = Yii::$app->user->id;\n\n// si l'utilisateur courant est un visiteur (non authentifié).\n$isGuest = Yii::$app->user->isGuest;\n```\n\nPour connecter un utilisateur, vous devez utiliser le code suivant :\n\n```php\n// trouve une identité d'utilisateur à partir du nom d'utilisateur spécifié\n// notez que vous pouvez vouloir vérifier le mot de passe si besoin.\n$identity = User::findOne(['username' => $username]);\n\n// connecte l'utilisateur\nYii::$app->user->login($identity);\n```\n\nLa méthode  [[yii\\web\\User::login()]] assigne l'identité de l'utilisateur courant à [[yii\\web\\User]]. Si la session est [[yii\\web\\User::enableSession|activée]], elle conserve l'identité de façon à ce que l'état d'authentification de l'utilisateur soit maintenu durant la session tout entière. Si la connexion basée sur les témoins de connexion (*cookies*) est [[yii\\web\\User::enableAutoLogin|activée]], elle sauvegarde également l'identité dans un témoin de connexion de façon à ce que l'état d'authentification de l'utilisateur puisse être récupéré du témoin de connexion durant toute la période de validité du témoin de connexion.\n\nPour activer la connexion basée sur les témoins de connexion, vous devez configurer [[yii\\web\\User::enableAutoLogin]] à `true` (vrai) dans la configuration de l'application. Vous devez également fournir une durée de vie lorsque vous appelez la méthode [[yii\\web\\User::login()]].\n\nPour déconnecter un utilisateur, appelez simplement\n\n```php\nYii::$app->user->logout();\n```\n\nNotez que déconnecter un utilisateur n'a de sens que si la session est activée. La méthode nettoie l'état d'authentification de l'utilisateur à la fois de la mémoire et de la session. Et, par défaut, elle détruit aussi *toutes* les données de session. Si vous voulez conserver les données de session, vous devez appeler  `Yii::$app->user->logout(false)`, à la place.\n\n\n## Événement d'authentification <span id=\"auth-events\"></span>\n\nLa classe [[yii\\web\\User]] lève quelques événements durant le processus de connexion et celui de déconnexion.\n\n* [[yii\\web\\User::EVENT_BEFORE_LOGIN|EVENT_BEFORE_LOGIN]]: levé au début de [[yii\\web\\User::login()]].\n  Si le gestionnaire d'événement définit la propriété  [[yii\\web\\UserEvent::isValid|isValid]] de l'objet événement à `false` (faux), le processus de connexion avorte.\n* [[yii\\web\\User::EVENT_AFTER_LOGIN|EVENT_AFTER_LOGIN]]: levé après une connexion réussie.\n* [[yii\\web\\User::EVENT_BEFORE_LOGOUT|EVENT_BEFORE_LOGOUT]]: levé au début de [[yii\\web\\User::logout()]].\n  Si le gestionnaire d'événement définit la propriété [[yii\\web\\UserEvent::isValid|isValid]] à `false` (faux) le processus de déconnexion avorte.\n* [[yii\\web\\User::EVENT_AFTER_LOGOUT|EVENT_AFTER_LOGOUT]]: levé après une déconnexion réussie.\n\nVous pouvez répondre à ces événements pour mettre en œuvre des fonctionnalités telles que l'audit de connexion, les statistiques d'utilisateurs en ligne. Par exemple, dans le gestionnaire pour l'événement [[yii\\web\\User::EVENT_AFTER_LOGIN|EVENT_AFTER_LOGIN]], vous pouvez enregistrer le temps de connexion et l'adresse IP dans la tale `user`.\n"
  },
  {
    "path": "docs/guide-fr/security-authorization.md",
    "content": "Autorisation\n=============\n\nL'autorisation est le processus qui vérifie si un utilisateur dispose des permissions suffisantes pour faire quelque chose. Yii fournit deux méthodes d'autorisation : le filtre de contrôle d'accès (ACF — Access Control Filter) et le contrôle d'accès basé sur les rôles (RBAC — Role-Based Access Control).\n\n\n## Filtre de contrôle d'accès <span id=\"access-control-filter\"></span>\n\nLe filtre de contrôle d'accès (ACF) est une simple méthode d'autorisation mise en œuvre sous le nom [[yii\\filters\\AccessControl]] qui trouve son meilleur domaine d'application dans les applications qui n'ont besoin que d'un contrôle d'accès simplifié. Comme son nom l'indique, le filtre de contrôle d'accès est un [filtre](structure-filters.md) d'action qui peut être utilisé dans un contrôleur ou dans un module. Quand un utilisateur requiert l'exécution d'une action, le filtre de contrôle d'accès vérifie une liste de [[yii\\filters\\AccessControl::rules|règles d'accès]] pour déterminer si l'utilisateur est autorisé à accéder à l'action requise.\n\nLe code ci-dessous montre comment utiliser le filtre de contrôle d'accès dans le contrôleur `site` :\n\n```php\nuse yii\\web\\Controller;\nuse yii\\filters\\AccessControl;\n\nclass SiteController extends Controller\n{\n    public function behaviors()\n    {\n        return [\n            'access' => [\n                'class' => AccessControl::class,\n                'only' => ['login', 'logout', 'signup'],\n                'rules' => [\n                    [\n                        'allow' => true,\n                        'actions' => ['login', 'signup'],\n                        'roles' => ['?'],\n                    ],\n                    [\n                        'allow' => true,\n                        'actions' => ['logout'],\n                        'roles' => ['@'],\n                    ],\n                ],\n            ],\n        ];\n    }\n    // ...\n}\n```\n\nDans le code précédent, le filtre de contrôle d'accès est attaché au contrôleur `site` en tant que comportement (*behavior*). C'est la manière typique d'utiliser un filtre d'action. L'option `only` spécifie que le filtre de contrôle d'accès doit seulement être appliqué aux actions `login`, `logout` et `signup`. Toutes les autres actions dans le contrôleur `site`ne sont pas sujettes au contrôle d'accès. L'option `rules` liste les [[yii\\filters\\AccessRule|règles d'accès]], qui se lisent comme suit :\n\n- Autorise tous les visiteurs (non encore authentifiés) à accéder aux actions `login` et `signup`. l'option `roles` contient un point d'interrogation `?` qui est un signe particulier représentant les « visiteurs non authentifiés ».\n- Autorise les utilisateurs authentifiés à accéder à l'action `logout`. L'arobase `@` est un autre signe particulier représentant les « utilisateurs authentifiés ».\n\nLe filtre de contrôle d'accès effectue les vérifications d'autorisation en examinant les règles d'accès une par une en commençant par le haut, jusqu'à ce qu'il trouve une règle qui correspond au contexte d'exécution courant. La valeur `allow` de la règle correspondante est utilisée ensuite pour juger si l'utilisateur est autorisé ou pas. Si aucune des règles ne correspond, cela signifie que l'utilisateur n'est PAS autorisé, et  le filtre de contrôle d'accès arrête la suite de l'exécution de l'action.\n\nQuand le filtre de contrôle d'accès détermine qu'un utilisateur n'est pas autorisé à accéder à l'action courante, par défaut, il prend les mesures suivantes :\n\n* Si l'utilisateur est un simple visiteur, il appelle [[yii\\web\\User::loginRequired()]] pour rediriger le navigateur de l'utilisateur sur la page de connexion.\n* Si l'utilisateur est déjà authentifié, il lève une exception [[yii\\web\\ForbiddenHttpException]].\n\nVous pouvez personnaliser ce comportement en configurant la propriété [[yii\\filters\\AccessControl::denyCallback]] comme indiqué ci-après :\n\n```php\n[\n    'class' => AccessControl::class,\n    ...\n    'denyCallback' => function ($rule, $action) {\n        throw new \\Exception('You are not allowed to access this page');\n    }\n]\n```\n\nLes [[yii\\filters\\AccessRule|règles d'accès]] acceptent beaucoup d'options. Ci-dessous, nous présentons un résumé des options acceptées. Vous pouvez aussi étendre la classe  [[yii\\filters\\AccessRule]] pour créer vos propres classe de règles d'accès.\n\n * [[yii\\filters\\AccessRule::allow|allow]]: spécifie s'il s'agit d'une règle \"allow\" (autorise) ou \"deny\" (refuse).\n\n * [[yii\\filters\\AccessRule::actions|actions]]: spécifie à  quelles actions cette règle correspond. Ce doit être un tableau d'identifiants d'action. La comparaison est sensible à la casse. Si cette option est vide ou non définie, cela signifie que la règle s'applique à toutes les actions.\n\n * [[yii\\filters\\AccessRule::controllers|controllers]]: spécifie à quels contrôleurs cette règle correspond. Ce doit être un tableau d'identifiants de contrôleurs. Si cette option est vide ou non définie, la règle s'applique à tous les contrôleurs.\n\n * [[yii\\filters\\AccessRule::roles|roles]]: spécifie à quels rôles utilisateur cette règle correspond. Deux rôles spéciaux sont reconnus, et ils sont vérifiés via [[yii\\web\\User::isGuest]]:\n\n     - `?`: correspond à un visiteur non authentifié.\n     - `@`: correspond à un visiteur authentifié.\n\n   L'utilisation d'autres noms de rôle déclenche l'appel de [[yii\\web\\User::can()]], qui requiert l'activation du contrôle d'accès basé sur les rôles qui sera décrit dans la prochaine sous-section. Si cette option est vide ou non définie, cela signifie que la règle s'applique à tous les rôles.\n\n * [[yii\\filters\\AccessRule::ips|ips]]: spécifie à quelles [[yii\\web\\Request::userIP|adresses IP de client]] cette règle correspond. Une adresse IP peut contenir le caractère générique `*` à la fin pour indiquer que la règle correspond à des adresses IP ayant le même préfixe. Par exemple, '192.168.*' correspond à toutes les adresse IP dans le segment '192.168.'. Si cette option est vide ou non définie, cela signifie que la règle s'applique à toutes les adresses IP.\n\n * [[yii\\filters\\AccessRule::verbs|verbs]]: spécifie à quelles méthodes de requête (p. ex. `GET`, `POST`) cette règle correspond. La comparaison est insensible à la casse.\n\n * [[yii\\filters\\AccessRule::matchCallback|matchCallback]]: spécifie une fonction de rappel PHP qui peut être appelée pour déterminer si cette règle s'applique.\n\n * [[yii\\filters\\AccessRule::denyCallback|denyCallback]]: spécifie une fonction de rappel PHP qui peut être appelée lorsqu'une règle refuse l'accès.\n\nCi-dessous nous présentons un exemple qui montre comment utiliser l'option `matchCallback`, qui vous permet d'écrire une logique d'accès arbitraire :\n\n```php\nuse yii\\filters\\AccessControl;\n\nclass SiteController extends Controller\n{\n    public function behaviors()\n    {\n        return [\n            'access' => [\n                'class' => AccessControl::class,\n                'only' => ['special-callback'],\n                'rules' => [\n                    [\n                        'actions' => ['special-callback'],\n                        'allow' => true,\n                        'matchCallback' => function ($rule, $action) {\n                            return date('d-m') === '31-10';\n                        }\n                    ],\n                ],\n            ],\n        ];\n    }\n\n    // Fonction de rappel appelée ! Cette page ne peut être accédée que chaque 31 octobre\n    public function actionSpecialCallback()\n    {\n        return $this->render('happy-halloween');\n    }\n}\n```\n\n\n## Contrôle d'accès basé sur les rôles <span id=\"rbac\"></span>\n\nLe contrôle d'accès basé sur les rôles (Role-Based Access Control – RBAC) fournit un contrôle d'accès centralisé simple mais puissant. Reportez-vous à [Wikipedia](https://fr.wikipedia.org/wiki/Contr%C3%B4le_d%27acc%C3%A8s_%C3%A0_base_de_r%C3%B4les) pour des détails comparatifs entre le contrôle d'accès basé sur les rôles et d'autres schéma de contrôle d'accès plus traditionnels.\n\nYii met en œuvre un contrôle d'accès basé sur les rôles général hiérarchisé, qui suit le  [modèle NIST RBAC](https://csrc.nist.gov/CSRC/media/Publications/conference-paper/1992/10/13/role-based-access-controls/documents/ferraiolo-kuhn-92.pdf). Il fournit la fonctionnalité de contrôle d'accès basé sur les rôles via le [composant d'application](structure-application-components.md)[[yii\\RBAC\\ManagerInterface|authManager]].\n\nL'utilisation du contrôle d'accès basé sur les rôles implique deux partie de travail. La première partie est de construire les données d'autorisation du contrôle d'accès basé sur les rôles, et la seconde partie est d'utiliser les données d'autorisation pour effectuer les vérifications d'autorisation d'accès là où elles sont nécessaires.\n\nPour faciliter la description qui suit, nous allons d'abord introduire quelques concepts sur le contrôle d'accès basé sur les rôles.\n\n\n### Concepts de base <span id=\"basic-concepts\"></span>\n\nUn rôle représente une collection de  *permissions* (p. ex. créer des articles, mettre des articles à jour). Un rôle peut être assigné à un ou plusieurs utilisateurs. Pour vérifier qu'un utilisateur dispose d'une permission spécifiée, nous pouvons vérifier si un rôle contenant cette permission a été assigné à l'utilisateur.\n\nAssociée à chacun des rôles, il peut y avoir une *règle*. Une règle représente un morceau de code à exécuter lors de l'accès pour vérifier si le rôle correspondant, ou la permission correspondante, s'applique à l'utilisateur courant. Par exemple, la permission « mettre un article à jour » peut disposer d'une règle qui  vérifie si l'utilisateur courant est celui qui a créé l'article. Durant la vérification de l'accès, si l'utilisateur n'est PAS le créateur de l'article, il est considéré comme ne disposant pas la permission « mettre un article à jour ».\n\nÀ la fois les rôles et les permissions peuvent être organisés en une hiérarchie. En particulier, un rôle peut être constitué d'autres rôles ou permissions ; Yii met en œuvre une hiérarchie *d'ordre partiel* qui inclut la hiérarchie plus spécifique dite *en arbre*. Tandis qu'un rôle peut contenir une permission, l'inverse n'est pas vrai.\n\n### Configuration du contrôle d'accès basé sur les rôles <span id=\"configuring-rbac\"></span>\n\nAvant que nous ne nous lancions dans la définition des données d'autorisation et effectuions la vérification d'autorisation d'accès, nous devons configurer le composant d'application [[yii\\base\\Application::authManager|gestionnaire d'autorisations (*authManager*)]]. Yii fournit deux types de gestionnaires d'autorisations : [[yii\\rbac\\PhpManager]] et  [[yii\\rbac\\DbManager]]. Le premier utilise un script PHP pour stocker les données d'autorisation, tandis que le second stocke les données d'autorisation dans une base de données. Vous pouvez envisager d'utiliser le premier si votre application n'a pas besoin d'une gestion des rôles et des permissions très dynamique.\n\n\n#### Utilisation de `PhpManager` <span id=\"using-php-manager\"></span>\n\nLe code qui suit montre comment configurer la propriété `authManager` dans la configuration de l'application en utilisant la classe  [[yii\\rbac\\PhpManager]] :\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'authManager' => [\n            'class' => 'yii\\rbac\\PhpManager',\n        ],\n        // ...\n    ],\n];\n```\n\nLe gestionnaire  `authManager` peut désormais être obtenu via `\\Yii::$app->authManager`.\n\nPar défaut, [[yii\\rbac\\PhpManager]] stocke les données du contrôle d'accès basé sur les rôles dans des fichiers du dossier `@app/rbac`. Assurez-vous que le dossier et tous les fichiers qui sont dedans sont accessibles en écriture par le processus du serveur Web si la hiérarchie des permissions a besoin d'être changée en ligne.\n\n\n#### Utilisation de  `DbManager` <span id=\"using-db-manager\"></span>\n\nLe code qui suit monte comment configurer la propriété `authManager`  dans la configuration de l'application en utilisant la classe [[yii\\rbac\\DbManager]] :\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'authManager' => [\n            'class' => 'yii\\rbac\\DbManager',\n        ],\n        // ...\n    ],\n];\n```\n> Note: si vous utilisez le modèle d'application yii2-basic-app, il y a un fichier de configuration `config/console.php` où la propriété `authManager` doit être également déclarée en plus de  `config/web.php`.\n\n> Dans le cas du modèle  yii2-advanced-app, la propriété `authManager` doit être déclarée seulement une fois dans `common/config/main.php`.\n\n`DbManager` utilise quatre tables de base de données pour stocker ses données :\n\n- [[yii\\rbac\\DbManager::$itemTable|itemTable]]: la table pour stocker les items d'autorisation. Valeur par défaut « auth_item ».\n- [[yii\\rbac\\DbManager::$itemChildTable|itemChildTable]]: la table pour stocker la hiérarchie des items d'autorisation. Valeur par défaut « auth_item_child ».\n- [[yii\\rbac\\DbManager::$assignmentTable|assignmentTable]]: la table pour stocker les assignations d'items d'autorisation. Valeur par défaut « auth_assignment ».\n- [[yii\\rbac\\DbManager::$ruleTable|ruleTable]]: la table pour stocker les règles. Valeur par défaut « auth_rule ».\n\nAvant de continuer vous devez créer ces tables dans la base de données. Pour le faire , vous pouvez utiliser la migration stockée dans  `@yii/rbac/migrations`:\n\n`yii migrate --migrationPath=@yii/rbac/migrations`\n\nLe gestionnaire d'autorisations  `authManager` peut désormais être obtenu par  `\\Yii::$app->authManager`.\n\n\n### Construction des données d'autorisation  <span id=\"generating-rbac-data\"></span>\n\nConstruire les donnés d'autorisation consiste à effectuer les tâches suivantes :\n\n- définir les rôles et les permissions ;\n- établir les relations entre les rôles et les permissions ;\n- définir les règles ;\n- associer les règles avec les rôles et les permissions ;\n- assigner des rôles aux utilisateurs.\n\nSelon les exigences de flexibilité des autorisations, les tâches énumérées ci-dessus peuvent être accomplies de différentes manières :\n\nSi la hiérarchie de vos permissions ne change pas du tout et que vous avez un nombre fixé d'utilisateurs, vous pouvez créer une [commande de console](tutorial-console.md#create-command) qui initialise les données d'autorisation une fois via l'API que fournit `authManager`:\n\n```php\n<?php\nnamespace app\\commands;\n\nuse Yii;\nuse yii\\console\\Controller;\n\nclass RbacController extends Controller\n{\n    public function actionInit()\n    {\n        $auth = Yii::$app->authManager;\n\n        // ajoute une permission  \"createPost\"\n        $createPost = $auth->createPermission('createPost');\n        $createPost->description = 'Créer un article';\n        $auth->add($createPost);\n\n        // ajoute une permission  \"updatePost\"\n        $updatePost = $auth->createPermission('updatePost');\n        $updatePost->description = 'Mettre à jour un article';\n        $auth->add($updatePost);\n\n        // ajoute un rôle  \"author\" et donne à ce rôle la permission \"createPost\"\n        $author = $auth->createRole('author');\n        $auth->add($author);\n        $auth->addChild($author, $createPost);\n\n        // ajoute un rôle \"admin\" role et donne à ce rôle la permission \"updatePost\"\n        // aussi bien que les permissions du rôle \"author\"\n        $admin = $auth->createRole('admin');\n        $auth->add($admin);\n        $auth->addChild($admin, $updatePost);\n        $auth->addChild($admin, $author);\n\n        // Assigne des rôles aux utilisateurs. 1 et 2 sont des identifiants retournés par IdentityInterface::getId()\n        // ordinairement mis en œuvre dans votre modèle  User .\n        $auth->assign($author, 2);\n        $auth->assign($admin, 1);\n    }\n}\n```\n\n> Note: si vous utilisez le modèle avancé, vous devez mettre votre `RbacController` dans le dossier  `console/controllers` et changer l'espace de noms en `console\\controllers`.\n\nAprès avoir exécuté la commande `yii rbac/init` vous vous retrouverez avec la hiérarchie suivante :\n\n![Hiérarchie simple du contrôle d'accès basé sur les rôles](images/rbac-hierarchy-1.png \"Simple RBAC hierarchy\")\n\nLe rôle *Author* peut créer des articles, le rôle *admin* peut mettre les articles à jour et faire tout ce que le rôle *author* peut faire.\n\nSi votre application autorise l'enregistrement des utilisateurs, vous devez assigner des rôles à ces nouveaux utilisateurs une fois. Par exemple, afin que tous les utilisateurs enregistrés deviennent des auteurs (rôle *author*) dans votre modèle de projet avancé, vous devez modifier la méthode `frontend\\models\\SignupForm::signup()` comme indiqué ci-dessous :\n\n```php\npublic function signup()\n{\n    if ($this->validate()) {\n        $user = new User();\n        $user->username = $this->username;\n        $user->email = $this->email;\n        $user->setPassword($this->password);\n        $user->generateAuthKey();\n        $user->save(false);\n\n        // Ces trois lignes ont été ajoutées\n        $auth = Yii::$app->authManager;\n        $authorRole = $auth->getRole('author');\n        $auth->assign($authorRole, $user->getId());\n\n        return $user;\n    }\n\n    return null;\n}\n```\n\nPour les applications qui requièrent un contrôle d'accès complexe avec des autorisations mises à jour dynamiquement, des interfaces utilisateur spéciales (c.-à-d. un panneau d'administration) doivent être  développées en utilisant l'API offerte par `authManager`.\n\n\n### Utilisation des règles <span id=\"using-rules\"></span>\n\nComme mentionné plus haut, les règles ajoutent des contraintes supplémentaires aux rôles et aux permissions. Une règle est une classe qui étend la classe [[yii\\rbac\\Rule]]. Elle doit implémenter la méthode [[yii\\rbac\\Rule::execute()|execute()]]. Dans la hiérarchie, que nous avons créée précédemment le rôle *author* ne peut pas modifier ses propres articles. Essayons de régler ce problème. Tout d'abord, nous devons vérifier que l'utilisateur courant est l'auteur de l'article :\n\n```php\nnamespace app\\rbac;\n\nuse yii\\rbac\\Rule;\n\n/**\n * Vérifie si l'identifiant de l'auteur correspond à celui passé en paramètre\n */\nclass AuthorRule extends Rule\n{\n    public $name = 'isAuthor';\n\n    /**\n     * @param string|int $user l'identifiant de l'utilisateur.\n     * @param Item $item le rôle ou la permission avec laquelle cette règle est associée\n     * @param array $params les paramètres passés à ManagerInterface::checkAccess().\n     * @return bool une valeur indiquant si la règles autorise le rôle ou la permission qui lui est associé.\n     */\n    public function execute($user, $item, $params)\n    {\n        return isset($params['post']) ? $params['post']->createdBy == $user : false;\n    }\n}\n```\n\nLa règles ci-dessus vérifie si l'article `post` a été créé par l'utilisateur `$user`. Nous allons créer une permission spéciale  `updateOwnPost` dans la commande que nous avons utilisée précédemment :\n\n```php\n$auth = Yii::$app->authManager;\n\n// ajoute la règle\n$rule = new \\app\\rbac\\AuthorRule;\n$auth->add($rule);\n\n// ajoute la permission \"updateOwnPost\" et associe lui la règle\n$updateOwnPost = $auth->createPermission('updateOwnPost');\n$updateOwnPost->description = 'Mettre à jour un des ses propres articles';\n$updateOwnPost->ruleName = $rule->name;\n$auth->add($updateOwnPost);\n\n// \"updateOwnPost\" sera utilisé depuis  \"updatePost\"\n$auth->addChild($updateOwnPost, $updatePost);\n\n// autorise les utilisateurs ayant le rôle  \"author\" à mettre à jour leurs propres articles.\n$auth->addChild($author, $updateOwnPost);\n```\n\nNous nous retrouvons avec la hiérarchie suivante :\n\n![Hiérarchie du contrôle d'accès basé sur les rôles avec un règle](images/rbac-hierarchy-2.png \"RBAC hierarchy with a rule\")\n\n\n### Vérification de l'autorisation d'accès <span id=\"access-check\"></span>\n\nAvec les données d'autorisation préparées, la vérification de l'autorisation d'accès est aussi simple que d'appeler la méthode [[yii\\rbac\\ManagerInterface::checkAccess()]]. Étant donné que la plupart des vérification d'autorisation d'accès concernent l'utilisateur courant, pour commodité, Yii procure une méthode raccourcie [[yii\\web\\User::can()]], qui peut être utilisée comme suit :\n\n```php\nif (\\Yii::$app->user->can('createPost')) {\n    // create post\n}\n```\n\nSi l'utilisateur courant est  Jane avec l'identifiant `ID=1`, nous commençons à `createPost` et essayons d'arriver à `Jane`:\n\n![Vérification d'autorisation d'accès](images/rbac-access-check-1.png \"Access check\")\n\nAfin de vérifier sur un utilisateur peut mettre un article à jour, nous devons passer un paramètre supplémentaire qui est requis par la règle `AuthorRule` décrite précédemment :\n\n```php\nif (\\Yii::$app->user->can('updatePost', ['post' => $post])) {\n    // met à jour l'article\n}\n```\n\nIci que se passe-t-il si l'utilisateur courant est John:\n\n\n![Vérification d'autorisation d'accès](images/rbac-access-check-2.png \"Access check\")\n\nNous commençons à  `updatePost` et passons par `updateOwnPost`. Afin d'obtenir l'autorisation,  la méthode `execute()` de `AuthorRule` doit retourner  `true` (vrai). La méthode reçoit ses paramètres `$params` de l'appel à la  méthode `can()` et sa valeur est ainsi `['post' => $post]`. Si tout est bon, nous arrivons à `author` auquel John est assigné.\n\nDans le cas de Jane, c'est un peu plus simple puisqu'elle a le rôle admin:\n\n![Vérification d'autorisation d'accès](images/rbac-access-check-3.png \"Access check\")\n\nDans votre contrôleur, il y a quelques façons de mettre en œuvre les autorisations. Si vous voulez des permission granulaires qui séparent l'accès entre ajouter et supprimer, alors vous devez vérifier l'accès pour chacune des actions. Vous pouvez soit utiliser la condition ci-dessus dans chacune des méthodes d'action, ou utiliser [[yii\\filters\\AccessControl]] :\n\n```php\npublic function behaviors()\n{\n    return [\n        'access' => [\n            'class' => AccessControl::class,\n            'rules' => [\n                [\n                    'allow' => true,\n                    'actions' => ['index'],\n                    'roles' => ['managePost'],\n                ],\n                [\n                    'allow' => true,\n                    'actions' => ['view'],\n                    'roles' => ['viewPost'],\n                ],\n                [\n                    'allow' => true,\n                    'actions' => ['create'],\n                    'roles' => ['createPost'],\n                ],\n                [\n                    'allow' => true,\n                    'actions' => ['update'],\n                    'roles' => ['updatePost'],\n                ],\n                [\n                    'allow' => true,\n                    'actions' => ['delete'],\n                    'roles' => ['deletePost'],\n                ],\n            ],\n        ],\n    ];\n}\n```\n\nSi toutes les opérations CRUD sont gérées ensemble, alors c'est une bonne idée que d'utiliser une permission unique comme  `managePost` (gérer article), et de la vérifier dans [[yii\\web\\Controller::beforeAction()]].\n\n### Utilisation des rôles par défaut <span id=\"using-default-roles\"></span>\n\nUn rôle par défaut est un rôle qui est assigné *implicitement* à tous les *utilisateurs*. L'appel de la méthode [[yii\\rbac\\ManagerInterface::assign()]] n'est pas nécessaire, et les données d'autorisations ne contiennent pas ses informations d'assignation.\n\nUn rôle par défaut est ordinairement associé à une règle qui détermine si le rôle s'applique à l'utilisateur en cours de vérification.\n\nLes rôles par défaut sont souvent utilisés dans des applications qui ont déjà une sorte d'assignation de rôles. Par  exemple, un application peut avoir une colonne « group » dans sa table des utilisateurs pour représenter à quel groupe de privilèges chacun des utilisateurs appartient. Si chaque groupe de privilèges peut être mis en correspondance avec un rôle du contrôle d'accès basé sur les rôles, vous pouvez utiliser la fonctionnalité de rôle par défaut pour assigner automatiquement un rôle du contrôle d'accès basé sur les rôles à chacun des utilisateurs. Prenons un exemple pour montrer comment cela se fait.\n\nSupposons que dans la table des utilisateurs, il existe en colonne `group` qui utilise la valeur 1 pour représenter le groupe des administrateurs et la valeur 2 pour représenter le groupe des auteurs. Vous envisagez d'avoir deux rôles dans le contrôle d'accès basé sur les rôles `admin` et`author` pour représenter les permissions de ces deux groupes respectivement. Vous pouvez configurer le contrôle d'accès basé sur les rôles comme suit :\n\n\n```php\nnamespace app\\rbac;\n\nuse Yii;\nuse yii\\rbac\\Rule;\n\n/**\n * Vérifie si le groupe utilisateurs correspond\n */\nclass UserGroupRule extends Rule\n{\n    public $name = 'userGroup';\n\n    public function execute($user, $item, $params)\n    {\n        if (!Yii::$app->user->isGuest) {\n            $group = Yii::$app->user->identity->group;\n            if ($item->name === 'admin') {\n                return $group == 1;\n            } elseif ($item->name === 'author') {\n                return $group == 1 || $group == 2;\n            }\n        }\n        return false;\n    }\n}\n\n$auth = Yii::$app->authManager;\n\n$rule = new \\app\\rbac\\UserGroupRule;\n$auth->add($rule);\n\n$author = $auth->createRole('author');\n$author->ruleName = $rule->name;\n$auth->add($author);\n// ... ajoute les  permissions en tant qu'enfant de  $author ...\n\n$admin = $auth->createRole('admin');\n$admin->ruleName = $rule->name;\n$auth->add($admin);\n$auth->addChild($admin, $author);\n// ... ajoute les  permissions en tant qu'enfant de  $admin ...\n```\n\nNotez que dans ce qui est présenté ci-dessus, comme « author » est ajouté en tant qu'enfant de « admin », lorsque vous implémentez la méthode `execute()` de la classe de règle, vous devez respecter cette hiérarchie elle aussi. C'est pourquoi, lorsque le nom de rôle est « author », la méthode `execute()` retourne `true` (vrai) si le groupe de l'utilisateur est soit 1, soit 2 (ce qui signifie que l'utilisateur est soit dans le groupe « admin », soit dans le groupe « author »).\n\nEnsuite, configurez `authManager` en listant les deux rôles dans  [[yii\\rbac\\BaseManager::$defaultRoles]]:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'authManager' => [\n            'class' => 'yii\\rbac\\PhpManager',\n            'defaultRoles' => ['admin', 'author'],\n        ],\n        // ...\n    ],\n];\n```\n\nDésormais, si vous effectuez une vérification d'autorisation d'accès, les deux rôles `admin` et `author` seront vérifiés en évaluant les règles qui leur sont associées. Si les règles retournent `true` (vrai), cela signifie que le rôle s'applique à l'utilisateur courant. En se basant sur la mise en œuvre des règles ci-dessus, cela signifie que si la valeur du `group` d'un utilisateur est 1, le rôle `admin` s'applique à l'utilisateur, si la valeur du `group` est  2, le rôle `author` s'applique.\n"
  },
  {
    "path": "docs/guide-fr/security-best-practices.md",
    "content": "Meilleures pratiques de sécurité\n================================\n\nCi-dessous, nous passons en revue les principes de sécurité courants et décrivons comment éviter les menaces lorsque vous développez une application Yii.\n\nPrincipes de base\n-----------------\n\nIl y a deux principes essentiels quand on en vient à traiter de la sécurité des applications quelles qu'elles soient :\n\n1. Filtrer les entrées.\n2. Échapper les sorties.\n\n\n### Filtrer les entrées\n\nFiltrer les entrées signifie que les entrées ne doivent jamais être considérées comme sures et que vous devriez toujours vérifier qu'une valeur que vous avez obtenue fait réellement partie de celles qui sont autorisées. Par exemple, si nous savons qu'un tri doit être fait sur la base de trois champs `title`, `created_at` et `status`, et que ces champs sont fournis sous forme d'entrées de l'utilisateur, il vaut mieux vérifier les valeurs exactement là où nous les recevons. En terme de PHP de base, ça devrait ressembler à ceci :\n\n```php\n$sortBy = $_GET['sort'];\nif (!in_array($sortBy, ['title', 'created_at', 'status'])) {\n\tthrow new Exception('Invalid sort value.');\n}\n```\n\nDans Yii, le plus probablement, vous utilisez la [validation de formulaire](input-validation.md) pour faire de telles vérifications. \n\n\n### Échapper les sorties\n\nÉchapper les sorties signifie que, selon le contexte dans lequel vous utilisez les données, elles doivent être échappées c.-à-d. dans le contexte de HTML vous devez échapper les caractères  `<`, `>` et autres caractères similaires. Dans le contexte de JavaScript ou de SQL, il s'agira d'un jeu différent de caractères. Comme échapper tout à la main serait propice aux erreurs, Yii fournit des outils variés pour effectuer l'échappement dans différents contextes. \n\nÉviter les injections SQL\n----------------------------\n\nLes injections SQL se produisent lorsque le texte d'une requête est formé en concaténant des chaînes non échappées comme la suivante :\n\n```php\n$username = $_GET['username'];\n$sql = \"SELECT * FROM user WHERE username = '$username'\";\n```\n\nAu lieu de fournir un nom d'utilisateur réel, l'attaquant pourrait donner à votre application quelque chose comme  `'; DROP TABLE user; --`.\nCe qui aboutirait à la requête  SQL suivante :\n\n```sql\nSELECT * FROM user WHERE username = ''; DROP TABLE user; --'\n```\n\nCela est une requête tout à fait valide qui recherche les utilisateurs avec un nom vide et détruit probablement la table `user`, ce qui conduit à un site Web cassé et à une perte de données (vous faites des sauvegardes régulières, pas vrai ?).\n\nDans Yii la plupart des requêtes de base de données se produisent via la classe [Active Record](db-active-record.md) qui utilise correctement des instructions PDO préparées en interne. En cas d'instructions préparées, il n'est pas possible de manipuler la requête comme nous le montrons ci-dessus.\n\nCependant, parfois, vous avez encore besoin de [requêtes brutes](db-dao.md) ou du  [constructeur de requêtes](db-query-builder.md). Dans ce cas, vous devriez passer les données de manière sure. Si les données sont utilisées pour des valeurs de colonnes, il est préférable d'utiliser des instructions préparées :\n\n```php\n// query builder\n$userIDs = (new Query())\n    ->select('id')\n    ->from('user')\n    ->where('status=:status', [':status' => $status])\n    ->all();\n\n// DAO\n$userIDs = $connection\n    ->createCommand('SELECT id FROM user where status=:status')\n    ->bindValues([':status' => $status])\n    ->queryColumn();\n```\n\nSi les données sont utilisées pour spécifier des noms de colonne ou des noms de table, la meilleure chose à faire est d'autoriser uniquement des jeux prédéfinis de valeurs : \n \n```php\nfunction actionList($orderBy = null)\n{\n    if (!in_array($orderBy, ['name', 'status'])) {\n        throw new BadRequestHttpException('Only name and status are allowed to order by.')\n    }\n    \n    // ...\n}\n```\n\nDans le cas où cela n'est pas possible, les noms de colonne et de table doivent être échappés. Yii a recours à une syntaxe spéciale pour un tel échappement qui permet de le faire d'une manière identique pour toutes les bases de données prises en charge :\n\n```php\n$sql = \"SELECT COUNT([[$column]]) FROM {{table}}\";\n$rowCount = $connection->createCommand($sql)->queryScalar();\n```\n\nVous pouvez obtenir tous les détails sur cette syntaxe dans la section [Échappement des noms de colonne et de table](db-dao.md#quoting-table-and-column-names).\n\n\nÉviter le XSS\n----------------\n\nLe XSS ou scriptage inter site se produit lorsque la sortie n'est pas échappée correctement lors de l'envoi de code HTML au navigateur. Par exemple, si l'utilisateur peut entrer son nom, et qu'au lieu de saisir `Alexander` il saisit `<script>alert('Hello!');</script>`, chaque page qui émet sont nom sans échappement exécute le code JavaScript `alert('Hello!');` ce qui se traduit par une boîte d'alerte jaillissant dans le navigateur. Selon le site web, au lieu de quelque chose d'aussi innocent, le script pourrait envoyer des messages en votre nom ou même effectuer des transactions bancaires. L'évitement de XSS est tout à fait facile. Il y a en général deux cas :\n\n1. Vous voulez que vos données soient transmises sous forme de texte simple. \n2. Vous voulez que vos données soient transmises sous forme de code HTML.\n\nSi vous désirez seulement du texte simple, l'échappement est aussi simple à réaliser que ce qui suit :\n\n\n```php\n<?= \\yii\\helpers\\Html::encode($username) ?>\n```\n\nSi ce doit être du code HTML vous pouvez obtenir de l'aide de HtmlPurifier:\n\n```php\n<?= \\yii\\helpers\\HtmlPurifier::process($description) ?>\n```\n\nNotez que le processus  de HtmlPurifier est très lourd, c'est pourquoi vous devez envisager la mise en cache.\n\nÉviter le CSRF\n-----------------\n\nLa CSRF est une abréviation de cross-site request forgery (falsification de requête inter sites). L'idée est que beaucoup d'applications partent du principe que les requêtes provenant d'un navigateur sont fabriquées par l'utilisateur lui-même. Cela peut être faux. \n\nPar exemple,  un site web `an.example.com` a une URL  `/logout`, qui, lorsqu'elle est accédée en utilisant une simple requête GET, déconnecte l'utilisateur. Tant qu'il s'agit d'une requête de l'utilisateur lui-même, tout va bien. Mais, un jour, des gens mal intentionnés, postent `<img src=\"https://an.example.com/logout\">` sur un forum que l'utilisateur visite fréquemment. Le navigateur ne fait pas de différence entre la requête d'une image et celle d'une page. C'est pourquoi, lorsque l'utilisateur ouvre une page avec une telle balise `img`, le navigateur envoie la requête GET vers cette URL, et l'utilisateur est déconnecté du site  `an.example.com`. \n\nC'est l'idée de base. D'aucuns diront que déconnecter un utilisateur n'a rien de très sérieux, mais les gens mal intentionnés peuvent faire bien plus, à partir de cette idée.  Imaginez qu'un site web possède une URL  `https://an.example.com/purse/transfer?to=anotherUser&amount=2000`. Accéder à cette URL en utilisant une requête GET, provoque le transfert de 2000 € d'un compte autorisé à l'utilisateur vers un autre compte `anotherUser`. Nous savons que le navigateur envoie toujours une requête GET pour charger une image. Nous pouvons donc modifier le code pour que seules des requêtes POST soient acceptées sur cette URL. Malheureusement, cela ne nous est pas d'un grand secours parce qu'un attaquant peut placer un peu le JavaScript à la place de la balise `<img>`, ce qui permet d'envoyer des requêtes POST sur cette URL:\n\nAfin d'éviter la falsification des requêtes inter-sites vous devez toujours :\n\n1. Suivre la spécification  HTTP c.-à-d. GET ne doit pas changer l'état de l'application. \n2. Tenir la protection Yii CSRF activée.\n\nParfois vous avez besoin de désactiver la validation CSRF pour un contrôleur ou une action. Cela peut être fait en définissant sa propriété :\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public $enableCsrfValidation = false;\n\n    public function actionIndex()\n    {\n        // la validation CSRF ne sera pas appliquée à cette action ainsi qu'aux autres.\n    }\n\n}\n```\n\nPour désactiver la validation CSRF pour des actions personnalisées vous pouvez faire :\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function beforeAction($action)\n    {\n        // ...définit `$this->enableCsrfValidation` ici en se basant sur quelques conditions...\n        // appelle la méthode  du parent qui vérifie CSRF si une telle propriété est vraie\n        return parent::beforeAction($action);\n    }\n}\n```\n\n\nÉviter l'exposition de fichiers\n-------------------------------------\n\nPar défaut, le racine du serveur web est censé pointer sur le dossier `web`, là où se trouve le fichier `index.php`. Dans le cas d'un hébergement partagé, il peut être impossible de réaliser cela et vous pouvez vous retrouver avec tout le code, configurations et journaux sous la racine du serveur web. \n\nSi c'est le cas, n'oubliez-pas de refuser l'accès à tout sauf au dossier `web`. Si cela n'est pas possible, envisagez d'héberger votre application ailleurs. \n\nÉviter les informations et des outils de débogage en mode production\n----------------------------------------------------------------------\n\nEn mode  débogage, Yii présente les erreurs de façon très verbeuse, ce qui s'avère très utile en développement. Le problème est que des erreurs aussi verbeuses sont pleines de renseignement pour l'attaquant lui aussi et peuvent révéler la structure de la base de données, les valeurs de configuration et des parties de votre code. Ne faites jamais tourner vos applications avec  `YII_DEBUG` définit à  `true` dans votre fichier `index.php`.\n\nVous ne devriez jamais activer  Gii en production. Il pourrait être utilisé pour obtenir des informations sur la structure de la base de données, sur le code et tout simplement réécrire du code avec celui généré par Gii. \n\nLa barre de débogage devrait être neutralisée en production sauf si vous en avez réellement besoin. Elle expose toute l'application et les détails de configuration. Si vous avez absolument besoin de cette barre, vérifier que cet accès est correctement réservé à votre adresse IP seulement.\n\nUtilisation de connexions sécurisées via TLS\n--------------------------------------------\n\nYii fournit des fonctionnalités qui comptent sur les témoins de connexion et/ou sur les sessions PHP. Cela peut créer des vulnérabilités dans le cas où votre connexion est compromise. Le risque est réduit si l'application utilise une connexion sécurisée via TLS. \nReportez-vous à la documentation de votre serveur web pour des instructions sur la manière de la configurer. Vous pouvez aussi jeter un coup d'œil aux exemples de configuration du projet H5BP : \n\n- [Nginx](https://github.com/h5bp/server-configs-nginx)\n- [Apache](https://github.com/h5bp/server-configs-apache).\n- [IIS](https://github.com/h5bp/server-configs-iis).\n- [Lighttpd](https://github.com/h5bp/server-configs-lighttpd).\n"
  },
  {
    "path": "docs/guide-fr/security-cryptography.md",
    "content": "Cryptographie\n=============\n\nDans cette section nous allons passer en revue les aspects suivants relatifs à la sécurité :\n\n- Génération de données aléatoires \n- Chiffrage et déchiffrage\n- Confirmation de l'intégrité des données\n\nGénération de données pseudo-aléatoires\n---------------------------------------\n\nLes données pseudo-aléatoires sont utiles dans de nombreuses situations. Par exemple, lors de la réinitialisation d'un mot de passe via courriel, vous devez générer un jeton, le sauvegarder dans la base de données, et l'envoyer à l'utilisateur afin qu'il puisse prouver qu'il est le détenteur du compte concerné. Il est très important que ce jeton soit unique et difficile à deviner, sinon il y aurait une possibilité que l'attaquant le devine et réinitialise le mot de passe de l'utilisateur.\n\nLes fonctions d'aide à la sécurité de Yii facilite la création de données pseudo-aléatoires :\n\n\n```php\n$key = Yii::$app->getSecurity()->generateRandomString();\n```\n\nChiffrage et déchiffrage\n----------------------\n\nYii fournit des fonctions d'aide pratiques qui vous permettent de chiffrer/déchiffrer les données en utilisant une clé secrète. Les données sont passées à la fonction de chiffrage de façon à ce que, seule la personne qui possède la clé secrète soit en mesure de les déchiffrer.\n\nPar exemple, nous avons besoin de stocker quelques informations dans notre base de données mais nous avons besoin de garantir que seul l'utilisateur qui dispose de la clé secrète soit en mesure des les visualiser (même si la base de données de l'application est compromise) :\n\n\n```php\n// $data et $secretKey sont obtenues du formulaire\n$encryptedData = Yii::$app->getSecurity()->encryptByPassword($data, $secretKey);\n// stocke  $encryptedData dans la base de données\n```\n\nPar la suite, lorsqu'un utilisateur désire lire les données :\n\n```php\n// $secretKey est obtenue de la saisie de l'utilisateur, $encryptedData provient de la base de données\n$data = Yii::$app->getSecurity()->decryptByPassword($encryptedData, $secretKey);\n```\n\nIl est également possible d'utiliser une clé à la place d'un mot de passe via [[\\yii\\base\\Security::encryptByKey()]] et\n[[\\yii\\base\\Security::decryptByKey()]].\n\nConfirmation de l'intégrité des données\n---------------------------------------\n\nIl y a des situations dans lesquelles vous avez besoin de vérifier que vos données n'ont pas été trafiquées par une tierce partie ou corrompue. Yii vous offre un moyen facile de confirmer l'intégrité des données sous forme d'une fonction d'aide.\n\nPréfixez les données par une valeur de hachage obtenue à l'aide de la clé secrète et des données. \n\n\n```php\n// $secretKey notre clé secrèe pour l'application ou l'utilisateur, $genuineData les données authentiques obtenues d'une source fiable.\n$data = Yii::$app->getSecurity()->hashData($genuineData, $secretKey);\n```\n\nVérifiez si l'intégrité des données est compromise.\n\n```php\n// $secretKey notre clé secrèe pour l'application ou l'utilisateur, $data données obtenues d'une source peu sûre\n$data = Yii::$app->getSecurity()->validateData($data, $secretKey);\n```\n"
  },
  {
    "path": "docs/guide-fr/security-overview.md",
    "content": "Sécurité\n========\n\nUne bonne sécurité est vitale pour la santé et le succès de toute application. Malheureusement, beaucoup de développeurs lésinent un peu quand ils en arrivent à la sécurité, soit par manque de compréhension ou parce que sa mise en œuvre n'est pas une mince affaire. Pour rendre votre application Yii aussi sûre que possible, Yii inclut plusieurs fonctionnalités de sécurité, excellentes et faciles à mettre en œuvre. \n\n* L'[authentification](security-authentication.md)\n* L'[autorisation](security-authorization.md)\n* Le [travail avec des mots de passe](security-passwords.md)\n* La [cryptographie](security-cryptography.md)\n* La [sécurité des vues](structure-views.md#security)\n* L'extension [Auth Clients](https://github.com/yiisoft/yii2-authclient/blob/master/docs/guide/README.md)\n* Les [bonnes pratiques](security-best-practices.md)\n"
  },
  {
    "path": "docs/guide-fr/security-passwords.md",
    "content": "Utilisation de mots de passe\n============================\n\nLa plupart des développeurs savent que les mots de passe ne peuvent pas être stockés « en clair », mais beaucoup d'entre-eux croient qu'il est toujours sûr des les hacher avec  `md5` ou `sha1`. Il fut un temps où utiliser ces algorithmes de hachage était suffisant, mais les matériels modernes font qu'il est désormais possible de casser de tels hachages – même les plus robustes – très rapidement en utilisant des attaques en force brute. \n\nPour apporter une sécurité améliorée pour les mots de passe des utilisateurs, même dans le pire des scénario (une brèche est ouverte dans votre application), vous devez utiliser des algorithmes de hachage qui résistent aux attaques en force brute. Le choix le meilleur couramment utilisé est `bcrypt`.\n\nEn  PHP, vous pouvez créer une valeur de hachage `bcrypt` à l'aide de la  [fonction crypt](https://www.php.net/manual/fr/function.crypt.php). Yii fournit deux fonctions d'aide qui facilitent l'utilisation de  `crypt` pour générer et vérifier des valeurs de hachage de manière sure. \n\nQuand un utilisateur fournit un mot de passe pour la première fois (p. ex. à l'enregistrement), le mot de passe doit être haché :\n\n\n```php\n$hash = Yii::$app->getSecurity()->generatePasswordHash($password);\n```\n\nLa valeur de hachage peut ensuite être associée à l'attribut du modèle correspondant afin de pouvoir être stockée dans la base de données pour utilisation ultérieure.\n\nLorsqu'un utilisateur essaye ensuite de se connecter, le mot de passe soumis est comparé au mot de passe précédemment haché et stocké : \n\n\n```php\nif (Yii::$app->getSecurity()->validatePassword($password, $hash)) {\n    // tout va bien, nous connectons l'utilisateur\n} else {\n    // wrong password\n}\n```\n"
  },
  {
    "path": "docs/guide-fr/start-databases.md",
    "content": "Travailler avec des bases de données\n======================\n\nCette section décrit comment créer une nouvelle page qui affiche des données pays récupérées dans une table de base \nde données nommée `country`. Pour ce faire, vous allez configurer une connexion à une base de données, créer une \nclasse [Active Record](db-active-record.md), définir une [action](structure-controllers.md), et créer une\n[vue](structure-views.md).\n\nAu long de ce tutoriel, vous apprendrez comment :\n\n* Configurer une connexion à une base de données\n* Définir une classe Active Record\n* Demander des données en utilisant la classe Active Record (enregistrement actif)\n* Afficher des données dans une vue paginée\n\nNotez que, pour finir cette section, vous aurez besoin d'avoir une connaissance basique des bases de données.\nEn particulier, vous devez savoir créer une base de données et exécuter des déclarations SQL en utilisant un client de\ngestion de bases de données.\n\n\nPréparer la Base de Données <span id=\"preparing-database\"></span>\n--------------------\n\nPour commencer, créez une base de données appelée `yii2basic`, depuis laquelle vous irez chercher les données dans votre application.\nVous pouvez créer une base de données SQLite, MySQL, PostgreSQL, MSSQL ou Oracle, car Yii gère nativement de nombreuses applications de base de données. Pour simplifier, nous supposerons que vous utilisez MySQL dans les descriptions qui suivent.\n\n>Note : bien que MariaDB a été un remplacement direct de MySQL, cela n'est plus complètement vrai. Dans le cas où vous auriez besoin de fonctionnalités avancées telles que la prise en charge de `JSON`, jetez un coup d'œil à la liste des extensions de MariaDB ci-dessous.\n\nCréez maintenant une table nommée `country` dans la base de données et insérez-y quelques données exemples. Vous pouvez exécuter l'instruction SQL suivante pour le faire : \n\n```sql\nCREATE TABLE `country` (\n  `code` CHAR(2) NOT NULL PRIMARY KEY,\n  `name` CHAR(52) NOT NULL,\n  `population` INT(11) NOT NULL DEFAULT '0'\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nINSERT INTO `country` VALUES ('AU','Australia',24016400);\nINSERT INTO `country` VALUES ('BR','Brazil',205722000);\nINSERT INTO `country` VALUES ('CA','Canada',35985751);\nINSERT INTO `country` VALUES ('CN','China',1375210000);\nINSERT INTO `country` VALUES ('DE','Germany',81459000);\nINSERT INTO `country` VALUES ('FR','France',64513242);\nINSERT INTO `country` VALUES ('GB','United Kingdom',65097000);\nINSERT INTO `country` VALUES ('IN','India',1285400000);\nINSERT INTO `country` VALUES ('RU','Russia',146519759);\nINSERT INTO `country` VALUES ('US','United States',322976000);\n```\n\nVous avez désormais une base de données appelée `yii2basic` comprenant une table `country` comportant trois colonnes et contenant dix lignes de données.\n\nConfigurer une Connexion à la BDD <span id=\"configuring-db-connection\"></span>\n---------------------------\n\nAvant de continuer, vérifiez que vous avez installé à la fois l'extension PHP \n[PDO](https://www.php.net/manual/fr/book.pdo.php) et le pilote PDO pour la base de données que vous utilisez (c'est\nà dire `pdo_mysql` pour MySQL). C'est une exigence de base si votre application utilise une base de données relationnelle.\n\nUne fois ces éléments installés, ouvrez le fichier `config/db.php` et modifiez les paramètres pour qu'ils correspondent à votre base de données. Par défaut, le fichier contient ce qui suit :\n\n```php\n<?php\n\nreturn [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=localhost;dbname=yii2basic',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n];\n```\n\nLe fichier `config/db.php` est un exemple type d'outil de [configuration](concept-configurations.md) basé sur un fichier. Ce fichier de configuration en particulier spécifie les paramètres nécessaires à la création et l'initialisation d'une instance de [[yii\\db\\Connection]] grâce à laquelle vous pouvez effectuer des requêtes SQL dans la base de données sous-jacente.\n\nOn peut accéder à connexion à la BDD configurée ci-dessus depuis le code de l'application via l'expression `Yii::$app->db`.\n\n> Info: le fichier `config/db.php` sera inclus par la configuration principale de l'application `config/web.php`, \n  qui spécifie comment l'instance d'[application](structure-applications.md) doit être initialisée.\n  Pour plus d'informations, reportez-vous à la section [Configurations](concept-configurations.md).\n\nSi vous avez besoin de fonctionnalités de base de données dont la prise en charge n'est pas comprise dans Yii, examinez les extensions suivantes: \n\n- [Informix](https://github.com/edgardmessias/yii2-informix)\n- [IBM DB2](https://github.com/edgardmessias/yii2-ibm-db2)\n- [Firebird](https://github.com/edgardmessias/yii2-firebird)\n- [MariaDB](https://github.com/sam-it/yii2-mariadb)\n\nCréer un Active Record <span id=\"creating-active-record\"></span>\n-------------------------\n\nPour représenter et aller chercher des données dans la table `country`, créez une classe dérivée d'[Active Record](db-active-record.md) appelée `Country`, et enregistrez-la dans le fichier `models/Country.php`.\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass Country extends ActiveRecord\n{\n}\n```\n\nLa classe `Country` étend [[yii\\db\\ActiveRecord]]. Vous n'avez pas besoin d'y écrire le moindre code ! Simplement, avec le code ci-dessus, Yii devine le nom de la table associée au nom de la classe. \n\n> Info: si aucune correspondance directe ne peut être faite à partir du nom de la classe, vous pouvez outrepasser la méthode [[yii\\db\\ActiveRecord::tableName()]] pour spécifier explicitement un nom de table.\n\nA l'aide de la classe `Country`, vous pouvez facilement manipuler les données de la table `country`, comme dans les bribes suivantes :\n\n```php\nuse app\\models\\Country;\n\n// chercher toutes les lignes de la table pays et les trier par \"name\"\n$countries = Country::find()->orderBy('name')->all();\n\n// chercher la ligne dont la clef primaire est \"US\"\n$country = Country::findOne('US');\n\n// afficher \"United States\"\necho $country->name;\n\n// remplace le nom du pays par \"U.S.A.\" et le sauvegarde dans la base de données\n$country->name = 'U.S.A.';\n$country->save();\n```\n\n> Info: Active Record (enregistrement actif) est un moyen puissant pour accéder et manipuler des données d'une base de manière orientée objet.\nVous pouvez trouver plus d'informations dans la section [Active Record](db-active-record.md). Sinon, vous pouvez également interagir avec une base de données en utilisant une méthode de plus bas niveau d'accès aux données appelée [Database Access Objects](db-dao.md).\n\n\nCréer une Action <span id=\"creating-action\"></span>\n------------------\n\nPour exposer les données pays aux utilisateurs, vous devez créer une action. Plutôt que de placer la nouvelle action \ndans le contrôleur `site`, comme vous l'avez fait dans les sections précédentes, il est plus cohérent de créer un \nnouveau contrôleur spécifique à toutes les actions liées aux données pays. Nommez ce contrôleur \n`CountryController`, et créez-y une action `index`, comme suit.\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\nuse yii\\data\\Pagination;\nuse app\\models\\Country;\n\nclass CountryController extends Controller\n{\n    public function actionIndex()\n    {\n        $query = Country::find();\n\n        $pagination = new Pagination([\n            'defaultPageSize' => 5,\n            'totalCount' => $query->count(),\n        ]);\n\n        $countries = $query->orderBy('name')\n            ->offset($pagination->offset)\n            ->limit($pagination->limit)\n            ->all();\n\n        return $this->render('index', [\n            'countries' => $countries,\n            'pagination' => $pagination,\n        ]);\n    }\n}\n```\n\nEnregistrez le code ci-dessus dans le fichier `controllers/CountryController.php`.\n\nL'action `index` appelle `Country::find()`. Cette méthode Active Record construit une requête de BDD et récupère toutes\nles données de la table `country`.\nPour limiter le nombre de pays retournés par chaque requête, la requête est paginée à l'aide d'un objet\n[[yii\\data\\Pagination]]. L'objet `Pagination` dessert deux buts :\n\n* Il ajuste les clauses `offset` et `limit` de la déclaration SQL représentée par la requête afin qu'elle en retourne\n  qu'une page de données à la fois (au plus 5 colonnes par page).\n* Il est utilisé dans la vue pour afficher un sélecteur de pages qui consiste en une liste de boutons de page, comme nous\n  l'expliquerons dans la prochaine sous-section.\n\nA la fin du code, l'action `index` effectue le rendu d'une vue nommée `index`, et lui transmet les données pays ainsi que les informations de pagination.\n\n\nCréer une Vue <span id=\"creating-view\"></span>\n---------------\n\nDans le dossier `views`, commencez par créer un sous-dossier nommé `country`. Ce dossier sera utilisé pour contenir\ntoutes les vues rendues par le contrôleur `country`. Dans le dossier `views/country`, créez un fichier nommé\n`index.php` contenant ce qui suit :\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\LinkPager;\n?>\n<h1>Countries</h1>\n<ul>\n<?php foreach ($countries as $country): ?>\n    <li>\n        <?= Html::encode(\"{$country->code} ({$country->name})\") ?>:\n        <?= $country->population ?>\n    </li>\n<?php endforeach; ?>\n</ul>\n\n<?= LinkPager::widget(['pagination' => $pagination]) ?>\n```\n\nLa vue comprend deux sections relatives à l'affichage des données pays. Dans la première partie, les données pays fournies\nsont parcourues et rendues sous forme de liste non ordonnée HTML.\nDans la deuxième partie, un objet graphique [[yii\\widgets\\LinkPager]] est rendu en utilisant les informations de pagination transmises par l'action.\nL'objet graphique `LinkPager` affiche une liste de boutons de page. Le fait de cliquer sur l'un deux rafraichit les données pays dans la page correspondante.\n\n\nEssayer <span id=\"trying-it-out\"></span>\n-------------\n\nPour voir comment tout le code ci-dessus fonctionne, pointez votre navigateur sur l'URL suivante :\n\n```\nhttps://hostname/index.php?r=country/index\n```\n\n![Liste de Pays](images/start-country-list.png)\n\nAu début, vous verrez une page affichant cinq pays. En dessous des pays, vous verrez un sélecteur de pages avec quatre boutons.\nSi vous cliquez sur le bouton \"2\", vous verrez la page afficher cinq autres pays de la base de données : la deuxième \npage d'enregistrements.\nObservez plus attentivement et vous noterez que l'URL dans le navigateur devient\n\n```\nhttps://hostname/index.php?r=country/index&page=2\n```\n\nEn coulisse, [[yii\\data\\Pagination|Pagination]] fournit toutes les fonctionnalités permettant de paginer un ensemble de données :\n\n* Au départ, [[yii\\data\\Pagination|Pagination]] représente la première page, qui reflète la requête SELECT de country\n  avec la clause `LIMIT 5 OFFSET 0`. Il en résulte que les cinq premiers pays seront trouvés et affichés.\n* L'objet graphique [[yii\\widgets\\LinkPager|LinkPager]] effectue le rendu des boutons de pages en utilisant les URL créées par \n  [[yii\\data\\Pagination::createUrl()|Pagination]]. Les URL contiendront le paramètre de requête `page`, qui représente\n  les différents numéros de pages.\n* Si vous cliquez sur le bouton de page \"2\", une nouvelle requête pour la route `country/index` sera déclenchée et \n  traitée.\n  [[yii\\data\\Pagination|Pagination]] lit le paramètre de requête `page` dans l'URL et met le numéro de page à 2.\n  La nouvelle requête de pays aura donc la clause `LIMIT 5 OFFSET 5` et retournera les cinq pays suivants pour être\n  affichés.\n\n\nRésumé <span id=\"summary\"></span>\n-------\n\nDans cette section, vous avez appris comment travailler avec une base de données. Vous avez également appris comment \nchercher et afficher des données dans des pages avec l'aide de [[yii\\data\\Pagination]] et de [[yii\\widgets\\LinkPager]].\n\nDans la prochaine section, vous apprendrez comment utiliser le puissant outil de génération de code, appelé \n[Gii](https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide), pour vous aider à rapidement mettre en œuvre des fonctionnalités communément requises, telles que les \nopérations Créer, Lire, Mettre à Jour et Supprimer (CRUD : Create-Read-Update-Delete) pour travailler avec les données \ndans une table de base de données. En fait, le code que vous venez d'écrire peut être généré automatiquement dans Yii \nen utilisant l'outil Gii.\n"
  },
  {
    "path": "docs/guide-fr/start-forms.md",
    "content": "Travailler avec les formulaires\n==================\n\nCette section décrit la création d'une nouvelle page comprenant un formulaire pour recevoir des données des \nutilisateurs.\nLa page affichera un formulaire composé d'un champ de saisie nom et un champ de saisie email.\nUne fois ces deux informations reçues de l'utilisateur, la page affichera les valeurs entrées pour confirmation.\n\nPour atteindre ce but, vous créerez non seulement une [action](structure-controllers.md) et deux \n[vues](structure-views.md), mais aussi un [modèle](structure-models.md).\n\nAu long de ce tutoriel, vous apprendrez à :\n\n* Créer un [modèle](structure-models.md) pour représenter les données saisies par un utilisateur à travers un \nformulaire\n* Déclarer des règles pour valider les données entrées\n* Construire un formulaire HTML dans une [vue](structure-views.md)\n\n\nCréer un Modèle <span id=\"creating-model\"></span>\n----------------\n\nLes données demandées à l'utilisateur seront représentées par une classe de modèle `EntryForm` comme montrée ci-dessous\nenregistrée dans le fichier `models/EntryForm.php`. Merci de vous référer à la section \n[Auto-chargement de Classes](concept-autoloading.md) pour plus de détails sur la convention de nommage de fichiers\nclasses.\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse yii\\base\\Model;\n\nclass EntryForm extends Model\n{\n    public $nom;\n    public $email;\n\n    public function rules()\n    {\n        return [\n            [['nom', 'email'], 'required'],\n            ['email', 'email'],\n        ];\n    }\n}\n```\n\nLa classe étend [[yii\\base\\Model]], une classe de base fournie par Yii, communément utilisée pour représenter des\ndonnées de formulaire.\n\n> Info: [[yii\\base\\Model]] est utilisée en tant que parent pour des classes modèles qui ne sont *pas* associées à des\ntables de base de données.\n[[yii\\db\\ActiveRecord]] est normalement le parent pour les classes modèles qui correspondent à des tables de bases de\ndonnées.\n\nLa classe `EntryForm` contient deux membres publics, `nom` et `email`, qui sont utilisés pour stocker les données\nsaisies par l'utilisateur. Elle contient également une méthode nommée `rules()`, qui renvoie un assortiment de règles\npour valider les données. Les règles de validation déclarées ci-dessus énoncent que\n\n* à la fois les valeurs de `nom` et `email` sont requises\n* la donnée `email` doit être une adresse email syntaxiquement valide\n\nSi vous avez un objet `EntryForm` peuplé par les données saisies par un utilisateur, vous pouvez appeler sa méthode\n[[yii\\base\\Model::validate()|validate()]] pour déclencher les routines de validation de données. Un échec de validation\nde données affectera la valeur `true` à la propriété [[yii\\base\\Model::hasErrors|hasErrors]], et vous pourrez connaître\nquelles erreurs de validations sont apparues via [[yii\\base\\Model::getErrors|errors]].\n\n```php\n<?php\n$model = new EntryForm();\n$model->nom = 'Qiang';\n$model->email = 'mauvais';\nif ($model->validate()) {\n    // Bien!\n} else {\n    // Echec!\n    // Use $model->getErrors()\n}\n```\n\n\nCréer une Action <span id=\"creating-action\"></span>\n------------------\n\nMaintenant, vous allez créer une action `entry` dans le contrôleur `site` qui utilisera le nouveau modèle. Le processus\nde création et d'utilisation d'actions a été expliqué dans la section [Hello World](start-hello.md).\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\nuse app\\models\\EntryForm;\n\nclass SiteController extends Controller\n{\n    // ...code existant...\n\n    public function actionEntry()\n    {\n        $model = new EntryForm;\n\n        if ($model->load(Yii::$app->request->post()) && $model->validate()) {\n            // données valides reçues dans $model\n\n            // faire quelque chose de significatif ici avec $model ...\n\n            return $this->render('entry-confirm', ['model' => $model]);\n        } else {\n            // soit la page est affichée au début soit il y a des erreurs de validation\n            return $this->render('entry', ['model' => $model]);\n        }\n    }\n}\n```\n\nL'action commence par créer un objet `EntryForm`. Puis, elle tente de peupler le modèle avec les données de `$_POST`,\nfournies dans yii par [[yii\\web\\Request::post()]].\nSi le modèle est peuplé avec succès (c'est à dire, si l'utilisateur a soumis le formulaire HTML), l'action appellera\n[[yii\\base\\Model::validate()|validate()]] pour s'assurer de la validité de toutes les valeurs.\n\n> Info: L'expression `Yii::$app` représente l'instance d'[application](structure-applications.md), qui est un singleton\naccessible de manière globale. C'est aussi un [annuaire de services](concept-service-locator.md) qui  fournit des\ncomposants tels que `request`, `response`, `db`, etc. pour assister des fonctionnalités spécifiques. Dans le code \nci-dessus, le composant `request` de l'instance d'application est utilisé pour accéder aux données `$_POST`.\n\nSi tout se passe bien, l'action effectuera le rendu d'une vue nommée `entry-confirm` pour confirmer le succès de la \nsoumission à l'utilisateur. Si aucune donnée n'est soumise ou si les données contiennent des erreurs, la vue `entry` \nsera générée, dans laquelle le formulaire HTML sera affiché, ainsi que tout message d'erreur de validation.\n\n> Note: Dans cet exemple très simple, nous effectuons le rendu de la page de confirmation après soumission de données\nvalides. En pratique, vous devriez envisager d'utiliser [[yii\\web\\Controller::refresh()|refresh()]] ou \n[[yii\\web\\Controller::redirect()|redirect()]] pour éviter les \n[problèmes de multiple soumission de formulaire](https://fr.wikipedia.org/wiki/Post-Redirect-Get).\n\n\nCréer des Vues <span id=\"creating-views\"></span>\n--------------\n\nEnfin, créez deux fichiers de vue nommés `entry-confirm` et `entry`. Ceux-ci seront rendus par l'action `entry`,\ncomme décrit précédemment.\n\nLa vue `entry-confirm` affiche simplement les données de nom et email. Elle doit être placée dans le fichier \n`views/site/entry-confirm.php`.\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n<p>Vous avez entré les informations suivantes :</p>\n\n<ul>\n    <li><label>Nom</label>: <?= Html::encode($model->nom) ?></li>\n    <li><label>Email</label>: <?= Html::encode($model->email) ?></li>\n</ul>\n```\n\nLa vue `entry` affiche un formulaire HTML. Elle doit être stockée dans le placée `views/site/entry.php`.\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n?>\n<?php $form = ActiveForm::begin(); ?>\n\n    <?= $form->field($model, 'nom') ?>\n\n    <?= $form->field($model, 'email') ?>\n\n    <div class=\"form-group\">\n        <?= Html::submitButton('Soumettre', ['class' => 'btn btn-primary']) ?>\n    </div>\n\n<?php ActiveForm::end(); ?>\n```\n\nLa vue utilise un [widget](structure-widgets.md) puissant appelé [[yii\\widgets\\ActiveForm|ActiveForm]] pour construire\nle formulaire HTML. Les méthodes `begin()` et `end()` du widget effectuent respectivement le rendu des tags ouvrant et\nfermant du formulaire. Entre les deux appels de méthode, des champs de saisie sont créés par la méthode \n[[yii\\widgets\\ActiveForm::field()|field()]]. Le premier champ de saisie concerne la donnée \"nom\", et le second la\ndonnée \"email\". Après les champs de saisie, la méthode [[yii\\helpers\\Html::submitButton()]] est appelée pour générer un\nbouton de soumission.\n\n\nEssayer <span id=\"trying-it-out\"></span>\n-------------\n\nPour voir comment ça fonctionne, utilisez votre navigateur pour accéder à l'URL suivante :\n\n```\nhttps://hostname/index.php?r=site/entry\n```\n\nVous verrez une page affichant un formulaire comportant deux champs de saisie. Devant chaque champ de saisie, une\nétiquette indique quelle donnée est attendue. Si vous cliquez sur le bouton de soumission sans entrer quoi que ce soit,\nou si vous ne fournissez pas d'adresse email valide, vous verrez un message d'erreur s'afficher à coté de chaque champ\nde saisie posant problème.\n\n![Formulaire Comportant des Erreurs de Validation](images/start-form-validation.png)\n\nAprès avoir saisi un nom et une adresse email valide et cliqué sur le bouton de soumission, vous verrez une nouvelle\npage affichant les données que vous venez de saisir.\n\n![Confirmation de la Saisie de Données](images/start-entry-confirmation.png)\n\n\n\n### La Magie expliquée <span id=\"magic-explained\"></span>\n\nVous vous demandez peut-être comment le formulaire HTML fonctionne en coulisse, parce qu'il semble presque magique\nqu'il puisse afficher une étiquette pour chaque champ de saisie et afficher sans rafraichir la page des messages \nd'erreur si vous n'entrez pas les données correctement.\n\nOui, la validation de données est initialement faite coté client en Javascript, et ensuite effectuée coté serveur en \nPHP.\n[[yii\\widgets\\ActiveForm]] est suffisamment intelligent pour extraire les règles de validation que vous avez déclarées \ndans `EntryForm`, le transformer en code Javascript exécutable, et utiliser le Javascript pour effectuer la validation\ndes données. Dans le cas où vous auriez désactivé le Javascript sur votre navigateur, la validation sera tout de même \neffectuée coté serveur, comme montré dans la méthode `actionEntry()`. Cela garantit la validité des données en toutes \ncirconstances.\n\n> Warning: La validation coté client est un confort qui permet une meilleure expérience utilisateur. La validation coté serveur est toujours nécessaire, que la validation coté client soit ou non en place.\n\nLes étiquettes des champs de saisie sont générés par la méthode `field()`, en utilisant les noms des propriété du\nmodèle.\nPar exemple, l'étiquette `Nom` sera générée à partir de la propriété `nom`. \n\nVous pouvez personnaliser une étiquette dans une vue en employant le code suivant :\n\n```php\n<?= $form->field($model, 'nom')->label('Votre Nom') ?>\n<?= $form->field($model, 'email')->label('Votre Email') ?>\n```\n\n> Info: Yii fournit ne nombreux widgets pour vous aider à construire rapidement des vues complexes et dynamiques.\n  Comme vous l'apprendrez plus tard, écrire un widget et aussi extrêmement simple. Vous voudrez sans doute transformer   une grande partie de votre code de vues en widgets réutilisables pour simplifier les développements de vues futurs.\n\n\nRésumé <span id=\"summary\"></span>\n-------\n\nDans cette section du guide, vous avez touché toutes les parties du patron de conception MVC. Vous avez appris à créer \nune classe modèle pour représenter les données utilisateur et valider lesdites données.\n\nVous avez également appris comment recevoir des données des utilisateurs et comment les réafficher dans le navigateur. \nC'est une tâche qui peut prendre beaucoup de temps lors du développement d'une application, mais Yii propose des \nwidgets puissants pour rendre cette tâche très facile.\n\nDans la prochaine section, vous apprendrez comment travailler avec des bases de données, qui sont nécessaires dans presque toutes les applications.\n"
  },
  {
    "path": "docs/guide-fr/start-gii.md",
    "content": "Générer du code avec Gii\n========================\n\nCette section décrit comment utiliser [Gii](https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide) pour générer du code qui met automatiquement en œuvre des fonctionnalités courantes de sites Web. Utiliser Gii pour auto-générer du code consiste simplement à saisir les bonnes informations en suivant les instructions affichées sur les pages Web de Gii.\n\nAu long de ce tutoriel, vous apprendrez comment :\n\n* Activer Gii dans votre application\n* Utiliser Gii pour générer des classes Active Record (enregistrement actif)\n* Utiliser Gii pour générer du code mettant en œuvre les opérations CRUD pour une table de BDD\n* Personnaliser le code généré par Gii\n\n\nDémarrer Gii <span id=\"starting-gii\"></span>\n------------\n\n[Gii](https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide) est fourni dans Yii en tant que [module](structure-modules.md). Vous pouvez activer Gii en le \nconfigurant dans la propriété [[yii\\base\\Application::modules|modules]] de l’application. En fonction de la manière dont vous avez créé votre application, il se peut que le code suivant soit déjà fourni dans le fichier de configuration `config/web.php`:\n\n```php\n$config = [ ... ];\n\nif (YII_ENV_DEV) {\n    $config['bootstrap'][] = 'gii';\n    $config['modules']['gii'] = [\n        'class' => 'yii\\gii\\Module',\n    ];\n}\n```\n\nLa configuration ci-dessus établit que dans un [environnement de développement](concept-configurations.md#environment-constants), l’application doit inclure un module appelé `gii`, qui est de classe [[yii\\gii\\Module]].\n\nSi vous vérifiez le [script de démarrage](structure-entry-scripts.md) `web/index.php` de votre application, vous y trouverez les lignes suivantes, qui en gros, font que `YII_ENV_DEV` est défini à `true` (vrai).\n\n```php\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n```\n\nGrâce à cette ligne, votre application est en mode développement, et active Gii, suivant la configuration  vue ci-dessus. Vous pouvez maintenant accéder à Gii via l’URL suivante :\n\n```\nhttps://hostname/index.php?r=gii\n```\n\n> Note : si vous accédez à Gii depuis une machine autre que localhost, l’accès sera refusé par défaut pour des raisons \n> de sécurité. Vous pouvez configurer Gii pour ajouter les adresses IP autorisées comme suit,\n>\n```php\n'gii' => [\n    'class' => 'yii\\gii\\Module',\n    'allowedIPs' => ['127.0.0.1', '::1', '192.168.0.*', '192.168.178.20'] // ajustez cela suivant vos besoins\n],\n```\n\n![Gii](images/start-gii.png)\n\n\nGénérer une Classe Active Record <span id=\"generating-ar\"></span>\n---------------------------------\n\nPour générer une classe Active Record avec Gii, sélectionnez le \"Model Generator\" (générateur de modèle), en cliquant sur le lien dans la page d'accueil de Gii, puis complétez le formulaire comme suit :\n\n* Table Name: `country`\n* Model Class: `Country`\n\n![Générateur de Modèles](images/start-gii-model.png)\n\nEnsuite, cliquez sur le bouton \"Preview\" (prévisualiser). Vous verrez que `models/Country.php` est listé comme fichier de classe à créer. Vous pouvez cliquer sur le nom du fichier de classe pour prévisualiser son contenu.\n\nSi vous avez déjà créé le même fichier, il sera écrasé. Cliquez sur le bouton `diff`\nà côté du nom de fichier pour voir les différences entre le fichier à générer et la version existante.\n\n![Prévisualisation du générateur de modèle](images/start-gii-model-preview.png)\n\nPour écraser un fichier existant, cochez la case située à côté de \"overwrite\" (écraser), puis cliquez sur le bouton \"Generate\" (générer). Pour créer un nouveau fichier, il suffit de cliquer sur \"Generate\". \n\nEn fin d'opération, vous verrez une page de confirmation indiquant que le code a été généré avec succès. Si vous aviez un fichier existant, vous verrez également un message indiquant qu’il a été écrasé par le code nouvellement généré.\n\n\nGénérer du Code CRUD <span id=\"generating-crud\"></span>\n--------------------\n\nCRUD signifie Create, Read, Update, and Delete (Créer, Lire, Mettre à Jour et Supprimer), soit les quatre tâches communes concernant des données sur la plupart des sites Web. Pour créer les fonctionnalités CRUD en utilisant Gii, sélectionnez le \"CRUD Generator\" en cliquant sur le lien dans la page d'accueil de Gii. Pour l’exemple de \"country\", remplissez le formulaire résultant comme suit :\n\n* Model Class: `app\\models\\Country`\n* Search Model Class: `app\\models\\CountrySearch`\n* Controller Class: `app\\controllers\\CountryController`\n\n![CRUD Generator](images/start-gii-crud.png)\n\nEnsuite, cliquez sur le bouton \"Preview\" (prévisualiser). Vous verrez une liste de fichiers à générer, comme ci-dessous.\n\n![CRUD Generator Preview](images/start-gii-crud-preview.png)\n\nSi vous aviez précédemment créé les fichiers  `controllers/CountryController.php` et\n`views/country/index.php` (dans la section bases de données du guide), cochez la case \"overwrite\" (écraser) pour les remplacer.\n(Les versions précédentes ne prenaient pas totalement en charge les fonctionnalités CRUD).\n\n\nEssayer <span id=\"trying-it-out\"></span>\n-------------\n\nPour voir comment ça fonctionne, utilisez votre navigateur pour accéder à l’URL suivant :\n\n```\nhttps://hostname/index.php?r=country/index\n```\n\nVous verrez une grille de données montrant les pays de la table de la base de données. Vous pouvez trier la table, ou lui appliquer des filtres en entrant des conditions de filtrage dans les entêtes de colonnes.\n\n\nPour chaque pays affiché dans la grille, vous pouvez choisir de visualiser les détails, le mettre à jour ou le supprimer.\nVous pouvez aussi cliquer sur le bouton \"Create Country\" (créer un pays) en haut de la grille pour que Yii vous présente un formulaire permettant de créer un nouveau pays.\n\n![Grille de Données Pays](images/start-gii-country-grid.png)\n\n![Mettre à Jour un Pays](images/start-gii-country-update.png)\n\nCe qui suit est la liste des fichiers générés par Gii, au cas où vous souhaiteriez investiguer la manière dont ces fonctionnalités sont mises en œuvre, ou les personnaliser :\n\n* Contrôleur: `controllers/CountryController.php`\n* Modèles: `models/Country.php` et `models/CountrySearch.php`\n* Vues: `views/country/*.php`\n\n> Info: Gii est conçu pour être un outil de génération de code hautement personnalisable et extensible. L’utiliser avec sagesse peut grandement accélérer le développement de vos applications. Pour plus de détails, merci de vous référer à la section [Gii](tool-gii.md).\n\n\nRésumé <span id=\"summary\"></span>\n-------\n\nDans cette section, vous avez appris à utiliser Gii pour générer le code qui met en œuvre une fonctionnalité CRUD complète pour des contenus stockés dans une table de base de données.\n\n"
  },
  {
    "path": "docs/guide-fr/start-hello.md",
    "content": "Hello World\n============\n\nCette section decrit la méthode pour créer une nouvelle page \"Hello\" dans votre application.\nPour ce faire, vous allez créer une [action](structure-controllers.md#creating-actions) et une [vue](structure-views.md):\n\n* L'application distribuera la requête à l'action\n* et à son tour, l'action générera un rendu de la vue qui affiche le mot \"Hello\" à l'utilisateur.\n\nA travers ce tutoriel, vous apprendrez trois choses :\n\n1. Comment créer une [action](structure-controllers.md) pour répondre aux requêtes,\n2. comment créer une [vue](structure-views.md) pour composer le contenu de la réponse, et\n3. comment une application distribue les requêtes aux [actions](structure-controllers.md#creating-actions).\n\n\nCréer une Action <span id=\"creating-action\"></span>\n------------------\n\nPour la tâche \"Hello\", vous allez créer une [action](structure-controllers.md#creating-actions) `dire` qui reçoit un paramètre\n`message` de la requête et affiche ce message à l'utilisateur. Si la requête ne fournit pas de paramètre `message`, l'action affichera le message par défaut \"Hello\".\n\n> Info: Les [actions](structure-controllers.md#creating-actions) sont les objets auxquels les utilisateurs peuvent directement   se référer pour les exécuter. Les actions sont groupées par [contrôleurs](structure-controllers.md). Le résultat de l'exécution d'une action est la réponse que l'utilisateur recevra.\n\nLes actions doivent être déclarées dans des [contrôleurs](structure-controllers.md). Par simplicité, vous pouvez déclarer l'action `dire` dans le contrôleur existant `SiteController`. Ce contrôleur  est défini dans le fichier classe `controllers/SiteController.php`. Voici le début de la nouvelle action :\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    // ...code existant...\n\n    public function actionDire($message = 'Hello')\n    {\n        return $this->render('dire', ['message' => $message]);\n    }\n}\n```\n\nDans le code ci-dessous, l'action `dire` est définie par une méthode nommée `actionDire` dans la classe `SiteController`.\nYii utilise le préfixe `action` pour faire la différence entre des méthodes actions et des méthodes non-actions dans une classe contrôleur.\nLe nom suivant le préfixe `action` est associé à l'ID de l'action.\n\nQuand vous choisissez le nom de vos actions, gardez à l'esprit comment Yii traite les IDs d'action. Les références aux IDs d'actions sont toujours effectuées en minuscules. Si un ID d'action nécessite plusieurs mots, ils seront concaténés à l'aide de tirets (par exemple `creer-commentaire`). Les noms de méthodes actions sont associés aux IDs d'actions en retirant tout tiret des IDs, en mettant la première lettre de chaque mot en majuscule, et en ajoutant un préfixe  `action` au résultat. Par exemple,\nl'ID d'action `creer-commentaire` correspond à l'action nommée `actionCreerCommentaire`.\n\nLa méthode action de notre exemple prend un paramètre `$message`, dont la valeur par défaut est `\"Hello\"` (de la même manière qu'on affecte une valeur par défaut à n'importe quel argument de fonction ou méthode en PHP). Quand l'application reçoit une requête et détermine que l'action `dire` est responsable de gérer ladite requête, l'application peuplera ce paramètre avec le paramètre du même nom trouvé dans la requête. En d'autres termes, si la requête contient un paramètre `message` ayant pour valeur `\"Goodbye\"`, la variable `$message` au sein de l'action recevra cette valeur.\n\nAu sein de la méthode action, [[yii\\web\\Controller::render()|render()]] est appelé pour effectuer le rendu d'un fichier [vue](structure-views.md) appelé `dire`. Le paramètre `message` est également transmis à la vue afin qu'il puisse y être utilisé. Le résultat du rendu est renvoyé à l'utilisateur par la méthode action. Ce résultat sera reçu par l'application et présenté à l'utilisateur dans le navigateur (en tant qu'élément d'une page HTML complète). \n\n\nCréer une Vue <span id=\"creating-view\"></span>\n---------------\n\nLes [vues](structure-views.md) sont des scripts qu'on écrit pour générer le contenu d'une réponse.\nPour la tâche \"Hello\", vous allez créer une vue `dire` qui affiche le paramètre `message` reçu de la méthode action, et passé par l'action à la vue :\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n<?= Html::encode($message) ?>\n```\n\nLa vue `dire` doit être enregistrée dans le fichier `views/site/dire.php`. Quand la méthode [[yii\\web\\Controller::render()|render()]]\nest appelée dans une action, elle cherchera un fichier PHP nommé `views/ControllerID/NomDeLaVue.php`.\n\nNotez que dans le code ci-dessus, le paramètre `message` est [[yii\\helpers\\Html::encode()|Encodé-HTML]]\navant d'être affiché. Cela est nécessaire car le paramètre vient de l'utilisateur, le rendant vulnérable aux [attaques cross-site scripting (XSS)](https://fr.wikipedia.org/wiki/Cross-site_scripting) en intégrant du code Javascript malicieux dans le paramètre.\n\nBien entendu, vous pouvez insérer plus de contenu dans la vue `dire`. Le contenu peut être des tags HTMML, du texte brut, ou même des expressions PHP.\nEn réalité, la vue `dire` est simplement un script PHP exécuté par la méthode [[yii\\web\\Controller::render()|render()]].\nLe contenu affiché par le script de vue sera renvoyé à l'application en tant que résultat de réponse. L'application renverra à son tour ce résultat à l'utilisateur.\n\n\nEssayer <span id=\"trying-it-out\"></span>\n-------------\n\nAprès avoir créé l'action et la vue, vous pouvez accéder à la nouvelle page en accédant à l'URL suivant :\n\n```\nhttps://hostname/index.php?r=site/dire&message=Hello+World\n```\n\n![Hello World](images/start-hello-world.png)\n\nLe résultat de cet URL sera une page affichant \"Hello World\". La page a les mêmes entête et pied de page que les autres pages de l'application. \n\nSi vous omettez le paramètre `message` dans l'URL, La page devrait simplement afficher \"Hello\". C'est parce que `message` est passé en paramètre de la méthode `actionDire()`, et quand il est omis, la valeur par défaut `\"Hello\"` sera employée à la place.\n\n> Info: L nouvelle page a les mêmes entête et pied de page que les autres pages parce que la méthode [[yii\\web\\Controller::render()|render()]] intègrera automatiquement le résultat de la vue `dire` dans une pseudo [mise en page](structure-views.md#layouts) qui dans notre cas est située dans `views/layouts/main.php`.\n\nLe paramètre `r` dans l'URL ci-dessus nécessite plus d'explications. Il signifie [route](runtime-routing.md), un ID unique commun toute l'application qui fait référence à une action. Le format de la route est `IDContrôleur/IDAction`. Quand l'application reçoit une requête, elle vérifie ce paramêtre, en utilisant la partie `IDContrôleur` pour déterminer quel classe contrôleur doit être instanciée pour traiter la requête. Ensuite, le contrôleur utilisera la partie `IDAction` pour déterminer quelle action doit être instanciée pour effectuer le vrait travail. Dans ce cas d'exemple, la route `site/dire`\nsera comprise comme la classe contrôleur `SiteController` et l'action `dire`. Il en resultera que la méthode `SiteController::actionDire()` sera appelée pour traiter la requête.\n\n> Info: De même que les actions, les contrôleurs ont des IDs qui les identifient de manière unique dans une application.\n  Les IDs de contrôleurs emploie les mêmes règles de nommage que les IDs d'actions. Les noms de classes Contrôleurs dérivent\n  des IDs de contrôleurs en retirant les tirets des IDs, en mettant la première lettre de chaque mot en majuscule,\n  et en suffixant la chaîne résultante du mot `Controller`. Par exemple, l'ID de contrôlleur `poster-commentaire` correspond\n  au nom de classe contrôleur `PosterCommentaireController`.\n\n\nRésumé <span id=\"summary\"></span>\n-------\n\nDans cette section, vous avez touché aux parties contrôleur et vue du patron de conception MVC.\nVous avez créé une action au sein d'un contrôleur pour traiter une requête spécifique. Vous avez également créé une vue pour composer le contenu de la réponse. Dans ce simple exemple, aucun modèle n'a été impliqué car les seules données utilisées étaient le paramètre `message`.\n\nVous avez également appris ce que sont les routes dans Yii, qu'elles font office de pont entre les requêtes utilisateur et les actions des contrôleurs.\n\nDans la prochaine section, vous apprendrez comment créer un modèle, et ajouter une nouvelle page contenant un formulaire HTML.\n"
  },
  {
    "path": "docs/guide-fr/start-installation.md",
    "content": "Installer Yii\n=============\n\nVous pouvez installer Yii de deux façons, en utilisant le gestionnaire de paquets [Composer](https://getcomposer.org/) ou en téléchargeant une archive.\nLa première méthode est conseillée, étant donné qu'elle permet d'installer de nouvelles [extensions](extend-creating-extensions.md) ou de mettre Yii à jour en exécutant simplement une commande.\n\nLes installations standard de Yii provoquent le téléchargement et l'installation d'un modèle de projet. Un modèle de projet et un projet Yii fonctionnel qui met en œuvre quelques fonctionnalités de base, telles que la connexion, le formulaire de contact, etc.\nSon code est organisé de la façon recommandée. En conséquence, c'est un bon point de départ pour vos propres projets.\n\nDans cette section et quelques-unes de ses suivantes, nous décrirons comment installer Yii avec le modèle baptisé *Basic Project Template* (modèle de projet de base) et comment mettre en œuvre de nouvelles fonctionnalités sur cette base. Yii vous offre également un autre modèle de projet appelé [Advanced Project Template ](https://www.yiiframework.com/extension/yiisoft/yii2-app-advanced/doc/guide) (modèle de projet avancé) qui convient mieux à un environnement de développement en équipe impliquant des tiers multiples. \n\n> Note: le modèle de projet de base conviendra à 90 pourcent des application Web. Il diffère du modèle de projet avancé essentiellement sur la manière dont le code est organisé. Si vous débutez avec Yii, nous vous conseillons fortement de vous en tenir au modèle de projet de base pour sa simplicité tout en disposant des fonctionnalités suffisantes.\n\n\nInstaller via Composer <span id=\"installing-via-composer\"></span>\n----------------------\n\n### Installer Composer\n\nSi vous n'avez pas déjà installé Composer, vous pouvez le faire en suivant les instructions du site [getcomposer.org](https://getcomposer.org/download/). \nSous Linux et Mac OS X, vous pouvez exécuter les commandes :\n\n```bash\ncurl -sS https://getcomposer.org/installer | php\nmv composer.phar /usr/local/bin/composer\n```\n\nSous Windows, téléchargez et exécutez [Composer-Setup.exe](https://getcomposer.org/Composer-Setup.exe).\n\nEn cas de problèmes, consultez la [section Troubleshooting (résolution des problèmes) de la documentation de Composer](https://getcomposer.org/doc/articles/troubleshooting.md),\n\nSi vous débutez avec Composer, nous vous recommandons au minimum la lecture de la section [Basic usage (utilisation de base)](https://getcomposer.org/doc/01-basic-usage.md) de la documentation de Composer\n\nDans ce guide, toutes les commandes de Composer suppose que vous avez installé Composer [globalement](https://getcomposer.org/doc/00-intro.md#globally) et qu'il est disponible par la commande `composer`. Si, au lieu de cela, vous utilisez `composer.phar` depuis un dossier local, vous devez adapter les exemples fournis en conséquence.\n\nSi Composer était déjà installé auparavant, assurez-vous d'utiliser une version à jour. Vous pouvez mettre Composer à jour avec la commande `composer self-update`.\n\n> Note: durant l'installation de Yii, Composer aura besoin d'obtenir de nombreuses informations de l'API de Github. Le nombre de requêtes dépend du nombre de dépendances de votre application et peut excéder la **Github API rate limit**. Si vous arrivez à cette limite, Composer peut vous demander vos identifiants de connexion pour obtenir un jeton d'accès à l'API de Github. Avec une connexion rapide, vous pouvez atteindre cette limite plus vite que Composer n'est capable de gérer. C'est pourquoi, nous vous recommandons de configurer ce jeton d'accès avant d'installer Yii.\n> Reportez-vous à la [documentation de Composer sur les jetons de l'API Github](https://getcomposer.org/doc/articles/troubleshooting.md#api-rate-limit-and-oauth-tokens)\n> pour savoir comment procéder.\n\n### Installer Yii <span id=\"installing-from-composer\"></span>\n\nAvec Composer installé, vous pouvez installer le modèle de projet Yii en exécutant la commande suivante dans un dossier accessible via le Web :\n\n```bash\ncomposer create-project --prefer-dist yiisoft/yii2-app-basic basic\n```\n\nCette commande installera la dernière version stable du modèle de projet Yii dans le dossier `basic`. Vous êtes libre de choisir un autre dossier si vous le désirez.\n\n> Note: si la commande `composer create-project` échoue, reportez-vous à la section \n> [Troubleshooting (résolution des problèmes) de la documentation de Composer](https://getcomposer.org/doc/articles/troubleshooting.md)\n> pour les erreurs communes. Une fois l'erreur corrigée, vous pouvez reprendre l'installation avortée en exécutant `composer update` dans le dossier  `basic` (ou celui que vous aviez choisi).\n\n> Tip: si vous souhaitez installer la dernière version de développement de Yii, vous pouvez utiliser la commande suivante qui ajoutera l'[option stability](https://getcomposer.org/doc/04-schema.md#minimum-stability) :\n>\n>```bash\n>composer create-project --prefer-dist --stability=dev yiisoft/yii2-app-basic basic\n>```\n>\n> Notez que la version de développement de Yii ne doit pas être utilisée en production, vu qu'elle pourrait *casser* votre code existant.\n\n\nInstaller depuis une archive <span id=\"installing-from-archive-file\"></span>\n----------------------------\n\nInstaller Yii depuis une archive se fait en trois étapes :\n\n1. Télécharger l'archive sur le site [yiiframework.com](https://www.yiiframework.com/download/).\n2. Décompresser l'archive dans un dossier accessible via le Web.\n3. Modifier le fichier `config/web.php` en entrant une clé secrète pour la configuration de `cookieValidationKey` (cela est fait automatiquement si vous installez Yii avec Composer) :\n\n ```php\n // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation\n 'cookieValidationKey' => 'enter your secret key here',\n ```\n\n\nAutres options d'installation <span id=\"other-installation-options\"></span>\n-----------------------------\n\nLes instructions d'installation ci-dessus montrent comment installer Yii, ce qui installe également une application Web de base qui fonctionne *out of the box* (sans configuration supplémentaire). \nCette approche est un bon point de départ pour les petits projets, en particulier si vous débutez avec Yii. \n\nMais il y a d'autres options d'installation disponibles :\n\n* Si vous voulez installer uniquement le framework et que vous souhaitez créer une application à partir de zéro, vous pouvez suivre les instructions dans la partie [Créer votre propre structure d'application](tutorial-start-from-scratch.md).\n* Si vous voulez commencer par une application plus sophistiquée, mieux adaptée aux environnements d'équipe de développement, vous pouvez envisager l'installation du [Modèle d'application avancée](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md).\n\nInstaller les Assets (ici bibliothèques CSS et JavaScript) <span id=\"installing-assets\"></span>\n-----------------\n\nYii s'appuie sur les paquets [Bower](https://bower.io/) et/ou [NPM](https://www.npmjs.com/) pour l'installation des bibliothèques CSS et JavaScript.\n\nIl utilise Composer pour les obtenir, permettant ainsi aux versions de paquet de PHP et à celles de CSS/JavaScript, d'être résolues en même temps.\nCela peut être obtenue soit en utilisant [asset-packagist.org](https://asset-packagist.org) ou [composer asset plugin](https://github.com/fxpio/composer-asset-plugin).\n\nReportez-vous à la documentation sur les  [Assets](structure-assets.md) pour plus de détail.\n\nVous pouvez souhaiter gérer vos « assets », soit via le client natif Bower/NPM, soit via CDN, soit éviter totalement leur installation. \nAfin d'empêcher l'installation des « assets » via Composer, ajoutez les lignes suivantes à votre fichier 'composer.json' :\n\n```json\n\"replace\": {\n    \"bower-asset/jquery\": \">=1.11.0\",\n    \"bower-asset/inputmask\": \">=3.2.0\",\n    \"bower-asset/punycode\": \">=1.3.0\",\n    \"bower-asset/yii2-pjax\": \">=2.0.0\"\n},\n```\n\n> Note: en cas de neutralisation de l'installation des « assets » via Composer, c'est à vous d'en assurer l'installation et de résoudre les problèmes de collision de versions. Attendez-vous à des incohérences possibles parmi les fichiers d'assets issus de vos différentes extensions.\n\nVérifier l'installation <span id=\"verifying-installation\"></span>\n-----------------------\n\nAprès l'installation, vous pouvez, soit configurer votre serveur Web (voir section suivante), soit utiliser le [serveur PHP web incorporé](https://www.php.net/manual/fr/features.commandline.webserver.php) en utilisant la commande en console suivante depuis le dossier racine de votre projet :\n \n```bash\nphp yii serve\n```\n\n> Note: par défaut le serveur HTTP écoute le port 8080. Néanmoins, si ce port est déjà utilisé ou si vous voulez servir plusieurs applications de cette manière, vous pouvez spécifier le port à utiliser en ajoutant l'argument --port à la commande : \n\n```bash\nphp yii serve --port=8888\n```\nPour accéder à l'application Yii pointez votre navigateur sur l'URL suivante :\n\n```\nhttp://localhost:8080/\n```\n\n\n![Successful Installation of Yii](images/start-app-installed.png)\n\nVous devriez voir dans votre navigateur la page ci-dessus. Sinon, merci de vérifier que votre installation remplit bien les pré-requis de Yii. Vous pouvez vérifier cela en utilisant l'une des approches suivantes : \n\n* Utilisez un navigateur pour accéder à l'URL `http://localhost/basic/requirements.php` \n* Exécutez les commandes suivantes:\n\n  ```bash\n  cd basic\n  php requirements.php\n  ```\n\nVous devez configurer votre installation de PHP afin qu'elle réponde aux exigences minimales de Yii. Le plus important étant que vous ayez PHP 5.4 ou plus, idéalement PHP 7. Si votre application a besoin d'une base de données, vous devez également installer l'[extension PHP PDO](https://www.php.net/manual/fr/pdo.installation.php) ainsi qu'un pilote correspondant à votre système de base de données (par exemple `pdo_mysql` pour MySQL).\n\n\nConfiguration du serveur Web <span id=\"configuring-web-servers\"></span>\n----------------------------\n\n> Note: si vous voulez juste tester Yii sans intention de l'utiliser sur un serveur de production, vous pouvez ignorer ce paragraphe.\n\nL'application installée selon les instructions ci-dessus devrait fonctionner *out of the box* (sans configuration supplémentaire) avec le [serveur HTTP Apache](https://httpd.apache.org/) ou le [serveur HTTP Nginx](https://nginx.org/), sous Windows, Mac OX X, ou Linux avec PHP 5.4 ou plus récent. Yii 2.0 est aussi compatible avec \n[HHVM](https://hhvm.com/) de Facebook. Cependant, il existe des cas marginaux pour lesquels HHVM se comporte différemment du PHP natif; c'est pourquoi vous devez faire plus attention en utilisant HHVM.. \n\nSur un serveur de production, vous pouvez configurer votre serveur Web afin que l'application soit accessible via l'URL `https://www.example.com/index.php` au lieu de `https://www.example.com/basic/web/index.php`. Cela implique que le dossier racine de votre serveur Web pointe vers le dossier `basic/web`.\nVous pouvez également cacher `index.php` dans l'URL, comme décrit dans la partie [Génération et traitement des URL](runtime-url-handling.md), vous y apprendrez comment configurer votre serveur Apache ou Nginx pour atteindre ces objectifs.\n\n> Note: en utilisant `basic/web` comme dossier racine, vous empêchez également aux utilisateurs finaux d'accéder à votre code d'application privé et fichiers de données sensibles qui sont stockés dans le dossier `basic`. Refuser l'accès à ces ressources est une amélioration de la sécurité.\n\n> Note: si votre application s'exécute dans un environnement d'hébergement mutualisé où vous n'avez pas la permission de modifier la configuration du serveur Web, vous pouvez ajuster la structure de votre application pour une meilleure sécurité. Reportez-vous à la partie [Environnement d'hébergement mutualisé](tutorial-shared-hosting.md) pour en savoir plus.\n\n> Note: si vous exécutez votre application Yii derrière un mandataire inverse, vous pourriez avoir besoin de configurer les\n> [mandataires de confiance et entêtes](runtime-requests.md#trusted-proxies) dans le composant « request ».\n\n### Configuration Apache recommandée <span id=\"recommended-apache-configuration\"></span>\n\nUtilisez la configuration suivante dans le fichier `httpd.conf`, ou dans la configuration de votre hôte virtuel. Notez que vous devez remplacer `path/to/basic/web` par le chemin vers le dossier `basic/web`.\n\n```apache\n# Configuration du dossier racine\nDocumentRoot \"path/to/basic/web\"\n\n<Directory \"path/to/basic/web\">\n    # utiliser mod_rewrite pour la prise en charge des URL élégantes (\"pretty URL\")\n    RewriteEngine on\n\n    # Si le dossier ou fichier existe, répondre directement\n    RewriteCond %{REQUEST_FILENAME} !-f\n    RewriteCond %{REQUEST_FILENAME} !-d\n    # Sinon on redirige vers index.php\n    RewriteRule . index.php\n\n    # si $showScriptName est à \"false\" dans UrlManager, ne pas autoriser l'accès aux URL incluant le nom du script\n    RewriteRule ^index.php/ - [L,R=404]\n\n    # ...other settings...\n</Directory>\n```\n\n\n### Configuration Nginx recommandée <span id=\"recommended-nginx-configuration\"></span>\n\nPour utiliser Nginx, vous devez avoir installé PHP en utilisant [FPM SAPI](https://www.php.net/manual/fr/install.fpm.php).\nUtilisez la configuration Nginx suivante, en remplaçant `path/to/basic/web` par le chemin vers le dossier `basic/web` et `mysite.test` par le nom d'hôte de votre serveur.\n\n```nginx\nserver {\n    charset utf-8;\n    client_max_body_size 128M;\n\n    listen 80; ## listen for ipv4\n    #listen [::]:80 default_server ipv6only=on; ## listen for ipv6\n\n    server_name mysite.test;\n    root        /path/to/basic/web;\n    index       index.php;\n\n    access_log  /path/to/basic/log/access.log;\n    error_log   /path/to/basic/log/error.log;\n\n    location / {\n        # Rediriger tout ce qui n'est pas un fichier réel index.php\n        try_files $uri $uri/ /index.php$is_args$args;\n    }\n\n    # enlevez les commentaires de ces lignes pour évitez que Yii ne gère les requêtes vers des fichiers statiques inexistants\n    #location ~ \\.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ {\n    #    try_files $uri =404;\n    #}\n    #error_page 404 /404.html;\n\n\n    # refuser l'accès aux fichiers php pour le dossier /assets \n    location ~ ^/assets/.*\\.php$ {\n        deny all;\n\n    location ~ \\.php$ {\n        include fastcgi.conf;\n        fastcgi_pass   127.0.0.1:9000;\n        #fastcgi_pass unix:/var/run/php5-fpm.sock;\n    }\n\n    location ~ /\\.(ht|svn|git) {\n        deny all;\n    }\n}\n```\n\nLorsque vous utilisez cette configuration, vous devez aussi mettre l'option `cgi.fix_pathinfo=0` dans le fichier `php.ini` afin d'éviter de nombreux appels système à `stat()`.\n\nNotez également que lors de l'utilisation d'un serveur HTTPS, vous devez ajouter l'option `fastcgi_param HTTPS on;` afin que Yii puisse détecter correctement si une connexion est sécurisée.\n"
  },
  {
    "path": "docs/guide-fr/start-looking-ahead.md",
    "content": "En savoir plus\n=============\n\nSi vous avez entièrement lu la section \"Mise en Route\", vous avez maintenant créé une application Yii complète. Ce faisant, vous avez appris comment mettre en œuvre des fonctionnalités couramment utilisées, telles que recueillir des données d'un utilisateur via un formulaire HTML, chercher des données dans une base de données, et afficher des données \nde manière paginée. Vous avez également appris à utiliser [Gii](https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide) pour générer du code automatiquement. \nUtiliser Gii pour générer du code rend le gros de votre processus de développement Web aussi simple que de remplir de \nsimples formulaires. \n\nCette section va résumer les ressources Yii disponibles pour vous aider à être plus productif dans l'utilisation du framework.\n\n* Documentation\n    - [Le Guide complet](https://www.yiiframework.com/doc-2.0/guide-README.html) :\n      Comme son nom l'indique, le guide définit précisément comment Yii fonctionne et fournit des instructions générales\n      sur l'utilisation de Yii. C'est le tutoriel pour Yii le plus important, un que vous devriez lire avant d'écrire le\n      moindre code Yii.\n    - [Le référentiel des Classes](https://www.yiiframework.com/doc-2.0/index.html) :\n      Il spécifie le mode d'utilisation de toutes les classes fournies par Yii. Il doit être principalement utilisé lorsque \n      vous écrivez du code et souhaitez comprendre le mode d'utilisation d'une classe, méthode ou propriété particulière.\n      L'utilisation du référentiel des classes est plus appropriée quand vous avez une compréhension contextuelle du framework entier.\n    - [Les Articles du Wiki](https://www.yiiframework.com/wiki/?tag=yii2):\n      Les articles wiki sont écrits par des utilisateurs de Yii sur la base de leurs propres expériences. Ils sont en\n      général écrits comme des recettes de cuisine, et montrent comment résoudre des problèmes pratiques en utilisant\n      Yii. Bien que la qualité de ces articles puisse être moindre que celle du Guide complet, ils sont utiles du fait \n      qu'ils couvrent des sujets plus vastes et peuvent fournir des solutions clef-en-main.\n    - [Livres](https://www.yiiframework.com/books)\n* [Extensions](https://www.yiiframework.com/extensions/):\n  Yii est fort d'une librairie de milliers d'extensions créées par les utilisateurs, qui peuvent être facilement\n  ajoutées à votre application, rendant son développement encore plus facile et plus rapide.\n* Communauté\n    - Forum : <https://forum.yiiframework.com/>\n    - Chat IRC : Les canal #yii sur le réseau Libera (<ircs://irc.libera.chat:6697/yii>)\n    - Slack chanel: <https://yii.slack.com>\n    - Gitter chat: <https://gitter.im/yiisoft/yii2>\n    - GitHub: <https://github.com/yiisoft/yii2>\n    - Facebook: <https://www.facebook.com/groups/yiitalk/>\n    - Twitter: <https://twitter.com/yiiframework>\n    - LinkedIn: <https://www.linkedin.com/groups/yii-framework-1483367>\n    - Stackoverflow: <https://stackoverflow.com/questions/tagged/yii2>\n"
  },
  {
    "path": "docs/guide-fr/start-prerequisites.md",
    "content": "# Que devez-vous connaître\n\nLa courbe d'apprentissage de Yii n’est pas aussi raide que celle des autres framework. Cependant, il y a un certain nombre de choses que vous devriez connaître avant de vous lancer avec Yii.\n\n## PHP\n\nYii un framework (base structurée de développement) en PHP. C’est pourquoi vous devez vous assurer de [maîtriser ce langage en comprenant sa référence](https://www.php.net/manual/fr/langref.php).\nLors de votre développement avec Yii, vous écrirez du code dans le style « orienté objet ». Vous devez donc être familiarisé avec les [Classes et Objets](https://www.php.net/manual/fr/language.oop5.basic.php), ainsi qu'avec les [espaces de noms](https://www.php.net/manual/fr/language.namespaces.php).\n\n## Programmation orientée Objet\n\nUne compréhension de base de la programmation orientée objet est requise. Si vous n’êtes pas familiarisé avec elle, orientez-vous vers l’un des nombreux tutoriels disponibles tels que [celui de tuts+](https://code.tutsplus.com/tutorials/object-oriented-php-for-beginners--net-12762).\n\nNotez que plus votre application sera complexe, plus vous devrez en savoir pour gérer cette complexité avec succès.\n\n## Ligne de commande et composer\n\nYii utilise abondamment le gestionnaire de paquet [Composer](https://getcomposer.org/) qui est un standard de fait. Vous devez donc lire et comprendre son [guide](https://getcomposer.org/doc/01-basic-usage.md). Si vous n’êtes pas encore familiarisé avec la ligne de commande, c’est le moment de vous y essayer. Une fois les bases acquises, vous ne saurez plus vous en passer. \n\n"
  },
  {
    "path": "docs/guide-fr/start-workflow.md",
    "content": "Fonctionnement des applications\n===============================\n\nAprès avoir installé Yii, vous obtenez une application Yii fonctionnelle accessible via l'URL `https://hostname/basic/web/index.php` ou `https://hostname/index.php`, en fonction\nde votre configuration. Cette section vous initiera aux fonctionnalités intégrées à l'application,\nà la manière dont le code est organisé et à la gestion des requêtes par l'application.\n\n> Info: pour simplifier, au long de ce tutoriel de démarrage, nous supposerons que `basic/web` est la racine de votre \n serveur Web, et que vous avez configuré l'URL pour accéder à votre application comme suit ou de façon similaire : \n  `https://hostname/index.php`.\n  Pour vos besoins, merci d'ajuster les URLs dans notre description comme il convient.\n\nNotez que contrairement au framework lui-même, après avoir installé un modèle de projet, vous êtes entièrement libre d'en disposer. Vous êtes libre d'ajouter ou de supprimer du code selon vos besoins. \n\nFonctionnalité <span id=\"Functionality\"></span>\n--------------\n\nL'application basique installée contient quatre pages :\n\n* La page d'accueil, affichée quand vous accédez à l'URL `https://hostname/index.php`,\n* la page \"About\" (À Propos),\n* la page \"Contact\", qui présente un formulaire de contact permettant aux utilisateurs finaux de vous contacter par courriel,\n* et la page \"Login\" (Connexion), qui présente un formulaire de connexion qui peut être utilisé pour authentifier des utilisateurs finaux. Essayez de vous connecter\n avec \"admin/admin\", et vous verrez l'élément \"Login\" du menu principal être remplacé par \"Logout\" (Déconnexion).\n\nCes pages ont en commun une entête et un pied de page. L'entête contient une barre de menu principal qui permet la navigation\nentre les différentes pages.\n\nVous devriez également voir une barre d'outils en bas de votre fenêtre de navigation.\nC'est un [outil de débogage](https://github.com/yiisoft/yii2-debug/blob/master/docs/guide/README.md) utile fourni par Yii pour enregistrer et afficher de nombreuses informations de débogage, telles que des messages de journaux, les statuts de réponses, les requêtes lancées vers la base de données, etc.\n\nEn plus de l'application Web, il existe,dans le dossier de base de l'application, un script en console appelé `yii`. Ce script peut être utilisé pour exécuter des tâches de fond et de maintenance pour l'application; ces tâches sont décrites à la section [Applications en console](tutorial-console.md).\n\n\nStructure de l'application <span id=\"application-structure\"></span>\n---------------------\n\nLes répertoires et fichiers les plus importants de votre application sont (en supposant que le répertoire racine de l'application est `basic`) :\n\n```\nbasic/                  chemin de base de l'application\n    composer.json       utilisé par Composer, décrit les information de paquets\n    config/             contient les configurations de l'application et autres\n        console.php     configuration de l'application console\n        web.php         configuration de l'application Web\n    commands/           contient les classes de commandes console\n    controllers/        contient les classes de contrôleurs\n    models/             contient les classes de modèles\n    runtime/            contient les fichiers générés par Yii au cours de l'exécution, tels que les fichiers de logs ou de cache and cache\n    vendor/             contient les paquets Composer installés, y compris le framework Yii\n    views/              contient les fichiers de vues\n    web/                racine Web de l'application, contient les fichiers accessibles via le Web\n        assets/         contient les fichiers assets (javascript et css) publiés par Yii\n        index.php       le script de démarrage (ou bootstrap) pour l'application\n    yii                 le script d'exécution de Yii en commande console\n```\n\nDans l'ensemble, les fichiers de l'application peuvent être séparés en deux types : ceux situés dans `basic/web` et ceux situés dans d'autres répertoires. Les premiers peuvent être atteints directement en HTTP (c'est à dire dans un navigateur), tandis que les seconds ne peuvent et ne doivent pas l'être.\n\nYii est mis en œuvre selon le modèle de conception [modèle-vue-contrôleur (MVC)](https://wikipedia.org/wiki/Model-view-controller),\nce qui se reflète dans l'organisation des répertoires ci-dessus. Le répertoire `models` contient toutes les [classes modèles](structure-models.md),\nle répertoire `views` contient tous les [scripts de vue](structure-views.md), et le répertoire `controllers` contient toutes les [classes contrôleurs](structure-controllers.md).\n\nLe schéma suivant présente la structure statique d'une application.\n\n![Structure Statique d'Application](images/application-structure.png)\n\nChaque application dispose d'un script de démarrage `web/index.php` qui est le seul script PHP de l'application accessible depuis le Web.\nLe script de démarrage reçoit une requête entrante et crée une instance d'[application](structure-applications.md) pour la traiter.\nL'[application](structure-applications.md) résout la requête avec l'aide de ses [composants](concept-components.md),\net distribue la requête aux éléments MVC. Les [composants graphiques (widgets)](structure-widgets.md) sont utilisés dans les [vues](structure-views.md)\npour aider à créer des éléments d'interface complexes et dynamiques.\n\n\nCycle de vie d'une requête <span id=\"request-lifecycle\"></span>\n-----------------\n\nLe diagramme suivant présente la manière dont une application traite une requête.\n\n![Cycle de vie d'une requête](images/request-lifecycle.png)\n\n1. Un utilisateur fait une requête au [script de démarrage](structure-entry-scripts.md) `web/index.php`.\n2. Le script de démarrage charge la [configuration](concept-configurations.md) de l'application et crée une instance d'[application](structure-applications.md) pour traiter la requête.\n3. L'application résout la [route](runtime-routing.md) requise avec l'aide du composant d'application [requête](runtime-requests.md).\n4. L'application créé une instance de [contrôleur](structure-controllers.md) pour traiter la requête.\n5. Le contrôleur crée une instance d'[action](structure-controllers.md) et applique les filtres pour l'action.\n6. Si un filtre échoue, l'action est annulée.\n7. Si tous les filtres sont validés, l'action est exécutée.\n8. L'action charge un modèle de données, potentiellement depuis une base de données.\n9. L'action génère une vue, lui fournissant le modèle de données.\n10. Le résultat généré est renvoyé au composant d'application [response](runtime-responses.md).\n11. Le composant « response » (réponse) envoie le résultat généré au navigateur de l'utilisateur.\n"
  },
  {
    "path": "docs/guide-fr/structure-application-components.md",
    "content": "Composants d'application\n======================\nLes applications sont des  [(localisateurs de services (service locators)](concept-service-locator.md). Elles hébergent un jeu composants appelés « composants d'application » qui procurent différents services pour la prise en charge des requêtes. Par exemple, le composant `urlManager` (gestionnaire d'url) est chargé de router les requêtes Web vers les contrôleurs appropriés ; le composant  `db` (base de données) fournit les services relatifs à la base de données ; et ainsi de suite.\n\nChaque composant d'application possède un identifiant unique qui le distingue des autres composants d'application de la même application. Vous pouvez accéder à un composant d'application via l'expression :\n\n```php\n\\Yii::$app->componentID\n```\n\nPar exemple, vous pouvez utiliser `\\Yii::$app->db` pour obtenir la  [[yii\\db\\Connection|connexion à la base de données]], et `\\Yii::$app->cache` pour accéder au  [[yii\\caching\\Cache|cache primaire]] enregistré dans l'application.\n\nUn composant d'application est créé la première fois qu'on veut y accéder en utilisant l'expression ci-dessus. Les accès ultérieurs retournent la même instance du composant.\n\nLes composants d'application peuvent être n'importe quel objet. Vous pouvez les enregistrer en configurant la propriété [[yii\\base\\Application::components]] dans la [configuration de l'application](structure-applications.md#application-configurations).\n\nPar exemple,\n\n```php\n[\n    'components' => [\n        // enregistre le composant  \"cache\" à partir du nom de classe\n        'cache' => 'yii\\caching\\ApcCache',\n\n        // enregistre le composant \"db\" à l'aide d'un tableau de configuration\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=demo',\n            'username' => 'root',\n            'password' => '',\n        ],\n\n        // enregistre le composant \"search\" en utilisant une fonction anonyme\n        'search' => function () {\n            return new app\\components\\SolrService;\n        },\n    ],\n]\n```\n\n> Info: bien que vous puissiez enregistrer autant de composants d'application que vous le désirez, vous devriez le faire avec discernement. Les composants d'application sont comme les variables globales, une utilisation trop importante de composants d'application est susceptible de rendre votre code plus difficile à tester et à maintenir. Dans beaucoup de cas, vous pouvez simplement créer un composant localement et l'utiliser lorsque vous en avez besoin. \n\n\n## Composants du processus d'amorçage <span id=\"bootstrapping-components\"></span>\n\nComme il a été dit plus haut, un composant d'application n'est instancié que lorsqu'on y accède pour la première fois. S'il n'est pas du tout accédé dans le traitement de la requête, il n'est pas instancié. Parfois, vous désirez peut être instancier un composant d'application pour chacune des requêtes, même s'il n'est pas explicitement accédé. \nPour cela, vous pouvez lister son identifiant (ID) dans la propriété [[yii\\base\\Application::bootstrap|bootstrap]] de l'application.\n\nVous pouvez également utiliser des « Closures » (Fermetures) pour amorcer des composants personnalisés. Il n'est pas nécessaire de retourner une instance de composant. Une « Closure » peut également être utilisée pour exécuter du code après l'instanciation de [[yii\\base\\Application]].\n\nPar exemple, la configuration d'application suivante garantit que le composant `log` est toujours chargé.\n\n```php\n[\n    'bootstrap' => [\n        'log',\n        function($app){\n            return new ComponentX();\n        },\n        function($app){\n            // some code\n           return;\n        }\n    ],\n    'components' => [\n        'log' => [\n            // configuration le composant \"log\" \n        ],\n    ],\n]\n```\n\n## Composants d'application du noyau <span id=\"core-application-components\"></span>\n\nYii définit un jeu de composants d'application dit *core application components* (composants d'application du noyau ou du cœur) avec des identifiants fixés et des configurations par défaut.   Par exemple, le composant [[yii\\web\\Application::request|request (requête)]] est utilisé pour collecter les informations sur une requête  utilisateur et la résoudre en une [route](runtime-routing.md); le composant  [[yii\\base\\Application::db|db (base de données)]] représente une connexion à une base de données à l'aide de laquelle vous pouvez effectuer des requêtes de base de données. C'est à l'aide des ces composants d'application du noyau que les applications Yii sont en mesure de prendre en charge les requêtes des utilisateurs.\n\nVous trouverez ci-après la liste des composants d'application prédéfinis du noyau. Vous pouvez les configurer et les personnaliser comme tout composant d'application. Lorsque vous configurez une composant d'application du noyau, vous n'avez pas besoin de spécifier sa classe, celle par défaut est utilisée. \n\n\n* [[yii\\web\\AssetManager|assetManager (gestionnaire de ressources]]: gère les paquets de ressources et la publication des ressources. \n  Reportez-vous à la section [Ressources](structure-assets.md) pour plus de détails.\n* [[yii\\db\\Connection|db (base de données)]]: représente une connexion à une base de données à l'aide de laquelle vous pouvez effectuer des requêtes de base de données. \n  Notez que lorsque vous configurez ce composant, vous devez spécifier la classe de composant tout comme les autres propriétés de composant, telle que [[yii\\db\\Connection::dsn]].\n  Reportez-vous à la section [Objets d'accès aux bases de données](db-dao.md) pour plus de détails.\n* [[yii\\base\\Application::errorHandler|errorHandler (gestionnaire d'erreurs) ]]: gère les erreurs PHP et les exceptions.\n  Reportez-vous à la section [Gestion des erreurs](runtime-handling-errors.md) pour plus de détails.\n* [[yii\\i18n\\Formatter|formatter ]]: formate les données lorsqu'elles sont présentées à l'utilisateur final. Par exemple, un nombre peut être affiché avec un séparateur de milliers, une date affichée dans un format long, etc.\n  Reportez-vous à la section [Formatage des données](output-formatting.md) pour plus de détails.\n* [[yii\\i18n\\I18N|i18n]]: prend en charge la traduction et le formatage des messages. \n  Reportez-vous à la section [Internationalisation](tutorial-i18n.md) pour plus de détails.\n* [[yii\\log\\Dispatcher|log]]: gère les journaux cibles. \n  Reportez-vous à la section  [Journaux](runtime-logging.md) pour plus de détails.\n* [[yii\\swiftmailer\\Mailer|mailer]]: prend en charge la composition et l'envoi des courriels.\n  Reportez-vous à la section [Mailing](tutorial-mailing.md) pour plus de détails.\n* [[yii\\base\\Application::response|response]]: représente la réponse qui est adressée à l'utilisateur final. \n  Reportez-vous à la section  [Réponses](runtime-responses.md) pour plus de détails.\n* [[yii\\base\\Application::request|request]]: représente la requête reçue de l'utilisateur final.\n  Reportez-vous à la section [Requests](runtime-requests.md) pour plus de détails.\n* [[yii\\web\\Session|session]]: représente les informations de session. Ce composant n'est disponible que dans les [[yii\\web\\Application|applications Web]].\n  Reportez-vous à la section [Sessions et Cookies](runtime-sessions-cookies.md) pour plus de détails.\n* [[yii\\web\\UrlManager|urlManager (gestionnaire d'url)]]: prend en charge l'analyse des URL et leur création.\n  Reportez-vous à la section  [Routage et création d'URL](runtime-routing.md) pour plus de détails.\n* [[yii\\web\\User|user]]: représente les informations d'authentification de l'utilisateur. Ce composant n'est disponible que dans les [[yii\\web\\Application|applications Web]].\n  Reportez-vous à la section [Authentification](security-authentication.md) pour plus de détails.\n* [[yii\\web\\View|view]]: prend en charge le rendu des vues. \n  Reportez-vous à la section  [Vues](structure-views.md) pour plus de détails.\n"
  },
  {
    "path": "docs/guide-fr/structure-applications.md",
    "content": "Applications\n============\n\nLes Applications sont des objets qui gouvernent la structure d'ensemble et le cycle de vie des systèmes mettant en œuvre Yii. \nChacun des systèmes mettant en œuvre Yii contient un objet *Application* unique qui est créé par le [Script d'entrée](structure-entry-scripts.md) et est globalement accessible à l'aide de l'expression `\\Yii::$app`.\n\n\n> Info: selon le contexte, lorsque nous utilisons le terme « application », cela peut signifier soit un objet *Application*, soit un système mettant en œuvre Yii. \n\nIl existe deux types d'application :  [[yii\\web\\Application|les applications Web]] et\n[[yii\\console\\Application|les applications de console]]. Comme leur nom l'indique, les premières prennent en charge des requêtes Web tandis que les deuxièmes prennent en charge des requêtes de la console. \n\n\n## Configurations d'application <span id=\"application-configurations\"></span>\n\nLorsqu'un [script d'entrée](structure-entry-scripts.md) crée une application, il charge une [configuration](concept-configurations.md) et l'applique à cette application de la manière suivante :\n\n```php\nrequire __DIR__ . '/../vendor/autoload.php';\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n\n// charger la configuration de l'application\n$config = require __DIR__ . '/../config/web.php';\n\n// instancier et configurer l'application\n(new yii\\web\\Application($config))->run();\n```\n\nTout comme les [configurations](concept-configurations.md) habituelles, les configurations d'application spécifient comment initialiser les propriétés des objets *Application*. Comme les configurations d'application sont souvent très complexes, elles sont ordinairement conservées dans des  [fichiers de configuration](concept-configurations.md#configuration-files),\ntels que le fichier `web.php` de l'exemple précédent.\n\n\n## Propriétés des applications <span id=\"application-properties\"></span>\n\nIl y a de nombreuses propriétés importantes des applications que vous devez spécifier dans les configurations d'application. \nCes propriétés décrivent l'environnement dans lequel ces applications sont exécutées. Par exemple, les applications doivent savoir comment charger les [contrôleurs](structure-controllers.md), où ranger les fichiers temporaires, etc. Nous allons passer en revue ces propriétés.\n\n\n### Propriétés requises <span id=\"required-properties\"></span>\n\nDans toute application, vous devez au moins spécifier deux propriétés :: [[yii\\base\\Application::id|id]] et [[yii\\base\\Application::basePath|basePath]].\n\n\n#### [[yii\\base\\Application::id|id]] <span id=\"id\"></span>\n\nLa propriété [[yii\\base\\Application::id|id]] spécifie un identifiant unique qui distingue une application des autres. On l'utilise principalement dans des instructions. Bien que cela ne soit pas une exigence, l'utilisation des seuls caractères alphanumériques, pour spécifier cet identifiant, est recommandée pour assurer une meilleure interopérabilité. \n\n\n#### [[yii\\base\\Application::basePath|basePath]] <span id=\"basePath (chemin du dossier racine)\"></span>\n\nLa propriété [[yii\\base\\Application::basePath|basePath]] spécifie le dossier racine d'une application. Il s'agit du dossier qui contient tout le code source protégé d'une application mettant en œuvre Yii. Dans ce dossier, on trouve généralement des sous-dossiers tels que `models`, `views` et`controllers`, qui contiennent le code source correspondant au modèle de conception MVC.\n\nVous pouvez configurer la propriété  [[yii\\base\\Application::basePath|basePath]] en utilisant un chemin de dossier\nou un [alias de chemin](concept-aliases.md). Dans les deux cas, le dossier correspondant doit exister,\n sinon une exception est levée. Le chemin doit être normalisé à l'aide de la fonction `realpath()`.\n\nLa propriété [[yii\\base\\Application::basePath|basePath]] est souvent utilisée pour dériver d'autres chemins importants\n(p. ex. le chemin runtime ). À cette fin, un alias nommé `@app` est prédéfini pour représenter ce chemin.\nLes chemins dérivés peuvent être formés à l'aide de cet alias (p. ex. `@app/runtime` pour faire référence au dossier `runtime`).\n\n\n### Propriétés importantes <span id=\"important-properties\"></span>\n\nLes propriétés décrites dans cette sous-section doivent souvent être spécifiées car elles différent à travers les différentes applications.\n\n\n#### [[yii\\base\\Application::aliases|alias]] <span id=\"aliases\"></span>\n\nCette propriété vous permet de définir un jeu d' [alias](concept-aliases.md) sous forme de tableau associatif.\nLes clés du tableau représentent les noms des alias, tandis que les valeurs représentent la définition des chemins. \nPar exemple :\n\n```php\n[\n    'aliases' => [\n        '@name1' => 'chemin/vers/dossier1',\n        '@name2' => 'chemin/vers/dossier2',\n    ],\n]\n```\n\nCette propriété est mise à votre disposition pour vous éviter d'avoir à définir les alias par programme en appelant la méthode [[Yii::setAlias()]].\n\n\n#### [[yii\\base\\Application::bootstrap|bootstrap (amorçage)]] <span id=\"bootstrap\"></span>\n\nCette propriété est très utile. Elle vous permet de spécifier un tableau de composants qui devraient être exécutés lors du [[yii\\base\\Application::bootstrap()|processus d'amorçage]].\nPar exemple, si vous désirez utiliser un [module](structure-modules.md) pour personnaliser les  [règles d'URL](runtime-routing.md),\nvous pouvez indiquer son identifiant (ID) en tant qu'élément de cette propriété. \n\nChacun des composants listés dans cette propriété peut être spécifié sous une des formes suivantes :\n\n- un identifiant (ID) de composant d'application comme vous le spécifieriez via [components](#components),\n- un identifiant (ID) de  module comme vous le spécifieriez via [modules](#modules),\n- un nom de classe,\n- un tableau de configuration,\n- une fonction anonyme qui crée et retourne un composant.\n\nPar exemple:\n\n```php\n[\n    'bootstrap' => [\n        // un identifiant de composant d'application ou de module\n        'demo',\n\n        // un nom de classe\n        'app\\components\\Profiler',\n\n        // un tableau de configuration\n        [\n            'class' => 'app\\components\\Profiler',\n            'level' => 3,\n        ],\n\n        // une fonction anonyme\n        function () {\n            return new app\\components\\Profiler();\n        }\n    ],\n]\n```\n\n> Info: si un identifiant (ID) de module est identique à celui d'un composant d'application, le composant d'application est utilisé lors du processus de démarrage. Si vous désirez utiliser le module, vous pouvez le spécifier via une fonction anonyme comme le montre l'exemple suivant : \n>\n> ```php\n> [\n>     function () {\n>         return Yii::$app->getModule('user');\n>     },\n> ]\n> ```\n\n\nLos du processus d'amorçage, chaque composant est instancié. Si la classe du composant implémente  [[yii\\base\\BootstrapInterface]], sa méthode [[yii\\base\\BootstrapInterface::bootstrap()|bootstrap()]] est également appelée.\n\nUn autre exemple pratique se trouve dans la configuration de l'application du [Modèle du projet Basic](start-installation.md), \noù les modules `debug` et `gii` sont configurés en tant que composants d'amorçage lorsque l'application est dans l'environnement de développement.\n\n```php\nif (YII_ENV_DEV) {\n    // réglages de configuration pour l'environnement 'dev'\n    $config['bootstrap'][] = 'debug';\n    $config['modules']['debug'] = 'yii\\debug\\Module';\n\n    $config['bootstrap'][] = 'gii';\n    $config['modules']['gii'] = 'yii\\gii\\Module';\n}\n```\n\n> Note: placer trop de composants dans `bootstrap` dégrade la performance de votre application car, à chaque requête, le même jeu de composants doit être exécuté. C'est pourquoi vous devez utiliser les composants de démarrage avec discernement. \n\n\n#### [[yii\\web\\Application::catchAll|catchAll (ramasse tout)]] <span id=\"catchAll\"></span>\n\nCette propriété est prise en charge par les [[yii\\web\\Application|applications Web]] uniquement. Elle spécifie une \n[action de contrôleur](structure-controllers.md) qui prend en charge toutes les requêtes de l'utilisateur. Cela est essentiellement utilisé lorsque l'application est dans le mode maintenance et doit prendre en charge toutes les requêtes avec une action unique.\nLa configuration est un tableau dont le premier élément spécifie la route de l'action. Le reste des éléments du tableau (paires clé-valeur) spécifie les paramètres à associer à l'action. Par exemple :\n\n```php\n[\n    'catchAll' => [\n        'offline/notice',\n        'param1' => 'valeur1',\n        'param2' => 'valeur2',\n    ],\n]\n```\n\n> Info: le panneau de débogage dans l'environnement de développement ne fonctionne pas lorsque cette propriété est activée. \n\n#### [[yii\\base\\Application::components|components (composants)]] <span id=\"components\"></span>\n\nIl s'agit de la seule plus importante propriété. Elle vous permet d'enregistrer par leur nom une liste de composants appelés  [composants d'application](structure-application-components.md) que vous pouvez utiliser partout ailleurs. Par exemple : \n\n```php\n[\n    'components' => [\n        'cache' => [\n            'class' => 'yii\\caching\\FileCache',\n        ],\n        'user' => [\n            'identityClass' => 'app\\models\\User',\n            'enableAutoLogin' => true,\n        ],\n    ],\n]\n```\n\nChaque composant d'application est spécifié sous la forme  d'un couple clé-valeur dans le tableau. La clé représente l'identifiant (ID) du composant, tandis que la valeur représente le nom de la classe du composant ou un tableau de [configuration](concept-configurations.md).\n\nVous pouvez enregistrer n'importe quel composant dans une application, et vous pouvez ensuite y accéder globalement via l'expression `\\Yii::$app->componentID`.\n\nReportez-vous à la section  [Composants d'application](structure-application-components.md) pour plus de détails.\n\n\n#### [[yii\\base\\Application::controllerMap|controllerMap (Table de mise en correspondance des contrôleurs)]] <span id=\"controllerMap\"></span>\n\nCette propriété vous permet de faire correspondre un identifiant (ID) de contrôleur avec une classe de contrôleur arbitraire. Par défaut, Yii fait correspondre un identifiant de contrôleur avec une classe de contrôleur selon une [convention](#controllerNamespace) (p. ex.  l'identifiant `post` correspond à `app\\controllers\\PostController`). En configurant cette propriété, vous passez outre la convention pour les contrôleurs spécifiés. Dans l'exemple qui suit,  `account` correspond à \n`app\\controllers\\UserController`, tandis que `article` correspond à  `app\\controllers\\PostController`.\n\n```php\n[\n    'controllerMap' => [\n        'account' => 'app\\controllers\\UserController',\n        'article' => [\n            'class' => 'app\\controllers\\PostController',\n            'enableCsrfValidation' => false,\n        ],\n    ],\n]\n```\n\nLes clés du tableau de cette propriété représentent les identifiants des contrôleurs, tandis que les valeurs représentent les noms des classes mises en correspondance ou les tableaux de [configurations](concept-configurations.md).\n\n\n#### [[yii\\base\\Application::controllerNamespace|controllerNamespace (espaces de noms des contrôleurs]] <span id=\"controllerNamespace\"></span>\n\nCette propriété spécifie l'espace de noms par défaut sous lequel les classes des contrôleurs sont situées. Par défaut, il s'agit de \n`app\\controllers`. Si l'identifiant d'un contrôleur est `post`, par convention le contrôleur correspondant (sans l'espace de noms) est  `PostController`, et le nom de classe totalement qualifié est `app\\controllers\\PostController`.\n\nLes classes de contrôleur peuvent aussi résider dans des sous-dossiers du dossier correspondant à cet espace de noms. \nPar exemple, étant donné un identifiant de contrôleur`admin/post`, le nom de classe de contrôleur totalement qualifié est  `app\\controllers\\admin\\PostController`.\n\nIl est important que la classe de contrôleur totalement qualifiée puisse être [auto-chargée](concept-autoloading.md) et que l'espace de noms réel de votre classe de contrôleur corresponde à la valeur de cette propriété. Autrement, vous obtenez une erreur « Page non trouvée » quand vous accédez à votre application. \n\nSi vous désirez passer outre la convention décrite précédemment, vous devez configurer la propriété [controllerMap](#controllerMap).\n\n\n#### [[yii\\base\\Application::language|language (langue)]] <span id=\"language\"></span>\n\nCette propriété spécifie la langue dans laquelle l'application présente les contenus aux utilisateurs finaux. \nLa valeur par défaut de cette propriété est  `en`, pour anglais. Vous devez configurer cette propriété si votre application doit prendre en charge plusieurs langues. \n\nLa valeur de cette propriété détermine des aspects variés de l'[internationalisation](tutorial-i18n.md) tels que la traduction des messages, le formatage des dates et des nombres, etc. Par exemple, l'objet graphique [[yii\\jui\\DatePicker]] utilise la valeur de cette propriété pour déterminer dans quelle langue le calendrier doit être affiché et comment les dates doivent être formatées. \n\nLa spécification de la langue par une  [étiquette IETF d'identification de langue ](https://fr.wikipedia.org/wiki/%C3%89tiquette_d%27identification_de_langues_IETF) est recommandée. Par exemple, `en` signifie anglais, tandis que `en-US` signifie anglais (États-Unis)..\n\nPour plus d'informations sur cette propriété, reportez-vous à la section [Internationalisation](tutorial-i18n.md).\n\n\n#### [[yii\\base\\Application::modules|modules]] <span id=\"modules\"></span>\n\nCette propriété spécifie les [modules](structure-modules.md) que comprend l'application.\n\nCette propriété accepte un tableau de classes de module ou de tableaux de [configurations](concept-configurations.md) dans lequel les clés sont les identifiants (ID) des modules. Par exemple :\n\n```php\n[\n    'modules' => [\n        // un module \"booking\" (réservations) spécifié par sa classe\n        'booking' => 'app\\modules\\booking\\BookingModule',\n\n        // un module  \"comment\" (commentaires) spécifié par un tableau de configuration\n        'comment' => [\n            'class' => 'app\\modules\\comment\\CommentModule',\n            'db' => 'db',\n        ],\n    ],\n]\n```\n\nReportez-vous à la section [Modules](structure-modules.md) pour des informations complémentaires.\n\n\n#### [[yii\\base\\Application::name|name (nom]] <span id=\"name\"></span>\n\nCette propriété spécifie le nom de l'application qui est présenté à l'utilisateur final. Contrairement à la propriété\n[[yii\\base\\Application::id|id]] qui ne peut prendre qu'une valeur unique, la valeur de cette propriété, qui n'intervient que pour l'affichage, n'a pas besoin d'être unique.\nVous n'avez pas besoin de configurer cette propriété si vous ne l'utilisez pas dans votre code.\n\n\n#### [[yii\\base\\Application::params|params (paramètres)]] <span id=\"params\"></span>\n\nCette propriété spécifie un tableau de paramètres de l'application accessibles globalement. Plutôt que de parsemer votre code des mêmes nombres et chaînes de caractères formulées `en dur`, une bonne pratique consiste à les définir une fois pour toute sous forme de paramètres et à utiliser ces paramètres ici et là, ce qui évite, si vous devez en modifier la valeur, d'intervenir en de multiples endroits de votre code. À titre d'exemple, vous pouvez définir la taille des vignettes d'images en tant que paramètre de la façon suivante :\n\n```php\n[\n    'params' => [\n        'thumbnail.size' => [128, 128],\n    ],\n]\n```\n\npuis dans votre code, là où vous devez utiliser cette taille, procéder de la façon suivante :\n\n```php\n$size = \\Yii::$app->params['thumbnail.size'];\n$width = \\Yii::$app->params['thumbnail.size'][0];\n```\n\nPlus tard, si vous changez d'avis à propos de la taille de ces vignettes, il vous suffit de modifier la valeur du paramètre dans la configuration de l'application sans avoir à toucher à votre code.\n\n#### [[yii\\base\\Application::sourceLanguage|sourceLanguage (langue source)]] <span id=\"sourceLanguage\"></span>\n\nCette propriété spécifie la langue dans laquelle l'application est écrite. La valeur par défaut est  `'en-US'`,\npour (anglais — États-Unis). Vous devriez configurer cette propriété si les textes dans votre code ne sont pas en anglais US. \n\nComme pour la propriété [language (langue)](#language), vous devez configurer cette propriété à l'aide d'une [étiquette IETF d'identification de langue](https://fr.wikipedia.org/wiki/%C3%89tiquette_d%27identification_de_langues_IETF). Par exemple, `en` signifie `anglais`,\ntandis que `en-US` signifie for `anglais-États-Unis`).\n\nPour plus d'informations sur cette propriété, reportez-vous à la section [Internationalisation](tutorial-i18n.md).\n\n\n#### [[yii\\base\\Application::timeZone|timeZone (fuseau horaire)]] <span id=\"timeZone\"></span>\n\nCette propriété est fournie comme une manière alternative de définir le fuseau horaire par défaut au moment de l'exécution du script PHP. \nEn configurant cette propriété, vous ne faites essentiellement qu'appeler la fonction PHP \n[date_default_timezone_set()](https://www.php.net/manual/fr/function.date-default-timezone-set.php). Par exemple :\n\n```php\n[\n    'timeZone' => 'America/Los_Angeles',\n]\n```\n\n\n#### [[yii\\base\\Application::version|version]] <span id=\"version\"></span>\n\nCette propriété spécifie la version de l'application. Sa valeur par défaut est  `'1.0'`. Il n'est pas nécessaire que vous définissiez cette propriété si vous ne l'utilisez pas dans votre code.\n\n\n### Propriétés utiles <span id=\"useful-properties\"></span>\n\nLes propriétés décrites dans cette sous-section ne sont en général pas  spécifiées car leur valeur par défaut dérive de conventions ordinaires. Néanmoins, vous pouvez les spécifier pour outrepasser les conventions.\n\n\n#### [[yii\\base\\Application::charset|charset (jeu de caractères)]] <span id=\"charset\"></span>\n\nCette propriété spécifie le jeu de caractères que l'application utilise. La valeur par défaut est `'UTF-8'`, qui devrait être gardée telle quelle dans la plupart des applications sauf si vous travaillez avec un système ancien qui utilise de nombreuses données non Unicode. \n\n\n#### [[yii\\base\\Application::defaultRoute|defaultRoute (route par défaut) ]] <span id=\"defaultRoute\"></span>\n\nCette propriété spécifie la [route](runtime-routing.md) qu'une application devrait utiliser lorsqu'une requête n'en spécifie aucune. La route peut être constituée à partir d'un identifiant de module, d'un identifiant de contrôleur et/ou d'un identifiant d'action. Par exemple, `help`, `post/create` ou `admin/post/create`. Si un identifiant d'action n'est pas fourni, cette propriété prend la valeur par défaut spécifiée dans [[yii\\base\\Controller::defaultAction]]\n\nPour les [[yii\\web\\Application|applications Web]], la valeur par défaut de cette propriété est `'site'`, ce qui donne le contrôleur \n`SiteController` et son action par défaut est utilisée. En conséquence, si vous accédez à l'application sans spécifier de route, vous aboutissez à ce que retourne l'action `app\\controllers\\SiteController::actionIndex()`.\n\nPour les [[yii\\console\\Application|applications de console]], la valeur par défaut est `'help'` (aide), ce qui conduit à\n[[yii\\console\\controllers\\HelpController::actionIndex()]]. Par conséquent, si vous exécutez la commande `yii` sans lui fournir d'argument, l'application affiche l'information d'aide.\n\n\n\n#### [[yii\\base\\Application::extensions|extensions]] <span id=\"extensions\"></span>\n\nCette propriété spécifie la liste des [extensions](structure-extensions.md) installées et utilisées par l'application.\nPar défaut, elle reçoit le tableau retourné par le fichier `@vendor/yiisoft/extensions.php`. Le fichier `extensions.php` est généré et maintenu automatiquement lorsque vous faites appel à [Composer](https://getcomposer.org) pour installer des extensions. Ainsi, dans la plupart des cas, vous n'avez pas besoin de spécifier cette propriété. \n\nDans le cas particulier où vous souhaitez maintenir les extensions à la main, vous pouvez configurer cette propriété de la manière suivante :\n\n\n```php\n[\n    'extensions' => [\n        [\n            'name' => 'extension name', //nom de l'extension\n            'version' => 'version number',//numéro de version\n            'bootstrap' => 'BootstrapClassName',  // facultatif, peut aussi être un tableau de configuration\n            'alias' => [  // facultatif\n                '@alias1' => 'vers/chemin1',\n                '@alias2' => 'vers/chemin2',\n            ],\n        ],\n\n        // ... configuration d'autres extensions similaires à ce qui précède ...\n\n    ],\n]\n```\n\nComme vous pouvez le constater,  la propriété reçoit un tableau de spécifications d'extension. Chacune des extensions est spécifiée par un tableau constitué du \nnom  (`name`) et de la `version` de l'extension. Si une extension doit être exécutée durant le processus d'[amorçage](runtime-bootstrapping.md), un élément  `bootstrap` doit être spécifié par un nom de classe d'amorçage (`bootstrap`) ou un tableau de [configuration](concept-configurations.md). Une extension peut aussi définir quelques [alias](concept-aliases.md).\n\n\n#### [[yii\\base\\Application::layout|layout (disposition de page)]] <span id=\"layout\"></span>\n\nCette propriété spécifie le nom de la disposition  de page par défaut (`layout`) qui doit être utilisée lors du rendu d'une [vue](structure-views.md). La valeur par défaut est `'main'`, ce qui signifie que le fichier de disposition de page `main.php` sous le chemin [layout path](#layoutPath) est utilisé.\nSi, à la fois, le chemin  de la disposition de page [layout path](#layoutPath)  et le chemin de la vue [view path](#viewPath) prennent leur valeur par défaut, le fichier de disposition de page par défaut peut être représenté par l'alias `@app/views/layouts/main.php`.\n\nVous pouvez définir cette propriété à la valeur  `false` pour désactiver la disposition de page par défaut, bien que cela se fasse rarement.\n\n\n#### [[yii\\base\\Application::layoutPath|layoutPath (chemin de la disposition de page)]] <span id=\"layoutPath\"></span>\n\nCette propriété spécifie le chemin du dossier où rechercher les fichiers de disposition de page. La valeur par défaut  `layouts` correspond à un sous-dossier de [view path](#viewPath). Si [view path](#viewPath) prend sa valeur par défaut, le chemin de la disposition de page par défaut peut être représenté par l'alias `@app/views/layouts`.\n\nVous pouvez le définir comme un dossier ou un [alias](concept-aliases.md) de chemin.\n\n\n#### [[yii\\base\\Application::runtimePath|runtimePath (chemin du dossier d'exécution)]] <span id=\"runtimePath\"></span>\n\nCette propriété spécifie le chemin du dossier où les fichiers temporaires, tels que les journaux et les fichiers de cache, sont placés. La valeur par défaut est `@app/runtime`.\n\nVous pouvez configurer cette propriété comme un dossier ou un [alias](concept-aliases.md) de chemin. Notez que le dossier d'exécution `runtimePath`  doit être accessible en écriture par le processus qui exécute l'application et rendu inaccessible aux utilisateurs finaux, parce que les fichiers temporaires qu'il contient peuvent contenir des informations sensibles. \n\nPour simplifier l'accès à ce chemin, Yii a prédéfini un alias de chemin nommé `@runtime`.\n\n\n#### [[yii\\base\\Application::viewPath|viewPath (chemin des vues)]] <span id=\"viewPath\"></span>\n\nCette propriété spécifie le dossier racine des fichiers de vues. La valeur par défaut est le dossier représenté par l'alias  `@app/views`. Vous pouvez le définir sous forme de dossier ou comme un [alias](concept-aliases.md) de chemin.\n\n\n#### [[yii\\base\\Application::vendorPath|vendorPath (chemin des vendeurs)]] <span id=\"vendorPath\"></span>\n\nCette propriété spécifie le dossier des vendeurs gérés par [Composer](https://getcomposer.org). Il contient toutes les bibliothèques de tierces parties utilisées par l'application, y compris le *framework* Yii. La valeur par défaut est le dossier représenté par  `@app/vendor`.\n\nVous pouvez configurer cette propriété comme un dossier ou un [alias](concept-aliases.md) de chemin. Lorsque vous modifiez cette propriété, assurez-vous d'ajuster la configuration de Composer en conséquence. \n\nPour simplifier l'accès à ce chemin, Yii a prédéfini un alias de chemin nommé  `@vendor`.\n\n\n#### [[yii\\console\\Application::enableCoreCommands|enableCoreCommands (activer les commandes du noyau)]] <span id=\"enableCoreCommands\"></span>\n\nCette propriété est prise en charge par les [[yii\\console\\Application|applications de console]] uniquement. Elle spécifie si les commandes du noyau de la version de Yii sont activées ou pas. La valeur par défaut est  `true` (vrai).\n\n\n## Événements d'application <span id=\"application-events\"></span>\n\nUne application déclenche plusieurs événements tout au long de son cycle de vie pour prendre en compte une requête. Vous pouvez attacher des gestionnaires d'événement à ces événements dans la configuration de l'application de la manière suivante : \n\n```php\n[\n    'on beforeRequest' => function ($event) {\n        // ...\n    },\n]\n```\n\nL'utilisation de la syntaxe `on eventName` (on Non d'événement) est décrite dans la section [Configurations](concept-configurations.md#configuration-format).\n\nEn alternative, vous pouvez attacher les gestionnaires d'événement lors du [processus d'amorçage](runtime-bootstrapping.md) après que l'objet Application a été instancié. Par exemple :\n\n```php\n\\Yii::$app->on(\\yii\\base\\Application::EVENT_BEFORE_REQUEST, function ($event) {\n    // ...\n});\n```\n\n### [[yii\\base\\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_REQUEST]] <span id=\"beforeRequest\"></span>\n\nCette événement est déclenché *avant* que l'application ne prenne la requête en charge. Le nom réel de l'événement est `beforeRequest`.\n\nLorsque cet événement est déclenché, l'objet Application a été configuré et initialisé. C'est donc un bon endroit pour insérer votre code personnalisé via le mécanisme événementiel pour intercepter le processus de prise en charge de la requête. Par exemple, dans le gestionnaire d'événement, vous pouvez définir dynamiquement la propriété  [[yii\\base\\Application::language (langue)]] en fonction de certains paramètres.\n\n\n### [[yii\\base\\Application::EVENT_AFTER_REQUEST|EVENT_AFTER_REQUEST]] <span id=\"afterRequest\"></span>\n\nCet événement est déclenché *après* que l'application a fini de prendre la requête en charge mais *avant* que la réponse ne soit envoyée. Le nom réel de l'événement est `afterRequest`.\n\nLorsque cet événement est déclenché, la prise en charge de la requête est terminée et vous pouvez profiter de cette opportunité pour effectuer quelques post-traitements de la requête  et personnaliser la réponse.\n\nNotez que le composant [[yii\\web\\Response|response (réponse)]] déclenche également quelques événements tandis qu'il envoie la réponse au navigateur. Ces événements sont déclenchés *après* cet événement. \n\n### [[yii\\base\\Application::EVENT_BEFORE_ACTION|EVENT_BEFORE_ACTION]] <span id=\"beforeAction\"></span>\n\nCet événement est déclenché *avant* d'exécuter toute [action de contrôleur](structure-controllers.md).\nLe nom réel de l'événement est  `beforeAction`.\nLe paramètre de l'événement est une instance de [[yii\\base\\ActionEvent]]. Un gestionnaire d'événement peut définir la propriété [[yii\\base\\ActionEvent::isValid (est valide)]] à `false` pour arrêter l'exécution de l'action.\nPar exemple:\n\n```php\n[\n    'on beforeAction' => function ($event) {\n        if (some condition) {\n            $event->isValid = false;\n        } else {\n        }\n    },\n]\n```\n\nNotez que le même événement `beforeAction` est  également déclenché par les [modules](structure-modules.md)\net les  [contrôleurs](structure-controllers.md).L'objet *Application* est le premier à déclencher cet événement, suivis des modules (s'il en existe) et, pour finir, des contrôleurs. Si un gestionnaire d'événement défini la propriété [[yii\\base\\ActionEvent::isValid]] à `false`, tous les événements qui devraient suivre ne sont PAS déclenchés.\n\n### [[yii\\base\\Application::EVENT_AFTER_ACTION|EVENT_AFTER_ACTION]] <span id=\"afterAction\"></span>\n\nCet événement est déclenché *après* que chacune des [actions de contrôleur](structure-controllers.md) a été exécutée. \nLe paramètre de l'événement est [[yii\\base\\ActionEvent]]. Un gestionnaire d'événement peut accéder au résultat de l'action et le modifier via la propriété [[yii\\base\\ActionEvent::result]].\nPar exemple:\n\n```php\n[\n    'on afterAction' => function ($event) {\n        if (some condition) {\n            // modify $event->result\n        } else {\n        }\n    },\n]\n```\n\nNotez que le même événement `afterAction` est également déclenché par les [modules](structure-modules.md)\net les  [contrôleurs](structure-controllers.md). Ces objets déclenchent ces événements dans l'ordre inverse de celui des événements déclenchés par `beforeAction`. En clair, les contrôleurs sont les premiers objets à déclencher cet événement, suivis des modules (s'il en existe) et, finalement, de l'application.\n\n\n## Cycle de vie d'une application<span id=\"application-lifecycle\"></span>\n\n![Application Lifecycle](images/application-lifecycle.png)\n\nLorsqu'un [script d'entrée](structure-entry-scripts.md) est exécuté pour prendre en compte une requête, une application entame le cycle de vie suivant :\n\n1. Le script d'entrée charge la configuration de l'application sous forme de tableau.\n2. Le script d'entrée crée un nouvel objet *Application* :\n  * Sa méthode [[yii\\base\\Application::preInit()|preInit()]] est appelée pour configurer quelques propriétés de haute priorité de cette application, comme [[yii\\base\\Application::basePath|basePath]].\n  * Il enregistre [[yii\\base\\Application::errorHandler|le gestionnaire d'erreurs]].\n  * Il configure les propriétés de l'application.\n  * Sa méthode [[yii\\base\\Application::init()|init()]]  est appelée qui appelle ensuite la méthode\n    [[yii\\base\\Application::bootstrap()|bootstrap()]] pour exécuter les composants du processus d'amorçage.\n3. Le script d'entrée appelle la méthode [[yii\\base\\Application::run()]] pour exécuter l'application qui :\n  * déclenche l'événement [[yii\\base\\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_REQUEST]] ;\n  * prend en charge la requête: résout la requête en une [route](runtime-routing.md) et ses paramètres associés ;\n  * crée le  module, le contrôleur et l'action spécifiés par la route et exécute l'action ;\n  * déclenche l'événement [[yii\\base\\Application::EVENT_AFTER_REQUEST|EVENT_AFTER_REQUEST]] ;\n  * renvoie la réponse au navigateur.\n4. Le script d'entrée reçoit l'état de sortie de l'exécution de l'application et complète le processus de prise en charge de la requête.\n"
  },
  {
    "path": "docs/guide-fr/structure-assets.md",
    "content": "Ressources\n==========\n\nUne ressource dans Yii est un fichier qui peut être référencé dans une page Web. Ça peut être un fichier CSS, un fichier JavaScript, une image, un fichier vidéo, etc. \nLes ressources sont situées dans un dossier accessible du Web et sont servies directement par les serveurs Web.\n\nIl est souvent préférable de gérer les ressources par programmation. \nPar exemple, lorsque vous utilisez l'objet graphique [[yii\\jui\\DatePicker]] dans une page, il inclut automatiquement les fichiers  CSS et JavaScript dont il a besoin,  au lieu de vous demander de les inclure à la main. \n\nDe plus, lorsque vous mettez à jour l'objet graphique, il utilise une nouvelle version des fichiers de ressources. \nDans ce tutoriel, nous décrivons les puissantes possibilités de la gestion des ressources de Yii. \n\n\n## Paquets de ressources <span id=\"asset-bundles\"></span>\n\nYii gère les ressources sous forme de *paquets de ressources*. \nUn paquet de ressources est simplement une collection de ressources situées dans un dossier.\nLorsque vous enregistrez un paquet de ressources dans une [vue](structure-views.md), cette vue inclut les fichiers CSS et JavaScript du paquet dans la page Web rendue. \n\n\n## Définition de paquets de ressources <span id=\"defining-asset-bundles\"></span>\n\nLes paquets de ressources sont spécifiés comme des classes PHP qui étendent [[yii\\web\\AssetBundle]]. \nLe nom du paquet est simplement le nom pleinement qualifié de la classe PHP correspondante (sans la barre oblique inversée de tête).\nUne classe de paquet de ressources doit être [auto-chargeable](concept-autoloading.md).\nGénéralement, elle spécifie où les ressources sont situées, quels fichiers CSS et JavaScript le paquet contient, et si le paquet dépend d'autres paquets de ressources. \n\nLe code suivant définit le paquet de ressources principal utilisé par le [modèle de projet *basic*](start-installation.md):\n\n```php\n<?php\n\nnamespace app\\assets;\n\nuse yii\\web\\AssetBundle;\n\nclass AppAsset extends AssetBundle\n{\n    public $basePath = '@webroot';\n    public $baseUrl = '@web';\n    public $css = [\n        'css/site.css',\n        ['css/print.css', 'media' => 'print'],\n    ];\n    public $js = [\n    ];\n    public $depends = [\n        'yii\\web\\YiiAsset',\n        'yii\\bootstrap\\BootstrapAsset',\n    ];\n}\n```\n\nLa classe `AppAsset` ci-dessus spécifie que les fichiers de ressources sont situés dans le dossier `@webroot` qui correspond à l'URL `@web`; \nle paquet contient un unique fichier CSS `css/site.css` et aucun fichier JavaScript ; \nle paquet dépend de deux autres paquets : [[yii\\web\\YiiAsset]] et [[yii\\bootstrap\\BootstrapAsset]]. \nDes explications plus détaillées sur les propriétés d'[[yii\\web\\AssetBundle]] sont disponibles dans les ressources suivantes :\n\n* [[yii\\web\\AssetBundle::sourcePath|sourcePath]] (chemin des sources): spécifie le dossier racine qui contient les fichiers de ressources dans ce paquet. \nCette propriété doit être définie si le dossier \nracine n'est pas accessible du Web. \nAutrement, vous devez définir les propriétés [[yii\\web\\AssetBundle::basePath|basePath]] et  [[yii\\web\\AssetBundle::baseUrl|baseUrl]]. Des [alias de chemin](concept-aliases.md) sont utilisables ici. \n* [[yii\\web\\AssetBundle::basePath|basePath ]] (chemin de base): spécifie un dossier accessible du Web qui contient les fichiers de ressources dans ce paquet. \nLorsque vous spécifiez la propriété[[yii\\web\\AssetBundle::sourcePath|sourcePath (chemin des sources)]], le [gestionnaire de ressources](#asset-manager) publie les ressources de ce paquet dans un dossier accessible du Web et redéfinit cette propriété en conséquence. \nVous devez définir cette propriété si vos fichiers de ressources sont déjà\n dans un dossier accessible du Web et n'ont pas besoin d'être publiés. \nLes [alias de chemin](concept-aliases.md) sont utilisables ici.\n* [[yii\\web\\AssetBundle::baseUrl|baseUrl ]] (URL de base): spécifie l'URL qui correspond au dossier\n [[yii\\web\\AssetBundle::basePath|basePath]]. \nComme pour  [[yii\\web\\AssetBundle::basePath|basePath]] (chemin de base),\n si vous spécifiez la propriété [[yii\\web\\AssetBundle::sourcePath|sourcePath]], le [gestionnaire de ressources](#asset-manager) publie les ressources et redéfinit cette propriété en conséquence. Les [alias de chemin](concept-aliases.md) sont utilisables ici.\n* [[yii\\web\\AssetBundle::css|css]]: un tableau listant les fichiers CSS contenu dans ce paquet de ressources. \nNotez que seul la barre oblique \"/\" doit être utilisée en tant que séparateur de dossier. Chaque fichier peut être spécifié en lui-même comme une chaîne de caractères ou dans un tableau avec les balises attributs et leur valeur.\n\n* [[yii\\web\\AssetBundle::js|js]]: un tableau listant les fichiers JavaScript contenus dans ce paquet. \nNotez que seule la barre oblique de division \"/\" peut être utilisée en tant que séparateur de dossiers. \nChaque fichier JavaScript peut être spécifié dans l'un des formats suivants :\n  - Un chemin relatif représentant un fichier JavaScript local (p. ex. `js/main.js`). \nLe chemin réel du fichier peut être déterminé en préfixant le chemin relatif avec le [[yii\\web\\AssetManager::basePath| chemin de base]], \net l'URL réelle du fichier peut être déterminée en préfixant le chemin relatif avec l'[[yii\\web\\AssetManager::baseUrl|URL de base]].\n  - Une URL absolue représentant un fichier JavaScript externe. \nPar exemple , `https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js` ou\n    `//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js`.\n* [[yii\\web\\AssetBundle::depends|depends (dépendances)]]: \nun tableau listant les paquets de ressources dont ce paquet dépend (brièvement expliqué).\n* [[yii\\web\\AssetBundle::jsOptions|jsOptions]]: spécifie les options qui sont passées à la méthode [[yii\\web\\View::registerJsFile()]] \nlorsqu'elle est appelée pour enregistrer *chacun des* fichiers JavaScript de ce paquet.\n* [[yii\\web\\AssetBundle::cssOptions|cssOptions]]: spécifie les options qui sont passées à la méthode \n[[yii\\web\\View::registerCssFile()]] lorsqu'elle est appelée pour enregistrer *chacun des* fichiers CSS de ce paquet.\n* [[yii\\web\\AssetBundle::publishOptions|publishOptions]]: spécifie les options qui sont passées à la méthode \n[[yii\\web\\AssetManager::publish()]] lorsqu'elle est appelée pour publier les fichiers de ressources sources dans un dossier accessible du Web.  \nCela n'est utilisé que si vous spécifiez la propriété  [[yii\\web\\AssetBundle::sourcePath|sourcePath]].\n\n\n### Emplacement des ressources <span id=\"asset-locations\"></span>\n\nEn se basant sur leur emplacement, les ressources peuvent être classifiées comme suit :\n\n* Les ressources sources : les fichiers de ressources qui sont situés avec du code source PHP et qui ne peuvent être accéder directement depuis le Web.\n Afin de pouvoir être utilisées dans une page, elles doivent être copiées dans un dossier accessible du Web et transformées en ressources publiées.\nCe processus est appelé *publication des ressources* et il sera décrit en détail bientôt. \n* Les ressources publiées : les fichiers de ressources sont situés dans un dossier accessible du Web et peuvent par conséquent être accédés directement depuis le Web. \n* Les ressources externes : les fichiers de ressources sont situés sur un serveur Web différent de celui qui héberge l'application Web. \n\n\nLors de la définition de classes de paquet de ressources, si vous spécifiez la propriété \n[[yii\\web\\AssetBundle::sourcePath|sourcePath (chemin des sources)]], cela veut dire que les ressources listées en utilisant des chemins relatifs sont considérées comme des ressources sources. \nSi vous ne spécifiez pas cette propriété, cela signifie que ces ressources sont des ressources publiées (vous devez en conséquence spécifier  [[yii\\web\\AssetBundle::basePath (chemin de base)|basePath]] et [[yii\\web\\AssetBundle::baseUrl|baseUrl (URL de base)]]\n pour faire connaître à Yii l'emplacement où elles se trouvent). \n\nIl est recommandé de placer les ressources appartenant à une application dans un dossier accessible du Web de manière à éviter une publication non nécessaire de ressources. \nC'est pourquoi `AppAsset` dans l'exemple précédent spécifie le [[yii\\web\\AssetBundle::basePath|chemin de base]] \nplutôt que le [[yii\\web\\AssetBundle::sourcePath|chemin des sources]].\n\nQuant aux [extensions](structure-extensions.md), comme leurs ressources sont situées avec le code source dans des dossiers non accessibles depuis le Web, vous devez spécifier la propriété \n[[yii\\web\\AssetBundle::sourcePath|sourcePath]] \nlorsque vous définissez des classes de paquet de ressources pour elles.\n\n> Note: n'utilisez pas  `@webroot/assets` en tant que [[yii\\web\\AssetBundle::sourcePath|chemin des sources]].\nCe dossier est utilisé par défaut par le \n[[yii\\web\\AssetManager|gestionnaire de ressources]] pour sauvegarder les fichiers de ressources publiés depuis leur emplacement source. \nTout contenu dans ce dossier est considéré temporaire et sujet à suppression. \n\n\n### Dépendances de ressources <span id=\"asset-dependencies\"></span>\n\nLorsque vous incluez plusieurs fichiers CSS ou JavaScript dans une page Web, ils doivent respecter un certain ordre pour éviter des problèmes de redéfinition. \nPar exemple, si vous utilisez l'objet graphique jQuery Ui dans une page Web, vous devez vous assurer que le fichier JavaScript jQuery est inclus avant le fichier  JavaScript  jQuery UI. \nNous appelons un tel ordre : « dépendances entre ressources ».\n\n\nLes dépendances entre ressources sont essentiellement spécifiées via la propriété \n[[yii\\web\\AssetBundle::depends]]. \nDans l'exemple `AppAsset`, le paquet de ressources dépend de deux autres paquets de ressources : [[yii\\web\\YiiAsset]] et [[yii\\bootstrap\\BootstrapAsset]], \nce qui veut dire que  les fichiers  CSS et JavaScript dans `AppAsset` sont inclus *après* les  fichiers contenus dans ces deux paquets de ressources dont ils dépendent. \n\nLes dépendances entre ressources sont transitives. Cela veut dire que si un paquet de ressources A dépend d'un paquet B qui lui-même dépend de C, A dépend de C également.\n\n\n### Options des ressources <span id=\"asset-options\"></span>\n\nVous pouvez spécifier les propriétés [[yii\\web\\AssetBundle::cssOptions|cssOptions]] et [[yii\\web\\AssetBundle::jsOptions|jsOptions]] \npour personnaliser la manière dont les fichiers CSS et JavaScript sont inclus dans une page. \nLes valeurs de ces propriétés sont passées aux méthodes [[yii\\web\\View::registerCssFile()]] et  [[yii\\web\\View::registerJsFile()]], respectivement, lorsqu'elles sont appelées par la\n [vue](structure-views.md) pour inclure les fichiers CSS et JavaScript.\n\n> Note: les options que vous définissez dans une classe de  paquet de ressources s'appliquent à  *chacun des* fichiers CSS/JavaScript du paquet.\nSi vous voulez utiliser des options différentes entre fichiers, vous devez utiliser le format indiqué [[yii\\web\\AssetBundle::css|ci-dessus]]\n ou créer des paquets de ressources séparés et utiliser un jeu d'options dans chacun des paquets. \n\nPar exemple, pour inclure un fichier CSS sous condition que le navigateur soit IE9 ou inférieur, vous pouvez utiliser l'option suivante :\n\n```php\npublic $cssOptions = ['condition' => 'lte IE9'];\n```\n\nAvec cela, le fichier  CSS du paquet pourra être inclus avec le code HTML suivant :\n\n```html\n<!--[if lte IE9]>\n<link rel=\"stylesheet\" href=\"path/to/foo.css\">\n<![endif]-->\n```\n\nPour envelopper le lien CSS généré dans une balise  `<noscript>`, vous pouvez configurer `cssOptions` comme ceci :\n\n```php\npublic $cssOptions = ['noscript' => true];\n```\n\nPour inclure un fichier JavaScript dans la section d'entête d'une page (par défaut les fichiers  JavaScript sont inclus à la fin de la section body), utilisez l'option suivante : \n\n```php\npublic $jsOptions = ['position' => \\yii\\web\\View::POS_HEAD];\n```\n\nPar défaut, lorsqu'un paquet de ressources est publié, tous les contenus dans le dossier spécifié par la propriété [[yii\\web\\AssetBundle::sourcePath]]\n sont publiés. \nVous pouvez personnaliser ce comportement en configurant la propriété [[yii\\web\\AssetBundle::publishOptions|publishOptions]]. \nPar exemple, pour publier seulement un ou quelques sous-dossiers du dossier spécifié par la propriété [[yii\\web\\AssetBundle::sourcePath]], \nvous pouvez procéder comme ceci dans la classe du paquet de ressources :\n\n```php\n<?php\nnamespace app\\assets;\n\nuse yii\\web\\AssetBundle;\n\nclass FontAwesomeAsset extends AssetBundle \n{\n    public $sourcePath = '@bower/font-awesome'; \n    public $css = [ \n        'css/font-awesome.min.css', \n    ];\n    public $publishOptions = [\n        'only' => [\n            'fonts/',\n            'css/',\n        ]\n    ];\n}  \n```\n\nL'exemple ci-dessus définit un paquet de ressources pour le [paquet \"fontawesome\"](https://fontawesome.com/). En spécifiant l'option de publication `only`, seuls les sous-dossiers `fonts` et  `css` sont publiés.\n\n\n### Installation des ressources Bower et NPM  <span id=\"bower-npm-assets\"></span>\n\nLa plupart des paquets JavaScript/CSS sont gérés par le gestionnaire de paquets [Bower](https://bower.io/) et/ou le gestionnaire de paquets [NPM](https://www.npmjs.com/). Dans le monde PHP, nous disposons de Composer, qui gère les dépendances, mais il est possible de charger des paquets Bower et NPM comme des paquets PHP en utilisant `composer.json`.\n\nPour cela, nous devons configurer quelque peu notre composer. Il y a deux options possibles :\n\n___\n\n#### En utilisant le dépôt asset-packagist\n\nCette façon de faire satisfera les exigences de la majorité des projets qui ont besoin de paquets Bower ou NPM.\n\n> Note: depuis la version 2.0.13, les modèles de projet  Basic et Advanced sont tous deux configuré pour utiliser asset-packagist par défaut, c'est pourquoi, vous pouvez sauter cette section.\n\nDans le fichier `composer.json` de votre projet, ajoutez les lignes suivantes :\n\n```json\n\"repositories\": [\n    {\n        \"type\": \"composer\",\n        \"url\": \"https://asset-packagist.org\"\n    }\n]\n```\n\nAjustez les [aliases](concept-aliases.md) `@npm` et `@bower` dans la [configuration](concept-configurations.md) de votre application :\n\n```php\n$config = [\n    ...\n    'aliases' => [\n        '@bower' => '@vendor/bower-asset',\n        '@npm'   => '@vendor/npm-asset',\n    ],\n    ...\n];\n```\n\nVisitez [asset-packagist.org](https://asset-packagist.org) pour savoir comment il fonctionne.\n\n#### En utilisant le fxp/composer-asset-plugin\n\nComparé à asset-packagist, composer-asset-plugin ne nécessite aucun changement dans la configuration de l'application. Au lieu de cela, il nécessite l'installation globale d'un greffon spécifique de Composer en exécutant la commande suivante :\n\n```bash\ncomposer global require \"fxp/composer-asset-plugin:^1.4.1\"\n```\n\nCette commande installe  [composer asset plugin](https://github.com/fxpio/composer-asset-plugin) globalement, ce qui permet de gérer les dépendances des paquets Bower et NPM via Composer. Après l'installation du greffon, tout projet de votre ordinateur prendra en charge les paquets Bower et NPM via `composer.json`.\n\nAjoutez les lignes suivantes au fichier `composer.json` de votre projet pour préciser les dossiers où seront installés les paquets, si vous voulez les publier en utilisant Yii :\n\n```json\n\"config\": {\n    \"asset-installer-paths\": {\n        \"npm-asset-library\": \"vendor/npm\",\n        \"bower-asset-library\": \"vendor/bower\"\n    }\n}\n```\n\n> Note: `fxp/composer-asset-plugin` ralentit significativement la commande `composer update` en comparaison avec asset-packagist.\n \n____\n \nAprès avoir configuré Composer pour qu'il prenne en charge Bower et NPM :\n\n1. Modifiez le fichier the `composer.json` de votre application ou extension et listez le paquet dans l'entrée `require`.\n   Vous devez utiliser `bower-asset/PackageName` (pour les paquets Bower) ou `npm-asset/PackageName` (pour les paquets NPM) pour faire référence à la bibliothèque.\n2. Exécutez `composer update`\n3. Créez une classe de paquet de ressources et listez les fichiers JavaScript/CSS que vous envisagez d'utiliser dans votre application ou extension.\n   Vous devez spécifier la propriété [[yii\\web\\AssetBundle::sourcePath|sourcePath]] comme `@bower/PackageName` ou `@npm/PackageName`.\n   Cela parce que Composer installera le paquet Bower ou NPM dans le dossier correspondant à cet alias.\n\n> Note: quelques paquets peuvent placer tous leurs fichiers distribués dans un sous-dossier. Si c'est le cas, vous devez spécifier le sous-dossier en tant que valeur de [[yii\\web\\AssetBundle::sourcePath|sourcePath]]. Par exemple, utilisez [[yii\\web\\JqueryAsset]] `@bower/jquery/dist` au lieu de `@bower/jquery`.\n\n\n## Utilisation des paquets de ressources <span id=\"using-asset-bundles\"></span>\n\nPour utiliser un paquet de ressources, enregistrez-le dans une [vue](structure-views.md) en appelant la méthode [[yii\\web\\AssetBundle::register()]]. Par exemple, dans un modèle de vue, vous pouvez enregistrer un paquet de ressources de la manière suivante :\n\n```php\nuse app\\assets\\AppAsset;\nAppAsset::register($this);  // $this représente l'objet *view* (vue)\n```\n\n> Info: la méthode [[yii\\web\\AssetBundle::register()]] retourne un objet paquet de ressources contenant les informations sur les ressources publiées, telles que le [[yii\\web\\AssetBundle::basePath|chemin de base]] ou l'[[yii\\web\\AssetBundle::baseUrl|URL de base]].\n\nSi vous êtes en train d'enregistrer un paquet de ressources dans d'autres endroits, vous devez fournir l'objet *view* requis. Par exemple, pour enregistrer un paquet de ressources dans une classe d'[objet graphique](structure-widgets.md), vous pouvez obtenir l'objet *view* avec l'expression `$this->view`.\n\nLorsqu'un paquet de ressources est enregistré avec une vue, en arrière plan. Yii enregistre tous les paquets de ressources dont il dépend. Et si un paquet de ressources est situé dans un dossier inaccessible depuis le Web, il est publié dans un dossier accessible depuis le Web. Plus tard, lorsque la vue rend une page, elle génère les balises  `<link>` et `<script>` pour les fichiers  CSS et JavaScript listés dans le paquet de ressources enregistré. L'ordre des ces balises est déterminé par les dépendances entre paquets enregistrés et l'ordre des ressources listées dans les propriétés  [[yii\\web\\AssetBundle::css]] et [[yii\\web\\AssetBundle::js]].\n\n\n### Paquets de ressources dynamiques <span id=\"dynamic-asset-bundles\"></span>\n\nUne classe PHP ordinaire de paquet de ressources peut comporter sa propre logique et peut ajuster ses paramètres internes dynamiquement.\nPar exemple : il se peut que vous utilisiez une bibliothèque JavaScript sophistiquée  qui des ressources d'internationalisation dans des fichiers séparés pour chacune des langues. En conséquence de quoi, vous devez ajouter certains fichiers '.js' particuliers à votre page pour la fonction de traduction de la bibliothèque fonctionne. Cela peut être fait en redéfinissant la méthode [[yii\\web\\AssetBundle::init()]] :\n\n\n```php\nnamespace app\\assets;\n\nuse yii\\web\\AssetBundle;\nuse Yii;\n\nclass SophisticatedAssetBundle extends AssetBundle\n{\n    public $sourcePath = '/path/to/sophisticated/src';\n    public $js = [\n        'sophisticated.js' // fichier toujours utilisé\n    ];\n\n    public function init()\n    {\n        parent::init();\n        $this->js[] = 'i18n/' . Yii::$app->language . '.js'; // fichier dynamique ajouté\n    }\n}\n```\n\nUn paquet de ressources particuliers peut aussi être ajusté via son instance retourné par [[yii\\web\\AssetBundle::register()]].\nPar exemple :\n\n```php\nuse app\\assets\\SophisticatedAssetBundle;\nuse Yii;\n\n$bundle = SophisticatedAssetBundle::register(Yii::$app->view);\n$bundle->js[] = 'i18n/' . Yii::$app->language . '.js'; // fichier dynamique ajouté\n```\n\n> Note : bien que l'ajustement dynamique des paquets de ressources soit pris e charge, c'est une **mauvaise** pratique qui peut conduire à des effets de bord inattendus et qui devrait être évité si possible. \n\n### Personnalisation des paquets de ressources <span id=\"customizing-asset-bundles\"></span>\n\nYii gère les paquets de ressources à l'aide d'un composant d'application nommé  `assetManager` (gestionnaire de ressources) qui est mis œuvre par [[yii\\web\\AssetManager]]. En configurant la propriété [[yii\\web\\AssetManager::bundles]], il est possible de personnaliser le comportement d'un paquet de ressources. Par exemple, le paquet de ressources par défaut [[yii\\web\\JqueryAsset]] utilise le fichier `jquery.js` du paquet Bower installé. Pour améliorer la disponibilité et la performance, vous désirez peut-être utiliser une version hébergée par Google. Vous pouvez le faire en configurant `assetManager` dans la configuration de l'application comme ceci :\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'bundles' => [\n                'yii\\web\\JqueryAsset' => [\n                    'sourcePath' => null,   // ne pas publier le paquet\n                    'js' => [\n                        '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js',\n                    ]\n                ],\n            ],\n        ],\n    ],\n];\n```\n\nVous pouvez configurer de multiples paquets de ressources de manière similaire via [[yii\\web\\AssetManager::bundles]]. Les clés du tableau doivent être les nom des classes (sans la barre oblique inversée de tête) des paquets de ressources, et les valeurs du tableau doivent être les [tableaux de configuration](concept-configurations.md) correspondants.\n\n> Tip: vous pouvez choisir quelles ressources utiliser dans un paquet en fonction d'une condition. L'exemple suivant montre comment utiliser  `jquery.js` dans l'environnement de développement et  `jquery.min.js` autrement :\n>\n> ```php\n> 'yii\\web\\JqueryAsset' => [\n>     'js' => [\n>         YII_ENV_DEV ? 'jquery.js' : 'jquery.min.js'\n>     ]\n> ],\n> ```\n\nVous pouvez désactiver un ou plusieurs paquets de ressources en associant `false` (faux) aux noms des paquets de ressources que vous voulez désactiver. Lorsque vous enregistrez un paquet de ressources dans une vue, aucun des paquets dont il dépend n'est enregistré, et la vue, elle non plus, n'inclut aucune des ressources du paquet dans la page qu'elle rend. Par exemple, pour désactiver [[yii\\web\\JqueryAsset]], vous pouvez utiliser la configuration suivante :\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'bundles' => [\n                'yii\\web\\JqueryAsset' => false,\n            ],\n        ],\n    ],\n];\n```\n\nVous pouvez aussi désactiver *tous* les paquets de ressources en définissant [[yii\\web\\AssetManager::bundles]] à la valeur `false`.\n\n\n### Mise en correspondance des ressources <span id=\"asset-mapping\"></span>\n\nParfois, vous désirez « corriger » des chemins de fichiers de ressources incorrects ou incompatibles utilisés par plusieurs paquets de ressources. Par exemple, un paquet A utilise  `jquery.min.js` version 1.11.1, et un paquet  B utilise  `jquery.js` version 2.1.1. Bien que vous puissiez corriger le problème en personnalisant chacun des paquets, une façon plus facile est d'utiliser la fonctionnalité *mise en correspondance des ressources* pour mettre en correspondance les ressources incorrectes avec celles désirées. Pour le faire, configurez la propriété [[yii\\web\\AssetManager::assetMap (table de mise en correspondance des ressources)]] comme indiqué ci-après :\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'assetMap' => [\n                'jquery.js' => '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js',\n            ],\n        ],\n    ],\n];\n```\n\nLes clés de la [[yii\\web\\AssetManager::assetMap|table de mise en correspondance des ressources]] sont les noms des ressources que vous voulez corriger, et les valeurs sont les chemins des ressources désirées. Lorsque vous enregistrez un paquet de ressources dans une vue, chacune des ressources relatives dans ses tableaux [[yii\\web\\AssetBundle::css|css]] et [[yii\\web\\AssetBundle::js|js]] sont examinées dans cette table. Si une des clés est trouvée comme étant la dernière partie d'un chemin de fichier de ressources (qui est préfixé par le [[yii\\web\\AssetBundle::chemin des sources si disponible)]], la valeur correspondante remplace la ressource et est enregistrée avec la vue.\nFor exemple, le fichier de ressources  `my/path/to/jquery.js` correspond à la clé `jquery.js`.\n\n> Note: seules les ressources spécifiées en utilisant des chemins relatifs peuvent faire l'objet d'une mise en correspondance. Les chemins de ressources cibles doivent être soit des URL absolues, soit des chemins relatifs à  [[yii\\web\\AssetManager::basePath]].\n\n\n### Publication des ressources <span id=\"asset-publishing\"></span>\n\nComme mentionné plus haut, si un paquet de ressources est situé dans un dossier non accessible depuis le Web, ses ressources sont copiées dans un dossier Web lorsque le paquet est enregistré dans une vue. Ce processus est appelé *publication des ressources* et est accompli automatiquement par le  [[yii\\web\\AssetManager|gestionnaire de ressources]].\n\nPar défaut, les ressources sont publiées dans le dossier `@webroot/assets` qui correspond à l'URL `@web/assets`. Vous pouvez personnaliser cet emplacement en configurant les propriétés [[yii\\web\\AssetManager::basePath|basePath]] et [[yii\\web\\AssetManager::baseUrl|baseUrl]].\n\nAu lieu de publier les ressources en copiant les fichiers, vous pouvez envisager d'utiliser des liens symboliques, si votre système d'exploitation et votre serveur Web le permettent. Cette fonctionnalité peut être activée en définissant la propriété [[yii\\web\\AssetManager::linkAssets|linkAssets]] à `true` (vrai).\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'linkAssets' => true,\n        ],\n    ],\n];\n```\n\nAvec la configuration ci-dessus, le gestionnaire de ressources crée un lien symbolique vers le chemin des sources d'un paquet de ressources lors de sa publication. Cela est plus rapide que la copie de fichiers et peut également garantir que les ressources publiées sont toujours à jour.\n\n\n\n### Fonctionnalité d'affranchissement du cache <span id=\"cache-busting\"></span>\n\nPour les application Web tournant en mode production, une pratique courante consiste à activer la mise en cache HTTP pour les ressources statiques. Un inconvénient de cette pratique est que si vous modifiez une ressource et la republiez en production, le navigateur peut toujours utiliser l'ancienne version à cause de la mise en cache HTTP. Pour s'affranchir de cet inconvénient, vous pouvez utiliser la fonctionnalité d'affranchissement du cache qui a été introduite dans la version 2.0.3 en configurant le gestionnaire de ressources [[yii\\web\\AssetManager]] comme suit :\n  \n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'appendTimestamp' => true,\n        ],\n    ],\n];\n```\n\nCe faisant, l'horodatage de la dernière modification du fichier est ajoutée en fin d'URL de la ressource publiée. Par exemple, l'URL vers `yii.js` ressemble à  `/assets/5515a87c/yii.js?v=1423448645\"`, où `v` représente l'horodatage de la dernière modification du fichier `yii.js`. Désormais, si vous modifiez une ressource, son URL change également ce qui force le navigateur à aller chercher la dernière version de la ressource.\n\n\n## Paquets de ressources couramment utilisés <span id=\"common-asset-bundles\"></span>\n\nLe code du noyau de  Yii a défini de nombreux paquets de ressources. Parmi eux, les paquets suivants sont couramment utilisés et peuvent être référencés dans le code de votre application ou de votre extension.\n\n- [[yii\\web\\YiiAsset]]: ce paquet comprend essentiellement le fichier `yii.js` qui met en œuvre un mécanisme d'organisation du code JavaScript en modules. Il fournit également une prise en charge spéciale des attributs `data-method` et `data-confirm` et autres fonctionnalités utiles. \n- [[yii\\web\\JqueryAsset]]: ce paquet comprend le fichier  `jquery.js` du paquet Bower de jQuery.\n- [[yii\\bootstrap\\BootstrapAsset]]: ce paquet inclut le fichier CSS du framework Twitter Bootstrap.\n- [[yii\\bootstrap\\BootstrapPluginAsset]]: ce paquet inclut le fichier JavaScript du framework Twitter Bootstrap pour la prise en charge des greffons JavaScript de Bootstrap.\n- [[yii\\jui\\JuiAsset]]: ce paquet inclut les fichiers CSS et JavaScript de la bibliothèque  jQuery UI.\n\nSi votre code dépend de jQuery, jQuery UI ou Bootstrap, vous devriez utiliser les paquets de ressources prédéfinis plutôt que de créer vos propres versions. Si les réglages par défaut des ces paquets de ressources prédéfinis ne répondent pas à vos besoins, vous pouvez les personnaliser  comme expliqué à la sous-section [Personnalisation des paquets de ressources](#customizing-asset-bundles). \n\n\n## Conversion de ressources <span id=\"asset-conversion\"></span>\n\nAu lieu d'écrire directement leur code CSS et/ou JavaScript, les développeurs l'écrivent souvent dans une syntaxe étendue et utilisent des outils spéciaux pour le convertir en CSS/JavaScript. Par exemple, pour le code CSS vous pouvez utiliser [LESS](https://lesscss.org/) ou [SCSS](https://sass-lang.com/); et pour JavaScript, vous pouvez utiliser [TypeScript](https://www.typescriptlang.org/).\n\nVous pouvez lister les fichiers de ressources écrits dans une syntaxe  étendue dans les propriétés [[yii\\web\\AssetBundle::css|css]] et [[yii\\web\\AssetBundle::js|js]] d'un paquet de ressources. \n\n```php\nclass AppAsset extends AssetBundle\n{\n    public $basePath = '@webroot';\n    public $baseUrl = '@web';\n    public $css = [\n        'css/site.less',\n    ];\n    public $js = [\n        'js/site.ts',\n    ];\n    public $depends = [\n        'yii\\web\\YiiAsset',\n        'yii\\bootstrap\\BootstrapAsset',\n    ];\n}\n```\n\nLorsque vous enregistrez une tel paquet de ressources dans une vue, le [[yii\\web\\AssetManager|gestionnaire de ressources]] exécute automatiquement l'outil de pré-traitement pour convertir les ressources, écrites dans une syntaxe reconnue, en CSS/JavaScript. Lorsque la vue rend finalement la page, elle inclut les fichiers CSS/JavaScript dans la page, au lieu des ressources originales écrites dans la syntaxe étendue. \n\nYii utilise l'extension du nom de fichier pour identifier dans quelle syntaxe une ressource est écrite. Par défaut, il reconnaît les syntaxes et les extensions de nom suivants :\n\n- [LESS](https://lesscss.org/): `.less`\n- [SCSS](https://sass-lang.com/): `.scss`\n- [Stylus](https://stylus-lang.com/): `.styl`\n- [CoffeeScript](https://coffeescript.org/): `.coffee`\n- [TypeScript](https://www.typescriptlang.org/): `.ts`\n\nYii se fie aux outils de pré-traitement installés pour convertir les ressources. Par exemple, pour utiliser [LESS](https://lesscss.org/), vous devriez utiliser la commande de pré-traitement `lessc`.\n\nVous pouvez personnaliser les commandes de pré-traitement et la syntaxe étendue prise en charge en configurant [[yii\\web\\AssetManager::converter]] comme ci-après :\n\n```php\nreturn [\n    'components' => [\n        'assetManager' => [\n            'converter' => [\n                'class' => 'yii\\web\\AssetConverter',\n                'commands' => [\n                    'less' => ['css', 'lessc {from} {to} --no-color'],\n                    'ts' => ['js', 'tsc --out {to} {from}'],\n                ],\n            ],\n        ],\n    ],\n];\n```\n\nDans la syntaxe précédente, nous spécifions les syntaxes étendues prises en charge via la propriété [[yii\\web\\AssetConverter::commands]]. Les clés du tableau sont les extensions de nom de fichier (sans le point de tête), et les valeurs sont les extensions des fichiers de ressources résultants ainsi que les commandes pour effectuer les conversions. Les valeurs à remplacer `{from}` et `{to}` dans les commandes doivent être remplacées par les chemins de fichiers de ressources sources et les chemins de fichiers de ressources cibles.\n\n> Info: il y a d'autres manières de travailler avec les ressources en syntaxe étendue, en plus de celle décrite ci-dessus. Par exemple, vous pouvez utiliser des outils de compilation comme [grunt](https://gruntjs.com/) pour surveiller et convertir automatiquement des ressources écrites en syntaxe étendue. Dans ce cas, vous devez lister les fichiers CSS/JavaScript résultants dans des paquets de ressources plutôt que les fichiers originaux. \n\n\n## Combinaison et compression de ressources <span id=\"combining-compressing-assets\"></span>\n\nUne page Web peut inclure plusieurs fichiers CSS et/ou JavaScript. Pour réduire le nombre de requêtes HTTP et la taille des fichiers téléchargés, une pratique courante est de combiner et compresser ces fichiers CSS/JavaScript multiples en un ou très peu de fichiers, et d'inclure ces fichiers compressés dans les pages Web à la place des fichiers originaux. \n \n> Info: la combinaison et la compression de ressources sont généralement nécessaires lorsqu'une application est dans le mode production. En mode développement, l'utilisation des fichiers CSS/JavaScript originaux est souvent plus pratique pour des raisons de débogage plus facile.\n\nDans ce qui est présenté ci-dessous, nous introduisons une approche pour combiner et compresser les fichiers de ressources sans avoir besoin de modifier le code existant. \n\n1. Identifier tous les paquets de ressources dans l'application que vous envisagez de combiner et de compresser.\n2. Diviser ces paquets en un ou quelques groupes. Notez que chaque paquet ne peut appartenir qu'à un seul groupe. \n3. Combiner/compresser les fichiers CSS de chacun des groupes en un fichier unique. Faire de même avec les fichiers JavaScript. \n4. Définir un nouveau paquet de ressources pour chacun des groupes : \n   * Définir les propriétés [[yii\\web\\AssetBundle::css|css]] et [[yii\\web\\AssetBundle::js|js]] comme étant les fichiers CSS et JavaScript combinés, respectivement. \n   * Personnaliser les paquets de ressources dans chacun des groupes en définissant leurs propriétés [[yii\\web\\AssetBundle::css|css]] et \n     [[yii\\web\\AssetBundle::js|js]] comme étant vides, et en définissant leur propriété [[yii\\web\\AssetBundle::depends|depends]] comme étant le nouveau paquet de ressources créé pour le groupe.\n\nEn utilisant cette approche, lorsque vous enregistrez un paquet de ressources dans une vue, cela engendre un enregistrement automatique du nouveau paquet de ressources pour le groupe auquel le paquet original appartient. Et, en conséquence, les fichiers de ressources combinés/compressés sont inclus dans la page à la place des fichiers originaux. \n\n\n### Un exemple <span id=\"example\"></span>\n\nExaminons ensemble un exemple pour expliquer plus précisément l'approche ci-dessus. \n\nSupposons que votre application possède deux pages X et Y. La page X utilise les paquets de ressources A, B et C, tandis que la page Y utilise les paquets des ressources B, C et D. \n\nVous avez deux possibilités pour diviser ces paquets de ressources. La première consiste à utiliser un groupe unique pour y inclure tous les paquets de ressources, la seconde est de mettre A dans un groupe X, D dans un groupe Y et (B,C) dans un groupe S. Laquelle des deux est la meilleure ? Cela dépend. La première possibilité offre l'avantage que les deux pages partagent les mêmes fichiers CSS et JavaScript combinés, ce qui rend la mise en cache HTTP plus efficace. Cependant, comme le groupe unique contient tous les paquets, la taille des fichiers combinés CSS et JavaScript est plus importante et accroît donc le temps de transmission initial. Par souci de simplification, dans cet exemple, nous utiliserons la première possibilité, c'est à dire, un groupe unique contenant tous les paquets. \n\n> Info: la division des paquets de ressources en groupes, n'est pas une tâche triviale. Cela requiert généralement une analyse du trafic réel des données des différentes ressources sur différentes pages. Au début, vous pouvez démarrer avec un groupe unique par souci de simplification. \n\nUtilisez les outils existants (p. ex. [Closure Compiler](https://developers.google.com/closure/compiler/), YUI Compressor](https://github.com/yui/yuicompressor/)) pour combiner et compresser les fichiers CSS et JavaScript dans tous les paquets. Notez que les fichiers doivent être combinés dans l'ordre qui permet de satisfaire toutes les dépendances entre paquets. Par exemple, si le paquet A dépend du paquet B, qui dépend lui-même du paquet C et du paquet D, alors vous devez lister les fichiers de ressources en commençant par C et D, suivi de B et, pour finir, A. \n\nAprès avoir combiné et compressé, nous obtenons un fichier CSS et un fichier JavaScript. Supposons qu'ils s'appellent `all-xyz.css` et `all-xyz.js`, où `xyz` est un horodatage ou une valeur de hachage qui est utilisé pour rendre le nom de fichier unique afin d'éviter les problèmes de mise en cache HTTP.\n \nNous en sommes au dernier stade maintenant. Configurez le [[yii\\web\\AssetManager|gestionnaire de ressources]] dans la configuration de l'application comme indiqué ci-dessous :\n\n\n```php\nreturn [\n    'components' => [\n        'assetManager' => [\n            'bundles' => [\n                'all' => [\n                    'class' => 'yii\\web\\AssetBundle',\n                    'basePath' => '@webroot/assets',\n                    'baseUrl' => '@web/assets',\n                    'css' => ['all-xyz.css'],\n                    'js' => ['all-xyz.js'],\n                ],\n                'A' => ['css' => [], 'js' => [], 'depends' => ['all']],\n                'B' => ['css' => [], 'js' => [], 'depends' => ['all']],\n                'C' => ['css' => [], 'js' => [], 'depends' => ['all']],\n                'D' => ['css' => [], 'js' => [], 'depends' => ['all']],\n            ],\n        ],\n    ],\n];\n```\n\nComme c'est expliqué dans la sous-section [Personnalisation des paquets de ressources](#customizing-asset-bundles), la configuration ci-dessus modifie le comportement par défaut des chacun des paquets. En particulier, les paquets  A, B, C et D ne possèdent plus aucun fichier de ressources. Ils dépendent tous du paquet `all` qui contient les fichiers combinés `all-xyz.css` et `all-xyz.js`.\nPar conséquent, pour la page X, au lieu d'inclure les fichiers sources originaux des paquets  A, B et C, seuls ces deux fichiers combinés sont inclus ; la même chose se passe par la page Y. \n\nIl y a un truc final pour rendre l'approche ci-dessus plus lisse. Au lieu de modifier directement le fichier de configuration de l'application, vous pouvez mettre le tableau de personnalisation dans un fichier séparé et l'inclure dans la configuration de l'application en fonction d'une condition. Par exemple :\n\n```php\nreturn [\n    'components' => [\n        'assetManager' => [\n            'bundles' => require __DIR__ . '/' . (YII_ENV_PROD ? 'assets-prod.php' : 'assets-dev.php'),  \n        ],\n    ],\n];\n```\n\nCela veut dire que le tableau de  configuration du paquet de ressources est sauvegardé dans  `assets-prod.php` pour le mode production, et `assets-dev.php` pour les autres modes.\n\n\n### Utilisation de la commande `asset`<span id=\"using-asset-command\"></span>\n\nYii fournit une commande de console nommée `asset` pour automatiser l'approche que nous venons juste de décrire. \n\nPour utiliser cette commande, vous devez d'abord créer un fichier de configuration pour décrire quels paquets de ressources seront combinés et comment ils seront regroupés. Vous pouvez utiliser la sous-commande `asset/template` pour créer d'abord un modèle, puis le modifier pour l'adapter à vos besoins. \n\n```\nyii asset/template assets.php\n```\n\nLa commande génère un fichier `assets.php` dans le dossier courant. Le contenu de ce fichier ressemble à ce qui suit :\n\n```php\n<?php\n/**\n * Fichier de configuration pour la commande de console \"yii asset\".\n * Notez que dans l'environnement console, quelques alias de chemin comme  '@webroot' et '@web' peuvent ne pas exister.\n * Pensez à définir ces alias de chemin manquants. \n */\nreturn [\n    // Ajuste la commande/fonction de rappel pour la compression des fichiers JavaScript :\n    'jsCompressor' => 'java -jar compiler.jar --js {from} --js_output_file {to}',\n    // Ajuste la commande/fonction de rappel pour la compression des fichiers   CSS :\n    'cssCompressor' => 'java -jar yuicompressor.jar --type css {from} -o {to}',\n    // La liste des paquets de ressources à compresser :\n    'bundles' => [\n        // 'yii\\web\\YiiAsset',\n        // 'yii\\web\\JqueryAsset',\n    ],\n    // Paquets de ressources par la sortie de compression :\n    'targets' => [\n        'all' => [\n            'class' => 'yii\\web\\AssetBundle',\n            'basePath' => '@webroot/assets',\n            'baseUrl' => '@web/assets',\n            'js' => 'js/all-{hash}.js',\n            'css' => 'css/all-{hash}.css',\n        ],\n    ],\n    // Configuration du gestionnaire de ressources :\n    'assetManager' => [\n    ],\n];\n```\n\nVous devez modifier ce fichier et spécifier quels paquets vous envisagez de combiner dans l'option  `bundles`. Dans l'option `targets` vous devez spécifier comment les paquets sont divisés en groupes. Vous pouvez spécifier un ou plusieurs groupes, comme nous l'avons déjà dit.\n\n> Note: comme les alias `@webroot` et `@web` ne sont pas disponibles dans l'application console, vous devez les définir explicitement dans la configuration.\n\nLes fichiers JavaScript sont combinés, compressés et écrits dans `js/all-{hash}.js` où {hash} est une valeur à remplacer par la valeur de hachage du fichier résultant.\n\nLes options `jsCompressor` et `cssCompressor` spécifient les commandes de console ou les fonctions de rappel PHP pour effectuer la combinaison/compression des fichiers JavaScript et CSS. Par défaut, Yii utilise [Closure Compiler](https://developers.google.com/closure/compiler/) pour combiner les fichiers JavaScript et [YUI Compressor](https://github.com/yui/yuicompressor/) pour combiner les fichiers CSS. Vous devez installer ces outils à la main ou ajuster ces options pour utiliser vos outils favoris.\n\n\nAvec le fichier de configuration, vous pouvez exécuter la commande `asset` pour combiner et compresser les fichiers de ressources et générer un nouveau fichier de configuration de paquet de ressources `assets-prod.php`:\n \n```\nyii asset assets.php config/assets-prod.php\n```\n\nLe fichier de configuration peut être inclus dans la configuration de l'application comme décrit dans la dernière sous-section . \n\n\n> Info: l'utilisation de la commande `asset` n'est pas la seule option pour automatiser la combinaison et la compression des ressources. Vous pouvez utiliser l'excellent outil d'exécution de tâches [grunt](https://gruntjs.com/) pour arriver au même résultat. \n\n\n### Regroupement des paquets de ressources  <span id=\"grouping-asset-bundles\"></span>\n\nDans la dernière sous-section présentée, nous avons expliqué comment combiner tous les paquets de ressources en un seul de manière à minimiser les requêtes HTTP pour les fichiers de ressources utilisés par l'application. Ce n'est pas toujours une pratique souhaitable. Par exemple, imaginez que votre application dispose d'une interface utilisateur (*frontend*) et d'une interface d'administration (*backend*), lesquelles utilisent un jeu différent de fichiers CSS et JavaScript. Dans un tel cas, combiner les paquets de ressources des deux interfaces en un seul  n'a pas beaucoup de sens, parce que les paquets de ressources pour l'interface utilisateur ne sont pas utilisés par l'interface d'administration, et parce que cela conduit à un gâchis de bande passante du réseau d'envoyer les ressources de l'interface d'administration lorsqu'une page du l'interface utilisateur est demandée. \n \nPour résoudre ce problème, vous pouvez diviser les paquets de ressources en groupes et combiner les paquets de ressources de chacun des groupes. La configuration suivante montre comment vous pouvez grouper les paquets de ressources :\n```php\nreturn [\n    ...\n    // Specifie les paquets de sortie par groupe :\n    'targets' => [\n        'allShared' => [\n            'js' => 'js/all-shared-{hash}.js',\n            'css' => 'css/all-shared-{hash}.css',\n            'depends' => [\n                // Include all assets shared between 'backend' and 'frontend'\n                'yii\\web\\YiiAsset',\n                'app\\assets\\SharedAsset',\n            ],\n        ],\n        'allBackEnd' => [\n            'js' => 'js/all-{hash}.js',\n            'css' => 'css/all-{hash}.css',\n            'depends' => [\n                // Include only 'backend' assets:\n                'app\\assets\\AdminAsset'\n            ],\n        ],\n        'allFrontEnd' => [\n            'js' => 'js/all-{hash}.js',\n            'css' => 'css/all-{hash}.css',\n            'depends' => [], // Include all remaining assets\n        ],\n    ],\n    ...\n];\n```\n\nComme vous le voyez, les paquets de ressources sont divisés en trois groupes : `allShared`, `allBackEnd` et `allFrontEnd`. Ils dépendent tous d'un jeu approprié de paquets de ressources. Par exemple,  `allBackEnd` dépend de  `app\\assets\\AdminAsset`. En exécutant la commande  `asset` avec cette configuration, les paquets de ressources sont combinés en respectant les spécifications ci-dessus. \n\n> Info: vous pouvez laisser la configuration de  `depends` vide pour l'un des paquets cible. Ce faisant, ce paquet de ressources dépendra de tous les paquets de ressources dont aucun autre paquet de ressources ne dépend. \n"
  },
  {
    "path": "docs/guide-fr/structure-controllers.md",
    "content": "Contrôleurs\n===========\n\nLes contrôleurs font partie du modèle d'architecture [MVC](https://fr.wikipedia.org/wiki/Mod%C3%A8le-vue-contr%C3%B4leur) (Modèle Vue Contrôleur). Ce sont des objets dont la classe étend [[yii\\base\\Controller]]. Ils sont chargés de traiter les requêtes et de générer les réponses. En particulier, après que l'objet [application](structure-applications.md) leur a passé le contrôle, ils analysent les données de la requête entrante, les transmettent aux [modèles](structure-models.md), injectent le résultat des modèles dans les [vues](structure-views.md) et, pour finir, génèrent les réponses sortantes. \n\n\n## Actions <span id=\"actions\"></span>\n\nLes contrôleurs sont constitués d'*actions* qui sont les unités les plus élémentaires dont l'utilisateur final peut demander l'exécution. Un contrôleur comprend une ou plusieurs actions. \n\nL'exemple qui suit présente un contrôleur `post` avec deux actions : `view` et `create`:\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse app\\models\\Post;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\n\nclass PostController extends Controller\n{\n    public function actionView($id)\n    {\n        $model = Post::findOne($id);\n        if ($model === null) {\n            throw new NotFoundHttpException;\n        }\n\n        return $this->render('view', [\n            'model' => $model,\n        ]);\n    }\n\n    public function actionCreate()\n    {\n        $model = new Post;\n\n        if ($model->load(Yii::$app->request->post()) && $model->save()) {\n            return $this->redirect(['view', 'id' => $model->id]);\n        } else {\n            return $this->render('create', [\n                'model' => $model,\n            ]);\n        }\n    }\n}\n```\n\nDans l'action `view` (définie par la méthode `actionView()`), le code commence par charger le [modèle](structure-models.md) en fonction de l'identifiant (ID) du modèle requis. Si le chargement du modèle réussit, l'action l'affiche en utilisant une [vue](structure-views.md) nommée `view`. Autrement, elle lève une exception. \n\nDans l'action  `create` (définie par le méthode `actionCreate()`), le code est similaire.  Elle commence par essayer de peupler une nouvelle instance du [modèle](structure-models.md) avec les données de la requête et sauvegarde le modèle. Si les deux opérations réussissent, elle redirige le navigateur vers l'action  `view` en lui passant l'identifiant (ID) du nouveau modèle. Autrement, elle affiche la vue `create` dans laquelle l'utilisateur peut saisir les entrées requises.\n\n\n## Routes <span id=\"routes\"></span>\n\nL'utilisateur final demande l'exécution des actions via ce qu'on appelle des *routes*. Une route est une chaîne de caractères constituée des parties suivantes :\n\n* un identifiant (ID) de module : cette partie n'est présente que si le contrôleur appartient à un [module](structure-modules.md) qui n'est pas en soi une application ;\n* un [identifiant de contrôleur](#controller-ids) : une chaîne de caractères qui distingue le contrôleur des autres contrôleurs de la même application — ou du même module si le contrôleur appartient à un module ;\n* un [identifiant d'action](#action-ids) : une chaîne de caractères qui distingue cette action des autres actions du même contrôleur.\n\nLes routes se présentent dans le format suivant :\n\n```\nidentifiant_de_contrôleur/identifiant_d_action\n```\n\nou dans le format suivant si le contrôleur appartient à un module :\n\n```php\nidentifiant_de_module/identifiant_de_contrôleur/identifiant_d_action\n```\n\nAinsi si un utilisateur requiert l'URL `https://hostname/index.php?r=site/index`, l'action `index` dans le contrôleur `site` sera exécutée. Pour plus de détails sur la façon dont les routes sont résolues, reportez-vous à la section [Routage et génération d'URL](runtime-routing.md).\n\n\n## Création des contrôleurs <span id=\"creating-controllers\"></span>\n\nDans les [[yii\\web\\Application|applications Web]], les contrôleur doivent étendre la classe [[yii\\web\\Controller]] ou ses classes filles. De façon similaire, dans les [[yii\\console\\Application|applications de console]], les contrôleurs doivent étendre la classe [[yii\\console\\Controller]] ou ses classes filles. Le code qui suit définit un contrôleur nommé `site` :\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n}\n```\n\n\n### Identifiant des contrôleurs <span id=\"controller-ids\"></span>\n\nGénéralement, un contrôleur est conçu pour gérer les requêtes concernant un type particulier de ressource. Pour cette raison, l'identifiant d'un contrôleur est souvent un nom faisant référence au type de ressources que ce contrôleur gère. \nPar exemple, vous pouvez utiliser `article` comme identifiant d'un contrôleur qui gère des données d'articles. \n\nPar défaut, l'identifiant d'un contrôleur ne peut contenir que les caractères suivants : lettres de l'alphabet anglais en bas de casse, chiffres, tiret  bas, trait d'union et barre oblique de division. Par exemple, `article` et `post-comment` sont tous deux des identifiants de contrôleur valides, tandis que `article?`, `PostComment` et `admin\\post` ne le sont pas.\nUn identifiant de contrôleur peut aussi contenir un préfixe de sous-dossier. Par exemple `admin/article` représente un contrôleur `article` dans le dossier `admin` dans l'[[yii\\base\\Application::controllerNamespace|espace de noms du contrôleur]].\nLes caractères valides pour le préfixe des sous-dossiers incluent : les lettres de l'alphabet anglais dans les deux casses, les chiffres, le tiret bas et la barre oblique de division, parmi lesquels les barres obliques de division sont utilisées comme séparateurs pour les sous-dossiers à plusieurs niveaux (p. ex. `panels/admin`).\n\n### Nommage des classes de contrôleur <span id=\"controller-class-naming\"></span>\n\nLes noms de classe de contrôleur peut être dérivés de l'identifiant du contrôleur selon la procédure suivante :\n\n1. Mettre la première lettre de chacun des mots séparés par des trait d'union en capitale. Notez que si l'identifiant du contrôleur contient certaines barres obliques, cette règle ne s'applique qu'à la partie après la dernière barre oblique dans l'identifiant.\n2. Retirer les traits d'union et remplacer toute barre oblique de division par une barre oblique inversée. \n3. Ajouter le suffixe  `Controller`.\n4. Préfixer avec l'[[yii\\base\\Application::controllerNamespace|espace de noms du contrôleur]].\n\nCi-après sont présentés quelques exemples en supposant que l'[[yii\\base\\Application::controllerNamespace|espace de noms du contrôleur]] prend la valeur par défaut, soit `app\\controllers`:\n\n* `article` donne `app\\controllers\\ArticleController`;\n* `post-comment` donne `app\\controllers\\PostCommentController`;\n* `admin/post-comment` donne `app\\controllers\\admin\\PostCommentController`;\n* `adminPanels/post-comment` donne `app\\controllers\\adminPanels\\PostCommentController`.\n\nLes classes de contrôleur doivent être [auto-chargeables](concept-autoloading.md). Pour cette raison, dans les exemples qui précèdent, la classe de contrôleur  `article` doit être sauvegardée dans le fichier dont l'[alias](concept-aliases.md) est `@app/controllers/ArticleController.php`; tandis que la classe de contrôleur `admin/post-comment` doit se trouver dans `@app/controllers/admin/PostCommentController.php`.\n\n> Info: dans le dernier exemple,  `admin/post-comment` montre comment placer un contrôleur dans un sous-dossier de l'[[yii\\base\\Application::controllerNamespace|espace de noms du contrôleur]]. Cela est utile lorsque vous voulez organiser vos contrôleurs en plusieurs catégories et que vous ne voulez pas utiliser de [modules](structure-modules.md).\n\n\n### Table de mise en correspondance des contrôleurs <span id=\"controller-map\"></span>\n\nVous pouvez configurer [[yii\\base\\Application::controllerMap|controller map (table de mise en correspondance des contrôleurs)]] pour outrepasser les contraintes concernant les identifiants de contrôleur et les noms de classe décrites plus haut. Cela est principalement utile lorsque vous utilisez des contrôleurs de tierces parties et que vous n'avez aucun contrôle sur le nommage de leur classe. \nVous pouvez configurer [[yii\\base\\Application::controllerMap|controller map]] dans la [configuration de l'application](structure-applications.md#application-configurations). Par exemple :\n\n```php\n[\n    'controllerMap' => [\n        // declares \"account\" controller using a class name\n        'account' => 'app\\controllers\\UserController',\n\n        // declares \"article\" controller using a configuration array\n        'article' => [\n            'class' => 'app\\controllers\\PostController',\n            'enableCsrfValidation' => false,\n        ],\n    ],\n]\n```\n\n\n### Contrôleur par défaut <span id=\"default-controller\"></span>\n\nChaque application possède un contrôleur par défaut spécifié via la propriété [[yii\\base\\Application::defaultRoute]]. Lorsqu'une requête ne précise aucune [route](#routes), c'est la route spécifiée par cette propriété qui est utilisée. Pour les [[yii\\web\\Application|applications Web]], sa valeur est `'site'`, tandis que pour les [[yii\\console\\Application|applications de console]], c'est `help`. Par conséquent, si une URL est de la forme `https://hostname/index.php`, c'est le contrôleur `site` qui prend la requête en charge.\n\nVous pouvez changer de contrôleur par défaut en utilisant la  [configuration d'application](structure-applications.md#application-configurations) suivante :\n\n```php\n[\n    'defaultRoute' => 'main',\n]\n```\n\n\n## Création d'actions <span id=\"creating-actions\"></span>\n\nCréer des actions est aussi simple que de définir ce qu'on appelle des *méthodes d'action* dans une classe de contrôleur. Une méthode d'action est une méthode *publique* dont le nom commence par le mot `action`. La valeur retournée par une méthode d'action représente les données de la réponse à envoyer à l'utilisateur final. Le code qui suit définit deux actions, `index` et `hello-world`:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actionIndex()\n    {\n        return $this->render('index');\n    }\n\n    public function actionHelloWorld()\n    {\n        return 'Hello World';\n    }\n}\n```\n\n\n### Identifiants d'action <span id=\"action-ids\"></span>\n\nUne action est souvent conçue pour effectuer une manipulation particulière d'une ressource. Pour cette raison, les identifiants d'action sont habituellement des verbes comme `view` (voir), `update` (mettre à jour), etc.\n\nPar défaut, les identifiants d'action ne doivent contenir rien d'autre que les caractères suivants : les lettres de l'alphabet anglais en bas de casse, les chiffres, le tiret bas et le trait d'union. Vous pouvez utiliser le trait d'union pour séparer les mots. Par exemple : \n`view`, `update2`, et `comment-post` sont des identifiants d'action valides, tandis que `view?` et `Update` ne le sont pas.\n\nVous pouvez créer des actions sous deux formes : les actions en ligne (*inline*) et les actions autonomes (*standalone*). Une action en ligne est définie en tant que méthode dans un contrôleur, alors qu'une action autonome est une classe qui étend la classe [[yii\\base\\Action]] ou une des ses classes filles. La définition d'une action en ligne requiert moins d'efforts et est souvent préférée lorsqu'il n'y a pas d'intention de réutiliser cette action. Par contre, les actions autonomes sont essentiellement créées pour être utilisées dans différents contrôleurs ou pour être redistribuées dans des [extensions](structure-extensions.md).\n\n\n### Actions en ligne <span id=\"inline-actions\"></span>\n\nLes actions en ligne sont les actions qui sont définies en terme de méthodes d'action comme nous l'avons décrit plus haut.\n\nLes noms des méthodes d'action sont dérivés des identifiants d'action selon la procédure suivante :\n\n1. Mettre la première lettre de chaque mot de l'identifiant en capitale.\n2. Supprimer les traits d'union.\n3. Préfixer le tout par le mot `action`.\n\nPar exemple, `index` donne `actionIndex`, et `hello-world` donne `actionHelloWorld`.\n\n> Note: les noms des méthodes d'action sont *sensibles à la casse*. Si vous avez une méthode nommée `ActionIndex`, elle ne sera pas considérée comme étant une méthode d'action et, par conséquent, la requête de l'action `index` aboutira à une exception. Notez également que les méthodes d'action doivent être publiques. Une méthode privée ou protégée ne définit PAS une action en ligne. \n\n\nLes actions en ligne sont les actions les plus communément définies parce qu'elle ne requièrent que peu d'efforts pour leur création. Néanmoins, si vous envisagez de réutiliser la même action en différents endroits, ou si vous voulez redistribuer cette action, vous devriez envisager de la définir en tant qu'*action autonome*.\n\n\n### Actions autonomes <span id=\"standalone-actions\"></span>\n\nLes actions autonomes sont définies comme des classes d'action qui étendent la classe [[yii\\base\\Action]] ou une de ses classes filles.\nPar exemple, dans les versions de Yii, il y a [[yii\\web\\ViewAction]] et [[yii\\web\\ErrorAction]], qui sont toutes les deux des actions autonomes.\n\nPour utiliser une action autonome, vous devez la déclarer dans la *table de mise en correspondance des actions* en redéfinissant les méthodes de la classe [[yii\\base\\Controller::actions()]] dans la classe de votre contrôleur de la manière suivante : \n\n```php\npublic function actions()\n{\n    return [\n        // déclare une action \"error\" en utilisant un nom de classe\n        'error' => 'yii\\web\\ErrorAction',\n\n        // déclare une action  \"view\" action en utilisant un tableau de configuration\n        'view' => [\n            'class' => 'yii\\web\\ViewAction',\n            'viewPrefix' => '',\n        ],\n    ];\n}\n```\n\nComme vous pouvez l'observer, les méthodes `actions()` doivent retourner un tableau dont les clés sont les identifiants d'action et les valeurs le nom de la classe d'action correspondant ou  des tableaux de [configuration](concept-configurations.md). Contrairement aux actions en ligne, les identifiants d'action autonomes peuvent comprendre n'importe quels caractères du moment qu'ils sont déclarés dans la méthode `actions()`.\n\nPour créer une classe d'action autonome, vous devez étendre la classe [[yii\\base\\Action]] ou une de ses classes filles, et implémenter une méthode publique nommée `run()`. Le rôle de la méthode `run()` est similaire à celui d'une méthode d'action. Par exemple :\n\n```php\n<?php\nnamespace app\\components;\n\nuse yii\\base\\Action;\n\nclass HelloWorldAction extends Action\n{\n    public function run()\n    {\n        return \"Hello World\";\n    }\n}\n```\n\n\n### Valeur de retour d'une action <span id=\"action-results\"></span>\n\nLe valeur de retour d'une méthode d'action, ou celle de la méthode `run()` d'une action autonome, représente le résultat de l'action correspondante.\n\nLa valeur de retour peut être un objet [response](runtime-responses.md) qui sera transmis à l'utilisateur final en tant que réponse.\n\n* Pour les [[yii\\web\\Application|applications Web]], la valeur de retour peut également être des données arbitraires qui seront assignées à l'objet [[yii\\web\\Response::data]] et converties ensuite en une chaîne de caractères représentant le corps de la réponse. \n* Pour les [[yii\\console\\Application|applications de console]], la valeur de retour peut aussi être un entier représentant l'[[yii\\console\\Response::exitStatus|état de sortie]] de l'exécution de la commande.\n\nDans les exemples ci-dessus, les valeurs de retour des actions sont toutes des chaînes de caractères qui seront traitées comme le corps de la réponse envoyée à l'utilisateur final. Les exemples qui suivent montrent comment une action peut rediriger le navigateur vers une nouvelle URL en retournant un objet *response* (parce que la méthode [[yii\\web\\Controller::redirect()|redirect()]] retourne un objet *response*) :\n\n```php\npublic function actionForward()\n{\n    // redirect the user browser to https://example.com\n    return $this->redirect('https://example.com');\n}\n```\n\n### Paramètres d'action <span id=\"action-parameters\"></span>\n\nLes méthodes d'action pour les actions en ligne et la méthode `run()` d'une action autonome acceptent des paramètres appelés *paramètres d'action*. Leurs valeurs sont tirées des requêtes. Pour les [[yii\\web\\Application|applications Web]], la valeur de chacun des paramètres d'action est obtenue de la méthode `$_GET` en utilisant le nom du paramètre en tant que clé. Pour les [[yii\\console\\Application|applications de console]], les valeurs des  paramètres correspondent aux argument de la commande. \nDans d'exemple qui suit, l'action `view` (une action en ligne) déclare deux paramètres : `$id` et `$version`.\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass PostController extends Controller\n{\n    public function actionView($id, $version = null)\n    {\n        // ...\n    }\n}\n```\n\nEn fonction de la requête, les paramètres de l'action seront établis comme suit :\n\n* `https://hostname/index.php?r=post/view&id=123`: le paramètre `$id` reçoit la valeur `'123'`,  tandis que le paramètre `$version` reste `null` (sa valeur par défaut) car la requête ne contient aucun paramètre `version`.\n* `https://hostname/index.php?r=post/view&id=123&version=2`: les paramètres `$id` et `$version` reçoivent les valeurs `'123'` et `'2'`, respectivement.\n* `https://hostname/index.php?r=post/view`: une exception [[yii\\web\\BadRequestHttpException]] est levée car le paramètre obligatoire `$id` n'est pas fourni par la requête.\n* `https://hostname/index.php?r=post/view&id[]=123`: une exception [[yii\\web\\BadRequestHttpException]] est levée car le paramètre `$id` reçoit, de manière inattendue,  un tableau (`['123']`).\n\nSi vous voulez que votre paramètre d'action accepte un tableau, il faut, dans la définition de la méthode, faire allusion à son type, avec `array`, comme ceci :\n\n```php\npublic function actionView(array $id, $version = null)\n{\n    // ...\n}\n```\n\nDésormais, si la requête est `https://hostname/index.php?r=post/view&id[]=123`, le paramètre `$id` accepte la valeur `['123']`. Si la requête est  `https://hostname/index.php?r=post/view&id=123`, le paramètre `$id` accepte également la valeur transmise par la requête parce que les valeurs scalaires sont automatiquement convertie en tableau (*array*).\n\nLes exemples qui précèdent montrent essentiellement comment les paramètres d'action fonctionnent dans les applications Web. Pour les applications de console, reportez-vous à la section  [Commandes de console](tutorial-console.md) pour plus de détails.\n\n\n### Action par défaut <span id=\"default-action\"></span>\n\nChaque contrôleur dispose d'une action par défaut spécifiée par la propriété [[yii\\base\\Controller::defaultAction]].\nLorsqu'une [route](#routes) ne contient que l'identifiant du contrôleur, cela implique que l'action par défaut de ce contrôleur est requise. \n\nPar défaut, l'action par défaut est définie comme étant `index`. Si vous désirez changer cette valeur par défaut, contentez-vous de redéfinir cette propriété dans la classe du contrôleur, comme indiqué ci-après :\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public $defaultAction = 'home';\n\n    public function actionHome()\n    {\n        return $this->render('home');\n    }\n}\n```\n\n\n## Cycle de vie d'un contrôleur <span id=\"controller-lifecycle\"></span>\n\nLors du traitement d'une requête, une [application](structure-applications.md) crée un contrôleur en se basant sur la [route](#routes) requise. Le contrôleur entame alors le cycle de vie suivant pour satisfaire la requête :\n\n1. La méthode [[yii\\base\\Controller::init()]] est appelée après que le contrôleur est créé et configuré. \n2. Le contrôleur crée un objet *action* en se basant sur l'identifiant d'action de la requête : \n   * Si l'identifiant de l'action n'est pas spécifié, l'[[yii\\base\\Controller::defaultAction|identifiant de l'action par défaut]] est utilisé.\n   * Si l'identifiant de l'action est trouvé dans la [[yii\\base\\Controller::actions()|table de mise en correspondance des actions]], une action autonome est créée.\n   * Si l'identifiant de l'action est trouvé et qu'il correspond à une méthode d'action, une action en ligne est créée.\n   * Dans les autres cas, une exception [[yii\\base\\InvalidRouteException]] est levée.\n3. Le contrôleur appelle consécutivement la méthode `beforeAction()` de l'application, celle du module (si module si le contrôleur appartient à un module) et celle du contrôleur. \n   * Si l'un des appels retourne `false`, les appels aux  méthodes `beforeAction()` qui devraient suivre ne sont pas effectués et l'exécution de l'action est annulée.\n   * Par défaut, chacun des appels à la méthode `beforeAction()` déclenche un événement  `beforeAction` auquel vous pouvez attacher un gestionnaire d'événement. \n4. Le contrôleur exécute l'action.\n   * Les paramètres de l'action sont analysés et définis à partir des données transmises par la requête.\n5. Le contrôleur appelle successivement la méthode  `afterAction()` du contrôleur, du module (si le contrôleur appartient à un module) et de l'application.\n   * Par défaut, chacun des appels à la méthode `afterAction()` déclenche un événement `afterAction` auquel vous pouvez attacher un gestionnaire d'événement. \n6. L'application assigne le résultat de l'action à l'objet [response](runtime-responses.md).\n\n\n## Meilleures pratiques <span id=\"best-practices\"></span>\n\nDans une application bien conçue, les contrôleurs sont souvent très légers avec des actions qui ne contiennent que peu de code. Si votre contrôleur est plutôt compliqué, cela traduit la nécessité de remanier le code pour en déplacer certaines parties dans d'autres classes. \n\nVoici quelques meilleures pratiques spécifiques. Les contrôleurs :\n* peuvent accéder aux données de la  [requête](runtime-requests.md) ;\n* peuvent appeler les méthodes des [modèles](structure-models.md) et des autres composants de service avec les données de la requête ;\n* peuvent utiliser des [vues](structure-views.md) pour composer leurs réponses ;\n* ne devraient PAS traiter les données de la requête — cela devrait être fait dans la [couche modèle](structure-models.md) ;\n* devraient éviter d'encapsuler du code HTML ou tout autre code relatif à la présentation — cela est plus avantageusement fait dans les [vues](structure-views.md).\n"
  },
  {
    "path": "docs/guide-fr/structure-entry-scripts.md",
    "content": "Scripts d'entrée\n=============\n\nLe script d'entrée est le premier rencontré dans le processus d'amorçage de l'application. Une application (qu'elle\nsoit une application Web ou une application console) a un unique script d'entrée. Les utilisateurs font des \nrequêtes au script d'entrée qui instancie un objet *Application*  et lui transmet les requêtes.\n\nLes scripts d'entrée des applications Web doivent être placés dans des dossiers accessibles par le Web pour que les \nutilisateurs puissent y accéder. Ils sont souvent nommés `index.php`, mais peuvent également avoir tout autre nom,\ndu moment que les serveurs Web peuvent les trouver.\n\nLes scripts d'entrée des applications console sont généralement placés dans le [répertoire de base](structure-applications.md)\ndes applications et sont nommés `yii` (avec le suffixe `.php`). Ils doivent être rendus exécutables afin que les \nutilisateurs puissent lancer des applications console grâce à la commande `./yii <route> [arguments] [options]`.\n\nLes scripts d'entrée effectuent principalement les tâches suivantes :\n\n* Définir des constantes globales;\n* Enregistrer le [chargeur automatique Composer](https://getcomposer.org/doc/01-basic-usage.md#autoloading);\n* Inclure le fichier de classe de [[Yii]];\n* Charger la configuration de l'application;\n* Créer et configurer une instance d'[application](structure-applications.md);\n* Appeler [[yii\\base\\Application::run()]] pour traiter la requête entrante.\n\n\n## Applications Web <span id=\"web-applications\"></span>\n\nCe qui suit est le code du script d'entrée du [Modèle Basique d'Application Web](start-installation.md).\n\n```php\n<?php\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n\n// register Composer autoloader\nrequire __DIR__ . '/../vendor/autoload.php';\n\n// include Yii class file\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n\n// load application configuration\n$config = require __DIR__ . '/../config/web.php';\n\n// create, configure and run application\n(new yii\\web\\Application($config))->run();\n```\n\n\n## Applications Console <span id=\"console-applications\"></span>\n\nDe même, le code qui suit est le code du script de démarrage d'une application console :\n\n```php\n#!/usr/bin/env php\n<?php\n/**\n * Yii console bootstrap file.\n *\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\n\n// register Composer autoloader\nrequire __DIR__ . '/vendor/autoload.php';\n\n// include Yii class file\nrequire __DIR__ . '/vendor/yiisoft/yii2/Yii.php';\n\n// load application configuration\n$config = require __DIR__ . '/config/console.php';\n\n$application = new yii\\console\\Application($config);\n$exitCode = $application->run();\nexit($exitCode);\n```\n\n\n## Définir des Constantes <span id=\"defining-constants\"></span>\n\nLes scripts d'entrée sont l'endroit idéal pour définir des constantes globales. Yii prend en charge les trois constantes suivantes :\n\n* `YII_DEBUG` : spécifie si une application tourne en mode de débogage. Si elle est en mode de débogage, une \n  application enregistrera des journaux plus détaillés, et révélera des piles d'appels d'erreurs détaillées si des exceptions\n  sont levées. C'est pour cette raison que le mode de débogage doit être utilisé principalement pendant la phase\n  de développement. La valeur par défaut de `YII_DEBUG` est `false` (faux).\n* `YII_ENV` : spécifie dans quel environnement l'application est en train de tourner. Cela est décrit plus en détails\n  dans la section [Configurations](concept-configurations.md#environment-constants). La valeur par défaut de `YII_ENV` \n  est `'prod'`, ce qui signifie que l'application tourne dans l'environnement de production.\n* `YII_ENABLE_ERROR_HANDLER` : spécifie si le gestionnaire d'erreurs fourni par Yii doit être activé. La valeur par \n  défaut de cette constante est `true` (vrai).\n\nQuand on définit une constante, on utilise souvent le code suivant :\n\n```php\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\n```\n\nqui est l'équivalent du code suivant :\n\n```php\nif (!defined('YII_DEBUG')) {\n    define('YII_DEBUG', true);\n}\n```\n\nClairement, le premier est plus succinct et plus aisé à comprendre.\n\nLes définitions de constantes doivent être faites au tout début d'un script d'entrée pour qu'elles puissent prendre \neffet quand d'autres fichiers PHP sont inclus.\n"
  },
  {
    "path": "docs/guide-fr/structure-extensions.md",
    "content": "Extensions\n==========\n\nLes extensions sont des paquets logiciels distribuables, spécialement conçus pour être utilisés dans des applications, et qui procurent des fonctionnalités prêtes à l'emploi. Par exemple, l'extension [yiisoft/yii2-debug](https://github.com/yiisoft/yii2-debug) ajoute une barre de débogage très pratique au pied de chaque page dans votre application pour vous aider à comprendre plus aisément comment les pages sont générées. Vous pouvez utiliser des extensions pour accélérer votre processus de développement. Vous pouvez aussi empaqueter votre code sous forme d'extensions pour partager votre travail avec d'autres personnes.\n\n> Info: nous utilisons le terme \"extension\" pour faire référence à des paquets logiciels spécifiques à Yii. Quant aux paquets à but plus général, qui peuvent être utilisés en dehors de Yii, nous y faisons référence en utilisant les termes « paquet » ou « bibliothèque ». \n\n\n## Utilisation des extensions <span id=\"using-extensions\"></span>\n\nPour utiliser une extension, vous devez d'abord l'installer. La plupart des extensions sont distribuées en tant que paquets [Composer](https://getcomposer.org/) qui peuvent être installés en suivant les deux étapes suivantes : \n\n1. Modifier le fichier `composer.json` de votre application et spécifier quelles extensions (paquets Composer) vous désirez installer.\n2. Exécuter la commande `composer install` pour installer les extensions spécifiées.\n\nNotez que devez installer [Composer](https://getcomposer.org/) si vous ne l'avez pas déjà fait.\n\nPar défaut, Composer installe les paquets enregistrés sur [Packagist](https://packagist.org/) — le plus grand dépôt pour les paquets Composer Open Source. Vous pouvez rechercher des extensions sur Packagist. Vous pouvez aussi [créer votre propre dépôt](https://getcomposer.org/doc/05-repositories.md#repository) et configurer Composer pour l'utiliser. Ceci est utile si vous développez des extensions privées que vous ne voulez partager que dans vos propres projets seulement.\n\nLes extensions installées par Composer sont stockées dans le dossier `BasePath/vendor`, où `BasePath` fait référence au [chemin de base](structure-applications.md#basePath) de l'application.  Comme Composer est un gestionnaire de dépendances, quand il installe un paquet, il installe aussi automatiquement tous les paquets dont le paquet dépend. \n\nPar exemple, pour installer l'extension `yiisoft/yii2-imagine`, modifier votre fichier `composer.json` comme indiqué ci-après :\n\n```json\n{\n    // ...\n\n    \"require\": {\n        // ... autres dépendances\n\n        \"yiisoft/yii2-imagine\": \"~2.0.0\"\n    }\n}\n```\n\nAprès l'installation, vous devriez apercevoir le dossier  `yiisoft/yii2-imagine` dans le dossier `BasePath/vendor`. Vous devriez également apercevoir un autre dossier `imagine/imagine` contenant les paquets dont l'extension dépend et qui ont été installés.\n\n> Info: l'extension `yiisoft/yii2-imagine` est une extension du noyau développée et maintenue par l'équipe de développement de Yii. Toutes les extensions du noyau sont hébergées sur [Packagist](https://packagist.org/) et nommées selon le format `yiisoft/yii2-xyz`, où `xyz` varie selon l'extension. \n  \nVous pouvez désormais utiliser les extensions installées comme si elles faisaient partie de votre application. L'exemple suivant montre comment vous pouvez utiliser la classe `yii\\imagine\\Image` que l'extension `yiisoft/yii2-imagine` fournit :\n\n```php\nuse Yii;\nuse yii\\imagine\\Image;\n\n// generate a thumbnail image\nImage::thumbnail('@webroot/img/test-image.jpg', 120, 120)\n    ->save(Yii::getAlias('@runtime/thumb-test-image.jpg'), ['quality' => 50]);\n```\n\n> Info: les classes d'extension sont chargées automatiquement par  la [classe de chargement automatique de Yii (*autoloader*)](concept-autoloading.md).\n\n\n### Installation manuelle d'extensions <span id=\"installing-extensions-manually\"></span>\n\nDans quelques cas rares, vous désirez installer quelques, ou toutes les, extensions manuellement, plutôt que de vous en remettre à Composer. Pour le faire, vous devez :\n\n1. Télécharger les archives des extensions et les décompresser dans le dossier `vendor`.\n2. Installer la classe *autoloader* procurée par les extensions, si elles en possèdent.\n3. Télécharger et installer toutes les extensions dont vos extensions dépendent selon les instructions.\n \nSi une extension ne possède pas de classe *autoloader* mais obéit à la [norme PSR-4](https://www.php-fig.org/psr/psr-4/), vous pouvez utiliser la classe *autoloader* procurée par Yii pour charger automatiquement les classes d'extension. Tout ce que vous avez à faire, c'est de déclarer un [alias racine](concept-aliases.md#defining-aliases) pour le dossier racine de l'extension. Par exemple, en supposant que vous avez installé une extension dans le dossier `vendor/mycompany/myext`, et que les classes d'extension sont sous l'espace de noms `myext`, alors vous pouvez inclure le code suivant dans la configuration de votre application :\n\n```php\n[\n    'aliases' => [\n        '@myext' => '@vendor/mycompany/myext',\n    ],\n]\n```\n\n\n## Création d'extensions <span id=\"creating-extensions\"></span>\n\nVous pouvez envisager de créer une extension lorsque vous ressentez l'envie de partager votre code avec d'autres personnes. Une extension pour contenir n'importe quel code à votre goût, comme une classe d'aide, un objet graphique, un module, etc.\n\nIl est recommandé de créer une extension sous la forme d'un [paquet Composer](https://getcomposer.org/) de façon à ce qu'elle puisse être installée facilement par d'autres utilisateurs, comme nous l'avons expliqué dans la sous-section précédente. \n\nCi-dessous, nous présentons les étapes de base à suivre pour créer une extension en tant que paquet Composer. \n\n1. Créer un projet pour votre extension et l'héberger dans un dépôt VCS, tel que [github.com](https://github.com). Le travail de développement et de maintenance pour cette extension doit être fait sur ce dépôt. \n2. Dans le dossier racine du projet, créez un fichier nommé `composer.json` comme le réclame Composer. Reportez-vous à la sous-section suivante pour plus de détails.\n3. Enregistrez votre extension dans un dépôt Composer tel que [Packagist](https://packagist.org/), afin que les autres utilisateurs puissent la trouver et l'installer avec Composer.\n\n\n### `composer.json` <span id=\"composer-json\"></span>\n\nTout paquet Composer doit disposer d'un fichier `composer.json` dans son dossier racine. Ce fichier contient les méta-données à propos du paquet. Vous pouvez trouver une spécification complète de ce fichier dans le [manuel de Composer](https://getcomposer.org/doc/01-basic-usage.md#composer-json-project-setup).\nL'exemple suivant montre le fichier `composer.json` de l'extension `yiisoft/yii2-imagine` :\n\n```json\n{\n    // package name (nom du paquet)\n    \"name\": \"yiisoft/yii2-imagine\",\n\n    // package type (type du paquet)\n    \"type\": \"yii2-extension\",\n\n    \"description\": \"l'intégration d'Imagine pour le framework Yii \",\n    \"keywords\": [\"yii2\", \"imagine\", \"image\", \"helper\"],\n    \"license\": \"BSD-3-Clause\",\n    \"support\": {\n        \"issues\": \"https://github.com/yiisoft/yii2/issues?labels=ext%3Aimagine\",\n        \"forum\": \"https://forum.yiiframework.com/\",\n        \"wiki\": \"https://www.yiiframework.com/wiki/\",\n        \"irc\": \"ircs://irc.libera.chat:6697/yii\",\n        \"source\": \"https://github.com/yiisoft/yii2\"\n    },\n    \"authors\": [\n        {\n            \"name\": \"Antonio Ramirez\",\n            \"email\": \"amigo.cobos@gmail.com\"\n        }\n    ],\n\n    // dépendances du paquet\n    \"require\": {\n        \"yiisoft/yii2\": \"~2.0.0\",\n        \"imagine/imagine\": \"v0.5.0\"\n    },\n\n    // class autoloading specs\n    \"autoload\": {\n        \"psr-4\": {\n            \"yii\\\\imagine\\\\\": \"\"\n        }\n    }\n}\n```\n\n\n#### Nommage des paquets <span id=\"package-name\"></span>\n\nChaque paquet Composer doit avoir un nom de paquet qui le distingue des autres paquets. Le format d'un nom de paquet est  `vendorName/projectName`. Par exemple, dans le nom de paquet `yiisoft/yii2-imagine`, le nom de vendeur et le nom du projet sont, respectivement, `yiisoft` et`yii2-imagine`.\n\nN'utilisez PAS  `yiisoft` comme nom de vendeur car il est réservé pour le noyau de Yii. \n\nNous recommandons que vous préfixiez votre nom de projet par `yii2-` pour les paquets qui sont des extensions de Yii, par exemple,`myname/yii2-mywidget`. Cela permet aux utilisateurs de distinguer plus facilement les extensions de Yii 2. \n\n#### Types de paquet <span id=\"package-type\"></span>\n\nIl est important de spécifier le type de paquet de votre extension comme `yii2-extension` afin que le paquet puisse être reconnu comme une extension de Yii lors de son installation. \n\nLosqu'un utilisateur exécute `composer install` pour installer une extension, le fichier  `vendor/yiisoft/extensions.php` est automatiquement mis à jour pour inclure les informations sur la nouvelle extension. Grâce à  ce fichier, les application Yii peuvent connaître quelles extensions sont installées (l'information est accessible via [[yii\\base\\Application::extensions]]).\n\n\n#### Dépendances <span id=\"dependencies\"></span>\n\nBien sûr, votre extension dépend de Yii. C'est pourquoi, vous devez lister  (`yiisoft/yii2`) dans l'entrée `require` dans `composer.json`. Si votre extension dépend aussi d'autres extensions ou bibliothèques de tierces parties, vous devez les lister également. Assurez-vous que vous de lister également les contraintes de versions appropriées (p. ex. `1.*`, `@stable`) pour chacun des paquets dont votre extension dépend. Utilisez des dépendances stables lorsque votre extension est publiée dans une version stable. \n\nLa plupart des paquets JavaScript/CSS sont gérés par [Bower](https://bower.io/) et/ou [NPM](https://www.npmjs.com/),\nplutôt que par Composer. Yii utilise le [greffon *assets* de Composer(https://github.com/fxpio/composer-asset-plugin) pour activer la gestion de ce genre de paquets par Composer. Si votre extension dépend d'un paquet Bower, vous pouvez simplement lister la dépendance dans  `composer.json` comme ceci : \n\n```json\n{\n    // paquets dépendances\n    \"require\": {\n        \"bower-asset/jquery\": \">=1.11.*\"\n    }\n}\n```\n\nLe code ci-dessus établit que l'extension dépend de paquet Bower `jquery`. En général, vous pouvez utiliser le nom `bower-asset/PackageName` — où `PackageName` est le nom du paquet — pour faire référence à un paquet Bower dans `composer.json`, et utiliser `npm-asset/PackageName` pour faire référence à un paquet NPM. Quand Composer installe un paquet Bower ou NPM, par défaut, le contenu du paquet est installé dans le dossier `@vendor/bower/PackageName` ou `@vendor/npm/Packages`, respectivement. On peut aussi faire référence à ces dossier en utilisant les alias plus courts  `@bower/PackageName` et `@npm/PackageName`.\n\nPour plus de détails sur la gestion des ressources, reportez-vous à la section sur  les [Ressources](structure-assets.md#bower-npm-assets).\n\n\n#### Chargement automatique des classes <span id=\"class-autoloading\"></span>\n\nAfin que vos classes soient chargées automatiquement par la classe *autoloader* de Yii ou celle de Composer, vous devez spécifier l'entrée  `autoload` dans le fichier `composer.json`, comme précisé ci-après :\n\n```json\n{\n    // ....\n\n    \"autoload\": {\n        \"psr-4\": {\n            \"yii\\\\imagine\\\\\": \"\"\n        }\n    }\n}\n```\n\nVous pouvez lister un ou plusieurs espaces de noms racines et leur chemin de fichier correspondant.\n\nLorsque l'extension est installée dans une application, Yii crée un [alias](concept-aliases.md#extension-aliases) pour chacun des espaces de noms racines. Cet alias fait référence au dossier correspondant à l'espace de noms. Par exemple, la déclaration `autoload` ci-dessus correspond à un alias nommé `@yii/imagine`.\n\n\n### Pratiques recommandées <span id=\"recommended-practices\"></span>\n\nParce que les extensions sont prévues pour être utilisées par d'autres personnes, vous avez souvent besoin de faire un effort supplémentaire pendant le développement. Ci-dessous nous introduisons quelques pratiques courantes et recommandées pour créer des extensions de haute qualité. \n\n\n#### Espaces de noms <span id=\"namespaces\"></span>\n\nPour éviter les collisions de noms et rendre le chargement des classes de votre extension automatique, vous devez utiliser des espaces de noms et nommer les classes de votre extension en respectant la [norme PSR-4](https://www.php-fig.org/psr/psr-4/) ou la [norme PSR-0](https://www.php-fig.org/psr/psr-0/).\n\nVos noms de classe doivent commencer par  `vendorName\\extensionName`, où `extensionName` est similaire au nom du projet dans le nom du paquet sauf qu'il doit contenir le préfixe `yii2-`. Par exemple, pour l'extension `yiisoft/yii2-imagine`, nous utilisons l'espace de noms `yii\\imagine` pour ses classes. \n\nN'utilisez pas `yii`, `yii2` ou `yiisoft` en tant que nom de vendeur. Ces noms sont réservés au code du noyau de Yii.\n\n\n#### Classes d'amorçage <span id=\"bootstrapping-classes\"></span>\n\nParfois, vous désirez que votre extension exécute un certain code durant le [processus d'amorçage](runtime-bootstrapping.md) d'une application. Par exemple, votre extension peut vouloir répondre à l'événement `beginRequest` pour ajuster quelques réglages d'environnement. Bien que vous puissiez donner des instructions aux utilisateurs de l'extension pour qu'ils attachent explicitement votre gestionnaire d'événement dans l'extension à l'événement `beginRequest`, c'est mieux de le faire automatiquement.\n\nPour ce faire, vous pouvez créer une classe dite *classe du processus d'amorçage* en implémentant l'interface [[yii\\base\\BootstrapInterface]].\nPar exemple :\n\n```php\nnamespace myname\\mywidget;\n\nuse yii\\base\\BootstrapInterface;\nuse yii\\base\\Application;\n\nclass MyBootstrapClass implements BootstrapInterface\n{\n    public function bootstrap($app)\n    {\n        $app->on(Application::EVENT_BEFORE_REQUEST, function () {\n             // do something here\n        });\n    }\n}\n```\nensuite, listez cette classe dans le fichier `composer.json` de votre extension de cette manière :\n\n```json\n{\n    // ...\n\n    \"extra\": {\n        \"bootstrap\": \"myname\\\\mywidget\\\\MyBootstrapClass\"\n    }\n}\n```\n\nLorsque l'extension est installée dans l'application, Yii instancie automatiquement la classe d'amorçage et appelle sa méthode [[yii\\base\\BootstrapInterface::bootstrap()|bootstrap()]] durant le processus de démarrage pour chacune des requêtes. \n\n\n#### Travail avec des bases de données <span id=\"working-with-databases\"></span>\n\nVotre extension peut avoir besoin d'accéder à des bases de données. Ne partez pas du principe que les applications qui utilisent votre extension utilisent toujours `Yii::$db` en tant que connexion à la base de données. Déclarez plutôt une propriété `db` pour les classes qui requièrent un accès à une base de données. Cette propriété permettra aux utilisateurs de votre extension de personnaliser la connexion qu'ils souhaitent que votre extension utilise. Pour un exemple, reportez-vous à la classe [[yii\\caching\\DbCache]] et voyez comment elle déclare et utilise la propriété`db`.\n\nSi votre extension a besoin de créer des tables de base de données spécifiques, ou de faire des changements dans le schéma de la base de données, vous devez :\n\n- fournir des [migrations](db-migrations.md) pour manipuler le schéma de base de données, plutôt que d'utiliser des fichiers SQL ;\n- essayer de rendre les migrations applicables à différents systèmes de gestion de bases de données ; \n- éviter d'utiliser  [Active Record](db-active-record.md) dans les migrations.\n\n\n#### Utilisation des ressources <span id=\"using-assets\"></span>\n\nSi votre extension est un objet graphique ou un module, il est probable qu'elle ait besoin de quelques [ressources](structure-assets.md) pour fonctionner. Par exemple, un module peut afficher quelques pages qui contiennent des images, du code JavaScript et/ou CSS. Comme les fichiers d'une extension sont tous dans le même dossier, qui n'est pas accessible depuis le Web lorsque l'extension est installée dans une application, vous avez deux possibilités pour rendre ces ressources accessibles depuis le Web. \n\n- demander aux utilisateurs de l'extension de copier les ressources manuellement dans un dossier spécifique accessible depuis le Web ; \n- déclarer un [paquet de ressources](structure-assets.md) et compter sur le mécanisme de publication automatique des ressources pour copier les fichiers listés dans le paquet de ressources dans un dossier accessible depuis le Web. \n\nNous recommandons la deuxième approche de façon à ce que votre extension puisse être plus facilement utilisée par d'autres personnes. Reportez-vous à la section [Ressources](structure-assets.md) pour plus de détails sur la manière de travailler avec des ressources en général. \n\n\n#### Internationalisation et Localisation <span id=\"i18n-l10n\"></span>\n\nVotre extension peut être utilisée par des applications prenant en charge différentes langues ! Par conséquent, si votre extension affiche des contenus pour l'utilisateur final, vous devez essayer de traiter à la fois [internationalisation et localisation](tutorial-i18n.md). Plus spécialement :\n\n- Si l'extension affiche des messages pour l'utilisateur final, les messages doivent être enveloppés dans la méthode `Yii::t()` afin de pouvoir être traduits. Les messages à l'attention des développeurs (comme les messages d'exceptions internes) n'ont pas besoin d'être traduits. \n-Si l'extension affiche des nombres, des dates, etc., ils doivent être formatés en utilisant [[yii\\i18n\\Formatter]] avec les règles de formatage appropriées. \n\nPour plus de détails, reportez-vous à la section [Internationalisation](tutorial-i18n.md).\n\n\n#### Tests <span id=\"testing\"></span>\n\nVous souhaitez que votre extension s'exécute sans créer de problème à ses utilisateurs. Pour atteindre ce but vous devez la tester avant de la publier. \n\nIl est recommandé que créiez des cas de test variés pour tester votre extension plutôt que de vous fier à des tests manuels. À chaque fois que vous vous apprêterez à publier une nouvelle version de votre extension, vous n'aurez plus qu'à exécuter ces cas de test pour garantir que tout est en ordre. Yii fournit une prise en charge des tests qui peut vous aider à écrire facilement des unités de test, des tests d'acceptation et des tests de fonctionnalités. Pour plus de détails, reportez-vous à la section [Tests](test-overview.md).\n\n\n#### Numérotation des versions <span id=\"versioning\"></span>\n\nVous devriez donner à chacune des versions publiées de votre extension un numéro (p. ex. `1.0.1`). Nous recommandons de suivre la pratique de la [numérotation sémantique des versions](https://semver.org) lors de la détermination d'un numéro de version. \n\n\n#### Publication <span id=\"releasing\"></span>\n\nPour permettre aux autres personnes de connaître votre extension, vous devez la publier. Si c'est la première fois que vous publiez l'extension, vous devez l'enregistrer sur un dépôt Composer tel que [Packagist](https://packagist.org/). Ensuite, tout ce que vous avez à faire, c'est de créer une balise de version (p. ex. `v1.0.1`) sur le dépôt VCS de votre extension et de notifier au dépôt Composer la nouvelle version. Les gens seront capables de trouver votre nouvelle version et, soit de l'installer, soit de la mettre à jour via le dépôt Composer. \n\nDans les versions de votre extension, en plus des fichiers de code, vous devez envisager d'inclure ce qui suit par aider les gens à connaître votre extension et à l'utiliser :\n* Un ficher *readme* (lisez-moi) dans le dossier racine du paquet : il doit décrire ce que fait votre extension, comment l'installer et l'utiliser. Nous vous recommandons de l'écrire dans le format [Markdown](https://daringfireball.net/projects/markdown/) et de nommer ce fichier `readme.md`.\n* Un fichier *changelog* (journal des modifications) dans le dossier racine du paquet : il liste les changements apportés dans chacune des versions. Ce fichier peut être écrit dans le format Markdown et nommé `changelog.md`.\n* Un fichier *upgrade* (mise à jour) dans le dossier racine du paquet : il donne les instructions sur la manière de mettre l'extension à jour en partant d'une version précédente.   Ce fichier peut être écrit dans le format Markdown et nommé `upgrade.md`.\n* Tutorials, demos, screenshots, etc.: ces derniers sont nécessaires si votre extension fournit de nombreuses fonctionnalités qui ne peuvent être couvertes dans le fichier readme. \n* Une documentation de l'API : votre code doit être bien documenté pour permettre aux autres personnes de le lire plus facilement et de le comprendre. Vous pouvez faire référence au [fichier de la classe BaseObject](https://github.com/yiisoft/yii2/blob/master/framework/base/BaseObject.php) pour savoir comment documenter votre code. \n \n> Info: les commentaires de votre code peuvent être écrits dans le format Markdown. L'extension `yiisoft/yii2-apidoc` vous fournit un outil pour générer une documentation d'API agréable et basée sur les commentaires de votre code. \n\n> Info: bien que cela ne soit pas une exigence, nous suggérons que votre extension respecte un certain style de codage. Vous pouvez vous reporter au document [style du codage du noyau du framework](https://github.com/yiisoft/yii2/blob/master/docs/internals/core-code-style.md).\n\n\n## Extensions du noyau <span id=\"core-extensions\"></span>\n\nYii fournit les extensions du noyau suivantes (ou [\"les extensions officielles\"](https://www.yiiframework.com/extensions/official)) qui sont développées et maintenues par l'équipe de développement de Yii. Elles sont toutes enregistrées sur[Packagist](https://packagist.org/) et peuvent être facilement installées comme décrit dans la sous-section [Utilisation des extensions](#using-extensions).\n\n- [yiisoft/yii2-apidoc](https://www.yiiframework.com/extension/yiisoft/yii2-apidoc) : fournit un générateur  d'API extensible et de haute performance. Elle est aussi utilisée pour générer l'API du noyau du framework.  \n- [yiisoft/yii2-authclient](https://www.yiiframework.com/extension/yiisoft/yii2-authclient) : fournit un jeu de clients d'authentification courants tels que  Facebook OAuth2 client, GitHub OAuth2 client.\n- [yiisoft/yii2-bootstrap](https://www.yiiframework.com/extension/yiisoft/yii2-bootstrap) : fournit un jeu d'objets graphiques qui encapsulent les composants et les greffons de [Bootstrap](https://getbootstrap.com/).\n- [yiisoft/yii2-debug](https://www.yiiframework.com/extension/yiisoft/yii2-debug) : fournit la prise en charge du débogage des applications Yii. Lorsque cette extension est utilisée, une barre de débogage apparaît au pied de chacune des pages. Cette extension fournit aussi un jeu de pages autonomes pour afficher des informations de débogage plus détaillées. \n- [yiisoft/yii2-elasticsearch](https://www.yiiframework.com/extension/yiisoft/yii2-elasticsearch) : fournit la prise en charge d'[Elasticsearch](https://www.elastic.co/). Elle inclut un moteur de requêtes/recherches de base et met en œuvre le motif [Active Record](db-active-record.md) qui permet de stocker des enregistrement actifs dans Elasticsearch.\n- [yiisoft/yii2-faker](https://www.yiiframework.com/extension/yiisoft/yii2-faker) : fournit la prise en charge de [Faker](https://www.yiiframework.com/extension/fzaninotto/Faker) pour générer des données factices pour vous.\n- [yiisoft/yii2-gii](https://www.yiiframework.com/extension/yiisoft/yii2-gii) : fournit un générateur de code basé sur le Web qui est hautement extensible et peut être utilisé pour générer rapidement des modèles, des formulaires, des modules, des requêtes CRUD, etc. \n- [yiisoft/yii2-httpclient](https://www.yiiframework.com/extension/yiisoft/yii2-httpclient) : provides an HTTP client.\n- [yiisoft/yii2-imagine](https://github.com/yiisoft/yii2-imagine) : fournit des fonctionnalités couramment utilisées de manipulation d'images basées sur [Imagine](https://www.yiiframework.com/extension/yiisoft/yii2-imagine).\n- [yiisoft/yii2-jui](https://www.yiiframework.com/extension/yiisoft/yii2-jui) : fournit un jeu d'objets graphiques qui encapsulent les interactions et les objets graphiques de [JQuery UI](https://jqueryui.com/).\n- [yiisoft/yii2-mongodb](https://www.yiiframework.com/extension/yiisoft/yii2-mongodb) : fournit la prise en charge de [MongoDB](https://www.mongodb.com/). Elle inclut des fonctionnalités telles que les requêtes de base, les enregistrements actifs, les migrations, la mise en cache, la génération de code, etc.\n- [yiisoft/yii2-queue](https://www.yiiframework.com/extension/yiisoft/yii2-queue): fournit la prise en charge pour exécuter des tâches en asynchrone via des queues. Il prend en charge les queues en se basant sur, DB, Redis, RabbitMQ, AMQP, Beanstalk et Gearman.\n- [yiisoft/yii2-redis](https://github.com/yiisoft/yii2-redis) : fournit la prise en charge de [redis](https://redis.io/). Elle inclut des fonctionnalités telles que les requêtes de base, les enregistrements actifs, la mise en cache, etc.\n- [yiisoft/yii2-shell](https://www.yiiframework.com/extension/yiisoft/yii2-shell): fournit un interprète de commandes (shell) basé sur [psysh](https://psysh.org/).\n- [yiisoft/yii2-smarty](https://www.yiiframework.com/extension/yiisoft/yii2-smarty) : fournit un moteur de modèles basé sur  [Smarty](https://www.smarty.net/).\n- [yiisoft/yii2-sphinx](https://github.com/yiisoft/yii2-sphinx) : fournit la prise en charge de [Sphinx](https://www.yiiframework.com/extension/yiisoft/yii2-sphinx). Elle inclut des fonctionnalités telles que les requêtes de base, les enregistrements actifs, la génération de code, etc.\n- [yiisoft/yii2-swiftmailer](https://www.yiiframework.com/extension/yiisoft/yii2-swiftmailer) : fournit les fonctionnalités d'envoi de courriels basées sur [swiftmailer](https://swiftmailer.symfony.com/).\n- [yiisoft/yii2-twig](https://www.yiiframework.com/extension/yiisoft/yii2-twig) : fournit un moteur de modèles basé sur [Twig](https://twig.symfony.com/).\n\nLes extensions officielles suivantes sont valables pour les versions Yii 2.1 et plus récentes. Vous n'avez pas besoin de les installer car elles sont incluse dans le cœur du framework.\n\n- [yiisoft/yii2-captcha](https://www.yiiframework.com/extension/yiisoft/yii2-captcha):\n  fournit un CAPTCHA.\n- [yiisoft/yii2-jquery](https://www.yiiframework.com/extension/yiisoft/yii2-jquery):\n  fournit une prise en charge de [jQuery](https://jquery.com/).\n- [yiisoft/yii2-maskedinput](https://www.yiiframework.com/extension/yiisoft/yii2-maskedinput):\n  fournit un composant graphique de saisie masqué basé sur [jQuery Input Mask plugin](https://robinherbots.github.io/Inputmask/).\n- [yiisoft/yii2-mssql](https://www.yiiframework.com/extension/yiisoft/yii2-mssql):\n  fournit la prise en charge de [MSSQL](https://www.microsoft.com/sql-server/).\n- [yiisoft/yii2-oracle](https://www.yiiframework.com/extension/yiisoft/yii2-oracle):\n  fournit la prise en charge de [Oracle](https://www.oracle.com/).\n- [yiisoft/yii2-rest](https://www.yiiframework.com/extension/yiisoft/yii2-rest):\n  fournit le support pour l'API REST.\n"
  },
  {
    "path": "docs/guide-fr/structure-filters.md",
    "content": "Filtres\n=======\n\nLes filtres sont des objets qui sont exécutés avant et/ou après les [actions de contrôleurs](structure-controllers.md#actions). Par exemple, un filtre de contrôle d'accès peut être exécuté avant les actions pour garantir qu'un utilisateur final particulier est autorisé à y accéder. Un filtre de compression de contenu peut être exécuté après les actions pour compresser la réponse avant de l'envoyer à l'utilisateur final. \n\nUn filtre peut être constitué d''un pré-filtre (logique de filtrage appliquée *avant* les actions) et/ou un post-filtre (logique appliquée *après* les actions). \n\n\n## Utilisation des filtres <span id=\"using-filters\"></span>\n\nPour l'essentiel, les filtres sont des sortes de [comportements](concept-behaviors.md). Par conséquent, leur utilisation est identique à l' [utilisation des comportements](concept-behaviors.md#attaching-behaviors). Vous pouvez déclarer des filtres dans une classe de contrôleur en redéfinissant sa méthode [[yii\\base\\Controller::behaviors()|behaviors()]] de la manière suivante :\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\HttpCache',\n            'only' => ['index', 'view'],\n            'lastModified' => function ($action, $params) {\n                $q = new \\yii\\db\\Query();\n                return $q->from('user')->max('updated_at');\n            },\n        ],\n    ];\n}\n```\n\nPar défaut, les filtres déclarés dans une classe de contrôleur sont appliqués à *toutes* les action de ce contrôleur. Vous pouvez cependant, spécifier explicitement à quelles actions ils s'appliquent en configurant la propriété [[yii\\base\\ActionFilter::only|only]]. Dans l'exemple précédent, le filtre `HttpCache` s'applique uniquement aux actions `index` et `view`. Vous pouvez également configurer la propriété [[yii\\base\\ActionFilter::except|except]] pour empêcher quelques actions d'être filtrées. \n\nEn plus des contrôleurs, vous pouvez également déclarer des filtres dans un [module](structure-modules.md) ou dans une [application](structure-applications.md). Lorsque vous faites cela, les filtres s'appliquent à *toutes* les actions de contrôleur qui appartiennent à ce module ou à cette application, sauf si vous configurez les propriétés des filtres [[yii\\base\\ActionFilter::only|only]] et [[yii\\base\\ActionFilter::except|except]] comme expliqué précédemment. \n\n> Note: lorsque vous déclarez des filtres dans des modules ou des applications, vous devriez utiliser des [routes](structure-controllers.md#routes) plutôt que des identifiants d'action dans les propriétés [[yii\\base\\ActionFilter::only|only]] et [[yii\\base\\ActionFilter::except|except]]. Cela tient au fait qu'un identifiant d'action seul ne peut pas pleinement spécifier une  action dans le cadre d'un module ou d'une application. \n\nLorsque plusieurs filtres sont configurés pour une même action, ils sont appliqués en respectant les règles et l'ordre qui suivent :\n\n* Pré-filtrage\n    - Les filtres déclarés dans l'application sont appliqués dans l'ordre dans lequel ils sont listés dans la méthode `behaviors()`.\n    - Les filtres déclarés dans le module sont appliqués dans l'ordre dans lequel ils sont listés dans la méthode `behaviors()`.\n    - Les filtres déclarés dans le contrôleur sont appliqués dans l'ordre dans lequel ils sont listés dans la méthode `behaviors()`.\n    - Si l'un quelconque des filtres annule l'exécution de l'action, les filtres subséquents (à la fois de pré-filtrage et de post-fitrage) ne sont pas appliqués.\n* L'action est exécutée si les filtres de pré-filtrage réussissent.\n* Post-filtrage\n    - Les filtres déclarés dans le contrôleur sont appliqués dans l'ordre dans lequel ils sont listés dans la méthode `behaviors()`.\n    - Les filtres déclarés dans le module sont appliqués dans l'ordre dans lequel ils sont listés dans la méthode `behaviors()`.\n    - Les filtres déclarés dans l'application sont appliqués dans l'ordre dans lequel ils sont listés dans la méthode `behaviors()`.\n\n\n## Création de filtres <span id=\"creating-filters\"></span>\n\nPour créer un filtre d'action, vous devez étendre la classe [[yii\\base\\ActionFilter]] et redéfinir la méthode [[yii\\base\\ActionFilter::beforeAction()|beforeAction()]] et/ou la méthode [[yii\\base\\ActionFilter::afterAction()|afterAction()]]. La première est exécutée avant l'exécution de l'action, tandis que la seconde est exécutée après l'exécution de l'action. Le valeur de retour de la méthode [[yii\\base\\ActionFilter::beforeAction()|beforeAction()]] détermine si une action doit être exécutée ou pas. Si c'est `false` (faux), les filtres qui suivent sont ignorés et l'action n'est pas exécutée. \n\n\nL'exemple qui suit montre un filtre qui enregistre dans un journal le temps d'exécution de l'action :\n\n```php\nnamespace app\\components;\n\nuse Yii;\nuse yii\\base\\ActionFilter;\n\nclass ActionTimeFilter extends ActionFilter\n{\n    private $_startTime;\n\n    public function beforeAction($action)\n    {\n        $this->_startTime = microtime(true);\n        return parent::beforeAction($action);\n    }\n\n    public function afterAction($action, $result)\n    {\n        $time = microtime(true) - $this->_startTime;\n        Yii::debug(\"Action '{$action->uniqueId}' spent $time second.\");\n        return parent::afterAction($action, $result);\n    }\n}\n```\n\n\n## Filtres du noyau <span id=\"core-filters\"></span>\n\nYii fournit un jeu de filtres couramment utilisés, que l'on trouve en premier lieu dans l'espace de noms `yii\\filters`. Dans ce qui suit, nous introduisons brièvement ces filtres. \n\n### [[yii\\filters\\AccessControl|AccessControl]] <span id=\"access-control\"></span>\n\n*AccessControl* (contrôle d'accès) fournit un contrôle d'accès simple basé sur un jeu de [[yii\\filters\\AccessControl::rules|règles]]. En particulier, avant qu'une action ne soit exécutée, *AccessControl* examine les règles listées et trouve la première qui  correspond aux variables du contexte courant (comme l'adresse IP, l'état de connexion de l'utilisateur, etc.). La règle qui correspond détermine si l'exécution de l'action requise doit être autorisée ou refusée. Si aucune des règles ne correspond, l'accès est refusé. \n\nL'exemple suivant montre comment autoriser les utilisateurs authentifiés à accéder aux actions `create` et `update` tout en refusant l'accès à ces actions aux autres utilisateurs.\n\n```php\nuse yii\\filters\\AccessControl;\n\npublic function behaviors()\n{\n    return [\n        'access' => [\n            'class' => AccessControl::class,\n            'only' => ['create', 'update'],\n            'rules' => [\n                // autoriser les utilisateurs authentifiés\n                [\n                    'allow' => true,\n                    'roles' => ['@'],\n                ],\n                // tout autre chose est interdite d'accès par défaut\n            ],\n        ],\n    ];\n}\n```\n\nPour plus de détails sur le contrôle d'accès en général, reportez-vous à la section [Authorization](security-authorization.md).\n\n\n### Filtres de méthodes d'authentification <span id=\"auth-method-filters\"></span>\n\nLes filtres de méthodes d'authentification sont utilisés pour authentifier un utilisateur qui utilise des méthodes d'authentification variées comme\n[HTTP Basic Auth](https://en.wikipedia.org/wiki/Basic_access_authentication) ou [OAuth 2](https://oauth.net/2/). Les classes de filtre sont dans l'espace de noms `yii\\filters\\auth`.\n\nL'exemple qui suit montre comment vous pouvez utiliser [[yii\\filters\\auth\\HttpBasicAuth]] pour authentifier un utilisateur qui utilise un jeton d'accès basé sur la méthode  *HTTP Basic Auth*. Notez qu'afin que cela fonctionne, votre [[yii\\web\\User::identityClass|classe *identity* de l'utilisateur]] doit implémenter l'interface [[yii\\web\\IdentityInterface::findIdentityByAccessToken()|findIdentityByAccessToken()]].\n\n```php\nuse yii\\filters\\auth\\HttpBasicAuth;\n\npublic function behaviors()\n{\n    return [\n        'basicAuth' => [\n            'class' => HttpBasicAuth::class,\n        ],\n    ];\n}\n```\n\nLes filtres de méthode d'authentification sont communément utilisés dans la mise en œuvre des API pleinement REST. Pour plus de détails, reportez-vous à la section [Authentification REST](rest-authentication.md).\n\n\n### [[yii\\filters\\ContentNegotiator|ContentNegotiator]] <span id=\"content-negotiator\"></span>\n\n*ContentNegotiator* (négociateur de contenu) prend en charge la négociation des formats de réponse et la négociation de langue d'application. Il essaye de déterminer le format de la réponse et/ou la langue en examinant les paramètres de la méthode `GET` et ceux de l'entête HTTP `Accept`.\n\nDans l'exemple qui suit, le filtre *ContentNegotiator* est configuré pour prendre en charge JSON et XML en tant que formats de réponse, et anglais (États-Unis) et allemand en tant que langues. \n\n```php\nuse yii\\filters\\ContentNegotiator;\nuse yii\\web\\Response;\n\npublic function behaviors()\n{\n    return [\n        [\n            'class' => ContentNegotiator::class,\n            'formats' => [\n                'application/json' => Response::FORMAT_JSON,\n                'application/xml' => Response::FORMAT_XML,\n            ],\n            'languages' => [\n                'en-US',\n                'de',\n            ],\n        ],\n    ];\n}\n```\n\nLes formats de réponse et les langues nécessitent souvent d'être déterminés bien plus tôt durant le [cycle de vie de l'application](structure-applications.md#application-lifecycle). Pour cette raison, *ContentNegotiator* est conçu de manière à être également utilisé en tant que [composant du processus d'amorçage](structure-applications.md#bootstrap). Par exemple, vous pouvez le configurer dans la [configuration de l'application](structure-applications.md#application-configurations) de la manière suivante :\n\n```php\nuse yii\\filters\\ContentNegotiator;\nuse yii\\web\\Response;\n\n[\n    'bootstrap' => [\n        [\n            'class' => ContentNegotiator::class,\n            'formats' => [\n                'application/json' => Response::FORMAT_JSON,\n                'application/xml' => Response::FORMAT_XML,\n            ],\n            'languages' => [\n                'en-US',\n                'de',\n            ],\n        ],\n    ],\n];\n```\n\n> Info: dans le cas où le type de contenu et la langue préférés ne peuvent être déterminés à partir de la requête, le premier format et la première langue listés dans [[formats]] et[[languages]], respectivement, sont utilisés.\n\n\n\n### [[yii\\filters\\HttpCache|HttpCache]] <span id=\"http-cache\"></span>\n\n*HttpCache* met en œuvre  la mise en cache côté client en utilisant les entêtes HTTP `Last-Modified` (dernier modifié) et `Etag`.\nPar exemple :\n\n```php\nuse yii\\filters\\HttpCache;\n\npublic function behaviors()\n{\n    return [\n        [\n            'class' => HttpCache::class,\n            'only' => ['index'],\n            'lastModified' => function ($action, $params) {\n                $q = new \\yii\\db\\Query();\n                return $q->from('user')->max('updated_at');\n            },\n        ],\n    ];\n}\n```\n\nReportez-vous à  la section [Mise en cache HTTP](caching-http.md) pour plus de détails sur l'utilisation de  *HttpCache*.\n\n\n### [[yii\\filters\\PageCache|PageCache]] <span id=\"page-cache\"></span>\n\n*PageCache* met en œuvre la mise en cache de pages entières côté serveur. Dans l'exemple qui suit, *PageCache0 est appliqué à l'action `index` pour mettre la page entière en cache pendant un maximum de 60 secondes ou jusqu'à un changement du nombre d'entrées dans la table `post`. Il stocke également différentes versions de la page en fonction de la langue choisie. \n\n```php\nuse yii\\filters\\PageCache;\nuse yii\\caching\\DbDependency;\n\npublic function behaviors()\n{\n    return [\n        'pageCache' => [\n            'class' => PageCache::class,\n            'only' => ['index'],\n            'duration' => 60,\n            'dependency' => [\n                'class' => DbDependency::class,\n                'sql' => 'SELECT COUNT(*) FROM post',\n            ],\n            'variations' => [\n                \\Yii::$app->language,\n            ]\n        ],\n    ];\n}\n```\n\nReportez-vous à la section [Page Caching](caching-page.md) pour plus de détails sur l'utilisation de  *PageCache*.\n\n\n### [[yii\\filters\\RateLimiter|RateLimiter]] <span id=\"rate-limiter\"></span>\n\n*RateLimiter* met en œuvre un algorithme de limitation de débit basé sur  l'[algorithme leaky bucket](https://en.wikipedia.org/wiki/Leaky_bucket). On l'utilise en premier lieu dans la mise en œuvre des API pleinement REST. Reportez-vous à la section [limitation de débit](rest-rate-limiting.md) pour plus de détails sur l'utilisation de ce filtre.\n\n\n### [[yii\\filters\\VerbFilter|VerbFilter]] <span id=\"verb-filter\"></span>\n\n*VerbFilter* vérifie si les méthodes de requête HTTP sont autorisées par l'action requise. Si ce n'est pas le cas, une exception HTTP 405 est levée. Dans l'exemple suivant, *VerbFilter* est déclaré pour spécifier un jeu typique de méthodes de requête pour des actions CRUD — Create (créer), Read (lire), Update (mettre à jour), DELETE (supprimer).\n\n```php\nuse yii\\filters\\VerbFilter;\n\npublic function behaviors()\n{\n    return [\n        'verbs' => [\n            'class' => VerbFilter::class,\n            'actions' => [\n                'index'  => ['get'],\n                'view'   => ['get'],\n                'create' => ['get', 'post'],\n                'update' => ['get', 'put', 'post'],\n                'delete' => ['post', 'delete'],\n            ],\n        ],\n    ];\n}\n```\n\n### [[yii\\filters\\Cors|Cors]] <span id=\"cors\"></span>\n\n*Cross-origin resource sharing* [CORS](https://developer.mozilla.org/fr/docs/Web/HTTP/CORS) est un mécanisme qui permet à des ressource (e.g. fonts, JavaScript, etc.) d'être requises d'un autre domaine en dehors du domaine dont la ressource est originaire. En particulier, les appels AJAX de Javascript peuvent utiliser le mécanisme *XMLHttpRequest*. Autrement, de telles requêtes \"cross-domain\" (inter domaines) seraient interdites par les navigateurs, à cause de la politique de sécurité dite d'origine identique (*same origin*). *CORS* définit une manière par laquelle le navigateur et le serveur interagissent pour déterminer si, oui ou non, la requête *cross-origin* (inter-site) est autorisée. \n\nLe [[yii\\filters\\Cors|filtre Cors]] doit être défini avant les filtres d'authentification et/ou d'autorisation pour garantir que les entêtes CORS sont toujours envoyés.\n\n```php\nuse yii\\filters\\Cors;\nuse yii\\helpers\\ArrayHelper;\n\npublic function behaviors()\n{\n    return ArrayHelper::merge([\n        [\n            'class' => Cors::class,\n        ],\n    ], parent::behaviors());\n}\n```\n\nConsultez également la section sur les [contrôleurs REST](rest-controllers.md#cors) si vous voulez ajouter le filtre CORS à une classe \n[[yii\\rest\\ActiveController]] dans votre API.\n\nLes filtrages Cors peuvent être peaufinés via la propriété [[yii\\filters\\Cors::$cors|$cors]].\n\n* `cors['Origin']`: un tableau utilisé pour définir les origines autorisées. Peut être `['*']` (tout le monde) ou `['https://www.myserver.net', 'https://www.myotherserver.com']`. Valeur par défaut  `['*']`.\n* `cors['Access-Control-Request-Method']`: un tableau des verbes autorisés tel que `['GET', 'OPTIONS', 'HEAD']`.  Valeur par défaut `['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']`.\n* `cors['Access-Control-Request-Headers']`: un tableau des entêtes autorisés. Peut être`['*']` tous les entêtes ou certains spécifiquement `['X-Request-With']`. Valeur par défaut `['*']`.\n* `cors['Access-Control-Allow-Credentials']`: définit si la requête courante peut être faite en utilisant des identifiants de connexion.  Peut être `true` (vrai), `false` (faux) ou  `null` (non défini). Valeur par défaut `null`.\n* `cors['Access-Control-Max-Age']`: définit la durée de vie des requêtes de pré-vérification (*preflight requests*). Valeur par défaut `86400`.\n\nPar exemple, autoriser  CORS pour l'origine  `https://www.myserver.net` avec les méthodes `GET`, `HEAD` et `OPTIONS` :\n\n```php\nuse yii\\filters\\Cors;\nuse yii\\helpers\\ArrayHelper;\n\npublic function behaviors()\n{\n    return ArrayHelper::merge([\n        [\n            'class' => Cors::class,\n            'cors' => [\n                'Origin' => ['https://www.myserver.net'],\n                'Access-Control-Request-Method' => ['GET', 'HEAD', 'OPTIONS'],\n            ],\n        ],\n    ], parent::behaviors());\n}\n```\n\nVous pouvez peaufiner les entêtes CORS en redéfinissant les paramètres par défaut action par action. Par exemple, ajouter les `Access-Control-Allow-Credentials`  (autoriser les identifiants de contrôle d'accès) pour l'action`login` pourrait être réalisé comme ceci :\n\n```php\nuse yii\\filters\\Cors;\nuse yii\\helpers\\ArrayHelper;\n\npublic function behaviors()\n{\n    return ArrayHelper::merge([\n        [\n            'class' => Cors::class,\n            'cors' => [\n                'Origin' => ['https://www.myserver.net'],\n                'Access-Control-Request-Method' => ['GET', 'HEAD', 'OPTIONS'],\n            ],\n            'actions' => [\n                'login' => [\n                    'Access-Control-Allow-Credentials' => true,\n                ]\n            ]\n        ],\n    ], parent::behaviors());\n}\n```\n"
  },
  {
    "path": "docs/guide-fr/structure-models.md",
    "content": "Modèles\n=======\n\nLes modèles font partie du modèle d'architecture [MVC](https://fr.wikipedia.org/wiki/Mod%C3%A8le-vue-contr%C3%B4leur) (Modèle Vue Contrôleur).\nCes objets représentent les données à traiter, les règles et la logique de traitement. \n\nVous pouvez créer des classes de modèle en étendant la classe [[yii\\base\\Model]] ou ses classe filles. La classe de base [[yii\\base\\Model]] prend en charge des fonctionnalités nombreuses et utiles :\n\n* Les [attributs](#attributes) : ils représentent les données à traiter et peuvent être accédés comme des propriétés habituelles d'objets ou des éléments de tableaux. \n* Les étiquettes d'[attribut](#attribute-labels) : elles spécifient les étiquettes pour l'affichage des attributs.\n* L'[assignation massive](#massive-assignment) : elle permet l'assignation de multiples attributs en une seule étape.\n* Les [règles de validation](#validation-rules) : elles garantissent la validité des données saisies en s'appuyant sur des règles de validation déclarées. \n* L'[exportation des données](#data-exporting) : elle permet au modèle de données d'être exporté sous forme de tableaux dans des formats personnalisables.\n\nLa classe `Model` est également la classe de base pour des modèles plus évolués, comme la classe class [Active Record (enregistrement actif)](db-active-record.md). Reportez-vous à la documentation ad hoc pour plus de détails sur ces modèles évolués.\n\n> Info: vous n'êtes pas forcé de baser vos classes de modèle sur la classe [[yii\\base\\Model]]. Néanmoins, comme il y a de nombreux composants de Yii conçus pour prendre en charge la classe [[yii\\base\\Model]], il est généralement préférable de baser vos modèles sur cette classe.\n\n\n## Attributs <span id=\"attributes\"></span>\n\nLes modèles représentent les données de l'application en termes  d'attributs. Chaque attribut est comme un propriété publiquement accessible d'un modèle. La méthode [[yii\\base\\Model::attributes()]] spécifie quels attributs une classe de modèle possède. \n\nVous pouvez accéder à un attribut comme vous accédez à une propriété d'un objet ordinaire :\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// \"name\" is an attribute of ContactForm\n$model->name = 'example';\necho $model->name;\n```\n\nVous pouvez également accéder aux attributs comme aux éléments d'un tableau, grâce à la prise en charge de [ArrayAccess](https://www.php.net/manual/en/class.arrayaccess.php) et [ArrayIterator](https://www.php.net/manual/en/class.arrayiterator.php)\npar la classe [[yii\\base\\Model]]:\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// accès aux attributs comme à des éléments de tableau\n$model['name'] = 'example';\necho $model['name'];\n\n// itération sur les attributs avec foreach\nforeach ($model as $name => $value) {\n    echo \"$name: $value\\n\";\n}\n```\n\n\n### Définition d'attributs <span id=\"defining-attributes\"></span>\n\nPar défaut, si votre classe de modèle étend directement la classe [[yii\\base\\Model]], toutes ses variables membres *non statiques et publiques* sont des attributs. Par exemple, la classe de modèle `ContactForm` ci-dessous possède quatre attributs : `name`, `email`,\n`subject` et `body`. Le modèle `ContactForm` est utilisé pour représenter les données saisies dans un formulaire HTML. \n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\n\nclass ContactForm extends Model\n{\n    public $name;\n    public $email;\n    public $subject;\n    public $body;\n}\n```\n\n\nVous pouvez redéfinir la méthode [[yii\\base\\Model::attributes()]] pour spécifier les attributs d'une autre manière. La méthode devrait retourner le nom des attributs d'un modèle. Par exemple, [[yii\\db\\ActiveRecord]] fait cela en retournant le nom des colonnes de la base de données associée en tant que noms d'attribut. Notez que vous pouvez aussi avoir besoin de redéfinir les méthodes magiques telles que `__get()` et `__set()` afin que les attributs puissent être accédés comme les propriétés d'un objet ordinaire. \n\n\n### Étiquettes d'attribut <span id=\"attribute-labels\"></span>\n\nLors de l'affichage de la valeur d'un attribut ou lors de la saisie d'une entrée pour une telle valeur, il est souvent nécessaire d'afficher une étiquette associée à l'attribut. Par exemple, étant donné l'attribut nommé `firstName` (prénom), vous pouvez utiliser une étiquette de la forme `First Name` qui est plus conviviale lorsqu'elle est présentée à l'utilisateur final dans un formulaire ou dans un message d'erreur. \n\nVous pouvez obtenir l'étiquette d'un attribut en appelant la méthode [[yii\\base\\Model::getAttributeLabel()]]. Par exemple :\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// displays \"Name\"\necho $model->getAttributeLabel('name');\n```\n\nPar défaut, les étiquettes d'attribut sont automatiquement générées à partir des noms d'attribut. La génération est faite en appelant la méthode [[yii\\base\\Model::generateAttributeLabel()]]. Cette méthode transforme un nom de variable avec une casse en dos de chameau en de multiples mots, chacun commençant par une capitale. Par exemple, `username` donne `Username` et `firstName` donne `First Name`.\n\nSi vous ne voulez pas utiliser les étiquettes à génération automatique, vous pouvez redéfinir la méthode [[yii\\base\\Model::attributeLabels()]] pour déclarer explicitement les étiquettes d'attribut. Par exemple :\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\n\nclass ContactForm extends Model\n{\n    public $name;\n    public $email;\n    public $subject;\n    public $body;\n\n    public function attributeLabels()\n    {\n        return [\n            'name' => 'Nom',\n            'email' => 'Adresse de courriel',\n            'subject' => 'Subjet',\n            'body' => 'Contenu',\n        ];\n    }\n}\n```\n\nPour les application prenant en charge de multiples langues, vous désirez certainement traduire les étiquettes d'attribut. Cela peut être fait dans la méthode [[yii\\base\\Model::attributeLabels()|attributeLabels()]] également, en procédant comme ceci :\n\n```php\npublic function attributeLabels()\n{\n    return [\n        'name' => \\Yii::t('app', 'Your name'),\n        'email' => \\Yii::t('app', 'Your email address'),\n        'subject' => \\Yii::t('app', 'Subject'),\n        'body' => \\Yii::t('app', 'Content'),\n    ];\n}\n```\n\nVous pouvez même définir les étiquettes en fonction de conditions. Par exemple, en fonction du [scénario](#scenarios) dans lequel le modèle est utilisé, vous pouvez retourner des étiquettes différentes pour le même attribut.\n\n> Info: strictement parlant, les étiquettes d'attribut font partie des [vues](structure-views.md). Mais la déclaration d'étiquettes dans les modèles est souvent très pratique et conduit à un code propre et réutilisable. \n\n\n## Scénarios <span id=\"scenarios\"></span>\n\nUn modèle peut être utilisé dans différents *scénarios*. Par exemple, un modèle `User` (utilisateur) peut être utilisé pour collecter les données d'un utilisateur, mais il peut aussi être utilisé à des fins d'enregistrement d'enregistrement de l'utilisateur. Dans différents scénarios, un modèle peut utiliser des règles de traitement et une logique différente. Par exemple, `email` peut être nécessaire lors de l'enregistrement de l'utilisateur mais pas utilisé lors de sa connexion. \n\nUn modèle utilise la propriété [[yii\\base\\Model::scenario]] pour conserver un trace du scénario dans lequel il est utilisé.\n\nPar défaut, un modèle prend en charge un unique scénario nommé `default`. Le code qui suit montre deux manières de définir le scénario d'un modèle :\n\n```php\n// le scénario est défini comme une propriété\n$model = new User;\n$model->scenario = User::SCENARIO_LOGIN;\n\n// le scénario est défini via une configuration\n$model = new User(['scenario' => User::SCENARIO_LOGIN]);\n```\n\nPar défaut, les scénarios pris en charge par un modèle sont déterminés par les [règles de validation](#validation-rules) déclarées dans le modèle. Néanmoins, vous pouvez personnaliser ce comportement en redéfinissant la méthode [[yii\\base\\Model::scenarios()]], de la manière suivante : \n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass User extends ActiveRecord\n{\n    const SCENARIO_LOGIN = 'login';\n    const SCENARIO_REGISTER = 'register';\n\n    public function scenarios()\n    {\n        return [\n            self::SCENARIO_LOGIN => ['username', 'password'],\n            self::SCENARIO_REGISTER => ['username', 'email', 'password'],\n        ];\n    }\n}\n```\n\n> Info: dans ce qui précède et dans l'exemple qui suit, les classes de modèle étendent la classe [[yii\\db\\ActiveRecord]] parce que l'utilisation de multiples scénarios intervient ordinairement dans les classes [Active Record](db-active-record.md).\n\nLa méthode `scenarios()` retourne un tableau dont les clés sont les noms de scénario et les valeurs les *attributs actifs* correspondants. Les attributs actifs peuvent être [assignés massivement](#massive-assignment) et doivent respecter des règles de [validation](#validation-rules). Dans l'exemple ci-dessus, les attributs `username` et `password`  sont actifs dans le scénario `login`, tandis que dans le scénario `register` , `email` est, en plus de `username` et`password`, également actif.\n\nLa mise en œuvre par défaut des `scenarios()` retourne tous les scénarios trouvés dans la méthode de déclaration des règles de validation [[yii\\base\\Model::rules()]]. Lors de la redéfinition des `scenarios()`, si vous désirez introduire de nouveaux scénarios en plus des scénarios par défaut, vous pouvez utiliser un code similaire à celui qui suit :\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass User extends ActiveRecord\n{\n    const SCENARIO_LOGIN = 'login';\n    const SCENARIO_REGISTER = 'register';\n\n    public function scenarios()\n    {\n        $scenarios = parent::scenarios();\n        $scenarios[self::SCENARIO_LOGIN] = ['username', 'password'];\n        $scenarios[self::SCENARIO_REGISTER] = ['username', 'email', 'password'];\n        return $scenarios;\n    }\n}\n```\nLa fonctionnalité *scénarios* est d'abord utilisée pour la [validation](#validation-rules) et dans l'[assignation massive des attributs](#massive-assignment). Vous pouvez, cependant l'utiliser à d'autres fins. Par exemple, vous pouvez déclarer des [étiquettes d'attribut](#attribute-labels) différemment en vous basant sur le scénario courant.\n\n\n## Règles de validation<span id=\"validation-rules\"></span>\n\nLosque les données pour un modèle sont reçues de l'utilisateur final, elles doivent être validées pour être sûr qu'elles respectent certaines règles (appelées *règles de validation*). Par exemple, étant donné un modèle pour un  formulaire de contact (`ContactForm`), vous voulez vous assurer qu'aucun des attributs n'est vide et que l'attribut `email` contient une adresse de courriel valide. Si les valeurs pour certains attributs ne respectent pas les règles de validation, les messages d'erreur appropriés doivent être affichés pour aider l'utilisateur à corriger les erreurs.\n\nVous pouvez faire appel à la méthode [[yii\\base\\Model::validate()]] pour valider les données reçues. La méthode utilise les règles de validation déclarées dans [[yii\\base\\Model::rules()]] pour valider chacun des attributs concernés. Si aucune erreur n'est trouvée, elle retourne `true` (vrai). Autrement, les erreurs sont stockées dans la propriété [[yii\\base\\Model::errors]] et la méthode retourne `false` (faux). Par exemple :\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// définit les attributs du modèle avec les entrées de l'utilisateur final\n$model->attributes = \\Yii::$app->request->post('ContactForm');\n\nif ($model->validate()) {\n    // toutes les entrées sont valides\n} else {\n    // la validation a échoué : le tableau $errors contient les messages d'erreur\n    $errors = $model->errors;\n}\n```\n\n\nPour déclarer des règles de validation associées à un modèle, redéfinissez la méthode [[yii\\base\\Model::rules()]] en retournant les règles que le modèle doit respecter. L'exemple suivant montre les règles de validation déclarées pour le modèle *formulaire de contact `ContactForm` :\n\n```php\npublic function rules()\n{\n    return [\n        // the name, email, subject and body attributes are required\n        [['name', 'email', 'subject', 'body'], 'required'],\n\n        // the email attribute should be a valid email address\n        ['email', 'email'],\n    ];\n}\n```\n\nUne règle peut être utilisée pour valider un ou plusieurs attributs, et un attribut peut être validé par une ou plusieurs règles. Reportez-vous à la section [validation des entrées](input-validation.md) pour plus de détails sur la manière de déclarer les règles de validation.\n\nParfois, vous désirez qu'une règle ne soit applicable que dans certains [scénarios](#scenarios). Pour cela, vous pouvez spécifier la propriété `on` d'une règle, comme ci-dessous :\n\n```php\npublic function rules()\n{\n    return [\n        // username, email et  password sont tous requis dans le scénario \"register\"\n        [['username', 'email', 'password'], 'required', 'on' => self::SCENARIO_REGISTER],\n\n        // username et password sont requis dans le scénario \"login\"\n        [['username', 'password'], 'required', 'on' => self::SCENARIO_LOGIN],\n    ];\n}\n```\n\nSi vous ne spécifiez pas la propriété `on`, la règle sera appliquée dans tous les scénarios. Une règle est dite *règle active* si elle s'applique au scénario courant [[yii\\base\\Model::scenario|scenario]].\n\nUn attribut n'est validé que si, et seulement si, c'est un attribut actif déclaré dans `scenarios()` et s'il est associé à une ou plusieurs règles actives déclarées dans `rules()`.\n\n\n## Assignation massive <span id=\"massive-assignment\"></span>\n\nL'assignation massive est une façon pratique de peupler un modèle avec les entrées de l'utilisateur final en utilisant une seule ligne de code . Elle peuple les attributs d'un modèle en assignant directement les données d'entrée à la propriété [[yii\\base\\Model::$attributes]]. Les deux extraits de code suivants sont équivalent. Ils tentent tous deux d'assigner les données du formulaire soumis par l'utilisateur final aux attributs du modèle `ContactForm`. En clair, le premier qui utilise l'assignation massive, est plus propre et moins sujet aux erreurs que le second :\n\n```php\n$model = new \\app\\models\\ContactForm;\n$model->attributes = \\Yii::$app->request->post('ContactForm');\n```\n\n```php\n$model = new \\app\\models\\ContactForm;\n$data = \\Yii::$app->request->post('ContactForm', []);\n$model->name = isset($data['name']) ? $data['name'] : null;\n$model->email = isset($data['email']) ? $data['email'] : null;\n$model->subject = isset($data['subject']) ? $data['subject'] : null;\n$model->body = isset($data['body']) ? $data['body'] : null;\n```\n\n\n### Attributs sûr <span id=\"safe-attributes\"></span>\n\nL'assignation massive ne s'applique qu'aux attributs dits *attributs sûrs* qui sont les attributs listés dans la méthode [[yii\\base\\Model::scenarios()]] pour le [[yii\\base\\Model::scenario|scénario]] courant d'un modèle. \nPar exemple, si le modèle `User` contient la déclaration de scénarios suivante, alors, lorsque le scénario courant est  `login`, seuls les attributs `username` et `password` peuvent être massivement assignés. Tout autre attribut n'est pas touché par l'assignation massive. \n\n```php\npublic function scenarios()\n{\n    return [\n        self::SCENARIO_LOGIN => ['username', 'password'],\n        self::SCENARIO_REGISTER => ['username', 'email', 'password'],\n    ];\n}\n```\n\n> Info: la raison pour laquelle l'assignation massive ne s'applique qu'aux attributs sûrs est de vous permettre de contrôler quels attributs peuvent être modifiés par les données envoyées par l'utilisateur final. Par exemple, si le modèle `User` possède un attribut `permission` qui détermine les permissions accordées à l'utilisateur, vous préférez certainement que cet attribut ne puisse être modifié que par un administrateur via l'interface d'administration seulement.\n\nComme la mise en œuvre par défaut de la méthode [[yii\\base\\Model::scenarios()]] retourne tous les scénarios et tous les attributs trouvés dans la méthode [[yii\\base\\Model::rules()]], si vous ne redéfinissez pas cette méthode, cela signifie qu'un attribut est *sûr* tant qu'il apparaît dans une des règles de validation actives. \n\nPour cette raison, un validateur spécial dont l'alias est `safe` est fourni pour vous permettre de déclarer un attribut *sûr* sans réellement le valider. Par exemple, les règles suivantes déclarent que  `title`\net `description` sont tous deux des attributs sûrs.\n\n```php\npublic function rules()\n{\n    return [\n        [['title', 'description'], 'safe'],\n    ];\n}\n```\n\n\n### Attributs non sûr <span id=\"unsafe-attributes\"></span>\n\nComme c'est expliqué plus haut, la méthode  [[yii\\base\\Model::scenarios()]] remplit deux objectifs : déterminer quels attributs doivent être validés, et déterminer quels attributs sont *sûrs*. Dans certains cas, vous désirez valider un attribut sans le marquer comme *sûr*. Vous pouvez le faire en préfixant son nom par un point d'exclamation `!` lorsque vous le déclarez dans la méthode `scenarios()`, comme c'est fait pour l'attribut `secret` dans le code suivant :\n\n```php\npublic function scenarios()\n{\n    return [\n        self::SCENARIO_LOGIN => ['username', 'password', '!secret'],\n    ];\n}\n```\n\nLorsque le modèle est dans le scénario `login`, les trois attributs sont validés. Néanmoins, seuls les attributs  `username`\net `password` peuvent être massivement assignés. Pour assigner une valeur d'entrée à l'attribut `secret`, vous devez le faire explicitement, comme montré ci-dessous :\n\n```php\n$model->secret = $secret;\n```\n\nLa même chose peut être faite dans la méthode `rules()` :\n\n```php\npublic function rules()\n{\n    return [\n        [['username', 'password', '!secret'], 'required', 'on' => 'login']\n    ];\n}\n```\n\nDans ce cas, les attributs `username`, `password` et `secret` sont requis, mais `secret` doit être assigné explicitement. \n\n## Exportation de données <span id=\"data-exporting\"></span>\n\nOn a souvent besoin d'exporter les modèles dans différents formats. Par exemple, vous désirez peut-être convertir une collection de modèles dans le format JSON ou dans le format Excel. Le processus d'exportation peut être décomposé en deux étapes indépendantes :\n\n- les modèles sont convertis en tableaux,\n- les tableaux sont convertis dans les formats cibles. \n\nVous pouvez vous concentrer uniquement sur la première étape, parce que la seconde peut être accomplie par des formateurs génériques de données, tels que [[yii\\web\\JsonResponseFormatter]].\n\nLa manière la plus simple de convertir un modèle en tableau est d'utiliser la propriété [[yii\\base\\Model::$attributes]]. Par exemple :\n\n```php\n$post = \\app\\models\\Post::findOne(100);\n$array = $post->attributes;\n```\n\nPar défaut, la propriété [[yii\\base\\Model::$attributes]] retourne les valeurs de *tous* les attributs déclarés dans la méthode [[yii\\base\\Model::attributes()]].\n\nUne manière plus souple et plus puissante de convertir un modèle en tableau est d'utiliser la méthode [[yii\\base\\Model::toArray()]]. Son comportement par défaut est de retourner la même chose que la propriété [[yii\\base\\Model::$attributes]]. Néanmoins, elle vous permet de choisir quelles données, appelées *champs*, doivent être placées dans le tableau résultant et comment elles doivent être formatées. En fait, c'est la manière par défaut pour exporter les modèles dans le développement d'un service Web respectant totalement l'achitecture REST, telle que décrite à la section [Formatage de la réponse](rest-response-formatting.md).\n\n\n### Champs <span id=\"fields\"></span>\n\nUn champ n'est rien d'autre qu'un élément nommé du tableau qui est obtenu en appelant la méthode [[yii\\base\\Model::toArray()]] d'un modèle.\n\nPar défaut, les noms de champ sont équivalents aux noms d'attribut. Cependant, vous pouvez changer ce comportement en redéfinissant la méthode [[yii\\base\\Model::fields()|fields()]] et/ou la méthode [[yii\\base\\Model::extraFields()|extraFields()]]. Ces deux méthodes doivent retourner une liste de définitions de champ. Les champs définis par `fields()` sont des champs par défaut, ce qui signifie que `toArray()` retourne ces champs par défaut. La méthode `extraFields()` définit des champs additionnels disponibles qui peuvent également être retournés par `toArray()` du moment que vous les spécifiez via le paramètre `$expand`. Par exemple, le code suivant retourne tous les champs définis dans la méthode `fields()` ainsi que les champs `prettyName` et `fullAddress`, s'ils sont définis dans `extraFields()`.\n\n```php\n$array = $model->toArray([], ['prettyName', 'fullAddress']);\n```\n\nVous pouvez redéfinir la méthode `fields()` pour ajouter, retirer, renommer ou redéfinir des champs. La valeur de retour de la méthode `fields()` doit être un tableau associatif. Les clés du tableau sont les noms des champs et les valeurs sont les définitions de champ correspondantes qui peuvent être, soit des noms d'attribut/propriété, soit des fonctions anonymes retournant les valeurs de champ correspondantes. Dans le cas particulier où un nom de champ est identique à celui du nom d'attribut qui le définit, vous pouvez omettre la clé du tableau. Par exemple :\n\n```php\n// liste explicitement chaque champ ; à utiliser de préférence quand vous voulez être sûr \n// que les changements dans votre table de base de données ou dans les attributs de votre modèle\n// ne créent pas de changement dans vos champs (pour conserver la rétro-compatibilité de l'API). \npublic function fields()\n\n{\n    return [\n        // le nom du champ est identique à celui de l'attribut\n        'id',\n\n        // le nom du champ est \"email\", le nom d'attribut correspondant est  \"email_address\"\n        'email' => 'email_address',\n\n        // le nom du champ est  \"name\", sa valeur est définie par une fonction PHP de rappel\n        'name' => function () {\n            return $this->first_name . ' ' . $this->last_name;\n        },\n    ];\n}\n\n// filtre quelques champs ; à utiliser de préférence quand vous voulez hériter de l'implémentation du parent \n// et mettre quelques champs sensibles en liste noire.\npublic function fields()\n{\n    $fields = parent::fields();\n\n    // retire les champs contenant des informations sensibles \n    unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);\n\n    return $fields;\n}\n```\n\n> Warning: étant donné que, par défaut, tous les attributs d'un modèle sont inclus dans le tableau exporté, \n> vous devez vous assurer que vos données ne contiennent pas d'information sensible. \n> Si de telles informations existent, vous devriez redéfinir la méthode `fields()` pour les filtrer. \n> Dans l'exemple ci-dessus, nous avons choisi d'exclure `auth_key`, `auth_key`, `password_hash` et `password_reset_token`.\n\n\n## Meilleures pratiques <span id=\"best-practices\"></span>\n\nLes modèles sont les endroits centraux pour représenter les données de l'application, les règles et la logique. Ils doivent souvent être réutilisés à différents endroits. Dans une application bien conçue, les modèles sont généralement plus volumineux que les [contrôleurs](structure-controllers.md).\n\nEn résumé, voici les caractéristiques essentielles des modèles :\n\n* Ils peuvent contenir des attributs pour représenter les données de l'application.\n* Ils peuvent contenir des règles de validation pour garantir la validité et l'intégrité des données.\n* Ils peuvent contenir des méthodes assurant le traitement logique des données de l'application. \n* Ils ne devraient PAS accéder directement à la requête, à la session ou à n'importe quelle autre donnée environnementale. Ces données doivent être injectées dans les modèles par les [contrôleurs](structure-controllers.md).\n* Ils devraient éviter d'inclure du code HTML ou tout autre code relatif à la présentation — cela est fait de manière plus avantageuse dans les [vues](structure-views.md).\n* Il faut éviter d'avoir trop de [scénarios](#scenarios) dans un même modèle.\n\nVous pouvez ordinairement considérer cette dernière recommandation lorsque vous développez des systèmes importants et complexes. Dans ces systèmes, les modèles pourraient être très volumineux parce que, étant utilisés dans de nombreux endroits, ils doivent contenir de nombreux jeux de règles et de traitement logiques. Cela se termine le plus souvent en cauchemar pour la maintenance du code du modèle parce que le moindre changement de code  est susceptible d'avoir de l'effet en de nombreux endroits. Pour rendre le modèle plus maintenable, vous pouvez adopter la stratégie suivante :\n\n* Définir un jeu de classes de base du modèle qui sont partagées par différentes [applications](structure-applications.md) ou\n  [modules](structure-modules.md). Ces classes de modèles devraient contenir un jeu minimal de règles et de logique qui sont communes à tous les usages. \n* Dans chacune des [applications](structure-applications.md) ou [modules](structure-modules.md) qui utilise un modèle, définir une classe de modèle concrète  en étendant la classe de base de modèle correspondante. Les classes de modèles concrètes devraient contenir certaines règles et logiques spécifiques à cette application ou à ce module.\n\nPar exemple, dans le [Modèle avancé de projet](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md), vous pouvez définir une classe de modèle de base `common\\models\\Post`. Puis, pour l'application « interface utilisateur » (*frontend*) vous pouvez définir une classe de base concrète `frontend\\models\\Post` qui étend la classe `common\\models\\Post`. De manière similaire, pour l'application « interface d'administration » (*backend*) vous pouvez définir une classe `backend\\models\\Post`. Avec cette stratégie, vous êtes sûr que le code de `frontend\\models\\Post` est seulement spécifique à l'application « interface utilisateur », et si vous y faite un changement, vous n'avez à vous soucier de savoir si cela à une influence sur l'application « interface d'administration ».\n"
  },
  {
    "path": "docs/guide-fr/structure-modules.md",
    "content": "Modules\n=======\n\nLes modules sont des unités logicielles auto-suffisantes constituées de [modèles](structure-models.md), [vues](structure-views.md),\n[contrôleurs](structure-controllers.md) et autres composants de prise en charge. L'utilisateur final peut accéder aux contrôleurs dans un module lorsqu'il est installé dans une [application](structure-applications.md). Pour ces raisons, les modules sont souvent regardés comme de mini-applications. Les modules diffèrent des [applications](structure-applications.md) par le fait que les modules ne peuvent être déployés seuls et doivent résider dans une application. \n\n## Création de modules <span id=\"creating-modules\"></span>\n\nUn module est organisé comme un dossier qui est appelé le [[yii\\base\\Module::basePath|dossier de base (*basePath*)]] du module. Dans ce dossier, se trouvent des sous-dossiers, tels que `controllers`, `models` et `views`, qui contiennent les contrôleurs, les modèles , les vues et d'autres parties de code, juste comme une application. L'exemple suivant présente le contenu d'un module : \n\n```\nforum/\n    Module.php                   le fichier de classe du module \n    controllers/                 contient les fichiers de classe des contrôleurs\n        DefaultController.php    le fichier de classe de contrôleur par défaut\n    models/                      contient les fichiers de classe des modèles\n    views/                       contient les fichiers de contrôleur, de vue et de disposition \n        layouts/                 contient les fichiers de diposition\n        default/                 contient les fichiers de vues pour le contrôleur par défaut\n            index.php            le fichier de vue index \n```\n\n\n### Classes de module <span id=\"module-classes\"></span>\n\nChacun des modules doit avoir une classe unique de module qui étend [[yii\\base\\Module]]. La classe doit être située directement dans le [[yii\\base\\Module::basePath|dossier de base]] du module et doit être [auto-chargeable](concept-autoloading.md). Quand un module est accédé, une instance unique de la classe de module correspondante est créée. Comme les [instances d'application](structure-applications.md), les instances de module sont utilisées pour partager des données et des composants.\n\nL'exemple suivant montre à quoi une classe de module peut ressembler :\n\n```php\nnamespace app\\modules\\forum;\n\nclass Module extends \\yii\\base\\Module\n{\n    public function init()\n    {\n        parent::init();\n\n        $this->params['foo'] = 'bar';\n        // ...  other initialization code ...\n    }\n}\n```\n\nLa méthode `init()` contient un code volumineux pour initialiser les propriétés du module. Vous pouvez également les sauvegarder sous forme de [configuration](concept-configurations.md) et charger cette configuration avec le code suivant dans la méthode `init()`:\n\n```php\npublic function init()\n{\n    parent::init();\n    // initialise le module à partir de la configuration chargée depuis config.php\n    \\Yii::configure($this, require __DIR__ . '/config.php');\n}\n```\n\noù le fichier de configuration `config.php` peut avoir le contenu suivant, similaire à celui d'une [configuration d'application](structure-applications.md#application-configurations).\n\n```php\n<?php\nreturn [\n    'components' => [\n        // liste des configurations de composant\n    ],\n    'params' => [\n        // liste des paramètres\n    ],\n];\n```\n\n\n### Contrôleurs dans les modules <span id=\"controllers-in-modules\"></span>\n\nLorsque vous créez des contrôleurs dans un module, une convention est de placer les classes de contrôleur dans le sous-espace de noms `controllers` dans l'espace de noms de la classe du module.  Cela signifie également que les fichiers de classe des contrôleur doivent être placés dans le dossier `controllers` dans le [[yii\\base\\Module::basePath|dossier de base]] du module. Par exemple, pour créer un contrôleur `post` dans le module `forum` présenté dans la section précédente, vous devez déclarer la classe de contrôleur comme ceci :\n\n```php\nnamespace app\\modules\\forum\\controllers;\n\nuse yii\\web\\Controller;\n\nclass PostController extends Controller\n{\n    // ...\n}\n```\n\nVous pouvez personnaliser l'espace de noms des classes de contrôleur en configurant la propriété [[yii\\base\\Module::controllerNamespace]]. Dans le cas où certains contrôleurs sont en dehors de cet espace de noms, vous pouvez les rendre accessibles en configurant la propriété  [[yii\\base\\Module::controllerMap]] comme vous le feriez dans une [application](structure-applications.md#controllerMap).\n\n\n### Vues dans les modules <span id=\"views-in-modules\"></span>\n\nLes vues dans les modules doivent être placées dans le dossier `views` du [[yii\\base\\Module::basePath|dossier de base (*base path*)]] du module. Quant aux vues rendues par un contrôleur du module, elles doivent être placées dans le dossier `views/ControllerID`, où `ControllerID` fait référence à l'[identifiant du contrôleur](structure-controllers.md#routes). Par exemple, si la classe du contrôleur est `PostController`, le dossier doit être `views/post` dans le [[yii\\base\\Module::basePath|dossier de base]] du module.\n\nUn module peut spécifier une [disposition](structure-views.md#layouts) qui s'applique aux vues rendues par les contrôleurs du module. La disposition doit être mise dans le dossier `views/layouts` par défaut, et vous devez configurer la propriété [[yii\\base\\Module::layout]] pour qu'elle pointe sur le nom de la disposition. Si vous ne configurez pas la propriété `layout` c'est la disposition de l'application qui est utilisée à sa place.\n\n\n### Commande de console dans les modules <span id=\"console-commands-in-modules\"></span>\n\nVotre module peut aussi déclarer des commandes, qui sont accessibles via le mode [Console](tutorial-console.md).\n\nAfin que l'utilitaire de ligne de commande reconnaisse vos commandes, vous devez changer la propriété [[yii\\base\\Module::controllerNamespace (espace de noms du contrôleur)]] lorsque Yii est exécuté en mode console, et le diriger sur votre espace de noms de commandes. \n\nUne manière de réaliser cela est de tester le type d'instance de l'application Yii dans la méthode `init` du module :\n\n```php\npublic function init()\n{\n    parent::init();\n    if (Yii::$app instanceof \\yii\\console\\Application) {\n        $this->controllerNamespace = 'app\\modules\\forum\\commands';\n    }\n}\n```\n\nVos commandes seront disponibles en ligne de commande en utilisant la route suivante :\n\n```\nyii <module_id>/<command>/<sub_command>\n```\n\n## Utilisation des modules <span id=\"using-modules\"></span>\n\nPour utiliser un module dans une  application, il vous suffit de configurer l'application en listant le module dans la propriété [[yii\\base\\Application::modules|modules]] de l'application. Le code qui suit dans la [configuration de l'application](structure-applications.md#application-configurations) permet l'utilisation du module `forum` :\n\n```php\n[\n    'modules' => [\n        'forum' => [\n            'class' => 'app\\modules\\forum\\Module',\n            // ... autres éléments de  configuration pour le module ...\n        ],\n    ],\n]\n```\n\nLa propriété [[yii\\base\\Application::modules|modules]] accepte un tableau de configurations de module. Chaque clé du tableau représente un *identifiant de module* qui distingue ce module parmi les autres modules de l'application, et la valeur correspondante est une  [configuration](concept-configurations.md) pour la création du module.\n\n\n### Routes <span id=\"routes\"></span>\n\nLes [routes](structure-controllers.md#routes) sont utilisées pour accéder aux contrôleurs d'un module comme elles le sont pour accéder aux contrôleurs d'une application. Une route, pour un contrôleur d'un module, doit commencer par l'identifiant du module, suivi de l'[identifiant du contrôleur](structure-controllers.md#controller-ids) et de [identifiant de l'action](structure-controllers.md#action-ids). Par exemple, si une application utilise un module nommé `forum`, alors la route `forum/post/index` représente l'action `index` du contrôleur `post` du module. Si la route ne contient que l'identifiant du module, alors la propriété [[yii\\base\\Module::defaultRoute]], dont la valeur par défaut est `default`, détermine quel contrôleur/action utiliser. Cela signifie que la route `forum` représente le contrôleur `default` dans le module `forum`.\n\nLe gestionnaire d'URL du module doit être ajouté avant que la fonction [[yii\\web\\UrlManager::parseRequest()]] ne soit exécutée. Cela siginifie que le faire dans la fonction `init()` du module ne fonctionne pas parce que le module est initialisé après que les routes ont été résolues. Par conséquent, les règles doivent être ajoutées à l'[étape d'amorçage](structure-extensions.md#bootstrapping-classes). C'est également une bonne pratique d'empaqueter les règles d'URL du module dans [[\\yii\\web\\GroupUrlRule]].\n\nDans le cas où un module est utilisé pour [versionner une API](rest-versioning.md), ses règles d'URL doivent être ajoutées directement dans la section `urlManager` de la configuration de l'application.\n\n### Accès aux modules <span id=\"accessing-modules\"></span>\n\nDans un module, souvent, il arrive que vous ayez besoin d'une instance de la [classe du module](#module-classes) de façon à pouvoir accéder à l'identifiant du module, à ses paramètres, à ses composants, etc. Vous pouvez le faire en utilisant l'instruction suivante :\n\n```php\n$module = MyModuleClass::getInstance();\n```\n\ndans laquelle `MyModuleClass` fait référence au nom de la classe du module qui vous intéresse. La méthode `getInstance()` retourne l'instance  de la classe du module actuellement requis. Si le module n'est pas requis, la méthode retourne `null`. Notez que vous n'avez pas besoin de créer manuellement une nouvelle instance de la classe du module parce que celle-ci serait différente de celle créée par Yii en réponse à la requête.\n\n> Info: lors du développement d'un module, vous ne devez pas supposer que le module va utiliser un identifiant fixe. Cela tient au fait qu'un module peut être associé à un identifiant arbitraire lorsqu'il est utilisé dans une application ou dans un autre module. Pour obtenir l'identifiant du module, vous devez utiliser l'approche ci-dessus pour obtenir d'abord une instance du module, puis obtenir l'identifiant via `$module->id`.\n\nVous pouvez aussi accéder à l'instance d'un module en utilisant les approches suivantes :\n\n```php\n// obtenir le module fils dont l'identifiant est \"forum\"\n$module = \\Yii::$app->getModule('forum');\n\n// obtenir le  module auquel le contrôleur actuellement requis appartient \n$module = \\Yii::$app->controller->module;\n```\n\nLa première approche n'est utile que lorsque vous connaissez l'identifiant du module, tandis que la seconde est meilleure lorsque vous connaissez le contrôleur actuellement requis. \n\nUne fois que vous disposez de l'instance du module, vous pouvez accéder aux paramètres et aux composants enregistrés avec le module. Par exemple :\n\n```php\n$maxPostCount = $module->params['maxPostCount'];\n```\n\n\n### Modules faisant partie du processus d'amorçage <span id=\"bootstrapping-modules\"></span>\n\nIl se peut que certains modules doivent être exécutés pour chacune des requêtes. Le module [[yii\\debug\\Module|debug]] en est un exemple. Pour que des modules soit exécutés pour chaque requête, vous devez les lister dans la propriété [[yii\\base\\Application::bootstrap|bootstrap]] de l'application. \n\nPar exemple, la configuration d'application suivante garantit que le module `debug` est chargé à chaque requête : \n\n```php\n[\n    'bootstrap' => [\n        'debug',\n    ],\n\n    'modules' => [\n        'debug' => 'yii\\debug\\Module',\n    ],\n]\n```\n\n\n## Modules imbriqués <span id=\"nested-modules\"></span>\n\nLes modules peuvent être imbriqués sur un nombre illimité de niveaux. C'est à dire qu'un module pour contenir un autre module qui contient lui-même un autre module. Nous parlons alors de *module parent* pour le module englobant, et de *module enfant* pour le module contenu. Les modules enfants doivent être déclarés dans la propriété [[yii\\base\\Module::modules|modules]] de leur module parent. Par exemple :\n\n```php\nnamespace app\\modules\\forum;\n\nclass Module extends \\yii\\base\\Module\n{\n    public function init()\n    {\n        parent::init();\n\n        $this->modules = [\n            'admin' => [\n                // Vous devriez envisager l'utilisation d'un espace de noms plus court ici !\n                'class' => 'app\\modules\\forum\\modules\\admin\\Module',\n            ],\n        ];\n    }\n}\n```\n\nLa route vers un contrôleur inclus dans un module doit inclure les identifiants de tous ses modules ancêtres. Par exemple, la route `forum/admin/dashboard/index` représente l'action `index` du contrôleur `dashboard` dans le module `admin` qui est un module enfant du module `forum`.\n\n> Info: la méthode [[yii\\base\\Module::getModule()|getModule()]] ne retourne que le module enfant appartenant directement à son parent.  La propriété [[yii\\base\\Application::loadedModules]] tient à jour une liste des modules chargés, y compris les enfant directs et les enfants des générations suivantes, indexée par le nom de classe. \n\n## Accès aux composants depuis l'intérieur des modules \n\nDepuis la version 2.0.13, les modules prennent en charge la [traversée des arbres](concept-service-locator.md#tree-traversal). Cela permet aux développeurs de modules de faire référence à des composants (d'application) via le localisateur de services qui se trouve dans leur module. \nCela signifie qu'il est préférable d'utiliser `$module->get('db')` plutôt que `Yii::$app->get('db')`.\nL'utilisateur d'un module est capable de spécifier un composant particulier pour une utilisation dans le module dans le cas où une configuration différente du composant est nécessaire. \n\nPar exemple, considérons cette partie de la configuration d'une application :\n\n\n```php\n'components' => [\n    'db' => [\n        'tablePrefix' => 'main_',\n        'class' => Connection::class,\n        'enableQueryCache' => false\n    ],\n],\n'modules' => [\n    'mymodule' => [\n        'components' => [\n            'db' => [\n                'tablePrefix' => 'module_',\n                'class' => Connection::class\n            ],\n        ],\n    ],\n],\n```\n\nLes tables de base de données de l'application seront préfixées par `main_`, tandis que les tables de tous les modules seront préfixées par `module_`.\nNotez cependant que la configuration ci-dessus n'est pas fusionnée; le composant des modules par exemple aura le cache de requêtes activé puisque c'est la valeur par défaut.\n\n\n\n## Meilleures pratiques <span id=\"best-practices\"></span>\n\nL'utilisation des modules est préférable dans les grosses applications dont les fonctionnalités peuvent être réparties en plusieurs groupes, consistant chacun en un jeu de fonctionnalités liées d'assez près. Chacune de ces fonctionnalités peut être conçue comme un module développé et maintenu par un développeur ou une équipe spécifique. \n\nLes modules sont aussi un bon moyen de réutiliser du code au niveau des groupes de fonctionnalités. Quelques fonctionnalité d'usage courant, telles que la gestion des utilisateurs, la gestion des commentaires, etc. peuvent être développées en tant que modules ce qui facilite leur réutilisation dans les projets suivants. \n"
  },
  {
    "path": "docs/guide-fr/structure-overview.md",
    "content": "Vue d'ensemble\n========\n\nLes applications Yii sont organisées suivant le modèle de conception \n[model-view-controller (MVC)](https://fr.wikipedia.org/wiki/Mod%C3%A8le-vue-contr%C3%B4leur). Les [Modèles](structure-models.md) \nreprésentent les données, la logique métier et les règles; les [vues](structure-views.md) sont les représentations\nvisuelles des modèles, et les [contrôleurs](structure-controllers.md) prennent une entrée et la convertissent en \ncommandes pour les [modèles](structure-models.md) et les [vues](structure-views.md).\n\nEn plus du MVC, les applications Yii ont les entités suivantes :\n\n* [scripts d'entrée](structure-entry-scripts.md): ce sont des scripts PHP qui sont directement accessibles aux \n  utilisateurs. Ils sont responsables de l'amorçage d'un cycle de gestion de requête.\n* [applications](structure-applications.md): ce sont des objets globalement accessibles qui gèrent les composants\n  d'application et les coordonnent pour satisfaire des requêtes.\n* [composants d'application](structure-application-components.md): ce sont des objets enregistrés avec des applications et \n  qui fournissent différents services pour satisfaire des requêtes.\n* [modules](structure-modules.md): ce sont des paquets auto-contenus qui contiennent du MVC complet. Une application peut\n  être organisée en de multiples modules.\n* [filtres](structure-filters.md): ils représentent du code qui doit être invoqué avant et après la gestion effective \n  de chaque requête par des contrôleurs.\n* [objets graphiques](structure-widgets.md): ce sont des objets qui peuvent être intégrés dans des [vues](structure-views.md). Ils\n  peuvent contenir de la logique contrôleur et peuvent être réutilisés dans différentes vues.\n\nLe diagramme suivant montre la structure statique d'une application :\n\n![Static Structure of Application](images/application-structure.png)\n"
  },
  {
    "path": "docs/guide-fr/structure-views.md",
    "content": "Vues\n=====\n\nLes vues font partie du modèle d'architecture [MVC](https://fr.wikipedia.org/wiki/Mod%C3%A8le-vue-contr%C3%B4leur) (Modèle Vue Contrôleur).\nElles sont chargées de présenter les données à l'utilisateur final. Dans une application Web, les vues sont ordinairement créées en termes de *modèles de vue* qui sont des script PHP contenant principalement du code HTML et du code PHP relatif à la présentation. \n\nElles sont gérées par le [[yii\\web\\View|view]] [composant application](structure-application-components.md) qui fournit des méthodes d'usage courant pour faciliter la composition des vues et leur rendu. Par souci de simplicité, nous appellerons *vues* les modèles de vue et les fichiers de modèle de vue.\n\n\n## Création des vues <span id=\"creating-views\"></span>\n\nComme nous l'avons dit ci-dessus, une vue n'est rien d'autre qu'un script PHP incluant du code HTML et du code PHP. Le script ci-dessous correspond à la vue d'un formulaire de connexion. Comme vous pouvez le voir le code PHP est utilisé pour générer le contenu dynamique, dont par exemple le titre de la page et le formulaire, tandis que le code HTML les organise en une page présentable. \n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n/**\n * @var \\yii\\web\\View $this\n * @var \\yii\\widgets\\ActiveForm $form\n * @var \\app\\models\\LoginForm $model\n */\n\n$this->title = 'Login';\n?>\n<h1><?= Html::encode($this->title) ?></h1>\n\n<p>Veuillez remplir les champs suivants pour vous connecter:</p>\n\n<?php $form = ActiveForm::begin(); ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n    <?= Html::submitButton('Login') ?>\n<?php ActiveForm::end(); ?>\n```\n\nÀ l'intérieur d'une vue, vous avez accès à `$this` qui fait référence au [[yii\\web\\View|composant view (vue)]] responsable de le gestion et du rendu de ce modèle de vue. \n\nEn plus de `$this`, il peut aussi y avoir d'autres variables prédéfinies dans une vue, telles que `$model` dans l'exemple précédent. Ces variables représentent les données qui sont *poussées* dans la vue par les [contrôleurs](structure-controllers.md) ou par d'autres objets qui déclenche le  [rendu d'une vue](#rendering-views).\n\n> Tip: les variables prédéfinies sont listées dans un bloc de commentaires au début d'une vue de manière à être reconnues par les EDI. C'est également une bonne manière de documenter vos vues. \n\n\n### Sécurité <span id=\"security\"></span>\n\nLors de la création de vues qui génèrent des pages HTML, il est important que vous encodiez et/ou filtriez les données en provenance de l'utilisateur final avant des les présenter. Autrement, votre application serait sujette aux [attaques par injection de scripts (*cross-site scripting*)](https://fr.wikipedia.org/wiki/Cross-site_scripting).\n\nPour afficher du texte simple, commencez par l'encoder en appelant la méthode [[yii\\helpers\\Html::encode()]]. Par exemple, le code suivant encode le nom d'utilisateur (*username*) avant de l'afficher :\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n\n<div class=\"username\">\n    <?= Html::encode($user->name) ?>\n</div>\n```\n\nPour afficher un contenu HTML, utilisez l'objet [[yii\\helpers\\HtmlPurifier]] pour d'abord en filtrer le contenu. Par exemple, le code suivant filtre le contenu de la variable *post* avant de l'afficher :\n\n\n```php\n<?php\nuse yii\\helpers\\HtmlPurifier;\n?>\n\n<div class=\"post\">\n    <?= HtmlPurifier::process($post->text) ?>\n</div>\n```\n\n> Tip: bien que l'objet  HTMLPurifier effectue un excellent travail en rendant vos sorties sûres, il n'est pas rapide. Vous devriez envisager de mettre le résultat en [cache](caching-overview.md) lorsque votre application requiert une performance élevée. \n\n\n### Organisation des vues <span id=\"organizing-views\"></span>\n\nComme les [contrôleurs](structure-controllers.md) et les  [modèles](structure-models.md), il existe certaines conventions pour organiser les vues. \n\n* Pour les vues rendues par un contrôleur, elles devraient être placées par défaut dans le dossier  `@app/views/ControllerID` où `ControllerID` doit être remplacé par l'[identifiant du contrôleur](structure-controllers.md#routes). Par exemple, si la classe du contrôleur est  `PostController`, le dossier est `@app/views/post`; si c'est  `PostCommentController` le dossier est `@app/views/post-comment`. Dans le cas où le contrôleur appartient à un module, le dossier s'appelle `views/ControllerID` et se trouve dans le [[yii\\base\\Module::basePath|dossier de base du module]].\n* Pour les vues rendues dans un [objet graphique](structure-widgets.md), elles devraient être placées par défaut dans le dossier `WidgetPath/views` où  `WidgetPath` est le dossier contenant le fichier de la classe de l'objet graphique. \n* Pour les vues rendues par d'autres objets, il est recommandé d'adopter une convention similaire à celle utilisée pour les objets graphiques. \n\nVous pouvez personnaliser ces dossiers par défaut en redéfinissant la méthode [[yii\\base\\ViewContextInterface::getViewPath()]] des contrôleurs ou des objets graphiques.\n\n\n## Rendu des vues <span id=\"rendering-views\"></span>\n\nVous pouvez rendre les vues dans des [contrôleurs](structure-controllers.md), des [objets graphiques](structure-widgets.md), ou dans d'autres endroits en appelant les méthodes de rendu des vues. Ces méthodes partagent un signature similaire comme montré ci-dessous :\n```\n/**\n * @param string $view nom de la vue ou chemin du fichier, selon la méthode réelle de rendu\n * @param array $params les données injectées dans la vue\n * @return string le résultat du rendu\n */\nmethodName($view, $params = [])\n```\n\n\n### Rendu des vues dans des contrôleurs <span id=\"rendering-in-controllers\"></span>\n\nDans les [contrôleurs](structure-controllers.md), vous pouvez appeler la méthode de contrôleur suivante pour rendre une vue :\n\n* [[yii\\base\\Controller::render()|render()]]: rend une [vue nommée](#named-views) et applique une [disposition](#layouts)\n  au résultat du rendu.\n* [[yii\\base\\Controller::renderPartial()|renderPartial()]]: rend une [vue nommée](#named-views) sans disposition.\n* [[yii\\web\\Controller::renderAjax()|renderAjax()]]: rend une [vue nommée ](#named-views) sans disposition et injecte tous les scripts et fichiers JS/CSS enregistrés. Cette méthode est ordinairement utilisée en réponse à une requête Web AJAX.\n* [[yii\\base\\Controller::renderFile()|renderFile()]]: rend une vue spécifiée en terme de chemin ou d'[alias](concept-aliases.md) de fichier de vue.\n* [[yii\\base\\Controller::renderContent()|renderContent()]]: rend un chaîne statique en l'injectant dans la [disposition](#layouts) courante. Cette méthode est disponible depuis la version 2.0.1.\n\nPar exemple :\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse app\\models\\Post;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\n\nclass PostController extends Controller\n{\n    public function actionView($id)\n    {\n        $model = Post::findOne($id);\n        if ($model === null) {\n            throw new NotFoundHttpException;\n        }\n\n        // rend une vue nommée  \"view\" et lui applique une disposition de page\n        return $this->render('view', [\n            'model' => $model,\n        ]);\n    }\n}\n```\n\n\n### Rendu des vues dans les objets graphiques <span id=\"rendering-in-widgets\"></span>\n\nDans les [objets graphiques](structure-widgets.md), vous pouvez appeler les méthodes suivantes de la classe *widget* pour rendre une vue : \n\n* [[yii\\base\\Widget::render()|render()]]: rend une [vue nommée](#named-views).\n* [[yii\\base\\Widget::renderFile()|renderFile()]]: rend une vue spécifiée en terme de chemin ou d'[alias](concept-aliases.md) de fichier de vue.\n\nPar exemple :\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Widget;\nuse yii\\helpers\\Html;\n\nclass ListWidget extends Widget\n{\n    public $items = [];\n\n    public function run()\n    {\n        // rend  une vue nommée \"list\"\n        return $this->render('list', [\n            'items' => $this->items,\n        ]);\n    }\n}\n```\n\n\n### Rendu des vues dans des vues <span id=\"rendering-in-views\"></span>\n\nVous pouvez rendre une vue dans une autre vue en appelant les méthodes suivantes du  [[yii\\base\\View|composant view]]:\n\n* [[yii\\base\\View::render()|render()]]: rend une [vue nommée](#named-views).\n* [[yii\\web\\View::renderAjax()|renderAjax()]]: rend une  [vue nommée](#named-views) et injecte tous les fichiers et scripts JS/CSS enregistrés. On l'utilise ordinairement en réponse à une requête Web AJAX.\n* [[yii\\base\\View::renderFile()|renderFile()]]: rend une vue spécifiée en terme de chemin ou d'[alias](concept-aliases.md) de fichier de vue.\n\nPar exemple, le code suivant dans une vue, rend le fichier de vue `_overview.php` qui se trouve dans le même dossier que la vue courante. Rappelez-vous que `$this` dans une vue fait référence au composant [[yii\\base\\View|view]] :\n\n```php\n<?= $this->render('_overview') ?>\n```\n\n\n### Rendu de vues en d'autres endroits <span id=\"rendering-in-other-places\"></span>\n\nDans n'importe quel endroit, vous pouvez accéder au composant d'application [[yii\\base\\View|view]] à l'aide de l'expression `Yii::$app->view` et ensuite appeler une de ses méthodes mentionnées plus haut pour rendre une vue. Par exemple :\n\n```php\n// displays the view file \"@app/views/site/license.php\"\necho \\Yii::$app->view->renderFile('@app/views/site/license.php');\n```\n\n\n### Vues nommées <span id=\"named-views\"></span>\n\nLorsque vous rendez une vue, vous pouvez spécifier la vue en utilisant soit un nom de vue, soit un chemin/alias de fichier de vue. Dans la plupart des cas, vous utilisez le nom car il est plus concis et plus souple. Nous appelons les vues spécifiées par leur nom, des *vues nommées*. \n\nUn nom de vue est résolu en le chemin de fichier correspondant en appliquant les règles suivantes : \n\n* Un nom de vue peut omettre l'extension du nom de fichier. Dans ce cas, `.php` est utilisé par défaut en tant qu'extension. Par exemple, le nom de vue  `about` correspond au nom de fichier `about.php`.\n* Si le nom de vue commence par une double barre de division `//`, le chemin de fichier correspondant est `@app/views/ViewName` où `ViewName` est le nom de la vue. Dans ce cas la vue est recherchée dans le dossier [[yii\\base\\Application::viewPath|chemin des vues de l'application]]. Par exemple, `//site/about` est résolu en `@app/views/site/about.php`.\n* Si le nom de la vue commence par une unique barre de division `/`, le chemin de fichier de la vue est formé en préfixant le nom de vue avec [[yii\\base\\Module::viewPath|chemin des vues]] du [module](structure-modules.md) actif courant . Si aucun module n'est actif, `@app/views/ViewName` — où `ViewName` est le nom de la vue — est utilisé. Par exemple, `/user/create` est résolu en `@app/modules/user/views/user/create.php`, si le module actif courant est `user` et en `@app/views/user/create.php`si aucun module n'est actif.\n* Si la vue est rendue avec un  [[yii\\base\\View::context|contexte]] et que le contexte implémente  [[yii\\base\\ViewContextInterface]],le chemin de fichier de vue est formé en préfixant le nom de vue avec le [[yii\\base\\ViewContextInterface::getViewPath()|chemin des vues]] du contexte. Cela s'applique principalement aux vues rendues dans des contrôleurs et dans des objets graphiques. Par exemple,  `about` est résolu en `@app/views/site/about.php` si le contexte est le contrôleur `SiteController`.\n* Si une vue est rendue dans une autre vue, le dossier contenant le nom de la nouvelle vue est préfixé avec le chemin du dossier contenant l'autre vue. Par exemple, la vue `item` est résolue en  `@app/views/post/item.php` lorsqu'elle est rendue dans `@app/views/post/index.php`.\n\nSelon les règles précédentes, l'appel de `$this->render('view')` dans le contrôleur  `app\\controllers\\PostController` rend réellement le fichier de vue `@app/views/post/view.php`, tandis que l'appel de `$this->render('_overview')` dans cette vue rend le fichier de vue `@app/views/post/_overview.php`.\n\n\n### Accès aux données dans les vues <span id=\"accessing-data-in-views\"></span>\n\nIl existe deux approches pour accéder aux données à l'intérieur d'une vue : *pousser* et *tirer*.\n\nEn passant les données en tant que second paramètre des méthodes de rendu de vues, vous utilisez la méthode *pousser*. Les données doivent être présentées sous forme de tableau clé-valeur. Lorsque la vue est rendue, la fonction PHP `extract()` est appelée sur ce tableau pour que le tableau soit restitué sous forme de variables dans la vue. Par exemple, le code suivant de rendu d'une vue dans un contrôleur *pousse* deux variables dans la vue  `report` :\n\n`$foo = 1` et `$bar = 2`.\n\n```php\necho $this->render('report', [\n    'foo' => 1,\n    'bar' => 2,\n]);\n```\n\nL'approche *tirer* retrouve les données de manière plus active à partir du [[yii\\base\\View|composant view]] ou à partir d'autres objets accessibles dans les vues  (p. ex. `Yii::$app`). En utilisant le code exemple qui suit, vous pouvez, dans une vue, obtenir l'objet contrôleur `$this->context`.  Et, en conséquence, il vous est possible d'accéder à n'importe quelle propriété ou méthode du contrôleur, comme la propriété `id` du contrôleur :\n\n```php\nThe controller ID is: <?= $this->context->id ?>\n```\n\nL'approche *pousser* est en général le moyen préféré d'accéder aux données dans les vues, parce qu'elle rend les vues moins dépendantes des objets de contexte. Son revers, et que vous devez construire le tableau de données à chaque fois, ce qui peut devenir ennuyeux et sujet aux erreurs si la vue est rendue en divers endroits. \n\n### Partage de données entre vues <span id=\"sharing-data-among-views\"></span>\n\nLe  [[yii\\base\\View|composant view ]] dispose de la propriété [[yii\\base\\View::params|params]] que vous pouvez utiliser pour partager des données entre vues. \n\nPar exempe, dans une vue  `about` (à propos), vous pouvez avoir le code suivant qui spécifie le segment courant du *fil d'Ariane*. \n\n```php\n$this->params['breadcrumbs'][] = 'About Us';\n```\n\nAinsi, dans le fichier de la [disposition](#layouts), qui est également une vue, vous pouvez afficher le *fil d'Ariane*  en utilisant les données passées par [[yii\\base\\View::params|params]] :\n\n```php\n<?= yii\\widgets\\Breadcrumbs::widget([\n    'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],\n]) ?>\n```\n\n\n## Dispositions <span id=\"layouts\"></span>\n\nLes dispositions (*layouts*) sont des types  spéciaux de vues qui représentent les parties communes de multiples vues. Par exemple, les pages de la plupart des applications Web  partagent le même entête et le même pied de page. Bien que vous puissiez répéter le même entête et le même pied de page dans chacune des vues, il est préférable de le faire une fois dans une disposition et d'inclure le résultat du rendu d'une vue de contenu  à l'endroit approprié de la disposition. \n\n\n### Création de dispositions <span id=\"creating-layouts\"></span>\n\nParce que les dispositions sont aussi des vues, elles peuvent être créées de manière similaire aux vues ordinaires. Par défaut, les dispositions sont stockées dans le dossier `@app/views/layouts`. Les dispositions utilisées dans un [module](structure-modules.md) doivent être stockées dans le dossier `views/layouts` du [[yii\\base\\Module::basePath|dossier de base du module]]. Vous pouvez personnaliser le dossier par défaut des dispositions en configurant la propriété [[yii\\base\\Module::layoutPath]] de l'application ou du module.\n\nL'exemple qui suit montre à quoi ressemble une disposition. Notez que dans un but illustratif, nous avons grandement simplifié le code à l'intérieur de cette disposition. En pratique, vous désirerez ajouter à ce code plus de contenu, comme des balises head, un menu principal, etc. \n\n```php\n<?php\nuse yii\\helpers\\Html;\n\n/**\n * @var \\yii\\web\\View $this\n * @var string $content\n */\n?>\n<?php $this->beginPage() ?>\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\"/>\n    <?= Html::csrfMetaTags() ?>\n    <title><?= Html::encode($this->title) ?></title>\n    <?php $this->head() ?>\n</head>\n<body>\n<?php $this->beginBody() ?>\n    <header>My Company</header>\n    <?= $content ?>\n    <footer>&copy; 2014 by My Company</footer>\n<?php $this->endBody() ?>\n</body>\n</html>\n<?php $this->endPage() ?>\n```\n\nComme vous pouvez le voir, la disposition génère les balises HTML qui sont communes à toutes les pages. Dans la section `<body>` la disposition rend  la variable `$content` qui représente le résultat de rendu d'une vue de contenu qui est poussée dans la disposition par l'appel à la fonction [[yii\\base\\Controller::render()]].\n\nLa plupart des dispositions devraient appeler les méthodes suivantes, comme illustré dans l'exemple précédent. Ces méthodes déclenchent essentiellement des événements concernant le processus de rendu de manière à ce que des balises et des scripts enregistrés dans d'autres endroits puissent être injectés à l'endroit où ces méthodes sont appelées. \n\n- [[yii\\base\\View::beginPage()|beginPage()]]: cette méthode doit être appelée au tout début de la disposition. Elle déclenche l'événement  [[yii\\base\\View::EVENT_BEGIN_PAGE|EVENT_BEGIN_PAGE]] qui signale le début d'une page.\n- [[yii\\base\\View::endPage()|endPage()]]: cette méthode doit être appelée à la fin de la disposition. Elle déclenche l'événement [[yii\\base\\View::EVENT_END_PAGE|EVENT_END_PAGE]] qui signale la fin d'une page.\n- [[yii\\web\\View::head()|head()]]: cette méthode doit être appelée dans la section `<head>` d'une page HTML. Elle génère une valeur à remplacer qui sera remplacée par le code d'entête HTML (p. ex. des balises liens, des balises meta, etc.) lorsqu'une page termine son processus de rendu. \n- [[yii\\web\\View::beginBody()|beginBody()]]: cette méthode doit être appelée au début de la section `<body>`. Elle déclenche l'événement [[yii\\web\\View::EVENT_BEGIN_BODY|EVENT_BEGIN_BODY]] et génère une valeur à remplacer qui sera remplacée par le code HTML enregistré (p. ex. Javascript) dont la cible est le début du corps de la page. \n- [[yii\\web\\View::endBody()|endBody()]]: cette méthode doit être appelée à la fin de la section `<body>`. Elle déclenche l'événement [[yii\\web\\View::EVENT_END_BODY|EVENT_END_BODY]] et génère une valeur à remplacer qui sera remplacée par le code HTML enregistré (p. ex. Javascript) dont la cible est la fin du corps de la page.\n\n### Accès aux données dans les dispositions <span id=\"accessing-data-in-layouts\"></span>\n\nDans une disposition, vous avez accès à deux variables prédéfinies : `$this` et `$content`. La première fait référence au composant [[yii\\base\\View|view]], comme dans les vues ordinaires, tandis que la seconde contient le résultat de rendu d'une vue de contenu qui est rendue par l'appel de la méthode  [[yii\\base\\Controller::render()|render()]] dans un contrôleur.\n\nSi vous voulez accéder à d'autres données dans les dispositions, vous devez utiliser l'approche *tirer* comme c'est expliqué à la sous-section  [Accès aux données dans les vues](#accessing-data-in-views). Si vous voulez passer des données d'une vue de contenu à une disposition, vous pouvez utiliser la méthode décrite à la sous-section [Partage de données entre vues](#sharing-data-among-views).\n\n\n### Utilisation des dispositions <span id=\"using-layouts\"></span>\n\nComme c'est décrit à la sous-section [Rendu des vues dans les contrôleurs](#rendering-in-controllers), lorsque vous rendez une vue en appelant la méthode  [[yii\\base\\Controller::render()|render()]] dans un contrôleur, une disposition est appliquée au résultat du rendu. Par défaut, la disposition  `@app/views/layouts/main.php` est utilisée. \n\nVous pouvez utiliser une disposition différente en configurant soit [[yii\\base\\Application::layout]], soit [[yii\\base\\Controller::layout]]. La première gouverne la disposition utilisée par tous les contrôleurs, tandis que la deuxième redéfinit la première pour les contrôleurs individuels. Par exemple, le code suivant fait que le contrôleur `post` utilise `@app/views/layouts/post.php` en tant qu disposition lorsqu'il rend ses vues. Les autres contrôleurs, en supposant que leur propriété  `layout` n'est pas modifiée, continuent d'utiliser la disposition par défaut `@app/views/layouts/main.php`.\n \n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass PostController extends Controller\n{\n    public $layout = 'post';\n    \n    // ...\n}\n```\n\nPour les contrôleurs appartenant à un module ,vous pouvez également configurer la propriété  [[yii\\base\\Module::layout|layout]] pour utiliser une disposition particulière pour ces contrôleurs. \n\nComme la propriété `layout` peut être configurée à différents niveaux (contrôleurs, modules, application), en arrière plan, Yii opère en deux étapes pour déterminer quel est le fichier de disposition réel qui doit être utilisé pour un contrôleur donné.  \n\nDans la première étape, il détermine la valeurs de la disposition et le module du contexte :\n\n- Si la propriété  [[yii\\base\\Controller::layout]] du contrôleur n'est pas nulle, il l'utilise en tant que valeur de disposition et le [[yii\\base\\Controller::module|module]] du contrôleur en tant que module du contexte. \n- Si  la propriété [[yii\\base\\Controller::layout|layout]] est nulle, il cherche, à travers tous les modules ancêtres (y compris l'application elle-même) du contrôleur, le premier module dont la propriété [[yii\\base\\Module::layout|layout]] n'est pas nulle. Il utilise alors ce module et la valeur de sa  [[yii\\base\\Module::layout|disposition]] comme module du contexte et valeur de disposition, respectivement. Si un tel module n'est pas trouvé, cela signifie qu'aucune disposition n'est appliquée. \n  \nDans la seconde étape, il détermine le fichier de disposition réel en fonction de la valeur de disposition et du module du contexte déterminés dans la première étape. La valeur de disposition peut être :\n\n- Un alias de chemin (p. ex. `@app/views/layouts/main`).\n- Un chemin absolu (p. ex. `/main`): la valeur de disposition commence par une barre oblique de division. Le fichier réel de disposition est recherché dans le [[yii\\base\\Application::layoutPath|chemin des disposition (*layoutPath*)]] (par défaut `@app/views/layouts`).\n- Un chemin relatif (p. ex. `main`): le fichier réel de disposition est recherché dans le [[yii\\base\\Module::layoutPath|chemin des dispositions (*layoutPath*)]] du module du contexte (par défaut`views/layouts`) dans le [[yii\\base\\Module::basePath|dossier de base du module]].\n- La valeur booléenne `false`: aucune disposition n'est appliquée.\n\nSi la valeur de disposition ne contient pas d'extension de fichier, l'extension par défaut `.php` est utilisée. \n\n\n### Dispositions imbriquées <span id=\"nested-layouts\"></span>\n\nParfois, vous désirez imbriquer une disposition dans une autre. Par exemple, dans les différentes sections d'un site Web, vous voulez utiliser des dispositions différentes, bien que ces dispositions partagent la même disposition de base qui génère la structure d'ensemble des pages HTML5. Vous pouvez réaliser cela en appelant la méthode [[yii\\base\\View::beginContent()|beginContent()]] et la méthode \n[[yii\\base\\View::endContent()|endContent()]] dans les dispositions filles comme illustré ci-après :\n```php\n<?php $this->beginContent('@app/views/layouts/base.php'); ?>\n\n...contenu de la disposition fille ici...\n\n<?php $this->endContent(); ?>\n```\n\nComme on le voit ci-dessus, le contenu de la disposition fille doit être  situé entre les appels des méthodes [[yii\\base\\View::beginContent()|beginContent()]] et [[yii\\base\\View::endContent()|endContent()]]. Le paramètre passé à la méthode [[yii\\base\\View::beginContent()|beginContent()]] spécifie quelle est la disposition parente. Ce peut être un fichier de disposition ou un alias. En utilisant l'approche ci-dessus, vous pouvez imbriquer des dispositions sur plusieurs niveaux. \n\n\n\n### Utilisation des blocs <span id=\"using-blocks\"></span>\n\nLes blocs vous permettent de spécifier le contenu de la vue à un endroit et l'afficher ailleurs. Ils sont souvent utilisés conjointement avec les dispositions. Par exemple, vous pouvez définir un bloc dans une vue de contenu et l'afficher dans la disposition. \n\nPour définir un bloc, il faut appeler les méthodes  [[yii\\base\\View::beginBlock()|beginBlock()]] et [[yii\\base\\View::endBlock()|endBlock()]]. Vous pouvez accéder au bloc via son identifiant avec `$view->blocks[$blockID]`, où `$blockID` représente l'identifiant unique que vous assignez au bloc lorsque vous le définissez. \n\nL'exemple suivant montre comment utiliser les blocs pour personnaliser des parties spécifiques dans la disposition d'une vue de contenu. \n\nTout d'abord, dans une vue de contenu, définissez un ou de multiples blocs :\n\n```php\n...\n\n<?php $this->beginBlock('block1'); ?>\n\n...contenu de block1...\n\n<?php $this->endBlock(); ?>\n\n...\n\n<?php $this->beginBlock('block3'); ?>\n\n...contenu de block3...\n\n<?php $this->endBlock(); ?>\n```\n\nEnsuite, dans la vue de la disposition, rendez les blocs s'ils sont disponibles, ou affichez un contenu par défaut si le bloc n'est pas défini. \n\n```php\n...\n<?php if (isset($this->blocks['block1'])): ?>\n    <?= $this->blocks['block1'] ?>\n<?php else: ?>\n    ... contenu par défaut de  block1 ...\n<?php endif; ?>\n\n...\n\n<?php if (isset($this->blocks['block2'])): ?>\n    <?= $this->blocks['block2'] ?>\n<?php else: ?>\n    ... contenu par défaut de block2 ...\n<?php endif; ?>\n\n...\n\n<?php if (isset($this->blocks['block3'])): ?>\n    <?= $this->blocks['block3'] ?>\n<?php else: ?>\n    ... contenu par défaut de  block3 ...\n<?php endif; ?>\n...\n```\n\n\n## Utilisation des composants view <span id=\"using-view-components\"></span>\n\nLes composants [[yii\\base\\View|view]] fournissent de nombreuses fonctionnalités relatives aux vues. Bien que vous puissiez créer des composants *view* en créant des instances de la classe [[yii\\base\\View]] ou de ses classes filles, dans la plupart des cas, vous utilisez principalement le composant d'application `view` . Vous pouvez configurer ce composant dans les [configuration d'application](structure-applications.md#application-configurations), comme l'illustre l'exemple qui suit :\n\n```php\n[\n    // ...\n    'components' => [\n        'view' => [\n            'class' => 'app\\components\\View',\n        ],\n        // ...\n    ],\n]\n```\n\nLes composants View fournissent les fonctionnalités utiles suivantes relatives aux vues, chacune décrite en détails dans une section séparée :\n\n* [gestion des thèmes](output-theming.md): vous permet des développer et de changer les thèmes pour votre site Web.\n* [mise en cache de fragments](caching-fragment.md): vous permet de mettre en cache un fragment de votre page Web.\n* [gestion des scripts client](output-client-scripts.md): prend en charge l'enregistrement et le rendu de code CSS et JavaScript. \n* [gestion de paquets de ressources](structure-assets.md): prend en charge l'enregistrement et le rendu de [paquets de ressources](structure-assets.md).\n* [moteurs de modèle alternatif](tutorial-template-engines.md): vous permet d'utiliser d'autres moteur de modèles tels que [Twig](https://twig.symfony.com/) et [Smarty](https://www.smarty.net/).\n\nVous pouvez aussi utiliser les fonctionnalités suivantes qui, bien que mineures, sont néanmoins utiles, pour développer vos pages Web. \n\n\n### Définition du titre des pages <span id=\"setting-page-titles\"></span>\n\nChaque page Web doit avoir un titre. Normalement la balise titre est affichée dans une  [disposition](#layouts). Cependant, en pratique, le titre est souvent déterminé dans les vues de contenu plutôt que dans les dispositions. Pour résoudre ce problème,[[yii\\web\\View]] met à votre disposition la propriété [[yii\\web\\View::title|title]] qui vous permet de passer l'information de titre de la vue de contenu à la disposition.\n\nPour utiliser cette fonctionnalité, dans chacune des vues de contenu, vous pouvez définir le titre de la page de la manière suivante :\n```php\n<?php\n$this->title = 'Le titre de ma page';\n?>\n```\n\nEnsuite dans la disposition, assurez-vous qui vous avez placé le code suivant dans la section `<head>` :\n\n```php\n<title><?= Html::encode($this->title) ?></title>\n```\n\n\n### Enregistrement des balises \"meta\" <span id=\"registering-meta-tags\"></span>\n\nGénéralement, les pages Web, ont besoin de générer des balises \"meta\" variées dont ont besoin diverses parties. Comme le titre des pages, les balises \"meta\" apparaissent dans la section `<head>` et sont généralement générées dans les dispositions.\n\nSi vous désirez spécifier quelles balises \"meta\" générer dans les vues de contenu, vous pouvez appeler [[yii\\web\\View::registerMetaTag()]] dans une vue de contenu comme illustrer ci-après :\n\n```php\n<?php\n$this->registerMetaTag(['name' => 'keywords', 'content' => 'yii, framework, php']);\n?>\n```\n\nLe code ci-dessus enregistre une balise \"meta\" \"mot clé\" dans le composant view. La balise \"meta\" enregistrée est rendue après que le rendu de la disposition est terminé. Le code HTML suivant est généré et inséré à l'endroit où vous appelez  [[yii\\web\\View::head()]] dans la disposition :\n\n```php\n<meta name=\"keywords\" content=\"yii, framework, php\">\n```\n\nNotez que si vous appelez [[yii\\web\\View::registerMetaTag()]] à de multiples reprises, elle enregistrera de multiples balises meta, que les balises soient les mêmes ou pas.\n\nPour garantir qu'il n'y a qu'une instance d'un type de balise meta, vous pouvez spécifier une clé en tant que deuxième paramètre lors de l'appel de la méthode. \nPar exemple, le code suivant, enregistre deux balises \"meta\" « description ». Cependant, seule la seconde sera rendue. \nF\n\n```php\n$this->registerMetaTag(['name' => 'description', 'content' => 'This is my cool website made with Yii!'], 'description');\n$this->registerMetaTag(['name' => 'description', 'content' => 'This website is about funny raccoons.'], 'description');\n```\n\n\n### Enregistrement de balises liens <span id=\"registering-link-tags\"></span>\n\nComme les [balises meta](#registering-meta-tags), les balises liens sont utiles dans de nombreux cas, comme la personnalisation de favicon, le pointage sur les flux RSS ou la délégation d'OpenID à un autre serveur. Vous pouvez travailler avec les balises liens comme avec les balises \"meta\" en utilisant [[yii\\web\\View::registerLinkTag()]]. Par exemple, dans une vue de contenu, vous pouvez enregistrer une balise lien de la manière suivante :\n\n```php\n$this->registerLinkTag([\n    'title' => 'Live News for Yii',\n    'rel' => 'alternate',\n    'type' => 'application/rss+xml',\n    'href' => 'https://www.yiiframework.com/rss.xml/',\n]);\n```\n\nLe code suivant produit le résultat suivant :\n\n```html\n<link title=\"Live News for Yii\" rel=\"alternate\" type=\"application/rss+xml\" href=\"https://www.yiiframework.com/rss.xml/\">\n```\n\nComme avec  [[yii\\web\\View::registerMetaTag()|registerMetaTag()]], vous pouvez spécifier un clé lors de l'appel de [[yii\\web\\View::registerLinkTag()|registerLinkTag()]] pour éviter de générer des liens identiques.\n\n\n## Événements de vues <span id=\"view-events\"></span>\n\nLes [[yii\\base\\View|composants View ]] déclenchent plusieurs événements durant le processus de rendu des vues. Vous pouvez répondre à ces événements pour injecter du contenu dans des vues ou traiter les résultats du rendu avant leur transmission à l'utilisateur final. \n\n- [[yii\\base\\View::EVENT_BEFORE_RENDER|EVENT_BEFORE_RENDER]]: déclenché au début du rendu d'un fichier dans un contrôleur. Les gestionnaires de cet événement peuvent définir [[yii\\base\\ViewEvent::isValid]] à `false` (faux) pour arrêter le processus de rendu. \n- [[yii\\base\\View::EVENT_AFTER_RENDER|EVENT_AFTER_RENDER]]: déclenché après le rendu d'un fichier par appel de [[yii\\base\\View::afterRender()]]. Les gestionnaires de cet événement peuvent obtenir le résultat du rendu via  [[yii\\base\\ViewEvent::output]] et peuvent modifier cette propriété pour modifier le résultat du rendu.\n- [[yii\\base\\View::EVENT_BEGIN_PAGE|EVENT_BEGIN_PAGE]]: déclenché par l'appel de [[yii\\base\\View::beginPage()]] dans une disposition.\n- [[yii\\base\\View::EVENT_END_PAGE|EVENT_END_PAGE]]: déclenché par l'appel de [[yii\\base\\View::endPage()]] dans une disposition.\n- [[yii\\web\\View::EVENT_BEGIN_BODY|EVENT_BEGIN_BODY]]: déclenché par l'appel de [[yii\\web\\View::beginBody()]] dans une disposition.\n- [[yii\\web\\View::EVENT_END_BODY|EVENT_END_BODY]]: déclenché par l'appel de [[yii\\web\\View::endBody()]] dans une disposition.\n\nPar exemple, le code suivant injecte la date courante à la fin du corps de la page.\n\n```php\n\\Yii::$app->view->on(View::EVENT_END_BODY, function () {\n    echo date('Y-m-d');\n});\n```\n\n\n## Rendu des pages statiques <span id=\"rendering-static-pages\"></span>\n\nLes pages statiques font références aux pages dont le contenu principal est essentiellement statique sans recours à des données dynamiques poussées par les contrôleurs. \n\nVous pouvez renvoyer des pages statiques en plaçant leur code dans des vues, et en utilisant un code similaire à ce qui suit dans un contrôleur :\n\n```php\npublic function actionAbout()\n{\n    return $this->render('about');\n}\n```\n\nSi un site Web contient beaucoup de pages statiques, ce serait très ennuyeux de répéter un code similaire de nombreuses fois. Pour résoudre ce problème, vous pouvez introduire une  [action autonome](structure-controllers.md#standalone-actions) appelée  [[yii\\web\\ViewAction]] dans un contrôleur. Par exemple :\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actions()\n    {\n        return [\n            'page' => [\n                'class' => 'yii\\web\\ViewAction',\n            ],\n        ];\n    }\n}\n```\nMaintenant, si vous créez une vue nommée `about` dans le dossier `@app/views/site/pages`, vous pourrez afficher cette vue via l'URL suivante :\n```\nhttp://localhost/index.php?r=site%2Fpage&view=about\n```\n\nLe paramètre `view` de la méthode  `GET` dit à [[yii\\web\\ViewAction]] quelle est la vue requise. L'action recherche alors cette vue dans le dossier `@app/views/site/pages`. Vous pouvez configurer la propriété [[yii\\web\\ViewAction::viewPrefix]] pour changer le dossier dans lequel la vue est recherchée.\n\n\n## Meilleures pratiques <span id=\"best-practices\"></span>\n\nLes vues sont chargées de présenter les modèles dans le format désiré par l'utilisateur final. En général :\n\n* Elles devraient essentiellement contenir du code relatif à la présentation, tel que le code HTML, du code PHP simple pour parcourir, formater et rendre les données. \n* Elles ne devraient pas contenir de code qui effectue des requêtes de base de données. Un tel code devrait être placé dans les modèles.\n* Elles devraient éviter d'accéder directement aux données de la requête, telles que `$_GET`, `$_POST`. C'est le rôle des contrôleurs.  Si les données de la requête sont nécessaires, elles devraient être poussées dans les vues par les contrôleurs.\n* Elles peuvent lire les propriétés des modèles, mais ne devraient pas les modifier.\n\nPour rendre les vues plus gérables, évitez de créer des vues qui sont trop complexes ou qui contiennent trop de code redondant. Vous pouvez utiliser les techniques suivantes pour atteindre cet but :\n\n* Utiliser des [dispositions](#layouts) pour représenter les sections communes de présentation (p. ex. l'entête de page, le pied de page). \n* Diviser une vue complexe en plusieurs vues plus réduites. Les vues plus réduites peuvent être rendue et assemblées dans une plus grande en utilisant les méthodes de rendu que nous avons décrites. \n* Créer et utiliser des [objets graphiques](structure-widgets.md) en tant que blocs de construction des vues.\n* Créer et utiliser des classes d'aide pour transformer et formater les données dans les vues.\n\n"
  },
  {
    "path": "docs/guide-fr/structure-widgets.md",
    "content": "Objets graphiques\n==============================\n\nLes objets graphiques (*widgets*) sont des blocs de construction réutilisables dans des [vues](structure-views.md) pour créer des éléments d'interface utilisateur complexes et configurables d'une manière orientée objet. Par exemple, un composant d'interface graphique de sélection de date peut générer un sélecteur de date original qui permet aux utilisateurs de sélectionner une date en tant qu'entrée. Tout ce que vous avez besoin de faire, c'est d'insérer le code dans une vue comme indiqué ci-dessous :\n\n```php\n<?php\nuse yii\\jui\\DatePicker;\n?>\n<?= DatePicker::widget(['name' => 'date']) ?>\n```\n\nIl existe un grand nombre d'objets graphiques fournis avec Yii, tels que les[[yii\\widgets\\ActiveForm|active form]], [[yii\\widgets\\Menu|menu]], [jQuery UI widgets](https://www.yiiframework.com/extension/yiisoft/yii2-jui), [Twitter Bootstrap widgets](https://www.yiiframework.com/extension/yiisoft/yii2-bootstrap). Dans ce qui suit, nous introduisons les connaissances de base sur les objets graphiques. Reportez-vous à la documentation de la classe dans l'API si vous désirez en apprendre davantage sur un objet graphique particulier. \n\n\n## Utilisation des objets graphiques <span id=\"using-widgets\"></span>\n\nLes objets graphiques sont utilisés en premier lieu dans des [vues](structure-views.md). Vous pouvez appeler la méthode [[yii\\base\\Widget::widget()]] pour utiliser un objet graphique dans une vue. Cette méthode accepte un tableau de [configuration](concept-configurations.md) pour initialiser l'objet graphique d'interface et retourne le résultat du rendu de cet objet. Par exemple, le code suivant insère un objet graphique de sélection de date  qui est configuré dans la langue *russe* et conserve l'entrée dans l'attribut  `from_date` du `$model`.\n\n```php\n<?php\nuse yii\\jui\\DatePicker;\n?>\n<?= DatePicker::widget([\n    'model' => $model,\n    'attribute' => 'from_date',\n    'language' => 'ru',\n    'dateFormat' => 'php:Y-m-d',\n]) ?>\n```\n\nQuelques objets graphiques peuvent accepter un bloc de contenu qui doit être compris entre l'appel des méthodes [[yii\\base\\Widget::begin()]] et  [[yii\\base\\Widget::end()]]. Par exemple, le code suivant utilise l'objet graphique [[yii\\widgets\\ActiveForm]] pour générer une ouverture de balise `<form>` à l'endroit de l'appel de  `begin()` et une  fermeture de la même balise à l'endroit de l'appel de `end()`. Tout ce qui se trouve entre les deux est rendu tel quel. \n\n```php\n<?php\nuse yii\\widgets\\ActiveForm;\nuse yii\\helpers\\Html;\n?>\n\n<?php $form = ActiveForm::begin(['id' => 'login-form']); ?>\n\n    <?= $form->field($model, 'username') ?>\n\n    <?= $form->field($model, 'password')->passwordInput() ?>\n\n    <div class=\"form-group\">\n        <?= Html::submitButton('Login') ?>\n    </div>\n\n<?php ActiveForm::end(); ?>\n```\n\nNotez que contrairement à  la méthode [[yii\\base\\Widget::widget()]] qui retourne le résultat du rendu d'un objet graphique, la méthode [[yii\\base\\Widget::begin()]] retourne une instance de l'objet graphique que vous pouvez utiliser pour construire le contenu de l'objet d'interface. \n\n> Note: quelques objets graphiques utilisent [la mise en tampon de sortie](https://www.php.net/manual/fr/book.outcontrol.php) \n> pour ajuster le contenu inclus quand la méthode [[yii\\base\\Widget::end()]] est appelée. \n> Pour cette raison, l'appel des méthodes [[yii\\base\\Widget::begin()]] et\n> [[yii\\base\\Widget::end()]]  est attendu dans le même fichier de vue.\n> Ne pas respecter cette règle peut conduire à des résultats inattendus. \n\n\n### Configuration des variables globales par défaut\n\nLes variables globales par défaut pour un objet graphique peuvent être configurées via le conteneur d'injection de dépendances (*DI container*) :\n\n```php\n\\Yii::$container->set('yii\\widgets\\LinkPager', ['maxButtonCount' => 5]);\n```\n\nVoir  la section [\"Utilisation pratique \" dans le Guide du conteneur d'injection de dépendances](concept-di-container.md#practical-usage) pour les détails.\n\n\n## Création d'objets graphiques <span id=\"creating-widgets\"></span>\n\nPour créer un objet graphique, étendez la classe [[yii\\base\\Widget]] et redéfinissez sa méthode  [[yii\\base\\Widget::init()]] et/ou sa méthode [[yii\\base\\Widget::run()]]. Ordinairement, la méthode `init()` devrait contenir le code qui initialise les propriétés de l'objet graphique, tandis que la méthode `run()` devrait contenir le  code qui génère le résultat du rendu de cet objet graphique. Le résultat du rendu peut être \"renvoyé en écho\" directement ou retourné comme une chaîne de caractères par la méthode `run()`.\n\nDans l'exemple suivant, `HelloWidget` encode en HTML et affiche le contenu assigné à sa propriété `message`.\nSi la propriété n'est pas définie, il affiche  \"Hello World\" par defaut.\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Widget;\nuse yii\\helpers\\Html;\n\nclass HelloWidget extends Widget\n{\n    public $message;\n\n    public function init()\n    {\n        parent::init();\n        if ($this->message === null) {\n            $this->message = 'Hello World';\n        }\n    }\n\n    public function run()\n    {\n        return Html::encode($this->message);\n    }\n}\n```\n\nPour utiliser cet objet graphique, contentez-vous d'insérer le code suivant dans une vue :\n\n```php\n<?php\nuse app\\components\\HelloWidget;\n?>\n<?= HelloWidget::widget(['message' => 'Good morning']) ?>\n```\n\nCe-dessous, nous présentons une variante de  `HelloWidget` qui prend le contenu inséré entre les appels des méthodes `begin()` et `end()`, l'encode en HTML et l'affiche.\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Widget;\nuse yii\\helpers\\Html;\n\nclass HelloWidget extends Widget\n{\n    public function init()\n    {\n        parent::init();\n        ob_start();\n    }\n\n    public function run()\n    {\n        $content = ob_get_clean();\n        return Html::encode($content);\n    }\n}\n```\n\nComme vous pouvez le voir, le tampon de sortie de PHP est démarré dans `init()` de manière à ce que toute sortie entre les appels de `init()` et de  `run()`\npuisse être capturée, traitée et retournée dans `run()`.\n\n> Info: lorsque vous appelez [[yii\\base\\Widget::begin()]], une nouvelle instance de l'objet graphique est créé et sa méthode  `init()` est appelée à la fin de la construction de l'objet. Lorsque vous appelez [[yii\\base\\Widget::end()]], la méthode  `run()` est appelée et sa valeur de retour est renvoyée en écho par `end()`.\n\nLe code suivant montre comment utiliser cette nouvelle variante de  `HelloWidget`:\n\n```php\n<?php\nuse app\\components\\HelloWidget;\n?>\n<?php HelloWidget::begin(); ?>\n\n    content that may contain <tag>'s\n\n<?php HelloWidget::end(); ?>\n```\n\nParfois, un objet graphique peut avoir à rendre un gros bloc de contenu. Bien que vous puissiez incorporer le contenu dans la méthode `run()`, une meilleure approche consiste à le mettre dans une  [vue](structure-views.md) et à appeler la méthode [[yii\\base\\Widget::render()]] pour obtenir son rendu. Par exemple :\n\n```php\npublic function run()\n{\n    return $this->render('hello');\n}\n```\n\nPar défaut, les vues pour un objet graphique doivent être stockées dans le dossier `WidgetPath/views`, où `WidgetPath` représente le dossier contenant la classe de l'objet graphique. Par conséquent, l'exemple ci-dessus rend le fichier de vue `@app/components/views/hello.php`, en supposant que la classe de l'objet graphique est située dans le dossier `@app/components`. Vous pouvez redéfinir la méthode [[yii\\base\\Widget::getViewPath()]] pour personnaliser le dossier qui contient les fichiers de vue des objets graphiques. \n\n\n## Meilleures pratiques <span id=\"best-practices\"></span>\n\nLes objets graphiques sont une manière orientée objets de réutiliser du code de vues. \n\nLors de la création d'objets graphiques, vous devriez continuer de suivre le modèle d'architecture MVC. En général, vous devriez conserver la logique dans les classes d'objets graphiques et la présentation dans les [vues](structure-views.md).\n\nLes objets graphiques devraient également être conçus pour être auto-suffisants. Cela veut dire que, lors de l'utilisation d'un tel objet, vous devriez être en mesure de vous contenter de le placer dans une vue sans rien faire d'autre. Cela peut s'avérer délicat si un objet graphique requiert des ressources externes, comme du CSS, du JavaScript, des images, etc. Heureusement, Yii fournit une prise en charge des [paquets de ressources](structure-assets.md) que vous pouvez utiliser pour résoudre le problème. \n\n\nQuand un objet graphique contient du code de vue seulement, il est très similaire à une [vue](structure-views.md). En fait, dans ce cas, la seule différence est que l'objet graphique est une classe distribuable, tandis qu'une vue est juste un simple script PHP que vous préférez conserver dans votre application. \n"
  },
  {
    "path": "docs/guide-fr/translators.json",
    "content": "[\n  \"Benoît\",\n  \"Kevin LEVRON\",\n  \"José FOURNIER\"\n]\n"
  },
  {
    "path": "docs/guide-fr/tutorial-i18n.md",
    "content": "Internationalisation\n====================\n\nLe terme *Internationalisation* (I18N) fait référence au processus de conception d'une application logicielle qui permet son adaptation à diverses langues et régions sans intervenir dans le code. Pour des applications Web, la chose est particulièrement importante puisque celle-ci peut concerner des utilisateurs potentiels répartis sur toute la surface de la terre. Yii met à votre disposition tout un arsenal de fonctionnalités qui prennent en charge la traduction des messages et des vues, ainsi que le formatage des nombres et des dates. \n\n\n## Locale et Langue <span id=\"locale-language\"></span>\n\nUne *locale* est un jeu de paramètres qui définissent la langue de l'utilisateur, son pays et des préférences spéciales que celui-ci désire voir dans l'interface utilisateur. \n\nElle est généralement identifiée par un identifiant (ID), lui-même constitué par un identifiant de langue et un identifiant de région. Par exemple, l'identifiant `en-US` représente la locale *anglais* pour la langue et   *États-Unis* pour la région. \n\nPour assurer la cohérence, tous les identifiants utilisés par les application Yii doivent être présentés sous leur forme canonique `ll-CC`, où `ll` est un code à 2 ou 3 lettres pour la langue conforme à la norme [ISO-639](https://www.loc.gov/standards/iso639-2/) et `CC` est un code à deux lettres pour le pays conforme à la norme [ISO-3166](https://www.iso.org/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html).\nPour plus de détails sur les locales, reportez-vous à la [documentation du projet ICU](https://unicode-org.github.io/icu/userguide/locale/#the-locale-concept).\n\nDans Yii, nous utilisons souvent le mot « langue » pour faire référence à la locale. \n\nUne application Yii utilise deux langues :  la [[yii\\base\\Application::$sourceLanguage|langue source ]] et la [[yii\\base\\Application::$language|langue cible]]. La première fait référence à la langue dans laquelle les messages sont rédigés dans le code source, tandis que la deuxième est celle qui est utilisée pour présenter les textes à l'utilisateur final.\nPour l'essentiel, le service appelé *message translation service*(service de traduction des messages) assure la traduction d'un message textuel de la langue source vers la langue cible. \n\nVous pouvez configurer les langues de l'application dans la configuration de la manière suivante :\n\n\n```php\nreturn [\n    // définit la langue cible comme étant le français-France\n    'language' => 'fr-FR',\n    \n    // définit la langue source comme étant l'anglais États-Unis\n    'sourceLanguage' => 'en-US',\n    \n    ......\n];\n```\n\nLa valeur par défaut pour la [[yii\\base\\Application::$sourceLanguage|langue source]] est `en-US`, qui signifie « anglais États-Unis ». Il est recommandé de conserver cette valeur sans la changer car il est généralement plus facile de trouver des gens capables de traduire de l'anglais vers d'autres langues que d'une langue non anglaise vers une autre langue. \n\nIl est souvent nécessaire de définir la [[yii\\base\\Application::$language|langue cible]] de façon dynamique en se basant sur différents facteurs tels que, par exemple, les préférences linguistiques de l'utilisateur final. Au lieu de la définir dans la configuration de l'application  vous pouvez utiliser l'instruction suivante pour changer la langue cible :\n\n```php\n// modifier la langue cible pour qu'elle soit français-FRANCE \n\\Yii::$app->language = 'fr-FR';\n```\n\n> Tip: si votre langue source change selon les différentes parties de votre code, vous pouvez modifier la valeur de la langue source localement comme c'est expliqué dans la section suivante.\n\n## Traduction des messages <span id=\"message-translation\"></span>\n\nLe service de traduction des messages traduit un message textuel d'une langue (généralement la [[yii\\base\\Application::$sourceLanguage|langue source]]) vers une autre langue  (généralement la [[yii\\base\\Application::$language|langue cible]]). Il effectue la traduction en recherchant le message à traduire dans une source de messages qui stocke les messages originaux et les messages traduits. \nSi le message est trouvé, le message traduit correspondant est renvoyé ; dans le cas contraire, le message original est renvoyé sans traduction. \n\nPour utiliser le service de traduction des messages, vous devez principalement effectuer les opérations suivantes :\n\n* Envelopper le message textuel à traduire dans un appel à la méthode [[Yii::t()]] ;\n* Configurer une ou plusieurs sources de messages dans lesquelles le service de traduction des messages peut rechercher des traductions ; \n* Confier aux traducteurs le soin de traduire les messages et de les stocker dans les sources de messages. \n\nLa méthode [[Yii::t()]] peut être utilisée comme le montre l'exemple suivant :\n\n```php\necho \\Yii::t('app', 'This is a string to translate!');\n```\n\noù le deuxième paramètre fait référence au message textuel à traduire, tandis que le premier paramètre fait référence au nom de la catégorie à laquelle le message appartient. \n\nLa méthode [[Yii::t()]] appelle la méthode `translate` du [composant d'application](structure-application-components.md) `i18n` pour assurer le travail réel de traduction. Le composant peut être configuré dans la configuration de l'application de la manière suivante :\n\n```php\n'components' => [\n    // ...\n    'i18n' => [\n        'translations' => [\n            'app*' => [\n                'class' => 'yii\\i18n\\PhpMessageSource',\n                //'basePath' => '@app/messages',\n                //'sourceLanguage' => 'en-US',\n                'fileMap' => [\n                    'app' => 'app.php',\n                    'app/error' => 'error.php',\n                ],\n            ],\n        ],\n    ],\n],\n```\n\nDans le code qui précède, une source de  messages prise en charge par  [[yii\\i18n\\PhpMessageSource]] est configurée. Le motif `app*` indique que toutes les catégories de messages dont les noms commencent par `app` doivent être traduites en utilisant cette source de messages. La classe [[yii\\i18n\\PhpMessageSource]] utilise des fichiers PHP pour stocker les traductions de messages. Chacun des fichiers PHP correspond aux messages d'une même catégorie. Par défaut, le nom du fichier doit être celui de la catégorie. Néanmoins, vous pouvez configurer  [[yii\\i18n\\PhpMessageSource::fileMap|fileMap (table de mise en correspondance de fichiers)]] pour faire correspondre une catégorie à un fichier PHP dont le nom obéit à une autre approche de nommage. Dans l'exemple qui précède, la catégorie  `app/error` correspond au fichier PHP `@app/messages/fr-FR/error.php` (en supposant que `fr-FR` est la langue cible). Sans cette configuration, la catégorie correspondrait à  `@app/messages/fr-FR/app/error.php`.\n\nEn plus de la possibilité de stocker les messages dans des fichiers PHP, vous pouvez aussi utiliser les sources de messages suivantes pour stocker vos traductions sous une autre forme :\n\n- [[yii\\i18n\\GettextMessageSource]] utilise des fichiers GNU Gettext, MO ou PO pour maintenir les messages traduits.\n- [[yii\\i18n\\DbMessageSource]] utilise une base de donnée pour stocker les messages traduits. \n\n\n## Format des messages <span id=\"message-formatting\"></span>\n\nLorsque vous traduisez un message, vous pouvez inclure dans le messages des « valeurs à remplacer » qui seront remplacées dynamiquement en fonction de la valeur d'un paramètre. Vous pouvez même utiliser une syntaxe spéciale des « valeurs à remplacer » pour que les valeurs de remplacement soient formatées en fonction de la langue cible.\nDans cette sous-section, nous allons décrire différentes manières de formater un message.\n\n### Valeurs à remplacer des message <span id=\"message-parameters\"></span>\n\nDans un message à traduire, vous pouvez inclure une ou plusieurs « valeurs à remplacer » pour qu'elles puissent être remplacées par les valeurs données. En spécifiant différents jeux de valeurs, vous pouvez faire varier le message dynamiquement. Dans l'exemple qui suit, la valeur à remplacer `{username}` du message `'Hello, {username}!'` sera remplacée par  `'Alexander'` et `'Qiang'`, respectivement.\n\n```php\n$username = 'Alexander';\n// affiche un message traduit en remplaçant {username} par \"Alexander\"\necho \\Yii::t('app', 'Hello, {username}!', [\n    'username' => $username,\n]);\n\n$username = 'Qiang';\n// affiche un message traduit en remplaçant {username} par \"Qiang\"\necho \\Yii::t('app', 'Hello, {username}!', [\n    'username' => $username,\n]);\n```\n\nLorsque le traducteur traduit un message contenant une valeur à remplacer, il doit laisser la valeur à remplacer telle quelle. Cela tient au fait que les valeurs à remplacer seront remplacées par les valeurs réelles au moment de l'appel de  `Yii::t()` pour traduire le message.\n\nDans un même message, vous pouvez utiliser, soit des « valeurs à remplacer nommées », soit des « valeurs à remplacer positionnelles », mais pas les deux types. \n \nL'exemple précédent montre comment utiliser des valeurs à remplacer nommées, c'est à dire, des valeurs à remplacer écrites sous la forme `{nom}`, et pour lesquelles vous fournissez un tableau associatif dont les clés sont les noms des valeurs à remplacer (sans les accolades) et les valeurs, les valeurs de remplacement.\n\nLes valeurs à remplacer positionnelles utilisent une suite d'entiers démarrant de zéro en tant que noms de valeurs à remplacer qui seront remplacées par les valeurs de remplacement, fournies sous forme d'un tableau, en fonction de leur position dans le tableau lors de l'appel de la méthode  `Yii::t()`. Dans l'exemple suivant, les valeurs à remplacer positionnelles `{0}`, `{1}` et `{2}` seront remplacées respectivement par les valeurs de  `$price`, `$count` et `$subtotal`.\n\n```php\n$price = 100;\n$count = 2;\n$subtotal = 200;\necho \\Yii::t('app', 'Price: {0}, Count: {1}, Subtotal: {2}', [$price, $count, $subtotal]);\n```\n\nDans le cas d'une seule valeur à remplacer, la valeur de remplacement peut être donnée sans la placer dans un tableau :\n\n```php\necho \\Yii::t('app', 'Price: {0}', $price);\n```\n\n> Tip: dans la plupart des cas, vous devriez utiliser des valeurs à remplacer nommées, parce que les noms permettent aux traducteurs de\n> mieux comprendre le sens des messages qu'ils doivent traduire. \n\n\n### Formatage des valeurs de remplacement <span id=\"parameter-formatting\"></span>\n\nVous pouvez spécifier des règles de formatage additionnelles dans les valeurs à remplacer qui seront appliquées aux valeurs de remplacement. Dans l'exemple suivant, la valeur de remplacement *price* est traitée comme un nombre et formatée comme une valeur monétaire :\n\n```php\n$price = 100;\necho \\Yii::t('app', 'Price: {0,number,currency}', $price);\n```\n\n> Note: le formatage des valeurs de remplacement nécessite l'installation de [extension intl de PHP](https://www.php.net/manual/fr/intro.intl.php).\n\nVous pouvez utiliser, soit la forme raccourcie, soit la forme complète pour spécifier une valeur à remplacer avec un format :\n```\nforme raccourcie : {name,type}\nforme complète : {name,type,style}\n```\n\n> Note: si vous avez besoin des caractères spéciaux tels que  `{`, `}`, `'`, `#`, entourez-les de `'`:\n> \n```php\necho Yii::t('app', \"Example of string with ''-escaped characters'': '{' '}' '{test}' {count,plural,other{''count'' value is # '#{}'}}\", ['count' => 3]);\n```\n\nLe format complet est décrit dans la [documentation ICU](https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classMessageFormat.html).\n\nDans ce qui suit, nous allons présenter quelques usages courants.\n\n\n\n#### Nombres <span id=\"number\"></span>\n\nLa valeur de remplacement est traitée comme un nombre. Par exemple,\n\n```php\n$sum = 42;\necho \\Yii::t('app', 'Balance: {0,number}', $sum);\n```\n\nVous pouvez spécifier un style facultatif pour la valeur de remplacement `integer` (entier), `currency` (valeur monétaire), ou `percent` (pourcentage) :\n\n```php\n$sum = 42;\necho \\Yii::t('app', 'Balance: {0,number,currency}', $sum);\n```\n\nVous pouvez aussi spécifier un motif personnalisé pour formater le nombre. Par exemple,\n\n```php\n$sum = 42;\necho \\Yii::t('app', 'Balance: {0,number,,000,000000}', $sum);\n```\n\nLes caractères à utiliser dans les formats personnalisés sont présentés dans le document [ICU API reference](https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classDecimalFormat.html) à la section \"Special Pattern Characters\" (Caractères pour motifs spéciaux).\n \n \nLa valeur de remplacement est toujours formatée en fonction de la locale cible c'est à dire que vous ne pouvez pas modifier les séparateurs de milliers et de décimales, les symboles monétaires, etc. sans modifier la locale de traduction. Si vous devez personnaliser ces éléments vous pouvez utiliser [[yii\\i18n\\Formatter::asDecimal()]] et [[yii\\i18n\\Formatter::asCurrency()]].\n\n#### Date <span id=\"date\"></span>\n\nLa valeur de remplacement doit être formatée comme une date. Par exemple,\n\n```php\necho \\Yii::t('app', 'Today is {0,date}', time());\n```\n\nVous pouvez spécifier des styles facultatifs pour la valeur de remplacement comme  `short` (court), `medium` (moyen), `long` (long) ou `full` (complet) :\n\n```php\necho \\Yii::t('app', 'Today is {0,date,short}', time());\n```\n\nVous pouvez aussi spécifier un motif personnalisé pour formater la date :\n\n```php\necho \\Yii::t('app', 'Today is {0,date,yyyy-MM-dd}', time());\n```\n\nVoir [Formatting reference](https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1SimpleDateFormat.html#details).\n\n\n#### Heure <span id=\"time\"></span>\n\nLa valeur de remplacement doit être formatée comme une heure (au sens large heure minute seconde). Par exemple, \n\n```php\necho \\Yii::t('app', 'It is {0,time}', time());\n```\n\nVous pouvez spécifier des styles facultatifs pour la valeur de remplacement comme `short` (court), `medium` (moyen), `long` (long) ou `full` (complet) :\n\n```php\necho \\Yii::t('app', 'It is {0,time,short}', time());\n```\n\nVous pouvez aussi spécifier un motif personnalisé pour formater l'heure :\n\n```php\necho \\Yii::t('app', 'It is {0,date,HH:mm}', time());\n```\n\nVoir [Formatting reference](https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1SimpleDateFormat.html#details).\n\n\n#### Prononciation <span id=\"spellout\"></span>\n\nLa valeur de remplacement doit être traitée comme un nombre et formatée comme une prononciation. Par exemple,\n\n```php\n//  produit \"42 is spelled as forty-two\" \necho \\Yii::t('app', '{n,number} is spelled as {n,spellout}', ['n' => 42]);\n```\n\nPar défaut le nombre est épelé en tant que cardinal. Cela peut être modifié :\n\n```php\n// produit  \"I am forty-seventh agent\" \necho \\Yii::t('app', 'I am {n,spellout,%spellout-ordinal} agent', ['n' => 47]);\n```\n\nNotez qu'il ne doit pas y avoir d'espace après `spellout,` et avant `%`.\n\nPour trouver une liste des options disponibles pour votre locale, reportez-vous à \n\"Numbering schemas, Spellout\" à [https://intl.rmcreative.ru/](https://intl.rmcreative.ru/).\n\n#### Nombre ordinal <span id=\"ordinal\"></span>\n\nLa valeur de remplacement doit être traitée comme un nombre et formatée comme un nombre ordinal. Par exemple,\n\n```php\n// produit \"You are the 42nd visitor here!\" (vous êtes le 42e visiteur ici !)\necho \\Yii::t('app', 'You are the {n,ordinal} visitor here!', ['n' => 42]);\n```\n\nLes nombres ordinaux acceptent plus de formats pour des langues telles que l'espagnol : \n\n```php\n// produit 471ª\necho \\Yii::t('app', '{n,ordinal,%digits-ordinal-feminine}', ['n' => 471]);\n```\n\nNotez qu'il ne doit pas y avoir d'espace après `ordinal,` et avant `%`.\n\nPour trouver une liste des options disponibles pour votre locale, reportez-vous à \n\"Numbering schemas, Ordinal\" à  [https://intl.rmcreative.ru/](https://intl.rmcreative.ru/).\n\n#### Durée <span id=\"duration\"></span>\n\nLa valeur de remplacement doit être traitée comme un nombre de secondes et formatée comme une durée. Par exemple, \n\n```php\n// produit \"You are here for 47 sec. already!\" (Vous êtes ici depuis 47sec. déjà !)\necho \\Yii::t('app', 'You are here for {n,duration} already!', ['n' => 47]);\n```\n\nLa durée accepte d'autres formats :\n\n```php\n// produit  130:53:47\necho \\Yii::t('app', '{n,duration,%in-numerals}', ['n' => 471227]);\n```\n\nNotez qu'il ne doit pas y avoir d'espace après `duration,` et avant `%`.\n\nPour trouver une liste des options disponibles pour votre locale, reportez-vous à \n\"Numbering schemas, Duration\" à [https://intl.rmcreative.ru/](https://intl.rmcreative.ru/).\n\n#### Pluriel <span id=\"plural\"></span>\n\nLes langues diffèrent dans leur manière de marquer le pluriel. Yii fournit un moyen pratique pour traduire les messages dans différentes formes de pluriel qui fonctionne même pour des règles très complexes. Au lieu de s'occuper des règles d'inflexion directement, il est suffisant de fournir la traductions des mots infléchis dans certaines situations seulement. Par exemple,\n\n```php\n// Lorsque  $n = 0, produit \"There are no cats!\" \n// Losque $n = 1, produit \"There is one cat!\" \n// Lorsque $n = 42, produit  \"There are 42 cats!\" \necho \\Yii::t('app', 'There {n,plural,=0{are no cats} =1{is one cat} other{are # cats}}!', ['n' => $n]);\n```\n\nDans les arguments des règles de pluralisation ci-dessus,  `=` signifie valeur exacte. Ainsi `=0` signifie exactement zéro, `=1` signifie exactement un.  `other` signifie n'importe quelle autre valeur. `#` est remplacé par la valeur de  `n` formatée selon la langue cible. \n\nLes formes du pluriel peuvent être très compliquées dans certaines langues. Dans l'exemple ci-après en russe, `=1` correspond exactement à `n = 1` \ntandis que  `one` correspond à  `21` ou `101`:\n\n```\nЗдесь {n,plural,=0{котов нет} =1{есть один кот} one{# кот} few{# кота} many{# котов} other{# кота}}!\n```\n\nCes noms d'arguments spéciaux tels que  `other`, `few`, `many` et autres varient en fonction de la langue. Pour savoir lesquels utiliser pour une locale particulière, reportez-vous aux \"Plural Rules, Cardinal\" à [https://intl.rmcreative.ru/](https://intl.rmcreative.ru/). \nEn alternative, vous pouvez vous reporter aux  [rules reference at unicode.org](https://cldr.unicode.org/index/cldr-spec/plural-rules).\n\n> Note: le message en russe ci-dessus est principalement utilisé comme message traduit, pas comme message source, sauf si vous définissez la [[yii\\base\\Application::$sourceLanguage|langue source]] de votre application comme étant `ru-RU` et traduisez à partir du russe.\n>\n> Lorsqu'une traduction n'est pas trouvée pour un message source spécifié dans un appel de  `Yii::t()`, les règles du pluriel pour la \n> [[yii\\base\\Application::$sourceLanguage|langue source]] seront appliquées au message source.\nIl existe un paramètre  `offset` dans le cas où la chaîne est de la forme suivante :\n\n```php\n$likeCount = 2;\necho Yii::t('app', 'You {likeCount,plural,\n    offset: 1\n    =0{did not like this}\n    =1{liked this}\n    one{and one other person liked this}\n    other{and # others liked this}\n}', [\n    'likeCount' => $likeCount\n]);\n\n// You and one other person liked this\n```\n\n#### Sélection ordinale <span id=\"ordinal-selection\"></span>\n\nL'argument `selectordinal` pour une valeur à remplacer  numérique a pour but de choisir une chaîne de caractères basée sur les règles linguistiques de la locale cible pour les ordinaux. Ainsi, \n\n```php\n$n = 3;\necho Yii::t('app', 'You are the {n,selectordinal,one{#st} two{#nd} few{#rd} other{#th}} visitor', ['n' => $n]);\n```\n//Produit pour l'anglais : \n//You are the 3rd visitor\n\n//Traduction  en russe, \n'You are the {n,selectordinal,one{#st} two{#nd} few{#rd} other{#th}} visitor' => 'Вы {n,selectordinal,other{#-й}} посетитель',\n\n//Traduit en russe produit :\n//Вы 3-й посетитель\n\n//Traduction en français\n'You are the {n,selectordinal,one{#st} two{#nd} few{#rd} other{#th}} visitor' =>  'Vous êtes le {n,selectordinal,one{#er} other{#e}} visiteur'\n\n//Traduit en français produit :\n//Vous êtes le 3e visiteur\n```\n\nLe format est assez proche de celui utilisé pour le pluriel. Pour connaître quels arguments utiliser pour une locale particulière, reportez-vous aux \"Plural Rules, Ordinal\" à [https://intl.rmcreative.ru/](https://intl.rmcreative.ru/). \nEn alternative, vous pouvez vous reporter aux  [rules reference at unicode.org](https://unicode-org.github.io/cldr-staging/charts/37/supplemental/language_plural_rules.html).\n\n#### Sélection <span id=\"selection\"></span>\n\nVous pouvez utiliser l'argument `select` dans une valeur à remplacer pour choisir une phrase en fonction de la valeur de remplacement. Par exemple, \n\n```php\n// Peut produire \"Snoopy is a dog and it loves Yii!\"\necho \\Yii::t('app', '{name} is a {gender} and {gender,select,female{she} male{he} other{it}} loves Yii!', [\n    'name' => 'Snoopy',\n    'gender' => 'dog',\n]);\n```\nDans l'expression qui précède, `female` et `male` sont des valeurs possibles de l'argument, tandis que `other` prend en compte les valeurs qui ne sont ni l'une ni l'autre des ces valeurs. Derrière chacune des valeurs possibles de l'argument, vous devez spécifier un segment de phrase en l'entourant d'accolades. \n\n\n\n### Spécification des sources de messages par défaut<span id=\"default-translation\"></span>\n\nVous pouvez spécifier les sources de messages par défaut qui seront utilisées comme solution de repli pour les catégories qui ne correspondent à aucune des catégories configurées. \nCette source de message doit être marquée par un caractère générique `*`. Pour cela ajoutez les lignes suivantes dans la configuration de l'application :\n\n```php\n//configure i18n component\n\n'i18n' => [\n    'translations' => [\n        '*' => [\n            'class' => 'yii\\i18n\\PhpMessageSource'\n        ],\n    ],\n],\n```\nDésormais, vous pouvez utiliser une catégorie sans la configurer, ce qui est un comportement identique à celui de Yii 1.1. Les messages pour cette catégorie proviendront d'une source de messages par défaut située dans le dossier `basePath` c.-à-d. `@app/messages`:\n\n\n```php\necho Yii::t('not_specified_category', 'message from unspecified category');\n```\n\nLe message sera chargé depuis `@app/messages/<LanguageCode>/not_specified_category.php`.\n\n### Traduction des messages d'un module <span id=\"module-translation\"></span>\n\nSi vous voulez traduire les messages d'un module et éviter d'avoir un unique fichier de traduction pour tous les messages, vous pouvez procéder comme suit :\n\n```php\n<?php\n\nnamespace app\\modules\\users;\n\nuse Yii;\n\nclass Module extends \\yii\\base\\Module\n{\n    public $controllerNamespace = 'app\\modules\\users\\controllers';\n\n    public function init()\n    {\n        parent::init();\n        $this->registerTranslations();\n    }\n\n    public function registerTranslations()\n    {\n        Yii::$app->i18n->translations['modules/users/*'] = [\n            'class' => 'yii\\i18n\\PhpMessageSource',\n            'sourceLanguage' => 'en-US',\n            'basePath' => '@app/modules/users/messages',\n            'fileMap' => [\n                'modules/users/validation' => 'validation.php',\n                'modules/users/form' => 'form.php',\n                ...\n            ],\n        ];\n    }\n\n    public static function t($category, $message, $params = [], $language = null)\n    {\n        return Yii::t('modules/users/' . $category, $message, $params, $language);\n    }\n\n}\n```\n\nDans l'exemple précédent, nous utilisons le caractère générique pour la correspondance puis nous filtrons chacune des catégories par fichier requis. Au lieu d'utiliser `fileMap`, vous pouvez utiliser la convention de mise en correspondance du fichier de même nom. \n\nDésormais, vous pouvez utiliser  `Module::t('validation', 'your custom validation message')` ou `Module::t('form', 'some form label')` directement.\n\n### Traduction des messages d'objets graphiques <span id=\"widget-translation\"></span>\n\nLa règle applicable aux modules présentée ci-dessus s'applique également aux objets graphiques, par exemple :\n\n```php\n<?php\n\nnamespace app\\widgets\\menu;\n\nuse yii\\base\\Widget;\nuse Yii;\n\nclass Menu extends Widget\n{\n\n    public function init()\n    {\n        parent::init();\n        $this->registerTranslations();\n    }\n\n    public function registerTranslations()\n    {\n        $i18n = Yii::$app->i18n;\n        $i18n->translations['widgets/menu/*'] = [\n            'class' => 'yii\\i18n\\PhpMessageSource',\n            'sourceLanguage' => 'en-US',\n            'basePath' => '@app/widgets/menu/messages',\n            'fileMap' => [\n                'widgets/menu/messages' => 'messages.php',\n            ],\n        ];\n    }\n\n    public function run()\n    {\n        echo $this->render('index');\n    }\n\n    public static function t($category, $message, $params = [], $language = null)\n    {\n        return Yii::t('widgets/menu/' . $category, $message, $params, $language);\n    }\n\n}\n```\n\nAu lieu d'utiliser `fileMap`, vous pouvez utiliser la convention de mise en correspondance du fichier de même nom. \nDésormais, vous pouvez utiliser `Menu::t('messages', 'new messages {messages}', ['{messages}' => 10])` directement.\n\n> Note: pour les objets graphiques, vous pouvez aussi utiliser les vues i18n, en y appliquant les mêmes règles que celles applicables aux contrôleurs.\n\n\n### Traduction des messages du framework <span id=\"framework-translation\"></span>\n\nYii est fourni avec les traductions par défaut des messages d'erreurs de validation et quelques autres chaînes. Ces messages sont tous dans la catégorie `yii`. Parfois, vous souhaitez corriger la traduction par défaut des messages du framework pour votre application. Pour le faire, configurez le [composant d'application](structure-application-components.md) `i18n` comme indiqué ci-après : \n\n```php\n'i18n' => [\n    'translations' => [\n        'yii' => [\n            'class' => 'yii\\i18n\\PhpMessageSource',\n            'sourceLanguage' => 'en-US',\n            'basePath' => '@app/messages'\n        ],\n    ],\n],\n```\n\nVous pouvez désormais placer vos traductions corrigées dans `@app/messages/<language>/yii.php`.\n\n### Gestion des traductions manquantes <span id=\"missing-translations\"></span>\n\nMême si la traduction n'est pas trouvée dans la source de traductions, Yii affiche le contenu du message demandé. Un tel comportement est très pratique tant que le message est une phrase valide. Néanmoins, quelques fois, cela ne suffit pas. Vous pouvez désirer faire quelque traitement de la situation, au moment où le message apparaît manquant. Vous pouvez utiliser pour cela l'événement [[yii\\i18n\\MessageSource::EVENT_MISSING_TRANSLATION|missingTranslation (traduction manquante)]] de [[yii\\i18n\\MessageSource]].\n\nPar exemple, vous désirez peut-être marquer toutes les traductions manquantes par quelque chose de voyant, de manière à les repérer facilement dans la page. Vous devez d'abord configurer un gestionnaire d'événement. Cela peut se faire dans la configuration de l'application :\n\n```php\n'components' => [\n    // ...\n    'i18n' => [\n        'translations' => [\n            'app*' => [\n                'class' => 'yii\\i18n\\PhpMessageSource',\n                'fileMap' => [\n                    'app' => 'app.php',\n                    'app/error' => 'error.php',\n                ],\n                'on missingTranslation' => ['app\\components\\TranslationEventHandler', 'handleMissingTranslation']\n            ],\n        ],\n    ],\n],\n```\n\nVous devez ensuite implémenter votre gestionnaire d'événement :\n\n```php\n<?php\n\nnamespace app\\components;\n\nuse yii\\i18n\\MissingTranslationEvent;\n\nclass TranslationEventHandler\n{\n    public static function handleMissingTranslation(MissingTranslationEvent $event)\n    {\n        $event->translatedMessage = \"@MISSING: {$event->category}.{$event->message} FOR LANGUAGE {$event->language} @\";\n    }\n}\n```\n\nSi [[yii\\i18n\\MissingTranslationEvent::translatedMessage]] est défini par le gestionnaire d'événement, il sera affiché en tant que résultat de la traduction.\n\n> Note: chacune des sources de messages gère ses traductions manquantes séparément. Si vous avez recours à plusieurs sources de messages et que vous voulez qu'elles gèrent les messages manquants de la même manière, vous devez assigner le gestionnaire d'événement correspondant à chacune d'entre-elles. \n\n\n### Utilisation de la commande `message`<span id=\"message-command\"></span>\n\nLes traductions peuvent être stockées dans des [[yii\\i18n\\PhpMessageSource|fichiers php]], des [[yii\\i18n\\GettextMessageSource|fichiers .po ]] ou dans une [[yii\\i18n\\DbMessageSource|bases de données]]. Reportez-vous aux classes spécifiques pour connaître les options supplémentaires. \n\nEn premier lieu, vous devez créer un fichier de configuration. Décidez de son emplacement et exécutez la commande suivante :\n\n\n```bash\n./yii message/config-template path/to/config.php\n```\n\nOuvrez le fichier ainsi créé et ajustez-en les paramètres pour qu'ils répondent à vos besoins. Portez-une attention particulière à :\n\n* `languages`: un tableau des langues dans lesquelles votre application doit être traduite  ;\n* `messagePath`: le chemin du dossier où doivent être placés les fichiers de messages, qui doit correspondre le paramètre `basePath` de `i18n` dans la configuration de l'application. \n\nVous pouvez également utiliser la commande './yii message/config' pour générer dynamiquement le fichier de configuration avec les options spécifiées via le ligne de commande. Par exemple, vous pouvez définir `languages` et `messagePath` comme indiqué ci-dessous :\n\n\n```bash\n./yii message/config --languages=de,ja --messagePath=messages path/to/config.php\n```\n\nPour connaître toutes les options utilisables, exécutez la commande :\n\n```bash\n./yii help message/config\n```\n\nUne fois que vous en avez terminé avec la configuration, vous pouvez finalement extraire vos messages par la commande :\n\n```bash\n./yii message path/to/config.php\n```\n\nVous pouvez aussi utiliser des options pour changer dynamiquement les paramètres d'extraction.\n\nVous trouverez alors vos fichiers de traduction (si vous avez choisi les traductions basées sur des fichiers) dans votre dossier  `messagePath`.\n\n\n## Traduction des vues<span id=\"view-translation\"></span>\n\nPlutôt que de traduire des textes de messages individuels, vous pouvez parfois désirer traduire le script d'une vue tout entier. Pour cela, contentez-vous de traduire la vue et de la sauvegarder dans un sous-dossier dont le nom est le code de la langue cible. Par exemple, si vous avez traduit le script de la vue `views/site/index.php` et que la langue cible est  `fr-FR`, vous devez sauvegarder la traduction dans `views/site/fr-FR/index.php`. Désormais, à chaque fois que vous appellerez  [[yii\\base\\View::renderFile()]] ou toute méthode qui invoque cette méthode (p. ex. [[yii\\base\\Controller::render()]]) pour rendre la vue, `views/site/index.php`, ce sera la vue `views/site/fr-FR/index.php` qui sera rendue à sa place.\n\n> Note: si la  [[yii\\base\\Application::$language|langue cible]] est identique à la  [[yii\\base\\Application::$sourceLanguage|langue source]]\n> la vue originale sera rendue sans tenir compte de l'existence de la vue traduite. \n\n\n## Formatage des dates et des nombres<span id=\"date-number\"></span>\n\nReportez-vous à la section [Formatage des données](output-formatting.md) pour les détails.\n\n\n## Configuration de l'environnement PHP <span id=\"setup-environment\"></span>\n\nYii utilise l'[extension intl de PHP](https://www.php.net/manual/fr/book.intl.php) pour fournir la plupart de ses fonctionnalités d'internationalisation, telles que le formatage des dates et des nombres de la classe [[yii\\i18n\\Formatter]] et le formatage des messages de la classe [[yii\\i18n\\MessageFormatter]].\nLes deux classes fournissent un mécanisme de remplacement lorsque l'extension `intl` n'est pas installée. Néanmoins, l'implémentation du mécanisme de remplacement ne fonctionne bien que quand la langue cible est l'anglais. C'est pourquoi, il est fortement recommandé d'installer `intl` quand c'est nécessaire.\nL'[extension intl de PHP](https://www.php.net/manual/fr/book.intl.php) est basée sur la [bibliothèque ICU](https://icu.unicode.org/) qui fournit la base de connaissances et les règles de formatage pour les différentes locales. Des versions différentes d'ICU peuvent conduire à des formatages différents des dates et des nombres. Pour être sûr que votre site Web donne les même résultats dans tous les environnements, il  est recommandé d'installer la même version de l'extension `intl` (et par conséquent la même version d'ICU) dans tous les environnements. \n\nPour savoir quelle version d'ICU est utilisée par PHP, vous pouvez exécuter le script suivant, qui vous restitue la version de PHP et d'ICU en cours d'utilisation. \n\n```php\n<?php\necho \"PHP: \" . PHP_VERSION . \"\\n\";\necho \"ICU: \" . INTL_ICU_VERSION . \"\\n\";\necho \"ICU Data: \" . INTL_ICU_DATA_VERSION . \"\\n\";\n```\n\nIl est également recommandé d'utiliser une version d'ICU supérieure ou égale à 48. Cela garantit que toutes les fonctionnalités décrites dans ce document sont utilisables. Par exemple, une version d'ICU inférieure à 49 ne prend pas en charge la valeur à remplacer `#` dans les règles de pluralisation. Reportez-vous à  <https://icu.unicode.org/download> pour obtenir une liste complète des versions d'ICU disponibles. Notez que le numérotage des versions a changé après la version 4.8  (p. ex., ICU 4.8, ICU 49, ICU 50, etc.)\n\nEn outre, les informations dans la base de donnée des fuseaux horaires fournie par la bibliothèque ICU peuvent être surannées. Reportez-vous au [manuel d'ICU](https://unicode-org.github.io/icu/userguide/datetime/timezone/#updating-the-time-zone-data) pour les détails sur la manière de mettre la base de données des fuseaux horaires à jour. Bien que la base de données des fuseaux horaires d'ICU soit utilisée pour le formatage, celle de PHP peut aussi être d'actualité. Vous pouvez la mettre à jour en installant la dernière version du [paquet `timezonedb` de pecl](https://pecl.php.net/package/timezonedb).\n"
  },
  {
    "path": "docs/guide-id/README.md",
    "content": "Panduan Definitif Untuk Yii 2.0\n===============================\n\nTutorial ini dirilis di bawah [Persyaratan Dokumentasi Yii](https://www.yiiframework.com/doc/terms/).\n\nSeluruh hak cipta dilindungi.\n\n2014 (c) Yii Software LLC.\n\n\nPengantar\n------------\n\n* [Tentang Yii](intro-yii.md)\n* [Upgrade dari Versi 1.1](intro-upgrade-from-v1.md)\n\n\nMulai\n---------------\n\n* [Instalasi Yii](start-installation.md)\n* [Menjalankan Aplikasi](start-workflow.md)\n* [Mengatakan Hello](start-hello.md)\n* [Bekerja dengan Form](start-forms.md)\n* [Bekerja dengan Database](start-databases.md)\n* [Membuat Kode Otomatis dengan Gii](start-gii.md)\n* [Menatap ke Depan](start-looking-ahead.md)\n\n\nStruktur Aplikasi\n---------------------\n\n* [Tinjauan](structure-overview.md)\n* [Script Masuk](structure-entry-scripts.md)\n* [Aplikasi](structure-applications.md)\n* [Komponen Aplikasi](structure-application-components.md)\n* [Controller](structure-controllers.md)\n* [Model](structure-models.md)\n* [Views](structure-views.md)\n* [Modul](structure-modules.md)\n* [Filter](structure-filters.md)\n* [Widgets](structure-widgets.md)\n* [Aset](structure-assets.md)\n* [Ekstensi](structure-extensions.md)\n\n\nPenanganan Permintaan\n-----------------\n\n* [Tinjauan](runtime-overview.md)\n* [Bootstrap](runtime-bootstrapping.md)\n* [Routing dan Pembuatan URL](runtime-routing.md)\n* [Permintaan](runtime-requests.md)\n* [Tanggapan](runtime-responses.md)\n* [Sesi dan Cookies](runtime-sessions-cookies.md)\n* [Penanganan Kesalahan](runtime-handling-errors.md)\n* [Logging](runtime-logging.md)\n\n\nKonsep Pokok\n------------\n\n* [Komponen](concept-components.md)\n* [Properti](concept-properties.md)\n* [Event](concept-events.md)\n* [Perilaku](concept-behaviors.md)\n* [Konfigurasi](concept-configurations.md)\n* [Alias](concept-aliases.md)\n* [Class Autoloading](concept-autoloading.md)\n* [Layanan Locator](concept-service-locator.md)\n* [Dependency Injection](concept-di-container.md)\n\n\nBekerja dengan Database\n----------------------\n\n* [Data Access Objects](db-dao.md): Menghubungkan ke database, query dasar, transaksi, dan manipulasi skema\n* [Query Builder](db-query-builder.md): Query database menggunakan lapisan abstraksi sederhana\n* [Active Record](db-active-record.md): ORM Active Record, mengambil dan memanipulasi catatan, dan mendefinisikan hubungan\n* [Migrasi](db-migrations.md): Terapkan kontrol versi untuk database Anda dalam lingkungan pengembangan tim\n* [Sphinx](https://github.com/yiisoft/yii2-sphinx/blob/master/docs/guide/README.md)\n* [Redis](https://github.com/yiisoft/yii2-redis/blob/master/docs/guide/README.md)\n* [MongoDB](https://github.com/yiisoft/yii2-mongodb/blob/master/docs/guide/README.md)\n* [ElasticSearch](https://github.com/yiisoft/yii2-elasticsearch/blob/master/docs/guide/README.md)\n\n\nMendapatkan Data dari Pengguna\n-----------------------\n\n* [Membuat Formulir](input-forms.md)\n* [Memvalidasi Masukan](input-validation.md)\n* [Mengunggah File](input-file-upload.md)\n* [Mengumpulkan Masukan Tabel](input-tabular-input.md)\n* [Mendapatkan Data untuk Beberapa Model](input-multiple-models.md)\n\n\nMenampilkan Data\n---------------\n\n* [Pemformatan Data](output-formatting.md)\n* [Pagination](output-pagination.md)\n* [Pengurutan](output-sorting.md)\n* [Penyedia Data](output-data-providers.md)\n* [Data Widget](output-data-widgets.md)\n* [Bekerja dengan Script Client](output-client-scripts.md)\n* [Tema](output-theming.md)\n\n\nKeamanan\n--------\n\n* [Tinjauan](security-overview.md)\n* [Otentikasi](security-authentication.md)\n* [Otorisasi](security-authorization.md)\n* [Bekerja dengan Kata Sandi](security-passwords.md)\n* [Kriptografi](security-cryptography.md)\n* [Otentikasi Klien](https://github.com/yiisoft/yii2-authclient/blob/master/docs/guide/README.md)\n* [Praktik Terbaik](security-best-practices.md)\n\n\nCaching\n-------\n\n* [Tinjauan](caching-overview.md)\n* [Caching Data](caching-data.md)\n* [Caching Fragmen](caching-fragment.md)\n* [Caching Halaman](caching-page.md)\n* [Caching HTTP](caching-http.md)\n\n\nLayanan Web RESTful\n--------------------\n\n* [Quick Start](rest-quick-start.md)\n* [Sumber Daya](rest-resources.md)\n* [Controller](rest-controllers.md)\n* [Routing](rest-routing.md)\n* [Penformatan Respon](rest-response-formatting.md)\n* [Otentikasi](rest-authentication.md)\n* [Pembatasan Laju](rest-rate-limiting.md)\n* [Versi](rest-versioning.md)\n* [Penanganan Kesalahan](rest-error-handling.md)\n\n\nAlat Pengembangan\n-----------------\n\n* [Debug Toolbar dan Debugger](https://github.com/yiisoft/yii2-debug/blob/master/docs/guide/README.md)\n* [Membuat Kode Otomatis dengan Gii](https://github.com/yiisoft/yii2-gii/blob/master/docs/guide/README.md)\n* [Membuat API Documentation](https://github.com/yiisoft/yii2-apidoc)\n\n\nPengujian\n-------\n\n* [Tinjauan](test-overview.md)\n* [Persiapan Lingkungan Pengujian](test-environment-setup.md)\n* [Tes Satuan](test-unit.md)\n* [Tes Fungsional](test-functional.md)\n* [Tes Penerimaan](test-acceptance.md)\n* [Jadwal](test-fixtures.md)\n\n\nTopik Khusus\n--------------\n\n* [Cetakan Proyek Lanjutan](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md)\n* [Membangun Aplikasi dari Awal](tutorial-start-from-scratch.md)\n* [Console Commands](tutorial-console.md)\n* [Validator Inti](tutorial-core-validators.md)\n* [Internasionalisasi](tutorial-i18n.md)\n* [Mailing](tutorial-mailing.md)\n* [Penyetelan Performa](tutorial-performance-tuning.md)\n* [Lingkungan Shared Hosting](tutorial-shared-hosting.md)\n* [Template Engine](tutorial-template-engines.md)\n* [Bekerja dengan Kode Pihak Ketiga](tutorial-yii-integration.md)\n\n\nWidget\n-------\n\n* [GridView](https://www.yiiframework.com/doc-2.0/yii-grid-gridview.html)\n* [ListView](https://www.yiiframework.com/doc-2.0/yii-widgets-listview.html)\n* [DetailView](https://www.yiiframework.com/doc-2.0/yii-widgets-detailview.html)\n* [ActiveForm](https://www.yiiframework.com/doc-2.0/guide-input-forms.html#activerecord-based-forms-activeform)\n* [Pjax](https://www.yiiframework.com/doc-2.0/yii-widgets-pjax.html)\n* [Menu](https://www.yiiframework.com/doc-2.0/yii-widgets-menu.html)\n* [LinkPager](https://www.yiiframework.com/doc-2.0/yii-widgets-linkpager.html)\n* [LinkSorter](https://www.yiiframework.com/doc-2.0/yii-widgets-linksorter.html)\n* [Bootstrap Widgets](https://github.com/yiisoft/yii2-bootstrap/blob/master/docs/guide/README.md)\n* [JQuery UI Widgets](https://github.com/yiisoft/yii2-jui/blob/master/docs/guide/README.md)\n\n\nAlat Bantu\n---------\n\n* [Tinjauan](helper-overview.md)\n* [ArrayHelper](helper-array.md)\n* [Html](helper-html.md)\n* [Url](helper-url.md)\n"
  },
  {
    "path": "docs/guide-id/blocktypes.json",
    "content": "{\n    \"Warning:\": \"Perhatian:\",\n    \"Note:\": \"Catatan:\",\n    \"Info:\": \"Info:\",\n    \"Tip:\": \"Tips:\"\n}\n"
  },
  {
    "path": "docs/guide-id/images/application-lifecycle.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.13-->\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d0\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key for=\"graphml\" id=\"d7\" yfiles.type=\"resources\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d0\"/>\n    <node id=\"n0\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"571.4472707112631\" width=\"763.2772213171534\" x=\"-1269.9373595143054\" y=\"-207.17439524332679\"/>\n              <y:Fill color=\"#FFCC0024\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FFCC00\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"763.2772213171534\" x=\"0.0\" y=\"0.0\">Entry script (index.php or yii)</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"225.33495140075684\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 4</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n0:\">\n        <node id=\"n0::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"324.9258883570935\" x=\"-1249.511914911339\" y=\"-169.79793039957679\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"126.759765625\" x=\"99.08306136604676\" y=\"5.6494140625\">Load application config<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n0::n1\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d5\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"309.37646484374994\" width=\"330.35133296005984\" x=\"-1254.9373595143054\" y=\"35.272875467936274\"/>\n                  <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"330.35133296005984\" x=\"0.0\" y=\"0.0\">Create application instance</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"19\" bottomF=\"19.15489692687993\" left=\"5\" leftF=\"5.425444602966309\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 5</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n0::n1:\">\n            <node id=\"n0::n1::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"72.64934031168627\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"45.34375\" x=\"124.79106917854676\" y=\"5.6494140625\">preInit()<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n1\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"122.64524027506516\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"120.71875\" x=\"87.10356917854676\" y=\"5.6494140625\">Register error handler<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n2\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"174.96110788981125\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"173.435546875\" x=\"60.74517074104676\" y=\"5.6494140625\">Configure application properties<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n3\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"226.56779181162517\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"27.33203125\" x=\"133.79692855354676\" y=\"5.6494140625\">init()<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n4\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.9258883570935\" x=\"-1234.511914911339\" y=\"280.4944433848063\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.025390625\" x=\"116.45024886604676\" y=\"5.6494140625\">bootstrap()<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n        <node id=\"n0::n2\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d5\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"411.6943410237631\" width=\"324.9258883570935\" x=\"-846.5860265542456\" y=\"-169.79793039957679\"/>\n                  <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"324.9258883570935\" x=\"0.0\" y=\"0.0\">Run application</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"1.1368683772161603E-13\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 3</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n0::n2:\">\n            <node id=\"n0::n2::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.9258883570935\" x=\"-831.5860265542456\" y=\"-132.42146555582667\"/>\n                  <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"164.705078125\" x=\"65.11040511604676\" y=\"5.6494140625\">EVENT_BEFORE_REQUEST<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n2::n1\" yfiles.foldertype=\"group\">\n              <data key=\"d4\"/>\n              <data key=\"d5\"/>\n              <data key=\"d6\">\n                <y:ProxyAutoBoundsNode>\n                  <y:Realizers active=\"0\">\n                    <y:GroupNode>\n                      <y:Geometry height=\"204.37646484375\" width=\"294.9258883570935\" x=\"-831.5860265542456\" y=\"-78.79793039957679\"/>\n                      <y:Fill color=\"#99336635\" transparent=\"false\"/>\n                      <y:BorderStyle hasColor=\"false\" type=\"dashed\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#993366\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#FFFFFF\" visible=\"true\" width=\"294.9258883570935\" x=\"0.0\" y=\"0.0\">Handle request</y:NodeLabel>\n                      <y:Shape type=\"roundrectangle\"/>\n                      <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                      <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                      <y:BorderInsets bottom=\"8\" bottomF=\"7.929194132486941\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                    </y:GroupNode>\n                    <y:GroupNode>\n                      <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                      <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 4</y:NodeLabel>\n                      <y:Shape type=\"roundrectangle\"/>\n                      <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                      <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                      <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                    </y:GroupNode>\n                  </y:Realizers>\n                </y:ProxyAutoBoundsNode>\n              </data>\n              <graph edgedefault=\"directed\" id=\"n0::n2::n1:\">\n                <node id=\"n0::n2::n1::n0\">\n                  <data key=\"d6\">\n                    <y:ShapeNode>\n                      <y:Geometry height=\"30.0\" width=\"264.9258883570935\" x=\"-816.5860265542456\" y=\"-41.421465555826785\"/>\n                      <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"231.4609375\" x=\"16.732475428546763\" y=\"5.6494140625\">Resolve request into route and parameters<y:LabelModel>\n                          <y:SmartNodeLabelModel distance=\"4.0\"/>\n                        </y:LabelModel>\n                        <y:ModelParameter>\n                          <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                        </y:ModelParameter>\n                      </y:NodeLabel>\n                      <y:Shape type=\"rectangle\"/>\n                    </y:ShapeNode>\n                  </data>\n                </node>\n                <node id=\"n0::n2::n1::n1\">\n                  <data key=\"d6\">\n                    <y:ShapeNode>\n                      <y:Geometry height=\"30.0\" width=\"264.9258883570935\" x=\"-816.5860265542456\" y=\"18.578534444173215\"/>\n                      <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"197.44140625\" x=\"33.74224105354676\" y=\"5.6494140625\">Create module, controller and action<y:LabelModel>\n                          <y:SmartNodeLabelModel distance=\"4.0\"/>\n                        </y:LabelModel>\n                        <y:ModelParameter>\n                          <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                        </y:ModelParameter>\n                      </y:NodeLabel>\n                      <y:Shape type=\"rectangle\"/>\n                    </y:ShapeNode>\n                  </data>\n                </node>\n                <node id=\"n0::n2::n1::n2\">\n                  <data key=\"d6\">\n                    <y:ShapeNode>\n                      <y:Geometry height=\"30.0\" width=\"264.9258883570935\" x=\"-816.5860265542456\" y=\"72.64934031168627\"/>\n                      <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.369140625\" x=\"101.77837386604676\" y=\"5.649414062500057\">Run action<y:LabelModel>\n                          <y:SmartNodeLabelModel distance=\"4.0\"/>\n                        </y:LabelModel>\n                        <y:ModelParameter>\n                          <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                        </y:ModelParameter>\n                      </y:NodeLabel>\n                      <y:Shape type=\"rectangle\"/>\n                    </y:ShapeNode>\n                  </data>\n                </node>\n              </graph>\n            </node>\n            <node id=\"n0::n2::n2\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.9258883570935\" x=\"-831.5860265542456\" y=\"149.20206960042316\"/>\n                  <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"154.697265625\" x=\"70.11431136604676\" y=\"5.6494140625\">EVENT_AFTER_REQUEST<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n2::n3\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-831.5860265542456\" y=\"196.89641062418633\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"148.099609375\" x=\"73.41313949104676\" y=\"5.6494140625\">Send response to end user<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n        <node id=\"n0::n3\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"324.9258883570935\" x=\"-846.5860265542456\" y=\"319.2728754679363\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"160.08203125\" x=\"82.42192855354676\" y=\"5.6494140625\">Complete request processing<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <edge id=\"e0\" source=\"n0\" target=\"n0::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-179.83580569315376\" sy=\"-180.3944529152355\" tx=\"-13.869777491410105\" ty=\"-154.8008369539754\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::e0\" source=\"n0::n0\" target=\"n0::n1\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#99CCFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"106.052734375\" x=\"-130.76131968438426\" y=\"78.18481445312506\">Configuration array<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"77.04379339868619\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e0\" source=\"n0::n1::n0\" target=\"n0::n1::n1\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e1\" source=\"n0::n1::n1\" target=\"n0::n1::n2\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e2\" source=\"n0::n1::n2\" target=\"n0::n1::n3\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e3\" source=\"n0::n1::n3\" target=\"n0::n1::n4\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::e1\" source=\"n0::n1\" target=\"n0::n2\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"2.8060680342755404\" sy=\"-48.37646484374994\" tx=\"-162.49660512430125\" ty=\"105.53540293375653\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::e0\" source=\"n0::n2::n0\" target=\"n0::n2::n1\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::n1::e0\" source=\"n0::n2::n1::n0\" target=\"n0::n2::n1::n1\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::n1::e1\" source=\"n0::n2::n1::n1\" target=\"n0::n2::n1::n2\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::e1\" source=\"n0::n2::n1\" target=\"n0::n2::n2\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::e2\" source=\"n0::n2::n2\" target=\"n0::n2::n3\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::e2\" source=\"n0::n2\" target=\"n0::n3\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#99CCFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"59.353515625\" x=\"-93.67673227804266\" y=\"28.318773905436274\">Exit status<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"63.99999999999999\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.47945569632951074\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d7\">\n    <y:Resources/>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-id/images/application-structure.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"872.1807999999999\" y=\"-14.764159999999947\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"32.265625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"69.712890625\" x=\"15.1435546875\" y=\"1.3671875\">application\ncomponent<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"-91.97375999999994\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"70.28125\" x=\"14.859375\" y=\"8.43359375\">entry script<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"-14.764159999999947\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"68.21875\" x=\"15.890625\" y=\"8.43359375\">application<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"62.44544000000004\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"60.267578125\" x=\"19.8662109375\" y=\"8.433593750000007\">controller<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"872.1807999999999\" y=\"62.44544000000005\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"31.43359375\" x=\"34.283203125\" y=\"8.43359375\">filter<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"532.664\" y=\"23.901600000000087\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"47.728515625\" x=\"26.1357421875\" y=\"8.43359375\">module<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"618.4047999999991\" y=\"139.65504000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"29.611328125\" x=\"35.1943359375\" y=\"8.43359375\">view<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"786.161599999999\" y=\"139.65504000000004\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"40.28125\" x=\"29.859375\" y=\"8.43359375\">model<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n8\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"532.664\" y=\"216.86464000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"42.923828125\" x=\"28.5380859375\" y=\"8.43359375\">widget<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n9\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"216.86464000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"77.986328125\" x=\"11.0068359375\" y=\"8.43359375\">asset bundle<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n2\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.97265625\" x=\"2.5392475585936154\" y=\"-25.635106635436955\">1:1<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.025600000000054\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.17992697197425173\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n0\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-48.97602119140629\" y=\"-23.09200633216852\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.025599999999994\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5470891790487767\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n5\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"15.193962466822086\" y=\"30.674065521861735\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"44.894496863129426\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.25416574623968574\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n3\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"2.7719538085937074\" y=\"-26.04470613037104\">1..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"15.254400000000032\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.2090245199765919\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n3\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-57.39355288912964\" y=\"-63.846685518352906\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"43.47658308018222\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.881412889692355\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n5\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"582.664\" y=\"-3.598399999999913\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-17.888505566406252\" y=\"-42.41848039245597\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"16.483200000000004\" distanceToCenter=\"true\" position=\"right\" ratio=\"5.822600000000001\" segment=\"-2\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n6\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-12.223966589163297\" y=\"-24.76668258085064\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"11.385360101663515\" distanceToCenter=\"true\" position=\"left\" ratio=\"0.10670293331985262\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e7\" source=\"n7\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-9.313146217848043\" y=\"-26.098020948340803\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.669798038586146\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.1670192242704811\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e8\" source=\"n9\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-9.797233483694527\" y=\"-25.82443358614151\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.15599378957306\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.15481212573620068\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e9\" source=\"n8\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-15.633139430038\" y=\"-24.050683308790553\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"13.24330808446041\" distanceToCenter=\"true\" position=\"left\" ratio=\"0.05506723556297327\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e10\" source=\"n4\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-47.337621191406356\" y=\"-22.682408449706998\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"13.616000000000007\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e11\" source=\"n9\" target=\"n8\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-44.265598339843905\" y=\"-19.61040553222648\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.543999999999983\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.4117077892835431\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e12\" source=\"n7\" target=\"n6\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-44.81721357421975\" y=\"-19.610410805664003\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.543999999999983\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.4531592446547025\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources/>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-id/images/rbac-access-check-1.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-id/images/rbac-access-check-2.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-id/images/rbac-access-check-3.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-id/images/rbac-hierarchy-1.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n5\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-id/images/rbac-hierarchy-2.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-id/images/request-lifecycle.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"70.13700103759766\" width=\"56.558998107910156\" x=\"198.38633947503078\" y=\"261.099458694458\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"26.279499053955078\" y=\"74.13700103759766\">\n            <y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"-0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"0.5\" offsetX=\"0.0\" offsetY=\"4.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"27.34375\" x=\"-31.34375\" y=\"25.717914581298828\">user<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.5\" labelRatioY=\"0.0\" nodeRatioX=\"-0.5\" nodeRatioY=\"0.0\" offsetX=\"-4.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"637.1271178722382\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"41.158203125\" y=\"13.1494140625\">model<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"46.887996673583984\" width=\"39.527000427246094\" x=\"828.8018617630005\" y=\"544.9831195354463\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"53.376953125\" x=\"-6.924976348876953\" y=\"-30.723247528076172\">database<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-12.022075653076172\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"17.763500213623047\" y=\"21.443998336791992\">\n            <y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"713.6271178722382\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"28.005859375\" x=\"45.4970703125\" y=\"13.1494140625\">view<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n4\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"377.0372841596604\" width=\"219.27949905395508\" x=\"527.4919853210449\" y=\"407.9266515731811\"/>\n              <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"219.27949905395508\" x=\"0.0\" y=\"0.0\">controller</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"2\" leftF=\"1.7335329055786133\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"412.2765645980835\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 1</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n4:\">\n        <node id=\"n4::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"445.3031164169311\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"72.70703125\" x=\"38.92598342895508\" y=\"5.6494140625\">create action<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n4::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"45.0\" width=\"159.91864204406738\" x=\"558.5326642990112\" y=\"521.8166599273682\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"77.34765625\" x=\"41.28549289703369\" y=\"13.1494140625\">perform filters<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"diamond\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n4::n2\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"162.71328270435333\" width=\"187.54596614837646\" x=\"544.2255182266235\" y=\"607.2506530284882\"/>\n                  <y:Fill color=\"#99336635\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#993366\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#FFFFFF\" visible=\"true\" width=\"187.54596614837646\" x=\"0.0\" y=\"0.0\">action</y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"4\" bottomF=\"3.8368178606033325\" left=\"4\" leftF=\"3.9869680404663086\" right=\"3\" rightF=\"3.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 3</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n4::n2:\">\n            <node id=\"n4::n2::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"644.6271178722382\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.705078125\" x=\"43.92695999145508\" y=\"5.6494140625\">load model<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n4::n2::n1\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"721.1271178722382\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"66.02734375\" x=\"42.26582717895508\" y=\"5.6494140625\">render view<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n      </graph>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"198.3863394750308\" y=\"713.6271178722382\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"116.072265625\" x=\"1.4638671875000284\" y=\"13.1494140625\">response component<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"254.50096702575684\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"106.732421875\" x=\"6.1337890625\" y=\"13.1494140625\">request component<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"144.132230758667\" width=\"219.27949905395508\" x=\"527.4919853210449\" y=\"222.86873626708984\"/>\n              <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"219.27949905395508\" x=\"0.0\" y=\"0.0\">application</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"21\" leftF=\"20.720500946044922\" right=\"18\" rightF=\"18.0\" top=\"2\" topF=\"1.7557659149169922\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 2</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n7:\">\n        <node id=\"n7::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"262.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"73.369140625\" x=\"38.59492874145508\" y=\"5.6494140625\">resolve route<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n7::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"322.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"90.0390625\" x=\"30.259967803955078\" y=\"5.6494140625\">create controller<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <node id=\"n8\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"142.37646484375\" width=\"159.91864204406738\" x=\"313.2978515625\" y=\"224.62450218200684\"/>\n              <y:Fill color=\"#FFCC0024\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FFCC00\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"159.91864204406738\" x=\"0.0\" y=\"0.0\">entry script</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"1\" leftF=\"0.9186420440673828\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"225.33495140075684\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 4</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n8:\">\n        <node id=\"n8::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"129.0\" x=\"329.2164936065674\" y=\"262.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"85.3984375\" x=\"21.80078125\" y=\"5.6494140625\">load app config<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n8::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"129.0\" x=\"329.2164936065674\" y=\"322.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"82.052734375\" x=\"23.4736328125\" y=\"5.6494140625\">run application<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <edge id=\"e0\" source=\"n2\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n5\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-31.22050094604495\" sy=\"-22.49430537223816\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"28.00000600945461\" y=\"-193.17557203769684\">\n            <y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"17.34765625\" x=\"-8.673822115545391\" y=\"-200.52615797519684\">11<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n6\" target=\"n7::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-29.525518994973595\" y=\"-10.349628448486328\">3<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"0.9990329742431641\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.25395048761528816\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n7::e0\" source=\"n7::n0\" target=\"n7::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n0\" target=\"n8\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"-79.9882439360955\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"36.76878085201736\" y=\"-9.523307113000556\">1<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n7::n1\" target=\"n4\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"2.408647025755731\" ty=\"-181.21658369302781\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-4.962140296002758\" y=\"18.61224679946895\">4<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n3\" target=\"n4::n2::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-30.100868416252297\" y=\"-9.35060429573059\">9<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.26448415477215953\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n4::n2::n1\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"-2.2737367544323206E-13\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"17.34765625\" x=\"-83.18526519030615\" y=\"-9.350604295730818\">10<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.2786124840137136\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e7\" source=\"n8::n1\" target=\"n7\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"55.750299312644984\" sy=\"0.355224609375\" tx=\"-109.63961141576266\" ty=\"42.066115379333496\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"29.326010704040527\" y=\"-9.508390885372137\">2<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e8\" source=\"n1\" target=\"n4::n2::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-31.270312699786246\" y=\"-10.35060429573059\">8<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"1.0\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.2858946861565087\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e9\" source=\"n4::n1\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-79.9882439360951\" sy=\"2.2737367544323206E-13\" tx=\"30.048898971244075\" ty=\"-1.4999999999999991\">\n            <y:Point x=\"287.93523844627487\" y=\"544.3166599273684\"/>\n          </y:Path>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-76.70009317381192\" y=\"-9.350576400756609\">6<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.23459266172695797\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::e0\" source=\"n4::n0\" target=\"n4::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFEFD6\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-5.336933135986328\" y=\"4.999985313415493\">5<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.0\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::e1\" source=\"n4::n1\" target=\"n4::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"-81.01828954355172\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFEFD6\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-5.4491733540852465\" y=\"5.039560317993164\">7<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.0\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::n2::e0\" source=\"n4::n2::n0\" target=\"n4::n2::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n8::e0\" source=\"n8::n0\" target=\"n8::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"66px\" viewBox=\"0 0 57 66\" enable-background=\"new 0 0 57 66\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3799\" y1=\"-2276.8809\" x2=\"27.6209\" y2=\"-2306.6792\" gradientTransform=\"matrix(1 0 0 -1 0.2803 -2252.9199)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_13_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t&lt;path fill=\"#2068A3\" stroke=\"#2068A3\" d=\"M28.106,33.487c-8.112,0-12.688,4.312-12.688,10.437c0,7.422,12.688,10.438,12.688,10.438\n\t\ts14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.487,28.106,33.487z M26.288,53.051c0,0-7.135-2.093-8.805-7.201\n\t\tc-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"14.2417\" cy=\"9.1006\" r=\"53.247\" gradientTransform=\"matrix(1 0 0 -1 0.04 65.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#74AEEE\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#2068A3\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#2068A3\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.022,7.807-14.022,7.807s-10.472-2.484-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path fill=\"#5491CF\" stroke=\"#2068A3\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397c-0.514,1.027-1.669,4.084-1.669,5.148\n\t\tc0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.335-2.36c-3.601-1.419-4.071-3.063-5.89-4.854\n\t\tC12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t&lt;path fill=\"#5491CF\" stroke=\"#2068A3\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617c0.516,1.025,3.617,3.693,3.617,6.617\n\t\tc0,5.186-10.27,8.576-16.698,9.145c1.429,4.938,11.372,1.293,13.804-0.313c3.563-2.354,4.563-5.133,7.854-3.705\n\t\tC47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t&lt;path fill=\"none\" stroke=\"#2068A3\" stroke-linecap=\"round\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t&lt;path fill=\"none\" stroke=\"#2068A3\" stroke-linecap=\"round\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.623\" cy=\"-2278.646\" r=\"23.425\" fx=\"23.0534\" fy=\"-2281.1357\" gradientTransform=\"matrix(1 0 0 -1 0.2803 -2252.9199)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"5761.7578\" y1=\"11330.6484\" x2=\"5785.3872\" y2=\"11424.0977\" gradientTransform=\"matrix(0.275 0 0 0.2733 -1558.9874 -3088.4209)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M27.958,6.333c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.083,13.952,36.271,6.268,27.958,6.333z\"/&gt;\n\t&lt;path id=\"Hair_Young_Brown_1_\" fill=\"#CC9869\" stroke=\"#99724F\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n\t&lt;path fill=\"#4B4B4B\" stroke=\"#4B4B4B\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" d=\"M28.105,2\n\t\tC22.464,2,20.2,4.246,18.13,5.533C29.753,2.865,41.152,10.375,44.46,20.5C44.459,16.875,44.459,2,28.105,2z\"/&gt;\n\t&lt;path fill=\"#9B9B9B\" stroke=\"#4B4B4B\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" d=\"M11.151,17.751\n\t\tC12.878,8.25,18.686,6.309,25.273,7.127C31.295,7.875,36.93,10.491,44.459,20.5C37.777,7.125,20.278-3.375,9.903,3.921\n\t\tC5.569,6.97,4.903,13.375,11.151,17.751z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\"\n\t xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\t x=\"0px\" y=\"0px\" width=\"41px\" height=\"48px\" viewBox=\"-0.875 -0.887 41 48\" enable-background=\"new -0.875 -0.887 41 48\"\n\t xml:space=\"preserve\"&gt;\n&lt;defs&gt;\n&lt;/defs&gt;\n&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-979.1445\" x2=\"682.0508\" y2=\"-979.1445\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_1_)\" d=\"M19.625,36.763C8.787,36.763,0,34.888,0,32.575v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,34.888,30.464,36.763,19.625,36.763z\"/&gt;\n&lt;linearGradient id=\"SVGID_2_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-973.1445\" x2=\"682.0508\" y2=\"-973.1445\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_2_)\" d=\"M19.625,36.763c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.927-18.396,3.927\n\tc-9.481,0-17.396-1.959-18.396-3.927l-1.229,2C0,34.888,8.787,36.763,19.625,36.763z\"/&gt;\n&lt;path fill=\"#3C89C9\" d=\"M19.625,26.468c10.16,0,19.625,2.775,19.625,2.775c-0.375,2.721-5.367,5.438-19.554,5.438\n\tc-12.125,0-18.467-2.484-19.541-4.918C-0.127,29.125,9.465,26.468,19.625,26.468z\"/&gt;\n&lt;linearGradient id=\"SVGID_3_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-965.6948\" x2=\"682.0508\" y2=\"-965.6948\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_3_)\" d=\"M19.625,23.313C8.787,23.313,0,21.438,0,19.125v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,21.438,30.464,23.313,19.625,23.313z\"/&gt;\n&lt;linearGradient id=\"SVGID_4_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-959.6948\" x2=\"682.0508\" y2=\"-959.6948\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_4_)\" d=\"M19.625,23.313c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.926-18.396,3.926\n\tc-9.481,0-17.396-1.959-18.396-3.926l-1.229,2C0,21.438,8.787,23.313,19.625,23.313z\"/&gt;\n&lt;path fill=\"#3C89C9\" d=\"M19.476,13.019c10.161,0,19.625,2.775,19.625,2.775c-0.375,2.721-5.367,5.438-19.555,5.438\n\tc-12.125,0-18.467-2.485-19.541-4.918C-0.277,15.674,9.316,13.019,19.476,13.019z\"/&gt;\n&lt;linearGradient id=\"SVGID_5_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-952.4946\" x2=\"682.0508\" y2=\"-952.4946\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_5_)\" d=\"M19.625,10.113C8.787,10.113,0,8.238,0,5.925v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,8.238,30.464,10.113,19.625,10.113z\"/&gt;\n&lt;linearGradient id=\"SVGID_6_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-946.4946\" x2=\"682.0508\" y2=\"-946.4946\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_6_)\" d=\"M19.625,10.113c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.926-18.396,3.926\n\tc-9.481,0-17.396-1.959-18.396-3.926L0,5.925C0,8.238,8.787,10.113,19.625,10.113z\"/&gt;\n&lt;linearGradient id=\"SVGID_7_\" gradientUnits=\"userSpaceOnUse\" x1=\"644.0293\" y1=\"-943.4014\" x2=\"680.8223\" y2=\"-943.4014\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;ellipse fill=\"url(#SVGID_7_)\" cx=\"19.625\" cy=\"3.926\" rx=\"18.396\" ry=\"3.926\"/&gt;\n&lt;path opacity=\"0.24\" fill=\"#FFFFFF\" enable-background=\"new    \" d=\"M31.04,45.982c0,0-4.354,0.664-7.29,0.781\n\tc-3.125,0.125-8.952,0-8.952,0l-2.384-10.292l0.044-2.108l-1.251-1.154L9.789,23.024l-0.082-0.119L9.5,20.529l-1.65-1.254\n\tL5.329,8.793c0,0,4.213,0.903,7.234,1.07s8.375,0.25,8.375,0.25l3,9.875l-0.25,1.313l1.063,2.168l2.312,9.645l-0.521,1.416\n\tl1.46,1.834L31.04,45.982z\"/&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-id/intro-upgrade-from-v1.md",
    "content": "# Upgrade dari Versi 1.1\n\nAda banyak perbedaan antara versi 1.1 dan 2.0 karena Yii Framework benar-benar ditulis ulang di versi 2.0.\nAkibatnya, upgrade dari versi 1.1 tidak mudah seperti upgrade untuk versi minor. Dalam panduan ini Anda akan\nmenemukan perbedaan utama antara dua versi.\n\nJika Anda belum pernah menggunakan Yii 1.1 sebelumnya, Anda dapat dengan aman melewati bagian ini dan menuju ke \"[Persiapan](start-installation.md)\".\n\nHarap dicatat bahwa Yii 2.0 memperkenalkan lebih banyak fitur baru dari yang tercakup dalam ringkasan ini. Sangat dianjurkan\nAnda membaca keseluruhan panduan definitif untuk mempelajari hal tersebut. Ada kemungkinan bahwa\nbeberapa fitur yang sebelumnya harus anda kembangkan sendiri, kini telah menjadi bagian dari kode inti.\n\n## Instalasi\n\nYii 2.0 sepenuhnya menggunakan [composer](https://getcomposer.org/), yaitu dependency manager yang sudah diakui oleh PHP.\n\n-   Instalasi dari kerangka inti serta ekstensi, ditangani melalui Composer.\n-   Silakan merujuk ke bagian [Instalasi Yii](start-installation.md) untuk belajar cara menginstal Yii 2.0.\n-   Jika ingin membuat ekstensi baru, atau mengubah/memperbarui ekstensi 1.1 yang telah Anda buat ke ekstensi 2.0 (agar kompatibel), silakan merujuk pada panduan [Membuat Ekstensi](structure-extensions.md#menciptakan-ekstensi).\n\n## Persyaratan PHP\n\nYii 2.0 membutuhkan PHP 5.4 atau versi yang lebih tinggi, ini karena ada perubahan besar atas PHP versi 5.2 yang sebelumnya dibutuhkan oleh Yii 1.1. Akibatnya, ada banyak perbedaan pada tingkat bahasa yang harus Anda perhatikan.\nDibawah ini ringkasan perubahan utama mengenai PHP tersebut:\n\n-   [Namespaces](https://www.php.net/manual/en/language.namespaces.php).\n-   [Anonymous fungsi](https://www.php.net/manual/en/functions.anonymous.php).\n-   Sintaks array pendek `[... elemen ...]` digunakan sebagai pengganti `array (... elemen ...)`.\n-   Tags echo pendek `<=` digunakan dalam tampilan file. Ini aman digunakan mulai dari PHP 5.4.\n-   [Class SPL dan interface](https://www.php.net/manual/en/book.spl.php).\n-   [Late Static Bindings](https://www.php.net/manual/en/language.oop5.late-static-bindings.php).\n-   [Tanggal dan Waktu](https://www.php.net/manual/en/book.datetime.php).\n-   [Traits](https://www.php.net/manual/en/language.oop5.traits.php).\n-   [Intl](https://www.php.net/manual/en/book.intl.php). Yii 2.0 menggunakan `ekstensi PHP intl`\n      untuk mendukung fitur internasionalisasi.\n\n## Namespace\n\nPerubahan yang paling terlihat jelas dalam Yii 2.0 adalah penggunaan namespace. Hampir setiap kelas inti\nmenggunakan namespace, misalnya, `yii\\web\\Request`. Awalan yang sebelumnya menggunakan huruf \"C\" tidak lagi\ndigunakan dalam nama kelas. Skema penamaan sekarang mengikuti struktur direktori. Misalnya, `yii\\web\\Request`\nmengindikasikan bahwa file kelasnya adalah `web/Request.php` sesuai dengan lokasi folder framework Yii yang ada.\n\n(Anda dapat menggunakan setiap kelas inti tanpa menyertakannya secara eksplisit berkat Yiiclass loader.)\n\n## Komponen dan Object\n\nYii 2.0 membagi kelas `CComponent` di 1.1 menjadi dua kelas: [[yii\\base\\BaseObject]] dan [[yii\\base\\Component]].\nClass [[yii\\base\\BaseObject|BaseObject]] adalah class dasar ringan yang memungkinkan mendefinisikan [objek properti](concept-properties.md)\nmelalui getter dan setter. Class [[yii\\base\\Component|Component]] adalah perluasan(extends) dari [[yii\\base\\BaseObject|BaseObject]] dengan dukungan [Event](concept-events.md) dan [Behavior](concept-behaviors.md).\n\nJika class Anda tidak memerlukan fitur event atau behavior, Anda harus mempertimbangkan menggunakan\n[[yii\\base\\BaseObject|BaseObject]] sebagai class dasar. Hal ini biasanya terjadi untuk class yang mewakili\nstruktur data dasar.\n\n## Konfigurasi objek\n\nClass [[yii\\base\\BaseObject|BaseObject]] memperkenalkan cara seragam untuk mengkonfigurasi objek. Setiap class turunan\ndari [[yii\\base\\BaseObject|BaseObject]] harus menyatakan konstruktor (jika diperlukan) dengan cara berikut agar\ndapat dikonfigurasi dengan benar:\n\n```php\nclass MyClass extends \\yii\\base\\BaseObject\n{\n    public function __construct($param1, $param2, $config = [])\n    {\n        // ... inisialisasi sebelum konfigurasi diterapkan\n\n        parent::__construct($config);\n    }\n\n    public function init()\n    {\n        parent::init();\n\n        // ... inisialisasi setelah konfigurasi diterapkan\n    }\n}\n```\n\nDalam contoh diatas, parameter terakhir dari konstruktor harus mengambil array konfigurasi (`$config`)\nyang berisi pasangan nama-nilai `[key => value]` untuk meng-inisialisasi properti pada akhir konstruktor.\nAnda dapat mengganti method [[yii\\base\\BaseObject::init()|init()]] untuk melakukan inisialisasi yang harus dilakukan setelah\nkonfigurasi diterapkan.\n\nDengan mengikuti ketentuan ini, Anda akan dapat membuat dan mengkonfigurasi objek baru menggunakan array konfigurasi:\n\n```php\n$object = Yii::createObject([\n    'class' => 'MyClass',\n    'property1' => 'abc',\n    'property2' => 'cde',\n], [$param1, $param2]);\n```\n\nRincian lebih lanjut mengenai konfigurasi dapat ditemukan pada bagian [Konfigurasi](concept-configurations.md).\n\n## Event\n\nDi Yii 1, event dibuat dengan mendefinisikan method `on` (misalnya,`onBeforeSave`). Di Yii 2, Anda sekarang dapat menggunakan semua nama sebagai event.\nUntuk memicu suatu event, Anda dapat melakukannya dengan memanggil method [[yii\\base\\Component::trigger()|trigger()]]:\n\n```php\n$event = new \\yii\\base\\Event;\n$component->trigger($eventName, $event);\n```\n\nUntuk melekatkan/memasang handler pada suatu event, dapat dilakukan dengan mengunakan method [[yii\\base\\Component::on()|on()]]:\n\n```php\n$component->on($eventName, $handler);\n// Untuk mematikannya dapat dilakukan dengan cara:\n// $component->off($eventName, $handler);\n```\n\nAda banyak pengembangan dari fitur event. Untuk lebih jelasnya, silakan lihat pada bagian [Event](concept-events.md).\n\n## Path Alias\n\nYii 2.0 memperluas penggunaan alias path baik untuk file/direktori maupun URL. Yii 2.0 juga sekarang mensyaratkan\nnama alias dimulai dengan karakter `@`.\nMisalnya, alias `@yii` mengacu pada direktori instalasi Yii. Alias path\ndidukung di sebagian besar tempat di kode inti Yii. Misalnya, [[yii\\caching\\FileCache::cachePath]] dapat mengambil\nbaik alias path maupun direktori normal.\n\nSebuah alias juga terkait erat dengan namespace kelas. Disarankan alias didefinisikan untuk setiap akar namespace,\nsehingga memungkinkan Anda untuk menggunakan autoloader class Yii tanpa konfigurasi lebih lanjut.\nMisalnya, karena `@yii` mengacu pada direktori instalasi Yii, class seperti `yii\\web\\Request` dapat otomatis diambil.\nJika Anda menggunakan librari pihak ketiga seperti Zend Framework. Anda dapat menentukan alias path `@Zend` yang mengacu pada\ndirektori instalasi framework direktori. Setelah Anda selesai melakukannya, Yii akan dapat menload setiap class dalam librari Zend Framework.\n\nLebih jauh tentang alias path dapat pelajari pada bagian [Alias](concept-aliases.md).\n\n## View\n\nPerubahan yang paling signifikan tentang view di Yii 2 adalah bahwa variabel khusus `$this` dalam sebuah view tidak lagi mengacu\ncontroller saat ini atau widget. Sebaliknya, `$this` sekarang mengacu pada objek _view_, konsep baru\nyang diperkenalkan di 2.0. Objek _view_ adalah [[yii\\web\\View]], yang merupakan bagian view\ndari pola MVC. Jika Anda ingin mengakses controller atau widget di tampilan, Anda dapat menggunakan `$this->context`.\n\nUntuk membuat tampilan parsial dalam view lain, Anda menggunakan `$this->render()`, tidak lagi `$this->renderPartial()`.\nPanggilan untuk `render` juga sekarang harus secara eksplisit _di-echo)-kan_, mengingat method `render()` sekarang mengembalikan nilai yang dirender, bukan langsung menampilkannya. Sebagai contoh:\n\n```php\necho $this->render('_item', ['item' => $item]);\n```\n\nSelain menggunakan PHP sebagai bahasa template utama, Yii 2.0 juga dilengkapi dengan dukungan resmi\ndua _template engine_ populer: Smarty dan Twig. _Template engine_ Prado tidak lagi didukung.\nUntuk menggunakan mesin template ini, Anda perlu mengkonfigurasi komponen aplikasi `view` dengan menetapkan\nproperti [[yii\\base\\View::$renderers|View::$renderers]]. Silakan merujuk ke bagian [Template Engine](tutorial-template-engines.md)\nuntuk lebih jelasnya.\n\n## Model\n\nYii 2.0 menggunakan [[yii\\base\\Model]] sebagai model dasar, mirip dengan `CModel` di 1.1.\nclass `CFormModel` telah dibuang seluruhnya. Sebaliknya, di Yii 2 Anda harus memperluas [[yii\\base\\Model]] untuk membuat class model formulir.\n\nYii 2.0 memperkenalkan metode baru yang disebut [[yii\\base\\Model::scenario()|scenario()]] untuk menyatakan\nskenario yang didukung, dan untuk menunjukkan di mana skenario atribut perlu divalidasi serta atribut yang dapat dianggap sebagai aman atau tidak\ndll Sebagai contoh:\n\n```php\npublic function scenarios()\n{\n    return [\n        'backend' => ['email', 'role'],\n        'frontend' => ['email', '!role'],\n    ];\n}\n```\n\nDalam contoh di atas, dua skenario dinyatakan: `backend` dan` frontend`. Untuk `skenario backend`, baik\natribut `email` maupun` role` aman dan dapat diassign secara masal. Untuk `skenario frontend`,\n`email` dapat diassign secara masal sementara` role` tidak bisa. Kedua `email` dan` role` harus divalidasi sesuai aturan.\n\nMethod [[yii\\base\\Model::rules()|rules()]] ini masih digunakan untuk menyatakan aturan validasi. Perhatikan bahwa dengan dikenalkannya\n[[yii\\base\\Model::scenario()|scenario()]] sekarang tidak ada lagi validator `unsafe`.\n\nDalam kebanyakan kasus, Anda tidak perlu menimpa [[yii\\base\\Model::scenario()|scenario()]]\njika method [[yii\\base\\Model::rules()|rules()]] sepenuhnya telah menentukan skenario yang akan ada dan jika tidak ada kebutuhan untuk menyatakan\natribut `unsafe`.\n\nUntuk mempelajari lebih lanjut tentang model, silakan merujuk ke bagian [Model](structure-models.md).\n\n## Controller\n\nYii 2.0 menggunakan [[yii\\web\\Controller]] sebagai kelas dasar controller, yang mirip dengan `CController` di Yii 1.1.\n[[Yii\\base\\Action]] adalah kelas dasar untuk kelas action.\n\nDampak paling nyata dari perubahan ini pada kode Anda adalah bahwa aksi kontroler harus mengembalikan nilai konten\nalih-alih menampilkannya:\n\n```php\npublic function actionView($id)\n{\n    $model = \\app\\models\\Post::findOne($id);\n    if ($model) {\n        return $this->render('view', ['model' => $model]);\n    } else {\n        throw new \\yii\\web\\NotFoundHttpException;\n    }\n}\n```\n\nSilakan merujuk ke bagian [Controller](structure-controllers.md) untuk rincian lebih lanjut tentang controller.\n\n## widget\n\nYii 2.0 menggunakan [[yii\\base\\Widget]] sebagai kelas dasar widget, mirip dengan `CWidget` di Yii 1.1.\n\nUntuk mendapatkan dukungan yang lebih baik untuk kerangka di IDE, Yii 2.0 memperkenalkan sintaks baru untuk menggunakan widget.\nMetode statis [[yii\\base\\Widget::begin()|begin()]], [[yii\\base\\Widget::end()|end()]], dan [[yii\\base\\Widget::widget()|widget()]]\nmulai diperkenalkan, yang akan digunakan seperti:\n\n```php\nuse yii\\widgets\\Menu;\nuse yii\\widgets\\ActiveForm;\n\n// Perlu diingat bahwa \"echo\" tetap diperlukan untuk menampilkan hasilnya\necho Menu::widget(['items' => $items]);\n\n// Passing an array to initialize the object properties\n$form = ActiveForm::begin([\n    'options' => ['class' => 'form-horizontal'],\n    'fieldConfig' => ['inputOptions' => ['class' => 'input-xlarge']],\n]);\n... form input fields here ...\nActiveForm::end();\n```\n\nSilakan merujuk ke bagian [Widget](structure-widgets.md) untuk lebih jelasnya.\n\n## Tema\n\nTema benar-benar bekerja secara berbeda di 2.0. Kini tema telah berdasarkan mekanisme pemetaan path yang memetakan\nfile sumber ke file tema. Misalnya, jika peta path untuk tema adalah\n`['/web/views' => '/web/themes/basic']`, maka versi tema dari file view\n`/web/views/site/index.php` akan menjadi `/web/themes/basic/site/index.php`. Untuk alasan ini, tema sekarang bisa\nditerapkan untuk setiap file view, bahkan view diberikan di luar controller ataupun widget.\n\nJuga, tidak ada lagi komponen `CThemeManager`. Sebaliknya, `theme` adalah properti dikonfigurasi dari komponen `view`\npada aplikasi.\n\nSilakan merujuk ke bagian [Theming](output-theming.md) untuk lebih jelasnya.\n\n## Aplikasi konsol (CLI)\n\nAplikasi konsol sekarang diatur sebagai controller seperti pada aplikasi Web. kontroler konsol\nharus diperluas dari [[yii\\console\\Controller]], mirip dengan `CConsoleCommand` di 1.1.\n\nUntuk menjalankan perintah konsol, menggunakan `yii <route>`, di mana `<route>` adalah rute kontroler\n(Misalnya `sitemap/index`). Argumen anonim tambahan dilewatkan sebagai parameter ke\naction controller yang sesuai, sedangkan argumen bernama diurai menurut\ndeklarasi pada [[yii\\console\\Controller::options()]].\n\nYii 2.0 mendukung pembuatan informasi bantuan command secara otomatis berdasarkan blok komentar.\n\nSilakan lihat bagian [Console Commands](tutorial-console.md) untuk lebih jelasnya.\n\n## I18N\n\nYii 2,0 menghilangkan formater tanggal dan angka terpasang bagian dari [PECL modul intl PHP](https://pecl.php.net/package/intl).\n\nPenterjemahan pesan sekarang dilakukan melalui komponen aplikasi `i18n`.\nKomponen ini mengelola satu set sumber pesan, yang memungkinkan Anda untuk menggunakan pesan yang berbeda\nsumber berdasarkan kategori pesan.\n\nSilakan merujuk ke bagian [Internasionalisasi](tutorial-i18n.md) untuk rincian lebih lanjut.\n\n## Action Filter\n\nAction Filter sekarang diimplementasikan melalui behavior. Untuk membuat baru, filter diperluas dari [[yii\\base\\ActionFilter]].\nUntuk menggunakan filter, pasang Kelas filter untuk controller sebagai behavior. Misalnya, untuk menggunakan filter [[yii\\filters\\AccessControl]],\nAnda harus mengikuti kode berikut di kontroler:\n\n```php\npublic function behaviors()\n{\n    return [\n        'access' => [\n            'class' => 'yii\\filters\\AccessControl',\n            'rules' => [\n                ['allow' => true, 'actions' => ['admin'], 'roles' => ['@']],\n            ],\n        ],\n    ];\n}\n```\n\nSilakan merujuk ke bagian [Filtering](structure-filters.md) untuk lebih jelasnya.\n\n## Aset\n\nYii 2.0 memperkenalkan konsep baru yang disebut _bundel aset_ yang menggantikan konsep paket script di Yii 1.1.\n\nBundel aset adalah kumpulan file asset (misalnya file JavaScript, file CSS, file gambar, dll)\ndalam direktori. Setiap bundel aset direpresentasikan sebagai kelas turunan dari [[yii\\web\\AssetBundle]].\nDengan mendaftarkan bundel aset melalui [[yii\\web\\AssetBundle::register()]], Anda membuat\naset dalam bundel diakses melalui Web. Tidak seperti di Yii 1, halaman yang mendaftarkan bundel akan secara otomatis\nberisi referensi ke JavaScript dan file CSS yang ditentukan dalam bundel itu.\n\nSilakan merujuk ke bagian [Managing Aset](structure-assets.md) untuk lebih jelasnya.\n\n## Helper\n\nYii 2.0 memperkenalkan banyak helper umum untuk digunakan, termasuk.\n\n-   [[yii\\helpers\\Html]]\n-   [[yii\\helpers\\ArrayHelper]]\n-   [[yii\\helpers\\StringHelper]]\n-   [[yii\\helpers\\FileHelper]]\n-   [[yii\\helpers\\Json]]\n\nSilakan lihat bagian [Tinjauan Helper](helper-overview.md) untuk lebih jelasnya.\n\n## Formulir\n\nYii 2.0 memperkenalkan konsep _field_ untuk membangun formulir menggunakan [[yii\\widgets\\ActiveForm]]. Field\nadalah wadah yang terdiri dari label, masukan, pesan kesalahan, dan atau teks petunjuk.\nField diwakili sebagai objek [[yii\\widgets\\ActiveField|ActiveField]].\nMenggunakan field, Anda dapat membangun formulir yang lebih bersih dari sebelumnya:\n\n```php\n<?php $form = yii\\widgets\\ActiveForm::begin(); ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n    <div class=\"form-group\">\n        <?= Html::submitButton('Login') ?>\n    </div>\n<?php yii\\widgets\\ActiveForm::end(); ?>\n```\n\nSilakan merujuk ke bagian [Membuat Formulir](input-forms.md) untuk lebih jelasnya.\n\n## Query Builder\n\nDalam 1.1, query builder itu tersebar di antara beberapa kelas, termasuk `CDbCommand`,\n`CDbCriteria`, dan` CDbCommandBuilder`. Yii 2.0 merepresentasikan sebuah query DB sebagai objek [[yii\\db\\Query|Query]]\nyang dapat berubah menjadi sebuah pernyataan SQL dengan bantuan [[yii\\db\\QueryBuilder|QueryBuilder]].\nSebagai contoh:\n\n```php\n$query = new \\yii\\db\\Query();\n$query->select('id, name')\n      ->from('user')\n      ->limit(10);\n\n$command = $query->createCommand();\n$sql = $command->sql;\n$rows = $command->queryAll();\n```\n\nYang terbaik dari semua itu adalah, query builder juga dapat digunakan ketika bekerja dengan [Active Record](db-active-record.md).\n\nSilakan lihat bagian [Query Builder](db-query-builder.md) untuk lebih jelasnya.\n\n## Active Record\n\nYii 2.0 memperkenalkan banyak perubahan [Active Record](db-active-record.md). Dua yang paling jelas melibatkan\nquery builder dan penanganan permintaan relasional.\n\nKelas `CDbCriteria` di 1.1 digantikan oleh [[yii\\db\\ActiveQuery]] di Yii 2. Karena kelas tersebut adalah perluasan dari [[yii\\db\\Query]], dengan demikian\nmewarisi semua metode query builder. Anda bisa memanggil [[yii\\db\\ActiveRecord::find()]] untuk mulai membangun query:\n\n```php\n// Untuk mengambil semua customer yang *active* diurutkan sesuai ID:\n$customers = Customer::find()\n    ->where(['status' => $active])\n    ->orderBy('id')\n    ->all();\n```\n\nUntuk menyatakan suatu relasi, hanya dengan menentukan metod getter yang mengembalikan sebuah objek [[yii\\db\\ActiveQuery|ActiveQuery]].\nNama properti yang didefinisikan oleh getter akan menjadi nama relasi. Misalnya, kode berikut mendeklarasikan\nsebuah relasi `orders` (di 1.1, Anda akan harus menyatakan relasi di tempat `relations()`):\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public function getOrders()\n    {\n        return $this->hasMany('Order', ['customer_id' => 'id']);\n    }\n}\n```\n\nSekarang Anda dapat menggunakan `$customer->orders` untuk mengakses pesanan pelanggan dari tabel terkait. Anda juga dapat menggunakan kode berikut\nuntuk melakukan permintaan relasi secara cepat dengan kondisi permintaan yang disesuaikan:\n\n```php\n$orders = $customer->getOrders()->andWhere('status=1')->all();\n```\n\nKetika ingin memuat relasi, Yii 2.0 melakukannya secara berbeda dari 1.1. Secara khusus, di 1.1 query JOIN\nakan dibuat untuk memilih data utama dan data relasi. Di Yii 2.0, dua pernyataan SQL dijalankan\ntanpa menggunakan JOIN: pernyataan pertama membawa kembali data utama dan yang kedua membawa kembali data relasi\ndengan menyaring sesuai kunci primer dari data utama.\n\nAlih-alih mengembalikan objek [[yii\\db\\ActiveRecord|ActiveRecord]], Anda mungkin ingin menyambung dengan [[yii\\db\\ActiveQuery::asArray()|asArray()]]\nketika membangun query untuk mendapatkan sejumlah besar data. Hal ini akan menyebabkan hasil query dikembalikan\nsebagai array, yang dapat secara signifikan mengurangi waktu CPU yang dibutuhkan dan memori jika terdapat sejumlah besar data. Sebagai contoh:\n\n```php\n$customers = Customer::find()->asArray()->all();\n```\n\nPerubahan lain adalah bahwa Anda tidak dapat menentukan nilai default atribut melalui properti publik lagi.\nJika Anda membutuhkan mereka, Anda harus mengatur mereka dalam metode init kelas record Anda.\n\n```php\npublic function init()\n{\n    parent::init();\n    $this->status = self::STATUS_NEW;\n}\n```\n\nAda beberapa masalah dengan override konstruktor dari kelas ActiveRecord di 1.1. Ini tidak lagi hadir di\nversi 2.0. Perhatikan bahwa ketika menambahkan parameter ke constructor Anda mungkin harus mengganti [[yii\\db\\ActiveRecord::instantiate()]].\n\nAda banyak perubahan lain dan perangkat tambahan untuk Rekaman Aktif. Silakan merujuk ke\nbagian [Rekaman Aktif](db-active-record.md) untuk rincian lebih lanjut.\n\n## Active Record Behaviors\n\nDalam 2.0, kami telah membuang kelas behavior dasar `CActiveRecordBehavior`. Jika Anda ingin membuat behavior Active Record,\nAnda akan harus memperluasnya langsung dari `yii\\base\\Behavior`. Jika kelas behavior perlu menanggapi beberapa event\ndari pemilik, Anda harus mengganti method `events()` seperti berikut ini,\n\n```php\nnamespace app\\components;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\base\\Behavior;\n\nclass MyBehavior extends Behavior\n{\n    // ...\n\n    public function events()\n    {\n        return [\n            ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',\n        ];\n    }\n\n    public function beforeValidate($event)\n    {\n        // ...\n    }\n}\n```\n\n## Pengguna dan IdentityInterface\n\nKelas `CWebUser` di 1.1 kini digantikan oleh [[yii\\web\\User]], dan sekarang tidak ada lagi\nKelas `CUserIdentity`. Sebaliknya, Anda harus menerapkan [[yii\\web\\IdentityInterface]] yang\njauh lebih mudah untuk digunakan. Template proyek lanjutan memberikan contoh seperti itu.\n\nSilakan merujuk ke bagian [Otentikasi](security-authentication.md), [Otorisasi](security-authorization.md),\ndan [Template Proyek Lanjutan](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md) untuk lebih jelasnya.\n\n## Manajemen URL\n\nManajemen URL di Yii 2 mirip dengan yang di 1.1. Tambahan utamanya adalah, sekarang manajemen URL mendukung opsional\nparameter. Misalnya, jika Anda memiliki aturan dinyatakan sebagai berikut, maka akan cocok\nbaik dengan `post/popular` maupun `post/1/popular`. Dalam 1.1, Anda akan harus menggunakan dua aturan untuk mencapai\ntujuan yang sama.\n\n```php\n[\n    'pattern' => 'post/<page:\\d+>/<tag>',\n    'route' => 'post/index',\n    'defaults' => ['page' => 1],\n]\n```\n\nSilakan merujuk ke bagian [docs manajer Url](runtime-routing.md) untuk lebih jelasnya.\n\nPerubahan penting dalam konvensi penamaan untuk rute adalah bahwa nama-nama camelcase dari controller\ndan action sekarang dikonversi menjadi huruf kecil di mana setiap kata dipisahkan oleh hypen, misal controller\nid untuk `CamelCaseController` akan menjadi `camel-case`.\nLihat bagian tentang [Kontroler ID](structure-controllers.md#controller-ids) dan [Action ID](structure-controllers.md#action-ids) untuk lebih jelasnya.\n\n## Menggunakan Yii 1.1 dan 2.x bersama-sama\n\nJika Anda memiliki warisan kode Yii 1.1 yang ingin Anda gunakan bersama-sama dengan Yii 2.0, silakan lihat\nbagian [Menggunakan Yii 1.1 dan 2.0 Bersama](tutorial-yii-integration.md).\n"
  },
  {
    "path": "docs/guide-id/intro-yii.md",
    "content": "# Apa Itu Yii\n\nYii merupakan kerangka kerja berbasis komponen yang berkecepatan tinggi (high performance) yang digunakan untuk kasus\npengembangan yang cepat pada aplikasi Web Modern. Disebut Yii (diucapkan `Yee` atau `[ji:]`) yang berarti \"sederhana dan\nberevolusi\" dalam bahasa Cina. Bisa juga dianggap sebagai (akronim) singkatan dari **Yes It Is (Ya, Itu Dia)**!\n\n## Yii Paling Cocok Digunakan untuk Apa?\n\nYii adalah kerangka kerja pemrograman web yg umum(general), ini berarti kita bisa menggunakannya untuk membangun berbagai\nmacam aplikasi berbasis web menggunakan PHP. Karena merupakan arsitektur berbasis komponen yang mana memiliki dukungan\ncaching yang canggih, Yii sangat cocok untuk pengembangan aplikasi skala besar seperti web portal, forum, CMS, e-commerce,\nREST API dan lain sebagainnya.\n\n## Bagaimana jika Yii Dibandingkan dengan Frameworks lain?\n\nJika Anda sudah familiar dengan framework lain, Anda mungkin akan bersyukur ketika membandingkan Yii dengan yang lain:\n\n-   Seperti kebanyakan PHP framework, Yii mengimplementasikan pola arsitektur MVC (Model-View-Controller) dan mempromosikan kode organisasi berdasarkan pola itu.\n-   Yii mengambil filosofi bahwa kode harus ditulis dengan cara sederhana namun elegan. Yii tidak akan pernah mencoba untuk mendesain berlebihan terutama untuk mengikuti beberapa pola desain secara ketat.\n-   Yii adalah fullstack framework yang menyediakan banyak fitur teruji dan siap pakai seperti: query builder dan ActiveRecord baik untuk relasional maupun NoSQL database; dukungan pengembangan REST API; dukungan caching berlapis dan masih banyak lagi.\n-   Yii sangat extensible. Anda dapat menyesuaikan atau mengganti hampir setiap bagian dari kode inti Yii. Anda juga bisa mengambil keuntungan dari arsitektur ekstensi Yii yang solid untuk menggunakan atau mengembangkan ekstensi untuk disebarkan kembali.\n-   Kinerja tinggi (High performance) selalu menjadi tujuan utama dari Yii.\n\nYii tidak dikerjakan oleh satu orang, Yii didukung oleh [tim pengembang inti yang kuat][yii_team], serta komunitas besar\nprofesional yang terus memberikan kontribusi bagi pengembangan Yii. Tim pengembang Yii terus mengamati perkembangan tren\nterbaru Web, pada penerapan terbaik (best practices) serta fitur yang ditemukan dalam framework dan proyek lain.\nPenerapan terbaik yang paling relevan dan fitur yang ditemukan di tempat lain secara teratur dimasukkan ke dalam kerangka inti\ndan menampilkannya melalui antarmuka yang sederhana dan elegan.\n\n[yii_team]: https://www.yiiframework.com/team\n\n## Versi Yii\n\nYii saat ini memiliki dua versi utama yang tersedia: 1.1 dan 2.0. Versi 1.1 adalah generasi lama dan sekarang dalam mode pemeliharaan.\nVersi 2.0 adalah penulisan ulang lengkap dari Yii, mengadopsi teknologi dan protokol terbaru, termasuk composer, PSR, namespace, trait, dan sebagainya.\nVersi 2.0 merupakan generasi framework yang sekarang dan terus menerima upaya pengembangan selama beberapa tahun ke depan.\nPanduan ini terutama tentang versi 2.0.\n\n## Persyaratan dan Prasyarat\n\nYii 2.0 memerlukan PHP 7.4.0 atau versi lebih tinggi. Anda dapat menemukan persyaratan yang lebih rinci untuk setiap fitur\ndengan menjalankan pengecek persyaratan yang diikutsertakan dalam setiap rilis Yii.\n\nMenggunakan Yii memerlukan pengetahuan dasar tentang pemrograman berorientasi objek (OOP), mengingat Yii adalah framework berbasis OOP murni.\nYii 2.0 juga memanfaatkan fitur terbaru dari PHP, seperti [namespace](https://www.php.net/manual/en/language.namespaces.php) dan [traits](https://www.php.net/manual/en/language.oop5.traits.php).\nMemahami konsep-konsep ini akan membantu Anda lebih mudah memahami Yii 2.0.\n"
  },
  {
    "path": "docs/guide-id/start-databases.md",
    "content": "Bekerja dengan Database\n======================\n\nBagian ini akan memaparkan bagaimana membuat halaman yang menampilkan daftar data negara yang diambil dari\ntabel `country` pada database. Untuk menyelesaikan tugas ini, anda akan melakukan konfigurasi koneksi ke database,\nmembuat class [Active Record](db-active-record.md), membuat [action](structure-controllers.md),\ndan membuat [view](structure-views.md).\n\nSepanjang tutorial ini, anda akan mempelajari bagaimana cara untuk:\n\n* konfigurasi koneksi ke database,\n* membuat class _ActiveRecord_,\n* mengambil _(query)_ data menggunakan class _ActiveRecord_,\n* menampilkan data ke view dengan halaman per halaman.\n\nSebagai catatan untuk menyelesaikan bagian ini, anda harus memiliki pengetahuan dan pengalaman dasar dalam menggunakan database.\nSecara khusus, anda harus mengetahui cara membuat database, dan cara menjalankan perintah SQL menggunakan aplikasi klien database.\n\n\nMenyiapkan Database <span id=\"preparing-database\"></span>\n----------------------\n\nUntuk memulai, buatlah database dengan nama `yii2basic`, yang akan digunakan untuk mengambil data dalam aplikasi anda.\nAnda bisa membuat database SQLite, MySQL, PostgreSQL, MSSQL, atau Oracle, dimana Yii mendukung banyak aplikasi database. Untuk memudahkan, database yang digunakan adalah MySQL.\n\nSelanjutnya, buat tabel dengan nama `country` pada database, dan _insert_ beberapa data sampel. Anda bisa menjalankan perintah SQL dibawah untuk memudahkan:\n\n```sql\nCREATE TABLE `country` (\n  `code` CHAR(2) NOT NULL PRIMARY KEY,\n  `name` CHAR(52) NOT NULL,\n  `population` INT(11) NOT NULL DEFAULT '0'\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nINSERT INTO `country` VALUES ('AU','Australia',24016400);\nINSERT INTO `country` VALUES ('BR','Brazil',205722000);\nINSERT INTO `country` VALUES ('CA','Canada',35985751);\nINSERT INTO `country` VALUES ('CN','China',1375210000);\nINSERT INTO `country` VALUES ('DE','Germany',81459000);\nINSERT INTO `country` VALUES ('FR','France',64513242);\nINSERT INTO `country` VALUES ('GB','United Kingdom',65097000);\nINSERT INTO `country` VALUES ('IN','India',1285400000);\nINSERT INTO `country` VALUES ('RU','Russia',146519759);\nINSERT INTO `country` VALUES ('US','United States',322976000);\n```\n\nHingga saat ini, anda memiliki database bernama `yii2basic`, dan didalamnya terdapat tabel `country` dengan tiga kolom, berisi 10 baris data.\n\nKonfigurasi Koneksi Database <span id=\"configuring-db-connection\"></span>\n---------------------------\n\nSebelum melanjutkan, pastikan anda memasang ekstensi PHP [PDO](https://www.php.net/manual/en/book.pdo.php) dan\ndriver PDO untuk database yang anda gunakan (misal, `pdo_mysql` untuk MySQL). Ini adalah kebutuhan mendasar\njika aplikasi anda menggunakan _relational database_.\n\nJika sudah terpasang, buka file `config/db.php` dan sesuaikan parameter yang sesuai untuk database anda. Secara default,\nisi file konfigurasi tersebut adalah:\n\n```php\n<?php\n\nreturn [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=localhost;dbname=yii2basic',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n];\n```\n\nFile `config/db.php` adalah tipikal [konfigurasi](concept-configurations.md) yang menggunakan file. File konfigurasi seperti ini menentukan parameter-parameter\nyang dibutuhkan untuk membuat dan menginisialisasi objek [[yii\\db\\Connection]], dimana anda dapat menjalankan perintah SQL\ndengan database yang dituju.\n\nKonfigurasi koneksi database di atas dapat diakses pada kode aplikasi melalui _expression_ `Yii::$app->db`.\n\n> Info: File `config/db.php` akan di _include_ oleh konfigurasi aplikasi utama `config/web.php`,\n  yang berfungsi sebagai konfigurasi untuk inisialisasi objek [aplikasi](structure-applications.md).\n  Untuk penjelasan lebih lengkap, silahkan lihat bagian [Konfigurasi](concept-configurations.md).\n\nJika anda membutuhkan dukungan database yang tidak didukung oleh Yii, silahkan cek _extensions_ di bawah ini:\n\n- [Informix](https://github.com/edgardmessias/yii2-informix)\n- [IBM DB2](https://github.com/edgardmessias/yii2-ibm-db2)\n- [Firebird](https://github.com/edgardmessias/yii2-firebird)\n\n\nMembuat Active Record <span id=\"creating-active-record\"></span>\n-------------------------\n\nUntuk mengambil data di tabel `country`, buat class turunan [Active Record](db-active-record.md)\ndengan nama `Country`, dan simpan pada file `models/Country.php`.\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass Country extends ActiveRecord\n{\n}\n```\n\n_Class_ `Country` di _extends_ dari [[yii\\db\\ActiveRecord]]. Anda tidak perlu untuk menulis kode di dalamnya! Hanya dengan kode di atas,\nYii akan mengetahui nama tabel yang dimaksud dari nama _class_ tersebut.\n\n> Info: Jika nama _class_ tidak sesuai dengan nama tabel, anda dapat meng-_override_\n  method [[yii\\db\\ActiveRecord::tableName()]] untuk menentukan nama tabel secara eksplisit.\n\nMenggunakan class `Country`, anda bisa memanipulasi data pada tabel `country` dengan mudah, sebagaimana yang ditunjukkan pada kode di bawah ini:\n\n```php\nuse app\\models\\Country;\n\n// mengambil semua negara dari tabel country, dan mengurutkan berdasarkan \"name\" (nama)\n$countries = Country::find()->orderBy('name')->all();\n\n// mengambil negara yang memiliki primary key \"US\"\n$country = Country::findOne('US');\n\n// menampilkan \"United States\"\necho $country->name;\n\n// Mengganti nama negara menjadi \"U.S.A.\" dan menyimpan ke database\n$country->name = 'U.S.A.';\n$country->save();\n```\n\n> Info: _Active Record_ adalah cara yang efektif untuk mengakses dan memanipulasi data dari database secara _object-oriented_.\n  Anda bisa mengetahui lebih banyak lagi pada bagian [Active Record](db-active-record.md). Sebagai alternatif, anda mungkin berinteraksi dengan database menggunakan metode data akses yang lebih mendasar yang disebut [Data Access Objects](db-dao.md).\n\n\nMembuat Action <span id=\"creating-action\"></span>\n------------------\n\nUntuk menampilkan data negara ke pengguna, anda harus membuat _action_. Dibanding menempatkan _action_ baru ini pada _controller_ `site`\nseperti yang sudah anda lakukan pada bagian sebelumnya, sekarang ini ada baiknya membuat spesifik _controller_\nuntuk semua _action_ yang berhubungan dengan data negara. Namakan _controller_ baru ini dengan `CountryController`, dan buat\n_action_ `index` pada controller tersebut, seperti yang ditunjukkan di bawah ini.\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\nuse yii\\data\\Pagination;\nuse app\\models\\Country;\n\nclass CountryController extends Controller\n{\n    public function actionIndex()\n    {\n        $query = Country::find();\n\n        $pagination = new Pagination([\n            'defaultPageSize' => 5,\n            'totalCount' => $query->count(),\n        ]);\n\n        $countries = $query->orderBy('name')\n            ->offset($pagination->offset)\n            ->limit($pagination->limit)\n            ->all();\n\n        return $this->render('index', [\n            'countries' => $countries,\n            'pagination' => $pagination,\n        ]);\n    }\n}\n```\n\nSimpan kode di atas pada file `controllers/CountryController.php`.\n\n_Action_ `index` memanggil `Country::find()`. _Method_ _Active Record_ ini membuat _query_ ke database dan mengambil semua data negara dari tabel `country`.\nUntuk membatasi jumlah negara yang didapatkan pada setiap pengambilan data, _query_ tersebut dipecah menjadi halaman per halaman dengan bantuan dari\nobjek [[yii\\data\\Pagination]]. Objek `Pagination` diperuntukkan untuk dua tujuan:\n\n* Menentukan klausa `offset` dan `limit` pada perintah SQL yang digunakan untuk _query_ agar mengambil\n  hanya satu halaman data dalam sekali perintah (pada umumnya akan mengambil 5 baris dalam satu halaman).\n* Digunakan pada _view_ untuk menampilkan tombol halaman yang terdiri dari tombol-tombol nomor halaman, yang selanjutnya akan dijelaskan\n  pada sub bagian berikutnya.\n\nDi akhir kode, _action_ `index` me-_render_ _view_ dengan nama `index`, dan mengirimkan data negara beserta dengan informasi\nhalaman dari data tersebut.\n\n\nMembuat View <span id=\"creating-view\"></span>\n---------------\n\nDi dalam folder `views`, pertama-tama buatlah sub-folder dengan nama `country`. Folder ini akan digunakan untuk menyimpan semua\n_view_ yang akan di _render_ oleh _controller_ `country`. Di dalam folder `views/country`, buatlah file dengan nama `index.php`\nberisi kode di bawah ini:\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\LinkPager;\n?>\n<h1>Countries</h1>\n<ul>\n<?php foreach ($countries as $country): ?>\n    <li>\n        <?= Html::encode(\"{$country->name} ({$country->code})\") ?>:\n        <?= $country->population ?>\n    </li>\n<?php endforeach; ?>\n</ul>\n\n<?= LinkPager::widget(['pagination' => $pagination]) ?>\n```\n\nTerkait dengan tampilan data negara, _view_ ini terdiri dari dua bagian. Bagian pertama, dilakukan perulangan _(looping)_ pada data negara yang tersedia dan di-_render_ sebagai _unordered list_ HTML.\nBagian kedua, _widget_ [[yii\\widgets\\LinkPager]] di-_render_ menggunakan informasi halaman _(pagination)_ yang dikirimkan dari _action_.\n_Widget_ `LinkPager` menampilkan tombol-tombol halaman. Mengklik pada salah satu tombol tersebut akan melakukan pengambilan data negara\nterkait dengan halaman yang diklik.\n\n\nMari Kita Coba <span id=\"trying-it-out\"></span>\n-------------\n\nUntuk melihat bagaimana kode-kode di atas bekerja, gunakan browser anda untuk mengakses URL ini:\n\n```\nhttps://hostname/index.php?r=country%2Findex\n```\n\n![Daftar Country](images/start-country-list.png)\n\nAwalnya, anda akan melihat sebuah halaman yang menampilkan 5 negara. Dibawah daftar negara tersebut, anda akan melihat tombol halaman yang berjumlah empat tombol.\nJika anda mengklik tombol \"2\", anda akan melihat halaman tersebut menampilkan 5 negara lain pada database: halaman kedua pada record.\nSilahkan melakukan observasi secara perlahan-lahan dan anda akan mengetahui bahwa URL pada browser juga akan berganti menjadi\n\n```\nhttps://hostname/index.php?r=country%2Findex&page=2\n```\n\nDi belakang layar, [[yii\\data\\Pagination|Pagination]] menyediakan semua kebutuhkan untuk memecah data menjadi halaman per halaman:\n\n* Pertama-tama, [[yii\\data\\Pagination|Pagination]] menampilkan halaman pertama, dimana menjalankan perintah SELECT pada tabel `country`\n  dengan klausa `LIMIT 5 OFFSET 0`. Hasilnya, 5 negara pertama akan diambil dan ditampilkan.\n* _Widget_ [[yii\\widgets\\LinkPager|LinkPager]] me-_render_ tombol halaman menggunakan URL\n  yang dibentuk oleh method [[yii\\data\\Pagination::createUrl()|Pagination]]. URL tersebut mengandung _query string_ `page`, yang\n  merupakan representasi dari nomor halaman.\n* Jika anda mengklik tombol halaman \"2\", sebuah _request_ yang mengarah ke _route_ `country/index` akan dijalankan hingga selesai.\n  [[yii\\data\\Pagination|Pagination]] membaca _query string_ `page` dari URL dan kemudian menentukan halaman sekarang adalah halaman 2.\n  Query data negara yang baru mengandung klausa `LIMIT 5 OFFSET 5` dan mengambil 5 data negara selanjutnya untuk\n  kemudian ditampilkan.\n\n\nRangkuman <span id=\"summary\"></span>\n-------\n\nPada bagian ini, anda mempelajari bagaimana bekerja dengan database. Anda juga mempelajari bagaimana cara mengambil dan membagi\ndata dengan halaman per halaman dengan bantuan [[yii\\data\\Pagination]] dan [[yii\\widgets\\LinkPager]].\n\nDi bagian selanjutnya, anda akan mempelajari bagaimana menggunakan _generator_ kode yang disebut [Gii](https://github.com/yiisoft/yii2-gii/blob/master/docs/guide/README.md),\nuntuk membantu anda mengimplementasikan fitur-fitur umum pada aplikasi secara instan, seperti operasi _Create_-_Read_-_Update_-_Delete_ (CRUD)\nuntuk bekerja dengan data yang terdapat pada tabel di sebuah database. Sebenarnya, kode-kode yang barusan anda tulis, semuanya bisa\ndi _generate_ secara otomatis oleh Yii menggunakan tool Gii.\n"
  },
  {
    "path": "docs/guide-id/start-forms.md",
    "content": "Bekerja dengan Form\n==================\n\nBagian ini memaparkan bagaimana membuat halaman dengan form untuk mengambil data dari pengguna.\nHalaman akan menampilkan form dengan input field Nama dan Email.\nSetelah mendapatkan dua data dari pengguna, halaman akan menampilkan kembali data yang diinput pada form sebagai konfirmasi.\n\nUntuk mencapai tujuan, disamping membuat sebuah [_action_](structure-controllers.md), dan\ndua [_view_](structure-views.md), anda juga harus membuat [_model_](structure-models.md).\n\nSepanjang tutorial ini, anda akan mempelajari bagaimana cara untuk:\n\n* Membuat sebuah [model](structure-models.md) sebagai representasi data yang diinput oleh pengguna melalui form,\n* Membuat _rules_ untuk memvalidasi data yang telah diinput.\n* Membuat form HTML di dalam [view](structure-views.md).\n\nMembuat Model <span id=\"creating-model\"></span>\n----------------\n\nData yang akan diambil dari pengguna akan direpresentasikan oleh class model `EntryForm` sebagaimana ditunjukkan di bawah dan\ndi simpan pada file `models/EntryForm.php`. Silahkan membaca bagian [Class Autoloading](concept-autoloading.md)\nuntuk penjelasan lengkap mengenai penamaan file class.\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse Yii;\nuse yii\\base\\Model;\n\nclass EntryForm extends Model\n{\n    public $name;\n    public $email;\n\n    public function rules()\n    {\n        return [\n            [['name', 'email'], 'required'],\n            ['email', 'email'],\n        ];\n    }\n}\n```\n\nClass di _extends_ dari [[yii\\base\\Model]], class standar yang disediakan oleh Yii, yang secara umum digunakan\nuntuk representasi data dari form.\n\n> Info: [[yii\\base\\Model]] digunakan sebagai _parent_ untuk class model yang tidak berhubungan dengan database.\n  [[yii\\db\\ActiveRecord]] normalnya digunakan sebagai _parent_ untuk class model yang berhubungan dengan tabel di database.\n\nClass `EntryForm` terdiri dari dua _public property_, `name` dan `email`, dimana akan digunakan untuk menyimpan\ndata yang diinput oleh pengguna. Class ini juga terdapat _method_ yang dinamakan `rules()`, yang akan mengembalikan (_return_) sejumlah\npengaturan (_rules_) untuk memvalidasi data. Pengaturan validasi (_Validation Rules_) yang di deklarasikan harus mendeskripsikan bahwa\n\n* kedua field, yaitu `name` and `email` wajib di input\n* data `email` harus merupakan alamat email yang valid\n\nJika anda memiliki objek `EntryForm` yang sudah mengandung data yang di input oleh pengguna, anda boleh memanggil\nmethod [[yii\\base\\Model::validate()|validate()]] untuk melaksanakan validasi data. Kegagalan validasi data\nakan menentukan (_set_) property [[yii\\base\\Model::hasErrors|hasErrors]] menjadi `true`, dan anda dapat mengetahui pesan kegagalan validasi\nmelalui [[yii\\base\\Model::getErrors|errors]].\n\n```php\n<?php\n$model = new EntryForm();\n$model->name = 'Qiang';\n$model->email = 'bad';\nif ($model->validate()) {\n    // Valid!\n} else {\n    // Tidak Valid!\n    // Panggil $model->getErrors()\n}\n```\n\n\nMembuat Action <span id=\"creating-action\"></span>\n------------------\n\nSelanjutnya, anda harus membuat `entry` _action_ pada controller `site` yang akan memanfaatkan model yang baru saja dibuat. Proses\nmembuat dan menggunakan _action_ dijelaskan pada bagian [Mengatakan Hello](start-hello.md).\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\nuse app\\models\\EntryForm;\n\nclass SiteController extends Controller\n{\n    // ...kode lain...\n\n    public function actionEntry()\n    {\n        $model = new EntryForm();\n\n        if ($model->load(Yii::$app->request->post()) && $model->validate()) {\n            // data yang valid diperoleh pada $model\n\n            // lakukan sesuatu terhadap $model di sini ...\n\n            return $this->render('entry-confirm', ['model' => $model]);\n        } else {\n            // menampilkan form pada halaman, ada atau tidaknya kegagalan validasi tidak masalah\n            return $this->render('entry', ['model' => $model]);\n        }\n    }\n}\n```\n\nPertama-tama, _action_ membuat objek `EntryForm`. Kemudian objek tersebut membangun model\nmenggunakan data dari `$_POST`, yang disediakan oleh Yii dengan method [[yii\\web\\Request::post()]].\nJika model berhasil dibuat (misal, jika pengguna telah mengirim form HTML), _action_ akan memanggil method\n[[yii\\base\\Model::validate()|validate()]] untuk memastikan data yang di input tersebut valid.\n\n> Info : _Expression_ `Yii::$app` adalah representasi dari objek [aplikasi](structure-applications.md),\n  dimana objek tersebut adalah _singleton_ yang bebas diakses secara global. Objek tersebut juga merupakan [service locator](concept-service-locator.md) yang\n  menyediakan _components_ seperti `request`, `response`, `db`, dll. untuk mendukung pekerjaan yang spesifik.\n  Pada kode di atas, _component_ `request` dari objek aplikasi digunakan untuk mengakses data `$_POST`.\n\nJika tidak ada error, _action_ akan me-_render_ _view_ bernama `entry-confirm` untuk menginformasikan ke pengguna bahwa pengiriman\ndata tersebut berhasil. Jika tidak ada data yang dikirim atau data tersebut tidak valid, _view_ `entry` yang akan di _render_,\ndimana form HTML akan ditampilkan, beserta informasi kegagalan pengiriman form tersebut.\n\n> Note: Pada contoh sederhana ini kita hanya me-_render_ halaman konfirmasi jika data yang dikirim tersebut valid. Pada prakteknya,\n  anda harus pertimbangkan untuk menggunakan [[yii\\web\\Controller::refresh()|refresh()]] atau [[yii\\web\\Controller::redirect()|redirect()]]\n  untuk mencegah [permasalahan pengiriman form](https://en.wikipedia.org/wiki/Post/Redirect/Get).\n\n\nMembuat View <span id=\"creating-views\"></span>\n--------------\n\nTerakhir, buatlah dua file _view_ dengan nama `entry-confirm` dan `entry`. _View_ ini akan di-_render_ oleh _action_ `entry`,\nyang sebelumnya dibahas.\n\n_View_ `entry-confirm` hanya menampilkan data nama dan email. File _view_ tersebut harus di simpan di `views/site/entry-confirm.php`.\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n<p>You have entered the following information:</p>\n\n<ul>\n    <li><label>Name</label>: <?= Html::encode($model->name) ?></li>\n    <li><label>Email</label>: <?= Html::encode($model->email) ?></li>\n</ul>\n```\n\n_View_ `entry` akan menampilkan form HTML. File _view_ tersebut harus di simpan di `views/site/entry.php`.\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n?>\n<?php $form = ActiveForm::begin(); ?>\n\n    <?= $form->field($model, 'name') ?>\n\n    <?= $form->field($model, 'email') ?>\n\n    <div class=\"form-group\">\n        <?= Html::submitButton('Submit', ['class' => 'btn btn-primary']) ?>\n    </div>\n\n<?php ActiveForm::end(); ?>\n```\n\n_View_ ini menggunakan [_widget_](structure-widgets.md) yaitu [[yii\\widgets\\ActiveForm|ActiveForm]] untuk\nmembangun form HTML. _Method_ `begin()` dan `end()` dari _widget_ masing-masing berfungsi untuk me-_render_ tag pembuka dan penutup\ndari form tag. Diantara dua method tersebut, akan dibuat field input oleh\nmethod [[yii\\widgets\\ActiveForm::field()|field()]]. Input field yang pertama diperuntukkan untuk data \"name\",\ndan yang kedua diperuntukkan untuk data \"email\". Setelah field input, _method_ [[yii\\helpers\\Html::submitButton()]]\nakan dipanggil untuk me-_render_ tombol pengiriman data.\n\n\nMari kita uji <span id=\"trying-it-out\"></span>\n-------------\n\nUntuk melihat bagaimana prosesnya, gunakan browser anda untuk mengakses URL ini :\n\n```\nhttps://hostname/index.php?r=site%2Fentry\n```\n\nAnda akan melihat halaman yang menampilkan form dengan dua field input. Dibagian atas dari semua input field, ada label yang menginformasikan data yang mana yang akan diinput. Jika anda menekan tombol pengiriman data tanpa\nmenginput apapun, atau anda tidak menginput email address yang tidak valid, anda akan melihat pesan kegagalan yang di tampilkan di bagian bawah field input yang bermasalah.\n\n![Form yang validasinya gagal](images/start-form-validation.png)\n\nSetelah menginput nama dan alamat email yang benar dan menekan tombol kirim, anda akan melihat halaman baru\nyang menampilkan data yang barusan anda input.\n\n![Konfirmasi penginputan data](images/start-entry-confirmation.png)\n\n\n\n### Penjelasan <span id=\"magic-explained\"></span>\n\nAnda mungkin bertanya-tanya bagaimana form HTML bekerja dibelakang layar, sepertinya tampak ajaib karna form tersebut mampu\nmenampilkan label di setiap field input dan menampilkan pesan kegagalan jika anda tidak menginput data dengan benar\ntanpa me-_reload_ halaman.\n\nBetul, validasi data sebenarnya dilakukan di sisi klien menggunakan Javascript, dan selanjutnya dilakukan lagi di sisi server menggunakan PHP.\n[[yii\\widgets\\ActiveForm]] cukup cerdas untuk menerjemahkan pengaturan validasi yang anda deklarasikan pada class `EntryForm`,\nkemudian merubahnya menjadi kode Javascript, dan menggunakan Javascript untuk melakukan validasi data. Jika saja anda menonaktifkan\nJavascript pada browser anda, validasi tetap akan dilakukan di sisi server, sepertinya yang ditunjukkan pada\n_method_ `actionEntry`. Hal ini memastikan bahwa data akan divalidasi dalam segala kondisi.\n\n> Warning: Validasi melalui sisi klien akan membuat pengalaman pengguna lebih baik. Validasi di sisi server\n  harus selalu dilakukan, walaupun validasi melalui sisi klien digunakan atau tidak.\n\nLabel untuk field input dibuat oleh method `field()`, menggunakan nama _property_ dari model.\nContoh, label `Name` akan dibuat untuk _property_ `name`.\n\nAnda boleh memodifikasi label di dalam view menggunakan\nkode seperti di bawah ini:\n\n```php\n<?= $form->field($model, 'name')->label('Your Name') ?>\n<?= $form->field($model, 'email')->label('Your Email') ?>\n```\n\n> Info: Yii menyediakan banyak _widget_ untuk membantu anda dalam membangun _view_ yang kompleks dan dinamis.\n  Sebentar lagi anda akan mengetahui, bahwa menulis _widget_ juga sangat mudah. Anda mungkin akan mengganti sebagian besar\n  dari kode _view_ anda menjadi _widget-widget_ yang mampu digunakan ulang untuk menyederhanakan penulisan _view_ ke depannya.\n\n\nRangkuman <span id=\"summary\"></span>\n-------\n\nPada bagian kali ini, anda telah mengetahui semua bagian dari pola arsitektur MVC. Anda sudah mempelajari bagaimana\nuntuk membuat class model sebagai representasi data pengguna dan memvalidasinya.\n\nAnda juga mempelajari bagaimana mengambil data dari pengguna dan bagaimana menampilkan kembali data tersebut ke browser. Pekerjaan seperti ini\nbiasanya memakan waktu lama pada saat mengembangkan aplikasi, tetapi Yii menyediakan _widget_ yang bermanfaat\nyang akan membuat pekerjaan ini menjadi lebih mudah.\n\nDi bagian selanjutnya, anda akan mempelajari bagaimana untuk bekerja dengan database, dimana hal tersebut hampir sangat dibutuhkan pada setiap aplikasi.\n"
  },
  {
    "path": "docs/guide-id/start-gii.md",
    "content": "Membuat Kode menggunakan Gii\n========================\n\nBagian ini akan menjelaskan bagaimana cara menggunakan [Gii](https://github.com/yiisoft/yii2-gii/blob/master/docs/guide/README.md) untuk membuat kode secara otomatis\nyang mengimplementasikan fitur-fitur yang bersifat umum dalam sebuah web site. Menggunakan Gii untuk membuat kode sesederhana menginput informasi yang sesuai per satu instruksi seperti yang diterangkan pada halaman web Gii.\n\nSepanjang bagian ini, anda akan mempelajari bagaimana cara untuk:\n\n* Mengaktifkan Gii pada aplikasi anda,\n* Menggunakan Gii untuk membuat _class ActiveRecord_\n* Menggunakan Gii untuk membuat kode yang mengoperasikan CRUD untuk database,\n* Memodifikasi kode yang sudah dibuat oleh Gii.\n\n\nMemulai Gii <span id=\"starting-gii\"></span>\n------------\n\n[Gii](https://github.com/yiisoft/yii2-gii/blob/master/docs/guide/README.md) telah disediakan oleh Yii sebagai [module](structure-modules.md). Anda dapat mengaktifkan Gii\ndengan mengatur konfigurasi Gii pada properti [[yii\\base\\Application::modules|modules]] dari objek aplikasi. Tergantung bagaimana anda mengatur aplikasi anda, kode di bawah ini sudah disediakan pada file konfigurasi `config/web.php`:\n\n```php\n$config = [ ... ];\n\nif (YII_ENV_DEV) {\n    $config['bootstrap'][] = 'gii';\n    $config['modules']['gii'] = [\n        'class' => 'yii\\gii\\Module',\n    ];\n}\n```\n\nKonfigurasi di atas menyatakan bahwa, ketika mode [development environment](concept-configurations.md#environment-constants) aktif,\nmaka aplikasi harus mengikutkan module yang bernama `gii`, dimana objek tersebut merupakan class [[yii\\gii\\Module]].\n\nJika anda melihat [_entry script_](structure-entry-scripts.md) `web/index.php` pada aplikasi anda, anda akan\nmenemukan baris dibawah ini, yang menyatakan secara explisit bahwa `YII_ENV_DEV` sama dengan `true`.\n\n```php\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n```\n\nKarna baris tersebut, aplikasi anda harusnya sudah berada pada mode _development_, dan secara otomatis mengaktifkan Gii karena konfigurasi sebelumnya. Anda dapat mengakses Gii melalui URL di bawah ini:\n\n```\nhttps://hostname/index.php?r=gii\n```\n\n> Note: Jika anda mengakses Gii melalui komputer diluar komputer localhost anda, secara default akses tidak akan diperbolehkan\n> karna alasan keamanan. Anda dapat mengatur Gii untuk menambah alamat IP yang di perbolehkan seperti ini,\n>\n```php\n'gii' => [\n    'class' => 'yii\\gii\\Module',\n    'allowedIPs' => ['127.0.0.1', '::1', '192.168.0.*', '192.168.178.20'] // adjust this to your needs\n],\n```\n\n![Gii](images/start-gii.png)\n\n\nMembuat class _Active Record_ <span id=\"generating-ar\"></span>\n---------------------------------\n\nUntuk menggunakan Gii dalam membuat class Active Record, pilih \"Model Generator\" (dengan cara mengklik link pada halaman index Gii). Kemudian isi form dengan data berikut:\n\n* Table Name: `country`\n* Model Class: `Country`\n\n![Pembuat Model](images/start-gii-model.png)\n\nSelanjutnya, klik pada tombol \"Preview\". Anda akan melihat `models/Country.php` pada daftar class yang akan dibuat. Anda bisa mengklik nama dari class tersebut untuk melihat isi kodenya.\n\nPada saat menggunakan Gii, jika anda sudah membuat file dengan nama yang sama sebelumnya dan akan menimpanya, klik\ntombol `diff` disebelah nama file untuk melihat perbedaan antara kode yang akan dibuat\ndengan kode yang ada saat ini.\n\n![Preview Pembuat Model](images/start-gii-model-preview.png)\n\nJika akan menimpa file yang sudah ada, centang kotak di sebelah tulisan \"overwrite\" dan kemudian klik tombol \"Generate\". Jika anda membuat file baru, anda cukup mengklik tombol \"Generate\".\n\nSelanjutnya, anda akan melihat\nhalaman konfirmasi yang memberitahui bahwa kode berhasil dibuat. Jika sebelumnya anda sudah mempunyai file yang sama, anda juga akan melihat pesan yang memberitahukan bahwa file tersebut sudah ditimpa dengan file yang baru.\n\n\nMembuat CRUD <span id=\"generating-crud\"></span>\n--------------------\n\n_CRUD_ adalah _Create, Read, Update,_ dan _Delete_, yang merepresentasikan empat tugas umum yang melibatkan website secara umum. Untuk membuat _CRUD_ menggunakan Gii, pilih tombol \"CRUD Generator\" (dengan cara mengklik pada halaman index Gii). Untuk contoh \"negara\", isi form yang ditampilkan dengan data berikut:\n\n* Model Class: `app\\models\\Country`\n* Search Model Class: `app\\models\\CountrySearch`\n* Controller Class: `app\\controllers\\CountryController`\n\n![Pembuat CRUD](images/start-gii-crud.png)\n\nSelanjutnya, klik tombol \"Preview\". Anda akan melihat daftar file-file yang akan dibuat, seperti gambar dibawah ini.\n\n![Preview Pembuat CRUD](images/start-gii-crud-preview.png)\n\nJika anda sebelumnya sudah membuat file `controllers/CountryController.php` dan\n`views/country/index.php` (pada bagian bekerja dengan database), centang kotak \"overwrite\" untuk menimpa file tersebut. (File pada bagian bekerja dengan database tidak memiliki dukungan CRUD secara penuh.)\n\n\nMari kita coba <span id=\"trying-it-out\"></span>\n-------------\n\nUntuk melihat bagaimana proses kerjanya, gunakan browser anda untuk mengakses URL dibawah ini:\n\n```\nhttps://hostname/index.php?r=country%2Findex\n```\n\nAnda akan melihat tabel data yang menampilkan negara dari tabel pada database. Anda dapat mengurutkan tabel,\natau memfilter dengan menginput pencarian filter pada baris judul kolom.\n\nDisetiap negara yang tampil pada tabel, anda dapat memilih apakah akan melihat (_view_) detail, memperbaharui (_update_), atau menghapus (_delete_) data tersebut,\nanda juga dapat mengklik tombol \"Create Country\" yang berada di atas tabel tersebut untuk menampilkan form untuk membuat data negara yang baru.\n\n![Tabel data negara](images/start-gii-country-grid.png)\n\n![Memperbaharui data negara](images/start-gii-country-update.png)\n\nDibawah ini adalah daftar file yang dihasilkan oleh Gii, mungkin saja anda ingin melakukan observasi bagaimana fitur-fitur ini di implementasikan,\natau melakukan modifikasi terhadap file-file yang dihasilkan:\n\n* Controller: `controllers/CountryController.php`\n* Model: `models/Country.php` dan `models/CountrySearch.php`\n* View: `views/country/*.php`\n\n> Info: Gii di desain agar mudah di modifikasi, dan dikembangkan. Menggunakan Gii\n  dapat membuat pengembangan aplikasi anda menjadi lebih cepat. Untuk informasi lebih lanjut, silahkan melihat\n  bagian [Gii](https://github.com/yiisoft/yii2-gii/blob/master/docs/guide/README.md).\n\n\nPenutup <span id=\"summary\"></span>\n-------\n\nPada bagian ini, anda telah mengetahui bagaimana cara menggunakan Gii untuk membuat kode yang mengimplementasikan\nfungsi CRUD secara lengkap dalam mengelola data yang tersimpan pada database.\n"
  },
  {
    "path": "docs/guide-id/start-hello.md",
    "content": "Katakan Hello\n============\n\nBagian ini menjelaskan cara membuat halaman \"Hello\" baru dalam aplikasi Anda.\nUntuk mencapai tujuan ini, Anda akan membuat [action](structure-controllers.md#creating-actions) dan\nsebuah [view](structure-views.md):\n\n* Aplikasi ini akan mengirimkan permintaan halaman ke `action`.\n* Dan `action` pada gilirannya akan membuat tampilan yang menunjukkan kata \"Hello\" kepada pengguna akhir.\n\nMelalui tutorial ini, Anda akan belajar tiga hal:\n\n1. Cara membuat [action](structure-controllers.md#creating-actions) untuk menanggapi permintaan,\n2. Cara membuat [view](structure-views.md) untuk menyusun konten respon, dan\n3. bagaimana aplikasi mengirimkan permintaan ke [action](structure-controllers.md#creating-actions).\n\n\nMembuat Action <span id=\"creating-action\"></span>\n---------------\n\nUntuk tugas \"Hello\", Anda akan membuat [action](structure-controllers.md#creating-actions) `say` yang membaca\nparameter `message` dari request dan menampilkan pesan bahwa kembali ke pengguna. Jika request\ntidak memberikan parameter `message`, aksi akan menampilkan pesan \"Hello\".\n\n> Info: [Action](structure-controllers.md#creating-actions) adalah objek yang pengguna akhir dapat langsung merujuk ke\n  eksekusi. Action dikelompokkan berdasarkan [controllers](structure-controllers.md). Hasil eksekusi\n  action adalah respon yang pengguna akhir akan terima.\n\nAction harus dinyatakan di [controllers](structure-controllers.md). Untuk mempermudah, Anda mungkin\nmendeklarasikan action `say` di` SiteController` yang ada. kontroler ini didefinisikan\ndalam file kelas `controllers/SiteController.php`. Berikut adalah awal dari action baru:\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    // ...existing code...\n\n    public function actionSay($message = 'Hello')\n    {\n        return $this->render('say', ['message' => $message]);\n    }\n}\n```\n\nPada kode di atas, action `say` didefinisikan sebagai metode bernama` actionSay` di kelas `SiteController`.\nYii menggunakan awalan `action` untuk membedakan metode action dari metode non-action dalam kelas controller.\nNama setelah awalan `action` peta untuk ID tindakan ini.\n\nUntuk sampai pada penamaan action, Anda harus memahami bagaimana Yii memperlakukan ID action. ID action selalu\ndireferensikan dalam huruf kecil. Jika ID tindakan membutuhkan beberapa kata, mereka akan digabungkan dengan `tanda hubung`\n(Mis, `create-comment`). nama metode aksi yang dipetakan ke ID tindakan diperoleh dengan menghapus tanda hubung apapun dari ID,\nmengkapitalkan huruf pertama di setiap kata, dan awalan string yang dihasilkan dengan `action`. Sebagai contoh,\nID action `create-comment` sesuai dengan nama method action `actionCreateComment`.\n\nMetode action dalam contoh kita mengambil parameter `$message`, yang nilai defaultnya adalah `\"Hello\"` (persis\ndengan cara yang sama Anda menetapkan nilai default untuk fungsi atau metode apapun argumen di PHP). Ketika aplikasi\nmenerima permintaan dan menentukan bahwa action `say` bertanggung jawab untuk penanganan request, aplikasi akan\nmengisi parameter ini dengan parameter bernama sama yang ditemukan dalam request. Dengan kata lain, jika permintaan mencakup\na parameter `message` dengan nilai` \"Goodbye\" `, maka variabel `$message` dalam aksi akan ditugaskan nilai itu.\n\nDalam metode action, [[yii\\web\\Controller::render()|render()]] dipanggil untuk membuat\nsebuah [view](structure-views.md) dari file bernama `say`. Parameter `message` juga diteruskan ke view\nsehingga dapat digunakan di sana. Hasil render dikembalikan dengan metode tindakan. Hasil yang akan diterima\noleh aplikasi dan ditampilkan kepada pengguna akhir di browser (sebagai bagian dari halaman HTML yang lengkap).\n\n\nMembuat View <span id=\"creating-view\"></span>\n---------------\n\n[View](structure-views.md) adalah skrip yang Anda tulis untuk menghasilkan konten respon.\nUntuk \"Hello\" tugas, Anda akan membuat view `say` yang mencetak parameter `message` yang diterima dari metode aksi:\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n<?= Html::encode($message) ?>\n```\n\nView `say` harus disimpan dalam file `views/site/say.php`. Ketika metode [[yii\\web\\Controller::render()|render()]]\ndisebut dalam tindakan, itu akan mencari file PHP bernama `views/ControllerID/ViewName.php`.\n\nPerhatikan bahwa dalam kode di atas, parameter `message` adalah di-[[yii\\helpers\\Html::encode()|HTML-encoded]]\nsebelum dicetak. Hal ini diperlukan karena sebagai parameter yang berasal dari pengguna akhir, sangat rentan terhadap\n[serangan Cross-site scripting (XSS)](https://en.wikipedia.org/wiki/Cross-site_scripting) dengan melekatkan\nkode JavaScript berbahaya dalam parameter.\n\nTentu, Anda dapat menempatkan lebih banyak konten di view `say`. konten dapat terdiri dari tag HTML, teks biasa, dan bahkan pernyataan PHP.\nNyatanya, view `say` hanyalah sebuah script PHP yang dijalankan oleh metode [[yii\\web\\Controller::render()|render()]].\nIsi dicetak oleh skrip view akan dikembalikan ke aplikasi sebagai hasil respon ini. Aplikasi ini pada gilirannya akan mengeluarkan hasil ini kepada pengguna akhir.\n\n\nTrying it Out <span id=\"trying-it-out\"></span>\n-------------\n\nSetelah membuat action dan view, Anda dapat mengakses halaman baru dengan mengakses URL berikut:\n\n```\nhttps://hostname/index.php?r=site%2Fsay&message=Hello+World\n```\n\n![Hello World](images/start-hello-world.png)\n\nURL ini akan menghasilkan halaman yang menampilkan \"Hello World\". Halaman yang berbagi header dan footer yang sama dengan halaman aplikasi lainnya.\n\nJika Anda menghilangkan parameter `message` dalam URL, Anda akan melihat tampilan halaman \"Hello\". Hal ini karena `message` dilewatkan sebagai parameter untuk metode `actionSay()`, dan ketika itu dihilangkan,\nnilai default `\"Hello\"` akan digunakan sebagai gantinya.\n\n> Info: Halaman baru berbagi header dan footer yang sama dengan halaman lain karena metode [[yii\\web\\Controller::render()|render()]]\n  otomatis akan menanamkan hasil view `say` kedalam apa yang disebut [layout](structure-views.md#layouts) yang dalam hal ini\n  Kasus terletak di `views/layouts/main.php`.\n\nParameter `r` di URL di atas memerlukan penjelasan lebih lanjut. Ini adalah singkatan dari [route](runtime-routing.md), sebuah ID unik aplikasi\nyang mengacu pada action. format rute ini adalah `ControllerID/ActionID`. Ketika aplikasi menerima\npermintaan, itu akan memeriksa parameter ini, menggunakan bagian `ControllerID` untuk menentukan kontroler\nkelas harus dipakai untuk menangani permintaan. Kemudian, controller akan menggunakan bagian `ActionID`\nuntuk menentukan action yang harus dipakai untuk melakukan pekerjaan yang sebenarnya. Dalam contoh kasus ini, rute `site/say`\nakan diselesaikan dengan kontroler kelas `SiteController` dan action `say`. Sebagai hasilnya,\nmetode `SiteController::actionSay()` akan dipanggil untuk menangani permintaan.\n\n> Info: Seperti action, kontroler juga memiliki ID yang unik mengidentifikasi mereka dalam sebuah aplikasi.\n  ID kontroler menggunakan aturan penamaan yang sama seperti ID tindakan. nama kelas controller yang berasal dari\n  kontroler ID dengan menghapus tanda hubung dari ID, memanfaatkan huruf pertama di setiap kata,\n  dan suffixing string yang dihasilkan dengan kata `Controller`. Misalnya, controller ID `post-comment` berkorespondensi\n  dengan nama kelas controller `PostCommentController`.\n\n\nRingkasan <span id=\"summary\"></span>\n-------\n\nPada bagian ini, Anda telah menyentuh controller dan melihat bagian dari pola arsitektur MVC.\nAnda menciptakan sebuah action sebagai bagian dari controller untuk menangani permintaan khusus. Dan Anda juga menciptakan view\nuntuk menulis konten respon ini. Dalam contoh sederhana ini, tidak ada model yang terlibat. Satu-satunya data yang digunakan adalah parameter `message`.\n\nAnda juga telah belajar tentang rute di Yii, yang bertindak sebagai jembatan antara permintaan pengguna dan tindakan controller.\n\nPada bagian berikutnya, Anda akan belajar cara membuat model, dan menambahkan halaman baru yang berisi bentuk HTML.\n"
  },
  {
    "path": "docs/guide-id/start-installation.md",
    "content": "Instalasi Yii\n==============\n\nAnda dapat menginstal Yii dalam dua cara, menggunakan [Composer](https://getcomposer.org/) paket manager atau dengan mengunduh file arsip.\nYang pertama adalah cara yang lebih disukai, karena memungkinkan Anda untuk menginstal [ekstensi](structure-extensions.md)  baru atau memperbarui Yii dengan hanya menjalankan *command line*.\n\nHasil instalasi standar Yii baik framework maupun template proyek keduanya akan terunduh dan terpasang.\nSebuah template proyek adalah proyek Yii yang menerapkan beberapa fitur dasar, seperti login, formulir kontak, dll.\nKode diatur dalam cara yang direkomendasikan. Oleh karena itu, dapat berfungsi sebagai titik awal yang baik untuk proyek-proyek Anda.\n    \nDalam hal ini dan beberapa bagian berikutnya, kita akan menjelaskan cara menginstal Yii dengan apa yang disebut *Template Proyek Dasar* dan\nbagaimana menerapkan fitur baru di atas template ini. Yii juga menyediakan template lain yang disebut\nyang [Template Proyek Lanjutan](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md) yang lebih baik digunakan dalam lingkungan pengembangan tim\nuntuk mengembangkan aplikasi dengan beberapa tingkatan.\n\n> Info: Template Proyek Dasar ini cocok untuk mengembangkan 90 persen dari aplikasi Web. Ini berbeda\n  dari Template Proyek Lanjutan terutama dalam bagaimana kode mereka diatur. Jika Anda baru untuk Yii, kami sangat\n  merekomendasikan Anda tetap pada Template Proyek Dasar untuk kesederhanaan dan fungsi yang cukup.\n\n\nMenginstal melalui Komposer <span id=\"installing-via-composer\"></span>\n-----------------------\n\nJika Anda belum memiliki Composer terinstal, Anda dapat melakukannya dengan mengikuti petunjuk di\n[getcomposer.org] (https://getcomposer.org/download/). Pada Linux dan Mac OS X, Anda akan menjalankan perintah berikut:\n\n```bash\ncurl -sS https://getcomposer.org/installer | php\nmv composer.phar /usr/local/bin/composer\n```\n\nPada Windows, Anda akan mengunduh dan menjalankan [Composer-Setup.exe](https://getcomposer.org/Composer-Setup.exe).\n\nSilakan merujuk ke [Dokumentasi Composer](https://getcomposer.org/doc/) jika Anda menemukan\nmasalah atau ingin mempelajari lebih lanjut tentang penggunaan Composer.\n\nJika Composer sudah terinstal sebelumnya, pastikan Anda menggunakan versi terbaru. Anda dapat memperbarui Komposer\ndengan menjalankan `composer self-update`.\n\nDengan Komposer diinstal, Anda dapat menginstal Yii dengan menjalankan perintah berikut di bawah folder yang terakses web:\n\n```bash\ncomposer global require \"fxp/composer-asset-plugin:^1.4.1\"\ncomposer create-project --prefer-dist yiisoft/yii2-app-basic basic\n```\n\nPerintah pertama menginstal [komposer aset Plugin](https://github.com/fxpio/composer-asset-plugin)\nyang memungkinkan mengelola bower dan paket npm melalui Composer. Anda hanya perlu menjalankan perintah ini\nsekali untuk semua. Perintah kedua menginstal Yii dalam sebuah direktori bernama `basic`. Anda dapat memilih nama direktori yang berbeda jika Anda ingin.\n\n> Catatan: Selama instalasi, Composer  dapat meminta login Github Anda. Ini normal karena Komposer\n> Perlu mendapatkan cukup API rate-limit untuk mengambil informasi paket dari Github. Untuk lebih jelasnya,\n> Silahkan lihat [Documentation Composer](https://getcomposer.org/doc/articles/troubleshooting.md#api-rate-limit-and-oauth-tokens).\n\n> Tip: Jika Anda ingin menginstal versi pengembangan terbaru dari Yii, Anda dapat menggunakan perintah berikut sebagai gantinya,\n> Yang menambahkan [opsi stabilitas](https://getcomposer.org/doc/04-schema.md#minimum-stability):\n>\n> ```bash\n> composer create-project --prefer-dist --stability=dev yiisoft/yii2-app-basic basic\n> ```\n>\n> Perhatikan bahwa versi pengembangan dari Yii tidak boleh digunakan untuk produksi karena kemungkinan dapat *merusak* kode Anda yang sedang berjalan.\n\n\nInstalasi dari file Arsip <span id=\"installing-from-archive-file\"></span>\n-------------------------------\n\nInstalasi Yii dari file arsip melibatkan tiga langkah:\n\n1. Download file arsip dari [yiiframework.com](https://www.yiiframework.com/download/).\n2. Uraikan file yang didownload ke folder yang bisa diakses web.\n3. Memodifikasi `config/web.php` dengan memasukkan kunci rahasia untuk `cookieValidationKey`.\n   (Ini dilakukan secara otomatis jika Anda menginstal Yii menggunakan Composer):\n\n   ```php\n   // !!! Isikan nilai key jika kosong - ini diperlukan oleh cookie validation\n   'cookieValidationKey' => 'enter your secret key here',\n   ```\n\n\nPilihan Instalasi lainnya <span id=\"other-installation-options\"></span>\n--------------------------\n\nPetunjuk instalasi di atas menunjukkan cara menginstal Yii, yang juga menciptakan aplikasi Web dasar yang bekerja di luar kotak.\nPendekatan ini adalah titik awal yang baik untuk sebagian besar proyek, baik kecil atau besar. Hal ini terutama cocok jika Anda hanya\nmulai belajar Yii.\n\nTetapi ada pilihan instalasi lain yang tersedia:\n\n* Jika Anda hanya ingin menginstal kerangka inti dan ingin membangun seluruh aplikasi dari awal,\n  Anda dapat mengikuti petunjuk seperti yang dijelaskan dalam [Membangun Aplikasi dari Scratch](tutorial-start-from-scratch.md).\n* Jika Anda ingin memulai dengan aplikasi yang lebih canggih, lebih cocok untuk tim lingkungan pengembangan,\n  Anda dapat mempertimbangkan memasang [Template Lanjutan Proyek] (https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md).\n\n\nMemverifikasi Instalasi <span id=\"memverifikasi instalasi\"></span>\n--------------------------\n\nSetelah instalasi selesai, baik mengkonfigurasi web server Anda (lihat bagian berikutnya) atau menggunakan\n[Built-in web server PHP] (https://www.php.net/manual/en/features.commandline.webserver.php) dengan menjalankan berikut\nkonsol perintah sementara dalam proyek `web` direktori:\n\n```bash\nphp yii serve\n```\n\n> Catatan: Secara default HTTP-server akan mendengarkan port 8080. Namun jika port yang sudah digunakan atau Anda ingin\nmelayani beberapa aplikasi dengan cara ini, Anda mungkin ingin menentukan port apa yang harus digunakan. Cukup tambahkan argumen --port:\n\n```bash\nphp yii serve --port = 8888\n```\n\nAnda dapat menggunakan browser untuk mengakses aplikasi Yii yang diinstal dengan URL berikut:\n\n```\nhttp://localhost:8080/\n```\n\n![Instalasi Sukses dari Yii](images/start-app-installed.png)\n\nAnda seharusnya melihat halaman \"Congratulations!\" di browser Anda. Jika tidak, periksa apakah instalasi PHP Anda memenuhi\npersyaratan Yii. Anda dapat memeriksa apakah persyaratan minimumnya cocok dengan menggunakan salah satu pendekatan berikut:\n\n* Copy `/requirements.php` ke `/web/requirements.php` kemudian gunakan browser untuk mengakses melalui `http://localhost/requirements.php`\n* Jalankan perintah berikut:\n\n  ```bash\n  cd basic\n  php requirements.php\n  ```\n\nAnda harus mengkonfigurasi instalasi PHP Anda sehingga memenuhi persyaratan minimal Yii. Yang paling penting, Anda\nharus memiliki PHP versi 5.4 atau lebih. Anda juga harus menginstal [PDO PHP Ekstensi](https://www.php.net/manual/en/pdo.installation.php)\ndan driver database yang sesuai (seperti `pdo_mysql` untuk database MySQL), jika aplikasi Anda membutuhkan database.\n\n\nKonfigurasi Web Server <span id=\"configuring-web-servers\"></span>\n-----------------------\n\n> Info: Anda dapat melewati seksi ini untuk saat ini jika Anda hanya menguji sebuah Yii dengan niat\n  penggelaran itu untuk server produksi.\n\nAplikasi yang diinstal sesuai dengan petunjuk di atas seharusnya bekerja dengan baik\npada [Apache HTTP server](https://httpd.apache.org/) atau [Nginx HTTP server](https://nginx.org/), pada\nWindows, Mac OS X, atau Linux yang menjalankan PHP 5.4 atau lebih tinggi. Yii 2.0 juga kompatibel dengan facebook\n[HHVM](https://hhvm.com/). Namun, ada beberapa kasus di mana HHVM berperilaku berbeda dari PHP asli,\nsehingga Anda harus mengambil beberapa perlakuan ekstra ketika menggunakan HHVM.\n\nPada server produksi, Anda mungkin ingin mengkonfigurasi server Web Anda sehingga aplikasi dapat diakses\nmelalui URL `https://www.example.com/index.php` bukannya `https://www.example.com/dasar/web/index.php`. konfigurasi seperti itu\nmembutuhkan root dokumen server Web Anda menunjuk ke folder `basic/web`. Anda mungkin juga\ningin menyembunyikan `index.php` dari URL, seperti yang dijelaskan pada bagian [Routing dan Penciptaan URL](runtime-routing.md).\nDalam bagian ini, Anda akan belajar bagaimana untuk mengkonfigurasi Apache atau Nginx server Anda untuk mencapai tujuan tersebut.\n\n> Info: Dengan menetapkan `basic/web` sebagai akar dokumen, Anda juga mencegah pengguna akhir mengakses\nkode private aplikasi Anda dan file data sensitif yang disimpan dalam direktori sejajar\ndari `basic/web`. Mencegah akses ke folder lainnya adalah sebuah peningkatan keamanan.\n\n> Info: Jika aplikasi Anda akan berjalan di lingkungan shared hosting di mana Anda tidak memiliki izin\nuntuk memodifikasi konfigurasi server Web-nya, Anda mungkin masih menyesuaikan struktur aplikasi Anda untuk keamanan yang lebih baik. Silakan merujuk ke\nyang lebih baik. Lihat bagian [Shared Hosting Lingkungan](tutorial-shared-hosting.md) untuk rincian lebih lanjut.\n\n\n### Konfigurasi Apache yang Direkomendasikan <span id=\"recommended-apache-configuration\"></span>\n\nGunakan konfigurasi berikut di file `httpd.conf` Apache atau dalam konfigurasi virtual host. Perhatikan bahwa Anda\nharus mengganti `path/to/basic/web` dengan path ` dasar/web` yang sebenarnya.\n\n```apache\n# Set document root to be \"basic/web\"\nDocumentRoot \"path/to/basic/web\"\n\n<Directory \"path/to/basic/web\">\n    # use mod_rewrite for pretty URL support\n    RewriteEngine on\n    # If a directory or a file exists, use the request directly\n    RewriteCond %{REQUEST_FILENAME} !-f\n    RewriteCond %{REQUEST_FILENAME} !-d\n    # Otherwise forward the request to index.php\n    RewriteRule . index.php\n\n    # ...other settings...\n</Directory>\n```\n\n\n### Konfigurasi Nginx yang Direkomendasikan<span id=\"recommended-nginx-configuration\"></span>\n\nUntuk menggunakan [Nginx](https://wiki.nginx.org/), Anda harus menginstal PHP sebagai [FPM SAPI](https://www.php.net/install.fpm).\nAnda dapat menggunakan konfigurasi Nginx berikut, menggantikan `path/to/basic/web` dengan path yang sebenarnya untuk\n`basic/web` dan `mysite.test` dengan hostname yang sebenarnya untuk server.\n\n```nginx\nserver {\n    charset utf-8;\n    client_max_body_size 128M;\n\n    listen 80; ## listen for ipv4\n    #listen [::]:80 default_server ipv6only=on; ## listen for ipv6\n\n    server_name mysite.test;\n    root        /path/to/basic/web;\n    index       index.php;\n\n    access_log  /path/to/basic/log/access.log;\n    error_log   /path/to/basic/log/error.log;\n\n    location / {\n        # Redirect everything that isn't a real file to index.php\n        try_files $uri $uri/ /index.php$is_args$args;\n    }\n\n    # uncomment to avoid processing of calls to non-existing static files by Yii\n    #location ~ \\.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ {\n    #    try_files $uri =404;\n    #}\n    #error_page 404 /404.html;\n\n    location ~ \\.php$ {\n        include fastcgi_params;\n        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;\n        fastcgi_pass   127.0.0.1:9000;\n        #fastcgi_pass unix:/var/run/php5-fpm.sock;\n        try_files $uri =404;\n    }\n\n    location ~ /\\.(ht|svn|git) {\n        deny all;\n    }\n}\n```\n\nBila menggunakan konfigurasi ini, Anda juga harus menetapkan `cgi.fix_pathinfo=0` di file` php.ini`\nuntuk menghindari banyak panggilan `stat()` sistem yang tidak perlu.\n\nSekalian catat bahwa ketika menjalankan server HTTPS, Anda perlu menambahkan `fastcgi_param HTTPS on;` sehingga Yii\nbenar dapat mendeteksi jika sambungan aman.\n"
  },
  {
    "path": "docs/guide-id/start-looking-ahead.md",
    "content": "Menatap ke Depan\n================\n\nJika anda membaca sepanjang bab \"Mulai\", sekarang anda sudah membuat aplikasi dengan Yii. Pada proses ini, anda sudah mempelajari bagaimana mengimplementasikan fitur-fitur umum\nyang dibutuhkan, seperti mengambil data dari pengguna melalui form HTML, mengambil data dari database, dan\nmenampilkan data dengan halaman per halaman. Anda juga sudah mempelajari bagaimana menggunakan [Gii](https://github.com/yiisoft/yii2-gii/blob/master/docs/guide/README.md) untuk membuat\nkode secara otomatis. Menggunakan Gii dalam membuat kode, mengubah tugas-tugas pengembangan web yang cukup banyak menjadi satu tugas sederhana, sesederhana mengisi form.\n\nBagian ini akan merangkum bacaan Yii yang tersedia untuk membantu anda menjadi lebih produktif dalam menggunakan framework ini.\n\n* Dokumentasi\n    - [Panduan Definitif](https://www.yiiframework.com/doc-2.0/guide-README.html):\n      Sesuai dengan judulnya, panduan ini merincikan bagaimana Yii seharusnya bekerja dan menyediakan petunjuk umum\n      tentang menggunakan Yii. Panduan ini sangat-sangat penting, dan panduan ini yang harus anda baca\n      sebelum menulis kode Yii.\n    - [Referensi Class](https://www.yiiframework.com/doc-2.0/index.html):\n      Ini menjelaskan bagaimana menggunakan semua class yang disediakan oleh Yii. Pada umumnya anda akan menggunakan ini ketika anda sedang menulis\n      kode dan ingin memahami bagaimana penggunaan _class, method, property_. Sebaiknya anda membaca referensi class ini ketika anda memilik pemahaman dasar tentang seluruh bagian framework.\n    - [Artikel Wiki](https://www.yiiframework.com/wiki/?tag=yii2):\n      Artikel wiki ditulis oleh para pengguna Yii berdasarkan pengalaman pribadi masing-masing. Kebanyakan dari artikel ini ditulis\n      seperti layaknya panduan memasak, dan menunjukkan bagaimana menyelesaikan beberapa masalah dengan menggunakan Yii. Walaupun kualitas artikel-artikel ini\n      mungkin tidak selengkap Panduan Definitif, tetapi artikel ini terkadang lebih bermanfaat karna membahas topik yang cukup luas\n      dan mungkin mampu menyediakan solusi-solusi yang sederhana.\n    - [Buku](https://www.yiiframework.com/books)\n* [Extensions](https://www.yiiframework.com/extensions/):\n  Yang harus dibanggakan adalah Yii memiliki ribuan library extension yang dibuat oleh pengguna yang dapat dipasang di aplikasi anda dengan mudah, dan akan membuat pengembangan aplikasi anda lebih mudah dan cepat.\n* Komunitas\n    - Forum: <https://forum.yiiframework.com/>\n    - IRC: Kanal #yii di Libera (<ircs://irc.libera.chat:6697/yii>)\n    - Gitter: <https://gitter.im/yiisoft/yii2>\n    - GitHub: <https://github.com/yiisoft/yii2>\n    - Facebook: <https://www.facebook.com/groups/yiitalk/>\n    - Twitter: <https://twitter.com/yiiframework>\n    - LinkedIn: <https://www.linkedin.com/groups/yii-framework-1483367>\n    - Stackoverflow: <https://stackoverflow.com/questions/tagged/yii2>\n"
  },
  {
    "path": "docs/guide-id/start-prerequisites.md",
    "content": "# Apa yang perlu kamu ketahui\n\nKurva pembelajaran Yii tidak setajam/sesulit kerangka kerja PHP lainnya, tapi masih ada beberapa hal yang harus kamu pelajari sebelum mulai menggunakan Yii\n\n## PHP\n\nYii adalah kerangka kerja PHP jadi pastikan kamu telah [membaca dan memahami referensi dari bahasa ini](https://www.php.net/manual/en/langref.php).\nKetika membangun aplikasi menggunakan Yii, kamu akan menulis kode dengan gaya OOP (Bahasa Pemrograman Berbasis Objek), jadi pastikan juga kamu sudah cukup familiar dengan [Kelas and Objek](https://www.php.net/manual/en/language.oop5.basic.php) dan juga [namespaces](https://www.php.net/manual/en/language.namespaces.php).\n\n## Object oriented programming (Bahasa Pemrograman Berbasis Objek)\n\nMemahami dasar dari OOP sangat dibutuhkan. Jika kamu tidak cukup familiar dengan ini, silahkan pelajari salah satu dari banyak tutorial yang ada pada [tuts+ salah satunya](https://code.tutsplus.com/tutorials/object-oriented-php-for-beginners--net-12762).\n\nPerlu dicatat juga bahwa semakin kompleks aplikasi yang Anda buat, akan semakin dalam juga konsep OOP yang harus dipelajari untuk dapat berhasil mengelola kompleksitas tersebut.\n\n## Command line and composer\n\nYii secara umum menggunakan pengelolaan paket PHP standar, [Composer](https://getcomposer.org/) jadi pastikan bahwa kamu telah membaca dan memahami apa itu composer [panduan](https://getcomposer.org/doc/01-basic-usage.md). Jika kamu tidak cukup familiar menggunakan perintah baris (CLI), ini waktunya untuk coba mempelajari itu. Ketika Anda sudah mempelajari dasarnya, anda tidak akan mau lagi kerja tanpa menggunakan itu.\n"
  },
  {
    "path": "docs/guide-id/start-workflow.md",
    "content": "Menjalankan Aplikasi\n====================\n\nSetelah menginstal Yii, Anda memiliki aplikasi Yii yang dapat diakses melalui\nURL `https://hostname/basic/web/index.php` atau `https://hostname/index.php`, tergantung\npada konfigurasi Anda. Bagian ini akan memperkenalkan fungsi built-in aplikasi,\nbagaimana kode ini disusun, dan bagaimana aplikasi menangani permintaan secara umum.\n\n> Info: Untuk mempermudah, selama tutorial \"Mulai\", itu diasumsikan bahwa Anda telah menetapkan `basic/web`\n  sebagai root dokumen server Web Anda, dan URL dikonfigurasi untuk mengakses\n  aplikasi Anda untuk menjadi `https://hostname/index.php` atau sesuatu yang serupa.\n  Untuk kebutuhan Anda, silakan menyesuaikan URL sesuai deskripsi kami.\n  \nPerhatikan bahwa tidak seperti framework itu sendiri, setelah template proyek diinstal, itu semua milikmu. Anda bebas untuk menambah atau menghapus\nkode dan memodifikasi keseluruhannya sesuai yang Anda butuhkan.\n\n\nFungsi <span id=\"functionality\"></span>\n-------------\n\nAplikasi dasar diinstal berisi empat halaman:\n\n* Homepage, ditampilkan saat Anda mengakses URL `https://hostname/index.php`,\n* Halaman \"About\",\n* Halaman \"Contact\", yang menampilkan formulir kontak yang memungkinkan pengguna akhir untuk menghubungi Anda melalui email,\n* Dan halaman \"Login\", yang menampilkan form login yang dapat digunakan untuk otentikasi pengguna akhir. Cobalah masuk\n  dengan \"admin/admin\", dan Anda akan menemukan item \"Login\" di menu utama akan berubah menjadi \"Logout\".\n\nHalaman ini berbagi header umum dan footer. header berisi menu bar utama untuk memungkinkan navigasi\nantara halaman yang berbeda.\n\nAnda juga harus melihat toolbar di bagian bawah jendela browser.\nIni adalah [debugger tool](https://github.com/yiisoft/yii2-debug/blob/master/docs/guide/README.md) yang disediakan oleh Yii \nuntuk merekam dan menampilkan banyak informasi debug, seperti log pesan, status respon, query database berjalan, dan sebagainya.\n\nSelain itu untuk aplikasi web, ada script konsol yang disebut `yii`, yang terletak di direktori aplikasi dasar.\nScript ini dapat digunakan untuk menjalankan aplikasi background dan tugas pemeliharaan untuk aplikasi, yang diuraikan\ndi bagian [Console Application](tutorial-console.md).\n\n\nStruktur aplikasi <span id=\"application-structure\"></span>\n---------------------\n\nDirektori yang paling penting dan file dalam aplikasi Anda (dengan asumsi direktori root aplikasi adalah `basic`):\n\n```\nbasic/                  path aplikasi dasar\n    composer.json       digunakan oleh Composer, package information\n    config/             berisi konfigurasi aplikasi dan yang lain\n        console.php     konfigurasi aplikasi konsole\n        web.php         konfigurasi aplikasi web\n    commands/           contains console command classes\n    controllers/        contains controller classes\n    models/             contains model classes\n    runtime/            contains files generated by Yii during runtime, such as logs and cache files\n    vendor/             contains the installed Composer packages, including the Yii framework itself\n    views/              contains view files\n    web/                application Web root, contains Web accessible files\n        assets/         contains published asset files (javascript and css) by Yii\n        index.php       the entry (or bootstrap) script for the application\n    yii                 the Yii console command execution script\n```\n\nSecara umum, file dalam aplikasi dapat dibagi menjadi dua jenis: mereka yang di bawah `basic/web` dan mereka yang\ndi bawah direktori lain. Yang pertama dapat langsung diakses melalui HTTP (yaitu, di browser), sedangkan yang kedua tidak dapat dan tidak seharusnya boleh.\n\nYii mengimplementasikan pola arsitektur [model-view-controller (MVC)](https://wikipedia.org/wiki/Model-view-controller),\nyang tercermin dalam organisasi direktori di atas. Direktori `models` berisi semua [Model kelas](structure-models.md),\ndirektori `views` berisi semua [view script] structure-views.md), dan direktori `controllers` mengandung\nsemua [kelas kontroler](structure-controllers.md).\n\nDiagram berikut memperlihatkan struktur statis dari sebuah aplikasi.\n\n![Struktur statis aplikasi](images/application-structure.png)\n\nSetiap aplikasi memiliki naskah entri `web/index.php` yang merupakan satu-satunya PHP skrip dalam aplikasi yang dapat diakses web.\nNaskah entri mengambil permintaan masuk dan menciptakan [aplikasi](structure-applications.md) untuk menanganinya.\n[Aplikasi](structure-applications.md) menyelesaikan permintaan dengan bantuan [komponen](concept-components.md)nya,\ndan mengirimkan permintaan ke elemen MVC. [Widget](structure-widgets.md) digunakan dalam [view](structure-views.md)\nuntuk membantu membangun elemen antarmuka pengguna yang kompleks dan dinamis.\n\n\nDaur Hidup Request <span id=\"request-lifecycle\"></span>\n-----------------\n\nDiagram berikut menunjukkan bagaimana aplikasi menangani permintaan.\n\n![Request Lifecycle](images/request-lifecycle.png)\n\n1. Pengguna membuat permintaan ke [skrip entri](structure-entry-scripts.md) `web/index.php`.\n2. Naskah entri memuat [konfigurasi](concept-configurations.md) aplikasi dan menciptakan\n   [aplikasi](structure-applications.md) untuk menangani permintaan.\n3. Aplikasi menyelesaikan [route](runtime-routing.md) yang diminta dengan bantuan\n   komponen [request](runtime-requests.md) aplikasi.\n4. Aplikasi ini menciptakan [kontroler](structure-controllers.md) untuk menangani permintaan.\n5. Controller menciptakan [action](structure-controllers.md) dan melakukan filter untuk action.\n6. Jika filter gagal, aksi dibatalkan.\n7. Jika semua filter lulus, aksi dieksekusi.\n8. Action memuat model data, mungkin dari database.\n9. Aksi meyiapkan view, menyediakannya dengan model data.\n10. Hasilnya diberikan dikembalikan ke komponen aplikasi [respon](runtime-responses.md).\n11. Komponen respon mengirimkan hasil yang diberikan ke browser pengguna.\n"
  },
  {
    "path": "docs/guide-id/structure-application-components.md",
    "content": "Komponen Aplikasi\n=================\n\nObjek Aplikasi _(Application)_ adalah [service locators](concept-service-locator.md). Objek ini menampung seperangkat\napa yang kita sebut sebagai *komponen aplikasi* yang menyediakan berbagai layanan untuk menangani proses _request_. Sebagai contoh,\n_component_ `urlManager` bertanggung jawab untuk menentukan _route_ dari _request_ menuju _controller_ yang sesuai;\n_component_ `db` menyediakan layanan terkait database; dan sebagainya.\n\nSetiap _component_ aplikasi memiliki sebuah ID yang mengidentifikasi dirinya secara unik dengan _component_ aplikasi lainnya\ndi dalam aplikasi yang sama. Anda dapat mengakses _component_ aplikasi melalui _expression_ berikut ini:\n\n```php\n\\Yii::$app->componentID\n```\n\nSebagai contoh, anda dapat menggunakan `\\Yii::$app->db` untuk mengambil [[yii\\db\\Connection|koneksi ke DB]],\ndan `\\Yii::$app->cache` untuk mengambil [[yii\\caching\\Cache|cache utama]] yang terdaftar dalam aplikasi.\n\nSebuah _component_ aplikasi dibuat pertama kali pada saat objek tersebut pertama diakses menggunakan _expression_ di atas. Pengaksesan\nberikutnya akan mengembalikan objek _component_ yang sama.\n\n_Component_ aplikasi bisa merupakan objek apa saja. Anda dapat mendaftarkannya dengan mengatur\n_property_ [[yii\\base\\Application::components]] pada [konfigurasi aplikasi](structure-applications.md#application-configurations).\nSebagai contoh,\n\n```php\n[\n    'components' => [\n        // mendaftarkan component \"cache\" menggunakan nama class\n        'cache' => 'yii\\caching\\ApcCache',\n\n        // mendaftaran component \"db\" menggunakan konfigurasi array\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=demo',\n            'username' => 'root',\n            'password' => '',\n        ],\n\n        // mendaftaran component \"search\" menggunakan anonymous function\n        'search' => function () {\n            return new app\\components\\SolrService;\n        },\n    ],\n]\n```\n\n> Info: Walaupun anda dapat mendaftarkan _component_ aplikasi sebanyak yang anda inginkan, anda harus bijaksana dalam melakukan hal ini.\n  _Component_ aplikasi seperti layaknya variabel global. Menggunakan _component_ aplikasi yang terlalu banyak dapat berpotensi\n  membuat kode anda menjadi rumit untuk diujicoba dan dikelola. Dalam banyak kasus, anda cukup membuat _component_ lokal\n  dan menggunakannya pada saat diperlukan.\n\n\n## _Bootstrap Components_ <span id=\"bootstrapping-components\"></span>\n\nSeperti yang disebutkan di atas, sebuah _component_ aplikasi akan dibuat ketika _component_ diakses pertama kali.\nJika tidak diakses sepanjang _request_ diproses, objek tersebut tidak akan dibuat. Terkadang, anda ingin\nmembuat objek _component_ aplikasi tersebut untuk setiap _request_, walaupun _component_ tersebut tidak diakses secara eksplisit.\nUntuk melakukannya, anda dapat memasukkan ID _component_ tersebut ke _property_ [[yii\\base\\Application::bootstrap|bootstrap]] dari objek _Application_.\n\nSebagai contoh, konfigurasi aplikasi di bawah ini memastikan bahwa objek _component_ `log` akan selalu dibuat disetiap _request_:\n\n```php\n[\n    'bootstrap' => [\n        'log',\n    ],\n    'components' => [\n        'log' => [\n            // Konfigurasi untuk component \"log\"\n        ],\n    ],\n]\n```\n\n\n## _Component_ Aplikasi Inti <span id=\"core-application-components\"></span>\n\nYii menentukan seperangkat _component_ aplikasi inti dengan ID tetap dan konfigurasi default. Sebagai contoh,\n_component_ [[yii\\web\\Application::request|request]] digunakan untuk memperoleh informasi tentang\n_request_ dari pengguna dan merubahnya menjadi [route](runtime-routing.md). _Component_ [[yii\\base\\Application::db|db]]\nmerepresentasikan sebuah koneksi ke database yang bisa anda gunakan untuk menjalankan _query_ ke database.\nDengan bantuan _component_ inti inilah maka aplikasi Yii bisa menangani _request_ dari pengguna.\n\nDibawah ini adalah daftar dari _component_ aplikasi inti. Anda dapat mengatur dan memodifikasinya\nseperti _component_ aplikasi pada umumnya. Ketika anda mengatur _component_ aplikasi inti,\njika anda tidak mendefinisikan _class_-nya, maka _class_ default yang akan digunakan.\n\n* [[yii\\web\\AssetManager|assetManager]]: mengatur bundel aset _(asset bundles)_ dan publikasi aset _(asset publishing)_.\n  Harap melihat bagian [Pengelolaan Aset](structure-assets.md) untuk informasi lebih lanjut.\n* [[yii\\db\\Connection|db]]: merepresentasikan sebuah koneksi database yang bisa anda gunakan untuk melakukan _query_ ke database.\n  Sebagai catatan, ketika anda mengatur _component_ ini, anda harus menentukan nama _class_ dari _component_ dan _property_ lain dari\n  _component_ yang dibutuhkan, seperti [[yii\\db\\Connection::dsn]].\n  Harap melihat bagian [Data Access Objects](db-dao.md) untuk informasi lebih lanjut.\n* [[yii\\base\\Application::errorHandler|errorHandler]]: menangani error PHP dan _exception_.\n  Harap melihat bagian [Menangani Error](runtime-handling-errors.md) untuk informasi lebih lanjut.\n* [[yii\\i18n\\Formatter|formatter]]: memformat data ketika data tersebut ditampilkan ke pengguna. Sebagai contoh, sebuah angka\n  mungkin ditampilkan menggunakan separator ribuan, dan tanggal mungkin diformat dalam format panjang.\n  Harap melihat bagian [Memformat Data](output-formatting.md) untuk informasi lebih lanjut.\n* [[yii\\i18n\\I18N|i18n]]: mendukung penerjemahan dan format pesan _(message)_.\n  Harap melihat bagian [Internasionalisasi](tutorial-i18n.md) untuk informasi lebih lanjut.\n* [[yii\\log\\Dispatcher|log]]: mengelola target log.\n  Harap melihat bagian [Log](runtime-logging.md) untuk informasi lebih lanjut.\n* [[yii\\swiftmailer\\Mailer|mailer]]: mendukung pembuatan dan pengiriman email.\n  Harap melihat bagian [Mail](tutorial-mailing.md) untuk informasi lebih lanjut.\n* [[yii\\base\\Application::response|response]]: merepresentasikan _response_ yang dikirimkan ke pengguna.\n  Harap melihat bagian [Response](runtime-responses.md) untuk informasi lebih lanjut.\n* [[yii\\base\\Application::request|request]]: merepresentasikan _request_ yang diterima dari pengguna.\n  Harap melihat bagian [Request](runtime-requests.md) untuk informasi lebih lanjut.\n* [[yii\\web\\Session|session]]: merepresentasikan informasi _session_. _Component_ ini hanya tersedia pada\n  objek [[yii\\web\\Application|Aplikasi Web]].\n  Harap melihat bagian [Session dan Cookie](runtime-sessions-cookies.md) untuk informasi lebih lanjut.\n* [[yii\\web\\UrlManager|urlManager]]: mendukung penguraian dan pembuatan URL.\n  Harap melihat bagian [Route dan Pembuatan URL](runtime-routing.md) untuk informasi lebih lanjut.\n* [[yii\\web\\User|user]]: merepresentasikan informasi otentikasi dari pengguna. _Component_ ini hanya tersedia pada\n  objek [[yii\\web\\Application|Aplikasi Web]].\n  Harap melihat bagian [Otentikasi](security-authentication.md) untuk informasi lebih lanjut.\n* [[yii\\web\\View|view]]: mendukung proses _render view_.\n  Harap melihat bagian [View](structure-views.md) untuk informasi lebih lanjut.\n"
  },
  {
    "path": "docs/guide-id/structure-applications.md",
    "content": "Aplikasi\n========\n\nAplikasi _(Application)_ adalah objek yang mengelola semua struktur dan siklus dari sistem aplikasi Yii.\nSetiap aplikasi sistem Yii mengandung satu objek aplikasi yang dibuat dalam\n[skrip masuk](structure-entry-scripts.md) dan mampu diakses secara global melalui _expression_ `\\Yii::$app`.\n\n> Info: Jika kami mengatakan \"sebuah aplikasi\", itu bisa diartikan sebagai sebuah objek aplikasi\n  atau sebuah sistem aplikasi, tergantung bagaimana konteksnya.\n\nTerdapat dua tipe aplikasi: [[yii\\web\\Application|Aplikasi Web]] dan\n[[yii\\console\\Application|Aplikasi Konsol]]. Sesuai dengan namanya, yang pertama bertujuan untuk menangani\n_web request_, sedangkan yang kedua menangani _request_ perintah pada konsol.\n\n\n## Konfigurasi Aplikasi <span id=\"application-configurations\"></span>\n\nKetika [skrip masuk](structure-entry-scripts.md) membuat objek aplikasi, objek ini akan mengambil dan memuat\nsebuah [array konfigurasi](concept-configurations.md) dan menerapkannya pada objek aplikasi seperti berikut ini:\n\n```php\nrequire __DIR__ . '/../vendor/autoload.php';\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n\n// memuat konfigurasi aplikasi\n$config = require __DIR__ . '/../config/web.php';\n\n// membuat objek aplikasi & menerapkan konfigurasi\n(new yii\\web\\Application($config))->run();\n```\n\nSeperti layaknya [konfigurasi](concept-configurations.md) normal, konfigurasi aplikasi menentukan bagaimana\nproses inisialisasi _property_ dari objek aplikasi. Karena konfigurasi aplikasi pada umumnya\nsangat kompleks, oleh karena itu konfigurasi tersebut di simpan dalam [file konfigurasi](concept-configurations.md#configuration-files),\nseperti file `web.php` pada contoh di atas.\n\n\n## _Property_ Aplikasi <span id=\"application-properties\"></span>\n\nTerdapat cukup banyak _property_ aplikasi penting yang harus anda atur dalam konfigurasi aplikasi.\n_Property_ ini secara khusus menjelaskan _environment_ yang sedang dijalankan oleh aplikasi.\nSebagai contoh, aplikasi ingin mengetahui bagaimana cara memuat [controller](structure-controllers.md),\ndimana seharusnya aplikasi menyimpan file-file yang bersifat sementara _(temporary files)_, dll. Kami akan meringkas _property_ tersebut dibawah ini:\n\n\n### _Property_ Wajib <span id=\"required-properties\"></span>\n\nDalam aplikasi apapun, anda harus menentukan setidaknya dua _property_:[[yii\\base\\Application::id|id]]\ndan [[yii\\base\\Application::basePath|basePath]].\n\n\n#### [[yii\\base\\Application::id|id]] <span id=\"id\"></span>\n\n_Property_ [[yii\\base\\Application::id|id]] menentukan ID unik yang membedakan objek aplikasi\ndengan yang lainnya. Ini pada umumnya digunakan secara programatik. Walaupun hal ini bukanlah sebuah keharusan, karena persoalan pertukaran informasi,\nanda sangat direkomendasikan hanya menggunakan karakter alfanumerik ketika menentukan ID dari sebuah aplikasi.\n\n\n#### [[yii\\base\\Application::basePath|basePath]] <span id=\"basePath\"></span>\n\n_Property_ [[yii\\base\\Application::basePath|basePath]] menentukan direktori _root_ dari sebuah aplikasi.\nYaitu direktori yang menyimpan semua sumber kode aplikasi sistem, dan aksesnya diproteksi dari luar. Didalam direktori ini,\nanda akan melihat sub-direktori seperti `models`, `views`, dan `controllers` yang menyimpan sumber kode\ndari pola MVC.\n\nAnda dapat menentukan _property_ [[yii\\base\\Application::basePath|basePath]] menggunakan _directory path_\natau [path alias](concept-aliases.md). Kedua bentuk ini, direktori yang dimaksud harus benar-benar ada, jika tidak maka sebuah _exception_\nakan di-_throw_. _Path_ akan dinormalkan dengan memanggil _function_ `realpath()`.\n\n_Property_ [[yii\\base\\Application::basePath|basePath]] pada umumnya digunakan untuk mengambil _path_ penting\nlainnya (contoh _runtime path_). Karna itulah _alias path_ yang dinamakan `@app` disediakan untuk merepresentasikan _path_\nini. _Path-path_ lainnya boleh dipanggil menggunakan alias ini (contoh: `@app/runtime` untuk merujuk ke direktori runtime).\n\n\n### _Property_ Penting <span id=\"important-properties\"></span>\n\n_Property_ yang dijelaskan di sub-bagian ini cenderung harus di tentukan karena mereka digunakan secara berbeda\ndi lintas aplikasi.\n\n\n#### [[yii\\base\\Application::aliases|Alias]] <span id=\"aliases\"></span>\n\n_Property_ ini memungkinkan anda untuk menentukan seperangkat [alias](concept-aliases.md) dalam bentuk _array_.\n_Array Key_ merupakan nama alias, dan _Array Value_ adalah definisi path yang dimaksud.\nSebagai contoh:\n\n```php\n[\n    'aliases' => [\n        '@nama1' => 'path/menuju/ke/path1',\n        '@nama2' => 'path/menuju/ke/path2',\n    ],\n]\n```\n\nKarna tersedianya _property_ ini, anda bisa menentukan beberapa alias pada konfigurasi aplikasi dibanding\ndengan memanggil _method_ [[Yii::setAlias()]].\n\n\n#### [[yii\\base\\Application::bootstrap|bootstrap]] <span id=\"bootstrap\"></span>\n\n_Property_ ini merupakan _property_ yang bermanfaat. _Property_ ini memungkinkan anda untuk menentukan _component_ berbentuk _array_ yang harus\ndijalankan dalam [[yii\\base\\Application::bootstrap()|proses bootstrap]].\nSebagai contoh, jika anda memerintahkan sebuah [module](structure-modules.md) untuk merubah [pengaturan URL](runtime-routing.md),\nanda dapat menyusun ID-nya sebagai elemen dari _property_ ini.\n\nSetiap _component_ yang terdaftar pada _property_ ini dapat ditentukan berdasarkan salah satu dari format berikut ini:\n\n- ID dari _Component_ aplikasi yang ditentukan melalui [component](#components),\n- ID dari _module_ yang ditentukan melalui [module](#modules),\n- Nama _class_,\n- Konfigurasi _array_,\n- _anonymous function_ yang membuat dan mengembalikan _(return)_ sebuah _component_.\n\nSebagai contoh:\n\n```php\n[\n    'bootstrap' => [\n        // Component ID atau Module ID\n        'demo',\n\n        // Nama Class\n        'app\\components\\Profiler',\n\n        // Konfigurasi dalam bentuk array\n        [\n            'class' => 'app\\components\\Profiler',\n            'level' => 3,\n        ],\n\n        // anonymous function\n        function () {\n            return new app\\components\\Profiler();\n        }\n    ],\n]\n```\n\n> Info: Jika ID _module_ tersebut sama dengan ID _component_ aplikasi, _component_ aplikasi tersebut yang akan dipakai\n> pada saat proses _boostrap_. Jika anda ingin menggunakan _module_, anda dapat menentukannya melalui _anonymous function_\n> seperti berikut ini:\n>\n> ```php\n> [\n>     function () {\n>         return Yii::$app->getModule('user');\n>     },\n> ]\n> ```\n\n\nSepanjang proses _bootstrap_, setiap _component_ akan dibuat objeknya. Jika _class component_\nmengimplementasikan _method interface_ [[yii\\base\\BootstrapInterface]], _method_ [[yii\\base\\BootstrapInterface::bootstrap()|bootstrap()]] dari class tersebut\njuga akan dipanggil.\n\nSalah satu contoh praktis lainnya adalah konfigurasi aplikasi untuk [Template Proyek Dasar](start-installation.md),\ndimana _module_ `debug` dan `gii` ditentukan sebagai _component bootstrap_ ketika aplikasi sedang dijalankan\ndalam mode pengembangan:\n\n```php\nif (YII_ENV_DEV) {\n    // penyesuaian konfigurasi untuk environment 'dev'\n    $config['bootstrap'][] = 'debug';\n    $config['modules']['debug'] = 'yii\\debug\\Module';\n\n    $config['bootstrap'][] = 'gii';\n    $config['modules']['gii'] = 'yii\\gii\\Module';\n}\n```\n\n> Note: Menentukan terlalu banyak _component_ pada `bootstrap` akan menurunkan performa dari aplikasi anda, dikarenakan\n  _component_ yang sama tersebut harus dijalakan dalam setiap _request_. Jadi gunakanlah _component bootstrap_ dengan bijaksana.\n\n#### [[yii\\web\\Application::catchAll|catchAll]] <span id=\"catchAll\"></span>\n\n_Property_ ini hanya dikenali oleh [[yii\\web\\Application|Web applications]]. _Property_ ini menentukan\nsebuah [action dari controller](structure-controllers.md) yang ditugaskan menangani semua _request_ dari pengguna. _Property_ ini biasanya\ndigunakan ketika aplikasi dalam mode pemeliharaan _(maintenance)_ yang mengarahkan semua _request_ menuju satu _action_.\n\nKonfigurasinya yaitu sebuah _array_ dimana elemen pertama menentukan _route_ dari _action_.\nElement lainnya _(sepasang key-value)_ menentukan parameter yang akan diteruskan ke _action_. Sebagai contoh:\n\n```php\n[\n    'catchAll' => [\n        'offline/notice',\n        'param1' => 'value1',\n        'param2' => 'value2',\n    ],\n]\n```\n\n> Info: Panel _Debug_ pada _development environment_ tidak akan berfungsi ketika _property_ ini diisi.\n\n#### [[yii\\base\\Application::components|components]] <span id=\"components\"></span>\n\n_Property_ ini adalah salah satu _property_ yang sangat penting. _Property_ ini memperbolehkan anda mendaftarkan beberapa _component_\nyang disebut [_component_ aplikasi](structure-application-components.md) yang bisa anda gunakan di tempat lain. Sebagai contoh:\n\n```php\n[\n    'components' => [\n        'cache' => [\n            'class' => 'yii\\caching\\FileCache',\n        ],\n        'user' => [\n            'identityClass' => 'app\\models\\User',\n            'enableAutoLogin' => true,\n        ],\n    ],\n]\n```\n\nSetiap _component_ aplikasi ditentukan dengan sepasang _key-value_ ke dalam _array_. _Key_ merepresentasikan ID _component_,\ndimana _value_ merepresentasikan nama class dari _component_ atau [konfigurasi _array_](concept-configurations.md).\n\nAnda dapat mendaftaran _component_ apapun ke dalam objek aplikasi, dan nantinya _component_ tersebut dapat diakses secara global\nmenggunakan _expression_ `\\Yii::$app->componentID`.\n\nHarap membaca bagian [_Component_ Aplikasi](structure-application-components.md) untuk penjelasan lebih lanjut.\n\n\n#### [[yii\\base\\Application::controllerMap|controllerMap]] <span id=\"controllerMap\"></span>\n\n_Property_ ini memperbolehkan anda untuk melakukan _mapping_ sebuah ID _controller_ ke class _controller_ yang anda inginkan. Secara default, Yii melakukan mapping\nID _controller_ ke _class controller_ berdasarkan [kaidah yang ditentukan](#controllerNamespace) (Contoh: ID `post` akan di _mapping_\nke `app\\controllers\\PostController`). Dengan menentukan _property_ ini, anda diperbolehkan untuk tidak mengikuti kaidah untuk\nspesifik _controller_. Pada contoh dibawah ini, `account` akan di _mapping_ ke\n`app\\controllers\\UserController`, sedangkan `article` akan di _mapping_ ke `app\\controllers\\PostController`.\n\n```php\n[\n    'controllerMap' => [\n        'account' => 'app\\controllers\\UserController',\n        'article' => [\n            'class' => 'app\\controllers\\PostController',\n            'enableCsrfValidation' => false,\n        ],\n    ],\n]\n```\n\n_Key array_ dari _property_ ini merepresentasikan ID _controller_, sedangkan _value_ merepresentasikan nama _class\nyang dimaksud atau [konfigurasi _array_](concept-configurations.md).\n\n\n#### [[yii\\base\\Application::controllerNamespace|controllerNamespace]] <span id=\"controllerNamespace\"></span>\n\n_Property_ ini menentukan _namespace_ default dimana _class controller_ tersebut harus dicari. Default ke\n`app\\controllers`. Jika ID _controller_ adalah `post`, secara kaidah,  nama _class controller_-nya (tanpa\n_namespace_) adalah `PostController`, dan `app\\controllers\\PostController` adalah nama class lengkapnya _(Fully Qualified Class Name)_.\n\n_class controller_ juga boleh disimpan dalam sub-direktori dari direktori yang dimaksud _namespace_ ini.\nSebagai contoh, jika ada ID _controller_ `admin/post`, nama class lengkap yang dimaksud adalah\n`app\\controllers\\admin\\PostController`.\n\nSangatlah penting bahwa nama class lengkap dari _controller_ tersebut [bisa di-autoload](concept-autoloading.md)\ndan _namespace_ dari _class controller_ anda cocok dengan nilai dari _property_ ini. Jika tidak,\nanda akan melihat error \"Halaman tidak ditemukan\" ketika mengakses aplikasi.\n\nJika saja anda tidak ingin mengikut kaidah-kaidah yang dijelaskan di atas, anda boleh menentukan _property_\n[controllerMap](#controllerMap).\n\n\n#### [[yii\\base\\Application::language|language]] <span id=\"language\"></span>\n\n_Property_ ini menentukan bahasa apa yang seharusnya ditampilkan pada konten aplikasi ke pengguna.\nNilai default dari _property_ ini adalah `en`, yang merupakan Bahasa Inggris. Anda harus menentukan _property_ ini\njika aplikasi anda menyediakan konten dalam berbagai bahasa.\n\nNilai dari _property_ ini menentukan banyak aspek dari [internasionalisasi](tutorial-i18n.md),\ntermasuk penerjemahan pesan, format tanggal, format penomoran, dll. Sebagai contoh, _widget_ [[yii\\jui\\DatePicker]]\nakan menggunakan _property_ ini secara _default_ untuk menentukan bahasa apa yang digunakan pada kalender yang ditampilkan dan bagaimana\nformat tanggal pada kalender tersebut.\n\nDisarankan agar anda menentukan bahasa dalam format [Tag Bahasa IETF](https://en.wikipedia.org/wiki/IETF_language_tag).\nSebagai contoh, `en` berarti Bahasa Inggris, sedangkan `en-US` berarti Bahasa Inggris yang digunakan di Amerika Serikat.\n\nInformasi selengkapnya mengenai _property_ ini dapat dipelajari di bagian [Internasionalisasi](tutorial-i18n.md).\n\n\n#### [[yii\\base\\Application::modules|modules]] <span id=\"modules\"></span>\n\n_Property_ ini menentukan [module](structure-modules.md) apa yang akan digunakan oleh aplikasi.\n\n_Property_ ini ditentukan menggunakan _array_ dari _class class modul_ atau [konfigurasi _array_](concept-configurations.md) dimana _array key_\nmerupakan ID dari _module_ tersebut. Berikut contohnya:\n\n```php\n[\n    'modules' => [\n        // modul \"booking\" dengan class module yang ditentukan\n        'booking' => 'app\\modules\\booking\\BookingModule',\n\n        // modul \"comment\" yang ditentukan menggunakan konfigurasi array\n        'comment' => [\n            'class' => 'app\\modules\\comment\\CommentModule',\n            'db' => 'db',\n        ],\n    ],\n]\n```\n\nSilahkan melihat bagian [Modules](structure-modules.md) untuk informasi lebih lanjut.\n\n\n#### [[yii\\base\\Application::name|name]] <span id=\"name\"></span>\n\n_Property_ ini menentukan nama aplikasi yang bisa ditampilkan ke pengguna. Berbeda dengan\n_property_ [[yii\\base\\Application::id|id]], yang mengharuskan nilainya unik, nilai dari _property_ ini secara umum bertujuan untuk\nkeperluan tampilan saja; tidak perlu unik.\n\nAnda tidak perlu menentukan _property_ ini jika memang tidak ada kode anda yang akan menggunakannya.\n\n\n#### [[yii\\base\\Application::params|params]] <span id=\"params\"></span>\n\n_Property_ ini menentukan parameter berbentuk _array_ yang bisa diakses secara global oleh aplikasi. Dibanding menuliskan secara manual\nangka dan _string_ di kode anda, merupakan hal yang bagus jika anda menentukan hal tersebut sebagai parameter-parameter aplikasi\ndi satu tempat yang sama, dan menggunakannya pada tempat dimana dia dibutuhkan. Sebagai contoh, anda mungkin menentukan ukuran _thumbnail_\nsebagai parameter seperti contoh dibawah ini:\n\n```php\n[\n    'params' => [\n        'thumbnail.size' => [128, 128],\n    ],\n]\n```\n\nKemudian, pada kode dimana anda akan menggunakan ukuran tersebut, anda cukup menggunakannya seperti kode dibawah ini:\n\n```php\n$size = \\Yii::$app->params['thumbnail.size'];\n$width = \\Yii::$app->params['thumbnail.size'][0];\n```\n\nJika di suatu hari anda memutuskan untuk mengganti ukuran _thumbnail_ tersebut, anda cukup menggantinya di konfigurasi aplikasi;\nanda tidak perlu mengganti di semua kode dimana anda menggunakannya.\n\n\n#### [[yii\\base\\Application::sourceLanguage|sourceLanguage]] <span id=\"sourceLanguage\"></span>\n\n_Property_ ini menentukan bahasa apa yang digunakan dalam menulis kode aplikasi. Nilai default-nya adalah `'en-US'`,\nyang berarti Bahasa Inggris (Amerika Serikat). Anda sebaiknya menentukan _property_ ini jika teks pada kode anda bukanlah Bahasa Inggris.\n\nSeperti layaknya _property_ [language](#language), anda seharusnya menentukan _property_ ini dalam\nformat [Tag Bahasa IETF](https://en.wikipedia.org/wiki/IETF_language_tag). Sebagai contoh, `en` berarti Bahasa Inggris,\nsedangkan `en-US` berarti Bahasa Inggris (Amerika Serikat).\n\nUntuk informasi lebih lanjut mengenai _property_ ini bisa anda pelajari pada bagian [Internasionalisasi](tutorial-i18n.md).\n\n\n#### [[yii\\base\\Application::timeZone|timeZone]] <span id=\"timeZone\"></span>\n\n_Property_ ini disediakan sebagai cara alternatif untuk menentukan zona waktu default dari _PHP runtime_.\nDengan menentukan _property_ ini, pada dasarnya anda memanggil _function_ PHP\n[date_default_timezone_set()](https://www.php.net/manual/en/function.date-default-timezone-set.php). Sebagi contoh:\n\n```php\n[\n    'timeZone' => 'America/Los_Angeles',\n]\n```\n\n\n#### [[yii\\base\\Application::version|version]] <span id=\"version\"></span>\n\n_Property_ ini menentukan versi dari aplikasi anda. Secara default nilainya adalah `'1.0'`. Anda tidak harus menentukan\n_property_ ini jika tidak ada kode anda yang akan menggunakannya.\n\n\n### _Property_ yang Bermanfaat <span id=\"useful-properties\"></span>\n\n_Property_ yang dijelaskan pada sub-bagian ini tidak secara umum digunakan karena nilai default-nya\nsudah ditentukan berdasarkan kaidah-kaidah yang umum digunakan. Tetapi anda boleh menentukannya sendiri jikalau anda tidak ingin mengikuti kaidah-kaidah tersebut.\n\n\n#### [[yii\\base\\Application::charset|charset]] <span id=\"charset\"></span>\n\n_Property_ ini menentukan _charset_ yang digunakan oleh aplikasi. Nilai default-nya adalah `'UTF-8'`, dimana harus\ndigunakan sebisa mungkin pada kebanyakan aplikasi, kecuali anda sedang membangun sistem lama yang banyak menggunakan data yang tidak termasuk dalam _Unicode_.\n\n\n#### [[yii\\base\\Application::defaultRoute|defaultRoute]] <span id=\"defaultRoute\"></span>\n\n_Property_ ini menentukan [route](runtime-routing.md) yang harus aplikasi gunakan ketika sebuah _request_\ntidak memiliki _route_. _Route_ dapat terdiri dari ID _child module_, ID _controller_, dan/atau ID _action_.\nSebagai contoh, `help`, `post/create`, atau `admin/post/create`. Jika ID _action_ tidak diberikan, maka _property_ ini akan mengambil\nnilai default yang ditentukan di [[yii\\base\\Controller::defaultAction]].\n\nUntuk [[yii\\web\\Application|aplikasi Web]], nilai default dari _property_ ini adalah `'site'`, yang berarti\n_controller_ `SiteController` dan default _action_-nya yang akan digunakan. Hasilnya, jika anda mengakses\naplikasi tanpa menentukan _route_ yang spesifik, maka akan menampilkan output dari `app\\controllers\\SiteController::actionIndex()`.\n\nUntuk [[yii\\console\\Application|aplikasi konsol]], nilai default-nya adalah `'help'`, yang berarti akan menggunakan\n[[yii\\console\\controllers\\HelpController::actionIndex()]] sebagai perintah utamanya. Hasilnya, jika anda menjalankan perintah `yii`\ntanpa memasukkan argumen, maka akan menampilkan informasi bantuan penggunaan.\n\n\n#### [[yii\\base\\Application::extensions|extensions]] <span id=\"extensions\"></span>\n\n_Property_ ini menentukan daftar dari [extension](structure-extensions.md) yang terpasang dan digunakan oleh aplikasi.\nSecara default, akan mengambil _array_ yang dikembalikan oleh file `@vendor/yiisoft/extensions.php`. File `extensions.php`\ndibuat dan dikelola secara otomatis jika anda menggunakan [Composer](https://getcomposer.org) untuk memasang _extensions_.\nSecara umum, anda tidak perlu menentukan _property_ ini.\n\nDalam kasus khusus jika anda ingin mengelola _extension_ secara manual, anda boleh menentukan _property_ ini seperti kode dibawah ini:\n\n```php\n[\n    'extensions' => [\n        [\n            'name' => 'extension name',\n            'version' => 'version number',\n            'bootstrap' => 'BootstrapClassName',  // Tidak wajib, bisa juga berupa konfigurasi array\n            'alias' => [  // Tidak Wajib\n                '@alias1' => 'to/path1',\n                '@alias2' => 'to/path2',\n            ],\n        ],\n\n        // ... extension lain yang ditentukan seperti kode di atas ...\n\n    ],\n]\n```\n\nSeperti yang anda lihat, _property_ ini menerima spesifikasi _extension_ dalam bentuk _array_. Setiap _extension_ ditentukan dengan _array_\nyang terdiri dari elemen `name` dan `version`. Jika _extension_ harus dijalankan ketika proses [bootstrap](runtime-bootstrapping.md)\n, elemen `bootstrap` dapat dispesifikasikan dengan nama _class bootstrap_-nya atau [konfigurasi array](concept-configurations.md)\n. _Extension_ juga dapat menentukan beberapa [alias](concept-aliases.md).\n\n\n#### [[yii\\base\\Application::layout|layout]] <span id=\"layout\"></span>\n\n_Property_ ini menentukan nama dari default layout yang akan digunakan ketika me-render sebuah [view](structure-views.md).\nNilai default-nya adalah `'main'`, yang berarti akan menggunakan file layout `main.php` yang disimpan di [layout path](#layoutPath).\nJika kedua dari [layout path](#layoutPath) dan [view path](#viewPath) mengambil nilai default,\nmaka representasi file layoutnya adalah _path alias_ `@app/views/layouts/main.php`.\n\nAnda dapat menentukan nilai _property_ ini menjadi `false` jika anda ingin menonaktifkan layout secara default, tetapi anda seharusnya tidak memerlukannya.\n\n\n#### [[yii\\base\\Application::layoutPath|layoutPath]] <span id=\"layoutPath\"></span>\n\n_Property_ ini menentukan path dimana sistem akan mencari file layout. Nilai default-nya adalah\nsub-direktori `layouts` di dalam [view path](#viewPath). Jika [view path](#viewPath) mengambil\nnilai defaultnya, maka path layout defaultnya adalah path alias `@app/views/layouts`.\n\nAnda dapat menentukannya sebagai direktori atau path [alias](concept-aliases.md).\n\n\n#### [[yii\\base\\Application::runtimePath|runtimePath]] <span id=\"runtimePath\"></span>\n\n_Property_ ini menentukan dimana path file yang bersifat sementara, seperti file _log_ dan _cache_.\nNilai default-nya adalah direktori yang direpresentasikan oleh alias `@app/runtime`.\n\nAnda dapat menentukan nilainya dengan direktori atau path [alias](concept-aliases.md). Sebagai catatan, _path runtime_ wajib\nmemiliki akses tulis _(writeable)_ oleh _web server_ yang menjalankan aplikasi. Dan path tersebut sebaiknya diproteksi aksesnya dari\npengguna, karena file yang bersifat sementara di dalamnya mungkin mengandung informasi sensitif.\n\nUntuk menyederhanakan akses ke path ini, Yii sudah menentukan path alias dengan nama `@runtime`.\n\n\n#### [[yii\\base\\Application::viewPath|viewPath]] <span id=\"viewPath\"></span>\n\n_Property_ ini menentukan direktori _root_ dimana file-file _view_ akan disimpan. Nilai default-nya adalah direktori\nyang di representasikan oleh alias `@app/views`. Anda dapat menentukan nilainya dengan direktori atau path [alias](concept-aliases.md).\n\n\n#### [[yii\\base\\Application::vendorPath|vendorPath]] <span id=\"vendorPath\"></span>\n\n_Property_ ini menentukan direktori _vendor_ yang di kelola oleh [Composer](https://getcomposer.org). Direktori ini akan\nmenyimpan semua _library_ pihak ketiga yang digunakan oleh aplikasi anda, termasuk Yii _framework_. Nilai default-nya adalah\ndirektori yang di representasikan oleh alias `@app/vendor`.\n\nAnda dapat menentukan nilai _property_ ini dengan direktori atau path [alias](concept-aliases.md). Jika anda mengganti\nnilai _property_ ini, pastikan anda juga menyesuaikan konfigurasi Composer.\n\nUntuk memudahkan akses ke path ini, Yii sudah menentukan path alias dengan nama `@vendor`.\n\n\n#### [[yii\\console\\Application::enableCoreCommands|enableCoreCommands]] <span id=\"enableCoreCommands\"></span>\n\n_Property_ ini hanya dikenali oleh [[yii\\console\\Application|console applications]]. _Property_ ini menentukan\napakah perintah inti yang dibawa oleh rilisan Yii harus diaktifkan. Nilai default-nya adalah `true`.\n\n\n## _Event_ Aplikasi <span id=\"application-events\"></span>\n\nSebuah objek _aplikasi_ menjalankan beberapa _event_ sepanjang siklus penanganan _request_. Anda dapat menempelkan penanganan _event_\nuntuk _event-event_ ini di dalam konfigurasi aplikasi seperti di bawah ini:\n\n```php\n[\n    'on beforeRequest' => function ($event) {\n        // ...\n    },\n]\n```\n\nPenggunaan dari sintaks `on eventName` akan dijelaskan pada bagian\n[Konfigurasi](concept-configurations.md#configuration-format).\n\nSebagai alternatif, anda dapat menempelkan penanganan _event_ ke dalam [proses bootstrap](runtime-bootstrapping.md)\nsetelah objek aplikasi telah dibuat. Sebagai contoh:\n\n```php\n\\Yii::$app->on(\\yii\\base\\Application::EVENT_BEFORE_REQUEST, function ($event) {\n    // ...\n});\n```\n\n### [[yii\\base\\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_REQUEST]] <span id=\"beforeRequest\"></span>\n\n_Event_ ini dijalankan *sebelum* objek aplikasi menangani sebuah _request_. Nama _event_-nya adalah `beforeRequest`.\n\nKetika _event_ ini dijalankan, objek aplikasi sudah dibuat dan di inisialisasi. Jadi waktu ini merupakan waktu yang tepat\nuntuk memasukkan kode anda melalui mekanisme _event_ untuk mengintervensi penanganan _request_. Sebagai contoh,\ndi penanganan _event_, anda dapat menentukan _property_ [[yii\\base\\Application::language]] secara dinamis berdasarkan parameter tertentu.\n\n\n### [[yii\\base\\Application::EVENT_AFTER_REQUEST|EVENT_AFTER_REQUEST]] <span id=\"afterRequest\"></span>\n\n_Event_ ini dijalankan *setelah* objek aplikasi menyelesaikan penanganan sebuah _request_ tetapi *sebelum* mengirimkan _response_.\n\nKetika _event_ ini dijalankan, proses penanganan _request_ sudah selesai dan anda dapat menggunakan kesempatan untuk melakukan beberapa tugas\nuntuk memodifikasi _request_ atau _response_.\n\nSebagai catatan, _component_ [[yii\\web\\Response|response]] juga menjalankan beberapa _event_ pada saat mengirim\nisi _response_ ke pengguna. _Event_ tersebut akan dijalankan *setelah* _event_ ini.\n\n\n### [[yii\\base\\Application::EVENT_BEFORE_ACTION|EVENT_BEFORE_ACTION]] <span id=\"beforeAction\"></span>\n\n_Event_ ini dijalankan *sebelum* semua [action dari controller](structure-controllers.md) diproses.\nNama _event_-nya adalah `beforeAction`.\n\nParameter _event_ merupakan objek dari [[yii\\base\\ActionEvent]]. Sebuah penanganan _event_ boleh menentukan\n_property_ [[yii\\base\\ActionEvent::isValid]] menjadi `false` untuk memberhentikan proses jalannya _action_.\nSebagai contoh:\n\n```php\n[\n    'on beforeAction' => function ($event) {\n        if (kondisi tertentu) {\n            $event->isValid = false;\n        } else {\n        }\n    },\n]\n```\n\nSebagai catatan, _event_ `beforeAction` yang sama juga dijalankan oleh [module](structure-modules.md)\ndan [controller](structure-controllers.md). _Event_ pada objek aplikasi yang menjalankan _event_ ini\nuntuk pertama kali, dilanjutkan oleh _module_ (jika ada), dan terakhir oleh _controller_. Jika sebuah penanganan _event_\nmenentukan _property_ [[yii\\base\\ActionEvent::isValid]] menjadi `false`, semua _event_ selanjutnya TIDAK akan dijalankan.\n\n\n### [[yii\\base\\Application::EVENT_AFTER_ACTION|EVENT_AFTER_ACTION]] <span id=\"afterAction\"></span>\n\n_Event_ ini dijalankan *setelah* menjalankan seluruh [action dari controller](structure-controllers.md).\nNama _event_-nya adalah `afterAction`.\n\nParameter _event_ adalah objek dari [[yii\\base\\ActionEvent]]. Menggunakan\n_property_ [[yii\\base\\ActionEvent::result]], _method_ penanganan _event_ dapat mengakses atau merubah hasil dari _action_.\nSebagai contoh:\n\n```php\n[\n    'on afterAction' => function ($event) {\n        if (kondisi tertentu) {\n            // rubah nilai dari $event->result\n        } else {\n        }\n    },\n]\n```\n\nSebagai catatan, _event_ `afterAction` yang sama juga dijalankan oleh [module](structure-modules.md)\ndan [controllers](structure-controllers.md). Objek-objek ini menjalankan _event_ ini sama seperti `beforeAction`,\nhanya saja urutannya merupakan kebalikan dari urutan di _event_ `beforeAction`. _Controller_ adalah objek pertama yang menjalankan _event_ ini,\nsetelah itu _module_ (jika ada), dan terakhir di level aplikasi.\n\n\n## Application Lifecycle <span id=\"application-lifecycle\"></span>\n\n![Siklus Aplikasi](images/application-lifecycle.png)\n\nKetika [skrip masuk](structure-entry-scripts.md) sedang dijalankan untuk menangani sebuah _request_,\naplikasi akan melewati proses siklus dibawah ini:\n\n1. Skrip masuk mengambil konfigurasi aplikasi dalam bentuk array.\n2. Skrip masuk membuat objek aplikasi:\n   * [[yii\\base\\Application::preInit()|preInit()]] dipanggil, dimana akan mengatur beberapa _property_ aplikasi\n     yang sangat penting seperti [[yii\\base\\Application::basePath|basePath]].\n   * Mendaftarkan [[yii\\base\\Application::errorHandler|penanganan error]].\n   * Mengatur _property_ aplikasi.\n   * [[yii\\base\\Application::init()|init()]] dipanggil, yang selanjutnya memanggil\n     [[yii\\base\\Application::bootstrap()|bootstrap()]] untuk menjalankan proses _bootstrap component_.\n3. Skrip masuk memanggil [[yii\\base\\Application::run()]] untuk menjalankan aplikasi:\n   * Menjalankan _event_ [[yii\\base\\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_REQUEST]].\n   * Menangani _request_: memproses _request_ menjadi [route](runtime-routing.md) dan parameter-parameternya;\n     membuat objek _module_, _controller_, dan _action_ yang dispesifikasikan oleh _route_; dan menjalankan _action_.\n   * Menjalankan _event_ [[yii\\base\\Application::EVENT_AFTER_REQUEST|EVENT_AFTER_REQUEST]].\n   * Mengirim _response_ ke pengguna.\n4. Skrip masuk mendapatkan _status exit_ dari aplikasi dan menyelesaikan proses penanganan _request_.\n"
  },
  {
    "path": "docs/guide-id/structure-entry-scripts.md",
    "content": "Skrip Masuk\n===========\n\nSkrip masuk adalah langkah pertama pada proses _bootstrap_ aplikasi. Dalam sebuah aplikasi (apakah\nitu aplikasi web atau aplikasi konsol) memiliki satu skrip masuk. Pengguna mengirim _request_ ke\nskrip masuk dimana skrip tersebut membangun objek aplikasi dan meneruskan _request_ ke objek tersebut.\n\nSkrip masuk untuk aplikasi web harus disimpan pada direktori yang dapat diakses dari web sehingga\ndapat di akses oleh pengguna. Secara umum, skrip tersebut diberi nama `index.php`, tetapi boleh menggunakan nama lain,\nselama _web server_ bisa mengakses skrip tersebut.\n\nSkrip masuk untuk aplikasi konsol pada umumnya disimpan di dalam [base path](structure-applications.md)\ndari objek aplikasi dan diberi nama `yii` (dengan suffix `.php`). Skrip tersebut harus memiliki akses _execute_\nsehingga pengguna dapat menjalan aplikasi konsol menggunakan perintah `./yii <route> [argument] [option]`.\n\nSkrip masuk umumnya mengerjakan tugas berikut ini:\n\n* Menentukan _global constant_;\n* Mendaftarkan [autoloader Composer](https://getcomposer.org/doc/01-basic-usage.md#autoloading);\n* Memasukkan file _class_ [[Yii]];\n* Mengambil konfigurasi aplikasi, dan memuatnya;\n* Membuat dan mengatur objek [application](structure-applications.md);\n* Memanggil [[yii\\base\\Application::run()]] untuk memproses _request_ yang diterima;\n\n\n## Aplikasi Web<span id=\"web-applications\"></span>\n\nKode berikut ini adalah kode yang terdapat pada skrip masuk [Template Proyek Dasar](start-installation.md).\n\n```php\n<?php\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n\n// mendaftarkan autoloader Composer\nrequire __DIR__ . '/../vendor/autoload.php';\n\n// memasukkan file class Yii\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n\n// Mengambil konfigurasi aplikasi\n$config = require __DIR__ . '/../config/web.php';\n\n// Membuat, mengkonfigurasi, dan menjalankan aplikasi\n(new yii\\web\\Application($config))->run();\n```\n\n\n## Aplikasi Konsol <span id=\"console-applications\"></span>\n\nDemikian juga dengan aplikasi konsol, kode berikut ini adalah kode yang terdapat pada skrip masuk aplikasi konsol :\n\n```php\n#!/usr/bin/env php\n<?php\n/**\n * Yii console bootstrap file.\n *\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n\n// mendaftarkan autoloader composer\nrequire __DIR__ . '/vendor/autoload.php';\n\n// memasukkan file class Yii\nrequire __DIR__ . '/vendor/yiisoft/yii2/Yii.php';\n\n// Mengambil konfigurasi aplikasi\n$config = require __DIR__ . '/config/console.php';\n\n$application = new yii\\console\\Application($config);\n$exitCode = $application->run();\nexit($exitCode);\n```\n\n\n## Menentukan _Constant_ <span id=\"defining-constants\"></span>\n\nSkrip masuk adalah file yang tepat untuk menentukan _global constant_. Yii mengenali tiga _constant_ berikut ini:\n\n* `YII_DEBUG`: untuk menentukan apakah aplikasi sedang dalam mode _debug_. Pada saat mode _debug_, aplikasi\n  akan menyimpan informasi log lebih banyak, dan akan menampilkan detail error urutan pemanggilan _(error call stack)_ jika ada _exception_ yang di-_throw_. Alasan inilah,\n  kenapa mode _debug_ sebaiknya digunakan pada tahap pengembangan. Nilai _default_ dari `YII_DEBUG` adalah `false`.\n* `YII_ENV`: untuk menentukan pada mode _environment_ manakah aplikasi ini dijalankan. _Constant_ ini akan dijelaskan lebih lanjut di\n  bagian [Konfigurasi](concept-configurations.md#environment-constants).\n  Nilai _default_ dari `YII_ENV` adalah `prod`, yang berarti aplikasi sedang dijalankan pada _production environment_.\n* `YII_ENABLE_ERROR_HANDLER`: untuk menentukan apakah akan mengaktifkan penanganan eror yang disediakan oleh Yii. Nilai _default_\n  dari _constant_ ini adalah `true`.\n\nUntuk menentukan _constant_, kita biasanya menggunakan kode berikut ini:\n\n```php\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\n```\n\nkode di atas memiliki tujuan yang sama dengan kode berikut ini:\n\n```php\nif (!defined('YII_DEBUG')) {\n    define('YII_DEBUG', true);\n}\n```\n\nJelas, kode yang pertama lah yang lebih ringkas dan lebih mudah untuk dimengerti.\n\nPenentuan _constant_ sebaiknya ditulis di baris-baris awal pada skrip masuk sehingga akan berfungsi\nketika file PHP lain akan dimasukkan _(include)_.\n"
  },
  {
    "path": "docs/guide-id/structure-overview.md",
    "content": "Tinjauan\n========\n\nAplikasi Yii diorganisir berdasarkan pola arsitektur [model-view-controller (MVC)](https://id.wikipedia.org/wiki/MVC).\n[Model](structure-models.md) merepresentasikan data, pengaturan dan proses bisnis; [view](structure-views.md)\nadalah output yang merepresentasikan model; dan [controller](structure-controllers.md) mengelola input dan merubahnya\nmenjadi perintah-perintah untuk [model](structure-models.md) dan [view](structure-views.md).\n\nSelain MVC, aplikasi Yii juga memiliki entitas berikut:\n\n* [entry scripts](structure-entry-scripts.md): Ini adalah skrip PHP yang diakses secara langsung oleh pengguna.\n  Ini bertugas untuk memulai siklus penanganan _request_.\n* [applications](structure-applications.md): Ini adalah objek yang dapat diakses secara global, yang mengelola _component_ aplikasi\n  dan mengaturnya untuk memenuhi sebuah _request_.\n* [application components](structure-application-components.md): Ini adalah objek-objek yang didaftarkan pada objek _application_ dan\n  menyediakan beragam layanan untuk memenuhi _request_.\n* [modules](structure-modules.md): Ini adalah paket _(package)_ mandiri yang berisikan MVC lengkap.\n  Sebuah aplikasi boleh diistilahkan sebagai module-module yang telah diorganisir.\n* [filters](structure-filters.md): Ini merepresentaikan kode yang mutlak untuk dijalakan sebelum dan sesudah\n  penanganan dari tiap-tiap _request_ yang dikelola oleh _controller_.\n* [widgets](structure-widgets.md): Ini adalah objek-objek yang dapat ditanam kedalam [views](structure-views.md). Ini\n  dapat mengandung logika _controller_ dan dapat digunakan berulang-ulang pada _view_ yang berbeda.\n\nDiagram dibawah ini menunjukkan struktur statis dari sebuah aplikasi:\n\n![Struktur Statis Aplikasi](images/application-structure.png)\n"
  },
  {
    "path": "docs/guide-id/translators.json",
    "content": "[\n    \"Misbahul D. Munir\",\n    \"Muhammad Cahya\",\n    \"Seto Kuslaksono\",\n    \"Novrian Y.F.\"\n]\n"
  },
  {
    "path": "docs/guide-it/README.md",
    "content": "La guida definitiva a Yii 2.0\n=============================\n\nQuesta guida è rilasciata nei [termini della documentazione di Yii](https://www.yiiframework.com/doc/terms/).\n\nTutti i diritti riservati.\n\n2014 (c) Yii Software LLC.\n\nTraduzione italiana a cura di Lorenzo Milesi ([yetopen.it](https://www.yetopen.it)).\n\n\nIntroduzione\n------------\n\n* [Informazioni su Yii](intro-yii.md)\n* [Aggiornare dalla versione 1.1](intro-upgrade-from-v1.md)\n\n\nPrimi passi\n-----------\n\n* [Installare Yii](start-installation.md)\n* [Esecuzione applicazioni](start-workflow.md)\n* [Dire Ciao](start-hello.md)\n* [Utilizzo dei form](start-forms.md)\n* [Utilizzo dei database](start-databases.md)\n* [Generare codice con Gii](start-gii.md)\n* [Passi successivi](start-looking-ahead.md)\n\n\nStruttura dell'applicazione\n---------------------------\n\n* [Panoramica](structure-overview.md)\n* [Entry Scripts](structure-entry-scripts.md)\n* [Applicazioni](structure-applications.md)\n* [Componenti applicazioni](structure-application-components.md)\n* [Controller](structure-controllers.md)\n* [Modelli](structure-models.md)\n* [Viste](structure-views.md)\n* [Moduli](structure-modules.md)\n* [Filtri](structure-filters.md)\n* [Widget](structure-widgets.md)\n* [Asset](structure-assets.md)\n* [Estensioni](structure-extensions.md)\n\n\nGestione delle richieste\n------------------------\n\n* [Panoramica](runtime-overview.md)\n* [Bootstrapping](runtime-bootstrapping.md)\n* [Instradamenti (routing)](runtime-routing.md)\n* [Richieste](runtime-requests.md)\n* [Risposte](runtime-responses.md)\n* **TBD** [Sessioni e cookie](runtime-sessions-cookies.md)\n* [Analisi e generazione URL](runtime-url-handling.md)\n* [Gestione errori](runtime-handling-errors.md)\n* [Log](runtime-logging.md)\n\n\nConcetti chiave\n---------------\n\n* [Componenti](concept-components.md)\n* [Proprietà](concept-properties.md)\n* [Eventi](concept-events.md)\n* [Behavior](concept-behaviors.md)\n* [Configurazioni](concept-configurations.md)\n* [Alias](concept-aliases.md)\n* [Caricamento automatico delle classi (autoload)](concept-autoloading.md)\n* [Service Locator](concept-service-locator.md)\n* [Container per Dependency Injection](concept-di-container.md)\n\n\nUtilizzo del database\n------------------------\n\n* [Data Access Objects](db-dao.md): Connessione ad un database, query semplici, transazioni e modifiche allo schema\n* [Query Builder](db-query-builder.md): Esecuzione di query al database usando un semplice livello di astrazione\n* [Active Record](db-active-record.md): The Active Record ORM, retrieving and manipulating records, and defining relations\n* [Migrazoni](db-migrations.md): Applicare il controllo di versione al database in un ambiente di sviluppo di gruppo\n* **TBD** [Sphinx](db-sphinx.md)\n* **TBD** [Redis](db-redis.md)\n* **TBD** [MongoDB](db-mongodb.md)\n* **TBD** [ElasticSearch](db-elasticsearch.md)\n\n\nRicezione dati dagli utenti\n---------------------------\n\n* [Creare form](input-forms.md)\n* [Validazione informazioni](input-validation.md)\n* **TBD** [Caricamento file](input-file-upload.md)\n* **TBD** [Raccogliere dati per più modelli](input-multiple-models.md)\n\n\nVisualizzazione dei dati\n------------------------\n\n* **TBD** [Formattazione](output-formatting.md)\n* **TBD** [Paginazione](output-pagination.md)\n* **TBD** [Ordinamento](output-sorting.md)\n* [Data Provider](output-data-providers.md)\n* [Data Widget](output-data-widgets.md)\n* [Utilizzo del Client Scripts](output-client-scripts.md)\n* [Temi](output-theming.md)\n\n\nSicurezza\n---------\n\n* [Autenticazione](security-authentication.md)\n* [Autorizzazione](security-authorization.md)\n* [Utilizzo delle password](security-passwords.md)\n* **TBD** [Auth Clients](security-auth-clients.md)\n* **TBD** [Buona prassi](security-best-practices.md)\n\n\nCache\n-----\n\n* [Panoramica](caching-overview.md)\n* [Cache dati](caching-data.md)\n* [Fragment Caching](caching-fragment.md)\n* [Cache pagina](caching-page.md)\n* [Cache HTTP](caching-http.md)\n\n\nServizi web RESTful\n-------------------\n\n* [Avvio veloce](rest-quick-start.md)\n* [Risorse](rest-resources.md)\n* [Controller](rest-controllers.md)\n* [Instradamenti](rest-routing.md)\n* [Formattazione risposte](rest-response-formatting.md)\n* [Autenticazione](rest-authentication.md)\n* [Limitazione di utilizzo](rest-rate-limiting.md)\n* [Versioning](rest-versioning.md)\n* [Gestione degli errori](rest-error-handling.md)\n\n\nStrumenti di sviluppo\n---------------------\n\n* [Barra di debug e debugger](tool-debugger.md)\n* [Generazione codice con Gii](tool-gii.md)\n* **TBD** [Generazione documentazione API](tool-api-doc.md)\n\n\nTest\n----\n\n* [Panoramica](test-overview.md)\n* [Inizializzazione ambiente di test](test-environment-setup.md)\n* [Unit Test](test-unit.md)\n* [Functional Test](test-functional.md)\n* [Acceptance Test](test-acceptance.md)\n* [Fixture](test-fixtures.md)\n\n\nArgomenti speciali\n------------------\n\n* [Modello di applicazione avanzata](tutorial-advanced-app.md)\n* [Creazione di una applicazione da zero](tutorial-start-from-scratch.md)\n* [Comandi da console](tutorial-console.md)\n* [Validazioni predefinite](tutorial-core-validators.md)\n* [Internazionalizzazione](tutorial-i18n.md)\n* [Invio email](tutorial-mailing.md)\n* [Ottimizzazione delle prestazioni](tutorial-performance-tuning.md)\n* **TBD** [Ambienti di hosting condiviso](tutorial-shared-hosting.md)\n* [Template Engine](tutorial-template-engines.md)\n* [Utilizzo di codice di terze parti](tutorial-yii-integration.md)\n\n\nWidget\n------\n\n* GridView: link to demo page\n* ListView: link to demo page\n* DetailView: link to demo page\n* ActiveForm: link to demo page\n* Pjax: link to demo page\n* Menu: link to demo page\n* LinkPager: link to demo page\n* LinkSorter: link to demo page\n* [Widget Bootstrap](widget-bootstrap.md)\n* [Widget Jquery UI](widget-jui.md)\n\n\nHelper\n------\n\n* [Panoramica](helper-overview.md)\n* **TBD** [ArrayHelper](helper-array.md)\n* **TBD** [Html](helper-html.md)\n* **TBD** [Url](helper-url.md)\n* **TBD** [Security](helper-security.md)\n\n"
  },
  {
    "path": "docs/guide-it/intro-upgrade-from-v1.md",
    "content": "Aggiornare dalla versione 1.1\n=============================\n\nCi sono molte differenze tra la versione 1.1 e la 2.0 di Yii, dato che il framework è stato completamente riscritto.\nDi conseguenza l'aggiornamento dalla versione 1.1 non è così semplice come passare da una versione minore all'altra. In questa guida\ntroverai le differenze principali tra le due versioni.\n\nSe non hai mai usato Yii 1.1 puoi ignorare questa sezione e passare direttamente a \"[Come inizare](start-installation.md)\".\n\nConsidera che Yii 2.0 introduce più funzionalità di quelle descritte in questo riepilogo. Ti consigliamo di leggere tutta la \nguida definitiva per apprenderle tutte. C'è la possibilità che alcune funzionalità che prima dovevi sviluppare da solo sono state\nimplementate nel codice principale.\n\n\nInstallazione\n-------------\n\nYii 2.0 usa [Composer](https://getcomposer.org/), lo standard di fatto per la gestione dei pacchetti PHP. L'installazione del\nframework di base, così come delle estensioni, sono gestite da Composer. Per favore leggi la guida [Installare Yii](start-installation.md)\nper comprendere come installare Yii 2.0. Se vuoi creare una nuova estensione, o trasformarne una sviluppata per 1.1 a 2.0, fai riferimento\nalla sezione [Creazione estensioni](structure-extensions.md#creating-extensions).\n\n\nRichieste PHP\n-------------\n\nYii 2.0 richiede PHP 5.4 o superiore, il che è un passaggio notevole rispetto alla richiesta di PHP 5.2 di Yii 1.1.\nDi conseguenza ci sono diverse differenze a livello di linguaggio a cui devi fare attenzione.\nDi seguito un riepilogo delle principali differenze relative a PHP:\n\n- [Namespace](https://www.php.net/manual/en/language.namespaces.php).\n- [Funzioni anonime](https://www.php.net/manual/en/functions.anonymous.php).\n- La sintassi breve per gli array `[...elementi...]` è utilizzabile invece di `array(...elementi...)`.\n- Le tag brevi per le echo `<?=` sono utilizzabili nei file delle viste. Il loro utilizzo è sicuro da PHP 5.4.\n- [Interfacce e classi SPL](https://www.php.net/manual/en/book.spl.php).\n- [Late Static Bindings](https://www.php.net/manual/en/language.oop5.late-static-bindings.php).\n- [Data e ora](https://www.php.net/manual/en/book.datetime.php).\n- [Trait](https://www.php.net/manual/en/language.oop5.traits.php).\n- [intl](https://www.php.net/manual/en/book.intl.php). Yii 2.0 utilizza l'estensione PHP `intl` per le funzionalità di \n  internazionalizzazione.\n\n\nNamespace\n---------\n\nIl cambiamento più evidente in Yii 2.0 è l'uso dei namespace. Praticamente tutte le classi del codice \nprincipale sono sotto namespace, ad esempio `yii\\web\\Request`. Il prefisso \"C\" non è più utilizzato nei nomi delle classi.\nLo schema dei nomi segue la struttura delle directory. Per esempio `yii\\web\\Request` indica che il file corrispondente per quella\nclasse si trova in `web/Request.php` nella directory principale del framework Yii.\n\n(Puoi utilizzare qualunque classe del core di Yii senza dover includere il file relativo, grazie al loader delle classi di Yii.)\n\n\nComponenti ed oggetti\n---------------------\n\nYii 2 divide la classe `CComponent` della versione 1.1 in due classi: [[yii\\base\\BaseObject]] and [[yii\\base\\Component]].\nLa classe [[yii\\base\\BaseObject|BaseObject]] è una classe leggera da usare come base, che consente la definizione di \n[proprietà dell'oggetto](concept-properties.md) tramite *geters* e *setters*. La classe [[yii\\base\\Component|Component]] estende\n[[yii\\base\\BaseObject|BaseObject]] e supporta [eventi](concept-events.md) e [behavior](concept-behaviors.md).\n\nSe la tua classe non ha necessità di usare eventi o behavior conviene usare [[yii\\base\\BaseObject|BaseObject]] come classe base.\nDi solito viene impiegata per classi che rappresentano strutture di dati semplici.\n\n\nConfigurazione oggetti\n----------------------\n\nLa classe [[yii\\base\\BaseObject|BaseObject]] introduce un metodo uniforme per la configurazione degli oggetti. \nOgni classe figlia di [[yii\\base\\BaseObject|BaseObject]] dovrebbe dichiarare il suo costruttore (se necessario) in questo modo, così da essere \nconfigurato correttamente:\n\n```php\nclass MyClass extends \\yii\\base\\BaseObject\n{\n    public function __construct($param1, $param2, $config = [])\n    {\n        // ... inizializzazione prima della configurazione\n\n        parent::__construct($config);\n    }\n\n    public function init()\n    {\n        parent::init();\n\n        // ... inizializzazione dopo la configurazione\n    }\n}\n```\n\nNell'esempio sopra, l'ultimo parametro del costruttore riceve l'array di configurazione che contiene coppie di nome-valore per\ninizializzare le proprietà alla fine del costruttore.\nPuoi sovrascrivere il metodo [[yii\\base\\BaseObject::init()|init()]] per eseguire operazioni dopo che la configurazione è stata applicata.\n\nSeguendo questa convenzione potrai creare e configurare nuovi oggetti usando un array di configurazione:\n\n```php\n$object = Yii::createObject([\n    'class' => 'MyClass',\n    'proprieta1' => 'abc',\n    'proprieta2' => 'cde',\n], [$param1, $param2]);\n```\n\nMaggiori dettagli sulla configurazione si trovano nella sezione [Configurazione oggetti](concept-configurations.md).\n\n\nEventi\n------\n\nIn Yii 1 gli eventi venivano creati definendo un metodo `on`-qualcosa (ad es. `onBeforeSave`). In Yii 2 ora puoi usare un qualunque\nnome per l'evento. Puoi scatenare un evento chiamando il metodo [[yii\\base\\Component::trigger()|trigger()]]:\n\n```php\n$event = new \\yii\\base\\Event;\n$component->trigger($eventName, $event);\n```\n\nPer collegare un metodo ad un evento usa il metodo [[yii\\base\\Component::on()|on()]]:\n\n```php\n$component->on($eventName, $handler);\n// Per scollegare il metodo dall'evento, usa:\n// $component->off($eventName, $handler);\n```\n\nCi sono molti miglioramenti sulle funzionalità degli eventi. Per maggiori dettagli fai riferimento alla sezione\n[Eventi](concept-events.md).\n\n\nAlias percorsi\n--------------\n\nYii 2.0 espande l'utilizzo degli alias di percorso (Path alias, in inglese) a file e directory sia locali che remoti (URL). Yii 2.0\nrichiede ora che un percorso alias inizi con il carattere `@`, per differenziarli da normali percorsi o URL.\nPer esempio, l'alias `@yii` si riferisce alla directory di installazione di Yii. Gli alias di percorso sono supportati nella maggior\nparte del codice base di Yii. Per esempio, [[yii\\caching\\FileCache::cachePath]] può ricevere sia un alias che un percorso normale ad \nuna directory.\n\nUn alias di percorso è strettamente legato al namespace della classe. Si saccomanda di definire un alias per ogni namespace root, \nconsentendo così di usare le funzioni di autoload di Yii senza configurazioni aggiuntive. Per esempio, visto che `@yii` si riferisce\nalla directory di instllazione di Yii, una classe come `yii\\web\\Request` può essere caricata automaticamente. Se usi una libreria di terze\nparti, come ad esempio il framework Zend, puoi definire un alias `@Zend` che si riferisce alla sua directory di installazione. Fatto\nquesto, Yii sarà in grado di caricare automaticamente qualunque classe della libreria Zend.\n\nMaggiori informazioni sugli alias di percorso nella sezione [Aliase](concept-aliases.md).\n\n\nViste\n-----\n\nIl cambiamento più evidente riguardante le viste è che in Yii 2 la variabile speciale `$this` in una vista non si riferisce più\nal controller o al widget corrente. Invece `$this` si riferisce ora all'oggetto *view*, un nuovo concetto introdotto nella versione 2.0.\nL'oggetto *view* è di tipo [[yii\\web\\View]], che rappresenta la parte della vista nel modello MVC. Per accedere al controller o al \nwidget dalla vista, puoi usare `$this->context`.\n\nPer effettuare il render di una vista parzioale all'interno di un'altra vista devi usare `$this->render()`, non `$this->renderPartial()`. \nLa chiamata a `render` deve essere ora esplicitamente mandata in output (tramite `echo`), dato che ora il metodo `render()` restituisce\nil risultato dell'elaborazione della vista piuttosto che visualizzarlo. Per esempio:\n\n```php\necho $this->render('_item', ['item' => $item]);\n```\n\nOltre ad usare PHP come linguaggio principale di template, Yii 2.0 supporta ufficialmente anche altri due motori di template:\nSmarty e Twig. Il motore Prado non è più supportato.\nPer usare questi engine devi configurare il componente `view` impostando la proprietà [[yii\\base\\View::$renderers|View::$renderers]]. \nFai riferimento alla sezione [Template Engine](tutorial-template-engines.md) per maggiori dettagli.\n\n\nModelli\n-------\n\nYii 2.0 usa [[yii\\base\\Model]] come modello base, simile a `CModel` di 1.1.\nLa classe `CFormModel` è stata rimossa. In Yii 2 invece devi estendere [[yii\\base\\Model]] per creare un modello da impiegare in un form.\n\nYii 2.0 introduce il nuovo metodo [[yii\\base\\Model::scenarios()|scenarios()]] per dichiarare gli scenari supportati, e per indicare\nin quale scenario devono essere validati gli attributi, se devono essere considerati *safe* o no, e così via. PEr esempio:\n\n```php\npublic function scenarios()\n{\n    return [\n        'backend' => ['email', 'role'],\n        'frontend' => ['email', '!role'],\n    ];\n}\n```\n\nNell'esempio sopra sono stati definiti due scenari: `backend` e `frontend`. Per lo scenario `backend` sono considerati sicuri (`safe`)\nentrambi gli attributi `email` e `role`, e possono essere assegnati massivamente. Per lo scenario `frontend` l'`email` può essere\nassegnata in sicurezza mentre il `role` no. Entrambi i campi dovrebbero essere validati usando regole opportune.\n\nViene ancora usato il metodo [[yii\\base\\Model::rules()|rules()]] per definire le regole di validazione. Nota che in conseguenza \ndell'introduzione del metodo [[yii\\base\\Model::scenarios()|scenarios()]] non esiste più la validazione `unsafe`.\n\nNella maggior parte dei casi non avrai la necessità di sovrascrivere [[yii\\base\\Model::scenarios()|scenarios()]] se il metodo\n[[yii\\base\\Model::rules()|rules()]] specifica già tutti gli scenari esistenti, e se non hai necessità di dichiarare attributi `unsafe`.\n\nPer apprendere più dettagli in merito ai modelli, fare riferimento alla sezione [Modelli](structure-models.md).\n\n\nController\n----------\n\nYii 2.0 use [[yii\\web\\Controller]] come classe base per i controller, che è simile a `CController` di Yii 1.1.\n[[yii\\base\\Action]] è la classe base per le classi di azioni.\n\nL'impatto più ovvio di questi cambiamenti nel tuo codice è che l'azione di un controller deve tornare il contenuto da visualizzare, invece\ndi emetterlo direttamente:\n\n```php\npublic function actionView($id)\n{\n    $model = \\app\\models\\Post::findOne($id);\n    if ($model) {\n        return $this->render('view', ['model' => $model]);\n    } else {\n        throw new \\yii\\web\\NotFoundHttpException;\n    }\n}\n```\n\nFai riferimento alla sezione [Controller](structure-controllers.md) per maggiori dettagli in merito.\n\n\nWidget\n------\n\nYii 2.0 use [[yii\\base\\Widget]] come classe base per i widget, simile a `CWidget` di Yii 1.1.\n\nPer ottenere un supporto migliore al framework usando le IDE, Yii 2.0 introduce una nuova sintassi per l'utilizzo dei widget. Sono stati\nintrodotti i metodi statici [[yii\\base\\Widget::begin()|begin()]], [[yii\\base\\Widget::end()|end()]], e [[yii\\base\\Widget::widget()|widget()]]\nda usare così:\n\n```php\nuse yii\\widgets\\Menu;\nuse yii\\widgets\\ActiveForm;\n\n// Nota che devi emettere a video (\"echo\") il risultato per visualizzarlo\necho Menu::widget(['items' => $items]);\n\n// Passaggio di un array per inizializzare le proprietà dell'oggetto\n$form = ActiveForm::begin([\n    'options' => ['class' => 'form-horizontal'],\n    'fieldConfig' => ['inputOptions' => ['class' => 'input-xlarge']],\n]);\n... campi di input del form ...\nActiveForm::end();\n```\n\nFai riferimento alla sezione [Widget](structure-widgets.md) per maggiori dettagli.\n\n\nTemi\n----\n\nI temi sono completamente diversi nella versione 2.0. Ora sono basati su un meccanismo di mappatura dei percorsi, in modo da \ncreare una corrispondenza tra il percorso di un file vista sorgente e il percorso di un file di vista del tema. Per esempio se la mappa\nè `['/web/views' => '/web/themes/basic']`, la versione personalizzata del tema del file \n`/web/views/site/index.php` sarà `/web/themes/basic/site/index.php`. Per questo motivo ora i temi possono essere applicati a qualunque\nfile di vista, anche per una vista elaborata al di fuori del contesto di un controller o di un widget.\n\nInoltre non c'è più il componente`CThemeManager`. Esiste invece una proprietà configurabile `theme` del componente `view`.\n\nFai rfierimento alla sezione [Temi](output-theming.md) per maggiori dettagli.\n\n\nApplicazioni da console\n-----------------------\n\nLe applicazioni da console (linea di comando) sono ora organizzate come controller, come le applicazioni web. I controller devono quindi\nestendere [[yii\\console\\Controller]], simile alla classe `CConsoleCommand` della versione 1.1.\n\nPer eseguire un comando da terminale usare `yii <route>`, dove `<route>` rappresenta la rotta di un controller\n(es. `sitemap/index`). I parametri anonimi aggiuntivi vengono passati come parametri al relativo metodo dell'azione nel controller, mentre\ni parametri specifici (con nome) vengono processati secondo le specifiche di [[yii\\console\\Controller::options()]].\n\nYii 2.0 supporta la generazione automatica dell'help dei comandi prelevando le informazioni dai blocchi di commento.\n\nFai riferimento alla sezione [Console Commands](tutorial-console.md) per ulteriori dettagli.\n\n\nI18N\n----\n\nYii 2.0 ha rimosso la formattazione interna di date e numeri in favore del [modulo PECL di PHP](https://pecl.php.net/package/intl).\n\nLa traduzione dei messaggi viene effettuata dal componente `i18n`.\nQuesto componente gestisce una serie di sorgenti di messaggi, il che ti consente di usare diverse sorgenti di messaggio basate sulle\ncategorie.\n\nFai riferimento alla sezione [Internazionalizzazione](tutorial-i18n.md) per maggiori dettagli.\n\n\nFiltri azioni\n-------------\n\nI filtri sulle azioni vengono ora implementati tramite i *behavior*. Per definire un nuovo filtro personalizzato devi estendere da \n[[yii\\base\\ActionFilter]]. Per usare un filtro collega la relativa classe ai *behavior* del controller. Per esempio, per usare \nil filtro [[yii\\filters\\AccessControl]] dovrai avere questo codice nel controller:\n\n```php\npublic function behaviors()\n{\n    return [\n        'access' => [\n            'class' => 'yii\\filters\\AccessControl',\n            'rules' => [\n                ['allow' => true, 'actions' => ['admin'], 'roles' => ['@']],\n            ],\n        ],\n    ];\n}\n```\n\nFai riferimento alla sezione [Filtri](structure-filters.md) per maggiori dettagli.\n\n\nAsset\n-----\n\nYii 2.0 introduce un nuovo concetto chiamato *asset bundle* che rimpiazza il concetto dei pacchetti di script di Yii 1.1.\n\nUn *asset bundle* è una collezione di file di asset (ad es. file Javascript, CSS, immagini...) all'interno di una directory.\nOgni *asset bundle* è rappresentato da una classe che estende [[yii\\web\\AssetBundle]].\nRegistrando un *asset bundle* tramite il metodo [[yii\\web\\AssetBundle::register()]], renderai disponibile gli asset di quel pachetto\ndisponibili via web. Diversamente da Yii 1.1 la pagina che registra il pacchetto conterrà automaticamente le referenze ai file Javascript\ne CSS specificati al suo interno.\n\nFai riferimento alla sezione [Gestione asset](structure-assets.md) per maggiori informazioni.\n\n\nHelper\n------\n\nYii 2.0 introduce molte classi statiche di uso comune, tra cui:\n\n* [[yii\\helpers\\Html]]\n* [[yii\\helpers\\ArrayHelper]]\n* [[yii\\helpers\\StringHelper]]\n* [[yii\\helpers\\FileHelper]]\n* [[yii\\helpers\\Json]]\n\nFai riferimento alla sezione [Panoramica sugli Helper](helper-overview.md) per maggiori dettagli.\n\nForm\n----\n\nYii 2.0 introduce il concetto di *campo* per la costruzione dei form usando [[yii\\widgets\\ActiveForm]]. Un campo è un\ncontentitore costituito da un'etichetta, un input, un messaggio di errore e/o un testo di suggerimento.\nUn campo è rappresentato come un oggetto [[yii\\widgets\\ActiveField|ActiveField]].\nUsando i campi potrai creare un form in un modo molto più pulito che in precedenza:\n\n```php\n<?php $form = yii\\widgets\\ActiveForm::begin(); ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n    <div class=\"form-group\">\n        <?= Html::submitButton('Login') ?>\n    </div>\n<?php yii\\widgets\\ActiveForm::end(); ?>\n```\n\nFai riferimento alla sezione [Creazione form](input-forms.md) per maggiori dettagli.\n\n\nQuery Builder\n-------------\n\nIn 1.1 la costruzione di query era dispersa in diverse classi, inclusa `CDbcommand`, \n`CDbCriteria`, e `CDbCommandBuilder`. Yii 2.0 gestisce le query mediante un oggetto [[yii\\db\\Query|Query]] \nche può essere trasformato in un comando SQL con l'aiuto di [[yii\\db\\QueryBuilder|QueryBuilder]] dietro le quinte.\nPer esempio:\n\n```php\n$query = new \\yii\\db\\Query();\n$query->select('id, nome')\n      ->from('user')\n      ->limit(10);\n\n$comando = $query->createCommand();\n$sql = $command->sql;\n$righe = $command->queryAll();\n```\n\nMa la cosa migliore di tutte è che gli stessi metodi di costruzione delle query possono essere usati con oggetti di\ntipo [Active Record](db-active-record.md).\n\nFai riferimento alla sezione [Query Builder](db-query-builder.md) per maggiori dettagli.\n\n\nActive Record\n-------------\n\nYii 2.0 introduce molti cambiamenti agli [Active Record](db-active-record.md). I due più evidenti riguardano la costruzione delle\nquery e la gestione delle relazioni.\n\nLa classe `CDbCriteria` della versione 1.1 è stata rimpiazzata da [[yii\\db\\ActiveQuery]]. Questa classe estende [[yii\\db\\Query]], e\nne eredita quindi tutti i metodi di costruzione delle query. Per iniziare la costruzione di una query devi chiamare \n[[yii\\db\\ActiveRecord::find()]]:\n\n```php\n// Per ottenere tutti i clienti *attivi* e ordinarli per ID:\n$clienti = Clienti::find()\n    ->where(['stato' => $attivo])\n    ->orderBy('id')\n    ->all();\n```\n\nPer dichiarare una relazione devi semplicemente definire una *getter* che ritorna un oggetto [[yii\\db\\ActiveQuery|ActiveQuery]].\nIl nome della proprietà definito dalla *getter* rappresenta il nome della relazione. Ad esempio il codice qui di seguito dichiara\nuna relazione `ordini` (in 1.1 avresti dovuto farlo nel metodo `relations()`):\n\n```php\nclass Cliente extends \\yii\\db\\ActiveRecord\n{\n    public function getOrdini()\n    {\n        return $this->hasMany('Ordine', ['cliente_id' => 'id']);\n    }\n}\n```\n\nOra puoi usare `$cliente->ordini` per accedere agli ordini del cliente nella tabella collegata. Puoi usare anche questo codice\nper effettuare una query relazionale al volo con una condizione di ricerca personalizzata:\n\n```php\n$ordini = $cliente->getOrdini()->andWhere('stato=1')->all();\n```\n\nQuando si usa il caricamento immediato di una relazione, Yii 2.0 si comporta diversamente rispetto alla versione precedente. In\nparticolare, Yii 1.1 creava una query con JOIN con sia il record primario che la relazione. In Yii 2.0 vengono invece eseguite due\nquery SQL distinte, senza JOIN: la prima carica le righe della tabella primaria e la seconda recupera le righe della tabella in relazione\nbasandosi sulle chiavi ottenute dalla prima.\n\nInvece di tornare oggetti [[yii\\db\\ActiveRecord|ActiveRecord]], puoi sfruttare il metodo [[yii\\db\\ActiveQuery::asArray()|asArray()]]\nin caso di query che tornano un cospicuo numero di risultati. In questo modo i risultati saranno in formato di array, il che consente\ndi risparmiare l'utilizzo di CPU e memoria in caso di grandi volumi di record. Per esempio:\n\n```php\n$clienti = Cliente::find()->asArray()->all();\n```\n\nUn'altra differenza è che non puoi più definire valori predefiniti per gli attributi tramite proprietà pubbliche.\nSe ti servono li puoi impostare nel metodo `init` della tua classe ActiveRecord.\n\n```php\npublic function init()\n{\n    parent::init();\n    $this->stato = self::STATO_NUOVO;\n}\n```\n\nNella versione precedente c'erano problemi nell'override del costruttore di un ActiveRecord. Questi problemi sono stati risolti \nin questa versione. Tieni presente che se devi aggiungere parametri al costruttore devi probabilmente sovrascrivere \n[[yii\\db\\ActiveRecord::instantiate()]].\n\nCi sono molti altri cambiamenti e miglioramenti sugli Active Record. Fai riferimento alla sezione\n[Active Record](db-active-record.md) per maggiori dettagli.  \n\n\nBehavior di Active Record \n-------------------------\n\nNella 2.0 è stata rimossa la classe base `CActiveRecordBehavior`. Per creare un nuovo behavior devi estendere direttamente\n`yii\\base\\Behavior`. Se la classe deve gestire degli eventi dell'*owner*, devi sovrascrivere il metodo `events()` come qui di seguito:\n\n```php\nnamespace app\\components;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\base\\Behavior;\n\nclass MioBehavior extends Behavior\n{\n    // ...\n\n    public function events()\n    {\n        return [\n            ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',\n        ];\n    }\n\n    public function beforeValidate($event)\n    {\n        // ...\n    }\n}\n```\n\n\nUtenti e IdentityInterface\n--------------------------\n\nLa classe `CWebUser` di Yii 1.1 è stata rimpiazzata da [[yii\\web\\User]], e non esiste più la \n`CUserIdentity`. In Yii 2.0 devi implementare [[yii\\web\\IdentityInterface]] che risulterà molto più immediata da usare.\nIl template dell'applicazione avanzata fornisce un esempio di implementazione di quella libreria.\n\nFai riferimento alle sezioni [Autenticazione](security-authentication.md), [Autorizzazione](security-authorization.md) e \n[Template applicazione avanzata](tutorial-advanced-app.md) per maggiori informazioni.\n\n\nGestione degli URL\n------------------\n\nLa gestione degli URL è molto simile a quella implementata in Yii 1.1. Uno dei miglioramenti più rilevanti è che ora sono supportati\ni parametri. Per esempio, una regola dichiarata come qui di seguito prenderà sia `post/popolari` che `post/1/popolari`. Nella 1.1\nci sarebbero volute due regole per lo stesso risultato.\n\n```php\n[\n    'pattern' => 'post/<page:\\d+>/<tag>',\n    'route' => 'post/index',\n    'defaults' => ['page' => 1],\n]\n```\n\nFai riferimento alla sezione [Url manager](runtime-url-handling.md) per ulteriori dettagli.\n\nUsare Yii 1.1 e 2.x insieme\n---------------------------\n\nSe hai del vecchio codice scritto per Yii 1.1 che vuoi usare insieme a Yii 2.0, fai riferimento alla sezione\n[Usare Yii 1.1 e 2.0 insieme](tutorial-yii-integration.md).\n\n"
  },
  {
    "path": "docs/guide-it/intro-yii.md",
    "content": "Cos'è Yii\n===========\n\nYii è un framework PHP ad alte prestazioni, basato su component, per lo sviluppo veloce di applicazioni web moderne.\nIl nome Yii (pronunciato `Yii` o `[ji:]`) significa \"semplice ed evoutivo\" in cinese. Può anche essere visto come un acronimo di **Yes It Iss** (si lo è)!\n\n\nQual'è il migliore impiego di Yii?\n----------------------------------\n\nYii è un framework di programmazione, il che significa che può essere utilizzato per sviluppare ogni\ntipo di appicazione con PHP. Grazie alla sua architettura basata sui componenti e al suo avanzato\nsupporto della cache, è particolarmente adeguato per lo sviluppo di applicazioni su larga scala quali\nportali, forum, gestori di contenuti (CMS), progetti di e-commerce, servizi web RESTful, e così via.\n\n\nCome si pone Yii rispetto ad altri framework?\n---------------------------------------------\n\nSe hai già familiarità con altri framework potrai apprezzare questi punti in comune:\n- Come la maggior parte dei framework, Yii implementa il paradigma di sviluppo MVC (Model-View-Controller) e \n  promuove l'organizzazione del codice secondo quelle regole.\n- Yii usa la filosofia secondo cui il codice dovrebbe essere semplice ed elegante. Yii non cercherà mai di \n  ridisegnare le cose solo per seguire dei pattern di sviluppo.\n- Yii è un framework completo in grado di fornire diverse funzionalità testate e pronte all'uso: costruttori di\n  query ed ActiveRecord sia per i database relazionali che NoSQL; supporto allo sviluppo di applicazioni RESTful;\n  supporto di caching a diversi livelli; e altro.\n- Yii è estremamente estensibile. Puoi pesonalizzare o sostituire quasi ogni singolo pezzo del codice base. Puoi anche\n  sfuttare la solida architettura delle estensioni di Yii per usare o sviluppare estensioni ridistribuibili.\n- Le prestazioni elevate sono sempre il focus primario di Yii.\n\nYii non è frutto di un uomo solo, ma è supportato da un [folto gruppo di sviluppatori][about_yii], così come da una numerosa\ncomunità di professionisti che contribuiscono costantemente allo sviluppo. Il gruppo di sviluppatori tiene sempre \nsott'occhio le ultime tendenze e tecnologie di sviluppo web, sulle pratiche ottimali e funzionalità degli altri\nframework e progetti. Le peculiarità più rilevanti che si trovano altrove sono regolarmente incorporate nel\ncodice principale del framework, e rese disponibili tramite semplici ed eleganti interfacce.\n\n[about_yii]: https://www.yiiframework.com/about/\n\nVersioni di Yii\n---------------\n\nYii al momento ha due versioni principali disponibili: 1.1 e 2.0. La versione 1.1 è la vecchia generazione ed è ora in \nuno stato di manutenzione. La versione 2.0 è una riscrittura completa di Yii che utilizza le ultime tecnologie e protocolli, \ninclusi Composer, PSR, namespace, trait, e così via. La versione 2.0 rappresenta l'attuale generazione del framework e \nriceverà i maggiori sforzi di sviluppo nei prossimi anni.\nQuesta guida è focalizzata principalmente sulla versione 2.0.\n\n\nRichieste e requisiti di sistema\n---------------------------------\n\nYii 2.0 richiede PHP 7.4.0 o successivo. Puoi trovare maggiori dettagli sulle richieste delle singole funzionalità\neseguendo lo script di verifica requisiti incluso in ogni versione di Yii.\n\nL'uso di Yii richiede una conoscenza base della programmazione ad oggetti (OOP), dato che Yii è un framework puramente OOP.\nYii 2.0 fa uso delle più recenti funzionalità di PHP, come i [namespace](https://www.php.net/manual/it/language.namespaces.php) e \n[trait](https://www.php.net/manual/it/language.oop5.traits.php). La compresione di questi concetti ti aiuterà a semplificare\nl'uso di Yii 2.0.\n"
  },
  {
    "path": "docs/guide-it/start-installation.md",
    "content": "Installare Yii\n==============\n\nPuoi installare Yii in due modi, usando [Composer](https://getcomposer.org/) o scaricando un archivio.\nIl metodo preferito è il primo, perché ti consente di installare [estensioni](structure-extensions.md) o aggiornare il core di Yii \nsemplicemente eseguendo un comando.\n\n> Nota: diversamente da Yii 1, le installazioni standard di Yii 2 comportano il download e l'installazione sia del framework che dello scheletro dell'applicazione\n\n\nInstallazione via Composer <span id=\"installing-via-composer\"></span>\n--------------------------\n\nSe non hai già installato Composer puoi farlo seguendo le istruzioni al sito \n[getcomposer.org](https://getcomposer.org/download/). Su Linux e Mac OS X puoi installare Composer con questo comando:\n\n    curl -sS https://getcomposer.org/installer | php\n    mv composer.phar /usr/local/bin/composer\n\nSu Windows devi scaricare ed eseguire [Composer-Setup.exe](https://getcomposer.org/Composer-Setup.exe).\n\nFai riferimento alla [documentazione di Composer](https://getcomposer.org/doc/) in casodi errori o se vuoi apprendere maggiori\ninformazioni sull'uso di Composer.\n\nSe hai già Composer installato assicurati di avere una versione aggiornata. Puoi aggiornare Composer con il comando\n`composer self-update`.\n\nUna volta installato Composer, puoi installare Yii eseguendo questo comando in una directory accessbile via web:\n\n    composer global require \"fxp/composer-asset-plugin:^1.4.1\"\n    composer create-project --prefer-dist yiisoft/yii2-app-basic basic\n\nIl primo comando installa il [plugin composer asset](https://github.com/fxpio/composer-asset-plugin)\nche consente di gestire le dipendenze di bower e npm tramite Composer. Devi eseguire questo comando solo una volta. Il secondo \ninstalla Yii in una directory di nome `basic`. Puoi scegliere un nome diverso, se preferisci.\n\n> Nota: durante l'installazione potrebbe essere che Composer ti chieda le credenziali di Github, per superato limite di utilizzo\n> delle API di Github. Questa situazione è normale perché Composer deve scaricare molte informazioni per tutti i pacchetti da Github.\n> Accedendo a Github aumenterà il limite di utilizzo delle API, consentendo a Composer di completare il suo lavoro. Per maggiori \n> dettagli fai riferimento alla \n> [documentazione di Composer](https://getcomposer.org/doc/articles/troubleshooting.md#api-rate-limit-and-oauth-tokens).\n\n> Suggerimento: se vuoi installare l'ultima versione di sviluppo di Yii, puoi usare questo comando che aggiunge una \n> [opzione di stabilità](https://getcomposer.org/doc/04-schema.md#minimum-stability):\n>\n>     composer create-project --prefer-dist --stability=dev yiisoft/yii2-app-basic basic\n>\n> Considera che la versione di sviluppo di Yii non dovrebbe essere utilizzata per siti di produzione perché potrebbe rendere instabile\n> il tuo codice.\n\n\nInstallazione da un archivio <span id=\"installing-from-archive-file\"></span>\n----------------------------\n\nL'installazione da un archivio compresso comporta tre passaggi:\n\n1. Scaricare l'archivio da [yiiframework.com](https://www.yiiframework.com/download/).\n2. Scompattare l'archivio in una directory accessible via web.\n3. Modificare il file `config/web.php` inserendo una chiave segreta per il parametro di configurazione `cookieValidationKey` \n   (questa operazione viene fatta automaticamente se installi tramite Composer):\n\n   ```php\n   // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation\n   'cookieValidationKey' => 'enter your secret key here',\n   ```\n\n\nAltre modalità di installazione <span id=\"other-installation-options\"></span>\n-------------------------------\n\nLe istruzioni sopra elencate mostrano come installare Yii, e creano inoltre un'applicazione web base funzionante.\nQuesto approccio è un ottimo punto di partenza per progetti minori, o se stai imparando Yii.\n\nMa ci sono altre opzioni disponibili per l'installazione:\n\n* se vuoi installare solo il core e costruire l'applocazione da zero puoi seguire le istruzioni della sezione\n  [costruire un'applicazione da zero](tutorial-start-from-scratch.md).\n* se vuoi avviare un'applicazione più sofisticata, che meglio si sposa per uno sviluppo di gruppo, puoi considerare l'insallazione del\n  [template di applicazione avanzata](tutorial-advanced-app.md).\n\n\nVerifica dell'installazione <span id=\"verifying-installation\"></span>\n---------------------------\n\nDopo l'installazione puoi usare il tuo browser per accedere all'applicazione Yii installata con l'URL seguente:\n\n```\nhttp://localhost/basic/web/index.php\n```\n\nQuesto indirizzo assume che hai installato Yii in una directory di nome `basic`, direttamente nella *root* del tuo webserver,\ne che il webserver è in esecuzione sulla tua macchina locale (`localhost`). Potresti doverlo modificare a seconda del tuo ambiente\ndi installazione.\n\n![Installazione di Yii completata con successo](images/start-app-installed.png)\n\nDovresti vedere la pagina sopra di congratulazioni. In caso contrario verifica se la tua installazione di PHP soddisfa i requisiti minimi\ndi Yii. Puoi verificare le richieste in due modi:\n\n* accedere all'indirizzo `http://localhost/basic/requirements.php` tramite browser;\n* lanciando questi comandi:\n\n  ```\n  cd basic\n  php requirements.php\n  ```\n\nDevi configurare la tua installazione di PHP in modo che soddisfi le richieste minime di Yii. E' molto importante che tu stia usando \nPHP 5.4 o successivo. Devi inoltre installare le [estensioni PDO di PHP](https://www.php.net/manual/en/pdo.installation.php) e un driver\ndi database di PDO (come ad esempio `pdo_mysql` per i database MySQL), se la tua applicazione richiede un database.\n\n\nConfigurazione del webserver <span id=\"configuring-web-servers\"></span>\n----------------------------\n\n> Informazione: puoi saltare questa parte per ora se stai solo provando Yii e non hai intenzione di installarlo su un server di produzione.\n\nL'applicazione installata secondo le istruzioni sopra dovrebbe funzionare senza problemi su un server \n[Apache](https://httpd.apache.org/) o [Nginx](https://nginx.org/), su Windows, Mac OS X, or Linux equipaggiati con PHP 5.4 o successivo. \nYii 2.0 è anche compatibile con le librerie [HHVM](https://hhvm.com/) di Facebook, tuttavia ci sono alcuni casi limite dove HHVM si\ncomporta diversamente dal PHP nativo, quindi devi avere maggiore cura se intendi usare HHVM.\n\nSu un server di produzione vorrai probabilmente che la tua applicazione sia accessibile tramite l'url \n`https://www.example.com/index.php` invece di `https://www.example.com/basic/web/index.php`. Questo risultato richiede che punti la\n*document root* del tuo webserver nella directory `basic/web`. Vorrai magari anche nascondere `index.php` dall'URL, come descritto\nnella sezione [analizzare e generare URL](runtime-url-handling.md).\nIn questa parte vedrai configurare il tuo server Apache o Nginx per ottenere questo risultato.\n\n> Informazione: impostando `basic/web` come *document root* impedisci agli utenti finali di accedere al codice e a file/informazioni\nriservate memorizzate nelle directory superiori a `basic/web`. Negare l'accesso a queste altre cartelle è sicuramente un vantaggio\nper la sicurezza.\n\n> Informazione: se la tua applicazione andrà installata su un servizio di hosting condiviso non avrai il permesso di modificare la\nconfigurazione del webserver, ma dovrai comunque correggere la struttura della tua applicazione per migliorare la sicurezza. Fai \nriferimento alla sezione [ambienti di hosting condiviso](tutorial-shared-hosting.md) per maggiori dettagli.\n\n\n### Configurazione consigliata di Apache <span id=\"recommended-apache-configuration\"></span>\n\nUsa questa configurazione nel file `httpd.conf` di Apache o nella definizione del tuo *VirtualHost*. Tieni presente che dovrai\nmodificare `path/to/basic/web` con il percorso reale della tua `basic/web`.\n\n```\n# Imposta *DocumentRoot* per essere \"basic/web\"\nDocumentRoot \"path/to/basic/web\"\n\n<Directory \"path/to/basic/web\">\n    # usa mod_rewrite per gli url SEF\n    RewriteEngine on\n    # If a directory or a file exists, use the request directly\n    RewriteCond %{REQUEST_FILENAME} !-f\n    RewriteCond %{REQUEST_FILENAME} !-d\n    # Otherwise forward the request to index.php\n    RewriteRule . index.php\n\n    # ...altre impostazioni...\n</Directory>\n```\n\n\n### Configurazione consigliata di Nginx <span id=\"recommended-nginx-configuration\"></span>\n\nDevi aver installato PHP con il demone [FPM](https://www.php.net/install.fpm) per usare [Nginx](https://wiki.nginx.org/).\nUsa questa configurazione per Nginx, sostituendo `path/to/basic/web` con il percorso reale di `basic/web` e `mysite.test` con\nil nome reale del server web.\n\n```\nserver {\n    charset utf-8;\n    client_max_body_size 128M;\n\n    listen 80; ## listen for ipv4\n    #listen [::]:80 default_server ipv6only=on; ## listen for ipv6\n\n    server_name mysite.test;\n    root        /path/to/basic/web;\n    index       index.php;\n\n    access_log  /path/to/basic/log/access.log main;\n    error_log   /path/to/basic/log/error.log;\n\n    location / {\n        # Redirect everything that isn't a real file to index.php\n        try_files $uri $uri/ /index.php?$args;\n    }\n\n    # uncomment to avoid processing of calls to non-existing static files by Yii\n    #location ~ \\.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ {\n    #    try_files $uri =404;\n    #}\n    #error_page 404 /404.html;\n\n    location ~ \\.php$ {\n        include fastcgi.conf;\n        fastcgi_pass   127.0.0.1:9000;\n        #fastcgi_pass unix:/var/run/php5-fpm.sock;\n        try_files $uri =404;\n    }\n\n    location ~ /\\.(ht|svn|git) {\n        deny all;\n    }\n}\n```\n\nUsando questa configurazione dovresti anche impostare `cgi.fix_pathinfo=0` in `php.ini` per evitare molte chiamate di sistema `stat()` \ninutili.\n\nInoltre considera che nel caso di server HTTPS dovrai aggiungere `fastcgi_param HTTPS on;` così che Yii possa correttamente rilevare\nse la connessione è sicura.\n"
  },
  {
    "path": "docs/guide-ja/README.md",
    "content": "Yii 2.0 決定版ガイド\n====================\n\nこのチュートリアルは [Yii ドキュメント許諾条件](https://www.yiiframework.com/doc/terms/) の下にリリースされています。\n\nAll Rights Reserved.\n\n2014 (c) Yii Software LLC.\n\n\n導入\n----\n\n* [Yii について](intro-yii.md)\n* [バージョン 1.1 からのアップグレード](intro-upgrade-from-v1.md)\n\n\n始めよう\n--------\n\n* [何を知っている必要があるか](start-prerequisites.md)\n* [Yii をインストールする](start-installation.md)\n* [アプリケーションを走らせる](start-workflow.md)\n* [こんにちは、と言う](start-hello.md)\n* [フォームを扱う](start-forms.md)\n* [データベースを扱う](start-databases.md)\n* [Gii でコードを生成する](start-gii.md)\n* [先を見通す](start-looking-ahead.md)\n\n\nアプリケーションの構造\n----------------------\n\n* [アプリケーションの構造の概要](structure-overview.md)\n* [エントリ・スクリプト](structure-entry-scripts.md)\n* [アプリケーション](structure-applications.md)\n* [アプリケーション・コンポーネント](structure-application-components.md)\n* [コントローラ](structure-controllers.md)\n* [モデル](structure-models.md)\n* [ビュー](structure-views.md)\n* [モジュール](structure-modules.md)\n* [フィルタ](structure-filters.md)\n* [ウィジェット](structure-widgets.md)\n* [アセット](structure-assets.md)\n* [エクステンション](structure-extensions.md)\n\n\nリクエストの処理\n----------------\n\n* [リクエストの処理の概要](runtime-overview.md)\n* [ブートストラップ](runtime-bootstrapping.md)\n* [ルーティングと URL 生成](runtime-routing.md)\n* [リクエスト](runtime-requests.md)\n* [レスポンス](runtime-responses.md)\n* [セッションとクッキー](runtime-sessions-cookies.md)\n* [エラー処理](runtime-handling-errors.md)\n* [ロギング](runtime-logging.md)\n\n\n鍵となる概念\n------------\n\n* [コンポーネント](concept-components.md)\n* [プロパティ](concept-properties.md)\n* [イベント](concept-events.md)\n* [ビヘイビア](concept-behaviors.md)\n* [構成情報](concept-configurations.md)\n* [エイリアス](concept-aliases.md)\n* [クラスのオートロード](concept-autoloading.md)\n* [サービス・ロケータ](concept-service-locator.md)\n* [依存注入コンテナ](concept-di-container.md)\n\n\nデータベースの取り扱い\n----------------------\n\n* [データベース・アクセス・オブジェクト](db-dao.md): データベースへの接続、基本的なクエリ、トランザクション、および、スキーマ操作\n* [クエリ・ビルダ](db-query-builder.md): シンプルな抽象レイヤを使ってデータベースに対してクエリを行う\n* [アクティブ・レコード](db-active-record.md): アクティブ・レコード ORM、レコードの読み出しと操作、リレーションの定義\n* [マイグレーション](db-migrations.md): チーム開発環境においてデータベースにバージョン・コントロールを適用\n* [Sphinx](https://www.yiiframework.com/extension/yiisoft/yii2-sphinx/doc/guide)\n* [Redis](https://www.yiiframework.com/extension/yiisoft/yii2-redis/doc/guide)\n* [MongoDB](https://www.yiiframework.com/extension/yiisoft/yii2-mongodb/doc/guide)\n* [ElasticSearch](https://www.yiiframework.com/extension/yiisoft/yii2-elasticsearch/doc/guide)\n\n\nユーザからのデータ取得\n----------------------\n\n* [フォームを作成する](input-forms.md)\n* [入力を検証する](input-validation.md)\n* [ファイルをアップロードする](input-file-upload.md)\n* [表形式インプットのデータ収集](input-tabular-input.md)\n* [複数のモデルのデータを取得する](input-multiple-models.md)\n* [クライアント・サイドで ActiveForm を拡張する](input-form-javascript.md)\n\n\nデータの表示\n------------\n\n* [データのフォーマット](output-formatting.md)\n* [ページネーション](output-pagination.md)\n* [並べ替え](output-sorting.md)\n* [データ・プロバイダ](output-data-providers.md)\n* [データ・ウィジェット](output-data-widgets.md)\n* [クライアント・スクリプトを扱う](output-client-scripts.md)\n* [テーマ](output-theming.md)\n\n\nセキュリティ\n------------\n\n* [セキュリティの概要](security-overview.md)\n* [認証](security-authentication.md)\n* [権限付与](security-authorization.md)\n* [パスワードを扱う](security-passwords.md)\n* [暗号化](security-cryptography.md)\n* [認証クライアント](https://www.yiiframework.com/extension/yiisoft/yii2-authclient/doc/guide)\n* [ベスト・プラクティス](security-best-practices.md)\n\n\nキャッシュ\n----------\n\n* [キャッシュの概要](caching-overview.md)\n* [データ・キャッシュ](caching-data.md)\n* [フラグメント・キャッシュ](caching-fragment.md)\n* [ページ・キャッシュ](caching-page.md)\n* [HTTP キャッシュ](caching-http.md)\n\n\nRESTful ウェブ・サービス\n------------------------\n\n* [クイック・スタート](rest-quick-start.md)\n* [リソース](rest-resources.md)\n* [コレクションのフィルタリング](rest-filtering-collections.md)\n* [コントローラ](rest-controllers.md)\n* [ルーティング](rest-routing.md)\n* [レスポンス形式の設定](rest-response-formatting.md)\n* [認証](rest-authentication.md)\n* [レート制限](rest-rate-limiting.md)\n* [バージョン管理](rest-versioning.md)\n* [エラー処理](rest-error-handling.md)\n\n\n開発ツール\n----------\n\n* [デバッグ・ツールバーとデバッガ](https://www.yiiframework.com/extension/yiisoft/yii2-debug/doc/guide)\n* [Gii を使ってコードを生成する](https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide)\n* [API ドキュメントを生成する](https://www.yiiframework.com/extension/yiisoft/yii2-apidoc)\n\n\nテスト\n------\n\n* [テストの概要](test-overview.md)\n* [テスト環境の構築](test-environment-setup.md)\n* [単体テスト](test-unit.md)\n* [機能テスト](test-functional.md)\n* [受入テスト](test-acceptance.md)\n* [フィクスチャ](test-fixtures.md)\n\n\nスペシャル・トピック\n--------------------\n\n* [アドバンスト・プロジェクト・テンプレート](https://www.yiiframework.com/extension/yiisoft/yii2-app-advanced/doc/guide)\n* [アプリケーションを一から構築する](tutorial-start-from-scratch.md)\n* [コンソール・コマンド](tutorial-console.md)\n* [コア・バリデータ](tutorial-core-validators.md)\n* [国際化](tutorial-i18n.md)\n* [メール送信](tutorial-mailing.md)\n* [パフォーマンス・チューニング](tutorial-performance-tuning.md)\n* [共有ホスティング環境](tutorial-shared-hosting.md)\n* [テンプレート・エンジン](tutorial-template-engines.md)\n* [サードパーティのコードを扱う](tutorial-yii-integration.md)\n* [Yii をマイクロ・フレームワークとして使う](tutorial-yii-as-micro-framework.md)\n\n\nウィジェット\n------------\n\n* [GridView](https://www.yiiframework.com/doc-2.0/yii-grid-gridview.html)\n* [ListView](https://www.yiiframework.com/doc-2.0/yii-widgets-listview.html)\n* [DetailView](https://www.yiiframework.com/doc-2.0/yii-widgets-detailview.html)\n* [ActiveForm](https://www.yiiframework.com/doc/guide/2.0/ja/input-forms#activerecord-based-forms-activeform)\n* [Pjax](https://www.yiiframework.com/doc-2.0/yii-widgets-pjax.html)\n* [Menu](https://www.yiiframework.com/doc-2.0/yii-widgets-menu.html)\n* [LinkPager](https://www.yiiframework.com/doc-2.0/yii-widgets-linkpager.html)\n* [LinkSorter](https://www.yiiframework.com/doc-2.0/yii-widgets-linksorter.html)\n* [Bootstrap ウィジェット](https://www.yiiframework.com/extension/yiisoft/yii2-bootstrap/doc/guide)\n* [jQuery UI ウィジェット](https://www.yiiframework.com/extension/yiisoft/yii2-jui/doc/guide)\n\n\nヘルパ\n------\n\n* [ヘルパの概要](helper-overview.md)\n* [配列ヘルパ](helper-array.md)\n* [Html ヘルパ](helper-html.md)\n* [Url ヘルパ](helper-url.md)\n\n"
  },
  {
    "path": "docs/guide-ja/blocktypes.json",
    "content": "{\n    \"Note:\": \"補足:\",\n    \"Info:\": \"情報:\",\n    \"Tip:\": \"ヒント:\",\n    \"Warning:\": \"警告:\"\n}"
  },
  {
    "path": "docs/guide-ja/caching-data.md",
    "content": "データ・キャッシュ\n==================\n\nデータ・キャッシュは PHP の変数をキャッシュに格納し、あとでキャッシュからそれらを読み込みます。\nこれは、[クエリ・キャッシュ](#query-caching) や [ページ・キャッシュ](caching-page.md) など、\nより高度なキャッシュ機能の基礎でもあります。\n\n以下のコードが、データ・キャッシュの典型的な利用パターンです。ここで、`$cache` は [キャッシュ・コンポーネント](#cache-components)\nを指しています。\n\n```php\n// キャッシュから $data を取得しようと試みる\n$data = $cache->get($key);\n\nif ($data === false) {\n    // キャッシュの中に $data が見つからない場合は一から作る\n    $data = $this->calculateSomething();\n\n    // $data をキャッシュに格納して、次回はそれを取得できるようにする\n    $cache->set($key, $data);\n}\n\n// この時点で $data は利用可能になっている\n```\n\nバージョン 2.0.11 以降は、[キャッシュ・コンポーネント](#cache-components) が提供する [[yii\\caching\\Cache::getOrSet()|getOrSet()]] メソッドを使って、\nデータを取得、計算、保存するためのコードを単純化することが出来ます。\n次に示すコードは、上述の例と全く同じことをするものです。\n\n```php\n$data = $cache->getOrSet($key, function () {\n    return $this->calculateSomething();\n});\n```\n\nキャッシュが `$key` と関連づけられたデータを保持している場合は、キャッシュされている値が返されます。\nそうでない場合は、渡された無名関数が実行されて値が計算され、その値がキャッシュされるとともに返されます。\n\n無名関数が外部のスコープの何らかのデータを必要とする場合は、それを `use` 文を使って渡すことが出来ます。\n例えば、\n\n```php\n$user_id = 42;\n$data = $cache->getOrSet($key, function () use ($user_id) {\n    return $this->calculateSomething($user_id);\n});\n```\n\n> Note: [[yii\\caching\\Cache::getOrSet()|getOrSet()]] メソッドは、有効期限と依存もサポートしています。\n  詳しくは [キャッシュの有効期限](#cache-expiration) と [キャッシュの依存](#cache-dependencies) を参照してください。\n  \n\n## キャッシュ・コンポーネント <span id=\"cache-components\"></span>\n\nデータ・キャッシュはメモリ、ファイル、データベースなどさまざまなキャッシュ・ストレージを表す、\nいわゆる *キャッシュ・コンポーネント* に依存しています。\n\nキャッシュ・コンポーネントは通常グローバルに設定しアクセスできるように\n[アプリケーション・コンポーネント](structure-application-components.md) として登録されます。\n以下のコードは、二台のキャッシュ・サーバを用いる [memcached](https://memcached.org/) を使うように\n`cache` アプリケーション・コンポーネントを構成する方法を示すものです。\n\n```php\n'components' => [\n    'cache' => [\n        'class' => 'yii\\caching\\MemCache',\n        'servers' => [\n            [\n                'host' => 'server1',\n                'port' => 11211,\n                'weight' => 100,\n            ],\n            [\n                'host' => 'server2',\n                'port' => 11211,\n                'weight' => 50,\n            ],\n        ],\n    ],\n],\n```\n\nこうすると、上記のキャッシュ・コンポーネントに `Yii::$app->cache` という式でアクセスできるようになります。\n\nすべてのキャッシュ・コンポーネントは同じ API をサポートしているので、アプリケーションの構成情報で設定しなおせば、\nキャッシュを使っているコードに変更を加えることなく、異なるキャッシュ・コンポーネントに入れ替えることができます。\n例えば上記の構成を [[yii\\caching\\ApcCache|APC キャッシュ]] を使うように変更する場合は以下のようにします:\n\n\n```php\n'components' => [\n    'cache' => [\n        'class' => 'yii\\caching\\ApcCache',\n    ],\n],\n```\n\n> Tip: キャッシュ・コンポーネントは複数登録することができます。`cache` という名前のコンポーネントが、\n  キャッシュに依存する多数のクラスによってデフォルトで使用されます (例えば [[yii\\web\\UrlManager]] など) 。\n\n\n### サポートされているキャッシュ・ストレージ <span id=\"supported-cache-storage\"></span>\n\nYii はさまざまなキャッシュ・ストレージをサポートしています。以下がその概要です:\n\n* [[yii\\caching\\ApcCache]]: PHP の [APC](https://www.php.net/manual/ja/book.apc.php) 拡張モジュールを使用します。\n  集中型の重厚なアプリケーションのキャッシュを扱うときには最速の一つとして考えることができます\n  (例えば、サーバが一台で、専用のロード・バランサを持っていない、などの場合)。\n* [[yii\\caching\\DbCache]]: キャッシュされたデータを格納するためにデータベースのテーブルを使用します。\n  このキャッシュを使用するには [[yii\\caching\\DbCache::cacheTable]] で指定したテーブルを作成する必要があります。\n* [[yii\\caching\\ArrayCache]]: 配列に値を保存することによって、現在のリクエストのためだけのキャッシュを提供します。\n  ArrayCache のパフォーマンスを高めるために、[[yii\\caching\\ArrayCache::$serializer]] を `false` に設定して、\n  保存するデータのシリアライズを無効にすることが出来ます。   .\n* [[yii\\caching\\DummyCache]]: 実際にはキャッシュを行わない、キャッシュのプレースホルダとして働きます。\n  このコンポーネントの目的は、キャッシュの可用性をチェックする必要があるコードを簡略化することです。\n  たとえば、開発中やサーバに実際のキャッシュ・サポートがない場合でも、\n  このキャッシュを使用するようにキャッシュ・コンポーネントを構成することができます。\n  そして、実際のキャッシュ・サポートが有効になったときに、対応するキャッシュ・コンポーネントに切替えて使用します。\n  どちらの場合も、`Yii::$app->cache` が `null` かも知れないと心配せずに、\n  データを取得するために同じコード `Yii::$app->cache->get($key)` を使用できます。\n* [[yii\\caching\\FileCache]]: キャッシュされたデータを保存するために通常のファイルを使用します。\n  これはページ・コンテントなど大きなかたまりのデータに特に適しています。\n* [[yii\\caching\\MemCache]]: PHP の [Memcache](https://www.php.net/manual/ja/book.memcache.php) と\n  [Memcached](https://www.php.net/manual/ja/book.memcached.php) 拡張モジュールを使用します。\n  分散型のアプリケーションでキャッシュを扱うときには最速の一つとして考えることができます\n  (例えば、複数台のサーバで、ロード・バランサがある、などの場合) 。\n* [[yii\\redis\\Cache]]: [Redis](https://redis.io/) の key-value ストアに基づいてキャッシュ・コンポーネントを実装しています。\n  (Redis の バージョン 2.6.12 以降が必要とされます) 。\n* [[yii\\caching\\WinCache]]: PHP の [WinCache](https://iis.net/downloads/microsoft/wincache-extension) エクステンションを使用します。\n  ([参照リンク](https://www.php.net/manual/ja/book.wincache.php))\n\n\n> Tip: 同じアプリケーション内で異なるキャッシュを使用することもできます。\n  一般的なやり方として、小さくとも常に使用されるデータ (例えば、統計データ) を格納する場合はメモリ・ベースのキャッシュ・ストレージを使用し、\n  大きくて使用頻度の低いデータ (例えば、ページ・コンテント) を格納する場合はファイル・ベース、またはデータベースのキャッシュ・ストレージを使用します  。\n\n\n## キャッシュ API <span id=\"cache-apis\"></span>\n\nすべてのキャッシュ・コンポーネントが同じ基底クラス [[yii\\caching\\Cache]] を持っているので、以下の API をサポートしています。\n\n* [[yii\\caching\\Cache::get()|get()]]: 指定されたキーを用いてキャッシュからデータを取得します。\n  データが見つからないか、もしくは有効期限が切れたり無効になったりしている場合は false を返します。\n* [[yii\\caching\\Cache::set()|set()]]: キーによって識別されるデータをキャッシュに格納します。\n* [[yii\\caching\\Cache::add()|add()]]: キーがキャッシュ内で見つからない場合に、キーによって識別されるデータをキャッシュに格納します。\n* [[yii\\caching\\Cache::getOrSet()|getOrSet()]]: 指定されたキーを用いてキャッシュからデータを取得します。\n  取得できなかった場合は、渡されたコールバック関数を実行し、関数の返り値をそのキーでキャッシュに保存し、そしてその値を返します。\n* [[yii\\caching\\Cache::multiGet()|multiGet()]]: 指定されたキーを用いてキャッシュから複数のデータを取得します。\n* [[yii\\caching\\Cache::multiSet()|multiSet()]]: キャッシュに複数のデータを格納します。各データはキーによって識別されます。\n* [[yii\\caching\\Cache::multiAdd()|multiAdd()]]: キャッシュに複数のデータを格納します。\n  各データはキーによって識別されます。もしキャッシュ内にキーがすでに存在する場合はスキップされます。\n* [[yii\\caching\\Cache::exists()|exists()]]: 指定されたキーがキャッシュ内で見つかったかどうかを示す値を返します。\n* [[yii\\caching\\Cache::delete()|delete()]]: キャッシュからキーによって識別されるデータを削除します。\n* [[yii\\caching\\Cache::flush()|flush()]]: キャッシュからすべてのデータを削除します。\n\n> Note: boolean 型の `false` を直接にキャッシュしてはいけません。\n  なぜなら、[[yii\\caching\\Cache::get()|get()]] メソッドは、データがキャッシュ内に見つからないことを示すために戻り値として `false` を使用しているからです。\n  代りに、配列内に `false` を置いてキャッシュすることによって、この問題を回避して下さい。\n\nキャッシュされたデータを取得する際に発生するオーバーヘッドを減らすために、MemCache, APC などのいくつかのキャッシュ・ストレージは、\nバッチ・モードで複数のキャッシュされた値を取得することをサポートしています。\n[[yii\\caching\\Cache::multiGet()|multiGet()]] や [[yii\\caching\\Cache::multiAdd()|multiAdd()]] などの API はこの機能を十分に引き出すために提供されています。\n基礎となるキャッシュ・ストレージがこの機能をサポートしていない場合には、シミュレートされます。\n\n[[yii\\caching\\Cache]] は `ArrayAccess` インタフェイスを継承しているので、キャッシュ・コンポーネントは配列のように扱うことができます。\n以下はいくつかの例です:\n\n```php\n$cache['var1'] = $value1;  // $cache->set('var1', $value1); と同等\n$value2 = $cache['var2'];  // $value2 = $cache->get('var2'); と同等\n```\n\n\n### キャッシュのキー <span id=\"cache-keys\"></span>\n\nキャッシュに格納される各データは、一意のキーによって識別されます。\nキャッシュ内にデータを格納するときはキーを指定する必要があります。\nまた、あとでキャッシュからデータを取得するときは、それに対応するキーを提供しなければなりません。\n\nキャッシュのキーとしては、文字列または任意の値を使用することができます。\nキーが文字列でない場合は、自動的に文字列にシリアライズされます。\n\nキャッシュのキーを定義する一般的なやり方として、全ての決定要素を配列の形で含めるという方方があります。\n例えば [[yii\\db\\Schema]] はデータベース・テーブルのスキーマ情報を以下のキーを使用してキャッシュしています。\n\n```php\n[\n    __CLASS__,              // スキーマ・クラス名\n    $this->db->dsn,         // データベース接続のデータ・ソース名\n    $this->db->username,    // データベース接続のログイン・ユーザ\n    $name,                  // テーブル名\n];\n```\n\n見ての通り、キーは一意にデータベースのテーブルを指定するために必要なすべての情報を含んでいます。\n\n> Note: [[yii\\caching\\Cache::multiSet()|multiSet()]] または [[yii\\caching\\Cache::multiAdd()|multiAdd()]] によってキャッシュに保存される値が持つことが出来るのは、\n文字列または整数のキーだけです。それらより複雑なキーを設定する必要がある場合は、\n[[yii\\caching\\Cache::set()|set()]] または [[yii\\caching\\Cache::add()|add()]] によって、値を個別に保存してください。\n\n同じキャッシュ・ストレージが異なるアプリケーションによって使用されているときは、\nキャッシュのキーの競合を避けるために、各アプリケーションではユニークなキーの接頭辞を指定する必要があります。\nこれは [[yii\\caching\\Cache::keyPrefix]] プロパティを設定することで出来ます。例えば、アプリケーション構成情報で以下のように書くことができます:\n\n```php\n'components' => [\n    'cache' => [\n        'class' => 'yii\\caching\\ApcCache',\n        'keyPrefix' => 'myapp',       // ユニークなキャッシュのキーの接頭辞\n    ],\n],\n```\n\n相互運用性を確保するために、英数字のみを使用する必要があります。\n\n\n### キャッシュの有効期限 <span id=\"cache-expiration\"></span>\n\nキャッシュに格納されたデータは、何らかのキャッシュ・ポリシー (例えば、キャッシュ・スペースがいっぱいになったときは最も古いデータが削除される、など)\nの執行によって除去されない限り、永遠に残り続けます。\nこの動作を変えるために [[yii\\caching\\Cache::set()|set()]] を呼んでデータ・アイテムを保存するときに、有効期限パラメータを指定することができます。\nこのパラメータは、データ・アイテムが何秒間有効なものとしてキャッシュ内に残ることが出来るかを示します。\n[[yii\\caching\\Cache::get()|get()]] でデータ・アイテムを取得する際に有効期限が切れていた場合は、\nキャッシュ内にデータが見つからなかったことを示す `false` が返されます。例えば、\n\n```php\n// 最大で 45 秒間キャッシュ内にデータを保持する\n$cache->set($key, $data, 45);\n\nsleep(50);\n\n$data = $cache->get($key);\nif ($data === false) {\n    // $data は有効期限が切れているか、またはキャッシュ内に見つからない\n}\n```\n\nバージョン 2.0.11 以降は、デフォルトの無限の有効期限に替えて特定の有効期限を指定したい場合には、\nキャッシュ・コンポーネントの構成で [[yii\\caching\\Cache::$defaultDuration|defaultDuration]] の値を指定することが出来ます。\nこれによって、特定の `duration` パラメータを毎回 [[yii\\caching\\Cache::set()|set()]] に渡さなくてもよくなります。\n\n\n### キャッシュの依存 <span id=\"cache-dependencies\"></span>\n\n有効期限の設定に加えて、キャッシュされたデータは、いわゆる *キャッシュの依存* (キャッシュが依存している事物) の変化によって無効にすることもできます。\n例えば [[yii\\caching\\FileDependency]] は、キャッシュがファイルの更新時刻に依存していることを表しています。\nこの依存が変化したときは、対応するファイルが更新されたことを意味します。\nその結果、キャッシュ内で見つかった古いファイルのコンテントは、無効とされるべきであり\n[[yii\\caching\\Cache::get()|get()]] は `false` を返さなければなりません。\n\nキャッシュの依存は [[yii\\caching\\Dependency]] の子孫クラスのオブジェクトとして表現されます。\n[[yii\\caching\\Cache::set()|set()]] でキャッシュにデータ・アイテムを格納する際に、\n関連するキャッシュの依存のオブジェクトを一緒に渡すことができます。例えば、\n\n```php\n// example.txt ファイルの更新日時への依存を作成します。\n$dependency = new \\yii\\caching\\FileDependency(['fileName' => 'example.txt']);\n\n// データは 30 秒で期限切れになります。\n// さらに、example.txt が変更された場合、有効期限内でも無効になります。\n$cache->set($key, $data, 30, $dependency);\n\n// キャッシュはデータの有効期限が切れているかをチェックします。\n// 同時に、関連付けられた依存が変更されているかもチェックします。\n// これらの条件のいずれかが満たされている場合は false を返します。\n$data = $cache->get($key);\n```\n\n以下は利用可能なキャッシュの依存の概要です:\n\n- [[yii\\caching\\ChainedDependency]]: チェーン上のいずれかの依存が変更された場合に、依存が変更されます。\n- [[yii\\caching\\DbDependency]]: 指定された SQL 文のクエリ結果が変更された場合、依存が変更されます。\n- [[yii\\caching\\ExpressionDependency]]: 指定された PHP の式の結果が変更された場合、依存が変更されます。\n- [[yii\\caching\\CallbackDependency]]: 指定されたPHPコールバックの結果が変更された場合、依存関係は変更されます。\n- [[yii\\caching\\FileDependency]]: ファイルの最終更新日時が変更された場合、依存が変更されます。\n- [[yii\\caching\\TagDependency]]: キャッシュされるデータ・アイテムに一つまたは複数のタグを関連付けます。\n  [[yii\\caching\\TagDependency::invalidate()]] を呼び出すことによって、指定されたタグ (複数可) を持つキャッシュされたデータ・アイテムを無効にすることができます。\n\n> Note: 依存を有するキャッシュについて [[yii\\caching\\Cache::exists()|exists()]] メソッドを使用することは避けてください。\n  このメソッドは、キャッシュされたデータに関連づけられた依存がある場合でも、依存が変化したかどうかをチェックしません。\n  つまり、[[yii\\caching\\Cache::exists()|exists()]] が `true` を返しているのに、 [[yii\\caching\\Cache::get()|get()]] が `false` を返すという場合があり得ます。\n\n\n## クエリ・キャッシュ <span id=\"query-caching\"></span>\n\nクエリ・キャッシュは、データ・キャッシュ上に構築された特別なキャッシュ機能で、\nデータベースのクエリ結果をキャッシュするために提供されています。\n\nクエリ・キャッシュは [[yii\\db\\Connection|データベース接続]] と有効な `cache` [アプリケーション・コンポーネント](#cache-components) を必要とします。\n`$db` を [[yii\\db\\Connection]] のインスタンスと仮定した場合、クエリ・キャッシュの基本的な使い方は以下のようになります:\n\n```php\n$result = $db->cache(function ($db) {\n\n    // クエリ・キャッシュが有効で、かつクエリ結果がキャッシュ内にある場合、\n    // SQL クエリ結果がキャッシュから提供されます\n    return $db->createCommand('SELECT * FROM customer WHERE id=1')->queryOne();\n\n});\n```\n\nクエリ・キャッシュは [DAO](db-dao.md) だけではなく [アクティブ・レコード](db-active-record.md) でも使用することができます。\n\n```php\n$result = Customer::getDb()->cache(function ($db) {\n    return Customer::find()->where(['id' => 1])->one();\n});\n```\n\n> Info: いくつかの DBMS (例えば [MySQL](https://dev.mysql.com/doc/refman/5.1/ja/query-cache.html))\n  もデータベース・サーバ・サイドのクエリ・キャッシュをサポートしています。\n  どちらのクエリ・キャッシュ・メカニズムを選んでも構いません。\n  前述した Yii のクエリ・キャッシュにはキャッシュの依存を柔軟に指定できるという利点があり、潜在的にはより効率的です。\n\n2.0.14 以降は、下記のショートカットを使用することが出来ます。\n\n```php\n(new Query())->cache(7200)->all();\n// および\nUser::find()->cache(7200)->all();\n```\n\n\n### 構成 <span id=\"query-caching-configs\"></span>\n\nクエリ・キャッシュには [[yii\\db\\Connection]] を通して設定可能な三つのグローバルなオプションがあります:\n\n* [[yii\\db\\Connection::enableQueryCache|enableQueryCache]]: クエリ・キャッシュを可能にするかどうか。デフォルトは `true` です。\n  実効的にクエリ・キャッシュをオンにするには [[yii\\db\\Connection::queryCache|queryCache]]\n  によって指定される有効なキャッシュを持っている必要があることに注意してください。\n* [[yii\\db\\Connection::queryCacheDuration|queryCacheDuration]]: これはクエリ結果がキャッシュ内に有効な状態として\n  持続できる秒数を表します。\n  クエリ・キャッシュを永遠にキャッシュに残したい場合は 0 を指定することができます。\n  このプロパティは [[yii\\db\\Connection::cache()]] が持続時間を指定せず呼び出されたときに使用されるデフォルト値です。\n* [[yii\\db\\Connection::queryCache|queryCache]]: これはキャッシュ・アプリケーション・コンポーネントの ID を表します。\nデフォルトは `'cache'` です。有効なキャッシュ・コンポーネントが存在する場合にのみ、クエリ・キャッシュが使用可能になります。\n\n\n### 使い方 <span id=\"query-caching-usages\"></span>\n\nクエリ・キャッシュを使用する必要がある複数の SQL クエリを持っている場合は [[yii\\db\\Connection::cache()]]\nを使用することができます。使い方は以下のとおりです。\n\n```php\n$duration = 60;     // クエリ結果を 60 秒間 キャッシュ\n$dependency = ...;  // 依存のオプション\n\n$result = $db->cache(function ($db) {\n\n    // ... ここで SQL クエリを実行します ...\n\n    return $result;\n\n}, $duration, $dependency);\n```\n\n無名関数内の任意の SQL クエリは、指定した依存とともに指定された期間キャッシュされます。\nもしキャッシュ内に有効なクエリ結果が見つかった場合は、クエリはスキップされ、代りに結果がキャッシュから提供されます。\n`$duration` の指定がない場合 [[yii\\db\\Connection::queryCacheDuration|queryCacheDuration]]\nで指定されている値が代りに使用されます。\n\n場合によっては `cache()` 内でいくつかの特定のクエリに対してクエリ・キャッシュを無効にしたいことが有るでしょう。\nそのときは [[yii\\db\\Connection::noCache()]] を使用します。\n\n```php\n$result = $db->cache(function ($db) {\n\n    // クエリ・キャッシュを使用する SQL クエリ\n\n    $db->noCache(function ($db) {\n\n        // クエリ・キャッシュを使用しない SQL クエリ\n\n    });\n\n    // ...\n\n    return $result;\n});\n```\n\n単一のクエリのためだけにクエリ・キャッシュを使用したい場合は、コマンドを構築するときに [[yii\\db\\Command::cache()]]\nを呼び出すことができます。例えば、\n\n```php\n// クエリ・キャッシュを使い、期間を 60 秒にセットする\n$customer = $db->createCommand('SELECT * FROM customer WHERE id=1')->cache(60)->queryOne();\n```\n\nまた、一つのコマンドに対してクエリ・キャッシュを無効にするために [[yii\\db\\Command::noCache()]] を使用することもできます。例えば、\n\n```php\n$result = $db->cache(function ($db) {\n\n    // クエリ・キャッシュを使用する SQL クエリ\n\n    // このコマンドにはクエリ・キャッシュを使用しない\n    $customer = $db->createCommand('SELECT * FROM customer WHERE id=1')->noCache()->queryOne();\n\n    // ...\n\n    return $result;\n});\n```\n\n\n### 制約 <span id=\"query-caching-limitations\"></span>\n\nリソース・ハンドラを返すようなクエリにはクエリ・キャッシュは働きません。\n例えば、いくつかの DBMS において BLOB 型のカラムを用いる場合、\nクエリ結果はカラム・データに対するリソース・ハンドラを返します。\n\nいくつかのキャッシュ・ストレージはサイズに制約があります。\n例えば Memcache では、各エントリのサイズは 1MB が上限値です。\nそのためクエリ結果のサイズがこの制約を越える場合、キャッシュは失敗します。\n\n\n## キャッシュのフラッシュ <span id=\"cache-flushing\">\n\n保存されている全てのキャッシュ・データを無効化する必要がある場合は、[[yii\\caching\\Cache::flush()]] を呼ぶことが出来ます。\n\nコンソールから `yii cache/flush` を呼ぶことによっても、キャッシュをフラッシュすることが出来ます。\n - `yii cache`: アプリケーションで利用可能なキャッシュのリストを表示します。\n - `yii cache/flush cache1 cache2`: キャッシュ・コンポーネント `cache1` と `cache2` をフラッシュします\n  (複数のコンポーネント名をスペースで区切って渡すことが出来ます)\n - `yii cache/flush-all`: アプリケーションの全てのキャッシュ・コンポーネントをフラッシュします。\n- `yii cache/flush-schema db`: 指定された DB 接続に対する DB スキーマ・キャッシュをクリアします。\n\n> Info: デフォルトでは、コンソール・アプリケーションは独立した構成情報ファイルを使用します。\n正しい結果を得るためには、ウェブとコンソールのアプリケーション構成で同じキャッシュ・コンポーネントを使用していることを確認してください。\n"
  },
  {
    "path": "docs/guide-ja/caching-fragment.md",
    "content": "フラグメント・キャッシュ\n========================\n\nフラグメント・キャッシュは、ウェブ・ページの断片をキャッシュすることを指します。\n例えば、ページ内の表に年間販売の概要が表示されている場合、リクエスト毎にこの表を生成するのにかかる時間を削減するために、キャッシュにこの表を格納することができます。\nフラグメント・キャッシュは [データ・キャッシュ](caching-data.md) 上に構築されています。\n\nフラグメント・キャッシュを使用するには [ビュー](structure-views.md) で以下の構文を使用します:\n\n```php\nif ($this->beginCache($id)) {\n\n    // ... ここに生成するコンテントを書く ...\n\n    $this->endCache();\n}\n```\n\nつまり、コンテント生成ロジックを [[yii\\base\\View::beginCache()|beginCache()]] と [[yii\\base\\View::endCache()|endCache()]] の呼び出しのペアで囲みます。\nコンテントがキャッシュ内で見つかった場合、[[yii\\base\\View::beginCache()|beginCache()]] はキャッシュされたコンテントをレンダリングして\n`false` を返し、結果として、コンテント生成ロジックがスキップされます。\nそれ以外の場合はコンテント生成ロジックが呼ばれ、そして [[yii\\base\\View::endCache()|endCache()]] が呼ばれたときに、\n生成されたコンテントがキャプチャされ、キャッシュに格納されます。\n\n[データ・キャッシュ](caching-data.md) と同様に、キャッシュされるコンテントを識別するためにユニークな `$id` が必要になります。\n\n次のようにすると、フラグメント・キャッシュを削除することが出来ます。\n```php\nYii::$app->cache->delete(['yii\\widgets\\FragmentCache', $id]);\n```\n\n\n## キャッシュのオプション <span id=\"caching-options\"></span>\n\n[[yii\\base\\View::beginCache()|beginCache()]] メソッドの 2 番目のパラメータとしてオプションの配列を渡すことによって、\nフラグメント・キャッシュに関する追加のオプションを指定することができます。\n舞台の裏側では、このオプションの配列が、実際のフラグメント・キャッシュ機能を実装する\n[[yii\\widgets\\FragmentCache]] ウィジェットを構成するために使用されます。\n\n### 持続時間 <span id=\"duration\"></span>\n\nおそらくフラグメント・キャッシュで通常よく使われるであろうオプションは [[yii\\widgets\\FragmentCache::duration|duration]] でしょう。\nこのオプションはコンテントがどれだけの時間キャッシュ内において有効であるかを指定します。\n以下のコードは最大で 1 時間コンテントの断片をキャッシュします:\n\n```php\nif ($this->beginCache($id, ['duration' => 3600])) {\n\n    // ... ここに生成するコンテントを書く ...\n\n    $this->endCache();\n}\n```\n\nこのオプションがセットされていない場合は、デフォルトである 60 が使われます。すなわち、キャッシュされたコンテントの有効期限は 60 秒後に切れることになります。\n\n\n### 依存 <span id=\"dependencies\"></span>\n\n[データ・キャッシュ](caching-data.md#cache-dependencies) と同様に、キャッシュされたコンテントの断片は依存を持つことができます。\n例えば、表示されている投稿の内容は、投稿が変更されたか否かに依存します。\n\n依存を指定するには [[yii\\widgets\\FragmentCache::dependency|dependency]] オプションに [[yii\\caching\\Dependency]]\nオブジェクトを指定するか、または依存オブジェクトを作成するための構成情報配列を指定します。\n以下のコードはコンテントの断片が `updated_at` カラムの値の変化に依存していることを指定しています:\n\n```php\n$dependency = [\n    'class' => 'yii\\caching\\DbDependency',\n    'sql' => 'SELECT MAX(updated_at) FROM post',\n];\n\nif ($this->beginCache($id, ['dependency' => $dependency])) {\n\n    // ... ここに生成するコンテントを書く ...\n\n    $this->endCache();\n}\n```\n\n\n### バリエーション <span id=\"variations\"></span>\n\nキャッシュされるコンテントには、何らかのパラメータによってバリエーションを持たせることが出来ます。\n例えば、複数の言語をサポートしているウェブ・アプリケーションでは、ビュー・コードの同じ部分が言語によってさまざまに異なるコンテントを生成することが有り得ます。\n従って、現在のアプリケーションの言語に応じて、キャッシュされるコンテントのバリエーションを持つ必要があります。\n\nキャッシュのバリエーションを指定するには [[yii\\widgets\\FragmentCache::variations|variations]] オプションを指定します。\nこのオプションは、それぞれが特定のバリエーションの要素を表すスカラ値の配列でなければなりません。\n例えば、言語によるキャッシュ・コンテントのバリエーションを持つためには、以下のコードを使います。\n\n```php\nif ($this->beginCache($id, ['variations' => [Yii::$app->language]])) {\n\n    // ... ここに生成するコンテントを書く ...\n\n    $this->endCache();\n}\n```\n\n\n### キャッシュをトグルする <span id=\"toggling-caching\"></span>\n\n時として、ある条件が満たされた場合にのみフラグメント・キャッシュを有効にしたい場合があるでしょう。\nたとえば、フォームが表示されているページでは、フォームをキャッシュしたいのは最初の (GET リクエストによる) リクエストの場合だけです。\nその後の (POST リクエストによる) フォームの表示では、フォームにユーザ入力が含まれている可能性があるため、キャッシュをすべきではありません。\nこれを行うには、以下のように [[yii\\widgets\\FragmentCache::enabled|enabled]] オプションをセットします:\n\n```php\nif ($this->beginCache($id, ['enabled' => Yii::$app->request->isGet])) {\n\n    // ... ここに生成するコンテントを書く ...\n\n    $this->endCache();\n}\n```\n\n\n## キャッシュのネスト <span id=\"nested-caching\"></span>\n\nフラグメント・キャッシュはネストすることができます。つまり、キャッシュされる断片を、それ自体もキャッシュされる別の断片に入れることができます。\n例えば、内側のフラグメント・キャッシュにはコメントがキャッシュされており、外側のフラグメント・キャッシュには記事内容と一緒にコメントもキャッシュされている、という形です。\n以下のコードは 2 つのフラグメント・キャッシュをネストする方法を示すものです。\n\n```php\nif ($this->beginCache($id1)) {\n\n    // ...コンテント生成ロジック...\n\n    if ($this->beginCache($id2, $options2)) {\n\n        // ...コンテント生成ロジック...\n\n        $this->endCache();\n    }\n\n    // ...コンテント生成ロジック...\n\n    $this->endCache();\n}\n```\n\nネストされたキャッシュには、異なるキャッシュ・オプションを設定することができます。\nたとえば、上記の例における内側のキャッシュと外側のキャッシュに対して、異なる持続期間の値を設定する事が可能です。\nこれによって、外側のキャッシュでキャッシュされたデータが無効になった場合でも、内側のキャッシュが有効な内側の断片を提供することが可能になります。\nしかし、その逆は真ではありません。\n外側のキャッシュが有効であると判断された場合には、内側のキャッシュが無効になった後でも、外側のキャッシュが古くなったコンテントのコピーを提供し続けます。\n従って、ネストされたキャッシュの持続時間や依存の設定を間違うと、無効になった内側のキャッシュ・データが外側のキャッシュに残り続けることになるので、注意が必要です。\n\n\n## ダイナミック・コンテント <span id=\"dynamic-content\"></span>\n\nフラグメント・キャッシュを使用する際、出力全体が比較的静的で、一ヶ所ないし数ヶ所だけが例外的に動的であるというような状況に遭遇するでしょう。\n例えば、ページのヘッダがメイン・メニュー・バーと現在のユーザ名を一緒に表示している場合です。\nもう一つの問題は、キャッシュされるコンテントに、リクエスト毎に実行しなければいけない PHP のコード \n(例えば、アセット・バンドルを登録するためのコード) が含まれている場合です。\nこの両方の問題は、いわゆる *ダイナミック・コンテント* 機能によって解決することができます。\n\nダイナミック・コンテントは、それがフラグメント・キャッシュの中に含まれていても、キャッシュすべきではない出力の部分を意味します。\nこのコンテントを常に動的にするためには、外側のコンテントがキャッシュから提供されている場合でも、\nすべてのリクエストに対して、何らかの PHP コードを実行することにより生成しなければいけません。\n\nダイナミック・コンテントを目的の場所に挿入するには、以下のように、キャッシュされる断片内で\n[[yii\\base\\View::renderDynamic()]] を呼び出します。\n\n```php\nif ($this->beginCache($id1)) {\n\n    // ...コンテント生成ロジック...\n\n    echo $this->renderDynamic('return Yii::$app->user->identity->name;');\n\n    // ...コンテント生成ロジック...\n\n    $this->endCache();\n}\n```\n\n[[yii\\base\\View::renderDynamic()|renderDynamic()]] メソッドはパラメータとして PHP コードを取ります。\nこの PHP コードの戻り値が、ダイナミック・コンテントとして扱われます。\n囲んでいる断片がキャッシュから提供されるか否かにかかわらず、同じ PHP コードがすべてのリクエストに対して実行されます。\n\n> Note: バージョン 2.0.14 以降、[[yii\\base\\DynamicContentAwareInterface]] インタフェイスとその [[yii\\base\\DynamicContentAwareTrait]] トレイトによって、ダイナミック・コンテント API が公開されています。.\nその一例としては、 [[yii\\widgets\\FragmentCache]] クラスを参照して下さい。\n"
  },
  {
    "path": "docs/guide-ja/caching-http.md",
    "content": "HTTP キャッシュ\n===============\n\nこれまでのセクションで説明したサーバ・サイドのキャッシュに加えて、ウェブ・アプリケーションは、\n同じページ・コンテントを生成し送信する時間を節約するために、クライアント・サイドでもキャッシュを利用することができます。\n\nクライアント・サイドのキャッシュを使用するには、レンダリング結果をキャッシュできるように、\nコントローラ・アクションのフィルタとして [[yii\\filters\\HttpCache]] を設定します。\n[[yii\\filters\\HttpCache]] は `GET` と `HEAD` リクエストに対してのみ動作し、それらのリクエストに対する 3 種類のキャッシュ関連の HTTP ヘッダを扱うことができます:\n\n* [[yii\\filters\\HttpCache::lastModified|Last-Modified]]\n* [[yii\\filters\\HttpCache::etagSeed|Etag]]\n* [[yii\\filters\\HttpCache::cacheControlHeader|Cache-Control]]\n\n\n## `Last-Modified` ヘッダ <span id=\"last-modified\"></span>\n\n`Last-Modified` ヘッダは、クライアントがキャッシュした時からページが変更されたかどうかを示すために、タイムスタンプを使用しています。\n\n`Last-Modified` ヘッダの送信を有効にするには [[yii\\filters\\HttpCache::lastModified]] プロパティを構成します。\nこのプロパティは、ページの更新時刻に関する UNIX タイムスタンプを返す PHP のコーラブルでなければなりません。\nこの PHP コーラブルのシグニチャは以下のとおりです。\n\n```php\n/**\n * @param Action $action 現在扱っているアクション・オブジェクト\n * @param array $params \"params\" プロパティの値\n * @return int ページの更新時刻を表す UNIX タイムスタンプ\n */\nfunction ($action, $params)\n```\n\n以下は `Last-Modified` ヘッダを使用する例です:\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\HttpCache',\n            'only' => ['index'],\n            'lastModified' => function ($action, $params) {\n                $q = new \\yii\\db\\Query();\n                return $q->from('post')->max('updated_at');\n            },\n        ],\n    ];\n}\n```\n\n上記のコードは `index` アクションでのみ HTTP キャッシュを有効にすべきことを記述しています。\n`Last-Modified` は、投稿の最終更新時刻に基づいて生成される必要があります。\nブラウザが初めて `index` ページにアクセスしたときは、ページはサーバ上で生成されブラウザに送信されます。\nもしブラウザが再度同じページにアクセスし、その期間中に投稿に変更がない場合は、サーバはページを再生成せず、\nブラウザはクライアント・サイドにキャッシュしたものを使用します。\nその結果、サーバ・サイドのレンダリング処理とページ・コンテントの送信は両方ともスキップされます。\n\n\n## `ETag` ヘッダ <span id=\"etag\"></span>\n\n\"Entity Tag\" (略して `ETag`) ヘッダはページ・コンテントを表すためのハッシュです。\nページが変更された場合ハッシュも同様に変更されます。\nサーバ・サイドで生成されたハッシュとクライアント・サイドで保持しているハッシュを比較することによって、ページが変更されたかどうか、そして再送信するべきかどうかを決定します。\n\n`ETag` ヘッダの送信を有効にするには [[yii\\filters\\HttpCache::etagSeed]] プロパティを設定します。\nプロパティは ETag のハッシュを生成するためのシードを返す PHP のコーラブルで、\n以下のようなシグネチャを持たなければなりません。\n\n```php\n/**\n * @param Action $action 現在扱っているアクション・オブジェクト\n * @param array $params \"params\" プロパティの値\n * @return string ETag のハッシュを生成するためのシードとして使用する文字列\n */\nfunction ($action, $params)\n```\n\n以下は `ETag` ヘッダを使用している例です:\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\HttpCache',\n            'only' => ['view'],\n            'etagSeed' => function ($action, $params) {\n                $post = $this->findModel(\\Yii::$app->request->get('id'));\n                return serialize([$post->title, $post->content]);\n            },\n        ],\n    ];\n}\n```\n\n上記のコードは `view` アクションでのみ HTTP キャッシュを有効にすべきことを記述しています。\n`Etag` HTTP ヘッダは、リクエストされた投稿のタイトルとコンテントに基づいて生成されなければなりません。\nブラウザが初めて `view` ページにアクセスしたときは、ページがサーバ上で生成されブラウザに送信されます。\nブラウザが再度同じページにアクセスし、投稿のタイトルやコンテントに変更がない場合には、\nサーバはページを再生成せず、ブラウザはクライアント・サイドにキャッシュしたものを使用します。\nその結果、サーバ・サイドのレンダリング処理とページ・コンテント送信は両方ともスキップされます。\n\nETag は `Last-Modified` ヘッダよりも複雑 かつ/または より正確なキャッシング方式を可能にします。\n例えば、サイトが別のテーマに切り替わった場合には ETag を無効化する、といったことができます。\n\nETag はリクエスト毎に再評価する必要があるため、負荷の高い生成方法を使うと `HttpCache`\nの本来の目的を損なって不必要なオーバーヘッドが生じる場合があります。\nページのコンテントが変更されたときにキャッシュを無効化するための式は単純なものを指定するようにして下さい。\n\n> Note: [RFC 7232](https://datatracker.ietf.org/doc/html/rfc7232#section-2.4) に準拠して\n  `Etag` と `Last-Modified` ヘッダの両方を設定した場合、\n  `HttpCache` はその両方とも送信します。  また、もし `If-None-Match` ヘッダと\n  `If-Modified-Since` ヘッダの両方を送信した場合は前者のみが尊重されます。\n\n\n## `Cache-Control` ヘッダ <span id=\"cache-control\"></span>\n\n`Cache-Control` ヘッダはページのための一般的なキャッシュ・ポリシーを指定します。\n[[yii\\filters\\HttpCache::cacheControlHeader]] プロパティにヘッダの値を設定することで、それを送ることができます。\nデフォルトでは、以下のヘッダが送信されます:\n\n```\nCache-Control: public, max-age=3600\n```\n\n## セッション・キャッシュ・リミッタ<span id=\"session-cache-limiter\"></span>\n\nページでセッションを使用している場合、PHP はいくつかのキャッシュ関連の HTTP ヘッダ\n(PHP の INI 設定ファイル内で指定されている session.cache_limiter など) を自動的に送信します。\nこれらのヘッダが `HttpCache` が実現しようとしているキャッシュ機能を妨害したり無効にしたりすることがあります。\nこの問題を防止するために、`HttpCache` はこれらのヘッダの送信をデフォルトで自動的に無効化します。\nこの動作を変更したい場合は [[yii\\filters\\HttpCache::sessionCacheLimiter]] プロパティを設定します。\nこのプロパティには `public`、`private`、`private_no_expire`、そして `nocache` などの文字列の値を使用することができます。\nこれらの値についての説明は [session_cache_limiter()](https://www.php.net/manual/ja/function.session-cache-limiter.php)\nを参照してください。\n\n\n## SEO への影響 <span id=\"seo-implications\"></span>\n\n検索エンジンのボットはキャッシュ・ヘッダを尊重する傾向があります。\nクローラの中には、一定期間内に処理するドメインごとのページ数に制限を持っているものもあるため、\nキャッシュ・ヘッダを導入して、処理の必要があるページ数を減らしてやると、サイトのインデックスの作成を促進できるかも知れません。\n\n"
  },
  {
    "path": "docs/guide-ja/caching-overview.md",
    "content": "キャッシュ\n==========\n\nキャッシュはウェブ・アプリケーションのパフォーマンスを向上させるための安価で効果的な方法です。\n比較的静的なデータをキャッシュに格納し、要求に応じてキャッシュからそれを取得します。\nこれによって、アプリケーションは、毎回一からデータを生成した場合に必要になるであろう時間を節約することができます。\n\nキャッシュはアプリケーション内のさまざまなレベルと場所で使用することができます。\n例えばサーバ・サイドでの低いレベルでは、データベースから取得した最新の記事情報リストのような\n基本的なデータを格納するためにキャッシュを使用することが出来ます。\nより高いレベルでは、ウェブ・ページの断片または全体、例えば、最新の記事のレンダリング結果を格納するためにキャッシュを使用することが出来ます。\nクライアント・サイドでは、最近訪れたページの内容をブラウザのキャッシュに格納するために、HTTP キャッシュを使用することができます。\n\nYii はこれら全てのキャッシュ機構をサポートしています:\n\n* [データ・キャッシュ](caching-data.md)\n* [フラグメント・キャッシュ](caching-fragment.md)\n* [ページ・キャッシュ](caching-page.md)\n* [HTTP キャッシュ](caching-http.md)\n"
  },
  {
    "path": "docs/guide-ja/caching-page.md",
    "content": "ページ・キャッシュ\n==================\n\nページ・キャッシュはサーバ・サイドでページ全体のコンテントをキャッシュするものです。\n後で再び同じページがリクエストされた場合に、その内容を一から生成するのではなく、キャッシュから提供します。\n\nページ・キャッシュは [[yii\\filters\\PageCache]] という [アクション・フィルタ](structure-filters.md) によってサポートされています。\nこれは、コントローラ・クラスで以下のように使用することができます：\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\PageCache',\n            'only' => ['index'],\n            'duration' => 60,\n            'variations' => [\n                \\Yii::$app->language,\n            ],\n            'dependency' => [\n                'class' => 'yii\\caching\\DbDependency',\n                'sql' => 'SELECT COUNT(*) FROM post',\n            ],\n        ],\n    ];\n}\n```\n\n上記のコードは、ページ・キャッシュが `index` アクションのみで使用されることを示しています。\nページのコンテントは最大 60 秒間キャッシュされ、現在のアプリケーションの言語によるバリエーションを持ち、\n投稿の総数に変化があった場合キャッシュされたページが無効になります。\n\n御覧のように、ページ・キャッシュは [フラグメント・キャッシュ](caching-fragment.md) ととてもよく似ています。\nそれらは両方とも `duration`、`dependencies`、`variations`、そして `enabled` などのオプションをサポートしています。\n主な違いとしては、ページ・キャッシュは [アクション・フィルタ](structure-filters.md) として、フラグメント・キャッシュは [ウィジェット](structure-widgets.md) として実装されているということです。\n\n[フラグメント・キャッシュ](caching-fragment.md) も、[ダイナミック・コンテント](caching-fragment.md#dynamic-content) も、\nページ・キャッシュと併用することができます。\n\n"
  },
  {
    "path": "docs/guide-ja/concept-aliases.md",
    "content": "エイリアス\n==========\n\nファイル・パスや URL を表すのにエイリアスを使用すると、あなたはプロジェクト内で絶対パスや URL をハードコードする必要がなくなります。\nエイリアスは、通常のファイル・パスや URL と区別するために、 `@` 文字で始まる必要があります。\n先頭に `@` を付けずに定義されたエイリアスは、`@` 文字が先頭に追加されます。\n\nYii はすでに利用可能な多くの事前定義エイリアスを持っています。\nたとえば、 `@yii` というエイリアスは Yii フレームワークのインストール・パスを表し、`@web` は現在実行中のウェブ・アプリケーションのベース URL を表します。\n\nエイリアスを定義する <span id=\"defining-aliases\"></span>\n--------------------\n\n[[Yii::setAlias()]] を呼び出すことにより、ファイル・パスまたは URL のエイリアスを定義することができます。\n\n```php\n// ファイル・パスのエイリアス\nYii::setAlias('@foo', '/path/to/foo');\n\n// URL のエイリアス\nYii::setAlias('@bar', 'https://www.example.com');\n\n// \\foo\\Bar クラスを保持する具体的なファイルのエイリアス\nYii::setAlias('@foo/Bar.php', '/definitely/not/foo/Bar.php');\n```\n\n> Note: エイリアスされているファイル・パスや URL は、必ずしも実在するファイルまたはリソースを参照しない場合があります。\n\n定義済みのエイリアスがあれば、スラッシュ `/` に続けて 1 つ以上のパス・セグメントを追加することで（[[Yii::setAlias()]]\nの呼び出しを必要とせずに) 新しいエイリアスを導出することができます。 [[Yii::setAlias()]] を通じて定義されたエイリアスは\n*ルート・エイリアス* となり、それから派生したエイリアスは *派生エイリアス* になります。たとえば、 `@foo` がルート・エイリアスなら、\n`@foo/bar/file.php` は派生エイリアスです。\n\nエイリアスを、他のエイリアス (ルートまたは派生のいずれか) を使用して定義することができます:\n\n```php\nYii::setAlias('@foobar', '@foo/bar');\n```\n\nルート・エイリアスは通常、 [ブートストラップ](runtime-bootstrapping.md) 段階で定義されます。\nたとえば、[エントリ・スクリプト](structure-entry-scripts.md) で [[Yii::setAlias()]] を呼び出すことができます。\n便利なように、 [アプリケーション](structure-applications.md) は、`aliases` という名前の書き込み可能なプロパティを提供しており、\nそれをアプリケーションの [構成情報](concept-configurations.md) で設定することが可能です。\n\n```php\nreturn [\n    // ...\n    'aliases' => [\n        '@foo' => '/path/to/foo',\n        '@bar' => 'https://www.example.com',\n    ],\n];\n```\n\n\nエイリアスを解決する <span id=\"resolving-aliases\"></span>\n--------------------\n\n[[Yii::getAlias()]] を呼び出して、ルート・エイリアスが表すファイル・パスまたは URL を解決することができます。\n同メソッドで、派生エイリアスを対応するファイル・パスまたは URL に解決することもできます。\n\n```php\necho Yii::getAlias('@foo');               // /path/to/foo を表示\necho Yii::getAlias('@bar');               // https://www.example.com を表示\necho Yii::getAlias('@foo/bar/file.php');  // /path/to/foo/bar/file.php を表示\n```\n\n派生エイリアスによって表されるパスや URL は、\n派生エイリアス内のルート・エイリアス部分を対応するパスや URL で置換して決定されます。\n\n> Note: [[Yii::getAlias()]] メソッドは、 結果のパスや URL が実在するファイルやリソースを参照しているかをチェックしません。\n\n\nルート・エイリアス名にはスラッシュ `/` 文字を含むことができます。 [[Yii::getAlias()]] メソッドは、\nエイリアスのどの部分がルート・エイリアスであるかを賢く判別し、\n正確に対応するファイル・パスや URL を決定します:\n\n```php\nYii::setAlias('@foo', '/path/to/foo');\nYii::setAlias('@foo/bar', '/path2/bar');\nYii::getAlias('@foo/test/file.php');  // /path/to/foo/test/file.php を表示\nYii::getAlias('@foo/bar/file.php');   // /path2/bar/file.php を表示\n```\n\nもし `@foo/bar` がルート・エイリアスとして定義されていなければ、最後のステートメントは `/path/to/foo/bar/file.php` を表示します。\n\n\nエイリアスを使用する <span id=\"using-aliases\"></span>\n--------------------\n\nYii では、多くの場所で、パスや URL に変換する [[Yii::getAlias()]] を呼び出す必要なく、\nエイリアスが認識されます。\nたとえば、 [[yii\\caching\\FileCache::cachePath]] は、ファイル・パスとファイル・パスを表すエイリアスの両方を受け入れることが出来ます。\nこれは、接頭辞 `@` によって、ファイル・パスとエイリアスを区別することが出来るためです。\n\n```php\nuse yii\\caching\\FileCache;\n\n$cache = new FileCache([\n    'cachePath' => '@runtime/cache',\n]);\n```\n\nプロパティやメソッドのパラメータがエイリアスをサポートしているかどうかは、API ドキュメントに注意を払ってください。\n\n\n事前定義されたエイリアス <span id=\"predefined-aliases\"></span>\n------------------------\n\nYii では、一般的に使用されるファイルのパスと URL を簡単に参照できるよう、エイリアスのセットが事前に定義されています:\n\n- `@yii`, `BaseYii.php` ファイルがあるディレクトリ (フレームワーク・ディレクトリとも呼ばれます)\n- `@app`, 現在実行中のアプリケーションの [[yii\\base\\Application::basePath|ベース・パス]]\n- `@runtime`, 現在実行中のアプリケーションの [[yii\\base\\Application::runtimePath|ランタイム・パス]] 。デフォルトは `@app/runtime` 。\n- `@webroot`, 現在実行中のウェブ・アプリケーションのウェブ・ルート・ディレクトリ。\n  エントリス・クリプトを含むディレクトリによって決定されます。\n- `@web`, 現在実行中のウェブ・アプリケーションのベース URL。これは、 [[yii\\web\\Request::baseUrl]] と同じ値を持ちます。\n- `@vendor`, [[yii\\base\\Application::vendorPath|Composer のベンダー・ディレクトリ]] 。デフォルトは `@app/vendor` 。\n- `@bower`, [bower パッケージ](https://bower.io/) が含まれるルート・ディレクトリ。デフォルトは `@vendor/bower` 。\n- `@npm`, [npm パッケージ](https://www.npmjs.com/) が含まれるルート・ディレクトリ。デフォルトは `@vendor/npm` 。\n\n`@yii` エイリアスは [エントリ・スクリプト](structure-entry-scripts.md) に `Yii.php` ファイルを読み込んだ時点で定義されます。\nエイリアスの残りの部分は、アプリケーションのコンストラクタ内で、アプリケーションの [構成情報](concept-configurations.md)\nを適用するときに定義されます。\n\n> Note: `@web` と `@webroot` のエイリアスは、その説明が示しているように、[[yii\\web\\Application|ウェブ・アプリケーション]] の中で定義されます。従って、デフォルトでは [[yii\\console\\Application|コンソール・アプリケーション]] では利用できません。\n\nエクステンションのエイリアス <span id=\"extension-aliases\"></span>\n----------------------------\n\nComposer でインストールされる [エクステンション](structure-extensions.md) のそれぞれに対してエイリアスが自動的に定義されます。\n各エイリアスは、その `composer.json` ファイルで宣言されたエクステンションのルート名前空間にちなんで名付けられ、\nパッケージのルート・ディレクトリを表します。たとえば、あなたが `yiisoft/yii2-jui` エクステンションをインストールしたとすると、\n自動的に `@yii/jui` というエイリアスが [ブートストラップ](runtime-bootstrapping.md) 段階で定義されます。これは次のものと等価です。\n\n```php\nYii::setAlias('@yii/jui', 'VendorPath/yiisoft/yii2-jui');\n```\n"
  },
  {
    "path": "docs/guide-ja/concept-autoloading.md",
    "content": "クラスのオートローディング\n==========================\n\nYiiは、必要となるすべてのクラス・ファイルを特定してインクルードするにあたり、\n[クラスのオートローディング・メカニズム](https://www.php.net/manual/ja/language.oop5.autoload.php) を頼りにします。\nYii は、[PSR-4 標準](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md) に準拠した、高性能なクラスのオートローダを提供しています。\nこのオートローダは、あなたが `Yii.php` ファイルをインクルードするときにインストールされます。\n\n> Note: 説明を簡単にするため、このセクションではクラスのオートローディングについてのみ話します。しかし、\n  ここに記述されている内容は、インタフェイスとトレイトのオートローディングにも同様に適用されることに注意してください。\n\n\nYii オートローダを使用する <span id=\"using-yii-autoloader\"></span>\n--------------------------\n\nYii のクラス・オートローダを使用するには、クラスを作成して名前を付けるとき、次の二つの単純なルールに従わなければなりません:\n\n* 各クラスは [名前空間](https://www.php.net/manual/ja/language.namespaces.php) の下になければなりません (例 `foo\\bar\\MyClass`)\n* 各クラスは次のアルゴリズムで決定される個別のファイルに保存されなければなりません:\n\n```php\n// $className は先頭にバック・スラッシュを持たない完全修飾クラス名\n$classFile = Yii::getAlias('@' . str_replace('\\\\', '/', $className) . '.php');\n```\n\nたとえば、クラス名と名前空間が `foo\\bar\\MyClass` であれば、対応するクラス・ファイルのパスの [エイリアス](concept-aliases.md) は、\n`@foo/bar/MyClass.php` になります。このエイリアスがファイル・パスとして解決できるようにするためには、`@foo` または `@foo/bar`\nのどちらかが、 [ルート・エイリアス](concept-aliases.md#defining-aliases) でなければなりません。\n\n[ベーシック・プロジェクト・テンプレート](start-installation.md) を使用している場合、最上位の名前空間 `app` の下にクラスを置くことができ、\nそうすると、新しいエイリアスを定義しなくても、Yii によってそれらをオートロードできるようになります。これは `@app`\nが [事前定義されたエイリアス](concept-aliases.md#predefined-aliases) であるためで、`app\\components\\MyClass` のようなクラス名を\n今説明したアルゴリズムに従って、クラス・ファイル `AppBasePath/components/MyClass.php` であると解決することが出来ます。\n\n[アドバンスト・プロジェクト・テンプレート](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide-ja/README.md) では、各層がそれ自身のルート・エイリアスを持っています。たとえば、\nフロントエンド層はルート・エイリアス `@frontend` を持ち、バックエンド層のルート・エイリアスは `@backend` です。その結果、名前空間 `frontend` の下に\nフロントエンド・クラスを置き、バックエンド・クラスを `backend` の下に置けます。これで、これらのクラスは Yii のオートローダによって\nオートロードできるようになります。\n\n独自の名前空間をオートローダに追加するためには、[[Yii::setAlias()]] を使って、その名前空間のベース・ディレクトリに対するエイリアスを定義する必要があります。\n例えば、`path/to/foo` ディレクトリに配置されている `foo` 名前空間に属するクラスをロードするためには、`Yii::setAlias('@foo', 'path/to/foo') を呼び出します。 \n\nクラス・マップ <span id=\"class-map\"></span>\n--------------\n\nYii のクラス・オートローダは、 *クラス・マップ* 機能をサポートしており、クラス名を対応するクラス・ファイルのパスにマップできます。\nオートローダがクラスをロードするときは、クラスがマップに見つかるかどうかを最初にチェックします。もしあれば、対応する\nファイル・パスは、それ以上チェックされることなく、直接インクルードされます。これでクラスのオートローディングを非常に高速化できます。\n実際のところ、すべての Yii のコア・クラスは、この方法でオートロードされています。\n\n次の方法で、 `Yii::$classMap` に格納されるクラス・マップにクラスを追加できます:\n\n```php\nYii::$classMap['foo\\bar\\MyClass'] = 'path/to/MyClass.php';\n```\n\nクラス・ファイルのパスを指定するのに、 [エイリアス](concept-aliases.md) を使うことができます。クラスが使用される前にマップが準備できるように、\nクラス・マップの設定は [ブートストラップ](runtime-bootstrapping.md) プロセス内でする必要があります。\n\n\n他のオートローダの使用 <span id=\"using-other-autoloaders\"></span>\n-----------------------\n\nYii はパッケージ依存関係マネージャとして Composer を包含しているので、Composer のオートローダもインストールすることをお勧めします。\nあなたが独自のオートローダを持つサードパーティ・ライブラリを使用している場合は、\nそれらもインストールする必要があります。\n\nYii オートローダを他のオートローダと一緒に使うときは、他のすべてのオートローダがインストールされた *後で* 、 `Yii.php`\nファイルをインクルードする必要があります。これで Yii のオートローダが、任意クラスのオートローディング要求に応答する最初のものになります。\nたとえば、次のコードは [ベーシック・プロジェクト・テンプレート](start-installation.md) の\n[エントリ・スクリプト](structure-entry-scripts.md) から抜き出したものです。\n最初の行は、Composer のオートローダをインストールしており、二行目は Yii のオートローダをインストールしています。\n\n```php\nrequire __DIR__ . '/../vendor/autoload.php';\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n```\n\nあなたは Yii のオートローダを使わず、Composer のオートローダだけを単独で使用することもできます。しかし、そうすることによって、\nあなたのクラスのオートローディングのパフォーマンスは低下し、クラスをオートロード可能にするために\nComposer が設定したルールに従わなければならなくなります。\n\n> Info: Yiiのオートローダを使用したくない場合は、`Yii.php` ファイルのあなた独自のバージョンを作成し、\n  それを [エントリ・スクリプト](structure-entry-scripts.md) でインクルードする必要があります。\n\n\nエクステンション・クラスのオートロード <span id=\"autoloading-extension-classes\"></span>\n--------------------------------------\n\nYii のオートローダは、 [エクステンション](structure-extensions.md) クラスのオートロードが可能です。唯一の要件は、\nエクステンションがその `composer.json` ファイルに正しく `autoload` セクションを指定していることです。\n`autoload` の指定方法の詳細については [Composer のドキュメント](https://getcomposer.org/doc/04-schema.md#autoload) 参照してください。\n\nYii のオートローダを使用しない場合でも、まだ Composer のオートローダがエクステンション・クラスをオートロードすることが可能です。\n"
  },
  {
    "path": "docs/guide-ja/concept-behaviors.md",
    "content": "ビヘイビア\n==========\n\nビヘイビアは [[yii\\base\\Behavior]] またその子クラスのインスタンスです。\nビヘイビアは [ミックスイン](https://ja.wikipedia.org/wiki/Mixin) としても知られ、既存の [[yii\\base\\Component|component]] クラスの\n機能を、クラスの継承を変更せずに拡張することができます。コンポーネントにビヘイビアをアタッチすると、その\nコンポーネントにはビヘイビアのメソッドとプロパティが \"注入\" され、それらのメソッドとプロパティは、\nコンポーネント・クラス自体に定義されているかのようにアクセスできるようになります。また、ビヘイビアは、\nコンポーネントによってトリガされた [イベント](concept-events.md) に応答することができるので、ビヘイビアでコンポーネントの通常のコード実行をカスタマイズすることができます。\n\n\nビヘイビアを定義する <span id=\"defining-behaviors\"></span>\n--------------------\n\nビヘイビアを定義するには、 [[yii\\base\\Behavior]] あるいは子クラスを継承するクラスを作成します。たとえば:\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Behavior;\n\nclass MyBehavior extends Behavior\n{\n    public $prop1;\n\n    private $_prop2;\n\n    public function getProp2()\n    {\n        return $this->_prop2;\n    }\n\n    public function setProp2($value)\n    {\n        $this->_prop2 = $value;\n    }\n\n    public function foo()\n    {\n        // ...\n    }\n}\n```\n\n上のコードは、`prop1`、`prop2` という2つのプロパティと `foo()` というメソッドを持つ `app\\components\\MyBehavior` ビヘイビア・クラスを定義します。\n`prop2` プロパティは、 `getProp2()` getter メソッドと `setProp2()` setter メソッドで定義されることに着目してください。\n[[yii\\base\\Behavior]] は [[yii\\base\\BaseObject]] を継承しているので、getter と​​ setter による [プロパティ](concept-properties.md) 定義をサポートします。\n\nこのクラスはビヘイビアなので、コンポーネントにアタッチされると、そのコンポーネントは `prop1` と `prop2` のプロパティと `foo()` メソッドを持つようになります。\n\n> Tip: ビヘイビア内から、[[yii\\base\\Behavior::owner]] プロパティを介して、ビヘイビアをアタッチしたコンポーネントにアクセスすることができます。\n\n> Note: ビヘイビアの [[yii\\base\\Behavior::__get()]] および/または [[yii\\base\\Behavior::__set()]] メソッドをオーバーライドする場合は、\n同時に [[yii\\base\\Behavior::canGetProperty()]] および/または [[yii\\base\\Behavior::canSetProperty()]] もオーバーライドする必要があります。\n\nコンポーネントのイベントを処理する\n----------------------------------\n\nビヘイビアが、アタッチされたコンポーネントがトリガするイベントに応答する必要がある場合は、\n[[yii\\base\\Behavior::events()]] メソッドをオーバーライドしなければなりません。たとえば:\n\n```php\nnamespace app\\components;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\base\\Behavior;\n\nclass MyBehavior extends Behavior\n{\n    // ...\n\n    public function events()\n    {\n        return [\n            ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',\n        ];\n    }\n\n    public function beforeValidate($event)\n    {\n        // ...\n    }\n}\n```\n\n[[yii\\base\\Behavior::events()]] メソッドは、イベントとそれに対応するハンドラのリストを返します。\n上の例では [[yii\\db\\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]] イベントがあること、\nそのハンドラ定義である `beforeValidate()` を宣言しています。イベント・ハンドラを指定するときは、以下の表記方法が使えます:\n\n* ビヘイビア・クラスのメソッド名を参照する文字列 (上の例など)\n* オブジェクトまたはクラス名と文字列のメソッド名 (括弧なし) 例 `[$object, 'methodName']`\n* 無名関数\n\nイベント・ハンドラのシグニチャは次のようにしてください。`$event` はイベントのパラメータを参照します。イベントの詳細については\n[イベント](concept-events.md) セクションを参照してください。\n\n```php\nfunction ($event) {\n}\n```\n\nビヘイビアをアタッチする <span id=\"attaching-behaviors\"></span>\n------------------------\n\n[[yii\\base\\Component|コンポーネント]] へのビヘイビアのアタッチは、静的にも動的にも可能です。実際は、前者のほうがより一般的ですが。\n\nビヘイビアを静的にアタッチするには、ビヘイビアをアタッチしたいコンポーネント・クラスの [[yii\\base\\Component::behaviors()|behaviors()]] メソッドをオーバーライドします。\n[[yii\\base\\Component::behaviors()|behaviors()]] メソッドは、ビヘイビアの [構成](concept-configurations.md) のリストを返さなければなりません。\n各ビヘイビアの構成内容は、ビヘイビアのクラス名でも、構成情報配列でもかまいません。\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\nuse app\\components\\MyBehavior;\n\nclass User extends ActiveRecord\n{\n    public function behaviors()\n    {\n        return [\n            // 無名ビヘイビア ビヘイビア・クラス名のみ\n            MyBehavior::class,\n\n            // 名前付きビヘイビア ビヘイビア・クラス名のみ\n            'myBehavior2' => MyBehavior::class,\n\n            // 無名ビヘイビア 構成情報配列\n            [\n                'class' => MyBehavior::class,\n                'prop1' => 'value1',\n                'prop2' => 'value2',\n            ],\n\n            // 名前付きビヘイビア 構成情報配列\n            'myBehavior4' => [\n                'class' => MyBehavior::class,\n                'prop1' => 'value1',\n                'prop2' => 'value2',\n            ]\n        ];\n    }\n}\n```\n\nビヘイビア構成に対応する配列のキーを指定することによって、ビヘイビアに名前を関連付けることができます。この場合、ビヘイビアは *名前付きビヘイビア* と呼ばれます。上の例では、2つの名前付きビヘイビア​​\n`myBehavior2` と `myBehavior4` があります。ビヘイビアが名前と関連付けられていない場合は、 *無名ビヘイビア* と呼ばれます。\n\n\nビヘイビアを動的にアタッチするには、ビヘイビアがアタッチされるコンポーネントの [[yii\\base\\Component::attachBehavior()]] メソッドを呼びます:\n\n```php\nuse app\\components\\MyBehavior;\n\n// ビヘイビア・オブジェクトをアタッチ\n$component->attachBehavior('myBehavior1', new MyBehavior());\n\n// ビヘイビア・クラスをアタッチ\n$component->attachBehavior('myBehavior2', MyBehavior::class);\n\n// 構成情報配列をアタッチ\n$component->attachBehavior('myBehavior3', [\n    'class' => MyBehavior::class,\n    'prop1' => 'value1',\n    'prop2' => 'value2',\n]);\n```\n\n[[yii\\base\\Component::attachBehaviors()]] メソッドを使うと、いちどに複数のビヘイビアをアタッチできます:\n\n```php\n$component->attachBehaviors([\n    'myBehavior1' => new MyBehavior(), // 名前付きビヘイビア\n    MyBehavior::class,                 // 無名ビヘイビア\n]);\n```\n\n次のように、 [構成情報](concept-configurations.md) を通じてビヘイビアをアタッチすることもできます:\n\n```php\n[\n    'as myBehavior2' => MyBehavior::class,\n\n    'as myBehavior3' => [\n        'class' => MyBehavior::class,\n        'prop1' => 'value1',\n        'prop2' => 'value2',\n    ],\n]\n```\n\n詳しくは [構成情報](concept-configurations.md#configuration-format) \nのセクションを参照してください。\n\nビヘイビアを使用する <span id=\"using-behaviors\"></span>\n--------------------\n\nビヘイビアを使用するには、まず上記の方法に従って [[yii\\base\\Component|コンポーネント]] にアタッチします。ビヘイビアがコンポーネントにアタッチされれば、その使用方法はシンプルです。\n\nあなたは、アタッチされているコンポーネントを介して、ビヘイビアの *パブリック* メンバ変数、\nまたは getter や setter によって定義されたプロパティにアクセスすることができます:\n\n```php\n// \"prop1\" はビヘイビア・クラス内で定義されたプロパティ\necho $component->prop1;\n$component->prop1 = $value;\n```\n\nまた同様に、ビヘイビアの *パブリック*・メソッドも呼ぶことができます:\n\n```php\n// foo() はビヘイビア・クラス内で定義されたパブリック・メソッド\n$component->foo();\n```\n\nご覧のように、 `$component` は `prop1` と `foo()` を定義していないにもかかわらず、\nアタッチされたビヘイビアによって、それらをコンポーネント定義の一部であるかのように使うことができるのです。\n\nもし2つのビヘイビアが同じプロパティやメソッドを定義し、かつ両方とも同じコンポーネントにアタッチされている場合は、\nプロパティやメソッドのアクセス時に、*最初に* コンポーネントにアタッチされたビヘイビアが優先されます。\n\nビヘイビアはコンポーネントにアタッチされるとき、名前と関連付けられているかもしれません。その場合、\nその名前を使用してビヘイビア・オブジェクトにアクセスすることができます:\n\n```php\n$behavior = $component->getBehavior('myBehavior');\n```\n\nまた、コンポーネントにアタッチされた全てのビヘイビアを取得することもできます:\n\n```php\n$behaviors = $component->getBehaviors();\n```\n\n\nビヘイビアをデタッチする <span id=\"detaching-behaviors\"></span>\n------------------------\n\nビヘイビアをデタッチするには、ビヘイビアに付けられた名前とともに [[yii\\base\\Component::detachBehavior()]] を呼び出します:\n\n```php\n$component->detachBehavior('myBehavior1');\n```\n\n*全ての* ビヘイビアをデタッチすることもできます:\n\n```php\n$component->detachBehaviors();\n```\n\n\n`TimestampBehavior` を利用する <span id=\"using-timestamp-behavior\"></span>\n------------------------------\n\nしめくくりに、[[yii\\behaviors\\TimestampBehavior]] を見てみましょう。このビヘイビアは、\n`insert()`、`update()` または `save()` のメソッドを通じて [[yii\\db\\ActiveRecord|アクティブ・レコード]] モデルが保存されるときに、\nタイムスタンプ属性の自動的な更新をサポートします。\n\nまず、使用しようと考えている [[yii\\db\\ActiveRecord|アクティブ・レコード]] クラスに、このビヘイビアをアタッチします:\n\n```php\nnamespace app\\models\\User;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\behaviors\\TimestampBehavior;\n\nclass User extends ActiveRecord\n{\n    // ...\n\n    public function behaviors()\n    {\n        return [\n            [\n                'class' => TimestampBehavior::class,\n                'attributes' => [\n                    ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],\n                    ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'],\n                ],\n                // UNIX タイムスタンプではなく datetime を使う場合は\n                // 'value' => new Expression('NOW()'),\n            ],\n        ];\n    }\n}\n```\n\n上のビヘイビア構成は、レコードが:\n\n* 挿入されるとき、ビヘイビアは現在の UNIX タイムスタンプを\n  `created_at` と `updated_at` 属性に割り当てます\n* 更新されるとき、ビヘイビアは現在の UNIX タイムスタンプを `updated_at` 属性に割り当てます\n\n> Note: 上記の実装が MySQL データベースで動作するようにするためには、`created_at` と `updated_at` のカラムを UNIX タイムスタンプ になるように int(11) として宣言してください。\n\nこのコードが所定の位置にあれば、例えば `User` オブジェクトがあって、それを保存しようとしたら、そこで、\n`created_at` と `updated_at` が自動的に現在の UNIX タイムスタンプで埋められます。\n\n```php\n$user = new User;\n$user->email = 'test@example.com';\n$user->save();\necho $user->created_at;  // 現在のタイムスタンプが表示される\n```\n\n[[yii\\behaviors\\TimestampBehavior|TimestampBehavior]] は、また、指定された属性に現在のタイムスタンプを割り当てて\nそれをデータベースに保存する、便利なメソッド [[yii\\behaviors\\TimestampBehavior::touch()|touch()]]\nを提供しています。\n\n```php\n$user->touch('login_time');\n```\n\nその他のビヘイビア\n------------------\n\nその他にも、内蔵または外部ライブラリによって利用できるビヘイビアがいくつかあります。\n\n- [[yii\\behaviors\\BlameableBehavior]] - 指定された属性に現在のユーザ ID を自動的に設定します。\n- [[yii\\behaviors\\SluggableBehavior]] - 指定された属性に、URL のスラグとして使用できる値を\n  自動的に設定します。\n- [[yii\\behaviors\\AttributeBehavior]] - 特定のイベントが発生したときに、ActiveRecord オブジェクトの一つまたは複数の属性に、\n  指定された値を自動的に設定します。\n- [yii2tech\\ar\\softdelete\\SoftDeleteBehavior](https://github.com/yii2tech/ar-softdelete) - ActiveRecord をソフト・デリートおよびソフト・リストアするメソッド、\n  すなわち、レコードの削除を示すフラグまたはステータスを設定するメソッドを提供します。\n- [yii2tech\\ar\\position\\PositionBehavior](https://github.com/yii2tech/ar-position) - レコードの順序を整数のフィールドによって管理することが出来るように、\n  順序変更メソッドを提供します。\n\nビヘイビアとトレイトの比較 <span id=\"comparison-with-traits\"></span>\n--------------------------\n\nビヘイビアは、主となるクラスにそのプロパティやメソッドを「注入する」という点で [トレイト](https://www.php.net/manual/ja/language.oop5.traits.php)\nに似ていますが、これらは多くの面で異なります。以下に説明するように、それらは互いに長所と短所を持っています。\nそれらは代替手段というよりも、むしろ相互補完関係のようなものです。\n\n\n### ビヘイビアを使う理由 <span id=\"pros-for-behaviors\"></span>\n\nビヘイビアは通常のクラスのように、継承をサポートしています。いっぽうトレイトは、\n言語サポートされたコピー&ペーストとみなすことができます。トレイトは継承をサポートしません。\n\nビヘイビアは、コンポーネント・クラスの変更を必要とせず、コンポーネントに動的にアタッチまたはデタッチすることが可能です。\nトレイトを使用するには、トレイトを使うクラスのコードを書き換える必要があります。\n\nビヘイビアは構成可能ですがトレイトは不可能です。\n\nビヘイビアは、イベントに応答することで、コンポーネントのコード実行をカスタマイズできます。\n\n同じコンポーネントにアタッチされた異なるビヘイビア間で名前の競合がある場合、その競合は自動的に、\n先にコンポーネントにアタッチされたものを優先することで解消されます。\n異なるトレイトによって引き起こされる名前競合の場合は、\n影響を受けるプロパティやメソッドの名前変更による、手動での解決が必要です。\n\n\n### トレイトを使う理由 <span id=\"pros-for-traits\"></span>\n\nビヘイビアは時間もメモリも食うオブジェクトなので、トレイトはビヘイビアよりはるかに効率的です。\n\nトレイトはネイティブな言語構造であるため、IDE との相性に優れています。\n\n"
  },
  {
    "path": "docs/guide-ja/concept-components.md",
    "content": "コンポーネント\n==============\n\nコンポーネントは、Yiiアプリケーションの主要な構成ブロックです。コンポーネントは [[yii\\base\\Component]] 、\nまたはその派生クラスのインスタンスです。コンポーネントが他のクラスに提供する主な機能は次の 3 つです:\n\n* [プロパティ](concept-properties.md)\n* [イベント](concept-events.md)\n* [ビヘイビア](concept-behaviors.md)\n\n個々にでも、組み合わせでも、これらの機能は Yii のクラスのカスタマイズ性と使いやすさをとても高めてくれます。\nたとえば、ユーザ・インタフェイス·コンポーネントである [[yii\\jui\\DatePicker|デイト・ピッカー]] は、\n[ビュー](structure-views.md) で次のように使用して、対話型の日付選択 UI を生成することができます:\n\n```php\nuse yii\\jui\\DatePicker;\n\necho DatePicker::widget([\n    'language' => 'ja',\n    'name'  => 'country',\n    'clientOptions' => [\n        'dateFormat' => 'yy-mm-dd',\n    ],\n]);\n```\n\nクラスが [[yii\\base\\Component]] を継承しているおかげで、ウィジェットのプロパティは簡単に記述できます。\n\nコンポーネントは非常に強力ですが、 [イベント](concept-events.md) と [ビヘイビア](concept-behaviors.md) をサポートするため、\n余分にメモリと CPU 時間を要し、通常のオブジェクトよりも少し重くなります。\nあなたのコンポーネントがこれら2つの機能を必要としない場合、[[yii\\base\\Component]] の代わりに、\n[[yii\\base\\BaseObject]] からコンポーネント・クラスを派生することを検討してもよいでしょう。\nそうすることで、あなたのコンポーネントは、 [プロパティ](concept-properties.md) のサポートが維持されたまま、通常の PHP オブジェクトのように効率的になります。\n\n[[yii\\base\\Component]] または [[yii\\base\\BaseObject]] からクラスを派生するときは、\n次の規約に従うことが推奨されます:\n\n- コンストラクタをオーバーライドする場合は、コンストラクタの *最後の* パラメータとして `$config` パラメータを指定し、\n  親のコンストラクタにこのパラメータを渡すこと。\n- 自分がオーバーライドしたコンストラクタの *最後で* 、必ず親クラスのコンストラクタを呼び出すこと。\n- [[yii\\base\\BaseObject::init()]] メソッドをオーバーライドする場合は、自分の `init()` メソッドの *最初に* 、必ず `init()` の親実装を呼び出すようにすること。\n\n例えば、\n\n```php\n<?php\n\nnamespace yii\\components\\MyClass;\n\nuse yii\\base\\BaseObject;\n\nclass MyClass extends BaseObject\n{\n    public $prop1;\n    public $prop2;\n\n    public function __construct($param1, $param2, $config = [])\n    {\n        // ... 構成前の初期化\n\n        parent::__construct($config);\n    }\n\n    public function init()\n    {\n        parent::init();\n\n        // ... 構成後の初期化\n    }\n}\n```\n\nこのガイドラインに従うことで、あなたのコンポーネントは生成時に [コンフィグ可能](concept-configurations.md) になります。例えば、\n\n```php\n$component = new MyClass(1, 2, ['prop1' => 3, 'prop2' => 4]);\n// あるいは、また\n$component = \\Yii::createObject([\n    'class' => MyClass::class,\n    'prop1' => 3,\n    'prop2' => 4,\n], [1, 2]);\n```\n\n> Note: [[Yii::createObject()]] を呼び出すアプローチは複雑に見えますが、より強力です。\n> というのも、それが [依存性注入コンテナ](concept-di-container.md) 上に実装されているからです。\n  \n\n[[yii\\base\\BaseObject]] クラスには、次のオブジェクト・ライフサイクルが適用されます:\n\n1. コンストラクタ内の事前初期化。ここでデフォルトのプロパティ値を設定することができます。\n2. `$config` によるオブジェクトの構成。構成情報は、コンストラクタ内で設定されたデフォルト値を上書きすることがあります。\n3. [[yii\\base\\BaseObject::init()|init()]] 内の事後初期化。サニティ・チェックやプロパティの正規化を行いたいときは、このメソッドをオーバーライドします。\n4. オブジェクトのメソッド呼び出し。\n\n最初の 3 つのステップは、すべて、オブジェクトのコンストラクタ内で発生します。これは、あなたがクラス・インスタンス (つまり、オブジェクト) を得たときには、\nすでにそのオブジェクトが適切な、信頼性の高い状態に初期化されていることを意味します。\n"
  },
  {
    "path": "docs/guide-ja/concept-configurations.md",
    "content": "構成情報\n==============\n\n新しいオブジェクトを作成したり、既存のオブジェクトを初期化するとき、Yii では構成情報が広く使用されています。\n構成情報は通常、作成されるオブジェクトのクラス名、およびオブジェクトの [プロパティ](concept-properties.md)\nに割り当てられる初期値のリストを含みます。\n構成情報は、オブジェクトの [イベント](concept-events.md) にアタッチされるハンドラのリストや、オブジェクトにアタッチされる\n[ビヘイビア](concept-behaviors.md) のリストを含むこともできます。\n\n以下では、データベース接続を作成して初期化するために、構成情報が使用されています:\n\n```php\n$config = [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n];\n\n$db = Yii::createObject($config);\n```\n\n[[Yii::createObject()]] メソッドは引数に構成情報の配列を受け取り、構成情報で名前指定されたクラスをインスタンス化してオブジェクトを作成します。\nオブジェクトがインスタンス化されるとき、構成情報の残りの部分を使って、\nオブジェクトのプロパティ、イベント・ハンドラ、およびビヘイビアが初期化されます。\n\nすでにオブジェクトがある場合は、構成情報配列でオブジェクトのプロパティを初期化するのに [[Yii::configure()]]\nを使用することができます:\n\n```php\nYii::configure($object, $config);\n```\n\nなお、この場合には、構成情報配列に `class` 要素を含んではいけません。\n\n\n## 構成情報の形式 <span id=\"configuration-format\"></span>\n\n構成情報の形式は、フォーマルには次のように説明できます:\n\n```php\n[\n    'class' => 'ClassName',\n    'propertyName' => 'propertyValue',\n    'on eventName' => $eventHandler,\n    'as behaviorName' => $behaviorConfig,\n]\n```\n\nここで\n\n* `class` 要素は、作成されるオブジェクトの完全修飾クラス名を指定します。\n* `propertyName` 要素は、名前で指定されたプロパティの初期値を指定します。\n  キーはプロパティ名で、値はそれに対応する初期値です。\n  パブリック・メンバ変数と getter/setter によって定義されている [プロパティ](concept-properties.md) のみを設定することができます。\n* `on eventName` 要素は、どのようなハンドラがオブジェクトの [イベント](concept-events.md) にアタッチされるかを指定します。\n  配列のキーが `on` に続けてイベント名という書式になることに注意してください。サポートされているイベント・ハンドラの形式については、\n  [イベント](concept-events.md) のセクションを参照してください。\n* `as behaviorName` 要素は、どのような [ビヘイビア](concept-behaviors.md) がオブジェクトにアタッチされるかを指定します。\n  配列のキーが `as` に続けてビヘイビア名という書式になり、`$behaviorConfig` で示される値が、ここで説明する一般的な構成情報のような、\n  ビヘイビアを作成するための構成情報になることに注意してください。\n\n下記は、初期プロパティ値、イベント・ハンドラ、およびビヘイビアでの構成を示した例です:\n\n```php\n[\n    'class' => 'app\\components\\SearchEngine',\n    'apiKey' => 'xxxxxxxx',\n    'on search' => function ($event) {\n        Yii::info(\"Keyword searched: \" . $event->keyword);\n    },\n    'as indexer' => [\n        'class' => 'app\\components\\IndexerBehavior',\n        // ... プロパティ初期値 ...\n    ],\n]\n```\n\n\n## 構成情報を使用する <span id=\"using-configurations\"></span>\n\n構成情報は Yii の多くの場所で使用されています。このセクションの冒頭では、 [[Yii::createObject()]]\nを使って、構成情報に応じてオブジェクトを作成する方法を示しました。この項では、\nアプリケーションの構成とウィジェットの構成という、二つの主要な構成情報の用途を説明します。\n\n\n### アプリケーションの構成 <span id=\"application-configurations\"></span>\n\n[アプリケーション](structure-applications.md) の構成情報は、おそらく Yii の中で最も複雑な配列のひとつです。\nそれは [[yii\\web\\Application|アプリケーション]] クラスが、設定可能なプロパティとイベントを数多く持つためです。\nさらに重要なことは、その [[yii\\web\\Application::components|components]] プロパティが、アプリケーションに登録されている\nコンポーネントの生成用の構成情報配列を受け取ることができることです。以下は、 [ベーシック・プロジェクト・テンプレート](start-basic.md)\nのアプリケーション構成ファイルの概要です。\n\n```php\n$config = [\n    'id' => 'basic',\n    'basePath' => dirname(__DIR__),\n    'extensions' => require __DIR__ . '/../vendor/yiisoft/extensions.php',\n    'components' => [\n        'cache' => [\n            'class' => 'yii\\caching\\FileCache',\n        ],\n        'mailer' => [\n            'class' => 'yii\\symfonymailer\\Mailer',\n        ],\n        'log' => [\n            'class' => 'yii\\log\\Dispatcher',\n            'traceLevel' => YII_DEBUG ? 3 : 0,\n            'targets' => [\n                [\n                    'class' => 'yii\\log\\FileTarget',\n                ],\n            ],\n        ],\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=stay2',\n            'username' => 'root',\n            'password' => '',\n            'charset' => 'utf8',\n        ],\n    ],\n];\n```\n\nこの構成情報には、 `class` キーがありません。それは、[エントリ・スクリプト](structure-entry-scripts.md) で以下のように、\nクラス名が既に与えられて使用されているためです。\n\n```php\n(new yii\\web\\Application($config))->run();\n```\n\nアプリケーションの `components` プロパティ構成の詳細については、 [アプリケーション](structure-applications.md) のセクションと\n[サービス・ロケータ](concept-service-locator.md) のセクションにあります。\n\nバージョン 2.0.11 以降では、アプリケーション構成で `container` プロパティを使って\n[依存注入コンテナ](concept-di-container.md) を構成することがサポートされています。例えば、\n\n```php\n$config = [\n    'id' => 'basic',\n    'basePath' => dirname(__DIR__),\n    'extensions' => require __DIR__ . '/../vendor/yiisoft/extensions.php',\n    'container' => [\n        'definitions' => [\n            'yii\\widgets\\LinkPager' => ['maxButtonCount' => 5]\n        ],\n        'singletons' => [\n            // 依存注入コンテナのシングルトンの構成\n        ]\n    ]\n];\n```\n\n`definitions` と `singletons` の構成情報配列に使用できる値とその実例についてさらに知るためには、\n[依存注入コンテナ](concept-di-container.md) の記事の [高度な実際の使用方法](concept-di-container.md#advanced-practical-usage)\nのセクションを読んでください。\n\n### ウィジェットの構成 <span id=\"widget-configurations\"></span>\n\n[ウィジェット](structure-widgets.md) を使用するときは、多くの場合、ウィジェットのプロパティをカスタマイズするために、構成情報を使用する必要があります。\n[[yii\\base\\Widget::widget()]] と [[yii\\base\\Widget::begin()]] の両メソッドを使って、ウィジェットを作成できます。\nそれらは、以下のような構成情報配列を取ります。\n\n```php\nuse yii\\widgets\\Menu;\n\necho Menu::widget([\n    'activateItems' => false,\n    'items' => [\n        ['label' => 'ホーム', 'url' => ['site/index']],\n        ['label' => '製品', 'url' => ['product/index']],\n        ['label' => 'ログイン', 'url' => ['site/login'], 'visible' => Yii::$app->user->isGuest],\n    ],\n]);\n```\n\n上記のコードは、 `Menu` ウィジェットを作成し、その `activateItems` プロパティが `false` になるよう初期化します。\n`items` プロパティも、表示されるメニュー項目で構成されます。\n\nクラス名がすでに与えられているので、構成情報配列が `class` キーを持つべきではないことに注意してください。\n\n\n## 構成情報ファイル <span id=\"configuration-files\"></span>\n\n構成情報がとても複雑になる場合、一般的な方法は、 *構成情報ファイル* と呼ばれる、ひとつまたは複数の PHP ファイルにそれを格納することです。\n構成情報ファイルは、構成情報を表す PHP 配列を返します。\nたとえば、次のように、 `web.php` と名づけたファイルにアプリケーションの構成情報を保持することができます。\n\n```php\nreturn [\n    'id' => 'basic',\n    'basePath' => dirname(__DIR__),\n    'extensions' => require __DIR__ . '/../vendor/yiisoft/extensions.php',\n    'components' => require __DIR__ . '/components.php',\n];\n```\n\n`components` の構成情報もまた複雑になるため、上記のように、 `components.php` と呼ぶ別のファイルにそれを格納し `web.php` でそのファイルを \"require\" しています。\nこの `components.php` の内容は、次のようになっています。\n\n```php\nreturn [\n    'cache' => [\n        'class' => 'yii\\caching\\FileCache',\n    ],\n    'mailer' => [\n        'class' => 'yii\\symfonymailer\\Mailer',\n    ],\n    'log' => [\n        'class' => 'yii\\log\\Dispatcher',\n        'traceLevel' => YII_DEBUG ? 3 : 0,\n        'targets' => [\n            [\n                'class' => 'yii\\log\\FileTarget',\n            ],\n        ],\n    ],\n    'db' => [\n        'class' => 'yii\\db\\Connection',\n        'dsn' => 'mysql:host=localhost;dbname=stay2',\n        'username' => 'root',\n        'password' => '',\n        'charset' => 'utf8',\n    ],\n];\n```\n\n構成情報ファイルに格納されている構成情報を取得するには、以下のように、それを \"require\" するだけです:\n\n```php\n$config = require 'path/to/web.php';\n(new yii\\web\\Application($config))->run();\n```\n\n\n## デフォルト設定 <span id=\"default-configurations\"></span>\n\n[[Yii::createObject()]] メソッドは、 [依存性注入コンテナ](concept-di-container.md) をベースに実装されています。\nそのため、指定されたクラスが [[Yii::createObject()]] を使用して作成されるとき、そのすべてのインスタンスに適用される、\nいわゆる *デフォルト設定* のセットを指定することができます。デフォルト設定は、\n[ブートストラップ](runtime-bootstrapping.md) 段階のコード内で `Yii::$container->set()` を呼び出すことで指定することができます。\n\nたとえばあなたが、すべてのリンク・ページャが最大で5つのページ・ボタン (デフォルト値は10) を伴って表示されるよう\n[[yii\\widgets\\LinkPager]] をカスタマイズしたいとき、その目標を達成するには次のコードを使用することができます。\n\n```php\n\\Yii::$container->set('yii\\widgets\\LinkPager', [\n    'maxButtonCount' => 5,\n]);\n```\n\nデフォルト設定を使用しなければ、あなたは、リンク・ページャを使うすべての箇所で\n`maxButtonCount` を設定しなければなりません。\n\n\n## 環境定数 <span id=\"environment-constants\"></span>\n\n構成情報は、多くの場合、アプリケーションが実行される環境に応じて変化します。たとえば、\n開発環境では `mydb_dev` という名前のデータベースを使用し、本番サーバ上では `mydb_prod` データベースを\n使用したいかもしれません。環境の切り替えを容易にするために、Yii は、あなたのアプリケーションの\n[エントリ・スクリプト](structure-entry-scripts.md) で定義可能な `YII_ENV` という名前の定数を提供します。\nたとえば:\n\n```php\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n```\n\n`YII_ENV` を次のいずれかの値と定義することができます:\n\n- `prod`: 本番環境。定数 `YII_ENV_PROD` が `true` と評価されます。\n  とくに定義しない場合、これが `YII_ENV` のデフォルト値です。\n- `dev`: 開発環境。定数 `YII_ENV_DEV` が `true` と評価されます。\n- `test`: テスト環境。定数 `YII_ENV_TEST` が `true` と評価されます。\n\nこれらの環境定数を使用すると、現在の環境に基づいて条件付きで構成情報を指定することもできます。\nたとえば、アプリケーション構成情報には、開発環境での [デバッグ・ツールバーとデバッガ](tool-debugger.md)\nを有効にするために、次のコードを含むことができます。\n\n```php\n$config = [...];\n\nif (YII_ENV_DEV) {\n    // 'dev' 環境用に構成情報を調整\n    $config['bootstrap'][] = 'debug';\n    $config['modules']['debug'] = 'yii\\debug\\Module';\n}\n\nreturn $config;\n```\n"
  },
  {
    "path": "docs/guide-ja/concept-di-container.md",
    "content": "依存注入コンテナ\n================\n\n依存注入 (DI) コンテナは、オブジェクトとそれが依存するすべてのオブジェクトを、インスタンス化し、設定する方法を知っているオブジェクトです。\nなぜ DI コンテナが便利なのかは、[Martin Fowler の記事](https://martinfowler.com/articles/injection.html) の説明がわかりやすいでしょう。\nここでは、主に Yii の提供する DI コンテナの使用方法を説明します。\n\n\n依存注入 <span id=\"dependency-injection\"></span>\n--------\n\nYii は [[yii\\di\\Container]] クラスを通して DI コンテナの機能を提供します。\nこれは、次の種類の依存注入をサポートしています:\n\n* コンストラクタ・インジェクション\n* メソッド・インジェクション\n* セッター/プロパティ・インジェクション\n* PHP コーラブル・インジェクション\n\n\n### コンストラクタ・インジェクション <span id=\"constructor-injection\"></span>\n\nDI コンテナは、コンストラクタ引数の型ヒントの助けを借りて、コンストラクタ・インジェクションをサポートしています。\nコンテナが新しいオブジェクトの作成に使用されるさい、そのオブジェクトがどういうクラスやインタフェイスに依存しているかを、型ヒントがコンテナに教えます。\nコンテナは、依存するクラスやインタフェイスのインスタンスを取得して、\nコンストラクタを通して、新しいオブジェクトにそれらを注入しようと試みます。たとえば\n\n```php\nclass Foo\n{\n    public function __construct(Bar $bar)\n    {\n    }\n}\n\n$foo = $container->get('Foo');\n// これは下記と等価:\n$bar = new Bar;\n$foo = new Foo($bar);\n```\n\n\n### メソッド・インジェクション <span id=\"method-injection\"></span>\n\n通常、クラスの依存はコンストラクタに渡されて、そのクラスの内部でライフサイクル全体にわたって利用可能になります。\nメソッド・インジェクションを使うと、クラスのメソッドの一つだけに必要となる依存、例えば、コンストラクタに渡すことが不可能であったり、\n大半のユース・ケースにおいてはオーバーヘッドが大きすぎるような依存を提供することが可能になります。\n\nクラス・メソッドを次の例の `doSomething` メソッドのように定義することが出来ます。\n\n```php\nclass MyClass extends \\yii\\base\\Component\n{\n    public function __construct(/* 軽量の依存はここに */, $config = [])\n    {\n        // ...\n    }\n\n    public function doSomething($param1, \\my\\heavy\\Dependency $something)\n    {\n        // $something を使って何かをする\n    }\n}\n```\n\nこのメソッドを呼ぶためには、あなた自身で `\\my\\heavy\\Dependency` のインスタンスを渡すか、または、次のように [[yii\\di\\Container::invoke()]] を使います。\n\n```php\n$obj = new MyClass(/*...*/);\nYii::$container->invoke([$obj, 'doSomething'], ['param1' => 42]); // $something は DI コンテナによって提供される\n```\n\n### セッター/プロパティ・インジェクション <span id=\"setter-and-property-injection\"></span>\n\nセッター/プロパティ・インジェクションは、[構成情報](concept-configurations.md) を通してサポートされます。\n依存を登録するときや、新しいオブジェクトを作成するときに、対応するセッターまたはプロパティを通しての依存注入に使用される構成情報を、\nコンテナに提供することが出来ます。\nたとえば\n\n```php\nuse yii\\base\\BaseObject;\n\nclass Foo extends BaseObject\n{\n    public $bar;\n\n    private $_qux;\n\n    public function getQux()\n    {\n        return $this->_qux;\n    }\n\n    public function setQux(Qux $qux)\n    {\n        $this->_qux = $qux;\n    }\n}\n\n$container->get('Foo', [], [\n    'bar' => $container->get('Bar'),\n    'qux' => $container->get('Qux'),\n]);\n```\n\n> Info: [[yii\\di\\Container::get()]] メソッドは三番目のパラメータを、生成されるオブジェクトに適用されるべき構成情報配列として受け取ります。\n  クラスが [[yii\\base\\Configurable]] インタフェイスを実装している場合 (例えば、クラスが [[yii\\base\\BaseObject]] である場合) には、\n  この構成情報配列がクラスのコンストラクタの最後のパラメータとして渡されます。\n  そうでない場合は、構成情報はオブジェクトが生成された *後で* 適用されることになります。\n\n\n### PHP コーラブル・インジェクション <span id=\"php-callable-injection\"></span>\n\nこの場合、コンテナは、登録された PHP のコーラブルを使用して、クラスの新しいインスタンスを構築します。\n[[yii\\di\\Container::get()]] が呼ばれるたびに、対応するコーラブルが起動されます。\nこのコーラブルが、依存を解決し、新しく作成されたオブジェクトに適切に依存を注入する役目を果たします。\nたとえば\n\n```php\n$container->set('Foo', function ($container, $params, $config) {\n    $foo = new Foo(new Bar);\n    // ... その他の初期化 ...\n    return $foo;\n});\n\n$foo = $container->get('Foo');\n```\n\n新しいオブジェクトを構築するための複雑なロジックを隠蔽するために、スタティックなクラスメソッドをコーラブルとして使うことが出来ます。例えば、\n\n```php\nclass FooBuilder\n{\n    public static function build($container, $params, $config)\n    {\n        $foo = new Foo(new Bar);\n        // ... その他の初期化 ...\n        return $foo;\n    }\n}\n\n$container->set('Foo', ['app\\helper\\FooBuilder', 'build']);\n\n$foo = $container->get('Foo');\n```\n\nこのようにすれば、`Foo` クラスを構成しようとする人は、`Foo` がどのように構築されるかを気にする必要はもうなくなります。\n\n\n依存を登録する <span id=\"registering-dependencies\"></span>\n--------------\n\n[[yii\\di\\Container::set()]] を使って依存を登録することができます。登録には依存の名前だけでなく、依存の定義が必要です。\n依存の名前は、クラス名、インタフェイス名、エイリアス名を指定することができます。\n依存の定義には、クラス名、構成情報配列、PHPのコーラブルを指定できます。\n\n```php\n$container = new \\yii\\di\\Container;\n\n// クラス名そのままの登録。これは省略可能です。\n$container->set('yii\\db\\Connection');\n\n// インタフェイスの登録\n// クラスがインタフェイスに依存する場合、対応するクラスが\n// 依存オブジェクトとしてインスタンス化されます\n$container->set('yii\\mail\\MailInterface', 'yii\\swiftmailer\\Mailer');\n\n// エイリアス名の登録。$container->get('foo') を使って\n// Connection のインスタンスを作成できます\n$container->set('foo', 'yii\\db\\Connection');\n\n// `Instance::of` を使ってエイリアスの登録。\n$container->set('bar', Instance::of('foo'));\n\n// 構成情報をともなうクラスの登録。クラスが get() でインスタンス化\n// されるとき構成情報が適用されます\n$container->set('yii\\db\\Connection', [\n    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n]);\n\n// クラスの構成情報をともなうエイリアス名の登録\n// この場合、クラスを指定する \"class\" または \"__class\" 要素が必要です\n$container->set('db', [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n]);\n\n// コーラブルなクロージャまたは配列の登録\n// このコーラブルは $container->get('db') が呼ばれるたびに実行されます\n$container->set('db', function ($container, $params, $config) {\n    return new \\yii\\db\\Connection($config);\n});\n$container->set('db', ['app\\db\\DbFactory', 'create']);\n\n// コンポーネント・インスタンスの登録\n// $container->get('pageCache') は呼ばれるたびに毎回同じインスタンスを返します\n$container->set('pageCache', new FileCache);\n```\n\n> Note: 依存の名前が対応する依存の定義と同じである場合は、\nそれを DI コンテナに登録する必要はありません。\n\n`set()` を介して登録された依存は、依存が必要とされるたびにインスタンスを生成します。\n[[yii\\di\\Container::setSingleton()]] を使うと、\n単一のインスタンスしか生成しない依存を登録することができます:\n\n```php\n$container->setSingleton('yii\\db\\Connection', [\n    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n]);\n```\n\n依存を解決する <span id=\"resolving-dependencies\"></span>\n--------------\n\n依存を登録すると、新しいオブジェクトを作成するのに DI コンテナを使用することができます。\nそして、コンテナが自動的に依存をインスタンス化し、新しく作成されたオブジェクトに注入して、\n依存を解決します。依存の解決は再帰的に行われます。つまり、ある依存が他の依存を持っている場合、\nそれらの依存も自動的に解決されます。\n\n[[yii\\di\\Container::get()|get()]] を使って、オブジェクトのインスタンスを作成または取得することができます。\nこのメソッドは依存の名前を引数として取りますが、依存の名前は、クラス名、インタフェイス名、あるいは、エイリアス名で指定できます。\n依存の名前は、 [[yii\\di\\Container::set()|set()]] を介して登録されていることもあれば、\n[[yii\\di\\Container::setSingleton()|setSingleton()]] を介して登録されていることもあります。\nオプションで、クラスのコンストラクタのパラメータのリストや、[設定情報](concept-configurations.md) を渡して、新しく作成されるオブジェクトを構成することも出来ます。\n\nたとえば、\n\n```php\n// \"db\" は事前に登録されたエイリアス名\n$db = $container->get('db');\n\n// これと同じ意味: $engine = new \\app\\components\\SearchEngine($apiKey, $apiSecret, ['type' => 1]);\n$engine = $container->get('app\\components\\SearchEngine', [$apiKey, $apiSecret], ['type' => 1]);\n```\n\n見えないところで、DIコンテナは、単に新しいオブジェクトを作成するよりもはるかに多くの作業を行います。\nコンテナは、最初にクラスのコンストラクタを調査し、依存するクラスまたはインタフェイスの名前を見つけると、\n自動的にそれらの依存を再帰的に解決します。\n\n次のコードでより洗練された例を示します。`UserLister` クラスは `UserFinderInterface`\nインタフェイスを実装するオブジェクトに依存します。`UserFinder` クラスはこのインタフェイスを実装していて、かつ、\n`Connection` オブジェクトに依存します。これらのすべての依存は、クラスのコンストラクタのパラメータの型ヒントによって宣言されています。\n依存の登録が適切にされていれば、DI コンテナは自動的にこれらの依存を解決し、単純に `get('userLister')`\nを呼び出すだけで新しい `UserLister` インスタンスを作成できます。\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\BaseObject;\nuse yii\\db\\Connection;\nuse yii\\di\\Container;\n\ninterface UserFinderInterface\n{\n    function findUser();\n}\n\nclass UserFinder extends BaseObject implements UserFinderInterface\n{\n    public $db;\n\n    public function __construct(Connection $db, $config = [])\n    {\n        $this->db = $db;\n        parent::__construct($config);\n    }\n\n    public function findUser()\n    {\n    }\n}\n\nclass UserLister extends BaseObject\n{\n    public $finder;\n\n    public function __construct(UserFinderInterface $finder, $config = [])\n    {\n        $this->finder = $finder;\n        parent::__construct($config);\n    }\n}\n\n$container = new Container;\n$container->set('yii\\db\\Connection', [\n    'dsn' => '...',\n]);\n$container->set('app\\models\\UserFinderInterface', [\n    'class' => 'app\\models\\UserFinder',\n]);\n$container->set('userLister', 'app\\models\\UserLister');\n\n$lister = $container->get('userLister');\n\n// と、いうのはこれと同じ:\n\n$db = new \\yii\\db\\Connection(['dsn' => '...']);\n$finder = new UserFinder($db);\n$lister = new UserLister($finder);\n```\n\n\n実際の使用方法 <span id=\"practical-usage\"></span>\n--------------\n\nあなたのアプリケーションの [エントリ・スクリプト](structure-entry-scripts.md) で `Yii.php` ファイルをインクルードするとき、\nYii は DI コンテナを作成します。この DI コンテナは [[Yii::$container]] を介してアクセス可能です。 [[Yii::createObject()]] を呼び出したとき、\nこのメソッドは実際にはコンテナの [[yii\\di\\Container::get()|get()]] メソッドを呼び出して新しいオブジェクトを作成します。\n前述のとおり、DI コンテナは(もしあれば)自動的に依存を解決し、取得されたオブジェクトにそれらを注入します。\nYii は、新しいオブジェクトを作成するコアコードのほとんどにおいて [[Yii::createObject()]] を使用しています。このことは、\n[[Yii::$container]] を操作することでグローバルにオブジェクトをカスタマイズすることができるということを意味しています。\n\n例として、 [[yii\\widgets\\LinkPager]] のページ・ネーションボタンのデフォルト個数をグローバルにカスタマイズしてみましょう。\n\n```php\n\\Yii::$container->set('yii\\widgets\\LinkPager', ['maxButtonCount' => 5]);\n```\n\nそして、次のコードでビューでウィジェットを使用すれば、`maxButtonCount` プロパティは、\nクラスで定義されているデフォルト値 10 の代わりに 5 で初期化されます。\n\n```php\necho \\yii\\widgets\\LinkPager::widget();\n```\n\nただし、DI コンテナを経由して設定された値を上書きすることは、まだ可能です:\n\n```php\necho \\yii\\widgets\\LinkPager::widget(['maxButtonCount' => 20]);\n```\n\n> Tip: ウィジェットの呼び出しで与えられたプロパティは常に DI コンテナが持つ定義を上書きします。\n> たとえ、`'options' => ['id' => 'mypager']` のように配列を指定したとしても、\n> それらは他のオプションとマージされるのでなく、他のオプションを置換えてしまいます。\n\nもう一つの例は、DI コンテナの自動コンストラクタ・インジェクションの利点を活かすものです。\nあなたのコントローラ・クラスが、ホテル予約サービスのような、いくつかの他のオブジェクトに依存するとします。\nあなたは、コンストラクタのパラメータを通して依存を宣言して、DI コンテナにそれを解決させることができます。\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\nuse app\\components\\BookingInterface;\n\nclass HotelController extends Controller\n{\n    protected $bookingService;\n\n    public function __construct($id, $module, BookingInterface $bookingService, $config = [])\n    {\n        $this->bookingService = $bookingService;\n        parent::__construct($id, $module, $config);\n    }\n}\n```\n\nあなたがブラウザからこのコントローラにアクセスすると、`BookingInterface` をインスタンス化できない、という不平を言う\nエラーが表示されるでしょう。これは、この依存に対処する方法を DI コンテナに教える必要があるからです:\n\n```php\n\\Yii::$container->set('app\\components\\BookingInterface', 'app\\components\\BookingService');\n```\n\nこれで、あなたが再びコントローラにアクセスするときは、`app\\components\\BookingService`\nのインスタンスが作成され、コントローラのコンストラクタに3番目のパラメータとして注入されるようになります。\n\nYii 2.0.36 以降は、PHP 7 を使う場合に、ウェブおよびコンソール両方のコントローラでアクション・インジェクションを利用することが出来ます。\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\nuse app\\components\\BookingInterface;\n\nclass HotelController extends Controller\n{    \n    public function actionBook($id, BookingInterface $bookingService)\n    {\n        $result = $bookingService->book($id);\n        // ...    \n    }\n}\n``` \n\n高度な実際の使用方法 <span id=\"advanced-practical-usage\"></span>\n--------------------\n\nAPI アプリケーションを開発していて、以下のクラスを持っているとします。\n\n- `app\\components\\Request` クラス。`yii\\web\\Request` から拡張され、追加の機能を提供する。\n- `app\\components\\Response` クラス。`yii\\web\\Response` から拡張。\n  生成されるときに、`format` プロパティが `json` に設定されなければならない。\n- `app\\storage\\FileStorage` および `app\\storage\\DocumentsReader` クラス。\n  何らかのファイルストレージに配置されているドキュメントを操作するロジックを実装する。\n  \n  ```php\n  class FileStorage\n  {\n      public function __construct($root) {\n          // あれやこれや\n      }\n  }\n  \n  class DocumentsReader\n  {\n      public function __construct(FileStorage $fs) {\n          // なんやかんや\n      }\n  }\n  ```\n\n[[yii\\di\\Container::setDefinitions()|setDefinitions()]] または [[yii\\di\\Container::setSingletons()|setSingletons()]] \nのメソッドに構成情報の配列を渡して、複数の定義を一度に構成することが可能です。\nこれらのメソッドは、構成情報配列を反復して、各アイテムに対し、\nそれぞれ [[yii\\di\\Container::set()|set()]] を呼び出します。\n\n構成情報配列のフォーマットは、\n\n - `key`: クラス名、インタフェイス名、または、エイリアス名。\n  このキーが [[yii\\di\\Container::set()|set()]] メソッドの最初の引数 `$class` として渡されます。\n - `value`: `$class` と関連づけられる定義。指定できる値は、[[yii\\di\\Container::set()|set()]] の `$definition`\n  パラメータのドキュメントで説明されています。\n  [[set()]] メソッドに二番目のパラメータ `$definition` として渡されます。\n\n例として、上述の要求に従うように私たちのコンテナを構成しましょう。\n\n```php\n$container->setDefinitions([\n    'yii\\web\\Request' => 'app\\components\\Request',\n    'yii\\web\\Response' => [\n        'class' => 'app\\components\\Response',\n        'format' => 'json'\n    ],\n    'app\\storage\\DocumentsReader' => function ($container, $params, $config) {\n        $fs = new app\\storage\\FileStorage('/var/tempfiles');\n        return new app\\storage\\DocumentsReader($fs);\n    }\n]);\n\n$reader = $container->get('app\\storage\\DocumentsReader'); \n// 構成情報に書かれている依存とともに DocumentReader オブジェクトが生成されます\n```\n\n> Tip: バージョン 2.0.11 以降では、アプリケーションの構成情報を使って、宣言的なスタイルでコンテナを構成することが出来ます。\n[構成情報](concept-configurations.md) のガイドの [アプリケーションの構成](concept-configurations.md#application-configurations)\nのセクションを参照してください。\n\nこれで全部動きますが、`DocumentWriter` クラスを生成する必要がある場合には、`FileStorage` オブジェクトを生成する行をコピペすることになるでしょう。\nもちろん、それが一番スマートな方法ではありません。\n\n[依存を解決する](#resolving-dependencies) のセクションで説明したように、[[yii\\di\\Container::set()|set()]] と [[yii\\di\\Container::setSingleton()|setSingleton()]] は、\nオプションで、第三の引数として依存のコンストラクタのパラメータを取ることが出来ます。\nコンストラクタのパラメータを設定するために、`__construct()` オプションを使うことが出来ます。\n\nでは、私たちの例を修正しましょう。\n\n```php\n$container->setDefinitions([\n    'tempFileStorage' => [ // 便利なようにエイリアスを作りました\n        'class' => 'app\\storage\\FileStorage',\n        '__construct()' => ['/var/tempfiles'], // 何らかの構成ファイルから抽出することも可能\n    ],\n    'app\\storage\\DocumentsReader' => [\n        'class' => 'app\\storage\\DocumentsReader',\n        '__construct()' => [Instance::of('tempFileStorage')],\n    ],\n    'app\\storage\\DocumentsWriter' => [\n        'class' => 'app\\storage\\DocumentsWriter',\n        '__construct()' => [Instance::of('tempFileStorage')]\n    ]\n]);\n\n$reader = $container->get('app\\storage\\DocumentsReader'); \n// 前の例と全く同じオブジェクトが生成されます\n```\n\n`Instance::of('tempFileStorage')` という記法に気づいたことでしょう。\nこれは、[[yii\\di\\Container|Container]] が、`tempFileStorage` という名前で登録されている依存を黙示的に提供して、\n`app\\storage\\DocumentsWriter` のコンストラクタの最初の引数として渡す、ということを意味しています。\n\n> Note: [[yii\\di\\Container::setDefinitions()|setDefinitions()]] および [[yii\\di\\Container::setSingletons()|setSingletons()]]\n  のメソッドは、バージョン 2.0.11 以降で利用できます。\n\n構成情報の最適化にかかわるもう一つのステップは、いくつかの依存をシングルトンとして登録することです。\n[[yii\\di\\Container::set()|set()]] を通じて登録された依存は、必要になるたびに、毎回インスタンス化されます。\nしかし、ある種のクラスは実行時を通じて状態を変化させませんので、\nアプリケーションのパフォーマンスを高めるためにシングルトンとして登録することが出来ます。\n\n`app\\storage\\FileStorage` クラスが好例でしょう。これは単純な API によってファイル・システムに対する何らかの操作を実行するもの\n(例えば `$fs->read()` や `$fs->write()`) ですが、これらの操作はクラスの内部状態を変化させないものです。\n従って、このクラスのインスタンスを一度だけ生成して、それを複数回使用することが可能です。\n\n```php\n$container->setSingletons([\n    'tempFileStorage' => [\n        'class' => 'app\\storage\\FileStorage',\n        '__construct()' => ['/var/tempfiles']\n    ],\n]);\n\n$container->setDefinitions([\n    'app\\storage\\DocumentsReader' => [\n        'class' => 'app\\storage\\DocumentsReader',\n        '__construct()' => [Instance::of('tempFileStorage')],\n    ],\n    'app\\storage\\DocumentsWriter' => [\n        'class' => 'app\\storage\\DocumentsWriter',\n        '__construct()' => [Instance::of('tempFileStorage')],\n    ]\n]);\n\n$reader = $container->get('app\\storage\\DocumentsReader');\n```\n\nいつ依存を登録するか <span id=\"when-to-register-dependencies\"></span>\n--------------------\n\n依存は、新しいオブジェクトが作成されるとき必要とされるので、それらの登録は可能な限り早期に行われるべきです。\n推奨されるプラクティスは以下のとおりです:\n\n* あなたがアプリケーションの開発者である場合は、アプリケーションの構成情報を使って依存を登録することが出来ます。\n  [構成情報](concept-configurations.md) のガイドの [アプリケーションの構成](concept-configurations.md#application-configurations)\n  のセクションを読んでください。\n* あなたが再配布可能な [エクステンション](structure-extensions.md) の開発者である場合は、エクステンションのブートストラップ・クラス内で\n  依存を登録することができます。\n\n\nまとめ <span id=\"summary\"></span>\n------\n\n依存注入と [サービス・ロケータ](concept-service-locator.md) はともに、疎結合でよりテストしやすい方法でのソフトウェア構築を可能にする、\n定番のデザインパターンです。\n依存注入とサービス・ロケータをより深く理解するために、 [Martin の記事](https://martinfowler.com/articles/injection.html)\nを読むことを強くお勧めします。\n\nYii はその [サービス・ロケータ](concept-service-locator.md) を、依存注入 (DI) コンテナの上に実装しています。\nサービス・ロケータは、新しいオブジェクトのインスタンスを作成しようとするとき、DI コンテナに呼び出しを転送します。\n後者は、依存を、上で説明したように自動的に解決します。\n\n"
  },
  {
    "path": "docs/guide-ja/concept-events.md",
    "content": "イベント\n========\n\nイベントを使うと、既存のコードの特定の実行ポイントに、カスタム・コードを挿入することができます。イベントにカスタム・コードをアタッチすると、\nイベントがトリガされたときにコードが自動的に実行されます。たとえば、メーラ・オブジェクトがメッセージを正しく送信できたとき、\n`messageSent` イベントをトリガするとします。もしメッセージの送信がうまく行ったことを知りたければ、単に `messageSent`\nイベントにトラッキング・コードを付与するだけで、それが可能になります。\n\nYiiはイベントをサポートするために、 [[yii\\base\\Component]] と呼ばれる基底クラスを導入してします。クラスがイベントをトリガする必要がある場合は、\n[[yii\\base\\Component]] もしくはその子クラスを継承する必要があります。\n\n\nイベント・ハンドラ <span id=\"event-handlers\"></span>\n------------------\n\nイベント・ハンドラとは、アタッチされたイベントがトリガされたときに実行される [PHP コールバック](https://www.php.net/manual/ja/language.types.callable.php)\nです。次のコールバックのいずれも使用可能です:\n\n- 文字列で指定されたグローバル PHP 関数 (括弧を除く)、例えば `'trim'`。\n- オブジェクトとメソッド名文字列の配列で指定された、オブジェクトのメソッド (括弧を除く)、例えば `[$object, 'methodName']`。\n- クラス名文字列とメソッド名文字列の配列で指定された、静的なクラス・メソッド (括弧を除く)、例えば `['ClassName', 'methodName']`。\n- 無名関数、例えば `function ($event) { ... }`。\n\nイベント・ハンドラのシグネチャはこのようになります:\n\n```php\nfunction ($event) {\n    // $event は yii\\base\\Event またはその子クラスのオブジェクト\n}\n```\n\n`$event` パラメータを介して、イベント・ハンドラは発生したイベントに関して次の情報を得ることができます:\n\n- [[yii\\base\\Event::name|イベント名]]\n- [[yii\\base\\Event::sender|イベント送信元]]: `trigger()` メソッドが呼ばれたオブジェクト\n- [[yii\\base\\Event::data|カスタム・データ]]: イベント・ハンドラをアタッチするときに提供されたデータ (次の項で説明します)\n\n\nイベント・ハンドラをアタッチする <span id=\"attaching-event-handlers\"></span>\n--------------------------------\n\nイベント・ハンドラは [[yii\\base\\Component::on()]] を呼び出すことでアタッチできます。たとえば:\n\n```php\n$foo = new Foo;\n\n// このハンドラはグローバル関数です\n$foo->on(Foo::EVENT_HELLO, 'function_name');\n\n// このハンドラはオブジェクトのメソッドです\n$foo->on(Foo::EVENT_HELLO, [$object, 'methodName']);\n\n// このハンドラは静的なクラスメソッドです\n$foo->on(Foo::EVENT_HELLO, ['app\\components\\Bar', 'methodName']);\n\n// このハンドラは無名関数です\n$foo->on(Foo::EVENT_HELLO, function ($event) {\n    // イベント処理ロジック\n});\n```\n\nまた、 [構成情報](concept-configurations.md) を通じてイベント・ハンドラをアタッチすることもできます。詳細については\n[構成情報](concept-configurations.md) の章を参照してください。\n\n\nイベント・ハンドラをアタッチするとき、 [[yii\\base\\Component::on()]] の3番目のパラメータとして、付加的なデータを提供することができます。\nそのデータは、イベントがトリガされてハンドラが呼び出されるときに、ハンドラ内で利用きます。たとえば:\n\n```php\n// 次のコードはイベントがトリガされたとき \"abc\" を表示します\n// \"on\" に3番目の引数として渡されたデータを $event->data が保持しているからです\n$foo->on(Foo::EVENT_HELLO, 'function_name', 'abc');\n\nfunction function_name($event) {\n    echo $event->data;\n}\n```\n\nイベント・ハンドラの順序\n------------------------\n\nひとつのイベントには、ひとつだけでなく複数のハンドラをアタッチすることができます。イベントがトリガされると、アタッチされたハンドラは、\nそれらがイベントにアタッチされた順序どおりに呼び出されます。あるハンドラがその後に続くハンドラの呼び出しを停止する必要がある場合は、\n`$event` パラメータの [[yii\\base\\Event::handled]] プロパティを `true` に設定します:\n\n```php\n$foo->on(Foo::EVENT_HELLO, function ($event) {\n    $event->handled = true;\n});\n```\n\nデフォルトでは、新たに接続されたハンドラは、イベントの既存のハンドラのキューに追加されます。その結果、\nイベントがトリガされたとき、そのハンドラは一番最後に呼び出されます。もし、そのハンドラが最初に呼び出されるよう、\nハンドラのキューの先頭に新しいハンドラを挿入したい場合は、[[yii\\base\\Component::on()]] を呼び出すとき、4番目のパラメータ `$append` に `false` を渡します:\n\n```php\n$foo->on(Foo::EVENT_HELLO, function ($event) {\n    // ...\n}, $data, false);\n```\n\nイベントをトリガする <span id=\"triggering-events\"></span>\n--------------------\n\nイベントは、 [[yii\\base\\Component::trigger()]] メソッドを呼び出すことでトリガされます。このメソッドには **イベント名** が必須で、\nオプションで、イベント・ハンドラに渡されるパラメータを記述したイベント・オブジェクトを渡すこともできます。たとえば:\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Component;\nuse yii\\base\\Event;\n\nclass Foo extends Component\n{\n    const EVENT_HELLO = 'hello';\n\n    public function bar()\n    {\n        $this->trigger(self::EVENT_HELLO);\n    }\n}\n```\n\n上記のコードでは、すべての `bar()` の呼び出しは、 `hello` という名前のイベントをトリガします。\n\n> Tip: イベント名を表すときはクラス定数を使用することをお勧めします。上記の例では、定数 `EVENT_HELLO` は\n  `hello` イベントを表しています。このアプローチには 3 つの利点があります。まず、タイプミスを防ぐことができます。次に、IDE の自動補完サポートでイベントを\n  認識できるようになります。第 3 に、クラスでどんなイベントがサポートされているかを表したいとき、定数の宣言をチェックするだけで済みます。\n\nイベントをトリガするとき、イベント・ハンドラに追加情報を渡したいことがあります。たとえば、メーラーが `messageSent` イベントのハンドラに\nメッセージ情報を渡して、ハンドラが送信されたメッセージの詳細を知ることができるようにしたいかもしれません。\nこれを行うために、 [[yii\\base\\Component::trigger()]] メソッドの2番目のパラメータとして、イベント・オブジェクトを与えることができます。\nイベント・オブジェクトは [[yii\\base\\Event]] クラスあるいはその子クラスのインスタンスでなければなりません。\nたとえば:\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Component;\nuse yii\\base\\Event;\n\nclass MessageEvent extends Event\n{\n    public $message;\n}\n\nclass Mailer extends Component\n{\n    const EVENT_MESSAGE_SENT = 'messageSent';\n\n    public function send($message)\n    {\n        // ... $message 送信 ...\n\n        $event = new MessageEvent;\n        $event->message = $message;\n        $this->trigger(self::EVENT_MESSAGE_SENT, $event);\n    }\n}\n```\n\n[[yii\\base\\Component::trigger()]] メソッドが呼び出されたとき、この名前を付けられたイベントに\nアタッチされたハンドラがすべて呼び出されます。\n\n\nイベント・ハンドラをデタッチする <span id=\"detaching-event-handlers\"></span>\n--------------------------------\n\nイベントからハンドラを取り外すには、 [[yii\\base\\Component::off()]] メソッドを呼び出します。たとえば:\n\n```php\n// このハンドラはグローバル関数です\n$foo->off(Foo::EVENT_HELLO, 'function_name');\n\n// このハンドラはオブジェクトのメソッドです\n$foo->off(Foo::EVENT_HELLO, [$object, 'methodName']);\n\n// このハンドラは静的なクラスメソッドです\n$foo->off(Foo::EVENT_HELLO, ['app\\components\\Bar', 'methodName']);\n\n// このハンドラは無名関数です\n$foo->off(Foo::EVENT_HELLO, $anonymousFunction);\n```\n\n一般的には、イベントにアタッチされたときどこかに保存してある場合を除き、無名関数を取り外そうとはしないでください。\n上記の例は、無名関数は変数 `$anonymousFunction` として保存されていたものとしています。\n\nイベントから *すべて* のハンドラを取り外すには、単純に、第 2 パラメータを指定せずに [[yii\\base\\Component::off()]] を呼び出します。\n\n```php\n$foo->off(Foo::EVENT_HELLO);\n```\n\n\nクラス・レベル・イベント・ハンドラ <span id=\"class-level-event-handlers\"></span>\n----------------------------------\n\nここまでの項では、*インスタンス・レベル* でのイベントにハンドラをアタッチする方法を説明してきました。\n場合によっては、特定のインスタンスだけではなく、\nクラスのすべてのインスタンスがトリガしたイベントに応答したいことがあります。\nすべてのインスタンスにイベント・ハンドラをアタッチする代わりに、静的メソッド [[yii\\base\\Event::on()]] を呼び出すことで、\n*クラス・レベル* でハンドラをアタッチすることができます。\n\nたとえば、[アクティブ・レコード](db-active-record.md) オブジェクトは、データベースに新しいレコードを挿入するたびに、\n[[yii\\db\\BaseActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]] イベントをトリガします。 *すべての*\n[アクティブ・レコード](db-active-record.md) オブジェクトによって行われる挿入を追跡するには、次のコードが使えます：\n\n```php\nuse Yii;\nuse yii\\base\\Event;\nuse yii\\db\\ActiveRecord;\n\nEvent::on(ActiveRecord::class, ActiveRecord::EVENT_AFTER_INSERT, function ($event) {\n    Yii::debug(get_class($event->sender) . ' が挿入されました');\n});\n```\n\n[[yii\\db\\ActiveRecord|ActiveRecord]] またはその子クラスのいずれかが、 [[yii\\db\\BaseActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]]\nをトリガするといつでも、このイベント・ハンドラが呼び出されます。ハンドラの中では、 `$event->sender` を通して、\nイベントをトリガしたオブジェクトを取得することができます。\n\nオブジェクトがイベントをトリガするときは、最初にインスタンス・レベルのハンドラを呼び出し、続いてクラス・レベルのハンドラとなります。\n\n静的メソッド [[yii\\base\\Event::trigger()]] を呼び出すことによって、 *クラス・レベル* でイベントをトリガすることができます。\nクラス・レベルでのイベントは、特定のオブジェクトに関連付けられていません。そのため、これはクラス・レベルのイベント・ハンドラだけを\n呼び出します。たとえば:\n\n```php\nuse yii\\base\\Event;\n\nEvent::on(Foo::class, Foo::EVENT_HELLO, function ($event) {\n    var_dump($event->sender);  // \"null\" を表示\n});\n\nEvent::trigger(Foo::class, Foo::EVENT_HELLO);\n```\n\nこの場合、`$event->sender` は、オブジェクト・インスタンスではなく、`null` になることに注意してください。\n\n> Note: クラス・レベルのハンドラは、そのクラスのあらゆるインスタンス、またはあらゆる子クラスのインスタンスがトリガしたイベントに応答\n  してしまうため、よく注意して使わなければなりません。 [[yii\\base\\BaseObject]] のように、クラスが低レベルの基底クラスの場合は特にそうです。\n\nクラス・レベルのイベント・ハンドラを取り外すときは、 [[yii\\base\\Event::off()]] を呼び出します。たとえば:\n\n```php\n// $handler をデタッチ\nEvent::off(Foo::class, Foo::EVENT_HELLO, $handler);\n\n// Foo::EVENT_HELLO のすべてのハンドラをデタッチ\nEvent::off(Foo::class, Foo::EVENT_HELLO);\n```\n\n\nインタフェイスを使うイベント <span id=\"interface-level-event-handlers\"></span>\n------------------------------\n\nイベントを扱うためには、もっと抽象的な方法もあります。\n特定のイベントのために専用のインタフェイスを作っておき、必要な場合にいろいろなクラスでそれを実装するのです。\n\n例えば、次のようなインタフェイスを作ります。\n\n```php\nnamespace app\\interfaces;\n\ninterface DanceEventInterface\n{\n    const EVENT_DANCE = 'dance';\n}\n```\n\nそして、それを実装する二つのクラスを作ります。\n\n```php\nclass Dog extends Component implements DanceEventInterface\n{\n    public function meetBuddy()\n    {\n        echo \"ワン!\";\n        $this->trigger(DanceEventInterface::EVENT_DANCE);\n    }\n}\n\nclass Developer extends Component implements DanceEventInterface\n{\n    public function testsPassed()\n    {\n        echo \"よっしゃ!\";\n        $this->trigger(DanceEventInterface::EVENT_DANCE);\n    }\n}\n```\n\nこれらのクラスのどれかによってトリガされた `EVENT_DANCE` を扱うためには、インタフェイス・クラスの名前を最初の引数にして\n[[yii\\base\\Event::on()|Event::on()]] を呼びます。\n\n```php\nEvent::on('app\\interfaces\\DanceEventInterface', DanceEventInterface::EVENT_DANCE, function ($event) {\n    Yii::debug(get_class($event->sender) . ' が躍り上がって喜んだ。'); // 犬または開発者が躍り上がって喜んだことをログに記録。\n});\n```\n\nこれらのクラスのイベントをトリガすることも出来ます。\n\n```php\n// trigger event for Dog class\nEvent::trigger(Dog::class, DanceEventInterface::EVENT_DANCE);\n\n// trigger event for Developer class\nEvent::trigger(Developer::class, DanceEventInterface::EVENT_DANCE);\n```\n\nただし、このインタフェイスを実装する全クラスのイベントをトリガすることは出来ない、ということに注意して下さい。\n\n```php\n// これは動かない。このインタフェイスを実装するクラスのイベントはトリガされない。\nEvent::trigger('app\\interfaces\\DanceEventInterface', DanceEventInterface::EVENT_DANCE);\n```\n\nイベント・ハンドラをデタッチするためには、[[yii\\base\\Event::off()|Event::off()]] を呼びます。例えば、\n\n```php\n// $handler をデタッチ\nEvent::off('app\\interfaces\\DanceEventInterface', DanceEventInterface::EVENT_DANCE, $handler);\n\n// DanceEventInterface::EVENT_DANCE の全てのハンドラをデタッチ\nEvent::off('app\\interfaces\\DanceEventInterface', DanceEventInterface::EVENT_DANCE);\n```\n\n\nグローバル・イベント <span id=\"global-events\"></span>\n--------------------\n\nYiiは、いわゆる *グローバル・イベント* をサポートしています。これは、実際には、上記のイベント・メカニズムに基づいたトリックです。\nグローバル・イベントは、 [アプリケーション](structure-applications.md) インスタンス自身などの、グローバルにアクセス可能なシングルトンを必要とします。\n\nグローバル・イベントを作成するには、イベント送信者は、送信者の自前の `trigger()` メソッドを呼び出す代わりに、シングルトンの\n`trigger()` メソッドを呼び出してイベントをトリガします。同じく、イベント・ハンドラも、シングルトンのイベントにアタッチされます。たとえば:\n\n```php\nuse Yii;\nuse yii\\base\\Event;\nuse app\\components\\Foo;\n\nYii::$app->on('bar', function ($event) {\n    echo get_class($event->sender);  // \"app\\components\\Foo\" を表示\n});\n\nYii::$app->trigger('bar', new Event(['sender' => new Foo]));\n```\n\nグローバル・イベントを使用する利点は、オブジェクトによってトリガされるイベント・ハンドラを設けたいとき、オブジェクトがなくてもいい\nということです。その代わりに、ハンドラのアタッチとイベントのトリガはともに、(アプリケーションのインスタンスなど) シングルトンを\n介して行われます。\n\nしかし、グローバル・イベントの名前空間はあらゆる部分から共有されているので、ある種の名前空間 (\"frontend.mail.sent\"、\"backend.mail.sent\" など)\nを導入するというような、賢いグローバル・イベントの名前付けをする必要があります。\n\n\nワイルドカード・イベント <span id=\"wildcard-events\"></span>\n------------------------\n\n2.0.14 以降は、ワイルドカード・パターンに一致する複数のイベントに対してイベント・ハンドラを設定することが出来ます。\n例えば、\n\n```php\nuse Yii;\n\n$foo = new Foo();\n\n$foo->on('foo.event.*', function ($event) {\n    // 'foo.event.' で始まる全てのイベントに対してトリガされる\n    Yii::debug('trigger event: ' . $event->name);\n});\n```\n\nクラス・レベル・イベントに対してもワイルドカード・パターンを用いることが出来ます。例えば、\n\n```php\nuse yii\\base\\Event;\nuse Yii;\n\nEvent::on('app\\models\\*', 'before*', function ($event) {\n    // 名前空間 'app\\models' の全てのクラスで、名前が 'before' で始まる全てのイベントに対してトリガされる\n    Yii::debug('trigger event: ' . $event->name . ' for class: ' . get_class($event->sender));\n});\n```\n\nこれを利用すると、以下のコードを使って、全てのアプリケーション・イベントを一つのハンドラでキャッチすることが出来ます。\n\n```php\nuse yii\\base\\Event;\nuse Yii;\n\nEvent::on('*', '*', function ($event) {\n    // 全てのクラスの全てのイベントに対してトリガされる\n    Yii::debug('trigger event: ' . $event->name);\n});\n```\n\n> Note: イベント・ハンドラにワイルドカードを使用する設定は、アプリケーションの性能を低下させ得ます。\n  可能であれば避ける方が良いでしょう。\n\nワイルドカード・パターンで指定されたイベント・ハンドラをデタッチするためには、[[yii\\base\\Component::off()]] または [[yii\\base\\Event::off()]] の呼び出しにおいて、\n同じパターンを使用しなければなりません。\nイベント・ハンドラをデタッチする際にワイルドカードを指定すると、そのワイルドカードで指定されたハンドラだけがデタッチされることに留意して下さい。\n通常のイベント名でアタッチされたハンドラは、パターンに合致する場合であっても、デタッチされません。例えば、\n\n```php\nuse Yii;\n\n$foo = new Foo();\n\n// 通常のハンドラをアタッチする\n$foo->on('event.hello', function ($event) {\n    echo 'direct-handler'\n});\n\n// ワイルドカード・ハンドラをアタッチする\n$foo->on('*', function ($event) {\n    echo 'wildcard-handler'\n});\n\n// ワイルドカード・ハンドラをデタッチする!\n$foo->off('*');\n\n$foo->trigger('event.hello'); // 出力: 'direct-handler'\n```\n"
  },
  {
    "path": "docs/guide-ja/concept-properties.md",
    "content": "プロパティ\n==========\n\nPHPでは、クラスのメンバ変数は *プロパティ* とも呼ばれます。\nこれらの変数は、クラス定義の一部で、クラスのインスタンスの状態を表すために(すなわち、クラスのあるインスタンスを別のものと区別するために) 使用されます。\n現実には、特別な方法でこのプロパティの読み書きを扱いたい場合がよくあります。\nたとえば、`label` プロパティに割り当てられる文字列が常にトリミングされるようにしたい、など。\nその仕事を成し遂げるために、あなたは次のようなコードを使おうと思えば使うことも出来ます。\n\n```php\n$object->label = trim($label);\n```\n\n上記のコードの欠点は、`label` プロパティを設定するすべてのコードで、`trim()` を呼び出す必要があるということです。\nもし将来的に、`label` プロパティに、最初の文字を大文字にしなければならない、といった新たな要件が発生したら、\n`label` に値を代入するすべてのコードを変更しなければなりません。\nコードの繰り返しはバグを誘発するので、可能な限り避けたいところです。\n\nこの問題を解決するために、Yii は *getter* メソッドと *setter* メソッドをベースにしたプロパティ定義をサポートする、\n[[yii\\base\\BaseObject]] 基底クラスを提供しています。\nクラスがその機能を必要とするなら、[[yii\\base\\BaseObject]] またはその子クラスを継承しましょう。\n\n> Note: Yiiのフレームワークのほぼすべてのコア・クラスは、 [[yii\\base\\BaseObject]] またはその子クラスを継承しています。\n  これは、コア・クラスに getter または setter があれば、それをプロパティのように使用できることを意味します。\n\ngetter メソッドは、名前が `get` で始まるメソッドで、setter メソッドは、`set` で始まるメソッドです。\n`get` または `set` 接頭辞の後の名前で、プロパティ名を定義します。次のコードに示すように、たとえば、`getLabel()` という getter と `setLabel()` という setter は、\n`label` という名前のプロパティを定義します:\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\BaseObject;\n\nclass Foo extends BaseObject\n{\n    private $_label;\n\n    public function getLabel()\n    {\n        return $this->_label;\n    }\n\n    public function setLabel($value)\n    {\n        $this->_label = trim($value);\n    }\n}\n```\n\n詳しく言うと、getter および setter メソッドは、この場合には、内部的に `_label` と名付けられた private な属性を参照する\n`label` というプロパティを作っています。\n\ngetter と setter によって定義されたプロパティは、クラスのメンバ変数のように使用することができます。主な違いは、\nそれらのプロパティが読み取りアクセスされるときは、対応する getter メソッドが呼び出されることであり、プロパティに値が割り当てられるときには、\n対応する setter メソッドが呼び出されるということです。例えば、\n\n```php\n// $label = $object->getLabel(); と同じ\n$label = $object->label;\n\n// $object->setLabel('abc'); と同じ\n$object->label = 'abc';\n```\n\nsetter なしの getter で定義されたプロパティは、 *読み取り専用* です。そのようなプロパティに値を代入しようとすると、\n[[yii\\base\\InvalidCallException|InvalidCallException]] が発生します。同様に、getter なしの setter で定義されたプロパティは、\n*書き込み専用* で、そのようなプロパティを読み取りしようとしても、例外が発生します。\n書き込み専用のプロパティを持つのは一般的ではありませんが。\n\ngetter と setter で定義されたプロパティには、いくつかの特別なルールと制限があります:\n\n* この種のプロパティでは、名前の *大文字と小文字を区別しません* 。たとえば、 `$object->label` と `$object->Label` は同じです。\n  これは、PHP のメソッド名が大文字と小文字を区別しないためです。\n* この種のプロパティの名前と、クラスのメンバ変数の名前とが同じである場合、後者が優先されます。\n  たとえば、上記の `Foo` クラスがメンバ変数 `label` を持っている場合は、`$object->label = 'abc'`\n  という代入は *メンバ変数の* `label` に作用することになります。その行から `setLabel()` setter メソッドは呼び出されません。\n* これらのプロパティは可視性をサポートしていません。プロパティが public、protected、private であるかどうかを、getter または setter メソッドの定義によって決めることは出来ません。\n* プロパティは、 *静的でない* getter および setter によってのみ定義することが出来ます。静的なメソッドは同様には扱われません。\n* 通常の `property_exists()` の呼び出しでは、マジック・プロパティが存在するかどうかを知ることは出来ません。\n  それぞれ、[[yii\\base\\BaseObject::canGetProperty()|canGetProperty()]] または [[yii\\base\\BaseObject::canSetProperty()|canSetProperty()]] を呼び出さなければなりません。\n\nこのガイドの冒頭で説明した問題に戻ると、`label` に値が代入されているあらゆる箇所で `trim()` を呼ぶのではなく、\n`setLabel()` という setter の内部だけで `trim()` を呼べば済むようになります。\nさらに、新しい要求でラベルの先頭を大文字にする必要が発生しても、他のいっさいのコードに触れることなく、\nすぐに `setLabel()` メソッドを変更することができます。一箇所の変更は、すべての `label` への代入に普遍的に作用します。\n"
  },
  {
    "path": "docs/guide-ja/concept-service-locator.md",
    "content": "サービス・ロケータ\n==================\n\nサービス・ロケータは、アプリケーションが必要とする可能性のある各種のサービス (またはコンポーネント) を提供する方法を知っているオブジェクトです。\nサービス・ロケータ内では、各コンポーネントは単一のインスタンスとして存在し、ID によって一意に識別されます。\nあなたは、この ID を使用してサービス・ロケータからコンポーネントを取得できます。\n\nYii では、サービス・ロケータは単純に [[yii\\di\\ServiceLocator]] のインスタンス、またはその子クラスのインスタンスです。\n\nYii の中で最も一般的に使用されるサービス・ロケータは、`\\Yii::$app` を通じてアクセスできる *アプリケーション*・オブジェクトです。\nこれが提供するサービスは、 *アプリケーション・コンポーネント* と呼ばれる `request` 、\n`response`、 `urlManager` などのコンポーネントです。あなたはサービス・ロケータによって提供される機能を通じて、\n簡単に、これらのコンポーネントを構成、あるいは独自の実装に置き換え、といったことができます。\n\nアプリケーション・オブジェクトの他に、各モジュール・オブジェクトもまたサービス・ロケータです。モジュールは [ツリー走査](#tree-traversal) を実装しています。\n\nサービス・ロケータを使用する最初のステップは、コンポーネントを登録することです。コンポーネントは、 [[yii\\di\\ServiceLocator::set()]]\nを通じて登録することができます。次のコードは、コンポーネントを登録するさまざまな方法を示しています。\n\n```php\nuse yii\\di\\ServiceLocator;\nuse yii\\caching\\FileCache;\n\n$locator = new ServiceLocator;\n\n// コンポーネントの作成に使われるクラス名を使用して \"cache\" を登録\n$locator->set('cache', 'yii\\caching\\ApcCache');\n\n// コンポーネントの作成に使われる構成情報配列を使用して \"db\" を登録\n$locator->set('db', [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=localhost;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n]);\n\n// コンポーネントを構築する匿名関数を使って \"search\" を登録\n$locator->set('search', function () {\n    return new app\\components\\SolrService;\n});\n\n// コンポーネントを使って \"pageCache\" を登録\n$locator->set('pageCache', new FileCache);\n```\n\nいったんコンポーネントが登録されたら、次の二つの方法のいずれかで、その ID を使ってそれにアクセスすることができます:\n\n```php\n$cache = $locator->get('cache');\n// または代りに\n$cache = $locator->cache;\n```\n\n上記のように、[[yii\\di\\ServiceLocator]] を使うと、コンポーネント ID を使用して、プロパティのようにコンポーネントにアクセスすることができます。\nあなたが最初にコンポーネントにアクセスしたとき、[[yii\\di\\ServiceLocator]] は\nコンポーネントの登録情報を使用してコンポーネントの新しいインスタンスを作成し、\nそれを返します。後でそのコンポーネントが再度アクセスされた場合、サービス・ロケータは同じインスタンスを返します。\n\n[[yii\\di\\ServiceLocator::has()]] を使って、コンポーネント ID がすでに登録されているかをチェックできます。\n無効な ID で [[yii\\di\\ServiceLocator::get()]] を呼び出した場合、例外が投げられます。\n\n\nサービス・ロケータは多くの場合、 [構成情報](concept-configurations.md) で作成されるため、\n[[yii\\di\\ServiceLocator::setComponents()|components]] という名前の書き込み可能プロパティが提供されています。\nこれで一度に複数のコンポーネントを設定して登録することができます。\n次のコードは、サービス・ロケータ (例えば [アプリケーション](structure-applications.md)) を\n`db`、`cache`、`tz`、`search` コンポーネントとともに構成するための構成情報配列を示しています。\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=demo',\n            'username' => 'root',\n            'password' => '',\n        ],\n        'cache' => 'yii\\caching\\ApcCache',\n        'tz' => function() {\n            return new \\DateTimeZone(Yii::$app->formatter->defaultTimeZone);\n        },\n        'search' => function () {\n            $solr = new app\\components\\SolrService('127.0.0.1');\n            // ... その他の初期化 ...\n            return $solr;\n        },\n    ],\n];\n```\n\n上記において、`search` コンポーネントを構成する別の方法があります。\n`SolrService` のインスタンスを構築する PHP コールバックを直接に書く代りに、\n下記のように、そういうコールバックを返すスタティックなクラス・メソッドを使うことが出来ます。\n\n```php\nclass SolrServiceBuilder\n{\n    public static function build($ip)\n    {\n        return function () use ($ip) {\n            $solr = new app\\components\\SolrService($ip);\n            // ... その他の初期化 ...\n            return $solr;\n        };\n    }\n}\n\nreturn [\n    // ...\n    'components' => [\n        // ...\n        'search' => SolrServiceBuilder::build('127.0.0.1'),\n    ],\n];\n```\n\nこの方法は、Yii に属さないサードパーティのライブラリをカプセル化する Yii コンポーネントをリリースしようとする場合に、特に推奨される代替手法です。\n上で示されているようなスタティックなメソッドを使ってサードパーティのオブジェクトを構築する複雑なロジックを表現します。\nそうすれば、あなたのコンポーネントのユーザは、コンポーネントを構成するスタティックなメソッドを呼ぶ必要があるだけになります。\n\n## ツリー走査 <span id=\"tree-traversal\"></span>\n\nモジュールは任意にネストすることが出来ます。Yii アプリケーションは本質的にモジュールのツリーなのです。\nこれらのモジュールのそれぞれがサービス・ロケータである訳ですから、子がその親にアクセスできるようにするのは理にかなった事です。\nこれによって、モジュールは、ルートのサービス・ロケータを参照して `Yii::$app->get('db')` とする代りに、`$this->get('db')` とすることが出来ます。\nまた、開発者にモジュール内で構成をオーバーライドするオプションを提供できることも、この仕組の利点です。\n\nモジュールからサービスを引き出そうとする全てのリクエストは、そのモジュールが要求に応じられない場合は、すべてその親に渡されます。\n\nモジュール内のコンポーネントの構成情報は、親モジュール内のコンポーネントの構成情報とは決してマージされないことに注意して下さい。\nサービス・ロケータのパターンによって私たちは名前の付いたサービスを定義することが出来ますが、同じ名前のサービスが同じ構成パラメータを使用すると想定することは出来ません。\n"
  },
  {
    "path": "docs/guide-ja/db-active-record.md",
    "content": "アクティブ・レコード\n====================\n\n[アクティブ・レコード](https://ja.wikipedia.org/wiki/Active_Record) は、データベースに保存されているデータにアクセスするために、\nオブジェクト指向のインタフェイスを提供するものです。\nアクティブ・レコード・クラスはデータベース・テーブルと関連付けられます。\nアクティブ・レコードのインスタンスはそのテーブルの行に対応し、アクティブ・レコードのインスタンスの *属性* がその行にある特定のカラムの値を表現します。\n生の SQL 文を書く代りに、アクティブ・レコードの属性にアクセスしたり、アクティブ・レコードのメソッドを呼んだりして、\nデータベース・テーブルに保存さているデータにアクセスしたり、データを操作したりします。\n\n例えば、`Customer` が `customer` テーブルに関連付けられたアクティブ・レコード・クラスであり、\n`name` が `customer` テーブルのカラムであると仮定しましょう。\n`customer` テーブルに新しい行を挿入するために次のコードを書くことが出来ます。\n\n```php\n$customer = new Customer();\n$customer->name = 'Qiang';\n$customer->save();\n```\n\n上記のコードは、MySQL では、次のような生の SQL 文を使うのと等価なものです。\nしかし、生の SQL 文の方は、直感的でなく、間違いも生じやすく、また、別の種類のデータベースを使う場合には、互換性の問題も生じ得ます。\n\n```php\n$db->createCommand('INSERT INTO `customer` (`name`) VALUES (:name)', [\n    ':name' => 'Qiang',\n])->execute();\n```\n\nYii は次のリレーショナル・データベースに対して、アクティブ・レコードのサポートを提供しています。\n\n* MySQL 4.1 以降: [[yii\\db\\ActiveRecord]] による。\n* PostgreSQL 7.3 以降: [[yii\\db\\ActiveRecord]] による。\n* SQLite 2 および 3: [[yii\\db\\ActiveRecord]] による。\n* Microsoft SQL Server 2008 以降: [[yii\\db\\ActiveRecord]] による。\n* Oracle: [[yii\\db\\ActiveRecord]] による。\n* CUBRID 9.3 以降: [[yii\\db\\ActiveRecord]] による。(cubrid PDO 拡張の [バグ](http://jira.cubrid.org/browse/APIS-658)\n  のために、値を引用符で囲む機能が動作しません。そのため、サーバだけでなくクライアントも CUBRID 9.3 が必要になります)\n* Sphinx: [[yii\\sphinx\\ActiveRecord]] による。`yii2-sphinx` エクステンションが必要。\n* ElasticSearch: [[yii\\elasticsearch\\ActiveRecord]] による。`yii2-elasticsearch` エクステンションが必要。\n\nこれらに加えて、Yii は次の NoSQL データベースに対しても、アクティブ・レコードの使用をサポートしています。\n\n* Redis 2.6.12 以降: [[yii\\redis\\ActiveRecord]] による。`yii2-redis` エクステンションが必要。\n* MongoDB 1.3.0 以降: [[yii\\mongodb\\ActiveRecord]] による。`yii2-mongodb` エクステンションが必要。\n\nこのチュートリアルでは、主としてリレーショナル・データベースのためのアクティブ・レコードの使用方法を説明します。\nしかし、ここで説明するほとんどの内容は NoSQL データベースのためのアクティブ・レコードにも適用することが出来るものです。\n\n\n## アクティブ・レコード・クラスを宣言する <span id=\"declaring-ar-classes\"></span>\n\nまずは、[[yii\\db\\ActiveRecord]] を拡張してアクティブ・レコード・クラスを宣言するところから始めましょう。\n\n### テーブル名を設定する\n\nデフォルトでは、すべてのアクティブ・レコード・クラスはデータベース・テーブルと関連付けられます。\n[[yii\\db\\ActiveRecord::tableName()|tableName()]] メソッドが、クラス名を [[yii\\helpers\\Inflector::camel2id()]] によって変換して、テーブル名を返します。\nテーブル名がこの規約に従っていない場合は、このメソッドをオーバライドすることが出来ます。\n\n同時に、デフォルトの [[yii\\db\\Connection::$tablePrefix|tablePrefix]] を適用することも可能です。\n例えば、[[yii\\db\\Connection::$tablePrefix|tablePrefix]] が `tbl_` である場合は、`Customer` は `tbl_customer` になり、`OrderItem` は`tbl_order_item` になります。\n\nテーブル名が `{{%TableName}}` という形式で与えられた場合は、パーセント記号 `%` がテーブルプレフィックスに置き換えられます。\n例えば、`{{%post}}` は `{{tbl_post}}` となります。\nテーブル名を囲む二重波括弧は、[テーブル名を囲む引用符号](db-dao.md#quoting-table-and-column-names) となります。\n\n次の例では、`customer` というデータベース・テーブルのための `Customer` という名前のアクティブ・レコード・クラスを宣言しています。\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass Customer extends ActiveRecord\n{\n    const STATUS_INACTIVE = 0;\n    const STATUS_ACTIVE = 1;\n    \n    /**\n     * @return string このアクティブ・レコード・クラスと関連付けられるテーブルの名前\n     */\n    public static function tableName()\n    {\n        return '{{customer}}';\n    }\n}\n```\n\n### アクティブ・レコードは「モデル」と呼ばれる\n\nアクティブ・レコードのインスタンスは [モデル](structure-models.md) であると見なされます。\nこの理由により、私たちは通常 `app\\models` 名前空間 (あるいはモデル・クラスを保管するための他の名前空間) の下にアクティブ・レコード・クラスを置きます。\n\n[[yii\\db\\ActiveRecord]] は [[yii\\base\\Model]] から拡張していますので、属性、検証規則、データのシリアル化など、\n[モデル](structure-models.md) が持つ *全ての* 機能を継承しています。\n\n\n## データベースに接続する <span id=\"db-connection\"></span>\n\nデフォルトでは、アクティブ・レコードは、`db` [アプリケーション・コンポーネント](structure-application-components.md) を\n[[yii\\db\\Connection|DB 接続]] として使用して、データベースのデータにアクセスしたり操作したりします。\n[データベース・アクセス・オブジェクト](db-dao.md) で説明したように、次のようにして、アプリケーションの構成情報ファイルの中で\n`db` コンポーネントを構成することが出来ます。\n\n```php\nreturn [\n    'components' => [\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=testdb',\n            'username' => 'demo',\n            'password' => 'demo',\n        ],\n    ],\n];\n```\n\n`db` コンポーネントとは異なるデータベース接続を使いたい場合は、[[yii\\db\\ActiveRecord::getDb()|getDb()]]\nメソッドをオーバーライドしなければなりません。\n\n```php\nclass Customer extends ActiveRecord\n{\n    // ...\n\n    public static function getDb()\n    {\n        // \"db2\" アプリケーション・コンポーネントを使用\n        return \\Yii::$app->db2;\n    }\n}\n```\n\n\n## データをクエリする <span id=\"querying-data\"></span>\n\nアクティブ・レコード・クラスを宣言した後、それを使って対応するデータベース・テーブルからデータをクエリすることが出来ます。\nこのプロセスは通常次の三つのステップを踏みます。\n\n1. [[yii\\db\\ActiveRecord::find()]] メソッドを呼んで、新しいクエリ・オブジェクトを作成する。\n2. [クエリ構築メソッド](db-query-builder.md#building-queries) を呼んで、クエリ・オブジェクトを構築する。\n3. [クエリ・メソッド](db-query-builder.md#query-methods) を呼んで、アクティブ・レコードのインスタンスの形でデータを取得する。\n\nご覧のように、このプロセスは [クエリ・ビルダ](db-query-builder.md) による手続きと非常によく似ています。\n唯一の違いは、`new` 演算子を使ってクエリ・オブジェクトを生成する代りに、[[yii\\db\\ActiveQuery]] クラスであるクエリ・オブジェクトを返す\n[[yii\\db\\ActiveRecord::find()]] を呼ぶ、という点です。\n\n以下の例は、アクティブ・クエリを使ってデータをクエリする方法を示すものです。\n\n```php\n// ID が 123 である一人の顧客を返す\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::find()\n    ->where(['id' => 123])\n    ->one();\n\n// アクティブな全ての顧客を返して、ID によって並べる\n// SELECT * FROM `customer` WHERE `status` = 1 ORDER BY `id`\n$customers = Customer::find()\n    ->where(['status' => Customer::STATUS_ACTIVE])\n    ->orderBy('id')\n    ->all();\n\n// アクティブな顧客の数を返す\n// SELECT COUNT(*) FROM `customer` WHERE `status` = 1\n$count = Customer::find()\n    ->where(['status' => Customer::STATUS_ACTIVE])\n    ->count();\n\n// 全ての顧客を顧客IDによってインデックスされた配列として返す\n// SELECT * FROM `customer`\n$customers = Customer::find()\n    ->indexBy('id')\n    ->all();\n```\n\n上記において、`$customer` は `Customer` オブジェクトであり、`$customers` は `Customer` オブジェクトの配列です。\n全てこれらには `customer` テーブルから取得されたデータが投入されます。\n\n> Info: [[yii\\db\\ActiveQuery]] は [[yii\\db\\Query]] から拡張しているため、[クエリ・ビルダ](db-query-builder.md)\n  のセクションで説明されたクエリ構築メソッドとクエリ・メソッドの *全て* を使うことが出来ます。\n\nプライマリ・キーの値や一群のカラムの値でクエリをすることはよく行われる仕事ですので、Yii はこの目的のために、\n二つのショートカット・メソッドを提供しています。\n\n- [[yii\\db\\ActiveRecord::findOne()]]: クエリ結果の最初の行を一つのアクティブ・レコード・インスタンスに投入して返す。\n- [[yii\\db\\ActiveRecord::findAll()]]: *全ての* クエリ結果をアクティブ・レコード・インスタンスの配列に投入して返す。\n\nどちらのメソッドも、次のパラメータ形式のどれかを取ることが出来ます。\n\n- スカラ値: 値は検索時に求められるプライマリ・キーの値として扱われます。\n  Yii は、データベースのスキーマ情報を読んで、どのカラムがプライマリ・キーのカラムであるかを自動的に判断します。\n- スカラ値の配列: 配列は検索時に求められるプライマリ・キーの値の配列として扱われます。\n- 連想配列: キーはカラム名であり、値は検索時に求められる対応するカラムの値です。\n  詳細については、[ハッシュ形式](db-query-builder.md#hash-format) を参照してください。\n\n次のコードは、これらのメソッドの使用方法を示すものです。\n\n```php\n// ID が 123 である一人の顧客を返す\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// ID が 100, 101, 123, 124 のどれかである顧客を全て返す\n// SELECT * FROM `customer` WHERE `id` IN (100, 101, 123, 124)\n$customers = Customer::findAll([100, 101, 123, 124]);\n\n// ID が 123 であるアクティブな顧客を返す\n// SELECT * FROM `customer` WHERE `id` = 123 AND `status` = 1\n$customer = Customer::findOne([\n    'id' => 123,\n    'status' => Customer::STATUS_ACTIVE,\n]);\n\n// アクティブでない全ての顧客を返す\n// SELECT * FROM `customer` WHERE `status` = 0\n$customers = Customer::findAll([\n    'status' => Customer::STATUS_INACTIVE,\n]);\n```\n\n> Warning: これらのメソッドにユーザ入力を渡す必要がある場合は、入力値がスカラ値であること、または、\n> 入力値が配列形式の条件である場合は配列の構造が外部から変更され得ないことを保証して下さい。\n>\n> ```php\n> // yii\\web\\Controller が $id はスカラ値であることを保証しています\n> public function actionView($id)\n> {\n>     $model = Post::findOne($id);\n>     // ...\n> }\n>\n> // 検索するカラムを明示的に指定する場合。ここでは、どんなスカラ値または配列を渡しても、単一のレコードを発見する結果になります。\n> $model = Post::findOne(['id' => Yii::$app->request->get('id')]);\n>\n> // 次のコードを使用してはいけません! 任意のカラムの値による検索が可能な配列形式の条件を挿入される可能性があります!\n> $model = Post::findOne(Yii::$app->request->get('id'));\n> ```\n\n\n> Note: [[yii\\db\\ActiveRecord::findOne()]] も [[yii\\db\\ActiveQuery::one()]] も、生成される SQL 文に `LIMIT 1` を追加しません。\n  あなたのクエリが多数のデータ行を返すかもしれない場合は、パフォーマンスを向上させるために、`limit(1)` を明示的に呼ぶべきです。\n  例えば `Customer::find()->limit(1)->one()` のように。\n\nクエリ構築メソッドを使う以外に、生の SQL を書いてデータをクエリして結果をアクティブ・レコード・オブジェクトに投入することも出来ます。\nそうするためには [[yii\\db\\ActiveRecord::findBySql()]] メソッドを呼ぶことが出来ます。\n\n```php\n// アクティブでない全ての顧客を返す\n$sql = 'SELECT * FROM customer WHERE status=:status';\n$customers = Customer::findBySql($sql, [':status' => Customer::STATUS_INACTIVE])->all();\n```\n\n[[yii\\db\\ActiveRecord::findBySql()|findBySql()]] を呼んだ後は、追加でクエリ構築メソッドを呼び出してはいけません。\n呼んでも無視されます。\n\n\n## データにアクセスする <span id=\"accessing-data\"></span>\n\n既に述べたように、データベースから取得されたデータはアクティブ・レコードのインスタンスに投入されます。\nそして、クエリ結果の各行がアクティブ・レコードの一つのインスタンスに対応します。\nアクティブ・レコード・インスタンスの属性にアクセスすることによって、カラムの値にアクセスすることが出来ます。例えば、\n\n```php\n// \"id\" と \"email\" は \"customer\" テーブルのカラム名\n$customer = Customer::findOne(123);\n$id = $customer->id;\n$email = $customer->email;\n```\n\n> Note: アクティブ・レコードの属性の名前は、関連付けられたテーブルのカラムの名前に従って、大文字と小文字を区別して名付けられます。\n  Yii は、関連付けられたテーブルの全てのカラムに対して、アクティブ・レコードの属性を自動的に定義します。\n  これらの属性は、すべて、再宣言してはいけません。\n\nアクティブ・レコードの属性はテーブルのカラムに従って命名されるため、\nテーブルのカラム名がアンダースコアで単語を分ける方法で命名されている場合は、\n`$customer->first_name` のような属性名を使って PHP コードを書くことになります。\nコード・スタイルの一貫性が気になるのであれば、テーブルのカラム名を (例えば camelCase を使う名前に) 変更しなければなりません。\n\n\n### データ変換 <span id=\"data-transformation\"></span>\n\n入力または表示されるデータの形式が、データベースにデータを保存するときに使われるものと異なる場合がよくあります。\n例えば、データベースでは顧客の誕生日を UNIX タイムスタンプで保存している (まあ、あまり良い設計ではありませんが)\nけれども、ほとんどの場合において誕生日を `'YYYY/MM/DD'` という形式の文字列として操作したい、というような場合です。\nこの目的を達するために、次のように、`Customer` アクティブ・レコード・クラスにおいて *データ変換*\nメソッドを定義することが出来ます。\n\n```php\nclass Customer extends ActiveRecord\n{\n    // ...\n\n    public function getBirthdayText()\n    {\n        return date('Y/m/d', $this->birthday);\n    }\n    \n    public function setBirthdayText($value)\n    {\n        $this->birthday = strtotime($value);\n    }\n}\n```\n\nこのようにすれば、PHP コードにおいて、`$customer->birthday` にアクセスする代りに、`$customer->birthdayText` にアクセスすれば、\n顧客の誕生日を `'YYYY/MM/DD'` の形式で入力および表示することが出来ます。\n\n> Tip: 上記は、一般にデータの変換を達成するための簡単な方法を示すためのものです。\n> 日付の値については、Yii は、[DateValidator](tutorial-core-validators.md#date) と DatePicker ウィジェットを使用するという、より良い方法を提供しています。\n> DatePicker については、[JUI ウィジェットのセクション](widget-jui#datepicker-date-input) で説明されています。\n\n\n### データを配列に取得する <span id=\"data-in-arrays\"></span>\n\nデータをアクティブ・レコード・オブジェクトの形で取得するのは便利であり柔軟ですが、大きなメモリ使用量を要するために、\n大量のデータを取得しなければならない場合は、必ずしも望ましい方法ではありません。\nそういう場合は、クエリ・メソッドを実行する前に [[yii\\db\\ActiveQuery::asArray()|asArray()]] を呼ぶことによって、PHP 配列を使ってデータを取得することが出来ます。\n\n```php\n// すべての顧客を返す\n// 各顧客は連想配列として返される\n$customers = Customer::find()\n    ->asArray()\n    ->all();\n```\n\n> Note: このメソッドはメモリを節約してパフォーマンスを向上させますが、低レベルの DB 抽象レイヤに近いものであり、\n  あなたはアクティブ・レコードの機能のほとんどを失うことになります。非常に重要な違いが、カラムの値のデータ型に現れます。\n  アクティブ・レコード・インスタンスとしてデータを返す場合、カラムの値は実際のカラムの型に従って自動的に型キャストされます。\n  一方、配列としてデータを返す場合は、実際のカラムの型に関係なく、カラムの値は文字列になります。\n  なぜなら、何も処理をしない場合の PDO の結果は文字列だからです。\n\n\n### データをバッチ・モードで取得する <span id=\"data-in-batches\"></span>\n\n[クエリ・ビルダ](db-query-builder.md) において、大量のデータをデータベースから検索する場合に、メモリ使用量を最小化するために\n*バッチ・クエリ* を使うことが出来るということを説明しました。おなじテクニックをアクティブ・レコードでも使うことが出来ます。例えば、\n\n```php\n// 一度に 10 人の顧客を読み出す\nforeach (Customer::find()->batch(10) as $customers) {\n    // $customers は 10 以下の Customer オブジェクトの配列\n}\n\n// 一度に 10 人の顧客を読み出して、一人ずつ反復する\nforeach (Customer::find()->each(10) as $customer) {\n    // $customer は Customer オブジェクト\n}\n\n// イーガー・ローディングをするバッチ・クエリ\nforeach (Customer::find()->with('orders')->each() as $customer) {\n    // $customer は 'orders' リレーションを投入された Customer オブジェクト\n}\n```\n\n\n## データを保存する <span id=\"inserting-updating-data\"></span>\n\nアクティブ・レコードを使えば、次のステップを踏んで簡単にデータをデータベースに保存することが出来ます。\n\n1. アクティブ・レコードのインスタンスを準備する\n2. アクティブ・レコードの属性に新しい値を割り当てる\n3. [[yii\\db\\ActiveRecord::save()]] を呼んでデータをデータベースに保存する\n\n例えば、\n\n```php\n// 新しいデータ行を挿入する\n$customer = new Customer();\n$customer->name = 'James';\n$customer->email = 'james@example.com';\n$customer->save();\n\n// 既存のデータ行を更新する\n$customer = Customer::findOne(123);\n$customer->email = 'james@newexample.com';\n$customer->save();\n```\n\n[[yii\\db\\ActiveRecord::save()|save()]] メソッドは、アクティブ・レコード・インスタンスの状態に従って、データ行を挿入するか、\nまたは、更新することが出来ます。インスタンスが `new` 演算子によって新しく作成されたものである場合は、\n[[yii\\db\\ActiveRecord::save()|save()]] を呼び出すと、新しい行が挿入されます。インスタンスがクエリ・メソッドの結果である場合は、\n[[yii\\db\\ActiveRecord::save()|save()]] を呼び出すと、そのインスタンスと関連付けられた行が更新されます。\n\nアクティブ・レコード・インスタンスの二つの状態は、その [[yii\\db\\ActiveRecord::isNewRecord|isNewRecord]]\nプロパティの値をチェックすることによって区別することが出来ます。\n下記のように、このプロパティは [[yii\\db\\ActiveRecord::save()|save()]] によっても内部的に使用されています。\n\n```php\npublic function save($runValidation = true, $attributeNames = null)\n{\n    if ($this->getIsNewRecord()) {\n        return $this->insert($runValidation, $attributeNames);\n    } else {\n        return $this->update($runValidation, $attributeNames) !== false;\n    }\n}\n```\n\n> Tip: [[yii\\db\\ActiveRecord::insert()|insert()]] または [[yii\\db\\ActiveRecord::update()|update()]] を直接に呼んで、\n  行を挿入または更新することも出来ます。\n\n\n### データの検証 <span id=\"data-validation\"></span>\n\n[[yii\\db\\ActiveRecord]] は [[yii\\base\\Model]] を拡張したものですので、同じ [データ検証](input-validation.md) 機能を共有しています。\n[[yii\\db\\ActiveRecord::rules()|rules()]] メソッドをオーバーライドすることによって検証規則を宣言し、\n[[yii\\db\\ActiveRecord::validate()|validate()]] メソッドを呼ぶことによってテータの検証を実行することが出来ます。\n\n[[yii\\db\\ActiveRecord::save()|save()]] を呼ぶと、デフォルトでは [[yii\\db\\ActiveRecord::validate()|validate()]] が自動的に呼ばれます。\n検証が通った時だけ、実際にデータが保存されます。\n検証が通らなかった時は単に `false` が返され、[[yii\\db\\ActiveRecord::errors|errors]] プロパティをチェックして検証エラー・メッセージを取得することが出来ます。\n\n> Tip: データが検証を必要としないことが確実である場合 (例えば、データが信頼できるソースに由来するものである場合) は、\n  検証をスキップするために `save(false)` を呼ぶことが出来ます。\n\n\n### 一括代入 <span id=\"massive-assignment\"></span>\n\n通常の [モデル](structure-models.md) と同じように、アクティブ・レコードのインスタンスも  [一括代入機能](structure-models.md#massive-assignment) を享受することが出来ます。\nこの機能を使うと、下記で示されているように、一つの PHP 文で、アクティブ・レコード・インスタンスの複数の属性に値を割り当てることが出来ます。\nただし、[安全な属性](structure-models.md#safe-attributes) だけが一括代入が可能であることを記憶しておいてください。\n\n```php\n$values = [\n    'name' => 'James',\n    'email' => 'james@example.com',\n];\n\n$customer = new Customer();\n\n$customer->attributes = $values;\n$customer->save();\n```\n\n\n### カウンタを更新する <span id=\"updating-counters\"></span>\n\nデータベース・テーブルのあるカラムの値を増加・減少させるのは、よくある仕事です。私たちはそのようなカラムをカウンタ・カラムと呼んでいます。\n[[yii\\db\\ActiveRecord::updateCounters()|updateCounters()]] を使って一つまたは複数のカウンタ・カラムを更新することが出来ます。\n例えば、\n\n```php\n$post = Post::findOne(100);\n\n// UPDATE `post` SET `view_count` = `view_count` + 1 WHERE `id` = 100\n$post->updateCounters(['view_count' => 1]);\n```\n\n> Note: カウンタ・カラムを更新するのに [[yii\\db\\ActiveRecord::save()]] を使うと、不正確な結果になってしまう場合があります。\n  というのは、同じカウンタの値を読み書きする複数のリクエストによって、同一のカウンタが保存される可能性があるからです。\n\n\n### ダーティな属性 <span id=\"dirty-attributes\"></span>\n\n[[yii\\db\\ActiveRecord::save()|save()]] を呼んでアクティブ・レコード・インスタンスを保存すると、*ダーティな属性* だけが保存されます。\n属性は、DB からロードされた後、または、最後に保存された後にその値が変更されると、*ダーティ* であると見なされます。\nただし、データ検証は、アクティブ・レコード・インスタンスがダーティな属性を持っているかどうかに関係なく実施される\nことに注意してください。\n\nアクティブ・レコードはダーティな属性のリストを自動的に保守します。\nそうするために、一つ前のバージョンの属性値を保持して、最新のバージョンと比較します。\n[[yii\\db\\ActiveRecord::getDirtyAttributes()]] を呼ぶと、現在ダーティである属性を取得することが出来ます。\nまた、[[yii\\db\\ActiveRecord::markAttributeDirty()]] を呼んで、ある属性をダーティであると明示的にマークすることも出来ます。\n\n最新の修正を受ける前の属性値を知りたい場合は、[[yii\\db\\ActiveRecord::getOldAttributes()|getOldAttributes()]]\nまたは [[yii\\db\\ActiveRecord::getOldAttribute()|getOldAttribute()]] を呼ぶことが出来ます。\n\n> Note: 新旧の値は `===` 演算子を使って比較されるため、同じ値を持っていても型が違うとダーティであると見なされます。\n> このことは、モデルが HTML フォームからユーザの入力を受け取るときにしばしば生じます。\n> HTML フォームでは全ての値が文字列として表現されるからです。\n> 入力値が正しい型、例えば整数値となることを保証するために、`['attributeName', 'filter', 'filter' => 'intval']` のように\n> [検証フィルタ](input-validation.md#data-filtering) を適用することが出来ます。\n> このフィルタは、[intval()](https://www.php.net/manual/ja/function.intval.php), [floatval()](https://www.php.net/manual/ja/function.floatval.php),\n> [boolval](https://www.php.net/manual/ja/function.boolval.php) など、PHP の全てのタイプキャスト関数で動作します。\n\n### デフォルト属性値 <span id=\"default-attribute-values\"></span>\n\nあなたのテーブルのカラムの中には、データベースでデフォルト値が定義されているものがあるかも知れません。\nそして、場合によっては、アクティブ・レコード・インスタンスのウェブ・フォームに、そういうデフォルト値をあらかじめ投入したいことがあるでしょう。\n同じデフォルト値を繰り返して書くことを避けるために、[[yii\\db\\ActiveRecord::loadDefaultValues()|loadDefaultValues()]]\nを呼んで、DB で定義されたデフォルト値を対応するアクティブ・レコードの属性に投入することが出来ます。\n\n```php\n$customer = new Customer();\n$customer->loadDefaultValues();\n// $customer->xyz には、\"xyz\" カラムを定義するときに宣言されたデフォルト値が割り当てられる\n```\n\n\n### 属性の型キャスト <span id=\"attributes-typecasting\"></span>\n\n[[yii\\db\\ActiveRecord]] は、クエリの結果を投入されるときに、[データベース・テーブル・スキーマ](db-dao.md#database-schema)\nからの情報を使って、自動的な型キャストを実行します。これによって、整数として宣言されているテーブルカラムから取得されるデータを\nアクティブ・レコードのインスタンスでも PHP の integer として投入し、真偽値として宣言されているデータを boolean として投入することが出来るようになっています。\nしかしながら、型キャストのメカニズムには、いくつかの制約があります。\n\n* 浮動小数点数値は変換されず、文字列として表されます。そうしないと精度が失われるおそれがあるからです。\n* 整数値の変換は、あなたが使っているオペレーティング・システムの整数の大きさに依存します。具体的に言うと、\n  'unsigned integer' または 'big integer' として宣言されたカラムの値は、64-bit オペレーティングシステムでのみ PHP の integer に変換されます。\n  32-bit オペレーティングシステムでは、文字列として表されます。\n\n属性の型キャストは、アクティブ・レコードのインスタンスにクエリの結果から値を投入するときだけしか実行されないことに注意してください。\nHTTP リクエストから値をロードしたり、プロパティにアクセスして直接に値を設定したりするときには、自動的な変換は行われません。\nまた、アクティブ・レコードのデータ保存のための SQL 文を準備する際にもテーブル・スキーマが使用されて、\n値が正しい型でクエリにバインドされることを保証します。\nしかし、アクティブ・レコードのインスタンスの属性値は保存の過程において変換されることはありません。\n\n> Tip: アクティブ・レコードの検証や保存の際の属性型キャストを楽にするために\n  [[yii\\behaviors\\AttributeTypecastBehavior]] を使うことが出来ます。\n\n2.0.14 以降、Yii のアクティブ・レコードは、JSON や多次元配列のような複雑な型をサポートしています。\n\n#### MySQL および PostgreSQL における JSON\n\nデータが取得された後、JSON カラムの値は標準的な JSON デコード規則に従って、\n自動的に JSON からデコードされます。\n\nアクティブ・レコードは、属性値を JSON カラムに保存するために [[yii\\db\\JsonExpression|JsonExpression]]\nオブジェクトを自動的に生成します。このオブジェクトが [クエリ・ビルダ](db-query-builder.md) レベルで JSON 文字列にエンコードされます。\n\n#### PostgreSQL における配列\n\nデータが取得された後、配列カラムの値は PgSQL 記法から自動的に [[yii\\db\\ArrayExpression|ArrayExpression]] オブジェクトにデコードされます。\nこのオブジェクトは PHP の `ArrayAccess` インタフェイスを実装しているため、これを配列として使うこと事が出来ます。また、`->getValue()` を呼んで配列そのものを取得することも出来ます。\n\nアクティブ・レコードは、属性値を配列カラムに保存するために [[yii\\db\\ArrayExpression|ArrayExpression]]\nオブジェクトを生成します。このオブジェクトが [クエリ・ビルダ](db-query-builder.md) のレベルで配列を表す PgSQL 文字列にエンコードされます。\n\nJSON カラムに対して条件を使用することも出来ます。\n\n```php\n$query->andWhere(['=', 'json', new ArrayExpression(['foo' => 'bar'])\n```\n\n式を構築するシステムについて更に学習するためには [クエリ・ビルダ – 特製の条件や式を追加する](db-query-builder.md#adding-custom-conditions-and-expressions)\nという記事を参照して下さい。\n\n### 複数の行を更新する <span id=\"updating-multiple-rows\"></span>\n\n上述のメソッドは、すべて、個別のアクティブ・レコード・インスタンスに対して作用し、個別のテーブル行を挿入したり更新したりするものです。\n複数の行を同時に更新するためには、代りに、スタティックなメソッドである [[yii\\db\\ActiveRecord::updateAll()|updateAll()]]\nを呼ばなければなりません。\n\n```php\n// UPDATE `customer` SET `status` = 1 WHERE `email` LIKE `%@example.com`\nCustomer::updateAll(['status' => Customer::STATUS_ACTIVE], ['like', 'email', '@example.com']);\n```\n\n同様に、[[yii\\db\\ActiveRecord::updateAllCounters()|updateAllCounters()]] を呼んで、\n複数の行のカウンタカラムを同時に更新することが出来ます。\n\n```php\n// UPDATE `customer` SET `age` = `age` + 1\nCustomer::updateAllCounters(['age' => 1]);\n```\n\n\n## データを削除する <span id=\"deleting-data\"></span>\n\n一行のデータを削除するためには、最初にその行に対応するアクティブ・レコード・インスタンスを取得して、\n次に [[yii\\db\\ActiveRecord::delete()]] メソッドを呼びます。\n\n```php\n$customer = Customer::findOne(123);\n$customer->delete();\n```\n\n[[yii\\db\\ActiveRecord::deleteAll()]] を呼んで、複数またはすべてのデータ行を削除することが出来ます。例えば、\n\n```php\nCustomer::deleteAll(['status' => Customer::STATUS_INACTIVE]);\n```\n\n> Note: [[yii\\db\\ActiveRecord::deleteAll()|deleteAll()]] を呼ぶときは、十分に注意深くしてください。\n  なぜなら、条件の指定を間違うと、あなたのテーブルからすべてのデータを完全に消し去ってしまうことになるからです。\n\n\n## アクティブ・レコードのライフサイクル <span id=\"ar-life-cycles\"></span>\n\nアクティブ・レコードがさまざまな目的で使用される場合のそれぞれのライフサイクルを理解しておくことは重要なことです。\nそれぞれのライフサイクルにおいては、特定の一続きのメソッドが呼び出されます。\nそして、これらのメソッドをオーバーライドして、ライフサイクルをカスタマイズするチャンスを得ることが出来ます。\nまた、ライフサイクルの中でトリガされる特定のアクティブ・レコード・イベントに反応して、あなたのカスタム・コードを挿入することも出来ます。\nこれらのイベントが特に役に立つのは、アクティブ・レコードのライフサイクルをカスタマイズする必要のあるアクティブ・レコード・[ビヘイビア](concept-behaviors.md) を開発する際です。\n\n次に、さまざまなアクティブ・レコードのライフサイクルと、\nそのライフサイクルに含まれるメソッドやイベントを要約します。\n\n\n### 新しいインスタンスのライフサイクル <span id=\"new-instance-life-cycle\"></span>\n\n`new` 演算子によって新しいアクティブ・レコード・インスタンスを作成する場合は、次のライフサイクルを経ます。\n\n1. クラスのコンストラクタ。\n2. [[yii\\db\\ActiveRecord::init()|init()]]: [[yii\\db\\ActiveRecord::EVENT_INIT|EVENT_INIT]] イベントをトリガ。\n\n\n### データをクエリする際のライフサイクル <span id=\"querying-data-life-cycle\"></span>\n\n[クエリ・メソッド](#querying-data) のどれか一つによってデータをクエリする場合は、\n新しくデータを投入されるアクティブ・レコードは次のライフサイクルを経ます。\n\n1. クラスのコンストラクタ。\n2. [[yii\\db\\ActiveRecord::init()|init()]]: [[yii\\db\\ActiveRecord::EVENT_INIT|EVENT_INIT]] イベントをトリガ。\n3. [[yii\\db\\ActiveRecord::afterFind()|afterFind()]]: [[yii\\db\\ActiveRecord::EVENT_AFTER_FIND|EVENT_AFTER_FIND]] イベントをトリガ。\n\n\n### データを保存する際のライフサイクル <span id=\"saving-data-life-cycle\"></span>\n\n[[yii\\db\\ActiveRecord::save()|save()]] を呼んでアクティブ・レコード・インスタンスを挿入または更新する場合は、\n次のライフサイクルを経ます。\n\n1. [[yii\\db\\ActiveRecord::beforeValidate()|beforeValidate()]]: [[yii\\db\\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]] イベントをトリガ。\n   このメソッドが `false` を返すか、[[yii\\base\\ModelEvent::isValid]] が `false` であった場合、\n   残りのステップはスキップされる。\n2. データ検証を実行。データ検証が失敗した場合、3 より後のステップはスキップされる。\n3. [[yii\\db\\ActiveRecord::afterValidate()|afterValidate()]]: [[yii\\db\\ActiveRecord::EVENT_AFTER_VALIDATE|EVENT_AFTER_VALIDATE]]\n   イベントをトリガ。\n4. [[yii\\db\\ActiveRecord::beforeSave()|beforeSave()]]: [[yii\\db\\ActiveRecord::EVENT_BEFORE_INSERT|EVENT_BEFORE_INSERT]]\n   または [[yii\\db\\ActiveRecord::EVENT_BEFORE_UPDATE|EVENT_BEFORE_UPDATE]] イベントをトリガ。\n   このメソッドが `false` を返すか、[[yii\\base\\ModelEvent::isValid]] が `false` であった場合、\n   残りのステップはスキップされる。\n5. 実際のデータの挿入または更新を実行。\n6. [[yii\\db\\ActiveRecord::afterSave()|afterSave()]]: [[yii\\db\\ActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]]\n    または [[yii\\db\\ActiveRecord::EVENT_AFTER_UPDATE|EVENT_AFTER_UPDATE]]\n    イベントをトリガ。\n   \n\n### データを削除する際のライフサイクル <span id=\"deleting-data-life-cycle\"></span>\n\n[[yii\\db\\ActiveRecord::delete()|delete()]] を呼んでアクティブ・レコード・インスタンスを削除する際は、\n次のライフサイクルを経ます。\n\n1. [[yii\\db\\ActiveRecord::beforeDelete()|beforeDelete()]]: [[yii\\db\\ActiveRecord::EVENT_BEFORE_DELETE|EVENT_BEFORE_DELETE]]\n   イベントをトリガ。このメソッドが `false` を返すか、[[yii\\base\\ModelEvent::isValid]] が `false` であった場合は、\n   残りのステップはスキップされる。\n2. 実際のデータの削除を実行。\n3. [[yii\\db\\ActiveRecord::afterDelete()|afterDelete()]]: [[yii\\db\\ActiveRecord::EVENT_AFTER_DELETE|EVENT_AFTER_DELETE]]\n   イベントをトリガ。\n\n\n> Note: 次のメソッドを呼んだ場合は、いずれの場合も、上記のライフサイクルのどれかを開始させることはありません。\n> これらのメソッドは、レコード単位ではなく、データベース上で直接に動作するためです。\n>\n> - [[yii\\db\\ActiveRecord::updateAll()]] \n> - [[yii\\db\\ActiveRecord::deleteAll()]]\n> - [[yii\\db\\ActiveRecord::updateCounters()]] \n> - [[yii\\db\\ActiveRecord::updateAllCounters()]] \n\n> Note: パフォーマンスを考慮して、DI(依存注入) はデフォルトではサポートされていません。必要であれば、\n> [[Yii::createObject()]] によってクラスのインスタンス生成をするように [[yii\\db\\ActiveRecord::instantiate()|instantiate()]] メソッドをオーバーライドして、サポートを追加することが出来ます。\n> \n> ```php\n> public static function instantiate($row)\n> {\n>     return Yii::createObject(static::class);\n> }\n> ```\n\n### データをリフレッシュする際のライフサイクル <span id=\"refreshing-data-life-cycle\"></span>\n\n[[yii\\db\\ActiveRecord::refresh()|refresh()]] を呼んでアクティブ・レコード・インスタンスをリフレッシュする際は、リフレッシュが成功してメソッドが `true` を返すと\n[[yii\\db\\ActiveRecord::EVENT_AFTER_REFRESH|EVENT_AFTER_REFRESH]] イベントがトリガされます。\n\n\n## トランザクションを扱う <span id=\"transactional-operations\"></span>\n\nアクティブ・レコードを扱う際には、二つの方法で [トランザクション](db-dao.md#performing-transactions) を処理することができます。\n\n最初の方法は、次に示すように、アクティブ・レコードのメソッドの呼び出しを明示的にトランザクションのブロックで囲む方法です。\n\n```php\n$customer = Customer::findOne(123);\n\nCustomer::getDb()->transaction(function($db) use ($customer) {\n    $customer->id = 200;\n    $customer->save();\n    // ... 他の DB 操作 ...\n});\n\n// あるいは、別の方法\n\n$transaction = Customer::getDb()->beginTransaction();\ntry {\n    $customer->id = 200;\n    $customer->save();\n    // ... 他の DB 操作 ...\n    $transaction->commit();\n} catch(\\Exception $e) {\n    $transaction->rollBack();\n    throw $e;\n} catch(\\Throwable $e) {\n    $transaction->rollBack();\n    throw $e;\n}\n```\n\n> Note: 上記のコードでは、PHP 5.x と PHP 7.x との互換性のために、二つの catch ブロックを持っています。\n> `\\Exception` は PHP 7.0 以降では、[`\\Throwable` インタフェイス](https://www.php.net/manual/ja/class.throwable.php) を実装しています。\n> 従って、あなたのアプリケーションが PHP 7.0 以上しか使わない場合は、`\\Exception` の部分を省略することが出来ます。\n\n第二の方法は、トランザクションのサポートが必要な DB 操作を [[yii\\db\\ActiveRecord::transactions()]]\nメソッドに列挙するという方法です。\n\n```php\nclass Post extends \\yii\\db\\ActiveRecord\n{\n    public function transactions()\n    {\n        return [\n            'admin' => self::OP_INSERT,\n            'api' => self::OP_INSERT | self::OP_UPDATE | self::OP_DELETE,\n            // 上は次と等価\n            // 'api' => self::OP_ALL,\n        ];\n    }\n}\n```\n\n[[yii\\db\\ActiveRecord::transactions()]] メソッドが返す配列では、キーは [シナリオ](structure-models.md#scenarios) の名前であり、\n値はトランザクションで囲まれるべき操作でなくてはなりません。\nいろいろな DB 操作を参照するのには、次の定数を使わなければなりません。\n\n* [[yii\\db\\ActiveRecord::OP_INSERT|OP_INSERT]]: [[yii\\db\\ActiveRecord::insert()|insert()]] によって実行される挿入の操作。\n* [[yii\\db\\ActiveRecord::OP_UPDATE|OP_UPDATE]]: [[yii\\db\\ActiveRecord::update()|update()]] によって実行される更新の操作。\n* [[yii\\db\\ActiveRecord::OP_DELETE|OP_DELETE]]: [[yii\\db\\ActiveRecord::delete()|delete()]] によって実行される削除の操作。\n\n複数の操作を示すためには、`|` を使って上記の定数を連結してください。\nショートカット定数 [[yii\\db\\ActiveRecord::OP_ALL|OP_ALL]] を使って、上記の三つの操作すべてを示すことも出来ます。\n\nこのメソッドを使って生成されたトランザクションは、[[yii\\db\\ActiveRecord::beforeSave()|beforeSave()]] を呼ぶ前に開始され、\n[[yii\\db\\ActiveRecord::afterSave()|afterSave()]] を実行した後にコミットされます。\n\n## 楽観的ロック <span id=\"optimistic-locks\"></span>\n\n楽観的ロックは、一つのデータ行が複数のユーザによって更新されるときに発生しうる衝突を回避するための方法です。\n例えば、ユーザ A と ユーザ B が 同時に同じ wiki 記事を編集しており、ユーザ A が自分の編集結果を保存した後に、\nユーザ B も自分の編集結果を保存しようとして「保存」ボタンをクリックする場合を考えてください。\nユーザ B は、実際には古くなったバージョンの記事に対する操作をしようとしていますので、彼が記事を保存するのを防止し、\n彼に何らかのヒント・メッセージを表示する方法があることが望まれます。\n\n楽観的ロックは、あるカラムを使って各行のバージョン番号を記録するという方法によって、上記の問題を解決します。\n古くなったバージョン番号とともに行を保存しようとすると、[[yii\\db\\StaleObjectException]] 例外が投げられて、\n行が保存されるのが防止されます。\n楽観的ロックは、 [[yii\\db\\ActiveRecord::update()]] または [[yii\\db\\ActiveRecord::delete()]]\nメソッドを使って既存の行を更新または削除しようとする場合にだけサポートされます。\n\n楽観的ロックを使用するためには、次のようにします。\n\n1. アクティブ・レコード・クラスと関連付けられている DB テーブルに、各行のバージョン番号を保存するカラムを作成します。\n   カラムは長倍精度整数 (big integer) タイプでなければなりません (MySQL では `BIGINT DEFAULT 0` です)。\n2. [[yii\\db\\ActiveRecord::optimisticLock()]] メソッドをオーバーライドして、このカラムの名前を返すようにします。\n3. あなたのモデル・クラスの中で [[\\yii\\behaviors\\OptimisticLockBehavior|OptimisticLockBehavior]] を実装し、受信したリクエストからその値を自動的に解析できるようにします。\n   [[\\yii\\behaviors\\OptimisticLockBehavior|OptimisticLockBehavior]] が検証を処理すべきですので、バージョンの属性は検証規則から削除します。\n4. ユーザ入力を収集するウェブフォームに、更新されるレコードの現在のバージョン番号を保持する隠しフィールドを追加します。\n5. アクティブ・レコードを使って行の更新を行うコントローラ・アクションにおいて、[[\\yii\\db\\StaleObjectException]] 例外を捕捉して、\n   衝突を解決するために必要なビジネス・ロジック (例えば、変更をマージしたり、データの陳腐化を知らせたり) を実装します。\n\n例えば、バージョン番号のカラムが `version` と名付けられているとすると、\n次のようなコードによって楽観的ロックを実装することが出来ます。\n\n```php\n// ------ ビューのコード -------\n\nuse yii\\helpers\\Html;\n\n// ... 他の入力フィールド\necho Html::activeHiddenInput($model, 'version');\n\n\n// ------ コントローラのコード -------\n\nuse yii\\db\\StaleObjectException;\n\npublic function actionUpdate($id)\n{\n    $model = $this->findModel($id);\n\n    try {\n        if ($model->load(Yii::$app->request->post()) && $model->save()) {\n            return $this->redirect(['view', 'id' => $model->id]);\n        } else {\n            return $this->render('update', [\n                'model' => $model,\n            ]);\n        }\n    } catch (StaleObjectException $e) {\n        // 衝突を解決するロジック\n    }\n}\n\n// ------ モデルのコード -------\n\nuse yii\\behaviors\\OptimisticLockBehavior;\n\npublic function behaviors()\n{\n    return [\n        OptimisticLockBehavior::class,\n    ];\n}\n\npublic function optimisticLock()\n{\n    return 'version';\n}\n\n```\n> Note: [[\\yii\\behaviors\\OptimisticLockBehavior|OptimisticLockBehavior]] は、ユーザが正しいバージョン番号を送信したときにだけ\n> レコードが保存されるという事を保証します。そして、そのために、[[\\yii\\web\\Request::getBodyParam()|getBodyParam()]] の結果を直接に解析します。\n> そこで、あなたのモデル・クラスを拡張して、親モデルで第2段階を行い、ビヘイビアのアタッチ(第3段階)を子モデルで行うようにすると便利でしょう。\n> そうすれば、一方を内部使用のためだけのインスタンスとして使うことが出来、他方をエンド・ユーザの入力の受信に責任を持つモデルとしてコントローラと結びつける事が出来ます。\n> もう一つのやり方としては、[[\\yii\\behaviors\\OptimisticLockBehavior::$value|value]] プロパティを構成して独自のロジックを実装することも可能です。\n\n\n## リレーショナル・データを扱う <span id=\"relational-data\"></span>\n\n個々のデータベース・テーブルを扱うだけでなく、アクティブ・レコードは関連したテーブルのデータも一緒に読み出して、\n主たるデータを通して簡単にアクセス出来るようにすることが出来ます。\n例えば、一人の顧客は一つまたは複数の注文を発することがあり得ますので、顧客のデータは注文のデータと関連を持っていることになります。\nこのリレーションが適切に宣言されていれば、`$customer->orders` という式を使って顧客の注文情報にアクセスすることが出来ます。\n`$customer->orders` は、顧客の注文情報を `Order` アクティブ・レコード・インスタンスの配列として返してくれます。\n\n\n### リレーションを宣言する <span id=\"declaring-relations\"></span>\n\nアクティブ・レコードを使ってリレーショナル・データを扱うためには、最初に、アクティブ・レコード・クラスの中でリレーションを宣言する必要があります。\nこれは、以下のように、関心のあるそれぞれのリレーションについて *リレーション・メソッド* を宣言するだけの簡単な作業です。\n\n```php\nclass Customer extends ActiveRecord\n{\n    // ...\n\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n}\n\nclass Order extends ActiveRecord\n{\n    // ...\n\n    public function getCustomer()\n    {\n        return $this->hasOne(Customer::class, ['id' => 'customer_id']);\n    }\n}\n```\n\n上記のコードでは、`Customer` クラスのために `orders` リレーションを宣言し、`Order` クラスのために `customer`\nリレーションを宣言しています。\n\n各リレーション・メソッドは `getXyz` という名前にしなければなりません。ここで `xyz` (最初の文字は小文字です) が *リレーション名* と呼ばれます。\nリレーション名は *大文字と小文字を区別する* ことに注意してください。\n\nリレーションを宣言する際には、次の情報を指定しなければなりません。\n\n- リレーションの多重性: [[yii\\db\\ActiveRecord::hasMany()|hasMany()]] または [[yii\\db\\ActiveRecord::hasOne()|hasOne()]]\n  のどちらかを呼ぶことによって指定されます。\n  上記の例では、リレーションの宣言において、顧客は複数の注文を持ち得るが、一方、注文は一人の顧客しか持たない、ということが容易に読み取れます。\n- 関連するアクティブ・レコード・クラスの名前: [[yii\\db\\ActiveRecord::hasMany()|hasMany()]] または [[yii\\db\\ActiveRecord::hasOne()|hasOne()]]\n  の最初のパラメータとして指定されます。\n  クラス名を取得するのに `Xyz::class` を呼ぶのが推奨されるプラクティスです。\n  そうすれば、IDE の自動補完のサポートを得ることことが出来るだけでなく、コンパイル段階でエラーを検出することが出来ます。\n- 二つの型のデータ間のリンク: 二つの型のデータの関連付けに用いられるカラムを指定します。\n  配列の値は主たるデータ (リレーションを宣言しているアクティブ・レコード・クラスによって表されるデータ) のカラムであり、\n  配列のキーは関連するデータのカラムです。\n\n  これを記憶するための簡単な規則は、上の例で見るように、関連するアクティブ・レコードを書いた直後に、それに属するカラムを\n  続けて書く、ということです。ご覧のように、`customer_id` は `Order` のプロパティであり、\n  `id` は`Customer` のプロパティです。\n  \n> Warning: リレーション名としては `relation` は予約済みで使えません。これを使うと `ArgumentCountError` となります。\n\n\n### リレーショナル・データにアクセスする <span id=\"accessing-relational-data\"></span>\n\nリレーションを宣言した後は、リレーション名を通じてリレーショナル・データにアクセスすることが出来ます。\nこれは、リレーション・メソッドによって定義されるオブジェクト・[プロパティ](concept-properties.md) にアクセスするのと同様です。\nこのため、これを *リレーション・プロパティ* と呼びます。例えば、\n\n```php\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123\n// $orders is an array of Order objects\n$orders = $customer->orders;\n```\n\n> Info: `xyz` という名前のリレーションを getter メソッド `getXyz()` によって宣言すると、`xyz` を\n  [オブジェクト・プロパティ](concept-properties.md) のようにアクセスすることが出来るようになります。名前は大文字と小文字を区別することに注意してください。\n\nリレーションが [[yii\\db\\ActiveRecord::hasMany()|hasMany()]] によって宣言されている場合は、\nこのリレーション・プロパティにアクセスすると、関連付けられたアクティブ・レコード・インスタンスの配列が返されます。\nリレーションが [[yii\\db\\ActiveRecord::hasOne()|hasOne()]] によって宣言されている場合は、\nこのリレーション・プロパティにアクセスすると、関連付けられたアクティブ・レコード・インスタンスか、関連付けられたデータが見つからないときは `null` が返されます。\n\nリレーション・プロパティに最初にアクセスしたときは、上記の例で示されているように、SQL 文が実行されます。\nその同じプロパティに再びアクセスしたときは、SQL 文を再実行することなく、以前の結果が返されます。\nSQL 文の再実行を強制するためには、まず、リレーション・プロパティの割り当てを解除 (unset) しなければなりません :\n`unset($customer->orders)`。\n\n> Note: リレーション・プロパティの概念は [オブジェクト・プロパティ](concept-properties.md) の機能と同一であるように見えますが、一つ、重要な相違点があります。\n> 通常のオブジェクト・プロパティでは、プロパティの値はそれを定義する getter メソッドと同じ型を持ちます。\n> しかし、リレーション・プロパティにアクセスすると [[yii\\db\\ActiveRecord]] のインスタンスまたはその配列が返されるのに対して、\n> リレーション・メソッドは [[yii\\db\\ActiveQuery]] のインスタンスを返します。\n> \n> ```php\n> $customer->orders; // `Order` オブジェクトの配列\n> $customer->getOrders(); // ActiveQuery のインスタンス\n> ```\n> \n> このことは、次のセクションで説明するように、カスタマイズしたクエリを作成するのに役に立ちます。\n\n\n### 動的なリレーショナル・クエリ <span id=\"dynamic-relational-query\"></span>\n\nリレーション・メソッドは [[yii\\db\\ActiveQuery]] のインスタンスを返すため、DB クエリを実行する前に、\nクエリ構築メソッドを使ってこのクエリを更に修正することが出来ます。例えば、\n\n```php\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123 AND `subtotal` > 200 ORDER BY `id`\n$orders = $customer->getOrders()\n    ->where(['>', 'subtotal', 200])\n    ->orderBy('id')\n    ->all();\n```\n\nリレーション・プロパティにアクセスする場合と違って、リレーション・メソッドによって動的なリレーショナル・クエリを実行する場合は、\n同じ動的なリレーショナル・クエリが以前に実行されたことがあっても、毎回、SQL 文が実行されます。\n\nさらに進んで、もっと簡単に動的なリレーショナル・クエリを実行できるように、リレーションの宣言をパラメータ化したい場合もあるでしょう。\n例えば、`bigOrders` リレーションを下記のように宣言することが出来ます。\n\n```php\nclass Customer extends ActiveRecord\n{\n    public function getBigOrders($threshold = 100)\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id'])\n            ->where('subtotal > :threshold', [':threshold' => $threshold])\n            ->orderBy('id');\n    }\n}\n```\n\nこれによって、次のようなリレーショナル・クエリを実行することが出来るようになります。\n\n```php\n// SELECT * FROM `order` WHERE `customer_id` = 123 AND `subtotal` > 200 ORDER BY `id`\n$orders = $customer->getBigOrders(200)->all();\n\n// SELECT * FROM `order` WHERE `customer_id` = 123 AND `subtotal` > 100 ORDER BY `id`\n$orders = $customer->bigOrders;\n```\n\n\n### 中間テーブルによるリレーション <span id=\"junction-table\"></span>\n\nデータベースの設計において、二つの関連するテーブル間の多重性が多対多である場合は、通常、\n[中間テーブル](https://ja.wikipedia.org/wiki/%E9%80%A3%E6%83%B3%E3%82%A8%E3%83%B3%E3%83%86%E3%82%A3%E3%83%86%E3%82%A3) が導入されます。\n例えば、`order` テーブルと `item` テーブルは、`order_item` と言う名前の中間テーブルによって関連付けることが出来ます。\nこのようにすれば、一つの注文を複数の商品に対応させ、また、一つの商品を複数の注文に対応させることが出来ます。\n\nこのようなリレーションを宣言するときは、[[yii\\db\\ActiveQuery::via()|via()]] または [[yii\\db\\ActiveQuery::viaTable()|viaTable()]]\nのどちらかを呼んで中間テーブルを指定します。\n[[yii\\db\\ActiveQuery::via()|via()]] と [[yii\\db\\ActiveQuery::viaTable()|viaTable()]] の違いは、\n前者が既存のリレーション名の形式で中間テーブルを指定するのに対して、後者は中間テーブルを直接に指定する、という点です。例えば、\n\n```php\nclass Order extends ActiveRecord\n{\n    public function getItems()\n    {\n        return $this->hasMany(Item::class, ['id' => 'item_id'])\n            ->viaTable('order_item', ['order_id' => 'id']);\n    }\n}\n```\n\nあるいは、また、\n\n```php\nclass Order extends ActiveRecord\n{\n    public function getOrderItems()\n    {\n        return $this->hasMany(OrderItem::class, ['order_id' => 'id']);\n    }\n\n    public function getItems()\n    {\n        return $this->hasMany(Item::class, ['id' => 'item_id'])\n            ->via('orderItems');\n    }\n}\n```\n\n中間テーブルを使って宣言されたリレーションの使い方は、通常のリレーションと同じです。例えば、\n\n```php\n// SELECT * FROM `order` WHERE `id` = 100\n$order = Order::findOne(100);\n\n// SELECT * FROM `order_item` WHERE `order_id` = 100\n// SELECT * FROM `item` WHERE `item_id` IN (...)\n// 商品オブジェクトの配列を返す\n$items = $order->items;\n```\n\n\n### 複数のテーブルを経由するリレーション定義の連鎖<span id=\"multi-table-relations\"></span>\n\nさらに、[[yii\\db\\ActiveQuery::via()|via()]] を使ってリレーション定義を連鎖させ、複数のテーブルを経由するリレーションを定義することも可能です。\n上記の例で考えましょう。そこには `Customer`(顧客)、`Order`(注文) そして `Item`(品目) というクラスがあります。\n`Customer` クラスに、発注された全ての注文によって購入された全ての品目を列挙するリレーションを追加して、\nそれに `getPurchasedItems()` という名前を付けることが出来ます。リレーション定義の連鎖が次のコード・サンプルで示されています。\n\n```php\nclass Customer extends ActiveRecord\n{\n    // ...\n\n    public function getPurchasedItems()\n    {\n        // 顧客の購入品目、すなわち、`Item` の 'id' カラムが OrderItem の 'item_id' に合致するもの\n        return $this->hasMany(Item::class, ['id' => 'item_id'])\n                    ->via('orderItems');\n    }\n\n    public function getOrderItems()\n    {\n        // 顧客の OrderItems、すなわち、`Order` の `id` カラムが `OrderItem` の 'order_id' に合致するもの\n        return $this->hasMany(OrderItem::class, ['order_id' => 'id'])\n                    ->via('orders');\n    }\n\n    public function getOrders()\n    {\n        // 顧客の注文\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n}\n```\n\n\n### レイジー・ローディングとイーガー・ローディング <span id=\"lazy-eager-loading\"></span>\n\n[リレーショナル・データにアクセスする](#accessing-relational-data) において、通常のオブジェクト・プロパティにアクセスするのと同じようにして、\nアクティブ・レコード・インスタンスのリレーション・プロパティにアクセスすることが出来ることを説明しました。\nSQL 文は、リレーション・プロパティに最初にアクセスするときにだけ実行されます。\nこのようなリレーショナル・データのアクセス方法を *レイジー・ローディング* と呼びます。例えば、\n\n```php\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123\n$orders = $customer->orders;\n\n// SQL は実行されない\n$orders2 = $customer->orders;\n```\n\nレイジー・ローディングは非常に使い勝手が良いものです。\nしかし、複数のアクティブ・レコード・インスタンスの同じリレーション・プロパティにアクセスする必要がある場合は、パフォーマンスの問題を生じ得ます。\n次のコードサンプルを考えて見てください。実行される SQL 文の数はいくらになるでしょう?\n\n```php\n// SELECT * FROM `customer` LIMIT 100\n$customers = Customer::find()->limit(100)->all();\n\nforeach ($customers as $customer) {\n    // SELECT * FROM `order` WHERE `customer_id` = ...\n    $orders = $customer->orders;\n}\n```\n\n上のコードのコメントから判るように、実行される SQL 文は 101 にもなります。\nこれは、for ループの中で、異なる `Customer` オブジェクトの `orders` リレーションにアクセスするたびに、\nSQL 文が一つ実行されることになるからです。\n\nこのパフォーマンスの問題を解決するために、次に示すように、いわゆる *イーガー・ローディング* の手法を使うことが出来ます。\n\n```php\n// SELECT * FROM `customer` LIMIT 100;\n// SELECT * FROM `orders` WHERE `customer_id` IN (...)\n$customers = Customer::find()\n    ->with('orders')\n    ->limit(100)\n    ->all();\n\nforeach ($customers as $customer) {\n    // SQL は実行されない\n    $orders = $customer->orders;\n}\n```\n\n[[yii\\db\\ActiveQuery::with()]] を呼ぶことによって、最初の 100 人の顧客の注文をたった一つの SQL 文で返すように、アクティブ・レコードに指示をしています。\n結果として、実行される SQL 文の数は 101 から 2 に減ります。\n\nイーガー・ローディングは、一つだけでなく、複数のリレーションに対しても使うことが出来ます。さらには、*ネストされたリレーション* でさえ、イーガー・ロードすることが出来ます。\nネストされたリレーションというのは、関連するアクティブ・レコードの中で宣言されているリレーションです。\n例えば、`Cutomer` が `orders` リレーションによって `Order` と関連しており、`Order` が `items` リレーションによって `Item` と関連している場合です。\n`Customer` に対するクエリを実行するときに、ネストされたリレーションの記法である `orders.items` を使って、`items` をイーガー・ロードすることが出来ます。\n\n次のコードは、[[yii\\db\\ActiveQuery::with()|with()]] のさまざまな使い方を示すものです。\nここでは、`Customer` クラスは `orders` と `country` という二つのリレーションを持っており、また、`Order` クラスは `items` という一つのリレーションを持っていると仮定しています。\n\n```php\n// \"orders\" と \"country\" の両方をイーガー・ロードする\n$customers = Customer::find()->with('orders', 'country')->all();\n// これは下の配列記法と等価\n$customers = Customer::find()->with(['orders', 'country'])->all();\n// SQL は実行されない\n$orders= $customers[0]->orders;\n// SQL は実行されない\n$country = $customers[0]->country;\n\n// \"orders\" リレーションと、ネストされた \"orders.items\" をイーガー・ロード\n$customers = Customer::find()->with('orders.items')->all();\n// 最初の顧客の、最初の注文の品目にアクセスする\n// SQL は実行されない\n$items = $customers[0]->orders[0]->items;\n```\n\n深くネストされたリレーション、たとえば `a.b.c.c` をイーガー・ロードすることも出来ます。\nこのとき、すべての親リレーションもイーガー・ロードされます。\nつまり、`a.b.c.d` を使って [[yii\\db\\ActiveQuery::with()|with()]] を呼ぶと、`a`、`a.b`、`a.b.c` そして `a.b.c.d` をイーガー・ロードすることになります。\n\n> Info: 一般化して言うと、`N` 個のリレーションのうち `M` 個のリレーションが [中間テーブル](#junction-table) によって定義されている場合、\n  この `N` 個のリレーションをイーガー・ロードしようとすると、合計で `1+M+N` 個の SQL クエリが実行されます。\n  ネストされたリレーション `a.b.c.d` は 4 個のリレーションとして数えられることに注意してください。\n\nリレーションをイーガー・ロードするときに、対応するリレーショナル・クエリを無名関数を使ってカスタマイズすることが出来ます。\n例えば、\n\n```php\n// 顧客を検索し、その国とアクティブな注文を同時に返す\n// SELECT * FROM `customer`\n// SELECT * FROM `country` WHERE `id` IN (...)\n// SELECT * FROM `order` WHERE `customer_id` IN (...) AND `status` = 1\n$customers = Customer::find()->with([\n    'country',\n    'orders' => function ($query) {\n        $query->andWhere(['status' => Order::STATUS_ACTIVE]);\n    },\n])->all();\n```\n\nリレーションのためのリレーショナル・クエリをカスタマイズするときは、リレーション名を配列のキーとし、対応する値に無名関数を使わなければなりません。\n無名関数が受け取る `$query` パラメータは、リレーションのためのリレーショナル・クエリを実行するのに使用される\n[[yii\\db\\ActiveQuery]] オブジェクトを表します。\n上のコード例では、注文の状態に関する条件を追加して、リレーショナル・クエリを修正しています。\n\n> Note: リレーションをイーガー・ロードするときに [[yii\\db\\Query::select()|select()]] を呼ぶ場合は、\n> リレーションの宣言で参照されているカラムが選択されるように注意しなければなりません。\n> そうしないと、リレーションのモデルが正しくロードされないことがあります。例えば、\n>\n> ```php\n> $orders = Order::find()->select(['id', 'amount'])->with('customer')->all();\n> // この場合、$orders[0]->customer は常に `null` になります。問題を修正するためには、次のようにしなければなりません。\n> $orders = Order::find()->select(['id', 'amount', 'customer_id'])->with('customer')->all();\n> ```\n\n\n### リレーションを使ってテーブルを結合する <span id=\"joining-with-relations\"></span>\n\n> Note: この項で説明されていることは、MySQL、PostgreSQL など、\n  リレーショナル・データベースに対してのみ適用されます。\n\nここまで説明してきたリレーショナル・クエリは、主たるデータを検索する際に主テーブルのカラムだけを参照するものでした。\n現実には、関連するテーブルのカラムを参照しなければならない場合がよくあります。\n例えば、少なくとも一つのアクティブな注文を持つ顧客を取得したい、というような場合です。\nこの問題を解決するためには、以下のようにして、テーブルを結合するクエリを構築することが出来ます。\n\n```php\n// SELECT `customer`.* FROM `customer`\n// LEFT JOIN `order` ON `order`.`customer_id` = `customer`.`id`\n// WHERE `order`.`status` = 1\n// \n// SELECT * FROM `order` WHERE `customer_id` IN (...)\n$customers = Customer::find()\n    ->select('customer.*')\n    ->leftJoin('order', '`order`.`customer_id` = `customer`.`id`')\n    ->where(['order.status' => Order::STATUS_ACTIVE])\n    ->with('orders')\n    ->all();\n```\n\n> Note: JOIN SQL 文を含むリレーショナル・クエリを構築する場合は、カラム名の曖昧さを解消することが重要です。\n  カラム名に対応するテーブル名をプレフィクスするのが慣例です。\n\nしかしながら、もっと良いのは、[[yii\\db\\ActiveQuery::joinWith()]] を呼んで、既にあるリレーションの宣言を利用するという手法です。\n\n```php\n$customers = Customer::find()\n    ->joinWith('orders')\n    ->where(['order.status' => Order::STATUS_ACTIVE])\n    ->all();\n```\n\nどちらの方法でも、実行される SQL 文のセットは同じです。けれども、後者の方がはるかに明快で簡潔です。\n\nデフォルトでは、[[yii\\db\\ActiveQuery::joinWith()|joinWith()]] は `LEFT JOIN` を使って、関連するテーブルを主テーブルに結合します。\n第三のパラメータ `$joinType` によって異なる結合タイプ (例えば `RIGHT JOIN`) を指定することが出来ます。\n指定したい結合タイプが `INNER JOIN` である場合は、代りに、[[yii\\db\\ActiveQuery::innerJoinWith()|innerJoinWith()]] を呼ぶだけで済ませることが出来ます。\n\nデフォルトでは、[[yii\\db\\ActiveQuery::joinWith()|joinWith()]] を呼ぶと、リレーションのデータが [イーガー・ロード](#lazy-eager-loading) されます。\nリレーションのデータを読み取りたくない場合は、第二のパラメータ `$eagerLoading` を `false` に指定することが出来ます。\n\n> Note: たとえイーガー・ローディングを有効にして [[yii\\db\\ActiveQuery::joinWith()|joinWith()]] や [[yii\\db\\ActiveQuery::innerJoinWith()|innerJoinWith()]] を使う場合でも、\n  リレーションのデータを取得するのには `JOIN` クエリの結果は**使われません**。\n  その場合でも、やはり、[イーガー・ローディング](#lazy-eager-loading) のセクションで説明したように、結合されたリレーションごとに追加のクエリが実行されます。\n\n[[yii\\db\\ActiveQuery::with()|with()]] と同じように、一つまたは複数のリレーションを結合したり、\nリレーションクエリをその場でカスタマイズしたり、ネストされたリレーションを結合したりすることが出来ます。\nまた、[[yii\\db\\ActiveQuery::with()|with()]] と [[yii\\db\\ActiveQuery::joinWith()|joinWith()]] を混ぜて使用することも出来ます。例えば、\n\n```php\n$customers = Customer::find()->joinWith([\n    'orders' => function ($query) {\n        $query->andWhere(['>', 'subtotal', 100]);\n    },\n])->with('country')\n    ->all();\n```\n\n二つのテーブルを結合するときに、結合クエリの `ON` の部分に追加の条件を指定する必要がある場合があるでしょう。\nこれは、次のように、[[yii\\db\\ActiveQuery::onCondition()]] メソッドを呼ぶことによって実現できます。\n\n```php\n// SELECT `customer`.* FROM `customer`\n// LEFT JOIN `order` ON `order`.`customer_id` = `customer`.`id` AND `order`.`status` = 1 \n// \n// SELECT * FROM `order` WHERE `customer_id` IN (...)\n$customers = Customer::find()->joinWith([\n    'orders' => function ($query) {\n        $query->onCondition(['order.status' => Order::STATUS_ACTIVE]);\n    },\n])->all();\n```\n\n上記のクエリは *全ての* 顧客を返し、各顧客について全てのアクティブな注文を返します。\nこれは、少なくとも一つのアクティブな注文を持つ顧客を全て返す、という以前の例とは異なっていることに注意してください。\n\n> Info: [[yii\\db\\ActiveQuery]] が [[yii\\db\\ActiveQuery::onCondition()|onCondition()]] によって条件を指定された場合、\n  クエリが JOIN 句を含む場合は、条件は `ON` の部分に置かれます。\n  クエリが JOIN 句を含まない場合は、条件は自動的に `WHERE` の部分に追加されます。\n  このようにして、リレーションのテーブルのカラムを含む条件だけが `ON` の部分に置かれます。\n\n#### リレーションのテーブルのエイリアス <span id=\"relation-table-aliases\"></span>\n\n前に注意したように、クエリに JOIN を使うときは、カラム名の曖昧さを解消する必要があります。そのために、テーブルにエイリアスを定義することがよくあります。\nリレーションのテーブルのためにエイリアスを設定することは、リレーショナル・クエリを次のようにカスタマイズすることによっても可能です。\n\n```php\n$query->joinWith([\n    'orders' => function ($q) {\n        $q->from(['o' => Order::tableName()]);\n    },\n])\n```\n\nしかし、これでは非常に複雑ですし、リレーションオブジェクトのテーブル名をハードコーディングしたり、`Order::tableName()` を呼んだりしなければなりません。\nバージョン 2.0.7 以降、Yii はこれに対するショートカットを提供しています。今では、次のようにしてリレーションのテーブルのエイリアスを定義して使うことが出来ます。\n\n```php\n// orders リレーションを JOIN し、結果を orders.id でソートする\n$query->joinWith(['orders o'])->orderBy('o.id');\n```\n\n上記の文法が動作するのは単純なリレーションの場合です。ネストされたリレーションを結合する\n(例えば、`$query->joinWith(['orders.product'])` ) ときに、中間テーブルのエイリアスが必要になった場合は、\n次の例のように、joinWith の呼び出しをネストさせる必要があります。\n\n```php\n$query->joinWith(['orders o' => function($q) {\n        $q->joinWith('product p');\n    }])\n    ->where('o.amount > 100');\n```\n\n### 逆リレーション <span id=\"inverse-relations\"></span>\n\nリレーションの宣言は、たいていの場合、二つのアクティブ・レコード・クラスの間で相互的なものになります。\n例えば、`Customer` は `orders` リレーションによって `Order` に関連付けられ、逆に、`Order` は`customer` リレーションによって `Customer` に関連付けられる、という具合です。\n\n```php\nclass Customer extends ActiveRecord\n{\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n}\n\nclass Order extends ActiveRecord\n{\n    public function getCustomer()\n    {\n        return $this->hasOne(Customer::class, ['id' => 'customer_id']);\n    }\n}\n```\n\nここで、次のコード断片について考えて見てください。\n\n```php\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123\n$order = $customer->orders[0];\n\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer2 = $order->customer;\n\n// \"異なる\" が表示される\necho $customer2 === $customer ? '同じ' : '異なる';\n```\n\n私たちは `$customer` と `$customer2` が同じであると期待しますが、そうではありません。\n実際、二つは同じ顧客データを含んでいますが、オブジェクトとしては異なります。\n`$order->customer` にアクセスするときに追加の SQL 文が実行されて、新しいオブジェクトである `$customer2` にデータが投入されます。\n\n上記の例において、冗長な最後の SQL 文の実行を避けるためには、下に示すように、\n[[yii\\db\\ActiveQuery::inverseOf()|inverseOf()]]メソッドを呼ぶことによって、`customer` が\n`orders` の *逆リレーション* であることを Yii に教えておかなければなりません。\n\n```php\nclass Customer extends ActiveRecord\n{\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id'])->inverseOf('customer');\n    }\n}\n```\n\nこのようにリレーションの宣言を修正すると、次の結果を得ることが出来ます。\n\n```php\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123\n$order = $customer->orders[0];\n\n// No SQL will be executed\n$customer2 = $order->customer;\n\n// \"同じ\" が表示される\necho $customer2 === $customer ? '同じ' : '異なる';\n```\n\n> Note: 逆リレーションは [中間テーブル](#junction-table) を含むリレーションについては宣言することが出来ません。\n  つまり、リレーションが [[yii\\db\\ActiveQuery::via()|via()]] または [[yii\\db\\ActiveQuery::viaTable()|viaTable()]]\n  によって定義されている場合は、[[yii\\db\\ActiveQuery::inverseOf()|inverseOf()]] を追加で呼んではいけません。\n\n\n## リレーションを保存する <span id=\"saving-relations\"></span>\n\nリレーショナル・データを扱う時には、たいてい、さまざまなデータ間にリレーションを確立したり、既存のリレーションを破棄したりする必要があります。\nそのためには、リレーションを定義するカラムの値を適切に設定することが必要です。\nアクティブ・レコードを使う場合は、結局の所、次のようなコードを書くことになるでしょう。\n\n```php\n$customer = Customer::findOne(123);\n$order = new Order();\n$order->subtotal = 100;\n// ...\n\n// Order において \"customer\" リレーションを定義する属性の値を設定する\n$order->customer_id = $customer->id;\n$order->save();\n```\n\nアクティブ・レコードは、この仕事をもっと楽に達成することが出来るように、[[yii\\db\\ActiveRecord::link()|link()]] メソッドを提供しています。\n\n```php\n$customer = Customer::findOne(123);\n$order = new Order();\n$order->subtotal = 100;\n// ...\n\n$order->link('customer', $customer);\n```\n\n[[yii\\db\\ActiveRecord::link()|link()]] メソッドは、リレーション名と、リレーションを確立する対象の\nアクティブ・レコード・インスタンスを指定することを要求します。\nこのメソッドは、二つのアクティブ・レコード・インスタンスをリンクする属性の値を修正して、それをデータベースに書き込みます。\n上記の例では、`Order` インスタンスの `customer_id` 属性を `Customer` インスタンスの `id`\n属性の値になるようにセットして、それをデータベースに保存します。\n\n> Note: 二つの新規作成されたアクティブ・レコード・インスタンスをリンクすることは出来ません。\n\n[[yii\\db\\ActiveRecord::link()|link()]] を使用することの利点は、リレーションが [中間テーブル](#junction-table)\nによって定義されている場合に、さらに明白になります。\n例えば、一つの `Order` インスタンスと一つの`Item` インスタンスをリンクするのに、次のコードを使うことが出来ます。\n\n```php\n$order->link('items', $item);\n```\n\n上記のコードによって、`order_item` 中間テーブルに、注文と商品を関連付けるための行が自動的に挿入されます。\n\n> Info: [[yii\\db\\ActiveRecord::link()|link()]] メソッドは、\n  影響を受けるアクティブ・レコード・インスタンスを保存する際に、データ検証を実行しません。\n  このメソッドを呼ぶ前にすべての入力値を検証することはあなたの責任です。\n\n[[yii\\db\\ActiveRecord::link()|link()]] の逆の操作が [[yii\\db\\ActiveRecord::unlink()|unlink()]] です。\nこれは、既存の二つのアクティブ・レコード・インスタンスのリレーションを破棄します。例えば、\n\n```php\n$customer = Customer::find()->with('orders')->where(['id' => 123])->one();\n$customer->unlink('orders', $customer->orders[0]);\n```\n\nデフォルトでは、[[yii\\db\\ActiveRecord::unlink()|unlink()]] メソッドは、\n既存のリレーションを指定している外部キーの値を `null` に設定します。\nただし、`$delete` パラメータを `true` にしてメソッドに渡して、その外部キーを含むテーブル行を削除するという方法を選ぶことも出来ます。\n\nリレーションに中間テーブルが含まれている場合は、[[yii\\db\\ActiveRecord::unlink()|unlink()]] を呼ぶと、\n中間テーブルにある外部キーがクリアされるか、または、`$delete` が `true` であるときは、\n中間テーブルにある対応する行が削除されるかします。\n\n\n## DBMS 間のリレーション <span id=\"cross-database-relations\"></span> \n\nアクティブ・レコードは、異なるデータベースをバックエンドに持つアクティブ・レコードの間でリレーションを宣言することを可能にしています。\nデータベースは異なるタイプ (例えば、MySQL と PostgreSQL、または、MS SQL と MongoDB) であってもよく、別のサーバで動作していても構いません。\n同じ構文を使ってリレーショナル・クエリを実行することが出来ます。例えば、\n\n```php\n// Customer はリレーショナル・データベース (例えば MySQL) の \"customer\" テーブルと関連付けられている\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public static function tableName()\n    {\n        return 'customer';\n    }\n\n    public function getComments()\n    {\n        // Customer は多くの Comment を持つ\n        return $this->hasMany(Comment::class, ['customer_id' => 'id']);\n    }\n}\n\n// Comment は MongoDb データベースの \"comment\" コレクションと関連付けられている\nclass Comment extends \\yii\\mongodb\\ActiveRecord\n{\n    public static function collectionName()\n    {\n        return 'comment';\n    }\n\n    public function getCustomer()\n    {\n        // Comment は 一つの Customer を持つ\n        return $this->hasOne(Customer::class, ['id' => 'customer_id']);\n    }\n}\n\n$customers = Customer::find()->with('comments')->all();\n```\n\nこのセクションで説明されたリレーショナル・クエリ機能のほとんどを使用することが出来ます。\n\n> Note: [[yii\\db\\ActiveQuery::joinWith()]] の使用は、データベース間の JOIN クエリをサポートしているデータベースに限定されます。\n  この理由により、上記の例では `joinWith` メソッドは使用することが出来ません。MongoDB は JOIN をサポートしていないからです。\n\n\n## クエリ・クラスをカスタマイズする <span id=\"customizing-query-classes\"></span>\n\nデフォルトでは、全てのアクティブ・レコードのクエリは [[yii\\db\\ActiveQuery]] によってサポートされます。\nカスタマイズされたクエリ・クラスをアクティブ・レコードで使用するためには、[[yii\\db\\ActiveRecord::find()]]\nメソッドをオーバーライドして、カスタマイズされたクエリ・クラスのインスタンスを返すようにしなければなりません。例えば、\n \n```php\n// file Comment.php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass Comment extends ActiveRecord\n{\n    public static function find()\n    {\n        return new CommentQuery(get_called_class());\n    }\n}\n```\n\nこのようにすると、`Comment` のクエリを実行したり (例えば `find()` や `findOne()` を呼んだり)、`Comment` とのリレーションを定義したり (例えば `hasOne()` を定義したり)\nする際には、いつでも、`AcctiveQuery` の代りに `CommentQuery` のインスタンスを使用することになります。\n\nさて、`CommentQuery` クラスを定義しなければならない訳ですが、このクラスをさまざまな創造的方法でカスタマイズして、あなたのクエリ構築作業を楽しいものにすることが出来ます。例えば、\n\n```php\n// file CommentQuery.php\nnamespace app\\models;\n\nuse yii\\db\\ActiveQuery;\n\nclass CommentQuery extends ActiveQuery\n{\n    // デフォルトで条件を追加 (省略可)\n    public function init()\n    {\n        $this->andOnCondition(['deleted' => false]);\n        parent::init();\n    }\n\n    // ... ここにカスタマイズしたクエリ・メソッドを追加 ...\n\n    public function active($state = true)\n    {\n        return $this->andOnCondition(['active' => $state]);\n    }\n}\n```\n\n> Note: 新しいクエリ構築メソッドを定義するときには、通常は、既存のどの条件も上書きしないように、\n  [[yii\\db\\ActiveQuery::onCondition()|onCondition()]] ではなく、[[yii\\db\\ActiveQuery::andOnCondition()|andOnCondition()]]\n  または [[yii\\db\\ActiveQuery::orOnCondition()|orOnCondition()]] を呼んで条件を追加しなければなりません。\n\nこのようにすると、次のようなクエリ構築のコードを書くことが出来るようになります。\n\n```php\n$comments = Comment::find()->active()->all();\n$inactiveComments = Comment::find()->active(false)->all();\n```\n\n> Tip: 大きなプロジェクトでは、アクティブ・レコード・クラスをクリーンに保つことが出来るように、\n  クエリ関連のコードのほとんどをカスタマイズされたクエリ・クラスに保持することが推奨されます。\n\nこの新しいクエリ構築メソッドは、`Comment` に関するリレーションを定義するときや、リレーショナル・クエリを実行するときにも使用することが出来ます。\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public function getActiveComments()\n    {\n        return $this->hasMany(Comment::class, ['customer_id' => 'id'])->active();\n    }\n}\n\n$customers = Customer::find()->with('activeComments')->all();\n\n// あるいは、また\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public function getComments()\n    {\n        return $this->hasMany(Comment::class, ['customer_id' => 'id']);\n    }\n}\n\n$customers = Customer::find()->with([\n    'comments' => function($q) {\n        $q->active();\n    }\n])->all();\n```\n\n> Info: Yii 1.1 には、*スコープ* と呼ばれる概念がありました。 Yii 2.0 では、スコープはもはや直接にはサポートされません。\n  同じ目的を達するためには、カスタマイズされたクエリ・クラスとクエリ・メソッドを使わなければなりません。\n\n\n## 追加のフィールドを選択する\n\nアクティブ・レコードのインスタンスにクエリ結果からデータが投入されるときは、\n受け取ったデータセットのカラムの値が対応する属性に入れられます。\n\nクエリ結果から追加のカラムや値を取得して、アクティブ・レコードの内部に格納することが出来ます。\n例えば、ホテルの客室の情報を含む `room` という名前のテーブルがあるとしましょう。\nそして、全ての客室のデータは `length` (長さ)、`width` (幅)、`height` (高さ) というフィールドを使って、部屋の幾何学的なサイズに関する情報を格納しているとします。\n空いている全ての部屋の一覧を容積の降順で取得する必要がある場合を考えて見てください。\nレコードをその値で並べ替える必要があるので、PHP を使って容積を計算することは出来ません。\nしかし、同時に、一覧には `volume` (容積) も表示したいでしょう。\n目的を達するためには、`Room` アクティブ・レコード・クラスにおいて追加のフィールドを宣言し、`volume` の値を格納する必要があります。\n\n```php\nclass Room extends \\yii\\db\\ActiveRecord\n{\n    public $volume;\n\n    // ...\n}\n```\n\nそして、部屋の容積を計算して並べ替えを実行するクエリを構築しなければなりません。\n\n```php\n$rooms = Room::find()\n    ->select([\n        '{{room}}.*', // 全てのカラムを選択\n        '([[length]] * [[width]] * [[height]]) AS volume', // 容積を計算\n    ])\n    ->orderBy('volume DESC') // 並べ替えを適用\n    ->all();\n\nforeach ($rooms as $room) {\n    echo $room->volume; // SQL によって計算された値を含んでいる\n}\n```\n\n追加のフィールドが選択できることは、集計クエリに対して特に有効に機能します。\n注文の数とともに顧客の一覧を表示する必要がある場合を想定してください。\nまず初めに、`Customer` クラスの中で、`orders` リレーションと、注文数を格納するための追加のフィールドを宣言しなければなりません。\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public $ordersCount;\n\n    // ...\n\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n}\n```\n\nそして、order を結合して注文数を計算するクエリを構築することが出来ます。\n\n```php\n$customers = Customer::find()\n    ->select([\n        '{{customer}}.*', // 顧客の全てのフィールドを選択\n        'COUNT({{order}}.id) AS ordersCount' // 注文数を計算\n    ])\n    ->joinWith('orders') // テーブルの結合を保証する\n    ->groupBy('{{customer}}.id') // 結果をグループ化して、集計関数の動作を保証する\n    ->all();\n```\n\nこの方法を使うことの短所の一つは、情報が SQL クエリでロードされていない場合には、それを別途計算しなければならない、ということです。\n従って、追加のセレクト文を持たない通常のクエリによって特定のレコードを読み出した場合には、追加のフィールドの実際の値を返すことは不可能になります。\n同じことが新しく保存されたレコードでも起こります。\n\n```php\n$room = new Room();\n$room->length = 100;\n$room->width = 50;\n$room->height = 2;\n\n$room->volume; // まだ指定されていないため、この値は `null` になります。\n```\n\n[[yii\\db\\BaseActiveRecord::__get()|__get()]] と [[yii\\db\\BaseActiveRecord::__set()|__set()]] のマジック・メソッドを使用すれば、\nプロパティの動作をエミュレートすることが出来ます。\n\n```php\nclass Room extends \\yii\\db\\ActiveRecord\n{\n    private $_volume;\n    \n    public function setVolume($volume)\n    {\n        $this->_volume = (float) $volume;\n    }\n    \n    public function getVolume()\n    {\n        if (empty($this->length) || empty($this->width) || empty($this->height)) {\n            return null;\n        }\n        \n        if ($this->_volume === null) {\n            $this->setVolume(\n                $this->length * $this->width * $this->height\n            );\n        }\n        \n        return $this->_volume;\n    }\n\n    // ...\n}\n```\n\nこのようにすると、SELECT クエリによって容積が提供されていない場合に、\nモデルの他の属性を使って容積を自動的に計算することが出来ます。\n\n集約フィールドも、同じように、定義されたリレーションを使って計算することが出来ます。\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    private $_ordersCount;\n\n    public function setOrdersCount($count)\n    {\n        $this->_ordersCount = (int) $count;\n    }\n\n    public function getOrdersCount()\n    {\n        if ($this->isNewRecord) {\n            return null; // プライマリ・キーが null の場合のリレーショナル・クエリを防止\n        }\n        \n        if ($this->_ordersCount === null) {\n            $this->setOrdersCount(count($this->orders)); // 要求に応じてリレーションから集約を計算\n        }\n\n        return $this->_ordersCount;\n    }\n\n    // ...\n\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n}\n```\n\nこのコードでは、'ordersCount' が 'select' 文に存在する場合は、`Customer::ordersCount` はクエリの結果によって投入されます。\nそうでない場合は、要求に応じて、`Customer::orders` リレーションを使って計算されます。\n\nマジック・メソッドによってプロパティをエミュレートする手法は、ある種のリレーショナル・データ、特に集約のショートカットを作成するためにも使用することが出来ます。\n例えば、\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    /**\n     * 読み出し専用の集約データの仮想プロパティを定義\n     */\n    public function getOrdersCount()\n    {\n        if ($this->isNewRecord) {\n            return null; // プライマリ・キーが null の場合のリレーショナル・クエリを防止\n        }\n        \n        return empty($this->ordersAggregation) ? 0 : $this->ordersAggregation[0]['counted'];\n    }\n\n    /**\n     * 通常の 'orders' リレーションを宣言\n     */\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n\n    /**\n     * 集約データを提供する新しいリレーションを 'orders' を元にして宣言\n     */\n    public function getOrdersAggregation()\n    {\n        return $this->getOrders()\n            ->select(['customer_id', 'counted' => 'count(*)'])\n            ->groupBy('customer_id')\n            ->asArray(true);\n    }\n\n    // ...\n}\n\nforeach (Customer::find()->with('ordersAggregation')->all() as $customer) {\n    echo $customer->ordersCount; // イーガー・ローディングにより、追加のクエリなしに、リレーションから集約データを出力\n}\n\n$customer = Customer::findOne($pk);\n$customer->ordersCount; // レイジーロードされたリレーションから集約データを出力\n```\n"
  },
  {
    "path": "docs/guide-ja/db-dao.md",
    "content": "データベース・アクセス・オブジェクト\n====================================\n\n[PDO](https://www.php.net/manual/ja/book.pdo.php) の上に構築された Yii DAO (データベース・アクセス・オブジェクト) は、\nリレーショナル・データベースにアクセスするためのオブジェクト指向 API を提供するものです。\nこれは、データベースにアクセスする他のもっと高度な方法、例えば [クエリ・ビルダ](db-query-builder.md) や [アクティブ・レコード](db-active-record.md) の基礎でもあります。\n\nYii DAO を使うときは、主として素の SQL と PHP 配列を扱う必要があります。\n結果として、Yii DAO はデータベースにアクセスする方法としては最も効率的なものになります。\nしかし、SQL の構文はデータベースによってさまざまに異なる場合がありますので、Yii DAO を使用するということは、特定のデータベースに依存しないアプリケーションを作るためには追加の労力が必要になる、ということをも同時に意味します。\n\nYii 2.0 では、DAO は下記の DBMS のサポートを内蔵しています。\n\n- [MySQL](https://www.mysql.com/)\n- [MariaDB](https://mariadb.com/)\n- [SQLite](https://sqlite.org/)\n- [PostgreSQL](https://www.postgresql.org/): バージョン 8.4 以上。\n- [CUBRID](https://www.cubrid.org/): バージョン 9.3 以上。\n- [Oracle](https://www.oracle.com/database/)\n- [MSSQL](https://www.microsoft.com/en-us/sqlserver/default.aspx): バージョン 2008 以上。\n\n> Note: PHP 7 用の pdo_oci の新しいバージョンは、現在、ソース・コードとしてのみ存在します。\n  [コミュニティによる説明](https://github.com/yiisoft/yii2/issues/10975#issuecomment-248479268) に従ってコンパイルするか、\n  または、[PDO エミュレーション・レイヤ](https://github.com/taq/pdooci) を使って下さい。\n\n## DB 接続を作成する <span id=\"creating-db-connections\"></span>\n\nデータベースにアクセスするためには、まずは、[[yii\\db\\Connection]] のインスタンスを作成して、データベースに接続する必要があります。\n\n```php\n$db = new yii\\db\\Connection([\n    'dsn' => 'mysql:host=localhost;dbname=example',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n]);\n```\n\nDB 接続は、たいていは、さまざまな場所でアクセスする必要がありますので、次のように、\n[アプリケーション・コンポーネント](structure-application-components.md) の形式で構成するのが通例です。\n\n```php\nreturn [\n    // ...\n    'components' => [\n        // ...\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=example',\n            'username' => 'root',\n            'password' => '',\n            'charset' => 'utf8',\n        ],\n    ],\n    // ...\n];\n```\n\nこうすると `Yii::$app->db` という式で DB 接続にアクセスすることが出来るようになります。\n\n> Tip: あなたのアプリケーションが複数のデータベースにアクセスする必要がある場合は、複数の DB アプリケーション・コンポーネントを構成することが出来ます。\n\nDB 接続を構成するときは、つねに [[yii\\db\\Connection::dsn|dsn]] プロパティによってデータ・ソース名 (DSN) を指定しなければなりません。\nDSN の形式はデータベースによってさまざまに異なります。\n詳細は [PHP マニュアル](https://www.php.net/manual/ja/function.PDO-construct.php) を参照して下さい。下記にいくつかの例を挙げます。\n\n* MySQL, MariaDB: `mysql:host=localhost;dbname=mydatabase`\n* SQLite: `sqlite:/path/to/database/file`\n* PostgreSQL: `pgsql:host=localhost;port=5432;dbname=mydatabase`\n* CUBRID: `cubrid:dbname=demodb;host=localhost;port=33000`\n* MS SQL Server (sqlsrv ドライバ経由): `sqlsrv:Server=localhost;Database=mydatabase`\n* MS SQL Server (dblib ドライバ経由): `dblib:host=localhost;dbname=mydatabase`\n* MS SQL Server (mssql ドライバ経由): `mssql:host=localhost;dbname=mydatabase`\n* Oracle: `oci:dbname=//localhost:1521/mydatabase`\n\nODBC 経由でデータベースに接続しようとする場合は、[[yii\\db\\Connection::driverName]] プロパティを構成して、Yii に実際のデータベースのタイプを知らせなければならないことに注意してください。\n例えば、\n\n```php\n'db' => [\n    'class' => 'yii\\db\\Connection',\n    'driverName' => 'mysql',\n    'dsn' => 'odbc:Driver={MySQL};Server=localhost;Database=test',\n    'username' => 'root',\n    'password' => '',\n],\n```\n\n[[yii\\db\\Connection::dsn|dsn]] プロパティに加えて、たいていは [[yii\\db\\Connection::username|username]] と [[yii\\db\\Connection::password|password]] も構成しなければなりません。\n構成可能なプロパティの全てのリストは [[yii\\db\\Connection]] を参照して下さい。\n\n> Info: DB 接続のインスタンスを作成するとき、実際のデータベース接続は、最初の SQL を実行するか、\n  [[yii\\db\\Connection::open()|open()]] メソッドを明示的に呼ぶかするまでは確立されません。\n\n> Tip: 時として、何らかの環境変数を初期化するために、データベース接続を確立した直後に何かクエリを実行したい場合があるでしょう\n> (例えば、タイムゾーンや文字セットを設定するなどです)。\n> そうするために、データベース接続の [[yii\\db\\Connection::EVENT_AFTER_OPEN|afterOpen]] イベントに対するイベント・ハンドラを登録することが出来ます。\n> 以下のように、アプリケーションの構成情報に直接にハンドラを登録することが出来ます。\n> \n> ```php\n> 'db' => [\n>     // ...\n>     'on afterOpen' => function($event) {\n>         // $event->sender は DB 接続を指す\n>         $event->sender->createCommand(\"SET time_zone = 'UTC'\")->execute();\n>     }\n> ]\n> ```\n\nMS SQL Server でバイナリ・データを正しく処理するためには追加の接続オプションが必要になります。\n\n```php\n'db' => [\n 'class' => 'yii\\db\\Connection',\n    'dsn' => 'sqlsrv:Server=localhost;Database=mydatabase',\n    'attributes' => [\n        \\PDO::SQLSRV_ATTR_ENCODING => \\PDO::SQLSRV_ENCODING_SYSTEM\n    ]\n],\n```\n\n\n## SQL クエリを実行する <span id=\"executing-sql-queries\"></span>\n\nいったんデータベース接続のインスタンスを得てしまえば、次の手順に従って SQL クエリを実行することが出来ます。\n\n1. 素の SQL クエリで [[yii\\db\\Command]] を作成する。\n2. パラメータをバインドする (オプション)。\n3. [[yii\\db\\Command]] の SQL 実行メソッドの一つを呼ぶ。\n\n次に、データベースからデータを読み出すさまざまな方法を例示します。\n\n```php\n// 行のセットを返す。各行は、カラム名と値の連想配列。\n// クエリが結果を返さなかった場合は空の配列が返される。\n$posts = Yii::$app->db->createCommand('SELECT * FROM post')\n            ->queryAll();\n\n// 一つの行 (最初の行) を返す。\n// クエリの結果が無かった場合は false が返される。\n$post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=1')\n           ->queryOne();\n\n// 一つのカラム (最初のカラム) を返す。\n// クエリが結果を返さなかった場合は空の配列が返される。\n$titles = Yii::$app->db->createCommand('SELECT title FROM post')\n             ->queryColumn();\n\n// スカラ値を返す。\n// クエリの結果が無かった場合は false が返される。\n$count = Yii::$app->db->createCommand('SELECT COUNT(*) FROM post')\n             ->queryScalar();\n```\n\n> Note: 精度を保つために、対応するデータベース・カラムの型が数値である場合でも、\n  データベースから取得されたデータは、全て文字列として表現されます。\n\n\n### パラメータをバインドする <span id=\"binding-parameters\"></span>\n\nパラメータを持つ SQL から DB コマンドを作成するときは、SQL インジェクション攻撃を防止するために、\nほとんど全ての場合においてパラメータをバインドする手法を用いるべきです。例えば、\n\n```php\n$post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status')\n           ->bindValue(':id', $_GET['id'])\n           ->bindValue(':status', 1)\n           ->queryOne();\n```\n\nSQL 文において、一つまたは複数のパラメータ・プレースホルダ (例えば、上記のサンプルにおける `:id`) を埋め込むことが出来ます。\nパラメータ・プレースホルダは、コロンから始まる文字列でなければなりません。\nそして、次に掲げるパラメータをバインドするメソッドの一つを使って、パラメータの値をバインドします。\n\n* [[yii\\db\\Command::bindValue()|bindValue()]]: 一つのパラメータの値をバインドします。\n* [[yii\\db\\Command::bindValues()|bindValues()]]: 一回の呼び出しで複数のパラメータの値をバインドします。\n* [[yii\\db\\Command::bindParam()|bindParam()]]: [[yii\\db\\Command::bindValue()|bindValue()]] と似ていますが、\n  パラメータを参照渡しでバインドすることもサポートしています。\n\n次の例はパラメータをバインドする方法の選択肢を示すものです。\n\n```php\n$params = [':id' => $_GET['id'], ':status' => 1];\n\n$post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status')\n           ->bindValues($params)\n           ->queryOne();\n           \n$post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status', $params)\n           ->queryOne();\n```\n\nパラメータ・バインディングは [プリペアド・ステートメント](https://www.php.net/manual/ja/mysqli.quickstart.prepared-statements.php) によって実装されています。\nパラメータ・バインディングには、SQL インジェクション攻撃を防止する以外にも、SQL 文を一度だけ準備して異なるパラメータで複数回実行することにより、\nパフォーマンスを向上させる効果もあります。例えば、\n\n```php\n$command = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id');\n\n$post1 = $command->bindValue(':id', 1)->queryOne();\n$post2 = $command->bindValue(':id', 2)->queryOne();\n// ...\n```\n\n[[yii\\db\\Command::bindParam()|bindParam()]] はパラメータを参照渡しでバインドすることをサポートしていますので、\n上記のコードは次のように書くことも出来ます。\n\n```php\n$command = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id')\n              ->bindParam(':id', $id);\n\n$id = 1;\n$post1 = $command->queryOne();\n\n$id = 2;\n$post2 = $command->queryOne();\n```\n\nクエリの実行の前にプレースホルダを変数 `$id` にバインドし、そして、後に続く各回の実行の前にその変数の値を変更していること\n(これは、たいてい、ループで行います) に着目してください。\nこのやり方でクエリを実行すると、パラメータの値が違うごとに新しいクエリを実行するのに比べて、\nはるかに効率を良くすることが出来ます。\n\n> Info: パラメータ・バインディングは、素の SQL を含む文字列に値を挿入しなければならない場所でのみ使用されます。\n> [クエリ・ビルダ](db-query-builder.md) や [アクティブ・レコード](db-active-record.md) のような高レベルの抽象的レイヤーでは、\n> 多くの場所で SQL に変換される値の配列を指定する場合がよくあります。\n> これらの場所では Yii によってパラメータ・バインディングが内部的に実行されますので、パラメータを手動で指定する必要はありません。\n\n\n### SELECT しないクエリを実行する <span id=\"non-select-queries\"></span>\n\n今までのセクションで紹介した `queryXyz()` メソッドは、すべて、データベースからデータを取得する SELECT クエリを扱うものでした。\nデータを返さないクエリのためには、代りに [[yii\\db\\Command::execute()]] メソッドを呼ばなければなりません。例えば、\n\n```php\nYii::$app->db->createCommand('UPDATE post SET status=1 WHERE id=1')\n   ->execute();\n```\n\n[[yii\\db\\Command::execute()]] メソッドは SQL の実行によって影響を受けた行の数を返します。\n\nINSERT、UPDATE および DELETE クエリのためには、素の SQL を書く代りに、それぞれ、\n[[yii\\db\\Command::insert()|insert()]]、[[yii\\db\\Command::update()|update()]]、[[yii\\db\\Command::delete()|delete()]] を呼んで、対応する SQL を構築することが出来ます。\nこれらのメソッドは、テーブルとカラムの名前を適切に引用符で囲み、パラメータの値をバインドします。例えば、\n\n```php\n// INSERT (テーブル名, カラムの値)\nYii::$app->db->createCommand()->insert('user', [\n    'name' => 'Sam',\n    'age' => 30,\n])->execute();\n\n// UPDATE (テーブル名, カラムの値, 条件)\nYii::$app->db->createCommand()->update('user', ['status' => 1], 'age > 30')->execute();\n\n// DELETE (テーブル名, 条件)\nYii::$app->db->createCommand()->delete('user', 'status = 0')->execute();\n```\n\n[[yii\\db\\Command::batchInsert()|batchInsert()]] を呼んで複数の行を一気に挿入することも出来ます。\nこの方法は、一度に一行を挿入するよりはるかに効率的です。\n\n```php\n// テーブル名, カラム名, カラムの値\nYii::$app->db->createCommand()->batchInsert('user', ['name', 'age'], [\n    ['Tom', 30],\n    ['Jane', 20],\n    ['Linda', 25],\n])->execute();\n```\n\nもう一つの有用なメソッドは [[yii\\db\\Command::upsert()|upsert()]] です。upsert は、(ユニーク制約に合致する)行がまだ存在しない場合はデータベース・テーブルに行を挿入し、\n既に行が存在している場合は行を更新する、アトミックな操作です。\n\n```php\nYii::$app->db->createCommand()->upsert('pages', [\n    'name' => 'フロント・ページ',\n    'url' => 'https://example.com/', // url はユニーク\n    'visits' => 0,\n], [\n    'visits' => new \\yii\\db\\Expression('visits + 1'),\n], $params)->execute();\n```\n\n上記のコードは、新しいページのレコードを挿入するか、または、既存のレコードの訪問者カウンタをインクリメントします。\n\n上述のメソッド群はクエリを生成するだけであり、実際にそれを実行するためには、常に [[yii\\db\\Command::execute()|execute()]]\nを呼び出す必要があることに注意してください。\n\n\n## テーブルとカラムの名前を引用符で囲む <span id=\"quoting-table-and-column-names\"></span>\n\n特定のデータベースに依存しないコードを書くときには、テーブルとカラムの名前を適切に引用符で囲むことが、たいてい、頭痛の種になります。\nデータベースによって名前を引用符で囲む規則がさまざまに異なるからです。\nこの問題を克服するために、次のように、Yii によって導入された引用符の構文を使用することが出来ます。\n\n* `[[カラム名]]`: 引用符で囲むべきカラム名は、二重角括弧で包む。\n* `{{テーブル名}}`: 引用符で囲むべきテーブル名は、二重波括弧で包む。\n\nYii DAO は、このような構文を、DBMS 固有の文法に従って、\n適切な引用符で囲まれたカラム名とテーブル名に自動的に変換します。\n例えば、\n\n```php\n// MySQL では SELECT COUNT(`id`) FROM `employee` という SQL が実行される\n$count = Yii::$app->db->createCommand(\"SELECT COUNT([[id]]) FROM {{employee}}\")\n            ->queryScalar();\n```\n\n\n### テーブル接頭辞を使う <span id=\"using-table-prefix\"></span>\n\nあなたの DB テーブル名のほとんどが何か共通の接頭辞を持っている場合は、\nYii DAO によって提供されているテーブル接頭辞の機能を使うことが出来ます。\n\n最初に、アプリケーションの構成情報で、[[yii\\db\\Connection::tablePrefix]] プロパティによって、テーブル接頭辞を指定します。\n\n```php\nreturn [\n    // ...\n    'components' => [\n        // ...\n        'db' => [\n            // ...\n            'tablePrefix' => 'tbl_',\n        ],\n    ],\n];\n```\n\nそして、あなたのコードの中で、そのテーブル接頭辞を名前に持つテーブルを参照しなければならないときには、いつでも `{{%テーブル名}}` という構文を使います。\nパーセント記号は DB 接続を構成したときに指定したテーブル接頭辞に自動的に置き換えられます。\n例えば、\n\n```php\n// MySQL では SELECT COUNT(`id`) FROM `tbl_employee` という SQL が実行される\n$count = Yii::$app->db->createCommand(\"SELECT COUNT([[id]]) FROM {{%employee}}\")\n            ->queryScalar();\n```\n\n\n## トランザクションを実行する <span id=\"performing-transactions\"></span>\n\n一続きになった複数の関連するクエリを実行するときは、データの整合性と一貫性を保証するために、\n一連のクエリをトランザクションで囲む必要がある場合があります。\n一連のクエリのどの一つが失敗した場合でも、データベースは、何一つクエリが実行されなかったかのような状態へとロールバックされます。\n\n次のコードはトランザクションの典型的な使用方法を示すものです。\n\n```php\nYii::$app->db->transaction(function($db) {\n    $db->createCommand($sql1)->execute();\n    $db->createCommand($sql2)->execute();\n    // ... その他の SQL 文を実行 ...\n});\n```\n\n上記のコードは、次のものと等価です。こちらの方が、エラー処理のコードをより細かく制御することが出来ます。\n\n```php\n$db = Yii::$app->db;\n$transaction = $db->beginTransaction();\ntry {\n    $db->createCommand($sql1)->execute();\n    $db->createCommand($sql2)->execute();\n    // ... その他の SQL 文を実行 ...\n\n    $transaction->commit();\n} catch(\\Exception $e) {\n    $transaction->rollBack();\n    throw $e;\n} catch(\\Throwable $e) {\n    $transaction->rollBack();\n    throw $e;\n}\n```\n\n[[yii\\db\\Connection::beginTransaction()|beginTransaction()]] メソッドを呼ぶことによって、新しいトランザクションが開始されます。\nトランザクションは、変数 `$transaction` に保存された [[yii\\db\\Transaction]] オブジェクトとして表現されます。\nそして、実行されるクエリが `try...catch...` ブロックで囲まれます。\n全てのクエリの実行が成功した場合には、トランザクションをコミットするために [[yii\\db\\Transaction::commit()|commit()]] が呼ばれます。\nそうでなく、例外がトリガされてキャッチされた場合は、[[yii\\db\\Transaction::rollBack()|rollBack()]]\nが呼ばれて、トランザクションの中で失敗したクエリに先行するクエリによって行なわれた変更が、ロールバックされます。\nそして、`throw $e` が、まるでそれをキャッチしなかったかのように、例外を再スローしますので、通常のエラー処理プロセスがその例外の面倒を見ることになります。\n\n> Note: 上記のコードでは、PHP 5.x と PHP 7.x との互換性のために、二つのcatch ブロックを持っています。\n> `\\Exception` は PHP 7.0 以降では、[`\\Throwable` インタフェイス](https://www.php.net/manual/ja/class.throwable.php) を実装しています。\n> 従って、あなたのアプリケーションが PHP 7.0 以上しか使わない場合は、`\\Exception` の部分を省略することが出来ます。\n\n\n### 分離レベルを指定する <span id=\"specifying-isolation-levels\"></span>\n\nYii は、トランザクションの [分離レベル] の設定もサポートしています。デフォルトでは、新しいトランザクションを開始したときは、\nデータベースシステムによって設定された分離レベルを使用します。デフォルトの分離レベルは、次のようにしてオーバーライドすることが出来ます。\n\n```php\n$isolationLevel = \\yii\\db\\Transaction::REPEATABLE_READ;\n\nYii::$app->db->transaction(function ($db) {\n    ....\n}, $isolationLevel);\n \n// あるいは\n\n$transaction = Yii::$app->db->beginTransaction($isolationLevel);\n```\n\nYii は、最もよく使われる分離レベルのために、四つの定数を提供しています。\n\n- [[\\yii\\db\\Transaction::READ_UNCOMMITTED]] - 最も弱いレベル。ダーティー・リード、非再現リード、ファントムが発生しうる。\n- [[\\yii\\db\\Transaction::READ_COMMITTED]] - ダーティー・リードを回避。\n- [[\\yii\\db\\Transaction::REPEATABLE_READ]] - ダーティー・リードと非再現リードを回避。\n- [[\\yii\\db\\Transaction::SERIALIZABLE]] - 最も強いレベル。上記の問題を全て回避。\n\n分離レベルを指定するためには、上記の定数を使う以外に、あなたが使っている DBMS によってサポートされている有効な構文の文字列を使うことも出来ます。\n例えば、PostreSQL では、`\"SERIALIZABLE READ ONLY DEFERRABLE\"` を使うことが出来ます。\n\nDBMS によっては、接続全体に対してのみ分離レベルの設定を許容しているものがあることに注意してください。\nその場合、すべての後続のトランザクションは、指定しなくても、それと同じ分離レベルで実行されます。\n従って、この機能を使用するときは、矛盾する設定を避けるために、全てのトランザクションについて分離レベルを明示的に指定しなければなりません。\nこのチュートリアルを書いている時点では、この制約の影響を受ける DBMS は MSSQL と SQLite だけです。\n\n> Note: SQLite は、二つの分離レベルしかサポートしていません。すなわち、`READ UNCOMMITTED` と `SERIALIZABLE` しか使えません。\n他のレベルを使おうとすると、例外が投げられます。\n\n> Note: PostgreSQL は、トランザクションを開始する前に分離レベルを指定することを許容していません。\nすなわち、トランザクションを開始するときに、分離レベルを直接に指定することは出来ません。\nこの場合、トランザクションを開始した後に [[yii\\db\\Transaction::setIsolationLevel()]] を呼び出す必要があります。\n\n[分離レベル]: https://ja.wikipedia.org/wiki/%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B6%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E5%88%86%E9%9B%A2%E3%83%AC%E3%83%99%E3%83%AB\n\n\n### トランザクションを入れ子にする <span id=\"nesting-transactions\"></span>\n\nあなたの DBMS が Savepoint をサポートしている場合は、次のように、複数のトランザクションを入れ子にすることが出来ます。\n\n```php\nYii::$app->db->transaction(function ($db) {\n    // 外側のトランザクション\n    \n    $db->transaction(function ($db) {\n        // 内側のトランザクション\n    });\n});\n```\n\nあるいは、\n\n```php\n$db = Yii::$app->db;\n$outerTransaction = $db->beginTransaction();\ntry {\n    $db->createCommand($sql1)->execute();\n\n    $innerTransaction = $db->beginTransaction();\n    try {\n        $db->createCommand($sql2)->execute();\n        $innerTransaction->commit();\n    } catch (\\Exception $e) {\n        $innerTransaction->rollBack();\n        throw $e;\n    } catch(\\Throwable $e) {\n        $transaction->rollBack();\n        throw $e;\n    }\n\n    $outerTransaction->commit();\n} catch (\\Exception $e) {\n    $outerTransaction->rollBack();\n    throw $e;\n} catch(\\Throwable $e) {\n    $transaction->rollBack();\n    throw $e;\n}\n```\n\n\n## レプリケーションと読み書きの分離 <span id=\"read-write-splitting\"></span>\n\n多くの DBMS は、データベースの可用性とサーバのレスポンス・タイムを向上させるために、\n[データベース・レプリケーション](https://ja.wikipedia.org/wiki/%E3%83%AC%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3#.E3.83.87.E3.83.BC.E3.82.BF.E3.83.99.E3.83.BC.E3.82.B9) をサポートしています。\nデータベース・レプリケーションによって、データはいわゆる *マスタ・サーバ* から *スレーブ・サーバ* に複製されます。\nデータの書き込みと更新はすべてマスタ・サーバ上で実行されなければなりませんが、データの読み出しはスレーブ・サーバ上でも可能です。\n\nデータベース・レプリケーションを活用して読み書きの分離を達成するために、\n[[yii\\db\\Connection]] コンポーネントを下記のように構成することが出来ます。\n\n```php\n[\n    'class' => 'yii\\db\\Connection',\n\n    // マスタの構成\n    'dsn' => 'マスタ・サーバの DSN',\n    'username' => 'master',\n    'password' => '',\n\n    // スレーブの共通の構成\n    'slaveConfig' => [\n        'username' => 'slave',\n        'password' => '',\n        'attributes' => [\n            // 短かめの接続タイムアウトを使う\n            PDO::ATTR_TIMEOUT => 10,\n        ],\n    ],\n\n    // スレーブの構成のリスト\n    'slaves' => [\n        ['dsn' => 'スレーブ・サーバ 1 の DSN'],\n        ['dsn' => 'スレーブ・サーバ 2 の DSN'],\n        ['dsn' => 'スレーブ・サーバ 3 の DSN'],\n        ['dsn' => 'スレーブ・サーバ 4 の DSN'],\n    ],\n]\n```\n\n上記の構成は、一つのマスタと複数のスレーブを指定するものです。\n読み出しのクエリを実行するためには、スレーブの一つが接続されて使用され、書き込みのクエリを実行するためには、マスタが使われます。\nそのような読み書きの分離が、この構成によって、自動的に達成されます。例えば、\n\n```php\n// 上記の構成を使って Connection のインスタンスを作成する\n$db = Yii::createObject($config);\n\n// スレーブの一つに対してクエリを実行する\n$rows = Yii::$app->db->createCommand('SELECT * FROM user LIMIT 10')->queryAll();\n\n// マスタに対してクエリを実行する\nYii::$app->db->createCommand(\"UPDATE user SET username='demo' WHERE id=1\")->execute();\n```\n\n> Info: [[yii\\db\\Command::execute()]] を呼ぶことで実行されるクエリは、書き込みのクエリと見なされ、\n  [[yii\\db\\Command]] の \"query\" メソッドのうちの一つによって実行されるその他すべてのクエリは、読み出しクエリと見なされます。\n  現在アクティブなスレーブ接続は `Yii::$app->db->slave` によって取得することが出来ます。\n\n`Connection` コンポーネントは、スレーブ間のロード・バランス調整とフェイルオーバーをサポートしています。\n読み出しクエリを最初に実行するときに、`Connection` コンポーネントはランダムにスレーブを選んで接続を試みま・す。\nそのスレーブが「死んでいる」ことが分かったときは、他のスレーブを試します。\nスレーブが一つも使用できないときは、マスタに接続します。\n[[yii\\db\\Connection::serverStatusCache|サーバ・ステータスキャッシュ]] を構成することによって、「死んでいる」サーバを記憶し、\n[[yii\\db\\Connection::serverRetryInterval|一定期間]] はそのサーバへの接続を再試行しないようにすることが出来ます。\n\n> Info: 上記の構成では、すべてのスレーブに対して 10 秒の接続タイムアウトが指定されています。\n  これは、10 秒以内に接続できなければ、そのスレーブは「死んでいる」と見なされることを意味します。\n  このパラメータは、実際の環境に基づいて調整することが出来ます。\n\n\n複数のマスタと複数のスレーブという構成にすることも可能です。例えば、\n\n\n```php\n[\n    'class' => 'yii\\db\\Connection',\n\n    // マスタの共通の構成\n    'masterConfig' => [\n        'username' => 'master',\n        'password' => '',\n        'attributes' => [\n            // 短かめの接続タイムアウトを使う\n            PDO::ATTR_TIMEOUT => 10,\n        ],\n    ],\n\n    // マスタの構成のリスト\n    'masters' => [\n        ['dsn' => 'マスタ・サーバ 1 の DSN'],\n        ['dsn' => 'マスタ・サーバ 2 の DSN'],\n    ],\n\n    // スレーブの共通の構成\n    'slaveConfig' => [\n        'username' => 'slave',\n        'password' => '',\n        'attributes' => [\n            // 短かめの接続タイムアウトを使う\n            PDO::ATTR_TIMEOUT => 10,\n        ],\n    ],\n\n    // スレーブの構成のリスト\n    'slaves' => [\n        ['dsn' => 'スレーブ・サーバ 1 の DSN'],\n        ['dsn' => 'スレーブ・サーバ 2 の DSN'],\n        ['dsn' => 'スレーブ・サーバ 3 の DSN'],\n        ['dsn' => 'スレーブ・サーバ 4 の DSN'],\n    ],\n]\n```\n\n上記の構成は、二つのマスタと四つのスレーブを指定しています。\n`Connection` コンポーネントは、スレーブ間での場合と同じように、マスタ間でのロード・バランス調整とフェイルオーバーをサポートしています。\n一つ違うのは、マスタが一つも利用できないときは例外が投げられる、という点です。\n\n> Note: [[yii\\db\\Connection::masters|masters]] プロパティを使って一つまたは複数のマスタを構成する場合は、\n  データベース接続を定義する `Connection` オブジェクト自体の他のプロパティ\n  (例えば、`dsn`、`username`、`password`) は全て無視されます。\n\n\nデフォルトでは、トランザクションはマスタ接続を使用します。\nそして、トランザクション内では、全ての DB 操作はマスタ接続を使用します。例えば、\n\n```php\n$db = Yii::$app->db;\n// トランザクションはマスタ接続で開始される\n$transaction = $db->beginTransaction();\n\ntry {\n    // クエリは両方ともマスタに対して実行される\n    $rows = $db->createCommand('SELECT * FROM user LIMIT 10')->queryAll();\n    $db->createCommand(\"UPDATE user SET username='demo' WHERE id=1\")->execute();\n\n    $transaction->commit();\n} catch(\\Exception $e) {\n    $transaction->rollBack();\n    throw $e;\n} catch(\\Throwable $e) {\n    $transaction->rollBack();\n    throw $e;\n}\n```\n\nスレーブ接続を使ってトランザクションを開始したいときは、次のように、明示的にそうする必要があります。\n\n```php\n$transaction = Yii::$app->db->slave->beginTransaction();\n```\n\n時として、読み出しクエリの実行にマスタ接続を使うことを強制したい場合があります。\nこれは、`useMaster()` メソッドを使うことによって達成できます。\n\n```php\n$rows = Yii::$app->db->useMaster(function ($db) {\n    return $db->createCommand('SELECT * FROM user LIMIT 10')->queryAll();\n});\n```\n\n直接に `Yii::$app->db->enableSlaves` を `false` に設定して、全てのクエリをマスタ接続に向けることも出来ます。\n\n\n## データベース・スキーマを扱う <span id=\"database-schema\"></span>\n\nYii DAO は、新しいテーブルを作ったり、テーブルからカラムを削除したりなど、データベース・スキーマを操作することを可能にする一揃いのメソッドを提供しています。\n以下がそのソッドのリストです。\n\n* [[yii\\db\\Command::createTable()|createTable()]]: テーブルを作成する\n* [[yii\\db\\Command::renameTable()|renameTable()]]: テーブルの名前を変更する\n* [[yii\\db\\Command::dropTable()|dropTable()]]: テーブルを削除する\n* [[yii\\db\\Command::truncateTable()|truncateTable()]]: テーブルの全ての行を削除する\n* [[yii\\db\\Command::addColumn()|addColumn()]]: カラムを追加する\n* [[yii\\db\\Command::renameColumn()|renameColumn()]]: カラムの名前を変更する\n* [[yii\\db\\Command::dropColumn()|dropColumn()]]: カラムを削除する\n* [[yii\\db\\Command::alterColumn()|alterColumn()]]: カラムを変更する\n* [[yii\\db\\Command::addPrimaryKey()|addPrimaryKey()]]: プライマリ・キーを追加する\n* [[yii\\db\\Command::dropPrimaryKey()|dropPrimaryKey()]]: プライマリ・キーを削除する\n* [[yii\\db\\Command::addForeignKey()|addForeignKey()]]: 外部キーを追加する\n* [[yii\\db\\Command::dropForeignKey()|dropForeignKey()]]: 外部キーを削除する\n* [[yii\\db\\Command::createIndex()|createIndex()]]: インデックスを作成する\n* [[yii\\db\\Command::dropIndex()|dropIndex()]]: インデックスを削除する\n\nこれらのメソッドは次のようにして使うことが出来ます。\n\n```php\n// CREATE TABLE\nYii::$app->db->createCommand()->createTable('post', [\n    'id' => 'pk',\n    'title' => 'string',\n    'text' => 'text',\n]);\n```\n\n上記の配列は、生成されるカラムの名前と型を記述しています。\nYii はカラムの型のために一連の抽象データ型を提供しているため、データベースの違いを意識せずにスキーマを定義することが可能です。\nこれらの抽象データ型は、テーブルが作成されるデータベースによって異なる DBMS 固有の型定義に変換されます。\n詳しい情報は [[yii\\db\\Command::createTable()|createTable()]] メソッドの API ドキュメントを参照してください。\n\nデータベースのスキーマを変更するだけでなく、テーブルに関する定義情報を DB 接続の [[yii\\db\\Connection::getTableSchema()|getTableSchema()]] メソッドによって取得することも出来ます。\n例えば、\n\n```php\n$table = Yii::$app->db->getTableSchema('post');\n```\n\nこのメソッドは、テーブルのカラム、プライマリ・キー、外部キーなどの情報を含む [[yii\\db\\TableSchema]] オブジェクトを返します。\nこの情報は、主として [クエリ・ビルダ](db-query-builder.md) や [アクティブ・レコード](db-active-record.md) によって利用されて、\n特定のデータベースに依存しないコードを書くことを助けてくれています。\n"
  },
  {
    "path": "docs/guide-ja/db-migrations.md",
    "content": "データベース・マイグレーション\n============================\n\nデータベース駆動型のアプリケーションを開発し保守する途上で、ソース・コードが進化するのと同じように、\n使用されるデータベースの構造も進化していきます。\n例えば、アプリケーションの開発中に、新しいテーブルが必要であることが分ったり、アプリケーションを配備した後に、\nクエリのパフォーマンスを向上させるためにインデックスを作成すべきことが発見されたりします。\nデータベースの構造の変更が何らかのソース・コードの変更を要求する場合はよくありますから、\nYii はいわゆる *データベース・マイグレーション* 機能を提供して、ソース・コードとともにバージョン管理される\n*データベース・マイグレーション* の形式でデータベースの変更を追跡できるようにしています。\n\n下記の一連のステップは、開発中にチームによってデータベース・マイグレーションがどのように使用されるかを示す例です。\n\n1. Tim が新しいマイグレーション (例えば、新しいテーブルを作成したり、カラムの定義を変更したりなど) を作る。\n2. Tim が新しいマイグレーションをソース・コントロール・システム (例えば Git や Mercurial) にコミットする。\n3. Doug がソース・コントロール・システムから自分のレポジトリを更新して新しいマイグレーションを受け取る。\n4. Doug がマイグレーションを彼のローカルの開発用データベースに適用して、自分のデータベースの同期を取り、\n   Tim が行った変更を反映する。\n\nそして、次の一連のステップは、本番環境でデータベース・マイグレーションとともに新しいリリースを配備する方法を示すものです。\n\n1. Scott は新しいデータベース・マイグレーションをいくつか含むプロジェクトのレポジトリにリリース・タグを作成する。\n2. Scott は本番サーバでソース・コードをリリース・タグまで更新する。\n3. Scott は本番のデータベースに対して累積したデータベース・マイグレーションを全て適用する。\n\nYii は一連のマイグレーション・コマンドライン・ツールを提供して、以下の機能をサポートします。\n\n* 新しいマイグレーションの作成\n* マイグレーションの適用\n* マイグレーションの取消\n* マイグレーションの再適用\n* マイグレーションの履歴と状態の表示\n\nこれらのツールは、全て、`yii migrate` コマンドからアクセスすることが出来ます。\nこのセクションでは、これらのツールを使用して、さまざまなタスクをどうやって達成するかを詳細に説明します。\n各ツールの使用方法は、ヘルプコマンド `yii help migrate` によっても知ることが出来ます。\n\n> Tip: マイグレーションはデータベース・スキーマに影響を及ぼすだけでなく、既存のデータを新しいスキーマに合うように修正したり、RBAC 階層を作成したり、\nキャッシュをクリーンアップしたりするために使うことも出来ます。\n\n> Note: マイグレーションを使ってデータを操作する際に、作成済みの[アクティブ・レコード](db-active-record.md)・クラスを使えば\n> 便利かも知れないと気が付くことがあるでしょう。なぜなら、ロジックのいくつかは既にアクティブ・レコードで実装済みなのですから。\n> しかしながら、永久に不変であり続けることを本質とするマイグレーションにおいて書かれるコードとは対照的に、アプリケーションの\n> ロジックは変化にさらされるものであることに留意しなければなりません。つまり、マイグレーション・コードにアクティブ・レコードを使った場合、\n> アクティブ・レコードのレイヤにおけるロジックの変更が既存のマイグレーションを偶発的に破壊する可能性があります。この理由により、\n> マイグレーション・コードは、アクティブ・レコード・クラスなど、他のアプリケーション・ロジックから独立を保つべきです。\n\n\n## マイグレーションを作成する <span id=\"creating-migrations\"></span>\n\n新しいマイグレーションを作成するためには、次のコマンドを実行します。\n\n```\nyii migrate/create <name>\n```\n\n要求される `name` パラメータには、マイグレーションの非常に短い説明を指定します。\n例えば、マイグレーションが *news* という名前のテーブルを作成するものである場合は、\n`create_news_table` という名前を使って、次のようにコマンドを実行すれば良いでしょう。\n\n```\nyii migrate/create create_news_table\n```\n\n> Note: この `name` 引数は、生成されるマイグレーション・クラス名の一部として使用されますので、\n  アルファベット、数字、および/または、アンダースコアだけを含むものでなければなりません。\n\n上記のコマンドは、`m150101_185401_create_news_table.php` という名前の新しい PHP クラス・ファイルを\n`@app/migrations` ディレクトリに作成します。このファイルは次のようなコードを含み、主として、\nスケルトン・コードを持った `m150101_185401_create_news_table` というマイグレーション・クラスを宣言するためのものす。\n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function up()\n    {\n\n    }\n\n    public function down()\n    {\n        echo \"m101129_185401_create_news_table cannot be reverted.\\n\";\n\n        return false;\n    }\n\n    /*\n    // Use safeUp/safeDown to run migration code within a transaction\n    public function safeUp()\n    {\n    }\n\n    public function safeDown()\n    {\n    }\n    */\n}\n```\n\n各データベース・マイグレーションは [[yii\\db\\Migration]] から拡張した PHP クラスとして定義されます。\nマイグレーション・クラスの名前は、`m<YYMMDD_HHMMSS>_<Name>` という形式で自動的に生成されます。ここで、\n\n* `<YYMMDD_HHMMSS>` は、マイグレーション作成コマンドが実行された UTC 日時を表し、\n* `<Name>` は、あなたがコマンドに与えた `name` 引数と同じ値になります。\n\nマイグレーション・クラスにおいて、あなたがなすべき事は、データベースの構造に変更を加える `up()` メソッドにコードを書くことです。\nまた、`up()` によって加えられた変更を取り消すための `down()` メソッドにも、コードを書きたいと思うかもしれません。\n`up()` メソッドは、このマイグレーションによってデータベースをアップグレードする際に呼び出され、`down()` メソッドはデータベースをダウングレードする際に呼び出されます。\n下記のコードは、新しい `news` テーブルを作成するマイグレーション・クラスをどのようにして実装するかを示すものです。\n\n```php\n<?php\n\nuse yii\\db\\Schema;\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function up()\n    {\n        $this->createTable('news', [\n            'id' => Schema::TYPE_PK,\n            'title' => Schema::TYPE_STRING . ' NOT NULL',\n            'content' => Schema::TYPE_TEXT,\n        ]);\n    }\n\n    public function down()\n    {\n        $this->dropTable('news');\n    }\n}\n```\n\n> Info: 全てのマイグレーションが取り消し可能な訳ではありません。\n  例えば、`up()` メソッドがテーブルからある行を削除するものである場合、`down()` メソッドでその行を回復することは出来ません。\n  また、データベース・マイグレーションを取り消すことはあまり一般的ではありませんので、場合によっては、面倒くさいというだけの理由で `down()` を実装しないこともあるでしょう。\n  そういう場合は、マイグレーションが取り消し不可能であることを示すために、`down()` メソッドで false を返さなければなりません。\n\n基底のマイグレーション・クラス [[yii\\db\\Migration]] は、[[yii\\db\\Migration::db|db]] プロパティによって、\nデータベース接続にアクセスすることを可能にしています。このデータベース接続によって、[データベース・スキーマを扱う](db-dao.md#database-schema)\nで説明されているメソッドを使い、データベース・スキーマを操作することが出来ます。\n\nテーブルやカラムを作成するときは、物理的な型を使うのでなく、*抽象型* を使って、\nあなたのマイグレーションが特定の DBMS に依存しないようにします。\n[[yii\\db\\Schema]] クラスが、サポートされている抽象型を表す一連の定数を定義しています。\nこれらの定数は `TYPE_<Name>` という形式の名前を持っています。\n例えば、`TYPE_PK` は、オート・インクリメントのプライマリ・キー型であり、`TYPE_STRING` は文字列型です。\nこれらの抽象型は、マイグレーションが特定のデータベースに適用されるときに、対応する物理型に翻訳されます。\nMySQL の場合は、`TYPE_PK` は `int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY` に変換され、`TYPE_STRING` は `varchar(255)` となります。\n\n抽象型を使用するときに、付随的な制約を追加することが出来ます。\n上記の例では、`Schema::TYPE_STRING` に ` NOT NULL` を追加して、このカラムが null を許容しないことを指定しています。\n\n> Info: 抽象型と物理型の対応関係は、それぞれの `QueryBuilder` の具象クラスの [[yii\\db\\QueryBuilder::$typeMap|$typeMap]]\n  プロパティによって定義されています。\n\nバージョン 2.0.6 以降は、カラムのスキーマを定義するための更に便利な方法を提供するスキーマビルダが新たに導入されています。\nしたがって、上記のマイグレーションは次のように書くことが出来ます。\n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function up()\n    {\n        $this->createTable('news', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string()->notNull(),\n            'content' => $this->text(),\n        ]);\n    }\n\n    public function down()\n    {\n        $this->dropTable('news');\n    }\n}\n```\n\nカラムの型を定義するために利用できる全てのメソッドのリストは、[[yii\\db\\SchemaBuilderTrait]] の API ドキュメントで参照することが出来ます。\n\n> Info: 生成されるファイルのパーミッションと所有者は現在の環境によって決定されます。\n  これが原因でファイルにアクセス出来ない場合が生じ得ます。例えば、docker コンテナ内で作成されたマイグレーションのファイルをホストで編集するとそうなる場合があります。\n  このような場合には MigrateController の `newFileMode` および/または `newFileOwnership` を変更することが出来ます。\n  例えば、アプリケーション設定で次のように設定します。\n  ```php\n  <?php\n  return [\n      'controllerMap' => [\n          'migrate' => [\n              'class' => 'yii\\console\\controllers\\MigrateController',\n              'newFileOwnership' => '1000:1000', # Default WSL user id\n              'newFileMode' => 0660,\n          ],\n      ],\n  ];\n  ```\n\n## マイグレーションを生成する <span id=\"generating-migrations\"></span>\n\nバージョン 2.0.7 以降では、マイグレーション・コンソールがマイグレーションを生成する便利な方法を提供しています。\n\nマイグレーションの名前が特別な形式である場合は、生成されるマイグレーション・ファイルに追加のコードが書き込まれます。\n例えば、`create_xxx_table` や `drop_xxx_table` であれば、テーブルの作成や削除をするコードが追加されます。\n以下で、この機能の全ての変種を説明します。\n\n### テーブルの作成\n\n```\nyii migrate/create create_post_table\n``` \n\n上記のコマンドは、次のコードを生成します。\n\n```php\n/**\n * Handles the creation for table `post`.\n */\nclass m150811_220037_create_post_table extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey()\n        ]);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        $this->dropTable('post');\n    }\n}\n```\n\nテーブルのフィールドも直接に生成したい場合は、`--fields` オプションでフィールドを指定します。\n \n```\nyii migrate/create create_post_table --fields=\"title:string,body:text\"\n``` \n\nこれは、次のコードを生成します。\n\n```php\n/**\n * Handles the creation for table `post`.\n */\nclass m150811_220037_create_post_table extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string(),\n            'body' => $this->text(),\n        ]);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        $this->dropTable('post');\n    }\n}\n\n```\n\nさらに多くのフィールド・パラメータを指定することも出来ます。\n\n```\nyii migrate/create create_post_table --fields=\"title:string(12):notNull:unique,body:text\"\n``` \n\nこれは、次のコードを生成します。\n\n```php\n/**\n * Handles the creation for table `post`.\n */\nclass m150811_220037_create_post_table extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string(12)->notNull()->unique(),\n            'body' => $this->text()\n        ]);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        $this->dropTable('post');\n    }\n}\n```\n\n> Note: プライマリ・キーが自動的に追加されて、デフォルトでは `id` と名付けられます。\n> 別の名前を使いたい場合は、`--fields=\"name:primaryKey\"` のように、明示的に指定してください。\n\n#### 外部キー\n\nバージョン 2.0.8 からは、`foreignKey` キーワードを使って外部キーを生成することができます。\n\n```\nyii migrate/create create_post_table --fields=\"author_id:integer:notNull:foreignKey(user),category_id:integer:defaultValue(1):foreignKey,title:string,body:text\"\n```\n\nこれは、次のコードを生成します。\n\n```php\n/**\n * Handles the creation for table `post`.\n * Has foreign keys to the tables:\n *\n * - `user`\n * - `category`\n */\nclass m160328_040430_create_post_table extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey(),\n            'author_id' => $this->integer()->notNull(),\n            'category_id' => $this->integer()->defaultValue(1),\n            'title' => $this->string(),\n            'body' => $this->text(),\n        ]);\n\n        // creates index for column `author_id`\n        $this->createIndex(\n            'idx-post-author_id',\n            'post',\n            'author_id'\n        );\n\n        // add foreign key for table `user`\n        $this->addForeignKey(\n            'fk-post-author_id',\n            'post',\n            'author_id',\n            'user',\n            'id',\n            'CASCADE'\n        );\n\n        // creates index for column `category_id`\n        $this->createIndex(\n            'idx-post-category_id',\n            'post',\n            'category_id'\n        );\n\n        // add foreign key for table `category`\n        $this->addForeignKey(\n            'fk-post-category_id',\n            'post',\n            'category_id',\n            'category',\n            'id',\n            'CASCADE'\n        );\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        // drops foreign key for table `user`\n        $this->dropForeignKey(\n            'fk-post-author_id',\n            'post'\n        );\n\n        // drops index for column `author_id`\n        $this->dropIndex(\n            'idx-post-author_id',\n            'post'\n        );\n\n        // drops foreign key for table `category`\n        $this->dropForeignKey(\n            'fk-post-category_id',\n            'post'\n        );\n\n        // drops index for column `category_id`\n        $this->dropIndex(\n            'idx-post-category_id',\n            'post'\n        );\n\n        $this->dropTable('post');\n    }\n}\n```\n\nカラムの記述における `foreignKey` キーワードの位置によって、生成されるコードが変ることはありません。\nつまり、\n\n- `author_id:integer:notNull:foreignKey(user)`\n- `author_id:integer:foreignKey(user):notNull`\n- `author_id:foreignKey(user):integer:notNull`\n\nこれらはすべて同じコードを生成します。\n\n`foreignKey` キーワードは括弧の中にパラメータを取ることが出来て、\nこれが生成される外部キーの関連テーブルの名前になります。\nパラメータが渡されなかった場合は、テーブル名はカラム名から推測されます。\n\n上記の例で `author_id:integer:notNull:foreignKey(user)` は、\n`user` テーブルへの外部キーを持つ `author_id` という名前のカラムを生成します。\n一方、`category_id:integer:defaultValue(1):foreignKey` は、\n`category` テーブルへの外部キーを持つ `category_id` というカラムを生成します。\n\n2.0.11 以降では、`foreignKey` キーワードは空白で区切られた第二のパラメータを取ることが出来ます。\nこれは、生成される外部キーに関連づけられるカラム名を表します。\n第二のパラメータが渡されなかった場合は、カラム名はテーブル・スキーマから取得されます。\nスキーマが存在しない場合や、プライマリ・キーが設定されていなかったり、複合キーであったりする場合は、デフォルト名として `id` が使用されます。\n\n### テーブルを削除する\n\n```\nyii migrate/create drop_post_table --fields=\"title:string(12):notNull:unique,body:text\"\n``` \n\nこれは、次のコードを生成します。\n\n```php\nclass m150811_220037_drop_post_table extends Migration\n{\n    public function up()\n    {\n        $this->dropTable('post');\n    }\n\n    public function down()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string(12)->notNull()->unique(),\n            'body' => $this->text()\n        ]);\n    }\n}\n```\n\n### カラムを追加する\n\nマイグレーションの名前が `add_xxx_column_to_yyy_table` の形式である場合、ファイルの内容は、\n必要となる `addColumn` と `dropColumn` を含むことになります。\n\nカラムを追加するためには、次のようにします。\n\n```\nyii migrate/create add_position_column_to_post_table --fields=\"position:integer\"\n```\n\nこれが次のコードを生成します。\n\n```php\nclass m150811_220037_add_position_column_to_post_table extends Migration\n{\n    public function up()\n    {\n        $this->addColumn('post', 'position', $this->integer());\n    }\n\n    public function down()\n    {\n        $this->dropColumn('post', 'position');\n    }\n}\n```\n\n次のようにして複数のカラムを指定することも出来ます。\n\n```\nyii migrate/create add_xxx_column_yyy_column_to_zzz_table --fields=\"xxx:integer,yyy:text\"\n```\n\n### カラムを削除する\n\nマイグレーションの名前が `drop_xxx_column_from_yyy_table` の形式である場合、\nファイルの内容は、必要となる `addColumn` と `dropColumn` を含むことになります。\n\n```\nyii migrate/create drop_position_column_from_post_table --fields=\"position:integer\"\n```\n\nこれは、次のコードを生成します。\n\n```php\nclass m150811_220037_drop_position_column_from_post_table extends Migration\n{\n    public function up()\n    {\n        $this->dropColumn('post', 'position');\n    }\n\n    public function down()\n    {\n        $this->addColumn('post', 'position', $this->integer());\n    }\n}\n```\n\n### 中間テーブルを追加する\n\nマイグレーションの名前が `create_junction_table_for_xxx_and_yyy_tables` の形式である場合は、\n中間テーブルを作成するのに必要となるコードが生成されます。\n\n```\nyii migrate/create create_junction_table_for_post_and_tag_tables --fields=\"created_at:dateTime\"\n```\n\nこれは、次のコードを生成します。\n\n```php\n/**\n * Handles the creation for table `post_tag`.\n * Has foreign keys to the tables:\n *\n * - `post`\n * - `tag`\n */\nclass m160328_041642_create_junction_table_for_post_and_tag_tables extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $this->createTable('post_tag', [\n            'post_id' => $this->integer(),\n            'tag_id' => $this->integer(),\n            'created_at' => $this->dateTime(),\n            'PRIMARY KEY(post_id, tag_id)',\n        ]);\n\n        // creates index for column `post_id`\n        $this->createIndex(\n            'idx-post_tag-post_id',\n            'post_tag',\n            'post_id'\n        );\n\n        // add foreign key for table `post`\n        $this->addForeignKey(\n            'fk-post_tag-post_id',\n            'post_tag',\n            'post_id',\n            'post',\n            'id',\n            'CASCADE'\n        );\n\n        // creates index for column `tag_id`\n        $this->createIndex(\n            'idx-post_tag-tag_id',\n            'post_tag',\n            'tag_id'\n        );\n\n        // add foreign key for table `tag`\n        $this->addForeignKey(\n            'fk-post_tag-tag_id',\n            'post_tag',\n            'tag_id',\n            'tag',\n            'id',\n            'CASCADE'\n        );\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        // drops foreign key for table `post`\n        $this->dropForeignKey(\n            'fk-post_tag-post_id',\n            'post_tag'\n        );\n\n        // drops index for column `post_id`\n        $this->dropIndex(\n            'idx-post_tag-post_id',\n            'post_tag'\n        );\n\n        // drops foreign key for table `tag`\n        $this->dropForeignKey(\n            'fk-post_tag-tag_id',\n            'post_tag'\n        );\n\n        // drops index for column `tag_id`\n        $this->dropIndex(\n            'idx-post_tag-tag_id',\n            'post_tag'\n        );\n\n        $this->dropTable('post_tag');\n    }\n}\n```\n\n2.0.11 以降では、中間テーブルの外部キーのカラム名はテーブル・スキーマから取得されます。\nスキーマでテーブルが定義されていない場合や、プライマリ・キーが設定されていなかったり複合キーであったりする場合は、デフォルト名 `id` が使われます。\n\n### トランザクションを使うマイグレーション <span id=\"transactional-migrations\"></span>\n\n複雑な一連の DB マイグレーションを実行するときは、通常、データベースの一貫性と整合性を保つために、\n各マイグレーションが全体として成功または失敗することを保証する必要があります。\nこの目的を達成するために、各マイグレーションの DB 操作を [トランザクション](db-dao.md#performing-transactions) で囲むことが推奨されます。\n\nトランザクションを使うマイグレーションを実装するためのもっと簡単な方法は、マイグレーションのコードを `safeUp()` と `safeDown()` のメソッドに入れることです。\nこの二つのメソッドが `up()` および `down()` と違う点は、これらが暗黙のうちにトランザクションに囲まれていることです。\n結果として、これらのメソッドの中で何か操作が失敗した場合は、先行する全ての操作が自動的にロールバックされます。\n\n次の例では、`news` テーブルを作成するだけでなく、このテーブルに初期値となる行を挿入しています。\n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function safeUp()\n    {\n        $this->createTable('news', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string()->notNull(),\n            'content' => $this->text(),\n        ]);\n\n        $this->insert('news', [\n            'title' => 'test 1',\n            'content' => 'content 1',\n        ]);\n    }\n\n    public function safeDown()\n    {\n        $this->delete('news', ['id' => 1]);\n        $this->dropTable('news');\n    }\n}\n```\n\n通常、`safeUp()` で複数の DB 操作を実行する場合は、`safeDown()` では実行の順序を逆にしなければならないことに注意してください。\n上記の例では、`safeUp()` では、最初にテーブルを作って、次に行を挿入し、\n`safeDown()` では、先に行を削除して、次にテーブルを削除しています。\n\n> Note: 全ての DBMS がトランザクションをサポートしている訳ではありません。また、トランザクションに入れることが出来ない DB クエリもあります。\n  そのいくつかの例を [暗黙のコミット](https://dev.mysql.com/doc/refman/5.7/en/implicit-commit.html) で見ることが出来ます。\n  その場合には、代りに、`up()` と `down()` を実装しなければなりません。\n\n\n### データベース・アクセス・メソッド <span id=\"db-accessing-methods\"></span>\n\n基底のマイグレーション・クラス [[yii\\db\\Migration]] は、データベースにアクセスして操作するための一連のメソッドを提供しています。\nあなたは、これらのメソッドが、[[yii\\db\\Command]] クラスによって提供される [DAO メソッド](db-dao.md) と同じような名前を付けられていることに気付くでしょう。\n例えば、[[yii\\db\\Migration::createTable()]] メソッドは、[[yii\\db\\Command::createTable()]] と全く同じように、\n新しいテーブルを作成します。\n\n[[yii\\db\\Migration]] によって提供されているメソッドを使うことの利点は、[[yii\\db\\Command]]\nインスタンスを明示的に作成する必要がないこと、そして、各メソッドを実行すると、\nどのようなデータベース操作がどれだけの時間をかけて実行されたかを教えてくれる有益なメッセージが自動的に表示されることです。\n\n以下がそういうデータベース・アクセス・メソッドの一覧です。\n\n* [[yii\\db\\Migration::execute()|execute()]]: SQL 文を実行\n* [[yii\\db\\Migration::insert()|insert()]]: 一行を挿入\n* [[yii\\db\\Migration::batchInsert()|batchInsert()]]: 複数行を挿入\n* [[yii\\db\\Migration::update()|update()]]: 行を更新\n* [[yii\\db\\Migration::upsert()|upsert()]]: 一行を挿入または既に存在していれば更新 (2.0.14 以降)\n* [[yii\\db\\Migration::delete()|delete()]]: 行を削除\n* [[yii\\db\\Migration::createTable()|createTable()]]: テーブルを作成\n* [[yii\\db\\Migration::renameTable()|renameTable()]]: テーブルの名前を変更\n* [[yii\\db\\Migration::dropTable()|dropTable()]]: テーブルを削除\n* [[yii\\db\\Migration::truncateTable()|truncateTable()]]: テーブル中の全ての行を削除\n* [[yii\\db\\Migration::addColumn()|addColumn()]]: カラムを追加\n* [[yii\\db\\Migration::renameColumn()|renameColumn()]]: カラムの名前を変更\n* [[yii\\db\\Migration::dropColumn()|dropColumn()]]: カラムを削除\n* [[yii\\db\\Migration::alterColumn()|alterColumn()]]: カラムの定義を変更\n* [[yii\\db\\Migration::addPrimaryKey()|addPrimaryKey()]]: プライマリ・キーを追加\n* [[yii\\db\\Migration::dropPrimaryKey()|dropPrimaryKey()]]: プライマリ・キーを削除\n* [[yii\\db\\Migration::addForeignKey()|addForeignKey()]]: 外部キーを追加\n* [[yii\\db\\Migration::dropForeignKey()|dropForeignKey()]]: 外部キーを削除\n* [[yii\\db\\Migration::createIndex()|createIndex()]]: インデックスを作成\n* [[yii\\db\\Migration::dropIndex()|dropIndex()]]: インデックスを削除\n* [[yii\\db\\Migration::addCommentOnColumn()|addCommentOnColumn()]]: カラムにコメントを追加\n* [[yii\\db\\Migration::dropCommentFromColumn()|dropCommentFromColumn()]]: カラムからコメントを削除\n* [[yii\\db\\Migration::addCommentOnTable()|addCommentOnTable()]]: テーブルにコメントを追加\n* [[yii\\db\\Migration::dropCommentFromTable()|dropCommentFromTable()]]: テーブルからコメントを削除\n\n> Info: [[yii\\db\\Migration]] は、データベース・クエリ・メソッドを提供しません。\n> これは、通常、データベースからのデータ取得については、メッセージを追加して表示する必要がないからです。\n> 更にまた、複雑なクエリを構築して実行するためには、強力な [クエリ・ビルダ](db-query-builder.md) を使うことが出来るからです。\n> マイグレーションの中でクエリ・ビルダを使うと、次のようなコードになります。\n>\n> ```php\n> // 全ユーザについて、status フィールドを更新する\n> foreach((new Query)->from('users')->each() as $user) {\n>     $this->update('users', ['status' => 1], ['id' => $user['id']]);\n> }\n> ```\n\n## マイグレーションを適用する <span id=\"applying-migrations\"></span>\n\nデータベースを最新の構造にアップグレードするためには、利用できる全ての新しいマイグレーションを適用するために、次のコマンドを使わなければなりません。\n\n```\nyii migrate\n```\n\nコマンドを実行すると、まだ適用されていない全てのマイグレーションが一覧表示されます。\nリストされたマイグレーションを適用することをあなたが確認すると、タイムスタンプの値の順に、一つずつ、\nすべての新しいマイグレーション・クラスの `up()` または `safeUp()` メソッドが実行されます。\nマイグレーションのどれかが失敗した場合は、コマンドは残りのマイグレーションを適用せずに終了します。\n\n> Tip: あなたのサーバでコマンドラインを使用できない場合は\n> [web shell](https://github.com/samdark/yii2-webshell) エクステンションを使ってみてください。\n\n適用が成功したマイグレーションの一つ一つについて、`migration` という名前のデータベース・テーブルに行が挿入されて、\nマイグレーションの成功が記録されます。この記録によって、マイグレーション・ツールは、どのマイグレーションが適用され、\nどのマイグレーションが適用されていないかを特定することが出来ます。\n\n> Info: マイグレーション・ツールは、コマンドの [[yii\\console\\controllers\\MigrateController::db|db]] オプションで指定されたデータベースに\n  `migration` テーブルを自動的に作成します。デフォルトでは、このデータベースは\n  `db` [アプリケーション・コンポーネント](structure-application-components.md) によって指定されます。\n\n時として、利用できる全てのマイグレーションではなく、一つまたは数個の新しいマイグレーションだけを適用したい場合があります。\nコマンドを実行するときに、適用したいマイグレーションの数を指定することによって、そうすることが出来ます。\n例えば、次のコマンドは、次の三個の利用できるマイグレーションを適用しようとするものです。\n\n```\nyii migrate 3\n```\n\nまた、そのマイグレーションまでをデータベースに適用するという、特定のマイグレーションを明示的に指定することも出来ます。\nそのためには、`migrate/to` コマンドを、次のどれかの形式で使います。\n\n```\nyii migrate/to 150101_185401                      # タイムスタンプを使ってマイグレーションを指定\nyii migrate/to \"2015-01-01 18:54:01\"              # strtotime() によって解釈できる文字列を使用\nyii migrate/to m150101_185401_create_news_table   # フルネームを使用\nyii migrate/to 1392853618                         # UNIX タイムスタンプを使用\n```\n\n指定されたマイグレーションよりも古いものが適用されずに残っている場合は、指定されたものが適用される前に、\nすべて適用されます。\n\n指定されたマイグレーションが既に適用済みである場合、それより新しいものが適用されていれば、すべて取り消されます。\n\n\n## マイグレーションを取り消す <span id=\"reverting-migrations\"></span>\n\n適用済みのマイグレーションを一個または複数個取り消したい場合は、下記のコマンドを使うことが出来ます。\n\n```\nyii migrate/down     # 最近に適用されたマイグレーション一個を取り消す\nyii migrate/down 3   # 最近に適用されたマイグレーション三個を取り消す\n```\n\n> Note: 全てのマイグレーションが取り消せるとは限りません。\n  そのようなマイグレーションを取り消そうとするとエラーとなり、取り消しのプロセス全体が終了させられます。\n\n\n## マイグレーションを再適用する <span id=\"redoing-migrations\"></span>\n\nマイグレーションの再適用とは、指定されたマイグレーションを最初に取り消してから、再度適用することを意味します。\nこれは次のコマンドによって実行することが出来ます。\n\n```\nyii migrate/redo        # 最後に適用された一個のマイグレーションを再適用する\nyii migrate/redo 3      # 最後に適用された三個のマイグレーションを再適用する\n```\n\n> Note: マイグレーションが取り消し不可能な場合は、それを再適用することは出来ません。\n\n## マイグレーションをリフレッシュする <span id=\"refreshing-migrations\"></span>\n\nYii 2.0.13 以降、データベースから全てのテーブルと外部キーを削除して、全てのマイグレーションを最初から再適用することが出来ます。\n\n```\nyii migrate/fresh       # データベースを削除し、全てのマイグレーションを最初から適用する\n```\n\n## マイグレーションをリスト表示する <span id=\"listing-migrations\"></span>\n\nどのマイグレーションが適用済みであり、どのマイグレーションが未適用であるかをリスト表示するために、次のコマンドを使うことが出来ます。\n\n```\nyii migrate/history     # 最後に適用された 10 個のマイグレーションを表示\nyii migrate/history 5   # 最後に適用された 5 個のマイグレーションを表示\nyii migrate/history all # 適用された全てのマイグレーションを表示\n\nyii migrate/new         # 適用可能な最初の 10 個のマイグレーションを表示\nyii migrate/new 5       # 適用可能な最初の 5 個のマイグレーションを表示\nyii migrate/new all     # 適用可能な全てのマイグレーションを表示\n```\n\n\n## マイグレーション履歴を修正する <span id=\"modifying-migration-history\"></span>\n\n時として、実際にマイグレーションを適用したり取り消したりするのではなく、\nデータベースが特定のマイグレーションまでアップグレードされたとマークしたいだけ、という場合があります。\nこのようなことがよく起るのは、データベースを手作業で特定の状態に変更した後に、その変更のための一つまたは複数のマイグレーションを記録はするが再度適用はしたくない、という場合です。\n次のコマンドでこの目的を達することが出来ます。\n\n```\nyii migrate/mark 150101_185401                      # タイムスタンプを使ってマイグレーションを指定\nyii migrate/mark \"2015-01-01 18:54:01\"              # strtotime() によって解釈できる文字列を使用\nyii migrate/mark m150101_185401_create_news_table   # フルネームを使用\nyii migrate/mark 1392853618                         # UNIX タイムスタンプを使用\n```\n\nこのコマンドは、一定の行を追加または削除して、`migration` テーブルを修正し、データベースが指定されたものまでマイグレーションが適用済みであることを示します。\nこのコマンドによってマイグレーションが適用されたり取り消されたりはしません。\n\n\n## マイグレーションをカスタマイズする <span id=\"customizing-migrations\"></span>\n\nマイグレーションコマンドをカスタマイズする方法がいくつかあります。\n\n\n### コマンドライン・オプションを使う <span id=\"using-command-line-options\"></span>\n\nマイグレーション・コマンドには、その動作をカスタマイズするために使うことが出来るコマンドライン・オプションがいくつかあります。\n\n* `interactive`: 真偽値 (デフォルト値は true)。マイグレーションを対話モードで実行するかどうかを指定します。\n  true である場合は、コマンドが何らかの操作を実行する前に、ユーザは確認を求められます。\n  コマンドがバックグラウンドのプロセスで使用される場合は、このオプションを false にセットします。\n\n* `migrationPath`: 文字列 (デフォルト値は `@app/migrations`)。\n  全てのマイグレーション・クラス・ファイルを保存しているディレクトリを指定します。\n  この値は、ディレクトリ・パスか、パス・[エイリアス](concept-aliases.md) として指定することが出来ます。\n  ディレクトリが存在する必要があり、そうでなければコマンドがエラーを発生させることに注意してください。\n\n* `migrationTable`: 文字列 (デフォルト値は `migration`)。マイグレーション履歴の情報を保存するためのデータベース・テーブル名を指定します。\n  テーブルが存在しない場合は、コマンドによって自動的に作成されます。\n  `version varchar(255) primary key, apply_time integer` という構造のテーブルを手作業で作成しても構いません。\n\n* `db`: 文字列 (デフォルト値は `db`)。データベース [アプリケーション・コンポーネント](structure-application-components.md) の ID を指定します。\n  このコマンドによってマイグレーションを適用されるデータベースを表します。\n\n* `templateFile`: 文字列 (デフォルト値は `@yii/views/migration.php`)。\n  スケルトンのマイグレーション・クラス・ファイルを生成するために使用されるテンプレート・ファイルのパスを指定します。\n  この値は、ファイル・パスか、パス [エイリアス](concept-aliases.md) として指定することが出来ます。\n  テンプレート・ファイルは PHP スクリプトであり、その中で、マイグレーション・クラスの名前を取得するための `$className` という事前定義された変数を使うことが出来ます。\n\n* `generatorTemplateFiles`: 配列 (デフォルト値は `[\n        'create_table' => '@yii/views/createTableMigration.php',\n        'drop_table' => '@yii/views/dropTableMigration.php',\n        'add_column' => '@yii/views/addColumnMigration.php',\n        'drop_column' => '@yii/views/dropColumnMigration.php',\n        'create_junction' => '@yii/views/createTableMigration.php'\n  ]`)。マイグレーション・コードを生成するためのテンプレート・ファイルを指定します。\n  詳細は \"[マイグレーションを生成する](#generating-migrations)\" を参照してください。\n  \n* `fields`: マイグレーション・コードを生成するためのカラム定義文字列の配列。\n  デフォルト値は `[]`。個々の定義の書式は `COLUMN_NAME:COLUMN_TYPE:COLUMN_DECORATOR` です。\n  例えば、`--fields=name:string(12):notNull` は、サイズが 12 の null でない文字列カラムを作成します。\n\n次の例は、これらのオプションの使い方を示すものです。\n\n例えば、`forum` モジュールにマイグレーションを適用しようとしており、\nそのマイグレーション・ファイルがモジュールの `migrations` ディレクトリに配置されている場合、\n次のコマンドを使うことが出来ます。\n\n```\n# forum モジュールのマイグレーションを非対話的に適用する\nyii migrate --migrationPath=@app/modules/forum/migrations --interactive=0\n```\n\n\n### コマンドをグローバルに構成する <span id=\"configuring-command-globally\"></span>\n\nマイグレーション・コマンドを実行するたびに同じオプションの値を入力する代りに、次のように、\nアプリケーションの構成情報でコマンドを一度だけ構成して済ませることが出来ます。\n\n```php\nreturn [\n    'controllerMap' => [\n        'migrate' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationTable' => 'backend_migration',\n        ],\n    ],\n];\n```\n\n上記のように構成しておくと、`migrate` コマンドを実行するたびに、\n`backend_migration` テーブルがマイグレーション履歴を記録するために使われるようになります。\nもう、`migrationTable` のコマンドライン・オプションを使ってテーブルを指定する必要はなくなります。\n\n\n### 名前空間を持つマイグレーション <span id=\"namespaced-migrations\"></span>\n\n2.0.10 以降では、マイグレーションのクラスに名前空間を適用することが出来ます。\nマイグレーションの名前空間のリストをを [[yii\\console\\controllers\\MigrateController::migrationNamespaces|migrationNamespaces]] によって指定することが出来ます。\nマイグレーションのクラスに名前空間を使うと、マイグレーションのソースについて、複数の配置場所を使用することが出来ます。例えば、\n\n```php\nreturn [\n    'controllerMap' => [\n        'migrate' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationPath' => null, // app\\migrations が下記にあげられている場合に、名前空間に属さないマイグレーションを無効化する\n            'migrationNamespaces' => [\n                'app\\migrations', // アプリケーション全体のための共通のマイグレーション\n                'module\\migrations', // プロジェクトの特定のモジュールのためのマイグレーション\n                'some\\extension\\migrations', // 特定のエクステンションのためのマイグレーション\n            ],\n        ],\n    ],\n];\n```\n\n> Note: 異なる名前空間に属するマイグレーションを適用しても、**単一の** マイグレーション履歴が生成されます。\n  つまり、特定の名前空間に属するマイグレーションだけを適用したり元に戻したりすることは出来ません。\n\n名前空間を持つマイグレーションを操作するときは、新規作成時も、元に戻すときも、マイグレーション名の前にフルパスの名前空間を指定しなければなりません。\nバック・スラッシュ (`\\`) のシンボルは、通常、シェルでは特殊文字として扱われますので、シェルのエラーや誤った動作を防止するために、\n適切にエスケープしなければならないことに注意して下さい。例えば、\n\n```\nyii migrate/create app\\\\migrations\\\\createUserTable\n```\n\n> Note: [[yii\\console\\controllers\\MigrateController::migrationPath|migrationPath]] によって指定されたマイグレーションは、\n  名前空間を持つことが出来ません。  名前空間を持つマイグレーションは [[yii\\console\\controllers\\MigrateController::migrationNamespaces]]\n  プロパティを通じてのみ適用可能です。\n\nバージョン 2.0.12 以降は [[yii\\console\\controllers\\MigrateController::migrationPath|migrationPath]] プロパティは\n名前空間を持たないマイグレーションを含む複数のディレクトリを指定した配列を受け入れるようになりました。\nこの機能追加は、主として、いろんな場所にあるマイグレーションを使っている既存のプロジェクトによって使われることを意図しています。\nこれらのマイグレーションは、主として、他の開発者による Yii エクステンションなど、外部ソースに由来するものであり、\n新しい手法を使い始めようとしても、名前空間を使うように変更することが簡単には出来ないものだからです。\n\n#### 名前空間を持つマイグレーションを生成する\n\n名前空間を持つマイグレーションは \"CamelCase\" の命名規則 `M<YYMMDDHHMMSS><Name>` (例えば `M190720100234CreateUserTable`) を持ちます。\nこのようなマイグレーションを生成するときは、テーブル名が \"CamenCase\" 形式から \"アンダースコア\" 形式に変換されることを\n覚えておいて下さい。例えば、\n\n```\nyii migrate/create app\\\\migrations\\\\DropGreenHotelTable\n```\n\n上記のコマンドは、名前空間 `app\\migrations` の中で、`green_hotel` というテーブルを削除するマイグレーションを生成します。そして、\n\n```\nyii migrate/create app\\\\migrations\\\\CreateBANANATable\n```\n\nというコマンドは、名前空間 `app\\migrations` の中で `b_a_n_a_n_a` というテーブルを作成するマイグレーションを生成します。\n\nテーブル名が大文字と小文字を含む（例えば `studentsExam`) ときは、名前の先頭にアンダースコアを付ける必要があります。\n\n```\nyii migrate/create app\\\\migrations\\\\Create_studentsExamTable\n```\n\nこのコマンドは、名前空間 `app\\migrations` の中で `studentsExam` というテーブルを作成するマイグレーションを生成します。\n\n### 分離されたマイグレーション <span id=\"separated-migrations\"></span>\n\nプロジェクトのマイグレーション全体に単一のマイグレーション履歴を使用することが望ましくない場合もあります。\n例えば、完全に独立した機能性とそれ自身のためのマイグレーションを持つような 'blog' エクステンションをインストールする場合には、\nメインのプロジェクトの機能専用のマイグレーションに影響を与えたくないでしょう。\n\nこれらをお互いに完全に分離して適用かつ追跡したい場合は、別々の名前空間とマイグレーション履歴テーブルを使う\n複数のマイグレーションコマンドを構成することが出来ます。\n\n```php\nreturn [\n    'controllerMap' => [\n        // アプリケーション全体のための共通のマイグレーション\n        'migrate-app' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationNamespaces' => ['app\\migrations'],\n            'migrationTable' => 'migration_app',\n            'migrationPath' => null,\n        ],\n        // 特定のモジュールのためのマイグレーション\n        'migrate-module' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationNamespaces' => ['module\\migrations'],\n            'migrationTable' => 'migration_module',\n            'migrationPath' => null,\n        ],\n        // 特定のエクステンションのためのマイグレーション\n        'migrate-rbac' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationPath' => '@yii/rbac/migrations',\n            'migrationTable' => 'migration_rbac',\n        ],\n    ],\n];\n```\n\nデータベースを同期するためには、一つではなく複数のコマンドを実行しなければならなくなることに注意してください。\n\n```\nyii migrate-app\nyii migrate-module\nyii migrate-rbac\n```\n\n\n## 複数のデータベースにマイグレーションを適用する <span id=\"migrating-multiple-databases\"></span>\n\nデフォルトでは、マイグレーションは `db` [アプリケーション・コンポーネント](structure-application-components.md) によって指定された同じデータベースに対して適用されます。\nマイグレーションを別のデータベースに適用したい場合は、次のように、`db` コマンドライン・オプションを指定することが出来ます。\n\n```\nyii migrate --db=db2\n```\n\n上記のコマンドはマイグレーションを `db2` データベースに適用します。\n\n場合によっては、*いくつかの* マイグレーションはあるデータベースに適用し、*別のいくつかの* マイグレーションはもう一つのデータベースに適用したい、ということがあります。\nこの目的を達するためには、マイグレーション・クラスを実装する時に、そのマイグレーションが使用する DB コンポーネントの ID を明示的に指定しなければなりません。\n例えば、次のようにします。\n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function init()\n    {\n        $this->db = 'db2';\n        parent::init();\n    }\n}\n```\n\n上記のマイグレーションは、`db` コマンドライン・オプションで別のデータベースを指定した場合でも、`db2` に対して適用されます。\nただし、マイグレーション履歴は、`db` コマンドライン・オプションで指定されたデータベースに記録されることに注意してください。\n\n同じデータベースを使う複数のマイグレーションがある場合は、上記の `init()` コードを持つ基底のマイグレーション・クラスを作成することを推奨します。\nそうすれば、個々のマイグレーション・クラスは、その基底クラスから拡張することが出来ます。\n\n> Tip: 異なるデータベースを操作するためには、[[yii\\db\\Migration::db|db]] プロパティを設定する以外にも、\n  マイグレーション・クラスの中で新しいデータベース接続を作成するという方法があります。\n  そうすれば、そのデータベース接続で [DAO メソッド](db-dao.md) を使って、違うデータベースを操作することが出来ます。\n\n複数のデータベースに対してマイグレーションを適用するために採用できるもう一つの戦略としては、異なるデータベースに対するマイグレーションは異なるマイグレーションパスに保持する、というものがあります。\nそうすれば、次のように、異なるデータベースのマイグレーションを別々のコマンドで適用することが出来ます。\n\n```\nyii migrate --migrationPath=@app/migrations/db1 --db=db1\nyii migrate --migrationPath=@app/migrations/db2 --db=db2\n...\n```\n\n最初のコマンドは `@app/migrations/db1` にあるマイグレーションを `db1` データベースに適用し、\n第二のコマンドは `@app/migrations/db2` にあるマイグレーションを `db2` データベースに適用する、という具合です。\n"
  },
  {
    "path": "docs/guide-ja/db-query-builder.md",
    "content": "クエリ・ビルダ\n==============\n\n[データベース・アクセス・オブジェクト](db-dao.md) の上に構築されているクエリ・ビルダは、SQL クエリをプログラム的に、\nかつ、DBMS の違いを意識せずに作成することを可能にしてくれます。\nクエリ・ビルダを使うと、生の SQL 文を書くことに比べて、より読みやすい SQL 関連のコードを書き、より安全な SQL 文を生成することが容易になります。\n\n通常、クエリ・ビルダの使用は、二つのステップから成ります。\n\n1. SELECT SQL 文のさまざまな部分 (例えば、`SELECT`、`FROM`) を表現する [[yii\\db\\Query]] オブジェクトを構築する。\n2. [[yii\\db\\Query]] のクエリ・メソッド (例えば `all()`) を実行して、データベースからデータを取得する。\n\n次のコードは、クエリ・ビルダを使用する典型的な方法を示すものです。\n\n```php\n$rows = (new \\yii\\db\\Query())\n    ->select(['id', 'email'])\n    ->from('user')\n    ->where(['last_name' => 'Smith'])\n    ->limit(10)\n    ->all();\n```\n\n上記のコードは、次の SQL クエリを生成して実行します。\nここでは、`:last_name` パラメータは `'Smith'` という文字列にバインドされています。\n\n```sql\nSELECT `id`, `email` \nFROM `user`\nWHERE `last_name` = :last_name\nLIMIT 10\n```\n\n> Info: 通常は、[[yii\\db\\QueryBuilder]] ではなく、主として [[yii\\db\\Query]] を使用します。\n  前者は、クエリ・メソッドの一つを呼ぶときに、後者によって黙示的に起動されます。\n  [[yii\\db\\QueryBuilder]] は、DBMS に依存しない [[yii\\db\\Query]] オブジェクトから、DBMS に依存する SQL 文を生成する\n  (例えば、テーブルやカラムの名前を DBMS ごとに違う方法で引用符で囲む) 役割を負っているクラスです。\n\n\n## クエリを構築する <span id=\"building-queries\"></span>\n\n[[yii\\db\\Query]] オブジェクトを構築するために、さまざまなクエリ構築メソッドを呼んで、SQL クエリのさまざまな部分を定義します。\nこれらのメソッドの名前は、SQL 文の対応する部分に使われる SQL キーワードに似たものになっています。\n例えば、SQL クエリの `FROM` の部分を定義するためには、`from()` メソッドを呼び出します。\nクエリ構築メソッドは、すべて、クエリ・オブジェクトそのものを返しますので、複数の呼び出しをチェーンしてまとめることが出来ます。\n\n以下で、それぞれのクエリ構築メソッドの使用方法を説明しましょう。\n\n\n### [[yii\\db\\Query::select()|select()]] <span id=\"select\"></span>\n\n[[yii\\db\\Query::select()|select()]] メソッドは、SQL 文の `SELECT` 句を定義します。\n選択されるカラムは、次のように、配列または文字列として指定することが出来ます。\n選択されるカラムの名前は、クエリ・オブジェクトから SQL 文が生成されるときに、自動的に引用符で囲まれます。\n\n```php\n$query->select(['id', 'email']);\n\n// 次と等価\n\n$query->select('id, email');\n```\n\n選択されるカラム名は、生の SQL クエリを書くときにするように、テーブル接頭辞 および/または カラムのエイリアスを含むことが出来ます。\n例えば、\n\n```php\n$query->select(['user.id AS user_id', 'email']);\n\n// 次と等価\n\n$query->select('user.id AS user_id, email');\n```\n\nカラムを指定するのに配列形式を使っている場合は、配列のキーを使ってカラムのエイリアスを指定することも出来ます。\n例えば、上記のコードは次のように書くことが出来ます。\n\n```php\n$query->select(['user_id' => 'user.id', 'email']);\n```\n\nクエリを構築するときに [[yii\\db\\Query::select()|select()]] メソッドを呼ばなかった場合は、`*` がセレクトされます。\nすなわち、*全て* のカラムが選択されることになります。\n\nカラム名に加えて、DB 式をセレクトすることも出来ます。\nカンマを含む DB 式をセレクトする場合は、自動的に引用符で囲む機能が誤動作しないように、配列形式を使わなければなりません。例えば、\n\n```php\n$query->select([\"CONCAT(first_name, ' ', last_name) AS full_name\", 'email']); \n```\n\n生の SQL が使われる場所ではどこでもそうですが、セレクトに DB 式を書く場合には、テーブルやカラムの名前を表すために\n[特定のデータベースに依存しない引用符の構文](db-dao.md#quoting-table-and-column-names) を使うことが出来ます。\n\nバージョン 2.0.1 以降では、サブ・クエリもセレクトすることが出来ます。\n各サブ・クエリは、[[yii\\db\\Query]] オブジェクトの形で指定しなければなりません。例えば、\n\n```php\n$subQuery = (new Query())->select('COUNT(*)')->from('user');\n\n// SELECT `id`, (SELECT COUNT(*) FROM `user`) AS `count` FROM `post`\n$query = (new Query())->select(['id', 'count' => $subQuery])->from('post');\n```\n\n重複行を除外してセレクトするためには、次のように、[[yii\\db\\Query::distinct()|distinct()]] を呼ぶことが出来ます。\n\n```php\n// SELECT DISTINCT `user_id` ...\n$query->select('user_id')->distinct();\n```\n\n追加のカラムをセレクトするためには [[yii\\db\\Query::addSelect()|addSelect()]] を呼ぶことが出来ます。例えば、\n\n```php\n$query->select(['id', 'username'])\n    ->addSelect(['email']);\n```\n\n\n### [[yii\\db\\Query::from()|from()]] <span id=\"from\"></span>\n\n[[yii\\db\\Query::from()|from()]] メソッドは、SQL 文の `FROM` 句を定義します。例えば、\n\n```php\n// SELECT * FROM `user`\n$query->from('user');\n```\n\nセレクトの対象になる (一つまたは複数の) テーブルは、文字列または配列として指定することが出来ます。\nテーブル名は、生の SQL 文を書くときにするように、スキーマ接頭辞 および/または テーブル・エイリアスを含むことが出来ます。例えば、\n\n```php\n$query->from(['public.user u', 'public.post p']);\n\n// 次と等価\n\n$query->from('public.user u, public.post p');\n```\n\n配列形式を使う場合は、次のように、配列のキーを使ってテーブル・エイリアスを指定することも出来ます。\n\n```php\n$query->from(['u' => 'public.user', 'p' => 'public.post']);\n```\n\nテーブル名の他に、[[yii\\db\\Query]] オブジェクトの形で指定することによって、サブ・クエリをセレクトの対象とすることも出来ます。\n例えば、\n\n```php\n$subQuery = (new Query())->select('id')->from('user')->where('status=1');\n\n// SELECT * FROM (SELECT `id` FROM `user` WHERE status=1) u \n$query->from(['u' => $subQuery]);\n```\n\n#### プレフィックス\nまた、デフォルトの [[yii\\db\\Connection::$tablePrefix|tablePrefix]] を適用することも出来ます。\n実装の仕方は [\"データベース・アクセス・オブジェクト\" ガイドの \"テーブル名を引用符で囲む\" のセクション](db-dao.md#quoting-table-and-column-names) にあります。\n\n### [[yii\\db\\Query::where()|where()]] <span id=\"where\"></span>\n\n[[yii\\db\\Query::where()|where()]] メソッドは、SQL クエリの `WHERE` 句を定義します。\n`WHERE` の条件を指定するために、次の4つの形式から一つを選んで使うことが出来ます。\n\n- 文字列形式、例えば、`'status=1'`\n- ハッシュ形式、例えば、`['status' => 1, 'type' => 2]`\n- 演算子形式、例えば、`['like', 'name', 'test']`\n- オブジェクト形式、例えば、`new LikeCondition('name', 'LIKE', 'test')`\n\n#### 文字列形式 <span id=\"string-format\"></span>\n\n文字列形式は、非常に単純な条件を定義する場合や、DBMS の組み込み関数を使う必要がある場合に最適です。\nこれは、生の SQL を書いている場合と同じように動作します。例えば、\n\n```php\n$query->where('status=1');\n\n// あるいは、パラメータ・バインディングを使って、動的にパラメータをバインドする\n$query->where('status=:status', [':status' => $status]);\n\n// date フィールドに対して MySQL の YEAR() 関数を使う生の SQL\n$query->where('YEAR(somedate) = 2015');\n```\n\n次のように、条件式に変数を直接に埋め込んではいけません。\n特に、変数の値がユーザの入力に由来する場合、あなたのアプリケーションを SQL インジェクション攻撃にさらすことになりますので、してはいけません。\n\n```php\n// 危険! $status が整数であることが絶対に確実でなければ、してはいけない。\n$query->where(\"status=$status\");\n```\n\n`パラメータ・バインディング` を使う場合は、[[yii\\db\\Query::params()|params()]] または [[yii\\db\\Query::addParams()|addParams()]]\nを使って、パラメータの指定を分離することが出来ます。\n\n```php\n$query->where('status=:status')\n    ->addParams([':status' => $status]);\n```\n\n生の SQL が使われる場所ではどこでもそうですが、文字列形式で条件を書く場合には、テーブルやカラムの名前を表すために\n[特定のデータベースに依存しない引用符の構文](db-dao.md#quoting-table-and-column-names) を使うことが出来ます。\n\n#### ハッシュ形式 <span id=\"hash-format\"></span>\n\n値が等しいことを要求する単純な条件をいくつか `AND` で結合する場合は、ハッシュ形式を使うのが最適です。\n個々の条件を表す配列の各要素は、キーをカラム名、値をそのカラムが持つべき値とします。\n例えば、\n\n```php\n// ...WHERE (`status` = 10) AND (`type` IS NULL) AND (`id` IN (4, 8, 15))\n$query->where([\n    'status' => 10,\n    'type' => null,\n    'id' => [4, 8, 15],\n]);\n```\n\nご覧のように、クエリ・ビルダは頭が良いので、null や配列である値も、適切に処理します。\n\n次のように、サブ・クエリをハッシュ形式で使うことも出来ます。\n\n```php\n$userQuery = (new Query())->select('id')->from('user');\n\n// ...WHERE `id` IN (SELECT `id` FROM `user`)\n$query->where(['id' => $userQuery]);\n```\n\nハッシュ形式を使う場合、Yii は内部的にパラメータ・バインディングを使用します。\n従って、[文字列形式](#string-format) とは対照的に、ここでは手動でパラメータを追加する必要はありません。ただし、Yii はカラム名を決してエスケープしないことに注意して下さい。\n従って、ユーザから取得した変数を何ら追加のチェックをすることなくカラム名として渡すと、SQL インジェクション攻撃に対して脆弱になります。\nアプリケーションを安全に保つためには、カラム名として変数を使わないこと、または、変数を許容リストによってフィルターすることが必要です。\nカラム名をユーザから取得する必要がある場合は、ガイドの [データをフィルタリングする](output-data-widgets.md#filtering-data) という記事を読んで下さい。\n例えば、次のコードは脆弱です。\n\n```php\n// 脆弱なコード:\n$column = $request->get('column');\n$value = $request->get('value');\n$query->where([$column => $value]);\n// $value は安全です。しかし、$column の名前はエンコードされません。\n```\n\n#### 演算子形式 <span id=\"operator-format\"></span>\n\n演算子形式を使うと、任意の条件をプログラム的な方法で指定することが出来ます。これは次の形式を取るものです。\n\n```php\n[演算子, オペランド1, オペランド2, ...]\n```\n\nここで、各オペランドは、文字列形式、ハッシュ形式、あるいは、再帰的に演算子形式として指定することが出来ます。\nそして、演算子には、次のどれか一つを使うことが出来ます。\n\n- `and`: 複数のオペランドが `AND` を使って結合されます。\n  例えば、`['and', 'id=1', 'id=2']` は `id=1 AND id=2` を生成します。\n  オペランドが配列である場合は、ここで説明されている規則に従って文字列に変換されます。\n  例えば、`['and', 'type=1', ['or', 'id=1', 'id=2']]` は `type=1 AND (id=1 OR id=2)` を生成します。\n  このメソッドは、文字列を引用符で囲ったりエスケープしたりしません。\n\n- `or`: オペランドが `OR` を使って結合されること以外は `and` 演算子と同じです。\n\n- `not`: オペランド1 だけを受け取って `NOT()` で包みます。例えば、`['not', 'id=1']` は `NOT (id=1)` を生成します。オペランド1 は、それ自体も複数の式を表す配列であっても構いません。例えば、`['not', ['status' => 'draft', 'name' => 'example']]` は `NOT ((status='draft') AND (name='example'))` を生成します。\n\n- `between`: オペランド 1 はカラム名、オペランド 2 と 3 はカラムの値が属すべき範囲の開始値と終了値としなければなりません。\n  例えば、`['between', 'id', 1, 10]` は `id BETWEEN 1 AND 10` を生成します。\n  値が二つのカラムの値の間にあるという条件 (例えば、`11 BETWEEN min_id AND max_id`) を構築する必要がある場合は、\n  [[yii\\db\\conditions\\BetweenColumnsCondition|BetweenColumnsCondition]] を使用しなければなりません。\n  条件定義のオブジェクト形式について更に学習するためには [条件 – オブジェクト形式](#object-format)\n  のセクションを参照して下さい。\n\n- `not between`: 生成される条件において `BETWEEN` が `NOT BETWEEN` に置き換えられる以外は、\n  `between` と同じです。\n\n- `in`: オペランド 1 はカラム名または DB 式でなければなりません。\n  オペランド 2 は、配列または `Query` オブジェクトのどちらかを取ることが出来ます。\n  オペランド 2 が配列である場合は、その配列は、カラムまたは DB 式が該当すべき値域を表すものとされます。\n  オペランド 2 が `Query` オブジェクトである場合は、サブ・クエリが生成されて、カラムまたは DB 式の値域として使われます。\n  例えば、`['in', 'id', [1, 2, 3]]` は `id IN (1, 2, 3)` を生成します。\n  このメソッドは、カラム名を適切に引用符で囲み、値域の値をエスケープします。\n  `in` 演算子はまた複合カラムをもサポートしています。\n  その場合、オペランド 1 はカラム名の配列とし、オペランド 2 は配列の配列、または、複合カラムの値域を表す `Query` オブジェクトでなければなりません。\n  例えば、`['in', ['id', 'name'], [['id' => 1, 'name' => 'oy']]]` は `(id, name) IN ((1, 'oy'))` を生成します。\n\n- `not in`: 生成される条件において `IN` が `NOT IN` に置き換えられる以外は、`in` と同じです。\n\n- `like`: オペランド 1 はカラム名または DB 式、オペランド 2 はそのカラムまたは DB 式がマッチすべき値を示す\n  文字列または配列でなければなりません。\n  例えば、`['like', 'name', 'tester']` は `name LIKE '%tester%'` を生成します。\n  値域が配列として与えられた場合は、複数の `LIKE` 述語が生成されて 'AND' によって結合されます。\n  例えば、`['like', 'name', ['test', 'sample']]` は\n  `name LIKE '%test%' AND name LIKE '%sample%'` を生成します。\n  さらに、オプションである三番目のオペランドによって、値の中の特殊文字をエスケープする方法を指定することも出来ます。\n  このオペランド 3 は、特殊文字とそのエスケープ結果のマッピングを示す配列でなければなりません。\n  このオペランドが提供されない場合は、デフォルトのエスケープ・マッピングが使用されます。\n  `false` または空の配列を使って、値が既にエスケープ済みであり、それ以上エスケープを適用すべきでないことを示すことが出来ます。\n  エスケープマッピングを使用する場合 (または第三のオペランドが与えられない場合) は、\n  値が自動的に一対のパーセント記号によって囲まれることに注意してください。\n\n  > Note: PostgreSQL を使っている場合は、`like` の代りに、大文字と小文字を区別しない比較のための\n  > [`ilike`](https://www.postgresql.org/docs/8.3/static/functions-matching.html#FUNCTIONS-LIKE) を使うことも出来ます。\n\n- `or like`: オペランド 2 が配列である場合に `LIKE` 述語が `OR` によって結合される以外は、\n  `like` 演算子と同じです。\n\n- `not like`: 生成される条件において `LIKE` が `NOT LIKE` に置き換えられる以外は、\n  `like` 演算子と同じです。\n\n- `or not like`: `NOT LIKE` 述語が `OR` によって結合される以外は、\n  `not like` 演算子と同じです。\n\n- `exists`: 要求される一つだけのオペランドは、サブ・クエリを表す [[yii\\db\\Query]] のインスタンスでなければなりません。\n  これは `EXISTS (sub-query)` という式を構築します。\n\n- `not exists`: `exists` 演算子と同じで、`NOT EXISTS (sub-query)` という式を構築します。\n\n- `>`、`<=`、その他、二つのオペランドを取る有効な DB 演算子全て: 最初のオペランドはカラム名、第二のオペランドは値でなければなりません。\n  例えば、`['>', 'age', 10]` は `age>10` を生成します。\n\n演算子形式を使う場合、Yii は値に対して内部的にパラメータ・バインディングを使用します。\n従って、[文字列形式](#string-format) とは対照的に、ここでは手動でパラメータを追加する必要はありません。\nただし、Yii はカラム名を決してエスケープしないことに注意して下さい。従って、変数をカラム名として渡すと、アプリケーションは SQL インジェクション攻撃に対して脆弱になります。\nアプリケーションを安全に保つためには、カラム名として変数を使わないこと、または、変数を許容リストによってフィルターすることが必要です。\nカラム名をユーザから取得する必要がある場合は、ガイドの [データをフィルタリングする](output-data-widgets.md#filtering-data) という記事を読んで下さい。\n例えば、次のコードは脆弱です。\n\n```php\n// 脆弱なコード:\n$column = $request->get('column');\n$value = $request->get('value');\n$query->where([$column => $value]);\n// $value は安全です。しかし、$column の名前はエンコードされません。\n```\n\n#### オブジェクト形式 <span id=\"object-format\"></span>\n\nオブジェクト形式は 2.0.14 から利用可能な、条件を定義するための最も強力でもあり、最も複雑でもある方法です。\nクエリ・ビルダの上にあなた自身の抽象レイヤを構築したいときや、または独自の複雑な条件を実装したいときは、\nこの形式を採用する必要があります。\n\n条件クラスのインスタンスはイミュータブルです。\n条件クラスのインスタンスは条件データを保持し、条件ビルダにゲッターを提供することを唯一の目的とします。\nそして、条件ビルダが、条件クラスのインスタンスに保存されたデータを SQL の式に変換するロジックを持つクラスです。\n\n内部的には、上述の三つの形式は、生の SQL を構築するに先立って、暗黙のうちにオブジェクト形式に変換されます。\n従って、複数の形式を単一の条件に結合することが可能です。\n\n```php\n$query->andWhere(new OrCondition([\n    new InCondition('type', 'in', $types),\n    ['like', 'name', '%good%'],\n    'disabled=false'\n]))\n```\n\n演算子形式からオブジェクト形式への変換は、\n演算子の名前とそれを表すクラス名を対応づける [[yii\\db\\QueryBuilder::conditionClasses|QueryBuilder::conditionClasses]]\nプロパティに従って行われます。\n\n- `AND`, `OR` -> `yii\\db\\conditions\\ConjunctionCondition`\n- `NOT` -> `yii\\db\\conditions\\NotCondition`\n- `IN`, `NOT IN` -> `yii\\db\\conditions\\InCondition`\n- `BETWEEN`, `NOT BETWEEN` -> `yii\\db\\conditions\\BetweenCondition`\n\n等々。\n\nオブジェクト形式を使うことによって、あなた独自の条件を作成したり、デフォルトの条件が作成される方法を変更したりすることが可能になります。\n詳細は [特製の条件や式を追加する](#adding-custom-conditions-and-expressions) のセクションを参照して下さい。\n\n\n#### 条件を追加する <span id=\"appending-conditions\"></span>\n\n[[yii\\db\\Query::andWhere()|andWhere()]] または [[yii\\db\\Query::orWhere()|orWhere()]] を使って、既存の条件に別の条件を追加することが出来ます。\nこれらのメソッドを複数回呼んで、複数の条件を別々に追加することが出来ます。\n例えば、\n\n```php\n$status = 10;\n$search = 'yii';\n\n$query->where(['status' => $status]);\n\nif (!empty($search)) {\n    $query->andWhere(['like', 'title', $search]);\n}\n```\n\n`$search` が空でない場合は次の `WHERE` 条件 が生成されます。\n\n```sql\nWHERE (`status` = 10) AND (`title` LIKE '%yii%')\n```\n\n\n#### フィルタ条件 <span id=\"filter-conditions\"></span>\n\nユーザの入力に基づいて `WHERE` の条件を構築する場合、普通は、空の入力値は無視したいものです。\n例えば、ユーザ名とメール・アドレスによる検索が可能な検索フォームにおいては、\nユーザが username/email のインプット・フィールドに何も入力しなかった場合は、username/email の条件を無視したいでしょう。\n[[yii\\db\\Query::filterWhere()|filterWhere()]] メソッドを使うことによって、この目的を達することが出来ます。\n\n```php\n// $username と $email はユーザの入力による\n$query->filterWhere([\n    'username' => $username,\n    'email' => $email,\n]);\n```\n\n[[yii\\db\\Query::filterWhere()|filterWhere()]] と [[yii\\db\\Query::where()|where()]] の唯一の違いは、\n前者は [ハッシュ形式](#hash-format) の条件において提供された空の値を無視する、という点です。\n従って、`$email` が空で `$sername` がそうではない場合は、上記のコードは、結果として `WHERE username=:username` という SQL 条件になります。\n\n> Info: 値が空であると見なされるのは、`null`、空の配列、空の文字列、または空白のみを含む文字列である場合です。\n\n[[yii\\db\\Query::andWhere()|andWhere()]] または [[yii\\db\\Query::orWhere()|orWhere()]] と同じように、\n[[yii\\db\\Query::andFilterWhere()|andFilterWhere()]] または [[yii\\db\\Query::orFilterWhere()|orFilterWhere()]] を使って、\n既存の条件に別のフィルタ条件を追加することも出来ます。\n\nさらに加えて、値の方に含まれている比較演算子を適切に判断してくれる\n[[yii\\db\\Query::andFilterCompare()]] があります。\n\n```php\n$query->andFilterCompare('name', 'John Doe');\n$query->andFilterCompare('rating', '>9');\n$query->andFilterCompare('value', '<=100');\n```\n\n演算子を明示的に指定することも可能です。\n\n```php\n$query->andFilterCompare('name', 'Doe', 'like');\n```\n\nYii 2.0.11 以降には、`HAVING` の条件のためにも、同様のメソッドがあります。\n\n- [[yii\\db\\Query::filterHaving()|filterHaving()]]\n- [[yii\\db\\Query::andFilterHaving()|andFilterHaving()]]\n- [[yii\\db\\Query::orFilterHaving()|orFilterHaving()]]\n\n### [[yii\\db\\Query::orderBy()|orderBy()]] <span id=\"order-by\"></span>\n\n[[yii\\db\\Query::orderBy()|orderBy()]] メソッドは SQL クエリの `ORDER BY` 句を指定します。例えば、\n\n```php\n// ... ORDER BY `id` ASC, `name` DESC\n$query->orderBy([\n    'id' => SORT_ASC,\n    'name' => SORT_DESC,\n]);\n```\n\n上記のコードにおいて、配列のキーはカラム名であり、配列の値は並べ替えの方向です。\nPHP の定数 `SORT_ASC` は昇順、`SORT_DESC` は降順を指定するものです。\n\n`ORDER BY` が単純なカラム名だけを含む場合は、生の SQL 文を書くときにするように、文字列を使って指定することが出来ます。\n例えば、\n\n```php\n$query->orderBy('id ASC, name DESC');\n```\n\n> Note: `ORDER BY` が何らかの DB 式を含む場合は、配列形式を使わなければなりません。\n\n[[yii\\db\\Query::addOrderBy()|addOrderBy()]] を呼んで、`ORDER BY' 句にカラムを追加することが出来ます。\n例えば、\n\n```php\n$query->orderBy('id ASC')\n    ->addOrderBy('name DESC');\n```\n\n\n### [[yii\\db\\Query::groupBy()|groupBy()]] <span id=\"group-by\"></span>\n\n[[yii\\db\\Query::groupBy()|groupBy()]] メソッドは SQL クエリの `GROUP BY` 句を指定します。例えば、\n\n```php\n// ... GROUP BY `id`, `status`\n$query->groupBy(['id', 'status']);\n```\n\n`GROUP BY` が単純なカラム名だけを含む場合は、生の SQL 文を書くときにするように、文字列を使って指定することが出来ます。\n例えば、\n\n```php\n$query->groupBy('id, status');\n```\n\n> Note: `GROUP BY` が何らかの DB 式を含む場合は、配列形式を使わなければなりません。\n \n[[yii\\db\\Query::addGroupBy()|addGroupBy()]] を呼んで、`GROUP BY` 句にカラムを追加することが出来ます。\n例えば、\n\n```php\n$query->groupBy(['id', 'status'])\n    ->addGroupBy('age');\n```\n\n\n### [[yii\\db\\Query::having()|having()]] <span id=\"having\"></span>\n\n[[yii\\db\\Query::having()|having()]] メソッドは SQL クエリの `HAVING` 句を指定します。\nこのメソッドが取る条件は、[where()](#where) と同じ方法で指定することが出来ます。例えば、\n\n```php\n// ... HAVING `status` = 1\n$query->having(['status' => 1]);\n```\n\n条件を指定する方法の詳細については、[where()](#where) のドキュメントを参照してください。\n\n[[yii\\db\\Query::andHaving()|andHaving()]] または [[yii\\db\\Query::orHaving()|orHaving()]] を呼んで、`HAVING` 句に条件を追加することが出来ます。\n例えば、\n\n```php\n// ... HAVING (`status` = 1) AND (`age` > 30)\n$query->having(['status' => 1])\n    ->andHaving(['>', 'age', 30]);\n```\n\n\n### [[yii\\db\\Query::limit()|limit()]] と [[yii\\db\\Query::offset()|offset()]] <span id=\"limit-offset\"></span>\n\n[[yii\\db\\Query::limit()|limit()]] と [[yii\\db\\Query::offset()|offset()]] のメソッドは、SQL クエリの `LIMIT` 句と `OFFSET` 句を指定します。\n例えば、\n \n```php\n// ... LIMIT 10 OFFSET 20\n$query->limit(10)->offset(20);\n```\n\n無効な上限やオフセット (例えば、負の数) を指定した場合は、無視されます。\n\n> Info: `LIMIT` と `OFFSET` をサポートしていない DBMS (例えば MSSQL) に対しては、\nクエリ・ビルダが `LIMIT`/`OFFSET` の振る舞いをエミュレートする SQL 文を生成します。\n\n\n### [[yii\\db\\Query::join()|join()]] <span id=\"join\"></span>\n\n[[yii\\db\\Query::join()|join()]] メソッドは SQL クエリの `JOIN` 句を指定します。例えば、\n\n```php\n// ... LEFT JOIN `post` ON `post`.`user_id` = `user`.`id`\n$query->join('LEFT JOIN', 'post', 'post.user_id = user.id');\n```\n\n[[yii\\db\\Query::join()|join()]] メソッドは、四つのパラメータを取ります。\n\n- `$type`: 結合のタイプ、例えば、`'INNER JOIN'`、`'LEFT JOIN'`。\n- `$table`: 結合されるテーブルの名前。\n- `$on`: オプション。結合条件、すなわち、`ON` 句。\n   条件の指定方法の詳細については、[where()](#where) を参照してください。\n   カラムに基づく条件を指定する場合は、配列記法は**使えない**ことに注意してください。\n   例えば、`['user.id' => 'comment.userId']` は、user の id が `'comment.userId'` という文字列でなければならない、という条件に帰結します。\n   配列記法ではなく文字列記法を使って、`'user.id = comment.userId'` という条件を指定しなければなりません。\n- `$params`: オプション。結合条件にバインドされるパラメータ。\n\n`INNER JOIN`、`LEFT JOIN` および `RIGHT JOIN` を指定するためには、それぞれ、次のショートカット・メソッドを使うことが出来ます。\n\n- [[yii\\db\\Query::innerJoin()|innerJoin()]]\n- [[yii\\db\\Query::leftJoin()|leftJoin()]]\n- [[yii\\db\\Query::rightJoin()|rightJoin()]]\n\n例えば、\n\n```php\n$query->leftJoin('post', 'post.user_id = user.id');\n```\n\n複数のテーブルを結合するためには、テーブルごとに一回ずつ、上記の結合メソッドを複数回呼び出します。\n\nテーブルを結合することに加えて、サブ・クエリを結合することも出来ます。\nそうするためには、結合されるべきサブ・クエリを [[yii\\db\\Query]] オブジェクトとして指定します。例えば、\n\n```php\n$subQuery = (new \\yii\\db\\Query())->from('post');\n$query->leftJoin(['u' => $subQuery], 'u.id = author_id');\n```\n\nこの場合、サブ・クエリを配列に入れて、配列のキーを使ってエイリアスを指定しなければなりません。\n\n\n### [[yii\\db\\Query::union()|union()]] <span id=\"union\"></span>\n\n[[yii\\db\\Query::union()|union()]] メソッドは SQL クエリの `UNION` 句を指定します。例えば、\n\n```php\n$query1 = (new \\yii\\db\\Query())\n    ->select(\"id, category_id AS type, name\")\n    ->from('post')\n    ->limit(10);\n\n$query2 = (new \\yii\\db\\Query())\n    ->select('id, type, name')\n    ->from('user')\n    ->limit(10);\n\n$query1->union($query2);\n```\n\n[[yii\\db\\Query::union()|union()]] を複数回呼んで、`UNION` 句をさらに追加することが出来ます。\n\n### [[yii\\db\\Query::withQuery()|withQuery()]] <span id=\"with-query\"></span>\n\n[[yii\\db\\Query::withQuery()|withQuery()]] メソッドは SQL クエリの `WITH` プレフィックスを指定するものです。サブクエリの代りに `WITH` を使うと読みやすさを向上させ、ユニークな機能(再帰 CTE)を利用することが出来ます。詳細は [modern-sql](https://modern-sql.com/feature/with) を参照して下さい。例えば、次のクエリは `admin` の持つ権限をその子も含めて全て再帰的に取得します。\n\n```php\n$initialQuery = (new \\yii\\db\\Query())\n    ->select(['parent', 'child'])\n    ->from(['aic' => 'auth_item_child'])\n    ->where(['parent' => 'admin']);\n\n$recursiveQuery = (new \\yii\\db\\Query())\n    ->select(['aic.parent', 'aic.child'])\n    ->from(['aic' => 'auth_item_child'])\n    ->innerJoin('t1', 't1.child = aic.parent');\n\n$mainQuery = (new \\yii\\db\\Query())\n    ->select(['parent', 'child'])\n    ->from('t1')\n    ->withQuery($initialQuery->union($recursiveQuery), 't1', true);\n```\n\n[[yii\\db\\Query::withQuery()|withQuery()]] を複数回呼び出してさらなる CTE をメイン・クエリに追加することが出来ます。クエリはアタッチされたのと同じ順序でプリペンドされます。クエリのうちの一つが再帰的である場合は CTE 全体が再帰的になります。\n\n## クエリ・メソッド <span id=\"query-methods\"></span>\n\n[[yii\\db\\Query]] は、さまざまな目的のクエリのために、一揃いのメソッドを提供しています。\n\n- [[yii\\db\\Query::all()|all()]]: 各行を「名前-値」のペアの連想配列とする、結果の行の配列を返す。\n- [[yii\\db\\Query::one()|one()]]: 結果の最初の行を返す。\n- [[yii\\db\\Query::column()|column()]]: 結果の最初のカラムを返す。\n- [[yii\\db\\Query::scalar()|scalar()]]: 結果の最初の行の最初のカラムにあるスカラ値を返す。\n- [[yii\\db\\Query::exists()|exists()]]: クエリが結果を含むか否かを示す値を返す。\n- [[yii\\db\\Query::count()|count()]]: `COUNT` クエリの結果を返す。\n- その他の集計クエリ、すなわち、[[yii\\db\\Query::sum()|sum($q)]], [[yii\\db\\Query::average()|average($q)]],\n  [[yii\\db\\Query::max()|max($q)]], [[yii\\db\\Query::min()|min($q)]].\n  これらのメソッドでは、`$q` パラメータは必須であり、カラム名または DB 式とすることが出来る。\n\n例えば、\n\n```php\n// SELECT `id`, `email` FROM `user`\n$rows = (new \\yii\\db\\Query())\n    ->select(['id', 'email'])\n    ->from('user')\n    ->all();\n    \n// SELECT * FROM `user` WHERE `username` LIKE `%test%`\n$row = (new \\yii\\db\\Query())\n    ->from('user')\n    ->where(['like', 'username', 'test'])\n    ->one();\n```\n\n> Note: [[yii\\db\\Query::one()|one()]] メソッドはクエリ結果の最初の行だけを返します。このメソッドは `LIMIT 1`\n  を生成された SQL 文に追加しません。このことは、クエリが一つまたは少数の行しか返さないことが判っている場合\n  (例えば、何らかのプライマリ・キーでクエリを発行する場合) は問題になりませんし、むしろ好ましいことです。\n  しかし、クエリ結果が多数のデータ行になる可能性がある場合は、パフォーマンスを向上させるために、明示的に `limit(1)` を呼ぶべきです。例えば、\n  `(new \\yii\\db\\Query())->from('user')->limit(1)->one()`\n\n上記のメソッドの全ては、オプションで、DB クエリの実行に使用されるべき [[yii\\db\\Connection|DB 接続]] を表す `$db` パラメータを取ることが出来ます。\nこのパラメータを省略した場合は、DB 接続として `db` [アプリケーション・コンポーネント](structure-application-components.md) が使用されます。\n次に [[yii\\db\\Query::count()|count()]] クエリ・メソッドを使う例をもう一つ挙げます。\n\n```php\n// 実行される SQL: SELECT COUNT(*) FROM `user` WHERE `last_name`=:last_name\n$count = (new \\yii\\db\\Query())\n    ->from('user')\n    ->where(['last_name' => 'Smith'])\n    ->count();\n```\n\nあなたが [[yii\\db\\Query]] のクエリ・メソッドを呼び出すと、実際には、内部的に次の仕事がなされます。\n\n* [[yii\\db\\QueryBuilder]] を呼んで、[[yii\\db\\Query]] の現在の構成に基づいた SQL 文を生成する。\n* 生成された SQL 文で [[yii\\db\\Command]] オブジェクトを作成する。\n* [[yii\\db\\Command]] のクエリ・メソッド (例えば [[yii\\db\\Command::queryAll()|queryAll()]]) を呼んで、SQL 文を実行し、データを取得する。\n\n場合によっては、[[yii\\db\\Query]] オブジェクトから構築された SQL 文を調べたり使ったりしたいことがあるでしょう。\n次のコードを使って、その目的を達することが出来ます。\n\n```php\n$command = (new \\yii\\db\\Query())\n    ->select(['id', 'email'])\n    ->from('user')\n    ->where(['last_name' => 'Smith'])\n    ->limit(10)\n    ->createCommand();\n    \n// SQL 文を表示する\necho $command->sql;\n// バインドされるパラメータを表示する\nprint_r($command->params);\n\n// クエリ結果の全ての行を返す\n$rows = $command->queryAll();\n```\n\n\n## クエリ結果をインデックスする <span id=\"indexing-query-results\"></span>\n\n[[yii\\db\\Query::all()|all()]] を呼ぶと、結果の行は連続した整数でインデックスされた配列で返されます。\n場合によっては、違う方法でインデックスしたいことがあるでしょう。\n例えば、特定のカラムの値や、何らかの式の値によってインデックスするなどです。\nこの目的は、[[yii\\db\\Query::all()|all()]] の前に [[yii\\db\\Query::indexBy()|indexBy()]] を呼ぶことによって達成することが出来ます。例えば、\n\n```php\n// [100 => ['id' => 100, 'username' => '...', ...], 101 => [...], 103 => [...], ...] を返す\n$query = (new \\yii\\db\\Query())\n    ->from('user')\n    ->limit(10)\n    ->indexBy('id')\n    ->all();\n```\n\nインデックス付けが働くためには、[[yii\\db\\Query::indexBy()|indexBy()]] メソッドに渡されるカラム名が結果セットに存在する必要があります。\nそのことを保証するのは開発者の責任です。\n\n式の値によってインデックスするためには、[[yii\\db\\Query::indexBy()|indexBy()]] メソッドに無名関数を渡します。\n\n```php\n$query = (new \\yii\\db\\Query())\n    ->from('user')\n    ->indexBy(function ($row) {\n        return $row['id'] . $row['username'];\n    })->all();\n```\n\nこの無名関数は、現在の行データを含む `$row` というパラメータを取り、\n現在の行のインデックス値として使われるスカラ値を返さなくてはなりません。\n\n> Note: [[yii\\db\\Query::groupBy()|groupBy()]] や [[yii\\db\\Query::orderBy()|orderBy()]]\n> のようなクエリ・メソッドが SQL に変換されてクエリの一部となるのとは対照的に、このメソッドはデータベースからデータが取得された後で動作します。\n> このことは、クエリの SELECT に含まれるカラム名だけを使うことが出来る、ということを意味します。\n> また、テーブル・プレフィックスを付けてカラムを選択した場合、例えば `customer.id` を選択した場合は、\n> リザルトセットのカラム名は `id` しか含みませんので、テーブルプレフィックス無しで `->indexBy('id')` と呼ぶ必要があります。\n\n\n## バッチ・クエリ <span id=\"batch-query\"></span>\n\n大量のデータを扱う場合は、[[yii\\db\\Query::all()]] のようなメソッドは適していません。\nなぜなら、それらのメソッドは、クエリの結果全てをクライアントのメモリに読み込むことを必要とするためです。\nこの問題を解決するために、Yii はバッチ・クエリのサポートを提供しています。クエリ結果はサーバに保持し、\nクライアントはカーソルを利用して1回に1バッチずつ結果セットを反復取得するのです。\n\n> Warning: MySQL のバッチ・クエリの実装には既知の制約と回避策があります。下記を参照して下さい。\n\nバッチ・クエリは次のようにして使うことが出来ます。\n\n```php\nuse yii\\db\\Query;\n\n$query = (new Query())\n    ->from('user')\n    ->orderBy('id');\n\nforeach ($query->batch() as $users) {\n    // $users は user テーブルから取得した 100 以下の行の配列\n}\n\n// または、一行ずつ反復する場合は\nforeach ($query->each() as $user) {\n    // データはサーバから 100 行のバッチで取得される\n    // しかし $user は user テーブルの一つの行を表す\n}\n```\n\n[[yii\\db\\Query::batch()]] メソッドと [[yii\\db\\Query::each()]] メソッドは [[yii\\db\\BatchQueryResult]]\nオブジェクトを返します。このオブジェクトは `Iterator` インタフェイスを実装しており、従って、`foreach` 構文の中で使うことが出来ます。\n初回の反復の際に、データベースに対する SQL クエリが作成されます。データは、その後、反復のたびにバッチ・モードで取得されます。\nデフォルトでは、バッチ・サイズは 100 であり、各バッチにおいて 100 行のデータが取得されます。\nバッチ・サイズは、`batch()` または `each()` メソッドに最初のパラメータを渡すことによって変更することが出来ます。\n\n[[yii\\db\\Query::all()]] とは対照的に、バッチ・クエリは一度に 100 行のデータしかメモリに読み込みません。\n\n[[yii\\db\\Query::indexBy()]] によって、いずれかのカラムでクエリ結果をインデックスするように指定している場合でも、\nバッチ・クエリは正しいインデックスを保持します。\n\n例えば、\n\n```php\n$query = (new \\yii\\db\\Query())\n    ->from('user')\n    ->indexBy('username');\n\nforeach ($query->batch() as $users) {\n    // $users は \"username\" カラムでインデックスされている\n}\n\nforeach ($query->each() as $username => $user) {\n    // ...\n}\n```\n\n#### MySQL におけるバッチ・クエリの制約 <span id=\"batch-query-mysql\"></span>\n\nMySQL のバッチ・クエリの実装は PDO ドライバのライブラリに依存しています。デフォルトでは、MySQL のクエリは\n[`バッファ・モード`](https://www.php.net/manual/ja/mysqlinfo.concepts.buffering.php) で実行されます。\nこのことが、カーソルを使ってデータを取得する目的を挫折させます。というのは、バッファ・モードでは、\nドライバによって結果セット全体がクライアントのメモリに読み込まれることを防止できないからです。\n\n> Note: `libmysqlclient` が使われている場合 (PHP5 ではそれが普通ですが) は、\n  結果セットに使用されたメモリは PHP のメモリ使用量としてカウントされません。そのため、一見、バッチ・クエリが正しく動作するように見えますが、\n  実際には、データ・セット全体がクライアントのメモリに読み込まれて、クライアントのメモリを使い果たす可能性があります。\n\nバッファ・モードを無効化してクライアントのメモリ要求量を削減するためには、PDO 接続のプロパティ\n`PDO::MYSQL_ATTR_USE_BUFFERED_QUERY` を `false` に設定しなければなりません。しかし、そうすると、\nデータ・セット全体を取得するまでは、同じ接続を通じては別のクエリを実行できなくなります。これによって\n`ActiveRecord` が必要に応じてテーブル・スキーマを取得するためのクエリを実行できなくなる可能性があります。\nこれが問題にならない場合 (テーブル・スキーマが既にキャッシュされている場合) は、元の接続を非バッファ・モードに切り替えて、\nバッチ・クエリを実行した後に元に戻すということが可能です。\n\n```php\nYii::$app->db->pdo->setAttribute(\\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);\n\n// バッチ・クエリを実行\n\nYii::$app->db->pdo->setAttribute(\\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);\n```\n\n> Note: MyISAM の場合は、バッチ・クエリが継続している間、テーブルがロックされて、\n  他の接続からの書き込みアクセスが遅延または拒絶されることがあります。非バッファ・モードのクエリを使う場合は、\n  カーソルを開いている時間を可能な限り短くするように努めて下さい。\n\nスキーマがキャッシュされていない場合、またはバッチ・クエリを処理している間に他のクエリを走らせる必要がある場合は、\n独立した非バッファ・モードのデータベース接続を作成することが出来ます。\n\n```php\n$unbufferedDb = new \\yii\\db\\Connection([\n    'dsn' => Yii::$app->db->dsn,\n    'username' => Yii::$app->db->username,\n    'password' => Yii::$app->db->password,\n    'charset' => Yii::$app->db->charset,\n]);\n$unbufferedDb->open();\n$unbufferedDb->pdo->setAttribute(\\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);\n```\n\n`$unbufferedDb` が `PDO::MYSQL_ATTR_USE_BUFFERED_QUERY` が `false` であること以外は、\n元のバッファ・モードの `$db` と同じ PDO 属性を持つことを保証したい場合は、\n[`$db` のディープ・コピー](https://github.com/yiisoft/yii2/issues/8420#issuecomment-301423833)\nをしてから、手動で false に設定することを考慮して下さい。\n\nそして、クエリは普通に作成します。新しい接続を使ってバッチ・クエリを走らせ、結果をバッチで取得、\nまたは一つずつ取得します。\n\n```php\n// データを 1000 のバッチで取得\nforeach ($query->batch(1000, $unbufferedDb) as $users) {\n    // ...\n}\n\n\n// データは 1000 のバッチでサーバから取得されるが、一つずつ反復処理される\nforeach ($query->each(1000, $unbufferedDb) as $user) {\n    // ...\n}\n```\n\n結果セットが全て取得されて接続が必要なくなったら、接続を閉じることが出来ます。\n\n```php\n$unbufferedDb->close();\n```\n\n> Note: 非バッファ・モードのクエリは PHP 側でのメモリ消費は少なくなりますが、MySQL サーバの負荷を増加させ得ます。\n特別に巨大なデータに対するアプリの動作については、あなた自身のコードを設計することが推奨されます。\n例えば、[整数のキーで範囲を分割して、非バッファ・モードのクエリでループする](https://github.com/yiisoft/yii2/issues/8420#issuecomment-296109257) など。\n\n### 特製の条件や式を追加する <span id=\"adding-custom-conditions-and-expressions\"></span>\n\n[条件 – オブジェクト形式](#object-format) のセクションで触れたように、特製の条件クラスを作成することが可能です。\n例として、特定のカラムが一定の値より小さいことをチェックする条件を作ってみましょう。\n演算子形式を使えば、それは次のようになるでしょう。\n\n```php\n[\n    'and',\n    '>', 'posts', $minLimit,\n    '>', 'comments', $minLimit,\n    '>', 'reactions', $minLimit,\n    '>', 'subscriptions', $minLimit\n]\n```\n\nこのような条件を一度に適用できたら良いですね。一つのクエリの中で複数回使われる場合には、最適化の効果が大きいでしょう。\n特製の条件オブジェクトを作って、それを実証しましょう。\n\nYii には、条件を表現するクラスを特徴付ける [[yii\\db\\conditions\\ConditionInterface|ConditionInterface]] があります。\nこのインタフェイスは、配列形式から条件を作ることを可能にするための `fromArrayDefinition()` メソッドを実装することを要求します。\nあなたがそれを必要としない場合は、例外を投げるだけのメソッドとして実装しても構いません。\n\n特製の条件クラスを作るのですから、私たちの仕事に最適な API を構築すれば良いのです。\n\n```php\nnamespace app\\db\\conditions;\n\nclass AllGreaterCondition implements \\yii\\db\\conditions\\ConditionInterface\n{\n    private $columns;\n    private $value;\n\n    /**\n     * @param string[] $columns $value よりも大きくなければならないカラムの配列\n     * @param mixed $value 各カラムと比較する値\n     */\n    public function __construct(array $columns, $value)\n    {\n        $this->columns = $columns;\n        $this->value = $value;\n    }\n    \n    public static function fromArrayDefinition($operator, $operands)\n    {\n        throw new InvalidArgumentException('未実装、あとでやる');\n    }\n    \n    public function getColumns() { return $this->columns; }\n    public function getValue() { return $this->vaule; }\n}\n```\n\nこれで条件オブジェクトを作ることが出来ます。\n\n```php\n$conditon = new AllGreaterCondition(['col1', 'col2'], 42);\n```\n\nしかし `QueryBuilder` は、このオブジェクトから SQL 条件式を作る方法を知りません。\n次に、この条件に対する式ビルダを作成する必要があります。\n式ビルダは `build()` メソッドを提供する [[yii\\db\\ExpressionBuilderInterface]] を実装しなければいけません。\n\n```php\nnamespace app\\db\\conditions;\n\nclass AllGreaterConditionBuilder implements \\yii\\db\\ExpressionBuilderInterface\n{\n    use \\yii\\db\\ExpressionBuilderTrait; // コンストラクタと `queryBuilder` プロパティを含む。\n\n    /**\n     * @param ExpressionInterface $condition ビルドすべき条件\n     * @param array $params バインディング・パラメータ\n     * @return AllGreaterCondition\n     */ \n    public function build(ExpressionInterface $expression, array &$params = [])\n    {\n        $value = $condition->getValue();\n        \n        $conditions = [];\n        foreach ($expression->getColumns() as $column) {\n            $conditions[] = new SimpleCondition($column, '>', $value);\n        }\n\n        return $this->queryBuilder->buildCondition(new AndCondition($conditions), $params);\n    }\n}\n```\n\n後は、単に [[yii\\db\\QueryBuilder|QueryBuilder]] に私たちの新しい条件について知らせるだけです – \n条件のマッピングを `expressionBuilders` 配列に追加します。次のように、アプリケーション構成で直接に追加することが出来ます。\n\n```php\n'db' => [\n    'class' => 'yii\\db\\mysql\\Connection',\n    // ...\n    'queryBuilder' => [\n        'expressionBuilders' => [\n            'app\\db\\conditions\\AllGreaterCondition' => 'app\\db\\conditions\\AllGreaterConditionBuilder',\n        ],\n    ],\n],\n```\n\nこれで、私たちの新しい条件を `where()` で使用することが出来るようになりました。\n\n```php\n$query->andWhere(new AllGreaterCondition(['posts', 'comments', 'reactions', 'subscriptions'], $minValue));\n```\n\n演算子形式を使って私たちの特製の条件を作成することが出来るようにしたい場合は、\n演算子を [[yii\\db\\QueryBuilder::conditionClasses|QueryBuilder::conditionClasses]] の中で宣言しなければなりません。\n\n```php\n'db' => [\n    'class' => 'yii\\db\\mysql\\Connection',\n    // ...\n    'queryBuilder' => [\n        'expressionBuilders' => [\n            'app\\db\\conditions\\AllGreaterCondition' => 'app\\db\\conditions\\AllGreaterConditionBuilder',\n        ],\n        'conditionClasses' => [\n            'ALL>' => 'app\\db\\conditions\\AllGreaterCondition',\n        ],\n    ],\n],\n```\n\nそして、`app\\db\\conditions\\AllGreaterCondition` の中で `AllGreaterCondition::fromArrayDefinition()`\nメソッドの本当の実装を作成します。\n\n```php\nnamespace app\\db\\conditions;\n\nclass AllGreaterCondition implements \\yii\\db\\conditions\\ConditionInterface\n{\n    // ... 上記の実装を参照\n     \n    public static function fromArrayDefinition($operator, $operands)\n    {\n        return new static($operands[0], $operands[1]);\n    }\n}\n```\n\nこれ以降は、私たちの特製の条件をより短い演算子形式を使って作成することが出来ます。\n\n```php\n$query->andWhere(['ALL>', ['posts', 'comments', 'reactions', 'subscriptions'], $minValue]);\n```\n\nお気付きのことと思いますが、ここには二つの概念があります。Expression(式)と Condition(条件)です。\n[[yii\\db\\ExpressionInterface]] は、それを構築するために [[yii\\db\\ExpressionBuilderInterface]]\nを実装した式ビルダクラスを必要とするオブジェクトを特徴付けるインタフェイスです。\nまた [[yii\\db\\condition\\ConditionInterface]] は、[[yii\\db\\ExpressionInterface|ExpressionInterface]] を拡張して、\n上述されたように配列形式の定義から作成できるオブジェクトに対して使用されるべきものですが、同様にビルダを必要とするものです。\n\n要約すると、\n\n- Expression(式) – データセットのためのデータ転送オブジェクトであり、最終的に何らかの SQL 文にコンパイルされる。\n  (演算子、文字列、配列、JSON、等)\n- Condition(条件) – Expression(式) のスーパーセットで、一つの SQL 条件にコンパイルすることが可能な複数の式\n  (またはスカラ値)の集合。\n\n[[yii\\db\\ExpressionInterface|ExpressionInterface]] を実装する独自のクラスを作成して、\nデータを SQL 文に変換することの複雑さを隠蔽することが出来ます。\n[次の記事](db-active-record.md) では、式について、さらに多くの例を学習します。\n"
  },
  {
    "path": "docs/guide-ja/glossary.md",
    "content": "# A\n\n## alias エイリアス\n\nエイリアスは、クラスやディレクトリを示すために Yii によって用いられる文字列です。例えば `@app/vendor`。\n\n## application アプリケーション\n\nアプリケーションは HTTP リクエスト処理のための中心的なオブジェクトです。\n内部に抱える数多くのコンポーネントによって、リクエストから情報を取得し、更に処理を進めるためにそれを適切なコントローラに送致します。\n\nアプリケーション・オブジェクトはエントリ・スクリプトによってシングルトンとしてインスタンス化されます。\nアプリケーション・シングルトンはどの場所からでも `\\Yii::$app` としてアクセス可能です。\n\n## assets アセット\n\nアセットとはリソース・ファイルを指し示すものです。\n典型的には JavaScript または CSS を含むファイルですが、HTTP によってアクセス可能なものなら何でも構いません。\n\n## attribute 属性\n\n属性は、**ビジネス・データ** を保存するモデルのプロパティ (クラスのメンバ変数、または、`__get()`/`__set()` によって定義されるマジック・プロパティ) です。\n\n# B\n\n## bundle バンドル\n\nバンドルは、Yii 1.1 では「パッケージ」として知られていましたが、\n一群のアセットと、アセットの一覧と依存関係を記述する構成ファイルです。\n\n# C\n\n## configuration 構成\n\n構成という言葉は、オブジェクトのプロパティを設定するプロセス、または、オブジェクトやクラスのための設定を保存する構成ファイルを指し示します。\n\n# E\n\n## extension エクステンション\n\nエクステンションは、アプリケーションに機能を追加するための、一セットとなったクラス、アセット・バンドル、構成ファイルです。\n\n# I\n\n## installation インストレーション\n\nインストレーションは、説明文書に従ったり、特別に用意されたスクリプトを実行したりして、何かが動作するように準備するプロセスを指します。\nYii の場合は、パーミッションを設定すること、および、ソフトウェアの必要条件を満たすことを指します。\n\n# M\n\n## module モジュール\n\nモジュールは、それ自体にモデル、ビュー、コントローラなどの MVC 要素を含み、メインのアプリケーションの中で使用することが出来る下位アプリケーションです。\nその場合、通常、メインのアプリケーションは自分のコントローラで処理する代りに、モジュールにリクエストをフォワードします。\n\n# N\n\n## namespace 名前空間\n\n名前空間は Yii 2 で積極的に使用されている [PHP 言語機能のひとつ](https://www.php.net/manual/ja/language.namespaces.php) です。\n\n# P\n\n## package パッケージ\n\n[バンドル](#bundle) を参照。\n\n# V\n\n## vendor ベンダ\n\nベンダは、エクステンション、モジュール、ライブラリの形式でコードを提供している、組織または個人の開発者です。\n"
  },
  {
    "path": "docs/guide-ja/helper-array.md",
    "content": "配列ヘルパ\n==========\n\n[PHP の充実した配列関数](https://www.php.net/manual/ja/book.array.php) への追加として、\nYii の配列ヘルパは、配列をさらに効率的に扱うことを可能にするスタティックなメソッドを提供しています。\n\n\n## 値を取得する <span id=\"getting-values\"></span>\n\n配列、オブジェクト、またはその両方から成る複雑な構造から標準的な PHP を使って値を取得することは、非常に面倒くさい仕事です。\n最初に `isset` でキーの存在をチェックしなければならず、次に、キーが存在していれば値を取得し、存在していなければ、\nデフォルト値を提供しなければなりません。\n\n```php\nclass User\n{\n    public $name = 'Alex';\n}\n\n$array = [\n    'foo' => [\n        'bar' => new User(),\n    ]\n];\n\n$value = isset($array['foo']['bar']->name) ? $array['foo']['bar']->name : null;\n```\n\nYii はこのための非常に便利なメソッドを提供しています。\n\n```php\n$value = ArrayHelper::getValue($array, 'foo.bar.name');\n```\n\nメソッドの最初の引数は、どこから値を取得しようとしているかを指定します。\n二番目の引数は、データの取得の仕方を指定します。これは、以下の一つとすることが出来ます。\n\n- 値を読み出すべき配列のキーまたはオブジェクトのプロパティの名前。\n- ドットで分割された配列のキーまたはオブジェクトのプロパティ名のセット。上の例で使用した形式です。\n- 値を返すコールバック。\n\nコールバックは次の形式でなければなりません。\n\n```php\n$fullName = ArrayHelper::getValue($user, function ($user, $defaultValue) {\n    return $user->firstName . ' ' . $user->lastName;\n});\n```\n\n三番目のオプションの引数はデフォルト値であり、指定されない場合は `null` となります。以下のようにして使用します。\n\n```php\n$username = ArrayHelper::getValue($comment, 'user.username', 'Unknown');\n```\n\n\n## 値を設定する <span id=\"setting-values\"></span>\n\n```php\n$array = [\n    'key' => [\n        'in' => ['k' => 'value']\n    ]\n];\n\nArrayHelper::setValue($array, 'key.in', ['arr' => 'val']);\n// `$array` で値を書くためのパスは配列として指定することも出来ます\nArrayHelper::setValue($array, ['key', 'in'], ['arr' => 'val']);\n```\n\n結果として、`$array['key']['in']` の初期値は新しい値によって上書きされます。\n\n```php\n[\n    'key' => [\n        'in' => ['arr' => 'val']\n    ]\n]\n```\n\nパスが存在しないキーを含んでいる場合は、キーが作成されます。\n\n```php\n// `$array['key']['in']['arr0']` が空でなければ、値が配列に追加される\nArrayHelper::setValue($array, 'key.in.arr0.arr1', 'val');\n\n// `$array['key']['in']['arr0']` の値を完全に上書きしたい場合は\nArrayHelper::setValue($array, 'key.in.arr0', ['arr1' => 'val']);\n```\n\n結果は以下のようになります\n\n```php\n[\n    'key' => [\n        'in' => [\n            'k' => 'value',\n            'arr0' => ['arr1' => 'val']\n        ]\n    ]\n]\n```\n\n\n## 配列から値を取り除く <span id=\"removing-values\"></span>\n\n値を取得して、その直後にそれを配列から削除したい場合は、`remove` メソッドを使うことが出来ます。\n\n```php\n$array = ['type' => 'A', 'options' => [1, 2]];\n$type = ArrayHelper::remove($array, 'type');\n```\n\nこのコードを実行した後では、`$array` には `['options' => [1, 2]]` が含まれ、`$type` は `A` となります。\n`getValue` メソッドとは違って、`remove` は単純なキー名だけをサポートすることに注意してください。\n\n\n## キーの存在をチェックする <span id=\"checking-existence-of-keys\"></span>\n\n`ArrayHelper::keyExists` は、大文字と小文字を区別しないキーの比較をサポートすることを除いて、\n[array_key_exists](https://www.php.net/manual/ja/function.array-key-exists.php) と同じ動作をします。例えば、\n\n```php\n$data1 = [\n    'userName' => 'Alex',\n];\n\n$data2 = [\n    'username' => 'Carsten',\n];\n\nif (!ArrayHelper::keyExists('username', $data1, false) || !ArrayHelper::keyExists('username', $data2, false)) {\n    echo \"username を提供してください。\";\n}\n```\n\n## カラムを取得する <span id=\"retrieving-columns\"></span>\n\nデータ行またはオブジェクトの配列から、あるカラムの値を取得する必要があることがよくあります。良くある例は、ID のリストの取得です。\n\n```php\n$array = [\n    ['id' => '123', 'data' => 'abc'],\n    ['id' => '345', 'data' => 'def'],\n];\n$ids = ArrayHelper::getColumn($array, 'id');\n```\n\n結果は `['123', '345']` となります。\n\n追加の変形が要求されたり、値の取得方法が複雑であったりする場合は、\n無名関数を二番目の引数として指定することが出来ます。\n\n```php\n$result = ArrayHelper::getColumn($array, function ($element) {\n    return $element['id'];\n});\n```\n\n\n## 配列を再インデックスする <span id=\"reindexing-arrays\"></span>\n\n指定されたキーに従って配列にインデックスを付けるために、`index` メソッドを使うことが出来ます。\n入力値は、多次元配列であるか、オブジェクトの配列でなければなりません。\n`$key` は、サブ配列のキーの名前、オブジェクトのプロパティの名前、または、キーとして使用される値を返す無名関数とすることが出来ます。\n\n`$groups` 属性はキーの配列であす。これは、入力値の配列を一つまたは複数のサブ配列にグループ化するために\nキーとして使用されます。\n\n特定の要素の `$key` 属性またはその値が `null` であるとき、`$groups` が定義されていない場合は、その要素は破棄されて、結果には入りません。\nそうではなく、`$groups` が指定されている場合は、\n配列の要素はキー無しで結果の配列に追加されます。\n\n例えば、\n\n```php\n$array = [\n    ['id' => '123', 'data' => 'abc', 'device' => 'laptop'],\n    ['id' => '345', 'data' => 'def', 'device' => 'tablet'],\n    ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone'],\n];\n$result = ArrayHelper::index($array, 'id');\n```\n\n結果は、`id` 属性の値をキーとする連想配列になります。\n\n```php\n[\n    '123' => ['id' => '123', 'data' => 'abc', 'device' => 'laptop'],\n    '345' => ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone']\n    // 元の配列の2番目の要素は、同じ id であるため、最後の要素によって上書きされます\n]\n```\n\n`$key` として無名関数を渡しても同じ結果になります。\n\n```php\n$result = ArrayHelper::index($array, function ($element) {\n    return $element['id'];\n});\n```\n\n`id` を3番目の引数として渡すと、`$array` を `id` によってグループ化することが出来ます。\n\n```php\n$result = ArrayHelper::index($array, null, 'id');\n```\n\n結果は、最初のレベルが `id` でグループ化され、第2のレベルはインデックスされていない連想配列になります。\n\n```php\n[\n    '123' => [\n        ['id' => '123', 'data' => 'abc', 'device' => 'laptop']\n    ],\n    '345' => [ // このインデックスを持つ全ての要素が結果の配列に入る\n        ['id' => '345', 'data' => 'def', 'device' => 'tablet'],\n        ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone'],\n    ]\n]\n```\n\n無名関数を配列のグループ化に使うことも出来ます。\n\n```php\n$result = ArrayHelper::index($array, 'data', [function ($element) {\n    return $element['id'];\n}, 'device']);\n```\n\n結果は、最初のレベルが `id` でグループ化され、第2のレベルが `device` でグループ化され、\n第3のレベルが `data` でインデックスされた連想配列になります。\n\n```php\n[\n    '123' => [\n        'laptop' => [\n            'abc' => ['id' => '123', 'data' => 'abc', 'device' => 'laptop']\n        ]\n    ],\n    '345' => [\n        'tablet' => [\n            'def' => ['id' => '345', 'data' => 'def', 'device' => 'tablet']\n        ],\n        'smartphone' => [\n            'hgi' => ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone']\n        ]\n    ]\n]\n```\n\n## マップを作成する <span id=\"building-maps\"></span>\n\n多次元配列またはオブジェクトの配列からマップ (キー・値 のペア) を作成するためには `map` メソッドを使うことが出来ます。\n`$from` と `$to` のパラメータで、マップを構成するキー名またはプロパティ名を指定します。\nオプションで、グループ化のためのフィールド `$group` に従って、マップをグループ化することも出来ます。例えば、\n\n```php\n$array = [\n    ['id' => '123', 'name' => 'aaa', 'class' => 'x'],\n    ['id' => '124', 'name' => 'bbb', 'class' => 'x'],\n    ['id' => '345', 'name' => 'ccc', 'class' => 'y'],\n];\n\n$result = ArrayHelper::map($array, 'id', 'name');\n// 結果は次のようになります\n// [\n//     '123' => 'aaa',\n//     '124' => 'bbb',\n//     '345' => 'ccc',\n// ]\n\n$result = ArrayHelper::map($array, 'id', 'name', 'class');\n// 結果は次のようになります\n// [\n//     'x' => [\n//         '123' => 'aaa',\n//         '124' => 'bbb',\n//     ],\n//     'y' => [\n//         '345' => 'ccc',\n//     ],\n// ]\n```\n\n\n## 多次元配列の並べ替え <span id=\"multidimensional-sorting\"></span>\n\n`multisort` メソッドは、オブジェクトの配列または入れ子にされた配列を、一つまたは複数のキーによって並べ替えることを手助けします。例えば、\n\n```php\n$data = [\n    ['age' => 30, 'name' => 'Alexander'],\n    ['age' => 30, 'name' => 'Brian'],\n    ['age' => 19, 'name' => 'Barney'],\n];\nArrayHelper::multisort($data, ['age', 'name'], [SORT_ASC, SORT_DESC]);\n```\n\n並べ替えの後には、`$data` に次のデータが入っています。\n\n```php\n[\n    ['age' => 19, 'name' => 'Barney'],\n    ['age' => 30, 'name' => 'Brian'],\n    ['age' => 30, 'name' => 'Alexander'],\n];\n```\n\n並べ替えで参照するキーを指定する二番目の引数は、一つのキーであれば文字列、複数のキーであれば配列を取ることが出来ます。\nさらに、次のような無名関数でも構いません。\n\n```php\nArrayHelper::multisort($data, function($item) {\n    // 存在していれば 'age' で、さもなくば 'name' でソート\n    return isset($item['age']) ? ['age', 'name'] : 'name';\n});\n```\n\n三番目の引数は並べ替えの順序です。\n一つのキーによる並べ替えの場合は、`SORT_ASC` か `SORT_DESC` のいずれかです。\n複数の値による並べ替えの場合は、並べ替えの順序の配列を渡して、値ごとに違う順序で並べ替えることが出来ます。\n\n最後の引数は並べ替えのフラグで、\nPHP の [sort()](https://www.php.net/manual/ja/function.sort.php) 関数に渡されるのと同じ値を取ることが出来ます。\n\n\n## 配列の型を検出する <span id=\"detecting-array-types\"></span>\n\n配列が添字配列であるか連想配列であるかを知ることが出来ると便利です。例を挙げましょう。\n\n```php\n// キーは指定されていない\n$indexed = ['Qiang', 'Paul'];\necho ArrayHelper::isIndexed($indexed);\n\n// 全てのキーは文字列\n$associative = ['framework' => 'Yii', 'version' => '2.0'];\necho ArrayHelper::isAssociative($associative);\n```\n\n\n## 値を HTML エンコード / デコードする <span id=\"html-encoding-values\"></span>\n\n文字列の配列の中にある特殊文字を HTML エンティティにエンコード、または、HTML エンティティからデコードするために、下記の関数を使うことが出来ます。\n\n```php\n$encoded = ArrayHelper::htmlEncode($data);\n$decoded = ArrayHelper::htmlDecode($data);\n```\n\nデフォルトでは、値だけがエンコードされます。二番目の引数を `false` として渡すことによって、配列のキーもエンコードすることが出来ます。\nエンコードにはアプリケーションの文字セットが使用されますが、三番目の引数によってそれを変更することも出来ます。\n\n\n## 配列をマージする <span id=\"merging-arrays\"></span>\n\n[[yii\\helpers\\ArrayHelper::merge()|ArrayHelper::merge()]] を使って、二つまたはそれ以上の配列を再帰的に一つの配列にマージすることが出来ます。\n各配列に同じ文字列のキー値を持つ要素がある場合は、\n([array_merge_recursive()](https://www.php.net/manual/ja/function.array-merge-recursive.php) とは違って)後のものが前のものを上書きします。\n両方の配列が、同じキーを持つ配列型の要素を持っている場合は、再帰的なマージが実行されます。\n添字型の要素については、後の配列の要素が前の配列の要素の後に追加されます。\n[[yii\\helpers\\UnsetArrayValue]] オブジェクトを使って前の配列にある値を非設定に指定したり、\n[[yii\\helpers\\ReplaceArrayValue]] オブジェクトを使って再帰的なマージでなく前の値の上書きを強制したりすることが出来ます。\n\n例えば、\n\n```php\n$array1 = [\n    'name' => 'Yii',\n    'version' => '1.1',\n    'ids' => [\n        1,\n    ],\n    'validDomains' => [\n        'example.com',\n        'www.example.com',\n    ],\n    'emails' => [\n        'admin' => 'admin@example.com',\n        'dev' => 'dev@example.com',\n    ],\n];\n\n$array2 = [\n    'version' => '2.0',\n    'ids' => [\n        2,\n    ],\n    'validDomains' => new \\yii\\helpers\\ReplaceArrayValue([\n        'yiiframework.com',\n        'www.yiiframework.com',\n    ]),\n    'emails' => [\n        'dev' => new \\yii\\helpers\\UnsetArrayValue(),\n    ],\n];\n\n$result = ArrayHelper::merge($array1, $array2);\n```\n\n結果は次のようになります。\n\n```php\n[\n    'name' => 'Yii',\n    'version' => '2.0',\n    'ids' => [\n        1,\n        2,\n    ],\n    'validDomains' => [\n        'yiiframework.com',\n        'www.yiiframework.com',\n    ],\n    'emails' => [\n        'admin' => 'admin@example.com',\n    ],\n]\n```\n\n\n## オブジェクトを配列に変換する <span id=\"converting-objects-to-arrays\"></span>\n\nオブジェクトまたはオブジェクトの配列を配列に変換する必要があることがよくあります。\n最もよくあるのは、REST API によってデータ配列を提供するなどの目的で、アクティブ・レコード・モデルを変換する場合です。そうするために、次のコードを使うことが出来ます。\n\n```php\n$posts = Post::find()->limit(10)->all();\n$data = ArrayHelper::toArray($posts, [\n    'app\\models\\Post' => [\n        'id',\n        'title',\n        // 結果配列のキー名 => プロパティの値\n        'createTime' => 'created_at',\n        // 結果配列のキー名 => 無名関数が返す値\n        'length' => function ($post) {\n            return strlen($post->content);\n        },\n    ],\n]);\n```\n\n最初の引数が変換したいデータです。この例では、`Post` AR モデルを変換しようとしています。\n\n二番目の引数は、クラスごとの変換マップです。ここでは、`Post` モデルの変換マップを設定しています。\n変換マップの配列が、一連のマップを含んでいます。各マップは以下のいずれかの形式を取ります。\n\n- フィールド名 - そのままインクルードされる。\n- キー/値 のペア - 配列のキー名にしたい文字列と、値を取得すべきモデルのカラムの名前。\n- キー/値 のペア - 配列のキー名にしたい文字列と、値を返すコールバック。\n\n単一のモデルに対する上記の変換の結果は以下のようになります。\n\n\n```php\n[\n    'id' => 123,\n    'title' => 'test',\n    'createTime' => '2013-01-01 12:00AM',\n    'length' => 301,\n]\n```\n\n特定のクラスについて、配列に変換するデフォルトの方法を提供するためには、\nそのクラスの [[yii\\base\\Arrayable|Arrayable]] インタフェイスを実装することが出来ます。\n\n## 配列の中にあるかどうか調べる <span id=\"testing-arrays\"></span>\n\nある要素が配列の中に存在するかどうか、また、一連の要素が配列のサブセットであるかどうか、ということを調べる必要がある場合がよくあります。\nPHP は `in_array()` を提供していますが、これはサブセットや `\\Traversable` なオブジェクトをサポートしていません。\n\nこの種のチェックを助けるために、[[yii\\helpers\\ArrayHelper]] は [[yii\\helpers\\ArrayHelper::isIn()|isIn()]]\nおよび [[yii\\helpers\\ArrayHelper::isSubset()|isSubset()]] を\n[in_array()](https://www.php.net/manual/ja/function.in-array.php) と同じシグニチャで提供しています。\n\n```php\n// true\nArrayHelper::isIn('a', ['a']);\n// true\nArrayHelper::isIn('a', new ArrayObject(['a']));\n\n// true\nArrayHelper::isSubset(new ArrayObject(['a', 'c']), new ArrayObject(['a', 'b', 'c']));\n```\n"
  },
  {
    "path": "docs/guide-ja/helper-html.md",
    "content": "Html ヘルパ\n===========\n\n全てのウェブ・アプリケーションは大量の HTML マークアップを生成します。\nマークアップが静的な場合は、[PHP と HTML を一つのファイルに混ぜる](https://www.php.net/manual/ja/language.basic-syntax.phpmode.php) ことによって効率よく生成することが可能ですが、マークアップを動的にするとなると、何らかの助けが無ければ、処理がトリッキーになってきます。\nYii はそのような手助けを Html ヘルパの形式で提供します。\nこれは、よく使われる HTML タグとそのオプションやコンテントを処理するための一連のスタティック・メソッドを提供するものです。\n\n> Note: あなたのマークアップがおおむね静的なものである場合は、HTML を直接に使用する方が適切です。\n> 何でもかんでも Html ヘルパの呼び出しでラップする必要はありません。\n\n\n## 基礎 <span id=\"basics\"></span>\n\n動的な HTML を文字列の連結によって構築していると、あっという間に乱雑なコードになります。\nそのため、Yii はタグのオプションを操作し、それらのオプションに基づいてタグを構築する一連のメソッドを提供します。\n\n\n### タグを生成する <span id=\"generating-tags\"></span>\n\nタグを生成するコードは次のようなものです。\n\n```php\n<?= Html::tag('p', Html::encode($user->name), ['class' => 'username']) ?>\n```\n\n最初の引数はタグの名前です。二番目の引数は、開始タグと終了タグの間に囲まれることになるコンテントです。\n`Html::encode` を使っていることに注目してください。\nこれは、必要な場合には HTML を使うことが出来るように、コンテントが自動的にはエンコードされないからです。\n三番目の引数は HTML のオプション、言い換えると、タグの属性です。この配列で、キーは `class`、`href`、`target` などの属性の名前であり、値は属性の値です。\n\n上記のコードは次の HTML を生成します。\n\n```html\n<p class=\"username\">samdark</p>\n```\n\n開始タグまたは終了タグだけが必要な場合は、`Html::beginTag()` または `Html::endTag()` のメソッドを使うことが出来ます。\n\nオプションは多くの Html ヘルパのメソッドとさまざまなウィジェットで使用されます。\nその全ての場合において、いくつか追加の処理がなされることを知っておいてください。\n\n- 値が `null` である場合は、対応する属性はレンダリングされません。\n- 値が真偽値である属性は、[真偽値属性 (boolean attributes)](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#boolean-attributes) \n  として扱われます。\n- 属性の値は [[yii\\helpers\\Html::encode()|Html::encode()]] を使って HTML エンコードされます。\n- 属性の値が配列である場合は、次のように処理されます。\n \n   * 属性が [[yii\\helpers\\Html::$dataAttributes]] にリストされているデータ属性である場合、例えば `data` や `ng` である場合は、\n    値の配列にある要素の一つ一つについて、属性のリストがレンダリングされます。\n     例えば、`'data' => ['id' => 1, 'name' => 'yii']` は `data-id=\"1\" data-name=\"yii\"` を生成します。\n     また、`'data' => ['params' => ['id' => 1, 'name' => 'yii'], 'status' => 'ok']` は \n     `data-params='{\"id\":1,\"name\":\"yii\"}' data-status=\"ok\"` を生成します。\n     後者の例において、下位の配列に対して JSON 形式が使用されていることに注意してください。\n   * 属性がデータ属性でない場合は、値は JSON エンコードされます。\n     例えば、`['params' => ['id' => 1, 'name' => 'yii']` は `params='{\"id\":1,\"name\":\"yii\"}'` を生成します。\n\n\n### CSS のクラスとスタイルを形成する <span id=\"forming-css\"></span>\n\nHTML タグのオプションを構築する場合、たいていは、デフォルトの値から始めて必要な修正をする、という方法をとります。\nCSS クラスを追加または削除するために、次のコードを使用することが出来ます。\n\n```php\n$options = ['class' => 'btn btn-default'];\n\nif ($type === 'success') {\n    Html::removeCssClass($options, 'btn-default');\n    Html::addCssClass($options, 'btn-success');\n}\n\necho Html::tag('div', 'Pwede na', $options);\n\n// $type が 'success' の場合、次のようにレンダリングされる\n// <div class=\"btn btn-success\">Pwede na</div>\n```\n\n配列形式を使って複数の CSS クラスを指定することも出来ます。\n\n```php\n$options = ['class' => ['btn', 'btn-default']];\n\necho Html::tag('div', 'Save', $options);\n// '<div class=\"btn btn-default\">Save</div>' をレンダリングする\n```\n\nクラスを追加・削除する際にも配列形式を使うことが出来ます。\n\n```php\n$options = ['class' => 'btn'];\n\nif ($type === 'success') {\n    Html::addCssClass($options, ['btn-success', 'btn-lg']);\n}\n\necho Html::tag('div', 'Save', $options);\n// '<div class=\"btn btn-success btn-lg\">Save</div>' をレンダリングする\n```\n\n`Html::addCssClass()` はクラスの重複を防止しますので、同じクラスが二度出現するかも知れないと心配する必要はありません。\n\n```php\n$options = ['class' => 'btn btn-default'];\n\nHtml::addCssClass($options, 'btn-default'); // クラス 'btn-default' は既に存在する\n\necho Html::tag('div', 'Save', $options);\n// '<div class=\"btn btn-default\">Save</div>' をレンダリングする\n```\n\nCSS のクラスオプションを配列形式で指定する場合には、名前付きのキーを使ってクラスの論理的な目的を示すことが出来ます。\nこの場合、`Html::addCssClass()` で同じキーを持つクラスを指定しても無視されます。\n\n```php\n$options = [\n    'class' => [\n        'btn',\n        'theme' => 'btn-default',\n    ]\n];\n\nHtml::addCssClass($options, ['theme' => 'btn-success']); // 'theme' キーは既に使用されている\n\necho Html::tag('div', 'Save', $options);\n// '<div class=\"btn btn-default\">Save</div>' をレンダリングする\n```\n\nCSS のスタイルも `style` 属性を使って、同じように設定することが出来ます。\n\n```php\n$options = ['style' => ['width' => '100px', 'height' => '100px']];\n\n// style=\"width: 100px; height: 200px; position: absolute;\" となる\nHtml::addCssStyle($options, 'height: 200px; position: absolute;');\n\n// style=\"position: absolute;\" となる\nHtml::removeCssStyle($options, ['width', 'height']);\n```\n\n[[yii\\helpers\\Html::addCssStyle()|addCssStyle()]] を使うときには、CSS プロパティの名前と値に対応する「キー-値」ペアの配列か、\nまたは、`width: 100px; height: 200px;` のような文字列を指定することが出来ます。\nこの二つの形式は、[[yii\\helpers\\Html::cssStyleFromArray()|cssStyleFromArray()]] と [[yii\\helpers\\Html::cssStyleToArray()|cssStyleToArray()]] を使って、双方向に変換することが出来ます。\n[[yii\\helpers\\Html::removeCssStyle()|removeCssStyle()]] メソッドは、削除すべきプロパティの配列を受け取ります。\nプロパティが一つだけである場合は、文字列で指定することも出来ます。\n\n\n### コンテントをエンコードおよびデコードする <span id=\"encoding-and-decoding-content\"></span>\n\nコンテントが適切かつ安全に HTML として表示されるためには、コンテント内の特殊文字がエンコードされなければなりません。\n特殊文字のエンコードとデコードは、PHP では [htmlspecialchars](https://www.php.net/manual/ja/function.htmlspecialchars.php) と\n[htmlspecialchars_decode](https://www.php.net/manual/ja/function.htmlspecialchars-decode.php) によって行われます。\nこれらのメソッドを直接使用する場合の問題は、文字エンコーディングと追加のフラグを毎回指定しなければならないことです。\nフラグは毎回同じものであり、文字エンコーディングはセキュリティ問題を防止するためにアプリケーションのそれと一致すべきものですから、\nYii は二つのコンパクトかつ使いやすいメソッドを用意しました。\n\n```php\n$userName = Html::encode($user->name);\necho $userName;\n\n$decodedUserName = Html::decode($userName);\n```\n\n\n## フォーム <span id=\"forms\"></span>\n\nフォームのマークアップを扱う仕事は、極めて面倒くさく、エラーを生じがちなものです。\nこのため、フォームのマークアップの仕事を助けるための一群のメソッドがあります。\n\n> Note: モデルを扱っており、検証が必要である場合は、[[yii\\widgets\\ActiveForm|ActiveForm]] を使うことを検討してください。\n\n\n### フォームを作成する <span id=\"creating-forms\"></span>\n\nフォームを開始するためには、次のように [[yii\\helpers\\Html::beginForm()|beginForm()]] メソッドを使うことが出来ます。\n\n```php\n<?= Html::beginForm(['order/update', 'id' => $id], 'post', ['enctype' => 'multipart/form-data']) ?>\n```\n\n最初の引数は、フォームが送信されることになる URL です。これは [[yii\\helpers\\Url::to()|Url::to()]] によって受け入れられる Yii のルートおよびパラメータの形式で指定することが出来ます。\n第二の引数は使われるメソッドです。`post` がデフォルトです。第三の引数はフォームタグのオプションの配列です。\n上記の場合では、POST リクエストにおけるフォーム・データのエンコーディング方法を `multipart/form-data` に変更しています。\nこれはファイルをアップロードするために必要とされます。\n\nフォーム・タグを閉じるのは簡単です。\n\n```php\n<?= Html::endForm() ?>\n```\n\n\n### ボタン <span id=\"buttons\"></span>\n\nボタンを生成するためには、次のコードを使うことが出来ます。\n\n```php\n<?= Html::button('押してね !', ['class' => 'teaser']) ?>\n<?= Html::submitButton('送信', ['class' => 'submit']) ?>\n<?= Html::resetButton('リセット', ['class' => 'reset']) ?>\n```\n\n最初の引数は、三つのメソッドのどれでも、ボタンのタイトルであり、第二の引数はオプションです。\nタイトルはエンコードされませんので、エンド・ユーザからデータを取得する場合は [[yii\\helpers\\Html::encode()|Html::encode()]] を使ってエンコードしてください。\n\n\n### インプット・フィールド <span id=\"input-fields\"></span>\n\nインプットのメソッドには二つのグループがあります。\n一つは `active` から始まるものでアクティブ・インプットと呼ばれます。もう一方は `active` から始まらないものです。\nアクティブ・インプットは、データを指定されたモデルと属性から取得しますが、通常のインプットでは、データは直接に指定されます。\n\n最も汎用的なメソッドは以下のものです。\n\n```php\nタイプ、インプットの名前、値、オプション\n<?= Html::input('text', 'username', $user->name, ['class' => $username]) ?>\n\nタイプ、モデル、モデルの属性名、オプション\n<?= Html::activeInput('text', $user, 'name', ['class' => $username]) ?>\n```\n\nインプットのタイプが前もって判っている場合は、ショートカットメソッドを使う方が便利です。\n\n- [[yii\\helpers\\Html::buttonInput()]]\n- [[yii\\helpers\\Html::submitInput()]]\n- [[yii\\helpers\\Html::resetInput()]]\n- [[yii\\helpers\\Html::textInput()]], [[yii\\helpers\\Html::activeTextInput()]]\n- [[yii\\helpers\\Html::hiddenInput()]], [[yii\\helpers\\Html::activeHiddenInput()]]\n- [[yii\\helpers\\Html::passwordInput()]] / [[yii\\helpers\\Html::activePasswordInput()]]\n- [[yii\\helpers\\Html::fileInput()]], [[yii\\helpers\\Html::activeFileInput()]]\n- [[yii\\helpers\\Html::textarea()]], [[yii\\helpers\\Html::activeTextarea()]]\n\nラジオとチェックボックスは、メソッドのシグニチャの面で少し異なっています。\n\n```php\n<?= Html::radio('agree', true, ['label' => '同意します']) ?>\n<?= Html::activeRadio($model, 'agree', ['class' => 'agreement']) ?>\n\n<?= Html::checkbox('agree', true, ['label' => '同意します']) ?>\n<?= Html::activeCheckbox($model, 'agree', ['class' => 'agreement']) ?>\n```\n\nドロップダウン・リストとリスト・ボックスは、次のようにしてレンダリングすることが出来ます。\n\n```php\n<?= Html::dropDownList('list', $currentUserId, ArrayHelper::map($userModels, 'id', 'name')) ?>\n<?= Html::activeDropDownList($users, 'id', ArrayHelper::map($userModels, 'id', 'name')) ?>\n\n<?= Html::listBox('list', $currentUserId, ArrayHelper::map($userModels, 'id', 'name')) ?>\n<?= Html::activeListBox($users, 'id', ArrayHelper::map($userModels, 'id', 'name')) ?>\n```\n\n最初の引数はインプットの名前、第二の引数は現在選択されている値です。そして第三の引数は「キー-値」のペアであり、配列のキーはリストの値、配列の値はリストのラベルです。\n\n複数の選択肢を選択できるようにしたい場合は、チェックボックス・リストが最適です。\n\n```php\n<?= Html::checkboxList('roles', [16, 42], ArrayHelper::map($roleModels, 'id', 'name')) ?>\n<?= Html::activeCheckboxList($user, 'role', ArrayHelper::map($roleModels, 'id', 'name')) ?>\n```\n\nそうでない場合は、ラジオ・リストを使います。\n\n```php\n<?= Html::radioList('roles', [16, 42], ArrayHelper::map($roleModels, 'id', 'name')) ?>\n<?= Html::activeRadioList($user, 'role', ArrayHelper::map($roleModels, 'id', 'name')) ?>\n```\n\n\n### ラベルとエラー <span id=\"labels-and-errors\"></span>\n\nインプットと同じように、ラベルを生成するメソッドが二つあります。モデルからデータを取るアクティブなラベルと、データを直接受け入れるアクティブでないラベルです。\n\n```php\n<?= Html::label('ユーザ名', 'username', ['class' => 'label username']) ?>\n<?= Html::activeLabel($user, 'username', ['class' => 'label username']) ?>\n```\n\n一つまたは複数のモデルから取得したエラーを要約として表示するためには、次のコードを使うことが出来ます。\n\n```php\n<?= Html::errorSummary($posts, ['class' => 'errors']) ?>\n```\n\n個別のエラーを表示するためには、次のようにします。\n\n```php\n<?= Html::error($post, 'title', ['class' => 'error']) ?>\n```\n\n\n### インプットの名前と値 <span id=\"input-names-and-values\"></span>\n\nモデルに基づいてインプット・フィールドの名前、ID、値を取得するメソッドがあります。\nこれらは主として内部的に使用されるものですが、場合によっては重宝します。\n\n```php\n// Post[title]\necho Html::getInputName($post, 'title');\n\n// post-title\necho Html::getInputId($post, 'title');\n\n// '私の最初の投稿'\necho Html::getAttributeValue($post, 'title');\n\n// $post->authors[0]\necho Html::getAttributeValue($post, '[0]authors[0]');\n```\n\n上記において、最初の引数はモデルであり、第二の引数は属性を示す式です。これは最も単純な形式においては属性名ですが、配列の添字を前 および/または 後に付けた属性名とすることも出来ます。配列の添字は主として表形式データ入力のために使用されます。\n\n- `[0]content` は、表形式データ入力で使われます。表形式入力の最初のモデルの \"content\" 属性を表します。\n- `dates[0]` は、\"dates\" 属性の最初の配列要素を表します。\n- `[0]dates[0]` は、表形式入力の最初のモデルの \"dates\" 属性の最初の配列要素を表します。\n\n前後の添字なしに属性名を取得するためには、次のコードを使うことが出来ます。\n\n```php\n// dates\necho Html::getAttributeName('dates[0]');\n```\n\n\n## スタイルとスクリプト <span id=\"styles-and-scripts\"></span>\n\n埋め込みのスタイルとスクリプトをラップするタグを生成するメソッドが二つあります。\n\n```php\n<?= Html::style('.danger { color: #f00; }', ['media' => 'print']) ?>\n\nこれは次の HTML を生成します。\n\n<style media=\"print\">.danger { color: #f00; }</style>\n\n\n<?= Html::script('alert(\"こんにちは!\");') ?>\n\nこれは次の HTML を生成します。\n\n<script>alert(\"こんにちは!\");</script>\n```\n\nCSS ファイルの外部スタイルをリンクしたい場合は、次のようにします。\n\n```php\n<?= Html::cssFile('@web/css/ie5.css', ['condition' => 'IE 5']) ?>\n\nこれは次の HTML を生成します。\n\n<!--[if IE 5]>\n    <link href=\"https://example.com/css/ie5.css\" />\n<![endif]-->\n```\n\n最初の引数は URL であり、第二の引数はオプションの配列です。通常のオプションに加えて、次のものを指定することが出来ます。\n\n- `condition` - 指定された条件を使って `<link` を条件付きコメントで囲みます。\n  条件付きコメントなんて、使う必要が無くなっちゃえば良いのにね ;)\n- `noscript` - `true` に設定すると `<link` を `<noscript>` タグで囲むことができます。\n  この場合、JavaScript がブラウザでサポートされていないか、ユーザが JavaScript を無効にしたときだけ、CSS がインクルードされます。\n\nJavaScript ファイルをリンクするためには、次のようにします。\n\n```php\n<?= Html::jsFile('@web/js/main.js') ?>\n```\n\nCSS と同じように、最初の引数はインクルードされるファイルへのリンクを指定するものです。オプションを第二の引数として渡すことが出来ます。\nオプションに置いて、`cssFile` のオプションと同じように、`condition` を指定することが出来ます。\n\n\n## ハイパーリンク <span id=\"hyperlinks\"></span>\n\nハイパーリンクを手軽に生成できるメソッドがあります。\n\n```php\n<?= Html::a('プロファイル', ['user/view', 'id' => $id], ['class' => 'profile-link']) ?>\n```\n\n最初の引数はタイトルです。これはエンコードされませんので、エンド・ユーザから取得したデータを使う場合は、`Html::encode()` でエンコードする必要があります。\n第二の引数が、`<a` タグの `href` に入ることになるものです。\nどのような値が受け入れられるかについて、詳細は [Url::to()](helper-url.md) を参照してください。\n第三の引数は、タグのプロパティの配列です。\n\n`mailto` リンクを生成する必要があるときは、次のコードを使うことが出来ます。\n\n```php\n<?= Html::mailto('連絡先', 'admin@example.com') ?>\n```\n\n\n## 画像 <span id=\"images\"></span>\n\nイメージタグを生成するためには次のようにします。\n\n```php\n<?= Html::img('@web/images/logo.png', ['alt' => '私のロゴ']) ?>\n\nこれは次の HTML を生成します。\n\n<img src=\"https://example.com/images/logo.png\" alt=\"私のロゴ\" />\n```\n\n最初の引数は、[エイリアス](concept-aliases.md) 以外にも、ルートとパラメータ、または URL を受け入れることが出来ます。[Url::to()](helper-url.md) と同様です。\n\n\n## リスト <span id=\"lists\"></span>\n\n順序なしリストは、次のようにして生成することが出来ます。\n\n```php\n<?= Html::ul($posts, ['item' => function($item, $index) {\n    return Html::tag(\n        'li',\n        $this->render('post', ['item' => $item]),\n        ['class' => 'post']\n    );\n}]) ?>\n```\n\n順序付きリストを生成するためには、代りに `Html:ol()` を使ってください。\n"
  },
  {
    "path": "docs/guide-ja/helper-json.md",
    "content": "Json ヘルパ\n===========\n\nJson ヘルパは JSON をエンコードおよびデコードする一連の静的メソッドを提供します。\n`[[yii\\helpers\\Json::encode()]]` メソッドはエンコード・エラーを処理しますが、\n `[[yii\\web\\JsExpression]]` オブジェクトの形式で表現された JavaScript の式はエンコードしません。\n既定ではエンコードは `JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE` のオプションで行われます。\n詳細については [PHP:json_encode](https://www.php.net/manual/ja/function.json-encode.php) を参照して下さい。\n\n## 整形出力 <span id=\"pretty-print\"></span>\n\n既定では `[[yii\\helpers\\Json::encode()]]` メソッドは整形されていない JSON (すなわち空白無しのもの) を出力します。\n人間にとって読みやすいものにするために、「整形出力 pretty printing」を ON にすることが出来ます。\n\n> Note: 整形出力は開発中のデバッグには役立つでしょうが、製品環境では推奨されません。\nインスタンスごとに整形出力を有効にするためにはオプションを指定することが出来ます。すなわち :\n\n```php\n$data = ['a' => 1, 'b' => 2];\n$json = yii\\helpers\\Json::encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);\n```\nJSON ヘルパの整形出力をグローバルに有効にすることも出来ます。例えば、設定ファイルや index.php の中で :\n```php\nyii\\helpers\\Json::$prettyPrint = YII_DEBUG; // デバッグ・モードでは整形出力を使用\n```\n"
  },
  {
    "path": "docs/guide-ja/helper-overview.md",
    "content": "ヘルパ\n======\n\n> Note: このセクションはまだ執筆中です。\n\nYii は、一般的なコーディングのタスク、例えば、文字列や配列の操作、HTML コードの生成などを手助けする多くのクラスを提供しています。\nこれらのヘルパ・クラスは `yii\\helpers` 名前空間の下に編成されており、すべてスタティックなクラス\n(すなわち、スタティックなプロパティとメソッドのみを含み、インスタンス化すべきでないクラス) です。\n\nヘルパ・クラスは、そのスタティックなメソッドの一つを直接に呼び出すことによって使用します。例えば、\n\n```php\nuse yii\\helpers\\Html;\n\necho Html::encode('Test > test');\n```\n\n> Note: [ヘルパ・クラスをカスタマイズする](#customizing-helper-classes) ことをサポートするために、Yii はコア・ヘルパ・クラスのすべてを二つのクラスに分割しています。\n> すなわち、基底クラス (例えば `BaseArrayHelper`) と具象クラス (例えば `ArrayHelper`) です。\n> ヘルパを使うときは、具象クラスのみを使うべきであり、基底クラスは決して使ってはいけません。\n\n\nコア・ヘルパ・クラス\n--------------------\n\n以下のコア・ヘルパ・クラスが Yii のリリースにおいて提供されています。\n\n- [配列ヘルパ](helper-array.md)\n- Console\n- FileHelper\n- FormatConverter\n- [Html ヘルパ](helper-html.md)\n- HtmlPurifier\n- Imagine (yii2-imagine エクステンションによって提供)\n- Inflector\n- [Json](helper-json.md)\n- Markdown\n- StringHelper\n- [Url ヘルパ](helper-url.md)\n- VarDumper\n\n\nヘルパ・クラスをカスタマイズする <span id=\"customizing-helper-classes\"></span>\n------------------------------\n\nコア・ヘルパ・クラス (例えば [[yii\\helpers\\ArrayHelper]]) をカスタマイズするためには、\nそのヘルパに対応する基底クラス (例えば [[yii\\helpers\\BaseArrayHelper]]) を拡張するクラスを作成して、名前空間も含めて、\n対応する具象クラス (例えば [[yii\\helpers\\ArrayHelper]]) と同じ名前を付けます。\nこのクラスが、フレームワークのオリジナルの実装を置き換えるものとしてセットアップされます。\n\n次の例は、[[yii\\helpers\\ArrayHelper]] クラスの [[yii\\helpers\\ArrayHelper::merge()|merge()]]\nメソッドをカスタマイズする方法を示すものです。\n\n```php\n<?php\n\nnamespace yii\\helpers;\n\nclass ArrayHelper extends BaseArrayHelper\n{\n    public static function merge($a, $b)\n    {\n        // あなた独自の実装\n    }\n}\n```\n\nあなたのクラスを `ArrayHelper.php` という名前のファイルに保存します。このファイルはどこに置いても構いません。例えば、`@app/components` に置くことにしましょう。\n\n次に、アプリケーションの [エントリ・スクリプト](structure-entry-scripts.md) で、\n次のコード行を `yii.php` ファイルをインクルードする行の後に追加して、[Yii クラス・オートローダ](concept-autoloading.md) に、\nフレームワークから本来のヘルパ・クラスをロードする代りに、あなたのカスタム・クラスをロードすべきことを教えます。\n\n```php\nYii::$classMap['yii\\helpers\\ArrayHelper'] = '@app/components/ArrayHelper.php';\n```\n\nヘルパ・クラスのカスタマイズは、ヘルパの既存の関数の振る舞いを変更したい場合にだけ役立つものであることに注意してください。\nアプリケーションの中で使用する関数を追加したい場合は、\nそのための独立したヘルパを作成する方が良いでしょう。\n"
  },
  {
    "path": "docs/guide-ja/helper-url.md",
    "content": "Url ヘルパ\n==========\n\nUrl ヘルパは URL を管理するための一連のスタティック・メソッドを提供します。\n\n\n## よく使う URL を取得する <span id=\"getting-common-urls\"></span>\n\nよく使う URL を取得するために使うことが出来るメソッドが二つあります。すなわち、ホーム URL と、現在のリクエストのベース URL を取得するメソッドです。\nホーム URL を取得するためには、次のようにします。\n\n```php\n$relativeHomeUrl = Url::home();\n$absoluteHomeUrl = Url::home(true);\n$httpsAbsoluteHomeUrl = Url::home('https');\n```\n\nパラメータが渡されない場合は、生成される URL は相対 URL になります。\nパラメータとして `true` を渡せば、現在のスキーマの絶対 URL を取得することが出来ます。または、スキーマ (`http`, `https`) を明示的に指定しても構いません。\n\n現在のリクエストのベース URL を取得するためには、次のようにします。\n \n```php\n$relativeBaseUrl = Url::base();\n$absoluteBaseUrl = Url::base(true);\n$httpsAbsoluteBaseUrl = Url::base('https');\n```\n\nこのメソッドの唯一のパラメータは、`Url::home()` の場合と全く同じ動作をします。\n\n\n## URL を生成する <span id=\"creating-urls\"></span>\n\n与えられたルートへの URL を生成するためには、`Url::toRoute()` メソッドを使います。\nこのメソッドは、[[\\yii\\web\\UrlManager]] を使って URL を生成します。\n\n```php\n$url = Url::toRoute(['product/view', 'id' => 42]);\n```\n\nルートは、文字列として指定することが出来ます (例えば、`site/index`)。\nまたは、生成される URL に追加のクエリ・パラメータを指定したい場合は、配列を使うことも出来ます。配列の形式は、以下のようにしなければなりません。\n\n```php\n// /index.php?r=site%2Findex&param1=value1&param2=value2 を生成\n['site/index', 'param1' => 'value1', 'param2' => 'value2']\n```\n\nアンカーの付いた URL を生成したい場合は、`#` パラメータを持つ配列を使うことが出来ます。例えば、\n\n```php\n// /index.php?r=site%2Findex&param1=value1#name を生成\n['site/index', 'param1' => 'value1', '#' => 'name']\n```\n\nルートは、絶対ルートか相対ルートかのどちらかです。絶対ルートは先頭にスラッシュを持ち (例えば `/site/index`)、相対ルートは持ちません (例えば `site/index` または `index`)。\n相対ルートは次の規則に従って絶対ルートに変換されます。\n\n- ルートが空文字列である場合は、現在の [[yii\\web\\Controller::route|ルート]] が使用されます。\n- ルートがスラッシュを全く含まない場合は (例えば `index`)、カレント・コントローラのアクション ID であると見なされて、\n  カレント・コントローラの [[\\yii\\web\\Controller::uniqueId|uniqueId]] が前置されます。\n- ルートが先頭にスラッシュを含まない場合は (例えば `site/index`)、カレント・モジュールに対する相対ルートと見なされて、\n  カレント・モジュールの [[\\yii\\base\\Module::uniqueId|uniqueId]] が前置されます。\n\nバージョン 2.0.2 以降では、[エイリアス](concept-aliases.md) の形式でルートを指定することが出来ます。\nその場合は、エイリアスが最初に実際のルートに変換され、\nそのルートが上記の規則に従って絶対ルートに変換されます。\n\n以下に、このメソッドの使用例をいくつか挙げます。\n\n```php\n// /index.php?r=site%2Findex\necho Url::toRoute('site/index');\n\n// /index.php?r=site%2Findex&src=ref1#name\necho Url::toRoute(['site/index', 'src' => 'ref1', '#' => 'name']);\n\n// /index.php?r=post%2Fedit&id=100     エイリアス \"@postEdit\" は \"post/edit\" と定義されていると仮定\necho Url::toRoute(['@postEdit', 'id' => 100]);\n\n// https://www.example.com/index.php?r=site%2Findex\necho Url::toRoute('site/index', true);\n\n// https://www.example.com/index.php?r=site%2Findex\necho Url::toRoute('site/index', 'https');\n```\n\nもうひとつ、[[toRoute()]] と非常によく似た `Url::to()` というメソッドがあります。\n唯一の違いは、このメソッドはルートを配列として指定することを要求する、という点です。文字列が与えられた場合は、URL として扱われます。\n\n最初の引数は、次のいずれかを取り得ます。\n\n- 配列: URL を生成するために [[toRoute()]] が呼び出されます。\n  例えば、`['site/index']`、`['post/index', 'page' => 2]`。\n  ルートの指定方法の詳細については [[toRoute()]] を参照してください。\n- `@` で始まる文字列: これはエイリアスとして扱われ、\n  エイリアスに対応する文字列が返されます。\n- 空文字列: 現在リクエストされている URL が返されます。\n- 通常の文字列: その通りのものとして扱われます。\n\n`$scheme` (文字列または `true`) が指定された場合は、ホスト情報 ([[\\yii\\web\\UrlManager::hostInfo]] から取得されます)\nを伴う絶対 URL が返されます。\n`$url` が既に絶対 URL であった場合には、スキームが指定されたものに置き換えられます。\n\n下記にいくつかの用例を挙げます。\n\n```php\n// /index.php?r=site%2Findex\necho Url::to(['site/index']);\n\n// /index.php?r=site%2Findex&src=ref1#name\necho Url::to(['site/index', 'src' => 'ref1', '#' => 'name']);\n\n// /index.php?r=post%2Fedit&id=100     エイリアス \"@postEdit\" が \"post/edit\" と定義されていると仮定\necho Url::to(['@postEdit', 'id' => 100]);\n\n// 現在リクエストされている URL\necho Url::to();\n\n// /images/logo.gif\necho Url::to('@web/images/logo.gif');\n\n// images/logo.gif\necho Url::to('images/logo.gif');\n\n// https://www.example.com/images/logo.gif\necho Url::to('@web/images/logo.gif', true);\n\n// https://www.example.com/images/logo.gif\necho Url::to('@web/images/logo.gif', 'https');\n```\n\nバージョン 2.0.3 以降では、[[yii\\helpers\\Url::current()]] を使って、現在リクエストされているルートと GET パラメータに基づいて URL を生成することが出来ます。\n`$params` パラメータを渡して、GET パラメータの中のいくつかを修正したり削除したり、または新しい GET パラメータを追加したりすることが出来ます。\n例えば、\n\n```php\n// $_GET が ['id' => 123, 'src' => 'google'] であり、現在のルートが \"post/view\" であると仮定\n\n// /index.php?r=post%2Fview&id=123&src=google\necho Url::current();\n\n// /index.php?r=post%2Fview&id=123\necho Url::current(['src' => null]);\n// /index.php?r=post%2Fview&id=100&src=google\necho Url::current(['id' => 100]);\n```\n\n\n## URL を記憶する <span id=\"remember-urls\"></span>\n\nURL を記憶して、後に続く一連のリクエストの一つを処理するときに、記憶した URL を使わなければならないという場合があります。\nこれは、次のようにして達成することが出来ます。\n \n```php\n// 現在の URL を記憶する\nUrl::remember();\n\n// 指定された URL を記憶する。引数の形式は Url::to() を参照。\nUrl::remember(['product/view', 'id' => 42]);\n\n// 指定された名前で URL を記憶する。\nUrl::remember(['product/view', 'id' => 42], 'product');\n```\n\n次のリクエストで、記憶された URL を次のようにして取得することが出来ます。\n\n```php\n$url = Url::previous();\n$productUrl = Url::previous('product');\n```\n\n## 相対 URL かどうかチェックする <span id=\"checking-relative-urls\"></span>\n\nURL が相対 URL であること、すなわち、URL がホスト情報の部分を持っていないことを確かめるために、次のコードを使うことが出来ます。\n\n```php\n$isRelative = Url::isRelative('test/it');\n```\n"
  },
  {
    "path": "docs/guide-ja/images/advanced-app-configs.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yFiles for Java 2.11-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Beschreibung\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"409.0\" width=\"148.0\" x=\"290.953299818952\" y=\"148.6669921875\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"148.0\" x=\"0.0\" y=\"0.0\">console</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 4</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n0:\"/>\n    </node>\n    <node id=\"n1\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"409.0\" width=\"148.0\" x=\"310.2342794220951\" y=\"167.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"148.0\" x=\"0.0\" y=\"0.0\">backend</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"310.2342794220951\" y=\"167.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 3</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n1:\"/>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"62.0\" width=\"118.0\" x=\"348.0\" y=\"628.9999999999999\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"32.6875\" x=\"42.65625\" y=\"21.6494140625\">index<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"rectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n3\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"402.37646484375\" width=\"148.0\" x=\"333.0\" y=\"188.62353515625\"/>\n              <y:Fill color=\"#DDFFDD\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#DDFFDD\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"148.0\" x=\"0.0\" y=\"0.0\">frontend</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 1</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n3:\">\n        <node id=\"n3::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"348.0\" y=\"226.0\"/>\n              <y:Fill color=\"#FFCCCC\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"44.013671875\" x=\"36.9931640625\" y=\"21.6494140625\">params<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n3::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"348.0\" y=\"322.0\"/>\n              <y:Fill color=\"#FFCCCC\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"72.689453125\" x=\"22.6552734375\" y=\"21.6494140625\">params-local<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n3::n2\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"348.0\" y=\"418.0\"/>\n              <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"30.009765625\" x=\"43.9951171875\" y=\"21.6494140625\">main<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n3::n3\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"348.0\" y=\"514.0\"/>\n              <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"58.685546875\" x=\"29.6572265625\" y=\"21.6494140625\">main-local<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"62.0\" width=\"223.21580824827026\" x=\"7.0\" y=\"628.9999999999999\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"41.353515625\" x=\"90.93114631163513\" y=\"21.6494140625\">aliases<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"rectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"306.37646484375\" width=\"311.0\" x=\"7.0\" y=\"188.62353515625\"/>\n              <y:Fill color=\"#DDFFDD\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#DDFFDD\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"311.0\" x=\"0.0\" y=\"0.0\">common</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"-8.0\" y=\"189.333984375\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 2</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n5:\">\n        <node id=\"n5::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"185.0\" y=\"226.0\"/>\n              <y:Fill color=\"#FFCCCC\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"72.689453125\" x=\"22.6552734375\" y=\"21.6494140625\">params-local<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n5::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"185.0\" y=\"418.0\"/>\n              <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"58.685546875\" x=\"29.6572265625\" y=\"21.6494140625\">main-local<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n5::n2\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"22.0\" y=\"226.0\"/>\n              <y:Fill color=\"#FFCCCC\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"44.013671875\" x=\"36.9931640625\" y=\"21.6494140625\">params<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n5::n3\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"22.0\" y=\"418.0\"/>\n              <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"30.009765625\" x=\"43.9951171875\" y=\"21.6494140625\">main<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <edge id=\"e0\" source=\"n3::n3\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"2.0\" y=\"10.125\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-111.48005839096935\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"3.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.25\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"33.47449351530429\" y=\"-2.0000000000001137\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n5::n1\" target=\"n3::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"5.6843418860808015E-14\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"218.8251533742331\" y=\"449.00000000000006\"/>\n            <y:Point x=\"218.8251533742331\" y=\"449.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"10.089752197265625\" y=\"-6.0\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n5::e0\" source=\"n5::n2\" target=\"n5::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"10.125\" y=\"-6.0\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n5::e1\" source=\"n5::n3\" target=\"n5::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"10.125\" y=\"-6.0\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n3::e0\" source=\"n3::n0\" target=\"n3::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"2.0\" y=\"10.125\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n3::e1\" source=\"n3::n1\" target=\"n3::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"2.0\" y=\"10.125\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n3::e2\" source=\"n3::n2\" target=\"n3::n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"2.0\" y=\"10.125\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n5::n0\" target=\"n3::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"-5.6843418860808015E-14\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"218.8251533742331\" y=\"256.99999999999994\"/>\n            <y:Point x=\"218.8251533742331\" y=\"257.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"10.089752197265625\" y=\"-6.0\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources/>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-ja/images/application-lifecycle.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.13-->\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d0\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key for=\"graphml\" id=\"d7\" yfiles.type=\"resources\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d0\"/>\n    <node id=\"n0\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"571.4472707112631\" width=\"763.2772213171534\" x=\"-1269.9373595143054\" y=\"-207.17439524332679\"/>\n              <y:Fill color=\"#FFCC0024\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FFCC00\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"763.2772213171534\" x=\"0.0\" y=\"0.0\">エントリスクリプト (index.php またはr yii)</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"225.33495140075684\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 4</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n0:\">\n        <node id=\"n0::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"324.9258883570935\" x=\"-1249.511914911339\" y=\"-169.79793039957679\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"208.0\" x=\"58.46294417854665\" y=\"5.6494140625\">アプリケーションの構成情報をロード<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n0::n1\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"309.37646484374994\" width=\"330.35133296005984\" x=\"-1254.9373595143054\" y=\"35.272875467936274\"/>\n                  <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"330.35133296005984\" x=\"0.0\" y=\"0.0\">アプリケーションのインスタンスを作成</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"19\" bottomF=\"19.15489692687993\" left=\"5\" leftF=\"5.425444602966309\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 5</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n0::n1:\">\n            <node id=\"n0::n1::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"72.64934031168627\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"45.34375\" x=\"124.79106917854665\" y=\"5.6494140625\">preInit()<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n1\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"122.64524027506516\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"124.0\" x=\"85.46294417854665\" y=\"5.6494140625\">エラーハンドラを登録<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n2\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"174.96110788981125\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"208.0\" x=\"43.46294417854665\" y=\"5.6494140625\">アプリケーションのプロパティを構成<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n3\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"226.56779181162517\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"27.33203125\" x=\"133.79692855354665\" y=\"5.6494140625\">init()<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n4\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.9258883570935\" x=\"-1234.511914911339\" y=\"280.4944433848063\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.025390625\" x=\"116.45024886604665\" y=\"5.6494140625\">bootstrap()<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n        <node id=\"n0::n2\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"411.6943410237631\" width=\"324.9258883570935\" x=\"-846.5860265542456\" y=\"-169.79793039957679\"/>\n                  <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"324.9258883570935\" x=\"0.0\" y=\"0.0\">アプリケーションを走らせる</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"1.1368683772161603E-13\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 3</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n0::n2:\">\n            <node id=\"n0::n2::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.9258883570935\" x=\"-831.5860265542456\" y=\"-132.42146555582667\"/>\n                  <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"164.705078125\" x=\"65.11040511604676\" y=\"5.6494140625\">EVENT_BEFORE_REQUEST<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n2::n1\" yfiles.foldertype=\"group\">\n              <data key=\"d4\"/>\n              <data key=\"d6\">\n                <y:ProxyAutoBoundsNode>\n                  <y:Realizers active=\"0\">\n                    <y:GroupNode>\n                      <y:Geometry height=\"204.37646484375\" width=\"294.9258883570935\" x=\"-831.5860265542456\" y=\"-78.79793039957679\"/>\n                      <y:Fill color=\"#99336635\" transparent=\"false\"/>\n                      <y:BorderStyle hasColor=\"false\" type=\"dashed\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#993366\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#FFFFFF\" visible=\"true\" width=\"294.9258883570935\" x=\"0.0\" y=\"0.0\">リクエストを処理</y:NodeLabel>\n                      <y:Shape type=\"roundrectangle\"/>\n                      <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                      <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                      <y:BorderInsets bottom=\"8\" bottomF=\"7.929194132486941\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                    </y:GroupNode>\n                    <y:GroupNode>\n                      <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                      <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 4</y:NodeLabel>\n                      <y:Shape type=\"roundrectangle\"/>\n                      <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                      <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                      <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                    </y:GroupNode>\n                  </y:Realizers>\n                </y:ProxyAutoBoundsNode>\n              </data>\n              <graph edgedefault=\"directed\" id=\"n0::n2::n1:\">\n                <node id=\"n0::n2::n1::n0\">\n                  <data key=\"d6\">\n                    <y:ShapeNode>\n                      <y:Geometry height=\"30.0\" width=\"264.9258883570935\" x=\"-816.5860265542456\" y=\"-41.421465555826785\"/>\n                      <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"220.0\" x=\"22.462944178546763\" y=\"5.6494140625\">リクエストをルートとパラメータに解決<y:LabelModel>\n                          <y:SmartNodeLabelModel distance=\"4.0\"/>\n                        </y:LabelModel>\n                        <y:ModelParameter>\n                          <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                        </y:ModelParameter>\n                      </y:NodeLabel>\n                      <y:Shape type=\"rectangle\"/>\n                    </y:ShapeNode>\n                  </data>\n                </node>\n                <node id=\"n0::n2::n1::n1\">\n                  <data key=\"d6\">\n                    <y:ShapeNode>\n                      <y:Geometry height=\"30.0\" width=\"264.9258883570935\" x=\"-816.5860265542456\" y=\"18.578534444173215\"/>\n                      <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"256.0\" x=\"4.462944178546763\" y=\"5.6494140625\">モジュール、コントローラ、アクションを作成<y:LabelModel>\n                          <y:SmartNodeLabelModel distance=\"4.0\"/>\n                        </y:LabelModel>\n                        <y:ModelParameter>\n                          <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                        </y:ModelParameter>\n                      </y:NodeLabel>\n                      <y:Shape type=\"rectangle\"/>\n                    </y:ShapeNode>\n                  </data>\n                </node>\n                <node id=\"n0::n2::n1::n2\">\n                  <data key=\"d6\">\n                    <y:ShapeNode>\n                      <y:Geometry height=\"30.0\" width=\"264.9258883570935\" x=\"-816.5860265542456\" y=\"72.64934031168627\"/>\n                      <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"124.0\" x=\"70.46294417854676\" y=\"5.6494140625\">アクションを走らせる<y:LabelModel>\n                          <y:SmartNodeLabelModel distance=\"4.0\"/>\n                        </y:LabelModel>\n                        <y:ModelParameter>\n                          <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                        </y:ModelParameter>\n                      </y:NodeLabel>\n                      <y:Shape type=\"rectangle\"/>\n                    </y:ShapeNode>\n                  </data>\n                </node>\n              </graph>\n            </node>\n            <node id=\"n0::n2::n2\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.9258883570935\" x=\"-831.5860265542456\" y=\"149.20206960042316\"/>\n                  <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"154.697265625\" x=\"70.11431136604676\" y=\"5.6494140625\">EVENT_AFTER_REQUEST<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n2::n3\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-831.5860265542456\" y=\"196.89641062418633\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"184.0\" x=\"55.46294417854676\" y=\"5.6494140625\">エンドユーザにレスポンスを送信<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n        <node id=\"n0::n3\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"324.9258883570935\" x=\"-846.5860265542456\" y=\"319.2728754679363\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"136.0\" x=\"94.46294417854676\" y=\"5.6494140625\">リクエストの処理を完了<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <edge id=\"e0\" source=\"n0\" target=\"n0::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-179.83580569315376\" sy=\"-180.3944529152355\" tx=\"-13.869777491410105\" ty=\"-154.8008369539754\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::e0\" source=\"n0::n0\" target=\"n0::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#99CCFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"76.0\" x=\"-115.73495249688426\" y=\"78.18481445312506\">構成情報配列<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"77.04379339868619\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e0\" source=\"n0::n1::n0\" target=\"n0::n1::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e1\" source=\"n0::n1::n1\" target=\"n0::n1::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e2\" source=\"n0::n1::n2\" target=\"n0::n1::n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e3\" source=\"n0::n1::n3\" target=\"n0::n1::n4\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::e1\" source=\"n0::n1\" target=\"n0::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"2.8060680342755404\" sy=\"-48.37646484374994\" tx=\"-162.49660512430125\" ty=\"105.53540293375653\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::e0\" source=\"n0::n2::n0\" target=\"n0::n2::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::n1::e0\" source=\"n0::n2::n1::n0\" target=\"n0::n2::n1::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::n1::e1\" source=\"n0::n2::n1::n1\" target=\"n0::n2::n1::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::e1\" source=\"n0::n2::n1\" target=\"n0::n2::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::e2\" source=\"n0::n2::n2\" target=\"n0::n2::n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::e2\" source=\"n0::n2\" target=\"n0::n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#99CCFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"88.0\" x=\"-107.99997446554255\" y=\"28.318773905436274\">終了ステータス<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"63.99999999999999\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.47945569632951074\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d7\">\n    <y:Resources/>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-ja/images/application-structure.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.13-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"872.1807999999999\" y=\"-14.764159999999947\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"10\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"28.501953125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"84.0\" x=\"8.0\" y=\"3.2490234375\">アプリケーション\nコンポーネント<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"-91.97375999999994\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"10\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"16.2509765625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"94.0\" x=\"3.0\" y=\"9.37451171875\">エントリスクリプト<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"-14.764159999999947\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"10\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"16.2509765625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"84.0\" x=\"8.0\" y=\"9.37451171875\">アプリケーション<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"62.44544000000004\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"10\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"16.2509765625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.0\" x=\"18.0\" y=\"9.374511718750007\">コントローラ<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"872.1807999999999\" y=\"62.44544000000005\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"10\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"16.2509765625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"44.0\" x=\"28.0\" y=\"9.37451171875\">フィルタ<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"532.664\" y=\"23.901600000000087\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"10\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"16.2509765625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"54.0\" x=\"23.0\" y=\"9.37451171875\">モジュール<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"618.4047999999991\" y=\"139.65504000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"10\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"16.2509765625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"34.0\" x=\"33.0\" y=\"9.37451171875\">ビュー<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"786.161599999999\" y=\"139.65504000000004\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"10\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"16.2509765625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"34.0\" x=\"33.0\" y=\"9.37451171875\">モデル<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n8\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"532.664\" y=\"216.86464000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"76.0\" x=\"12.0\" y=\"8.1494140625\">ウィジェット<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n9\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"216.86464000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"10\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"16.2509765625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"84.0\" x=\"8.0\" y=\"9.37451171875\">アセットバンドル<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n2\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"20.681640625\" x=\"3.684755371093729\" y=\"-26.101202829100025\">1:1<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.025600000000054\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.17992697197425173\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n0\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.01171875\" x=\"-47.638518923284664\" y=\"-23.37618601966852\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.025599999999994\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5470891790487767\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n5\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.01171875\" x=\"16.6705249668222\" y=\"30.389885834361735\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"44.894496863129426\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.25416574623968574\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n3\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.01171875\" x=\"4.248516308593707\" y=\"-26.494264459837467\">1..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"15.254400000000032\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.2090245199765919\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n3\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.01171875\" x=\"-57.21845160906594\" y=\"-64.42636347296329\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"43.47658308018222\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.881412889692355\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n5\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"582.664\" y=\"-3.598399999999913\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.01171875\" x=\"-16.411943066406252\" y=\"-42.70266007995597\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"16.483200000000004\" distanceToCenter=\"true\" position=\"right\" ratio=\"5.822600000000001\" segment=\"-2\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n6\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.01171875\" x=\"-10.471163234315782\" y=\"-25.304719149604495\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"11.385360101663515\" distanceToCenter=\"true\" position=\"left\" ratio=\"0.10670293331985262\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e7\" source=\"n7\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.01171875\" x=\"-8.09331897615914\" y=\"-26.61891685238112\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.669798038586146\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.1670192242704811\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e8\" source=\"n9\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.01171875\" x=\"-8.582034677365982\" y=\"-26.348798499628572\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.15599378957306\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.15481212573620068\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e9\" source=\"n8\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.01171875\" x=\"-13.858375192872018\" y=\"-24.603393693996793\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"13.24330808446041\" distanceToCenter=\"true\" position=\"left\" ratio=\"0.05506723556297327\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e10\" source=\"n4\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.01171875\" x=\"-45.861058691406356\" y=\"-22.966588137206998\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"13.616000000000007\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e11\" source=\"n9\" target=\"n8\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.01171875\" x=\"-42.528297905071895\" y=\"-19.894585219726537\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.543999999999983\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.4117077892835431\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e12\" source=\"n7\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.01171875\" x=\"-43.20232446859063\" y=\"-19.894590493164003\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.543999999999983\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.4531592446547025\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources/>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-ja/images/rbac-access-check-1.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-ja/images/rbac-access-check-2.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-ja/images/rbac-access-check-3.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-ja/images/rbac-hierarchy-1.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n5\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-ja/images/rbac-hierarchy-2.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-ja/images/request-lifecycle.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.13-->\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d0\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key for=\"graphml\" id=\"d7\" yfiles.type=\"resources\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d0\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"70.13700103759766\" width=\"56.558998107910156\" x=\"198.38633947503078\" y=\"261.099458694458\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"26.279499053955078\" y=\"74.13700103759766\">\n            <y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"-0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"0.5\" offsetX=\"0.0\" offsetY=\"4.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"40.0\" x=\"-35.30331532610154\" y=\"25.717914581298828\">ユーザ<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.3825828831525385\" labelRatioY=\"0.0\" nodeRatioX=\"-0.5\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"637.1271178722382\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"40.0\" x=\"39.5\" y=\"13.1494140625\">モデル<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"46.887996673583984\" width=\"39.527000427246094\" x=\"828.8018617630005\" y=\"544.9831195354463\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"76.0\" x=\"-18.236499786376953\" y=\"-30.723247528076172\">データベース<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-12.022075653076172\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"17.763500213623047\" y=\"21.443998336791992\">\n            <y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"713.6271178722382\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"40.0\" x=\"39.5\" y=\"13.1494140625\">ビュー<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n4\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"377.0372841596604\" width=\"219.27949905395508\" x=\"527.4919853210449\" y=\"407.9266515731811\"/>\n              <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"219.27949905395508\" x=\"0.0\" y=\"0.0\">コントローラ</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"2\" leftF=\"1.7335329055786133\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"412.2765645980835\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 1</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n4:\">\n        <node id=\"n4::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"445.3031164169311\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"100.0\" x=\"25.279499053955078\" y=\"5.6494140625\">アクションを作成<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n4::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"45.0\" width=\"159.91864204406738\" x=\"558.5326642990112\" y=\"521.8166599273682\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"88.0\" x=\"35.95932102203369\" y=\"13.1494140625\">フィルタを実行<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"diamond\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n4::n2\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"162.71328270435333\" width=\"187.54596614837646\" x=\"544.2255182266235\" y=\"607.2506530284882\"/>\n                  <y:Fill color=\"#99336635\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#993366\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#FFFFFF\" visible=\"true\" width=\"187.54596614837646\" x=\"0.0\" y=\"0.0\">アクション</y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"4\" bottomF=\"3.8368178606033325\" left=\"4\" leftF=\"3.9869680404663086\" right=\"3\" rightF=\"3.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 3</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n4::n2:\">\n            <node id=\"n4::n2::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"644.6271178722382\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"88.0\" x=\"31.279499053955078\" y=\"5.6494140625\">モデルをロード<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n4::n2::n1\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"721.1271178722382\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"124.0\" x=\"13.279499053955078\" y=\"5.6494140625\">ビューをレンダリング<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n      </graph>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"198.3863394750308\" y=\"713.6271178722382\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"33.40234375\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"88.0\" x=\"15.5\" y=\"5.798828125\">レスポンス\nコンポーネント<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"254.50096702575684\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"33.40234375\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"88.0\" x=\"15.5\" y=\"5.798828125\">リクエスト\nコンポーネント<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"144.132230758667\" width=\"219.27949905395508\" x=\"527.4919853210449\" y=\"222.86873626708984\"/>\n              <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"219.27949905395508\" x=\"0.0\" y=\"0.0\">アプリケーション</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"21\" leftF=\"20.720500946044922\" right=\"18\" rightF=\"18.0\" top=\"2\" topF=\"1.7557659149169922\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 2</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n7:\">\n        <node id=\"n7::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"262.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"76.0\" x=\"37.27949905395508\" y=\"5.6494140625\">ルートを解決<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n7::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"322.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"112.0\" x=\"19.279499053955078\" y=\"5.6494140625\">コントローラを作成<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <node id=\"n8\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"142.37646484375\" width=\"199.93387606143483\" x=\"292.6574954986572\" y=\"224.62450218200684\"/>\n              <y:Fill color=\"#FFCC0024\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FFCC00\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"199.93387606143483\" x=\"0.0\" y=\"0.0\">エントリスクリプト</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"225.33495140075684\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 4</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n8:\">\n        <node id=\"n8::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"169.93387606143483\" x=\"307.6574954986572\" y=\"262.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"148.0\" x=\"10.966938030717415\" y=\"5.6494140625\">アプリの構成情報をロード<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n8::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"169.93387606143483\" x=\"307.6574954986572\" y=\"322.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"160.0\" x=\"4.966938030717415\" y=\"5.6494140625\">アプリケーションを走らせる<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <edge id=\"e0\" source=\"n2\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n5\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-31.22050094604495\" sy=\"-22.49430537223816\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"28.00000600945461\" y=\"-193.17557203769684\">\n            <y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"17.34765625\" x=\"-8.673822115545391\" y=\"-200.52615797519684\">11<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n6\" target=\"n7::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-29.525518994973595\" y=\"-10.349628448486328\">3<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"0.9990329742431641\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.25395048761528816\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n7::e0\" source=\"n7::n0\" target=\"n7::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n0\" target=\"n8\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"-86.58565098150827\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"17.89324407708159\" y=\"-9.454540677880914\">1<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.0\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n7::n1\" target=\"n4\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"2.408647025755731\" ty=\"-181.21658369302781\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-4.962140296002758\" y=\"18.61224679946895\">4<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n3\" target=\"n4::n2::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-30.100868416252297\" y=\"-9.35060429573059\">9<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.26448415477215953\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n4::n2::n1\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"-2.2737367544323206E-13\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"17.34765625\" x=\"-83.18526519030615\" y=\"-9.350604295730818\">10<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.2786124840137136\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e7\" source=\"n8::n1\" target=\"n7\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"60.77427719662158\" sy=\"0.355224609375\" tx=\"-109.63961141576266\" ty=\"42.066115379333496\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"19.602683079240364\" y=\"-9.470142300213183\">2<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e8\" source=\"n1\" target=\"n4::n2::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-31.270312699786246\" y=\"-10.35060429573059\">8<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"1.0\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.2858946861565087\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e9\" source=\"n4::n1\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-79.9882439360951\" sy=\"2.2737367544323206E-13\" tx=\"30.048898971244075\" ty=\"-1.4999999999999991\">\n            <y:Point x=\"287.93523844627487\" y=\"544.3166599273684\"/>\n          </y:Path>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-76.70009317381192\" y=\"-9.350576400756609\">6<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.23459266172695797\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::e0\" source=\"n4::n0\" target=\"n4::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFEFD6\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-5.336933135986328\" y=\"4.999985313415493\">5<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.0\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::e1\" source=\"n4::n1\" target=\"n4::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"-81.01828954355172\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFEFD6\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-5.4491733540852465\" y=\"5.039560317993164\">7<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.0\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::n2::e0\" source=\"n4::n2::n0\" target=\"n4::n2::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n8::e0\" source=\"n8::n0\" target=\"n8::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d7\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"66px\" viewBox=\"0 0 57 66\" enable-background=\"new 0 0 57 66\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3799\" y1=\"-2276.8809\" x2=\"27.6209\" y2=\"-2306.6792\" gradientTransform=\"matrix(1 0 0 -1 0.2803 -2252.9199)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_13_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t&lt;path fill=\"#2068A3\" stroke=\"#2068A3\" d=\"M28.106,33.487c-8.112,0-12.688,4.312-12.688,10.437c0,7.422,12.688,10.438,12.688,10.438\n\t\ts14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.487,28.106,33.487z M26.288,53.051c0,0-7.135-2.093-8.805-7.201\n\t\tc-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"14.2417\" cy=\"9.1006\" r=\"53.247\" gradientTransform=\"matrix(1 0 0 -1 0.04 65.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#74AEEE\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#2068A3\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#2068A3\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.022,7.807-14.022,7.807s-10.472-2.484-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path fill=\"#5491CF\" stroke=\"#2068A3\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397c-0.514,1.027-1.669,4.084-1.669,5.148\n\t\tc0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.335-2.36c-3.601-1.419-4.071-3.063-5.89-4.854\n\t\tC12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t&lt;path fill=\"#5491CF\" stroke=\"#2068A3\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617c0.516,1.025,3.617,3.693,3.617,6.617\n\t\tc0,5.186-10.27,8.576-16.698,9.145c1.429,4.938,11.372,1.293,13.804-0.313c3.563-2.354,4.563-5.133,7.854-3.705\n\t\tC47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t&lt;path fill=\"none\" stroke=\"#2068A3\" stroke-linecap=\"round\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t&lt;path fill=\"none\" stroke=\"#2068A3\" stroke-linecap=\"round\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.623\" cy=\"-2278.646\" r=\"23.425\" fx=\"23.0534\" fy=\"-2281.1357\" gradientTransform=\"matrix(1 0 0 -1 0.2803 -2252.9199)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"5761.7578\" y1=\"11330.6484\" x2=\"5785.3872\" y2=\"11424.0977\" gradientTransform=\"matrix(0.275 0 0 0.2733 -1558.9874 -3088.4209)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M27.958,6.333c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.083,13.952,36.271,6.268,27.958,6.333z\"/&gt;\n\t&lt;path id=\"Hair_Young_Brown_1_\" fill=\"#CC9869\" stroke=\"#99724F\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n\t&lt;path fill=\"#4B4B4B\" stroke=\"#4B4B4B\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" d=\"M28.105,2\n\t\tC22.464,2,20.2,4.246,18.13,5.533C29.753,2.865,41.152,10.375,44.46,20.5C44.459,16.875,44.459,2,28.105,2z\"/&gt;\n\t&lt;path fill=\"#9B9B9B\" stroke=\"#4B4B4B\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" d=\"M11.151,17.751\n\t\tC12.878,8.25,18.686,6.309,25.273,7.127C31.295,7.875,36.93,10.491,44.459,20.5C37.777,7.125,20.278-3.375,9.903,3.921\n\t\tC5.569,6.97,4.903,13.375,11.151,17.751z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\"\n\t xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\t x=\"0px\" y=\"0px\" width=\"41px\" height=\"48px\" viewBox=\"-0.875 -0.887 41 48\" enable-background=\"new -0.875 -0.887 41 48\"\n\t xml:space=\"preserve\"&gt;\n&lt;defs&gt;\n&lt;/defs&gt;\n&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-979.1445\" x2=\"682.0508\" y2=\"-979.1445\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_1_)\" d=\"M19.625,36.763C8.787,36.763,0,34.888,0,32.575v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,34.888,30.464,36.763,19.625,36.763z\"/&gt;\n&lt;linearGradient id=\"SVGID_2_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-973.1445\" x2=\"682.0508\" y2=\"-973.1445\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_2_)\" d=\"M19.625,36.763c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.927-18.396,3.927\n\tc-9.481,0-17.396-1.959-18.396-3.927l-1.229,2C0,34.888,8.787,36.763,19.625,36.763z\"/&gt;\n&lt;path fill=\"#3C89C9\" d=\"M19.625,26.468c10.16,0,19.625,2.775,19.625,2.775c-0.375,2.721-5.367,5.438-19.554,5.438\n\tc-12.125,0-18.467-2.484-19.541-4.918C-0.127,29.125,9.465,26.468,19.625,26.468z\"/&gt;\n&lt;linearGradient id=\"SVGID_3_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-965.6948\" x2=\"682.0508\" y2=\"-965.6948\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_3_)\" d=\"M19.625,23.313C8.787,23.313,0,21.438,0,19.125v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,21.438,30.464,23.313,19.625,23.313z\"/&gt;\n&lt;linearGradient id=\"SVGID_4_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-959.6948\" x2=\"682.0508\" y2=\"-959.6948\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_4_)\" d=\"M19.625,23.313c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.926-18.396,3.926\n\tc-9.481,0-17.396-1.959-18.396-3.926l-1.229,2C0,21.438,8.787,23.313,19.625,23.313z\"/&gt;\n&lt;path fill=\"#3C89C9\" d=\"M19.476,13.019c10.161,0,19.625,2.775,19.625,2.775c-0.375,2.721-5.367,5.438-19.555,5.438\n\tc-12.125,0-18.467-2.485-19.541-4.918C-0.277,15.674,9.316,13.019,19.476,13.019z\"/&gt;\n&lt;linearGradient id=\"SVGID_5_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-952.4946\" x2=\"682.0508\" y2=\"-952.4946\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_5_)\" d=\"M19.625,10.113C8.787,10.113,0,8.238,0,5.925v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,8.238,30.464,10.113,19.625,10.113z\"/&gt;\n&lt;linearGradient id=\"SVGID_6_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-946.4946\" x2=\"682.0508\" y2=\"-946.4946\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_6_)\" d=\"M19.625,10.113c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.926-18.396,3.926\n\tc-9.481,0-17.396-1.959-18.396-3.926L0,5.925C0,8.238,8.787,10.113,19.625,10.113z\"/&gt;\n&lt;linearGradient id=\"SVGID_7_\" gradientUnits=\"userSpaceOnUse\" x1=\"644.0293\" y1=\"-943.4014\" x2=\"680.8223\" y2=\"-943.4014\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;ellipse fill=\"url(#SVGID_7_)\" cx=\"19.625\" cy=\"3.926\" rx=\"18.396\" ry=\"3.926\"/&gt;\n&lt;path opacity=\"0.24\" fill=\"#FFFFFF\" enable-background=\"new    \" d=\"M31.04,45.982c0,0-4.354,0.664-7.29,0.781\n\tc-3.125,0.125-8.952,0-8.952,0l-2.384-10.292l0.044-2.108l-1.251-1.154L9.789,23.024l-0.082-0.119L9.5,20.529l-1.65-1.254\n\tL5.329,8.793c0,0,4.213,0.903,7.234,1.07s8.375,0.25,8.375,0.25l3,9.875l-0.25,1.313l1.063,2.168l2.312,9.645l-0.521,1.416\n\tl1.46,1.834L31.04,45.982z\"/&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-ja/input-file-upload.md",
    "content": "ファイルをアップロードする\n==========================\n\nYii におけるファイルのアップロードは、通常、アップロードされる個々のファイルを `UploadedFile` としてカプセル化する\n[[yii\\web\\UploadedFile]] の助けを借りて実行されます。これを [[yii\\widgets\\ActiveForm]] および [モデル](structure-models.md)\nと組み合わせることで、安全なファイル・アップロード・メカニズムを簡単に実装することが出来ます。\n\n\n## モデルを作成する <span id=\"creating-models\"></span>\n\nプレーンなテキスト・インプットを扱うのと同じように、一つのファイルをアップロードするためには、モデル・クラスを作成して、\nそのモデルの一つの属性を使ってアップロードされるファイルのインスタンスを保持します。\nまた、ファイルのアップロードを検証するために、検証規則も宣言しなければなりません。例えば、\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\nuse yii\\web\\UploadedFile;\n\nclass UploadForm extends Model\n{\n    /**\n     * @var UploadedFile\n     */\n    public $imageFile;\n\n    public function rules()\n    {\n        return [\n            [['imageFile'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg'],\n        ];\n    }\n    \n    public function upload()\n    {\n        if ($this->validate()) {\n            $this->imageFile->saveAs('uploads/' . $this->imageFile->baseName . '.' . $this->imageFile->extension);\n            return true;\n        } else {\n            return false;\n        }\n    }\n}\n```\n\n上記のコードにおいては、`imageFile` 属性がアップロードされたファイルのインスタンスを保持するのに使われます。\nこの属性が関連付けられている `file` 検証規則は、[[yii\\validators\\FileValidator]] を使って、`png` または `jpg` の拡張子を持つファイルがアップロードされることを保証しています。\n`upload()` メソッドは検証を実行して、アップロードされたファイルをサーバに保存します。\n\n`file` バリデータによって、ファイル拡張子、サイズ、MIME タイプなどをチェックすることが出来ます。\n詳細については、[コア・バリデータ](tutorial-core-validators.md#file) のセクションを参照してください。\n\n> Tip: 画像をアップロードしようとする場合は、`image` バリデータを代りに使うことを考慮しても構いません。\n  `image` バリデータは [[yii\\validators\\ImageValidator]] によって実装されており、属性が有効な画像、すなわち、\n  保存したり [Imagine エクステンション](https://github.com/yiisoft/yii2-imagine) を使って処理したりすることが可能な有効な画像を、受け取ったかどうかを検証します。\n\n\n## ファイル・インプットをレンダリングする <span id=\"rendering-file-input\"></span>\n\n次に、ビューでファイル・インプットを作成します。\n\n```php\n<?php\nuse yii\\widgets\\ActiveForm;\n?>\n\n<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?>\n\n    <?= $form->field($model, 'imageFile')->fileInput() ?>\n\n    <button>送信</button>\n\n<?php ActiveForm::end() ?>\n```\n\nファイルが正しくアップロードされるように、フォームに `enctype` オプションを追加することを憶えておくのは重要なことです。\n`fileInput()` を呼ぶと `<input type=\"file\">` のタグがレンダリングされて、ユーザがアップロードするファイルを選ぶことが出来るようになります。\n\n> Tip: バージョン 2.0.8 以降では、ファイル・インプットのフィールドが使われているときは、\n  [[yii\\widgets\\ActiveField::fileInput|fileInput]] がフォームに `enctype` オプションを自動的に追加します。\n\n## 繋ぎ合せる <span id=\"wiring-up\"></span>\n\nそして、コントローラ・アクションの中で、モデルとビューを繋ぎ合せるコードを書いて、ファイルのアップロードを実装します。\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\nuse app\\models\\UploadForm;\nuse yii\\web\\UploadedFile;\n\nclass SiteController extends Controller\n{\n    public function actionUpload()\n    {\n        $model = new UploadForm();\n\n        if (Yii::$app->request->isPost) {\n            $model->imageFile = UploadedFile::getInstance($model, 'imageFile');\n            if ($model->upload()) {\n                // ファイルのアップロードが成功\n                return;\n            }\n        }\n\n        return $this->render('upload', ['model' => $model]);\n    }\n}\n```\n\n上記のコードでは、フォームが送信されると [[yii\\web\\UploadedFile::getInstance()]] メソッドが呼ばれて、\nアップロードされたファイルが `UploadedFile` のインスタンスとして表現されます。\nそして、次に、モデルの検証によってアップロードされたファイルが有効なものであることを確かめ、サーバにファイルを保存します。\n\n\n## 複数のファイルをアップロードする <span id=\"uploading-multiple-files\"></span>\n\nここまでの項で示したコードに若干の修正を加えれば、複数のファイルを一度にアップロードすることも出来ます。\n\n最初に、モデル・クラスを修正して、`file` 検証規則に `maxFiles` オプションを追加して、アップロードを許可されるファイルの最大数を制限しなければなりません。\n`maxFiles` を `0` に設定することは、同時にアップロード出来るファイル数に制限がないことを意味します。\n同時にアップロードすることを許されるファイルの数は、また、PHP のディレクティブ\n[`max_file_uploads`](https://www.php.net/manual/ja/ini.core.php#ini.max-file-uploads) (デフォルト値は 20) によっても制限されます。\n`upload()` メソッドも、アップロードされた複数のファイルを一つずつ保存するように修正しなければなりません。\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\nuse yii\\web\\UploadedFile;\n\nclass UploadForm extends Model\n{\n    /**\n     * @var UploadedFile[]\n     */\n    public $imageFiles;\n\n    public function rules()\n    {\n        return [\n            [['imageFiles'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg', 'maxFiles' => 4],\n        ];\n    }\n    \n    public function upload()\n    {\n        if ($this->validate()) { \n            foreach ($this->imageFiles as $file) {\n                $file->saveAs('uploads/' . $file->baseName . '.' . $file->extension);\n            }\n            return true;\n        } else {\n            return false;\n        }\n    }\n}\n```\n\nビュー・ファイルでは、`fileInput()` の呼び出しに `multiple` オプションを追加して、ファイル・アップロードのフィールドが複数のファイルを受け取ることが出来るようにしなければなりません。\nまた、`imageFiles` を `imageFiles[]` に変更して、属性の値が配列として送信されるようにする必要があります。\n\n```php\n<?php\nuse yii\\widgets\\ActiveForm;\n?>\n\n<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?>\n\n    <?= $form->field($model, 'imageFiles[]')->fileInput(['multiple' => true, 'accept' => 'image/*']) ?>\n\n    <button>送信</button>\n\n<?php ActiveForm::end() ?>\n```\n\nそして、最後に、コントローラ・アクションの中では、`UploadedFile::getInstance()` の代りに `UploadedFile::getInstances()` を呼んで、\n`UploadedFile` インスタンスの配列を `UploadForm::imageFiles` に代入しなければなりません。\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\nuse app\\models\\UploadForm;\nuse yii\\web\\UploadedFile;\n\nclass SiteController extends Controller\n{\n    public function actionUpload()\n    {\n        $model = new UploadForm();\n\n        if (Yii::$app->request->isPost) {\n            $model->imageFiles = UploadedFile::getInstances($model, 'imageFiles');\n            if ($model->upload()) {\n                // ファイルのアップロードが成功\n                return;\n            }\n        }\n\n        return $this->render('upload', ['model' => $model]);\n    }\n}\n```\n"
  },
  {
    "path": "docs/guide-ja/input-form-javascript.md",
    "content": "クライアント・サイドで ActiveForm を拡張する\n============================================\n\n[[yii\\widgets\\ActiveForm]] ウィジェットは、クライアント・サイドの検証に使う一連の JavaScript メソッドを備えています。\nその実装は非常に柔軟で、様々な方法で拡張することが可能になっています。\n下記でそれについて解説します。\n\n## ActiveForm イベント\n\nActiveForm は、一連の専用のイベントを発生させます。\n次のようなコードを使って、これらのイベントを購読して処理することが出来ます。\n\n```javascript\n$('#contact-form').on('beforeSubmit', function (e) {\n\tif (!confirm(\"全てオーケー。送信しますか?\")) {\n\t\treturn false;\n\t}\n\treturn true;\n});\n```\n\n以下、利用できるイベントを見ていきましょう。\n\n### `beforeValidate`\n\n`beforeValidate` は、フォーム全体を検証する前にトリガされます。\n\nイベント・ハンドラのシグニチャは以下の通り:\n\n```javascript\nfunction (event, messages, deferreds)\n```\n\n引数は以下の通り:\n\n- `event`: イベントのオブジェクト。\n- `messages`: 連想配列で、キーは属性の ID、\n  値は対応する属性のエラー・メッセージの配列です。\n- `deferreds`: Deferred オブジェクトの配列。`deferreds.add(callback)` を使って、\n  新しい deferrd な検証を追加することが出来ます。\n\nハンドラが真偽値 `false` を返すと、このイベントに続くフォームの検証は中止されます。\nその結果、`afterValidate` イベントもトリガされません。\n\n### `afterValidate`\n\n`afterValidate` イベントは、フォーム全体を検証した後でトリガされます。\n\nイベント・ハンドラのシグニチャは以下の通り:\n\n```javascript\nfunction (event, messages, errorAttributes)\n```\n\n引数は以下の通り:\n\n- `event`: イベントのオブジェクト。\n- `messages`: 連想配列で、キーは属性の ID、\n  値は対応する属性のエラー・メッセージの配列です。\n- `errorAttributes`: 検証エラーがある属性の配列。\n  この引数の構造については `attributeDefaults` を参照して下さい。\n\n### `beforeValidateAttribute`\n\n`beforeValidateAttribute` イベントは、属性を検証する前にトリガされます。\nイベント・ハンドラのシグニチャは以下の通り:\n\n```javascript\nfunction (event, attribute, messages, deferreds)\n```\n     \n引数は以下の通り:\n\n- `event`: イベントのオブジェクト。\n- `attribute`: 検証される属性。\n  この引数の構造については `attributeDefaults` を参照して下さい。\n- `messages`: 指定された属性に対する検証エラー・メッセージを追加することが出来る配列。\n- `deferreds`: Deferred オブジェクトの配列。\n  `deferreds.add(callback)` を使って、新しい deferrd な検証を追加することが出来ます。\n\nハンドラが真偽値 `false` を返すと、指定された属性の検証は中止されます。\nその結果、`afterValidateAttribute` イベントもトリガされません。\n\n### `afterValidateAttribute`\n\n`afterValidateAttribute` イベントは、フォーム全体および各属性の検証の後にトリガされます。\n\nイベント・ハンドラのシグニチャは以下の通り:\n\n```javascript\nfunction (event, attribute, messages)\n```\n\n引数は以下の通り:\n\n- `event`: イベントのオブジェクト。\n- `attribute`: 検証される属性。\n  この引数の構造については `attributeDefaults` を参照して下さい。\n- `messages`: 指定された属性に対する追加の検証エラー・メッセージを追加することが\n  出来る配列。\n\n### `beforeSubmit`\n\n`beforeSubmit` イベントは、全ての検証が通った後、フォームを送信する前にトリガされます。\n\nイベント・ハンドラのシグニチャは以下の通り:\n\n```javascript\nfunction (event)\n```\n\nここで、`event` は、イベントのオブジェクトです。\n\nハンドラが真偽値 `false` を返すと、フォームの送信は中止されます。\n\n### `ajaxBeforeSend`\n         \n`ajaxBeforeSend` イベントは、AJAX ベースの検証のための AJAX リクエストを送信する前にトリガされます。\n\nイベント・ハンドラのシグニチャは以下の通り:\n\n```javascript\nfunction (event, jqXHR, settings)\n```\n\n引数は以下の通り:\n\n- `event`: イベントのオブジェクト。\n- `jqXHR`: jqXHR のオブジェクト。\n- `settings`: AJAX リクエストの設定。\n\n### `ajaxComplete`\n\n`ajaxComplete` イベントはAJAX ベースの検証のための AJAX リクエストが完了した後にトリガされます。\n\nイベント・ハンドラのシグニチャは以下の通り:\n\n```javascript\nfunction (event, jqXHR, textStatus)\n```\n\n引数は以下の通り:\n\n- `event`: イベントのオブジェクト。\n- `jqXHR`: jqXHR のオブジェクト。\n- `textStatus`: リクエストの状態 (\"success\", \"notmodified\", \"error\", \"timeout\",\n\"abort\", または \"parsererror\")。\n\n## AJAX でフォームを送信する\n\n検証(バリデーション)は、クライアント・サイドまたは AJAX リクエストによって行うことが出来ますが、\nフォームの送信そのものはデフォルトでは通常のリクエストとして実行されます。\nフォームを AJAX で送信したい場合は、次のように、フォームの `beforeSubmit` イベントを処理することによって達成することが出来ます。\n\n```javascript\nvar $form = $('#formId');\n$form.on('beforeSubmit', function() {\n    var data = $form.serialize();\n    $.ajax({\n        url: $form.attr('action'),\n        type: 'POST',\n        data: data,\n        success: function (data) {\n            // 成功したときの実装\n        },\n        error: function(jqXHR, errMsg) {\n            alert(errMsg);\n        }\n     });\n     return false; // デフォルトの送信を抑止\n});\n```\n\njQuery の `ajax()` 関数について更に学習するためには、[jQuery documentation](https://api.jquery.com/jQuery.ajax/) を参照して下さい。\n\n\n## フィールドを動的に追加する\n\n現在のウェブ・アプリケーションでは、ユーザに対して表示した後でフォームを変更する必要がある場合がよくあります。\n例えば、\"追加\"アイコンをクリックするとフィールドが追加される場合などです。\nこのようなフィールドに対するクライアント・サイドの検証を有効にするためには、フィールドを ActiveForm JavaScript プラグインに登録しなければなりません。\n\nフィールドそのものを追加して、そして、検証のリストに追加しなければなりません。\n\n```javascript\n$('#contact-form').yiiActiveForm('add', {\n    id: 'address',\n    name: 'address',\n    container: '.field-address',\n    input: '#address',\n    error: '.help-block',\n    validate:  function (attribute, value, messages, deferred, $form) {\n        yii.validation.required(value, messages, {message: \"Validation Message Here\"});\n    }\n});\n```\n\nフィールドを検証のリストから削除して検証されないようにするためには、次のようにします。\n\n```javascript\n$('#contact-form').yiiActiveForm('remove', 'address');\n```\n"
  },
  {
    "path": "docs/guide-ja/input-forms.md",
    "content": "フォームを作成する\n==================\n\nアクティブ・レコードに基づくフォーム : ActiveForm <span id=\"activerecord-based-forms-activeform\"></span>\n-----------------------------------------------\nYii においてフォームを使用するときは、主として [[yii\\widgets\\ActiveForm]] による方法を使います。\nフォームがモデルに基づくものである場合はこの方法を選ぶべきです。\nこれに加えて、[[yii\\helpers\\Html]] にはいくつかの有用なメソッドがあり、どんなフォームでも、ボタンやヘルプ・テキストを追加するのには、通常、それらのメソッドを使います。\n\nフォームは、クライアント・サイドで表示されるものですが、たいていの場合、対応する [モデル](structure-models.md) を持ち、それを使ってサーバ・サイドでフォームの入力を検証します\n(入力の検証の詳細については、[入力を検証する](input-validation.md) のセクションを参照してください)。\nモデルに基づくフォームを作成する場合、最初のステップは、モデルそのものを定義することです。\nモデルは、データベースの何らかのデータを表現するために [アクティブ・レコード](db-active-record.md) から派生させたクラスか、\nあるいは、任意の入力、例えばログイン・フォームの入力を保持するための ([[yii\\base\\Model]] から派生させた) 汎用的な Model クラスか、どちらかにすることが出来ます。\n\n> Tip: フォームのフィールドがデータベースのカラムと異なっていたり、そのフォーム特有のフォーマット形式やロジックがあったりする場合は、\n> [[yii\\base\\Model]] を拡張した独自のモデルを作るほうを選んで下さい。\n\n以下の例においては、ログイン・フォームのために汎用的なモデルを使う方法を示します。\n\n```php\n<?php\n\nclass LoginForm extends \\yii\\base\\Model\n{\n    public $username;\n    public $password;\n\n    public function rules()\n    {\n        return [\n            // 検証規則をここで定義\n        ];\n    }\n\n```\n\nコントローラにおいて、このモデルのインスタンスをビューに渡し、ビューでは [[yii\\widgets\\ActiveForm|ActiveForm]]\nウィジェットがフォームを表示するのに使われます。\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n$form = ActiveForm::begin([\n    'id' => 'login-form',\n    'options' => ['class' => 'form-horizontal'],\n]) ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n\n    <div class=\"form-group\">\n        <div class=\"col-lg-offset-1 col-lg-11\">\n            <?= Html::submitButton('ログイン', ['class' => 'btn btn-primary']) ?>\n        </div>\n    </div>\n<?php ActiveForm::end() ?>\n```\n\n### `begin()` と `end()` で囲む <span id=\"wrapping-with-begin-and-end\"></span>\n上記のコードでは、[[yii\\widgets\\ActiveForm::begin()|ActiveForm::begin()]] がフォームのインスタンスを作成するとともに、フォームの開始をマークしています。\n[[yii\\widgets\\ActiveForm::begin()|ActiveForm::begin()]] と [[yii\\widgets\\ActiveForm::end()|ActiveForm::end()]]\nの間に置かれた全てのコンテントが HTML の `<form>` タグによって囲まれます。\nどのウィジェットでも同じですが、ウィジェットをどのように構成すべきかに関するオプションを指定するために、`begin` メソッドに配列を渡すことが出来ます。\nこの例では、追加の CSS クラスと要素を特定するための ID が渡されて、`<form>` の開始タグに適用されています。\n利用できるオプションの全ては [[yii\\widgets\\ActiveForm]] の API ドキュメントに記されていますので参照してください。\n\n### ActiveField <span id=\"activefield\"></span>\nフォームの中では、フォームの要素を作成するために、ActiveForm ウィジェットの [[yii\\widgets\\ActiveForm::field()|ActiveForm::field()]] メソッドが呼ばれています。\nこのメソッドは、フォームの要素だけでなく、そのラベルも作成し、適用できる JavaScript の検証メソッドがあれば、それも追加します。[[yii\\widgets\\ActiveForm::field()|ActiveForm::field()]] メソッドは、\n[[yii\\widgets\\ActiveField]] のインスタンスを返します。このメソッドの呼び出し結果を直接にエコーすると、結果は通常の (text の) インプットになります。\nこのメソッドの呼び出しに追加の [[yii\\widgets\\ActiveField|ActiveField]] のメソッドをチェーンして、出力結果をカスタマイズすることが出来ます。\n\n```php\n// パスワードのインプット\n<?= $form->field($model, 'password')->passwordInput() ?>\n// ヒントとカスタマイズしたラベルを追加\n<?= $form->field($model, 'username')->textInput()->hint('お名前を入力してください')->label('お名前') ?>\n// HTML5 のメール・インプット要素を作成\n<?= $form->field($model, 'email')->input('email') ?>\n```\n\nこれで、フォームのフィールドによって定義された [[yii\\widgets\\ActiveField::$template|テンプレート]] に従って、`<label>`、`<input>` など、全てのタグが生成されます。\nインプット・フィールドの名前は、モデルの [[yii\\base\\Model::formName()|フォーム名]] と属性から自動的に決定されます。\n例えば、上記の例における `username` 属性のインプット・フィールドの名前は `LoginForm[username]` となります。\nこの命名規則の結果として、ログイン・フォームの全ての属性が配列として、サーバ・サイドにおいては `$_POST['LoginForm']` に格納されて利用できることになります。\n\n> Tip: 一つのフォームに一つのモデルだけがある場合、インプットの名前を単純化したいときは、\n> モデルの [[yii\\base\\Model::formName()|formName()]] メソッドをオーバーライドして空文字列を返すようにして、配列の部分をスキップすることが出来ます。\n> この方法を使えば、[GridView](output-data-widgets.md#grid-view) で使われるフィルター・モデルで、もっと見栄えの良い URL を生成させることが出来ます。\n\nモデルの属性を指定するために、もっと洗練された方法を使うことも出来ます。\n例えば、複数のファイルをアップロードしたり、複数の項目を選択したりする場合に、属性の名前に `[]` を付けて、\n属性が配列の値を取り得ることを指定することが出来ます。\n\n```php\n// 複数のファイルのアップロードを許可する\necho $form->field($model, 'uploadFile[]')->fileInput(['multiple'=>'multiple']);\n\n// 複数の項目をチェックすることを許可する\necho $form->field($model, 'items[]')->checkboxList(['a' => 'Item A', 'b' => 'Item B', 'c' => 'Item C']);\n```\n\n送信ボタンなどのフォーム要素に名前をつけるときには注意が必要です。\n[jQuery ドキュメント](https://api.jquery.com/submit/) によれば、衝突を生じさせ得る予約された名前がいくつかあります。\n\n> フォームおよびフォームの子要素は、フォームのプロパティと衝突するインプット名や id、たとえば `submit`、`length`、`method` などを使ってはなりません。\n> 名前の衝突は訳の分らない失敗を生じさせることがあります。\n> 命名規則の完全なリストを知り、この問題についてあなたのマークアップをチェックするためには、[DOMLint](https://kangax.github.io/domlint/) を参照してください。\n\nフォームに HTML タグを追加するためには、素の HTML を使うか、または、上記の例の [[yii\\helpers\\Html::submitButton()|Html::submitButton()]] のように、\n[[yii\\helpers\\Html|Html]] ヘルパ・クラスのメソッドを使うことが出来ます。\n\n\n> Tip: あなたのアプリケーションで Twitter Bootstrap CSS を使っている場合は、[[yii\\widgets\\ActiveForm]] の代りに\n> [[yii\\bootstrap\\ActiveForm]] を使うのが良いでしょう。\n> 後者は前者の拡張であり、bootstrap CSS フレームワークで使用するための追加のスタイルをサポートしています。\n\n\n> Tip: 必須フィールドをアスタリスク付きのスタイルにするために、次の CSS を使うことが出来ます。\n>\n> ```css\n> div.required label.control-label:after {\n>     content: \" *\";\n>     color: red;\n> }\n> ```\n\nリストを作る <span id=\"creating-activeform-lists\"></span>\n--------------------------\n\n三種類のリストがあります:\n* ドロップダウン・リスト\n* ラジオ・リスト\n* チェックボックス・リスト\n\nリストを作るためには、項目の配列を準備しなければなりません。これは、手作業でやることも出来ます。\n\n```php\n$items = [\n    1 => '項目 1', \n    2 => '項目 2'\n]\n```\n\nまたは、DB から取得することも出来ます。\n\n```php\n$items = Category::find()\n        ->select(['label'])\n        ->indexBy('id')\n        ->column();\n```\n\nこのような `$items` が、いろんなリスト・ウィジェットによって処理されるべきものとなります。\nフォームのフィールドの値(および現在アクティブな項目)は、\n`$model` の属性の現在の値に従って自動的に設定されます。\n\n#### ドロップダウン・リストを作る <span id=\"creating-activeform-dropdownlist\"></span>\n\nActiveField の [[\\yii\\widgets\\ActiveField::dropDownList()]] メソッドを使って、ドロップダウン・リストを作ることが出来ます。\n\n```php\n/** @var \\yii\\widgets\\ActiveForm $form */\n\necho $form->field($model, 'category')->dropdownList([\n        1 => '項目 1', \n        2 => '項目 2'\n    ],\n    ['prompt'=>'カテゴリーを選択してください']\n);\n```\n\n#### ラジオ・リストを作る <span id=\"creating-activeform-radioList\"></span>\n\nActiveField の [[\\yii\\widgets\\ActiveField::radioList()]] メソッドを使ってラジオ・リストを作ることが出来ます。\n\n```php\n/** @var \\yii\\widgets\\ActiveForm $form */\n\necho $form->field($model, 'category')->radioList([\n    1 => 'ラジオ 1', \n    2 => 'ラジオ 2'\n]);\n```\n\n#### チェックボックス・リストを作る <span id=\"creating-activeform-checkboxList\"></span>\n\nActiveField の [[\\yii\\widgets\\ActiveField::checkboxList()]] メソッドを使ってチェックボックス・リストを作ることが出来ます。\n\n```php\n/** @var \\yii\\widgets\\ActiveForm $form */\n\necho $form->field($model, 'category')->checkboxList([\n    1 => 'チェックボックス 1', \n    2 => 'チェックボックス 2'\n]);\n```\n\n\nPjax を使う <span id=\"working-with-pjax\"></span>\n-----------\n\n[[yii\\widgets\\Pjax|Pjax]] ウィジェットを使うと、ページ全体をリロードせずに、\nページの一部分だけを更新することが出来ます。\nこれを使うと、送信後にフォームだけを更新して、その中身を入れ替えることが出来ます。\n\n[[yii\\widgets\\Pjax::$formSelector|$formSelector]] を構成すると、どのフォームの送信が pjax を起動するかを指定することが出来ます。\nそれが指定されていない場合は、Pjax に囲まれたコンテントの中にあって\n`data-pjax` 属性を持つすべてのフォームが pjax リクエストを起動することになります。\n\n```php\nuse yii\\widgets\\Pjax;\nuse yii\\widgets\\ActiveForm;\n\nPjax::begin([\n    // Pjax のオプション\n]);\n    $form = ActiveForm::begin([\n        'options' => ['data' => ['pjax' => true]],\n        // ActiveForm の追加のオプション\n    ]);\n\n        // ActiveForm のコンテント\n\n    ActiveForm::end();\nPjax::end();\n```\n> Tip: [[yii\\widgets\\Pjax|Pjax]] ウィジェット内部のリンクに注意してください。\n> と言うのは、リンクに対するレスポンスもウィジェット内部でレンダリングされるからです。\n> これを防ぐためには、`data-pjax=\"0\"` という HTML 属性を使用します。\n\n#### 送信ボタンの値とファイルのアップロード\n\n`jQuery.serializeArray()` については、\n[ファイル](https://github.com/jquery/jquery/issues/2321) および\n[送信ボタンの値](https://github.com/jquery/jquery/issues/2321)\nを扱うときに問題があることが知られています。\nこの問題は解決される見込みがなく、関数自体も HTML5 で導入された `FormData` クラスによって置き換えられるべきものとして、廃止予定となっています。\n\nこのことは、すなわち、ajax または [[yii\\widgets\\Pjax|Pjax]] ウィジェットを使う場合、\nファイルと送信ボタンの値に対する唯一の公式なサポートは、\n`FormData` クラスに対する [ブラウザのサポート](https://developer.mozilla.org/ja/docs/Web/API/FormData#%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E5%AE%9F%E8%A3%85%E7%8A%B6%E6%B3%81)\nに依存しているということを意味します。\n\nさらに読むべき文書 <span id=\"further-reading\"></span>\n------------------\n\n次のセクション [入力を検証する](input-validation.md) は、送信されたフォームデータのサーバ・サイドでの検証と、ajax 検証およびクライアント・サイドでの検証を扱います。\n\nフォームのもっと複雑な使用方法については、以下のセクションを読んで下さい。\n\n- [表形式インプットのデータ収集](input-tabular-input.md) - 同じ種類の複数のモデルのデータを収集する。\n- [複数のモデルのデータを取得する](input-multiple-models.md) - 同じフォームの中で複数の異なるモデルを扱う。\n- [ファイルをアップロードする](input-file-upload.md) - フォームを使ってファイルをアップロードする方法。\n"
  },
  {
    "path": "docs/guide-ja/input-multiple-models.md",
    "content": "複数のモデルのデータを取得する\n==============================\n\n複雑なデータを扱う場合には、複数の異なるモデルを使用してユーザの入力を収集する必要があることがあり得ます。\n例えば、ユーザのログイン情報は `user` テーブルに保存されているけれども、ユーザのプロファイル情報は\n`profile` テーブルに保存されているという場合を考えて見ると、ユーザに関して入力されたデータを `User` モデルと\n`Profile` モデルによって収集しなければならないでしょう。\nYii のモデルとフォームのサポートを使えば、単一のモデルを扱うのとそれほど違いのない方法によってこの問題を解決することが出来ます。\n\n下記において、`User` と `Profile` の二つのモデルのデータを収集することが出来るフォームをどのようにして作成することが\n出来るかを示します。\n\n最初に、ユーザとプロファイルのデータを収集するためのコントローラ・アクションは、次のように書くことが出来ます。\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\base\\Model;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\nuse app\\models\\User;\nuse app\\models\\Profile;\n\nclass UserController extends Controller\n{\n    public function actionUpdate($id)\n    {\n        $user = User::findOne($id);\n        if (!$user) {\n            throw new NotFoundHttpException(\"ユーザが見つかりませんでした。\");\n        }\n        \n        $profile = Profile::findOne($id);\n        \n        if (!$profile) {\n            throw new NotFoundHttpException(\"ユーザのプロファイルがありません。\");\n        }\n        \n        $user->scenario = 'update';\n        $profile->scenario = 'update';\n        \n        if ($user->load(Yii::$app->request->post()) && $profile->load(Yii::$app->request->post())) {\n            $isValid = $user->validate();\n            $isValid = $profile->validate() && $isValid;\n            if ($isValid) {\n                $user->save(false);\n                $profile->save(false);\n                return $this->redirect(['user/view', 'id' => $id]);\n            }\n        }\n        \n        return $this->render('update', [\n            'user' => $user,\n            'profile' => $profile,\n        ]);\n    }\n}\n```\n\nこの `update` アクションでは、最初に、更新の対象になる `$user` と `$profile` のモデルをデータベースからロードします。\n次に [[yii\\base\\Model::load()]] を呼んで、これら二つのモデルにユーザ入力を代入します。\n代入が成功すれば、二つのモデルを検証して保存します。\n&mdash; モデルの中では、ユーザの入力データは既に検証済みであるため、過剰な検証を避けるために `save(false)` を使っていることに注意して下さい。\nそうでない場合は、次の内容を持つ `update` ビューをレンダリングします。\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n$form = ActiveForm::begin([\n    'id' => 'user-update-form',\n    'options' => ['class' => 'form-horizontal'],\n]) ?>\n    <?= $form->field($user, 'username') ?>\n\n    ...other input fields...\n    \n    <?= $form->field($profile, 'website') ?>\n\n    <?= Html::submitButton('更新', ['class' => 'btn btn-primary']) ?>\n<?php ActiveForm::end() ?>\n```\n\nご覧のように、`update` ビューでは、二つのモデル、すなわち `$user` と `$profile` を使ってインプット・フィールドをレンダリングすることになります。\n"
  },
  {
    "path": "docs/guide-ja/input-tabular-input.md",
    "content": "表形式インプットでデータを収集する\n==================================\n\n時として、一つのフォームで同じ種類の複数のモデルを扱わなければならないことがあります。\n例えば、それぞれが「名前-値」の形で保存され、`Setting` [アクティブ・レコード](db-active-record.md)\nモデルとして表される複数の設定項目を扱うフォームです。\nこの種のフォームは「表形式インプット」と呼ばれることもよくあります。\nこれとは対照的な、異なる種類のさまざまなモデルを扱うことについては、[複数のモデルを持つ複雑なフォーム](input-multiple-models.md) のセクションで扱います。\n\n以下に、表形式インプットを Yii で実装する方法を示します。\n\nカバーすべき三つの異なる状況があり、それぞれ少しずつ異なる処理をしなければなりません。\n- 特定の数のデータベース・レコードを更新する\n- 不特定の数の新しいレコードを作成する\n- 一つのページでレコードを更新、作成、および、削除する\n\n前に説明した単一モデルのフォームとは対照的に、モデルの配列を扱うことになります。\nこの配列がビューに渡されて、各モデルのためのインプット・フィールドが表のような形式で表示されます。\nそして、複数のモデルを一度にロードしたり検証したりするために [[yii\\base\\Model]] のヘルパ・メソッドを使用します。\n\n- [[yii\\base\\Model::loadMultiple()|Model::loadMultiple()]] - 送信されたデータをモデルの配列にロードします。\n- [[yii\\base\\Model::validateMultiple()|Model::validateMultiple()]] - モデルの配列を検証します。\n\n### 特定の数のレコードを更新する\n\nコントローラのアクションから始めましょう。\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\base\\Model;\nuse yii\\web\\Controller;\nuse app\\models\\Setting;\n\nclass SettingsController extends Controller\n{\n    // ...\n\n    public function actionUpdate()\n    {\n        $settings = Setting::find()->indexBy('id')->all();\n\n        if ($this->request->isPost) {\n            if (Setting::loadMultiple($settings, $this->request->post()) && Setting::validateMultiple($settings)) {\n            foreach ($settings as $setting) {\n                $setting->save(false);\n            }\n            return $this->redirect('index');\n        }\n        }\n\n        return $this->render('update', ['settings' => $settings]);\n    }\n}\n```\n\n上記のコードでは、データベースからモデルを読み出すときに [[yii\\db\\ActiveQuery::indexBy()|indexBy()]] を使って、\nモデルのプライマリ・キーでインデックスされた配列にデータを投入しています。このインデックスが、後で、\nフォーム・フィールドを特定するために使われます。[[yii\\base\\Model::loadMultiple()|Model::loadMultiple()]] が\nPOST から来るフォーム・データを複数のモデルに代入し、[[yii\\base\\Model::validateMultiple()|Model::validateMultiple()]] が全てのモデルを一度に検証します。\n保存するときには、`validateMultiple()` を使ってモデルの検証を済ませていますので、[[yii\\db\\ActiveRecord::save()|save()]]\nのパラメータに `false` を渡して、二度目の検証を実行しないようにしています。\n\n次に、`update` ビューの中にあるフォームです。\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n$form = ActiveForm::begin();\n\nforeach ($settings as $id => $setting) {\n    echo $form->field($setting, \"[$id]value\")->label($setting->name);\n}\n\necho Html::submitButton('Save');\n\nActiveForm::end();\n```\n\nここで全ての設定項目について、それぞれ、項目名を示すラベルと、項目の値を入れたインプットをレンダリングしています。\nインプットの名前に適切なインデックスを追加することが肝腎です。というのは、`loadMultiple` がそれを見て、どのモデルにどの値を代入するかを決定するからです。\n\n### 不特定の数の新しいレコードを動的に作成する\n\n新しいレコードを作成するのは、モデルのインスタンスを作成する部分を除いて、更新の場合と同じです。\n\n```php\npublic function actionCreate()\n{\n    $settings = [];\n    if ($this->request->isPost) {\n        $count = count($this->request->post($setting->tableName())) - 1;\n        for ($i = 0; $i < $count; $i++) {\n            $settings[$i] = new Setting();\n        }\n        if (Setting::loadMultiple($settings, $this->request->post()) && Setting::validateMultiple($settings)) {\n            foreach ($settings as $setting) {\n                $setting->save(false);\n            }\n            return $this->redirect('index');\n    }\n    }\n    $settings[] = new Setting();\n\n    return $this->render('create', ['settings' => $settings]);\n}\n```\n\nここでは、デフォルトで一個のモデルを含む `$settings` 配列を初期値として作成し、少なくとも一個のテキスト・フィールドが常にビューに表示されるようにしています。\nそして、受信したインプットの行数に合せて、配列にモデルを追加しています。\n\nビューでは JavaScript を使ってインプットの行を動的に追加することが出来ます。\n\n### 更新、作成、削除を一つのページに組み合わせる\n\n> Note: このセクションはまだ執筆中です。\n>\n> まだ内容がありません。\n\n(未定)\n"
  },
  {
    "path": "docs/guide-ja/input-validation.md",
    "content": "入力を検証する\n==============\n\n経験則として言えることは、エンド・ユーザから受信したデータは決して信用せず、\n利用する前に検証しなければならない、ということです。\n\n[モデル](structure-models.md) にユーザの入力が投入されたら、モデルの [[yii\\base\\Model::validate()]] メソッドを呼んで入力を検証することが出来ます。\nこのメソッドは検証が成功したか否かを示す真偽値を返します。\n検証が失敗した場合は、[[yii\\base\\Model::errors]] プロパティからエラー・メッセージを取得することが出来ます。例えば、\n\n```php\n$model = new \\app\\models\\ContactForm();\n\n// モデルの属性にユーザ入力を投入する\n$model->load(\\Yii::$app->request->post());\n// これは次と等価\n// $model->attributes = \\Yii::$app->request->post('ContactForm');\n\nif ($model->validate()) {\n    // 全ての入力が有効\n} else {\n    // 検証が失敗。$errors はエラー・メッセージを含む配列\n    $errors = $model->errors;\n}\n```\n\n\n## 規則を宣言する <span id=\"declaring-rules\"></span>\n\n`validate()` を現実に動作させるためには、検証する予定の属性に対して検証規則を宣言しなければなりません。\n規則は [[yii\\base\\Model::rules()]] メソッドをオーバーライドすることで宣言します。\n次の例は、`ContactForm` モデルに対して検証規則を宣言する方法を示すものです。\n\n```php\npublic function rules()\n{\n    return [\n        // 名前、メール・アドレス、主題、本文が必須項目\n        [['name', 'email', 'subject', 'body'], 'required'],\n\n        // email 属性は有効なメール・アドレスでなければならない\n        ['email', 'email'],\n    ];\n}\n```\n\n[[yii\\base\\Model::rules()|rules()]] メソッドは規則の配列を返すべきものですが、\nその配列の各要素は次の形式の配列でなければなりません。\n\n```php\n[\n    // 必須。この規則によって検証されるべき属性を指定する。\n    // 属性が一つだけの場合は、属性の名前を直接に書いてもよい。\n    // (配列の中に入れずに)\n    ['属性1', '属性2', ...],\n\n    // 必須。この規則のタイプを指定する。\n    // クラス名、バリデータのエイリアス、または、バリデーション・メソッドの名前。\n    'バリデータ',\n\n    // オプション。この規則が適用されるべき一つまたは複数のシナリオを指定する。\n    // 指定しない場合は、この規則が全てのシナリオに適用されることを意味する。\n    // \"except\" オプションを構成して、列挙したシナリオを除く全てのシナリオに\n    // この規則が適用されるべきことを指定してもよい。\n    'on' => ['シナリオ1', 'シナリオ2', ...],\n\n    // オプション。バリデータ・オブジェクトに対する追加の構成情報を指定する。\n    'プロパティ1' => '値1', 'プロパティ2' => '値2', ...\n]\n```\n\n各規則について、最低限、規則がどの属性に適用されるか、そして、規則がどのタイプであるかを指定しなければなりません。\n規則のタイプは、次に挙げる形式のどれか一つを選ぶことが出来ます。\n\n* コア・バリデータのエイリアス。例えば、`required`、`in`、`date`、等々。\n  コア・バリデータの完全なリストは [コア・バリデータ](tutorial-core-validators.md) を参照してください。\n* モデル・クラス内のバリデーション・メソッドの名前、または無名関数。\n  詳細は、[インライン・バリデータ](#inline-validators) の項を参照してください。\n* 完全修飾のバリデータ・クラス名。詳細は [スタンドアロン・バリデータ](#standalone-validators) \n  の項を参照してください。\n\n一つの規則は、一つまたは複数の属性を検証するために使用することが出来ます。そして、一つの属性は、一つまたは複数の規則によって検証され得ます。\n`on` オプションを指定することで、規則を特定の [シナリオ](structure-models.md#scenarios) においてのみ適用することが出来ます。\n`on` オプションを指定しない場合は、規則が全てのシナリオに適用されることになります。\n\n`validate()` メソッドが呼ばれると、次のステップを踏んで検証が実行されます。\n\n1. 現在の [[yii\\base\\Model::scenario|シナリオ]] を使って [[yii\\base\\Model::scenarios()]] から属性のリストを取得し、どの属性が検証されるべきかを決定します。\n   検証されるべき属性が *アクティブな属性* と呼ばれます。\n2. 現在の [[yii\\base\\Model::scenario|シナリオ]] を使って [[yii\\base\\Model::rules()]] から規則のリストを取得し、どの検証規則が使用されるべきかを決定します。\n   使用されるべき規則が *アクティブな規則* と呼ばれます。\n3. 全てのアクティブな規則を一つずつ使って、その規則に関連付けられた全てのアクティブな属性を一つずつ検証します。\n   検証規則はリストに挙げられている順に評価されます。\n\n属性は、上記の検証のステップに従って、`scenarios()` でアクティブな属性であると宣言されており、\nかつ、`rules()` で宣言された一つまたは複数のアクティブな規則と関連付けられている場合に、\nまた、その場合に限って、検証されます。\n\n> Note: 規則に名前を付けると便利です。すなわち、\n>\n> ```php\n> public function rules()\n> {\n>     return [\n>         // ...\n>         'password' => [['password'], 'string', 'max' => 60],\n>     ];\n> }\n> ```\n>\n> これを子のモデルで使うことが出来ます。\n>\n> ```php\n> public function rules()\n> {\n>     $rules = parent::rules();\n>     unset($rules['password']);\n>     return $rules;\n> }\n\n\n### エラー・メッセージをカスタマイズする <span id=\"customizing-error-messages\"></span>\n\nたいていのバリデータはデフォルトのエラー・メッセージを持っていて、属性の検証が失敗した場合にそれを検証の対象であるモデルに追加します。\n例えば、[[yii\\validators\\RequiredValidator|required]] バリデータは、このバリデータを使って `username` 属性を検証したとき、\n規則に合致しない場合は「ユーザ名は空ではいけません。」というエラー・メッセージをモデルに追加します。\n\n規則のエラー・メッセージは、次に示すように、規則を宣言するときに `message`\nプロパティを指定することによってカスタマイズすることが出来ます。\n\n```php\npublic function rules()\n{\n    return [\n        ['username', 'required', 'message' => 'ユーザ名を選んでください。'],\n    ];\n}\n```\n\nバリデータの中には、検証を失敗させたさまざまな原因をより詳しく説明するための追加のエラー・メッセージをサポートしているものがあります。\n例えば、[[yii\\validators\\NumberValidator|number]] バリデータは、検証される値が大きすぎたり小さすぎたりしたときに、\n検証の失敗を説明するために、それぞれ、[[yii\\validators\\NumberValidator::tooBig|tooBig]] および\n[[yii\\validators\\NumberValidator::tooSmall|tooSmall]] のメッセージをサポートしています。\nこれらのエラー・メッセージも、バリデータの他のプロパティと同様、検証規則の中で構成することが出来ます。\n\n\n### 検証のイベント <span id=\"validation-events\"></span>\n\n[[yii\\base\\Model::validate()]] は、呼び出されると、\n検証のプロセスをカスタマイズするためにオーバーライドできる二つのメソッドを呼び出します。\n\n* [[yii\\base\\Model::beforeValidate()]]: デフォルトの実装は [[yii\\base\\Model::EVENT_BEFORE_VALIDATE]] イベントをトリガするものです。\n  このメソッドをオーバーライドするか、または、イベントに反応して、検証が実行される前に、\n  何らかの前処理 (例えば入力されたデータの正規化) をすることが出来ます。\n  このメソッドは、検証を続行すべきか否かを示す真偽値を返さなくてはなりません。\n* [[yii\\base\\Model::afterValidate()]]: デフォルトの実装は [[yii\\base\\Model::EVENT_AFTER_VALIDATE]] イベントをトリガするものです。\n  このメソッドをオーバーライドするか、または、イベントに反応して、検証が完了した後に、\n  何らかの後処理をすることが出来ます。\n\n\n### 条件付きの検証 <span id=\"conditional-validation\"></span>\n\n特定の条件が満たされる場合に限って属性を検証したい場合、例えば、ある属性の検証が他の属性の値に依存する場合には、\n[[yii\\validators\\Validator::when|when]] プロパティを使って、そのような条件を定義することが出来ます。\n例えば、\n\n```php\n    ['state', 'required', 'when' => function($model) {\n        return $model->country == 'USA';\n    }],\n```\n\n[[yii\\validators\\Validator::when|when]] プロパティは、次のシグニチャを持つ PHP コーラブルを値として取ります。\n\n```php\n/**\n * @param Model $model 検証されるモデル\n * @param string $attribute 検証される属性\n * @return bool 規則が適用されるか否か\n */\nfunction ($model, $attribute)\n```\n\nクライアント・サイドでも条件付きの検証をサポートする必要がある場合は、[[yii\\validators\\Validator::whenClient|whenClient]] プロパティを構成しなければなりません。\nこのプロパティは、規則を適用すべきか否かを返す JavaScript 関数を表す文字列を値として取ります。\n例えば、\n\n```php\n    ['state', 'required', 'when' => function ($model) {\n        return $model->country == 'USA';\n    }, 'whenClient' => \"function (attribute, value) {\n        return $('#country').val() == 'USA';\n    }\"]\n```\n\n\n### データのフィルタリング <span id=\"data-filtering\"></span>\n\nユーザ入力をフィルタまたは前処理する必要があることがよくあります。\n例えば、`username` の入力値の前後にある空白を除去したいというような場合です。この目的を達するために検証規則を使うことが出来ます。\n\n次の例では、入力値の前後にある空白を除去して、空の入力値を null に変換することを、\n[trim](tutorial-core-validators.md#trim) および [default](tutorial-core-validators.md#default) のコア・バリデータで行っています。\n\n```php\nreturn [\n    [['username', 'email'], 'trim'],\n    [['username', 'email'], 'default'],\n];\n```\n\nもっと汎用的な [filter](tutorial-core-validators.md#filter) バリデータを使って、\nもっと複雑なデータ・フィルタリングをすることも出来ます。\n\nお分かりのように、これらの検証規則は実際には入力を検証しません。\nそうではなくて、検証される属性の値を処理して書き戻すのです。\n\nユーザ入力の完全な処理を次のサンプル・コードで示します。\nこれは、ある属性に整数の値だけが保存されるように保証しようとするものです。\n\n```php\n['age', 'trim'],\n['age', 'default', 'value' => null],\n['age', 'integer', 'min' => 0],\n['age', 'filter', 'filter' => 'intval', 'skipOnEmpty' => true],\n```\n\n上記のコードは入力に対して以下の操作を実行します。\n\n1. 入力値から先頭と末尾のホワイト・スペースをトリムします。\n2. 空の入力値がデータベースで `null` として保存されることを保証します。\n   \"not set(未設定)\" という値と、実際の値である `0` は区別します。`null` が許されない時は、ここで別のデフォルト値を設定することが出来ます。\n3. 空でない場合は、値は 0 以上の整数であることを検証します。\n   通常のバリデータでは [[yii\\validators\\Validator::$skipOnEmpty|$skipOnEmpty]] が `true` に設定されています。\n4. 例えば、文字列 `'42'` は、整数 `42` にキャストして、値が整数型になることを保証します。\n   デフォルトでは `false` である [[yii\\validators\\FilterValidator|filter]] バリデータの\n   [[yii\\validators\\FilterValidator::$skipOnEmpty|$skipOnEmpty]] を`true` に設定しています。\n\n### 空の入力値を扱う <span id=\"handling-empty-inputs\"></span>\n\nHTML フォームから入力データが送信されたとき、入力値が空である場合には何らかのデフォルト値を割り当てなければならないことがよくあります。\n[default](tutorial-core-validators.md#default) バリデータを使ってそうすることが出来ます。例えば、\n\n```php\nreturn [\n    // 空の時は \"username\" と \"email\" を null にする\n    [['username', 'email'], 'default'],\n\n    // 空の時は \"level\" を 1 にする\n    ['level', 'default', 'value' => 1],\n];\n```\n\nデフォルトでは、入力値が空であると見なされるのは、それが、空文字列であるか、空配列であるか、null であるときです。\n空を検知するこのデフォルトのロジックは、[[yii\\validators\\Validator::isEmpty]] プロパティを PHP コーラブルで構成することによって、\nカスタマイズすることが出来ます。例えば、\n\n```php\n    ['agree', 'required', 'isEmpty' => function ($value) {\n        return empty($value);\n    }]\n```\n\n> Note: たいていのバリデータは、[[yii\\validators\\Validator::skipOnEmpty]] プロパティがデフォルト値 `true` を取っている場合は、\n  空の入力値を処理しません。そのようなバリデータは、関連付けられた属性が空の入力値を受け取ったときは、\n  検証の過程ではスキップされるだけになります。[コア・バリデータ](tutorial-core-validators.md) の中では、\n  `captcha`、`default`、`filter`、`required`、そして `trim` だけが空の入力値を処理します。\n\n\n## その場限りの検証 <span id=\"ad-hoc-validation\"></span>\n\n時として、何らかのモデルに結び付けられていない値に対する *その場限りの検証* を実行しなければならない場合があります。\n\n実行する必要がある検証が一種類 (例えば、メール・アドレスの検証) だけである場合は、\n使いたいバリデータの [[yii\\validators\\Validator::validate()|validate()]] メソッドを次のように呼び出すことが出来ます。\n\n```php\n$email = 'test@example.com';\n$validator = new yii\\validators\\EmailValidator();\n\nif ($validator->validate($email, $error)) {\n    echo 'メール・アドレスは有効。';\n} else {\n    echo $error;\n}\n```\n\n> Note: 全てのバリデータがこの種の検証をサポートしている訳ではありません。\n  その一例が [unique](tutorial-core-validators.md#unique) コア・バリデータであり、これはモデルとともに使用されることだけを前提にして設計されています。\n\n> Note: [[yii\\base\\Validator::skipOnEmpty]] プロパティは [[yii\\base\\Model]] の検証の場合にのみ使用されます。モデル無しで使っても効果はありません。\n\nいくつかの値に対して複数の検証を実行する必要がある場合は、属性と規則の両方をその場で宣言することが出来る [[yii\\base\\DynamicModel]] を使うことが出来ます。\nこれは、次のような使い方をします。\n\n```php\npublic function actionSearch($name, $email)\n{\n    $model = DynamicModel::validateData(['name' => $name, 'email' => $email], [\n        [['name', 'email'], 'string', 'max' => 128],\n        ['email', 'email'],\n    ]);\n\n    if ($model->hasErrors()) {\n        // 検証が失敗\n    } else {\n        // 検証が成功\n    }\n}\n```\n\n[[yii\\base\\DynamicModel::validateData()]] メソッドは `DynamicModel` のインスタンスを作成し、\n与えられた値 (この例では `name` と `email`) を使って属性を定義し、そして、与えられた規則で\n[[yii\\base\\Model::validate()]] を呼び出します。\n\n別の選択肢として、次のように、もっと「クラシック」な構文を使って、その場限りのデータ検証を実行することも出来ます。\n\n```php\npublic function actionSearch($name, $email)\n{\n    $model = new DynamicModel(compact('name', 'email'));\n    $model->addRule(['name', 'email'], 'string', ['max' => 128])\n        ->addRule('email', 'email')\n        ->validate();\n\n    if ($model->hasErrors()) {\n        // 検証が失敗\n    } else {\n        // 検証が成功\n    }\n}\n```\n\n検証を実行した後は、通常のモデルで行うのと同様に、検証が成功したか否かを\n[[yii\\base\\DynamicModel::hasErrors()|hasErrors()]] メソッドを呼んでチェックして、\n[[yii\\base\\DynamicModel::errors|errors]] プロパティから検証エラーを取得することが出来ます。\nまた、このモデルのインスタンスによって定義された動的な属性に対しても、例えば `$model->name` や\n`$model->email` のようにして、アクセスすることが出来ます。\n\n\n## バリデータを作成する <span id=\"creating-validators\"></span>\n\nYii のリリースに含まれている [コア・バリデータ](tutorial-core-validators.md) を使う以外に、あなた自身のバリデータを作成することも出来ます。\nインライン・バリデータとスタンドアロン・バリデータを作ることが出来ます。\n\n\n### インライン・バリデータ <span id=\"inline-validators\"></span>\n\nインライン・バリデータは、モデルのメソッドまたは無名関数として定義されるバリデータです。\nメソッド/関数 のシグニチャは、\n\n```php\n/**\n * @param string $attribute 現在検証されている属性\n * @param mixed $params 規則に与えられる \"params\" の値\n * @param \\yii\\validators\\InlineValidator $validator 関係する InlineValidator のインスタンス。\n * このパラメータは、バージョン 2.0.11 以降で利用可能。\n * @param mixed $current 現在検証されている属性の値\n * このパラメータは、バージョン 2.0.36 以降で利用可能。\n */\nfunction ($attribute, $params, $validator, $current)\n```\n\n属性が検証に失敗した場合は、メソッド/関数 は [[yii\\base\\Model::addError()]] を呼んでエラー・メッセージをモデルに保存し、\n後で読み出してエンド・ユーザに表示することが出来るようにしなければなりません。\n\n下記にいくつかの例を示します。\n\n```php\nuse yii\\base\\Model;\n\nclass MyForm extends Model\n{\n    public $country;\n    public $token;\n\n    public function rules()\n    {\n        return [\n            // モデル・メソッド validateCountry() として定義されるインライン・バリデータ\n            ['country', 'validateCountry'],\n\n            // 無名関数として定義されるインライン・バリデータ\n            ['token', function ($attribute, $params, $validator) {\n                if (!ctype_alnum($this->$attribute)) {\n                    $this->addError($attribute, 'トークンは英数字で構成しなければなりません。');\n                }\n            }],\n        ];\n    }\n\n    public function validateCountry($attribute, $params, $validator)\n    {\n        if (!in_array($this->$attribute, ['USA', 'Indonesia'])) {\n            $this->addError($attribute, '国は \"USA\" または \"Indonesia\" でなければなりません。');\n        }\n    }\n}\n```\n\n> Note: バージョン 2.0.11 以降では、代わりに、[[yii\\validators\\InlineValidator::addError()]] を使ってエラー・メッセージを追加することが出来ます。\n> そうすれば、エラー・メッセージはそのまま [[yii\\i18n\\I18N::format()]] を使ってフォーマットされます。\n> 属性のラベルと値を参照するためには、それぞれ、`{attribute}` と `{value}` を使ってください(手作業で取得する必要はありません)。\n>\n> ```php\n> $validator->addError($this, $attribute, 'The value \"{value}\" is not acceptable for {attribute}.');\n> ```\n\n> Note: デフォルトでは、インライン・バリデータは、関連付けられている属性が空の入力値を受け取ったり、\n> 既に何らかの検証規則に失敗したりしている場合には、適用されません。規則が常に適用されることを保証したい場合は、\n> 規則の宣言において [[yii\\validators\\Validator::skipOnEmpty|skipOnEmpty]] および/または\n> [[yii\\validators\\Validator::skipOnError|skipOnError]] のプロパティを false に設定することが出来ます。例えば、\n>\n> ```php\n> [\n>     ['country', 'validateCountry', 'skipOnEmpty' => false, 'skipOnError' => false],\n> ]\n> ```\n\n\n### スタンドアロン・バリデータ <span id=\"standalone-validators\"></span>\n\nスタンドアロン・バリデータは、[[yii\\validators\\Validator]] またはその子クラスを拡張するクラスです。\n[[yii\\validators\\Validator::validateAttribute()]] メソッドをオーバーライドすることによって、その検証ロジックを実装することが出来ます。\n[インライン・バリデータ](#inline-validators) でするのと同じように、属性が検証に失敗した場合は、\n[[yii\\base\\Model::addError()]] を呼んでエラー・メッセージをモデルに保存します。\n\n\n例えば、上記のインライン・バリデータは、新しい [[components/validators/CountryValidator]] クラスに作りかえることが出来ます。\nこの場合、[[yii\\validators\\Validator::addError()]] を使って特製のメッセージをモデルに設定することが出来ます。\n\n```php\nnamespace app\\components;\n\nuse yii\\validators\\Validator;\n\nclass CountryValidator extends Validator\n{\n    public function validateAttribute($model, $attribute)\n    {\n        if (!in_array($model->$attribute, ['USA', 'Indonesia'])) {\n            $this->addError($model, $attribute, '国は \"{coutry1}\" または \"{coutry2}\" でなければなりません。', ['country1' => 'USA', 'country2' => 'Indonesia']);\n        }\n    }\n}\n```\n\nあなたのバリデータで、モデルを使わない値の検証をサポートしたい場合は、[[yii\\validators\\Validator::validate()]]\nもオーバーライドしなければなりません。または、`validateAttribute()` と `validate()` の代りに、\n[[yii\\validators\\Validator::validateValue()]] をオーバーライドしても構いません。\nと言うのは、前の二つは、デフォルトでは、`validateValue()` を呼び出すことによって実装されているからです。\n\n次の例は、上記のバリデータ・クラスをあなたのモデルの中でどのように使用することが出来るかを示すものです。\n\n```php\nnamespace app\\models;\n\nuse Yii;\nuse yii\\base\\Model;\nuse app\\components\\validators\\CountryValidator;\n\nclass EntryForm extends Model\n{\n    public $name;\n    public $email;\n    public $country;\n\n    public function rules()\n    {\n        return [\n            [['name', 'email'], 'required'],\n            ['country', CountryValidator::class],\n            ['email', 'email'],\n        ];\n    }\n}\n```\n\n\n## 複数の属性の検証 <span id=\"multiple-attributes-validation\"></span>\n\n時として、バリデータが複数の属性に関係する場合があります。次のようなフォームを考えてみてください。\n\n```php\nclass MigrationForm extends \\yii\\base\\Model\n{\n    /**\n     * 成人一人のための最低限の生活費\n     */\n    const MIN_ADULT_FUNDS = 3000;\n    /**\n     * こども一人のための最低限の生活費\n     */\n    const MIN_CHILD_FUNDS = 1500;\n\n    public $personalSalary;   // 給与\n    public $spouseSalary;     // 配偶者の給与\n    public $childrenCount;    // こどもの数\n    public $description;\n\n    public function rules()\n    {\n        return [\n            [['personalSalary', 'description'], 'required'],\n            [['personalSalary', 'spouseSalary'], 'integer', 'min' => self::MIN_ADULT_FUNDS],\n            ['childrenCount', 'integer', 'min' => 0, 'max' => 5],\n            [['spouseSalary', 'childrenCount'], 'default', 'value' => 0],\n            ['description', 'string'],\n        ];\n    }\n}\n```\n\n### バリデータを作成する <span id=\"multiple-attributes-validator\"></span>\n\n家族の収入が子ども達のために十分であるかどうかをチェックする必要があるとしましょう。\nそのためには、`childrenCount` が 1 以上である場合にのみ実行される `validateChildrenFunds` というインライン・バリデータを作れば良いわけです。\n\n検証されるすべての属性 (`['personalSalary', 'spouseSalary', 'childrenCount']`) にこのバリデータをアタッチすることは出来ない、ということに注意してください。\nそのようにすると、同じバリデータが属性ごとに (合計で三回) 走ることになりますが、\n属性のセット全体に対してこのバリデータを一度だけ走らせれば十分なのです。\n\nこれらの属性のどれを使っても構いません (あるいは、もっとも関係が深いと思うものを使ってください)。\n\n```php\n['childrenCount', 'validateChildrenFunds', 'when' => function ($model) {\n    return $model->childrenCount > 0;\n}],\n```\n\n`validateChildrenFunds` の実装は次のようにすることが出来ます。\n\n```php\npublic function validateChildrenFunds($attribute, $params)\n{\n    $totalSalary = $this->personalSalary + $this->spouseSalary;\n    // 配偶者の給与が指定されているときは、成人の最低生活費を倍にする\n    $minAdultFunds = $this->spouseSalary ? self::MIN_ADULT_FUNDS * 2 : self::MIN_ADULT_FUNDS;\n    $childFunds = $totalSalary - $minAdultFunds;\n    if ($childFunds / $this->childrenCount < self::MIN_CHILD_FUNDS) {\n        $this->addError('childrenCount', '子どもの数に対して給与が不足しています。');\n    }\n}\n```\n\nこの検証は属性一つだけに関係するものではないので、`$attribute` のパラメータは無視することが出来ます。\n\n\n### エラー・メッセージを追加する <span id=\"multiple-attributes-errors\"></span>\n\n複数の属性の場合のエラー・メッセージの追加は、フォームをどのように設計するかによって異なってきます。\n\n- もっとも関係が深いとあなたが思うフィールドを選んで、その属性にエラー・メッセージを追加する。\n\n```php\n$this->addError('childrenCount', '子どもの数に対して給与が不足しています。');\n```\n\n- 重要な複数の属性、または、すべての属性を選んで、同じエラー・メッセージを追加する。\n  メッセージを独立した変数に格納してから `addError` に渡せば、コードを DRY に保つことが出来ます。\n\n```php\n$message = '子どもの数に対して給与が不足しています。';\n$this->addError('personalSalary', $message);\n$this->addError('wifeSalary', $message);\n$this->addError('childrenCount', $message);\n```\n\nあるいは、ループを使います。\n\n```php\n$attributes = ['personalSalary', 'wifeSalary', 'childrenCount'];\nforeach ($attributes as $attribute) {\n    $this->addError($attribute, '子どもの数に対して給与が不足しています。');\n}\n```\n\n- (特定の属性に結び付かない) 共通のエラー・メッセージを追加する。\n  その時点では属性の存在はチェックされませんので、存在しない属性の名前、例えば `*` を使ってエラー・メッセージを追加することが出来ます。\n\n```php\n$this->addError('*', '子どもの数に対して給与が不足しています。');\n```\n\n結果として、フォームのフィールドの近くにはこのエラー・メッセージは表示されません。これを表示するためには、ビューにエラー・サマリを含めます。\n\n```php\n<?= $form->errorSummary($model) ?>\n```\n\n> Note: 複数の属性を一度に検証するバリデータを作成する方法が [community cookbook](https://github.com/samdark/yii2-cookbook/blob/master/book/forms-validator-multiple-attributes.md) で分り易く解説されています。.\n\n## クライアント・サイドでの検証 <span id=\"client-side-validation\"></span>\n\nエンド・ユーザが HTML フォームで値を入力する際には、JavaScript に基づくクライアント・サイドでの検証を提供することが望まれます。\nというのは、クライアント・サイドでの検証は、ユーザが入力のエラーを早く見つけることが出来るようにすることによって、より良いユーザ体験を提供するものだからです。\nあなたも、サーバ・サイドでの検証 *に加えて* クライアント・サイドでの検証をサポートするバリデータを使用したり実装したりすることが出来ます。\n\n> Info: クライアント・サイドでの検証は望ましいものですが、不可欠なものではありません。\n  その主たる目的は、ユーザにより良い体験を提供することにあります。\n  エンド・ユーザから来る入力値と同じように、クライアント・サイドでの検証を決して信用してはいけません。\n  この理由により、これまでの項で説明したように、常に [[yii\\base\\Model::validate()]] を呼び出してサーバ・サイドでの検証を実行しなければなりません。\n\n\n### クライアント・サイドでの検証を使う <span id=\"using-client-side-validation\"></span>\n\n多くの [コア・バリデータ](tutorial-core-validators.md) は、そのままで、クライアント・サイドでの検証をサポートしています。\nあなたがする必要があるのは、[[yii\\widgets\\ActiveForm]] を使って HTML フォームを作るということだけです。\n例えば、下の `LoginForm` は二つの規則を宣言しています。\n一つは、[required](tutorial-core-validators.md#required) コア・バリデータを使っていますが、これはクライアント・サイドとサーバ・サイドの両方でサポートされています。\nもう一つは `validatePassword` インライン・バリデータを使っていますが、こちらはサーバ・サイドでのみサポートされています。\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\nuse app\\models\\User;\n\nclass LoginForm extends Model\n{\n    public $username;\n    public $password;\n\n    public function rules()\n    {\n        return [\n            // username と password はともに必須\n            [['username', 'password'], 'required'],\n\n            // password は validatePassword() によって検証される\n            ['password', 'validatePassword'],\n        ];\n    }\n\n    public function validatePassword()\n    {\n        $user = User::findByUsername($this->username);\n\n        if (!$user || !$user->validatePassword($this->password)) {\n            $this->addError('password', 'ユーザ名またはパスワードが違います。');\n        }\n    }\n}\n```\n\n次のコードによって構築される HTML フォームは、`username` と `password` の二つの入力フィールドを含みます。\n何も入力せずにこのフォームを送信すると、何かを入力するように要求するエラー・メッセージが、\nサーバと少しも交信することなく、ただちに表示されることに気付くでしょう。\n\n```php\n<?php $form = yii\\widgets\\ActiveForm::begin(); ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n    <?= Html::submitButton('ログイン') ?>\n<?php yii\\widgets\\ActiveForm::end(); ?>\n```\n\n舞台裏では、[[yii\\widgets\\ActiveForm]] がモデルで宣言されている検証規則を読んで、\nクライアント・サイドの検証をサポートするバリデータのために、適切な JavaScript コードを生成します。\nユーザが入力フィールドの値を変更したりフォームを送信したりすると、クライアント・サイドの検証の JavaScript が起動されます。\n\nクライアント・サイドの検証を完全に無効にしたい場合は、[[yii\\widgets\\ActiveForm::enableClientValidation]]\nプロパティを `false` に設定することが出来ます。\nまた、個々の入力フィールドごとにクライアント・サイドの検証を無効にしたい場合には、\n入力フィールドの [[yii\\widgets\\ActiveField::enableClientValidation]] プロパティを false に設定することが出来ます。\n`eanbleClientValidation` が入力フィールドのレベルとフォームのレベルの両方で構成されている場合は前者が優先されます。\n\n> Info: バージョン 2.0.11 以降、[[yii\\validators\\Validator]] を拡張する全てのバリデータは、クライアント・サイドのオプションを\n> 独立したメソッド - [[yii\\validators\\Validator::getClientOptions()]] から受け取るようになりました。これを使うと、次のことが可能になります。\n>\n> - 独自のクライアント・サイド検証を実装しながら、\n>   サーバ・サイド検証のオプションとの同期はそのまま残す\n> - 特殊な要求に合うように拡張またはカスタマイズする\n>\n> ```php\n> public function getClientOptions($model, $attribute)\n> {\n>     $options = parent::getClientOptions($model, $attribute);\n>     // ここで $options を修正\n>\n>     return $options;\n> }\n> ```\n\n### クライアント・サイドの検証を実装する <span id=\"implementing-client-side-validation\"></span>\n\nクライアント・サイドの検証をサポートするバリデータを作成するためには、\nクライアント・サイドでの検証を実行する JavaScript コードを返す \n[[yii\\validators\\Validator::clientValidateAttribute()]] メソッドを実装しなければなりません。\nその JavaScript の中では、次の事前定義された変数を使用することが出来ます。\n\n- `attribute`: 検証される属性の名前。\n- `value`: 検証される値。\n- `messages`: 属性に対する検証のエラー・メッセージを保持するために使用される配列。\n- `deferred`: Deferred オブジェクトをプッシュして入れることが出来る配列 (次の項で説明します)。\n\n次の例では、入力された値が既存のステータスのデータに含まれる有効なステータス値であるかどうかを検証する `StatusValidator` を作成します。\nこのバリデータは、サーバ・サイドとクライアント・サイドの両方の検証をサポートします。\n\n```php\nnamespace app\\components;\n\nuse yii\\validators\\Validator;\nuse app\\models\\Status;\n\nclass StatusValidator extends Validator\n{\n    public function init()\n    {\n        parent::init();\n        $this->message = '無効なステータスが入力されました。';\n    }\n\n    public function validateAttribute($model, $attribute)\n    {\n        $value = $model->$attribute;\n        if (!Status::find()->where(['id' => $value])->exists()) {\n            $model->addError($attribute, $this->message);\n        }\n    }\n\n    public function clientValidateAttribute($model, $attribute, $view)\n    {\n        $statuses = json_encode(Status::find()->select('id')->asArray()->column());\n        $message = json_encode($this->message, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);\n        return <<<JS\nif ($.inArray(value, $statuses) === -1) {\n    messages.push($message);\n}\nJS;\n    }\n}\n```\n\n> Tip: 上記のコード例の主たる目的は、クライアント・サイドの検証をサポートする方法を説明することにあります。\n> 実際の仕事では、[in](tutorial-core-validators.md#in) コア・バリデータを使って、同じ目的を達することが出来ます。\n> 次のように検証規則を書けばよいのです。\n>\n> ```php\n> [\n>     ['status', 'in', 'range' => Status::find()->select('id')->asArray()->column()],\n> ]\n> ```\n\n> Tip: クライアント・サイドの検証を手動で操作する必要がある場合、すなわち、動的にフィールドを追加したり、何か特殊な UI ロジックを実装する場合は、\n> Yii 2.0 Cookbook の [Working with ActiveForm via JavaScript](https://github.com/samdark/yii2-cookbook/blob/master/book/forms-activeform-js.md)\n> を参照してください。\n\n### Deferred 検証 <span id=\"deferred-validation\"></span>\n\n非同期のクライアント・サイドの検証をサポートする必要がある場合は、[Deferred オブジェクト](https://api.jquery.com/category/deferred-object/) を作成することが出来ます。\n例えば、AJAX によるカスタム検証を実行するために、次のコードを使うことが出来ます。\n\n```php\npublic function clientValidateAttribute($model, $attribute, $view)\n{\n    return <<<JS\n        deferred.push($.get(\"/check\", {value: value}).done(function(data) {\n            if ('' !== data) {\n                messages.push(data);\n            }\n        }));\nJS;\n}\n```\n\n上のコードにおいて、`deferred` は Yii が提供する変数で、Deferred オブジェクトの配列です。\njQuery の `$.get()` メソッドによって作成された Deferred オブジェクトが `deferred` 配列にプッシュされています。\n\nDeferred オブジェクトを明示的に作成して、非同期のコールバックが呼ばれたときに、Deferred オブジェクトの `resolve()` メソッドを呼ぶことも出来ます。\n次の例は、アップロードされる画像ファイルの大きさをクライアント・サイドで検証する方法を示すものです。\n\n```php\npublic function clientValidateAttribute($model, $attribute, $view)\n{\n    return <<<JS\n        var def = $.Deferred();\n        var img = new Image();\n        img.onload = function() {\n            if (this.width > 150) {\n                messages.push('画像の幅が大きすぎます。');\n            }\n            def.resolve();\n        }\n        var reader = new FileReader();\n        reader.onloadend = function() {\n            img.src = reader.result;\n        }\n        reader.readAsDataURL(file);\n\n        deferred.push(def);\nJS;\n}\n```\n\n> Note: 属性が検証された後に、`resolve()` メソッドを呼び出さなければなりません。\n  そうしないと、主たるフォームの検証が完了しません。\n\n簡潔に記述できるように、`deferred` 配列はショートカット・メソッド `add()` を装備しており、このメソッドを使うと、自動的に Deferred オブジェクトを作成して\n`deferred` 配列に追加することが出来ます。このメソッドを使えば、上記の例は次のように簡潔に記すことが出来ます。\n\n```php\npublic function clientValidateAttribute($model, $attribute, $view)\n{\n    return <<<JS\n        deferred.add(function(def) {\n            var img = new Image();\n            img.onload = function() {\n                if (this.width > 150) {\n\t                messages.push('画像の幅が大きすぎます。');\n                }\n                def.resolve();\n            }\n            var reader = new FileReader();\n            reader.onloadend = function() {\n                img.src = reader.result;\n            }\n            reader.readAsDataURL(file);\n        });\nJS;\n}\n```\n\n\n## AJAX 検証 <span id=\"ajax-validation\"></span>\n\n場合によっては、サーバだけが必要な情報を持っているために、サーバ・サイドでしか検証が実行できないことがあります。\n例えば、ユーザ名がユニークであるか否かを検証するためには、サーバ・サイドで user テーブルを調べることが必要になります。\nこのような場合には、AJAX ベースの検証を使うことが出来ます。\nAJAX 検証は、通常のクライアント・サイドでの検証と同じユーザ体験を保ちながら、入力値を検証するためにバックグラウンドで AJAX リクエストを発行します。\n\n単一のインプット・フィールドに対して AJAX 検証を有効にするためには、そのフィールドの\n[[yii\\widgets\\ActiveField::enableAjaxValidation|enableAjaxValidation]] プロパティを true に設定し、フォームに一意の `id` を指定します。\n\n```php\nuse yii\\widgets\\ActiveForm;\n\n$form = ActiveForm::begin([\n    'id' => 'registration-form',\n]);\n\necho $form->field($model, 'username', ['enableAjaxValidation' => true]);\n\n// ...\n\nActiveForm::end();\n```\n\nフォームの全てのインプットに対して AJAX 検証を有効にするためには、フォームのレベルで\n[[yii\\widgets\\ActiveForm::enableAjaxValidation|enableAjaxValidation]] を true に設定します。\n\n```php\n$form = ActiveForm::begin([\n    'id' => 'contact-form',\n    'enableAjaxValidation' => true,\n]);\n```\n\n> Note: `enableAjaxValidation` プロパティがインプット・フィールドのレベルとフォームのレベルの両方で構成された場合は、\n  前者が優先されます。\n\nまた、サーバ・サイドでは、AJAX 検証のリクエストを処理できるように準備しておく必要があります。\nこれは、コントローラのアクションにおいて、次のようなコード断片を使用することで達成できます。\n\n```php\nif (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {\n    Yii::$app->response->format = Response::FORMAT_JSON;\n    return ActiveForm::validate($model);\n}\n```\n\n上記のコードは、現在のリクエストが AJAX であるかどうかをチェックします。\nもし AJAX であるなら、リクエストに応えて検証を実行し、エラーを JSON 形式で返します。\n\n> Info: AJAX 検証を実行するためには、[Deferred 検証](#deferred-validation) を使うことも出来ます。\n  しかし、ここで説明された AJAX 検証の機能の方がより体系化されており、コーディングの労力も少なくて済みます。\n\n`enableClientValidation` と `enableAjaxValidation` が両方とも `true` に設定されているときは、\nクライアント検証が成功した後でだけ AJAX 検証のリクエストが起動されます。\n`validateOnChange`, `validateOnBlur` または `validateOnType` が `true` に設定されている単一のフィールドを検証する場合、\n当該フィールドが単独でクライアント検証を通ったら AJAX リクエストが送信されることに注意して下さい。\n"
  },
  {
    "path": "docs/guide-ja/intro-upgrade-from-v1.md",
    "content": "バージョン 1.1 からアップグレードする\n=====================================\n\nYii フレームワークは 2.0 のために完全に書き直されたため、バージョン 1.1 と 2.0 の間には数多くの違いがあります。\n結果として、バージョン 1.1 からのアップグレードは、マイナー・バージョン間でのアップグレードのような些細な仕事ではなくなりました。\nこのセクションでは、二つのバージョン間の主要な違いを説明します。\n\nもしあなたが以前に Yii 1.1 を使ったことがなければ、このガイドを飛ばして直接に \"[始めよう](start-installation.md)\" に進んでも、問題はありません。\n\nYii 2.0 はこの要約でカバーされているよりも多くの新機能を導入していることに注意してください。\n決定版ガイド全体を通読して全ての新機能について学習することを強く推奨します。\nおそらく、以前は自分自身で開発する必要があったいくつかの機能が、今ではコア・コードの一部になっていることに気付くでしょう。\n\n\nインストール\n------------\n\nYii 2.0 は、事実上の標準的 PHP パッケージ管理ソフトである [Composer](https://getcomposer.org/) を全面的に採用しています。\nコア・フレームワークも、エクステンションも、インストールは Composer を通じて処理されます。\n[Yii をインストールする](start-installation.md) のセクションを参照して、Yii 2.0 をインストールする方法を学習してください。\n新しいエクステンションを作成したい場合、または既存の 1.1 エクステンションを 2.0 互換のエクステンションに作り直したい場合は、ガイドの\n[エクステンションを作成する](structure-extensions.md#creating-extensions) のセクションを参照してください。\n\n\nPHP の必要条件\n--------------\n\nYii 2.0 は PHP 5.4 以上を必要とします。PHP 5.4 は、Yii 1.1 によって必要とされていた PHP 5.2 に比べて、非常に大きく改良されています。\nこの結果として、注意を払うべき言語レベルでの違いが数多くあります。\n以下は PHP に関する主要な変更点の要約です。\n\n- [名前空間](https://www.php.net/manual/ja/language.namespaces.php)。\n- [無名関数](https://www.php.net/manual/ja/functions.anonymous.php)。\n- 配列の短縮構文 `[...要素...]` が `array(...要素...)` の代りに使われています。\n- 短縮形の echo タグ `<?=` がビュー・ファイルに使われています。PHP 5.4 以降は、この形を使っても安全です。\n- [SPL のクラスとインタフェイス](https://www.php.net/manual/ja/book.spl.php)。\n- [遅延静的束縛(Late Static Bindings)](https://www.php.net/manual/ja/language.oop5.late-static-bindings.php)。\n- [日付と時刻](https://www.php.net/manual/ja/book.datetime.php)。\n- [トレイト](https://www.php.net/manual/ja/language.oop5.traits.php)。\n- [国際化(intl)](https://www.php.net/manual/ja/book.intl.php)。\n  Yii 2.0 は国際化の機能をサポートするために `intl` PHP 拡張を利用しています。\n\n\n名前空間\n--------\n\nYii 2.0 での最も顕著な変更は名前空間の使用です。\nほとんど全てのコア・クラスが、例えば、`yii\\web\\Request` のように名前空間に属します。\nクラス名に \"C\" の接頭辞はもう使われません。\n命名のスキームはディレクトリ構造に従うようになりました。\n例えば、`yii\\web\\Request` は、対応するクラス・ファイルが Yii フレームワーク・フォルダの下の `web/Request.php` であることを示します。\n\n(全てのコア・クラスは、Yii のクラス・ローダのおかげで、そのクラス・ファイルを明示的にインクルードせずに使うことが出来ます。)\n\n\nコンポーネントとオブジェクト\n----------------------------\n\nYii 2.0 は、1.1 の `CComponent` クラスを二つのクラス、すなわち、[[yii\\base\\BaseObject]] と [[yii\\base\\Component]] に分割しました。\n[[yii\\base\\BaseObject|BaseObject]] クラスは、ゲッターとセッターを通じて [オブジェクト・プロパティ](concept-properties.md) を定義することを可能にする、軽量な基底クラスです。\n[[yii\\base\\Component|Component]] クラスは [[yii\\base\\BaseObject|BaseObject]] からの拡張であり、\n[イベント](concept-events.md) と [ビヘイビア](concept-behaviors.md) をサポートします。\n\nあなたのクラスがイベントやビヘイビアの機能を必要としない場合は、[[yii\\base\\BaseObject|BaseObject]]\nを基底クラスとして使うことを考慮すべきです。\n基本的なデータ構造を表すクラスに対して、通常、このことが当てはまります。\n\n\nオブジェクトの構成\n------------------\n\n[[yii\\base\\BaseObject|BaseObject]] クラスはオブジェクトを構成するための統一された方法を導入しています。\n[[yii\\base\\BaseObject|BaseObject]] の全ての派生クラスは、コンストラクタが必要な場合には、インスタンスが正しく構成されるように、\nコンストラクタを以下のようにして宣言しなければなりません。\n\n```php\nclass MyClass extends \\yii\\base\\BaseObject\n{\n    public function __construct($param1, $param2, $config = [])\n    {\n        // ... 構成情報が適用される前の初期化処理\n\n        parent::__construct($config);\n    }\n\n    public function init()\n    {\n        parent::init();\n\n        // ... 構成情報が適用された後の初期化処理\n    }\n}\n```\n\n上記のように、コンストラクタは最後のパラメータとして構成情報の配列を取らなければなりません。\n構成情報の配列に含まれる「名前・値」のペアが、コンストラクタの最後でプロパティを構成します。\n[[yii\\base\\BaseObject::init()|init()]] メソッドをオーバーライドして、\n構成情報が適用された後に行うべき初期化処理を行うことが出来ます。\n\nこの規約に従うことによって、構成情報配列を使って新しいオブジェクトを生成して構成することが\n出来るようになります。\n\n```php\n$object = Yii::createObject([\n    'class' => 'MyClass',\n    'property1' => 'abc',\n    'property2' => 'cde',\n], [$param1, $param2]);\n```\n\n構成情報に関する詳細は、[構成情報](concept-configurations.md) のセクションで見ることが出来ます。\n\n\nイベント\n--------\n\nYii 1 では、イベントは `on` メソッド (例えば、`onBeforeSave`) を定義することによって作成されました。Yii 2 では、どのようなイベント名でも使うことが出来るようになりました。\n[[yii\\base\\Component::trigger()|trigger()]] メソッドを呼んでイベントを発生させます。\n\n```php\n$event = new \\yii\\base\\Event;\n$component->trigger($eventName, $event);\n```\n\nイベントにハンドラをアタッチするためには、[[yii\\base\\Component::on()|on()]] メソッドを使います。\n\n```php\n$component->on($eventName, $handler);\n// ハンドラをデタッチするためには、以下のようにします。\n// $component->off($eventName, $handler);\n```\n\nイベントの機能には数多くの改良がなされました。詳細は [イベント](concept-events.md) のセクションを参照してください。\n\n\nパス・エイリアス\n----------------\n\nYii 2.0 は、パス・エイリアスの使用を、ファイル/ディレクトリのパスと URL の両方に広げました。\nまた、Yii 2.0 では、通常のファイル/ディレクトリのパスや URL と区別するために、エイリアス名は `@` という文字で始まることが要求されるようになりました。\n例えば、`@yii` というエイリアスは Yii のインストール・ディレクトリを指します。\nパス・エイリアスは Yii のコア・コードのほとんどの場所でサポートされています。\n例えば [[yii\\caching\\FileCache::cachePath]] はパス・エイリアスと通常のディレクトリ・パスの両方を受け取ることが出来ます。\n\nパス・エイリアスは、また、クラスの名前空間とも密接に関係しています。\nルートの名前空間に対しては、それぞれ、パス・エイリアスを定義することが推奨されます。\nそうすれば、余計な構成をしなくても、Yii のクラス・オートローダを使うことが出来るようになります。\n例えば、`@yii` が Yii のインストール・ディレクトリを指しているので、`yii\\web\\Request` というようなクラスをオートロードすることが出来る訳です。\nサード・パーティのライブラリ、例えば Zend フレームワークなどを使う場合にも、そのフレームワークのインストール・ディレクトリを指す `@Zend` というパス・エイリアスを定義することが出来ます。\n一旦そうしてしまえば、その Zend フレームワークのライブラリ内のどんなクラスでも、Yii からオートロードすることが出来るようになります。\n\nパス・エイリアスに関する詳細は [エイリアス](concept-aliases.md) のセクションを参照してください。\n\n\nビュー\n------\n\nYii 2 のビューについての最も顕著な変更は、ビューの中の `$this` という特殊な変数が現在のコントローラやウィジェットを指すものではなくなった、ということです。\n今や `$this` は 2.0 で新しく導入された概念である *ビュー*・オブジェクトを指します。\n*ビュー*・オブジェクトは [[yii\\web\\View]] という型であり、MVC パターンのビューの部分を表すものです。\nビューにおいてコントローラやウィジェットにアクセスしたい場合は、`$this->context` を使うことが出来ます。\n\nパーシャル・ビューを別のビューの中でレンダリングするためには、`$this->renderPartial()` ではなく、`$this->render()` を使います。さらに、`render` の呼び出しは、2.0 では明示的に echo しなくてはなりません。\nと言うのは、`render()` メソッドは、レンダリング結果を返すものであり、それを直接に表示するものではないからです。例えば、\n\n```php\necho $this->render('_item', ['item' => $item]);\n```\n\nPHP を主たるテンプレート言語として使う以外に、Yii 2.0 は人気のある二つのテンプレート・エンジン、Smarty と Twig に対する正式なサポートを備えています。\nPrado テンプレート・エンジンはもはやサポートされていません。\nこれらのテンプレート・エンジンを使うためには、`view` アプリケーション・コンポーネントを構成して\n[[yii\\base\\View::$renderers|View::$renderers]] プロパティをセットする必要があります。\n詳細は [テンプレート・エンジン](tutorial-template-engines.md) のセクションを参照してください。\n\n\nモデル\n------\n\nYii 2.0 は  1.1 における `CModel` と同様な [[yii\\base\\Model]] を基底モデルとして使います。`CFormModel` というクラスは完全に廃止されました。\nYii 2 では、それの代りに [[yii\\base\\Model]] を拡張して、フォームのモデル・クラスを作成しなければなりません。\n\nYii 2.0 は サポートされるシナリオを宣言するための [[yii\\base\\Model::scenarios()|scenarios()]] という新しいメソッドを導入しました。\nこのメソッドを使って、どのシナリオの下で、ある属性が検証される必要があるか、また、安全とみなされるか否か、などを宣言します。例えば、\n\n```php\npublic function scenarios()\n{\n    return [\n        'backend' => ['email', 'role'],\n        'frontend' => ['email', '!role'],\n    ];\n}\n```\n\n上記では二つのシナリオ、すなわち、`backend` と `frontend` が宣言されています。\n`backend` シナリオでは、`email` と `role` の属性が両方とも安全であり、一括代入が可能です。\n`frontend` シナリオでは、`email` は一括代入が可能ですが、`role` は不可能です。`email` と `role` は、両方とも、規則を使って検証されなければなりません。\n\n[[yii\\base\\Model::rules()|rules()]] メソッドが、Yii 1.1 に引き続き、検証規則を宣言するために使われます。\n[[yii\\base\\Model::scenarios()|scenarios()]] が導入されたことにより、`unsafe` バリデータが無くなったことに注意してください。\n\nほとんどの場合、すなわち、[[yii\\base\\Model::rules()|rules()]] メソッドが存在しうるシナリオを完全に指定しており、\nそして `unsafe` な属性を宣言する必要が無い場合であれば、[[yii\\base\\Model::scenarios()|scenarios()]] をオーバーライドする必要はありません。\n\nモデルについての詳細を学習するためには、[モデル](structure-models.md) のセクションを参照してください。\n\n\nコントローラ\n------------\n\nYii 2.0 は [[yii\\web\\Controller]] を基底のコントローラ・クラスとして使います。これは Yii 1.1 における`CController` と同様なクラスです。\n[[yii\\base\\Action]] がアクション・クラスの基底クラスです。\n\nコントローラに関して、あなたのコードに最も顕著な影響を及ぼす変更点は、\nコントローラのアクションは表示したいコンテントを、エコーするのでなく、返さなければならなくなった、ということです。\n\n```php\npublic function actionView($id)\n{\n    $model = \\app\\models\\Post::findOne($id);\n    if ($model) {\n        return $this->render('view', ['model' => $model]);\n    } else {\n        throw new \\yii\\web\\NotFoundHttpException;\n    }\n}\n```\n\nコントローラに関する詳細については [コントローラ](structure-controllers.md) のセクションを参照してください。\n\n\nウィジェット\n------------\n\nYii 2.0 は [[yii\\base\\Widget]] を基底のウィジェット・クラスとして使用します。これは Yii 1.1 の `CWidget` と同様なクラスです。\n\nいろんな IDE においてフレームワークに対するより良いサポートを得るために、Yii 2.0 はウィジェットを使うための新しい構文を導入しました。\nスタティックなメソッド [[yii\\base\\Widget::begin()|begin()]]、[[yii\\base\\Widget::end()|end()]]、そして [[yii\\base\\Widget::widget()|widget()]] が導入されました。\n以下のようにして使います。\n\n```php\nuse yii\\widgets\\Menu;\nuse yii\\widgets\\ActiveForm;\n\n// 表示するためには結果を \"echo\" しなければならないことに注意\necho Menu::widget(['items' => $items]);\n\n// オブジェクトのプロパティを初期化するための配列を渡す\n$form = ActiveForm::begin([\n    'options' => ['class' => 'form-horizontal'],\n    'fieldConfig' => ['inputOptions' => ['class' => 'input-xlarge']],\n]);\n... ここにフォームの入力フィールド ...\nActiveForm::end();\n```\n\n詳細については [ウィジェット](structure-widgets.md) のセクションを参照してください。\n\n\nテーマ\n------\n\nテーマは 2.0 では完全に違う動き方をします。\nテーマは、ソースのビュー・ファイル・パスをテーマのビュー・ファイル・パスにマップするパス・マッピング機構に基づくものになりました。\n例えば、あるテーマのパス・マップが `['/web/views' => '/web/themes/basic']` である場合、ビュー・ファイル `/web/views/site/index.php` のテーマ版は `/web/themes/basic/site/index.php` になります。\nこの理由により、テーマはどのようなビュー・ファイルに対してでも適用することが出来るようになりました。\nコントローラやウィジェットのコンテキストの外で表示されるビューであっても適用できます。\n\nまた、`CThemeManager` コンポーネントはもうありません。\nその代りに、`theme` は `view` アプリケーション・コンポーネントの構成可能なプロパティになりました。\n\n詳細については [テーマ](output-theming.md) のセクションを参照してください。\n\n\nコンソール・アプリケーション\n----------------------------\n\nコンソール・アプリケーションは、ウェブ・アプリケーションと同じように、コントローラとして編成されるようになりました。\n1.1 における `CConsoleCommand` と同様に、コンソール・コントローラは [[yii\\console\\Controller]] を拡張したものでなければなりません。\n\nコンソール・コマンドを走らせるためには、`yii <route>` という構文を使います。\nここで `<route>` はコントローラのルート (例えば `sitemap/index`) を表します。\n追加の無名の引数は、対応するコントローラのアクション・メソッドに引数として渡されます。\n一方、名前付きの引数は、[[yii\\console\\Controller::options()]] での宣言に従って解析されます。\n\nYii 2.0 はコメント・ブロックからコマンドのヘルプ情報を自動的に生成する機能をサポートしています。\n\n詳細については [コンソール・コマンド](tutorial-console.md) のセクションを参照してください。\n\n\n国際化\n------\n\nYii 2.0 は [PECL intl PHP モジュール](https://pecl.php.net/package/intl) に賛同して、内蔵の日付フォーマッタと数字フォーマッタの部品を取り除きました。\n\nメッセージは `i18n` アプリケーション・コンポーネント経由で翻訳されるようになりました。\nこのコンポーネントは一連のメッセージ・ソースを管理するもので、\nメッセージのカテゴリに基づいて異なるメッセージ・ソースを使うことを可能にするものです。\n\n詳細については [国際化](tutorial-i18n.md) のセクションを参照してください。\n\n\nアクション・フィルタ\n--------------------\n\nアクション・フィルタはビヘイビアによって実装されるようになりました。新しいカスタム・フィルタを定義するためには、[[yii\\base\\ActionFilter]] を拡張します。\nフィルタを使うためには、そのフィルタ・クラスをビヘイビアとしてコントローラにアタッチします。\n例えば、[[yii\\filters\\AccessControl]] フィルタを使うためには、コントローラに次のコードを書くことになります。\n\n```php\npublic function behaviors()\n{\n    return [\n        'access' => [\n            'class' => 'yii\\filters\\AccessControl',\n            'rules' => [\n                ['allow' => true, 'actions' => ['admin'], 'roles' => ['@']],\n            ],\n        ],\n    ];\n}\n```\n\n詳細については [フィルタ](structure-filters.md) のセクションを参照してください。\n\n\nアセット\n--------\n\nYii 2.0 は、*アセット・バンドル* と呼ばれる新しい概念を導入しました。これは、Yii 1.1 にあったスクリプト・パッケージの概念を置き換えるものです。\n\nアセット・バンドルは、あるディレクトリの下に集められた一群のアセット・ファイル (例えば、JavaScript ファイル、CSS ファイル、イメージ・ファイルなど) です。\nそれぞれのアセット・バンドルは [[yii\\web\\AssetBundle]] を拡張したクラスとして表わされます。\nアセット・バンドルを [[yii\\web\\AssetBundle::register()]] を通じて登録することによって、\nそのバンドルに含まれるアセットにウェブ経由でアクセスできるようになります。\nYii 1 とは異なり、バンドルを登録したページは、そのバンドルで指定されている JavaScript と CSS ファイルへの参照を自動的に含むようになります。\n\n詳細については [アセット](structure-assets.md) のセクションを参照してください。\n\n\nヘルパ\n------\n\nYii 2.0 はよく使われるスタティックなヘルパ・クラスを数多く導入しました。それには以下のものが含まれます。\n\n* [[yii\\helpers\\Html]]\n* [[yii\\helpers\\ArrayHelper]]\n* [[yii\\helpers\\StringHelper]]\n* [[yii\\helpers\\FileHelper]]\n* [[yii\\helpers\\Json]]\n\n詳細については、[ヘルパの概要](helper-overview.md) のセクションを参照してください。\n\nフォーム\n--------\n\nYii 2.0 は [[yii\\widgets\\ActiveForm]] を使ってフォームを作成する際に使用する *フィールド* の概念を導入しました。\nフィールドは、ラベル、インプット、エラー・メッセージ および/または ヒント・テキストを含むコンテナです。\nフィールドは [[yii\\widgets\\ActiveField|ActiveField]] のオブジェクトとして表現されます。\nフィールドを使うことによって、以前よりもすっきりとフォームを作成することが出来るようになりました。\n\n```php\n<?php $form = yii\\widgets\\ActiveForm::begin(); ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n    <div class=\"form-group\">\n        <?= Html::submitButton('ログイン') ?>\n    </div>\n<?php yii\\widgets\\ActiveForm::end(); ?>\n```\n\n詳細については [フォームを作成する](input-forms.md) のセクションを参照してください。\n\n\nクエリ・ビルダ\n--------------\n\n1.1 においては、クエリの構築が `CDbCommand`、`CDbCriteria`、`CDbCommandBuilder` など、いくつかのクラスに散らばっていました。\nYii 2.0 は DB クエリを [[yii\\db\\Query|Query]] オブジェクトの形で表現します。\nこのオブジェクトが舞台裏で [[yii\\db\\QueryBuilder|QueryBuilder]] の助けを得て SQL 文に変換されます。\n例えば、\n\n```php\n$query = new \\yii\\db\\Query();\n$query->select('id, name')\n      ->from('user')\n      ->limit(10);\n\n$command = $query->createCommand();\n$sql = $command->sql;\n$rows = $command->queryAll();\n```\n\n何より良いのは、このようなクエリ構築メソッドが [アクティブ・レコード](db-active-record.md) を扱う時にも使える、ということです。\n\n詳細については [クエリ・ビルダ](db-query-builder.md) のセクションを参照してください。\n\n\nアクティブ・レコード\n--------------------\n\nYii 2.0 は [アクティブ・レコード](db-active-record.md) に数多くの変更を導入しました。\n最も顕著な違いは、クエリの構築方法とリレーショナル・クエリの処理の二つです。\n\n1.1 の `CDbCriteria` クラスは Yii 2 では [[yii\\db\\ActiveQuery]] に置き換えられました。このクラスは [[yii\\db\\Query]] を拡張したものであり、従って全てのクエリ構築メソッドを継承します。\n以下のように、[[yii\\db\\ActiveRecord::find()]] を呼んでクエリの構築を開始します。\n\n```php\n// 全てのアクティブな顧客を読み出し、ID によって並べる\n$customers = Customer::find()\n    ->where(['status' => $active])\n    ->orderBy('id')\n    ->all();\n```\n\nリレーションを宣言するために必要なことは、[[yii\\db\\ActiveQuery|ActiveQuery]] オブジェクトを返す getter メソッドを定義するだけのことです。\ngetter によって定義されたプロパティの名前がリレーションの名前を表します。例えば、以下のコードは `orders` リレーションを宣言するものです\n(1.1 では `relations()` という一個の中枢でリレーションを宣言しなければなりませんでした)。\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public function getOrders()\n    {\n        return $this->hasMany('Order', ['customer_id' => 'id']);\n    }\n}\n```\n\nこうすることで、`$customer->orders` という構文によって関連テーブルにある顧客のオーダにアクセスすることが出来るようになります。\nまた、下記のコードを用いて、カスタマイズしたクエリ条件によるリレーショナル・クエリをその場で実行することも出来ます。\n\n```php\n$orders = $customer->getOrders()->andWhere('status=1')->all();\n```\n\nリレーションをイーガー・ロードするとき、Yii 2.0 は 1.1 とは異なる動きをします。\n具体的に言うと、1.1 では JOIN クエリが生成されて、主レコードと関連レコードの両方がセレクトされていました。\nYii 2.0 では、JOIN を使わずに二つの SQL 文が実行されます。\nすなわち、第一の SQL 文が主たるレコードを返し、第二の SQL 文は主レコードのプライマリ・キーを使うフィルタリングによって関連レコードを返します。\n\n多数のレコードを返すクエリを構築するときは、[[yii\\db\\ActiveRecord|ActiveRecord]] オブジェクトを返す代りに、[[yii\\db\\ActiveQuery::asArray()|asArray()]] メソッドをチェインすることが出来ます。\nそうすると、クエリ結果は配列として返されることになり、レコードの数が多い場合は、必要とされる CPU 時間とメモリを著しく削減することが出来ます。\n例えば、\n\n```php\n$customers = Customer::find()->asArray()->all();\n```\n\nもう一つの変更点は、属性のデフォルト値を public なプロパティによって定義することは出来なくなった、ということです。\nデフォルト値を定義する必要がある場合は、アクティブ・レコード・クラスの `init` メソッドの中で設定しなければなりません。\n\n```php\npublic function init()\n{\n    parent::init();\n    $this->status = self::STATUS_NEW;\n}\n```\n\n1.1 では、アクティブ・レコード・クラスのコンストラクタをオーバーライドすることについて、いくつか問題がありました。バージョン 2.0 では、もう問題はありません。\nコンストラクタにパラメータを追加する場合は、[[yii\\db\\ActiveRecord::instantiate()]] をオーバーライドする必要があるかもしれないことに注意してください。\n\nアクティブ・レコードについては、他にも多くの変更と機能強化がなされています。\n詳細については [アクティブ・レコード](db-active-record.md) のセクションを参照してください。\n\n\nアクティブ・レコードのビヘイビア\n--------------------------------\n\n2.0 では基底のビヘイビア・クラス `CActiveRecordBehavior` を廃止しました。\nアクティブ・レコードのビヘイビアを作成したいときは、直接に `yii\\base\\Behavior` を拡張しなければなりません。\nビヘイビア・クラスがオーナーの何らかのイベントに反応する必要がある場合は、以下のように `events()` メソッドをオーバーライドしなければなりません。\n\n```php\nnamespace app\\components;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\base\\Behavior;\n\nclass MyBehavior extends Behavior\n{\n    // ...\n\n    public function events()\n    {\n        return [\n            ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',\n        ];\n    }\n\n    public function beforeValidate($event)\n    {\n        // ...\n    }\n}\n```\n\n\nUser と IdentityInterface\n-------------------------\n\n1.1 の `CWebUser` クラスは [[yii\\web\\User]] に取って換られました。\nそして `CUserIdentity` クラスはもうありません。代りに、使い方がもっと単純な [[yii\\web\\IdentityInterface]] を実装しなければなりません。\nアドバンスト・プロジェクト・テンプレートがそういう例を提供しています。\n\n詳細は [認証](security-authentication.md)、[権限付与](security-authorization.md)、そして [アドバンスト・プロジェクト・テンプレート](https://www.yiiframework.com/extension/yiisoft/yii2-app-advanced/doc/guide) のセクションを参照してください。\n\n\nURL 管理\n--------\n\nYii 2 の URL 管理は 1.1 のそれと似たようなものです。\n主な機能強化は、URL 管理がオプションのパラメータをサポートするようになったことです。\n例えば、下記のような規則を宣言した場合に、`post/popular` と `post/1/popular` の両方に合致するようになります。\n1.1 では、同じ目的を達成するためには、二つの規則を使う必要がありました。\n\n```php\n[\n    'pattern' => 'post/<page:\\d+>/<tag>',\n    'route' => 'post/index',\n    'defaults' => ['page' => 1],\n]\n```\n\n詳細については [ルーティングと URL 生成](runtime-routing.md) のセクションを参照してください。\n\nルートの命名規約における重要な変更は、コントローラとアクションのキャメル・ケースの名前が\n各単語をハイフンで分けた小文字の名前になるようになった、という点です。\n例えば、`CamelCaseController` のコントローラ ID は `camel-case` となります。\n詳細については、[コントローラ ID](structure-controllers.md#controller-ids) と [アクション ID](structure-controllers.md#action-ids) のセクションを参照してください。\n\n\nYii 1.1 と 2.x を一緒に使う\n---------------------------\n\nYii 2.0 と一緒に使いたい Yii 1.1 のレガシー・コードを持っている場合は、\n[Yii 1.1 と 2.0 を一緒に使う](tutorial-yii-integration.md#using-both-yii2-yii) のセクションを参照してください。\n\n"
  },
  {
    "path": "docs/guide-ja/intro-yii.md",
    "content": "Yii とは何か\n============\n\nYii は現代的なウェブ・アプリケーションを迅速に開発するための、高性能な、コンポーネント・ベースの PHP フレームワークです。\nYii という名前 (`イー` すなわち `[ji:]` と発音します) は、中国語では「易」であり、「シンプルかつ進化的」であることを意味します。\nまた **Yes It Is** のアクロニム (頭字語) であると考えることも出来ます。\n\n\nYii は何に適しているか\n----------------------\n\nYii は汎用的なウェブ・プログラミング・フレームワークです。\nつまり、あらゆる種類のウェブ・アプリケーションを PHP を使って開発するときに、Yii を使用することが出来ます。\nコンポーネント・ベースのアーキテクチャと洗練されたキャッシュ・サポートを有しているため、Yii は大規模なアプリケーション、\nたとえば、ポータル、フォーラム、コンテント・マネージメント・システム (CMS)、電子商取引プロジェクト、RESTful ウェブ・サービス、等々を開発するのに特に適しています。\n\n\nYii を他のフレームワークと比べると\n----------------------------------\n\nあなたが既に他のフレームワークに親しんでいる場合は、Yii を比較するとどうなのかを知りたいでしょう。\n\n- ほとんどの PHP フレームワーク同様、Yii は MVC (Model-View-Controller) アーキテクチャ・パターンを実装し、\n  このパターンに基づいたコードの編成を推進しています。\n- Yii は、コードはシンプルかつエレガントに書かれるべきである、という哲学を採用しています。\n  何らかのデザイン・パターンの厳密な遵守を主目的とする凝りすぎた設計は、Yii が決して試みようとしないものです。\n- Yii はフル装備のフレームワークです。\n  クエリ・ビルダ、リレーショナル・データベースと NoSQL データベース双方のためのアクティブ・レコード、RESTful API 開発サポート、多層構成のキャッシュ・サポート、\n  等々、検証済みで直ちに使える多数の機能を提供します。\n- Yii は極めて拡張性の高いフレームワークです。あなたはコアのコードのほとんど全ての要素をカスタマイズしたり置き換えたりすることが出来ます。\n  また、Yii の堅固なエクステンション・アーキテクチャを利用して、再配布可能なエクステンションを使用したり開発したりすることも出来ます。\n- 高性能であることは常に Yii の主たる目標です。\n\nYii はワンマン・ショーではありません。Yii は [強力なコア開発チーム](https://www.yiiframework.com/team/) および\nYii 開発に間断なく貢献してくれるプロフェッショナルの大きなコミュニティーに支えられたプロジェクトです。\nYii 開発チームは、最新のウェブ開発の潮流と、他のフレームワークやプロジェクトに見出される最善のプラクティスと機能を、注意深く見守り続けています。\n他のところで見出された最善のプラクティスと機能で最も適切なものは、定期的にコア・フレームワークに組み込まれ、\nシンプルかつエレガントなインタフェイスを通じて公開されます。\n\n\nYii のバージョン\n----------------\n\nYii は現在、利用可能な二つのメジャー・バージョン、すなわち 1.1 と 2.0 を持っています。バージョン 1.1 は古い世代のもので、現在はメンテナンス・モードにあります。\nバージョン 2.0 は、最新のテクノロジーとプロトコル、例えば、Composer、PSR、名前空間、トレイトなどを採用して、Yii を完全に書き直したものです。\nバージョン 2.0 がこのフレームワークの現世代を表すものであり、今後数年間にわたって主要な開発努力の対象となるものです。\nこのガイドは主としてバージョン 2.0 について述べます。\n\n\n必要条件と前提条件\n------------------\n\nYii 2.0 は PHP 7.4.0 以上を必要とし、PHP の最新バージョンで最高の力を発揮します。\n個々の機能に対する詳細な必要条件は、全ての Yii リリースに含まれている必要条件チェッカを走らせることによって知ることが出来ます。\n\nYii を使うためには、オブジェクト指向プログラミング (OOP) の基本的な知識が必要です。\nなぜなら、Yii は純粋な OOP ベースのフレームワークだからです。\nまた、Yii 2.0 は [名前空間](https://www.php.net/manual/ja/language.namespaces.php) や [トレイト](https://www.php.net/manual/ja/language.oop5.traits.php) のような PHP の最新の機能を利用しています。\nこれらの概念を理解することは、Yii 2.0 を採用することを一層容易にするでしょう。\n\n"
  },
  {
    "path": "docs/guide-ja/output-client-scripts.md",
    "content": "クライアント・スクリプトを扱う\n==============================\n\n今日のウェブ・アプリケーションでは、静的な HTML ページがレンダリングされてブラウザに送信されるだけでなく、\nJavaScript によって、既存の要素を操作したり、新しいコンテントを AJAX でロードしたりして、\nブラウザに表示されるページを修正します。\nこのセクションでは、JavaScript と CSS をウェブ・サイトに追加したり、\nそれらを動的に調整するために Yii によって提供されているメソッドを説明します。\n\n## スクリプトを登録する <span id=\"register-scripts\"></span>\n\n[[yii\\web\\View]] オブジェクトを扱う際には、フロントエンド・スクリプトを動的に登録することが出来ます。\nこのための専用のメソッドが二つあります。\n\n- インライン・スクリプトのための [[yii\\web\\View::registerJs()|registerJs()]]\n- 外部スクリプトのための [[yii\\web\\View::registerJsFile()|registerJsFile()]]\n\n### インライン・スクリプトを登録する <span id=\"inline-scripts\"></span>\n\nインライン・スクリプトは、設定のためのコード、動的に生成されるコード、および、[ウィジェット](structure-widgets.md) に含まれる再利用可能なフロントエンド・コードが生成する\nコード断片などです。インライン・スクリプトを追加するためのメソッド [[yii\\web\\View::registerJs()|registerJs()]] は、次のようにして使うことが出来ます。\n\n```php\n$this->registerJs(\n    \"$('#myButton').on('click', function() { alert('ボタンがクリックされました'); });\",\n    View::POS_READY,\n    'my-button-handler'\n);\n```\n\n最初の引数は、ページに挿入したい実際の JS コードです。これが `<script>` タグに包まれて挿入されます。\n二番目の引数は、スクリプトがページのどの位置に挿入されるべきかを決定します。\n取りうる値は以下のとおりです。\n\n- [[yii\\web\\View::POS_HEAD|View::POS_HEAD]] - head セクション。\n- [[yii\\web\\View::POS_BEGIN|View::POS_BEGIN]] - 開始の `<body>` の直後。\n- [[yii\\web\\View::POS_END|View::POS_END]] - 終了の `</body>` の直前。\n- [[yii\\web\\View::POS_READY|View::POS_READY]] - [ドキュメントの `ready` イベント](https://learn.jquery.com/using-jquery-core/document-ready/) でコードを実行するための指定。\n  これを指定すると、[[yii\\web\\JqueryAsset|jQuery]] が自動的に登録され、コードは適切な jQuery コードの中に包まれます。これがデフォルトの位置指定です。\n- [[yii\\web\\View::POS_LOAD|View::POS_LOAD]] - [ドキュメントの `load` イベント](https://learn.jquery.com/using-jquery-core/document-ready/) でコードを実行するための指定。\n  上記と同じく、これを指定すると、[[yii\\web\\JqueryAsset|jQuery]] が自動的に登録されます。\n\n最後の引数は、スクリプトのコード・ブロックを一意に特定するために使われるスクリプトのユニークな ID です。同じ ID のスクリプトが既にある場合は、新しいものを追加するのでなく、それを置き換えます。\nID を指定しない場合は、JS コードそれ自身が ID として扱われます。この ID によって、同じコードが複数回登録されるのを防止します。\n\n### スクリプト・ファイルを登録する <span id=\"script-files\"></span>\n\n[[yii\\web\\View::registerJsFile()|registerJsFile()]] の引数は、\n[[yii\\web\\View::registerCssFile()|registerCssFile()]] の引数と同様なものです。\n以下に示す例では、`main.js` ファイルを、[[yii\\web\\JqueryAsset]] への依存関係とともに、登録します。\nこれは、`main.js` ファイルは `jquery.js` の後に追加される、ということを意味します。\nこのような依存関係の仕様が無ければ、`main.js` と `jquery.js` の間の相対的な順序は未定義となり、コードは動作しなくなるでしょう。\n\n外部スクリプトは次のようにして追加することが出来ます。\n\n```php\n$this->registerJsFile(\n    '@web/js/main.js',\n    ['depends' => [\\yii\\web\\JqueryAsset::class]]\n);\n```\n\nこれによって、アプリケーションの [base URL](concept-aliases.md#predefined-aliases) の下に配置されている `/js/main.js` スクリプトを読み込むタグが追加されます。\n\nただし、外部 JS ファイルを登録するのには、 [[yii\\web\\View::registerJsFile()|registerJsFile()]] を使わずに、[アセット・バンドル](structure-assets.md) を使うことが強く推奨されます。\nなぜなら、そうする方が、柔軟性も高く、依存関係の構成も粒度を細かく出来るからです。また、アセット・バンドルを使えば、複数の JS ファイルを結合して圧縮すること (アクセスの多いウェブ・サイトではそうすることが望まれます) が可能になります。\n\n## CSS を登録する <span id=\"register-css\"></span>\n\nJavascript と同様に、[[yii\\web\\View::registerCss()|registerCss()]]\nまたは [[yii\\web\\View::registerCssFile()|registerCssFile()]]\nを使って CSS を登録することが出来ます。\n前者は CSS のコードブロックを登録し、後者は外部 CSS ファイルを登録するものです。\n\n### インライン CSS を登録する <span id=\"inline-css\"></span>\n\n```php\n$this->registerCss(\"body { background: #f00; }\");\n```\n\n上記のコードによって、結果として、下記の出力がページの `<head>` セクションに追加されます。\n\n```html\n<style>\nbody { background: #f00; }\n</style>\n```\n\n`style` タグに追加の属性を指定したい場合は、名前-値 の配列を二番目の引数として渡します。最後の引数は、スタイルのブロックを一意に特定するために使われるユニークな ID です。\n同じスタイルがコードの別の箇所で重複して登録されたとしても、このスタイルのブロックが一度だけ追加されることを保証するものです。\n\n### CSS ファイルを登録する <span id=\"css-files\"></span>\n\nCSS ファイルは次のようにして登録することが出来ます。\n\n```php\n$this->registerCssFile(\"@web/css/themes/black-and-white.css\", [\n    'depends' => [\\yii\\bootstrap\\BootstrapAsset::class],\n    'media' => 'print',\n], 'css-print-theme');\n```\n\n上記のコードは `/css/themes/black-and-white.css` という CSS ファイルに対するリンクをページの `<head>` セクションに追加します。\n\n* 最初の引数が、登録される CSS ファイルを指定します。\n  この例における `@web` in this example is an [アプリケーションのベース URL に対するエイリアス](concept-aliases.md#predefined-aliases) です。\n* 二番目の引数は、結果として出力される `<link>` タグの HTML 属性を指定するものです。\n  ただし、`depends` というオプションは特別な処理を受けます。これは、この CSS ファイルが依存するアセット・バンドルを指定するものです。\n  この例の場合は、[[yii\\bootstrap\\BootstrapAsset|BootstrapAsset]] が依存するアセット・バンドルです。\n  これは、この CSS ファイルが [[yii\\bootstrap\\BootstrapAsset|BootstrapAsset]] に属する CSS ファイルの*後に*追加されることを意味します。\n* 最後の引数はこの CSS ファイルを特定する ID を指定するものです。\n  省略された場合は、CSS ファイルの URL が代りに ID として使用されます。\n\n外部 CSS ファイルを登録するのには、 [[yii\\web\\View::registerCssFile()|registerCssFile()]] を使わずに、\n[アセット・バンドル](structure-assets.md) を使うことが強く推奨されます。アセット・バンドルを使えば、複数の CSS ファイルを結合して圧縮すること\n(アクセスの多いウェブ・サイトではそうすることが望まれます) が可能になります。\nまた、アプリケーションの全てのアセットの依存関係を一ヶ所で構成することが出来るため、より大きな柔軟性を得ることが出来ます。\n\n\n## アセット・バンドルを登録する <span id=\"asset-bundles\"></span>\n\n既に述べたように、CSS ファイルと JavaScript ファイルを直接に登録する代りにアセット・バンドルを使うことが推奨されます。\nアセット・バンドルを定義する方法の詳細は、ガイドの [アセット](structure-assets.md) のセクションで知ることが出来ます。\n既に定義されているアセット・バンドルの使い方は、\n次のように非常に単純明快です。\n\n```php\n\\frontend\\assets\\AppAsset::register($this);\n```\n\n上記のコードでは、ビュー・ファイルのコンテキストにおいて、`AppAsset` バンドルが (`$this` で表される) 現在のビューに対して登録されています。\nウィジェットの中からアセット・バンドルを登録するときは、ウィジェットの [[yii\\base\\Widget::$view|$view]]\nを代りに渡します (`$this->view`)。\n\n\n## 動的な Javascript を生成する <span id=\"dynamic-js\"></span>\n\nビュー・ファイルでは、HTML コードが直接に書き出されのではなく、ビューの変数に依存して、PHP のコードによって生成されることがよくあります。\n生成された HTML を Javascript によって操作するためには、JS コードも同様に動的な部分を含まなければなりません。\n例えば、jQuery セレクタの ID などがそうです。\n\nPHP の変数を JS コードに挿入するためには、変数の値を適切にエスケープする必要があります。\nJS コードを専用の JS ファイルの中に置くのではなく、HTML に挿入する場合は特にそうです。\nYii は、この目的のために、[[yii\\helpers\\Json|Json]] ヘルパの [[yii\\helpers\\Json::htmlEncode()|htmlEncode()]] メソッドを提供しています。\nその使用方法は、以下の例の中で示されています。\n\n### グローバルな JavaScript の構成情報を登録する <span id=\"js-configuration\"></span>\n\nこの例では、配列を使って、グローバルな構成情報のパラメータをアプリケーションの\nPHP のパートから JS のフロントエンド・コードに渡します。\n\n```php\n$options = [\n    'appName' => Yii::$app->name,\n    'baseUrl' => Yii::$app->request->baseUrl,\n    'language' => Yii::$app->language,\n    // ...\n];\n$this->registerJs(\n    \"var yiiOptions = \".\\yii\\helpers\\Json::htmlEncode($options).\";\",\n    View::POS_HEAD,\n    'yiiOptions'\n);\n```\n\n上記のコードは、次のような JavaScript の変数定義を含む `<script>` タグを登録します。\n例えば、\n\n```javascript\nvar yiiOptions = {\"appName\":\"My Yii Application\",\"baseUrl\":\"/basic/web\",\"language\":\"en\"};\n```\n\nこのようにすれば、あなたの Javascript コードで、これらの構成情報に `yiiOptions.baseUrl` や `yiiOptions.language` のようにしてアクセスすることが出来るようになります。.\n\n### 翻訳されたメッセージを渡す <span id=\"translated-messages\"></span>\n\nあなたの JavaScript が何らかのイベントに反応してメッセージを表示する必要がある、という状況に遭遇するかも知れません。\n複数の言語で動作するアプリケーションでは、この文字列は、現在のアプリケーシの言語に翻訳されなければなりません。\nこれを達成する一つの方法は、Yii によって提供されている [メッセージ翻訳機能] (tutorial-i18n.md#message-translation) を使って、その結果を JavaScript コードに渡すことです。\n\n```php\n$message = \\yii\\helpers\\Json::htmlEncode(\n    \\Yii::t('app', 'Button clicked!')\n);\n$this->registerJs(<<<JS\n    $('#myButton').on('click', function() { alert( $message ); });\nJS\n);\n```\n\n上記のサンプル・コードは、可読性を高めるために、PHP の [ヒアドキュメント構文](https://www.php.net/manual/ja/language.types.string.php#language.types.string.syntax.heredoc) を使っています。\nまた、ヒアドキュメントは、たいていの IDE で、より良い構文ハイライトが可能になるので、\nインライン JavaScript、特に一行に収まらないものを書くときに推奨される方法です。\n変数 `$message` は PHP で生成され、[[yii\\helpers\\Json::htmlEncode|Json::htmlEncode]] のおかげで、適切な JS 構文の文字列を含むものになります。\nそれを JavaScript コードに挿入して、`alert()` の関数呼び出しに動的な文字列を渡すことが出来ます。\n\n> Note: ヒアドキュメントを使う場合は、JS コード中の変数名に注意してください。\n> `$` で始まる変数は、PHP の変数として解釈され、\n> その値によって置き換えられる可能性があります。\n> ただし、`$(` または `$.` という形式の jQuery 関数は\n> PHP 変数として解釈される心配は無く、安全に使うことが出来ます。\n\n## `yii.js` スクリプト <span id=\"yii.js\"></span>\n\n> Note: このセクションはまだ書かれていません。このセクションは、`yii.js` によって提供される以下の機能についての説明を含むはずのものです。\n> \n> - Yii JavaScript モジュール\n> - CSRF パラメータの処理\n> - `data-confirm` ハンドラ\n> - `data-method` ハンドラ\n> - スクリプトのフィルタリング\n> - リダイレクトの処理\n\n"
  },
  {
    "path": "docs/guide-ja/output-data-providers.md",
    "content": "データ・プロバイダ\n================\n\n[ページネーション](output-pagination.md) と [並べ替え](output-sorting.md) のセクションにおいて\n、エンド・ユーザが特定のページのデータを選んで表示し、いずれかのカラムによってデータを並べ替えることが出来るようにする方法を説明しました。\nデータのページネーションと並べ替えは非常によくあるタスクですから、Yii はこれをカプセル化した一連の *データ・プロバイダ* を提供しています。\n\nデータ・プロバイダは [[yii\\data\\DataProviderInterface]] を実装するクラスであり、主として、ページ分割され並べ替えられたデータの取得をサポートするものです。\n通常は、[データ・ウィジェット](output-data-widgets.md) と共に使用して、\nエンド・ユーザが対話的にデータのページネーションと並べ替えをすることが出来るようにします。\n\nYii のリリースには次のデータ・プロバイダのクラスが含まれています。\n\n* [[yii\\data\\ActiveDataProvider]]: [[yii\\db\\Query]] または [[yii\\db\\ActiveQuery]] を使ってデータベースからデータを取得して、\n  配列または [アクティブ・レコード](db-active-record.md)・インスタンスの形式でデータを返します。\n* [[yii\\data\\SqlDataProvider]]: SQL 文を実行して、データベースのデータを配列として返します。\n* [[yii\\data\\ArrayDataProvider]]: 大きな配列を受け取り、ページネーションと並べ替えの指定に基づいて、\n  一部分を切り出して返します。\n\nこれら全てのデータ・プロバイダの使用方法は、次の共通のパターンを持っています。\n\n```php\n// ページネーションと並べ替えのプロパティを構成してデータ・プロバイダを作成する\n$provider = new XyzDataProvider([\n    'pagination' => [...],\n    'sort' => [...],\n]);\n\n// ページ分割されて並べ替えられたデータを取得する\n$models = $provider->getModels();\n\n// 現在のページにあるデータ・アイテムの数を取得する\n$count = $provider->getCount();\n\n// 全ページ分のデータ・アイテムの総数を取得する\n$totalCount = $provider->getTotalCount();\n```\n\nデータ・プロバイダのページネーションと並べ替えの振る舞いを指定するためには、その [[yii\\data\\BaseDataProvider::pagination|pagination]]\nと [[yii\\data\\BaseDataProvider::sort|sort]] のプロパティを構成します。\n二つのプロパティは、それぞれ、[[yii\\data\\Pagination]] と [[yii\\data\\Sort]] の構成情報に対応します。\nこれらを `false` に設定して、ページネーションや並べ替えの機能を無効にすることも出来ます。\n\n[データ・ウィジェット](output-data-widgets.md)、例えば [[yii\\grid\\GridView]] は、`dataProvider` という名前のプロパティを持っており、\nこれにデータ・プロバイダのインスタンスを受け取らせて、それが提供するデータを表示させることが出来ます。例えば、\n\n```php\necho yii\\grid\\GridView::widget([\n    'dataProvider' => $dataProvider,\n]);\n```\n\nこれらのデータ・プロバイダの主たる相異点は、データソースがどのように指定されるかという点にあります。\n次に続く項において、各データ・プロバイダの詳細な使用方法を説明します。\n\n\n## アクティブ・データ・プロバイダ <span id=\"active-data-provider\"></span> \n\n[[yii\\data\\ActiveDataProvider]] を使用するためには、その [[yii\\data\\ActiveDataProvider::query|query]] プロパティを構成しなければなりません。\nこれは、[[yii\\db\\Query]] または [[yii\\db\\ActiveQuery]] のオブジェクトを取ることが出来ます。\n前者であれば、返されるデータは配列になります。後者であれば、返されるデータは配列または [アクティブ・レコード](db-active-record.md)\nインスタンスとすることが出来ます。例えば、\n\n```php\nuse yii\\data\\ActiveDataProvider;\n\n$query = Post::find()->where(['status' => 1]);\n\n$provider = new ActiveDataProvider([\n    'query' => $query,\n    'pagination' => [\n        'pageSize' => 10,\n    ],\n    'sort' => [\n        'defaultOrder' => [\n            'created_at' => SORT_DESC,\n            'title' => SORT_ASC, \n        ]\n    ],\n]);\n\n// Post オブジェクトの配列を返す\n$posts = $provider->getModels();\n```\n\n上記の例における `$query` が次のコードによって作成される場合は、提供されるデータは生の配列になります。\n\n```php\nuse yii\\db\\Query;\n\n$query = (new Query())->from('post')->where(['status' => 1]); \n```\n\n> Note: クエリが既に `orderBy` 句を指定しているものである場合、(`sort` の構成を通して) エンド・ユーザによって与えられる並べ替えの指定は、\n  既存の `orderBy` 句に追加されます。一方、`limit` と `offset` の句が存在している場合は、\n  (`pagenation` の構成を通して) エンド・ユーザによって指定されるページネーションのリクエストによって上書きされます。\n\nデフォルトでは、[[yii\\data\\ActiveDataProvider]] はデータベース接続として `db` アプリケーション・コンポーネントを使用します。\n[[yii\\data\\ActiveDataProvider::db]] プロパティを構成すれば、別のデータベース接続を使用することが出来ます。\n\n\n## SQL データ・プロバイダ <span id=\"sql-data-provider\"></span>\n\n[[yii\\data\\SqlDataProvider]] は、生の SQL 文を使用して、必要なデータを取得します。\nこのデータ・プロバイダは、[[yii\\data\\SqlDataProvider::sort|sort]] と [[yii\\data\\SqlDataProvider::pagination|pagination]]\nの指定に基づいて、SQL 文の `ORDER BY` と `OFFSET/LIMIT` の句を修正し、\n指定された順序に並べ替えられたデータを要求されたページの分だけ取得します。\n\n[[yii\\data\\SqlDataProvider]] を使用するためには、[[yii\\data\\SqlDataProvider::sql|sql]] プロパティだけでなく、\n[[yii\\data\\SqlDataProvider::totalCount|totalCount]] プロパティを指定しなければなりません。例えば、\n\n```php\nuse yii\\data\\SqlDataProvider;\n\n$count = Yii::$app->db->createCommand('\n    SELECT COUNT(*) FROM post WHERE status=:status\n', [':status' => 1])->queryScalar();\n\n$provider = new SqlDataProvider([\n    'sql' => 'SELECT * FROM post WHERE status=:status',\n    'params' => [':status' => 1],\n    'totalCount' => $count,\n    'pagination' => [\n        'pageSize' => 10,\n    ],\n    'sort' => [\n        'attributes' => [\n            'title',\n            'view_count',\n            'created_at',\n        ],\n    ],\n]);\n\n// データ行の配列を返す\n$models = $provider->getModels();\n```\n\n> Info: [[yii\\data\\SqlDataProvider::totalCount|totalCount]] プロパティは、データにページネーションを適用しなければならない\n  場合にだけ要求されます。これは、[[yii\\data\\SqlDataProvider::sql|sql]] によって指定される SQL 文は、\n  現在要求されているページのデータだけを返すように、データ・プロバイダによって修正されてしまうからです。\n  データ・プロバイダは、総ページ数を正しく計算するためには、データ・アイテムの総数を知る必要があります。\n\n\n## 配列データ・プロバイダ <span id=\"array-data-provider\"></span>\n\n[[yii\\data\\ArrayDataProvider]] は、一つの大きな配列を扱う場合に最も適しています。\nこのデータ・プロバイダによって、一つまたは複数のカラムで並べ替えた配列データの 1 ページ分を返すことが出来ます。\n[[yii\\data\\ArrayDataProvider]] を使用するためには、全体の大きな配列として\n[[yii\\data\\ArrayDataProvider::allModels|allModels]] プロパティを指定しなければなりません。\nこの大きな配列の要素は、連想配列 (例えば [DAO](db-dao.md) のクエリ結果) またはオブジェクト\n(例えば [アクティブ・レコード](db-active-record.md) インスタンス) とすることが出来ます。例えば、\n\n```php\nuse yii\\data\\ArrayDataProvider;\n\n$data = [\n    ['id' => 1, 'name' => 'name 1', ...],\n    ['id' => 2, 'name' => 'name 2', ...],\n    ...\n    ['id' => 100, 'name' => 'name 100', ...],\n];\n\n$provider = new ArrayDataProvider([\n    'allModels' => $data,\n    'pagination' => [\n        'pageSize' => 10,\n    ],\n    'sort' => [\n        'attributes' => ['id', 'name'],\n    ],\n]);\n\n// 現在リクエストされているページの行を返す\n$rows = $provider->getModels();\n``` \n\n> Note: [アクティブ・データ・プロバイダ](#active-data-provider) および [SQL データ・プロバイダ](#sql-data-provider) と比較すると、\n  配列データ・プロバイダは効率の面では劣ります。何故なら、*全ての* データをメモリにロードしなければならないからです。\n\n\n## データのキーを扱う <span id=\"working-with-keys\"></span>\n\nデータ・プロバイダによって返されたデータ・アイテムを使用する場合、各データ・アイテムを一意のキーで\n特定しなければならないことがよくあります。例えば、データ・アイテムが顧客情報を表す場合、顧客 ID\nを各顧客データのキーとして使用したいでしょう。データ・プロバイダは、[[yii\\data\\DataProviderInterface::getModels()]]\nによって返されたデータ・アイテムに対応するそのようなキーのリストを返すことが出来ます。例えば、\n\n```php\nuse yii\\data\\ActiveDataProvider;\n\n$query = Post::find()->where(['status' => 1]);\n\n$provider = new ActiveDataProvider([\n    'query' => $query,\n]);\n\n// Post オブジェクトの配列を返す\n$posts = $provider->getModels();\n\n// $post に対応するプライマリ・キーの値を返す\n$ids = $provider->getKeys();\n```\n\n上記の例では、[[yii\\data\\ActiveDataProvider]] に対して [[yii\\db\\ActiveQuery]] オブジェクトを供給していますから、\nキーとしてプライマリ・キーの値を返すのが理にかなっています。キーの値の計算方法を明示的に指定するために、\n[[yii\\data\\ActiveDataProvider::key]] にカラム名を設定したり、キーの値を計算するコーラブルを設定したりすることも出来ます。\n例えば、\n\n```php\n// \"slug\" カラムをキーの値として使用する\n$provider = new ActiveDataProvider([\n    'query' => Post::find(),\n    'key' => 'slug',\n]);\n\n// md5(id) の結果をキーの値として使用する\n$provider = new ActiveDataProvider([\n    'query' => Post::find(),\n    'key' => function ($model) {\n        return md5($model->id);\n    }\n]);\n```\n\n\n## カスタム・データ・プロバイダを作成する <span id=\"custom-data-provider\"></span>\n\nあなた自身のカスタム・データ・プロバイダ・クラスを作成するためには、[[yii\\data\\DataProviderInterface]] を実装しなければなりません。\n[[yii\\data\\BaseDataProvider]] を拡張するのが比較的簡単な方法です。そうすれば、データ・プロバイダのコアのロジックに集中することが出来ます。\n具体的に言えば、実装する必要があるのは、主として次のメソッドです。\n                                                   \n- [[yii\\data\\BaseDataProvider::prepareModels()|prepareModels()]]: 現在のページで利用できるデータ・モデルを準備して、\n  それを配列として返します。\n- [[yii\\data\\BaseDataProvider::prepareKeys()|prepareKeys()]]: 現在利用できるデータ・モデルの配列を受け取って、\n  それと関連付けられるキーの配列を返します。\n- [[yii\\data\\BaseDataProvider::prepareTotalCount()|prepareTotalCount]]: データ・プロバイダにある\n  データ・モデルの総数を示す値を返します。\n\n下記は、CSV ファイルを効率的に読み出すデータ・プロバイダのサンプルです。\n\n```php\n<?php\nuse yii\\data\\BaseDataProvider;\n\nclass CsvDataProvider extends BaseDataProvider\n{\n    /**\n     * @var string 読み出す CSV ファイルの名前\n     */\n    public $filename;\n    \n    /**\n     * @var string|callable キーカラムの名前またはそれを返すコーラブル\n     */\n    public $key;\n    \n    /**\n     * @var SplFileObject\n     */\n    protected $fileObject; // ファイルの特定の行までシークするのに SplFileObject が非常に便利\n    \n \n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        \n        // ファイルを開く\n        $this->fileObject = new SplFileObject($this->filename);\n    }\n \n    /**\n     * {@inheritdoc}\n     */\n    protected function prepareModels()\n    {\n        $models = [];\n        $pagination = $this->getPagination();\n \n        if ($pagination === false) {\n            // ページネーションが無い場合、全ての行を読む\n            while (!$this->fileObject->eof()) {\n                $models[] = $this->fileObject->fgetcsv();\n                $this->fileObject->next();\n            }\n        } else {\n            // ページネーションがある場合、一つのページだけを読む\n            $pagination->totalCount = $this->getTotalCount();\n            $this->fileObject->seek($pagination->getOffset());\n            $limit = $pagination->getLimit();\n \n            for ($count = 0; $count < $limit; ++$count) {\n                $models[] = $this->fileObject->fgetcsv();\n                $this->fileObject->next();\n            }\n        }\n \n        return $models;\n    }\n \n    /**\n     * {@inheritdoc}\n     */\n    protected function prepareKeys($models)\n    {\n        if ($this->key !== null) {\n            $keys = [];\n \n            foreach ($models as $model) {\n                if (is_string($this->key)) {\n                    $keys[] = $model[$this->key];\n                } else {\n                    $keys[] = call_user_func($this->key, $model);\n                }\n            }\n \n            return $keys;\n        } else {\n            return array_keys($models);\n        }\n    }\n \n    /**\n     * {@inheritdoc}\n     */\n    protected function prepareTotalCount()\n    {\n        $count = 0;\n \n        while (!$this->fileObject->eof()) {\n            $this->fileObject->next();\n            ++$count;\n        }\n \n        return $count;\n    }\n}\n```\n\n## データ・フィルタを使ってデータ・プロバイダをフィルタリングする <span id=\"filtering-data-providers-using-data-filters\"></span>\n\n[データをフィルタリングする](output-data-widgets.md#filtering-data) や [独立したフィルタ・フォーム](output-data-widgets.md#separate-filter-form) で述べられているように、\nアクティブ・データ・プロバイダの条件を手作業で構築することも可能ですが、\n柔軟なフィルタ条件を必要とする場合には、Yii が持っているデータ・フィルタが非常に役に立ちます。\nデータ・フィルタは次のようにして使うことが出来ます。\n\n```php\n$filter = new ActiveDataFilter([\n    'searchModel' => 'app\\models\\PostSearch'\n]);\n\n$filterCondition = null;\n\n// どのようなソースからでもフィルタをロードすることが出来ます。例えば、\n// リクエスト・ボディの JSON からロードしたい場合は、\n// 下記のように Yii::$app->request->getBodyParams() を使います。\nif ($filter->load(\\Yii::$app->request->get())) { \n    $filterCondition = $filter->build();\n    if ($filterCondition === false) {\n        // シリアライザがフィルタの抽出でエラーを出すかもしれない\n        return $filter;\n    }\n}\n\n$query = Post::find();\nif ($filterCondition !== null) {\n    $query->andWhere($filterCondition);\n}\n\nreturn new ActiveDataProvider([\n    'query' => $query,\n]);\n```\n\n`PostSearch` モデルは、どういうプロパティと値がフィルタリングに使用できるかを定義するという目的のために使用されています。\n\n```php\nuse yii\\base\\Model;\n\nclass PostSearch extends Model \n{\n    public $id;\n    public $title;\n    \n    public function rules()\n    {\n        return [\n            ['id', 'integer'],\n            ['title', 'string', 'min' => 2, 'max' => 200],            \n        ];\n    }\n}\n```\n\nデータ・フィルタは極めて柔軟で、どのようにして条件が構築されるか、また、どんな演算子が許容されるかをカスタマイズすることが可能です。\n詳細は API リファレンスで [[\\yii\\data\\DataFilter]] を参照して下さい。\n"
  },
  {
    "path": "docs/guide-ja/output-data-widgets.md",
    "content": "データ・ウィジェット\n====================\n\nYii はデータを表示するために使うことが出来る一連の [ウィジェット](structure-widgets.md) を提供しています。\n[DetailView](#detail-view) は、単一のレコードのデータを表示するのに使うことが出来ます。\nそれに対して、[ListView](#list-view) と [GridView](#grid-view) は、複数のデータ・レコードをリストまたはテーブルで表示することが出来るもので、\nページネーション、並べ替え、フィルタリングなどの機能を提供するものです。\n\n\nDetailView <span id=\"detail-view\"></span>\n----------\n\nDetailView は単一のデータ [[yii\\widgets\\DetailView::$model|モデル]] の詳細を表示します。\n\nモデルを標準的な書式で表示する場合 (例えば、全てのモデル属性をそれぞれテーブルの一行として表示する場合) に最も適しています。\nモデルは [[\\yii\\base\\Model]] またはそのサブ・クラス、例えば [アクティブ・レコード](db-active-record.md) のインスタンスか、連想配列かのどちらかにすることが出来ます。\n \nDetailView は [[yii\\widgets\\DetailView::$attributes]] プロパティを使って、モデルのどの属性が表示されるべきか、また、どういうフォーマットで表示されるべきかを決定します。\n利用できるフォーマットのオプションについては、[フォーマッタのセクション](output-formatting.md) を参照してください。\n\n次に DetailView の典型的な用例を示します。\n\n```php\necho DetailView::widget([\n    'model' => $model,\n    'attributes' => [\n        'title',                                           // title 属性 (平文テキストで)\n        'description:html',                                // description 属性は HTML としてフォーマットされる\n        [                                                  // モデルの所有者の名前\n            'label' => '所有者',\n            'value' => $model->owner->name,\n            'contentOptions' => ['class' => 'bg-red'],     // 値のタグをカスタマイズする HTML 属性\n            'captionOptions' => ['tooltip' => 'Tooltip'],  // ラベルのタグをカスタマイズする HTML 属性\n        ],\n        'created_at:datetime',                             // 作成日時は datetime としてフォーマットされる\n    ],\n]);\n```\n\n[[yii\\widgets\\GridView|GridView]] が一組のモデルを処理するのとは異なって、\n[[yii\\widgets\\DetailView|DetailView]] は一つのモデルしか処理しないということを覚えておいてください。\n表示すべきモデルはビューの変数としてアクセスできる `$model` 一つだけですから、たいていの場合、クロージャを使用する必要はありません。\n\nしかし、クロージャが役に立つ場合もあります。例えば、`visible` が指定されており、それが `false` と評価される場合には\n`value` の計算を避けたい場合です。\n\n```php\necho DetailView::widget([\n    'model' => $model,\n    'attributes' => [\n        [\n            'attribute' => 'owner',\n            'value' => function ($model) {\n                return $model->owner->name;\n            },\n            'visible' => \\Yii::$app->user->can('posts.owner.view'),\n        ],\n    ],\n]);\n```\n\nListView <span id=\"list-view\"></span>\n--------\n\n[[yii\\widgets\\ListView|ListView]] ウィジェットは、[データ・プロバイダ](output-data-providers.md) からのデータを表示するのに使用されます。\n各データ・モデルは指定された [[yii\\widgets\\ListView::$itemView|ビュー・ファイル]] を使って表示されます。\nListView は、特に何もしなくても、ページネーション、並べ替え、フィルタリングなどの機能を提供してくれますので、\nエンド・ユーザに情報を表示するためにも、データ管理 UI を作成するためにも、非常に便利なウィジェットです。\n\n典型的な使用方法は以下の通りです。\n\n```php\nuse yii\\widgets\\ListView;\nuse yii\\data\\ActiveDataProvider;\n\n$dataProvider = new ActiveDataProvider([\n    'query' => Post::find(),\n    'pagination' => [\n        'pageSize' => 20,\n    ],\n]);\necho ListView::widget([\n    'dataProvider' => $dataProvider,\n    'itemView' => '_post',\n]);\n```\n\n`_post` ビューは次のような内容を含むことが出来ます。\n\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\helpers\\HtmlPurifier;\n?>\n<div class=\"post\">\n    <h2><?= Html::encode($model->title) ?></h2>\n\n    <?= HtmlPurifier::process($model->text) ?>    \n</div>\n```\n\n上記のビュー・ファイルでは、現在のデータ・モデルを `$model` としてアクセスすることが出来ます。追加で次のものを利用することも出来ます。\n\n- `$key`: mixed - データ・アイテムと関連付けられたキーの値。\n- `$index`: integer - データ・プロバイダによって返されるアイテムの配列の 0 から始まるインデックス。\n- `$widget`: ListView - ウィジェットのインスタンス。\n\n追加のデータを各ビューに渡す必要がある場合は、次のように、[[yii\\widgets\\ListView::$viewParams|$viewParams]]\nを使って「キー・値」のペアを渡すことが出来ます。\n\n```php\necho ListView::widget([\n    'dataProvider' => $dataProvider,\n    'itemView' => '_post',\n    'viewParams' => [\n        'fullView' => true,\n        'context' => 'main-page',\n        // ...\n    ],\n]);\n```\n\nこのようにすると、これらをビューで変数として利用できるようになります。\n\n\nGridView <span id=\"grid-view\"></span>\n--------\n\nデータ・グリッドすなわち [[yii\\grid\\GridView|GridView]] は Yii の最も強力なウィジェットの一つです。これは、システムの管理セクションを素速く作らねばならない時に、\nこの上なく便利なものです。このウィジェットは [データ・プロバイダ](output-data-providers.md) からデータを受けて、\nテーブルの形式で、行ごとに一組の [[yii\\grid\\GridView::columns|カラム]] を使ってデータを表示します。\n\nテーブルの各行が一つのデータ・アイテムを表します。そして、一つのカラムは通常はアイテムの一属性を表します\n(カラムの中に、複数の属性を組み合わせた複雑な式に対応するものや、静的なテキストを表すものを含めることも出来ます)。\n\nGridView を使うために必要な最小限のコードは次のようなものです。\n\n```php\nuse yii\\grid\\GridView;\nuse yii\\data\\ActiveDataProvider;\n\n$dataProvider = new ActiveDataProvider([\n    'query' => Post::find(),\n    'pagination' => [\n        'pageSize' => 20,\n    ],\n]);\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n]);\n```\n\n上記のコードは、最初にデータ・プロバイダを作成し、次に GridView を使って、データ・プロバイダから受け取る全ての行の全ての属性を表示するものです。\n表示されるテーブルには、特に何も設定しなくても、並べ替えとページネーションの機能が装備されます。\n\n\n### グリッドのカラム <span id=\"grid-columns\"></span>\n\nグリッドのテーブルのカラムは [[yii\\grid\\Column]] クラスとして表現され、GridView の構成情報の\n[[yii\\grid\\GridView::columns|columns]] プロパティで構成されます。カラムは、タイプや設定の違いに応じて、\nデータをさまざまな形で表現することが出来ます。デフォルトのクラスは [[yii\\grid\\DataColumn]] です。\nこれは、モデルの一つの属性を表現し、その属性による並べ替えとフィルタリングを可能にするものです。\n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'columns' => [\n        ['class' => 'yii\\grid\\SerialColumn'],\n        // $dataProvider に含まれるデータによって定義される単純なカラム\n        // モデルのカラムのデータが使われる\n        'id',\n        'username',\n        // 複雑なカラム定義\n        [\n            'class' => 'yii\\grid\\DataColumn', // 省略可。これがデフォルト値。\n            'value' => function ($data) {\n                return $data->name; // 配列データの場合は $data['name']。例えば、SqlDataProvider を使う場合。\n            },\n        ],\n    ],\n]);\n```\n\n構成情報の [[yii\\grid\\GridView::columns|columns]] の部分が指定されない場合は、Yii は、\nデータ・プロバイダのモデルの表示可能な全てのカラムを表示しようとすることに注意してください。\n\n\n### カラム・クラス <span id=\"column-classes\"></span>\n\nグリッドのカラムは、いろいろなカラム・クラスを使うことでカスタマイズすることが出来ます。\n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'columns' => [\n        [\n            'class' => 'yii\\grid\\SerialColumn', // <-- ここ\n            // ここで追加のプロパティを構成することが出来ます\n        ],\n```\n\nYii によって提供されるカラム・クラスを以下で見ていきますが、それらに加えて、あなた自身のカラム・クラスを作成することも出来ます。\n\n全てのカラム・クラスは [[yii\\grid\\Column]] を拡張するものですので、グリッドのカラムを構成するときに設定できる\n共通のオプションがいくつかあります。\n\n- [[yii\\grid\\Column::header|header]] によって、ヘッダ行のコンテントを設定することが出来ます。\n- [[yii\\grid\\Column::footer|footer]] によって、フッタ行のコンテントを設定することが出来ます。\n- [[yii\\grid\\Column::visible|visible]] はカラムの可視性を定義します。\n- [[yii\\grid\\Column::content|content]] によって、行のデータを返す有効な PHP コールバックを渡すことが出来ます。書式は以下の通りです。\n\n  ```php\n  function ($model, $key, $index, $column) {\n      return '文字列';\n  }\n  ```\n\n下記のオプションに配列を渡して、コンテナ要素のさまざまな HTML オプションを指定することが出来ます。\n\n- [[yii\\grid\\Column::headerOptions|headerOptions]]\n- [[yii\\grid\\Column::footerOptions|footerOptions]]\n- [[yii\\grid\\Column::filterOptions|filterOptions]]\n- [[yii\\grid\\Column::contentOptions|contentOptions]]\n\n\n#### データ・カラム <span id=\"data-column\"></span>\n\n[[yii\\grid\\DataColumn|データ・カラム]] は、データの表示と並べ替えに使用されます。\nこれがデフォルトのカラムタイプですので、これを使用するときはクラスの指定を省略することが出来ます。\n\nデータ・カラムの主要な設定項目は、その [[yii\\grid\\DataColumn::format|format]] プロパティです。\nその値が、デフォルトでは [[\\yii\\i18n\\Formatter|Formatter]] である `formatter` [アプリケーション・コンポーネント](structure-application-components.md) のメソッドに対応します。\n\n```php\necho GridView::widget([\n    'columns' => [\n        [\n            'attribute' => 'name',\n            'format' => 'text'\n        ],\n        [\n            'attribute' => 'birthday',\n            'format' => ['date', 'php:Y-m-d']\n        ],\n        'created_at:datetime', // shortcut format\n        [\n            'label' => '教育',\n            'attribute' => 'education',\n            'filter' => ['0' => '初等教育', '1' => '中等教育', '2' => '高等教育'],\n            'filterInputOptions' => ['prompt' => '全ての教育', 'class' => 'form-control', 'id' => null]\n        ],\n    ],\n]);\n```\n\n上記において、`text` は [[\\yii\\i18n\\Formatter::asText()]] に対応し、カラムの値が最初の引数として渡されます。\n二番目のカラムの定義では、`date` が [[\\yii\\i18n\\Formatter::asDate()]] に対応します。\nカラムの値が、ここでも、最初の引数として渡され、'php:Y-m-d' が二番目の引数の値として渡されます。\n\n利用できるフォーマッタの一覧については、[データのフォーマット](output-formatting.md) のセクションを参照してください。\n\nデータカラムを構成するためには、ショートカット形式を使うことも出来ます。\nそれについては、[[yii\\grid\\GridView::columns|columns]] の API ドキュメントで説明されています。\n\nフィルタ・インプットの HTML を制御するためには、[[yii\\grid\\DataColumn::filter|filter]] と\n[[yii\\grid\\DataColumn::filterInputOptions|filterInputOptions]] を使用して下さい。\n\nデフォルトでは、カラム・ヘッダは [[yii\\data\\Sort::link]] によってレンダリングされますが、[[yii\\grid\\Column::header]] を使って調整することが出来ます。\nヘッダのテキストを変更するには、上の例のように、[[yii\\grid\\DataColumn::$label]] を設定しなければなりません。\nデフォルトでは、ラベルはデータ・モデルによって設定されます。詳細は [[yii\\grid\\DataColumn::getHeaderCellLabel]] を参照して下さい。\n\n#### アクション・カラム <span id=\"action-column\"></span>\n\n[[yii\\grid\\ActionColumn|アクション・カラム]] は、各行について、更新や削除などのアクション・ボタンを表示します。\n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'columns' => [\n        [\n            'class' => 'yii\\grid\\ActionColumn',\n            // ここで追加のプロパティを構成することが出来ます\n        ],\n```\n\n構成が可能なプロパティは、以下の通りです。\n\n- [[yii\\grid\\ActionColumn::controller|controller]] は、アクションを処理すべきコントローラの ID です。\n  設定されていない場合は、現在アクティブなコントローラが使われます。\n- [[yii\\grid\\ActionColumn::template|template]] は、アクション・カラムの各セルを構成するのに使用されるテンプレートを定義します。\n  波括弧に囲まれたトークンは、コントローラのアクション ID として扱われます (アクション・カラムのコンテキストでは *ボタンの名前* とも呼ばれます)。\n  これらは、[[yii\\grid\\ActionColumn::$buttons|buttons]] によって定義される、対応するボタン表示コールバックによって置き換えられます。\n  例えば、`{view}` というトークンは、`buttons['view']` のコールバックの結果によって置き換えられます。\n  コールバックが見つからない場合は、トークンは空文字列によって置き換えられます。デフォルトのテンプレートは `{view} {update} {delete}` です。\n- [[yii\\grid\\ActionColumn::buttons|buttons]] はボタン表示コールバックの配列です。\n  配列のキーはボタンの名前 (波括弧を除く) であり、値は対応するボタン表示コールバックです。コールバックは下記のシグニチャを使わなければなりません。\n\n  ```php\n  function ($url, $model, $key) {\n      // ボタンの HTML コードを返す\n  }\n  ```\n\n  上記のコードで、`$url` はカラムがボタンのために生成する URL、`$model` は現在の行に表示されるモデル・オブジェクト、\n  そして `$key` はデータ・プロバイダの配列の中にあるモデルのキーです。\n\n- [[yii\\grid\\ActionColumn::urlCreator|urlCreator]] は、指定されたモデルの情報を使って、ボタンの URL を生成するコールバックです。\n  コールバックのシグニチャは [[yii\\grid\\ActionColumn::createUrl()]] のそれと同じでなければなりません。\n  このプロパティが設定されていないときは、ボタンの URL は [[yii\\grid\\ActionColumn::createUrl()]] を使って生成されます。\n- [[yii\\grid\\ActionColumn::visibleButtons|visibleButtons]] は、各ボタンの可視性の条件を定義する配列です。\n  配列のキーはボタンの名前 (波括弧を除く) であり、値は真偽値 `true`/`false` または無名関数です。\n  ボタンの名前がこの配列の中で指定されていない場合は、デフォルトで、ボタンが表示されます。\n  コールバックは次のシグニチャを使わなければなりません。\n\n  ```php\n  function ($model, $key, $index) {\n      return $model->status === 'editable';\n  }\n  ```\n\n  または、真偽値を渡すことも出来ます。\n\n  ```php\n  [\n      'update' => \\Yii::$app->user->can('update')\n  ]\n  ```\n\n#### チェックボックス・カラム <span id=\"checkbox-column\"></span>\n\n[[yii\\grid\\CheckboxColumn|チェックボックス・カラム]] はチェックボックスのカラムを表示します。\n\nGridView に CheckboxColumn を追加するためには、以下のようにして、[[yii\\grid\\GridView::$columns|columns]] 構成情報にカラムを追加します。\n\n```php\necho GridView::widget([\n    'id' => 'grid',\n    'dataProvider' => $dataProvider,\n    'columns' => [\n        // ...\n        [\n            'class' => 'yii\\grid\\CheckboxColumn',\n            // ここで追加のプロパティを構成することが出来ます\n        ],\n    ],\n```\n\nユーザはチェックボックスをクリックして、グリッドの行を選択することが出来ます。\n選択された行は、次の JavaScript コードを呼んで取得することが出来ます。\n\n```javascript\nvar keys = $('#grid').yiiGridView('getSelectedRows');\n// keys は選択された行と関連付けられたキーの配列\n```\n\n#### シリアル・カラム <span id=\"serial-column\"></span>\n\n[[yii\\grid\\SerialColumn|シリアルカラム]] は、`1` から始まる行番号を表示します。\n\n使い方は、次のように、とても簡単です。\n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'columns' => [\n        ['class' => 'yii\\grid\\SerialColumn'], // <-- ここ\n        // ...\n```\n\n\n### データを並べ替える <span id=\"sorting-data\"></span>\n\n> Note: このセクションはまだ執筆中です。\n>\n> - https://github.com/yiisoft/yii2/issues/1576\n\n### データをフィルタリングする <span id=\"filtering-data\"></span>\n\nデータをフィルタリングするためには、GridView は検索基準を表す [モデル](structure-models.md) を必要とします。\n検索基準は、通常は、グリッド・ビューのテーブルのフィルタのフィールドから取得されます。\n[アクティブ・レコード](db-active-record.md) を使用している場合は、必要な機能を提供する検索用のモデル・クラスを\n作成するのが一般的なプラクティスです (あなたに代って [Gii](start-gii.md) が生成してくれます)。\nこのクラスが、グリッド・ビューのテーブルに表示されるフィルタ・コントロールのための検証規則を定義し、\n検索基準に従って修正されたクエリを持つデータ・プロバイダを返す `search()` メソッドを提供します。\n\n`Post` モデルに対して検索機能を追加するために、次の例のようにして、`PostSearch` モデルを作成することが出来ます。\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse Yii;\nuse yii\\base\\Model;\nuse yii\\data\\ActiveDataProvider;\n\nclass PostSearch extends Post\n{\n    public function rules()\n    {\n        // rules() にあるフィールドだけが検索可能\n        return [\n            [['id'], 'integer'],\n            [['title', 'creation_date'], 'safe'],\n        ];\n    }\n\n    public function scenarios()\n    {\n        // 親クラスの scenarios() の実装をバイパスする\n        return Model::scenarios();\n    }\n\n    public function search($params)\n    {\n        $query = Post::find();\n\n        $dataProvider = new ActiveDataProvider([\n            'query' => $query,\n        ]);\n\n        // 検索フォームのデータをロードして検証する\n        if (!($this->load($params) && $this->validate())) {\n            return $dataProvider;\n        }\n\n        // フィルタを追加してクエリを修正する\n        $query->andFilterWhere(['id' => $this->id]);\n        $query->andFilterWhere(['like', 'title', $this->title])\n              ->andFilterWhere(['like', 'creation_date', $this->creation_date]);\n\n        return $dataProvider;\n    }\n}\n```\n\n> Tip: フィルタのクエリを構築する方法を学ぶためには、[クエリ・ビルダ](db-query-builder.md)、\n> 中でも特に [フィルタ条件](db-query-builder.md#filter-conditions) を参照してください。\n\nこの `search()` メソッドをコントローラで使用して、GridView のためのデータ・プロバイダを取得することが出来ます。\n\n```php\n$searchModel = new PostSearch();\n$dataProvider = $searchModel->search(Yii::$app->request->get());\n\nreturn $this->render('myview', [\n    'dataProvider' => $dataProvider,\n    'searchModel' => $searchModel,\n]);\n```\n\nそしてビューでは、`$dataProvider` と `$searchModel` を GridView に与えます。\n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'filterModel' => $searchModel,\n    'columns' => [\n        // ...\n    ],\n]);\n```\n\n### 独立したフィルタ・フォーム <span id=\"separate-filter-form\"></span>\n\nたいていの場合はグリッド・ビューのヘッダのフィルタで十分でしょう。しかし、独立したフィルタのフォームが必要な場合でも、\n簡単に追加することができます。まず、以下の内容を持つパーシャル・ビュー `_search.php` を作成します。\n\n```php\n<?php\n\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n/**\n * @var \\yii\\web\\View $this\n * @var \\app\\models\\PostSearch $model\n * @var \\yii\\widgets\\ActiveForm $form\n */\n?>\n\n<div class=\"post-search\">\n    <?php $form = ActiveForm::begin([\n        'action' => ['index'],\n        'method' => 'get',\n    ]); ?>\n\n    <?= $form->field($model, 'title') ?>\n\n    <?= $form->field($model, 'creation_date') ?>\n\n    <div class=\"form-group\">\n        <?= Html::submitButton('Search', ['class' => 'btn btn-primary']) ?>\n        <?= Html::submitButton('Reset', ['class' => 'btn btn-default']) ?>\n    </div>\n\n    <?php ActiveForm::end(); ?>\n</div>\n```\n\nそして、これを以下のように `index.php` ビューにインクルードします。\n\n```php\n<?= $this->render('_search', ['model' => $searchModel]) ?>\n```\n\n> Note: Gii を使って CRUD コードを生成する場合、デフォルトで、独立したフィルタ・フォーム (`_search.php`) が生成されます。\n  ただし、`index.php` ビューの中ではコメント・アウトされています。コメントを外せば、すぐに使うことが出来ます。\n\n独立したフィルタ・フォームは、グリッド・ビューに表示されないフィールドによってフィルタをかけたり、\nまたは日付の範囲のような特殊なフィルタ条件を使う必要があったりする場合に便利です。\n日付の範囲によってフィルタする場合は、DB には存在しない `createdFrom` と `createdTo` という属性を検索用のモデルに追加すると良いでしょう。\n\n```php\nclass PostSearch extends Post\n{\n    /**\n     * @var string\n     */\n    public $createdFrom;\n\n    /**\n     * @var string\n     */\n    public $createdTo;\n}\n```\n\nそして、`search()` メソッドでクエリの条件を次のように拡張します。\n\n```php\n$query->andFilterWhere(['>=', 'creation_date', $this->createdFrom])\n      ->andFilterWhere(['<=', 'creation_date', $this->createdTo]);\n```\n\nそして、フィルタ・フォームに、日付の範囲を示すフィールドを追加します。\n\n```php\n<?= $form->field($model, 'creationFrom') ?>\n\n<?= $form->field($model, 'creationTo') ?>\n```\n\n### モデルのリレーションを扱う <span id=\"working-with-model-relations\"></span>\n\nGridView でアクティブ・レコードを表示するときに、例えば、単に投稿者の `id` ではなく、\n投稿者の名前のような関連するカラムの値を表示するという場合に遭遇するかも知れません。\n`Post` モデルが `author` という名前のリレーションを持っていて、その投稿者のモデルが `name` という属性を持っているなら、\n[[yii\\grid\\GridView::$columns]] の属性名を `author.name` と定義します。\nそうすれば、GridView が投稿者の名前を表示するようになります。ただし、並べ替えとフィルタリングは、デフォルトでは有効になりません。\nこれらの機能を追加するためには、前の項で導入した `PostSearch` モデルを修正しなければなりません。\n\nリレーションのカラムによる並べ替えを有効にするためには、リレーションのテーブルを結合し、\nデータ・プロバイダの Sort コンポーネントに並べ替えの規則を追加します。\n\n```php\n$query = Post::find();\n$dataProvider = new ActiveDataProvider([\n    'query' => $query,\n]);\n\n// リレーション `author` を結合します。これはテーブル `users` に対するリレーションであり、\n// テーブル・エイリアスを `author` とします。\n$query->joinWith(['author' => function($query) { $query->from(['author' => 'users']); }]);\n// バージョン 2.0.7 以降では、上の行は $query->joinWith('author AS author'); として単純化することが出来ます。\n// リレーションのカラムによる並べ替えを有効にします。\n$dataProvider->sort->attributes['author.name'] = [\n    'asc' => ['author.name' => SORT_ASC],\n    'desc' => ['author.name' => SORT_DESC],\n];\n\n// ...\n```\n\nフィルタリングも上記と同じ joinWith の呼び出しを必要とします。また、次のように、`attributes` と `rules` の中で、検索可能なカラムを追加で定義する必要があります。\n\n```php\npublic function attributes()\n{\n    // 検索可能な属性にリレーションのフィールドを追加する\n    return array_merge(parent::attributes(), ['author.name']);\n}\n\npublic function rules()\n{\n    return [\n        [['id'], 'integer'],\n        [['title', 'creation_date', 'author.name'], 'safe'],\n    ];\n}\n```\n\n`search()` メソッドでは、次のように、もう一つのフィルタ条件を追加するだけです。\n\n```php\n$query->andFilterWhere(['LIKE', 'author.name', $this->getAttribute('author.name')]);\n```\n\n> Info: 上の例では、リレーション名とテーブル・エイリアスに同じ文字列を使用しています。\n> しかし、エイリアスとリレーション名が異なる場合は、どこでエイリアスを使い、どこでリレーション名を使うかに注意を払わなければなりません。\n> これに関する簡単な規則は、データベース・クエリを構築するために使われる全ての場所でエイリアスを使い、\n> `attributes()` や `rules()` など、その他の全ての定義においてリレーション名を使う、というものです。\n>\n> 例えば、投稿者のリレーションテーブルに `au` というエイリアスを使う場合は、joinWith の文は以下のようになります。\n>\n> ```php\n> $query->joinWith(['author au']);\n> ```\n>\n> リレーションの定義においてエイリアスが定義されている場合は、単に `$query->joinWith(['author']);` として呼び出すことも可能です。\n>\n> フィルタ条件においてはエイリアスが使われなければなりませんが、属性の名前はリレーション名のままで変りません。\n>\n> ```php\n> $query->andFilterWhere(['LIKE', 'au.name', $this->getAttribute('author.name')]);\n> ```\n>\n> 並べ替えの定義についても同じことです。\n>\n> ```php\n> $dataProvider->sort->attributes['author.name'] = [\n>      'asc' => ['au.name' => SORT_ASC],\n>      'desc' => ['au.name' => SORT_DESC],\n> ];\n> ```\n>\n> さらに、並べ替えの [[yii\\data\\Sort::defaultOrder|defaultOrder]] を指定するときも、\n> エイリアスではなくリレーション名を使う必要があります。\n>\n> ```php\n> $dataProvider->sort->defaultOrder = ['author.name' => SORT_ASC];\n> ```\n\n> Info: `joinWith` およびバックグラウンドで実行されるクエリの詳細については、\n> [アクティブ・レコード - リレーションを使ってテーブルを結合する](db-active-record.md#joining-with-relations) を参照してください。\n\n#### SQL ビューを使って、データのフィルタリング・並べ替え・表示をする <span id=\"using-sql-views\"></span>\n\nもう一つ別に、もっと高速で便利な手法があります。SQL ビューです。\n例えば、ユーザとユーザのプロファイルを一緒にグリッド・ビューに表示する必要がある場合、次のような SQL ビューを作成することが出来ます。\n\n```sql\nCREATE OR REPLACE VIEW vw_user_info AS\n    SELECT user.*, user_profile.lastname, user_profile.firstname\n    FROM user, user_profile\n    WHERE user.id = user_profile.user_id\n```\n\nそして、このビューを表す ActiveRecord を作成します。\n\n```php\n\nnamespace app\\models\\views\\grid;\n\nuse yii\\db\\ActiveRecord;\n\nclass UserView extends ActiveRecord\n{\n\n    /**\n     * {@inheritdoc}\n     */\n    public static function tableName()\n    {\n        return 'vw_user_info';\n    }\n\n    public static function primaryKey()\n    {\n        return ['id'];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function rules()\n    {\n        return [\n            // ここで規則を定義\n        ];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function attributeLabels()\n    {\n        return [\n            // ここで属性のラベルを定義\n        ];\n    }\n\n\n}\n```\n\nこのようにした後は、この UserView アクティブ・レコードを検索用のモデルとともに使うことが出来ます。並べ替えやフィルタリングの属性を追加で定義する必要はありません。\n全ての属性がそのままで動作します。この手法にはいくつかの長所と短所があることに注意してください。\n\n- 並べ替えとフィルタリングの条件をいろいろと定義する必要はありません。全てそのままで動きます。\n- データサイズが小さく、実行される SQL クエリの数が少ない (通常なら全てのリレーションについて一つずつ必要になる追加のクエリが要らない) ため、非常に高速になり得ます。\n- これは SQL ビューにかぶせた単純な UI に過ぎないもので、エンティティに含まれるドメイン・ロジックを欠いています。\n従って、`isActive` や `isDeleted` などのような UI に影響するメソッドがある場合は、それらをこのクラスの中に複製する必要があります。\n\n\n### 一つのページに複数のグリッド・ビュー <span id=\"multiple-gridviews\"></span>\n\n一つのページで二つ以上のグリッド・ビューを使うことが出来ますが、\nお互いが干渉しないように、追加の構成がいくつか必要になります。\nグリッド・ビューの複数のインスタンスを使う場合は、並べ替えとページネーションのリンクが違うパラメータ名を持って生成されるように構成して、\nそれぞれのグリッド・ビューが独立した並べ替えとページネーションを持つことが出来るようにしなければなりません。\nそのためには、データ・プロバイダの [[yii\\data\\BaseDataProvider::$sort|sort]] と [[yii\\data\\BaseDataProvider::$pagination|pagination]]\nインスタンスの [[yii\\data\\Sort::sortParam|sortParam]] と [[yii\\data\\Pagination::pageParam|pageParam]]\nを設定します。\n\n`Post` と `User` のリストを表示するために、二つのプロバイダ、`$userProvider` と `$postProvider`\nを準備済みであると仮定します。\n\n```php\nuse yii\\grid\\GridView;\n\n$userProvider->pagination->pageParam = 'user-page';\n$userProvider->sort->sortParam = 'user-sort';\n\n$postProvider->pagination->pageParam = 'post-page';\n$postProvider->sort->sortParam = 'post-sort';\n\necho '<h1>ユーザ</h1>';\necho GridView::widget([\n    'dataProvider' => $userProvider,\n]);\n\necho '<h1>投稿</h1>';\necho GridView::widget([\n    'dataProvider' => $postProvider,\n]);\n```\n\n### GridView を Pjax とともに使う <span id=\"using-gridview-with-pjax\"></span>\n\n[[yii\\widgets\\Pjax|Pjax]] ウィジェットを使うと、ページ全体をリロードせずに、\nページの一部分だけを更新することが出来ます。\nこれを使うと、フィルタを使うときに、[[yii\\grid\\GridView|GridView]] の中身だけを更新することが出来ます。\n\n```php\nuse yii\\widgets\\Pjax;\nuse yii\\grid\\GridView;\n\nPjax::begin([\n    // PJax のオプション\n]);\n    Gridview::widget([\n        // GridView のオプション\n    ]);\nPjax::end();\n```\n\nPjax は、[[yii\\widgets\\Pjax|Pjax]] ウィジェットの内側にあるリンク、および、[[yii\\widgets\\Pjax::$linkSelector|Pjax::$linkSelector]]\nで指定されているリンクに対しても動作します。\nしかし、これは [[yii\\grid\\ActionColumn|ActionColumn]] のリンクに対しては問題となり得ます。\nこの問題を防止するためには、[[yii\\grid\\ActionColumn::$buttons|ActionColumn::$buttons]]\nプロパティを編集して `data-pjax=\"0\"` という HTML 属性を追加します。\n\n#### Gii における Pjax を伴う GridView/ListView\n\nバージョン 2.0.5 以降、[Gii](start-gii.md) の CRUD ジェネレータでは `$enablePjax`\nというオプションがウェブ・インタフェイスまたはコマンドラインで使用可能になっています。\n\n```php\nyii gii/crud --controllerClass=\"backend\\\\controllers\\PostController\" \\\n  --modelClass=\"common\\\\models\\\\Post\" \\\n  --enablePjax=1\n```\n\nこれによって、[[yii\\grid\\GridView|GridView]] または [[yii\\widgets\\ListView|ListView]]\nを囲む [[yii\\widgets\\Pjax|Pjax]] ウィジェットが生成されます。\n\nさらに読むべき文書\n------------------\n\n- Arno Slatius による [Rendering Data in Yii 2 with GridView and ListView](https://www.sitepoint.com/rendering-data-in-yii-2-with-gridview-and-listview/)\n"
  },
  {
    "path": "docs/guide-ja/output-formatting.md",
    "content": "データのフォーマット\n====================\n\nユーザにとってより読みやすい形式でデータを表示するために、`formatter` [アプリケーション・コンポーネント](structure-application-components.md)\nを使ってデータをフォーマットすることが出来ます。デフォルトでは、フォーマッタは [[yii\\i18n\\Formatter]] によって実装されており、\nこれが、日付/時刻、数字、通貨、その他のよく使われる形式にデータをフォーマットする一連のメソッドを提供します。このフォーマッタは次のようにして使うことが出来ます。\n\n```php\n$formatter = \\Yii::$app->formatter;\n\n// 出力: January 1, 2014\necho $formatter->asDate('2014-01-01', 'long');\n \n// 出力: 12.50%\necho $formatter->asPercent(0.125, 2);\n \n// 出力: <a href=\"mailto:cebe@example.com\">cebe@example.com</a>\necho $formatter->asEmail('cebe@example.com'); \n\n// 出力: Yes\necho $formatter->asBoolean(true); \n// it also handles display of null values:\n\n// 出力: (not set)\necho $formatter->asDate(null); \n```\n\nご覧のように、これらのメソッドは全て `asXyz()` という名前を付けられており、`Xyz` がサポートされている形式を表しています。\n別の方法として、汎用メソッド [[yii\\i18n\\Formatter::format()|format()]] を使ってデータをフォーマットすることも出来ます。\nこの方法を使うと望む形式をプログラム的に制御することが可能になりますので、[[yii\\grid\\GridView]] や [[yii\\widgets\\DetailView]]\nなどのウィジェットでは、こちらがよく使われています。例えば、\n\n```php\n// 出力: January 1, 2014\necho Yii::$app->formatter->format('2014-01-01', 'date'); \n\n// 配列を使ってフォーマット・メソッドのパラメータを指定することも出来ます。\n// `2` は asPercent() メソッドの $decimals パラメータの値です。\n// 出力: 12.50%\necho Yii::$app->formatter->format(0.125, ['percent', 2]); \n```\n\n> Note: フォーマッタ・コンポーネントは、エンド・ユーザへの表示用に値をフォーマットすることを目的に設計されています。\n> ユーザの入力を機械が読み取れる形式にフォーマットしたい場合、また、日付を機械が読み取れる形式にフォーマットしたいだけ、という場合には、\n> フォーマッタは適切なツールではありません。\n> 日付と数値についてユーザ入力を変換するためには、それぞれ、[[yii\\validators\\DateValidator]] と [[yii\\validators\\NumberValidator]]\n> を使うことが出来ます。機械が読み取れる日付と時刻のフォーマットの単純な相互変換には、PHP の \n> [date()](https://www.php.net/manual/ja/function.date.php) 関数で十分です。\n\n## フォーマッタを構成する <span id=\"configuring-formater\"></span>\n\n[アプリケーションの構成情報](concept-configurations.md#application-configurations) の中で `formatter` コンポーネントを構成して、\nフォーマットの規則をカスタマイズすることが出来ます。例えば、\n\n```php\nreturn [\n    'components' => [\n        'formatter' => [\n            'dateFormat' => 'dd.MM.yyyy',\n            'decimalSeparator' => ',',\n            'thousandSeparator' => ' ',\n            'currencyCode' => 'EUR',\n       ],\n    ],\n];\n```\n\n構成可能なプロパティについては、[[yii\\i18n\\Formatter]] を参照してください。\n\n\n## 日付と時刻の値をフォーマットする <span id=\"date-and-time\"></span>\n\nフォーマッタは日付と時刻に関連した下記の出力形式をサポートしています。\n\n- [[yii\\i18n\\Formatter::asDate()|date]] - 値は日付としてフォーマットされます。例えば `January 01, 2014`。\n- [[yii\\i18n\\Formatter::asTime()|time]] - 値は時刻としてフォーマットされます。例えば `14:23`。\n- [[yii\\i18n\\Formatter::asDatetime()|datetime]] - 値は日付および時刻としてフォーマットされます。例えば `January 01, 2014 14:23`。\n- [[yii\\i18n\\Formatter::asTimestamp()|timestamp]] - 値は [unix タイムスタンプ](https://ja.wikipedia.org/wiki/UNIX%E6%99%82%E9%96%93) としてフォーマットされます。例えば `1412609982`\n- [[yii\\i18n\\Formatter::asRelativeTime()|relativeTime]] - 値は、その日時と現在との間隔として、人間に分かりやすい言葉でフォーマットされます。\n  例えば `1 hour ago`。\n- [[yii\\i18n\\Formatter::asDuration()|duration]] - 値は継続時間として、人間に分かりやすい言葉でフォーマットされます。例えば `1 day, 2 minutes`。\n\n[[yii\\i18n\\Formatter::asDate()|date]]、[[yii\\i18n\\Formatter::asTime()|time]]、[[yii\\i18n\\Formatter::asDatetime()|datetime]]\nメソッドに使われるデフォルトの日時書式は、フォーマッタの [[yii\\i18n\\Formatter::$dateFormat|$dateFormat]]、\n[[yii\\i18n\\Formatter::$timeFormat|$timeFormat]]、[[yii\\i18n\\Formatter::$datetimeFormat|$datetimeFormat]] を構成することで、\nグローバルにカスタマイズすることが出来ます。\n\n日付と時刻のフォーマットは、[ICU 構文](https://unicode-org.github.io/icu/userguide/format_parse/datetime/) によって指定することが出来ます。\nまた、ICU 構文と区別するために `php:` という接頭辞を付けて、[PHP の date() 構文](https://www.php.net/manual/ja/function.date.php)\nを使うことも出来ます。例えば、\n\n```php\n// ICU 形式\necho Yii::$app->formatter->asDate('now', 'yyyy-MM-dd'); // 2014-10-06\n\n// PHP date() 形式\necho Yii::$app->formatter->asDate('now', 'php:Y-m-d'); // 2014-10-06\n```\n\n> Info: PHP 形式の書式の文字の中には ICU でサポートされておらず、従って PHP intl エクステンションでもサポートされていないため、\n> Yii のフォーマッタで使用できないものがあります。それらの文字のほとんどのもの (`w`, `t`, `L`, `B`, `u`, `I`, `Z`) は、日付の書式としては大して有用ではなく、\n> むしろ日数の計算をするのに使われるものです。しかし、`S` と `U` は有用かも知れません。これらの動作は次のようにして達成することが出来ます。\n>\n> - `S` は、月の何日目かを示す英語の序数接尾詞序詞 (すなわ st, nd, rd または th ) ですが、これの代りに以下のような代替手段が使用出来ます。\n>\n>   ```php\n>   $f = Yii::$app->formatter;\n>   $d = $f->asOrdinal($f->asDate('2017-05-15', 'php:j'));\n>   echo \"On the $d day of the month.\";  // \"On the 15th day of the month.\" と表示\n>   ```\n>\n> - `U`、すなわち Unix エポックに対しては、[[yii\\i18n\\Formatter::asTimestamp()|timestamp]] 形式を使うことが出来ます。\n\n複数の言語をサポートする必要があるアプリケーションを扱う場合には、ロケールごとに異なる日付と時刻のフォーマットを指定しなければならないことがよくあります。\nこの仕事を単純化するためには、(`long`、`short` などの) フォーマットのショートカットを代りに使うことが出来ます。\nフォーマッタは、現在アクティブな [[yii\\i18n\\Formatter::locale|locale]] に従って、フォーマットのショートカットを適切なフォーマットに変換します。\nフォーマットのショートカットとして、次のものがサポートされています (例は `en_GB` がアクティブなロケールであると仮定したものです)。\n\n- `short`: 日付は `06/10/2014`、時刻は `15:58` を出力\n- `medium`: `6 Oct 2014` と `15:58:42` を出力\n- `long`: `6 October 2014` と `15:58:42 GMT` を出力\n- `full`: `Monday, 6 October 2014` と `15:58:42 GMT` を出力\n\n> Info: ja_JP ロケールでは、次のようになります。\n> \n> short: `2014/10/06` と `15:58`\n> medium: `2014/10/06` と `15:58:42`\n> long: `2014年10月6日` と `15:58:42 JST`\n> full: `2014年10月6日月曜日` と `15時58分42秒 日本標準時`\n\nバージョン 2.0.7 以降では、さまざまな暦法に従って日付をフォーマットすることが可能です。\n通常とは異なる暦法を使用する方法については、フォーマッタの [[yii\\i18n\\Formatter::$calendar|$calendar]] プロパティの API ドキュメントを参照して下さい。\n\n\n### タイム・ゾーン <span id=\"time-zones\"></span>\n\n日時の値をフォーマットするときに、Yii はその値をターゲット・[[yii\\i18n\\Formatter::timeZone|タイム・ゾーン]] に変換します。\nフォーマットされる日付の値は、タイム・ゾーンが明示的に指定されるか、[[yii\\i18n\\Formatter::defaultTimeZone]] が構成されるかしていない限り、\nUTC であると見なされます。\n\n次の例では、ターゲット・[[yii\\i18n\\Formatter::timeZone|タイム・ゾーン]] が `Europe/Berlin` に設定されているものとします。\n\n```php\n// UNIX タイムスタンプを時刻としてフォーマット\necho Yii::$app->formatter->asTime(1412599260); // 14:41:00\n\n// UTC の日付時刻文字列を時刻としてフォーマット\necho Yii::$app->formatter->asTime('2014-10-06 12:41:00'); // 14:41:00\n\n// CEST の日付時刻文字列を時刻としてフォーマット\necho Yii::$app->formatter->asTime('2014-10-06 14:41:00 CEST'); // 14:41:00\n```\n\n> Info:\n> ターゲット・[[yii\\i18n\\Formatter::timeZone|タイム・ゾーン]] が `Asia/Tokyo` である場合は、次のようになります。\n> \n> ```php\n> echo Yii::$app->formatter->asTime(1412599260); // 21:41:00\n> echo Yii::$app->formatter->asTime('2014-10-06 12:41:00'); // 21:41:00\n> echo Yii::$app->formatter->asTime('2014-10-06 21:41:00 JST'); // 21:41:00\n> ```\n\nフォーマッタ・コンポーネントに対して [[yii\\i18n\\Formatter::timeZone|タイム・ゾーン]] が明示的に設定されていない場合は、\n[[yii\\base\\Application::timeZone|アプリケーションで設定されたタイム・ゾーン]]\n(PHP の構成で設定されたタイム・ゾーンと同じ) が使用されます。\n\n> Note: タイム・ゾーンは世界中のさまざまな政府によって作られる規則に従うものであり、頻繁に変更されるものであるため、\n> あなたのシステムにインストールされたタイム・ゾーンのデータベースが最新の情報を持っていない可能性が大いにあります。\n> タイム・ゾーン・データベースの更新についての詳細は、\n> [ICU マニュアル](https://unicode-org.github.io/icu/userguide/datetime/timezone/#updating-the-time-zone-data) で参照することが出来ます。\n> [PHP 環境を国際化のために設定する](tutorial-i18n.md#setup-environment) も参照してください。\n\n\n## 数値をフォーマットする <span id=\"numbers\"></span>\n\nフォーマッタは、数値に関連した下記の出力フォーマットをサポートしています。\n\n- [[yii\\i18n\\Formatter::asInteger()|integer]] - 値は整数としてフォーマットされます。例えば `42`。\n- [[yii\\i18n\\Formatter::asDecimal()|decimal]] - 値は小数点と三桁ごとの区切りを考慮して十進数としてフォーマットされます。\n  例えば `2,542.123` または `2.542,123`。\n- [[yii\\i18n\\Formatter::asPercent()|percent]] - 値は百分率としてフォーマットされます。例えば `42%`。\n- [[yii\\i18n\\Formatter::asScientific()|scientific]] - 値は科学記法による数値としてフォーマットされます。例えば `4.2E4`。\n- [[yii\\i18n\\Formatter::asCurrency()|currency]] - 値は通貨の値としてフォーマットされます。例えば `£420.00`。\n  この関数が正しく働くためには、`en_GB` や `en_US` のように、ロケールが国コードを含んでいる必要があります。\n  なぜなら、この場合は言語だけでは曖昧になるからです。\n- [[yii\\i18n\\Formatter::asSize()|size]] - バイト数である値が人間にとって読みやすいサイズとしてフォーマットされます。例えば `410 キビバイト`。\n- [[yii\\i18n\\Formatter::asShortSize()|shortSize]] - [[yii\\i18n\\Formatter::asSize()|size]] の短いバージョンです。例えば `410 KiB`。\n\n数値のフォーマットに使われる書式は、デフォルトではロケールに従って設定される\n[[yii\\i18n\\Formatter::decimalSeparator|decimalSeparator]] と [[yii\\i18n\\Formatter::thousandSeparator|thousandSeparator]]\nを使って調整することが出来ます。\n\n更に高度な設定のためには、[[yii\\i18n\\Formatter::numberFormatterOptions]] と [[yii\\i18n\\Formatter::numberFormatterTextOptions]]\nを使って、内部的に使用される [NumberFormatter クラス](https://www.php.net/manual/ja/class.numberformatter.php) を構成することが出来ます。\n例えば、小数部の最大桁数と最小桁数を調整するためには、次のように [[yii\\i18n\\Formatter::numberFormatterOptions]]\nプロパティを構成します。\n\n```php\n'numberFormatterOptions' => [\n    NumberFormatter::MIN_FRACTION_DIGITS => 0,\n    NumberFormatter::MAX_FRACTION_DIGITS => 2,\n]\n```\n\n\n## その他のフォーマット <span id=\"other\"></span>\n\n日付/時刻と数値のフォーマット以外にも、Yii はよく使われるフォーマットをサポートしています。その中には、次のものが含まれます。\n\n- [[yii\\i18n\\Formatter::asRaw()|raw]] - 値はそのまま出力されます。\n  `null` 値が [[nullDisplay]] を使ってフォーマットされる以外は、何の効果もない擬似フォーマッタです。\n- [[yii\\i18n\\Formatter::asText()|text]] - 値は HTML エンコードされます。\n  これは [GridView DataColumn](output-data-widgets.md#data-column) で使われるデフォルトのフォーマットです。\n- [[yii\\i18n\\Formatter::asNtext()|ntext]] - 値は HTML エンコードされ、\n  改行文字が強制改行に変換された平文テキストとしてフォーマットされます。\n- [[yii\\i18n\\Formatter::asParagraphs()|paragraphs]] - 値は HTML エンコードされ、\n  `<p>` タグに囲まれた段落としてフォーマットされます。\n- [[yii\\i18n\\Formatter::asHtml()|html]] - 値は XSS 攻撃を避けるために [[HtmlPurifier]] を使って浄化されます。\n  `['html', ['Attr.AllowedFrameTargets' => ['_blank']]]` のような追加のオプションを渡すことが出来ます。\n- [[yii\\i18n\\Formatter::asEmail()|email]] - 値は `mailto` リンクとしてフォーマットされます。\n- [[yii\\i18n\\Formatter::asImage()|image]] - 値は `image` タグとしてフォーマットされます。\n- [[yii\\i18n\\Formatter::asUrl()|url]] - 値はハイパーリンクとしてフォーマットされます。\n- [[yii\\i18n\\Formatter::asBoolean()|boolean]] - 値は真偽値としてフォーマットされます。\n  デフォルトでは、`true` は `Yes`、`false` は `No` とレンダリングされ、現在のアプリケーションの言語に翻訳されます。\n  この動作は [[yii\\i18n\\Formatter::booleanFormat]] プロパティを構成して調整できます。\n\n\n## `null` 値 <span id=\"null-values\"></span>\n\nNull 値は特殊な方法でフォーマットされます。空文字列を表示する代りに、フォーマッタは null 値を事前定義された文字列 \n(そのデフォルト値は `(not set)` です) に変換し、それを現在のアプリケーションの言語に翻訳します。\nこの文字列は [[yii\\i18n\\Formatter::nullDisplay|nullDisplay]] プロパティを構成してカスタマイズすることが出来ます。\n\n\n## データのフォーマットをローカライズする <span id=\"localizing-data-format\"></span>\n\n既に述べたように、フォーマッタは現在のアクティブな [[yii\\i18n\\Formatter::locale|locale]] を使って、\nターゲットの国/地域にふさわしい値のフォーマットを決定することが出来ます。\n例えば、同じ日時の値でも、ロケールによって異なる書式にフォーマットされます。\n\n```php\nYii::$app->formatter->locale = 'en-US';\necho Yii::$app->formatter->asDate('2014-01-01'); // 出力: January 1, 2014\n\nYii::$app->formatter->locale = 'de-DE';\necho Yii::$app->formatter->asDate('2014-01-01'); // 出力: 1. Januar 2014\n\nYii::$app->formatter->locale = 'ru-RU';\necho Yii::$app->formatter->asDate('2014-01-01'); // 出力: 1 января 2014 г.\n\nYii::$app->formatter->locale = 'ja-JP';\necho Yii::$app->formatter->asDate('2014-01-01'); // 出力: 2014/01/01\n```\n\nデフォルトでは、現在のアクティブな [[yii\\i18n\\Formatter::locale|locale]] は [[yii\\base\\Application::language]] の値によって決定されます。\nこれは [[yii\\i18n\\Formatter::locale]] プロパティを明示的に指定することによってオーバーライドすることが出来ます。\n\n> Note: Yii のフォーマッタは、[PHP intl 拡張](https://www.php.net/manual/ja/book.intl.php) に依存してデータのフォーマットの\n> ローカライズをサポートしています。PHP にコンパイルされた ICU ライブラリのバージョンによってフォーマットの結果が異なる場合がありますので、\n> あなたの全ての環境で、同じ ICU バージョンを使うことが推奨されます。\n> 詳細については、[PHP 環境を国際化のために設定する](tutorial-i18n.md#setup-environment) を参照してください。\n>\n> intl 拡張がインストールされていない場合は、データはローカライズされません。\n>\n> 1901年より前、または、2038年より後の日時の値は、たとえ intl 拡張がインストールされていても、32-bit システムではローカライズされないことに注意してください。\n> これは、この場合、ICU ライブラリが日時の値に対して 32-bit の UNIX タイムスタンプを使用しているのが原因です。\n"
  },
  {
    "path": "docs/guide-ja/output-pagination.md",
    "content": "ページネーション\n================\n\n一つのページに表示するにはデータの数が多すぎるという場合に、データを複数のページに分割して、それぞれのページでは一部分だけを表示する、\nという戦略がよく使われます。この戦略が *ページネーション* として知られるものです。\n\nYii は [[yii\\data\\Pagination]] オブジェクトを使って、ページネーションのスキームに関する情報を表します。具体的に言えば、\n\n* [[yii\\data\\Pagination::$totalCount|totalCount]] データ・アイテムの総数を指定します。\n  通常、データ・アイテムの総数は、一つのページを表示することが可能なデータ・アイテムの数より、ずっと大きなものになることに注意してください。\n* [[yii\\data\\Pagination::$pageSize|pageSize]] 各ページが含むアイテムの数を指定します。\n  デフォルト値は 20 です。\n* [[yii\\data\\Pagination::$page|page]] 現在のページ番号 (0 から始まる) を示します。デフォルト値は 0 であり、最初のページを意味します。\n\nこれらの情報を全て定義した [[yii\\data\\Pagination]] オブジェクトを使って、データの一部分を取得して表示することが出来ます。\n例えば、データ・プロバイダからデータを取得する場合であれば、ページネーションによって提供される値によって、それに対応する\n`OFFSET` と `LIMIT` の句を DB クエリに指定することが出来ます。下記に例を挙げます。\n\n```php\nuse yii\\data\\Pagination;\n\n// status = 1 である全ての記事を取得する DB クエリを構築する\n$query = Article::find()->where(['status' => 1]);\n\n// 記事の総数を取得する (ただし、記事のデータはまだ取得しない)\n$count = $query->count();\n\n// 記事の総数を使ってページネーション・オブジェクトを作成する\n$pagination = new Pagination(['totalCount' => $count]);\n\n// ページネーションを使ってクエリの OFFSET と LIMIT を修正して記事を取得する\n$articles = $query->offset($pagination->offset)\n    ->limit($pagination->limit)\n    ->all();\n```\n\n上記の例で返される記事のページ番号はどうなるでしょう? それは `page` という名前のクエリ・パラメータがリクエストに含まれるかどうかによって決ります。\nデフォルトでは、ページネーション・オブジェクトは [[yii\\data\\Pagination::$page|page]] に `page` パラメータの値をセットしようと試みます。\nそして、このパラメータが提供されていない場合には、デフォルト値である 0 が使用されます。\n\nページネーションをサポートする UI 要素の構築を容易にするために、Yii はページ・ボタンのリストを表示する [[yii\\widgets\\LinkPager]] ウィジェットを提供しています。\nこれは、ユーザがページ・ボタンをクリックして、どのページを表示すべきかを指示することが出来るものです。\nこのウィジェットは、ページネーション・オブジェクトを受け取って、現在のページ番号が何であるかを知り、何個のページ・ボタンを表示すべきかを知ります。\n例えば、\n\n```php\nuse yii\\widgets\\LinkPager;\n\necho LinkPager::widget([\n    'pagination' => $pagination,\n]);\n```\n\nUI 要素を手動で構築したい場合は、[[yii\\data\\Pagination::createUrl()]] を使って、いろんなページに跳ぶ URL を作成することが出来ます。\nこのメソッドは page パラメータを要求し、その page パラメータを含む正しくフォーマットされた URL を作成します。\n例えば、\n\n```php\n// 作成される URL が使用すべきルートを指定する\n// 指定しない場合は、現在リクエストされているルートが使用される\n$pagination->route = 'article/index';\n\n// /index.php?r=article%2Findex&page=100 を表示\necho $pagination->createUrl(100);\n\n// /index.php?r=article%2Findex&page=101 を表示\necho $pagination->createUrl(101);\n```\n\n> Tip: `page` クエリ・パラメータの名前をカスタマイズするためには、ページネーション・オブジェクトを作成する際に\n[[yii\\data\\Pagination::pageParam|pageParam]] プロパティを構成します。\n"
  },
  {
    "path": "docs/guide-ja/output-sorting.md",
    "content": "並べ替え\n========\n\n複数のデータ行を表示する際に、エンド・ユーザによって指定されるカラムに従ってデータを並べ替えなければならないことがよくあります。\nYii は [[yii\\data\\Sort]] オブジェクトを使って並べ替えのスキーマに関する情報を表します。\n具体的に言えば、\n\n* [[yii\\data\\Sort::$attributes|attributes]] データの並べ替えに使用できる *属性* を指定します。\n  単純で良ければ、[モデルの属性](structure-models.md#attributes) をこの属性とすることが出来ます。\n  また、複数のモデル属性や DB のカラムを結合した合成的な属性を指定することも出来ます。詳細については後述します。\n* [[yii\\data\\Sort::$attributeOrders|attributeOrders]] 各属性について、\n  現在リクエストされている並べ替えの方向を指定します。\n* [[yii\\data\\Sort::$orders|orders]] 並べ替えの方向をカラムを使う低レベルな形式で示します。\n\n[[yii\\data\\Sort]] を使用するためには、最初にどの属性が並べ替え可能であるかを宣言します。\n次に、現在リクエストされている並べ替え情報を [[yii\\data\\Sort::$attributeOrders|attributeOrders]]\nまたは [[yii\\data\\Sort::$orders|orders]] から取得して、データのクエリをカスタマイズします。例えば、\n\n```php\nuse yii\\data\\Sort;\n\n$sort = new Sort([\n    'attributes' => [\n        'age',\n        'name' => [\n            'asc' => ['first_name' => SORT_ASC, 'last_name' => SORT_ASC],\n            'desc' => ['first_name' => SORT_DESC, 'last_name' => SORT_DESC],\n            'default' => SORT_DESC,\n            'label' => '氏名',\n        ],\n    ],\n]);\n\n$articles = Article::find()\n    ->where(['status' => 1])\n    ->orderBy($sort->orders)\n    ->all();\n```\n\n上記の例では、[[yii\\data\\Sort|Sort]] オブジェクトに対して二つの属性が宣言されています。すなわち、`age` と `name` です。\n\n`age` 属性は `Article` アクティブ・レコード・クラスの `age` 属性に対応する *単純な* 属性です。\nこれは、次の宣言と等価です。\n\n```php\n'age' => [\n    'asc' => ['age' => SORT_ASC],\n    'desc' => ['age' => SORT_DESC],\n    'default' => SORT_ASC,\n    'label' => Inflector::camel2words('age'),\n]\n```\n\n`name` 属性は `Article` の `first_name` と `last_name` によって定義される *合成的な* 属性です。\nこれは次のような配列構造を使って宣言されています。\n\n- `asc` および `desc` の要素は、それぞれ、この属性を昇順および降順に並べ替える方法を指定します。\n  この値が、データの並べ替えに使用されるべき実際のカラムと方向を表します。\n  一つまたは複数のカラムを指定して、単純な並べ替えや合成的な並べ替えを示すことが出来ます。\n- `default` 要素は、最初にリクエストされたときの属性の並べ替えに使用されるべき方向を指定します。\n  デフォルト値は昇順です。\n  つまり、以前に並べ替えられたことがない状態でこの属性による並べ替えをリクエストすると、この属性の昇順に従ってデータが並べ替えられることになります。\n- `label` 要素は、並べ替えのリンクを作成するために [[yii\\data\\Sort::link()]] を呼んだときに、どういうラベルを使用すべきかを指定するものです。\n  設定されていない場合は、[[yii\\helpers\\Inflector::camel2words()]] が呼ばれて、属性名からラベルが生成されます。\n  ラベルは HTML エンコードされないことに注意してください。\n\n> Info: [[yii\\data\\Sort::$orders|orders]] の値をデータベースのクエリに直接に供給して、\n  `ORDER BY` 句を構築することが出来ます。データベースのクエリが認識できない合成的な属性が入っている場合があるため、\n  [[yii\\data\\Sort::$attributeOrders|attributeOrders]] を使ってはいけません。\n\n[[yii\\data\\Sort::link()]] を呼んでハイパーリンクを生成すれば、それをクリックして、指定した属性によるデータの並べ替えをリクエストすることが出来るようになります。\n[[yii\\data\\Sort::createUrl()]] を呼んで並べ替えを実行する URL を生成することも出来ます。\n例えば、\n\n```php\n// 生成される URL が使用すべきルートを指定する\n// これを指定しない場合は、現在リクエストされているルートが使用される\n$sort->route = 'article/index';\n\n// 氏名による並べ替えと年齢による並べ替えを実行するリンクを表示\necho $sort->link('name') . ' | ' . $sort->link('age');\n\n// /index.php?r=article%2Findex&sort=age を表示\necho $sort->createUrl('age');\n```\n\n[[yii\\data\\Sort]] は、リクエストの `sort` クエリ・パラメータをチェックして、どの属性による並べ替えがリクエストされたかを判断します。\nこのクエリ・パラメータが存在しない場合のデフォルトの並べ替え方法は [[yii\\data\\Sort::defaultOrder]] によって指定することが出来ます。\nまた、[[yii\\data\\Sort::sortParam|sortParam]] プロパティを構成して、このクエリ・パラメータの名前をカスタマイズすることも出来ます。\n"
  },
  {
    "path": "docs/guide-ja/output-theming.md",
    "content": "テーマ\n======\n\nテーマは、元のビュー・レンダリングのコードに触れる必要なしに、[ビュー](structure-views.md) のセットを別のセットに置き換えるための方法です。\nテーマを使うとアプリケーションのルック・アンド・フィールを体系的に変更することが出来ます。\n\nテーマを使うためには、`view` アプリケーション・コンポーネントの [[yii\\base\\View::theme|theme]] プロパティを構成しなければなりません。\nこのプロパティが、ビュー・ファイルが置換される方法を管理する [[yii\\base\\Theme]] オブジェクトを構成します。\n指定しなければならない [[yii\\base\\Theme]] のプロパティは主として以下のものです。\n\n- [[yii\\base\\Theme::basePath]]: テーマのリソース (CSS、JS、画像など) を含むベース・ディレクトリを指定します。\n- [[yii\\base\\Theme::baseUrl]]: テーマのリソースのベース URL を指定します。\n- [[yii\\base\\Theme::pathMap]]: ビュー・ファイルの置換の規則を指定します。\n  詳細は後述する項で説明します。\n\n例えば、`SiteController` で `$this->render('about')` を呼び出すと、ビュー・ファイル `@app/views/site/about.php`\nをレンダリングすることになります。しかし、下記のようにアプリケーション構成情報でテーマを有効にすると、\n代りに、ビュー・ファイル `@app/themes/basic/site/about.php` がレンダリングされます。\n\n```php\nreturn [\n    'components' => [\n        'view' => [\n            'theme' => [\n                'basePath' => '@app/themes/basic',\n                'baseUrl' => '@web/themes/basic',\n                'pathMap' => [\n                    '@app/views' => '@app/themes/basic',\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n> Info: テーマではパス・エイリアスがサポートされています。\n  ビューの置換を行う際に、パス・エイリアスは実際のファイル・パスまたは URL に変換されます。\n\n[[yii\\base\\View::theme]] プロパティを通じて [[yii\\base\\Theme]] オブジェクトにアクセスすることが出来ます。\n例えば、ビュー・ファイルの中では `$this` がビュー・オブジェクトを指すので、次のようなコードを書くことが出来ます。\n\n```php\n$theme = $this->theme;\n\n// $theme->baseUrl . '/img/logo.gif' を返す\n$url = $theme->getUrl('img/logo.gif');\n\n// $theme->basePath . '/img/logo.gif' を返す\n$file = $theme->getPath('img/logo.gif');\n```\n\n[[yii\\base\\Theme::pathMap]] プロパティが、ビュー・ファイルがどのように置換されるべきかを制御します。\nこのプロパティは「キー・値」ペアの配列を取ります。\nキーは置き換えられる元のビューのパスであり、値は対応するテーマのビューのパスです。\n置換は部分一致に基づいて行われます。\nあるビューのパスが [[yii\\base\\Theme::pathMap|pathMap]] 配列のキーのどれかで始っていると、その一致している部分が対応する配列の値によって置き換えられます。\n上記の構成例を使う場合、`@app/views/site/about.php` は `@app/views` というキーに部分一致するため、`@app/themes/basic/site/about.php` に置き換えられることになります。\n\n\n### モジュールにテーマを適用する <span id=\"theming-modules\"></span>\n\nモジュールにテーマを適用するためには、[[yii\\base\\Theme::pathMap]] を次のように構成します。\n\n```php\n'pathMap' => [\n    '@app/views' => '@app/themes/basic',\n    '@app/modules' => '@app/themes/basic/modules', // <-- !!!\n],\n```\n\nこれによって、`@app/modules/blog/views/comment/index.php` に `@app/themes/basic/modules/blog/views/comment/index.php` というテーマを適用することが出来ます。\n\n\n### ウィジェットにテーマを適用する <span id=\"theming-widgets\"></span>\n\nウィジェットにテーマを適用するためには、[[yii\\base\\Theme::pathMap]] を次のように構成します。\n\n```php\n'pathMap' => [\n    '@app/views' => '@app/themes/basic',\n    '@app/widgets' => '@app/themes/basic/widgets', // <-- !!!\n],\n```\n\nこれによって、`@app/widgets/currency/views/index.php` に `@app/themes/basic/widgets/currency/views/index.php` というテーマを適用することが出来ます。\n\n\n## テーマの継承 <span id=\"theme-inheritance\"></span>\n\n場合によっては、基本的なルック・アンド・フィールを含むアプリケーションの基本テーマを定義しておいて、\n現在の祝日に基づいてルック・アンド・フィールを少し変更したい、ということがあるかもしれません。\nテーマの継承を使ってこの目的を達することが出来ます。テーマの継承は、一つのビュー・パスを複数のターゲットに割り付けることによって設定することが出来ます。例えば、\n\n```php\n'pathMap' => [\n    '@app/views' => [\n        '@app/themes/christmas',\n        '@app/themes/basic',\n    ],\n]\n```\n\nこの場合、ビュー `@app/views/site/index.php` には、どちらのテーマ・ファイルが存在するかに従って、\n`@app/themes/christmas/site/index.php` か `@app/themes/basic/site/index.php` か、どちらかのテーマが適用されます。\nテーマ・ファイルが両方とも存在する場合は、最初のものが優先されます。\n実際の場面では、ほとんどのテーマ・ビュー・ファイルを `@app/themes/basic` に保管し、その中のいくつかを `@app/themes/christmas` でカスタマイズすることになるでしょう。\n"
  },
  {
    "path": "docs/guide-ja/rest-authentication.md",
    "content": "認証\n====\n\nウェブ・アプリケーションとは異なり、RESTful API は通常はステート・レスです。\nこれは、セッションやクッキーは使用すべきでないことを意味します。\n従って、ユーザの認証ステータスをセッションやクッキーで保持することが出来ないため、全てのリクエストに何らかの認証情報を付加する必要があります。\n通常使われるのは、ユーザを認証するための秘密のアクセス・トークンを全てのリクエストとともに送信する方法です。\nアクセス・トークンはユーザを一意に特定して認証することが出来るものですので、\n**API リクエストは、中間者攻撃 (man-in-the-middle attack) を防止するために、常に HTTPS 経由で送信されなければなりません**。\n\nアクセス・トークンを送信するには、いくつかの異なる方法があります。\n\n* [HTTP Basic 認証](https://ja.wikipedia.org/wiki/Basic%E8%AA%8D%E8%A8%BC): アクセス・トークンはユーザ名として送信されます。\n  この方法は、アクセス・トークンを API コンシューマ側で安全に保存することが出来る場合、\n  例えば API コンシューマがサーバ上で走るプログラムである場合などのみに使用されるべきです。\n* クエリ・パラメータ: アクセス・トークンは API の URL、例えば、`https://example.com/users?access-token=xxxxxxxx`\n  でクエリ・パラメータとして送信されます。\n  ほとんどのウェブ・サーバはクエリ・パラメータをサーバのログに記録するため、この手法は、\n  アクセス・トークンを HTTP ヘッダを使って送信することができない `JSONP` リクエストに応答するために主として使用されるべきです。\n* [OAuth 2](https://oauth.net/2/): OAuth2 プロトコルに従って、\n  アクセス・トークンはコンシューマによって権限付与サーバから取得され、[HTTP Bearer Tokens](https://datatracker.ietf.org/doc/html/rfc6750) 経由で\n  API サーバに送信されます。\n\nYii は上記の全ての認証方法をサポートしています。新しい認証方法を作成することも簡単に出来ます。\n\nあなたの API に対して認証を有効にするためには、次のステップを実行します。\n\n1. `user` [アプリケーション・コンポーネント](structure-application-components.md) を構成します。\n   - [[yii\\web\\User::enableSession|enableSession]] プロパティを `false` に設定します。\n   - [[yii\\web\\User::loginUrl|loginUrl]] プロパティを `null` に設定し、ログインページにリダイレクトする代りに HTTP 403 エラーを表示します。\n2. REST コントローラ・クラスにおいて、`authenticator` ビヘイビアを構成することによって、\n  どの認証方法を使用するかを指定します。\n3. [[yii\\web\\User::identityClass|ユーザ・アイデンティティ・クラス]] において [[yii\\web\\IdentityInterface::findIdentityByAccessToken()]] を実装します。\n\nステップ 1 は必須ではありませんが、ステート・レスであるべき RESTful API のために推奨されます。\n[[yii\\web\\User::enableSession|enableSession]] が `false` である場合、ユーザの認証ステータスがセッションを使ってリクエストをまたいで存続することはありません。\nその代りに、すべてのリクエストに対して認証が実行されます。このことは、ステップ 2 と 3 によって達成されます。\n\n> Tip: RESTful API をアプリケーションの形式で開発する場合は、アプリケーションの構成情報で `user` アプリケーション・コンポーネント(structure-application-components.md) \n>  [[yii\\web\\User::enableSession|enableSession]] プロパティを構成することが出来ます。\n> RESTful API をモジュールとして開発する場合は、次のように、モジュールの `init()` メソッドに一行を追加することが出来ます。\n>\n> ```php\n> public function init()\n> {\n>     parent::init();\n>     \\Yii::$app->user->enableSession = false;\n> }\n> ```\n\n例えば、HTTP Basic 認証を使う場合は、`authenticator` ビヘイビアを次のように構成することが出来ます。\n\n```php\nuse yii\\filters\\auth\\HttpBasicAuth;\n\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n    $behaviors['authenticator'] = [\n        'class' => HttpBasicAuth::class,\n    ];\n    return $behaviors;\n}\n```\n\n上で説明した三つの認証方法を全てサポートしたい場合は、次のように `CompositeAuth` を使うことが出来ます。\n\n```php\nuse yii\\filters\\auth\\CompositeAuth;\nuse yii\\filters\\auth\\HttpBasicAuth;\nuse yii\\filters\\auth\\HttpBearerAuth;\nuse yii\\filters\\auth\\QueryParamAuth;\n\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n    $behaviors['authenticator'] = [\n        'class' => CompositeAuth::class,\n        'authMethods' => [\n            HttpBasicAuth::class,\n            HttpBearerAuth::class,\n            QueryParamAuth::class,\n        ],\n    ];\n    return $behaviors;\n}\n```\n\n`authMethods` の各要素は、認証方法クラスの名前であるか、構成情報配列でなければなりません。\n\n\n`findIdentityByAccessToken()` の実装はアプリケーション固有のものです。\n例えば、各ユーザが一つだけアクセス・トークンを持ち得るような単純なシナリオでは、アクセス・トークンをユーザのテーブルの `access_token` カラムに保存することが出来ます。\nそうすれば、次のように、`findIdentityByAccessToken()` メソッドを `User` クラスにおいて簡単に実装することが出来ます。\n\n```php\nuse yii\\db\\ActiveRecord;\nuse yii\\web\\IdentityInterface;\n\nclass User extends ActiveRecord implements IdentityInterface\n{\n    public static function findIdentityByAccessToken($token, $type = null)\n    {\n        return static::findOne(['access_token' => $token]);\n    }\n}\n```\n\n上記のように認証が有効化された後は、全ての API リクエストに対して、リクエストされたコントローラ\n `beforeAction()` の段階でユーザを認証することを試みます。\n\n認証が成功すると、コントローラはその他のチェック (レート制限、権限付与など) をしてから、アクションを実行します。\n認証されたユーザのアイデンティティは `Yii::$app->user->identity` によって取得することが出来ます。\n\n認証が失敗したときは、HTTP ステータス 401 およびその他の適切なヘッダ (HTTP Basic 認証に対する `WWW-Authenticate` ヘッダなど)\nを持つレスポンスが送り返されます。\n\n\n## 権限付与 <span id=\"authorization\"></span>\n\nユーザが認証された後、おそらくは、リクエストされたリソースに対してリクエストされたアクションを実行する許可を\n彼または彼女が持っているかどうかをチェックしたいでしょう。\n*権限付与* と呼ばれるこのプロセスについては、[権限付与](security-authorization.md) のセクションで詳細に説明されています。\n\nあなたのコントローラが [[yii\\rest\\ActiveController]] から拡張したものである場合は、\n[[yii\\rest\\ActiveController::checkAccess()|checkAccess()]] メソッドをオーバーライドして権限付与のチェックを実行することが出来ます。\nこのメソッドが [[yii\\rest\\ActiveController]] によって提供されている内蔵のアクションから呼び出されます。\n"
  },
  {
    "path": "docs/guide-ja/rest-controllers.md",
    "content": "コントローラ\n============\n\nリソース・クラスを作成して、リソース・データをどのようにフォーマットすべきかを指定したら、\n次は、RESTful API を通じてエンド・ユーザにリソースを公開するコントローラ・アクションを作成します。\n\nYii は、RESTful アクションを作成する仕事を簡単にするための二つの基底コントローラ・クラスを提供しています。\nすなわち、[[yii\\rest\\Controller]] と [[yii\\rest\\ActiveController]] です。\n二つのコントローラの違いは、後者は [アクティブ・レコード](db-active-record.md) として表現されるリソースの扱いに特化した一連のアクションをデフォルトで提供する、という点にあります。\n従って、あなたが [アクティブ・レコード](db-active-record.md) を使っていて、提供される組み込みのアクションに満足できるのであれば、\nコントローラ・クラスを [[yii\\rest\\ActiveController]] から拡張することを検討すると良いでしょう。\nそうすれば、最小限のコードで強力な RESTful API を作成することが出来ます。\n\n[[yii\\rest\\Controller]] と [[yii\\rest\\ActiveController]] は、ともに、下記の機能を提供します。\nこれらのいくつかについては、後続のセクションで詳細に説明します。\n\n* HTTP メソッドのバリデーション\n* [コンテント・ネゴシエーションとデータの書式設定](rest-response-formatting.md)\n* [認証](rest-authentication.md)\n* [レート制限](rest-rate-limiting.md)\n\n[[yii\\rest\\ActiveController]] は次の機能を追加で提供します。\n\n* 普通は必要とされる一連のアクション: `index`、`view`、`create`、`update`、`delete`、`options`\n* リクエストされたアクションとリソースに対するユーザへの権限付与\n\n\n## コントローラ・クラスを作成する <span id=\"creating-controller\"></span>\n\n新しいコントローラ・クラスを作成する場合、コントローラ・クラスの命名規約は、\nリソースの型の名前を単数形で使う、というものです。\n例えば、ユーザの情報を提供するコントローラは `UserController` と名付けることが出来ます。\n\n新しいアクションを作成する仕方はウェブ・アプリケーションの場合とほぼ同じです。\n唯一の違いは、`render()` メソッドを呼んでビューを使って結果を表示する代りに、RESTful アクションの場合はデータを直接に返す、という点です。\n[[yii\\rest\\Controller::serializer|シリアライザ]] と [[yii\\web\\Response|レスポンス・オブジェクト]] が、\n元のデータからリクエストされた形式への変換を処理します。\n例えば、\n\n```php\npublic function actionView($id)\n{\n    return User::findOne($id);\n}\n```\n\n\n## フィルタ <span id=\"filters\"></span>\n\n[[yii\\rest\\Controller]] によって提供される RESTful API 機能のほとんどは [フィルタ](structure-filters.md) の形で実装されています。\n具体的に言うと、次のフィルタがリストされた順に従って実行されます。\n\n* [[yii\\filters\\ContentNegotiator|contentNegotiator]]: コンテント・ネゴシエーションをサポート。\n  [レスポンス形式の設定](rest-response-formatting.md) のセクションで説明します。\n* [[yii\\filters\\VerbFilter|verbFilter]]: HTTP メソッドのバリデーションをサポート。\n* [[yii\\filters\\auth\\AuthMethod|authenticator]]: ユーザ認証をサポート。\n  [認証](rest-authentication.md) のセクションで説明します。\n* [[yii\\filters\\RateLimiter|rateLimiter]]: レート制限をサポート。\n  [レート制限](rest-rate-limiting.md) のセクションで説明します。\n\nこれらの名前付きのフィルタは、[[yii\\rest\\Controller::behaviors()|behaviors()]] メソッドで宣言されます。\nこのメソッドをオーバーライドして、個々のフィルタを構成したり、どれかを無効にしたり、あなた自身のフィルタを追加したりすることが出来ます。\n例えば、HTTP 基本認証だけを使いたい場合は、次のようなコードを書くことが出来ます。\n\n```php\nuse yii\\filters\\auth\\HttpBasicAuth;\n\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n    $behaviors['authenticator'] = [\n        'class' => HttpBasicAuth::class,\n    ];\n    return $behaviors;\n}\n```\n\n### CORS <span id=\"cors\"></span>\n\nコントローラに [CORS (クロス・オリジン・リソース共有)](structure-filters.md#cors) フィルタを追加するのは、上記の他のフィルタを追加するのより、若干複雑になります。\nと言うのは、CORS フィルタは認証メソッドより前に適用されなければならないため、他のフィルタとは少し異なるアプローチが必要だからです。\nまた、ブラウザが認証クレデンシャルを送信する必要なく、リクエストが出来るかどうかを前もって安全に判断できるように、\n[CORS プリフライト・リクエスト](https://developer.mozilla.org/ja/docs/Web/HTTP/CORS#preflighted_requests)\nの認証を無効にする必要もあります。\n下記のコードは、[[yii\\rest\\ActiveController]] を拡張した既存のコントローラに\n[[yii\\filters\\Cors]] フィルタを追加するのに必要なコードを示しています。\n\n```php\nuse yii\\filters\\auth\\HttpBasicAuth;\n\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n\n    // 認証フィルタを削除する\n    $auth = $behaviors['authenticator'];\n    unset($behaviors['authenticator']);\n    \n    // CORS フィルタを追加する\n    $behaviors['corsFilter'] = [\n        'class' => \\yii\\filters\\Cors::class,\n    ];\n    \n    // 認証フィルタを再度追加する\n    $behaviors['authenticator'] = $auth;\n    // CORS プリフライト・リクエスト (HTTP OPTIONS メソッド) の認証を回避する\n    $behaviors['authenticator']['except'] = ['options'];\n\n    return $behaviors;\n}\n```\n\n\n## `ActiveController` を拡張する <span id=\"extending-active-controller\"></span>\n\nコントローラを [[yii\\rest\\ActiveController]] から拡張する場合は、このコントローラを通じて提供しようとしているリソース・クラスの名前を\n[[yii\\rest\\ActiveController::modelClass|modelClass]] プロパティにセットしなければなりません。\nリソース・クラスは [[yii\\db\\ActiveRecord]] から拡張しなければなりません。\n\n\n### アクションをカスタマイズする <span id=\"customizing-actions\"></span>\n\nデフォルトでは、[[yii\\rest\\ActiveController]] は次のアクションを提供します。\n\n* [[yii\\rest\\IndexAction|index]]: リソースをページごとにリストする。\n* [[yii\\rest\\ViewAction|view]]: 指定されたリソースの詳細を返す。\n* [[yii\\rest\\CreateAction|create]]: 新しいリソースを作成する。\n* [[yii\\rest\\UpdateAction|update]]: 既存のリソースを更新する。\n* [[yii\\rest\\DeleteAction|delete]]: 指定されたりソースを削除する。\n* [[yii\\rest\\OptionsAction|options]]: サポートされている HTTP メソッドを返す。\n\nこれらのアクションは全て [[yii\\rest\\ActiveController::actions()|actions()]] メソッドによって宣言されます。\n`actions()` メソッドをオーバーライドすることによって、これらのアクションを構成したり、そのいくつかを無効化したりすることが出来ます。例えば、\n\n```php\npublic function actions()\n{\n    $actions = parent::actions();\n\n    // \"delete\" と \"create\" のアクションを無効にする\n    unset($actions['delete'], $actions['create']);\n\n    // データ・プロバイダの準備を \"prepareDataProvider()\" メソッドでカスタマイズする\n    $actions['index']['prepareDataProvider'] = [$this, 'prepareDataProvider'];\n\n    return $actions;\n}\n\npublic function prepareDataProvider()\n{\n    // \"index\" アクションのためにデータ・プロバイダを準備して返す\n}\n```\n\nどういう構成オプションが利用できるかを学ぶためには、個々のアクション・クラスのリファレンスを参照してください。\n\n\n### アクセス・チェックを実行する <span id=\"performing-access-check\"></span>\n\nRESTful API によってリソースを公開するときには、たいてい、\n現在のユーザがリクエストしているリソースにアクセスしたり操作したりする許可を持っているか否かをチェックする必要があります。\nこれは、[[yii\\rest\\ActiveController]] を使う場合は、[[yii\\rest\\ActiveController::checkAccess()|checkAccess()]] メソッドを次のようにオーバーライドすることによって出来ます。\n\n```php\n/**\n * 現在のユーザの特権をチェックする。\n *\n * 現在のユーザが指定されたデータ・モデルに対して指定されたアクションを実行する特権を\n * 有するか否かをチェックするためには、このメソッドをオーバーライドしなければなりません。\n * ユーザが権限をもたない場合は、[[ForbiddenHttpException]] が投げられなければなりません。\n *\n * @param string $action 実行されるアクションの ID。\n * @param \\yii\\base\\Model $model アクセスされるモデル。null の場合は、アクセスされる特定のモデルが無いことを意味する。\n * @param array $params 追加のパラメータ\n * @throws ForbiddenHttpException ユーザが権限をもたない場合\n */\npublic function checkAccess($action, $model = null, $params = [])\n{\n    // ユーザが $action と $model に対する権限を持つかどうかをチェック\n    // アクセスを拒否すべきときは ForbiddenHttpException を投げる\n    if ($action === 'update' || $action === 'delete') {\n        if ($model->author_id !== \\Yii::$app->user->id)\n            throw new \\yii\\web\\ForbiddenHttpException(sprintf('You can only %s articles that you\\'ve created.', $action));\n    }\n}\n```\n\n`checkAccess()` メソッドは [[yii\\rest\\ActiveController]] のデフォルトのアクションから呼ばれます。\n新しいアクションを作成して、それに対してもアクセス・チェックをしたい場合は、新しいアクションの中からこのメソッドを明示的に呼び出さなければなりません。\n\n> Tip: [ロール・ベース・アクセス制御 (RBAC) コンポーネント](security-authorization.md) を使って `checkAccess()` を実装することも可能です。\n"
  },
  {
    "path": "docs/guide-ja/rest-error-handling.md",
    "content": "エラー処理\n==========\n\nRESTful API リクエストを処理していて、ユーザのリクエストにエラーがあったり、何か予期しないことがサーバ上で起ったりしたときには、\n単に例外を投げて、ユーザに何かがうまく行かなかったことを知らせることも出来ます。\nしかし、エラーの原因 (例えば、リクエストされたリソースが存在しない、など) を特定することが出来るなら、\n適切な HTTP ステータス・コード (例えば、404 ステータス・コードを表わす [[yii\\web\\NotFoundHttpException]])\nと一緒に例外を投げることを検討すべきです。\nそうすれば、Yii は対応する HTTP ステータスのコードとテキストをレスポンスとともに送信します。\nYii はまた、レスポンス・ボディにも、シリアライズされた表現形式の例外を含めます。例えば、\n\n```\nHTTP/1.1 404 Not Found\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"name\": \"Not Found Exception\",\n    \"message\": \"The requested resource was not found.\",\n    \"code\": 0,\n    \"status\": 404\n}\n```\n\n次のリストは、Yii の REST フレームワークで使われる HTTP ステータス・コードの要約です。\n\n* `200`: OK。すべて期待されたとおりに動作しました。\n* `201`: `POST` リクエストに対するレスポンスとしてリソースが成功裡に作成されました。\n  `Location` ヘッダが、新しく作成されたリソースを指し示す URL を含んでいます。\n* `204`: リクエストは成功裡に処理されましたが、レスポンスはボディ・コンテントを含んでいません (`DELTE` リクエストなどの場合)。\n* `304`: リソースは修正されていません。キャッシュしたバージョンを使うことが可能です。\n* `400`: 無効なリクエストです。これはユーザのさまざまな行為によって引き起こされます。\n  例えば、リクエストのボディに無効な JSON データを入れたり、無効なアクションパラメータを指定したり、など。\n* `401`: 認証が失敗しました。\n* `403`: 認証されたユーザは指定された API エンド・ボイントにアクセスすることを許可されていません。\n* `404`: リクエストされたリソースは存在しません。\n* `405`: メソッドが許可されていません。どの HTTP メソッドが許可されているか、`Allow` ヘッダをチェックしてください。\n* `415`: サポートされていないメディア・タイプです。リクエストされたコンテント・タイプまたはバージョン番号が無効です。\n* `422`: データの検証が失敗しました (例えば `POST` リクエストに対するレスポンスで)。レスポンス・ボディで詳細なエラー・メッセージをチェックしてください。\n* `429`: リクエストの数が多すぎます。レート制限のためにリクエストが拒絶されました。\n* `500`: 内部的サーバエラー。これは内部的なプログラムエラーによって生じ得ます。\n\n\n## エラー・レスポンスをカスタマイズする <span id=\"customizing-error-response\"></span>\n\n場合によっては、デフォルトのエラー・レスポンス形式をカスタマイズしたいことがあるでしょう。\n例えば、さまざまな HTTP ステータスを使ってさまざまなエラーを示すという方法によるのではなく、次に示すように、\nHTTP ステータスとしては常に 200 を使い、実際の HTTP ステータス・コードはレスポンスの JSON 構造の一部として包み込む、という方式です。\n\n```\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"success\": false,\n    \"data\": {\n        \"name\": \"Not Found Exception\",\n        \"message\": \"The requested resource was not found.\",\n        \"code\": 0,\n        \"status\": 404\n    }\n}\n```\n\nアプリケーションの構成情報で `response` コンポーネントの `beforeSend` イベントに応答することで、この目的を達することが出来ます。\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'response' => [\n            'class' => 'yii\\web\\Response',\n            'on beforeSend' => function ($event) {\n                $response = $event->sender;\n                if ($response->data !== null && Yii::$app->request->get('suppress_response_code')) {\n                    $response->data = [\n                        'success' => $response->isSuccessful,\n                        'data' => $response->data,\n                    ];\n                    $response->statusCode = 200;\n                }\n            },\n        ],\n    ],\n];\n```\n\n上記のコードは、`suppress_response_code` が `GET` のパラメータとして渡された場合に、\nレスポンスを (成功したものも、失敗したものも) 上記で説明したように再フォーマットします。\n"
  },
  {
    "path": "docs/guide-ja/rest-filtering-collections.md",
    "content": "コレクションのフィルタリング\n============================\n\nバージョン 2.0.13 以降、リソースのコレクションは [[yii\\data\\DataFilter]] コンポーネントを使ってフィルタにかけることが出来ます。\nこのコンポーネントは、リクエスト経由で渡されるフィルタ条件の構築を可能にし、そして、拡張バージョンの [[yii\\data\\ActiveDataFilter]] の助力によって、\n[[yii\\db\\QueryInterface::where()]] にとって適切な形式でフィルタ条件を使う事を可能にします。\n\n\n## データ・プロバイダをフィルタリングのために構成する <span id=\"configuring-data-provider-for-filtering\"></span>\n\n[コレクション](rest-resources.md#collections) のセクションで言及されているように、 \n[データ・プロバイダ](output-data-providers#data-providers) を使うと、並べ替えてページ付けしたリソースのリストを出力することが出来ます。\nまた、データ・プロバイダを使って、そのリストをフィルタにかけることも出来ます。\n\n```php\n$filter = new ActiveDataFilter([\n    'searchModel' => 'app\\models\\PostSearch',\n]);\n\n$filterCondition = null;\n// どのようなソースからでもフィルタをロードすることが出来ます。例えば、\n// リクエスト・ボディの JSON からロードしたい場合は、\n// 下記のように Yii::$app->request->getBodyParams() を使います。\nif ($filter->load(Yii::$app->request->get())) { \n    $filterCondition = $filter->build();\n    if ($filterCondition === false) {\n        // シリアライザがフィルタの抽出でエラーを出すかもしれない\n        return $filter;\n    }\n}\n\n$query = Post::find();\nif ($filterCondition !== null) {\n    $query->andWhere($filterCondition);\n}\n\nreturn new ActiveDataProvider([\n    'query' => $query,\n]);\n```\n\n`PostSearch` モデルが、どのプロパティと値がフィルタリングのために許容されるかを定義する役目を担います。\n\n```php\nuse yii\\base\\Model;\n\nclass PostSearch extends Model \n{\n    public $id;\n    public $title;\n    \n    public function rules()\n    {\n        return [\n            ['id', 'integer'],\n            ['title', 'string', 'min' => 2, 'max' => 200],            \n        ];\n    }\n}\n```\n\nそこで特別なビジネス・ロジックが必要でない場合には、検索ルールのためのスタンドアロンなモデルを準備する代わりに、\n[[yii\\base\\DynamicModel]] を使うことが出来ます。\n\n```php\n$filter = new ActiveDataFilter([\n    'searchModel' => (new DynamicModel(['id', 'title']))\n        ->addRule(['id'], 'integer')\n        ->addRule(['title'], 'string', ['min' => 2, 'max' => 200]),\n]);\n```\n\n`searchModel` を定義することは、エンド・ユーザに許容するフィルタ条件を制御するために欠かすことが出来ません。\n\n\n## リクエストのフィルタリング <span id=\"filtering-request\"></span>\n\n通常、エンド・ユーザは許容された一つ以上のメソッド（これらはAPIドキュメントに明示的に記述されるべきものです）を使ってフィルタリング条件をリクエストで提供するものと期待されます。\n例えば、フィルタリングが JSON を使って POST メソッドで操作される場合は、\n下記と似たようなものになります。\n\n```json\n{\n    \"filter\": {\n        \"id\": {\"in\": [2, 5, 9]},\n        \"title\": {\"like\": \"cheese\"}\n    }\n}\n```\n\n上記の条件は、次のように解釈されます :\n- `id` は、2, 5, または 9 でなければならず、**かつD**\n- `title` は `cheese` という語を含まなければならない。\n\n同一の条件が GET クエリの一部として送信される場合は、次のようになります :\n\n```\n?filter[id][in][]=2&filter[id][in][]=5&filter[id][in][]=9&filter[title][like]=cheese\n```\n\nデフォルトの `filter` キー・ワードは、[[yii\\data\\DataFilter::$filterAttributeName]] を設定して変更することが出来ます。\n\n\n## フィルタ制御キーワード <span id=\"filter-control-keywords\"></span>\n\n許容されているフィルタ制御キーワードは下記の通りです :\n\n| キーワード     |     意味      |\n|:--------------:|:-------------:|\n|     `and`      |     `AND`     |\n|      `or`      |     `OR`      |\n|     `not`      |     `NOT`     |\n|      `lt`      |      `<`      |\n|      `gt`      |      `>`      |\n|     `lte`      |     `<=`      |\n|     `gte`      |     `>=`      |\n|      `eq`      |      `=`      |\n|     `neq`      |     `!=`      |\n|      `in`      |     `IN`      |\n|     `nin`      |   `NOT IN`    |\n|     `like`     |    `LIKE`     |\n\nオプションの [[yii\\data\\DataFilter::$filterControls]] を拡張して、上記のリストを拡張することが出来ます。\n例えば、下記のように、同一のフィルタ構築キーにいくつかのキーワードを与えて、複数のエイリアスを作成することが出来ます :\n\n```php\n[\n    'eq' => '=',\n    '=' => '=',\n    '==' => '=',\n    '===' => '=',\n    // ...\n]\n```\n\n未定義のキーワードは、すべて、フィルタ制御とは認識されず、属性名として扱われることに注意して下さい。\n制御キーワードと属性名の衝突は避けなければなりません。\n（例えば、制御キーワードとしての 'like' と属性名としての 'like' が存在する場合、そのような属性に対して条件を指定することは不可能です。）\n\n> Note: フィルタ制御を指定する時に、あなたのAPIが使用する実際のデータ交換形式に留意しましょう。\n  すべての指定された制御キーワードがその形式にとって妥当なものであることを確認して下さい。\n  例えば、XML ではタグ名は Letter クラスの文字でしか開始出来ませんから、`>`, `=`, `$gt` 等は XML スキーマに違反することになります。\n\n> Note: 新しいフィルタ制御キーワードを追加する時は、演算子の結合規則および所期の動作に基づいて、期待されるクエリ結果を得るためには\n  [[yii\\data\\DataFilter::$conditionValidators]] および/または [[yii\\data\\DataFilter::$operatorTypes]] をも\n  更新する必要があるかどうか、必ず確認して下さい。\n\n\n## Null 値の扱い <span id=\"handling-the-null-values\"></span>\n\nJSON の式野中では `null` を使う事は容易ですが、文字通りの 'null' を文字列としての \"null\" と混乱させずに GET クエリを使ってを送信することは不可能です。\nバージョン 2.0.40 以降では、[[yii\\data\\DataFilter::$nullValue]] オプションを使って、文字通りの `null` に置換される単語(デフォルトでは、\"NULL\")を構成することが出来ます。\n\n\n## 属性のエイリアス <span id=\"aliasing-attributes\"></span>\n\n属性を別の名前で呼びたい場合や、結合された DB テーブルでフィルタをかけたい場合に、\n[[yii\\data\\DataFilter::$attributeMap]] を使ってエイリアスのマップを設定することが出来ます。\n\n```php\n[\n    'carPart' => 'car_part', // car_part 属性でフィルタするために carPart が使われる\n    'authorName' => '{{author}}.[[name]]', // 結合された author テーブルの name 属性でフィルタするために authorName が使われる\n]\n```\n\n## `ActiveController` のためにフィルタを構成する <span id=\"configuring-filters-for-activecontroller\"></span>\n\n[[yii\\rest\\ActiveController]] には一般的な一揃いの REST アクションが失踪されていますが、\n[[yii\\rest\\IndexAction::$dataFilter]] プロパティによってフィルタを使うことも簡単に出来ます。\n可能な方法のうちの一つは [[yii\\rest\\ActiveController::actions()]] を使ってそうすることです :\n\n```php\npublic function actions()\n{\n    $actions = parent::actions();\n    \n    $actions['index']['dataFilter'] = [\n        'class' => \\yii\\data\\ActiveDataFilter::class,\n        'attributeMap' => [\n            'clockIn' => 'clock_in',\n        ],\n        'searchModel' => (new DynamicModel(['id', 'clockIn']))->addRule(['id', 'clockIn'], 'integer', ['min' => 1]),\n    ];\n    \n    return $actions;\n}\n```\n\nこれで(`index` アクションによってアクセス可能な)コレクションを `id` と `clockIn` プロパティによってフィルタすることが出来ます。\n"
  },
  {
    "path": "docs/guide-ja/rest-quick-start.md",
    "content": "クイック・スタート\n==================\n\nYii は、RESTful ウェブサービス API を実装する仕事を簡単にするために、一揃いのツールを提供しています。\n具体的に言えば、RESTful API に関する次の機能をサポートしています。\n\n* [アクティブ・レコード](db-active-record.md) のための共通 API をサポートした迅速なプロトタイプ作成\n* レスポンス形式のネゴシエーション (デフォルトで JSON と XML をサポート)\n* 出力フィールドの選択をサポートした、カスタマイズ可能なオブジェクトのシリアライゼーション\n* コレクション・データと検証エラーの適切な書式設定\n* コレクションのページネーション、フィルタリングおよびソーティング\n* [HATEOAS](https://en.wikipedia.org/wiki/HATEOAS) のサポート\n* HTTP 動詞を適切にチェックする効率的なルーティング\n* `OPTIONS` および `HEAD` 動詞のサポートを内蔵\n* 認証と権限付与\n* データ・キャッシュと HTTP キャッシュ\n* レート制限\n\n\n以下においては、例を使って、どのようにして最小限のコーディング労力で一組の RESTful API を構築することが出来るかを説明します。\n\nユーザのデータを RESTful API によって公開したいと仮定しましょう。\nユーザのデータは `user` という DB テーブルに保存されており、それにアクセスするための [アクティブ・レコード](db-active-record.md) クラス `app\\models\\User` が既に作成済みであるとします。\n\n\n## コントローラを作成する <span id=\"creating-controller\"></span>\n\n最初に、[コントローラ](structure-controllers.md)・クラス `app\\controllers\\UserController` を次のようにして作成します。\n\n```php\nnamespace app\\controllers;\n\nuse yii\\rest\\ActiveController;\n\nclass UserController extends ActiveController\n{\n    public $modelClass = 'app\\models\\User';\n}\n```\n\nこのコントローラ・クラスは、よく使用される一揃いの RESTful アクションを実装した [[yii\\rest\\ActiveController]] を拡張するものです。\n[[yii\\rest\\ActiveController::modelClass|modelClass]] として `app\\models\\User` が指定されているため、\nコントローラがどのモデルを使用してデータの取得と操作が出来るかがわかります。\n\n\n## URL 規則を構成する <span id=\"configuring-url-rules\"></span>\n\n次に、アプリケーションの構成情報において、`urlManager` コンポーネントの構成情報を修正します。\n\n```php\n'urlManager' => [\n    'enablePrettyUrl' => true,\n    'enableStrictParsing' => true,\n    'showScriptName' => false,\n    'rules' => [\n        ['class' => 'yii\\rest\\UrlRule', 'controller' => 'user'],\n    ],\n]\n```\n\n上記の構成情報は、主として、`user` コントローラの URL 規則を追加して、ユーザのデータが綺麗な URL と意味のある\nHTTP 動詞によってアクセスおよび操作できるようにするものです。\n\n> Info: Yii はコントローラの名前を自動的に複数形にしてエンド・ボイントとして使用します (下の「試してみる」(#trying-it-out) を参照してください)。\n> この振る舞いは [[yii\\rest\\UrlRule::$pluralize]] プロパティを使って構成することが可能です。\n\n\n## JSON の入力を可能にする <span id=\"enabling-json-input\"></span>\n\nAPI が JSON 形式で入力データを受け取ることが出来るように、`request` [アプリケーション・コンポーネント](structure-application-components.md) の\n[[yii\\web\\Request::$parsers|parsers]] プロパティを構成して、JSON 入力のために [[yii\\web\\JsonParser]] を使うようにします。\n\n```php\n'request' => [\n    'parsers' => [\n        'application/json' => 'yii\\web\\JsonParser',\n    ]\n]\n```\n\n> Info: 上記の構成はオプションです。\n  上記のように構成しない場合は、API は `application/x-www-form-urlencoded` と `multipart/form-data` だけを入力形式として認識します。\n\n\n## 試してみる <span id=\"trying-it-out\"></span>\n\n上記で示した最小限の労力によって、ユーザのデータにアクセスする RESTful API を作成する仕事は既に完成しています。\n作成した API は次のものを含みます。\n\n* `GET /users`: 全てのユーザをページごとに一覧する\n* `HEAD /users`: ユーザ一覧の概要を示す\n* `POST /users`: 新しいユーザを作成する\n* `GET /users/123`: ユーザ 123 の詳細を返す\n* `HEAD /users/123`: ユーザ 123 の概要を示す\n* `PATCH /users/123` と `PUT /users/123`: ユーザ 123 を更新する\n* `DELETE /users/123`: ユーザ 123 を削除する\n* `OPTIONS /users`: エンド・ボイント `/users` に関してサポートされている動詞を示す\n* `OPTIONS /users/123`: エンド・ボイント `/users/123` に関してサポートされている動詞を示す\n\n作成した API は、次のように、`curl` コマンドでアクセスすることが出来ます。\n\n```\n$ curl -i -H \"Accept:application/json\" \"http://localhost/users\"\n\nHTTP/1.1 200 OK\n...\nX-Pagination-Total-Count: 1000\nX-Pagination-Page-Count: 50\nX-Pagination-Current-Page: 1\nX-Pagination-Per-Page: 20\nLink: <http://localhost/users?page=1>; rel=self, \n      <http://localhost/users?page=2>; rel=next, \n      <http://localhost/users?page=50>; rel=last\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n[\n    {\n        \"id\": 1,\n        ...\n    },\n    {\n        \"id\": 2,\n        ...\n    },\n    ...\n]\n```\n\n受入れ可能なコンテント・タイプを `application/xml` に変更してみてください。\nすると、結果が XML 形式で返されます。\n\n```\n$ curl -i -H \"Accept:application/xml\" \"http://localhost/users\"\n\nHTTP/1.1 200 OK\n...\nX-Pagination-Total-Count: 1000\nX-Pagination-Page-Count: 50\nX-Pagination-Current-Page: 1\nX-Pagination-Per-Page: 20\nLink: <http://localhost/users?page=1>; rel=self, \n      <http://localhost/users?page=2>; rel=next, \n      <http://localhost/users?page=50>; rel=last\nTransfer-Encoding: chunked\nContent-Type: application/xml\n\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<response>\n    <item>\n        <id>1</id>\n        ...\n    </item>\n    <item>\n        <id>2</id>\n        ...\n    </item>\n    ...\n</response>\n```\n\n次のコマンドは、JSON 形式でユーザのデータを持つ POST リクエストを送信して、新しいユーザを作成します。\n\n```\n$ curl -i -H \"Accept:application/json\" -H \"Content-Type:application/json\" \\\n    -XPOST \"http://localhost/users\" \\\n    -d '{\"username\": \"example\", \"email\": \"user@example.com\"}'\n\nHTTP/1.1 201 Created\n...\nLocation: http://localhost/users/1\nContent-Length: 99\nContent-Type: application/json; charset=UTF-8\n\n{\"id\":1,\"username\":\"example\",\"email\":\"user@example.com\",\"created_at\":1414674789,\"updated_at\":1414674789}\n```\n\n> Tip: URL `http://localhost/users` を入力すれば、ウェブ・ブラウザ経由で API にアクセスすることも出来ます。\n  ただし、特殊なリクエスト・ヘッダを送信するためには、何らかのブラウザプラグインが必要になるでしょう。\n\nご覧のように、レスポンス・ヘッダの中には、総ユーザ数やページ数などの情報が書かれています。\nまた、データの他のページへナビゲートすることを可能にするリンクもあります。\n例えば、`http://localhost/users?page=2` にアクセスすれば、ユーザのデータの次のページを取得することが出来ます。\n\n`fields` と `expand` パラメータを使えば、どのフィールドが結果に含まれるべきかを指定することも出来ます。\n例えば、URL `http://localhost/users?fields=id,email` は、`id` と `email` のフィールドだけを返します。\n\n\n> Info: 気がついたかも知れませんが、`http://localhost/users` の結果は、いくつかの公開すべきでないフィールド、例えば `password_hash` や `auth_key` を含んでいます。\n> 当然ながら、これらが API の結果に出現することは避けたいでしょう。\n> [リソース](rest-resources.md) のセクションで説明されているように、これらのフィールドを除外することは出来ますし、また、除外しなければなりません。\n\nさらに、`http://localhost/users?sort=email` や `http://localhost/users?sort=-email` のように、コレクションをソートすることも出来ます。\n`http://localhost/users?filter[id]=10` や `http://localhost/users?filter[email][like]=gmail.com` のように、\nコレクションをフィルタリングすることも、データ・フィルターを使って実装することが出来ます。\n詳細は、[リソース](rest-resources.md#filtering-collections) のセクションを参照して下さい。\n\n\n## まとめ <span id=\"summary\"></span>\n\nYii の RESTful API フレームワークを使う場合は、API エンド・ボイントをコントローラ・アクションの形式で実装します。\nそして、コントローラを使って、単一タイプのリソースに対するエンド・ボイントを実装するアクションを編成します。\n\nリソースは [[yii\\base\\Model]] クラスを拡張するデータ・モデルとして表現されます。\nデータベース (リレーショナルまたは NoSQL) を扱っている場合は、[[yii\\db\\ActiveRecord|ActiveRecord]]\nを使ってリソースを表現することが推奨されます。\n\n[[yii\\rest\\UrlRule]] を使って API エンド・ボイントへのルーティングを簡単にすることが出来ます。\n\nこれは要求されてはいませんが、RESTful API は、保守を容易にするために、\nウェブのフロントエンドやバックエンドとは別の独立したアプリケーションとして開発することが推奨されます。\n"
  },
  {
    "path": "docs/guide-ja/rest-rate-limiting.md",
    "content": "レート制限\n==========\n\n悪用を防止するために、あなたの API に *レート制限* を加えることを検討すべきです。\n例えば、各ユーザの API 使用を 10 分間で最大 100 回までの API 呼び出しに制限したいとしましょう。\nユーザから上記の期間内に多すぎるリクエストを受け取った場合は、ステータス・コード 429 (「リクエストが多すぎる」の意味) を持つレスポンスを返さなければなりません。\n\nレート制限を可能にするためには、[[yii\\web\\User::identityClass|ユーザ・アイデンティティ・クラス]] で [[yii\\filters\\RateLimitInterface]] を実装しなければなりません。\nこのインタフェイスは次の三つのメソッドを実装することを要求します。\n\n* `getRateLimit()`: 許可されているリクエストの最大数と期間を返します\n  (例えば、`[100, 600]` は 600 秒間に最大 100 回の API 呼び出しが出来ることを意味します)。\n* `loadAllowance()`: 許可されているリクエストの残り数と、レート制限が最後にチェックされたときの対応する UNIX タイムスタンプを返します。\n* `saveAllowance()`: 許可されているリクエストの残り数と現在の UNIX タイムスタンプの両方を保存します。\n\nユーザ・テーブルに二つのカラムを追加して、許容されているリクエスト数とタイムスタンプの情報を記録するのが良いでしょう。\nそれらを定義すれば、`loadAllowance()` と `saveAllowance()` は、\n認証された現在のユーザに対応する二つのカラムの値を読み書きするものとして実装することが出来ます。\nパフォーマンスを向上させるために、これらの情報をキャッシュや NoSQL ストレージに保存することを検討しても構いません。\n\n`User` モデルにおける実装は次のようなものになります。\n\n```php\npublic function getRateLimit($request, $action)\n{\n    return [$this->rateLimit, 1]; // 1秒間に $rateLimit 回のリクエスト\n}\n\npublic function loadAllowance($request, $action)\n{\n    return [$this->allowance, $this->allowance_updated_at];\n}\n\npublic function saveAllowance($request, $action, $allowance, $timestamp)\n{\n    $this->allowance = $allowance;\n    $this->allowance_updated_at = $timestamp;\n    $this->save();\n}\n```\n\nアイデンティティのクラスに必要なインタフェイスを実装すると、Yii は [[yii\\rest\\Controller]] のアクション・フィルタとして構成された\n[[yii\\filters\\RateLimiter]] を使って、自動的にレート制限のチェックを行うようになります。\nレート制限を超えると、レート・リミッタが [[yii\\web\\TooManyRequestsHttpException]] を投げます。\n\nレート・リミッタは、REST コントローラ・クラスの中で、\n次のようにして構成することが出来ます。\n\n```php\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n    $behaviors['rateLimiter']['enableRateLimitHeaders'] = false;\n    return $behaviors;\n}\n```\n\nレート制限が有効にされると、デフォルトでは、送信される全てのレスポンスに、\n現在のレート制限の情報を含む次の HTTP ヘッダが付加されます。\n\n* `X-Rate-Limit-Limit` - 一定期間内に許可されるリクエストの最大数\n* `X-Rate-Limit-Remaining` - 現在の期間において残っている許可されているリクエスト数\n* `X-Rate-Limit-Reset` - 許可されているリクエストの最大数にリセットされるまで待たなければならない秒数\n\nこれらのヘッダは、上記のコード例で示されているように、[[yii\\filters\\RateLimiter::enableRateLimitHeaders]] を\n`false` に設定することで無効にすることが出来ます。\n"
  },
  {
    "path": "docs/guide-ja/rest-resources.md",
    "content": "リソース\n========\n\nRESTful API は、つまるところ、*リソース* にアクセスし、それを操作するものです。\nMVC の枠組の中では、リソースは [モデル](structure-models.md) として見ることが出来ます。\n\nリソースをどのように表現すべきかについて制約がある訳ではありませんが、\nYii においては、通常は、次のような理由によって、リソースを [[yii\\base\\Model]] またはその子クラス (例えば [[yii\\db\\ActiveRecord]])\nのオブジェクトとして表現することになります。\n\n* [[yii\\base\\Model]] は [[yii\\base\\Arrayable]] インタフェイスを実装しています。\n  これによって、リソースのデータを RESTful API を通じて公開する仕方をカスタマイズすることが出来ます。\n* [[yii\\base\\Model]] は [入力値の検証](input-validation.md) をサポートしています。\n  これは、RESTful API がデータ入力をサポートする必要がある場合に役に立ちます。\n* [[yii\\db\\ActiveRecord]] は DB データのアクセスと操作に対する強力なサポートを提供しています。\n  リソース・データがデータベースに保存されているときは、アクティブ・レコードが最適の選択です。\n\nこのセクションでは、主として、[[yii\\base\\Model]] クラス (またはその子クラス) から拡張したリソース・クラスにおいて、\nRESTful API を通じて返すことが出来るデータを指定する方法を説明します。\nリソース・クラスが [[yii\\base\\Model]] から拡張したものでない場合は、全てのパブリックなメンバ変数が返されます。\n\n\n## フィールド <span id=\"fields\"></span>\n\nRESTful API のレスポンスにリソースを含めるとき、リソースは文字列にシリアライズされる必要があります。\nYii はこのプロセスを二つのステップに分けます。\n最初に、リソースは [[yii\\rest\\Serializer]] によって配列に変換されます。\n次に、その配列が [[yii\\web\\ResponseFormatterInterface|レスポンス・フォーマッタ]] によって、リクエストされた形式 (例えば JSON や XML) の文字列にシリアライズされます。\nリソース・クラスを開発するときに主として力を注ぐべきなのは、最初のステップです。\n\n[[yii\\base\\Model::fields()|fields()]] および/または [[yii\\base\\Model::extraFields()|extraFields()]] をオーバーライドすることによって、\nリソースのどういうデータ (*フィールド* と呼ばれます) を配列表現に入れることが出来るかを指定することが出来ます。\nこの二つのメソッドの違いは、前者が配列表現に含まれるべきフィールドのデフォルトのセットを指定するのに対して、\n後者はエンド・ユーザが `expand` クエリ・パラメータで要求したときに配列に含めることが出来る追加のフィールドを指定する、\nという点にあります。例えば、\n\n```\n// fields() で宣言されている全てのフィールドを返す。\nhttp://localhost/users\n\n// \"id\" と \"email\" のフィールドだけを返す (ただし、fields() で宣言されていれば) 。\nhttp://localhost/users?fields=id,email\n\n// fields() の全てのフィールドと \"profile\" のフィールドを返す (ただし、\"profile\" が extraFields() で宣言されていれば)。\nhttp://localhost/users?expand=profile\n\n// fields() の全てのフィールドと post の \"author\" フィールドを返す\n// (ただし、\"author\" が post モデルの extraFields() にあれば）。\nhttp://localhost/comments?expand=post.author\n\n// \"id\" と\"email\" (ただし、fileds() で宣言されていれば) と\n// \"profile\" (ただし、extraFields() で宣言されていれば) を返す。\nhttp://localhost/users?fields=id,email&expand=profile\n```\n\n### fields()` をオーバーライドする <span id=\"overriding-fields\"></span>\n\nデフォルトでは、[[yii\\base\\Model::fields()]] は、モデルの全ての属性をフィールドとして返し、\n[[yii\\db\\ActiveRecord::fields()]] は、DB から投入された属性だけを返します。\n\n`fields()` をオーバーライドして、フィールドを追加、削除、名前変更、または再定義することが出来ます。\n`fields()` の返り値は配列でなければなりません。\n配列のキーはフィールド名であり、配列の値は対応するフィールドの定義です。\nフィールドの定義は、プロパティ/属性の名前か、あるいは、対応するフィールドの値を返す無名関数とすることが出来ます。\nフィールド名がそれを定義する属性名と同一であるという特殊な場合においては、配列のキーを省略することが出来ます。例えば、\n\n```php\n// 明示的に全てのフィールドをリストする方法。(API の後方互換性を保つために) DB テーブルやモデル属性の\n// 変更がフィールドの変更を引き起こさないことを保証したい場合に適している。\npublic function fields()\n{\n    return [\n        // フィールド名が属性名と同じ\n        'id',\n        // フィールド名は \"email\"、対応する属性名は \"email_address\"\n        'email' => 'email_address',\n        // フィールド名は \"name\"、その値は PHP コールバックで定義\n        'name' => function ($model) {\n            return $model->first_name . ' ' . $model->last_name;\n        },\n    ];\n}\n\n// いくつかのフィールドを除去する方法。親の実装を継承しつつ、\n// 公開すべきでないフィールドを除外したいときに適している。\npublic function fields()\n{\n    $fields = parent::fields();\n\n    // 公開すべきでない情報を含むフィールドを削除する\n    unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);\n\n    return $fields;\n}\n```\n\n> Warning: デフォルトではモデルの全ての属性がエクスポートされる配列に含まれるため、データを精査して、\n> 公開すべきでない情報が含まれていないことを確認すべきです。そういう情報がある場合は、\n> `fields()` をオーバーライドして、除去すべきです。上記の例では、`auth_key`、`password_hash`\n> および `password_reset_token` を選んで除去しています。\n\n\n### `extraFields()` をオーバーライドする<span id=\"overriding-extra-fields\"></span>\n\nデフォルトでは、[[yii\\base\\Model::extraFields()]] は空の配列を返し、[[yii\\db\\ActiveRecord::extraFields()]]\nは DB から取得されたリレーションの名前を返します。\n\n`extraFields()` によって返されるデータの形式は `fields()` のそれと同じです。\n通常、`extraFields()` は、主として、値がオブジェクトであるフィールドを指定するのに使用されます。\n例えば、次のようなフィールドの宣言があるとしましょう。\n\n```php\npublic function fields()\n{\n    return ['id', 'email'];\n}\n\npublic function extraFields()\n{\n    return ['profile'];\n}\n```\n\n`http://localhost/users?fields=id,email&expand=profile` というリクエストは、次のような JSON データを返すことが出来ます。\n\n```php\n[\n    {\n        \"id\": 100,\n        \"email\": \"100@example.com\",\n        \"profile\": {\n            \"id\": 100,\n            \"age\": 30,\n        }\n    },\n    ...\n]\n```\n\n\n## リンク <span id=\"links\"></span>\n\n[HATEOAS](https://en.wikipedia.org/wiki/HATEOAS) は、Hypermedia as the Engine of Application State (アプリケーション状態のエンジンとしてのハイパーメディア) の略称です。\nHATEOAS は、RESTful API は自分が返すリソースについて、どのようなアクションがサポートされているかをクライアントが発見できるような情報を返すべきである、という概念です。\nHATEOAS のキーポイントは、リソース・データが API によって提供されるときには、\n関連する情報を一群のハイパーリンクによって返すべきである、ということです。\n\nあなたのリソース・クラスは、[[yii\\web\\Linkable]] インタフェイスを実装することによって、HATEOAS をサポートすることが出来ます。\nこのインタフェイスは、[[yii\\web\\Link|リンク]] のリストを返すべき [[yii\\web\\Linkable::getLinks()|getLinks()]] メソッド一つだけを含みます。\n典型的には、少なくとも、リソース・オブジェクトそのものへの URL を表現する `self` リンクを返さなければなりません。例えば、\n\n```php\nuse yii\\base\\Model;\nuse yii\\web\\Link; // JSON ハイパーメディア API 言語に定義されているリンク・オブジェクトを表す\nuse yii\\web\\Linkable;\nuse yii\\helpers\\Url;\n\nclass UserResource extends Model implements Linkable\n{\n    public $id;\n    public $email;\n\n    //...\n\n    public function fields()\n    {\n        return ['id', 'email'];\n    }\n\n    public function extraFields()\n    {\n        return ['profile'];\n    }\n\n    public function getLinks()\n    {\n        return [\n            Link::REL_SELF => Url::to(['user/view', 'id' => $this->id], true),\n            'edit' => Url::to(['user/view', 'id' => $this->id], true),\n            'profile' => Url::to(['user/profile/view', 'id' => $this->id], true),\n            'index' => Url::to(['users'], true),\n        ];\n    }\n}\n```\n\n`UserResource` オブジェクトがレスポンスで返されるとき、レスポンスはそのユーザに関連するリンクを表現する `_links` 要素を含むことになります。\n例えば、\n\n```\n{\n    \"id\": 100,\n    \"email\": \"user@example.com\",\n    // ...\n    \"_links\" => {\n        \"self\": {\n            \"href\": \"https://example.com/users/100\"\n        },\n        \"edit\": {\n            \"href\": \"https://example.com/users/100\"\n        },\n        \"profile\": {\n            \"href\": \"https://example.com/users/profile/100\"\n        },\n        \"index\": {\n            \"href\": \"https://example.com/users\"\n        }\n    }\n}\n```\n\n\n## コレクション <span id=\"collections\"></span>\n\nリソース・オブジェクトは *コレクション* としてグループ化することが出来ます。\n各コレクションは、同じ型のリソースのリストを含みます。\n\nコレクションは配列として表現することも可能ですが、通常は、[データ・プロバイダ](output-data-providers.md) として表現する方がより望ましい方法です。\nこれは、データ・プロバイダがリソースの並べ替えとページネーションをサポートしているからです。\n並べ替えとページネーションは、コレクションを返す RESTful API にとっては、普通に必要とされる機能です。\n例えば、次のアクションは投稿のリソースについてデータ・プロバイダを返すものです。\n\n```php\nnamespace app\\controllers;\n\nuse yii\\rest\\Controller;\nuse yii\\data\\ActiveDataProvider;\nuse app\\models\\Post;\n\nclass PostController extends Controller\n{\n    public function actionIndex()\n    {\n        return new ActiveDataProvider([\n            'query' => Post::find(),\n        ]);\n    }\n}\n```\n\nデータ・プロバイダが RESTful API のレスポンスで送信される場合は、[[yii\\rest\\Serializer]]\nが現在のページのリソースを取り出して、リソース・オブジェクトの配列としてシリアライズします。\nそれだけでなく、[[yii\\rest\\Serializer]] は次の HTTP ヘッダを使ってページネーション情報もレスポンスに含めます。\n\n* `X-Pagination-Total-Count`: リソースの総数\n* `X-Pagination-Page-Count`: ページ数\n* `X-Pagination-Current-Page`: 現在のページ (1 から始まる)\n* `X-Pagination-Per-Page`: 各ページのリソース数\n* `Link`: クライアントがリソースをページごとにたどることが出来るようにするための一群のナビゲーションリンク\n\nREST API におけるコレクションはデータ・プロバイダであるため、データ・プロバイダの全ての機能、すなわち、ページネーションやソーティングを共有しています。\n\nその一例を [クイック・スタート](rest-quick-start.md#trying-it-out) のセクションで見ることが出来ます。\n"
  },
  {
    "path": "docs/guide-ja/rest-response-formatting.md",
    "content": "レスポンス形式の設定\n====================\n\nRESTful API のリクエストを処理するとき、アプリケーションは、通常、レスポンス形式の設定に関して次のステップを踏みます。\n\n1. レスポンス形式に影響しうるさまざまな要因、例えば、メディア・タイプ、言語、バージョンなどを決定します。\n   このプロセスは [コンテント・ネゴシエーション](https://en.wikipedia.org/wiki/Content_negotiation) としても知られるものです。\n2. リソース・オブジェクトを配列に変換します。\n   [リソース](rest-resources.md) のセクションで説明したように、この作業は [[yii\\rest\\Serializer]] によって実行されます。\n3. 配列をコンテント・ネゴシエーションのステップで決定された形式の文字列に変換します。\n   この作業は、`response` [アプリケーション・コンポーネント](structure-application-components.md) の [[yii\\web\\Response::formatters|formatters]] プロパティに登録された [[yii\\web\\ResponseFormatterInterface|レスポンス・フォーマッタ]] によって実行されます。\n\n## コンテント・ネゴシエーション <span id=\"content-negotiation\"></span>\n\nYii は [[yii\\filters\\ContentNegotiator]] フィルタによってコンテント・ネゴシエーションをサポートします。\nRESTful API の基底コントローラ・クラス [[yii\\rest\\Controller]] は `contentNegotiator` という名前でこのフィルタを持っています。\nこのフィルタは、レスポンス形式のネゴシエーションと同時に言語のネゴシエーションも提供します。\n例えば、RESTful API リクエストが下記のヘッダを含んでいるとします。\n\n```\nAccept: application/json; q=1.0, */*; q=0.1\n```\n\nこの場合、リクエストは JSON 形式のレスポンスを受け取ることになります。例えば、次のような具合です。\n\n```\n$ curl -i -H \"Accept: application/json; q=1.0, */*; q=0.1\" \"http://localhost/users\"\n\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nX-Powered-By: PHP/5.4.20\nX-Pagination-Total-Count: 1000\nX-Pagination-Page-Count: 50\nX-Pagination-Current-Page: 1\nX-Pagination-Per-Page: 20\nLink: <http://localhost/users?page=1>; rel=self,\n      <http://localhost/users?page=2>; rel=next,\n      <http://localhost/users?page=50>; rel=last\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n[\n    {\n        \"id\": 1,\n        ...\n    },\n    {\n        \"id\": 2,\n        ...\n    },\n    ...\n]\n```\n\n舞台裏では、RESTful API コントローラ・アクションが実行される前に、[[yii\\filters\\ContentNegotiator]] フィルタがリクエストの `Accept` HTTP ヘッダをチェックして、[[yii\\web\\Response::format|レスポンス形式]] を `'json'` に設定します。\nアクションが実行されて、その結果のリソースのオブジェクトまたはコレクションが返されると、[[yii\\rest\\Serializer]] が結果を配列に変換します。\nそして最後に、[[yii\\web\\JsonResponseFormatter]] が配列を JSON 文字列に変換して、それをレスポンス・ボディに入れます。\n\nデフォルトでは、RESTful API は JSON と XML の両方の形式をサポートします。\n新しい形式をサポートするためには、下記のように、API コントローラ・クラスの中で `contentNegotiator` フィルタの [[yii\\filters\\ContentNegotiator::formats|formats]] プロパティを構成しなければなりません。\n\n```php\nuse yii\\web\\Response;\n\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n    $behaviors['contentNegotiator']['formats']['text/html'] = Response::FORMAT_HTML;\n    return $behaviors;\n}\n```\n\n`formats` プロパティのキーはサポートされる MIME タイプであり、値は対応するレスポンス形式名です。\nこのレスポンス形式名は、[[yii\\web\\Response::formatters]] の中でサポートされているものでなければなりません。\n\n\n## データのシリアライズ <span id=\"data-serializing\"></span>\n\n上記で説明したように、[[yii\\rest\\Serializer]] が、リソースのオブジェクトやコレクションを配列に変換する際に、中心的な役割を果たします。\n`Serializer` は、[[yii\\base\\Arrayable]] および [[yii\\data\\DataProviderInterface]] のインタフェイスを実装したオブジェクトを認識します。\n前者は主としてリソース・オブジェクトによって実装され、後者はリソース・コレクションによって実装されています。\n\n[[yii\\rest\\Controller::serializer]] プロパティに構成情報配列をセットしてシリアライザを構成することが出来ます。\n例えば、場合によっては、クライアントの開発作業を単純化するために、ページネーション情報をレスポンス・ボディに直接に含ませたいことがあるでしょう。\nそうするためには、[[yii\\rest\\Serializer::collectionEnvelope]] プロパティを次のように構成します。\n\n```php\nuse yii\\rest\\ActiveController;\n\nclass UserController extends ActiveController\n{\n    public $modelClass = 'app\\models\\User';\n    public $serializer = [\n        'class' => 'yii\\rest\\Serializer',\n        'collectionEnvelope' => 'items',\n    ];\n}\n```\n\nこのようにすると、`http://localhost/users` というリクエストに対して、次のレスポンスを得ることが出来ます。\n\n```\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nX-Powered-By: PHP/5.4.20\nX-Pagination-Total-Count: 1000\nX-Pagination-Page-Count: 50\nX-Pagination-Current-Page: 1\nX-Pagination-Per-Page: 20\nLink: <http://localhost/users?page=1>; rel=self,\n      <http://localhost/users?page=2>; rel=next,\n      <http://localhost/users?page=50>; rel=last\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"items\": [\n        {\n            \"id\": 1,\n            ...\n        },\n        {\n            \"id\": 2,\n            ...\n        },\n        ...\n    ],\n    \"_links\": {\n        \"self\": {\n            \"href\": \"http://localhost/users?page=1\"\n        },\n        \"next\": {\n            \"href\": \"http://localhost/users?page=2\"\n        },\n        \"last\": {\n            \"href\": \"http://localhost/users?page=50\"\n        }\n    },\n    \"_meta\": {\n        \"totalCount\": 1000,\n        \"pageCount\": 50,\n        \"currentPage\": 1,\n        \"perPage\": 20\n    }\n}\n```\n\n### JSON 出力を制御する\n\nJSON 形式のレスポンスを生成する [[yii\\web\\JsonResponseFormatter|JsonResponseFormatter]] クラスは [[yii\\helpers\\Json|JSON ヘルパ]] を内部的に使用します。\nこのフォーマッタはさまざまなオプションによって構成することが可能です。\n例えば、[[yii\\web\\JsonResponseFormatter::$prettyPrint|$prettyPrint]] オプションは、より読みやすいレスポンスのためのもので、開発時に有用なオプションです。\nまた、[[yii\\web\\JsonResponseFormatter::$encodeOptions|$encodeOptions]] によって JSON エンコーディングの出力を制御することが出来ます。\n\nフォーマッタは、以下のように、アプリケーションの [構成情報](concept-configurations.md) の中で、`response` アプリケーション・コンポーネントの [[yii\\web\\Response::formatters|formatters]] プロパティの中で構成することが出来ます。\n\n```php\n'response' => [\n    // ...\n    'formatters' => [\n        \\yii\\web\\Response::FORMAT_JSON => [\n            'class' => 'yii\\web\\JsonResponseFormatter',\n            'prettyPrint' => YII_DEBUG, // デバッグ・モードでは \"きれい\" な出力を使用\n            'encodeOptions' => JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE,\n            // ...\n        ],\n    ],\n],\n```\n\n[DAO](db-dao.md) データベース・レイヤを使ってデータベースからデータを返す場合は、全てのデータが文字列として表されます。\nしかし、特に数値は JSON では数として表現されなければなりませんので、これは必ずしも期待通りの結果であるとは言えません。\n一方、ActiveRecord レイヤを使ってデータベースからデータを取得する場合は、数値カラムの値は、[[yii\\db\\ActiveRecord::populateRecord()]] においてデータベースからデータが取得される際に、整数に変換されます。\n"
  },
  {
    "path": "docs/guide-ja/rest-routing.md",
    "content": "ルーティング\n============\n\nリソースとコントローラのクラスが準備できたら、通常のウェブ・アプリケーションと同じように、\n`http://localhost/index.php?r=user/create` というような URL を使ってリソースにアクセスすることが出来ます。\n\n実際には、綺麗な URL を有効にして HTTP 動詞を利用したいというのが普通でしょう。\n例えば、`POST /users` というリクエストが `user/create` アクションへのアクセスを意味するようにする訳です。\nこれは、アプリケーションの構成情報で `urlManager` [アプリケーション・コンポーネント](structure-application-components.md)\nを次のように構成することによって容易に達成することが出来ます。\n\n```php\n'urlManager' => [\n    'enablePrettyUrl' => true,\n    'enableStrictParsing' => true,\n    'showScriptName' => false,\n    'rules' => [\n        ['class' => 'yii\\rest\\UrlRule', 'controller' => 'user'],\n    ],\n]\n```\n\nウェブ・アプリケーションの URL 管理と比べたときに、上記で目に付く新しいことは、\nRESTful API リクエストのルーティングに [[yii\\rest\\UrlRule]] を使用していることです。\nこの特殊な URL 規則クラスが、一揃いの子 URL 規則を作成して、指定されたコントローラのルーティングと URL 生成をサポートします。\n例えば、上記のコードは、おおむね下記の規則と等価です。\n\n```php\n[\n    'PUT,PATCH users/<id>' => 'user/update',\n    'DELETE users/<id>' => 'user/delete',\n    'GET,HEAD users/<id>' => 'user/view',\n    'POST users' => 'user/create',\n    'GET,HEAD users' => 'user/index',\n    'users/<id>' => 'user/options',\n    'users' => 'user/options',\n]\n```\n\nそして、次の API エンド・ボイントがこの規則によってサポートされます。\n\n* `GET /users`: 全てのユーザをページごとにリストする。\n* `HEAD /users`: ユーザ一覧の概要を示す。\n* `POST /users`: 新しいユーザを作成する。\n* `GET /users/123`: ユーザ 123 の詳細を返す。\n* `HEAD /users/123`: ユーザ 123 の概要情報を示す。\n* `PATCH /users/123` と `PUT /users/123`: ユーザ 123 を更新する。\n* `DELETE /users/123`: ユーザ 123 を削除する。\n* `OPTIONS /users`: エンド・ボイント `/users` に関してサポートされる動詞を示す。\n* `OPTIONS /users/123`: エンド・ボイント `/users/123` に関してサポートされる動詞を示す。\n\n`only` および `except` オプションを構成すると、それぞれ、どのアクションをサポートするか、\nまた、どのアクションを無効にするかを明示的に指定することが出来ます。例えば、\n\n```php\n[\n    'class' => 'yii\\rest\\UrlRule',\n    'controller' => 'user',\n    'except' => ['delete', 'create', 'update'],\n],\n```\n\nまた、`patterns` あるいは `extraPatterns` を構成して、既存のパターンを再定義したり、この規則によってサポートされる新しいパターンを追加したりすることも出来ます。\n例えば、エンド・ボイント `GET /users/search` によって新しいアクション `search` をサポートするためには、`extraPatterns` オプションを次のように構成します。\n\n```php\n[\n    'class' => 'yii\\rest\\UrlRule',\n    'controller' => 'user',\n    'extraPatterns' => [\n        'GET search' => 'search',\n    ],\n]\n```\n\nエンド・ボイントの URL ではコントローラ ID `user` が `users` という複数形で出現していることに気が付いたかもしれません。\nこれは、[[yii\\rest\\UrlRule]] が子 URL 規則を作るときに、コントローラの ID を自動的に複数形にするためです。\nこの振舞いは [[yii\\rest\\UrlRule::pluralize]] を `false` に設定することで無効にすることが出来ます。\n\n> Info: コントローラ ID の複数形化は [[yii\\helpers\\Inflector::pluralize()]] によって行われます。\n  このメソッドは特殊な複数形の規則を考慮します。例えば、`box` という単語の複数形は `boxs` ではなく `boxes` になります。\n\n自動的な複数形化があなたの要求を満たさない場合は、[[yii\\rest\\UrlRule::controller]] プロパティを構成して、\nエンド・ボイント URL で使用される名前とコントローラ ID の対応を明示的に指定することも可能です。\n例えば、次のコードはエンド・ボイント名 `u` をコントローラ ID `user` に割り当てます。\n \n```php\n[\n    'class' => 'yii\\rest\\UrlRule',\n    'controller' => ['u' => 'user'],\n]\n```\n\n## 内蔵の規則に対する追加の構成\n\n[[yii\\rest\\UrlRule]] の中に含まれるそれぞれの規則に対して適用される追加の構成を指定すると役に立つことがあります。\n`expand` パラメータに対するデフォルトの指定が良い例です。\n\n```php\n[\n    'class' => 'yii\\rest\\UrlRule',\n    'controller' => ['user'],\n    'ruleConfig' => [\n        'class' => 'yii\\web\\UrlRule',\n        'defaults' => [\n            'expand' => 'profile',\n        ]\n    ],\n],\n```\n"
  },
  {
    "path": "docs/guide-ja/rest-versioning.md",
    "content": "バージョン管理\n==============\n\n良い API は*バージョン管理* されています。すなわち、一つのバージョンを絶え間なく変更するのではなく、変更と新機能は API の新しいバージョンにおいて実装されます。\nクライアント・サイドとサーバ・サイドの両方のコードを完全に制御できるウェブ・アプリケーションとは違って、API はあなたの制御が及ばないクライアントによって使用されることを想定したものです。このため、API の後方互換性 (BC) は、可能な限り保たれなければなりません。\nBC を損なうかも知れない変更が必要な場合は、それを API の新しいバージョンにおいて導入し、バージョン番号を上げるべきです。そうすれば、既存のクライアントは、API の古いけれども動作するバージョンを使い続けることが出来ますし、新しいまたはアップグレードされたクライアントは、新しい API バージョンで新しい機能を使うことが出来ます。\n\n> Tip: API のバージョン番号の設計についての詳細な情報は\n  [Semantic Versioning](https://semver.org/) を参照してください。\n\nAPI のバージョン管理を実装する方法としてよく使われるのは、バージョン番号を API の URL に埋め込む方法です。\n例えば、`https://example.com/v1/users` が API バージョン 1 の `/users` エンド・ボイントを指す、というものです。\n\nAPI のバージョン管理のもう一つの方法は、最近流行しているものですが、バージョン番号を HTTP リクエスト・ヘッダに付ける方法です。\nこれは、典型的には、`Accept` ヘッダによって行われます。\n\n```\n// パラメータによって\nAccept: application/json; version=v1\n// ベンダーのコンテント・タイプによって\nAccept: application/vnd.company.myapp-v1+json\n```\n\nどちらの方法にも長所と短所があり、それぞれのアプローチに対して多くの議論があります。\n下記では、この二つの方法をミックスした API バージョン管理の実際的な戦略を紹介します。\n\n* API 実装の各メジャー・バージョンを独立したモジュールに置き、モジュールの ID はメジャー・バージョン番号 (例えば `v1` や `v2`) とします。\n  当然ながら、API の URL はメジャー・バージョン番号を含むことになります。\n* 各メジャー・バージョンの中では (従って対応するモジュールの中では) `Accept` HTTP リクエスト・ヘッダを使ってマイナー・バージョン番号を決定し、\n  マイナー・バージョンに応じたレスポンスのための条件分岐コードを書きます。\n\nメジャー・バージョンを提供する各モジュールは、それぞれ、指定されたバージョンのためのリソースとコントローラのクラスを含んでいなければなりません。\nコードの責任範囲をより良く分離するために、共通の基底のリソースとコントローラのクラスを保持して、\nそれをバージョンごとの個別のモジュールでサブ・クラス化することが出来ます。\nサブ・クラスの中で、`Model::fields()` のような具体的なコードを実装します。\n\nあなたのコードを次のように編成することが出来ます。\n\n```\napi/\n    common/\n        controllers/\n            UserController.php\n            PostController.php\n        models/\n            User.php\n            Post.php\n    modules/\n        v1/\n            controllers/\n                UserController.php\n                PostController.php\n            models/\n                User.php\n                Post.php\n            Module.php\n        v2/\n            controllers/\n                UserController.php\n                PostController.php\n            models/\n                User.php\n                Post.php\n            Module.php\n```\n\nアプリケーションの構成情報は次のようなものになります。\n\n```php\nreturn [\n    'modules' => [\n        'v1' => [\n            'class' => 'app\\modules\\v1\\Module',\n        ],\n        'v2' => [\n            'class' => 'app\\modules\\v2\\Module',\n        ],\n    ],\n    'components' => [\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            'enableStrictParsing' => true,\n            'showScriptName' => false,\n            'rules' => [\n                ['class' => 'yii\\rest\\UrlRule', 'controller' => ['v1/user', 'v1/post']],\n                ['class' => 'yii\\rest\\UrlRule', 'controller' => ['v2/user', 'v2/post']],\n            ],\n        ],\n    ],\n];\n```\n\n上記のコードの結果として、`https://example.com/v1/users` はバージョン 1 のユーザ一覧を返し、\n`https://example.com/v2/users` はバージョン 2 のユーザ一覧を返すことになります。\n\nモジュール化のおかげで、異なるメジャー・バージョンのためのコードを綺麗に分離することが出来ます。\nしかし、モジュール化しても、共通の基底クラスやその他の共有リソースを通じて、モジュール間でコードを再利用することは引き続いて可能です。\n\nマイナー・バージョン番号を扱うためには、[[yii\\filters\\ContentNegotiator|contentNegotiator]]\nビヘイビアによって提供されるコンテント・ネゴシエーションの機能を利用することが出来ます。\n`contentNegotiator` ビヘイビアは、どのコンテント・タイプをサポートするかを決定するときに、\n[[yii\\web\\Response::acceptParams]] プロパティをセットします。\n\n例えば、リクエストが HTTP ヘッダ `Accept: application/json; version=v1` を伴って送信された場合、\nコンテント・ネゴシエーションの後では、[[yii\\web\\Response::acceptParams]] に `['version' => 'v1']` という値が含まれています。\n\n`acceptParams` のバージョン情報に基づいて、アクション、リソース・クラス、シリアライザなどの個所で条件付きのコードを書いて、\n適切な機能を提供することが出来ます。\n\nマイナー・バージョンは、定義上、後方互換性を保つことを要求するものですので、コードの中でバージョンチェックをする個所はそれほど多くないものと期待されます。\nそうでない場合は、たいていは、新しいメジャー・バージョンを作成する必要がある、ということです。\n"
  },
  {
    "path": "docs/guide-ja/runtime-bootstrapping.md",
    "content": "ブートストラップ\n================\n\nブートストラップとは、アプリケーションが、入ってくるリクエストの解決と処理を開始する前に、環境を準備する過程を指すものです。\nブートストラップは二つの場所、すなわち、[エントリ・スクリプト](structure-entry-scripts.md) と\n[アプリケーション](structure-applications.md)で行われます。\n\n[エントリ・スクリプト](structure-entry-scripts.md) では、さまざまなライブラリのためのクラス・オートローダが登録されます。\nこの中には、Composer の `autoload.php` によるオートローダと、Yii の `Yii` クラス・ファイルによるオートローダが含まれます。\nエントリ・スクリプトは、次に、アプリケーションの [構成情報](concept-configurations.md) をロードして、\n[アプリケーション](structure-applications.md) のインスタンスを作成します。\n\nアプリケーションのコンストラクタでは、次のようなブートストラップの仕事が行われます。\n\n1. [[yii\\base\\Application::preInit()|preInit()]] が呼ばれます。\n   このメソッドは、いくつかの優先度の高いアプリケーション・プロパティ、例えば [[yii\\base\\Application::basePath|basePath]] などを構成します。\n2. [[yii\\base\\Application::errorHandler|エラー・ハンドラ]] を登録します。\n3. 与えられたアプリケーションの構成情報を使って、アプリケーションのプロパティを初期化します。\n4. [[yii\\base\\Application::init()|init()]] が呼ばれます。\n   そして `init()` が [[yii\\base\\Application::bootstrap()|bootstrap()]] を呼んで、ブートストラップ・コンポーネントを走らせます。\n   - エクステンション・マニフェスト・ファイル `vendor/yiisoft/extensions.php` をインクルードします。\n   - エクステンションによって宣言された [ブートストラップ・コンポーネント](structure-extensions.md#bootstrapping-classes)\n     を作成して実行します。\n   - アプリケーションの [bootstrap プロパティ](structure-applications.md#bootstrap) に宣言されている\n     [アプリケーション・コンポーネント](structure-application-components.md) および/または [モジュール](structure-modules.md)\n     を作成して実行します。\n\nブートストラップの仕事は *全て* のリクエストを処理する前に、毎回しなければなりませんので、\nこの過程を軽いものに保って可能な限り最適化することは非常に重要なことです。\n\nあまりに多くのブートストラップ・コンポーネントを登録しないように努めてください。\nブートストラップ・コンポーネントが必要になるのは、リクエスト処理のライフサイクル全体に関与する必要がある場合だけです。\n例えば、モジュールが追加の URL 解析規則を登録する必要がある場合は、モジュールを [bootstrap プロパティ](structure-applications.md#bootstrap)\nのリストに挙げなければなりません。\nなぜなら、URL 規則を使ってリクエストが解決される前に、新しい URL 規則を有効にしなければならないからです。\n\n本番運用モードにおいては、[PHP OPCache] や [APC]  など、バイトコード・キャッシュを有効にして、\nPHP ファイルをインクルードして解析するのに要する時間を最小化してください。\n\n[PHP OPcache]: https://www.php.net/manual/ja/book.opcache.php\n[APC]: https://www.php.net/manual/ja/book.apcu.php\n\n大規模なアプリケーションには、多数の小さな構成情報ファイルに分割された、非常に複雑なアプリケーション [構成情報](concept-configurations.md) を持つものがあります。\nそのような場合には、構成情報配列全体をキャッシュするという方法を考慮して下さい。\nエントリ・スクリプトでアプリケーションのインスタンスを作成する前に構成情報をロードするときには、\n配列全体を直接にキャッシュからロードするのです。\n"
  },
  {
    "path": "docs/guide-ja/runtime-handling-errors.md",
    "content": "エラー処理\n==========\n\nYii が内蔵している [[yii\\web\\ErrorHandler|エラー・ハンドラ]] は、エラー処理を従来よりはるかに快適な経験にしてくれます。\n具体的には、Yii のエラー・ハンドラはエラー処理をより良くするために、次のことを行います。\n\n* 致命的でない全ての PHP エラー (警告や通知) は捕捉可能な例外に変換されます。\n* 例外および致命的 PHP エラーは、デバッグ・モードでは、\n  詳細なコール・スタック情報とソース・コード行とともに表示されます。\n* エラーを表示するために専用の [コントローラ・アクション](structure-controllers.md#actions) を使うことがサポートされています。\n* さまざまなエラー・レスポンス形式をサポートしています。\n\n[[yii\\web\\ErrorHandler|エラー・ハンドラ]] はデフォルトで有効になっています。\nアプリケーションの [エントリ・スクリプト](structure-entry-scripts.md) において、定数 `YII_ENABLE_ERROR_HANDLER` を `false` と定義することによって、これを無効にすることが出来ます。\n\n\n## エラー・ハンドラを使用する <span id=\"using-error-handler\"></span>\n\n[[yii\\web\\ErrorHandler|エラー・ハンドラ]] は `errorHandler` という名前の [アプリケーション・コンポーネント](structure-application-components.md) です。\n次のように、アプリケーションの構成情報でこれをカスタマイズすることが出来ます。\n\n```php\nreturn [\n    'components' => [\n        'errorHandler' => [\n            'maxSourceLines' => 20,\n        ],\n    ],\n];\n```\n\n上記の構成によって、例外ページで表示されるソース・コードの行数は最大で 20 までとなります。\n\n既に述べたように、エラー・ハンドラは致命的でない全ての PHP エラーを捕捉可能な例外に変換します。\nこれは、次のようなコードを使って PHP エラーを処理することが出来るということを意味します。\n\n```php\nuse Yii;\nuse yii\\base\\ErrorException;\n\ntry {\n    10/0;\n} catch (ErrorException $e) {\n    Yii::warning(\"0 による除算。\");\n}\n\n// 実行を継続 ...\n```\n\nリクエストが無効または予期しないものであることをユーザに知らせるエラー・ページを表示したい場合は、\n単に [[yii\\web\\NotFoundHttpException]] のような [[yii\\web\\HttpException|HTTP 例外]] を投げるだけで済ませることが出来ます。\nそうすれば、エラー・ハンドラがレスポンスの HTTP ステータス・コードを正しく設定し、\n適切なエラー・ビューを使ってエラー・メッセージを表示してくれます。\n\n```php\nuse yii\\web\\NotFoundHttpException;\n\nthrow new NotFoundHttpException();\n```\n\n\n## エラー表示をカスタマイズする <span id=\"customizing-error-display\"></span>\n\n[[yii\\web\\ErrorHandler|エラー・ハンドラ]] は、定数 `YII_DEBUG` の値に従って、エラー表示を調整します。\n`YII_DEBUG` が `true` である (デバッグ・モードである) 場合は、エラー・ハンドラは、デバッグがより容易になるように、\n例外とともに、詳細なコール・スタック情報とソース・コード行を表示します。\nそして、`YII_DEBUG` が `false` のときは、アプリケーションに関する公開できない情報の開示を防ぐために、エラー・メッセージだけが表示されます。\n\n> Info: 例外が [[yii\\base\\UserException]] の子孫である場合は、`YII_DEBUG` の値の如何にかかわらず、コール・スタックは表示されません。\nこれは、この種の例外はユーザの誤操作によって引き起こされるものであり、\n開発者は何も修正する必要がないと考えられるからです。\n\nデフォルトでは、[[yii\\web\\ErrorHandler|エラー・ハンドラ]] は二つの [ビュー](structure-views.md) を使ってエラーを表示します。\n\n* `@yii/views/errorHandler/error.php`: エラーがコール・スタック情報なしで表示されるべき場合に使用されます。\n  `YII_DEBUG` が `false` の場合、これが表示される唯一のビューとなります。\n* `@yii/views/errorHandler/exception.php`: エラーがコール・スタック情報と共に表示されるべき場合に使用されます。\n\nエラー表示をカスタマイズするために、エラー・ハンドラの [[yii\\web\\ErrorHandler::errorView|errorView]] および [[yii\\web\\ErrorHandler::exceptionView|exceptionView]] プロパティを構成して、\n自分自身のビューを使用することが出来ます。\n\n\n### エラー・アクションを使う <span id=\"using-error-actions\"></span>\n\nエラー表示をカスタマイズするためのもっと良い方法は、専用のエラー [アクション](structure-controllers.md) を使うことです。\nそうするためには、まず、`errorHandler` コンポーネントの [[yii\\web\\ErrorHandler::errorAction|errorAction]]\nプロパティを次のように構成します。\n\n```php\nreturn [\n    'components' => [\n        'errorHandler' => [\n            'errorAction' => 'site/error',\n        ],\n    ]\n];\n```\n\n[[yii\\web\\ErrorHandler::errorAction|errorAction]] プロパティは、アクションへの [ルート](structure-controllers.md#routes) を値として取ります。\n上記の構成は、エラーをコール・スタック情報なしで表示する必要がある場合は、\n`site/error` アクションが実行されるべきことを記述しています。\n\n`site/error` アクションは次のようにして作成することが出来ます。\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actions()\n    {\n        return [\n            'error' => [\n                'class' => 'yii\\web\\ErrorAction',\n            ],\n        ];\n    }\n}\n```\n\n上記のコードは [[yii\\web\\ErrorAction]] クラスを使って `error` アクションを定義しています。\n[[yii\\web\\ErrorAction]] クラスは `error` という名前のビューを使ってエラーをレンダリングします。\n\n[[yii\\web\\ErrorAction]] を使う以外に、次のようにアクション・メソッドを使って `error` アクションを定義することも出来ます。\n\n```php\npublic function actionError()\n{\n    $exception = Yii::$app->errorHandler->exception;\n    if ($exception !== null) {\n        return $this->render('error', ['exception' => $exception]);\n    }\n}\n```\n\n次に `views/site/error.php` に配置されるビュー・ファイルを作成しなければなりません。\nエラー・アクションが [[yii\\web\\ErrorAction]] として定義されている場合は、このビュー・ファイルの中で次の変数にアクセスすることが出来ます。\n\n* `name`: エラーの名前。\n* `message`: エラー・メッセージ。\n* `exception`: 例外オブジェクト。これを通じて、更に有用な情報、例えば、HTTP ステータス・コード、エラー・コード、\n  エラー・コール・スタックなどにアクセスすることが出来ます。\n\n> Info: あなたが [ベーシック・プロジェクト・テンプレート](start-installation.md) または [アドバンスト・プロジェクト・テンプレート](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide-ja/README.md) を使っている場合は、\n  エラー・アクションとエラー・ビューは、既にあなたのために定義されています。\n\n> Note: エラー・ハンドラの中でリダイレクトする必要がある場合は、次のようにしてください。\n>\n> ```php\n> Yii::$app->getResponse()->redirect($url)->send();\n> return;\n> ```\n\n\n### エラーのレスポンス形式をカスタマイズする <span id=\"error-format\"></span>\n\nエラー・ハンドラは、[レスポンス](runtime-responses.md) 形式の設定に従ってエラーを表示します。\n[[yii\\web\\Response::format|レスポンス形式]] が `html` である場合は、直前の項で説明したように、\nエラー・ビューまたは例外ビューを使ってエラーを表示します。\nその他のレスポンス形式の場合は、エラー・ハンドラは例外の配列表現を [[yii\\web\\Response::data]] プロパティに代入し、\n次に `data` プロパティを様々な形式に変換します。\n例えば、レスポンス形式が `json` である場合は、次のようなレスポンスになります。\n\n```\nHTTP/1.1 404 Not Found\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"name\": \"Not Found Exception\",\n    \"message\": \"リクエストされたリソースは見つかりませんでした。\",\n    \"code\": 0,\n    \"status\": 404\n}\n```\n\nエラーのレスポンス形式をカスタマイズするために、アプリケーションの構成情報の中で、\n`response` コンポーネントの `beforeSend` イベントに反応するハンドラを構成することが出来ます。\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'response' => [\n            'class' => 'yii\\web\\Response',\n            'on beforeSend' => function ($event) {\n                $response = $event->sender;\n                if ($response->data !== null) {\n                    $response->data = [\n                        'success' => $response->isSuccessful,\n                        'data' => $response->data,\n                    ];\n                    $response->statusCode = 200;\n                }\n            },\n        ],\n    ],\n];\n```\n\n上記のコードは、エラーのレスポンスを以下のようにフォーマットし直すものです。\n\n```\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"success\": false,\n    \"data\": {\n        \"name\": \"Not Found Exception\",\n        \"message\": \"リクエストされたリソースは見つかりませんでした。\",\n        \"code\": 0,\n        \"status\": 404\n    }\n}\n```\n"
  },
  {
    "path": "docs/guide-ja/runtime-logging.md",
    "content": "ロギング\n========\n\nYii は高度なカスタマイズ性と拡張性を持った強力なロギング・フレームワークを提供しています。\nこのフレームワークを使用すると、さまざまな種類のメッセージを記録し、それをフィルタして、ファイル、データベース、メールなど、\nさまざまなターゲットに収集することが簡単に出来ます。\n\nYii のロギング・フレームワークを使うためには、下記のステップを踏みます。\n \n* コードのさまざまな場所で [ログ・メッセージ](#log-messages) を記録する。\n* ログ・メッセージをフィルタしてエクスポートするために、アプリケーションの構成情報で [ログ・ターゲット](#log-targets) を構成する。\n* さまざまなターゲット (例えば [Yii デバッガ](tool-debugger.md)) によって、フィルタされエクスポートされたログ・メッセージを調査する。\n\nこのセクションでは、主として最初の二つのステップについて説明します。\n\n\n## メッセージを記録する <span id=\"log-messages\"></span>\n\nログ・メッセージを記録することは、次のログ記録メソッドのどれかを呼び出すだけの簡単なことです。\n\n* [[Yii::debug()]]: コードの断片がどのように走ったかをトレースするメッセージを記録します。主として開発のために使用します。\n* [[Yii::info()]]: 何らかの有用な情報を伝えるメッセージを記録します。\n* [[Yii::warning()]]: 何か予期しないことが発生したことを示す警告メッセージを記録します。\n* [[Yii::error()]]: 出来るだけ早急に調査すべき致命的なエラーを記録します。\n\nこれらのログ記録メソッドは、ログ・メッセージをさまざまな *重大性レベル* と *カテゴリ* で記録するものです。\nこれらのメソッドは `function ($message, $category = 'application')` という関数シグニチャを共有しており、\n`$message` は記録されるログ・メッセージを示し、`$category` はログ・メッセージのカテゴリを示します。\n次のコード・サンプルは、トレース・メッセージをデフォルトのカテゴリである `application` の下に記録するものです。\n\n```php\nYii::debug('平均収益の計算を開始');\n```\n\n> Info: ログ・メッセージは文字列でも、配列やオブジェクトのような複雑なデータでも構いません。\nログ・メッセージを適切に取り扱うのは [ログ・ターゲット](#log-targets) の責任です。\nデフォルトでは、ログ・メッセージが文字列でない場合は、[[yii\\helpers\\VarDumper::export()]] が呼ばれて文字列に変換されることになります。\n\nログ・メッセージを上手に編成しフィルタするために、すべてのログ・メッセージにそれぞれ適切なカテゴリを指定することが推奨されます。\nカテゴリに階層的な命名方法を採用すると、[ログ・ターゲット](#log-targets) がカテゴリに基づいてメッセージをフィルタすることが容易になります。\n簡単でしかも効果的な命名方法は、カテゴリ名に PHP のマジック定数 `__METHOD__` を使用することです。\nこれは、Yii フレームワークのコアコードでも使われている方法です。\n例えば、\n\n```php\nYii::debug('平均収益の計算を開始', __METHOD__);\n```\n\n`__METHOD__` という定数は、それが出現する場所のメソッド名 (完全修飾のクラス名が前置されます) として評価されます。\n例えば、上記のコードが `app\\controllers\\RevenueController::calculate` というメソッドの中で呼ばれている場合は、\n`__METHOD__` は `'app\\controllers\\RevenueController::calculate'` という文字列と同じになります。\n\n> Info: 上記で説明したメソッドは、実際には、[[yii\\log\\Logger|ロガー・オブジェクト]] の [[yii\\log\\Logger::log()|log()]] メソッドへのショートカットです。\n[[yii\\log\\Logger|ロガー・オブジェクト]] は `Yii::getLogger()` という式でアクセス可能なシングルトンです。\nロガー・オブジェクトは、十分な量のメッセージが記録されたとき、または、アプリケーションが終了するときに、\n[[yii\\log\\Dispatcher|メッセージ・ディスパッチャ]] を呼んで、登録された [ログ・ターゲット](#log-targets) に記録されたログ・メッセージを送信します。\n\n\n## ログ・ターゲット <span id=\"log-targets\"></span>\n\nログ・ターゲットは [[yii\\log\\Target]] クラスまたはその子クラスのインスタンスです。\nログ・ターゲットは、ログ・メッセージを重大性レベルとカテゴリによってフィルタして、何らかの媒体にエクスポートします。\n例えば、[[yii\\log\\DbTarget|データベース・ターゲット]] は、フィルタされたログ・メッセージをデータベース・テーブルにエクスポートし、\n[[yii\\log\\EmailTarget|メール・ターゲット]] は、ログ・メッセージを指定されたメール・アドレスにエクスポートします。\n\n一つのアプリケーションの中で複数のログ・ターゲットを登録することが出来ます。\nそのためには、次のように、アプリケーションの構成情報の中で、`log` [アプリケーション・コンポーネント](structure-application-components.md) によってログ・ターゲットを構成します。\n\n```php\nreturn [\n    // \"log\" コンポーネントはブートストラップ時にロードされなければならない\n    'bootstrap' => ['log'],\n    // \"log\" コンポーネントはタイムスタンプを持つメッセージを処理するので、正しいタイムスタンプを出力するように PHP タイムゾーンを設定\n    'timeZone' => 'America/Los_Angeles',\n    'components' => [\n        'log' => [\n            'targets' => [\n                [\n                    'class' => 'yii\\log\\DbTarget',\n                    'levels' => ['error', 'warning'],\n                ],\n                [\n                    'class' => 'yii\\log\\EmailTarget',\n                    'levels' => ['error'],\n                    'categories' => ['yii\\db\\*'],\n                    'message' => [\n                       'from' => ['log@example.com'],\n                       'to' => ['admin@example.com', 'developer@example.com'],\n                       'subject' => 'example.com で、データベースエラー発生',\n                    ],\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n> Note: `log` コンポーネントは、ログ・メッセージをターゲットに即座に送付することが出来るように、[ブートストラップ](runtime-bootstrapping.md) 時にロードされなければなりません。\n上記の例で `bootstrap` の配列に `log` がリストアップされているのは、そのためです。\n\n上記のコードでは、二つのログ・ターゲットが [[yii\\log\\Dispatcher::targets]] プロパティに登録されています。\n\n* 最初のターゲットは、エラーと警告のメッセージを選択して、データベース・テーブルに保存します。\n* 第二のターゲットは、名前が `yii\\db\\` で始まるカテゴリのエラー・メッセージを選んで、`admin@example.com` と `developer@example.com`\n  の両方にメールで送信します。\n\nYii は下記のログ・ターゲットをあらかじめ内蔵しています。\nその構成方法と使用方法を学ぶためには、これらのクラスの API ドキュメントを参照してください。\n\n* [[yii\\log\\DbTarget]]: ログ・メッセージをデータベース・テーブルに保存する。\n* [[yii\\log\\EmailTarget]]: ログ・メッセージを事前に指定されたメール・アドレスに送信する。\n* [[yii\\log\\FileTarget]]: ログ・メッセージをファイルに保存する。\n* [[yii\\log\\SyslogTarget]]: ログ・メッセージを PHP 関数 `syslog()` を呼んでシステム・ログに保存する。\n\n以下では、全てのターゲットに共通する機能について説明します。\n\n  \n### メッセージのフィルタリング <span id=\"message-filtering\"></span>\n\n全てのログ・ターゲットについて、それぞれ、[[yii\\log\\Target::levels|levels]] と [[yii\\log\\Target::categories|categories]] のプロパティを構成して、\nターゲットが処理すべきメッセージの重要性レベルとカテゴリを指定することが出来ます。\n\n[[yii\\log\\Target::levels|levels]] プロパティは、次のレベルの一つまたは複数からなる配列を値として取ります。\n\n* `error`: [[Yii::error()]] によって記録されたメッセージに対応。\n* `warning`: [[Yii::warning()]] によって記録されたメッセージに対応。\n* `info`: [[Yii::info()]] によって記録されたメッセージに対応。\n* `trace`: [[Yii::debug()]] によって記録されたメッセージに対応。\n* `profile`: [[Yii::beginProfile()]] と [[Yii::endProfile()]] によって記録されたメッセージに対応。\n  これについては、[プロファイリング](#performance-profiling) の項で詳細に説明します。\n\n[[yii\\log\\Target::levels|levels]] プロパティを指定しない場合は、\nターゲットが *全ての* 重大性レベルのメッセージを処理することを意味します。\n\n[[yii\\log\\Target::categories|categories]] プロパティは、メッセージ・カテゴリの名前またはパターンからなる配列を値として取ります。\nターゲットは、カテゴリの名前がこの配列にあるか、または配列にあるパターンに合致する場合にだけ、メッセージを処理します。\nカテゴリ・パターンというのは、最後にアスタリスク `*` を持つカテゴリ名接頭辞です。\nカテゴリ名は、パターンと同じ接頭辞で始まる場合に、カテゴリ・パターンに合致します。\n例えば、`yii\\db\\Command::execute` と `yii\\db\\Command::query` は、[[yii\\db\\Command]] クラスで記録されるログ・メッセージのためのカテゴリ名です。\nそして、両者は共に `yii\\db\\*` というパターンに合致します。\n\n[[yii\\log\\Target::categories|categories]] プロパティを指定しない場合は、\nターゲットが *全ての* カテゴリのメッセージを処理することを意味します。\n\n処理するカテゴリを [[yii\\log\\Target::categories|categories]] プロパティで指定する以外に、\n処理から除外するカテゴリを [[yii\\log\\Target::except|except]] プロパティによって指定することも可能です。\nカテゴリの名前がこの配列にあるか、または配列にあるパターンに合致する場合は、メッセージはターゲットによって処理されません。\n\n次のターゲットの構成は、ターゲットが、`yii\\db\\*` または `yii\\web\\HttpException:*` に合致するカテゴリ名を持つエラーおよび警告のメッセージだけを処理すべきこと、\nただし、`yii\\web\\HttpException:404` は除外すべきことを指定するものです。\n\n```php\n[\n    'class' => 'yii\\log\\FileTarget',\n    'levels' => ['error', 'warning'],\n    'categories' => [\n        'yii\\db\\*',\n        'yii\\web\\HttpException:*',\n    ],\n    'except' => [\n        'yii\\web\\HttpException:404',\n    ],\n]\n```\n\n> Info: HTTP 例外が [エラー・ハンドラ](runtime-handling-errors.md) によって捕捉されたときは、\n  `yii\\web\\HttpException:ErrorCode` という書式のカテゴリ名でエラー・メッセージがログに記録されます。\n  例えば、[[yii\\web\\NotFoundHttpException]] は、`yii\\web\\HttpException:404` というカテゴリのエラー・メッセージを発生させます。\n\n\n### メッセージの書式設定 <span id=\"message-formatting\"></span>\n\nログ・ターゲットはフィルタされたログ・メッセージを一定の書式でエクスポートします。\n例えば、[[yii\\log\\FileTarget]] クラスのログ・ターゲットをインストールした場合は、\n`runtime/log/app.log` ファイルに、下記と同様なログ・メッセージが書き込まれます。\n\n```\n2014-10-04 18:10:15 [::1][][-][trace][yii\\base\\Module::getModule] Loading module: debug\n```\n\nデフォルトでは、ログ・メッセージは [[yii\\log\\Target::formatMessage()]] によって、下記のように書式設定されます。\n\n```\nタイムスタンプ [IP アドレス][ユーザ ID][セッション ID][重要性レベル][カテゴリ] メッセージテキスト\n```\n\nこの書式は、[[yii\\log\\Target::prefix]] プロパティを構成することでカスタマイズすることが出来ます。\n[[yii\\log\\Target::prefix]] プロパティは、カスタマイズされたメッセージ前置情報を返す PHP コーラブルを値として取ります。\n例えば、次のコードは、ログ・ターゲットが全てのログ・メッセージの前にカレント・ユーザの ID を置くようにさせるものです(IP アドレスとセッション ID はプライバシー上の理由から削除されています)。\n\n```php\n[\n    'class' => 'yii\\log\\FileTarget',\n    'prefix' => function ($message) {\n        $user = Yii::$app->has('user', true) ? Yii::$app->get('user') : null;\n        $userID = $user ? $user->getId(false) : '-';\n        return \"[$userID]\";\n    }\n]\n```\n\nメッセージ前置情報以外にも、ログ・ターゲットは、一群のログ・メッセージごとに一定のコンテキスト情報を追加します。\nデフォルトでは、その情報には、次のグローバル PHP 変数、すなわち、`$_GET`、`$_POST`、`$_FILES`、`$_COOKIE`、`$_SESSION` および `$_SERVER` の値が含まれます。\nログ・ターゲットに含ませたいグローバル変数の名前を [[yii\\log\\Target::logVars]] プロパティに設定することによって、\nこの動作を調整することが出来ます。\n例えば、次のログ・ターゲットの構成は、`$_SERVER` の値だけをログ・メッセージに追加するように指定するものです。\n\n```php\n[\n    'class' => 'yii\\log\\FileTarget',\n    'logVars' => ['_SERVER'],\n]\n```\n\n`logVars` を空の配列として構成して、コンテキスト情報をまったく含ませないようにすることも出来ます。\nあるいは、また、コンテキスト情報の提供方法を自分で実装したい場合は、\n[[yii\\log\\Target::getContextMessage()]] メソッドをオーバーライドすることも出来ます。\n\nログに出力したくない機密情報 (例えば、パスワードやアクセス・トークン) を含んでいるリクエストのフィールドについては、`maskVars` プロパティを追加で構成することが出来ます。\nデフォルトでは、`$_SERVER[HTTP_AUTHORIZATION]`、`$_SERVER[PHP_AUTH_USER]`、`$_SERVER[PHP_AUTH_PW]` のリクエスト・パラメータが `***` でマスクされまが、\n自分自身で設定することも出来ます。例えば、\n\n```php\n[\n    'class' => 'yii\\log\\FileTarget',\n    'logVars' => ['_SERVER'],\n    'maskVars' => ['_SERVER.HTTP_X_PASSWORD']\n]\n```\n\n### メッセージのトレース・レベル <span id=\"trace-level\"></span>\n\n開発段階では、各ログ・メッセージがどこから来ているかを知りたい場合がよくあります。\nこれは、次のように、`log` コンポーネントの [[yii\\log\\Dispatcher::traceLevel|traceLevel]] プロパティを構成することによって達成できます。\n\n```php\nreturn [\n    'bootstrap' => ['log'],\n    'components' => [\n        'log' => [\n            'traceLevel' => YII_DEBUG ? 3 : 0,\n            'targets' => [...],\n        ],\n    ],\n];\n```\n\n上記のアプリケーションの構成は、[[yii\\log\\Dispatcher::traceLevel|traceLevel]] を `YII_DEBUG` が on のときは 3、`YII_DEBUG` が off のときは 0 に設定します。\nこれは、`YII_DEBUG` が on のときは、各ログ・メッセージに対して、ログ・メッセージが記録されたときのコール・スタックを最大 3 レベルまで追加し、\n`YII_DEBUG` が 0 のときはコール・スタックを含めない、\nということを意味します。\n\n> Info: コール・スタック情報の取得は軽微な処理ではありません。\n  従って、この機能は開発時またはアプリケーションをデバッグするときに限って使用するべきです。\n\n\n### メッセージの吐き出しとエクスポート <span id=\"flushing-exporting\"></span>\n\n既に述べたように、ログ・メッセージは [[yii\\log\\Logger|ロガー・オブジェクト]] によって配列の中に保持されます。\nこの配列のメモリ消費を制限するために、この配列に一定数のログ・メッセージが蓄積されるたびに、\nロガーは記録されたメッセージを [ログ・ターゲット](#log-targets) に吐き出します。\nこの数は、`log` コンポーネントの [[yii\\log\\Dispatcher::flushInterval|flushInterval]] プロパティを構成することによってカスタマイズすることが出来ます。\n\n\n```php\nreturn [\n    'bootstrap' => ['log'],\n    'components' => [\n        'log' => [\n            'flushInterval' => 100,   // デフォルトは 1000\n            'targets' => [...],\n        ],\n    ],\n];\n```\n\n> Info: メッセージの吐き出しは、アプリケーションの終了時にも実行されます。これによって、ログ・ターゲットが完全なログ・メッセージを受け取ることが保証されます。\n\n[[yii\\log\\Logger|ロガー・オブジェクト]] が [ログ・ターゲット](#log-targets) にログ・メッセージを吐き出しても、ログ・メッセージはただちにはエクスポートされません。\nそうではなく、ログ・ターゲットが一定数のフィルタされたメッセージを蓄積して初めて、メッセージのエクスポートが発生します。\nこの数は、下記のように、個々の [ログ・ターゲット](#log-targets) の [[yii\\log\\Target::exportInterval|exportInterval]]\nプロパティを構成することによってカスタマイズすることが出来ます。\n\n```php\n[\n    'class' => 'yii\\log\\FileTarget',\n    'exportInterval' => 100,  // デフォルトは 1000\n]\n```\n\nデフォルトの状態では、吐き出しとエクスポートの間隔の設定のために、`Yii::debug()` やその他のログ記録メソッドを呼んでも、\nただちには、ログ・メッセージはログ・ターゲットに出現しません。\nこのことは、長時間にわたって走るコンソール・アプリケーションでは、問題になる場合もあります。\n各ログ・メッセージがただちにログ・ターゲットに出現するようにするためには、下記のように、[[yii\\log\\Dispatcher::flushInterval|flushInterval]] と\n[[yii\\log\\Target::exportInterval|exportInterval]] の両方を 1 に設定しなければなりません。\n\n```php\nreturn [\n    'bootstrap' => ['log'],\n    'components' => [\n        'log' => [\n            'flushInterval' => 1,\n            'targets' => [\n                [\n                    'class' => 'yii\\log\\FileTarget',\n                    'exportInterval' => 1,\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n> Note: 頻繁なメッセージの吐き出しとエクスポートはアプリケーションのパフォーマンスを低下させます。\n\n\n### ログ・ターゲットの 有効/無効 を切り替える <span id=\"toggling-log-targets\"></span>\n\n[[yii\\log\\Target::enabled|enabled]] プロパティを構成することによって、ログ・ターゲットを有効にしたり無効にしたりすることが出来ます。\nこの切り替えは、ログ・ターゲットのコンフィギュレーションでも出来ますが、コードの中で次の PHP 文を使っても出来ます。\n\n```php\nYii::$app->log->targets['file']->enabled = false;\n```\n\n上記のコードでは、ターゲットが `file` という名前であることが必要とされています。\n下記のように、`targets` の配列で文字列のキーを使ってターゲットの名前を指定して下さい。\n\n```php\nreturn [\n    'bootstrap' => ['log'],\n    'components' => [\n        'log' => [\n            'targets' => [\n                'file' => [\n                    'class' => 'yii\\log\\FileTarget',\n                ],\n                'db' => [\n                    'class' => 'yii\\log\\DbTarget',\n                ],\n            ],\n        ],\n    ],\n];\n```\n\nバージョン 2.0.13 以降は、[[yii\\log\\Target::enabled|enabled]] を設定するのに、\nログ・ターゲットを有効にすべきか否かの動的な条件を定義するコーラブルを指定することが可能です。\n[[yii\\log\\Target::setEnabled()]] のドキュメントに一例がありますので参照して下さい。\n\n### 新しいターゲットを作る <span id=\"new-targets\"></span>\n\n新しいログ・ターゲット・クラスを作ることは非常に簡単です。\n必要なことは、主として、[[yii\\log\\Target::messages]] 配列の中身を指定された媒体に送出する [[yii\\log\\Target::export()]] メソッドを実装することです。\n各メッセージに書式を設定するためには、[[yii\\log\\Target::formatMessage()]] を呼ぶことが出来ます。\n詳細については、Yii リリースに含まれているログ・ターゲット・クラスのどれか一つを参照してください。\n\n> Tip: あなた自身のロガーを書く代りに、[PSR ログ・ターゲット・エクステンション](https://github.com/samdark/yii2-psr-log-target) によって、\n  [Monolog](https://github.com/Seldaek/monolog) のような\n  PSR-3 互換ロガーのどれかを使ってみるのも良いでしょう。\n\n## パフォーマンス・プロファイリング <span id=\"performance-profiling\"></span>\n\nパフォーマンス・プロファイリングは、特定のコード・ブロックに要した時間を測定してパフォーマンスのボトルネックになっている所を見つけ出すために使われる、\n特殊なタイプのメッセージ・ロギングです。\n例えば、[[yii\\db\\Command]] クラスは、各 DB クエリに要した時間を知るために、パフォーマンス・プロファイリングを使用しています。\n\nパフォーマンス・プロファイリングを使用するためには、最初に、プロファイリングが必要なコード・ブロックを特定します。\nそして、各コード・ブロックを次のように囲みます。\n\n```php\n\\Yii::beginProfile('myBenchmark');\n\n... プロファイリングされるコード・ブロック ...\n\n\\Yii::endProfile('myBenchmark');\n```\n\nここで `myBenchmark` はコード・ブロックを特定するユニークなトークンを表します。\n後でプロファイリング結果を検査するときに、このトークンを使って、対応するコード・ブロックによって消費された時間を調べます。\n\n`beginProfile` と `endProfile` のペアが適正な入れ子になっていることを確認することが非常に重要なことです。\n例えば、\n\n```php\n\\Yii::beginProfile('block1');\n\n    // プロファイリングされる何らかのコード\n\n    \\Yii::beginProfile('block2');\n        // プロファイリングされる別のコード\n    \\Yii::endProfile('block2');\n\n\\Yii::endProfile('block1');\n```\n\n`\\Yii::endProfile('block1')` を忘れたり、`\\Yii::endProfile('block1')` と `\\Yii::endProfile('block2')` の順序を入れ替えたりすると、\nパフォーマンス・プロファイリングは機能しません。\n\nプロファイルされるコード・ブロックの全てについて、おのおの、重大性レベルが `profile` であるログ・メッセージが記録されます。\nそのようなメッセージを集めてエクスポートする [ログ・ターゲット](#log-targets) を構成してください。\n[Yii デバッガ](tool-debugger.md) が、プロファイリング結果を表示するパフォーマンス・プロファイリング・パネルを内蔵しています。\n"
  },
  {
    "path": "docs/guide-ja/runtime-overview.md",
    "content": "概要\n====\n\nYii のアプリケーションがリクエストを処理するときは、毎回、同じようなワーク・フローになります。\n\n1. ユーザが [エントリ・スクリプト](structure-entry-scripts.md) `web/index.php` にリクエストをします。\n2. エントリ・スクリプトは、アプリケーションの [構成情報](concept-configurations.md) をロードして、\n   リクエストを処理するための [アプリケーション](structure-applications.md) のインスタンスを作成します。\n3. アプリケーションは、[リクエスト](runtime-requests.md) アプリケーション・コンポーネントの助けを借りて、\n   リクエストされた [ルート](runtime-routing.md) を解決します。\n4. アプリケーションはリクエストを処理するための [コントローラ](structure-controllers.md) のインスタンスを作成します。\n5. コントローラは [アクション](structure-controllers.md) のインスタンスを作成して、アクションのためのフィルタを実行します。\n6. [フィルタ](structure-filters.md)のどれかが失敗すると、アクションはキャンセルされます。\n7. すべてのフィルタを無事に通ったら、アクションが実行されます。\n8. アクションはデータ[モデル](structure-models.md)を、おそらくはデータベースから、ロードします。\n9. アクションはデータ・モデルを[ビュー](structure-views.md)に提供して、ビューをレンダリングします。\n10. レンダリングの結果は [レスポンス](runtime-responses.md) アプリケーション・コンポーネントに返されます。\n11. レスポンス・コンポーネントがレンダリングの結果をユーザのブラウザに送信します。\n\n次の図は、アプリケーションがどのようにしてリクエストを処理するかを示すものです。\n\n![リクエストのライフサイクル](images/request-lifecycle.png)\n\nこのセクションでは、これらのステップのいくつかについて、どのように動作するかを詳細に説明します。\n"
  },
  {
    "path": "docs/guide-ja/runtime-requests.md",
    "content": "リクエスト\n==========\n\nアプリケーションに対するリクエストは、リクエストのパラメータ、HTTP ヘッダ、クッキーなどの情報を提供する [[yii\\web\\Request]] オブジェクトの形で表されます。\n与えられたリクエストに対応するリクエスト・オブジェクトには、デフォルトでは [[yii\\web\\Request]] のインスタンスである `request` [アプリケーション・コンポーネント](structure-application-components.md)\nを通じてアクセスすることが出来ます。\nこのセクションでは、アプリケーションの中でこのコンポーネントをどのように利用できるかを説明します。\n\n\n## リクエストのパラメータ <span id=\"request-parameters\"></span>\n\nリクエストのパラメータを取得するためには、`request` コンポーネントの [[yii\\web\\Request::get()|get()]] および [[yii\\web\\Request::post()|post()]] メソッドを呼ぶことが出来ます。\nこれらは、ぞれぞれ、`$_GET` と `$_POST` の値を返します。例えば、\n\n```php\n$request = Yii::$app->request;\n\n$get = $request->get(); \n// $get = $_GET; と同等\n\n$id = $request->get('id');\n// $id = isset($_GET['id']) ? $_GET['id'] : null; と同等\n\n$id = $request->get('id', 1);\n// $id = isset($_GET['id']) ? $_GET['id'] : 1; と同等\n\n$post = $request->post(); \n// $post = $_POST; と同等\n\n$name = $request->post('name');\n// $name = isset($_POST['name']) ? $_POST['name'] : null; と同等\n\n$name = $request->post('name', '');\n// $name = isset($_POST['name']) ? $_POST['name'] : ''; と同等\n```\n\n> Info: 直接に `$_GET` と `$_POST` にアクセスしてリクエストのパラメータを読み出す代りに、上記に示されているように、\n`request` コンポーネントを通じてそれらを取得することが推奨されます。\n  このようにすると、ダミーのリクエスト・データを持った模擬リクエスト・コンポーネントを作ることが出来るため、テストを書くことがより容易になります。\n\n[RESTful API](rest-quick-start.md) を実装するときは、PUT、PATCH またはその他の [リクエスト・メソッド](#request-methods) によって送信されたパラメータを読み出さなければならないことがよくあります。\nそういうパラメータは [[yii\\web\\Request::getBodyParam()]] メソッドを呼ぶことで取得することが出来ます。\n例えば、\n\n```php\n$request = Yii::$app->request;\n\n// 全てのパラメータを返す\n$params = $request->bodyParams;\n\n// パラメータ \"id\" を返す\n$param = $request->getBodyParam('id');\n```\n\n> Info: `GET` パラメータとは異なって、`POST`、`PUT`、`PATCH` などで送信されたパラメータは、リクエストのボディの中で送られます。\n  上述のメソッドによってこれらのパラメータにアクセスすると、`request` コンポーネントがパラメータを解析します。\n  [[yii\\web\\Request::parsers]] プロパティを構成することによって、これらのパラメータが解析される方法をカスタマイズすることが出来ます。\n\n\n## リクエスト・メソッド <span id=\"request-methods\"></span>\n\n現在のリクエストに使用された HTTP メソッドは、`Yii::$app->request->method` という式によって取得することが出来ます。\n現在のメソッドが特定のタイプであるかどうかをチェックするための、一揃いの真偽値のプロパティも提供されています。\n例えば、\n\n```php\n$request = Yii::$app->request;\n\nif ($request->isAjax) { /* リクエストは AJAX リクエスト */ }\nif ($request->isGet)  { /* リクエスト・メソッドは GET */ }\nif ($request->isPost) { /* リクエスト・メソッドは POST */ }\nif ($request->isPut)  { /* リクエスト・メソッドは PUT */ }\n```\n\n## リクエストの URL <span id=\"request-urls\"></span>\n\n`request` コンポーネントは現在リクエストされている URL を調べるための方法を数多く提供しています。\n\nリクエストされた URL が `https://example.com/admin/index.php/product?id=100` であると仮定したとき、\n次にまとめたように、この URL のさまざまな部分を取得することが出来ます。\n\n* [[yii\\web\\Request::url|url]]: `/admin/index.php/product?id=100` を返します。ホスト情報の部分を省略した URL です。\n* [[yii\\web\\Request::absoluteUrl|absoluteUrl]]: `https://example.com/admin/index.php/product?id=100` を返します。\n  ホスト情報の部分を含んだ URL です。\n* [[yii\\web\\Request::hostInfo|hostInfo]]: `https://example.com` を返します。URL のホスト情報の部分です。\n* [[yii\\web\\Request::pathInfo|pathInfo]]: `/product` を返します。\n  エントリ・スクリプトの後、疑問符 (クエリ文字列) の前の部分です。\n* [[yii\\web\\Request::queryString|queryString]]: `id=100` を返します。疑問符の後の部分です。\n* [[yii\\web\\Request::baseUrl|baseUrl]]: `/admin` を返します。ホスト情報の後、かつ、\n  エントリ・スクリプトの前の部分です。\n* [[yii\\web\\Request::scriptUrl|scriptUrl]]: `/admin/index.php` を返します。パス情報とクエリ文字列を省略した URL です。\n* [[yii\\web\\Request::serverName|serverName]]: `example.com` を返します。URL の中のホスト名です。\n* [[yii\\web\\Request::serverPort|serverPort]]: 80 を返します。ウェブ・サーバによって使用されているポートです。\n\n\n## HTTP ヘッダ <span id=\"http-headers\"></span> \n\n[[yii\\web\\Request::headers]] プロパティによって返される [[yii\\web\\HeaderCollection|header コレクション]] を通じて、\nHTTP ヘッダ情報を取得することが出来ます。例えば、\n\n```php\n// $headers は yii\\web\\HeaderCollection のオブジェクト\n$headers = Yii::$app->request->headers;\n\n// Accept ヘッダの値を返す\n$accept = $headers->get('Accept');\n\nif ($headers->has('User-Agent')) { /* User-Agent ヘッダが在る */ }\n```\n\n`request` コンポーネントは、よく使用されるいくつかのヘッダにすばやくアクセスする方法を提供しています。その中には下記のものが含まれます。\n\n* [[yii\\web\\Request::userAgent|userAgent]]: `User-Agent` ヘッダの値を返します。\n* [[yii\\web\\Request::contentType|contentType]]: リクエスト・ボディのデータの MIME タイプを示す\n  `Content-Type` ヘッダの値を返します。\n* [[yii\\web\\Request::acceptableContentTypes|acceptableContentTypes]]: ユーザが受け入れ可能なコンテントの MIME タイプを返します。\n  返されるタイプは品質スコアによって順序付けられます。最もスコアの高いタイプが最初に返されます。\n* [[yii\\web\\Request::acceptableLanguages|acceptableLanguages]]: ユーザが受け入れ可能な言語を返します。\n  返される言語は優先レベルによって順序付けられます。最初の要素が最も優先度の高い言語を表します。\n\nあなたのアプリケーションが複数の言語をサポートしており、エンド・ユーザが最も優先する言語でページを表示したいと思う場合は、\n言語ネゴシエーション・メソッド [[yii\\web\\Request::getPreferredLanguage()]] を使うことが出来ます。\nこのメソッドはアプリケーションによってサポートされている言語のリストを引数として取り、\n[[yii\\web\\Request::acceptableLanguages|acceptableLanguages]] と比較して、最も適切な言語を返します。\n\n> Tip: [[yii\\filters\\ContentNegotiator|ContentNegotiator]] フィルタを使用して、\n  レスポンスにおいてどのコンテント・タイプと言語を使うべきかを動的に決定することも出来ます。\n  このフィルタは、上記で説明したプロパティとメソッドの上に、コンテント・ネゴシエーションを実装しています。\n\n\n## クライアント情報 <span id=\"client-information\"></span>\n\nクライアント・マシンのホスト名と IP アドレスを、\nそれぞれ、[[yii\\web\\Request::userHost|userHost]] と [[yii\\web\\Request::userIP|userIP]] によって取得することが出来ます。\n例えば、\n\n```php\n$userHost = Yii::$app->request->userHost;\n$userIP = Yii::$app->request->userIP;\n```\n\n## 信頼できるプロキシとヘッダ <span id=\"trusted-proxies\"></span>\n\n前のセクションでホストや IP アドレスなどのユーザ情報を取得する方法を説明しました。\n単一のウェブ・サーバがウェブ・サイトをホストしている通常の環境では、このままで動作します。\nしかし、Yii アプリケーションがリバース・プロキシの背後で動作している場合は、この情報を読み出すために構成情報を追加する必要があります。\nなぜなら、その場合、直接のクライアントはプロキシになっており、ユーザの IP アドレスはプロキシがセットするヘッダによって\nYii アプリケーションに渡されるからです。\n\n明示的に信頼したプロキシ以外は、プロキシによって提供されるヘッダを盲目的に信頼してはいけません。\n2.0.13 以降、Yii は `request` コンポーネントの以下のプロパティによって、\n信頼できるプロキシの情報を構成することが出来るようになっています。\n[[yii\\web\\Request::trustedHosts|trustedHosts]]、\n[[yii\\web\\Request::secureHeaders|secureHeaders]]、 \n[[yii\\web\\Request::ipHeaders|ipHeaders]] \n[[yii\\web\\Request::secureProtocolHeaders|secureProtocolHeaders]] および\n[[yii\\web\\Request::portHeaders|portHeaders]] (2.0.46 以降)\n\n以下は、リバース・プロキシ・アレイの背後で動作するアプリケーションのための、request の構成例です\n(リバース・プロキシ・アレイは `10.0.2.0/24` のネットワークに設置されているとします)。\n\n```php\n'request' => [\n    // ...\n    'trustedHosts' => [\n        '10.0.2.0/24',\n    ],\n],\n```\n\nプロキシは、デフォルトでは、IP を `X-Forwarded-For` ヘッダで送信し、プロトコル (`http` または `https`) を `X-Forwarded-Proto` で送信します。\n\nあなたのプロキシが異なるヘッダを使っている場合は、request の構成情報を使って調整することが出来ます。例えば、\n\n```php\n'request' => [\n    // ...\n    'trustedHosts' => [\n        '10.0.2.0/24' => [\n            'X-ProxyUser-Ip',\n            'Front-End-Https',\n        ],\n    ],\n    'secureHeaders' => [\n        'X-Forwarded-For',\n        'X-Forwarded-Host',\n        'X-Forwarded-Proto',\n        'X-Proxy-User-Ip',\n        'Front-End-Https',\n    ],\n    'ipHeaders' => [\n        'X-Proxy-User-Ip',\n    ],\n    'secureProtocolHeaders' => [\n        'Front-End-Https' => ['on']\n    ],\n],\n```\n\n上記の構成によって、`secureHeaders` としてリストされているヘッダはリクエストから除去され、\n信頼できるプロキシからのリクエストである場合にのみ、`X-ProxyUser-Ip` と `Front-End-Https` ヘッダが受け入れられます。\nその場合、前者は `ipHeaders` で構成されているようにユーザの IP を読み出すために使用され、\n後者は [[yii\\web\\Request::getIsSecureConnection()]] の結果を決定するために使用されます。\n\n2.0.31 以降、[RFC 7239](https://datatracker.ietf.org/doc/html/rfc7239) の `Forwarded` ヘッダがサポートされています。\n有効にするためには、ヘッダ名を `secureHeaders` に追加する必要があります。\nあなたのプロキシにそれを設定させることを忘れないで下さい。さもないと、エンド・ユーザが IP とプロトコルを盗み見ることが可能になります。\n\n### 解決済みのユーザ IP<span id=\"already-respolved-user-ip\"></span>\n\nユーザの IP アドレスが Yii アプリケーション以前に解決済みである場合(例えば、`ngx_http_realip_module` など) は、\n`request` コンポーネントは下記の構成で正しく動作します。\n\n```php\n'request' => [\n    // ...\n    'trustedHosts' => [\n        '0.0.0.0/0',\n    ],\n    'ipHeaders' => [], \n],\n```\n\nこの場合、[[yii\\web\\Request::userIP|userIP]] の値は `$_SERVER['REMOTE_ADDR']` に等しくなります。\n同時に、HTTP ヘッダから解決されるプロパティも正しく動作します (例えば、[[yii\\web\\Request::getIsSecureConnection()]])。\n\n> 注意: `trustedHosts=['0.0.0.0/0']` の設定は、全ての IP が信頼できることを前提としています。\n"
  },
  {
    "path": "docs/guide-ja/runtime-responses.md",
    "content": "レスポンス\n==========\n\nアプリケーションは [リクエスト](runtime-requests.md) の処理を完了すると、[[yii\\web\\Response|レスポンス]]・オブジェクトを生成して、エンド・ユーザに送信します。\nレスポンス・オブジェクトは、HTTP ステータス・コード、HTTP ヘッダ、HTTP ボディなどの情報を含みます。\nウェブ・アプリケーション開発の最終的な目的は、本質的には、さまざまなリクエストに対してそのようなレスポンス・オブジェクトを作成することにあります。\n\nほとんどの場合は、主として、デフォルトでは [[yii\\web\\Response]] のインスタンスである `response`\n[アプリケーション・コンポーネント](structure-application-components.md) を使用すべきです。\nしかしながら、Yii は、以下で説明するように、あなた自身のレスポンス・オブジェクトを作成してエンド・ユーザに送信することも許容しています。\n\nこのセクションでは、レスポンスを構成してエンド・ユーザに送信する方法を説明します。\n\n\n## ステータス・コード <span id=\"status-code\"></span>\n\nレスポンスを作成するときに最初にすることの一つは、リクエストが成功裡に処理されたかどうかを記述することです。\nそのためには、[[yii\\web\\Response::statusCode]] プロパティに有効な\n[HTTP ステータス・コード](https://tools.ietf.org/html/rfc2616#section-10) の一つを設定します。\n例えば、下記のように、リクエストの処理が成功したことを示すために、ステータス・コードを 200 に設定します。\n\n```php\nYii::$app->response->statusCode = 200;\n```\n\nただし、たいていの場合、ステータス・コードを明示的に設定する必要はありません。\nこれは、[[yii\\web\\Response::statusCode]] のデフォルト値が 200 であるからです。\nそして、リクエストが失敗したことを示したいときは、下記のように、適切な HTTP 例外を投げることが出来ます。\n\n```php\nthrow new \\yii\\web\\NotFoundHttpException;\n```\n\n[エラー・ハンドラ](runtime-handling-errors.md) は、例外をキャッチすると、例外からステータス・コードを抽出してレスポンスに割り当てます。\n上記の [[yii\\web\\NotFoundHttpException]] の場合は、HTTP ステータス 404 と関連付けられています。\n次の HTTP 例外が Yii によって事前定義されています。\n\n* [[yii\\web\\BadRequestHttpException]]: ステータス・コード 400\n* [[yii\\web\\ConflictHttpException]]: ステータス・コード 409\n* [[yii\\web\\ForbiddenHttpException]]: ステータス・コード 403\n* [[yii\\web\\GoneHttpException]]: ステータス・コード 410\n* [[yii\\web\\MethodNotAllowedHttpException]]: ステータス・コード 405\n* [[yii\\web\\NotAcceptableHttpException]]: ステータス・コード 406 \n* [[yii\\web\\NotFoundHttpException]]: ステータス・コード 404\n* [[yii\\web\\ServerErrorHttpException]]: ステータス・コード 500\n* [[yii\\web\\TooManyRequestsHttpException]]: ステータス・コード 429\n* [[yii\\web\\UnauthorizedHttpException]]: ステータス・コード 401\n* [[yii\\web\\UnsupportedMediaTypeHttpException]]: ステータス・コード 415\n\n投げたい例外が上記のリストに無い場合は、[[yii\\web\\HttpException]] から拡張したものを作成することが出来ます。\nあるいは、ステータス・コードを指定して [[yii\\web\\HttpException]] を直接に投げることも出来ます。例えば、\n\n```php\nthrow new \\yii\\web\\HttpException(402);\n```\n\n\n## HTTP ヘッダ <span id=\"http-headers\"></span> \n\n`response` コンポーネントの [[yii\\web\\Response::headers|ヘッダ・コレクション]] を操作することによって、HTTP ヘッダを送信することが出来ます。\n例えば、\n\n```php\n$headers = Yii::$app->response->headers;\n\n// Pragma ヘッダを追加する。既存の Pragma ヘッダは上書きされない。\n$headers->add('Pragma', 'no-cache');\n\n// Pragma ヘッダを設定する。既存の Pragma ヘッダは全て破棄される。\n$headers->set('Pragma', 'no-cache');\n\n// Pragma ヘッダを削除して、削除された Pragma ヘッダの値を配列に返す。\n$values = $headers->remove('Pragma');\n```\n\n> Info: ヘッダ名は大文字小文字を区別しません。\n  そして、新しく登録されたヘッダは、[[yii\\web\\Response::send()]] メソッドが呼ばれるまで送信されません。\n\n\n## レスポンス・ボディ <span id=\"response-body\"></span>\n\nほとんどのレスポンスは、エンド・ユーザに対して表示したい内容を示すボディを持っていなければなりません。\n\n既にフォーマットされたボディの文字列を持っている場合は、それをレスポンスの [[yii\\web\\Response::content]] プロパティに割り付けることが出来ます。\n例えば、\n\n```php\nYii::$app->response->content = 'hello world!';\n```\n\nデータをエンド・ユーザに送信する前にフォーマットする必要がある場合は、[[yii\\web\\Response::format|format]] と [[yii\\web\\Response::data|data]] の両方のプロパティをセットしなければなりません。\n[[yii\\web\\Response::format|format]] プロパティは [[yii\\web\\Response::data|data]] がどの形式でフォーマットされるべきかを指定するものです。\n例えば、\n\n```php\n$response = Yii::$app->response;\n$response->format = \\yii\\web\\Response::FORMAT_JSON;\n$response->data = ['message' => 'hello world'];\n```\n\nYii は下記の形式を初めからサポートしています。それぞれ、[[yii\\web\\ResponseFormatterInterface|フォーマッタ]] クラスとして実装されています。\n[[yii\\web\\Response::formatters]] プロパティを構成することで、これらのフォーマッタをカスタマイズしたり、新しいフォーマッタを追加したりすることが出来ます。\n\n* [[yii\\web\\Response::FORMAT_HTML|HTML]]: [[yii\\web\\HtmlResponseFormatter]] によって実装\n* [[yii\\web\\Response::FORMAT_XML|XML]]: [[yii\\web\\XmlResponseFormatter]] によって実装\n* [[yii\\web\\Response::FORMAT_JSON|JSON]]: [[yii\\web\\JsonResponseFormatter]] によって実装\n* [[yii\\web\\Response::FORMAT_JSONP|JSONP]]: [[yii\\web\\JsonResponseFormatter]] によって実装\n* [[yii\\web\\Response::FORMAT_RAW|RAW]]: 書式を何も適用せずにレスポンスを送信したいときは、このフォーマットを使用\n\nレスポンス・ボディは、上記のように、明示的に設定することも出来ますが、たいていの場合は、[アクション](structure-controllers.md) メソッドの返り値によって暗黙のうちに設定することが出来ます。\nよくあるユースケースは下記のようなものになります。\n\n```php\npublic function actionIndex()\n{\n    return $this->render('index');\n}\n```\n\n上記の `index` アクションは、`index` ビューのレンダリング結果を返しています。\n返された値は `response` コンポーネントによって受け取られ、フォーマットされてエンド・ユーザに送信されます。\n\nデフォルトのレスポンス形式が [[yii\\web\\Response::FORMAT_HTML|HTML]] であるため、アクション・メソッドの中では文字列を返すだけにすべきです。\n別のレスポンス形式を使いたい場合は、データを返す前にレスポンス形式を設定しなければなりません。\n例えば、\n\n```php\npublic function actionInfo()\n{\n    \\Yii::$app->response->format = \\yii\\web\\Response::FORMAT_JSON;\n    return [\n        'message' => 'hello world',\n        'code' => 100,\n    ];\n}\n```\n\n既に述べたように、デフォルトの `response` アプリケーション・コンポーネントを使う代りに、自分自身のレスポンス・オブジェクトを作成してエンド・ユーザに送信することも出来ます。\nそうするためには、次のように、アクション・メソッドの中でそのようなオブジェクトを返します。\n\n```php\npublic function actionInfo()\n{\n    return \\Yii::createObject([\n        'class' => 'yii\\web\\Response',\n        'format' => \\yii\\web\\Response::FORMAT_JSON,\n        'data' => [\n            'message' => 'hello world',\n            'code' => 100,\n        ],\n    ]);\n}\n```\n\n> Note: 自分自身のレスポンス・オブジェクトを作成しようとする場合は、アプリケーションの構成情報で\n  `response` コンポーネントのために設定した構成情報を利用することは出来ません。\n  しかし、 [依存の注入](concept-di-container.md) を使えば、 共通の構成情報をあなたの新しいレスポンス・オブジェクトに適用することが出来ます。\n\n\n## ブラウザのリダイレクト <span id=\"browser-redirection\"></span>\n\nブラウザのリダイレクトは `Location` HTTP ヘッダの送信に依存しています。\nこの機能は通常よく使われるものであるため、Yii はこれについて特別のサポートを提供しています。\n\n[[yii\\web\\Response::redirect()]] メソッドを呼ぶことによって、ユーザのブラウザをある URL にリダイレクトすることが出来ます。\nこのメソッドは与えられた URL を持つ適切な `Location` ヘッダを設定して、レスポンス・オブジェクトそのものを返します。\nアクション・メソッドの中では、そのショートカット版である [[yii\\web\\Controller::redirect()]] を呼ぶことが出来ます。例えば、\n\n```php\npublic function actionOld()\n{\n    return $this->redirect('https://example.com/new', 301);\n}\n```\n\n上記のコードでは、アクション・メソッドが `redirect()` メソッドの結果を返しています。\n前に説明したように、アクション・メソッドによって返されるレスポンス・オブジェクトが、エンド・ユーザに送信されるレスポンスとして使用されることになります。\n\nアクション・メソッド以外の場所では、[[yii\\web\\Response::redirect()]] を直接に呼び出し、\nメソッド・チェーンで [[yii\\web\\Response::send()]] メソッドを呼んで、レスポンスに余計なコンテントが追加されないことを保証しなければなりません。\n\n```php\n\\Yii::$app->response->redirect('https://example.com/new', 301)->send();\n```\n\n> Info: デフォルトでは、[[yii\\web\\Response::redirect()]] メソッドはレスポンスのステータス・コードを 302 にセットします。\nこれはブラウザに対して、リクエストされているリソースが *一時的に* 異なる URI に配置されていることを示すものです。\nブラウザに対してリソースが *恒久的に* 配置替えされたことを教えるためには、ステータス・コード 301 を渡すことが出来ます。\n\n現在のリクエストが AJAX リクエストである場合は、`Location` ヘッダを送っても自動的にブラウザをリダイレクトすることにはなりません。\nこの問題を解決するために、[[yii\\web\\Response::redirect()]] メソッドは `X-Redirect` ヘッダにリダイレクト先 URL を値としてセットします。\nそして、クライアント・サイドで、このヘッダの値を読み、\nそれに応じてブラウザをリダイレクトする JavaScript を書くことが出来ます。\n\n> Info: Yii には `yii.js` という JavaScript ファイルが付属しています。\n  これは、よく使われる一連の JavaScript 機能を提供するもので、その中には `X-Redirect` ヘッダに基づくブラウザのリダイレクトも含まれています。\n  従って、あなたが ([[yii\\web\\YiiAsset]] アセット・バンドルを登録して) この JavaScript ファイルを使うつもりなら、AJAX のリダイレクトをサポートするためには、何も書く必要がなくなります。\n  `yii.js` に関する更なる情報は [クライアント・スクリプトのセクション](output-client-scripts.md#yii.js) にあります。\n\n## ファイルを送信する <span id=\"sending-files\"></span>\n\nブラウザのリダイレクトと同じように、ファイルの送信という機能も特定の HTTP ヘッダに依存しています。\nYii はさまざまなファイル送信の必要をサポートするための一連のメソッドを提供しています。それらはすべて、HTTP range ヘッダに対するサポートを内蔵しています。\n\n* [[yii\\web\\Response::sendFile()]]: 既存のファイルをクライアントに送信する\n* [[yii\\web\\Response::sendContentAsFile()]]: テキストの文字列をファイルとしてクライアントに送信する\n* [[yii\\web\\Response::sendStreamAsFile()]]: 既存のファイル・ストリームをファイルとしてクライアントに送信する\n\nこれらのメソッドは同じメソッド・シグニチャを持ち、返り値としてレスポンス・オブジェクトを返します。\n送信しようとしているファイルが非常に大きなものである場合は、メモリ効率の良い [[yii\\web\\Response::sendStreamAsFile()]] の使用を検討すべきです。\n次の例は、コントローラ・アクションでファイルを送信する方法を示すものです。\n\n```php\npublic function actionDownload()\n{\n    return \\Yii::$app->response->sendFile('path/to/file.txt');\n}\n```\n\nファイル送信メソッドをアクション・メソッド以外の場所で呼ぶ場合は、その後で [[yii\\web\\Response::send()]] メソッドも呼んで、\nレスポンスに余計なコンテントが追加されないことを保証しなければなりません。\n\n```php\n\\Yii::$app->response->sendFile('path/to/file.txt')->send();\n```\n\nウェブ・サーバには、*X-Sendfile* と呼ばれる特別なファイル送信をサポートするものがあります。\nアイデアとしては、ファイルに対するリクエストをウェブ・サーバにリダイレクトして、ウェブ・サーバに直接にファイルを送信させる、というものです。\nその結果として、ウェブ・サーバがファイルを送信している間でも、ウェブ・アプリケーションは早期に終了することが出来るようになります。\nこの機能を使うために、[[yii\\web\\Response::xSendFile()]] を呼ぶことが出来ます。\n次のリストは、よく使われるいくつかのウェブ・サーバにおいて `X-Sendfile` 機能を有効にする方法を要約するものです。\n\n- Apache: [X-Sendfile](https://tn123.org/mod_xsendfile)\n- Lighttpd v1.4: [X-LIGHTTPD-send-file](https://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file)\n- Lighttpd v1.5: [X-Sendfile](https://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file)\n- Nginx: [X-Accel-Redirect](https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile/)\n- Cherokee: [X-Sendfile and X-Accel-Redirect](https://www.cherokee-project.com/doc/other_goodies.html#x-sendfile)\n\n\n## レスポンスを送信する <span id=\"sending-response\"></span>\n\nレスポンスの中のコンテントは、[[yii\\web\\Response::send()]] メソッドが呼ばれるまでは、エンド・ユーザに向けて送信されません。\nデフォルトでは、このメソッドは [[yii\\base\\Application::run()]] の最後で自動的に呼ばれます。\nしかし、このメソッドを明示的に呼んで、強制的にレスポンスを即座に送信することも可能です。\n\n[[yii\\web\\Response::send()]] メソッドは次のステップを踏んでレスポンスを送出します。\n\n1. [[yii\\web\\Response::EVENT_BEFORE_SEND]] イベントをトリガする。\n2. [[yii\\web\\Response::prepare()]] を呼んで [[yii\\web\\Response::data|レスポンス・データ]] を\n   [[yii\\web\\Response::content|レスポンス・コンテント]] としてフォーマットする。\n3. [[yii\\web\\Response::EVENT_AFTER_PREPARE]] イベントをトリガする。\n4. [[yii\\web\\Response::sendHeaders()]] を呼んで、登録された HTTP ヘッダを送出する。\n5. [[yii\\web\\Response::sendContent()]] を呼んで、レスポンスのボディ・コンテントを送出する。\n6. [[yii\\web\\Response::EVENT_AFTER_SEND]] イベントをトリガする。\n\n[[yii\\web\\Response::send()]] メソッドが一度呼び出された後では、このメソッドに対する更なる呼び出しは無視されます。\nこのことは、いったんレスポンスが送出された後では、それにコンテントを追加することは出来なくなる、ということを意味します。\n\nごらんのように、[[yii\\web\\Response::send()]] メソッドはいくつかの有用なイベントをトリガします。\nこれらのイベントに反応することによって、レスポンスを調整したり修飾したりすることが出来ます。\n"
  },
  {
    "path": "docs/guide-ja/runtime-routing.md",
    "content": "ルーティングと URL 生成\n=======================\n\nYii のアプリケーションがリクエストされた URL の処理を開始するときに、最初に実行するステップは URL を解析して [ルート](structure-controllers.md#routes) にすることです。\n次に、リクエストを処理するために、このルートを使って、対応する [コントローラ・アクション](structure-controllers.md) のインスタンスが作成されます。\nこのプロセスの全体が *ルーティング* と呼ばれます。\n\nルーティングの逆のプロセスが *URL 生成* と呼ばれます。\nこれは、与えられたルートとそれに結び付けられたクエリ・パラメータから URL を生成するものです。\n生成された URL が後でリクエストされたときには、ルーティングのプロセスがその URL を解決して元のルートとクエリ・パラメータに戻すことが出来ます。\n\nルーティングと URL 生成について主たる役割を果たすのが `urlManager` [アプリケーション・コンポーネント](structure-application-components.md) として登録されている\n[[yii\\web\\UrlManager|URL マネージャ]] です。\n[[yii\\web\\UrlManager|URL マネージャ]] は、入ってくるリクエストをルートとそれに結び付けられたクエリ・パラメータとして解析するための [[yii\\web\\UrlManager::parseRequest()|parseRequest()]] メソッドと、\n与えられたルートとそれに結び付けられたクエリ・パラメータから URL を生成するための [[yii\\web\\UrlManager::createUrl()|createUrl()]]\nメソッドを提供します。\n\nアプリケーション構成情報の `urlManager` コンポーネントを構成することによって、既存のアプリケーション・コードを修正することなく、\n任意の URL 形式をアプリケーションに認識させることが出来ます。\n例えば、`post/view` アクションのための URL を生成するためには、次のコードを使うことが出来ます。\n\n```php\nuse yii\\helpers\\Url;\n\n// Url::to() は UrlManager::createUrl() を呼び出して URL を生成します\n$url = Url::to(['post/view', 'id' => 100]);\n```\n\nこのコードによって生成される URL は、`urlManager` の構成に応じて、下記のどれか (またはその他) の形式になります。\nそして、こうして生成された URL が後でリクエストされた場合には、解析されて元のルートとクエリ・パラメータの値に戻されます。\n\n```\n/index.php?r=post%2Fview&id=100\n/index.php/post/100\n/posts/100\n```\n\n\n## URL 形式 <span id=\"url-formats\"></span>\n\n[[yii\\web\\UrlManager|URL マネージャ]] は二つの URL 形式をサポートします。すなわち、\n\n- デフォルトの URL 形式と、\n- 綺麗な URL (プリティ URL) の 形式。\n\nデフォルトの URL 形式は、`r` という [[yii\\web\\UrlManager::$routeParam|クエリ・パラメータ]] を使用してルートを表し、\n通常のクエリ・パラメータを使用してルートに結び付けられたクエリ・パラメータを表します。\n例えば、`/index.php?r=post/view&id=100` という URL は、`post/view` というルートと、`id` というクエリ・パラメータが 100 であることを表します。\nデフォルトの URL 形式は、[[yii\\web\\UrlManager|URL マネージャ]] についての構成を何も必要とせず、ウェブ・サーバの設定がどのようなものでも動作します。\n\n綺麗な URL 形式は、エントリ・スクリプトの名前に続く追加のパスを使用して、ルートとそれに結び付けられたクエリ・パラメータを表します。\n例えば、`/index.php/post/100` という URL の追加のパスは `/post/100` ですが、\n適切な [[yii\\web\\UrlManager::rules|URL 規則]] があれば、この追加のパスが `post/view` というルートと `id` のクエリ・パラメータ `100` を表すものとすることが出来ます。\n綺麗な URL 形式を使用するためには、URL をどのように表現すべきかという実際の要求に従って、\n一連の [[yii\\web\\UrlManager::rules|URL 規則]] を設計する必要があります。\n\nこの二つの URL 形式は、[[yii\\web\\UrlManager|URL マネージャ]] の [[yii\\web\\UrlManager::enablePrettyUrl|enablePrettyUrl]] プロパティを ON/OFF することによって、\n他のアプリケーション・コードを少しも変えることなく、切り替えることが出来ます。\n\n\n## ルーティング <span id=\"routing\"></span>\n\nルーティングは二つのステップを含みます。\n\n- まず、入ってくるリクエストが解析されて、ルートとそれに結び付けられたクエリ・パラメータに分解されます。\n- そして、解析されたルートに対応する [コントローラ・アクション](structure-controllers.md#actions)\nがリクエストを処理するために生成されます。\n\nデフォルトの URL 形式を使っている場合は、リクエストからルートを解析することは、\n`r` という名前の `GET` クエリ・パラメータを取得するだけの簡単なことです。\n\n綺麗な URL 形式を使っている場合は、[[yii\\web\\UrlManager|URL マネージャ]] が、登録されている [[yii\\web\\UrlManager::rules|URL 規則]] を調べます。\n合致する規則が見つかれば、リクエストをルートに解決することが出来ます。\n合致する規則が見つからなかったら、[[yii\\web\\NotFoundHttpException]] 例外が投げられます。\n\nいったんリクエストからルートが解析されたら、今度はルートによって特定されるコントローラ・アクションを生成する番です。\nルートはその中にあるスラッシュによって複数の部分に分けられます。例えば、`site/index` は `site` と `index` に分割されます。\nその各部分は、モジュール、コントローラ、または、アクションを参照する ID です。\nアプリケーションは、ルートの最初の部分の ID から始めて、下記のステップを踏んで、\nモジュール (もし有れば)、コントローラ、アクションを生成します。\n\n1. アプリケーションをカレント・モジュールとして設定します。\n2. カレント・モジュールの [[yii\\base\\Module::controllerMap|コントローラ・マップ]] が現在の ID を含むかどうかを調べます。\n   含んでいる場合は、マップの中で見つかった構成情報に従ってコントローラのオブジェクトが生成されます。\n   そして、ステップ 5 に跳んで、ルートの残りの部分を処理します。\n3. 現在の ID がカレント・モジュールの [[yii\\base\\Module::modules|modules]] プロパティのリストに挙げられたモジュールを指すものかどうかを調べます。\n   もしそうであれば、モジュールのリストで見つかった構成情報に従ってモジュールが生成されます。\n   そして、新しく生成されたモジュールのコンテキストのもとで、ステップ 2 に戻って、ルートの次の部分を処理します。\n4. 現在の ID を [コントローラ ID](structure-controllers.md#controller-ids) として扱ってコントローラ・オブジェクトを生成します。\n   そしてルートの残りの部分を持って次のステップに進みます。\n5. コントローラは、[[yii\\base\\Controller::actions()|アクション・マップ]] の中に現在の ID があるかどうかを調べます。\n   もし有れば、マップの中で見つかった構成情報に従ってアクションを生成します。\n   もし無ければ、現在の [アクション ID](structure-controllers.md#action-ids) に対応するアクション・メソッドで定義されるインライン・アクションを生成しようと試みます。\n\n上記のステップの中で、何かエラーが発生すると、[[yii\\web\\NotFoundHttpException]] が投げられて、\nルーティングのプロセスが失敗したことが示されます。\n\n\n### デフォルト・ルート <span id=\"default-route\"></span>\n\nリクエストから解析されたルートが空になった場合は、いわゆる *デフォルト・ルート* が代りに使用されることになります。\nデフォルトでは、デフォルト・ルートは `site/index` であり、`site` コントローラの `index` アクションを指します。\nデフォルト・ルートは、次のように、アプリケーションの構成情報の中でアプリケーションの [[yii\\web\\Application::defaultRoute|defaultRoute]]\nプロパティを構成することによって、カスタマイズすることが出来ます。\n\n```php\n[\n    // ...\n    'defaultRoute' => 'main/index',\n];\n```\n\nアプリケーションのデフォルト・ルートと同じく、モジュールにもデフォルト・ルートがあります。\n従って、例えば、`user` というモジュールがあって、リクエストの解析結果が `user` というルートになった場合、このモジュールの [[yii\\base\\Module::defaultRoute|defaultRoute]] がコントローラを決定するのに使用されます。\nデフォルトでは、このコントローラの名前は `default` となります。\n[[yii\\base\\Module::defaultRoute|defaultRoute]] でアクションが指定されていない場合は、コントローラの [[yii\\base\\Controller::defaultAction|defaultAction]] プロパティがアクションを決定するのに使用されます。\nこの例の場合だと、完全なルートは `user/default/index` となります。\n\n\n### `catchAll` ルート <span id=\"catchall-route\"></span>\n\nたまには、ウェブ・アプリケーションを一時的にメンテナンス・モードにして、全てのリクエストに対して同じ「お知らせ」のページを表示したいことがあるでしょう。\nこの目的を達する方法はたくさんありますが、最も簡単な方法の一つは、次のように、\nアプリケーションの構成情報の中で [[yii\\web\\Application::catchAll]] プロパティを構成することです。\n\n```php\n[\n    // ...\n    'catchAll' => ['site/offline'],\n];\n```\n\n上記の構成によって、入ってくる全てのリクエストを処理するために `site/offline` アクションが使われるようになります。\n\n`catchAll` プロパティは配列を取り、最初の要素はルートを指定し、残りの要素 (「名前-値」のペア) は\n[アクションのパラメータ](structure-controllers.md#action-parameters) を指定するものでなければなりません。\n\n> Info: このプロパティを有効にすると、開発環境で [デバッグ・ツール・バー](https://github.com/yiisoft/yii2-debug/blob/master/docs/guide-ja/README.md)が\n> 動作しなくなります。\n\n\n## URL を生成する <span id=\"creating-urls\"></span>\n\nYii は、与えられたルートとそれに結び付けられたクエリ・パラメータからさまざまな URL を生成する\n[[yii\\helpers\\Url::to()]] というヘルパ・メソッドを提供しています。例えば、\n\n```php\nuse yii\\helpers\\Url;\n\n// ルートへの URL を生成する: /index.php?r=post%2Findex\necho Url::to(['post/index']);\n\n// パラメータを持つルートへの URL を生成する: /index.php?r=post%2Fview&id=100\necho Url::to(['post/view', 'id' => 100]);\n\n// アンカー付きの URL を生成する: /index.php?r=post%2Fview&id=100#content\necho Url::to(['post/view', 'id' => 100, '#' => 'content']);\n\n// 絶対 URL を生成する: https://www.example.com/index.php?r=post%2Findex\necho Url::to(['post/index'], true);\n\n// https スキームを使って絶対 URL を生成する: https://www.example.com/index.php?r=post%2Findex\necho Url::to(['post/index'], 'https');\n```\n\n上記の例では、デフォルトの URL 形式が使われていると仮定していることに注意してください。\n綺麗な URL 形式が有効になっている場合は、生成される URL は、使われている [[yii\\web\\UrlManager::rules|URL 規則]] に従って、異なるものになります。\n\n[[yii\\helpers\\Url::to()]] メソッドに渡されるルートの意味は、コンテキストに依存します。\nルートは *相対* ルートか *絶対* ルートかのどちらかであり、下記の規則によって正規化されます。\n\n- ルートが空文字列である場合は、現在リクエストされている [[yii\\web\\Controller::route|ルート]] が使用されます。\n- ルートがスラッシュを全く含まない場合は、カレント・コントローラのアクション ID であると見なされて、\n   カレント・コントローラの [[\\yii\\web\\Controller::uniqueId|uniqueId]] の値が前置されます。\n- ルートが先頭にスラッシュを含まない場合は、カレント・モジュールに対する相対ルートと見なされて、\n   カレント・モジュールの [[\\yii\\base\\Module::uniqueId|uniqueId]] の値が前置されます。\n\nバージョン 2.0.2 以降では、[エイリアス](concept-aliases.md) の形式でルートを指定することが出来ます。\nその場合は、エイリアスが最初に実際のルートに変換され、\nそのルートが上記の規則に従って絶対ルートに変換されます。\n\n例えば、カレント・モジュールが `admin` であり、カレント・コントローラが `post` であると仮定すると、\n\n```php\nuse yii\\helpers\\Url;\n\n// 現在リクエストされているルート: /index.php?r=admin%2Fpost%2Findex\necho Url::to(['']);\n\n// アクション ID だけの相対ルート: /index.php?r=admin%2Fpost%2Findex\necho Url::to(['index']);\n\n// 相対ルート: /index.php?r=admin%2Fpost%2Findex\necho Url::to(['post/index']);\n\n// 絶対ルート: /index.php?r=post%2Findex\necho Url::to(['/post/index']);\n\n// \"/post/index\" と定義されているエイリアス \"@posts\"を使用: /index.php?r=post%2Findex\necho Url::to(['@posts']);\n```\n\n[[yii\\helpers\\Url::to()]] メソッドは、[[yii\\web\\UrlManager|URL マネージャ]] の\n[[yii\\web\\UrlManager::createUrl()|createUrl()]] メソッド、および、[[yii\\web\\UrlManager::createAbsoluteUrl()|createAbsoluteUrl()]]\nを呼び出すことによって実装されています。\n次に続くいくつかの項では、[[yii\\web\\UrlManager|URL マネージャ]] を構成して、生成される URL の形式をカスタマイズする方法を説明します。\n\n[[yii\\helpers\\Url::to()]] メソッドは、特定のルートとの関係を持た**ない** URL の生成もサポートしています。\nその場合、最初のパラメータには、配列を渡す代りに文字列を渡さなければなりません。例えば、\n \n```php\nuse yii\\helpers\\Url;\n\n// 現在リクエストされている URL: /index.php?r=admin%2Fpost%2Findex\necho Url::to();\n\n// エイリアス化された URL: https://example.com\nYii::setAlias('@example', 'https://example.com/');\necho Url::to('@example');\n\n// 絶対 URL: https://example.com/images/logo.gif\necho Url::to('/images/logo.gif', true);\n```\n\n`to()` メソッドの他にも、[[yii\\helpers\\Url]]` ヘルパ・クラスは、便利な URL 生成メソッドをいくつか提供しています。\n例えば、\n\n```php\nuse yii\\helpers\\Url;\n\n// ホームページの URL: /index.php?r=site%2Findex\necho Url::home();\n\n// ベース URL。アプリケーションがウェブ・ルートのサブ・ディレクトリに配置されているときに便利\necho Url::base();\n\n// 現在リクエストされている URL の canonical URL。\n// https://en.wikipedia.org/wiki/Canonical_link_element を参照\necho Url::canonical();\n\n// 現在リクエストされている URL を記憶し、それを後のリクエストの中で呼び戻す。\nUrl::remember();\necho Url::previous();\n```\n\n\n## 綺麗な URL を使う <span id=\"using-pretty-urls\"></span>\n\n綺麗な URL を使うためには、アプリケーションの構成情報の中で `urlManager` コンポーネントを次のように構成します。\n\n```php\n[\n    'components' => [\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            'showScriptName' => false,\n            'enableStrictParsing' => false,\n            'rules' => [\n                // ...\n            ],\n        ],\n    ],\n]\n```\n\n[[yii\\web\\UrlManager::enablePrettyUrl|enablePrettyUrl]] プロパティは、綺麗な URL 形式の有効/無効を切り替えますので、必須です。\nその他のプロパティはオプションですが、上記で示されている構成が最もよく用いられているものです。\n\n* [[yii\\web\\UrlManager::showScriptName|showScriptName]]: このプロパティは、生成される URL にエントリ・スクリプトを含めるべきかどうかを決定します。\n  例えば、このプロパティを `false` にすると、`/index.php/post/100` という URL を生成する代りに、\n  `/post/100` という URL を生成することが出来ます。\n* [[yii\\web\\UrlManager::enableStrictParsing|enableStrictParsing]]: このプロパティは、厳密なリクエスト解析を有効にするかどうかを決定します。\n  厳密な解析が有効にされた場合、リクエストされた URL が有効なリクエストとして扱われるためには、それが [[yii\\web\\UrlManager::rules|rules]] の少なくとも一つに合致しなければなりません。\n  そうでなければ、[[yii\\web\\NotFoundHttpException]] が投げられます。\n  厳密な解析が無効にされた場合は、リクエストされた URL が [[yii\\web\\UrlManager::rules|rules]] のどれにも合致しない場合は、\n  URL のパス情報の部分がリクエストされたルートとして扱われます。\n* [[yii\\web\\UrlManager::rules|rules]]: このプロパティが URL を解析および生成するための一連の規則を含みます。\n  このプロパティが、アプリケーションの固有の要求を満たす形式を持つ URL を生成するために、\n  あなたが主として使うプロパティです。\n\n> Note: 生成された URL からエントリ・スクリプト名を隠すためには、[[yii\\web\\UrlManager::showScriptName|showScriptName]] を `false` に設定するだけでなく、\n  ウェブ・サーバを構成して、リクエストされた URL が PHP スクリプトを明示的に指定していない場合でも、\n  正しい PHP スクリプトを特定出来るようにする必要があります。\nもしあなたが Apache または nginx ウェブ・サーバを使うつもりなら、[インストール](start-installation.md#recommended-apache-configuration)\n  のセクションで説明されている推奨設定を参照することが出来ます。\n\n\n### URL 規則 <span id=\"url-rules\"></span>\n\nURL 規則は [[yii\\web\\UrlRuleInterface]] を実装するクラス、通常は、[[yii\\web\\UrlRule]] クラスです。\nすべての URL 規則は、URL のパス情報の部分との照合に使われるパターン、ルート、そして、いくつかのクエリ・パラメータから構成されます。\nURL 規則は、パターンがリクエストされた URL と合致する場合に、リクエストの解析に使用することが出来ます。\nまた、URL 規則は、ルートとクエリ・パラメータ名が与えられたものと合致する場合に、URL の生成に使用することが出来ます。\n\n綺麗な URL 形式が有効にされている場合、[[yii\\web\\UrlManager|URL マネージャ]] は、その [[yii\\web\\UrlManager::rules|rules]] プロパティに宣言されている URL 規則を使って、\n入ってくるリクエストの解析と URL の生成を行います。\n具体的に言えば、入ってくるリクエストを解析するためには、[[yii\\web\\UrlManager|URL マネージャ]] は宣言されている順に規則を調べて、\nリクエストされた URL に合致する *最初の* 規則を探します。\nそして、その合致する規則を使って URL を解析して、ルートとそれに結び付けられたパラメータを得ます。\n同じように、URL を生成するためには、[[yii\\web\\UrlManager|URL マネージャ]] は、与えられたルートとパラメータに合致する最初の規則を探して、それを使って URL を生成します。\n\n[[yii\\web\\UrlManager::rules]] は、 [[yii\\web\\UrlRule::$pattern|パターン]] をキーとし、それに対応する [[yii\\web\\UrlRule::$route|ルート]] を値とする配列として構成することが出来ます。\n「パターン - ルート」のペアが、それぞれ、URL 規則を構成します。\n例えば、次の [[yii\\web\\UrlManager::rules|rules]] の構成は、二つの URL 規則を宣言するものです。\n最初の規則は `posts` という URL に合致し、それを `post/index` というルートにマップします。\n第二の規則は `post/(\\d+)` という正規表現にマッチする URL に合致し、それを `post/view` というルートと `id` という名前のパラメータにマップします。\n\n```php\n'rules' => [\n    'posts' => 'post/index',\n    'post/<id:\\d+>' => 'post/view',\n]\n```\n\n> Info: 規則のパターンは URL のパス情報の部分との照合に使用されます。\n  例えば、`/index.php/post/100?source=ad` のパス情報は `post/100` であり (先頭と末尾のスラッシュは無視します)、\n  これは `post/(\\d+)` というパターンに合致します。\n\nURL 規則は、「パターン - ルート」のペアとして宣言する以外に、構成情報配列として宣言することも出来ます。\n構成情報の一つの配列が、それぞれ、一つの URL 規則のオブジェクトを構成するために使われます。\nこの形式は、URL 規則の他のプロパティを構成したい場合に、よく必要になります。例えば、\n\n```php\n'rules' => [\n    // ... 他の URL 規則 ...\n    [\n        'pattern' => 'posts',\n        'route' => 'post/index',\n        'suffix' => '.json',\n    ],\n]\n```\n\nURL 規則の構成情報で `class` を指定しない場合は、デフォルトとして、[[yii\\web\\UrlRule]] が使われます。\nこのクラスが、[[yii\\web\\UrlManager::$ruleConfig]] で\nデフォルト値として定義されています。\n\n\n### 名前付きパラメータ <span id=\"named-parameters\"></span>\n\nURL 規則は、パターンの中で `<ParamName:RgExp>` の形式で指定される、名前付きクエリ・パラメータと結び付けることが出来ます。\nここで、`ParamName` はパラメータ名を指定し、`RegExp` はパラメータの値との照合に使われるオプションの正規表現を指定するものです。\n`RegExp` が指定されていない場合は、\nパラメータの値がスラッシュを含まない文字列であるべきことを意味します。\n\n> Note: 正規表現はパラメータの中でのみ使用できます。パターンの残りの部分はプレーンテキストとして解釈されます。\n\n規則が URL の解析に使われるときには、URL の対応する部分に合致した値が、結び付けられたパラメータに入れられます。\nそして、そのパラメータは、後に `request` アプリケーション・コンポーネントによって、`$_GET` に入れられて利用できるようになります。\n規則が URL の生成に使われるときは、提供されたパラメータの値を受けて、\nパラメータが宣言されている所にその値が挿入されます。\n\n名前付きパラメータの動作を説明するためにいくつかの例を挙げましょう。次の三つの URL 規則を宣言したと仮定してください。\n\n```php\n'rules' => [\n    'posts/<year:\\d{4}>/<category>' => 'post/index',\n    'posts' => 'post/index',\n    'post/<id:\\d+>' => 'post/view',\n]\n```\n\n規則が URL 解析に使われる場合は、\n\n- `/index.php/posts` は、二番目の規則を使って解析され、ルート `post/index` になります。\n- `/index.php/posts/2014/php` は、最初の規則を使って解析され、ルートは `post/index`、`year` パラメータの値は 2014、\n  そして、`category` パラメータの値は `php` となります。\n- `/index.php/post/100` は、三番目の規則を使って解析され、ルートが `post/view`、\n  `id` パラメータの値が 100 となります。\n- `/index.php/posts/php` は、どのパターンにも合致しないため、[[yii\\web\\UrlManager::enableStrictParsing]] が `true` の場合は、[[yii\\web\\NotFoundHttpException]] を引き起こします。\n  [[yii\\web\\UrlManager::enableStrictParsing]] が `false` (これがデフォルト値です) の場合は、パス情報の部分である `posts/php` がルートとして返されることになります。\n  こうして解析されたルートに対応するアクションがあればそれが実行され、そうでなければ [[yii\\web\\NotFoundHttpException]] が投げられます。\n\n規則が URL 生成に使われる場合は、\n\n- `Url::to(['post/index'])` は、二番目の規則を使って、`/index.php/posts` を生成します。\n- `Url::to(['post/index', 'year' => 2014, 'category' => 'php'])` は、最初の規則を使って、`/index.php/posts/2014/php` を生成します。\n- `Url::to(['post/view', 'id' => 100])` は、三番目の規則を使って、`/index.php/post/100` を生成します。\n- `Url::to(['post/view', 'id' => 100, 'source' => 'ad'])` も、三番目の規則を使って、`/index.php/post/100?source=ad` を生成します。\n  `source` パラメータは規則の中で指定されていないので、クエリ・パラメータとして、生成される URL に追加されます。\n- `Url::to(['post/index', 'category' => 'php'])` は、どの規則も使わずに、`/index.php/post/index?category=php` を生成します。\n  どの規則も当てはまらないため、URL は、単純に、ルートをパス情報とし、\n  すべてのパラメータをクエリ文字列として追加して生成されます。\n\n\n### ルートをパラメータ化する <span id=\"parameterizing-routes\"></span>\n\nURL 規則のルートにはパラメータ名を埋め込むことが出来ます。このことによって、URL 規則を複数のルートに合致させることが可能になっています。\n例えば、以下の規則は `controller` と `action` というパラメータをルートに埋め込んでいます。\n\n```php\n'rules' => [\n    '<controller:(post|comment)>/create' => '<controller>/create',\n    '<controller:(post|comment)>/<id:\\d+>/<action:(update|delete)>' => '<controller>/<action>',\n    '<controller:(post|comment)>/<id:\\d+>' => '<controller>/view',\n    '<controller:(post|comment)>s' => '<controller>/index',\n]\n```\n\n`/index.php/comment/100/update` という URL の解析には、二番目の規則が適用され、`controller` パラメータには `comment`、`action` パラメータには `update` がセットされます。\nこうして、`<controller>/<action>` というルートは、`comment/update` として解決されます。\n\n同じように、`comment/index` というルートの URL を生成するためには、最後の規則が適用されて、`index.php/comments` という URL が生成されます。\n\n> Info: ルートをパラメータ化することによって、URL 規則の数を大幅に減らすことが可能になり、\n  [[yii\\web\\UrlManager|URL マネージャ]] のパフォーマンスを目に見えて改善することが出来ます。\n\n### デフォルトのパラメータ値 <span id=\"default-parameter-values\"></span>\n\nデフォルトでは、規則の中で宣言されたパラメータは必須となります。\nリクエストされた URL が特定のパラメータを含まない場合や、特定のパラメータなしで URL を生成する場合には、規則は適用されません。\nパラメータのどれかをオプション扱いにしたい場合は、規則の [[yii\\web\\UrlRule::defaults|defaults]] プロパティを構成することが出来ます。\nこのプロパティのリストに挙げられたパラメータはオプション扱いとなり、提供されなかった場合は指定された値を取るようになります。\n\n次の規則の宣言においては、`page` と `tag` のパラメータは両方ともオプション扱いで、\n提供されなかった場合は、それぞれ、1 と空文字列を取ります。\n\n```php\n'rules' => [\n    // ... 他の規則 ...\n    [\n        'pattern' => 'posts/<page:\\d+>/<tag>',\n        'route' => 'post/index',\n        'defaults' => ['page' => 1, 'tag' => ''],\n    ],\n]\n```\n\n上記の規則を以下の URL を解析または生成するために使用することが出来ます。\n\n* `/index.php/posts`: `page` は 1, `tag` は ''.\n* `/index.php/posts/2`: `page` は 2, `tag` は ''.\n* `/index.php/posts/2/news`: `page` は 2, `tag` は `'news'`.\n* `/index.php/posts/news`: `page` は 1, `tag` は `'news'`.\n\nオプション扱いのパラメータを使わなければ、同じ結果を得るために 4 個の規則を作らなければならなかったところです。\n\n> Note: [[yii\\web\\UrlRule::$pattern|pattern]] がオプション扱いのパラメータとスラッシュだけを含んでいるときは、\n  最初のパラメータは、他のパラメータが省略されている場合に限り、省略することが出来ます。\n\n\n### サーバ名を持つ規則 <span id=\"rules-with-server-names\"></span>\n\nURL 規則のパターンには、ウェブ・サーバ名を含むことが出来ます。\nこのことが役に立つのは、主として、あなたのアプリケーションがウェブ・サーバ名によって異なる動作をしなければならない場合です。\n例えば、次の規則は、`https://admin.example.com/login` という URL を `admin/user/login` のルートとして解析し、`https://www.example.com/login` を `site/login` として解析するものです。\n\n```php\n'rules' => [\n    'https://admin.example.com/login' => 'admin/user/login',\n    'https://www.example.com/login' => 'site/login',\n]\n```\n\nサーバ名にパラメータを埋め込んで、そこから動的な情報を抽出することも出来ます。\n例えば、次の規則は `https://en.example.com/posts` という URL を解析して、`post/index` というルートと `language=en` というパラメータを取得するものです。\n\n```php\n'rules' => [\n    'http://<language:\\w+>.example.com/posts' => 'post/index',\n]\n```\n\nバージョン 2.0.11 以降は、`http` と `https` の両方に通用する、プロトコル相対パターンを使うことも出来ます。\n記法は上記と同じです、ただ、`http:` の部分を省略します。例えば、`'//www.example.com/login' => 'site/login'`。\n\n> Note: サーバ名を持つ規則は、そのパターンに、エントリ・スクリプトのサブフォルダを**含まない**ようにすべきです。\n例えば、アプリケーションのエントリ・スクリプトが `https://www.example.com/sandbox/blog/index.php` である場合は、`https://www.example.com/sandbox/blog/posts` ではなく、`https://www.example.com/posts` というパターンを使うべきです。\nこうすれば、アプリケーションをどのようなディレクトリに配置しても、URL 規則を変更する必要がなくなります。Yii はアプリケーションのベース URL を自動的に検出します。\n\n\n### URL 接尾辞 <span id=\"url-suffixes\"></span>\n\nさまざまな目的から URL に接尾辞を追加したいことがあるでしょう。\n例えば、静的な HTML ページに見えるように、`.html` を URL に追加したいかも知れません。\nまた、レスポンスとして期待されているコンテント・タイプを示すために、`.json` を URL に追加したい場合もあるでしょう。\nアプリケーションの構成情報で、次のように、[[yii\\web\\UrlManager::suffix]] プロパティを構成することによって、この目的を達することが出来ます。\n\n```php\n[\n    // ...\n    'components' => [\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            // ...\n            'suffix' => '.html',\n            'rules' => [\n                // ...\n            ],\n        ],\n    ],\n]\n```\n\n上記の構成によって、[[yii\\web\\UrlManager|URL マネージャ]] は、接尾辞として `.html` の付いた URL を認識し、\nまた、生成するようになります。\n\n> Tip: URL が全てスラッシュで終るようにするためには、URL 接尾辞として `/` を設定することが出来ます。\n\n> Note: URL 接尾辞を構成すると、リクエストされた URL が接尾辞を持たない場合は、認識できない URL であると見なされるようになります。\n  これは、異なる URL 上の重複コンテンツを防止するためのものであり、SEO (検索エンジン最適化) の見地からも推奨されるプラクティスです。\n\n場合によっては、URL によって異なる接尾辞を使いたいことがあるでしょう。\nその目的は、個々の URL 規則の [[yii\\web\\UrlRule::suffix|suffix]] プロパティを構成することによって達成できます。\nURL 規則にこのプロパティが設定されている場合は、それが [[yii\\web\\UrlManager|URL マネージャ]] レベルの接尾辞の設定をオーバーライドします。\n例えば、次の構成には、グローバルな接尾辞 `.html` の代りに `.json` を使用するカスタマイズされた URL 規則が含まれています。\n\n```php\n[\n    'components' => [\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            // ...\n            'suffix' => '.html',\n            'rules' => [\n                // ...\n                [\n                    'pattern' => 'posts',\n                    'route' => 'post/index',\n                    'suffix' => '.json',\n                ],\n            ],\n        ],\n    ],\n]\n```\n\n### HTTP メソッド <span id=\"http-methods\"></span>\n\nRESTful API を実装するときは、使用されている HTTP メソッドに応じて、同一の URL を異なるルートとして解析することが必要になる場合がよくあります。\nこれは、規則のパターンにサポートされている HTTP メソッドを前置することによって、簡単に達成することが出来ます。\n一つの規則が複数の HTTP メソッドをサポートする場合は、メソッド名をカンマで区切ります。\n例えば、次の三つの規則は、`post/<id:\\d+>` という同一のパターンを持って、異なる HTTP メソッドをサポートするものです。\n`PUT post/100` に対するリクエストは `post/update` と解析され、`GET post/100` に対するリクエストは `post/view` と解析されることになります。\n\n```php\n'rules' => [\n    'PUT,POST post/<id:\\d+>' => 'post/update',\n    'DELETE post/<id:\\d+>' => 'post/delete',\n    'post/<id:\\d+>' => 'post/view',\n]\n```\n\n> Note: URL 規則が HTTP メソッドをパターンに含む場合、指定されたメソッドに `GET` が入っていない限り、その規則は解析目的にだけ使用されます。\n  [[yii\\web\\UrlManager|URL マネージャ]] が URL 生成のために呼ばれたときは、その規則はスキップされます。\n\n> Tip: RESTful API のルーティングを簡単にするために、Yii は特別な URL 規則クラス [[yii\\rest\\UrlRule]] を提供しています。\n  これは非常に効率的なもので、コントローラ ID の自動的な複数形化など、いくつかの素敵な機能をサポートするものです。\n  詳細については、RESTful API 開発についての [ルーティング](rest-routing.md) のセクションを参照してください。\n\n\n### 規則を動的に追加する <span id=\"adding-rules\"></span>\n\nURL 規則は [[yii\\web\\UrlManager|URL マネージャ]] に動的に追加することが出来ます。\nこのことは、再配布可能な [モジュール](structure-modules.md) が自分自身の URL 規則を管理する必要がある場合に、しばしば必要になります。\n動的に追加された規則がルーティングのプロセスで効果を発揮するためには、その規則をアプリケーションの [ブートストラップ](runtime-bootstrapping.md) の段階で追加しなければなりません。\nこれは、モジュールにとっては、次のように、[[yii\\base\\BootstrapInterface]] を実装して、\n[[yii\\base\\BootstrapInterface::bootstrap()|bootstrap()]] メソッドの中で規則を追加しなければならないことを意味します。\n\n```php\npublic function bootstrap($app)\n{\n    $app->getUrlManager()->addRules([\n        // ここに規則の宣言\n    ], false);\n}\n```\n\nさらに、モジュールが [ブートストラップ](runtime-bootstrapping.md) の過程に関与できるように、\nそれを [[yii\\web\\Application::bootstrap]] のリストに挙げなければならないことに注意してください。\n\n\n### 規則クラスを作成する <span id=\"creating-rules\"></span>\n\nデフォルトの [[yii\\web\\UrlRule]] クラスはほとんどのプロジェクトに対して十分に柔軟なものであるというのは事実ですが、それでも、自分自身で規則クラスを作る必要があるような状況はあります。\n例えば、自動車ディーラーのウェブ・サイトにおいて、`/Manufacturer/Model` のような URL 形式をサポートしたいけれども、\n`Manufacturer` と `Model` は、両方とも、データベース・テーブルに保存されている何らかのデータに合致するものでなければならない、というような場合です。\nデフォルトの規則クラスは、静的に宣言されるパターンに依拠しているため、ここでは役に立ちません。\n\nこの問題を解決するために、次のような URL 規則クラスを作成することが出来ます。\n\n```php\n<?php\n\nnamespace app\\components;\n\nuse yii\\web\\UrlRuleInterface;\nuse yii\\base\\BaseObject;\n\nclass CarUrlRule extends BaseObject implements UrlRuleInterface\n{\n    public function createUrl($manager, $route, $params)\n    {\n        if ($route === 'car/index') {\n            if (isset($params['manufacturer'], $params['model'])) {\n                return $params['manufacturer'] . '/' . $params['model'];\n            } elseif (isset($params['manufacturer'])) {\n                return $params['manufacturer'];\n            }\n        }\n        return false; // この規則は適用されない\n    }\n\n    public function parseRequest($manager, $request)\n    {\n        $pathInfo = $request->getPathInfo();\n        if (preg_match('%^(\\w+)(/(\\w+))?$%', $pathInfo, $matches)) {\n            // $matches[1] と $matches[3] をチェックして、\n            // データベースの中の製造者とモデルに合致するかどうか調べる\n            // 合致すれば、$params['manufacturer'] および/または $params['model']\n            // をセットし、['car/index', $params] を返す\n        }\n        return false; // この規則は適用されない\n    }\n}\n```\n\nそして、[[yii\\web\\UrlManager::rules]] の構成情報で、新しい規則クラスを使います。\n\n```php\n'rules' => [\n    // ... 他の規則 ...\n    [\n        'class' => 'app\\components\\CarUrlRule', \n        // ... 他のプロパティを構成する ...\n    ],\n]\n```\n\n\n## URL の正規化 <span id=\"url-normalization\"></span>\n\nバージョン 2.0.10 以降、[[yii\\web\\UrlManager|UrlManager]] で [[yii\\web\\UrlNormalizer|UrlNormalizer]] を使って、\n同一 URL のバリエーション (例えば、末尾のスラッシュの有無) の問題を処理する出来るようになりました。\n技術的には `https://example.com/path` と `https://example.com/path/` は別の URL ですから、これらの両方に同一のコンテントを提供することは SEO ランキングを低下させる可能性があります。\nデフォルトでは、URL ノーマライザは、連続したスラッシュを畳み、サフィックスが末尾のスラッシュを持っているかどうかに従って末尾のスラッシュを追加または削除し、\n正規化された URL に [恒久的な移動](https://en.wikipedia.org/wiki/HTTP_301) を使ってリダイレクトします。\nノーマライザは、URL マネージャのためにグローバルに構成することも、各規則のために個別に構成することも出来ます。\n各規則は、デフォルトでは、URL マネージャのノーマライザを使用します。\n[[yii\\web\\UrlRule::$normalizer|UrlRule::$normalizer]] を `false` にすれば、特定の URL 規則について正規化を無効にすることが出来ます。\n\n次に、[[yii\\web\\UrlNormalizer|UrlNormalizer]] の構成例を示します。\n\n```php\n'urlManager' => [\n    'enablePrettyUrl' => true,\n    'showScriptName' => false,\n    'enableStrictParsing' => true,\n    'suffix' => '.html',\n    'normalizer' => [\n        'class' => 'yii\\web\\UrlNormalizer',\n        // デバッグのために、恒久的移動のかわりに一時的リダイレクションを使う\n        'action' => UrlNormalizer::ACTION_REDIRECT_TEMPORARY,\n    ],\n    'rules' => [\n        // ... 他の規則 ...\n        [\n            'pattern' => 'posts',\n            'route' => 'post/index',\n            'suffix' => '/',\n            'normalizer' => false, // この規則では正規化を無効にする\n        ],\n        [\n            'pattern' => 'tags',\n            'route' => 'tag/index',\n            'normalizer' => [\n                // この規則では連続するスラッシュを畳まない\n                'collapseSlashes' => false,\n            ],\n        ],\n    ],\n]\n```\n\n> Note: デフォルトでは [[yii\\web\\UrlManager::$normalizer|UrlManager::$normalizer]] は無効になっています。\n  URL の正規化を有効にするためには、明示的に構成する必要があります。\n\n\n\n## パフォーマンスに対する考慮 <span id=\"performance-consideration\"></span>\n\n複雑なウェブ・アプリケーションを開発するときは、リクエストの解析と URL 生成に要する時間を削減するために\nURL 規則を最適化することが重要になります。\n\nパラメータ化したルートを使うことによって、URL 規則の数を減らして、パフォーマンスを著しく向上させることが出来ます。\n\nURL を解析または生成するときに、[[yii\\web\\UrlManager|URL マネージャ]] は、宣言された順序で URL 規則を調べます。\n従って、より多く使われる規則がより少なく使われる規則より前に来るように順序を調整することを検討してください。\n\nパターンまたはルートに共通の先頭部分を持つ URL 規則がある場合は、[[yii\\web\\UrlManager|URL マネージャ]] がそれらをグループ化して効率的に調べることが出来るように、\n[[yii\\web\\GroupUrlRule]] を使うことを検討してください。\nあなたのアプリケーションがモジュールによって構成されており、モジュールごとに、モジュール ID を共通の先頭部分とする一群の URL 規則を持っている場合は、通常、このことが当てはまります。\n"
  },
  {
    "path": "docs/guide-ja/runtime-sessions-cookies.md",
    "content": "セッションとクッキー\n====================\n\nセッションとクッキーは、データが複数回のユーザ・リクエストにまたがって持続することを可能にします。\n素の PHP では、それぞれ、グローバル変数 `$_SESSION` と `$_COOKIE` によってアクセスすることが出来ます。\nYii はセッションとクッキーをオブジェクトとしてカプセル化し、オブジェクト指向の流儀でアクセスできるようにするとともに、有用な機能強化を追加しています。\n\n\n## セッション <span id=\"sessions\"></span>\n\n[リクエスト](runtime-requests.md) や [レスポンス](runtime-responses.md) と同じように、\nデフォルトでは [[yii\\web\\Session]] のインスタンスである `session` [アプリケーション・コンポーネント] によって、\nセッションにアクセスすることが出来ます。\n\n\n### セッションのオープンとクローズ <span id=\"opening-closing-sessions\"></span>\n\nセッションのオープンとクローズは、次のようにして出来ます。\n\n```php\n$session = Yii::$app->session;\n\n// セッションが既に開かれているかチェックする\nif ($session->isActive) ...\n\n// セッションを開く\n$session->open();\n\n// セッションを閉じる\n$session->close();\n\n// セッションに登録されている全てのデータを破壊する\n$session->destroy();\n```\n\nエラーを発生させずに [[yii\\web\\Session::open()|open()]] と [[yii\\web\\Session::close()|close()]] を複数回呼び出すことが出来ます。\n内部的には、これらのメソッドは、セッションが既に開かれているかどうかを最初にチェックします。\n\n\n### セッション・データにアクセスする <span id=\"access-session-data\"></span>\n\nセッションに保存されているデータにアクセスするためには、次のようにすることが出来ます。\n\n```php\n$session = Yii::$app->session;\n\n// セッション変数を取得する。次の三つの用法は等価。\n$language = $session->get('language');\n$language = $session['language'];\n$language = isset($_SESSION['language']) ? $_SESSION['language'] : null;\n\n// セッション変数を設定する。次の三つの用法は等価。\n$session->set('language', 'en-US');\n$session['language'] = 'en-US';\n$_SESSION['language'] = 'en-US';\n\n// セッション変数を削除する。次の三つの用法は等価。\n$session->remove('language');\nunset($session['language']);\nunset($_SESSION['language']);\n\n// セッション変数が存在するかどうかをチェックする。次の三つの用法は等価。\nif ($session->has('language')) ...\nif (isset($session['language'])) ...\nif (isset($_SESSION['language'])) ...\n\n// 全てのセッション変数をたどる。次の二つの用法は等価。\nforeach ($session as $name => $value) ...\nforeach ($_SESSION as $name => $value) ...\n```\n\n> Info: `session` コンポーネントによってセッション・データにアクセスする場合は、まだ開かれていないときは、自動的にセッションが開かれます。\n  これに対して `$_SESSION` によってセッション・データにアクセスする場合は、\n  `session_start()` を明示的に呼び出すことが必要になります。\n\n配列であるセッション・データを扱う場合、`session` コンポーネントには、配列の要素を直接修正することができない、という制約があります。\n例えば、\n\n```php\n$session = Yii::$app->session;\n\n// 次のコードは動かない\n$session['captcha']['number'] = 5;\n$session['captcha']['lifetime'] = 3600;\n\n// 次のコードは動く\n$session['captcha'] = [\n    'number' => 5,\n    'lifetime' => 3600,\n];\n\n// 次のコードも動く\necho $session['captcha']['lifetime'];\n```\n\n次の回避策のどれかを使ってこの問題を解決することが出来ます。\n\n```php\n$session = Yii::$app->session;\n\n// $_SESSION を直接使う (既に Yii::$app->session->open() が呼び出されていることを確認)\n$_SESSION['captcha']['number'] = 5;\n$_SESSION['captcha']['lifetime'] = 3600;\n\n// 配列全体を取得し、修正して、保存しなおす\n$captcha = $session['captcha'];\n$captcha['number'] = 5;\n$captcha['lifetime'] = 3600;\n$session['captcha'] = $captcha;\n\n// 配列の代わりに ArrayObject を使う\n$session['captcha'] = new \\ArrayObject;\n...\n$session['captcha']['number'] = 5;\n$session['captcha']['lifetime'] = 3600;\n\n// 共通の接頭辞を持つキーを使って配列データを保存する\n$session['captcha.number'] = 5;\n$session['captcha.lifetime'] = 3600;\n```\n\nパフォーマンスとコードの可読性を高めるためには、最後の回避策を推奨します。\nすなわち、配列を一つのセッション変数として保存する代りに、配列の個々の要素を、他の配列要素と共通の接頭辞を持つ、\n個別のセッション変数として保存することです。\n\n\n### カスタム・セッション・ストレージ <span id=\"custom-session-storage\"></span>\n\nデフォルトの [[yii\\web\\Session]] クラスはセッション・データをサーバ上のファイルとして保存します。\nYii は、また、さまざまなセッション・ストレージを実装する下記のクラスをも提供しています。\n\n* [[yii\\web\\DbSession]]: セッション・データをデータベース・テーブルを使って保存する。\n* [[yii\\web\\CacheSession]]: セッション・データを、構成された [キャッシュ・コンポーネント](caching-data.md#cache-components) の力を借りて、キャッシュを使って保存する。\n* [[yii\\redis\\Session]]: セッション・データを [redis](https://redis.io/) をストレージ媒体として使って保存する。\n* [[yii\\mongodb\\Session]]: セッション・データを [MongoDB](https://www.mongodb.com/) に保存する。\n\nこれらのセッション・クラスは全て一連の同じ API メソッドをサポートします。\nその結果として、セッションを使用するアプリケーション・コードを修正することなしに、セッション・ストレージ・クラスを切り替えることが出来ます。\n\n> Note: カスタム・セッション・ストレージを使っているときに `$_SESSION` を通じてセッション・データにアクセスしたい場合は、\n  セッションが [[yii\\web\\Session::open()]] によって既に開始されていることを確認しなければなりません。\n  これは、カスタム・セッション・ストレージのハンドラは、この `open()` メソッドの中で登録されるからです。\n\n> Note: カスタム・セッション・ストレージを使うときは、セッションのガーベッジ・コレクタを明示的に構成する必要がある場合があります。\n  PHP のインストレーションによっては(例えば Debian では)、ガーベッジ・コレクタの蓋然性は 0 とされ、セッション・ファイルはクロン・ジョブでオフラインで消去することになっています。\n  このプロセスはあなたのカスタム・セッション・ストレージには当てはまりませんので、\n  [[yii\\web\\Session::$GCProbability]] に 0 でない値を設定して構成しなければなりません。\n  \nこれらのコンポーネント・クラスの構成方法と使用方法については、それらの API ドキュメントを参照してください。\n下記の例は、アプリケーションの構成情報において、データベース・テーブルをセッション・ストレージとして使うために [[yii\\web\\DbSession]]\nを構成する方法を示すものです。\n\n```php\nreturn [\n    'components' => [\n        'session' => [\n            'class' => 'yii\\web\\DbSession',\n            // 'db' => 'mydb',  // DB 接続のアプリケーション・コンポーネント ID。デフォルトは 'db'。\n            // 'sessionTable' => 'my_session', // セッション・テーブル名。デフォルトは 'session'。\n        ],\n    ],\n];\n```\n\nセッション・データを保存するために、次のようなデータベース・テーブルを作成することも必要です。\n\n```sql\nCREATE TABLE session\n(\n    id CHAR(40) NOT NULL PRIMARY KEY,\n    expire INTEGER,\n    data BLOB\n)\n```\n\nここで 'BLOB' はあなたが選んだ DBMS の BLOB 型を指します。下記は人気のあるいくつかの DBMS で使用できる BLOB 型です。\n\n- MySQL: LONGBLOB\n- PostgreSQL: BYTEA\n- MSSQL: BLOB\n\n> Note: php.ini の `session.hash_function` の設定によっては、`id` カラムの長さを修正する必要があるかも知れません。\n  例えば、`session.hash_function=sha256` である場合は\n  40 の代りに 64 の長さを使わなければなりません。\n\n別の方法として、次のマイグレーションを使ってこれを達成することも出来ます。\n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m170529_050554_create_table_session extends Migration\n{\n    public function up()\n    {\n        $this->createTable('{{%session}}', [\n            'id' => $this->char(64)->notNull(),\n            'expire' => $this->integer(),\n            'data' => $this->binary()\n        ]);\n        $this->addPrimaryKey('pk-id', '{{%session}}', 'id');\n    }\n\n    public function down()\n    {\n        $this->dropTable('{{%session}}');\n    }\n}\n```\n\n### フラッシュ・データ <span id=\"flash-data\"></span>\n\nフラッシュ・データは特殊な種類のセッション・データで、あるリクエストの中で設定されると、\n次のリクエストの間においてのみ読み出すことが出来て、その後は自動的に削除されるものです。\nフラッシュ・データが最もよく使われるのは、エンド・ユーザに一度だけ表示されるべきメッセージ、\n例えば、ユーザのフォーム送信が成功した後に表示される確認メッセージなどを実装するときです。\n\n`session` アプリケーション・コンポーネントによって、フラッシュ・データを設定し、アクセスすることが出来ます。例えば、\n\n```php\n$session = Yii::$app->session;\n\n// リクエスト #1\n// \"postDeleted\" という名前のフラッシュ・メッセージを設定する\n$session->setFlash('postDeleted', '投稿の削除に成功しました。');\n\n// リクエスト #2\n// \"postDeleted\" という名前のフラッシュ・メッセージを表示する\necho $session->getFlash('postDeleted');\n\n// リクエスト #3\n// フラッシュ・メッセージは自動的に削除されるので、$result は false になる\n$result = $session->hasFlash('postDeleted');\n```\n\n通常のセッション・データと同様に、任意のデータをフラッシュ・データとして保存することが出来ます。\n\n[[yii\\web\\Session::setFlash()]] を呼び出すと、同じ名前の既存のフラッシュ・データは上書きされます。\n同じ名前の既存のメッセージに新しいフラッシュ・データを追加するためには、代りに [[yii\\web\\Session::addFlash()]] を使うことが出来ます。\n例えば、\n\n```php\n$session = Yii::$app->session;\n\n// リクエスト #1\n// \"alerts\" という名前の下にフラッシュ・メッセージを追加する\n$session->addFlash('alerts', '投稿の削除に成功しました。');\n$session->addFlash('alerts', '友達の追加に成功しました。');\n$session->addFlash('alerts', 'あなたのレベルが上りました。');\n\n// リクエスト #2\n// $alerts は \"alerts\" という名前の下にあるフラッシュ・メッセージの配列となる\n$alerts = $session->getFlash('alerts');\n```\n\n> Note: 同じ名前のフラッシュ・データに対して、[[yii\\web\\Session::setFlash()]] と [[yii\\web\\Session::addFlash()]] を一緒に使わないようにしてください。\n  これは、後者のメソッドが、同じ名前のフラッシュ・データを追加できるように、\n  フラッシュ・データを自動的に配列に変換するからです。\n  その結果、[[yii\\web\\Session::getFlash()]] を呼び出したとき、この二つのメソッドを呼び出した順序に従って、\n  あるときは配列を受け取り、あるときは文字列を受け取るということになります。\n\n> Tip: フラッシュ・メッセージを表示するためには、[[yii\\bootstrap\\Alert|bootstrap Alert]] ウィジェットを次のように使用することが出来ます。\n>\n> ```php\n> echo Alert::widget([\n>    'options' => ['class' => 'alert-info'],\n>    'body' => Yii::$app->session->getFlash('postDeleted'),\n> ]);\n> ```\n\n\n## クッキー <span id=\"cookies\"></span>\n\nYii は個々のクッキーを [[yii\\web\\Cookie]] のオブジェクトとして表します。\n[[yii\\web\\Request]] と [[yii\\web\\Response]] は、ともに、`cookies` という名前のプロパティによって、クッキーのコレクションを保持します。\n前者のクッキー・コレクションはリクエストの中で送信されてきたクッキーを表し、\n一方、後者のクッキー・コレクションは、これからユーザに送信されるクッキーを表します。\n\nアプリケーションで、リクエストとレスポンスを直接に操作する部分は、コントローラです。\n従って、クッキーの読み出しと送信はコントローラで実行されるべきです。\n\n### クッキーを読み出す <span id=\"reading-cookies\"></span>\n\n現在のリクエストに含まれるクッキーは、下記のコードを使って取得することが出来ます。\n\n```php\n// \"request\" コンポーネントからクッキー・コレクション (yii\\web\\CookieCollection) を取得する。\n$cookies = Yii::$app->request->cookies;\n\n// \"language\" というクッキーの値を取得する。クッキーが存在しない場合は、デフォルト値として \"en\" を返す。\n$language = $cookies->getValue('language', 'en');\n\n// \"language\" というクッキーの値を取得する別の方法。\nif (($cookie = $cookies->get('language')) !== null) {\n    $language = $cookie->value;\n}\n\n// $cookies を配列のように使うことも出来る。\nif (isset($cookies['language'])) {\n    $language = $cookies['language']->value;\n}\n\n// \"language\" というクッキーが在るかどうかチェックする。\nif ($cookies->has('language')) ...\nif (isset($cookies['language'])) ...\n```\n\n\n### クッキーを送信する <span id=\"sending-cookies\"></span>\n\n下記のコードを使って、クッキーをエンド・ユーザに送信することが出来ます。\n\n```php\n// \"response\" コンポーネントからクッキー・コレクション (yii\\web\\CookieCollection) を取得する。\n$cookies = Yii::$app->response->cookies;\n\n// 送信されるレスポンスに新しいクッキーを追加する。\n$cookies->add(new \\yii\\web\\Cookie([\n    'name' => 'language',\n    'value' => 'zh-CN',\n]));\n\n// クッキーを削除する。\n$cookies->remove('language');\n// 次のようにしても同じ。\nunset($cookies['language']);\n```\n\n[[yii\\web\\Cookie]] クラスは、上記の例で示されている [[yii\\web\\Cookie::name|name]] と [[yii\\web\\Cookie::value|value]]\nのプロパティ以外にも、[[yii\\web\\Cookie::domain|domain]] や [[yii\\web\\Cookie::expire|expire]] など、\n他のプロパティを定義して、利用可能なクッキー情報の全てを完全に表しています。\nクッキーを準備するときに必要に応じてこれらのプロパティを構成してから、レスポンスのクッキー・コレクションに追加することが出来ます。\n\n### クッキー検証 <span id=\"cookie-validation\"></span>\n\n最後の二つの項で示されているように、`request` と `response` のコンポーネントを通じてクッキーを読んだり送信したりする場合には、\nクッキーがクライアント・サイドで修正されるのを防止するクッキー検証という追加のセキュリティを享受することが出来ます。\nこれは、個々のクッキーにハッシュ文字列をサインとして追加することによって達成されます。\nアプリケーションは、サインを見て、クッキーがクライアント・サイドで修正されたかどうかを知ることが出来ます。\nもし、修正されていれば、そのクッキーは `request` コンポーネントの [[yii\\web\\Request::cookies|クッキー・コレクション]] からはアクセスすることが出来なくなります。\n\n> Note: クッキー検証は値が修正されたクッキーの読み込みを防止するだけです。\n  検証に失敗した場合でも、`$_COOKIE` を通じてそのクッキーにアクセスすることは引き続いて可能です。\n  これは、サードパーティのライブラリが、クッキー検証を含まない独自の方法でクッキーを操作することが出来るようにするするためです。\n\nクッキー検証はデフォルトで有効になっています。\n[[yii\\web\\Request::enableCookieValidation]] プロパティを `false` に設定することによって無効にすることが出来ますが、無効にしないことを強く推奨します。\n\n> Note: `$_COOKIE` と `setcookie()` によって直接に 読み出し/送信 されるクッキーは検証されません。\n\nクッキー検証を使用する場合は、前述のハッシュ文字列を生成するために使用される [[yii\\web\\Request::cookieValidationKey]] を指定しなければなりません。\nアプリケーションの構成情報で `request` コンポーネントを構成することによって、そうすることが出来ます。\n\n```php\nreturn [\n    'components' => [\n        'request' => [\n            'cookieValidationKey' => 'ここに秘密のキーを書く',\n        ],\n    ],\n];\n```\n\n> Info: [[yii\\web\\Request::cookieValidationKey|cookieValidationKey]] は、あなたのアプリケーションにとって、決定的に重要なものです。\n  これは信頼する人にだけ教えるべきものです。バージョン・コントロール・システムに保存してはいけません。\n\n## セキュリティの設定\n\n[[yii\\web\\Cookie]] と [[yii\\web\\Session]] の両者は下記のセキュリティ・フラグをサポートしています。\n\n### httpOnly\n\nセキュリティを向上させるために、[[yii\\web\\Cookie::httpOnly]] および [[yii\\web\\Session::cookieParams]] の 'httponly' パラメータの\nデフォルト値は `true` に設定されています。\nこれによって、クライアント・サイド・スクリプトが保護されたクッキーにアクセスする危険が軽減されます (ブラウザがサポートしていれば)。\n詳細については、[httpOnly の wiki 記事](https://owasp.org/www-community/HttpOnly) を読んでください。\n\n### secure\n\nsecure フラグの目的は、クッキーが平文で送信されることを防止することです。ブラウザが secure フラグをサポートしている場合、\nリクエストが secure な接続 (TLS) によって送信される場合にのみクッキーがリクエストに含まれます。\n詳細については [Secure フラグの wiki 記事](https://owasp.org/www-community/controls/SecureCookieAttribute) を参照して下さい。\n\n### sameSite\n\nYii 2.0.21 以降、[[yii\\web\\Cookie::sameSite]] 設定がサポートされています。これは PHP バージョン 7.4.0 以降を必要とします。\n`sameSite` 設定の目的は CSRF (Cross-Site Request Forgery) 攻撃を防止することです。\nブラウザが `sameSite` 設定をサポートしている場合、指定されたポリシー ('Lax' または 'Strict') に従うクッキーだけが送信されます。\n詳細については [SameSite の wiki 記事](https://owasp.org/www-community/SameSite) を参照して下さい。\n更なるセキュリティ強化のために、`sameSite` がサポートされていない PHP のバージョンで使われた場合には例外が投げられます。\nこの機能を PHP のバージョンに関わりなく使用する場合は、最初にバージョンをチェックして下さい。例えば\n\n```php\n[\n    'sameSite' => PHP_VERSION_ID >= 70300 ? yii\\web\\Cookie::SAME_SITE_LAX : null,\n]\n```\n> Note: 今はまだ `sameSite` 設定をサポートしていないブラウザもありますので、\n  [追加の CSRF 保護](security-best-practices.md#avoiding-csrf) を行うことを強く推奨します。\n\n## セッションに関する php.ini の設定\n\n[PHP マニュアル](https://www.php.net/manual/ja/session.security.ini.php) で示されているように、`php.ini` にはセッションのセキュリティに関する重要な設定があります。\n推奨される設定を必ず適用して下さい。特に、PHP インストールのデフォルトでは有効にされていない\n`session.use_strict_mode` を有効にして下さい。\nこの設定は [[yii\\web\\Session::useStrictMode]] を使って設定することも出来ます。\n"
  },
  {
    "path": "docs/guide-ja/security-authentication.md",
    "content": "認証\n====\n\n認証は、ユーザが誰であるかを確認するプロセスです。\n通常は、識別子 (ユーザ名やメール・アドレスなど) と秘密のトークン (パスワードやアクセス・トークンなど) を使って、ユーザがそうであると主張する通りのユーザであるか否かを判断します。\n認証がログイン機能の基礎となります。\n\nYii はさまざまなコンポーネントを結び付けてログインをサポートする認証フレームワークを提供しています。\nこのフレームワークを使用するために、あなたは主として次の仕事をする必要があります。\n\n* [[yii\\web\\User|user]] アプリケーション・コンポーネントを構成する。\n* [[yii\\web\\IdentityInterface]] インタフェイスを実装するクラスを作成する。\n\n\n## [[yii\\web\\User]] を構成する <span id=\"configuring-user\"></span>\n\n[[yii\\web\\User|user]] アプリケーション・コンポーネントがユーザの認証状態を管理します。\n実際の認証ロジックを含む [[yii\\web\\User::identityClass|ユーザ識別情報クラス]] は、あなたが指定しなければなりません。\n下記のアプリケーション構成情報においては、[[yii\\web\\User|user]] の [[yii\\web\\User::identityClass|ユーザ識別情報クラス]] は\n`app\\models\\User` であると構成されています。\n`app\\models\\User` の実装については、次の項で説明します。\n\n```php\nreturn [\n    'components' => [\n        'user' => [\n            'identityClass' => 'app\\models\\User',\n        ],\n    ],\n];\n```\n\n\n## [[yii\\web\\IdentityInterface]] を実装する <span id=\"implementing-identity\"></span>\n\n[[yii\\web\\User::identityClass|ユーザ識別情報クラス]] が実装しなければならない\n[[yii\\web\\IdentityInterface]] は次のメソッドを含んでいます。\n\n* [[yii\\web\\IdentityInterface::findIdentity()|findIdentity()]]: 指定されたユーザ ID を使ってユーザ識別情報クラスのインスタンスを探します。\n  セッションを通じてログイン状態を保持する必要がある場合に、このメソッドが使用されます。\n* [[yii\\web\\IdentityInterface::findIdentityByAccessToken()|findIdentityByAccessToken()]]: \n  指定されたアクセス・トークンを使ってユーザ識別情報クラスのインスタンスを探します。\n  単一の秘密のトークンでユーザを認証する必要がある場合 (ステートレスな RESTful アプリケーションなどの場合) に、このメソッドが使用されます。\n* [[yii\\web\\IdentityInterface::getId()|getId()]]: ユーザ識別情報クラスのインスタンスによって表されるユーザの ID を返します。\n* [[yii\\web\\IdentityInterface::getAuthKey()|getAuthKey()]]: 自動ログインが有効にされている場合に、\n  セッションと自動ログインを検証するキーを返します。\n* [[yii\\web\\IdentityInterface::validateAuthKey()|validateAuthKey()]]: \n  クッキー・ベースのログイン・キーを検証するロジックを実装します。\n\n特定のメソッドが必要でない場合は、中身を空にして実装しても構いません。例えば、あなたのアプリケーションが純粋なステート・レス RESTful アプリケーションであるなら、\n実装する必要があるのは [[yii\\web\\IdentityInterface::findIdentityByAccessToken()|findIdentityByAccessToken()]] と\n[[yii\\web\\IdentityInterface::getId()|getId()]] だけであり、他のメソッドは全て中身を空にしておくことが出来ます。\n逆にあなたのアプリケーションがセッションのみの認証を使用する場合は、\n[[yii\\web\\IdentityInterface::findIdentityByAccessToken()|findIdentityByAccessToken()]] 以外の全てのメソッドを実装する必要があります。\n\n次の例では、[[yii\\web\\User::identityClass|ユーザ識別情報クラス]] は、`user` データベース・テーブルと関連付けられた\n[アクティブ・レコード](db-active-record.md) クラスとして実装されています。\n\n```php\n<?php\n\nuse yii\\db\\ActiveRecord;\nuse yii\\web\\IdentityInterface;\n\nclass User extends ActiveRecord implements IdentityInterface\n{\n    public static function tableName()\n    {\n        return 'user';\n    }\n\n    /**\n     * 与えられた ID によってユーザ識別情報を探す\n     *\n     * @param string|int $id 探すための ID\n     * @return IdentityInterface|null 与えられた ID に合致する Identity オブジェクト\n     */\n    public static function findIdentity($id)\n    {\n        return static::findOne($id);\n    }\n\n    /**\n     * 与えられたトークンによってユーザ識別情報を探す\n     *\n     * @param string $token 探すためのトークン\n     * @return IdentityInterface|null 与えられたトークンに合致する Identity オブジェクト\n     */\n    public static function findIdentityByAccessToken($token, $type = null)\n    {\n        return static::findOne(['access_token' => $token]);\n    }\n\n    /**\n     * @return int|string 現在のユーザの ID\n     */\n    public function getId()\n    {\n        return $this->id;\n    }\n\n    /**\n     * @return string|null 現在のユーザの認証キー\n     */\n    public function getAuthKey()\n    {\n        return $this->auth_key;\n    }\n\n    /**\n     * @param string $authKey\n     * @return bool|null 認証キーが現在のユーザに対して有効か否か\n     */\n    public function validateAuthKey($authKey)\n    {\n        return $this->getAuthKey() === $authKey;\n    }\n}\n```\n\n次のコードを使って、各ユーザに対して認証キーを生成して、\n`user` テーブルに保存しておくことが出来ます。\n\n```php\nclass User extends ActiveRecord implements IdentityInterface\n{\n    ......\n\n    public function beforeSave($insert)\n    {\n        if (parent::beforeSave($insert)) {\n            if ($this->isNewRecord) {\n                $this->auth_key = \\Yii::$app->security->generateRandomString();\n            }\n            return true;\n        }\n        return false;\n    }\n}\n```\n\n> Note: ユーザ識別情報クラスである `User` と [[yii\\web\\User]] を混同してはいけません。\n  前者は認証のロジックを実装するクラスであり、普通は、ユーザの認証情報を保存する何らかの持続的ストレージと関連付けられた\n  [アクティブ・レコード](db-active-record.md) クラスとして実装されます。\n  後者はユーザの認証状態の管理に責任を持つアプリケーション・コンポーネントです。\n\n\n## [[yii\\web\\User]] を使う <span id=\"using-user\"></span>\n\n[[yii\\web\\User]] は、主として、`user` アプリケーション・コンポーネントの形で使います。\n\n現在のユーザの識別情報は、`Yii::$app->user->identity` という式を使って取得することが出来ます。\nこれは、現在ログインしているユーザの [[yii\\web\\User::identityClass|ユーザ識別情報クラス]] \nのインスタンスを返すか、現在のユーザが認証されていない (つまりゲストである) 場合は null を返します。\n次のコードは、[[yii\\web\\User]] からその他の認証関連の情報を取得する方法を示すものです。\n\n```php\n// 現在のユーザの識別情報。ユーザが認証されていない場合は null\n$identity = Yii::$app->user->identity;\n\n// 現在のユーザの ID。ユーザが認証されていない場合は null\n$id = Yii::$app->user->id;\n\n// 現在のユーザがゲストである (認証されていない) かどうか\n$isGuest = Yii::$app->user->isGuest;\n```\n\nユーザをログインさせるためには、次のコードを使うことが出来ます。\n\n```php\n// 指定された username を持つユーザ識別情報を探す\n// 必要ならパスワードをチェックしてもよいことに注意\n$identity = User::findOne(['username' => $username]);\n\n// ユーザをログインさせる\nYii::$app->user->login($identity);\n```\n\n[[yii\\web\\User::login()]] メソッドは現在のユーザの識別情報を [[yii\\web\\User]] にセットします。\nセッションが [[yii\\web\\User::enableSession|有効]] にされている場合は、\nユーザの認証状態がセッション全体を通じて保持されるように、ユーザ識別情報がセッションに保管されます。\nクッキー・ベースのログイン (つまり \"remember me\"、「次回は自動ログイン」) が [[yii\\web\\User::enableAutoLogin|有効]] にされている場合は、\nユーザ識別情報をクッキーにも保存して、クッキーが有効である限りは、ユーザの認証状態をクッキーから復元することが可能になります。\n\nクッキー・ベースのログインを有効にするためには、アプリケーションの構成情報で\n[[yii\\web\\User::enableAutoLogin]] を `true` に構成する必要があります。\nまた、[[yii\\web\\User::login()]] メソッドを呼ぶときには、有効期間のパラメータを与える必要があります。\n\nユーザをログアウトさせるためには、単に次のように `logout()` を呼びます。\n\n```php\nYii::$app->user->logout();\n```\n\nユーザのログアウトはセッションが有効にされている場合にだけ意味があることに注意してください。\n`logout()` メソッドは、ユーザ認証状態をメモリとセッションの両方から消去します。そして、デフォルトでは、ユーザのセッションデータの *全て* を破壊します。\nセッション・データを保持したい場合は、代りに、`Yii::$app->user->logout(false)` を呼ばなければなりません。\n\n\n## 認証のイベント <span id=\"auth-events\"></span>\n\n[[yii\\web\\User]] クラスは、ログインとログアウトのプロセスで、いくつかのイベントを発生させます。\n\n* [[yii\\web\\User::EVENT_BEFORE_LOGIN|EVENT_BEFORE_LOGIN]]: [[yii\\web\\User::login()]] の開始時に発生します。\n  イベント・ハンドラがイベントの [[yii\\web\\UserEvent::isValid|isValid]] プロパティを `false` にセットした場合は、\n  ログインのプロセスがキャンセルされます。\n* [[yii\\web\\User::EVENT_AFTER_LOGIN|EVENT_AFTER_LOGIN]]: ログインが成功した時に発生します。\n* [[yii\\web\\User::EVENT_BEFORE_LOGOUT|EVENT_BEFORE_LOGOUT]]: [[yii\\web\\User::logout()]] の開始時に発生します。\n  イベント・ハンドラがイベントの [[yii\\web\\UserEvent::isValid|isValid]] プロパティを `false` にセットした場合は、\n  ログアウトのプロセスがキャンセルされます。\n* [[yii\\web\\User::EVENT_AFTER_LOGOUT|EVENT_AFTER_LOGOUT]]: ログアウトが成功した時に発生します。\n\nこれらのイベントに反応して、ログイン監査、オンライン・ユーザ統計などの機能を実装することが出来ます。\n例えば、[[yii\\web\\User::EVENT_AFTER_LOGIN|EVENT_AFTER_LOGIN]] のハンドラの中で、\n`user` テーブルにログインの日時と IP アドレスを記録することが出来ます。\n"
  },
  {
    "path": "docs/guide-ja/security-authorization.md",
    "content": "権限付与\n========\n\n権限付与は、ユーザが何かをするのに十分な許可を有しているか否かを確認するプロセスです。\nYii は二つの権限付与の方法を提供しています。すなわち、アクセス制御フィルタ (ACF) と、ロール・ベース・アクセス制御 (RBAC) です。\n\n\n## アクセス制御フィルタ (ACF) <span id=\"access-control-filter\"></span>\n\nアクセス制御フィルタ (ACF) は、[[yii\\filters\\AccessControl]] として実装される単純な権限付与の方法であり、\n何らかの単純なアクセス制御だけを必要とするアプリケーションで使うのに最も適したものです。\nその名前が示すように、ACF は、コントローラまたはモジュールで使用することが出来るアクション [フィルタ](structure-filters.md) です。\nACF は、ユーザがアクションの実行をリクエストしたときに、一連の [[yii\\filters\\AccessControl::rules|アクセス規則]] をチェックして、\n現在のユーザがそのアクションにアクセスする許可を持つかどうかを決定します。\n\n下記のコードは、`site` コントローラで ACF を使う方法を示すものです。\n\n```php\nuse yii\\web\\Controller;\nuse yii\\filters\\AccessControl;\n\nclass SiteController extends Controller\n{\n    public function behaviors()\n    {\n        return [\n            'access' => [\n                'class' => AccessControl::class,\n                'only' => ['login', 'logout', 'signup'],\n                'rules' => [\n                    [\n                        'allow' => true,\n                        'actions' => ['login', 'signup'],\n                        'roles' => ['?'],\n                    ],\n                    [\n                        'allow' => true,\n                        'actions' => ['logout'],\n                        'roles' => ['@'],\n                    ],\n                ],\n            ],\n        ];\n    }\n    // ...\n}\n```\n\n上記のコードにおいて、ACF は `site` コントローラにビヘイビアとしてアタッチされています。これがアクション・フィルタを使用する典型的な方法です。\n`only` オプションは、ACF が `login`、`logout`、`signup` のアクションにのみ適用されるべきであることを指定しています。\n`site` コントローラの他の全てのアクションには ACF の影響は及びません。\n`rules` オプションは [[yii\\filters\\AccessRule|アクセス規則]] を指定するものであり、以下のように読むことが出来ます。\n\n- 全てのゲスト・ユーザ (まだ認証されていないユーザ) に、`login` と `singup` のアクションにアクセスすることを許可します。\n  `roles` オプションに疑問符 `?` が含まれていますが、これは「ゲスト」を表す特殊なトークンです。\n- 認証されたユーザに、`logout` アクションにアクセスすることを許可します。\n  `@` という文字はもう一つの特殊なトークンで、「認証されたユーザ」を表すものです。\n\nACF による権限付与のプロセスにおいては、現在の実行コンテキストに合致する規則が見つかるまで、\nアクセス規則が上から下へと一つずつ調べられます。\nそして、合致したアクセス規則の `allow` の値が、ユーザが権限を有するか否かを決定するのに使われます。\n合致する規則が一つもなかった場合は、ユーザが権限をもたないことを意味し、ACF はアクションの継続を中止します。\n\nユーザが現在のアクションにアクセスする権限を持っていないと判定した場合は、デフォルトでは、ACF は以下の手段を取ります。\n\n* ユーザがゲストである場合は、[[yii\\web\\User::loginRequired()]] を呼び出して、ユーザのブラウザをログイン・ページにリダイレクトします。\n* ユーザが既に認証されている場合は、[[yii\\web\\ForbiddenHttpException]] を投げます。\n\nこの動作は、次のように、[[yii\\filters\\AccessControl::denyCallback]] プロパティを構成することによって、カスタマイズすることが出来ます。\n\n```php\n[\n    'class' => AccessControl::class,\n    ...\n    'denyCallback' => function ($rule, $action) {\n        throw new \\Exception('このページにアクセスする権限がありません。');\n    }\n]\n```\n\n[[yii\\filters\\AccessRule|アクセス規則]] は多くのオプションをサポートしています。以下はサポートされているオプションの要約です。\n[[yii\\filters\\AccessRule]] を拡張して、あなた自身のカスタマイズしたアクセス規則のクラスを作ることも出来ます。\n\n * [[yii\\filters\\AccessRule::allow|allow]]: これが「許可」の規則であるか、「禁止」の規則であるかを指定します。\n\n * [[yii\\filters\\AccessRule::actions|actions]]: どのアクションにこの規則が適用されるかを指定します。\n   これはアクション ID の配列でなければなりません。比較は大文字と小文字を区別します。\n   このオプションが空であるか指定されていない場合は、規則が全てのアクションに適用されることを意味します。\n\n * [[yii\\filters\\AccessRule::controllers|controllers]]: どのコントローラにこの規則が適用されるかを指定します。これはコントローラ ID の配列でなければなりません。\n   コントローラがモジュールに属する場合は、モジュール ID をコントローラ ID の前に付けます。比較は大文字と小文字を区別します。\n   このオプションが空であるか指定されていない場合は、規則が全てのコントローラに適用されることを意味します。\n\n * [[yii\\filters\\AccessRule::roles|roles]]: どのユーザ・ロールにこの規則が適用されるかを指定します。\n   二つの特別なロールが認識されます。これらは、[[yii\\web\\User::isGuest]] によって判断されます。\n\n    - `?`: ゲスト・ユーザ (まだ認証されていないユーザ) を意味します。\n    - `@`: 認証されたユーザを意味します。\n\n   その他のロール名を使うと、[[yii\\web\\User::can()]] の呼び出しが惹起されますが、そのためには、RBAC (次のセクションで説明します) を有効にする必要があります。\n   このオプションが空であるか指定されていない場合は、規則が全てのロールに適用されることを意味します。\n\n * [[yii\\filters\\AccessRule::roleParams|roleParams]]: [[yii\\web\\User::can()]] に渡されるパラメータを指定します。\n   パラメータがどのように使われるかは、RBAC 規則を説明する後のセクションを参照して下さい。このオプションが空であるか設定されていない場合は、パラメータは渡されません。\n\n * [[yii\\filters\\AccessRule::ips|ips]]: どの [[yii\\web\\Request::userIP|クライアントの IP アドレス]] にこの規則が適用されるかを指定します。\n   IP アドレスは、最後にワイルドカード `*` を含むことが出来て、同じプレフィクスを持つ IP アドレスに合致させることが出来ます。\n   例えば、'192.168.*' は、'192.168.' のセグメントに属する全ての IP アドレスに合致します。\n   このオプションが空であるか指定されていない場合は、規則が全ての IP アドレスに適用されることを意味します。\n\n * [[yii\\filters\\AccessRule::verbs|verbs]]: どのリクエスト・メソッド (HTTP 動詞、例えば `GET` や `POST`) にこの規則が適用されるかを指定します。\n   比較は大文字と小文字を区別しません。\n\n * [[yii\\filters\\AccessRule::matchCallback|matchCallback]]: この規則が適用されるべきか否かを決定するために呼び出されるべき\n   PHP コーラブルを指定します。\n\n * [[yii\\filters\\AccessRule::denyCallback|denyCallback]]: この規則がアクセスを禁止する場合に呼び出されるべき\n   PHP コーラブルを指定します。\n\n下記は、`matchCallback` オプションを利用する方法を示す例です。\nこのオプションによって、任意のアクセス制御ロジックを書くことが可能になります。\n\n```php\nuse yii\\filters\\AccessControl;\n\nclass SiteController extends Controller\n{\n    public function behaviors()\n    {\n        return [\n            'access' => [\n                'class' => AccessControl::class,\n                'only' => ['special-callback'],\n                'rules' => [\n                    [\n                        'actions' => ['special-callback'],\n                        'allow' => true,\n                        'matchCallback' => function ($rule, $action) {\n                            return date('d-m') === '31-10';\n                        }\n                    ],\n                ],\n            ],\n        ];\n    }\n\n    // matchCallback が呼ばれる。このページは毎年10月31日だけアクセス出来ます。\n    public function actionSpecialCallback()\n    {\n        return $this->render('happy-halloween');\n    }\n}\n```\n\n\n## ロール・ベース・アクセス制御 (RBAC) <span id=\"rbac\"></span>\n\nロール・ベース・アクセス制御 (RBAC) は、単純でありながら強力な集中型のアクセス制御を提供します。\nRBAC と他のもっと伝統的なアクセス制御スキーマとの比較に関する詳細については、\n[Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%AD%E3%83%BC%E3%83%AB%E3%83%99%E3%83%BC%E3%82%B9%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E5%88%B6%E5%BE%A1) を参照してください。\n\nYii は、[NIST RBAC モデル](https://csrc.nist.gov/CSRC/media/Publications/conference-paper/1992/10/13/role-based-access-controls/documents/ferraiolo-kuhn-92.pdf) に従って、一般的階層型 RBAC を実装しています。\nRBAC の機能は、[[yii\\rbac\\ManagerInterface|authManager]] [アプリケーション・コンポーネント](structure-application-components.md) を通じて提供されます。\n\nRBAC を使用することには、二つの作業が含まれます。\n最初の作業は、RBAC 権限付与データを作り上げることであり、第二の作業は、権限付与データを使って必要とされる場所でアクセス・チェックを実行することです。\n\n説明を容易にするために、まず、いくつかの基本的な RBAC の概念を導入します。\n\n\n### 基本的な概念 <span id=\"basic-concepts\"></span>\n\nロール (役割) は、*許可* (例えば、記事を作成する、記事を更新するなど) のコレクションです。\n一つのロールを一人または複数のユーザに割り当てることが出来ます。\nユーザが特定の許可を有しているか否かをチェックするためには、その許可を含むロールがユーザに割り当てられているか否かをチェックすればよいのです。\n\n各ロールまたは許可に関連付けられた *規則* が存在し得ます。\n規則とは、アクセス・チェックの際に、対応するロールや許可が現在のユーザに適用されるか否かを決定するために実行されるコード断片のことです。\n例えば、「記事更新」の許可は、現在のユーザが記事の作成者であるかどうかをチェックする規則を持つことが出来ます。\nそして、アクセス・チェックのときに、ユーザが記事の作成者でない場合は、彼/彼女は「記事更新」の許可を持っていないと見なすことが出来ます。\n\nロールおよび許可は、ともに、階層的に構成することが出来ます。具体的に言えば、一つのロールは他のロールと許可を含むことが出来、\n許可は他の許可を含むことが出来ます。Yii は、一般的な *半順序* 階層を実装していますが、これはその特殊形として *木* 階層を含むものです。\nロールは許可を含むことが出来ますが、許可はロールを含むことが出来ません。\n\n\n### RBAC を構成する <span id=\"configuring-rbac\"></span>\n\n権限付与データを定義してアクセス・チェックを実行する前に、\n[[yii\\base\\Application::authManager|authManager]] アプリケーション・コンポーネントを構成する必要があります。\nYii は二種類の権限付与マネージャを提供しています。すなわち、[[yii\\rbac\\PhpManager]] と [[yii\\rbac\\DbManager]] です。\n前者は権限付与データを保存するのに PHP スクリプト・ファイルを使いますが、後者は権限付与データをデータベースに保存します。\nあなたのアプリケーションが非常に動的なロールと許可の管理を必要とするのでなければ、前者を使うことを考慮するのが良いでしょう。\n\n\n#### `PhpManager` を使用する <span id=\"using-php-manager\"></span>\n\n次のコードは、アプリケーションの構成情報で [[yii\\rbac\\PhpManager]] クラスを使って `authManager` を構成する方法を示すものです。\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'authManager' => [\n            'class' => 'yii\\rbac\\PhpManager',\n        ],\n        // ...\n    ],\n];\n```\n\nこれで `authManager` は `\\Yii::$app->authManager` によってアクセスすることが出来るようになります。\n\nデフォルトでは、[[yii\\rbac\\PhpManager]] は RBAC データを `@app/rbac/` ディレクトリの下のファイルに保存します。\n権限の階層をオンラインで変更する必要がある場合は、必ず、ウェブ・サーバのプロセスがこのディレクトリとその中の全てのファイルに対する書き込み権限を有するようにしてください。\n\n\n#### `DbManager` を使用する <span id=\"using-db-manager\"></span>\n\n次のコードは、アプリケーションの構成情報で [[yii\\rbac\\DbManager]] クラスを使って `authManager` を構成する方法を示すものです。\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'authManager' => [\n            'class' => 'yii\\rbac\\DbManager',\n            // RBAC アイテムの階層をキャッシュしたい場合はコメントを外す\n            // 'cache' => 'cache',\n        ],\n        // ...\n    ],\n];\n```\n> Note: yii2-basic-app テンプレートを使おうとする場合は、`config/web.php` に加えて、\n  `config/console.php` 構成ファイルにおいても `authManager` を宣言する必要があります。\n> yii2-advanced-app の場合は、`authManager` は `common/config/main.php` で一度だけ宣言されなければなりません。\n\n`DbManager` は四つのデータベース・テーブルを使ってデータを保存します。\n\n- [[yii\\rbac\\DbManager::$itemTable|itemTable]]: 権限アイテムを保存するためのテーブル。デフォルトは \"auth_item\"。\n- [[yii\\rbac\\DbManager::$itemChildTable|itemChildTable]]: 権限アイテムの階層を保存するためのテーブル。デフォルトは \"auth_item_child\"。\n- [[yii\\rbac\\DbManager::$assignmentTable|assignmentTable]]: 権限アイテムの割り当てを保存するためのテーブル。デフォルトは \"auth_assignment\"。\n- [[yii\\rbac\\DbManager::$ruleTable|ruleTable]]: 規則を保存するためのテーブル。デフォルトは \"auth_rule\"。\n\n先に進む前にこれらのテーブルをデータベースに作成する必要があります。そのためには、`@yii/rbac/migrations` に保存されているマイグレーションを使うことが出来ます。\n\n`yii migrate --migrationPath=@yii/rbac/migrations`\n\n異なる名前空間のマイグレーションを扱う方法の詳細については\n[分離されたマイグレーション](db-migrations.md#separated-migrations) のセクションを参照して下さい。\n\nこれで `authManager` は `\\Yii::$app->authManager` によってアクセスすることが出来るようになります。\n\n\n### 権限付与データを構築する <span id=\"generating-rbac-data\"></span>\n\n権限付与データを構築する作業は、つまるところ、以下のタスクに他なりません。\n\n- ロールと許可を定義する\n- ロールと許可の関係を定義する\n- 規則を定義する\n- 規則をロールと許可に結び付ける\n- ロールをユーザに割り当てる\n\n権限付与に要求される柔軟性の程度によって、上記のタスクのやりかたも異なってきます。\n許可の階層構造が開発者によってのみ変更されることを意図する場合は、\nマイグレーションまたはコンソールコマンドを使うことが出来ます。\nマイグレーションを使う場合の利点は、他のマイグレーションと一緒に実行できることです。\nコンソール・コマンドを使う場合の利点は、階層構造の全体が、複数のマイグレーションに分散することなく、コード中に見やすい形で保たれることです。\n\nどちらの方法でも、結局は次のような RBAC 階層を得ることになります。\n\n![単純な RBAC 階層](images/rbac-hierarchy-1.png \"単純な RBAC 階層\")\n\n許可の階層構造が動的に形成される必要がある場合は、UI またはコンソール・コマンドが必要になります。\n階層構造そのものを構築するために使用される API には違いはありません。\n\n#### マイグレーションを使う\n\n[マイグレーション](db-migrations.md) を使って、\n`authManager` が提供する API によって階層を初期化したり変更したりすることが出来ます。\n\n`./yii migrate/create init_rbac` を使って新しいマイグレーションを作成し、階層の作成を実装します。\n\n```php\n<?php\nuse yii\\db\\Migration;\n\nclass m170124_084304_init_rbac extends Migration\n{\n    public function up()\n    {\n        $auth = Yii::$app->authManager;\n\n        // \"createPost\" という許可を追加する\n        $createPost = $auth->createPermission('createPost');\n        $createPost->description = '記事を投稿';\n        $auth->add($createPost);\n\n        // \"updatePost\" という許可を追加する\n        $updatePost = $auth->createPermission('updatePost');\n        $updatePost->description = '記事を更新';\n        $auth->add($updatePost);\n\n        // \"author\" ロールを追加し、このロールに \"createPost\" の許可を付与する\n        $author = $auth->createRole('author');\n        $auth->add($author);\n        $auth->addChild($author, $createPost);\n\n        // \"admin\" ロールを追加し、このロールに \"updatePost\" の許可を付与する\n        // 同時に、\"author\" ロールが持つ許可も付与する\n        $admin = $auth->createRole('admin');\n        $auth->add($admin);\n        $auth->addChild($admin, $updatePost);\n        $auth->addChild($admin, $author);\n\n        // ロールをユーザに割り当てる。1 と 2 は IdentityInterface::getId() によって返される ID\n        // IdentityInterface::getId() は、通常は User モデルの中で実装される\n        $auth->assign($author, 2);\n        $auth->assign($admin, 1);\n    }\n    \n    public function down()\n    {\n        $auth = Yii::$app->authManager;\n\n        $auth->removeAll();\n    }\n}\n```\n\n> どのユーザにどのロールを割り当てるかをハードコードしたくない場合は、マイグレーションに `->assign()` の呼び出しを書かないで下さい。\n  その代りに、ロールの割り当てを管理する UI またはコンソール・コマンドを作成して下さい。\n\nマイグレーションは `yii migrate` を使って適用することが出来ます。\n\n### コンソール・コマンドを使う\n\n許可の階層が全く変化せず、決った数のユーザしか存在しない場合は、\n`authManager` が提供する API によって権限付与データを一回だけ初期設定する [コンソール・コマンド](tutorial-console.md#create-command)\nを作ることが出来ます。\n\n```php\n<?php\nnamespace app\\commands;\n\nuse Yii;\nuse yii\\console\\Controller;\n\nclass RbacController extends Controller\n{\n    public function actionInit()\n    {\n        $auth = Yii::$app->authManager;\n        $auth->removeAll();\n\n        // \"createPost\" という許可を追加する\n        $createPost = $auth->createPermission('createPost');\n        $createPost->description = '記事を投稿';\n        $auth->add($createPost);\n\n        // \"updatePost\" という許可を追加\n        $updatePost = $auth->createPermission('updatePost');\n        $updatePost->description = '記事を更新';\n        $auth->add($updatePost);\n\n        // \"author\" というロールを追加し、このロールに \"createPost\" の許可を付与する\n        $author = $auth->createRole('author');\n        $auth->add($author);\n        $auth->addChild($author, $createPost);\n\n        // \"admin\" というロールを追加し、このロールに \"updatePost\" 許可を付与する\n        // 同時に、\"author\" ロールが持つ許可も付与する\n        $admin = $auth->createRole('admin');\n        $auth->add($admin);\n        $auth->addChild($admin, $updatePost);\n        $auth->addChild($admin, $author);\n\n        // ロールをユーザに割り当てる。1 と 2 は IdentityInterface::getId() によって返される ID\n        // IdentityInterface::getId() は、通常は User モデルの中で実装される\n        $auth->assign($author, 2);\n        $auth->assign($admin, 1);\n    }\n}\n```\n\n> Note: アドバンスト・テンプレートを使おうとするときは、`RbacController` を `console/controllers`\n  ディレクトリの中に置いて、名前空間を `console\\controllers` に変更する必要があります。\n\n上記のコマンドは、コンソールから次のようにして実行することが出来ます。\n\n```\nyii rbac/init\n```\n\n> どのユーザにどのロールを割り当てるかをハードコードしたくない場合は、  コマンドに `->assign()` の呼び出しを書かないで下さい。\n  その代りに、ロールの割り当てを管理する UI またはコンソール・コマンドを作成して下さい。\n\n## ロールをユーザに割り当てる\n\n投稿者 (author) は記事を投稿することが出来、管理者 (admin) は記事を更新することに加えて投稿者が出来る全てのことが出来ます。\n\nあなたのアプリケーションがユーザ自身によるユーザ登録を許している場合は、新しく登録されたユーザに一度はロールを割り当てる必要があります。\n例えば、アドバンスト・プロジェクト・テンプレートにおいては、登録したユーザの全てを「投稿者」にするために、\n`frontend\\models\\SignupForm::signup()` を次のように修正しなければなりません。\n\n```php\npublic function signup()\n{\n    if ($this->validate()) {\n        $user = new User();\n        $user->username = $this->username;\n        $user->email = $this->email;\n        $user->setPassword($this->password);\n        $user->generateAuthKey();\n        $user->save(false);\n\n        // 次の三行が追加されたものです\n        $auth = Yii::$app->authManager;\n        $authorRole = $auth->getRole('author');\n        $auth->assign($authorRole, $user->getId());\n\n        return $user;\n    }\n\n    return null;\n}\n```\n\n動的に更新される権限付与データを持つ複雑なアクセス制御を必要とするアプリケーションについては、\n`authManager` が提供する API を使って、特別なユーザ・インタフェイス (つまり、管理パネル) を開発する必要があるでしょう。\n\n\n### 規則を使う <span id=\"using-rules\"></span>\n\n既に述べたように、規則がロールと許可に制約を追加します。規則は [[yii\\rbac\\Rule]] を拡張したクラスであり、\n[[yii\\rbac\\Rule::execute()|execute()]] メソッドを実装しなければなりません。前に作った権限階層においては、投稿者は自分自身の記事を編集することが出来ませんでした。\nこれを修正しましょう。最初に、ユーザが記事の投稿者であることを確認する規則が必要です。\n\n```php\nnamespace app\\rbac;\n\nuse yii\\rbac\\Rule;\nuse app\\models\\Post;\n\n/**\n * authorID がパラメータで渡されたユーザと一致するかチェックする\n */\nclass AuthorRule extends Rule\n{\n    public $name = 'isAuthor';\n\n    /**\n     * @param string|int $user ユーザ ID\n     * @param Item $item この規則が関連付けられているロールまたは許可\n     * @param array $params ManagerInterface::checkAccess() に渡されたパラメータ\n     * @return bool 関連付けられたロールまたは許可を認めるか否かを示す値\n     */\n    public function execute($user, $item, $params)\n    {\n        return isset($params['post']) ? $params['post']->createdBy == $user : false;\n    }\n}\n```\n\n上の規則は、`post` が `$user` によって作成されたかどうかをチェックします。\n次に、前に使ったコマンドの中で、`updateOwnPost` という特別な許可を作成します。\n\n```php\n$auth = Yii::$app->authManager;\n\n// 規則を追加する\n$rule = new \\app\\rbac\\AuthorRule;\n$auth->add($rule);\n\n// \"updateOwnPost\" という許可を作成し、それに規則を関連付ける\n$updateOwnPost = $auth->createPermission('updateOwnPost');\n$updateOwnPost->description = '自分の記事を更新';\n$updateOwnPost->ruleName = $rule->name;\n$auth->add($updateOwnPost);\n\n// \"updateOwnPost\" は \"updatePost\" から使われる\n$auth->addChild($updateOwnPost, $updatePost);\n\n// \"author\" に自分の記事を更新することを許可する\n$auth->addChild($author, $updateOwnPost);\n```\n\nこれで、次のような権限階層になります。\n\n![規則を持つ RBAC 階層](images/rbac-hierarchy-2.png \"規則を持つ RBAC 階層\")\n\n\n### アクセス・チェック <span id=\"access-check\"></span>\n\n権限付与データが準備できてしまえば、アクセス・チェックは [[yii\\rbac\\ManagerInterface::checkAccess()]] メソッドを呼ぶだけの簡単な仕事です。\nたいていのアクセス・チェックは現在のユーザに関するものですから、Yii は、便利なように、[[yii\\web\\User::can()]] というショートカット・メソッドを提供しています。\nこれは、次のようにして使うことが出来ます。\n\n```php\nif (\\Yii::$app->user->can('createPost')) {\n    // 記事を作成する\n}\n```\n\n現在のユーザが `ID=1` である Jane であるとすると、`createPost` からスタートして `Jane` まで到達しようと試みます。\n\n![アクセス・チェック](images/rbac-access-check-1.png \"アクセス・チェック\")\n\nユーザが記事を更新することが出来るかどうかをチェックするためには、前に説明した `AuthorRule` によって要求される追加のパラメータを渡す必要があります。\n\n```php\nif (\\Yii::$app->user->can('updatePost', ['post' => $post])) {\n    // 記事を更新する\n}\n```\n\n現在のユーザが John であるとすると、次の経路をたどります。\n\n\n![アクセス・チェック](images/rbac-access-check-2.png \"アクセス・チェック\")\n\n`updatePost` からスタートして、`updateOwnPost` を通過します。通過するためには、`AuthorRule` が `execute` メソッドで `true` を返さなければなりません。\n`execute` メソッドは `can` メソッドの呼び出しから `$params` を受け取りますので、その値は `['post' => $post]` です。\nすべて OK であれば、John に割り当てられている `author` に到達します。\n\nJane の場合は、彼女が管理者であるため、少し簡単になります。\n\n![アクセス・チェック](images/rbac-access-check-3.png \"アクセス・チェック\")\n\nコントローラ内で権限付与を実装するのには、いくつかの方法があります。\n追加と削除に対するアクセス権を分離する細分化された許可が必要な場合は、それぞれのアクションに対してアクセス権をチェックする必要があります。\n各アクション・メソッドの中で上記の条件を使用するか、または [[yii\\filters\\AccessControl]] を使います。\n\n```php\npublic function behaviors()\n{\n    return [\n        'access' => [\n            'class' => AccessControl::class,\n            'rules' => [\n                [\n                    'allow' => true,\n                    'actions' => ['index'],\n                    'roles' => ['managePost'],\n                ],\n                [\n                    'allow' => true,\n                    'actions' => ['view'],\n                    'roles' => ['viewPost'],\n                ],\n                [\n                    'allow' => true,\n                    'actions' => ['create'],\n                    'roles' => ['createPost'],\n                ],\n                [\n                    'allow' => true,\n                    'actions' => ['update'],\n                    'roles' => ['updatePost'],\n                ],\n                [\n                    'allow' => true,\n                    'actions' => ['delete'],\n                    'roles' => ['deletePost'],\n                ],\n            ],\n        ],\n    ];\n}\n```\n\n全ての CRUD 操作がまとめて管理される場合は、`managePost` のような単一の許可を使い、\n[[yii\\web\\Controller::beforeAction()]] の中でそれをチェックするのが良いアイデアです。\n\n上記の例では、アクションにアクセスするために必要と指定されたロールについて、パラメータは渡されていません。\nしかし、`updatePost` 許可の場合は、それが正しく動作するためには `post` パラメータを渡す必要があります。\nアクセス規則の中で [[yii\\filters\\AccessRule::roleParams|roleParams]] を指定することによって、\n[[yii\\web\\User::can()]] にパラメータを渡すことが出来ます。\n\n```php\n[\n    'allow' => true,\n    'actions' => ['update'],\n    'roles' => ['updatePost'],\n    'roleParams' => function() {\n        return ['post' => Post::findOne(['id' => Yii::$app->request->get('id')])];\n    },\n],\n```\n\n上記の例では、[[yii\\filters\\AccessRule::roleParams|roleParams]] はアクセス規則がチェックされるときに評価されるクロージャになっています。\n従って、モデルは必要になったときだけロードされます。\nロール・パラメータの作成が簡単な操作である場合は、次のように、単に配列を指定しても構いません。\n\n```php\n[\n    'allow' => true,\n    'actions' => ['update'],\n    'roles' => ['updatePost'],\n    'roleParams' => ['postId' => Yii::$app->request->get('id')],\n],\n```\n\n### デフォルト・ロールを使う <span id=\"using-default-roles\"></span>\n\nデフォルト・ロールというのは、*全て* のユーザに *黙示的* に割り当てられるロールです。\n[[yii\\rbac\\ManagerInterface::assign()]] を呼び出す必要はなく、権限付与データはその割り当て情報を含みません。\n\nデフォルト・ロールは、通常、そのロールが当該ユーザに適用されるかどうかを決定する規則と関連付けられます。\n\nデフォルト・ロールは、たいていは、何らかのロールの割り当てを既に持っているアプリケーションにおいて使われます。\n例えば、アプリケーションによっては、ユーザのテーブルに \"group\" というカラムを持って、個々のユーザが属する特権グループを表している場合があります。\nそれぞれの特権グループを RBAC ロールに対応付けることが出来るのであれば、デフォルト・ロールの機能を使って、それぞれのユーザに RBAC ロールを自動的に割り当てることが出来ます。\nどのようにすればこれが出来るのか、例を使って説明しましょう。\n\nユーザのテーブルに `group` というカラムがあって、1 は管理者グループ、2 は投稿者グループを示していると仮定しましょう。\nこれら二つのグループの権限を表すために、それぞれ、`admin` と `author` という RBAC ロールを作ることにします。\nこのとき、次のように RBAC データをセットアップすることが出来ます。\n\n\n```php\nnamespace app\\rbac;\n\nuse Yii;\nuse yii\\rbac\\Rule;\n\n/**\n * ユーザのグループが合致するかどうかをチェックする\n */\nclass UserGroupRule extends Rule\n{\n    public $name = 'userGroup';\n\n    public function execute($user, $item, $params)\n    {\n        if (!Yii::$app->user->isGuest) {\n            $group = Yii::$app->user->identity->group;\n            if ($item->name === 'admin') {\n                return $group == 1;\n            } elseif ($item->name === 'author') {\n                return $group == 1 || $group == 2;\n            }\n        }\n        return false;\n    }\n}\n```\n\n次に、[前のセクション](#generating-rbac-data) で説明したように、あなた独自のコマンド/マイグレーションを作成します。\n\n```php\n$auth = Yii::$app->authManager;\n\n$rule = new \\app\\rbac\\UserGroupRule;\n$auth->add($rule);\n\n$author = $auth->createRole('author');\n$author->ruleName = $rule->name;\n$auth->add($author);\n// ... $author の子として許可を追加 ...\n\n$admin = $auth->createRole('admin');\n$admin->ruleName = $rule->name;\n$auth->add($admin);\n$auth->addChild($admin, $author);\n// ... $admin の子として許可を追加 ...\n```\n\n上記において、\"author\" が \"admin\" の子として追加されているため、規則クラスの `execute()` メソッドを実装する時には、\nこの階層関係にも配慮しなければならないことに注意してください。\nこのために、ロール名が \"author\" である場合には、`execute()` メソッドは、ユーザのグループが 1 または 2 である\n(ユーザが \"admin\" グループまたは \"author\" グループに属している) ときに true を返しています。\n\n次に、`authManager` の構成情報で、この二つのロールを [[yii\\rbac\\BaseManager::$defaultRoles]] としてリストします。\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'authManager' => [\n            'class' => 'yii\\rbac\\PhpManager',\n            'defaultRoles' => ['admin', 'author'],\n        ],\n        // ...\n    ],\n];\n```\n\nこのようにすると、アクセス・チェックを実行すると、`admin` と `author` の両方のロールは、それらと関連付けられた規則を評価することによってチェックされるようになります。\n規則が true を返せば、そのロールが現在のユーザに適用されることになります。\n上述の規則の実装に基づいて言えば、ユーザの `group` の値が 1 であれば `admin` ロールがユーザに適用され、\n`group` の値が 2 であれば `author` ロールが適用されるということを意味します。\n"
  },
  {
    "path": "docs/guide-ja/security-best-practices.md",
    "content": "セキュリティのベスト・プラクティス\n==================================\n\n下記において、一般的なセキュリティの指針を復習し、Yii を使ってアプリケーションを開発するときに脅威を回避する方法を説明します。\nこれらの原則のほとんどのものは Yii に固有のものではなく、ウェブ・サイトまたはソフトウェアの開発一般に適用されるものです。\n従って、これらの原則の背後にある一般的な考え方について、さらに参照すべき文書へのリンクが追加されています。\n\n\n基本的な指針\n------------\n\nどのようなアプリケーションが開発されているかに関わらず、セキュリティに関しては二つの大きな指針が存在します。\n\n1. 入力をフィルタする。\n2. 出力をエスケープする。\n\n\n### 入力をフィルタする\n\n入力をフィルタするとは、入力値は決して安全なものであると見なさず、取得した値が実際に許容されるものであるかどうかを、\n常にチェックしなければならない、ということを意味します。例えば、並べ替えが三つのフィールド `title`、`created_at` および `status` によって実行され、\nフィールドの名前がユーザの入力によって提供されるものであることが判っている場合、取得した値を受信するその場でチェックする方が良い、ということです。\n基本的な PHP の形式では、次のようなコードになります。\n\n```php\n$sortBy = $_GET['sort'];\nif (!in_array($sortBy, ['title', 'created_at', 'status'])) {\n\tthrow new Exception('sort の値が不正です。');\n}\n```\n\nYii においては、たいていの場合、同様のチェックを行うために [フォームの検証](input-validation.md) を使うことになるでしょう。\n\nこのトピックについて更に読むべき文書:\n\n- <https://owasp.org/www-community/vulnerabilities/Improper_Data_Validation>\n- <https://www.owasp.org/index.php/Input_Validation_Cheat_Sheet>\n\n\n### 出力をエスケープする\n\nデータを使用するコンテキストに応じて、出力をエスケープしなければなりません。\nつまり、HTML のコンテキストでは、`<` や `>` などの特殊な文字をエスケープしなければなりません。\nJavaScript や SQL のコンテキストでは、対象となる文字は別のセットになります。\n全てを手動でエスケープするのは間違いを生じやすいことですから、Yii は異なるコンテキストに応じたエスケープを実行するためのさまざまなツールを提供しています。\n\nこのトピックについて更に読むべき文書:\n\n- <https://owasp.org/www-community/attacks/Command_Injection>\n- <https://owasp.org/www-community/attacks/Code_Injection>\n- <https://owasp.org/www-community/attacks/xss/>\n\n\nSQL インジェクションを回避する\n------------------------------\n\nSQL インジェクションは、次のように、エスケープされていない文字列を連結してクエリ・テキストを構築する場合に発生します。\n\n```php\n$username = $_GET['username'];\n$sql = \"SELECT * FROM user WHERE username = '$username'\";\n```\n\n正しいユーザ名を提供する代りに、攻撃者は `'; DROP TABLE user; --` のような文字列をあなたのアプリケーションに与えることが出来ます。\n結果として構築される SQL は次のようになります。\n\n```sql\nSELECT * FROM user WHERE username = ''; DROP TABLE user; --'\n```\n\nこれは有効なクエリで、空のユーザ名を持つユーザを探してから、`user` テーブルを削除します。\nおそらく、ウェブ・サイトは破壊されて、データは失われることになります (定期的なバックアップは設定済みですよね、ね? )。\n\nYii においては、ほとんどのデータベース・クエリは、PDO のプリペアド・ステートメントを適切に使用する [アクティブ・レコード](db-active-record.md) を経由して実行されます。\nプリペアド・ステートメントの場合は、上で説明したようなクエリの改竄は不可能です。\n\nそれでも、[生のクエリ](db-dao.md) や [クエリ・ビルダ](db-query-builder.md) を必要とする場合はあります。\nその場合には、データを渡すための安全な方法を使わなければなりません。データをカラムの値として使う場合は、プリペアド・ステートメントを使うことが望まれます。\n\n```php\n// クエリ・ビルダ\n$userIDs = (new Query())\n    ->select('id')\n    ->from('user')\n    ->where('status=:status', [':status' => $status])\n    ->all();\n\n// DAO\n$userIDs = $connection\n    ->createCommand('SELECT id FROM user where status=:status')\n    ->bindValues([':status' => $status])\n    ->queryColumn();\n```\n\nデータがカラム名やテーブル名を指定するために使われる場合は、事前定義された一連の値だけを許可するのが最善の方法です。\n \n```php\nfunction actionList($orderBy = null)\n{\n    if (!in_array($orderBy, ['name', 'status'])) {\n        throw new BadRequestHttpException('name と status だけを並べ替えに使うことが出来ます。')\n    }\n    \n    // ...\n}\n```\n\nそれが不可能な場合は、テーブル名とカラム名をエスケープしなければなりません。\nYii はそういうエスケープのための特別な文法を持っており、それを使うと、サポートされている全てのデータベースに対して同じ方法でエスケープすることが出来ます。\n\n```php\n$sql = \"SELECT COUNT([[$column]]) FROM {{table}}\";\n$rowCount = $connection->createCommand($sql)->queryScalar();\n```\n\nこの文法の詳細は、[テーブルとカラムの名前を引用符で囲む](db-dao.md#quoting-table-and-column-names) で読むことが出来ます。\n\nこのトピックについて更に読むべき文書:\n\n- <https://owasp.org/www-community/attacks/SQL_Injection>\n\n\nXSS を回避する\n--------------\n\nXSS すなわちクロス・サイト・スクリプティングは、ブラウザに HTML を出力する際に、出力が適切にエスケープされていないと発生します。\n例えば、ユーザ名を入力できるフォームで `Alexander` の代りに `<script>alert('Hello!');</script>` と入力した場合、\nユーザ名をエスケープせずに出力している全てのページでは、JavaScript `alert('Hello!');` が実行されて、ブラウザにアラート・ボックスがポップアップ表示されます。\nウェブ・サイト次第では、そのようなスクリプトを使って、無害なアラートではなく、あなたの名前を使ってメッセージを送信したり、\nさらには銀行取引を実行したりすることが可能です。\n\nXSS の回避は、Yii においてはとても簡単です。一般に、二つのケースがあります。\n\n1. データをプレーン・テキストとして出力したい。\n2. データを HTML として出力したい。\n\nプレーン・テキストしか必要でない場合は、エスケープは次のようにとても簡単です。\n\n\n```php\n<?= \\yii\\helpers\\Html::encode($username) ?>\n```\n\nHTML である場合は、HtmlPurifier から助けを得ることが出来ます。\n\n```php\n<?= \\yii\\helpers\\HtmlPurifier::process($description) ?>\n```\n\nHtmlPurifier の処理は非常に重いので、キャッシュを追加することを検討してください。\n\nこのトピックについて更に読むべき文書:\n\n- <https://owasp.org/www-community/attacks/xss/>\n\n\nCSRF を回避する\n---------------\n\nCSRF は、クロス・サイト・リクエスト・フォージェリ (cross-site request forgery) の略称です。\n多くのアプリケーションは、ユーザのブラウザから来るリクエストはユーザ自身によって発せられたものだと仮定しているけれども、その仮定は間違っているかもしれない ... というのが CSRF の考え方です。\n\n例えば、`an.example.com` というウェブ・サイトが `/logout` という URL を持っており、\nこの URL を単純な GET でアクセスするとユーザをログアウトさせるようになっているとします。\nユーザ自身によってこの URL がリクエストされる限りは何も問題はありませんが、ある日、悪い奴が、ユーザが頻繁に訪れるフォーラムに `<img src=\"https://an.example.com/logout\">` というリンクを含むコンテントを何らかの方法で投稿します。\nブラウザは画像のリクエストとページのリクエストの間に何ら区別を付けませんので、ユーザがそのような `img` タグを含むページを開くとブラウザはその URL に対して GET リクエストを送信します。\nそして、ユーザが `an.example.com` からログアウトされてしまうことになる訳です。\n\nこれは CSRF 攻撃がどのように動作するかという基本的な考え方です。ユーザがログアウトされるぐらいは大したことではない、と言うことも出来るでしょう。\nしかしこれは例に過ぎません。この考え方を使って、支払いを開始させたり、データを変更したりというような、もっとひどいことをすることも出来ます。\n`https://an.example.com/purse/transfer?to=anotherUser&amount=2000` という URL を持つウェブ・サイトがあると考えて見てください。この URL に GET リクエストを使ってアクセスすると、権限を持つユーザ・アカウントから `anotherUser` に $2000 が送金されるのです。\n私たちは、ブラウザは画像をロードするのに常に GET リクエストを使う、ということを知っていますから、\nこの URL が POST リクエストだけを受け入れるようにコードを修正することは出来ます。\nしかし残念なことに、それで問題が解決する訳ではありません。攻撃者は `<img>` タグの代りに何らかの JavaScript コードを書いて、\nその URL に対する POST リクエストの送信を可能にすることが出来ます。\n\nこれを理由として、Yii は CSRF 攻撃を防御するための追加のメカニズムを適用します。\n\nCSRF を回避するためには、常に次のことを守らなければなりません。\n\n1. HTTP の規格、すなわち、GET はアプリケーションの状態を変更すべきではない、という規則に従うこと。\n   詳細は [RFC2616](https://www.rfc-editor.org/rfc/rfc9110.html#name-method-definitions) を参照して下さい。\n2. Yii の CSRF 保護を有効にしておくこと。\n\n場合によっては、コントローラやアクションの単位で CSRF 検証を無効化する必要があることがあるでしょう。これは、そのプロパティを設定することによって達成することが出来ます。\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public $enableCsrfValidation = false;\n\n    public function actionIndex()\n    {\n        // CSRF 検証はこのアクションおよびその他のアクションに対して適用されない\n    }\n\n}\n```\n\n特定のアクションに対して CSRF 検証を無効化したいときは、次のようにすることが出来ます。\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function beforeAction($action)\n    {\n        // ... ここで何らかの条件に従って `$this->enableCsrfValidation` を設定する ...\n        // 親のメソッドを呼ぶ。プロパティが true であれば、その中で CSRF がチェックされる。\n        return parent::beforeAction($action);\n    }\n}\n```\n\n[スタンドアロン・アクション](structure-controllers.md#standalone-actions) における CSRF 検証の無効化は `init()` メソッドの中で行わなければなりません。\nこのコードは `beforeRun()` メソッドに置いてはいけません。そこでは効果がありません。\n\n```php\n<?php\n\nnamespace app\\components;\n\nuse yii\\base\\Action;\n\nclass ContactAction extends Action\n{\n    public function init()\n    {\n        parent::init();\n        $this->controller->enableCsrfValidation = false;\n    }\n\n    public function run()\n    {\n          $model = new ContactForm();\n          $request = Yii::$app->request;\n          if ($request->referrer === 'yiipowered.com'\n              && $model->load($request->post())\n              && $model->validate()\n          ) {\n              $model->sendEmail();\n          }\n    }\n}\n```\n\n> Warning: CSRF を無効化すると、あらゆるサイトから POST リクエストをあなたのサイトに送信することが出来るようになります。その場合には、IP アドレスや秘密のトークンをチェックするなど、追加の検証を実装することが重要です。\n\n> Note: バージョン 2.0.21 以降、Yii は `sameSite` クッキー設定 (PHP バージョン 7.4.0 以上が必要) をサポートしています。\n  ただし、`sameSite` クッキー設定を行えば、上記の CSRF 対策が不要になるということではありません。何故なら、今はまだ全てのブラウザがこの設定をサポートしている訳ではないからです。\n 詳細については [セッションとクッキー - sameSite オプション](runtime-sessions-cookies.md#samesite) を参照して下さい。\n\nこのトピックについて更に読むべき文書:\n\n- <https://owasp.org/www-community/attacks/csrf>\n- <https://owasp.org/www-community/SameSite>\n\n\nファイルの曝露を回避する\n------------------------\n\nデフォルトでは、サーバのウェブ・ルートは、`index.php` がある `web` ディレクトリを指すように意図されています。\n共有ホスティング環境の場合、それをすることが出来ずに、全てのコード、構成情報、ログをサーバのウェブ・ルートの下に置かなくてはならないことがあり得ます。\n\nそういう場合には、`web` 以外の全てに対してアクセスを拒否することを忘れないでください。\nそれも出来ない場合は、アプリケーションを別の場所でホストすることを検討してください。\n\n\n本番環境ではデバッグ情報とデバッグ・ツールを無効にする\n------------------------------------------------------\n\nデバッグ・モードでは、Yii は極めて多くのエラー情報を出力します。これは確かに開発には役立つものです。\nしかし、実際の所、これらの饒舌なエラー情報は、攻撃者にとっても、データベース構造、構成情報の値、コードの断片などを曝露してくれる重宝なものです。\n本番でのアプリケーションにおいては、決して `index.php` の `YII_DEBUG` を `true` にして走らせてはいけません。\n\n本番環境では Gii やデバッグ・ツール・バーを決して有効にしてはいけません。\nこれらを有効にすると、データベース構造とコードに関する情報を得ることが出来るだけでなく、コードを Gii によって生成したもので書き換えることすら出来てしまいます。\n\nデバッグ・ツール・バーは本当に必要でない限り本番環境では使用を避けるべきです。これはアプリケーションと構成情報の全ての詳細を曝露することが出来ます。\nどうしても必要な場合は、あなたの IP だけに適切にアクセス制限されていることを再度チェックしてください。\n\nこのトピックについて更に読むべき文書:\n\n- <https://owasp.org/www-project-.net/articles/Exception_Handling.md>\n- <https://owasp.org/www-pdf-archive/OWASP_Top_10_2007.pdf> (A6 - Information Leakage and Improper Error Handling)\n\n\nTLS によるセキュアな接続を使う\n------------------------------\n\nYii が提供する機能には、クッキーや PHP セッションに依存するものがあります。これらのものは、接続が侵害された場合には、脆弱性となり得ます。\nアプリケーションが TLS (しばしば [SSL](https://ja.wikipedia.org/wiki/Transport_Layer_Security) と呼ばれます) によるセキュアな接続を使用している場合は、この危険性を減少させることが出来ます。\n\nその設定の仕方については、あなたのウェブ・サーバのドキュメントの指示を参照してください。\nH5BP プロジェクトが提供する構成例を参考にすることも出来ます。\n\n- [Nginx](https://github.com/h5bp/server-configs-nginx)\n- [Apache](https://github.com/h5bp/server-configs-apache).\n- [IIS](https://github.com/h5bp/server-configs-iis).\n- [Lighttpd](https://github.com/h5bp/server-configs-lighttpd).\n\n> Note: TLS が構成されているときは、(セッションの)クッキーを TLS のみで送信することが推奨されます。\n  これは、セッション および/または クッキーのの `secure` フラグを設定することで達成されます。\n  詳細は [セッションとクッキー - secure フラグ](runtime-sessions-cookies.md#secure) を参照して下さい。\n\n\nサーバの構成をセキュアにする\n----------------------------\n\nこのセクションの目的は、Yii ベースのウェブ・サイトをホストするサーバの構成を作成するときに、\n考慮に入れなければならないリスクに照明を当てることにあります。\nここで触れられる問題点以外にも、セキュリティに関連して考慮すべき構成オプションがあるかもしれません。\nこのセクションの説明が完全であるとは考えないで下さい。\n\n### `Host` ヘッダ攻撃を避ける\n\n[[yii\\web\\UrlManager]] や [[yii\\helpers\\Url]] のクラスは、リンクを生成するために [[yii\\web\\Request::getHostInfo()|現在リクエストされているホスト名]] を使うことがあります。\nウェブ・サーバが `Host` ヘッダの値とは無関係に同じサイトとして応答するように構成されている場合は、\nこの情報は信頼できないものになっており、[HTTP リクエストを送信するユーザによって偽装されている](https://www.acunetix.com/vulnerabilities/web/host-header-attack) 可能性があります。\nそのような状況においては、ウェブ・サーバの構成を改修して、指定されたホスト名に対してのみ応答するようにするか、\nまたは、`request` アプリケーション・コンポーネントの [[yii\\web\\Request::setHostInfo()|hostInfo]] プロパティを設定して、\nホスト名の値を明示的に設定ないしフィルタするか、どちらかの対策を取るべきです。\n\nサーバの構成についての詳細な情報は、ウェブ・サーバのドキュメントを参照して下さい。\n\n- Apache 2: <https://httpd.apache.org/docs/trunk/vhosts/examples.html#defaultallports>\n- Nginx: <https://www.nginx.com/resources/wiki/start/topics/examples/server_blocks/>\n\nサーバの構成にアクセスする権限がない場合は、このような攻撃に対して防御するために、\n[[yii\\filters\\HostControl]] フィルタを設定することが出来ます。\n\n```php\n// ウェブ・アプリケーション構成ファイル\nreturn [\n    'as hostControl' => [\n        'class' => 'yii\\filters\\HostControl',\n        'allowedHosts' => [\n            'example.com',\n            '*.example.com',\n        ],\n        'fallbackHostInfo' => 'https://example.com',\n    ],\n    // ...\n];\n```\n\n> Note: 「ホスト・ヘッダ攻撃」に対する保護のためには、常に、フィルタの使用よりもウェブ・サーバの構成を優先すべきです。\n  [[yii\\filters\\HostControl]] は、サーバの構成が出来ない場合にだけ使うべきものです。\n\n### SSL ピア検証を構成する\n\nSSL 証明書検証の問題、例えば :\n\n```\ncURL error 60: SSL certificate problem: unable to get local issuer certificate\n```\n\nまたは\n\n```\nstream_socket_enable_crypto(): SSL operation failed with code 1. OpenSSL Error messages: error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed\n```\n\nを解決する方法については、典型的な誤解があります。SSL ピア検証を無効化するよう示唆する間違った情報が数多くありますが、決して従ってはいけません。\nそんなことをすれば、マン・イン・ザ・ミドル型の攻撃を可能にします。そうするのではなく、PHP を適切に構成すべきです。\n\n1. [https://curl.haxx.se/ca/cacert.pem](https://curl.haxx.se/ca/cacert.pem) をダウンロードする。\n2. php.ini に以下を追加する。\n  ```\n  openssl.cafile=\"/path/to/cacert.pem\"\n  curl.cainfo=\"/path/to/cacert.pem\".\n  ```\n\n`cacert.pem` ファイルを最新に保つ必要があることに注意して下さい。\n"
  },
  {
    "path": "docs/guide-ja/security-cryptography.md",
    "content": "暗号化\n======\n\nこのセクションでは、セキュリティの以下の側面について見ていきます。\n\n- 乱数データの生成\n- 暗号化と復号化\n- データの完全性の確認\n\n擬似乱数データを生成する\n------------------------\n\n擬似乱数データはさまざまな状況で役に立ちます。\n例えば、メール経由でパスワードをリセットするときは、トークンを生成してデータベースに保存し、それをユーザにメールで送信します。\nそして、ユーザはこのトークンを自分がアカウントの所有者であることの証拠として使用します。このトークンがユニークかつ推測困難なものであることは非常に重要なことです。\nさもなくば、攻撃者がトークンの値を推測してユーザのパスワードをリセットする可能性があります。\n\nYii のセキュリティヘルパは擬似乱数データの生成を単純な作業にしてくれます。\n\n\n```php\n$key = Yii::$app->getSecurity()->generateRandomString();\n```\n\n暗号化と復号化\n--------------\n\nYii は秘密鍵を使ってデータを暗号化/復号化することを可能にする便利なヘルパ関数を提供しています。データを暗号化関数に渡して、秘密鍵を持つ者だけが復号化することが出来るようにすることが出来ます。\n例えば、何らかの情報をデータベースに保存する必要があるけれども、(たとえアプリケーションのデータベースが第三者に漏洩した場合でも) 秘密鍵を持つユーザだけがそれを見ることが出来るようにする必要がある、という場合には次のようにします。\n\n\n```php\n// $data と $secretKey はフォームから取得する\n$encryptedData = Yii::$app->getSecurity()->encryptByPassword($data, $secretKey);\n// $encryptedData をデータベースに保存する\n```\n\nそして、後でユーザがデータを読みたいときは、次のようにします。\n\n```php\n// $secretKey はユーザ入力から取得、$encryptedData はデータベースから取得\n$data = Yii::$app->getSecurity()->decryptByPassword($encryptedData, $secretKey);\n```\n\n[[\\yii\\base\\Security::encryptByKey()]] と [[\\yii\\base\\Security::decryptByKey()]] によって、\nパスワードの代わりにキーを使うことも可能です。\n\nデータの完全性を確認する\n------------------------\n\nデータが第三者によって改竄されたり、更には何らかの形で毀損されたりしていないことを確認する必要がある、という場合があります。Yii は二つのヘルパ関数の形で、データの完全性を確認するための簡単な方法を提供しています。\n\n秘密鍵とデータから生成されたハッシュをデータにプレフィクスします。\n\n\n```php\n// $secretKey はアプリケーションまたはユーザの秘密、$genuineData は信頼できるソースから取得\n$data = Yii::$app->getSecurity()->hashData($genuineData, $secretKey);\n```\n\nデータの完全性が毀損されていないかチェックします。\n\n```php\n// $secretKey はアプリケーションまたはユーザの秘密、$data は信頼できないソースから取得\n$data = Yii::$app->getSecurity()->validateData($data, $secretKey);\n```\n"
  },
  {
    "path": "docs/guide-ja/security-overview.md",
    "content": "セキュリティ\n============\n\n十分なセキュリティは、すべてのアプリケーションの健全さと成功のために欠くことが出来ないものです。\n不幸なことに、理解が不足しているためか、実装の難易度が高すぎるためか、セキュリティのことになると手を抜く開発者がたくさんいます。\nYii によって駆動されるあなたのアプリケーションを可能な限り安全にするために、Yii はいくつかの優秀な使いやすいセキュリティ機能を内蔵しています。\n\n* [認証](security-authentication.md)\n* [権限付与](security-authorization.md)\n* [パスワードを扱う](security-passwords.md)\n* [暗号化](security-cryptography.md)\n* [ビューのセキュリティ](structure-views.md#security)\n* [認証クライアント](https://github.com/yiisoft/yii2-authclient/blob/master/docs/guide-ja/README.md)\n* [ベスト・プラクティス](security-best-practices.md)\n* [信頼できるプロキシとヘッダ](runtime-requests.md#trusted-proxies)\n"
  },
  {
    "path": "docs/guide-ja/security-passwords.md",
    "content": "パスワードを扱う\n================\n\nほとんどの開発者はパスワードを平文テキストで保存してはいけないということを知っていますが、パスワードを `md5` や `sha1`\nでハッシュしてもまだ安全だと思っている開発者がたくさんいます。かつては、前述のハッシュ・アルゴリズムを使えば十分であった時もありましたが、\nそれらのハッシュや更に強力なハッシュでも、現代のハードウェアをもってすればブルート・フォース・アタックを使って非常に簡単にクラックすることが可能です。\n\n最悪のシナリオ (アプリケーションに侵入された場合) であっても、ユーザのパスワードについて強化されたセキュリティを提供することが出来るように、\nブルート・フォース・アタックに対する耐性が強いハッシュ・アルゴリズムを使う必要があります。現在、最善の選択は `bcrypt` です。\nPHP では、[crypt 関数](https://www.php.net/manual/ja/function.crypt.php) を使って `bcrypt` ハッシュを生成することが出来ます。\nYii は `crypt` を使ってハッシュを安全に生成し検証することを容易にするために、二つのヘルパ関数を提供しています。\n\nユーザが初めてパスワードを提供するとき (例えば、ユーザ登録の時) には、パスワードをハッシュする必要があります。\n\n\n```php\n$hash = Yii::$app->getSecurity()->generatePasswordHash($password);\n```\n\nそして、ハッシュを対応するモデル属性と関連付けて、後で使用するためにデータベースに保存します。\n\nユーザがログインを試みたときは、送信されたパスワードは、前にハッシュされて保存されたパスワードと照合して検証されなければなりません。\n\n\n```php\nif (Yii::$app->getSecurity()->validatePassword($password, $hash)) {\n    // よろしい、ユーザをログインさせる\n} else {\n    // パスワードが違う\n}\n```\n"
  },
  {
    "path": "docs/guide-ja/start-databases.md",
    "content": "データベースを扱う\n==================\n\nこのセクションでは、`country` という名前のデータベース・テーブルから読み出した国データを表示する新しいページの作り方を説明します。\nこの目的を達するために、データベース接続を構成し、\n[アクティブ・レコード](db-active-record.md) クラスを作成し、[アクション](structure-controllers.md) を定義し、\nそして [ビュー](structure-views.md) を作成します。\n\nこのチュートリアルを通じて、次のことを学びます。\n\n* DB 接続を構成する方法\n* アクティブ・レコードのクラスを定義する方法\n* アクティブ・レコードのクラスを使ってデータを検索する方法\n* 改ページを伴う仕方でビューにデータを表示する方法\n\nこのセクションを完了するためには、データベースを使うことについて基本的な知識と経験が無ければならないことに注意してください。\n具体的に言えば、DB クライアント・ツールを用いてデータベースを作成する方法と、SQL 文を実行する方法を知っていなければなりません。\n\n\nデータベースを準備する <span id=\"preparing-database\"></span>\n----------------------\n\nまず初めに、`yii2basic` という名前のデータベースを作成してください。このデータベースからアプリケーションにデータを読み出すことになります。\nYii は多数のデータベース製品に対するサポートを内蔵しており、作成するデータベースは、SQLite、MySQL、PosttreSQL、MSSQL または Oracle から選ぶことが出来ます。以下の説明では、話を単純にするために、MySQL を前提とします。\n\n> Info: MariaDB は、かつては MySQL と差し替え可能な代替物でしたが、現在では完全にそうだとは言えません。MariaDB で `JSON` サポートのような高度な機能を使いたいときは、後述する MariaDB エスステンションの使用を検討して下さい。\n\n次に、データベースに `country` という名前のテーブルを作り、いくつかのサンプル・データを挿入します。そうするためには、次の SQL 文を実行することが出来ます。\n\n```sql\nCREATE TABLE `country` (\n  `code` CHAR(2) NOT NULL PRIMARY KEY,\n  `name` CHAR(52) NOT NULL,\n  `population` INT(11) NOT NULL DEFAULT '0'\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nINSERT INTO `country` VALUES ('AU','Australia',24016400);\nINSERT INTO `country` VALUES ('BR','Brazil',205722000);\nINSERT INTO `country` VALUES ('CA','Canada',35985751);\nINSERT INTO `country` VALUES ('CN','China',1375210000);\nINSERT INTO `country` VALUES ('DE','Germany',81459000);\nINSERT INTO `country` VALUES ('FR','France',64513242);\nINSERT INTO `country` VALUES ('GB','United Kingdom',65097000);\nINSERT INTO `country` VALUES ('IN','India',1285400000);\nINSERT INTO `country` VALUES ('RU','Russia',146519759);\nINSERT INTO `country` VALUES ('US','United States',322976000);\n```\n\nこの時点で、あなたは `yii2basic` という名前のデータベースを持ち、その中に三つのカラムを持つ `country` というテーブルがあり、`country` テーブルは 10 行のデータを持っている、ということになります。\n\nDB 接続を構成する <span id=\"configuring-db-connection\"></span>\n-----------------\n\n先に進む前に、[PDO](https://www.php.net/manual/ja/book.pdo.php) PHP 拡張および使用しているデータベースの PDO ドライバ\n(例えば、MySQL のための `pdo_mysql`) の両方をインストール済みであることを確認してください。\nアプリケーションがリレーショナル・データベースを使う場合、これは基本的な必要条件です。\n\nこれらがインストール済みなら、`config/db.php` というファイルを開いて、あなたのデータベースに適合するようにパラメータを変更してください。\nデフォルトでは、このファイルは下記の記述を含んでいます。\n\n```php\n<?php\n\nreturn [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=localhost;dbname=yii2basic',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n];\n```\n\nこの `config/db.php` というファイルは典型的なファイル・ベースの [構成情報](concept-configurations.md) ツールです。\nこの構成情報ファイルが、背後のデータベースに対する SQL クエリの実行を可能にする [[yii\\db\\Connection]]\nインスタンスの作成と初期化に必要なパラメータを指定するものです。\n\n上記のようにして構成された DB 接続は、アプリケーション・コードの中で `Yii::$app->db` という式でアクセスすることが出来ます。\n\n> Info: `config/db.php` は、メインのアプリケーション構成情報ファイルである `config/web.php` によってインクルードされます。\n  この `config/web.php` が [アプリケーション](structure-applications.md) インスタンスが初期化される仕方を指定するものです。\n  詳しい情報については、[構成情報](concept-configurations.md) のセクションを参照してください。\n\nYii がサポートを内蔵していないデータベースを扱う必要がある場合は、以下のエクステンションの利用を検討してください。\n\n- [Informix](https://github.com/edgardmessias/yii2-informix)\n- [IBM DB2](https://github.com/edgardmessias/yii2-ibm-db2)\n- [Firebird](https://github.com/edgardmessias/yii2-firebird)\n- [MariaDB](https://github.com/sam-it/yii2-mariadb)\n\n\nアクティブ・レコードを作成する <span id=\"creating-active-record\"></span>\n------------------------------\n\n`country` テーブルの中のデータを表現し取得するために、[アクティブ・レコード](db-active-record.md) から派生した `Country` という名前のクラスを作成し、\nそれを `models/Country.php` というファイルに保存します。\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass Country extends ActiveRecord\n{\n}\n```\n\n`Country` クラスは [[yii\\db\\ActiveRecord]] を拡張しています。ここにコードを追加する必要は全くありません。\n上記のコードだけで、Yii は関連付けられたテーブル名をクラス名から推測します。\n\n> Info: クラス名とテーブル名を直接に合致させることが出来ない場合は、[[yii\\db\\ActiveRecord::tableName()]]\nメソッドをオーバーライドして、関連づけられたテーブル名を明示的に指定することが出来ます。\n\n`Country` クラスを使うことによって、以下のコード断片で示すように、`country` テーブルの中のデータを簡単に操作することが出来ます。\n\n```php\nuse app\\models\\Country;\n\n// country テーブルから全ての行を取得して \"name\" 順に並べる\n$countries = Country::find()->orderBy('name')->all();\n\n// プライマリ・キーが \"US\" である行を取得する\n$country = Country::findOne('US');\n\n// \"United States\" を表示する\necho $country->name;\n\n// 国名を \"U.S.A.\" に修正してデータベースに保存する\n$country->name = 'U.S.A.';\n$country->save();\n```\n\n> Info: アクティブ・レコードは、オブジェクト指向の流儀でデータベースのデータにアクセスし、操作する強力な方法です。[アクティブ・レコード](db-active-record.md) のセクションで、詳細な情報を得ることが出来ます。\nもう一つの方法として、[データベース・アクセス・オブジェクト](db-dao.md) と呼ばれる、より低レベルなデータ・アクセス方法を使ってデータベースを操作することも出来ます。\n\n\nアクションを作成する <span id=\"creating-action\"></span>\n--------------------\n\n国データをエンド・ユーザに公開するために、新しいアクションを作成する必要があります。\nこれまでのセクションでしたように `site` コントローラの中に新しいアクションを置くのではなく、国データに関係する全てのアクションに限定した新しいコントローラを作成する方が理にかなうでしょう。\nこの新しいコントローラを `CountryController` と名付けます。そして、下記に示すように、`index` アクションをその中に作成します。\n\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\nuse yii\\data\\Pagination;\nuse app\\models\\Country;\n\nclass CountryController extends Controller\n{\n    public function actionIndex()\n    {\n        $query = Country::find();\n\n        $pagination = new Pagination([\n            'defaultPageSize' => 5,\n            'totalCount' => $query->count(),\n        ]);\n\n        $countries = $query->orderBy('name')\n            ->offset($pagination->offset)\n            ->limit($pagination->limit)\n            ->all();\n\n        return $this->render('index', [\n            'countries' => $countries,\n            'pagination' => $pagination,\n        ]);\n    }\n}\n```\n\n上記のコードを `controllers/CountryController.php` というファイルに保存します。\n\n最初に `index` アクションは `Country::find()` を呼び出します。この [find()](https://www.yiiframework.com/doc/api/2.0/yii-db-activerecord#find()-detail) メソッドが `country` テーブルからデータを取得するメソッドを提供する [ActiveQuery](https://www.yiiframework.com/doc/api/2.0/yii-db-activequery) クエリ・オブジェクトオブジェクトを生成します。\n\n一回のリクエストで返される国の数を制限するために、クエリ・オブジェクトは [[yii\\data\\Pagination]] オブジェクトの助けを借りてページ付けされます。\n`Pagination` オブジェクトは二つの目的に奉仕します。\n\n* クエリによって表現される SQL 文に `offset` 句と `limit` 句をセットして、\n  一度に一ページ分のデータだけ (1ページ最大5行) を返すようにします。\n* 次の項で説明されるように、一連のページ・ボタンからなるページャを\n  ビューに表示するために使われます。\n\n次に、[all()](https://www.yiiframework.com/doc/api/2.0/yii-db-activequery#all()-detail) メソッドがクエリ結果に基づいて全ての `country` レコードを返します。\n\nコードの最後で、`index` アクションは `index` と言う名前のビューをレンダリングします。\nこのときに、返された国データとそのページネーション情報がビューに渡されます。\n\n\nビューを作成する <span id=\"creating-view\"></span>\n----------------\n\n最初に、`views` ディレクトリの下に `country` という名前のサブ・ディレクトリを作ってください。\nこのフォルダが `country` コントローラによって表示される全てのビューを保持するのに使われます。\n`views/country` ディレクトリの中に、下記のコードを含む `index.php` という名前のファイルを作成します。\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\LinkPager;\n?>\n<h1>国リスト</h1>\n<ul>\n<?php foreach ($countries as $country): ?>\n    <li>\n        <?= Html::encode(\"{$country->code} ({$country->name})\") ?>:\n        <?= $country->population ?>\n    </li>\n<?php endforeach; ?>\n</ul>\n\n<?= LinkPager::widget(['pagination' => $pagination]) ?>\n```\n\nビューは国データの表示に関連して二つの部分に分けられます。\n最初の部分では、提供された国データがたどられて、HTML の順序無しリストとしてレンダリングされます。\n第二の部分では、アクションから渡されたページネーション情報を使って、[[yii\\widgets\\LinkPager]] ウィジェットがレンダリングされます。\n`LinkPager` ウィジェットはページ・ボタンのリストを表示します。ボタンのどれかをクリックすると、対応するページの国データが更新表示されます。\n\n\n試してみる <span id=\"trying-it-out\"></span>\n----------\n\n上記のコード全てがどのように動作するかを見るために、ブラウザで下記の URL をアクセスします。\n\n```\nhttps://hostname/index.php?r=country%2Findex\n```\n\n![国リスト](images/start-country-list.png)\n\n最初、ページは5つの国を表示しています。そして、国リストの下には、4つのボタンを持ったページャがあります。\n\"2\" のボタンをクリックすると、ページはデータベースにある次の5つの国、すなわち、2ページ目のレコードを表示します。\n注意深く観察すると、ブラウザの URL も次のように変ったことに気付くでしょう。\n\n```\nhttps://hostname/index.php?r=country%2Findex&page=2\n```\n\n舞台裏では、[[yii\\data\\Pagination|Pagination]] が、データ・セットをページ付けするのに必要な全ての機能を提供しています。\n\n* 初期状態では、[[yii\\data\\Pagination|Pagination]] は、1ページ目を表しています。\n  これを反映して、国の SELECT クエリは `LIMIT 5 OFFSET 0` という句を伴うことになります。その結果、最初の5つの国が取得されて表示されます。\n* [[yii\\widgets\\LinkPager|LinkPager]] ウィジェットは、[[yii\\data\\Pagination::createUrl()|Pagination]] によって作成された\n  URL を使ってページ・ボタンをレンダリングします。\n  URL は、別々のページ番号を表現する `page` というクエリ・パラメータを含んだものになります。\n* ページ・ボタン \"2\" をクリックすると、`country/index` のルートに対する新しいリクエストが発行され、処理されます。\n  [[yii\\data\\Pagination|Pagination]] が URL から `page` クエリ・パラメータを読み取って、カレント・ページ番号を 2 にセットします。\n  こうして、新しい国のクエリは `LIMIT 5 OFFSET 5` という句を持ち、\n  次の5つの国を表示のために返すことになります。\n\n\nまとめ <span id=\"summary\"></span>\n------\n\nこのセクションでは、データベースを扱う方法を学びました。\nまた、[[yii\\data\\Pagination]] と [[yii\\widgets\\LinkPager]] の助けを借りて、ページ付けされたデータを取得し表示する方法も学びました。\n\n次のセクションでは、[Gii](https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide) と呼ばれる強力なコード生成ツールを使う方法を学びます。\nこのツールは、データベース・テーブルのデータを取り扱うための「作成・読出し・更新・削除 (CRUD)」操作のような、\n通常必要とされることが多い諸機能の迅速な実装を手助けしてくれるものです。\n実際のところ、あなたがたった今書いたばかりのコードは、Gii ツールを使えば、全部、Yii が自動的に生成してくれるものです。\n"
  },
  {
    "path": "docs/guide-ja/start-forms.md",
    "content": "フォームを扱う\n==============\n\nこのセクションでは、ユーザからデータを取得するためのフォームを持つ新しいページを作る方法を説明します。\nこのページは名前のインプット・フィールドとメールのインプット・フィールドを持つフォームを表示します。\nユーザからこれら二つの情報を受け取った後、ウェブ・ページは確認のために入力された値をエコー・バックします。\n\nこの目的を達するために、一つの [アクション](structure-controllers.md) と 二つの [ビュー](structure-views.md) を作成する以外に、\n一つの [モデル](structure-models.md) をも作成します。\n\nこのチュートリアルを通じて、次の方法を学びます。\n\n* フォームを通じてユーザによって入力されるデータを表す [モデル](structure-models.md) を作成する方法\n* 入力されたデータを検証する規則を宣言する方法\n* [ビュー](structure-views.md) の中で HTML フォームを構築する方法\n\n\nモデルを作成する <span id=\"creating-model\"></span>\n----------------\n\nユーザに入力してもらうデータは、下に示されているように `EntryForm` モデル・クラスとして表現され、\n`models/EntryForm.php` というファイルに保存されます。\nクラス・ファイルの命名規約についての詳細は [クラスのオートロード](concept-autoloading.md) のセクションを参照してください。\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse Yii;\nuse yii\\base\\Model;\n\nclass EntryForm extends Model\n{\n    public $name;\n    public $email;\n\n    public function rules()\n    {\n        return [\n            [['name', 'email'], 'required'],\n            ['email', 'email'],\n        ];\n    }\n}\n```\n\nこのクラスは、Yii によって提供される基底クラス [[yii\\base\\Model]] を拡張するものです。\n通常、この基底クラスがフォーム・データを表現するのに使われます。\n\n> Info: [[yii\\base\\Model]] はデータベース・テーブルと関連*しない*モデル・クラスの親として使われます。\nデータベース・テーブルと対応するモデル・クラスでは、通常は [[yii\\db\\ActiveRecord]] が親になります。\n\n`EntryForm` クラスは二つのパブリック・メンバー、`name` と `email` を持っており、これらがユーザによって入力されるデータを保管するのに使われます。\nこのクラスはまた `rules()` という名前のメソッドを持っています。このメソッドがデータを検証する一連の規則を返します。\n上記で宣言されている検証規則は次のことを述べています。\n\n* `name` と `email` は、ともに値を要求される\n* `email` のデータは構文的に有効なメール・アドレスでなければならない\n\nユーザによって入力されたデータを `EntryForm` オブジェクトに投入した後、[[yii\\base\\Model::validate()|validate()]] メソッドを呼んでデータ検証ルーチンを始動することが出来ます。\nデータ検証が失敗すると [[yii\\base\\Model::hasErrors|hasErrors]] プロパティが `true` に設定されます。\nそして、[[yii\\base\\Model::getErrors|errors]] を通じて、どのような検証エラーが発生したかを知ることが出来ます。\n\n\n```php\n<?php\n$model = new EntryForm();\n$model->name = 'Qiang';\n$model->email = 'bad';\nif ($model->validate()) {\n    // 良し!\n} else {\n    // 失敗!\n    // $model->getErrors() を使う\n}\n```\n\n\nアクションを作成する <span id=\"creating-action\"></span>\n--------------------\n\n次に、この新しいモデルを使う `entry` アクションを `site` コントローラに作る必要があります。\nアクションを作成して使うプロセスについては、[こんにちは、と言う](start-hello.md) のセクションで既に説明されています。\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\nuse app\\models\\EntryForm;\n\nclass SiteController extends Controller\n{\n    // ... 既存のコード ...\n\n    public function actionEntry()\n    {\n        $model = new EntryForm();\n\n        if ($model->load(Yii::$app->request->post()) && $model->validate()) {\n            // $model に有効なデータを受け取った場合\n\n            // ここで $model について何か意味のあることをする ...\n\n            return $this->render('entry-confirm', ['model' => $model]);\n        } else {\n            // ページの初期表示か、または、何か検証エラーがある場合\n            return $this->render('entry', ['model' => $model]);\n        }\n    }\n}\n```\n\nアクションは最初に `EntryForm` オブジェクトを生成します。\n次に、モデルに `$_POST` のデータ、Yii においては [[yii\\web\\Request::post()]] によって提供されるデータを投入しようと試みます。\nモデルへのデータ投入が成功した場合（つまり、ユーザが HTML フォームを送信した場合)、アクションは[[yii\\base\\Model::validate()|validate()]] を呼んで、\n入力された値が有効なものであるかどうかを確認します。\n\n> Info: `Yii::$app` という式は [アプリケーション](structure-applications.md) インスタンスを表現します。\n  これはグローバルにアクセス可能なシングルトンです。\n  これは、また、特定の機能性をサポートする `request`、`response`、`db` などのコンポーネントを提供する [サービス・ロケータ](concept-service-locator.md) でもあります。\n  上記のコードでは、アプリケーション・インスタンスの `request` コンポーネントが `$_POST` データにアクセスするために使われています。\n\nすべてが適正である場合、アクションは `entry-confirm` という名前のビューを表示して、データの送信が成功したことをユーザに確認させます。\nデータが送信されなかったり、データがエラーを含んでいたりする場合は、`entry` ビューが表示され、\nその中で HTML フォームが (もし有れば) 検証エラーのメッセージとともに表示されます。\n\n> Note: この簡単な例では、有効なデータ送信に対して単純に確認ページを表示しています。\n  実際の仕事では、[フォーム送信の諸問題](https://en.wikipedia.org/wiki/Post/Redirect/Get) を避けるために、\n  [[yii\\web\\Controller::refresh()|refresh()]] または [[yii\\web\\Controller::redirect()|redirect()]] を使うことを考慮すべきです。\n\n\nビューを作成する <span id=\"creating-views\"></span>\n----------------\n\n最後に、`entry-confirm` と `entry` という名前の二つのビュー・ファイルを作成します。\n今まさに説明したように、これらが `entry` アクションによって表示されます。\n\n`entry-confirm` ビューは単純に名前とメールのデータを表示するものです。このビューは `views/site/entry-confirm.php` というファイルに保存しなければなりません。\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n<p>あなたは次の情報を入力しました</p>\n\n<ul>\n    <li><label>名前</label>: <?= Html::encode($model->name) ?></li>\n    <li><label>メール</label>: <?= Html::encode($model->email) ?></li>\n</ul>\n```\n\n`entry` ビューは HTML フォームを表示します。これは `views/site/entry.php` というファイルに保存しなければなりません。\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n?>\n<?php $form = ActiveForm::begin(); ?>\n\n    <?= $form->field($model, 'name') ?>\n\n    <?= $form->field($model, 'email') ?>\n\n    <div class=\"form-group\">\n        <?= Html::submitButton('送信', ['class' => 'btn btn-primary']) ?>\n    </div>\n\n<?php ActiveForm::end(); ?>\n```\n\nこのビューは HTML フォームを構築するのに、[[yii\\widgets\\ActiveForm|ActiveForm]] と呼ばれる強力な\n[ウィジェット](structure-widgets.md) を使います。\nウィジェットの `begin()` メソッドと `end()` メソッドが、それぞれ、フォームの開始タグと終了タグをレンダリングします。\nこの二つのメソッドの呼び出しの間に、[[yii\\widgets\\ActiveForm::field()|field()]] メソッドによってインプット・フィールドが作成されます。\n最初のインプット・フィールドは \"name\" のデータ、第二のインプット・フィールドは \"email\" のデータのためのものです。\nインプット・フィールドの後に、[[yii\\helpers\\Html::submitButton()]] メソッドが呼ばれて、送信ボタンを生成しています。\n\n\n試してみる <span id=\"trying-it-out\"></span>\n----------\n\nどのように動作するかを見るために、ブラウザで下記の URL にアクセスしてください。\n\n```\nhttps://hostname/index.php?r=site%2Fentry\n```\n\n二つのインプット・フィールドを持つフォームを表示するページが表示されるでしょう。それぞれのインプット・フィールドの前には、どんなデータを入力すべきかを示すラベルがあります。\n何も入力せずに、あるいは、無効なメール・アドレスを入力して送信ボタンをクリックすると、それぞれ問題のあるインプット・フィールドの後ろにエラー・メッセージが表示されます。\n\n![検証エラーのあるフォーム](images/start-form-validation.png)\n\n有効な名前とメール・アドレスを入力してから送信ボタンをクリックすると、\nたった今入力したデータを表示する新しいページが表示されます。\n\n![データ入力の確認](images/start-entry-confirmation.png)\n\n\n\n### 魔法の説明<span id=\"magic-explained\"></span>\n\nあなたは、舞台裏で HTML フォームがどのように動いているのか、不思議に思うかも知れません。\nなぜなら、フォームが、ほとんど魔法のように、各インプット・フィールドのラベルを表示し、データを正しく入力しなかった場合には、\nページをリロードすることなく、エラー・メッセージを表示するからです。\n\nそう、データの検証は、最初に JavaScript を使ってクライアント・サイドで実行され、次に PHP によってサーバ・サイドで実行されます。\n[[yii\\widgets\\ActiveForm]] は、賢いことに、`EntryForm` で宣言した検証規則を抽出し、それを実行可能な JavaScript コードに変換して、\nJavaScript を使ってデータ検証を実行します。\nブラウザで JavaScript を無効にした場合でも、`actionEntry()` メソッドで示されているように、サーバ・サイドでの検証は引き続き実行されます。\nこれにより、どのような状況であっても、データの有効性が保証されます。\n\n> Warning: クライアント・サイドの検証は、ユーザにとってのより良い使い心地のために利便性を提供するものです。\n  クライアント・サイドの検証の有無にかかわらず、サーバ・サイドの検証は常に必要です。\n\nインプット・フィールドのラベルは、モデルのプロパティ名を使用して、`field()` メソッドによって生成されます。\n例えば、`name` というプロパティから `Name` というラベルが生成されます。\n\nビューの中で、下記のコードのように、\nラベルをカスタマイズすることも出来ます。\n\n```php\n<?= $form->field($model, 'name')->label('お名前') ?>\n<?= $form->field($model, 'email')->label('メール・アドレス') ?>\n```\n\n> Info: Yii はこのようなウィジェットを数多く提供して、複雑で動的なビューを素速く作成することを手助けしてくれます。\n  後で学ぶように、新しいウィジェットを書くことも非常に簡単です。\n  あなたは、将来のビュー開発を単純化するために、多くのビュー・コードを再利用可能なウィジェットに変換したいと思うことでしょう。\n\n\nまとめ <span id=\"summary\"></span>\n------\n\nガイドのこのセクションにおいては、MVC アーキテクチャ・パターンの全ての部分に触れました。\nそして、ユーザ・データを表現し、当該データを検証するモデル・クラスを作成する方法を学びました。\n\nまた、ユーザからデータを取得する方法と、ブラウザにデータを表示して返す方法も学びました。\nこの作業は、アプリケーションを開発するときに、多大な時間を必要とするものになり得るものです。\nしかし、Yii はこの作業を非常に容易にする強力なウィジェットを提供しています。\n\n次のセクションでは、ほとんど全てのアプリケーションで必要とされるデータベースを取り扱う方法を学びます。\n"
  },
  {
    "path": "docs/guide-ja/start-gii.md",
    "content": "Gii でコードを生成する\n======================\n\nこのセクションでは、[Gii](https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide) を使って、ウェブ・サイトの一般的な機能のいくつかを実装するコードを自動的に生成する方法を説明します。\nGii を使ってコードを自動生成することは、Gii のウェブ・ページに表示される指示に対して正しい情報を入力するだけのことです。\n\nこのチュートリアルを通じて、次のことを学びます。\n\n* アプリケーションで Gii を有効にする方法\n* Gii を使って、アクティブ・レコードのクラスを生成する方法\n* Gii を使って、DB テーブルの CRUD 操作を実装するコードを生成する方法\n* Gii によって生成されるコードをカスタマイズする方法\n\n\nGii を開始する <span id=\"starting-gii\"></span>\n--------------\n\n[Gii](https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide) は Yii の [モジュール](structure-modules.md) として提供されています。\nGii は、アプリケーションの [[yii\\base\\Application::modules|modules]] プロパティの中で構成することで有効にすることが出来ます。アプリケーションを生成した仕方にもよりますが、`config/web.php` の構成情報ファイルの中に、多分、下記のコードが既に提供されているでしょう。\n\n```php\n$config = [ ... ];\n\nif (YII_ENV_DEV) {\n    $config['bootstrap'][] = 'gii';\n    $config['modules']['gii'] = [\n        'class' => 'yii\\gii\\Module',\n    ];\n}\n```\n\n上記の構成情報は、[開発環境](concept-configurations.md#environment-constants) において、アプリケーションは `gii` という名前のモジュールをインクルードすべきこと、\nそして `gii` は [[yii\\gii\\Module]] というクラスであることを記述しています。\n\nアプリケーションの [エントリ・スクリプト](structure-entry-scripts.md) である `web/index.php` をチェックすると、次の行があることに気付くでしょう。\nこれは本質的には `YII_ENV_DEV` を `true` に設定するものです。\n\n```php\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n```\n\nこの行のおかげで、アプリケーションは開発モードになっており、上記の構成情報によって、Gii が既に有効になっています。これで、下記の URL によって Gii にアクセスすることが出来ます。\n\n```\nhttps://hostname/index.php?r=gii\n```\n\n> Note: ローカルホスト以外のマシンから Gii にアクセスしようとすると、デフォルトではセキュリティ上の理由でアクセスが拒否されます。\n> 下記のように Gii を構成して、許可される IP アドレスを追加することが出来ます。\n>\n```php\n'gii' => [\n    'class' => 'yii\\gii\\Module',\n    'allowedIPs' => ['127.0.0.1', '::1', '192.168.0.*', '192.168.178.20'] // 必要に応じて調整\n],\n```\n\n![Gii](images/start-gii.png)\n\n\nアクティブ・レコードのクラスを生成する <span id=\"generating-ar\"></span>\n------------------------------------\n\nGii を使ってアクティブ・レコードのクラスを生成するためには、\"Model Generator\" を選びます (Gii のインデックス・ページのリンクをクリックして下さい)。そして、次のようにフォームに入力します。\n\n* Table Name: `country`\n* Model Class: `Country`\n\n![Model Generator](images/start-gii-model.png)\n\n次に、\"Preview\" ボタンをクリックします。そうすると、結果として作成されるクラス・ファイルのリストに `models/Country.php` が挙ってきます。クラス・ファイルの名前をクリックすると、内容をプレビューすることが出来ます。\n\nGii を使うときに、既に同じファイルを作成していて、それを上書きしようとしている場合は、\nファイル名の隣の `diff` ボタンをクリックして、\n生成されようとしているコードと既存のバージョンの違いを見てください。\n\n![Model Generator のプレビュー](images/start-gii-model-preview.png)\n\n既存のファイルを上書きするときは、\"overwrite\" の隣のチェックボックスをチェックしてから \"Generate\" ボタンをクリックします。新しいファイルを作成するときは、単に \"Generate\" をクリックすれば十分です。\n\n次に、コードの生成が成功したことを示す確認ページが表示されます。\n既存のファイルがあった場合は、それが新しく生成されたコードで上書きされたことを示すメッセージも同じく表示されます。\n\n\nCRUD コードを生成する <span id=\"generating-crud\"></span>\n---------------------\n\nCRUD は Create(作成)、Read(読出し)、Update(更新)、そして Delete(削除) を意味しており、ほとんどのウェブ・サイトでデータを扱うときによく用いられる4つのタスクを表しています。Gii を使って CRUD 機能を作成するためには、\"CRUD Generator\" を選びます (Gii のインデックス・ページのリンクをクリックしてください) 。「国リスト」のサンプルのためには、表示されたフォームに以下のように入力します。\n\n* Model Class: `app\\models\\Country`\n* Search Model Class: `app\\models\\CountrySearch`\n* Controller Class: `app\\controllers\\CountryController`\n\n![CRUD Generator](images/start-gii-crud.png)\n\n次に、\"Preview\" ボタンをクリックします。生成されるファイルのリストは、次のようになります。\n\n![CRUD Generator のプレビュー](images/start-gii-crud-preview.png)\n\n以前に（ガイドのデータベースのセクションで）`controllers/CountryController.php` と `views/country/index.php` のファイルを作成していた場合は、それらを置き換えるために \"overwrite\" のチェックボックスをチェックしてください。\n(以前のバージョンは フル機能の CRUD をサポートしていません。)\n\n\n試してみる <span id=\"trying-it-out\"></span>\n----------\n\nどのように動作するかを見るために、ブラウザを使って下記の URL にアクセスしてください。\n\n```\nhttps://hostname/index.php?r=country%2Findex\n```\n\nデータ・グリッドがデータベース・テーブルから取得した国を表示しているページが表示されます。\nグリッドをソートしたり、カラムのヘッダに検索条件を入力してグリッドにフィルタを適用したりすることが出来ます。\n\nグリッドに表示されているそれぞれの国について、詳細を見たり、更新したり、または削除したりすることが出来ます。\nまた、グリッドの上にある \"Create Country\" ボタンをクリックすると、新しい国データを作成するためのフォームが利用に供されます。\n\n![国リストのデータ・グリッド](images/start-gii-country-grid.png)\n\n![国データを更新する](images/start-gii-country-update.png)\n\n下記が Gii によって生成されるファイルのリストです。\nこれらの機能がどのように実装されているかを調査したい場合、また、これらの機能をカスタマイズしたいときに参照してください。\n\n* Controller: `controllers/CountryController.php`\n* Models: `models/Country.php` と `models/CountrySearch.php`\n* Views: `views/country/*.php`\n\n> Info: Gii は非常にカスタマイズしやすく拡張しやすいコード生成ツールとして設計されています。\n  これを賢く使うと、アプリケーションの開発速度を大いに高めることが出来ます。\n  詳細については、[Gii](https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide) のセクションを参照してください。\n\n\nまとめ <span id=\"summary\"></span>\n------\n\nこのセクションでは、Gii を使ってコードを生成して、データベース・テーブルに保存されているコンテントのための\n完全な CRUD 機能を実装する方法を学びました。\n"
  },
  {
    "path": "docs/guide-ja/start-hello.md",
    "content": "こんにちは、と言う\n==================\n\nこのセクションでは、アプリケーションに「こんにちは」という新しいページを作成する方法を説明します。\nこの目的を達するために、[アクション](structure-controllers.md#creating-actions) と\n[ビュー](structure-views.md) を作成します。\n\n* アプリケーションは、このページへのリクエストをそのアクションに送付します。\n* 次にそのアクションが「こんにちは」という言葉をエンド・ユーザに示すビューを表示します。\n\nこのチュートリアルを通じて、三つのことを学びます。\n\n1. リクエストに応える [アクション](structure-controllers.md#creating-actions) を作成する方法\n2. レスポンスのコンテントを作成する [ビュー](structure-views.md) を作成する方法\n3. アプリケーションがリクエストを [アクション](structure-controllers.md#creating-actions) に送付する仕組み\n\n\nアクションを作成する <span id=\"creating-action\"></span>\n--------------------\n\n「こんにちは」のタスクのために、リクエストから `message` パラメータを読んで、そのメッセージをユーザに表示して返す\n`say` [アクション](structure-controllers.md#creating-actions) を作ります。\nリクエストが `message` パラメータを提供しなかった場合は、アクションはデフォルト値として \"こんにちは\" というメッセージを表示するものとします。\n\n> Info: [アクション](structure-controllers.md#creating-actions) は、エンド・ユーザが直接に参照して実行できるオブジェクトです。\n  アクションは [コントローラ](structure-controllers.md) によってグループ化されます。\n  アクションの実行結果が、エンド・ユーザが受け取るレスポンスです。\n\nアクションは [コントローラ](structure-controllers.md) の中で宣言されなければなりません。\n話を簡単にするために、`say` アクションを既存の `SiteController` の中で宣言しましょう。\nこのコントローラは `controllers/SiteController.php` というクラス・ファイルの中で定義されています。次のようにして、新しいアクションが始まります。\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    // ... 既存のコード ...\n\n    public function actionSay($message = 'こんにちは')\n    {\n        return $this->render('say', ['message' => $message]);\n    }\n}\n```\n\n上記のコードでは、`SiteController` クラスの中で、`say` アクションが `actionSay` という名前のメソッドとして定義されています。\nYii はコントローラ・クラスの中で、アクション・メソッドと非アクション・メソッドを区別するために、`action` という接頭辞を使います。\n`action` という接頭辞の後に続く名前がアクション ID にマップされます。\n\nアクションを命名するについては、Yii がアクション ID をどのように取り扱うかを知っていなければなりません。\nアクション ID は常に小文字で参照されます。\nアクション ID が複数の単語を必要とするときは、単語がダッシュ (-) で連結されます (例えば、`create-comment`)。\nアクション・メソッドの名前は、アクション ID からダッシュを全て削除し、各単語の先頭の文字を大文字にした結果に `action` という接頭辞を付けたものになります。\n例えば、アクション ID `create-comment` はアクション・メソッド名 `actionCreateComment` に対応します。\n\n私たちの例では、アクション・メソッドは `$message` というパラメータを取り、そのデフォルト値は `\"こんにちは\"` です\n(PHP で関数やメソッドの引数にデフォルト値を設定するのと全く同じ方法です)。\nアプリケーションがリクエストを受け取って、当該リクエストの処理を `say` アクションが担当すべきであると決定した場合は、\nリクエストの中に見つかった同じ名前のパラメータの値をこの `$message` パラメータに代入します。\n言い換えれば、もしリクエストの中に `\"さようなら\"` という値の `message` パラメータが入っていれば、アクションの `$message` 変数にその値が割り当てられます。\n\nアクション・メソッドの中では、[[yii\\web\\Controller::render()|render()]] が呼ばれて `say` と言う名前の [ビュー](structure-views.md) ファイルがレンダリングされます。\n`message` パラメータも同時にビューに渡され、そこで使用されます。\nレンダリング結果はアクション・メソッドによって返されます。\n返された結果はアプリケーションによって受け取られ、ブラウザ上でエンド・ユーザに (完全な HTML ページの一部として) 表示されます。\n\n\nビューを作成する <span id=\"creating-view\"></span>\n----------------\n\n[ビュー](structure-views.md) は、レスポンスのコンテントを生成するために書かれるスクリプトです。\n「こんにちは」のタスクのためには、アクション・メソッドから受け取った `message` パラメータを出力する `say` ビューを作成します。\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n<?= Html::encode($message) ?>\n```\n\n`say` ビューは `views/site/say.php` というファイルに保存しなければなりません。\nアクションの中で [[yii\\web\\Controller::render()|render()]] メソッドが呼ばれるとき、`render()` メソッドは `views/ControllerID/ViewName.php` という名前の PHP ファイルを探します。\n\n上記のコードで `message` パラメータが出力される前に  [[yii\\helpers\\Html::encode()|HTML-エンコード]] されていることに注意してください。\nパラメータはエンド・ユーザから来るものであり、悪意のある JavaScript コードを埋め込まれて\n[クロス・サイト・スクリプティング (XSS) 攻撃](https://ja.wikipedia.org/wiki/%E3%82%AF%E3%83%AD%E3%82%B9%E3%82%B5%E3%82%A4%E3%83%88%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0) に使われうるものですから、\n脆弱性を防止するためにこうすることが必要です。\n\n当然ながら、`say` ビューにはもっと多くのコンテントを入れても構いません。コンテントには、HTML タグ、平文テキスト、さらには PHP 文を含めることが出来ます。\n実際、`say` ビューは [[yii\\web\\Controller::render()|render()]] メソッドによって実行される PHP スクリプトであるに過ぎません。\nビュー・スクリプトによって出力されたコンテントはレスポンス結果としてアプリケーションに返されます。そしてアプリケーションがこの結果をエンド・ユーザに対して出力します。\n\n\n試してみる <span id=\"trying-it-out\"></span>\n----------\n\nアクションとビューを作成したら、下記の URL で新しいページにアクセスすることが出来ます。\n\n```\nhttps://hostname/index.php?r=site%2Fsay&message=Hello+World\n```\n\n![Hello World](images/start-hello-world.png)\n\nこの URL は、結果として、\"Hello World\" を表示するページになります。このページはアプリケーションの他のページと同じヘッダとフッタを共有しています。\n\nURL から `message` パラメータを省略すると、\"こんにちは\" を表示するページを見ることになるでしょう。\nこれは、`message` が `actionSay()` メソッドにパラメータとして渡されるものであり、それが省略された場合には、デフォルト値である `\"こんにちは\"` が代りに使われるからです。\n\n> Info: 新しいページは他のページと同じヘッダとフッタを共有していますが、それは [[yii\\web\\Controller::render()|render()]] メソッドが `say` ビューの結果を\nいわゆる [レイアウト](structure-views.md#layouts) に自動的に埋め込むからです。\nレイアウトは、この場合、`views/layouts/main.php` にあります。\n\n上記の URL の `r` パラメータについては、さらに説明が必要でしょう。\nこれは [ルート](runtime-routing.md)、すなわち、アクションを指し示すアプリケーションを通じて一意な ID を表します。\nルートの書式は `ControllerID/ActionID` です。\nアプリケーションはリクエストを受け取ると、このパラメータ `r` をチェックし、`ControllerID` の部分を使って、このリクエストを処理するためにどのコントローラ・クラスのインスタンスを作成すべきかを決定します。\nそして、コントローラは `ActionID` の部分を使って、実際の仕事をするためにどのアクションを呼び出すべきかを決定します。\nこの例で言えば、`site/say` というルートは、`SiteController` コントローラ・クラスと `say` アクションとして解決されます。\n結果として、`SiteController::actionSay()` メソッドがリクエストを処理するために呼び出されます。\n\n> Info: アクションと同じく、コントローラもまたアプリケーションの中で一意に定義される ID を持ちます。\n  コントローラ ID も、アクション ID と同じ命名規則を使います。\n  コントローラ・クラスの名前は、コントローラ ID からダッシュを削除し、各単語の最初の文字を大文字にし、\n  結果として出来る文字列に `Controller` という接尾辞を追加したものとなります。\n  例えば、`post-comment` というコントローラ ID に対応するコントローラ・クラスの名前は `PostCommentController` です。\n\n\nまとめ <span id=\"summary\"></span>\n------\n\nこのセクションでは、MVC アーキテクチャ・パターンのうちのコントローラとビューの部分に触れました。\n特定のリクエストを処理するためのアクションをコントローラの一部として作成しました。また、レスポンスのコンテントを作成するためのビューも作成しました。\nこの単純な例においては、使用される唯一のデータが `message` パラメータであったため、モデルは関係していません。\n\nまた、Yii におけるルートについても学びました。ルートはユーザのリクエストとコントローラのアクションとの橋渡しとして働くものです。\n\n次のセクションでは、モデルを作成する方法を学びます。そして、HTML フォームを含むページを追加します。\n"
  },
  {
    "path": "docs/guide-ja/start-installation.md",
    "content": "Yii をインストールする\n======================\n\nYii は二つの方法でインストールすることが出来ます。すなわち、[Composer](https://getcomposer.org/) を使うか、アーカイブ・ファイルをダウンロードするかです。\n前者がお薦めの方法です。と言うのは、一つのコマンドを走らせるだけで、新しい [エクステンション](structure-extensions.md) をインストールしたり、Yii をアップデートしたりすることが出来るからです。\n\nYii の標準的なインストールを実行すると、フレームワークとプロジェクト・テンプレートの両方がダウンロードされてインストールされます。\nプロジェクト・テンプレートは、いくつかの基本的な機能、例えば、ログインやコンタクト・フォームなどを実装した、動作する Yii アプリケーションです。\nそのコードは推奨される方法に従って編成されています。そのため、プロジェクト・テンプレートは、あなたのプロジェクトのための良い開始点としての役割を果たしうるものです。\n\nここから続くいくつかのセクションにおいては、いわゆる *ベーシック・プロジェクト・テンプレート* とともに Yii をインストールする方法、\nおよび、このテンプレートの上に新しい機能を実装する方法を説明します。\nYii はもう一つ、[アドバンスト・プロジェクト・テンプレート](https://www.yiiframework.com/extension/yiisoft/yii2-app-advanced/doc/guide) と呼ばれるテンプレートも提供しています。\nこちらは、チーム開発環境において多層構造のアプリケーションを開発するときに使用する方が望ましいものです。\n\n> Info: ベーシック・プロジェクト・テンプレートは、ウェブ・アプリケーションの 90 パーセントを開発するのに適したものです。\n  アドバンスト・プロジェクト・テンプレートとの主な違いは、コードがどのように編成されているかという点にあります。\n  あなたが Yii は初めてだという場合は、シンプルでありながら十分な機能を持っているベーシック・プロジェクト・テンプレートに留まることを強く推奨します。\n\n\nComposer によるインストール <span id=\"installing-via-composer\"></span>\n---------------------------\n\n### Composer をインストールする\n\nまだ Composer をインストールしていない場合は、[getcomposer.org](https://getcomposer.org/download/) の指示に従ってインストールすることが出来ます。\nLinux や Mac OS X では、次のコマンドを実行します。\n\n```bash\ncurl -sS https://getcomposer.org/installer | php\nsudo mv composer.phar /usr/local/bin/composer\n```\n\nWindows では、[Composer-Setup.exe](https://getcomposer.org/Composer-Setup.exe) をダウンロードして実行します。\n\n何か問題が生じたときは、[Composer ドキュメントのトラブル・シューティングのセクション](https://getcomposer.org/doc/articles/troubleshooting.md)\nを参照してください。\nComposer は初めてだという場合は、少なくとも、Composer ドキュメントの [基本的な使い方のセクション](https://getcomposer.org/doc/01-basic-usage.md)\nも参照することを推奨します。\n\nこのガイドでは、composer のコマンドの全ては、あなたが composer を [グローバル](https://getcomposer.org/doc/00-intro.md#globally) にインストールし、\n`composer` コマンドとして使用できるようにしているものと想定しています。\nそうではなく、ローカル・ディレクトリにある `composer.phar` を使おうとする場合は、例に出てくるコマンドをそれに合せて修正しなければなりません。\n\n以前に Composer をインストールしたことがある場合は、確実に最新のバージョンを使うようにしてください。\nComposer は `composer self-update` コマンドを実行してアップデートすることが出来ます。\n\n> Note: Yii のインストールを実行する際に、Composer は大量の情報を Github API から要求する必要が生じます。\n> リクエストの数は、あなたのアプリケーションが持つ依存の数によりますが、**Github API レート制限** より大きくなることがあり得ます。\n> この制限にかかった場合、Composer は Github API アクセス・トークンを取得するために、あなたの Github ログイン認証情報を要求するでしょう。\n> 高速な接続においては、Composer が対処できるよりも早い段階でこの制限にかかることもありますので、\n> Yii のインストールの前に、このアクセス・トークンを構成することを推奨します。\n> アクセス・トークンの構成の仕方については、[Github API トークンに関する Composer ドキュメント](https://getcomposer.org/doc/articles/troubleshooting.md#api-rate-limit-and-oauth-tokens)\n> の指示を参照して下さい。\n\n### Yii をインストールする <span id=\"installing-from-composer\"></span>\n\nComposer がインストールされたら、ウェブ・アクセス可能なフォルダで下記のコマンドを実行することによって\nYii アプリケーション・テンプレートをインストールすることが出来ます。\n\n```bash\ncomposer create-project --prefer-dist yiisoft/yii2-app-basic basic\n```\n\nこのコマンドが `basic` という名前のディレクトリに Yii アプリケーション／テンプレートの最新の安定版をインストールします。\n必要なら別のディレクトリ名を選ぶことも出来ます。\n\n> Info: `composer create-project` コマンドが失敗するときは、\n> よくあるエラーについて [Composer ドキュメントのトラブル・シューティングのセクション](https://getcomposer.org/doc/articles/troubleshooting.md) を参照して下さい。\n> エラーを修正した後は、`basic` ディレクトリの中で `composer update` を実行して、\n> 中断されたインストールを再開することが出来ます。\n\n> Tip: Yii の最新の開発バージョンをインストールしたい場合は、[stability option](https://getcomposer.org/doc/04-schema.md#minimum-stability)\n> を追加した次のコマンドを代りに使うことが出来ます。\n>\n> ```bash\n> composer create-project --prefer-dist --stability=dev yiisoft/yii2-app-basic basic\n> ```\n>\n> 開発バージョンは動いているあなたのコードを動かなくするかもしれませんので、本番環境では使うべきでないことに注意してください。\n\n\nアーカイブ・ファイルからインストールする <span id=\"installing-from-archive-file\"></span>\n----------------------------------------\n\nアーカイブ・ファイルから Yii をインストールするには、三つの手順を踏みます。\n\n1. [yiiframework.com](https://www.yiiframework.com/download/) からアーカイブ・ファイルをダウンロードします。\n2. ダウンロードしたファイルをウェブ・アクセス可能なフォルダに展開します。\n3. `config/web.php` ファイルを編集して、`cookieValidationKey` という構成情報の項目に秘密キーを入力します\n   (Composer を使って Yii をインストールするときは、これは自動的に実行されます)。\n\n   ```php\n   // !!! 下記に(もし空白なら)秘密キーを入力する - これはクッキー検証のために必要\n   'cookieValidationKey' => '秘密キーをここに入力',\n   ```\n\n\n他のインストール・オプション <span id=\"other-installation-options\"></span>\n----------------------------\n\n上記のインストール方法の説明は Yii のインストールの仕方を示すものですが、それは同時に、直ちに動作する基本的なウェブ・アプリケーションを作成するものでもあります。\nこれは、規模の大小に関わらず、ほとんどのプロジェクトを開始するのに良い方法です。\n特に、Yii の学習を始めたばかりの場合には、この方法が適しています。\n\nしかし、他のインストール・オプションも利用可能です。\n\n* コア・フレームワークだけをインストールし、アプリケーション全体を一から構築したい場合は、[アプリケーションを一から構築する](tutorial-start-from-scratch.md)\n  で説明されている指示に従うことが出来ます。\n* もっと洗練された、チーム開発環境により適したアプリケーションから開始したい場合は、 \n  [アドバンスト・プロジェクト・テンプレート](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide-ja/README.md) をインストールすることを考慮することが出来ます。\n\n\nアセットをインストールする <span id=\"installing-assets\"></span>\n--------------------------\n\nYii は、アセット (CSS および JavaScript) ライブラリのインストールについて [Bower](https://bower.io/) および/または [NPM](https://www.npmjs.com/) のパッケージに依存しています。\nYii はこれらのライブラリを取得するのに Composer を使って、PHP と CSS/JavaScript のパッケージ・バージョンを同時に解決できるようにしています。\nこのことは、[asset-packagist.org](https://asset-packagist.org) または [composer asset plugin](https://github.com/fxpio/composer-asset-plugin) を使用することによって達成されます。\n詳細は [アセットのドキュメント](structure-assets.md) を参照して下さい。\n\nあなたは、アセットの管理をネイティブの Bower/NPM クライアントで行ったり、CND を使ったり、アセットのインストールを完全に避けたりしたいかも知れません。\nComposer によるアセットのインストールを抑止するためには、`composer.json` に次の記述を追加して下さい。\n\n```json\n\"replace\": {\n    \"bower-asset/jquery\": \">=1.11.0\",\n    \"bower-asset/inputmask\": \">=3.2.0\",\n    \"bower-asset/punycode\": \">=1.3.0\",\n    \"bower-asset/yii2-pjax\": \">=2.0.0\"\n},\n```\n\n> Note: Composer によるアセットのインストールをバイパスする場合は、アセットのインストールとバージョン衝突の解決についてあなたが責任を持たなければなりません。\n> さまざまなエクステンションに由来するアセット・ファイル間で不整合が生じうることを覚悟して下さい。\n\n\nインストールを検証する <span id=\"verifying-installation\"></span>\n----------------------\n\nインストール完了後、あなたのウェブ・サーバを構成してください (次のセクションを参照してください)。\nあるいは、プロジェクトの `web` ディレクトリで次のコマンドを実行して、\n[PHP の内蔵ウェブ・サーバ](https://www.php.net/manual/ja/features.commandline.webserver.php) を使ってください。\n\n```bash\nphp yii serve\n```\n\n> Note: デフォルトでは、この HTTP サーバは 8080 ポートをリスンします。しかし、このポートがすでに使われていたり、複数のアプリケーションをこの方法で動かしたい場合は、\n  どのポートを使うかを指定したいと思うでしょう。単に --port 引数を追加して下さい。\n\n```bash\nphp yii serve --port=8888\n```\n\n下記の URL によって、インストールされた Yii アプリケーションにブラウザを使ってアクセスすることが出来ます。\n\n```\nhttp://localhost:8080/\n```\n\n![Yii のインストールが成功](images/start-app-installed.png)\n\nブラウザに上のような \"おめでとう!\" のページが表示されるはずです。もし表示されなかったら、PHP のインストールが Yii の必要条件を満たしているかどうか、チェックしてください。\n最低限の必要条件を満たしているかどうかは、次の方法のどちらかによってチェックすることが出来ます。\n\n* `/requirements.php` を `/web/requirements.php` としてコピーし、ブラウザを使って URL `http://localhost/requirements.php` にアクセスする。\n* 次のコマンドを実行する。\n\n  ```bash\n  cd basic\n  php requirements.php\n  ```\n\nYii の最低必要条件を満たすように PHP のインストールを構成しなければなりません。\n最も重要なことは、PHP 5.4 以上でなければならないということです。最新の PHP 7 なら理想的です。\nまた、アプリケーションがデータベースを必要とする場合は、[PDO PHP 拡張](https://www.php.net/manual/ja/pdo.installation.php) および対応するデータベース・ドライバ (MySQL データベースのための `pdo_mysql` など) をインストールしなければなりません。\n\n\nウェブ・サーバを構成する <span id=\"configuring-web-servers\"></span>\n------------------------\n\n> Info: もし Yii の試運転をしているだけで、本番サーバに配備する意図がないのであれば、\n  当面、この項は飛ばしても構いません。\n\n上記の説明に従ってインストールされたアプリケーションは、[Apache HTTP サーバ](https://httpd.apache.org/)\nと [Nginx HTTP サーバ](https://nginx.org/) のどちらでも、また、Windows、Mac OS X、Linux のどれでも、\nPHP 5.4 以上を走らせている環境であれば、そのままの状態で動作するはずです。\nYii 2.0 は、また、facebook の [HHVM](https://hhvm.com/) とも互換性があります。\nただし HHVM がネイティブの PHP とは異なる振舞いをする特殊なケースもいくつかありますので、HHVM を使うときはいくらか余分に注意を払う必要があります。\n\n本番用のサーバでは、`https://www.example.com/basic/web/index.php` の代りに `https://www.example.com/index.php` という\nURL でアプリケーションにアクセス出来るようにウェブ・サーバを設定したいでしょう。\nそういう設定をするためには、ウェブ・サーバのドキュメント・ルートを `basic/web` フォルダに向けることが必要になります。\nまた、[ルーティングと URL 生成](runtime-routing.md) のセクションで述べられているように、URL から `index.php` を隠したいとも思うでしょう。\nこの項では、これらの目的を達するために Apache または Nginx サーバをどのように設定すれば良いかを学びます。\n\n> Info: `basic/web` をドキュメント・ルートに設定することは、`basic/web` の兄弟ディレクトリに保存されたプライベートなアプリケーション・コードや\n公開できないデータ・ファイルにエンド・ユーザがアクセスすることを防止することにもなります。\n`basic/web` 以外のフォルダに対するアクセスを拒否することはセキュリティ強化の一つです。\n\n> Info: あなたがウェブ・サーバの設定を修正する権限を持たない共用ホスティング環境でアプリケーションが走る場合であっても、\nセキュリティ強化のためにアプリケーションの構造を調整することがまだ出来ます。\n詳細については、[共有ホスティング環境](tutorial-shared-hosting.md) のセクションを参照してください。\n\n> Info: あなたのアプリケーションをリバース・プロキシの背後で動かそうとする場合は、\n> リクエスト・コンポーネントの [信頼できるプロキシとヘッダ](runtime-requests.md#trusted-proxies) を構成する必要があるかもしれません。\n\n### 推奨される Apache の構成 <span id=\"recommended-apache-configuration\"></span>\n\n下記の設定を Apache の `httpd.conf` ファイルまたはバーチャル・ホスト設定の中で使います。\n`path/to/basic/web` の部分を `basic/web` の実際のパスに置き換えなければならないことに注意してください。\n\n```apache\n# ドキュメント・ルートを \"basic/web\" に設定\nDocumentRoot \"path/to/basic/web\"\n\n<Directory \"path/to/basic/web\">\n    # 綺麗な URL をサポートするために mod_rewrite を使う\n    RewriteEngine on\n\n    # UrlManager の $showScriptName が false の場合は、スクリプト名で URL にアクセスすることを許さない\n    RewriteRule ^index.php/ - [L,R=404]\n\n    # ディレクトリかファイルが存在する場合は、リクエストをそのまま通す\n    RewriteCond %{REQUEST_FILENAME} !-f\n    RewriteCond %{REQUEST_FILENAME} !-d\n\n    # そうでなければ、リクエストを index.php に送付する\n    RewriteRule . index.php\n\n    # ... 他の設定 ...\n</Directory>\n```\n\n\n### 推奨される Nginx の構成 <span id=\"recommended-nginx-configuration\"></span>\n\n[Nginx](https://www.nginx.com/resources/wiki/) を使うためには、PHP を [FPM SAPI](https://www.php.net/manual/ja/install.fpm.php) としてインストールしなければなりません。\n下記の Nginx の設定を使うことができます。\n`path/to/basic/web` の部分を `basic/web` の実際のパスに置き換え、`mysite.test` を実際のサーバのホスト名に置き換えてください。\n\n```nginx\nserver {\n    charset utf-8;\n    client_max_body_size 128M;\n\n    listen 80; ## listen for ipv4\n    #listen [::]:80 default_server ipv6only=on; ## listen for ipv6\n\n    server_name mysite.test;\n    root        /path/to/basic/web;\n    index       index.php;\n\n    access_log  /path/to/basic/log/access.log;\n    error_log   /path/to/basic/log/error.log;\n\n    location / {\n        # 本当のファイルでないものは全て index.php にリダイレクト\n        try_files $uri $uri/ /index.php$is_args$args;\n    }\n\n    # 存在しない静的ファイルの呼び出しを Yii に処理させたくない場合はコメントを外す\n    #location ~ \\.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ {\n    #    try_files $uri =404;\n    #}\n    #error_page 404 /404.html;\n\n    # /assets ディレクトリの php ファイルへのアクセスを拒否する\n    location ~ ^/assets/.*\\.php$ {\n        deny all;\n    }\n\n    location ~ \\.php$ {\n        include fastcgi_params;\n        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;\n        fastcgi_pass 127.0.0.1:9000;\n        #fastcgi_pass unix:/var/run/php5-fpm.sock;\n        try_files $uri =404;\n    }\n\n    location ~* /\\. {\n        deny all;\n    }\n}\n```\n\nこの構成を使う場合は、多数の不要な `stat()` システム・コールを避けるために、\n`php.ini` ファイルで `cgi.fix_pathinfo=0` を同時に設定しておくべきです。\n\nまた、HTTPS サーバを走らせている場合には、安全な接続であることを Yii が正しく検知できるように、\n`fastcgi_param HTTPS on;` を追加しなければならないことにも注意を払ってください。\n\n### 推奨される NGINX Unit の構成<span id=\"recommended-nginx-unit-configuration\"></span>\n\n[NGINX Unit](https://unit.nginx.org/) と PHP 言語モジュールを使って Yii ベースのアプリを走らせることが出来ます。\nその構成のサンプルです。\n\n```json\n{\n    \"listeners\": {\n        \"*:80\": {\n            \"pass\": \"routes/yii\"\n        }\n    },\n\n    \"routes\": {\n        \"yii\": [\n            {\n                \"match\": {\n                    \"uri\": [\n                        \"!/assets/*\",\n                        \"*.php\",\n                        \"*.php/*\"\n                    ]\n                },\n\n                \"action\": {\n                    \"pass\": \"applications/yii/direct\"\n                }\n            },\n            {\n                \"action\": {\n                    \"share\": \"/path/to/app/web/\",\n                    \"fallback\": {\n                        \"pass\": \"applications/yii/index\"\n                    }\n                }\n            }\n        ]\n    },\n\n    \"applications\": {\n        \"yii\": {\n            \"type\": \"php\",\n            \"user\": \"www-data\",\n            \"targets\": {\n                \"direct\": {\n                    \"root\": \"/path/to/app/web/\"\n                },\n\n                \"index\": {\n                    \"root\": \"/path/to/app/web/\",\n                    \"script\": \"index.php\"\n                }\n            }\n        }\n    }\n}\n```\n\nまた、自分の PHP 環境を [セットアップ](https://unit.nginx.org/configuration/#php) したり、この同じ構成でカスタマイズした `php.ini` を提供したりすることも出来ます。\n\n### IIS の構成 <span id=\"iis-configuration\"></span>\n\nドキュメント・ルートが `path/to/app/web` フォルダを指し、PHP を実行するように構成された仮想ホスト (ウェブ・サイト) でアプリケーションをホストすることを推奨します。その `web` フォルダに `web.config` という名前のファイル、すなわち `path/to/app/web/web.config` を配置しなければなりません。ファイルの内容は以下の通りです。\n\n```xml\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration>\n<system.webServer>\n<directoryBrowse enabled=\"false\" />\n  <rewrite>\n    <rules>\n      <rule name=\"Hide Yii Index\" stopProcessing=\"true\">\n        <match url=\".\" ignoreCase=\"false\" />\n        <conditions>\n        <add input=\"{REQUEST_FILENAME}\" matchType=\"IsFile\" \n              ignoreCase=\"false\" negate=\"true\" />\n        <add input=\"{REQUEST_FILENAME}\" matchType=\"IsDirectory\" \n              ignoreCase=\"false\" negate=\"true\" />\n        </conditions>\n        <action type=\"Rewrite\" url=\"index.php\" appendQueryString=\"true\" />\n      </rule> \n    </rules>\n  </rewrite>\n</system.webServer>\n</configuration>\n```\nまた、IIS 上で PHP を構成するためには、以下にリストした Microsoft の公式リソースが有用でしょう。\n 1. [IIS の最初の Web サイトを構成する方法](https://support.microsoft.com/ja-jp/help/323972/how-to-set-up-your-first-iis-web-site)\n 2. [Configure a PHP Website on IIS](https://docs.microsoft.com/en-us/iis/application-frameworks/scenario-build-a-php-website-on-iis/configure-a-php-website-on-iis)\n"
  },
  {
    "path": "docs/guide-ja/start-looking-ahead.md",
    "content": "先を見通す\n==========\n\n「はじめよう」の章全体を読み通したなら、いまやあなたは、完全な Yii のアプリケーションを作成したことがある、ということになります。\nその過程で、あなたは必要とされることが多いいくつかの機能、例えば、HTML フォームを通じてユーザからデータを取得することや、データベースからデータを取得すること、また、ページ付けをしてデータを表示することなどを実装する方法を学びました。\nまた、[Gii](https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide) を使ってコードを自動的に生成する方法も学びました。\nGii をコード生成に使うと、ウェブ開発のプロセスの大部分が、いくつかのフォームに入力していくだけの簡単な仕事になります。\n\nこのセクションでは、Yii フレームワークを使うときの生産性を更に高めるために利用できるリソースについてまとめます。\n\n* ドキュメント\n    - [決定版ガイド](https://www.yiiframework.com/doc-2.0/guide-README.html):\n      名前が示すように、このガイドは Yii がどのように動作すべきものかを正確に記述し、Yii を使用するについての全般的な手引きを提供するものです。\n      これは唯一の最も重要な Yii のチュートリアルであり、Yii のコードを少しでも書く前にあなたが読まなければならないものです。\n    - [クラス・リファレンス](https://www.yiiframework.com/doc-2.0/index.html):\n      これは Yii によって提供される全てのクラスの使用法を記述しています。\n      主として、コードを書いている時に、特定のクラス、メソッド、プロパティについて理解したい場合に読まれるべきものです。\n      クラス・リファレンスの使用は、フレームワーク全体の文脈的な理解が出来てからにするのが最善です。\n    - [Wiki の記事](https://www.yiiframework.com/wiki/?tag=yii2):\n      Wiki の記事は、Yii のユーザが自身の経験に基づいて書いたものです。\n      ほとんどの記事は、料理本のレシピのように書かれており、特定の問題を Yii を使って解決する方法を示しています。\n      これらの記事の品質は決定版ガイドほどには良くないかもしれませんが、より広範なトピックをカバーしていることと、たいていは即座に使えるソリューションを提供してくれることにおいて有用なものです。\n    - [書籍](https://www.yiiframework.com/books/)\n* [エクステンション](https://www.yiiframework.com/extensions/):\n  Yii は、ユーザによって作られた数千におよぶエクステンションのライブラリを誇りとしています。\n  エクステンションはあなたのアプリケーションに簡単に組み込むことが出来、そうすることでアプリケーションの開発作業をより一層速くて簡単なものにします。\n* コミュニティ\n    - フォーラム: <https://forum.yiiframework.com/>\n    - IRC チャット: Libera ネットワーク (<ircs://irc.libera.chat:6697/yii>) の #yii チャンネル\n    - Slack チャンネル:  <https://join.slack.com/t/yii/shared_invite/enQtMzQ4MDExMDcyNTk2LTc0NDQ2ZTZhNjkzZDgwYjE4YjZlNGQxZjFmZDBjZTU3NjViMDE4ZTMxNDRkZjVlNmM1ZTA1ODVmZGUwY2U3NDA>\n    - Gitter チャット: <https://gitter.im/yiisoft/yii2>\n    - GitHub: <https://github.com/yiisoft/yii2>\n    - Facebook: <https://www.facebook.com/groups/yiitalk/>\n    - Twitter: <https://twitter.com/yiiframework>\n    - LinkedIn: <https://www.linkedin.com/groups/yii-framework-1483367>\n    - Stackoverflow: <https://stackoverflow.com/questions/tagged/yii2>\n"
  },
  {
    "path": "docs/guide-ja/start-prerequisites.md",
    "content": "# 何を知っている必要があるか\n\nYii の学習曲線は他の PHP フレームワークほど急峻ではありませんが、それでも Yii を使い始める前に学習すべき事がいくつかはあります。\n\n## PHP\n\nYii は PHP フレームワークですから、必ず [言語リファレンスを読んで理解する](https://www.php.net/manual/ja/langref.php) ようにして下さい。\nYii を使って開発するときはオブジェクト指向の流儀でコードを書くことになりますから、必ず、[クラスとオブジェクト](https://www.php.net/manual/ja/language.oop5.basic.php) および [名前空間](https://www.php.net/manual/ja/language.namespaces.php) に慣れ親しんでおいて下さい。\n\n## オブジェクト指向プログラミング\n\nオブジェクト指向プログラミングの基礎的な理解が必要です。これに慣れていない場合は、利用できるチュートリアルが沢山ありますので、その中の一つをチェックして下さい。\n例えば、[tuts+ の中の一つ](https://code.tutsplus.com/tutorials/object-oriented-php-for-beginners--net-12762) など。\n\nあなたのアプリケーションが複雑であればあるほど、その複雑性をうまく管理するために、\nより高度な OOP 概念を学ぶ必要があることに留意して下さい。\n\n## コマンド・ラインと Composer\n\nYii は業界標準の PHP パッケージ管理ソフトである [Composer](https://getcomposer.org/) を広範囲に使用していますので、必ずその [ガイド](https://getcomposer.org/doc/01-basic-usage.md) を読んで理解して下さい。\nあなたがコマンド・ラインの使用に慣れていないのであれば、今こそ使い始めてみるべき時です。\nいったん基礎さえ学習してしまえば、二度とコマンド・ラインなしで仕事をしようとは思わなくなりますよ。\n\n"
  },
  {
    "path": "docs/guide-ja/start-workflow.md",
    "content": "アプリケーションを走らせる\n==========================\n\nYii のインストールが終ると、実際に動く Yii のアプリケーションにアクセスすることが出来ます。\nその URL は、`https://hostname/basic/web/index.php` あるいは `https://hostname/index.php` など、設定によって異なります。\nこのセクションでは、アプリケーションに組み込み済みの機能を紹介し、コードがどのように編成されているか、\nそして、一般にアプリケーションがリクエストをどのように処理するかを説明します。\n\n> Info: 話を簡単にするために、この「始めよう」のチュートリアルを通じて、`basic/web`\n  をウェブ・サーバのドキュメント・ルートとして設定したと仮定します。\n  そして、アプリケーションにアクセスするための URL は `https://hostname/index.php` またはそれに似たものになるように設定したと仮定します。\n  必要に応じて、説明の中の URL を読み替えてください。\n\nフレームワークそのものとは異なり、プロジェクト・テンプレートはインストール後は完全にあなたのものであることに注意してください。\n必要に応じてコードを追加したり削除したり、完全に書き換えたりするのはあなたの自由です。\n\n\n機能 <span id=\"functionality\"></span>\n----\n\nインストールされたベーシック・アプリケーションは四つのページを持っています。\n\n* ホームページ: `https://hostname/index.php` の URL にアクセスすると表示されます。\n* 「について」のページ。\n* 「コンタクト」のページ: エンド・ユーザがメールであなたに連絡を取ることが出来るコンタクト・フォームが表示されます。\n* 「ログイン」ページ: エンド・ユーザを認証するためのログイン・フォームが表示されます。\n  \"admin/admin\" でログインしてみてください。「ログイン」のメイン・メニュー項目が「ログアウト」に変ることに気付くでしょう。\n\nこれらのページは共通のヘッダとフッタを持っています。\nヘッダには、異なるページ間を行き来することを可能にするメイン・メニュー・バーがあります。\n\nブラウザのウィンドウの下部にツールバーがあることにも気がつくはずです。\nこれは Yii によって提供される便利な [デバッグ・ツール](https://github.com/yiisoft/yii2-debug/blob/master/docs/guide-ja/README.md) であり、たくさんのデバッグ情報、例えば、ログ・メッセージ、レスポンスのステータス、実行されたデータベース・クエリなどを記録して表示するものです。\n\nウェブ・アプリケーションに加えて、`yii` というコンソール・スクリプトがアプリケーションのベース・ディレクトリにあります。\nこのスクリプトは、バックグラウンドのタスクまたはメンテナンスのタスクを実行するために使用することが出来ます。\nこれについては、[コンソール・アプリケーションのセクション](tutorial-console.md) で説明されています。\n\n\nアプリケーションの構造 <span id=\"application-structure\"></span>\n----------------------\n\nアプリケーションにとって最も重要なディレクトリとファイルは (アプリケーションのルート・ディレクトリが `basic` だと仮定すると) 以下の通りです。\n\n```\nbasic/                  アプリケーションのベース・パス\n    composer.json       Composer によって使用される。パッケージ情報を記述\n    config/             アプリケーションその他の構成情報を格納\n        console.php     コンソール・アプリケーションの構成情報\n        web.php         ウェブ・アプリケーションの構成情報\n    commands/           コンソール・コマンドのクラスを格納\n    controllers/        コントローラのクラスを格納\n    models/             モデルのクラスを格納\n    runtime/            実行時に Yii によって生成されるファイル (ログやキャッシュなど) を格納\n    vendor/             インストールされた Composer パッケージ (Yii フレームワークそのものを含む) を格納\n    views/              ビュー・ファイルを格納\n    web/                アプリケーションのウェブ・ルート。ウェブ・アクセス可能なファイルを格納\n        assets/         Yii によって発行されるアセット・ファイル (javascript と CSS) を格納\n        index.php       アプリケーションのエントリ・スクリプト (ブートストラップ・スクリプト)\n    yii                 Yii コンソール・コマンド実行スクリプト\n```\n\n一般に、アプリケーションのファイルは二種類に分けることが出来ます。すなわち、`basic/web` の下にあるファイルとその他のディレクトリの下にあるファイルです。\n前者は HTTP で (すなわちブラウザで) 直接にアクセスすることが出来ますが、後者は直接のアクセスは出来ませんし、許可すべきでもありません。\n\nYii は [モデル・ビュー・コントローラ (MVC)](https://wikipedia.org/wiki/Model-view-controller) アーキテクチャ・パターンを実装していますが、\nそれが上記のディレクトリ構成にも反映されています。\n`models` ディレクトリが全ての [モデル・クラス](structure-models.md) を格納し、`views` ディレクトリが全ての [ビュー・スクリプト](structure-views.md) を格納し、\n`controllers` ディレクトリが全ての [コントローラ・クラス](structure-controllers.md) を格納しています。\n\n次の図がアプリケーションの静的な構造を示すものです。\n\n![アプリケーションの静的な構造](images/application-structure.png)\n\n各アプリケーションは一つのエントリ・スクリプト `web/index.php` を持ちます。\nこれはアプリケーション中で唯一ウェブ・アクセス可能な PHP スクリプトです。\nエントリ・スクリプトは入力されたリクエストを受け取って、[アプリケーション](structure-applications.md) のインスタンスを作成します。\n[アプリケーション](structure-applications.md) は [コンポーネント](concept-components.md) の助力を得てリクエストを解決し、リクエストを MVC に送付します。\n[ウィジェット](structure-widgets.md) は、複雑で動的なユーザ・インタフェイス要素を構築するために、[ビュー](structure-views.md) の中で使われます。\n\n\nリクエストのライフサイクル <span id=\"request-lifecycle\"></span>\n--------------------------\n\n次の図は、アプリケーションがどのようにリクエストを処理するかを示すものです。\n\n![リクエストのライフサイクル](images/request-lifecycle.png)\n\n1. ユーザが [エントリ・スクリプト](structure-entry-scripts.md) `web/index.php` に対してリクエストを出します。\n2. エントリ・スクリプトはアプリケーションの [構成情報](concept-configurations.md) を読み出して、\n  リクエストを処理する [アプリケーション](structure-applications.md) のインスタンスを作成します。\n3. アプリケーションは、[リクエスト](runtime-requests.md) アプリケーション・コンポーネントの助力を得て、\n  リクエストされた [ルート](runtime-routing.md) を解決します。\n4. アプリケーションがリクエストを処理する [コントローラ](structure-controllers.md) のインスタンスを作成します。\n5. コントローラが [アクション](structure-controllers.md) のインスタンスを作成し、アクションのためのフィルタを実行します。\n6. 一つでもフィルタが失敗したときは、アクションはキャンセルされます。\n7. すべてのフィルタを通ったとき、アクションが実行されます。\n8. アクションはデータ・モデルを、おそらくはデータベースから、読み出します。\n9. アクションはデータ・モデルをビューに提供して、ビューをレンダリングします。\n10. レンダリング結果が [レスポンス](runtime-responses.md) アプリケーション・コンポーネントに返されます。\n11. レスポンス・コンポーネントがレンダリング結果をユーザのブラウザに送信します。\n\n"
  },
  {
    "path": "docs/guide-ja/structure-application-components.md",
    "content": "アプリケーション・コンポーネント\n================================\n\nアプリケーションは [サービス・ロケータ](concept-service-locator.md) です。\nアプリケーションは、リクエストを処理するためのいろいろなサービスを提供する一組の *アプリケーション・コンポーネント* と呼ばれるものをホストします。\n例えば、`urlManager` がウェブ・リクエストを適切なコントローラにルーティングする役割を負い、\n`db` コンポーネントが DB 関連のサービスを提供する、等々です。\n\n全てのアプリケーション・コンポーネントは、それぞれ、同一のアプリケーション内で他のアプリケーション・コンポーネントから区別できるように、ユニークな ID を持ちます。\nアプリケーション・コンポーネントには、次の式によってアクセス出来ます。\n\n```php\n\\Yii::$app->componentID\n```\n\n例えば、`\\Yii::$app->db` を使って、アプリケーションに登録された [[yii\\db\\Connection|DB 接続]] を取得することが出来ます。\nまた、`\\Yii::$app->cache` を使って、[[yii\\caching\\Cache|プライマリ・キャッシュ]] を取得できます。\n\nアプリケーション・コンポーネントは、上記の式を使ってアクセスされた最初の時に作成されます。\n二度目以降のアクセスでは、同じコンポーネント・インスタンスが返されます。\n\nどのようなオブジェクトでも、アプリケーション・コンポーネントとすることが可能です。\n[アプリケーションの構成情報](structure-applications.md#application-configurations) の中で [[yii\\base\\Application::components]] プロパティを構成することによって、アプリケーション・コンポーネントを登録することが出来ます。\n例えば、\n\n```php\n[\n    'components' => [\n        // クラス名を使って \"cache\" コンポーネントを登録\n        'cache' => 'yii\\caching\\ApcCache',\n\n        // 構成情報の配列を使って \"db\" コンポーネントを登録\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=demo',\n            'username' => 'root',\n            'password' => '',\n        ],\n\n        // 無名関数を使って \"search\" コンポーネントを登録\n        'search' => function () {\n            return new app\\components\\SolrService;\n        },\n    ],\n]\n```\n\n> Info: 必要なだけ多くのアプリケーション・コンポーネントを登録することが出来ますが、慎重にしなければなりません。\n  アプリケーション・コンポーネントはグローバル変数のようなものです。\n  あまり多くのアプリケーション・コンポーネントを使うと、コードのテストと保守が困難になるおそれがあります。\n  多くの場合、必要なときにローカルなコンポーネントを作成して使用するだけで十分です。\n\n\n## コンポーネントをブートストラップに含める <span id=\"bootstrapping-components\"></span>\n\n上述のように、アプリケーション・コンポーネントは最初にアクセスされた時に初めてインスタンスが作成されます。\nリクエストの間に全くアクセスされなかった時は、インスタンスは作成されません。\nけれども、場合によっては、明示的にアクセスされないときでも、リクエストごとにアプリケーション・コンポーネントのインスタンスを作成したいことがあります。\nそうするために、アプリケーションの [[yii\\base\\Application::bootstrap|bootstrap]] プロパティのリストにそのコンポーネントの ID を挙げることが出来ます。\n\nまた、カスタマイズされたコンポーネントをブートストラップするためにクロージャを用いることも出来ます。インスタンス化されたコンポーネントを返すことは要求されません。\n単に [[yii\\base\\Application]] のインスタンス化の後にコードを走らせるだけのためにクロージャを使うことも出来ます。\n\n例えば、次のアプリケーション構成情報は、`log` コンポーネントが常にロードされることを保証するものです。\n\n```php\n[\n    'bootstrap' => [\n        'log',\n        function($app){\n            return new ComponentX();\n        },\n        function($app){\n            // 何らかのコード\n           return;\n        }\n    ],\n    'components' => [\n        'log' => [\n            // \"log\" コンポーネントの構成情報\n        ],\n    ],\n]\n```\n\n\n## コア・アプリケーション・コンポーネント <span id=\"core-application-components\"></span>\n\nYii は固定の ID とデフォルトの構成情報を持つ一連の *コア*・アプリケーション・コンポーネントを定義しています。\n例えば、[[yii\\web\\Application::request|request]] コンポーネントは、ユーザ・リクエストに関する情報を収集して、\nそれを [ルート](runtime-routing.md) として解決するために使用されます。\nまた、[[yii\\base\\Application::db|db]] コンポーネントは、それを通じてデータ・ベースクエリを実行できるデータベース接続を表現します。\nYii のアプリケーションがユーザ・リクエストを処理出来るのは、まさにこれらのコア・アプリケーション・コンポーネントの助けがあってこそです。\n\n下記が事前に定義されたコア・アプリケーション・コンポーネントです。\n通常のアプリケーション・コンポーネントに対するのと同様に、これらを構成し、カスタマイズすることが出来ます。\nコア・アプリケーション・コンポーネントを構成するときは、クラスを指定しない場合は、デフォルトのクラスが使用されます。\n\n* [[yii\\web\\AssetManager|assetManager]]: アセット・バンドルとアセットの発行を管理します。\n  詳細は [アセット](structure-assets.md) のセクションを参照してください。\n* [[yii\\db\\Connection|db]]: データベース接続を表します。これを通じて、DB クエリを実行することが出来ます。\n  このコンポーネントを構成するときは、コンポーネントのクラスはもちろん、[[yii\\db\\Connection::dsn]]\n  のような必須のコンポーネント・プロパティを指定しなければならないことに注意してください。\n  詳細は [データベース・アクセス・オブジェクト](db-dao.md) のセクションを参照してください。\n* [[yii\\base\\Application::errorHandler|errorHandler]]: PHP のエラーと例外を処理します。\n  詳細は [エラー処理](runtime-handling-errors.md) のセクションを参照してください。\n* [[yii\\i18n\\Formatter|formatter]]: エンド・ユーザに表示されるデータに書式を設定します。\n  例えば、数字が3桁ごとの区切りを使って表示されたり、日付が長い書式で表示されたりします。\n  詳細は [データの書式設定](output-formatting.md) のセクションを参照してください。\n* [[yii\\i18n\\I18N|i18n]]: メッセージの翻訳と書式設定をサポートします。\n  詳細は [国際化](tutorial-i18n.md) のセクションを参照してください。\n* [[yii\\log\\Dispatcher|log]]: ログ・ターゲットを管理します。\n  詳細は [ロギング](runtime-logging.md) のセクションを参照してください。\n* [[yii\\swiftmailer\\Mailer|mailer]]: メールの作成と送信をサポートします。\n  詳細は [メール](tutorial-mailing.md) のセクションを参照してください。\n* [[yii\\base\\Application::response|response]]: エンド・ユーザに送信されるレスポンスを表現します。\n  詳細は [レスポンス](runtime-responses.md) のセクションを参照してください。\n* [[yii\\base\\Application::request|request]]: エンド・ユーザから受信したリクエストを表現します。\n  詳細は [リクエスト](runtime-requests.md) のセクションを参照してください。\n* [[yii\\web\\Session|session]]: セッション情報を表現します。\n  このコンポーネントは、[[yii\\web\\Application|ウェブ・アプリケーション]] においてのみ利用できます。\n  詳細は [セッションとクッキー](runtime-sessions-cookies.md) のセクションを参照してください。\n* [[yii\\web\\UrlManager|urlManager]]: URL の解析と生成をサポートします。\n  詳細は [ルーティング と URL 生成](runtime-routing.md) のセクションを参照してください。\n* [[yii\\web\\User|user]]: ユーザの認証情報を表現します。\n  このコンポーネントは、[[yii\\web\\Application|ウェブ・アプリケーション]] においてのみ利用できます。\n  詳細は [認証](security-authentication.md) のセクションを参照してください。\n* [[yii\\web\\View|view]]: ビューのレンダリングをサポートします。\n  詳細は [ビュー](structure-views.md) のセクションを参照してください。\n"
  },
  {
    "path": "docs/guide-ja/structure-applications.md",
    "content": "アプリケーション\n================\n\nアプリケーションは Yii アプリケーション・システム全体の構造とライフサイクルを統制するオブジェクトです。\n全ての Yii アプリケーション・システムは、それぞれ、単一のアプリケーション・オブジェクトを持ちます。\nアプリケーション・オブジェクトは、[エントリ・スクリプト](structure-entry-scripts.md) において作成され、`\\Yii::$app` という式でグローバルにアクセスすることが出来るオブジェクトです。\n\n> Info: ガイドの中で「アプリケーション」という言葉は、文脈に応じて、アプリケーション・オブジェクトを意味したり、\n  アプリケーション・システムを意味したりします。\n\n二種類のアプリケーション、すなわち、[[yii\\web\\Application|ウェブ・アプリケーション]] と\n[[yii\\console\\Application|コンソール・アプリケーション]] があります。\n名前が示すように、前者は主にウェブのリクエストを処理し、後者はコンソール・コマンドのリクエストを処理します。\n\n\n## アプリケーションの構成情報 <span id=\"application-configurations\"></span>\n\n[エントリ・スクリプト](structure-entry-scripts.md) は、アプリケーションを作成するときに、下記のように、\n[構成情報](concept-configurations.md) を読み込んで、それをアプリケーションに適用します。\n\n```php\nrequire __DIR__ . '/../vendor/autoload.php';\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n\n// アプリケーションの構成情報を読み込む\n$config = require __DIR__ . '/../config/web.php';\n\n// アプリケーションのインスタンスを作成し、構成情報を適用する\n(new yii\\web\\Application($config))->run();\n```\n\n通常の [構成情報](concept-configurations.md) と同じように、アプリケーションの構成情報は、\nアプリケーション・オブジェクトのプロパティをどのように初期化するかを指定するものです。\nアプリケーションの構成情報は、たいていは非常に複雑なものですから、通常は、上記の例の `web.php` ファイルのように、\n[構成情報ファイル](concept-configurations.md#configuration-files) に保管されます。\n\n\n## アプリケーションのプロパティ <span id=\"application-properties\"></span>\n\nアプリケーションの構成情報で構成すべき重要なアプリケーションのプロパティは数多くあります。\nそれらのプロパティの典型的なものは、アプリケーションが走る環境を記述するものです。\n例えば、アプリケーションは、どのようにして [コントローラ](structure-controllers.md) をロードするか、また、どこにテンポラリフ・ァイルを保存するかなどを知らなければなりません。\n以下において、それらのプロパティを要約します。\n\n\n### 必須のプロパティ <span id=\"required-properties\"></span>\n\nどのアプリケーションでも、最低二つのプロパティは構成しなければなりません。\nすなわち、[[yii\\base\\Application::id|id]] と [[yii\\base\\Application::basePath|basePath]] です。\n\n\n#### [[yii\\base\\Application::id|id]] <span id=\"id\"></span>\n\n[[yii\\base\\Application::id|id]] プロパティは、アプリケーションを他のアプリケーションから区別するユニークな ID を指定するものです。\nこのプロパティは主としてプログラム内部で使われます。\n必須ではありませんが、最良の相互運用性を確保するために、アプリケーション ID を指定するときには英数字だけを使うことが推奨されます。\n\n\n#### [[yii\\base\\Application::basePath|basePath]] <span id=\"basePath\"></span>\n\n[[yii\\base\\Application::basePath|basePath]] プロパティは、アプリケーションのルート・ディレクトリを指定するものです。\nこれは、アプリケーション・システムの全ての保護されたソース・コードを収容するディレクトリです。\n通常、このディレクトリの下に、MVC パターンに対応するソース・コードを収容した `models`、`views`、`controllers`\nなどのサブ・ディレクトリがあります。\n\n[[yii\\base\\Application::basePath|basePath]] プロパティの構成には、ディレクトリ・パスを使っても、[パス・エイリアス](concept-aliases.md) を使っても構いません。\nどちらの形式においても、対応するディレクトリが存在しなければなりません。\nさもなくば、例外が投げられます。パスは `realpath()` 関数を呼び出して正規化されます。\n\n[[yii\\base\\Application::basePath|basePath]] プロパティは、しばしば、他の重要なパス (例えば、runtime のパス) を派生させるために使われます。\nこのため、`basePath` を示す `@app` というパス・エイリアスが、あらかじめ定義されています。\nその結果、派生的なパスはこのエイリアスを使って形成することが出来ます(例えば、runtime ディレクトリを示す `@app/runtime` など)。\n\n\n### 重要なプロパティ<span id=\"important-properties\"></span>\n\nこの項で説明するプロパティは、アプリケーションごとに異なるものであるため、\n構成する必要がよく生じるものです。\n\n\n#### [[yii\\base\\Application::aliases|aliases]] <span id=\"aliases\"></span>\n\nこのプロパティを使って、配列形式で一連の [エイリアス](concept-aliases.md) を定義することが出来ます。\n配列のキーがエイリアスの名前であり、配列の値が対応するパスの定義です。\n例えば、\n\n```php\n[\n    'aliases' => [\n        '@name1' => 'path/to/path1',\n        '@name2' => 'path/to/path2',\n    ],\n]\n```\n\nこのプロパティが提供されているのは、[[Yii::setAlias()]] メソッドを呼び出す代りに、\nアプリケーションの構成情報を使ってエイリアスを定義することが出来るようにするためです。\n\n\n#### [[yii\\base\\Application::bootstrap|bootstrap]] <span id=\"bootstrap\"></span>\n\nこれは非常に有用なプロパティです。\nこれによって、アプリケーションの [[yii\\base\\Application::bootstrap()|ブートストラップの過程]] において走らせるべきコンポーネントを配列として指定することが出来ます。\n例えば、ある [モジュール](structure-modules.md) に [URL 規則](runtime-routing.md) をカスタマイズさせたいときに、\nモジュールの ID をこのプロパティの要素として挙げることが出来ます。\n\nこのプロパティにリストする各コンポーネントは、以下の形式のいずれかによって指定することが出来ます。\n\n- [components](#components) によって指定されているアプリケーション・コンポーネントの ID\n- [modules](#modules) によって指定されているモジュールの ID\n- クラス名\n- 構成情報の配列\n- コンポーネントを作成して返す無名関数\n\n例えば、\n\n```php\n[\n    'bootstrap' => [\n        // アプリケーション・コンポーネント ID、または、モジュール ID\n        'demo',\n\n        // クラス名\n        'app\\components\\Profiler',\n\n        // 構成情報の配列\n        [\n            'class' => 'app\\components\\Profiler',\n            'level' => 3,\n        ],\n\n        // 無名関数\n        function () {\n            return new app\\components\\Profiler();\n        }\n    ],\n]\n```\n\n> Info: モジュール ID と同じ ID のアプリケーション・コンポーネントがある場合は、\n> ブートストラップの過程ではアプリケーション・コンポーネントが使われます。\n> 代りにモジュールを使いたいときは、次のように、無名関数を使って指定することが出来ます。\n>\n> ```php\n> [\n>     function () {\n>         return Yii::$app->getModule('user');\n>     },\n> ]\n> ```\n\n\nブートストラップの過程で、各コンポーネントのインスタンスが作成されます。\nそして、コンポーネント・クラスが [[yii\\base\\BootstrapInterface]] を実装している場合は、\nその [[yii\\base\\BootstrapInterface::bootstrap()|bootstrap()]] メソッドも呼び出されます。\n\nもう一つの実用的な例が [ベーシック・プロジェクト・テンプレート](start-installation.md) のアプリケーションの構成情報の中にあります。\nそこでは、アプリケーションが開発環境で走るときには `debug` モジュールと `gii` モジュールが\nブートストラップ・コンポーネントとして構成されています。\n\n```php\nif (YII_ENV_DEV) {\n    // 'dev' 環境のための構成情報の修正\n    $config['bootstrap'][] = 'debug';\n    $config['modules']['debug'] = 'yii\\debug\\Module';\n\n    $config['bootstrap'][] = 'gii';\n    $config['modules']['gii'] = 'yii\\gii\\Module';\n}\n```\n\n> Note: あまり多くのコンポーネントを `bootstrap` に置くと、アプリケーションのパフォーマンスを劣化させます。\n  なぜなら、リクエストごとに同じ一連のコンポーネントを走らせなければならないからです。ですから、ブートストラップ・コンポーネントは賢く使ってください。\n\n\n#### [[yii\\web\\Application::catchAll|catchAll]] <span id=\"catchAll\"></span>\n\nこのプロパティは [[yii\\web\\Application|ウェブ・アプリケーション]] においてのみサポートされます。\nこれは、全てのユーザ・リクエストを処理すべき [コントローラ・アクション](structure-controllers.md) を指定するものです。\nこれは主としてアプリケーションがメンテナンス・モードにあって、入ってくる全てのリクエストを単一のアクションで処理する必要があるときに使われます。\n\n構成情報は配列の形を取り、最初の要素はアクションのルートを指定します。\nそして、配列の残りの要素 (キー・値のペア) は、アクションに渡されるパラメータを指定します。例えば、\n\n```php\n[\n    'catchAll' => [\n        'offline/notice',\n        'param1' => 'value1',\n        'param2' => 'value2',\n    ],\n]\n```\n\n> Info: このプロパティを有効にすると、開発環境でデバッグ・パネルが動作しなくなります。\n\n#### [[yii\\base\\Application::components|components]] <span id=\"components\"></span>\n\nこれこそが、唯一の最も重要なプロパティです。\nこれによって、[アプリケーション・コンポーネント](structure-application-components.md) と呼ばれる一連の名前付きのコンポーネントを登録して、それらを他の場所で使うことが出来るようになります。例えば、\n\n```php\n[\n    'components' => [\n        'cache' => [\n            'class' => 'yii\\caching\\FileCache',\n        ],\n        'user' => [\n            'identityClass' => 'app\\models\\User',\n            'enableAutoLogin' => true,\n        ],\n    ],\n]\n```\n\n全てのアプリケーション・コンポーネントは、それぞれ、配列の中で「キー・値」のペアとして指定されます。\nキーはコンポーネントの ID を示し、値はコンポーネントのクラス名または [構成情報](concept-configurations.md) を示します。\n\nどのようなコンポーネントでもアプリケーションに登録することが出来ます。\nそして登録されたコンポーネントは、後で、`\\Yii::$app->componentID` という式を使ってグローバルにアクセスすることが出来ます。\n\n詳細は [アプリケーション・コンポーネント](structure-application-components.md) のセクションを読んでください。\n\n\n#### [[yii\\base\\Application::controllerMap|controllerMap]] <span id=\"controllerMap\"></span>\n\nこのプロパティは、コントローラ ID を任意のコントローラ・クラスに割り付けることを可能にするものです。\nデフォルトでは、Yii は [規約](#controllerNamespace) に基づいてコントローラ ID をコントローラ・クラスに割り付けます\n(例えば、`post` という ID は `app\\controllers\\PostController` に割り付けられます)。\nこのプロパティを構成することによって、特定のコントローラに対する規約を破ることが出来ます。\n下記の例では、`account` は `app\\controllers\\UserController` に割り付けられ、`article` は `app\\controllers\\PostController` に割り付けられることになります。\n\n```php\n[\n    'controllerMap' => [\n        'account' => 'app\\controllers\\UserController',\n        'article' => [\n            'class' => 'app\\controllers\\PostController',\n            'enableCsrfValidation' => false,\n        ],\n    ],\n]\n```\n\nこのプロパティの配列のキーはコントローラ ID を表し、配列の値は対応するコントローラ・クラスの名前または\n[構成情報](concept-configurations.md) を表します。\n\n\n#### [[yii\\base\\Application::controllerNamespace|controllerNamespace]] <span id=\"controllerNamespace\"></span>\n\nこのプロパティは、コントローラ・クラスが配置されるべきデフォルトの名前空間を指定するものです。\nデフォルト値は `app\\controllers` です。\nコントローラ ID が `post` である場合、規約によって対応するコントローラの (名前空間を略した) クラス名は `PostController` となり、完全修飾クラス名は `app\\controllers\\PostController` となります。\n\nコントローラ・クラスは、この名前空間に対応するディレクトリのサブ・ディレクトリに配置されても構いません。\n例えば、コントローラ ID として `admin/post` を仮定すると、対応するコントローラの完全修飾クラス名は\n`app\\controllers\\admin\\PostController` となります。\n\n重要なことは、完全修飾されたコントローラ・クラスが [オートロード可能](concept-autoloading.md) でなければならず、\nコントローラ・クラスの実際の名前空間がこのプロパティと合致していなければならない、ということです。\nそうでないと、アプリケーションにアクセスしたときに \"ページがみつかりません\" というエラーを受け取ることになります。\n\n上で説明された規約を破りたい場合は、\n[controllerMap](#controllerMap) プロパティを構成することが出来ます。\n\n\n#### [[yii\\base\\Application::language|language]] <span id=\"language\"></span>\n\nこのプロパティは、アプリケーションがコンテントをエンド・ユーザに表示するときに使うべき言語を指定するものです。\nこのプロパティのデフォルト値は `en` であり、英語を意味します。\nアプリケーションが多言語をサポートする必要があるときは、このプロパティを構成しなければなりません。\n\nこのプロパティの値が、メッセージの翻訳、日付の書式、数字の書式などを含む [国際化](tutorial-i18n.md) のさまざまな側面を決定します。\n例えば、[[yii\\jui\\DatePicker]] ウィジェットは、どの言語でカレンダーを表示すべきか、\nそして日付をどのように書式設定すべきかを、デフォルトでは、\nこのプロパティを使用して決定します。\n\n言語を指定するのには、[IETF 言語タグ](https://ja.wikipedia.org/wiki/IETF%E8%A8%80%E8%AA%9E%E3%82%BF%E3%82%B0) に従うことが推奨されます。\n例えば、`en` は英語を意味しますが、`en-US` はアメリカ合衆国の英語を意味します。\n\nこのプロパティに関する詳細は [国際化](tutorial-i18n.md) のセクションで読むことが出来ます。\n\n\n#### [[yii\\base\\Application::modules|modules]] <span id=\"modules\"></span>\n\nこのプロパティはアプリケーションが含む [モジュール](structure-modules.md) を指定するものです。\n\nこのプロパティは、モジュールの ID をキーとする、モジュールのクラスまたは [構成情報](concept-configurations.md) の配列です。\n例えば、\n\n```php\n[\n    'modules' => [\n        // モジュール・クラスで指定された \"booking\" モジュール\n        'booking' => 'app\\modules\\booking\\BookingModule',\n\n        // 構成情報の配列で指定された \"comment\" モジュール\n        'comment' => [\n            'class' => 'app\\modules\\comment\\CommentModule',\n            'db' => 'db',\n        ],\n    ],\n]\n```\n\n詳細は [モジュール](structure-modules.md) のセクションを参照してください。\n\n\n#### [[yii\\base\\Application::name|name]] <span id=\"name\"></span>\n\nこのプロパティは、エンド・ユーザに対して表示されるアプリケーション名を指定するものです。\n[[yii\\base\\Application::id|id]] プロパティがユニークな値を取らなければならないのとは違って、\nこのプロパティの値は主として表示目的であり、ユニークである必要はありません。\n\nコードのどこにも使わないのであれば、このプロパティは必ずしも構成する必要はありません。\n\n\n#### [[yii\\base\\Application::params|params]] <span id=\"params\"></span>\n\nこのプロパティは、グローバルにアクセス可能なアプリケーション・パラメータの配列を指定するものです。\nコードの中のいたる処でハードコードされた数値や文字列を使う代りに、それらをアプリケーション・パラメータとして一ヶ所で定義し、\n必要な場所ではそのパラメータを使うというのが良いプラクティスです。\n例えば、次のように、サムネール画像のサイズをパラメータとして定義することが出来ます。\n\n```php\n[\n    'params' => [\n        'thumbnail.size' => [128, 128],\n    ],\n]\n```\n\nそして、このサイズの値を使う必要があるコードにおいては、下記のようなコードを使うだけで済ませることが出来ます。\n\n```php\n$size = \\Yii::$app->params['thumbnail.size'];\n$width = \\Yii::$app->params['thumbnail.size'][0];\n```\n\n後でサムネールのサイズを変更すると決めたときは、アプリケーションの構成情報においてのみサイズを修正すればよく、\nこれに依存するコードには少しも触れる必要がありません。\n\n\n#### [[yii\\base\\Application::sourceLanguage|sourceLanguage]] <span id=\"sourceLanguage\"></span>\n\nこのプロパティはアプリケーション・コードが書かれている言語を指定するものです。デフォルト値は `'en-US'`、アメリカ合衆国の英語です。\nあなたのコードのテキストのコンテントが英語以外で書かれているときは、このプロパティを構成しなければなりません。\n\n[language](#language) プロパティと同様に、このプロパティは\n[IETF 言語タグ](https://ja.wikipedia.org/wiki/IETF%E8%A8%80%E8%AA%9E%E3%82%BF%E3%82%B0) に従って構成しなければなりません。\n例えば、`en` は英語を意味しますが、`en-US` はアメリカ合衆国の英語を意味します。\n\nこのプロパティに関する詳細は [国際化](tutorial-i18n.md) のセクションで読むことが出来ます。\n\n\n#### [[yii\\base\\Application::timeZone|timeZone]] <span id=\"timeZone\"></span>\n\nこのプロパティは、PHP ランタイムのデフォルト・タイム・ゾーンを設定する代替手段として提供されています。\nこのプロパティを構成することによって、本質的には PHP 関数 [date_default_timezone_set()](https://www.php.net/manual/ja/function.date-default-timezone-set.php) を呼び出すことになります。\n例えば、\n\n```php\n[\n    'timeZone' => 'Asia/Tokyo',\n]\n```\n\nタイム・ゾーンを設定することの意味合いについては、[日付のフォーマッティングのセクション](output-formatting.md#time-zones) で詳細を参照して下さい。\n\n#### [[yii\\base\\Application::version|version]] <span id=\"version\"></span>\n\nこのプロパティはアプリケーションのバージョンを指定するものです。デフォルト値は `'1.0'` です。\nコードの中で全く使わないのであれば、必ずしも構成する必要はありません。\n\n\n### 有用なプロパティ <span id=\"useful-properties\"></span>\n\nこの項で説明されるプロパティは通常は構成されません。というのは、そのデフォルト値が通常の規約に由来するものであるからです。\nしかしながら、規約を破る必要がある場合には、これらのプロパティを構成することが出来ます。\n\n\n#### [[yii\\base\\Application::charset|charset]] <span id=\"charset\"></span>\n\nこのプロパティはアプリケーションが使う文字セットを指定するものです。\nデフォルト値は `'UTF-8'` であり、多数の非ユニコード・データを使うレガシー・システムを扱っている場合を除けば、たいていのアプリケーションでは、そのままにしておくべきです。\n\n\n#### [[yii\\base\\Application::defaultRoute|defaultRoute]] <span id=\"defaultRoute\"></span>\n\nこのプロパティは、リクエストがルートを指定していないときにアプリケーションが使用すべき [ルート](runtime-routing.md) を指定するものです。\nルートは、チャイルド・モジュール ID、コントローラ ID、および/または アクション ID を構成要素とすることが出来ます。\n例えば、`help`、`post/create`、`admin/post/create` などです。\nアクション ID が与えられていない場合は、[[yii\\base\\Controller::defaultAction]] で指定されるデフォルト値を取ります。\n\n[[yii\\web\\Application|ウェブ・アプリケーション]] では、このプロパティのデフォルト値は `'site'` であり、\nその意味するところは、`SiteController` コントローラとそのデフォルト・アクションが使用されるべきである、ということです。\n結果として、ルートを指定せずにアプリケーションにアクセスすると、`app\\controllers\\SiteController::actionIndex()` の結果が表示されます。\n\n[[yii\\console\\Application|コンソール・アプリケーション]] では、デフォルト値は `'help'` であり、\nコア・コマンドの [[yii\\console\\controllers\\HelpController::actionIndex()]] が使用されるべきであるという意味です。\n結果として、何も引数を与えずに `yii` というコマンドを実行すると、ヘルプ情報が表示されることになります。\n\n\n#### [[yii\\base\\Application::extensions|extensions]] <span id=\"extensions\"></span>\n\nこのプロパティは、アプリケーションにインストールされて使われている [エクステンション](structure-extensions.md) のリストを指定するものです。\nデフォルトでは、`@vendor/yiisoft/extensions.php` というファイルによって返される配列を取ります。\n`extensions.php` は、[Composer](https://getcomposer.org) を使ってエクステンションをインストールすると、自動的に生成され保守されます。\nですから、たいていの場合、このプロパティをあなたが構成する必要はありません。\n\nエクステンションを手作業で保守したいという特殊なケースにおいては、次のようにしてこのプロパティを構成することが出来ます。\n\n```php\n[\n    'extensions' => [\n        [\n            'name' => 'extension name',\n            'version' => 'version number',\n            'bootstrap' => 'BootstrapClassName',  // オプション、構成情報の配列でもよい\n            'alias' => [  // optional\n                '@alias1' => 'to/path1',\n                '@alias2' => 'to/path2',\n            ],\n        ],\n\n        // ... 上記と同じように、更にエクステンションを構成 ...\n\n    ],\n]\n```\n\nご覧のように、このプロパティはエクステンションの仕様を示す配列を取ります。\nそれぞれのエクステンションは、`name` と `version` の要素を含む配列によって指定されます。\nエクステンションが [ブートストラップ](runtime-bootstrapping.md) の過程で走る必要がある場合には、`bootstrap` 要素をブートストラップのクラス名または [構成情報](concept-configurations.md) の配列によって指定することが出来ます。\nまた、エクステンションはいくつかの [エイリアス](concept-aliases.md) を定義することも出来ます。\n\n\n#### [[yii\\base\\Application::layout|layout]] <span id=\"layout\"></span>\n\nこのプロパティは、[ビュー](structure-views.md) をレンダリングするときに使われるべきデフォルトのレイアウトを指定するものです。\nデフォルト値は `'main'` であり、[レイアウト・パス](#layoutPath) の下にある `main.php` というファイルが使われるべきことを意味します。\n[レイアウト・パス](#layoutPath) と [ビュー・パス](#viewPath) の両方がデフォルト値を取る場合、デフォルトのレイアウト・ファイルは\n`@app/views/layouts/main.php` というパス・エイリアスとして表すことが出来ます。\n\n滅多には無いことですが、レイアウトをデフォルトで無効にしたい場合は、このプロパティを `false` として構成することが出来ます。\n\n\n#### [[yii\\base\\Application::layoutPath|layoutPath]] <span id=\"layoutPath\"></span>\n\nこのプロパティは、レイアウト・ファイルが捜されるべきパスを指定するものです。\nデフォルト値は、[ビュー・パス](#viewPath) の下の `layouts` サブ・ディレクトリです。\n[ビュー・パス](#viewPath) がデフォルト値を取る場合、デフォルトのレイアウト・パスは `@app/views/layouts` というパス・エイリアスとして表すことが出来ます。\n\nこのプロパティはディレクトリまたはパス・[エイリアス](concept-aliases.md) として構成することが出来ます。\n\n\n#### [[yii\\base\\Application::runtimePath|runtimePath]] <span id=\"runtimePath\"></span>\n\nこのプロパティは、ログ・ファイルやキャッシュ・ファイルなどの一時的ファイルを生成することが出来るパスを指定するものです。\nデフォルト値は、`@app/runtime` というエイリアスで表現されるディレクトリです。\n\nこのプロパティはディレクトリまたはパス・[エイリアス](concept-aliases.md) として構成することが出来ます。\nランタイムパスは、アプリケーションを実行するプロセスによって書き込みが可能なものでなければならないことに注意してください。\nそして、この下にある一時的ファイルは秘匿を要する情報を含みうるものですので、ランタイム・パスはエンド・ユーザによるアクセスから保護されなければなりません。\n\nこのパスに簡単にアクセスできるように、Yii は `@runtime` というパス・エイリアスを事前に定義しています。\n\n\n#### [[yii\\base\\Application::viewPath|viewPath]] <span id=\"viewPath\"></span>\n\nこのプロパティはビュー・ファイルが配置されるルート・ディレクトリを指定するものです。デフォルト値は、`@app/views` というエイリアスで表現されるディレクトリです。\nこのプロパティはディレクトリまたはパス・[エイリアス](concept-aliases.md) として構成することが出来ます。\n\n\n#### [[yii\\base\\Application::vendorPath|vendorPath]] <span id=\"vendorPath\"></span>\n\nこのプロパティは、[Composer](https://getcomposer.org) によって管理される vendor ディレクトリを指定するものです。\nYii フレームワークを含めて、あなたのアプリケーションによって使われる全てのサード・パーティ・ライブラリを格納するディレクトリです。\nデフォルト値は、`@app/vendor` というエイリアスで表現されるディレクトリです。\n\nこのプロパティはディレクトリまたはパス・[エイリアス](concept-aliases.md) として構成することが出来ます。\nこのプロパティを修正するときは、必ず、Composer の構成もそれに合せて修正してください。\n\nこのパスに簡単にアクセスできるように、Yii は `@vendor` というパス・エイリアスを事前に定義しています。\n\n\n#### [[yii\\console\\Application::enableCoreCommands|enableCoreCommands]] <span id=\"enableCoreCommands\"></span>\n\nこのプロパティは [[yii\\console\\Application|コンソール・アプリケーション]] においてのみサポートされています。\nYii リリースに含まれているコア・コマンドを有効にすべきか否かを指定するものです。デフォルト値は `true` です。\n\n\n## アプリケーションのイベント <span id=\"application-events\"></span>\n\nアプリケーションはリクエストを処理するライフサイクルの中でいくつかのイベントをトリガします。\nこれらのイベントに対して、下記のようにして、アプリケーションの構成情報の中でイベント・ハンドラをアタッチすることが出来ます。\n\n```php\n[\n    'on beforeRequest' => function ($event) {\n        // ...\n    },\n]\n```\n\n`on eventName` という構文の使い方については、[構成情報](concept-configurations.md#configuration-format)\nのセクションで説明されています。\n\n別の方法として、アプリケーションのインスタンスが生成された後、[ブートストラップの過程](runtime-bootstrapping.md) の中でイベント・ハンドラをアタッチすることも出来ます。\n例えば、\n\n```php\n\\Yii::$app->on(\\yii\\base\\Application::EVENT_BEFORE_REQUEST, function ($event) {\n    // ...\n});\n```\n\n### [[yii\\base\\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_REQUEST]] <span id=\"beforeRequest\"></span>\n\nこのイベントは、アプリケーションがリクエストを処理する *前* にトリガされます。実際のイベント名は `beforeRequest` です。\n\nこのイベントがトリガされるときには、アプリケーションのインスタンスは既に構成されて初期化されています。\nですから、イベント・メカニズムを使って、リクエスト処理のプロセスに干渉するカスタム・コードを挿入するのには、ちょうど良い場所です。\n例えば、このイベント・ハンドラの中で、何らかのパラメータに基づいて [[yii\\base\\Application::language]] プロパティを動的にセットすることが出来ます。\n\n\n### [[yii\\base\\Application::EVENT_AFTER_REQUEST|EVENT_AFTER_REQUEST]] <span id=\"afterRequest\"></span>\n\nこのイベントは、アプリケーションがリクエストの処理を完了した *後*、レスポンスを送信する *前* にトリガされます。\n実際のイベント名は `afterRequest` です。\n\nこのイベントがトリガされるときにはリクエストの処理は完了していますので、この機をとらえて、\nリクエストに対する何らかの後処理をしたり、レスポンスをカスタマイズしたりすることが出来ます。\n\n[[yii\\web\\Response|response]] コンポーネントも、エンド・ユーザにレスポンスのコンテントを送出する間にいくつかのイベントをトリガすることに注意してください。\nそれらのイベントは、このイベントの *後* にトリガされます。\n\n\n### [[yii\\base\\Application::EVENT_BEFORE_ACTION|EVENT_BEFORE_ACTION]] <span id=\"beforeAction\"></span>\n\nこのイベントは、[コントローラ・アクション](structure-controllers.md) を実行する *前* に毎回トリガされます。\n実際のイベント名は `beforeAction` です。\n\nイベントのパラメータは [[yii\\base\\ActionEvent]] のインスタンスです。\nイベント・ハンドラは、[[yii\\base\\ActionEvent::isValid]] プロパティを `false` にセットして、アクションの実行を中止することが出来ます。\n例えば、\n\n```php\n[\n    'on beforeAction' => function ($event) {\n        if (何らかの条件) {\n            $event->isValid = false;\n        } else {\n        }\n    },\n]\n```\n\n同じ `beforeAction` イベントが、[モジュール](structure-modules.md) と [コントローラ](structure-controllers.md) からもトリガされることに注意してください。\nアプリケーション・オブジェクトが最初にこのイベントをトリガし、次に (もし有れば) モジュールが、\nそして最後にコントローラがこのイベントをトリガします。\nいずれかのイベント・ハンドラが [[yii\\base\\ActionEvent::isValid]] を `false` にセットすると、後続のイベントはトリガされません。\n\n\n### [[yii\\base\\Application::EVENT_AFTER_ACTION|EVENT_AFTER_ACTION]] <span id=\"afterAction\"></span>\n\nこのイベントは、[コントローラ・アクション](structure-controllers.md) を実行した *後* に毎回トリガされます。\n実際のイベント名は `afterAction` です。\n\nイベントのパラメータは [[yii\\base\\ActionEvent]] のインスタンスです。\n[[yii\\base\\ActionEvent::result]] プロパティを通じて、イベント・ハンドラはアクションの結果にアクセスしたり、またはアクションの結果を修正したり出来ます。\n例えば、\n\n```php\n[\n    'on afterAction' => function ($event) {\n        if (何らかの条件) {\n            // $event->result を修正する\n        } else {\n        }\n    },\n]\n```\n\n同じ `afterAction` イベントが、[モジュール](structure-modules.md) と [コントローラ](structure-controllers.md) からもトリガされることに注意してください。\nこれらのオブジェクトは、`beforeAction` の場合とは逆の順でイベントをトリガします。\nすなわち、コントローラ・オブジェクトが最初にこのイベントをトリガし、次に (もし有れば) モジュールが、\nそして最後にアプリケーションがこのイベントをトリガします。\n\n\n## アプリケーションのライフサイクル<span id=\"application-lifecycle\"></span>\n\n![アプリケーションのライフサイクル](images/application-lifecycle.png)\n\n[エントリ・スクリプト](structure-entry-scripts.md) が実行されて、リクエストが処理されるとき、\nアプリケーションは次のようなライフサイクルを経ます。\n\n1. エントリ・スクリプトがアプリケーションの構成情報を配列として読み出す。\n2. エントリ・スクリプトがアプリケーションの新しいインスタンスを作成する。\n  * [[yii\\base\\Application::preInit()|preInit()]] が呼び出されて、[[yii\\base\\Application::basePath|basePath]] のような、\n  優先度の高いアプリケーション・プロパティを構成する。\n  * [[yii\\base\\Application::errorHandler|エラー・ハンドラ]] を登録する。\n  * アプリケーションのプロパティを構成する。\n  * [[yii\\base\\Application::init()|init()]] が呼ばれ、そこから更に、ブートストラップ・コンポーネントを走らせるために、\n  [[yii\\base\\Application::bootstrap()|bootstrap()]] が呼ばれる。\n3. エントリ・スクリプトが [[yii\\base\\Application::run()]] を呼んで、アプリケーションを走らせる。\n  * [[yii\\base\\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_REQUEST]] イベントをトリガする。\n  * リクエストを処理する: リクエストを [ルート](runtime-routing.md) とそれに結び付くパラメータとして解決する。\n    ルートによって指定されたモジュール、コントローラ、および、アクションを作成する。そしてアクションを実行する。\n  * [[yii\\base\\Application::EVENT_AFTER_REQUEST|EVENT_AFTER_REQUEST]] イベントをトリガする。\n  * エンド・ユーザにレスポンスを送信する。\n4. エントリ・スクリプトがアプリケーションから終了ステータスを受け取り、リクエストの処理を完了する。\n"
  },
  {
    "path": "docs/guide-ja/structure-assets.md",
    "content": "アセット\n========\n\nYii では、アセットは、ウェブ・ページで参照できるファイルを意味します。アセットは CSS ファイルであったり、JavaScript ファイルであったり、画像やビデオのファイルであったりします。\nアセットはウェブでアクセス可能なディレクトリに配置され、ウェブ・サーバによって直接に提供されます。\n\nたいていの場合、アセットはプログラム的に管理する方が望ましいものです。\n例えば、ページの中で [[yii\\jui\\DatePicker]] ウィジェットを使うとき、このウィジェットは必要な CSS と JavaScript のファイルを自動的にインクルードします。\nあなたに対して、手作業で必要なファイルを探してインクルードするように要求したりはしません。\nそして、このウィジェットを新しいバージョンにアップグレードしたときは、自動的に新しいバージョンのアセット・ファイルが使用されるようになります。\nこのチュートリアルでは、Yii によって提供される強力なアセット管理機能について説明します。\n\n\n## アセット・バンドル <span id=\"asset-bundles\"></span>\n\nYii はアセットを *アセット・バンドル* を単位として管理します。アセット・バンドルは、簡単に言えば、\nあるディレクトリの下に集められた一群のアセットです。\n[ビュー](structure-views.md) の中でアセット・バンドルを登録すると、バンドルの中の CSS や JavaScript のファイルがレンダリングされるウェブ・ページに挿入されます。\n\n\n## アセット・バンドルを定義する <span id=\"defining-asset-bundles\"></span>\n\nアセット・バンドルは [[yii\\web\\AssetBundle]] から拡張された PHP クラスとして定義されます。\nバンドルの名前は、対応する PHP クラスの完全修飾名 (先頭のバック・スラッシュを除く) です。\nアセット・バンドル・クラスは [オートロード可能](concept-autoloading.md) でなければなりません。\nアセット・バンドル・クラスは、通常、アセットがどこに置かれているか、バンドルがどういう CSS や JavaScript のファイルを含んでいるか、そして、バンドルが他のバンドルにどのように依存しているかを定義します。\n\n以下のコードは [ベーシック・プロジェクト・テンプレート](start-installation.md) によって使用されているメインのアセット・バンドルを定義するものです。\n\n```php\n<?php\n\nnamespace app\\assets;\n\nuse yii\\web\\AssetBundle;\n\nclass AppAsset extends AssetBundle\n{\n    public $basePath = '@webroot';\n    public $baseUrl = '@web';\n    public $css = [\n        'css/site.css',\n        ['css/print.css', 'media' => 'print'],\n    ];\n    public $js = [\n    ];\n    public $depends = [\n        'yii\\web\\YiiAsset',\n        'yii\\bootstrap\\BootstrapAsset',\n    ];\n}\n```\n\n上の `AppAsset` クラスは、アセット・ファイルが `@webroot` ディレクトリの下に配置されており、それが URL `@web` に対応することを定義しています。\nバンドルは一つだけ CSS ファイル `css/site.css` を含み、JavaScript ファイルは含みません。\nバンドルは、他の二つのバンドル、すなわち [[yii\\web\\YiiAsset]] と [[yii\\bootstrap\\BootstrapAsset]] に依存しています。\n以下、[[yii\\web\\AssetBundle]] のプロパティに関して、更に詳細に説明します。\n\n* [[yii\\web\\AssetBundle::sourcePath|sourcePath]]: このバンドルのアセット・ファイルを含むルート・ディレクトリを指定します。\n  ルート・ディレクトリがウェブ・アクセス可能でない場合に、このプロパティをセットしなければなりません。\n  そうでない場合は、代りに、[[yii\\web\\AssetBundle::basePath|basePath]] と [[yii\\web\\AssetBundle::baseUrl|baseUrl]] のプロパティをセットしなければなりません。\n  [パス・エイリアス](concept-aliases.md) をここで使うことが出来ます。\n* [[yii\\web\\AssetBundle::basePath|basePath]]: このバンドルのアセット・ファイルを含むウェブ・アクセス可能なディレクトリを指定します。\n  [[yii\\web\\AssetBundle::sourcePath|sourcePath]] プロパティをセットした場合は、[アセット・マネージャ](#asset-manager) がバンドルに含まれるファイルをウェブ・アクセス可能なディレクトリに発行して、\n  その結果、このプロパティを上書きします。\n  アセット・ファイルが既にウェブ・アクセス可能なディレクトリにあり、アセットの発行が必要でない場合に、このプロパティをセットしなければなりません。\n  [パス・エイリアス](concept-aliases.md) をここで使うことが出来ます。\n* [[yii\\web\\AssetBundle::baseUrl|baseUrl]]: [[yii\\web\\AssetBundle::basePath|basePath]] ディレクトリに対応する URL を指定します。\n  [[yii\\web\\AssetBundle::basePath|basePath]] と同じように、[[yii\\web\\AssetBundle::sourcePath|sourcePath]] プロパティをセットした場合は、\n  [アセット・マネージャ](#asset-manager) がアセットを発行して、その結果、このプロパティを上書きします。\n  [パス・エイリアス](concept-aliases.md) をここで使うことが出来ます。\n* [[yii\\web\\AssetBundle::css|css]]: このバンドルに含まれている CSS ファイルをリストする配列です。\n  ディレクトリの区切りとしてフォワード・スラッシュ \"/\" だけを使わなければならないことに注意してください。\n  それぞれのファイルは、個別に、パス文字列、または、パス文字列と属性のタグと値を一緒に含む配列によって指定することが出来ます。\n* [[yii\\web\\AssetBundle::js|js]]: このバンドルに含まれる JavaScript ファイルをリストする配列です。\n  この配列の形式は、[[yii\\web\\AssetBundle::css|css]] の配列の形式と同じです。\n  それぞれの JavaScript ファイルは、以下の二つの形式のどちらかによって指定することが出来ます。\n  - ローカルの JavaScript ファイルを表す相対パス (例えば `js/main.js`)。\n    実際のファイルのパスは、この相対パスの前に [[yii\\web\\AssetManager::basePath]] を付けることによって決定されます。\n    また、実際の URL は、この相対パスの前に [[yii\\web\\AssetManager::baseUrl]] を付けることによって決定されます。\n  - 外部の JavaScript ファイルを表す絶対 URL。\n    例えば、`https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js` や\n    `//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js` など。\n* [[yii\\web\\AssetBundle::depends|depends]]: このバンドルが依存しているアセット・バンドルの名前をリストする配列です\n   (バンドルの依存関係については、すぐ後で説明します)。\n* [[yii\\web\\AssetBundle::jsOptions|jsOptions]]: このバンドルにある *全て* の JavaScript ファイルについて、\n  それを登録するときに呼ばれる [[yii\\web\\View::registerJsFile()]] メソッドに渡されるオプションを指定します。\n* [[yii\\web\\AssetBundle::cssOptions|cssOptions]]: このバンドルにある *全て* の CSS ファイルについて、\n  それを登録するときに呼ばれる [[yii\\web\\View::registerCssFile()]] メソッドに渡されるオプションを指定します。\n* [[yii\\web\\AssetBundle::publishOptions|publishOptions]]: ソースのアセット・ファイルをウェブ・ディレクトリに発行するときに呼ばれる\n  [[yii\\web\\AssetManager::publish()]] メソッドに渡されるオプションを指定します。\n  これは [[yii\\web\\AssetBundle::sourcePath|sourcePath]] プロパティを指定した場合にだけ使用されます。\n\n\n### アセットの配置場所 <span id=\"asset-locations\"></span>\n\nアセットは、配置場所を基準にして、次のように分類することが出来ます。\n\n* ソース・アセット: アセット・ファイルは、ウェブ経由で直接にアクセスすることが出来ない PHP ソース・コードと一緒に配置されています。\n  ページの中でソース・アセットを使用するためには、ウェブ・ディレクトリにコピーして、いわゆる発行されたアセットに変換しなければなりません。\n  このプロセスは、すぐ後で詳しく説明しますが、*アセット発行* と呼ばれます。\n* 発行されたアセット: アセット・ファイルはウェブ・ディレクトリに配置されており、したがってウェブ経由で直接にアクセスすることが出来ます。\n* 外部アセット: アセット・ファイルは、あなたのウェブ・アプリケーションをホストしているのとは別のウェブ・サーバ上に\n  配置されています。\n\nアセット・バンドル・クラスを定義するときに、[[yii\\web\\AssetBundle::sourcePath|sourcePath]] プロパティを指定した場合は、\n相対パスを使ってリストに挙げられたアセットは全てソース・アセットであると見なされます。\nこのプロパティを指定しなかった場合は、アセットは発行されたアセットであることになります\n(したがって、[[yii\\web\\AssetBundle::basePath|basePath]] と [[yii\\web\\AssetBundle::baseUrl|baseUrl]] を指定して、アセットがどこに配置されているかを Yii に知らせなければなりません)。\n\nアプリケーションに属するアセットは、不要なアセット発行プロセスを避けるために、ウェブ・ディレクトリに置くことが推奨されます。\n前述の例において `AppAsset` が [[yii\\web\\AssetBundle::sourcePath|sourcePath]] ではなく\n[[yii\\web\\AssetBundle::basePath|basePath]] を指定しているのは、これが理由です。\n\n[エクステンション](structure-extensions.md) の場合は、\nアセットがソース・コードと一緒にウェブからアクセス出来ないディレクトリに配置されているため、\nアセット・バンドル・クラスを定義するときには [[yii\\web\\AssetBundle::sourcePath|sourcePath]] プロパティを指定しなければなりません。\n\n> Note: `@webroot/assets` を [[yii\\web\\AssetBundle::sourcePath|ソース・パス]] として使ってはいけません。\n  このディレクトリは、デフォルトでは、[[yii\\web\\AssetManager|アセット・マネージャ]] がソースの配置場所から発行されたアセット・ファイルを保存する場所として使われます。\n  このディレクトリの中のファイルはすべて一時的なものと見なされており、\n  削除されることがあります。\n\n\n### アセットの依存関係 <span id=\"asset-dependencies\"></span>\n\nウェブ・ページに複数の CSS や JavaScript ファイルをインクルードするときは、オーバーライドの問題を避けるために、一定の順序に従わなければなりません。\n例えば、ウェブ・ページで jQuery UI ウィジェットを使おうとするときは、jQuery JavaScript ファイルが\njQuery UI JavaScript ファイルより前にインクルードされることを保証しなければなりません。\nこのような順序付けをアセット間の依存関係と呼びます。\n\nアセットの依存関係は、主として、[[yii\\web\\AssetBundle::depends]] プロパティによって指定されます。\n`AppAsset` の例では、このアセット・バンドルは他の二つのアセット・バンドル、すなわち、[[yii\\web\\YiiAsset]] と [[yii\\bootstrap\\BootstrapAsset]] に依存しています。\nこのことは、`AppAsset` の CSS と JavaScript ファイルが、依存している二つのアセット・バンドルにあるファイルの *後に*\nインクルードされることを意味します。\n\nアセットの依存関係は中継されます。つまり、バンドル A が B に依存し、B が C に依存していると、A は C にも依存していることになります。\n\n\n### アセットのオプション <span id=\"asset-options\"></span>\n\n[[yii\\web\\AssetBundle::cssOptions|cssOptions]] および [[yii\\web\\AssetBundle::jsOptions|jsOptions]] のプロパティを指定して、\nCSS と JavaScript ファイルがページにインクルードされる方法をカスタマイズすることが出来ます。\nこれらのプロパティの値は、[ビュー](structure-views.md) が CSS と JavaScript ファイルをインクルードするために、[[yii\\web\\View::registerCssFile()]] および\n[[yii\\web\\View::registerJsFile()]] メソッドを呼ぶときに、それぞれ、オプションとして引き渡されます。\n\n> Note: バンドル・クラスでセットしたオプションは、バンドルの中の *全て* の CSS/JavaScript ファイルに適用されます。\n  いろいろなファイルに別々のオプションを使用したい場合は、上述した [[yii\\web\\AssetBundle::css|css] の形式を使うか、\nまたは、別々のアセット・バンドルを作成して、個々のバンドルの中では、一組のオプションを使うようにしなければなりません。\n\n例えば、IE9 以下のブラウザに対して CSS ファイルを条件的にインクルードするために、次のオプションを使うことが出来ます。\n\n```php\npublic $cssOptions = ['condition' => 'lte IE9'];\n```\n\nこうすると、バンドルの中の CSS ファイルは下記の HTML タグを使ってインクルードされるようになります。\n\n```html\n<!--[if lte IE9]>\n<link rel=\"stylesheet\" href=\"path/to/foo.css\">\n<![endif]-->\n```\n\n生成された CSS のリンクタグを `<noscript>` の中に包むためには、次のように `cssOptions` を構成することが出来ます。\n\n```php\npublic $cssOptions = ['noscript' => true];\n```\n\nJavaScript ファイルをページの head セクションにインクルードするためには、次のオプションを使います\n(デフォルトでは、JavaScript ファイルは body セクションの最後にインクルードされます)。\n\n```php\npublic $jsOptions = ['position' => \\yii\\web\\View::POS_HEAD];\n```\n\nデフォルトでは、アセット・バンドルが発行されるときは、[[yii\\web\\AssetBundle::sourcePath]]\nで指定されたディレクトリの中にある全てのコンテントが発行されます。\n[[yii\\web\\AssetBundle::publishOptions|publishOptions]] プロパティを構成することによって、この振る舞いをカスタマイズすることが出来ます。\n例えば、[[yii\\web\\AssetBundle::sourcePath]] の一個または数個のサブ・ディレクトリだけを発行するために、アセット・バンドル・クラスの中で下記のようにすることが出来ます。\n\n```php\n<?php\nnamespace app\\assets;\n\nuse yii\\web\\AssetBundle;\n\nclass FontAwesomeAsset extends AssetBundle \n{\n    public $sourcePath = '@bower/font-awesome'; \n    public $css = [ \n        'css/font-awesome.min.css', \n    ]; \n    public $publishOptions = [\n        'only' => [\n            'fonts/*',\n            'css/*',\n        ]\n    ];\n}  \n```\n\n上記の例は、[\"fontawesome\" パッケージ](https://fontawesome.com/) のためのアセット・バンドルを定義するものです。\n発行オプション `only` を指定して、`fonts` と `css` サブ・ディレクトリだけが発行されるようにしています。\n\n\n### Bower と NPM のアセットのインストール <span id=\"bower-npm-assets\"></span>\n\nほとんどの JavaScript/CSS パッケージは、[Bower](https://bower.io/) および/または [NPM](https://www.npmjs.com/) によって管理されています。\nPHP の世界には Composer があって、PHP の依存を管理していますが、PHP のパッケージと全く同じように\n`composer.json` を使って Bower のパッケージも NPM のパッケージもロードすることが可能です。\n\nこのことを達成するために Composer の構成を少し修正しなければなりません。二つの方法があります。\n\n___\n\n#### asset-packagist レポジトリを使う\n\nこの方法は NPM または Bower のパッケージを必要とするプロジェクトの大半の要求を満たすことが出来ます。\n\n> Note: 2.0.13 以降、ベーシック・アプリケーション・テンプレートとアドバンスト・アプリケーション・テンプレートはともに、\n  デフォルトで asset-packagist を使うように前もって構成されていますので、このセクションは読み飛ばすことが出来ます。\n\nプロジェクトの `composer.json` に、下記を追加します。\n\n```json\n\"repositories\": [\n    {\n        \"type\": \"composer\",\n        \"url\": \"https://asset-packagist.org\"\n    }\n]\n```\n\n[アプリケーション構成情報](concept-configurations.md) の `@npm` と `@bower` の [エイリアス](concept-aliases.md) を修正します。\n\n```php\n$config = [\n    ...\n    'aliases' => [\n        '@bower' => '@vendor/bower-asset',\n        '@npm'   => '@vendor/npm-asset',\n    ],\n    ...\n];\n```\n\nasset-packagist がどのようにして動作するのかは、[asset-packagist.org のサイト](https://asset-packagist.org) に説明があります。\n\n#### fxp/composer-asset-plugin を使う\n\nasset-packagist と異なって、composer-asset-plugin はアプリケーション構成の変更を少しも要求しません。\nその代りに、次のコマンドを実行して特別な Composer プラグインをグローバルにインストールすることが要求されます。\n\n```bash\ncomposer global require \"fxp/composer-asset-plugin:^1.4.1\"\n```\n\nこのコマンドによって [composer asset plugin](https://github.com/fxpio/composer-asset-plugin) がグローバルにインストールされ、\nBower と NPM パッケージの依存関係を Composer によって管理することが出来るようになります。\nプラグインがインストールされた後は、あなたのコンピュータ上の全てのプロジェクトが `composer.json` を通じて Bower と NPM パッケージをサポートすることが出来ます。\n\nYii を使ってこれらのアセットを発行したい場合は、プロジェクトの `composer.json` に下記を追加して、\nインストールされるパッケージが配置されるディレクトリを調整します。\n\n```json\n\"config\": {\n    \"fxp-asset\": {\n        \"installer-paths\": {\n            \"npm-asset-library\": \"vendor/npm\",\n            \"bower-asset-library\": \"vendor/bower\"\n        }\n    }\n}\n```\n\n> Note: `fxp/composer-asset-plugin` は、asset-packagist に比べて、`composer update`\n  コマンドを著しく遅くします。\n \n____\n \nComposer で Bower と NPM をサポートできるように構成した後は:\n\n1. アプリケーションまたはエクステンションの `composer.json` ファイルを修正して、パッケージを `require` のエントリに入れます。\n   ライブラリを参照するのに、`bower-asset/PackageName` (Bower パッケージ) または `npm-asset/PackageName` (NPM パッケージ)\n   を使わなければなりません。\n2. `composer update` を実行します。\n3. アセット・バンドル・クラスを作成して、アプリケーションまたはエクステンションで使う予定の JavaScript/CSS ファイルをリストに挙げます。\n   [[yii\\web\\AssetBundle::sourcePath|sourcePath]] プロパティは、`@bower/PackageName` または `@npm/PackageName` としなければなりません。\n   これは、Composer が Bower または NPM パッケージを、このエイリアスに対応するディレクトリにインストールするためです。\n\n> Note: パッケージの中には、全ての配布ファイルをサブ・ディレクトリに置くものがあります。\n  その場合には、そのサブ・ディレクトリを [[yii\\web\\AssetBundle::sourcePath|sourcePath]] の値として指定しなければなりません。\n  例えば、[[yii\\web\\JqueryAsset]] は `@bower/jquery` ではなく `@bower/jquery/dist` を使います。\n\n\n## アセット・バンドルを使う <span id=\"using-asset-bundles\"></span>\n\nアセット・バンドルを使うためには、[[yii\\web\\AssetBundle::register()]] メソッドを呼んでアセット・バンドルを [ビュー](structure-views.md) に登録します。\n例えば、次のようにしてビュー・テンプレートの中でアセット・バンドルを登録することが出来ます。\n\n```php\nuse app\\assets\\AppAsset;\nAppAsset::register($this);  // $this はビュー・オブジェクトを表す\n```\n\n> Info: [[yii\\web\\AssetBundle::register()]] メソッドは、[[yii\\web\\AssetBundle::basePath|basePath]] や [[yii\\web\\AssetBundle::baseUrl|baseUrl]] など、\n  発行されたアセットに関する情報を含むアセット・バンドル・オブジェクトを返します。\n\n他の場所でアセット・バンドルを登録しようとするときは、必要とされるビュー・オブジェクトを提供しなければなりません。\n例えば、[ウィジェット](structure-widgets.md)・クラスの中でアセット・バンドルを登録するためには、`$this->view` によってビュー・オブジェクトを取得することが出来ます。\n\nアセット・バンドルがビューに登録されるとき、舞台裏では、依存している全てのアセット・バンドルが Yii によって登録されます。\nそして、アセット・バンドルがウェブからはアクセス出来ないディレクトリに配置されている場合は、\nアセット・バンドルがウェブ・ディレクトリに発行されます。\nその後、ビューがページをレンダリングするときに、登録されたバンドルのリストに挙げられている CSS と JavaScript ファイルのための `<link>` タグと `<script>` タグが生成されます。\nこれらのタグの順序は、登録されたバンドル間の依存関係、および、[[yii\\web\\AssetBundle::css]] と\n[[yii\\web\\AssetBundle::js]] のプロパティのリストに挙げられたアセットの順序によって決定されます。\n\n\n### 動的なアセット・バンドル <span id=\"dynamic-asset-bundles\"></span>\n\nアセット・バンドルは、通常の PHP クラスですので、内部のパラメータを動的に調整する追加のロジックを持つことが出来ます。\n例えば、洗練された JavaScript ライブラリには、国際化の機能を、サポートする言語ごとに独立したソース・ファイルにパッケージして提供しているものもあります。\nその場合、ライブラリの翻訳を動作させるためには、特定の '.js' ファイルをページに追加しなければなりません。\nこのことを [[yii\\web\\AssetBundle::init()]] メソッドをオーバーライドすることによって実現することが出来ます。\n\n```php\nnamespace app\\assets;\n\nuse yii\\web\\AssetBundle;\nuse Yii;\n\nclass SophisticatedAssetBundle extends AssetBundle\n{\n    public $sourcePath = '/path/to/sophisticated/src';\n    public $js = [\n        'sophisticated.js' // 常に使用されるファイル\n    ];\n\n    public function init()\n    {\n        parent::init();\n        $this->js[] = 'i18n/' . Yii::$app->language . '.js'; // 動的に追加されるファイル\n    }\n}\n```\n\n個々のアセット・バンドルは、 [[yii\\web\\AssetBundle::register()]] によって返されるインスタンスによって調整することも出来ます。\n例えば、\n\n```php\nuse app\\assets\\SophisticatedAssetBundle;\nuse Yii;\n\n$bundle = SophisticatedAssetBundle::register(Yii::$app->view);\n$bundle->js[] = 'i18n/' . Yii::$app->language . '.js'; // 動的に追加されるファイル\n```\n\n> Note: アセット・バンドルの動的な調整はサポートされてはいますが、**推奨はされません**。\n  予期しない副作用を引き起こしやすいので、可能であれば避けるべきです。\n\n\n### アセット・バンドルをカスタマイズする <span id=\"customizing-asset-bundles\"></span>\n\nYii は、[[yii\\web\\AssetManager]] によって実装されている `assetManager` という名前のアプリケーション・コンポーネントを通じてアセット・バンドルを管理します。\n[[yii\\web\\AssetManager::bundles]] プロパティを構成することによって、アセット・バンドルの振る舞いをカスタマイズすることが出来ます。\n例えば、デフォルトの [[yii\\web\\JqueryAsset]] アセット・バンドルはインストールされた jQuery の Bower パッケージにある `jquery.js` ファイルを使用します。\nあなたは、可用性とパフォーマンスを向上させるために、Google によってホストされたバージョンを使いたいと思うかも知れません。\n次のように、アプリケーションの構成情報で `assetManager` を構成することによって、それが達成できます。\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'bundles' => [\n                'yii\\web\\JqueryAsset' => [\n                    'sourcePath' => null,   // バンドルを発行しない\n                    'js' => [\n                        '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js',\n                    ]\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n複数のアセット・バンドルも同様に [[yii\\web\\AssetManager::bundles]] によって構成することが出来ます。\n配列のキーは、アセット・バンドルのクラス名 (最初のバック・スラッシュを除く) とし、\n配列の値は、対応する [構成情報配列](concept-configurations.md) とします。\n\n> Tip: アセット・バンドルの中で使うアセットを条件的に選択することが出来ます。\n> 次の例は、開発環境では `jquery.js` を使い、そうでなければ `jquery.min.js` を使う方法を示すものです。\n>\n> ```php\n> 'yii\\web\\JqueryAsset' => [\n>     'js' => [\n>         YII_ENV_DEV ? 'jquery.js' : 'jquery.min.js'\n>     ]\n> ],\n> ```\n\n無効にしたいアセット・バンドルの名前に `false` を結びつけることによって、一つまたは複数のアセット・バンドルを無効にすることが出来ます。\n無効にされたアセット・バンドルをビューに登録した場合は、依存するバンドルは一つも登録されません。\nまた、ビューがページをレンダリングするときも、バンドルの中のアセットは一つもインクルードされません。\n例えば、[[yii\\web\\JqueryAsset]] を無効化するために、次の構成情報を使用することが出来ます。\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'bundles' => [\n                'yii\\web\\JqueryAsset' => false,\n            ],\n        ],\n    ],\n];\n```\n\n[[yii\\web\\AssetManager::bundles]] を `false` にセットすることによって、*全て* のバンドルを無効にすることも出来ます。\n\n[[yii\\web\\AssetManager::bundles]] によってなされたカスタマイズはアセット・バンドルの生成時、すなわち、オブジェクトのコンストラクタの段階で適用される、\nということを心に留めてください。\n従って、[[yii\\web\\AssetManager::bundles]] のレベルで設定されたマッピングは、それ以後にバンドルのオブジェクトに対してなされる修正によって上書きされます。\n具体的に言えば、[[yii\\web\\AssetBundle::init()]] メソッドの中での修正や、登録されたバンドル・オブジェクトに対する修正は、`AssetManager` の構成よりも優先されます。\n以下に、[[yii\\web\\AssetManager::bundles]] によって設定されたマッピングが効力を持たない例を示します。\n\n```php\n// プログラムのソース・コード\n\nnamespace app\\assets;\n\nuse yii\\web\\AssetBundle;\nuse Yii;\n\nclass LanguageAssetBundle extends AssetBundle\n{\n    // ...\n\n    public function init()\n    {\n        parent::init();\n        $this->baseUrl = '@web/i18n/' . Yii::$app->language; // AssetManager` では処理出来ない!\n    }\n}\n// ...\n\n$bundle = \\app\\assets\\LargeFileAssetBundle::register(Yii::$app->view);\n$bundle->baseUrl = YII_DEBUG ? '@web/large-files': '@web/large-files/minified'; // AssetManager` では処理出来ない!\n\n\n// アプリケーション構成\n\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'bundles' => [\n                'app\\assets\\LanguageAssetBundle' => [\n                    'baseUrl' => 'https://some.cdn.com/files/i18n/en' // 効力を持たない!\n                ],\n                'app\\assets\\LargeFileAssetBundle' => [\n                    'baseUrl' => 'https://some.cdn.com/files/large-files' // 効力を持たない!\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n\n### アセット・マッピング <span id=\"asset-mapping\"></span>\n\n時として、複数のアセット・バンドルで使われている 正しくない/互換でない アセット・ファイル・パスを「修正」したい場合があります。\n例えば、バンドル A がバージョン 1.11.1 の `jquery.min.js` を使い、バンドル B がバージョン 2.1.1 の `jquery.js` を使っているような場合です。\nそれぞれのバンドルをカスタマイズすることで問題を修正することも出来ますが、それよりも簡単な方法は、*アセット・マップ* 機能を使って、正しくないアセットを望ましいアセットに割り付けることです。\nそうするためには、以下のように [[yii\\web\\AssetManager::assetMap]] プロパティを構成します。\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'assetMap' => [\n                'jquery.js' => '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js',\n            ],\n        ],\n    ],\n];\n```\n\n[[yii\\web\\AssetManager::assetMap|assetMap]] のキーは修正したいアセットの名前であり、値は望ましいアセットのパスです。\nアセット・バンドルをビューに登録するとき、[[yii\\web\\AssetBundle::css|css]] と\n[[yii\\web\\AssetBundle::js|js]] の配列に含まれるすべてのアセット・ファイルの相対パスがこのマップと突き合わせて調べられます。\nキーのどれかがアセット・ファイルのパス (利用できる場合は、[[yii\\web\\AssetBundle::sourcePath]] が前置されます)\nの最後の部分と一致した場合は、対応する値によってアセットが置き換えられ、ビューに登録されます。\n例えば、`my/path/to/jquery.js` というアセット・ファイルは `jquery.js` というキーにマッチします。\n\n> Note: 相対パスを使って指定されたアセットだけがアセット・マッピングの対象になります。\n  そして、置き換える側のアセットのパスは、絶対 URL であるか、[[yii\\web\\AssetManager::basePath]] からの相対パスであるかの、どちらかでなければなりません。\n\n\n### アセット発行 <span id=\"asset-publishing\"></span>\n\n既に述べたように、アセット・バンドルがウェブからアクセス出来ないディレクトリに配置されている場合は、\nバンドルがビューに登録されるときに、アセットがウェブ・ディレクトリにコピーされます。\nこのプロセスは *アセット発行* と呼ばれ、[[yii\\web\\AssetManager|アセット・マネージャ]] によって自動的に実行されます。\n\nデフォルトでは、アセットが発行されるディレクトリは `@webroot/assets` であり、`@web/assets` という URL に対応するものです。\nこの場所は、[[yii\\web\\AssetManager::basePath|basePath]] と [[yii\\web\\AssetManager::baseUrl|baseUrl]]\nのプロパティを構成してカスタマイズすることが出来ます。\n\nファイルをコピーすることでアセットを発行する代りに、OS とウェブ・サーバが許容するなら、シンボリック・リンクを使うことを考慮しても良いでしょう。\nこの機能は [[yii\\web\\AssetManager::linkAssets|linkAssets]] を `true` にセットすることで有効にすることが出来ます。\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'linkAssets' => true,\n        ],\n    ],\n];\n```\n\n上記の構成によって、アセット・マネージャはアセット・バンドルを発行するときにソース・パスへのシンボリック・リンクを作成するようになります。\nこの方がファイルのコピーより速く、また、\n発行されたアセットが常に最新であることを保証することも出来ます。\n\n\n### キャッシュの廃棄 <span id=\"cache-busting\"></span>\n\n運用モードで動作しているウェブ・アプリケーションでは、アセットなどの静的なリソースに対する HTTP キャッシュを有効にするのが通例です。\nこの慣行の難点は、アセットを修正して運用サーバに配備したときに、ユーザのクライアントが HTTP キャッシュのせいで古いバージョンを使い続けるおそれが常にあるという点です。\nこの難点を克服するために、キャッシュ廃棄機能を使うことが出来ます。\nこれはバージョン 2.0.3 で導入された機能で、[[yii\\web\\AssetManager]]　を下記のように構成することで有効になります。\n  \n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'appendTimestamp' => true,\n        ],\n    ],\n];\n```\n\nこのようにすると、全ての発行されたアセットの URL の末尾に最終更新日時のタイムスタンプが追加されます。\n例えば、`yii.js` に対する URL は `/assets/5515a87c/yii.js?v=1423448645\"` のようなものになります。\nパラメータ `v` が `yii.js` ファイルの最終更新日時のタイムスタンプを表しています。\nこれで、あなたがアセットを修正したときには、その URL も変更され、クライアントに最新バージョンのアセットを強制的に取得させることが出来ます。\n\n\n## よく使われるアセット・バンドル <span id=\"common-asset-bundles\"></span>\n\nコアの Yii コードは多くのアセット・バンドルを定義しています。\nその中で、下記のバンドルはよく使われるものであり、あなたのアプリケーションやエクステンションのコードでも参照することが出来るものです。\n\n- [[yii\\web\\YiiAsset]]: 主として `yii.js` ファイルをインクルードするためのバンドルです。  このファイルはモジュール化された JavaScript のコードを編成するメカニズムを実装しています。\n  また、`data-method` と `data-confirm` の属性に対する特別なサポートや、その他の有用な機能を提供します。\n  `yii.js` に関する詳細な情報は [クライアント・スクリプトのセクション](output-client-scripts.md#yii.js) にあります。\n- [[yii\\web\\JqueryAsset]]: jQuery の bower パッケージから `jquery.js` ファイルをインクルードします。\n- [[yii\\bootstrap\\BootstrapAsset]]: Twitter Bootstrap フレームワークから CSS ファイルをインクルードします。\n- [[yii\\bootstrap\\BootstrapPluginAsset]]: Bootstrap JavaScript プラグインをサポートするために、\n  Twitter Bootstrap フレームワークから JavaScript ファイルをインクルードします。\n- [[yii\\jui\\JuiAsset]]: jQuery UI ライブラリから CSS と JavaScript のファイルをインクルードします。\n\nあなたのコードが、jQuery や jQuery UI または Bootstrap に依存する場合は、自分自身のバージョンを作るのではなく、これらの定義済みのアセット・バンドルを使用すべきです。\nこれらのバンドルのデフォルトの設定があなたの必要を満たさない時は、[アセット・バンドルをカスタマイズする](#customizing-asset-bundles)\nの項で説明したように、それをカスタマイズすることが出来ます。\n\n\n## アセット変換 <span id=\"asset-conversion\"></span>\n\n直接に CSS および/または JavaScript のコードを書く代りに、何らかの拡張構文を使って書いたものを特別なツールを使って CSS/JavaScript に変換する、ということを開発者はしばしば行います。\n例えば、CSS コードのためには、[LESS](https://lesscss.org/) や [SCSS](https://sass-lang.com/) を使うことが出来ます。\nまた、JavaScript のためには、[TypeScript](https://www.typescriptlang.org/) を使うことが出来ます。\n\n拡張構文を使ったアセット・ファイルをアセット・バンドルの中の [[yii\\web\\AssetBundle::css|css]] と [[yii\\web\\AssetBundle::js|js]] のリストに挙げることが出来ます。例えば、\n\n```php\nclass AppAsset extends AssetBundle\n{\n    public $basePath = '@webroot';\n    public $baseUrl = '@web';\n    public $css = [\n        'css/site.less',\n    ];\n    public $js = [\n        'js/site.ts',\n    ];\n    public $depends = [\n        'yii\\web\\YiiAsset',\n        'yii\\bootstrap\\BootstrapAsset',\n    ];\n}\n```\n\nこのようなアセット・バンドルをビューに登録すると、[[yii\\web\\AssetManager|アセット・マネージャ]] が自動的にプリ・プロセッサ・ツールを走らせて、\n認識できた拡張構文のアセットを CSS/JavaScript に変換します。\n最終的にビューがページをレンダリングするときには、ビューは元の拡張構文のアセットではなく、\n変換後の CSS/JavaScript ファイルをページにインクルードします。\n\nYii はファイル名の拡張子を使って、アセットが使っている拡張構文を識別します。\nデフォルトでは、下記の構文とファイル名拡張子を認識します。\n\n- [LESS](https://lesscss.org/): `.less`\n- [SCSS](https://sass-lang.com/): `.scss`\n- [Stylus](https://stylus-lang.com/): `.styl`\n- [CoffeeScript](https://coffeescript.org/): `.coffee`\n- [TypeScript](https://www.typescriptlang.org/): `.ts`\n\nYii はインストールされたプリ・プロセッサ・ツールに頼ってアセットを変換します。\n例えば、[LESS](https://lesscss.org/) を使うためには、`lessc` プリ・プロセッサ・コマンドをインストールしなければなりません。\n\n下記のように [[yii\\web\\AssetManager::converter]] を構成することで、\nプリ・プロセッサ・コマンドとサポートされる拡張構文をカスタマイズすることが出来ます。\n\n```php\nreturn [\n    'components' => [\n        'assetManager' => [\n            'converter' => [\n                'class' => 'yii\\web\\AssetConverter',\n                'commands' => [\n                    'less' => ['css', 'lessc {from} {to} --no-color'],\n                    'ts' => ['js', 'tsc --out {to} {from}'],\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n上記においては、サポートされる拡張構文が [[yii\\web\\AssetConverter::commands]] プロパティによって定義されています。\n配列のキーはファイルの拡張子 (先頭のドットは省く) であり、\n配列の値は結果として作られるアセット・ファイルの拡張子とアセット変換を実行するためのコマンドです。\nコマンドの中の `{from}` と `{to}` のトークンは、ソースのアセット・ファイルのパスとターゲットのアセット・ファイルのパスに置き換えられます。\n\n> Info: 上記で説明した方法の他にも、拡張構文のアセットを扱う方法はあります。\n  例えば、[grunt](https://gruntjs.com/) のようなビルド・ツールを使って、拡張構文のアセットをモニターし、\n  自動的に変換することが出来ます。\n  この場合は、元のファイルではなく、結果として作られる CSS/JavaScript ファイルをアセット・バンドルのリストに挙げなければなりません。\n\n\n## アセットを結合して圧縮する <span id=\"combining-compressing-assets\"></span>\n\nウェブ・ページは数多くの CSS および/または JavaScript ファイルをインクルードすることがあり得ます。\nHTTP リクエストの数とこれらのファイルの全体としてのダウンロード・サイズを削減するためによく用いられる方法は、複数の CSS/JavaScript ファイルを結合して圧縮し、一つまたはごく少数のファイルにまとめることです。\nそして、ウェブ・ページでは元のファイルをインクルードする代りに、圧縮されたファイルをインクルードする訳です。\n \n> Info: アセットの結合と圧縮は、通常はアプリケーションが本番モードにある場合に必要になります。\n  開発モードにおいては、たいていは元の CSS/JavaScript ファイルを使う方がデバッグのために好都合です。\n\n次に、既存のアプリケーション・コードを修正する必要なしに、\nアセット・ファイルを結合して圧縮する方法を紹介します。\n\n1. アプリケーションの中で、結合して圧縮する予定のアセット・バンドルを全て探し出す。\n2. これらのバンドルを一個か数個のグループにまとめる。どのバンドルも一つのグループにしか属することが出来ないことに注意。\n3. 各グループの CSS ファイルを一つのファイルに結合/圧縮する。JavaScript ファイルに対しても同様にこれを行う。\n4. 各グループに対して新しいアセット・バンドルを定義する。\n   * [[yii\\web\\AssetBundle::css|css]] と [[yii\\web\\AssetBundle::js|js]] のプロパティに、\n     それぞれ、結合された CSS ファイルと JavaScript ファイルをセットする。\n   * 各グループに属する元のアセット・バンドルをカスタマイズして、[[yii\\web\\AssetBundle::css|css]] と\n     [[yii\\web\\AssetBundle::js|js]] のプロパティを空にし、[[yii\\web\\AssetBundle::depends|depends]]\n     プロパティにグループのために作られた新しいバンドルを指定する。\n\nこの方法を使うと、ビューでアセット・バンドルを登録したときに、\n元のバンドルが属するグループのための新しいアセット・バンドルが自動的に登録されるようになります。\nそして、結果として、結合/圧縮されたアセット・ファイルが、元のファイルの代りに、ページにインクルードされます。\n\n\n### 一例 <span id=\"example\"></span>\n\n上記の方法をさらに説明するために一つの例を挙げましょう。\n\nあなたのアプリケーションが二つのページ、X と Y を持つと仮定します。ページ X はアセット・バンドル A、B、C を使用し、ページ Y はアセット・バンドル B、C、D を使用します。\n\nこれらのアセット・バンドルを分割する方法は二つあります。一つは単一のグループを使用して全てのアセット・バンドルを含める方法です。\nもう一つは、A をグループ X に入れ、D をグループ Y に入れ、そして、B と C をグループ S に入れる方法です。\nどちらが良いでしょう? 場合によります。\n最初の方法の利点は、二つのページが同一の結合された CSS と JavaScript のファイルを共有するため、HTTP キャッシュの効果が高くなることです。\nその一方で、単一のグループが全てのバンドルを含むために、結合された CSS と JavaScript のファイルはより大きくなり、従って最初のファイル転送時間はより長くなります。\nこの例では話を簡単にするために、最初の方法、すなわち、全てのバンドルを含む単一のグループを使用することにします。\n\n> Info: アセット・バンドルをグループに分けることは些細な仕事ではありません。通常、そのためには、いろいろなページのさまざまなアセットの現実世界での転送量を分析することが必要になります。\n  とりあえず、最初は、簡単にするために、単一のグループから始めて良いでしょう。\n\n既存のツール (例えば [Closure Compiler](https://developers.google.com/closure/compiler/) や [YUI Compressor](https://github.com/yui/yuicompressor/)) を使って、\n全てのバンドルにある CSS と JavaScript のファイルを結合して圧縮します。\nファイルは、バンドル間の依存関係を満たす順序に従って結合しなければならないことに注意してください。\n例えば、バンドル A が B に依存し、B が C と D の両方に依存している場合は、アセット・ファイルの結合順は、\n最初に C と D、次に B、そして最後に A としなければなりません。\n\n結合と圧縮が完了すると、一つの CSS ファイルと一つの JavaScript ファイルが得られます。\nそれらは、`all-xyz.css` および `all-xyz.js` と命名されたとしましょう。\nここで `xyz` は、ファイル名をユニークにして HTTP キャッシュの問題を避けるために使用されるタイムスタンプまたはハッシュを表します。\n\nいよいよ最終ステップです。\nアプリケーションの構成情報の中で、[[yii\\web\\AssetManager|アセット・マネージャ]] を次のように構成します。\n\n```php\nreturn [\n    'components' => [\n        'assetManager' => [\n            'bundles' => [\n                'all' => [\n                    'class' => 'yii\\web\\AssetBundle',\n                    'basePath' => '@webroot/assets',\n                    'baseUrl' => '@web/assets',\n                    'css' => ['all-xyz.css'],\n                    'js' => ['all-xyz.js'],\n                ],\n                'A' => ['css' => [], 'js' => [], 'depends' => ['all']],\n                'B' => ['css' => [], 'js' => [], 'depends' => ['all']],\n                'C' => ['css' => [], 'js' => [], 'depends' => ['all']],\n                'D' => ['css' => [], 'js' => [], 'depends' => ['all']],\n            ],\n        ],\n    ],\n];\n```\n\n[アセット・バンドルをカスタマイズする](#customizing-asset-bundles) の項で説明したように、上記の構成によって元のバンドルは全てデフォルトの振る舞いを変更されます。\n具体的にいえば、バンドル A、B、C、D は、もはやアセット・ファイルを一つも持っていません。\nこの4つは、それぞれ、結合された `all-xyz.css` と `all-xyz.js` ファイルを持つ `all` バンドルに依存するようになりました。\n結果として、ページ X では、バンドル A、B、C から元のソース・ファイルをインクルードする代りに、これら二つの結合されたファイルだけがインクルードされます。\n同じことはページ Y でも起ります。\n\n最後にもう一つ、上記の方法をさらにスムーズに運用するためのトリックがあります。\nアプリケーションの構成情報ファイルを直接修正する代りに、バンドルのカスタマイズ用の配列を独立したファイルに置いて、条件によってそのファイルをアプリケーションの構成情報にインクルードすることが出来ます。\n例えば、\n\n```php\nreturn [\n    'components' => [\n        'assetManager' => [\n            'bundles' => require __DIR__ . '/' . (YII_ENV_PROD ? 'assets-prod.php' : 'assets-dev.php'),  \n        ],\n    ],\n];\n```\n\nつまり、アセット・バンドルの構成情報配列は、本番モードのものは `assets-prod.php` に保存し、\n開発モードのものは `assets-dev.php` に保存するという訳です。\n\n> Note: このアセット結合のメカニズムは、登録されるアセット・バンドルのプロパティをオーバーライドできるという [[yii\\web\\AssetManager::bundles]] の機能に基づいています。\n  しかし、既に上で述べたように、この機能は、[[yii\\web\\AssetBundle::init()]]\n  メソッドの中やバンドルが登録された後で実行されるアセット・バンドルの修正をカバーしていません。\n  そのような動的なバンドルの使用は、アセット結合をする際には避けなければなりません。\n\n\n### `asset` コマンドを使う <span id=\"using-asset-command\"></span>\n\nYii は、たった今説明した方法を自動化するための `asset` という名前のコンソール・コマンドを提供しています。\n\nこのコマンドを使うためには、最初に構成情報ファイルを作成して、どのアセット・バンドルが結合されるか、\nそして、それらがどのようにグループ化されるかを記述しなければなりません。\n`asset/template` サブ・コマンドを使って最初にテンプレートを生成し、それをあなたの要求に合うように修正することが出来ます。\n\n```\nyii asset/template assets.php\n```\n\n上記のコマンドは、カレント・ディレクトリに `assets.php` というファイルを生成します。このファイルの内容は以下のようなものです。\n\n```php\n<?php\n/**\n * \"yii asset\" コンソール・コマンドのための構成情報ファイル\n * コンソール環境では、'@webroot' や '@web' のように、存在しないパス・エイリアスがあり得ることに注意してください。\n * これらの欠落したパス・エイリアスは手作業で定義してください。\n */\nreturn [\n    // JavaScript ファイルの圧縮のためのコマンド/コールバックを調整。\n    'jsCompressor' => 'java -jar compiler.jar --js {from} --js_output_file {to}',\n    // CSS ファイルの圧縮のためのコマンド/コールバックを調整。\n    'cssCompressor' => 'java -jar yuicompressor.jar --type css {from} -o {to}',\n    // 圧縮後にアセットのソースを削除するかどうか。\n    'deleteSource' => false,\n    // 圧縮するアセット・バンドルのリスト。\n    'bundles' => [\n        // 'yii\\web\\YiiAsset',\n        // 'yii\\web\\JqueryAsset',\n    ],\n    // 圧縮出力用のアセット・バンドル。\n    'targets' => [\n        'all' => [\n            'class' => 'yii\\web\\AssetBundle',\n            'basePath' => '@webroot/assets',\n            'baseUrl' => '@web/assets',\n            'js' => 'js/all-{hash}.js',\n            'css' => 'css/all-{hash}.css',\n        ],\n    ],\n    // アセット・マネージャの構成情報\n    'assetManager' => [\n    ],\n];\n```\n\nこのファイルを修正して、どのバンドルを結合するつもりであるかを `bundles` オプションで指定しなければなりません。\n`targets` オプションでは、バンドルがどのようにグループに分割されるかを指定しなければなりません。\n既に述べたように、一つまたは複数のグループを定義することが出来ます。\n\n> Note: パス・エイリアス `@webroot` および `@web` はコンソール・アプリケーションでは利用できませんので、\n  これらは構成情報の中で明示的に定義しなければなりません。\n\nJavaScript ファイルは結合され、圧縮されて `js/all-{hash}.js` に保存されます。ここで {hash} は、\n結果として作られたファイルのハッシュで置き換えられるものです。\n\n`jsCompressor` と `cssCompressor` のオプションは、JavaScript と CSS の結合/圧縮を実行するコンソール・コマンドまたは PHP コールバックを指定するものです。\nデフォルトでは、Yii は JavaScript ファイルの結合に [Closure Compiler](https://developers.google.com/closure/compiler/) を使い、\nCSS ファイルの結合に [YUI Compressor](https://github.com/yui/yuicompressor/) を使用します。\nあなたの好みのツールを使うためには、手作業でツールをインストールしたり、オプションを修正したりしなければなりません。\n\n\nこの構成情報ファイルを使い、`asset` コマンドを走らせて、アセット・ファイルを結合して圧縮し、\n同時に、新しいアセット・バンドルの構成情報ファイル `assets-prod.php` を生成することが出来ます。\n \n```\nyii asset assets.php config/assets-prod.php\n```\n\n直前の項で説明したように、\nこの生成された構成情報ファイルをアプリケーションの構成情報にインクルードすることが出来ます。\n\n> Note: アプリケーションのアセット・バンドルを [[yii\\web\\AssetManager::bundles]] または [[yii\\web\\AssetManager::assetMap]] によってカスタマイズしており、\nそのカスタマイズを圧縮のソース・ファイルにも適用したい場合は、それらのオプションを `asset` コマンドの構成ファイルの\n`assetManager` のセクションに含めなければいけません。\n\n> Note: 圧縮のソースを指定する場合は、パラメータが動的に (例えば `init()` メソッドの中や登録後に) 修正されるアセット・バンドルを避けなければなりません。\n  なぜなら、パラメータの動的な修正は、圧縮後は正しく働かない可能性があるからです。\n\n\n> Info: `asset` コマンドを使うことは、アセットの結合・圧縮のプロセスを自動化する唯一の選択肢ではありません。\n  優秀なタスク実行ツールである [grunt](https://gruntjs.com/) を使っても、同じ目的を達することが出来ます。\n\n\n### アセット・バンドルをグループ化する <span id=\"grouping-asset-bundles\"></span>\n\n直前の項において、全てのアセット・バンドルを一つに結合して、アプリケーションで参照されるアセット・ファイルに対する HTTP リクエストを最小化する方法を説明しました。\n現実には、それが常に望ましいわけではありません。\n例えば、あなたのアプリケーションがフロントエンドとバックエンドを持っており、\nそれぞれが異なる一群の JavaScript と CSS ファイルを使う場合を想像してください。\nこの場合、両方の側の全てのアセット・バンドルを一つに結合するのは合理的ではありません。\n何故なら、フロントエンドのためのアセット・バンドルはバックエンドでは使用されませんから、フロントエンドのページがリクエストされているときにバックエンドのアセットを送信するのはネットワーク帯域の浪費です。\n\n上記の問題を解決するために、アセット・バンドルをグループ化して、グループごとにアセット・バンドルを結合することが出来ます。\n下記の構成情報は、アセット・バンドルをグループ化する方法を示すものです。\n\n```php\nreturn [\n    ...\n    // 出力されるバンドルをグループ化する\n    'targets' => [\n        'allShared' => [\n            'js' => 'js/all-shared-{hash}.js',\n            'css' => 'css/all-shared-{hash}.css',\n            'depends' => [\n                // バックエンドとフロントエンドで共有される全てのアセットを含める\n                'yii\\web\\YiiAsset',\n                'app\\assets\\SharedAsset',\n            ],\n        ],\n        'allBackEnd' => [\n            'js' => 'js/all-{hash}.js',\n            'css' => 'css/all-{hash}.css',\n            'depends' => [\n                // バックエンドだけのアセットを含める\n                'app\\assets\\AdminAsset'\n            ],\n        ],\n        'allFrontEnd' => [\n            'js' => 'js/all-{hash}.js',\n            'css' => 'css/all-{hash}.css',\n            'depends' => [], // 残りの全てのアセットを含める\n        ],\n    ],\n    ...\n];\n```\n\nご覧のように、アセット・バンドルは三つのグループ、すなわち、`allShared`、`allBackEnd` および `allFrontEnd` に分けられています。\nそして、それぞれが適切な一群のアセット・バンドルに依存しています。例えば、`allBackEnd` は `app\\assets\\AdminAsset` に依存しています。\nこの構成情報によって `asset` コマンドを実行すると、上記の定義に従ってアセット・バンドルが結合されます。\n\n> Info: ターゲット・バンドルのうちの一つについて `depends` の構成を空のままにしておくことが出来ます。\n  そのようにすると、他のターゲット・バンドルが依存しないために残された全てのアセット・バンドルが、このターゲット・バンドルに含まれるようになります。\n"
  },
  {
    "path": "docs/guide-ja/structure-controllers.md",
    "content": "コントローラ\n============\n\nコントローラは [MVC](https://ja.wikipedia.org/wiki/Model_View_Controller) アーキテクチャの一部を構成するものです。\nそれは [[yii\\base\\Controller]] を拡張したクラスのオブジェクトであり、リクエストの処理とレスポンスの生成について責任を負います。\n具体的には、コントローラは、[アプリケーション](structure-applications.md) から制御を引き継いだ後、\n入ってきたリクエストのデータを分析し、それを [モデル](structure-models.md) に引き渡して、\nモデルが生成した結果を [ビュー](structure-views.md) に投入し、最終的に外に出て行くレスポンスを生成します。\n\n\n## アクション <span id=\"actions\"></span>\n\nコントローラは、エンド・ユーザがアドレスを指定して実行をリクエストできる最も基本的なユニットである *アクション* から構成されます。\nコントローラは一つまたは複数のアクションを持つことが出来ます。\n\n次の例は、`view` と `create` という二つのアクションを持つ `post` コントローラを示すものです。\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse app\\models\\Post;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\n\nclass PostController extends Controller\n{\n    public function actionView($id)\n    {\n        $model = Post::findOne($id);\n        if ($model === null) {\n            throw new NotFoundHttpException;\n        }\n\n        return $this->render('view', [\n            'model' => $model,\n        ]);\n    }\n\n    public function actionCreate()\n    {\n        $model = new Post;\n\n        if ($model->load(Yii::$app->request->post()) && $model->save()) {\n            return $this->redirect(['view', 'id' => $model->id]);\n        } else {\n            return $this->render('create', [\n                'model' => $model,\n            ]);\n        }\n    }\n}\n```\n\n`view` アクション (`actionView()` メソッドで定義されます) において、コードは最初に、リクエストされたモデルの ID に従って [モデル](structure-models.md) を読み出します。\nモデルの読み出しが成功したときは、`view` という名前の [ビュー](structure-views.md) を使ってモデルを表示します。\n失敗したときは例外を投げます。\n\n`create` アクション (`actionCreate()` メソッドで定義されます) においても、コードは似たようなものです。\n最初に、リクエスト・データを使って [モデル](structure-models.md) の新しいインスタンスにデータを投入することを試み、そして、モデルを保存することを試みます。\n両方が成功したときは、新しく作成されたモデルの ID を使って `view` アクションにブラウザをリダイレクトします。\nどちらかが失敗したときは、ユーザが必要なデータを入力できるようにするための `create` ビューを表示します。\n\n\n## ルート <span id=\"routes\"></span>\n\nエンド・ユーザは、いわゆる *ルート* によって、アクションを指定します。ルートは、次の部分からなる文字列です。\n\n* モジュール ID: この部分は、コントローラがアプリケーションではない [モジュール](structure-modules.md) に属する場合にのみ存在します。\n* [コントローラ ID]((#controller-ids): 同じアプリケーション (または、コントローラがモジュールに属する場合は、同じモジュール)\n  に属する全てのコントローラの中から、コントローラを一意に特定する文字列。\n* [アクション ID](#action-ids): 同じコントローラに属する全てのアクションの中から、アクションを一意に特定する文字列。\n\nルートは次の形式を取ります。\n\n```\nControllerID/ActionID\n```\n\nまたは、コントローラがモジュールに属する場合は、次の形式を取ります。\n\n```php\nModuleID/ControllerID/ActionID\n```\n\nですから、ユーザが `https://hostname/index.php?r=site/index` という URL でリクエストをした場合は、\n`site` コントローラの中の `index` アクションが実行されます。\nルートがどのようにしてアクションとして解決されるかについての詳細は、[ルーティングと URL 生成](runtime-routing.md) のセクションを参照してください。\n\n\n## コントローラを作成する <span id=\"creating-controllers\"></span>\n\n[[yii\\web\\Application|ウェブ・アプリケーション]] では、コントローラは [[yii\\web\\Controller]] またはその子クラスから派生させなければなりません。\n同様に、[[yii\\console\\Application|コンソール・アプリケーション]] では、コントローラは [[yii\\console\\Controller]] またはその子クラスから派生させなければなりません。\n次のコードは `site` コントローラを定義するものです。\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n}\n```\n\n\n### コントローラ ID <span id=\"controller-ids\"></span>\n\n通常、コントローラは特定のタイプのリソースに関するリクエストを処理するように設計されます。\nこの理由により、たいていは、処理するリソースのタイプを示す名詞をコントローラの ID として使います。\n例えば、記事データを処理するコントローラの ID としては、`article` を使うことが出来ます。\n\nデフォルトでは、コントローラ ID は、小文字の英字、数字、アンダースコア、ダッシュ、および、フォワード・スラッシュのみを含むべきものです。\n例えば、`article` と `post-comment` はともに有効なコントローラ ID ですが、\n`article?`、`PostComment`、`admin\\post` はそうではありません。\n\nコントローラ ID は、サブ・ディレクトリの接頭辞を含んでも構いません。\n例えば、`admin/article` は、[[yii\\base\\Application::controllerNamespace|コントローラ名前空間]] の下の `admin` サブ・ディレクトリにある `article` コントローラを表します。\nサブ・ディレクトリの接頭辞として有効な文字は、小文字または大文字の英字、数字、アンダースコア、そして、フォワード・スラッシュです。\nフォワード・スラッシュは、複数レベルのサブ・ディレクトリの区切り文字として使われます (例えば、`panels/admin`)。\n\n\n### コントローラ・クラスの命名規則 <span id=\"controller-class-naming\"></span>\n\nコントローラ・クラスの名前は下記の手順に従ってコントローラ ID から導出することが出来ます。\n\n1. ハイフンで区切られた各単語の最初の文字を大文字に変える。\n   コントローラ ID がスラッシュを含む場合、この規則は ID の最後のスラッシュの後ろの部分にのみ適用されることに注意。\n2. ハイフンを削除し、フォワード・スラッシュを全てバックワード・スラッシュに置き換える。\n3. 接尾辞 `Controller` を追加する。\n4. [[yii\\base\\Application::controllerNamespace|コントローラ名前空間]] を頭に付ける。\n\n以下は、[[yii\\base\\Application::controllerNamespace|コントローラ名前空間]] がデフォルト値 `app\\controllers`\nを取っていると仮定したときの、いくつかの例です。\n\n* `article` は `app\\controllers\\ArticleController` になる。\n* `post-comment` は `app\\controllers\\PostCommentController` になる。\n* `admin/post-comment` は `app\\controllers\\admin\\PostCommentController` になる。\n* `adminPanels/post-comment` は `app\\controllers\\adminPanels\\PostCommentController` になる。\n\nコントローラ・クラスは [オートロード可能](concept-autoloading.md) でなければなりません。\nこの理由により、上記の例の `aritcle` コントローラ・クラスは [エイリアス](concept-aliases.md) が\n`@app/controllers/ArticleController.php` であるファイルに保存されるべきものとなります。\n一方、`admin/post-comment` コントローラは `@app/controllers/admin/PostCommentController.php` というエイリアスのファイルに保存されるべきものとなります。\n\n> Info: 最後の例である `admin/post-comment` は、どうすれば [[yii\\base\\Application::controllerNamespace|コントローラ名前空間]]\nのサブ・ディレクトリにコントローラを置くことが出来るかを示しています。\n  この方法は、コントローラをいくつかのカテゴリに分けて編成したい、けれども [モジュール](structure-modules.md) は使いたくない、という場合に役立ちます。\n\n\n### コントローラ・マップ <span id=\"controller-map\"></span>\n\n[[yii\\base\\Application::controllerMap|コントローラ・マップ]] を構成すると、上で述べたコントローラ ID\nとクラス名の制約を乗り越えることが出来ます。\nこれは、主として、クラス名に対する制御が及ばないサード・パーティのコントローラを使おうとする場合に有用です。\n\n[[yii\\base\\Application::controllerMap|コントローラ・マップ]] は [アプリケーションの構成情報](structure-applications.md#application-configurations)\nの中で、次のように構成することが出来ます。\n\n```php\n[\n    'controllerMap' => [\n        // クラス名を使って \"account\" コントローラを宣言する\n        'account' => 'app\\controllers\\UserController',\n\n        // 構成情報配列を使って \"article\" コントローラを宣言する\n        'article' => [\n            'class' => 'app\\controllers\\PostController',\n            'enableCsrfValidation' => false,\n        ],\n    ],\n]\n```\n\n\n### デフォルト・コントローラ <span id=\"default-controller\"></span>\n\n全てのアプリケーションは、それぞれ、[[yii\\base\\Application::defaultRoute]] プロパティによって指定されるデフォルト・コントローラを持ちます。\nリクエストが [ルート](#routes) を指定していない場合、このプロパティによって指定されたルートが使われます。\n[[yii\\web\\Application|ウェブ・アプリケーション]] では、この値は `'site'` であり、一方、[[yii\\console\\Application|コンソール・アプリケーション]] では、`help` です。\n従って、URL が `https://hostname/index.php` である場合は、`site` コントローラがリクエストを処理することになります。\n\n次のように [アプリケーションの構成情報](structure-applications.md#application-configurations) を構成して、デフォルト・コントローラを変更することが出来ます。\n\n```php\n[\n    'defaultRoute' => 'main',\n]\n```\n\n\n## アクションを作成する <span id=\"creating-actions\"></span>\n\nアクションは、コントローラ・クラスの中にいわゆる *アクション・メソッド* を定義するだけで簡単に作成することが出来ます。\nアクション・メソッドとは、`action` という語で始まる名前を持つ *public* メソッドのことです。\nアクション・メソッドの返り値がエンド・ユーザに送信されるレスポンス・データを表します。次のコードは、`index` と `hello-world` という二つのアクションを定義するものです。\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actionIndex()\n    {\n        return $this->render('index');\n    }\n\n    public function actionHelloWorld()\n    {\n        return 'Hello World';\n    }\n}\n```\n\n\n### アクション ID <span id=\"action-ids\"></span>\n\nアクションは、たいてい、あるリソースについて特定の操作を実行するように設計されます。\nこの理由により、アクション ID は、通常、`view`、`update` などのような動詞になります。\n\nデフォルトでは、アクション ID は、小文字の英字、数字、アンダースコア、そして、ハイフンのみを含むべきものです。\nアクション ID の中のハイフンは単語を分けるために使われます。\n例えば、`view`、`update2`、`comment-post` は全て有効なアクション ID ですが、`view?`、`Update` はそうではありません。\n\nアクションは二つの方法、すなわち、インライン・アクションまたはスタンドアロン・アクションとして作成することが出来ます。\nインライン・アクションはコントローラ・クラスのメソッドとして定義されるものであり、\n一方、スタンドアロン・アクションは [[yii\\base\\Action]] またはその子クラスを拡張するクラスです。\nインライン・アクションは作成するのにより少ない労力を要するため、通常は、アクションを再利用する意図がない場合に推奨されます。\nもう一方のスタンドアロン・アクションは、主として、さまざまなコントローラの中で使われることや、[エクステンション](structure-extensions.md) として再配布されることを目的として作成されます。\n\n\n### インライン・アクション <span id=\"inline-actions\"></span>\n\nインライン・アクションは、たった今説明したように、アクション・メソッドの形で定義されるアクションを指します。\n\nアクション・メソッドの名前は、次の手順に従って、アクション ID から導出されます。\n\n1. アクション ID に含まれる各単語の最初の文字を大文字に変換する。\n2. ハイフンを削除する。\n3. 接頭辞 `action` を付ける。\n\n例えば、`index` は `actionIndex` となり、`hello-world` は `actionHelloWorld` となります。\n\n> Note: アクション・メソッドの名前は、*大文字と小文字を区別* します。\n  `ActionIndex` という名前のメソッドがあっても、それはアクション・メソッドとは見なされず、結果として、`index` アクションに対するリクエストは例外に帰結します。\n  アクション・メソッドが public でなければならない事にも注意してください。\n  private や protected なメソッドがインライン・アクションを定義することはありません。\n\n\nインライン・アクションは作成するのにほとんど労力を要さないため、たいていのアクションはインライン・アクションとして定義されます。\nしかし、同じアクションを別の場所で再利用する計画を持っていたり、また、アクションを再配布したいと思っていたりする場合は、\nアクションを *スタンドアロン・アクション* として定義することを検討すべきです。\n\n\n### スタンドアロン・アクション <span id=\"standalone-actions\"></span>\n\nスタンドアロン・アクションは、[[yii\\base\\Action]] またはその子クラスを拡張するアクション・クラスの形で定義されるものです。\n例えば、Yii のリリースに [[yii\\web\\ViewAction]] と [[yii\\web\\ErrorAction]] が含まれていますが、\nこれらは両方ともスタンドアロン・アクションです。\n\nスタンドアロン・アクションを使用するためには、下記のように、コントローラの [[yii\\base\\Controller::actions()]] メソッドをオーバーライドして、\n*アクション・マップ* の中でスタンドアロン・アクションを宣言しなければなりません。\n\n```php\npublic function actions()\n{\n    return [\n        // クラス名を使って \"error\" アクションを宣言する\n        'error' => 'yii\\web\\ErrorAction',\n\n        // 構成情報配列を使って \"view\" アクションを宣言する\n        'view' => [\n            'class' => 'yii\\web\\ViewAction',\n            'viewPrefix' => '',\n        ],\n    ];\n}\n```\n\nご覧のように、`actions()` メソッドは、キーがアクション ID であり、値が対応するアクションのクラス名または\n[構成情報](concept-configurations.md) である配列を返さなければなりません。\nインライン・アクションと違って、スタンドアロン・アクションのアクション ID は、`actions()` メソッドにおいて宣言される限りにおいて、任意の文字を含むことが出来ます。\n\nスタンドアロン・アクション・クラスを作成するためには、[[yii\\base\\Action]] またはその子クラスを拡張して、`run()` という名前の public メソッドを実装しなければなりません。\n`run()` メソッドの役割はアクション・メソッドの役割と同じです。例えば、\n\n```php\n<?php\nnamespace app\\components;\n\nuse yii\\base\\Action;\n\nclass HelloWorldAction extends Action\n{\n    public function run()\n    {\n        return \"Hello World\";\n    }\n}\n```\n\n\n### アクションの結果 <span id=\"action-results\"></span>\n\nアクション・メソッド、または、スタンドアロン・アクションの `run()` メソッドの返り値は、重要な意味を持ちます。\nそれは、対応するアクションの結果を表すものです。\n\n返り値は、エンド・ユーザにレスポンスとして送信される [レスポンス](runtime-responses.md) オブジェクトとすることが出来ます。\n\n* [[yii\\web\\Application|ウェブ・アプリケーション]] では、返り値を [[yii\\web\\Response::data]] に割り当てられる任意のデータとすることも出来ます。\n  このデータは、後に、レスポンス・ボディを表す文字列へと変換されます。\n* [[yii\\console\\Application|コンソール・アプリケーション]] では、返り値をコマンド実行の\n  [[yii\\console\\Response::exitStatus|終了ステータス]] を示す整数とすることも出来ます。\n\nこれまでに示した例においては、アクションの結果はすべて文字列であり、エンド・ユーザに送信されるレスポンス・ボディとして扱われるものでした。\n次の例では、アクションがレスポンス・オブジェクトを返すことによって、ユーザのブラウザを\n新しい URL にリダイレクトすることが出来る様子が示されています\n([[yii\\web\\Controller::redirect()|redirect()]] メソッドの返り値はレスポンス・オブジェクトです)。\n\n```php\npublic function actionForward()\n{\n    // ユーザのブラウザを https://example.com にリダイレクトする\n    return $this->redirect('https://example.com');\n}\n```\n\n\n### アクション・パラメータ <span id=\"action-parameters\"></span>\n\nインライン・アクションのアクション・メソッドと、スタンドアロン・アクションの `run()` メソッドは、*アクション・パラメータ* と呼ばれるパラメータを取ることが出来ます。\nパラメータの値はリクエストから取得されます。\n[[yii\\web\\Application|ウェブ・アプリケーション]] では、各アクション・パラメータの値は `$_GET` からパラメータ名をキーとして読み出されます。\n[[yii\\console\\Application|コンソール・アプリケーション]] では、アクション・パラメータはコマンドライン引数に対応します。\n\n次の例では、`view` アクション (インライン・アクションです) は、二つのパラメータ、すなわち、`$id` と `$version` を宣言しています。\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass PostController extends Controller\n{\n    public function actionView($id, $version = null)\n    {\n        // ...\n    }\n}\n```\n\nアクション・パラメータには、次のように、さまざまなリクエストに応じて異なる値が投入されます。\n\n* `https://hostname/index.php?r=post/view&id=123`: `$id` パラメータには `'123'` という値が入れられます。\n  一方、`version` というクエリ・パラメータは無いので、`$version` は `null` のままになります。\n* `https://hostname/index.php?r=post/view&id=123&version=2`: `$id` および `$version` パラメータに、\n  それぞれ、`'123'` と `'2'` が入ります。\n* `https://hostname/index.php?r=post/view`: 必須の `$id` パラメータがリクエストで提供されていないため、\n  [[yii\\web\\BadRequestHttpException]] 例外が投げられます。\n* `https://hostname/index.php?r=post/view&id[]=123`: `$id` パラメータが予期しない配列値 `['123']` を受け取ろうとするため、\n  [[yii\\web\\BadRequestHttpException]] 例外が投げられます。\n\nアクション・パラメータに配列値を受け取らせたい場合は、次のように、パラメータに `array` の型ヒントを付けなければなりません。\n\n```php\npublic function actionView(array $id, $version = null)\n{\n    // ...\n}\n```\n\nこのようにすると、リクエストが `https://hostname/index.php?r=post/view&id[]=123` である場合は、`$id` パラメータは `['123']` という値を受け取ります。\nリクエストが `https://hostname/index.php?r=post/view&id=123` である場合も、スカラ値 `'123'` が自動的に配列に変換されるため、\n`$id` パラメータは引き続き同じ配列値を受け取ります。\n\n上記の例は主としてウェブ・アプリケーションでのアクション・パラメータの動作を示すものです。\nコンソール・アプリケーションについては、[コンソール・コマンド](tutorial-console.md) のセクションで詳細を参照してください。\n\n\n### デフォルト・アクション <span id=\"default-action\"></span>\n\nすべてのコントローラは、それぞれ、[[yii\\base\\Controller::defaultAction]] によって指定されるデフォルト・アクションを持ちます。\n[ルート](#routes) がコントローラ ID のみを含む場合は、\n指定されたコントローラのデフォルト・アクションがリクエストされたことを意味します。\n\nデフォルトでは、デフォルト・アクションは `index` と設定されます。\nこのデフォルト値を変更したい場合は、以下のように、コントローラ・クラスでこのプロパティをオーバーライドするだけです。\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public $defaultAction = 'home';\n\n    public function actionHome()\n    {\n        return $this->render('home');\n    }\n}\n```\n\n\n## コントローラのライフサイクル <span id=\"controller-lifecycle\"></span>\n\nリクエストを処理するときに、[アプリケーション](structure-applications.md) はリクエストされた [ルート](#routes)\nに基いてコントローラを作成します。\nそして、次に、コントローラはリクエストに応じるために以下のライフサイクルを経過します。\n\n1. コントローラが作成され構成された後、[[yii\\base\\Controller::init()]] メソッドが呼ばれる。\n2. コントローラは、リクエストされたアクション ID に基いて、アクション・オブジェクトを作成する。\n   * アクション ID が指定されていないときは、[[yii\\base\\Controller::defaultAction|デフォルト・アクション ID]] が使われる。\n   * アクション ID が [[yii\\base\\Controller::actions()|アクション・マップ]] の中に見つかった場合は、\n     スタンドアロン・アクションが作成される。\n   * アクション ID に合致するアクション・メソッドが見つかった場合は、インライン・アクションが作成される。\n   * 上記以外の場合は、[[yii\\base\\InvalidRouteException]] 例外が投げられる。\n3. コントローラは、アプリケーション、(コントローラがモジュールに属する場合は) モジュール、\n  そしてコントローラの `beforeAction()` メソッドをこの順で呼び出す。\n   * どれか一つの呼び出しが `false` を返した場合は、残りのまだ呼ばれていない `beforeAction()` メソッドはスキップされ、\n     アクションの実行はキャンセルされる。\n   * デフォルトでは、それぞれの `beforeAction()` メソッドは、ハンドラをアタッチすることが可能な `beforeAction` イベントをトリガする。\n4. コントローラがアクションを実行する。\n   * アクション・パラメータが解析されて、リクエスト・データからデータが投入される。\n5. コントローラは、コントローラ、(コントローラがモジュールに属する場合は) モジュール、\n     そしてアプリケーションの `afterAction()` メソッドをこの順で呼び出す。\n   * デフォルトでは、それぞれの `afterAction()` メソッドは、ハンドラをアタッチすることが可能な `afterAction` イベントをトリガする。\n6. アプリケーションはアクションの結果を受け取り、それを [レスポンス](runtime-responses.md) に割り当てる。\n\n\n## ベスト・プラクティス <span id=\"best-practices\"></span>\n\n良く設計されたアプリケーションでは、コントローラはたいてい非常に軽いものになり、\nそれぞれのアクションは数行のコードしか含まないものになります。\nあなたのコントローラが少々複雑になっている場合、そのことは、通常、コントローラをリファクタして、コードの一部を他のクラスに移動すべきことを示すものです。\n\nいくつかのベスト・プラクティスを特に挙げるなら、コントローラは、\n\n* [リクエスト](runtime-requests.md) データにアクセスすることが出来ます。\n* リクエスト・データを使って [モデル](structure-models.md) や他のサービス・コンポーネントのメソッドを呼ぶことが出来ます。\n* [ビュー](structure-views.md) を使ってレスポンスを構成することが出来ます。\n* リクエストされたデータの処理をするべきではありません - データは [モデルのレイヤ](structure-models.md) において処理されるべきです。\n* HTML を埋め込むなどの表示に関わるコードは避けるべきです - 表示は [ビュー](structure-views.md) で行う方が良いです。\n"
  },
  {
    "path": "docs/guide-ja/structure-entry-scripts.md",
    "content": "エントリ・スクリプト\n==================\n\nエントリ・スクリプトは、アプリケーションのブートストラップの過程における最初のステップです。\nアプリケーションは (ウェブ・アプリケーションであれ、コンソール・アプリケーションであれ）単一のエントリ・スクリプトを持ちます。\nエンド・ユーザはエントリ・スクリプトに対してリクエストを発行し、エントリ・スクリプトはアプリケーションのインスタンスを作成して、それにリクエストを送付します。\n\nウェブ・アプリケーションのエントリ・スクリプトは、エンド・ユーザからアクセス出来るように、\nウェブからのアクセスが可能なディレクトリの下に保管されなければなりません。\nたいていは `index.php` と名付けられますが、ウェブ・サーバが見つけることが出来る限り、どのような名前を使っても構いません。\n\nコンソール・アプリケーションのエントリ・スクリプトは、通常は、アプリケーションの [ベース・パス](structure-applications.md) の下に保管され、`yii` と名付けられます (`.php` の拡張子を伴います) 。\nこれは、ユーザが `./yii <route> [引数] [オプション]` というコマンドによってコンソール・アプリケーションを走らせることが出来るようにするためのスクリプトであり、\n実行可能なパーミッションを与えられるべきものです。\n\nエントリ・スクリプトは主として次の仕事をします。\n\n* グローバルな定数を定義する。\n* [Composer のオートローダ](https://getcomposer.org/doc/01-basic-usage.md#autoloading) を登録する。\n* [[Yii]] クラス・ファイルをインクルードする。\n* アプリケーションの構成情報を読み出す。\n* [アプリケーション](structure-applications.md) のインスタンスを生成して構成する。\n* [[yii\\base\\Application::run()]] を呼んで、受け取ったリクエストを処理する。\n\n\n## ウェブ・アプリケーション<span id=\"web-applications\"></span>\n\n次に示すのが、[ベーシック・ウェブ・プロジェクト・テンプレート](start-installation.md) のエントリ・スクリプトです。\n\n```php\n<?php\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n\n// Composer のオートローダを登録\nrequire __DIR__ . '/../vendor/autoload.php';\n\n// Yii クラス・ファイルをインクルード\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n\n// アプリケーションの構成情報を読み出す\n$config = require __DIR__ . '/../config/web.php';\n\n// アプリケーションを作成し、構成して、走らせる\n(new yii\\web\\Application($config))->run();\n```\n\n\n## コンソール・アプリケーション<span id=\"console-applications\"></span>\n\n同様に、下記がコンソール・アプリケーションのエントリ・スクリプトです。\n\n```php\n#!/usr/bin/env php\n<?php\n/**\n * Yii console bootstrap file.\n *\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n\n// Composer のオートローダを登録\nrequire __DIR__ . '/vendor/autoload.php';\n\n// Yii クラス・ファイルをインクルード\nrequire __DIR__ . '/vendor/yiisoft/yii2/Yii.php';\n\n// アプリケーションの構成情報を読み出す\n$config = require __DIR__ . '/config/console.php';\n\n$application = new yii\\console\\Application($config);\n$exitCode = $application->run();\nexit($exitCode);\n```\n\n\n## 定数を定義する<span id=\"defining-constants\"></span>\n\nグローバルな定数を定義するには、エントリ・スクリプトが最善の場所です。Yii は下記の三つの定数をサポートしています。\n\n* `YII_DEBUG`: アプリケーションがデバッグ・モードで走るかどうかを指定します。\n  デバッグ・モードにおいては、アプリケーションはより多くのログ情報を保持し、例外が投げられたときに、より詳細なエラーのコール・スタックを表示します。\n  この理由により、デバッグ・モードは主として開発時に使用されるべきものとなります。`YII_DEBUG` のデフォルト値は `false` です。\n* `YII_ENV`: どういう環境でアプリケーションが走っているかを指定します。\n  詳細は、[構成情報](concept-configurations.md#environment-constants) のセクションで説明されます。\n  `YII_ENV` のデフォルト値は `'prod'` であり、アプリケーションが本番環境で走っていることを意味します。\n* `YII_ENABLE_ERROR_HANDLER`: Yii によって提供されるエラー・ハンドラを有効にするかどうかを指定します。\n  この定数のデフォルト値は `true` です。\n\n定数を定義するときには、しばしば次のようなコードを用います。\n\n```php\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\n```\n\nこれは下記のコードと同じ意味のものです。\n\n```php\nif (!defined('YII_DEBUG')) {\n    define('YII_DEBUG', true);\n}\n```\n\n明らかに前者の方が簡潔で理解しやすいでしょう。\n\n他のPHP ファイルがインクルードされる時に定数の効力が生じるようにするために、\n定数はエントリ・スクリプトの冒頭で定義されなければなりません。\n"
  },
  {
    "path": "docs/guide-ja/structure-extensions.md",
    "content": "エクステンション\n================\n\nエクステンションは、Yii のアプリケーションで使われることに限定して設計され、そのまますぐに使える機能を提供する再配布可能なソフトウェア・パッケージです。\n例えば、[yiisoft/yii2-debug](https://github.com/yiisoft/yii2-debug) エクステンションは、あなたのアプリケーションにおいて、全てのページの末尾に便利なデバッグ・ツールバーを追加して、\nページが生成される過程をより容易に把握できるように手助けしてくれます。\nエクステンションを使うと、あなたの開発プロセスを加速することが出来ます。\nまた、あなたのコードをエクステンションとしてパッケージ化すると、あなたの優れた仕事を他の人たちと共有することが出来ます。\n\n> Info: 「エクステンション」という用語は Yii に限定されたソフトウェア・パッケージを指すものとして使用します。\n  Yii がなくても使用できる汎用のソフトウェア・パッケージを指すためには、「パッケージ」または「ライブラリ」という用語を使うことにします。\n\n\n## エクステンションを使う <span id=\"using-extensions\"></span>\n\nエクステンションを使うためには、先ずはそれをインストールする必要があります。\nほとんどのエクステンションは [Composer](https://getcomposer.org/) のパッケージとして配布されていて、次の二つの簡単なステップをふめばインストールすることが出来ます。\n\n1. アプリケーションの `composer.json` ファイルを修正して、どのエクステンション (Composer パッケージ) をインストールしたいかを指定する。\n2. `composer install` コマンドを走らせて指定したエクステンションをインストールする。\n\n[Composer](https://getcomposer.org/) を持っていない場合は、それをインストールする必要があることに注意してください。\n\nデフォルトでは、Composer はオープン・ソース Composer パッケージの最大のレポジトリである [Packagist](https://packagist.org/) に登録されたパッケージをインストールします。\nエクステンションは Packagist で探すことが出来ます。\nまた、[自分自身のレポジトリを作成](https://getcomposer.org/doc/05-repositories.md#repository) して、それを使うように Composer を構成することも出来ます。\nこれは、あなたがプライベートなエクステンションを開発していて、それを自分のプロジェクト間でのみ共有したい場合に役に立つ方法です。\n\nComposer によってインストールされるエクステンションは `BasePath/vendor` ディレクトリに保存されます。\nここで `BasePath` は、アプリケーションの [ベース・パス](structure-applications.md#basePath) を指します。\nComposer は依存関係を管理するものですから、あるパッケージをインストールするときには、それが依存する全てのパッケージも同時にインストールします。\n\n例えば、`yiisoft/yii2-imagine` エクステンションをインストールするためには、あなたの `composer.json` を次のように修正します。\n\n```json\n{\n    // ...\n\n    \"require\": {\n        // ... 他の依存パッケージ\n\n        \"yiisoft/yii2-imagine\": \"*\"\n    }\n}\n```\n\nインストール完了後には、`BasePath/vendor` の下に `yiisoft/yii2-imagine` ディレクトリが作られている筈です。\nそれと同時に、`imagine/imagine` という別のディレクトリも作られて、依存するパッケージがそこにインストールされている筈です。\n\n> Info: `yiisoft/yii2-imagine` は Yii 開発チームによって開発され保守されるコア・エクステンションの一つです。\n  全てのコア・エクステンションは [Packagist](https://packagist.org/) でホストされ、`yiisoft/yii2-xyz` のように名付けられます。\n  ここで `xyz` はエクステンションによってさまざまに変ります。\n\nこれであなたはインストールされたエクステンションをあなたのアプリケーションの一部であるかのように使うことが出来ます。\n次の例は、`yiisoft/yii2-imagine` エクステンションによって提供される `yii\\imagine\\Image` クラスをどのようにして使うことが出来るかを示すものです。\n\n```php\nuse Yii;\nuse yii\\imagine\\Image;\n\n// サムネール画像を生成する\nImage::thumbnail('@webroot/img/test-image.jpg', 120, 120)\n    ->save(Yii::getAlias('@runtime/thumb-test-image.jpg'), ['quality' => 50]);\n```\n\n> Info: エクステンションのクラスは [Yii クラス・オートローダ](concept-autoloading.md) によってオートロードされます。\n\n\n### エクステンションを手作業でインストールする <span id=\"installing-extensions-manually\"></span>\n\nあまり無いことですが、いくつかまたは全てのエクステンションを Composer に頼らずに手作業でインストールしたい場合があるかもしれません。\nそうするためには、次のようにしなければなりません。\n\n1. エクステンションのアーカイブ・ファイルをダウンロードして、`vendor` ディレクトリに解凍する。\n2. もし有れば、エクステンションによって提供されているクラス・オートローダをインストールする。\n3. 指示に従って、依存するエクステンションを全てダウンロードしインストールする。\n\nエクステンションがクラス・オートローダを持っていなくても、[PSR-4 標準](https://www.php-fig.org/psr/psr-4/) に従っている場合は、Yii が提供しているクラス・オートローダを使ってエクステンションのクラスをオートロードすることが出来ます。\n必要なことは、エクステンションのルート・ディレクトリのための [ルート・エイリアス](concept-aliases.md#defining-aliases) を宣言することだけです。\n例えば、エクステンションを `vendor/mycompany/myext` というディレクトリにインストールしたと仮定します。\nそして、エクステンションのクラスは `myext` 名前空間の下にあるとします。\nその場合、アプリケーションの構成情報に下記のコードを含めます。\n\n```php\n[\n    'aliases' => [\n        '@myext' => '@vendor/mycompany/myext',\n    ],\n]\n```\n\n\n## エクステンションを作成する <span id=\"creating-extensions\"></span>\n\nあなたの優れたコードを他の人々と共有する必要があると感じたときは、エクステンションを作成することを考慮するのが良いでしょう。\nエクステンションは、ヘルパ・クラス、ウィジェット、モジュールなど、どのようなコードでも含むことが出来ます。\n\nエクステンションは、[Composer パッケージ](https://getcomposer.org/) の形式で作成することが推奨されます。\nそうすれば、直前の項で説明したように、いっそう容易に他のユーザによってインストールされ、使用されることが出来ます。\n\n以下は、エクステンションを Composer のパッケージとして作成するために踏む基本的なステップです。\n\n1. エクステンションのためのプロジェクトを作成して、[github.com](https://github.com) などの VCS レポジトリ上でホストします。\n   エクステンションに関する開発と保守の作業はこのレポジトリ上でしなければなりません。\n2. プロジェクトのルート・ディレクトリに、Composer によって要求される `composer.json` という名前のファイルを作成します。\n   詳細については、次の項を参照してください。\n3. エクステンションを [Packagist](https://packagist.org/) などの Composer レポジトリに登録します。\n   そうすると、他のユーザがエクステンションを見つけて Composer を使ってインストールすることが出来るようになります。\n\n\n### `composer.json` <span id=\"composer-json\"></span>\n\n全ての Composer パッケージは、ルート・ディレクトリに `composer.json` というファイルを持たなければなりません。このファイルはパッケージに関するメタデータを含むものです。\nこのファイルに関する完全な仕様は [Composer Manual](https://getcomposer.org/doc/01-basic-usage.md#composer-json-project-setup) に記載されています。\n次の例は、`yiisoft/yii2-imagine` エクステンションのための `composer.json` ファイルを示すものです。\n\n```json\n{\n    // パッケージ名\n    \"name\": \"yiisoft/yii2-imagine\",\n\n    // パッケージタイプ\n    \"type\": \"yii2-extension\",\n\n    \"description\": \"The Imagine integration for the Yii framework\",\n    \"keywords\": [\"yii2\", \"imagine\", \"image\", \"helper\"],\n    \"license\": \"BSD-3-Clause\",\n    \"support\": {\n        \"issues\": \"https://github.com/yiisoft/yii2/issues?labels=ext%3Aimagine\",\n        \"forum\": \"https://forum.yiiframework.com/\",\n        \"wiki\": \"https://www.yiiframework.com/wiki/\",\n        \"irc\": \"ircs://irc.libera.chat:6697/yii\",\n        \"source\": \"https://github.com/yiisoft/yii2\"\n    },\n    \"authors\": [\n        {\n            \"name\": \"Antonio Ramirez\",\n            \"email\": \"amigo.cobos@gmail.com\"\n        }\n    ],\n\n    // 依存パッケージ\n    \"require\": {\n        \"yiisoft/yii2\": \"~2.0.0\",\n        \"imagine/imagine\": \"v0.5.0\"\n    },\n\n    // クラスのオートロードの仕様\n    \"autoload\": {\n        \"psr-4\": {\n            \"yii\\\\imagine\\\\\": \"\"\n        }\n    }\n}\n```\n\n\n#### パッケージ名 <span id=\"package-name\"></span>\n\n全ての Composer パッケージは、他の全てパッケージと異なる一意に特定できる名前を持たなければなりません。\nパッケージ名の形式は `vendorName/projectName` です。\n例えば、`yiisoft/yii2-imagine` というパッケージ名の中では、ベンダー名とプロジェクト名は、それぞれ、`yiisoft` と `yii2-imagine` です。\n\nベンダー名として `yiisoft` を使ってはいけません。これは Yii のコア・コードに使うために予約されています。\n\nプロジェクト名には、Yii 2 エクステンションを表す `yii2-` を前置することを推奨します。例えば、`myname/yii2-mywidget` です。\nこのようにすると、ユーザはパッケージが Yii 2 エクステンションであることをより容易に知ることが出来ます。\n\n\n#### パッケージ・タイプ <span id=\"package-type\"></span>\n\nパッケージがインストールされたときに Yii のエクステンションとして認識されるように、エクステンションのパッケージ・タイプを `yii2-extension` と指定することは重要なことです。\n\nユーザが `composer install` を走らせてエクステンションをインストールすると、\n`vendor/yiisoft/extensions.php` というファイルが自動的に更新されて、新しいエクステンションに関する情報を含むようになります。\nYii のアプリケーションは、このファイルによって、どんなエクステンションがインストールされているかを知ることが出来ます\n(その情報には、[[yii\\base\\Application::extensions]] を通じてアクセスすることが出来ます)。\n\n\n#### 依存パッケージ <span id=\"dependencies\"></span>\n\nあなたのエクステンションは Yii に依存します (当然ですね)。ですから、`composer.json` の `require` エントリのリストにそれ (`yiisoft/yii2`) を挙げなければなりません。\nあなたのエクステンションがその他のエクステンションやサード・パーティのライブラリに依存する場合は、それらもリストに挙げなければなりません。\nそれぞれの依存パッケージについて、適切なバージョン制約 (例えば `1.*` や `@stable`) を指定することも忘れてはなりません。\nあなたのエクステンションを安定バージョンとしてリリースする場合は、安定した依存パッケージを使ってください。\n\nたいていの JavaScript/CSS パッケージは、Composer の代りに、[Bower](https://bower.io/) および/または [NPM](https://www.npmjs.com/) を使って管理されています。\nYii は [Composer アセット・プラグイン](https://github.com/fxpio/composer-asset-plugin) を使って、この種のパッケージを Composer によって管理することを可能にしています。\nあなたのエクステンションが Bower パッケージに依存している場合でも、次のように、\n`composer.json` に依存パッケージをリストアップすることが簡単に出来ます。\n\n```json\n{\n    // 依存パッケージ\n    \"require\": {\n        \"bower-asset/jquery\": \">=1.11.*\"\n    }\n}\n```\n\n上記のコードは、エクステンションが `jquery` Bower パッケージに依存することを述べています。\n一般に、`composer.json` の中で Bower パッケージを指すためには `bower-asset/PackageName` を使うことが出来ます。\nそして、NPM パッケージを指すためには `npm-asset/PackageName` を使うことが出来ます。\nComposer が Bower または NPM のパッケージをインストールする場合は、デフォルトでは、それぞれ、`@vendor/bower/PackageName` および `@vendor/npm/Packages` というディレクトリの下にパッケージの内容がインストールされます。\nこの二つのディレクトリは、`@bower/PackageName` および `@npm/PackageName` という短いエイリアスを使って参照することも可能です。\n\nアセット管理に関する詳細については、[アセット](structure-assets.md#bower-npm-assets) のセクションを参照してください。\n\n\n#### クラスのオートロード <span id=\"class-autoloading\"></span>\n\nエクステンションのクラスが Yii のクラス・オートローダまたは Composer のクラス・オートローダによってオートロードされるように、\n下記に示すように、`composer.json` ファイルの `autoload` エントリを指定しなければなりません。\n\n```json\n{\n    // ....\n\n    \"autoload\": {\n        \"psr-4\": {\n            \"yii\\\\imagine\\\\\": \"\"\n        }\n    }\n}\n```\n\n一つまたは複数のルート名前空間と、それに対応するファイル・パスをリストに挙げることが出来ます。\n\nエクステンションがアプリケーションにインストールされると、Yii は列挙されたルート名前空間の一つ一つに対して、\n名前空間に対応するディレクトリを指す [エイリアス](concept-aliases.md#extension-aliases) を作成します。\n例えば、上記の `autoload` の宣言は、`@yii/imagine` という名前のエイリアスに対応することになります。\n\n\n### 推奨されるプラクティス <span id=\"recommended-practices\"></span>\n\nエクステンションは他の人々によって使われることを意図したものですから、多くの場合、追加の開発努力が必要になります。\n以下に、高品質のエクステンションを作成するときによく用いられ、また推奨されるプラクティスのいくつかを紹介します。\n\n\n#### 名前空間 <span id=\"namespaces\"></span>\n\n名前の衝突を避けて、エクステンションの中のクラスをオートロード可能にするために、名前空間を使うべきであり、\nエクステンションの中のクラスには [PSR-4 標準](https://www.php-fig.org/psr/psr-4/) または [PSR-0 標準](https://www.php-fig.org/psr/psr-0/)\nに従った名前を付けるべきです。\n\nあなたのクラスの名前空間は `vendorName\\extensionName` で始まるべきです。\nここで `extensionName` は、`yii2-` という接頭辞を含むべきでないことを除けば、パッケージ名におけるプロジェクト名と同じものです。\n例えば、`yiisoft/yii2-imagine` エクステンションでは、`yii\\imagine` をエクステンションのクラスの名前空間として使っています。\n\n`yii`、`yii2` または `yiisoft` をベンダー名として使ってはいけません。これらの名前は、Yii のコア・コードに使うために予約されています。\n\n\n#### ブートストラップ・クラス <span id=\"bootstrapping-classes\"></span>\n\n場合によっては、アプリケーションが [ブートストラップ](runtime-bootstrapping.md) の段階にある間に、エクステンションに何らかのコードを実行させたい場合があるでしょう。\n例えば、エクステンションをアプリケーションの `beginRequest` イベントに反応させて、何らかの環境設定を調整したいことがあります。\nエクステンションのユーザに対して、エクステンションの中にあるイベント・ハンドラを `beginRequest`\nイベントに明示的にアタッチするように指示することも出来ますが、より良い方法は、それを自動的に行うことです。\n\nこの目的を達するためには、[[yii\\base\\BootstrapInterface]] を実装する、いわゆる *ブートストラップ・クラス* を作成します。\n例えば、\n\n```php\nnamespace myname\\mywidget;\n\nuse yii\\base\\BootstrapInterface;\nuse yii\\base\\Application;\n\nclass MyBootstrapClass implements BootstrapInterface\n{\n    public function bootstrap($app)\n    {\n        $app->on(Application::EVENT_BEFORE_REQUEST, function () {\n             // ここで何かをする\n        });\n    }\n}\n```\n\nそして、次のように、このクラスを `composer.json` ファイルのリストに挙げます。\n\n```json\n{\n    // ...\n\n    \"extra\": {\n        \"bootstrap\": \"myname\\\\mywidget\\\\MyBootstrapClass\"\n    }\n}\n```\n\nこのエクステンションがアプリケーションにインストールされると、すべてのリクエストのブートストラップの過程において、\n毎回、Yii が自動的にブートストラップ・クラスのインスタンスを作成し、\nその [[yii\\base\\BootstrapInterface::bootstrap()|bootstrap()]] メソッドを呼びます。\n\n\n#### データベースを扱う <span id=\"working-with-databases\"></span>\n\nあなたのエクステンションはデータベースにアクセスする必要があるかも知れません。エクステンションを使うアプリケーションが常に `Yii::$db` を DB 接続として使用すると仮定してはいけません。\nその代りに、DB アクセスを必要とするクラスのために `db` プロパティを宣言すべきです。\nこのプロパティによって、エクステンションのユーザは、エクステンションにどの DB 接続を使わせるかをカスタマイズすることが出来るようになります。\nその一例として、[[yii\\caching\\DbCache]] クラスを参照して、それがどのように `db` プロパティを宣言して使っているかを見ることが出来ます。\n\nあなたのエクステンションが特定の DB テーブルを作成したり、DB スキーマを変更したりする必要がある場合は、次のようにするべきです。\n\n- DB スキーマを操作するために、平文の SQL ファイルを使うのではなく、[マイグレーション](db-migrations.md) を提供する。\n- マイグレーションがさまざまな DBMS に適用可能なものになるように試みる。\n- マイグレーションの中では [アクティブ・レコード](db-active-record.md) の使用を避ける。\n\n\n#### アセットを使う <span id=\"using-assets\"></span>\n\nあなたのエクステンションがウィジェットかモジュールである場合は、動作するために何らかの [アセット](structure-assets.md) が必要である可能性が高いでしょう。\n例えば、モジュールは、画像、JavaScript、そして CSS を含むページを表示することがあるでしょう。\nアプリケーションにインストールされるときに、エクステンションの全てのファイルは同じディレクトリの下に配置されますが、そのディレクトリはウェブからはアクセス出来ないものです。\nそのため、次のどちらかの方法を使って、アセット・ファイルをウェブから直接アクセス出来るようにしなければなりません。\n\n- アセット・ファイルをウェブからアクセス出来る特定のフォルダに手作業でコピーするように、エクステンションのユーザに要求する。\n- [アセット・バンドル](structure-assets.md) を宣言し、アセット発行メカニズムに頼って、\nアセット・バンドルにリストされているファイルをウェブからアクセス出来るフォルダに自動的にコピーする。\n\nあなたのエクステンションが他の人々にとって一層使いやすいものになるように、第二の方法をとることを推奨します。\nアセットの取り扱い一般に関する詳細は [アセット](structure-assets.md) のセクションを参照してください。\n\n\n#### 国際化と地域化 <span id=\"i18n-l10n\"></span>\n\nあなたのエクステンションは、さまざまな言語をサポートするアプリケーションによって使われるかもしれません。\n従って、あなたのエクステンションがエンド・ユーザにコンテントを表示するものである場合は、それを [国際化](tutorial-i18n.md) するように努めるべきです。具体的には、\n\n- エクステンションがエンド・ユーザに向けたメッセージを表示する場合は、翻訳することが出来るようにメッセージを `Yii::t()` で囲むべきです。\n  開発者に向けられたメッセージ (内部的な例外のメッセージなど)\n  は翻訳される必要はありません。\n- エクステンションが数値や日付などを表示する場合は、\n  [[yii\\i18n\\Formatter]] を適切な書式化の規則とともに使って書式設定すべきです。\n\n詳細については、[国際化](tutorial-i18n.md) のセクションを参照してください。\n\n\n#### テスト <span id=\"testing\"></span>\n\nあなたは、あなたのエクステンションが他の人々に問題をもたらすことなく完璧に動作することを望むでしょう。\nこの目的を達するためには、あなたのエクステンションを公開する前にテストすべきです。\n\n手作業のテストに頼るのではなく、あなたのエクステンションのコードをカバーするさまざまなテスト・ケースを作成することが推奨されます。\nあなたのエクステンションの新しいバージョンを公開する前には、毎回、それらのテスト・ケースを走らせるだけで、全てが良い状態にあることを確認することが出来ます。\nYii はテストのサポートを提供しており、それよって、単体テスト、機能テスト、受入テストを書くことが一層簡単に出来るようになっています。\n詳細については、[テスト](test-overview.md) のセクションを参照してください。\n\n\n#### バージョン管理 <span id=\"versioning\"></span>\n\nエクステンションのリリースごとにバージョン番号 (例えば `1.0.1`) を付けるべきです。\nどのようなバージョン番号を付けるべきかを決定するときは、[セマンティック・バージョニング](https://semver.org) のプラクティスに従うことを推奨します。\n\n\n#### リリース(公開) <span id=\"releasing\"></span>\n\n他の人々にあなたのエクステンションを知ってもらうためには、それをリリース(公開)する必要があります。\n\nエクステンションをリリースするのが初めての場合は、[Packagist](https://packagist.org/) などの Composer レポジトリにエクステンションを登録するべきです。\nその後は、あなたがしなければならない仕事は、エクステンションの VCS レポジトリでリリース・タグ (例えば `v1.0.1`) を作成することと、\nComposer レポジトリに新しいリリースについて通知するだけのことになります。\nそうすれば、人々が新しいリリースを見出すことが出来るようになり、Composer レポジトリを通じてエクステンションをインストールしたりアップデートしたりするようになります。\n\nエクステンションのリリースには、コード・ファイル以外に、人々があなたのエクステンションについて知ったり、\nエクステンションを使ったりするのを助けるために、下記のものを含めることを考慮すべきです。\n\n* パッケージのルート・ディレクトリに readme ファイル: あなたのエクステンションが何をするものか、\n  そして、どのようにインストールして使うものかを説明するものです。\n  [Markdown](https://daringfireball.net/projects/markdown/) 形式で書いて、`readme.md` という名前にすることを推奨します。\n* パッケージのルート・ディレクトリに changelog ファイル: それぞれのリリースで何が変ったかを一覧表示するものです。\n  このファイルは Markdown 形式で書いて `changelog.md` と名付けることが出来ます。\n* パッケージのルート・ディレクトリに upgrade ファイル: エクステンションの古いリリースからのアップグレード方法について説明するものです。\n  このファイルは Markdown 形式で書いて `upgrade.md` と名付けることが出来ます。\n* チュートリアル、デモ、スクリーン・ショットなど: あなたのエクステンションが readme ファイルでは十分にカバーできないほど多くの機能を提供するものである場合は、\n  これらが必要になります。\n* API ドキュメント: あなたのコードは、他の人々が読んで理解することがより一層容易に出来るように、十分な解説を含むべきです。\n  [BaseObject のクラス・ファイル](https://github.com/yiisoft/yii2/blob/master/framework/base/BaseObject.php) を参照すると、\n  コードに解説を加える方法を学ぶことが出来ます。\n\n> Info: コードのコメントを Markdown 形式で書くことが出来ます。\n  `yiisoft/yii2-apidoc` エクステンションが、コードのコメントに基づいて綺麗な API ドキュメントを生成するツールを提供しています。\n\n> Info: これは要求ではありませんが、あなたのエクステンションも一定のコーディング・スタイルを守るのが良いと思います。\n  [コア・フレームワーク・コード・スタイル](https://github.com/yiisoft/yii2/blob/master/docs/internals/core-code-style.md) を参照してください。\n\n\n## コア・エクステンション <span id=\"core-extensions\"></span>\n\nYii は下記のコア・エクステンション (または [\"公式エクステンション\"](https://www.yiiframework.com/extensions/official)) を提供しています。\nこれらは Yii 開発チームによって開発され保守されているものです。\n全て [Packagist](https://packagist.org/) に登録され、[エクステンションを使う](#using-extensions) の項で説明したように、簡単にインストールすることが出来ます。\n\n- [yiisoft/yii2-apidoc](https://github.com/yiisoft/yii2-apidoc):\n  拡張可能で高性能な API ドキュメント生成機能を提供します。\n  コア・フレームワークの API ドキュメントを生成するためにも使われています。\n- [yiisoft/yii2-authclient](https://github.com/yiisoft/yii2-authclient):\n  Facebook OAuth2 クライアント、GitHub OAuth2 クライアントなど、よく使われる一連の auth クライアントを提供します。\n- [yiisoft/yii2-bootstrap](https://github.com/yiisoft/yii2-bootstrap):\n  [Bootstrap](https://getbootstrap.com/) のコンポーネントとプラグインをカプセル化した一連のウィジェットを提供します。\n- [yiisoft/yii2-debug](https://github.com/yiisoft/yii2-debug):\n  Yii アプリケーションのデバッグのサポートを提供します。\n  このエクステンションが使われると、全てのページの末尾にデバッガ・ツールバーが表示されます。\n  このエクステンションは、より詳細なデバッグ情報を表示する一連のスタンドアロン・ページも提供します。\n- [yiisoft/yii2-elasticsearch](https://github.com/yiisoft/yii2-elasticsearch):\n  [Elasticsearch](https://www.elastic.co/) の使用に対するサポートを提供します。\n  基本的なクエリ/サーチのサポートを含むだけでなく、Elasticsearch にアクティブ・レコードを保存することを可能にする\n  [アクティブ・レコード](db-active-record.md) パターンをも実装しています。\n- [yiisoft/yii2-faker](https://github.com/yiisoft/yii2-faker):\n  ダミー・データを作る [Faker](https://github.com/fzaninotto/Faker) を使うためのサポートを提供します。\n- [yiisoft/yii2-gii](https://github.com/yiisoft/yii2-gii):\n  拡張性が非常に高いウェブ・ベースのコード・ジェネレータを提供します。\n  これを使って、モデル、フォーム、モジュール、CRUD などを迅速に生成することが出来ます。\n- [yiisoft/yii2-httpclient](https://github.com/yiisoft/yii2-httpclient):\n  HTTP クライアントを提供します。\n- [yiisoft/yii2-imagine](https://github.com/yiisoft/yii2-imagine):\n  [Imagine](https://imagine.readthedocs.org/) に基づいて、使われることの多い画像操作機能を提供します。\n- [yiisoft/yii2-jui](https://github.com/yiisoft/yii2-jui):\n  [JQuery UI](https://jqueryui.com/) のインタラクションとウィジェットをカプセル化した一連のウィジェットを提供します。\n- [yiisoft/yii2-mongodb](https://github.com/yiisoft/yii2-mongodb):\n  [MongoDB](https://www.mongodb.com/) の使用に対するサポートを提供します。\n  基本的なクエリ、アクティブ・レコード、マイグレーション、キャッシュ、コード生成などの機能を含みます。\n- [yiisoft/yii2-queue](https://www.yiiframework.com/extension/yiisoft/yii2-queue):\n  キューによるタスクの非同期実行のサポートを提供します。\n  データベース、Redis、RabbitMQ、AMQP、Beanstalk および Gearman によるキューをサポートしています。\n- [yiisoft/yii2-redis](https://github.com/yiisoft/yii2-redis):\n  [redis](https://redis.io/) の使用に対するサポートを提供します。\n  基本的なクエリ、アクティブ・レコード、キャッシュなどの機能を含みます。\n- [yiisoft/yii2-shell](https://www.yiiframework.com/extension/yiisoft/yii2-shell):\n  [psysh](https://psysh.org/) に基づくイタラクティブなシェルを提供します。\n- [yiisoft/yii2-smarty](https://github.com/yiisoft/yii2-smarty):\n  [Smarty](https://www.smarty.net/) に基づいたテンプレート・エンジンを提供します。\n- [yiisoft/yii2-sphinx](https://github.com/yiisoft/yii2-sphinx):\n  [Sphinx](https://sphinxsearch.com/) の使用に対するサポートを提供します。\n  基本的なクエリ、アクティブ・レコード、コード生成などの機能を含みます。\n- [yiisoft/yii2-swiftmailer](https://github.com/yiisoft/yii2-swiftmailer):\n  [swiftmailer](https://swiftmailer.symfony.com/) に基づいたメール送信機能を提供します。\n- [yiisoft/yii2-twig](https://github.com/yiisoft/yii2-twig):\n  [Twig](https://twig.symfony.com/) に基づいたテンプレート・エンジンを提供します。\n\n下記の公式エクステンションは Yii 2.1 以上のためのものです。\nこれらは、Yii 2.0 ではコア・フレームワークに含まれていますので、インストールする必要はありません。.\n\n- [yiisoft/yii2-captcha](https://www.yiiframework.com/extension/yiisoft/yii2-captcha):\n  CAPTCHA を提供します。\n- [yiisoft/yii2-jquery](https://www.yiiframework.com/extension/yiisoft/yii2-jquery):\n  [jQuery](https://jquery.com/) のサポートを提供します。\n- [yiisoft/yii2-maskedinput](https://www.yiiframework.com/extension/yiisoft/yii2-maskedinput):\n  [jQuery Input Mask plugin](https://robinherbots.github.io/Inputmask/) に基づいて、マスクト・インプットを提供します。\n- [yiisoft/yii2-mssql](https://www.yiiframework.com/extension/yiisoft/yii2-mssql):\n  [MSSQL](https://www.microsoft.com/sql-server/) を使うためのサポートを提供します。\n- [yiisoft/yii2-oracle](https://www.yiiframework.com/extension/yiisoft/yii2-oracle):\n  [Oracle](https://www.oracle.com/) を使うためのサポートを提供します。\n- [yiisoft/yii2-rest](https://www.yiiframework.com/extension/yiisoft/yii2-rest):\n  REST API に対するサポートを提供します。\n"
  },
  {
    "path": "docs/guide-ja/structure-filters.md",
    "content": "フィルタ\n========\n\nフィルタは、[コントローラ・アクション](structure-controllers.md#actions) の前 および/または 後に走るオブジェクトです。\n例えば、アクセス・コントロール・フィルタはアクションの前に走って、アクションが特定のエンド・ユーザだけにアクセスを許可するものであることを保証します。\nまた、コンテント圧縮フィルタはアクションの後に走って、レスポンスのコンテントをエンド・ユーザに送出する前に圧縮します。\n\n一つのフィルタは、前フィルタ (アクションの *前* に適用されるフィルタのロジック) および/または\n後フィルタ (アクションの *後* に適用されるロジック) から構成することが出来ます。\n\n\n## フィルタを使用する <span id=\"using-filters\"></span>\n\nフィルタは、本質的には特別な種類の [ビヘイビア](concept-behaviors.md) です。\nしたがって、フィルタを使うことは [ビヘイビアを使う](concept-behaviors.md#attaching-behaviors) ことと同じです。\n下記のように、[[yii\\base\\Controller::behaviors()|behaviors()]] メソッドをオーバーライドすることによって、コントローラの中でフィルタを宣言することが出来ます。\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\HttpCache',\n            'only' => ['index', 'view'],\n            'lastModified' => function ($action, $params) {\n                $q = new \\yii\\db\\Query();\n                return $q->from('user')->max('updated_at');\n            },\n        ],\n    ];\n}\n```\n\nデフォルトでは、コントローラ・クラスの中で宣言されたフィルタは、そのコントローラの *全て* のアクションに適用されます。\nしかし、[[yii\\base\\ActionFilter::only|only]] プロパティを構成することによって、\nフィルタがどのアクションに適用されるべきかを明示的に指定することも出来ます。\n上記の例では、 `HttpCache` フィルタは、`index` と `view` のアクションに対してのみ適用されています。\nまた、[[yii\\base\\ActionFilter::except|except]] プロパティを構成して、いくつかのアクションをフィルタされないように除外することも可能です。\n\nコントローラのほかに、[モジュール](structure-modules.md) または [アプリケーション](structure-applications.md)\nでもフィルタを宣言することが出来ます。\nそのようにした場合、[[yii\\base\\ActionFilter::only|only]] と [[yii\\base\\ActionFilter::except|except]] のプロパティを上で説明したように構成しない限り、\nそのフィルタは、モジュールまたはアプリケーションに属する *全て* のコントローラ・アクションに適用されます。\n\n> Note: モジュールやアプリケーションでフィルタを宣言する場合、[[yii\\base\\ActionFilter::only|only]] と [[yii\\base\\ActionFilter::except|except]] のプロパティでは、\n  アクション ID ではなく、[ルート](structure-controllers.md#routes) を使わなければなりません。\n  なぜなら、モジュールやアプリケーションのスコープでは、アクション ID だけでは完全にアクションを指定することが出来ないからです。\n\n一つのアクションに複数のフィルタが構成されている場合、フィルタは下記で説明されている規則に従って適用されます。\n\n* 前フィルタ\n    - アプリケーションで宣言されたフィルタを `behaviors()` にリストされた順に適用する。\n    - モジュールで宣言されたフィルタを `behaviors()` にリストされた順に適用する。\n    - コントローラで宣言されたフィルタを `behaviors()` にリストされた順に適用する。\n    - フィルタのどれかがアクションをキャンセルすると、\n      そのフィルタの後のフィルタ (前フィルタと後フィルタの両方) は適用されない。\n* 前フィルタを通過したら、アクションを走らせる。\n* 後フィルタ\n    - コントローラで宣言されたフィルタを `behaviors()` にリストされた逆順で適用する。\n    - モジュールで宣言されたフィルタを `behaviors()` にリストされた逆順で適用する。\n    - アプリケーションで宣言されたフィルタを `behaviors()` にリストされた逆順で適用する。\n\n\n## フィルタを作成する <span id=\"creating-filters\"></span>\n\n新しいアクション・フィルタを作成するためには、[[yii\\base\\ActionFilter]] を拡張して、\n[[yii\\base\\ActionFilter::beforeAction()|beforeAction()]] および/または [[yii\\base\\ActionFilter::afterAction()|afterAction()]] メソッドをオーバーライドします。\n前者はアクションが走る前に実行され、後者は走った後に実行されます。\n[[yii\\base\\ActionFilter::beforeAction()|beforeAction()]] の返り値が、アクションが実行されるべきか否かを決定します。\n返り値が `false` である場合、このフィルタの後に続くフィルタはスキップされ、アクションは実行を中止されます。\n\n次の例は、アクションの実行時間をログに記録するフィルタを示すものです。\n\n```php\nnamespace app\\components;\n\nuse Yii;\nuse yii\\base\\ActionFilter;\n\nclass ActionTimeFilter extends ActionFilter\n{\n    private $_startTime;\n\n    public function beforeAction($action)\n    {\n        $this->_startTime = microtime(true);\n        return parent::beforeAction($action);\n    }\n\n    public function afterAction($action, $result)\n    {\n        $time = microtime(true) - $this->_startTime;\n        Yii::debug(\"アクション '{$action->uniqueId}' は $time 秒を消費。\");\n        return parent::afterAction($action, $result);\n    }\n}\n```\n\n\n## コアのフィルタ <span id=\"core-filters\"></span>\n\nYii はよく使われる一連のフィルタを提供しており、それらは、主として `yii\\filters` 名前空間の下にあります。\n以下では、それらのフィルタを簡単に紹介します。\n\n\n### [[yii\\filters\\AccessControl|AccessControl]] <span id=\"access-control\"></span>\n\nAccessControl は、一組の [[yii\\filters\\AccessControl::rules|規則]] に基づいて、シンプルなアクセス・コントロールを提供するものです。\n具体的に言うと、アクションが実行される前に、AccessControl はリストされた規則を調べて、\n現在のコンテキスト変数 (例えば、ユーザの IP アドレスや、ユーザのログイン状態など) に最初に合致するものを見つけます。\nそして、合致した規則によって、リクエストされたアクションの実行を許可するか拒否するかを決定します。\n合致する規則がなかった場合は、アクセスは拒否されます。\n\n次の例は、認証されたユーザに対しては `create` と `update` のアクションへのアクセスを許可し、\nその他のすべてのユーザにはこれら二つのアクションに対するアクセスを拒否する仕方を示すものです。\n\n```php\nuse yii\\filters\\AccessControl;\n\npublic function behaviors()\n{\n    return [\n        'access' => [\n            'class' => AccessControl::class,\n            'only' => ['create', 'update'],\n            'rules' => [\n                // 認証されたユーザに許可する\n                [\n                    'allow' => true,\n                    'roles' => ['@'],\n                ],\n                // その他はすべてデフォルトにより拒否される\n            ],\n        ],\n    ];\n}\n```\n\nアクセス・コントロール一般についての詳細は [権限](security-authorization.md) のセクションを参照してください。\n\n\n### 認証メソッド・フィルタ <span id=\"auth-method-filters\"></span>\n\n認証メソッド・フィルタは、[HTTP Basic 認証](https://ja.wikipedia.org/wiki/Basic%E8%AA%8D%E8%A8%BC)、\n[OAuth 2](https://oauth.net/2/) などの様々なメソッドを使ってユーザを認証するために使われるものです。\nこれらのフィルタ・クラスはすべて `yii\\filters\\auth` 名前空間の下にあります。\n\n次の例は、[[yii\\filters\\auth\\HttpBasicAuth]] の使い方を示すもので、HTTP Basic 認証に基づくアクセス・トークンを使ってユーザを認証しています。\nこれを動作させるためには、あなたの [[yii\\web\\User::identityClass|ユーザ・アイデンティティ・クラス]]\nが [[yii\\web\\IdentityInterface::findIdentityByAccessToken()|findIdentityByAccessToken()]]\nメソッドを実装していなければならないことに注意してください。\n\n```php\nuse yii\\filters\\auth\\HttpBasicAuth;\n\npublic function behaviors()\n{\n    return [\n        'basicAuth' => [\n            'class' => HttpBasicAuth::class,\n        ],\n    ];\n}\n```\n\n認証メソッド・フィルタは RESTful API を実装するときに使われるのが通例です。\n詳細については、RESTful の [認証](rest-authentication.md) のセクションを参照してください。\n\n\n### [[yii\\filters\\ContentNegotiator|ContentNegotiator]] <span id=\"content-negotiator\"></span>\n\nContentNegotiator は、レスポンス形式のネゴシエーションとアプリケーション言語のネゴシエーションをサポートします。\nこのフィルタは `GET` パラメータと `Accept` HTTP ヘッダを調べることによって、レスポンス形式 および/または 言語を決定しようとします。\n\n次の例では、ContentNegotiator はレスポンス形式として JSON と XML をサポートし、\n(合衆国の)英語とドイツ語を言語としてサポートするように構成されています。\n\n```php\nuse yii\\filters\\ContentNegotiator;\nuse yii\\web\\Response;\n\npublic function behaviors()\n{\n    return [\n        [\n            'class' => ContentNegotiator::class,\n            'formats' => [\n                'application/json' => Response::FORMAT_JSON,\n                'application/xml' => Response::FORMAT_XML,\n            ],\n            'languages' => [\n                'en-US',\n                'de',\n            ],\n        ],\n    ];\n}\n```\n\nレスポンス形式と言語は [アプリケーションのライフサイクル](structure-applications.md#application-lifecycle)\nのもっと早い段階で決定される必要があることがよくあります。\nこのため、ContentNegotiator はフィルタの他に、[ブートストラップ・コンポーネント](structure-applications.md#bootstrap) としても使うことができるように設計されています。\n例えば、次のように、ContentNegotiator を [アプリケーションの構成情報](structure-applications.md#application-configurations)\nの中で構成することが出来ます。\n\n```php\nuse yii\\filters\\ContentNegotiator;\nuse yii\\web\\Response;\n\n[\n    'bootstrap' => [\n        [\n            'class' => ContentNegotiator::class,\n            'formats' => [\n                'application/json' => Response::FORMAT_JSON,\n                'application/xml' => Response::FORMAT_XML,\n            ],\n            'languages' => [\n                'en-US',\n                'de',\n            ],\n        ],\n    ],\n];\n```\n\n> Info: 望ましいコンテント・タイプと言語がリクエストから決定できない場合は、\n  [[formats]] および [[languages]] に挙げられている最初の形式と言語が使用されます。\n\n\n\n### [[yii\\filters\\HttpCache|HttpCache]] <span id=\"http-cache\"></span>\n\nHttpCache は `Last-Modified` および `Etag` の HTTP ヘッダを利用して、クライアント・サイドのキャッシュを実装するものです。\n例えば、\n\n```php\nuse yii\\filters\\HttpCache;\n\npublic function behaviors()\n{\n    return [\n        [\n            'class' => HttpCache::class,\n            'only' => ['index'],\n            'lastModified' => function ($action, $params) {\n                $q = new \\yii\\db\\Query();\n                return $q->from('user')->max('updated_at');\n            },\n        ],\n    ];\n}\n```\n\nHttpCache に関する詳細は [HTTP キャッシュ](caching-http.md) のセクションを参照してください。\n\n\n### [[yii\\filters\\PageCache|PageCache]] <span id=\"page-cache\"></span>\n\nPageCache はサーバ・サイドにおけるページ全体のキャッシュを実装するものです。\n次の例では、PageCache が `index` アクションに適用されて、最大 60 秒間、または、`post` テーブルのエントリ数が変化するまでの間、ページ全体をキャッシュしています。\nさらに、このページ・キャッシュは、選択されたアプリケーションの言語に従って、違うバージョンのページを保存するようにしています。\n\n```php\nuse yii\\filters\\PageCache;\nuse yii\\caching\\DbDependency;\n\npublic function behaviors()\n{\n    return [\n        'pageCache' => [\n            'class' => PageCache::class,\n            'only' => ['index'],\n            'duration' => 60,\n            'dependency' => [\n                'class' => DbDependency::class,\n                'sql' => 'SELECT COUNT(*) FROM post',\n            ],\n            'variations' => [\n                \\Yii::$app->language,\n            ]\n        ],\n    ];\n}\n```\n\nPageCache の使用に関する詳細は [ページ・キャッシュ](caching-page.md) のセクションを参照してください。\n\n\n### [[yii\\filters\\RateLimiter|RateLimiter]] <span id=\"rate-limiter\"></span>\n\nRateLimiter は [リーキー・バケット・アルゴリズム](https://ja.wikipedia.org/wiki/%E3%83%AA%E3%83%BC%E3%82%AD%E3%83%BC%E3%83%90%E3%82%B1%E3%83%83%E3%83%88) に基づいてレート制限のアルゴリズムを実装するものです。\n主として RESTful API を実装するときに使用されます。\nこのフィルタの使用に関する詳細は [レート制限](rest-rate-limiting.md) のセクションを参照してください。\n\n\n### [[yii\\filters\\VerbFilter|VerbFilter]] <span id=\"verb-filter\"></span>\n\nVerbFilter は、HTTP リクエスト・メソッド (HTTP 動詞) がリクエストされたアクションによって許可されているかどうかをチェックするものです。\n許可されていない場合は、HTTP 405 例外を投げます。\n次の例では、VerbFilter が宣言されて、CRUD アクションに対して許可されるメソッドの典型的なセットを指定しています。\n\n```php\nuse yii\\filters\\VerbFilter;\n\npublic function behaviors()\n{\n    return [\n        'verbs' => [\n            'class' => VerbFilter::class,\n            'actions' => [\n                'index'  => ['get'],\n                'view'   => ['get'],\n                'create' => ['get', 'post'],\n                'update' => ['get', 'put', 'post'],\n                'delete' => ['post', 'delete'],\n            ],\n        ],\n    ];\n}\n```\n\n### [[yii\\filters\\Cors|Cors]] <span id=\"cors\"></span>\n\nクロス・オリジン・リソース共有 [CORS](https://developer.mozilla.org/ja/docs/Web/HTTP/CORS) とは、ウェブ・ページにおいて、さまざまなリソース (例えば、フォントや JavaScript など) を、それを生成するドメイン以外のドメインからリクエストすることを可能にするメカニズムです。\n特に言えば、JavaScript の AJAX 呼出しが使用することが出来る XMLHttpRequest メカニズムです。\nこのような「クロス・ドメイン｣のリクエストは、このメカニズムに拠らなければ、\n同一生成元のセキュリティ・ポリシーによって、ウェブ・ブラウザから禁止されるはずのものです。\nCORS は、ブラウザとサーバが交信して、クロス・ドメインのリクエストを許可するか否かを決定する方法を定義するものです。\n\n[[yii\\filters\\Cors|Cors フィルタ]] は、CORS ヘッダが常に送信されることを保証するために、\nAuthentication / Authorization のフィルタよりも前に定義されなければなりません。\n\n```php\nuse yii\\filters\\Cors;\nuse yii\\helpers\\ArrayHelper;\n\npublic function behaviors()\n{\n    return ArrayHelper::merge([\n        [\n            'class' => Cors::class,\n        ],\n    ], parent::behaviors());\n}\n```\n\nあなたの API の [[yii\\rest\\ActiveController]] クラスに CORS フィルタを追加したい場合は、\n[REST コントローラ](rest-controllers.md#cors) のセクションも参照して下さい。\n\nCors のフィルタリングは [[yii\\filters\\Cors::$cors|$cors]] プロパティを使ってチューニングすることが出来ます。\n\n* `cors['Origin']`: 許可される生成元を定義するのに使われる配列。`['*']` (すべて) または `['https://www.myserver.net'、'https://www.myotherserver.com']` などが設定可能。デフォルトは `['*']`。\n* `cors['Access-Control-Request-Method']`: 許可される HTTP 動詞の配列。たとえば、`['GET', 'OPTIONS', 'HEAD']`。デフォルトは `['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']`。\n* `cors['Access-Control-Request-Headers']`: 許可されるヘッダの配列。全てのヘッダを意味する `['*']` または特定のヘッダを示す `['X-Request-With']` が設定可能。デフォルトは `['*']`。\n* `cors['Access-Control-Allow-Credentials']`: 現在のリクエストをクレデンシャルを使ってすることが出来るかどうかを定義。`true`、`false` または `null` (設定なし) が設定可能。デフォルトは `null`。\n* `cors['Access-Control-Max-Age']`: プリフライト・リクエストの寿命を定義。デフォルトは `86400`。\n\n次の例は、生成元 `https://www.myserver.net` に対する `GET`、`HEAD` および `OPTIONS` のメソッドによる CORS を許可するものです。\n\n```php\nuse yii\\filters\\Cors;\nuse yii\\helpers\\ArrayHelper;\n\npublic function behaviors()\n{\n    return ArrayHelper::merge([\n        [\n            'class' => Cors::class,\n            'cors' => [\n                'Origin' => ['https://www.myserver.net'],\n                'Access-Control-Request-Method' => ['GET', 'HEAD', 'OPTIONS'],\n            ],\n        ],\n    ], parent::behaviors());\n}\n```\n\nデフォルトのパラメータをアクション単位でオーバーライドして CORS ヘッダをチューニングすることも可能です。\n例えば、`login` アクションに `Access-Control-Allow-Credentials` を追加することは、次のようにすれば出来ます。\n\n```php\nuse yii\\filters\\Cors;\nuse yii\\helpers\\ArrayHelper;\n\npublic function behaviors()\n{\n    return ArrayHelper::merge([\n        [\n            'class' => Cors::class,\n            'cors' => [\n                'Origin' => ['https://www.myserver.net'],\n                'Access-Control-Request-Method' => ['GET', 'HEAD', 'OPTIONS'],\n            ],\n            'actions' => [\n                'login' => [\n                    'Access-Control-Allow-Credentials' => true,\n                ]\n            ]\n        ],\n    ], parent::behaviors());\n}\n```\n"
  },
  {
    "path": "docs/guide-ja/structure-models.md",
    "content": "モデル\n======\n\nモデルは [MVC](https://ja.wikipedia.org/wiki/Model_View_Controller) アーキテクチャの一部を成すものです。\nこれは、ビジネスのデータ、規則、ロジックを表現するオブジェクトです。\n\nモデル・クラスは、[[yii\\base\\Model]] またはその子クラスを拡張することによって作成することが出来ます。\n基底クラス [[yii\\base\\Model]] は、次のような数多くの有用な機能をサポートしています。\n\n* [属性](#attributes): ビジネス・データを表現します。通常のオブジェクト・プロパティや配列要素のようにして\n  アクセスすることが出来ます。\n* [属性のラベル](#attribute-labels): 属性の表示ラベルを指定します。\n* [一括代入](#massive-assignment): 一回のステップで複数の属性にデータを投入することをサポートしています。\n* [検証規則](#validation-rules): 宣言された検証規則に基いて入力されたデータの有効性を保証します。\n* [データのエクスポート](#data-exporting): カスタマイズ可能な形式でモデル・データを配列にエクスポートすることが出来ます。\n\n`Model` クラスは、[アクティブ・レコード](db-active-record.md) のような、更に高度なモデルの基底クラスでもあります。\nそれらの高度なモデルについての詳細は、関連するドキュメントを参照してください。\n\n> Info: あなたのモデル・クラスの基底クラスとして [[yii\\base\\Model]] を使うことが要求されている訳ではありません。\n  しかしながら、Yii のコンポーネントの多くが [[yii\\base\\Model]] をサポートするように作られていますので、通常は [[yii\\base\\Model]] がモデルの基底クラスとして推奨されます。\n\n\n## 属性 <span id=\"attributes\"></span>\n\nモデルはビジネス・データを *属性* の形式で表現します。全ての属性はそれぞれパブリックにアクセス可能なモデルのプロパティと同様なものです。\n[[yii\\base\\Model::attributes()]] メソッドが、モデルがどのような属性を持つかを指定します。\n\n属性に対しては、通常のオブジェクト・プロパティにアクセスするのと同じようにして、アクセスすることが出来ます。\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// \"name\" は ContactForm の属性\n$model->name = 'example';\necho $model->name;\n```\n\nまた、配列の要素にアクセスするようして、属性にアクセスすることも出来ます。\nこれは、[[yii\\base\\Model]] が [ArrayAccess インタフェイス](https://www.php.net/manual/ja/class.arrayaccess.php) と\n[Traversable インタフェイス](https://www.php.net/manual/ja/class.traversable.php) をサポートしている恩恵です。\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// 配列要素のように属性にアクセスする\n$model['name'] = 'example';\necho $model['name'];\n\n// モデルは foreach で中身をたどることが出来る\nforeach ($model as $name => $value) {\n    echo \"$name: $value\\n\";\n}\n```\n\n\n### 属性を定義する <span id=\"defining-attributes\"></span>\n\nあなたのモデルが [[yii\\base\\Model]] を直接に拡張するものである場合、デフォルトでは、全ての *static でない public な* メンバ変数は属性となります。\n例えば、次に示す `ContactForm` モデルは四つの属性、すなわち、`name`、`email`、`subject`、そして、`body` を持ちます。\nこの `ContactForm` モデルは、HTML フォームから受け取る入力データを表現するために使われます。\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\n\nclass ContactForm extends Model\n{\n    public $name;\n    public $email;\n    public $subject;\n    public $body;\n}\n```\n\n\n[[yii\\base\\Model::attributes()]] をオーバーライドして、属性を異なる方法で定義することが出来ます。\nこのメソッドはモデルが持つ属性の名前を返さなくてはなりません。\n例えば、[[yii\\db\\ActiveRecord]] は、関連付けられたデータベース・テーブルのコラム名を属性の名前として返すことによって、属性を定義しています。\nただし、これと同時に、定義された属性に対して通常のオブジェクト・プロパティと同じようにアクセスすることが出来るように、\n`__get()` や `__set()` などのマジック・メソッドをオーバーライドする必要があるかもしれないことに注意してください。\n\n\n### 属性のラベル <span id=\"attribute-labels\"></span>\n\n属性の値を表示したり、入力してもらったりするときに、属性と関連付けられたラベルを表示する必要があることがよくあります。\n例えば、`firstName` という名前の属性を考えたとき、入力フォームやエラー・メッセージのような箇所でエンド・ユーザに表示するときは、\nもっとユーザ・フレンドリーな `First Name` というラベルを表示したいと思うでしょう。\n\n[[yii\\base\\Model::getAttributeLabel()]] を呼ぶことによって属性のラベルを得ることが出来ます。例えば、\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// \"Name\" を表示する\necho $model->getAttributeLabel('name');\n```\n\nデフォルトでは、属性のラベルは属性の名前から自動的に生成されます。\nラベルの生成は [[yii\\base\\Model::generateAttributeLabel()]] というメソッドによって行われます。\nこのメソッドは、キャメルケースの変数名を複数の単語に分割し、各単語の最初の文字を大文字にします。\n例えば、`username` は `Username` となり、`firstName` は `First Name` となります。\n\n自動的に生成されるラベルを使用したくない場合は、[[yii\\base\\Model::attributeLabels()]] をオーバーライドして、\n属性のラベルを明示的に宣言することが出来ます。例えば、\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\n\nclass ContactForm extends Model\n{\n    public $name;\n    public $email;\n    public $subject;\n    public $body;\n\n    public function attributeLabels()\n    {\n        return [\n            'name' => 'Your name',\n            'email' => 'Your email address',\n            'subject' => 'Subject',\n            'body' => 'Content',\n        ];\n    }\n}\n```\n\n複数の言語をサポートするアプリケーションでは、属性のラベルを翻訳したいと思うでしょう。\nこれも、以下のように、[[yii\\base\\Model::attributeLabels()|attributeLabels()]] の中で行うことが出来ます。\n\n```php\npublic function attributeLabels()\n{\n    return [\n        'name' => \\Yii::t('app', 'Your name'),\n        'email' => \\Yii::t('app', 'Your email address'),\n        'subject' => \\Yii::t('app', 'Subject'),\n        'body' => \\Yii::t('app', 'Content'),\n    ];\n}\n```\n\n条件に従って属性のラベルを定義することも出来ます。\n例えば、モデルが使用される [シナリオ](#scenarios) に基づいて、同じ属性に対して違うラベルを返すことことが出来ます。\n\n> Info: 厳密に言えば、属性のラベルは [ビュー](structure-views.md) の一部を成すものです。\n  しかし、たいていの場合、モデルの中でラベルを宣言する方が便利が良く、結果としてクリーンで再利用可能なコードになります。\n\n\n## シナリオ <span id=\"scenarios\"></span>\n\nモデルはさまざまに異なる *シナリオ* で使用されます。\n例えば、`User` モデルはユーザ・ログインの入力を収集するために使われますが、同時に、ユーザ登録の目的でも使われます。\n異なるシナリオの下では、モデルが使用するビジネス・ルールとロジックも異なるものになり得ます。\n例えば、`email` 属性はユーザ登録の際には必須とされるかも知れませんが、ログインの際にはそうではないでしょう。\n\nモデルは [[yii\\base\\Model::scenario]] プロパティを使って、自身が使われているシナリオを追跡します。\nデフォルトでは、モデルは `default` という一つのシナリオだけをサポートします。\n次のコードは、モデルのシナリオを設定する二つの方法を示すものです。\n\n```php\n// シナリオをプロパティとして設定する\n$model = new User;\n$model->scenario = User::SCENARIO_LOGIN;\n\n// シナリオを設定情報で設定する\n$model = new User(['scenario' => User::SCENARIO_LOGIN]);\n```\n\nデフォルトでは、モデルによってサポートされるシナリオは、モデルで宣言されている [検証規則](#validation-rules) によって決定されます。\nしかし、次のように、[[yii\\base\\Model::scenarios()]] メソッドをオーバーライドして、\nこの振る舞いをカスタマイズすることが出来ます。\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass User extends ActiveRecord\n{\n    const SCENARIO_LOGIN = 'login';\n    const SCENARIO_REGISTER = 'register';\n\n    public function scenarios()\n    {\n        return [\n            self::SCENARIO_LOGIN => ['username', 'password'],\n            self::SCENARIO_REGISTER => ['username', 'email', 'password'],\n        ];\n    }\n}\n```\n\n> Info: 上記の例と後続の例では、モデル・クラスは [[yii\\db\\ActiveRecord]] を拡張するものとなっています。\n  というのは、複数のシナリオを使用することは、通常は、[アクティブ・レコード](db-active-record.md) クラスで発生するからです。\n\n`scenarios()` メソッドは、キーがシナリオの名前であり、値が対応する *アクティブな属性* である配列を返します。\nアクティブな属性とは、[一括代入](#massive-assignment) することが出来て、[検証](#validation-rules) の対象になる属性です。\n上記の例では、`login` シナリオにおいては `username` と `password` の属性がアクティブであり、\n一方、`register` シナリオにおいては、`username` と `password` に加えて `email` もアクティブです。\n\n`scenarios()` のデフォルトの実装は、検証規則の宣言メソッドである [[yii\\base\\Model::rules()]] に現れる全てのシナリオを返すものです。\n`scenarios()` をオーバーライドするときに、デフォルトのシナリオに加えて新しいシナリオを導入したい場合は、\n次のようなコードを書きます。\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass User extends ActiveRecord\n{\n    const SCENARIO_LOGIN = 'login';\n    const SCENARIO_REGISTER = 'register';\n\n    public function scenarios()\n    {\n        $scenarios = parent::scenarios();\n        $scenarios[self::SCENARIO_LOGIN] = ['username', 'password'];\n        $scenarios[self::SCENARIO_REGISTER] = ['username', 'email', 'password'];\n        return $scenarios;\n    }\n}\n```\n\nシナリオの機能は、主として、[検証](#validation-rules) と [属性の一括代入](#massive-assignment) によって使用されます。\nしかし、他の目的に使うことも可能です。例えば、現在のシナリオに基づいて異なる [属性のラベル](#attribute-labels)\nを宣言することも出来ます。\n\n\n## 検証規則 <span id=\"validation-rules\"></span>\n\nモデルのデータをエンド・ユーザから受け取ったときは、データを検証して、\nそれが一定の規則 (*検証規則*、あるいは、いわゆる *ビジネス・ルール*) を満たしていることを確認しなければなりません。\n`ContactForm` モデルを例に挙げるなら、全ての属性が空ではなく、`email` 属性が有効なメール・アドレスを含んでいることを確認したいでしょう。\nいずれかの属性の値が対応するビジネス・ルールを満たしていないときは、\nユーザがエラーを訂正するのを助ける適切なエラー・メッセージが表示されなければなりません。\n\n受信したデータを検証するために、[[yii\\base\\Model::validate()]] を呼ぶことが出来ます。\nこのメソッドは、[[yii\\base\\Model::rules()]] で宣言された検証規則を使って、該当するすべての属性を検証します。\nエラーが見つからなければ、メソッドは `true` を返します。\nそうでなければ、[[yii\\base\\Model::errors]] にエラーを保存して、`false` を返します。例えば、\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// モデルの属性にユーザの入力を代入する\n$model->attributes = \\Yii::$app->request->post('ContactForm');\n\nif ($model->validate()) {\n    // すべての入力値は有効である\n} else {\n    // 検証が失敗: $errors はエラー・メッセージを含む配列\n    $errors = $model->errors;\n}\n```\n\n\nモデルに関連付けられた検証規則を宣言するためには、[[yii\\base\\Model::rules()]] メソッドをオーバーライドして、\nモデルの属性が満たすべき規則を返すようにします。\n次の例は、`ContactForm` モデルのために宣言された検証規則を示します。\n\n```php\npublic function rules()\n{\n    return [\n        // name、email、subject、body の属性が必須\n        [['name', 'email', 'subject', 'body'], 'required'],\n\n        // email 属性は、有効なメール・アドレスでなければならない\n        ['email', 'email'],\n    ];\n}\n```\n\n一つの規則は、一つまたは複数の属性を検証するために使うことが出来ます。\nまた、一つの属性は、一つまたは複数の規則によって検証することが出来ます。\n検証規則をどのように宣言するかについての詳細は [入力を検証する](input-validation.md) のセクションを参照してください。\n\n時として、特定の [シナリオ](#scenarios) にのみ適用される規則が必要になるでしょう。\nそのためには、下記のように、規則に `on` プロパティを指定することが出来ます。\n\n```php\npublic function rules()\n{\n    return [\n        // \"register\" シナリオでは、username、email、password のすべてが必須\n        [['username', 'email', 'password'], 'required', 'on' => self::SCENARIO_REGISTER],\n\n        // \"login\" シナリオでは、username と password が必須\n        [['username', 'password'], 'required', 'on' => self::SCENARIO_LOGIN],\n        \n        [['username'], 'string'], // username は文字列でなければならない。この規則は全てのシナリオに適用される\n    ];\n}\n```\n\n`on` プロパティを指定しない場合は、その規則は全てのシナリオに適用されることになります。\n現在の [[yii\\base\\Model::scenario|シナリオ]] に適用可能な規則は *アクティブな規則* と呼ばれます。\n\n属性が検証されるのは、それが `scenarios()` の中でアクティブな属性であると宣言されており、かつ、\nその属性が `rules()` の中で宣言されている一つまたは複数のアクティブな規則と結び付けられている場合であり、また、そのような場合だけです。\n\n\n## 一括代入 <span id=\"massive-assignment\"></span>\n\n一括代入は、一行のコードを書くだけで、ユーザの入力した複数のデータをモデルに投入できる便利な方法です。\n一括代入は、入力されたデータを [[yii\\base\\Model::$attributes]] プロパティに直接に代入することによって、モデルの属性にデータを投入します。\n次の二つのコード断片は等価であり、どちらもエンド・ユーザから送信されたフォームのデータを\n`ContactForm` モデルの属性に割り当てようとするものです。\n明らかに、一括代入を使う前者の方が、後者よりも明快で間違いも起こりにくいでしょう。\n\n```php\n$model = new \\app\\models\\ContactForm;\n$model->attributes = \\Yii::$app->request->post('ContactForm');\n```\n\n```php\n$model = new \\app\\models\\ContactForm;\n$data = \\Yii::$app->request->post('ContactForm', []);\n$model->name = isset($data['name']) ? $data['name'] : null;\n$model->email = isset($data['email']) ? $data['email'] : null;\n$model->subject = isset($data['subject']) ? $data['subject'] : null;\n$model->body = isset($data['body']) ? $data['body'] : null;\n```\n\n\n### 安全な属性 <span id=\"safe-attributes\"></span>\n\n一括代入は、いわゆる *安全な属性*、すなわち、[[yii\\base\\Model::scenarios()]] においてモデルの現在の\n[[yii\\base\\Model::scenario|シナリオ]] のためにリストされている属性に対してのみ適用されます。\n例えば、`User` モデルが次のようなシナリオ宣言を持っている場合において、現在のシナリオが `login` であるときは、\n`username` と `password` のみが一括代入が可能です。\nその他の属性はいっさい触れられません。\n\n```php\npublic function scenarios()\n{\n    return [\n        self::SCENARIO_LOGIN => ['username', 'password'],\n        self::SCENARIO_REGISTER => ['username', 'email', 'password'],\n    ];\n}\n```\n\n> Info: 一括代入が安全な属性に対してのみ適用されるのは、エンド・ユーザの入力データがどの属性を修正することが出来るか、\nということを制御する必要があるからです。\n  例えば、`User` モデルに、ユーザに割り当てられた権限を決定する `permission` という属性がある場合、\nこの属性を修正できるのは、管理者がバックエンドのインタフェイスを通じてする時だけに制限したいでしょう。\n\n[[yii\\base\\Model::scenarios()]] のデフォルトの実装は [[yii\\base\\Model::rules()]] に現われる全てのシナリオと属性を返すものです。\n従って、このメソッドをオーバーライドしない場合は、アクティブな検証規則のどれかに出現する限り、\nその属性は安全である、ということになります。\n\nこのため、実際に検証することなく属性を安全であると宣言できるように、\n`safe` というエイリアスを与えられた特別なバリデータが提供されています。\n例えば、次の規則は `title` と `description` の両方が安全な属性であると宣言しています。\n\n```php\npublic function rules()\n{\n    return [\n        [['title', 'description'], 'safe'],\n    ];\n}\n```\n\n\n### 安全でない属性 <span id=\"unsafe-attributes\"></span>\n\n上記で説明したように、[[yii\\base\\Model::scenarios()]] メソッドは二つの目的を持っています。\nすなわち、どの属性が検証されるべきかを決めることと、どの属性が安全であるかを決めることです。\nめったにない場合として、属性を検証する必要はあるが、安全であるという印は付けたくない、ということがあります。\nそういう時は、下の例の `secret` 属性のように、名前の前に感嘆符 `!` を付けて `scenarios()` の中で宣言することが出来ます。\n\n```php\npublic function scenarios()\n{\n    return [\n        self::SCENARIO_LOGIN => ['username', 'password', '!secret'],\n    ];\n}\n```\n\nこのモデルが `login` シナリオにある場合、三つの属性は全て検証されます。\nしかし、`username` と `password` の属性だけが一括代入が可能です。\n`secret` 属性に入力値を割り当てるためには、下記のように明示的に代入を実行する必要があります。\n\n```php\n$model->secret = $secret;\n```\n\n同じ事が `rules()` メソッドの中でも出来ます。\n\n```php\npublic function rules()\n{\n    return [\n        [['username', 'password', '!secret'], 'required', 'on' => 'login']\n    ];\n}\n```\n\nこの場合、`username`、`password` そして `secret` の属性が必須項目とされますが、`secret` は明示的に代入される必要があります。\n\n\n## データのエクスポート <span id=\"data-exporting\"></span>\n\nモデルを他の形式にエクスポートする必要が生じることはよくあります。例えば、モデルのコレクションを JSON や Excel 形式に変換したい場合があるでしょう。\nこのエクスポートのプロセスは二つの独立したステップに分割することが出来ます。\n\n- モデルが配列に変換され、\n- 配列が目的の形式に変換される。\n\nあなたは最初のステップだけに注力することが出来ます。\nと言うのは、第二のステップは汎用的なデータ・フォーマッタ、例えば [[yii\\web\\JsonResponseFormatter]] によって達成できるからです。\n\nモデルを配列に変換する最も簡単な方法は、[[yii\\base\\Model::$attributes]] プロパティを使うことです。\n例えば、\n\n```php\n$post = \\app\\models\\Post::findOne(100);\n$array = $post->attributes;\n```\n\nデフォルトでは、[[yii\\base\\Model::$attributes]] プロパティは [[yii\\base\\Model::attributes()]]\nで宣言されている *全て* の属性の値を返します。\n\nモデルを配列に変換するためのもっと柔軟で強力な方法は、[[yii\\base\\Model::toArray()]] メソッドを使うことです。\nこのメソッドのデフォルトの動作は [[yii\\base\\Model::$attributes]] のそれと同じものです。\nしかしながら、このメソッドを使うと、どのデータ項目 (*フィールド* と呼ばれます) を結果の配列に入れるか、そして、その項目にどのような書式を適用するかを選ぶことが出来ます。\n実際、[レスポンス形式の設定](rest-response-formatting.md) で説明されているように、RESTful ウェブサービスの開発においては、\nこれがモデルをエクスポートするデフォルトの方法となっています。\n\n\n### フィールド <span id=\"fields\"></span>\n\nフィールドとは、単に、モデルの [[yii\\base\\Model::toArray()]] メソッドを呼ぶことによって取得される配列に含まれる、\n名前付きの要素のことです。\n\nデフォルトでは、フィールドの名前は属性の名前と等しいものになります。\nしかし、このデフォルトの動作は、[[yii\\base\\Model::fields()|fields()]] および/または [[yii\\base\\Model::extraFields()|extraFields()]] メソッドをオーバーライドして、変更することが出来ます。\nどちらのメソッドも、フィールド定義のリストを返すものです。\n`fields()` によって定義されるフィールドは、デフォルト・フィールドです。すなわち、`toArray()` はデフォルトでこれらのフィールドを返す、ということを意味します。\n`extraFields()` メソッドは、`$expand` パラメータによって指定する限りにおいて `toArray()` によって返される、追加のフィールドを定義するものです。\n例として、次のコードは、`fields()` で定義された全てのフィールドと、(`extraFields()` で定義されていれば)\n`prettyName` と `fullAddress` フィールドを返すものです。\n\n```php\n$array = $model->toArray([], ['prettyName', 'fullAddress']);\n```\n\n`fields()` をオーバーライドして、フィールドを追加、削除、リネーム、再定義することが出来ます。\n`fields()` の返り値は配列でなければなりません。配列のキーはフィールド名であり、配列の値は対応するフィールド定義です。\nフィールドの定義には、プロパティ/属性 の名前か、または、対応するフィールドの値を返す無名関数を使うことが出来ます。\nフィールド名がそれを定義する属性名と同一であるという特殊な場合においては、配列のキーを省略することが出来ます。\n例えば、\n\n```php\n// 明示的に全てのフィールドをリストする方法。(API の後方互換性を保つために) DB テーブルや\n// モデル属性の変更がフィールドの変更を引き起こさないことを保証したい場合に適している。\npublic function fields()\n{\n    return [\n        // フィールド名が属性名と同じ\n        'id',\n\n        // フィールド名は \"email\"、対応する属性名は \"email_address\"\n        'email' => 'email_address',\n\n        // フィールド名は \"name\"、その値は PHP コールバックで定義\n        'name' => function () {\n            return $this->first_name . ' ' . $this->last_name;\n        },\n    ];\n}\n\n// いくつかのフィールドを除外する方法。親の実装を継承しつつ、公開すべきでないフィールドを\n// 除外したいときに最適。\npublic function fields()\n{\n    $fields = parent::fields();\n\n    // 公開すべきでない情報を含むフィールドを削除する\n    unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);\n\n    return $fields;\n}\n```\n\n> Warning: デフォルトではモデルの全ての属性がエクスポートされる配列に含まれるため、データを精査して、\n> 公開すべきでない情報が含まれていないことを確認しなければなりません。\n> そういう情報がある場合は、`fields()` をオーバーライドして、除外しなければなりません。\n> 上記の例では、`auth_key`、`password_hash` および `password_reset_token` を除外しています。\n\n\n## ベスト・プラクティス <span id=\"best-practices\"></span>\n\nモデルは、ビジネスのデータ、規則、ロジックを表わす中心的なオブジェクトです。\nモデルは、たいてい、さまざまな場所で再利用される必要があります。\n良く設計されたアプリケーションでは、通常、モデルは [コントローラ](structure-controllers.md) よりもはるかに太ったものになります。\n\n要約すると、モデルは、\n\n* ビジネス・データを表現する属性を含むことが出来ます。\n* データの有効性と整合性を保証する検証規則を含むことが出来ます。\n* ビジネス・ロジックを実装するメソッドを含むことが出来ます。\n* リクエスト、セッション、または他の環境データに直接アクセスするべきではありません。\n  これらのデータは、[コントローラ](structure-controllers.md) によってモデルに注入されるべきです。\n* HTML を埋め込むなどの表示用のコードは避けるべきです -  表示は [ビュー](structure-views.md) で行う方が良いです。\n* あまりに多くの [シナリオ](#scenarios) を一つのモデルで持つことは避けましょう。\n\n大規模で複雑なシステムを開発するときには、たいてい、上記の最後にあげた推奨事項を考慮するのが良いでしょう。\nそういうシステムでは、モデルは数多くの場所で使用され、それに従って、数多くの規則セットやビジネス・ロジックを含むため、\n非常に太ったものになり得ます。\nコードの一ヶ所に触れるだけで数ヶ所の違った場所に影響が及ぶため、ついには、モデルのコードの保守が悪夢になってしまうこともよくあります。\nモデルのコードの保守性を高めるためには、以下の戦略をとることが出来ます。\n\n* 異なる [アプリケーション](structure-applications.md) または [モジュール](structure-modules.md)\n  によって共有される一連の基底モデル・クラスを定義します。\n  これらのモデル・クラスは、すべてで共通に使用される最小限の規則セットとロジックのみを含むべきです。\n* モデルを使用するそれぞれの [アプリケーション](structure-applications.md) または [モジュール](structure-modules.md) において、\n  対応する基底モデル・クラスから拡張した具体的なモデル・クラスを定義します。\n  この具体的なモデル・クラスが、そのアプリケーションやモジュールに固有の規則やロジックを含むべきです。\n\n例えば、[アドバンスト・プロジェクト・テンプレート](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide-ja/README.md) の中で、基底モデル・クラス `common\\models\\Post` を定義することが出来ます。\n次に、フロントエンド・アプリケーションにおいては、`common\\models\\Post` から拡張した具体的なモデル・クラス\n`frontend\\models\\Post` を定義して使います。\nまた、バックエンド・アプリケーションにおいても、同様に、`backend\\models\\Post` を定義します。\nこの戦略を取ると、`frontend\\models\\Post` の中のコードはフロントエンド・アプリケーション固有のものであると保証することが出来ます。\nそして、フロントエンドのコードにどのような変更を加えても、バックエンド・アプリケーションを壊すかもしれないと心配する必要がなくなります。\n"
  },
  {
    "path": "docs/guide-ja/structure-modules.md",
    "content": "モジュール\n==========\n\nモジュールは、[モデル](structure-models.md)、[ビュー](structure-views.md)、[コントローラ](structure-controllers.md)、およびその他の支援コンポーネントから構成される自己充足的なソフトウェアのユニットです。\nモジュールが [アプリケーション](structure-applications.md) にインストールされている場合、エンド・ユーザはモジュールのコントローラにアクセスする事が出来ます。\nこれらのことを理由として、モジュールは小さなアプリケーションと見なされることがよくあります。\nしかし、モジュールは単独では配備できず、アプリケーションの中に存在しなければならないという点で\n[アプリケーション](structure-applications.md) とは異なります。\n\n\n## モジュールを作成する <span id=\"creating-modules\"></span>\n\nモジュールは、モジュールの [[yii\\base\\Module::basePath|ベース・パス]] と呼ばれるディレクトリとして編成されます。\nこのディレクトリの中に、ちょうどアプリケーションの場合と同じように、`controllers`、`models`、`views` のようなサブ・ディレクトリが存在して、コントローラ、モデル、ビュー、その他のコードを収納しています。\n次の例は、モジュール内の中身を示すものです。\n\n```\nforum/\n    Module.php                   モジュール・クラス・ファイル\n    controllers/                 コントローラ・クラス・ファイルを含む\n        DefaultController.php    デフォルトのコントローラ・クラス・ファイル\n    models/                      モデル・クラス・ファイルを含む\n    views/                       コントローラのビューとレイアウトのファイルを含む\n        layouts/                 レイアウトのビュー・ファイルを含む\n        default/                 DefaultController のためのビュー・ファイルを含む\n            index.php            index ビュー・ファイル\n```\n\n\n### モジュール・クラス <span id=\"module-classes\"></span>\n\n全てのモジュールは [[yii\\base\\Module]] から拡張したユニークなモジュール・クラスを持たなければなりません。\nモジュール・クラスは、モジュールの [[yii\\base\\Module::basePath|ベース・パス]] 直下に配置されて [オートロード可能](concept-autoloading.md) になっていなければなりません。\nモジュールがアクセスされたとき、対応するモジュール・クラスの単一のインスタンスが作成されます。\n[アプリケーションのインスタンス](structure-applications.md) と同じように、モジュールのインスタンスは、\nモジュール内のコードがデータとコンポーネントを共有するために使用されます。\n\n次のコードは、モジュール・クラスがどのようなものかを示す例です。\n\n```php\nnamespace app\\modules\\forum;\n\nclass Module extends \\yii\\base\\Module\n{\n    public function init()\n    {\n        parent::init();\n\n        $this->params['foo'] = 'bar';\n        // ... 他の初期化コード ...\n    }\n}\n```\n\n`init` メソッドがモジュールのプロパティを初期化するためのコードをたくさん含む場合は、\nそれを [構成情報](concept-configurations.md) の形で保存し、`init()` の中で次のコードを使って読み出すことも可能です。\n\n```php\npublic function init()\n{\n    parent::init();\n    // config.php からロードした構成情報でモジュールを初期化する\n    \\Yii::configure($this, require __DIR__ . '/config.php');\n}\n```\n\nここで、構成情報ファイル `config.php` は、[アプリケーションの構成情報](structure-applications.md#application-configurations) の場合と同じように、\n次のような内容を含むことが出来ます。\n\n```php\n<?php\nreturn [\n    'components' => [\n        // コンポーネントの構成情報のリスト\n    ],\n    'params' => [\n        // パラメータのリスト\n    ],\n];\n```\n\n\n### モジュール内のコントローラ <span id=\"controllers-in-modules\"></span>\n\nモジュールの中でコントローラを作成するときは、コントローラ・クラスをモジュール・クラスの名前空間の\n`controllers` サブ名前空間に置くことが規約です。\nこのことは、同時に、コントローラのクラス・ファイルをモジュールの [[yii\\base\\Module::basePath|ベース・パス]] 内の `controllers` ディレクトリに置くべきことをも意味します。\n例えば、前の項で示された `forum` モジュールの中で `post` コントローラを作成するためには、\n次のようにしてコントローラを宣言しなければなりません。\n\n```php\nnamespace app\\modules\\forum\\controllers;\n\nuse yii\\web\\Controller;\n\nclass PostController extends Controller\n{\n    // ...\n}\n```\n\nコントローラ・クラスの名前空間は、[[yii\\base\\Module::controllerNamespace]] プロパティを構成してカスタマイズすることが出来ます。\nいくつかのコントローラがこの名前空間の外にある場合でも、[[yii\\base\\Module::controllerMap]] プロパティを構成することによって、それらをアクセス可能にすることが出来ます。\nこれは、[アプリケーションでのコントローラ・マップ](structure-applications.md#controller-map) の場合と同様です。\n\n\n### モジュール内のビュー <span id=\"views-in-modules\"></span>\n\nモジュール内のビューは、モジュールの [[yii\\base\\Module::basePath|ベース・パス]] 内の `views` ディレクトリに置かれなくてはなりません。\nモジュール内のコントローラによってレンダリングされるビューは、ディレクトリ `views/ControllerID` の下に置きます。\nここで、`ControllerID` は [コントローラ ID](structure-controllers.md#routes) を指します。\n例えば、コントローラ・クラスが `PostController` である場合、ディレクトリはモジュールの [[yii\\base\\Module::basePath|ベース・パス]]\nの中の `views/post` となります。\n\nモジュールは、そのモジュールのコントローラによってレンダリングされるビューに適用される [レイアウト](structure-views.md#layouts) を指定することが出来ます。\nレイアウトは、デフォルトでは `views/layouts` ディレクトリに置かれなければならず、また、\n[[yii\\base\\Module::layout]] プロパティがレイアウトの名前を指すように構成しなければなりません。\n`layout` プロパティを構成しない場合は、アプリケーションのレイアウトが代りに使用されます。\n\n\n### モジュール内のコンソールコマンド <span id=\"console-commands-in-modules\"></span>\n\n[コンソール](tutorial-console.md) モードで使用する事が出来るコマンドをモジュール内で宣言することも可能です。\n\nあなたのコマンドがコマンド・ライン・ユーティリティから見えるようにするためには、Yii がコンソール・モードで実行されたときに\n[[yii\\base\\Module::controllerNamespace]] を変更して、コマンドの名前空間を指し示すようにする必要があります。\n\nそれを達成する一つの方法は、モジュールの `init()` メソッドの中で Yii アプリケーションのインスタンスの型を調べるという方法です。\n\n```php\npublic function init()\n{\n    parent::init();\n    if (Yii::$app instanceof \\yii\\console\\Application) {\n        $this->controllerNamespace = 'app\\modules\\forum\\commands';\n    }\n}\n```\n\nこのようにすれば、コマンドラインから次のルートを使ってあなたのコマンドを使用する事が出来るようになります。\n\n```\nyii <module_id>/<command>/<sub_command>\n```\n\n## モジュールを使う <span id=\"using-modules\"></span>\n\nアプリケーションの中でモジュールを使うためには、アプリケーションの [[yii\\base\\Application::modules|modules]]\nプロパティのリストにそのモジュールを載せてアプリケーションを構成するだけで大丈夫です。\n[アプリケーションの構成情報](structure-applications.md#application-configurations) の中の次のコードは、`forum` モジュールを使うようにするものです。\n\n```php\n[\n    'modules' => [\n        'forum' => [\n            'class' => 'app\\modules\\forum\\Module',\n            // ... モジュールのその他の構成情報 ...\n        ],\n    ],\n]\n```\n\n> Info: モジュール内のコマンドを使用するためには、\n> モジュールを [コンソール・アプリケーション設定](tutorial-console.md#configuration) にも含める必要があります。\n\n[[yii\\base\\Application::modules|modules]] プロパティは、モジュールの構成情報の配列を取ります。\n各配列のキーは、アプリケーションの全てのモジュールの中でそのモジュールを特定するためのユニークな *モジュール ID* を表します。\nそして、対応する配列の値は、そのモジュールを作成するための [構成情報](concept-configurations.md) です。\n\n\n### ルート <span id=\"routes\"></span>\n\nアプリケーションの中のコントローラをアクセスするのと同じように、[ルート](structure-controllers.md#routes)\nがモジュールの中のコントローラを指し示すために使われます。\nモジュール内のコントローラのルートは、モジュール ID で始まり、[コントローラ ID](structure-controllers.md#controller-ids)、\n[アクション ID](structure-controllers.md#action-ids) と続くものでなければなりません。\n例えば、アプリケーションが `forum` という名前のモジュールを使用している場合、`forum/post/index` というルートは、\n`forum` モジュール内の `post` コントローラの `index` アクションを表します。\nルートがモジュール ID だけを含む場合は、[[yii\\base\\Module::defaultRoute]] プロパティ (デフォルト値は `default` です) が、どのコントローラ・アクションが使用されるべきかを決定します。\nこれは、`forum` というルートは `forum` モジュール内の `default` コントローラを表すという意味です。\n\nモジュールのための URL マネージャの規則は [[yii\\web\\UrlManager::parseRequest()]] が起動される前に追加されなくてはなりません。\nすなわち、モジュールの `init()` の中で規則を追加しても動作しない、ということです。なぜなら、モジュールの初期化はルートの処理が済んだ後になるからです。\n従って、URL 規則は [ブートストラップ段階](structure-extensions.md#bootstrapping-classes) で追加される必要があります。\nまた、モジュールの URL 規則を [[\\yii\\web\\GroupUrlRule]] に入れるのも良い方法です。\n\nモジュールが [バージョン管理された API](rest-versioning.md) で使用される場合は、その URL 規則はアプリケーション構成情報の\n`urlManager` のセクションに直接に追加されなければなりません。\n\n\n### モジュールにアクセスする <span id=\"accessing-modules\"></span>\n\nモジュール内において、モジュール ID や、モジュールのパラメータ、モジュールのコンポーネントなどにアクセスするために、[モジュール・クラス](#module-classes) のインスタンスを取得する必要があることがよくあります。\n次の文を使ってそうすることが出来ます。\n\n```php\n$module = MyModuleClass::getInstance();\n```\n\nここで `MyModuleClass` は、当該モジュール・クラスの名前を指すものです。\n`getInstance()` メソッドは、現在リクエストされているモジュール・クラスのインスタンスを返します。\nモジュールがリクエストされていない場合は、このメソッドは `null` を返します。モジュール・クラスの新しいインスタンスを手動で作成しようとしてはいけないことに注意してください。\n手動で作成したインスタンスは、リクエストに対するレスポンスとして Yii によって作成されたインスタンスとは別のものになります。\n\n> Info: モジュールを開発するとき、モジュールが固定の ID を使うと仮定してはいけません。\n  なぜなら、モジュールは、アプリケーションや他のモジュールの中で使うときに、任意の ID と結び付けることが出来るからです。\n  モジュール ID を取得するためには、上記の方法を使って最初にモジュールのインスタンスを取得し、\n  そして `$module->id` によって ID を取得しなければなりません。\n\nモジュールのインスタンスにアクセスするためには、次の二つの方法を使うことも出来ます。\n\n```php\n// ID が \"forum\" である子モジュールを取得する\n$module = \\Yii::$app->getModule('forum');\n\n// 現在リクエストされているコントローラが属するモジュールを取得する\n$module = \\Yii::$app->controller->module;\n```\n\n最初の方法は、モジュール ID を知っている時しか役に立ちません。一方、第二の方法は、\nリクエストされているコントローラについて知っている場合に使うのに最適な方法です。\n\nいったんモジュールのインスタンスをとらえれば、モジュールに登録されたパラメータやコンポーネントにアクセスすることが可能になります。例えば、\n\n```php\n$maxPostCount = $module->params['maxPostCount'];\n```\n\n\n### モジュールをブートストラップする <span id=\"bootstrapping-modules\"></span>\n\nいくつかのモジュールは、全てのリクエストで毎回走らせる必要があります。[[yii\\debug\\Module|デバッグ]]・モジュールがその一例です。\nそうするためには、そのようなモジュールをアプリケーションの [[yii\\base\\Application::bootstrap|bootstrap]] プロパティのリストに挙げます。\n\n例えば、次のアプリケーションの構成情報は、`debug` モジュールが常にロードされることを保証するものです。\n\n```php\n[\n    'bootstrap' => [\n        'debug',\n    ],\n\n    'modules' => [\n        'debug' => 'yii\\debug\\Module',\n    ],\n]\n```\n\n\n## 入れ子のモジュール <span id=\"nested-modules\"></span>\n\nモジュールはレベルの制限無く入れ子にすることが出来ます。つまり、モジュールは別のモジュールを含むことが出来、その含まれたモジュールもさらに別のモジュールを含むことが出来ます。\n含む側を *親モジュール*、含まれる側を *子モジュール* と呼びます。\n子モジュールは、親モジュールの [[yii\\base\\Module::modules|modules]] プロパティの中で宣言されなければなりません。例えば、\n\n```php\nnamespace app\\modules\\forum;\n\nclass Module extends \\yii\\base\\Module\n{\n    public function init()\n    {\n        parent::init();\n\n        $this->modules = [\n            'admin' => [\n                // ここはもっと短い名前空間の使用を考慮すべきです\n                'class' => 'app\\modules\\forum\\modules\\admin\\Module',\n            ],\n        ];\n    }\n}\n```\n\n入れ子にされたモジュールの中にあるコントローラのルートは、全ての祖先のモジュールの ID を含まなければなりません。\n例えば、`forum/admin/dashboard/index` というルートは、`forum` モジュールの子モジュールである `admin` モジュールの\n`dashboard` コントローラの `index` アクションを表します。\n\n> Info: [[yii\\base\\Module::getModule()|getModule()]] メソッドは、親モジュールに直接属する子モジュールだけを返します。\n[[yii\\base\\Application::loadedModules]] プロパティがロードされた全てのモジュールのリストを保持しています。\nこのリストには、直接の子と孫以下の両方のモジュールが含まれ、クラス名によってインデックスされています。\n\n## モジュール内からコンポーネントにアクセスする\n\nバージョン 2.0.13 以降、モジュールは [ツリー走査](concept-service-locator.md#tree-traversal) をサポートしています。\nこれによって、モジュールの開発者は(アプリケーション)コンポーネントを参照するのに、\nサービス・ロケータとして自身のモジュールを使うことが出来るようになりました。\nこれが意味することは、`Yii::$app->get('db')` よりも `$module->get('db')` を使う方が良い、ということです。\nモジュールのユーザは、異なるコンポーネント(構成)が要求される場合は、モジュールで使用する特定のコンポーネントを指定することが出来ます。\n\n例えば、次のような部分的なアプリケーション構成が可能です。\n\n```php\n'components' => [\n    'db' => [\n        'tablePrefix' => 'main_',\n        'class' => Connection::class,\n        'enableQueryCache' => false\n    ],\n],\n'modules' => [\n    'mymodule' => [\n        'components' => [\n            'db' => [\n                'tablePrefix' => 'module_',\n                'class' => Connection::class\n            ],\n        ],\n    ],\n],\n```\n\nアプリケーションのデータベース・テーブルは `main_` という接頭辞を持つ一方で、モジュールのデータベース・テーブルは `module_` という接頭辞を持ちます。\n上記の構成はマージされないということに注意して下さい。例えば、モジュールのコンポーネントではクエリ・キャッシュはデフォルト値に従って有効なままになります。\n\n## ベスト・プラクティス <span id=\"best-practices\"></span>\n\nモジュールは、それぞれ密接に関係する一連の機能を含む数個のグループに分割できるような、\n規模の大きなアプリケーションに最も適しています。\nそのような機能グループをそれぞれモジュールとして、特定の個人やチームによって開発することが出来ます。\n\nモジュールは、また、機能グループ・レベルでコードを再利用するための良い方法でもあります。\nある種のよく使われる機能、例えばユーザ管理やコメント管理などは、全て、将来のプロジェクトで容易に再利用できるように、\nモジュールの形式で開発することが出来ます。\n"
  },
  {
    "path": "docs/guide-ja/structure-overview.md",
    "content": "概要\n====\n\nYii のアプリケーションは [モデル・ビュー・コントローラ (MVC)](https://ja.wikipedia.org/wiki/Model_View_Controller) アーキテクチャ・パターンに従って編成されています。\n[モデル](structure-models.md) は、データ、ビジネス・ロジック、規則を表現します。\n[ビュー](structure-views.md) は、モデルの出力表現です。\nそして [コントローラ](structure-controllers.md) は入力を受け取って、それを [モデル](structure-models.md) と [ビュー](structure-views.md) のためのコマンドに変換します。\n\nMVC 以外にも、Yii のアプリケーションは下記の要素を持っています。\n\n* [エントリ・スクリプト](structure-entry-scripts.md): エンド・ユーザから直接アクセスできる PHP スクリプトです。\n  これはリクエスト処理サイクルを開始する役目を持っています。\n* [アプリケーション](structure-applications.md): グローバルにアクセス可能なオブジェクトであり、\n  アプリケーション・コンポーネントを管理し、連携させて、リクエストに応えます。\n* [アプリケーション・コンポーネント](structure-application-components.md): アプリケーションと共に登録されたオブジェクトであり、\n  リクエストに応えるための様々なサービスを提供します。\n* [モジュール](structure-modules.md): それ自身に完全な MVC を含む自己完結的なパッケージです。\n  アプリケーションは複数のモジュールとして編成することが出来ます。\n* [フィルタ](structure-filters.md): 各リクエストが実際に処理される前と後に、\n  コントローラから呼び出される必要があるコードを表現します。\n* [ウィジェット](structure-widgets.md): [ビュー](structure-views.md) に埋め込むことが出来るオブジェクトです。\n  コントローラのロジックを含むことが可能で、異なるビューで再利用することが出来ます。\n\n下の図がアプリケーションの静的な構造を示すものです。\n\n![アプリケーションの静的な構造](images/application-structure.png)\n"
  },
  {
    "path": "docs/guide-ja/structure-views.md",
    "content": "ビュー\n======\n\nビューは [MVC](https://ja.wikipedia.org/wiki/Model_View_Controller) アーキテクチャの一部を成すものです。\nビューはエンド・ユーザにデータを表示することに責任を持つコードです。\nウェブ・アプリケーションにおいては、ビューは、通常、*ビュー・テンプレート* の形式、すなわち、\n主として HTML コードと表示目的の PHP コードを含む PHP スクリプト・ファイルとして作成されます。\nそして、ビュー・テンプレートを管理する [[yii\\web\\View|ビュー]] [アプリケーション・コンポーネント](structure-application-components.md) が、ビューの構築とレンダリングを助けるためによく使われるメソッドを提供します。\nなお、簡潔さを重視して、ビュー・テンプレートまたはビュー・テンプレート・ファイルを単にビューと呼ぶことがよくあります。\n\n\n## ビューを作成する <span id=\"creating-views\"></span>\n\n前述のように、ビューは HTML と PHP コードが混ざった単なる PHP スクリプトです。\n次に示すのは、ログイン・フォームを表示するビューです。\nご覧のように、PHP コードがタイトルやフォームなど動的なコンテントを生成するのに使われ、HTML コードがそれらを編成して表示可能な HTML ページを作っています。\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n/**\n * @var \\yii\\web\\View $this\n * @var \\yii\\widgets\\ActiveForm $form\n * @var \\app\\models\\LoginForm $model\n */\n\n$this->title = 'ログイン';\n?>\n<h1><?= Html::encode($this->title) ?></h1>\n\n<p>次の項目を入力してログインしてください:</p>\n\n<?php $form = ActiveForm::begin(); ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n    <?= Html::submitButton('ログイン') ?>\n<?php ActiveForm::end(); ?>\n```\n\nビューの中では、このビュー・テンプレートを管理しレンダリングしている [[yii\\web\\View|ビュー・コンポーネント]] を参照する\n`$this` にアクセスすることが出来ます。\n\n`$this` 以外に、上記の例の `$model` のように、事前に定義される変数をビューの中に置くことが出来ます。\nこのような変数は、[ビューのレンダリング](#rendering-views) をトリガする [コントローラ](structure-controllers.md)\nなどのオブジェクトによってビューに *プッシュ* されるデータを表します。\n\n> Tip: 上の例では、事前に定義される変数は、IDE に認識されるように、ビューの先頭のコメント・ブロックの中にリストされています。\n  これは、ビューにドキュメントを付けるのにも良い方法です。\n\n\n### セキュリティ <span id=\"security\"></span>\n\nHTML ページを生成するビューを作成するときは、エンド・ユーザから受け取るデータを表示する前にエンコード および/または フィルタすることが重要です。\nそうしなければ、あなたのアプリケーションは [クロス・サイト・スクリプティング](https://ja.wikipedia.org/wiki/%E3%82%AF%E3%83%AD%E3%82%B9%E3%82%B5%E3%82%A4%E3%83%88%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0) 攻撃を\nこうむるおそれがあります。\n\nプレーン・テキストを表示するためには、まず [[yii\\helpers\\Html::encode()]] を呼んでエンコードします。\n例えば、次のコードはユーザの名前を表示する前にエンコードしています。\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n\n<div class=\"username\">\n    <?= Html::encode($user->name) ?>\n</div>\n```\n\nHTML コンテントを表示するためには、[[yii\\helpers\\HtmlPurifier]] を使って、最初にコンテントをフィルタします。\n例えば、次のコードは、投稿のコンテントを表示する前にフィルタしています。\n\n```php\n<?php\nuse yii\\helpers\\HtmlPurifier;\n?>\n\n<div class=\"post\">\n    <?= HtmlPurifier::process($post->text) ?>\n</div>\n```\n\n> Tip: HTMLPurifier は、出力を安全なものにすることにおいては素晴らしい仕事をしますが、速くはありません。\n  アプリケーションが高いパフォーマンスを要求する場合は、フィルター結果を [キャッシュ](caching-overview.md) することを考慮すべきです。\n\n\n### ビューを編成する <span id=\"organizing-views\"></span>\n\n[コントローラ](structure-controllers.md) や [モデル](structure-models.md) と同じように、ビューを編成するための規約があります。.\n\n* コントローラによって表示されるビューは、デフォルトでは、ディレクトリ `@app/views/ControllerID` の下に置かれるべきものです。\n  ここで、`ControllerID` は [コントローラ ID](structure-controllers.md#routes) を指します。\n  例えば、コントローラ・クラスが `PostController` である場合、ディレクトリは `@app/views/post` となります。\n  `PostCommentController` の場合は、ディレクトリは `@app/views/post-comment` です。\n  また、コントローラがモジュールに属する場合は、ディレクトリは [[yii\\base\\Module::basePath|モジュール・ディレクトリ]] の下の `views/ControllerID` です。\n* [ウィジェット](structure-widgets.md) で表示されるビューは、デフォルトでは、`WidgetPath/views` ディレクトリの下に置かれるべきものです。\n  ここで、`WidgetPath` は、ウィジェットのクラス・ファイルを含んでいるディレクトリを指します。\n* 他のオブジェクトによって表示されるビューについても、ウィジェットの場合と同じ規約に従うことが推奨されます。\n\nこれらのデフォルトのビュー・ディレクトリは、コントローラやウィジェットの [[yii\\base\\ViewContextInterface::getViewPath()]]\nメソッドをオーバーライドすることでカスタマイズすることが可能です。\n\n\n## ビューをレンダリングする <span id=\"rendering-views\"></span>\n\n[コントローラ](structure-controllers.md) の中でも、[ウィジェット](structure-widgets.md) の中でも、または、その他のどんな場所でも、ビューをレンダリングするメソッドを呼ぶことによってビューをレンダリングすることが出来ます。\nこれらのメソッドは、下記に示されるような類似のシグニチャを共有します。\n\n```\n/**\n * @param string $view ビュー名またはファイル・パス (実際のレンダリング・メソッドに依存する)\n * @param array $params ビューに引き渡されるデータ\n * @return string レンダリングの結果\n */\nmethodName($view, $params = [])\n```\n\n\n### コントローラでのレンダリング <span id=\"rendering-in-controllers\"></span>\n\n[コントローラ](structure-controllers.md) の中では、ビューをレンダリングするために次のコントローラ・メソッドを呼ぶことが出来ます。\n\n* [[yii\\base\\Controller::render()|render()]]: [名前付きビュー](#named-views) をレンダリングし、\n  その結果に [レイアウト](#layouts) を適用する。\n* [[yii\\base\\Controller::renderPartial()|renderPartial()]]: [名前付きビュー](#named-views) をレイアウトなしでレンダリングする。\n* [[yii\\web\\Controller::renderAjax()|renderAjax()]]: [名前付きビュー](#named-views) をレイアウトなしでレンダリングし、登録されている全ての JS/CSS スクリプトおよびファイルを注入する。\n  通常、AJAX ウェブ・リクエストに対するレスポンスにおいて使用される。\n* [[yii\\base\\Controller::renderFile()|renderFile()]]: ビュー・ファイルのパスまたは [エイリアス](concept-aliases.md)\n  の形式で指定されたビューをレンダリングする。\n* [[yii\\base\\Controller::renderContent()|renderContent()]]: 静的な文字列をレンダリングして、現在適用可能な [レイアウト](#layouts)に埋め込む。\n  このメソッドは バージョン 2.0.1 以降で使用可能。\n\n例えば、\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse app\\models\\Post;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\n\nclass PostController extends Controller\n{\n    public function actionView($id)\n    {\n        $model = Post::findOne($id);\n        if ($model === null) {\n            throw new NotFoundHttpException;\n        }\n\n        // \"view\" という名前のビューをレンダリングし、レイアウトを適用する\n        return $this->render('view', [\n            'model' => $model,\n        ]);\n    }\n}\n```\n\n\n### ウィジェットでのレンダリング <span id=\"rendering-in-widgets\"></span>\n\n[ウィジェット](structure-widgets.md) の中では、ビューをレンダリングするために、次のウィジェット・メソッドを使用することが出来ます。\n\n* [[yii\\base\\Widget::render()|render()]]: [名前付きビュー](#named-views) をレンダリングする。\n* [[yii\\base\\Widget::renderFile()|renderFile()]]: ビュー・ファイルのパスまたは [エイリアス](concept-aliases.md)\n  の形式で指定されたビューをレンダリングする。\n\n例えば、\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Widget;\nuse yii\\helpers\\Html;\n\nclass ListWidget extends Widget\n{\n    public $items = [];\n\n    public function run()\n    {\n        // \"list\" という名前のビューをレンダリングする\n        return $this->render('list', [\n            'items' => $this->items,\n        ]);\n    }\n}\n```\n\n\n### ビューでのレンダリング <span id=\"rendering-in-views\"></span>\n\n[[yii\\base\\View|ビュー・コンポーネント]] によって提供される下記のメソッドのどれかを使うと、ビューの中で、別のビューをレンダリングすることが出来ます。\n\n* [[yii\\base\\View::render()|render()]]: [名前付きビュー](#named-views) をレンダリングする。\n* [[yii\\web\\View::renderAjax()|renderAjax()]]: [名前付きビュー](#named-views) をレンダリングし、登録されている全ての JS/CSS スクリプトおよびファイルを注入する。\n  通常、AJAX ウェブリクエストに対するレスポンスにおいて使用される。\n* [[yii\\base\\View::renderFile()|renderFile()]]: ビュー・ファイルのパスまたは\n  [エイリアス](concept-aliases.md) の形式で指定されたビューをレンダリングする。\n\n例えば、ビューの中の次のコードは、現在レンダリングされているビューと同じディレクトリにある `_overview.php` というビュー・ファイルをレンダリングします。\nビューでは `$this` が [[yii\\base\\View|ビュー]]・コンポーネントを参照することを思い出してください。\n\n```php\n<?= $this->render('_overview') ?>\n```\n\n\n### 他の場所でのレンダリング <span id=\"rendering-in-other-places\"></span>\n\n場所がどこであれ、`Yii::$app->view` という式によって [[yii\\base\\View|ビュー]]・アプリケーション・コンポーネントにアクセスすることが出来ますから、\n前述の [[yii\\base\\View|ビュー]]・コンポーネント・メソッドを使ってビューをレンダリングすることが出来ます。例えば、\n\n```php\n// ビュー・ファイル \"@app/views/site/license.php\" を表示\necho \\Yii::$app->view->renderFile('@app/views/site/license.php');\n```\n\n\n### 名前付きビュー <span id=\"named-views\"></span>\n\nビューをレンダリングするとき、ビューを指定するのには、ビューの名前か、ビュー・ファイルのパス/エイリアスか、どちらかを使うことが出来ます。\nたいていの場合は、より簡潔で柔軟な前者を使います。名前を使って指定されるビューを *名前付きビュー* と呼びます。\n\nビューの名前は、以下の規則に従って、対応するビュー・ファイルのパスに解決されます。\n\n* ビュー名はファイル拡張子を省略することが出来ます。その場合、`.php` が拡張子として使われます。\n  例えば、`about` というビュー名は `about.php` というファイル名に対応します。\n* ビュー名が二つのスラッシュ (`//`) で始まる場合は、対応するビュー・ファイルのパスは `@app/views/ViewName` となります。\n  つまり、ビュー・ファイルは [[yii\\base\\Application::viewPath|アプリケーションのビュー・パス]] の下で探されます。\n  例えば、`//site/about` は `@app/views/site/about.php` へと解決されます。\n* ビュー名が一つのスラッシュ (`/`) で始まる場合は、ビュー・ファイルのパスは、ビュー名の前に、現在アクティブな [モジュール](structure-modules.md) の\n  [[yii\\base\\Module::viewPath|ビュー・パス]] を置くことによって形成されます。\n  アクティブなモジュールが無い場合は、`@app/views/ViewName` が使用されます。\n  例えば、`/user/create` は、現在アクティブなモジュールが `user` である場合は、`@app/modules/user/views/user/create.php` へと解決されます。\n  アクティブなモジュールが無い場合は、ビュー・ファイルのパスは `@app/views/user/create.php` となります。\n* ビューが [[yii\\base\\View::context|コンテキスト]] を伴ってレンダリングされ、そのコンテキストが [[yii\\base\\ViewContextInterface]] を実装している場合は、\n  ビュー・ファイルのパスは、コンテキストの [[yii\\base\\ViewContextInterface::getViewPath()|ビュー・パス]] をビュー名の前に置くことによって形成されます。\n  これは、主として、コントローラとウィジェットの中でレンダリングされるビューに当てはまります。\n  例えば、コンテキストが `SiteController` コントローラである場合、`about` は `@app/views/site/about.php` へと解決されます。\n* あるビューが別のビューの中でレンダリングされる場合は、後者のビュー・ファイルを含んでいるディレクトリが前者のビュー名の前に置かれて、\n  実際のビュー・ファイルのパスが形成されます。\n  例えば、`item` は、`@app/views/post/index.php` というビューの中でレンダリングされる場合、`@app/views/post/item` へと解決されます。\n\n上記の規則によって、コントローラ `app\\controllers\\PostController` の中で `$this->render('view')` を呼ぶと、\n実際には、ビュー・ファイル `@app/views/post/view.php` がレンダリングされ、一方、そのビューの中で `$this->render('_overview')` を呼ぶと、\nビュー・ファイル `@app/views/post/_overview.php` がレンダリングされることになります。\n\n\n### ビューの中でデータにアクセスする <span id=\"accessing-data-in-views\"></span>\n\nビューの中でデータにアクセスするためのアプローチが二つあります。「プッシュ」と「プル」です。\n\nビューをレンダリングするメソッドに二番目のパラメータとしてデータを渡すのが「プッシュ」のアプローチです。\nデータは、「名前-値」のペアの配列として表わされなければなりません。\nビューがレンダリングされるときに、PHP の `extract()` 関数がこの配列に対して呼び出され、ビューの中で使う変数が抽出されます。\n例えば、次のコードはコントローラの中でビューをレンダリングしていますが、`report` ビューに二つの変数、\nすなわち、`$foo = 1` と `$bar = 2` をプッシュしています。\n\n```php\necho $this->render('report', [\n    'foo' => 1,\n    'bar' => 2,\n]);\n```\n\n「プル」のアプローチは、[[yii\\base\\View|ビュー・コンポーネント]] またはビューからアクセス出来るその他のオブジェクト (例えば `Yii::$app`) から積極的にデータを読み出すものです。\n下記のコード例のように、ビューの中では `$this->context` という式でコントローラ・オブジェクトを取得することが出来ます。\nその結果、`report` ビューの中で、コントローラの全てのプロパティやメソッドにアクセスすることが出来ます。\n次の例ではコントローラ ID にアクセスしています。\n\n```php\nThe controller ID is: <?= $this->context->id ?>\n```\n\n通常は「プッシュ」アプローチが、ビューでデータにアクセスする方法として推奨されます。なぜなら、ビューのコンテキスト・オブジェクトに対する依存がより少ないからです。\nその短所は、常にデータ配列を手作業で作成する必要がある、ということです。\nビューが共有されてさまざまな場所でレンダリングされる場合、その作業が面倒くさくなり、また、間違いも生じやすくなります。\n\n\n### ビューの間でデータを共有する <span id=\"sharing-data-among-views\"></span>\n\n[[yii\\base\\View|ビュー・コンポーネント]] が提供する [[yii\\base\\View::params|params]] プロパティを使うと、\nビューの間でデータを共有することが出来ます。\n\n例えば、`about` というビューで、次のようなコードを使って、\nパン屑リストの現在の区分を指定することが出来ます。\n\n```php\n$this->params['breadcrumbs'][] = 'About Us';\n```\n\nそして、[レイアウト](#layouts) ファイル (これも一つのビューです) の中で、[[yii\\base\\View::params|params]] によって渡されたデータを使って、\nパン屑リストを表示することが出来ます。\n\n```php\n<?= yii\\widgets\\Breadcrumbs::widget([\n    'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],\n]) ?>\n```\n\n\n## レイアウト <span id=\"layouts\"></span>\n\nレイアウトは、複数のビューの共通部分をあらわす特殊なタイプのビューです。\n例えば、たいていのウェブ・アプリケーションでは、ページは共通のヘッダとフッタを持っています。\nすべてのビューで同じヘッダとフッタを繰り返すことも出来ますが、もっと良い方法は、\nそういうことはレイアウトの中で一度だけして、コンテント・ビューのレンダリング結果をレイアウトの中の適切な場所に埋め込むことです。\n\n\n### レイアウトを作成する <span id=\"creating-layouts\"></span>\n\nレイアウトもまたビューですので、通常のビューと同様な方法で作成することが出来ます。\nデフォルトでは、レイアウトは `@app/views/layouts` ディレクトリに保存されます。\n[モジュール](structure-modules.md) の中で使用されるレイアウトについては、[[yii\\base\\Module::basePath|モジュール・ディレクトリ]] の下の `views/layouts` ディレクトリに保存されるべきものとなります。\nデフォルトのレイアウト・ディレクトリは、アプリケーションまたはモジュールの\n[[yii\\base\\Module::layoutPath]] プロパティを構成することでカスタマイズすることが出来ます。\n\n次の例は、レイアウトがどのようなものであるかを示すものです。説明のために、レイアウトの中のコードを大幅に単純化していることに注意してください。\n実際には、ヘッドのタグやメイン・メニューなど、もっと多くのコンテントを追加する必要があるでしょう。\n\n```php\n<?php\nuse yii\\helpers\\Html;\n\n/**\n * @var \\yii\\web\\View $this\n * @var string $content\n */\n?>\n<?php $this->beginPage() ?>\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\"/>\n    <?= Html::csrfMetaTags() ?>\n    <title><?= Html::encode($this->title) ?></title>\n    <?php $this->head() ?>\n</head>\n<body>\n<?php $this->beginBody() ?>\n    <header>My Company</header>\n    <?= $content ?>\n    <footer>&copy; 2014 by My Company</footer>\n<?php $this->endBody() ?>\n</body>\n</html>\n<?php $this->endPage() ?>\n```\n\nご覧のように、レイアウトはすべてのページに共通な HTML タグを生成しています。\n`<body>` セクションの中でレイアウトが `$content` という変数をエコーしていますが、これは、コンテント・ビューのレンダリング結果を表すものであり、\n[[yii\\base\\Controller::render()]] が呼ばれるときに、レイアウトにプッシュされるものです。\n\n上記のコードに示されているように、たいていのレイアウトは次に挙げるメソッドを呼び出さなければなりません。\nこれらのメソッドは、主としてレンダリングの過程に関するイベントをトリガするもので、\n他の場所で登録されたスクリプトやタグが、メソッドが呼ばれた場所に正しく注入されるようにするためのものです。\n\n- [[yii\\base\\View::beginPage()|beginPage()]]: このメソッドがレイアウトの冒頭で呼ばれなければなりません。\n  これは、ページの開始を示す [[yii\\base\\View::EVENT_BEGIN_PAGE|EVENT_BEGIN_PAGE]] イベントをトリガします。\n- [[yii\\base\\View::endPage()|endPage()]]: このメソッドがレイアウトの末尾で呼ばれなければなりません。\n  これは、ページの終了を示す [[yii\\base\\View::EVENT_END_PAGE|EVENT_END_PAGE]] イベントをトリガします。\n- [[yii\\web\\View::head()|head()]]: このメソッドが HTML ページの `<head>` セクションの中で呼ばれなければなりません。\n  このメソッドは、ページのレンダリングが完了したときに、登録された head の HTML コード (リンク・タグ、メタ・タグなど)\n  に置き換えられるプレースホルダを生成します。\n- [[yii\\web\\View::beginBody()|beginBody()]]: このメソッドが `<body>` セクションの冒頭で呼ばれなければなりません。\n  このメソッドは [[yii\\web\\View::EVENT_BEGIN_BODY|EVENT_BEGIN_BODY]] イベントをトリガし、\n  body の開始位置をターゲットとする登録された HTML コード (JavaScript など) によって置き換えられるプレースホルダを生成します。\n- [[yii\\web\\View::endBody()|endBody()]]: このメソッドが `<body`> セクションの末尾で呼ばれるなければなりません。\n  このメソッドは  [[yii\\web\\View::EVENT_END_BODY|EVENT_END_BODY]] イベントをトリガし、\n  body の終了位置をターゲットとする登録された HTML コード (JavaScript など) によって置き換えられるプレースホルダを生成します。\n\n\n### レイアウトでデータにアクセスする <span id=\"accessing-data-in-layouts\"></span>\n\nレイアウトの中では、事前定義された二つの変数、すなわち、`$this` と `$content` にアクセスすることが出来ます。\n前者は、通常のビューにおいてと同じく、[[yii\\base\\View|ビュー]] コンポーネントを参照します。\n一方、後者は、コントローラの中で [[yii\\base\\Controller::render()|render()]] メソッドを呼ぶことによってレンダリングされる、コンテント・ビューのレンダリング結果を含むものです。\n\nレイアウトの中でその他のデータにアクセスする必要があるときは、[ビューの中でデータにアクセスする](#accessing-data-in-views) の項で説明されている「プル」の方法を使う必要があります。\nコンテント・ビューからレイアウトにデータを渡す必要があるときは、[ビューの間でデータを共有する](#sharing-data-among-views)\nの項で説明されている方法を使うことが出来ます。\n\n\n### レイアウトを使う <span id=\"using-layouts\"></span>\n\n[コントローラでのレンダリング](#rendering-in-controllers) の項で説明されているように、\nコントローラの中で [[yii\\base\\Controller::render()|render()]] メソッドを呼んでビューをレンダリングすると、レンダリング結果にレイアウトが適用されます。\nデフォルトでは、`@app/views/layouts/main.php` というレイアウトが使用されます。\n\n[[yii\\base\\Application::layout]] または [[yii\\base\\Controller::layout]] のどちらかを構成することによって、異なるレイアウトを使うことが出来ます。\n前者は全てのコントローラによって使用されるレイアウトを決定するものですが、後者は個々のコントローラについて前者をオーバーライドするものです。\n例えば、次のコードは、`post` コントローラがビューをレンダリングするときに `@app/views/layouts/post.php` をレイアウトとして使うようにするものです。\nその他のコントローラは、`layout` プロパティに触れられていないと仮定すると、\n引き続きデフォルトの `@app/views/layouts/main.php` をレイアウトとして使います。\n \n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass PostController extends Controller\n{\n    public $layout = 'post';\n    \n    // ...\n}\n```\n\nモジュールに属するコントローラについては、モジュールの [[yii\\base\\Module::layout|layout]] プロパティを構成して、\nモジュール内のコントローラに特定のレイアウトを使用することも出来ます。\n\n`layout` プロパティは異なるレベル (コントローラ、モジュール、アプリケーション) で構成されうるものですので、\nYii は舞台裏で二つのステップを踏んで、特定のコントローラで実際に使われるレイアウト・ファイルが何であるかを決定します。\n\n最初のステップで、Yii はレイアウトの値とコンテキスト・モジュールを決定します。\n\n- コントローラの [[yii\\base\\Controller::layout]] プロパティが `null` でないときは、それをレイアウトの値として使い、\n  コントローラの [[yii\\base\\Controller::module|モジュール]] をコンテキスト・モジュールとして使う。\n- [[yii\\base\\Controller::layout|layout]] が `null` のときは、コントローラの祖先となっている全てのモジュール (アプリケーション自体も含む) を探して、\n  [[yii\\base\\Module::layout|layout]] プロパティが `null` でない最初のモジュールを見つける。\n  見つかったモジュールとその [[yii\\base\\Module::layout|layout]] の値をコンテキスト・モジュールと選ばれたレイアウトの値とする。\n  そのようなモジュールが見つからなかったときは、レイアウトは適用されないということを意味する。\n\n第二のステップでは、最初のステップで決定されたレイアウトの値とコンテキスト・モジュールに従って、実際のレイアウト・ファイルを決定します。\nレイアウトの値は下記のいずれかであり得ます。\n\n- パス・エイリアス (例えば、`@app/views/layouts/main`)。\n- 絶対パス (例えば、`/main`): すなわち、スラッシュで始まるレイアウトの値の場合。\n  実際のレイアウトファイルはアプリケーションの [[yii\\base\\Application::layoutPath|レイアウト・パス]]\n  (デフォルトでは `@app/views/layouts`) の下で探される。\n- 相対パス (例えば、`main`): 実際のレイアウト・ファイルはコンテキスト・モジュールの [[yii\\base\\Module::layoutPath|レイアウト・パス]]\n  (デフォルトでは [[yii\\base\\Module::basePath|モジュール・ディレクトリ]] の下の `views/layouts` ディレクトリ)\n  の下で探される。\n- 真偽値 `false`: レイアウトは適用されない。\n\nレイアウトの値がファイル拡張子を含んでいない場合は、デフォルト値である `.php` を使います。\n\n\n### 入れ子のレイアウト <span id=\"nested-layouts\"></span>\n\nときとして、あるレイアウトの中に別のレイアウトを入れたい場合があるでしょう。\n例えば、ウェブ・サイトの別々のセクションにおいて、違うレイアウトを使いたいけれども、\nそれらのレイアウトは全て、全体としての HTML5 ページ構造を生成する同一の基本レイアウトを共有している、という場合です。\nこの目的を達することは、次のように、子レイアウトの中で [[yii\\base\\View::beginContent()|beginContent()]] と [[yii\\base\\View::endContent()|endContent()]] を呼ぶことで可能になります。\n\n```php\n<?php $this->beginContent('@app/views/layouts/base.php'); ?>\n\n... 子レイアウトのコンテントをここに ...\n\n<?php $this->endContent(); ?>\n```\n\n上のコードが示すように、子レイアウトのコンテントは [[yii\\base\\View::beginContent()|beginContent()]] と [[yii\\base\\View::endContent()|endContent()]] によって囲まれなければなりません。\n[[yii\\base\\View::beginContent()|beginContent()]] に渡されるパラメータは、親レイアウトが何であるかを指定するものです。\nレイアウトのファイルまたはエイリアスのどちらかを使うことが出来ます。\n\n上記のアプローチを使って、2レベル以上のレイアウトを入れ子にすることも出来ます。\n\n\n### ブロックを使う <span id=\"using-blocks\"></span>\n\nブロックを使うと、ある場所でビューコンテントを定義して、別の場所でそれを表示することが可能になります。ブロックはたいていはレイアウトと一緒に使われます。\n例えば、ブロックをコンテント・ビューで定義して、それをレイアウトで表示する、ということが出来ます。\n\n[[yii\\base\\View::beginBlock()|beginBlock()]] と [[yii\\base\\View::endBlock()|endBlock()]] を呼んでブロックを定義します。\nすると、そのブロックを `$view->blocks[$blockID]` によってアクセス出来るようになります。\nここで `$blockID` は、定義したときにブロックに割り当てたユニークな ID を指します。\n\n次の例は、どのようにブロックを使えば、レイアウトの特定の部分をコンテント・ビューでカスタマイズすることが出来るかを示すものです。\n\n最初に、コンテント・ビューで、一つまたは複数のブロックを定義します。\n\n```php\n...\n\n<?php $this->beginBlock('block1'); ?>\n\n... block1 のコンテント ...\n\n<?php $this->endBlock(); ?>\n\n...\n\n<?php $this->beginBlock('block3'); ?>\n\n... block3 のコンテント ...\n\n<?php $this->endBlock(); ?>\n```\n\n次に、レイアウト・ビューで、得ることが出来ればブロックをレンダリングし、\nブロックが定義されていないときは何らかのデフォルトのコンテントを表示します。\n\n```php\n...\n<?php if (isset($this->blocks['block1'])): ?>\n    <?= $this->blocks['block1'] ?>\n<?php else: ?>\n    ... block1 のデフォルトのコンテント ...\n<?php endif; ?>\n\n...\n\n<?php if (isset($this->blocks['block2'])): ?>\n    <?= $this->blocks['block2'] ?>\n<?php else: ?>\n    ... block2 のデフォルトのコンテント ...\n<?php endif; ?>\n\n...\n\n<?php if (isset($this->blocks['block3'])): ?>\n    <?= $this->blocks['block3'] ?>\n<?php else: ?>\n    ... block3 のデフォルトのコンテント ...\n<?php endif; ?>\n...\n```\n\n\n## ビュー・コンポーネントを使う <span id=\"using-view-components\"></span>\n\n[[yii\\base\\View|ビュー・コンポーネント]] はビューに関連する多くの機能を提供します。\nビュー・コンポーネントは、[[yii\\base\\View]] またはその子クラスの個別のインスタンスを作成することによっても取得できますが、\nたいていの場合は、`view` アプリケーション・コンポーネントを主として使うことになるでしょう。\nこのコンポーネントは [アプリケーションの構成情報](structure-applications.md#application-configurations) の中で、次のようにして構成することが出来ます。\n\n```php\n[\n    // ...\n    'components' => [\n        'view' => [\n            'class' => 'app\\components\\View',\n        ],\n        // ...\n    ],\n]\n```\n\nビュー・コンポーネントは、次に挙げるビュー関連の有用な機能を提供します。それぞれについては、独立のセクションで更に詳細に説明されます。\n\n* [テーマ](output-theming.md): ウェブ・サイトのテーマを開発し変更することを可能にします。\n* [フラグメント・キャッシュ](caching-fragment.md): ウェブ・ページの中の断片をキャッシュすることを可能にします。\n* [クライアント・スクリプトの取り扱い](output-client-scripts.md): CSS と JavaScript の登録とレンダリングをサポートします。\n* [アセット・バンドルの取り扱い](structure-assets.md): [アセット・バンドル](structure-assets.md) の登録とレンダリングをサポートします。\n* [代替のテンプレート・エンジン](tutorial-template-engines.md): [Twig](https://twig.symfony.com/)、[Smarty](https://www.smarty.net/) など、\n  他のテンプレート・エンジンを使用することを可能にします。\n\n次に挙げるマイナーではあっても有用な諸機能は、ウェブ・ページを開発するときに頻繁に使用するでしょう。\n\n\n### ページタイトルを設定する <span id=\"setting-page-titles\"></span>\n\nどんなウェブ・ページにもタイトルが無ければなりません。通常、タイトル・タグは [layout](#layouts) の中で表示されます。\nしかし、実際においては、多くの場合、タイトルはレイアウトではなくコンテント・ビューで決められます。\nこの問題を解決するために、[[yii\\web\\View]] は、タイトル情報をコンテント・ビューからレイアウトに渡すための [[yii\\web\\View::title|title]] プロパティを提供しています。\n\nこの機能を利用するためには、全てのコンテント・ビューにおいて、次のようにタイトルを設定します。\n\n```php\n<?php\n$this->title = 'My page title';\n?>\n```\n\nそして、レイアウト・ビューで、`<head>` セクションに次のコードを忘れずに書くようにします。\n\n```php\n<title><?= Html::encode($this->title) ?></title>\n```\n\n\n### メタ・タグを登録する <span id=\"registering-meta-tags\"></span>\n\nウェブ・ページは、通常、いろいろな関係者によって必要とされるさまざまなメタ・タグを生成する必要があります。\nページ・タイトルと同じように、メタ・タグは `<head>` セクションに出現して、通常はレイアウトの中で生成されます。\n\nどのようなメタ・タグを生成するかをコンテント・ビューの中で指定したい場合は、下記のように、\n[[yii\\web\\View::registerMetaTag()]] をコンテント・ビューで呼ぶことが出来ます。\n\n```php\n<?php\n$this->registerMetaTag(['name' => 'keywords', 'content' => 'yii, framework, php']);\n?>\n```\n\n上記のコードは、ビュー・コンポーネントによって \"keywords\" メタ・タグを登録するものです。\n登録されたメタ・タグは、レイアウトがレンダリングを完了した後でレンダリングされます。\nすなわち、レイアウトの中で [[yii\\web\\View::head()]] を呼び出した場所に、次の HTML コードが生成されて挿入されます。\n\n```php\n<meta name=\"keywords\" content=\"yii, framework, php\">\n```\n\n[[yii\\web\\View::registerMetaTag()]] を複数回呼び出した場合は、メタ・タグが同じものか否かに関係なく、\n複数のメタ・タグが登録されることに注意してください。\n\nある型のメタ・タグのインスタンスが一つだけになることを保証したい場合は、このメソッドを呼ぶときに第二のパラメータとしてキーを指定することが出来ます。\n例えば、次のコードでは、二つの \"description\" メタ・タグを登録していますが、二番目のものだけがレンダリングされることになります。\n\n```php\n$this->registerMetaTag(['name' => 'description', 'content' => '俺が Yii で作ったクールなウェブ・サイトだぜぃ!!'], 'description');\n$this->registerMetaTag(['name' => 'description', 'content' => '面白いアライグマに関するウェブ・サイトです。'], 'description');\n```\n\n\n### リンク・タグを登録する <span id=\"registering-link-tags\"></span>\n\n[メタ・タグ](#registering-meta-tags) と同じように、リンク・タグも多くの場合において有用なものです。例えば、favicon をカスタマイズしたり、RSS フィードを指し示したり、OpenID を別のサーバに委任したり、等々。\nリンク・タグも、[[yii\\web\\View::registerLinkTag()]] を使って、メタ・タグと同じような方法で取り扱うことが出来ます。\n例えば、コンテント・ビューにおいて、次のようにしてリンク・タグを登録することが出来ます。\n\n```php\n$this->registerLinkTag([\n    'title' => 'Yii ライブ・ニューズ',\n    'rel' => 'alternate',\n    'type' => 'application/rss+xml',\n    'href' => 'https://www.yiiframework.com/rss.xml/',\n]);\n```\n\n上記のコードは、次の結果になります。\n\n```html\n<link title=\"Yii ライブ・ニューズ\" rel=\"alternate\" type=\"application/rss+xml\" href=\"https://www.yiiframework.com/rss.xml/\">\n```\n\n[[yii\\web\\View::registerMetaTag()|registerMetaTag()]] と同じように、[[yii\\web\\View::registerLinkTag()|registerLinkTag()]] を呼ぶときにキーを指定すると、\n同じリンク・タグを繰り返して生成するのを避けることが出来ます。\n\n\n## ビューのイベント <span id=\"view-events\"></span>\n\n[[yii\\base\\View|ビュー・コンポーネント]] はビューをレンダリングする過程においていくつかのイベントをトリガします。\nこれらのイベントに反応することによって、ビューにコンテントを注入したり、エンド・ユーザに送信される前にレンダリング結果を加工したりすることが出来ます。\n\n- [[yii\\base\\View::EVENT_BEFORE_RENDER|EVENT_BEFORE_RENDER]]: コントローラでファイルをレンダリングする前にトリガされます。\n  このイベントのハンドラは、[[yii\\base\\ViewEvent::isValid]] を `false` にセットして、レンダリングのプロセスをキャンセルすることが出来ます。\n- [[yii\\base\\View::EVENT_AFTER_RENDER|EVENT_AFTER_RENDER]]: ファイルのレンダリングの後、[[yii\\base\\View::afterRender()]] を呼ぶことによってトリガされます。\n  このイベントのハンドラは、レンダリング結果をプロパティ [[yii\\base\\ViewEvent::output]] を通じて取得して、\n  それを修正してレンダリング結果を変更することが出来ます。\n- [[yii\\base\\View::EVENT_BEGIN_PAGE|EVENT_BEGIN_PAGE]]: レイアウトの中で [[yii\\base\\View::beginPage()]] を呼ぶことによってトリガされます。\n- [[yii\\base\\View::EVENT_END_PAGE|EVENT_END_PAGE]]: レイアウトの中で [[yii\\base\\View::endPage()]] を呼ぶことによってトリガされます。\n- [[yii\\web\\View::EVENT_BEGIN_BODY|EVENT_BEGIN_BODY]]: レイアウトの中で [[yii\\web\\View::beginBody()]] を呼ぶことによってトリガされます。\n- [[yii\\web\\View::EVENT_END_BODY|EVENT_END_BODY]]: レイアウトの中で [[yii\\web\\View::endBody()]] を呼ぶことによってトリガされます。\n\n例えば、次のコードはページの body の最後に現在の日付を注入するものです。\n\n```php\n\\Yii::$app->view->on(View::EVENT_END_BODY, function () {\n    echo date('Y-m-d');\n});\n```\n\n\n## 静的なページをレンダリングする <span id=\"rendering-static-pages\"></span>\n\n静的なページというのは、主たるコンテントのほとんどが静的なもので、\nコントローラからプッシュされる動的なデータにアクセスする必要がないページを指します。\n\n静的なページは、そのコードをビューに置き、そして、コントローラで次のようなコードを使うと表示することが出来ます。\n\n```php\npublic function actionAbout()\n{\n    return $this->render('about');\n}\n```\n\nウェブ・サイトが多くの静的なページを含んでいる場合、同じようなコードを何度も繰り返すのは非常に面倒くさいでしょう。\nこの問題を解決するために、[[yii\\web\\ViewAction]] という [スタンドアロン・アクション](structure-controllers.md#standalone-actions) をコントローラに導入することが出来ます。\n例えば、\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actions()\n    {\n        return [\n            'page' => [\n                'class' => 'yii\\web\\ViewAction',\n            ],\n        ];\n    }\n}\n```\n\nこのようにすると、ディレクトリ `@app/views/site/pages` の下に `about` という名前のビューを作成したときに、\n次の URL によってこのビューを表示することが出来るようになります。\n\n```\nhttp://localhost/index.php?r=site%2Fpage&view=about\n```\n\n`view` という `GET` パラメータが、どのビューがリクエストされているかを [[yii\\web\\ViewAction]] に教えます。\nそこで、アクションはこのビューをディレクトリ `@app/views/site/pages` の下で探します。\n[[yii\\web\\ViewAction::viewPrefix]] を構成して、ビューを探すディレクトリを変更することが出来ます。\n\n\n## ベスト・プラクティス <span id=\"best-practices\"></span>\n\nビューはエンド・ユーザが望む形式でモデルを表現することに対して責任を持ちます。一般的に、ビューは\n\n* 主として表示目的のコードを含むべきです。例えば、HTML、または、データをたどって書式化してレンダリングする簡単な PHP コードなど。\n* DB クエリを実行するコードは含むべきではありません。そのようなコードはモデルの中で実行されるべきです。\n* `$_GET` や `$_POST` のようなリクエスト・データに直接アクセスするべきではありません。それはコントローラの仕事です。\n  リクエスト・データが必要な場合は、コントローラからビューにプッシュされるべきです。\n* モデルのプロパティを読み出すことが出来ます。しかし、それを修正するべきではありません。\n\nビューを管理しやすいものにするために、複雑すぎるビューや、冗長なコードをあまりに多く含むビューを作ることは避けましょう。\nこの目的を達するために、次のテクニックを使うことが出来ます。\n\n* 共通の表示セクション (ページのヘッダやフッタなど) を表すために [レイアウト](#layouts) を使う。\n* 複雑なビューはいくつかの小さなビューに分割する。既に説明したレンダリングのメソッドを使えば、\n  小さなビューをレンダリングして大きなビューを組み上げることが出来る。\n* ビューの構成要素として [ウィジェット](structure-widgets.md) を使う。\n* ビューでデータを変換し書式化するためのヘルパ・クラスを作成して使う。\n\n"
  },
  {
    "path": "docs/guide-ja/structure-widgets.md",
    "content": "ウィジェット\n============\n\nウィジェットは、[ビュー](structure-views.md) で使用される再利用可能な構成ブロックで、\n複雑かつ構成可能なユーザ・インタフェイス要素をオブジェクト指向の流儀で作成するためのものです。\n例えば、日付選択ウィジェットを使うと、入力として日付を選択することを可能にする素敵なデイト・ピッカーを生成することが出来ます。\nこのとき、あなたがしなければならないことは、次のようなコードをビューに挿入することだけです:\n\n```php\n<?php\nuse yii\\jui\\DatePicker;\n?>\n<?= DatePicker::widget(['name' => 'date']) ?>\n```\n\n数多くのウィジェットが Yii にバンドルされています。\n例えば、[[yii\\widgets\\ActiveForm|アクティブ・フォーム]] や、[[yii\\widgets\\Menu|メニュー]]、[jQuery UI ウィジェット](https://www.yiiframework.com/extension/yiisoft/yii2-jui)、[Twitter Bootstrap ウィジェット](https://www.yiiframework.com/extension/yiisoft/yii2-bootstrap) などです。\n下記では、ウィジェットに関する基本的な知識の手引きをします。\n特定のウィジェットの使い方について学ぶ必要がある場合は、クラス API ドキュメントを参照してください。\n\n\n## ウィジェットを使う <span id=\"using-widgets\"></span>\n\nウィジェットは主として [ビュー](structure-views.md) で使われます。\n[[yii\\base\\Widget::widget()]] メソッドを呼んで、ビューでウィジェットを使います。\nこのメソッドは、ウィジェットを初期化するための [構成情報](concept-configurations.md) 配列を受け取り、ウィジェットのレンダリング結果を返します。\n例えば、下記のコードは、日本語を使い、入力を `$model` の `from_date` 属性に保存するように構成された日付選択ウィジェットを挿入するものです。\n\n```php\n<?php\nuse yii\\jui\\DatePicker;\n?>\n<?= DatePicker::widget([\n    'model' => $model,\n    'attribute' => 'from_date',\n    'language' => 'ja',\n    'dateFormat' => 'php:Y-m-d',\n]) ?>\n```\n\nウィジェットの中には、コンテントのブロックを受け取ることが出来るものもあります。\nその場合、コンテントのブロックは [[yii\\base\\Widget::begin()]] と [[yii\\base\\Widget::end()]] の呼び出しで囲むようにしなければなりません。\n例えば、次のコードは [[yii\\widgets\\ActiveForm]] ウィジェットを使ってログイン・フォームを生成するものです。\nこのウィジェットは、`begin()` と `end()` が呼ばれる場所で、それぞれ、開始と終了の `<form>` タグを生成します。\nその間に置かれたものは全てそのままレンダリングされます。\n\n```php\n<?php\nuse yii\\widgets\\ActiveForm;\nuse yii\\helpers\\Html;\n?>\n\n<?php $form = ActiveForm::begin(['id' => 'login-form']); ?>\n\n    <?= $form->field($model, 'username') ?>\n\n    <?= $form->field($model, 'password')->passwordInput() ?>\n\n    <div class=\"form-group\">\n        <?= Html::submitButton('ログイン') ?>\n    </div>\n\n<?php ActiveForm::end(); ?>\n```\n\n[[yii\\base\\Widget::widget()]] がウィジェットのレンダリング結果を返すのとは違って、[[yii\\base\\Widget::begin()]] メソッドがウィジェットのインスタンスを返すことに注意してください。\n返されたウィジェットのインスタンスを使って、ウィジェットのコンテントを構築することが出来ます。\n\n> Note: いくつかのウィジェットは、[[yii\\base\\Widget::end()]] が呼ばれるときに囲んだコンテンツを調整するため、\n> [出力バッファリング](https://www.php.net/manual/ja/book.outcontrol.php) を使用します。\n> この理由から、[[yii\\base\\Widget::begin()]] と [[yii\\base\\Widget::end()]] の呼び出しは、同じビュー・ファイルの中で発生するものと想定されています。\n> この規則に従わない場合は、予期しない出力結果が生じ得ます。\n\n\n### グローバルなデフォルトを構成する\n\nあるタイプのウィジェットのグローバルなデフォルトを DI コンテナによって構成することが出来ます。\n\n```php\n\\Yii::$container->set('yii\\widgets\\LinkPager', ['maxButtonCount' => 5]);\n```\n\n詳細については [依存注入コンテナのガイドの \"実際の使いかた\" のセクション](concept-di-container.md#practical-usage)\nを参照してください。\n\n\n## ウィジェットを作成する <span id=\"creating-widgets\"></span>\n\nウィジェットは必要に従って二つの異なる方法で作成することが出来ます。\n\n### 1: `widget()` メソッドを利用する\n\nウィジェットを作成するためには、[[yii\\base\\Widget]] を拡張して、[[yii\\base\\Widget::init()]] および/または\n[[yii\\base\\Widget::run()]] メソッドをオーバーライドします。\n通常、`init()` メソッドはウィジェットのプロパティを初期化するコードを含むべきものであり、`run()` メソッドはウィジェットのレンダリング結果を生成するコードを含むべきものです。\nレンダリング結果は、直接に \"echo\" しても、`run()` の返り値として文字列として返しても構いません。\n\n次の例では、`HelloWidget` が `message` プロパティとして割り当てられたコンテントを HTML エンコードして表示します。\nプロパティがセットされていない場合は、デフォルトとして \"Hello World\" を表示します。\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Widget;\nuse yii\\helpers\\Html;\n\nclass HelloWidget extends Widget\n{\n    public $message;\n\n    public function init()\n    {\n        parent::init();\n        if ($this->message === null) {\n            $this->message = 'Hello World';\n        }\n    }\n\n    public function run()\n    {\n        return Html::encode($this->message);\n    }\n}\n```\n\nこのウィジェットを使うために必要なことは、次のコードをビューに挿入するだけのことです。\n\n```php\n<?php\nuse app\\components\\HelloWidget;\n?>\n<?= HelloWidget::widget(['message' => 'おはよう']) ?>\n```\n\n\nウィジェットが大きなかたまりのコンテントをレンダーする必要がある場合もあります。\nコンテントを `run()` の中に埋め込むことも出来ますが、もっと良い方法は、コンテントを [view](structure-views.md) に置き、\n[[yii\\base\\Widget::render()]] を呼んでレンダーする方法です。例えば、\n\n```php\npublic function run()\n{\n    return $this->render('hello');\n}\n```\n\n### 2: `begin()` と `end()` のメソッドを利用する\n\nこれは上記のものと少し異なるだけのものです。\n下記は `HelloWidget` の変種で、`begin()` と `end()` の間に包まれたコンテントを受け取り、\nそれを HTML エンコードして表示するものです。\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Widget;\nuse yii\\helpers\\Html;\n\nclass HelloWidget extends Widget\n{\n    public function init()\n    {\n        parent::init();\n        ob_start();\n    }\n\n    public function run()\n    {\n        $content = ob_get_clean();\n        return Html::encode($content);\n    }\n}\n```\n\nご覧のように、`init()` の中で PHP の出力バッファが開始され、`init()` と `run()` の呼び出しの間の全ての出力がキャプチャされ、\n`run()` の中で処理されて返されます。\n\n> Info: [[yii\\base\\Widget::begin()]] を呼ぶと、ウィジェットの新しいインスタンスが作成され、\n  ウィジェットのコンストラクタの最後で `init()` メソッドが呼ばれます。\n  [[yii\\base\\Widget::end()]] を呼ぶと、`run()` メソッドが呼ばれて、その返り値が `end()` によって echo されます。\n\n次のコードは、この `HelloWidget` の新しい変種をどのように使うかを示すものです:\n\n```php\n<?php\nuse app\\components\\HelloWidget;\n?>\n<?php HelloWidget::begin(); ?>\n\n    一つまたはそれ以上の <strong>HTML</strong> <pre>タグ</pre> を含みうるサンプル・コンテント\n\n    このコンテントが大きくなりすぎる場合は、サブ・ビューを使います。\n\n    例えば、\n\n    <?php echo $this->render('viewfile'); // 注意: この render() メソッドは \\yii\\base\\View クラスのもの。コードのこの部分はビュー・ファイルの中にあり、Widget クラス・ファイルの中にはない ?>\n\n<?php HelloWidget::end(); ?>\n```\n\nデフォルトでは、ウィジェット用のビューは `WidgetPath/views` ディレクトリの中のファイルに保存すべきものです。\nここで `WidgetPath` はウィジェットのクラス・ファイルを含むディレクトリを指します。\nしたがって、上記の例では、ウィジェット・クラスが `@app/components` に配置されていると仮定すると、`@app/components/views/hello.php` というビュー・ファイルがレンダリングされることになります。\n[[yii\\base\\Widget::getViewPath()]] メソッドをオーバーライドして、ウィジェットのビュー・ファイルを含むディレクトリをカスタマイズすることが出来ます。\n\n\n## ベスト・プラクティス <span id=\"best-practices\"></span>\n\nウィジェットはビューのコードを再利用するためのオブジェクト指向の方法です。\n\nウィジェットを作成するときでも、MVC パターンに従うべきです。\n一般的に言うと、ロジックはウィジェット・クラスに保持し、表現は [ビュー](structure-views.md) に保持すべきです。\n\nウィジェットは自己完結的に設計されるべきです。\n言い換えると、ウィジェットを使うときに、他に何もしないでもビューに挿入することが出来るようにすべきです。\nこの要求は、ウィジェットが CSS、JavaScript、画像などの外部リソースを必要とする場合は、扱いにくい問題になり得ます。\n幸いなことに、Yii はこの問題を解決するのに利用することが出来る [アセット・バンドル](structure-assets.md) のサポートを提供しています。\n\nウィジェットがビュー・コードだけを含む場合は、[ビュー](structure-views.md) と非常に似たものになります。\n実際のところ、この場合、両者の唯一の違いは、ウィジェットが再配布可能なクラスである一方で、\nビューはアプリケーション内に保持することが望ましい素の PHP スクリプトである、というぐらいの事です。\n"
  },
  {
    "path": "docs/guide-ja/test-acceptance.md",
    "content": "受入テスト\n==========\n\n受入テストはユーザの視点からシナリオを検証するものです。テストされるアプリケーションは PhpBrowser または実際のブラウザによってアクセスされます。\nどちらの場合でも、ブラウザは HTTP によって通信しますので、アプリケーションはウェブ・サーバによってホストされる必要があります。\n\n受入テストは Codeception フレームワークの助けを借りて実装されています。Codeception フレームワークには優れたドキュメントがありますので、参照して下さい。\n\n- [Codeception for Yii framework](https://codeception.com/for/yii)\n- [Codeception Acceptance Tests](https://codeception.com/docs/03-AcceptanceTests)\n\n## ベーシック・テンプレート、アドバンスト・テンプレートのテストを実行する\n\nアドバンスト・テンプレートで開発をしている場合は、テスト実行の詳細について、\n[\"テスト\" のガイド](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide-ja/start-testing.md) を参照して下さい。\n\nベーシック・テンプレートで開発をしている場合は、[README の \"testing\" のセクション](https://github.com/yiisoft/yii2-app-basic/blob/master/README.md#testing) を参照して下さい。\n"
  },
  {
    "path": "docs/guide-ja/test-environment-setup.md",
    "content": "テスト環境の構築\n================\n\nYii 2 は [`Codeception`](https://github.com/Codeception/Codeception) テスト・フレームワークとの統合を公式にサポートしており、\n次のタイプのテストを作成することを可能にしています。\n\n- [単体テスト](test-unit.md) - 一かたまりのコードが期待通りに動くことを検証する。\n- [機能テスト](test-functional.md) - ブラウザのエミュレーションによって、ユーザの視点からシナリオを検証する。\n- [受入テスト](test-acceptance.md) - ブラウザの中で、ユーザの視点からシナリオを検証する。\n\nこれら三つのタイプのテスト全てについて、Yii は、[`yii2-basic`](https://github.com/yiisoft/yii2-app-basic) と\n[`yii2-advanced`](https://github.com/yiisoft/yii2-app-advanced) の両方のプロジェクト・テンプレートで、\nそのまま使えるテストセットを提供しています。\n\nベーシック・テンプレート、アドバンスト・テンプレートの両方とも、Codeception がプリ・インストールされて付いて来ます。\nこれらのテンプレートの一つを使っていない場合は、下記のコンソールコマンドを発行することで\nCodeception をインストールすることが出来ます。\n\n```\ncomposer require --dev codeception/codeception\ncomposer require --dev codeception/specify\ncomposer require --dev codeception/verify\n```\n"
  },
  {
    "path": "docs/guide-ja/test-fixtures.md",
    "content": "フィクスチャ\n============\n\nフィクスチャはテストの重要な部分です。\nフィクスチャの主な目的は、テストを期待されている方法で繰り返して実行できるように、環境を固定された既知の状態に設定することです。\nYii は、Codeception でテストを実行する場合でも、単独でテストを実行する場合でも、\nフィクスチャを正確に定義して容易に使うことが出来るように、フィクスチャ・フレームワークを提供しています。\n\nYii のフィクスチャ・フレームワークにおける鍵となる概念は、いわゆる *フィクスチャ・オブジェクト* です。\nフィクスチャ・オブジェクトはテスト環境のある特定の側面を表現するもので、[[yii\\test\\Fixture]] またはその子クラスのインスタンスです。\n例えば、ユーザの DB テーブルが固定されたデータセットを含むことを保証するために `UserFixture` を使う、という具合です。\nテストを実行する前に一つまたは複数のフィクスチャ・オブジェクトをロードし、テストの完了時にアンロードします。\n\nフィクスチャは他のフィクスチャに依存する場合があります。依存は [[yii\\test\\Fixture::depends]] プロパティによって定義されます。\nフィクスチャがロードされるとき、依存するフィクスチャはそのフィクスチャの前に自動的にロードされます。\nそしてフィクスチャがアンロードされるときには、依存するフィクスチャはそのフィクスチャの後にアンロードされます。\n\n\n## フィクスチャを定義する\n\nフィクスチャを定義するためには、[[yii\\test\\Fixture]] または [[yii\\test\\ActiveFixture]] を拡張して新しいクラスを作ります。\n前者は汎用目的のフィクスチャに最も適しています。\n一方、後者はデータベースとアクティブ・レコードを扱うために専用に設計された拡張機能を持っています。\n\n次のコードは、`User` アクティブ・レコードとそれに対応するテーブルに関して、フィクスチャを定義するものです。\n\n```php\n<?php\nnamespace app\\tests\\fixtures;\n\nuse yii\\test\\ActiveFixture;\n\nclass UserFixture extends ActiveFixture\n{\n    public $modelClass = 'app\\models\\User';\n}\n```\n\n> Tip: すべての `ActiveFixture` は、テストの目的のために DB テーブルを準備するものです。\n> [[yii\\test\\ActiveFixture::tableName]] プロパティまたは [[yii\\test\\ActiveFixture::modelClass]] プロパティを設定することによって、テーブルを指定することが出来ます。\n> 後者を使う場合は、`modelClass` によって指定される `ActiveRecord` クラスからテーブル名が取得されます。\n\n> Note: [[yii\\test\\ActiveFixture]] は SQL データベースにのみ適しています。\n> NoSQL データベースのためには、Yii は以下の `ActiveFixture` クラスを提供しています。\n>\n> - Mongo DB: [[yii\\mongodb\\ActiveFixture]]\n> - Elasticsearch: [[yii\\elasticsearch\\ActiveFixture]] (バージョン 2.0.2 以降)\n\n\n`ActiveFixture` フィクスチャのフィクスチャ・データは通常は `FixturePath/data/TableName.php` として配置されるファイルで提供されます。\nここで `FixturePath` はフィクスチャ・クラス・ファイルを含むディレクトリを意味し、`TableName` はフィクスチャと関連付けられているテーブルの名前です。\n上記の例では、ファイルは `@app/tests/fixtures/data/user.php` となります。\nデータ・ファイルは、ユーザのテーブルに挿入されるデータ行の配列を返さなければなりません。\n例えば、\n\n```php\n<?php\nreturn [\n    'user1' => [\n        'username' => 'lmayert',\n        'email' => 'strosin.vernice@jerde.com',\n        'auth_key' => 'K3nF70it7tzNsHddEiq0BZ0i-OU8S3xV',\n        'password' => '$2y$13$WSyE5hHsG1rWN2jV8LRHzubilrCLI5Ev/iK0r3jRuwQEs2ldRu.a2',\n    ],\n    'user2' => [\n        'username' => 'napoleon69',\n        'email' => 'aileen.barton@heaneyschumm.com',\n        'auth_key' => 'dZlXsVnIDgIzFgX4EduAqkEPuphhOh9q',\n        'password' => '$2y$13$kkgpvJ8lnjKo8RuoR30ay.RjDf15bMcHIF7Vz1zz/6viYG5xJExU6',\n    ],\n];\n```\n\nデータ行にはエイリアスを付けることが出来て、後でテストのときにエイリアスを使って行を参照することが出来ます。\n上の例では、二つの行はそれぞれ `user1` および `user2` というエイリアスを付けられています。\n\nまた、オート・インクリメントのカラムに対してはデータを指定する必要はありません。\nフィクスチャがロードされるときに Yii が自動的に実際の値を行に入れます。\n\n> Tip: [[yii\\test\\ActiveFixture::dataFile]] プロパティを設定して、データ・ファイルの所在をカスタマイズすることが出来ます。\n> [[yii\\test\\ActiveFixture::getData()]] をオーバーライドしてデータを提供することも可能です。\n\n前に説明したように、フィクスチャは別のフィクスチャに依存する場合があります。\n例えば、ユーザ・プロファイルのテーブルはユーザのテーブルを指す外部キーを含んでいるため、`UserProfileFixture` は `UserFixture` に依存します。\n依存関係は、次のように、[[yii\\test\\Fixture::depends]] プロパティによって指定されます。\n\n```php\nnamespace app\\tests\\fixtures;\n\nuse yii\\test\\ActiveFixture;\n\nclass UserProfileFixture extends ActiveFixture\n{\n    public $modelClass = 'app\\models\\UserProfile';\n    public $depends = ['app\\tests\\fixtures\\UserFixture'];\n}\n```\n\n依存関係は、また、複数のフィクスチャが正しく定義された順序でロードされ、アンロードされることを保証します。\n上記の例では、全ての外部キー参照が存在することを保証するために `UserFixture` は常に `UserProfileFixture` の前にロードされます。\nまた、同じ理由によって、`UserFixture` は常に `UserProfileFixture` がアンロードされた後でアンロードされます。\n\n上記では、DB テーブルに関してフィクスチャを定義する方法を示しました。\nDB と関係しないフィクスチャ (例えば、何らかのファイルやディレクトリに関するフィクスチャ) を定義するためには、より汎用的な基底クラス [[yii\\test\\Fixture]] から拡張して、\n[[yii\\test\\Fixture::load()|load()]] と [[yii\\test\\Fixture::unload()|unload()]] のメソッドをオーバーライドすることが出来ます。\n\n\n## フィクスチャを使用する\n\n[Codeception](https://codeception.com/) を使ってコードをテストしている場合は、\nフィクスチャのローディングとアクセスについて、内蔵されているサポートを使用することが出来ます。\n\nその他のテスト・フレームワークを使っている場合は、テスト・ケースで [[yii\\test\\FixtureTrait]]\nを使って同じ目的を達することが出来ます。\n\n次に、Codeception を使って `UserProfile` 単体テストクラスを書く方法を説明します。\n\n`\\Codeception\\Test\\Unit` を拡張するあなたの単体テスト・クラスにおいて、 `_fixtures()` メソッドの中で使いたいフィクスチャを宣言するか、\nまたは、アクターの `haveFixtures()` メソッドを直接に使用します。例えば、\n\n```php\nnamespace app\\tests\\unit\\models;\n\n\nuse app\\tests\\fixtures\\UserProfileFixture;\n\nclass UserProfileTest extends \\Codeception\\Test\\Unit\n{   \n    public function _fixtures()\n    {\n        return [\n            'profiles' => [\n                'class' => UserProfileFixture::class,\n                // フィクスチャ・データは tests/_data/user.php に配置されている\n                'dataFile' => codecept_data_dir() . 'user.php'\n            ],\n        ];\n    }\n\n    // ... テストのメソッド ...\n}\n```\n\n`_fixtures()` メソッドにリストされたフィクスチャは、テストが実行される前に自動的にロードされます。\n前に説明したように、フィクスチャがロードされるときには、それが依存するフィクスチャのすべてが自動的に先にロードされます。\n上の例では、`UserProfileFixture` は `UserFixture` に依存しているので、テスト・クラスのどのテスト・メソッドを走らせるときでも、二つのフィクスチャが連続してロードされます。\nすなわち、最初に `UserFixture` がロードされ、次に `UserProfileFixture` がロードされます。\n\n`_fixtures()` でフィクスチャを指定するときも、`haveFixtures()` でフィクスチャを指定するときも、\nクラス名あるいはフィクスチャを指す構成情報配列を使うことが出来ます。\n構成情報配列を使うと、フィクスチャがロードされるときのフィクスチャのプロパティをカスタマイズすることが出来ます。\n\nまた、フィクスチャにエイリアスを割り当てることも出来ます。上記の例では、`UserProfileFixture` に `profiles` というエイリアスが与えられています。\nそうすると、テスト・メソッドの中でエイリアスを使ってフィクスチャ・オブジェクトにアクセスすることが出来るようになります。例えば、\n\n```php\n$profile = $I->grabFixture('profiles');\n```\n\nは `UserProfileFixture` オブジェクトを返します。\n\nさらには、`UserProfileFixture` は `ActiveFixture` を拡張するものですので、フィクスチャによって提供されたデータに対して、\n次の構文を使ってアクセスすることも出来ます。\n\n```php\n// 'user1' というエイリアスのデータ行に対応する UserProfileModel を返す\n$profile = $I->grabFixture('profiles', 'user1');\n// フィクスチャにある全てのデータ行をたどる\nforeach ($I->grabFixture('profiles') as $profile) ...\n```\n\n## フィクスチャ・クラスとデータ・ファイルを編成する\n\nデフォルトでは、フィクスチャ・クラスは対応するデータ・ファイルを探すときに、フィクスチャのクラス・ファイルを含むフォルダのサブ・フォルダである `data` フォルダの中を見ます。\n簡単なプロジェクトではこの規約に従うことができます。\n大きなプロジェクトでは、おそらくは、同じフィクスチャ・クラスを異なるテストに使うために、データ・ファイルを切り替える必要がある場合が頻繁に生じるでしょう。\n従って、クラスの名前空間と同じように、データ・ファイルを階層的な方法で編成することを推奨します。\n例えば、\n\n```\n# tests\\unit\\fixtures フォルダの下に\n\ndata\\\n    components\\\n        fixture_data_file1.php\n        fixture_data_file2.php\n        ...\n        fixture_data_fileN.php\n    models\\\n        fixture_data_file1.php\n        fixture_data_file2.php\n        ...\n        fixture_data_fileN.php\n# 等々\n```\n\nこのようにして、テスト間でフィクスチャのデータ・ファイルが衝突するのを回避し、必要に応じてデータ・ファイルを使い分けます。\n\n> Note: 上の例では、フィクスチャ・ファイルには例示目的だけの名前が付けられています。\n> 実際の仕事では、フィクスチャ・クラスがどのフィクスチャ・クラスを拡張したものであるかに従って名前を付けるべきです。\n> 例えば、DB フィクスチャを [[yii\\test\\ActiveFixture]] から拡張している場合は、DB テーブルの名前をフィクスチャのデータ・ファイル名として使うべきです。\n> MongoDB フィクスチャを [[yii\\mongodb\\ActiveFixture]] から拡張している場合は、コレクション名をファイル名として使うべきです。\n\n同様な階層は、フィクスチャ・クラス・ファイルを編成するのにも使うことが出来ます。\n`data` をルート・ディレクトリとして使うのでなく、データ・ファイルとの衝突を避けるために `fixtures` をルート・ディレクトリとして使うのが良いでしょう。\n\n## `yii fixture` でフィクスチャを管理する\n\nYii は `yii fixture` コマンドライン・ツールでフィクスチャをサポートしています。以下の機能をサポートしています。\n\n* 異なるストレージ (RDBMS、NoSQL など) へのフィクスチャのロード\n* 様々な方法でのフィクスチャのアンロード (通常はストレージをクリア)\n* フィクスチャの自動生成およびランダム・データの投入\n\n### フィクスチャのデータ形式\n\n次のようなフィクスチャ・データをロードするとしましょう。\n\n```\n# users.php ファイル - フィクスチャ・データ・パス (デフォルトでは @tests\\unit\\fixtures\\data) に保存\n\nreturn [\n    [\n        'name' => 'Chase',\n        'login' => 'lmayert',\n        'email' => 'strosin.vernice@jerde.com',\n        'auth_key' => 'K3nF70it7tzNsHddEiq0BZ0i-OU8S3xV',\n        'password' => '$2y$13$WSyE5hHsG1rWN2jV8LRHzubilrCLI5Ev/iK0r3jRuwQEs2ldRu.a2',\n    ],\n    [\n        'name' => 'Celestine',\n        'login' => 'napoleon69',\n        'email' => 'aileen.barton@heaneyschumm.com',\n        'auth_key' => 'dZlXsVnIDgIzFgX4EduAqkEPuphhOh9q',\n        'password' => '$2y$13$kkgpvJ8lnjKo8RuoR30ay.RjDf15bMcHIF7Vz1zz/6viYG5xJExU6',\n    ],\n];\n```\nデータベースにデータをロードするフィクチャを使う場合は、これらの行が `users` テーブルに対して適用されます。NoSQL フィクスチャ、例えば `mongodb` フィクチャを使う場合は、このデータは `users` コレクションに対して適用されます。\nさまざまなロード戦略を実装する方法などについて公式 [ドキュメント](https://github.com/yiisoft/yii2/blob/master/docs/guide/test-fixtures.md)を参照して下さい。\n上記のフィクスチャのサンプルは `yii2-faker` エクステンションによって生成されました。これについての詳細は、[自動生成のセクション](#auto-generating-fixtures) を参照して下さい。\nフィクスチャ・クラスの名前は複数形であってはいけません。\n\n### フィクスチャをロードする\n\nフィクスチャ・クラスは `Fixture` という接尾辞を持たなければいけません。デフォルトでは、フィクスチャは `tests\\unit\\fixtures` 名前空間の下で探されます。\nこの挙動は構成またはコマンド・オプションによって変更することが出来ます。`-User` のように名前の前に `-` を指定することで、ロードまたはアンロードから除外するフィクスチャを指定することが出来ます。\n\nフィクスチャをロードするためには、次のコマンドを実行します。\n\n> Note: データをロードする前に、アンロードのシーケンスが実行されます。これによって、通常は、前に実行されたフィクスチャによって挿入された 既存のデータが全てクリーンアップされることになります。\n\n```\nyii fixture/load <fixture_name>\n```\n\n要求される `fixture_name` パラメータが、データがロードされるフィクスチャの名前を指定するものです。\nいくつかのフィクスチャを一度にロードすることが出来ます。下記はこのコマンドの正しい形式です。\n\n```\n// `User` フィクスチャをロードする\nyii fixture/load User\n\n// 上記と同じ、\"fixture\" コマンドのデフォルトのアクションは \"load\" であるため\nyii fixture User\n\n// いくつかのフィクスチャをロードする\nyii fixture \"User, UserProfile\"\n\n// 全てのフィクスチャをロードする\nyii fixture/load \"*\"\n\n// 同上\nyii fixture \"*\"\n\n// 一つを除いて全てのフィクスチャをロードする\nyii fixture \"*, -DoNotLoadThisOne\"\n\n// 異なる名前空間からフィクスチャをロードする (デフォルトの名前空間は tests\\unit\\fixtures)\nyii fixture User --namespace='alias\\my\\custom\\namespace'\n\n// 他のフィクスチャをロードする前に、グローバルフィクスチャ `some\\name\\space\\CustomFixture` をロードする。\n// デフォルトでは、このオプションが `InitDbFixture` について適用され、整合性チェックが無効化/有効化されます。\n// カンマで区切って複数のグローバル・フィクスチャを指定することが出来ます。\nyii fixture User --globalFixtures='some\\name\\space\\Custom'\n```\n\n### フィクスチャをアンロードする\n\nフィクスチャをアンロードするためには、次のコマンドを実行します。\n\n```\n// Users フィクスチャをアンロードする。デフォルトではフィクスチャのストレージをクリアします。(例えば、\"users\" テーブル、または、mongodb フィクスチャなら \"users\" コレクションがクリアされます。)\nyii fixture/unload User\n\n// いくつかのフィクスチャをアンロードする\nyii fixture/unload \"User, UserProfile\"\n\n// すべてのフィクスチャをアンロードする\nyii fixture/unload \"*\"\n\n// 一つを除いて全てのフィクスチャをアンロードする\nyii fixture/unload \"*, -DoNotUnloadThisOne\"\n\n```\n\nこのコマンドでも、`namespace` や `globalFixtures` という同じオプションを適用することが出来ます。\n\n### コマンドをグローバルに構成する\n\nコマンドライン・オプションはフィクスチャ・コマンドをその場で構成することを可能にするものですが、\nコマンドを一度だけ構成して済ませたい場合もあります。\n例えば、次のように、異なるフィクスチャのパスを構成することが出来ます。\n\n```\n'controllerMap' => [\n    'fixture' => [\n        'class' => 'yii\\console\\controllers\\FixtureController',\n        'namespace' => 'myalias\\some\\custom\\namespace',\n        'globalFixtures' => [\n            'some\\name\\space\\Foo',\n            'other\\name\\space\\Bar'\n        ],\n    ],\n]\n```\n\n### フィクスチャを自動生成する\n\nYii は、あなたの代りに、何らかのテンプレートに従ってフィクスチャを自動生成することが出来ます。さまざまなデータで、また、いろいろな言語と形式で、フィクスチャを生成することが出来ます。\nこの機能は、[Faker](https://github.com/fzaninotto/Faker) ライブラリと `yii2-faker` エクステンションによって実現されています。\n詳細については、エクステンションの [ガイド](https://github.com/yiisoft/yii2-faker/tree/master/docs/guide-ja) を参照して下さい。.\n\n## まとめ\n\n以上、フィクスチャを定義して使用する方法を説明しました。\n下記に、DB に関連した単体テストを走らせる場合の典型的なワークフローをまとめておきます。\n\n1. `yii migrate` ツールを使って、テストのデータベースを最新版にアップグレードする\n2. テスト・ケースを走らせる\n   - フィクスチャをロードする - 関係する DB テーブルをクリーンアップし、フィクスチャ・データを投入する\n   - 実際のテストを実行する\n   - フィクスチャをアンロードする\n3. 全てのテストが完了するまで、ステップ 2 を繰り返す\n"
  },
  {
    "path": "docs/guide-ja/test-functional.md",
    "content": "機能テスト\n==========\n\n機能テストはユーザの視点からシナリオを検証するものです。\n[受入テスト](test-acceptance.md) と似ていますが、HTTP によって通信する代りに、\nPOST や GET のパラメータなどの環境変数を設定しておいてから、アプリケーションのインスタンスをコードから直接に実行します。\n\n機能テストは一般的に受入テストより高速であり、失敗した場合に詳細なスタックトレースを提供してくれます。\n経験則から言うと、特別なウェブ・サーバ設定や JavaScript による複雑な UI を持たない場合は、\n機能テストの方を選ぶべきです。\n\n機能テストは Codeception フレームワークの助けを借りて実装されています。これについては、優れたドキュメントがあります。\n\n- [Codeception for Yii framework](https://codeception.com/for/yii)\n- [Codeception Functional Tests](https://codeception.com/docs/04-FunctionalTests)\n\n## ベーシック・テンプレート、アドバンスト・テンプレートのテストを実行する\n\nアドバンスト・テンプレートで開発をしている場合は、テスト実行の詳細について、\n[\"テスト\" のガイド](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide-ja/start-testing.md) を参照して下さい。\n\nベーシック・テンプレートで開発をしている場合は、[README の \"testing\" のセクション](https://github.com/yiisoft/yii2-app-basic/blob/master/README.md#testing) を参照して下さい。\n"
  },
  {
    "path": "docs/guide-ja/test-overview.md",
    "content": "テスト\n======\n\nテストはソフトウェア開発の重要な部分です。\n気付いているか否かにかかわらず、私たちは継続的にテストをしています。\n例えば、PHP でクラスを書くとき、私たちはステップごとにデバッグしたり、または単純に `echo` 文や `die` 文を使ったりして、実装が最初の計画通りに動作することを検証します。\nウェブ・アプリケーションの場合は、何らかのテスト・データをフォームに入力して、ページが期待通りにユーザと相互作用をすることを確認します。\n\nテストを実行するプロセスを自動化して、何かを検証する必要があるときは、いつでも、\nそれを代行してくれるコードを呼び出すだけでよいようにすることが出来ます。\n結果が計画したものと合致することを検証するコードが *テスト* と呼ばれ、それを作成して更に実行するプロセスが *テスト自動化* として知られています。\nこのテストの章の主題は、このテストの自動化です。\n\n\n## テストとともに開発する\n\nテスト駆動開発 (TDD) とビヘイビア駆動開発 (BDD) のソフトウェア開発手法においては、実際のコードを書く前に、\nコードの断片または全体の機能の振る舞いを一連のシナリオまたはテストとして記述します。\nそして、その後で初めて、テストに合格するように実装を作成して、\n意図された振る舞いが達成されていることを検証します。\n\n一つの機能を開発するプロセスは以下のようになります。\n\n- 実装されるべき機能を記述するテストを作成する。\n- 新しいテストを走らせて、失敗することを確認する。まだ実装がないので、これは予期された結果です。\n- 新しいテストに合格するための単純なコードを書く。\n- 全てのテストを走らせて、全てが合格することを確認する。\n- コードを改良して、それでも全てのテストが OK であることを確認する。\n\n完了すれば、別の機能または改良のために、このプロセスを再び繰り返します。\n既存の機能が変更される場合は、テストも変更されなければなりません。\n\n> Tip: 多数の小さくて単純なイテレーションを繰り返すために時間を取られていると感じる場合は、テスト・シナリオのカバー範囲を広くして、テストを再度実行するまでの作業量を増やしてみてください。\n> デバッグばかりやっている場合は、逆に範囲を狭めてみてください。\n\n全ての実装作業の前にテストを作成する理由は、そうすれば、その後で、\n達成したい事柄に集中して「どのようにするか」に没頭することが出来るからです。\n通常、そのようにすることは、良い抽象化、機能修正時の容易なテスト保守、また、結合度の低いコンポーネントにつながります。\n\nですから、このような手法の長所を要約すると次のようになります。\n\n- 一時に一つの事柄に集中できるため、計画と実装がより良いものになる。\n- より多くの機能をより詳細にテストでカバーできる。つまり、テストが OK なら何も問題がないと期待できる。\n\n通常は、長い期間で見れば、かなり時間を節約する効果があります。\n\n## いつ、どうやって、テストするか\n\n上記で説明したテスト・ファーストの手法は長期間にわたる比較的複雑なプロジェクトには合理的なものですが、簡単なプロジェクトでは、やりすぎとなるおそれもあります。\nこの手法が適切であることを示す指標がいくつかあります。\n\n- プロジェクトは既に大きくて複雑である。\n- プロジェクトの要求仕様が複雑になってきている。プロジェクトが継続的に大きくなっている。\n- プロジェクトが長期にわたる予定である。\n- 失敗のコストが高すぎる。\n\n既存の実装の振る舞いをカバーするテストを作成することは、何も悪いことではありません。\n\n- プロジェクトはレガシーなものであるが、段階的に刷新される予定である。\n- 従事すべきプロジェクトを得たが、それにはテストがなかった。\n\nどんな形式の自動化テストもやりすぎになる、という場合もあるでしょう。\n\n- プロジェクトが単純で、この先も、複雑になる心配はない。\n- これ以上かかわることはない一度限りのプロジェクトである。\n\nただ、このような場合であっても、時間に余裕があれば、テストを自動化することは良いことです。\n\n## 参考\n\n- Test Driven Development: By Example / Kent Beck. ISBN: 0321146530.\n"
  },
  {
    "path": "docs/guide-ja/test-unit.md",
    "content": "単体テスト\n==========\n\n単体テストは、一かたまりのコードが期待通りに動作することを検証するものです。\nすなわち、さまざまな入力パラメータを与えて、クラスのメソッドが期待通りの結果を返すかどうかを検証します。\n単体テストは、通常は、テストされるクラスを書く人によって開発されます。\n\nYii における単体テストは、PHPUnit と Codeception (こちらはオプションです) の上に構築されます。従って、それらのドキュメントを通読することが推奨されます。\n\n- [Codeception for Yii framework](https://codeception.com/for/yii)\n- [Codeception Unit Tests](https://codeception.com/docs/05-UnitTests)\n- [第2章から始まる PHPUnit のドキュメント](https://phpunit.readthedocs.io/en/9.5/writing-tests-for-phpunit.html)\n\n## ベーシック・テンプレート、アドバンスト・テンプレートのテストを実行する\n\nアドバンスト・テンプレートでプロジェクトを開始した場合、テストの実行については、\n[\"テスト\" のガイド](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide-ja/start-testing.md) を参照して下さい。\n\nベーシック・テンプレートでプロジェクトを開始した場合は、[README の \"testing\" のセクション](https://github.com/yiisoft/yii2-app-basic/blob/master/README.md#testing) を参照して下さい。\n\n## フレームワークの単体テスト\n\nYii フレームワーク自体に対する単体テストを走らせたい場合は、\"[Yii 2 の開発を始めよう](https://github.com/yiisoft/yii2/blob/master/docs/internals-ja/getting-started.md)\"\nの説明に従ってください。\n"
  },
  {
    "path": "docs/guide-ja/translators.json",
    "content": "[\n  \"Kazunari Hoshina, @crifff\",\n  \"Tomoki Morit,a, @jamband\",\n  \"Atsuhi Sakurai @mocapapa\",\n  \"Nobuo Kihara, @softark\",\n  \"Hisateru Tanaka, @tanakahisateru\"\n]\n"
  },
  {
    "path": "docs/guide-ja/tutorial-console.md",
    "content": "コンソール・アプリケーション\n============================\n\nウェブ・アプリケーションを構築するための豊富な機能に加えて、Yii はコンソール・アプリケーションのためのフル装備のサポートを持っています。\nコンソール・アプリケーションは、主として、ウェブ・サイトのために実行する必要のあるバックグラウンドのタスクやメンテナンスのタスクを作成するために使われるものです。\n\nコンソール・アプリケーションの構造は Yii のウェブ・アプリケーションのそれと非常に良く似ています。\nコンソール・アプリケーションは一つまたは複数の [[yii\\console\\Controller]] クラスから構成されます。コントローラはコンソール環境ではしばしば「コマンド」と呼ばれます。\nまた、各コントローラは、ウェブのコントローラと全く同じように、一つまたは複数のアクションを持つことが出来ます。\n\nプロジェクト・テンプレートは、両方とも、既にコンソール・アプリケーションを持っています。\nレポジトリのベース・ディレクトリにある `yii` スクリプトを呼び出すことによって、コンソール・アプリケーションを実行することが出来ます。\nこのスクリプトは、何もパラメータを追加せずに実行すると、利用できるコマンドの一覧を表示します。\n\n![./yii コマンドを実行してヘルプを表示する](images/tutorial-console-help.png)\n\nスクリーン・ショットに表示されているように、デフォルトで利用できる一連のコマンドが Yii によって既に定義されています。\n\n- [[yii\\console\\controllers\\AssetController|AssetController]] - JavaScript と CSS ファイルを結合して圧縮することが出来ます。\n  このコマンドについては、[アセットのセクション](structure-assets.md#using-the-asset-command) でさらに学習することが出来ます。\n- [[yii\\console\\controllers\\CacheController|CacheController]] - アプリケーションのキャッシュをフラッシュすることが出来ます。\n- [[yii\\console\\controllers\\FixtureController|FixtureController]] - テストのために、フィクスチャ・データのロードとアンロードを管理します。\n  このコマンドについては [テストのフィクスチャのセクション](test-fixtures.md#managing-fixtures) で詳細に説明されています。\n- [[yii\\console\\controllers\\HelpController|HelpController]] - コンソール・コマンドについてのヘルプ情報を提供します。\n  これがデフォルトのコマンドであり、上のスクリーン・ショットで見た出力を表示するものです。\n- [[yii\\console\\controllers\\MessageController|MessageController]] - ソース・ファイルから翻訳すべきメッセージを抽出します。\n  このコマンドについてさらに学習するためには、[国際化のセクション](tutorial-i18n.md#message-command) を参照してください。\n- [[yii\\console\\controllers\\MigrateController|MigrateController]] - アプリケーションのマイグレーションを管理します。\n  データベースのマイグレーションについては、[データベースのマイグレーションのセクション](db-migrations.md) で詳しく説明されています。\n- [[yii\\console\\controllers\\ServeController|ServeController]] - PHP の内蔵ウェブ・サーバを走らせることが出来ます。\n\n\n使用方法 <span id=\"usage\"></span>\n--------\n\nコンソールのコントローラ・アクションは次の構文を使って実行します。\n\n```\nyii <route> [--option1=value1 ... argument1 argument2 ... --option2=value2]\n```\n\nオプションはどの位置で指定しても構いません。\n\n上記において、`<route>` はコントローラ・アクションへのルートを示すものです。\nオプション (options) はクラスのプロパティに代入され、引数 (arguments) はアクション・メソッドのパラメータとなります。\n\n例えば、[[yii\\console\\controllers\\MigrateController::$migrationTable|MigrateController::$migrationTable]] として `migrations` を指定し、\nマイグレーションの上限を 5 と指定して [[yii\\console\\controllers\\MigrateController::actionUp()|MigrateController::actionUp()]]\nを呼び出すためには、次のようにします。\n\n```\nyii migrate/up 5 --migrationTable=migrations\n```\n\n> Note: コンソールで `*` を使う場合は、`\"*\"` として引用符号で囲むことを忘れないでください。\n> これは、`*` をカレント・ディレクトリの全てのファイル名に置き換えられるシェルのグロブとして実行してしまうことを避けるためです。\n\n\nエントリ・スクリプト <span id=\"entry-script\"></span>\n------------------\n\nコンソール・アプリケーションのエントリ・スクリプトは、ウェブ・アプリケーションで使用されるブートストラップ・ファイル `index.php` に相当するものです。\nコンソールのエントリ・スクリプトは通常は `yii` と呼ばれるもので、アプリケーションのルート・ディレクトリに配置されています。\nそれは次のようなコードを含んでいます。\n\n```php\n#!/usr/bin/env php\n<?php\n/**\n * Yii console bootstrap file.\n */\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n\nrequire __DIR__ . '/vendor/autoload.php';\nrequire __DIR__ . '/vendor/yiisoft/yii2/Yii.php';\n\n$config = require __DIR__ . '/config/console.php';\n\n$application = new yii\\console\\Application($config);\n$exitCode = $application->run();\nexit($exitCode);\n```\n\nこのスクリプトはアプリケーションの一部として生成されるものです。あなたの必要を満たすように、自由に編集して構いません。\nエラー発生時にスタック・トレースを見たくない、または、全体のパフォーマンスを上げたい、という場合は、`YII_DEBUG` 定数を `false` に設定することが出来ます。\nベーシック・プロジェクト・テンプレートでも、アドバンスト・プロジェクト・テンプレートでも、コンソール・アプリケーションのエントリ・スクリプトは、開発者に優しい環境を提供するために、デフォルトでデバッグを有効にしています。\n\n\n構成情報 <span id=\"configuration\"></span>\n--------\n\n上記のコードで見るように、コンソール・アプリケーションは、`console.php` という名前のそれ自身の構成情報ファイルを使用します。\nこのファイルの中で、さまざまな [アプリケーション・コンポーネント](structure-application-components.md)、取り分け、コンソール・アプリケーションのためのプロパティを構成しなければなりません。\n\nウェブ・アプリケーションとコンソール・アプリケーションが構成情報のパラメータと値を数多く共有する場合は、共通の部分を独立したファイルに移動して、\nそのファイルを両方のアプリケーション (ウェブとコンソール) の構成情報にインクルードすることを検討しても良いでしょう。\nその例をアドバンスト・プロジェクト・テンプレートの中で見ることが出来ます。\n\n> Tip: 場合によっては、エントリ・スクリプトで指定されているのとは異なるアプリケーション構成情報を使って\n> コンソール・コマンドを実行したいことがあります。\n> 例えば、`yii migrate` コマンドを使ってテストのデータベースをアップグレードするとき、\n> データベースが個々のテストスイートの中で構成されているような場合です。\n> 構成情報を動的に変更するためには、コマンドを実行するときに `appconfig` オプションを使ってカスタムの構成情報ファイルを指定するだけで大丈夫です。\n> \n> ```\n> yii <route> --appconfig=path/to/config.php ...\n> ```\n\n\nコンソール・コマンドの補完 <span id=\"console-command-completion\"></span>\n--------------------------\n\nシェルで作業をしている場合、コマンド引数の自動補完は便利なものです。\n2.0.11 以降、`./yii` コマンドは、Bash および ZSH のための自動補完を内蔵でサポートしています。\n\n### Bash の補完\n\nbash completion がインストールされていることを確認して下さい。ほとんどのインストレーションでは、デフォルトで利用可能になっています。\n\n補完スクリプトを `/etc/bash_completion.d/` に置いて下さい。\n\n     curl -L https://raw.githubusercontent.com/yiisoft/yii2/master/contrib/completion/bash/yii -o /etc/bash_completion.d/yii\n\n一時的な利用の場合は、ファイルをカレント・ディレクトリに置いて、`source yii` でカレント・セッションに読み込みます。\nグローバルにインストールした場合は、ターミナルを再起動するか、`source ~/.bashrc` を実行して、有効化する必要があります。\n\nあなたの環境で補完スクリプトを読み込む他の方法については、\n[Bash マニュアル](https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion.html) を参照して下さい。\n\n### ZSH の補完\n\n補完のためのディレクトリ、例えば `~/.zsh/completion/` に補完スクリプトを置いて下さい。\n\n```\nmkdir -p ~/.zsh/completion\ncurl -L https://raw.githubusercontent.com/yiisoft/yii2/master/contrib/completion/zsh/_yii -o ~/.zsh/completion/_yii\n```\n\nそのディレクトリを `$fpath` に追加します。例えば `~/.zshrc` に次の記述を追加します。\n\n```\nfpath=(~/.zsh/completion $fpath)\n```\n\n`compinit` がロードされていることを確認して下さい。そうでなければ、`~/.zshrc` の中でロードします。\n\n```\nautoload -Uz compinit && compinit -i\n```\n\nそしてシェルをリロードします。\n\n```\nexec $SHELL -l\n```\n\nあなた自身のコンソール・コマンドを作成する <span id=\"create-command\"></span>\n------------------------------------------\n\n### コンソールのコントローラとアクション\n\nコンソール・コマンドは、[[yii\\console\\Controller]] を拡張するコントローラ・クラスとして定義することが出来ます。\nコントローラ・クラスの中で、コントローラのサブ・コマンドに対応する一つまたは複数のアクションを定義します。各アクションの中で、その特定のサブ・コマンドのための適切なタスクを実装するコードを書きます。\n\nコマンドを実行するときは、コントローラのアクションに対するルートを指定する必要があります。\n例えば、ルート `migrate/create` は、[[yii\\console\\controllers\\MigrateController::actionCreate()|MigrateController::actionCreate()]]\nアクション・メソッドに対応するサブコマンドを呼び出します。\n実行時に提供されたルートにアクション ID が含まれない場合は、(ウェブのコントローラの場合と同じように) デフォルトのアクションが実行されます。\n\n### オプション\n\n[[yii\\console\\Controller::options()]] メソッドをオーバーライドすることによって、コンソール・コマンド (controller/actionID) で利用できるオプションを指定することが出来ます。\nこのメソッドはコントローラ・クラスのパブリックなプロパティのリストを返さなければなりません。\nコマンドを実行するときは、`--OptionName=OptionValue` という構文を使ってオプションの値を指定することが出来ます。\nこれはコントローラ・クラスの `OptionName` プロパティに `OptionValue` を割り当てるものです。\n\nオプションのデフォルト値が配列型である場合、実行時にこのオプションをセットすると、\nオプションの値は、入力文字列をカンマで分離することによって、配列に変換されます。\n\n### オプションのエイリアス\n\nバージョン 2.0.8 以降、コンソールコマンドは、オプションにエイリアスを追加するための\n[[yii\\console\\Controller::optionAliases()]] メソッドを提供しています。\n\nエイリアスを定義するためには、コントローラで [[yii\\console\\Controller::optionAliases()]] をオーバーライドします。例えば、\n\n```php\nnamespace app\\commands;\n\nuse yii\\console\\Controller;\n\nclass HelloController extends Controller\n{\n    public $message;\n    \n    public function options($actionID)\n    {\n        return ['message'];\n    }\n    \n    public function optionAliases()\n    {\n        return ['m' => 'message'];\n    }\n    \n    public function actionIndex()\n    {\n        echo $this->message . \"\\n\";\n    }\n}\n```\n\nこれで、次の構文を使ってコマンドを走らせることが出来るようになります。\n\n```\n./yii hello -m=hello\n```\n\n### 引数\n\nオプションに加えてに、コマンドは引数を取ることも出来ます。引数は、リクエストされたサブ・コマンドに対応するアクション・メソッドへのパラメータとして渡されます。\n最初の引数は最初のパラメータに対応し、二番目の引数は二番目のパラメータに対応し、以下同様です。\nコマンドが呼び出されたときに十分な数の引数が提供されなかったときは、対応するパラメータは、定義されていれば、宣言されているデフォルト値をとります。\nデフォルト値が設定されておらず、実行時に値が提供されなかった場合は、コマンドはエラーで終了します。\n\n`array` タイプ・ヒントを使って、引数が配列として扱われるべきことを示すことが出来ます。\n配列は入力文字列をカンマで分割することによって生成されます。\n\n次に引数を宣言する方法を示す例を挙げます。\n\n```php\nclass ExampleController extends \\yii\\console\\Controller\n{\n    // コマンド \"yii example/create test\" は \"actionCreate('test')\" を呼び出す\n    public function actionCreate($name) { ... }\n\n    // コマンド \"yii example/index city\" は \"actionIndex('city', 'name')\" を呼び出す\n    // コマンド \"yii example/index city id\" は call \"actionIndex('city', 'id')\" を呼び出す\n    public function actionIndex($category, $order = 'name') { ... }\n\n    // コマンド \"yii example/add test\" は \"actionAdd(['test'])\" を呼び出す\n    // コマンド \"yii example/add test1,test2\" は \"actionAdd(['test1', 'test2'])\" を呼び出す\n    public function actionAdd(array $name) { ... }\n}\n```\n\n\n### 終了コード\n\n終了コードを使うことはコンソール・アプリケーション開発のベスト・プラクティスです。\nコマンドは何も問題が無かったことを示すために `0` を返すのが慣例です。コマンドが 1 以上の値を返したときは、何かエラーを示唆するものとみなされます。\n返される数値がエラーコードであり、それによってエラーに関する詳細を見出すことが出来る場合もあります。\n例えば、`1` は一般的な未知のエラーを示すものとし、`2` 以上の全てのコードは特定のエラー、例えば、入力エラー、ファイルが見つからない、等々を示すものとすることが出来ます。\n\nコンソール・コマンドに終了コードを返させるためには、\n単にコントローラのアクション・メソッドで整数を返すようにします。\n\n```php\npublic function actionIndex()\n{\n    if (/* 何らかの問題が発生 */) {\n        echo \"A problem occurred!\\n\";\n        return 1;\n    }\n    // 何かをする\n    return 0;\n}\n```\n\nいくつか使用できる事前定義された定数があります。それらは [[yii\\console\\ExitCode]] クラスで定義されています。\n\n```php\npublic function actionIndex()\n{\n    if (/* 何らかの問題が発生 */) {\n        echo \"A problem occurred!\\n\";\n        return ExitCode::UNSPECIFIED_ERROR;\n    }\n    // 何かをする\n    return ExitCode::OK;\n}\n```\n\nもっと詳細なエラー・コードを必要とする場合は、コントローラで詳細な定数を定義するのが良いプラクティスです。\n\n### 書式設定と色\n\nYii のコンソール・コマンドは出力の書式設定をサポートしています。\nこれは、コマンドを走らせている端末がサポートしていない場合は、自動的に書式設定の無い出力にグレードダウンされます。\n\n書式設定された文字列を出力することは簡単です。ボールドのテキストを出力するには、次のようにします。\n\n```php\n$this->stdout(\"Hello?\\n\", Console::BOLD);\n```\n\n複数のスタイルを動的に結合して文字列を構成する必要がある場合は、[[yii\\helpers\\Console::ansiFormat()|ansiFormat()]] を使うほうが良いでしょう。\n\n```php\n$name = $this->ansiFormat('Alex', Console::FG_YELLOW);\necho \"Hello, my name is $name.\";\n```\n\n### 表形式\n\nバージョン 2.0.13 以降、表形式のデータをコンソールに表示するウィジェットが追加されています。次のようにして使うことが出来ます。\n\n```php\necho Table::widget([\n    'headers' => ['Project', 'Status', 'Participant'],\n    'rows' => [\n        ['Yii', 'OK', '@samdark'],\n        ['Yii', 'OK', '@cebe'],\n    ],\n]);\n```\n\n詳細については [[yii\\console\\widgets\\Table|API リファレンス]] を参照して下さい。\n"
  },
  {
    "path": "docs/guide-ja/tutorial-core-validators.md",
    "content": "コア・バリデータ\n================\n\nYii は、一般的に使われる一連のコア・バリデータを提供しています。コア・バリデータは、主として、`yii\\validators` 名前空間の下にあります。\n長ったらしいバリデータ・クラス名を使う代りに、*エイリアス* を使って使用するコア・バリデータを指定することが出来ます。\n例えば、[[yii\\validators\\RequiredValidator]] クラスを参照するのに `required` というエイリアスを使うことが出来ます。\n\n```php\npublic function rules()\n{\n    return [\n        [['email', 'password'], 'required'],\n    ];\n}\n```\n\n[[yii\\validators\\Validator::builtInValidators]] プロパティがサポートされている全てのコア・バリデータのエイリアスを宣言しています。\n\n以下では、全てのコア・バリデータについて、主な使用方法とプロパティを説明します。\n\n\n## [[yii\\validators\\BooleanValidator|boolean]] <span id=\"boolean\"></span>\n\n```php\n[\n    // データ型にかかわらず、\"selected\" が 0 または 1 であるかどうかチェック\n    ['selected', 'boolean'],\n\n    // \"deleted\" が boolean 型であり、true または false であるかどうかチェック\n    ['deleted', 'boolean', 'trueValue' => true, 'falseValue' => false, 'strict' => true],\n]\n```\n\nこのバリデータは、入力値が真偽値であるかどうかをチェックします。\n\n- `trueValue`: `true` を表す値。デフォルト値は `'1'`。\n- `falseValue`: `false` を表す値。デフォルト値は `'0'`。\n- `strict`: 入力値の型が `trueValue` と `falseValue` の型と一致しなければならないかどうか。デフォルト値は `false`。\n\n\n> Note: HTML フォームで送信されたデータ入力値は全て文字列であるため、通常は、\n  [[yii\\validators\\BooleanValidator::strict|strict]] プロパティは `false` のままにすべきです。\n\n\n## [[yii\\captcha\\CaptchaValidator|captcha]] <span id=\"captcha\"></span>\n\n```php\n[\n    ['verificationCode', 'captcha'],\n]\n```\n\nこのバリデータは、通常、[[yii\\captcha\\CaptchaAction]] および [[yii\\captcha\\Captcha]] と一緒に使われ、\n入力値が [[yii\\captcha\\Captcha|CAPTCHA]] ウィジェットによって表示された検証コードと同じであることを確認します。\n\n- `caseSensitive`: 検証コードの比較で大文字と小文字を区別するか否か。デフォルト値は `false`。\n- `captchaAction`: CAPTCHA 画像を表示する [[yii\\captcha\\CaptchaAction|CAPTCHA アクション]] に対応する\n  [ルート](structure-controllers.md#routes)。デフォルト値は `'site/captcha'`。\n- `skipOnEmpty`: 入力値が空のときに検証をスキップできるかどうか。デフォルト値は `false` で、\n  入力が必須であることを意味します。\n\n\n## [[yii\\validators\\CompareValidator|compare]] <span id=\"compare\"></span>\n\n```php\n[\n    // \"password\" 属性の値が \"password_repeat\" 属性の値と同じであるかどうか検証する\n    ['password', 'compare'],\n\n    // 上記と同じだが、比較する属性を明示的に指定\n    ['password', 'compare', 'compareAttribute' => 'password_repeat'],\n\n    // \"age\" が 30 以上であるかどうか検証する\n    ['age', 'compare', 'compareValue' => 30, 'operator' => '>=', 'type' => 'number'],\n]\n```\n\nこのバリデータは指定された入力値を他の値と比較し、両者の関係が\n`operator` プロパティで指定されたものであることを確認します。\n\n- `compareAttribute`: その値が比較対象となる属性の名前。\n  このバリデータが属性を検証するのに使用されるとき、このプロパティのデフォルト値はその属性の名前に接尾辞\n  `_repeat` を付けた名前になります。\n  例えば、検証される属性が `password` であれば、このプロパティのデフォルト値は `password_repeat` となります。\n- `compareValue`: 入力値が比較される定数値（または値を返すクロージャ）。\n  このプロパティと `compareAttribute` の両方が指定された場合は、このプロパティが優先されます。\n- `operator`: 比較演算子。デフォルト値は `==` で、入力値が `compareAttribute` の値または `compareValue` と等しいことを検証することを意味します。\n  次の演算子がサポートされています。\n     * `==`: 二つの値が等しいことを検証。厳密でない比較を行う。\n     * `===`: 二つの値が等しいことを検証。厳密な比較を行う。\n     * `!=`: 二つの値が等しくないことを検証。厳密でない比較を行う。\n     * `!==`: 二つの値が等しくないことを検証。厳密な比較を行う。\n     * `>`: 検証される値が比較される値よりも大きいことを検証する。\n     * `>=`: 検証される値が比較される値よりも大きいか等しいことを検証する。\n     * `<`: 検証される値が比較される値よりも小さいことを検証する。\n     * `<=`: 検証される値が比較される値よりも小さいか等しいことを検証する。\n- `type`: デフォルトの比較タイプは '[[yii\\validators\\CompareValidator::TYPE_STRING|string]]' (文字列) であり、その場合、値は 1 バイトごとに比較されます。\n  数値を比較する場合は、必ず [[yii\\validators\\CompareValidator::$type|$type]] を '[[yii\\validators\\CompareValidator::TYPE_NUMBER|number]]' に設定して、\n  数値としての比較を有効にして下さい。\n\n### 日付の値を比較する\n\ncompare バリデータは、文字列や数値を比較するためにしか使えません。\n日付のような値を比較する必要がある場合は、二つの選択肢があります。\n日付をある固定値と比較するときは、単に [[yii\\validators\\DateValidator|date]] バリデータを使って、\nその [[yii\\validators\\DateValidator::$min|$min]] や [[yii\\validators\\DateValidator::$max|$max]] のプロパティを指定すれば良いでしょう。\nフォームに入力された二つの日付、例えば、`fromDate` と `toDate` のフィールドを比較する必要がある場合は、\n次のように、compare バリデータと date バリデータを組み合わせて使うことが出来ます。\n\n```php\n['fromDate', 'date', 'timestampAttribute' => 'fromDate'],\n['toDate', 'date', 'timestampAttribute' => 'toDate'],\n['fromDate', 'compare', 'compareAttribute' => 'toDate', 'operator' => '<', 'enableClientValidation' => false],\n```\n\nバリデータは指定された順序に従って実行されますので、まず最初に、`fromDate` と `toDate` に入力された値が\n有効な日付であることが確認され、有効な日付であった場合は、機械が読める形式に変換されます。\nその後に、これらの二つの値が compare バリデータによって比較されます。\n現在、date バリデータはクライアント・サイドのバリデーションを提供していませんので、これはサーバ・サイドでのみ動作します。\nそのため、compare バリデータについても、[[yii\\validators\\CompareValidator::$enableClientValidation|$enableClientValidation]] は\n`false` に設定されています。\n\n\n## [[yii\\validators\\DateValidator|date]] <span id=\"date\"></span>\n\n[[yii\\validators\\DateValidator|date]] バリデータには三つの異なる\nショートカットがあります。\n\n```php\n[\n    [['from_date', 'to_date'], 'date'],\n    [['from_datetime', 'to_datetime'], 'datetime'],\n    [['some_time'], 'time'],\n]\n```\n\nこのバリデータは、入力値が正しい書式の date、time、または datetime であるかどうかをチェックします。\nオプションとして、入力値を UNIX タイムスタンプ (または、その他、機械による読み取りが可能な形式) に変換して、\n[[yii\\validators\\DateValidator::timestampAttribute|timestampAttribute]] によって指定された属性に保存することも出来ます。\n\n- `format`: 検証される値が従っているべき日付/時刻の書式。\n  これには [ICU manual](https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax)\n  で記述されている日付/時刻のパターンを使うことが出来ます。\n  あるいは、PHP の `Datetime` クラスによって認識される書式に接頭辞 `php:` を付けた文字列でも構いません。\n  サポートされている書式については、<https://www.php.net/manual/ja/datetime.createfromformat.php> を参照してください。\n  このプロパティが設定されていないときは、`Yii::$app->formatter->dateFormat` の値を取ります。\n\n- `timestampAttribute`: このバリデータが、入力された日付/時刻から変換した UNIX タイムスタンプを代入することが出来る属性の名前。\n  これは、検証される属性と同じ属性であってもかまいません。\n  その場合は、元の値は検証実行後にタイムスタンプで上書きされます。\n  [DatePicker で日付の入力を扱う](https://github.com/yiisoft/yii2-jui/blob/master/docs/guide-ja/topics-date-picker.md) に使用例がありますので、参照してください。\n\n  バージョン 2.0.4 以降では、[[yii\\validators\\DateValidator::$timestampAttributeFormat|$timestampAttributeFormat]] と\n[[yii\\validators\\DateValidator::$timestampAttributeTimeZone|$timestampAttributeTimeZone]] を使って、\nこの属性に対するフォーマットとタイム・ゾーンを指定することが出来ます。\n\n`timestampAttribute` を使う場合、入力値が UNIX タイムスタンプに変換されること、そして、UNIX タイムスタンプは定義により UTC であることに注意して下さい。\nすなわち、[[yii\\validators\\DateValidator::timeZone|入力のタイム・ゾーン]] から UTC への変換が実行されます。\n（この動作は、2.0.39 以降では、[[yii\\validators\\DateValidator::$defaultTimeZone|$defaultTimeZone]] を設定して変更することが出来ます）\n\n- バージョン 2.0.4 以降では、タイムスタンプの [[yii\\validators\\DateValidator::$min|最小値]] または\n  [[yii\\validators\\DateValidator::$max|最大値]] を指定することも出来ます。\n\n入力が必須でない場合には、date バリデータに加えて、default バリデータ (デフォルト値フィルタ) を追加すれば、\n空の入力値が `null` として保存されることを保証することが出来ます。そうしないと、データベースに `0000-00-00` という日付が保存されたり、\nデート・ピッカーの入力フィールドが `1970-01-01` になったりしてしまいます。\n\n```php\n[\n    [['from_date', 'to_date'], 'default', 'value' => null],\n    [['from_date', 'to_date'], 'date'],\n]\n```\n\n## [[yii\\validators\\DefaultValueValidator|default]] <span id=\"default\"></span>\n\n```php\n[\n    // 空のときは \"age\" を null にする\n    ['age', 'default', 'value' => null],\n\n    // 空のときは \"country\" を \"USA\" にする\n    ['country', 'default', 'value' => 'USA'],\n\n    // 空のときは \"from\" と \"to\" に今日から三日後・六日後の日付を入れる\n    [['from', 'to'], 'default', 'value' => function ($model, $attribute) {\n        return date('Y-m-d', strtotime($attribute === 'to' ? '+3 days' : '+6 days'));\n    }],\n]\n```\n\nこのバリデータはデータを検証しません。\nその代りに、検証される属性が空のときに、その属性にデフォルト値を割り当てます。\n\n- `value`: デフォルト値、または、デフォルト値をコールバックとして返すクロージャ。\n  検証される属性が空のときにこの値が割り当てられます。クロージャのシグニチャは、次のものでなければなりません。\n\n```php\nfunction foo($model, $attribute) {\n    // ... $value を計算 ...\n    return $value;\n}\n```\n\n> Info: 値が空であるか否かを決定する方法については、独立したトピックとして、[空の入力値を扱う](input-validation.md#handling-empty-inputs) のセクションでカバーされています。\n  データベース・スキーマによるデフォルト値は、モデルの [loadDefaultValues()](db-active-record.md#default-attribute-values)\n  によってロードすることが出来ます。\n\n\n## [[yii\\validators\\NumberValidator|double]] <span id=\"double\"></span>\n\n```php\n[\n    // \"salary\" が実数であるかどうかチェック\n    ['salary', 'double'],\n]\n```\n\nこのバリデータは、入力値が実数値であるかどうかをチェックします。[number](#number) バリデータと等価です。\n\n- `max`: 上限値 (その値を含む)。設定されていない場合は、バリデータが上限値をチェックしないことを意味します。\n- `min`: 下限値 (その値を含む)。設定されていない場合は、バリデータが下限値をチェックしないことを意味します。\n\n\n## [[yii\\validators\\EachValidator|each]] <span id=\"each\"></span>\n\n> Info: このバリデータは、バージョン 2.0.4 以降で利用できます。\n\n```php\n[\n    // 全てのカテゴリ ID が整数であるかどうかチェックする\n    ['categoryIDs', 'each', 'rule' => ['integer']],\n]\n```\n\nこのバリデータは配列の属性に対してのみ働きます。\nこれは、配列の *全ての* 要素が指定された検証規則による検証に成功するかどうかを調べるものです。\n上の例では、`categoryIDs` 属性は配列を値として取らなければならず、配列の各要素は `integer` の検証規則によって検証されることになります。\n\n- `rule`: 検証規則を指定する配列。配列の最初の要素がバリデータのクラス名かエイリアスを指定します。\n  配列の残りの「名前・値」のペアが、バリデータ・オブジェクトを構成するのに使われます。\n- `allowMessageFromRule`: 埋め込まれた検証規則によって返されるエラー・メッセージを使うかどうか。\n  デフォルト値は `true` です。これが `false` の場合は、`message` をエラー・メッセージとして使います。\n\n> Note: 属性が配列でない場合は、検証が失敗したと見なされ、\n  `message` がエラー・メッセージとして返されます。\n\n\n## [[yii\\validators\\EmailValidator|email]] <span id=\"email\"></span>\n\n```php\n[\n    // \"email\" が有効なメール・アドレスであるかどうかチェック\n    ['email', 'email'],\n]\n```\n\nこのバリデータは、入力値が有効なメール・アドレスであるかどうかをチェックします。\n\n- `allowName`: メール・アドレスに表示名 (例えば、`John Smith <john.smith@example.com>`) を許容するか否か。デフォルト値は `false`。\n- `checkDNS`: メールのドメインが存在して A または MX レコードを持っているかどうかをチェックするか否か。\n  このチェックは、メール・アドレスが実際には有効なものでも、一時的な DNS の問題によって失敗する場合があることに注意してください。\n  デフォルト値は `false`。\n- `enableIDN`: 検証のプロセスが IDN (国際化ドメイン名) を考慮に入れるか否か。\n  デフォルト値は `false`。\n  IDN の検証を使用するためには、`intl` PHP 拡張をインストールして有効化する必要があることに注意してください。そうしないと、例外が投げられます。\n\n\n## [[yii\\validators\\ExistValidator|exist]] <span id=\"exist\"></span>\n\n```php\n[\n    // a1 の入力値が a1 のカラムに存在する必要がある\n    // すなわち、a1 = 1 は、\"a1\" カラムに 1 の値が存在する場合に有効\n    ['a1', 'exist'],\n    // 以下と同義\n    ['a1', 'exist', 'targetAttribute' => 'a1'],\n    ['a1', 'exist', 'targetAttribute' => ['a1' => 'a1']],\n\n    // a1 の入力値が a2 のカラムに存在する必要がある\n    // すなわち、a1 = 2 は、\"a2\" カラムに 2 の値が存在する場合に有効\n    ['a1', 'exist', 'targetAttribute' => 'a2'],\n    // 以下と同義\n    ['a1', 'exist', 'targetAttribute' => ['a1' => 'a2']],\n\n    // a2 の入力値が a2 のカラムに存在する必要がある。a1 がエラー・メッセージを受ける\n    // すなわち、a2 = 2 は、\"a2\" カラムに 2 の値が存在する場合に有効\n    ['a1', 'exist', 'targetAttribute' => ['a2']],\n    // 以下と同義\n    ['a1', 'exist', 'targetAttribute' => ['a2' => 'a2']],\n\n    // a1 と a2 の両方が存在する必要がある。エラーの無い最初の属性がエラー・メッセージを受け取る\n    // すなわち、a1 = 3, a2 = 4 は、\"a1\" カラムに 3, \"a2\" カラムに 4 が存在する場合に有効\n    [['a1', 'a2'], 'exist', 'targetAttribute' => ['a1', 'a2']],\n    // 以下と同義\n    [['a1', 'a2'], 'exist', 'targetAttribute' => ['a1' => 'a1', 'a2' => 'a2']],\n\n    // a1 と a2 の両方が存在する必要がある。a1 のみがエラー・メッセージを受け取る\n    // すなわち、a1 = 5, a2 = 6 は、\"a1\" カラムに 5, \"a2\" カラムに 6 が存在する場合に有効\n    ['a1', 'exist', 'targetAttribute' => ['a1', 'a2']],\n    // 以下と同義\n    ['a1', 'exist', 'targetAttribute' => ['a1' => 'a1', 'a2' => 'a2']],\n\n    // a2 の値が a2 のカラム、a1 の値が a3 のカラムに存在する必要がある。a1 がエラー・メッセージを受け取る\n    // すなわち、a1 = 7, a2 = 8 は、\"a3\" カラムに 7, \"a2\" カラムに 8 が存在する場合に有効\n    ['a1', 'exist', 'targetAttribute' => ['a2', 'a1' => 'a3']],\n    // 以下と同義\n    ['a1', 'exist', 'targetAttribute' => ['a2' => 'a2', 'a1' => 'a3']],\n\n    // a1 が存在する必要がある。a1 が配列である場合は、その全ての要素が存在する必要がある\n    ['a1', 'exist', 'allowArray' => true],\n    // すなわち、a1 = 9 は、\"a1\" カラムに 9 が存在する場合に有効\n    //           a1 = [9, 10] は、\"a1\" カラムに 9 と 10 が存在する場合に有効\n\n    // type_id が ProductType クラスで定義されているテーブルの id カラムに存在する必要がある\n    // すなわち、type_id = 1 は ProductType のテーブルの \"id\" カラムに 1 が存在する場合に有効\n    ['type_id', 'exist', 'targetClass' => ProductType::class, 'targetAttribute' => ['type_id' => 'id']],    \n    \n    // 同上。定義済みの \"type\" リレーションを使用。\n    ['type_id', 'exist', 'targetRelation' => 'type'],\n]\n```\n\nこのバリデータは、入力値が [アクティブ・レコード](db-active-record.md) の属性によって表されるテーブルのカラムに存在するかどうかをチェックします。\n`targetAttribute` を使って [アクティブ・レコード](db-active-record.md) の属性を指定し、\n`targetClass` によって対応するクラスを指定することが出来ます。\nこれらを指定しない場合は、検証されるモデルの属性とクラスが使用されます。\n\nこのバリデータは、一つまたは複数のカラムに対する検証に使用することが出来ます\n(複数のカラムに対する検証の場合は、それらの属性の組み合せが存在しなければならないことを意味します)。\n同時に複数のカラムをチェックして（例えば `['a1', 'a2']`）バリデーションが失敗したときに、`skipOnError` が `true` に設定されている場合は、\n先行するエラーが無い最初の属性だけが新しいエラー・メッセージを受け取ります。\n\n- `targetClass`: 検証される入力値を探すために使用される [アクティブ・レコード](db-active-record.md) クラスの名前。\n  設定されていない場合は、現在検証されているモデルのクラスが使用されます。\n- `targetAttribute`: `targetClass` において、入力値の存在を検証するために使用される属性の名前。\n  設定されていない場合は、現在検証されている属性の名前が使用されます。\n  複数のカラムの存在を同時に検証するために配列を使うことが出来ます。\n  配列の値は存在を検証するのに使用される属性であり、配列のキーは入力値が検証される属性です。\n  キーと値が同じ場合は、値だけを指定することが出来ます。\n  検証されるモデルが ModelA であり、検証に使用されるモデルが ModelB であるとすると、\n  下記のように `targetAttribute` を構成することが出来ます。\n    - `null` => ModelA の現在検証されている属性の値が ModelB の同名の属性の保存されている値に対してチェックされる\n    - `'a'` => ModelA の現在検証されている属性の値が ModelB の属性 \"a\" の保存されている値に対してチェックされる\n    - `['a']` => ModelA の属性 \"a\" の値が ModelB の属性 \"a\" の保存されている値に対してチェックされる\n    - `['a' => 'a']` => 同上\n    - `['a', 'b']` => ModelA の属性 \"a\" の値が ModelB の属性 \"a\" の保存されている値に対してチェックされ、\n    同時に、ModelA の属性 \"b\" の値が ModelB の属性 \"b\" の保存されている値に対してチェックされる\n    - `['a' => 'b']` => ModelA の属性 \"a\" の値が ModelB の属性 \"b\" の保存されている値に対してチェックされる\n- `targetRelation`: バージョン 2.0.14 以降は簡便な `targetRelation` 属性が使えます。これは指定されたリレーションの定義によって `targetClass` と `targetAttribute` の属性をオーバーライドするものです。\n- `filter`: 入力値の存在をチェックするのに使用される DB クエリに適用される追加のフィルタ。\n  これには、追加のクエリ条件を表現する文字列または配列を使うことが出来ます (クエリ条件の書式については、[[yii\\db\\Query::where()]] を参照してください)。\n  または、`function ($query)` というシグニチャを持つ無名関数でも構いません。\n  `$query` は関数の中で修正できる [[yii\\db\\Query|Query]] オブジェクトです。\n- `allowArray`: 入力値が配列であることを許容するか否か。デフォルト値は `false`。\n  このプロパティが `true` で入力値が配列であった場合は、配列の全ての要素がターゲットのカラムに存在しなければなりません。\n  `targetAttribute` を配列で指定して複数のカラムに対して検証しようとしている場合は、このプロパティを `true` に設定することが出来ないことに注意してください。\n\n\n## [[yii\\validators\\FileValidator|file]] <span id=\"file\"></span>\n\n```php\n[\n    // \"primaryImage\" が PNG、JPG、または GIF 形式のアップロードされた\n    // 画像ファイルであり、ファイルサイズが 1MB 以下であるかどうかチェック\n    ['primaryImage', 'file', 'extensions' => ['png', 'jpg', 'gif'], 'maxSize' => 1024*1024],\n]\n```\n\nこのバリデータは、入力値がアップロードされた有効なファイルであるかどうかをチェックします。\n\n- `extensions`: アップロードを許可されるファイル名拡張子のリスト。\n  リストは、配列、または、空白かカンマで区切られたファイル名拡張子からなる文字列 (例えば、\"gif, jpg\") で指定することが出来ます。\n  拡張子名は大文字と小文字を区別しません。\n  デフォルト値は `null` であり、すべてのファイル名拡張子が許可されることを意味します。\n- `mimeTypes`: アップロードを許可されるファイルの MIME タイプのリスト。\n  リストは、配列、または、空白かカンマで区切られたファイルの MIME タイプからなる文字列 (例えば、\"image/jpeg, image/png\") で指定することが出来ます。\n  特殊文字 `*` によるワイルドカードのマスクを使って、一群の MIME タイプに一致させることも出来ます。\n  例えば `image/*` は、`image/` で始まる全ての MIME タイプ (`image/jpeg`, `image/png` など) を通します。\n  MIME タイプ名は大文字と小文字を区別しません。デフォルト値は `null` であり、すべての MIME タイプが許可されることを意味します。\n  MIME タイプの詳細については、[一般的なメディア・タイプ](https://en.wikipedia.org/wiki/Media_type) を参照してください。\n- `minSize`: アップロードされるファイルに要求される最小限のバイト数。デフォルト値は `null` であり、下限値が無いことを意味します。\n- `maxSize`: アップロードされるファイルに許可される最大限のバイト数。デフォルト値は `null` であり、上限値が無いことを意味します。\n- `maxFiles`: 指定された属性が保持しうる最大限のファイル数。\n  デフォルト値は 1 であり、入力値がアップロードされた一つだけのファイルでなければならないことを意味します。\n  この値が 2 以上である場合は、入力値は最大で `maxFiles` 数のアップロードされたファイルからなる配列でなければなりません。\n- `checkExtensionByMimeType`: ファイルの MIME タイプでファイル拡張子をチェックするか否か。\n  MIME タイプのチェックから導かれる拡張子がアップロードされたファイルの拡張子と違う場合に、そのファイルは無効であると見なされます。\n  デフォルト値は `true` であり、そのようなチェックが行われることを意味します。\n\n`FileValidator` は [[yii\\web\\UploadedFile]] と一緒に使用されます。\nファイルのアップロードおよびアップロードされたファイルの検証の実行に関する完全な説明は、[ファイルをアップロードする](input-file-upload.md) のセクションを参照してください。\n\n\n## [[yii\\validators\\FilterValidator|filter]] <span id=\"filter\"></span>\n\n```php\n[\n    // \"username\" と \"email\" の入力値をトリムする\n    [['username', 'email'], 'filter', 'filter' => 'trim', 'skipOnArray' => true],\n\n    // \"phone\" の入力値を正規化する\n    ['phone', 'filter', 'filter' => function ($value) {\n        // 電話番号の入力値をここで正規化する\n        return $value;\n    }],\n    \n    // 関数 \"normalizePhone\" を使って \"phone\" の入力値を正規化する\n    ['phone', 'filter', 'filter' => [$this, 'normalizePhone']],\n    \n    public function normalizePhone($value) {\n        return $value;\n    }\n]\n```\n\nこのバリデータはデータを検証しません。\n代りに、入力値にフィルタを適用して、それを検証される属性に書き戻します。\n\n- `filter`: フィルタを定義する PHP コールバック。これには、グローバル関数の名前、無名関数などを指定することが出来ます。\n  関数のシグニチャは ``function ($value) { return $newValue; }` でなければなりません。このプロパティは必須項目です。\n- `skipOnArray`: 入力値が配列である場合にフィルタをスキップするか否か。デフォルト値は `false`。\n  フィルタが配列の入力を処理できない場合は、このプロパティを `true` に設定しなければなりません。そうしないと、\n  何らかの PHP エラーが生じ得ます。\n\n> Tip: 入力値をトリムしたい場合は、[trim](#trim) バリデータを直接使うことが出来ます。\n\n> Tip: `filter` のコールバックに期待されるシグニチャを持つ PHP 関数が多数存在します。\n> 例えば、([intval](https://www.php.net/manual/ja/function.intval.php) や [boolval](https://www.php.net/manual/ja/function.boolval.php) \n> などを使って) 型キャストを適用し、属性が特定の型になるように保証したい場合は、\n> それらの関数をクロージャで包む必要はなく、単にフィルタの関数名を指定するだけで十分です。\n>\n> ```php\n> ['property', 'filter', 'filter' => 'boolval'],\n> ['property', 'filter', 'filter' => 'intval'],\n> ```\n\n\n## [[yii\\validators\\ImageValidator|image]] <span id=\"image\"></span>\n\n```php\n[\n    // \"primaryImage\" が適切なサイズの有効な画像であることを検証\n    ['primaryImage', 'image', 'extensions' => 'png, jpg',\n        'minWidth' => 100, 'maxWidth' => 1000,\n        'minHeight' => 100, 'maxHeight' => 1000,\n    ],\n]\n```\n\nこのバリデータは、入力値が有効な画像ファイルであるかどうかをチェックします。\nこれは [file](#file) バリデータを拡張するものであり、従って、そのプロパティの全てを継承しています。\nそれに加えて、画像の検証の目的に特化した次のプロパティをサポートしています。\n\n- `minWidth`: 画像の幅の最小値。デフォルト値は `null` であり、下限値がないことを意味します。\n- `maxWidth`: 画像の幅の最大値。デフォルト値は `null` であり、上限値がないことを意味します。\n- `minHeight`: 画像の高さの最小値。デフォルト値は `null` であり、下限値がないことを意味します。\n- `maxHeight`: 画像の高さの最大値。デフォルト値は `null` であり、上限値がないことを意味します。\n\n## [[yii\\validators\\IpValidator|ip]] <span id=\"ip\"></span>\n```php\n[\n    // \"ip_address\" が有効な IPv4 または IPv6 アドレスであることを検証\n    ['ip_address', 'ip'],\n\n    // \"ip_address\" が有効な IPv6 アドレスまたはサブネットであることを検証\n    // 値は完全な IPv6 記法に展開される\n    ['ip_address', 'ip', 'ipv4' => false, 'subnet' => null, 'expandIPv6' => true],\n\n    // \"ip_address\" が有効な IPv4 または IPv6 アドレスであることを検証\n    // 先頭に否定文字 `!` を置くことを許可\n    ['ip_address', 'ip', 'negation' => true],\n]\n```\n\nこのバリデータは属性の値が有効な IPv4/IPv6 アドレスまたはサブネットであることを検証します。\n正規化または IPv6 展開が有効にされた場合は、属性の値を変更することも出来ます。\n\nバリデータは以下の構成オプションを持っています。\n\n- `ipv4`: 検証の対象となる値が IPv4 アドレスであってよいか否か。デフォルト値は `true`。\n- `ipv6`: 検証の対象となる値が IPv6 アドレスであってよいか否か。デフォルト値は `true`。\n- `subnet`: アドレスが `192.168.10.0/24` のような CIDR サブネットを持つ IP であってよいか否か。\n     * `true` - サブネットが必要。CIDR の無いアドレスは却下されます\n     * `false` - アドレスは CIDR を伴ってはいけません\n     * `null` - CIDR は有っても無くても構いません\n\n    デフォルト値は `false`。\n- `normalize`: CIDR を持たないアドレスに、最も短い (IPv4 では 32、IPv6 では 128) CIDR プレフィクスを追加するか否か。\n  `subnet` が `false` 以外の場合にのみ動作します。例えば、\n    * `10.0.1.5` は `10.0.1.5/32` に正規化され、\n    * `2008:db0::1` は `2008:db0::1/128` に正規化されます\n\n    デフォルト値は `false`。\n- `negation`: 検証の対象となるアドレスが先頭に否定文字 `!` を持つことが出来るか否か。デフォルト値は `false`。\n- `expandIPv6`: IPv6 アドレスを完全な記法に展開するか否か。\n  例えば、`2008:db0::1` は `2008:0db0:0000:0000:0000:0000:0000:0001` に展開されます。デフォルト値は `false`。\n- `ranges`: 許容または禁止される IPv4 または IPv6 の範囲の配列。\n\n    配列が空の場合、またはこのオプションが設定されていない場合は、全ての IP アドレスが許容されます。\n    そうでない場合は、最初に合致するものが見つかるまで、規則が順番にチェックされます。\n    どの規則にも合致しなかった場合、その IP アドレスは禁止されます。\n    \n    例えば、\n    ```php\n    [\n         'client_ip', 'ip', 'ranges' => [\n             '192.168.10.128'\n             '!192.168.10.0/24',\n             'any' // 他の IP アドレスは全て許容\n         ]\n    ]\n    ```\nこの例では、`192.168.10.0/24` のサブネットを除いて、全ての IPv4 および IPv6 アドレスが許容されます。\nIPv4 アドレス `192.168.10.128` も、制約の前にリストされているため、同様に許容されます。\n- `networks`: `ranges` で使用する事が出来るネットワークのエイリアスの配列。配列の形式は、\n    * キー - エイリアス名\n    * 値 - 文字列の配列。文字列は、範囲、IP アドレス、または、他のエイリアスとすることが出来ます。\n      また、文字列は (`negation` オプションとは独立に) `!` によって否定することが出来ます。\n\n    デフォルトで、次のエイリアスが定義されています。\n    \n    * `*`: `any`\n    * `any`: `0.0.0.0/0, ::/0`\n    * `private`: `10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fd00::/8`\n    * `multicast`: `224.0.0.0/4, ff00::/8`\n    * `linklocal`: `169.254.0.0/16, fe80::/10`\n    * `localhost`: `127.0.0.0/8', ::1`\n    * `documentation`: `192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24, 2001:db8::/32`\n    * `system`: `multicast, linklocal, localhost, documentation`\n\n> Info: このバリデータは、バージョン 2.0.7 以降で利用することが出来ます。\n\n## [[yii\\validators\\RangeValidator|in]] <span id=\"in\"></span>\n\n```php\n[\n    // \"level\" が 1, 2 または 3 であるかどうかチェック\n    ['level', 'in', 'range' => [1, 2, 3]],\n]\n```\n\nこのバリデータは、入力値が所与の値のリストにあるかどうかをチェックします。\n\n- `range`: 与えられた値のリスト。この中に、入力値がなければならない。\n- `strict`: 入力値と所与の値の比較が厳密でなければならない (型と値の両方が同じでなければならない) かどうか。\n  デフォルト値は `false`。\n- `not`: 検証結果を反転すべきか否か。デフォルト値は `false`。\n  このプロパティが `true` に設定されているときは、入力値が所与の値のリストにない場合に検証が成功したとされます。\n- `allowArray`: 入力値が配列であることを許可するかどうか。\n  このプロパティが `true` であるときに、入力値が配列である場合は、配列の全ての要素が所与の値のリストにある必要があり、そうでなければ検証は失敗します。\n\n\n## [[yii\\validators\\NumberValidator|integer]] <span id=\"integer\"></span>\n\n```php\n[\n    // \"age\" が整数であるかどうかチェック\n    ['age', 'integer'],\n]\n```\n\nこのバリデータは入力値が整数であるかどうかをチェックします。\n\n- `max`: 上限値 (その値を含む)。設定されていないときは、バリデータは上限をチェックしません。\n- `min`: 下限値 (その値を含む)。設定されていないときは、バリデータは下限をチェックしません。\n\n\n## [[yii\\validators\\RegularExpressionValidator|match]] <span id=\"match\"></span>\n\n```php\n[\n    // \"username\" が英字から始まり、英字、数字、アンダーバーだけで構成されているかどうかチェック\n    ['username', 'match', 'pattern' => '/^[a-z]\\w*$/i']\n]\n```\n\nこのバリデータは、入力値が指定された正規表現に一致するかどうかをチェックします。\n\n- `pattern`: 入力値が一致すべき正規表現。このプロパティを設定することは必須です。\n  そうしないと、例外が投げられます。\n- `not`: 検証結果を反転すべきかどうか。\n  デフォルト値は false で、入力値がパターンに一致したときにだけ検証が成功することを意味します。\n  このプロパティが true に設定されているときは、入力値がパターンに一致しない場合にだけ検証が成功したと見なされます。\n\n\n## [[yii\\validators\\NumberValidator|number]] <span id=\"number\"></span>\n\n```php\n[\n    // \"salary\" が数値であるかどうかチェック\n    ['salary', 'number'],\n]\n```\n\nこのバリデータは、入力値が数値であるかどうかをチェックします。[double](#double) バリデータと等価です。\n\n- `max`: 上限値 (その値を含む)。設定されていないときは、バリデータは上限をチェックしません。\n- `min`: 下限値 (その値を含む)。設定されていないときは、バリデータは下限をチェックしません。\n\n\n## [[yii\\validators\\RequiredValidator|required]] <span id=\"required\"></span>\n\n```php\n[\n    // \"username\" と \"password\" がともに空ではないことをチェックする\n    [['username', 'password'], 'required'],\n]\n```\n\nこのバリデータは、入力値が提供されており、空ではないことをチェックします。\n\n- `requiredValue`: 入力値として要求される値。このプロパティが設定されていない場合は、入力値が空ではいけないことを意味します。\n- `strict`: 値を検証するときに、データ型をチェックするかどうか。デフォルト値は `false`。\n  `requiredValue` が設定されていない場合、このプロパティが `true` であるときは、バリデータは入力値が厳密な意味で `null` であるかどうかをチェックします。\n  一方、このプロパティが `false` であるときは、値が空か否かの判断に緩い規則を使います。\n  `requiredValue` が設定されている場合、このプロパティが `true` であるときは、\n入力値と `requiredValue` を比較するときに型のチェックを行います。\n\n> Info: 値が空であるか否かを決定する方法については、独立したトピックとして、\n> [空の入力値を扱う](input-validation.md#handling-empty-inputs) のセクションでカバーされています。\n\n\n## [[yii\\validators\\SafeValidator|safe]] <span id=\"safe\"></span>\n\n```php\n[\n    // \"description\" を安全な属性としてマーク\n    ['description', 'safe'],\n]\n```\n\nこのバリデータは検証を実行しません。\nその代りに、このバリデータは、属性を [安全な属性](structure-models.md#safe-attributes) としてマークするために使われます。\n\n\n## [[yii\\validators\\StringValidator|string]] <span id=\"string\"></span>\n\n```php\n[\n    // \"username\" が、長さが 4 以上 24 以下の文字列であるかどうかチェック\n    ['username', 'string', 'length' => [4, 24]],\n]\n```\n\nこのバリデータは、入力値が一定の長さを持つ有効な文字列であるかどうかをチェックします。\n\n- `length`: 検証される入力文字列の長さの制限を指定します。\n  これは、次のいずれかの形式で指定することが出来ます。\n     * 一つの整数: 文字列がちょうどその長さでなければならない、その長さ。\n     * 一つの要素を持つ配列: 入力文字列の長さの最小値 (例えば、`[8]`)。これは `min` を上書きします。\n     * 二つの要素を持つ配列: 入力文字列の長さの最小値と最大値 (例えば、`[8, 128]`)。\n       これは `min` と `max` の両方を上書きします。\n- `min`: 入力文字列の長さの最小値。設定されていない時は、長さの下限値がないことを意味します。\n- `max`: 入力文字列の長さの最大値。設定されていない時は、長さの上限値がないことを意味します。\n- `encoding`: 検証される入力文字列の文字エンコーディング。設定されていない時は、\n  アプリケーションの [[yii\\base\\Application::charset|charset]] の値が使われ、デフォルトでは `UTF-8` となります。\n\n\n## [[yii\\validators\\FilterValidator|trim]] <span id=\"trim\"></span>\n\n```php\n[\n    // \"username\" と \"email\" の前後にあるホワイトスペースをトリムする\n    [['username', 'email'], 'trim'],\n]\n```\n\nこのバリデータはデータの検証を実行しません。その代りに、入力値の前後にあるホワイト・スペースをトリムします。\n入力値が配列であるときは、このバリデータによって無視されることに注意してください。\n\n\n## [[yii\\validators\\UniqueValidator|unique]] <span id=\"unique\"></span>\n\n```php\n[\n    // a1 の入力値が a1 のカラムにおいてユニークである必要がある\n    // すなわち、a1 = 1 は、\"a1\" カラムに 1 の値が存在しない場合に有効\n    ['a1', 'unique'],\n    // 以下と同義\n    ['a1', 'unique', 'targetAttribute' => 'a1'],\n    ['a1', 'unique', 'targetAttribute' => ['a1' => 'a1']],\n\n    // a1 の入力値がユニークである必要がある。ただし a2 のカラムが a1 の入力値のユニークネスのチェックに用いられる\n    // すなわち、a1 = 2 は、\"a2\" カラムに 2 の値が存在しない場合に有効\n    ['a1', 'unique', 'targetAttribute' => 'a2'],\n    // 以下と同義\n    ['a1', 'unique', 'targetAttribute' => ['a1' => 'a2']],\n\n    // a1 と a2 の両方がユニークである必要がある。両者がともにエラー・メッセージを受け取る\n    // すなわち、a1 = 3, a2 = 4 は、\"a1\" カラムに 3 の値が存在せず、同時に、\"a2\" カラムに 4 の値が存在しない場合に有効\n    [['a1', 'a2'], 'unique', 'targetAttribute' => ['a1', 'a2']],\n    // 以下と同義\n    [['a1', 'a2'], 'unique', 'targetAttribute' => ['a1' => 'a1', 'a2' => 'a2']],\n\n    // a1 と a2 の両方がユニークである必要がある。a1 のみがエラー・メッセージを受け取る\n    ['a1', 'unique', 'targetAttribute' => ['a1', 'a2']],\n\n    // a2 の値が a2 のカラム、a1 の値が a3 のカラムにおいてユニークである必要がある。a1 がエラー・メッセージを受け取る\n    // すなわち、a1 = 5, a2 = 6 は、\"a3\" カラムに 5 の値が存在せず、同時に、\"a2\" カラムに 6 の値が存在しない場合に有効\n    ['a1', 'unique', 'targetAttribute' => ['a2', 'a1' => 'a3']],\n    \n    // type_id が ProductType クラスで定義されているテーブルの \"id\" カラムにおいてユニークである必要がある\n    // すなわち、type_id = 1 は ProductType のテーブルの \"id\" カラムに 1 が存在しない場合に有効\n    ['type_id', 'unique', 'targetClass' => ProductType::class, 'targetAttribute' => 'id'],\n]\n```\n\nこのバリデータは、入力値がテーブルのカラムにおいてユニークであるかどうかをチェックします。このバリデータは [アクティブ・レコード](db-active-record.md) モデルの属性に対してのみ働きます。\n一つのカラムに対する検証か、複数のカラムに対する検証か、どちらかをサポートします。\n同時に複数のカラムをチェックするバリデーション（例えば上記の `['a1', 'a2']` ）が失敗したときに、\n`skipOnError` が `true` に設定されている場合は、先行するエラーが無い最初の属性のみが新しいエラー・メッセージを受け取ります。\n\n- `targetClass`: 検証される入力値を探すために使用される [アクティブ・レコード](db-active-record.md) クラスの名前。\n  設定されていない場合は、現在検証されているモデルのクラスが使用されます。\n- `targetAttribute`: `targetClass` において、入力値がユニークであることを検証するために使用される属性の名前。\n  設定されていない場合は、現在検証されている属性の名前が使用されます。\n  複数のカラムのユニーク性を同時に検証するために配列を使うことが出来ます。\n  配列の値はユニーク性を検証するのに使用される属性であり、配列のキーはその入力値が検証される属性です。\n  キーと値が同じ場合は、値だけを指定することが出来ます。\n  検証されるモデルが ModelA であり、検証に使用されるモデルが ModelB であるとすると、\n  下記のように `targetAttribute` を構成することが出来ます。\n    - `null` => ModelA の現在検証されている属性の値が ModelB の同名の属性の保存されている値に対してチェックされる\n    - `'a'` => ModelA の現在検証されている属性の値が ModelB の属性 \"a\" の保存されている値に対してチェックされる\n    - `['a']` => ModelA の属性 \"a\" の値が ModelB の属性 \"a\" の保存されている値に対してチェックされる\n    - `['a' => 'a']` => 同上\n    - `['a', 'b']` => ModelA の属性 \"a\" の値が ModelB の属性 \"a\" の保存されている値に対してチェックされ、\n    同時に、ModelA の属性 \"b\" の値が ModelB の属性 \"b\" の保存されている値に対してチェックされる\n    - `['a' => 'b']` => ModelA の属性 \"a\" の値が ModelB の属性 \"b\" の保存されている値に対してチェックされる\n- `filter`: 入力値がユニークであることをチェックするのに使用される DB クエリに適用される追加のフィルタ。\n  これには、追加のクエリ条件を表現する文字列または配列を使うことが出来ます (クエリ条件の書式については、[[yii\\db\\Query::where()]] を参照してください)。\n  これは、または、`function ($query)` というシグニチャを持つ無名関数でも構いません。\n  `$query` は関数の中で修正できる [[yii\\db\\Query|Query]] オブジェクトです。\n\n\n## [[yii\\validators\\UrlValidator|url]] <span id=\"url\"></span>\n\n```php\n[\n    // \"website\" が有効な URL であるかどうかをチェック。\n    // URI スキームを持たない場合は、\"website\" 属性に \"http://\" を前置する\n    ['website', 'url', 'defaultScheme' => 'http'],\n]\n```\n\nこのバリデータは、入力値が有効な URL であるかどうかをチェックします。\n\n- `validSchemes`: 有効と見なされるべき URI スキームを指定する配列。\n  デフォルト値は  `['http', 'https']` であり、`http` と `https` の URL がともに有効と見なされることを意味します。\n- `defaultScheme`: 入力値がスキームの部分を持たないときに前置されるデフォルトの URI スキーム。\n  デフォルト値は `null` であり、入力値を修正しないことを意味します。\n- `enableIDN`: バリデータが IDN (国際化ドメイン名) を考慮すべきか否か。デフォルト値は `false`。\n  IDN の検証を使用するためには、`intl` PHP 拡張をインストールして有効化する必要があることに注意してください。\n  そうしないと、例外が投げられます。\n\n> Note: このバリデータは URL スキームとホスト部分が正しいものであることを検証します。\n  URL の残りの部分はチェックしません。また、XSS や他の攻撃に対して防御するように設計されてもいません。\n  アプリケーション開発における脅威に対する防御について更に学習するために[セキュリティのベスト・プラクティス](security-best-practices.md) を参照して下さい。\n"
  },
  {
    "path": "docs/guide-ja/tutorial-docker.md",
    "content": "Yii と Docker\n=============\n\n開発および配備の際に Yii アプリケーションを Docker コンテナとして実行することが出来ます。コンテナは隔絶された軽量の仮想マシンのようなもので、そのサービスをホストのポートにマップします。例えば、コンテナ内の 80 番ポートにあるウェブ・サーバが(ローカル)ホストの 8888 番で利用できます。\n\nコンテナによって、開発用コンピュータと実運用サーバでソフトウェアのバージョンを全く同一にすること、迅速な配備、開発時におけるマルチ・サーバ・アーキテクチャのシミュレーションなど、数多くの問題を解決することが出来ます。\n\nDocker コンテナの詳細については、[docker.com](https://www.docker.com/why-docker) を参照して下さい。\n\n## 必要なもの\n\n- `docker`\n- `docker-compose`\n\n[ダウンロード・ページ](https://www.docker.com/products/container-runtime) で Docker のツールを取得して下さい。\n\n## インストール\n\nインストール後、`docker ps` を実行すると、以下と同様の出力が得られるはずです。\n\n```\nCONTAINER ID   IMAGE   COMMAND   CREATED   STATUS   PORTS\n```\n\nこれは Docker デーモンが起動して走っていることを意味します。\n\nさらに、`docker-compose version` を実行すると、出力は次のようになるはずです。\n\n```\ndocker-compose version 1.20.0, build unknown\ndocker-py version: 3.1.3\nCPython version: 3.6.4\nOpenSSL version: OpenSSL 1.1.0g  2 Nov 2017\n```\n\nCompose を使って、データベースやキャッシュなど、アプリケーションに必要な全てのサービスを設定して管理することが出来ます。\n\n## リソース\n\n- Yii のための PHP ベースのイメージが [yii2-docker](https://github.com/yiisoft/yii2-docker) にあります\n- [yii2-app-basic](https://github.com/yiisoft/yii2-app-basic#install-with-docker) のための Docker サポート\n- [yii2-app-advanced](https://github.com/yiisoft/yii2-app-advanced/pull/347) のための Docker サポートは開発中です\n\n## 使用方法\n\nDocker の基本的なコマンド:\n\n    docker-compose up -d\n    \nスタックにある全てのサービスをバックグラウンドで実行\n\n    docker-compose ps\n    \n実行中のサービスをリストアップ\n\n    docker-compose logs -f\n    \n全てのサービスのログを連続的に表示\n\n    docker-compose stop\n    \nスタックにある全てのサービスを穏やかに停止\n\n    docker-compose kill\n    \nスタックにある全てのサービスを即座に停止\n\n    docker-compose down -v\n    \n全てのサービスを停止して削除、**ホスト・ボリュームを使っていない場合のデータ損失に注意**\n\nコンテナの中でのコマンドの実行:\n\n    docker-compose run --rm php composer install\n    \n新しいコンテナの中で composer install を実行\n\n    docker-compose exec php bash\n    \n*実行中の* `php` サービスの中で bash を実行\n\n\n## 高度なトピック\n\n### Yii フレームワークのテスト\n\n[ここ](https://github.com/yiisoft/yii2/blob/master/tests/README.md#dockerized-testing) で説明されているように、Yii 自体に対する Docker を使ったフレームワーク・テストを実行することが出来ます。\n\n### データベース管理ツール\n\nMySQL を (`mysql`) として実行するときは、以下のようにして phpMyAdmin コンテナをスタックに追加することが出来ます。\n\n```\n    phpmyadmin:\n        image: phpmyadmin/phpmyadmin\n        ports:\n            - '8888:80'\n        environment:\n            - PMA_ARBITRARY=1\n            - PMA_HOST=mysql\n        depends_on:\n            - mysql\n```\n"
  },
  {
    "path": "docs/guide-ja/tutorial-i18n.md",
    "content": "国際化\n======\n\n国際化 (I18N) とは、工学的な変更を伴わずにさまざまな言語と地域に順応できるように、\nソフトウェア・アプリケーションを設計するプロセスを指します。\n潜在的なユーザが世界中にいるウェブ・アプリケーションにとっては、このことは特に重要な意味を持ちます。\nYii は、全ての領域にわたる国際化機能を提供し、メッセージの翻訳、ビューの翻訳、日付と数字の書式設定をサポートします。\n\n\n## ロケールと言語 <span id=\"locale-language\"></span>\n\n### ロケール\n\nロケールとは、ユーザの言語、国、そして、ユーザが彼らのユーザ・インタフェイスにおいて目にすることを期待する\nすべての変異形式を定義する一連のパラメータです。\nロケールは、通常、言語 ID と地域 ID から成るロケール ID によって定義されます。\n\n例えば、`en-US` という ID は、「英語とアメリカ合衆国」というロケールを意味します。\n\nYii アプリケーションで使用される全てのロケール ID は、一貫性のために、\n`ll-CC` の形式に正規化されなければなりません。\nここで `ll` は [ISO-639](https://www.loc.gov/standards/iso639-2/) に従った小文字二つまたは三つの言語コードであり、\n`CC` は [ISO-3166](https://en.wikipedia.org/wiki/ISO_3166-1#Current_codes) に従った二文字の国コードです。\nロケールに関する更なる詳細は [ICU プロジェクトのドキュメント](https://unicode-org.github.io/icu/userguide/locale/#the-locale-concept)\nに述べられています。\n\n### 言語\n\nYii では、\"言語\" という用語でロケールに言及することがしばしばあります。\n\nYii のアプリケーションでは二つの言語を使用します。すなわち、\n* [[yii\\base\\Application::$sourceLanguage|ソース言語]] : ソース・コード中のテキスト・メッセージが書かれている言語。\n* [[yii\\base\\Application::$language|ターゲット言語]] : コンテントをエンド・ユーザに表示するのに使用されるべき言語。\n\nいわゆるメッセージ翻訳サービスは、主として、テキスト・メッセージをソース言語からターゲット言語に翻訳するものです。\n\n### 構成\nアプリケーションの言語は、アプリケーションの構成情報で次のように構成することが出来ます。\n\n```php\nreturn [\n    // ターゲット言語を日本語に設定\n    'language' => 'ja-JP',\n    \n    // ソース言語を英語に設定\n    'sourceLanguage' => 'en-US',\n    \n    ......\n];\n```\n\n[[yii\\base\\Application::$sourceLanguage|ソース言語]] のデフォルト値は `en-US` であり、合衆国の英語を意味します。\nこのデフォルト値は変えないことが **推奨** されます。\nなぜなら、通常は、英語から他の言語への翻訳者を見つける方が、非英語から非英語への翻訳者を見つけるより、はるかに簡単だからです。\n\n[[yii\\base\\Application::$language|ターゲット言語]] は、エンド・ユーザの言語選択など、\nさまざまな要因に基づいて、動的に設定しなければならないことがよくあります。\nアプリケーションの構成情報で構成するかわりに、次の文を使ってターゲット言語を変更することが出来ます。\n\n```php\n// ターゲット言語を中国語に変更\n\\Yii::$app->language = 'zh-CN';\n```\n\n> Tip: ソース言語がコードの部分によって異なる場合は、メッセージ・ソースごとにソース言語をオーバーライドすることが出来ます。\n> これについては、次の説で説明します。\n\n## メッセージ翻訳 <span id=\"message-translation\"></span>\n\n### ソース言語からターゲット言語へ\nメッセージ翻訳サービスは、テキスト・メッセージをある言語 (通常は [[yii\\base\\Application::$sourceLanguage|ソース言語]])\nから別の言語 (通常は [[yii\\base\\Application::$language|ターゲット言語]]) に翻訳するものです。\n\n翻訳は、元のメッセージと翻訳されたメッセージを格納するメッセージ・ソースの中から、翻訳対象となったメッセージを探すことにより行われます。\nメッセージが見つかれば、対応する翻訳されたメッセージが返されます。メッセージが見つからなければ、元のメッセージが翻訳されずに返されます。\n\n### 実装の仕方\nメッセージ翻訳サービスを使用するためには、主として次の作業をする必要があります。\n\n1. 翻訳する必要のある全てのテキスト・メッセージを [[Yii::t()]] メソッドの呼び出しの中に包む。\n2. メッセージ翻訳サービスが翻訳されたメッセージを探すことが出来る一つまたは複数のメッセージ・ソースを構成する。\n3. 翻訳者にメッセージを翻訳させて、それをメッセージ・ソースに格納する。\n\n\n#### 1. テキスト・メッセージを包む\n[[Yii::t()]] メソッドは次のように使います。\n\n```php\necho \\Yii::t('app', 'This is a string to translate!');\n```\n\nここで、二番目のパラメータが翻訳されるべきテキスト・メッセージを示し、\n最初のパラメータはメッセージを分類するのに使用されるカテゴリ名を示します。\n\n#### 2. 一つまたは複数のメッセージ・ソースを構成する\n[[Yii::t()]] メソッドは `i18n` [アプリケーション・コンポーネント](structure-application-components.md) の `translate` メソッドを呼んで実際の翻訳作業を実行します。\nこのコンポーネントはアプリケーションの構成情報の中で次のようにして構成することが出来ます。\n\n```php\n'components' => [\n    // ...\n    'i18n' => [\n        'translations' => [\n            'app*' => [\n                'class' => 'yii\\i18n\\PhpMessageSource',\n                //'basePath' => '@app/messages',\n                //'sourceLanguage' => 'en-US',\n                'fileMap' => [\n                    'app' => 'app.php',\n                    'app/error' => 'error.php',\n                ],\n            ],\n        ],\n    ],\n],\n```\n\n上記のコードにおいては、[[yii\\i18n\\PhpMessageSource]] によってサポートされるメッセージ・ソースが構成されています。\n\n##### シンボル `*` によるカテゴリのワイルドカード\n\n`app*` は、`app` で始まる全てのメッセージ・カテゴリが\nこのメッセージ・ソースを使って翻訳されるべきであることを示しています。\n\n#### 3. 翻訳者にメッセージを翻訳させて、それをメッセージ・ソースに格納する\n\n[[yii\\i18n\\PhpMessageSource]] クラスは、単純な PHP 配列を持つ複数の PHP ファイルを使用してメッセージ翻訳を格納します。\nそれらのファイルが、「ソース言語」のメッセージと「ターゲット言語」の翻訳とのマップを含みます。\n\n> Info: それらのファイルを [`message` コマンド](#message-command) を使用して自動的に生成することが出来ます。\n> このセクションで後で紹介します。\n\nPHP ファイルは、それぞれ、一つのカテゴリのメッセージに対応します。\nデフォルトでは、ファイル名はカテゴリ名と同じでなければなりません。`app/messages/nl-NL/main.ph` の例を示します。\n\n```php\n<?php\n\n/**\n* Translation map for nl-NL\n*/\nreturn [\n    'welcome' => 'welkom'\n];\n\n```\n\n\n##### ファイルのマッピング\n\nただし、[[yii\\i18n\\PhpMessageSource::fileMap|fileMap]] を構成して、別の命名方法によってカテゴリを PHP ファイルにマップすることも可能です。\n\n上記の例では、(`ja-JP` がターゲット言語であると仮定すると) `app/error` のカテゴリは \n`@app/messages/ja-JP/error.php` という PHP ファイルにマップされます。\n`fileMap` を構成しなければ、このカテゴリは `@app/messages/ja-JP/app/error.php` にマップされることになります。\n\n#####  他のストレージ・タイプ\n\n翻訳メッセージを格納するのには、PHP ファイル以外に、次のメッセージ・ソースを\n使うことも可能です。\n\n- [[yii\\i18n\\GettextMessageSource]] - 翻訳メッセージを保持するのに GNU Gettext の MO ファイルまたは PO ファイルを使用する\n- [[yii\\i18n\\DbMessageSource]] - 翻訳メッセージを保存するのにデータベース・テーブルを使用する\n\n\n## メッセージのフォーマット <span id=\"message-formatting\"></span>\n\nメッセージを翻訳するときには、プレースホルダを埋め込んで、動的なパラメータ値で実行時に置き換えさせることが出来ます。\n更には、パラメータ値をターゲット言語に応じてフォーマットさせるための特別なプレースホルダの構文を使うことも出来ます。\nこの項では、メッセージをフォーマットする様々な方法を説明します。\n\n> Note: 以下においては、メッセージ・フォーマットの理解を助けるために、原文にはない日本語への翻訳例 (とその出力結果) をコード・サンプルに追加しています。\n\n### メッセージ・パラメータ <span id=\"message-parameters\"></span>\n\n翻訳対象となるメッセージには、一つまたは複数のパラメータ (プレースホルダとも呼びます) を埋め込んで、\n与えられたパラメータ値で置き換えられるようにすることが出来ます。\n様々なパラメータ値のセットを与えることによって、翻訳されるメッセージを動的に変化させることが出来ます。\n次の例では、`'Hello, {username}!'` というメッセージの中のプレースホルダ `{username}` が `'Alexander'` と `'Qiang'` にそれぞれ置き換えられます。\n\n```php\n$username = 'Alexander';\n// username が \"Alexander\" になった翻訳メッセージを表示\necho \\Yii::t('app', 'Hello, {username}!', [\n    'username' => $username,\n]);\n\n$username = 'Qiang';\n// username が \"Qiang\" になった翻訳メッセージを表示\necho \\Yii::t('app', 'Hello, {username}!', [\n    'username' => $username,\n]);\n```\n\nプレースホルダを持つメッセージを翻訳する時には、プレースホルダはそのままにしておかなければなりません。\nこれは、プレースホルダは `Yii::t()` を呼んでメッセージを翻訳する時に、実際の値に置き換えられるものだからです。\n\n```php\n// 日本語翻訳: '{username} さん、こんにちは!'\n```\n\nプレースホルダには、*名前付きプレースホルダ* と *序数プレースホルダ* のどちらかを使用する事が出来ます。ただし、一つのメッセージに両方を使うことは出来ません。\n\n上記の例は名前付きプレースホルダの使い方を示すものです。\nすなわち、各プレースホルダは `{name}` という形式で書かれていますが、それに対して、キーが(波括弧なしの)プレースホルダ名であり、\n値がそのプレースホルダを置き換える値である連想配列を渡す訳です。\n\n序数プレースホルダは、0 ベースの整数の序数をプレースホルダ名として使います。\nこのプレースホルダは、`Yii::t()` の呼び出しに出現する順序に従って、パラメータ値によって置き換えられます。\n次の例では、序数プレースホルダ `{0}`、`{1}` および `{2}` は、それぞれ、`$price`、`$count` および `$subtotal` の値によって置き換えられます。\n\n```php\n$price = 100;\n$count = 2;\n$subtotal = 200;\necho \\Yii::t('app', 'Price: {0}, Count: {1}, Subtotal: {2}', [$price, $count, $subtotal]);\n```\n\n```php\n// 日本語翻訳: '価格: {0}, 数量: {1}, 小計: {2}'\n```\n\n序数プレースホルダが一つだけの場合は、値を配列に入れずにそのまま指定することができます。\n\n```php\necho \\Yii::t('app', 'Price: {0}', $price);\n```\n\n> Tip: たいていの場合は名前付きプレースホルダを使うべきです。\n> と言うのは、翻訳者にとっては、パラメータ名がある方が、翻訳すべきメッセージ全体をより良く理解できるからです。\n\n\n### パラメータのフォーマット <span id=\"parameter-formatting\"></span>\n\nメッセージのプレースホルダにフォーマットの規則を追加して指定し、\nパラメータ値がプレースホルダを置き換える前に適切にフォーマットされるようにすることが出来ます。\n次の例では、`price` のパラメータ値の型は数値として扱われ、通貨の形式でフォーマットされます。\n\n```php\n$price = 100;\necho \\Yii::t('app', 'Price: {0,number,currency}', $price);\n```\n\n> Note: パラメータのフォーマットには、[intl PHP 拡張](https://www.php.net/manual/ja/intro.intl.php) のインストールが必要です。\n\nプレースホルダにフォーマット規則を指定するためには、短い構文または完全な構文のどちらかを使うことが出来ます。\n\n```\n短い形式: {name,type}\n完全な形式: {name,type,style}\n```\n\n> Note: `{`、`}`、`'`、`#` などの特殊な文字を使用する必要がある場合は、その部分の文字列を `'` で囲んでください。\n> \n```php\necho Yii::t('app', \"Example of string with ''-escaped characters'': '{' '}' '{test}' {count,plural,other{''count'' value is # '#{}'}}\", ['count' => 3]);\n+```\n\nこのようなプレースホルダを指定する方法についての完全な説明は、[ICU ドキュメント](https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classMessageFormat.html)を参照してください。以下では、よくある使用方法をいくつか示します。\n\n\n#### 数値 <span id=\"number\"></span>\n\n```php\n$sum = 12345;\necho \\Yii::t('app', 'Balance: {0,number}', $sum);\n\n// 日本語翻訳: '差引残高: {0,number}'\n// 日本語出力: '差引残高: 12,345'\n```\n\nオプションのパラメータとして、`integer`、`currency`、`percent` のスタイルを指定することが出来ます。\n\n```php\n$sum = 12345;\necho \\Yii::t('app', 'Balance: {0,number,currency}', $sum);\n\n// 日本語翻訳: '差引残高: {0,number,currency}'\n// 日本語出力: '差引残高: ￥12,345'\n```\n\nまたは、数値をフォーマットするカスタム・パターンを指定することも出来ます。\n\n```php\n$sum = 12345;\necho \\Yii::t('app', 'Balance: {0,number,,000,000000}', $sum);\n\n// 日本語翻訳: '差引残高: {0,number,,000,000000}'\n// 日本語出力: '差引残高: 000,012345'\n```\n\nカスタムフォーマットで使用される文字については、\n[ICU API リファレンス](https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classDecimalFormat.html) の \"Special Pattern Characters\"\nのセクションに記述されています。\n \n数値は常に翻訳先のロケールに従ってフォーマットされます。\nつまり、ロケールを変更せずに、小数点や桁区切りを変更することは出来ません。\nそれらをカスタマイズしたい場合は [[yii\\i18n\\Formatter::asDecimal()]] や [[yii\\i18n\\Formatter::asCurrency()]] を使うことが出来ます。\n\n#### 日付 <span id=\"date\"></span>\n\nパラメータ値は日付としてフォーマットされます。例えば、\n\n```php\necho \\Yii::t('app', 'Today is {0,date}', time());\n\n// 日本語翻訳: '今日は {0,date} です。'\n// 日本語出力: '今日は 2015/01/07 です。'\n```\n\nオプションのパラメータとして、`short`、`medium`、`long`、そして `full` のスタイルを指定することが出来ます。\n\n```php\necho \\Yii::t('app', 'Today is {0,date,short}', time());\n\n// 日本語翻訳: '今日は {0,date,short} です。'\n// 日本語出力: '今日は 2015/01/07 です。'\n```\n\n日付の値をフォーマットするカスタム・パターンを指定することも出来ます。\n\n```php\necho \\Yii::t('app', 'Today is {0,date,yyyy-MM-dd}', time());\n\n// 日本語翻訳: '今日は {0,date,yyyy-MM-dd} です。'\n// 日本語出力: '今日は 2015-01-07 です。'\n```\n\n[書式のリファレンス](https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1SimpleDateFormat.html#details).\n\n#### 時刻 <span id=\"time\"></span>\n\nパラメータ値は時刻としてフォーマットされます。例えば、\n\n```php\necho \\Yii::t('app', 'It is {0,time}', time());\n\n// 日本語翻訳: '現在 {0,time} です。'\n// 日本語出力: '現在 22:37:47 です。'\n```\n\nオプションのパラメータとして、`short`、`medium`、`long`、そして `full` のスタイルを指定することが出来ます。\n\n```php\necho \\Yii::t('app', 'It is {0,time,short}', time());\n\n// 日本語翻訳: '現在 {0,time,short} です。'\n// 日本語出力: '現在 22:37 です。'\n```\n\n時刻の値をフォーマットするカスタム・パターンを指定することも出来ます。\n\n```php\necho \\Yii::t('app', 'It is {0,date,HH:mm}', time());\n\n// 日本語翻訳: '現在 {0,time,HH:mm} です。'\n// 日本語出力: '現在 22:37 です。'\n```\n\n[書式のリファレンス](https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1SimpleDateFormat.html#details).\n\n\n#### 綴り <span id=\"spellout\"></span>\n\nパラメータ値は数値として取り扱われ、綴りとしてフォーマットされます。例えば、\n\n```php\n// 出力例 : \"42 is spelled as forty-two\"\necho \\Yii::t('app', '{n,number} is spelled as {n,spellout}', ['n' => 42]);\n\n// 日本語翻訳: '{n,number} は、文字で綴ると {n,spellout} です。'\n// 日本語出力: '42 は、文字で綴ると 四十二 です。'\n```\n\nデフォルトでは、数値は基数として綴られます。それを変更することは可能です。\n\n```php\n// 出力例 : \"I am forty-seventh agent\"\necho \\Yii::t('app', 'I am {n,spellout,%spellout-ordinal} agent', ['n' => 47]);\n\n// 日本語翻訳: '私は{n,spellout,%spellout-ordinal}の工作員です。'\n// 日本語出力: '私は第四十七の工作員です。'\n```\n\n'spellout,' と '%' の間に空白を入れてはならないことに注意してください。\n\nあなたが使用しているロケールで利用可能なオプションのリストについては、\n[https://intl.rmcreative.ru/](https://intl.rmcreative.ru/) の \"Numbering schemas, Spellout\" を参照してください。\n\n\n#### 序数 <span id=\"ordinal\"></span>\n\nパラメータ値は数値として取り扱われ、順序を表す文字列としてフォーマットされます。例えば、\n\n```php\n// 出力: \"You are the 42nd visitor here!\"\necho \\Yii::t('app', 'You are the {n,ordinal} visitor here!', ['n' => 42]);\n```\n\n序数については、スペイン語などの言語では、さらに多くのフォーマットがサポートされています。\n\n```php\n// 出力: \"471ª\"\necho \\Yii::t('app', '{n,ordinal,%digits-ordinal-feminine}', ['n' => 471]);\n```\n\n'ordinal,' と '%' の間に空白を入れてはならないことに注意してください。\n\nあなたが使用しているロケールで利用可能なオプションのリストについては、\n[https://intl.rmcreative.ru/](https://intl.rmcreative.ru/) の \"Numbering schemas, Ordinal\" を参照してください。\n\n> Note: 上記のソース・メッセージを、プレースホルダのスタイルを守って日本語に翻訳すると、'あなたはこのサイトの{n,ordinal}の訪問者です' となります。\n> しかし、その出力結果は、'あなたはこのサイトの第42の訪問者です' となり、意味は通じますが、日本語としては若干不自然なものになります。\n>\n> プレースホルダのスタイル自体も、翻訳の対象として、より適切なものに変更することが可能であることに注意してください。\n>\n> この場合も、'あなたはこのサイトの{n,plural,=1{最初} other{#番目}}の訪問者です' のように翻訳するほうが適切でしょう。\n\n#### 継続時間 <span id=\"duration\"></span>\n\nパラメータ値は秒数として取り扱われ、継続時間を表す文字列としてフォーマットされます。例えば、\n\n```php\n// 出力: \"You are here for 47 sec. already!\"\necho \\Yii::t('app', 'You are here for {n,duration} already!', ['n' => 47]);\n```\n\n継続時間については、さらに多くのフォーマットがサポートされています。\n\n```php\n// 出力: '130:53:47'\necho \\Yii::t('app', '{n,duration,%in-numerals}', ['n' => 471227]);\n```\n\n'duration,' と '%' の間に空白を入れてはならないことに注意してください。\n\nあなたが使用しているロケールで利用可能なオプションのリストについては、\n[https://intl.rmcreative.ru/](https://intl.rmcreative.ru/) の \"Numbering schemas, Duration\" を参照してください。\n\n> Note: このソース・メッセージを 'あなたはこのサイトに既に{n,duration}の間滞在しています' と翻訳した場合の出力結果は、'あなたはこのサイトに既に47の間滞在しています' となります。\n> これも、プレースホルダのスタイルも含めて全体を翻訳し直す方が良いでしょう。\n> どうも、ICU ライブラリは、ja_JP の数値関連の書式指定においては、割と貧弱な実装にとどまっている印象です。\n\n\n#### 複数形 <span id=\"plural\"></span>\n\n言語によって、複数形の語形変化はさまざまに異なります。Yii は、さまざまな形式の複数形語形変化に対応したメッセージ翻訳のための便利な方法を提供しています。\nそれは、非常に複雑な規則に対しても、十分に機能するものです。\n語形変化の規則を直接に処理する代りに、特定の状況における語形変化した言葉の翻訳を提供するだけで十分です。\n\n```php\n// $n = 0 の場合の出力: \"There are no cats!\"\n// $n = 1 の場合の出力: \"There is one cat!\"\n// $n = 42 の場合の出: \"There are 42 cats!\"\necho \\Yii::t('app', 'There {n,plural,=0{are no cats} =1{is one cat} other{are # cats}}!', ['n' => $n]);\n```\n\n上記の複数形規則の引数において、`=` はぴったりその値であることを意味します。従って、`=0` はぴったりゼロ、`=1` はぴったり 1 を表します。\n`other` はそれ以外の数を表します。`#` は ターゲット言語に従ってフォーマットされた `n` の値によって置き換えられます。\n\n複数形の規則が非常に複雑な言語もあります。\n例えば、次のロシア語の例では、`=1` が `n = 1` にぴったりと一致するのに対して、`one` が `21` や `101` などに一致します。\n\n```\nЗдесь {n,plural,=0{котов нет} =1{есть один кот} one{# кот} few{# кота} many{# котов} other{# кота}}!\n```\n\nこれら `other`、`few`、`many` などの特別な引数の名前は言語によって異なります。\n特定のロケールに対してどんな引数を指定すべきかを学ぶためには、[https://intl.rmcreative.ru/](https://intl.rmcreative.ru/) の \"Plural Rules, Cardinal\" を参照してください。\nあるいは、その代りに、[unicode.org の規則のリファレンス](https://cldr.unicode.org/index/cldr-spec/plural-rules) を参照することも出来ます。\n\n> Note: 上記のロシア語のメッセージのサンプルは、主として翻訳メッセージとして使用されるものです。\n> アプリケーションの [[yii\\base\\Application::$sourceLanguage|ソース言語]] を `ru-RU` にしてロシア語から他の言語に翻訳するという設定にしない限り、オリジナルのメッセージとしては使用されることはありません。\n>\n> `Yii::t()` の呼び出しにおいて、オリジナルのメッセージに対する翻訳が見つからない場合は、\n> [[yii\\base\\Application::$sourceLanguage|ソース言語]] の複数形規則がオリジナルのメッセージに対して適用されます。\n\n文字列が以下のようなものである場合のために `offset` というパラメータがあります。\n \n```php\n$likeCount = 2;\necho Yii::t('app', 'You {likeCount,plural,\n    offset: 1\n    =0{did not like this}\n    =1{liked this}\n    one{and one other person liked this}\n    other{and # others liked this}\n}', [\n    'likeCount' => $likeCount\n]);\n\n// 出力: 'You and one other person liked this'\n```\n\n> Note: 上記のソース・メッセージの日本語翻訳は以下のようなものになります。\n>\n> '猫は{n, plural, =0{いません} other{#匹います}}。'\n>\n> 日本語では単数形と複数形を区別しませんので、たいていの場合、`=0` と `other` を指定するだけで事足ります。\n> 横着をして、`{n, plural, ...}` を `{n, number}` に置き換えても、多分、大きな問題は生じないでしょう。\n\n\n#### 序数選択肢 <span id=\"ordinal-selection\"></span>\n\nパラメータのタイプとして `selectordinal` を使うと、翻訳先ロケールの言語規則に基づいて序数のための文字列を選択することが出来ます。\n\n```php\n$n = 3;\necho Yii::t('app', 'You are the {n,selectordinal,one{#st} two{#nd} few{#rd} other{#th}} visitor', ['n' => $n]);\n// 英語の出力\n// You are the 3rd visitor\n\n// ロシア語の翻訳\n'You are the {n,selectordinal,one{#st} two{#nd} few{#rd} other{#th}} visitor' => 'Вы {n, selectordinal, other{#-й}} посетитель',\n\n// ロシア語の出力\n// Вы 3-й посетитель\n```\n\nフォーマットは複数形で使われるものと非常に近いものです。\n特定のロケールに対してどんな引数を指定すべきかを学ぶためには、[https://intl.rmcreative.ru/](https://intl.rmcreative.ru/) の \"Plural Rules, Ordinal\" を参照してください。\nあるいは、その代りに、[unicode.org の規則のリファレンス](https://unicode-org.github.io/cldr-staging/charts/37/supplemental/language_plural_rules.html) を参照することも出来ます。\n\n\n#### 選択肢 <span id=\"selection\"></span>\n\nパラメータのタイプとして `select` を使うと、パラメータの値に基づいて表現を選択することが出来ます。例えば、\n\n```php\n// 出力: \"Snoopy is a dog and it loves Yii!\"\necho \\Yii::t('app', '{name} is a {gender} and {gender,select,female{she} male{he} other{it}} loves Yii!', [\n    'name' => 'Snoopy',\n    'gender' => 'dog',\n]);\n```\n\n上記の式の中で、`female` と `male` が `gender` が取り得る値であり、`other` がそれらに一致しない値を処理します。\nそれぞれの取り得る値の後には、波括弧で囲んで対応する表現を指定します。\n\n> Note: 日本語翻訳: '{name} は {gender} であり、{gender,select,female{彼女} male{彼} other{それ}}は Yii を愛しています。'\n>\n> 日本語出力: 'Snoopy は dog であり、それは Yii を愛しています。'\n\n### デフォルトのメッセージ・ソースを指定する <span id=\"default-message-source\"></span>\n\n構成されたカテゴリのどれにもマッチしないカテゴリのためのフォールバックとして使用される、デフォルトのメッセージ・ソースを指定することが出来ます。\nこれは、ワイルドカードのカテゴリ `*` を構成することによって可能になります。\nそうするためには、アプリケーションの構成情報に次のように追加します。\n\n```php\n// i18n コンポーネントを構成する\n\n'i18n' => [\n    'translations' => [\n        '*' => [\n            'class' => 'yii\\i18n\\PhpMessageSource'\n        ],\n    ],\n],\n```\n\nこうすることで、個別に構成することなくカテゴリを使うことが可能になり、Yii 1.1 の振る舞いと同じになります。\nカテゴリのメッセージは、デフォルトの翻訳の `basePath` すなわち `@app/messages` の下にあるファイルから読み込まれます。\n\n```php\necho Yii::t('not_specified_category', 'message from unspecified category');\n```\n\nこの場合、メッセージは `@app/messages/<LanguageCode>/not_specified_category.php` から読み込まれます。\n\n### モジュールのメッセージを翻訳する <span id=\"module-translation\"></span>\n\nモジュール用のメッセージを翻訳したいけれども、全てのメッセージに対して一つの翻訳ファイルを使うことは避けたい、という場合には、次のようにすることが出来ます。\n\n```php\n<?php\n\nnamespace app\\modules\\users;\n\nuse Yii;\n\nclass Module extends \\yii\\base\\Module\n{\n    public $controllerNamespace = 'app\\modules\\users\\controllers';\n\n    public function init()\n    {\n        parent::init();\n        $this->registerTranslations();\n    }\n\n    public function registerTranslations()\n    {\n        Yii::$app->i18n->translations['modules/users/*'] = [\n            'class' => 'yii\\i18n\\PhpMessageSource',\n            'sourceLanguage' => 'en-US',\n            'basePath' => '@app/modules/users/messages',\n            'fileMap' => [\n                'modules/users/validation' => 'validation.php',\n                'modules/users/form' => 'form.php',\n                ...\n            ],\n        ];\n    }\n\n    public static function t($category, $message, $params = [], $language = null)\n    {\n        return Yii::t('modules/users/' . $category, $message, $params, $language);\n    }\n\n}\n```\n\n上記の例では、マッチングのためにワイルドカードを使い、次に必要なファイルごとに各カテゴリをフィルタリングしています。\n`fileMap` を使わずに、カテゴリを同じ名前のファイルにマップする規約を使って済ませることも出来ます。\n以上のようにすれば、直接に `Module::t('validation', 'your custom validation message')` や `Module::t('form', 'some form label')` などを使用することが出来ます。\n\n### ウィジェットのメッセージを翻訳する <span id=\"widget-translation\"></span>\n\nモジュールに適用できる同じ規則をウィジェットにも適用することが出来ます。例えば、\n\n```php\n<?php\n\nnamespace app\\widgets\\menu;\n\nuse yii\\base\\Widget;\nuse Yii;\n\nclass Menu extends Widget\n{\n\n    public function init()\n    {\n        parent::init();\n        $this->registerTranslations();\n    }\n\n    public function registerTranslations()\n    {\n        $i18n = Yii::$app->i18n;\n        $i18n->translations['widgets/menu/*'] = [\n            'class' => 'yii\\i18n\\PhpMessageSource',\n            'sourceLanguage' => 'en-US',\n            'basePath' => '@app/widgets/menu/messages',\n            'fileMap' => [\n                'widgets/menu/messages' => 'messages.php',\n            ],\n        ];\n    }\n\n    public function run()\n    {\n        echo $this->render('index');\n    }\n\n    public static function t($category, $message, $params = [], $language = null)\n    {\n        return Yii::t('widgets/menu/' . $category, $message, $params, $language);\n    }\n\n}\n```\n\n`fileMap` を使わずに、カテゴリを同じ名前のファイルにマップする規約を使って済ませることも出来ます。\nこれで、直接に `Menu::t('messages', 'new messages {messages}', ['{messages}' => 10])` を使用することが出来ます。\n\n> Note: ウィジェットのためには i18n ビューも使うことが出来ます。コントローラのための同じ規則がウィジェットにも適用されます。\n\n\n### フレームワーク・メッセージを翻訳する <span id=\"framework-translation\"></span>\n\nYii には、検証エラーとその他いくつかの文字列に対するデフォルトの翻訳メッセージが付属しています。これらのメッセージは、全て 'yii' というカテゴリの中にあります。\n場合によっては、あなたのアプリケーションのために、デフォルトのフレームワーク・メッセージの翻訳を修正したいことがあるでしょう。\nそうするためには、`i18n` [アプリケーション・コンポーネント](structure-application-components.md) を以下のように構成してください。\n\n```php\n'i18n' => [\n    'translations' => [\n        'yii' => [\n            'class' => 'yii\\i18n\\PhpMessageSource',\n            'sourceLanguage' => 'en-US',\n            'basePath' => '@app/messages'\n        ],\n    ],\n],\n```\n\nこれで、あなたの修正した翻訳を `@app/messages/<language>/yii.php` に置くことが出来ます。\n\n### 欠落している翻訳の処理 <span id=\"missing-translations\"></span>\n\nソースに翻訳が欠落している場合でも、Yii はリクエストされたメッセージの内容を表示します。\nこの振舞いは、原文のメッセージが正当かつ詳細なテキストである場合には、非常に好都合です。しかし、場合によっては、それだけでは十分ではありません。\nリクエストされた翻訳がソースに欠落しているときに、何らかの特別な処理を実行する必要がある場合もあります。\nそういう処理は、[[yii\\i18n\\MessageSource]] の [[yii\\i18n\\MessageSource::EVENT_MISSING_TRANSLATION|missingTranslation]] イベントを使うことによって達成できます。\n\n例えば、全ての欠落している翻訳に何か目立つマークを付けて、ページに表示されたときに簡単に見つけられるようにしましょう。\n最初にイベント・ハンドラをセットアップする必要があります。これはアプリケーションの構成によって行うことが出来ます。\n\n```php\n'components' => [\n    // ...\n    'i18n' => [\n        'translations' => [\n            'app*' => [\n                'class' => 'yii\\i18n\\PhpMessageSource',\n                'fileMap' => [\n                    'app' => 'app.php',\n                    'app/error' => 'error.php',\n                ],\n                'on missingTranslation' => ['app\\components\\TranslationEventHandler', 'handleMissingTranslation']\n            ],\n        ],\n    ],\n],\n```\n\n次に、私たち独自のイベント・ハンドラを実装する必要があります。\n\n```php\n<?php\n\nnamespace app\\components;\n\nuse yii\\i18n\\MissingTranslationEvent;\n\nclass TranslationEventHandler\n{\n    public static function handleMissingTranslation(MissingTranslationEvent $event)\n    {\n        $event->translatedMessage = \"@MISSING: {$event->category}.{$event->message} FOR LANGUAGE {$event->language} @\";\n    }\n}\n```\n\nこのイベント・ハンドラによって [[yii\\i18n\\MissingTranslationEvent::translatedMessage]] がセットされた場合は、それが翻訳結果として表示されます。\n\n> Note: 全てのメッセージ・ソースは、欠落した翻訳をそれぞれ独自に処理します。\n> いくつかのメッセージ・ソースを使っていて、それらが同じ方法で欠落した翻訳を取り扱うようにしたい場合は、対応するイベント・ハンドラを全てのメッセージ・ソースそれぞれに割り当てなければなりません。\n\n### `message` コマンドを使う <span id=\"message-command\"></span>\n\n翻訳は [[yii\\i18n\\PhpMessageSource|php ファイル]]、[[yii\\i18n\\GettextMessageSource|.po ファイル]]、または [[yii\\i18n\\DbMessageSource|database]] に保存することが出来ます。追加のオプションについてはそれぞれのクラスを参照してください。\n\n最初に、構成情報ファイルを作成する必要があります。\nどこに保存したいかを決めて、次のコマンドを発行してください。\n\n```bash\n./yii message/config-template path/to/config.php\n```\n\n作成されたファイルを開いて、あなたの要求に合わせてパラメータを修正します。特に、下記の項目に注意を払ってください。\n\n* `languages`: あなたのアプリケーションが翻訳されるべき言語を表す配列。\n* `messagePath`: メッセージファイルを保存するパス。これは、アプリケーションの構成情報で記述されている `i18n` の `basePath` と合致しなければなりません。\n\n'./yii message/config' コマンドを使って、CLI 経由で、指定したオプションを持つ設定ファイルを動的に生成することも可能です。\n例えば、`languages` と `messagePath` のパラメータは、次のようにして設定することが出来ます。\n\n```bash\n./yii message/config --languages=de,ja --messagePath=messages path/to/config.php\n```\n\n利用可能なオプションのリストを取得するためには、次のコマンドを実行します。\n\n```bash\n./yii help message/config\n```\n\n構成情報ファイルの編集が完了すれば、ついに、下記のコマンドを使ってメッセージを抽出することが出来ます。\n\n```bash\n./yii message path/to/config.php\n```\n\nまた、オプションを指定して、抽出のパラメータを動的に変更することも出来ます。\n\nこれで、(あなたがファイル・ベースの翻訳を選択していた場合は) `messagePath` ディレクトリにファイルが出現します。\n\n\n## ビューの翻訳 <span id=\"view-translation\"></span>\n\n個々のテキスト・メッセージを翻訳する代りに、ビュー・スクリプト全体を翻訳したい場合があるでしょう。\nこの目的を達するためには、ビューを翻訳して、ターゲット言語と同じ名前のサブ・ディレクトリに保存するだけで大丈夫です。\n例えば、`views/site/index.php` というビューをターゲット言語 `ru-RU` に翻訳したい場合は、翻訳したビューを `views/site/ru-RU/index.php` というファイルとして保存します。\nこのようにすると、[[yii\\base\\View::renderFile()]] メソッド、または、このメソッドを呼び出す他のメソッド\n(例えば [[yii\\base\\Controller::render()]]) を呼んで `views/site/index.php` をレンダリングするたびに、\n翻訳された `views/site/ru-RU/index.php` が代りにレンダリングされるようになります。\n\n> Note: [[yii\\base\\Application::$language|ターゲット言語]] が [[yii\\base\\Application::$sourceLanguage|ソース言語]] と同じ場合は、\n> 翻訳されたビューの有無にかかわらず、オリジナルのビューがレンダリングされます。\n\n\n## 数値と日付の値を書式設定する <span id=\"date-number\"></span>\n\n詳細は [データのフォーマット](output-formatting.md) のセクションを参照して下さい。\n\n\n## PHP 環境をセットアップする <span id=\"setup-environment\"></span>\n\nYii は、[[yii\\i18n\\Formatter]] クラスの数値や日付の書式設定や、[[yii\\i18n\\MessageFormatter]] を使うメッセージのフォーマッティングなど、ほとんどの国際化機能を提供するために [PHP intl 拡張](https://www.php.net/manual/ja/book.intl.php) を使います。\nこの二つのクラスは、`intl` がインストールされていない場合に備えて基本的な機能を提供するフォールバックを実装しています。\nだだし、このフォールバックの実装は、英語がターゲット言語である場合にのみ十分に機能するものす。\n従って、国際化機能が必要とされる場合は、`intl` をインストールすることが強く推奨されます。\n\n[PHP intl 拡張](https://www.php.net/manual/ja/book.intl.php) は、さまざまに異なる全てのロケールについて知識と書式の規則を提供する\n[ICU ライブラリ](https://icu.unicode.org/) に基礎を置いています。\nICU のバージョンが異なると、日付や数値のフォーマットの結果も異なる場合があります。\nあなたのウェブ・サイトが全ての環境で同じ出力をすることを保証するためには、\n全ての環境において同じバージョンの PHP intl 拡張 (従って同じバージョンの ICU) をインストールすることが推奨されます。\n\nどのバージョンの ICU が PHP によって使われているかを知るために、次のスクリプトを走らせることが出来ます。このスクリプトは、使用されている PHP と ICU のバージョンを出力します。\n\n```php\n<?php\necho \"PHP: \" . PHP_VERSION . \"\\n\";\necho \"ICU: \" . INTL_ICU_VERSION . \"\\n\";\necho \"ICU Data: \" . INTL_ICU_DATA_VERSION . \"\\n\";\n```\n\nさらに、バージョン 49 以上の ICU を使用する事も推奨されます。そうすることによって、このドキュメントで説明されている全ての機能を使うことが出来るようになります。\n例えば、49 未満の ICU は、複数形規則における `#` プレースホルダをサポートしていません。\n入手できる ICU バージョン については、<https://icu.unicode.org/download> を参照してください。\nバージョン番号の採番方式が 4.8 リリースの後に変更されたことに注意してください (すなわち、ICU 4.8、ICU 49、ICU 50、等々となっています)。\n\nこれに加えて、ICU ライブラリとともに出荷されるタイム・ゾーン・データベースの情報も古くなっている可能性があります。\nタイム・ゾーン・データベースの更新に関する詳細は [ICU マニュアル](https://unicode-org.github.io/icu/userguide/datetime/timezone/#updating-the-time-zone-data) を参照してください。\n出力の書式設定には ICU タイム・ゾーン・データベースが使用されますが、PHP によって使われるタイム・ゾーン・データベースも影響する可能性があります。\nPHP のタイム・ゾーン・データベースは、[`timezonedb` pecl パッケージ](https://pecl.php.net/package/timezonedb) の最新版をインストールすることによって更新することが出来ます。\n"
  },
  {
    "path": "docs/guide-ja/tutorial-mailing.md",
    "content": "メール送信\n==========\n\n> Note: このセクションはまだ執筆中です。\n\nYii は電子メールの作成と送信をサポートしています。\nただし、フレームワークのコアが提供するのは、コンテント作成の機能と基本的なインタフェイスだけです。\n実際のメール送信メカニズムはエクステンションによって提供されなければなりません。\nと言うのは、メール送信はプロジェクトが異なるごとに異なる実装が必要とされるでしょうし、通常、外部のサービスやライブラリに依存するものだからです。\n\nごく一般的な場合であれば、yii2-symfonymailer](https://www.yiiframework.com/extension/yiisoft/yii2-symfonymailer) 公式エクステンションを使用することが出来ます。\n\n\n構成\n----\n\nメール・コンポーネントの構成は、あなたが選んだエクステンションに依存します。\n一般的には、アプリケーションの構成情報は次のようなものになる筈です。\n\n```php\nreturn [\n    //....\n    'components' => [\n        'mailer' => [\n            'class' => 'yii\\symfonymailer\\Mailer',\n            'useFileTransport' => false,\n            'transport' => [\n                'dsn' => 'smtp://user:pass@smtp.example.com:465',\n            ],\n        ],\n    ],\n];\n```\n\n\n基本的な使用方法\n----------------\n\nいったん `mailer` コンポーネントを構成すれば、次のコードを使って電子メールのメッセージを送信することが出来るようになります。\n\n```php\nYii::$app->mailer->compose()\n    ->setFrom('from@domain.com')\n    ->setTo('to@domain.com')\n    ->setSubject('メッセージの題')\n    ->setTextBody('プレーンテキストのコンテント')\n    ->setHtmlBody('<b>HTML のコンテント</b>')\n    ->send();\n```\n\n上の例では、`compose()` メソッドでメール・メッセージのインスタンスを作成し、それに値を投入して送信しています。\n必要であれば、このプロセスにもっと複雑なロジックを置くことも可能です。\n\n```php\n$message = Yii::$app->mailer->compose();\nif (Yii::$app->user->isGuest) {\n    $message->setFrom('from@domain.com');\n} else {\n    $message->setFrom(Yii::$app->user->identity->email);\n}\n$message->setTo(Yii::$app->params['adminEmail'])\n    ->setSubject('メッセージの題')\n    ->setTextBody('プレーン・テキストのコンテント')\n    ->send();\n```\n\n> Note: すべての `mailer` エクステンションは、二つの主要なクラス、すなわち、`Mailer` と `Message` のセットとして提供されます。\n  `Mailer` は常に `Message` のクラス名と仕様を知っています。\n  `Message` オブジェクトのインスタンスを直接に作成しようとしてはいけません。常に `compose()` メソッドを使って作成してください。\n\nいくつかのメッセージを一度に送信することも出来ます。\n\n```php\n$messages = [];\nforeach ($users as $user) {\n    $messages[] = Yii::$app->mailer->compose()\n        // ...\n        ->setTo($user->email);\n}\nYii::$app->mailer->sendMultiple($messages);\n```\n\nメール・エクステンションの中には、単一のネットワーク・メッセージを使うなどして、この手法の恩恵を享受することが出来るものもいくつかあるでしょう。\n\n\nメールのコンテントを作成する\n----------------------------\n\nYii は実際のメール・メッセージを特別なビュー・ファイルによって作成することを許容しています。\nデフォルトでは、それらのファイルは `@app/mail` というパスに配置されなければなりません。\n\n以下はメール・ビュー・ファイルの内容の例です。\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\helpers\\Url;\n\n/**\n * @var \\yii\\web\\View $this ビュー・コンポーネントのインスタンス\n * @var \\yii\\mail\\BaseMessage $message 新しく作成されたメール・メッセージのインスタンス\n */\n\n?>\n<h2>ワン・クリックで私たちのサイトのホームページを訪問することが出来ます</h2>\n<?= Html::a('ホームページへ', Url::home('http')) ?>\n```\n\nビュー・ファイルによってメッセージを作成するためには、単に `compose()` メソッドにビューの名前を渡すだけで十分です。\n\n```php\nYii::$app->mailer->compose('home-link') // ここでビューのレンダリング結果がメッセージのボディになります\n    ->setFrom('from@domain.com')\n    ->setTo('to@domain.com')\n    ->setSubject('メッセージの題')\n    ->send();\n```\n\nビュー・ファイルの中で利用できる追加のビュー・パラメータを `compose()` メソッドに渡すことができます。\n\n```php\nYii::$app->mailer->compose('greetings', [\n    'user' => Yii::$app->user->identity,\n    'advertisement' => $adContent,\n]);\n```\n\nHTML と平文テキストのメッセージ・コンテントに違うビューを指定することが出来ます。\n\n```php\nYii::$app->mailer->compose([\n    'html' => 'contact-html',\n    'text' => 'contact-text',\n]);\n```\n\nビュー名をスカラーの文字列として渡した場合は、そのレンダリング結果は HTML ボディとして使われます。\nそして、平文テキストのボディは HTML のボディから全ての HTML 要素を削除することによって作成されます。\n\nビューのレンダリング結果はレイアウトで包むことが出来ます。レイアウトは、[[yii\\mail\\BaseMailer::htmlLayout]] と [[yii\\mail\\BaseMailer::textLayout]] を使ってセットアップすることが可能です。\nレイアウトは、通常のウェブ・アプリケーションのレイアウトと同じように働きます。\nレイアウトは、メールの CSS スタイルや、その他の共有されるコンテントをセットアップするために使うことが出来ます。\n\n```php\n<?php\nuse yii\\helpers\\Html;\n\n/**\n * @var \\yii\\web\\View $this ビュー・コンポーネントのインスタンス\n * @var \\yii\\mail\\MessageInterface $message 作成されるメッセージ\n * @var string $content メイン・ビューのレンダリング結果\n */\n?>\n<?php $this->beginPage() ?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n<head>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=<?= Yii::$app->charset ?>\" />\n    <style type=\"text/css\">\n        .heading {...}\n        .list {...}\n        .footer {...}\n    </style>\n    <?php $this->head() ?>\n</head>\n<body>\n    <?php $this->beginBody() ?>\n    <?= $content ?>\n    <div class=\"footer\">よろしくお願いします。<?= Yii::$app->name ?> チーム</div>\n    <?php $this->endBody() ?>\n</body>\n</html>\n<?php $this->endPage() ?>\n```\n\n\nファイルの添付\n--------------\n\n`attach()` メソッド、`attachContent()` メソッドを使って、メッセージにファイルを添付することが出来ます。\n\n```php\n$message = Yii::$app->mailer->compose();\n\n// ローカル・ファイル・システムからファイルを添付する\n$message->attach('/path/to/source/file.pdf');\n\n// 添付ファイルをその場で生成する\n$message->attachContent('添付される内容', ['fileName' => 'attach.txt', 'contentType' => 'text/plain']);\n```\n\n\n画像の埋め込み\n--------------\n\n`embed()` メソッドを使って、メッセージのコンテントに画像を埋め込むことが出来ます。\nこのメソッドは添付ファイルの ID を返しますので、それを `img` タグで使わなければなりません。\nこのメソッドはビュー・ファイルによってメッセージのコンテントを作成するときに簡単に使うことが出来ます。\n\n```php\nYii::$app->mailer->compose('embed-email', ['imageFileName' => '/path/to/image.jpg'])\n    // ...\n    ->send();\n```\n\nそして、ビュー・ファイルの中では、次のコードを使うことが出来ます。\n\n```php\n<img src=\"<?= $message->embed($imageFileName); ?>\">\n```\n\n\nテストとデバッグ\n----------------\n\n開発者は、実際にどのようなメールがアプリケーションによって送信されたか、その内容はどのようなものであったか、等をチェックしなければならないことが多くあります。\nYii は、そのようなチェックが出来ることを `yii\\mail\\BaseMailer::useFileTransport` によって保証しています。\nこのオプションを有効にすると、メールのメッセージ・データが、通常のように送信される代りに、ローカル・ファイルに強制的に保存されます。\nファイルは、`yii\\mail\\BaseMailer::fileTransportPath`、デフォルトでは `@runtime/mail` の下に保存されます。\n\n> Note: メッセージをファイルに保存するか、実際の受信者に送信するか、どちらかを選ぶことが出来ますが、両方を同時に実行することは出来ません。\n\nメール・メッセージのファイルは通常のテキストエディタで開くことが出来ますので、実際のメッセージ・ヘッダやコンテントなどを閲覧することが出来ます。\nこのメカニズムは、アプリケーションのデバッグや単体テストを実行する際に、真価を発揮するでしょう。\n\n> Note: メール・メッセージのファイルの内容は `\\yii\\mail\\MessageInterface::toString()` によって作成されますので、\n  あなたのアプリケーションで使用している実際のメール・エクステンションに依存したものになります。\n\n\nあなた自身のメール・ソリューションを作成する\n--------------------------------------------\n\nあなた自身のカスタム・メール・ソリューションを作成するためには、二つのクラスを作成する必要があります。\nすなわち、一つは `Mailer` であり、もう一つは `Message` です。\n`yii\\mail\\BaseMailer` と `yii\\mail\\BaseMessage` をあなたのソリューションの基底クラスとして使うことが出来ます。\nこれらのクラスが、このガイドで説明された基本的なロジックを既に持っています。しかし、それを使用することは強制されていません。\n`yii\\mail\\MailerInterface` と `yii\\mail\\MessageInterface` のインタフェイスを実装すれば十分です。\nそして、あなたのソリューションをビルドするために、全ての抽象メソッドを実装しなければなりません。\n"
  },
  {
    "path": "docs/guide-ja/tutorial-performance-tuning.md",
    "content": "パフォーマンス・チューニング\n============================\n\nあなたのウェブ・アプリケーションのパフォーマンスに影響を及ぼす要因は数多くあります。\n環境の要因もありますし、あなたのコードに関係する要因もあります。また、Yii そのものに関係する要因もあります。\nこのセクションでは要因のほとんどを列挙して、どのようにそれらを修正すればあなたのアプリケーションのパフォーマンスを向上させることが出来るかを説明します。\n\n\n## PHP 環境を最適化する <span id=\"optimizing-php\"></span>\n\nPHP 環境を正しく構成することは非常に重要です。最大のパフォーマンスを得るためには、\n\n- 最新の安定した PHP バージョンを使うこと。使用する PHP のメジャー・リリースを上げると、顕著なパフォーマンスの改善がもたらされることがあります。\n- [Opcache](https://www.php.net/manual/ja/book.opcache.php) (PHP 5.5 以降) または [APC](https://www.php.net/manual/ja/book.apcu.php) (PHP 5.4) を使って、\n  バイト・コード・キャッシュを有効にすること。\n  バイト・コード・キャッシュによって、リクエストが入ってくるたびに PHP スクリプトを解析してインクルードする時間の浪費を避けることが出来ます。\n- [`realpath()` キャッシュをチューニングする](https://github.com/samdark/realpath_cache_tuner).\n\n\n## デバッグ・モードを無効にする <span id=\"disable-debug\"></span>\n\n本番環境でアプリケーションを実行するときには、デバッグ・モードを無効にしなければなりません。\nYii は、`YII_DEBUG` という名前の定数の値を使って、デバッグ・モードを有効にすべきか否かを示します。\nデバッグ・モードが有効になっているときは、Yii はデバッグ情報の生成と記録のために時間を余計に費やします。\n\n[エントリ・スクリプト](structure-entry-scripts.md) の冒頭に次のコード行を置くことによってデ\nバッグ・モードを無効にすることが出来ます。\n\n```php\ndefined('YII_DEBUG') or define('YII_DEBUG', false);\n```\n\n> Info: `YII_DEBUG` のデフォルト値は `false` です。\n  従って、アプリケーション・コードの他のどこかでこのデフォルト値を変更していないと確信できるなら、単に上記の行を削除してデバッグ・モードを無効にしても構いません。\n\n\n## キャッシュのテクニックを使う <span id=\"using-caching\"></span>\n\nさまざまなキャッシュのテクニックを使うと、あなたのアプリケーションのパフォーマンスを目に見えて改善することが出来ます。\nたとえば、あなたのアプリケーションが Markdown 形式のテキスト入力をユーザに許可している場合、解析済みの Markdown のコンテントをキャッシュすることを考慮してください。\nそうすれば、リクエストごとに毎回同じ Markdown テキストの解析を繰り返すことを回避できるでしょう。\nYii によって提供されているキャッシュのサポートについて学ぶためには [キャッシュ](caching-overview.md) のセクションを参照してください。\n\n\n## スキーマ・キャッシュを有効にする <span id=\"enable-schema-caching\"></span>\n\nスキーマ・キャッシュは、[アクティブ・レコード](db-active-record.md) を使おうとする場合には、いつでも有効にすべき特別なキャッシュ機能です。\nご存じのように、アクティブ・レコードは、賢いことに、あなたがわざわざ記述しなくても、\nDB テーブルに関するスキーマ情報 (カラムの名前、カラムのタイプ、外部キー制約など) を自動的に検出します。\nアクティブ・レコードはこの情報を取得するために追加の SQL クエリを実行しています。\nスキーマ・キャッシュを有効にすると、取得されたスキーマ情報はキャッシュに保存されて将来のクエリで再利用されるようになります。\n\nスキーマ・キャッシュを有効にするためには、[アプリケーションの構成情報](concept-configurations.md) の中で、\n`cache` [アプリケーション・コンポーネント](structure-application-components.md) にスキーマ情報を保存するように構成し、[[yii\\db\\Connection::enableSchemaCache]] を `true` に設定します。\n\n```php\nreturn [\n    // ...\n    'components' => [\n        // ...\n        'cache' => [\n            'class' => 'yii\\caching\\FileCache',\n        ],\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=mydatabase',\n            'username' => 'root',\n            'password' => '',\n            'enableSchemaCache' => true,\n\n            // スキーマ・キャッシュの持続時間\n            'schemaCacheDuration' => 3600,\n\n            // スキーマ情報を保存するのに使用されるキャッシュ・コンポーネントの名前\n            'schemaCache' => 'cache',\n        ],\n    ],\n];\n```\n\n\n## アセットを結合して最小化する <span id=\"optimizing-assets\"></span>\n\n複雑なウェブ・ページでは、多数の CSS や JavaScript のアセット・ファイルをインクルードすることがよくあります。\nHTTP リクエストの回数、および、これらのアセットの全体としてのダウンロード・サイズを削減するために、アセットを単一のファイルに結合して、それを圧縮することを考慮すべきです。\nこれによって、ページのロードにかかる時間とサーバの負荷を大きく削減することが出来ます。\n詳細については、[アセット](structure-assets.md) のセクションを参照してください。\n\n\n## セッションのストレージを最適化する <span id=\"optimizing-session\"></span>\n\nデフォルトでは、セッションのデータはファイルに保存されます。\nこれは、`session_write_close()` が呼ばれる (Yii では `Yii::$app->session->close()` によって呼び出されます) か、あるいはリクエストの処理が終了して、セッションが閉じられる時点まで、ファイルが開かれるのをロックするという実装になっています。\nセッション・ファイルがロックされている間は、同じセッションを使用しようとする全てのリクエストはブロックされ、最初のリクエストがセッション・ファイルを解放するのを待たなければなりません。\n開発時はこれでも構いません。おそらく、小さなプロジェクトでも、これで大丈夫でしょう。\nしかし、大量のリクエストを並列処理するとなると、データベースのような、もっと洗練されたストレージを使う方が良いでしょう。\nYii はさまざまなセッション・ストレージのサポートを内蔵しています。\nこれらのストレージは、[アプリケーションの構成情報](concept-configurations.md) の中で `session` コンポーネントを次のように構成することによって使用することが出来ます。\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'session' => [\n            'class' => 'yii\\web\\DbSession',\n\n            // デフォルトの 'db' 以外の DB コンポーネントを使用したい場合は\n            // 以下を設定する\n            // 'db' => 'mydb',\n\n            // デフォルトの session テーブルをオーバーライドするためには以下を設定する\n            // 'sessionTable' => 'my_session',\n        ],\n    ],\n];\n```\n\n上記の構成は、セッション・データの保存にデータベース・テーブルを使用するものです。\nデフォルトでは、`db` アプリケーション・コンポーネントをデータベース接続として使用し、セッション・データを `session` テーブルに保存します。\nただし、前もって `session` テーブルを次のように作っておく必要があります。\n\n```sql\nCREATE TABLE session (\n    id CHAR(40) NOT NULL PRIMARY KEY,\n    expire INTEGER,\n    data BLOB\n)\n```\n\n[[yii\\web\\CacheSession]] を使って、セッションをキャッシュに保存することも出来ます。\n理論上、サポートされている [キャッシュ・ストレージ](caching-data.md#supported-cache-storage) のどれでも使うことが出来ます。\nただし、キャッシュ・ストレージの中には、容量の上限に達したときにキャッシュされたデータをフラッシュするものがあることに注意してください。\nこの理由により、主として容量の上限が無い種類のキャッシュ・ストレージを使用すべきです。\n\nあなたのサーバに [Redis](https://redis.io/) がある場合は、[[yii\\redis\\Session]] によって redis\nをセッション・ストレージとして使用することを強く推奨します。\n\n\n## データベースを最適化する <span id=\"optimizing-databases\"></span>\n\nDB クエリの実行とデータベースからのデータ取得がウェブ・アプリケーションのパフォーマンスの主たるボトルネックになることがよくあります。\n[データ・キャッシュ](caching-data.md) の使用によってパフォーマンスの劣化を緩和することは出来ますが、問題を完全に解決することは出来ません。\nデータベースが膨大なデータを抱えている場合、キャッシュされたデータが無効化されたときに最新のデータを取得するためのコストは、\nデータベースとクエリが適切に設計されていないと、法外なものになり得ます。\n\nDB クエリのパフォーマンスを向上させるための一般的なテクニックは、フィルタの対象になるテーブル・カラムにインデックスを作成することです。\n例えば、`username` によってユーザのレコードを検索する必要があるなら、`username` に対してインデックスを作成するべきです。\nただし、インデックスを付けると SELECT クエリを非常に速くすることが出来る代りに、INSERT、UPDATE、または DELTE のクエリが遅くなることに注意してください。\n\n複雑な DB クエリについては、クエリの解析と準備の時間を節約するために、データベース・ビューを作成することが推奨されます。\n\n最後にもう一つ大事なことですが、SELECT クエリで LIMIT を使ってください。\nこうすることで、大量のデータが返されて、PHP のために確保されたメモリを使い尽くすということがなくなります。\n\n\n## プレーンな配列を使う <span id=\"using-arrays\"></span>\n\n[アクティブ・レコード](db-active-record.md) は非常に使い勝手のよいものですが、データベースから大量のデータを取得する必要がある場合は、プレーンな配列を使うほどには効率的ではありません。\nそういう場合は、アクティブ・レコードを使ってデータを取得する際に `asArray()` を呼んで、\n取得したデータがかさばるアクティブ・レコードのオブジェクトではなく配列として表現されるようにすることを考慮するのが良いでしょう。\n例えば、\n\n```php\nclass PostController extends Controller\n{\n    public function actionIndex()\n    {\n        $posts = Post::find()->limit(100)->asArray()->all();\n        \n        return $this->render('index', ['posts' => $posts]);\n    }\n}\n```\n\n上記において、`$posts` は、テーブル行の配列としてデータを代入されることになります。各行はプレーンな配列になります。\n`$i` 番目の行の `title` カラムにアクセスするためには、`$posts[$i]['title']` という式を使うことが出来ます。\n\nクエリを構築するのに [DAO](db-dao.md) を使って、データをプレーンな配列に取得することも出来ます。\n\n\n## Composer オートローダを最適化する <span id=\"optimizing-autoloader\"></span>\n\nComposer のオートローダは、ほとんどのサードパーティのクラス・ファイルをインクルードするのに使われますので、\n次のコマンドを実行して Composer のオートローダを最適化することを考慮すべきです。\n\n```\ncomposer dumpautoload -o\n```\n\nさらに加えて、\n[authoritative class maps](https://getcomposer.org/doc/articles/autoloader-optimization.md#optimization-level-2-a-authoritative-class-maps)\nおよび [APCu cache](https://getcomposer.org/doc/articles/autoloader-optimization.md#optimization-level-2-b-apcu-cache) の使用を検討して下さい。\nただし、この二つの最適化があなたの特定のケースに適切である場合もあれば、そうでない場合もあります。\n\n\n## オフラインでデータを処理する <span id=\"processing-data-offline\"></span>\n\nリクエストが何らかのリソース集約的な操作を必要とするものである場合は、そういう操作が終るまでユーザを待たせずに、\nオフラインモードで操作を実行する方策を考えるべきです。\n\nオフラインでデータを処理するための方法が二つあります。すなわち、プルとプッシュです。\n\nプルの方法では、リクエストが何らかの複雑な操作を必要とするたびに、タスクを作成してデータベースなどの持続的ストレージに保存します。\nそうしておいて、別の独立したプロセス (例えばクロンジョブ) を使い、タスクを引き出して処理します。\nこの方法は、実装は容易ですが、いくつかの欠点があります。\n例えば、タスクのプロセスはストレージから定期的にタスクを引き出さなければなりません。\n引き出す間隔が長すぎると、タスクの処理に大きな遅延が生じます。しかし、間隔が短すぎると、オーバーヘッドが大きくなります。\n\nプッシュの方法では、タスクを管理するのにメッセージ・キュー (例えば、RabbitMQ、ActiveMQ、Amazon SQS など) を使用します。\n新しいタスクがキューに入れられるたびに、タスクを処理するプロセスが起動されたり通知を受けたりして、タスク処理がトリガされます。\n\n\n## パフォーマンス・プロファイリング <span id=\"performance-profiling\"></span>\n\nあなたは、あなたのコードをプロファイルして、パフォーマンスのボトルネックを発見し、それに応じた適切な手段を講じるべきです。\n次のプロファイリング・ツールが役に立つでしょう。\n\n- [Yii のデバッグ・ツールバーとデバッガ](https://github.com/yiisoft/yii2-debug/blob/master/docs/guide-ja/README.md)\n- [Blackfire](https://blackfire.io/)\n- [XHProf](https://www.php.net/manual/ja/book.xhprof.php)\n- [XDebug プロファイラ](https://xdebug.org/docs/profiler)\n\n## アプリケーションをスケーラブルなものにする覚悟を決める\n\n何をやっても助けにならないときは、あなたのアプリケーションをスケーラブルにすることを試みましょう。良い導入記事が [Configuring a Yii 2 Application for an Autoscaling Stack (Yii 2 アプリケーションを自動スケール環境のために構成する)](https://github.com/samdark/yii2-cookbook/blob/master/book/scaling.md) の中で提供されています。\n"
  },
  {
    "path": "docs/guide-ja/tutorial-shared-hosting.md",
    "content": "共有ホスティング環境\n====================\n\n共有ホスティング環境では、たいてい、構成やディレクトリ構造について大きな制約があります。\nそれでも、ほとんどの場合、少し調整をすれば、Yii 2.0 を共有ホスティング環境で走らせることが可能です。\n\n## ベーシック・プロジェクト・テンプレートを配備する\n\n通例、共有ホスティング環境では、一つのウェブ・ルートしかありませんので、可能であればベーシック・プロジェクト・テンプレートを使用して下さい。\nまず、[Yii をインストールする](start-installation.md) のセクションを参照して、\nプロジェクト・テンプレートをローカル環境にインストールします。\nそして、ローカル環境でアプリケーションが動くようにした後で、共有ホスティング環境でホスト出来るようにいくつかの修正を行います。\n\n### ウェブ・ルートの名前を変える <span id=\"renaming-webroot\"></span>\n\nFTP またはその他の手段であなたの共有ホストに接続します。おそらく、下記のようなディレクトリが見えるでしょう。\n \n```\nconfig\nlogs\nwww\n```\n\n上記で `www` はウェブ・サーバのウェブ・ルート・ディレクトリです。\n別の名前かもしれません。よくある名前は、`www`、`htdocs`、`public_html` です。\n\n私たちのベーシック・プロジェクト・テンプレートではウェブ・ルートの名前は `web` になっています。\nあなたのウェブ・サーバにアップロードする前に、ローカルのウェブ・ルートの名前をあなたのサーバに適合するように変更します。\nすなわち、`web` から `www` や `public_html` など、何であれ、あなたの共有ホストのウェブ・ルートの名前に変更します。\n\n### FTP ルート・ディレクトリは書き込み可能\n\nルート・レベルのディレクトリ、すなわち、`config`、`logs`、`www` があるディレクトリに対して書き込みが出来るのであれば、\n`assets`、`commands` などをそのままルート・レベルのディレクトリにアップロードします。\n\n### ウェブ・サーバのための追加設定 <span id=\"add-extras-for-webserver\"></span>\n\n使用されているウェブ・サーバが Apache である場合は、次の内容を持つ `.htaccess` ファイルを `web`\n(または `public_html` など、要するに、`index.php` があるディレクトリ) に追加する必要があります。\n\n```\nOptions +FollowSymLinks\nIndexIgnore */*\n\nRewriteEngine on\n\n# ディレクトリかファイルが存在すれば、それを直接使う\nRewriteCond %{REQUEST_FILENAME} !-f\nRewriteCond %{REQUEST_FILENAME} !-d\n\n# それ以外は、index.php にフォワードする\nRewriteRule . index.php\n```\n\nnginx の場合は、追加の構成ファイルは必要がない筈です。\n\n### 必要条件をチェックする\n\nYii を走らせるためには、あなたのウェブ・サーバは Yii の必要条件を満たさなければなりません。最低限の必要条件は PHP 5.4 です。\n必要条件をチェックするために、`requirements.php` をルート・ディレクトリからウェブ・ルート・ディレクトリにコピーして、\n`https://example.com/requirements.php` という URL を使ってブラウザ経由で走らせます。後でファイルを削除するのを忘れないでください。\n\n## アドバンスト・プロジェクト・テンプレートを配備する\n\nアドバンスト・プロジェクト・テンプレートを共有ホストに配備することは、ベーシック・プロジェクト・テンプレートを配備するのに比べると少しトリッキーにはなりますが、可能です。\n[アドバンスト・プロジェクト・テンプレートのドキュメント](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide-ja/topic-shared-hosting.md)\nで説明されている指示に従って下さい。\n"
  },
  {
    "path": "docs/guide-ja/tutorial-start-from-scratch.md",
    "content": "あなた自身のアプリケーション構造を作成する\n==========================================\n\n> Note: このセクションはまだ執筆中です。\n\n[ベーシック](https://github.com/yiisoft/yii2-app-basic) と [アドバンスト](https://github.com/yiisoft/yii2-app-advanced)\nのプロジェクト・テンプレートは、あなたの要求をほとんどカバーする優れたものですが、\nあなたのプロジェクトを開始するためのあなた自身のテンプレートを作成したいこともあるでしょう。\n\nYii におけるプロジェクト・テンプレートは、`composer.json` ファイルを含み、Composer パッケージとして登録されたレポジトリであるに過ぎません。\nどのようなレポジトリでも、Composer パッケージとして特定し、`create-project` Composer コマンドによってインストール可能なものにすることが出来ます。\n\nテンプレート全体を最初から構築するのは少々大変ですので、内蔵のテンプレートの一つを基礎として使うのが良いでしょう。\nここでは、ベーシック・テンプレートを使いましょう。\n\nベーシック・テンプレートをクローンする\n--------------------------------------\n\n最初のステップは、ベーシック Yii テンプレートの Git レポジトリをクローンすることです。\n\n```bash\ngit clone git@github.com:yiisoft/yii2-app-basic.git\n```\n\nそして、レポジトリがあなたのコンピュータにダウンロードされるのを待ちます。\nテンプレートに加えられた変更がレポジトリにプッシュ・バックされることはありませんので、ダウンロードしたものから `.git` ディレクトリとその中身を全て削除して構いません。\n\nファイルを修正する\n------------------\n\n次に、あなたのテンプレートに合うように `composer.json` を修正します。\n`name`、`description`、`keywords`、`homepage`、`license` および `support` の値を、あなたの新しいテンプレートを説明するものに変更します。\nまた、`require`、`require-dev`、`suggest` や、その他のオプションも、あなたのテンプレートの要求に合うように調整します。\n\n> Note: `composer.json` ファイルで、`extra` の下の `writable` パラメータを使って、\n> アプリケーションがテンプレートを使って作成された後に設定されるべきファイル単位のアクセス権限を指定してください。\n\n次に、あなたが好むデフォルトの状態に合うように、アプリケーションの構造と内容を実際に修正します。\n最後に、あなたのテンプレートに適用できるように、README ファイルを更新します。\n\nパッケージを作る\n----------------\n\nテンプレートが定義できたら、それを基に Git レポジトリを作成して、ファイルをそこにプッシュします。\nあなたのテンプレートをオープンソース化するつもりなら、レポジトリをホストするのには [Github](https://github.com) が最適の場所です。\nテンプレートを共同作業に使わないつもりであれば、どんな Git レポジトリサイトでも構いません。\n\n次に、Composer のためにパッケージを登録する必要があります。パブリックなテンプレートであれば、パッケージは [Packagist](https://packagist.org/) に登録すべきです。\nプライベートなテンプレートは、パッケージの登録が少々トリッキーです。\nその説明については [Composer ドキュメント](https://getcomposer.org/doc/05-repositories.md#hosting-your-own) を参照してください。\n\nテンプレートを使う\n------------------\n\nYii の新しいプロジェクト・テンプレートを作成するのに必要なことは以上です。\nこれで、あなたのテンプレートを使ってプロジェクトを作成することが出来ます。\n\n```\ncomposer create-project --prefer-dist --stability=dev mysoft/yii2-app-coolone new-project\n```\n"
  },
  {
    "path": "docs/guide-ja/tutorial-template-engines.md",
    "content": "テンプレートエンジンを使う\n==========================\n\nデフォルトでは、Yii は PHP をテンプレート言語として使いますが、[Twig](https://twig.symfony.com/) や\n[Smarty](https://www.smarty.net/) などの他のレンダリング・エンジンをサポートするように Yii を構成することが出来ます。\n\n`view` コンポーネントがビューのレンダリングに責任を持っています。\nこのコンポーネントのビヘイビアを構成することによって、カスタム・テンプレート・エンジンを追加することが出来ます。\n\n```php\n[\n    'components' => [\n        'view' => [\n            'class' => 'yii\\web\\View',\n            'renderers' => [\n                'tpl' => [\n                    'class' => 'yii\\smarty\\ViewRenderer',\n                    //'cachePath' => '@runtime/Smarty/cache',\n                ],\n                'twig' => [\n                    'class' => 'yii\\twig\\ViewRenderer',\n                    'cachePath' => '@runtime/Twig/cache',\n                    // twig のオプションの配列\n                    'options' => [\n                        'auto_reload' => true,\n                    ],\n                    'globals' => ['html' => '\\yii\\helpers\\Html'],\n                    'uses' => ['yii\\bootstrap'],\n                ],\n                // ...\n            ],\n        ],\n    ],\n]\n```\n\n上記のコードにおいては、Smarty と Twig の両者がビュー・ファイルによって使用可能なものとして構成されています。しかし、\nこれらのエクステンションをプロジェクトで使うためには、`composer.json` ファイルも修正して、これらのエクステンションを含める必要があります。\n\n```\n\"yiisoft/yii2-smarty\": \"~2.0.0\",\n\"yiisoft/yii2-twig\": \"~2.0.0\",\n```\n上のコードを `composer.json` の `require` セクションに追加します。変更をファイルに保存した後、コマンドラインで `composer update --prefer-dist` を実行することによってエクステンションをインストールすることが出来ます。\n\n具体的にテンプレート・エンジンを使用する方法については、それぞれのドキュメントで詳細を参照してください。\n\n- [Twig ガイド](https://www.yiiframework.com/extension/yiisoft/yii2-twig/doc/guide/)\n- [Smarty ガイド](https://www.yiiframework.com/extension/yiisoft/yii2-smarty/doc/guide/)\n"
  },
  {
    "path": "docs/guide-ja/tutorial-yii-as-micro-framework.md",
    "content": "# Yii をマイクロ・フレームワークとして使う\n\nYii はベーシック・テンプレートやアドバンスト・テンプレートに含まれる機能なしで使うことが簡単にできます。言葉を換えれば、Yii は既にマイクロ・フレームワークです。Yii を使うためにテンプレートによって提供されているディレクトリ構造を持つことは要求されていません。\n\nこのことは、アセットやビューなどの事前定義されたテンプレート・コードを必要としない場合には、特に好都合です。そのような場合の一つが JSON API です。以下に続くセクションで、どのようにしてそれを実現するかを示します。\n\n## Yii をインストールする\n\nプロジェクト・ファイルのためのディレクトリを作成し、ワーキング・ディレクトリをそのパスに変更します。例で使用されているコマンドは UNIX ベースのものですが、同様のコマンドが Windows にもあります。\n\n```bash\nmkdir micro-app\ncd micro-app\n```\n\n> Note: 続けるためには Composer についての知識が多少必要です。Composer の使い方をまだ知らない場合は、時間を取って、[Composer Guide](https://getcomposer.org/doc/00-intro.md) を読んでください。\n\n`micro-app` ディレクトリの下に  `composer.json` ファイルを作成し、あなたの好みのエディタを使って、下記を追加します。\n\n```json\n{\n    \"require\": {\n        \"yiisoft/yii2\": \"~2.0.0\"\n    },\n    \"repositories\": [\n        {\n            \"type\": \"composer\",\n            \"url\": \"https://asset-packagist.org\"\n        }\n    ]\n}\n```\n\nファイルを保存して `composer install` コマンドを実行します。これによって、フレームワークがその全ての依存とともにインストールされます。\n\n## プロジェクトの構造を作成する\n\nフレームワークをインストールしたら、次は、アプリケーションの [エントリ・ポイント](structure-entry-scripts.md) を作成します。エントリ・ポイントは、アプリケーションを開こうとしたときに、一番最初に実行されるファイルです。セキュリティ上の理由により、エントリ・ポイントを置くディレクトリは別にして、それをウェブ・ルートとします。\n\n`web` ディレクトリを作成して、下記の内容を持つ `index.php` をそこに置きます。\n\n```php \n<?php\n\n// 実運用サーバに配備するときは次の2行をコメント・アウトする\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n\nrequire(__DIR__ . '/../vendor/autoload.php');\nrequire(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');\n\n$config = require __DIR__ . '/../config.php';\n(new yii\\web\\Application($config))->run();\n```\n\nまた `config.php` という名前のファイルを作成し、アプリケーションの全ての構成情報をそこに含ませます。\n\n```php\n<?php\nreturn [\n    'id' => 'micro-app',\n    // アプリケーションの basePath は `micro-app` ディレクトリになります\n    'basePath' => __DIR__,\n    // この名前空間からアプリケーションは全てのコントローラを探します\n    'controllerNamespace' => 'micro\\controllers',\n    // 'micro' 名前空間からのクラスのオートロードを可能にするためにエイリアスを設定します\n    'aliases' => [\n        '@micro' => __DIR__,\n    ],\n];\n```\n\n> Info: 構成情報を `index.php` ファイルに持つことも出来ますが、別のファイルに持つことを推奨します。\n> そうすれば、後で示しているように、同じ構成情報をコンソール・アプリケーションから使うことが出来ます。\n\nこれであなたのプロジェクトはコーディングの準備が出来ました。プロジェクトのディレクトリ構造を決定するのは、名前空間に注意する限り、あなた次第です。\n\n## 最初のコントローラを作成する\n\n`controllers` ディレクトリを作成し、`SiteController.php` というファイルを追加します。\nこれが、パス情報を持たないリクエストを処理する、デフォルトのコントローラです。\n\n```php\n<?php\n\nnamespace micro\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actionIndex()\n    {\n        return 'こんにちは!';\n    }\n}\n```\n\nこのコントローラに違う名前を使いたい場合は、名前を変更して [[yii\\base\\Application::$defaultRoute]] をそれに応じて変更します。\n例えば、`DefaultController` であれば、`'defaultRoute' => 'default/index'` と変更します。\n\nこの時点で、プロジェクトの構造は次のようになっています。\n\n```\nmicro-app/\n├── composer.json\n├── config.php\n├── web/\n    └── index.php\n└── controllers/\n    └── SiteController.php\n```\n\nまだウェブ・サーバをセットアップしていない場合は、[ウェブ・サーバの構成ファイル例](start-installation.md#configuring-web-servers) を参照すると良いでしょう。\nもう一つのオプションは、PHP の内蔵ウェブ・サーバを利用する `yii serve` コマンドを使うことです。\n`micro-app/` ディレクトリから、次のコマンドを実行します。\n\n    vendor/bin/yii serve --docroot=./web\n\nアプリケーションの URL をブラウザで開くと、`SiteController::actionIndex()` で返された \"こんにちは!\" という文字列が表示される筈です。\n\n> Info: 私たちの例では、アプリケーションのデフォルトの名前空間 `app` を `micro` に変更しています。\n> これは、あなたがその名前に縛られていないことを示すためです(万一あなたが縛られていると思っている場合を考えて)。\n> そして、[[yii\\base\\Application::$controllerNamespace|コントローラの名前空間]] を修正し、正しいエイリアスを設定しています。\n\n\n## REST API を作成する\n\n私たちの \"マイクロ・フレームワーク\" の使い方を示すために、記事のための簡単な REST API を作成しましょう。\n`\nこの API が何らかのデータを提供するためには、まず、データベースが必要です。\nデータベース接続の構成をアプリケーション構成に追加します。\n\n```php\n'components' => [\n    'db' => [\n        'class' => 'yii\\db\\Connection',\n        'dsn' => 'sqlite:@micro/database.sqlite',\n    ],\n],\n```\n\n> Info: ここでは話を簡単にするために sqlite データベースを使用します。他のオプションについては [データベースのガイド](db-dao.md) を参照してください。\n\n次に、[データベース・マイグレーション](db-migrations.md) を作成して、記事のテーブルを作成します。\n既に述べたように、独立した構成情報ファイルがあることを確認してください。\n下記のコンソール・コマンドを実行するためには、それが必要です。\n次のコマンドを実行すると、データベース・マイグレーション・ファイルが作成され、そして、マイグレーションがデータベースに適用されます。\n\n    vendor/bin/yii migrate/create --appconfig=config.php create_post_table --fields=\"title:string,body:text\"\n    vendor/bin/yii migrate/up --appconfig=config.php\n\n`models` ディレクトリを作成し、`Post.php` ファイルをそのディレクトリに置きます。以下がそのモデルのためのコードです。\n\n```php\n<?php\n\nnamespace micro\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass Post extends ActiveRecord\n{ \n    public static function tableName()\n    {\n        return '{{post}}';\n    }\n}\n```\n\n> Info: ここで作成されたモデルは ActiveRecord クラスのもので、`post` テーブルのデータを表します。\n> 詳細な情報は [アクティブ・レコードのガイド](db-active-record.md) を参照してください。\n\n私たちの API で記事データへのアクセスを提供するために、`controllers` に `PostController` を追加します。\n\n```php\n<?php\n\nnamespace micro\\controllers;\n\nuse yii\\rest\\ActiveController;\n\nclass PostController extends ActiveController\n{\n    public $modelClass = 'micro\\models\\Post';\n\n    public function behaviors()\n    {\n        // 動作のために認証済みユーザであることを要求する rateLimiter を削除\n        $behaviors = parent::behaviors();\n        unset($behaviors['rateLimiter']);\n        return $behaviors;\n    }\n}\n```\n\nこの時点で私たちの API は以下の URL を提供します。\n\n- `/index.php?r=post` - 全ての記事をリストする\n- `/index.php?r=post/view&id=1` - ID 1 の記事を表示する\n- `/index.php?r=post/create` - 記事を作成する\n- `/index.php?r=post/update&id=1` - ID 1 の記事を更新する\n- `/index.php?r=post/delete&id=1` - ID 1 の記事を削除する\n\nここから開始して、あなたのアプリケーションの開発を更に進めるために、次のガイドを読むと良いでしょう。\n\n- API は今のところ入力として URL エンコードされたフォームデータだけを理解します。\n  本物の JSON API にするためには、[[yii\\web\\JsonParser]] を構成する必要があります。\n- URL をもっと馴染みやすいものにするためには、ルーティングを構成しなければなりません。\n  方法を知るためには [REST のルーティングのガイド](rest-routing.md) を参照してください。\n- 更に参照すべき文書を知るために [先を見通す](start-looking-ahead.md) のセクションを読んでください。\n"
  },
  {
    "path": "docs/guide-ja/tutorial-yii-integration.md",
    "content": "サードパーティのコードを扱う\n============================\n\n時々、Yii アプリケーションの中でサードパーティのコードを使用する必要があることがあります。\nあるいは、サードパーティのシステムの中で Yii をライブラリとして使用したいこともあるでしょう。このセクションでは、こういう目的をどうやって達成するかを説明します。\n\n\nYii の中でサードパーティのライブラリを使う <span id=\"using-libs-in-yii\"></span>\n------------------------------------------\n\nYii アプリケーションの中でサードパーティのライブラリを使うために主として必要なことは、\nそのライブラリのクラスが適切にインクルードされること、または、オートロード可能であることを保証することです。\n\n### Composer パッケージを使う <span id=\"using-composer-packages\"></span>\n\n多くのサードパーティ・ライブラリは [Composer](https://getcomposer.org/) パッケージの形式でリリースされています。\nそのようなライブラリは、次の二つの簡単なステップを踏むことによって、インストールすることが出来ます。\n\n1. アプリケーションの `composer.json` ファイルを修正して、どの Composer パッケージをインストールしたいかを指定する。\n2. `composer install` を実行して、指定したパッケージをインストールする。\n\nインストールされた Composer パッケージ内のクラスは、Composer のオートローダを使ってオートロードすることが出来ます。\nアプリケーションの [エントリ・スクリプト](structure-entry-scripts.md) に、\nComposer のオートローダをインストールするための下記の行があることを確認してください。\n\n```php\n// Composer のオートローダをインストール\nrequire __DIR__ . '/../vendor/autoload.php';\n\n// Yii クラス・ファイルをインクルード\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n```\n\n### ダウンロードしたライブラリを使う <span id=\"using-downloaded-libs\"></span>\n\nライブラリが Composer パッケージとしてリリースされていない場合は、そのライブラリのインストールの指示に従ってインストールしなければなりません。\nたいていの場合は、リリース・ファイルを手動でダウンロードし、`BasePath/vendor` ディレクトリの下に解凍する必要があります。\nここで `BasePath` は、アプリケーションの [base path](structure-applications.md#basePath) を表すものです。\n\nライブラリがそれ自身のオートローダを持っている場合は、それをアプリケーションの [エントリ・スクリプト](structure-entry-scripts.md) でインストールすることが出来ます。\n複数のオートローダ・クラスの中で Yii のクラス・オートローダが優先されるように、\nライブラリのオートローダは `Yii.php` ファイルをインクルードする前にインストールすることを推奨します。\n\nライブラリがクラスオートローダを提供していない場合でも、クラスの命名規約が [PSR-4](https://www.php-fig.org/psr/psr-4/) に従っている場合は、ライブラリのクラスをオートロードするのに Yii のクラス・オートローダを使うことが出来ます。\n必要なことは、ライブラリのクラスによって使われている全てのルート名前空間に対して [ルート・エイリアス](concept-aliases.md#defining-aliases) を宣言することだけです。\n例えば、ライブラリを `vendor/foo/bar` ディレクトリの下にインストールしたとしましょう。\nそしてライブラリのクラスは `xyz` ルート名前空間の下にあるとします。\nこの場合、アプリケーションの構成情報において、次のコードを含めれば良いのです。\n\n```php\n[\n    'aliases' => [\n        '@xyz' => '@vendor/foo/bar',\n    ],\n]\n```\n\n上記のどちらにも当てはまらない場合、おそらくそのライブラリは、クラス・ファイルを探して適切にインクルードするために、PHP の include path 設定に依存しているのでしょう。\nこの場合は、PHP include path の設定に関するライブラリの指示に従うしかありません。\n\n最悪の場合として、ライブラリが全てのクラス・ファイルを明示的にインクルードすることを要求している場合は、\n次の方法を使ってクラスを必要に応じてインクルードすることが出来るようになります。\n\n* ライブラリに含まれるクラスを特定する。\n* アプリケーションの [エントリ・スクリプト](structure-entry-scripts.md) において、\n  クラスと対応するファイル・パスを `Yii::$classMap` としてリストアップする。例えば、\n```php\nYii::$classMap['Class1'] = 'path/to/Class1.php';\nYii::$classMap['Class2'] = 'path/to/Class2.php';\n```\n\n\nサードパーティのシステムで Yii を使う <span id=\"using-yii-in-others\"></span>\n-------------------------------------\n\nYii は数多くの優れた機能を提供していますので、サードパーティのシステム (例えば、WordPress、Joomla、または、他の PHP フレームワークを使って開発されるアプリケーション)\nを開発したり機能拡張したりするのをサポートするために Yii の機能のいくつかを使用したいことがあるでしょう。\n例えば、[[yii\\helpers\\ArrayHelper]] クラスや [アクティブ・レコード](db-active-record.md) をサードパーティのシステムで使いたいことがあるでしょう。\nこの目的を達するためには、主として、二つのステップを踏む必要があります。\nすなわち、Yii のインストールと、Yii のブートストラップです。\n\nサードパーティのシステムが Composer を使って依存を管理している場合は、\n単に下記のコマンドを実行すれば Yii をインストールすることが出来ます。\n\n```bash\ncomposer require yiisoft/yii2\n```\n\nデータベース抽象レイヤなど、アセットに関係しない Yii の機能だけを使用したい場合は、\nBower および NPM のパッケージのインストールを阻止する特別な composer パッケージが必要になります。\n詳細については [cebe/assetfree-yii2](https://github.com/cebe/assetfree-yii2) を参照して下さい。\n\nComposer に関する更なる情報や、インストールの過程で出現しうる問題に対する解決方法については、\n一般的な [Composer によるインストール](start-installation.md#installing-via-composer) のセクションを参照してください。\n\nそうでない場合は、Yii のリリースを [ダウンロード](https://www.yiiframework.com/download/) して、\n`BasePath/vendor` ディレクトリに解凍してください。\n\n次に、サードパーティのシステムのエントリ・スクリプトを修正します。次のコードをエントリ・スクリプトの先頭に追加してください。\n\n```php\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n\n$yiiConfig = require __DIR__ . '/../config/yii/web.php';\nnew yii\\web\\Application($yiiConfig); // ここで run() を呼ばない\n```\n\nごらんのように、上記のコードは典型的な Yii アプリケーションの [エントリ・スクリプト](structure-entry-scripts.md) と非常に良く似ています。\n唯一の違いは、アプリケーションのインスタンスが作成された後に `run()` メソッドが呼ばれない、という点です。\n`run()` を呼ぶと Yii がリクエスト処理のワークフローを制御するようになりますが、\nこの場合はリクエストを処理する別のアプリケーションが既に存在していますので、これは必要ではないからです。\n\nYii アプリケーションでの場合と同じように、サードパーティ・システムが走っている環境に基づいて Yii のアプリケーション・インスタンスを構成する必要があります。\n例えば、[アクティブ・レコード](db-active-record.md) の機能を使うためには、サードパーティ・システムによって使用されている DB 接続の設定を使って\n`db` [アプリケーション・コンポーネント](structure-application-components.md) を構成しなければなりません。\n\nこれで、Yii によって提供されているほとんどの機能を使うことが出来ます。\n例えば、アクティブ・レコード・クラスを作成して、それを使ってデータベースを扱うことが出来ます。\n\n\nYii 2 を Yii 1 とともに使う <span id=\"using-both-yii2-yii1\"></span>\n---------------------------\n\nあなたが Yii 1 を前から使っている場合は、たぶん、稼働中の Yii 1 アプリケーションを持っているでしょう。\nアプリケーション全体を Yii 2 で書き直す代りに、Yii 2 でのみ利用できる機能を使ってアプリケーションを機能拡張したいこともあるでしょう。\nこのことは、以下に述べるようにして、実現できます。\n\n> Note: Yii 2 は PHP 5.4 以上を必要とします。\n> あなたのサーバと既存のアプリケーションが PHP 5.4 以上をサポートしていることを確認しなければなりません。\n\n最初に、[直前の項](#using-yii-in-others) で述べられている指示に従って、Yii 2 を既存のアプリケーションにインストールします。\n\n次に、アプリケーションのエントリ・スクリプトを以下のように修正します。\n\n```php\n// カスタマイズされた Yii クラスをインクルード (下記で説明)\nrequire __DIR__ . '/../components/Yii.php';\n\n// Yii 2 アプリケーションの構成\n$yii2Config = require __DIR__ . '/../config/yii2/web.php';\nnew yii\\web\\Application($yii2Config); // ここで run() を呼ばない。yii2 app はサービス・ロケータとしてのみ使用される。\n\n// Yii 1 アプリケーションの構成\n$yii1Config = require __DIR__ . '/../config/yii1/main.php';\nYii::createWebApplication($yii1Config)->run();\n```\n\nYii 1 と Yii 2 の両者が `Yii` クラスを持っているため、二つを結合するカスタム・バージョンを作成する必要があります。\n上記のコードでカスタマイズされた `Yii` クラス・ファイルをインクルードしていますが、これは下記のようにして作成することが出来ます。\n\n```php\n$yii2path = '/path/to/yii2';\nrequire $yii2path . '/BaseYii.php'; // Yii 2.x\n\n$yii1path = '/path/to/yii1';\nrequire $yii1path . '/YiiBase.php'; // Yii 1.x\n\nclass Yii extends \\yii\\BaseYii\n{\n    // YiiBase (1.x) のコードをここにコピー・ペースト\n}\n\nspl_autoload_unregister(array('YiiBase','autoload'));\nspl_autoload_register(array('Yii','autoload'));\n\nYii::$classMap = include($yii2path . '/classes.php');\n// Yii 2 オートローダを Yii 1 によって登録\nYii::registerAutoloader(['yii\\BaseYii', 'autoload']);\n// 依存注入コンテナを作成\nYii::$container = new yii\\di\\Container;\n```\n\n以上です。\nこれで、あなたのコードのどの部分においても、`Yii::$app` を使って Yii 2 アプリケーション・インスタンスにアクセスすることが出来、`Yii::app()` によって Yii 1 アプリケーション・インスタンスを取得することが出来ます。\n\n```php\necho get_class(Yii::app()); // 'CWebApplication' を出力\necho get_class(Yii::$app);  // 'yii\\web\\Application' を出力\n```\n"
  },
  {
    "path": "docs/guide-pl/README.md",
    "content": "Przewodnik po Yii 2.0\n=====================\n\nTen poradnik udostępniony jest na [Warunkach dokumentacji Yii](https://www.yiiframework.com/doc/terms/).\n\nWszelkie prawa zastrzeżone.\n\n2014 (c) Yii Software LLC.\n\n\nWstęp\n-----\n\n* [O Yii](intro-yii.md)\n* [Aktualizacja z wersji 1.1](intro-upgrade-from-v1.md)\n\n\nPierwsze kroki\n--------------\n\n* [Co musisz wiedzieć](start-prerequisites.md)\n* [Instalacja Yii](start-installation.md)\n* [Uruchamianie aplikacji](start-workflow.md)\n* [Witaj świecie](start-hello.md)\n* [Praca z formularzami](start-forms.md)\n* [Praca z bazami danych](start-databases.md)\n* [Generowanie kodu za pomocą Gii](start-gii.md)\n* [Dalsze kroki](start-looking-ahead.md)\n\n\nStruktura aplikacji\n-------------------\n\n* [Przegląd](structure-overview.md)\n* [Skrypty wejściowe](structure-entry-scripts.md)\n* [Aplikacje](structure-applications.md)\n* [Komponenty aplikacji](structure-application-components.md)\n* [Kontrolery](structure-controllers.md)\n* [Modele](structure-models.md)\n* [Widoki](structure-views.md)\n* [Moduły](structure-modules.md)\n* [Filtry](structure-filters.md)\n* [Widżety](structure-widgets.md)\n* [Zasoby (Assets)](structure-assets.md)\n* [Rozszerzenia](structure-extensions.md)\n\n\nObsługa żądań\n-------------\n\n* [Przegląd](runtime-overview.md)\n* [Bootstrapping](runtime-bootstrapping.md)\n* [Parsowanie i generowanie adresów URL](runtime-routing.md)\n* [Żądania](runtime-requests.md)\n* [Odpowiedzi](runtime-responses.md)\n* [Sesje i ciasteczka](runtime-sessions-cookies.md)\n* [Obsługa błędów](runtime-handling-errors.md)\n* [Logowanie](runtime-logging.md)\n\n\nKluczowe koncepcje\n------------------\n\n* [Komponenty](concept-components.md)\n* [Właściwości](concept-properties.md)\n* [Zdarzenia (Events)](concept-events.md)\n* [Zachowania (Behaviors)](concept-behaviors.md)\n* [Konfiguracje](concept-configurations.md)\n* [Aliasy](concept-aliases.md)\n* [Autoładowanie klas](concept-autoloading.md)\n* [Lokator usług (Service Locator)](concept-service-locator.md)\n* [Kontener wstrzykiwania zależności (DI Container)](concept-di-container.md)\n\n\nPraca z bazami danych\n---------------------\n\n* [Obiekty dostępu do danych (DAO)](db-dao.md): Łączenie z bazą, podstawowe zapytania, transakcje i manipulacja schematem.\n* [Konstruktor kwerend](db-query-builder.md): Zapytania do bazy danych z użyciem warstwy abstrakcyjnej.\n* [Active Record](db-active-record.md): Active Record ORM, otrzymywanie i manipulacja rekordami oraz definiowanie relacji.\n* [Migracje](db-migrations.md): Użycie systemu kontroli wersji bazy danych do pracy z wieloma środowiskami.\n* [Sphinx](https://www.yiiframework.com/extension/yiisoft/yii2-sphinx/doc/guide)\n* [Redis](https://www.yiiframework.com/extension/yiisoft/yii2-redis/doc/guide)\n* [MongoDB](https://www.yiiframework.com/extension/yiisoft/yii2-mongodb/doc/guide)\n* [ElasticSearch](https://www.yiiframework.com/extension/yiisoft/yii2-elasticsearch/doc/guide)\n\n\nOdbieranie danych od użytkowników\n---------------------------------\n\n* [Tworzenie formularzy](input-forms.md)\n* [Walidacja danych wejściowych](input-validation.md)\n* [Wysyłanie plików](input-file-upload.md)\n* [Odczytywanie tablicowych danych wejściowych](input-tabular-input.md)\n* [Pobieranie danych dla wielu modeli](input-multiple-models.md)\n* [Rozszerzanie ActiveForm po stronie klienta](input-form-javascript.md)\n\n\nWyświetlanie danych\n-------------------\n\n* [Formatowanie danych](output-formatting.md)\n* [Stronicowanie](output-pagination.md)\n* [Sortowanie](output-sorting.md)\n* [Dostawcy danych](output-data-providers.md)\n* [Widżety danych](output-data-widgets.md)\n* [Praca ze skryptami](output-client-scripts.md)\n* [Skórki i motywy (Theming)](output-theming.md)\n\n\nBezpieczeństwo\n--------------\n\n* [Omówienie](security-overview.md)\n* [Uwierzytelnianie](security-authentication.md)\n* [Autoryzacja](security-authorization.md)\n* [Praca z hasłami](security-passwords.md)\n* [Kryptografia](security-cryptography.md)\n* [Klienty autoryzacji](https://www.yiiframework.com/extension/yiisoft/yii2-authclient/doc/guide)\n* [Bezpieczeństwo w praktyce](security-best-practices.md)\n\n\nPamięć podręczna\n----------------\n\n* [Przegląd](caching-overview.md)\n* [Pamięć podręczna danych](caching-data.md)\n* [Pamięć podręczna fragmentów](caching-fragment.md)\n* [Pamięć podręczna stron](caching-page.md)\n* [Pamięć podręczna HTTP](caching-http.md)\n\n\nWebserwisy z wykorzystaniem REST\n--------------------------------\n\n* [Szybki start](rest-quick-start.md)\n* [Zasoby](rest-resources.md)\n* [Kontrolery](rest-controllers.md)\n* [Routing](rest-routing.md)\n* [Formatowanie odpowiedzi](rest-response-formatting.md)\n* [Uwierzytelnianie](rest-authentication.md)\n* [Limit użycia](rest-rate-limiting.md)\n* [Wersjonowanie](rest-versioning.md)\n* [Obsługa błędów](rest-error-handling.md)\n\n\nNarzędzia wspomagające tworzenie aplikacji\n------------------------------------------\n\n* [Pasek debugowania i debuger](https://www.yiiframework.com/extension/yiisoft/yii2-debug/doc/guide)\n* [Generowanie kodu przy użyciu Gii](https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide)\n* [Generowanie dokumentacji API](https://www.yiiframework.com/extension/yiisoft/yii2-apidoc)\n\n\nTestowanie\n----------\n\n* [Przegląd](test-overview.md)\n* [Konfiguracja środowiska testowego](test-environment-setup.md)\n* [Testy jednostkowe](test-unit.md)\n* [Testy funkcjonalnościowe](test-functional.md)\n* [Testy akceptacyjne](test-acceptance.md)\n* [Fixtures](test-fixtures.md)\n\n\nTematy specjalne\n----------------\n\n* [Szablon zaawansowanej aplikacji](https://www.yiiframework.com/extension/yiisoft/yii2-app-advanced/doc/guide)\n* [Tworzenie aplikacji od podstaw](tutorial-start-from-scratch.md)\n* [Komendy konsolowe](tutorial-console.md)\n* [Wbudowane walidatory](tutorial-core-validators.md)\n* [Docker](tutorial-docker.md)\n* [Internacjonalizacja](tutorial-i18n.md)\n* [Wysyłanie poczty](tutorial-mailing.md)\n* [Poprawianie wydajności](tutorial-performance-tuning.md)\n* [Współdzielone środowisko hostingowe](tutorial-shared-hosting.md)\n* [Silniki szablonów](tutorial-template-engines.md)\n* [Praca z kodem zewnętrznym](tutorial-yii-integration.md)\n* [Używanie Yii jako mikroframeworka](tutorial-yii-as-micro-framework.md)\n\n\nWidżety\n-------\n\n* [GridView](https://www.yiiframework.com/doc-2.0/yii-grid-gridview.html)\n* [ListView](https://www.yiiframework.com/doc-2.0/yii-widgets-listview.html)\n* [DetailView](https://www.yiiframework.com/doc-2.0/yii-widgets-detailview.html)\n* [ActiveForm](https://www.yiiframework.com/doc-2.0/guide-input-forms.html#activerecord-based-forms-activeform)\n* [Pjax](https://www.yiiframework.com/doc-2.0/yii-widgets-pjax.html)\n* [Menu](https://www.yiiframework.com/doc-2.0/yii-widgets-menu.html)\n* [LinkPager](https://www.yiiframework.com/doc-2.0/yii-widgets-linkpager.html)\n* [LinkSorter](https://www.yiiframework.com/doc-2.0/yii-widgets-linksorter.html)\n* [Widżety Bootstrapowe](https://www.yiiframework.com/extension/yiisoft/yii2-bootstrap/doc/guide)\n* [Widżety jQuery UI](https://www.yiiframework.com/extension/yiisoft/yii2-jui/doc/guide)\n\n\nKlasy pomocnicze\n----------------\n\n* [Przegląd](helper-overview.md)\n* [ArrayHelper](helper-array.md)\n* [Html](helper-html.md)\n* [Json](helper-json.md)\n* [Url](helper-url.md)\n\n\nUwagi do polskiego tłumaczenia przewodnika\n------------------------------------------\n\nNiektóre z użytych w tym przewodniku programistycznych nazw zostały celowo spolszczone, w przypadku, gdy \nw literaturze popularnej nie występują ich polskie odpowiedniki. Mam nadzieję, że czytelnik wybaczy okazjonalne \n\"settery\", \"gettery\" i \"traity\", które umieszczamy tutaj licząc na powszechne zrozumienie tych terminów w polskiej \nspołeczności programistycznej. Jednocześnie spolszczenia/tłumaczenia niektórych terminów, jak \"Fixtures\", odmawiamy na razie \ncałkowicie, licząc na to, że język polski w końcu nadgoni lub wchłonie, w ten, czy inny sposób, techniczne nowości.\n"
  },
  {
    "path": "docs/guide-pl/blocktypes.json",
    "content": "{\n    \"Warning:\": \"Ostrzeżenie:\",\n    \"Note:\": \"Uwaga:\",\n    \"Info:\": \"Informacja:\",\n    \"Tip:\": \"Wskazówka:\"\n}\n"
  },
  {
    "path": "docs/guide-pl/caching-fragment.md",
    "content": "Pamięć podręczna fragmentów\n===========================\n\nPamięć podręczna fragmentów dotyczy zapisywania w pamięci podręcznej części strony Web. Dla przykładu, jeśli strona wyświetla podsumowanie danych rocznej sprzedaży w postaci tabeli, \nmożna tę tabelę zapisać w pamięci podręcznej, aby wyeliminować konieczność generowania jej za każdym razem od nowa. Mechanizm pamięci podręcznej fragmentów zbudowany jest w oparciu \no [pamięć podręczną danych](caching-data.md).\n\nAby wykorzystać pamięć podręczną fragmentów, należy użyć następującego kodu w [widoku](structure-views.md):\n\n```php\nif ($this->beginCache($id)) {\n\n    // ... generowanie zawartości w tym miejscu ...\n\n    $this->endCache();\n}\n```\n\nJak widać, chodzi tu o zamknięcie bloku generatora zawartości pomiędzy wywołaniem metod [[yii\\base\\View::beginCache()|beginCache()]] i [[yii\\base\\View::endCache()|endCache()]]. \nJeśli wskazana zawartość zostanie odnaleziona w pamięci podręcznej, [[yii\\base\\View::beginCache()|beginCache()]] wyrenderuje zapisaną zawartość i zwróci `false`, przez co pominie \nblok jej generowania. W przeciwnym wypadku generowanie zostanie uruchomione, a w momencie wywołania [[yii\\base\\View::endCache()|endCache()]] wygenerowana zawartość zostanie zapisana \nw pamięci podręcznej.\n\nTak, jak w przypadku [pamięci podręcznej danych](caching-data.md), unikalne `$id` jest wymagane do identyfikacji zawartości.\n\n\n## Opcje zapisu w pamięci podręcznej <span id=\"caching-options\"></span>\n\nMożesz określić dodatkowe opcje zapisu pamięci podręcznej fragmentów, przekazując tablicę opcji jako drugi parametr w metodzie [[yii\\base\\View::beginCache()|beginCache()]]. \nOpcje te będą użyte do skonfigurowania widżetu [[yii\\widgets\\FragmentCache|FragmentCache]], który implementuje właściwą funkcjonalność zapisu pamięci podręcznej.\n\n### Czas życia <span id=\"duration\"></span>\n\nPrawdopodobnie najczęściej używaną opcją zapisu fragmentów jest [[yii\\widgets\\FragmentCache::duration|duration]].\nParametr ten określa, przez ile sekund zawartość może być przechowywana w pamięci podręcznej, zanim konieczne będzie wygenerowanie jej ponownie. Poniższy kod zapisuje fragment \nzawartości w pamięci podręcznej na maksymalnie godzinę:\n\n```php\nif ($this->beginCache($id, ['duration' => 3600])) {\n\n    // ... generowanie zawartości w tym miejscu ...\n\n    $this->endCache();\n}\n```\n\nJeśli ta opcja nie jest określona, przyjmuje domyślną wartość 60, co oznacza, że ważność zapisanej zawartości wygaśnie po upływie 60 sekund.\n\n\n### Zależności <span id=\"dependencies\"></span>\n\nTak, jak w przypadku [pamięci podręcznej danych](caching-data.md#cache-dependencies), zapis fragmentów może opierać się na zależnościach.\nDla przykładu, zawartość wyświetlanego posta zależy od tego, czy został on zmodyfikowany, bądź nie.\n\nAby określić zależność, należy ustawić opcję [[yii\\widgets\\FragmentCache::dependency|dependency]], która może przyjąć postać zarówno obiektu klasy [[yii\\caching\\Dependency|Dependency]], \njak i tablicy konfiguracyjnej, służacej do utworzenia obiektu zależności. Poniższy kod określa pamięć podręczną fragmentu jako zależną od zmiany wartości kolumny `updated_at`:\n\n```php\n$dependency = [\n    'class' => 'yii\\caching\\DbDependency',\n    'sql' => 'SELECT MAX(updated_at) FROM post',\n];\n\nif ($this->beginCache($id, ['dependency' => $dependency])) {\n\n    // ... generowanie zawartości w tym miejscu ...\n\n    $this->endCache();\n}\n```\n\n\n### Wariacje <span id=\"variations\"></span>\n\nZapisana zawartość może mieć kilka wersji, zależnych od niektórych parametrów. Przykładowo, w aplikacji Web wspierającej kilka języków, ten sam fragment kodu w widoku może generować \nzawartość w różnych językach. Z tego powodu wymagana może być konieczność zapisu zawartości w wariacji zależnej od aktualnie wybranego języka aplikacji.\n\nAby określić wariacje pamięci podręcznej, ustaw opcję [[yii\\widgets\\FragmentCache::variations|variations]], która powinna mieć postać tablicy wartości skalarnych, z których każda \nbędzie reprezentować odpowiedni czynnik modyfikujący wersję. Dla prykładu, aby zapisać zawartość w zależności od języka, możesz użyć następującego kodu:\n\n```php\nif ($this->beginCache($id, ['variations' => [Yii::$app->language]])) {\n\n    // ... generowanie zawartości w tym miejscu ...\n\n    $this->endCache();\n}\n```\n\n\n### Warunkowe uruchamianie pamięci podręcznej <span id=\"toggling-caching\"></span>\n\nCzasem konieczne może być uruchamianie pamięci podręcznej fragmentów tylko w przypadku, gdy spełnione są określone warunki. Przykładowo, dla strony zawierającej formularz, \npożądane może być zapisanie i wyświetlenie go z pamięci podręcznej tylko w momencie pierwszego pobrania jego treści (poprzez żądanie GET). Każde kolejne żądanie wyświetlenia formularza \n(już za pomocą metody POST) nie powinno być zapisane w pamięci, ponieważ może zawierać dane podane przez użytkownika.\nAby użyć takiego mechanizmu, należy ustawić opcję [[yii\\widgets\\FragmentCache::enabled|enabled]], jak w przykładzie poniżej:\n\n```php\nif ($this->beginCache($id, ['enabled' => Yii::$app->request->isGet])) {\n\n    // ... generowanie zawartości w tym miejscu ...\n\n    $this->endCache();\n}\n```\n\n\n## Zagnieżdżony zapis w pamięci <span id=\"nested-caching\"></span>\n\nFragmenty zapisane w pamięci podręcznej mogą być zagnieżdżane. Oznacza to, że zapisany fragment może być częścią innego, również zapisanego w pamięci podręcznej.\nPrzykładowo, komentarze mogą być zapisane jako fragmenty w pamięci podręcznej, które z kolei w całości również są zapisane jako większy fragment w pamięci. \nPoniższy kod pokazuje, w jaki sposób można zagnieździć dwa fragmenty w pamięci podręcznej:\n\n```php\nif ($this->beginCache($id1)) {\n\n    // ... generowanie zawartości ...\n\n    if ($this->beginCache($id2, $options2)) {\n\n        // ... generowanie zawartości ...\n\n        $this->endCache();\n    }\n\n    // ... generowanie zawartości ...\n\n    $this->endCache();\n}\n```\n\nZagnieżdżone fragmenty mogą mieć różne opcje zapisu. Dla przykładu, wewnętrzny i zewnętrzy fragment może mieć inną wartość czasu życia. Nawet w przypadku, gdy zawartość zapisana \nw zewnętrznym fragmencie straci ważność, wewnętrzny fragment wciąż będzie pobierany z pamięci. Nie zadziała to jednak w przeciwnym przypadku - dopóki zewnętrzny fragment będzie ważny, \nbędzie zwracał tą samą zawartość za każdym razem, niezależnie od tego, czy zawartość wewnętrznego fragmentu już wygasła, czy nie. Z tego powodu należy zwrócić szczególną ostrożność \nprzy ustalaniu czasu życia lub zależności zagnieżdżonych fragmentów, ponieważ \"stara\" zawartość może być wciąż niezamierzenie przechowywana w zewnętrznym fragmencie.\n\n\n## Dynamiczna zawartość <span id=\"dynamic-content\"></span>\n\nUżywając pamięci podręcznej fragmentów, można napotkać na sytuację, kiedy duża część zawartości strony jest względnie statyczna z wyjątkiem kilku nielicznych miejsc. \nPrzykładowo, nagłówek strony może wyświetlać pasek głównego menu razem z imieniem aktualnie zalogowanego użytkownika. Innym kłopotem może być to, że zapisywana w pamięci zawartość \nmoże zawierać kod PHP, który musi być wykonany dla każdego żądania (np. kod rejestrujący paczkę assetów). W obu tych przypadkach pomoże nam skorzystanie z funkcjonalności tzw. \n*dynamicznej zawartości*.\n\nDynamiczna zawartość oznacza fragment zwrotki, który nie powinien zostać zapisany w pamięci podręcznej, nawet jeśli znajduje się w bloku objętym zapisem. Aby określić zawartość jako \ndynamiczną, musi być ona generowana poprzez wykonanie jakiegoś kodu PHP dla każdego zapytania, nawet jeśli całość fragmentu serwowana jest z pamięci podręcznej.\n\nMożesz wywołać metodę [[yii\\base\\View::renderDynamic()|renderDynamic()]] wewnątrz zapisywanego fragmentu, aby wstawić w danym miejscu zawartość dynamiczną, jak w przykładzie poniżej:\n\n```php\nif ($this->beginCache($id1)) {\n\n    // ... generowanie zawartości ...\n\n    echo $this->renderDynamic('return Yii::$app->user->identity->name;');\n\n    // ... generowanie zawartości ...\n\n    $this->endCache();\n}\n```\n\nMetoda [[yii\\base\\View::renderDynamic()|renderDynamic()]] przyjmuje jako parametr kod PHP.\nWartość zwracana przez ten kod jest traktowana jako zawartość dynamiczna. Ten sam kod PHP będzie wykonany dla każdego zapytania, niezależnie od tego, czy obejmujący go fragment będzie \npobierany z pamięci podręcznej czy też nie.\n"
  },
  {
    "path": "docs/guide-pl/caching-http.md",
    "content": "Pamięć podręczna HTTP\n=====================\n\nOprócz pamięci podręcznej tworzonej po stronie serwera, która została opisana w poprzednich rozdziałach, aplikacje mogą również\nskorzystać z pamięci podręcznej tworzonej po stronie klienta, aby zaoszczędzić czas poświęcany na ponowne generowanie i przesyłanie\nidentycznej zawartości strony.\n\nAby skorzystać z tego mechanizmu, należy skonfigurować [[yii\\filters\\HttpCache]] jako filtr kontrolera akcji, których wyrenderowana\nzwrotka może być zapisana w pamięci podręcznej po stronie klienta. [[yii\\filters\\HttpCache|HttpCache]] obsługuje tylko żądania typu\n`GET` i `HEAD` i dla tych typów tylko trzy nagłówki HTTP związane z pamięcią podręczną:\n\n* [[yii\\filters\\HttpCache::lastModified|Last-Modified]]\n* [[yii\\filters\\HttpCache::etagSeed|Etag]]\n* [[yii\\filters\\HttpCache::cacheControlHeader|Cache-Control]]\n\n\n## Nagłówek `Last-Modified` <span id=\"last-modified\"></span>\n\nNagłówek `Last-Modified` korzysta ze znacznika czasu, aby określić, czy strona została zmodyfikowana od momentu jej ostatniego zapisu\nw pamięci podręcznej.\n\nMożesz skonfigurować właściwość [[yii\\filters\\HttpCache::lastModified]], aby przesyłać nagłowek `Last-Modified`. Właściwość powinna być\ntypu PHP callable i zwracać uniksowy znacznik czasu informujący o czasie modyfikacji strony. Sygnatura metody jest następująca:\n\n```php\n/**\n * @param Action $action aktualnie przetwarzany obiekt akcji\n * @param array $params wartość właściwości \"params\"\n * @return int uniksowy znacznik czasu modyfikacji strony\n */\nfunction ($action, $params)\n```\n\nPoniżej znajdziesz przykład wykorzystania nagłówka `Last-Modified`:\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\HttpCache',\n            'only' => ['index'],\n            'lastModified' => function ($action, $params) {\n                $q = new \\yii\\db\\Query();\n                return $q->from('post')->max('updated_at');\n            },\n        ],\n    ];\n}\n```\n\nKod ten uruchamia pamięć podręczną HTTP wyłącznie dla akcji `index`, która powinna wygenerować nagłówek HTTP `Last-Modified` oparty\no datę ostatniej aktualizacji postów. Przeglądarka, wyświetlając stronę `index` po raz pierwszy, otrzymuje jej zawartość wygenerowaną\nprzez serwer; każda kolejna wizyta, przy założeniu, że żaden post nie został zmodyfikowany w międzyczasie, skutkuje wyświetleniem\nwersji strony przechowywanej w pamięci podręcznej po stronie klienta, zamiast generować ją ponownie przez serwer.\nW rezultacie, renderowanie zawartości po stronie serwera i przesyłanie jej do klienta jest pomijane.\n\n\n## Nagłowek `ETag` <span id=\"etag\"></span>\n\nNagłowek \"Entity Tag\" (lub w skrócie `ETag`) wykorzystuje skrót hash jako reprezentację strony. W momencie, gdy strona się zmieni, jej\nhash również automatycznie ulega zmianie. Porównując hash przechowywany po stronie klienta z hashem wygenerowanym przez serwer,\nmechanizm pamięci podręcznej ustala, czy strona się zmieniła i powinna być ponownie przesłana.\n\nMożesz skonfigurować właściwość [[yii\\filters\\HttpCache::etagSeed]], aby przesłać nagłowek `ETag`.\nWłaściwość powinna być typu PHP callable i zwracać ziarno do wygenerowania hasha ETag. Sygnatura metody jest następująca:\n\n```php\n/**\n * @param Action $action aktualnie przetwarzany obiekt akcji\n * @param array $params wartość właściwości \"params\"\n * @return string łańcuch znaków użyty do generowania hasha ETag\n */\nfunction ($action, $params)\n```\n\nPoniżej znajdziesz przykład użycia nagłówka `ETag`:\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\HttpCache',\n            'only' => ['view'],\n            'etagSeed' => function ($action, $params) {\n                $post = $this->findModel(\\Yii::$app->request->get('id'));\n                return serialize([$post->title, $post->content]);\n            },\n        ],\n    ];\n}\n```\n\nKod ten uruchamia pamięć podręczną HTTP wyłącznie dla akcji `view`, która powinna wygenerować nagłówek HTTP `ETag` oparty o tytuł\ni zawartość przeglądanego posta. Przeglądarka, wyświetlając stronę `view` po raz pierwszy, otrzymuje jej zawartość wygenerowaną\nprzez serwer; każda kolejna wizyta, przy założeniu, że ani tytuł, ani zawartość posta nie została zmodyfikowana w międzyczasie,\nskutkuje wyświetleniem wersji strony przechowywanej w pamięci podręcznej po stronie klienta, zamiast generować ją ponownie przez\nserwer.\nW rezultacie, renderowanie zawartości po stronie serwera i przesyłanie jej do klienta jest pomijane.\n\nETagi pozwalają na bardziej skomplikowane i precyzyjne strategie przechowywania w pamięci podręcznej niż nagłówki `Last-Modified`.\nDla przykładu, ETag może być zmieniony dla strony w momencie, gdy użyty na niej będzie nowy szablon wyglądu.\n\nZasobożerne generowanie ETagów może przekreślić cały zysk z użycia `HttpCache` i wprowadzić niepotrzebny narzut, ponieważ muszą być one\nokreślane przy każdym żądaniu. Z tego powodu należy używać jak najprostszych metod generujących.\n\n> Note: Aby spełnić wymagania [RFC 7232](https://datatracker.ietf.org/doc/html/rfc7232#section-2.4),\n  `HttpCache` przesyła zarówno nagłówek `ETag`, jak i `Last-Modified`, jeśli oba są skonfigurowane.\n  Jeśli klient wysyła nagłówek `If-None-Match` razem z `If-Modified-Since`, tylko pierwszy z nich jest brany pod uwagę.\n\n\n## Nagłówek `Cache-Control` <span id=\"cache-control\"></span>\n\nNagłówek `Cache-Control` określa ogólną politykę obsługi pamięci podręcznej stron. Możesz go przesłać konfigurując właściwość\n[[yii\\filters\\HttpCache::cacheControlHeader]] z wartością nagłówka. Domyślnie przesyłany jest następujący nagłówek:\n\n```\nCache-Control: public, max-age=3600\n```\n\n## Ogranicznik pamięci podręcznej sesji <span id=\"session-cache-limiter\"></span>\n\nKiedy strona używa mechanizmu sesji, PHP automatycznie wysyła związane z pamięcią podręczną nagłówki HTTP, określone\nw `session.cache_limiter` w ustawieniach PHP INI. Mogą one kolidować z funkcjonalnością `HttpCache`, a nawet całkowicie ją wyłączyć -\naby temu zapobiec, `HttpCache` blokuje to automatyczne wysyłanie. Jeśli jednak chcesz zmienić to zachowanie, powinieneś skonfigurować\nwłaściwość [[yii\\filters\\HttpCache::sessionCacheLimiter]]. Powinna ona przyjmować wartość zawierającą łańcuch znaków `public`,\n`private`, `private_no_expire` i `nocache`. Szczegóły dotyczące tego zapisu znajdziesz w dokumentacji PHP dla\n[session_cache_limiter()](https://www.php.net/manual/pl/function.session-cache-limiter.php).\n\n\n## Korzyści dla SEO <span id=\"seo-implications\"></span>\n\nBoty silników wyszukiwarek zwykle respektują ustawienia nagłówków pamięci podręcznej. Niektóre automaty mają limit ilości stron\nzaindeksowanych w pojedynczej domenie w danej jednostce czasu, dlatego też wprowadzenie nagłówków dla pamięci podręcznej może\nw znaczącym stopniu przyspieszyć cały proces indeksacji, poprzez redukcję ilości stron, które trzeba przeanalizować.\n"
  },
  {
    "path": "docs/guide-pl/caching-overview.md",
    "content": "Pamięć podręczna\n================\n\nMechanizmy wykorzystujące pamięć podręczną pozwalają na poprawienie wydajności aplikacji sieciowej w tani i efektywny sposób. \nZapisanie statycznych danych w pamięci podręcznej, zamiast generowania ich od podstaw przy każdym wywołaniu, pozwala na znaczne zaoszczędzenie czasu odpowiedzi aplikacji.\n\nZapis pamięci podręcznej może odbywać się na wielu poziomach i w wielu miejscach aplikacji. Po stronie serwera, na niskim poziomie, \nmożna wykorzystać pamięć podręczną do zapisania podstawowych danych, takich jak zbiór informacji o najnowszych artykułach pobieranych z bazy danych. \nNa wyższym poziomie, pamięci podręcznej można użyć do przechowania części bądź całości strony www, na przykład w postaci rezultatu wyrenderowania \nlisty ww. najświeższych artykułów. Po stronie klienta, pamięć podręczna HTTP przeglądarki może zapisać zawartość ostatnio odwiedzonej strony.\n\nYii wpiera wszystkie te mechanizmy zapisu w pamięci podręcznej:\n\n* [Pamięć podręczna danych](caching-data.md)\n* [Pamięć podręczna fragmentów](caching-fragment.md)\n* [Pamięć podręczna stron](caching-page.md)\n* [Pamięć podręczna HTTP](caching-http.md)\n"
  },
  {
    "path": "docs/guide-pl/caching-page.md",
    "content": "Pamięć podręczna stron\n======================\n\nPamięć podręczna stron odnosi się do zapisu zawartości całej strony po stronie serwera. Kiedy zostanie ona ponownie wywołana, \nzawartość zostanie wyświetlona od razu z pamięci podręcznej zamiast generować ją ponownie od podstaw.\n\nPamięć podręczna stron jest obsługiwana przez [filtr akcji](structure-filters.md) [[yii\\filters\\PageCache|PageCache]].\nPoniżej znajdziesz przykładowy sposób użycia go w klasie kontrolera:\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\PageCache',\n            'only' => ['index'],\n            'duration' => 60,\n            'variations' => [\n                \\Yii::$app->language,\n            ],\n            'dependency' => [\n                'class' => 'yii\\caching\\DbDependency',\n                'sql' => 'SELECT COUNT(*) FROM post',\n            ],\n        ],\n    ];\n}\n```\n\nW powyższym przykładzie zakładamy użycie pamięci podręcznej tylko dla akcji `index` - zawartość strony powinna zostać zapisana na maksymalnie \n60 sekund i powinna różnić się w zależności od wybranego w aplikacji języka. Dodatkowo, jeśli całkowita liczba postów w bazie danych ulegnie zmianie, \nzawartość pamięci powinna natychmiast stracić ważność i zostać pobrana ponownie.\n\nJak widać, pamięć podręczna stron jest bardzo podobna do [pamięci podręcznej fragmentów](caching-fragment.md). W obu przypadkach można \nużyć opcji takich jak `duration` (czas ważności), `dependencies` (zależności), `variations` (warianty) oraz `enabled` (flaga aktywowania). \nGłówną różnicą w tych dwóch przypadkach jest to, że pamięć podręczna stron jest implemetowana jako [filtr akcji](structure-filters.md), a \npamięć podręczna fragmentów jako [widżet](structure-widgets.md).\n\nOczywiście nic nie stoi na przeszkodzie, aby używać [pamięci podręcznej fragmentów](caching-fragment.md) jak \ni [zawartości dynamicznej](caching-fragment.md#dynamic-content) w połączeniu z pamięcią podręczną stron.\n\n"
  },
  {
    "path": "docs/guide-pl/concept-aliases.md",
    "content": "Aliasy\n======\n\nAliasy używane są do reprezentowania ścieżek do plików lub adresów URL i pozwalają uniknąć konieczności wielokrotnego definiowania ich w kodzie aplikacji. Alias musi zaczynać się od znaku `@`, dla odróżnienia od zwykłej ścieżki i adresu URL. W przypadku zdefiniowania aliasu bez tego znaku, będzie on automatycznie dodany na początku.\n\nYii korzysta z wielu predefiniowanych aliasów. Dla przykładu, alias `@yii` reprezentuje ścieżkę instalacji frameworka, a `@web` bazowy adres URL aktualnie uruchomionej aplikacji Web.\n\n\nDefiniowanie aliasów <span id=\"defining-aliases\"></span>\n--------------------\n\nMożesz zdefiniować alias do ścieżki pliku lub adresu URL wywołując [[Yii::setAlias()]]:\n\n```php\n// alias do ścieżki pliku\nYii::setAlias('@foo', '/path/to/foo');\n\n// alias do adresu URL\nYii::setAlias('@bar', 'https://www.example.com');\n\n// alias istniejącego pliku, zawierającego klasę \\foo\\Bar\nYii::setAlias('@foo/Bar.php', '/zdecydowanie/nie/foo/Bar.php');\n```\n\n> Note: *nie* jest konieczne, aby aliasowana ścieżka pliku lub URL wskazywał istniejący plik lub zasób.\n\nMając już zdefiniowany alias, możesz rozbudować go, tworząc nowy alias (bez konieczności wywołania [[Yii::setAlias()]]), \ndodając ukośnik `/` i kolejne segmenty ścieżki. Aliasy zdefiniowane za pomocą [[Yii::setAlias()]] nazywane są *bazowymi aliasami*, a te, które je rozbudowują, *aliasami pochodnymi*. Dla przykładu, `@foo` jest aliasem bazowym, a `@foo/bar/file.php` pochodnym.\n\nMożesz definiować aliasy używając innych aliasów (zarówno bazowych, jak i pochodnych):\n\n```php\nYii::setAlias('@foobar', '@foo/bar');\n```\n\nAliasy bazowe są zwykle definiowane podczas fazy [bootstrappingu](runtime-bootstrapping.md).\nMożliwe jest wywołanie [[Yii::setAlias()]] już w [skrypcie wejściowym](structure-entry-scripts.md).\n[Aplikacja](structure-applications.md) dla wygody deweloperów posiada właściwość `aliases`, którą można zmodyfikować \nw [konfiguracji](concept-configurations.md):\n\n```php\nreturn [\n    // ...\n    'aliases' => [\n        '@foo' => '/path/to/foo',\n        '@bar' => 'https://www.example.com',\n    ],\n];\n```\n\n\nRozwiązywanie aliasów <span id=\"resolving-aliases\"></span>\n---------------------\n\nMożesz wywołać [[Yii::getAlias()]], aby rozwiązać alias, czyli zamienić go na ścieżkę pliku lub adres URL, który reprezentuje. \nDotyczy to zarówno bazowych aliasów, jak i pochodnych:\n\n```php\necho Yii::getAlias('@foo');               // wyświetla: /ścieżka/do/foo\necho Yii::getAlias('@bar');               // wyświetla: https://www.example.com\necho Yii::getAlias('@foo/bar/file.php');  // wyświetla: /ścieżka/do/foo/bar/file.php\n```\n\nŚcieżka/URL reprezentowany przez pochodny alias jest ustalany poprzez zamianę części z bazowym aliasem na jego rozwiązaną \nreprezentację.\n\n> Note: Metoda [[Yii::getAlias()]] nie sprawdza, czy reprezentowana ścieżka/URL wskazuje na istniejący plik lub zasób.\n\nAlias bazowy może również zawierać ukośnik `/`. Metoda [[Yii::getAlias()]] potrafi określić, która część aliasu jest aliasem bazowym i prawidłowo określić odpowiadającą mu ścieżkę pliku lub URL:\n\n```php\nYii::setAlias('@foo', '/path/to/foo');\nYii::setAlias('@foo/bar', '/path2/bar');\nYii::getAlias('@foo/test/file.php');  // wyświetla: /path/to/foo/test/file.php\nYii::getAlias('@foo/bar/file.php');   // wyświetla: /path2/bar/file.php\n```\n\nGdyby `@foo/bar` nie był zdefiniowany jako bazowy alias, ostatnia instrukcja wyświetliłaby `/path/to/foo/bar/file.php`.\n\n\nKorzystanie z aliasów <span id=\"using-aliases\"></span>\n---------------------\n\nAliasy są rozwiązywane automatycznie w wielu miejscach w Yii bez konieczności wywołania bezpośrednio metody [[Yii::getAlias()]]. \nPrzykładowo, [[yii\\caching\\FileCache::cachePath]] akceptuje zarówno ścieżkę pliku, jak i alias ją reprezentujący, odróżniając je \nod siebie dzięki prefiksowi `@`.\n\n```php\nuse yii\\caching\\FileCache;\n\n$cache = new FileCache([\n    'cachePath' => '@runtime/cache',\n]);\n```\n\nAby sprawdzić, czy dana właściwość lub parametr metody wspierają użycie aliasów, zapoznaj się z dokumentacją API.\n\n\nPredefiniowane aliasy <span id=\"predefined-aliases\"></span>\n---------------------\n\nYii predefiniuje zestaw aliasów do łatwego wskazywania często używanych ścieżek plików i adresów URL:\n\n- `@yii`, folder, w którym znajduje się plik `BaseYii.php` (nazywany także folderem frameworka).\n- `@app`, [[yii\\base\\Application::basePath|bazowa ścieżka]] aktualnie używanej aplikacji.\n- `@runtime`, [[yii\\base\\Application::runtimePath|ścieżka cyklu życia]] aktualnie używanej aplikacji. Domyślnie wskazuje na \n  `@app/runtime`.\n- `@webroot`, folder bazowy Web aktualnie używanej aplikacji Web. Określany jest jako lokalizacja folderu zawierającego \n  [skrypt wejścia](structure-entry-scripts.md).\n- `@web`, bazowy adres URL aktualnie używanej aplikacji Web. Wskazuje na tą samą wartość co [[yii\\web\\Request::baseUrl]].\n- `@vendor`, [[yii\\base\\Application::vendorPath|folder pakietów composera]]. Domyślnie wskazuje na `@app/vendor`.\n- `@bower`, bazowy folder zawierający [pakiety bowera](https://bower.io/). Domyślnie wskazuje na `@vendor/bower`.\n- `@npm`, bazowy folder zawierający [pakiety npm](https://www.npmjs.com/). Domyślnie wskazuje na `@vendor/npm`.\n\nAlias `@yii` jest definiowany poprzez dołączenie pliku `Yii.php` w [skrypcie wejścia](structure-entry-scripts.md).\nPozostałe aliasy są definiowane w konstruktorze aplikacji podczas ładowania [konfiguracji](concept-configurations.md).\n\n> Note: aliasy `@web` i `@webroot`, zgodnie z ich opisami, są zdefiniowane w [[yii\\web\\Application|aplikacji Web]] i z tego powodu niedostępne domyślnie w [[yii\\console\\Application|aplikacji konsolowej]].\n\nAliasy rozszerzeń <span id=\"extension-aliases\"></span>\n-----------------\n\nInstalacja [rozszerzenia](structure-extensions.md) za pomocą composera automatycznie definiuje dla niego alias.\nKażdy z takich aliasów jest nazwany zgodnie z bazową przestrzenią nazw rozszerzenia określonej w swoim pliku `composer.json` \ni reprezentuje bazowy folder pakietu. Dla przykładu, instalując rozszerzenie `yiisoft/yii2-jui` automatycznie uzyskasz alias \n`@yii/jui` zdefiniowany podczas fazy [bootstrappingu](runtime-bootstrapping.md), będący odpowiednikiem wywołania:\n\n```php\nYii::setAlias('@yii/jui', 'VendorPath/yiisoft/yii2-jui');\n```\n"
  },
  {
    "path": "docs/guide-pl/concept-autoloading.md",
    "content": "Autoładowanie klas\n==================\n\nYii opiera się na [mechanizmie automatycznego ładowania klas](https://www.php.net/manual/pl/language.oop5.autoload.php) służącym do \nzlokalizowania i dołączenia wszystkich wymaganych plików klas. Wbudowany wysoce wydajny autoloader klas, zgodny ze \n[standardem PSR-4](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md), jest instalowany po załączeniu \npliku `Yii.php`.\n\n> Note: Dla uproszczenia opisów, w tej sekcji zostanie omówione jedynie autoładowanie klas. Należy mieć jednak na uwadze, że poniższe \n  informacje odnoszą się również do autoładowania interfejsów i traitów.\n\n\nKorzystanie z autoloadera Yii <span id=\"using-yii-autoloader\"></span>\n-----------------------------\n\nAby skorzystać z autoloadera klas Yii, powinieneś przestrzegać dwóch prostych zasad tworzenia i nazywania własnych klas:\n\n* Każda klasa musi znajdować się w [przestrzeni nazw](https://www.php.net/manual/pl/language.namespaces.php) (np. `foo\\bar\\MyClass`)\n* Każda klasa musi być zapisana jako oddzielny plik, do którego ścieżka określona jest poniższym algorytmem:\n\n```php\n// $className jest w pełni uściśloną nazwą klasy bez początkowego odwrotnego ukośnika\n$classFile = Yii::getAlias('@' . str_replace('\\\\', '/', $className) . '.php');\n```\n\nPrzykładowo, jeśli nazwa klasy i przestrzeń nazw to `foo\\bar\\MyClass`, odpowiadającym ścieżce pliku klasy [aliasem](concept-aliases.md) \njest `@foo/bar/MyClass.php`. Aby ten alias mógł być przetłumaczony na ścieżkę pliku, `@foo` lub `@foo/bar` musi być \n[aliasem bazowym](concept-aliases.md#defining-aliases).\n\nUżywając [podstawowego szablonu projektu](start-installation.md), możesz umieścić swoje klasy w bazowej przestrzeni nazw `app`, dzięki \nczemu mogą być autoładowane przez Yii bez potrzeby definiowania nowego aliasu. Dzieje się tak dzięki temu, że `@app` jest \n[predefiniowanym aliasem](concept-aliases.md#predefined-aliases) i klasa `app\\components\\MyClass` może być odszukana w pliku \n`AppBasePath/components/MyClass.php`, zgodnie z opisanym algorytmem.\n\nW [zaawansowanym szablonie projektu](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md) każdy poziom \naplikacji posiada swój własny bazowy alias. Dla przykładu, front-end określony jest przez bazowy alias `@frontend`, a back-end - \n`@backend`. Dzięki temu możesz umieścić klasy front-endu w przestrzeni nazw `frontend`, a klasy back-endu w przestrzeni nazw `backend`. \nWszystkie te klasy będą automatycznie załadowane przez autoloader Yii.\n\n\nMapa klas <span id=\"class-map\"></span>\n---------\n\nAutoloader klas Yii wspiera mechanizm *mapy klas*, która mapuje nazwy klas do odpowiadających im ścieżek plików. \nKiedy autoloader ładuje klasę, najpierw sprawdza czy klasa znajduje się w mapie. Jeśli tak, odpowiadająca nazwie ścieżka pliku zostanie \ndołączona od razu, bez dalszej weryfikacji, co jest powodem, dla którego autoładowanie klas jest błyskawiczne. Wszystkie podstawowe \nklasy Yii są autoładowane właśnie w ten sposób.\n\nMożesz dodać klasę do mapy klas, przechowywanej w `Yii::$classMap`, za pomocą instrukcji:\n\n```php\nYii::$classMap['foo\\bar\\MyClass'] = 'path/to/MyClass.php';\n```\n\nDo określenia ścieżek plików klas można użyć [aliasów](concept-aliases.md). Zapisywanie mapy klas powinno odbywać się w procesie \n[bootstrappingu](runtime-bootstrapping.md), aby mapa była gotowa zanim rozpocznie się korzystanie z klas.\n\n\nKorzystanie z innych autoloaderów <span id=\"using-other-autoloaders\"></span>\n---------------------------------\n\nPonieważ Yii opiera się głównie na composerze, jako menedżerze pakietów zależności, zalecane jest również zainstalowanie autoloadera \ncomposera. Jeśli używasz zewnętrznych bibliotek, korzystających z własnych autoloaderów, powinieneś również je zainstalować.\n\nUżywając autoloadera Yii razem z innymi autoloaderami, powinieneś dołączyć plik `Yii.php` *po* wszystkich pozostałych autoloaderach. Dzięki temu \nautoloader Yii jako pierwszy odpowie na żądanie autoładowania klasy. Dla przykładu, poniższy kod znajduje się \nw [skrypcie wejściowym](structure-entry-scripts.md) [podstawowego szablonu projektu](start-installation.md). Pierwsza linia jest \ninstrukcją instalacji autoloadera composera, a druga instaluje autoloader Yii:\n\n```php\nrequire __DIR__ . '/../vendor/autoload.php';\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n```\n\nMożesz używać jedynie autoloadera composera bez autoloadera Yii, ale wydajność autoładowania klas może być wtedy obniżona i, dodatkowo, \nmusisz przestrzegać zasad ustalonych przez composera, aby Twoje klasy mogły być autoładowane.\n\n> Info: Jeśli nie chcesz korzystać z autoloadera Yii, musisz stworzyć swoją własną wersję pliku `Yii.php` i dołączyć ją \n  w [skrypcie wejściowym](structure-entry-scripts.md).\n\n\nAutoładowanie klas rozszerzeń <span id=\"autoloading-extension-classes\"></span>\n-----------------------------\n\nAutoloader Yii potrafi również automatycznie ładować klasy [rozszerzeń](structure-extensions.md). Jedynym wymaganiem ze strony \nrozszerzenia jest prawidłowy zapis sekcji `autoload` w swoim pliku `composer.json`. Szczegóły na temat specyfikacji `autoload` znajdują \nsię [dokumentacji composera](https://getcomposer.org/doc/04-schema.md#autoload).\n\nJeśli nie korzystasz z autoloadera Yii, autoloader composera załaduje dla Ciebie automatycznie klasy rozszerzeń.\n"
  },
  {
    "path": "docs/guide-pl/concept-behaviors.md",
    "content": "Behaviory\n=========\n\nBehaviory są instancjami klasy [[yii\\base\\Behavior]] lub jej pochodnych. Behaviory, zwane także \n[domieszkami](https://pl.wikipedia.org/wiki/Domieszka_(programowanie_obiektowe)), pozwalają na wzbogacenie funkcjonalności \njuż istniejącej klasy [[yii\\base\\Component|komponentu]] bez konieczności modyfikacji jej struktury dziedziczenia.  \nDołączenie behavioru \"wstrzykuje\" jego metody i właściwości do komponentu, dzięki czemu są one dostępne w taki sam sposób, \njakby były zdefiniowane od razu w klasie komponentu. Ponadto behavior może reagować na [eventy](concept-events.md) wywołane \nprzez komponent, co pozwala na modyfikowanie sposobu, w jaki kod komponentu jest wykonywany.\n\n\nDefiniowane behaviorów <span id=\"defining-behaviors\"></span>\n----------------------\n\nAby zdefiniować behavior, stwórz klasę, która rozszerza [[yii\\base\\Behavior]] lub jej klasę potomną. Przykładowo:\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Behavior;\n\nclass MyBehavior extends Behavior\n{\n    public $prop1;\n\n    private $_prop2;\n\n    public function getProp2()\n    {\n        return $this->_prop2;\n    }\n\n    public function setProp2($value)\n    {\n        $this->_prop2 = $value;\n    }\n\n    public function foo()\n    {\n        // ...\n    }\n}\n```\n\nPowyższy kod definiuje klasę behavioru `app\\components\\MyBehavior` z dwoma właściwościami `prop1` i `prop2` oraz jedną metodą \n`foo()`. Zwróć uwagę na to, że właściwość `prop2` jest zdefiniowana poprzez getter `getProp2()` i setter `setProp2()`. \nJest to możliwe dzięki temu, że [[yii\\base\\Behavior]] rozszerza [[yii\\base\\BaseObject]], przez co ma możliwość definiowania \n[właściwości](concept-properties.md) za pomocą getterów i setterów.\n\nKomponent, po załączeniu tego behavioru, będzie również posiadał właściwości `prop1` i `prop2` oraz metodę `foo()`.\n\n> Tip: Wewnątrz behavioru możesz odwołać się do komponentu, do którego jest on załączony, przez właściwość \n  [[yii\\base\\Behavior::owner]].\n\n> Note: Jeśli nadpisujesz metody [[yii\\base\\Behavior::__get()]] i/lub [[yii\\base\\Behavior::__set()]] behavioru, musisz również \n  nadpisać [[yii\\base\\Behavior::canGetProperty()]] i/lub [[yii\\base\\Behavior::canSetProperty()]].\n\nObsługa eventów komponentu\n--------------------------\n\nJeśli behavior powinien reagować na eventy wywołane przez komponent, do którego jest załączony, należy nadpisać jego metodę \n[[yii\\base\\Behavior::events()]]. Dla przykładu:\n\n```php\nnamespace app\\components;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\base\\Behavior;\n\nclass MyBehavior extends Behavior\n{\n    // ...\n\n    public function events()\n    {\n        return [\n            ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',\n        ];\n    }\n\n    public function beforeValidate($event)\n    {\n        // ...\n    }\n}\n```\n\nMetoda [[yii\\base\\Behavior::events()|events()]] powinna zwrócić listę eventów i odpowiadających im uchwytów.\nPowyższy przykład deklaruje, że event [[yii\\db\\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]] istnieje i jego \nuchwytem jest metoda `beforeValidate()`. Do określenia uchwytów eventów możesz użyć następujących formatów:\n\n* łańcuch znaków odnoszący się do nazwy metody w klasie behavioru, jak w przykładzie powyżej,\n* tablica obiektu lub nazwy klasy i nazwy metody w postaci łańcucha znaków (bez nawiasów), np. `[$obiekt, 'nazwaMetody']`,\n* funkcja anonimowa.\n\nSygnatura funkcji uchwytu eventu powinna wyglądać jak poniżej, gdzie `$event` odwołuje się do obsługiwanego eventu. \nW sekcji [Eventy](concept-events.md) znajdziesz więcej szczegółów dotyczących samych eventów.\n\n```php\nfunction ($event) {\n}\n```\n\nZałączanie behaviorów <span id=\"attaching-behaviors\"></span>\n---------------------\n\nMożesz załączyć behavior do [[yii\\base\\Component|komponentu]] zarówno statycznie, jak i dynamicznie. Pierwszy sposób jest \nczęściej wykorzystywany w praktyce.\n\nAby załączyć behavior statycznie, nadpisz metodę [[yii\\base\\Component::behaviors()|behaviors()]] w klasie komponentu, do której \nbehavior ma być załączony. Metoda [[yii\\base\\Component::behaviors()|behaviors()]] powinna zwracać listę \n[konfiguracji](concept-configurations.md) behaviorów.  \nKażda konfiguracja behavioru może być zarówno nazwą klasy behavioru jak i tablicą konfiguracyjną:\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\nuse app\\components\\MyBehavior;\n\nclass User extends ActiveRecord\n{\n    public function behaviors()\n    {\n        return [\n            // anonimowy behavior, tylko nazwa klasy behavioru\n            MyBehavior::class,\n\n            // imienny behavior, tylko nazwa klasy behavioru\n            'myBehavior2' => MyBehavior::class,\n\n            // anonimowy behavior, tablica konfiguracyjna\n            [\n                'class' => MyBehavior::class,\n                'prop1' => 'value1',\n                'prop2' => 'value2',\n            ],\n\n            // imienny behavior, tablica konfiguracyjna\n            'myBehavior4' => [\n                'class' => MyBehavior::class,\n                'prop1' => 'value1',\n                'prop2' => 'value2',\n            ],\n        ];\n    }\n}\n```\n\nMożesz przypisać konkretną nazwę dla behavioru, definiując klucz tablicy odpowiadający jego konfiguracji - w tym przypadku \nmówimy o *imiennym behaviorze*. W powyższym przykładzie znajdują się dwa imienne behaviory: `myBehavior2` i `myBehavior4`. \nJeśli behavior nie ma przypisanej nazwy, nazywamy go *anonimowym*.\n\n\nAby załączyć behavior dynamicznie, wywołaj metodę [[yii\\base\\Component::attachBehavior()]] na komponencie, do którego behavior \nma być załączony:\n\n```php\nuse app\\components\\MyBehavior;\n\n// załącz obiekt behavioru\n$component->attachBehavior('myBehavior1', new MyBehavior);\n\n// załącz klasę behavioru\n$component->attachBehavior('myBehavior2', MyBehavior::class);\n\n// załącz tablicę konfiguracyjną\n$component->attachBehavior('myBehavior3', [\n    'class' => MyBehavior::class,\n    'prop1' => 'value1',\n    'prop2' => 'value2',\n]);\n```\n\nMożesz załączyć wiele behaviorów jednocześnie, korzystając z metody [[yii\\base\\Component::attachBehaviors()]]:\n\n```php\n$component->attachBehaviors([\n    'myBehavior1' => new MyBehavior,  // imienny behavior\n    MyBehavior::class,          // anonimowy behavior\n]);\n```\n\nMożliwe jest również załączenie behaviorów poprzez [konfigurację](concept-configurations.md), jak widać to poniżej: \n\n```php\n[\n    'as myBehavior2' => MyBehavior::class, // zwróć uwagę na konstrukcję \"as nazwaBehavioru\"\n\n    'as myBehavior3' => [\n        'class' => MyBehavior::class,\n        'prop1' => 'value1',\n        'prop2' => 'value2',\n    ],\n]\n```\n\nWięcej szczegółów znajdziesz w sekcji [Konfiguracje](concept-configurations.md#configuration-format).\n\n\nKorzystanie z behaviorów <span id=\"using-behaviors\"></span>\n------------------------\n\nAby użyć behavioru, najpierw załącz go do [[yii\\base\\Component|komponentu]] zgodnie z powyższymi instrukcjami. Kiedy behavior \njest już załączony, korzystanie z niego jest bardzo proste.\n\nMożesz uzyskać dostęp do *publicznej* zmiennej lub [właściwości](concept-properties.md) zdefiniowanej przez getter i/lub setter \nbehavioru z poziomu komponentu, do którego jest on załączony:\n\n```php\n// \"prop1\" jest właściwością zdefiniowaną w klasie behavioru\necho $component->prop1;\n$component->prop1 = $value;\n```\n\nMożesz również wywołać *publiczną* metodę behavioru w podobny sposób:\n\n```php\n// foo() jest publiczną metodą zdefiniowaną w klasie behavioru\n$component->foo();\n```\n\nJak widać, pomimo że `$component` nie definiuje `prop1` ani `foo()`, można ich użyć tak, jakby były zdefiniowane przez \nkomponent, dzięki załączonemu behaviorowi.\n\nJeśli dwa behaviory definiują tą samą właściwość lub metodę i oba są załączone do tego samego komponentu, behavior, który \nzostał załączony jako *pierwszy*, będzie obsługiwał wywołaną właściwość lub metodę.\n\nBehavior może być powiązany z konkretną nazwą podczas załączania do komponentu - w takim przypadku można odwołać się do \nobiektu behavioru, korzystając z jego nazwy:\n\n```php\n$behavior = $component->getBehavior('myBehavior');\n```\n\nMożna również uzyskać listę wszystkich behaviorów załączonych do komponentu:\n\n```php\n$behaviors = $component->getBehaviors();\n```\n\n\nOdłączanie behaviorów <span id=\"detaching-behaviors\"></span>\n---------------------\n\nAby odłączyć behavior, wywołaj metodę [[yii\\base\\Component::detachBehavior()]] z nazwą przypisaną temu behaviorowi:\n\n```php\n$component->detachBehavior('myBehavior1');\n```\n\nMożna również odłączyć *wszystkie* behaviory jednocześnie:\n\n```php\n$component->detachBehaviors();\n```\n\n\nKorzystanie z `TimestampBehavior` <span id=\"using-timestamp-behavior\"></span>\n---------------------------------\n\nBehavior [[yii\\behaviors\\TimestampBehavior]] pozwala na automatyczne aktualizowanie atrybutów znaczników czasu dla modelu \n[[yii\\db\\ActiveRecord|Active Record]] za każdym razem, gdy model jest zapisywany za pomocą metod `insert()`, `update()` lub \n`save()`.\n\nZałącz ten behavior do klasy [[yii\\db\\ActiveRecord|Active Record]], której chcesz użyć:\n\n```php\nnamespace app\\models\\User;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\behaviors\\TimestampBehavior;\n\nclass User extends ActiveRecord\n{\n    // ...\n\n    public function behaviors()\n    {\n        return [\n            [\n                'class' => TimestampBehavior::class,\n                'attributes' => [\n                    ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],\n                    ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'],\n                ],\n                // opcjonalnie jeśli używasz kolumny typu datetime zamiast uniksowego znacznika czasu:\n                // 'value' => new Expression('NOW()'),\n            ],\n        ];\n    }\n}\n```\n\nPowyższa konfiguracja behavioru określa, co powinno stać się z wierszem danych podczas:\n\n* dodawania; behavior powinien ustawić aktualny uniksowy znacznik czasu dla atrybutów `created_at` i `updated_at`.\n* aktualizacji; behavior powinien ustawić aktualny uniksowy znacznik czasu dla atrybutu `updated_at`.\n\n> Note: Aby powyższa implementacja zadziałała dla bazy danych MySQL, zadeklaruj kolumny (`created_at`, `updated_at`) jako \n  int(11) na potrzeby przechowania uniksowego znacznika czasu.\n\nZ tak wprowadzonym kodem, po zapisie obiektu `User` zobaczysz, że jego atrybuty `created_at` i `updated_at` zostały \nautomatycznie ustawione na aktualny uniksowy znacznik czasu:\n\n```php\n$user = new User;\n$user->email = 'test@example.com';\n$user->save();\necho $user->created_at;  // wyświetli znacznik czasu z momentu zapisu\n```\n\n[[yii\\behaviors\\TimestampBehavior|TimestampBehavior]] oferuje również użyteczną metodę \n[[yii\\behaviors\\TimestampBehavior::touch()|touch()]], która ustawia aktualny znacznik czasu określonemu atrybutowi i zapisuje \ngo w bazie danych:\n\n```php\n$user->touch('login_time');\n```\n\nInne behaviory\n--------------\n\nPoniżej znajdziesz kilka behaviorów wbudowanych lub też dostępnych w zewnętrznych bibliotekach:\n\n- [[yii\\behaviors\\BlameableBehavior]] - automatycznie wypełnia wskazane atrybuty ID aktualnego użytkownika.\n- [[yii\\behaviors\\SluggableBehavior]] - automatycznie wypełnia wskazany atrybut wartością, która może być użyta jako poprawna \n  część adresu URL (slug).\n- [[yii\\behaviors\\AttributeBehavior]] - automatycznie ustawia określoną wartość jednemu lub więcej atrybutom obiektu \n  ActiveRecord w momencie wystąpienia konkretnych eventów.\n- [yii2tech\\ar\\softdelete\\SoftDeleteBehavior](https://github.com/yii2tech/ar-softdelete) - udostępnia metody do \n  \"miękkiego usunięcia\" ActiveRecordu i przywrócenia go z powrotem np. poprzez ustawienie flagi lub statusu oznaczającego \n  rekord jako usunięty.\n- [yii2tech\\ar\\position\\PositionBehavior](https://github.com/yii2tech/ar-position) - pozwala na zarządzanie kolejnością \n  rekordów w polu typu integer.\n\nRóżnice pomiędzy behaviorami a traitami <span id=\"comparison-with-traits\"></span>\n---------------------------------------\n\nPomimo że behaviory są podobne do [traitów](https://www.php.net/traits) w taki sposób, że również \"wstrzykują\" swoje \nwłaściwości i metody do klasy, struktury te różnią się w wielu aspektach. Obie mają swoje wady i zalety, jak opisano to \nponiżej, i powinny być raczej traktowane jako swoje uzupełnienia, a nie alternatywy.\n\n\n### Zalety używania behaviorów <span id=\"pros-for-behaviors\"></span>\n\nKlasy behaviorów, tak jak zwyczajne klasy, pozwalają na dziedziczenie. Traity można raczej nazwać wspieranym przez język \nprogramowania \"kopiuj-wklej\", jako że nie oferują dziedziczenia.\n\nBehaviory można załączać i odłączać od komponentu dynamicznie bez konieczności modyfikowania klasy komponentu.  \nAby użyć traita, konieczne jest zmodyfikowanie kodu klasy, która będzie go używać.\n\nBehaviory są konfigurowalne w przeciwieństwie do traitów.\n\nBehaviory mogą modyfikować wykonywanie kodu komponentu, poprzez reagowanie na jego eventy.\n\nW przypadku, gdy zdarza się konflikt nazw pomiędzy różnymi behaviorami załączonymi do tego samego komponentu, jest on \nautomatycznie rozwiązywany przez przyznanie pierwszeństwa behaviorowi załączonemu jako pierwszy.  \nKonflikty nazw spowodowane przez różne traity wymagają ręcznego rozwiązania poprzez zmianę nazw dotkniętych problemem \nwłaściwości i metod.\n\n\n### Zalety używania traitów <span id=\"pros-for-traits\"></span>\n\nTraity są znacznie bardziej wydajne niż behaviory, ponieważ behaviory są obiektami, które wymagają czasu i pamięci.\n\nŚrodowiska IDE o wiele lepiej wspierają traity, ponieważ są one natywnymi konstruktami języka programowania.\n"
  },
  {
    "path": "docs/guide-pl/concept-components.md",
    "content": "Komponenty\n==========\n\nKomponenty są głównym budulcem aplikacji Yii. Komponenty to instancje klasy [[yii\\base\\Component|Component]] lub jej potomnych. \nTrzy główne funkcjonalności, które zapewniają komponenty innym klasom to:\n\n* [Właściwości](concept-properties.md)\n* [Eventy (zdarzenia)](concept-events.md)\n* [Behaviory (zachowania)](concept-behaviors.md)\n \nWszystkie razem i każda z tych funkcjonalności osobno zapewnia klasom Yii o wiele większą elastyczność i łatwość użycia. Dla przykładu,\ndołączony [[yii\\jui\\DatePicker|widżet wybierania daty]], komponent interfejsu użytkownika, może być użyty w [widoku](structure-views.md), \naby wygenerować interaktywny kalendarz:\n\n```php\nuse yii\\jui\\DatePicker;\n\necho DatePicker::widget([\n    'language' => 'pl',\n    'name'  => 'country',\n    'clientOptions' => [\n        'dateFormat' => 'yy-mm-dd',\n    ],\n]);\n```\n\nWłaściwości widżetu są w łatwy sposób konfigurowalne ponieważ jego klasa rozszerza [[yii\\base\\Component|Component]].\n\nKomponenty zapewniają duże możliwości, ale przez to są też bardziej zasobożerne od standardowych obiektów, ponieważ wymagają dodatkowej pamięci i czasu CPU dla wsparcia \n[eventów](concept-events.md) i [behaviorów](concept-behaviors.md) w szczególności.\nJeśli komponent nie wymaga tych dwóch funkcjonalności, należy rozważyć rozszerzenie jego klasy z [[yii\\base\\BaseObject|BaseObject]] zamiast [[yii\\base\\Component|Component]]. \nDzięki temu komponent będzie tak samo wydajny jak standardowy obiekt PHP, ale z dodatkowym wsparciem [właściwości](concept-properties.md).\n\nRozszerzając klasę [[yii\\base\\Component|Component]] lub [[yii\\base\\BaseObject|BaseObject]], zalecane jest aby przestrzegać następującej konwencji:\n\n- Przeciążając konstruktor, dodaj parametr `$config` jako *ostatni* na liście jego argumentów i przekaż go do konstruktora rodzica.\n- Zawsze wywoływuj konstruktor rodzica *na końcu* przeciążanego konstruktora.\n- Przeciążając metodę [[yii\\base\\BaseObject::init()|init()]], upewnij się, że wywołujesz metodę `init()` rodzica *na początku* własnej implementacji metody `init()`.\n\nPrzykład:\n\n```php\n<?php\n\nnamespace yii\\components\\MyClass;\n\nuse yii\\base\\BaseObject;\n\nclass MyClass extends BaseObject\n{\n    public $prop1;\n    public $prop2;\n\n    public function __construct($param1, $param2, $config = [])\n    {\n        // ... inicjalizacja przed zaaplikowaniem konfiguracji\n\n        parent::__construct($config);\n    }\n\n    public function init()\n    {\n        parent::init();\n\n        // ... inicjalizacja po zaaplikowaniu konfiguracji\n    }\n}\n```\n\nPostępowanie zgodnie z tymi zasadami zapewni [konfigurowalność](concept-configurations.md) Twojego komponentu, kiedy już zostanie utworzony. Dla przykładu:\n\n```php\n$component = new MyClass(1, 2, ['prop1' => 3, 'prop2' => 4]);\n// lub alternatywnie\n$component = \\Yii::createObject([\n    'class' => MyClass::class,\n    'prop1' => 3,\n    'prop2' => 4,\n], [1, 2]);\n```\n\n> Info: Wersja z wywołaniem [[Yii::createObject()]] wygląda na bardziej skomplikowaną, ale jest o wiele wydajniejsza, ponieważ jej implementację zapewnia \n> [kontener wstrzykiwania zależności](concept-di-container.md).\n  \n\nKlasa [[yii\\base\\BaseObject|BaseObject]] wymusza następujący cykl życia obiektu:\n\n1. Preinicjalizacja w konstruktorze. W tym miejscu można ustawić domyślne wartości właściwości.\n2. Konfiguracja obiektu poprzez `$config`. Konfiguracja może nadpisać domyślne wartości ustawione w konstruktorze.\n3. Postinicjalizacja w metodzie [[yii\\base\\BaseObject::init()|init()]]. Metoda może być przeciążona w celu normalizacji i sanityzacji właściwości.\n4. Wywołanie metody obiektu.\n\nPierwsze trzy kroki są w całości wykonywane w konstruktorze obiektu, co oznacza, że uzyskana instancja klasy jest już poprawnie zainicjowana i stabilna.\n"
  },
  {
    "path": "docs/guide-pl/db-active-record.md",
    "content": "Active Record\n=============\n\n[Active Record](https://en.wikipedia.org/wiki/Active_record_pattern) zapewnia zorientowany obiektowo interfejs dostępu i manipulacji danymi \nzapisanymi w bazie danych. Klasa typu Active Record jest powiązana z tabelą bazodanową, a instacja tej klasy odpowiada pojedynczemu wierszowi \nw tabeli - *atrybut* obiektu Active Record reprezentuje wartość konkretnej kolumny w tym wierszu. Zamiast pisać bezpośrednie kwerendy bazy danych, \nmożna skorzystać z atrybutów i metod klasy Active Record.\n\nDla przykładu, załóżmy, że `Customer` jest klasą Active Record, powiązaną z tabelą `customer` i `name` jest kolumną w tabeli `customer`. \nAby dodać nowy wiersz do tabeli `customer`, wystarczy wykonać następujący kod:\n\n```php\n$customer = new Customer();\n$customer->name = 'Qiang';\n$customer->save();\n```\n\nKod z przykładu jest odpowiednikiem poniższej komendy SQL dla MySQL, która jest mniej intuicyjna, bardziej podatna na błędy i, co bardzo \nprawdopodobne, niekompatybilna z innymi rodzajami baz danych:\n\n```php\n$db->createCommand('INSERT INTO `customer` (`name`) VALUES (:name)', [\n    ':name' => 'Qiang',\n])->execute();\n```\n\nYii zapewnia wsparcie Active Record dla następujących typów relacyjnych baz danych:\n\n* MySQL 4.1 lub nowszy: poprzez [[yii\\db\\ActiveRecord]]\n* PostgreSQL 8.4 lub nowszy: poprzez [[yii\\db\\ActiveRecord]]\n* SQLite 2 i 3: poprzez [[yii\\db\\ActiveRecord]]\n* Microsoft SQL Server 2008 lub nowszy: poprzez [[yii\\db\\ActiveRecord]]\n* Oracle: poprzez [[yii\\db\\ActiveRecord]]\n* CUBRID 9.3 lub nowszy: poprzez [[yii\\db\\ActiveRecord]] (zwróć uwagę, że z powodu [błędu](https://jira.cubrid.org/browse/APIS-658) \n  w rozszerzeniu PDO cubrid, umieszczanie wartości w cudzysłowie nie będzie działać, zatem wymagane jest zainstalowanie CUBRID 9.3 zarówno \n  jako klienta jak i serwer)\n* Sphinx: poprzez [[yii\\sphinx\\ActiveRecord]], wymaga rozszerzenia `yii2-sphinx`\n* ElasticSearch: poprzez [[yii\\elasticsearch\\ActiveRecord]], wymaga rozszerzenia `yii2-elasticsearch`\n\nDodatkowo Yii wspiera również Active Record dla następujących baz danych typu NoSQL:\n\n* Redis 2.6.12 lub nowszy: poprzez [[yii\\redis\\ActiveRecord]], wymaga rozszerzenia `yii2-redis`\n* MongoDB 1.3.0 lub nowszy: poprzez [[yii\\mongodb\\ActiveRecord]], wymaga rozszerzenia `yii2-mongodb`\n\nW tej sekcji przewodnika opiszemy sposób użycia Active Record dla baz relacyjnych, jednakże większość zagadnień można zastosować również dla NoSQL.\n\n\n## Deklarowanie klas Active Record <span id=\"declaring-ar-classes\"></span>\n\nNa początek zadeklaruj klasę typu Active Record rozszerzając [[yii\\db\\ActiveRecord|ActiveRecord]].\n \n### Deklarowanie nazwy tabeli\n\nDomyślnie każda klasa Active Record jest powiązana ze swoją tabelą w bazie danych.\nMetoda [[yii\\db\\ActiveRecord::tableName()|tableName()]] zwraca nazwę tabeli konwertując nazwę klasy za pomocą [[yii\\helpers\\Inflector::camel2id()]].\nMożesz przeciążyć tę metodę, jeśli tabela nie jest nazwana zgodnie z tą konwencją.\n\nIdentycznie zastosowany może być domyślny prefiks tabeli [[yii\\db\\Connection::$tablePrefix|tablePrefix]]. Przykładowo, jeśli \n[[yii\\db\\Connection::$tablePrefix|tablePrefix]] to `tbl_`, tabelą klasy `Customer` staje się `tbl_customer`, a dla `OrderItem` jest to `tbl_order_item`. \n\nJeśli nazwa tabeli zostanie podana jako `{{%NazwaTabeli}}`, znak procent `%` zostanie zamieniony automatycznie na prefiks tabeli. \nDla przykładu, `{{%post}}` staje się `{{tbl_post}}`. Nawiasy wokół nazwy tabeli są używane dla odpowiedniego [podawania nazw w kwerendach SQL](db-dao.md#quoting-table-and-column-names).\n\nW poniższym przykładzie deklarujemy klasę Active Record nazwaną `Customer` dla tabeli `customer` w bazie danych.\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass Customer extends ActiveRecord\n{\n    const STATUS_INACTIVE = 0;\n    const STATUS_ACTIVE = 1;\n    \n    /**\n     * @return string nazwa tabeli powiązanej z klasą ActiveRecord.\n     */\n    public static function tableName()\n    {\n        return '{{customer}}';\n    }\n}\n```\n\n### Aktywne rekordy nazywane są \"modelami\"\nInstancje Active Record są traktowane jak [modele](structure-models.md). Z tego powodu zwykle dodajemy klasy Active Record \ndo przestrzeni nazw `app\\models` (lub innej, przeznaczonej dla klas modeli). \n\nDzięki temu, że [[yii\\db\\ActiveRecord|ActiveRecord]] rozszerza [[yii\\base\\Model|Model]], dziedziczy *wszystkie* funkcjonalności [modelu](structure-models.md), \ntakie jak atrybuty, zasady walidacji, serializację danych itd.\n\n\n## Łączenie się z bazą danych <span id=\"db-connection\"></span>\n\nDomyślnie Active Record używa [komponentu aplikacji](structure-application-components.md) `db` jako [[yii\\db\\Connection|połączenia z bazą danych]], \ndo uzyskania dostępu i manipulowania jej danymi. Jak zostało to już wyjaśnione w sekcji [Obiekty dostępu do danych (DAO)](db-dao.md), \nkomponent `db` można skonfigurować w pliku konfiguracyjnym aplikacji jak poniżej:\n\n```php\nreturn [\n    'components' => [\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=testdb',\n            'username' => 'demo',\n            'password' => 'demo',\n        ],\n    ],\n];\n```\n\nJeśli chcesz użyć innego połączenia do bazy danych niż za pomocą komponentu `db`, musisz nadpisać metodę [[yii\\db\\ActiveRecord::getDb()|getDb()]]:\n\n```php\nclass Customer extends ActiveRecord\n{\n    // ...\n\n    public static function getDb()\n    {\n        // użyj komponentu aplikacji \"db2\"\n        return \\Yii::$app->db2;  \n    }\n}\n```\n\n\n## Kwerendy <span id=\"querying-data\"></span>\n\nPo zadeklarowaniu klasy Active Record, możesz użyć jej do pobrania danych z powiązanej tabeli bazy danych.\nProces ten zwykle sprowadza się do następujących trzech kroków:\n\n1. Stworzenie nowego obiektu kwerendy za pomocą metody [[yii\\db\\ActiveRecord::find()|find()]];\n2. Zbudowanie obiektu kwerendy za pomocą [metod konstruktora kwerend](db-query-builder.md#building-queries);\n3. Wywołanie [metod kwerendy](db-query-builder.md#query-methods) w celu uzyskania danych jako instancji klasy Active Record.\n\nJak widać, procedura jest bardzo podobna do tej używanej przy [konstruktorze kwerend](db-query-builder.md). Jedyna różnica jest taka, że \nzamiast użycia operatora `new` do stworzenia obiektu kwerendy, wywołujemy metodę [[yii\\db\\ActiveRecord::find()|find()]], która zwraca \nnowy obiekt kwerendy klasy [[yii\\db\\ActiveQuery|ActiveQuery]].\n\nPoniżej znajdziesz kilka przykładów pokazujących jak używać Active Query do pobierania danych:\n\n```php\n// zwraca pojedynczego klienta o ID 123\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::find()\n    ->where(['id' => 123])\n    ->one();\n\n// zwraca wszystkich aktywnych klientów posortowanych po ID\n// SELECT * FROM `customer` WHERE `status` = 1 ORDER BY `id`\n$customers = Customer::find()\n    ->where(['status' => Customer::STATUS_ACTIVE])\n    ->orderBy('id')\n    ->all();\n\n// zwraca liczbę aktywnych klientów\n// SELECT COUNT(*) FROM `customer` WHERE `status` = 1\n$count = Customer::find()\n    ->where(['status' => Customer::STATUS_ACTIVE])\n    ->count();\n\n// zwraca wszystkich klientów w tablicy zaindeksowanej wg ID\n// SELECT * FROM `customer`\n$customers = Customer::find()\n    ->indexBy('id')\n    ->all();\n```\n\nW powyższych przykładach `$customer` jest obiektem typu `Customer`, a `$customers` jest tablicą obiektów typu `Customer`. W obu przypadkach \ndane pobrane są z tabeli `customer`.\n\n> Info: Dzięki temu, że [[yii\\db\\ActiveQuery|ActiveQuery]] rozszerza klasę [[yii\\db\\Query|Query]], możesz użyć *wszystkich* metod dotyczących kwerend i ich budowania \n> opisanych w sekcji [Konstruktor kwerend](db-query-builder.md).\n\nPonieważ zwykle kwerendy korzystają z zapytań zawierających klucz główny lub też zestaw wartości dla kilku kolumn, Yii udostępnia dwie skrótowe metody, \npozwalające na szybsze ich użycie:\n\n- [[yii\\db\\ActiveRecord::findOne()|findOne()]]: zwraca pojedynczą instancję klasy Active Record, zawierającą dane z pierwszego pobranego odpowiadającego zapytaniu \nwiersza danych.\n- [[yii\\db\\ActiveRecord::findAll()|findAll()]]: zwraca tablicę instancji klasy Active Record zawierających *wszystkie* wyniki zapytania.\n\nObie metody mogą przyjmować jeden z następujących formatów parametrów:\n\n- wartość skalarna: wartość jest traktowana jako wartość klucza głównego, który należy odszukać. Yii automatycznie ustali, która kolumna jest kluczem \n  głównym, odczytując informacje ze schematu bazy.\n- tablica wartości skalarnych: tablica jest traktowana jako lista poszukiwanych wartości klucza głównego.\n- tablica asocjacyjna: klucze tablicy są poszukiwanymi nazwami kolumn a wartości tablicy są odpowiadającymi im wartościami kolumn. Po więcej \n  szczegółów zajrzyj do rozdziału [Format asocjacyjny](db-query-builder.md#hash-format).\n  \nPoniższy kod pokazuje, jak mogą być użyte opisane metody:\n\n```php\n// zwraca pojedynczego klienta o ID 123\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// zwraca klientów o ID 100, 101, 123 i 124\n// SELECT * FROM `customer` WHERE `id` IN (100, 101, 123, 124)\n$customers = Customer::findAll([100, 101, 123, 124]);\n\n// zwraca aktywnego klienta o ID 123\n// SELECT * FROM `customer` WHERE `id` = 123 AND `status` = 1\n$customer = Customer::findOne([\n    'id' => 123,\n    'status' => Customer::STATUS_ACTIVE,\n]);\n\n// zwraca wszystkich nieaktywnych klientów\n// SELECT * FROM `customer` WHERE `status` = 0\n$customers = Customer::findAll([\n    'status' => Customer::STATUS_INACTIVE,\n]);\n```\n\n> Note: Ani metoda [[yii\\db\\ActiveRecord::findOne()|findOne()]] ani [[yii\\db\\ActiveQuery::one()|one()]] nie dodaje `LIMIT 1` do wygenerowanej \n> kwerendy SQL. Jeśli zapytanie może zwrócić więcej niż jeden wiersz danych, należy wywołać bezpośrednio `limit(1)`, w celu zwiększenia \n> wydajności aplikacji, np. `Customer::find()->limit(1)->one()`.\n\nOprócz korzystania z metod konstruktora kwerend możesz również użyć surowych zapytań SQL w celu pobrania danych do obiektu Active Record za \npomocą metody [[yii\\db\\ActiveRecord::findBySql()|findBySql()]]:\n\n```php\n// zwraca wszystkich nieaktywnych klientów\n$sql = 'SELECT * FROM customer WHERE status=:status';\n$customers = Customer::findBySql($sql, [':status' => Customer::STATUS_INACTIVE])->all();\n```\n\nNie wywołuj dodatkowych metod konstruktora kwerend po wywołaniu [[yii\\db\\ActiveRecord::findBySql()|findBySql()]], ponieważ zostaną one pominięte.\n\n\n## Dostęp do danych <span id=\"accessing-data\"></span>\n\nJak wspomniano wyżej, dane pobrane z bazy danych są dostępne w obiekcie Active Record i każdy wiersz wyniku zapytania odpowiada pojedynczej \ninstancji Active Record. Możesz odczytać wartości kolumn odwołując się do atrybutów obiektu Active Record, dla przykładu:\n\n```php\n// \"id\" i \"email\" są nazwami kolumn w tabeli \"customer\"\n$customer = Customer::findOne(123);\n$id = $customer->id;\n$email = $customer->email;\n```\n\n> Note: nazwy atrybutów Active Record odpowiadają nazwom powiązanych z nimi kolumn z uwzględnieniem wielkości liter.\n> Yii automatycznie definiuje atrybut Active Record dla każdej kolumny powiązanej tabeli.\n> NIE należy definiować ich własnoręcznie. \n\nPonieważ atrybuty Active Record nazywane są zgodnie z nazwami kolumn, możesz natknąć się na kod PHP typu \n`$customer->first_name`, gdzie podkreślniki używane są do oddzielenia poszczególnych słów w nazwach atrybutów, w przypadku, gdy kolumny tabeli nazywane są \nwłaśnie w ten sposób. Jeśli masz wątpliwości dotyczące spojności takiego stylu programowania, powinieneś zmienić odpowiednio nazwy kolumn tabeli \n(używając np. formatowania typu \"camelCase\").\n\n\n### Transformacja danych <span id=\"data-transformation\"></span>\n\nCzęsto zdarza się, że dane wprowadzane i/lub wyświetlane zapisane są w formacie różniącym się od tego używanego w bazie danych. Dla przykładu, \nw bazie danych przechowywane są daty urodzin klientów jako uniksowe znaczniki czasu, podczas gdy w większości przypadków pożądana forma zapisu daty \nto `'RRRR/MM/DD'`. Aby osiągnąć ten format, można zdefiniować metody *transformujące dane* w klasie `Customer`:\n\n```php\nclass Customer extends ActiveRecord\n{\n    // ...\n\n    public function getBirthdayText()\n    {\n        return date('Y/m/d', $this->birthday);\n    }\n    \n    public function setBirthdayText($value)\n    {\n        $this->birthday = strtotime($value);\n    }\n}\n```\n\nOd tego momentu, w kodzie PHP, zamiast odwołać się do `$customer->birthday`, można użyć `$customer->birthdayText`, co pozwala na \nwprowadzenie i wyświetlenie daty urodzin klienta w formacie `'RRRR/MM/DD'`.\n\n> Tip: Powyższy przykład pokazuje podstawowy sposób transformacji danych. Podczas zwyczajowej pracy z formularzami danych można skorzystać z \n> [DateValidator](tutorial-core-validators.md#date) i [[yii\\jui\\DatePicker|DatePicker]], co jest prostsze w użyciu i daje więcej możliwości.\n\n\n### Pobieranie danych jako tablice <span id=\"data-in-arrays\"></span>\n\nPobieranie danych jako obiekty Active Record jest wygodne i elastyczne, ale nie zawsze pożądane, zwłaszcza kiedy konieczne jest \nuzyskanie ogromnej liczby danych, z powodu użycia sporej ilości pamięci. W takim przypadku można pobrać dane jako tablicę PHP, wywołując metodę \n[[yii\\db\\ActiveQuery::asArray()|asArray()]] przed wykonaniem kwerendy:\n\n```php\n// zwraca wszystkich klientów\n// każdy klient jest zwracany w postaci tablicy asocjacyjnej\n$customers = Customer::find()\n    ->asArray()\n    ->all();\n```\n\n> Note: Powyższy sposób zwiększa wydajność aplikacji i pozwala na zmniejszenie zużycia pamięci, ale ponieważ jest on znacznie bliższy niskiej warstwie \n> abstrakcji DB, traci się większość funkcjonalności Active Record. Bardzo ważną różnicą jest zwracany typ danych dla wartości kolumn. Kiedy dane zwracane \n> są jako obiekt Active Record, wartości kolumn są automatycznie odpowiednio rzutowane zgodnie z typem kolumny; przy danych zwracanych jako tablice \n> wartości kolumn są zawsze typu string (jako rezultat zapytania PDO bez żadnego przetworzenia), niezależnie od typu kolumny.\n   \n\n### Pobieranie danych seriami <span id=\"data-in-batches\"></span>\n\nW sekcji [Konstruktor kwerend](db-query-builder.md) wyjaśniliśmy, że można użyć *kwerendy serii*, aby zmniejszyć zużycie pamięci przy pobieraniu \ndużej ilości danych z bazy. Tej samej techniki można użyć w przypadku Active Record. Dla przykładu:\n\n```php\n// pobiera dziesięciu klientów na raz\nforeach (Customer::find()->batch(10) as $customers) {\n    // $customers jest tablicą dziesięciu lub mniej obiektów Customer\n}\n\n// pobiera dziesięciu klientów na raz i iteruje po nich pojedynczo\nforeach (Customer::find()->each(10) as $customer) {\n    // $customer jest obiektem Customer\n}\n\n// kwerenda seryjna z gorliwym ładowaniem\nforeach (Customer::find()->with('orders')->each() as $customer) {\n    // $customer jest obiektem Customer\n}\n```\n\n\n## Zapisywanie danych <span id=\"inserting-updating-data\"></span>\n\nUżywając Active Record możesz w łatwy sposób zapisać dane w bazie, w następujących krokach:\n\n1. Przygotowanie instancji Active Record\n2. Przypisanie nowych wartości do atrybutów Active Record\n3. Wywołanie metody [[yii\\db\\ActiveRecord::save()|save()]] w celu zapisania danych w bazie.\n\nPrzykład:\n\n```php\n// dodaj nowy wiersz danych\n$customer = new Customer();\n$customer->name = 'James';\n$customer->email = 'james@example.com';\n$customer->save();\n\n// zaktualizuj istniejący wiersz danych\n$customer = Customer::findOne(123);\n$customer->email = 'james@newexample.com';\n$customer->save();\n```\n\nMetoda [[yii\\db\\ActiveRecord::save()|save()]] może zarówno dodawać jak i aktualizować wiersz danych, w zależności od stanu instacji Active Record. \nJeśli instancja została dopiero utworzona poprzez operator `new`, wywołanie [[yii\\db\\ActiveRecord::save()|save()]] spowoduje dodanie nowego wiersza. \nJeśli instacja jest wynikiem użycia kwerendy, wywołanie [[yii\\db\\ActiveRecord::save()|save()]] zaktualizuje wiersz danych powiązanych z instancją. \n\nMożna odróżnić dwa stany instancji Active Record sprawdzając wartość jej właściwości [[yii\\db\\ActiveRecord::isNewRecord|isNewRecord]]. Jest ona także \nużywana przez [[yii\\db\\ActiveRecord::save()|save()]] w poniższy sposób:\n\n```php\npublic function save($runValidation = true, $attributeNames = null)\n{\n    if ($this->getIsNewRecord()) {\n        return $this->insert($runValidation, $attributeNames);\n    } else {\n        return $this->update($runValidation, $attributeNames) !== false;\n    }\n}\n```\n\n> Tip: Możesz również wywołać [[yii\\db\\ActiveRecord::insert()|insert()]] lub [[yii\\db\\ActiveRecord::update()|update()]] bezpośrednio, aby, odpowiednio, \n> dodać lub uaktualnić wiersz.\n  \n\n### Walidacja danych <span id=\"data-validation\"></span>\n\nDzięki temu, że [[yii\\db\\ActiveRecord|ActiveRecord]] rozszerza klasę [[yii\\base\\Model|Model]], korzysta z tych samych mechanizmów [walidacji danych](input-validation.md).\nMożesz definiować zasady walidacji nadpisując metodę [[yii\\db\\ActiveRecord::rules()|rules()]] i uruchamiać procedurę walidacji wywołując metodę \n[[yii\\db\\ActiveRecord::validate()|validate()]].\n\nWywołanie [[yii\\db\\ActiveRecord::save()|save()]] automatycznie wywołuje również metodę [[yii\\db\\ActiveRecord::validate()|validate()]]. \nDopiero po pomyślnym przejściu walidacji rozpocznie się proces zapisywania danych; w przeciwnym wypadku zostanie zwrócona flaga `false` - komunikaty z \nbłędami walidacji można odczytać sprawdzając właściwość [[yii\\db\\ActiveRecord::errors|errors]]. \n\n> Tip: Jeśli masz pewność, że dane nie potrzebują przechodzić procesu walidacji (np. pochodzą z zaufanych źródeł), możesz wywołać `save(false)`, \n> aby go pominąć.\n\n\n### Masowe przypisywanie <span id=\"massive-assignment\"></span>\n\nTak jak w zwyczajnych [modelach](structure-models.md), instancje Active Record posiadają również mechanizm \n[masowego przypisywania](structure-models.md#massive-assignment). Funkcjonalność ta umożliwia przypisanie wartości wielu atrybutom Active Record za \npomocą pojedynczej instrukcji PHP, jak pokazano to poniżej. \nNależy jednak pamiętać, że w ten sposób mogą być przypisane tylko [bezpieczne atrybuty](structure-models.md#safe-attributes).\n\n```php\n$values = [\n    'name' => 'James',\n    'email' => 'james@example.com',\n];\n\n$customer = new Customer();\n\n$customer->attributes = $values;\n$customer->save();\n```\n\n\n### Aktualizowanie liczników <span id=\"updating-counters\"></span>\n\nJednym z częstych zadań jest zmniejszanie lub zwiększanie wartości kolumny w tabeli bazy danych. Takie kolumny nazywamy licznikami.\nMetoda [[yii\\db\\ActiveRecord::updateCounters()|updateCounters()]] służy do aktualizacji jednego lub wielu liczników.\nPrzykład:\n\n```php\n$post = Post::findOne(100);\n\n// UPDATE `post` SET `view_count` = `view_count` + 1 WHERE `id` = 100\n$post->updateCounters(['view_count' => 1]);\n```\n\n> Note: Jeśli używasz [[yii\\db\\ActiveRecord::save()|save()]] do aktualizacji licznika, możesz otrzymać nieprawidłowe rezultaty, ponieważ jest możliwe, że \n> ten sam licznik zostanie odczytany i zapisany jednocześnie przez wiele zapytań.\n\n\n### Brudne atrybuty <span id=\"dirty-attributes\"></span>\n\nKiedy wywołujesz [[yii\\db\\ActiveRecord::save()|save()]], aby zapisać instancję Active Record, tylko *brudne atrybuty* są zapisywane. \nAtrybut uznawany jest za *brudny* jeśli jego wartość została zmodyfikowana od momentu pobrania z bazy danych lub ostatniego zapisu. \nPamiętaj, że walidacja danych zostanie przeprowadzona niezależnie od tego, czy instancja Active Record zawiera brudne atrybuty czy też nie.\n\nActive Record automatycznie tworzy listę brudnych atrybutów, poprzez porównanie starej wartości atrybutu do aktualnej. Możesz wywołać metodę \n[[yii\\db\\ActiveRecord::getDirtyAttributes()|getDirtyAttributes()]], aby otrzymać najnowszą listę brudnych atrybutów. Dodatkowo można wywołać \n[[yii\\db\\ActiveRecord::markAttributeDirty()|markAttributeDirty()]], aby oznaczyć konkretny atrybut jako brudny.\n\nJeśli chcesz sprawdzić wartość atrybutu sprzed ostatniej zmiany, możesz wywołać [[yii\\db\\ActiveRecord::getOldAttributes()|getOldAttributes()]] lub \n[[yii\\db\\ActiveRecord::getOldAttribute()|getOldAttribute()]].\n\n> Note: Porównanie starej i nowej wartości atrybutu odbywa się za pomocą operatora `===`, zatem atrybut zostanie uznany za brudny nawet jeśli \n> ma tą samą wartość, ale jest innego typu. Taka sytuacja zdarza się często, kiedy model jest aktualizowany danymi pochodzącymi z formularza \n> HTML, gdzie każda wartość jest reprezentowana jako string.\n> Aby upewnić się, że wartości będą odpowiednich typów, np. integer, możesz zaaplikować [filtr walidacji](input-validation.md#data-filtering):\n> `['attributeName', 'filter', 'filter' => 'intval']`.  Działa on z wszystkimi funkcjami PHP rzutującymi typy jak [intval()](https://www.php.net/manual/en/function.intval.php), \n> [floatval()](https://www.php.net/manual/en/function.floatval.php), [boolval](https://www.php.net/manual/en/function.boolval.php), itp...\n\n\n### Domyślne wartości atrybutów <span id=\"default-attribute-values\"></span>\n\nNiektóre z kolumn tabeli bazy danych mogą mieć przypisane domyślne wartości w bazie danych. W przypadku, gdy chcesz wypełnić takimi wartościami \nformularz dla instancji Active Record, zamiast ponownie ustawiać wszystkie domyślne wartości, możesz wywołać metodę \n[[yii\\db\\ActiveRecord::loadDefaultValues()|loadDefaultValues()]], która przypisze wszystkie domyślne wartości odpowiednim atrybutom:\n\n```php\n$customer = new Customer();\n$customer->loadDefaultValues();\n// $customer->xyz otrzyma domyślną wartość, zadeklarowaną przy definiowaniu kolumny \"xyz\"\n```\n\n\n### Rzutowanie typów atrybutów <span id=\"attributes-typecasting\"></span>\n\nPo wypełnieniu rezultatem kwerendy, [[yii\\db\\ActiveRecord]] przeprowadza automatyczne rzutowanie typów na wartościach swoich atrybutów, \nużywając do tego celu informacji zawartych w [schemacie tabeli bazy danych](db-dao.md#database-schema). Pozwala to na prawidłowe przedstawienie \ndanych pobranych z kolumny tabeli zadeklarowanej jako liczba całkowita, w postaci wartości typu PHP integer w instancji klasy ActiveRecord (typu boolean jako boolean itp.).\nMechanizm rzutowania ma jednak kilka ograniczeń:\n\n* Wartości typu zmiennoprzecinkowego nie są konwertowane na float, a zamiast tego są przedstawiane jako łańcuch znaków, aby zachować dokładność ich liczbowej prezentacji.\n* Konwersja typu integer zależy od zakresu liczb całkowitych używanego systemu operacyjnego.\n  Wartości kolumn zadeklarowanych jako 'unsigned integer' lub 'big integer' będą przekonwertowane do PHP integer tylko na systemach 64-bitowych,\n  a na 32-bitowych będą przedstawione jako łańcuchy znaków.\n\nZwróć uwagę na to, że rzutowanie typów jest wykonywane tylko podczas wypełniania instancji ActiveRecord rezultatem kwerendy. Automatyczna konwersja nie jest przeprowadzana \ndla wartości załadowanych poprzez żądanie HTTP lub ustawionych bezpośrednio dla właściwości klasy.\nSchemat tabeli będzie również użyty do przygotowania instrukcji SQL przy zapisywaniu danych ActiveRecord, aby upewnić się, że wartości są przypisane w kwerendzie z prawidłowymi typami. \nAtrybuty instancji ActiveRecord nie będą jednak przekonwertowane w procesie zapisywania.\n\n> Tip: możesz użyć [[yii\\behaviors\\AttributeTypecastBehavior]], aby skonfigurować proces rzutowania typów dla wartości atrybutów w momencie ich walidacji lub zapisu.\n\n\n### Aktualizowanie wielu wierszy jednocześnie <span id=\"updating-multiple-rows\"></span>\n\nMetody przedstawione powyżej działają na pojedynczych instancjach Active Record, dodając lub aktualizując indywidualne wiersze tabeli. \nAby uaktualnić wiele wierszy jednocześnie, należy wywołać statyczną metodę [[yii\\db\\ActiveRecord::updateAll()|updateAll()]].\n\n```php\n// UPDATE `customer` SET `status` = 1 WHERE `email` LIKE `%@example.com%`\nCustomer::updateAll(['status' => Customer::STATUS_ACTIVE], ['like', 'email', '@example.com']);\n```\n\nW podobny sposób można wywołać [[yii\\db\\ActiveRecord::updateAllCounters()|updateAllCounters()]], aby uaktualnić liczniki wielu wierszy w tym samym czasie.\n\n```php\n// UPDATE `customer` SET `age` = `age` + 1\nCustomer::updateAllCounters(['age' => 1]);\n```\n\n\n## Usuwanie danych <span id=\"deleting-data\"></span>\n\nAby usunąć pojedynczy wiersz danych, utwórz najpierw instancję Active Record odpowiadającą temu wierszowi, a następnie wywołaj metodę \n[[yii\\db\\ActiveRecord::delete()|delete()]].\n\n```php\n$customer = Customer::findOne(123);\n$customer->delete();\n```\n\nMożesz również wywołać [[yii\\db\\ActiveRecord::deleteAll()|deleteAll()]], aby usunąć kilka lub wszystkie wiersze danych. Dla przykładu:\n\n```php\nCustomer::deleteAll(['status' => Customer::STATUS_INACTIVE]);\n```\n\n> Note: Należy być bardzo ostrożnym przy wywoływaniu [[yii\\db\\ActiveRecord::deleteAll()|deleteAll()]], ponieważ w efekcie można całkowicie usunąć \n> wszystkie dane z tabeli bazy, jeśli popełni się błąd przy ustalaniu warunków dla metody.\n\n\n## Cykl życia Active Record <span id=\"ar-life-cycles\"></span>\n\nIstotnym elementem pracy z Yii jest zrozumienie cyklu życia Active Record w zależności od metodyki jego użycia.\nPodczas każdego cyklu wykonywane są określone sekwencje metod i aby dopasować go do własnych potrzeb, wystarczy je nadpisać. \nMożna również śledzić i odpowiadać na eventy Active Record uruchamiane podczas cyklu życia, aby wstrzyknąć swój własny kod. \nTakie eventy są szczególnie użyteczne podczas tworzenia wpływających na cykl życia [behaviorów](concept-behaviors.md) Active Record.\n\nPoniżej znajdziesz wyszczególnione cykle życia Active Record wraz z metodami/eventami, które są w nie zaangażowane.\n\n\n### Cykl życia nowej instancji <span id=\"new-instance-life-cycle\"></span>\n\nPodczas tworzenia nowej instancji Active Record za pomocą operatora `new`, zachodzi następujący cykl:\n\n1. Konstruktor klasy.\n2. [[yii\\db\\ActiveRecord::init()|init()]]: uruchamia event [[yii\\db\\ActiveRecord::EVENT_INIT|EVENT_INIT]].\n\n\n### Cykl życia przy pobieraniu danych <span id=\"querying-data-life-cycle\"></span>\n\nPodczas pobierania danych za pomocą jednej z [metod kwerendy](#querying-data), każdy świeżo wypełniony obiekt Active Record przechodzi następujący cykl:\n\n1. Konstruktor klasy.\n2. [[yii\\db\\ActiveRecord::init()|init()]]: uruchamia event [[yii\\db\\ActiveRecord::EVENT_INIT|EVENT_INIT]].\n3. [[yii\\db\\ActiveRecord::afterFind()|afterFind()]]: uruchamia event [[yii\\db\\ActiveRecord::EVENT_AFTER_FIND|EVENT_AFTER_FIND]].\n\n\n### Cykl życia przy zapisywaniu danych <span id=\"saving-data-life-cycle\"></span>\n\nPodczas wywołania [[yii\\db\\ActiveRecord::save()|save()]], w celu dodania lub uaktualnienia danych instancji Active Record, zachodzi następujący cykl:\n\n1. [[yii\\db\\ActiveRecord::beforeValidate()|beforeValidate()]]: uruchamia event [[yii\\db\\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]]. \n   Jeśli metoda zwróci `false` lub właściwość [[yii\\base\\ModelEvent::isValid|isValid]] ma wartość `false`, kolejne kroki są pomijane.\n2. Proces walidacji danych. Jeśli proces zakończy się niepowodzeniem, kolejne kroki po kroku 3. są pomijane. \n3. [[yii\\db\\ActiveRecord::afterValidate()|afterValidate()]]: uruchamia event [[yii\\db\\ActiveRecord::EVENT_AFTER_VALIDATE|EVENT_AFTER_VALIDATE]].\n4. [[yii\\db\\ActiveRecord::beforeSave()|beforeSave()]]: uruchamia event [[yii\\db\\ActiveRecord::EVENT_BEFORE_INSERT|EVENT_BEFORE_INSERT]] lub \n   [[yii\\db\\ActiveRecord::EVENT_BEFORE_UPDATE|EVENT_BEFORE_UPDATE]]. Jeśli metoda zwróci `false` lub właściwość [[yii\\base\\ModelEvent::isValid|isValid]] ma \n   wartość `false`, kolejne kroki są pomijane.\n5. Proces właściwego dodawania lub aktulizowania danych.\n6. [[yii\\db\\ActiveRecord::afterSave()|afterSave()]]: uruchamia event [[yii\\db\\ActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]] lub \n   [[yii\\db\\ActiveRecord::EVENT_AFTER_UPDATE|EVENT_AFTER_UPDATE]].\n   \n\n### Cykl życia przy usuwaniu danych <span id=\"deleting-data-life-cycle\"></span>\n\nPodczas wywołania [[yii\\db\\ActiveRecord::delete()|delete()]], w celu usunięcia danych instancji Active Record, zachodzi następujący cykl:\n\n1. [[yii\\db\\ActiveRecord::beforeDelete()|beforeDelete()]]: uruchamia event [[yii\\db\\ActiveRecord::EVENT_BEFORE_DELETE|EVENT_BEFORE_DELETE]]. \n   Jeśli metoda zwróci `false` lub właściwość [[yii\\base\\ModelEvent::isValid|isValid]] ma wartość `false`, kolejne kroki są pomijane.\n2. Proces właściwego usuwania danych.\n3. [[yii\\db\\ActiveRecord::afterDelete()|afterDelete()]]: uruchamia event [[yii\\db\\ActiveRecord::EVENT_AFTER_DELETE|EVENT_AFTER_DELETE]].\n\n\n> Note: Wywołanie poniższych metod NIE uruchomi żadnego z powyższych cykli:\n>\n> - [[yii\\db\\ActiveRecord::updateAll()|updateAll()]] \n> - [[yii\\db\\ActiveRecord::deleteAll()|deleteAll()]]\n> - [[yii\\db\\ActiveRecord::updateCounters()|updateCounters()]] \n> - [[yii\\db\\ActiveRecord::updateAllCounters()|updateAllCounters()]] \n\n\n### Odświeżanie cyklu życia danych <span id=\"refreshing-data-life-cycle\"></span>\n\nWywołanie [[yii\\db\\ActiveRecord::refresh()|refresh()]] w celu odświeżenia instancji Active Record, uruchamia event \n[[yii\\db\\ActiveRecord::EVENT_AFTER_REFRESH|EVENT_AFTER_REFRESH]], o ile odświeżenie się powiedzie i metoda zwróci `true`.\n\n\n## Praca z transakcjami <span id=\"transactional-operations\"></span>\n\nSą dwa sposoby użycia [transakcji](db-dao.md#performing-transactions) podczas pracy z Active Record. \n\nPierwszy zakłada bezpośrednie ujęcie wywołań metod Active Record w blok transakcji, jak pokazano to poniżej:\n\n```php\n$customer = Customer::findOne(123);\n\nCustomer::getDb()->transaction(function($db) use ($customer) {\n    $customer->id = 200;\n    $customer->save();\n    // ...inne operacje bazodanowe...\n});\n\n// lub alternatywnie\n\n$transaction = Customer::getDb()->beginTransaction();\ntry {\n    $customer->id = 200;\n    $customer->save();\n    // ...inne operacje bazodanowe...\n    $transaction->commit();\n} catch(\\Exception $e) {\n    $transaction->rollBack();\n    throw $e;\n} catch(\\Throwable $e) {\n    $transaction->rollBack();\n    throw $e;\n}\n```\n\n> Note: w powyższym kodzie znajdują się dwa bloki catch dla kompatybilności \n> z PHP 5.x i PHP 7.x. `\\Exception` implementuje [interfejs `\\Throwable`](https://www.php.net/manual/en/class.throwable.php)\n> od PHP 7.0, zatem można pominąć część z `\\Exception`, jeśli Twoja aplikacja używa tylko PHP 7.0 lub wyższego.\n\nDrugi sposób polega na utworzeniu listy operacji bazodanowych, które wymagają transakcji za pomocą metody [[yii\\db\\ActiveRecord::transactions()|transactions()]]. \nDla przykładu:\n\n```php\nclass Customer extends ActiveRecord\n{\n    public function transactions()\n    {\n        return [\n            'admin' => self::OP_INSERT,\n            'api' => self::OP_INSERT | self::OP_UPDATE | self::OP_DELETE,\n            // powyższy zapis jest odpowiednikiem następującego skróconego:\n            // 'api' => self::OP_ALL,\n        ];\n    }\n}\n```\n\nMetoda [[yii\\db\\ActiveRecord::transactions()|transactions()]] powinna zwracać tablicę, której klucze są nazwami [scenariuszy](structure-models.md#scenarios), \na wartości to operacje bazodanowe, które powinny być objęte transakcją. Używaj następujących stałych do określenia typu operacji:\n\n* [[yii\\db\\ActiveRecord::OP_INSERT|OP_INSERT]]: operacja dodawania wykonywana za pomocą [[yii\\db\\ActiveRecord::insert()|insert()]];\n* [[yii\\db\\ActiveRecord::OP_UPDATE|OP_UPDATE]]: operacja aktualizacji wykonywana za pomocą [[yii\\db\\ActiveRecord::update()|update()]];\n* [[yii\\db\\ActiveRecord::OP_DELETE|OP_DELETE]]: operacja usuwania wykonywana za pomocą [[yii\\db\\ActiveRecord::delete()|delete()]].\n\nUżywaj operatora `|`, aby podać więcej niż jedną operację za pomocą powyższych stałych. Możesz również użyć stałej dla skróconej definicji \nwszystkich trzech powyższych operacji [[yii\\db\\ActiveRecord::OP_ALL|OP_ALL]].\n\n\n## Optymistyczna blokada <span id=\"optimistic-locks\"></span>\n\nOptymistyczne blokowanie jest jednym ze sposobów uniknięcia konfliktów, które mogą wystąpić, kiedy pojedynczy wiersz danych jest aktualizowany przez \nkilku użytkowników. Dla przykładu, użytkownik A i użytkownik B edytują artykuł wiki w tym samym czasie - po tym jak użytkownik A zapisał już swoje \nzmiany, użytkownik B klika przycisk \"Zapisz\", aby również wykonać identyczną operację. Ponieważ użytkownik B pracował w rzeczywistości na \"starej\" wersji \nartykułu, byłoby wskazane powstrzymać go przed nadpisaniem wersji użytkownika A i wyświelić komunikat wyjaśniający sytuację.\n\nOptymistyczne blokowanie rozwiązuje ten problem za pomocą dodatkowej kolumny w bazie przechowującej numer wersji każdego wiersza.\nKiedy taki wiersz jest zapisywany z wcześniejszym numerem wersji niż aktualna rzucany jest wyjątek [[yii\\db\\StaleObjectException|StaleObjectException]], który powstrzymuje \nzapis wiersza. Optymistyczne blokowanie może być użyte tylko przy aktualizacji lub usuwaniu istniejącego wiersza za pomocą odpowiednio \n[[yii\\db\\ActiveRecord::update()|update()]] lub [[yii\\db\\ActiveRecord::delete()|delete()]].\n\nAby skorzystać z optymistycznej blokady:\n\n1. Stwórz kolumnę w tabeli bazy danych powiązaną z klasą Active Record do przechowywania numeru wersji każdego wiersza.\n   Kolumna powinna być typu big integer (przykładowo w MySQL `BIGINT DEFAULT 0`).\n2. Nadpisz metodę [[yii\\db\\ActiveRecord::optimisticLock()|optimisticLock()]], aby zwrócić nazwę tej kolumny.\n3. W formularzu pobierającym dane od użytkownika, dodaj ukryte pole, gdzie przechowasz aktualny numer wersji uaktualnianego wiersza. \n   Upewnij się, że atrybut wersji ma dodaną zasadę walidacji i przechodzi poprawnie jej proces.\n4. W akcji kontrolera uaktualniającej wiersz za pomocą Active Record, użyj bloku try-catch, aby wyłapać wyjątek [[yii\\db\\StaleObjectException|StaleObjectException]]. \n   Zaimplemetuj odpowiednią logikę biznesową (np. scalenie zmian, wyświetlenie komunikatu o nieaktualnej wersji, itp.), aby rozwiązać konflikt.\n   \nDla przykładu, załóżmy, że kolumna wersji nazywa się `version`. Implementację optymistycznego blokowania można wykonać za pomocą następującego kodu:\n\n```php\n// ------ kod widoku -------\n\nuse yii\\helpers\\Html;\n\n// ...inne pola formularza\necho Html::activeHiddenInput($model, 'version');\n\n\n// ------ kod kontrolera -------\n\nuse yii\\db\\StaleObjectException;\n\npublic function actionUpdate($id)\n{\n    $model = $this->findModel($id);\n\n    try {\n        if ($model->load(Yii::$app->request->post()) && $model->save()) {\n            return $this->redirect(['view', 'id' => $model->id]);\n        } else {\n            return $this->render('update', [\n                'model' => $model,\n            ]);\n        }\n    } catch (StaleObjectException $e) {\n        // logika rozwiązująca konflikt\n    }\n}\n```\n\n\n## Praca z danymi relacji <span id=\"relational-data\"></span>\n\nOprócz korzystania z indywidualnych tabel bazy danych, Active Record umożliwia również na uzyskanie danych relacji, \npozwalając na odczytanie ich z poziomu głównego obiektu. Dla przykładu, dane klienta są powiązane relacją z danymi zamówienia, \nponieważ jeden klient może złożyć jedno lub wiele zamówień. Odpowiednio deklarując tę relację, można uzyskać dane zamówienia klienta, \nużywając wyrażenia `$customer->orders`, które zwróci informacje o zamówieniu klienta jako tablicę instancji `Order` typu Active Record.\n\n\n### Deklarowanie relacji <span id=\"declaring-relations\"></span>\n\nAby móc pracować z relacjami używając Active Record, najpierw musisz je zadeklarować w obrębie klasy.\nDeklaracja odbywa się za pomocą utworzenia prostej *metody relacyjnej* dla każdej relacji osobno, jak w przykładach poniżej:\n\n```php\nclass Customer extends ActiveRecord\n{\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n}\n\nclass Order extends ActiveRecord\n{\n    public function getCustomer()\n    {\n        return $this->hasOne(Customer::class, ['id' => 'customer_id']);\n    }\n}\n```\n\nW powyższym kodzie, zadeklarowano relację `orders` dla klasy `Customer` i relację `customer` dla klasy `Order`. \n\nKażda metoda relacyjna musi mieć nazwę utworzoną według wzoru `getXyz`. `xyz` (pierwsza litera jest mała) jest *nazwą relacji*.\nZwróć uwagę na to, że nazwy relacji *uwzględniają wielkość liter*.\n\nDeklarując relację powinno się zwrócić uwagę na następujące dane:\n\n- mnogość relacji: określona przez wywołanie odpowiednio [[yii\\db\\ActiveRecord::hasMany()|hasMany()]]\n  lub [[yii\\db\\ActiveRecord::hasOne()|hasOne()]]. W powyższym przykładzie można łatwo zobaczyć w definicji relacji, że \n  klient może mieć wiele zamówień, podczas gdy zamówienie ma tylko jednego klienta.\n- nazwę powiązanej klasy Active Record: określoną jako pierwszy argument w [[yii\\db\\ActiveRecord::hasMany()|hasMany()]] lub \n  [[yii\\db\\ActiveRecord::hasOne()|hasOne()]].\n  Rekomendowany sposób uzyskania nazwy klasy to wywołanie `Xyz::class`, dzięki czemu możemy posiłkować się wsparciem autouzupełniania IDE \n  i wykrywaniem błędów na poziomie kompilacji. \n- powiązanie pomiędzy dwoma rodzajami danych: określone jako kolumna(y), poprzez którą dane nawiązują relację.\n  Wartości tablicy są kolumnami głównych danych (reprezentowanymi przez klasę Active Record, w której deklaruje się relacje), a klucze tablicy są \n  kolumnami danych relacyjnych.\n\n  Aby łatwo opanować technikę deklarowania relacji wystarczy zapamiętać, że kolumnę należącą do relacyjnej klasy Active Record zapisuje się zaraz obok \n  jej nazwy (jak to widać w przykładzie powyżej - `customer_id` jest właściwością `Order` a `id` jest właściwością `Customer`).\n  \n\n### Uzyskiwanie dostępu do danych relacji <span id=\"accessing-relational-data\"></span>\n\nPo zadeklarowaniu relacji, możesz uzyskać dostęp do danych poprzez jej nazwę. Odbywa się to w taki sam sposób jak uzyskiwanie dostępu do \n[właściwości](concept-properties.md) obiektu zdefiniowanego w metodzie relacyjnej. Właśnie dlatego też nazywamy je *właściwościami relacji*.\nPrzykład:\n\n```php\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123\n// $orders jest tablicą obiektów typu Order\n$orders = $customer->orders;\n```\n\n> Info: Deklarując relację o nazwie `xyz` poprzez metodę-getter `getXyz()`, uzyskasz dostęp do `xyz` jak do [właściwości obiektu](concept-properties.md). \n> Zwróć uwagę na to, że nazwa uwzględnia wielkość liter.\n  \nJeśli relacja jest zadeklarowana poprzez [[yii\\db\\ActiveRecord::hasMany()|hasMany()]], zwraca tablicę powiązanych instancji Active Record; \njeśli deklaracja odbywa się poprzez [[yii\\db\\ActiveRecord::hasOne()|hasOne()]], zwraca pojedynczą powiązaną instancję Active Record lub wartość `null`, \nw przypadku, gdy nie znaleziono powiązanych danych.\n\nPodczas pierwszego odwołania się do właściwości relacji wykonywana jest kwerenda SQL, tak jak pokazano to w przykładzie powyżej. \nOdwołanie się do tej samej właściwości kolejny raz zwróci poprzedni wynik, bez wykonywanie ponownie kwerendy. Aby wymusić wykonanie kwerendy w takiej \nsytuacji, należy najpierw usunąć z pamięci właściwość relacyjną poprzez `unset($customer->orders)`.\n\n> Note: Pomimo podobieństwa mechanizmu relacji do [właściwości obiektu](concept-properties.md), jest tutaj znacząca różnica. \n> Wartości właściwości zwykłych obiektów są tego samego typu jak definiująca je metoda-getter.\n> Metoda relacyjna zwraca jednak instancję [[yii\\db\\ActiveQuery|ActiveQuery]], a właściwości relacji są instancjami [[yii\\db\\ActiveRecord|ActiveRecord]] lub tablicą takich obiektów.\n> \n> ```php\n> $customer->orders; // tablica obiektów `Order`\n> $customer->getOrders(); // instancja ActiveQuery\n> ```\n> \n> Taka funkcjonalność jest użyteczna przy tworzeniu kwerend dostosowanych do potrzeb programisty, co opisane jest w następnej sekcji.\n\n\n### Dynamiczne kwerendy relacyjne <span id=\"dynamic-relational-query\"></span>\n\nDzięki temu, że metoda relacyjna zwraca instancję [[yii\\db\\ActiveQuery|ActiveQuery]], możliwe jest dalsze rozbudowanie takiej kwerendy korzystając z \nmetod konstruowania kwerend. Dla przykładu:\n\n```php\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `subtotal` > 200 ORDER BY `id`\n$orders = $customer->getOrders()\n    ->where(['>', 'subtotal', 200])\n    ->orderBy('id')\n    ->all();\n```\n\nInaczej niż w przypadku właściwości relacji, za każdym razem, gdy wywyłujesz dynamiczną kwerendę relacyjną poprzez metodę relacji, wykonywane jest \nzapytanie do bazy, nawet jeśli identyczna kwerenda została już wywołana wcześniej.\n\nMożliwe jest także sparametryzowanie deklaracji relacji, dzięki czemu można w łatwiejszy sposób wykonywać relacyjne kwerendy. Dla przykładu, możesz \nzadeklarować relację `bigOrders` jak to pokazano poniżej: \n\n```php\nclass Customer extends ActiveRecord\n{\n    public function getBigOrders($threshold = 100)\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id'])\n            ->where('subtotal > :threshold', [':threshold' => $threshold])\n            ->orderBy('id');\n    }\n}\n```\n\nDzięki czemu możesz wykonać następujące relacyjne kwerendy:\n\n```php\n// SELECT * FROM `order` WHERE `subtotal` > 200 ORDER BY `id`\n$orders = $customer->getBigOrders(200)->all();\n\n// SELECT * FROM `order` WHERE `subtotal` > 100 ORDER BY `id`\n$orders = $customer->bigOrders;\n```\n\n\n### Relacje za pomocą tabeli węzła <span id=\"junction-table\"></span>\n\nW projekcie bazy danych, kiedy połączenie pomiędzy dwoma relacyjnymi tabelami jest typu wiele-do-wielu, zwykle stosuje się tzw. \n[tabelę węzła](https://en.wikipedia.org/wiki/Junction_table). Dla przykładu, tabela `order` i tabela `item` mogą być powiązane poprzez węzeł nazwany \n`order_item`. Jedno zamówienie będzie posiadało wiele produktów zamówienia (pozycji), a każdy indywidualny produkt będzie także powiązany z wieloma \npozycjami zamówienia.\n\nDeklarując takie relacje, możesz wywołać zarówno metodę [[yii\\db\\ActiveQuery::via()|via()]] jak i [[yii\\db\\ActiveQuery::viaTable()|viaTable()]], aby \nokreślić tabelę węzła. Różnica pomiędzy [[yii\\db\\ActiveQuery::via()|via()]] i [[yii\\db\\ActiveQuery::viaTable()|viaTable()]] jest taka, że pierwsza metoda \ndefiniuje tabelę węzła dla istniejącej nazwy relacji, podczas gdy druga definiuje bezpośrednio węzeł. Przykład:\n\n```php\nclass Order extends ActiveRecord\n{\n    public function getItems()\n    {\n        return $this->hasMany(Item::class, ['id' => 'item_id'])\n            ->viaTable('order_item', ['order_id' => 'id']);\n    }\n}\n```\n\nlub alternatywnie,\n\n```php\nclass Order extends ActiveRecord\n{\n    public function getOrderItems()\n    {\n        return $this->hasMany(OrderItem::class, ['order_id' => 'id']);\n    }\n\n    public function getItems()\n    {\n        return $this->hasMany(Item::class, ['id' => 'item_id'])\n            ->via('orderItems');\n    }\n}\n```\n\nSposób użycia relacji zadeklarowanych z pomocą tabeli węzła jest taki sam jak dla zwykłych relacji. Dla przykładu:\n\n```php\n// SELECT * FROM `order` WHERE `id` = 100\n$order = Order::findOne(100);\n\n// SELECT * FROM `order_item` WHERE `order_id` = 100\n// SELECT * FROM `item` WHERE `item_id` IN (...)\n// zwraca tablicę obiektów Item\n$items = $order->items;\n```\n\n\n### Pobieranie leniwe i gorliwe <span id=\"lazy-eager-loading\"></span>\n\nW sekcji [Uzyskiwanie dostępu do danych relacji](#accessing-relational-data) wyjaśniliśmy, że można uzyskać dostęp do właściwości relacji instancji \nActive Record w identyczny sposób jak w przypadku zwykłych właściwości obiektu. Kwerenda SQL zostanie wykonana tylko w momencie pierwszego \nodwołania się do właściwości relacji. Taki sposób uzyskiwania relacyjnych danych nazywamy *pobieraniem leniwym*.\nPrzykład:\n\n```php\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123\n$orders = $customer->orders;\n\n// bez wykonywania zapytania SQL\n$orders2 = $customer->orders;\n```\n\nLeniwe pobieranie jest bardzo wygodne w użyciu, może jednak powodować spadek wydajności aplikacji, kiedy konieczne jest uzyskanie dostępu do \ntej samej relacyjnej właściwości dla wielu instancji Active Record. Rozważmy poniższy przykład - ile zapytań SQL zostanie wykonanych?\n\n```php\n// SELECT * FROM `customer` LIMIT 100\n$customers = Customer::find()->limit(100)->all();\n\nforeach ($customers as $customer) {\n    // SELECT * FROM `order` WHERE `customer_id` = ...\n    $orders = $customer->orders;\n}\n```\n\nJak wynika z opisu powyżej, zostanie wykonanych aż 101 kwerend SQL! Dzieje się tak, ponieważ za każdym razem, gdy uzyskujemy dostęp do właściwości \nrelacyjnej `orders` dla kolejnego obiektu `Customer` w pętli, wykonywane jest nowe zapytanie SQL.\n\nAby rozwiązać ten wydajnościowy problem, należy użyć tak zwanego *gorliwego pobierania*, jak w przykładzie poniżej:\n\n```php\n// SELECT * FROM `customer` LIMIT 100;\n// SELECT * FROM `orders` WHERE `customer_id` IN (...)\n$customers = Customer::find()\n    ->with('orders')\n    ->limit(100)\n    ->all();\n\nforeach ($customers as $customer) {\n    // kwerenda SQL nie jest wykonywana\n    $orders = $customer->orders;\n}\n```\n\nWywołanie metody [[yii\\db\\ActiveQuery::with()|with()]] powoduje pobranie zamówień dla pierwszych 100 klientów w pojedynczej kwerendzie SQL, dzięki czemu \nredukujemy ilość zapytań ze 101 do 2!\n\nMożliwe jest gorliwe pobranie jednej lub wielu relacji, a nawet gorliwe pobranie *zagnieżdżonych relacji*. Zagnieżdżona relacja to taka, która \nzostała zadeklarowana w relacyjnej klasie Active Record. Dla przykładu, `Customer` jest powiązany z `Order` poprzez relację `orders`, a `Order` \njest powiązany z `Item` poprzez relację `items`. Ładując dane dla `Customer`, możesz gorliwie pobrać `items` używając notacji zagnieżdżonej \nrelacji `orders.items`. \n\nPoniższy kod pokazuje różne sposoby użycia [[yii\\db\\ActiveQuery::with()|with()]]. Zakładamy, że klasa `Customer` posiada dwie relacje `orders` i \n`country`, a klasa `Order` jedną relację `items`.\n\n```php\n// gorliwe pobieranie \"orders\" i \"country\"\n$customers = Customer::find()->with('orders', 'country')->all();\n// odpowiednik powyższego w zapisie tablicowym\n$customers = Customer::find()->with(['orders', 'country'])->all();\n// kwerenda SQL nie jest wykonywana\n$orders= $customers[0]->orders;\n// kwerenda SQL nie jest wykonywana\n$country = $customers[0]->country;\n\n// gorliwe pobieranie \"orders\" i zagnieżdżonej relacji \"orders.items\"\n$customers = Customer::find()->with('orders.items')->all();\n// uzyskanie dostępu do produktów pierwszego zamówienia pierwszego klienta\n// kwerenda SQL nie jest wykonywana\n$items = $customers[0]->orders[0]->items;\n```\n\nMożesz pobrać gorliwie także głęboko zagnieżdżone relacje, jak np. `a.b.c.d`. Każda z kolejnych następujących po sobie relacji zostanie pobrana gorliwie - \nwywołując [[yii\\db\\ActiveQuery::with()|with()]] z `a.b.c.d`, pobierzesz `a`, `a.b`, `a.b.c` i `a.b.c.d`.  \n\n> Info: Podsumowując, podczas gorliwego pobierania `N` relacji, pośród których `M` relacji jest zdefiniowanych za pomocą \n> [tabeli węzła](#junction-table), zostanie wykonanych łącznie `N+M+1` kwerend SQL.\n> Zwróć uwagę na to, że zagnieżdżona relacja `a.b.c.d` jest liczona jako 4 relacje.\n\nPodczas gorliwego pobierania relacji, możesz dostosować kwerendę do własnych potrzeb korzystając z funkcji anonimowej.\nPrzykład:\n\n```php\n// znajdź klientów i pobierz ich kraje zamieszkania i aktywne zamówienia\n// SELECT * FROM `customer`\n// SELECT * FROM `country` WHERE `id` IN (...)\n// SELECT * FROM `order` WHERE `customer_id` IN (...) AND `status` = 1\n$customers = Customer::find()->with([\n    'country',\n    'orders' => function ($query) {\n        $query->andWhere(['status' => Order::STATUS_ACTIVE]);\n    },\n])->all();\n```\n\nDostosowując relacyjną kwerendę należy podać nazwę relacji jako klucz tablicy i użyć funkcji anonimowej jako odpowiadającej kluczowi wartości. \nFunkcja anonimowa otrzymuje parametr `$query`, reprezentujący obiekt [[yii\\db\\ActiveQuery|ActiveQuery]], służący do wykonania relacyjnej kwerendy.\nW powyższym przykładzie modyfikujemy relacyjną kwerendę dodając warunek ze statusem zamówienia.\n\n> Note: Wywołując [[yii\\db\\Query::select()|select()]] podczas gorliwego pobierania relacji, należy upewnić się, że kolumny określone w deklaracji \n> relacji znajdują się na liście pobieranych. W przeciwnym razie powiązany model może nie zostać poprawnie załadowany. Przykład:\n>\n> ```php\n> $orders = Order::find()->select(['id', 'amount'])->with('customer')->all();\n> // $orders[0]->customer ma zawsze wartość `null`. Aby rozwiązać ten problem, należy użyć:\n> $orders = Order::find()->select(['id', 'amount', 'customer_id'])->with('customer')->all();\n> ```\n\n\n### Przyłączanie relacji <span id=\"joining-with-relations\"></span>\n\n> Note: Zawartość tej sekcji odnosi się tylko do relacyjnych baz danych, takich jak MySQL, PostgreSQL, itp.\n\nRelacyjne kwerendy opisane do tej pory jedynie nawiązują do głównych kolumn tabeli podczas pobierania danych. W rzeczywistości często musimy \nodnieść się do kolumn w powiązanych tabelach. Przykładowo chcemy pobrać klientów, którzy złożyli przynajmniej jedno aktywne zamówienie - możemy tego \ndokonać za pomocą następującej przyłączającej kwerendy:\n\n```php\n// SELECT `customer`.* FROM `customer`\n// LEFT JOIN `order` ON `order`.`customer_id` = `customer`.`id`\n// WHERE `order`.`status` = 1\n// \n// SELECT * FROM `order` WHERE `customer_id` IN (...)\n$customers = Customer::find()\n    ->select('customer.*')\n    ->leftJoin('order', '`order`.`customer_id` = `customer`.`id`')\n    ->where(['order.status' => Order::STATUS_ACTIVE])\n    ->with('orders')\n    ->all();\n```\n\n> Note: Podczas tworzenia relacyjnych kwerend zawierających instrukcję SQL JOIN koniecznym jest ujednoznacznienie nazw kolumn. \n> Standardową praktyką w takim wypadku jest poprzedzenie nazwy kolumny odpowiadającą jej nazwą tabeli.\n\nJeszcze lepszym rozwiązaniem jest użycie istniejącej deklaracji relacji wywołując metodę [[yii\\db\\ActiveQuery::joinWith()|joinWith()]]:\n\n```php\n$customers = Customer::find()\n    ->joinWith('orders')\n    ->where(['order.status' => Order::STATUS_ACTIVE])\n    ->all();\n```\n\nOba rozwiązania wykonują te same zestawy instrukcji SQL, ale ostatnie jest o wiele schludniejsze. \n\n[[yii\\db\\ActiveQuery::joinWith()|joinWith()]] domyślnie korzysta z `LEFT JOIN` do przyłączenia głównej tabeli z relacyjną. \nMożesz określić inny typ przyłączenia (np. `RIGHT JOIN`) podając trzeci parametr `$joinType`. Jeśli chcesz użyć typu przyłączenia `INNER JOIN`, \nmożesz bezpośrednio wywołać metodę [[yii\\db\\ActiveQuery::innerJoinWith()|innerJoinWith()]].\n\nWywołanie [[yii\\db\\ActiveQuery::joinWith()|joinWith()]] domyślnie [pobierze gorliwie](#lazy-eager-loading) dane relacyjne.\nJeśli nie chcesz pobierać danych w ten sposób, możesz ustawić drugi parametr `$eagerLoading` na `false`. \n\nTak jak w przypadku [[yii\\db\\ActiveQuery::with()|with()]], możesz przyłączyć jedną lub wiele relacji na raz, dodać do nich dodatkowe warunki, \nprzyłączyć zagnieżdżone relacje i korzystać z zarówno [[yii\\db\\ActiveQuery::with()|with()]] jak i [[yii\\db\\ActiveQuery::joinWith()|joinWith()]]. Przykładowo:\n\n```php\n$customers = Customer::find()->joinWith([\n    'orders' => function ($query) {\n        $query->andWhere(['>', 'subtotal', 100]);\n    },\n])->with('country')\n    ->all();\n```\n\nCzasem, przyłączając dwie tabele, musisz sprecyzować dodatkowe warunki dla części `ON` kwerendy JOIN.\nMożna to zrobić wywołując metodę [[yii\\db\\ActiveQuery::onCondition()|onCondition()]] w poniższy sposób:\n\n```php\n// SELECT `customer`.* FROM `customer`\n// LEFT JOIN `order` ON `order`.`customer_id` = `customer`.`id` AND `order`.`status` = 1 \n// \n// SELECT * FROM `order` WHERE `customer_id` IN (...)\n$customers = Customer::find()->joinWith([\n    'orders' => function ($query) {\n        $query->onCondition(['order.status' => Order::STATUS_ACTIVE]);\n    },\n])->all();\n```\n\nPowyższa kwerenda pobiera *wszystkich* klientów i dla każdego z nich pobiera wszystkie aktywne zamówienia.\nZwróć uwagę na to, że ten przykład różni się od poprzedniego, gdzie pobierani byli tylko klienci posiadający przynajmniej jedno aktywne zamówienie.\n\n> Info: Jeśli [[yii\\db\\ActiveQuery|ActiveQuery]] zawiera warunek podany za pomocą [[yii\\db\\ActiveQuery::onCondition()|onCondition()]],\n> będzie on umieszczony w części instrukcji `ON` tylko jeśli kwerenda zawiera JOIN. W przeciwnym wypadku warunek ten będzie automatycznie \n> dodany do części `WHERE`. Może zatem składać się z warunków opierających się tylko na kolumnach powiązanej tabeli.\n\n\n#### Aliasy dołączanych tabeli <span id=\"relation-table-aliases\"></span>\n\nJak już wspomniano wcześniej, używając JOIN w kwerendzie, musimy ujednoznacznić nazwy kolumn. Z tego powodu często stosuje się aliasy dla tabel. \nAlias dla kwerendy relacyjnej można ustawić, modyfikując ją w następujący sposób:\n\n```php\n$query->joinWith([\n    'orders' => function ($q) {\n        $q->from(['o' => Order::tableName()]);\n    },\n])\n```\n\nPowyższy sposób wygląda jednak na bardzo skomplikowany i wymaga ręcznej modyfikacji w kodzie nazwy tabeli obiektu relacji lub wywołania `Order::tableName()`.\nOd wersji 2.0.7, Yii udostępnia do tego celu skróconą metodę. Możliwe jest zdefiniowanie i używanie aliasu dla tabeli relacji w poniższy sposób:\n\n```php\n// dołącz relację 'orders' i posortuj wyniki po 'orders.id'\n$query->joinWith(['orders o'])->orderBy('o.id');\n```\n\nPowyższy kod działa dla prostych relacji. Jeśli jednak potrzebujesz aliasu dla tabeli dołączonej w zagnieżdżonej relacji,\nnp. `$query->joinWith(['orders.product'])`, musisz rozwinąć wywołanie `joinWith` jak w poniższym przykładzie:\n\n```php\n$query->joinWith(['orders o' => function($q) {\n        $q->joinWith('product p');\n    }])\n    ->where('o.amount > 100');\n```\n\n### Odwrócone relacje <span id=\"inverse-relations\"></span>\n\nDeklaracje relacji są zazwyczaj obustronne dla dwóch klas Active Record. Przykładowo `Customer` jest powiązany z `Order` poprzez relację `orders`, \na `Order` jest powiązany jednocześnie z `Customer` za pomocą relacji `customer`.\n\n```php\nclass Customer extends ActiveRecord\n{\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n}\n\nclass Order extends ActiveRecord\n{\n    public function getCustomer()\n    {\n        return $this->hasOne(Customer::class, ['id' => 'customer_id']);\n    }\n}\n```\n\nRozważmy teraz poniższy kod:\n\n```php\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123\n$order = $customer->orders[0];\n\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer2 = $order->customer;\n\n// zwraca \"różne\"\necho $customer2 === $customer ? 'takie same' : 'różne';\n```\n\nWydawałoby się, że `$customer` i `$customer2` powinny być identyczne, ale jednak nie są! W rzeczywistości zawierają takie same dane klienta, ale \nsą różnymi obiektami. Wywołując `$order->customer` wykonywana jest dodatkowa kwerenda SQL do wypełnienia nowego obiektu `$customer2`.\n\nAby uniknąć nadmiarowego wykonywania ostatniej kwerendy SQL w powyższym przykładzie, powinniśmy wskazać Yii, że `customer` jest *odwróconą relacją* \n`orders` wywołując metodę [[yii\\db\\ActiveQuery::inverseOf()|inverseOf()]] jak pokazano to poniżej:\n\n```php\nclass Customer extends ActiveRecord\n{\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id'])->inverseOf('customer');\n    }\n}\n```\n\nZ tą dodatkową instrukcją w deklaracji relacji uzyskamy:\n\n```php\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123\n$order = $customer->orders[0];\n\n// kwerenda SQL nie jest wykonywana\n$customer2 = $order->customer;\n\n// wyświetla \"takie same\"\necho $customer2 === $customer ? 'takie same' : 'różne';\n```\n\n> Note: Odwrócone relacje nie mogą być definiowane dla relacji zawierających [tabelę węzła](#junction-table), dlatego też definiując relację z użyciem \n> [[yii\\db\\ActiveQuery::via()|via()]] lub [[yii\\db\\ActiveQuery::viaTable()|viaTable()]] nie powinno się już wywoływać \n> [[yii\\db\\ActiveQuery::inverseOf()|inverseOf()]].\n\n\n## Zapisywanie relacji <span id=\"saving-relations\"></span>\n\nPodczas pracy z danymi relacyjnymi często konieczne jest ustalenie związku pomiędzy różnymi danymi lub też usunięcie istniejącego połączenia. \nTakie akcje wymagają ustalenia właściwych wartości dla kolumn definiujących relacje. Korzystając z Active Record można użyć następujących instrukcji:\n\n```php\n$customer = Customer::findOne(123);\n$order = new Order();\n$order->subtotal = 100;\n// ...\n\n// ustawianie wartości dla atrybutu definiującego relację \"customer\" dla Order\n$order->customer_id = $customer->id;\n$order->save();\n```\n\nActive Record zawiera metodę [[yii\\db\\ActiveRecord::link()|link()]], która pozwala na uzyskanie powyższego w efektywniejszy sposób:\n\n```php\n$customer = Customer::findOne(123);\n$order = new Order();\n$order->subtotal = 100;\n// ...\n\n$order->link('customer', $customer);\n```\n\nMetoda [[yii\\db\\ActiveRecord::link()|link()]] wymaga podania konkretnej nazwy relacji i docelowej instancji Active Record, z którą powinna być nawiązany \nzwiązek. Mechanizm ten zmodyfikuje wartości atrybutów łączących obie instancje Active Record i zapisze je w bazie danych. W powyższym przykładzie \natrybut `customer_id` instancji `Order` otrzyma wartość atrybutu `id` instancji `Customer`, a następnie zostanie zapisany w bazie danych.\n\n> Note: Nie możesz łączyć w ten sposób dwóch świeżo utworzonych instancji Active Record.\n\nZaleta używania [[yii\\db\\ActiveRecord::link()|link()]] jest jeszcze bardziej widoczna, jeśli relacja jest zdefiniowana poprzez \n[tabelę węzła](#junction-table). Przykładowo możesz użyć następującego kodu, aby połączyć instancję `Order` z instancją `Item`:\n\n```php\n$order->link('items', $item);\n```\n\nPowyższy przykład automatycznie doda nowy wiersz w tabeli węzła `order_item`, aby połączyć zamówienie z produktem.\n\n> Info: Metoda [[yii\\db\\ActiveRecord::link()|link()]] NIE wykona automatycznie żadnego procesu walidacji danych podczas zapisywania instancji Active Record. \n> Na Tobie spoczywa obowiązek walidacji wszystkich danych przed wywołaniem tej metody.\n\nOdwrotną operacją do [[yii\\db\\ActiveRecord::link()|link()]] jest [[yii\\db\\ActiveRecord::unlink()|unlink()]], która usuwa istniejący związek pomiędzy \ndwoma instancjami Active Record. Przykładowo:\n\n```php\n$customer = Customer::find()->with('orders')->where(['id' => 123])->one();\n$customer->unlink('orders', $customer->orders[0]);\n```\n\nDomyślnie metoda [[yii\\db\\ActiveRecord::unlink()|unlink()]] ustawia wartość klucza obcego (lub wielu kluczy obcych), który definiuje istniejącą relację, na `null`. \nMożna jednak zamiast tego wybrać opcję usuwania wiersza tabeli, który zawiera klucz obcy, ustawiając w metodzie parametr `$delete` na `true`.\n \nJeśli w relacji użyty jest węzeł, wywołanie [[yii\\db\\ActiveRecord::unlink()|unlink()]] spowoduje wyczyszczenie kluczy obcych w tabeli węzła lub też \nusunięcie odpowiadających im wierszy, jeśli `$delete` jest ustawione na `true`.\n\n\n## Relacje międzybazowe <span id=\"cross-database-relations\"></span> \n\nActive Record pozwala na deklarowanie relacji pomiędzy klasami Active Record zasilanymi przez różne bazy danych.\nBazy danych mogę być różnych typów (np. MySQL i PostgreSQL lub MS SQL i MongoDB) i mogą pracować na różnych serwerach. \nDo wykonania relacyjnych zapytań używa się takich samych procedur, jak w przypadku relacji w obrębie jednej bazy danych. Przykład:\n\n```php\n// Customer jest powiązany z tabelą \"customer\" w relacyjnej bazie danych (np. MySQL)\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public static function tableName()\n    {\n        return 'customer';\n    }\n\n    public function getComments()\n    {\n        // klient posiada wiele komentarzy\n        return $this->hasMany(Comment::class, ['customer_id' => 'id']);\n    }\n}\n\n// Comment jest powiązany z kolekcją \"comment\" w bazie danych MongoDB\nclass Comment extends \\yii\\mongodb\\ActiveRecord\n{\n    public static function collectionName()\n    {\n        return 'comment';\n    }\n\n    public function getCustomer()\n    {\n        // komentarz jest przypisany do jednego klienta\n        return $this->hasOne(Customer::class, ['id' => 'customer_id']);\n    }\n}\n\n$customers = Customer::find()->with('comments')->all();\n```\n\nMożesz używać większości funkcjonalności dostępnych dla relacyjnych kwerend opisanych w tym rozdziale. \n \n> Note: Użycie [[yii\\db\\ActiveQuery::joinWith()|joinWith()]] jest ograniczone do baz danych pozwalających na międzybazowe kwerendy JOIN, dlatego też \n> nie możesz użyć tej metody w powyższym przykładzie, ponieważ MongoDB nie wspiera instrukcji JOIN.\n\n\n## Niestandardowe klasy kwerend <span id=\"customizing-query-classes\"></span>\n\nDomyślnie wszystkie kwerendy Active Record używają klasy [[yii\\db\\ActiveQuery|ActiveQuery]]. Aby użyć niestandardowej klasy kwerend razem z klasą Active Record, \nnależy nadpisać metodę [[yii\\db\\ActiveRecord::find()|find()]], aby zwracała instancję żądanej klasy kwerend. Przykład:\n \n```php\n// plik Comment.php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass Comment extends ActiveRecord\n{\n    public static function find()\n    {\n        return new CommentQuery(get_called_class());\n    }\n}\n```\n\nOd tego momentu, za każdym razem, gdy wykonywana będzie kwerenda (np. `find()`, `findOne()`) lub pobierana relacja  (np. `hasOne()`) klasy `Comment`, \npraca będzie odbywać się na instancji `CommentQuery` zamiast `ActiveQuery`.\n\nTeraz należy zdefiniować klasę `CommentQuery`, którą można dopasować do własnych kreatywnych potrzeb, dzięki czemu budowanie zapytań bazodanowych będzie o wiele bardziej ułatwione. Dla przykładu,\n\n```php\n// plik CommentQuery.php\nnamespace app\\models;\n\nuse yii\\db\\ActiveQuery;\n\nclass CommentQuery extends ActiveQuery\n{\n    // dodatkowe warunki relacyjnej kwerendy dołączone jako domyślne (ten krok można pominąć)\n    public function init()\n    {\n        $this->andOnCondition(['deleted' => false]);\n        parent::init();\n    }\n\n    // ... dodaj zmodyfikowane metody kwerend w tym miejscu ...\n\n    public function active($state = true)\n    {\n        return $this->andOnCondition(['active' => $state]);\n    }\n}\n```\n\n> Note: Zwykle, zamiast wywoływać metodę [[yii\\db\\ActiveQuery::onCondition()|onCondition()]], powinno się używać metody \n> [[yii\\db\\ActiveQuery::andOnCondition()|andOnCondition()]] lub [[yii\\db\\ActiveQuery::orOnCondition()|orOnCondition()]], aby dołączać kolejne warunki zapytania w \n> konstruktorze kwerend, dzięki czemu istniejące warunki nie zostaną nadpisane.\n\nPowyższy przykład pozwala na użycie następującego kodu:\n \n```php\n$comments = Comment::find()->active()->all();\n$inactiveComments = Comment::find()->active(false)->all();\n```\n\n> Tip: Dla dużych projektów rekomendowane jest, aby używać własnych, odpowiednio dopasowanych do potrzeb, klas kwerend, dzięki czemu klasy Active Record \n> pozostają przejrzyste.\n\nMożesz także użyć nowych metod budowania kwerend przy definiowaniu relacji z `Comment` lub wykonywaniu relacyjnych kwerend:\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public function getActiveComments()\n    {\n        return $this->hasMany(Comment::class, ['customer_id' => 'id'])->active();\n    }\n}\n\n$customers = Customer::find()->joinWith('activeComments')->all();\n\n// lub alternatywnie\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public function getComments()\n    {\n        return $this->hasMany(Comment::class, ['customer_id' => 'id']);\n    }\n}\n\n$customers = Customer::find()->joinWith([\n    'comments' => function($q) {\n        $q->active();\n    }\n])->all();\n```\n\n> Info: W Yii 1.1 do tego celu służy mechanizm *podzbiorów (scope)*, nie jest on jednak bezpośrednio wspierany w Yii 2.0, a zamiast tego \n> powinno się używać dopasowanych do własnych potrzeb klas kwerend.\n\n\n## Pobieranie dodatkowych pól\n\nW momencie, gdy instancja Active Record pobiera dane z wyniku kwerendy, wartości kolumn przypisywane są do odpowiadających im atrybutów.\n\nMożliwe jest pobranie dodatkowych kolumn lub wartości za pomocą kwerendy i przypisanie ich w Active Record.\nPrzykładowo załóżmy, że mamy tabelę `room`, która zawiera informacje o pokojach dostępnych w hotelu. Każdy pokój przechowuje informacje na temat swojej \nwielkości za pomocą pól `length`, `width` i `height`.\nTeraz wyobraźmy sobie, że potrzebujemy pobrać listę wszystkich pokojów posortowaną po ich kubaturze w malejącej kolejności.\nNie możemy obliczyć kubatury korzystając z PHP, ponieważ zależy nam na szybkim posortowaniu rekordów i dodatkowo chcemy wyświetlić pole `volume` na liście.\nAby osiągnąć ten cel, musimy zadeklarować dodatkowe pole w klasie `Room` rozszerzającej Active Record, które przechowa wartość `volume`:\n\n```php\nclass Room extends \\yii\\db\\ActiveRecord\n{\n    public $volume;\n\n    // ...\n}\n```\n\nNastępnie należy skonstruować kwerendę, która obliczy kubaturę i wykona sortowanie:\n\n```php\n$rooms = Room::find()\n    ->select([\n        '{{room}}.*', // pobierz wszystkie kolumny\n        '([[length]] * [[width]] * [[height]]) AS volume', // oblicz kubaturę\n    ])\n    ->orderBy('volume DESC') // posortuj\n    ->all();\n\nforeach ($rooms as $room) {\n    echo $room->volume; // zawiera wartość obliczoną przez SQL\n}\n```\n\nMożliwość pobrania dodatkowych pól jest szczególnie pomocna przy kwerendach agregujących.\nZałóżmy, że potrzebujesz wyświetlić listę klientów wraz z liczbą zamówień, których dokonali.\nNajpierw musisz zadeklarować klasę `Customer` wraz z relacją `orders` i dodatkowym polem przechowującym liczbę zamówień:\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public $ordersCount;\n\n    // ...\n\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n}\n```\n\nTeraz już możesz skonstruować kwerendę, która przyłączy zamówienia i policzy ich liczbę:\n\n```php\n$customers = Customer::find()\n    ->select([\n        '{{customer}}.*', // pobierz wszystkie kolumny klienta\n        'COUNT({{order}}.id) AS ordersCount' // oblicz ilość zamówień\n    ])\n    ->joinWith('orders') // przyłącz tabelę węzła\n    ->groupBy('{{customer}}.id') // pogrupuj wyniki dla funkcji agregacyjnej\n    ->all();\n```\n\nWadą tej metody jest to, że jeśli informacja nie może zostać pobrana za pomocą kwerendy SQL, musi ona być obliczona oddzielnie. \nZatem po pobraniu konkretnego wiersza tabeli za pomocą regularnej kwerendy bez dodatkowej instrukcji select, niemożliwym będzie \nzwrócenie wartości dla dodatkowych pól. Tak samo stanie się w przypadku świeżo zapisanych rekordów.\n\n```php\n$room = new Room();\n$room->length = 100;\n$room->width = 50;\n$room->height = 2;\n\n$room->volume; // ta wartość będzie wynosić `null` ponieważ nie została jeszcze zadeklarowana\n```\n\nUżywając magicznych metod [[yii\\db\\BaseActiveRecord::__get()|__get()]] i [[yii\\db\\BaseActiveRecord::__set()|__set()]], możemy emulować \nzachowania właściwości:\n\n```php\nclass Room extends \\yii\\db\\ActiveRecord\n{\n    private $_volume;\n    \n    public function setVolume($volume)\n    {\n        $this->_volume = (float) $volume;\n    }\n    \n    public function getVolume()\n    {\n        if (empty($this->length) || empty($this->width) || empty($this->height)) {\n            return null;\n        }\n        \n        if ($this->_volume === null) {\n            $this->setVolume(\n                $this->length * $this->width * $this->height\n            );\n        }\n        \n        return $this->_volume;\n    }\n\n    // ...\n}\n```\n\nKiedy kwerenda nie zapewni wartości kubatury, model będzie w stanie automatycznie ją obliczyć, używając swoich atrybutów.\n\nMożesz obliczyć sumaryczne pola rónież korzystając ze zdefiniowanych relacji:\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    private $_ordersCount;\n    \n    public function setOrdersCount($count)\n    {\n        $this->_ordersCount = (int) $count;\n    }\n    \n    public function getOrdersCount()\n    {\n        if ($this->isNewRecord) {\n            return null; // dzięki temu unikamy wywołania kwerendy szukającej głównych kluczy o wartości null\n        }\n        \n        if ($this->_ordersCount === null) {\n            $this->setOrdersCount($this->getOrders()->count()); // oblicz sumę na żądanie z relacji\n        }\n\n        return $this->_ordersCount;\n    }\n\n    // ...\n\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n}\n```\n\nDla powyższego kodu, kiedy 'ordersCount' występuje w instrukcji 'select' - `Customer::ordersCount` zostanie wypełnione \nrezultatem kwerendy, w pozostałych przypadkach zostanie obliczone na żądanie używając relacji `Customer::orders`.\n\nTakie podejście może być równie dobrze użyte do stworzenia skrótów dla niektórych danych relacji, zwłąszcza tych służących do obliczania sumarycznego.\nPrzykładowo:\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    /**\n     * Deklaracja wirtualnej właściwości tylko do odczytu dla danych sumarycznych.\n     */\n    public function getOrdersCount()\n    {\n        if ($this->isNewRecord) {\n            return null; // to pozwala na uniknięcie uruchamiania wyszukującej kwerendy dla pustych kluczy głównych\n        }\n        \n        return empty($this->ordersAggregation) ? 0 : $this->ordersAggregation[0]['counted'];\n    }\n\n    /**\n     * Deklaracja zwykłej relacji 'orders'.\n     */\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n\n    /**\n     * Deklaracja nowej relacji bazującej na 'orders', ale zapewniającej pobranie danych sumarycznych.\n     */\n    public function getOrdersAggregation()\n    {\n        return $this->getOrders()\n            ->select(['customer_id', 'counted' => 'count(*)'])\n            ->groupBy('customer_id')\n            ->asArray(true);\n    }\n\n    // ...\n}\n\nforeach (Customer::find()->with('ordersAggregation')->all() as $customer) {\n    echo $customer->ordersCount; // wyświetla dane sumaryczne z relacji bez dodatkowej kwerendy dzięki gorliwemu pobieraniu\n}\n\n$customer = Customer::findOne($pk);\n$customer->ordersCount; // wyświetla dane sumaryczne z relacji pobranej leniwie\n```\n"
  },
  {
    "path": "docs/guide-pl/db-migrations.md",
    "content": "Migracje bazy danych\n====================\n\nW czasie rozwoju i utrzymywania aplikacji zasilanej danymi z bazy danych, struktura tej ostatniej ewoluuje podobnie jak\nsam kod źródłowy. Przykładowo, rozbudowując aplikację konieczne jest dodanie nowej tabeli, lub też już po wydaniu aplikacji \nna serwerze produkcyjnym przydałby się indeks, aby poprawić wydajność zapytania itd. Zmiana struktury bazy danych często \npociąga za sobą zmiany w kodzie źródłowym, dlatego też Yii udostępnia funkcjonalność tak zwanych *migracji bazodanowych*,\nktóra pozwala na kontrolowanie zmian w bazie danych (*migracji*).\n\nPoniższe kroki pokazują, jak migracje mogą być wykorzystane przez zespół deweloperski w czasie pracy:\n\n1. Tomek tworzy nową migrację (np. dodaje nową tabelę, zmienia definicję kolumny, itp.).\n2. Tomek rejestruje (\"commit\") nową migrację w systemie kontroli wersji (np. Git, Mercurial).\n3. Mariusz uaktualnia swoje repozytorium z systemu kontroli wersji i otrzymuje nową migrację.\n4. Mariusz dodaje migrację do swojej lokalnej bazy danych, dzięki czemu synchronizuje ją ze zmianami, które wprowadził \n   Tomek.\n\nA poniższe kroki opisują w skrócie jak stworzyć nowe wydanie z migracją bazy danych na produkcji:\n\n1. Rafał tworzy tag wydania dla repozytorium projektu, który zawiera nowe migracje bazy danych.\n2. Rafał uaktualnia kod źródłowy na serwerze produkcyjnym do otagowanej wersji.\n3. Rafał dodaje zebrane nowe migracje do produkcyjnej bazy danych.\n\nYii udostępnia zestaw narzędzi konsolowych, które pozwalają na:\n\n* utworzenie nowych migracji;\n* dodanie migracji;\n* cofnięcie migracji;\n* ponowne zaaplikowanie migracji;\n* wyświetlenie historii migracji i jej statusu.\n\nPowyższe narzędzia są dostępne poprzez komendę `yii migrate`. W tej sekcji opiszemy szczegółowo w jaki sposób z nich \nkorzystać. Możesz również zapoznać się ze sposobem użycia narzędzi w konsoli za pomocą komendy pomocy `yii help migrate`.\n\n> Tip: Migracje mogą modyfikować nie tylko schemat bazy danych, ale również same dane, a także mogą służyć do innych zadań\n  jak tworzenie hierarchi kontroli dostępu dla ról (RBAC) lub czyszczenie pamięci podręcznej.\n\n> Note: Modyfikowanie danych w migracji zwykle jest znacznie prostsze, jeśli użyje się do tego klas \n  [Active Record](db-active-record.md), dzięki logice już tam zaimplementowanej. Należy jednak pamiętać, że logika \n  aplikacji jest podatna na częste zmiany, a naturalnym stanem kodu migracji jest jego stałość - w przypadku zmian w \n  warstwie Active Record aplikacji ryzykujemy zepsucie migracji, które z niej korzystają. Z tego powodu kod migracji\n  powinien być utrzymywany niezależnie od pozostałej logiki aplikacji.\n\n\n## Tworzenie migracji <span id=\"creating-migrations\"></span>\n\nAby utworzyć nową migrację, uruchom poniższą komendę:\n\n```\nyii migrate/create <nazwa>\n```\n\nWymagany argument `nazwa` przekazuje zwięzły opis migracji. Przykładowo, jeśli migracja ma dotyczyć utworzenia nowej \ntabeli o nazwie *news*, możesz użyć jako argumentu `create_news_table` i uruchomić komendę:\n\n```\nyii migrate/create create_news_table\n```\n\n> Note: Argument `nazwa` zostanie użyty jako część nazwy klasy nowej migracji i z tego powodu powinien składać się tylko\n  z łacińskich liter, cyfr i/lub znaków podkreślenia.\n\nPowyższa komenda utworzy nowy plik klasy PHP o nazwie podobnej do `m150101_185401_create_news_table.php` w folderze \n`@app/migrations`. Plik będzie zawierał poniższy kod, gdzie zadeklarowany jest szkielet klasy `m150101_185401_create_news_table`:\n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function up()\n    {\n\n    }\n\n    public function down()\n    {\n        echo \"m101129_185401_create_news_table cannot be reverted.\\n\";\n\n        return false;\n    }\n\n    /*\n    // Use safeUp/safeDown to run migration code within a transaction\n    public function safeUp()\n    {\n    }\n\n    public function safeDown()\n    {\n    }\n    */\n}\n```\n\nKażda migracja zdefiniowana jest jako klasa PHP rozszerzająca [[yii\\db\\Migration]]. Nazwa klasy migracji jest generowana \nautomatycznie w formacie `m<YYMMDD_HHMMSS>_<Nazwa>`, gdzie\n\n* `<YYMMDD_HHMMSS>` to data i czas UTC wskazujące na moment utworzenia migracji,\n* `<Nazwa>` jest identyczna z wartością argumentu `nazwa` podanego dla komendy.\n\nWewnątrz klasy migracji należy napisać kod w metodzie `up()`, która wprowadzi zmiany w strukturze bazy danych.\nMożna również napisać kod w metodzie `down()`, który spowoduje cofnięcie zmian wprowadzonych w `up()`. Metoda `up()` jest\nuruchamiana w momencie aktualizacji bazy, a `down()` w momencie przywracania jej do poprzedniego stanu.\nPoniższy kod pokazuje, jak można zaimplementować klasę migracji, aby utworzyć tabelę `news`:\n\n```php\n<?php\n\nuse yii\\db\\Schema;\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function up()\n    {\n        $this->createTable('news', [\n            'id' => Schema::TYPE_PK,\n            'title' => Schema::TYPE_STRING . ' NOT NULL',\n            'content' => Schema::TYPE_TEXT,\n        ]);\n    }\n\n    public function down()\n    {\n        $this->dropTable('news');\n    }\n}\n```\n\n> Info: Nie wszystkie migracje są odwracalne. Dla przykładu, jeśli w `up()` usuwane są wiersze z tabeli, możesz nie być \n  w stanie przywrócić ich w metodzie `down()`. Może też zdarzyć się, że celowo nie podasz nic w `down()` - cofanie zmian\n  migracji bazy danych nie jest czymś powszechnym - w takim wypadku należy zwrócić `false` w metodzie `down()`, aby \n  wyraźnie wskazać, że migracja nie jest odwracalna.\n\nPodstawowa klasa migracji [[yii\\db\\Migration]] umożliwia połączenie z bazą danych poprzez właściwość \n[[yii\\db\\Migration::db|db]]. Możesz użyć jej do modyfikowania schematu bazy za pomocą metod opisanych w sekcji \n[Praca ze schematem bazy danych](db-dao.md#database-schema).\n\nPrzy tworzeniu tabeli albo kolumny zamiast używać rzeczywistych typów, powinno się stosować *typy abstrakcyjne*, dzięki \nczemu migracje będą niezależne od pojedynczych silników bazodanowych. Klasa [[yii\\db\\Schema]] definiuje zestaw stałych, \nktóre reprezentują wspierane typy abstrakcyjne. Stałe te nazwane są według schematu `TYPE_<Nazwa>`. Dla przykładu, \n`TYPE_PK` odnosi się do typu klucza głównego z autoinkrementacją; `TYPE_STRING` do typu łańcucha znaków. Kiedy migracja \njest dodawana do konkretnej bazy danych, typy abstrakcyjne są tłumaczone na odpowiadające im typy rzeczywiste. W przypadku \nMySQL, `TYPE_PK` jest zamieniony w `int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY`, a `TYPE_STRING` staje się `varchar(255)`.\n\nMożesz łączyć abstrakcyjne typy z dodatkowymi definicjami - w powyższym przykładzie ` NOT NULL` jest dodane do \n`Schema::TYPE_STRING`, aby oznaczyć, że kolumna nie może być ustawiona jako `null`.\n\n> Info: Mapowanie typów abstrakcyjnych na rzeczywiste jest określone we właściwości [[yii\\db\\QueryBuilder::$typeMap|$typeMap]]\n  dla każdej klasy `QueryBuilder` poszczególnych wspieranych silników baz danych.\n\nPocząwszy od wersji 2.0.6, możesz skorzystać z nowej klasy budowania schematów, która pozwala na znacznie wygodniejszy \nsposób definiowana kolumn. Dzięki temu migracja z przykładu powyżej może być napisana następująco:\n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function up()\n    {\n        $this->createTable('news', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string()->notNull(),\n            'content' => $this->text(),\n        ]);\n    }\n\n    public function down()\n    {\n        $this->dropTable('news');\n    }\n}\n```\n\nLista wszystkich metod do definiowania typów kolumn dostępna jest w dokumentacji API dla [[yii\\db\\SchemaBuilderTrait]].\n\n\n## Generowanie migracji <span id=\"generating-migrations\"></span>\n\nPocząwszy od wersji 2.0.7 konsola migracji pozwala na wygodne utworzenie nowej migracji.\n\nJeśli nazwa migracji podana jest w jednej z rozpoznawalnych form, np. `create_xxx_table` lub `drop_xxx_table`, wtedy \nwygenerowany plik migracji będzie zawierał dodatkowy kod, w tym przypadku odpowiednio kod tworzenia i usuwania tabeli.\nPoniżej opisane są wszystkie warianty tej funkcjonalności.\n\n### Tworzenie tabeli\n\n```\nyii migrate/create create_post_table\n```\n\ngeneruje\n\n```php\n/**\n * Handles the creation for table `post`.\n */\nclass m150811_220037_create_post_table extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey()\n        ]);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        $this->dropTable('post');\n    }\n}\n```\n\nAby jednocześnie od razu dodać kolumny tabeli, zdefiniuj je za pomocą opcji `--fields`.\n\n```\nyii migrate/create create_post_table --fields=\"title:string,body:text\"\n```\n\ngeneruje\n\n```php\n/**\n * Handles the creation for table `post`.\n */\nclass m150811_220037_create_post_table extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string(),\n            'body' => $this->text(),\n        ]);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        $this->dropTable('post');\n    }\n}\n\n```\n\nMożesz określić też więcej parametrów kolumny.\n\n```\nyii migrate/create create_post_table --fields=\"title:string(12):notNull:unique,body:text\"\n```\n\ngeneruje\n\n```php\n/**\n * Handles the creation for table `post`.\n */\nclass m150811_220037_create_post_table extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string(12)->notNull()->unique(),\n            'body' => $this->text()\n        ]);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        $this->dropTable('post');\n    }\n}\n```\n\n> Note: Klucz główny jest dodawany automatycznie i nazwany domyślnie `id`. Jeśli chcesz użyć innej nazwy, możesz \n  zdefiniować go bezpośrednio np. `--fields=\"name:primaryKey\"`.\n\n#### Klucze obce\n\nPocząwszy od wersji 2.0.8 generator pozwala na zdefiniowanie kluczy obcych za pomocą opcji `foreignKey`.\n\n```\nyii migrate/create create_post_table --fields=\"author_id:integer:notNull:foreignKey(user),category_id:integer:defaultValue(1):foreignKey,title:string,body:text\"\n```\n\ngeneruje\n\n```php\n/**\n * Handles the creation for table `post`.\n * Has foreign keys to the tables:\n *\n * - `user`\n * - `category`\n */\nclass m160328_040430_create_post_table extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey(),\n            'author_id' => $this->integer()->notNull(),\n            'category_id' => $this->integer()->defaultValue(1),\n            'title' => $this->string(),\n            'body' => $this->text(),\n        ]);\n\n        // creates index for column `author_id`\n        $this->createIndex(\n            'idx-post-author_id',\n            'post',\n            'author_id'\n        );\n\n        // add foreign key for table `user`\n        $this->addForeignKey(\n            'fk-post-author_id',\n            'post',\n            'author_id',\n            'user',\n            'id',\n            'CASCADE'\n        );\n\n        // creates index for column `category_id`\n        $this->createIndex(\n            'idx-post-category_id',\n            'post',\n            'category_id'\n        );\n\n        // add foreign key for table `category`\n        $this->addForeignKey(\n            'fk-post-category_id',\n            'post',\n            'category_id',\n            'category',\n            'id',\n            'CASCADE'\n        );\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        // drops foreign key for table `user`\n        $this->dropForeignKey(\n            'fk-post-author_id',\n            'post'\n        );\n\n        // drops index for column `author_id`\n        $this->dropIndex(\n            'idx-post-author_id',\n            'post'\n        );\n\n        // drops foreign key for table `category`\n        $this->dropForeignKey(\n            'fk-post-category_id',\n            'post'\n        );\n\n        // drops index for column `category_id`\n        $this->dropIndex(\n            'idx-post-category_id',\n            'post'\n        );\n\n        $this->dropTable('post');\n    }\n}\n```\n\nUmiejscowienie słowa `foreignKey` w definicji kolumny nie ma znaczenia dla generatora, zatem:\n\n- `author_id:integer:notNull:foreignKey(user)`\n- `author_id:integer:foreignKey(user):notNull`\n- `author_id:foreignKey(user):integer:notNull`\n\nwygenerują ten sam kod.\n\nOpcja `foreignKey` może być wzbogacona o parametr w nawiasach, który oznacza nazwę tabeli relacji dla generowanego \nklucza obcego. Bez tego parametru użyta zostanie nazwa tabeli relacji zgodna z nazwą kolumny.\n\nW przykładzie powyżej `author_id:integer:notNull:foreignKey(user)` wygeneruje kolumnę o nazwie `author_id` z kluczem \nobcym wskazującym na tabelę `user`, natomiast `category_id:integer:defaultValue(1):foreignKey` wygeneruje kolumnę \n`category_id` z kluczem obcym wskazującym na tabelę `category`.\n\nPocząwszy od wersji 2.0.11, dla `foreignKey` można podać drugi parametr, oddzielony białym znakiem, z nazwą kolumny \nrelacji dla generowanego klucza obcego. Jeśli drugi parametr nie jest podany, nazwa kolumny jest pobierana ze schematu tabeli.\nJeśli schemat nie istnieje , klucz główny nie jest ustawiony lub jest kluczem kompozytowym, używana jest domyślna nazwa `id`.\n\n### Usuwanie tabeli\n\n```\nyii migrate/create drop_post_table --fields=\"title:string(12):notNull:unique,body:text\"\n```\n\ngeneruje\n\n```php\nclass m150811_220037_drop_post_table extends Migration\n{\n    public function up()\n    {\n        $this->dropTable('post');\n    }\n\n    public function down()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string(12)->notNull()->unique(),\n            'body' => $this->text()\n        ]);\n    }\n}\n```\n\n### Dodawanie kolumny\n\nJeśli nazwa migracji jest w postaci `add_xxx_column_to_yyy_table`, wtedy plik będzie zawierał wywołania metod `addColumn` \ni `dropColumn`.\n\nAby dodać kolumnę:\n\n```\nyii migrate/create add_position_column_to_post_table --fields=\"position:integer\"\n```\n\nco generuje\n\n```php\nclass m150811_220037_add_position_column_to_post_table extends Migration\n{\n    public function up()\n    {\n        $this->addColumn('post', 'position', $this->integer());\n    }\n\n    public function down()\n    {\n        $this->dropColumn('post', 'position');\n    }\n}\n```\n\nMożesz dodać wiele kolumn jednocześnie:\n\n```\nyii migrate/create add_xxx_column_yyy_column_to_zzz_table --fields=\"xxx:integer,yyy:text\"\n```\n\n### Usuwanie kolumny\n\nJeśli nazwa migracji jest w postaci `drop_xxx_column_from_yyy_table`, wtedy plik będzie zawierał wywołania metod\n`dropColumn` i `addColumn`.\n\n```php\nyii migrate/create drop_position_column_from_post_table --fields=\"position:integer\"\n```\n\ngeneruje\n\n```php\nclass m150811_220037_drop_position_column_from_post_table extends Migration\n{\n    public function up()\n    {\n        $this->dropColumn('post', 'position');\n    }\n\n    public function down()\n    {\n        $this->addColumn('post', 'position', $this->integer());\n    }\n}\n```\n\n### Dodawanie tabeli węzła\n\nJeśli nazwa migracji jest w postaci `create_junction_table_for_xxx_and_yyy_tables` lub `create_junction_xxx_and_yyy_tables`,\nwtedy plik będzie zawierał kod potrzebny do wygenerowania tabeli węzła pomiędzy tabelami `xxx` i `yyy`.\n\n```\nyii migrate/create create_junction_table_for_post_and_tag_tables --fields=\"created_at:dateTime\"\n```\n\ngeneruje\n\n```php\n/**\n * Handles the creation for table `post_tag`.\n * Has foreign keys to the tables:\n *\n * - `post`\n * - `tag`\n */\nclass m160328_041642_create_junction_table_for_post_and_tag_tables extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $this->createTable('post_tag', [\n            'post_id' => $this->integer(),\n            'tag_id' => $this->integer(),\n            'created_at' => $this->dateTime(),\n            'PRIMARY KEY(post_id, tag_id)',\n        ]);\n\n        // creates index for column `post_id`\n        $this->createIndex(\n            'idx-post_tag-post_id',\n            'post_tag',\n            'post_id'\n        );\n\n        // add foreign key for table `post`\n        $this->addForeignKey(\n            'fk-post_tag-post_id',\n            'post_tag',\n            'post_id',\n            'post',\n            'id',\n            'CASCADE'\n        );\n\n        // creates index for column `tag_id`\n        $this->createIndex(\n            'idx-post_tag-tag_id',\n            'post_tag',\n            'tag_id'\n        );\n\n        // add foreign key for table `tag`\n        $this->addForeignKey(\n            'fk-post_tag-tag_id',\n            'post_tag',\n            'tag_id',\n            'tag',\n            'id',\n            'CASCADE'\n        );\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        // drops foreign key for table `post`\n        $this->dropForeignKey(\n            'fk-post_tag-post_id',\n            'post_tag'\n        );\n\n        // drops index for column `post_id`\n        $this->dropIndex(\n            'idx-post_tag-post_id',\n            'post_tag'\n        );\n\n        // drops foreign key for table `tag`\n        $this->dropForeignKey(\n            'fk-post_tag-tag_id',\n            'post_tag'\n        );\n\n        // drops index for column `tag_id`\n        $this->dropIndex(\n            'idx-post_tag-tag_id',\n            'post_tag'\n        );\n\n        $this->dropTable('post_tag');\n    }\n}\n```\n\nPocząwszy od wersji 2.0.11, nazwy kolumn kluczy obcych dla tabeli węzła są pobierane ze schematu tabel.\nJeśli tabela nie jest zdefiniowana w schemacie, lub jej klucz główny nie jest ustawiony lub jest kluczem kompozytowym, \nużywana jest domyślna nazwa `id`.\n\n### Migracje transakcyjne <span id=\"transactional-migrations\"></span>\n\nPrzy wykonywaniu skomplikowanych migracji bazodanowych, bardzo ważnym jest zapewnienie, aby wszystkie ich operacje \nzakończyły się sukcesem, a w przypadku niepowodzenia nie zostały wprowadzone tylko częściowo, dzięki czemu baza danych \nmoże zachować spójność. Zalecane jest, aby w tym celu wykonywać operacje migracji wewnątrz [transakcji](db-dao.md#performing-transactions).\n\nNajprostszym sposobem implementacji migracji transakcyjnych jest umieszczenie ich kodu w metodach `safeUp()` i `safeDown()`.\nMetody te różnią się od `up()` i `down()` tym, że są wywoływane automatycznie wewnątrz transakcji.\nW rezultacie niepowodzenie wykonania dowolnej z operacji skutkuje automatycznym cofnięciem wszystkich poprzenich udanych \noperacji.\n\nW poniższym przykładzie oprócz stworzenia tabeli `news` dodatkowo dodajemy pierwszy wiersz jej danych.\n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function safeUp()\n    {\n        $this->createTable('news', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string()->notNull(),\n            'content' => $this->text(),\n        ]);\n\n        $this->insert('news', [\n            'title' => 'test 1',\n            'content' => 'content 1',\n        ]);\n    }\n\n    public function safeDown()\n    {\n        $this->delete('news', ['id' => 1]);\n        $this->dropTable('news');\n    }\n}\n```\n\nZwróć uwagę na to, że dodając wiele operacji bazodanowych w `safeUp()`, zwykle powinieneś odwrócić kolejność ich \nwykonywania w `safeDown()`. W naszym przykładzie najpierw tworzymy tabelę, a potem dodajemy wiersz w `safeUp()`, natomiast\nw `safeDown()` najpierw kasujemy wiersz, a potem usuwamy tabelę.\n\n> Note: Nie wszystkie silniki baz danych wspierają transakcje i nie wszystkie rodzaje komend bazodanowych można umieszczać\n  w transakcjach. Dla przykładu, zapoznaj się z rozdziałem dokumentacji MySQL \n  [Statements That Cause an Implicit Commit](https://dev.mysql.com/doc/refman/5.7/en/implicit-commit.html). W przypadku \n  braku możliwości skorzystania z transakcji, powinieneś użyć `up()` i `down()`.\n\n\n### Metody pozwalające na dostęp do bazy danych <span id=\"db-accessing-methods\"></span>\n\nBazowa klasa migracji [[yii\\db\\Migration]] udostępnia zestaw metod, dzięki którym można połączyć się z i manipulować \nbazą danych. Metody te są nazwane podobnie jak [metody DAO](db-dao.md) klasy [[yii\\db\\Command]].\nPrzykładowo metoda [[yii\\db\\Migration::createTable()]] pozwala na stworzenie nowej tabeli, tak jak \n[[yii\\db\\Command::createTable()]].\n\nZaletą korzystania z metod [[yii\\db\\Migration]] jest brak konieczności bezpośredniego tworzenia instancji [[yii\\db\\Command]],\na wywołanie każdej z tych metod dodatkowo wyświetli użyteczne informacje na temat operacji bazodanowych i \nczasu ich wykonywania.\n\nPoniżej znajdziesz listę wspomnianych wcześniej metod:\n\n* [[yii\\db\\Migration::execute()|execute()]]: wykonywanie komendy SQL\n* [[yii\\db\\Migration::insert()|insert()]]: dodawanie pojedynczego wiersza\n* [[yii\\db\\Migration::batchInsert()|batchInsert()]]: dodawanie wielu wierszy\n* [[yii\\db\\Migration::upsert()|upsert()]]: dodawanie pojedynczego wiersza lub aktualizowanie go, jeśli już istnieje (od 2.0.14)\n* [[yii\\db\\Migration::update()|update()]]: aktualizowanie wierszy\n* [[yii\\db\\Migration::delete()|delete()]]: usuwanie wierszy\n* [[yii\\db\\Migration::createTable()|createTable()]]: tworzenie tabeli\n* [[yii\\db\\Migration::renameTable()|renameTable()]]: zmiana nazwy tabeli\n* [[yii\\db\\Migration::dropTable()|dropTable()]]: usuwanie tabeli\n* [[yii\\db\\Migration::truncateTable()|truncateTable()]]: usuwanie wszystkich wierszy w tabeli\n* [[yii\\db\\Migration::addColumn()|addColumn()]]: dodawanie kolumny\n* [[yii\\db\\Migration::renameColumn()|renameColumn()]]: zmiana nazwy kolumny\n* [[yii\\db\\Migration::dropColumn()|dropColumn()]]: usuwanie kolumny\n* [[yii\\db\\Migration::alterColumn()|alterColumn()]]: zmiana definicji kolumny\n* [[yii\\db\\Migration::addPrimaryKey()|addPrimaryKey()]]: dodawanie klucza głównego\n* [[yii\\db\\Migration::dropPrimaryKey()|dropPrimaryKey()]]: usuwanie klucza głównego\n* [[yii\\db\\Migration::addForeignKey()|addForeignKey()]]: dodawanie klucza obcego\n* [[yii\\db\\Migration::dropForeignKey()|dropForeignKey()]]: usuwanie klucza obcego\n* [[yii\\db\\Migration::createIndex()|createIndex()]]: tworzenie indeksu\n* [[yii\\db\\Migration::dropIndex()|dropIndex()]]: usuwanie indeksu\n* [[yii\\db\\Migration::addCommentOnColumn()|addCommentOnColumn()]]: dodawanie komentarza do kolumny\n* [[yii\\db\\Migration::dropCommentFromColumn()|dropCommentFromColumn()]]: usuwanie komentarza z kolumny\n* [[yii\\db\\Migration::addCommentOnTable()|addCommentOnTable()]]: dodawanie komentarza do tabeli\n* [[yii\\db\\Migration::dropCommentFromTable()|dropCommentFromTable()]]: usuwanie komentarza z tabeli\n\n> Info: [[yii\\db\\Migration]] nie udostępnia metod dla kwerendy danych. Wynika to z tego, że zwykle nie jest potrzebne\n  wyświetlanie dodatkowych informacji na temat pobieranych danych z bazy. Dodatkowo możesz zawsze użyć potężnego\n  [Konstruktora kwerend](db-query-builder.md) do zbudowania i wywołania skomplikowanych kwerend.\n  Użycie konstruktora kwerend w migracji może wyglądać następująco:\n>\n> ```php\n> // uaktualnij kolumnę statusu dla wszystkich użytkowników\n> foreach((new Query)->from('users')->each() as $user) {\n>     $this->update('users', ['status' => 1], ['id' => $user['id']]);\n> }\n> ```\n\n## Stosowanie migracji <span id=\"applying-migrations\"></span>\n\nAby uaktualnić bazę danych do najświeższej wersji jej struktury, należy zastosować wszystkie dostępne nowe migracje, \nkorzystając z poniższej komendy:\n\n```\nyii migrate\n```\n\nKomenda ta wyświetli listę wszystkich migracji, które jeszcze nie zostały zastosowane. Jeśli potwierdzisz, że chcesz je\nzastosować, wywoła ona metodę `up()` lub `safeUp()` dla każdej z migracji na liście, w kolejności ich znaczników czasu.\nJeśli którakolwiek z migracji nie powiedzie się, komenda zakończy działanie bez stosowania pozostałych migracji.\n\n> Tip: Jeśli nie masz dostępu do linii komend na serwerze, wypróbuj rozszerzenie [web shell](https://github.com/samdark/yii2-webshell).\n\nDla każdej udanej migracji komenda doda wiersz do bazy danych w tabeli `migration`, aby oznaczyć fakt zastosowania migracji.\nPozwoli to na identyfikację, która z migracji została już zastosowana, a która jeszcze nie.\n\n> Info: Narzędzie do migracji automatycznie utworzy tabelę `migration` w bazie danych, wskazaną przez opcję \n  [[yii\\console\\controllers\\MigrateController::db|db]] komendy. Domyślnie jest to baza danych określona w \n  [komponencie aplikacji](structure-application-components.md) `db`.\n\nCzasem możesz mieć potrzebę zastosowania tylko jednej bądź kilku nowych migracji, zamiast wszystkich na raz.\nMożesz tego dokonać określając liczbę migracji, które chcesz zastosować uruchamiając komendę.\nPrzykładowo, poniższa komenda spróbuje zastosować następne trzy dostępne migracje:\n\n```\nyii migrate 3\n```\n\nMożesz również dokładnie wskazać konkretną migrację, która powinna być zastosowana na bazie danych, używając komendy \n`migrate/to` na jeden z poniższych sposobów:\n\n```\nyii migrate/to 150101_185401                      # używając znacznika czasu z nazwy migracji\nyii migrate/to \"2015-01-01 18:54:01\"              # używając łańcucha znaków, który może być sparsowany przez strtotime()\nyii migrate/to m150101_185401_create_news_table   # używając pełnej nazwy\nyii migrate/to 1392853618                         # używając UNIXowego znacznika czasu\n```\n\nJeśli dostępne są niezaaplikowane migracje wcześniejsze niż ta wyraźnie wskazane w komendzie, zostaną one zastosowane \nautomatycznie przed wskazaną migracją.\n\nJeśli wskazana migracja została już wcześniej zaaplikowana, wszystkie zaaplikowane aplikacje z późniejszą datą zostaną\ncofnięte.\n\n\n## Cofanie migracji <span id=\"reverting-migrations\"></span>\n\nAby odwrócić (wycofać) jedną lub więcej migracji, które zostały wcześniej zastosowane, możesz uruchomić następującą komendę:\n\n```\nyii migrate/down     # cofa ostatnio dodaną migrację\nyii migrate/down 3   # cofa 3 ostatnio dodane migracje\n```\n\n> Note: Nie wszystkie migracje są odwracalne. Próba cofnięcia takiej migracji spowoduje błąd i zatrzyma cały proces.\n\n\n## Ponawianie migracji <span id=\"redoing-migrations\"></span>\n\nPonawianie migracji oznacza najpierw wycofanie jej, a potem ponowne zastosowanie. Można tego dokonać następująco:\n\n```\nyii migrate/redo        # ponawia ostatnio zastosowaną migrację\nyii migrate/redo 3      # ponawia ostatnie 3 zastosowane migracje\n```\n\n> Note: Jeśli migracja nie jest odwracalna, nie będziesz mógł jej ponowić.\n\n## Odświeżanie migracji <span id=\"refreshing-migrations\"></span>\n\nPocząwszy od Yii 2.0.13 możliwe jest usunięcie wszystkich tabel i kluczy obcych z bazy danych i zastosowanie wszystkich \nmigracji od początku.\n\n```\nyii migrate/fresh       # czyści bazę danych i wykonuje wszystkie migracje od początku\n```\n\n## Lista migracji <span id=\"listing-migrations\"></span>\n\nAby wyświetlić listę wszystkich zastosowanych i oczekujących migracji, możesz użyć następujących komend:\n\n```\nyii migrate/history     # pokazuje ostatnie 10 zastosowanych migracji\nyii migrate/history 5   # pokazuje ostatnie 5 zastosowanych migracji\nyii migrate/history all # pokazuje wszystkie zastosowane migracje\n\nyii migrate/new         # pokazuje pierwsze 10 nowych migracji\nyii migrate/new 5       # pokazuje pierwsze 5 nowych migracji\nyii migrate/new all     # pokazuje wszystkie nowe migracje\n```\n\n\n## Modyfikowanie historii migracji <span id=\"modifying-migration-history\"></span>\n\nCzasem zamiast aplikowania lub odwracania migracji, możesz chcieć po prostu zaznaczyć, że baza danych zostałą już \nuaktualniona do konkretnej migracji. Może się tak zdarzyć, gdy ręcznie modyfikujesz bazę i nie chcesz, aby migracja(e) z\ntymi zmianami zostały potem ponownie zaaplikowane. Możesz to osiągnąć w następujący sposób:\n\n```\nyii migrate/mark 150101_185401                      # używając znacznika czasu z nazwy migracji\nyii migrate/mark \"2015-01-01 18:54:01\"              # używając łańcucha znaków, który może być sparsowany przez strtotime()\nyii migrate/mark m150101_185401_create_news_table   # używając pełnej nazwy\nyii migrate/mark 1392853618                         # używając UNIXowego znacznika czasu\n```\n\nKomenda zmodyfikuje tabelę `migration` poprzez dodanie lub usunięcie wierszy, aby zaznaczyć, że baza danych ma już \nzastosowane migracje aż do tej określonej w komendzie. Migracje nie zostaną faktycznie zastosowane lub usunięte.\n\n\n## Dostosowywanie migracji <span id=\"customizing-migrations\"></span>\n\nDostępnych jest kilka opcji pozwalających na dostosowanie komendy migracji do własnych potrzeb.\n\n\n### Użycie opcji linii komend <span id=\"using-command-line-options\"></span>\n\nKomenda migracji ma kilka opcji, które pozwalają na zmianę jej działąnia:\n\n* `interactive`: boolean (domyślnie `true`), określa czy przeprowadzić migrację w trybie interaktywnym.\n  Jeśli ustawione jest `true`, użytkownik będzie poproszony o potwierdzenie przed wykonaniem określonych operacji.\n  Możesz chcieć zmienić to ustawienie na `false`, jeśli komenda ma być używana w tle.\n\n* `migrationPath`: string|array (domyślnie `@app/migrations`), określa folder, gdzie znajdują się wszystkie pliki migracji.\n  Parametr może być określony jako rzeczywista ścieżka lub [alias](concept-aliases.md).\n  Zwróć uwagę na to, że folder musi istnieć, inaczej okmenda może wywołać błąd. Począwszy od wersji 2.0.12 można tutaj \n  podać tablicę, aby załadować migracje z wielu źródeł.\n\n* `migrationTable`: string (domyślnie `migration`), określa nazwę tabeli w bazie danych, gdzie trzymana będzie historia \n  migracji. Tabela będzie automatycznie stworzona, jeśli nie istnieje.\n  Możesz również utworzyć ją ręcznie używając struktury `version varchar(255) primary key, apply_time integer`.\n\n* `db`: string (domyślnie `db`), określa identyfikator bazodanowego [komponentu aplikacji](structure-application-components.md).\n  Reprezentuje on bazę danych, na której będą zastosowane migracje.\n\n* `templateFile`: string (domyślnie `@yii/views/migration.php`), określa ścieżkę pliku szablonu, używanego do generowania\n  szkieletu plików migracji. Parametr może być określony jako rzeczywista ścieżka lub [alias](concept-aliases.md). \n  Plik szablonu jest skryptem PHP, w którym możesz użyć predefiniowanej zmiennej `$className`, aby pobrać nazwę klasy\n  migracji.\n\n* `generatorTemplateFiles`: array (domyślnie `[\n        'create_table' => '@yii/views/createTableMigration.php',\n        'drop_table' => '@yii/views/dropTableMigration.php',\n        'add_column' => '@yii/views/addColumnMigration.php',\n        'drop_column' => '@yii/views/dropColumnMigration.php',\n        'create_junction' => '@yii/views/createTableMigration.php'\n  ]`), określa pliki szablonów do generowania kodu migracji. Po więcej szczegółów przejdź do \n  \"[Generowanie migracji](#generating-migrations)\".\n\n* `fields`: tablica definicji kolumn w postaci łańcuchów znaków do wygenerowania kodu migracji. Domyślnie `[]`.\n  Format każdej definicji to `NAZWA_KOLUMNY:TYP_KOLUMNY:DEKORATOR_KOLUMNY`. Dla przykładu, \n  `--fields=name:string(12):notNull` generuje kolumnę typu \"string\" o rozmiarze 12, która nie może mieć wartości `null`.\n\nPoniższy przykład pokazuje jak można użyć tych opcji.\n\nChcemy zmigrować moduł `forum`, którego pliki migracji znajdują się w folderze `migrations` modułu - używamy następującej \nkomendy:\n\n```\n# stosuje migracje dla modułu forum w trybie nieinteraktywnym\nyii migrate --migrationPath=@app/modules/forum/migrations --interactive=0\n```\n\n\n### Konfigurowanie komendy globalnie <span id=\"configuring-command-globally\"></span>\n\nZamiast podawać żmudnie te same opcje za każdym razem, gdy uruchamiamy komendę migracji, można ją skonfigurować w \nkonfiguracji aplikacji:\n\n```php\nreturn [\n    'controllerMap' => [\n        'migrate' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationTable' => 'backend_migration',\n        ],\n    ],\n];\n```\n\nPowyższa konfiguracja powoduje, że z każdym uruchomieniem komendy migracji, tabela `backend_migration` jest używana do\nzapisu historii migracji i nie musisz już określać jej za pomocą opcji linii komend `migrationTable`.\n\n\n### Migracje w przestrzeni nazw <span id=\"namespaced-migrations\"></span>\n\nPocząwszy od 2.0.10 możliwe jest używanie przestrzeni nazw w klasach migracji. Możesz zdefiniować listę przestrzeni nazw\nza pomocą [[yii\\console\\controllers\\MigrateController::migrationNamespaces|migrationNamespaces]]. Korzystanie z przestrzeni\nnazw pozwala na łatwe używanie wielu źródeł migracji. Przykładowo:\n\n```php\nreturn [\n    'controllerMap' => [\n        'migrate' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationPath' => null, // wyłącz ścieżkę do folderu migracji, jeśli dodajesz app\\migrations na liście poniżej\n            'migrationNamespaces' => [\n                'app\\migrations', // Wspólne migracje dla całej aplikacji\n                'module\\migrations', // Migracje konkretnego modułu\n                'some\\extension\\migrations', // Migracje konkretnego rozszerzenia\n            ],\n        ],\n    ],\n];\n```\n\n> Note: Migracje zaaplikowane z różnych przestrzeni nazw będą dodane do **pojedynczej** historii migracji, przez co np. \n  niemożliwym jest zastosowanie lub cofnięcie migracji z tylko wybranej przestrzeni nazw.\n\nWykonując operacje na migracjach z przestrzeni nazw: dodając nowe, odwracając je, itd., należy podać pełną przestrzeń nazw\nprzed nazwą migracji. Zwróć uwagę na to, że odwrotny ukośnik (`\\`) jest zwykle uważany za znak specjalny linii komend, \nzatem musisz odpowiednio zastosować symbol ucieczki, aby uniknąć błędów konsoli i niespodziewanych skutków komendy. \nDla przykładu:\n\n```\nyii migrate/create app\\\\migrations\\\\CreateUserTable\n```\n\n> Note: Migracje, których lokalizacja określona jest poprzez \n  [[yii\\console\\controllers\\MigrateController::migrationPath|migrationPath]] nie mogą zawierać przestrzeni nazw. Migracje \n  w przestrzeni nazw mogą być zaaplikowane tylko jeśli są wymienione we właściwości \n  [[yii\\console\\controllers\\MigrateController::migrationNamespaces]].\n\nPocząwszy od wersji 2.0.12 właściwość [[yii\\console\\controllers\\MigrateController::migrationPath|migrationPath]] pozwala\nrównież na podanie tablicy wymieniającej wszystkie foldery zawierające migracje bez przestrzeni nazw.\nZmiana ta została wprowadzona dla istniejących projektów, które używają migracji z wielu lokalizacji, głównie z zewnętrznych\nźródeł jak rozszerzenia Yii tworzone przez innych deweloperów, które z tego powodu nie mogą łatwo być zmodyfikowane, aby \nużywać przestrzeni nazw.\n\n#### Generowanie migracji w przestrzeni nazw\n\nMigracje w przestrzeni nazw korzystają z formatu nazw \"CamelCase\" `M<YYMMDDHHMMSS><Nazwa>` (przykładowo `M190720100234CreateUserTable`). \nGenerując taką migrację pamiętaj, że nazwa tabeli będzie przekonwertowana z formatu \"CamelCase\" na format \"podkreślnikowy\".\nDla przykładu:\n\n```\nyii migrate/create app\\\\migrations\\\\DropGreenHotelTable\n```\n\ngeneruje migrację w przestrzeni nazw `app\\migrations` usuwającą tabelę `green_hotel`, a\n\n```\nyii migrate/create app\\\\migrations\\\\CreateBANANATable\n```\n\ngeneruje migrację w przestrzeni nazw `app\\migrations` tworzącą tabelę `b_a_n_a_n_a`.\n\nJeśli nazwa tabeli zawiera małe i wielkie litery (like `studentsExam`), poprzedź nazwę podkreślnikiem:\n\n```\nyii migrate/create app\\\\migrations\\\\Create_studentsExamTable\n```\n\nTo wygeneruje migrację w przestrzeni nazw `app\\migrations` tworzącą tabelę `studentsExam`.\n\n### Rozdzielenie migracji <span id=\"separated-migrations\"></span>\n\nCzasem korzystanie z pojedynczej historii migracji dla wszystkich migracji w projekcie jest uciążliwe. Dla przykładu, \nmożesz zainstalować rozszerzenie 'blog', zawierające całkowicie oddzielne funkcjonalności i dostarczające własne migracje, \nktóre nie powinny wpływać na te dedykowane dla funkcjonalności głównego projektu.\n\nJeśli chcesz, aby część migracji mogła być zastosowana i śledzona całkowicie niezależnie od pozostałych, możesz skonfigurować \nkilka komend migracji, które będą używać różnych przestrzeni nazw i tabeli historii migracji:\n\n```php\nreturn [\n    'controllerMap' => [\n        // Wspólne migracje dla całej aplikacji\n        'migrate-app' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationNamespaces' => ['app\\migrations'],\n            'migrationTable' => 'migration_app',\n            'migrationPath' => null,\n        ],\n        // Migracje dla konkretnego modułu\n        'migrate-module' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationNamespaces' => ['module\\migrations'],\n            'migrationTable' => 'migration_module',\n            'migrationPath' => null,\n        ],\n        // Migrations dla konkretnego rozszerzenia\n        'migrate-rbac' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationPath' => '@yii/rbac/migrations',\n            'migrationTable' => 'migration_rbac',\n        ],\n    ],\n];\n```\n\nZwróć uwagę na to, że teraz, aby zsynchronizować bazę danych, musisz uruchomić kilka komend zamiast jednej:\n\n```\nyii migrate-app\nyii migrate-module\nyii migrate-rbac\n```\n\n\n## Migrowanie wielu baz danych <span id=\"migrating-multiple-databases\"></span>\n\nDomyślnie migracje są stosowane do jednej bazy danych określonej przez \n[komponent aplikacji](structure-application-components.md) `db`. Jeśli chcesz, aby były zastosowane do innej bazy, musisz\nzdefiniować opcję `db` w linii komend, jak poniżej,\n\n```\nyii migrate --db=db2\n```\n\nTa komenda zastosuje migracje do bazy `db2`.\n\nCzasem konieczne jest, aby zastosować *niektóre* migracje do jednej bazy, a inne do drugiej. Aby to uzyskać, podczas \nimplementacji klasy migracji należy bezpośrednio wskazać identyfikator komponentu bazy danych, który migracja ma użyć, \njak poniżej:\n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function init()\n    {\n        $this->db = 'db2';\n        parent::init();\n    }\n}\n```\n\nTa migracja będzie zastosowana do bazy `db2`, nawet jeśli w opcjach komendy określona będzie inna baza. \nZwróć uwagę na to, że historia migracji będzie uaktualniona wciąż w bazie danych określonej przez opcję `db` linii komend.\n\nJeśli masz wiele migracji korzystających z tej samej bazy danych, zalecane jest utworzenie bazowej klasy migracji z \npowyższym kodem metody `init()`, a następnie dziedziczenie po niej w każdej kolejnej migracji.\n\n> Tip: Oprócz ustawiania właściwości [[yii\\db\\Migration::db|db]], możesz również operować na różnych bazach poprzez \n  tworzenie nowych połączeń bazodanowych w klasach migracji, a następnie korzystanie z [metod DAO](db-dao.md) i tych\n  połączeń.\n\nInną strategią migracji wielu baz danych jest utrzymywanie migracji dla różnych baz w różnych folderach migracji. Dzięki\ntemu możesz te bazy migrować w osobnych komendach:\n\n```\nyii migrate --migrationPath=@app/migrations/db1 --db=db1\nyii migrate --migrationPath=@app/migrations/db2 --db=db2\n...\n```\n\nPierwsza komenda zastosuje migracje z folderu `@app/migrations/db1` na bazie `db1`, druga migracje z folderu \n`@app/migrations/db2` na bazie `db2`, itd.\n"
  },
  {
    "path": "docs/guide-pl/glossary.md",
    "content": "# A\n\n## alias\n\nAlias jest ciągiem znaków używanym przez Yii jako odnośnik do klasy lub folderu np. `@app/vendor`.\n\n## aplikacja\n\nAplikacja jest głównym obiektem przy obsłudze żądania HTTP. Zawiera komponenty, za pomocą których odbiera informacje z żądania i przekazuje je do odpowiedniego kontrolera w celu dalszego przetworzenia.\n\nObiekt aplikacji jest inicjowany jako singleton przez skrypt wejściowy i jest dostępny z każdego miejsca przez `\\Yii::$app`.\n\n## application\n\n[Zobacz aplikacja](#aplikacja).\n\n## asset\n\n[Zobacz zasób](#zasób).\n\n## atrybut\n\nAtrybut jest właściwością modelu (zmienną obiektu klasy lub magiczną właściwością definiowaną przez `__get()`/`__set()`), która przechowuje **dane biznesowe**.\n\n## attribute\n\n[Zobacz atrybut](#atrybut).\n\n# B\n\n## bundle\n\n[Zobacz pakiet](#pakiet).\n\n# C\n\n## configuration\n\n[Zobacz konfiguracja](#konfiguracja).\n\n# D\n\n## dostawca\n\nDostawca jest organizacją lub indywidualnym deweloperem dostarczającym kod w formie rozszerzeń, modułów i bibliotek.\n\n# E\n\n## extension\n\n[Zobacz rozszerzenie](#rozszerzenie).\n\n# I\n\n## instalacja\n\nInstalacja jest procesem przygotowania do działania, zarówno poprzez wykonanie instrukcji zawartych w pliku typu readme, jak i przez uruchomienie specjalnie do tego celu \nprzygotowanego skryptu. W przypadku Yii dotyczy to ustawienia praw dostępu i spełnienia wymogów oprogramowania.\n\n## installation\n\n[Zobacz instalacja](#instalacja).\n\n# K\n\n## konfiguracja\n\nKonfiguracja może odnosić się zarówno do procesu ustawiania właściwości obiektu, jak i do pliku konfiguracyjnego, przechowującego ustawienia obiektu lub klasy obiektów.\n\n# M\n\n## module\n\n[Zobacz moduł](#moduł).\n\n## moduł\n\nModuł jest mini aplikacją zawierającą elementy MVC, takie jak modele, widoki, kontrolery itp., która może być używana wewnątrz głównej aplikacji, poprzez przekierowanie obsługi \nżądań do modułu zamiast standardowo do własnego kontrolera.\n\n# N\n\n## namespace\n\n[Zobacz przestrzeń nazw](#przestrzeń-nazw).\n\n# P\n\n## pakiet\n\nPakiet opisuje listę powiązanych zasobów wraz z plikiem konfiguracyjnym definiującym ich zależności.\n\n## przestrzeń nazw\n\nPrzestrzeń nazw jest [funkcjonalnością języka PHP](https://www.php.net/manual/pl/language.namespaces.php), aktywnie wykorzystywaną w Yii 2.\n\n# R\n\n## rozszerzenie\n\nRozszerzenie jest zestawem klas, pakietów zasobów i konfiguracji, które dodają nowe funkcjonalności do aplikacji.\n\n# V\n\n## vendor\n\n[Zobacz dostawca](#dostawca).\n\n# Z\n\n## zasób\n\nZasób odnosi się do pliku. Zazwyczaj jest to kod JavaScript lub CSS, ale może zawierać wszystko, co jest dostępne z poziomu HTTP.\n"
  },
  {
    "path": "docs/guide-pl/helper-overview.md",
    "content": "Klasy pomocnicze\n================\n\n> Note: Ta sekcja jest w trakcie tworzenia.\n\nYii jest wyposażone w wiele klas upraszczających pisanie często wykorzystywanych zadań w kodzie, takich jak manipulowanie ciągami znaków bądź tablicami, generowanie kodu HTML, itp.\nTe pomocnicze klasy znajdują się w przestrzeni nazw `yii\\helpers` i wszystkie są klasami statycznymi (czyli zawierają wyłącznie statyczne właściwości i nie powinny być tworzone ich \ninstancje).\n\nAby skorzystać z klasy pomocnicznej, należy bezpośrednio wywołać jedną z jej statycznych metod, jak w przykładzie poniżej:\n\n```php\nuse yii\\helpers\\Html;\n\necho Html::encode('Test > test');\n```\n\n> Note: W celu zapewnienia możliwości [dostosowania klas pomocniczych do własnych potrzeb](#customizing-helper-classes), Yii rozdziela każdą z ich wbudowanych wersji \n> na dwie klasy: podstawę (np. `BaseArrayHelper`) i klasę właściwą (np. `ArrayHelper`). Kiedy chcesz użyć klasy pomocnicznej, powinieneś korzystać wyłącznie z jej właściwej wersji \n> i nigdy nie używać bezpośrednio podstawy.\n\n\nWbudowane klasy pomocnicze\n--------------------------\n\nPoniższe wbudowane klasy pomocnicze dostępne są w każdym wydaniu Yii:\n\n- [ArrayHelper](helper-array.md)\n- Console\n- FileHelper\n- FormatConverter\n- [Html](helper-html.md)\n- HtmlPurifier\n- Imagine (poprzez rozszerzenie yii2-imagine)\n- Inflector\n- Json\n- Markdown\n- StringHelper\n- [Url](helper-url.md)\n- VarDumper\n\n\nDostosowywanie klas pomocniczych do własnych potrzeb <span id=\"customizing-helper-classes\"></span>\n----------------------------------------------------\n\nAby zmodyfikować wbudowaną klasę pomocniczną (np. [[yii\\helpers\\ArrayHelper|ArrayHelper]]), należy stworzyć nową klasę rozszerzającą odpowiednią podstawę \n(np. [[yii\\helpers\\BaseArrayHelper|BaseArrayHelper]]) i nazwać ją identycznie jak jej wersja właściwa (np. [[yii\\helpers\\ArrayHelper|ArrayHelper]]), łącznie z zachowaniem jej \nprzestrzeni nazw. Ta klasa może następnie zostać użyta do zastąpienia oryginalnej implementacji we frameworku.\n\nPoniższy przykład ilustruje w jaki sposób zmodyfikować metodę [[yii\\helpers\\ArrayHelper::merge()|merge()]] klasy [[yii\\helpers\\ArrayHelper|ArrayHelper]]:\n\n```php\n<?php\n\nnamespace yii\\helpers;\n\nclass ArrayHelper extends BaseArrayHelper\n{\n    public static function merge($a, $b)\n    {\n        // zmodyfikowana wersja metody\n    }\n}\n```\n\nKlasę należy zapisać w pliku o nazwie `ArrayHelper.php`, który może znajdować się w dowolnym odpowiednim folderze, np. `@app/components`.\n\nNastępnie dopisujemy poniższą linijkę kodu w [skrypcie wejściowym](structure-entry-scripts.md) aplikacji po fragmencie dołączającym plik `yii.php`, \ndzięki czemu [autoloader klas Yii](concept-autoloading.md) załaduje zmodyfikowaną wersję klasy pomocniczej zamiast oryginalnej:\n\n```php\nYii::$classMap['yii\\helpers\\ArrayHelper'] = '@app/components/ArrayHelper.php';\n```\n\nNależy pamiętać o tym, że modyfikowanie klasy pomocniczej jest użyteczne tylko w przypadku, gdy chcemy zmienić domyślny sposób działania jej metody. \nW przypadku dodawania do aplikacji dodatkowych funkcjonalności, lepszym pomysłem jest stworzenie całkowicie nowej, osobnej klasy pomocniczej."
  },
  {
    "path": "docs/guide-pl/images/application-lifecycle.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.13-->\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d0\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key for=\"graphml\" id=\"d7\" yfiles.type=\"resources\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d0\"/>\n    <node id=\"n0\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"571.4472707112631\" width=\"763.2772213171534\" x=\"-1269.9373595143054\" y=\"-207.17439524332679\"/>\n              <y:Fill color=\"#FFCC0024\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FFCC00\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"763.2772213171534\" x=\"0.0\" y=\"0.0\">Entry script (index.php or yii)</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"225.33495140075684\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 4</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n0:\">\n        <node id=\"n0::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"324.9258883570935\" x=\"-1249.511914911339\" y=\"-169.79793039957679\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"126.759765625\" x=\"99.08306136604676\" y=\"5.6494140625\">Load application config<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n0::n1\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d5\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"309.37646484374994\" width=\"330.35133296005984\" x=\"-1254.9373595143054\" y=\"35.272875467936274\"/>\n                  <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"330.35133296005984\" x=\"0.0\" y=\"0.0\">Create application instance</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"19\" bottomF=\"19.15489692687993\" left=\"5\" leftF=\"5.425444602966309\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 5</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n0::n1:\">\n            <node id=\"n0::n1::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"72.64934031168627\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"45.34375\" x=\"124.79106917854676\" y=\"5.6494140625\">preInit()<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n1\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"122.64524027506516\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"120.71875\" x=\"87.10356917854676\" y=\"5.6494140625\">Register error handler<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n2\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"174.96110788981125\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"173.435546875\" x=\"60.74517074104676\" y=\"5.6494140625\">Configure application properties<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n3\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"226.56779181162517\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"27.33203125\" x=\"133.79692855354676\" y=\"5.6494140625\">init()<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n4\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.9258883570935\" x=\"-1234.511914911339\" y=\"280.4944433848063\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.025390625\" x=\"116.45024886604676\" y=\"5.6494140625\">bootstrap()<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n        <node id=\"n0::n2\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d5\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"411.6943410237631\" width=\"324.9258883570935\" x=\"-846.5860265542456\" y=\"-169.79793039957679\"/>\n                  <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"324.9258883570935\" x=\"0.0\" y=\"0.0\">Run application</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"1.1368683772161603E-13\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 3</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n0::n2:\">\n            <node id=\"n0::n2::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.9258883570935\" x=\"-831.5860265542456\" y=\"-132.42146555582667\"/>\n                  <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"164.705078125\" x=\"65.11040511604676\" y=\"5.6494140625\">EVENT_BEFORE_REQUEST<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n2::n1\" yfiles.foldertype=\"group\">\n              <data key=\"d4\"/>\n              <data key=\"d5\"/>\n              <data key=\"d6\">\n                <y:ProxyAutoBoundsNode>\n                  <y:Realizers active=\"0\">\n                    <y:GroupNode>\n                      <y:Geometry height=\"204.37646484375\" width=\"294.9258883570935\" x=\"-831.5860265542456\" y=\"-78.79793039957679\"/>\n                      <y:Fill color=\"#99336635\" transparent=\"false\"/>\n                      <y:BorderStyle hasColor=\"false\" type=\"dashed\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#993366\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#FFFFFF\" visible=\"true\" width=\"294.9258883570935\" x=\"0.0\" y=\"0.0\">Handle request</y:NodeLabel>\n                      <y:Shape type=\"roundrectangle\"/>\n                      <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                      <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                      <y:BorderInsets bottom=\"8\" bottomF=\"7.929194132486941\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                    </y:GroupNode>\n                    <y:GroupNode>\n                      <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                      <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 4</y:NodeLabel>\n                      <y:Shape type=\"roundrectangle\"/>\n                      <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                      <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                      <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                    </y:GroupNode>\n                  </y:Realizers>\n                </y:ProxyAutoBoundsNode>\n              </data>\n              <graph edgedefault=\"directed\" id=\"n0::n2::n1:\">\n                <node id=\"n0::n2::n1::n0\">\n                  <data key=\"d6\">\n                    <y:ShapeNode>\n                      <y:Geometry height=\"30.0\" width=\"264.9258883570935\" x=\"-816.5860265542456\" y=\"-41.421465555826785\"/>\n                      <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"231.4609375\" x=\"16.732475428546763\" y=\"5.6494140625\">Resolve request into route and parameters<y:LabelModel>\n                          <y:SmartNodeLabelModel distance=\"4.0\"/>\n                        </y:LabelModel>\n                        <y:ModelParameter>\n                          <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                        </y:ModelParameter>\n                      </y:NodeLabel>\n                      <y:Shape type=\"rectangle\"/>\n                    </y:ShapeNode>\n                  </data>\n                </node>\n                <node id=\"n0::n2::n1::n1\">\n                  <data key=\"d6\">\n                    <y:ShapeNode>\n                      <y:Geometry height=\"30.0\" width=\"264.9258883570935\" x=\"-816.5860265542456\" y=\"18.578534444173215\"/>\n                      <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"197.44140625\" x=\"33.74224105354676\" y=\"5.6494140625\">Create module, controller and action<y:LabelModel>\n                          <y:SmartNodeLabelModel distance=\"4.0\"/>\n                        </y:LabelModel>\n                        <y:ModelParameter>\n                          <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                        </y:ModelParameter>\n                      </y:NodeLabel>\n                      <y:Shape type=\"rectangle\"/>\n                    </y:ShapeNode>\n                  </data>\n                </node>\n                <node id=\"n0::n2::n1::n2\">\n                  <data key=\"d6\">\n                    <y:ShapeNode>\n                      <y:Geometry height=\"30.0\" width=\"264.9258883570935\" x=\"-816.5860265542456\" y=\"72.64934031168627\"/>\n                      <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.369140625\" x=\"101.77837386604676\" y=\"5.649414062500057\">Run action<y:LabelModel>\n                          <y:SmartNodeLabelModel distance=\"4.0\"/>\n                        </y:LabelModel>\n                        <y:ModelParameter>\n                          <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                        </y:ModelParameter>\n                      </y:NodeLabel>\n                      <y:Shape type=\"rectangle\"/>\n                    </y:ShapeNode>\n                  </data>\n                </node>\n              </graph>\n            </node>\n            <node id=\"n0::n2::n2\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.9258883570935\" x=\"-831.5860265542456\" y=\"149.20206960042316\"/>\n                  <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"154.697265625\" x=\"70.11431136604676\" y=\"5.6494140625\">EVENT_AFTER_REQUEST<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n2::n3\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-831.5860265542456\" y=\"196.89641062418633\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"148.099609375\" x=\"73.41313949104676\" y=\"5.6494140625\">Send response to end user<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n        <node id=\"n0::n3\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"324.9258883570935\" x=\"-846.5860265542456\" y=\"319.2728754679363\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"160.08203125\" x=\"82.42192855354676\" y=\"5.6494140625\">Complete request processing<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <edge id=\"e0\" source=\"n0\" target=\"n0::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-179.83580569315376\" sy=\"-180.3944529152355\" tx=\"-13.869777491410105\" ty=\"-154.8008369539754\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::e0\" source=\"n0::n0\" target=\"n0::n1\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#99CCFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"106.052734375\" x=\"-130.76131968438426\" y=\"78.18481445312506\">Configuration array<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"77.04379339868619\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e0\" source=\"n0::n1::n0\" target=\"n0::n1::n1\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e1\" source=\"n0::n1::n1\" target=\"n0::n1::n2\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e2\" source=\"n0::n1::n2\" target=\"n0::n1::n3\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e3\" source=\"n0::n1::n3\" target=\"n0::n1::n4\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::e1\" source=\"n0::n1\" target=\"n0::n2\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"2.8060680342755404\" sy=\"-48.37646484374994\" tx=\"-162.49660512430125\" ty=\"105.53540293375653\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::e0\" source=\"n0::n2::n0\" target=\"n0::n2::n1\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::n1::e0\" source=\"n0::n2::n1::n0\" target=\"n0::n2::n1::n1\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::n1::e1\" source=\"n0::n2::n1::n1\" target=\"n0::n2::n1::n2\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::e1\" source=\"n0::n2::n1\" target=\"n0::n2::n2\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::e2\" source=\"n0::n2::n2\" target=\"n0::n2::n3\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::e2\" source=\"n0::n2\" target=\"n0::n3\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#99CCFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"59.353515625\" x=\"-93.67673227804266\" y=\"28.318773905436274\">Exit status<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"63.99999999999999\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.47945569632951074\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d7\">\n    <y:Resources/>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-pl/images/application-structure.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"872.1807999999999\" y=\"-14.764159999999947\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"32.265625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"69.712890625\" x=\"15.1435546875\" y=\"1.3671875\">application\ncomponent<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"-91.97375999999994\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"70.28125\" x=\"14.859375\" y=\"8.43359375\">entry script<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"-14.764159999999947\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"68.21875\" x=\"15.890625\" y=\"8.43359375\">application<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"62.44544000000004\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"60.267578125\" x=\"19.8662109375\" y=\"8.433593750000007\">controller<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"872.1807999999999\" y=\"62.44544000000005\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"31.43359375\" x=\"34.283203125\" y=\"8.43359375\">filter<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"532.664\" y=\"23.901600000000087\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"47.728515625\" x=\"26.1357421875\" y=\"8.43359375\">module<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"618.4047999999991\" y=\"139.65504000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"29.611328125\" x=\"35.1943359375\" y=\"8.43359375\">view<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"786.161599999999\" y=\"139.65504000000004\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"40.28125\" x=\"29.859375\" y=\"8.43359375\">model<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n8\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"532.664\" y=\"216.86464000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"42.923828125\" x=\"28.5380859375\" y=\"8.43359375\">widget<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n9\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"216.86464000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"77.986328125\" x=\"11.0068359375\" y=\"8.43359375\">asset bundle<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n2\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.97265625\" x=\"2.5392475585936154\" y=\"-25.635106635436955\">1:1<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.025600000000054\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.17992697197425173\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n0\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-48.97602119140629\" y=\"-23.09200633216852\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.025599999999994\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5470891790487767\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n5\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"15.193962466822086\" y=\"30.674065521861735\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"44.894496863129426\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.25416574623968574\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n3\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"2.7719538085937074\" y=\"-26.04470613037104\">1..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"15.254400000000032\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.2090245199765919\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n3\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-57.39355288912964\" y=\"-63.846685518352906\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"43.47658308018222\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.881412889692355\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n5\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"582.664\" y=\"-3.598399999999913\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-17.888505566406252\" y=\"-42.41848039245597\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"16.483200000000004\" distanceToCenter=\"true\" position=\"right\" ratio=\"5.822600000000001\" segment=\"-2\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n6\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-12.223966589163297\" y=\"-24.76668258085064\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"11.385360101663515\" distanceToCenter=\"true\" position=\"left\" ratio=\"0.10670293331985262\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e7\" source=\"n7\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-9.313146217848043\" y=\"-26.098020948340803\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.669798038586146\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.1670192242704811\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e8\" source=\"n9\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-9.797233483694527\" y=\"-25.82443358614151\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.15599378957306\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.15481212573620068\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e9\" source=\"n8\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-15.633139430038\" y=\"-24.050683308790553\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"13.24330808446041\" distanceToCenter=\"true\" position=\"left\" ratio=\"0.05506723556297327\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e10\" source=\"n4\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-47.337621191406356\" y=\"-22.682408449706998\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"13.616000000000007\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e11\" source=\"n9\" target=\"n8\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-44.265598339843905\" y=\"-19.61040553222648\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.543999999999983\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.4117077892835431\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e12\" source=\"n7\" target=\"n6\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-44.81721357421975\" y=\"-19.610410805664003\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.543999999999983\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.4531592446547025\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources/>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-pl/images/rbac-access-check-1.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-pl/images/rbac-access-check-2.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-pl/images/rbac-access-check-3.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-pl/images/rbac-hierarchy-1.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n5\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-pl/images/rbac-hierarchy-2.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-pl/images/request-lifecycle.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"70.13700103759766\" width=\"56.558998107910156\" x=\"198.38633947503078\" y=\"261.099458694458\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"26.279499053955078\" y=\"74.13700103759766\">\n            <y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"-0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"0.5\" offsetX=\"0.0\" offsetY=\"4.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"27.34375\" x=\"-31.34375\" y=\"25.717914581298828\">user<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.5\" labelRatioY=\"0.0\" nodeRatioX=\"-0.5\" nodeRatioY=\"0.0\" offsetX=\"-4.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"637.1271178722382\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"41.158203125\" y=\"13.1494140625\">model<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"46.887996673583984\" width=\"39.527000427246094\" x=\"828.8018617630005\" y=\"544.9831195354463\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"53.376953125\" x=\"-6.924976348876953\" y=\"-30.723247528076172\">database<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-12.022075653076172\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"17.763500213623047\" y=\"21.443998336791992\">\n            <y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"713.6271178722382\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"28.005859375\" x=\"45.4970703125\" y=\"13.1494140625\">view<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n4\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"377.0372841596604\" width=\"219.27949905395508\" x=\"527.4919853210449\" y=\"407.9266515731811\"/>\n              <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"219.27949905395508\" x=\"0.0\" y=\"0.0\">controller</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"2\" leftF=\"1.7335329055786133\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"412.2765645980835\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 1</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n4:\">\n        <node id=\"n4::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"445.3031164169311\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"72.70703125\" x=\"38.92598342895508\" y=\"5.6494140625\">create action<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n4::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"45.0\" width=\"159.91864204406738\" x=\"558.5326642990112\" y=\"521.8166599273682\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"77.34765625\" x=\"41.28549289703369\" y=\"13.1494140625\">perform filters<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"diamond\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n4::n2\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"162.71328270435333\" width=\"187.54596614837646\" x=\"544.2255182266235\" y=\"607.2506530284882\"/>\n                  <y:Fill color=\"#99336635\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#993366\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#FFFFFF\" visible=\"true\" width=\"187.54596614837646\" x=\"0.0\" y=\"0.0\">action</y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"4\" bottomF=\"3.8368178606033325\" left=\"4\" leftF=\"3.9869680404663086\" right=\"3\" rightF=\"3.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 3</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n4::n2:\">\n            <node id=\"n4::n2::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"644.6271178722382\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.705078125\" x=\"43.92695999145508\" y=\"5.6494140625\">load model<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n4::n2::n1\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"721.1271178722382\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"66.02734375\" x=\"42.26582717895508\" y=\"5.6494140625\">render view<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n      </graph>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"198.3863394750308\" y=\"713.6271178722382\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"116.072265625\" x=\"1.4638671875000284\" y=\"13.1494140625\">response component<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"254.50096702575684\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"106.732421875\" x=\"6.1337890625\" y=\"13.1494140625\">request component<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"144.132230758667\" width=\"219.27949905395508\" x=\"527.4919853210449\" y=\"222.86873626708984\"/>\n              <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"219.27949905395508\" x=\"0.0\" y=\"0.0\">application</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"21\" leftF=\"20.720500946044922\" right=\"18\" rightF=\"18.0\" top=\"2\" topF=\"1.7557659149169922\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 2</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n7:\">\n        <node id=\"n7::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"262.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"73.369140625\" x=\"38.59492874145508\" y=\"5.6494140625\">resolve route<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n7::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"322.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"90.0390625\" x=\"30.259967803955078\" y=\"5.6494140625\">create controller<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <node id=\"n8\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"142.37646484375\" width=\"159.91864204406738\" x=\"313.2978515625\" y=\"224.62450218200684\"/>\n              <y:Fill color=\"#FFCC0024\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FFCC00\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"159.91864204406738\" x=\"0.0\" y=\"0.0\">entry script</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"1\" leftF=\"0.9186420440673828\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"225.33495140075684\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 4</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n8:\">\n        <node id=\"n8::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"129.0\" x=\"329.2164936065674\" y=\"262.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"85.3984375\" x=\"21.80078125\" y=\"5.6494140625\">load app config<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n8::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"129.0\" x=\"329.2164936065674\" y=\"322.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"82.052734375\" x=\"23.4736328125\" y=\"5.6494140625\">run application<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <edge id=\"e0\" source=\"n2\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n5\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-31.22050094604495\" sy=\"-22.49430537223816\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"28.00000600945461\" y=\"-193.17557203769684\">\n            <y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"17.34765625\" x=\"-8.673822115545391\" y=\"-200.52615797519684\">11<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n6\" target=\"n7::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-29.525518994973595\" y=\"-10.349628448486328\">3<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"0.9990329742431641\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.25395048761528816\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n7::e0\" source=\"n7::n0\" target=\"n7::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n0\" target=\"n8\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"-79.9882439360955\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"36.76878085201736\" y=\"-9.523307113000556\">1<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n7::n1\" target=\"n4\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"2.408647025755731\" ty=\"-181.21658369302781\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-4.962140296002758\" y=\"18.61224679946895\">4<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n3\" target=\"n4::n2::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-30.100868416252297\" y=\"-9.35060429573059\">9<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.26448415477215953\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n4::n2::n1\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"-2.2737367544323206E-13\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"17.34765625\" x=\"-83.18526519030615\" y=\"-9.350604295730818\">10<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.2786124840137136\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e7\" source=\"n8::n1\" target=\"n7\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"55.750299312644984\" sy=\"0.355224609375\" tx=\"-109.63961141576266\" ty=\"42.066115379333496\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"29.326010704040527\" y=\"-9.508390885372137\">2<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e8\" source=\"n1\" target=\"n4::n2::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-31.270312699786246\" y=\"-10.35060429573059\">8<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"1.0\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.2858946861565087\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e9\" source=\"n4::n1\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-79.9882439360951\" sy=\"2.2737367544323206E-13\" tx=\"30.048898971244075\" ty=\"-1.4999999999999991\">\n            <y:Point x=\"287.93523844627487\" y=\"544.3166599273684\"/>\n          </y:Path>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-76.70009317381192\" y=\"-9.350576400756609\">6<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.23459266172695797\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::e0\" source=\"n4::n0\" target=\"n4::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFEFD6\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-5.336933135986328\" y=\"4.999985313415493\">5<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.0\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::e1\" source=\"n4::n1\" target=\"n4::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"-81.01828954355172\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFEFD6\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-5.4491733540852465\" y=\"5.039560317993164\">7<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.0\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::n2::e0\" source=\"n4::n2::n0\" target=\"n4::n2::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n8::e0\" source=\"n8::n0\" target=\"n8::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"66px\" viewBox=\"0 0 57 66\" enable-background=\"new 0 0 57 66\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3799\" y1=\"-2276.8809\" x2=\"27.6209\" y2=\"-2306.6792\" gradientTransform=\"matrix(1 0 0 -1 0.2803 -2252.9199)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_13_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t&lt;path fill=\"#2068A3\" stroke=\"#2068A3\" d=\"M28.106,33.487c-8.112,0-12.688,4.312-12.688,10.437c0,7.422,12.688,10.438,12.688,10.438\n\t\ts14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.487,28.106,33.487z M26.288,53.051c0,0-7.135-2.093-8.805-7.201\n\t\tc-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"14.2417\" cy=\"9.1006\" r=\"53.247\" gradientTransform=\"matrix(1 0 0 -1 0.04 65.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#74AEEE\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#2068A3\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#2068A3\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.022,7.807-14.022,7.807s-10.472-2.484-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path fill=\"#5491CF\" stroke=\"#2068A3\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397c-0.514,1.027-1.669,4.084-1.669,5.148\n\t\tc0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.335-2.36c-3.601-1.419-4.071-3.063-5.89-4.854\n\t\tC12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t&lt;path fill=\"#5491CF\" stroke=\"#2068A3\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617c0.516,1.025,3.617,3.693,3.617,6.617\n\t\tc0,5.186-10.27,8.576-16.698,9.145c1.429,4.938,11.372,1.293,13.804-0.313c3.563-2.354,4.563-5.133,7.854-3.705\n\t\tC47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t&lt;path fill=\"none\" stroke=\"#2068A3\" stroke-linecap=\"round\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t&lt;path fill=\"none\" stroke=\"#2068A3\" stroke-linecap=\"round\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.623\" cy=\"-2278.646\" r=\"23.425\" fx=\"23.0534\" fy=\"-2281.1357\" gradientTransform=\"matrix(1 0 0 -1 0.2803 -2252.9199)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"5761.7578\" y1=\"11330.6484\" x2=\"5785.3872\" y2=\"11424.0977\" gradientTransform=\"matrix(0.275 0 0 0.2733 -1558.9874 -3088.4209)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M27.958,6.333c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.083,13.952,36.271,6.268,27.958,6.333z\"/&gt;\n\t&lt;path id=\"Hair_Young_Brown_1_\" fill=\"#CC9869\" stroke=\"#99724F\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n\t&lt;path fill=\"#4B4B4B\" stroke=\"#4B4B4B\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" d=\"M28.105,2\n\t\tC22.464,2,20.2,4.246,18.13,5.533C29.753,2.865,41.152,10.375,44.46,20.5C44.459,16.875,44.459,2,28.105,2z\"/&gt;\n\t&lt;path fill=\"#9B9B9B\" stroke=\"#4B4B4B\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" d=\"M11.151,17.751\n\t\tC12.878,8.25,18.686,6.309,25.273,7.127C31.295,7.875,36.93,10.491,44.459,20.5C37.777,7.125,20.278-3.375,9.903,3.921\n\t\tC5.569,6.97,4.903,13.375,11.151,17.751z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\"\n\t xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\t x=\"0px\" y=\"0px\" width=\"41px\" height=\"48px\" viewBox=\"-0.875 -0.887 41 48\" enable-background=\"new -0.875 -0.887 41 48\"\n\t xml:space=\"preserve\"&gt;\n&lt;defs&gt;\n&lt;/defs&gt;\n&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-979.1445\" x2=\"682.0508\" y2=\"-979.1445\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_1_)\" d=\"M19.625,36.763C8.787,36.763,0,34.888,0,32.575v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,34.888,30.464,36.763,19.625,36.763z\"/&gt;\n&lt;linearGradient id=\"SVGID_2_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-973.1445\" x2=\"682.0508\" y2=\"-973.1445\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_2_)\" d=\"M19.625,36.763c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.927-18.396,3.927\n\tc-9.481,0-17.396-1.959-18.396-3.927l-1.229,2C0,34.888,8.787,36.763,19.625,36.763z\"/&gt;\n&lt;path fill=\"#3C89C9\" d=\"M19.625,26.468c10.16,0,19.625,2.775,19.625,2.775c-0.375,2.721-5.367,5.438-19.554,5.438\n\tc-12.125,0-18.467-2.484-19.541-4.918C-0.127,29.125,9.465,26.468,19.625,26.468z\"/&gt;\n&lt;linearGradient id=\"SVGID_3_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-965.6948\" x2=\"682.0508\" y2=\"-965.6948\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_3_)\" d=\"M19.625,23.313C8.787,23.313,0,21.438,0,19.125v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,21.438,30.464,23.313,19.625,23.313z\"/&gt;\n&lt;linearGradient id=\"SVGID_4_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-959.6948\" x2=\"682.0508\" y2=\"-959.6948\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_4_)\" d=\"M19.625,23.313c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.926-18.396,3.926\n\tc-9.481,0-17.396-1.959-18.396-3.926l-1.229,2C0,21.438,8.787,23.313,19.625,23.313z\"/&gt;\n&lt;path fill=\"#3C89C9\" d=\"M19.476,13.019c10.161,0,19.625,2.775,19.625,2.775c-0.375,2.721-5.367,5.438-19.555,5.438\n\tc-12.125,0-18.467-2.485-19.541-4.918C-0.277,15.674,9.316,13.019,19.476,13.019z\"/&gt;\n&lt;linearGradient id=\"SVGID_5_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-952.4946\" x2=\"682.0508\" y2=\"-952.4946\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_5_)\" d=\"M19.625,10.113C8.787,10.113,0,8.238,0,5.925v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,8.238,30.464,10.113,19.625,10.113z\"/&gt;\n&lt;linearGradient id=\"SVGID_6_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-946.4946\" x2=\"682.0508\" y2=\"-946.4946\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_6_)\" d=\"M19.625,10.113c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.926-18.396,3.926\n\tc-9.481,0-17.396-1.959-18.396-3.926L0,5.925C0,8.238,8.787,10.113,19.625,10.113z\"/&gt;\n&lt;linearGradient id=\"SVGID_7_\" gradientUnits=\"userSpaceOnUse\" x1=\"644.0293\" y1=\"-943.4014\" x2=\"680.8223\" y2=\"-943.4014\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;ellipse fill=\"url(#SVGID_7_)\" cx=\"19.625\" cy=\"3.926\" rx=\"18.396\" ry=\"3.926\"/&gt;\n&lt;path opacity=\"0.24\" fill=\"#FFFFFF\" enable-background=\"new    \" d=\"M31.04,45.982c0,0-4.354,0.664-7.29,0.781\n\tc-3.125,0.125-8.952,0-8.952,0l-2.384-10.292l0.044-2.108l-1.251-1.154L9.789,23.024l-0.082-0.119L9.5,20.529l-1.65-1.254\n\tL5.329,8.793c0,0,4.213,0.903,7.234,1.07s8.375,0.25,8.375,0.25l3,9.875l-0.25,1.313l1.063,2.168l2.312,9.645l-0.521,1.416\n\tl1.46,1.834L31.04,45.982z\"/&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-pl/input-file-upload.md",
    "content": "Wysyłanie plików\n===============\n\nPrzesyłanie plików w Yii jest zazwyczaj wykonywane przy użyciu klasy [[yii\\web\\UploadedFile|UploadedFile]], która hermetyzuje każdy przesłany plik jako obiekt `UploadedFile`.\nW połączeniu z [[yii\\widgets\\ActiveForm|ActiveForm]] oraz [modelem](structure-models.md), możesz w łatwy sposób zaimplementować bezpieczny mechanizm przesyłania plików.\n\n## Tworzenie modeli <span id=\"creating-models\"></span>\n\nTak jak przy zwykłych polach tekstowych, aby przesłać pojedyńczy plik musisz utworzyć klasę modelu oraz użyć atrybutu tego modelu do przechowania instancji przesłanego pliku.\nPowinieneś również zadeklarować zasadę walidacji do zwalidowania przesłanego pliku.\nDla przykładu:\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\nuse yii\\web\\UploadedFile;\n\nclass UploadForm extends Model\n{\n    /**\n     * @var UploadedFile\n     */\n    public $imageFile;\n\n    public function rules()\n    {\n        return [\n            [['imageFile'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg'],\n        ];\n    }\n    \n    public function upload()\n    {\n        if ($this->validate()) {\n            $this->imageFile->saveAs('uploads/' . $this->imageFile->baseName . '.' . $this->imageFile->extension);\n            return true;\n        } else {\n            return false;\n        }\n    }\n}\n```\n\nW powyższym kodzie, atrybut `imageFile` zostanie użyty do przechowania instancji przesłanego pliku. Jest połączony z zasadą walidacji `file`, która korzysta z \nwalidatora [[yii\\validators\\FileValidator|FileValidator]], aby upewnić się, że przesłany plik posiada rozszerzenie `png` lub `jpg`.\nMetoda `upload()` wywoła walidację oraz zapis przesłanego pliku na serwerze.\n\nWalidator `file` pozwala na sprawdzenie rozszerzenia, wielkości, typu MIME, itp. \nPo więcej szczegółów zajrzyj do sekcji [Podstawowe walidatory](tutorial-core-validators.md#file)\n\n> Tip: Jeśli przesyłasz obrazek, możesz rozważyć użycie walidatora `image`. \n> Walidator ten jest implementowany przez [[yii\\validators\\ImageValidator|ImageValidator]], który weryfikuje czy atrybut otrzymał prawidłowy obrazek który może być \n> zapisany i przetworzony przez [rozszerzenie Imagine](https://github.com/yiisoft/yii2-imagine).\n\n## Renderowanie pola wyboru pliku <span id=\"rendering-file-input\"></span>\n\nPo zapisaniu modelu, utwórz pole wyboru pliku w widoku:\n\n```php\n<?php\nuse yii\\widgets\\ActiveForm;\n?>\n\n<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?>\n\n    <?= $form->field($model, 'imageFile')->fileInput() ?>\n\n    <button>Wyślij</button>\n\n<?php ActiveForm::end() ?>\n```\n\nNależy pamiętać, aby dodać opcję `enctype` do formularza, przez co plik będzie mógł być prawidłowo przesłany.\nWywołanie `fileInput()` spowoduje wyrenderowanie tagu `<input type=\"file\">`, który pozwala użytkownikowi na wybranie oraz przesłanie pliku.\n\n> Tip: od wersji 2.0.8, [[yii\\widgets\\ActiveField::fileInput|fileInput]] dodaje automatycznie opcję `enctype` do formularza, kiedy pole typu 'file input' jest używane.\n\n## Implementacja kontrolera <span id=\"wiring-up\"></span>\n\nW akcji kontrolera musimy połączyć model oraz widok aby zaimplementować przesyłanie plików:\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\nuse app\\models\\UploadForm;\nuse yii\\web\\UploadedFile;\n\nclass SiteController extends Controller\n{\n    public function actionUpload()\n    {\n        $model = new UploadForm();\n\n        if (Yii::$app->request->isPost) {\n            $model->imageFile = UploadedFile::getInstance($model, 'imageFile');\n            if ($model->upload()) {\n                // plik został przesłany\n                return;\n            }\n        }\n\n        return $this->render('upload', ['model' => $model]);\n    }\n}\n```\n\nW powyższym kodzie, kiedy formularz jest wysłany, metoda [[yii\\web\\UploadedFile::getInstance()|getInstance()]] wywoływana jest do reprezentowania pliku jako instancji `UploadedFile`.\nNastępnie przystępujemy do walidacji modelu, aby upewnić się, że przesłany plik jest prawidłowy, po czym zapisujemy go na serwerze.\n\n## Przesyłanie wielu plików <span id=\"uploading-multiple-files\"></span>\n\nMożesz przesyłać wiele plików za jednym razem, modyfikując odrobinę kod wylistowany w powyższych sekcjach.\n\nNajpierw powinieneś dostosować klasę modelu dodając opcję `maxFiles` do zasady walidacji `file`, aby określić dozwoloną maksymalną liczbę przesyłanych plików.\nMetoda `upload()` powinna również zostać zaktualizowana, aby zapisywać pliki jeden po drugim.\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\nuse yii\\web\\UploadedFile;\n\nclass UploadForm extends Model\n{\n    /**\n     * @var UploadedFile[]\n     */\n    public $imageFiles;\n\n    public function rules()\n    {\n        return [\n            [['imageFiles'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg', 'maxFiles' => 4],\n        ];\n    }\n    \n    public function upload()\n    {\n        if ($this->validate()) { \n            foreach ($this->imageFiles as $file) {\n                $file->saveAs('uploads/' . $file->baseName . '.' . $file->extension);\n            }\n            return true;\n        } else {\n            return false;\n        }\n    }\n}\n```\n\nW pliku widoku, powinieneś dodać opcję `multiple` do wywołania `fileInput()`, aby pole wyboru pliku pozwalało na wybór wielu plików na raz:\n \n```php\n<?php\nuse yii\\widgets\\ActiveForm;\n?>\n\n<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?>\n\n    <?= $form->field($model, 'imageFiles[]')->fileInput(['multiple' => true, 'accept' => 'image/*']) ?>\n\n    <button>Submit</button>\n\n<?php ActiveForm::end() ?>\n```\n\nNa koniec, w akcji kontrolera musimy zmienić wywołanie `UploadedFile::getInstance()` na `UploadedFile::getInstances()`, aby przypisać tablicę instancji `UploadedFile` \ndo `UploadForm::imageFiles`. \n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\nuse app\\models\\UploadForm;\nuse yii\\web\\UploadedFile;\n\nclass SiteController extends Controller\n{\n    public function actionUpload()\n    {\n        $model = new UploadForm();\n\n        if (Yii::$app->request->isPost) {\n            $model->imageFiles = UploadedFile::getInstances($model, 'imageFiles');\n            if ($model->upload()) {\n                // plik został przesłany\n                return;\n            }\n        }\n\n        return $this->render('upload', ['model' => $model]);\n    }\n}\n```\n"
  },
  {
    "path": "docs/guide-pl/input-form-javascript.md",
    "content": "Rozszerzanie ActiveForm po stronie klienta\n==========================================\n\nWidżet [[yii\\widgets\\ActiveForm]] posiada szereg wbudowanych metod JavaScript, służących do walidacji po stronie klienta.\nIch implementacja jest bardzo elastyczna i pozwala na rozszerzanie ich na wiele sposobów.\n\n## Zdarzenia ActiveForm\n\nActiveForm wyzwala serie dedykowanych zdarzeń. Używając poniższego kodu, można przechwycić te zdarzenia i je obsłużyć:\n\n```javascript\n$('#contact-form').on('beforeSubmit', function (e) {\n\tif (!confirm(\"Wszystko jest w porządku. Wysłać formularz?\")) {\n\t\treturn false;\n\t}\n\treturn true;\n});\n```\n\nPoniżej znajdziesz opis dostępnych zdarzeń.\n\n### `beforeValidate`\n\n`beforeValidate` jest wyzwalane przed walidacją całego formularza.\n\nSygnatura metody obsługującej to zdarzenie powinna wyglądać następująco:\n\n```javascript\nfunction (event, messages, deferreds)\n```\n\ngdzie\n\n- `event`: obiekt Event.\n- `messages`: asocjacyjna tablica, gdzie kluczami są ID atrybutów, a wartościami tablice opisów błędów dla tych atrybutów.\n- `deferreds`: tablica obiektów kolejkujących. Możesz użyć `deferreds.add(callback)`, aby dodać nową walidację do kolejki.\n\nJeśli metoda obsługująca zwróci boolean `false`, zatrzyma dalszą walidację formularza. W takim wypadku zdarzenie \n`afterValidate` nie będzie już wyzwalane.\n\n### `afterValidate`\n\n`afterValidate` jest wyzwalane po walidacji całego formularza.\n\nSygnatura metody obsługującej to zdarzenie powinna wyglądać następująco:\n\n```javascript\nfunction (event, messages, errorAttributes)\n```\n\ngdzie\n\n- `event`: obiekt Event.\n- `messages`: asocjacyjna tablica, gdzie kluczami są ID atrybutów, a wartościami tablice opisów błędów dla tych atrybutów.\n- `errorAttributes`: tablica atrybutów z błędami walidacji. Sprawdź konstrukcję `attributeDefaults`, aby dowiedzieć się więcej o strukturze tego parametru.\n\n### `beforeValidateAttribute`\n\n`beforeValidateAttribute` jest wyzwalane przed walidacją atrybutu.\n\nSygnatura metody obsługującej to zdarzenie powinna wyglądać następująco:\n\n```javascript\nfunction (event, attribute, messages, deferreds)\n```\n     \ngdzie\n\n- `event`: obiekt Event.\n- `attribute`: atrybut poddawany walidacji. Sprawdź konstrukcję `attributeDefaults`, aby dowiedzieć się więcej o strukturze tego parametru.\n- `messages`: tablica, do której możesz dodać opisy błędów walidacji dla wybranego atrybutu.\n- `deferreds`: tablica obiektów kolejki. Możesz użyć `deferreds.add(callback)`, aby dodać nową walidację do kolejki.\n\nJeśli metoda obsługująca zwróci boolean `false`, zatrzyma dalszą walidację wybranego atrybutu. W takim wypadku zdarzenie \n`afterValidateAttribute` nie będzie już wyzwalane.\n\n### `afterValidateAttribute`\n\n`afterValidateAttribute` jest wyzwalane po walidacji całego formularza i każdego atrybutu.\n\nSygnatura metody obsługującej to zdarzenie powinna wyglądać następująco:\n\n```javascript\nfunction (event, attribute, messages)\n```\n\ngdzie\n\n- `event`: obiekt Event.\n- `attribute`: atrybut poddawany walidacji. Sprawdź konstrukcję `attributeDefaults`, aby dowiedzieć się więcej o strukturze tego parametru.\n- `messages`: tablica, do której możesz dodać opisy błędów walidacji dla wybranego atrybutu.\n\n### `beforeSubmit`\n\n`beforeSubmit` jest wyzwalane przed wysłaniem formularza, po pomyślnej walidacji.\n\nSygnatura metody obsługującej to zdarzenie powinna wyglądać następująco:\n\n```javascript\nfunction (event)\n```\n\ngdzie event jest obiektem Event.\n\nJeśli metoda obsługująca zwróci boolean `false`, zatrzyma wysyłanie formularza.\n\n### `ajaxBeforeSend`\n         \n`ajaxBeforeSend` jest wyzwalane przed wysłaniem żądania AJAX w przypadku walidacji AJAX-owej.\n\nSygnatura metody obsługującej to zdarzenie powinna wyglądać następująco:\n\n```javascript\nfunction (event, jqXHR, settings)\n```\n\ngdzie\n\n- `event`: obiekt Event.\n- `jqXHR`: obiekt jqXHR.\n- `settings`: konfiguracja żądania AJAX.\n\n### `ajaxComplete`\n\n`ajaxComplete` jest wyzwalane po ukończeniu żądania AJAX w przypadku walidacji AJAX-owej.\n\nSygnatura metody obsługującej to zdarzenie powinna wyglądać następująco:\n\n```javascript\nfunction (event, jqXHR, textStatus)\n```\n\ngdzie\n\n- `event`: obiekt Event.\n- `jqXHR`: obiekt jqXHR.\n- `textStatus`: status żądania (\"success\", \"notmodified\", \"error\", \"timeout\", \"abort\" lub \"parsererror\").\n\n## Wysyłanie formularza za pomocą AJAX\n\nWalidacja może być przeprowadzona po stronie klienta lub za pomocą AJAX-a, ale wysyłanie formularza jest domyślnie przeprowadzane \nza pomocą zwyczajnego żądania. Jeśli chcesz przesłać formularz za pomocą AJAX, możesz to zrobić obsługując zdarzenie `beforeSubmit` \nformularza w następujący sposób:\n\n```javascript\nvar $form = $('#formId');\n$form.on('beforeSubmit', function() {\n    var data = $form.serialize();\n    $.ajax({\n        url: $form.attr('action'),\n        type: 'POST',\n        data: data,\n        success: function (data) {\n            // Implementacja pomyślnego statusu\n        },\n        error: function(jqXHR, errMsg) {\n            alert(errMsg);\n        }\n     });\n     return false; // powstrzymuje przed domyślnym sposobem wysłania\n});\n```\n\nAby dowiedzieć się więcej o funkcji jQuery `ajax()`, zapoznaj się z [dokumentacją jQuery](https://api.jquery.com/jQuery.ajax/).\n\n\n## Dynamiczne dodawanie pól\n\nWe współczesnych aplikacjach webowych często koniecznie jest modyfikowanie formularza już po tym, jak został zaprezentowany użytkownikowi.\nDla przykładu może to być dodawanie nowego pola po kliknięciu w ikonę \"z plusem\".\nAby uruchomić walidację takich pól, należy je zarejestrować za pomocą JavaScriptowego pluginu ActiveForm.\n\nPo dodaniu pola do formularza, należy dołączyć je również do listy walidacji:\n\n```javascript\n$('#contact-form').yiiActiveForm('add', {\n    id: 'address',\n    name: 'address',\n    container: '.field-address',\n    input: '#address',\n    error: '.help-block',\n    validate:  function (attribute, value, messages, deferred, $form) {\n        yii.validation.required(value, messages, {message: \"Informacja dotycząca walidacji tutaj\"});\n    }\n});\n```\n\nAby usunąć pole z listy walidacji (aby nie było już sprawdzane), możesz wykonać następujący kod:\n\n```javascript\n$('#contact-form').yiiActiveForm('remove', 'address');\n```\n"
  },
  {
    "path": "docs/guide-pl/input-forms.md",
    "content": "Tworzenie formularzy\n====================\n\nFormularze oparte na ActiveRecord: ActiveForm\n---------------------------------------------\nPodstawowym sposobem korzystania z formularzy w Yii jest użycie [[yii\\widgets\\ActiveForm|ActiveForm]]. Ten sposób powinien być używany, jeśli formularz jest bazowany na modelu.\nDodatkowo, klasa [[yii\\helpers\\Html|Html]] zawiera sporo użytecznych metod, które zazwyczaj używane są do dodawania przycisków i tekstów pomocniczych do każdego formularza.\n\nFormularz, który jest wyświetlany po stronie klienta, w większości przypadków, posiada odpowiedni [model](structure-models.md), który jest używany do walidacji danych wejściowych po \nstronie serwera (sprawdź sekcję [Walidacja danych wejściowych](input-validation.md) aby uzyskać więcej szczegółów).  \nPodczas tworzenia formularza na podstawie modelu, pierwszym krokiem jest zdefiniowanie samego modelu. \nModel może być bazowany na klasie [Active Record](db-active-record.md), reprezentując dane z bazy danych, lub może być też bazowany na klasie generycznej [[yii\\base\\Model|Model]], \naby przechwytywać dowolne dane wejściowe, np. formularz logowania.\n\n> Tip: Jeśli pola formularza są różne od kolumn tabeli w bazie danych lub też występuje tu formatowanie i logika specyficzna tylko dla tego formularza, \n> zaleca się stworzenie oddzielnego modelu rozszerzającego [[yii\\base\\Model]].\n\nW poniższym przykładzie pokażemy, jak model generyczny może być użyty do stworzenia formularza logowania:\n\n```php\n<?php\n\nclass LoginForm extends \\yii\\base\\Model\n{\n    public $username;\n    public $password;\n\n    public function rules()\n    {\n        return [\n            // zasady walidacji\n        ];\n    }\n}\n```\n\nW kontrolerze przekażemy instancję tego modelu do widoku, gdzie widżet [[yii\\widgets\\ActiveForm|ActiveForm]] zostanie użyty do wyświetlenia formularza:\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n$form = ActiveForm::begin([\n    'id' => 'login-form',\n    'options' => ['class' => 'form-horizontal'],\n]) ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n\n    <div class=\"form-group\">\n        <div class=\"col-lg-offset-1 col-lg-11\">\n            <?= Html::submitButton('Login', ['class' => 'btn btn-primary']) ?>\n        </div>\n    </div>\n<?php ActiveForm::end() ?>\n```\n\n### Otaczanie kodu przez `begin()` i `end()` <span id=\"wrapping-with-begin-and-end\"></span>\nW powyższym kodzie, [[yii\\widgets\\ActiveForm::begin()|begin()]] nie tylko tworzy instancję formularza, ale zaznacza też jego początek.\nCała zawartość położona pomiędzy [[yii\\widgets\\ActiveForm::begin()|begin()]] i [[yii\\widgets\\ActiveForm::end()|end()]] zostanie otoczona tagiem HTML'owym `<form>`.\nJak w przypadku każdego widżetu, możesz określić kilka opcji z jakimi widżet powinien być skonfigurowany przez przekazanie tablicy do metody `begin`.\nW tym przypadku dodatkowa klasa CSS i identyfikator ID zostały przekazane do otwierającego tagu `<form>`.\nAby zobaczyć wszystkie dostępne opcje, zajrzyj do dokumentacji API [[yii\\widgets\\ActiveForm|ActiveForm]].\n\nDo utworzenia formularza, wraz z elementami etykiet oraz wszelkimi walidacjami JavaScript, wywoływana jest metoda [[yii\\widgets\\ActiveForm::field()|field()]], która zwraca instancję \nobiektu [[yii\\widgets\\ActiveField|ActiveField]].\nKiedy rezultat tej metody jest bezpośrednio wyświetlany, tworzone jest regularne pole tekstowe.\nAby dostosować pola, możesz używać dodatkowych metod łączonych [[yii\\widgets\\ActiveField|ActiveField]]:\n\n```php\n// pole hasła\n<?= $form->field($model, 'password')->passwordInput() ?>\n// dodanie podpowiedzi oraz zmiana etykiety\n<?= $form->field($model, 'username')->textInput()->hint('Please enter your name')->label('Name') ?>\n// utworzenie pola email w formacie HTML5\n<?= $form->field($model, 'email')->input('email') ?>\n```\n\nPowyższy kod utworzy tagi `<label>`, `<input>` oraz wszystkie inne, według pól formularza zdefiniowanych w [[yii\\widgets\\ActiveField::$template|template]].\nNazwa pola określana jest automatycznie z modelu [[yii\\base\\Model::formName()|formName()]] i nazwy atrybutu.\nDla przykładu, nazwą pola dla atrybutu `username` w powyższym przykładzie będzie `LoginForm[username]`. \nTa zasada nazewnictwa spowoduje, że tablica wszystkich atrybutów z formularza logowania będzie dostępna w zmiennej `$_POST['LoginForm']` po stronie serwera.\n\nOkreślanie atrybutów modelu może być wykonane w bardziej wyrafinowany sposób. \nDla przykładu, kiedy atrybut będzie potrzebował pobierać tablicę wartości, podczas przesyłania wielu plików lub wybrania wielu pozycji, możesz określić go jako tablicę dodając `[]` do \nnazwy atrybutu:\n\n```php\n// pozwól na przesłanie wielu plików\necho $form->field($model, 'uploadFile[]')->fileInput(['multiple'=>'multiple']);\n\n// pozwól na zaznaczenie wielu pozycji\necho $form->field($model, 'items[]')->checkboxList(['a' => 'Item A', 'b' => 'Item B', 'c' => 'Item C']);\n```\n\nBądź ostrożny podczas nazywania elementów formularza takich jak przyciski wysyłania. \nOdnosząc się do [dokumentacji jQuery](https://api.jquery.com/submit), istnieje kilka zarezerwowanych nazw, które mogą powodować konflikty.\n\n> Formularz i jego elementy podrzędne powinny nie używać nazw pól lub nazw identyfikatorów które tworzą konflikt z właściwościami formularza,\n> takich jak `submit`, `length` lub `method`. Konflikty nazw mogą powodować mylące błędy.\n> Kompletna lista zasad oraz walidator znaczników dla tych problemów znajduje się na stronie [DOMLint](https://kangax.github.io/domlint). \n\nDodatkowe tagi HTML mogą zostać dodane do formularza używając czystego HTML'a lub używając metody z klasy pomocniczej - [[yii\\helpers\\Html|Html]],\ntak jak było to zrobione w przykładzie wyżej z [[yii\\helpers\\Html::submitButton()|submitButton()]].\n\n> Tip: Jeśli używasz Twitter Bootstrap CSS w Twojej aplikacji, możesz użyć [[yii\\bootstrap\\ActiveForm]] zamiast [[yii\\widgets\\ActiveForm]]. \n> Rozszerza on [[yii\\widgets\\ActiveForm|ActiveForm]] i podczas generowania pól formularza używa stylu specyficznego dla Bootstrap.\n\n\n> Tip: Jeśli chcesz oznaczyć wymagane pola gwiazdką, możesz uzyć poniższego kodu CSS:\n>\n> ```css\n> div.required label:after {\n>     content: \" *\";\n>     color: red;\n> }\n> ```\n\nTworzenie list <span id=\"creating-activeform-lists\"></span>\n--------------\n\nWyróżniamy trzy typy list:\n* Listy rozwijane \n* Listy opcji typu radio\n* Listy opcji typu checkbox\n\nAby stworzyć listę, musisz najpierw przygotować jej elementy. Można to zrobić ręcznie:\n\n```php\n$items = [\n    1 => 'item 1', \n    2 => 'item 2'\n]\n```\n\nlub też pobierając elementy z bazy danych:\n\n```php\n$items = Category::find()\n        ->select(['label'])\n        ->indexBy('id')\n        ->column();\n```\n\nElementy `$items` muszą być następnie przetworzone przez odpowiednie widżety list.\nWartość pola formularza (i aktualnie aktywny element) będzie automatycznie ustawiony przez aktualną wartość atrybutu `$model`. \n\n#### Tworzenie listy rozwijanej <span id=\"creating-activeform-dropdownlist\"></span>\n\nMożemy użyć metody klasy ActiveForm [[yii\\widgets\\ActiveForm::dropDownList()|dropDownList()]] do utworzenia rozwijanej listy:\n\n```php\n/** @var \\yii\\widgets\\ActiveForm $form */\n\necho $form->field($model, 'category')->dropdownList([\n        1 => 'item 1', \n        2 => 'item 2'\n    ],\n    ['prompt'=>'Wybierz kategorię']\n);\n```\n\n#### Tworzenie radio listy <span id=\"creating-activeform-radioList\"></span>\n\nDo stworzenia takiej listy możemy użyć metody ActiveField [[\\yii\\widgets\\ActiveField::radioList()]]:\n\n```php\n/** @var \\yii\\widgets\\ActiveForm $form */\n\necho $form->field($model, 'category')->radioList([\n    1 => 'radio 1', \n    2 => 'radio 2'\n]);\n```\n\n#### Tworzenie checkbox listy <span id=\"creating-activeform-checkboxList\"></span>\n\nDo stworzenia takiej listy możemy użyć metody ActiveField [[\\yii\\widgets\\ActiveField::checkboxList()]]:\n\n```php\n/** @var \\yii\\widgets\\ActiveForm $form */\n\necho $form->field($model, 'category')->checkboxList([\n    1 => 'checkbox 1', \n    2 => 'checkbox 2'\n]);\n```\n\nPraca z Pjaxem <span id=\"working-with-pjax\"></span>\n-----------------------\n\nWidżet [[yii\\widgets\\Pjax|Pjax]] pozwala na aktualizację określonej sekcji strony, \nzamiast przeładowywania jej całkowicie. Możesz użyć go do odświeżenia formularza \ni podmienić jego zawartość po wysłaniu danych.\n\nMożesz skonfigurować [[yii\\widgets\\Pjax::$formSelector|$formSelector]], aby wskazać, \nktóre formularze powinny wyzwalać użycie pjaxa. Jeśli nie zostanie to ustawione inaczej, \nwszystkie formularze z atrybutem `data-pjax` objęte widżetem Pjax będą wyzwalały jego użycie.\n\n```php\nuse yii\\widgets\\Pjax;\nuse yii\\widgets\\ActiveForm;\n\nPjax::begin([\n    // opcje Pjaxa\n]);\n    $form = ActiveForm::begin([\n        'options' => ['data' => ['pjax' => true]],\n        // więcej opcji ActiveForm\n    ]);\n\n        // zawartość ActiveForm\n\n    ActiveForm::end();\nPjax::end();\n```\n> Tip: Należy być ostrożnym z użyciem linków wewnątrz widżetu [[yii\\widgets\\Pjax|Pjax]], ponieważ\n> ich cel również zostanie wyrenderowany wewnątrz widżetu. Aby temu zapobiec, należy użyć atrybutu HTML `data-pjax=\"0\"`.\n\n#### Wartości w przyciskach submit i przesyłanie plików\n\nZnane są problemy z użyciem `jQuery.serializeArray()` podczas obsługi \n[[https://github.com/jquery/jquery/issues/2321|plików]] i \n[[https://github.com/jquery/jquery/issues/2321|wartości przycisku submit]], które nie \nbędą jednak rozwiązane i zamiast tego zostały porzucone na rzecz klasy `FormData` \nwprowadzonej w HTML5.\n\nOznacza to, że oficjalne wsparcie dla plików i wartości przycisku submit używanych w połączeniu \nz ajaxem lub widżetem [[yii\\widgets\\Pjax|Pjax]] zależy od \n[[https://developer.mozilla.org/en-US/docs/Web/API/FormData#browser_compatibility|wsparcia przeglądarki]]\ndla klasy `FormData`.\n\nDalsza lektura <span id=\"further-reading\"></span>\n---------------\n\nNastępna sekcja [Walidacja danych wejściowych](input-validation.md) dotyczy walidacji przesłanych przed formularz danych po stronie serwera, przy użyciu ajax oraz walidacji po stronie \nklienta.\n\nAby przeczytać o bardziej złożonych użyciach formularzy możesz zajrzeć do poniższych sekcji:\n\n- [Odczytywanie tablicowych danych wejściowych](input-tabular-input.md) - do pobierania danych dla wielu modeli tego samego rodzaju.\n- [Pobieranie danych dla wielu modeli](input-multiple-models.md) - do obsługi wielu różnych modeli w tym samym formularzu.\n- [Wysyłanie plików](input-file-upload.md) - jak używać formularzy do przesyłania plików.\n"
  },
  {
    "path": "docs/guide-pl/input-multiple-models.md",
    "content": "Pobieranie danych dla wielu modeli\n==================================\n\nKiedy mamy do czynienia ze skomplikowanym zestawem danych, jest możliwe, że trzeba będzie użyć wielu różnych modeli, aby pobrać te dane od użytkownika.\nDla przykładu - zakładając, że dane logowania użytkownika zapisane są w tabeli `user`, podczas gdy dane profilu użytkownika są przechowywane w tabeli `profile`,\nbędziesz chciał pobrać dane od użytkownika za pomocą modeli `User` oraz `Profile`. \nDzięki wsparciu modeli i formularzy przez Yii, możesz rozwiązać ten problem w sposób nie różniący się za bardzo od przetwarzania pojedynczego modelu.\n\nW poniższym przykładzie pokażemy jak utworzyć formularz, który pozwoli Ci na zbieranie danych dla obydwu modeli: `User` oraz `Profile`.\n\nNa początek, akcja w kontrolerze do zbierania danych użytkownika oraz danych profilowych może zostać napisana następująco:\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\base\\Model;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\nuse app\\models\\User;\nuse app\\models\\Profile;\n\nclass UserController extends Controller\n{\n    public function actionUpdate($id)\n    {\n        $user = User::findOne($id);\n        if (!$user) {\n            throw new NotFoundHttpException(\"The user was not found.\");\n        }\n        \n        $profile = Profile::findOne($user->profile_id);\n        \n        if (!$profile) {\n            throw new NotFoundHttpException(\"The user has no profile.\");\n        }\n        \n        $user->scenario = 'update';\n        $profile->scenario = 'update';\n        \n        if ($user->load(Yii::$app->request->post()) && $profile->load(Yii::$app->request->post())) {\n            $isValid = $user->validate();\n            $isValid = $profile->validate() && $isValid;\n            if ($isValid) {\n                $user->save(false);\n                $profile->save(false);\n                return $this->redirect(['user/view', 'id' => $id]);\n            }\n        }\n        \n        return $this->render('update', [\n            'user' => $user,\n            'profile' => $profile,\n        ]);\n    }\n}\n```\n\nW akcji `update`, najpierw ładujemy modele `$user` oraz `$profile`, które zostaną zaktualizowane danymi z bazy.\nNastępnie wywołujemy metodę [[yii\\base\\Model::load()|load()]], aby wypełnić te dwa modele danymi wprowadzonymi przez użytkownika.\nNa końcu modele zostają poddane walidacji i, jeśli wszystko jest w porządku, zapisane.\nW przeciwnym razie zostanie wyrenderowany widok `update`, który zawiera następujący kod:\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n$form = ActiveForm::begin([\n    'id' => 'user-update-form',\n    'options' => ['class' => 'form-horizontal'],\n]) ?>\n    <?= $form->field($user, 'username') ?>\n\n    ...other input fields...\n    \n    <?= $form->field($profile, 'website') ?>\n\n    <?= Html::submitButton('Update', ['class' => 'btn btn-primary']) ?>\n<?php ActiveForm::end() ?>\n```\n\nJak widzisz, w widoku `update` tworzymy pola tekstowe używając dwóch modeli: `$user` oraz `$profile`.\n"
  },
  {
    "path": "docs/guide-pl/input-tabular-input.md",
    "content": "Odczytywanie tablicowych danych wejściowych\n========================\n\nCzasami zachodzi potrzeba obsłużenia wielu modeli tego samego rodzaju w jednym formularzu. Dla przykładu - ustawienia, gdzie każde z nich jest przechowywane jako para klucz-wartość \ni każde z nich jest reprezentowane przez model `Setting` [active record](db-active-record.md). \nDla kontrastu obsługa wielu modeli różnych rodzajów pokazana jest w sekcji [Pobieranie danych dla wielu modeli](input-multiple-models.md).\n\n\nPoniższe przykłady pokazują jak zaimplementować tablicowe dane wejściowe w Yii.\n\nWystępują trzy różne sytuacje, które należy obsłużyć inaczej:\n- Aktualizacja określonej liczby rekordów z bazy danych\n- Dynamiczne tworzenie nowych rekordów\n- Aktualizacja, tworzenie oraz usuwanie rekordów na jednej stronie\n\n\nW porównaniu do formularza z pojedyńczym modelem, wytłumaczonym poprzednio, pracujemy teraz na tablicy modeli.\nTablica przekazywana jest do widoku, aby wyświetlić pola wejściowe dla każdego modelu w stylu tabeli, \nużyjemy do tego metod pomocniczych z [[yii\\base\\Model|Model]], które pozwalają na wczytywanie oraz walidację wielu modeli na raz:\n\n- [[yii\\base\\Model::loadMultiple()|loadMultiple()]] wczytuje dane z tablicy POST do tablicy modeli. \n- [[yii\\base\\Model::validateMultiple()|validateMultiple()]] waliduje tablicę modeli.\n\n### Aktualizacja określonej liczby rekordów\n\nZacznijmy od akcji kontrolera:\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\base\\Model;\nuse yii\\web\\Controller;\nuse app\\models\\Setting;\n\nclass SettingsController extends Controller\n{\n    // ...\n\n    public function actionUpdate()\n    {\n        $settings = Setting::find()->indexBy('id')->all();\n\n        if (Model::loadMultiple($settings, Yii::$app->request->post()) && Model::validateMultiple($settings)) {\n            foreach ($settings as $setting) {\n                $setting->save(false);\n            }\n            return $this->redirect('index');\n        }\n\n        return $this->render('update', ['settings' => $settings]);\n    }\n}\n```\n\nW powyższym kodzie używamy [[yii\\db\\ActiveQuery::indexBy()|indexBy()]] podczas pobierania danych z bazy danych aby zasilić tablicę modelami zaindeksowanymi przez główny klucz.\nBędzie to później użyte do zidentyfikowania pól formularza. [[yii\\base\\Model::loadMultiple()|loadMultiple()]] uzupełnia modele danymi formularza przesłanymi metodą POST \na następnie metoda [[yii\\base\\Model::validateMultiple()|validateMultiple()]] waliduje te modele. \nPo walidacji przekazujemy parametr `false` do metody [[yii\\db\\ActiveRecord::save()|save()]], aby nie uruchamiać walidacji ponownie.\n\nPrzejdziemy teraz do formularza w widoku `update`:\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n$form = ActiveForm::begin();\n\nforeach ($settings as $index => $setting) {\n    echo $form->field($setting, \"[$index]value\")->label($setting->name);\n}\n\nActiveForm::end();\n```\n\nDla każdego z ustawień renderujemy nazwę oraz pole wejściowe z wartością. Bardzo ważne jest dodanie odpowiedniego indeksu do nazwy pola, ponieważ dzięki temu \nmetoda [[yii\\base\\Model::loadMultiple()|loadMultiple()]] określa który model powinna uzupełnić przekazanymi wartościami.\n\n### Dynamiczne tworzenie nowych rekordów\n\nTworzenie nowych rekordów jest podobne do ich aktualizacji, poza częścią, w której instancjujemy modele:\n\n```php\npublic function actionCreate()\n{\n    $count = count(Yii::$app->request->post('Setting', []));\n    $settings = [new Setting()];\n    for($i = 1; $i < $count; $i++) {\n        $settings[] = new Setting();\n    }\n\n    // ...\n}\n```\n\nTworzymy tutaj początkową tablicę `$settings` zawierającą domyślnie jeden model, dlatego zawsze co najmniej jedno pole będzie widoczne w widoku.\nDodatkowo dodajemy więcej modeli dla każdej linii pól wejściowych jakie otrzymaliśmy.\n\nW widoku możemy użyć kodu JavaScript do dynamicznego dodawania nowych linii pól wejściowych.\n\n### Aktualizacja, tworzenie oraz usuwanie rekordów na jednej stronie\n\n> Note: Ta sekcja nie została jeszcze skończona.\n\nTBD\n"
  },
  {
    "path": "docs/guide-pl/input-validation.md",
    "content": "Walidacja danych wejściowych\n============================\n\nJedna z głównych zasad mówi, że nigdy nie należy ufać danym otrzymanym od użytkownika oraz że zawsze należy walidować je przed użyciem.\n\nRozważmy [model](structure-models.md) wypełniony danymi pobranymi od użytkownika. Możemy zweryfikować je poprzez wywołanie metody [[yii\\base\\Model::validate()|validate()]].\nMetoda zwróci wartość `boolean` wskazującą, czy walidacja się powiodła, czy też nie. Jeśli nie, można pobrać informacje o błędach za pomocą właściwości\n[[yii\\base\\Model::errors|errors]].\nDla przykładu,\n\n```php\n$model = new \\app\\models\\ContactForm();\n\n// uzupełniamy model danymi od użytkownika\n$model->load(\\Yii::$app->request->post());\n// ten zapis jest tożsamy z poniższą metodą\n// $model->attributes = \\Yii::$app->request->post('ContactForm');\n\nif ($model->validate()) {\n    // akcja w przypadku poprawnej walidacji\n} else {\n    // akcja w przypadku niepoprawnej walidacji. Zmienna $errors jest tablicą zawierającą wiadomości błędów\n    $errors = $model->errors;\n}\n```\n\n\n## Deklaracja zasad <span id=\"declaring-rules\"></span>\n\nAby metoda [[yii\\base\\Model::validate()|validate()]] naprawdę zadziałała, należy zdefiniować zasady walidacji dla atrybutów, które mają jej podlegać.\nPowinno zostać to zrobione przez nadpisanie metody [[yii\\base\\Model::rules()|rules()]]. Poniższy przykład pokazuje jak zostały zadeklarowane zasady walidacji dla modelu\n`ContactForm`:\n\n```php\npublic function rules()\n{\n    return [\n        // atrybuty name, email, subject oraz body są wymagane\n        [['name', 'email', 'subject', 'body'], 'required'],\n\n        // atrybut email powinien być poprawnym adresem email\n        ['email', 'email'],\n    ];\n}\n```\n\nMetoda [[yii\\base\\Model::rules()|rules()]] powinna zwracać tablicę zasad, gdzie każda zasada jest również tablicą o następującym formacie:\n\n```php\n[\n    // wymagane, określa atrybut który powinien zostać zwalidowany przez tę zasadę.\n    // Dla pojedyńczego atrybutu możemy użyć bezpośrednio jego nazwy, bez osadzania go w tablicy\n    ['attribute1', 'attribute2', ...],\n\n    // wymagane, określa rodzaj walidacji\n    // Może to być nazwa klasy, alias walidatora lub nazwa metody walidacji\n    'validator',\n\n    // opcjonalne, określa, w którym scenariuszu/scenariuszach ta zasada powinna zostać użyta\n    // w przypadku nie podania żadnego argumentu zasada zostanie zaaplikowana do wszystkich scenariuszy\n    // Możesz również skonfigurować opcję \"except\", jeśli chcesz użyć tej zasady dla wszystkich scenariuszy, z wyjątkiem wymienionych\n    'on' => ['scenario1', 'scenario2', ...],\n\n    // opcjonalne, określa dodatkową konfigurację dla obiektu walidatora\n    'property1' => 'value1', 'property2' => 'value2', ...\n]\n```\n\nDla każdej z zasad musisz określić co najmniej jeden atrybut, którego ma ona dotyczyć, oraz określić rodzaj zasady jako\njedną z następujących form:\n\n* alias walidatora podstawowego, np. `required`, `in`, `date` itd. Zajrzyj do sekcji [Podstawowe walidatory](tutorial-core-validators.md),\n  aby uzyskać pełną listę walidatorów podstawowych.\n* nazwa metody walidacji w klasie modelu lub funkcja anonimowa. Po więcej szczegółów zajrzyj do sekcji [Walidatory wbudowane](#inline-validators).\n* pełna nazwa klasy walidatora. Po więcej szczegółów zajrzyj do sekcji [Walidatory niezależne](#standalone-validators).\n\nZasada może zostać użyta do walidacji jednego lub wielu atrybutów, a atrybut może być walidowany przez jedną lub wiele zasad.\nZasada może zostać użyta dla konkretnych [scenariuszy](structure-models.md#scenarios) przez dodanie opcji `on`.\nJeśli nie dodasz opcji `on` oznacza to, że zasada zostanie użyta w każdym scenariuszu.\n\nWywołanie metody [[yii\\base\\Model::validate()|validate()]] powoduje podjęcie następujących kroków w celu wykonania walidacji:\n\n1. Określenie, które atrybuty powinny zostać zweryfikowane poprzez pobranie ich listy z metody [[yii\\base\\Model::scenarios()|scenarios()]], używając aktualnego\n   [[yii\\base\\Model::scenario|scenariusza]]. Wybrane atrybuty nazywane są *atrybutami aktywnymi*.\n2. Określenie, które zasady walidacji powinny zostać użyte przez pobranie ich listy z metody [[yii\\base\\Model::rules()|rules()]], używając aktualnego\n   [[yii\\base\\Model::scenario|scenariusza]]. Wybrane zasady nazywane są *zasadami aktywnymi*.\n3. Użycie każdej aktywnej zasady do walidacji każdego aktywnego atrybutu, który jest powiązany z konkretną zasadą. Zasady walidacji są wykonywane w kolejności,\n   w jakiej zostały zapisane.\n\nOdnosząc się do powyższych kroków, atrybut zostanie zwalidowany wtedy i tylko wtedy, gdy jest on aktywnym atrybutem zadeklarowanym w\n[[yii\\base\\Model::scenarios()|scenarios()]] oraz jest powiązany z jedną lub wieloma aktywnymi zasadami zadeklarowanymi w [[yii\\base\\Model::rules()|rules()]].\n\n> Note: Czasem użyteczne jest nadanie nazwy zasadzie np.\n>\n> ```php\n> public function rules()\n> {\n>     return [\n>         // ...\n>         'password' => [['password'], 'string', 'max' => 60],\n>     ];\n> }\n> ```\n>\n> W modelu potomnym można to wykorzystać:\n>\n> ```php\n> public function rules()\n> {\n>     $rules = parent::rules();\n>     unset($rules['password']);\n>     return $rules;\n> }\n\n\n### Dostosowywanie wiadomości błedów <span id=\"customizing-error-messages\"></span>\n\nWiększość walidatorów posiada domyślne wiadomości błędów, które zostają dodane do poddanego walidacji modelu, kiedy któryś z atrybutów nie przejdzie pomyślnie walidacji.\nDla przykładu, walidator [[yii\\validators\\RequiredValidator|required]] dodaje komunikat \"Username cannot be blank.\", kiedy atrybut `username` nie przejdzie walidacji tej zasady.\n\nMożesz dostosować wiadomość błędu zasady przez określenie właściwości `message` podczas jej deklaracji.\nDla przykładu,\n\n```php\npublic function rules()\n{\n    return [\n        ['username', 'required', 'message' => 'Proszę wybrać login.'],\n    ];\n}\n```\n\nNiektóre walidatory mogą wspierać dodatkowe wiadomości błedów, aby bardziej precyzyjnie określić problemy powstałe przy walidacji.\nDla przykładu, walidator [[yii\\validators\\NumberValidator|number]] dodaje [[yii\\validators\\NumberValidator::tooBig|tooBig]] oraz\n[[yii\\validators\\NumberValidator::tooSmall|tooSmall]] do opisania sytuacji, kiedy poddawana walidacji liczba jest za duża lub za mała.\nMożesz skonfigurować te wiadomości tak, jak pozostałe właściwości walidatorów podczas deklaracji zasady.\n\n\n### Zdarzenia walidacji <span id=\"validation-events\"></span>\n\nPodczas wywołania metody [[yii\\base\\Model::validate()|validate()]] zostaną wywołane dwie metody, które możesz nadpisać, aby dostosować proces walidacji:\n\n* [[yii\\base\\Model::beforeValidate()|beforeValidate()]]: domyślna implementacja wywoła zdarzenie [[yii\\base\\Model::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]].\n  Możesz nadpisać tę metodę lub odnieść się do zdarzenia, aby wykonać dodatkowe operacje przed walidacją.\n  Metoda powinna zwracać wartość `boolean` wskazującą, czy walidacja powinna zostać przeprowadzona, czy też nie.\n* [[yii\\base\\Model::afterValidate()|afterValidate()]]: domyślna implementacja wywoła zdarzenie [[yii\\base\\Model::EVENT_AFTER_VALIDATE|EVENT_AFTER_VALIDATE]].\n  Możesz nadpisać tę metodę lub odnieść się do zdarzenia, aby wykonać dodatkowe operacje po zakończonej walidacji.\n\n\n### Walidacja warunkowa <span id=\"conditional-validation\"></span>\n\nAby zwalidować atrybuty tylko wtedy, gdy zostaną spełnione pewne założenia, np. walidacja jednego atrybutu zależy od wartości drugiego atrybutu, możesz użyć właściwości\n [[yii\\validators\\Validator::when|when]], aby zdefiniować taki warunek. Dla przykładu,\n\n```php\n[\n    ['state', 'required', 'when' => function($model) {\n        return $model->country == 'USA';\n    }],\n]\n```\n\nWłaściwość [[yii\\validators\\Validator::when|when]] pobiera możliwą do wywołania funkcję PHP z następującą definicją:\n\n```php\n/**\n * @param Model $model model, który podlega walidacji\n * @param string $attribute atrybut, który podlega walidacji\n * @return bool wartość zwrotna; czy reguła powinna zostać zastosowana\n */\nfunction ($model, $attribute)\n```\n\nJeśli potrzebujesz również wsparcia walidacji warunkowej po stronie użytkownika, powinieneś skonfigurować właściwość [[yii\\validators\\Validator::whenClient|whenClient]],\nktóra przyjmuje wartość `string` reprezentującą funkcję JavaScript, zwracającą wartość `boolean`, która będzie określała, czy zasada powinna zostać zastosowana, czy nie.\nDla przykładu,\n\n```php\n[\n    ['state', 'required', 'when' => function ($model) {\n        return $model->country == 'USA';\n    }, 'whenClient' => \"function (attribute, value) {\n        return $('#country').val() == 'USA';\n    }\"],\n]\n```\n\n\n### Filtrowanie danych <span id=\"data-filtering\"></span>\n\nDane od użytkownika często muszą zostać przefiltrowane. Dla przykładu, możesz chcieć wyciąć znaki spacji na początku i na końcu pola `username`.\nAby osiągnąć ten cel, możesz również użyć zasad walidacji.\n\nPoniższy przykład pokazuje, jak wyciąć znaki spacji z pola oraz zmienić puste pole na wartość `null` przy użyciu podstawowych walidatorów\n[trim](tutorial-core-validators.md#trim) oraz [default](tutorial-core-validators.md#default):\n\n```php\n[\n    [['username', 'email'], 'trim'],\n    [['username', 'email'], 'default'],\n]\n```\n\nMożesz użyć również bardziej ogólnego walidatora [filter](tutorial-core-validators.md#filter), aby przeprowadzić złożone filtrowanie.\n\nJak pewnie zauważyłeś, te zasady walidacji tak naprawdę nie walidują danych. Zamiast tego przetwarzają wartości, a następnie przypisują je do atrybutów,\nktóre zostały poddane walidacji.\n\n\n### Obsługa pustych danych wejściowych <span id=\"handling-empty-inputs\"></span>\n\nKiedy dane wejściowe są wysłane przez formularz HTML, często zachodzi potrzeba przypisania im domyślnych wartości, jeśli są puste.\nMożesz to osiągnąć przez użycie walidatora [default](tutorial-core-validators.md#default). Dla przykładu,\n\n```php\n[\n    // ustawia atrybuty \"username\" oraz \"email\" jako `null` jeśli są puste\n    [['username', 'email'], 'default'],\n\n    // ustawia atrybut \"level\" równy \"1\", jeśli jest pusty\n    ['level', 'default', 'value' => 1],\n]\n```\n\nDomyślnie pole uważane jest za puste, jeśli jego wartość to pusty łańcuch znaków, pusta tablica lub `null`.\nMożesz dostosować domyślną logikę wykrywania pustych pól przez skonfigurowanie parametru [[yii\\validators\\Validator::isEmpty|isEmpty]], przekazując mu funkcję PHP.\nDla przykładu,\n\n```php\n[\n    ['agree', 'required', 'isEmpty' => function ($value) {\n        return empty($value);\n    }],\n]\n```\n\n> Note: Większość walidatorów nie obsługuje pustych pól, jeśli ich właściwość [[yii\\validators\\Validator::skipOnEmpty|skipOnEmpty]] przyjmuje domyślnie wartość `true`.\n> Zostaną one po prostu pominięte podczas walidacji, jeśli ich powiązany atrybut otrzyma wartość uznawaną za pustą.\n> Wśród [podstawowych walidatorów](tutorial-core-validators.md), tylko walidatory `captcha`, `default`, `filter`, `required` oraz `trim` obsługują puste pola.\n\n## Walidacja \"Ad Hoc\" <span id=\"ad-hoc-validation\"></span>\n\nCzasami potrzebna będzie walidacja *ad hoc* dla wartości które nie są powiązane z żadnym modelem.\n\nJeśli potrzebujesz wykonać tylko jeden typ walidacji (np. walidację adresu email), możesz wywołać metodę\n[[yii\\validators\\Validator::validate()|validate()]] wybranego walidatora, tak jak poniżej:\n\n```php\n$email = 'test@example.com';\n$validator = new yii\\validators\\EmailValidator();\n\nif ($validator->validate($email, $error)) {\n    echo 'Email is valid.';\n} else {\n    echo $error;\n}\n```\n\n> Note: Nie każdy walidator wspiera tego typu walidację. Dla przykładu, podstawowy walidator [unique](tutorial-core-validators.md#unique)\n> został zaprojektowany do pracy wyłącznie z modelami.\n\nJeśli potrzebujesz przeprowadzić wielokrotne walidacje, możesz użyć modelu [[yii\\base\\DynamicModel|DynamicModel]],\nktóry wspiera deklarację atrybutów oraz zasad walidacji \"w locie\".\nDla przykładu,\n\n```php\npublic function actionSearch($name, $email)\n{\n    $model = DynamicModel::validateData(compact('name', 'email'), [\n        [['name', 'email'], 'string', 'max' => 128],\n        ['email', 'email'],\n    ]);\n\n    if ($model->hasErrors()) {\n        // validation fails\n    } else {\n        // validation succeeds\n    }\n}\n```\n\nMetoda [[yii\\base\\DynamicModel::validateData()|validateData()]] tworzy instancję `DynamicModel`, definiuje atrybuty używając przekazanych danych\n(`name` oraz `email` w tym przykładzie), a następnie wywołuje metodę [[yii\\base\\Model::validate()|validate()]] z podanymi zasadami walidacji.\n\nAlternatywnie, możesz użyć bardziej \"klasycznego\" zapisu to przeprowadzenia tego typu walidacji:\n\n```php\npublic function actionSearch($name, $email)\n{\n    $model = new DynamicModel(compact('name', 'email'));\n    $model->addRule(['name', 'email'], 'string', ['max' => 128])\n        ->addRule('email', 'email')\n        ->validate();\n\n    if ($model->hasErrors()) {\n        // validation fails\n    } else {\n        // validation succeeds\n    }\n}\n```\n\nPo walidacji możesz sprawdzić, czy przebiegła ona poprawnie, poprzez wywołanie metody [[yii\\base\\DynamicModel::hasErrors()|hasErrors()]],\na następnie pobrać błędy walidacji z właściwości [[yii\\base\\DynamicModel::errors|errors]], tak jak w przypadku zwykłego modelu.\nMożesz również uzyskać dostęp do dynamicznych atrybutów tej instancji, np. `$model->name` i `$model->email`.\n\n\n## Tworzenie walidatorów <span id=\"creating-validators\"></span>\n\nOprócz używania [podstawowych walidatorów](tutorial-core-validators.md) dołączonych do wydania Yii, możesz dodatkowo utworzyć własne; wbudowane lub niezależne.\n\n### Walidatory wbudowane <span id=\"inline-validators\"></span>\n\nWbudowany walidator jest zdefiniowaną w modelu metodą lub funkcją anonimową. Jej definicja jest następująca:\n\n```php\n/**\n * @param string $attribute atrybut podlegający walidacji\n * @param mixed $params wartość parametru podanego w zasadzie walidacji\n * @param \\yii\\validators\\InlineValidator $validator powiązana instancja InlineValidator\n * Ten parametr jest dostępny od wersji 2.0.11.\n * @param mixed $current aktualnie walidowana wartość atrybutu.\n * Ten parametr jest dostępny od wersji 2.0.36.\n */\nfunction ($attribute, $params, $validator, $current)\n```\n\nJeśli atrybut nie przejdzie walidacji, metoda/funkcja powinna wywołać metodę [[yii\\base\\Model::addError()|addError()]] do zapisania wiadomości o błędzie w modelu,\nktóra może zostać później pobrana i zaprezentowana użytkownikowi.\n\nPoniżej znajduje się kilka przykładów:\n\n```php\nuse yii\\base\\Model;\n\nclass MyForm extends Model\n{\n    public $country;\n    public $token;\n\n    public function rules()\n    {\n        return [\n            // Wbudowany walidator zdefiniowany jako metoda validateCountry() w modelu\n            ['country', 'validateCountry'],\n\n            // Wbudowany walidator zdefiniowany jako funkcja anonimowa\n            ['token', function ($attribute, $params, $validator) {\n                if (!ctype_alnum($this->$attribute)) {\n                    $this->addError($attribute, 'Token musi zawierać litery lub cyfry.');\n                }\n            }],\n        ];\n    }\n\n    public function validateCountry($attribute, $params, $validator)\n    {\n        if (!in_array($this->$attribute, ['USA', 'Web'])) {\n            $this->addError($attribute, 'Wybrany kraj musi być jednym z: \"USA\", \"Web\".');\n        }\n    }\n}\n```\n\n> Note: Począwszy od wersji 2.0.11 możesz użyć [[yii\\validators\\InlineValidator::addError()]], aby dodać błędy bezpośrednio. W tym sposobie treść błędu \n> może być sformatowana bezpośrednio za pomocą [[yii\\i18n\\I18N::format()]]. Użyj `{attribute}` i `{value}` w treści błędu, aby odwołać się odpowiednio \n> do etykiety atrybutu (bez konieczności pobierania jej ręcznie) i wartości atrybutu:\n>\n> ```php\n> $validator->addError($this, $attribute, 'Wartość \"{value}\" nie jest poprawna dla {attribute}.');\n> ```\n\n> Note: Domyślnie wbudowane walidatory nie zostaną zastosowane, jeśli ich powiązane atrybuty otrzymają puste wartości lub wcześniej nie przeszły którejś z zasad walidacji.\n> Jeśli chcesz się upewnić, że zasada zawsze zostanie zastosowana, możesz skonfigurować właściwość [[yii\\validators\\Validator::skipOnEmpty|skipOnEmpty]] i/lub\n> [[yii\\validators\\Validator::skipOnError|skipOnError]], przypisując jej wartość `false` w deklaracji zasady walidacji. Dla przykładu:\n>\n> ```php\n> [\n>     ['country', 'validateCountry', 'skipOnEmpty' => false, 'skipOnError' => false],\n> ]\n> ```\n\n\n### Walidatory niezależne <span id=\"standalone-validators\"></span>\n\nWalidator niezależy jest klasą rozszerzającą [[yii\\validators\\Validator|Validator]] lub klasy po nim dziedziczące.\nMożesz zaimplementować jego logikę walidacji poprzez nadpisanie metody [[yii\\validators\\Validator::validateAttribute()|validateAttribute()]].\nJeśli atrybut nie przejdzie walidacji, wywołaj metodę [[yii\\base\\Model::addError()|addError()]] do zapisania wiadomości błędu w modelu, tak jak w\n[walidatorach wbudowanych](#inline-validators).\n\n\nDla przykładu, poprzedni wbudowany walidator mógłby zostać przeniesiony do nowej klasy `components/validators/CountryValidator`.\n\n```php\nnamespace app\\components;\n\nuse yii\\validators\\Validator;\n\nclass CountryValidator extends Validator\n{\n    public function validateAttribute($model, $attribute)\n    {\n        if (!in_array($model->$attribute, ['USA', 'Web'])) {\n            $this->addError($model, $attribute, 'Wybrany kraj musi być jednym z: \"USA\", \"Web\".');\n        }\n    }\n}\n```\n\nJeśli chcesz, aby walidator wspierał walidację wartości bez modelu, powinieneś nadpisać metodę [[yii\\validators\\Validator::validate()|validate()]].\nMożesz nadpisać także [[yii\\validators\\Validator::validateValue()|validateValue()]] zamiast `validateAttribute()` oraz `validate()`,\nponieważ domyślnie te dwie metody są implementowane użyciem metody `validateValue()`.\n\nPoniżej znajduje się przykład użycia powyższej klasy walidatora w modelu.\n\n```php\nnamespace app\\models;\n\nuse Yii;\nuse yii\\base\\Model;\nuse app\\components\\validators\\CountryValidator;\n\nclass EntryForm extends Model\n{\n    public $name;\n    public $email;\n    public $country;\n\n    public function rules()\n    {\n        return [\n            [['name', 'email'], 'required'],\n            ['country', CountryValidator::class],\n            ['email', 'email'],\n        ];\n    }\n}\n```\n\n## Walidacja wielu atrybutów na raz <span id=\"multiple-attributes-validation\"></span>\n\nZdarza się, że walidatory sprawdzają wiele atrybutów jednocześnie. Rozważmy następujący formularz:\n\n```php\nclass MigrationForm extends \\yii\\base\\Model\n{\n    /**\n     * Kwota minimalnych funduszy dla jednej dorosłej osoby\n     */\n    const MIN_ADULT_FUNDS = 3000;\n    /**\n     * Kwota minimalnych funduszy dla jednego dziecka\n     */\n    const MIN_CHILD_FUNDS = 1500;\n\n    public $personalSalary;\n    public $spouseSalary;\n    public $childrenCount;\n    public $description;\n\n    public function rules()\n    {\n        return [\n            [['personalSalary', 'description'], 'required'],\n            [['personalSalary', 'spouseSalary'], 'integer', 'min' => self::MIN_ADULT_FUNDS],\n            ['childrenCount', 'integer', 'min' => 0, 'max' => 5],\n            [['spouseSalary', 'childrenCount'], 'default', 'value' => 0],\n            ['description', 'string'],\n        ];\n    }\n}\n```\n\n### Tworzenie walidatora <span id=\"multiple-attributes-validator\"></span>\n\nPowiedzmy, że chcemy sprawdzić, czy dochód rodziny jest wystarczający do utrzymania dzieci. W tym celu możemy utworzyć wbudowany walidator\n`validateChildrenFunds`, który będzie uruchamiany tylko jeśli `childrenCount` będzie większe niż 0.\n\nZwróć uwagę na to, że nie możemy użyć wszystkich walidowanych atrybutów (`['personalSalary', 'spouseSalary', 'childrenCount']`) przy dołączaniu walidatora.\nWynika to z tego, że ten sam walidator będzie uruchomiony dla każdego z atrybutów oddzielnie (łącznie 3 razy), a musimy użyć go tylko raz dla całego zestawu atrybutów.\n\nMożesz użyć dowolnego z tych atrybutów zamiast podanego poniżej (lub też tego, który uważasz za najbardziej tu odpowiedni):\n\n```php\n['childrenCount', 'validateChildrenFunds', 'when' => function ($model) {\n    return $model->childrenCount > 0;\n}],\n```\n\nImplementacja `validateChildrenFunds` może wyglądać następująco:\n\n```php\npublic function validateChildrenFunds($attribute, $params)\n{\n    $totalSalary = $this->personalSalary + $this->spouseSalary;\n    // Podwój minimalny fundusz dorosłych, jeśli ustalono zarobki współmałżonka\n    $minAdultFunds = $this->spouseSalary ? self::MIN_ADULT_FUNDS * 2 : self::MIN_ADULT_FUNDS;\n    $childFunds = $totalSalary - $minAdultFunds;\n    if ($childFunds / $this->childrenCount < self::MIN_CHILD_FUNDS) {\n        $this->addError('childrenCount', 'Twoje zarobki nie są wystarczające, aby utrzymać dzieci.');\n    }\n}\n```\n\nMożesz zignorować parametr `$attribute`, ponieważ walidacja nie jest powiązana bezpośrednio tylko z jednym atrybutem.\n\n\n### Dodawanie informacji o błędach <span id=\"multiple-attributes-errors\"></span>\n\nDodawanie błędów walidacji w przypadku wielu atrybutów może różnić się w zależności od ustalonej metodyki pracy z formularzami:\n\n- Można wybrać najbardziej w naszej opinii pole i dodać błąd do jego atrybutu:\n\n```php\n$this->addError('childrenCount', 'Twoje zarobki nie są wystarczające dla potrzeb dzieci.');\n```\n\n- Można wybrać wiele ważnych odpowiednich atrybutów lub też wszystkie i dodać ten sam błąd do każdego z nich. Możemy przechować \ntreść w oddzielnej zmiennej przed przekazaniem jej do `addError`, aby nie powtarzać się w kodzie (zasada DRY - Don't Repeat Yourself).\n\n```php\n$message = 'Twoje zarobki nie są wystarczające dla potrzeb dzieci.';\n$this->addError('personalSalary', $message);\n$this->addError('wifeSalary', $message);\n$this->addError('childrenCount', $message);\n```\n\nLub też użyć pętli:\n\n```php\n$attributes = ['personalSalary, 'wifeSalary', 'childrenCount'];\nforeach ($attributes as $attribute) {\n    $this->addError($attribute, 'Twoje zarobki nie są wystarczające dla potrzeb dzieci.');\n}\n```\n\n- Można też dodać ogólny błąd (niepowiązany z żadnym szczególnym atrybutem). Do tego celu możemy wykorzystać nazwę nieistniejącego atrybutu, \nna przykład `*`, ponieważ to, czy atrybut istnieje, nie jest sprawdzane w tym kroku.\n\n```php\n$this->addError('*', 'Twoje zarobki nie są wystarczające dla potrzeb dzieci.');\n```\n\nW rezultacie takiej operacji nie zobaczymy błędu zaraz obok pól formularza. Aby go wyświetlić, możemy dodać do widoku podsumowanie błędów formularza:\n\n```php\n<?= $form->errorSummary($model) ?>\n```\n\n> Note: Tworzenie walidatora operującego na wielu atrybutach jednocześnie jest dobrze opisane w [książce kucharskiej społeczności Yii](https://github.com/samdark/yii2-cookbook/blob/master/book/forms-validator-multiple-attributes.md).\n\n\n## Walidacja po stronie klienta <span id=\"client-side-validation\"></span>\n\nWalidacja po stronie klienta, bazująca na kodzie JavaScript jest wskazana, kiedy użytkownicy dostarczają dane przez formularz HTML,\nponieważ pozwala na szybszą walidację błędów, a tym samym zapewnia lepszą ich obsługę dla użytkownika. Możesz użyć lub zaimplementować walidator,\nktóry wspiera walidację po stronie klienta jako *dodatek* do walidacji po stronie serwera.\n\n> Info: Walidacja po stronie klienta nie jest wymagana. Głównym jej celem jest poprawa jakości korzystania z formularzy dla użytkowników.\n> Podobnie jak w przypadku danych wejściowych pochodzących od użytkowników, nigdy nie powinieneś ufać walidacji przeprowadanej po stronie klienta.\n> Z tego powodu należy zawsze przeprowadzać główną walidację po stronie serwera wywołując metodę [[yii\\base\\Model::validate()|validate()]],\n> tak jak zostało to opisane w poprzednich sekcjach.\n\n### Używanie walidacji po stronie klienta <span id=\"using-client-side-validation\"></span>\n\nWiele [podstawowych walidatorów](tutorial-core-validators.md) domyślnie wspiera walidację po stronie klienta. Wszystko, co musisz zrobić, to użyć widżetu\n[[yii\\widgets\\ActiveForm|ActiveForm]] do zbudowania formularza HTML. Dla przykładu, model `LoginForm` poniżej deklaruje dwie zasady: jedną, używającą podstawowego walidatora\n[required](tutorial-core-validators.md#required), który wspiera walidację po stronie klienta i serwera, oraz drugą, w której użyto walidatora wbudowanego `validatePassword`,\nktóry wspiera tylko walidację po stronie serwera.\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\nuse app\\models\\User;\n\nclass LoginForm extends Model\n{\n    public $username;\n    public $password;\n\n    public function rules()\n    {\n        return [\n            // atrybuty username oraz password są wymagane\n            [['username', 'password'], 'required'],\n\n            // atrybut password jest walidowany przez validatePassword()\n            ['password', 'validatePassword'],\n        ];\n    }\n\n    public function validatePassword()\n    {\n        $user = User::findByUsername($this->username);\n\n        if (!$user || !$user->validatePassword($this->password)) {\n            $this->addError('password', 'Nieprawidłowa nazwa użytkownika lub hasło.');\n        }\n    }\n}\n```\n\nFormularz HTML zbudowany przez następujący kod zawiera dwa pola: `username` oraz `password`.\nJeśli wyślesz formularz bez wpisywania jakichkolwiek danych, otrzymasz komunikaty błędów o ich braku, bez konieczności przeprowadzania komunikacji z serwerem.\n\n```php\n<?php $form = yii\\widgets\\ActiveForm::begin(); ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n    <?= Html::submitButton('Login') ?>\n<?php yii\\widgets\\ActiveForm::end(); ?>\n```\n\n\"Za kulisami\", widżet [[yii\\widgets\\ActiveForm|ActiveForm]] odczyta wszystkie zasady walidacji zadeklarowane w modelu i wygeneruje odpowiedni kod JavaScript\ndla walidatorów wspierających walidację po stronie klienta. Kiedy użytkownik zmieni wartość w polu lub spróbuje wysłać formularz, zostanie wywołana walidacja po stronie klienta.\n\nJeśli chcesz wyłączyć całkowicie walidację po stronie klienta, możesz ustawić właściwość [[yii\\widgets\\ActiveForm::enableClientValidation|enableClientValidation]] na `false`.\nMożesz również wyłączyć ten rodzaj walidacji dla konkretnego pola, przez ustawienie jego właściwości\n[[yii\\widgets\\ActiveField::enableClientValidation|enableClientValidation]] na `false`. Jeśli właściwość `enableClientValidation` zostanie skonfigurowana na poziomie pola\nformularza i w samym formularzu jednocześnie, pierwszeństwo będzie miała opcja określona w formularzu.\n\n> Info: Od wersji 2.0.11 wszystkie walidatory rozszerzające klasę [[yii\\validators\\Validator]] używają opcji klienta przekazywanych \n> z oddzielnej metody - [[yii\\validators\\Validator::getClientOptions()]]. Możesz jej użyć:\n>\n> - jeśli chcesz zaimplementować swoją własną walidację po stronie klienta, ale pozostawić synchronizację z opcjami walidatora po stronie serwera;\n> - do rozszerzenia lub zmodyfikowania dla uzyskania specjalnych korzyści:\n>\n> ```php\n> public function getClientOptions($model, $attribute)\n> {\n>     $options = parent::getClientOptions($model, $attribute);\n>     // Zmodyfikuj $options w tym miejscu\n>\n>     return $options;\n> }\n> ```\n\n\n### Implementacja walidacji po stronie klienta <span id=\"implementing-client-side-validation\"></span>\n\nAby utworzyć walidator wspierający walidację po stronie klienta, powinieneś zaimplementować metodę\n[[yii\\validators\\Validator::clientValidateAttribute()|clientValidateAttribute()]], która zwraca kod JavaScript, odpowiedzialny za przeprowadzenie walidacji.\nW kodzie JavaScript możesz użyć następujących predefiniowanych zmiennych:\n\n- `attribute`: nazwa atrybutu podlegającego walidacji.\n- `value`: wartość atrybutu podlegająca walidacji.\n- `messages`: tablica używana do przechowywania wiadomości błędów dla danego atrybutu.\n- `deferred`: tablica, do której można dodać zakolejkowane obiekty (wyjaśnione w późniejszej podsekcji).\n\nW poniższym przykładzie, tworzymy walidator `StatusValidator`, który sprawdza, czy wartość danego atrybutu jest wartością znajdującą się na liście statusów w bazie danych.\nWalidator wspiera obydwa typy walidacji; po stronie klienta oraz serwerową.\n\n```php\nnamespace app\\components;\n\nuse yii\\validators\\Validator;\nuse app\\models\\Status;\n\nclass StatusValidator extends Validator\n{\n    public function init()\n    {\n        parent::init();\n        $this->message = 'Niepoprawna wartość pola status.';\n    }\n\n    public function validateAttribute($model, $attribute)\n    {\n        $value = $model->$attribute;\n        if (!Status::find()->where(['id' => $value])->exists()) {\n            $model->addError($attribute, $this->message);\n        }\n    }\n\n    public function clientValidateAttribute($model, $attribute, $view)\n    {\n        $statuses = json_encode(Status::find()->select('id')->asArray()->column());\n        $message = json_encode($this->message, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);\n        return <<<JS\nif ($.inArray(value, $statuses) === -1) {\n    messages.push($message);\n}\nJS;\n    }\n}\n```\n\n> Tip: Powyższy kod został podany głównie do zademonstrowania, jak wspierać walidację po stronie klienta.\n> W praktyce można użyć podstawowego walidatora [in](tutorial-core-validators.md#in), aby osiągnąć ten sam cel.\n> Możesz napisać taką zasadę walidacji następująco:\n>\n> ```php\n> [\n>     ['status', 'in', 'range' => Status::find()->select('id')->asArray()->column()],\n> ]\n> ```\n\n> Tip: Jeśli musisz dodać ręcznie walidację po stronie klienta np. podczas dynamicznego dodawania pól formularza lub przeprowadzania specjalnej logiki w obrębie interfejsu\n> użytkownika, zapoznaj się z rozdziałem [Praca z ActiveForm za pomocą JavaScript](https://github.com/samdark/yii2-cookbook/blob/master/book/forms-activeform-js.md)\n> w Yii 2.0 Cookbook.\n\n### Kolejkowa walidacja <span id=\"deferred-validation\"></span>\n\nJeśli potrzebujesz przeprowadzić asynchroniczną walidację po stronie klienta, możesz utworzyć [obiekt kolejkujący](https://api.jquery.com/category/deferred-object/).\nDla przykładu, aby przeprowadzić niestandardową walidację AJAX, możesz użyć następującego kodu:\n\n```php\npublic function clientValidateAttribute($model, $attribute, $view)\n{\n    return <<<JS\n        deferred.push($.get(\"/check\", {value: value}).done(function(data) {\n            if ('' !== data) {\n                messages.push(data);\n            }\n        }));\nJS;\n}\n```\n\nW powyższym kodzie, zmienna `deferred` jest dostarczoną przez Yii tablicą zakolejkowanych obiektów.\nMetoda jQuery `$.get()` tworzy obiekt kolejkowy, który jest dodawany do tablicy `deferred`.\n\nMożesz także utworzyć osobny obiekt kolejkowania i wywołać jego metodę `resolve()` po otrzymaniu asynchronicznej informacji zwrotnej.\nPoniższy przykład pokazuje, jak zwalidować wymiary przesłanego obrazka po stronie klienta.\n\n```php\npublic function clientValidateAttribute($model, $attribute, $view)\n{\n    return <<<JS\n        var def = $.Deferred();\n        var img = new Image();\n        img.onload = function() {\n            if (this.width > 150) {\n                messages.push('Image too wide!!');\n            }\n            def.resolve();\n        }\n        var reader = new FileReader();\n        reader.onloadend = function() {\n            img.src = reader.result;\n        }\n        reader.readAsDataURL(file);\n\n        deferred.push(def);\nJS;\n}\n```\n\n> Note: Metoda `resolve()` musi być wywołana po walidacji atrybutu. W przeciwnym razie główna walidacja formularza nie zostanie ukończona.\n\nDla uproszczenia, tablica `deferred` jest wyposażona w skrótową metodę `add()`, która automatycznie tworzy obiekt kolejkowy i dodaje go do tej tablicy.\nUżywając tej metody, możesz uprościć powyższy przykład:\n\n```php\npublic function clientValidateAttribute($model, $attribute, $view)\n{\n    return <<<JS\n        deferred.add(function(def) {\n            var img = new Image();\n            img.onload = function() {\n                if (this.width > 150) {\n                    messages.push('Image too wide!!');\n                }\n                def.resolve();\n            }\n            var reader = new FileReader();\n            reader.onloadend = function() {\n                img.src = reader.result;\n            }\n            reader.readAsDataURL(file);\n        });\nJS;\n}\n```\n\n\n### Walidacja przy użyciu AJAX <span id=\"ajax-validation\"></span>\n\nNiektóre walidacje mogą zostać wykonane tylko po stronie serwera, ponieważ tylko serwer posiada niezbędne informacje do ich przeprowadzenia.\nDla przykładu, aby sprawdzić, czy login został już zajęty, musimy sprawdzić tabelę użytkowników w bazie danych.\nW tym właśnie przypadku możesz użyć walidacji AJAX. Wywoła ona żądanie AJAX w tle, aby spradzić to pole.\n\nAby uaktywnić walidację AJAX dla pojedyńczego pola formularza, ustaw właściwość [[yii\\widgets\\ActiveField::enableAjaxValidation|enableAjaxValidation]] na `true`\noraz zdefiniuj unikalne `id` formularza:\n\n```php\nuse yii\\widgets\\ActiveForm;\n\n$form = ActiveForm::begin([\n    'id' => 'registration-form',\n]);\n\necho $form->field($model, 'username', ['enableAjaxValidation' => true]);\n\n// ...\n\nActiveForm::end();\n```\n\nAby uaktywnić walidację AJAX dla całego formularza, ustaw właściwość [[yii\\widgets\\ActiveForm::enableAjaxValidation|enableAjaxValidation]] na `true` na poziomie formularza:\n\n```php\n$form = ActiveForm::begin([\n    'id' => 'contact-form',\n    'enableAjaxValidation' => true,\n]);\n```\n\n> Note: Jeśli właściwość [[yii\\widgets\\ActiveForm::enableAjaxValidation|enableAjaxValidation]] zostanie skonfigurowana na poziomie pola formularza i jednocześnie w samym formularzu,\n> pierwszeństwo będzie miała opcja określona w formularzu.\n\n\nMusisz również przygotować serwer na obsłużenie AJAXowego zapytanie o walidację. Możesz to osiągnąć przez następujący fragment kodu w akcji kontrolera:\n\n```php\nif (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {\n    Yii::$app->response->format = Response::FORMAT_JSON;\n    return ActiveForm::validate($model);\n}\n```\n\nPowyższy kod sprawdzi, czy zapytanie zostało wysłane przy użyciu AJAXa. Jeśli tak, w odpowiedzi zwróci wynik walidacji w formacie JSON.\n\n> Info: Możesz również użyć [walidacji kolejkowej](#deferred-validation) do wykonania walidacji AJAX,\n> jednakże walidacja AJAXowa opisana w tej sekcji jest bardziej systematyczna i wymaga mniej wysiłku przy kodowaniu.\n\nKiedy zarówno `enableClientValidation`, jak i `enableAjaxValidation` ustawione są na `true`, walidacja za pomocą AJAX zostanie uruchomiona dopiero po udanej\nwalidacji po stronie klienta.\n"
  },
  {
    "path": "docs/guide-pl/intro-upgrade-from-v1.md",
    "content": "Aktualizacja z wersji 1.1\n=========================\n\nPomiędzy wersjami 1.1 i 2.0 Yii jest ogrom różnic, ponieważ framework został całkowicie przepisany w 2.0.\nZ tego też powodu aktualizacja z wersji 1.1 nie jest tak trywialnym procesem, jak w przypadku aktualizacji pomiędzy pomniejszymi wersjami. \nW tym przewodniku zapoznasz się z największymi różnicami dwóch głównych wersji.\n\nJeśli nie korzystałeś wcześniej z Yii 1.1, możesz pominąć tę sekcję i przejść bezpośrednio do \"[Pierwszych kroków](start-installation.md)\".\n\nZwróć uwagę na to, że Yii 2.0 wprowadza znacznie więcej nowych funkcjonalności, niż wymienionych jest w tym podsumowaniu. \nWskazane jest zapoznanie się z treścią całego przewodnika, aby poznać je wszystkie. Jest bardzo prawdopodobne, że niektóre z mechanizmów, które \npoprzednio musiałeś stworzyć samemu, teraz są częścią podstawowego kodu.\n\n\nInstalacja\n----------\n\nYii 2.0 w pełni korzysta z udogodnień [Composera](https://getcomposer.org/), będącego de facto menadżerem projektów PHP. \nZ jego pomocą odbywa się zarówno instalacja podstawowego frameworka, jak i wszystkich rozszerzeń. Aby zapoznać się ze szczegółową \ninstrukcją instalacji Yii 2.0, przejdź do sekcji [Instalacja Yii](start-installation.md). Jeśli chcesz stworzyć nowe rozszerzenie \nlub zmodyfikować istniejące w wersji 1.1, aby było kompatybilne z 2.0, przejdź do sekcji [Tworzenie rozszerzeń](structure-extensions.md#creating-extensions).\n\n\nWymagania PHP\n-------------\n\nYii 2.0 wymaga PHP w wersji 5.4 lub nowszej, która została znacząco ulepszona w stosunku do wersji 5.2 (wymaganej przez Yii 1.1).\nZ tego też powodu już na poziomie samego języka pojawiło się sporo różnic, na które należy zwrócić uwagę.\nPoniżej znajdziesz krótkie podsumowanie głównych różnic dotyczących PHP:\n\n- [Przestrzenie nazw](https://www.php.net/manual/pl/language.namespaces.php).\n- [Funkcje anonimowe](https://www.php.net/manual/pl/functions.anonymous.php).\n- Skrócona składnia zapisu tablic `[...elementy...]` używana zamiast `array(...elementy...)`.\n- Krótkie tagi echo `<?=` używane w plikach widoków. Można ich używać bezpiecznie, począwszy od PHP 5.4.\n- [Klasy i interfejsy SPL](https://www.php.net/manual/pl/book.spl.php).\n- [Opóźnione statyczne wiązania](https://www.php.net/manual/pl/language.oop5.late-static-bindings.php).\n- [Data i czas](https://www.php.net/manual/pl/book.datetime.php).\n- [Traity](https://www.php.net/manual/pl/language.oop5.traits.php).\n- [Rozszerzenie intl](https://www.php.net/manual/pl/book.intl.php). Yii 2.0 korzysta z rozszerzenia PHP `intl` do wsparcia obsługi internacjonalizacji.\n\n\nPrzestrzeń nazw\n---------------\n\nNajbardziej oczywista zmiana w Yii 2.0 dotyczy używania przestrzeni nazw. Praktycznie każda z podstawowych klas je wykorzystuje, np. `yii\\web\\Request`. \nPrefiks \"C\" nie jest już używany w nazwach, a sam schemat nazewnictwa odpowiada teraz strukturze folderów - dla przykładu `yii\\web\\Request` wskazuje, \nże plik klasy to `web/Request.php` znajdujący się w folderze frameworka Yii.\n\nDzięki mechanizmowi ładowania klas Yii możesz użyć dowolnej podstawowej klasy frameworka bez konieczności bezpośredniego dołączania jej kodu.\n\n\nKomponent i obiekt\n------------------\n\nYii 2.0 rozdzielił klasę `CComponent` z 1.1 na dwie: [[yii\\base\\BaseObject|BaseObject]] i [[yii\\base\\Component|Component]].\nLekka klasa [[yii\\base\\BaseObject|BaseObject]] pozwala na zdefiniowanie [właściwości obiektu](concept-properties.md) poprzez gettery i settery. \nKlasa [[yii\\base\\Component|Component]] dziedziczy po [[yii\\base\\BaseObject|BaseObject]] i dodatkowo wspiera obsługę [zdarzeń](concept-events.md) oraz [zachowań](concept-behaviors.md).\n\nJeśli Twoja klasa nie wymaga ww. wsparcia, rozważ użycie [[yii\\base\\BaseObject|BaseObject]] jako klasy podstawowej. Tak jest zazwyczaj w przypadku klas reprezentujących \nnajbardziej podstawową strukturę danych.\n\n\nKonfiguracja obiektu\n--------------------\n\nKlasa [[yii\\base\\BaseObject|BaseObject]] wprowadza ujednoliconą formę konfigurowania obiektów. Każda klasa dziedzicząca po [[yii\\base\\BaseObject|BaseObject]] powinna zadeklarować swój \nkonstruktor (jeśli tego wymaga) w następujący sposób, dzięki czemu zostanie poprawnie skonfigurowana:\n\n```php\nclass MyClass extends \\yii\\base\\BaseObject\n{\n    public function __construct($param1, $param2, $config = [])\n    {\n        // ... inicjalizacja przed skonfigurowaniem\n\n        parent::__construct($config);\n    }\n\n    public function init()\n    {\n        parent::init();\n\n        // ... inicjalizacja po skonfigurowaniu\n    }\n}\n```\n\nW powyższym przykładzie ostatnim parametrem konstruktora musi być tablica konfiguracyjna, \nzawierająca pary nazwa-wartość służące do zainicjowania właściwości na końcu konstruktora.\nMożesz nadpisać metodę [[yii\\base\\BaseObject::init()|init()]], aby wykonać dodatkowy proces inicjalizacyjny po \nzaaplikowaniu konfiguracji.\n\nDzięki tej konwencji możesz tworzyć i konfigurować nowe obiekty, używając \ntablicy konfiguracyjnej:\n\n```php\n$object = Yii::createObject([\n    'class' => 'MyClass',\n    'property1' => 'abc',\n    'property2' => 'cde',\n], [$param1, $param2]);\n```\n\nWięcej szczegółów na temat konfiguracji znajdziesz w sekcji [Konfiguracje](concept-configurations.md).\n\n\nZdarzenia (Events)\n------------------\n\nW Yii 1 zdarzenia były tworzone poprzez definiowanie `on`-metody (np., `onBeforeSave`). W Yii 2 możesz użyć dowolnej nazwy. \nUruchomienie zdarzenia następuje poprzez wywołanie metody [[yii\\base\\Component::trigger()|trigger()]]:\n\n```php\n$event = new \\yii\\base\\Event;\n$component->trigger($eventName, $event);\n```\n\nAby dołączyć uchwyt do zdarzenia, użyj metody [[yii\\base\\Component::on()|on()]]:\n\n```php\n$component->on($eventName, $handler);\n// a aby odłączyć uchwyt użyj:\n// $component->off($eventName, $handler);\n```\n\nZdarzenia zostały wzbogacone w wiele udoskonaleń. Więcej szczegółów na ten temat znajdziesz w sekcji [Zdarzenia (Events)](concept-events.md).\n\n\nAliasy ścieżek\n--------------\n\nYii 2.0 rozszerza funkcjonalność aliasów ścieżek zarówno na ścieżki plików oraz folderów, jak i adresy URL. Yii 2.0 wymaga teraz też, \naby nazwa aliasu zaczynała się znakiem `@` w celu odróżnienia jej od zwyczajnych ścieżek plików/folderów lub URLi.\nDla przykładu: alias `@yii` odnosi się do folderu instalacji Yii. Aliasy ścieżek są wykorzystywane w większości miejsc w podstawowym \nkodzie Yii, choćby [[yii\\caching\\FileCache::cachePath|cachePath]] - można tu przekazać zarówno zwykłą ścieżkę, jak i alias.\n\nAlias ścieżki jest mocno powiązany z przestrzenią nazw klasy. Zalecane jest, aby zdefiniować alias dla każdej podstawowej \nprzestrzeni nazw, dzięki czemu mechanizm automatycznego ładowania klas Yii nie będzie wymagał dodatkowej konfiguracji. \nDla przykładu: dzięki temu, że `@yii` odwołuje się do folderu instalacji Yii, klasa taka jak `yii\\web\\Request` może być automatycznie załadowana. \nJeśli używasz zewnętrznych bibliotek, jak np. Zend Framework, możesz zdefiniować alias `@Zend` odnoszący się do folderu instalacji tego frameworka. \nOd tej pory Yii będzie również w stanie automatycznie załadować każdą klasę z tej biblioteki.\n\nWięcej o aliasach ścieżek dostępne jest w sekcji [Aliasy](concept-aliases.md).\n\n\nWidoki\n------\n\nNajbardziej znaczącą zmianą dotyczącą widoków w Yii 2 jest użycie specjalnej zmiennej `$this`. W widoku nie odnosi się ona już do \naktualnego kontrolera lub widżetu, lecz do obiektu *widoku*, nowej koncepcji przedstawionej w 2.0. Obiekt *widoku* jest klasą typu [[yii\\web\\View|View]], która \nreprezentuje część wzorca MVC. Jeśli potrzebujesz odwołać się do kontrolera lub widżetu w widoku, możesz użyć `$this->context`.\n\nAby zrenderować częściowy widok wewnątrz innego widoku, możesz użyć `$this->render()` zamiast dotychczasowego `$this->renderPartial()`. \nWywołanie `render` musi teraz też być bezpośrednio wyechowane, ponieważ metoda `render()` zwraca rezultat renderowania zamiast od razu go wyświetlać. \n\n```php\necho $this->render('_item', ['item' => $item]);\n```\n\nOprócz wykorzystania PHP jako podstawowego języka szablonów, Yii 2.0 oficjalnie wspiera dwa popularne silniki szablonów: Smarty i Twig (The Prado nie jest już wspierany).\nAby użyć któregokolwiek z tych silników, musisz skonfigurować komponent aplikacji `view` poprzez ustawienie właściwości [[yii\\base\\View::$renderers|$renderers]]. \nPo więcej szczegółów przejdź do sekcji [Silniki szablonów](tutorial-template-engines.md).\n\n\nModele\n------\n\nYii 2.0 korzysta z [[yii\\base\\Model|Model]] jako bazowego modelu, podobnie jak `CModel` w 1.1.\nKlasa `CFormModel` została całkowicie usunięta, w Yii 2 należy rozszerzyć [[yii\\base\\Model|Model]], aby stworzyć klasę modelu formularza.\n\nYii 2.0 wprowadza nową metodę [[yii\\base\\Model::scenarios()|scenarios()]], służącą do deklarowania scenariuszy, jak i do oznaczania, w którym scenariuszu \natrybut będzie wymagał walidacji, może być uznany za bezpieczny lub nie itp. Dla przykładu:\n\n```php\npublic function scenarios()\n{\n    return [\n        'backend' => ['email', 'role'],\n        'frontend' => ['email', '!role'],\n    ];\n}\n```\n\nWidzimy tutaj dwa zadeklarowane scenariusze: `backend` i `frontend`. W scenariuszu `backend` obydwa atrybuty, \n`email` i `role`, są traktowane jako bezpieczne i mogą być przypisane zbiorczo. W przypadku scenariusza `frontend`, \n`email` może być przypisany zbiorczo, ale `role` już nie. Zarówno `email` jak i `role` powinny przejść proces walidacji.\n\nMetoda [[yii\\base\\Model::rules()|rules()]] wciąż służy do zadeklarowania zasad walidacji. Zauważ, że z powodu wprowadzenia [[yii\\base\\Model::scenarios()|scenarios()]], \nnie ma już walidatora `unsafe`.\n\nJeśli metoda [[yii\\base\\Model::rules()|rules()]] deklaruje użycie wszystkich możliwych scenariuszy i jeśli nie masz potrzeby deklarowania atrybutów `unsafe` (niebezpiecznych), \nw większości przypadków nie potrzebujesz nadpisywać metody [[yii\\base\\Model::scenarios()|scenarios()]].\n\nAby dowiedzieć się więcej o modelach, przejdź do sekcji [Modele](structure-models.md).\n\n\nKontrolery\n----------\n\nYii 2.0 używa [[yii\\web\\Controller|Controller]] jako bazowej klasy kontrolera, podobnie do `CController` w Yii 1.1.\n[[yii\\base\\Action|Action]] jest bazową klasą dla akcji.\n\nNajbardziej oczywistą implikacją tych zmian jest to, że akcja kontrolera powinna zwracać zawartość, którą chcesz wyświetlić, zamiast wyświetlać ją bezpośrednio:\n\n```php\npublic function actionView($id)\n{\n    $model = \\app\\models\\Post::findOne($id);\n    if ($model) {\n        return $this->render('view', ['model' => $model]);\n    } else {\n        throw new \\yii\\web\\NotFoundHttpException;\n    }\n}\n```\n\nPrzejdź do sekcji [Kontrolery](structure-controllers.md), aby poznać więcej szczegółów na ten temat.\n\n\nWidżety\n-------\n\nYii 2.0 korzysta z [[yii\\base\\Widget|Widget]] jako bazowej klasy widżetów, podobnie jak `CWidget` w Yii 1.1.\n\nDla lepszego wsparcia frameworka w aplikacjach IDE Yii 2.0 wprowadził nową składnię używania widżetów. Używane są teraz metody \n[[yii\\base\\Widget::begin()|begin()]], [[yii\\base\\Widget::end()|end()]] i [[yii\\base\\Widget::widget()|widget()]] w następujący sposób:\n\n```php\nuse yii\\widgets\\Menu;\nuse yii\\widgets\\ActiveForm;\n\n// Zwróć uwagę na konieczność użycia \"echo\", aby wyświetlić rezultat\necho Menu::widget(['items' => $items]);\n\n// Przekazujemy tablicę, aby zainicjalizować właściwości obiektu\n$form = ActiveForm::begin([\n    'options' => ['class' => 'form-horizontal'],\n    'fieldConfig' => ['inputOptions' => ['class' => 'input-xlarge']],\n]);\n... pola formularza w tym miejscu ...\nActiveForm::end();\n```\n\nWięcej szczegółów na ten temat znajdziesz w sekcji [Widżety](structure-widgets.md).\n\n\nSkórki i motywy (Theming)\n-------------------------\n\nSkórki działają zupełnie inaczej w 2.0. Oparte są teraz na mechanizmie mapowania ścieżki, który przekształca źródłowy plik widoku \nw plik widoku skórki. Dla przykładu, jeśli mapa ścieżki dla skórki to `['/web/views' => '/web/themes/basic']`, to skórkowa wersja pliku widoku \n`/web/views/site/index.php` to `/web/themes/basic/site/index.php`. Dzięki temu skórki mogą być użyte dla dowolnego pliku widoku, nawet w przypadku \nwidoku wyrenderowanego poza kontekstem kontrolera lub widżetu.\n\nNie ma również już komponentu `CThemeManager`. Zamiast tego `theme` jest konfigurowalną właściwością komponentu aplikacji `view`.\n\nSekcja [Skórki i motywy (Theming)](output-theming.md) zawiera więcej szczegółów na ten temat.\n\n\nAplikacje konsolowe\n-------------------\n\nAplikacje konsolowe używają teraz kontrolerów tak jak aplikacje webowe. Kontrolery konsolowe powinny rozszerzać klasę \n[[yii\\console\\Controller]], podobnie jak `CConsoleCommand` w 1.1.\n\nAby uruchomić polecenie konsoli, użyj `yii <route>`, gdzie `<route>` oznacza ścieżkę kontrolera (np. `sitemap/index`). \nDodatkowe anonimowe argumenty są przekazywane jako parametry do odpowiedniej metody akcji kontrolera, natomiast nazwane \nargumenty są przetwarzane według deklaracji zawartych w [[yii\\console\\Controller::options()|options()]].\n\nYii 2.0 wspiera automatyczne generowanie informacji pomocy poprzez bloki komentarzy.\n\nAby dowiedzieć się więcej na ten temat, przejdź do sekcji [Komendy konsolowe](tutorial-console.md).\n\n\nI18N\n----\n\nYii 2.0 usunął wbudowany formater dat i liczb na rzecz [modułu PECL intl PHP](https://pecl.php.net/package/intl).\n\nTłumaczenia wiadomości odbywają się teraz poprzez komponent aplikacji `i18n`, w którym można ustalić zestaw źródeł treści, \ndzięki czemu możliwy jest ich wybór dla różnych kategorii wiadomości.\n\nW sekcji [Internacjonalizacja](tutorial-i18n.md) znajdziesz więcej szczegółów na ten temat.\n\n\nFiltry akcji\n------------\n\nFiltry akcji są implementowane od teraz za pomocą zachowań (behavior). Aby zdefiniować nowy filtr, należy rozszerzyć klasę [[yii\\base\\ActionFilter|ActionFilter]]. \nUżycie filtru odbywa się poprzez dołączenie go do kontrolera jako zachowanie. Dla przykładu: aby użyć filtra [[yii\\filters\\AccessControl]], dodaj poniższy kod w kontrolerze:\n\n```php\npublic function behaviors()\n{\n    return [\n        'access' => [\n            'class' => 'yii\\filters\\AccessControl',\n            'rules' => [\n                ['allow' => true, 'actions' => ['admin'], 'roles' => ['@']],\n            ],\n        ],\n    ];\n}\n```\n\nWięcej informacji na ten temat znajdziesz w sekcji [Filtry](structure-filters.md).\n\n\nZasoby (Assets)\n---------------\n\nYii 2.0 wprowadza nowy mechanizm tzw. *pakietów zasobów*, który zastąpił koncepcję pakietów skryptowych z Yii 1.1.\n\nPakiet zasobów jest kolekcją plików zasobów (np. plików JavaScript, CSS, obrazków, itd.) zgromadzoną w folderze. \nKażdy pakiet jest reprezentowany przez klasę rozszerzającą [[yii\\web\\AssetBundle|AssetBundle]]. Zarejestrowanie pakietu poprzez \nmetodę [[yii\\web\\AssetBundle::register()|register()]] pozwala na udostępnienie go publicznie. W przeciwieństwie do rozwiązania z Yii 1, \nstrona rejestrująca pakiet będzie automatycznie zawierać referencje do plików JavaScript i CSS wymienionych na jego liście.\n\nSekcja [Zasoby (Assets)](structure-assets.md) zawiera szczegółowe informacje na ten temat.\n\n\nKlasy pomocnicze\n----------------\n\nYii 2.0 zawiera wiele powszechnie używanych statycznych klas pomocniczych (helperów), takich jak:\n\n* [[yii\\helpers\\Html|Html]]\n* [[yii\\helpers\\ArrayHelper|ArrayHelper]]\n* [[yii\\helpers\\StringHelper|StringHelper]]\n* [[yii\\helpers\\FileHelper|FileHelper]]\n* [[yii\\helpers\\Json|Json]]\n\nW sekcji [Klasy pomocnicze](helper-overview.md) znajdziesz więcej informacji na ten temat.\n\n\nFormularze\n----------\n\nYii 2.0 wprowadza koncepcję *pola* do budowy formularzy, korzystając z klasy [[yii\\widgets\\ActiveForm|ActiveForm]]. \nPole jest kontenerem składającym się z etykiety, pola wprowadzenia danych formularza, informacji o błędzie i/lub tekstu podpowiedzi, reprezentowanym \nprzez obiekt klasy [[yii\\widgets\\ActiveField|ActiveField]].\nUżywając pól, możesz stworzyć formularz w sposób o wiele prostszy i bardziej przejrzysty niż do tej pory:\n\n```php\n<?php $form = yii\\widgets\\ActiveForm::begin(); ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n    <div class=\"form-group\">\n        <?= Html::submitButton('Login') ?>\n    </div>\n<?php yii\\widgets\\ActiveForm::end(); ?>\n```\n\nAby dowiedzieć się więcej na ten temat, przejdź do sekcji [Tworzenie formularzy](input-forms.md).\n\n\nKonstruktor kwerend\n-------------------\n\nW 1.1 budowanie kwerend było rozrzucone pomiędzy kilka klas, tj. `CDbCommand`, `CDbCriteria` i `CDbCommandBuilder`. \nYii 2.0 reprezentuje kwerendę bazodanową w postaci obiektu [[yii\\db\\Query|Query]], który może być zamieniony \nw komendę SQL za pomocą [[yii\\db\\QueryBuilder|QueryBuilder]].\nPrzykładowo:\n\n```php\n$query = new \\yii\\db\\Query();\n$query->select('id, name')\n      ->from('user')\n      ->limit(10);\n\n$command = $query->createCommand();\n$sql = $command->sql;\n$rows = $command->queryAll();\n```\n\nCo najlepsze, taki sposób tworzenia kwerend może być również wykorzystany przy pracy z [Active Record](db-active-record.md).\n\nPo więcej szczegółów udaj się do sekcji [Konstruktor kwerend](db-query-builder.md).\n\n\nActive Record\n-------------\n\nYii 2.0 wprowadza sporo zmian w mechanizmie [Active Record](db-active-record.md). Dwie najbardziej znaczące to konstruowanie kwerend i obsługa relacji.\n\nKlasa `CDbCriteria` z 1.1 została zastąpiona przez [[yii\\db\\ActiveQuery|ActiveQuery]] w Yii 2. Klasa ta rozszerza [[yii\\db\\Query|Query]], dzięki czemu \ndziedziczy wszystkie metody konstruowania kwerend. Aby rozpocząć budowanie kwerendy, wywołaj metodę [[yii\\db\\ActiveRecord::find()|find()]]:\n\n```php\n// Pobranie wszystkich *aktywnych* klientów i posortowanie po ich ID:\n$customers = Customer::find()\n    ->where(['status' => $active])\n    ->orderBy('id')\n    ->all();\n```\n\nDeklaracja relacji polega na prostym zdefiniowaniu metody gettera, który zwróci obiekt [[yii\\db\\ActiveQuery|ActiveQuery]].\nNazwa właściwości określonej przez tego gettera reprezentuje nazwę stworzonej relacji. Dla przykładu: w poniższym kodzie deklarujemy \nrelację `orders` (w 1.1 konieczne było zadeklarowanie relacji wewnątrz wydzielonej specjalnie metody `relations()`):\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public function getOrders()\n    {\n        return $this->hasMany('Order', ['customer_id' => 'id']);\n    }\n}\n```\n\nOd tej pory można posługiwać się `$customer->orders`, aby uzyskać dostęp do tabeli zamówień klientów poprzez relację. Dodatkowo można również posłużyć się \nnastępującym kodem, aby wywołać relacyjną kwerendę dla zadanych warunków:\n\n```php\n$orders = $customer->getOrders()->andWhere('status=1')->all();\n```\n\nPrzy \"gorliwym\" pobieraniu relacji (\"eager\", w przyciwieństwie do leniwego pobierania \"lazy\") Yii 2.0 działa inaczej niż w wersji 1.1. W 1.1 tworzono kwerendę JOIN, \naby pobrać zarówno główne, jak i relacyjne rekordy. W Yii 2.0 wywoływane są dwie komendy SQL bez użycia JOIN - pierwsza pobiera główne rekordy, a druga relacyjne, filtrując je \nprzy użyciu kluczy głównych rekordów.\n\nAby zmniejszyć zużycie CPU i pamięci, zamiast zwracać obiekty [[yii\\db\\ActiveRecord|ActiveRecord]], do kwerendy pobierającej dużą ilość rekordów możesz podpiąć metodę \n[[yii\\db\\ActiveQuery::asArray()|asArray()]], dzięki czemu zostaną one pobrane jako tablice. Przykładowo:\n\n```php\n$customers = Customer::find()->asArray()->all();\n```\n\nInną istotną zmianą jest to, że nie można już definiować domyślnych wartości atrybutów poprzez publiczne właściwości. \nJeśli potrzebujesz takich definicji, powinieneś przypisać je wewnątrz metody `init` w klasie rekordu.\n\n```php\npublic function init()\n{\n    parent::init();\n    $this->status = self::STATUS_NEW;\n}\n```\n\nNadpisywanie konstruktora klasy ActiveRecord w 1.1 wiązało się z pewnymi problemami, co nie występuje już w wersji 2.0. \nZwróć jednak uwagę na to, że przy dodawaniu parametrów do konstruktora możesz potrzebować nadpisać metodę [[yii\\db\\ActiveRecord::instantiate()|instantiate()]].\n\nW nowym rekordzie aktywnym znajdziesz wiele innych zmian i udogodnień. Aby zapoznać się z nimi, przejdź do sekcji [Rekord aktywny](db-active-record.md).\n\n\nZachowania Active Record\n------------------------\n\nW 2.0 zrezygnowaliśmy z bazowej klasy zachowania `CActiveRecordBehavior`. Jeśli chcesz stworzyć zachowanie dla rekordu aktywnego, musisz \nrozszerzyć bezpośrednio klasę [[yii\\base\\Behavior|Behavior]]. Jeśli klasa zachowania ma reagować na zdarzenia, powinna nadpisywać metodę [[yii\\base\\Behavior::events()|events()]], \njak zaprezentowano poniżej:\n\n```php\nnamespace app\\components;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\base\\Behavior;\n\nclass MyBehavior extends Behavior\n{\n    // ...\n\n    public function events()\n    {\n        return [\n            ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',\n        ];\n    }\n\n    public function beforeValidate($event)\n    {\n        // ...\n    }\n}\n```\n\n\nKlasa User i IdentityInterface\n------------------------------\n\nKlasa `CWebUser` z 1.1 została zastąpiona przez [[yii\\web\\User|User]] i nie ma już klasy `CUserIdentity`. \nZamiast tego należy zaimplementować interfejs [[yii\\web\\IdentityInterface|IdentityInterface]], który jest znacznie bardziej wygodny i oczywisty w użyciu. \nSzablon zaawansowanego projektu zawiera przykład takiego właśnie użycia.\n\nPo więcej szczegółów zajrzyj do sekcji [Uwierzytelnianie](security-authentication.md), [Autoryzacja](security-authorization.md) \ni [Szablon zaawansowanego projektu](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md).\n\n\nZarządzanie adresami URL\n------------------------\n\nZarządzanie adresami URL w Yii 2 jest bardzo podobne do tego znanego z 1.1. Głównym ulepszeniem tego mechanizmu jest teraz wsparcie dla parametrów opcjonalnych. \nDla przykładu: warunek dla adresu zadeklarowany poniżej obejmie zarówno `post/popular` jak i `post/1/popular`. W 1.1 konieczne byłoby napisanie dwóch warunków, aby \nosiągnąć to samo.\n\n```php\n[\n    'pattern' => 'post/<page:\\d+>/<tag>',\n    'route' => 'post/index',\n    'defaults' => ['page' => 1],\n]\n```\n\nPrzejdź do sekcji [Zarządzania adresami URL](runtime-routing.md) po więcej informacji.\n\nIstotną zmianą konwencji nazw dla adresów jest to, że nazwy kontrolerów i akcji typu \"camel case\" są teraz \nkonwertowane do małych liter, z każdym słowem oddzielonym za pomocą myślnika, np. ID kontrolera `CamelCaseController` zostanie \nprzekształcone w `camel-case`.\n\nZapoznaj się z sekcją dotyczącą [ID kontrolerów](structure-controllers.md#controller-ids) i [ID akcji](structure-controllers.md#action-ids).\n\n\nKorzystanie z Yii 1.1 i 2.x jednocześnie\n----------------------------------------\n\nJeśli chciałbyś skorzystać z kodu napisanego dla Yii 1.1 w aplikacji Yii 2.0, \nprosimy o zapoznanie się z sekcją [Używanie Yii 1.1 i 2.0 razem](tutorial-yii-integration.md#using-both-yii2-yii1).\n"
  },
  {
    "path": "docs/guide-pl/intro-yii.md",
    "content": "Czym jest Yii\n=============\n\nYii jest wysoko wydajnym, opartym na komponentach frameworkiem PHP do szybkiego programowania \nnowoczesnych stron internetowych. Nazwa Yii (wymawiana `[ji:]`) oznacza w języku chińskim \"prosto i ewolucyjnie\".\nMoże to być również rozumiane jako akronim dla **Yes It Is**!\n\n\nDla jakich zastosowań Yii jest najlepszy?\n-----------------------------------------\n\nYii jest frameworkiem ogólnego przeznaczenia, co oznacza, że może być wykorzystany do stworzenia \nkażdego rodzaju aplikacji internetowych korzystających z PHP. Z uwagi na architekturę \nopartą na komponentach i zaawansowane wsparcie dla mechanizmów pamięci podręcznej jest on odpowiedni\ndo tworzenia rozbudowanych aplikacji, takich jak: portale, fora, systemy zarządzania treścią (CMS),\nprojekty komercyjne (e-sklepy), usługi sieciowe i inne.\n\n\nJak wygląda porównanie Yii z innymi frameworkami?\n-------------------------------------------------\n\nJeśli korzystałeś już z innych frameworków, na pewno docenisz, jak Yii wypada na ich tle:\n\n* Jak większość frameworków, Yii wykorzystuje architekturę MVC (Model-Widok-Kontroler) i wspiera organizację kodu zgodną \n  z tym wzorcem.\n* Yii opiera się na filozofii, która mówi, że kod powinien być napisany w prosty, ale jednocześnie elegancki sposób. \n  Yii nigdy nie będzie upierać się przy przeprojektowaniu kodu jedynie w celu dokładnego trzymania się zasad wzorca projektowego.\n* Yii jest w pełni rozwiniętym frameworkiem dostarczającym sprawdzonych i gotowych do użycia funkcjonalności: konstruktorów \n  zapytań oraz ActiveRecord dla baz danych relacyjnych i NoSQL, wsparcia dla tworzenia RESTful API oraz wielopoziomowych \n  mechanizmów pamięci podręcznej i wielu, wielu innych.\n* Yii jest ekstremalnie rozszerzalny. Możesz dostosować lub wymienić praktycznie każdy fragment podstawowego kodu. \n  Dodatkowo Yii wykorzystuje architekturę rozszerzeń, dzięki czemu możesz w prosty sposób stworzyć i opublikować swoje \n  własne moduły i widżety.\n* Podstawowym celem, do którego Yii zawsze dąży, jest wysoka wydajność.\n\nYii nie jest efektem pracy pojedynczego programisty - projekt wspiera zarówno \n[grupa doświadczonych deweloperów](https://www.yiiframework.com/team/), jak i ogromna społeczność programistyczna, \nnieustannie przyczyniając się do jego rozwoju. Deweloperzy trzymają rękę na pulsie najnowszych trendów Internetu, \nza pomocą prostych i eleganckich interfejsów wzbogacając Yii w najlepsze sprawdzone rozwiązania i funkcjonalności, \ndostępne w innych frameworkach i projektach.\n\nWersje Yii\n----------\n\nYii aktualnie dostępny jest w dwóch głównych wersjach: 1.1 i 2.0. Wersja 1.1 jest kodem starszej generacji, obecnie \nw fazie utrzymaniowej. Wersja 2.0 jest całkowicie przepisaną wersją Yii z uwzględnieniem najnowszych protokołów i technologii, \ntakich jak Composer, PSR, przestrzenie nazw, traity i wiele innych. 2.0 reprezentuje aktualną generację frameworka i na \nniej skupi się głównie praca programistów w ciągu najbliższych lat.   \nTen przewodnik opisuje wersję 2.0.\n\n\nWymagania i zależności\n----------------------\n\nYii 2.0 wymaga PHP w wersji 7.4.0 lub nowszej i pracuje najwydajniej na najnowszej wersji PHP. Aby otrzymać więcej \ninformacji na temat wymagań i indywidualnych funkcjonalności, uruchom specjalny skrypt testujący system dołączony w każdym wydaniu Yii.\n\nUżywanie Yii wymaga podstawowej wiedzy o programowaniu obiektowym w PHP (OOP), ponieważ Yii\njest frameworkiem czysto obiektowym. Yii 2.0 wykorzystuje ostatnie udoskonalenia w PHP, jak \n[przestrzenie nazw](https://www.php.net/manual/pl/language.namespaces.php) i [traity](https://www.php.net/manual/pl/language.oop5.traits.php). \nZrozumienie tych konstrukcji pomoże Ci szybciej i łatwiej rozpocząć pracę z Yii 2.0.\n"
  },
  {
    "path": "docs/guide-pl/output-client-scripts.md",
    "content": "Praca ze skryptami\n===========================\n\n> Note: Ta sekcja nie została jeszcze ukończona.\n\n### Rejestrowanie skryptów\n\nDzięki obiektowi [[yii\\web\\View|View]] możesz rejestrować skrypty w aplikacji. Przeznaczone są do tego dwie dedykowane metody: \n[[yii\\web\\View::registerJs()|registerJs()]] dla skryptów wbudowanych oraz [[yii\\web\\View::registerJsFile()|registerJsFile()]] dla skryptów zewnętrznych.\nSkrypty wbudowane są przydatne przy konfiguracji oraz dynamicznym generowaniu kodu.\nMożesz dodać je w następujący sposób:\n\n```php\n$this->registerJs(\"var options = \" . json_encode($options) . \";\", View::POS_END, 'my-options');\n```\n\nPierwszy argument przekazywany do metody `registerJs` to kod JavaScript, który chcemy umieścić na stronie. Jako drugi argument wskazujemy miejsce, \nw którym skrypt ma zostać umieszczony na stronie. Możliwe wartości to:\n\n- [[yii\\web\\View::POS_HEAD|POS_HEAD]] dla sekcji `head`.\n- [[yii\\web\\View::POS_BEGIN|POS_BEGIN]] zaraz po otwarciu tagu `<body>`.\n- [[yii\\web\\View::POS_END|POS_END]] zaraz przed zamknięciem tagu `</body>`.\n- [[yii\\web\\View::POS_READY|POS_READY]] do wywołania kodu z użyciem zdarzenia `ready` na dokumencie. Ta opcja zarejestruje automatycznie [[yii\\web\\JqueryAsset|jQuery]]\n- [[yii\\web\\View::POS_LOAD|POS_LOAD]] do wywołania kodu z użyciem zdarzenia `load` na dokumencie. Ta opcja zarejestruje automatycznie [[yii\\web\\JqueryAsset|jQuery]]\n\nOstatnim argumentem jest unikalne ID skryptu, które jest używane do zidentyfikowania bloku kodu i zastąpienia go, jeśli taki został już zarejestrowany. \nJeśli ten argument nie zostanie podany, kod JavaScript zostanie użyty jako ID.\n\nSkrypt zewnętrzny może zostać dodany następująco:\n\n```php\n$this->registerJsFile('https://example.com/js/main.js', ['depends' => [\\yii\\web\\JqueryAsset::class]]);\n```\n\nArgumenty dla metod [[yii\\web\\View::registerCssFile()|registerCssFile()]] są podobne do [[yii\\web\\View::registerJsFile()|registerJsFile()]].\nW powyższym przykładzie, zarejestrowaliśmy plik `main.js` z zależnością od `JqueryAsset`. Oznacza to, że plik `main.js` zostanie dodany PO pliku `jquery.js`. \nBez określenia tej zależności, względny porządek pomiędzy `main.js` a `jquery.js` nie zostałby zachowany.\n\nTak jak i w przypadku [[yii\\web\\View::registerCssFile()|registerCssFile()]], mocno rekomendujemy, abyś użył [assetów](structure-assets.md) do zarejestrowania zewnętrznych plików JS \nzamiast używania [[yii\\web\\View::registerJsFile()|registerJsFile()]].\n\n\n### Rejestracja assetów\n\nJak zostało wspomniane wcześniej, korzystniejsze jest stosowanie assetów, zamiast kodu CSS i JS bezpośrednio (po informacje na ten temat sięgnij do sekcji \n[menedżera assetów](structure-assets.md)). \nKorzystanie z już zdefiniowanych pakietów jest bardzo proste:\n\n```php\n\\frontend\\assets\\AppAsset::register($this);\n```\n\n\n### Rejestrowanie kodu CSS\n\nMożesz zarejestrować kod CSS przy użyciu metody [[yii\\web\\View::registerCss()|registerCss()]] lub [[yii\\web\\View::registerCssFile()|registerCssFile()]].\nPierwsza z nich rejestruje blok kodu CSS, natomiast druga zewnętrzny plik `.css`. Dla przykładu:\n\n```php\n$this->registerCss(\"body { background: #f00; }\");\n```\n\nPowyższy kod doda kod CSS do sekcji `head` strony:\n\n```html\n<style>\nbody { background: #f00; }\n</style>\n```\n\nJeśli chcesz określić dodatkowe właściwości dla tagu `style`, przekaż tablicę `nazwa => wartość` jako drugi argument.\nJeśli chcesz się upewnić, że jest tylko jeden tag `style`, użyj trzeciego argumentu, tak jak zostało to opisane dla meta tagów.\n\n```php\n$this->registerCssFile(\"https://example.com/css/themes/black-and-white.css\", [\n    'depends' => [BootstrapAsset::class],\n    'media' => 'print',\n], 'css-print-theme');\n```\n\nKod powyżej doda link w sekcji `head` strony do pliku CSS.\n\n* Pierwszy argument określa, który plik ma zostać zarejestrowany,\n* Drugi argument określa atrybuty tagu `<link>`. Opcja `depends` jest obsługiwana w specjalny sposób, od niej zależy położenie pliku CSS.\n  W tym przypadku, plik link do pliku CSS zostanie umieszony ZA plikami CSS w [[yii\\bootstrap\\BootstrapAsset|BootstrapAsset]],\n* Ostatni argument określa ID identyfikujące ten plik CSS. W przypadku jego braku, zostanie użyty do tego celu adres URL pliku CSS.\n\nJest mocno wskazane używanie [assetów](structure-assets.md) do rejestrowania zewnętrznych plików CSS. Użycie ich pozwala Ci na łączenie i kompresowanie \nwielu plików CSS, które jest wręcz niezbędne na stronach internetowych o dużym natężeniu ruchu.\n"
  },
  {
    "path": "docs/guide-pl/output-pagination.md",
    "content": "Paginacja\n=========\n\nKiedy danych jest zbyt dużo, aby wyświetlić je w całości na jednej stronie, zwykle stosuje się mechanizm podziału na wiele stron, \nz których każda prezentuje tylko część danych na raz. Mechanizm ten nazywamy *paginacją*.\n\nW Yii obiekt [[yii\\data\\Pagination|Pagination]] reprezentuje zbiór informacji o schemacie paginacji.\n\n* [[yii\\data\\Pagination::$totalCount|liczba wyników]] określa całkowitą liczbę elementów zestawu danych. Zwykle jest to znacznie większa liczba \n  niż ilość elementów, które można umieścić na pojedynczej stronie.\n* [[yii\\data\\Pagination::$pageSize|rozmiar strony]] określa jak wiele elementów może znaleźć się na pojedynczej stronie. Domyślna wartość to 20.\n* [[yii\\data\\Pagination::$page|aktualna strona]] wskazuje numer aktualnie wyświetlanej strony (począwszy od zera). Domyślna wartość to 0, wskazująca na pierwszą stronę.\n\nKorzystając z w pełni zdefiniowanego obiektu [[yii\\data\\Pagination|Pagination]], można pobrać i wyświetlić dane w partiach. Dla przykładu, przy pobieraniu danych z bazy można \nużyć wartości `OFFSET` i `LIMIT` w kwerendzie, które będą odpowiadać tym zdefiniowanym przez paginację.\n\n```php\nuse yii\\data\\Pagination;\n\n// stwórz kwerendę bazodanową, aby pobrać wszystkie artykuły o statusie = 1\n$query = Article::find()->where(['status' => 1]);\n\n// ustal całkowitą liczbę artykułów (ale nie pobieraj jeszcze danych artykułów)\n$count = $query->count();\n\n// stwórz obiekt paginacji z całkowitą liczbą wyników\n$pagination = new Pagination(['totalCount' => $count]);\n\n// ogranicz wyniki kwerendy korzystając z paginacji i pobierz artykuły\n$articles = $query->offset($pagination->offset)\n    ->limit($pagination->limit)\n    ->all();\n```\n\nKtóra strona wyników z artykułami zostanie pobrana w powyższym przykładzie? To zależy od tego, czy ustawiono parametr kwerendy `page`. \nDomyślnie paginacja próbuje ustawić [[yii\\data\\Pagination::$page|aktualną stronę]] na odpowiadającą wartości parametru `page`. \nJeśli ten parametr nie jest przekazany, wartość będzie domyślna, czyli 0.\n\nAby ułatwić tworzenie elementów UI, które będą odpowiedzialne za korzystanie z mechanizmu paginacji, Yii posiada wbudowany widżet [[yii\\widgets\\LinkPager|LinkPager]], \nktóry wyświetla listę przycisków z numerami, po kliknięciu których użytkownik przechodzi do pożądanej strony wyników. \nWidżet korzysta z obiektu paginacji, dzięki czemu wie, który numer ma aktualnie wyświetlana strona i jak wiele przycisków stron powinien wyświetlić. \nPrzykład:\n\n```php\nuse yii\\widgets\\LinkPager;\n\necho LinkPager::widget([\n    'pagination' => $pagination,\n]);\n```\n\nJeśli chcesz samemu stworzyć takie elementy UI, możesz skorzystać z metody [[yii\\data\\Pagination::createUrl()|createUrl()]], aby uzyskać adresy URL poszczególnych stron. \nMetoda ta wymaga podania parametru page i zwraca poprawnie sformatowany adres URL zawierający ten parametr. Przykładowo,\n\n```php\n// określa trasę, która będzie zawarta w nowoutworzonym adresie URL\n// Jeśli nie będzie podana, użyta zostanie aktualna trasa\n$pagination->route = 'article/index';\n\n// wyświetla: /index.php?r=article%2Findex&page=100\necho $pagination->createUrl(100);\n\n// wyświetla: /index.php?r=article%2Findex&page=101\necho $pagination->createUrl(101);\n```\n\n> Tip: Możesz zmodyfikować nazwę parametru kwerendy `page`, poprzez ustawienie właściwości \n> [[yii\\data\\Pagination::pageParam|pageParam]] w czasie tworzenia obiektu paginacji.\n"
  },
  {
    "path": "docs/guide-pl/rest-error-handling.md",
    "content": "Obsługa błędów\n==============\n\nPodczas obsługi żądania RESTfulowego API, w przypadku wystąpienia błędu w zapytaniu użytkownika lub gdy stanie się coś nieprzewidywanego \nz serwerem, możesz po prostu rzucić wyjątkiem, aby powiadomić użytkownika, że coś poszło nieprawidłowo.\nJeśli możesz zidentyfikować przyczynę błędu (np. żądany zasób nie istnieje), powinieneś rozważyć rzucenie wyjątkiem razem z odpowiednim kodem statusu HTTP \n(np. [[yii\\web\\NotFoundHttpException|NotFoundHttpException]] odpowiada statusowi o kodzie 404). \nYii wyśle odpowiedź razem z odpowiadającym jej kodem i treścią statusu HTTP. Yii dołączy również do samej odpowiedzi zserializowaną reprezentację \nwyjątku. Przykładowo:\n\n```\nHTTP/1.1 404 Not Found\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"name\": \"Not Found Exception\",\n    \"message\": \"The requested resource was not found.\",\n    \"code\": 0,\n    \"status\": 404\n}\n```\n\nPoniższa lista zawiera kody statusów HTTP, które są używane przez Yii REST framework:\n\n* `200`: OK. Wszystko działa w porządku.\n* `201`: Zasób został poprawnie stworzony w odpowiedzi na żądanie `POST`. Nagłówek `Location` zawiera URL kierujący do nowoutworzonego zasobu.\n* `204`: Żądanie zostało poprawnie przetworzone, ale odpowiedź nie zawiera treści (jak w przypadku żądania `DELETE`).\n* `304`: Zasób nie został zmodyfikowany. Można użyć wersji przetrzymywanej w pamięci podręcznej.\n* `400`: Nieprawidłowe żądanie. Może być spowodowane przez wiele czynników po stronie użytkownika, takich jak przekazanie nieprawidłowych danych JSON,\n   nieprawidłowych parametrów akcji, itp.\n* `401`: Nieudana autentykacja.\n* `403`: Autoryzowany użytkownik nie ma uprawnień do danego punktu końcowego API.\n* `404`: Żądany zasób nie istnieje.\n* `405`: Niedozwolona metoda. Sprawdź nagłówek `Allow`, aby poznać dozwolone metody HTTP.\n* `415`: Niewspierany typ mediów. Żądany typ zawartości lub numer wersji jest nieprawidłowy.\n* `422`: Nieudana walidacja danych (dla przykładu w odpowiedzi na żądanie `POST`). Sprawdź treść odpowiedzi, aby poznać szczegóły błędu.\n* `429`: Zbyt wiele żądań. Żądanie zostało odrzucone z powodu osiagnięcia limitu użycia.\n* `500`: Wewnętrzny błąd serwera. To może być spowodowane wewnętrznymi błędami programu.\n\n\n## Modyfikowanie błędnej odpowiedzi <span id=\"customizing-error-response\"></span>\n\nCzasem wymagane może być dostosowanie domyślnego formatu błędnej odpowiedzi. Dla przykładu, zamiast używać różnych statusów HTTP dla oznaczenia różnych błędów, \nmożna serwować zawsze status 200 i dodawać prawidłowy kod statusu HTTP jako część struktury JSON w odpowiedzi, jak pokazano to poniżej:\n\n```\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"success\": false,\n    \"data\": {\n        \"name\": \"Not Found Exception\",\n        \"message\": \"The requested resource was not found.\",\n        \"code\": 0,\n        \"status\": 404\n    }\n}\n```\n\nAby osiągnąć powyższy efekt, należy skonfigurować odpowiedź na event `beforeSend` dla komponentu `response` w aplikacji:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'response' => [\n            'class' => 'yii\\web\\Response',\n            'on beforeSend' => function ($event) {\n                $response = $event->sender;\n                if ($response->data !== null && Yii::$app->request->get('suppress_response_code')) {\n                    $response->data = [\n                        'success' => $response->isSuccessful,\n                        'data' => $response->data,\n                    ];\n                    $response->statusCode = 200;\n                }\n            },\n        ],\n    ],\n];\n```\n\nPowyższy kod zreformatuje odpowiedź (zarówno typu sukces jak i błąd), kiedy `suppress_response_code` zostanie przekazane jako parametr `GET`.\n"
  },
  {
    "path": "docs/guide-pl/rest-rate-limiting.md",
    "content": "Limit użycia\n============\n\nW celu zapobiegnięcia nadużyciom, powinno się rozważyć wprowadzenie *limitu użycia* swojego API. Może to być na przykład ograniczenie \ndo maksymalnie 100 zapytań do API dla każdego użytkownika w czasie 10 minut. Jeśli użytkownik przekroczy ten limit w zadanym czasie, \nnależy zwrócić odpowiedź ze statusem 429 (oznaczającym \"Zbyt dużo zapytań\").\n\nAby ustalić limit użycia, [[yii\\web\\User::identityClass|klasa identyfikująca użytkownika]] powinna zaimplementować [[yii\\filters\\RateLimitInterface|RateLimitInterface]].\nInterfejs ten wymaga dodania trzech metod:\n\n* `getRateLimit()`: zwraca maksymalną liczbę zapytań i okres czasu (np. `[100, 600]` oznacza maksymalnie 100 zapytań do API w czasie 600 sekund).\n* `loadAllowance()`: zwraca liczbę pozostałych dozwolonych zapytań z limitu i uniksowy znacznik czasu wskazujący datę ostatniego sprawdzenia limitu.\n* `saveAllowance()`: zapisuje liczbę pozostałych dozwolonych zapytań i aktualny uniksowy znacznik czasu.\n\nDo celów obsługi powyższych metod można wykorzystać dwie dodatkowe kolumny w bazie danych użytkowników dla liczby dokonanych połączeń i znacznika czasu. \nPo ustaleniu tych wartości, metody `loadAllowance()` i `saveAllowance()` mogą być poprawnie zaimplementowane do odczytu i zapisu tych wartości dla aktualnego \nzautoryzowanego użytkownika. Aby zwiększyć wydajność tego mechanizmu, należy rozważyć użycie pamięci podręcznej lub bazy typu NoSQL.\n\nPo zaimplemetowaniu wymaganego interfejsu, Yii automatycznie użyje [[yii\\filters\\RateLimiter|RateLimiter]], skonfigurowanego jako filtr akcji dla [[yii\\rest\\Controller|Controller]], \naby pilnować limitu użycia API. Mechanizm rzuci wyjątek [[yii\\web\\TooManyRequestsHttpException|TooManyRequestsHttpException]], kiedy limit zostanie przekroczony. \n\nPo dodaniu limitu, każda odpowiedź będzie domyślnie zawierała następujące nagłówki HTTP, zawierające informacje o aktualnym użyciu limitu:\n\n* `X-Rate-Limit-Limit`, maksymalna liczba zapytań w zadanym okresie czasu,\n* `X-Rate-Limit-Remaining`, liczba pozostałych dozwolonych zapytań z limitu w aktualnym okresie czasu,\n* `X-Rate-Limit-Reset`, liczba sekund, którą należy odczekać, aby uzyskać ponownie maksymalną liczbę zapytań z limitu.\n\nWysyłanie powyższych nagłówków można wyłączyć konfigurując [[yii\\filters\\RateLimiter::enableRateLimitHeaders|enableRateLimitHeaders]] w klasie kontrolera REST jak w poniższym przykładzie.\n\n```php\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n    $behaviors['rateLimiter']['enableRateLimitHeaders'] = false;\n    return $behaviors;\n}\n```\n"
  },
  {
    "path": "docs/guide-pl/rest-routing.md",
    "content": "Routing\n=======\n\nPo przygotowaniu klas zasobów i kontrolerów dostęp do nich można uzyskać w ten sam sposób, jak w przypadku zwykłej aplikacji, używając URL np. \n`http://localhost/index.php?r=user/create`.\n\nW praktyce zwykle chcemy skorzystać z opcji \"ładnych\" URLi i metod HTTP.\nPrzykładowo żądanie `POST /users` może oznaczać wywołanie akcji `user/create`, co możemy uzyskać w łatwy sposób konfigurując \n[komponent aplikacji](structure-application-components.md) `urlManager` w pliku konfiguracyjnym jak poniżej:\n\n```php\n'urlManager' => [\n    'enablePrettyUrl' => true,\n    'enableStrictParsing' => true,\n    'showScriptName' => false,\n    'rules' => [\n        ['class' => 'yii\\rest\\UrlRule', 'controller' => 'user'],\n    ],\n]\n```\n\nPorównując to z menadżerem URLi dla aplikacji Web, główną nowością tutaj jest użycie [[yii\\rest\\UrlRule|UrlRule]] do routingu RESTfulowych zasobów API. \nTa specjalna klasa zasad URL stworzy cały zestaw potomnych zasad URL obsługujących routing i tworzenie URLi dla wyznaczonego kontrolera.\nDla przykładu, kod powyżej jest zgrubnym odpowiednikiem następujących zasad:\n\n```php\n[\n    'PUT,PATCH users/<id>' => 'user/update',\n    'DELETE users/<id>' => 'user/delete',\n    'GET,HEAD users/<id>' => 'user/view',\n    'POST users' => 'user/create',\n    'GET,HEAD users' => 'user/index',\n    'users/<id>' => 'user/options',\n    'users' => 'user/options',\n]\n```\n\nI poniższe punkty końcowe API są obsługiwane przez tę zasadę:\n\n* `GET /users`: lista wszystkich użytkowników strona po stronie;\n* `HEAD /users`: pokazuje streszczenie informacji listy użytkowników;\n* `POST /users`: tworzy nowego użytkownika;\n* `GET /users/123`: zwraca szczegóły na temat użytkownika 123;\n* `HEAD /users/123`: zwraca streszczenie informacji o użytkowniku 123;\n* `PATCH /users/123` i `PUT /users/123`: aktualizuje użytkownika 123;\n* `DELETE /users/123`: usuwa użytkownika 123;\n* `OPTIONS /users`: pokazuje obsługiwane metody dla punktu końcowego `/users`;\n* `OPTIONS /users/123`: pokazuje obsługiwane metody dla punktu końcowego `/users/123`.\n\nMożesz skonfigurować opcje `only` i `except`, aby wskazać listę akcji, które mają być odpowiednio: tylko obsługiwane lub pominięte.\nPrzykładowo,\n\n```php\n[\n    'class' => 'yii\\rest\\UrlRule',\n    'controller' => 'user',\n    'except' => ['delete', 'create', 'update'],\n],\n```\n\nDodatkowo można dodać opcję `patterns` lub `extraPatterns`, aby zredefiniować istniejące wzorce lub dodać nowe obsługiwane przez tę zasadę.\nDla przykładu, aby dodać obsługę nowej akcji `search` dla punktu końcowego `GET /users/search`, skonfiguruj opcję `extraPatterns` jak następuje,\n\n```php\n[\n    'class' => 'yii\\rest\\UrlRule',\n    'controller' => 'user',\n    'extraPatterns' => [\n        'GET search' => 'search',\n    ],\n]\n```\n\nNa pewno zwróciłeś uwagę na to, że ID kontrolera `user` występuje tu w formie mnogiej jako `users` dla URLi punktu końcowego.\nDzieje się tak, ponieważ [[yii\\rest\\UrlRule|UrlRule]] automatycznie przechodzi na formę mnogą dla ID kontrolerów podczas tworzenia potomnych zasad URL.\nZachowanie to można wyłączyć ustawiając [[yii\\rest\\UrlRule::pluralize|pluralize]] na `false`. \n\n> Info: forma mnoga ID kontrolerów jest tworzona poprzez metodę [[yii\\helpers\\Inflector::pluralize()|pluralize()]]. Uwzględnia ona specjalne zasady tworzenia form mnogich. \n> Dla przykładu, od słowa `box` zostanie utworzona liczba mnoga `boxes` a nie `boxs`.\n\nW przypadku, gdy mechanizm automatycznego tworzenia formy mnogiej nie spełnia Twoich oczekiwań, możesz również skonfigurować właściwość \n[[yii\\rest\\UrlRule::controller|controller]], aby bezpośrednio określić w jaki sposób nazwa użyta w punkcie końcowym URLi ma być zmapowana na ID kontrolera. \nDla przykładu, poniższy kod mapuje nazwę `u` na ID kontrolera `user`.  \n \n```php\n[\n    'class' => 'yii\\rest\\UrlRule',\n    'controller' => ['u' => 'user'],\n]\n```\n"
  },
  {
    "path": "docs/guide-pl/rest-versioning.md",
    "content": "Wersjonowanie\n=============\n\nCechą dobrego API jest jego *wersjonowanie*: zmiany i nowe funkcjonalności powinny być implementowane w nowych wersjach API, zamiast \nciągłych modyfikacji jednej już istniejącej. W przeciwieństwie do aplikacji Web, nad którymi ma się pełną kontrolę zarówno po stronie \nklienta, jak i serwera, nad API zwykle nie posiada się kontroli po stronie klienta. Z tego powodu niezwykle istotnym jest, aby zachować \npełną wsteczną kompatybilność (BC = backward compatibility), kiedy to tylko możliwe. Jeśli konieczne jest wprowadzenie zmiany, która \nmoże nie spełniać BC, należy wprowadzić ją w nowej wersji API, z kolejnym numerem. Istniejące klienty mogą wciąż używać starej, \ndziałającej wersji API, a nowe lub uaktualnione klienty mogą otrzymać nową funkcjonalność oferowaną przez kolejną wersję API. \n\n> Tip: Zapoznaj się z [Wersjonowaniem semantycznym](https://semver.org/lang/pl/), aby uzyskać więcej informacji na temat nazewnictwa \n  numerów wersji.\n\nJedną z często spotykanych implementacji wersjonowania API jest dodawanie numeru wersji w adresach URL API.\nDla przykładu `https://example.com/v1/users` oznacza punkt końcowy `/users` API w wersji 1. \n\nInną metodą wersjonowania API, która zyskuje ostatnio popularność, jest umieszczanie numeru wersji w nagłówkach HTTP żądania. Zwykle \nużywa się do tego nagłówka `Accept`:\n\n```\n// poprzez parametr\nAccept: application/json; version=v1\n// poprzez dostarczany typ zasobu\nAccept: application/vnd.company.myapp-v1+json\n```\n\nObie metody mają swoje wady i zalety i wciąż prowadzone są dyskusje na ich temat. Poniżej prezentujemy strategię wersjonowania, która \nw praktyczny sposób łączy je obie:\n\n* Umieść każdą główną wersję implementacji API w oddzielnym module, którego ID odpowiada numerowi głównej wersji (np. `v1`, `v2`).\n  Adresy URL API będą zawierały numery głównych wersji.\n* Wewnątrz każdej głównej wersji (i w związku z tym w każdym odpowiadającym jej module), użyj nagłówka HTTP `Accept`, aby określić \npomniejszy numer wersji i napisz warunkowy kod odpowiadający temu numerowi.\n\nKażdy moduł obsługujący główną wersję powinien zawierać klasy zasobów i kontrolerów odpowiednie dla tej wersji. W celu lepszego \nrozdzielenia zadań kodu, możesz trzymać razem zestaw podstawowych wspólnych klas zasobów i kontrolerów w jednym miejscu i rozdzielać go \nna podklasy w każdym module wersji. W podklasie implementujesz bazowy kod, taki jak `Model::fields()`.\n\nStruktura Twojego kodu może wyglądać jak poniższa:\n\n```\napi/\n    common/\n        controllers/\n            UserController.php\n            PostController.php\n        models/\n            User.php\n            Post.php\n    modules/\n        v1/\n            controllers/\n                UserController.php\n                PostController.php\n            models/\n                User.php\n                Post.php\n            Module.php\n        v2/\n            controllers/\n                UserController.php\n                PostController.php\n            models/\n                User.php\n                Post.php\n            Module.php\n```\n\nKonfiguracja Twojej aplikacji mogłaby wyglądać następująco:\n\n```php\nreturn [\n    'modules' => [\n        'v1' => [\n            'class' => 'app\\modules\\v1\\Module',\n        ],\n        'v2' => [\n            'class' => 'app\\modules\\v2\\Module',\n        ],\n    ],\n    'components' => [\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            'enableStrictParsing' => true,\n            'showScriptName' => false,\n            'rules' => [\n                ['class' => 'yii\\rest\\UrlRule', 'controller' => ['v1/user', 'v1/post']],\n                ['class' => 'yii\\rest\\UrlRule', 'controller' => ['v2/user', 'v2/post']],\n            ],\n        ],\n    ],\n];\n```\n\nRezultatem powyższego kodu będzie skierowanie pod adresem `https://example.com/v1/users` do listy użytkowników w wersji 1, podczas gdy \n`https://example.com/v2/users` pokaże użytkowników w wersji 2.\n\nDzięki podziałowi na moduły, kod różnych głównych wersji może być dobrze izolowany, ale jednocześnie wciąż możliwe jest ponowne \nwykorzystanie wspólnego kodu poprzez wspólną bazę klas i dzielonych zasobów.\n\nAby prawidłowo obsłużyć pomniejsze numery wersji, możesz wykorzystać funkcjonalność negocjatora zawartości dostarczaną przez behavior \n[[yii\\filters\\ContentNegotiator|contentNegotiator]]. Ustawi on właściwość [[yii\\web\\Response::acceptParams]], kiedy już zostanie \nustalone, który typ zasobów wspierać.\n\nPrzykładowo, jeśli żądanie jest wysłane z nagłówkiem HTTP `Accept: application/json; version=v1`, po negocjacji zawartości \n[[yii\\web\\Response::acceptParams]] będzie zawierać wartość `['version' => 'v1']`.\n\nBazując na informacji o wersji w `acceptParams`, możesz napisać obsługujący ją warunkowy kod w miejscach takich jak akcje, klasy \nzasobów, serializatory, itp., aby zapewnić odpowiednią funkcjonalność.\n\nPonieważ pomniejsze wersje z definicji wymagają zachowania wstecznej kompatybilności, w kodzie nie powinno znaleźć się zbyt wiele \nmiejsc, gdzie numer wersji będzie sprawdzany. W przeciwnym wypadku możliwe, że konieczne będzie utworzenie kolejnej głównej wersji API.\n"
  },
  {
    "path": "docs/guide-pl/runtime-bootstrapping.md",
    "content": "Bootstrapping\n=============\n\nBootstrapping to proces przygotowania środowiska działania aplikacji przed jej uruchomieniem, w celu przyjęcia i przetworzenia przychodzącego żądania. \nMechanizm ten zachodzi w dwóch miejscach: [skrypcie wejściowym](structure-entry-scripts.md) i w samej [aplikacji](structure-applications.md).\n\n[Skrypt wejściowy](structure-entry-scripts.md) zajmuje się rejestracją autoloaderów klas dla poszczególnych bibliotek, wliczając w to autoloader Composera \npoprzez jego plik `autoload.php` i autoloader Yii poprzez plik klasy `Yii`. Następnie skrypt wejściowy ładuje [konfigurację](concept-configurations.md) \ni tworzy instancję [aplikacji](structure-applications.md).\n\nW konstruktorze aplikacji zachodzi następujący proces bootstrappingu:\n\n1. Wywołanie metody [[yii\\base\\Application::preInit()|preInit()]], która konfiguruje niektóre z właściwości aplikacji o wysokim priorytecie, \n   takich jak [[yii\\base\\Application::basePath|basePath]].\n2. Rejestracja [[yii\\base\\Application::errorHandler|obsługi błędów]].\n3. Inicjalizacja właściwości aplikacji za pomocą utworzonej konfiguracji.\n4. Wywołanie metody [[yii\\base\\Application::init()|init()]], która uruchamia proces bootstrappingu komponentów za pomocą metody \n   [[yii\\base\\Application::bootstrap()|bootstrap()]].\n   - Załadowanie pliku manifestu dla rozszerzeń `vendor/yiisoft/extensions.php`.\n   - Utworzenie i uruchomienie [komponentów bootstrapowych](structure-extensions.md#bootstrapping-classes) zadeklarowanych przez rozszerzenia.\n   - Utworzenie i uruchomienie [komponentów aplikacji](structure-application-components.md) i/lub [modułów](structure-modules.md), zadeklarowanych \n     we [właściwości bootstrapowej](structure-applications.md#bootstrap) aplikacji.\n\nPonieważ proces bootstrappingu jest wykonywany przed obsługą *każdego* żądania, niezwykle istotnym jest, aby był lekki i zoptymalizowany tak bardzo, jak to tylko możliwe.\n\nNależy unikać rejestrowania zbyt dużej ilości komponentów bootstrappingowych. Wymagane jest to tylko w przypadku, gdy taki komponent powinien uczestniczyć \nw całym cyklu życia obsługi przychodzącego żądania. Dla przykładu, jeśli moduł wymaga zarejestrowania dodatkowych zasad przetwarzania adresów URL, powinien być wymieniony \nwe [właściwości bootstrapowej](structure-applications.md#bootstrap), aby nowe zasady mogły być wykorzystane do przetworzenia żądania.\n\nW środowisku produkcyjnym zaleca się zastosowanie pamięci podręcznej kodu, takiej jak [PHP OPcache] lub [APC], aby zminimalizować czas konieczny do załadowania i przetworzenia \nplików PHP.\n\n[PHP OPcache]: https://www.php.net/manual/en/intro.opcache.php\n[APC]: https://www.php.net/manual/en/book.apcu.php\n\nNiektóre duże aplikacje posiadają bardzo skomplikowaną [konfigurację](concept-configurations.md), składającą się z wielu mniejszych plików konfiguracyjnych. \nW takim przypadku zalecane jest zapisanie w pamięci całej wynikowej tablicy konfiguracji i załadowanie jej stamtąd bezpośrednio przed utworzeniem instancji aplikacji \nw skrypcie wejściowym.\n"
  },
  {
    "path": "docs/guide-pl/runtime-overview.md",
    "content": "Przegląd\n========\n\nZa każdym razem kiedy aplikacja Yii obsługuje żądanie, przetwarza je w podobny sposób.\n\n1. Użytkownik wykonuje żądanie do [skryptu wejściowego](structure-entry-scripts.md) `web/index.php`.\n2. Skrypt wejściowy ładuje [konfigurację](concept-configurations.md) aplikacji i tworzy [instancję aplikacji](structure-applications.md), aby obsłużyć zapytanie.\n3. Aplikacja osiąga żądaną [ścieżkę](runtime-routing.md) za pomocą komponentu [żądania](runtime-requests.md) aplikacji.\n4. Aplikacja tworzy instancję [kontrolera](structure-controllers.md), który obsłuży żądanie.\n5. Kontroler tworzy instancję [akcji](structure-controllers.md) i przetwarza filtry dla akcji.\n6. Jeżeli jakikolwiek filtr się nie wykona, akcja zostanie anulowana.\n7. Jeżeli wszystkie filtry przejdą, akcja zostaje wykonana.\n8. Akcja wczytuje model danych, być może z bazy danych.\n9. Akcja renderuje widok dostarczając go z modelem danych.\n10. Wyrenderowana zawartość jest zwracana do komponentu [odpowiedzi](runtime-responses.md) aplikacji.\n11. Komponent odpowiedzi wysyła wyrenderowaną zawartość do przeglądarki użytkownika.\n\n![Cykl życia żądania](images/request-lifecycle.png)\n\nW tej sekcji opiszemy szczegóły dotyczące niektórych kroków przetwarzania żądania."
  },
  {
    "path": "docs/guide-pl/security-overview.md",
    "content": "Bezpieczeństwo\n==============\n\nSpełnienie wymogów bezpieczeństwa jest podstawą sukcesu i długiej żywotności każdej aplikacji. Niestety, wielu deweloperów idzie \nw tym temacie drogą na skróty, czy to z powodu braku jego zrozumienia, czy też kłopotów z implementacją we własnym kodzie. \nAby uczynić Twoją aplikację opartą na Yii tak bezpieczną, jak to tylko możliwe, Yii zapewnia kilka świetnych i jednocześnie łatwych \nw użyciu funkcjonalności służących poprawie bezpieczeństwa.\n\n* [Uwierzytelnianie](security-authentication.md)\n* [Autoryzacja](security-authorization.md)\n* [Praca z hasłami](security-passwords.md)\n* [Kryptografia](security-cryptography.md)\n* [Bezpieczeństwo widoków](structure-views.md#security)\n* [Klienty autoryzacji](https://github.com/yiisoft/yii2-authclient/blob/master/docs/guide/README.md)\n* [Bezpieczeństwo w praktyce](security-best-practices.md)\n"
  },
  {
    "path": "docs/guide-pl/start-databases.md",
    "content": "Praca z bazami danych\n======================\n\nTa sekcja opisuje jak utworzyć nową stronę, która będzie wyświetlała dane krajów pobrane z tabeli bazy danych o nazwie `country`.\nAby to osiągnąć, musisz skonfigurować swoje połączenie z bazą danych, utworzyć klasę [Active Record](db-active-record.md), zdefiniować [akcję](structure-controllers.md) oraz utworzyć \n[widok](structure-views.md).\n\nW tej sekcji nauczysz się:\n\n* konfigurowania połączenia z bazą danych,\n* tworzenia klasy Active Record,\n* tworzenia zapytań o dane przy użyciu klasy Active Record,\n* wyświetlania danych w widoku wraz ze stronicowaniem.\n\n\nZauważ, że w celu przejścia tej sekcji należy mieć już podstawową wiedzę o bazach danych.\nW szczególności powinieneś wiedzieć, jak utworzyć bazę dancyh oraz jak wywołać komendę SQL używając klienta bazy danych.\n\nPrzygotowanie bazy danych <span id=\"preparing-database\"></span>\n----------------------\n\nAby rozpocząć, musisz utworzyć bazę danych o nazwie `yii2basic`, z której będziesz pobierał dane do swojej aplikacji.\nMożesz utworzyć bazę SQLite, MySQL, PostgreSQL, MSSQL lub Oracle, ponieważ Yii posiada wbudowane wsparcie dla wielu aplikacji bazodanowych. \nDla uproszczenia w naszym przykładzie wykorzystamy MySQL.\n\nNastępnie, utwórz tabelę o nazwie `country` i wstaw przykładowe dane. Możesz użyc poniższej komendy:\n\n```sql\nCREATE TABLE `country` (\n  `code` CHAR(2) NOT NULL PRIMARY KEY,\n  `name` CHAR(52) NOT NULL,\n  `population` INT(11) NOT NULL DEFAULT '0'\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nINSERT INTO `country` VALUES ('AU','Australia',24016400);\nINSERT INTO `country` VALUES ('BR','Brazil',205722000);\nINSERT INTO `country` VALUES ('CA','Canada',35985751);\nINSERT INTO `country` VALUES ('CN','China',1375210000);\nINSERT INTO `country` VALUES ('DE','Germany',81459000);\nINSERT INTO `country` VALUES ('FR','France',64513242);\nINSERT INTO `country` VALUES ('GB','United Kingdom',65097000);\nINSERT INTO `country` VALUES ('IN','India',1285400000);\nINSERT INTO `country` VALUES ('RU','Russia',146519759);\nINSERT INTO `country` VALUES ('US','United States',322976000);\n```\n\nW tym miejscu masz już utworzoną bazę danych o nazwie `yii2basic`, posiadającą tabelę `country` z trzema kolumnami. Tabela zawiera 10 wierszy danych.\n\nKonfiguracja połączenia z bazą danych <span id=\"configuring-db-connection\"></span>\n---------------------------\n\nPrzed przystąpieniem do tej części, upewnij się, że masz zainstalowane rozszerzenie [PDO](https://www.php.net/manual/en/book.pdo.php) oraz sterownik PDO dla bazy danych której używasz \n(np. `pdo_mysql` dla MySQL).\nJest to podstawowe wymaganie, jeśli Twoja aplikacja używa relacyjnej bazy danych.\n\nJeśli posiadasz zainstalowane powyższe rozszerzenia, otwórz plik `config/db.php` i zmień parametry na odpowiednie do Twojej bazy danych. Domyślnie plik zawiera poniższy fragment:\n\n```php\n<?php\n\nreturn [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=localhost;dbname=yii2basic',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n];\n```\n\nPlik `config/db.php` jest typowym narzędziem [konfiguracyjnym](concept-configurations.md) opartym na plikach.\nTen szczególny plik konfiguracyjny określa parametry potrzebne do utworzenia oraz zainicjalizowania instancji [[yii\\db\\Connection|Connection]], dzięki czemu będziesz mógł wywoływać \nkomendy SQL do swojej bazy przez aplikację.\n\nPowyższa konfiguracja może być dostępna z poziomu kodu aplikacji używając wyrażenia `Yii::$app->db`.\n\n> Info: Plik `config/db.php` będzie załączony do głównej konfiguracji aplikacji `config/web.php`, która określa jak instancja [aplikacji](structure-applications.md) powinna zostać \n> zainicjalizowana.\n> Po więcej informacji zajrzyj do sekcji [Konfiguracje](concept-configurations.md).\n\n\nTworzenie klasy Active Record <span id=\"creating-active-record\"></span>\n-------------------------\n\nDo pobrania i reprezentowania danych z tabeli `country` utwórz pochodną klasę [Active Record](db-active-record.md) o nazwie `Country`, kolejnie zapisz ją w pliku `models/Country.php`.\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass Country extends ActiveRecord\n{\n}\n```\n\nKlasa `Country` rozszerza klasę [[yii\\db\\ActiveRecord|ActiveRecord]]. Nie musisz pisać w niej żadnego kodu! Posiadając tylko powyżej podany kod, Yii odgadnie nazwę powiązanej tabeli \nz nazwy klasy.\n\n> Info: Jeśli nie można dopasować tabeli do nazwy klasy, możesz nadpisać metodę [[yii\\db\\ActiveRecord::tableName()|tableName()]], aby wskazywała na konkretną powiązaną tabelę.\n\nUżywając klasy `Country` możesz w łatwy sposób manipulować danymi z tabeli `country`, tak jak pokazano w poniższych przykładach:\n\n```php\nuse app\\models\\Country;\n\n// pobiera wszystkie wiersze tabeli `country` i porządkuje je według kolumny \"name\"\n$countries = Country::find()->orderBy('name')->all();\n\n// pobiera wiersz, którego kluczem głównym jest \"US\"\n$country = Country::findOne('US');\n\n// wyświetla \"United States\"\necho $country->name;\n\n// modyfikuje nazwę kraju na \"U.S.A.\" i zapisuje go do bazy danych\n$country->name = 'U.S.A.';\n$country->save();\n```\n\n> Info: Active Record jest potężnym narzędziem do dostępu i manipulacji danymi w bazie danych w sposób zorientowany obiektowo.\n> Więcej szczegółowych informacji znajdziesz w sekcji [Active Record](db-active-record.md). Alternatywnie, do łączenia się z bazą danych możesz użyć niskopoziomowej metody dostępu do \n> danych nazwanej [Data Access Objects](db-dao.md).\n\n\nTworzenie akcji <span id=\"creating-action\"></span>\n------------------\n\nAby przedstawić kraje użytkownikowi musisz utworzyć nową akcję. Zamiast umieszczać nową akcję w kontrolerze `site`, tak jak w poprzednich sekcjach, bardziej sensownym rozwiązaniem jest \nutworzenie nowego kontrolera odpowiedzialnego za wszystkie akcje dotyczące danych z tabeli `country`. Nazwij nowy kontroler `CountryController`, a następnie utwórz w nim akcję `index`, \ntak jak na poniższym przykładzie:\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\nuse yii\\data\\Pagination;\nuse app\\models\\Country;\n\nclass CountryController extends Controller\n{\n    public function actionIndex()\n    {\n        $query = Country::find();\n\n        $pagination = new Pagination([\n            'defaultPageSize' => 5,\n            'totalCount' => $query->count(),\n        ]);\n\n        $countries = $query->orderBy('name')\n            ->offset($pagination->offset)\n            ->limit($pagination->limit)\n            ->all();\n\n        return $this->render('index', [\n            'countries' => $countries,\n            'pagination' => $pagination,\n        ]);\n    }\n}\n```\n\nZapisz powyższy kod w pliku `controllers/CountryController.php`.\n\nAkcja `index` wywołuje metodę `Country::find()`, pochodzącą z klasy Active Record, która buduje zapytanie bazodanowe i wyszukuje wszystkich danych z tabeli `country`.\nAby ograniczyć liczbę zwracanych krajów w każdym żądaniu, zapytanie jest stronicowane przy pomocy obiektu [[yii\\data\\Pagination|Pagination]]. Obiekt `Pagination` służy dwóm celom:\n\n* Ustawia klauzule `offset` i `limit` do komend SQL reprezentowanych przez zapytanie tak, aby zwracały tylko jedną stronę na raz (maksymalnie 5 wierszy na stronę),\n* Jest używany w widoku do wyświetlania stronicowania jako listy przycisków z numerami stron, co będzie wyjaśnione w kolejnej sekcji.\n\nNa końcu akcja `index` renderuje widok o nazwie `index`, do którego przekazuje dane krajów oraz informacje o ich stronicowaniu.\n\n\nTworzenie widoku <span id=\"creating-view\"></span>\n---------------\n\nW katalogu `views` utwórz nowy katalog o nazwie `country`. Będzie on używany do przechowywania wszystkich plików widoków renderowanych przez kontroler `country`. W katalogu \n`views/country` utwórz plik o nazwie `index.php` zawierający poniższy kod:\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\LinkPager;\n?>\n<h1>Kraje</h1>\n<ul>\n<?php foreach ($countries as $country): ?>\n    <li>\n        <?= Html::encode(\"{$country->name} ({$country->code})\") ?>:\n        <?= $country->population ?>\n    </li>\n<?php endforeach; ?>\n</ul>\n\n<?= LinkPager::widget(['pagination' => $pagination]) ?>\n```\n\n\nWidok posiada dwie części. Pierwsza część odpowiada za wyświetlenie danych krajów jako nieuporządkowana lista HTML, natomiast druga część renderuje widżet \n[[yii\\widgets\\LinkPager|LinkPager]] na podstawie dostarczonych mu informacji z akcji.\nWidżet `LinkPager` wyświetla listę przycisków z numerami stron. Kliknięcie w którykolwiek z nich zmienia dane z listy na dane odpowiadające wybranej stronie.\n\nSprawdź jak to działa <span id=\"trying-it-out\"></span>\n-------------\n\nAby zobaczyć jak działa powyższy kod, użyj przeglądarki i przejdź pod podany adres URL:\n\n```\nhttps://hostname/index.php?r=country%2Findex\n```\n\n![Lista krajów](images/start-country-list.png)\n\nNa początku zobaczysz stronę pokazującą pięć krajów. Poniżej listy znajduje się paginacja z czterema przyciskami.\nJeśli klikniesz przycisk \"2\", zobaczysz stronę wyświetlającą pięć innych krajów z bazy danych: druga strona wierszy.\nZauważ, że adres URL w przeglądarce również się zmienił na \n\n```\nhttps://hostname/index.php?r=country%2Findex&page=2\n```\n\nZa kulisami, [[yii\\data\\Pagination|Pagination]] dostarcza wszystkich niezbędnych funkcjonalności do stronicowania zbioru danych: \n\n* Początkowo [[yii\\data\\Pagination|Pagination]] prezentuje pierwszą stronę, która odzwierciedla zapytanie \"SELECT\" tabeli `country` z klauzulą `LIMIT 5 OFFSET 0`.\n  W rezultacie pobieranych i wyświetlanych jest pięć pierwszych krajów.\n* Widżet [[yii\\widgets\\LinkPager|LinkPager]] renderuje przyciski stron używając adresów URL tworzonych przez metodę [[yii\\data\\Pagination::createUrl()|createUrl()]]. \n  Adresy zawierają parametr zapytania `page`, który reprezentuje różne numery stron.\n* Jeśli klikniesz przycisk \"2\", zostanie uruchomione i przetworzone nowe żądanie dla route'a `country/index`.\n  [[yii\\data\\Pagination|Pagination]] odczytuje parametr `query` z adresu URL, a następnie ustawia aktualny numer strony na 2.\n  Nowe zapytanie o kraje będzie zawierało klauzulę `LIMIT 5 OFFSET 5` i zwróci pięć kolejnych krajów do wyświetlenia.\n\n\nPodsumowanie <span id=\"summary\"></span>\n-------\n\nW tej sekcji nauczyłeś się jak pracować z bazą danych. Nauczyłeś się również jak pobierać i wyświetlać dane ze stronicowaniem przy pomocy [[yii\\data\\Pagination|Pagination]] oraz \n[[yii\\widgets\\LinkPager|LinkPager]].\n\nW następnej sekcji nauczysz się jak używać potężnego narzędzie do generowania kodu nazwanego [Gii](https://github.com/yiisoft/yii2-gii/blob/master/docs/guide/README.md),\naby pomóc Ci w szybki sposób implementować niektóre powszechnie wymagane funkcjonalności, takie jak operacje CRUD dla zadań z danymi w bazie danych.\nKod, który właśnie napisaliśmy, może być w całości automatycznie wygenerowany w Yii przy użyciu narzędzia Gii.\n"
  },
  {
    "path": "docs/guide-pl/start-forms.md",
    "content": "Praca z formularzami\n==================\n\nTa sekcja opisuje jak utworzyć nową stronę z formularzem pobierającym dane od użytkownika.\nStrona będzie wyświetlała formularz z dwoma polami do uzupełnienia: `nazwa` oraz `email`.\nPo otrzymaniu tych dwóch danych od użytkownika, wyświetlimy z powrotem wprowadzone wartości w celu ich potwierdzenia.\n\nAby to osiągnąć, oprócz utworzenia [akcji](structure-controllers.md) i dwóch [widoków](structure-views.md), będziesz musiał utworzyć [model](structure-models.md).\n\nW tym poradniku nauczysz się jak:\n\n* utworzyć [model](structure-models.md) reprezentujący dane wprowadzone przez użytkownika przez formularz,\n* zadeklarować zasady do sprawdzenia wprowadzonych danych,\n* zbudować formularz HTML w [widoku](structure-views.md).\n\n\nTworzenie modelu <span id=\"creating-model\"></span>\n----------------\n\nDane które pobierzemy od użytkownika będą reprezentowane przez klasę modelu `EntryForm`, która pokazana jest poniżej. Jest ona zapisana w pliku `models/EntryForm.php`.\nPo więcej szczegółów odnośnie konwencji nazewnictwa plików zajrzyj do sekcji[autoładowania klas](concept-autoloading.md)\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse Yii;\nuse yii\\base\\Model;\n\nclass EntryForm extends Model\n{\n    public $name;\n    public $email;\n\n    public function rules()\n    {\n        return [\n            [['name', 'email'], 'required'],\n            ['email', 'email'],\n        ];\n    }\n}\n```\n\nKlasa `EntryForm` rozszerza [[yii\\base\\Model|Model]], podstawową klasę dostarczoną przez Yii, głównie używaną do reprezentowania danych z formularzy.\n\n> Info: [[yii\\base\\Model|Model]] jest używane jako rodzic dla klasy modeli *NIE* powiązanych z tabelą bazy danych.\n> [[yii\\db\\ActiveRecord|ActiveRecord]] jest rodzicem dla klas modeli powiązanych z tabelami bazy danych.\n\nKlasa `EntryForm` zawiera dwa elementy publiczne, `name` oraz `email`, które są używane do przechowania danych wprowadzonych przez użytkownika.\nZawiera również metodę nazwaną `rules()`, która zwraca zestaw zasad do walidacji wprowadzonych danych. Zadeklarowane zasady oznaczają:\n\n* wartości w polach `name` oraz `email są wymagane\n* wartość pola `email` musi być prawidłowym adresem email\n\nJeśli posiadasz uzupełniony obiekt `EntryForm` danymi wprowadzonymi przez użytkownika, możesz wywołać jego funkcję [[yii\\base\\Model::validate()|validate()]], aby uruchomić procedurę \nsprawdzania poprawności danych.\nW przypadku wystąpienia błędów w walidacji, wartość [[yii\\base\\Model::hasErrors|hasErrors]] zostanie ustawiona na `true`. Możesz zobaczyć jakie błędy wystąpiły za pomocą metody \n[[yii\\base\\Model::getErrors()|getErrors()]].\n\n```php\n<?php\n$model = new EntryForm();\n$model->name = 'Qiang';\n$model->email = 'bad';\nif ($model->validate()) {\n    // Dobrze!\n} else {\n    // Źle!\n    // Use $model->getErrors()\n}\n```\n\n\nTworzenie akcji <span id=\"creating-action\"></span>\n------------------\n\nNastępnie musisz utworzyć akcję o nazwie `entry` w kontrolerze `site`,  która użyje Twojego nowego modelu. Proces tworzenia i używania akcji był wytłumaczony w sekcji \n[Witaj świecie](start-hello.md).\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\nuse app\\models\\EntryForm;\n\nclass SiteController extends Controller\n{\n    // ...obecny kod...\n\n    public function actionEntry()\n    {\n        $model = new EntryForm();\n\n        if ($model->load(Yii::$app->request->post()) && $model->validate()) {\n            // walidacja otrzymanych danych w modelu\n\n            // zrób coś sensownego z modelem\n\n            return $this->render('entry-confirm', ['model' => $model]);\n        } else {\n            // w przypadku błędów walidacji wyświetlana jest strona z błędami\n            return $this->render('entry', ['model' => $model]);\n        }\n    }\n}\n```\n\nAkcja tworzy na początku obiekt `EntryForm`. Następnie próbuje uzupełnić model danymi ze zmiennej `$_POST`, dostarczanymi w Yii przez metodę [[yii\\web\\Request::post()|post()]].\nJeśli model został prawidłowo uzupełniony(np. jeśli użytkownik wysłał formularz HTML), akcja wywoła metodę [[yii\\base\\Model::validate()|validate()]], aby upewnić się, że wprowadzone \ndane są prawidłowe.\n\n> Info: Wyrażenie `Yii::$app` reprezentuje instancję [aplikacji](structure-applications.md), która jest globalnie dostępnym singletonem.\n> Jest również [lokatorem usług](concept-service-locator.md), który dostarcza komponenty takie jak `request`, `response` lub `db` do wsparcia specyficznej funkcjonalności.\n> W powyższym kodzie użyty jest komponent `request` aby uzyskać dostęp do danych w zmiennej `$_POST`.\n\nJeśli wszystko jest w porządku, akcja wyrenderuje widok o nazwie `entry-confirm` w celu potwierdzenia prawidłowego przesłania danych przez użytkownika.\nJeśli nie zostały wysłane żadne dane lub dane zawierają błędy, zostanie wyrenderowany widok `entry`, w którym będzie pokazany formularz HTML wraz z wiadomościami błędów walidacji.\n\n> Note: W tym prostym przykładzie po prostu renderujemy stronę z potwierdzeniem prawidłowego przesłania danych. \n> W praktyce powinieneś rozważyć użycie [[yii\\web\\Controller::refresh()|refresh()]] lub [[yii\\web\\Controller::redirect()|redirect()]], aby uniknąć \n> [problemów z ponownym przesłaniem formularza](https://en.wikipedia.org/wiki/Post/Redirect/Get).\n\n\nTworzenie widoku <span id=\"creating-views\"></span>\n--------------\n\nNa koniec utwórz dwa pliki o nazwach `entry-confirm` oraz `entry`. Będą one renderowane przez akcję `entry`, tak jak to przed chwilą opisaliśmy.\n\nWidok `entry-confirm` wyświelta po prostu dane `name` oraz `email`. Powinien być zapisany w pliku `views/site/entry-confirm.php`.\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n<p>Wpisałeś następujące informacje:</p>\n\n<ul>\n    <li><label>Nazwa</label>: <?= Html::encode($model->name) ?></li>\n    <li><label>Email</label>: <?= Html::encode($model->email) ?></li>\n</ul>\n```\n\nWidok `entry` wyświetla formularz HTML. Powinien być zapisany w pliku `views/site/entry.php`.\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n?>\n<?php $form = ActiveForm::begin(); ?>\n\n    <?= $form->field($model, 'name') ?>\n\n    <?= $form->field($model, 'email') ?>\n\n    <div class=\"form-group\">\n        <?= Html::submitButton('Wyślij', ['class' => 'btn btn-primary']) ?>\n    </div>\n\n<?php ActiveForm::end(); ?>\n```\n\nWidok używa potężnego [widżetu](structure-widgets.md) nazwanego [[yii\\widgets\\ActiveForm|ActiveForm]] do budowania formularza HTML.\nMetody `begin()` oraz `end()` renderują odpowiednio otwierające i zamykające tagi formularza. Pomiędzy wywołaniami tych dwóch metod, pola do wprowadzania są tworzone przez metodę \n[[yii\\widgets\\ActiveForm::field()|field()]].\nNastępnie, po polach do wprowadzania danych, wywoływana jest metoda [[yii\\helpers\\Html::submitButton()|submitButton()]] do wygenerowania przycisku \"Wyślij\", który wysyła formularz.\n\n\nPróba <span id=\"trying-it-out\"></span>\n-------------\n\nAby zobaczyć jak to działa, użyj przeglądarki i przejdź pod dany adres:\n\n```\nhttps://hostname/index.php?r=site%2Fentry\n```\n\nZobaczysz stronę wyświetlającą formularz z dwoma polami. Przed każdym polem znajduje się etykieta opisująca to pole. Jeśli klikniesz przycisk \"Wyślij\" nie wpisując żadnych danych, lub \njeśli nie wprowadzisz prawidłowego adresu email, zobaczysz wiadomość błędu wyświeloną pod polem którego ona dotyczy.\n\n![Formularz z błędami walidacji](images/start-form-validation.png)\n\nPo wpisaniu prawidłowej nazwy, adresu email oraz kliknięciu przycisku \"Wyślij\", zobaczysz nową stronę wyświetlającą dane, które właśnie wprowadziłes.\n\n![Potwierdzenie](images/start-entry-confirmation.png)\n\n\n### \"Wyjaśnienie magii\" <span id=\"magic-explained\"></span>\n\nMożesz się zastanawiać jak działa ten formularz HTML, ponieważ wydaje się prawie magicznym to, że wyświetla etykietę do każdego pola oraz wyświetla komunikat błędu, jeśli wprowadzisz \nbłędne dane, bez przeładowania strony.\n\nWstępna walidacja jest wykonywana po stronie klienta używając JavaScriptu, kolejnie dopiero po stronie serwera przez PHP.\n[[yii\\widgets\\ActiveForm|ActiveForm]] potrafi wyodrębnić zasady walidacji, które zadeklarowałeś w `EntryForm`, przekształcić je w wykonywalny kod JavaScript oraz użyć go do walidacji \ndanych. \nJeśli zablokowałeś JavaScript w swojej przeglądarce, walidacja wciąż będzie wykonywana po stronie serwera, jak pokazano w metodzie `actionEntry()`. \nGwarantuje to poprawność danych w każdych okolicznościach.\n\n> Warning: Walidacja po stronie klienta jest opcją, która zapewnia wygodniejszą współpracę aplikacji z użytkownikiem. Walidacja po stronie serwera jest zawsze wymagana, niezależnie, \nczy walidacja po stronie klienta jest włączona, czy też nie.\n\nEtykiety dla pól w formularzu generowane są przy pomocy metody `field()`, używającej nazw właściwości z modelu.\nDla przykładu, etykieta `Name` zostanie wygenerowana dla właściwości `name`.\n\nMożesz dostosowywać etykiety w widoku, używając poniższego kodu:\n\n```php\n<?= $form->field($model, 'name')->label('Your Name') ?>\n<?= $form->field($model, 'email')->label('Your Email') ?>\n```\n\n> Info: Yii dostarcza wiele podobnych widżetów, które pomogą Ci szybko tworzyć złożone i dynamiczne widoki.\n> Tak jak nauczysz się później, pisanie nowego widżetu jest ekstremalnie łatwe. \n> Będziesz mógł przekształcić Twój kod widoku na widżet do wielokrotnego użytku, aby uprościć rozwój swoich widoków w przyszłości.\n\n\nPodsumowanie <span id=\"summary\"></span>\n-------\n\nW tej sekcji poradnika dotknęliśmy każdej części struktury MVC. Nauczyłeś się jak utworzyć klasę modelu, aby reprezentowała dane użytkownika oraz je walidowała.\n\nNauczyłeś się także, jak pobierać dane od użytkowników oraz jak wyświetlać pobrane dane w przeglądarce. \nTo zadanie mogłoby zabrać Ci wiele czasu podczas pisania aplikacji, jednak Yii dostarcza wiele widżetów, które bardzo je ułatwiają.\n\nW następnej sekcji nauczysz się pracy z bazą danych, która jest wymagana w niemalże każdej aplikacji.\n"
  },
  {
    "path": "docs/guide-pl/start-gii.md",
    "content": "Generowanie kodu za pomocą Gii\n========================\n\nTa sekcja opisuje jak używać [Gii](https://github.com/yiisoft/yii2-gii/blob/master/docs/guide/README.md) do automatycznego generowania kodu, który implementuje podstawowe \nfunkcjonalności do aplikacji Web.\nUżywanie Gii do automatycznego generowania kodu jest po prostu kwestią wprowadzenia odpowiednich informacji w formularzach zgodnie z instrukcjami widocznymi na podstronach Gii.\n\nW tym poradniku nauczysz się:\n\n* aktywować Gii w Twojej aplikacji,\n* używać Gii do generowania klas Active Record,\n* używać Gii do generowania kodu implementującego operacje CRUD dla tabel bazy danych,\n* dostosować kod wygenerowany przez Gii,\n\n\nPoczątki z Gii <span id=\"starting-gii\"></span>\n------------\n\n[Gii](https://github.com/yiisoft/yii2-gii/blob/master/docs/guide/README.md) jest [modułem](structure-modules.md) dostarczonym przez Yii. \nMożesz aktywować Gii konfigurując właściwość [[yii\\base\\Application::modules|modules]] aplikacji. Zależnie od tego, jak utworzyłeś swoją aplikację, może się okazać, że poniższy kod jest \njuż zawarty w pliku konfiguracyjnym `config/web.php`:\n\n```php\n$config = [ ... ];\n\nif (YII_ENV_DEV) {\n    $config['bootstrap'][] = 'gii';\n    $config['modules']['gii'] = [\n        'class' => 'yii\\gii\\Module',\n    ];\n}\n```\n\nPowyższa konfiguracja sprawdza, czy aplikacja jest w [środowisku rozwojowym](concept-configurations.md#environment-constants) - jeśli tak, dołącza moduł `gii` określając klasę modułu \n[[yii\\gii\\Module]].\n\nJeśli sprawdzisz [skrypt wejściowy](structure-entry-scripts.md) `web/index.php` Twojej aplikacji, zauważysz linię kodu, która ustawia `YII_ENV` na wartość `dev`.\n\n```php\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n```\n\nDzięki temu Twoja aplikacja ustawiana jest w tryb rozwojowy, co uaktywnia moduł Gii. Możesz teraz uzyskać dostęp do Gii przez przejście pod podany adres URL:\n\n```\nhttps://hostname/index.php?r=gii\n```\n\n> Note: Jeśli próbujesz dostać się do Gii z maszyny innej niż localhost, dostęp domyślnie będzie zablokowany ze względów bezpieczeństwa.\n> Możesz dodać dozwolone adresy IP następująco:\n>\n```php\n'gii' => [\n    'class' => 'yii\\gii\\Module',\n    'allowedIPs' => ['127.0.0.1', '::1', '192.168.0.*', '192.168.178.20'] // adjust this to your needs\n],\n```\n\n![Gii](images/start-gii.png)\n\n\nGenerowanie klasy Active Record <span id=\"generating-ar\"></span>\n---------------------------------\n\nAby użyć Gii do wygenerowania klasy Active Record, wybierz \"Model Generator\" z odnośników na stronie głównej Gii. Następnie uzupełnij formularz następująco:\n\n* Table Name: `country`\n* Model Class: `Country`\n\n![Generator modeli](images/start-gii-model.png)\n\nNastępnie kliknij przycisk \"Preview\". Powinieneś zauważyć na liście plik `models/Country.php`, który zostanie utworzony. Możesz kliknąć w nazwę pliku aby zobaczyć podgląd \njego zawartości.\n\nPodczas używania Gii, jeśli posiadałeś już utworzony plik o tej samej nazwie i będziesz chciał go nadpisać, kliknij w przycisk `diff` obok nazwy pliku, aby zobaczyć różnice w kodzie \npomiędzy wygenerowanym, a już istniejącym plikiem.\n\n![Podgląd generatora modeli](images/start-gii-model-preview.png)\n\nPodczas nadpisywania istniejącego już pliku musisz zaznaczyć opcję \"overwrite\", a następnie kliknąć przycisk \"Generate\". Jeśli tworzysz nowy plik, wystarczy kliknięcie \"Generate\".\n\nNastępnie zobaczysz stronę potwierdzającą, że kod został pomyślnie wygenerowany. Jeśli nadpisywałeś istniejący już plik, zobaczysz również wiadomość o nadpisaniu go nowo wygenerowanym \nkodem.\n\nGenerowanie kodu CRUD <span id=\"generating-crud\"></span>\n--------------------\n\nAkronim CRUD pochodzi od słów *tworzenie*, *odczytywanie*, *aktualizowanie* oraz *usuwanie* (Create-Read-Update-Delete) i reprezentuje cztery podstawowe zadania dotyczące obsługi \ndanych w większości serwisów internetowych.\nAby utworzyć funkcjonalność CRUD używając Gii, wybierz na stronie głównej Gii \"CRUD Generator\". Do przykładu \"country\" uzupełnij formularz następująco:\n\n* Model Class: `app\\models\\Country`\n* Search Model Class: `app\\models\\CountrySearch`\n* Controller Class: `app\\controllers\\CountryController`\n\n![Generator CRUD](images/start-gii-crud.png)\n\nNastępnie kliknij przycisk \"Preview\". Zobaczysz listę plików, które zostaną wygenerowane, tak jak pokazano niżej.\n\n![Podgląd generatora CRUD](images/start-gii-crud-preview.png)\n\nJeśli wcześniej utworzyłeś pliki `controllers/CountryController.php` oraz `views/country/index.php` (w sekcji baz danych tego poradnika), zaznacz opcję \"overwrite\", aby je zastąpić \n(poprzednia wersja nie posiada pełnego wsparcia CRUD). \n\nSprawdzenie w działaniu <span id=\"trying-it-out\"></span>\n-----------------------\n\nAby zobaczyć, jak działa nowo wygenerowany kod, użyj przeglądarki, aby uzyskać dostęp do podanego adresu URL:\n\n```\nhttps://hostname/index.php?r=country%2Findex\n```\n\nZobaczysz tabelę prezentującą kraje z bazy danych. Możesz sortować tabelę, lub filtrować ją przez wpisanie odpowiednich warunków w nagłówkach kolumn.\n\nDla każdego wyświetlanego kraju możesz zobaczyć jego szczegóły, zaktualizować go lub usunąć.\nMożesz również kliknąć przycisk \"Create Country\", aby przejść do formularza tworzenia nowego państwa.\n\n![Siatka danych krajów](images/start-gii-country-grid.png)\n\n![Aktualizacja kraju](images/start-gii-country-update.png)\n\nPoniżej przedstawiamy listę plików wygenerowanych przez Gii, gdybyś chciał zbadać jak zostały zaimplementowane powyższe funkcjonalności (lub je edytować):\n\n* Kontroler: `controllers/CountryController.php`\n* Model: `models/Country.php` and `models/CountrySearch.php`\n* Widok: `views/country/*.php`\n\n> Info: Gii zostało zaprojektowane jako wysoce konfiguralne i rozszerzalne narzędzie przeznaczone do generowania kodu.\n> Prawidłowe używane może znacznie przyspieszyć szybkość tworzenia Twojej aplikacji. Po więcej szczegółów zajrzyj do sekcji \n> [Gii](https://github.com/yiisoft/yii2-gii/blob/master/docs/guide/README.md).\n\n\nPodsumowanie <span id=\"summary\"></span>\n-------\n\nW tej sekcji nauczyłeś się jak używać Gii do wygenerowania kodu, który implementuje pełną funkcjonalność CRUD dla treści zawartch w tabelach bazy danych.\n"
  },
  {
    "path": "docs/guide-pl/start-hello.md",
    "content": "Witaj świecie\n============\n\nTa sekcja opisuje jak utworzyć nową stronę \"Witaj\" w Twojej aplikacji.\nAby to osiągnąć, musisz utworzyć [akcję](structure-controllers.md#creating-actions) i [widok](structure-views.md):\n\n* Aplikacja wyśle żądanie strony web do akcji\n* Następnie akcja włączy widok, który pokazuje użytkownikowi słowo \"Witaj\".\n\nPodczas tego poradnika nauczysz się trzech rzeczy:\n\n1. Jak utworzyć [akcję](structure-controllers.md#creating-actions), która będzie odpowiadać na żądania,\n2. Jak utworzyć [widok](structure-views.md), aby wyeksponować treść odpowiedzi,\n3. Jak aplikacja wysyła żądania do [akcji](structure-controllers.md#creating-actions).\n\nTworzenie akcji <span id=\"creating-action\"></span>\n------------------\n\nDo zadania \"Witaj\" utworzysz [akcję](structure-controllers.md#creating-actions) `say`, która odczytuje parametr `message` z żądania oraz wyświetla tą wiadomość użytkownikowi.\nJeśli żądanie nie dostarczy parametru `message`, akcja wyświetli domyślnie wiadomość \"Witaj\".\n\n> Info: [Akcje](structure-controllers.md#creating-actions) są obiektami, do których użytkownik może bezpośrednio odnieść się, aby je wywołać.\n> Akcje są pogrupowane w [kontrolery](structure-controllers.md). Wynikiem użycia akcji jest odpowiedź, którą otrzyma końcowy użytkownik.\n\nAkcje muszą być deklarowane w [kontrolerach](structure-controllers.md). Dla uproszczenia, możesz zdeklarować akcję `say` w już istniejącym kontrolerze `SiteController`. \nKontroler jest zdefiniowany w klasie `controllers/SiteController.php`. Oto początek nowej akcji:\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    // ...obecny kod...\n\n    public function actionSay($message = 'Hello')\n    {\n        return $this->render('say', ['message' => $message]);\n    }\n}\n```\n\nW powyższym kodzie, akcja `say` jest zdefiniowana jako metoda o nazwie `actionSay` w klasie `SiteController`.\nYii używa prefixu `action` do rozróżnienia metod akcji od zwykłych metod w klasie kontrolera. Nazwa po prefixie `action` kieruje do ID akcji.\n\nPodczas nazywania Twoich akcji powinieneś zrozumieć jak Yii traktuje ID akcji. Odwołanie do ID akcji zawsze występuje z małych liter. \nJeśli ID akcji potrzebuje wielu słów, będą one łączone myślnikami (np. `create-comment`). Nazwy metod akcji są przypisywane do ID akcji przez usunięcie myślników z ID, przekształcenie \npiewszej litery w słowie na dużą literę oraz dodanie prefixu `action`. Dla przykładu akcja o ID `create-comment` odpowiada metodzie akcji o nazwie `actionCreateComment`.\n\nMetoda akcji w naszym przykładzie przyjmuje parametr `$message`, którego wartość domyślna to `\"Hello\"` (w ten sam sposób ustawiasz domyślną wartość dla każdego argumentu funkcji lub \nmetody w PHP).\nKiedy aplikacja otrzymuje żądanie i określa, że akcja `say` jest odpowiedzialna za jego obsługę, aplikacja uzupełni parametr znaleziony w żądaniu. \nInnymi słowy, jeśli żądanie zawiera parametr `message` z wartością `\"Goodbye\"` to do zmiennej `$message` w akcji będzie przypisana ta wartość.\n\nW metodzie akcji wywołana jest funkcja [[yii\\web\\Controller::render()|render()]], która renderuje nam [widok](structure-views.md) pliku o nazwie `say`.\nParametr `message` jest również przekazywany do widoku, co sprawia, że może być w nim użyty. Metoda akcji zwraca wynik renderowania. Wynik ten będzie odebrany przez aplikację oraz \nwyświetlony końcowemu użytownikowi w przeglądarce (jako część kompletnej strony HTML).\n\nTworzenie widoku <span id=\"creating-view\"></span>\n---------------\n\n[Widoki](structure-views.md) są skryptami, które tworzysz w celu wyświetlenia treści odpowiedzi.\nDo zadania \"Hello\" utworzysz widok `say`, który wypisuje parametr `message` otrzymany z metody akcji.\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n<?= Html::encode($message) ?>\n```\n\nWidok `say` powinien być zapisany w pliku `views/site/say.php`. Kiedy wywołana jest metoda [[yii\\web\\Controller::render()|render()]] w akcji, będzie ona szukała pliku PHP nazwanego \nwg schematu `views/ControllerID/ViewName.php`.\n\nZauważ, że w powyższym kodzie parametr `message` jest przetworzony za pomocą metody [[yii\\helpers\\Html::encode()|encode()]] przed wyświetleniem go. Jest to konieczne w przypadku \nparametrów pochodzących od użytkownika, wrażliwych na ataki [XSS](https://en.wikipedia.org/wiki/Cross-site_scripting) przez podanie złośliwego kodu JavaScript.\n\nNaturalnie możesz umieścić więcej zawartości w widoku `say`. Zawartość może zawierać tagi HTML, czysty tekst, a nawet kod PHP.\nTak naprawdę, widok `say` jest tylko skryptem PHP, który jest wywoływany przez metodę [[yii\\web\\Controller::render()|render()]].\nZawartość wyświetlana przez skrypt widoku będzie zwrócona do aplikacji jako wynik odpowiedzi. Aplikacja z kolei przedstawi ten wynik końcowemu użytkownikowi.\n\nPróba <span id=\"trying-it-out\"></span>\n-------------\n\nPo utworzeniu akcji oraz widoku możesz uzyskać dostęp do nowej strony przez przejście pod podany adres URL:\n\n```\nhttps://hostname/index.php?r=site%2Fsay&message=Hello+World\n```\n\n![Witaj świecie](images/start-hello-world.png)\n\nWynikiem wywołania tego adresu jest wyświetlenie napisu \"Hello World\". Strona dzieli ten sam nagłówek i stopkę z innymi stronami aplikacji. \n\nJeśli pominiesz parametr `message` w adresie URL, zobaczysz na stronie tylko \"Hello\". `message` jest przekazywane jako parametr do metody `actionSay` i, jeśli zostanie pominięty, \nzostanie użyta domyślna wartość `\"Hello\"`.\n\n> Info: Nowa strona dzieli ten sam nagłówek i stopkę z innymi stronami, ponieważ metoda [[yii\\web\\Controller::render()|render()]] automatycznie osadza wynik widoku `say` w tak zwanym \n[układzie strony](structure-views.md#layouts), który, w tym przypadku, znajduje się w `views/layouts/main.php`.\n\nParametr `r` w powyższym adresie URL wymaga głębszego objaśnienia. Oznacza on [route'a](runtime-routing.md), identyfikator akcji unikatowy w obrębie aplikacji.\nFormat route'a to `ControllerID/ActionID`. Kiedy aplikacja otrzymuje żądanie, sprawdza ten parametr, a następnie używa części `ControllerID`, aby ustalić, która klasa kontrolera \npowinna zostać zainstancjowana dla przetworzenia tego żądania.\nNastępnie, kontroler używa części `ActionID` do ustalenia, która akcja powinna zostać użyta. W tym przykładzie, route `site/say` będzie odczytany jako klasa kontrolera `SiteController` \noraz akcja `say`.\nW rezultacie zostanie wywołana metoda `SiteController::actionSay()`.\n\n> Info: Tak jak i akcje, kontrolery również posiadają swoje ID, które jednoznacznie identyfikuje je w aplikacji.\n> ID kontrolerów używają tych samych zasad nazewnictwa, co ID akcji. Nazwy klas kontrolerów uzyskiwane są z ID kontrolerów przez usunięcie myślników z ID, zamianę pierwszej litery na \nwielką w każdym słowie oraz dodanie przyrostka `Controller`.\nDla przykładu ID kontrolera `post-comment` odpowiada nazwie klasy kontrolera `PostCommentController`.\n\nPodsumowanie <span id=\"summary\"></span>\n-------\n\nW tej sekcji zobaczyłeś część kontrolerów oraz widoków wzorca architektonicznego MVC.\nUtworzyłeś akcję jako część kontrolera do obsługi specyficznego żądania. Utworzyłeś też widok, który prezentuje zawartość odpowiedzi.\nW tym prostym przykładzie nie został zaangażowany żaden model, ponieważ dane jakimi się posługiwaliśmy były zawarte w parametrze `message`.\n\nNauczyłeś się też czegoś o routingu w Yii, który działa jak most pomiędzy żądaniami użytkownika a akcjami kontrolerów. \n\nW następnej sekcji nauczysz się jak utworzyć model oraz dodać nową stronę zawierającą formularz HTML.\n"
  },
  {
    "path": "docs/guide-pl/start-installation.md",
    "content": "Instalacja Yii\n==============\n\nYii możesz zainstalować na dwa sposoby, korzystając z [Composera](https://getcomposer.org/) lub pobierając plik archiwum.  \nPreferowanym sposobem jest ten pierwszy, ponieważ pozwala na instalację i aktualizację dodatkowych \n[rozszerzeń](structure-extensions.md) oraz samego Yii przy użyciu zaledwie jednej komendy.\n\nStandardowa instalacja Yii skutkuje pobraniem i wstępnym skonfigurowaniem frameworka wraz z szablonem projektu.  \nSzablon projektu jest aplikacją Yii zawierającą podstawowe funkcjonalności, takie jak logowanie, formularz kontaktowy itp.  \nStruktura jego kodu została stworzona w oparciu o zalecany sposób pisania aplikacji opartych na Yii, dlatego może służyć \njako dobry punkt wyjściowy dla stworzenia Twojego bardziej zaawansowanego projektu.\n    \nW tej oraz kilku kolejnych sekcjach opiszemy jak zainstalować Yii z tak zwanym \"podstawowym szablonem projektu\" oraz jak \nzaimplementować w nim nowe funkcjonalności. Oprócz podstawowego, Yii dostarcza również drugi, \n[zaawansowany szablon projektu](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md), \nprzystosowany dla programistów tworzących wielowarstwowe aplikacje.\n\n> Info: Podstawowy szablon projektu jest odpowiedni dla 90% aplikacji webowych. Główną różnicą, w porównaniu do \n  zaawansowanego szablonu projektu, jest organizacja kodu. Jeśli dopiero zaczynasz swoją przygodę z Yii, zalecamy \n  zapoznać się z podstawowym szablonem, ze względu na jego prostotę oraz funkcjonalność.\n\nInstalacja z użyciem Composera <span id=\"installing-via-composer\"></span>\n------------------------------\n\n### Instalacja Composera\n\nJeśli nie posiadasz jeszcze Composera, to możesz go zainstalować korzystając z instrukcji zamieszczonej na stronie \n[getcomposer.org](https://getcomposer.org/download/). W systemach operacyjnych Linux i Mac OS X należy wywołać następujące komendy:\n\n```bash\ncurl -sS https://getcomposer.org/installer | php\nmv composer.phar /usr/local/bin/composer\n```\n\nW systemie Windows należy pobrać i uruchomić [Composer-Setup.exe](https://getcomposer.org/Composer-Setup.exe).\n\nW przypadku napotkania jakichkolwiek problemów należy zapoznać się z \n[sekcją Rozwiązywania problemów w dokumentacji Composera](https://getcomposer.org/doc/articles/troubleshooting.md).  \nJeśli dopiero rozpoczynasz przygodę z Composerem, zalecamy przeczytanie przynajmniej [sekcji Podstaw użycia](https://getcomposer.org/doc/01-basic-usage.md) \nw dokumentacji Composera.\n\nW tym przewodniku zakładamy, że Composer został zainstalowany [globalnie](https://getcomposer.org/doc/00-intro.md#globally), \ndzięki czemu jest dostępny z użyciem komendy `composer`. Jeśli jednak zamiast tego używasz pliku `composer.phar` w lokalnym folderze, \npamiętaj, żeby odpowiednio zmodyfikować podane tu przykładowe komendy.\n\nJeśli jesteś już posiadaczem Composera, upewnij się, że jest on zaktualizowany do najnowszej wersji (komenda `composer self-update`).\n\n> Note: Podczas instalacji Yii, Composer będzie potrzebował pobrać sporo informacji z API serwisu Github. \n  Ilość zapytań zależy od liczby powiązanych wtyczek, rozszerzeń i modułów, których wymaga Twoja aplikacja, i może być \n  większa niż **limit zapytań API GitHuba**. Jeśli faktycznie tak będzie, Composer może poprosić o Twoje dane logowania \n  w serwisie Github, aby uzyskać token dostępowy API Githuba. Przy szybkim łączu napotkanie limitu może nastąpić szybciej \n  niż Composer jest w stanie obsłużyć zapytania, zatem zalecane jest skonfigurowanie tokenu dostępowego przed instalacją Yii.  \n  Instrukcja opisująca jak tego dokonać znajduje się w  \n  [dokumentacji Composera dotyczącej tokenów API Githuba](https://getcomposer.org/doc/articles/troubleshooting.md#api-rate-limit-and-oauth-tokens).\n\n### Installing Yii\n\nTeraz możesz przejść już do instalacji samego Yii, wywołując poniższe komendy w katalogu dostępnym z poziomu sieci web:\n\n```bash\ncomposer create-project --prefer-dist yiisoft/yii2-app-basic basic\n```\n\nKomenda ta zainstaluje najnowszą stabilną wersję szablonu aplikacji Yii w katalogu `basic`. Możesz oczywiście wybrać \ninną nazwę.\n\n> Info: Jeśli komenda `composer create-project` zwróci błąd, sprawdź, czy przypadkiem nie jest on już opisany\n  w [dokumentacji Composera w sekcji Rozwiązywania problemów](https://getcomposer.org/doc/articles/troubleshooting.md). \n  Kiedy uporasz się już z błędem, możesz wznowić przerwaną instalację uruchamiając komendę `composer update` w folderze `basic`.\n\n> Tip: Jeśli chcesz zainstalować najnowszą wersję deweloperską Yii, użyj poniższej komendy, która dodaje \n> [opcję stabilności](https://getcomposer.org/doc/04-schema.md#minimum-stability):\n>\n> ```bash\n> composer create-project --prefer-dist --stability=dev yiisoft/yii2-app-basic basic\n> ```\n>\n> Pamiętaj, że wersja deweloperska Yii nie powinna być używana w wersjach produkcyjnych Twojej aplikacji, ponieważ mogą \n> wystąpić w niej niespodziewane błędy.\n\nInstalacja z pliku archiwum <span id=\"installing-from-archive-file\"></span>\n---------------------------\n\nInstalacja Yii z pliku archiwum składa się z trzech kroków:\n\n1. Pobranie pliku archiwum z [yiiframework.com](https://www.yiiframework.com/download/).\n2. Rozpakowanie pliku archiwum do katalogu dostępnego z poziomu sieci web.\n3. Zmodyfikowanie pliku `config/web.php` przez dodanie sekretnego klucza do elementu konfiguracji `cookieValidationKey`\n   (jest to wykonywane automatycznie, jeśli instalujesz Yii używając Composera):\n\n   ```php\n   // !!! wprowadź sekretny klucz tutaj - jest to wymagane do walidacji ciasteczek\n   'cookieValidationKey' => 'enter your secret key here',\n   ```\n\nInne opcje instalacji <span id=\"other-installation-options\"></span>\n---------------------\n\nPowyższe instrukcje pokazują, jak zainstalować Yii oraz utworzyć podstawową, gotową do uruchomienia aplikację web.\nTo podejście jest dobrym punktem startowym dla większości projektów, zarówno małych jak i dużych. Jest to szczególnie \nkorzystne, gdy zaczynasz naukę Yii.\n\nDostępne są również inne opcje instalacji:\n\n* Jeśli chcesz zainstalować wyłącznie framework i samemu zbudować aplikację, zapoznaj się z rozdziałem \n  [Tworzenie aplikacji od podstaw](tutorial-start-from-scratch.md).\n* Jeśli chcesz utworzyć bardziej zaawansowaną aplikację, przystosowaną do programowania dla wielu środowisk, \n  powinienieś rozważyć instalację \n  [zaawansowanego szablonu aplikacji](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md).\n\n\nInstalowanie zasobów <span id=\"installing-assets\"></span>\n--------------------\n\nYii używa menadżerów pakietów [Bower](https://bower.io/) i/lub [NPM](https://www.npmjs.com/) do instalacji bibliotek \nzasobów (CSS i JavaScript). Proces pobierania tych bibliotek korzysta z Composera, pozwalając na rozwiązywanie zależności \npakietów PHP i CSS/JavaScript w tym samym czasie, za pomocą serwisu [asset-packagist.org](https://asset-packagist.org) \nlub wtyczki [composer asset plugin](https://github.com/fxpio/composer-asset-plugin).  \nPo więcej informacji sięgnij do sekcji [dokumentacji Zasobów](structure-assets.md).\n\nMożesz, rzecz jasna, również zarządzać swoimi zasobami za pomocą natywnego klienta Bower/NPM, korzystać z CDN, albo też \ncałkowicie zrezygnować z instalacji zasobów. Aby zablokować automatyczne pobieranie zasobów podczas używania Composera, \ndodaj poniższe linie w pliku 'composer.json':\n\n```json\n\"replace\": {\n    \"bower-asset/jquery\": \">=1.11.0\",\n    \"bower-asset/inputmask\": \">=3.2.0\",\n    \"bower-asset/punycode\": \">=1.3.0\",\n    \"bower-asset/yii2-pjax\": \">=2.0.0\"\n},\n```\n\n> Note: w przypadku zablokowania instalacji zasobów przez Composera, odpowiedzialność za ich instalację i rozwiązywanie \n  zależności spada na Ciebie. Przygotuj się na potencjalne niezgodności w plikach zasobów pochodzących z różnych rozszerzeń.\n\n\n\nWeryfikacja instalacji <span id=\"verifying-installation\"></span>\n----------------------\n\nPo zakończeniu instalacji, skonfiguruj swój serwer (zobacz następną sekcję) lub użyj \n[wbudowanego serwera PHP](https://www.php.net/manual/en/features.commandline.webserver.php), \nuruchamiając poniższą komendę w konsoli z poziomu folderu `web` w projekcie:\n \n```bash\nphp yii serve\n```\n\n> Note: Domyślnym portem, na którym serwer HTTP nasłuchuje, jest 8080. Jeśli jednak ten port jest już w użyciu lub też \n  chcesz obsłużyć wiele aplikacji w ten sposób, możesz podać inny numer portu, dodając argument --port:\n\n```bash\nphp yii serve --port=8888\n```\n\nMożesz teraz użyć swojej przeglądarki, aby uzyskać dostęp do zainstalowanej aplikacji Yii przechodząc pod adres:\n\n```\nhttp://localhost:8080/\n```\n\n![Poprawna instalacja Yii](images/start-app-installed.png)\n\nPowinienieś zobaczyć stronę z napisem \"Congratulations!\" (\"Gratulacje!\"). Jeśli nie, sprawdź, czy zainstalowane elementy \nśrodowiska spełniają wymagania Yii. Możesz sprawdzić minimalne wymagania na dwa sposoby:\n\n* Skopiuj plik `/requirements.php` do `/web/requirements.php`, a następnie przejdź do przeglądarki i uruchom go \n  przechodząc pod adres `http://localhost/requirements.php`\n* Lub też uruchom następujące komendy:\n\n  ```bash\n  cd basic\n  php requirements.php\n  ```\n\nPowinienieś skonfigurować swoją instalację PHP tak, aby spełniała minimalne wymogi Yii. Najważniejszym z nich jest \nposiadanie PHP w wersji 5.4 lub wyższej. Powinienieś również zainstalować \n[rozszerzenie PDO](https://www.php.net/manual/en/pdo.installation.php) oraz odpowiedni sterownik bazy danych \n(np. `pdo_mysql` dla bazy danych MySQL), jeśli Twoja aplikacja potrzebuje bazy danych.\n\n\nKonfigurowanie serwerów WWW <span id=\"configuring-web-servers\"></span>\n---------------------------\n\n> Info: Możesz pominąć tą sekcję, jeśli tylko testujesz Yii, bez zamiaru zamieszczania aplikacji na serwerze produkcyjnym.\n\nAplikacja zainstalowana według powyższych instrukcji powinna działać bezproblemowo zarówno na \n[serwerze HTTP Apache](https://httpd.apache.org) jak i [serwerze HTTP Nginx](https://nginx.org), na systemie \noperacyjnym Windows, Mac OS X oraz Linux, posiadającym zainstalowane PHP 5.4 lub nowsze. Yii 2.0 jest również kompatybilne \nz [facebookowym HHVM](https://hhvm.com), są jednak przypadki, gdzie Yii zachowuje się inaczej w HHVM niż w natywnym PHP, \ndlatego powinieneś zachować szczególną ostrożność używając HHVM.\n\nNa serwerze produkcyjnym możesz skonfigurować swój host tak, aby aplikacja była dostępna pod adresem \n`https://www.example.com/index.php` zamiast `https://www.example.com/basic/web/index.php`. Taka konfiguracja wymaga wskazania \ngłównego katalogu serwera jako katalogu `basic/web`. Jeśli chcesz ukryć `index.php` w adresie URL, skorzystaj z informacji \nopisanych w dziale [routing i tworzenie adresów URL](runtime-routing.md).  \nW tej sekcji dowiesz się, jak skonfigurować Twój serwer Apache lub Nginx, aby osiągnąć te cele.\n\n> Info: Ustawiając `basic/web` jako główny katalog serwera, unikasz niechcianego dostępu użytkowników końcowych do \n  prywatnego kodu oraz wrażliwych plików aplikacji, które są przechowywane w katalogu `basic`. Zablokowanie dostępu do \n  tych folderów jest jednym z wymogów bezpieczeństwa aplikacji.\n\n> Info: W przypadku, gdy Twoja aplikacja działa na wspólnym środowisku hostingowym, gdzie nie masz dostępu do modyfikowania \n  konfiguracji serwera, nadal możesz zmienić strukturę aplikacji dla lepszej ochrony. Po więcej informacji zajrzyj do \n  działu [Współdzielone środowisko hostingowe](tutorial-shared-hosting.md).\n\n### Zalecane ustawienia Apache <span id=\"recommended-apache-configuration\"></span>\n\nUżyj następującej konfiguracji serwera Apache w pliku `httpd.conf` lub w konfiguracji wirtualnego hosta.\nPamiętaj, że musisz zamienić ścieżkę `path/to/basic/web` na aktualną ścieżkę do `basic/web` Twojej aplikacji.\n\n```apache\n# Ustaw główny katalog na \"basic/web\"\nDocumentRoot \"path/to/basic/web\"\n\n<Directory \"path/to/basic/web\">\n    # użyj mod_rewrite do wsparcia \"ładnych URLi\"\n    RewriteEngine on\n\n    # jeśli $showScriptName jest ustawione na false w UrlManager, nie pozwalaj na dostęp do URLi za pomocą nazwy skryptu\n    RewriteRule ^index.php/ - [L,R=404]\n\n    # Jeśli katalog lub plik istnieje, użyj go bezpośrednio\n    RewriteCond %{REQUEST_FILENAME} !-f\n    RewriteCond %{REQUEST_FILENAME} !-d\n\n    # W innym przypadku przekieruj żądanie na index.php\n    RewriteRule . index.php\n\n    # ...inne ustawienia...\n</Directory>\n```\n\n### Zalecane ustawienia Nginx <span id=\"recommended-nginx-configuration\"></span>\n\nAby użyć [Nginx](https://wiki.nginx.org/) powinienieś zainstalować PHP jako [FPM SAPI](https://www.php.net/install.fpm).\nMożesz użyć przedstawionej poniżej konfiguracji Nginx, zastępując jedynie ścieżkę `path/to/basic/web` aktualną ścieżką \ndo `basic/web` Twojej aplikacji oraz `mysite.test` aktualną nazwą hosta.\n\n```nginx\nserver {\n    charset utf-8;\n    client_max_body_size 128M;\n\n    listen 80; ## nasłuchuj ipv4\n    #listen [::]:80 default_server ipv6only=on; ## nasłuchuj ipv6\n\n    server_name mysite.test;\n    root        /path/to/basic/web;\n    index       index.php;\n\n    access_log  /path/to/basic/log/access.log;\n    error_log   /path/to/basic/log/error.log;\n\n    location / {\n        # Przekieruj wszystko co nie jest prawdziwym plikiem na index.php\n        try_files $uri $uri/ /index.php$is_args$args;\n    }\n\n    # odkomentuj poniższe aby uniknąć przetwarzania żądań do nieistniejących plików przez Yii\n    #location ~ \\.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ {\n    #    try_files $uri =404;\n    #}\n    #error_page 404 /404.html;\n\n    # zablokuj dostęp do plików php w folderze /assets\n    location ~ ^/assets/.*\\.php$ {\n        deny all;\n    }\n\n    location ~ \\.php$ {\n        include fastcgi_params;\n        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;\n        fastcgi_pass 127.0.0.1:9000;\n        #fastcgi_pass unix:/var/run/php5-fpm.sock;\n        try_files $uri =404;\n    }\n\n    location ~* /\\. {\n        deny all;\n    }\n}\n```\n\nW przypadku użycia tej konfiguracji, powinienieś ustawić również `cgi.fix_pathinfo=0` w pliku `php.ini`, \naby zapobiec wielu zbędnym wywołaniom `stat()`.\n\nNależy również pamiętać, że podczas pracy na serwerze HTTPS musisz dodać `fastcgi_param HTTPS on;`, \naby Yii prawidłowo wykrywało, że połączenie jest bezpieczne.\n\n### Zalecane ustawienia NGINX Unit <span id=\"recommended-nginx-unit-configuration\"></span>\n\nMożesz uruchomić aplikacje oparte na Yii korzystając z [NGINX Unit](https://unit.nginx.org/) z modułem języka PHP. Poniżej \nznajdziesz przykładową konfigurację.\n\n```json\n{\n    \"listeners\": {\n        \"*:80\": {\n            \"pass\": \"routes/yii\"\n        }\n    },\n\n    \"routes\": {\n        \"yii\": [\n            {\n                \"match\": {\n                    \"uri\": [\n                        \"!/assets/*\",\n                        \"*.php\",\n                        \"*.php/*\"\n                    ]\n                },\n\n                \"action\": {\n                    \"pass\": \"applications/yii/direct\"\n                }\n            },\n            {\n                \"action\": {\n                    \"share\": \"/path/to/app/web/\",\n                    \"fallback\": {\n                        \"pass\": \"applications/yii/index\"\n                    }\n                }\n            }\n        ]\n    },\n\n    \"applications\": {\n        \"yii\": {\n            \"type\": \"php\",\n            \"user\": \"www-data\",\n            \"targets\": {\n                \"direct\": {\n                    \"root\": \"/path/to/app/web/\"\n                },\n\n                \"index\": {\n                    \"root\": \"/path/to/app/web/\",\n                    \"script\": \"index.php\"\n                }\n            }\n        }\n    }\n}\n```\n\nMożesz również [skonfigurować](https://unit.nginx.org/configuration/#php) swoje środowisko PHP lub przygotować \nspersonalizowany plik `php.ini` w tej samej konfiguracji.\n\n### Konfiguracja IIS <span id=\"iis-configuration\"></span>\n\nZalecane jest hostowanie aplikacji na wirtualnym hoście (strona Web), gdzie podstawowa ścieżka dokumentów wskazuje na folder \n`path/to/app/web` i strona Web jest skonfigurowana do uruchamiania PHP. W folderze `web` musisz umieścić plik `web.config` \n(`path/to/app/web/web.config`). Zawartość tego pliku powinna wyglądać jak poniżej:\n\n```xml\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration>\n<system.webServer>\n<directoryBrowse enabled=\"false\" />\n  <rewrite>\n    <rules>\n      <rule name=\"Hide Yii Index\" stopProcessing=\"true\">\n        <match url=\".\" ignoreCase=\"false\" />\n        <conditions>\n        <add input=\"{REQUEST_FILENAME}\" matchType=\"IsFile\" \n              ignoreCase=\"false\" negate=\"true\" />\n        <add input=\"{REQUEST_FILENAME}\" matchType=\"IsDirectory\" \n              ignoreCase=\"false\" negate=\"true\" />\n        </conditions>\n        <action type=\"Rewrite\" url=\"index.php\" appendQueryString=\"true\" />\n      </rule> \n    </rules>\n  </rewrite>\n</system.webServer>\n</configuration>\n```\nSprawdź również poniższe oficjalne poradniki firmy Microsoft, opisujące jak poprawnie skonfigurować PHP dla IIS:\n1. [Jak uruchomić swoją pierwszą stronę Web na IIS](https://docs.microsoft.com/en-us/iis/manage/creating-websites/scenario-build-a-static-website-on-iis)\n2. [Konfiguracja strony Web PHP dla IIS](https://docs.microsoft.com/en-us/iis/application-frameworks/scenario-build-a-php-website-on-iis/configure-a-php-website-on-iis)\n"
  },
  {
    "path": "docs/guide-pl/start-looking-ahead.md",
    "content": "Dalsze kroki\n=============\n\nJeśli przebyłeś już cały dział \"Pierwsze kroki\", powinieneś mieć utworzoną kompletną aplikację Yii. Podczas tego procesu, nauczyłeś się jak zaimplementować zwykle niezbędne \nfunkcjonalności, takie jak zbieranie danych od użytkownika wykorzystując formularz HTML, odczytywanie danych z bazy danych oraz wyświetlanie ich wraz ze stronicowaniem.\nNauczyłeś się również jak korzystać z [Gii](https://github.com/yiisoft/yii2-gii/blob/master/docs/guide-pl/README.md) i generować kod automatycznie. Używanie Gii zmienia proces \nrozwoju Twojej aplikacji w tak proste zadanie, jak uzupełnienie formularza.\n\nTa sekcja podsumuje dostępne zasoby Yii, które pomogą Ci być bardziej produktywnym podczas używania frameworka.\n\n* Dokumentacja\n    - [Przewodnik po Yii](https://www.yiiframework.com/doc-2.0/guide-README.html):\n      Jak sama nazwa wskazuje, jest to przewodnik, który opisuje jak działa Yii oraz dostarcza generalnych porad o użyciu Yii.\n      Jest jednym z najważniejszych poradników, które powinieneś przeczytać przed napisaniem kodu w Yii.\n    - [Dokumentacja klas](https://www.yiiframework.com/doc-2.0/index.html):\n      Określa korzystanie z każdej klasy dostarczonej przez Yii. Powinna być stosowana głównie przy pisaniu kodu oraz chęci zrozumienia, jak działa poszczególna klasa, metoda lub \n      właściwość.\n      Najlepiej używać jej po zrozumieniu działania całego frameworka.\n    - [Artykuły Wiki](https://www.yiiframework.com/wiki/?tag=yii2):\n      Artykuły w Wiki są pisane przez użytkowników Yii i są oparte na ich doświadczeniu. Większość z nich jest pisana w stylu przepisów kucharskich, pokazując jak rozwiązać poszczególne \n      problemy używając Yii.\n      Chociaż jakość tych artykułów może nie być tak dobra jak przewodnik, są one bardzo użyteczne oraz mogą dostarczać gotowych do użycia rozwiązań.\n    - [Książki](https://www.yiiframework.com/books)\n* [Rozszerzenia](https://www.yiiframework.com/extensions/):\n  Yii może pochwalić się biblioteką tysięcy rozszerzeń użytkowników, które mogą być łatwo zainstalowane w Twojej aplikacji, dzięki czemu tworzenie Twojej aplikacji jest jeszcze prostsze \n  i szybsze.\n* Społeczność\n    - Forum: <https://forum.yiiframework.com/>\n    - IRC chat: Kanał #yii w sieci Libera (<ircs://irc.libera.chat:6697/yii>)\n    - GitHub: <https://github.com/yiisoft/yii2>\n    - Facebook: <https://www.facebook.com/groups/yiitalk/>\n    - Twitter: <https://twitter.com/yiiframework>\n    - LinkedIn: <https://www.linkedin.com/groups/yii-framework-1483367>\n    - StackOverflow: <https://stackoverflow.com/questions/tagged/yii2>\n"
  },
  {
    "path": "docs/guide-pl/start-workflow.md",
    "content": "Uruchamianie aplikacji\n====================\n\nPo zainstalowaniu Yii posiadasz działającą aplikację Yii dostępną pod adresem `https://hostname/basic/web/index.php` lub `https://hostname/index.php`, zależnie od Twojej konfiguracji.\nTa sekcja wprowadzi Cię do wbudowanych funkcjonalności aplikacji, pokaże jak zorganizowany jest jej kod oraz jak aplikacja obsługuje żądania.\n\n> Info: Dla uproszczenia zakładamy, że ustawiłeś główny katalog serwera na `basic/web`, według poradnika \"Instalacja Yii\", oraz skonfigurowałeś adres URL tak, aby Twoja aplikacja była \n> dostępna pod adresem `https://hostname/index.php`.\n> Dla Twoich potrzeb dostosuj odpowiednio adres URL w naszych opisach.\n\nNależy pamiętać, że w przeciwieństwie do samego frameworka, po zainstalowaniu szablonu projektu należy on w całości do Ciebie. Możesz dowolnie dodawać, modyfikować lub usuwać kod, \nzależnie od Twoich potrzeb.\n  \nFunkcjonalność <span id=\"functionality\"></span>\n-------------\n\nZainstalowana podstawowa aplikacja posiada cztery strony:\n\n* stronę główną, która jest wyświetlana przy wywołaniu adresu `https://hostname/index.php`,\n* strona informacyjna `About`,\n* strona kontaktowa `Contact`, gdzie wyświetlany jest formularz kontaktowy, pozwalający użytkownikowi skontaktować się z Tobą przez email,\n* strona logowania `Login`, gdzie wyświetlany jest formularz logowania, który może być użyty do uwierzytelniania użytkowników. Zaloguj się danymi \"admin/admin\", przez co pozycja \n  `Login` z menu zamieni się na `Logout`.\n\nWszystkie te strony posiadają wspólne nagłówek i stopkę. Nagłówek zawiera główne menu pozwalające na nawigację po innych stronach.\n\nPowinieneś również widzieć pasek narzędzi na dole okna przeglądarki.\nJest to użyteczne [narzędzie do debugowania](https://github.com/yiisoft/yii2-debug/blob/master/docs/guide/README.md) dostarczone przez Yii, zapisujące i wyświetlające wiele informacji, \ntakich jak wiadomości logów, statusy odpowiedzi, zapytania do baz danych i wiele innych.\n\nDodatkowo do aplikacji Web dostarczono skrypt konsolowy nazwany `yii`, który jest ulokowany w głównym katalogu aplikacji.\nSkrypt może być użyty do uruchomienia w tle zadań dla aplikacji, które są opisane w sekcji [Komendy konsolowe](tutorial-console.md).\n\nStruktura aplikacji <span id=\"application-structure\"></span>\n---------------------\n\nNajważniejsze katalogi oraz pliki w Twojej aplikacji to (zakładając, że główny katalog aplikacji to `basic`):\n\n```\nbasic/                  bazowa ścieżka aplikacji\n    composer.json       plik używany przez Composer, opisuje informacje paczek\n    config/             zawiera wszystkie konfiguracje, w tym aplikacji\n        console.php     konfiguracja konsoli aplikacji\n        web.php         konfiguracja aplikacji Web\n    commands/           zawiera klasy komend konsoli\n    controllers/        zawiera klasy kontrolerów\n    models/             zawiera klasy modeli\n    runtime/            zawiera pliki wygenerowane przez Yii podczas pracy, takie jak logi i pliki cache\n    vendor/             zawiera zainstalowane paczki Composer'a, w tym framework Yii\n    views/              zawiera pliki widoków\n    web/                ścieżka aplikacji Web, zawiera dostępne publicznie pliki\n        assets/         zawiera opublikowane przez Yii pliki zasobów (javascript oraz css)\n        index.php       skrypt wejściowy dla aplikacji\n    yii                 skrypt wykonujący komendy konsolowe Yii\n```\n\nOgólnie pliki aplikacji mogą zostać podzielone na dwa typy: pliki w katalogu `basic/web` oraz pliki w innych katalogach.\nDostęp do pierwszego typu można uzyskać przez HTTP (np. przez przeglądarkę), podczas gdy reszta nie może, i nie powinna być, dostępna publicznie.\n\nYii implementuje wzór architektoniczny [model-widok-kontroler (MVC)](https://wikipedia.org/wiki/Model-view-controller), który jest odzwierciedleniem przedstawionej wyżej organizacji \nkatalogów.\nKatalog `models` zawiera wszystkie [klasy modeli](structure-models.md), katalog `views` zawiera wszystkie [skrypty widoków](structure-views.md) oraz katalog `controllers` zawiera \nwszystkie [klasy kontrolerów](structure-controllers.md).\n\nPoniższy schemat pokazuje statyczną strukturę aplikacji.\n\n![Statyczna struktura aplikacji](images/application-structure.png)\n\nKażda aplikacja zawiera skrypt wejściowy `web/index.php`, który jest jedynym publicznie dostępnym skryptem PHP w aplikacji.\nSkrypt wejściowy pobiera przychodzące żądanie i tworzy instancję [aplikacji](structure-applications.md) do przetworzenia tego żądania.\n[Aplikacja](structure-applications.md) obsługuje żądanie z pomocą [komponentów](concept-components.md), po czym wysyła żądanie do elementów MVC. \n[Widżety](structure-widgets.md) są używane w [widokach](structure-views.md), aby pomóc zbudować złożone i dynamiczne elementy interfejsu użytkownika.\n\n\n\nCykl życia żądania <span id=\"request-lifecycle\"></span>\n-----------------\n\nPoniższy schemat pokazuje jak aplikacja przetwarza żądania.\n\n![Cykl życia żądania](images/request-lifecycle.png)\n\n1. Użytkownik tworzy zapytanie do [skryptu wejściowego](structure-entry-scripts.md) `web/index.php`.\n2. Skrypy wejściowy ładuje [konfigurację](concept-configurations.md) aplikacji oraz tworzy instancję [aplikacji](structure-applications.md) w celu przetworzenia żądania.\n3. Aplikacja obsługuje żądanie [route'a](runtime-routing.md) z pomocą komponentu [żądania](runtime-requests.md) aplikacji.\n4. Aplikacja tworzy instancję [kontrolera](structure-controllers.md) do obsługi żądania.\n5. Kontroler tworzy instancję [akcji](structure-controllers.md) i wykonuje filtrowanie dla akcji.\n6. Jeśli warunek dowolnego z filtrów nie jest spełniony, akcja jest zatrzymana.\n7. W przeciwnym wypadku wywoływana jest akcja.\n8. Akcja wczytuje model danych, prawdopodobnie z bazy danych.\n9. Akcja renderuje widok, dostarczając mu model danych.\n10. Wynik zwracany jest do komponentu [odpowiedzi](runtime-responses.md) aplikacji.\n11. Komponent odpowiedzi wysyła wynik do przeglądarki użytkownika.\n"
  },
  {
    "path": "docs/guide-pl/structure-application-components.md",
    "content": "Komponenty aplikacji\n======================\n\nAplikacje są [lokatorami usług](concept-service-locator.md). Posiadają one zestawy *komponentów aplikacji*, \nktóre zajmują się dostarczaniem różnych serwisów do obsługi żądań. Dla przykładu,\nkomponent `urlManager` jest odpowiedzialny za przekierowania żądań do odpowiednich kontrolerów, \nkomponent `db` dostarcza serwisy powiązane z bazami danych, itp.\n\n\nKażdy komponent aplikacji posiada unikalne ID identyfikujące go w całej aplikacji.\nMożesz dostać się do tego komponentu przez wyrażenie:\n\n```php\n\\Yii::$app->componentID\n```\n\nDla przykładu, możesz użyć `\\Yii::$app->db` do uzyskania [[yii\\db\\Connection|połączenia z bazą danych]] lub `\\Yii::$app->cache` do uzyskania \n[[yii\\caching\\Cache|dostępu do pamięci podręcznej]] zarejestrowanej w aplikacji.\n\nKomponent jest tworzony przy pierwszym jego wywołaniu przez powyższe wyrażenie, każde kolejne wywołanie zwróci tą samą instancję tego komponentu.\n\nKomponentami aplikacji może być każdy objekt. Możesz je zarejestrować przez skonfigurowanie parametru [[yii\\base\\Application::components|components]] w \n[konfiguracji aplikacji](structure-applications.md#application-configurations).\nDla przykładu:\n\n```php\n[\n    'components' => [\n        // rejestracja komponentu \"cache\" przy użyciu nazwy klasy\n        'cache' => 'yii\\caching\\ApcCache',\n\n        // rejestracja komponentu \"db\" przy użyciu tablicy konfiguracyjnej\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=demo',\n            'username' => 'root',\n            'password' => '',\n        ],\n\n        // rejestracja komponentu \"search\" przy użyciu funkcji anonimowej\n        'search' => function () {\n            return new app\\components\\SolrService;\n        },\n    ],\n]\n```\n\n> Info: Możesz zarejestrować tak wiele komponentów jak chcesz, jednak powinieneś robić to rozważnie.\n> Komponenty aplikacji są podobne do zmiennych globalnych. \n> Używanie zbyt wielu komponentów może potencjalnie uczynić Twój kod trudniejszym do testowania i utrzymania.\n> W wielu przypadkach możesz po prostu utworzyć lokalny komponent i użyć go, kiedy jest to konieczne.\n\n\n## Bootstrapping komponentów <span id=\"bootstrapping-components\"></span>\n\nTak, jak było wspomniane wcześniej, komponent aplikacji zostanie zinstancjowany tylko w momencie pierwszego wywołania.\nCzasami jednak chcemy, aby komponent został zainstancjowany dla każdego żądania, nawet jeśli nie jest bezpośrednio wywoływany.\nAby to osiągnąć, możesz wylistować ID komponentów we właściwości [[yii\\base\\Application::bootstrap|bootstrap]] aplikacji.\n\n\nDla przykładu, następująca konfiguracja aplikacji zapewnia załadowanie komponentu `log` przy każdym żądaniu:\n\n```php\n[\n    'bootstrap' => [\n        'log',\n    ],\n    'components' => [\n        'log' => [\n            // konfiguracja komponentu `log`\n        ],\n    ],\n]\n```\n\n\n## Podstawowe komponenty aplikacji <span id=\"core-application-components\"></span>\n\nYii posiada podstawowe komponenty aplikacji ze stałymi ID oraz domyślną ich konfiguracją. Dla przykładu,\nkomponent [[yii\\web\\Application::request|request]] jest używany do zbierania informacji na temat żądania użytkownika \noraz przekazanie go do [route'a](runtime-routing.md); [[yii\\base\\Application::db|db]] reprezentuje \npołączenie z bazą danych, dzięki któremu możesz wykonywać zapytania do bazy.\nZ pomocą tych podstawowych komponentów aplikacja jest w stanie obsłużyć żądania użytkowników.\n\nPoniżej znajduje się lista predefiniowanych podstawowych komponentów aplikacji. Możesz je konfigurować lub zmieniać,\ntak jak z normalnymi komponentami. Podczas konfigurowania podstawowych komponentów aplikacji, w przypadku nie podania klasy, \nzostanie użyta klasa domyślna.\n\n* [[yii\\web\\AssetManager|assetManager]]: zarządzanie zasobami oraz ich publikacja.\n  Po więcej informacji zajrzyj do sekcji [Assets](structure-assets.md).\n* [[yii\\db\\Connection|db]]: reprezentuje połączenie z bazą danych, dzięki której możliwe jest wykonywanie zapytań.\n  Konfigurując ten komponent musisz określić klasę komponentu, tak samo jak inne wymagane właściwości, np. [[yii\\db\\Connection::dsn|dsn]].\n  Po więcej informacji zajrzyj do sekcji [Obiekty dostępu do danych (DAO)](db-dao.md).\n* [[yii\\base\\Application::errorHandler|errorHandler]]: obsługuje błędy oraz wyjątki PHP.\n  Po więcej informacji zajrzyj do sekcji [Obsługa błędów](runtime-handling-errors.md).\n* [[yii\\i18n\\Formatter|formatter]]: formatuje dane wyświetlane użytkownikom. Dla przykładu liczba może zostać wyświetlona z separatorem tysięcy. \n  Po więcej informacji zajrzyj do sekcji [Formatowanie danych](output-formatting.md).\n* [[yii\\i18n\\I18N|i18n]]: wspiera tłumaczenie i formatowanie wiadomości.\n  Po więcej informacji zajrzyj do sekcji [Internacjonalizacja](tutorial-i18n.md).\n* [[yii\\log\\Dispatcher|log]]: zarządza logowaniem informacji oraz błędów\n  Po więcej informacji zajrzyj do sekcji [Logowanie](runtime-logging.md).\n* [[yii\\swiftmailer\\Mailer|mail]]: wspiera tworzenie oraz wysyłanie emaili.\n  Po więcej informacji zajrzyj do sekcji [Wysyłanie poczty](tutorial-mailing.md).\n* [[yii\\base\\Application::response|response]]: reprezentuje odpowiedź wysyłaną do użytkowników.\n  Po więcej informacji zajrzyj do sekcji [Odpowiedzi](runtime-responses.md).\n* [[yii\\base\\Application::request|request]]: reprezentuje żądanie otrzymane od użytkownika.\n  Po więcej informacji zajrzyj do sekcji [Żądania](runtime-requests.md).\n* [[yii\\web\\Session|session]]: reprezentuje informacje przetrzymywane w sesji. Ten komponent jest dostępny \n  tylko w [[yii\\web\\Application|aplikacjach WEB]].\n  Po więcej informacji zajrzyj do sekcji [Sesje i ciasteczka](runtime-sessions-cookies.md).\n* [[yii\\web\\UrlManager|urlManager]]: wspiera przetwarzania oraz tworzenie adresów URL.\n  Po więcej informacji zajrzyj do sekcji [Przetwarzanie i tworzenie adresów URL](runtime-routing.md)\n* [[yii\\web\\User|user]]: reprezentuje informacje dotyczące uwierzytelniania użytkownika. Ten komponent jest dostępny \n  tylko w [[yii\\web\\Application|aplikacjach WEB]].\n  Po więcej informacji zajrzyj do sekcji [Uwierzytelnianie](security-authentication.md).\n* [[yii\\web\\View|view]]: wspiera renderowanie widoków.\n  Po więcej informacji zajrzyj do sekcji [Widoki](structure-views.md).\n"
  },
  {
    "path": "docs/guide-pl/structure-entry-scripts.md",
    "content": "Skrypty wejściowe\n=================\n\nSkrypty wejściowe są pierwszym krokiem procesu bootstrapowania aplikacji. Aplikacja (zarówno Web\njak i konsolowa) posiada pojedynczy skrypt wejściowy. Użytkownicy końcowi wysyłają żądania do skryptów \nwejściowych, które inicjują instancje aplikacji i przekazują do nich te żądania.\n\nSkrypty wejściowe dla aplikacji Web muszą znajdować się w folderach dostępnych dla Web, aby użytkownicy końcowi mogli je wywołać.\nZwykle nazywane są `index.php`, ale mogą mieć inne nazwy pod warunkiem, że serwery Web potrafią je zlokalizować.\n\nSkrypty wejściowe dla aplikacji konsolowych trzymane są zwykle w [ścieżce głównej](structure-applications.md)\naplikacji i nazywane `yii` (z sufiksem `.php`). Powinny być wykonywalne, aby użytkownicy \nmogli uruchomić aplikacje konsolowe za pomocą komendy `./yii <ścieżka> [argumenty] [opcje]`.\n\nSkrypty wejściowe wykonują głównie następującą pracę:\n\n* Definiują globalne stałe,\n* Rejestrują [autoloader Composera](https://getcomposer.org/doc/01-basic-usage.md#autoloading),\n* Dołączają plik klasy [[Yii]],\n* Ładują konfigurację aplikacji,\n* Tworzą i konfigurują instancję [aplikacji](structure-applications.md),\n* Wywołują [[yii\\base\\Application::run()|run()]], aby przetworzyć wysłane żądanie.\n\n\n## Aplikacje Web <span id=\"web-applications\"></span>\n\nPoniżej znajdziesz kod skryptu wejściowego dla [Podstawowego projektu szablonu Web](start-installation.md).\n\n```php\n<?php\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n\n// register Composer autoloader\nrequire __DIR__ . '/../vendor/autoload.php';\n\n// include Yii class file\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n\n// load application configuration\n$config = require __DIR__ . '/../config/web.php';\n\n// create, configure and run application\n(new yii\\web\\Application($config))->run();\n```\n\n\n## Aplikacje konsoli <span id=\"console-applications\"></span>\n\nPodobnie poniżej kod skryptu wejściowego dla aplikacji konsolowej:\n\n```php\n#!/usr/bin/env php\n<?php\n/**\n * Yii console bootstrap file.\n *\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\n\n// register Composer autoloader\nrequire __DIR__ . '/vendor/autoload.php';\n\n// include Yii class file\nrequire __DIR__ . '/vendor/yiisoft/yii2/Yii.php';\n\n// load application configuration\n$config = require __DIR__ . '/config/console.php';\n\n$application = new yii\\console\\Application($config);\n$exitCode = $application->run();\nexit($exitCode);\n```\n\n\n## Definiowanie stałych <span id=\"defining-constants\"></span>\n\nSkrypty wejściowe są najlepszym miejscem do definiowania globalnych stałych. Yii wspiera następujące trzy stałe:\n\n* `YII_DEBUG`: określa czy aplikacja działa w trybie debugowania. Podczas tego trybu aplikacja \n  przetrzymuje więcej informacji w logach i zdradza szczegóły stosu błędów, kiedy rzucony jest wyjątek. Z tego powodu\n  tryb debugowania powinien być używany głównie podczas fazy deweloperskiej. Domyślną wartością `YII_DEBUG` jest `false`.\n* `YII_ENV`: określa środowisko, w którym aplikacja działa. Opisane jest to bardziej szczegółowo \n  w sekcji [Konfiguracje](concept-configurations.md#environment-constants).\n  Domyślną wartością `YII_ENV` jest `'prod'`, co oznacza, że aplikacja jest uruchomiona w środowisku produkcyjnym.\n* `YII_ENABLE_ERROR_HANDLER`: określa czy uruchomić obsługę błędów przygotowaną przez Yii. Domyślną wartością tej stałej jest `true`.\n\nPodczas definiowania stałej często korzystamy z poniższego kodu:\n\n```php\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\n```\n\nco odpowiada:\n\n```php\nif (!defined('YII_DEBUG')) {\n    define('YII_DEBUG', true);\n}\n```\n\nJak widać pierwszy sposób jest bardziej zwięzły i łatwiejszy do zrozumienia.\n\nDefiniowanie stałych powinno odbyć się na samym początku skryptu wejściowego, aby odniosło skutek podczas dołączania pozostałych plików PHP.\n"
  },
  {
    "path": "docs/guide-pl/structure-overview.md",
    "content": "Struktura aplikacji\n===================\n\nAplikacja Yii jest zorganizowana według wzorca architektonicznego [model-widok-kontroler (MVC)](https://pl.wikipedia.org/wiki/Model-View-Controller). \n[Modele](structure-models.md) reprezentują dane, logikę biznesową i zasady walidacji, [widoki](structure-views.md) \nsą odpowiedzialne za wyświetlanie informacji związanych z modelami, a [kontrolery](structure-controllers.md) przyjmują dane wejściowe \ni przekształcają je w polecenia dla [modeli](structure-models.md) i [widoków](structure-views.md).\n\nOprócz MVC, w aplikacjach Yii zdefiniowane są następujące struktury:\n\n* [skrypty wejściowe](structure-entry-scripts.md): skrypty PHP dostępne bezpośrednio dla użytkowników końcowych, \n  odpowiedzialne za uruchomienie obsługi cyklu życia żądania.\n* [aplikacje](structure-applications.md): globalnie dostępne obiekty koordynujące działanie i zarządzające komponentami aplikacji.\n* [komponenty aplikacji](structure-application-components.md): obiekty zarejestrowane w aplikacji, zapewniające dostępność dedykowanych usług.\n* [moduły](structure-modules.md): niezależne pakiety kodu zawierające kompletną wewnętrzną strukturę MVC.\n  Aplikacja może być zorganizowana modułowo.\n* [filtry](structure-filters.md): reprezentują kod, który musi być wykonany przed i po obsłużeniu każdego z żądań kontrolera.\n* [widżety](structure-widgets.md): obiekty, które mogą być dołączone w [widokach](structure-views.md). Mogą zawierać logikę kontrolera \n  i być wykorzystane wielokrotnie w różnych miejscach.\n\nPoniższy diagram ilustruje statyczną strukturę aplikacji:\n\n![Statyczna struktura aplikacji](images/application-structure.png)\n"
  },
  {
    "path": "docs/guide-pl/test-acceptance.md",
    "content": "Testy akceptacyjne\n==================\n\n> Uwaga: Ta sekcja jest w trakcie tworzenia.\n\n- [Testy akceptacyjne Codeception](https://codeception.com/docs/03-AcceptanceTests)\n\nUruchamianie testów akceptacyjnych dla podstawowego i zaawansowanego szablonu projektu\n--------------------------------------------------------------------------------------\n\nProsimy o zapoznanie się z instrukcjami dostępnymi w plikach `apps/advanced/tests/README.md` i `apps/basic/tests/README.md`.\n"
  },
  {
    "path": "docs/guide-pl/test-environment-setup.md",
    "content": "Przygotowanie środowiska testowego\n==================================\n\n> Uwaga: Ta sekcja jest w trakcie tworzenia.\n\nYii 2 jest oficjalnie zintegrowany z [`Codeception`](https://github.com/Codeception/Codeception) - frameworkiem testowym, pozwalającym \nna utworzenie testów następujących typów:\n\n- [Testy jednostkowe](test-unit.md) - sprawdzające czy pojedyncza jednostka kodu działa poprawnie;\n- [Testy funkcjonalne](test-functional.md) - weryfikujące scenariusze działań z perspektywy użytkownika poprzez emulację przeglądarki;\n- [Testy akceptacyjne](test-acceptance.md) - weryfikujące scenariusze działań z perspektywy użytkownika w przeglądarce.\n\nYii dostarcza gotowy do użycia zestaw testów wszystkich trzech typów zarówno dla szablonu projektu \n[`yii2-basic`](https://github.com/yiisoft/yii2-app-basic) jak i \n[`yii2-advanced`](https://github.com/yiisoft/yii2-app-advanced).\n\nW celu uruchomienia testów koniecznie jest zainstalowanie [Codeception](https://github.com/Codeception/Codeception).\nInstalację można wykonać lokalnie - dla konkretnego pojedynczego projektu - lub globalnie - na komputerze deweloperskim.\n\nPoniższe komendy służą do instalacji lokalnej:\n\n```\ncomposer require \"codeception/codeception=2.0.*\"\ncomposer require \"codeception/specify=*\"\ncomposer require \"codeception/verify=*\"\n```\n\nDo instalacji globalnej należy dodać dyrektywę `global`:\n\n```\ncomposer global require \"codeception/codeception=2.0.*\"\ncomposer global require \"codeception/specify=*\"\ncomposer global require \"codeception/verify=*\"\n```\n\nJeśli nigdy wcześniej nie używałeś Composera do globalnych pakietów, uruchom komendę `composer global status`. W odpowiedzi powinieneś uzyskać:\n\n```\nChanged current directory to <directory>\n```\n\nNastępnie dodaj `<directory>/vendor/bin` do zmiennej systemowej `PATH`. Od tej pory będziesz mógł użyć `codecept` z linii komend globalnie.\n\n> Uwaga: instalacja globalna Codeception pozwala na użycie go we wszystkich projektach na komputerze deweloperskim oraz na wykonywanie \n> komendy `codecept` globalnie bez konieczności wskazywania ścieżki. Taka instalacja może jednak nie być pożądana, kiedy, dla przykładu, \n> dwa różne projekty wymagają różnych wersji Codeception.\n> Dla uproszczenia wszystkie komendy powłoki odnoszące się do uruchamiania testów użyte w tym przewodniku są napisane przy założeniu, że Codeception\n> został zainstalowany globalnie.\n"
  },
  {
    "path": "docs/guide-pl/test-functional.md",
    "content": "Testy funkcjonalne\n==================\n\n> Uwaga: Ta sekcja jest w trakcie tworzenia.\n\n- [Testy funkcjonalne Codeception](https://codeception.com/docs/04-FunctionalTests)\n\nUruchamianie testów funkcjonalnych dla podstawowego i zaawansowanego szablonu projektu\n--------------------------------------------------------------------------------------\n\nProsimy o zapoznanie się z instrukcjami dostępnymi w plikach `apps/advanced/tests/README.md` i `apps/basic/tests/README.md`.\n"
  },
  {
    "path": "docs/guide-pl/test-overview.md",
    "content": "Testowanie\n==========\n\nTestowanie jest istotnym elementem produkcji każdego oprogramowania. Niezależnie, czy jesteśmy tego świadomi, czy też nie, testy przeprowadzamy nieustannie.\nDla przykładu, kiedy napiszemy klasę w PHP, możemy debugować ją krok po kroku lub po prostu użyć wyrażeń jak echo lub die, aby sprawdzić, czy implementacja \ndziała zgodnie z naszym początkowym planem. W przypadku aplikacji web wprowadzamy testowe dane w formularzach, aby upewnić się, że strona odpowiada tak, jak powinna.\n\nProces testowania może zostać zautomatyzowany, dzięki czemu za każdym razem, kiedy musimy coś sprawdzić, wystarczy wywołać kod, który zrobi to za nas. \nKod, który weryfikuje zgodność wyniku z planowaną odpowiedzią, jest nazywany testem, a proces jego tworzenia i późniejszego wykonania jest nazywany testowaniem zautomatyzowanym, \nco jest głównym tematem tych rozdziałów.\n\n\nTworzenie kodu z testami\n------------------------\n\nTworzenie kodu opartego na testach (Test-Driven Development, TDD) i opartego na zachowaniach (Behavior-Driven Development, BDD) jest podejściem deweloperskim \nopierającym się na opisywaniu zachowania fragmentu kodu lub też całej jego funkcjonalności jako zestawu scenariuszy lub testów przed napisaniem właściwego kodu\ni dopiero potem stworzeniu implementacji, która pozwoli na poprawne przejście testów, spełniających zadane kryteria.\n\nProces tworzenia funkcjonalności wygląda następująco:\n\n- Stwórz nowy test, opisujący funkcjonalność do zaimplementowania.\n- Uruchom nowy test i upewnij się, że zakończy się błędem. To właściwe zachowanie, ponieważ nie ma jeszcze implementacji funkcjonalności.\n- Napisz prosty kod, który przejdzie poprawnie nowy test.\n- Uruchom wszystkie testy i upewnij się, że wszystkie zakończą się poprawnie.\n- Ulepsz kod, sprawdzając czy testy wciąż są zdane.\n\nPo zakończeniu proces jest powtarzany dla kolejnej funkcjonalności lub ulepszenia. Jeśli istniejąca funkcjonalność ma być zmodyfikowana,\ntesty powinny być również zmienione.\n\n> **Wskazówka**: Jeśli czujesz, że tracisz czas, przeprowadzając dużo krótkich i prostych iteracji, spróbuj objąć testowym scenariuszem więcej działań,\n> aby sprawdzić więcej kodu, przed ponownym uruchomieniem testów. Jeśli debugujesz zbyt dużo, spróbuj zrobić dokładnie na odwrót.\n\nPowodem tworzenia testów przed jakąkolwiek implementacją, jest możliwość skupienia się na tym, co chcemy osiągnąć, zanim przystąpimy do \"w jaki sposób to zrobić\".\nZwykle prowadzi to do stworzenia lepszej warstwy abstrakcji i łatwiejszej obsługi testów w przypadku poprawek funkcjonalności.\n\nPodsumowując, zalety takiego projektowania są następujące:\n\n- Pozwala na skupienie się na pojedynczej rzeczy na raz, dzięki czemu pozwala na lepsze planowanie i implementacje.\n- Obejmuje testami więcej funkcjonalności w większym stopniu, co oznacza, że jeśli testy zakończyły się poprawnie jest spore prawdopodobieństwo, że wszystko działa poprawnie.\n\nNa dłuższą metę przynosi to zwykle efekt w postaci mnóstwa oszczędzonego czasu i problemów.\n\n> **Wskazówka**: Jeśli chcesz dowiedzieć się więcej na temat reguł ustalania wymagań dla oprogramowania i modelowania istoty tego rozdziału, \n> warto zapoznać się z domenowym podejściem do tworzenia aplikacji [(Domain Driven Development, DDD)](https://pl.wikipedia.org/wiki/Domain-Driven_Design).\n\nKiedy i jak testować\n--------------------\n\nPodejście typu \"testy najpierw\" opisane powyżej, ma sens w przypadku długofalowych i relatywnie skomplikowanych projektów i może być przesadne w przypadku prostszych. \nPrzesłanki, kiedy testy są odpowiednie są następujące:\n\n- Projekt jest już duży i skomplikowany.\n- Wymagania projektowe zaczynają być skomplikowane. Projekt wciąż się powiększa.\n- Projekt jest planowany jako długoterminowy.\n- Koszty potencjalnych błędów są zbyt duże.\n\nNie ma nic złego w tworzeniu testów obejmujących zachowania istniejących implementacji.\n\n- Projekt jest oparty starszym kodzie i stopniowo przepisywany.\n- Projekt, nad którym masz pracować, nie ma w ogóle testów.\n\nW niektórych przypadkach jakakolwiek forma automatycznego testu może być nadmiarowa:\n\n- Projekt jest prosty i nie jest rozbudowywany.\n- Projekt jest jednorazowym zadaniem, które nie będzie rozwijane.\n\nPomimo tego, jeśli masz na to czas, automatyzacja testowania jest również dobrym pomysłem.\n\n\nBiblioteka\n-------------\n\n- Test Driven Development: By Example - Kent Beck. (ISBN: 0321146530)\n"
  },
  {
    "path": "docs/guide-pl/test-unit.md",
    "content": "Testy jednostkowe\n=================\n\n> Uwaga: Ta sekcja jest w trakcie tworzenia.\n\nTest jednostkowy weryfikuje poprawność działania pojedynczej jednostki kodu. W programowaniu zorientowanym obiektowo \nnajbardziej podstawową jednostką kodu jest klasa. Test jednostkowy musi sprawdzić, czy każda z metod interfejsu klasy działa poprawnie tj. czy przy \nzadanych różnych parametrach wejściowych metoda zwraca spodziewane rezultaty.\nTesty jednostkowe są zazwyczaj tworzone przez osoby, które piszą klasy poddawane tym testom.\n\nTesty jednostkowe w Yii są oparte o PHPUnit oraz, opcjonalnie, Codeception, zatem zalecane jest, aby zapoznać się z ich dokumentacją:\n\n- [PHPUnit (dokumentacja zaczyna się w rozdziale 2)](https://phpunit.de/manual/current/en/writing-tests-for-phpunit.html).\n- [Testy jednostkowe Codeception](https://codeception.com/docs/05-UnitTests).\n\nUruchamianie testów jednostkowych dla podstawowego i zaawansowanego szablonu projektu\n-------------------------------------------------------------------------------------\n\nProsimy o zapoznanie się z instrukcjami dostępnymi w plikach `apps/advanced/tests/README.md` i `apps/basic/tests/README.md`.\n\nTesty jednostkowe frameworka\n----------------------------\n\nJeśli chcesz przeprowadzić testy jednostkowe na frameworku Yii przejdź do sekcji \n\"[Od czego zacząć projektowanie w Yii 2](https://github.com/yiisoft/yii2/blob/master/docs/internals/getting-started.md)\".\n"
  },
  {
    "path": "docs/guide-pl/translators.json",
    "content": "[\n  \"Paweł Brzozowski\",\n  \"Tomek Romik\",\n  \"Daniel Filipek\"\n]\n"
  },
  {
    "path": "docs/guide-pl/tutorial-mailing.md",
    "content": "Wysyłanie poczty\n=======\n\n> Note: Ta sekcja jest w trakcie tworzenia.\n\nYii wspiera tworzenie oraz wysyłanie wiadomości email, jednakże silnik dostarcza jedynie funkcjonalność składania treści oraz prosty interfejs.\nMechanizm wysyłania wiadomości powinien być dostarczony przez rozszerzenie, \nponieważ projekty mogą wymagać różnych implementacji, przez co mechanizm jest zależny od zewnętrznych usług i bibliotek.\n\nDla większości przypadków możesz używać oficjalnego rozszerzenia [yii2-swiftmailer](https://github.com/yiisoft/yii2-swiftmailer).\n\n\nKonfiguracja\n-------------\n\nKonfiguracja tego komponentu zależy od rozszerzenia jakie wybrałeś.\nGeneralnie, konfiguracja Twojego komponentu w aplikacji powinna wyglądać tak:\n\n```php\nreturn [\n    //....\n    'components' => [\n        'mailer' => [\n            'class' => 'yii\\swiftmailer\\Mailer',\n        ],\n    ],\n];\n```\n\n\nPodstawowe użycie\n-----------\nKiedy komponent 'mailer' zostanie skonfigurowany, możesz użyć następującego kodu do wysłania wiadomości email:\n\n```php\nYii::$app->mailer->compose()\n    ->setFrom('from@domain.com')\n    ->setTo('to@domain.com')\n    ->setSubject('Temat wiadomości')\n    ->setTextBody('Zwykła treść wiadomości')\n    ->setHtmlBody('<b>Treść HTML wiadomości</b>')\n    ->send();\n```\n\nW powyższym przykładzie metoda `compose()` tworzy instancję wiadomości email, która następnie jest wypełniana danymi i wysłana.\nMożesz utworzyć tutaj więcej złożonej logiki jeśli jest to potrzebne:\n\n```php\n$message = Yii::$app->mailer->compose();\nif (Yii::$app->user->isGuest) {\n    $message->setFrom('from@domain.com')\n} else {\n    $message->setFrom(Yii::$app->user->identity->email)\n}\n$message->setTo(Yii::$app->params['adminEmail'])\n    ->setSubject('Temat wiadomości')\n    ->setTextBody('Zwykła treść wiadomości')\n    ->send();\n```\n\n> Note: każde rozszerzenie mailingowe posiada dwie główne klasy: 'Mailer' oraz 'Message'. Klasa 'Mailer' zawsze posiada nazwę klasy 'Message'.\n> Nie próbuj instancjować obiektu 'Message' bezpośrednio - zawsze używaj do tego metody `compose()`.\n\nMożesz również wysłać wiele wiadomości na raz:\n\n```php\n$messages = [];\nforeach ($users as $user) {\n    $messages[] = Yii::$app->mailer->compose()\n        // ...\n        ->setTo($user->email);\n}\nYii::$app->mailer->sendMultiple($messages);\n```\n\nNiektóre rozszerzenia mailingowe mogą czerpać korzyści z tego sposobu, np. używając pojedyńczych wiadomości sieciowych.\n\n\nTworzenie treści maila\n----------------------\n\nYii pozwala na tworzenie treści aktualnej wiadomości email przez specjalne pliki widoków.\nDomyślnie, pliki te zlokalizowane są w ścieżce '@app/mail'.\n\nPrzykładowy widok pliku treści wiadomości email:\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\helpers\\Url;\n\n\n/**\n * @var \\yii\\web\\View $this instancja komponentu View\n * @var \\yii\\mail\\BaseMessage $message instancja nowo utworzonej wiadomości email\n */\n\n?>\n<h2>Ta wiadomość pozwala Ci odwiedzić stronę główną naszej witryny przez jedno kliknięcie</h2>\n<?= Html::a('Idź do strony głównej', Url::home('http')) ?>\n```\n\nW celu wykorzystania tego pliku do utworzenia treści wiadomości, przekaż po prostu nazwę tego widoku do metody `compose()`:\n\n```php\nYii::$app->mailer->compose('home-link') // wynik renderingu widoku staje się treścią wiadomości\n    ->setFrom('from@domain.com')\n    ->setTo('to@domain.com')\n    ->setSubject('Temat wiadomości')\n    ->send();\n```\n\nMożesz przekazać dodatkowe parametry do metody `compose()`, które będą dostępne w plikach widoków:\n\n```php\nYii::$app->mailer->compose('greetings', [\n    'user' => Yii::$app->user->identity,\n    'advertisement' => $adContent,\n]);\n```\n\nMożesz określić różne pliki do zwykłej treści oraz treści HTML:\n\n```php\nYii::$app->mailer->compose([\n    'html' => 'contact-html',\n    'text' => 'contact-text',\n]);\n```\n\nJeśli określisz nazwę widoku jako ciąg skalarny, to wynik jego renderowania zostanie użyty jako ciało HTML wiadomości,\npodczas gdy przy użyciu zwykłego teksu zostanie ono utworzone przez usunięcie wszystkich encji HTML z tego widoku. \n\nWynik renderowania widoku może zostać opakowany w szablon. Szablon możesz ustawić przez właściwość [[yii\\mail\\BaseMailer::htmlLayout|htmlLayout]] lub \n[[yii\\mail\\BaseMailer::textLayout|textLayout]].\nZadziała to w identyczny sposób co w standardowej aplikacji web.\nSzalony mogą zostać użyte do ustawienia styli CSS, lub innej wspólnej treści:\n\n```php\n<?php\nuse yii\\helpers\\Html;\n\n/**\n * @var \\yii\\web\\View $this view component instance\n * @var \\yii\\mail\\MessageInterface $message the message being composed\n * @var string $content main view render result\n */\n?>\n<?php $this->beginPage() ?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n<head>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=<?= Yii::$app->charset ?>\" />\n    <style type=\"text/css\">\n        .heading {...}\n        .list {...}\n        .footer {...}\n    </style>\n    <?php $this->head() ?>\n</head>\n<body>\n    <?php $this->beginBody() ?>\n    <?= $content ?>\n    <div class=\"footer\">Z pozdrowieniami, zespół <?= Yii::$app->name ?></div>\n    <?php $this->endBody() ?>\n</body>\n</html>\n<?php $this->endPage() ?>\n```\n\n\nZałączniki do wiadomości\n---------------\n\nMożesz dodać załączniki do wiadomości przez użycie metod `attach()` oraz `attachContent()`:\n\n```php\n$message = Yii::$app->mailer->compose();\n\n// Załącz plik z lokalnego systemu plików:\n$message->attach('/path/to/source/file.pdf');\n\n// Utwórz załącznik w locie:\n$message->attachContent('Attachment content', ['fileName' => 'attach.txt', 'contentType' => 'text/plain']);\n```\n\n\nOsadzanie obrazków\n----------------\n\nW treści wiadomości możesz osadzać również obrazki przy użyciu metody `embed()`. Metoda ta zwraca ID załącznika,\nktóry powinien zostać później użyty w tagu 'img'.\nUżycie tej metody jest proste podczas tworzenia treści wiadomości z pliku widoku:\n\n```php\nYii::$app->mailer->compose('embed-email', ['imageFileName' => '/path/to/image.jpg'])\n    // ...\n    ->send();\n```\n\nNastępnie, w pliku widoku możesz użyć następującego kodu:\n\n```php\n<img src=\"<?= $message->embed($imageFileName); ?>\">\n```\n\n\nTestowanie i debugowanie\n---------------------\n\nDeweloperzy często muszą sprawdzić, czy emaile zostały wysłane przez aplikację lub jaka była ich treść.\nMożesz tego dokonać w łatwy sposób, używając dostarczonej przez Yii funkcjonalności, którą aktywujesz przez parametr [[yii\\mail\\BaseMailer::useFileTransport|useFileTransport]].\nJeśli zostanie aktywowana, każda wiadomość email będzie zapisywana do lokalnych plików zamiast zostać wysłana. Wszystkie pliki będą zapisane w ścieżce podanej w \n[[yii\\mail\\BaseMailer::fileTransportPath|fileTransportPath]], która domyślnie ustawiona jest na '@runtime/mail'.\n\n> Note: możesz albo zapisywać wiadomości do plików, albo wysyłać je do odbiorców, nie można wykonać tych dwóch czynności na raz.\n\nPlik z wiadomością email może zostać otwarty przez standardowy edytor tekstu, dzięki czemu będziesz mógł przeglądać nagłówki oraz treść wiadomości.\n\n> Note: plik wiadomości jest tworzony przy użyciu metody [[yii\\mail\\MessageInterface::toString()|toString()]], więc jest zależny od aktualnie używanego rozszerzenia mailingowego w \n> Twojej aplikacji.\n\nTworzenie własnego rozwiązania mailingowego\n-------------------------------\n\nAby utworzyć swoje własne rozwiązanie mailingowe, musisz utworzyć dwie klasy: 'Mailer' oraz 'Message'.\nMożesz rozszerzyć klasy [[yii\\mail\\BaseMailer|BaseMailer]] i [[yii\\mail\\BaseMessage|BaseMessage]] jako bazowe klasy do tego rozwiązania.\nZawierają one podstawową logikę mechanizmu mailingu, który został opisany w tej sekcji. \nOczywiście ich użycie nie jest obowiązkowe, wystarczy zaimplementowanie interfejsów [[yii\\mail\\MailerInterface|MailerInterface]] oraz [[yii\\mail\\MessageInterface|MessageInterface]].\nNastępnie musisz zaimplementować wszystkie abstrakcyjne metody do swoich klas.\n"
  },
  {
    "path": "docs/guide-pl/tutorial-shared-hosting.md",
    "content": "Współdzielone środowisko hostujące\n==================================\n\nWspółdzielone środowiska hostujące są często ograniczone, jeśli chodzi o możliwości ich konfiguracji i struktury folderów. Pomimo to, wciąż, w większości przypadków, \nmożesz w takim środowisku uruchomić Yii 2.0 po kilku drobnych modyfikacjach.\n\nWdrożenie podstawowej aplikacji\n-------------------------------\n\nW standardowym współdzielonym środowisku hostującym jest zwykle tylko jeden główny folder publiczny (webroot), zatem wygodniej jest stosować podstawowy szablon projektu. \nKorzystając z instrukcji w sekcji [Instalowanie Yii](start-installation.md), zainstaluj taki szablon lokalnie. Po udanej instalacji, dokonamy kilku modyfikacji, aby aplikacji \nmogła działać na współdzielonym środowisku.\n\n### Zmiana nazwy webroota <span id=\"renaming-webroot\"></span>\n\nPołącz się ze swoim współdzielonym hostem za pomocą np. klienta FTP. Prawdopodobnie zobaczysz listę folderów podobną do poniższej.\n \n```\nconfig\nlogs\nwww\n```\n\nW tym przykładzie, `www` jest folderem webroot. Folder ten może mieć różne nazwy, zwykle stosowane są: `www`, `htdocs` i `public_html`.\n\nWebroot w naszym podstawowym szablonie projektu nazywa się `web`. Przed skopiowaniem aplikacji na serwer, zmień nazwę lokalnego folderu webroot, aby odpowiadała folderowi \nna serwerze, czyli z `web` na `www`, `public_html` lub na inną nazwę, która używana jest na serwerze.\n\n### Folder root FTP jest zapisywalny\n\nJeśli masz prawa zapisu w folderze poziomu root, czyli tam, gdzie znajdują się foldery `config`, `logs` i `www`, skopiuj foldery `assets`, `commands` itd. bezpośrednio w to \nmiejsce.\n\n### Dodatkowe opcje serwera <span id=\"add-extras-for-webserver\"></span>\n\nJeśli Twój serwer to Apache, będziesz musiał dodać plik `.htaccess` z poniższą zawartością do folderu `web` (czy też `public_html`, bądź jakakolwiek jest jego nazwa), \ngdzie znajduje się plik `index.php`:\n\n```\nOptions +FollowSymLinks\nIndexIgnore */*\n\nRewriteEngine on\n\n# jeśli katalog lub plik istnieje, użyj go bezpośrednio\nRewriteCond %{REQUEST_FILENAME} !-f\nRewriteCond %{REQUEST_FILENAME} !-d\n\n# w innym przypadku przekieruj żądanie na index.php\nRewriteRule . index.php\n```\n\nW przypadku serwera nginx nie powinieneś potrzebować dodatkowego pliku konfiguracyjnego.\n\n### Sprawdzenie wymagań\n\nAby uruchomić Yii, Twój serwer musi spełniać jego wymagania. Minimalnym wymaganiem jest PHP w wersji 5.4. Możesz sprawdzić wszystkie wymagania, kopiując plik \n`requirements.php` z folderu root do folderu webroot i uruchamiając go w przeglądarce pod adresem `https://example.com/requirements.php`. \nNie zapomnij o skasowaniu tego pliku po sprawdzeniu wymagań.\n\n\nWdrożenie zaawansowanej aplikacji\n---------------------------------\n\nWdrażanie zaawansowanej aplikacji na współdzielonym środowisku jest odrobinę bardziej problematyczne, niż w przypadku podstawowej aplikacji, ponieważ wymaga ona dwóch folderów \nwebroot, czego zwykle nie wspierają serwery środowisk współdzielonych. Będziemy musieli odpowiednio dostosować strukturę folderów.\n\n### Przeniesienie skryptów wejściowych do jednego folderu webroot\n\nNa początek potrzebujemy folderu webroot. Stwórz nowy folder i nazwij go tak, jak webroot docelowego serwera, jak opisane zostało to w \n[Zmiana nazwy webroota](#renaming-webroot) powyżej, np. `www` czy też `public_html`. Następnie utwórz poniższą strukturę, gdzie `www` jest folderem webroot, \nktóry właśnie stworzyłeś:\n\n```\nwww\n    admin\nbackend\ncommon\nconsole\nenvironments\nfrontend\n...\n```\n\n`www` będzie naszym folderem frontend, zatem przenieś tam zawartość `frontend/web`. Do folderu `www/admin` przenieś zawartość `backend/web`. \nW każdym przypadku będziesz musiał zmodyfikować ścieżki w plikach `index.php` i `index-test.php`.\n\n### Rozdzielone sesje i ciasteczka\n\nBackend i frontend zostały stworzone z myślą o uruchamianiu ich z poziomu oddzielnych domen. Jeśli uruchamiamy je z poziomu jednej domeny, frontend i backend będą dzielić \nte same ciasteczka, co może wywołać konflikty. Aby temu zapobiec, zmodyfikuj backendową konfigurację aplikacji `backend/config/main.php` jak poniżej:\n\n```php\n'components' => [\n    'request' => [\n        'csrfParam' => '_backendCSRF',\n        'csrfCookie' => [\n            'httpOnly' => true,\n            'path' => '/admin',\n        ],\n    ],\n    'user' => [\n        'identityCookie' => [\n            'name' => '_backendIdentity',\n            'path' => '/admin',\n            'httpOnly' => true,\n        ],\n    ],\n    'session' => [\n        'name' => 'BACKENDSESSID',\n        'cookieParams' => [\n            'path' => '/admin',\n        ],\n    ],\n],\n```\n"
  },
  {
    "path": "docs/guide-pl/tutorial-start-from-scratch.md",
    "content": "Tworzenie własnej struktury aplikacji\n=====================================\n\n> Note: Ta sekcja jest w trakcie tworzenia.\n\n[Podstawowy](https://github.com/yiisoft/yii2-app-basic) i [zaawansowany](https://github.com/yiisoft/yii2-app-advanced) szablon projektu jest w pełni wystarczający w większości \nprzypadków, ale czasem może zajść potrzeba stworzenia własnego szablonu, na bazie którego tworzony będzie projekt.\n\nSzablony projektów w Yii to po prostu repozytoria zawierające plik `composer.json` i zarejestrowane jako paczki Composera.\nKażde repozytorium może stać sie taką paczką, dzięki czemu można je zainstalować wywołując komendę Composera `create-project`.\n\nPonieważ stworzenie nowego szablonu projektu od podstaw jest pracochłonne, łatwiej po prostu użyć jednego z gotowych szablonów jako bazy. W tym przykładzie skorzystamy \nz podstawowego szablonu.\n\nKopia podstawowego szablonu projektu\n------------------------------------\n\nPierwszym krokiem jest wykonanie kopii podstawowego szablonu Yii z repozytorium Git:\n\n```bash\ngit clone git@github.com:yiisoft/yii2-app-basic.git\n```\n\nPo zakończeniu pobierania plików repozytorium, można skasować folder `.git` wraz z zawartością, ponieważ wprowadzonych przez nas zmian nie zamierzamy wysyłać z powrotem.\n\nModyfikacja plików\n------------------\n\nNastępnie należy zmodyfikować plik `composer.json`, aby opisywał nasz szablon. Zmień wartości `name` (nazwa), `description` (opis), `keywords` (słowa kluczowe), `homepage` \n(strona domowa), `license` (licencja), i `support` (wsparcie) na takie, które odpowiadają nowemu szablonowi. Zmodyfikuj również `require`, `require-dev`, `suggest` i wszelkie inne opcje \nzgodnie z wymaganiami.\n\n> Note: W pliku `composer.json` użyj parametru `writable` znajdującego się w elemencie `extra`, aby określić \n> uprawnienia dla plików, które zostaną ustawione po utworzeniu aplikacji na podstawie szablonu.\n\nNastępnie należy zmodyfikować właściwą strukturę i zawartość aplikacji, aby stanowiły domyślną początkową wersję dla projektów. \nNa samym końcu zmodyfikuj plik README, aby pasował do szablonu.\n\nTworzenie paczki\n----------------\n\nNowy szablon umieść w odpowiadającym mu repozytorium Git. Jeśli zamierzasz udostępnić go jako open source, [Github](https://github.com) jest najlepszym miejscem do tego celu. \nJeśli jednak nie przewidujesz współpracy z innymi nad swoim szablonem, dowolne repozytorium Git będzie odpowiednie.\n\nNastępnie należy zarejestrować swoją paczkę dla Composera. Dla publicznie dostępnych szablonów paczkę należy zarejestrować w serwisie [Packagist](https://packagist.org/).\nZ prywatnymi szablonami sprawa jest trochę bardziej skomplikowana - instrukcję, jak to zrobić, znajdziesz w \n[dokumentacji Composera](https://getcomposer.org/doc/05-repositories.md#hosting-your-own).\n\nUżycie szablonu\n---------------\n\nTylko tyle jest wymagane, aby stworzyć nowy szablon projektu Yii. Teraz już możesz rozpocząć pracę nad świeżym projektem, używając swojego szablonu, za pomocą komend:\n\n```\ncomposer global require \"fxp/composer-asset-plugin:^1.4.1\"\ncomposer create-project --prefer-dist --stability=dev mojafirma/yii2-app-fajna nowy-projekt\n```\n"
  },
  {
    "path": "docs/guide-pl/tutorial-template-engines.md",
    "content": "Silniki szablonów\n=================\n\nYii domyślnie używa PHP jako języka szablonów, ale nic nie stoi na przeszkodzie, aby skonfigurować wsparcie dla innych silników renderujących widok, \ntakich jak [Twig](https://twig.symfony.com/) lub [Smarty](https://www.smarty.net/), dostępnych w postaci rozszerzeń.\n\nKomponent `view` jest odpowiedzialny za renderowanie widoków. Aby dodać niestandardowy silnik szablonów, należy skonfigurować komponent jak poniżej:\n\n```php\n[\n    'components' => [\n        'view' => [\n            'class' => 'yii\\web\\View',\n            'renderers' => [\n                'tpl' => [\n                    'class' => 'yii\\smarty\\ViewRenderer',\n                    //'cachePath' => '@runtime/Smarty/cache',\n                ],\n                'twig' => [\n                    'class' => 'yii\\twig\\ViewRenderer',\n                    'cachePath' => '@runtime/Twig/cache',\n                    // Tablica ustawień twig:\n                    'options' => [\n                        'auto_reload' => true,\n                    ],\n                    'globals' => ['html' => '\\yii\\helpers\\Html'],\n                    'uses' => ['yii\\bootstrap'],\n                ],\n                // ...\n            ],\n        ],\n    ],\n]\n```\n\nW powyższym przykładzie zarówno Smarty jak i Twig są gotowe do użycia w plikach widoków. Aby dodać te rozszerzenia w projekcie, należy zmodyfikować \ndodatkowo plik `composer.json` poprzez dopisanie w wymaganiach (`require`):\n\n```\n\"yiisoft/yii2-smarty\": \"~2.0.0\",\n\"yiisoft/yii2-twig\": \"~2.0.0\",\n```\nPo zapisaniu pliku można zainstalować rozszerzenia uruchamiając komendę `composer update --prefer-dist` z konsoli.\n\nSzczegóły na temat każdego z powyższych silników szablonów dostępne są w ich dokumentacjach:\n\n- [Przewodnik po Twig](https://github.com/yiisoft/yii2-twig/tree/master/docs/guide)\n- [Przewodnik po Smarty](https://github.com/yiisoft/yii2-smarty/tree/master/docs/guide)\n"
  },
  {
    "path": "docs/guide-pl/tutorial-yii-as-micro-framework.md",
    "content": "# Używanie Yii jako mikroframeworka\n\nYii może być z powodzeniem wykorzystywane bez dodatkowych funkcjonalności dostarczanych przez prosty i zaawansowany szablon aplikacji. Inaczej mówiąc, \nYii już jest samo w sobie mikroframeworkiem. Do pracy z Yii nie jest wymagane, aby struktura folderów była dokładnie taka, jak pokazana w szablonach.\n\nJest to szczególnie korzystne, kiedy nie potrzebujesz gotowego kodu szablonów, jak w przypadku assetów luc widoków. Jednym z takich przypadków jest budowa JSON API. \nW tej sekcji pokażemy jak to zrobić.\n\n## Instalacja Yii\n\nStwórz folder dla plików swojego projektu i ustaw go jako aktywną ścieżkę. Komendy używane w przykładach oparte są na składni UNIXowej, ale podobne dostępne są również w Windows.\n\n```bash\nmkdir micro-app\ncd micro-app\n```\n\n> Note: minimalna wiedza na temat użytkowania Composera jest wymagana w celu kontynuacji. Jeśli nie wiesz, jak używać Composera, prosimy o zapoznanie się najpierw z [Przewodnikiem po Composerze](https://getcomposer.org/doc/00-intro.md).\n\nStwórz plik `composer.json` w folderze `micro-app`, używając swojego ulubionego edytora i dodaj co następuje:\n\n```json\n{\n    \"require\": {\n        \"yiisoft/yii2\": \"~2.0.0\"\n    },\n    \"repositories\": [\n        {\n            \"type\": \"composer\",\n            \"url\": \"https://asset-packagist.org\"\n        }\n    ]\n}\n```\n\nZapisz plik i uruchom komendę `composer install`. Dzięki temu zainstalujesz framework i wszystkie jego zależności.\n\n## Tworzenie struktury projektu\n\nPo zainstalowaniu frameworka, czas na utworzenie [punktu wejścia](structure-entry-scripts.md) dla aplikacji. Punkt wejścia to pierwszy plik, który będzie uruchamiany, \npodczas startu aplikacji. Ze względów bezpieczeństwa, zalecane jest, aby plik punktu wejścia umieścić w osobnym folderze, który będzie ustawiony jako bazowy folder aplikacji.\n\nStwórz folder `web` i umieść w nim plik `index.php` z następującą zawartością:\n\n```php \n<?php\n\n// zakomentuj poniższe dwie linie przy wydaniu aplikacji na środowisku produkcyjnym\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n\nrequire(__DIR__ . '/../vendor/autoload.php');\nrequire(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');\n\n$config = require __DIR__ . '/../config.php';\n(new yii\\web\\Application($config))->run();\n```\n\nStwórz również plik `config.php`, który będzie zawierał całą konfigurację aplikacji:\n\n```php\n<?php\nreturn [\n    'id' => 'micro-app',\n    // ścieżką bazową aplikacji będzie folder `micro-app`\n    'basePath' => __DIR__,\n    // w tym miejscu określamy, gdzie aplikacja ma szukać wszystkich kontrolerów\n    'controllerNamespace' => 'micro\\controllers',\n    // ustawiamy alias, aby umożliwić autoładowanie klas z przestrzeni nazw 'micro'\n    'aliases' => [\n        '@micro' => __DIR__,\n    ],\n];\n```\n\n> Info: Pomimo że konfiguracja mogłaby być przechowywana w pliku `index.php`, zalecane jest, aby zapisana była osobno.\n> Dzięki temu może być również wykorzystywana dla aplikacji konsolowej, jak pokazano to poniżej.\n\nTwój projekt jest już gotowy do rozpoczęcia kodowania. Od Ciebie również zależy struktura jego folderów, dopóki jak będziesz pamiętać o poprawnych przestrzeniach nazw.\n\n## Tworzenie pierwszego kontrolera\n\nStwórz folder `controllers` i dodaj w nim plik `SiteController.php`, który będzie domyślnym kontrolerem obsługującym żądania bez wskazanej wyraźnie ścieżki.\n\n```php\n<?php\n\nnamespace micro\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actionIndex()\n    {\n        return 'Hello World!';\n    }\n}\n```\n\nJeśli chcesz użyć innej nazwy dla tego kontrolera, nie krępuj się - musisz jedynie skonfigurować odpowiednio [[yii\\base\\Application::$defaultRoute]].\nDla przykładu, jeśli chcesz go zmienić na `DefaultController`, ustaw `'defaultRoute' => 'default/index'` w konfiguracji.\n\nW tym momencie struktura projektu powinna wyglądać jak poniżej:\n\n```\nmicro-app/\n├── composer.json\n├── web/\n    └── index.php\n└── controllers/\n    └── SiteController.php\n```\n\nJeśli nie ustawiłeś jeszcze serwera web, być może zechcesz zerknąć na [pliki przykładów konfiguracji serwera web](start-installation.md#configuring-web-servers).\nInną opcją jest skorzystanie z komendy `yii serve`, która użyje wbudowanego w PHP serwera web. Możesz uruchomić ją z poziomu folderu `micro-app/` za pomocą:\n\n    vendor/bin/yii serve --docroot=./web\n\nUruchomienie adresu URL aplikacji w przeglądarce powinno zaowocować teraz komunikatem \"Hello World!\", który jest zwracany w `SiteController::actionIndex()`.\n\n> Info: W naszym przykładzie zmieniliśmy domyślną przestrzeń nazw aplikacji `app` na `micro`, aby zademonstrować, \n> że nie ma potrzeby być ograniczonym przez tę nazwę (w przypadku, gdyby ktoś myślał, że jednak jest). Po zmianie jej na inną,  \n> należy jedynie zmodyfikować odpowiednio [[yii\\base\\Application::$controllerNamespace|przestrzeń nazw kontrolerów]] i ustawić właściwy alias.\n\n\n## Tworzenie API REST\n\nAby zademonstrować, jak korzystać z naszego \"mikroframeworka\", stworzymy proste API REST dla postów.\n\nAby API mogło zwrócić jakieś dane, najpierw potrzebujemy ich bazy. Dodaj konfigurację połączenia z bazą danych do konfiguracji aplikacji:\n\n```php\n'components' => [\n    'db' => [\n        'class' => 'yii\\db\\Connection',\n        'dsn' => 'sqlite:@micro/database.sqlite',\n    ],\n],\n```\n\n> Info: Używamy w tym przykładzie bazy danych sqlite dla uproszczenia. Aby zapoznać się z innymi opcjami, przejdź do [przewodnika po bazach danych](db-dao.md).\n\nNastępnie tworzymy [migrację bazodanową](db-migrations.md), aby skonstruować tabelę postów.\nUpewnij się, że posiadasz oddzielny plik konfiguracji, jak zostało to opisane powyżej, ponieważ musimy teraz uruchomić komendę konsolową, jak poniżej.\nUruchomienie tych komend utworzy plik migracji i wprowadzi migrację do bazy danych:\n\n    vendor/bin/yii migrate/create --appconfig=config.php create_post_table --fields=\"title:string,body:text\"\n    vendor/bin/yii migrate/up --appconfig=config.php\n\nStwórz folder `models` i plik `Post.php` w tym folderze. Poniżej znajdziesz kod dla modelu:\n\n```php\n<?php\n\nnamespace micro\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass Post extends ActiveRecord\n{ \n    public static function tableName()\n    {\n        return '{{posts}}';\n    }\n}\n```\n\n> Info: Tak utworzony model jest klasą ActiveRecord, która reprezentuje dane z tabeli `posts`.\n> Zapoznaj się z [przewodnikiem po active record](db-active-record.md), aby uzyskać więcej informacji.\n\nAby obsłużyć posty w naszym API, dodaj `PostController` w `controllers`:\n\n```php\n<?php\n\nnamespace micro\\controllers;\n\nuse yii\\rest\\ActiveController;\n\nclass PostController extends ActiveController\n{\n    public $modelClass = 'micro\\models\\Post';\n\n    public function behaviors()\n    {\n        // wyłącz rateLimiter, który do pracy wymaga, aby użytkownik był zalogowany\n        $behaviors = parent::behaviors();\n        unset($behaviors['rateLimiter']);\n        return $behaviors;\n    }\n}\n```\n\nW tym momencie nasze API obsługuje już następujące adresy URL:\n\n- `/index.php?r=post` - wyświetla listę wszystkich postów\n- `/index.php?r=post/view&id=1` - wyświetla post o ID 1\n- `/index.php?r=post/create` - tworzy post\n- `/index.php?r=post/update&id=1` - aktualizuje post o ID 1\n- `/index.php?r=post/delete&id=1` - usuwa post o ID 1\n\nZapoznaj się z poniższymi wskazówkami, które pomogą Ci w dalszym rozwijaniu Twojej aplikacji:\n\n- Aktualnie API rozpoznaje jedynie urlenkodowane dane formularza na wejściu - aby zmienić je w prawdziwe JSON API, \n  musisz skonfigurować [[yii\\web\\JsonParser]].\n- Aby uczynić adresy URL, bardziej przyjaznymi dla użytkownika, musisz skonfigurować ruting.\n  Zobacz [przewodnik po rutingu REST](rest-routing.md), który wyjaśnia, jak to zrobić.\n- Dodatkowo przeczytaj też sekcję [Dalsze kroki](start-looking-ahead.md), która podpowie jak zaplanować rozwój projektu.\n"
  },
  {
    "path": "docs/guide-pt-BR/README.md",
    "content": "Guia Definitivo para Yii 2.0\n============================\n\nEsse tutorial está disponível sob os [termos da documentação do Yii](https://www.yiiframework.com/doc/terms/).\n\nTodos os Direitos Reservados.\n\n2014 (c) Yii Software LLC.\n\n\nIntrodução\n----------\n\n* [Sobre o Yii](intro-yii.md)\n* [Migrando a partir da versão 1.1](intro-upgrade-from-v1.md)\n\n\nPrimeiros Passos\n----------------\n\n* [O que você precisa saber](start-prerequisites.md)\n* [Instalando o Yii](start-installation.md)\n* [Executando Aplicações](start-workflow.md)\n* [Dizendo \"Olá!\"](start-hello.md)\n* [Trabalhando com Formulários](start-forms.md)\n* [Trabalhando com Bancos de Dados](start-databases.md)\n* [Gerando Código com Gii](start-gii.md)\n* [Seguindo em Frente](start-looking-ahead.md)\n\n\nEstrutura da Aplicação\n--------------------------\n\n* [Visão Geral](structure-overview.md)\n* [Scripts de Entrada](structure-entry-scripts.md)\n* [Aplicações](structure-applications.md)\n* [Componentes de Aplicação](structure-application-components.md)\n* [Controladores (Controllers)](structure-controllers.md)\n* [Modelos (Models)](structure-models.md)\n* [Visões (Views)](structure-views.md)\n* [Módulos](structure-modules.md)\n* [Filtros](structure-filters.md)\n* [Widgets](structure-widgets.md)\n* [Assets](structure-assets.md)\n* [Extensões](structure-extensions.md)\n\n\nTratando Requisições\n-------------------------\n\n* [Visão Geral](runtime-overview.md)\n* [Preparação do Ambiente (Bootstrapping)](runtime-bootstrapping.md)\n* [Roteamento e Criação de URL](runtime-routing.md)\n* [Requisições](runtime-requests.md)\n* [Respostas](runtime-responses.md)\n* [Sessões e Cookies](runtime-sessions-cookies.md)\n* [Tratamento de Erros](runtime-handling-errors.md)\n* [Gerenciamento de Logs](runtime-logging.md)\n\n\nConceitos Chave\n---------------\n\n* [Componentes](concept-components.md)\n* [Propriedades](concept-properties.md)\n* [Eventos](concept-events.md)\n* [Comportamentos](concept-behaviors.md)\n* [Configurações](concept-configurations.md)\n* [Apelidos (Aliases)](concept-aliases.md)\n* [Carregamento Automático de Classes (Autoloading)](concept-autoloading.md)\n* [Service Locator](concept-service-locator.md)\n* [Container de Injeção de Dependência](concept-di-container.md)\n\n\nTrabalhando com Banco de Dados\n------------------------------\n\n* [Objetos de Acesso a Dados - (Database Access Objects)](db-dao.md): Conectando a um banco de dados, consultas básicas, transações e manipulação de esquema\n* [Construtor de Consulta (Query Builder)](db-query-builder.md): Consultando o banco de dados usando uma camada de abstração simples\n* [Active Record](db-active-record.md): Sobre o Active Record ORM, recuperando e manipulando registros e definindo relacionamentos\n* [Migrações (Migrations)](db-migrations.md): Aplica controle de versão para seus banco de dados em um ambiente de desenvolvimento em equipe\n* [Sphinx](https://www.yiiframework.com/extension/yiisoft/yii2-sphinx/doc/guide)\n* [Redis](https://www.yiiframework.com/extension/yiisoft/yii2-redis/doc/guide)\n* [MongoDB](https://www.yiiframework.com/extension/yiisoft/yii2-mongodb/doc/guide)\n* [ElasticSearch](https://www.yiiframework.com/extension/yiisoft/yii2-elasticsearch/doc/guide)\n\n\nColetando Dados de Usuários\n---------------------------\n\n* [Criando Formulários](input-forms.md)\n* [Validando Dados](input-validation.md)\n* [Recebendo Arquivos (Upload)](input-file-upload.md)\n* [Coletando Dados Tabulares](input-tabular-input.md)\n* [Coletando Dados para Múltiplos Models](input-multiple-models.md)\n* [Extendendo o ActiveForm no Client Side](input-form-javascript.md)\n\n\nExibindo Dados\n---------------\n\n* [Formatação de Dados](output-formatting.md)\n* [Paginação](output-pagination.md)\n* [Ordenação](output-sorting.md)\n* [Provedores de Dados (Data Providers)](output-data-providers.md)\n* [Widgets de Dados](output-data-widgets.md)\n* [Trabalhando com Client Scripts](output-client-scripts.md)\n* [Temas](output-theming.md)\n\n\nSegurança\n--------\n\n* [Visão Geral](security-overview.md)\n* [Autenticação](security-authentication.md)\n* [Autorização](security-authorization.md)\n* [Trabalhando com Senhas](security-passwords.md)\n* [Criptografia](security-cryptography.md)\n* [Auth Clients](https://www.yiiframework.com/extension/yiisoft/yii2-authclient/doc/guide)\n* [Melhores Práticas](security-best-practices.md)\n\n\nCache\n-------\n\n* [Visão Geral](caching-overview.md)\n* [Cache de Dados](caching-data.md)\n* [Cache de Fragmento](caching-fragment.md)\n* [Cache de Página](caching-page.md)\n* [Cache HTTP](caching-http.md)\n\n\nWeb Services RESTful\n------------------------\n\n* [Introdução](rest-quick-start.md)\n* [Recursos](rest-resources.md)\n* [Controladores (Controllers)](rest-controllers.md)\n* [Roteamento](rest-routing.md)\n* [Formatação de Respostas](rest-response-formatting.md)\n* [Autenticação](rest-authentication.md)\n* [Taxa de Limite de Acessos](rest-rate-limiting.md)\n* [Versionamento](rest-versioning.md)\n* [Tratamento de Erros](rest-error-handling.md)\n\n\nFerramentas de Desenvolvimento\n------------------------------\n\n* [Barra de Ferramentas de Depuração e Depurador](https://www.yiiframework.com/extension/yiisoft/yii2-debug/doc/guide)\n* [Gerando Código usando o Gii](https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide)\n* [Gerando Documentação da API](https://www.yiiframework.com/extension/yiisoft/yii2-apidoc)\n\n\nTestes\n------\n\n* [Visão Geral](test-overview.md)\n* [Configuração do ambiente de testes](test-environment-setup.md)\n* [Testes Unitários](test-unit.md)\n* [Testes Funcionais](test-functional.md)\n* [Testes de Aceitação](test-acceptance.md)\n* [Fixtures](test-fixtures.md)\n\n\nTópicos Especiais\n-----------------\n\n* [Template Avançado de Projetos](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide-pt-BR)\n* [Construindo uma Aplicação a Partir do Zero](tutorial-start-from-scratch.md)\n* [Comandos de Console](tutorial-console.md)\n* [Validadores Nativos](tutorial-core-validators.md)\n* [Docker](tutorial-docker.md)\n* [Internacionalização](tutorial-i18n.md)\n* [Envio de E-mails](tutorial-mailing.md)\n* [Ajustes de Desempenho](tutorial-performance-tuning.md)\n* [Ambiente de Hospedagem Compartilhada](tutorial-shared-hosting.md)\n* [Motores de Template (Template Engines)](tutorial-template-engines.md)\n* [Trabalhando com Código de Terceiros](tutorial-yii-integration.md)\n* [Usando Yii como um Microframework](tutorial-yii-as-micro-framework.md)\n\n\n\nWidgets\n-------\n\n* [GridView](https://www.yiiframework.com/doc-2.0/yii-grid-gridview.html)\n* [ListView](https://www.yiiframework.com/doc-2.0/yii-widgets-listview.html)\n* [DetailView](https://www.yiiframework.com/doc-2.0/yii-widgets-detailview.html)\n* [ActiveForm](https://www.yiiframework.com/doc-2.0/guide-input-forms.html#activerecord-based-forms-activeform)\n* [Pjax](https://www.yiiframework.com/doc-2.0/yii-widgets-pjax.html)\n* [Menu](https://www.yiiframework.com/doc-2.0/yii-widgets-menu.html)\n* [LinkPager](https://www.yiiframework.com/doc-2.0/yii-widgets-linkpager.html)\n* [LinkSorter](https://www.yiiframework.com/doc-2.0/yii-widgets-linksorter.html)\n* [Widgets Bootstrap](https://www.yiiframework.com/extension/yiisoft/yii2-bootstrap/doc/guide)\n* [Widgets jQuery UI](https://www.yiiframework.com/extension/yiisoft/yii2-jui/doc/guide)\n\n\nHelpers - Funções Auxiliares\n-------\n\n* [Visão Geral](helper-overview.md)\n* [ArrayHelper](helper-array.md)\n* [Html](helper-html.md)\n* [Url](helper-url.md)\n"
  },
  {
    "path": "docs/guide-pt-BR/caching-data.md",
    "content": "Cache de Dados\n============\n\nO Cache de Dados é responsável por armazenar uma ou mais variáveis PHP em um arquivo temporário para\nser recuperado posteriormente.\nEste também é a fundação para funcionalidades mais avançadas do cache, como [cache de consulta](#query-caching)\ne [cache de página](caching-page.md).\n\nO código a seguir é um padrão de uso típico de cache de dados, onde `$cache` refere-se a\num [Componente de Cache](#cache-components):\n\n```php\n// tentar recuperar $data do cache\n$data = $cache->get($key);\n\nif ($data === false) {\n\n    // $data não foi encontrado no cache, calculá-la do zero\n\n    // armazenar $data no cache para que esta possa ser recuperada na próxima vez\n    $cache->set($key, $data);\n}\n\n// $data é acessível a partir daqui\n```\n\n\n## Componentes de Cache <span id=\"cache-components\"></span>\n\nO cache de dados se baseia nos, então chamados, *Componentes de Cache* que representam vários armazenamentos de cache,\ncomo memória, arquivos, bancos de dados.\n\nComponentes de Cache são normalmente registrados como [componentes de aplicação](structure-application-components.md) para que possam ser globalmente configuráveis e acessíveis. O código a seguir exibe como configurar o componente de aplicação `cache` para usar [memcached](https://memcached.org/) com dois servidores de cache:\n\n```php\n'components' => [\n    'cache' => [\n        'class' => 'yii\\caching\\MemCache',\n        'servers' => [\n            [\n                'host' => 'servidor1',\n                'port' => 11211,\n                'weight' => 100,\n            ],\n            [\n                'host' => 'servidor2',\n                'port' => 11211,\n                'weight' => 50,\n            ],\n        ],\n    ],\n],\n```\n\nVocê pode então, acessar o componente de cache acima usando a expressão `Yii::$app->cache`.\n\nJá que todos os componentes de cache suportam as mesmas APIs, você pode trocar o componente de cache por outro \nreconfigurando-o nas configurações da aplicação sem modificar o código que usa o cache.\nPor exemplo, você pode modificar a configuração acima para usar [[yii\\caching\\ApcCache|APC cache]]:\n\n\n```php\n'components' => [\n    'cache' => [\n        'class' => 'yii\\caching\\ApcCache',\n    ],\n],\n```\n\n> Dica: Você pode registrar múltiplos componentes de cache na aplicação. O componente chamado `cache` é usado \n  por padrão por muitas classes dependentes de cache (ex., [[yii\\web\\UrlManager]]).\n\n\n### Sistemas de Cache Suportados <span id=\"supported-cache-storage\"></span>\n\nYii suporta uma ampla gama de sistemas de cache. A seguir um resumo:\n\n* [[yii\\caching\\ApcCache]]: usa a extensão do PHP [APC](https://www.php.net/manual/pt_BR/book.apcu.php). Esta opção pode ser\n  considerada a mais rápida ao se implementar o cache de uma aplicação densa e centralizada (por exemplo, um\n  servidor, sem balanceadores de carga dedicados, etc.).\n* [[yii\\caching\\DbCache]]: usa uma tabela no banco de dados para armazenar os dados em cache. Para usar este cache\n  você deve criar uma tabela como especificada em [[yii\\caching\\DbCache::cacheTable]].\n* [[yii\\caching\\DummyCache]]: serve apenas como um substituto e não faz nenhum cache na realidade.\n  O propósito deste componente é simplificar o código que precisa checar se o cache está disponível.\n  Por exemplo, durante o desenvolvimento, se o servidor não suporta cache, você pode configurar um\n  componente de cache para usar este cache. Quando o suporte ao cache for habilitado, você pode trocá-lo\n  para o componente correspondente. Em ambos os casos, você pode usar o mesmo código \n  `Yii::$app->cache->get($key)` para tentar recuperar os dados do cache sem se procupar que\n  `Yii::$app->cache` possa ser `null`.\n* [[yii\\caching\\FileCache]]: usa arquivos para armazenar os dados em cache. Este é particularmente indicado \n  para armazenar grandes quantidades de dados como o conteúdo da página.\n* [[yii\\caching\\MemCache]]: usa o [memcache](https://www.php.net/manual/pt_BR/book.memcache.php) do PHP e as extensões\n  [memcached](https://www.php.net/manual/pt_BR/book.memcached.php). Esta opção pode ser considerada a mais rápida\n  ao se implementar o cache em aplicações distribuídas (ex., vários servidores, balanceadores de carga, etc.)\n* [[yii\\redis\\Cache]]: implementa um componente de cache baseado em armazenamento chave-valor \n  [Redis](https://redis.io/) (requer redis versão 2.6.12 ou mais recente).\n* [[yii\\caching\\WinCache]]: usa a extensão PHP [WinCache](https://iis.net/downloads/microsoft/wincache-extension)\n  ([veja também](https://www.php.net/manual/pt_BR/book.wincache.php)).\n\n\n> Dica: Você pode usar vários tipos de cache na mesma aplicação. Uma estratégia comum é usar caches baseados \n  em memória para armazenar dados pequenos mas constantemente usados (ex., dados estatísticos), e usar caches\n  baseados em arquivo ou banco da dados para armazenar dados que são maiores mas são menos usados \n  (ex., conteúdo da página).\n\n\n## APIs De Cache <span id=\"cache-apis\"></span>\n\nTodos os componentes de caches estendem a mesma classe base [[yii\\caching\\Cache]] e assim suportam as seguintes APIs:\n\n* [[yii\\caching\\Cache::get()|get()]]: recupera um registro no cache usando uma chave específica. \n  Retorna `false` caso o item não for encontrado no cache ou se o registro está expirado/invalidado.\n* [[yii\\caching\\Cache::set()|set()]]: armazena um registro no cache identificado por uma chave.\n* [[yii\\caching\\Cache::add()|add()]]: armazena um registro no cache identificado por uma chave se a chave não \n  for encontrada em cache.\n* [[yii\\caching\\Cache::mget()|mget()]]: recupera múltiplos registros do cache com as chaves especificadas.\n* [[yii\\caching\\Cache::mset()|mset()]]: armazena múltiplos registros no cache. Cada item identificado por uma chave.\n* [[yii\\caching\\Cache::madd()|madd()]]: armazena múltiplos registros no cache. Cada item identificado por uma chave.\n  Se a chave já existir em cache, o registro é ignorado.\n* [[yii\\caching\\Cache::exists()|exists()]]: retorna se a chave específica é encontrada no cache.\n* [[yii\\caching\\Cache::delete()|delete()]]: remove um registro do cache identificado por uma chave.\n* [[yii\\caching\\Cache::flush()|flush()]]: remove todos os registros do cache.\n\n> Observação: Não armazene o valor boleano `false` diretamente, porque o método [[yii\\caching\\Cache::get()|get()]] retorna `false`para indicar que o registro não foi encontrado em cache. Você pode armazena `false` em um array e armazenar este em cache para evitar este problema.\n\nAlguns tipos de cache como MemCache, APC, suportam recuperar em lote múltiplos registros em cache, o que poder reduzir\no custo de processamento envolvido ao recuperar informações em cache. As APIs [[yii\\caching\\Cache::mget()|mget()]]\ne [[yii\\caching\\Cache::madd()|madd()]] são equipadas para explorar esta funcionalidade. Em caso do cache em questão não suportar esta funcionalidade, ele será simulado.\n\nComo [[yii\\caching\\Cache]] implementa `ArrayAccess`, um componente de cache pode ser usado como um array. A seguir alguns exemplos:\n\n```php\n$cache['var1'] = $valor1;  // equivalente a: $cache->set('var1', $valor1);\n$valor2 = $cache['var2'];  // equivalente a: $valor2 = $cache->get('var2');\n```\n\n\n### Chaves de Cache <span id=\"cache-keys\"></span>\n\nCada registro armazenado no cache é identificado por uma chave única. Quando você armazena um registro em cache,\nvocê deve especificar uma chave para ele. Mais tarde, quando você quiser recuperar o registro do cache, você deve \nfornecer a chave correspondente.\n\nVocê pode usar uma string ou um valor arbitrário como uma chave do cache. Quando a chave não for uma string, ela será\nautomaticamente serializada em uma string.\n\nUma estratégia comum ao definir uma chave de cache é incluir todos os fatores determinantes na forma de um array.\nPor exemplo, [[yii\\db\\Schema]] usa a seguinte chave para armazenar a informação de um esquema de uma tabela do banco\nde dados.\n\n```php\n[\n    __CLASS__,              // nome da classe do esquema\n    $this->db->dsn,         // nome da fonte de dados da conexão BD\n    $this->db->username,    // usuario da conexão BD\n    $name,                  // nome da tabela\n];\n```\n\nComo você pode ver, a chave inclui toda a informação necessária para especificar unicamente uma tabela do banco.\n\nQuando o cache de diferentes aplicações é armazenado no mesmo lugar, é aconselhável especificar, para cada \naplicação, um prefixo único a chave do cache para evitar conflitos entre elas. Isto pode ser feito ao configurar\na propriedade [[yii\\caching\\Cache::keyPrefix]]. Por exemplo, na configuração da aplicação você pode escrever o seguinte código:\n\n```php\n'components' => [\n    'cache' => [\n        'class' => 'yii\\caching\\ApcCache',\n        'keyPrefix' => 'minhaapp',       // um prefíxo de chave único\n    ],\n],\n```\nPara assegurar interoperabilidade, apenas caracteres alfanuméricos devem ser usados.\n\n\n### Expiração de Cache <span id=\"cache-expiration\"></span>\n\nUm registro armazenado em cache não será apagado a menos que seja removido por alguma política aplicada\n(por exemplo, espaço determinado para o cache esteja cheio e os registros mais antigos sejam removidos). Para alterar\nestes comportamento, você pode fornecer um parâmetro de expiração ao chamar [[yii\\caching\\Cache::set()|set()]]\npara armazenar um registro. O parâmetro indica por quantos segundos um registro pode permanecer validado no cache.\nQuando você chamar [[yii\\caching\\Cache::get()|get()]] para recuperar um registro, se o tempo de expiração houver passado, o método retornará `false`, indicando que o registro não foi encontrado no cache. Por exemplo,\n\n```php\n// Manter o registro em cache por até 45 segundos\n$cache->set($chave, $registro, 45);\n\nsleep(50);\n\n$data = $cache->get($chave);\nif ($registro === false) {\n    // $registro está expirado ou não foi encontrado no sistema\n}\n```\n\n\n### Dependências de Cache <span id=\"cache-dependencies\"></span>\n\nAlém da definição de expiração, um registro em cache pode também ser invalidado por mudanças nas, então chamadas,\n*dependências de cache*. Por exemplo, [[yii\\caching\\FileDependency]] representa a dependência na data de modificação\nde um arquivo.\nQuando esta dependência muda, significa que o arquivo correspondente foi mudado. Como um resultado, qualquer \narquivo com data ultrapassada encontrado no cache deve ser invalidado e a chamada de [[yii\\caching\\Cache::get()|get()]]\nretornará `false`.\n\nDependências de Cache são representadas como objetos de classes dependentes de [[yii\\caching\\Dependency]]. Quando você chamar [[yii\\caching\\Cache::set()|set()]] para armazenar um registro em cache, você pode passar um objeto de dependência. Por exemplo,\n\n```php\n// Criar uma dependência sobre a data de modificação do arquivo exemplo.txt.\n$dependencia = new \\yii\\caching\\FileDependency(['fileName' => 'exemplo.txt']);\n\n// O registro expirará em 30 segundos.\n// Ele também pode ser invalidado antes, caso o exemplo.txt seja modificado.\n$cache->set($key, $data, 30, $dependency);\n\n// O cache verificará se o registro expirou.\n// E também verificará se a dependência associada foi alterada.\n// Ele retornará false se qualquer uma dessas condições seja atingida.\n$data = $cache->get($key);\n```\nAbaixo um sumário das dependências de cache disponíveis:\n\n- [[yii\\caching\\ChainedDependency]]: a dependência muda caso alguma das dependências na cadeia for alterada.\n- [[yii\\caching\\DbDependency]]: a dependência muda caso o resultado da consulta especificada pela instrução SQL seja\n  alterado.\n- [[yii\\caching\\ExpressionDependency]]: a dependência muda se o resultado da expressão PHP especificada for alterado.\n- [[yii\\caching\\CallbackDependency]]: a dependência é alterada se o resultado do callback PHP especificado for alterado..\n- [[yii\\caching\\FileDependency]]: A dependência muda se a data da última alteração do arquivo for alterada.\n- [[yii\\caching\\TagDependency]]: associa um registro em cache com uma ou múltiplas tags. Você pode invalidar os\n  registros em cache com a tag especificada ao chamar [[yii\\caching\\TagDependency::invalidate()]].\n\n\n## Cache de Consulta <span id=\"query-caching\"></span>\n\nCache de consulta é uma funcionalidade especial de cache construída com o cache de dados. Ela é fornecida para armazenar em cache consultas ao banco de dados.\n\nO cache de consulta requer uma [[yii\\db\\Connection|conexão ao banco de dados]] e um [componente de aplicação](#cache-components) de `cache` válido.\nA seguir uma utilização básica do cache de consulta, assumindo que `$bd` é uma instância de [[yii\\db\\Connection]]:\n\n```php\n$resultado = $bd->cache(function ($bd) {\n  \n    // O resultado da consulta SQL será entregue pelo cache\n    // se o cache de consulta estiver sido habilitado e o resultado da consulta for encontrado em cache\n    return $bd->createCommand('SELECT * FROM clientes WHERE id=1')->queryOne();\n\n});\n```\n\nCache de consulta pode ser usado pelo [DAO](db-dao.md) da mesma forma que um [ActiveRecord](db-active-record.md):\n\n```php\n$resultado = Cliente::getDb()->cache(function ($bd) {\n    return Cliente::find()->where(['id' => 1])->one();\n});\n```\n\n> Informação: Alguns SGBDs (ex., [MySQL](https://dev.mysql.com/doc/refman/5.6/en/query-cache.html))\n  também suportam o cache de consulta no servidor. Você pode escolher usá-lo ao invés do mecanismo de cache \n  de consulta.\n  O cache de consulta descrito acima tem a vantagem de poder especificar dependências de cache flexíveis \n  e assim sendo potencialmente mais eficiente.\n\n\n### Configurações <span id=\"query-caching-configs\"></span>\n\nCache de consulta tem três opções configuráveis globalmente através de [[yii\\db\\Connection]]:\n\n* [[yii\\db\\Connection::enableQueryCache|enableQueryCache]]: Configura se o cache de consulta está habilitado.\n  O padrão é `true`. Observe que para ter efetivamente o cache de consulta habilitado, você também deve ter um cache válido como especificado por [[yii\\db\\Connection::queryCache|queryCache]].\n* [[yii\\db\\Connection::queryCacheDuration|queryCacheDuration]]: representa o número de segundos que o resultado de uma  \n  consulta pode se manter válido em cache. Você pode usar 0 para indicar que o resultado da consulta deve permanecer no\n  cache indefinidamente. Este é o valor padrão usado quando [[yii\\db\\Connection::cache()]] é chamado sem nenhuma\n  especificação de duração.\n* [[yii\\db\\Connection::queryCache|queryCache]]: representa a ID do componente de aplicação de cache.\n  Seu padrão é `'cache'`. Cache de consulta é habilitado apenas se houver um componente de aplicacão de cache válido.\n\n\n### Usando o Cache de Consulta <span id=\"query-caching-usages\"></span>\n\nVocê pode usar [[yii\\db\\Connection::cache()]] se tiver múltiplas consultas SQL que precisam ser armazenadas no\ncache de consulta. Utilize da seguinte maneira,\n\n```php\n$duracao = 60;     // armazenar os resultados em cache por 60 segundos\n$dependencia = ...;  // alguma dependência opcional\n\n$result = $db->cache(function ($db) {\n\n    // ... executar consultas SQL aqui ...\n\n    return $result;\n\n}, $duracao, $dependencia);\n```\n\nQualquer consulta SQL na função anônima será armazenada em cache pela duração especificada com a dependência informada. Se o resultado da consulta for encontrado em cache e for válido, a consulta não será necessária e o \nresultado será entregue pelo cache. Se você não especificar o parâmetro `$duracao`, o valor de \n[[yii\\db\\Connection::queryCacheDuration|queryCacheDuration]] será usado.\n\nOcasionalmente em `cache()`, você pode precisar desabilitar o cache de consulta para algumas consultas em particular. Você pode usar [[yii\\db\\Connection::noCache()]] neste caso.\n\n```php\n$result = $db->cache(function ($db) {\n\n    // consultas SQL que usarão o cache de consulta\n\n    $db->noCache(function ($db) {\n\n        // consultas SQL que não usarão o cache de consulta\n\n    });\n\n    // ...\n\n    return $result;\n});\n```\n\nSe você apenas deseja usar o cache de consulta para apenas uma consulta, você pode chamar [[yii\\db\\Command::cache()]]\nao construir o comando. Por exemplo,\n\n```php\n// usar cache de consulta e definir duração do cache para 60 segundos\n$customer = $db->createCommand('SELECT * FROM customer WHERE id=1')->cache(60)->queryOne();\n```\n\nVocê pode também usar [[yii\\db\\Command::noCache()]] para desabilitar o cache de consulta para um único comando, Por exemplo,\n\n```php\n$result = $db->cache(function ($db) {\n\n    // consultas SQL que usam o cache de consulta\n\n    // não usar cache de consulta para este comando\n    $customer = $db->createCommand('SELECT * FROM customer WHERE id=1')->noCache()->queryOne();\n\n    // ...\n\n    return $result;\n});\n```\n\n\n### Limitações <span id=\"query-caching-limitations\"></span>\n\nO cache de consulta não funciona com resultados de consulta que contêm <i>manipuladores de recursos</i> (resource handlers). \nPor exemplo, ao usar o tipo de coluna `BLOB` em alguns SGBDs, o resultado da consulta retornará um <i>manipulador de recurso</i> (resource handler) para o registro na coluna.\n\nAlguns armazenamentos em cache têm limitações de tamanho. Por exemplo, memcache limita o uso máximo de espaço de 1MB para cada registro. Então, se o tamanho do resultado de uma consulta exceder este limite, o cache falhará. \n"
  },
  {
    "path": "docs/guide-pt-BR/caching-fragment.md",
    "content": "Cache de Fragmentos\n================\n\nCache de fragmentos é responsável por armazenar em cache um fragmento de uma página Web. Por exemplo, se uma\npágina exibe o sumario de vendas anuais em uma tabela, você pode armazenar esta tabela em cache para eliminar\no tempo necessário para gerar esta tabela em cada requisição. O Cache de Fragmentos é construído a partir \ndo [cache de dados](caching-data.md).\n\nPara usar o cache de fragmentos, utilize o seguinte modelo em uma [view](structure-views.md):\n\n```php\nif ($this->beginCache($id)) {\n\n    // ... gere o conteúdo aqui ...\n\n    $this->endCache();\n}\n```\nOu seja, encapsule a lógica de geração do conteúdo entre as chamadas [[yii\\base\\View::beginCache()|beginCache()]]\ne [[yii\\base\\View::endCache()|endCache()]]. Se o conteúdo for encontrado em cache, [[yii\\base\\View::beginCache()|beginCache()]] renderizará o conteúdo em cache e retornará falso, e assim não executará a lógica de geração de conteúdo.\nCaso contrário, o conteúdo será gerado, e quando [[yii\\base\\View::endCache()|endCache()]] for chamado, o conteúdo gerado será capturado e armazenado no cache.\n\nAssim como [cache de dados](caching-data.md), uma `$id` única é necessária para identificar um conteúdo no cache.\n\n\n## Opções do Cache <span id=\"caching-options\"></span>\n\nVocê poderá especificar opções adicionais sobre o cache de fragmentos passando um array de opções como o segundo parâmetro do método [[yii\\base\\View::beginCache()|beginCache()]]. Por trás dos panos, este array de opções será usado para configurar um widget [[yii\\widgets\\FragmentCache]] que implementa, por sua vez, a funcionalidade de cache de fragmentos.\n\n\n### Duração <span id=\"duration\"></span>\n\nTalvez a opção que tenha mais frequência de uso seja a \n[[yii\\widgets\\FragmentCache::duration|duration]].\nEla especifica por quantos segundos o conteúdo pode permanecer válido no cache. O código a seguir armazena em cache o fragmento do conteúdo por até uma hora:\n\n```php\nif ($this->beginCache($id, ['duration' => 3600])) {\n\n    // ... gerar o conteúdo aqui ...\n\n    $this->endCache();\n}\n```\n\nSe a opção não for definida, o padrão definido é 60, que significa que o conteúdo em cache expirará em 60 segundos.\n\n\n### Dependências <span id=\"dependencies\"></span>\n\nAssim como [cache de dados](caching-data.md#cache-dependencies), o fragmento de conteúdo sendo armazenado em cache pode ter dependências.\nPor exemplo, o conteúdo de um *post* sendo exibido depende de ele ter sido ou não modificado.\n\nPara especificar uma dependência, defina a opção [[yii\\widgets\\FragmentCache::dependency|dependency]], que pode\nser um objeto [[yii\\caching\\Dependency]] ou um array de configuração para criar um objeto de dependência.\nO código a seguir especifica que o conteúdo do fragmento depende do valor da coluna `atualizado_em`:\n\n```php\n$dependency = [\n    'class' => 'yii\\caching\\DbDependency',\n    'sql' => 'SELECT MAX(atualizado_em) FROM post',\n];\n\nif ($this->beginCache($id, ['dependency' => $dependency])) {\n\n    // ... gere o conteúdo aqui ...\n\n    $this->endCache();\n}\n```\n\n\n### Variações <span id=\"variations\"></span>\n\nO conteúdo armazenado em cache pode variar de acordo com alguns parâmetros. Por exemplo, para uma aplicação Web que suporta múltiplos idiomas, a mesma porção de código de uma view pode gerar conteúdo diferente para cada idioma. \nDesta forma, você pode desejar que o código em cache exibisse um conteúdo diferente para a idioma exibido na requisição.\n\nPara especificar variações de cache, defina a opção [[yii\\widgets\\FragmentCache::variations|variations]],\nque pode ser um array de valores escalares, cada um representando um fator de variação particular.\nPor exemplo, para fazer o conteúdo em cache variar em função da linguagem, você pode usar o seguinte código:\n\n```php\nif ($this->beginCache($id, ['variations' => [Yii::$app->language]])) {\n\n    // ... gerar o conteúdo aqui ...\n\n    $this->endCache();\n}\n```\n\n\n### *Cache Alternante* (Toggling Caching) <span id=\"toggling-caching\"></span>\n\nEm alguns casos, você pode precisar habilitar o cache de fragmentos somente quando certas condições se aplicam.\nPor exemplo, para uma página exibindo um formulário, e você deseja armazenar o formulário em cache apenas na\nprimeira requisição (via requisição GET). Qualquer exibição subsequente (via requisição POST) ao formulário não\ndeve ser armazenada em cache porque o formulário pode conter os dados submetidos pelo usuário. Para assim fazê-lo,\nvocê pode definir a opção [[yii\\widgets\\FragmentCache::enabled|enabled]], da seguinte maneira:\n\n```php\nif ($this->beginCache($id, ['enabled' => Yii::$app->request->isGet])) {\n\n    // ... gerar conteúdo aqui ...\n\n    $this->endCache();\n}\n```\n\n\n## *Cache Aninhado* (Nested Caching) <span id=\"nested-caching\"></span>\n\nCache de fragmentos pode ser aninhado. Isto é, um fragmento em cache pode estar contido em outro fragmento que também está em cache. Por exemplo, os comentários estão sendo armazenados em um cache de fragmento inserido em conjunto com o conteúdo do *post* em outro cache de fragmento. O código a seguir exibe como dois caches de fragmento podem ser aninhados.\n\n```php\nif ($this->beginCache($id1)) {\n\n    // ...lógica de geração de conteúdo...\n\n    if ($this->beginCache($id2, $options2)) {\n\n        // ...lógica de geração de conteúdo...\n\n        $this->endCache();\n    }\n\n    // ...lógica de geração de conteúdo...\n\n    $this->endCache();\n}\n```\n\nDiferentes opções de cache podem ser definidas para os caches aninhados. Por exemplo, o cache interior e o cache\nexterior podem ter tempos diferentes de expiração. Mesmo quando um registro no cache exterior é invalidado, o cache\ninterior ainda pode permanecer válido. Entretanto, o inverso não pode acontecer. Se o cache exterior é identificado\ncomo validado, ele continuará a servir a mesma copia em cache mesmo após o conteúdo no cache interior ter sido\ninvalidado. Desta forma, você deve ser cuidadoso ao definir durações ou dependências para os caches aninhados, \njá que os fragmentos interiores ultrapassados podem ser mantidos no fragmento externo.\n\n\n## Conteúdo Dinâmico <span id=\"dynamic-content\"></span>\n\nAo usar o cache de fragmentos, você pode encontrar-se na situação em que um grande fragmento de conteúdo é\nrelativamente estático exceto por alguns lugares. Por exemplo, um cabeçalho de uma página pode exibir\na barra do menu principal junto ao nome do usuário logado. Outro problema é que o conteúdo sendo armazenado em cache\npode conter código PHP que deve ser executado para cada requisição (ex., o código para registrar um *pacote de recursos estáticos* (asset bundles)). Ambos os problemas podem ser resolvidos com a funcionalidade, então chamada de *Conteúdos Dinâmicos*.\n\nUm conteúdo dinâmico compreende um fragmento de uma saída que não deveria ser armazenada em cache mesmo que esteja encapsulada em um cache de fragmento. Para fazer o conteúdo dinâmico indefinidamente, este deve ser gerado pela execução de\nalgum código PHP em cada requisição, mesmo que o conteúdo encapsulado esteja sendo servido do cache. \n\nVocê pode chamar [[yii\\base\\View::renderDynamic()]] dentro de um cache de fragmento para inserir conteúdo dinâmico no local desejado, como o seguinte,\n\n```php\nif ($this->beginCache($id1)) {\n\n    // ...lógica de geração de conteúdo...\n\n    echo $this->renderDynamic('return Yii::$app->user->identity->name;');\n\n    // ...lógica de geração de conteúdo...\n\n    $this->endCache();\n}\n```\n\nO método [[yii\\base\\View::renderDynamic()|renderDynamic()]] recebe uma porção de código PHP como parâmetro.\nO valor retornado pelo código PHP é tratado como conteúdo dinâmico. O mesmo código PHP será executado em\ncada requisição, não importando se este esteja encapsulado em um fragmento em cache ou não.\n"
  },
  {
    "path": "docs/guide-pt-BR/caching-http.md",
    "content": "Cache HTTP\n============\n\nAlém do cache no servidor que nós descrevemos nas seções anteriores, aplicações Web pode também aproveitar-se\nde cache no cliente para economizar o tempo na montagem e transmissão do mesmo conteúdo de uma página.\n\nPara usar o cache no cliente, você poderá configurar [[yii\\filters\\HttpCache]] como um filtro de ações de um controller ao qual o resultado de sua renderização possa ser armazenado em cache no navegador do cliente. A classe [[yii\\filters\\HttpCache|HttpCache]] funciona apenas para requisições `GET` e `HEAD`. Ele pode manipular três tipos de cache relacionados a cabeçalhos HTTP para estas requisições:\n\n* [[yii\\filters\\HttpCache::lastModified|Last-Modified]]\n* [[yii\\filters\\HttpCache::etagSeed|Etag]]\n* [[yii\\filters\\HttpCache::cacheControlHeader|Cache-Control]]\n\n\n## Cabeçalho `Last-modified` <span id=\"last-modified\"></span>\n\nO cabeçalho `Last-modified` usa uma data (timestamp) para indicar se a página foi modificada desde que o cliente a armazenou em cache.\n\nVocê pode configurar a propriedade [[yii\\filters\\HttpCache::lastModified]] para permitir enviar o cabeçalho `Last-modified`. A propriedade deve ser um PHP *callable* retornando uma data (UNIX timestamp) sobre o tempo de modificação. A declaração do PHP *callable* deve ser a seguinte,\n\n```php\n/**\n * @param Action $action O Objeto da ação que está sendo manipulada no momento\n * @param array $params o valor da propriedade \"params\"\n * @return int uma data(timestamp) UNIX timestamp representando o tempo da\n * última modificação na página\n */\nfunction ($action, $params)\n```\n\nA seguir um exemplo que faz o uso do cabeçalho `Last-modified`:\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\HttpCache',\n            'only' => ['index'],\n            'lastModified' => function ($action, $params) {\n                $q = new \\yii\\db\\Query();\n                return $q->from('post')->max('updated_at');\n            },\n        ],\n    ];\n}\n```\n\nO código acima afirma que o cache HTTP deve ser habilitado apenas para a ação `index`. Este deve\ngerar um cabeçalho HTTP `last-modified` baseado na última data de alteração dos*posts*. Quando um\nnavegador visitar a página `index` pela primeira vez, a página será gerada no servidor e enviada para\no navegador; Se o navegador visitar a mesma página novamente e não houver modificação dos *posts* durante este\nperíodo, o servidor não irá remontará a página e o navegador usará a versão em cache no cliente.\nComo resultado, a renderização do conteúdo na página não será executada no servidor.\n\n\n## Cabeçalho `ETag` <span id=\"etag\"></span>\n\nO cabeçalho *\"Entity Tag\"* (ou `ETag` abreviado) usa um hash para representar o conteúdo de uma página.\nSe a página for alterada, o hash irá mudar também. Ao comparar o hash mantido no cliente com o hash gerado no\nservidor, o cache pode determinar se a página foi alterada e se deve ser retransmitida.\n\nVocê pode configurar a propriedade [[yii\\filters\\HttpCache::etagSeed]] para habilitar o envio do cabeçalho `ETag`.\nA propriedade deve ser um PHP *callable* retornando um conteúdo ou dados serializados (chamado também de *seed* na referência americana) para a geração do hash do Etag. A declaração do PHP *callable* deve ser como a seguinte,\n\n```php\n/**\n * @param Action $action o objeto da ação que está sendo manipulada no momento\n * @param array $params o valor da propriedade \"params\"\n * @return string uma string usada como um conteúdo para gerar o hash ETag\n */\nfunction ($action, $params)\n```\n\nA seguir um exemplo que faz o uso do cabeçalho `ETag`:\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\HttpCache',\n            'only' => ['view'],\n            'etagSeed' => function ($action, $params) {\n                $post = $this->findModel(\\Yii::$app->request->get('id'));\n                return serialize([$post->title, $post->content]);\n            },\n        ],\n    ];\n}\n```\n\nO código acima afirma que o cache de HTTP deve ser habilitado apenas para a ação `view`. Este deve\ngerar um cabeçalho HTTP `ETag` baseado no título e no conteúdo do *post* requisitado. Quando um navegador visitar\na página `view` pela primeira vez, a página será gerada no servidor e enviada para ele; Se o navegador visitar\na mesma página novamente e não houver alteração para o título e o conteúdo do *post*, o servidor não remontará\na página e o navegador usará a versão que estiver no cache do cliente. Como resultado, a renderização do\nconteúdo na página não será executada no servidor.\n\nETags permite estratégias mais complexas e/ou mais precisas do que o uso do cabeçalho de `Last-modified`.\nPor exemplo, um ETag pode ser invalidado se o site tiver sido alterado para um novo tema.\n\nGerações muito complexas de ETags podem contrariar o propósito de se usar `HttpCache` e introduzir despesas desnecessárias ao processamento, já que eles precisam ser reavaliados a cada requisição.\nTente encontrar uma expressão simples que invalida o cache se o conteúdo da página for modificado.\n\n> Observação: Em concordância com a [RFC 7232](https://datatracker.ietf.org/doc/html/rfc7232#section-2.4), o\n  `HttpCache` enviará os cabeçalhos `ETag` e `Last-Modified` se ambos forem assim configurados.\n  E se o cliente enviar ambos o cabeçalhos `If-None-Match` e `If-Modified-Since`, apenas o primeiro será\n  respeitado.\n\n\n## Cabeçalho `Cache-Control` <span id=\"cache-control\"></span>\n\nO cabeçalho `Cache-Control` especifica políticas de cache gerais para as páginas. Você pode enviá-lo configurando a propriedade [[yii\\filters\\HttpCache::cacheControlHeader]] com o valor do cabeçalho. Por padrão, o seguinte cabeçalho será enviado:\n\n```\nCache-Control: public, max-age=3600\n```\n\n\n## Limitador de Cache na Sessão <span id=\"session-cache-limiter\"></span>\n\nQuando uma página usa sessão, o PHP automaticamente enviará alguns cabeçalhos HTTP relacionados ao cache\ncomo especificado na configuração `session.cache_limiter` do PHP.INI. Estes cabeçalhos podem interferir ou\ndesabilitar o cache que você deseja do `HttpCache`. Para prevenir-se deste problema, por padrão, o `HttpCache`\ndesabilitará o envio destes cabeçalhos automaticamente. Se você quiser modificar estes comportamentos, deve\nconfigurar a propriedade [[yii\\filters\\HttpCache::sessionCacheLimiter]]. A propriedade pode receber um valor string, como: `public`, `private`, `private_no_expire` e `nocache`. Por favor, consulte o manual do\nPHP sobre [session_cache_limiter()](https://www.php.net/manual/pt_BR/function.session-cache-limiter.php)\npara mais explicações sobre estes valores.\n\n\n## Implicações para SEO <span id=\"seo-implications\"></span>\n\nOs bots do motor de buscas tendem a respeitar cabeçalhos de cache. Já que alguns rastreadores têm um limite sobre a quantidade de páginas por domínio que eles processam em um certo espaço de tempo, introduzir cabeçalhos de cache podem\najudar na indexação do seu site já que eles reduzem o número de páginas que precisam ser processadas.\n\n"
  },
  {
    "path": "docs/guide-pt-BR/caching-overview.md",
    "content": "Cache\n=======\n\nCache é uma maneira simples e eficiente de melhorar o desempenho de uma aplicação Web. Ao gravar dados relativamente\nestáticos em cache e servindo os do cache quando requisitados, a aplicação economiza o tempo que seria necessário para renderizar as informações do zero todas as vezes.\n\nCache pode ocorrer em diferentes níveis e locais em uma aplicação Web. No servidor, no baixo nível,\ncache pode ser usado para armazenar dados básicos, como a informação de uma lista de artigos mais recentes trazidos\ndo banco de dados; e no alto nível, cache pode ser usado para armazenar fragmentos ou páginas Web inteiras, como o\nresultado da renderização dos artigos mais recentes. No cliente, cache HTTP pode ser usado para manter o conteúdo da última página acessada no cache do navegador.\n\nYii suporta todos os quatro métodos de cache:\n* [Cache de Dados](caching-data.md)\n* [Cache de Fragmento](caching-fragment.md)\n* [Cache de Página](caching-page.md)\n* [Cache de HTTP](caching-http.md)\n"
  },
  {
    "path": "docs/guide-pt-BR/caching-page.md",
    "content": "Cache de Página\n============\n\nO Cache de página é responsável por armazenar em cache o conteúdo de uma página inteira no servidor. Mais tarde, \nquando a mesma página é requisitada novamente, seu conteúdo será servido do cache em vez de ela ser gerada novamente\ndo zero.\n\nO Cache de página é implementado pela classe [[yii\\filters\\PageCache]], um [filtro de ações](structure-filters.md).\nEsta pode ser usada da seguinte maneira em uma classe de controller:\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\PageCache',\n            'only' => ['index'],\n            'duration' => 60,\n            'variations' => [\n                \\Yii::$app->language,\n            ],\n            'dependency' => [\n                'class' => 'yii\\caching\\DbDependency',\n                'sql' => 'SELECT COUNT(*) FROM post',\n            ],\n        ],\n    ];\n}\n```\n\nO código acima afirma que o cache da página deve ser usado apenas para a ação `index`; o conteúdo da página deve \nser armazenado em cache por, no máximo, 60 segundos e deve variar de acordo com a linguagem atual da aplicação;\ne esta página em cache deve ser invalidada se o número total de *posts* for alterado.\n\nComo você pode observar, o cache de página é bastante similar ao [cache de fragmentos](caching-fragment.md). Ambos suportam opções como `duration`, `dependencies`, `variations`, e `enabled`. Sua principal diferença é que o cache de página é implementado como um [filtro de ações](structure-filters.md) enquanto que o cache de fragmentos é um [widget](structure-widgets.md).\n\nVocê pode usar o [cache de fragmentos](caching-fragment.md) ou [conteúdo dinâmico](caching-fragment.md#dynamic-content)\nem conjunto com o cache de página.\n\n"
  },
  {
    "path": "docs/guide-pt-BR/concept-aliases.md",
    "content": "Aliases (Apelidos)\n=======\n\nAliases são usados para representar caminhos de arquivos ou URLs de forma que você não precise acoplar o código usando caminhos absolutos ou URLs em seu projeto. Um alias deve começar com o caractere `@` para se diferenciar de um caminho de arquivo normal ou URL. O Yii já possui vários aliases predefinidos disponíveis. \nPor exemplo, o alias `@yii` representa o local em que o framework Yii foi instalado; `@web` representa a URL base para a aplicação que está sendo executada no momento. \n\n\nDefinindo Aliases <span id=\"defining-aliases\"></span>\n----------------\n\nVocê pode definir um alias para um caminho de arquivo ou URL chamando [[Yii::setAlias()]]:\n\n```php\n// um alias de um caminho de arquivo\nYii::setAlias('@foo', '/caminho/para/foo');\n\n// um alias de uma URL\nYii::setAlias('@bar', 'https://www.exemplo.com.br');\n```\n\n> Observação: O caminho do arquivo ou URL sendo *apelidado* (aliased) *não* necessariamente refere-se a um arquivo ou a recursos existentes.\n\nDado um alias definido, você pode derivar um novo alias (sem a necessidade de chamar [[Yii::setAlias()]]) apenas acrescentando uma barra `/` seguido de um ou mais segmentos de caminhos de arquivos. Os aliases definidos através de [[Yii::setAlias()]] tornam-se o *alias raiz* (root alias), enquanto que aliases derivados dele, tornam-se *aliases derivados*. Por exemplo, `@foo` é um *alias raiz* (root alias), enquanto `@foo/bar/arquivo.php` é um alias derivado.\n\nVocê pode definir um alias usando outro alias (tanto raiz quanto derivado):\n\n```php\nYii::setAlias('@foobar', '@foo/bar');\n```\n\nAliases raiz são normalmente definidos durante o estágio de [inicialização](runtime-bootstrapping.md).\nPor exemplo, você pode chamar [[Yii::setAlias()]] no [script de entrada](structure-entry-scripts.md).\nPor conveniência, as [aplicações](structure-applications.md) difinem uma propriedade `aliases` que você pode configurar na [configuração](concept-configurations.md) da aplicação:\n\n```php\nreturn [\n    // ...\n    'aliases' => [\n        '@foo' => '/caminho/para/foo',\n        '@bar' => 'https://www.exemplo.com.br',\n    ],\n];\n```\n\n\nResolvendo Aliases <span id=\"resolving-aliases\"></span>\n-----------------\n\nVocê pode chamar [[Yii::getAlias()]] em um alias raiz para resolver o caminho de arquivo ou URL que ele representa.\nO mesmo método pode resolver também um alias derivado em seu caminho de arquivo ou URL correspondente.\n\n```php\necho Yii::getAlias('@foo');               // exibe: /caminho/para/foo\necho Yii::getAlias('@bar');               // exibe: https://www.example.com\necho Yii::getAlias('@foo/bar/arquivo.php');  // exibe: /caminho/para/foo/bar/arquivo.php\n```\n\nO caminho/URL representado por um alias derivado é determinado substituindo a parte do alias raiz com o seu caminho/URL correspondente.\n\n> Observação: O método [[Yii::getAlias()]] não checa se o caminho/URL resultante refere-se a um arquivo ou recursos existentes.\n\nUm alias raiz pode também conter caracteres de barra `/`. O método [[Yii::getAlias()]] é inteligente o suficiente\npara descobrir que parte de um alias é um alias raiz e assim determina o caminho de arquivo ou URL correspondente:\n\n```php\nYii::setAlias('@foo', '/caminho/para/foo');\nYii::setAlias('@foo/bar', '/caminho2/bar');\nYii::getAlias('@foo/test/arquivo.php');  // exibe: /caminho/para/foo/test/arquivo.php\nYii::getAlias('@foo/bar/arquivo.php');   // exibe: /caminho2/bar/arquivo.php\n```\n\nSe `@foo/bar` não estivesse definido como um alias raiz, a última chamada exibiria `/caminho/para/foo/bar/arquivo.php`.\n\n\nUsando Aliases <span id=\"using-aliases\"></span>\n-------------\n\nAliases são reconhecidos em muitos lugares no Yii sem a necessidade de chamar [[Yii::getAlias()]] para convertê-los em caminhos e URLs. Por exemplo, [[yii\\caching\\FileCache::cachePath]] pode aceitar tanto um caminho de arquivo quanto um alias representando o caminho do arquivo, graças ao prefíxo `@` que nos permite diferenciar um caminho de arquivo de um alias.\n\n```php\nuse yii\\caching\\FileCache;\n\n$cache = new FileCache([\n    'cachePath' => '@runtime/cache',\n]);\n```\n\nPor favor, consulte a documentação da API para saber se o parâmetro de uma propriedade ou método suporta aliases.\n\n\nAliases Predefinidos <span id=\"predefined-aliases\"></span>\n------------------\n\nO Yii já predefine uma gama de aliases para referenciar facilmente caminhos de arquivos e URLs comumente usados:\n\n- `@yii`, o diretório onde o arquivo `BaseYii.php` está localizado (também chamado de diretório do framework).\n- `@app`, o [[yii\\base\\Application::basePath|caminho base]] da aplicação sendo executada no momento.\n- `@runtime`, o [[yii\\base\\Application::runtimePath|caminho runtime]] da aplicação sendo executada no momento.\n- `@webroot`, o diretório webroot da aplicação sendo executada no momento. Este é determinado baseado no diretório\n   contendo o [script de entrada](structure-entry-scripts.md).\n- `@web`, a URL base da aplicacão sendo executada no momento. Esta tem o mesmo valor de [[yii\\web\\Request::baseUrl]].\n- `@vendor`, o [[yii\\base\\Application::vendorPath|caminho da pasta vendor do Composer]]. \n   Seu padrão é `@app/vendor`.\n- `@bower`, o caminho raiz que contém os [pacotes bower](https://bower.io/). Seu padrão é `@vendor/bower`.\n- `@npm`, o caminho raiz que contém [pacotes npm](https://www.npmjs.com/). Seu padrão é `@vendor/npm`.\n\nO alias `@yii` é definido quando você inclui o arquivo `Yii.php` em seu [script de entrada](structure-entry-scripts.md).\nO resto dos aliases são definidos no construtor da aplicação ao aplicar a [configuração](concept-configurations.md) da aplicação.\n\n\nAliases para Extensões <span id=\"extension-aliases\"></span>\n-----------------\n\nUm alias é automaticamente definido para cada [extensão](structure-extensions.md) que for instalada através do Composer.\nCada alias é nomeado a partir do namespace raiz da extensão como declarada em seu arquivo `composer.json`, e cada alias representa o diretório raiz de seu pacote. Por exemplo, se você instalar a extensão `yiisoft/yii2-jui`, você terá automaticamente o alias `@yii/jui` definido durante o estágio de [inicialização](runtime-bootstrapping.md), equivalente a:\n\n```php\nYii::setAlias('@yii/jui', 'VendorPath/yiisoft/yii2-jui');\n```\n"
  },
  {
    "path": "docs/guide-pt-BR/concept-autoloading.md",
    "content": "Autoloading de Classes \n=================\n\nO Yii baseia-se no [mecanismo de autoloading de classe](https://www.php.net/manual/pt_BR/language.oop5.autoload.php) para localizar e incluir todos os arquivos de classe necessários. Ele fornece um autoloader de alto desempenho que é compatível com o\n[PSR-4 standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md).\nO autoloader é instalado quando o arquivo `Yii.php` é incluído. \n> Observação: Para simplificar a descrição, nesta seção, nós falaremos apenas sobre autoloading de classe. No entanto, tenha em mente que o conteúdo que estamos descrevendo aqui se aplica a autoloading de interfaces e traits também.\n\n\nUsando o Autoloader do Yii <span id=\"using-yii-autoloader\"></span>\n------------------------\n\nPara fazer uso da autoloader de classe do Yii, você deve seguir duas regras simples ao criar e nomear suas classes:\n\n* Cada classe deve estar debaixo de um [namespace](https://www.php.net/manual/pt_BR/language.namespaces.php) (exemplo. `foo\\bar\\MyClass`)\n* Cada classe deve ser salvo em um arquivo individual cujo caminho é determinado pelo seguinte algoritmo:\n\n```php\n// $className é um nome de classe totalmente qualificado sem o primeiro barra invertida\n$classFile = Yii::getAlias('@' . str_replace('\\\\', '/', $className) . '.php');\n```\nPor exemplo, se um nome de classe e seu namespace for `foo\\bar\\MyClass`, o [alias](concept-aliases.md) correspondente ao caminho do arquivo da classe seria `@foo/bar/MyClass.php`. Para que este alias seja resolvido em um caminho de arquivo, de outra forma `@foo` ou `@foo/bar` deve ser um [alias raiz](concept-aliases.md#defining-aliases).\n\nQuando se utiliza o [Template Básico de Projetos](start-installation.md), você pode colocar suas classes sob o namespace de nível superior `app` de modo que eles podem ser carregados automaticamente pelo Yii sem a necessidade de definir um novo alias. Isto é porque\n`@app` é um [alias predefinido](concept-aliases.md#predefined-aliases), e um nome de classe como `app\\components\\MyClass` pode ser resolvido no arquivo de classe `AppBasePath/components/MyClass.php`, de acordo com o algoritmo já descrito.\n\nNo [Template Avançado de Projetos](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide-pt-BR/README.md), cada camada tem seu própria alias raiz. Por exemplo, a camada front-end tem um alias raiz `@frontend`, enquanto a camada back-end o alias raiz é `@backend`. Como resultado, você pode colocar as classes do front-end debaixo do namespace `frontend` enquanto as classes do back-end estão debaixo do namespace `backend`. Isto permitirá que estas classes sejam carregadas automaticamente pelo Yii autoloader.\n\n\nMapeamento de Classes <span id=\"class-map\"></span>\n---------\n\nO autoloader de classe do Yii suporta o recurso de *mapeamento de classe*, que mapeia nomes de classe para os caminhos de arquivo das classes correspondentes.\nQuando o autoloader está carregando uma classe, ele irá primeiro verificar se a classe se encontra no mapa. Se assim for, o caminho do arquivo correspondente será incluído diretamente, sem mais verificações. Isso faz com que classe seja carregada super rápido. De fato, todas as classes principais do Yii são carregadas automaticamente dessa maneira.\nVocê pode adcionar uma classe no mapa de classe, armazenado em `Yii::$classMap`, usando:\n\n```php\nYii::$classMap['foo\\bar\\MyClass'] = 'path/to/MyClass.php';\n```\n\nOs [aliases](concept-aliases.md) podem ser usados para especificar caminhos de arquivo de classe. Você deve definir o mapa de classe no processo de [inicialização (bootstrapping)](runtime-bootstrapping.md) de modo que o mapa está pronto antes de suas classes serem usadas.\n\n\nUsando outros Autoloaders <span id=\"using-other-autoloaders\"></span>\n-----------------------\n\nUma vez que o Yii utiliza o Composer como seu gerenciador de dependência de pacotes, é recomendado que você sempre instale o autoloader do Composer. Se você está utilizando bibliotecas de terceiros que tem seus próprios autoloaders, você também deve instala-los. \nAo usar o Yii autoloader junto com outros autoloaders, você deve incluir o arquivo `Yii.php` *depois* de todos os outros autoloaders serem instalados. Isso fará com que o Yii autoloader seja o primeiro a responder a qualquer solicitação de autoloading  de classe. Por exemplo, o código a seguir foi extraído do [script de entrada](structure-entry-scripts.md) do [Template Básico de Projeto](start-installation.md). A primeira linha instala o Composer autoloader, enquanto a segunda linha instala o Yii autoloader:\n\n```php\nrequire __DIR__ . '/../vendor/autoload.php';\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n```\nVocê pode usar o autoloader do Composer sozinho sem o autoloader do Yii. No entanto, ao fazê-lo, o desempenho do carregamento automático das classes pode ser degradada, e você deve seguir as regras estabelecidas pelo Composer para que suas classes sejam auto carregáveis.\n\n> Informação: Se você não quiser utilizar o autoloader do Yii, você deve criar sua própria versão do arquivo `Yii.php` e incluí-lo no seu [script de entrada](structure-entry-scripts.md).\n\n\nAutoloading de Classes de Extensões <span id=\"autoloading-extension-classes\"></span>\n-----------------------------\n\nO autoloader do Yii é capaz de realizar autoloading de classes de [extensões](structure-extensions.md). O único requisito é que a extensão especifique a seção `autoload` corretamente no seu arquivo `composer.json`. Por favor, consulte a\n[documentação do Composer](https://getcomposer.org/doc/04-schema.md#autoload) para mais detalhes sobre especificação de `autoload`.\n\nNo caso de você não usar o autoloader do Yii, o autoloader do Composer ainda pode realizar o autoload das classes de extensão para você.\n\n"
  },
  {
    "path": "docs/guide-pt-BR/concept-behaviors.md",
    "content": "Behaviors (Comportamentos)\n=========\n\nBehaviors são instâncias de  [[yii\\base\\Behavior]], ou de uma classe-filha, também conhecido como [mixins](https://en.wikipedia.org/wiki/Mixin), permite melhorar a funcionalidade de uma classe [[yii\\base\\Component|componente]]  existente sem a necessidade de mudar a herança dela.\nAnexar um behavior a um componente \"introduz\" os métodos e propriedades do behavior dentro do componente, tornando esses métodos e propriedades acessíveis como se estes fossem definidos na própria classe do componente. Além disso, um behavior pode responder a um [evento](concept-events.md) disparado pelo componente, o que permite a customização do código normal.\n\n\nDefinindo Behaviors <span id=\"defining-behaviors\"></span>\n------------------\n\nPara definir um behavior, crie uma classe estendendo [[yii\\base\\Behavior]], ou de uma classe-filha. Por exemplo:\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Behavior;\n\nclass MyBehavior extends Behavior\n{\n   public $prop1;\n\n   private $_prop2;\n\n   public function getProp2()\n   {\n       return $this->_prop2;\n   }\n\n   public function setProp2($value)\n   {\n       $this->_prop2 = $value;\n   }\n\n   public function foo()\n   {\n       // ...\n   }\n}\n```\n\nO código acima define a classe behavior `app\\components\\MyBehavior`, com duas propriedades --`prop1` e `prop2`-- e um método `foo()`. Note que a propriedade `prop2`\nÉ definida através do método getter `getProp2()` e setter `setProp2()`. Isto é possível porque [[yii\\base\\Behavior]] estende de [[yii\\base\\BaseObject]] e portanto suporta definição de propriedades através de [propriedades](concept-properties.md) getters e setters.\n\nComo essa classe é um behavior, quando ela está anexada a um componente, então este componente terá as propriedades `prop1` e `prop2` e o método `foo()`.\n\n> Dica: Em um behavior, você pode acessar o componente que o behavior está anexado através da propriedade [[yii\\base\\Behavior::owner]].\n\n\nManuseando Eventos de Componente\n------------------\n\nSe um behavior precisar responder a eventos disparados pelo componente ao qual está ligado, este deve sobrescrever o método [[yii\\base\\Behavior::events()]]. Por exemplo:\n\n```php\nnamespace app\\components;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\base\\Behavior;\n\nclass MyBehavior extends Behavior\n{\n   // ...\n\n   public function events()\n   {\n       return [\n           ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',\n       ];\n   }\n\n   public function beforeValidate($event)\n   {\n       // ...\n   }\n}\n```\n\nO método [[yii\\base\\Behavior::events()|events()]] deve retornar uma lista de eventos e seus manipuladores correspondentes.\nO exemplo acima declara o evento [[yii\\db\\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]] existente e define seu manipulador, `beforeValidate()`. Ao especificar um manipulador de evento, você pode utilizar um dos seguintes formatos:\n\n* uma string que refere-se ao nome do método da classe behavior, como o exemplo acima\n* um array com o nome do objeto ou classe, e um nome de método como string (sem parênteses), por exemplo, `[$object, 'methodName']`;\n* uma função anônima\n\nA assinatura de um manipulador de eventos deve ser como o exemplo abaixo, onde `$event` refere-se ao parâmetro do evento. Por favor, consulte a seção [Eventos](concept-events.md) para mais detalhes sobre eventos.\n\n```php\nfunction ($event) {\n}\n```\n\n\nAnexando Behaviors (Comportamentos) <span id=\"attaching-behaviors\"></span>\n-------------------\n\nVocê pode anexar um behavior a um [[yii\\base\\Component|componente]] de forma estática ou dinâmica. Na prática a forma estática é a mais comum.\n\nPara anexar um behavior de forma estática, sobrescreva o método [[yii\\base\\Component::behaviors()|behaviors()]] da classe componente\npara o behavior que está sendo anexado. O método [[yii\\base\\Component::behaviors()|behaviors()]] deve retornar uma lista de [configurações](concept-configurations.md) de behaviors.\nCada configuração de behavior pode ser tanto um nome da classe behavior ou um array de configuração:\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\nuse app\\components\\MyBehavior;\n\nclass User extends ActiveRecord\n{\n   public function behaviors()\n   {\n       return [\n           // behavior anônimo, somente o nome da classe\n           MyBehavior::class,\n\n           // behavior nomeado, somente o nome da classe\n           'myBehavior2' => MyBehavior::class,\n\n           // behavior anônimo, array de configuração\n           [\n               'class' => MyBehavior::class,\n               'prop1' => 'value1',\n               'prop2' => 'value2',\n           ],\n\n           // behavior nomeado, array de configuração\n           'myBehavior4' => [\n               'class' => MyBehavior::class,\n               'prop1' => 'value1',\n               'prop2' => 'value2',\n           ]\n       ];\n   }\n}\n```\n\nVocê pode associar um nome com um behavior especificando a chave do array correspondente à configuração do behavior. Neste caso o behavior é chamado *behavior nomeado*. No exemplo acima existem dois behaviors nomeados: `myBehavior2` e `myBehavior4`. Se um behavior não está associado a um nome, ele é chamado de *behavior anônimo*.\n\nPara anexar um behavior dinamicamente, execute o método [[yii\\base\\Component::attachBehavior()]] do componente para o behavior que está sendo anexado:\n\n```php\nuse app\\components\\MyBehavior;\n\n// anexando um objeto behavior \n$component->attachBehavior('myBehavior1', new MyBehavior);\n\n// anexando uma classe behavior \n$component->attachBehavior('myBehavior2', MyBehavior::class);\n\n// anexando através de um array de configuração\n$component->attachBehavior('myBehavior3', [\n   'class' => MyBehavior::class,\n   'prop1' => 'value1',\n   'prop2' => 'value2',\n]);\n```\n\nVocê pode anexar vários behaviors de uma só vez usando o método [[yii\\base\\Component::attachBehaviors()]]:\n\n```php\n$component->attachBehaviors([\n   'myBehavior1' => new MyBehavior,  // um behavior nomeado\n   MyBehavior::class,          // um behavior anônimo \n]);\n```\n\nVocê também pode anexar behaviors através de [configurações](concept-configurations.md) conforme o exemplo a seguir: \n\n```php\n[\n   'as myBehavior2' => MyBehavior::class,\n\n   'as myBehavior3' => [\n       'class' => MyBehavior::class,\n       'prop1' => 'value1',\n       'prop2' => 'value2',\n   ],\n]\n```\n\nPara mais detalhes, por favor, consulte a seção [Configurações](concept-configurations.md#configuration-format).\n\n\nUsando Behaviors <span id=\"using-behaviors\"></span>\n---------------\n\nPara usar um behavior, primeiro este deve ser anexado à um [[yii\\base\\Component|componente]] conforme as instruções mencionadas anteriormente. Uma vez que o behavior está anexado ao componente, seu uso é simples.\n\nVocê pode acessar uma variável *pública* ou uma [propriedade](concept-properties.md) definida por um getter e/ou um setter do behavior através do componente ao qual ele está anexado:\n\n```php\n// \"prop1\" é uma propriedade definida na classe behavior \necho $component->prop1;\n$component->prop1 = $value;\n```\n\nVocê também pode executar um método *público* do behavior de forma parecida:\n\n```php\n// foo() é um método público definido na classe behavior \n$component->foo();\n```\n\nComo você pode ver, embora `$component` não defina `prop1` e nem `foo()`, eles podem ser utilizados como se eles fizessem parte  da definição do componente, isto se deve ao behavior anexado.\n\nSe dois behaviors definem a mesma propriedade ou método e ambos são anexados ao mesmo componente, o behavior que for anexado primeiramente ao componente terá precedência quando a propriedade ou método for acessada.\n\nUm behavior pode estar associado a um nome quando ele for anexado a um componente. Sendo esse o caso, você pode acessar o objeto behavior usando o seu nome:\n\n```php\n$behavior = $component->getBehavior('myBehavior');\n```\n\nVocê também pode pegar todos os behaviors anexados a um componente:\n\n```php\n$behaviors = $component->getBehaviors();\n```\n\n\nDesvinculando Behaviors (Comportamentos)<span id=\"detaching-behaviors\"></span>\n-------------------\n\nPara desvincular um behavior, execute [[yii\\base\\Component::detachBehavior()]] com o nome associado ao behavior:\n\n```php\n$component->detachBehavior('myBehavior1');\n```\n\nVocê também pode desvincular *todos* os behaviors:\n\n```php\n$component->detachBehaviors();\n```\n\n\nUsando `TimestampBehavior` <span id=\"using-timestamp-behavior\"></span>\n-------------------------\n\nPara encerrar, vamos dar uma olhada no [[yii\\behaviors\\TimestampBehavior]]. Este behavior suporta atualização automática dos atributos timestamp de um [[yii\\db\\ActiveRecord|Active Record]] toda vez que o model (modelo) for salvo (por exemplo, na inserção ou na alteração).\n\nPrimeiro, anexe este behavior na classe [[yii\\db\\ActiveRecord|Active Record]] que você planeja usar:\n\n```php\nnamespace app\\models\\User;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\behaviors\\TimestampBehavior;\n\nclass User extends ActiveRecord\n{\n   // ...\n\n   public function behaviors()\n   {\n       return [\n           [\n               'class' => TimestampBehavior::class,\n               'attributes' => [\n                   ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],\n                   ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'],\n               ],\n           ],\n       ];\n   }\n}\n```\n\nA configuração do behavior acima especifica que o registro ao ser:\n\n* inserido, o behavior deve atribuir o timestamp atual para os atributos `created_at` e `updated_at` \n* atualizado, o behavior deve atribuir o timestamp atual para o atributo `updated_at` \n\nCom esse código no lugar, se você tem um objeto `User` e tenta salvá-lo, você encontrará seus `created_at` e `updated_at` automaticamente preenchidos com a data e hora atual:\n\n```php\n$user = new User;\n$user->email = 'test@example.com';\n$user->save();\necho $user->created_at;  // mostra a data atual\n```\n\nO [[yii\\behaviors\\TimestampBehavior|TimestampBehavior]] também oferece um método útil\n[[yii\\behaviors\\TimestampBehavior::touch()|touch()]], que irá atribuir a data e hora atual para um atributo específico e o salva no banco de dados:\n\n```php\n$user->touch('login_time');\n```\n\n\nComparando Behaviors com Traits <span id=\"comparison-with-traits\"></span>\n----------------------\n\nApesar de behaviors serem semelhantes a [traits](https://www.php.net/manual/pt_BR/language.oop5.traits.php) em que ambos \"injetam\" suas propriedades e métodos para a classe principal, eles diferem em muitos aspectos. Tal como explicado abaixo, ambos têm prós e contras. Eles funcionam mais como complemento um do outro.\n\n\n### Razões para usar Behaviors <span id=\"pros-for-behaviors\"></span>\n\nClasses Behavior, como classes normais, suportam herança. Traits, por outro lado, pode ser só suporta a programação “copia e cola”. Eles não suportam herança.\n\nBehaviors podem ser anexados e desvinculados a um componente dinamicamente sem necessidade de modificação da classe componente.\nPara usar um trait, você deve modificar o código da classe.\n\nBehaviors são configuráveis enquanto traits não são.\n\nBehaviors podem customizar a execução do código do componente respondendo aos seus eventos.\nQuando houver nomes conflitantes entre diferentes behaviors anexados ao mesmo componente, o conflito é automaticamente resolvido priorizando o behavior anexado primeiramente ao componente. Nomes conflitantes causados por diferentes traits requer resolução manual renomeando as propriedades ou métodos afetados.\n\n\n### Razões para usar Traits <span id=\"pros-for-traits\"></span>\n\nTraits são muito mais eficientes do que behaviors, estes são objetos e requerem mais tempo e memória.\n\nIDEs são mais amigáveis com traits por serem nativos do PHP.\n\n"
  },
  {
    "path": "docs/guide-pt-BR/concept-components.md",
    "content": "Componentes\n===========\n\nComponente é a parte principal na construção de aplicações Yii. Componentes são instâncias de [[yii\\base\\Component]], ou uma classe estendida. As três características principais que os componentes fornecem a outras classes são:\n\n* [Propriedades](concept-properties.md)\n* [Eventos](concept-events.md)\n* [Behaviors (Comportamentos)](concept-behaviors.md)\n\nSeparadamente e combinadas, essas características fazem com que as classes no Yii sejam muito mais customizáveis e fáceis de usar. Por exemplo, o [[yii\\jui\\DatePicker|widget date picker]], um componente de interface do usuário, pode ser usado na [view (visão)](structure-view.md) para gerar um `date picker` interativo:\n\n```php\nuse yii\\jui\\DatePicker;\n\necho DatePicker::widget([\n   'language' => 'ru',\n   'name'  => 'country',\n   'clientOptions' => [\n       'dateFormat' => 'yy-mm-dd',\n   ],\n]);\n```\n\nOs widgets são facilmente escritos porque a classe estende de [[yii\\base\\Component]].\n\nEnquanto os componentes são muito poderosos, eles são um pouco mais pesados do que um objeto normal, devido ao fato de que é preciso memória e CPU extra para dar suporte às funcionalidades de [evento](concept-events.md) e de [behavior](concept-behaviors.md) em particular.\nSe o seu componente não precisa dessas duas características, você pode considerar estender a sua classe de componente de [[yii\\base\\BaseObject]] em vez de [[yii\\base\\Component]]. Se o fizer, fará com que seus componentes sejam tão eficientes como objetos normais do PHP, mas com um suporte adicional para [propriedades](concept-properties.md).\n\nAo estender a classe de [[yii\\base\\Component]] ou [[yii\\base\\BaseObject]], é recomendado que você siga as seguintes convenções:\n\n- Se você sobrescrever o construtor, declare um parâmetro `$config` como último parâmetro do construtor, e em seguida passe este parâmetro para o construtor pai.\n- Sempre chame o construtor pai *no final* do seu construtor reescrito.\n- Se você sobrescrever o método [[yii\\base\\BaseObject::init()]], certifique-se de chamar a implementação pai do `init` *no início* do seu método `init`.\n\nPor Exemplo:\n\n```php\n<?php\n\nnamespace yii\\components\\MyClass;\n\nuse yii\\base\\BaseObject;\n\nclass MyClass extends BaseObject\n{\n   public $prop1;\n   public $prop2;\n\n   public function __construct($param1, $param2, $config = [])\n   {\n       // ... initialization before configuration is applied\n\n       parent::__construct($config);\n   }\n\n   public function init()\n   {\n       parent::init();\n\n       // ... initialization after configuration is applied\n   }\n}\n```\n\nSeguindo essas orientações fará com que seus componentes sejam [configuráveis](concept-configurations.md) quando forem criados. Por Exemplo:\n\n```php\n$component = new MyClass(1, 2, ['prop1' => 3, 'prop2' => 4]);\n// alternatively\n$component = \\Yii::createObject([\n   'class' => MyClass::class,\n   'prop1' => 3,\n   'prop2' => 4,\n], [1, 2]);\n```\n\n> Informação: Embora a forma de chamar [[Yii::createObject()]] pareça ser mais complicada, ela é mais poderosa porque ela é aplicada no topo de um [container \nde injeção de dependência](concept-di-container.md).\n \n\nA classe [[yii\\base\\BaseObject]] impõe o seguinte ciclo de vida do objeto:\n\n1. Pré-inicialização dentro do construtor. Você pode definir valores de propriedade padrão aqui.\n2. Configuração de objeto via `$config`. A configuração pode sobrescrever o valor padrão configurado dentro do construtor.\n3. Pós-inicialização dentro do [[yii\\base\\BaseObject::init()|init()]]. Você pode sobrescrever este método para executar checagens e normalização das propriedades.\n4. Chamadas de método de objeto.\n\nOs três primeiros passos acontecem dentro do construtor do objeto. Isto significa que uma vez que você instanciar a classe (isto é, um objeto), esse objeto será inicializado adequadamente. \n\n"
  },
  {
    "path": "docs/guide-pt-BR/concept-configurations.md",
    "content": "Configurações\n============\n\nAs configurações são amplamente utilizadas em Yii na criação de novos objetos ou inicializando objetos existentes.\nConfigurações geralmente incluem o nome da classe do objeto que está sendo criado, e uma lista de valores iniciais que devem ser atribuídos as  [propriedades](concept-properties.md) do objeto. Configurações também podem incluir uma lista de manipuladores que devem ser anexados aos [eventos](concept-events.md) do objeto e/ou uma lista de [behaviors](concept-behaviors.md) que também deve ser ligado ao objeto.\n\nA seguir, uma configuração é usada para criar e inicializar uma conexão com o banco:\n\n```php\n$config = [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n];\n\n$db = Yii::createObject($config);\n```\n\nO método [[Yii::createObject()]] recebe um array de configuração como argumento, e cria um objeto instanciando a classe informada na configuração. Quando o objeto é instanciado, o resto da configuração será usada para inicializar as propriedades, manipuladores de eventos, e behaviors do objeto.\n\nSe você já tem um objeto, você pode utilizar [[Yii::configure()]] para inicializar as propriedades do objeto com o array de configuração:\n\n```php\nYii::configure($object, $config);\n```\n\nNote que, neste caso, o array de configuração não deve conter o elemento `class`.\n\n\n## Formato da Configuração <span id=\"configuration-format\"></span>\n\nO formato de uma configuração pode ser descrita formalmente como:\n\n```php\n[\n    'class' => 'ClassName',\n    'propertyName' => 'propertyValue',\n    'on eventName' => $eventHandler,\n    'as behaviorName' => $behaviorConfig,\n]\n```\n\nOnde:\n\n* O elemento `class` determina um nome de classe totalmente qualificado para o objeto que está sendo criado.\n* O elemento `propertyName` determina os valores iniciais para a propriedade nomeada. As chaves são os nomes das propriedades e os valores são os valores iniciais correspondentes. Apenas variáveis públicas e [propriedades](concept-properties.md) definidas por getters/setters podem ser configuradas.\n* O elemento `on eventName` determina quais manipuladores devem ser anexados aos [eventos](concept-events.md) do objeto. Observe que as chaves do array são formadas prefixando a palavra `on `  ao nome do evento. Por favor, consulte a seção [Eventos](concept-events.md) para formatos de manipulador de eventos suportados.\n* O elemento `as behaviorName` determina quais [behaviors](concept-behaviors.md) devem ser anexados ao objeto. Observe que as chaves do  array são formadas prefixando a palavra `as` ao nome do behavior;  O valor, `$behaviorConfig`, representa a configuração para a criação do behavior, como uma configuração normal descrita aqui.\n\nAbaixo está um exemplo mostrando uma configuração com valores iniciais de propriedades, manipulador de evento e behaviors:\n\n```php\n[\n    'class' => 'app\\components\\SearchEngine',\n    'apiKey' => 'xxxxxxxx',\n    'on search' => function ($event) {\n        Yii::info(\"Keyword searched: \" . $event->keyword);\n    },\n    'as indexer' => [\n        'class' => 'app\\components\\IndexerBehavior',\n        // ... property init values ...\n    ],\n]\n```\n\n\n## Usando Configurações <span id=\"using-configurations\"></span>\n\nConfigurações são utilizadas em vários lugares no  Yii. No início desta seção, mostramos como criar um objeto utilizando configuração [[Yii::createObject()]]. Nesta subseção, nós descreveremos a configuração de aplicação e configuração de widget - dois principais usos de configurações.\n\n\n### Configurações da Aplicação <span id=\"application-configurations\"></span>\n\nA configuração de uma [aplicação](structure-applications.md) é provavelmente um dos mais complexos arrays no Yii.\nIsto porque a classe [[yii\\web\\Application|application]] tem muitas propriedades e eventos configuráveis.\nMais importante, suas propriedades [[yii\\web\\Application::components|components]] podem receber um array de configuração para a criação de componentes que são registrados através da aplicação. O exemplo abaixo é um resumo do arquivo de configuração da aplicação para o [Template Básico de Projetos](start-installation.md).\n\n```php\n$config = [\n    'id' => 'basic',\n    'basePath' => dirname(__DIR__),\n    'extensions' => require __DIR__ . '/../vendor/yiisoft/extensions.php',\n    'components' => [\n        'cache' => [\n            'class' => 'yii\\caching\\FileCache',\n        ],\n        'mailer' => [\n            'class' => 'yii\\swiftmailer\\Mailer',\n        ],\n        'log' => [\n            'class' => 'yii\\log\\Dispatcher',\n            'traceLevel' => YII_DEBUG ? 3 : 0,\n            'targets' => [\n                [\n                    'class' => 'yii\\log\\FileTarget',\n                ],\n            ],\n        ],\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=stay2',\n            'username' => 'root',\n            'password' => '',\n            'charset' => 'utf8',\n        ],\n    ],\n];\n```\n\nA configuração não tem uma chave `class`. Isto porque ele é utilizado como um [script de entrada](structure-entry-scripts.md), onde o nome da classe já está informado,\n\n```php\n(new yii\\web\\Application($config))->run();\n```\n\nMais detalhes sobre a configuração das propriedades  `componentes` de uma aplicação podem ser encontrados na seção [Aplicações](structure-applications.md) e na seção [Service Locator](concept-service-locator.md).\n\n\n### Configurações de Widget <span id=\"widget-configurations\"></span>\n\nAo utilizar osos [widgets](structure-widgets.md), muitas vezes você precisa usar configurações para customizar as propriedades do widget.\nAmbos os métodos [[yii\\base\\Widget::widget()]] e [[yii\\base\\Widget::begin()]] podem ser utilizados para criar um widget. Eles precisam de um array de configuração, como o exemplo abaixo,\n\n```php\nuse yii\\widgets\\Menu;\n\necho Menu::widget([\n    'activateItems' => false,\n    'items' => [\n        ['label' => 'Home', 'url' => ['site/index']],\n        ['label' => 'Products', 'url' => ['product/index']],\n        ['label' => 'Login', 'url' => ['site/login'], 'visible' => Yii::$app->user->isGuest],\n    ],\n]);\n```\n\nO código acima cria um  widget `Menu` e inicializa suas propriedades `activateItems` com `false`. A propriedade `items` também é configurada com os itens do menu para serem exibidos.\n\nObserve que, como o nome da classe já está dado, o array de configuração não precisa da chave `class`.\n\n\n## Arquivos de Configuração <span id=\"configuration-files\"></span>\n\nQuando uma configuração é muito complexa, uma prática comum é armazená-la em um ou mais arquivos PHP, conhecidos como *arquivos de configuração*. Um arquivo de configuração retorna um array PHP representando a configuração.\nPor exemplo, você pode guardar uma configuração da aplicação em um arquivo chamado `web.php`, como a seguir,\n\n```php\nreturn [\n    'id' => 'basic',\n    'basePath' => dirname(__DIR__),\n    'extensions' => require __DIR__ . '/../vendor/yiisoft/extensions.php',\n    'components' => require __DIR__ . '/components.php',\n];\n```\n\nComo a configuração de `componentes` é muito complexa, você o guarda em um arquivo separado chamado `components.php` e faz um \"require\" deste arquivo no `web.php` como mostrado acima. o conteúdo de `components.php` é como abaixo,\n\n```php\nreturn [\n    'cache' => [\n        'class' => 'yii\\caching\\FileCache',\n    ],\n    'mailer' => [\n        'class' => 'yii\\swiftmailer\\Mailer',\n    ],\n    'log' => [\n        'class' => 'yii\\log\\Dispatcher',\n        'traceLevel' => YII_DEBUG ? 3 : 0,\n        'targets' => [\n            [\n                'class' => 'yii\\log\\FileTarget',\n            ],\n        ],\n    ],\n    'db' => [\n        'class' => 'yii\\db\\Connection',\n        'dsn' => 'mysql:host=localhost;dbname=stay2',\n        'username' => 'root',\n        'password' => '',\n        'charset' => 'utf8',\n    ],\n];\n```\n\nPara pegar a configuração armazenada em um arquivo de configuração, simplismente faça um \"require\" deste arquivo, como o exemplo abaixo:\n\n```php\n$config = require 'path/to/web.php';\n(new yii\\web\\Application($config))->run();\n```\n\n\n## Configurações Padrões <span id=\"default-configurations\"></span>\n\nO método [[Yii::createObject()]] é implementado com base em um [container de injeção de dependência](concept-di-container.md).\nEle permite que você especifique um conjunto do chamado *configurações padrões* que será aplicado a todas as instâncias das classes especificadas quando elas forem criadas usando [[Yii::createObject()]]. As configurações padrões podem ser especificadas executando `Yii::$container->set()` na [inicialização (bootstrapping)](runtime-bootstrapping.md) do código.\n\nPor exemplo, se você quiser personalizar [[yii\\widgets\\LinkPager]] de modo que todas as páginas mostrarão no máximo 5 botões (o valor padrão é 10), você pode utilizar o código abaixo para atingir esse objetivo,\n\n```php\n\\Yii::$container->set('yii\\widgets\\LinkPager', [\n    'maxButtonCount' => 5,\n]);\n```\n\nSem usar as configurações padrão, você teria que configurar `maxButtonCount` em todos os lugares que utilizassem este recurso.\n\n\n## Constantes de Ambiente <span id=\"environment-constants\"></span>\n\nConfigurações frequentemente variam de acordo com o ambiente no qual a aplicação é executada. Por exemplo, no ambiente de desenvolvimento, você pode querer usar um banco de dados chamado `mydb_dev`, enquanto no servidor de produção você pode querer usar o banco de dados `mydb_prod`. Para facilitar a troca de ambientes, o Yii fornece uma constante chamada `YII_ENV` que você pode definir no [script de entrada](structure-entry-scripts.md) da sua aplicação.\nPor exemplo,\n\n```php\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n```\n\nvocê pode definir `YII_ENV` como um dos seguintes valores:\n\n- `prod`: ambiente de produção. A constante `YII_ENV_PROD` será avaliada como verdadeira.\n  Este é o valor padrão da constante `YII_ENV` caso você não a defina.\n- `dev`: ambiente de desenvolvimento. A constante `YII_ENV_DEV` será avaliada como verdadeira.\n- `test`: ambiente de teste. A constante `YII_ENV_TEST` será avaliada como verdadeira.\n\nCom estas constantes de ambientes, você pode especificar suas configurações de acordo com o ambiente atual. Por exemplo, sua configuração da aplicação pode conter o seguinte código para habilitar a [barra de ferramentas de depuração e depurador](tool-debugger.md) no ambiente de desenvolvimento.\n\n```php\n$config = [...];\n\nif (YII_ENV_DEV) {\n    // configuration adjustments for 'dev' environment\n    $config['bootstrap'][] = 'debug';\n    $config['modules']['debug'] = 'yii\\debug\\Module';\n}\n\nreturn $config;\n```\n"
  },
  {
    "path": "docs/guide-pt-BR/concept-di-container.md",
    "content": "Container de Injeção de Dependência \n==============================\n\nUm container de injeção de dependência (DI) é um objeto que sabe como instanciar e configurar objetos e todas as suas dependências. O [artigo do Martin](https://martinfowler.com/articles/injection.html) explica bem porque o container de DI é útil. Aqui vamos explicar principalmente a utilização do container de DI fornecido pelo Yii.\n\n\nInjeção de Dependência <span id=\"dependency-injection\"></span>\n--------------------\n\nO Yii fornece o recurso container de DI através da classe [[yii\\di\\Container]]. Ela suporta os seguintes tipos de injeção de dependência:\n\n* Injeção de Construtor;\n* Injeção de setter e propriedade;\n* Injeção de PHP callable.\n\n\n### Injeção de Construtor <span id=\"constructor-injection\"></span>\n\nO container de DI suporta injeção de construtor com o auxílio dos *type hints* identificados nos parâmetros dos construtores. Os type hints informam ao container quais classes ou interfaces são dependentes no momento da criação de um novo objeto.\nO container tentará pegar as instâncias das classes dependentes ou interfaces e depois injetá-las dentro do novo objeto através do construtor. Por exemplo:\n\n```php\nclass Foo\n{\n    public function __construct(Bar $bar)\n    {\n    }\n}\n$foo = $container->get('Foo');\n// que equivale a:\n$bar = new Bar;\n$foo = new Foo($bar);\n```\n\n\n### Injeção de Setter e Propriedade <span id=\"setter-and-property-injection\"></span>\n\nA injeção de setter e propriedade é suportado através de [configurações](concept-configurations.md).\nAo registrar uma dependência ou ao criar um novo objeto, você pode fornecer uma configuração que será utilizada pelo container para injetar as dependências através dos setters ou propriedades correspondentes.\nPor exemplo:\n\n```php\nuse yii\\base\\BaseObject;\n\nclass Foo extends BaseObject\n{\n    public $bar;\n\n    private $_qux;\n\n    public function getQux()\n    {\n        return $this->_qux;\n    }\n\n    public function setQux(Qux $qux)\n    {\n        $this->_qux = $qux;\n    }\n}\n\n$container->get('Foo', [], [\n    'bar' => $container->get('Bar'),\n    'qux' => $container->get('Qux'),\n]);\n```\n\n> Informação: O método [[yii\\di\\Container::get()]] recebe em seu terceiro parâmetro um array de configuração que deve ser aplicado ao objecto a ser criado. Se a classe implementa a interface [[yii\\base\\Configurable]] (por exemplo, [[yii\\base\\BaseObject]]), o array de configuração será passado como o último parâmetro para o construtor da classe; caso contrário, a configuração será aplicada *depois* que o objeto for criado.\n\n\n### Injeção de PHP Callable <span id=\"php-callable-injection\"></span>\n\nNeste caso, o container usará um PHP callable registrado para criar novas instâncias da classe.\nCada vez que [[yii\\di\\Container::get()]] for chamado, o callable correspondente será invocado.\nO callable é responsável por resolver as dependências e injetá-las de forma adequada para os objetos recém-criados. Por exemplo:\n\n```php\n$container->set('Foo', function ($container, $params, $config) {\n    $foo = new Foo(new Bar);\n    // ... Outras inicializações...\n    return $foo;\n});\n\n$foo = $container->get('Foo');\n```\n\nPara ocultar a lógica complexa da construção de um novo objeto você pode usar um método estático de classe para retornar o PHP callable. Por exemplo:\n\n```php\nclass FooBuilder\n{\n    public static function build($container, $params, $config)\n    {\n        return function () {\n            $foo = new Foo(new Bar);\n            // ... Outras inicializações...\n            return $foo;\n       };        \n    }\n}\n\n$container->set('Foo', FooBuilder::build());\n\n$foo = $container->get('Foo');\n```\n\nComo você pode ver, o PHP callable é retornado pelo método `FooBuilder::build()`. Ao fazê-lo, quem precisar configurar a classe `Foo` não precisará saber como ele é construído.\n\n\nRegistrando Dependências <span id=\"registering-dependencies\"></span>\n------------------------\n\nVocê pode usar [[yii\\di\\Container::set()]] para registrar dependências. O registro requer um nome de dependência, bem como uma definição de dependência. Um nome de dependência pode ser um nome de classe, um nome de interface, ou um alias; e a definição de dependência pode ser um nome de classe, um array de configuração ou um PHP callable.\n\n```php\n$container = new \\yii\\di\\Container;\n\n// registrar um nome de classe. Isso pode ser ignorado.\n$container->set('yii\\db\\Connection');\n\n// registrar uma interface\n// Quando uma classe depende da interface, a classe correspondente\n// será instanciada como o objeto dependente\n$container->set('yii\\mail\\MailInterface', 'yii\\swiftmailer\\Mailer');\n\n// registrar um alias. Você pode utilizar $container->get('foo')\n// para criar uma instância de Connection\n$container->set('foo', 'yii\\db\\Connection');\n\n// registrar uma classe com configuração. A configuração\n// será aplicada quando quando a classe for instanciada pelo get()\n$container->set('yii\\db\\Connection', [\n    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n]);\n\n// registrar um alias com a configuração de classe\n// neste caso, um elemento \"class\" é requerido para especificar a classe\n$container->set('db', [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n]);\n\n// registrar um PHP callable\n// O callable será executado sempre quando $container->get('db') for chamado\n$container->set('db', function ($container, $params, $config) {\n    return new \\yii\\db\\Connection($config);\n});\n\n// registrar uma instância de componente\n// $container->get('pageCache') retornará a mesma instância toda vez que for chamada\n$container->set('pageCache', new FileCache);\n```\n\n> Dica: Se um nome de dependência é o mesmo que a definição de dependência correspondente, você não precisa registrá-lo no container de DI.\n\nUm registro de dependência através de `set()` irá gerar uma instância a cada vez que a dependência for necessária. Você pode usar [[yii\\di\\Container::setSingleton()]] para registrar a dependência de forma a gerar apenas uma única instância:\n\n```php\n$container->setSingleton('yii\\db\\Connection', [\n    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n]);\n```\n\n\nResolvendo Dependências <span id=\"resolving-dependencies\"></span>\n----------------------\n\nDepois de registrar as dependências, você pode usar o container de DI para criar novos objetos e o container resolverá automaticamente as dependências instanciando e as injetando dentro do novo objeto criado. A resolução de dependência é recursiva, isso significa que\nse uma dependência tem outras dependências, essas dependências também serão resolvidas automaticamente.\n\nVocê pode usar [[yii\\di\\Container::get()]] para criar novos objetos. O método recebe um nome de dependência, que pode ser um nome de classe, um nome de interface ou um alias. O nome da dependência pode ou não ser registrado através de `set()` ou `setSingleton()`. Você pode, opcionalmente, fornecer uma lista de parâmetros de construtor de classe e uma [configuração](concept-configurations.md) para configurara o novo objeto criado.\nPor exemplo:\n\n```php\n// \"db\" é um alias registrado previamente\n$db = $container->get('db');\n\n// equivale a: $engine = new \\app\\components\\SearchEngine($apiKey, ['type' => 1]);\n$engine = $container->get('app\\components\\SearchEngine', [$apiKey], ['type' => 1]);\n```\n\nNos bastidores, o container de DI faz muito mais do que apenas a criação de um novo objeto.\nO container irá inspecionar primeiramente o construtor da classe para descobrir classes ou interfaces dependentes e automaticamente resolver estas dependências recursivamente.\nO código abaixo mostra um exemplo mais sofisticado. A classe `UserLister` depende de um objeto que implementa a interface `UserFinderInterface`; A Classe `UserFinder` implementa esta interface e depende do objeto `Connection`. Todas estas dependências são declaradas através de type hint dos parâmetros do construtor da classe. Com o registro de dependência de propriedade, o container de DI é capaz de resolver estas dependências automaticamente e cria uma nova instância de `UserLister` simplesmente com `get('userLister')`.\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\BaseObject;\nuse yii\\db\\Connection;\nuse yii\\di\\Container;\n\ninterface UserFinderInterface\n{\n    function findUser();\n}\n\nclass UserFinder extends BaseObject implements UserFinderInterface\n{\n    public $db;\n\n    public function __construct(Connection $db, $config = [])\n    {\n        $this->db = $db;\n        parent::__construct($config);\n    }\n\n    public function findUser()\n    {\n    }\n}\n\nclass UserLister extends BaseObject\n{\n    public $finder;\n\n    public function __construct(UserFinderInterface $finder, $config = [])\n    {\n        $this->finder = $finder;\n        parent::__construct($config);\n    }\n}\n\n$container = new Container;\n$container->set('yii\\db\\Connection', [\n    'dsn' => '...',\n]);\n$container->set('app\\models\\UserFinderInterface', [\n    'class' => 'app\\models\\UserFinder',\n]);\n$container->set('userLister', 'app\\models\\UserLister');\n\n$lister = $container->get('userLister');\n\n// que é equivalente a:\n\n$db = new \\yii\\db\\Connection(['dsn' => '...']);\n$finder = new UserFinder($db);\n$lister = new UserLister($finder);\n```\n\n\nUso Prático <span id=\"practical-usage\"></span>\n---------------\n\nO Yii cria um container de DI quando você inclui o arquivo `Yii.php` no [script de entrada](structure-entry-scripts.md) de sua aplicação. O container de DI é acessível através do [[Yii::$container]]. Quando você executa o método [[Yii::createObject()]],  na verdade o que será realmente executado é o método [[yii\\di\\Container::get()|get()]] do container para criar um novo objeto.\nConforme já informado acima, o container de DI resolverá automaticamente as dependências (se existir) e as injeta dentro do novo objeto criado. Como o Yii utiliza [[Yii::createObject()]] na maior parte do seu código principal para criar novos objetos, isso significa que você pode personalizar os objetos globalmente lidando com [[Yii::$container]].\n\nPor exemplo, você pode customizar globalmente o número padrão de botões de paginação do [[yii\\widgets\\LinkPager]]:\n\n```php\n\\Yii::$container->set('yii\\widgets\\LinkPager', ['maxButtonCount' => 5]);\n```\n\nAgora, se você usar o widget na view (visão) com o seguinte código, a propriedade `maxButtonCount` será inicializado como 5 em lugar do valor padrão 10 como definido na class.\n\n```php\necho \\yii\\widgets\\LinkPager::widget();\n```\n\nTodavia, você ainda pode substituir o valor definido através container de DI:\n\n```php\necho \\yii\\widgets\\LinkPager::widget(['maxButtonCount' => 20]);\n```\n\nOutro exemplo é se beneficiar da injeção automática de construtor do container de DI. Assumindo que a sua classe controller (controlador) depende de alguns outros objetos, tais como um serviço de reserva de um hotel.\n\nVocê pode declarar a dependência através de um parâmetro de construtor e deixar o container DI resolver isto para você.\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\nuse app\\components\\BookingInterface;\n\nclass HotelController extends Controller\n{\n    protected $bookingService;\n\n    public function __construct($id, $module, BookingInterface $bookingService, $config = [])\n    {\n        $this->bookingService = $bookingService;\n        parent::__construct($id, $module, $config);\n    }\n}\n```\n\nSe você acessar este controller (controlador) a partir de um navegador, você vai ver um erro informando que `BookingInterface` não pode ser instanciado. Isso ocorre porque você precisa dizer ao container de DI como lidar com esta dependência:\n\n```php\n\\Yii::$container->set('app\\components\\BookingInterface', 'app\\components\\BookingService');\n```\n\nAgora se você acessar o controller (controlador) novamente, uma instância de `app\\components\\BookingService` será criada e injetada como o terceiro parâmetro do construtor do controller (controlador).\n\n\nQuando Registrar Dependência <span id=\"when-to-register-dependencies\"></span>\n-----------------------------\n\nEm função de existirem dependências na criação de novos objetos, o seu registo deve ser feito o mais cedo possível. Seguem abaixo algumas práticas recomendadas:\n\n* Se você é o desenvolvedor de uma aplicação, você pode registrar dependências no [script de entrada] (structure-entry-scripts.md) da sua aplicação ou em um script incluído no script de entrada.\n * Se você é um desenvolvedor de [extensão](structure-extensions.md), você pode registrar as dependências no bootstrapping (inicialização) da classe da sua extensão.\n\n\nResumo <span id=\"summary\"></span>\n-------\n\nAmbas as injeção de dependência e [service locator](concept-service-locator.md) são padrões de projetos conhecidos que permitem a construção de software com alta coesão e baixo acoplamento. É altamente recomendável que você leia o\n[Artigo do Martin](https://martinfowler.com/articles/injection.html) para obter uma compreensão mais profunda da injeção de dependência e service locator.\n\nO Yii implementa o [service locator](concept-service-locator.md) no topo da injeção dependência container (DI).\nQuando um service locator tenta criar uma nova instância de objeto, ele irá encaminhar a chamada para o container de DI.\nEste último vai resolver as dependências automaticamente tal como descrito acima.\n\n"
  },
  {
    "path": "docs/guide-pt-BR/concept-events.md",
    "content": "Eventos\n=======\n\nEventos permitem que você injete código personalizado dentro de outo código existente em determinados pontos de execução. Você pode anexar o código personalizado a um evento de modo que ao acionar o evento, o código é executado automaticamente. Por exemplo, um objeto de e-mail pode disparar um evento `messageSent` quando o envio da mensagem for bem sucedida. Se você quiser acompanhar as mensagens que são enviadas com sucesso, você poderia então simplesmente anexar o código de acompanhamento ao evento `messageSent`.\nO Yii disponibiliza uma classe base chamada [[yii\\base\\Component]] para dar suporte aos eventos.\nSe sua classe precisar disparar eventos, ela deverá estender de [[yii\\base\\Component]], ou de uma classe-filha.\n\n\nManipuladores de Evento <span id=\"event-handlers\"></span>\n--------------\n\nUm manipulador de evento é uma função [Callback do PHP] (https://www.php.net/manual/pt_BR/language.types.callable.php) que é executada quando o evento é disparado. Você pode usar qualquer um dos seguintes callbacks:\n- uma função global do PHP especificada como uma string (sem parênteses), por exemplo, `'trim'`;\n- Um método do objeto especificado como um array, informando o objeto e um nome do método como uma string (sem parênteses), por exemplo `[$object, 'methodName']`;\n- Um método estático da classe especificado como um array informando o nome da classe e nome do método como string (sem parênteses), por exemplo, `['ClassName', 'methodName']`; \n- Uma função anônima, por exemplo, `function ($event) { ... }`.\n\nA assinatura de um manipulador de eventos é a seguinte:\n\n```php\nfunction ($event) {\n   // $event is an object of yii\\base\\Event or a child class\n}\n```\n\nAtravés do parâmetro `$event`, um manipulador de evento pode receber as seguintes informações sobre o evento que ocorreu:\n\n- [[yii\\base\\Event::name|nome do evento]]\n- [[yii\\base\\Event::sender|objeto chamador]]: o objeto cujo método `trigger()` foi chamado\n- [[yii\\base\\Event::data|dados personalizados]]: os dados que são fornecidos ao anexar o manipulador de eventos (a ser explicado a seguir)\n\n\nAnexando manipuladores de eventos <span id=\"attaching-event-handlers\"></span>\n------------------------\n\nVocê pode anexar um manipulador para um evento, chamando o método [[yii\\base\\Component::on()]]. Por exemplo:\n\n```php\n$foo = new Foo;\n\n// esse manipulador é uma função global\n$foo->on(Foo::EVENT_HELLO, 'function_name');\n\n// esse manipulador é um método de objeto\n$foo->on(Foo::EVENT_HELLO, [$object, 'methodName']);\n\n// esse manipulador é um método estático da classe\n$foo->on(Foo::EVENT_HELLO, ['app\\components\\Bar', 'methodName']);\n\n// esse manipulador é uma função anônima\n$foo->on(Foo::EVENT_HELLO, function ($event) {\n   // Código ...\n});\n```\n\nVocê também pode anexar manipuladores de eventos por meio de [configurações](concept-configurations.md). Para mais detalhes, consulte a seção [Configurações](concept-configurations.md#configuration-format).\n\nAo anexar um manipulador de eventos, você pode fornecer dados adicionais no terceiro parâmetro do método [[yii\\base\\Component::on()]].\nOs dados serão disponibilizados para o manipulador quando o evento for disparado e o manipulador chamado. Por exemplo:\n\n```php\n// O código a seguir mostrará \"abc\" quando o evento for disparado\n// porque $event->data contêm os dados passados no terceiro parâmetro do \"on\"\n$foo->on(Foo::EVENT_HELLO, 'function_name', 'abc');\n\nfunction function_name($event) {\n   echo $event->data;\n}\n```\n\n\nOrdem dos Manipuladores de Eventos\n-------------------\n\nVocê pode anexar um ou mais manipuladores para um único evento. Quando o evento é disparado, os manipuladores anexados serão chamados na ordem em que eles foram anexados ao evento. Se o manipulador precisar interromper os eventos subsequentes, pode definir a propriedade [[yii\\base\\Event::handled]] do parâmetro `$event` para *true*:\n\n```php\n$foo->on(Foo::EVENT_HELLO, function ($event) {\n   $event->handled = true;\n});\n```\n\nPor padrão, um novo manipulador é anexado a fila de manipuladores existente para o evento.\n\nComo resultado, o manipulador será chamado por último quando o evento for disparado.\n\nPara inserir um novo manipulador de evento no início da fila de modo a ser chamado primeiro, você pode chamar o método [[yii\\base\\Component::on()]], passando *false* para o quarto parâmetro `$append`:\n\n```php\n$foo->on(Foo::EVENT_HELLO, function ($event) {\n   // ...\n}, $data, false);\n```\n\n\nDisparando Eventos <span id=\"triggering-events\"></span>\n-----------------\n\nOs eventos são disparados chamando o método [[yii\\base\\Component::trigger()]]. Este método requer um *nome de evento*, e, opcionalmente, um objeto de evento que descreve os parâmetros a serem passados para os manipuladores de eventos. Por exemplo:\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Component;\nuse yii\\base\\Event;\n\nclass Foo extends Component\n{\n   const EVENT_HELLO = 'hello';\n\n   public function bar()\n   {\n       $this->trigger(self::EVENT_HELLO);\n   }\n}\n```\n\nCom o código acima, todas as chamadas para `bar ()` irão disparar um evento chamado `hello`.\n\n> Dica: Recomenda-se usar constantes de classe para representar nomes de eventos. No exemplo acima, a constante `EVENT_HELLO` representa o evento `hello`. Esta abordagem tem três benefícios. Primeiro, previne erros de digitação. Segundo, pode fazer o evento se tornar reconhecível para recursos de *auto-complete* de IDEs. Terceiro, você pode especificar quais eventos são suportados em uma classe, basta verificar suas declarações de constantes.\n\nÀs vezes, quando um evento é disparado você pode querer passar junto informações adicionais para os manipuladores de eventos. Por exemplo, um objeto de e-mail pode querer passar uma informação para o manipulador do evento `messageSent` de modo que os manipuladores podem conhecer os detalhes das mensagens enviadas. Para fazer isso, você pode fornecer um objeto de evento como o segundo parâmetro para o método  [[yii\\base\\Component::trigger()]]. Este objeto precisa ser uma instância da classe  [[yii\\base\\Event]] ou de uma classe filha. Por exemplo:\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Component;\nuse yii\\base\\Event;\n\nclass MessageEvent extends Event\n{\n   public $message;\n}\n\nclass Mailer extends Component\n{\n   const EVENT_MESSAGE_SENT = 'messageSent';\n\n   public function send($message)\n   {\n       // ...sending $message...\n\n       $event = new MessageEvent;\n       $event->message = $message;\n       $this->trigger(self::EVENT_MESSAGE_SENT, $event);\n   }\n}\n```\n\nQuando o método [[yii\\base\\Component::trigger()]] é chamado, ele chamará todos os manipuladores ligados ao evento passado.\n\n\nDesvinculando manipuladores de eventos <span id=\"detaching-event-handlers\"></span>\n------------------------\n\nPara retirar um manipulador de um evento, chame o método [[yii\\base\\Component::off()]]. Por Exemplo:\n\n```php\n// o manipulador é uma função global\n$foo->off(Foo::EVENT_HELLO, 'function_name');\n\n// o manipulador é um método de objeto\n$foo->off(Foo::EVENT_HELLO, [$object, 'methodName']);\n\n// o manipulador é um método de estático da Classe\n$foo->off(Foo::EVENT_HELLO, ['app\\components\\Bar', 'methodName']);\n\n// o manipulador é uma função anônima\n$foo->off(Foo::EVENT_HELLO, $anonymousFunction);\n```\n\nNote que, em geral, você não deve tentar desvincular uma função anônima, a menos que você guarde em algum lugar quando ela for ligada ao evento. No exemplo acima, é assumido que a função anónima é armazenada em uma variável `$anonymousFunction`.\n\nPara desvincular todos os manipuladores de um evento, simplesmente chame [[yii\\base\\Component::off()]] sem o segundo parâmetro:\n\n```php\n$foo->off(Foo::EVENT_HELLO);\n```\n\n\nManipuladores de Eventos de Classe <span id=\"class-level-event-handlers\"></span>\n--------------------------\n\nAs subseções acima descreveram como anexar um manipulador para um evento a *nível de instância* (objeto).\nÀs vezes, você pode querer responder a um evento acionado por *todas* as instâncias da classe em vez de apenas uma instância específica. Em vez de anexar um manipulador de evento em todas as instâncias, você pode anexar o manipulador a *nível da classe* chamando o método estático [[yii\\base\\Event::on()]].\n\nPor exemplo, um objeto [Active Record](db-active-record.md) irá disparar um evento [[yii\\db\\BaseActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]] sempre que inserir um novo registro no banco de dados. A fim de acompanhar as inserções feitas por *cada* objeto [Active Record](db-active-record.md), você pode usar o seguinte código:\n\n```php\nuse Yii;\nuse yii\\base\\Event;\nuse yii\\db\\ActiveRecord;\n\nEvent::on(ActiveRecord::class, ActiveRecord::EVENT_AFTER_INSERT, function ($event) {\n   Yii::debug(get_class($event->sender) . ' is inserted');\n});\n```\n\nO manipulador de evento será invocado sempre que uma instância de [[yii\\db\\ActiveRecord|ActiveRecord]], ou uma de suas classes filhas, disparar o evento [[yii\\db\\BaseActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]]. No manipulador, você pode obter o objeto que disparou o evento através de `$event->sender`.\n\nQuando um objecto dispara um evento, ele irá primeiro chamar manipuladores de nível de instância, seguido pelos manipuladores de nível de classe.\n\nVocê pode disparar um evento de *nível de classe* chamando o método estático [[yii\\base\\Event::trigger()]]. Um evento de nível de classe não está associado com um objeto particular. Como resultado, ele fará a chamada dos manipuladores de eventos apenas a nível da classe. Por exemplo:\n\n```php\nuse yii\\base\\Event;\n\nEvent::on(Foo::class, Foo::EVENT_HELLO, function ($event) {\n   var_dump($event->sender);  // displays \"null\"\n});\n\nEvent::trigger(Foo::class, Foo::EVENT_HELLO);\n```\n\nNote que, neste caso, `$event->sender` refere-se ao nome da classe acionando o evento em vez de uma instância do objeto.\n\n> Observação: Já que um manipulador de nível de classe vai responder a um evento acionado por qualquer instância dessa classe, ou qualquer classe filha, você deve usá-lo com cuidado, especialmente se a classe é uma classe base de baixo nível, tal como [[yii\\base\\BaseObject]].\n\nPara desvincular um manipulador de evento de nível de classe, chame [[yii\\base\\Event::off()]]. Por exemplo:\n\n```php\n// desvincula $handler\nEvent::off(Foo::class, Foo::EVENT_HELLO, $handler);\n\n// Desvincula todos os manipuladores de Foo::EVENT_HELLO\nEvent::off(Foo::class, Foo::EVENT_HELLO);\n```\n\n\nEventos Globais <span id=\"global-events\"></span>\n-------------\n\nO Yii suporta o assim chamado *evento global*, que na verdade é um truque com base no mecanismo de eventos descrito acima.\nO evento global requer um *singleton* acessível globalmente, tal como a própria instância da [aplicação](structure-applications.md).\n\nPara criar o evento global, um evento *remetente* chama o método singleton `trigger()` para disparar o evento, em vez de chamar o método `trigger()` do *remetente* . Da mesma forma, os manipuladores de eventos são anexados ao evento no *singleton* . Por exemplo:\n\n```php\nuse Yii;\nuse yii\\base\\Event;\nuse app\\components\\Foo;\n\nYii::$app->on('bar', function ($event) {\n   echo get_class($event->sender);  // Mostra na tela \"app\\components\\Foo\"\n});\n\nYii::$app->trigger('bar', new Event(['sender' => new Foo]));\n```\n\nA vantagem de usar eventos globais é que você não precisa de um objeto ao anexar um manipulador para o evento que será acionado pelo objeto. Em vez disso, a inclusão do manipulador e o evento acionado são ambos feitos através do *singleton*. (Por exemplo, uma instância da aplicação). Contudo, já que o namespace dos eventos globais é compartilhado com todos, você deve nomear os eventos globais sabiamente, tais como a introdução de algum tipo de namespace (por exemplo. \"frontend.mail.sent\", \"backend.mail.sent\").\n"
  },
  {
    "path": "docs/guide-pt-BR/concept-properties.md",
    "content": "Propriedades\n===========\n\nNo PHP, atributos da classe são sempre chamadas de *propriedades*. Esses atributos fazem parte da definição da classe e são usadas para representar o estado de uma instância da classe (para diferenciar uma instância da classe de outra). Na prática, muitas vezes você pode querer lidar com a leitura ou a escrita de propriedades de maneiras especiais. Por exemplo, você pode querer trimar uma string sempre que for atribuído um valor para a propriedade `label`. Você *poderia* usar o código a seguir para realizar esta tarefa:\n\n```php\n$object->label = trim($label);\n```\n\nA desvantagem do código acima é que você teria que chamar `trim ()` em todos os lugares onde você definir a propriedade `label` no seu código. Se, no futuro, a propriedade `label` receber um novo requisito, tais como a primeira letra deve ser capitalizado, você teria que modificar novamente todos os pedaços de código que atribui um valor para a propriedade `label`. A repetição de código leva a erros, e é uma prática que você deve evitar sempre que possível.\n\nPara resolver este problema, o Yii introduz uma classe base chamada [[yii\\base\\BaseObject]] que suporta definições de propriedades baseadas nos métodos *getter* e *setter* da classe. Se uma classe precisar dessa funcionalidade, ela deve estender a classe [[yii\\base\\BaseObject]], ou de uma classe-filha.\n\n> Informação: Quase todas as classes nativas (core) do framework Yii estendem de [[yii\\base\\BaseObject]] ou de uma classe-filha. Isto significa que sempre que você vê um método *getter* ou *setter* em uma classe nativa (core), você pode usá-lo como uma propriedade.\n\nUm método getter é um método cujo nome inicia com a palavra `get`; um método setter inicia com `set`.\nO nome depois do prefixo `get` ou `set` define o nome da propriedade. Por exemplo, um getter `getLabel()` e/ou um setter `setLabel()` define a propriedade chamada `label`, como mostrado no código a seguir:\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\BaseObject;\n\nclass Foo extends BaseObject\n{\n    private $_label;\n\n    public function getLabel()\n    {\n        return $this->_label;\n    }\n\n    public function setLabel($value)\n    {\n        $this->_label = trim($value);\n    }\n}\n```\n\n(Para ser claro, os métodos getter e setter criam a propriedade `label`, que neste caso internamente refere-se ao atributo privado chamado `_label`.)\n\nPropriedades definidas por getters e setters podem ser usados como atributos da classe. A principal diferença é que quando tal propriedade é iniciada para leitura, o método getter correspondente será chamado; quando a propriedade é iniciada atribuindo um valor, o método setter correspondente será chamado. Por exemplo:\n\n```php\n// equivalent to $label = $object->getLabel();\n$label = $object->label;\n\n// equivalent to $object->setLabel('abc');\n$object->label = 'abc';\n```\n\nA propriedade definida por um método getter sem um método setter é *somente de leitura*. Tentando atribuir um valor a tal propriedade causará uma exceção [[yii\\base\\InvalidCallException|InvalidCallException]]. Semelhantemente, uma propriedade definida por um método setter sem um método getter é *somente de gravação *, e tentar ler tal propriedade também causará uma exceção. Não é comum ter propriedade *somente de gravação*.\n\nExistem várias regras especiais para, e limitações sobre, as propriedades definidas via getters e setters:\n\n* Os nomes dessas propriedades são *case-insensitive*. Por exemplo, `$object->label` e `$object->Label` são a mesma coisa.  Isso ocorre porque nomes de métodos no PHP são case-insensitive.\n* Se o nome de uma tal propriedade é o mesmo que um atributo da classe, esta última terá precedência. Por exemplo, se a classe `Foo` descrita acima tiver um atributo `label`, então a atribuição `$object->label = 'abc'` afetará o *atributo* 'label'; esta linha não executaria   `setLabel()` método setter.\n* Essas propriedades não suportam visibilidade. Não faz nenhuma diferença para a definição dos métodos getter ou setter se a propriedade é pública, protegida ou privada.\n* As propriedades somente podem ser definidas por getters e/ou setters *não estáticos*. Os métodos estáticos não serão tratados da mesma maneira.\n\nVoltando para o problema descrito no início deste guia, em vez de chamar `trim()` em todos os lugares que um valor for atribuído a `label`, agora o `trim()` só precisa ser invocado dentro do  setter `setLabel()`. E se uma nova exigência faz com que seja necessário que o 'label' seja inicializado capitalizado, o método `setLabel()` pode rapidamente ser modificado sem tocar em nenhum outro código. Esta única mudança afetará de forma global cada atribuição à propriedade `label`.\n"
  },
  {
    "path": "docs/guide-pt-BR/concept-service-locator.md",
    "content": "Service Locator\n===============\n\nUm service locator é um objeto que sabe como fornecer todos os tipos de serviços (ou componentes) que uma aplicação pode precisar. Num service locator, existe uma única instância de cada componente, exclusivamente identificados por um ID.\nVocê usa o ID para recuperar um componente do service locator.\n\nNo Yii, um service locator é simplesmente uma instância da classe [[yii\\di\\ServiceLocator]] ou de classes que as estendam.\n\nO service locator mais comumente utilizado no Yii é o objeto *application*, que pode ser acessado através de `\\Yii::$app`. Os serviços que ele fornece são chamados de *componentes de aplicação*, tais como os componentes `request`, `response`, e `urlManager`. Você pode configurar esses componentes, ou mesmo substituí-los com suas próprias implementações, facilmente através de funcionalidades fornecidas pelo service locator.\n\nAlém do objeto *application*, cada objeto *module* também é um service locator.\nPara usar um service locator, o primeiro passo é registrar os componentes nele. Um componente pode ser registrado com [[yii\\di\\ServiceLocator::set()]]. O código abaixo mostra os diferentes modos de registrar um componente:\n\n```php\nuse yii\\di\\ServiceLocator;\nuse yii\\caching\\FileCache;\n\n$locator = new ServiceLocator;\n\n// registra \"cache\" utilizando um nome de classe que pode ser usado para criar um componente\n$locator->set('cache', 'yii\\caching\\ApcCache');\n\n// Registra \"db\" utilizando um array de configuração que pode ser usado para criar um componente\n$locator->set('db', [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=localhost;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n]);\n\n// registra \"search\" utilizando uma função anônima que cria um componente\n$locator->set('search', function () {\n    return new app\\components\\SolrService;\n});\n\n// registra \"pageCache\" utilizando um componente\n$locator->set('pageCache', new FileCache);\n```\n\nUma vez que um componente tenha sido registado, você pode acessá-lo utilizando seu ID, em uma das duas maneiras abaixo:\n\n```php\n$cache = $locator->get('cache');\n// ou alternativamente\n$cache = $locator->cache;\n```\n\nComo mostrado acima, [[yii\\di\\ServiceLocator]] permite-lhe acessar um componente como uma propriedade usando o ID do componente. Quando você acessa um componente pela primeira vez, [[yii\\di\\ServiceLocator]] usará as informações de registro do componente para criar uma nova instância do componente e retorná-lo. Mais tarde, se o componente for acessado novamente, o service locator irá retornar a mesma instância.\n\nVocê pode utilizar [[yii\\di\\ServiceLocator::has()]] para checar se um ID de componente já está registrado.\nSe você executar [[yii\\di\\ServiceLocator::get()]] com um ID inválido, uma exceção será lançada.\n\n\nUma vez que service locators geralmente são criados com [configurações](concept-configurations.md), uma propriedade chamada [[yii\\di\\ServiceLocator::setComponents()|components]] é fornecida. Isso permite que você possa configurar e registrar vários componentes de uma só vez. O código a seguir mostra um array de configuração que pode ser utilizado para configurar um service locator (por exemplo. uma [aplicação](structure-applications.md)) com o \"db\", \"cache\" e \"search\" components:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=demo',\n            'username' => 'root',\n            'password' => '',\n        ],\n        'cache' => 'yii\\caching\\ApcCache',\n        'search' => function () {\n            $solr = new app\\components\\SolrService('127.0.0.1');\n            // ... outras inicializações ...\n            return $solr;\n        },\n    ],\n];\n```\n\nNo código acima, existe um caminho alternativo para configurar o componente \"search\". Em vez de escrever diretamente um PHP callback que cria uma instância de `SolrService`, você pode usar um método estático de classe para retornar semelhante a um callback, como mostrado abaixo:\n\n```php\nclass SolrServiceBuilder\n{\n    public static function build($ip)\n    {\n        return function () use ($ip) {\n            $solr = new app\\components\\SolrService($ip);\n            // ... outras inicializações ...\n            return $solr;\n        };\n    }\n}\n\nreturn [\n    // ...\n    'components' => [\n        // ...\n        'search' => SolrServiceBuilder::build('127.0.0.1'),\n    ],\n];\n```\n\nEsta abordagem alternativa é mais preferível quando você disponibiliza um componente Yii que encapsula alguma biblioteca de terceiros. Você usa o método estático como mostrado acima para representar a lógica complexa da construção do objeto de terceiros e o usuário do seu componente só precisa chamar o método estático para configurar o componente.\n\n"
  },
  {
    "path": "docs/guide-pt-BR/db-active-record.md",
    "content": "Active Record\n=============\n\nO [Active Record](https://pt.wikipedia.org/wiki/Active_record) fornece uma interface orientada a objetos para acessar e manipular dados armazenados em bancos de dados. Uma classe Active Record está associado a uma tabela da base de dados, uma instância do Active Record corresponde a uma linha desta tabela, e um *atributo* desta instância representa o valor de uma coluna desta linha. Em vez de escrever instruções SQL a mão, você pode acessar os atributos do Active Record e chamar os métodos do Active Record para acessar e manipular  os dados armazenados nas tabelas do banco de dados.\n\nPor exemplo, assumindo que `Customer` é uma classe Active Record que está associada com a tabela `customer` e `name` é uma coluna desta tabela. Você pode escrever o seguinte código para inserir uma nova linha na tabela `customer`:\n\n```php\n$customer = new Customer();\n$customer->name = 'Qiang';\n$customer->save();\n```\n\nO código acima é equivalente a seguinte instrução SQL escrita à mão para MySQL, que é menos intuitiva, mais propenso a erros, e pode até ter problemas de compatibilidade se você estiver usando um tipo diferente de banco de dados:\n\n```php\n$db->createCommand('INSERT INTO `customer` (`name`) VALUES (:name)', [\n   ':name' => 'Qiang',\n])->execute();\n```\n\nO Yii fornece suporte Active Record para os seguintes bancos de dados relacionais:\n\n* MySQL 4.1 ou superior: via [[yii\\db\\ActiveRecord]]\n* PostgreSQL 8.4 ou superior: via [[yii\\db\\ActiveRecord]]\n* SQLite 2 e 3: via [[yii\\db\\ActiveRecord]]\n* Microsoft SQL Server 2008 ou superior: via [[yii\\db\\ActiveRecord]]\n* Oracle: via [[yii\\db\\ActiveRecord]]\n* CUBRID 9.3 ou superior: via [[yii\\db\\ActiveRecord]] (observe que devido a um [bug](http://jira.cubrid.org/browse/APIS-658) na extensão PDO do cubrid, o tratamento de aspas não funciona, então você precisa do  CUBRID 9.3 como o cliente, bem como servidor)\n* Sphinx: via [[yii\\sphinx\\ActiveRecord]], requer a extensão `yii2-sphinx`\n* ElasticSearch: via [[yii\\elasticsearch\\ActiveRecord]], requer a extensão `yii2-elasticsearch`. Adicionalmente, o Yii também suporta o uso de Active Record com os seguintes bancos de dados NoSQL:\n\n* Redis 2.6.12 ou superior: via [[yii\\redis\\ActiveRecord]], requer a extensão `yii2-redis`\n* MongoDB 1.3.0 ou superior: via [[yii\\mongodb\\ActiveRecord]], requer a extensão `yii2-mongodb`\n\nNeste tutorial, vamos principalmente descrever o uso do Active Record para banco de dados relacionais. Todavia, maior parte do conteúdo descrito aqui também são aplicáveis a Active Record para bancos de dados NoSQL.\n\n\n## Declarando Classes Active Record <span id=\"declaring-ar-classes\"></span>\n\nPara começar, declare uma classe Active Record estendendo [[yii\\db\\ActiveRecord]]. Porque cada classe Active Record é associada a uma tabela do banco de dados, nesta classe você deve sobrescrever o método [[yii\\db\\ActiveRecord::tableName()|tableName()]] para especificar a tabela que a classe está associada.\n\nNo exemplo abaixo, declaramos uma classe Active Record chamada `Customer` para a tabela do banco de dados `customer`.\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass Customer extends ActiveRecord\n{\n   const STATUS_INACTIVE = 0;\n   const STATUS_ACTIVE = 1;\n   \n   /**\n    * @return string the name of the table associated with this ActiveRecord class.\n    */\n   public static function tableName()\n   {\n       return 'customer';\n   }\n}\n```\n\nInstâncias de Active Record são consideradas como [models (modelos)](structure-models.md). Por esta razão, geralmente colocamos as classes Active Record debaixo do namespace  `app\\models` (ou outros namespaces destinados a classes model). \n\nPorque [[yii\\db\\ActiveRecord]] estende a partir de [[yii\\base\\Model]], ele herda *todas* as características de [model](structure-models.md), tal como atributos, regras de validação, serialização de dados, etc.\n\n\n## Conectando ao Banco de Dados <span id=\"db-connection\"></span>\n\nPor padrão, o Active Record usa o [componente de aplicação](structure-application-components.md) `db` com a [[yii\\db\\Connection|DB connection]] para acessar e manipular os dados da base de dados. Como explicado em [Database Access Objects](db-dao.md), você pode configurar o componente `db` na configuração da aplicação como mostrado abaixo,\n\n```php\nreturn [\n   'components' => [\n       'db' => [\n           'class' => 'yii\\db\\Connection',\n           'dsn' => 'mysql:host=localhost;dbname=testdb',\n           'username' => 'demo',\n           'password' => 'demo',\n       ],\n   ],\n];\n```\n\nSe você quiser usar uma conexão de banco de dados diferente do que o componente `db`, você deve sobrescrever o método [[yii\\db\\ActiveRecord::getDb()|getDb()]]:\n\n```php\nclass Customer extends ActiveRecord\n{\n   // ...\n\n   public static function getDb()\n   {\n       // use the \"db2\" application component\n       return \\Yii::$app->db2;  \n   }\n}\n```\n\n\n## Consultando Dados <span id=\"querying-data\"></span>\n\nDepois de declarar uma classe Active Record, você pode usá-lo para consultar dados da tabela de banco de dados correspondente. Este processo geralmente leva os seguintes três passos:\n\n1. Crie um novo objeto query chamando o método [[yii\\db\\ActiveRecord::find()]];\n2. Construa o objeto query chamando os [métodos de query building](db-query-builder.md#building-queries);\n3. Chame um [método de query](db-query-builder.md#query-methods) para recuperar dados em uma instância do Active Record.\n\nComo você pode ver, isso é muito semelhante ao procedimento com [query builder](db-query-builder.md). A única diferença é que em vez de usar o operador `new` para criar um objeto query, você chama [[yii\\db\\ActiveRecord::find()]] para retornar um novo objeto query que é da classe [[yii\\db\\ActiveQuery]].\n\nA seguir, estão alguns exemplos que mostram como usar Active Query para pesquisar dados:\n\n```php\n// retorna um único customer cujo ID é 123\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::find()\n   ->where(['id' => 123])\n   ->one();\n\n// retorna todos customers ativos e os ordena por seus IDs\n// SELECT * FROM `customer` WHERE `status` = 1 ORDER BY `id`\n$customers = Customer::find()\n   ->where(['status' => Customer::STATUS_ACTIVE])\n   ->orderBy('id')\n   ->all();\n\n// retorna a quantidade de customers ativos\n// SELECT COUNT(*) FROM `customer` WHERE `status` = 1\n$count = Customer::find()\n   ->where(['status' => Customer::STATUS_ACTIVE])\n   ->count();\n\n// retorna todos customers em um array indexado pelos seus IDs\n// SELECT * FROM `customer`\n$customers = Customer::find()\n   ->indexBy('id')\n   ->all();\n```\n\nNo exemplo acima, `$customer` é um objeto `Customer` enquanto `$customers` é um array de objetos `Customer`. Todos são preenchidos com os dados recuperados da tabela `customer`.\n\n> Observação: Uma vez que o [[yii\\db\\ActiveQuery]] estende de [[yii\\db\\Query]], você pode usar *todos* os métodos do query building e da query tal como descrito na seção [Query Builder](db-query-builder.md).\n\nJá que consultar por valores de chave primária ou um conjunto de valores de coluna é uma tarefa comum, o Yii fornece dois métodos de atalho para este propósito:\n\n- [[yii\\db\\ActiveRecord::findOne()]]: retorna uma única instância de Active Record populado com a primeira linha do resultado da query.\n- [[yii\\db\\ActiveRecord::findAll()]]: retorna um array de instâncias de Active Record populados com *todo* o resultado da query.\n\nAmbos os métodos pode ter um dos seguintes formatos de parâmetro:\n\n- Um valor escalar: o valor é tratado como uma chave primária que se deseja procurar. O Yii irá determinar automaticamente que coluna é a chave primária lendo o schema da base de dados.\n- Um array de valores escalar: o array como uma chaves primárias que se deseja procurar.\n- Um array associativo: as chaves são nomes de colunas e os valores são os valores correspondentes as colunas que se deseja procurar. Por favor, consulte o [Hash Format](db-query-builder.md#hash-format) para mais detalhes.\n \nO código a seguir mostra como estes métodos podem ser usados:\n\n```php\n// retorna um único customer cujo ID é 123\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// retorna customers cujo ID é 100, 101, 123 or 124\n// SELECT * FROM `customer` WHERE `id` IN (100, 101, 123, 124)\n$customers = Customer::findAll([100, 101, 123, 124]);\n\n// retorna um customer ativo cujo ID é 123\n// SELECT * FROM `customer` WHERE `id` = 123 AND `status` = 1\n$customer = Customer::findOne([\n   'id' => 123,\n   'status' => Customer::STATUS_ACTIVE,\n]);\n\n// retorna todos os customers inativos\n// SELECT * FROM `customer` WHERE `status` = 0\n$customers = Customer::findAll([\n   'status' => Customer::STATUS_INACTIVE,\n]);\n```\n\n> Observação: Nem o [[yii\\db\\ActiveRecord::findOne()]] ou o [[yii\\db\\ActiveQuery::one()]] irão adicionar  `LIMIT 1` para a instrução SQL gerada. Se a sua query retornar muitas linhas de dados, você deve chamar `limit(1)` explicitamente para melhorar o desempenho, ex., `Customer::find()->limit(1)->one()`.\n\nAlém de usar os métodos do query building, você também pode escrever SQLs `puros` para pesquisar dados e preencher os objetos Active Record com o resultado. Você pode fazer isso chamando o método [[yii\\db\\ActiveRecord::findBySql()]]:\n\n```php\n// retorna todos os customers inatrivos\n$sql = 'SELECT * FROM customer WHERE status=:status';\n$customers = Customer::findBySql($sql, [':status' => Customer::STATUS_INACTIVE])->all();\n```\n\nNão chame outros métodos do query building depois de chamar [[yii\\db\\ActiveRecord::findBySql()|findBySql()]] pois eles serão ignorados.\n\n\n## Acessando Dados <span id=\"accessing-data\"></span>\n\nComo já mencionado, os dados retornados da base de dados são populados em uma instância do Active Record, e cada linha do resultado da query  corresponde a uma única instância do Active Record. Você pode acessar os valores das colunas acessando os atributos da instância do Active Record, Por exemplo,\n\n```php\n// \"id\" and \"email\" são os nomes das colunas na tabela \"customer\"\n$customer = Customer::findOne(123);\n$id = $customer->id;\n$email = $customer->email;\n```\n\n> Observação: Os atributos Active Record são nomeados após a associação com as colunas da tabela de uma forma case-sensitive. O Yii automaticamente define um atributo no Active Record para todas as colunas da tabela associada. Você NÃO deve declarar novamente qualquer um dos atributos. \n\nUma vez que os atributos do Active Record são nomeados de acordo com as colunas das tabelas, você pode achar que está escrevendo código PHP como `$customer->first_name`, que usa sublinhados para separar palavras em nomes de atributos se as colunas da tabela forem nomeadas desta maneira. Se você está preocupado com um estilo de código consistente, você deve renomear suas colunas da tabela em conformidade (usar camelCase, por exemplo).\n\n\n### Transformação de Dados (Data Transformation) <span id=\"data-transformation\"></span>\n\nAcontece frequentemente que os dados que estão sendo inseridos e/ou exibidos estão em um formato que é diferente do utilizado no momento da gravação na base de dados. Por exemplo, em uma base de dados você está gravando a data de aniversário do `customer` como UNIX timestamps (que não é muito amigável), embora, na maioria das vezes você gostaria de manipular aniversários como strings no formato de `'YYYY/MM/DD'`. Para atingir este objetivo, você pode definir métodos de *transformação de dados* na classe Active Record  `Customer` como a seguir:\n\n```php\nclass Customer extends ActiveRecord\n{\n   // ...\n\n   public function getBirthdayText()\n   {\n       return date('Y/m/d', $this->birthday);\n   }\n   \n   public function setBirthdayText($value)\n   {\n       $this->birthday = strtotime($value);\n   }\n}\n```\n\nAgora no seu código PHP, em vez de acessar `$customer->birthday`, você acessaria `$customer->birthdayText`, que lhe permitirá inserir e exibir data de aniversário dos `customers` no formato `'YYYY/MM/DD'`.\n\n> Dica: O exemplo acima mostra uma forma genérica de transformação de dados em diferentes formatos. Se você estiver trabalhando com valores de data, você pode usar o [DateValidator](tutorial-core-validators.md#date) e o [[yii\\jui\\DatePicker|DatePicker]], que é mais fácil e mais poderoso.\n\n\n### Recuperando Dados em Arrays <span id=\"data-in-arrays\"></span>\n\nEmbora a recuperação de dados através de objetos Active Record seja conveniente e flexível, pode não ser a melhor opção caso você tenha que retornar uma grande quantidade de dados devido ao grande consumo de memória. Neste caso, você pode recuperar usando arrays do PHP chamando [[yii\\db\\ActiveQuery::asArray()|asArray()]] antes de executar um método query:\n\n```php\n// retorna todos os `customers`\n// cada `customer` retornado é associado a um array\n$customers = Customer::find()\n   ->asArray()\n   ->all();\n```\n\n> Observação: Enquanto este método economiza memória e melhora o desempenho, ele é muito próximo a camada de abstração do DB e você vai perder a maioria dos recursos do Active Record. Uma distinção muito importante reside no tipo dos valores de coluna de dados. Quando você retorna dados em uma instância de Active Record, valores de colunas serão automaticamente convertidos de acordo com os tipos de coluna reais; de outra forma quando você retorna dados em arrays, valores de colunas serão strings (uma vez que são o resultado do PDO sem nenhum processamento), independentemente seus tipos de coluna reais.\n\n\n### Recuperando Dados em Lote <span id=\"data-in-batches\"></span>\n\nNo [Query Builder](db-query-builder.md), explicamos que você pode usar *batch query* para minimizar o uso de memória quando pesquisar uma grande quantidade de dados do banco de dados. Você pode utilizar a mesma técnica no Active Record. Por exemplo,\n\n```php\n// descarrega 10 `customers` a cada vez\nforeach (Customer::find()->batch(10) as $customers) {\n   // $customers é um array de 10 ou memos objetos Customer\n}\n\n// descarrega 10 `customers` por vez e faz a iteração deles um por um\nforeach (Customer::find()->each(10) as $customer) {\n   // $customer é um objeto Customer\n}\n\n// batch query com carga antecipada\nforeach (Customer::find()->with('orders')->each() as $customer) {\n   // $customer é um objeto Customer\n}\n```\n\n\n## Salvando Dados <span id=\"inserting-updating-data\"></span>\n\nUsando Active Record, você pode facilmente salvar dados em um banco de dados realizando as seguintes etapas:\n\n1. Preparar uma instância de Active Record\n2. Atribuir novos valores aos atributos do Active Record\n3. Chamar o método [[yii\\db\\ActiveRecord::save()]] para salvar os informações no banco de dados.\n\nPor exemplo,\n\n```php\n// insere uma nova linha de dados\n$customer = new Customer();\n$customer->name = 'James';\n$customer->email = 'james@example.com';\n$customer->save();\n\n// atualiza uma linha de dados existente\n$customer = Customer::findOne(123);\n$customer->email = 'james@newexample.com';\n$customer->save();\n```\n\nO método [[yii\\db\\ActiveRecord::save()|save()]] pode tanto inserir ou atualizar dados, dependendo do estado da instância do Active Record. Se a instância tiver sido recém criada através do operador `new`, ao chamar [[yii\\db\\ActiveRecord::save()|save()]] será realizado a inserção de uma nova linha; se a instância for o resultado de um método da query, ao chamar [[yii\\db\\ActiveRecord::save()|save()]] será realizado a atualização dos dados associados a instância. \n\nVocê pode diferenciar os dois estados de uma instância de Active Record verificando o valor da sua propriedade [[yii\\db\\ActiveRecord::isNewRecord|isNewRecord]]. Esta propriedade também é usada internamente pelo [[yii\\db\\ActiveRecord::save()|save()]] como mostrado abaixo:\n\n```php\npublic function save($runValidation = true, $attributeNames = null)\n{\n   if ($this->getIsNewRecord()) {\n       return $this->insert($runValidation, $attributeNames);\n   } else {\n       return $this->update($runValidation, $attributeNames) !== false;\n   }\n}\n```\n\n> Dica: Você pode chamar [[yii\\db\\ActiveRecord::insert()|insert()]] ou [[yii\\db\\ActiveRecord::update()|update()]] diretamente para inserir ou atualizar dados.\n \n\n### Validação de Dados<span id=\"data-validation\"></span>\n\nJá que o [[yii\\db\\ActiveRecord]] estende de [[yii\\base\\Model]], ele compartilha os mesmos recursos de [validação de dados](input-validation.md). Você pode declarar regras de validação sobrescrevendo o método [[yii\\db\\ActiveRecord::rules()|rules()]] e realizar a validação de dados chamando o método [[yii\\db\\ActiveRecord::validate()|validate()]].\n\nQuando você chama [[yii\\db\\ActiveRecord::save()|save()]], por padrão chamará [[yii\\db\\ActiveRecord::validate()|validate()]] automaticamente. somente quando a validação passa, os dados são de fato salvos; do contrário, simplesmente retorna falso, e você pode verificar a propriedade [[yii\\db\\ActiveRecord::errors|errors]] para recuperar a mensagem de erro de validação.  \n\n> Dica: Se você tiver certeza que os seus dados não precisam de validação (ex., os dados tem uma origem confiável),  você pode chamar `save(false)` para pular a validação.\n\n\n### Atribuição Maciça <span id=\"massive-assignment\"></span>\n\nComo um [models](structure-models.md) normal, instância de Active Record também oferece o [recurso de atribuição maciça](structure-models.md#massive-assignment). Usando este recurso, você pode atribuir valores para vários atributos de uma instância de Active Record em uma única declaração PHP, como mostrado a seguir. Lembre-se que somente [atributos de segurança](structure-models.md#safe-attributes) pode ser massivamente atribuídos.\n\n```php\n$values = [\n   'name' => 'James',\n   'email' => 'james@example.com',\n];\n\n$customer = new Customer();\n\n$customer->attributes = $values;\n$customer->save();\n```\n\n\n### Atualizando Contadores <span id=\"updating-counters\"></span>\n\nIsto é uma tarefa comum para incrementar ou decrementar uma coluna em uma tabela do banco de dados. Chamamos essas colunas como colunas de contador. Você pode usar [[yii\\db\\ActiveRecord::updateCounters()|updateCounters()]] para atualizar uma ou mais colunas de contadores. Por exemplo,\n\n```php\n$post = Post::findOne(100);\n\n// UPDATE `post` SET `view_count` = `view_count` + 1 WHERE `id` = 100\n$post->updateCounters(['view_count' => 1]);\n```\n\n> Observação: Se você usar [[yii\\db\\ActiveRecord::save()]] para atualizar uma coluna de contador, você pode acabar com um resultado impreciso, porque é provável que o mesmo contador esteja sendo salvo por várias solicitações que lêem e escrevem o mesmo valor do contador.\n\n\n### Atributos Sujos <span id=\"dirty-attributes\"></span>\n\nQuando você chama [[yii\\db\\ActiveRecord::save()|save()]] para salvar uma instância de Active Record, somente *atributos sujos* serão salvos. Um atributo é considerado *sujo* se o seu valor foi modificado desde que foi carregado a partir de DB ou salvos em DB, mais recentemente. Note que a validação de dados será realizada, independentemente se a instância do Active Record tiver ou não atributos sujos.\n\nO Active Record mantém automaticamente a lista de atributos sujas. Isto é feito mantendo uma versão antiga dos valores de atributos e comparando-as com as últimas informações. Você pode chamar [[yii\\db\\ActiveRecord::getDirtyAttributes()]] para pegar os atributos sujos correntes. Você também pode chamar [[yii\\db\\ActiveRecord::markAttributeDirty()]] para marcar explicitamente um atributo como sujo.\n\nSe você estiver interessado nos valores de atributos antes da sua modificação mais recente, você pode chamar [[yii\\db\\ActiveRecord::getOldAttributes()|getOldAttributes()]] ou [[yii\\db\\ActiveRecord::getOldAttribute()|getOldAttribute()]].\n\n> Observação: A comparação dos valores antigos e novos será feito usando o operador `===` portanto, um valor será considerado sujo mesmo se ele tiver o mesmo valor, mas um tipo diferente. Isto é comum quando o modelo recebe a entrada de dados do usuário a partir de um formulário HTML onde todos os valores são representados como string. Para garantir o tipo correto, por exemplo, valores inteiros você pode aplicar um [filtro de validação](input-validation.md#data-filtering): `['attributeName', 'filter', 'filter' => 'intval']`.\n\n\n### Valores Padrões de Atributos <span id=\"default-attribute-values\"></span>\n\nAlgumas de suas colunas de tabelas podem ter valores padrões definidos em um banco de dados. Algumas vezes, você pode querer preencher previamente o formulário Web para uma instância de Active Record com os seus valores padrões. Para evitar escrever os mesmos valores padrão novamente, você pode chamar [[yii\\db\\ActiveRecord::loadDefaultValues()|loadDefaultValues()]] para popular os valores padrões definidos pelo DB para os atributos correspondentes do Active Record:\n\n```php\n$customer = new Customer();\n$customer->loadDefaultValues();\n// $customer->xyz será atribuído o valor padrão declarado na definição da coluna \"xyz\"\n```\n\n\n### Atualizando Múltiplas Linhas <span id=\"updating-multiple-rows\"></span>\n\nOs métodos descritos acima fazem todo o trabalho em uma instância individual de Active Record, causando inserção ou atualização de linhas de tabela individuais. Para atualizar múltiplas linhas individualmente, você deve chamar o método estático [[yii\\db\\ActiveRecord::updateAll()|updateAll()]].\n\n```php\n// UPDATE `customer` SET `status` = 1 WHERE `email` LIKE `%@example.com%`\nCustomer::updateAll(['status' => Customer::STATUS_ACTIVE], ['like', 'email', '@example.com']);\n```\n\nDa mesma forma você pode chamar [[yii\\db\\ActiveRecord::updateAllCounters()|updateAllCounters()]] para atualizar colunas de contador de várias linhas ao mesmo tempo.\n\n```php\n// UPDATE `customer` SET `age` = `age` + 1\nCustomer::updateAllCounters(['age' => 1]);\n```\n\n\n## Deletando Dados <span id=\"deleting-data\"></span>\n\nPara deletar uma única linha de dados, primeiro recupere a instância de Active Record correspondente a linha e depois chame o método [[yii\\db\\ActiveRecord::delete()]].\n\n```php\n$customer = Customer::findOne(123);\n$customer->delete();\n```\n\nVocê pode chamar [[yii\\db\\ActiveRecord::deleteAll()]] para deletar múltiplas ou todas a linhas de dados. Por exemplo,\n\n```php\nCustomer::deleteAll(['status' => Customer::STATUS_INACTIVE]);\n```\n\n> Observação: Tenha muito cuidado quando chamar [[yii\\db\\ActiveRecord::deleteAll()|deleteAll()]] porque pode apagar todos os dados de sua tabela se você cometer um erro na especificação da condição.\n\n\n## Ciclo de Vida de um Active Record <span id=\"ar-life-cycles\"></span>\n\nÉ importante entender o ciclo de vida de um Active Record quando ele é usado para diferentes propósitos. Durante cada ciclo de vida, uma determinada sequência de métodos será invocada, e você pode substituir esses métodos para ter a chance de personalizar o ciclo de vida. Você também pode responder certos eventos disparados pelo Active Record durante o ciclo de vida para injetar o seu código personalizado. Estes eventos são especialmente úteis quando você está desenvolvendo [behaviors](concept-behaviors.md)que precisam personalizar o ciclo de vida do Active Record.\n\nA seguir, vamos resumir diversos ciclos de vida de um Active Record e os métodos/eventos que fazem parte do ciclo de vida.\n\n\n### Ciclo de Vida de uma Nova Instância <span id=\"new-instance-life-cycle\"></span>\n\nQuando se cria uma nova instância de Active Record através do operador `new`, acontece o seguinte ciclo de vida:\n\n1. Construção da classe;\n2. [[yii\\db\\ActiveRecord::init()|init()]]: dispara um evento [[yii\\db\\ActiveRecord::EVENT_INIT|EVENT_INIT]].\n\n\n### Ciclo de Vida de uma Pesquisa de Dados <span id=\"querying-data-life-cycle\"></span>\n\nQuando se pesquisa dados através de um dos [métodos de consulta](#querying-data), cada novo Active Record populado sofrerá o seguinte ciclo de vida:\n\n1. Contrução da classe.\n2. [[yii\\db\\ActiveRecord::init()|init()]]: dispara um evento [[yii\\db\\ActiveRecord::EVENT_INIT|EVENT_INIT]].\n3. [[yii\\db\\ActiveRecord::afterFind()|afterFind()]]: dispara um evento [[yii\\db\\ActiveRecord::EVENT_AFTER_FIND|EVENT_AFTER_FIND]].\n\n\n### Ciclo de Vida da Persistência de Dados <span id=\"saving-data-life-cycle\"></span>\n\nQuando se chama [[yii\\db\\ActiveRecord::save()|save()]] para inserir ou atualizar uma instância de Active Record, acontece o seguinte ciclo de vida:\n\n1. [[yii\\db\\ActiveRecord::beforeValidate()|beforeValidate()]]: dispara um evento [[yii\\db\\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]]. se o método retornar falso ou [[yii\\base\\ModelEvent::isValid]] for falso, o restante das etapas serão ignoradas.\n2. Executa a validação de dados. Se a validação de dados falhar, Os passos após o passo 3 serão ignorados. \n3. [[yii\\db\\ActiveRecord::afterValidate()|afterValidate()]]: dispara um evento [[yii\\db\\ActiveRecord::EVENT_AFTER_VALIDATE|EVENT_AFTER_VALIDATE]].\n4. [[yii\\db\\ActiveRecord::beforeSave()|beforeSave()]]: dispara um evento [[yii\\db\\ActiveRecord::EVENT_BEFORE_INSERT|EVENT_BEFORE_INSERT]] ou evento [[yii\\db\\ActiveRecord::EVENT_BEFORE_UPDATE|EVENT_BEFORE_UPDATE]]. Se o método retornar falso ou [[yii\\base\\ModelEvent::isValid]] for falso, o restante dos passos serão ignorados.\n5. Realiza a atual inserção ou atualização de dados;\n6. [[yii\\db\\ActiveRecord::afterSave()|afterSave()]]: dispara um evento [[yii\\db\\ActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]] ou evento [[yii\\db\\ActiveRecord::EVENT_AFTER_UPDATE|EVENT_AFTER_UPDATE]].\n  \n\n### Ciclo de Vida da Deleção de Dados <span id=\"deleting-data-life-cycle\"></span>\n\nQuando se chama [[yii\\db\\ActiveRecord::delete()|delete()]] para deletar uma instância de Active Record, acontece o seguinte ciclo de vida:\n\n1. [[yii\\db\\ActiveRecord::beforeDelete()|beforeDelete()]]: dispara um evento [[yii\\db\\ActiveRecord::EVENT_BEFORE_DELETE|EVENT_BEFORE_DELETE]]. Se o método retornar falso ou [[yii\\base\\ModelEvent::isValid]] for falso, o restante dos passos serão ignorados.\n2. Executa a atual deleção de dados.\n3. [[yii\\db\\ActiveRecord::afterDelete()|afterDelete()]]: dispara um evento [[yii\\db\\ActiveRecord::EVENT_AFTER_DELETE|EVENT_AFTER_DELETE]].\n\n\n> Observação: Chamar qualquer um dos seguintes métodos não iniciará qualquer um dos ciclos de vida listados acima:\n>\n> - [[yii\\db\\ActiveRecord::updateAll()]] \n> - [[yii\\db\\ActiveRecord::deleteAll()]]\n> - [[yii\\db\\ActiveRecord::updateCounters()]] \n> - [[yii\\db\\ActiveRecord::updateAllCounters()]] \n\n\n## Trabalhando com Transações <span id=\"transactional-operations\"></span>\n\nExistem duas formas de usar [transações](db-dao.md#performing-transactions) quando se trabalha com Active Record. A primeira maneira é anexar explicitamente chamadas de método Active Record em um bloco transacional, como mostrado abaixo,\n\n```php\n$customer = Customer::findOne(123);\n\nCustomer::getDb()->transaction(function($db) use ($customer) {\n   $customer->id = 200;\n   $customer->save();\n   // ...outras operações DB...\n});\n\n// ou como alternativa\n\n$transaction = Customer::getDb()->beginTransaction();\ntry {\n   $customer->id = 200;\n   $customer->save();\n   // ...outras operações DB...\n   $transaction->commit();\n} catch(\\Exception $e) {\n   $transaction->rollBack();\n   throw $e;\n}\n```\n\nA segunda maneira é listar as operações de banco de dados que exigem suporte transacional no método [[yii\\db\\ActiveRecord::transactions()]]. Por exemplo,\n\n```php\nclass Customer extends ActiveRecord\n{\n   public function transactions()\n   {\n       return [\n           'admin' => self::OP_INSERT,\n           'api' => self::OP_INSERT | self::OP_UPDATE | self::OP_DELETE,\n           // o código acima é equivalente ao código abaixo:\n           // 'api' => self::OP_ALL,\n       ];\n   }\n}\n```\n\nO método [[yii\\db\\ActiveRecord::transactions()]] deve retornar um array cujas chaves são [cenário](structure-models.md#scenarios) nomes e valores das operações correspondentes que devem ser embutidas dentro de transações. Você deve usar as seguintes constantes para fazer referência a diferentes operações de banco de dados:\n\n* [[yii\\db\\ActiveRecord::OP_INSERT|OP_INSERT]]: operação de inserção realizada pelo [[yii\\db\\ActiveRecord::insert()|insert()]];\n* [[yii\\db\\ActiveRecord::OP_UPDATE|OP_UPDATE]]: operação de atualização realizada pelo [[yii\\db\\ActiveRecord::update()|update()]];\n* [[yii\\db\\ActiveRecord::OP_DELETE|OP_DELETE]]: operação de deleção realizada pelo [[yii\\db\\ActiveRecord::delete()|delete()]].\n\nUtilize o operador `|` concatenar as constantes acima para indicar várias operações. Você também pode usar a constante de atalho [[yii\\db\\ActiveRecord::OP_ALL|OP_ALL]] para referenciar todas as três constantes acima.\n\n\n## Bloqueios Otimistas <span id=\"optimistic-locks\"></span>\n\nO bloqueio otimista é uma forma de evitar conflitos que podem ocorrer quando uma única linha de dados está sendo atualizado por vários usuários. Por exemplo, se ambos os usuário A e B estiverem editando o mesmo artigo de Wiki ao mesmo tempo. Após o usuário A salvar suas alterações, o usuário B pressiona o botão  \"Save\" em uma tentativa de salvar suas edições também. Uma vez que o usuário B está trabalhando em uma versão desatualizada do artigo, seria desejável ter uma maneira de impedi-lo de salvar o artigo e mostrar-lhe alguma mensagem.\n\nBloqueio otimista resolve o problema acima usando uma coluna para gravar o número de versão de cada registro. Quando um registro está sendo salvo com um número de versão desatualizado, uma exceção [[yii\\db\\StaleObjectException]] será lançada, que impede que o registro seja salvo. Bloqueio otimista só é suportado quando você atualiza ou exclui um registro de dados existente usando [[yii\\db\\ActiveRecord::update()]] ou [[yii\\db\\ActiveRecord::delete()]] respectivamente.\n\nPara usar bloqueio otimista:\n\n1. Crie uma coluna na tabela do banco de dados associada com a classe Active Record para armazenar o número da versão de cada registro. A coluna deve ser do tipo  biginteger (no MySQL ela deve ser `BIGINT DEFAULT 0`).\n2. Sobrescreva o método [[yii\\db\\ActiveRecord::optimisticLock()]] para retornar o nome desta coluna.\n3. No formulário Web que recebe as entradas dos usuários, adicione um campo hidden para armazenar o número da versão corrente do registro que está sendo atualizado. Certifique-se que seu atributo versão possui regras de validação de entrada validadas com êxito.\n4. Na ação do controller que atualiza o registro usando Active Record, mostre uma estrutura *try catch* da [[yii\\db\\StaleObjectException]] exceção. Implementar lógica de negócios necessária (ex. mesclar as mudanças, mostrar dados obsoletos) para resolver o conflito.\n  \nPor exemplo, digamos que a coluna de versão se chama `version`. Você pode implementar bloqueio otimista conforme o código abaixo.\n\n```php\n// ------ view code -------\n\nuse yii\\helpers\\Html;\n\n// ...Outros campos de entrada de dados\necho Html::activeHiddenInput($model, 'version');\n\n\n// ------ controller code -------\n\nuse yii\\db\\StaleObjectException;\n\npublic function actionUpdate($id)\n{\n   $model = $this->findModel($id);\n\n   try {\n       if ($model->load(Yii::$app->request->post()) && $model->save()) {\n           return $this->redirect(['view', 'id' => $model->id]);\n       } else {\n           return $this->render('update', [\n               'model' => $model,\n           ]);\n       }\n   } catch (StaleObjectException $e) {\n       // lógica para resolver o conflito\n   }\n}\n```\n\n\n## Trabalhando com Dados Relacionais <span id=\"relational-data\"></span>\n\nAlém de trabalhar com tabelas individuais, o Active Record também é capaz de trazer dados de várias tabelas relacionadas, tornando-os prontamente acessíveis através dos dados primários. Por exemplo, a tabela de clientes está relacionada a tabela de pedidos porque um cliente pode ter múltiplos pedidos. Com uma declaração adequada desta relação, você pode ser capaz de acessar informações do pedido de um cliente utilizando a expressão `$customer->orders` que devolve informações sobre o pedido do cliente em um array de instâncias de Active Record de `Pedidos`.\n\n\n### Declarando Relações <span id=\"declaring-relations\"></span>\n\nPara trabalhar com dados relacionais usando Active Record, você primeiro precisa declarar as relações nas classes Active Record. A tarefa é tão simples como declarar um *método de relação* para cada relação desejada. Segue exemplos:\n\n```php\nclass Customer extends ActiveRecord\n{\n   public function getOrders()\n   {\n       return $this->hasMany(Order::class, ['customer_id' => 'id']);\n   }\n}\n\nclass Order extends ActiveRecord\n{\n   public function getCustomer()\n   {\n       return $this->hasOne(Customer::class, ['id' => 'customer_id']);\n   }\n}\n```\n\nNo código acima, nós declaramos uma relação `orders` para a classe `Customer`, e uma relação `customer` para a classe `Order`. \n\nCada método de relação deve ser nomeado como `getXyz`. Nós chamamos de `xyz` (a primeira letra é em letras minúsculas) o *nome da relação*. Note que os nomes de relações são *case sensitive*.\n\nAo declarar uma relação, você deve especificar as seguintes informações:\n\n- A multiplicidade da relação: especificada chamando tanto o método [[yii\\db\\ActiveRecord::hasMany()|hasMany()]] quanto o método [[yii\\db\\ActiveRecord::hasOne()|hasOne()]]. No exemplo acima você pode facilmente ler nas declarações de relação que um `customer` tem vários `orders` enquanto uma `order` só tem um `customer`.\n- O nome da classe Active Record relacionada: especificada no primeiro parâmetro dos métodos [[yii\\db\\ActiveRecord::hasMany()|hasMany()]] e [[yii\\db\\ActiveRecord::hasOne()|hasOne()]]. Uma prática recomendada é chamar `Xyz::class` para obter o nome da classe para que você possa receber suporte do preenchimento automático de IDEs bem como detecção de erros. \n- A ligação entre os dois tipos de dados: especifica a(s) coluna(s) por meio do qual os dois tipos de dados se relacionam. Os valores do array são as colunas da tabela primária (representada pela classe Active Record que você declarou as relações), enquanto as chaves do array são as colunas da tabela relacionada.\n\nUma regra fácil de lembrar é, como você pode ver no exemplo acima, você escreve a coluna que pertence ao Active Record relacionado diretamente ao lado dele. Você pode ver que `customer_id` é uma propriedade de `Order` e `id` é uma propriedade de  `Customer`.\n \n\n### Acessando Dados Relacionais <span id=\"accessing-relational-data\"></span>\n\nApós declarar as relações, você pode acessar os dados relacionados através dos nomes das relações. Isto é como acessar uma [propriedade](concept-properties.md) de um objeto definida por um método de relação. Por esta razão, nós podemos chamá-la de *propriedade de relação*. Por exemplo,\n\n```php\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123\n// $orders is an array of Order objects\n$orders = $customer->orders;\n```\n\n> Observação: quando você declara uma relação chamada `xyz` através de um método getter `getXyz()`, você terá acesso a `xyz` como uma [propriedade de objeto](concept-properties.md). Note que o nome é case-sensitive.\n \nSe a relação for declarada com [[yii\\db\\ActiveRecord::hasMany()|hasMany()]], acessar esta propriedade irá retornar um array de instâncias de Active Record relacionais; Se a relação for declarada com [[yii\\db\\ActiveRecord::hasOne()|hasOne()]], acessar esta propriedade irá retornar a instância de Active Record relacional ou `null` se não encontrar dados relacionais.\n\nQuando você acessa uma propriedade de relação pela primeira vez, uma instrução SQL será executada, como mostrado no exemplo acima. Se a mesma propriedade for acessada novamente, o resultado anterior será devolvido sem executar novamente a instrução SQL. Para forçar a execução da instrução SQL, você deve primeiramente remover a configuração da propriedade de relação: `unset($customer->orders)`.\n\n> Observação: Embora este conceito é semelhante ao recurso  de [propriedade de objeto](concept-properties.md), existe uma diferença importante. Em uma propriedade de objeto normal o valor da propriedade é do mesmo tipo que o método getter definindo. Já o método de relação retorna uma instância de  [[yii\\db\\ActiveQuery]], embora o acesso a uma propriedade de relação retorne uma instância de [[yii\\db\\ActiveRecord]] ou um array deste tipo.\n>\n> ```php\n> $customer->orders; // é um array de objetos `Order`\n> $customer->getOrders(); // returna uma instância de ActiveQuery\n> ```\n> \n> Isso é útil para a criação de consultas personalizadas, que está descrito na próxima seção.\n\n\n### Consulta Relacional Dinâmica <span id=\"dynamic-relational-query\"></span>\n\nUma vez que um método de relação retorna uma instância de [[yii\\db\\ActiveQuery]], você pode construir uma query usando os métodos de `query building` antes de executá-la. Por exemplo,\n\n```php\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `subtotal` > 200 ORDER BY `id`\n$orders = $customer->getOrders()\n   ->where(['>', 'subtotal', 200])\n   ->orderBy('id')\n   ->all();\n```\n\nDiferente de acessar uma propriedade de relação, cada vez que você executar uma consulta relacional dinâmica através de um método de relação, uma instrução SQL será executada, mesmo que a mesma consulta relacional dinâmica tenha sido executada anteriormente.\n\nAlgumas vezes você pode querer parametrizar uma relação para que possa executar mais facilmente uma consulta relacional dinâmica. Por exemplo, você pode declarar uma relação  `bigOrders` da seguinte forma, \n\n```php\nclass Customer extends ActiveRecord\n{\n   public function getBigOrders($threshold = 100)\n   {\n       return $this->hasMany(Order::class, ['customer_id' => 'id'])\n           ->where('subtotal > :threshold', [':threshold' => $threshold])\n           ->orderBy('id');\n   }\n}\n```\n\nEm seguida, você será capaz de executar as seguintes consultas relacionais:\n\n```php\n// SELECT * FROM `order` WHERE `subtotal` > 200 ORDER BY `id`\n$orders = $customer->getBigOrders(200)->all();\n\n// SELECT * FROM `order` WHERE `subtotal` > 100 ORDER BY `id`\n$orders = $customer->bigOrders;\n```\n\n\n### Relações Através de Tabela de Junção <span id=\"junction-table\"></span>\n\nEm uma modelagem de banco de dados, quando a multiplicidade entre duas tabelas relacionadas é `many-to-many`, geralmente é criada uma [tabela de junção](https://pt.wikipedia.org/wiki/Entidade_associativa). Por exemplo, a tabela `order` e a tabela `item` podem se relacionar através da tabela de junção chamada `order_item`. Um `order`, então, corresponderá a múltiplos `order items`, enquanto um `product item` também corresponderá a múltiplos `order items`.\n\nAo declarar tais relações, você chamaria [[yii\\db\\ActiveQuery::via()|via()]] ou [[yii\\db\\ActiveQuery::viaTable()|viaTable()]] para especificar a tabela de junção. A diferença entre [[yii\\db\\ActiveQuery::via()|via()]] e [[yii\\db\\ActiveQuery::viaTable()|viaTable()]] é que o primeiro especifica a tabela de junção em função a uma relação existente enquanto o último faz referência diretamente a tabela de junção. Por exemplo,\n\n```php\nclass Order extends ActiveRecord\n{\n   public function getItems()\n   {\n       return $this->hasMany(Item::class, ['id' => 'item_id'])\n           ->viaTable('order_item', ['order_id' => 'id']);\n   }\n}\n```\n\nou alternativamente,\n\n```php\nclass Order extends ActiveRecord\n{\n   public function getOrderItems()\n   {\n       return $this->hasMany(OrderItem::class, ['order_id' => 'id']);\n   }\n\n   public function getItems()\n   {\n       return $this->hasMany(Item::class, ['id' => 'item_id'])\n           ->via('orderItems');\n   }\n}\n```\n\nA utilização das relações declaradas com uma tabela de junção é a mesma que a das relações normais. Por exemplo,\n\n```php\n// SELECT * FROM `order` WHERE `id` = 100\n$order = Order::findOne(100);\n\n// SELECT * FROM `order_item` WHERE `order_id` = 100\n// SELECT * FROM `item` WHERE `item_id` IN (...)\n// returns an array of Item objects\n$items = $order->items;\n```\n\n\n### Lazy e Eager Loading <span id=\"lazy-eager-loading\"></span>\n\nNa seção [Acessando Dados Relacionais](#accessing-relational-data), nós explicamos que você pode acessar uma propriedade de relação de uma instância de Active Record como se acessa uma propriedade de objeto normal. Uma instrução SQL será executada somente quando você acessar a propriedade de relação pela primeira vez. Chamamos esta forma de acessar estes dados de *lazy loading*. Por exemplo,\n\n```php\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123\n$orders = $customer->orders;\n\n// nenhum SQL é executado\n$orders2 = $customer->orders;\n```\n\nO uso de lazy loading é muito conveniente. Entretanto, pode haver um problema de desempenho caso você precise acessar a mesma propriedade de relação a partir de múltiplas instâncias de Active Record. Considere o exemplo a seguir. Quantas instruções SQL serão executadas?\n\n```php\n// SELECT * FROM `customer` LIMIT 100\n$customers = Customer::find()->limit(100)->all();\n\nforeach ($customers as $customer) {\n   // SELECT * FROM `order` WHERE `customer_id` = ...\n   $orders = $customer->orders;\n}\n```\n\nComo você pode ver no comentário do código acima, há 101 instruções SQL sendo executadas! Isto porque cada vez que você acessar a propriedade de relação  `orders` com um objeto `Customer` diferente no bloco `foreach`, uma instrução SQL será executada.\n\nPara resolver este problema de performance, você pode usar o *eager loading*, como mostrado abaixo,\n\n```php\n// SELECT * FROM `customer` LIMIT 100;\n// SELECT * FROM `orders` WHERE `customer_id` IN (...)\n$customers = Customer::find()\n   ->with('orders')\n   ->limit(100)\n   ->all();\n\nforeach ($customers as $customer) {\n   // nenhum SQL é executado\n   $orders = $customer->orders;\n}\n```\n\nAo chamar [[yii\\db\\ActiveQuery::with()]], você instrui ao Active Record trazer os `orders` dos primeiros 100 `customers` em uma única instrução SQL. Como resultado, você reduz o número de instruções SQL executadas de 101 para 2.\n\nVocê pode utilizar esta abordagem com uma ou várias relações. Você pode até mesmo utilizar *eager loading* com *relações aninhadas*. Um relação aninhada é uma relação que é declarada dentro de uma classe Active Record de relação. Por exemplo, `Customer` se relaciona com `Order` através da relação `orders`, e `Order` está relacionado com `Item` através da relação `items`. Quando pesquisar por `Customer`, você pode fazer o *eager loading* de `items` usando a notação de relação aninhada `orders.items`. \n\nO código a seguir mostra diferentes formas de utilizar [[yii\\db\\ActiveQuery::with()|with()]]. Assumimos que a classe `Customer` tem duas relações `orders` e `country`, enquanto a classe `Order` tem uma relação `items`.\n\n```php\n// carga antecipada de \"orders\" e \"country\"\n$customers = Customer::find()->with('orders', 'country')->all();\n// equivalente à sintaxe de array abaixo\n$customers = Customer::find()->with(['orders', 'country'])->all();\n// nenhum SQL executado\n$orders= $customers[0]->orders;\n// nenhum SQL executado\n$country = $customers[0]->country;\n\n// carga antecipada de \"orders\" e a relação aninhada \"orders.items\"\n$customers = Customer::find()->with('orders.items')->all();\n// acessa os \"items\" do primeiro \"order\" e do primeiro \"customer\"\n// nenhum SQL executado\n$items = $customers[0]->orders[0]->items;\n```\n\nVocê pode carregar antecipadamente relações profundamente aninhadas, tais como `a.b.c.d`. Todas as relações serão carregadas antecipadamente. Isto é, quando você chamar [[yii\\db\\ActiveQuery::with()|with()]] usando `a.b.c.d`, você fará uma carga antecipada de `a`, `a.b`, `a.b.c` e `a.b.c.d`.  \n\n> Observação: Em geral, ao fazer uma carga antecipada de `N` relações em que `M` relações são definidas com uma [tabela de junção](#junction-table), um total de instruções SQL `N+M+1` serão executadas. Note que uma relação aninhada `a.b.c.d` conta como 4 relações.\n\nAo carregar antecipadamente uma relação, você pode personalizar a consulta relacional correspondente utilizando uma função anônima. Por exemplo,\n\n```php\n// procura “customers” trazendo junto “country” e “orders” ativas\n// SELECT * FROM `customer`\n// SELECT * FROM `country` WHERE `id` IN (...)\n// SELECT * FROM `order` WHERE `customer_id` IN (...) AND `status` = 1\n$customers = Customer::find()->with([\n   'country',\n   'orders' => function ($query) {\n       $query->andWhere(['status' => Order::STATUS_ACTIVE]);\n   },\n])->all();\n```\n\nAo personalizar a consulta relacional para uma relação, você deve especificar o nome da relação como uma chave de array e usar uma função anônima como o valor de array correspondente. A função anônima receberá um parâmetro `$query` que representa o objeto [[yii\\db\\ActiveQuery]] utilizado para realizar a consulta relacional para a relação. No exemplo acima, modificamos a consulta relacional, acrescentando uma condição adicional sobre o status de 'orders'.\n\n> Observação: Se você chamar [[yii\\db\\Query::select()|select()]] ao carregar antecipadamente as relações, você tem que certificar-se de que as colunas referenciadas na relação estão sendo selecionadas. Caso contrário, os modelos relacionados podem não ser carregados corretamente. Por exemplo,\n>\n> ```php\n> $orders = Order::find()->select(['id', 'amount'])->with('customer')->all();\n> // $orders[0]->customer é sempre nulo. Para corrigir o problema, você deve fazer o seguinte:\n> $orders = Order::find()->select(['id', 'amount', 'customer_id'])->with('customer')->all();\n> ```\n\n\n### Relações utilizando JOIN <span id=\"joining-with-relations\"></span>\n\n> Observação: O conteúdo descrito nesta subsecção é aplicável apenas aos bancos de dados relacionais, tais como MySQL, PostgreSQL, etc.\n\nAs consultas relacionais que temos descrito até agora só fizeram referência as chave primária ao consultar os dados. Na realidade, muitas vezes precisamos referenciar colunas nas tabelas relacionadas. Por exemplo, podemos querer trazer os 'customers' que tenham no mínimo um 'order' ativo. Para solucionar este problema, podemos fazer uma query com join como a seguir:\n\n```php\n// SELECT `customer`.* FROM `customer`\n// LEFT JOIN `order` ON `order`.`customer_id` = `customer`.`id`\n// WHERE `order`.`status` = 1\n// \n// SELECT * FROM `order` WHERE `customer_id` IN (...)\n$customers = Customer::find()\n   ->select('customer.*')\n   ->leftJoin('order', '`order`.`customer_id` = `customer`.`id`')\n   ->where(['order.status' => Order::STATUS_ACTIVE])\n   ->with('orders')\n   ->all();\n```\n\n> Observação: É importante evitar ambiguidade de nomes de colunas ao criar queries com JOIN. Uma prática comum é prefixar os nomes das colunas com os nomes de tabela correspondente. Entretanto, uma melhor abordagem é a de explorar as declarações de relação existente chamando [[yii\\db\\ActiveQuery::joinWith()]]:\n\n```php\n$customers = Customer::find()\n   ->joinWith('orders')\n   ->where(['order.status' => Order::STATUS_ACTIVE])\n   ->all();\n```\n\nAmbas as formas executam o mesmo conjunto de instruções SQL. Embora a última abordagem seja mais elegante. \n\nPor padrão , [[yii\\db\\ActiveQuery::joinWith()|joinWith()]] usará `LEFT JOIN` para juntar a tabela primária com a tabela relacionada. Você pode especificar um tipo de join diferente (ex. `RIGHT JOIN`) através do seu terceiro parâmetro `$joinType`. Se o tipo de join que você quer for `INNER JOIN`, você pode apenas chamar [[yii\\db\\ActiveQuery::innerJoinWith()|innerJoinWith()]].\n\nChamando [[yii\\db\\ActiveQuery::joinWith()|joinWith()]] os dados relacionais serão  [carregados antecipadamente](#lazy-eager-loading) por padrão. Se você não quiser trazer o dados relacionados, você pode especificar o segundo parâmetro `$eagerLoading` como falso. \n\nAssim como [[yii\\db\\ActiveQuery::with()|with()]], você pode juntar uma ou várias relações; você pode personalizar as queries relacionais dinamicamente; você pode utilizar join com relações aninhadas; e pode misturar o uso de [[yii\\db\\ActiveQuery::with()|with()]] e [[yii\\db\\ActiveQuery::joinWith()|joinWith()]]. Por exemplo,\n\n```php\n$customers = Customer::find()->joinWith([\n   'orders' => function ($query) {\n       $query->andWhere(['>', 'subtotal', 100]);\n   },\n])->with('country')\n   ->all();\n```\n\nAlgumas vezes ao juntar duas tabelas, você pode precisar especificar alguma condição extra na estrutura SQL `ON` do JOIN. Isto pode ser feito chamando o método [[yii\\db\\ActiveQuery::onCondition()]] como a seguir:\n\n```php\n// SELECT `customer`.* FROM `customer`\n// LEFT JOIN `order` ON `order`.`customer_id` = `customer`.`id` AND `order`.`status` = 1 \n// \n// SELECT * FROM `order` WHERE `customer_id` IN (...)\n$customers = Customer::find()->joinWith([\n   'orders' => function ($query) {\n       $query->onCondition(['order.status' => Order::STATUS_ACTIVE]);\n   },\n])->all();\n```\n\nA query acima retorna *todos* `customers`, e para cada `customer` retorna todos `orders` ativos. Note que isto difere do nosso primeiro exemplo onde retornávamos apenas `customers` que tinham no mínimo um `order` ativo.\n\n> Observação: Quando [[yii\\db\\ActiveQuery]] é especificada com uma condição usando [[yii\\db\\ActiveQuery::onCondition()|onCondition()]], a condição será colocada no `ON` se a query tiver um JOIN. Se a query não utilizar JOIN, a condição será colocada no `WHERE` da query.\n\n\n### Relações Inversas <span id=\"inverse-relations\"></span>\n\nDeclarações de relação são geralmente recíprocas entre duas classes de Active Record. Por exemplo, `Customer` se relaciona com `Order` através da relação `orders`, e `Order` se relaciona com `Customer` através da relação `customer`.\n\n```php\nclass Customer extends ActiveRecord\n{\n   public function getOrders()\n   {\n       return $this->hasMany(Order::class, ['customer_id' => 'id']);\n   }\n}\n\nclass Order extends ActiveRecord\n{\n   public function getCustomer()\n   {\n       return $this->hasOne(Customer::class, ['id' => 'customer_id']);\n   }\n}\n```\n\nAgora considere a seguinte parte do código:\n\n```php\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123\n$order = $customer->orders[0];\n\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer2 = $order->customer;\n\n// displays \"not the same\"\necho $customer2 === $customer ? 'same' : 'not the same';\n```\n\nPodemos pensar que `$customer` e `$customer2` são a mesma coisa, mas não são! Na verdade eles contêm a mesma informação de `customer`, mas são objetos diferentes. Ao acessar `$order->customer`, uma instrução SQL extra é executada para popular um novo objeto `$customer2`.\n\nPara evitar esta redundância de execução de SQL no exemplo acima, devemos dizer para o Yii que `customer` é uma *relação inversa* de `orders` chamando o método [[yii\\db\\ActiveQuery::inverseOf()|inverseOf()]] como mostrado abaixo:\n\n```php\nclass Customer extends ActiveRecord\n{\n   public function getOrders()\n   {\n       return $this->hasMany(Order::class, ['customer_id' => 'id'])->inverseOf('customer');\n   }\n}\n```\n\nCom esta modificação na declaração de relação, podemos ter:\n\n```php\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123\n$order = $customer->orders[0];\n\n// Nenhum SQL será executado\n$customer2 = $order->customer;\n\n// exibe \"same\"\necho $customer2 === $customer ? 'same' : 'not the same';\n```\n\n> Observação: relações inversas não podem ser definidas por relações que envolvam uma  [tabela de junção](#junction-table). Isto é, se uma relação for definida com [[yii\\db\\ActiveQuery::via()|via()]] ou [[yii\\db\\ActiveQuery::viaTable()|viaTable()]], você não deve chamar [[yii\\db\\ActiveQuery::inverseOf()|inverseOf()]].\n\n\n## Salvando Relações <span id=\"saving-relations\"></span>\n\nAo trabalhar com dados relacionais, muitas vezes você precisa estabelecer relações entre dados diferentes ou destruir relações existentes. Isso requer a definição de valores apropriados para as colunas que definem as relações. Usando Active Record, você pode acabar escrevendo o seguinte código:\n\n```php\n$customer = Customer::findOne(123);\n$order = new Order();\n$order->subtotal = 100;\n// ...\n\n// definindo o atributo que define a relação \"customer\" em \"Order\"\n$order->customer_id = $customer->id;\n$order->save();\n```\n\nActive Record fornece o método [[yii\\db\\ActiveRecord::link()|link()]] que lhe permite realizar essa tarefa de uma forma mais elegante:\n\n```php\n$customer = Customer::findOne(123);\n$order = new Order();\n$order->subtotal = 100;\n// ...\n\n$order->link('customer', $customer);\n```\n\nO método [[yii\\db\\ActiveRecord::link()|link()]] requer que você especifique o nome da relação e a instância de Active Record alvo cuja relação deve ser estabelecida. O método irá modificar os valores dos atributos que apontam para duas instâncias de Active Record e salvá-los no banco de dados. No exemplo acima, o atributo `customer_id` da instância `Order` será definido para ser o valor do atributo `id` da instância `Customer` e depois salvá-lo no banco de dados.\n\n> Observação: Você não pode conectar duas instâncias de Active Record recém-criadas.\n\nOs benefícios de usar [[yii\\db\\ActiveRecord::link()|link()]] é ainda mais óbvio quando uma relação é definida por meio de uma [tabela de junção](#junction-table). Por exemplo, você pode usar o código a seguir para ligar uma instância de `Order` com uma instância de `Item`:\n\n```php\n$order->link('items', $item);\n```\n\nO código acima irá inserir automaticamente uma linha na tabela de junção `order_item` para relacionar a `order` com o `item`.\n\n> Observação: O método [[yii\\db\\ActiveRecord::link()|link()]] não realizará nenhuma validação de dados ao salvar a instância de Active Record afetada. É de sua responsabilidade validar todos os dados de entrada antes de chamar esse método.\n\nA operação inversa para [[yii\\db\\ActiveRecord::link()|link()]] é [[yii\\db\\ActiveRecord::unlink()|unlink()]] que quebra uma relação existente entre duas instâncias de Active Record. Por exemplo,\n\n```php\n$customer = Customer::find()->with('orders')->where(['id' => 123])->one();\n$customer->unlink('orders', $customer->orders[0]);\n```\n\n\nPor padrão, o método [[yii\\db\\ActiveRecord::unlink()|unlink()]]  irá definir o(s) valor(es) da(s) chave(s) estrangeira(s)  que especificam a relação existente para `null`. Você pode, entretanto, optar por excluir a linha da tabela que contém a chave estrangeira passando o parâmetro `$delete` como `true` para o método.\n\nQuando uma tabela de junção está envolvida numa relação, chamar o método [[yii\\db\\ActiveRecord::unlink()|unlink()]] fará com que as chaves estrangeiras na tabela de junção sejam apagadas, ou a deleção da linha correspondente na tabela de junção se `$delete` for `true`.\n\n\n## Relações Entre Banco de Dados Diferentes <span id=\"cross-database-relations\"></span> \n\nO Active Record lhe permite declarar relações entre classes Active Record que são alimentados por diferentes bancos de dados. As bases de dados podem ser de diferentes tipos (ex. MySQL e PostgreSQL, ou MS SQL e MongoDB) e podem rodar em diferentes servidores. Você pode usar a mesma sintaxe para executar consultas relacionais. Por exemplo,\n\n```php\n// Customer está associado a tabela \"customer\" em um banco de dados relacional (ex. MySQL)\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n   public static function tableName()\n   {\n       return 'customer';\n   }\n\n   public function getComments()\n   {\n       // a customer tem muitos comments\n       return $this->hasMany(Comment::class, ['customer_id' => 'id']);\n   }\n}\n\n// Comment está associado com a coleção \"comment\" em um banco de dados MongoDB\nclass Comment extends \\yii\\mongodb\\ActiveRecord\n{\n   public static function collectionName()\n   {\n       return 'comment';\n   }\n\n   public function getCustomer()\n   {\n       // um comment tem um customer\n       return $this->hasOne(Customer::class, ['id' => 'customer_id']);\n   }\n}\n\n$customers = Customer::find()->with('comments')->all();\n```\n\nVocê pode usar a maioria dos recursos de consulta relacional que foram descritos nesta seção. \n\n> Observação: O uso de [[yii\\db\\ActiveQuery::joinWith()|joinWith()]] está limitado às bases de dados que permitem JOIN entre diferentes bancos de dados (cross-database). Por esta razão, você não pode usar este método no exemplo acima porque MongoDB não suporta JOIN.\n\n\n## Personalizando Classes de Consulta <span id=\"customizing-query-classes\"></span>\n\nPor padrão, todas as consultas do Active Record são suportadas por [[yii\\db\\ActiveQuery]]. Para usar uma classe de consulta customizada em uma classe Active Record, você deve sobrescrever o método [[yii\\db\\ActiveRecord::find()]] e retornar uma instância da sua classe de consulta customizada. Por exemplo,\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\db\\ActiveQuery;\n\nclass Comment extends ActiveRecord\n{\n   public static function find()\n   {\n       return new CommentQuery(get_called_class());\n   }\n}\n\nclass CommentQuery extends ActiveQuery\n{\n   // ...\n}\n```\n\nAgora sempre que você realizar uma consulta (ex. `find()`, `findOne()`) ou definir uma relação (ex. `hasOne()`) com `Comment`, você estará trabalhando com a instância de `CommentQuery` em vez de `ActiveQuery`.\n\n> Dica: Em grandes projetos, é recomendável que você use classes de consulta personalizadas para manter a maioria dos códigos de consultas relacionadas de modo que a classe Active Record possa se manter limpa.\n\nVocê pode personalizar uma classe de consulta  de várias formas criativas afim de melhorar a sua experiência na construção de consultas. Por exemplo, você pode definir um novo método query building em uma classe de consulta customizada: \n\n```php\nclass CommentQuery extends ActiveQuery\n{\n   public function active($state = true)\n   {\n       return $this->andWhere(['active' => $state]);\n   }\n}\n```\n\n> Observação: Em vez de chamar [[yii\\db\\ActiveQuery::where()|where()]], você geralmente deve chamar [[yii\\db\\ActiveQuery::andWhere()|andWhere()]] ou [[yii\\db\\ActiveQuery::orWhere()|orWhere()]] para anexar condições adicionais ao definir um novo método de query building de modo que as condições existentes não serão sobrescritas.\n\nIsto permite que você escreva códigos de query building conforme o exemplo abaixo:\n\n```php\n$comments = Comment::find()->active()->all();\n$inactiveComments = Comment::find()->active(false)->all();\n```\n\nVocê também pode usar um novo método de query building  ao definir relações sobre `Comment` ou executar uma consulta relacional:\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n   public function getActiveComments()\n   {\n       return $this->hasMany(Comment::class, ['customer_id' => 'id'])->active();\n   }\n}\n\n$customers = Customer::find()->with('activeComments')->all();\n\n// ou alternativamente\n\n$customers = Customer::find()->with([\n   'comments' => function($q) {\n       $q->active();\n   }\n])->all();\n```\n\n> Observação: No Yii 1.1, existe um conceito chamado *scope*. Scope já não é suportado no Yii 2.0, e você deve usar classes de consulta personalizadas e métodos de consulta para atingir o mesmo objetivo.\n\n\n## Selecionando Campos Extras\n\nQuando uma instância de Active Record é populada através do resultado de uma consulta, seus atributos são preenchidos pelos valores correspondentes das colunas do conjunto de dados recebidos.\n\nVocê é capaz de buscar colunas ou valores adicionais da consulta e armazená-lo dentro do Active Record. Por exemplo, suponha que tenhamos uma tabela chamada 'room', que contém informações sobre quartos disponíveis em um hotel. Cada 'room' grava informações sobre seu tamanho geométrico usando os campos 'length', 'width', 'height'. Imagine que precisemos de uma lista de todos os quartos disponíveis com o seu volume em ordem decrescente. Então você não pode calcular o volume usando PHP, porque precisamos ordenar os registros pelo seu valor, mas você também quer que o 'volume' seja mostrado na lista. Para alcançar este objetivo, você precisa declarar um campo extra na sua classe Active Record 'Room', que vai armazenar o valor do campo  'volume':\n\n```php\nclass Room extends \\yii\\db\\ActiveRecord\n{\n   public $volume;\n\n   // ...\n}\n```\n\nEntão você precisa criar uma consulta, que calcule o volume da sala e realize a ordenação:\n\n```php\n$rooms = Room::find()\n   ->select([\n       '{{room}}.*', // select all columns\n       '([[length]] * [[width]].* [[height]]) AS volume', // calculate a volume\n   ])\n   ->orderBy('volume DESC') // apply sort\n   ->all();\n\nforeach ($rooms as $room) {\n   echo $room->volume; // contains value calculated by SQL\n}\n```\n\nCapacidade de selecionar campos extras pode ser extremamente útil para consultas de agregação. Suponha que você precise exibir uma lista de clientes com a contagem de seus pedidos. Em primeiro lugar, você precisa declarar uma classe `Customer` com uma relação com 'orders' e um campo extra para armazenamento da contagem:\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n   public $ordersCount;\n\n   // ...\n\n   public function getOrders()\n   {\n       return $this->hasMany(Order::class, ['customer_id' => 'id']);\n   }\n}\n```\n\nEntão você pode criar uma consulta que faça um JOIN com 'orders' e calcule a quantidade:\n\n```php\n$customers = Customer::find()\n   ->select([\n       '{{customer}}.*', // select all customer fields\n       'COUNT({{order}}.id) AS ordersCount' // calculate orders count\n   ])\n   ->joinWith('orders') // ensure table junction\n   ->groupBy('{{customer}}.id') // group the result to ensure aggregation function works\n   ->all();\n```\n"
  },
  {
    "path": "docs/guide-pt-BR/db-migrations.md",
    "content": "Migrações de Dados (Migrations)\n==================\n\nDurante o curso de desenvolvimento e manutenção de uma aplicação orientada a banco de dados, a estrutura de banco sendo usada evolui ao mesmo tempo em que o código. Por exemplo, durante o desenvolvimento de uma aplicação, a criação de uma nova tabela pode ser necessária; após ser feito o deploy da aplicação em produção, pode ser descoberto que um índice deveria ser criado para melhorar a performance de alguma query; entre outros. Como a mudança de uma estrutura de banco de dados normalmente necessita de alguma mudança no código, o Yii suporta a então chamada funcionalidade de *migração de dados* que permite que você mantenha um registro das mudanças feitas no banco de dados em termos de *migrações de dados* que são versionadas em conjunto com o código fonte da aplicação.\n\nOs seguintes passos mostram como uma migração de dados pode ser usada pela equipe durante o desenvolvimento:\n\n1. João cria uma nova migração (ex. cria uma nova tabela, muda a definição de uma coluna, etc.).\n2. João comita a nova migração no sistema de controle de versão (ex. Git, Mercurial).\n3. Pedro atualiza seu repositório a partir do sistema de controle de versão e recebe a nova migração.\n4. Pedro aplica a nova migração ao seu banco de dados local em seu ambiente de desenvolvimento, e assim, sincronizando seu banco de dados para refletir as mudanças que João fez.\n\nE os seguintes passos mostram como fazer o deploy para produção de uma nova versão:\n\n1. Luiz cria uma nova tag para o repositório do projeto que contem algumas novas migrações de dados.\n2. Luiz atualiza o código fonte no servidor em produção para a tag criada.\n3. Luiz aplica todas as migrações de dados acumuladas para o banco de dados em produção. \n\nO Yii oferece um conjunto de ferramentas de linha de comando que permitem que você:\n\n* crie novas migrações;\n* aplique migrações;\n* reverta migrações;\n* reaplique migrações;\n* exiba um histórico das migrações;\n\nTodas estas ferramentas são acessíveis através do comando `yii migrate`. Nesta seção nós iremos descrever em detalhes como realizar várias tarefas usando estas ferramentas. Você também pode descobrir como usar cada ferramenta através do comando de ajuda `yii help migrate`.\n\n> Observação: os migrations (migrações) podem afetar não só o esquema do banco de dados, mas também ajustar os dados existentes para se conformar ao novo esquema, como criar novas hierarquias de RBAC ou limpar dados de cache.\n   \n\n## Criando Migrações <span id=\"creating-migrations\"></span>\n\nPara criar uma nova migração, execute o seguinte comando:\n\n```\nyii migrate/create <nome>\n```\n\nO argumento obrigatório `nome` serve como uma breve descrição sobre a nova migração. Por exemplo, se\na migração é sobre a criação de uma nova tabela chamada *noticias*, você pode usar o nome `criar_tabela_noticias`\ne executar o seguinte comando:\n\n```\nyii migrate/create criar_tabela_noticias\n```\n\n> Observação: Como o argumento `nome` será usado como parte do nome da classe de migração gerada, este deve conter apenas letras, dígitos, e/ou underline. \n\nO comando acima criará um novo arquivo contendo uma classe PHP chamada `m150101_185401_criar_tabela_noticias.php`\nna pasta `@app/migrations`. O arquivo contém o seguinte código que declara a classe de migração\n`m150101_185401_criar_tabela_noticias` com o código esqueleto:\n\n```php\n<?php\n\nuse yii\\db\\Schema;\nuse yii\\db\\Migration;\n\nclass m150101_185401_criar_tabela_noticias extends Migration\n{\n    public function up()\n    {\n    }\n\n    public function down()\n    {\n        echo \"m101129_185401_criar_tabela_noticias cannot be reverted.\\n\";\n        return false;\n    }\n}\n```\n\nCada migração de dados é definida como uma classe PHP estendida de [[yii\\db\\Migration]]. O nome da classe de migração é automaticamente gerado no formato `m<YYMMDD_HHMMSS>_<Nome>`, onde \n\n* `<YYMMDD_HHMMSS>` refere-se a data UTC em que o comando de criação da migração foi executado.\n* `<Nome>` é igual ao valor do argumento `nome` que você passou no comando.\n\nNa classe de migração, é esperado que você escreva no método `up()` as mudanças a serem feitas na estrutura do banco de dados.\nVocê também pode escrever códigos no método `down()` para reverter as mudanças feitas por `up()`. O método `up()` é invocado quando você atualiza o seu banco de dados com esta migração, enquanto o método `down()` é invocado quando você reverte as mudanças no banco. O seguinte código mostra como você pode implementar a classe de migração para criar a tabela `noticias`: \n\n```php\n\nuse yii\\db\\Schema;\nuse yii\\db\\Migration;\n\nclass m150101_185401_criar_tabela_noticias extends \\yii\\db\\Migration\n{\n    public function up()\n    {\n        $this->createTable('noticias', [\n            'id' => Schema::TYPE_PK,\n            'titulo' => Schema::TYPE_STRING . ' NOT NULL',\n            'conteudo' => Schema::TYPE_TEXT,\n        ]);\n    }\n\n    public function down()\n    {\n        $this->dropTable('noticias');\n    }\n\n}\n```\n\n> Observação: Nem todas as migrações são reversíveis. Por exemplo, se o método `up()` deleta um registro de uma tabela,\n  você possivelmente não será capaz de recuperar este registro com o método `down()`. Em alguns casos, você pode ter \n  tido muita preguiça e não ter implementado o método `down()`, porque não é muito comum reverter migrações de dados.\n  Neste caso, você deve retornar `false` no método `down()` para indicar que a migração não é reversível.\n\nA classe base [[yii\\db\\Migration]] expõe a conexão ao banco através da propriedade [[yii\\db\\Migration::db|db]].\nVocê pode usá-la para manipular o esquema do banco de dados usando os métodos como descritos em [Trabalhando com um Esquema de Banco de Dados](db-dao.md#database-schema).\n\nAo invés de usar tipos físicos, ao criar uma tabela ou coluna, você deve usar *tipos abstratos* para que\nsuas migrações sejam independentes do SGBD. A classe [[yii\\db\\Schema]] define uma gama de constantes para\nrepresentar os tipos abstratos suportados. Estas constantes são nomeadas no formato `TYPE_<NOME>`. Por exemplo,\n`TYPE_PK` refere-se ao tipo chave primária auto incrementável; `TYPE_STRING` refere-se ao típo string. \nQuando a migração for aplicada a um banco de dados em particular, os tipos abstratos serão traduzidos nos\nrespectivos tipos físicos. No caso do MySQL, `TYPE_PK` será traduzida para \n`int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY`, enquanto `TYPE_STRING` será `varchar(255)`. \n\nVocê pode adicionar algumas constraints ao usar tipos abstratos. No exemplo acima, ` NOT NULL` é adicionado\na `Schema::TYPE_STRING` para especificar que a coluna não pode ser nula.\n\n> Observação: O mapeamento entre tipos abstratos e tipos físicos é especificado pela propriedade [[yii\\db\\QueryBuilder::$typeMap|$typeMap]] em cada classe `QueryBuilder`.\n\n\n### Migrações Transacionais <span id=\"transactional-migrations\"></span>\n\nAo realizar migrações de dados complexas, é importante assegurar que cada migração terá sucesso ou falhará \npor completo para que o banco não perca sua integridade e consistência. Para atingir este objetivo, recomenda-se que\nvocê encapsule suas operações de banco de dados de cada migração em uma [transação](db-dao.md#performing-transactions-).\n\nUm jeito mais fácil de implementar uma migração transacional é colocar o seu código de migração nos métodos `safeUp()` e `safeDown()`. Estes métodos diferem de `up()` e `down()` porque eles estão implicitamente encapsulados em uma transação. Como resultado, se qualquer operação nestes métodos falhar, todas as operações anteriores sofrerão roll back\nautomaticamente.\n\nNo exemplo a seguir, além de criar a tabela `noticias` nós também inserimos um registro inicial a esta tabela.\n\n```php\n\nuse yii\\db\\Schema;\nuse yii\\db\\Migration;\n\nclass m150101_185401_criar_tabela_noticias extends Migration\n{\n    public function safeUp()\n    {\n        $this->createTable('noticias', [\n            'id' => 'pk',\n            'titulo' => Schema::TYPE_STRING . ' NOT NULL',\n            'conteudo' => Schema::TYPE_TEXT,\n        ]);\n        \n        $this->insert('noticias', [\n            'titulo' => 'título 1',\n            'conteudo' => 'conteúdo 1',\n        ]);\n    }\n\n    public function safeDown()\n    {\n        $this->delete('noticias', ['id' => 1]);\n        $this->dropTable('noticias');\n    }\n}\n```\n\nObserve que normalmente quando você realiza múltiplas operações em `safeUp()`, você deverá reverter a ordem de execução\nem `safeDown()`. No exemplo acima nós primeiramente criamos a tabela e depois inserimos uma túpla em `safeUp()`; enquanto\nem `safeDown()` nós primeiramente apagamos o registro e depois eliminamos a tabela.\n\n> Observação: Nem todos os SGBDs suportam transações. E algumas requisições de banco não podem ser encapsuladas em uma transação. Para alguns exemplos, referir a [commit implícito](https://dev.mysql.com/doc/refman/5.1/en/implicit-commit.html). Se este for o caso, implemente os métodos `up()` e `down()`.\n\n\n### Métodos de Acesso ao Banco de Dados <span id=\"db-accessing-methods\"></span>\n\nA classe base [[yii\\db\\Migration]] entrega vários métodos que facilitam o acesso e a manipulação de \nbancos de dados. Você deve achar que estes métodos são nomeados similarmente a [métodos DAO](db-dao.md) encontrados \nna classe [[yii\\db\\Command]]. Por exemplo, o método [[yii\\db\\Migration::createTable()]] permite que você crie uma \nnova tabela assim como [[yii\\db\\Command::createTable()]] o faz.\n\nO benefício ao usar os métodos encontrados em [[yii\\db\\Migration]] é que você não precisa criar explícitamente\ninstancias de [[yii\\db\\Command]] e a execução de cada método automaticamente exibirá mensagens úteis que dirão\na você quais operações estão sendo feitas e quanto tempo elas estão durando.\n\nAbaixo está uma lista de todos estes métodos de acesso ao banco de dados:\n\n* [[yii\\db\\Migration::execute()|execute()]]: executando um SQL\n* [[yii\\db\\Migration::insert()|insert()]]: inserindo um novo registro\n* [[yii\\db\\Migration::batchInsert()|batchInsert()]]: inserindo vários registros\n* [[yii\\db\\Migration::update()|update()]]: atualizando registros\n* [[yii\\db\\Migration::delete()|delete()]]: apagando registros\n* [[yii\\db\\Migration::createTable()|createTable()]]: criando uma tabela\n* [[yii\\db\\Migration::renameTable()|renameTable()]]: renomeando uma tabela\n* [[yii\\db\\Migration::dropTable()|dropTable()]]: removendo uma tabela\n* [[yii\\db\\Migration::truncateTable()|truncateTable()]]: removendo todos os registros em uma tabela\n* [[yii\\db\\Migration::addColumn()|addColumn()]]: adicionando uma coluna\n* [[yii\\db\\Migration::renameColumn()|renameColumn()]]: renomeando uma coluna\n* [[yii\\db\\Migration::dropColumn()|dropColumn()]]: removendo uma coluna\n* [[yii\\db\\Migration::alterColumn()|alterColumn()]]: alterando uma coluna\n* [[yii\\db\\Migration::addPrimaryKey()|addPrimaryKey()]]: adicionando uma chave primária\n* [[yii\\db\\Migration::dropPrimaryKey()|dropPrimaryKey()]]: removendo uma chave primária\n* [[yii\\db\\Migration::addForeignKey()|addForeignKey()]]: adicionando uma chave estrangeira\n* [[yii\\db\\Migration::dropForeignKey()|dropForeignKey()]]: removendo uma chave estrangeira\n* [[yii\\db\\Migration::createIndex()|createIndex()]]: criando um índice\n* [[yii\\db\\Migration::dropIndex()|dropIndex()]]: removendo um índice\n\n> Observação: [[yii\\db\\Migration]] não possui um método de consulta ao banco de dados. Isto porque você normalmente não precisará exibir informações extras ao recuperar informações de um banco de dados. E além disso você pode usar o poderoso [Query Builder](db-query-builder.md) para construir e executar consultas complexas.\n\n\n## Aplicando Migrações <span id=\"applying-migrations\"></span>\n\nPara atualizar um banco de dados para a sua estrutura mais atual, você deve aplicar todas as migrações disponíveis usando o seguinte comando:\n\n```\nyii migrate\n```\n\nEste comando listará todas as migrações que não foram alicadas até agora. Se você confirmar que deseja aplicar\nestas migrações, cada nova classe de migração executará os métodos `up()` ou `safeUp()` um após o outro, na \nordem relacionada à data marcada em seus nomes. Se qualquer uma das migrações falhar, o comando terminará sem aplicar\no resto das migrações. \n\nPara cada migração aplicada com sucesso, o comando inserirá um registro numa tabela no banco de dados chamada\n`migration` para registrar uma aplicação de migração. Isto permitirá que a ferramenta de migração identifique\nquais migrações foram aplicadas e quais não foram.\n\n> Observação: Esta ferramenta de migração automaticamente criará a tabela `migration` no banco de dados especificado pela opção do comando [[yii\\console\\controllers\\MigrateController::db|db]]. Por padrão, o banco de dados é especificado por `db` em [Componentes de Aplicação](structure-application-components.md).\n\nEventualmente, você desejará aplicar apenas uma ou algumas migrações, em vez de todas as disponíveis.\nVocê pode fazê-lo especificando o número de migrações que deseja aplicar ao executar o comando.\nPor exemplo, o comando a seguir tentará aplicar as próximas 3 migrações disponíveis:\n  \n```\nyii migrate 3\n```\nVocê também pode especificar para qual migração em particular o banco de dados deve ser migrado\nusando o comando `migrate/to` em um dos formatos seguintes:\n\n```\nyii migrate/to 150101_185401                        # usando a marcação de data para especificar a migração\nyii migrate/to \"2015-01-01 18:54:01\"                # usando uma string que pode ser analisada por strtotime()\nyii migrate/to m150101_185401_criar_tabela_noticias # usando o nome completo\nyii migrate/to 1392853618                           # usando uma marcação de data no estilo UNIX\n```\nSe existirem migrações mais recentes do que a especificada, elas serão todas aplicadas antes da migração definida.\n\nSe a migração especificada já tiver sido aplicada, qualquer migração posterior já aplicada será revertida.\n\n\n## Revertendo Migrações <span id=\"reverting-migrations\"></span>\n\nPara reverter uma ou múltiplas migrações que tenham sido aplicadas antes, você pode executar o seguinte comando:\n\n```\nyii migrate/down     # reverter a última migração aplicada\nyii migrate/down 3   # reverter as 3 últimas migrações aplicadas\n```\n\n> Observação: Nem todas as migrações são reversíveis. Tentar reverter tais migrações causará um erro que cancelará todo o processo de reversão.\n\n\n## Refazendo Migrações <span id=\"redoing-migrations\"></span>\n\nRefazer as migrações significa primeiramente reverter migrações especificadas e depois aplicá-las novamente.\nIsto pode ser feito da seguinte maneira:\n\n```\nyii migrate/redo        # refazer a última migração aplicada \nyii migrate/redo 3      # refazer as 3 últimas migrações aplicadas\n```\n\n> Observação: Se a migração não for reversível, você não poderá refazê-la.\n\n\n## Listando Migrações <span id=\"listing-migrations\"></span>\n\nPara listar quais migrações foram aplicadas e quais não foram, você deve usar os seguintes comandos:\n\n```\nyii migrate/history     # exibir as 10 últimas migrações aplicadas\nyii migrate/history 5   # exibir as 5 últimas migrações aplicadas\nyii migrate/history all # exibir todas as migrações aplicadas\n\nyii migrate/new         # exibir as 10 primeiras novas migrações\nyii migrate/new 5       # exibir as 5 primeiras novas migrações\nyii migrate/new all     # exibir todas as novas migrações\n```\n\n\n## Modificando o Histórico das Migrações <span id=\"modifying-migration-history\"></span>\n\nAo invés de aplicar ou reverter migrações, pode ser que você queira apenas definir que o seu banco de dados\nfoi atualizado para uma migração em particular. Isto normalmente acontece quando você muda manualmente o banco\nde dados para um estado em particular, e não deseja que as mudanças para aquela migração sejam reaplicadas\nposteriormente. Você pode alcançar este objetivo com o seguinte comando:\n\n```\nyii migrate/mark 150101_185401                        # usando a marcação de data para especificar a migração\nyii migrate/mark \"2015-01-01 18:54:01\"                # usando uma string que pode ser analisada por strtotime()\nyii migrate/mark m150101_185401_criar_tabela_noticias # usando o nome completo\nyii migrate/mark 1392853618                           # usando uma marcação de data no estilo UNIX\n```\nO comando modificará a tabela `migration` adicionando ou deletando certos registros para indicar que o banco\nde dados sofreu as migrações especificadas. Nenhuma migração será aplicada ou revertida por este comando.\n\n\n## Customizando Migrações <span id=\"customizing-migrations\"></span>\n\nExistem várias maneiras de customizar o comando de migração.\n\n\n### Usando Opções na Linha de Comando <span id=\"using-command-line-options\"></span>\n\nO comando de migração vem com algumas opções de linha de comando que podem ser usadas para customizar o seu comportamento:\n\n* `interactive`: boolean (o padrão é `true`), especifica se as migrações serão executadas em modo interativo.\n  Quando for `true`, ao usuário será perguntado se a execução deve continuar antes de o comando executar certas ações. \n  Você provavelmente marcará isto para falso se o comando estiver sendo feito em algum processo em segundo plano.\n\n* `migrationPath`: string (o padrão é `@app/migrations`), especifica o diretório em que os arquivos das classes de migração estão. Isto pode ser especificado ou como um diretório ou como um [alias](concept-aliases.md).\n  Observe que o diretório deve existir, ou o comando disparará um erro. \n\n* `migrationTable`: string (o padrão é `migration`), especifica o nome da tabela no banco de dados para armazenar o histórico das migrações. A tabela será automaticamente criada pelo comando caso não exista.\n  Você também pode criá-la manualmente usando a estrutura `version varchar(255) primary key, apply_time integer`.\n\n* `db`: string (o padrão é `db`), especifica o banco de dados do [componente de aplicação](structure-application-components.md).\n  Representa qual banco sofrerá as migrações usando este comando.\n\n* `templateFile`: string (o padrão é `@yii/views/migration.php`), especifica o caminho do arquivo de modelo que é usado para gerar um esqueleto para os arquivos das classes de migração. Isto pode ser especificado por um caminho de arquivo ou por um [alias](concept-aliases.md). O arquivo modelo é um script PHP em que você pode usar uma variával pré-definida `$className` para obter o nome da classe de migração.\n\nO seguinte exemplo exibe como você pode usar estas opções.\n\nPor exemplo, se nós quisermos migrar um módulo `forum` cujo os arquivos de migração estão localizados dentro da pasta `migrations` do módulo, nós podemos usar o seguinte comando:\n\n```\n# migrate the migrations in a forum module non-interactively\nyii migrate --migrationPath=@app/modules/forum/migrations --interactive=0\n```\n\n\n### Configurando o Comando Globalmente <span id=\"configuring-command-globally\"></span>\n\nAo invés de fornecer opções todas as vezes que você executar o comando de migração,\nvocê pode configurá-lo de uma vez por todas na configuração da aplicação como exibido a seguir:\n\n```php\nreturn [\n    'controllerMap' => [\n        'migrate' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationTable' => 'backend_migration',\n        ],\n    ],\n];\n```\n\nCom a configuração acima, toda a vez que você executar o comando de migração, a tabela `backend_migration`\nserá usada para gravar o histórico de migração. Você não precisará mais fornecê-la através da opção `migrationTable`.\n\n\n## Migrando Múltiplos Bancos De Dados <span id=\"migrating-multiple-databases\"></span>\n\nPor padrão, as migrações são aplicadas no mesmo banco de dados especificado por `db` do [componente de aplicação](structure-application-components.md).\nSe você quiser que elas sejam aplicadas em um banco de dados diferente, você deve especificar na opção `db` como exibido a seguir:\n\n```\nyii migrate --db=db2\n```\n\nO comando acima aplicará as migrações para o banco de dados `db2`.\n\nAlgumas vezes pode ocorrer que você queira aplicar *algumas* das migrações para um banco de dados, e outras para\noutro banco de dados. Para atingir este objetivo, ao implementar uma classe de migração você deve especificar a\nID do componente DB que a migração usará, como o seguinte:\n\n```php\nuse yii\\db\\Schema;\nuse yii\\db\\Migration;\n\nclass m150101_185401_criar_tabela_noticias extends Migration\n{\n    public function init()\n    {\n        $this->db = 'db2';\n        parent::init();\n    }\n}\n```\n\nA migração acima será aplicada a `db2`, mesmo que você especifique um banco de dados diferente através da opção `db`. Observe que o histórico da migração continuará sendo registrado no banco especificado pela opção `db`.\nSe você tiver múltiplas migrações que usam o mesmo banco de dados, é recomenda-se criar uma classe de migração\nbase com o código acima em `init()`. Então cada classe de migração poderá ser estendida desta classe base. \n\n> Dica: Apesar de definir a propriedade [[yii\\db\\Migration::db|db]], você também pode operar em diferentes bancos\n  de dados ao criar novas conexões de banco para eles em sua classe de migração. Você então usará os [métodos DAO](db-dao.md)\n  com estas conexões para manipular diferentes bancos de dados.\n\nOutra estratégia que você pode seguir para migrar múltiplos bancos de dados é manter as migrações para diferentes bancos\nde dados em diferentes pastas de migrações. Então você poderá migrar estes bancos de dados em comandos separados como os seguintes:\n\n```\nyii migrate --migrationPath=@app/migrations/db1 --db=db1\nyii migrate --migrationPath=@app/migrations/db2 --db=db2\n...\n```\n\nO primeiro comando aplicará as migrações em `@app/migrations/db1` para o banco de dados `db1`, e o segundo comando\naplicará as migrações em `@app/migrations/db2` para `db2`, e assim sucessivamente. \n"
  },
  {
    "path": "docs/guide-pt-BR/db-query-builder.md",
    "content": "Query Builder (Construtor de Consulta)\n=============\n\nDesenvolvido à partir do [Database Access Objects](db-dao.md), o query builder permite que você construa uma instrução SQL em um programático e independente de banco de dados. Comparado a escrever instruções SQL à mão, usar query builder lhe ajudará a escrever um código SQL relacional mais legível e gerar declarações  SQL mais seguras.  \n\nUsar query builder geralmente envolve dois passos:\n\n1. Criar um objeto  [[yii\\db\\Query]] para representar diferentes partes de uma instrução SQL (ex. `SELECT`, `FROM`).\n2. Executar um método (ex. `all()`) do objeto [[yii\\db\\Query]] para recuperar dados do banco de dados.\n\nO código a seguir mostra uma forma habitual de utilizar query builder:\n\n```php\n$rows = (new \\yii\\db\\Query())\n   ->select(['id', 'email'])\n   ->from('user')\n   ->where(['last_name' => 'Smith'])\n   ->limit(10)\n   ->all();\n```\n\nO código acima gera e executa a seguinte instrução SQL, onde o parâmetro `:last_name` está ligado a string `'Smith'`.\n\n```sql\nSELECT `id`, `email` \nFROM `user`\nWHERE `last_name` = :last_name\nLIMIT 10\n```\n\n> Observação: Geralmente, você trabalhará mais com o [[yii\\db\\Query]] do que com o [[yii\\db\\QueryBuilder]]. Este último é chamado pelo primeiro implicitamente quando você chama um dos métodos da query. O [[yii\\db\\QueryBuilder]] é a classe responsável por gerar instruções SGDBs dependentes (ex. colocar aspas em nomes de tabela/coluna) a partir de objetos de query independentemente do SGDB.\n\n\n## Construindo Queries <span id=\"building-queries\"></span>\n\nPara construir um objeto [[yii\\db\\Query]], você pode chamar diferentes métodos de construção de query para especificar diferentes partes de uma instrução SQL. Os nomes destes métodos assemelha-se as palavras-chave SQL utilizados nas partes correspondentes da instrução SQL. Por exemplo, para especificar a parte da instrução SQL `FROM`, você deve chamar o método `from()`. Todos os métodos de construção de query retornam o próprio objeto query, que permite você encadear várias chamadas em conjunto. A seguir, descreveremos o uso de cada método de construção de query.\n\n\n### [[yii\\db\\Query::select()|select()]] <span id=\"select\"></span>\n\nO método [[yii\\db\\Query::select()|select()]] especifica o fragmento de uma instrução SQL `SELECT`. Você pode especificar colunas para ser selecionado em um array ou uma string, como mostrado abaixo. Os nomes das colunas que estão sendo selecionadas serão automaticamente envolvidas entre aspas quando a instrução SQL está sendo gerada a partir do objeto query.\n\n```php\n$query->select(['id', 'email']);\n\n// equivalente a:\n\n$query->select('id, email');\n```\n\nOs nomes das colunas que estão sendo selecionadas podem incluir prefixos de tabela e/ou aliases de colunas, como você faz ao escrever instruções SQL manualmente. \nPor exemplo:\n\n```php\n$query->select(['user.id AS user_id', 'email']);\n\n// é equivalente a:\n\n$query->select('user.id AS user_id, email');\n```\n\nSe você estiver usando um array para especificar as colunas, você também pode usar as chaves do array para especificar os aliases das colunas. Por exemplo, o código acima pode ser reescrito da seguinte forma,\n\n```php\n$query->select(['user_id' => 'user.id', 'email']);\n```\n\nSe você não chamar o método [[yii\\db\\Query::select()|select()]] na criação da query, o `*` será selecionado, o que significa selecionar *todas* as colunas.\n\nAlém dos nomes de colunas, você também pode selecionar expressões DB. Você deve usar o formato array quando utilizar uma expressão DB que contenha vírgula para evitar que sejam gerados nomes de colunas de forma equivocada. Por exemplo:\n\n```php\n$query->select([\"CONCAT(first_name, ' ', last_name) AS full_name\", 'email']); \n```\n\nA partir da versão 2.0.1, você também pode selecionar sub-queries. Você deve especificar cada sub-query na forma de um objeto [[yii\\db\\Query]]. Por exemplo:\n\n```php\n$subQuery = (new Query())->select('COUNT(*)')->from('user');\n\n// SELECT `id`, (SELECT COUNT(*) FROM `user`) AS `count` FROM `post`\n$query = (new Query())->select(['id', 'count' => $subQuery])->from('post');\n```\n\nPara utilizar a cláusula `distinct`, você pode chamar [[yii\\db\\Query::distinct()|distinct()]], como a seguir:\n\n```php\n// SELECT DISTINCT `user_id` ...\n$query->select('user_id')->distinct();\n```\n\nVocê pode chamar [[yii\\db\\Query::addSelect()|addSelect()]] para selecionar colunas adicionais. Por exemplo:\n\n```php\n$query->select(['id', 'username'])\n   ->addSelect(['email']);\n```\n\n\n### [[yii\\db\\Query::from()|from()]] <span id=\"from\"></span>\n\nO método [[yii\\db\\Query::from()|from()]] especifica o fragmento de uma instrução SQL `FROM`. Por exemplo:\n\n```php\n// SELECT * FROM `user`\n$query->from('user');\n```\n\nVocê pode especificar todas tabelas a serem selecionadas a partir de uma string ou um array. O nome da tabela pode conter prefixos de esquema e/ou aliases de tabela, da mesma forma quando você escreve instruções SQL manualmente. Por exemplo:\n\n```php\n$query->from(['public.user u', 'public.post p']);\n\n// é equivalente a:\n\n$query->from('public.user u, public.post p');\n```\n\nSe você estiver usando o formato array, você também pode usar as chaves do array para especificar os aliases de tabelas, como mostrado a seguir:\n\n```php\n$query->from(['u' => 'public.user', 'p' => 'public.post']);\n```\n\nAlém de nome de tabelas, você também pode selecionar a partir de sub-queries especificando-o um objeto [[yii\\db\\Query]].\nPor exemplo:\n\n```php\n$subQuery = (new Query())->select('id')->from('user')->where('status=1');\n\n// SELECT * FROM (SELECT `id` FROM `user` WHERE status=1) u \n$query->from(['u' => $subQuery]);\n```\n\n\n### [[yii\\db\\Query::where()|where()]] <span id=\"where\"></span>\n\nO método [[yii\\db\\Query::where()|where()]] especifica o fragmento de uma instrução SQL `WHERE`. Você pode usar um dos três formatos para especificar uma condição `WHERE`:\n\n- formato string, ex., `'status=1'`\n- formato hash, ex. `['status' => 1, 'type' => 2]`\n- formato operador, ex. `['like', 'name', 'test']`\n\n\n#### Formato String <span id=\"string-format\"></span>\n\nFormato de string é mais usado para especificar condições `WHERE` muito simples. Esta forma é muito semelhante a condições `WHERE` escritas manualmente. Por exemplo:\n\n```php\n$query->where('status=1');\n\n// ou usar parâmetro para vincular os valores dinamicamente \n$query->where('status=:status', [':status' => $status]);\n```\n\nNÃO incorporar variáveis diretamente na condição como exemplificado abaixo, especialmente se os valores das variáveis vêm de entradas de dados dos usuários finais, porque isso vai fazer a sua aplicação ficar sujeita a ataques de injeção de SQL.\n\n```php\n// Perigoso! NÃO faça isto a menos que você esteja muito certo que o $status deve ser um número inteiro.\n$query->where(\"status=$status\");\n```\n\nAo usar parâmetro, você pode chamar [[yii\\db\\Query::params()|params()]] ou [[yii\\db\\Query::addParams()|addParams()]] para especificar os parâmetros separadamente.\n\n```php\n$query->where('status=:status')\n   ->addParams([':status' => $status]);\n```\n\n\n#### Formato Hash <span id=\"hash-format\"></span>\n\nFormato HASH é mais usado para especificar múltiplos  `AND` - sub-condições concatenadas, sendo cada uma afirmação simples de igualdade.\nÉ escrito como um array cujas chaves são nomes de coluna e os valores correspondem ao conteúdo destas colunas. Por exemplo:\n\n```php\n// ...WHERE (`status` = 10) AND (`type` IS NULL) AND (`id` IN (4, 8, 15))\n$query->where([\n   'status' => 10,\n   'type' => null,\n   'id' => [4, 8, 15],\n]);\n```\n\nComo você pode ver, o query builder é inteligente o suficiente para lidar corretamente com valores que são nulos ou arrays. Você também pode usar sub-queries com o formato hash conforme mostrado abaixo:\n\n```php\n$userQuery = (new Query())->select('id')->from('user');\n\n// ...WHERE `id` IN (SELECT `id` FROM `user`)\n$query->where(['id' => $userQuery]);\n```\n\n\n#### Formato Operador <span id=\"operator-format\"></span>\n\nFormato operador lhe permite especificar arbitrariamente condições de uma forma programática. Ele tem o seguinte formato:\n\n```php\n[operator, operand1, operand2, ...]\n```\n\nonde cada um dos operandos pode ser especificado no formato string, formato hash ou formato operador recursivamente, enquanto o operador pode ser um dos seguintes procedimentos:\n\n- `and`: os operandos devem ser concatenados juntos usando `AND`. Por exemplo,\n `['and', 'id=1', 'id=2']` irá gerar `id=1 AND id=2`. Se um operando for um array,\n ele será convertido para string usando as regras descritas aqui. Por exemplo,\n `['and', 'type=1', ['or', 'id=1', 'id=2']]` irá gerar `type=1 AND (id=1 OR id=2)`.\n O método NÃO vai fazer qualquer tratamento de escapar caracteres ou colocar aspas.\n\n- `or`: similar ao operador `and` exceto pelo fato de que os operandos são concatenados usando `OR`.\n\n- `between`: o operando 1 deve ser um nome de coluna, e os operandos 2 e 3 devem ser os valores de início e fim. Por exemplo, `['between', 'id', 1, 10]` irá gerar `id BETWEEN 1 AND 10`.\n\n- `not between`: similar ao `between` exceto pelo fato de que `BETWEEN` é substituído por `NOT BETWEEN` na geração da condição.\n\n- `in`: o operando 1 deve ser um nome de coluna ou uma expressão DB. O operando 2 pode ser tanto um array ou um objeto `Query`. Será gerado uma condição `IN`. Se o operando 2 for um array, representará o intervalo dos valores que a coluna ou expressão DB devem ser; se o operando 2 for um objeto `Query`, uma sub-query será gerada e usada como intervalo da coluna ou expressão DB. Por exemplo, `['in', 'id', [1, 2, 3]]` irá gerar `id IN (1, 2, 3)`. O método fará o tratamento apropriado de aspas e escape de valores para o intervalo. O operador `in` também suporta colunas compostas. Neste caso, o operando 1 deve ser um array de colunas, enquanto o operando 2 deve ser um array de arrays ou um objeto `Query` representando o intervalo das colunas.\n\n- `not in`: similar ao operador `in` exceto pelo fato de que o `IN` é substituído por `NOT IN` na geração da condição.\n\n- `like`: o operando 1 deve ser uma coluna ou uma expressão DB, e o operando 2 deve ser uma string ou um array representando o valor que a coluna ou expressão DB devem atender. Por exemplo, `['like', 'name', 'tester']` irá gerar `name LIKE '%tester%'`. Quando a faixa de valor é dado como um array, múltiplos predicados `LIKE` serão gerados e concatenadas utilizando `AND`. Por exemplo, `['like', 'name', ['test', 'sample']]` irá gerar `name LIKE '%test%' AND name LIKE '%sample%'`. Você também pode fornecer um terceiro operando opcional para especificar como escapar caracteres especiais nos valores. O operando deve ser um array de mapeamentos de caracteres especiais. Se este operando não for fornecido, um mapeamento de escape padrão será usado. Você pode usar `false` ou um array vazio para indicar que os valores já estão escapados e nenhum escape deve ser aplicado. Note-se que ao usar um mapeamento de escape (ou o terceiro operando não é fornecido), os valores serão automaticamente fechado dentro de um par de caracteres percentuais.\n\n > Observação: Ao utilizar o SGDB PostgreSQL você também pode usar [`ilike`](https://www.postgresql.org/docs/8.3/functions-matching.html#FUNCTIONS-LIKE)\n > em vez de `like` para diferenciar maiúsculas de minúsculas.\n\n- `or like`: similar ao operador `like` exceto pelo fato de que `OR` é usado para concatenar os predicados `LIKE` quando o operando 2 é um array.\n\n- `not like`: similar ao operador `like` exceto pelo fato de que `LIKE` é substituído por `NOT LIKE`.\n\n- `or not like`: similar ao operador `not like` exceto pelo fato de que `OR` é usado para concatenar os predicados `NOT LIKE`.\n\n- `exists`: requer um operador que deve ser uma instância de [[yii\\db\\Query]] representando a sub-query. Isto criará uma expressão `EXISTS (sub-query)`.\n\n- `not exists`: similar ao operador `exists` e cria uma expressão `NOT EXISTS (sub-query)`.\n\n- `>`, `<=`, ou qualquer outro operador válido que leva dois operandos: o primeiro operando deve ser um nome de coluna enquanto o segundo um valor. Ex., `['>', 'age', 10]` vai gerar `age>10`.\n\n\n#### Acrescentando Condições <span id=\"appending-conditions\"></span>\n\nVocê pode usar [[yii\\db\\Query::andWhere()|andWhere()]] ou [[yii\\db\\Query::orWhere()|orWhere()]] para acrescentar condições adicionais a uma condição já existente. Você pode chamá-los várias vezes para acrescentar várias condições separadamente. Por exemplo:\n\n```php\n$status = 10;\n$search = 'yii';\n\n$query->where(['status' => $status]);\n\nif (!empty($search)) {\n   $query->andWhere(['like', 'title', $search]);\n}\n```\n\nSe o `$search` não estiver vazio, a seguinte instrução SQL será gerada:\n\n```sql\n... WHERE (`status` = 10) AND (`title` LIKE '%yii%')\n```\n\n\n#### Filtrar Condições <span id=\"filter-conditions\"></span>\n\nAo construir condições `WHERE` a partir de entradas de usuários finais, você geralmente deseja ignorar os valores vazios. Por exemplo, em um formulário de busca que lhe permite pesquisar por nome ou e-mail, você poderia ignorar as condições nome/e-mail se não houver entradas destes valores. Para atingir este objetivo utilize o método [[yii\\db\\Query::filterWhere()|filterWhere()]]:\n\n```php\n// $username and $email são inputs dos usuário finais \n$query->filterWhere([\n   'username' => $username,\n   'email' => $email,\n]);\n```\n\nA única diferença entre  [[yii\\db\\Query::filterWhere()|filterWhere()]] e [[yii\\db\\Query::where()|where()]] é que o primeiro irá ignorar valores vazios fornecidos na condição no [formato hash](#hash-format). Então se `$email` for vazio e `$username` não, o código acima resultará um SQL como: `...WHERE username=:username`.\n\n> Observação: Um valor é considerado vazio se ele for `null`, um array vazio, uma string vazia ou uma string que consiste em apenas espaços em branco. Assim como [[yii\\db\\Query::andWhere()|andWhere()]] e [[yii\\db\\Query::orWhere()|orWhere()]], você pode usar [[yii\\db\\Query::andFilterWhere()|andFilterWhere()]] e [[yii\\db\\Query::orFilterWhere()|orFilterWhere()]] para inserir condições de filtro adicionais.\n\n\n### [[yii\\db\\Query::orderBy()|orderBy()]] <span id=\"order-by\"></span>\n\nO método  [[yii\\db\\Query::orderBy()|orderBy()]] especifica o fragmento de uma instrução SQL `ORDER BY`. Por exemplo:\n\n```php\n// ... ORDER BY `id` ASC, `name` DESC\n$query->orderBy([\n   'id' => SORT_ASC,\n   'name' => SORT_DESC,\n]);\n```\n\nNo código acima, as chaves do array são nomes de colunas e os valores são a direção da ordenação. A constante PHP `SORT_ASC` indica ordem crescente e `SORT_DESC` ordem decrescente. Se `ORDER BY` envolver apenas nomes simples de colunas, você pode especificá-lo usando string, da mesma forma como faria escrevendo SQL manualmente. Por exemplo:\n\n```php\n$query->orderBy('id ASC, name DESC');\n```\n\n> Observação: Você deve usar o formato array se `ORDER BY` envolver alguma expressão DB. \n\nVocê pode chamar [[yii\\db\\Query::addOrderBy()|addOrderBy()]] para incluir colunas adicionais para o fragmento `ORDER BY`. Por exemplo:\n\n```php\n$query->orderBy('id ASC')\n   ->addOrderBy('name DESC');\n```\n\n\n### [[yii\\db\\Query::groupBy()|groupBy()]] <span id=\"group-by\"></span>\n\nO método [[yii\\db\\Query::groupBy()|groupBy()]] especifica o fragmento de uma instrução SQL `GROUP BY`. Por exemplo:\n\n```php\n// ... GROUP BY `id`, `status`\n$query->groupBy(['id', 'status']);\n```\n\nSe o `GROUP BY` envolver apenas nomes de colunas simples, você pode especificá-lo usando uma string, da mesma forma como faria escrevendo SQL manualmente. Por exemplo:\n\n```php\n$query->groupBy('id, status');\n```\n\n> Observação: Você deve usar o formato array se `GROUP BY` envolver alguma expressão DB.\n\nVocê pode chamar [[yii\\db\\Query::addGroupBy()|addGroupBy()]] para incluir colunas adicionais ao fragmento `GROUP BY`. Por exemplo:\n\n```php\n$query->groupBy(['id', 'status'])\n   ->addGroupBy('age');\n```\n\n\n### [[yii\\db\\Query::having()|having()]] <span id=\"having\"></span>\n\nO método [[yii\\db\\Query::having()|having()]] especifica o fragmento de uma instrução SQL `HAVING`. Este método recebe uma condição que pode ser especificada da mesma forma como é feito para o [where()](#where). Por exemplo:\n\n```php\n// ... HAVING `status` = 1\n$query->having(['status' => 1]);\n```\n\nPor favor, consulte a documentação do [where()](#where) para mais detalhes de como especificar uma condição.\n\nVocê pode chamar [[yii\\db\\Query::andHaving()|andHaving()]] ou [[yii\\db\\Query::orHaving()|orHaving()]] para incluir uma condição adicional para o fragmento `HAVING`. Por exemplo:\n\n```php\n// ... HAVING (`status` = 1) AND (`age` > 30)\n$query->having(['status' => 1])\n   ->andHaving(['>', 'age', 30]);\n```\n\n\n### [[yii\\db\\Query::limit()|limit()]] e [[yii\\db\\Query::offset()|offset()]] <span id=\"limit-offset\"></span>\n\nOs métodos  [[yii\\db\\Query::limit()|limit()]] e [[yii\\db\\Query::offset()|offset()]] especificam os fragmentos de uma instrução SQL `LIMIT` e `OFFSET`. Por exemplo:\n\n```php\n// ... LIMIT 10 OFFSET 20\n$query->limit(10)->offset(20);\n```\n\nSe você especificar um limit ou offset inválido (Ex. um valor negativo), ele será ignorado.\n\n> Observação: Para SGDBs que não suportam `LIMIT` e `OFFSET` (ex. MSSQL), query builder irá gerar uma instrução SQL que emula o comportamento `LIMIT`/`OFFSET`.\n\n\n### [[yii\\db\\Query::join()|join()]] <span id=\"join\"></span>\n\nO método [[yii\\db\\Query::join()|join()]] especifica o fragmento de uma instrução SQL `JOIN`. Por exemplo:\n\n```php\n// ... LEFT JOIN `post` ON `post`.`user_id` = `user`.`id`\n$query->join('LEFT JOIN', 'post', 'post.user_id = user.id');\n```\n\nO método [[yii\\db\\Query::join()|join()]] recebe quatro parâmetros:\n\n- `$type`: tipo do join, ex., `'INNER JOIN'`, `'LEFT JOIN'`.\n- `$table`: o nome da tabela a ser unida.\n- `$on`: opcional, a condição do join, isto é, o fragmento `ON`. Por favor, consulte [where()](#where) para detalhes sobre como especificar uma condição.\n- `$params`: opcional, os parâmetros a serem vinculados à condição do join.\n\nVocê pode usar os seguintes métodos de atalho para especificar `INNER JOIN`, `LEFT JOIN` e `RIGHT JOIN`, respectivamente.\n\n- [[yii\\db\\Query::innerJoin()|innerJoin()]]\n- [[yii\\db\\Query::leftJoin()|leftJoin()]]\n- [[yii\\db\\Query::rightJoin()|rightJoin()]]\n\nPor exemplo:\n\n```php\n$query->leftJoin('post', 'post.user_id = user.id');\n```\n\nPara unir múltiplas tabelas, chame os métodos join acima multiplas vezes, uma para cada tabela. Além de unir tabelas, você também pode unir sub-queries. Para fazê-lo, especifique a sub-queries a ser unida como um objeto [[yii\\db\\Query]]. Por exemplo:\n\n```php\n$subQuery = (new \\yii\\db\\Query())->from('post');\n$query->leftJoin(['u' => $subQuery], 'u.id = author_id');\n```\n\nNeste caso, você deve colocar a sub-query em um array e usar as chaves do array para especificar o alias.\n\n\n### [[yii\\db\\Query::union()|union()]] <span id=\"union\"></span>\n\nO método [[yii\\db\\Query::union()|union()]] especifica o fragmento de uma instrução SQL `UNION`. Por exemplo:\n\n```php\n$query1 = (new \\yii\\db\\Query())\n   ->select(\"id, category_id AS type, name\")\n   ->from('post')\n   ->limit(10);\n\n$query2 = (new \\yii\\db\\Query())\n   ->select('id, type, name')\n   ->from('user')\n   ->limit(10);\n\n$query1->union($query2);\n```\n\nVocê  pode chamar [[yii\\db\\Query::union()|union()]] múltiplas vezes para acrescentar mais fragmentos `UNION`. \n\n\n## Métodos Query <span id=\"query-methods\"></span>\n\n[[yii\\db\\Query]] fornece um conjunto de métodos para diferentes propósitos da consulta:\n\n- [[yii\\db\\Query::all()|all()]]: retorna um array de linhas sendo cada linha um array de pares nome-valor.\n- [[yii\\db\\Query::one()|one()]]: retorna a primeira linha do resultado.\n- [[yii\\db\\Query::column()|column()]]: retorna a primeira coluna do resultado.\n- [[yii\\db\\Query::scalar()|scalar()]]: retorna um valor escalar localizado na primeira linha e coluna do primeiro resultado.\n- [[yii\\db\\Query::exists()|exists()]]: retorna um valor que indica se a consulta contém qualquer resultado.\n- [[yii\\db\\Query::count()|count()]]: retorna a quantidade de resultados da query.\n- Outros métodos de agregação da query, incluindo [[yii\\db\\Query::sum()|sum($q)]], [[yii\\db\\Query::average()|average($q)]], [[yii\\db\\Query::max()|max($q)]], [[yii\\db\\Query::min()|min($q)]]. O parâmetro `$q` é obrigatório para estes métodos e pode ser um nome de uma coluna ou expressão DB. Por exemplo:\n\n```php\n// SELECT `id`, `email` FROM `user`\n$rows = (new \\yii\\db\\Query())\n   ->select(['id', 'email'])\n   ->from('user')\n   ->all();\n   \n// SELECT * FROM `user` WHERE `username` LIKE `%test%`\n$row = (new \\yii\\db\\Query())\n   ->from('user')\n   ->where(['like', 'username', 'test'])\n   ->one();\n```\n\n> Observação: O método [[yii\\db\\Query::one()|one()]] retorna apenas a primeira linha do resultado da query. Ele não adiciona `LIMIT 1` para a geração da sentença SQL. Isso é bom e preferível se você souber que a query retornará apenas uma ou algumas linhas de dados (Ex. se você estiver consultando com algumas chaves primárias). Entretanto, se a query pode retornar muitas linha de dados, você deve chamar `limit(1)` explicitamente para melhorar a performance. Ex., `(new \\yii\\db\\Query())->from('user')->limit(1)->one()`.\n\nTodos estes métodos query recebem um parâmetro opcional `$db` que representa a [[yii\\db\\Connection|conexão do DB]] que deve ser usada para realizar uma consulta no DB. Se você omitir este parâmetro, o [componente da aplicação](structure-application-components.md) `db` será usado como a conexão do DB. Abaixo está um outro exemplo do método  `count()`:\n\n```php\n// executes SQL: SELECT COUNT(*) FROM `user` WHERE `last_name`=:last_name\n$count = (new \\yii\\db\\Query())\n   ->from('user')\n   ->where(['last_name' => 'Smith'])\n   ->count();\n```\n\nQuando você chamar um método de [[yii\\db\\Query]], ele na verdade faz o seguinte trabalho por baixo dos panos:\n\n* Chama [[yii\\db\\QueryBuilder]] para gerar uma instrução SQL com base na atual construção de [[yii\\db\\Query]];\n* Cria um objeto [[yii\\db\\Command]] com a instrução SQL gerada;\n* Chama um método query (ex. `queryAll()`) do [[yii\\db\\Command]] para executar a instrução SQL e retornar os dados.\n\nAlgumas vezes, você pode querer examinar ou usar a instrução SQL construído a partir de um objeto [[yii\\db\\Query]]. Você pode atingir este objetivo com o seguinte código: \n\n```php\n$command = (new \\yii\\db\\Query())\n   ->select(['id', 'email'])\n   ->from('user')\n   ->where(['last_name' => 'Smith'])\n   ->limit(10)\n   ->createCommand();\n   \n// mostra a instrução SQL \necho $command->sql;\n\n// Mostra os parâmetros que serão ligados\nprint_r($command->params);\n\n// retorna todas as linhas do resultado da query\n$rows = $command->queryAll();\n```\n\n\n### Indexando os Resultados da Query <span id=\"indexing-query-results\"></span>\n\nQuando você chama [[yii\\db\\Query::all()|all()]], será retornado um array de linhas que são indexadas por inteiros consecutivos. Algumas vezes você pode querer indexa-los de forma diferente, tal como indexar por uma coluna ou valor de expressão em particular. Você pode atingir este objetivo chamando [[yii\\db\\Query::indexBy()|indexBy()]] antes de [[yii\\db\\Query::all()|all()]]. Por exemplo:\n\n```php\n// retorna [100 => ['id' => 100, 'username' => '...', ...], 101 => [...], 103 => [...], ...]\n$query = (new \\yii\\db\\Query())\n   ->from('user')\n   ->limit(10)\n   ->indexBy('id')\n   ->all();\n```\n\nPara indexar através de valores de expressão, passe uma função anônima para o método [[yii\\db\\Query::indexBy()|indexBy()]]:\n\n```php\n$query = (new \\yii\\db\\Query())\n   ->from('user')\n   ->indexBy(function ($row) {\n       return $row['id'] . $row['username'];\n   })->all();\n```\n\nA função anônima recebe um parâmetro `$row` que contém os dados da linha atual e deve devolver um valor escalar que irá ser utilizada como índice para o valor da linha atual.\n\n\n### Batch Query (Consultas em Lote) <span id=\"batch-query\"></span>\n\nAo trabalhar com grandes quantidades de dados, métodos tais como [[yii\\db\\Query::all()]] não são adequados porque eles exigem carregar todos os dados na memória. Para manter o requisito de memória baixa, Yii fornece o chamado suporte batch query. Um batch query faz uso do cursor de dados e obtém dados em lotes. Batch query pode ser usado como a seguir:\n\n```php\nuse yii\\db\\Query;\n\n$query = (new Query())\n   ->from('user')\n   ->orderBy('id');\n\nforeach ($query->batch() as $users) {\n   // $users é um array de 100 ou menos linha da tabela user\n}\n\n// ou se você quiser fazer uma iteração da linha uma por uma\nforeach ($query->each() as $user) {\n   // $user representa uma linha de dados a partir da tabela user\n}\n```\n\nO método [[yii\\db\\Query::batch()]] and [[yii\\db\\Query::each()]] retorna um objeto [[yii\\db\\BatchQueryResult]] que implementa a interface `Iterator` e, assim, pode ser utilizado na construção do `foreach`. Durante a primeira iteração, uma consulta SQL é feita à base de dados. Os dados são, então, baixados em lotes nas iterações restantes. Por padrão, o tamanho do batch é 100, significando 100 linhas de dados que serão baixados a cada batch. Você pode mudar o tamanho do batch passando o primeiro parâmetro para os métodos `batch()` ou `each()`.\n\nEm comparação com o [[yii\\db\\Query::all()]], o batch query somente carrega 100 linhas de dados na memória a cada vez. Se você processar os dados e, em seguida, descartá-lo imediatamente, o batch query pode ajudar a reduzir o uso de memória. Se você especificar o resultado da query a ser indexado por alguma coluna via [[yii\\db\\Query::indexBy()]], o batch query ainda vai manter o índice adequado. Por exemplo:\n\n```php\n$query = (new \\yii\\db\\Query())\n   ->from('user')\n   ->indexBy('username');\n\nforeach ($query->batch() as $users) {\n   // $users é indexado pela coluna  \"username\"\n}\n\nforeach ($query->each() as $username => $user) {\n}\n```\n"
  },
  {
    "path": "docs/guide-pt-BR/helper-overview.md",
    "content": "Helpers\n=======\n\n> Observação: Esta seção está em desenvolvimento.\n\nO Yii oferece muitas classes que ajudam a simplificar as tarefas comuns de codificação, como manipulação de string ou de array,\ngeração de código HTML, e assim por diante. Essas classes helpers (auxiliares) são organizadas no namespace `yii\\helpers` e\nsão todas classes estáticas (o que significa que contêm apenas propriedades e métodos estáticos e não devem ser instanciadas).\n\nVocê usa uma classe helper chamando diretamente um de seus métodos estáticos, como o seguinte:\n\n```php\nuse yii\\helpers\\Html;\n\necho Html::encode('Test > test');\n```\n\n> Observação: Para oferecer suporte à [personalização de classes helper](#customizing-helper-classes),  o Yii divide cada classe helper principal\n  em duas classes: uma classe base (ex. `BaseArrayHelper`) e uma classe concreta (ex. `ArrayHelper`).\n  Ao usar um helper, você deve usar apenas a versão concreta e nunca a classe base.\n\n\nPrincipais Classes Helper\n-------------------\n\nAs seguintes classes helper são fornecidas nas versões Yii:\n\n- [ArrayHelper](helper-array.md)\n- Console\n- FileHelper\n- FormatConverter\n- [Html](helper-html.md)\n- HtmlPurifier\n- Imagine (fornecido pela extensão yii2-imagine)\n- Inflector\n- [Json](helper-json.md)\n- Markdown\n- StringHelper\n- [Url](helper-url.md)\n- VarDumper\n\n\nPersonalização de Classes Helper <span id=\"customizing-helper-classes\"></span>\n--------------------------\n\nPara personalizar uma classe helper principal (ex. [[yii\\helpers\\ArrayHelper]]), você deve criar uma nova classe que \nestende da classe base correspondente ao helper (ex. [[yii\\helpers\\BaseArrayHelper]]) e nomear a sua classe da mesma\nforma que a classe concreta correspondente (ex. [[yii\\helpers\\ArrayHelper]]), Essa classe será então configurada para \nsubstituir a implementação original da estrutura.\n\nO exemplo a seguir mostra como personalizar o método [[yii\\helpers\\ArrayHelper::merge()|merge()]] da classe\n[[yii\\helpers\\ArrayHelper]]:\n\n```php\n<?php\n\nnamespace yii\\helpers;\n\nclass ArrayHelper extends BaseArrayHelper\n{\n    public static function merge($a, $b)\n    {\n        // sua implementação personalizada\n    }\n}\n```\n\nSalve sua classe em um arquivo chamado `ArrayHelper.php`. O arquivo pode estar em qualquer diretório, por exemplo `@app/components`.\n\nEm seguida, no [script de entrada](structure-entry-scripts.md) da sua aplicação, adicione a seguinte linha de código\ndepois do include do arquivo `yii.php` para dizer ao [autoloader de classes do Yii](concept-autoloading.md) para carregar sua classe\npersonalizada em vez da classe Helper original do framework:\n\n```php\nYii::$classMap['yii\\helpers\\ArrayHelper'] = '@app/components/ArrayHelper.php';\n```\n\nObserve que personalizar as classes Helper só é útil se você quiser mudar o comportamento de uma função existente\ndos Helpers. Se você deseja adicionar outras funções para usar em sua aplicação, é melhor criar um\nHelper para isso.\n"
  },
  {
    "path": "docs/guide-pt-BR/helper-url.md",
    "content": "URL Helper\n==========\n\nURL helper fornece um conjunto de métodos estáticos para o gerenciamento de URLs.\n\n\n## Obtendo URLs comuns <span id=\"getting-common-urls\"></span>\n\nHá dois métodos que você pode usar para obter URLs comuns: URL home e URL base para a requisição corrente. Para obter a URL home, use o seguinte:\n\n```php\n$relativeHomeUrl = Url::home();\n$absoluteHomeUrl = Url::home(true);\n$httpsAbsoluteHomeUrl = Url::home('https');\n```\n\nSe nenhum parâmetro for passado, a URL gerada é relativa. Você pode passar `true` para obter uma URL absoluta para a o esquema corrente ou especificar um esquema explicitamente (`https`, `http`).\n\nPara obter a URL base da requisição corrente, use o seguinte :\n \n```php\n$relativeBaseUrl = Url::base();\n$absoluteBaseUrl = Url::base(true);\n$httpsAbsoluteBaseUrl = Url::base('https');\n```\n\nO único parâmetro do metódo funciona exatamente da mesma que em  `Url::home()`.\n\n\n## Criando URLs <span id=\"creating-urls\"></span>\n\nAfim de criar ma URL para uma rota utilize o metódo `Url::toRoute()`. O metódo usa [[\\yii\\web\\UrlManager]] para criar a URL:\n\n```php\n$url = Url::toRoute(['product/view', 'id' => 42]);\n```\n \nVocê pode especificar uma URL como string, ou seja, `site/index`. Você pode também usar um array se você precisa especificar parâmetros adicionais para a URL a ser criada. O formato do array deve ser:\n\n```php\n// generates: /index.php?r=site/index&param1=value1&param2=value2\n['site/index', 'param1' => 'value1', 'param2' => 'value2']\n```\n\nSe você quiser criar uma URL com uma ancôra (anchor), você pode usar no array o parâmetro `#`. Por exemplo,\n\n```php\n// generates: /index.php?r=site/index&param1=value1#name\n['site/index', 'param1' => 'value1', '#' => 'name']\n```\n\nUma rota pode ser absoluta ou relativa. Uma rota absoluta tem uma barra inicial (e.g. `/site/index`) enquanto uma rota relativa não (e.g. `site/index` or `index`). Uma rota relativa pode ser convertida em absoluta seguinda as seguintes regras:\n\n- Se a rota é uma string vazia, será usada a corrente rota [[\\yii\\web\\Controller::route|route]];\n- Se a rota não contém nenhuma barra (e.g. `index`), considera-se ser o ID da acão corrente do controlador;\n  e serão precedidas por [[\\yii\\web\\Controller::uniqueId]];\n- Se a rota não tem uma barra inicial (e.g. `site/index`), isto é considerado um URL relativa para para o modulo corrente\n  e será precedido por [[\\yii\\base\\Module::uniqueId|uniqueId]].\n  \nA partir da versão 2.0.2, você pode espeficiar uma rota como um [alias](concept-aliases.md). Se esse é o caso,\no alias será primeiro convertido para rota atual que irá então ser transformado em uma rota absoluta de acordo\nàs regras acima .\n\nAbaixo estão alguns exemplos de como usar este método:\n\n```php\n// /index.php?r=site/index\necho Url::toRoute('site/index');\n\n// /index.php?r=site/index&src=ref1#name\necho Url::toRoute(['site/index', 'src' => 'ref1', '#' => 'name']);\n\n// /index.php?r=post/edit&id=100     assume the alias \"@postEdit\" is defined as \"post/edit\"\necho Url::toRoute(['@postEdit', 'id' => 100]);\n\n// https://www.example.com/index.php?r=site/index\necho Url::toRoute('site/index', true);\n\n// https://www.example.com/index.php?r=site/index\necho Url::toRoute('site/index', 'https');\n```\n\nHá um outro método `Url::to()` que é muito semelhante a [[toRoute()]]. A única diferença é que este método requer uma rota a ser especificado como apenas como array. Se for dado uma string, ela será tratada como um URL.\n\nO primeiro argumento pode ser:\n         \n- um array: [[toRoute()]]  irá ser chamado para gerar a URL. Por exemplo:\n  `['site/index']`, `['post/index', 'page' => 2]`. Por favor consulte [[toRoute()]] para mais detalhes de como especificar uma rota.\n- uma string com inicio `@`: ele é tratado como um alias, e as strings correspondentes ao alias serão devolvidos.\n- uma string vazia: A URL da requisição corrente será retornado;\n- uma string normal: será devolvida como ele foi passada (como ela é).\n\nQuando `$scheme` é espefificado (como uma string ou `true`),uma URL absoluta com informações do host (obtida de\n[[\\yii\\web\\UrlManager::hostInfo]]) será retornada. Se `$url` já é uma URL absoluta, seu scheme\nirá ser substituído pelo o especificado.\n\nAbaixo estão alguns exemplos de uso:\n\n```php\n// /index.php?r=site/index\necho Url::to(['site/index']);\n\n// /index.php?r=site/index&src=ref1#name\necho Url::to(['site/index', 'src' => 'ref1', '#' => 'name']);\n\n// /index.php?r=post/edit&id=100     assume the alias \"@postEdit\" is defined as \"post/edit\"\necho Url::to(['@postEdit', 'id' => 100]);\n\n// the currently requested URL\necho Url::to();\n\n// /images/logo.gif\necho Url::to('@web/images/logo.gif');\n\n// images/logo.gif\necho Url::to('images/logo.gif');\n\n// https://www.example.com/images/logo.gif\necho Url::to('@web/images/logo.gif', true);\n\n// https://www.example.com/images/logo.gif\necho Url::to('@web/images/logo.gif', 'https');\n```\n\nA partir da versão 2.0.3, você pode usar [[yii\\helpers\\Url::current()]] para criar uma URL base para rota solicitada e parâmetros GET. Você pode modificar ou remover alguns dos parâmetros GET ou adicionar novos por\npassando o parâmetro `$ params` para o método. Por exemplo,\n\n```php\n// assume $_GET = ['id' => 123, 'src' => 'google'], current route is \"post/view\"\n\n// /index.php?r=post/view&id=123&src=google\necho Url::current();\n\n// /index.php?r=post/view&id=123\necho Url::current(['src' => null]);\n// /index.php?r=post/view&id=100&src=google\necho Url::current(['id' => 100]);\n```\n\n\n## Relembrar URLs <span id=\"remember-urls\"></span>\n\nHá casos em que você precisa se lembrar URL e depois usá-lo durante o processamento de uma das requisições sequenciais.\nPode ser conseguida da seguinte forma:\n \n```php\n// Remember current URL \nUrl::remember();\n\n// Remember URL specified. See Url::to() for argument format.\nUrl::remember(['product/view', 'id' => 42]);\n\n// Remember URL specified with a name given\nUrl::remember(['product/view', 'id' => 42], 'product');\n```\n\nNa próxima requisição, podemos obter URL lembrada da seguinte forma:\n\n```php\n$url = Url::previous();\n$productUrl = Url::previous('product');\n```\n                        \n## Verificar URLs relativas <span id=\"checking-relative-urls\"></span>\n\nPara saber se a URL é relativa, ou seja, ele não tem informações do host, você pode usar o seguinte código:\n                             \n```php\n$isRelative = Url::isRelative('test/it');\n```\n"
  },
  {
    "path": "docs/guide-pt-BR/images/advanced-app-configs.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yFiles for Java 2.11-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Beschreibung\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"409.0\" width=\"148.0\" x=\"290.953299818952\" y=\"148.6669921875\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"148.0\" x=\"0.0\" y=\"0.0\">console</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 4</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n0:\"/>\n    </node>\n    <node id=\"n1\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"409.0\" width=\"148.0\" x=\"310.2342794220951\" y=\"167.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"148.0\" x=\"0.0\" y=\"0.0\">backend</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"310.2342794220951\" y=\"167.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 3</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n1:\"/>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"62.0\" width=\"118.0\" x=\"348.0\" y=\"628.9999999999999\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"32.6875\" x=\"42.65625\" y=\"21.6494140625\">index<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"rectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n3\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"402.37646484375\" width=\"148.0\" x=\"333.0\" y=\"188.62353515625\"/>\n              <y:Fill color=\"#DDFFDD\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#DDFFDD\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"148.0\" x=\"0.0\" y=\"0.0\">frontend</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 1</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n3:\">\n        <node id=\"n3::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"348.0\" y=\"226.0\"/>\n              <y:Fill color=\"#FFCCCC\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"44.013671875\" x=\"36.9931640625\" y=\"21.6494140625\">params<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n3::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"348.0\" y=\"322.0\"/>\n              <y:Fill color=\"#FFCCCC\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"72.689453125\" x=\"22.6552734375\" y=\"21.6494140625\">params-local<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n3::n2\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"348.0\" y=\"418.0\"/>\n              <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"30.009765625\" x=\"43.9951171875\" y=\"21.6494140625\">main<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n3::n3\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"348.0\" y=\"514.0\"/>\n              <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"58.685546875\" x=\"29.6572265625\" y=\"21.6494140625\">main-local<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"62.0\" width=\"223.21580824827026\" x=\"7.0\" y=\"628.9999999999999\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"41.353515625\" x=\"90.93114631163513\" y=\"21.6494140625\">aliases<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"rectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"306.37646484375\" width=\"311.0\" x=\"7.0\" y=\"188.62353515625\"/>\n              <y:Fill color=\"#DDFFDD\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#DDFFDD\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"311.0\" x=\"0.0\" y=\"0.0\">common</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"-8.0\" y=\"189.333984375\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 2</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n5:\">\n        <node id=\"n5::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"185.0\" y=\"226.0\"/>\n              <y:Fill color=\"#FFCCCC\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"72.689453125\" x=\"22.6552734375\" y=\"21.6494140625\">params-local<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n5::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"185.0\" y=\"418.0\"/>\n              <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"58.685546875\" x=\"29.6572265625\" y=\"21.6494140625\">main-local<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n5::n2\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"22.0\" y=\"226.0\"/>\n              <y:Fill color=\"#FFCCCC\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"44.013671875\" x=\"36.9931640625\" y=\"21.6494140625\">params<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n5::n3\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"22.0\" y=\"418.0\"/>\n              <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"30.009765625\" x=\"43.9951171875\" y=\"21.6494140625\">main<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <edge id=\"e0\" source=\"n3::n3\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"2.0\" y=\"10.125\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-111.48005839096935\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"3.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.25\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"33.47449351530429\" y=\"-2.0000000000001137\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n5::n1\" target=\"n3::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"5.6843418860808015E-14\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"218.8251533742331\" y=\"449.00000000000006\"/>\n            <y:Point x=\"218.8251533742331\" y=\"449.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"10.089752197265625\" y=\"-6.0\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n5::e0\" source=\"n5::n2\" target=\"n5::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"10.125\" y=\"-6.0\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n5::e1\" source=\"n5::n3\" target=\"n5::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"10.125\" y=\"-6.0\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n3::e0\" source=\"n3::n0\" target=\"n3::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"2.0\" y=\"10.125\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n3::e1\" source=\"n3::n1\" target=\"n3::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"2.0\" y=\"10.125\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n3::e2\" source=\"n3::n2\" target=\"n3::n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"2.0\" y=\"10.125\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n5::n0\" target=\"n3::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"-5.6843418860808015E-14\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"218.8251533742331\" y=\"256.99999999999994\"/>\n            <y:Point x=\"218.8251533742331\" y=\"257.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"10.089752197265625\" y=\"-6.0\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources/>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-pt-BR/images/application-structure.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.13-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"872.1807999999999\" y=\"-14.764159999999947\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"32.265625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"76.3984375\" x=\"11.80078125\" y=\"1.3671875\">componente\nda aplicação<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"-91.97375999999994\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"32.265625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"54.71875\" x=\"22.640625\" y=\"1.3671875\">script de\nentrada<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"-14.764159999999947\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"58.03515625\" x=\"20.982421875\" y=\"8.43359375\">aplicação<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"62.44544000000004\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"60.267578125\" x=\"19.8662109375\" y=\"8.433593750000007\">controller<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"872.1807999999999\" y=\"62.44544000000005\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"32.119140625\" x=\"33.9404296875\" y=\"8.43359375\">filtro<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"532.664\" y=\"23.901600000000087\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"48.4140625\" x=\"25.79296875\" y=\"8.43359375\">módulo<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"618.4047999999991\" y=\"139.65504000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"29.611328125\" x=\"35.1943359375\" y=\"8.43359375\">view<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"786.161599999999\" y=\"139.65504000000004\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"40.28125\" x=\"29.859375\" y=\"8.43359375\">model<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n8\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"532.664\" y=\"216.86464000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"42.923828125\" x=\"28.5380859375\" y=\"8.43359375\">widget<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n9\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"216.86464000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"77.986328125\" x=\"11.0068359375\" y=\"8.43359375\">asset bundle<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n2\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.97265625\" x=\"2.5392475585936154\" y=\"-25.635106635436955\">1:1<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.025600000000054\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.17992697197425173\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n0\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-48.97602119140629\" y=\"-23.09200633216852\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.025599999999994\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5470891790487767\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n5\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"15.193962466822086\" y=\"30.674065521861735\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"44.894496863129426\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.25416574623968574\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n3\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"2.7719538085937074\" y=\"-26.04470613037104\">1..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"15.254400000000032\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.2090245199765919\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n3\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-57.39355288912964\" y=\"-63.846685518352906\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"43.47658308018222\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.881412889692355\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n5\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"582.664\" y=\"-3.598399999999913\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-17.888505566406252\" y=\"-42.41848039245597\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"16.483200000000004\" distanceToCenter=\"true\" position=\"right\" ratio=\"5.822600000000001\" segment=\"-2\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n6\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-12.223966589163297\" y=\"-24.76668258085064\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"11.385360101663515\" distanceToCenter=\"true\" position=\"left\" ratio=\"0.10670293331985262\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e7\" source=\"n7\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-9.313146217848043\" y=\"-26.098020948340803\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.669798038586146\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.1670192242704811\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e8\" source=\"n9\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-9.797233483694527\" y=\"-25.82443358614151\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.15599378957306\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.15481212573620068\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e9\" source=\"n8\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-15.633139430038\" y=\"-24.050683308790553\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"13.24330808446041\" distanceToCenter=\"true\" position=\"left\" ratio=\"0.05506723556297327\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e10\" source=\"n4\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-47.337621191406356\" y=\"-22.682408449706998\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"13.616000000000007\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e11\" source=\"n9\" target=\"n8\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-44.265598339843905\" y=\"-19.61040553222648\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.543999999999983\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.4117077892835431\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e12\" source=\"n7\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-44.81721357421975\" y=\"-19.610410805664003\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.543999999999983\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.4531592446547025\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources/>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-pt-BR/images/rbac-access-check-1.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-pt-BR/images/rbac-access-check-2.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-pt-BR/images/rbac-access-check-3.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-pt-BR/images/rbac-hierarchy-1.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n5\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-pt-BR/images/rbac-hierarchy-2.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-pt-BR/images/request-lifecycle.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.13-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"70.13700103759766\" width=\"56.558998107910156\" x=\"198.38633947503078\" y=\"261.099458694458\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"26.279499053955078\" y=\"74.13700103759766\">\n            <y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"-0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"0.5\" offsetX=\"0.0\" offsetY=\"4.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"47.388671875\" x=\"-51.388671875\" y=\"26.002094268798828\">usuário<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.5\" labelRatioY=\"0.0\" nodeRatioX=\"-0.5\" nodeRatioY=\"0.0\" offsetX=\"-4.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"637.1271178722382\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"40.28125\" x=\"39.359375\" y=\"13.43359375\">model<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"46.887996673583984\" width=\"39.527000427246094\" x=\"828.8018617630005\" y=\"544.9831195354463\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"96.197265625\" x=\"-28.335132598876953\" y=\"-30.154888153076172\">banco de dados<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-12.022075653076172\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"17.763500213623047\" y=\"21.443998336791992\">\n            <y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"713.6271178722382\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"29.611328125\" x=\"44.6943359375\" y=\"13.43359375\">view<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n4\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"376.3268349409104\" width=\"219.27949905395508\" x=\"527.4919853210449\" y=\"408.6371007919311\"/>\n              <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.666015625\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"219.27949905395508\" x=\"0.0\" y=\"0.0\">controller</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"2\" leftF=\"1.7335329055786133\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"412.2765645980835\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.666015625\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"63.75830078125\" x=\"-6.879150390625\" y=\"0.0\">Folder 1</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n4:\">\n        <node id=\"n4::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"445.3031164169311\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.498046875\" x=\"43.03047561645508\" y=\"5.93359375\">cria action<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n4::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"45.0\" width=\"159.91864204406738\" x=\"558.5326642990112\" y=\"521.8166599273682\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"75.923828125\" x=\"41.99740695953369\" y=\"13.43359375\">aplica filtros<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"diamond\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n4::n2\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"162.00283348560333\" width=\"187.54596614837646\" x=\"544.2255182266235\" y=\"607.9611022472382\"/>\n                  <y:Fill color=\"#99336635\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#993366\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.666015625\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#FFFFFF\" visible=\"true\" width=\"187.54596614837646\" x=\"0.0\" y=\"0.0\">action</y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"4\" bottomF=\"3.8368178606033325\" left=\"4\" leftF=\"3.9869680404663086\" right=\"3\" rightF=\"3.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.666015625\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"63.75830078125\" x=\"-6.879150390625\" y=\"0.0\">Folder 3</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n4::n2:\">\n            <node id=\"n4::n2::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"644.6271178722382\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"87.466796875\" x=\"31.546100616455078\" y=\"5.93359375\">carrega model<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n4::n2::n1\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"721.1271178722382\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"88.57421875\" x=\"30.992389678955078\" y=\"5.93359375\">renderiza view<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n      </graph>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"96.0\" x=\"189.47636032104495\" y=\"713.6271178722382\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"53.869140625\" x=\"21.0654296875\" y=\"13.43359375\">resposta<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"254.50096702575684\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"63.794921875\" x=\"27.6025390625\" y=\"13.43359375\">requisição<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"143.421781539917\" width=\"219.27949905395508\" x=\"527.4919853210449\" y=\"223.57918548583984\"/>\n              <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.666015625\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"219.27949905395508\" x=\"0.0\" y=\"0.0\">aplicação</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"21\" leftF=\"20.720500946044922\" right=\"18\" rightF=\"18.0\" top=\"2\" topF=\"1.7557659149169922\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.666015625\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"63.75830078125\" x=\"-6.879150390625\" y=\"0.0\">Folder 2</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n7:\">\n        <node id=\"n7::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"262.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"72.642578125\" x=\"38.95820999145508\" y=\"5.93359375\">resolve rota<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n7::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"322.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"85.216796875\" x=\"32.67110061645508\" y=\"5.93359375\">cria controller<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <node id=\"n8\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"141.666015625\" width=\"171.33075141906738\" x=\"307.591796875\" y=\"225.33495140075684\"/>\n              <y:Fill color=\"#FFCC0024\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FFCC00\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.666015625\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"171.33075141906738\" x=\"0.0\" y=\"0.0\">script de entrada</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"1\" leftF=\"0.9186420440673828\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"225.33495140075684\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.666015625\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"63.75830078125\" x=\"-6.879150390625\" y=\"0.0\">Folder 4</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n8:\">\n        <node id=\"n8::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"129.0\" x=\"329.2164936065674\" y=\"262.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"140.412109375\" x=\"-5.7060546875\" y=\"5.93359375\"> carrega configurações <y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n8::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"129.0\" x=\"329.2164936065674\" y=\"322.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"107.271484375\" x=\"10.8642578125\" y=\"5.93359375\">executa aplicação<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <edge id=\"e0\" source=\"n2\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n5\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-10.81052179205907\" sy=\"-22.46484374999998\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"28.000006009454637\" y=\"-193.20499098300934\">\n            <y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"19.17578125\" x=\"-9.587884615545363\" y=\"-200.27139723300934\">11<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n6\" target=\"n7::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.587890625\" x=\"-30.207454877387818\" y=\"-10.065448760986328\">3<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"0.9990329742431641\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.25395048761528816\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n7::e0\" source=\"n7::n0\" target=\"n7::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n0\" target=\"n8\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"-79.9882439360955\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.587890625\" x=\"34.38903536861892\" y=\"-9.066415786743164\">1<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n7::n1\" target=\"n4\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"2.408647025755731\" ty=\"-181.21658369302781\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.587890625\" x=\"-5.416119621118469\" y=\"19.25165109634395\">4<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n3\" target=\"n4::n2::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.587890625\" x=\"-30.77317586853087\" y=\"-9.06642460823059\">9<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.26448415477215953\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n4::n2::n1\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"-2.2737367544323206E-13\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"19.17578125\" x=\"-93.46793455769887\" y=\"-9.066424608230705\">10<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.2786124840137136\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e7\" source=\"n8::n1\" target=\"n7\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"55.750299312644984\" sy=\"0.355224609375\" tx=\"-109.63961141576266\" ty=\"42.066115379333496\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.587890625\" x=\"28.868979454040527\" y=\"-9.066415786743164\">2<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e8\" source=\"n1\" target=\"n4::n2::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.587890625\" x=\"-31.923049588221375\" y=\"-10.06642460823059\">8<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"1.0\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.2858946861565087\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e9\" source=\"n4::n1\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-79.9882439360951\" sy=\"2.2737367544323206E-13\" tx=\"24.24112858184396\" ty=\"-1.4999999999999991\">\n            <y:Point x=\"261.71748890288893\" y=\"544.3166599273684\"/>\n          </y:Path>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.587890625\" x=\"-83.6574311462557\" y=\"-9.066396713256609\">6<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.23459266172695797\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::e0\" source=\"n4::n0\" target=\"n4::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFEFD6\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.587890625\" x=\"-5.793964385986328\" y=\"4.999985313415493\">5<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.0\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::e1\" source=\"n4::n1\" target=\"n4::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"-81.01828954355172\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFEFD6\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.587890625\" x=\"-5.903311505104057\" y=\"5.037729263305664\">7<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.0\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::n2::e0\" source=\"n4::n2::n0\" target=\"n4::n2::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n8::e0\" source=\"n8::n0\" target=\"n8::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"66px\" viewBox=\"0 0 57 66\" enable-background=\"new 0 0 57 66\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3799\" y1=\"-2276.8809\" x2=\"27.6209\" y2=\"-2306.6792\" gradientTransform=\"matrix(1 0 0 -1 0.2803 -2252.9199)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_13_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t&lt;path fill=\"#2068A3\" stroke=\"#2068A3\" d=\"M28.106,33.487c-8.112,0-12.688,4.312-12.688,10.437c0,7.422,12.688,10.438,12.688,10.438\n\t\ts14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.487,28.106,33.487z M26.288,53.051c0,0-7.135-2.093-8.805-7.201\n\t\tc-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"14.2417\" cy=\"9.1006\" r=\"53.247\" gradientTransform=\"matrix(1 0 0 -1 0.04 65.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#74AEEE\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#2068A3\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#2068A3\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.022,7.807-14.022,7.807s-10.472-2.484-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path fill=\"#5491CF\" stroke=\"#2068A3\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397c-0.514,1.027-1.669,4.084-1.669,5.148\n\t\tc0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.335-2.36c-3.601-1.419-4.071-3.063-5.89-4.854\n\t\tC12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t&lt;path fill=\"#5491CF\" stroke=\"#2068A3\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617c0.516,1.025,3.617,3.693,3.617,6.617\n\t\tc0,5.186-10.27,8.576-16.698,9.145c1.429,4.938,11.372,1.293,13.804-0.313c3.563-2.354,4.563-5.133,7.854-3.705\n\t\tC47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t&lt;path fill=\"none\" stroke=\"#2068A3\" stroke-linecap=\"round\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t&lt;path fill=\"none\" stroke=\"#2068A3\" stroke-linecap=\"round\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.623\" cy=\"-2278.646\" r=\"23.425\" fx=\"23.0534\" fy=\"-2281.1357\" gradientTransform=\"matrix(1 0 0 -1 0.2803 -2252.9199)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"5761.7578\" y1=\"11330.6484\" x2=\"5785.3872\" y2=\"11424.0977\" gradientTransform=\"matrix(0.275 0 0 0.2733 -1558.9874 -3088.4209)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M27.958,6.333c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.083,13.952,36.271,6.268,27.958,6.333z\"/&gt;\n\t&lt;path id=\"Hair_Young_Brown_1_\" fill=\"#CC9869\" stroke=\"#99724F\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n\t&lt;path fill=\"#4B4B4B\" stroke=\"#4B4B4B\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" d=\"M28.105,2\n\t\tC22.464,2,20.2,4.246,18.13,5.533C29.753,2.865,41.152,10.375,44.46,20.5C44.459,16.875,44.459,2,28.105,2z\"/&gt;\n\t&lt;path fill=\"#9B9B9B\" stroke=\"#4B4B4B\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" d=\"M11.151,17.751\n\t\tC12.878,8.25,18.686,6.309,25.273,7.127C31.295,7.875,36.93,10.491,44.459,20.5C37.777,7.125,20.278-3.375,9.903,3.921\n\t\tC5.569,6.97,4.903,13.375,11.151,17.751z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\"\n\t xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\t x=\"0px\" y=\"0px\" width=\"41px\" height=\"48px\" viewBox=\"-0.875 -0.887 41 48\" enable-background=\"new -0.875 -0.887 41 48\"\n\t xml:space=\"preserve\"&gt;\n&lt;defs&gt;\n&lt;/defs&gt;\n&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-979.1445\" x2=\"682.0508\" y2=\"-979.1445\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_1_)\" d=\"M19.625,36.763C8.787,36.763,0,34.888,0,32.575v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,34.888,30.464,36.763,19.625,36.763z\"/&gt;\n&lt;linearGradient id=\"SVGID_2_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-973.1445\" x2=\"682.0508\" y2=\"-973.1445\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_2_)\" d=\"M19.625,36.763c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.927-18.396,3.927\n\tc-9.481,0-17.396-1.959-18.396-3.927l-1.229,2C0,34.888,8.787,36.763,19.625,36.763z\"/&gt;\n&lt;path fill=\"#3C89C9\" d=\"M19.625,26.468c10.16,0,19.625,2.775,19.625,2.775c-0.375,2.721-5.367,5.438-19.554,5.438\n\tc-12.125,0-18.467-2.484-19.541-4.918C-0.127,29.125,9.465,26.468,19.625,26.468z\"/&gt;\n&lt;linearGradient id=\"SVGID_3_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-965.6948\" x2=\"682.0508\" y2=\"-965.6948\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_3_)\" d=\"M19.625,23.313C8.787,23.313,0,21.438,0,19.125v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,21.438,30.464,23.313,19.625,23.313z\"/&gt;\n&lt;linearGradient id=\"SVGID_4_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-959.6948\" x2=\"682.0508\" y2=\"-959.6948\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_4_)\" d=\"M19.625,23.313c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.926-18.396,3.926\n\tc-9.481,0-17.396-1.959-18.396-3.926l-1.229,2C0,21.438,8.787,23.313,19.625,23.313z\"/&gt;\n&lt;path fill=\"#3C89C9\" d=\"M19.476,13.019c10.161,0,19.625,2.775,19.625,2.775c-0.375,2.721-5.367,5.438-19.555,5.438\n\tc-12.125,0-18.467-2.485-19.541-4.918C-0.277,15.674,9.316,13.019,19.476,13.019z\"/&gt;\n&lt;linearGradient id=\"SVGID_5_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-952.4946\" x2=\"682.0508\" y2=\"-952.4946\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_5_)\" d=\"M19.625,10.113C8.787,10.113,0,8.238,0,5.925v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,8.238,30.464,10.113,19.625,10.113z\"/&gt;\n&lt;linearGradient id=\"SVGID_6_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-946.4946\" x2=\"682.0508\" y2=\"-946.4946\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_6_)\" d=\"M19.625,10.113c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.926-18.396,3.926\n\tc-9.481,0-17.396-1.959-18.396-3.926L0,5.925C0,8.238,8.787,10.113,19.625,10.113z\"/&gt;\n&lt;linearGradient id=\"SVGID_7_\" gradientUnits=\"userSpaceOnUse\" x1=\"644.0293\" y1=\"-943.4014\" x2=\"680.8223\" y2=\"-943.4014\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;ellipse fill=\"url(#SVGID_7_)\" cx=\"19.625\" cy=\"3.926\" rx=\"18.396\" ry=\"3.926\"/&gt;\n&lt;path opacity=\"0.24\" fill=\"#FFFFFF\" enable-background=\"new    \" d=\"M31.04,45.982c0,0-4.354,0.664-7.29,0.781\n\tc-3.125,0.125-8.952,0-8.952,0l-2.384-10.292l0.044-2.108l-1.251-1.154L9.789,23.024l-0.082-0.119L9.5,20.529l-1.65-1.254\n\tL5.329,8.793c0,0,4.213,0.903,7.234,1.07s8.375,0.25,8.375,0.25l3,9.875l-0.25,1.313l1.063,2.168l2.312,9.645l-0.521,1.416\n\tl1.46,1.834L31.04,45.982z\"/&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-pt-BR/intro-upgrade-from-v1.md",
    "content": "Atualizando a partir da Versão 1.1\n==================================\n\nExistem muitas diferenças entre as versões 1.1 e 2.0 do Yii, uma vez que o\nframework foi completamente reescrito na 2.0.\nPor causa disso, atualizar a partir da versão 1.1 não é tão trivial quanto atualizar de versões menores. Neste guia você encontrará as principais diferenças entre as duas versões.\n\nSe você nunca usou o Yii 1.1 antes, você pode pular com segurança esta seção\ne ir diretamente para \"[Instalando o Yii](start-installation.md)\".\n\nPor favor, note que o Yii 2.0 introduz outras novas funcionalidades além das que\nsão abordadas neste resumo. Recomenda-se fortemente que você leia o guia definitivo por completo para aprender todas elas. É possível que algumas funcionalidades\nque antes você tinha de desenvolver por conta própria agora façam parte do\ncódigo principal.\n\n\nInstalação\n----------\n\nO Yii 2.0 utiliza plenamente o [Composer](https://getcomposer.org/), o gerenciador\nde pacotes PHP. Tanto a instalação do núcleo do framework quanto das extensões são feitas através do Composer. Por favor, consulte a seção\n[Instalando o Yii](start-installation.md) para aprender como instalar\no Yii 2.0. Se você quer criar novas extensões ou tornar compatíveis as suas extensões\nexistentes do 1.1 com o 2.0, por favor consulte a seção [Criando Extensões](structure-extensions.md#creating-extensions)\ndo guia.\n\n\nRequisitos do PHP\n-----------------\n\nO Yii 2.0 requer o PHP 5.4 ou superior, que é uma versão de grande melhoria sobre a versão 5.2, que era exigida pelo Yii 1.1.\nComo resultado, existem muitas diferenças na linguagem às quais você deve dar a devida atenção.\nSegue abaixo um resumo das principais mudanças do PHP:\n\n- [Namespaces](https://www.php.net/manual/pt_BR/language.namespaces.php).\n- [Funções anônimas](https://www.php.net/manual/pt_BR/functions.anonymous.php).\n- A sintaxe curta de arrays `[...elementos...]` é utilizada ao invés de `array(...elementos...)`.\n- Tags curtas de *echo* `<?=` são usadas nos arquivos de view. É seguro utilizá-las a partir do PHP 5.4.\n- [Classes e interfaces da SPL](https://www.php.net/manual/pt_BR/book.spl.php).\n- [Late Static Bindings](https://www.php.net/manual/pt_BR/language.oop5.late-static-bindings.php).\n- [Date e Time](https://www.php.net/manual/pt_BR/book.datetime.php).\n- [Traits](https://www.php.net/manual/pt_BR/language.oop5.traits.php).\n- [intl](https://www.php.net/manual/pt_BR/book.intl.php). O Yii 2.0 utiliza a extensão\n  `intl` do PHP para suportar as funcionalidades de internacionalização.\n\n\nNamespace\n---------\n\nA mudança mais óbvia no Yii 2.0 é o uso de namespaces. Praticamente todas as\nclasses do *core* possuem namespace, por exemplo, `yii\\web\\Request`. O prefixo \"C\"\nnão é mais utilizado nos nomes de classes. O esquema de nomenclatura agora segue\na estrutura de diretórios. Por exemplo, `yii\\web\\Request` indica que o arquivo\nda classe correspondente é `web/Request.php` sob a pasta do Yii Framework.\n\n(Você pode utilizar qualquer classe do *core* sem explicitamente incluir o arquivo\ndessa classe, graças ao carregador de classes do Yii).\n\n\nComponent e Object\n------------------\n\nO Yii 2.0 divide a classe `CComponent` do 1.1 em duas classes: [[yii\\base\\BaseObject]]\ne [[yii\\base\\Component]]. A classe [[yii\\base\\BaseObject|BaseObject]] é uma classe base\nleve que permite a definição das [propriedades de objetos](concept-properties.md)\nvia getters e setters. A classe [[yii\\base\\Component|Component]] estende de\n[[yii\\base\\BaseObject|BaseObject]] e suporta [eventos](concept-events.md) e\n[comportamentos (behaviors)](concept-behaviors.md).\n\n\nSe a sua classe não precisa de eventos nem de comportamentos,\nvocê deveria considerar utilizar [[yii\\base\\BaseObject|BaseObject]] como classe base.\nEsse geralmente é o caso de classes que representam estruturas básicas de dados.\n\n\nConfiguração de Objetos\n-----------------------\n\nA classe [[yii\\base\\BaseObject|BaseObject]] introduz uma maneira uniforme de configurar\nobjetos. Qualquer classe descendente de [[yii\\base\\BaseObject|BaseObject]] deveria\ndeclarar seu construtor (se necessário) da seguinte maneira, para que ela\nseja configurada adequadamente:\n\n```php\nclass MinhaClasse extends \\yii\\base\\BaseObject\n{\n    public function __construct($param1, $param2, $config = [])\n    {\n        // ... inicialização antes da configuração ser aplicada\n\n        parent::__construct($config);\n    }\n\n    public function init()\n    {\n        parent::init();\n\n        // ... inicialização depois da configuração ser aplicada\n    }\n}\n```\n\nNo código acima, o último parâmetro do construtor deve receber um array de\nconfiguração que contém pares de nome-valor para a inicialização das propriedades\nno final do construtor. Você pode sobrescrever o método [[yii\\base\\BaseObject::init()|init()]]\npara fazer o trabalho de inicialização que deve ser feito após a configuração\nter sido aplicada.\n\nSeguindo esta convenção, você poderá criar e configurar novos objetos usando um\narray de configuração:\n\n```php\n$object = Yii::createObject([\n    'class' => 'MinhaClasse',\n    'property1' => 'abc',\n    'property2' => 'cde',\n], [$param1, $param2]);\n```\n\nMais detalhes sobre configurações podem ser encontrados na seção de\n[Configurações](concept-configurations.md).\n\n\nEventos\n-------\n\nNo Yii 1 os eventos eram criados definindo-se um método `on`-alguma-coisa\n(por exemplo, `onBeforeSave`). No Yii 2 você pode usar qualquer nome de\nevento. Você dispara um evento chamando o método\n[[yii\\base\\Component::trigger()|trigger()]]:\n\n```php\n$evento = new \\yii\\base\\Event;\n$componente->trigger($nomeDoEvento, $evento);\n```\n\nPara anexar um ouvinte (handler) a um evento, use o método [[yii\\base\\Component::on()|on()]]:\n\n```php\n$componente->on($nomeDoEvento, $handler);\n// Para desanexar o handler, utilize:\n// $componente->off($nomeDoEvento, $handler);\n```\n\nHá muitas melhorias nas funcionalidades de evento. Para mais detalhes,\npor favor, consulte a seção [Eventos](concept-events.md).\n\n\nPath Aliases\n------------\n\nO Yii 2.0 expande o uso de *path aliases* (apelidos de caminhos) tanto para caminhos de arquivos e diretórios como para URLs. Agora ele requer que um nome de alias\ncomece com o caractere `@` para diferenciar entre aliases e caminhos e URLs normais de arquivos e diretórios. Por exemplo, o alias `@yii` se refere ao diretório de\ninstalação do Yii. Os path aliases são suportados na maior porte do código do core\ndo Yii. Por exemplo, o método [[yii\\caching\\FileCache::cachePath]] pode receber\ntanto um path alias quanto um caminho de diretório normal.\n\nUm path alias também está intimamente relacionado a um namespace de classe.\nÉ recomendado que um path alias seja definido para cada namespace raiz, desta forma\npermitindo que você use o auto-carregamento de classes do Yii sem qualquer\nconfiguração adicional. Por exemplo, como `@yii` se refere ao diretório de\ninstalação do Yii, uma classe como `yii\\web\\Request` pode ser carregada\nautomaticamente. Se você utilizar uma biblioteca de terceiros, tal como o Zend\nFramework, você pode definir um path alias `@Zend` que se refere ao diretório\nde instalação desse framework. Uma vez que você tenha feito isso, o Yii também\npoderá carregar automaticamente qualquer classe nessa biblioteca do Zend Framework.\n\nVocê pode encontrar mais informações sobre *path aliases* na seção [Aliases](concept-aliases.md).\n\n\nViews (Visões)\n--------------\n\nA mudança mais significante das views no Yii 2 é que a variável especial `$this`\nem uma view não se refere mais ao controller ou widget atual. Ao invés disso, `$this` agora se refer[ a um objeto **view**, um novo conceito\nintroduzido no 2.0. O objeto *view* é do tipo [[yii\\web\\View]] e representa a\nparte da visão do padrão MVC. Se você quiser acessar o controller ou\no widget em uma visão, você pode utilizar `$this->context`.\n\nPara renderizar uma view parcial (partial view) dentro de outra view, você usa\n`$this->render()`, e não `$this->renderPartial()`. Agora a chamada de `render`\ntambém precisa ser explicitamente impressa com *echo*, uma vez que o métood\n`render()` retorna o resultado da renderização ao invés de exibi-lo diretamente.\nPor exemplo:\n\n```php\necho $this->render('_item', ['item' => $item]);\n```\n\nAlém de utilizar o PHP como linguagem de template principal, o Yii 2.0 também\né equipado com suporte oficial a duas populares engines de template: Smarty e\nTwig. A engine de template do Prado não é mais suportada. Para utilizar essas\nengines de template, você precisa configurar o componente de aplicação `view`\ndefinindo a propriedade [[yii\\base\\View::$renderers|View::$renderers]]. Por favor\nconsulte a seção [Template Engines](tutorial-template-engines.md) para mais\ndetalhes.\n\n\nModels (Modelos)\n----------------\n\nO Yii 2.0 usa o [[yii\\base\\Model]] como base, semelhante à `CModel` no 1.1.\nA classe `CFormModel` foi removida inteiramente. Ao invés dela, no Yii 2 você\ndeve estender a classe [[yii\\base\\Model]] parar criar uma classe de model de formulário.\n\nO Yii 2.0 introduz um novo método chamado [[yii\\base\\Model::scenarios()|scenarios()]]\npara declarar os cenários suportados, para indicar sob qual cenário um atributo\nprecisa ser validado ou pode ser considerado safe (seguro) ou não, etc. Por exemplo:\n\n```php\npublic function scenarios()\n{\n    return [\n        'backend' => ['email', 'cargo'],\n        'frontend' => ['email', '!cargo'],\n    ];\n}\n```\n\nNo código acima, dois cenários são declarados: `backend` e `frontend`. Para o\ncenário `backend`, os atributos `email` e `cargo` são seguros (safe) e podem ser\natribuídos em massa. Para o cenário `frontend`, `email` pode ser atribuído em\nmassa enquanto `cargo` não. Tanto `email` quanto `role` devem ser validados utilizando-se\n*rules* (regras).\n\nO método [[yii\\base\\Model::rules()|rules()]] ainda é usado para declarar regras\nde validação. Perceba que devido à introdução do método [[yii\\base\\Model::scenarios()|scenarios()]],\nnão existe mais o validador `unsafe` (inseguro).\n\nNa maioria dos casos, você não precisa sobrescrever [[yii\\base\\Model::scenarios()|scenarios()]]\nse o método [[yii\\base\\Model::rules()|rules()]] especifica completamente os\ncenários que existirão, e se não houver necessidade para declarar atributos\n`unsafe`.\n\nPara aprender mais sobre models, por favor consulte a seção [Models (Modelos)](basic-models.md).\n\n\nControllers (Controladores)\n---------------------------\n\nO Yii 2.0 utiliza a [[yii\\web\\Controller]] como classe base dos controllers de maneira semelhante à `CWebController` no Yii 1.1. A\n[[yii\\base\\Action]] é a classe base para classes de actions (ações).\n\nO impacto mais óbvio destas mudanças em seu código é que uma action de um controller\ndeve sempre retornar o conteúdo que você quer renderizar ao invés de dar *echo* nele:\n\n```php\npublic function actionView($id)\n{\n    $model = \\app\\models\\Post::findOne($id);\n    if ($model) {\n        return $this->render('exibir', ['model' => $model]);\n    } else {\n        throw new \\yii\\web\\NotFoundHttpException;\n    }\n}\n```\n\nPor favor, consulte a seção [Controllers (Controladores)](structure-controllers.md) para mais detalhes.\n\n\nWidgets\n-------\n\nO Yii 2.0 usa [[yii\\base\\Widget]] como a classe base dos widgets, de maneira\nsemelhante à `CWidget` no Yii 1.1.\n\nPara obter um melhor suporte ao framework nas IDEs, o Yii 2.0 introduz uma nova\nsintaxe para utilização de widgets. Os métodos estáticos [[yii\\base\\Widget::begin()|begin()]],\n[[yii\\base\\Widget::end()|end()]] e [[yii\\base\\Widget::widget()|widget()]] foram\nintroduzidos, para serem utilizados do seguinte modo:\n\n```php\nuse yii\\widgets\\Menu;\nuse yii\\widgets\\ActiveForm;\n\n// Note que você tem que dar um \"echo\" no resultado para exibi-lo\necho Menu::widget(['items' => $items]);\n\n// Passando um array para inicializar as propriedades do objeto\n$form = ActiveForm::begin([\n    'options' => ['class' => 'form-horizontal'],\n    'fieldConfig' => ['inputOptions' => ['class' => 'input-xlarge']],\n]);\n... campos do formulário aqui ...\nActiveForm::end();\n```\n\nPor favor, consulte a seção [Widgets](structure-widgets.md) para mais detalhes.\n\n\nTemas\n-----\n\nOs temas funcionam de maneira completamente diferente no 2.0. Agora eles se baseiam\nem um mecanismo de mapeamento de caminhos que mapeia um caminho de arquivo de view fonte\na um caminho de arquivo de view com o tema. Por exemplo, se o mapa de caminho de um tema\né `['/web/views' => '/web/themes/basic']`, então a versão com tema deste arquivo\nde view `/web/views/site/index.php` será `/web/themes/basic/site/index.php`.\nPor esse motivo, os temas agora podem ser aplicados a qualquer arquivo de view,\naté mesmo uma view renderizada fora do contexto de um controller ou widget.\n\nAlém disso, não há mais um componente `CThemeManager`. Em vez disso, `theme` é\numa propriedade configurável do componente `view` da aplicação.\n\nPor favor, consulte a seção [Temas](tutorial-theming.md) para mais detalhes.\n\n\nAplicações de Console\n---------------------\n\nAs aplicações de console agora são organizadas como controllers\nassim como as aplicações web. Os controllers de console devem estender de [[yii\\console\\Controller]],\nde maneira semelhante à `CConsoleCommand` no 1.1.\n\nPara rodar um comando do console, use `yii <rota>`, onde `<rota>` representa a rota de\num controller (por exemplo, `sitemap/index`). Argumentos anônimos adicionais são\npassados como parâmetros à action correspondente no controller, enquanto\nargumentos com nome são \"convertidos\" de acordo com as declarações em [[yii\\console\\Controller::options()]].\n\nO Yii 2.0 suporta a geração automática de informação de ajuda do comando a partir de blocos de comentários.\n\nPor favor consulte a seção [Comandos de Console](tutorial-console.md) para mais detalhes.\n\n\nI18N\n----\n\nO Yii 2.0 remove os formatadores de data e número embutidos em favor do módulo\n[intl do PECL do PHP](https://pecl.php.net/package/intl).\n\nA tradução de mensagens agora é realizada pelo componente `i18n` da aplicação.\nEste componente gerencia um conjunto de fontes de mensagens, o  que permite a você usar diferentes fontes de mensagens baseadas em categorias de mensagens.\n\nPor favor consulte a seção [Internacionalização](tutorial-i18n.md) para mais detalhes.\n\n\nAction Filters (Filtros de Ação)\n--------------------------------\n\nAgora os filtros de ação (action filters) são implementados via comportamentos (behaviors).\nPara definir um novo filtro personalizado, estenda de [[yii\\base\\ActionFilter]].\nPara usar um filtro, anexe a classe do filtro ao controller como um behavior. Por exemplo, para usar o filtro [[yii\\filters\\AccessControl]],\nvocê teria o seguinte código em um controller:\n\n```php\npublic function behaviors()\n{\n    return [\n        'access' => [\n            'class' => 'yii\\filters\\AccessControl',\n            'rules' => [\n                [\n                    'allow' => true,\n                    'actions' => ['admin'],\n                    'roles' => ['@']\n                ],\n            ],\n        ],\n    ];\n}\n```\n\nPor favor, consulte a seção [Filtragem](structure-filters.md) para mais detalhes.\n\n\nAssets\n------\n\nO Yii 2.0 introduz um novo conceito chamado de *asset bundle* (pacote de recursos estáticos)\nque substitui o conceito de script packages (pacotes de script) encontrado no Yii 1.1.\n\nUm *asset bundle* é uma coleção de arquivos de assets (por exemplo, arquivos JavaScript,\narquivos CSS, arquivos de imagens, etc.) dentro de um diretório. Cada *asset bundle*\né representado por uma classe que estende [[yii\\web\\AssetBundle]]. Ao registrar\num *asset bundle* via [[yii\\web\\AssetBundle::register()]], você torna os assets\ndeste pacote acessíveis via Web. Ao contrário do Yii 1, a página que registra o\n*bundle* automaticamente conterá as referências aos arquivos JavaScript e CSS\nespecificados naquele *bundle*.\n\nPor favor consulte a seção [Gerenciando Assets](output-assets.md) para mais detalhes.\n\n\nHelpers - Classes Auxiliares\n-------\n\nO Yii 2.0 introduz muitas classes auxiliares (helpers) estáticas comumente usadas, incluindo:\n\n* [[yii\\helpers\\Html]]\n* [[yii\\helpers\\ArrayHelper]]\n* [[yii\\helpers\\StringHelper]]\n* [[yii\\helpers\\FileHelper]]\n* [[yii\\helpers\\Json]]\n\nPor favor, consulte a seção [Visão Geral](helper-overview.md) dos helpers para mais detalhes.\n\nForms\n-----\n\nO Yii 2.0 introduz o conceito de campos (*fields*) para a construção de\nformulários usando [[yii\\widgets\\ActiveForm]]. Um *field* é um container\nconsistindo de um *label*, um *input*, uma mensagem de erro, e/ou um texto\nde ajuda. Um *field* é representado como um objeto [[yii\\widgets\\ActiveField|ActiveField]].\nUsando *fields* você pode construir um formulário de maneira mais limpa do que antes:\n\n```php\n<?php $form = yii\\widgets\\ActiveForm::begin(); ?>\n    <?= $form->field($model, 'login') ?>\n    <?= $form->field($model, 'senha')->passwordInput() ?>\n    <div class=\"form-group\">\n        <?= Html::submitButton('Entrar') ?>\n    </div>\n<?php yii\\widgets\\ActiveForm::end(); ?>\n```\n\nPor favor, consulte a seção [Criando um Formulário](input-forms.md) para mais detalhes.\n\n\nQuery Builder (Construtor de Consultas)\n---------------------------------------\n\nNo 1.1, a construção de consultas estava espalhada por diversas classes, incluindo\na `CDbCommand`, a `CDbCriteria` e a `CDbCommandBuilder`. O Yii 2.0 representa uma\nconsulta do banco de dados em termos de um objeto [[yii\\db\\Query|Query]] que pode\nser convertido em uma instrução SQL com a ajuda do [[yii\\db\\QueryBuilder|QueryBuilder]]\nque está por trás das cortinas.\nPor exemplo:\n\n```php\n$query = new \\yii\\db\\Query();\n$query->select('id, nome')\n      ->from('usuario')\n      ->limit(10);\n\n$command = $query->createCommand();\n$sql = $command->sql;\n$rows = $command->queryAll();\n```\n\nE o melhor de tudo, estes métodos de construção de consultas também podem ser utilizados ao trabalhar com o [Active Record](db-active-record.md).\n\nPor favor, consulte a seção [Query Builder](db-query-builder.md) para mais detalhes.\n\n\nActive Record\n-------------\n\nO Yii 2.0 introduz várias mudanças ao [Active Record](db-active-record.md). As\nduas mais óbvias envolvem a construção de consultas simples e o tratamento de consultas relacionais.\n\nA classe `CDbCriteria` do 1.1 foi substituída pela [[yii\\db\\ActiveQuery]] do Yii 2.\nEssa classe estende de [[yii\\db\\Query]], e assim herda todos os métodos de\nconstrução de consultas. Você chama [[yii\\db\\ActiveRecord::find()]] para começar a construir uma consulta:\n\n```php\n// Para obter todos os clientes *ativos* e ordená-los pelo ID:\n$customers = Cliente::find()\n    ->where(['status' => $ativo])\n    ->orderBy('id')\n    ->all();\n```\n\nPara declarar um relacionamento, simplesmente defina um método getter que retorne\num objeto [[yii\\db\\ActiveQuery|ActiveQuery]]. O nome da propriedade definida pelo\ngetter representa o nome do relacionamento. Por exemplo, o código a seguir\ndeclara um relacionamento `pedidos` (no 1.1, você teria que declarar as relações\nem um local central, `relations()`):\n\n```php\nclass Cliente extends \\yii\\db\\ActiveRecord\n{\n    public function getPedidos()\n    {\n        return $this->hasMany('Pedido', ['id_cliente' => 'id']);\n    }\n}\n```\n\nAgora você pode usar `$cliente->pedidos` para acessar os pedidos de um cliente a partir da tabela relacionada.\nVocê também pode usar o código a seguir para realizar uma consulta relacional imediata (*on-the-fly*) com uma condição personalizada:\n\n```php\n$pedidos = $cliente->getPedidos()->andWhere('status=1')->all();\n```\n\nAo fazer o eager loading (carregamento antecipado) de um relacionamento,\no Yii 2.0 faz isso de maneira diferente do 1.1. Em particular, no 1.1 uma consulta\nJOIN seria criada para selecionar tanto o registro primário quanto os de\nrelacionamentos. No Yii 2.0, duas instruções SQL são executadas sem usar JOIN:\na primeira instrução retorna os registros primários e a segunda retorna os registros\nrelacionados por filtrar pelas chaves primárias dos registros primários.\n\nEm vez de retornar objetos [[yii\\db\\ActiveRecord|ActiveRecord]], você pode\nencadear o método [[yii\\db\\ActiveQuery::asArray()|asArray()]] ao construir uma\nconsulta para retornar um grande número de registros. Isso fará com que o resultado\nda consulta retorne como arrays, o que pode reduzir significativamente o tempo\nde CPU e memória necessários para um grande número de registros. Por exemplo,\n\n```php\n$clientes = Cliente::find()->asArray()->all();\n```\n\nOutra mudança é que você não pode mais definir valores padrão de atributos através\nde propriedades públicas. Se você precisar disso, você deve defini-las no método\ninit na classe do seu registro.\n\n```php\npublic function init()\n{\n    parent::init();\n    $this->status = self::STATUS_NOVO;\n}\n```\n\nHavia alguns problemas ao sobrescrever o construtor de uma classe ActiveRecord\nno 1.1. Estes não ocorrem mais na versão 2.0. Perceba que ao adicionar parâmetros\nao construtor você pode ter que sobrescrever o método [[yii\\db\\ActiveRecord::instantiate()]].\n\nExistem muitas outras mudanças e melhorias no Active Record. Por favor, consulte\na seção [Active Record](db-active-record.md) para mais detalhes.\n\n\nComportamentos (Behaviors) do Active Record\n-----------------------\n\nNo Yii 2, removemos a classe base de behaviors `CActiveRecordBehavior`. Se você quer criar um Active Record Behavior, você terá que estender diretamente de `yii\\base\\Behavior`. Se a classe behavior class precisa responder a algums eventos da classe que a possui, você deve sobrescrever o método `events()` conforme a seguir:\n\n```php\nnamespace app\\components;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\base\\Behavior;\n\nclass MeuComportamento extends Behavior\n{\n    // ...\n\n    public function events()\n    {\n        return [\n            ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',\n        ];\n    }\n\n    public function beforeValidate($event)\n    {\n        // ...\n    }\n}\n```\n\n\nUser e IdentityInterface\n------------------------\n\nA classe `CWebUser` do 1.1 foi substituída pela [[yii\\web\\User]] e não há\nmais a classe `CUserIdentity`. Em vez disso, você deve implementar a interface\n[[yii\\web\\IdentityInterface]] que é muito mais simples de usar. O template avançado\nde projetos fornece um exemplo de como fazer isso.\n\nPor favor, consulte as seções [Autenticação](security-authentication.md),\n[Autorização](security-authorization.md) e [Template Avançado de Projetos](tutorial-advanced-app.md)\npara mais detalhes.\n\n\nGerenciamento de URLs\n---------------------\n\nO gerenciamento de URLs no Yii 2 é semelhante ao do 1.1. Uma grande melhoria é que o gerenciamento de URLs agora suporta parâmetros opcionais. Por exemplo,\nse você tiver uma regra declarada como a seguir, ela vai corresponder tanto a `post/popular` quanto a `post/1/popular`. No 1.1, você teria que usar duas\nregrar para fazer isso.\n\n```php\n[\n    'pattern' => 'post/<page:\\d+>/<tag>',\n    'route' => 'post/index',\n    'defaults' => ['page' => 1],\n]\n```\n\nPor favor, consulte a seção [Roteamento e Criação de URL](runtime-routing.md) para mais detalhes.\n\nUma importante mudança nas convenções de nomes para rotas é que actions e controllers com nomes em estilo camel case agora, quando referenciados em rotas, são convertidos para minúsculas separando cada palavra com um hífen. Por exemplo, o ID de controller `GestaoDeClientesController` deve ser referenciado em uma rota como `gestao-de-clientes`.\n\nVeja as seções [IDs de Controllers](structure-controllers.md#controller-ids) e [IDs de Actions](structure-controllers.md#action-ids) para mais detalhes.\n\n\nUtilizando o Yii 1.1 e o 2.x juntos\n-----------------------------------\n\nSe você tem código legado do Yii 1.1 que você quer utilizar com o Yii 2.0, por favor, consulte a seção [Usando Yii 1.1 e 2.0 juntos](tutorial-yii-integration.md).\n"
  },
  {
    "path": "docs/guide-pt-BR/intro-yii.md",
    "content": "O que é o Yii\n=============\n\nYii é um framework PHP de alta performance baseado em componentes para desenvolvimento rápido de aplicações web modernas.\nO nome Yii (pronunciado `ii`) significa \"simples e evolutivo\" em chinês. Ele também pode ser considerado um acrônimo de **Yes It Is** (*Sim, ele é*)!\n\n\nYii é melhor para que tipo de aplicações?\n------------------------\n\nYii é um framework de programação web genérico, o que significa que ele pode\nser usado para o desenvolvimento de todo tipo de aplicações web usando PHP.\nPor causa de sua arquitetura baseada em componentes e suporte sofisticado a\ncaching, ele é especialmente adequado para o desenvolvimento de aplicações de\nlarga escala como portais, fóruns, sistemas de gerenciamento de conteúdo (CMS),\nprojetos de e-commerce, Web services RESTful e assim por diante.\n\n\nComo o Yii se Compara a Outros Frameworks?\n------------------------------------------\n\nSe já estiver familiarizado com um outro framework, você pode gostar de saber como o Yii se compara:\n\n- Como a maioria dos frameworks PHP, o Yii implementa o padrão de arquitetura MVC\n  (Modelo-Visão-Controlador) e promove a organização do código baseada nesse padrão.\n- Yii tem a filosofia de que o código deveria ser escrito de uma maneira simples,\n  porém elegante. O Yii nunca vai tentar exagerar no projeto só para seguir estritamente algum padrão de projeto.\n- Yii é um framework completo fornecendo muitas funcionalidades comprovadas\n  e prontas para o uso, tais como: construtores de consultas (query builders) e\n  ActiveRecord tanto para bancos de dados relacionais quanto para NoSQL; suporte ao\n  desenvolvimento de APIs RESTful; suporte a caching de múltiplas camadas; e mais.\n- Yii é extremamente extensível. Você pode personalizá-lo ou substituir quase\n  todas as partes do código central (core). Você também pode tirar vantagem de sua\n  sólida arquitetura de extensões para utilizar ou desenvolver extensões\n  que podem ser redistribuídas.\n- Alta performance é sempre um objetivo principal do Yii.\n\nYii não é um show de um homem só, ele é apoiado por uma [forte equipe de desenvolvedores do código central (core)][yii_team]\nbem como por uma ampla comunidade de profissionais constantemente\ncontribuindo com o desenvolvimento do Yii. A equipe de desenvolvedores do Yii\nacompanha de perto às últimas tendências do desenvolvimento Web e as\nmelhores práticas e funcionalidades encontradas em outros frameworks e projetos.\nAs mais relevantes e melhores práticas e características encontradas em outros lugares\nsão incorporadas regularmente no core do framework e expostas via interfaces\nsimples e elegantes.\n\n[yii_team]: https://www.yiiframework.com/team\n\nVersões do Yii\n--------------\n\nAtualmente, o Yii tem duas versões principais disponíveis: a 1.1 e a 2.0. A Versão\n1.1 é a antiga geração e agora está em modo de manutenção. A versão 2.0 é uma\nreescrita completa do Yii, adotando as tecnologias e protocolos mais recentes, incluindo Composer, PSR, namespaces, traits, e assim por diante. A versão 2.0 representa\na geração atual do framework e receberá os nossos esforços principais de\ndesenvolvimento nos próximos anos. Este guia trata principalmente da versão 2.0.\n\n\nRequisitos e Pré-requisitos\n---------------------------\n\nYii 2.0 requer PHP 7.4.0 ou superior. Você pode encontrar requisitos mais\ndetalhados para recursos específicos executando o verificador de requisitos\n(requirement checker) incluído em cada lançamento do Yii.\n\nUtilizar o Yii requer conhecimentos básicos sobre programação orientada a objetos\n(OOP), uma vez que o Yii é um framework puramente OOP.\nO Yii 2.0 também utiliza as funcionalides mais recentes do PHP, tais como [namespaces](https://www.php.net/manual/pt_BR/language.namespaces.php) e [traits](https://www.php.net/manual/pt_BR/language.oop5.traits.php). Compreender esses conceitos lhe ajudará a entender mais facilmente o Yii 2.0.\n\n"
  },
  {
    "path": "docs/guide-pt-BR/output-data-providers.md",
    "content": "Data Providers (Provedores de Dados)\n==============\n\nNas seções [Paginação](output-pagination.md) e [Ordenação](output-sorting.md), descrevemos como os usuários finais podem escolher uma determinada página de dados para exibir e ordená-los por determinadas colunas. Uma vez que esta tarefa de paginação e ordenação de dados é muito comum, o Yii fornece um conjunto de classes *data provider* para encapsular estes recursos.\n\nUm data provider é uma classe que implementa\n[[yii\\data\\DataProviderInterface]]. Ele suporta principalmente a recuperação de dados paginados e ordenados. Geralmente é usado para trabalhar com [widgets de dados](output-data-widgets.md) de modo que os usuários finais possam interativamente paginar e ordenar dados.\n\nO Yii fornece as seguintes classes de data provider:\n\n* [[yii\\data\\ActiveDataProvider]]: Utilize [[yii\\db\\Query]] ou [[yii\\db\\ActiveQuery]] para consultar dados de um database e retorná-los na forma de array ou uma instância de [Active Record](db-active-record.md).\n* [[yii\\data\\SqlDataProvider]]: executa uma instrução SQL e retorna os dados do banco de dados como array.\n* [[yii\\data\\ArrayDataProvider]]: pega um grande array e retorna apenas uma parte deste baseado na paginação e ordenação especificada.\n\nO uso de todos estes data providers compartilham o seguinte padrão comum:\n\n```php\n// cria o data provider configurando suas propriedades de paginação e ordenação\n$provider = new XyzDataProvider([\n  'pagination' => [...],\n  'sort' => [...],\n]);\n\n// recupera dados paginados e ordenados\n$models = $provider->getModels();\n\n// obtém o número de itens de dados na página atual\n$count = $provider->getCount();\n\n// obtém o número total de itens de dados de todas as páginas\n$totalCount = $provider->getTotalCount();\n```\n\nVocê define o comportamento da paginação e da ordenação do data provider configurando suas propriedades [[yii\\data\\BaseDataProvider::pagination|pagination]] e [[yii\\data\\BaseDataProvider::sort|sort]] que correspondem às configurações [[yii\\data\\Pagination]] e [[yii\\data\\Sort]] respectivamente. Você também pode configurá-los como `false` para desativar os recursos de paginação e/ou ordenação.\n\nOs [widgets de dados](output-data-widgets.md), assim como [[yii\\grid\\GridView]], tem uma propriedade chamada `dataProvider` que pode receber uma instância de data provider e exibir os dados que ele fornece. Por exemplo:\n\n```php\necho yii\\grid\\GridView::widget([\n  'dataProvider' => $dataProvider,\n]);\n```\n\nEstes data providers variam principalmente conforme a fonte de dados é especificada. Nas subseções seguintes, vamos explicar o uso detalhado de cada um dos data providers.\n\n\n## Active Data Provider <span id=\"active-data-provider\"></span>\n\nPara usar [[yii\\data\\ActiveDataProvider]], você deve configurar sua propriedade [[yii\\data\\ActiveDataProvider::query|query]].\nEle pode receber qualquer um dos objetos [[yii\\db\\Query]] ou [[yii\\db\\ActiveQuery]]. Se for o primeiro, os dados serão retornados em array; se for o último, os dados podem ser retornados em array ou uma instância de [Active Record](db-active-record.md).\nPor exemplo:\n\n```php\nuse yii\\data\\ActiveDataProvider;\n\n$query = Post::find()->where(['status' => 1]);\n\n$provider = new ActiveDataProvider([\n  'query' => $query,\n  'pagination' => [\n      'pageSize' => 10,\n  ],\n  'sort' => [\n      'defaultOrder' => [\n          'created_at' => SORT_DESC,\n          'title' => SORT_ASC,\n      ]\n  ],\n]);\n\n// retorna um array de objetos Post\n$posts = $provider->getModels();\n```\n\nSe `$query` no exemplo acima fosse criada usando o código a seguir, então o data provider retornaria um array.\n\n```php\nuse yii\\db\\Query;\n\n$query = (new Query())->from('post')->where(['status' => 1]);\n```\n\n> Observação: Se uma query já especificou a cláusula `orderBy, as novas instruções de ordenação dadas por usuários finais (através da configuração `sort`) será acrescentada a cláusula `orderBy` existente. Existindo qualquer uma das cláusulas `limit` e `offset` será substituído pelo request de paginação dos usuários finais (através da configuração `pagination`).\n\nPor padrão, [[yii\\data\\ActiveDataProvider]] utiliza o componente da aplicação `db` como a conexão de banco de dados. Você pode usar uma conexão de banco de dados diferente, configurando a propriedade [[yii\\data\\ActiveDataProvider::db]].\n\n\n## SQL Data Provider <span id=\"sql-data-provider\"></span>\n\nO [[yii\\data\\SqlDataProvider]] trabalha com uma instrução SQL, que é usado para obter os dados necessários. Com base nas especificações de [[yii\\data\\SqlDataProvider::sort|sort]] e\n[[yii\\data\\SqlDataProvider::pagination|pagination]], o provider ajustará as cláusulas `ORDER BY` e `LIMIT` da instrução SQL em conformidade para buscar somente a página de dados solicitada na ordem desejada.\n\nPara usar [[yii\\data\\SqlDataProvider]], você deve especificar a propriedade [[yii\\data\\SqlDataProvider::sql|sql]] bem como a propriedade [[yii\\data\\SqlDataProvider::totalCount|totalCount]]. Por exemplo:\n\n```php\nuse yii\\data\\SqlDataProvider;\n\n$count = Yii::$app->db->createCommand('\n  SELECT COUNT(*) FROM post WHERE status=:status\n', [':status' => 1])->queryScalar();\n\n$provider = new SqlDataProvider([\n  'sql' => 'SELECT * FROM post WHERE status=:status',\n  'params' => [':status' => 1],\n  'totalCount' => $count,\n  'pagination' => [\n      'pageSize' => 10,\n  ],\n  'sort' => [\n      'attributes' => [\n          'title',\n          'view_count',\n          'created_at',\n      ],\n  ],\n]);\n\n// retorna um array de linha de dados\n$models = $provider->getModels();\n```\n\n> Observação: A propriedade [[yii\\data\\SqlDataProvider::totalCount|totalCount]] é requerida somente se você precisar paginar os dados. Isto porque a instrução SQL definida por [[yii\\data\\SqlDataProvider::sql|sql]] será modificada pelo provider para retornar somente a página atual de dados solicitada. O provider ainda precisa saber o número total de dados a fim de calcular corretamente o número de páginas disponíveis.\n\n\n## Array Data Provider <span id=\"array-data-provider\"></span>\n\nO [[yii\\data\\ArrayDataProvider]] é melhor usado quando se trabalha com um grande array. O provider permite-lhe retornar uma página dos dados do array ordenados por uma ou várias colunas. Para usar [[yii\\data\\ArrayDataProvider]], você precisa especificar a propriedade [[yii\\data\\ArrayDataProvider::allModels|allModels]] como um grande array. Elementos deste array podem ser outros arrays associados (por exemplo, resultados de uma query do [DAO](db-dao.md)) ou objetos (por exemplo, uma instância do [Active Record](db-active-record.md)).\nPor exemplo:\n\n```php\nuse yii\\data\\ArrayDataProvider;\n\n$data = [\n  ['id' => 1, 'name' => 'name 1', ...],\n  ['id' => 2, 'name' => 'name 2', ...],\n  ...\n  ['id' => 100, 'name' => 'name 100', ...],\n];\n\n$provider = new ArrayDataProvider([\n  'allModels' => $data,\n  'pagination' => [\n      'pageSize' => 10,\n  ],\n  'sort' => [\n      'attributes' => ['id', 'name'],\n  ],\n]);\n\n// obter as linhas na página corrente\n$rows = $provider->getModels();\n```\n\n> Observação: Comparando o [Active Data Provider](#active-data-provider) com o [SQL Data Provider](#sql-data-provider), o array data provider é menos eficiente porque requer o carregamento de *todo* os dados na memória.\n\n\n## Trabalhando com Chave de Dados <span id=\"working-with-keys\"></span>\n\nAo usar os itens de dados retornados por um data provider, muitas vezes você precisa identificar cada item de dados com uma chave única.\nPor exemplo, se os itens de dados representam as informações do cliente, você pode querer usar o ID do cliente como a chave para cada dado do cliente. Data providers podem retornar uma lista das tais chaves correspondentes aos itens de dados retornados por [[yii\\data\\DataProviderInterface::getModels()]]. Por exemplo:\n\n```php\nuse yii\\data\\ActiveDataProvider;\n\n$query = Post::find()->where(['status' => 1]);\n\n$provider = new ActiveDataProvider([\n  'query' => $query,\n]);\n\n// retorna uma array de objetos Post\n$posts = $provider->getModels();\n\n// retorna os valores de chave primária correspondente a $posts\n$ids = $provider->getKeys();\n```\n\nNo exemplo abaixo, como você fornece um objeto [[yii\\db\\ActiveQuery]] para o [[yii\\data\\ActiveDataProvider]], ele é inteligente o suficiente para retornar os valores de chave primária como chaves no resultado. Você também pode especificar explicitamente como os valores das chaves devem ser calculados configurando a propriedade\n[[yii\\data\\ActiveDataProvider::key]] com um nome de coluna ou com uma função callback que retorna os valores das chaves. Por exemplo:\n\n```php\n// usa a coluna \"slug\" como valor da chave\n$provider = new ActiveDataProvider([\n  'query' => Post::find(),\n  'key' => 'slug',\n]);\n\n// usa o resultados do md5(id) como valor da chave\n$provider = new ActiveDataProvider([\n  'query' => Post::find(),\n  'key' => function ($model) {\n      return md5($model->id);\n  }\n]);\n```\n\n\n## Criado Data Provider Personalizado <span id=\"custom-data-provider\"></span>\n\nPara criar sua própria classe de data provider personalizada, você deve implementar o [[yii\\data\\DataProviderInterface]].\nUm caminho fácil é estender de [[yii\\data\\BaseDataProvider]], o que lhe permite concentrar-se na lógica principal do data provider. Em particular, você precisa principalmente implementar os seguintes métodos:\n                                                 \n- [[yii\\data\\BaseDataProvider::prepareModels()|prepareModels()]]: prepara o data models que será disponibilizado na página atual e as retorna como um array.\n- [[yii\\data\\BaseDataProvider::prepareKeys()|prepareKeys()]]: recebe um array de data models disponíveis e retorna chaves que lhes estão associados.\n- [[yii\\data\\BaseDataProvider::prepareTotalCount()|prepareTotalCount]]: retorna um valor que indica o número total de data models no data provider.\n\nAbaixo está um exemplo de um data provider que lê dados em CSV eficientemente:\n\n```php\n<?php\nuse yii\\data\\BaseDataProvider;\n\nclass CsvDataProvider extends BaseDataProvider\n{\n  /**\n   * @var string nome do arquivo CSV que será lido\n   */\n  public $filename;\n  \n  /**\n   * @var string|nome da coluna chave ou função que a retorne\n   */\n  public $key;\n  \n  /**\n   * @var SplFileObject\n   */\n  protected $fileObject; // SplFileObject é muito conveniente para procurar uma linha específica em um arquivo\n  \n  /**\n   * {@inheritdoc}\n   */\n  public function init()\n  {\n      parent::init();\n      \n      // abre o arquivo\n      $this->fileObject = new SplFileObject($this->filename);\n  }\n\n  /**\n   * {@inheritdoc}\n   */\n  protected function prepareModels()\n  {\n      $models = [];\n      $pagination = $this->getPagination();\n      if ($pagination === false) {\n          // no caso não há paginação, lê todas as linhas\n          while (!$this->fileObject->eof()) {\n              $models[] = $this->fileObject->fgetcsv();\n              $this->fileObject->next();\n          }\n      } else {\n          // no caso existe paginação, lê somente uma página\n          $pagination->totalCount = $this->getTotalCount();\n          $this->fileObject->seek($pagination->getOffset());\n          $limit = $pagination->getLimit();\n          for ($count = 0; $count < $limit; ++$count) {\n              $models[] = $this->fileObject->fgetcsv();\n              $this->fileObject->next();\n          }\n      }\n      return $models;\n  }\n\n  /**\n   * {@inheritdoc}\n   */\n  protected function prepareKeys($models)\n  {\n      if ($this->key !== null) {\n          $keys = [];\n          foreach ($models as $model) {\n              if (is_string($this->key)) {\n                  $keys[] = $model[$this->key];\n              } else {\n                  $keys[] = call_user_func($this->key, $model);\n              }\n          }\n          return $keys;\n      } else {\n          return array_keys($models);\n      }\n  }\n\n  /**\n   * {@inheritdoc}\n   */\n  protected function prepareTotalCount()\n  {\n      $count = 0;\n      while (!$this->fileObject->eof()) {\n          $this->fileObject->next();\n          ++$count;\n      }\n      return $count;\n  }\n}\n```\n\n\n"
  },
  {
    "path": "docs/guide-pt-BR/output-pagination.md",
    "content": "Paginação\n==========\n\nQuando existem muitos dados para serem exibidos em uma única página, uma estratégia comum é mostrá-los em várias páginas e em cada página exibir uma porção pequena dos dados. Esta estratégia é conhecida como *paginação*. O Yii usa o objeto [[yii\\data\\Pagination]] para representar as informações sobre um esquema de paginação. Em particular, \n\n* [[yii\\data\\Pagination::$totalCount|contagem total]] especifica o número total de itens de dados. Note que este é geralmente muito maior do que o número de itens de dados necessários para exibir em uma única página.\n* [[yii\\data\\Pagination::$pageSize|quantidade por página]] especifica quantos itens cada página contém. O padrão é 20.\n* [[yii\\data\\Pagination::$page|página atual]] retorna a página corrente (baseada em zero). O valor padrão é 0, ou seja, a primeira página.\n\nCom o objeto [[yii\\data\\Pagination]] totalmente especificado, você pode recuperar e exibir dados parcialmente. Por exemplo, se você está buscando dados a partir de um banco de dados, você pode especificar as cláusulas `OFFSET` e `LIMIT` da query com os valores correspondentes fornecidos pela paginação. Abaixo está um exemplo, \n\n```php\n\nuse yii\\data\\Pagination;\n\n// Cria uma query para pegar todos os artigos com status = 1\n\n$query = Article::find()->where(['status' => 1]);\n\n// pega o total de artigos (mas não baixa os dados ainda)\n\n$count = $query->count();\n\n// cria um objeto pagination com o total em $count\n\n$pagination = new Pagination(['totalCount' => $count]);\n\n// Lima a query usando a paginação e recupera os artigos\n\n$articles = $query->offset($pagination->offset)\n\n    ->limit($pagination->limit)\n\n    ->all();\n\n```\n\nQual página de artigos será devolvido no exemplo acima? Depende se um parâmetro da query chamado `page` for fornecido. Por padrão, a paginação \ntentará definir a [[yii\\data\\Pagination::$page|página atual]] com o  valor do parâmetro `page`. Se o parâmetro não for fornecido, então o padrão será 0.\nPara facilitar a construção de um elemento UI que suporta a paginação, Yii fornece o widget [[yii\\widgets\\LinkPager]] que exibe uma lista de botões de página na qual os usuários podem clicar para indicar qual a página de dados deve ser exibido. O widget recebe um objeto de paginação para que ele saiba qual é a sua página corrente e quantas botões de páginas devem ser exibido. Por exemplo,\n\n```php\n\nuse yii\\widgets\\LinkPager;\n\necho LinkPager::widget([\n\n    'pagination' => $pagination,\n\n]);\n\n```\n\nSe você quer construir elemento UI manualmente, você pode utilizar [[yii\\data\\Pagination::createUrl()]] para criar URLs que conduziria a diferentes páginas. O método requer um parâmetro página e criará um formatado apropriado de URL Contendo o parâmetro página. Por exemplo,\n\n```php\n\n// especifica a rota que o URL a ser criada deve usar\n\n// Se você não a especificar, a atual rota requerida será usado\n\n$pagination->route = 'article/index';\n\n// exibe: /index.php?r=article/index&page=100\n\necho $pagination->createUrl(100);\n\n// exibe: /index.php?r=article/index&page=101\n\necho $pagination->createUrl(101);\n\n```\n\n> Dica: Você pode personalizar o nome do parâmetro de consulta `page` configurando a propriedade [[yii\\data\\Pagination::pageParam|pageParam]] ao criar o objeto de paginação.\n"
  },
  {
    "path": "docs/guide-pt-BR/output-sorting.md",
    "content": "Ordenação\n=======\n\nAo exibir várias linhas de dados, muitas vezes é necessário que os dados sejam ordenados de acordo com algumas colunas especificadas pelos usuários finais. O Yii utiliza um objeto [[yii\\data\\Sort]] para representar as informações sobre um esquema de ordenação. Em particular, \n\n* [[yii\\data\\Sort::$attributes|attributes]] especifica os *atributos* através dos quais os dados podem ser ordenados.\n  Um atributo pode ser simples como um [atributo do model](structure-models.md#attributes). Ele também pode ser um composto por uma combinação de múltiplos atributos de model ou colunas do DB. Mais detalhes serão mostrados logo a seguir.\n* [[yii\\data\\Sort::$attributeOrders|attributeOrders]] dá as instruções de ordenação requisitadas para cada atributo.\n* [[yii\\data\\Sort::$orders|orders]] dá a direção da ordenação das colunas.\n\nPara usar [[yii\\data\\Sort]], primeiro declare quais atributos podem ser ordenados. Em seguida, pegue a requisição com as informações de ordenação através de\n[[yii\\data\\Sort::$attributeOrders|attributeOrders]] ou [[yii\\data\\Sort::$orders|orders]]\nE use-os para personalizar a consulta de dados. Por exemplo:\n\n```php\nuse yii\\data\\Sort;\n\n$sort = new Sort([\n    'attributes' => [\n        'age',\n        'name' => [\n            'asc' => ['first_name' => SORT_ASC, 'last_name' => SORT_ASC],\n            'desc' => ['first_name' => SORT_DESC, 'last_name' => SORT_DESC],\n            'default' => SORT_DESC,\n            'label' => 'Name',\n        ],\n    ],\n]);\n\n$articles = Article::find()\n    ->where(['status' => 1])\n    ->orderBy($sort->orders)\n    ->all();\n```\n\nNo exemplo abaixo, dois atributos são declarados para o objeto [[yii\\data\\Sort|Sort]]: `age` e `name`. \n\nO atributo `age` é um atributo *simples* que corresponde ao atributo `age` da classe Active Record `Article`. É equivalente a seguinte declaração:\n\n```php\n'age' => [\n    'asc' => ['age' => SORT_ASC],\n    'desc' => ['age' => SORT_DESC],\n    'default' => SORT_ASC,\n    'label' => Inflector::camel2words('age'),\n]\n```\n\nO atributo `name` é um atributo *composto* definido por `first_name` e `last_name` de `Article`. Declara-se com a seguinte estrutura de array:\n\n- Os elementos `asc` e `desc` determina a direção da ordenação dos atributos em ascendente ou descendente respectivamente. Seus valores representam as colunas e as direções pelas quais os dados devem ser classificados. Você pode especificar uma ou várias colunas para indicar uma ordenação simples ou composta.\n- O elemento `default` especifica a direção pela qual o atributo deve ser ordenado quando requisitado. O padrão é a ordem crescente, ou seja, se a ordenação não for definida previamente e você pedir para ordenar por esse atributo, os dados serão ordenados por esse atributo em ordem crescente.\n- O elemento `label` especifica o rótulo deve ser usado quando executar [[yii\\data\\Sort::link()]] para criar um link de ordenação. \nSe não for definida, [[yii\\helpers\\Inflector::camel2words()]] será chamado para gerar um rótulo do nome do atributo.\nPerceba que não será HTML-encoded.\n\n> Observação: Você pode alimentar diretamente o valor de [[yii\\data\\Sort::$orders|orders]] para a consulta do banco de dados para implementar a sua cláusula `ORDER BY`. Não utilize [[yii\\data\\Sort::$attributeOrders|attributeOrders]] porque alguns dos atributos podem ser compostos e não podem ser reconhecidos pela consulta do banco de dados.\n\nVocê pode chamar [[yii\\data\\Sort::link()]] para gerar um hyperlink em que os usuários finais podem clicar para solicitar a ordenação dos dados pelo atributo especificado. Você também pode chamar [[yii\\data\\Sort::createUrl()]] para criar um URL ordenáveis.\nPor exemplo:\n\n```php\n// especifica a rota que a URL a ser criada deve usar\n// Se você não especificar isso, a atual rota requisitada será utilizada \n$sort->route = 'article/index';\n\n// exibe links direcionando a ordenação por ‘name‘ e ‘age‘, respectivamente\necho $sort->link('name') . ' | ' . $sort->link('age');\n\n// exibe: /index.php?r=article/index&sort=age\necho $sort->createUrl('age');\n```\n\nO [[yii\\data\\Sort]] verifica o parâmetro `sort` da consulta para determinar quais atributos estão sendo requisitados para ordenação.\nVocê pode especificar uma ordenação padrão através de [[yii\\data\\Sort::defaultOrder]] quando o parâmetro de consulta não está fornecido.\nVocê também pode personalizar o nome do parâmetro de consulta configurando  propriedade [[yii\\data\\Sort::sortParam|sortParam]].\n"
  },
  {
    "path": "docs/guide-pt-BR/output-theming.md",
    "content": "Temas\n=====\n \nTema é uma forma de substituir um conjunto de [views](structure-views.md) por outras, sem a necessidade de tocar no código de renderização de view original. Você pode usar tema para alterar sistematicamente a aparência de uma aplicação.\n \nPara usar tema, você deve configurar a propriedade [[yii\\base\\View::theme|theme]] da `view (visão)` da aplicação.\nA propriedade configura um objeto [[yii\\base\\Theme]] que rege a forma como os arquivos de views serão substituídos. Você deve principalmente especificar as seguintes propriedades de [[yii\\base\\Theme]]:\n \n- [[yii\\base\\Theme::basePath]]: determina o diretório de base que contém os recursos temáticos (CSS, JS, images, etc.)\n- [[yii\\base\\Theme::baseUrl]]: determina a URL base dos recursos temáticos.\n- [[yii\\base\\Theme::pathMap]]: determina as regras de substituição dos arquivos de view. Mais detalhes serão mostradas nas subseções logo a seguir.\n \nPor exemplo, se você chama `$this->render('about')` no `SiteController`, você estará renderizando a view\n`@app/views/site/about.php`. Todavia, se você habilitar tema na seguinte configuração da aplicação, a view `@app/themes/basic/site/about.php` será renderizada, no lugar da primeira.\n \n```php\nreturn [\n    'components' => [\n        'view' => [\n            'theme' => [\n                'basePath' => '@app/themes/basic',\n                'baseUrl' => '@web/themes/basic',\n                'pathMap' => [\n                    '@app/views' => '@app/themes/basic',\n                ],\n            ],\n        ],\n    ],\n];\n```\n \n> Observação: Aliases de caminhos são suportados por temas. Ao fazer substituição de view, aliases de caminho serão transformados nos caminhos ou URLs reais.\n \nVocê pode acessar o objeto [[yii\\base\\Theme]] através da propriedade [[yii\\base\\View::theme]]. Por exemplo, na view, você pode escrever o seguinte código, pois `$this` refere-se ao objeto view:\n \n```php\n$theme = $this->theme;\n \n// retorno: $theme->baseUrl . '/img/logo.gif'\n$url = $theme->getUrl('img/logo.gif');\n \n// retorno: $theme->basePath . '/img/logo.gif'\n$file = $theme->getPath('img/logo.gif');\n```\n \nA propriedade [[yii\\base\\Theme::pathMap]] rege como a view deve ser substituída. É preciso um array de pares de valores-chave, onde as chaves são os caminhos originais da view que serão substituídos e os valores são os caminhos dos temas correspondentes. A substituição é baseada na correspondência parcial: Se um caminho de view inicia com alguma chave no array [[yii\\base\\Theme::pathMap|pathMap]], a parte correspondente será substituída pelo valor do array.\nUsando o exemplo de configuração acima,\n`@app/views/site/about.php` corresponde parcialmente a chave\n`@app/views`, ele será substituído por `@app/themes/basic/site/about.php`.\n \n \n## Tema de Módulos <span id=\"theming-modules\"></span>\n \nA fim de configurar temas por módulos, [[yii\\base\\Theme::pathMap]] pode ser configurado da seguinte forma:\n \n```php\n'pathMap' => [\n    '@app/views' => '@app/themes/basic',\n    '@app/modules' => '@app/themes/basic/modules', // <-- !!!\n],\n```\n \nIsto lhe permitirá tematizar `@app/modules/blog/views/comment/index.php` com `@app/themes/basic/modules/blog/views/comment/index.php`.\n \n \n## Tema de Widgets <span id=\"theming-widgets\"></span>\n \nA fim de configurar temas por widgets, você pode configurar [[yii\\base\\Theme::pathMap]] da seguinte forma:\n \n```php\n'pathMap' => [\n    '@app/views' => '@app/themes/basic',\n    '@app/widgets' => '@app/themes/basic/widgets', // <-- !!!\n],\n```\n \nIsto lhe permitirá tematizar `@app/widgets/currency/views/index.php` com `@app/themes/basic/widgets/currency/views/index.php`.\n \n \n## Herança de Tema <span id=\"theme-inheritance\"></span>\n \nAlgumas vezes você pode querer definir um tema que contém um visual básico da aplicação, e em seguida, com base em algum feriado, você pode querer variar o visual levemente. Você pode atingir este objetivo usando herança de tema que é feito através do mapeamento de um único caminho de view para múltiplos alvos. Por exemplo:\n \n```php\n'pathMap' => [\n    '@app/views' => [\n        '@app/themes/christmas',\n        '@app/themes/basic',\n    ],\n]\n```\n \nNeste caso, a view `@app/views/site/index.php` seria tematizada tanto como `@app/themes/christmas/site/index.php` ou\n`@app/themes/basic/site/index.php`, dependendo de qual arquivo de tema existir. Se os dois arquivos existirem, o primeiro terá precedência. Na prática, você iria manter mais arquivos de temas em `@app/themes/basic` e personalizar alguns deles em `@app/themes/christmas`.\n\n"
  },
  {
    "path": "docs/guide-pt-BR/rest-authentication.md",
    "content": "Autenticação\n==============\n\nAo contrário de aplicações Web, APIs RESTful são geralmente stateless, o que significa que as sessões ou os cookies não devem ser utilizados. Portanto, cada requisição deve vir com algum tipo de credencial de autenticação pois o estado de autenticação do usuário não pode ser mantido por sessões ou cookies. Uma prática comum é enviar um token de acesso secreto com cada solicitação para autenticar o usuário. Uma vez que um token de acesso pode ser utilizado para identificar de forma exclusiva e autenticar um usuário. **Solicitações de API devem sempre ser enviadas via HTTPS para evitar ataques man-in-the-middle (MitM)**.\n\nExistem diferentes maneiras de enviar um token de acesso:\n\n* [Autenticação Básica HTTP](https://en.wikipedia.org/wiki/Basic_access_authentication): o token de acesso é enviado como um nome de usuário. Isso só deve ser usado quando um token de acesso puder ser armazenado com segurança no lado do consumidor da API. Por exemplo, o consumidor API é um programa executado em um servidor.\n* Parâmetro de consulta da URL: o token de acesso é enviado como um parâmetro de consulta na URL da API, ex., `https://example.com/users?access-token=xxxxxxxx`. Como a maioria dos servidores Web manterão os parâmetros de consulta nos logs do servidor, esta abordagem deve ser utilizada principalmente para servir requisições `JSONP` que não pode usar cabeçalhos HTTP para enviar tokens de acesso.\n* [OAuth 2](https://oauth.net/2/): o token de acesso é obtido pelo consumidor a partir de um servidor de autorização e enviado para o servidor da API via [HTTP Bearer Tokens] (https://datatracker.ietf.org/doc/html/rfc6750),  de acordo com o protocolo OAuth2.\n\nYii suporta todos os métodos de autenticação descritos acima. Você também pode criar facilmente um novo método de autenticação.\n\nPara ativar a autenticação nas suas APIs, siga os seguintes passos:\n\n1. Configure o [componente de aplicação](structure-application-components.md) `user`:\n  - Defina a propriedade [[yii\\web\\User::enableSession|enableSession]] como `false`.\n  - Defina a propriedade [[yii\\web\\User::loginUrl|loginUrl]] como `null` para mostrar o erro HTTP 403 em vez de redirecionar para a página de login. \n2. Especificar quais métodos de autenticação você planeja usar configurando o behavior `authenticator` na sua classe controller REST.\n3. Implemente [[yii\\web\\IdentityInterface::findIdentityByAccessToken()]] na sua [[yii\\web\\User::identityClass|classe de identidade do usuário]].\n\nPasso 1 não é obrigatório, mas é recomendado para APIs RESTful stateless. Quando [[yii\\web\\User::enableSession|enableSession]] está marcado como falso, o status de autenticação de usuário NÃO será mantido entre as requisições usando sessões. Em lugar disso, autenticação será realizada para cada requisição, que é realizado no passo 2 e 3.\n\n> Dica: Você pode configurar [[yii\\web\\User::enableSession|enableSession]] do componente `user`\n> nas configurações da aplicação se você estiver desenvolvendo APIs RESTful para sua aplicação. Se você desenvolver\n> APIs RESTful como um módulo, você pode colocar a seguinte linha no método `init()` do módulo, conforme exemplo a seguir:\n>\n> ```php\n> public function init()\n> {\n>     parent::init();\n>     \\Yii::$app->user->enableSession = false;\n> }\n> ```\n\nPor exemplo, para usar autenticação HTTP básica, você pode configurar o behavior `authenticator` como o seguinte:\n\n```php\nuse yii\\filters\\auth\\HttpBasicAuth;\n\npublic function behaviors()\n{\n   $behaviors = parent::behaviors();\n   $behaviors['authenticator'] = [\n       'class' => HttpBasicAuth::class,\n   ];\n   return $behaviors;\n}\n```\n\nSe você quiser dar suporte a todos os três métodos de autenticação explicado acima, você pode utilizar o `CompositeAuth` conforme mostrado a seguir:\n\n```php\nuse yii\\filters\\auth\\CompositeAuth;\nuse yii\\filters\\auth\\HttpBasicAuth;\nuse yii\\filters\\auth\\HttpBearerAuth;\nuse yii\\filters\\auth\\QueryParamAuth;\n\npublic function behaviors()\n{\n   $behaviors = parent::behaviors();\n   $behaviors['authenticator'] = [\n       'class' => CompositeAuth::class,\n       'authMethods' => [\n           HttpBasicAuth::class,\n           HttpBearerAuth::class,\n           QueryParamAuth::class,\n       ],\n   ];\n   return $behaviors;\n}\n```\n\nCada elemento em `authMethods` deve ser o nome de uma classe de método de autenticação ou um array de configuração.\n\n\nImplementação de `findIdentityByAccessToken()` é específico por aplicação. Por exemplo, em cenários simples quando cada usuário só pode ter um token de acesso, você pode armazenar o token de acesso em uma coluna `access_token` na tabela `user`. O método pode então ser facilmente implementado na classe `User` como o seguinte:\n\n```php\nuse yii\\db\\ActiveRecord;\nuse yii\\web\\IdentityInterface;\n\nclass User extends ActiveRecord implements IdentityInterface\n{\n   public static function findIdentityByAccessToken($token, $type = null)\n   {\n       return static::findOne(['access_token' => $token]);\n   }\n}\n```\n\nApós a autenticação ser ativada, conforme descrito acima, para todas as requisições da API, o controller requisitado irá tentar autenticar o usuário no passo `beforeAction()`.\n\nSe a autenticação retornar com sucesso, o controller irá executar outras verificações (tais como limitação de taxa, autorização) e então executará a ação. As informações de identidade do usuário autenticado podem ser recuperadas através de `Yii::$app->user->identity`.\n\nSe a autenticação falhar, uma resposta  HTTP com status 401 será enviado de volta junto com outros cabeçalhos apropriados (tal como um `WWW-Authenticate` cabeçalho HTTP para Autenticação Básica).\n\n\n## Autorização <span id=\"authorization\"></span>\n\nApós um usuário se autenticar, você provavelmente vai querer verificar se ele ou ela tem a permissão para executar a ação do recurso solicitado. Este processo é chamado de *autorização* que é tratada em pormenor na seção de [Autorização](security-authorization.md).\n\nSe o seu controller estende de [[yii\\rest\\ActiveController]], você pode sobrescrever o método [[yii\\rest\\Controller::checkAccess()|checkAccess()]] para executar a verificação de autorização. O método será chamado pelas ações incorporadas fornecidas pelo [[yii\\rest\\ActiveController]].\n\n"
  },
  {
    "path": "docs/guide-pt-BR/rest-controllers.md",
    "content": "Controllers (Controladores)\n===========\n\nDepois de criar as classes de recursos e especificar como os dados de recursos devem ser formatados, a próxima coisa a fazer é criar ações do controller para expor os recursos para os usuários finais através das APIs RESTful.\n\nO Yii fornece duas classes básicas de controller para simplificar seu trabalho de criar ações RESTful: [[yii\\rest\\Controller]] e [[yii\\rest\\ActiveController]]. A diferença entre os dois controllers é que o último fornece um conjunto padrão de ações que são especificamente concebidos para lidar com recursos do [Active Record](db-active-record.md). Então, se você estiver usando [Active Record](db-active-record.md) e está confortável com as ações fornecidas, você pode considerar estender suas classes de controller de [[yii\\rest\\ActiveController]], que permitirá criar poderosas APIs RESTful com um mínimo de código.\n\nAmbas classes [[yii\\rest\\Controller]] e [[yii\\rest\\ActiveController]] fornecem os seguintes recursos, algumas das quais serão descritas em detalhes nas próximas seções:\n\n* Validação de Método HTTP;\n* [Negociação de conteúdo e formatação de dados](rest-response-formatting.md);\n* [Autenticação](rest-authentication.md);\n* [Limitação de taxa](rest-rate-limiting.md).\n\nO [[yii\\rest\\ActiveController]] oferece também os seguintes recursos:\n\n* Um conjunto de ações comumente necessárias: `index`, `view`, `create`, `update`, `delete`, `options`;\n* Autorização do usuário em relação à ação solicitada e recursos.\n\n\n## Criando Classes Controller <span id=\"creating-controller\"></span>\n\nAo criar uma nova classe de controller, uma convenção na nomenclatura da classe é usar o nome do tipo de recurso no singular. Por exemplo, para disponibilizar as informações do usuário, o controlador pode ser nomeado como `UserController`. Criar uma nova ação é semelhante à criação de uma ação de uma aplicação Web. A única diferença é que em vez de renderizar o resultado usando uma view e chamando o método `render()`, para ações RESTful você retorna diretamente os dados. O [[yii\\rest\\Controller::serializer|serializer]] e o [[yii\\web\\Response|objeto response]] vão converter os dados originais para o formato solicitado. Por exemplo:\n\n```php\npublic function actionView($id)\n{\n   return User::findOne($id);\n}\n```\n\n\n## Filtros <span id=\"filters\"></span>\n\nA maioria dos recursos da API RESTful fornecidos por [[yii\\rest\\Controller]] são implementadas por [filtros](structure-filters.md).\nEm particular, os seguintes filtros serão executados na ordem em que estão listados:\n\n* [[yii\\filters\\ContentNegotiator|contentNegotiator]]: suporta a negociação de conteúdo, a ser explicado na seção [Formatação de Resposta](rest-response-formatting.md);\n* [[yii\\filters\\VerbFilter|verbFilter]]: suporta validação de métodos HTTP;\n* [[yii\\filters\\auth\\AuthMethod|authenticator]]: suporta autenticação de usuários, que será explicado na seção [Autenticação](rest-authentication.md);\n* [[yii\\filters\\RateLimiter|rateLimiter]]: suporta limitação de taxa, que será explicado na seção\n [Limitação de taxa](rest-rate-limiting.md).\n\nEstes filtros são declarados no método [[yii\\rest\\Controller::behaviors()|behaviors()]].\nVocê pode sobrescrever esse método para configurar alguns filtros, desativar outros, ou adicionar seus próprios filtros. Por exemplo, se você precisar somente de autenticação básica de HTTP, poderá utilizar o seguinte código:\n\n```php\nuse yii\\filters\\auth\\HttpBasicAuth;\n\npublic function behaviors()\n{\n   $behaviors = parent::behaviors();\n   $behaviors['authenticator'] = [\n       'class' => HttpBasicAuth::class,\n   ];\n   return $behaviors;\n}\n```\n\n\n## Estendendo `ActiveController` <span id=\"extending-active-controller\"></span>\n\nSe a sua classe controller estende de [[yii\\rest\\ActiveController]], você deve configurar a propriedade [[yii\\rest\\ActiveController::modelClass|modelClass]] para ser o nome da classe de recurso que você pretende servir através deste controller. A classe deve estender de [[yii\\db\\ActiveRecord]].\n\n\n### Customizando Ações <span id=\"customizing-actions\"></span>\n\nPor padrão, o [[yii\\rest\\ActiveController]] fornece as seguintes ações:\n\n* [[yii\\rest\\IndexAction|index]]: recursos de lista página por página;\n* [[yii\\rest\\ViewAction|view]]: retorna os detalhes de um recurso especificado;\n* [[yii\\rest\\CreateAction|create]]: cria um novo recurso;\n* [[yii\\rest\\UpdateAction|update]]: atualiza um recurso existente;\n* [[yii\\rest\\DeleteAction|delete]]: excluir o recurso especificado;\n* [[yii\\rest\\OptionsAction|options]]: retorna os métodos HTTP suportados.\n\nTodas essas ações são declaradas através do método [[yii\\rest\\ActiveController::actions()|actions()]]. Você pode configurar essas ações ou desativar algumas delas, sobrescrevendo o método `actions()`, como mostrado a seguir:\n\n```php\npublic function actions()\n{\n   $actions = parent::actions();\n\n   // desabilita as ações \"delete\" e \"create\"\n   unset($actions['delete'], $actions['create']);\n\n   // customiza a preparação do  data provider com o método \"prepareDataProvider()\"\n   $actions['index']['prepareDataProvider'] = [$this, 'prepareDataProvider'];\n\n   return $actions;\n}\n\npublic function prepareDataProvider()\n{\n   // preparar e retornar um data provider para a ação \"index\"\n}\n```\n\nPor favor, consulte as referências de classe para classes de ação individual para saber as opções de configuração que estão disponíveis.\n\n\n### Executando Verificação de Acesso <span id=\"performing-access-check\"></span>\n\nAo disponibilizar recursos por meio de APIs RESTful, muitas vezes você precisa verificar se o usuário atual tem permissão para acessar e manipular o(s) recurso(s) solicitado(s). Com o [[yii\\rest\\ActiveController]], isso pode ser feito sobrescrevendo o método [[yii\\rest\\ActiveController::checkAccess()|checkAccess()]]  conforme a seguir:\n\n```php\n/**\n* Verifica os privilégios do usuário corrente.\n*\n* Este método deve ser sobrescrito para verificar se o usuário atual tem o privilégio\n* para executar a ação especificada diante do modelo de dados especificado.\n* se o usuário não tiver acesso, uma [[ForbiddenHttpException]] deve ser lançada.\n*\n* @param string $action o ID da ação a ser executada\n* @param \\yii\\base\\Model $model o model a ser acessado. Se `null`, isso significa que nenhum model específico está sendo acessado.\n* @param array $params parâmetros adicionais\n* @throws ForbiddenHttpException se o usuário não tiver acesso\n*/\npublic function checkAccess($action, $model = null, $params = [])\n{\n   // verifica se o usuário pode acessar $action and $model\n   // lança a ForbiddenHttpException se o acesso for negado\n   if ($action === 'update' || $action === 'delete') {\n        if ($model->author_id !== \\Yii::$app->user->id)\n            throw new \\yii\\web\\ForbiddenHttpException(sprintf('You can only %s articles that you\\'ve created.', $action));\n    }\n}\n```\n\nO método `checkAccess()` será chamado pelas ações padrões do [[yii\\rest\\ActiveController]]. Se você criar novas ações e também desejar executar a verificação de acesso, deve chamar esse método explicitamente nas novas ações.\n\n> Dica: Você pode implementar `checkAccess()` usando o [componente de Role-Based Access Control (RBAC)](security-authorization.md).\n\n"
  },
  {
    "path": "docs/guide-pt-BR/rest-error-handling.md",
    "content": "Tratamento de Erros\n==============\n\nAo manusear uma requisição da API RESTful, se existir um erro na requisição do usuário ou se alguma coisa inesperada acontecer no servidor, você pode simplesmente lançar uma exceção para notificar o usuário de que algo deu errado.\nSe você puder identificar a causa do erro (ex., o recurso requisitado não existe), você deve considerar lançar uma exceção juntamente com um código de status HTTP adequado (ex., [[yii\\web\\NotFoundHttpException]] representa um código de status 404). O Yii enviará a resposta juntamente com o código e o texto do status HTTP correspondente. O Yii também incluirá a representação serializada da exceção no corpo da resposta. Por exemplo:\n\n```\nHTTP/1.1 404 Not Found\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n   \"name\": \"Not Found Exception\",\n   \"message\": \"The requested resource was not found.\",\n   \"code\": 0,\n   \"status\": 404\n}\n```\n\nA lista a seguir descrimina os códigos de status HTTP que são usados pelo framework REST do Yii:\n\n* `200`: OK. Tudo funcionou conforme o esperado;\n* `201`: Um recurso foi criado com êxito em resposta a uma requisição `POST`. O cabeçalho `location` contém a URL que aponta para o recurso recém-criado;\n* `204`: A requisição foi tratada com sucesso e a resposta não contém nenhum conteúdo no corpo (por exemplo uma requisição `DELETE`);\n* `304`: O recurso não foi modificado. Você pode usar a versão em cache;\n* `400`: Requisição malfeita. Isto pode ser causado por várias ações por parte do usuário, tais como o fornecimento de um JSON inválido no corpo da requisição, fornecendo parâmetros inválidos, etc;\n* `401`: Falha de autenticação;\n* `403`: O usuário autenticado não tem permissão para acessar o recurso da API solicitado;\n* `404`: O recurso requisitado não existe;\n* `405`: Método não permitido. Favor verificar o cabeçalho `Allow` para conhecer os métodos HTTP permitidos;\n* `415`: Tipo de mídia não suportada. O número de versão ou o content type requisitado são inválidos;\n* `422`: Falha na validação dos dados (na resposta a uma requisição `POST`, por exemplo). Por favor, verifique o corpo da resposta para visualizar a mensagem detalhada do erro;\n* `429`: Excesso de requisições. A requisição foi rejeitada devido a limitação de taxa;\n* `500`: Erro interno do servidor. Isto pode ser causado por erros internos do programa.\n\n## Customizando Resposta de Erro<span id=\"customizing-error-response\"></span>\n\nÀs vezes você pode querer personalizar o formato de resposta de erro padrão. Por exemplo, em vez de confiar em usar diferentes status HTTP para indicar os diversos erros, você pode querer usar sempre o status 200 como resposta e colocar o código de status real como parte da estrutura JSON da resposta, como mostrado abaixo,\n\n```\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n   \"success\": false,\n   \"data\": {\n       \"name\": \"Not Found Exception\",\n       \"message\": \"The requested resource was not found.\",\n       \"code\": 0,\n       \"status\": 404\n   }\n}\n```\n\nPara atingir este objetivo, você pode responder o evento `beforeSend` do componente `response` na configuração da aplicação:\n\n```php\nreturn [\n   // ...\n   'components' => [\n       'response' => [\n           'class' => 'yii\\web\\Response',\n           'on beforeSend' => function ($event) {\n               $response = $event->sender;\n               if ($response->data !== null && Yii::$app->request->get('suppress_response_code')) {\n                   $response->data = [\n                       'success' => $response->isSuccessful,\n                       'data' => $response->data,\n                   ];\n                   $response->statusCode = 200;\n               }\n           },\n       ],\n   ],\n];\n```\n\nO código acima formatará a resposta (para ambas as respostas, bem-sucedidas e com falha) como explicado quando `suppress_response_code` é passado como um parâmetro `GET`.\n\n\n"
  },
  {
    "path": "docs/guide-pt-BR/rest-quick-start.md",
    "content": "Introdução\n===========\n\nO Yii fornece um conjunto de ferramentas para simplificar a tarefa de implementar APIs RESTful Web Service. Em particular, o Yii suporta os seguintes recursos sobre APIs RESTful:\n\n* Prototipagem rápida com suporte para APIs comuns de [Active Record](db-active-record.md);\n* Negociação de formato do Response (suporte JSON e XML por padrão);\n* Serialização de objeto configurável com suporte a campos de saída selecionáveis;\n* Formatação adequada para a coleção e dados e validação de erros;\n* Suporte a [HATEOAS](https://en.wikipedia.org/wiki/HATEOAS);\n* Roteamento eficiente com verificação dos verbs (métodos) HTTP;\n* Construído com suporte aos métodos `OPTIONS` e `HEAD`;\n* Autenticação e autorização;\n* Data caching e HTTP caching;\n* Limitação de taxa;\n\n\nAbaixo, utilizamos um exemplo para ilustrar como você pode construir um conjunto de APIs RESTful com um mínimo de codificação.\n\nSuponha que você deseja expor os dados do usuário via APIs RESTful. Os dados do usuário estão guardados na tabela `user` e você já criou a classe [active record](db-active-record.md) `app\\models\\User` para acessar os dados do usuário.\n\n\n## Criando um Controller (Controlador) <span id=\"creating-controller\"></span>\n\nPrimeiramente, crie uma classe [controller](structure-controllers.md) `app\\controllers\\UserController` como a seguir,\n\n```php\nnamespace app\\controllers;\n\nuse yii\\rest\\ActiveController;\n\nclass UserController extends ActiveController\n{\n   public $modelClass = 'app\\models\\User';\n}\n```\n\nA classe controller estende de [[yii\\rest\\ActiveController]], que implementa um conjunto comum de ações RESTful. Especificando [[yii\\rest\\ActiveController::modelClass|modelClass]]\ncomo `app\\models\\User`, o controller sabe qual o model que pode ser usado para a recuperação e manipulação de dados.\n\n\n## Configurando Regras de URL <span id=\"configuring-url-rules\"></span>\n\n\nEm seguida, modifique a configuração do componente `urlManager` na configuração da aplicação:\n\n```php\n'urlManager' => [\n   'enablePrettyUrl' => true,\n   'enableStrictParsing' => true,\n   'showScriptName' => false,\n   'rules' => [\n       ['class' => 'yii\\rest\\UrlRule', 'controller' => 'user'],\n   ],\n]\n```\n\nA configuração acima primeiramente adiciona uma regra de URL para o controller `user` de modo que os dados do usuário podem ser acessados e manipulados com URLs amigáveis e métodos HTTP significativos.\n\n\n## Ativando o Input via JSON <span id=\"enabling-json-input\"></span>\n\nPara fazer a API aceitar dados no formato JSON, configure a propriedade [[yii\\web\\Request::$parsers|parsers]] do [componente de aplicação](structure-application-components.md) `request` para usar o [[yii\\web\\JsonParser]] para realizar input via JSON:\n\n```php\n'request' => [\n   'parsers' => [\n       'application/json' => 'yii\\web\\JsonParser',\n   ]\n]\n```\n\n> Observação: A configuração acima é opcional. Sem esta configuração, a API só iria reconhecer os formatos de input `application/x-www-form-urlencoded` e `multipart/form-data`.\n\n\n## Testando <span id=\"trying-it-out\"></span>\n\nCom o mínimo de esforço acima, você já terminou sua tarefa de criar as APIs RESTful para acessar os dados do usuário. As APIs que você criou incluem:\n\n* `GET /users`: listar todos os usuários página por página;\n* `HEAD /users`: mostrar a informações gerais da listagem de usuários;\n* `POST /users`: criar um novo usuário;\n* `GET /users/123`: retorna detalhes do usuário 123;\n* `HEAD /users/123`: mostra informações gerais do usuário 123;\n* `PATCH /users/123` e `PUT /users/123`: atualiza o usuário 123;\n* `DELETE /users/123`: deleta o usuário 123;\n* `OPTIONS /users`: mostra os métodos suportados em relação à URL `/users`;\n* `OPTIONS /users/123`: mostra os métodos suportados em relação à URL `/users/123`.\n\n> Observação: O Yii vai pluralizar automaticamente nomes de controllers para uso em URLs (também chamadas *endpoints*).\n> Você pode configurar isso usando a propriedade [[yii\\rest\\UrlRule::$pluralize]].\n\nVocê pode acessar suas APIs com o comando `curl` mostrado abaixo,\n\n```\n$ curl -i -H \"Accept:application/json\" \"http://localhost/users\"\n\nHTTP/1.1 200 OK\n...\nX-Pagination-Total-Count: 1000\nX-Pagination-Page-Count: 50\nX-Pagination-Current-Page: 1\nX-Pagination-Per-Page: 20\nLink: <http://localhost/users?page=1>; rel=self, \n     <http://localhost/users?page=2>; rel=next, \n     <http://localhost/users?page=50>; rel=last\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n[\n   {\n       \"id\": 1,\n       ...\n   },\n   {\n       \"id\": 2,\n       ...\n   },\n   ...\n]\n```\n\nTente alterar o tipo de conteúdo para `application/xml` e você vai ver o resultado retornado em formato XML:\n\n```\n$ curl -i -H \"Accept:application/xml\" \"http://localhost/users\"\n\nHTTP/1.1 200 OK\n...\nX-Pagination-Total-Count: 1000\nX-Pagination-Page-Count: 50\nX-Pagination-Current-Page: 1\nX-Pagination-Per-Page: 20\nLink: <http://localhost/users?page=1>; rel=self, \n     <http://localhost/users?page=2>; rel=next, \n     <http://localhost/users?page=50>; rel=last\nTransfer-Encoding: chunked\nContent-Type: application/xml\n\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<response>\n   <item>\n       <id>1</id>\n       ...\n   </item>\n   <item>\n       <id>2</id>\n       ...\n   </item>\n   ...\n</response>\n```\n\nO seguinte comando irá criar um novo usuário, enviando uma solicitação POST com os dados do usuário em formato JSON:\n\n```\n$ curl -i -H \"Accept:application/json\" -H \"Content-Type:application/json\" -XPOST \"http://localhost/users\" -d '{\"username\": \"example\", \"email\": \"user@example.com\"}'\n\nHTTP/1.1 201 Created\n...\nLocation: http://localhost/users/1\nContent-Length: 99\nContent-Type: application/json; charset=UTF-8\n\n{\"id\":1,\"username\":\"example\",\"email\":\"user@example.com\",\"created_at\":1414674789,\"updated_at\":1414674789}\n```\n\n> Dica: Você também pode acessar suas APIs via navegador, digitando a URL `http://localhost/users`. No entanto, você pode precisar de alguns plugins do navegador para enviar cabeçalhos de solicitações específicas.\n\nComo você pode ver, no cabeçalho da resposta, há informações sobre a contagem total, número de páginas, etc. Há também links que permitem navegar para outras páginas de dados. Por exemplo, `http://localhost/users?page=2` lhe daria a próxima página dos dados de usuário.\n\nUsando os parâmetros `fields` e `expand`, você também pode especificar os campos que devem ser incluídos no resultado. Por exemplo, a URL `http://localhost/users?fields=id,email` só retornará os campos `id` e `email`.\n\n\n> Observação: Você deve ter notado que o resultado de `http://localhost/users` \n> inclui alguns campos confidenciais,\n> Tal como `password_hash`, `auth_key`. Você certamente não quer que \n> eles apareçam no resultado da sua API.\n> Você pode e deve filtrar esses campos, conforme descrito na seção \n> [Response Formatting](rest-response-formatting.md).\n\n\n## Resumo <span id=\"summary\"></span>\n\nUsando o framework API RESTful do Yii, você implementa uma URL desses campos, conforme descrito na seção de ações do controller, um controller para organizar as ações que implementam as URLs para um único tipo de recurso.\n\nOs recursos são representados como modelos de dados, que se estendem a partir da classe [[yii\\base\\Model]]. Se você estiver trabalhando com bancos de dados (relacional ou NoSQL), é recomendado que você use [[yii\\db\\ActiveRecord|ActiveRecord]] para representar recursos.\n\nVocê pode usar [[yii\\rest\\UrlRule]] para simplificar o roteamento para suas URLs da API.\n\n\nEmbora não seja exigido, é recomendável que você desenvolva suas APIs RESTful  como uma aplicação separada, diferente do seu frontend e backend para facilitar a manutenção.\n\n"
  },
  {
    "path": "docs/guide-pt-BR/rest-rate-limiting.md",
    "content": "Limitador de Acesso\n=============\n\nPara prevenir abusos, você pode considerar a utilização de um *limitador de acesso* nas suas APIs. Por exemplo, você pode querer limitar o uso da API para cada usuário em no máximo 100 chamadas a cada 10 minutos. Se o número de solicitações recebidas por usuário ultrapassar este limite, uma resposta com status 429 (significa \"Muitas Requisições\") deve ser retornada.\n\nPara habilitar o limitador de acesso, a [[yii\\web\\User::identityClass|classe de identidade do usuário]] deve implementar [[yii\\filters\\RateLimitInterface]]. Esta interface requer a implementação de três métodos:\n\n* `getRateLimit()`: retorna o número máximo de pedidos permitidos e o período de tempo (ex., `[100, 600]` significa que pode haver, no máximo, 100 chamadas de API dentro de 600 segundo);\n* `loadAllowance()`: retorna o número restante de pedidos permitidos e a hora da última verificação;\n* `saveAllowance()`: salva tanto o número restante de requisições e a hora atual.\n\nVocê pode usar duas colunas na tabela de usuários para registrar estas informações. Com esses campos definidos, então `loadAllowance()` e `saveAllowance()` podem ser implementados para ler e guardar os valores das duas colunas correspondentes ao atual usuário autenticado.\nPara melhorar o desempenho, você também pode considerar armazenar essas informações em um cache ou armazenamento NoSQL.\n\nUma vez que a classe de identidade do usuário estiver com a interface necessária implementada, o Yii automaticamente usará a classe [[yii\\filters\\RateLimiter]] configurada como um filtro da ação para o [[yii\\rest\\Controller]] realizar a verificação da limitação do acesso. O limitador de acesso lançará uma exceção [[yii\\web\\TooManyRequestsHttpException]] quando o limite for excedido.\n\nVocê pode configurar o limitador de acesso da seguinte forma em suas classes controller REST:\n\n```php\npublic function behaviors()\n{\n  $behaviors =\nparent::behaviors();\n  $behaviors['rateLimiter']['enableRateLimitHeaders']\n= false;\n  return $behaviors;\n}\n```\n\nQuando o limitador de acesso está habilitado, por padrão a cada resposta será enviada com o seguinte cabeçalho HTTP contendo a informação da atual taxa de limitação:\n\n* `X-Rate-Limit-Limit`, o número máximo permitido de pedidos em um período de tempo;\n* `X-Rate-Limit-Remaining`, o número de pedidos restantes no período de tempo atual;\n* `X-Rate-Limit-Reset`, o número de segundos de espera a fim de obter o número máximo de pedidos permitidos.\n\nVocê pode desativar esses cabeçalhos, configurando [[yii\\filters\\RateLimiter::enableRateLimitHeaders]] para `false`, como mostrado no exemplo acima.\n\n\n"
  },
  {
    "path": "docs/guide-pt-BR/rest-resources.md",
    "content": "Recursos\n=========\n\nAPIs RESTful tratam de como acessar e manipular *recursos*. Você pode ver recursos como [models](structure-models.md) no paradigma MVC.\n\nEmbora não haja restrição na forma de representar um recurso, no Yii você normalmente representaria recursos como objetos de [[yii\\base\\Model]] ou de uma classe filha (ex. [[yii\\db\\ActiveRecord]]), pelas seguintes razões:\n\n* [[yii\\base\\Model]] implementa a interface [[yii\\base\\Arrayable]], que permite que você personalize como você deseja expor dados de recursos através das APIs RESTful.\n* [[yii\\base\\Model]] suporta [validação de dados de entrada](input-validation.md), que é importante se as suas APIs RESTful precisarem suportar entrada de dados.\n* [[yii\\db\\ActiveRecord]] fornece acesso poderoso a banco de dados com suporte a manipulação dos dados, o que o torna um ajuste perfeito se seus dados de recursos estiverem armazenado em bases de dados.\n\nNesta seção, vamos principalmente descrever como uma classe de recurso que se estende de [[yii\\base\\Model]] (ou alguma classe filha) pode especificar quais os dados podem ser retornados via APIs RESTful. Se a classe de recurso não estender de [[yii\\base\\Model]], então todas as suas variáveis públicas serão retornadas.\n\n\n## Campos <span id=\"fields\"></span>\n\nAo incluir um recurso em uma resposta da API RESTful, o recurso precisa ser serializado em uma string. O Yii quebra este processo em duas etapas. Primeiro, o recurso é convertido em um array utilizando [[yii\\rest\\Serializer]]. Por último, o array é serializado em uma string no formato solicitado (ex. JSON, XML) através do [[yii\\web\\ResponseFormatterInterface|response formatters]]. O primeiro passo é o que você deve centrar-se principalmente no desenvolvimento de uma classe de recurso.\n\nSobrescrevendo [[yii\\base\\Model::fields()|fields()]] e/ou [[yii\\base\\Model::extraFields()|extraFields()]], você pode especificar quais os dados, chamados *fields*, no recurso podem ser colocados no array.\nA diferença entre estes dois métodos é que o primeiro especifica o conjunto padrão de campos que devem ser incluídos no array, enquanto que o último especifica campos adicionais que podem ser incluídos no array, se um usuário final solicitá-los via o parâmetro de pesquisa `expand`. Por exemplo:\n\n```\n// retorna todos os campos declarados em fields()\nhttp://localhost/users\n\n// retorna apenas os campos id e email, desde que estejam declarados em fields()\nhttp://localhost/users?fields=id,email\n\n// retorna todos os campos de fields() e o campo profile se este estiver no extraFields()\nhttp://localhost/users?expand=profile\n\n// retorna apenas o campo id, email e profile, desde que estejam em fields() e extraFields()\nhttp://localhost/users?fields=id,email&expand=profile\n```\n\n\n### Sobrescrevendo `fields()` <span id=\"overriding-fields\"></span>\n\nPor padrão, [[yii\\base\\Model::fields()]] retorna todos os atributos do model como campos, enquanto [[yii\\db\\ActiveRecord::fields()]] só retorna os atributos que tenham sido preenchidos a partir do DB.\n\nVocê pode sobrescrever `fields()` para adicionar, remover, renomear ou redefinir campos. O valor do retorno de `fields()` deve ser um array. As chaves do array são os nomes dos campos e os valores são as definições dos campos correspondentes, que podem ser tanto nomes de propriedade/atributo ou funções anônimas retornando o valor do campo correspondente. No caso especial de um nome de um campo for o mesmo que sua definição de nome de atributo, você pode omitir a chave do array. Por exemplo:\n\n```php\n// explicitamente lista todos os campos, \n// melhor usado quando você quer ter certeza de que as alterações\n// na sua tabela ou atributo do model não causaram alterações\n// nos seus campos (Manter compatibilidade da API).\npublic function fields()\n{\n   return [\n       // Nome do campo é igual ao nome do atributo\n       'id',\n       // nome do campo é \"email\", o nome do atributo correspondente é  \"email_address\"\n       'email' => 'email_address',\n       // nome do campo é \"name\", seu valor é definido por um PHP callback\n       'name' => function ($model) {\n           return $model->first_name . ' ' . $model->last_name;\n       },\n   ];\n}\n\n// filtrar alguns campos, melhor usado quando você deseja herdar a implementação do pai\n// e deseja esconder alguns campos confidenciais.\npublic function fields()\n{\n   $fields = parent::fields();\n\n   // remove campos que contém informações confidenciais\n   unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);\n\n   return $fields;\n}\n```\n\n> Aviso: Como o padrão é ter todos os atributos de um model incluídos \n> no resultados da API, você deve examinar os seus dados para certificar-se de que \n> eles não contenham informações confidenciais. \n> Se existirem tais informações, você deve sobrescrever `fields()` para filtrá-los. \n> No exemplo acima, nós escolhemos filtrar `auth_key`, \n> `password_hash` e `password_reset_token`.\n\n\n### Sobrescrevendo `extraFields()` <span id=\"overriding-extra-fields\"></span>\n\nPor padrão, o [[yii\\base\\Model::extraFields()]] não retorna nada, enquanto o [[yii\\db\\ActiveRecord::extraFields()]] retorna os nomes das relações que foram populadas a partir do DB.\n\nO formato do retorno dos dados do `extraFields()` é o mesmo de `fields()`. Geralmente, `extraFields()` é mais usado para especificar os campos cujos valores são objetos. Por exemplo, dada a seguinte declaração de campo,\n\n```php\npublic function fields()\n{\n   return ['id', 'email'];\n}\n\npublic function extraFields()\n{\n   return ['profile'];\n}\n```\n\no request com `http://localhost/users?fields=id,email&expand=profile` pode retornar o seguinte dados em formato JSON:\n\n```php\n[\n   {\n       \"id\": 100,\n       \"email\": \"100@example.com\",\n       \"profile\": {\n           \"id\": 100,\n           \"age\": 30,\n       }\n   },\n   ...\n]\n```\n\n\n## Links <span id=\"links\"></span>\n\n[HATEOAS](https://en.wikipedia.org/wiki/HATEOAS) é uma abreviação de “Hypermedia as the Engine of Application State”, que promove as APIs Restfull retornarem informações para permitir aos clientes descobrirem quais ações são suportadas pelos recursos retornados. O sentido de HATEOAS é retornar um conjunto de hiperlinks em relação às informações quando os recursos de dados são servidos pelas APIs.\n\nSuas classes de recursos podem suportar HATEOAS implementando a interface [[yii\\web\\Linkable]]. Esta interface contém um único método [[yii\\web\\Linkable::getLinks()|getLinks()]] que deve retornar uma lista de [[yii\\web\\Link|links]].\nTipicamente, você deve retornar pelo menos o link `self` representando a URL para o mesmo objeto de recurso. Por exemplo:\n\n```php\nuse yii\\db\\ActiveRecord;\nuse yii\\web\\Link;\nuse yii\\web\\Linkable;\nuse yii\\helpers\\Url;\n\nclass User extends ActiveRecord implements Linkable\n{\n   public function getLinks()\n   {\n       return [\n           Link::REL_SELF => Url::to(['user/view', 'id' => $this->id], true),\n       ];\n   }\n}\n```\n\nQuando o objeto `User` for retornado em uma resposta, será composto de um elemento `_links` representando os links relacionados ao *user*, por exemplo:\n\n```\n{\n   \"id\": 100,\n   \"email\": \"user@example.com\",\n   // ...\n   \"_links\" => {\n       \"self\": {\n           \"href\": \"https://example.com/users/100\"\n       }\n   }\n}\n```\n\n\n## Collections (Coleções) <span id=\"collections\"></span>\n\nObjetos de recursos podem ser agrupados em *collections*. Cada collection contém uma lista de objetos de recurso do mesmo tipo.\n\nEmbora os collections podem ser representados como arrays, normalmente, é preferível representá-los como [data providers](output-data-providers.md). Isto porque data providers suportam ordenação e paginação de recursos, que é um recurso comumente necessário para APIs RESTful retornarem collections. Por exemplo, ação a seguir retorna um data provider sobre o recurso *post*:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\rest\\Controller;\nuse yii\\data\\ActiveDataProvider;\nuse app\\models\\Post;\n\nclass PostController extends Controller\n{\n   public function actionIndex()\n   {\n       return new ActiveDataProvider([\n           'query' => Post::find(),\n       ]);\n   }\n}\n```\n\nQuando um data provider está enviando uma resposta com a API RESTful, o [[yii\\rest\\Serializer]] pegará a página atual de recursos e a serializa como um array de objetos de recurso. Adicionalmente, o [[yii\\rest\\Serializer]] também incluirá as informações de paginação pelo seguinte cabeçalho HTTP:\n\n* `X-Pagination-Total-Count`: O número total de recursos;\n* `X-Pagination-Page-Count`: O número de páginas;\n* `X-Pagination-Current-Page`: A página atual (a primeira página é 1);\n* `X-Pagination-Per-Page`: O numero de recursos em cada página;\n* `Link`: Um conjunto de links de navegação, permitindo que o cliente percorra os recursos página por página.\n\nUm exemplo pode ser encontrado na seção [Introdução](rest-quick-start.md#trying-it-out).\n\n\n"
  },
  {
    "path": "docs/guide-pt-BR/rest-response-formatting.md",
    "content": "Formatando Respostas\n===================\n\nAo manipular uma requisição da API RESTful, a aplicação normalmente realiza as seguintes etapas que estão relacionadas com a formatação da resposta:\n\n1. Determinar diversos fatores que podem afetar o formato da resposta, tais como tipo de mídia, idioma, versão, etc. Este processo também é conhecido como [negociação de conteúdo (*content negotiation*)](https://en.wikipedia.org/wiki/Content_negotiation).\n2. Converter objetos de recursos em arrays, como descrito na seção [Recursos](rest-resources.md). Isto é feito por [[yii\\rest\\Serializer]].\n3. Converte arrays em uma string no formato como determinado pela etapa de negociação de conteúdo. Isto é feito pelos [[yii\\web\\ResponseFormatterInterface|formatadores de respostas]] registrados na propriedade [[yii\\web\\Response::formatters|formatters]] do [componente de aplicação](structure-application-components.md) `response`.\n\n\n## Negociação de Conteúdo <span id=\"content-negotiation\"></span>\n\nO Yii suporta a negociação de conteúdo através do filtro [[yii\\filters\\ContentNegotiator]]. A classe base de controller API RESTful [[yii\\rest\\Controller]] está equipado com este filtro sob o nome de `contentNegotiator`. O filtro fornece negociação de formato de resposta, bem como negociação de idioma. Por exemplo, se uma requisição da API RESTful tiver o seguinte cabeçalho,\n\n```\nAccept: application/json; q=1.0, */*; q=0.1\n```\n\nele obterá uma resposta em formato JSON, como o seguinte:\n\n```\n$ curl -i -H \"Accept: application/json; q=1.0, */*; q=0.1\" \"http://localhost/users\"\n\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nX-Powered-By: PHP/5.4.20\nX-Pagination-Total-Count: 1000\nX-Pagination-Page-Count: 50\nX-Pagination-Current-Page: 1\nX-Pagination-Per-Page: 20\nLink: <http://localhost/users?page=1>; rel=self,\n      <http://localhost/users?page=2>; rel=next,\n      <http://localhost/users?page=50>; rel=last\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n[\n    {\n        \"id\": 1,\n        ...\n    },\n    {\n        \"id\": 2,\n        ...\n    },\n    ...\n]\n```\n\nPor baixo dos panos, antes de uma ação do controlador API RESTful ser executada, o filtro [[yii\\filters\\ContentNegotiator]] verificará o `Accept` do cabeçalho HTTP na requisição e definirá o [[yii\\web\\Response::format|response format]] para `'json'`. Após a ação ser executada e retornar o objeto resultante de recursos ou coleção, [[yii\\rest\\Serializer]] converterá o resultado em um array. E finalmente, [[yii\\web\\JsonResponseFormatter]] irá serializar o array em uma string JSON e incluí-la no corpo da resposta.\n\nPor padrão, APIs RESTful suportam tanto os formatos JSON quanto XML. Para suportar um novo formato, você deve configurar a propriedade [[yii\\filters\\ContentNegotiator::formats|formats]] do filtro `contentNegotiator` como mostrado no exemplo a seguir em suas classes do controlador da API:\n\n```php\nuse yii\\web\\Response;\n\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n    $behaviors['contentNegotiator']['formats']['text/html'] = Response::FORMAT_HTML;\n    return $behaviors;\n}\n```\n\n\nAs chaves da propriedade `formats` são os tipos MIME suportados, enquanto os valores são os nomes de formato de resposta correspondentes que devem ser suportados em\n [[yii\\web\\Response::formatters]].\n\n\n## Serializando Dados <span id=\"data-serializing\"></span>\n\nComo foi descrito acima, [[yii\\rest\\Serializer]] é a peça central responsável pela conversão de objetos de recursos ou coleções em arrays. Ele reconhece objetos que implementam a interface [[yii\\base\\ArrayableInterface]] bem como [[yii\\data\\DataProviderInterface]]. O primeiro é aplicado principalmente pelos objetos de recursos, enquanto o último se aplica mais a coleções de recursos.\n\nVocê pode configurar o serializador, definindo a propriedade [[yii\\rest\\Controller::serializer]] com um array de configuração.\nPor exemplo, às vezes você pode querer ajudar a simplificar o trabalho de desenvolvimento do cliente, incluindo informações de paginação diretamente no corpo da resposta. Para fazê-lo, configure a propriedade [[yii\\rest\\Serializer::collectionEnvelope]] como a seguir:\n\n```php\nuse yii\\rest\\ActiveController;\n\nclass UserController extends ActiveController\n{\n    public $modelClass = 'app\\models\\User';\n    public $serializer = [\n        'class' => 'yii\\rest\\Serializer',\n        'collectionEnvelope' => 'items',\n    ];\n}\n```\n\nVocê pode, então, obter a seguinte resposta para a url `http://localhost/users`:\n\n```\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nX-Powered-By: PHP/5.4.20\nX-Pagination-Total-Count: 1000\nX-Pagination-Page-Count: 50\nX-Pagination-Current-Page: 1\nX-Pagination-Per-Page: 20\nLink: <http://localhost/users?page=1>; rel=self,\n      <http://localhost/users?page=2>; rel=next,\n      <http://localhost/users?page=50>; rel=last\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"items\": [\n        {\n            \"id\": 1,\n            ...\n        },\n        {\n            \"id\": 2,\n            ...\n        },\n        ...\n    ],\n    \"_links\": {\n        \"self\": {\n            \"href\": \"http://localhost/users?page=1\"\n        },\n        \"next\": {\n            \"href\": \"http://localhost/users?page=2\"\n        },\n        \"last\": {\n            \"href\": \"http://localhost/users?page=50\"\n        }\n    },\n    \"_meta\": {\n        \"totalCount\": 1000,\n        \"pageCount\": 50,\n        \"currentPage\": 1,\n        \"perPage\": 20\n    }\n}\n```\n\n"
  },
  {
    "path": "docs/guide-pt-BR/rest-routing.md",
    "content": "Roteamento\n=======\n\nCom as classes de recurso e controller prontas, você pode acessar os recursos utilizando uma URL como `http://localhost/index.php?r=user/create`, semelhante ao que você pode fazer com aplicações Web normais.\n\nNa prática, normalmente você desejará utilizar URLs amigáveis e tirar proveito dos métodos HTTP.\nPor exemplo, uma requisição `POST /users` seria o mesmo que a ação `user/create`.\nIsto pode ser feito facilmente através da configuração do [componente de aplicação](structure-application-components.md) `urlManager` conforme mostrado a seguir:\n\n```php\n'urlManager' => [\n   'enablePrettyUrl' => true,\n   'enableStrictParsing' => true,\n   'showScriptName' => false,\n   'rules' => [\n       ['class' => 'yii\\rest\\UrlRule', 'controller' => 'user'],\n   ],\n]\n```\n\nEm comparação com o gerenciamento de URL para aplicações Web, a principal novidade acima é o uso de [[yii\\rest\\UrlRule]] para rotear requisições API RESTful. Esta classe especial criará um conjunto de regras de URL filhas para dar suporte ao roteamento e a criação de URL para o controller especificado.\nPor exemplo, o código acima é mais ou menos equivalente às seguintes regras:\n\n```php\n[\n   'PUT,PATCH users/<id>' => 'user/update',\n   'DELETE users/<id>' => 'user/delete',\n   'GET,HEAD users/<id>' => 'user/view',\n   'POST users' => 'user/create',\n   'GET,HEAD users' => 'user/index',\n   'users/<id>' => 'user/options',\n   'users' => 'user/options',\n]\n```\n\nE as seguintes URLs (também chamadas de *endpoints*) da API são suportados por esta regra:\n\n* `GET /users`: lista todos os usuários página por página;\n* `HEAD /users`: mostrar a informações gerais da listagem de usuários;\n* `POST /users`: cria um novo usuário;\n* `GET /users/123`: retorna detalhes do usuário 123;\n* `HEAD /users/123`: mostrar a informações gerais do usuário 123;\n* `PATCH /users/123` and `PUT /users/123`: atualiza o usuário 123;\n* `DELETE /users/123`: deleta o usuário 123;\n* `OPTIONS /users`: exibe os métodos suportados pela URL `/users`;\n* `OPTIONS /users/123`: exibe os métodos suportados pela URL `/users/123`.\n\nVocê pode configurar as opções `only` e `except` para listar explicitamente quais ações são suportadas ou quais ações devem ser desativadas, respectivamente. Por exemplo,\n\n```php\n[\n   'class' => 'yii\\rest\\UrlRule',\n   'controller' => 'user',\n   'except' => ['delete', 'create', 'update'],\n],\n```\n\nVocê também pode configurar `patterns` ou `extraPatterns` para redefinir padrões existentes ou adicionar novos padrões suportados por esta regra. Por exemplo, para acessar a uma nova ação `search` pela URL `GET /users/search`, configure a opção `extraPatterns` como a seguir,\n\n```php\n[\n   'class' => 'yii\\rest\\UrlRule',\n   'controller' => 'user',\n   'extraPatterns' => [\n       'GET search' => 'search',\n   ],\n]\n```\n\nVocê deve ter notado que o ID `user` de controller aparece no plural como `users` na extremidade das  URLs. Isto acontece porque [[yii\\rest\\UrlRule]] pluraliza os IDs de controllers automaticamente na criação de regras de URLs filhas.\nVocê pode desabilitar este comportamento configurando [[yii\\rest\\UrlRule::pluralize]] para `false`.\n\n> Observação: A pluralização dos IDs de controllers são feitas pelo método [[yii\\helpers\\Inflector::pluralize()]]. O método respeita as regras especiais de pluralização. Por exemplo, a palavra `box` será pluralizada para `boxes` em vez de `boxs`.\n\n\nCaso a pluralização automática não encontre uma opção para a palavra requerida, você pode configurar a propriedade [[yii\\rest\\UrlRule::controller]] para especificar explicitamente como mapear um nome para ser usado como uma URL para um ID de controller. Por exemplo, o seguinte código mapeia o nome `u` para o ID `user` de controller.  \n\n```php\n[\n   'class' => 'yii\\rest\\UrlRule',\n   'controller' => ['u' => 'user'],\n]\n```\n\n\n"
  },
  {
    "path": "docs/guide-pt-BR/rest-versioning.md",
    "content": "Versionamento\n==========\n\nUma boa API é *versionada*: Mudanças e novos recursos são implementados em novas versões da API em vez de alterar continuamente apenas uma versão. Diferente de aplicações Web, com a qual você tem total controle do código de ambos os lados cliente e servidor, APIs são destinadas a ser utilizadas por clientes além de seu controle. Por esta razão, a compatibilidade (BC) entre as APIs deve ser mantida sempre que possível. Se uma mudança que pode quebrar esta compatibilidade é necessária, você deve introduzi-la em uma nova versão de API e subir o número da versão. Os clientes existentes podem continuar a usar a versão antiga da API; e os clientes novos ou atualizados podem obter a nova funcionalidade na nova versão da API.\n\n> Dica: Consulte o artigo [Semantic Versioning](https://semver.org/) para obter mais informações sobre como projetar números de versão da API.\n\nUma maneira comum de implementar versionamento de API é incorporar o número da versão nas URLs da API. Por exemplo, `https://example.com/v1/users` representa o terminal `/users` da API versão 1.\n\nOutro método de versionamento de API, que tem sido muito utilizado recentemente, é colocar o número da versão nos cabeçalhos das requisições HTTP. Isto é tipicamente feito através do cabeçalho `Accept`:\n\n```\n// Através de um parâmetro\nAccept: application/json; version=v1\n// através de um vendor content type\nAccept: application/vnd.company.myapp-v1+json\n```\n\nAmbos os métodos tem seus prós e contras, e há uma série de debates sobre cada abordagem. A seguir, você verá uma estratégia prática para o controle de versão de API que é uma mistura dos dois métodos:\n\n* Coloque cada versão principal de implementação da API em um módulo separado cuja identificação é o número de versão principal (ex. `v1`, `v2`). Naturalmente, as URLs da API irão conter os números da versão principal.\n* Dentro de cada versão principal (e, assim, dentro do módulo correspondente), utilize o cabeçalho `Accept` da requisição HTTP para determinar o número de versão secundária e escrever código condicional para responder às versões menores em conformidade.\n\nPara cada módulo destinado a uma versão principal, deve incluir o recurso e a classe de controller destinados a esta versão específica.\nPara melhor separar a responsabilidade do código, você pode manter um conjunto comum de classes base de recursos e de controller e criar subclasses delas para cada versão individual do módulo. Dentro das subclasses, implementar o código concreto, tais como `Model::fields()`.\n\nSeu código pode ser organizado da seguinte maneira:\n\n```\napi/\n   common/\n       controllers/\n           UserController.php\n           PostController.php\n       models/\n           User.php\n           Post.php\n   modules/\n       v1/\n           controllers/\n               UserController.php\n               PostController.php\n           models/\n               User.php\n               Post.php\n           Module.php\n       v2/\n           controllers/\n               UserController.php\n               PostController.php\n           models/\n               User.php\n               Post.php\n           Module.php\n```\n\nA configuração da sua aplicação seria algo como:\n\n```php\nreturn [\n   'modules' => [\n       'v1' => [\n           'class' => 'app\\modules\\v1\\Module',\n       ],\n       'v2' => [\n           'class' => 'app\\modules\\v2\\Module',\n       ],\n   ],\n   'components' => [\n       'urlManager' => [\n           'enablePrettyUrl' => true,\n           'enableStrictParsing' => true,\n           'showScriptName' => false,\n           'rules' => [\n               ['class' => 'yii\\rest\\UrlRule', 'controller' => ['v1/user', 'v1/post']],\n               ['class' => 'yii\\rest\\UrlRule', 'controller' => ['v2/user', 'v2/post']],\n           ],\n       ],\n   ],\n];\n```\n\nComo resultado do código acima, `https://example.com/v1/users` retornará a lista de usuários na versão 1, enquanto `https://example.com/v2/users` retornará a lista de usuários na versão 2.\n\nGraças aos módulos, o código para diferentes versões principais pode ser bem isolado. Entretanto esta abordagem torna possível a reutilização de código entre os módulos através de classes bases comuns e outros recursos partilhados.\n \nPara lidar com números de subversões, você pode tirar proveito da negociação de conteúdo oferecida pelo behavior [[yii\\filters\\ContentNegotiator|contentNegotiator]]. O behavior `contentNegotiator` irá configurar a propriedade [[yii\\web\\Response::acceptParams]] que determinará qual versão é suportada.\n\nPor exemplo, se uma requisição é enviada com o cabeçalho HTTP `Accept: application/json; version=v1`, Após a negociação de conteúdo, [[yii\\web\\Response::acceptParams]] terá o valor `['version' => 'v1']`.\n\nCom base na informação da versão em `acceptParams`, você pode escrever um código condicional em lugares tais como ações, classes de recursos, serializadores, etc. para fornecer a funcionalidade apropriada.\n\n\nUma vez que subversões por definição devem manter compatibilidades entre si, esperamos que não haja muitas verificações de versão em seu código. De outra forma, você pode precisar criar uma nova versão principal.\n"
  },
  {
    "path": "docs/guide-pt-BR/runtime-bootstrapping.md",
    "content": "Inicialização (Bootstrapping)\n=============================\n\nA inicialização refere-se ao processo de preparação do ambiente antes que uma \naplicação comece a resolver e processar um pedido de requisição. A inicialização \né feita em duas etapas:\nO [script de entrada](structure-entry-scripts.md) e a \n[aplicação](structure-applications.md).\n\nNo [script de entrada](structure-entry-scripts.md), a classe de autoloaders de \ndiferentes bibliotecas são registradas. Inclui o autoloader do Composer através \ndo seu arquivo `autoload.php` e o autoloader do Yii através do seu arquivo `Yii`. \nO script de entrada, em seguida, carrega a [configuração](concept-configurations.md) \nda aplicação e cria uma instância da [aplicação](structure-applications.md).\n\nNo construtor da aplicação, as seguintes etapas de inicialização serão realizadas:\n\n1. O método [[yii\\base\\Application::preInit()|preInit()]] é chamado, na qual \n   algumas propriedades da aplicação de alta prioridade serão configuradas, como \n   o [[yii\\base\\Application::basePath|basePath]].\n2. Registra o [[yii\\base\\Application::errorHandler|manipulador de erro]].\n3. Inicializa as propriedades da aplicação a partir da configuração da aplicação.\n4. O método [[yii\\base\\Application::init()|init()]] é chamado, que por sua vez \n   chamará o método [[yii\\base\\Application::bootstrap()|bootstrap()]] para executar \n   os componentes de inicialização.\n   - Inclui o arquivo `vendor/yiisoft/extensions.php` de manifesto da extensão.\n   - Cria e executa os [componentes de inicialização](structure-extensions.md#bootstrapping-classes) \n     declaradas pelas extensões.\n   - Cria e executa os [componentes da aplicação](structure-application-components.md) \n     e/ou os [módulos](structure-modules.md) declarados na \n     [propriedade bootstrap](structure-applications.md#bootstrap) da aplicação.\n\nComo as etapas de inicialização tem que ser feitos antes da manipulação de *cada* \nrequisição, é muito importante que mantenha este processo limpo e otimizado o \nmáximo possível.\n\nTente não registrar muitos componentes de inicialização. Um componente de \ninicialização é necessário apenas se quiser participar de todo o ciclo de vida \ndo processo da requisição. Por exemplo, se um módulo precisar registrar uma \nanálise de regras de URL adicionais, deve ser listados na \n[propriedade bootstrap](structure-applications.md#bootstrap) de modo que as novas \nregras de URL possam ter efeito antes que sejam usados para resolver as requisições.\n\nNo modo de produção, habilite um cache de bytecode, como o [PHP OPcache] ou [APC], \npara minimizar o tempo necessário para a inclusão e análise os arquivos PHP.\n\n[PHP OPcache]: https://www.php.net/manual/pt_BR/intro.opcache.php\n[APC]: https://www.php.net/manual/pt_BR/book.apcu.php\n\nAlgumas aplicações de larga escala possuem [configurações](concept-configurations.md) \ncomplexas, que são divididos em vários arquivos menores. Se este for o caso, \nconsidere guardar o cache de todo o array da configuração e carregue-o \ndiretamente a partir deste cache antes da criação da instância da aplicação no \nscript de entrada.\n\n"
  },
  {
    "path": "docs/guide-pt-BR/runtime-handling-errors.md",
    "content": "Tratamento de Erros\n===============\n\nO Yii inclui um próprio [[yii\\web\\ErrorHandler|tratamento de erro]] que o torna uma experiência muito mais agradável do que antes. Em particular, o manipulador de erro do Yii faz o seguinte para melhorar o tratamento de erros:\n\n* Todos os erros não-fatais do PHP (ex. advertências, avisos) são convertidas em exceções capturáveis.\n\n* Exceções e erros fatais do PHP são exibidos com detalhes de informação em uma pilha de chamadas (call stack) e linhas de código-fonte no modo de depuração.\n\n* Suporta o uso de uma [ação do controller](structure-controllers.md#actions) dedicado para exibir erros.\n\n* Suporta diferentes formatos de resposta de erro.\n\nO [[yii\\web\\ErrorHandler|manipulador de erro]] é habilitado por padrão. Você pode desabilitá-lo definindo a constante `YII_ENABLE_ERROR_HANDLER` como `false` no [script de entrada](structure-entry-scripts.md) da aplicação.\n\n\n## Usando Manipulador de Erro <span id=\"using-error-handler\"></span>\n\nO [[yii\\web\\ErrorHandler|manipulador de erro]] é registrado como um [componente da aplicação](structure-application-components.md) chamado `errorHandler`.\nVocê pode configurá-lo na configuração da aplicação da seguinte forma:\n\n```php\nreturn [\n   'components' => [\n       'errorHandler' => [\n           'maxSourceLines' => 20,\n       ],\n   ],\n];\n```\n\nCom a configuração acima, o número de linhas de código fonte para ser exibido em páginas de exceção será de até 20.\n\nComo já informado, o manipulador de erro transforma todos os erros não fatais do PHP em exceções capturáveis. Isto significa que você pode usar o seguinte código para lidar com erros do PHP:\n\n```php\nuse Yii;\nuse yii\\base\\ErrorException;\n\ntry {\n   10/0;\n} catch (ErrorException $e) {\n   Yii::warning(\"Division by zero.\");\n}\n\n// Continua a execução...\n```\n\nSe você deseja mostrar uma página de erro dizendo ao usuário que a sua requisição é inválida ou inesperada, você pode simplesmente lançar uma [[yii\\web\\HttpException|exceção HTTP]], tal como [[yii\\web\\NotFoundHttpException]]. O manipulador de erro irá definir corretamente o código de status HTTP da resposta e usar uma exibição de erro apropriada para exibir a mensagem de erro.\n\n```php\nuse yii\\web\\NotFoundHttpException;\n\nthrow new NotFoundHttpException();\n```\n\n\n## Personalizando a Exibição de Erro <span id=\"customizing-error-display\"></span>\n\nO [[yii\\web\\ErrorHandler|manipulador de erro]] ajusta a exibição de erro de acordo com o valor da constante `YII_DEBUG`. Quando `YII_DEBUG` for `True` (significa modo de debug), o manipulador de erro irá exibir exceções com informações detalhadas da pilha de chamadas e linhas do código fonte para ajudar na depuração do erro. E quando `YII_DEBUG` for `false`, apenas a mensagem de erro será exibida para evitar revelar informações relevantes sobre a aplicação.\n\n> Observação: Se uma exceção descende de [[yii\\base\\UserException]], nenhuma pilha de chamadas será exibido independentemente do valor do `YII_DEBUG`. Isso porque tais exceções são consideradas erros causados pelo usuário não havendo nada a ser corrigido por parte dos programadores.\n\nPor padrão, o [[yii\\web\\ErrorHandler|manipulador de erro]] mostra os erros utilizando duas  [views](structure-views.md):\n\n* `@yii/views/errorHandler/error.php`: utilizada quando os erros devem ser exibidos sem informações pilha de chamadas. Quando `YII_DEBUG` for `false`, esta é a única exibição de erro a ser exibida.\n\n* `@yii/views/errorHandler/exception.php`: utilizada quando os erros devem ser exibidos com informações pilha de chamadas. Você pode configurar as propriedades [[yii\\web\\ErrorHandler::errorView|errorView]] e [[yii\\web\\ErrorHandler::exceptionView|exceptionView]]\ndo manipulador de erros para usar suas próprias views personalizando  a exibição de erro.\n\n\n### Usando Ações de Erros <span id=\"using-error-actions\"></span>\n\nA melhor maneira de personalizar a exibição de erro é usar uma [ação](structure-controllers.md) dedicada para este fim. Para fazê-lo, primeiro configure a propriedade [[yii\\web\\ErrorHandler::errorAction|errorAction]] do componente `errorHandler`\ncomo a seguir:\n\n```php\nreturn [\n   'components' => [\n       'errorHandler' => [\n           'errorAction' => 'site/error',\n       ],\n   ]\n];\n```\n\nA propriedade [[yii\\web\\ErrorHandler::errorAction|errorAction]] define uma [rota](structure-controllers.md#routes) para uma ação. A configuração acima afirma que quando um erro precisa ser exibido sem informações da pilha de chamadas, a ação `site/error` deve ser executada.\n\nVocê pode criar a ação `site/error` da seguinte forma,\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n   public function actions()\n   {\n       return [\n           'error' => [\n               'class' => 'yii\\web\\ErrorAction',\n           ],\n       ];\n   }\n}\n```\n\nO código acima define a ação `error` usando a classe [[yii\\web\\ErrorAction]] que renderiza um erro usando a view `error`.\n\nAlém de usar [[yii\\web\\ErrorAction]], você também pode definir a ação `error` usando um método da ação como o seguinte,\n\n```php\npublic function actionError()\n{\n   $exception = Yii::$app->errorHandler->exception;\n   if ($exception !== null) {\n       return $this->render('error', ['exception' => $exception]);\n   }\n}\n```\n\nAgora você deve criar um arquivo de exibição localizado na `views/site/error.php`. Neste arquivo de exibição, você pode acessar as seguintes variáveis se a ação de erro for definida como\n[[yii\\web\\ErrorAction]]:\n\n* `name`: o nome do erro;\n* `message`: a mensagem de erro;\n* `exception`: o objeto de exceção através do qual você pode recuperar mais informações úteis, como o código de status HTTP, o código de erro, pilha de chamadas de erro, etc.\n\n> Observação: Se você está utilizando o [template básico](start-installation.md) ou o [template avançado](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide-pt-BR/README.md), a ação e a view de erro já estão definidas para você.\n\n\n### Customizando o Formato da Resposta de Erro <span id=\"error-format\"></span>\n\nO manipulador de erro exibe erros de acordo com a definição do formato da [resposta](runtime-responses.md). Se o [[yii\\web\\Response::format|formato da resposta]] for `html`, ele usará a view de erro ou exceção para exibir os erros, como descrito na última subseção. Para outros formatos de resposta, o manipulador de erro irá atribuir o array de representação da exceção para a propriedade [[yii\\web\\Response::data]] que será então convertida para diferentes formatos de acordo com o que foi configurado. Por exemplo, se o formato de resposta for `json`, você pode visualizar a seguinte resposta:\n\n```\nHTTP/1.1 404 Not Found\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n   \"name\": \"Not Found Exception\",\n   \"message\": \"The requested resource was not found.\",\n   \"code\": 0,\n   \"status\": 404\n}\n```\n\nVocê pode personalizar o formato de resposta de erro, respondendo ao evento `beforeSend` do componente `response` na configuração da aplicação:\n\n```php\nreturn [\n   // ...\n   'components' => [\n       'response' => [\n           'class' => 'yii\\web\\Response',\n           'on beforeSend' => function ($event) {\n               $response = $event->sender;\n               if ($response->data !== null) {\n                   $response->data = [\n                       'success' => $response->isSuccessful,\n                       'data' => $response->data,\n                   ];\n                   $response->statusCode = 200;\n               }\n           },\n       ],\n   ],\n];\n```\n\nO código acima irá reformatar a resposta de erro como a seguir:\n\n```\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n   \"success\": false,\n   \"data\": {\n       \"name\": \"Not Found Exception\",\n       \"message\": \"The requested resource was not found.\",\n       \"code\": 0,\n       \"status\": 404\n   }\n}\n```\n"
  },
  {
    "path": "docs/guide-pt-BR/runtime-logging.md",
    "content": "Log\n=======\n\nO Yii fornece um poderoso framework de log que é altamente personalizável e extensível. Utilizando este framework, você pode facilmente gerar logs de vários tipos de mensagens, filtrá-las, e salva-las em diferentes meios, tais como arquivos, banco de dados, e-mails. \n\nUsar o Yii framework Log envolve os seguintes passos:\n\n* Registrar [mensagens de log](#log-messages) de vários lugares do seu código;\n* Configurar o [destino de log](#log-targets) na configuração da aplicação para filtrar e exportar mensagens de log;\n* Examinar as mensagens de erro exportadas  (ex.: [Yii debugger](tool-debugger.md)).\n\nNesta seção, vamos descrever principalmente os dois primeiros passos.\n\n\n## Gravar Mensagens <span id=\"log-messages\"></span>\n\nGravar mensagens de log é tão simples como chamar um dos seguintes métodos de registro:\n\n* [[Yii::debug()]]: gravar uma mensagem para rastrear como um determinado trecho de código é executado. Isso é principalmente para o uso de desenvolvimento.\n* [[Yii::info()]]: gravar uma mensagem que transmite algumas informações úteis.\n* [[Yii::warning()]]: gravar uma mensagem de aviso que indica que algo inesperado aconteceu.\n* [[Yii::error()]]: gravar um erro fatal que deve ser investigado o mais rápido possível.\n\nEstes métodos gravam mensagens de log em vários *níveis* e *categorias*. Eles compartilham a mesma assinatura de função `function ($message, $category = 'application')`, onde `$message` significa a mensagem de log a ser gravada, enquanto `$category` é a categoria da mensagem de log. O código no exemplo a seguir registra uma mensagem de rastreamento sob a categoria padrão `application`:\n\n```php\nYii::debug('start calculating average revenue');\n```\n\n> Observação: Mensagens de log podem ser strings, bem como dados complexos, tais como arrays ou objetos. É da responsabilidade dos [destinos de log](#log-targets) lidar adequadamente com as mensagens de log. Por padrão, se uma mensagem de log não for uma string, ela será exportada como uma string chamando [[yii\\helpers\\VarDumper::export()]].\n\nPara melhor organizar e filtrar as mensagens de log, é recomendável que você especifique uma categoria apropriada para cada mensagem de log. Você pode escolher um esquema de nomenclatura hierárquica para as categorias, o que tornará mais fácil para os [destinos de log](#log-targets) filtrar mensagens com base em suas categorias. Um esquema de nomes simples, mas eficaz é usar a constante mágica PHP `__METHOD__` para os nomes das categorias. Esta é também a abordagem utilizada no código central do framework Yii. Por exemplo,\n\n```php\nYii::debug('start calculating average revenue', __METHOD__);\n```\n\nA constante `__METHOD__` corresponde ao nome do método (prefixado com o caminho completo do nome da classe) onde a constante aparece. Por exemplo, é igual a string `'app\\controllers\\RevenueController::calculate'` se o código acima for chamado dentro deste método.\n\n> Observação: Os métodos de registro descritos acima são na verdade atalhos para o método [[yii\\log\\Logger::log()|log()]] do [[yii\\log\\Logger|logger object]] que é um singleton acessível através da expressão `Yii::getLogger()`. Quando um determinado número de mensagens são logadas ou quando a aplicação termina, o objeto logger irá chamar um [[yii\\log\\Dispatcher|message dispatcher]] para enviar mensagens de log gravadas [destinos de log](#log-targets).\n\n\n## Destinos de Log <span id=\"log-targets\"></span>\n\nUm destino de log é uma instância da classe [[yii\\log\\Target]] ou uma classe filha. Ele filtra as mensagens de log por seus níveis e categorias e, em seguida, às exportam para algum meio. Por exemplo, um [[yii\\log\\DbTarget|banco de dados de destino]] exporta as mensagens de log para uma tabela no banco de dados, enquanto um [[yii\\log\\EmailTarget|e-mail de destino]] exporta as mensagens de log para algum e-mail especificado.\n\nVocê pode registrar vários destinos de log em uma aplicação configurando-os através do [componente da aplicação](structure-application-components.md) `log` na configuração da aplicação, como a seguir:\n\n```php\nreturn [\n  // o componente  \"log\" deve ser carregado durante o tempo de inicialização\n  'bootstrap' => ['log'],\n  \n  'components' => [\n      'log' => [\n          'targets' => [\n              [\n                  'class' => 'yii\\log\\DbTarget',\n                  'levels' => ['error', 'warning'],\n              ],\n              [\n                  'class' => 'yii\\log\\EmailTarget',\n                  'levels' => ['error'],\n                  'categories' => ['yii\\db\\*'],\n                  'message' => [\n                     'from' => ['log@example.com'],\n                     'to' => ['admin@example.com', 'developer@example.com'],\n                     'subject' => 'Database errors at example.com',\n                  ],\n              ],\n          ],\n      ],\n  ],\n];\n```\n\n> Observação: O componente `log` deve ser carregado durante a [inicialização](runtime-bootstrapping.md) para que ele possa enviar mensagens de log para alvos prontamente. É por isso que ele está listado no array `bootstrap` como mostrado acima.\n\nNo código acima, dois destinos de log são registrados na propriedade [[yii\\log\\Dispatcher::targets]]: \n\n* o primeiro seleciona mensagens de erro e de advertência e os salva em uma tabela de banco de dados;\n* o segundo seleciona mensagens de erro sob as categorias cujos nomes começam com `yii\\db\\`, e as envia para os e-mails `admin@example.com` e `developer@example.com`.\n\nYii vem com os seguintes destinos de log preparados. Por favor consulte a documentação da API sobre essas classes para aprender como configurar e usá-los. \n\n* [[yii\\log\\DbTarget]]: armazena mensagens de log em uma tabela de banco de dados.\n* [[yii\\log\\EmailTarget]]: envia mensagens de log para um endereço de e-mail pré-definido.\n* [[yii\\log\\FileTarget]]: salva mensagens de log em arquivos.\n* [[yii\\log\\SyslogTarget]]: salva mensagens de log para o syslog chamando a função PHP `syslog()`.\n\nA seguir, vamos descrever as características comuns a todos os destinos de log.\n\n\n### Filtragem de Mensagem <span id=\"message-filtering\"></span>\n\nPara cada destino de log, você pode configurar suas propriedades [[yii\\log\\Target::levels|levels]] e [[yii\\log\\Target::categories|categories]] para especificar que os níveis e categorias das mensagens o destino de log deve processar.\n\nA propriedade [[yii\\log\\Target::levels|levels]] é um array que consiste em um ou vários dos seguintes valores:\n\n* `error`: corresponde a mensagens logadas por [[Yii::error()]].\n* `warning`: corresponde a mensagens logadas por [[Yii::warning()]].\n* `info`: corresponde a mensagens logadas por [[Yii::info()]].\n* `trace`: corresponde a mensagens logadas por [[Yii::debug()]].\n* `profile`: corresponde a mensagens logadas por [[Yii::beginProfile()]] e [[Yii::endProfile()]], que será explicado em mais detalhes na subseção [Perfil de Desempenho](#performance-profiling).\n\nSe você não especificar a propriedade [[yii\\log\\Target::levels|levels]], significa que o alvo de log processará mensagens de *qualquer* nível.\n\nA propriedade [[yii\\log\\Target::categories|categories]] é um array que consiste em categorias de mensagens ou padrões. Um destino de log irá processar apenas mensagens cuja categoria possa ser encontrada ou corresponder a um dos padrões do array. Um padrão de categoria é um prefixo de nome de categoria com um asterisco `*` na sua extremidade. Um nome de categoria corresponde a um padrão de categoria se ela iniciar com o mesmo prefixo do padrão. Por exemplo, `yii\\db\\Command::execute` e `yii\\db\\Command::query`\nsão usados como nome de categoria para as mensagens de log gravadas na classe [[yii\\db\\Command]]. Ambos correspondem ao padrão `yii\\db\\*`. Se você não especificar a propriedade [[yii\\log\\Target::categories|categories]], significa que o destino de log processará mensagens de *qualquer* categoria.\n\nAlém de criar uma whitelist de categorias através da propriedade [[yii\\log\\Target::categories|categories]], você também pode criar uma blacklist de categorias através da propriedade [[yii\\log\\Target::except|except]]. Se a categoria da mensagem for encontrada ou corresponder a um dos padrões desta propriedade, ela não será processada pelo destino de log.\n\nA próxima configuração de destino de log especifica que o destino deve processar somente mensagens de erro e alertas das categorias cujos nomes correspondam a `yii\\db\\*` ou `yii\\web\\HttpException:*`, mas não correspondam a `yii\\web\\HttpException:404`.\n\n```php\n[\n  'class' => 'yii\\log\\FileTarget',\n  'levels' => ['error', 'warning'],\n  'categories' => [\n      'yii\\db\\*',\n      'yii\\web\\HttpException:*',\n  ],\n  'except' => [\n      'yii\\web\\HttpException:404',\n  ],\n]\n```\n\n> Observação: Quando uma exceção HTTP  é capturada pelo [error handler](runtime-handling-errors.md), uma mensagem de erro será logada com o nome da categoria no formato de `yii\\web\\HttpException:ErrorCode`. Por exemplo, o [[yii\\web\\NotFoundHttpException]] causará uma mensagem de erro da categoria `yii\\web\\HttpException:404`.\n\n\n### Formatando Mensagem <span id=\"message-formatting\"></span>\n\nDestinos de log exportam as mensagens de logs filtradas em um determinado formato. Por exemplo, se você instalar um destino de log da classe [[yii\\log\\FileTarget]], você pode encontrar uma mensagem de log semelhante à seguinte no `runtime/log/app.log` file:\n\n```\n2014-10-04 18:10:15 [::1][][-][trace][yii\\base\\Module::getModule] Loading module: debug\n```\n\nPor padrão, mensagens de log serão formatadas do seguinte modo pelo [[yii\\log\\Target::formatMessage()]]:\n\n```\nTimestamp [IP address][User ID][Session ID][Severity Level][Category] Message Text\n```\n\nVocê pode personalizar este formato configurando a propriedade [[yii\\log\\Target::prefix]] que recebe um PHP callable retornando um prefixo de mensagem personalizado. Por exemplo, o código a seguir configura um destino de log para prefixar cada mensagem de log com o ID do usuário corrente (Endereço IP e ID da sessão são removidos por razões de privacidade).\n\n```php\n[\n  'class' => 'yii\\log\\FileTarget',\n  'prefix' => function ($message) {\n      $user = Yii::$app->has('user', true) ? Yii::$app->get('user') : null;\n      $userID = $user ? $user->getId(false) : '-';\n      return \"[$userID]\";\n  }\n]\n```\n\nAlém de prefixos de mensagens, destinos de mensagens também anexa algumas informações de contexto para cada lote de mensagens de log. Por padrão, os valores destas variáveis globais PHP são incluídas: `$_GET`, `$_POST`, `$_FILES`, `$_COOKIE`,\n`$_SESSION` e `$_SERVER`. Você pode ajustar este comportamento configurando a propriedade [[yii\\log\\Target::logVars]] com os nomes das variáveis globais que você deseja incluir para o destino de log. Por exemplo, a seguinte configuração de destino de log especifica que somente valores da variável `$_SERVER` seriam anexadas as mensagens de log.\n\n```php\n[\n  'class' => 'yii\\log\\FileTarget',\n  'logVars' => ['_SERVER'],\n]\n```\n\nVocê pode configurar `logVars` para ser um array vazio para desativar totalmente a inclusão de informações de contexto. Ou se você quiser implementar sua própria maneira de fornecer informações de contexto, você pode sobrescrever o método [[yii\\log\\Target::getContextMessage()]].\n\n\n### Nível de Rastreio de Mensagem <span id=\"trace-level\"></span>\n\nDurante o desenvolvimento, é desejável definir de onde cada mensagem de log virá. Isto pode ser conseguido por meio da configuração da propriedade [[yii\\log\\Dispatcher::traceLevel|traceLevel]] do componente `log` como a seguir:\n\n```php\nreturn [\n  'bootstrap' => ['log'],\n  'components' => [\n      'log' => [\n          'traceLevel' => YII_DEBUG ? 3 : 0,\n          'targets' => [...],\n      ],\n  ],\n];\n```\n\nA configuração da aplicação acima define o [[yii\\log\\Dispatcher::traceLevel|traceLevel]] para ser 3 se `YII_DEBUG` estiver ligado e 0 se `YII_DEBUG` estiver desligado. Isso significa, se `YII_DEBUG` estiver ligado, cada mensagem de log será anexada com no máximo 3 níveis de call stack (pilhas de chamadas) em que a mensagem de log é registrada; e se `YII_DEBUG` estiver desligado, nenhuma informação do call stack será incluída.\n\n> Observação: Obter informação do call stack não é trivial. Portanto, você deverá usar somente este recurso durante o desenvolvimento ou durante o debug da aplicação.\n\n\n### Libertação e Exportação de Mensagens <span id=\"flushing-exporting\"></span>\n\nComo já mencionado, mensagens de log são mantidas em um array através do [[yii\\log\\Logger|objeto logger]]. Para limitar o consumo de memória por este array, o objeto logger irá liberar as mensagens gravadas para os [destinos de log](#log-targets) cada vez que o array acumula um certo número de mensagens de log. Você pode personalizar este número configurando a propriedade [[yii\\log\\Dispatcher::flushInterval|flushInterval]] do componente `log`:\n\n\n```php\nreturn [\n  'bootstrap' => ['log'],\n  'components' => [\n      'log' => [\n          'flushInterval' => 100,   // o padrão é 1000\n          'targets' => [...],\n      ],\n  ],\n];\n```\n\n> Observação: Liberação de mensagens também acontece quando a aplicação termina, o que garante que alvos de log possam receber as informações completas de mensagens de log.\n\nQuando o [[yii\\log\\Logger|logger object]] libera mensagens de log para os [alvos de log](#log-targets), elas não são exportadas imediatamente. Em vez disso, a exportação de mensagem só ocorre quando o alvo de log acumula certo número de mensagens filtradas. Você pode personalizar este número configurando a propriedade [[yii\\log\\Target::exportInterval|exportInterval]] de cada [alvo de log](#log-targets), como a seguir,\n\n```php\n[\n  'class' => 'yii\\log\\FileTarget',\n  'exportInterval' => 100,  // default is 1000\n]\n```\n\nDevido a configuração de nível, liberação e exportação, por padrão quando você chama `Yii::debug()` ou qualquer outro método de log, você NÃO verá a mensagem de log imediatamente no destino. Isto poderia ser um problema para algumas aplicações console de longa execução. Para fazer cada mensagem de log aparecer imediatamente no destino, você deve configurar ambos [[yii\\log\\Dispatcher::flushInterval|flushInterval]] e [[yii\\log\\Target::exportInterval|exportInterval]] para  1, como mostrado a seguir:\n\n```php\nreturn [\n  'bootstrap' => ['log'],\n  'components' => [\n      'log' => [\n          'flushInterval' => 1,\n          'targets' => [\n              [\n                  'class' => 'yii\\log\\FileTarget',\n                  'exportInterval' => 1,\n              ],\n          ],\n      ],\n  ],\n];\n```\n\n> Observação: A frequente liberação e exportação de mensagens irá degradar o desempenho da sua aplicação.\n\n\n### Alternando Destinos de Log <span id=\"toggling-log-targets\"></span>\n\nVocê pode habilitar ou desabilitar um destino de log configurando a propriedade [[yii\\log\\Target::enabled|enabled]]. Você pode fazê-lo através da configuração do destino de log ou pela seguinte declaração em seu código PHP:\n\n```php\nYii::$app->log->targets['file']->enabled = false;\n```\n\nO código acima requer que você nomeie um destino como`file`, como mostrado acima usando chaves de string no array `targets`:\n\n```php\nreturn [\n  'bootstrap' => ['log'],\n  'components' => [\n      'log' => [\n          'targets' => [\n              'file' => [\n                  'class' => 'yii\\log\\FileTarget',\n              ],\n              'db' => [\n                  'class' => 'yii\\log\\DbTarget',\n              ],\n          ],\n      ],\n  ],\n];\n```\n\n\n### Criando Novos Destinos <span id=\"new-targets\"></span>\n\nCriar uma nova classe de destino de log é muito simples. Você primeiramente precisa implementar o método [[yii\\log\\Target::export()]] enviando o conteúdo do array [[yii\\log\\Target::messages]] para o meio designado. Você pode chamar o método\n[[yii\\log\\Target::formatMessage()]] para formatar cada mensagem. Para mais detalhes, você pode consultar qualquer uma das classes de destino de log incluído na versão Yii.\n\n\n## Perfil de Desempenho<span id=\"performance-profiling\"></span>\n\nPerfil de desempenho é um tipo especial de log de mensagem que é usado para medir o tempo que certos blocos de código demora e para descobrir quais são os gargalos de desempenho. Por exemplo, a classe [[yii\\db\\Command]] utiliza perfil de desempenho para descobrir o tempo que cada db query leva.\n\nPara usar perfil de desempenho, primeiro identifique o bloco de código que precisa ser analisado. Então, encapsula cada bloco de código como o seguinte:\n\n```php\n\\Yii::beginProfile('myBenchmark');\n\n...code block being profiled...\n\n\\Yii::endProfile('myBenchmark');\n```\n\nonde `myBenchmark` representa um token único de identificação de um bloco de código. Mais tarde quando você for examinar o resultado, você usará este token para localizar o tempo gasto pelo determinado bloco de código.\n\nÉ importante certificar-se de que os pares de `beginProfile` e `endProfile` estão corretamente aninhadas. Por exemplo,\n\n```php\n\\Yii::beginProfile('block1');\n\n  // algum código a ser analizado\n\n  \\Yii::beginProfile('block2');\n      // algum outro código a ser analizado\n  \\Yii::endProfile('block2');\n\n\\Yii::endProfile('block1');\n```\n\nSe você esquecer `\\Yii::endProfile('block1')` ou trocar a ordem de `\\Yii::endProfile('block1')` e `\\Yii::endProfile('block2')`, o perfil de desempenho não funcionará.\n\nPara cada bloco de código iniciado com  `beginProfile`, uma mensagem de log com o nível `profile` é registrada. Você pode configurar um [destino de log](#log-targets) para coletar tais mensagens e exportá-las. O [Yii debugger](tool-debugger.md) implementa um painel de perfil de desempenho mostrando os seus resultados.\n"
  },
  {
    "path": "docs/guide-pt-BR/runtime-overview.md",
    "content": "Visão Geral\n===========\n\nCada vez que uma aplicação Yii processa uma requisição, ele passa por um fluxo \nde trabalho parecido como o seguinte:\n\n1. Um usuário faz uma pedido para o [script de entrada](structure-entry-scripts.md) \n   `web/index.php`.\n2. O script de entrada carrega a [configuração](concept-configurations.md) da \n   aplicação e cria uma instância da [aplicação](structure-applications.md) para \n   processar o pedido.\n3. A aplicação resolve a [rota](runtime-routing.md) solicitada com a ajuda do \n   componente [request](runtime-requests.md) da aplicação.\n4. A aplicação cria uma instância do [controller (controlador)](structure-controllers.md) \n   para processar o pedido.\n5. O controller (controlador) cria uma instância da [ação](structure-controllers.md) \n   e executar os filtros para a ação.\n6. Se qualquer filtro falhar, a ação será cancelada.\n7. Se todos os filtros passarem, a ação será executada.\n8. A ação carrega os dados do model (modelo), possivelmente a partir de um banco \n   de dados.\n9. A ação renderiza uma view (visão), com os dados fornecidos pelo model (modelo).\n10. O resultado da renderização é devolvida para o componente [response](runtime-responses.md) \n    da aplicação.\n11. O componente response envia o resultado da renderização para o navegador do \n    usuário.\n\nO diagrama a seguir mostra como uma aplicação processa um pedido.\n\n![Request Lifecycle](images/request-lifecycle.png)\n\nNesta seção, descreveremos com mais detalhes como alguns destes passos trabalham.\n"
  },
  {
    "path": "docs/guide-pt-BR/runtime-requests.md",
    "content": "Requisições\n===========\n\nAs requisições realizadas na aplicação são representadas pelo objeto [[yii\\web\\Request]] \nque fornece informações como os parâmetros da requisição, cabeçalhos HTTP, cookies \ne etc. Em uma determinada requisição, você pode acessar o objeto da requisição \ncorrespondente através do [componente da aplicação](structure-application-components.md) \n`request`, que é uma instância de [[yii\\web\\Request]], por padrão. Nesta seção, \ndescreveremos como você pode usar este componente em sua aplicação.\n\n\n## Parâmetros da Requisição <span id=\"request-parameters\"></span>\n\nPara obter os parâmetros da requisição, você pode chamar os métodos \n[[yii\\web\\Request::get()|get()]] e [[yii\\web\\Request::post()|post()]] do \ncomponente `request`. Estes métodos retornam os valores de `$_GET` e `$_POST`, \nrespectivamente. Por exemplo,\n\n```php\n$request = Yii::$app->request;\n\n$get = $request->get(); \n// equivalente à: $get = $_GET;\n\n$id = $request->get('id');   \n// equivalente à: $id = isset($_GET['id']) ? $_GET['id'] : null;\n\n$id = $request->get('id', 1);   \n// equivalente à: $id = isset($_GET['id']) ? $_GET['id'] : 1;\n\n$post = $request->post(); \n// equivalente à: $post = $_POST;\n\n$name = $request->post('name');   \n// equivalente à: $name = isset($_POST['name']) ? $_POST['name'] : null;\n\n$name = $request->post('name', '');   \n// equivalente à: $name = isset($_POST['name']) ? $_POST['name'] : '';\n```\n\n> Informação: Ao invés de acessar diretamente o `$_GET` e o `$_POST` para recuperar \n  os parâmetros da requisição, é recomendável que os utilizem através do componente \n  `request`, como mostrado nos exemplos acima. Isto permite que você escreva testes \n  de forma mais simples, utilizando um componente da requisição que retornem valores \n  pré-determinados.\n\nAo implementar o [RESTful APIs](rest-quick-start.md), muitas vezes você precisará \nrecuperar os parâmetros que foram enviados pelos [métodos de requisição](#request-methods) \nPUT, PATCH ou outro. Você pode recuperá-los chamando o método [[yii\\web\\Request::getBodyParam()]]. \nPor exemplo,\n\n```php\n$request = Yii::$app->request;\n\n// retorna todos os parâmetros \n$params = $request->bodyParams;\n\n// retorna o parâmetro \"id\"\n$param = $request->getBodyParam('id');\n```\n\n> Informação: Tirando os parâmetros `GET`, os parâmetros `POST`, `PUT`, `PATCH` \n  e etc são enviados no corpo da requisição. O componente `request` analisará \n  estes parâmetros quando você acessá-los através dos métodos descritos acima.\n  Você pode personalizar a forma como estes parâmetros são analisados pela \n  configuração da propriedade [[yii\\web\\Request::parsers]].\n\n\n## Métodos da Requisição <span id=\"request-methods\"></span>\n\nVocê pode obter o método HTTP usado pela requisição atual através da expressão \n`Yii::$app->request->method`. Um conjunto de propriedades booleanas também são \nfornecidos para que você consiga verificar se o método atual é o correto.\nPor exemplo,\n\n```php\n$request = Yii::$app->request;\n\nif ($request->isAjax) { /* a requisição é uma requisição Ajax */ }\nif ($request->isGet)  { /* o método da requisição é GET */ }\nif ($request->isPost) { /* o método da requisição é POST */ }\nif ($request->isPut)  { /* o método da requisição é PUT */ }\n```\n\n## URLs da Requisição <span id=\"request-urls\"></span>\n\nO componente `request` fornece muitas formas de inspecionar a atual URL da requisição.\nAssumindo que a URL da requisição seja `https://example.com/admin/index.php/product?id=100`, \nvocê pode obter várias partes desta URL através das propriedades explicadas a seguir:\n\n* [[yii\\web\\Request::url|url]]: retorna `/admin/index.php/product?id=100`, que é \n  a URL sem as informações de protocolo e de domínio. \n* [[yii\\web\\Request::absoluteUrl|absoluteUrl]]: retorna `https://example.com/admin/index.php/product?id=100`, \n  que é a URL completa, incluindo as informações de protocolo e de domínio.\n* [[yii\\web\\Request::hostInfo|hostInfo]]: retorna `https://example.com`, que são \n  as informações de protocolo e de domínio da URL.\n* [[yii\\web\\Request::pathInfo|pathInfo]]: retorna `/product`, que é a informação \n  depois do script de entrada e antes da interrogação (da query string).\n* [[yii\\web\\Request::queryString|queryString]]: retorna `id=100`, que é a \n  informação depois da interrogação. \n* [[yii\\web\\Request::baseUrl|baseUrl]]: retorna `/admin`, que é a informação \n  depois do domínio e antes do script de entrada.\n* [[yii\\web\\Request::scriptUrl|scriptUrl]]: retorna `/admin/index.php`, que é a \n  informação depois do domínio até o script de entrada, inclusive.\n* [[yii\\web\\Request::serverName|serverName]]: retorna `example.com`, que é o \n  domínio da URL.\n* [[yii\\web\\Request::serverPort|serverPort]]: retorna 80, que é a porta usada \n  pelo servidor Web.\n\n\n## Cabeçalho HTTP <span id=\"http-headers\"></span> \n\nVocê pode obter as informações do cabeçalho HTTP através da \n[[yii\\web\\HeaderCollection|coleção de cabeçalho]] retornado pela propriedade \n[[yii\\web\\Request::headers]]. Por exemplo,\n\n```php\n// $headers é um objeto de yii\\web\\HeaderCollection \n$headers = Yii::$app->request->headers;\n\n// retorna o valor do cabeçalho Accept\n$accept = $headers->get('Accept');\n\nif ($headers->has('User-Agent')) { /* existe o cabeçalho User-Agent */ }\n```\n\nO componente `request` também fornece suporte para fácil acesso de alguns \ncabeçalhos mais utilizados, incluindo:\n\n* [[yii\\web\\Request::userAgent|userAgent]]: retorna o valor do cabeçalho `User-Agent`.\n* [[yii\\web\\Request::contentType|contentType]]: retorna o valor do cabeçalho \n  `Content-Type` que indica o tipo MIME dos dados do corpo da requisição.\n* [[yii\\web\\Request::acceptableContentTypes|acceptableContentTypes]]: retorna os \n  tipos MIME acessíveis pelos usuários. Os tipos retornados são ordenados pela \n  sua pontuação de qualidade.  Tipos com mais pontuação aparecerão nas primeiras posições.\n* [[yii\\web\\Request::acceptableLanguages|acceptableLanguages]]: retorna os idiomas \n  acessíveis pelos usuários. Os idiomas retornados são ordenados pelo nível de \n  preferência. O primeiro elemento representa o idioma de maior preferência.\n\nSe a sua aplicação suportar diversos idiomas e quiser exibir páginas no idioma \nde maior preferência do usuário, você pode usar o método de negociação \n[[yii\\web\\Request::getPreferredLanguage()]]. Este método pega uma lista de \nidiomas suportadas pela sua aplicação e compara com \n[[yii\\web\\Request::acceptableLanguages|acceptableLanguages]], para retornar o \nidioma mais adequado.\n\n> Dica: Você também pode utilizar o filtro [[yii\\filters\\ContentNegotiator|ContentNegotiator]] \n  para determinar dinamicamente qual tipo de conteúdo e idioma que deve ser utilizado \n  na resposta. O filtro implementa negociação de conteúdo em cima das propriedades \n  e métodos descritos acima.\n\n\n## Informações do Cliente <span id=\"client-information\"></span>\n\nVocê pode obter o nome do domínio ou endereço IP da máquina do cliente através \ndas propriedades [[yii\\web\\Request::userHost|userHost]] e \n[[yii\\web\\Request::userIP|userIP]], respectivamente. Por exemplo,\n\n```php\n$userHost = Yii::$app->request->userHost;\n$userIP = Yii::$app->request->userIP;\n```\n"
  },
  {
    "path": "docs/guide-pt-BR/runtime-routing.md",
    "content": "Roteamento e Criação de URL\n===========================\n\nQuando uma aplicação Yii começa a processar uma URL requerida, o primeiro passo \nnecessário é obter a rota pela análise da URL. A rota é usada para instanciar o \n[controlador (controller) da ação](structure-controllers.md) correspondente para \nmanipular a requisição. Todo este processo é chamado de *roteamento*.\n \nO processo inverso do roteamento é chamada de *criação de URL*, onde é criado uma \nURL a partir de uma determinada rota e seus parâmetros. Quando a URL criada for \nexigida em outro momento, o processo de roteamento pode resolve-la de volta para \na rota original e seus parâmetros.\n\nO ponto central responsável pelo roteamento e pela criação de URL é o \n[[yii\\web\\UrlManager|gerenciador de URL]], na qual é registrado como o \n[componente da aplicação](structure-application-components.md) `urlManager`. O \n[[yii\\web\\UrlManager|gerenciador de URL]] fornece o método \n[[yii\\web\\UrlManager::parseRequest()|parseRequest()]] para analisar um requisição \nde entrada a fim de obter uma rota e seus parâmetros associados; e o método \n[[yii\\web\\UrlManager::createUrl()|createUrl()]] para criar uma URL a partir de \numa rota e seus parâmetros associados.\n\nAo configurar o componente `urlManager` na configuração da aplicação, poderá \nfazer com que sua aplicação reconheça de forma arbitrária diversos formatos de \nURL sem modificar o código existente da aplicação. Por exemplo, você pode usar o \ncódigo a seguir para criar uma URL a partir da ação `post/view`:\n\n```php\nuse yii\\helpers\\Url;\n\n// Url::to() chama UrlManager::createUrl() para criar uma URL\n$url = Url::to(['post/view', 'id' => 100]);\n```\n\nDependendo da configuração da propriedade `urlManager`, a URL criada pode ser \nparecida com um dos formatos a seguir (ou até mesmo com outro formato). E se a \nURL criada for requerida, ainda será analisada a fim de obter a rota e os valores \ndos parâmetros originais.\n\n```\n/index.php?r=post/view&id=100\n/index.php/post/100\n/posts/100\n```\n\n\n## Formatos de URL <span id=\"url-formats\"></span>\n\nO [[yii\\web\\UrlManager|gerenciador de URL]] suporta dois formatos de URL: o \nformato de URL padrão e o formato de URL amigável (pretty URL).\n\nO formato de URL padrão usa um parâmetro chamado `r` para representar a rota e \nos demais parâmetros representam os parâmetros associados a rota. Por exemplo, a \nURL `/index.php?r=post/view&id=100` representa a rota `post/view` e o parâmetro \n`id` com o valor 100. O formato de URL padrão não exige qualquer tipo de \nconfiguração no [[yii\\web\\UrlManager|gerenciador de URL]] e trabalha em qualquer \nservidor Web.\n\nO formato de URL amigável (pretty URL) usa um caminho adicional após o nome do \nscript de entrada para representar a rota e seus parâmetros. Por exemplo, o \ncaminho adicional na URL `/index.php/post/100` é `/post/100`, onde pode \nrepresentar, em uma adequada [[yii\\web\\UrlManager::rules|regra de URL]], a rota \n`post/view` e o parâmetro `id` com o valor 100. Para usar o formato de URL \namigável (pretty URL), você precisará escrever um conjunto de \n[[yii\\web\\UrlManager::rules|regras de URLs]] de acordo com a necessidade sobre \ncomo as URLs devem parecer.\n \nVocê pode alterar entre os dois formatos de URLs, alternando a propriedade \n[[yii\\web\\UrlManager::enablePrettyUrl|enablePrettyUrl]] do \n[[yii\\web\\UrlManager|gerenciador de URL]] sem alterar qualquer código na aplicação.\n\n\n## Roteamento <span id=\"routing\"></span>\n\nO roteamento envolve duas etapas. Na primeira etapa, a requisição de entrada é \ntransformada em uma rota e seus parâmetros. Na segunda etapa, a \n[ação do controller (controlador)](structure-controllers.md) correspondente a \nrota analisada será criada para processar a requisição.\n\nAo utilizar o formato de URL padrão, a análise de uma requisição para obter uma \nrota é tão simples como pegar o valor via `GET` do parâmetro `r`.\n\nAo utilizar o formato de URL amigável (pretty URL), o \n[[yii\\web\\UrlManager|gerenciado de URL]] examinará as \n[[yii\\web\\UrlManager::rules|regras de URLs]] registradas para encontrar alguma \ncorrespondência que poderá resolver a requisição em uma rota. Se tal regra não \nfor encontrada, uma exceção [[yii\\web\\NotFoundHttpException]] será lançada.\n\nUma vez que a requisição analisada apresentar uma rota, é hora de criar a ação \ndo controller (controlador) identificado pela rota.\nA rota é dividida em várias partes pelo separador barra (“/”). Por exemplo, a \nrota `site/index` será dividida em duas partes: `site` e `index`. Cada parte é \num ID que pode referenciar a um módulo, um controller (controlador) ou uma ação.\nA partir da primeira parte da rota, a aplicação executará as seguintes etapas para \ncriar o módulo (se existir), o controller (controlador) e a ação:\n\n1. Define a aplicação como o módulo atual.\n2. Verifica se o [[yii\\base\\Module::controllerMap|mapa do controller (controlador)]] \n   do módulo contém o ID atual. Caso exista, um objeto do controller (controlador) \n   será criado de acordo com a configuração do controller (controlador) encontrado \n   no mapa e a etapa 3 e 4 não serão executadas.   \n3. Verifica se o ID referência a um módulo listado na propriedade \n   [[yii\\base\\Module::modules|modules]] do módulo atual. Caso exista, um módulo \n   será criado de acordo com as configurações encontradas na lista e a etapa 2 \n   será executada como etapa seguinte do processo, no âmbito de usar o contexto \n   do módulo recém-criado.\n4. Trata o ID como um ID do controller (controlador) e cria um objeto do controller \n   (controlador). Siga para a próxima etapa, como parte restante do processo.\n5. O controller (controlador) procura o ID atual em seu \n   [[yii\\base\\Controller::actions()|mapa de ações]]. Caso exista, será criado uma \n   ação de acordo com a configuração encontrada no mapa. Caso contrário, o \n   controller (controlador) tentará criar uma ação inline que é definida por um \n   método da ação correspondente ao ID atual.\n\nNas etapas acima, se ocorrer qualquer erro, uma exceção [[yii\\web\\NotFoundHttpException]] \nserá lançada, indicando a falha no processo de roteamento.\n\n\n### Rota Padrão <span id=\"default-route\"></span>\n\nQuando uma requisição analisada apresentar uma rota vazia, a assim chamada \n*rota padrão* será usada em seu lugar. A rota padrão é `site/index` é utilizada \ncomo padrão, que referencia a ação `index` do controller (controlador) `site`.\nVocê pode personalizar esta configuração pela propriedade \n[[yii\\web\\Application::defaultRoute|defaultRoute]] na configuração da aplicação \ncomo mostrado a seguir:\n\n```php\n[\n    // ...\n    'defaultRoute' => 'main/index',\n];\n```\n\n\n### Rota `catchAll` <span id=\"catchall-route\"></span>\n\nÀs vezes você pode querer colocar sua aplicação Web em modo de manutenção \ntemporariamente e exibir uma mesma página com informações para todas as requisições. \nExistem muitas maneiras de atingir este objetivo. Mas uma das maneiras mais simples \né apenas configurar a propriedade [[yii\\web\\Application::catchAll]] na configuração \nda aplicação como mostrado a seguir:\n\n```php\n[\n    // ...\n    'catchAll' => ['site/offline'],\n];\n```\n\nNa configuração acima, a ação `site/offline` será utilizado para lidar com todas \nas requisições recebidas.\n\nA propriedade `catchAll` deve receber um array, o primeiro elemento especifica a \nrota e o restante dos elementos (pares de nomes e seus valores) especificam os \nparâmetros a serem [associados a ação](structure-controllers.md#action-parameters).\n\n\n## Criando URLs <span id=\"creating-urls\"></span>\n\nO Yii fornece um método de ajuda [[yii\\helpers\\Url::to()]] para criar vários tipos \nde URLs a partir de determinadas rotas e seus parâmetros. Por exemplo,\n\n```php\nuse yii\\helpers\\Url;\n\n// cria uma URL para uma rota: /index.php?r=post/index\necho Url::to(['post/index']);\n\n// cria uma URL para uma rota com parâmetros: /index.php?r=post/view&id=100\necho Url::to(['post/view', 'id' => 100]);\n\n// cria uma URL ancorada: /index.php?r=post/view&id=100#content\necho Url::to(['post/view', 'id' => 100, '#' => 'content']);\n\n// cria uma URL absoluta: https://www.example.com/index.php?r=post/index\necho Url::to(['post/index'], true);\n\n// cria uma URL absoluta usando https: https://www.example.com/index.php?r=post/index\necho Url::to(['post/index'], 'https');\n```\n\nObserve que nos exemplos acima, assumimos o formato de URL padrão. Se o formato \nde URL amigável (pretty URL) estiver habilitado, as URLs criadas serão diferentes \nde acordo com as [[yii\\web\\UrlManager::rules|regra de URL]] em uso. \n\nA rota passada para o método [[yii\\helpers\\Url::to()]] é sensível ao contexto. \nEle pode ser tanto uma rota *relativa* quanto uma rota *absoluta* que será \nnormalizada de acordo com as regras a seguir:\n\n- Se a rota for uma string vazia, a requisição atual [[yii\\web\\Controller::route|route]] será usada;\n- Se a rota não contivér barras (“/”), ele será considerado um ID da ação do \n  controller (controlador) atual e será antecedido pelo valor da propriedade \n  [[\\yii\\web\\Controller::uniqueId|uniqueId]] do controller (controlador) atual;\n- Se a rota não contivér uma barra (“/”) inicial, será considerado como uma rota\n  relativa ao módulo atual e será antecedido pelo valor da propriedade \n  [[\\yii\\base\\Module::uniqueId|uniqueId]] do módulo atual.\n\nA partir da versão 2.0.2, você pode especificar uma rota usando \n[alias](concept-aliases.md). Se for este o caso, a alias será a primeira a ser \nconvertida em uma rota real e em seguida será transformada em uma rota absoluta \nde acordo com as regras informadas anteriormente.\n\nPor exemplo, assumindo que o módulo atual é `admin` e o controller (controlador) \natual é `post`,\n\n```php\nuse yii\\helpers\\Url;\n\n// rota atual requerida: /index.php?r=admin/post/index\necho Url::to(['']);\n\n// uma rota relativa com apenas o ID da ação: /index.php?r=admin/post/index\necho Url::to(['index']);\n\n// uma rota relativa: /index.php?r=admin/post/index\necho Url::to(['post/index']);\n\n// uma rota absoluta: /index.php?r=post/index\necho Url::to(['/post/index']);\n\n// /index.php?r=post/index     assumindo que a alias \"@posts\" foi definida como \"/post/index\"\necho Url::to(['@posts']);\n```\n\nO método [[yii\\helpers\\Url::to()]] é implementado através das chamadas dos métodos \n[[yii\\web\\UrlManager::createUrl()|createUrl()]] e \n[[yii\\web\\UrlManager::createAbsoluteUrl()|createAbsoluteUrl()]] do \n[[yii\\web\\UrlManager|gerenciador de URL]].\nNas próximas subseções, iremos explicar como configurar o \n[[yii\\web\\UrlManager|gerenciador de URL]] para personalizar os formatos das URLs criadas.\n\nO método [[yii\\helpers\\Url::to()]] também suporta a criação de URLs NÃO relacionadas \na uma rota em particular. Neste caso, ao invés de passar um array como seu primeiro \nparâmetro, você pode passar uma string. Por exemplo,\n \n```php\nuse yii\\helpers\\Url;\n\n// rota atual requerida: /index.php?r=admin/post/index\necho Url::to();\n\n// uma alias da URL: https://example.com\nYii::setAlias('@example', 'https://example.com/');\necho Url::to('@example');\n\n// uma URL absoluta: https://example.com/images/logo.gif\necho Url::to('/images/logo.gif', true);\n```\n\nAlém do método `to()`, a classe auxiliar [[yii\\helpers\\Url]] também fornece uma \nsérie de métodos referentes a criação de URLs. Por exemplo,\n\n```php\nuse yii\\helpers\\Url;\n\n// URL da página inicial: /index.php?r=site/index\necho Url::home();\n\n// URL base, útil se a aplicação for implementada em uma subpasta da pasta raiz do servidor Web\necho Url::base();\n\n// A URL canônica da requisição atual\n// Veja mais detalhes em https://en.wikipedia.org/wiki/Canonical_link_element\necho Url::canonical();\n\n// Obtêm a URL da requisição anterior para reutilizá-la em requisições futuras\nUrl::remember();\necho Url::previous();\n``` \n\n\n## Usando URLs Amigáveis (Pretty URLs) <span id=\"using-pretty-urls\"></span>\n\nPara utilizar as URLs amigáveis (pretty URLs), configure o componente `urlManager` \nna configuração da aplicação conforme o exemplo a seguir:\n\n```php\n[\n    'components' => [\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            'showScriptName' => false,\n            'enableStrictParsing' => false,\n            'rules' => [\n                // ...\n            ],\n        ],\n    ],\n]\n```\n\nA propriedade [[yii\\web\\UrlManager::enablePrettyUrl|enablePrettyUrl]] é obrigatória \npara habilitar o formato de URL amigável (pretty URL).\nO restante das propriedades são opcionais. No entanto, a configuração utilizada \nacima é a mais utilizada.\n\n* [[yii\\web\\UrlManager::showScriptName|showScriptName]]: esta propriedade determina\n  se o script de entrada deve ser incluído ou não nas URLs criadas. Por exemplo, \n  ao invés de criar uma URL `/index.php/post/100`, definindo esta propriedade \n  como `false`, a URL `/post/100` será gerada.\n* [[yii\\web\\UrlManager::enableStrictParsing|enableStrictParsing]]: esta propriedade \n  habilita uma análise rigorosa (strict parsing) da requisição. Caso a análise \n  rigorosa estiver habilitada, a URL da requisição deve corresponder pelo menos \n  a uma das regras definidas pela propriedade [[yii\\web\\UrlManager::rules|rules]] \n  a fim de ser tratado como uma requisição válida, caso contrário a exceção \n  [[yii\\web\\NotFoundHttpException]] será lançada. Caso a análise rigorosa estiver \n  desabilitada, as regras definidas pela propriedade \n  [[yii\\web\\UrlManager::rules|rules]] NÃO serão verificadas e as informações \n  obtidas pela URL serão tratadas como a rota da requisição.\n* [[yii\\web\\UrlManager::rules|rules]]: esta propriedade contém uma lista de \n  regras especificando como serão analisadas e criadas as URLs. Esta é a principal \n  propriedade que você deve trabalhar para criar URLs cujos formatos satisfaçam \n  a sua exigência em particular.\n\n> Observação: A fim de esconder o nome do script de entrada nas URLs criadas, \n  além de definir a propriedade [[yii\\web\\UrlManager::showScriptName|showScriptName]] \n  como `false`, você também pode precisar configurar o seu servidor Web para \n  identificar corretamente o script PHP quando uma URL da requisição não \n  especificar um explicitamente. Caso você estejam utilizando um servidor Web \n  com Apache, você pode consultar a configuração recomendada, conforme descrito \n  na seção [Installation](start-installation.md#recommended-apache-configuration).\n\n\n### Regras de URLs <span id=\"url-rules\"></span>\n\nUma regra de URL é uma instância de [[yii\\web\\UrlRule]] ou de classes que as estendam. \nCada regra de URL consiste de um padrão usado para combinar as partes do caminho \ndas URLs, como uma rota e alguns parâmetros. Uma regra de URL pode ser usado para \nanalisar uma URL da requisição somente se o padrão corresponder com a esta URL. \nUma regra de URL pode ser usada para criar uma URL para corresponder a uma \ndeterminada rota e seus parâmetros.\n\nQuando uma formato de URL amigável (pretty URL) estiver habilitada, o \n[[yii\\web\\UrlManager|gerenciador de URL]] utilizará as regras de URLs declaradas \nna propriedade [[yii\\web\\UrlManager::rules|rules]] para analisar as requisições \ne para criar URLs. Em particular, para analisar uma requisição, o \n[[yii\\web\\UrlManager|gerenciador de URL]] verificará as regras na ordem em que \nforam declaradas e só enxergará a *primeira* regra que corresponda a URL da \nrequisição. A regra que foi correspondida é então utilizada para obter a rota e \nseus parâmetros a partir de sua análise. A mesma coisa acontece na criação de\nURLs, o [[yii\\web\\UrlManager|gerenciador de URL]] enxergará apenas a primeira \nregra que corresponda a rota e seus parâmetros para serem utilizados na criação \nde uma URL.\n\nVocê pode configurar a propriedade [[yii\\web\\UrlManager::rules]] com um array \ncomposto de pares de chave-valor, onde a chave é o padrão da regra e o valor \nserão as rotas. Cada par de padrão-rota define uma regra de URL. Por exemplo, \nas [[yii\\web\\UrlManager::rules|regras]] a seguir configuram duas regras de URL.\nA primeira regra corresponde a uma URL chamada `posts` sendo mapeado para utilizar \na rota `post/index`.\nA segunda regra corresponde a uma URL que combine com expressão regular \n`post/(\\d+)` seguido de um parâmetro chamado `id` sendo mapeado para utilizar a\nrota `post/view`.\n\n```php\n[\n    'posts' => 'post/index', \n    'post/<id:\\d+>' => 'post/view',\n]\n```\n\n> Informação: O padrão em uma regra é usado para identificar o caminho de uma URL. \n  Por exemplo, o caminho da URL `/index.php/post/100?source=ad` é `post/100` \n  (as barras (“/”) iniciais e finais serão ignoradas) combinando com o padrão `post/(\\d+)`.\n\nAlém de declarar regras de URL como pares de padrão-rota, você também pode declarar \ncomo array. Cada array é utilizado para configurar um único objeto da regra de URL. \nIsto se faz necessário quando você deseja configurar outras propriedades de uma \nregra de URL. Por exemplo,\n\n```php\n[\n    // ...outras regras de URL...\n    \n    [\n        'pattern' => 'posts',\n        'route' => 'post/index',\n        'suffix' => '.json',\n    ],\n]\n```\n\nPor padrão, se você não especificar a opção `class` na configuração de uma regra, \nserá utilizado a classe [[yii\\web\\UrlRule]].\n\n\n### Parâmetros Nomeados <span id=\"named-parameters\"></span>\n\nUma regra de URL pode ser associado a alguns parâmetros nomeados que são \nespecificados no padrão `<ParamName:RegExp>`, onde o `ParamName` especifica o \nnome do parâmetro e o `RegExp` especifica uma expressão regular opcional usada \npara corresponder ao valor do parâmetro. Se o `RegExp` são for especificado, \nsignificará que o valor do parâmetro será uma string sem barras (“/”).\n\n> Observação: Você apenas pode especificar expressões regulares para os parâmetros. \n  As demais partes de um padrão serão considerados como um texto simples.\n\nQuando esta regra for utilizada para analisar uma URL, os parâmetros associados \nserão preenchidos com os valores que foram correspondidos pela regra e estes \nparâmetros serão disponibilizados logo a seguir no `$_GET` pelo componente da \naplicação `request`.\n\nVamos utilizar alguns exemplos para demonstrar como os parâmetros nomeados \nfuncionam. Supondo que declaramos as três regras a seguir:\n\n```php\n[\n    'posts/<year:\\d{4}>/<category>' => 'post/index',\n    'posts' => 'post/index',\n    'post/<id:\\d+>' => 'post/view',\n]\n```\n\nQuando as regras forem utilizadas para analisar as URLs:\n\n- `/index.php/posts` obterá a rota `post/index` usando a segunda regra;\n- `/index.php/posts/2014/php` obterá a rota `post/index`, o parâmetro `year` \n  cujo o valor é 2014 e o parâmetro `category` cujo valor é `php` usando a primeira regra;\n- `/index.php/post/100` obterá a rota `post/view` e o parâmetro `id` cujo valor  \n  é 100 usando a terceira regra;\n- `/index.php/posts/php` causará uma exceção [[yii\\web\\NotFoundHttpException]] \n  quando a propriedade [[yii\\web\\UrlManager::enableStrictParsing]] for `true`, \n  por não ter correspondido a nenhum dos padrões. Se a propriedade \n  [[yii\\web\\UrlManager::enableStrictParsing]] for `false` (o valor padrão), o \n  caminho `posts/php` será retornado como uma rota.\n \nE quando as regras fores utilizadas para criar as URLs:\n\n- `Url::to(['post/index'])` cria `/index.php/posts` usando a segunda regra;\n- `Url::to(['post/index', 'year' => 2014, 'category' => 'php'])` cria `/index.php/posts/2014/php` usando a primeira regra;\n- `Url::to(['post/view', 'id' => 100])` cria `/index.php/post/100` usando a terceira regra;\n- `Url::to(['post/view', 'id' => 100, 'source' => 'ad'])` cria `/index.php/post/100?source=ad` usando a terceira regra.\n  Pela razão do parâmetro `source` não foi especificado na regra, ele será acrescentado como uma query string na criação da URL.\n- `Url::to(['post/index', 'category' => 'php'])` cria `/index.php/post/index?category=php` usando nenhuma das regras.\n  Observe que, se nenhuma das regras forem aplicadas, a URL será criada simplesmente \n  como a rota sendo o caminho e todos os parâmetros como query string.\n\n\n### Parametrizando Rotas <span id=\"parameterizing-routes\"></span>\n\nVocê pode incorporar nomes de parâmetros na rota de uma regra de URL. Isto permite \nque uma regra de URL seja utilizada para combinar diversas rotas. Por exemplo, a \nregra a seguir incorpora os parâmetros `controller` e `action` nas rotas.\n\n```php\n[\n    '<controller:(post|comment)>/<id:\\d+>/<action:(create|update|delete)>' => '<controller>/<action>',\n    '<controller:(post|comment)>/<id:\\d+>' => '<controller>/view',\n    '<controller:(post|comment)>s' => '<controller>/index',\n]\n```\n\nPara analisar uma URL `/index.php/comment/100/create`, a primeira regra será \naplicada, na qual foi definida o parâmetro `controller` para ser `comment` e o \nparâmetro `action` para ser `create`. Sendo assim, a rota `<controller>/<action>` \né resolvida como `comment/create`.\n \nDe forma similar, para criar uma URL com a rota `comment/index`, a terceira regra \nserá aplicada, criando um URL `/index.php/comments`.\n\n> Informação: Pela parametrização de rotas, é possível reduzir significativamente \n  o número de regras de URL, que também pode melhorar o desempenho do \n  [[yii\\web\\UrlManager|gerenciador de URL]]. \n  \nPor padrão, todos os parâmetros declarados nas regras são obrigatórios. Se uma\nURL da requisição não contiver um dos parâmetros em particular, ou se a URL está \nsendo criado sem um dos parâmetros em particular, a regra não será aplicada. Para \nfazer com que algum parâmetro em particular seja opcional, você pode configurar \na propriedade [[yii\\web\\UrlRule::defaults|defaults]] da regra. Os parâmetros\nlistados nesta propriedade são opcionais e serão utilizados quando os mesmos não \nforem fornecidos.\n\nA declaração da regra a seguir, ambos os parâmetros `page` e `tag` são opcionais \ne utilizarão o valor 1 e a string vazia, respectivamente, quando não forem fornecidos.\n\n```php\n[\n    // ...outras regras...\n    [\n        'pattern' => 'posts/<page:\\d+>/<tag>',\n        'route' => 'post/index',\n        'defaults' => ['page' => 1, 'tag' => ''],\n    ],\n]\n```\n\nA regra anterior pode ser usado para analisar ou criar qualquer uma das seguintes URLs:\n\n* `/index.php/posts`: `page` é 1, `tag` é ''.\n* `/index.php/posts/2`: `page` é 2, `tag` is ''.\n* `/index.php/posts/2/news`: `page` é 2, `tag` é `'news'`.\n* `/index.php/posts/news`: `page` é 1, `tag` é `'news'`.\n\nSem o uso dos parâmetros opcionais, você deveria criar 4 regras para alcançar o \nmesmo resultado.\n\n\n### Regras com Domínios <span id=\"rules-with-server-names\"></span>\n\nÉ possível incluir domínios nos padrões das regras de URL. Isto é útil quando sua\naplicação se comporta de forma diferente em diferentes domínios. Por exemplo, a \nregra a seguir obtém a rota `admin/user/login` pela análise da URL \n`https://admin.example.com/login` e a rota `site/login` pela análise da URL \n`https://www.example.com/login`.\n\n```php\n[\n    'https://admin.example.com/login' => 'admin/user/login',\n    'https://www.example.com/login' => 'site/login',\n]\n```\n\nVocê também pode incorporar parâmetros nos domínios para extrair informações \ndinamicamente a partir deles. Por exemplo, a regra a seguir obtém a rota \n`post/index` e o parâmetro `language=en` pela análise da URL `https://en.example.com/posts`\n\n```php\n[\n    'http://<language:\\w+>.example.com/posts' => 'post/index',\n]\n```\n\n> Observação: Regras com domínios NÃO devem ser incluídos com subpastas do script \n  de entrada em seus padrões. Por exemplo, se a aplicação estiver sob \n  `https://www.example.com/sandbox/blog`, você deve usar o padrão \n  `https://www.example.com/posts` ao invés de `https://www.example.com/sandbox/blog/posts`. \n  Isto permite que sua aplicação seja implantado sob qualquer diretório sem a \n  necessidade de alterar o código da aplicação.\n\n\n### Sufixos da URL <span id=\"url-suffixes\"></span>\n\nVocê pode querer adicionar sufixos nas URLs para diversos fins. Por exemplo, \nvocê pode adicionar o `.html` nas URLs para que se pareçam com páginas estáticas. \nVocê também pode adicionar o `.json` nas URLs para indicar o tipo de conteúdo \nesperado na resposta. Você pode alcançar este objetivo configurando a propriedade \n[[yii\\web\\UrlManager::suffix]] na configuração da aplicação conforme o exemplo a seguir:\n\n```php\n[\n    'components' => [\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            'showScriptName' => false,\n            'enableStrictParsing' => true,\n            'suffix' => '.html',\n            'rules' => [\n                // ...\n            ],\n        ],\n    ],\n]\n```\n\nA configuração anterior permitirá que o [[yii\\web\\UrlManager|gerenciador de URL]] \nreconheçam as URLs solicitadas e que também criem URLs com o prefixo `.html`.\n\n> Dica: Você pode definir `/` como o sufixo para que todas as URLs terminem com barra.\n\n> Observação: Ao configurar um sufixo da URL e a URL da requisição não conter um, \n  será considerado como uma URL não válida. Isto é uma prática recomendada no \n  SEO (otimização para mecanismos de pesquisa, do *inglês search engine optimization*).\n\nÁs vezes você poder querer utilizar diferentes sufixos para diferentes URLs.\nIsto pode ser alcançado pela configuração da propriedade \n[[yii\\web\\UrlRule::suffix|suffix]] individualmente para cada regra de URL. \nQuando uma regra de URL possuir esta propriedade definida, sobrescreverá o \nsufixo que foi definido da camada do [[yii\\web\\UrlManager|gerenciador de URL]]. \nPor exemplo, a configuração a seguir contém uma regra de URL personalizada que \nusa o `.json` como sufixo ao invés do sufixo `.html` definido globalmente.\n\n```php\n[\n    'components' => [\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            'showScriptName' => false,\n            'enableStrictParsing' => true,\n            'suffix' => '.html',\n            'rules' => [\n                // ...\n                [\n                    'pattern' => 'posts',\n                    'route' => 'post/index',\n                    'suffix' => '.json',\n                ],\n            ],\n        ],\n    ],\n]\n```\n\n\n### Métodos HTTP <span id=\"http-methods\"></span>\n\nAo implementar RESTful API, é necessário que sejam obtidas rotas diferentes pela \nanálise de uma mesma URL de acordo com o método HTTP utilizado. Isto pode ser \nalcançado facilmente adicionando o prefixo do método HTTP suportado, separando \nos nomes dos métodos por vírgulas. Por exemplo, a regra a seguir possui o mesmo \npadrão `post/<id:\\d+>` com suporte a diferentes métodos HTTP. A análise de uma\nrequisição `PUT post/100` obterá a rota `post/create`, enquanto a requisição \n`GET post/100` obterá a rota `post/view`.\n\n```php\n[\n    'PUT,POST post/<id:\\d+>' => 'post/create',\n    'DELETE post/<id:\\d+>' => 'post/delete',\n    'post/<id:\\d+>' => 'post/view',\n]\n```\n\n> Observação: Se uma regra de URL contiver método(s) HTTP, esta regra só será \nutilizada para análises de URLs. A regra será ignorada quando o \n[[yii\\web\\UrlManager|gerenciador de URL]] for chamado para criar URLs.\n\n> Dica: Para simplificar o roteamento do RESTful APIs, o Yii fornece uma classe \nespecial [[yii\\rest\\UrlRule]] de regras que é muito diferente. Esta classe \nsuporta muitos recursos como a pluralização automática de IDs do controller \n(controlador). Para mais detalhes, por favor, consulte a seção \n[Routing](rest-routing.md) sobre o desenvolvimento de RESTful APIs.\n\n\n### Regras Personalizadas <span id=\"customizing-rules\"></span>\n\nNos exemplo anteriores, as regras de URL são declaradas principalmente no formato\nde pares de padrão-rota. Este é um formato de atalho bastante utilizado. Em \nalguns cenários, você pode querer personalizar uma regra de URL configurando \noutras propriedades, tais como o [[yii\\web\\UrlRule::suffix]]. Isto pode ser \nfeito utilizando um array de configuração para especificar uma regra. O exemplo \na seguir foi retirado da subseção [Sufixos da URL](#url-suffixes),\n\n```php\n[\n    // ...outras regras de URL...\n    \n    [\n        'pattern' => 'posts',\n        'route' => 'post/index',\n        'suffix' => '.json',\n    ],\n]\n```\n\n> Informações: Por padrão, se você não especificar a opção `class` na configuração \n  de uma regra, será usado como padrão a classe [[yii\\web\\UrlRule]].\n  \n\n### Adicionando Regras Dinamicamente <span id=\"adding-rules\"></span>\n\nAs regras de URL podem ser adicionadas dinamicamente ao [[yii\\web\\UrlManager|gerenciador de URL]]. \nEsta técnica muitas vezes se faz necessária em [módulos](structure-modules.md) que \nsão redistribuídos e que desejam gerenciar as suas próprias regras de URL. Para \nque estas regras sejam adicionadas dinamicamente e terem efeito durante o processo \nde roteamento, você pode adiciona-los durante a [inicialização (bootstrapping)](runtime-bootstrapping.md). \nPara os módulos, significa que deve implementar a interface [[yii\\base\\BootstrapInterface]] \ne adicionar as regras no método [[yii\\base\\BootstrapInterface::bootstrap()|bootstrap()]] \nconforme o exemplo a seguir:\n\n```php\npublic function bootstrap($app)\n{\n    $app->getUrlManager()->addRules([\n        // declare as regras aqui\n    ], false);\n}\n```\n\nObserve que você também deve listar estes módulos no [[yii\\web\\Application::bootstrap]]\npara que eles sejam usados no processo de [inicialização (bootstrapping)](runtime-bootstrapping.md)\n\n\n### Criando Classes de Regras <span id=\"creating-rules\"></span>\n\nApesar do fato que a classe padrão [[yii\\web\\UrlRule]] é flexível o suficiente \npara a maior parte dos projetos, há situações em que você terá que criar a sua \nprópria classe de regra. Por exemplo, em um site de venda de carros, você pode \nquerer dar suporte a um formato de URL como `/Manufacturer/Model`, que tanto o \n`Manufacturer` quanto o `Model` devem coincidir com os dados armazenados em uma \ntabela do banco de dados. A classe de regra padrão não vai funcionar nesta \nsituação pois vão se basear em padrões estaticamente declarados.\n\nPodemos criar uma classe de regra de URL para resolver este formato.\n\n```php\nnamespace app\\components;\n\nuse yii\\web\\UrlRuleInterface;\nuse yii\\base\\BaseObject;\n\nclass CarUrlRule extends BaseObject implements UrlRuleInterface\n{\n\n    public function createUrl($manager, $route, $params)\n    {\n        if ($route === 'car/index') {\n            if (isset($params['manufacturer'], $params['model'])) {\n                return $params['manufacturer'] . '/' . $params['model'];\n            } elseif (isset($params['manufacturer'])) {\n                return $params['manufacturer'];\n            }\n        }\n        return false;  // esta regra não se aplica\n    }\n\n    public function parseRequest($manager, $request)\n    {\n        $pathInfo = $request->getPathInfo();\n        if (preg_match('%^(\\w+)(/(\\w+))?$%', $pathInfo, $matches)) {\n            // checa o $matches[1] e $matches[3] para verificar\n            // se coincidem com um *fabricante* e um *modelo* no banco de dados.\n            // Caso coincida, define o $params['manufacturer'] e/ou $params['model']\n            // e retorna ['car/index', $params]\n        }\n        return false;  // esta regra não se aplica\n    }\n}\n```\n\nE utilize esta nova classe de regra na configuração [[yii\\web\\UrlManager::rules]]:\n\n```php\n[\n    // ...outras regras...\n    \n    [\n        'class' => 'app\\components\\CarUrlRule', \n        // ...configurar outras propriedades...\n    ],\n]\n```\n\n\n## Considerando Performance <span id=\"performance-consideration\"></span>\n\nAo desenvolver uma aplicação Web complexa, é importante otimizar as regras de URL \npara que leve menos tempo na análise de requisições e criação de URLs.\n\nUtilizando rotas parametrizadas, você reduz o número de regras de URL, na qual \npode melhorar significativamente o desempenho.\n\nNa análise e criação de URLs, o [[yii\\web\\UrlManager|gerenciador de URL]] examina \nas regras de URL na ordem em que foram declaradas.\nPortanto, você pode considerar ajustar a ordem destas regras, fazendo com que as\nregras mais específicas e/ou mais comuns sejam colocadas antes que os menos.\n\nSe algumas regras de URL compartilharem o mesmo prefixo em seus padrões ou rotas, \nvocê pode considerar utilizar o [[yii\\web\\GroupUrlRule]] para que sejam examinados \nde forma mais eficiente pelo [[yii\\web\\UrlManager|gerenciador de URL]] como um \ngrupo. Normalmente é o caso de aplicações compostos por módulos, onde cada módulo\npossui o seu próprio conjunto de regras de URL utilizando o ID do módulo como \nprefixo comum.\n"
  },
  {
    "path": "docs/guide-pt-BR/runtime-sessions-cookies.md",
    "content": "Sessões e Cookies\n====================\n\nSessões e cookies permitem que dados sejam persistentes entre várias requisições de usuários. No PHP puro você pode acessá-los através das variáveis globais `$_SESSION` e `$_COOKIE`, respectivamente. Yii encapsula sessões e cookies como objetos e portanto, permite que você possa acessá-los de um modo orientado à objetos com melhorias adicionais úteis.\n\n\n## Sessões<span id=\"sessions\"></span>\n\nAssim como [requisições](runtime-requests.md) e [respostas](runtime-responses.md), você pode ter acesso a sessões através do [componente de aplicação](structure-application-components.md) `session` que é uma instância de [[yii\\web\\Session]], por padrão.\n\n\n### Abrindo e Fechando Sessões <span id=\"opening-closing-sessions\"></span>\n\nPara abrir e fechar uma sessão, você pode fazer o seguinte:\n\n```php\n$session = Yii::$app->session;\n\n// verifica se a sessão está pronta para abrir\nif ($session->isActive) ...\n\n// abre uma sessão\n$session->open();\n\n// fecha uma sessão\n$session->close();\n\n// destrói todos os dados registrados em uma sessão.\n$session->destroy();\n```\n\nVocê pode chamar [[yii\\web\\Session::open()|open()]] and [[yii\\web\\Session::close()|close()]] várias vezes, sem causar erros; internamente os métodos irão verificar primeiro se a sessão já está aberta.\n\n\n### Acessando Dados da Sessão <span id=\"access-session-data\"></span>\n\nPara acessar os dados armazenados em sessão, você pode fazer o seguinte:\n\n```php\n$session = Yii::$app->session;\n\n// obter uma variável de sessão. Os exemplos abaixo são equivalentes:\n$language = $session->get('language');\n$language = $session['language'];\n$language = isset($_SESSION['language']) ? $_SESSION['language'] : null;\n\n// definir uma variável de sessão. Os exemplos abaixo são equivalentes:\n$session->set('language', 'en-US');\n$session['language'] = 'en-US';\n$_SESSION['language'] = 'en-US';\n\n// remover uma variável de sessão. Os exemplos abaixo são equivalentes:\n$session->remove('language');\nunset($session['language']);\nunset($_SESSION['language']);\n\n// verifica se a variável de sessão existe. Os exemplos abaixo são equivalentes:\nif ($session->has('language')) ...\nif (isset($session['language'])) ...\nif (isset($_SESSION['language'])) ...\n\n// percorrer todas as variáveis de sessão. Os exemplos abaixo são equivalentes:\nforeach ($session as $name => $value) ...\nforeach ($_SESSION as $name => $value) ...\n```\n\n> Observação: Quando você acessa os dados da sessão através do componente `session`, uma sessão será automaticamente aberta caso não tenha sido feito antes. Isso é diferente de acessar dados da sessão através de `$_SESSION`, o que requer uma chamada explícita de `session_start()`.\n\nAo trabalhar com dados de sessão que são arrays, o componente `session` tem uma limitação que o impede de modificar diretamente um elemento do array. Por exemplo,\n\n```php\n$session = Yii::$app->session;\n\n// o seguinte código não funciona\n$session['captcha']['number'] = 5;\n$session['captcha']['lifetime'] = 3600;\n\n// o seguinte código funciona:\n$session['captcha'] = [\n    'number' => 5,\n    'lifetime' => 3600,\n];\n\n// o seguinte código também funciona:\necho $session['captcha']['lifetime'];\n```\n\nVocê pode usar uma das seguintes soluções para resolver este problema:\n\n```php\n$session = Yii::$app->session;\n\n// use diretamente $_SESSION (certifique-se que Yii::$app->session->open() tenha sido chamado)\n$_SESSION['captcha']['number'] = 5;\n$_SESSION['captcha']['lifetime'] = 3600;\n\n// obter todo o array primeiro, modificá-lo e depois salvá-lo\n$captcha = $session['captcha'];\n$captcha['number'] = 5;\n$captcha['lifetime'] = 3600;\n$session['captcha'] = $captcha;\n\n// use ArrayObject em vez de array\n$session['captcha'] = new \\ArrayObject;\n...\n$session['captcha']['number'] = 5;\n$session['captcha']['lifetime'] = 3600;\n\n// armazenar dados de array utilizando chaves com um prefixo comum\n$session['captcha.number'] = 5;\n$session['captcha.lifetime'] = 3600;\n```\n\nPara um melhor desempenho e legibilidade do código, recomendamos a última solução alternativa. Isto é, em vez de armazenar um array como uma variável de sessão única, você armazena cada elemento do array como uma variável de sessão que compartilha o mesmo prefixo de chave com outros elementos do array.\n\n\n### Armazenamento de Sessão Personalizado <span id=\"custom-session-storage\"></span>\n\nA classe padrão [[yii\\web\\Session]] armazena dados da sessão como arquivos no servidor. Yii Também fornece as seguintes classes de sessão implementando diferentes formas de armazenamento:\n\n* [[yii\\web\\DbSession]]: armazena dados de sessão em uma tabela no banco de dados.\n* [[yii\\web\\CacheSession]]: armazena dados de sessão em um cache com a ajuda de um [cache component](caching-data.md#cache-components) configurado.\n* [[yii\\redis\\Session]]: armazena dados de sessão utilizando [redis](https://redis.io/) como meio de armazenamento.\n* [[yii\\mongodb\\Session]]: armazena dados de sessão em um [MongoDB](https://www.mongodb.com/).\n\nTodas essas classes de sessão suportam o mesmo conjunto de métodos da API. Como resultado, você pode mudar para uma classe de armazenamento de sessão diferente, sem a necessidade de modificar o código da aplicação que usa sessões.\n\n> Observação: Se você deseja acessar dados de sessão via`$_SESSION` enquanto estiver usando armazenamento de sessão personalizado, você deve certificar-se de que a sessão já foi iniciada por [[yii\\web\\Session::open()]]. Isso ocorre porque os manipuladores de armazenamento de sessão personalizada são registrados dentro deste método.\n\nPara saber como configurar e usar essas classes de componentes, por favor consulte a sua documentação da API. Abaixo está um exemplo que mostra como configurar [[yii\\web\\DbSession]] na configuração da aplicação para usar uma tabela do banco de dados para armazenamento de sessão:\n\n```php\nreturn [\n    'components' => [\n        'session' => [\n            'class' => 'yii\\web\\DbSession',\n            // 'db' => 'mydb',  // ID do componente de aplicação da conexão DB. O padrão é 'db'.\n            // 'sessionTable' => 'my_session', // nome da tabela de sessão. O padrão é 'session'.\n        ],\n    ],\n];\n```\n\nVocê também precisa criar a tabela de banco de dados a seguir para armazenar dados de sessão:\n\n```sql\nCREATE TABLE session\n(\n    id CHAR(40) NOT NULL PRIMARY KEY,\n    expire INTEGER,\n    data BLOB\n)\n```\nonde 'BLOB' refere-se ao tipo BLOB do seu DBMS preferido. Estes são os tipos de BLOB que podem ser usados para alguns SGBD populares:\n\n- MySQL: LONGBLOB\n- PostgreSQL: BYTEA\n- MSSQL: BLOB\n\n> Observação: De acordo com a configuração `session.hash_function` no php.ini , pode ser necessário ajustar o tamanho da coluna `id`. Por exemplo, se a configuração for `session.hash_function=sha256`, você deve usar um tamanho de 64 em vez de 40.\n\n\n### Dados Flash <span id=\"flash-data\"></span>\n\nDados flash é um tipo especial de dados de sessão que, uma vez posto em uma requisição, só estarão disponíveis durante a próxima requisição e serão automaticamente excluídos depois. Dados flash são mais comumente usados para implementar mensagens que só devem ser exibidas para os usuários finais, uma vez, tal como uma mensagem de confirmação exibida após um usuário submeter um formulário com sucesso.\n\nVocê pode definir e acessar dados de flash através do componente da aplicação `session`. Por exemplo,\n\n```php\n$session = Yii::$app->session;\n\n// Request #1\n// defini uma mensagem flash chamada \"postDeleted\"\n$session->setFlash('postDeleted', 'You have successfully deleted your post.');\n\n// Request #2\n// exibe uma mensagem flash chamada \"postDeleted\"\necho $session->getFlash('postDeleted');\n\n// Request #3\n// $result será falso uma vez que a mensagem flash foi automaticamente excluída\n$result = $session->hasFlash('postDeleted');\n```\n\nAssim como os dados da sessão regular, você pode armazenar dados arbitrários como os dados flash.\n\nQuando você chama [[yii\\web\\Session::setFlash()]], ele substituirá todos os dados flash existente que tenha o mesmo nome.\nPara acrescentar novos dados flash para uma mensagem existente com o mesmo nome, você pode chamá [[yii\\web\\Session::addFlash()]].\nPor exemplo:\n\n```php\n$session = Yii::$app->session;\n\n// Request #1\n// adicionar algumas mensagens flash com o nome de \"alerts\"\n$session->addFlash('alerts', 'You have successfully deleted your post.');\n$session->addFlash('alerts', 'You have successfully added a new friend.');\n$session->addFlash('alerts', 'You are promoted.');\n\n// Request #2\n// $alerts é um array de mensagens flash com o nome de \"alerts\"\n$alerts = $session->getFlash('alerts');\n```\n\n> Observação: Tente não usar [[yii\\web\\Session::setFlash()]] junto com [[yii\\web\\Session::addFlash()]] para dados flash com o mesmo nome. Isto porque o último método coloca os dados flash automaticamente em um array, de modo que ele pode acrescentar novos dados flash com o mesmo nome. Como resultado, quando você chamar [[yii\\web\\Session::getFlash()]], você pode às vezes achar que está recebendo um array, quando na verdade você está recebendo uma string, dependendo da ordem da invocação destes dois métodos.\n\n> Dica: Para mostrar mensagens flash você pode usar o widget [[yii\\bootstrap\\Alert|bootstrap Alert]] da seguinte forma:\n>\n> ```php\n> echo Alert::widget([\n>    'options' => ['class' => 'alert-info'],\n>    'body' => Yii::$app->session->getFlash('postDeleted'),\n> ]);\n> ```\n\n\n## Cookies <span id=\"cookies\"></span>\n\nYii representa cada cookie como um objeto de [[yii\\web\\Cookie]]. Ambos, [[yii\\web\\Request]] e [[yii\\web\\Response]] mantém uma coleção de cookies através da propriedade chamada `cookies`. A coleção de cookie no primeiro representa os cookies submetidos na requisição, enquanto a coleção de cookie no último representa os cookies que são para serem enviados ao usuário.\n\n\n### Lendo Cookies <span id=\"reading-cookies\"></span>\n\nVocê pode obter os cookies na requisição corrente usando o seguinte código:\n\n```php\n// pega a coleção de cookie  (yii\\web\\CookieCollection) do componente \"request\"\n$cookies = Yii::$app->request->cookies;\n\n// pega o valor do cookie \"language\". se o cookie não existir, retorna \"en\" como o valor padrão.\n$language = $cookies->getValue('language', 'en');\n\n// um caminho alternativo para pegar o valor do cookie \"language\"\nif (($cookie = $cookies->get('language')) !== null) {\n    $language = $cookie->value;\n}\n\n// você também pode usar $cookies como um array\nif (isset($cookies['language'])) {\n    $language = $cookies['language']->value;\n}\n\n// verifica se existe um cookie \"language\"\nif ($cookies->has('language')) ...\nif (isset($cookies['language'])) ...\n```\n\n\n### Enviando Cookies <span id=\"sending-cookies\"></span>\n\nVocê pode enviar cookies para o usuário final utilizando o seguinte código:\n\n```php\n// pega a coleção de cookie (yii\\web\\CookieCollection) do componente \"response\"\n$cookies = Yii::$app->response->cookies;\n\n// adicionar um novo cookie a resposta que será enviado\n$cookies->add(new \\yii\\web\\Cookie([\n    'name' => 'language',\n    'value' => 'zh-CN',\n]));\n\n// remove um cookie\n$cookies->remove('language');\n// outra alternativa para remover um cookie\nunset($cookies['language']);\n```\n\nAlém das propriedades [[yii\\web\\Cookie::name|name]], [[yii\\web\\Cookie::value|value]] mostradas nos exemplos acima, a classe [[yii\\web\\Cookie]] também define outras propriedades para representar plenamente todas as informações de cookie disponíveis, tal como [[yii\\web\\Cookie::domain|domain]], [[yii\\web\\Cookie::expire|expire]]. Você pode configurar essas propriedades conforme necessário para preparar um cookie e, em seguida, adicioná-lo à coleção de cookie da resposta.\n\n> Observação: Para melhor segurança, o valor padrão de [[yii\\web\\Cookie::httpOnly]] é definido para `true`. Isso ajuda a reduzir o risco de um script do lado do cliente acessar o cookie protegido (se o browser suporta-lo). Você pode ler o [httpOnly wiki article](https://owasp.org/www-community/HttpOnly) para mais detalhes.\n\n\n### Validação de Cookie <span id=\"cookie-validation\"></span>\n\nQuando você está lendo e enviando cookies através dos componentes `request` e `response` como mostrado nas duas últimas subseções, você aproveita a segurança adicional de validação de cookie que protege que os cookies sejam modificados no lado do cliente. Isto é conseguido através da assinatura de cada cookie com um hash string, que permite a aplicação dizer se o cookie foi modificado no lado do cliente. Se assim for, o cookie não será acessível através do [[yii\\web\\Request::cookies|cookie collection]] do componente `request`.\n\n> Observação: Validação de cookie apenas protege os valores dos cookies de serem modificados. Se um cookie não for validado, você ainda pode acessá-lo através de `$_COOKIE`. Isso ocorre porque as bibliotecas de terceiros podem manipular os cookies de forma independente que não envolve a validação de cookie.\n\nValidação de cookie é habilitada por padrão. Você pode desabilitá-la definindo a propriedade [[yii\\web\\Request::enableCookieValidation]] para `false`, entretanto recomendamos fortemente que você não o faça.\n\n> Observação: Cookies que são enviados/recebidos diretamente através de `$_COOKIE` e `setcookie()` NÃO serão validados.\n\nAo usar a validação de cookie, você deve especificar uma [[yii\\web\\Request::cookieValidationKey]] que será usada para gerar o supracitado hash strings. Você pode fazê-lo através da configuração do componente `request` na configuração da aplicação:\n\n```php\nreturn [\n    'components' => [\n        'request' => [\n            'cookieValidationKey' => 'fill in a secret key here',\n        ],\n    ],\n];\n```\n\n> Observação: [[yii\\web\\Request::cookieValidationKey|cookieValidationKey]] é fundamental para a segurança da sua aplicação. Ela só deve ser conhecida por pessoas de sua confiança. Não armazená-la no sistema de controle de versão.\n"
  },
  {
    "path": "docs/guide-pt-BR/security-authentication.md",
    "content": "Autenticação\n==============\n\nAutenticação é o processo de verificação da identidade do usuário. Geralmente é usado um identificador (ex. um nome de usuário ou endereço de e-mail) e um token secreto (ex. uma senha ou um token de acesso) para determinar se o usuário é quem ele diz ser. Autenticação é a base do recurso de login.\n\nO Yii fornece um framework de autenticação com vários componentes que dão suporte ao login. Para usar este framework, você precisará primeiramente fazer o seguinte:\n\n* Configurar o componente [[yii\\web\\User|user]] da aplicação;\n* Criar uma classe que implementa a interface [[yii\\web\\IdentityInterface]].\n\n\n## Configurando o [[yii\\web\\User]] <span id=\"configuring-user\"></span>\n\nO componente [[yii\\web\\User|user]] da aplicação gerencia o status de autenticação dos usuários. Ele requer que você especifique uma [[yii\\web\\User::identityClass|classe de identidade]] que contém a atual lógia de autenticação.\nNa cofiguração abaixo, a [[yii\\web\\User::identityClass|classe de identidade]] do\n[[yii\\web\\User|user]] é configurada para ser `app\\models\\User` cuja implementação é explicada na próxima subseção:\n\n```php\nreturn [\n    'components' => [\n        'user' => [\n            'identityClass' => 'app\\models\\User',\n        ],\n    ],\n];\n```\n\n\n## Implementação do [[yii\\web\\IdentityInterface]] <span id=\"implementing-identity\"></span>\n\nA [[yii\\web\\User::identityClass|classe de identidade]] deve implementar a interface [[yii\\web\\IdentityInterface]] que contém os seguintes métodos:\n\n* [[yii\\web\\IdentityInterface::findIdentity()|findIdentity()]]: ele procura por uma instância da classe de identidade usando o ID do usuário especificado. Este método é usado quando você precisa manter o status de login via sessão.\n* [[yii\\web\\IdentityInterface::findIdentityByAccessToken()|findIdentityByAccessToken()]]: ele procura por uma instância da classe de identidade usando o token de acesso informado. Este método é usado quando você precisa autenticar um usuário por um único token secreto (por exemplo, em uma aplicação stateless RESTful).\n* [[yii\\web\\IdentityInterface::getId()|getId()]]: Retorna o ID do usuário representado por essa instância da classe de identidade.\n* [[yii\\web\\IdentityInterface::getAuthKey()|getAuthKey()]]: retorna uma chave para verificar o login via cookie. A chave é mantida no cookie do login e será comparada com o informação do lado servidor para atestar a validade do cookie.\n* [[yii\\web\\IdentityInterface::validateAuthKey()|validateAuthKey()]]: Implementa a lógica de verificação da chave de login via cookie.\n\nSe um método em particular não for necessário, você pode implementá-lo com um corpo vazio. Por exemplo, se a sua aplicação é somente stateless RESTful, você só precisa implementar [[yii\\web\\IdentityInterface::findIdentityByAccessToken()|findIdentityByAccessToken()]]\ne [[yii\\web\\IdentityInterface::getId()|getId()]] deixando todos os outros métodos com um corpo vazio.\n\nNo exemplo a seguir, uma [[yii\\web\\User::identityClass|classe de identidade]] é implementado como uma classe [Active Record](db-active-record.md) associada com a tabela `user` do banco de dados.\n\n```php\n<?php\n\nuse yii\\db\\ActiveRecord;\nuse yii\\web\\IdentityInterface;\n\nclass User extends ActiveRecord implements IdentityInterface\n{\n    public static function tableName()\n    {\n        return 'user';\n    }\n\n    /**\n     * Localiza uma identidade pelo ID informado\n     *\n     * @param string|int $id o ID a ser localizado\n     * @return IdentityInterface|null o objeto da identidade que corresponde ao ID informado\n     */\n    public static function findIdentity($id)\n    {\n        return static::findOne($id);\n    }\n\n    /**\n     * Localiza uma identidade pelo token informado\n     *\n     * @param string $token o token a ser localizado\n     * @return IdentityInterface|null o objeto da identidade que corresponde ao token informado\n     */\n    public static function findIdentityByAccessToken($token, $type = null)\n    {\n        return static::findOne(['access_token' => $token]);\n    }\n\n    /**\n     * @return int|string o ID do usuário atual\n     */\n    public function getId()\n    {\n        return $this->id;\n    }\n\n    /**\n     * @return string a chave de autenticação do usuário atual\n     */\n    public function getAuthKey()\n    {\n        return $this->auth_key;\n    }\n\n    /**\n     * @param string $authKey\n     * @return bool se a chave de autenticação do usuário atual for válida\n     */\n    public function validateAuthKey($authKey)\n    {\n        return $this->getAuthKey() === $authKey;\n    }\n}\n```\n\nComo explicado anteriormente, você só precisa implementar `getAuthKey()` e `validateAuthKey()` se a sua aplicação usa recurso de login via cookie. Neste caso, você pode utilizar o seguinte código para gerar uma chave de autenticação para cada usuário\ne gravá-la na tabela `user`:\n\n```php\nclass User extends ActiveRecord implements IdentityInterface\n{\n    ......\n\n    public function beforeSave($insert)\n    {\n        if (parent::beforeSave($insert)) {\n            if ($this->isNewRecord) {\n                $this->auth_key = \\Yii::$app->security->generateRandomString();\n            }\n            return true;\n        }\n        return false;\n    }\n}\n```\n\n> Observação: Não confunda a classe de identidade `User` com [[yii\\web\\User]]. O primeiro é a classe que implementa a lógica de autenticação. Muitas vezes, é implementado como uma classe  [Active Record](db-active-record.md) associado com algum tipo de armazenamento persistente para armazenar as informações de credenciais do usuário. O último é um componente da aplicação responsável pela gestão do estado da autenticação do usuário.\n\n\n## Usando o [[yii\\web\\User]] <span id=\"using-user\"></span>\n\nVocê usa o [[yii\\web\\User]] principalmente como um componente `user` da aplicação.\n\nÉ possível detectar a identidade do usuário atual utilizando a expressão `Yii::$app->user->identity`. Ele retorna uma instância da [[yii\\web\\User::identityClass|classe de identidade]] representando o atual usuário logado, ou `null` se o usuário corrente não estiver autenticado (acessando como convidado). O código a seguir mostra como recuperar outras informações relacionadas à autenticação de [[yii\\web\\User]]:\n\n```php\n// identidade do usuário atual. Null se o usuário não estiver autenticado.\n$identity = Yii::$app->user->identity;\n\n// o ID do usuário atual. Null se o usuário não estiver autenticado.\n$id = Yii::$app->user->id;\n\n// se o usuário atual é um convidado (não autenticado)\n$isGuest = Yii::$app->user->isGuest;\n```\n\nPara logar um usuário, você pode usar o seguinte código:\n\n```php\n// encontrar uma identidade de usuário com o nome de usuário especificado.\n// observe que você pode querer checar a senha se necessário\n$identity = User::findOne(['username' => $username]);\n\n// logar o usuário\nYii::$app->user->login($identity);\n```\n\nO método [[yii\\web\\User::login()]] define a identidade do usuário atual para o [[yii\\web\\User]]. Se a sessão estiver [[yii\\web\\User::enableSession|habilitada]], ele vai manter a identidade na sessão para que o status de autenticação do usuário seja mantido durante toda a sessão. Se o login via cookie (ex. login \"remember me\") estiver [[yii\\web\\User::enableAutoLogin|habilitada]], ele também guardará a identidade em um cookie para que o estado de autenticação do usuário possa ser recuperado a partir do cookie enquanto o cookie permanece válido.\n\nA fim de permitir login via cookie, você pode configurar [[yii\\web\\User::enableAutoLogin]] como `true` na configuração da aplicação. Você também precisará fornecer um parâmetro de tempo de duração quando chamar o método [[yii\\web\\User::login()]].\n\nPara realizar o logout de um usuário, simplesmente chame:\n\n```php\nYii::$app->user->logout();\n```\n\nObserve que o logout de um usuário só tem sentido quando a sessão está habilitada. O método irá limpar o status de autenticação do usuário na memória e na sessão. E por padrão, ele também destruirá *todos* os dados da sessão do usuário. Se você quiser guardar os dados da sessão, você deve chamar `Yii::$app->user->logout(false)`.\n\n\n## Eventos de Autenticação <span id=\"auth-events\"></span>\n\nA classe [[yii\\web\\User]] dispara alguns eventos durante os processos de login e logout:\n\n* [[yii\\web\\User::EVENT_BEFORE_LOGIN|EVENT_BEFORE_LOGIN]]: disparado no início de [[yii\\web\\User::login()]].\n  Se o manipulador de evento define a propriedade [[yii\\web\\UserEvent::isValid|isValid]] do objeto de evento para `false`, o processo de login será cancelado.\n* [[yii\\web\\User::EVENT_AFTER_LOGIN|EVENT_AFTER_LOGIN]]: dispara após de um login com sucesso.\n* [[yii\\web\\User::EVENT_BEFORE_LOGOUT|EVENT_BEFORE_LOGOUT]]: dispara no início de [[yii\\web\\User::logout()]]. Se o manipulador de evento define a propriedade [[yii\\web\\UserEvent::isValid|isValid]] do objeto de evento para `false`, o processo de logout será cancelado.\n* [[yii\\web\\User::EVENT_AFTER_LOGOUT|EVENT_AFTER_LOGOUT]]: dispara após um logout com sucesso.\n\nVocê pode responder a estes eventos implementando funcionalidades, tais como auditoria de login, estatísticas de usuários on-line. Por exemplo, no manipulador\n[[yii\\web\\User::EVENT_AFTER_LOGIN|EVENT_AFTER_LOGIN]], você pode registrar o tempo de login e endereço IP na tabela `user`.\n\n\n\n"
  },
  {
    "path": "docs/guide-pt-BR/security-authorization.md",
    "content": "Autorização\n=========\n\nAutorização é o processo que verifica se um usuário tem permissão para fazer alguma coisa. O Yii fornece dois métodos de autorização: Filtro de Controle de Acesso (ACF) e Controle de Acesso Baseado em Role (RBAC).\n\n\n## Filtro de Controle de Acesso <span id=\"access-control-filter\"></span>\n\nO Filtro de Controle de Acesso (ACF) é um método simples de autorização implementado como [[yii\\filters\\AccessControl]] que é mais indicado para aplicações que só precisam de algum controle de acesso simples. Como o próprio nome indica, ACF é uma ação de [filtro](structure-filters.md) que pode ser usada em um controller (controlador) ou um módulo. Enquanto um usuário faz uma solicitação para executar uma ação, ACF verificará a lista de  [[yii\\filters\\AccessControl::rules|regras de acesso]] para determinar se o usuário tem permissão para acessar a ação solicitada.\n\nO código a seguir mostra como usar ACF no controller (controlador) `site`:\n\n```php\nuse yii\\web\\Controller;\nuse yii\\filters\\AccessControl;\n\nclass SiteController extends Controller\n{\n   public function behaviors()\n   {\n       return [\n           'access' => [\n               'class' => AccessControl::class,\n               'only' => ['login', 'logout', 'signup'],\n               'rules' => [\n                   [\n                       'allow' => true,\n                       'actions' => ['login', 'signup'],\n                       'roles' => ['?'],\n                   ],\n                   [\n                       'allow' => true,\n                       'actions' => ['logout'],\n                       'roles' => ['@'],\n                   ],\n               ],\n           ],\n       ];\n   }\n   // ...\n}\n```\n\nNo código acima do ACF é anexado ao controller (controlador) `site` como um behavior (comportamento). Esta é a maneira habitual de utilizar uma ação\nfiltro. A opção `only` determina que o ACF só deva ser aplicado nas ações `login`, `logout` e `signup`.\nTodas as outras ações no controller (controlador) não estão sujeitas ao controle de acesso. A opção `rules` lista as [[yii\\filters\\AccessRule|regras de acesso]], onde se lê da seguinte forma:\n\n- Permite todos os usuários convidados (ainda não autorizados) acessar as ações `login` e `signup`. A opção `roles` contém um ponto de interrogação `?` que é um token especial que representa \"usuários convidados\".\n- Permite que usuários autenticados acessem a ação `logout`. O caractere `@` é outro token especial que representa \"usuários autenticados\".\n\nO ACF executa a verificação de autorização examinando as regras de acesso, uma por uma de cima para baixo até encontrar uma regra que corresponda ao contexto de execução atual. O valor da opção `allow` corresponde a regra que irá dizer se o usuário está autorizado ou não. Se nenhuma das regras forem correspondidas, significa que o usuário não está autorizado, e o ACF vai parar a próxima execução.\n\nQuando o ACF determina que um usuário não está autorizado a acessar a ação atual, ele toma a seguinte medida por padrão:\n\n* Se o usuário é convidado, será chamado [[yii\\web\\User::loginRequired()]] para redirecionar o navegador do usuário para a página de login.\n* Se o usuário já está autenticado, ele lançará um [[yii\\web\\ForbiddenHttpException]].\n\nVocê pode personalizar este behavior configurando a propriedade  [[yii\\filters\\AccessControl::denyCallback]] da seguinte forma:\n\n```php\n[\n   'class' => AccessControl::class,\n   ...\n   'denyCallback' => function ($rule, $action) {\n       throw new \\Exception('Você não está autorizado a acessar esta página');\n   }\n]\n```\n\nAs [[yii\\filters\\AccessRule|regras de acesso]] suporta muitas opções. A seguir, está um resumo das opções suportadas. Você também pode estender [[yii\\filters\\AccessRule]] para criar suas próprias classes personalizadas de regras de acesso.\n\n* [[yii\\filters\\AccessRule::allow|allow]]: especifica se é uma regra para \"permitir\" ou \"negar\".\n\n* [[yii\\filters\\AccessRule::actions|actions]]: especifica quais ações essa regra corresponde. Deve ser um array de IDs das ações. A comparação é case-sensitive. Se esta opção estiver vazia ou não definida, isso significa que a regra se aplica a todas as ações.\n\n* [[yii\\filters\\AccessRule::controllers|controllers]]: especifica que controllers (controlador) esta regra corresponde. Deve ser um array de IDs de controller. A comparação é case-sensitive. Se esta opção estiver vazia ou não definida, isso significa que a regra se aplica a todos controllers.\n\n* [[yii\\filters\\AccessRule::roles|roles]]: especifica quais roles de usuários que esta regra corresponde. Dois caracteres especiais são reconhecidos, e eles são verificados através [[yii\\web\\User::isGuest]]:\n\n    - `?`: corresponde a um usuário convidado (ainda não autenticado)\n    - `@`: corresponde a um usuário autenticado\n\n  A utilização de outros nomes invocará o método [[yii\\web\\User::can()]], que requer RBAC permitindo (a ser descrito na próxima subsecção). Se esta opção estiver vazia ou não definida, significa que esta regra se aplica a todas as roles.\n\n* [[yii\\filters\\AccessRule::ips|ips]]: especifica quais  [[yii\\web\\Request::userIP|client IP addresses]] esta regra corresponde. Um endereço de ip pode conter o coringa `*` no final para que ele corresponda endereços IP com o mesmo prefixo.\nPor exemplo, '192.168.*' corresponde a todos os endereços IPs no seguimento '192.168.'. Se esta opção estiver vazia ou não definida, significa que esta regra se aplica a todos os endereços IPs.\n\n* [[yii\\filters\\AccessRule::verbs|verbs]]: especifica quais métodos de request (ex. `GET`, `POST`) esta regra corresponde. A comparação é case-insensitive.\n\n* [[yii\\filters\\AccessRule::matchCallback|matchCallback]]: especifica um PHP callable que deve ser chamado para determinar se esta regra deve ser aplicada.\n\n* [[yii\\filters\\AccessRule::denyCallback|denyCallback]]: especifica um PHP callable que deve ser chamado quando esta regra negar o acesso.\n\nAbaixo está um exemplo que mostra como fazer uso da opção `matchCallback`, que lhe permite escrever uma lógica arbritária de validação de acesso:\n\n\n```php\nuse yii\\filters\\AccessControl;\n\nclass SiteController extends Controller\n{\n   public function behaviors()\n   {\n       return [\n           'access' => [\n               'class' => AccessControl::class,\n               'only' => ['special-callback'],\n               'rules' => [\n                   [\n                       'actions' => ['special-callback'],\n                       'allow' => true,\n                       'matchCallback' => function ($rule, $action) {\n                           return date('d-m') === '31-10';\n                       }\n                   ],\n               ],\n           ],\n       ];\n   }\n\n   // Match callback chamada! Esta página pode ser acessado somente a cada 31 de outubro\n   public function actionSpecialCallback()\n   {\n       return $this->render('happy-halloween');\n   }\n}\n```\n\n\n## Controle de Acesso Baseado em Role (RBAC) <span id=\"rbac\"></span>\n\nControle de Acesso Baseado em Role (RBAC) fornece um simples porém poderoso controle de acesso centralizado. Por favor, consulte [Wikipedia](https://pt.wikipedia.org/wiki/Controle_de_acesso_baseado_em_fun%C3%A7%C3%B5es) para obter detalhes sobre comparação de RBAC com outros sistemas de controle de acesso mais tradicionais.\n\nYii implementa um RBAC Hierárquico genérico, conforme [NIST RBAC model](https://csrc.nist.gov/CSRC/media/Publications/conference-paper/1992/10/13/role-based-access-controls/documents/ferraiolo-kuhn-92.pdf). Ele fornece as funcionalidades RBAC através do [componente de aplicação](structure-application-components.md) [[yii\\rbac\\ManagerInterface|authManager]].\n\nO uso do RBAC divide-se em duas partes. A primeira parte é construir os dados de autorização RBAC, e a segunda parte é usar os dados de autorização para executar verificação de acesso em locais onde ela é necessária.\n\nPara facilitar a próxima descrição, vamos primeiro introduzir alguns conceitos básicos do RBAC.\n\n\n### Conceitos Básicos <span id=\"basic-concepts\"></span>\n\nUma role representa uma coleção de *permissões* (ex. criar posts, atualizar posts). Uma role pode ser atribuído a um ou vários usuários. Para verificar se um usuário tem uma permissão especifica, podemos verificar se o usuário está associado a uma role que contém esta permissão.\n\nAssociado com cada role ou permissão, pode haver uma *regra*. Uma regra representa uma parte do código que será executado durante verificação de acesso para determinar se a role ou permissão correspondentes se aplicam ao usuário corrente.\nPor exemplo, a permissão para \"atualizar post\" pode ter uma regra que verifica se  o usuário corrente é quem criou o post.\nDurante a verificação de acesso, se o usuário NÃO for quem criou o post, ele não terá permissão para \"atualizar o post\".\n\nAmbos roles e permissões podem ser organizadas numa hierarquia. Em particular, uma role pode constituída de outras roles ou permissões; e uma permissão pode consistir em outras permissões. Yii implementa uma hierarquia de *ordem parcial* que inclui a hierarquia de *árvore* mais especial. Enquanto uma role pode conter uma permissão, o inverso não é verdadeiro.\n\n\n### Configurando RBAC <span id=\"configuring-rbac\"></span>\n\nAntes de partimos para definir dados de autorização e realizar a verificação de acesso, precisamos configurar o componente de aplicação [[yii\\base\\Application::authManager|authManager]]. Yii oferece dois tipos de gerenciadores de autorização: [[yii\\rbac\\PhpManager]] e [[yii\\rbac\\DbManager]]. O primeiro utiliza um script PHP para armazena os dados de autorização, enquanto o último armazena os dados de autorização no banco. Você pode considerar o uso do primeiro se a sua aplicação não requerer um gerenciamento  muito dinâmico das role e permissões.\n\n\n#### Usando`PhpManager` <span id=\"using-php-manager\"></span>\n\nO código a seguir mostra como configurar o `authManager` na configuração da aplicação utilizando a classe [[yii\\rbac\\PhpManager]]:\n\n```php\nreturn [\n   // ...\n   'components' => [\n       'authManager' => [\n           'class' => 'yii\\rbac\\PhpManager',\n       ],\n       // ...\n   ],\n];\n```\n\nO `authManager` agora pode ser acessado via `\\Yii::$app->authManager`.\n\nPor padrão, [[yii\\rbac\\PhpManager]] armazena os dados RBAC em arquivos sob o diretório `@app/rbac` . Verifique se o diretório e todos os arquivos estão com direito de escrita pelo processo do servidor da Web caso seja necessário realizar alteração on-line.\n\n\n#### Usando `DbManager` <span id=\"using-db-manager\"></span>\n\nO código a seguir mostra como configurar o `authManager` na configuração da apilcação utilizando a classe [[yii\\rbac\\DbManager]]:\n\n```php\nreturn [\n   // ...\n   'components' => [\n       'authManager' => [\n           'class' => 'yii\\rbac\\DbManager',\n       ],\n       // ...\n   ],\n];\n```\n\n`DbManager` usa quatro tabelas de banco de dados para armazenar seus dados:\n\n- [[yii\\rbac\\DbManager::$itemTable|itemTable]]: tabela para armazenar itens de autorização. O padrão é \"auth_item\".\n- [[yii\\rbac\\DbManager::$itemChildTable|itemChildTable]]: tabela para armazenar hierarquia de itens de autorização. O padrão é \"auth_item_child\".\n- [[yii\\rbac\\DbManager::$assignmentTable|assignmentTable]]: tabela para armazenar tarefas de itens de autorização. O padrão é \"auth_assignment\".\n- [[yii\\rbac\\DbManager::$ruleTable|ruleTable]]: tabela para armazenar as regras. O padrão é \"auth_rule\". Antes de começar é preciso criar essas tabelas no banco de dados . Para fazer isto, você pode usar o migration armazenado em `@yii/rbac/migrations`:\n\n`yii migrate --migrationPath=@yii/rbac/migrations`\n\nO `authManager` já pode ser acessado via `\\Yii::$app->authManager`.\n\n\n### Construindo Dados de Autorização <span id=\"generating-rbac-data\"></span>\n\nPara construir dados de autorização devem ser realizadas as seguintes tarefas:\n\n- definir roles e permissões;\n- estabelecer relações entre roles e permissões;\n- definir regras;\n- associar regras com roles e permissões;\n- atribuir roles a usuários.\n\n\nDependendo dos requisitos de flexibilidade de autorização das tarefas acima poderia ser feito de maneiras diferentes.\n\nSe a sua hierarquia de permissões não se altera e você tem um número fixo de usuários pode-se criar um [console command](tutorial-console.md#create-command) que irá iniciar os dados de autorização uma vez através das APIs oferecidas pelo `authManager`:\n\n```php\n<?php\nnamespace app\\commands;\n\nuse Yii;\nuse yii\\console\\Controller;\n\nclass RbacController extends Controller\n{\n   public function actionInit()\n   {\n       $auth = Yii::$app->authManager;\n\n       // adciona a permissão \"createPost\"\n       $createPost = $auth->createPermission('createPost');\n       $createPost->description = 'Create a post';\n       $auth->add($createPost);\n\n       // adciona a permissão  \"updatePost\"\n       $updatePost = $auth->createPermission('updatePost');\n       $updatePost->description = 'Update post';\n       $auth->add($updatePost);\n\n       // adciona a role \"author\" e da a esta role a permissão \"createPost\"\n       $author = $auth->createRole('author');\n       $auth->add($author);\n       $auth->addChild($author, $createPost);\n\n       // adciona a role \"admin\" e da a esta role a permissão \"updatePost\"\n       // bem como as permissões da role \"author\"\n       $admin = $auth->createRole('admin');\n       $auth->add($admin);\n       $auth->addChild($admin, $updatePost);\n       $auth->addChild($admin, $author);\n\n       // Atribui roles para usuários. 1 and 2 são IDs retornados por IdentityInterface::getId()\n       // normalmente implementado no seu model User.\n       $auth->assign($author, 2);\n       $auth->assign($admin, 1);\n   }\n}\n```\n\nDepois de executar o comando com `yii rbac/init` nós vamos chegar a seguinte hierarquia:\n\n![Simple RBAC hierarchy](images/rbac-hierarchy-1.png \"Simple RBAC hierarchy\")\n\nAuthor pode criar post, admin pode atualizar post e fazer tudo que author pode.\n\nSe a sua aplicação permite inscrição de usuários, você precisa atribuir roles a esses novos usuários. Por exemplo, para que todos os usuários inscritos tornem-se authors, no seu template avançado de projeto você precisa modificar o `frontend\\models\\SignupForm::signup()`\nconforme abaixo:\n\n```php\npublic function signup()\n{\n   if ($this->validate()) {\n       $user = new User();\n       $user->username = $this->username;\n       $user->email = $this->email;\n       $user->setPassword($this->password);\n       $user->generateAuthKey();\n       $user->save(false);\n\n       // foram adicionadas as seguintes três linhas:\n       $auth = Yii::$app->authManager;\n       $authorRole = $auth->getRole('author');\n       $auth->assign($authorRole, $user->getId());\n\n       return $user;\n   }\n\n   return null;\n}\n```\n\nPara aplicações que requerem controle de acesso complexo com dados de autorização atualizados dinamicamente, interfaces de usuário especiais\n(Isto é: painel de administração) pode ser necessário desenvolver usando APIs oferecidas pelo `authManager`.\n\n\n### Usando Regras <span id=\"using-rules\"></span>\n\nComo já mencionado, regras coloca restrição adicional às roles e permissões. Uma regra é uma classe que se estende de [[yii\\rbac\\Rule]]. Ela deve implementar o método [[yii\\rbac\\Rule::execute()|execute()]]. Na hierarquia que criamos anteriormente, author não pode editar seu próprio post. Vamos corrigir isto. Primeiro nós precisamos de uma regra para verificar se o usuário é o autor do post:\n\n```php\nnamespace app\\rbac;\n\nuse yii\\rbac\\Rule;\n\n/**\n* Verifica se o authorID corresponde ao usuário passado via  parâmetro\n*/\nclass AuthorRule extends Rule\n{\n   public $name = 'isAuthor';\n\n   /**\n    * @param string|int $user the user ID.\n    * @param Item $item the role or permission that this rule is associated with\n    * @param array $params parameters passed to ManagerInterface::checkAccess().\n    * @return bool a value indicating whether the rule permits the role or permission it is associated with.\n    */\n   public function execute($user, $item, $params)\n   {\n       return isset($params['post']) ? $params['post']->createdBy == $user : false;\n   }\n}\n```\n\nA regra acima verifica se o `post` foi criado pelo `$user`. Nós vamos criar uma permissão especial `updateOwnPost` no comando que usamos previamente:\n\n```php\n$auth = Yii::$app->authManager;\n\n// adciona a regra\n$rule = new \\app\\rbac\\AuthorRule;\n$auth->add($rule);\n\n// adciona a permissão \"updateOwnPost\" e associar a regra com ela.\n$updateOwnPost = $auth->createPermission('updateOwnPost');\n$updateOwnPost->description = 'Update own post';\n$updateOwnPost->ruleName = $rule->name;\n$auth->add($updateOwnPost);\n\n// \"updateOwnPost\" será usado no \"updatePost\"\n$auth->addChild($updateOwnPost, $updatePost);\n\n// autoriza \"author\"  a atualizar seus próprios posts\n$auth->addChild($author, $updateOwnPost);\n```\n\nAgora temos a seguinte hierarquia:\n\n![RBAC hierarchy with a rule](images/rbac-hierarchy-2.png \"RBAC hierarchy with a rule\")\n\n\n### Verificação de Acesso <span id=\"access-check\"></span>\n\nCom os dados de autorização prontos, você pode verificar o acesso simplesmente chamando o método [[yii\\rbac\\ManagerInterface::checkAccess()]]. Como a maioria das verificações de acesso é sobre o usuário corrente, por conveniência, o Yii fornece um método de atalho [[yii\\web\\User::can()]], que pode ser usado como a seguir:\n\n```php\nif (\\Yii::$app->user->can('createPost')) {\n   // create post\n}\n```\n\nSe o usuário corrente é Jane com `ID=1` começaremos com `createPost` e tentaremos chegar à `Jane`:\n\n![Access check](images/rbac-access-check-1.png \"Access check\")\n\nA fim de verificar se o usuário pode atualizar um post, precisamos passar um parâmetro extra que é requerido por `AuthorRule` descrito abaixo:\n\n```php\nif (\\Yii::$app->user->can('updatePost', ['post' => $post])) {\n   // update post\n}\n```\n\nAqui está o que acontece se o usuário atual é John:\n\n\n![Access check](images/rbac-access-check-2.png \"Access check\")\n\nNós começamos com o `updatePost` e passamos por `updateOwnPost`. Para passar pela verificação de acesso, `AuthorRule` deve retornar `true` no seu método `execute()`. O método recebe `$params` da chamada do método `can()` de modo que o valor é `['post' => $post]`. Se tudo estiver correto, vamos chegar a `author` que é atribuído a John.\n\nNo caso de Jane é um pouco mais simples, uma vez que ela é um administrador:\n\n![Access check](images/rbac-access-check-3.png \"Access check\")\n\n\n### Usando Roles Padrões <span id=\"using-default-roles\"></span>\n\nUma role padrão é uma role que é *implicitamente* atribuída a *todos* os usuários. A chamada a [[yii\\rbac\\ManagerInterface::assign()]] não é necessária, e os dados de autorização não contém informação de atribuição.\n\nUma role padrão é geralmente associada com uma regra que determina se a role aplica-se ao do usuário que está sendo verificado.\n\nRoles padrões são muitas vezes utilizados em aplicações que já têm algum tipo de atribuição de role. Por exemplo, uma aplicação pode ter uma coluna de \"grupo\" em sua tabela de usuário para representar a que grupo de privilégio cada usuário pertence.\nSe cada grupo privilégio pode ser mapeado para uma RBAC role, você pode usar o recurso de role padrão para associar automaticamente cada usuário ao role de RBAC. Vamos usar um exemplo para mostrar como isso pode ser feito.\n\nSuponha que na tabela user, você tem uma coluna `group` que usa 1 para representar o grupo administrator e 2 o grupo author.\nVocê pretende ter duas roles RBAC `admin` and `author` para representar as permissões para estes dois grupos, respectivamente. Você pode configurar os dados da RBAC da seguinte forma,\n\n\n```php\nnamespace app\\rbac;\n\nuse Yii;\nuse yii\\rbac\\Rule;\n\n/**\n* Verifica se o grupo de usuário corresponde\n*/\nclass UserGroupRule extends Rule\n{\n   public $name = 'userGroup';\n\n   public function execute($user, $item, $params)\n   {\n       if (!Yii::$app->user->isGuest) {\n           $group = Yii::$app->user->identity->group;\n           if ($item->name === 'admin') {\n               return $group == 1;\n           } elseif ($item->name === 'author') {\n               return $group == 1 || $group == 2;\n           }\n       }\n       return false;\n   }\n}\n\n$auth = Yii::$app->authManager;\n\n$rule = new \\app\\rbac\\UserGroupRule;\n$auth->add($rule);\n\n$author = $auth->createRole('author');\n$author->ruleName = $rule->name;\n$auth->add($author);\n// ... adiciona permissões como filhas de  $author ...\n\n$admin = $auth->createRole('admin');\n$admin->ruleName = $rule->name;\n$auth->add($admin);\n$auth->addChild($admin, $author);\n// ... adiciona permissões como filhas de  $admin ...\n```\n\nNote que no exemplo acima, porque \"author\" é adicionado como filho de  \"admin\", quando você implementar o método `execute()` da classe rule, você também precisa respeitar essa hierarquia. É por isso que quando o nome da role é \"author\", o método `execute()` retornará  `true` se o grupo de usuário for 1 or 2 (significa que o usuário está no grupo \"admin\" ou \"author\").\n\nEm seguida, configure `authManager` listando as duas roles [[yii\\rbac\\BaseManager::$defaultRoles]]:\n\n```php\nreturn [\n   // ...\n   'components' => [\n       'authManager' => [\n           'class' => 'yii\\rbac\\PhpManager',\n           'defaultRoles' => ['admin', 'author'],\n       ],\n       // ...\n   ],\n];\n```\n\nAgora, se você executar uma verificação de acesso, ambas as roles `admin` e `author` serão verificadas através da avaliação das regras associado com elas. se a regra retornar `true`, isso significa que a role se aplica ao usuário atual. A partir da implementação da regra acima, isto significa que se o valor do ‘grupo’ de um usuário for 1, a role `admin` seria aplicável ao usuário; e se o valor do `grupo` for 2, seria a role `author`.\n\n\n\n"
  },
  {
    "path": "docs/guide-pt-BR/start-databases.md",
    "content": "Trabalhando com Bancos de Dados\n===============================\n\nEsta seção descreverá como criar uma nova página que exibe informações de países obtidos de uma tabela de banco de dados chamada `pais`. Para isso, você\nconfigurará uma conexão com o banco de dados, criará uma classe de\n[Active Record](db-active-record.md), definirá uma [action](structure-controllers.md) e criará uma [view](structure-views.md).\n\nAo longo deste tutorial, você aprenderá como:\n\n* configurar uma conexão de BD\n* definir uma classe Active Record\n* consultar dados usando a classe de Active Record\n* exibir dados em uma view de forma paginada\n\nPerceba que para terminar essa seção, você deve ter conhecimento e experiência\nbásicos em bancos de dados. Em particular, você deve saber como criar um banco de dados e como executar instruções SQL usando uma ferramenta cliente de bancos de dados.\n\n\nPreparando o Banco de Dados <span id=\"preparing-database\"></span>\n---------------------------\n\nPara começar, crie um banco de dados chamado `yii2basico`, de onde você\nobterá os dados em sua aplicação. Você pode criar um banco de dados SQLite, MySQL,\nPostgreSQL, MSSQL ou Oracle, já que o Yii tem suporte embutido a vários gerenciadores de bancos de dados. Por questões de simplicidade, será assumido o uso do MySQL\nna descrição a seguir.\n\n> Info: O MariaDB costumava ser um substituto transparente do MySQL. Isto já não é mais totalmente verdade. Caso você queira usar recursos avançados como suporte a `JSON` no MariaDB, por favor, consulte a extensão do MariaDB listada mais à frente.\n\nEm seguida, crie uma tabela chamada `pais` no banco de dados e insira alguns\ndados de exemplo. Você pode rodar as seguintes declarações SQL para fazer isso:\n\n```sql\nCREATE TABLE `pais` (\n  `codigo` CHAR(2) NOT NULL PRIMARY KEY,\n  `nome` CHAR(52) NOT NULL,\n  `populacao` INT(11) NOT NULL DEFAULT '0'\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nINSERT INTO `pais` VALUES ('AU','Austrália',24016400);\nINSERT INTO `pais` VALUES ('BR','Brasil',205722000);\nINSERT INTO `pais` VALUES ('CA','Canadá',35985751);\nINSERT INTO `pais` VALUES ('CN','China',1375210000);\nINSERT INTO `pais` VALUES ('DE','Alemanha',81459000);\nINSERT INTO `pais` VALUES ('FR','França',64513242);\nINSERT INTO `pais` VALUES ('GB','Reino Unido',65097000);\nINSERT INTO `pais` VALUES ('IN','Índia',1285400000);\nINSERT INTO `pais` VALUES ('RU','Rússia',146519759);\nINSERT INTO `pais` VALUES ('US','Estados Unidos',322976000);\n```\n\nNeste ponto, você tem um banco de dados chamado `yii2basico` e dentro dele uma\ntabela `pais` com três colunas, contendo dez linhas de dados.\n\nConfigurando uma Conexão de BD <span id=\"configuring-db-connection\"></span>\n------------------------------\n\nAntes de prosseguir, certifique-se de que você possui instalados tanto a\nextensão [PDO](https://www.php.net/manual/pt_BR/book.pdo.php) do PHP quanto o driver\nPDO para o gerenciador de banco de dados que você está usando (por exemplo, `pdo_mysql` para o MySQL).\nEste é um requisito básico se a sua aplicação usa um banco de dados relacional.\n\nTendo esses instalados, abra o arquivo `config/db.php` e mude os parâmetros conforme seu banco de dados.\nPor padrão, o arquivo contém o seguinte:\n\n```php\n<?php\n\nreturn [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=localhost;dbname=yii2basico',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n];\n```\n\nO arquivo `config/db.php` é uma típica ferramenta de [configuração](concept-configurations.md) baseada em arquivo. Este arquivo de configuração em particular especifica\nos parâmetros necessários para criar e inicializar uma instância [[yii\\db\\Connection]]\npor meio da qual você pode fazer consultas SQL ao banco de dados subjacente.\n\nA conexão configurada acima pode ser acessada no código da aplicação através da expressão `Yii::$app->db`.\n\n> Info: O arquivo `config/db.php` será absorvido (incluso) pela configuração principal da\n  aplicação `config/web.php`, que especifica como a instância da [aplicação](structure-applications.md)\n  deve ser inicializada. Para mais informações, por favor, consulte a seção [Configurações](concept-configurations.md).\n\nSe você precisa trabalhar com bancos de dados para os quais não há suporte nativo no Yii, consulte as seguintes extensões:\n\n- [Informix](https://github.com/edgardmessias/yii2-informix)\n- [IBM DB2](https://github.com/edgardmessias/yii2-ibm-db2)\n- [Firebird](https://github.com/edgardmessias/yii2-firebird)\n- [MariaDB](https://github.com/sam-it/yii2-mariadb)\n\nCriando um Active Record <span id=\"creating-active-record\"></span>\n------------------------\n\nPara representar e buscar os dados da tabela `pais`, crie uma classe que deriva de [Active Record](db-active-record.md) chamada `Pais` e salve-a\nno arquivo `models/Pais.php`.\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass Pais extends ActiveRecord\n{\n}\n```\n\nA classe `Pais` estende de [[yii\\db\\ActiveRecord]]. Você não precisa escrever\nnenhum código nela! Só com o código acima, o Yii descobrirá o nome da tabela\nassociada a partir do nome da classe.\n\n> Info: Se não houver nenhuma correspondência direta do nome da classe com o nome\n  da tabela, você pode sobrescrever o método [[yii\\db\\ActiveRecord::tableName()]]\n  para especificar explicitamente o nome da tabela associada.\n\nUsando a classe `Pais`, você pode manipular facilmente os dados na tabela\n`pais`, conforme é demonstrado nos fragmentos de código a seguir:\n\n```php\nuse app\\models\\Pais;\n\n// obtém todas as linhas da tabela pais e as ordena pela coluna \"nome\"\n$paises = Pais::find()->orderBy('nome')->all();\n\n// obtém a linha cuja chave primária é \"BR\"\n$pais = Pais::findOne('BR');\n\n// exibe \"Brasil\"\necho $pais->nome;\n\n// altera o nome do país para \"Brazil\" e o salva no banco de dados\n$pais->nome = 'Brazil';\n$pais->save();\n```\n\n> Info: O Active Record é uma maneira poderosa de acessar e manipular dados\n  do banco de dados de uma forma orientada a objetos. Você pode encontrar informações\n  mais detalhadas na seção [Active Record](db-active-record.md. Alternativamente,\n  você também pode interagir com o banco de dados usando um método de acesso a\n  dados em baixo nível chamado [Objeto de Acesso a Dados (Data Access Objects)](db-dao.md).\n\n\nCriando uma Action <span id=\"creating-action\"></span>\n------------------\n\nPara disponibiliar os dados de países aos usuários finais, você precisa criar uma nova\naction. Em vez de colocar a nova action no controller `site`\ncomo você fez nas seções anteriores, faz mais sentido criar um novo controller\nespecificamente para todas as actions relacionadas aos dados de países. Chame\neste novo controller de `PaisController`, e crie uma action `index` nele,\nconforme o exemplo a seguir:\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\nuse yii\\data\\Pagination;\nuse app\\models\\Pais;\n\nclass PaisController extends Controller\n{\n    public function actionIndex()\n    {\n        $query = Pais::find();\n\n        $paginacao = new Pagination([\n            'defaultPageSize' => 5,\n            'totalCount' => $query->count(),\n        ]);\n\n        $paises = $query->orderBy('nome')\n            ->offset($paginacao->offset)\n            ->limit($paginacao->limit)\n            ->all();\n\n        return $this->render('index', [\n            'paises' => $paises,\n            'paginacao' => $paginacao,\n        ]);\n    }\n}\n```\n\nSalve o código acima no arquivo `controllers/PaisController.php`.\n\nA action `index` chama `Pais::find()`. Este método do Active Record constrói\numa consulta ao BD e retorna todos os dados da tabela `pais`. Para limitar o\nnúmero de países retornados a cada requisição, a consulta é paginada com a ajuda\nde um objeto [[yii\\data\\Pagination]]. O objeto `Pagination` serve para dois propósitos:\n\n* Define as cláusulas `offset` e `limit` da declaração SQL representada pela query\n  (consulta) de modo que apenas retorne uma única página de dados por vez (no exemplo, no máximo\n  5 linhas por página).\n* É usado na view para exibir um paginador que consiste de uma lista de botões de páginas, conforme será explicado na próxima subseção.\n\nNo final do código, a action `index` renderiza uma view chamada `index` e envia a ela os dados dos países e as informações de paginação.\n\n\nCriando uma View <span id=\"creating-view\"></span>\n----------------\n\nDentro do diretório `views`, primeiro crie um subdiretório chamado `pais`.\nEsta pasta será usada para guardar todas as views renderizadas pelo controller\n`PaisController`. Dentro do diretório `views/pais`, crie um arquivo `index.php`\ncontendo o seguinte:\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\LinkPager;\n?>\n<h1>Países</h1>\n<ul>\n<?php foreach ($paises as $pais): ?>\n    <li>\n        <?= Html::encode(\"{$pais->nome} ({$pais->codigo})\") ?>:\n        <?= $pais->populacao ?>\n    </li>\n<?php endforeach; ?>\n</ul>\n\n<?= LinkPager::widget(['pagination' => $paginacao]) ?>\n```\n\nA view tem duas seções relativas à exibição dos dados dos países. Na primeira parte,\nos dados de países fornecidos são percorridos e renderizados como uma lista HTML.\nNa segunda parte, um widget [[yii\\widgets\\LinkPager]] é renderizado usando as\ninformações de paginação passadas pela action. O widget `LinkPager` exibe uma\nlista de botões de páginas. Clicar em qualquer um deles vai atualizar os dados dos países conforme a página correspondente.\n\n\nConferindo <span id=\"trying-it-out\"></span>\n--------\n\nPara ver se todo os códigos acima funcionam, use o seu navegador para acessar a seguinte URL:\n\n```\nhttps://hostname/index.php?r=pais/index\n```\n\n![Lista de Países](images/start-country-list.png)\n\nPrimeiramente, você verá uma lista exibindo cinco países. Abaixo dos países,\nvocê verá um paginador com quatro botões. Se você clicar no botão \"2\", você\nverá a página exibir outros cinco países do banco de dados: a segunda\npágina de registros. Observe mais cuidadosamente e você perceberá que a URL no\nbrowser mudou para\n\n```\nhttps://hostname/index.php?r=pais/index&page=2\n```\n\nPor trás das cortinas, [[yii\\data\\Pagination|Pagination]] está fornecendo toda\na funcionalidade necessária para paginar um conjunto de dados:\n\n* Inicialmente, [[yii\\data\\Pagination|Pagination]] representa a primeira página,\n  que reflete a consulta SELECT de países com a cláusula `LIMIT 5 OFFSET 0`.\n  Como resultado, os primeiros cinco países serão buscados e exibidos.\n* O widget [[yii\\widgets\\LinkPager|LinkPager]] renderiza os botões das páginas\n  usando as URLs criadas pelo [[yii\\data\\Pagination::createUrl()|Pagination]].\n  As URLs conterão um parâmetro `page`, que representa os diferentes números de\n  páginas.\n* Se você clicar no botão de página \"2\", uma nova requisição para a rota\n  `pais/index` será disparada e tratada. [[yii\\data\\Pagination|Pagination]] lê\n  o parâmetro `page` da URL e define o número da página atual como sendo 2. A nova\n  consulta de países então terá a cláusula `LIMIT 5 OFFSET 5` e retornará os\n  próximos cinco países para a exibição.\n\n\nResumo <span id=\"summary\"></span>\n------\n\nNesta seção, você aprendeu como trabalhar com um banco de dados. Você também\naprendeu como buscar e exibir dados em páginas com a ajuda do\n[[yii\\data\\Pagination]] e do [[yii\\widgets\\LinkPager]].\n\nNa próxima seção, você aprenderá como usar a poderosa ferramenta de geração de códigos,\nchamada [Gii](tool-gii.md), para ajudá-lo a implementar rapidamente algumas\nfuncionalidades comumente necessárias, tais como as operações CRUD\n(Criar-Ler-Atualizar-Excluir) para trabalhar com os dados de uma tabela do\nbanco de dados. Na verdade, todo o código que você acabou de escrever pode ser\ngerado automaticamente no Yii usando a ferramenta Gii.\n"
  },
  {
    "path": "docs/guide-pt-BR/start-forms.md",
    "content": "Trabalhando com Formulários\n===========================\n\nEsta seção descreve como criar uma nova página com um formulário para receber\ndados dos usuários. A página exibirá um formulário com um campo para o nome e outro para o e-mail. Depois de obter essas duas informações do usuário, a página exibirá os valores inseridos de volta para confirmação.\n\nPara alcançar esse objetivo, além de criar uma [action](structure-controllers.md) e\nduas [views](structure-views.md), você também criará um [model](structure-models.md).\n\nNo decorrer deste tutorial, você aprenderá como:\n\n* criar um [model](structure-models.md) para representar os dados que o usuário insere por meio de um formulário\n* declarar regras (rules) para validar os dados inseridos\n* criar um formulário HTML em uma [view](structure-views.md)\n\n\nCriando um Model <span id=\"creating-model\"></span>\n----------------\n\nOs dados a serem solicitados do usuário serão representados por uma classe model\n`FormularioDeRegistro`, como visto a seguir, que será salva no arquivo `models/FormularioDeRegistro.php`. Por\nfavor, consulte a seção [Autoloading de Classes](concept-autoloading.md) para mais\ndetalhes sobre convenção de nomenclatura de arquivos de classes.\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse Yii;\nuse yii\\base\\Model;\n\nclass FormularioDeRegistro extends Model\n{\n    public $nome;\n    public $e_mail;\n\n    public function rules()\n    {\n        return [\n            [['nome', 'e_mail'], 'required'],\n            [['e_mail'], 'email'],\n        ];\n    }\n}\n```\n\nA classe estende de [[yii\\base\\Model]], uma classe base fornecida pelo Yii comumente usada para representar dados de formulários.\n\n> Info: [[yii\\base\\Model]] é usado como pai das classes de models que *não*\nsão associadas com tabelas de bancos de dados.\nEnquanto [[yii\\db\\ActiveRecord]], como pai das classes de models que correspondem a tabelas de bancos de dados.\n\nA classe `FormularioDeRegistro` contém dois atributos públicos, `nome` e `e_mail`, que são\nusados para armazenar os dados fornecidos pelo usuário. Ele também contém um método\nchamado `rules()`, que retorna um conjunto de regras para validação dos dados.\nAs regras de validação declaradas no código acima declaram que:\n\n* tanto o `nome` quanto o `e_mail` são obrigatórios\n* o `e_mail` deve ser preenchido com um e-mail sintaticamente válido (por exemplo, um valor sem @ não pode ser considerado válido para um e-mail, etc.)\n\nSe você tem um objeto `FormularioDeRegistro` preenchido com dados fornecidos pelo usuário,\nvocê pode chamar seu método [[yii\\base\\Model::validate()|validate()]] para iniciar as rotinas de validação dos dados. Se a validação de dados falhar, a propriedade\n[[yii\\base\\Model::hasErrors|hasErrors]] será definida como *true* e você pode\nsaber quais erros de validação ocorreram por consultar [[yii\\base\\Model::getErrors|errors]].\n\n```php\n<?php\n$model = new FormularioDeRegistro();\n$model->nome = 'Fulano';\n$model->e_mail = 'emailruim';\nif ($model->validate()) {\n    // Bom!\n} else {\n    // Falhou!\n    // Utilize $model->getErrors()\n}\n```\n\n\nCriando uma Action <span id=\"creating-action\"></span>\n------------------\n\nEm seguida, você precisará criar uma action `registro` no controller `site` que usará o novo model. O processo de criação e utilização de ações foi explicado na seção\n[Dizendo \"Olá!\"](start-hello.md).\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\nuse app\\models\\FormularioDeRegistro;\n\nclass SiteController extends Controller\n{\n    // ...código existente...\n\n    public function actionRegistro()\n    {\n        $model = new FormularioDeRegistro();\n\n        if ($model->load(Yii::$app->request->post()) && $model->validate()) {\n            // dados válidos recebidos no $model\n\n            // faça alguma coisa significativa com o $model aqui ...\n\n            return $this->render('confirmar-registro', ['model' => $model]);\n        } else {\n            // Ou a página esta sendo exibida inicial ou houve algum erro de validação\n            return $this->render('registro', ['model' => $model]);\n        }\n    }\n}\n```\n\nA primeira action cria um objeto `FormularioDeRegistro`. Ele, então, tenta preencher o model com os dados de `$_POST`, fornecidos no Yii por [[yii\\web\\Request::post()]].\nSe o model for preenchido (observe o método `load()`) com sucesso, ou seja, se o usuário\nenviou o formulário HTML, a action chamará o [[yii\\base\\Model::validate()|validate()]]\npara se certificar de que os valores fornecidos são válidos.\n\n> Info: A expressão `Yii::$app` representa a instância da\n  [aplicação](structure-applications.md), que é um \"singleton\" globalmente acessível.\n  Ao mesmo tempo, é um [service locator](concept-service-locator.md) que fornece componentes\n  tais como `request`, `response`, `db`, etc., para permitir funcionalidades específicas.\n  No código acima, o componente `request` da instância da aplicação é usado para acessar os dados do `$_POST`.\n\nSe tudo estiver certo, a action renderizará a view chamada `confirmar-registro` para confirmar ao usuário que os dados foram enviados corretamente. Se nenhum dado foi enviado ou se tiverem erros, a view `registro` será renderizada novamente\ne seu formulário HTML voltará a ser exibido mas, dessa vez, juntamente com as mensagens de erro de validação.\n\n> Nota: Neste exemplo muito simples, renderizamos a página de confirmação somente se os dados enviados eram válidos. Na prática, você deve considerar usar [[yii\\web\\Controller::refresh()|refresh()]] ou [[yii\\web\\Controller::redirect()|redirect()]]\n  para evitar [problemas de reenvio de formulário](https://en.wikipedia.org/wiki/Post/Redirect/Get).\n\n\nCriando Views <span id=\"creating-views\"></span>\n--------------\n\nPor fim, crie dois arquivos de views chamados `confirmar-registro` e `registro`. Essas serão as views renderizadas pela action `registro`, como acabamos de descrever acima.\n\nA view `confirmar-registro` simplesmente exibe os dados dos campos `nome` e `e_mail` e deve ser salva no arquivo `views/site/confirmar-registro.php`.\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n<p>Você enviou as seguintes informações:</p>\n\n<ul>\n    <li><label>Nome</label>: <?= Html::encode($model->nome) ?></li>\n    <li><label>E-mail</label>: <?= Html::encode($model->e_mail) ?></li>\n</ul>\n```\n\nA view `registro` exibe um formulário HTML e deve ser salva no arquivo `views/site/registro.php`.\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n?>\n<?php $form = ActiveForm::begin(); ?>\n\n    <?= $form->field($model, 'nome') ?>\n\n    <?= $form->field($model, 'e_mail') ?>\n\n    <div class=\"form-group\">\n        <?= Html::submitButton('Enviar', ['class' => 'btn btn-primary']) ?>\n    </div>\n\n<?php ActiveForm::end(); ?>\n```\n\nA view usa um poderoso [widget](structure-widgets.md) chamado\n[[yii\\widgets\\ActiveForm|ActiveForm]] para construir o formulário HTML. Os métodos\n`begin()` e `end()` do widget renderizam as tags de abertura e de fechamento do formulário.\nEntre as duas chamadas de método, campos são criados pelo\nmétodo [[yii\\widgets\\ActiveForm::field()|field()]]. O primeiro campo é para o nome e o segundo é para o e-mail. Após os campos,\no método [[yii\\helpers\\Html::submitButton()]] é chamado para criar um botão de envio do formulário (submit).\n\n\nConferindo <span id=\"trying-it-out\"></span>\n-------------\n\nPara ver como funciona, utilize seu navegador para acessar a seguinte URL:\n\n```\nhttps://hostname/index.php?r=site/registro\n```\n\nVocê verá uma página exibindo um formulário com dois campos. Na frente de cada campo, um *label* indica quais dados devem ser inseridos. Se você clicar\nno botão Enviar sem informar nenhum dado, ou se você não fornecer um e-mail\nválido, você verá uma mensagem de erro próxima a cada campo com problema.\n\n![Formulário com Erros de Validação](images/start-form-validation.png)\n\nApós informar um nome e e-mail válidos e clicar no botão de enviar, você verá uma\nnova página exibindo os dados informados por você.\n\n![Confirmação de Registro](images/start-entry-confirmation.png)\n\n\n\n### Mágica Explicada <span id=\"magic-explained\"></span>\n\nVocê talvez se pergunte como o formulário HTML trabalha por trás das cortinas, já que parece\nquase mágica exibir um *label* para cada campo e mostrar mensagens\nde erro sem recarregar a página quando você não informa os dados corretamente.\n\nSim, a validação de dados inicialmente é feita no cliente usando JavaScript e, posteriormente, realizada no servidor via PHP.\nO [[yii\\widgets\\ActiveForm]] é esperto o suficiente para extrair as regras de\nvalidação que você declarou no `FormularioDeRegistro`, transformá-las em código JavaScript executável e usar esse\ncódigo JavaScript para realizar a validação de dados. Caso você tenha desabilitado o JavaScript em seu navegador, a validação ainda será realizada no servidor, como mostrado\nno método `actionRegistro()`. Isso garante a validade dos dados em todas as circunstâncias.\n\n> Warning: (Alerta!) A validação feita no cliente é uma conveniência que fornece uma melhor\n  experiência para o usuário. A validação feita no servidor é sempre necessária, quer a validação no cliente aconteça, quer não.\n\nOs *labels* dos campos são gerados pelo método `field()`, usando os nomes\ndas propriedades do model.\nPor exemplo, um *label* chamado `Nome` será gerado para a propriedade `nome`.\n\nVocê pode personalizar um *label* em uma view utilizando o seguinte código:\n\n```php\n<?= $form->field($model, 'nome')->label('Seu Nome') ?>\n<?= $form->field($model, 'e_mail')->label('Seu E-mail') ?>\n```\n\n> Info: O Yii fornece muitos desses widgets para ajudá-lo a construir rapidamente views dinâmicas e complexas.\n  Conforme você vai aprender mais tarde, escrever um novo widget também é extremamente fácil.\n  Você talvez queira transformar grande parte do código de suas views em widgets reutilizáveis\n  para simplificar o desenvolvimento de views no futuro.\n\n\nResumo <span id=\"summary\"></span>\n-------\n\nNesta seção do guia, você teve uma introduçao à última parte do padrão de arquitetura MVC. Você aprendeu como criar uma classe model para representar os dados do usuário e validá-los.\n\nTambém aprendeu como obter os dados enviados pelos usuários e como exibi-los de\nvolta no navegador. Essa é uma tarefa que poderia tomar muito tempo ao desenvolver uma aplicação, mas o Yii fornece widgets poderosos que tornam o processo muito simples.\n\nNa próxima seção, você aprenderá como trabalhar com bancos de dados, que são\nnecessários em quase todas as aplicações.\n"
  },
  {
    "path": "docs/guide-pt-BR/start-gii.md",
    "content": "Gerando Código com Gii\n========================\n\nEssa seção irá descrever como usar o [Gii](https://github.com/yiisoft/yii2-gii/blob/master/docs/guide-pt-BR/README.md) para  gerar automaticamente código que implementa algumas funcionalidades comuns de sites. Usar o Gii para gerar código é simplesmente uma questão de informar os dados corretos conforme as instruções mostradas nas páginas do Gii.\n\nAtravés desse tutorial, você irá aprender a:\n\n* habilitar o Gii em sua aplicação,\n* usar o Gii para gerar uma classe Active Record,\n* usar o Gii para gerar código que implementa as operações CRUD para uma tabela do banco de dados, e\n* personalizar o código gerado pelo Gii.\n\n\nComeçando a usar o Gii <span id=\"starting-gii\"></span>\n------------\n\nO [Gii](https://github.com/yiisoft/yii2-gii/blob/master/docs/guide-pt-BR/README.md) é fornecido como um [módulo](structure-modules.md) do Yii. Você pode habilitar o Gii ao configurá-lo na propriedade [[yii\\base\\Application::modules|modules]] da aplicação. Dependendo de como você criou sua aplicação, você pode encontrar o seguinte código já pronto no arquivo de configuração `config/web.php`:\n\n```php\n$config = [ ... ];\n\nif (YII_ENV_DEV) {\n    $config['bootstrap'][] = 'gii';\n    $config['modules']['gii'] = [\n        'class' => 'yii\\gii\\Module',\n    ];\n}\n```\n\nA configuração acima declara que, quando estiver usando o [ambiente de desenvolvimento](concept-configurations.md#environment-constants), a aplicação deve incluir um módulo chamado `gii`, da classe [[yii\\gii\\Module]].\n\nSe você verificar o [script de entrada](structure-entry-scripts.md) `web/index.php` da sua aplicação, você encontrará a seguinte linha que, basicamente, torna  `YII_ENV_DEV` verdadeira.\n\n```php\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n```\n\nGraças a essa linha, sua aplicação está em modo de desenvolvimento e terá o Gii habilitado, devido a configuração mais acima. Agora você pode acessar o Gii pela URL:\n\n```\nhttps://hostname/index.php?r=gii\n```\n\n> Note: Se você está acessando o Gii por um endereço IP que não seja o localhost, o acesso será negado por padrão, por questões de segurança.\n> Você pode configurar o Gii adicionando endereços IP permitidos como mostrado a seguir:\n>\n```php\n'gii' => [\n    'class' => 'yii\\gii\\Module',\n    'allowedIPs' => ['127.0.0.1', '::1', '192.168.0.*', '192.168.178.20'] // ajuste de acordo com suas necessidades\n],\n```\n\n![Gii](images/start-gii.png)\n\n\nGerando uma classe Active Record <span id=\"generating-ar\"></span>\n---------------------------------\n\nPara gerar uma classe Active Record usando o Gii, selecione o \"Model Generator\" clicando no link na página inicial do Gii. Então, preencha o formulário como indicado abaixo:\n\n* Nome da tabela: `pais`\n* Classe do modelo: `Pais`\n\n![Gerador de Modelo](images/start-gii-model.png)\n\nEm seguida, clique no botão \"Preview\". Você verá o `models/Pais.php` listado como arquivo a ser criado. Você pode clicar no nome do arquivo para pré-visualizar seu conteúdo.\n\nAo usar o Gii, se você já havia criado o mesmo arquivo e pretende sobrescrevê-lo, clique no botão `diff` próximo ao nome do arquivo para ver as diferenças entre o código a ser gerado e a versão já existente.\n\n![Pré-visualização do Gerador de Modelo](images/start-gii-model-preview.png)\n\nQuando estiver sobrescrevendo um arquivo, marque a caixa próxima a \"overwrite\" (sobrescrever) e clique no botão \"Generate\". Se estiver criando um novo arquivo, apenas clique em \"Generate\".\n\nEm seguida, você verá uma página de confirmação indicando que o código foi gerado com sucesso. Se você já tinha um arquivo, também verá uma mensagem indicando que ele foi sobrescrito pelo novo código.\n\n\nGerando código CRUD <span id=\"generating-crud\"></span>\n--------------------\n\nCRUD significa a Create, Read, Update, and Delete (criar, ler, atualizar e apagar), representando as quatro tarefas comuns feitas com os dados na maioria das aplicações. Para criar funcionalidades CRUD usando o Gii, selecione \"CRUD Generator\" clicando no link na página inicial do Gii. Seguindo o exemplo \"pais\", preencha o formulário com as seguintes informações:\n\n* Model Class: `app\\models\\Pais`\n* Search Model Class: `app\\models\\PaisSearch`\n* Controller Class: `app\\controllers\\PaisController`\n\n![Gerador de CRUD](images/start-gii-crud.png)\n\nEm seguida, clique no botão \"Preview\". Você verá uma lista de arquivos a serem gerados, como mostrado abaixo.\n\n![Pré-visualização do Gerador de CRUD](images/start-gii-crud-preview.png)\n\nSe você criou anteriormente os arquivos `controllers/PaisController.php` e\n`views/pais/index.php` (na seção de banco de dados deste guia), marque a caixa \"overwrite\" para substitui-los. (As versões anteriores não tinham suporte completo às operações CRUD.)\n\n\nConferindo <span id=\"trying-it-out\"></span>\n-------------\n\nPara ver como ficou, use seu navegador para acessar a seguinte URL:\n\n```\nhttps://hostname/index.php?r=pais/index\n```\n\nVocê verá uma tabela mostrando os países do seu banco de dados. Você pode ordená-los ou filtrá-los inserindo condições nos cabeçalhos das colunas.\n\nPara cada país exibido na tabela, você pode visualizar detalhes, editar ou excluir.\nVocê também pode clicar no botão “Create Pais” no topo da tabela para ter acesso a um formulário para cadastrar um novo país.\n\n![Grade de Países](images/start-gii-country-grid.png)\n\n![Editando um País](images/start-gii-country-update.png)\n\nEsta é uma lista de arquivos gerados pelo Gii, caso você queira investigar como essas funcionalidades estão implementadas ou ajustá-las:\n\n* Controller: `controllers/PaisController.php`\n* Modelo: `models/Pais.php` e `models/PaisSearch.php`\n* Views: `views/pais/*.php`\n\n> Info: o Gii é projetado para ser uma ferramenta altamente adaptável e extensível. Usando-o sabiamente, você pode acelerar bastante o desenvolvimento de sua aplicação. Para mais detalhes, por favor, consulte a seção [Gii](https://github.com/yiisoft/yii2-gii/blob/master/docs/guide-pt-BR/README.md).\n\n\nResumo <span id=\"summary\"></span>\n-------\n\nNessa seção, você aprendeu a usar o Gii para gerar código que implementa todas as operações CRUD para o conteúdo armazenado em uma tabela de banco de dados.\n"
  },
  {
    "path": "docs/guide-pt-BR/start-hello.md",
    "content": "Dizendo \"Olá!\"\n=====================\n\nEsta seção descreve como criar uma nova página de \"Olá!\" em sua aplicação.\nPara atingir este objetivo, você criará uma [action](structure-controllers.md#creating-actions)\ne uma [view](structure-views.md):\n\n* A aplicação enviará a requisição de página para a action\n* e a action, por sua vez, renderizará a view que mostra a palavra \"Olá!\" ao usuário final.\n\nAtravés deste tutorial, você aprenderá três coisas:\n\n1. Como criar uma [action](structure-controllers.md) para responder às requisições,\n2. como criar uma [view](structure-views.md) para compor o conteúdo da resposta, e\n3. como uma aplicação envia requisições às [actions](structure-controllers.md#creating-actions).\n\n\nCriando uma Action <span id=\"creating-action\"></span>\n----------------\n\nPara a tarefa \"Olá!\", você criará uma [action](structure-controllers.md#creating-actions)\n`cumprimentar` que lê um parâmetro `mensagem` da requisição e exibe essa mensagem de volta\npara o usuário. Se a requisição não fornecer um parâmetro `mensagem`, a action exibirá a mensagem padrão \"Olá!\".\n\n> Info: [Actions](structure-controllers.md#creating-actions) são os objetos que usuários finais podem solicitar diretamente para execução. Actions são\n  agrupadas nos [controllers](structure-controllers.md). O resultado da execução de uma action é a resposta que o usuário final receberá.\n\nAs actions devem ser declaradas em [controllers](structure-controllers.md). Para manter a simplicidade, você pode declarar a action `cumprimentar` na classe já existente\n`SiteController`. Esse controller está definido no arquivo `controllers/SiteController.php`.\nSegue aqui o início da nova action:\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    // ...código existente...\n\n    public function actionCumprimentar($mensagem = 'Olá!')\n    {\n        return $this->render('cumprimentar', ['mensagem' => $mensagem]);\n    }\n}\n```\n\nNo código acima, a action `cumprimentar` está definida como um método chamado `actionCumprimentar`\nna classe `SiteController`. O Yii usa o prefixo `action` para diferenciar os métodos\nde actions dos métodos que não são de actions em uma classe de controller. O nome após o prefixo `action` é mapeado como o ID da action.\n\nQuando se trata de dar nome às suas actions, você deveria entender como o Yii\ntrata os IDs de actions. Os IDs de actions são sempre referenciados em minúsculo.\nSe o ID de uma action necessitar de múltiplas palavras, elas serão concatenadas\npor hífens (por exemplo, `criar-comentario`). Os IDs de actions são convertidos em nomes de actions removendo-se os hífens dos IDs, colocando em maiúscula a\nprimeira letra de cada palavra, e prefixando o resultado com a palavra `action`. Por exemplo,\no ID de action `criar-comentario` corresponde ao método de action `actionCriarComentario`.\n\nO método da action em nosso exemplo recebe um parâmetro `$mensagem`, cujo valor\npadrão é \"Olá!\" (exatamente da mesma forma que você define um valor padrão para\n  qualquer argumento de função ou método no PHP). Quando a aplicação recebe a\nrequisição e determina que a action `cumprimentar` é responsável por tratar a requisição,\na aplicação vai preencher esse parâmetro com o parâmetro que tiver o mesmo nome\nna requisição. Em outras palavras, se a requisição inclui um parâmetro `mensagem`\ncom o valor `\"Adeus!\"`, a variável `$mensagem` na action receberá esse valor.\n\nDentro do método da action, [[yii\\web\\Controller::render()|render()]] é chamado\npara renderizar um arquivo de [view](structure-views.md) chamado `cumprimentar`. O\nparâmetro `mensagem` também é passado para a view de modo que ele possa ser usado\npor ela. O resultado da renderização da view é retornado pelo método da action. Esse resultado\nserá recebido pela aplicação e exibido para o usuário final no navegador (como parte de uma página HTML completa).\n\n\nCriando uma View <span id=\"creating-view\"></span>\n-----------------\n\nAs [views](structure-views.md) são scripts que você escreve para gerar o conteúdo de uma resposta.\nPara a tarefa \"Olá!\", você criará uma view `cumprimentar` que exibe o parâmetro `mensagem` recebido do método da action:\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n<?= Html::encode($mensagem) ?>\n```\n\nA view `cumprimentar` deve ser salva no arquivo `views/site/cumprimentar.php`. Quando o método\n[[yii\\web\\Controller::render()|render()]] é chamado em uma action, ele procurará\no arquivo PHP em `views/IDdoController/NomeDaView.php`.\n\nPerceba que no código acima o parâmetro `mensagem` é [[yii\\helpers\\Html::encode()|codificado como HTML]]\nantes de ser impresso. Isso é necessário, já que o parâmetro vem de um usuário final,\ntornando-o vulnerável a [ataques de cross-site scripting (XSS)](https://en.wikipedia.org/wiki/Cross-site_scripting)\npor embutir código JavaScript malicioso no parâmetro.\n\nNaturalmente, você pode colocar mais conteúdo na view `cumprimentar`. O conteúdo pode consistir\nde tags HTML, texto puro, ou até mesmo instruções de PHP. De fato, a view `cumprimentar` é\napenas um script PHP que é executado pelo método [[yii\\web\\Controller::render()|render()]].\nO conteúdo impresso pelo script da view será retornado à aplicação como o resultado\nda resposta. A aplicação, por sua vez, retornará esse resultado para o usuário final.\n\n\nConferindo <span id=\"trying-it-out\"></span>\n--------\n\nApós criar a action e a view, você pode acessar a nova página através da seguinte URL:\n\n```\nhttps://hostname/index.php?r=site/cumprimentar&mensagem=Olá+Mundo!\n```\n\n![Olá Mundo!](images/start-hello-world.png)\n\nEssa URL resultará em uma página exibindo \"Olá Mundo!\". Essa página compartilha o mesmo cabeçalho e rodapé das outras páginas da aplicação.\n\nSe você omitir o parâmetro `mensagem` na URL, você verá a página exibindo somente\n\"Olá!\". Isso ocorre por que `mensagem` é passado como um parâmetro para o método `actionCumprimentar()` e,\nquando ele é omitido, o valor padrão `\"Olá!\"` é usado em seu lugar.\n\n> Info: A nova página compartilha o mesmo cabeçalho e rodapé de outras páginas\n  porque o método [[yii\\web\\Controller::render()|render()]] vai automaticamente\n  incluir o resultado da view `cumprimentar` em um [layout](structure-views.md#layouts)\n  que neste caso está localizado em `views/layouts/main.php`.\n\nO parâmetro `r` na URL acima requer mais explicação. Ele significa [rota](runtime-routing.md),\num ID único e amplo de uma aplicação que se refere a uma action. O formato da rota\né `IDdoController/IDdaAction`. Quando a aplicação recebe uma requisição, ela\nverificará esse parâmetro e usará a parte `IDdoController` para determinar qual\nclasse de controller deve ser instanciada para tratar a requisição. Então o\ncontroller usará a parte `IDdaAction` para determinar qual action deverá ser\ninstanciada para fazer o trabalho. No caso deste exemplo, a rota `site/cumprimentar` será\nresolvida como a classe de controller `SiteController` e a action `cumprimentar`. Como\nresultado, o método `SiteController::actionCumprimentar()` será chamado para tratar a requisição.\n\n> Info: Assim como as actions, os controllers também possuem IDs que os identificam\n  de maneira única em uma aplicação. IDs de controllers seguem as mesmas regras de nomenclatura dos IDs de actions. Os nomes das classes de controlllers\n  derivam dos IDs de controllers removendo-se os hífens dos IDs, convertendo a\n  primeira letra de cada palavra em maiúscula, e adicionando o sufixo `Controller`.\n  Por exemplo, o ID de controller `comentario-de-artigo` corresponde ao nome de classe\n  de controller `ComentarioDeArtigoController`.\n\n\nResumo <span id=\"summary\"></span>\n------\n\nNesta seção, você teve uma introdução sobre as partes controller e view do padrão de arquitetura MVC. Você criou uma action como parte de um controller para tratar uma requisição específica.\nE você também criou uma view para compor o conteúdo da resposta. Nesse exemplo simples, nenhum modelo (model) foi\nutilizado, já que o único dado exibido foi o parâmetro `mensagem`.\n\nVocê também aprendeu sobre as rotas no Yii, que agem como a ponte entre as\nrequisições de usuário e as actions de controllers.\n\nNa próxima seção, você aprenderá como criar um modelo (model) e adicionar uma nova página contendo um formulário HTML.\n"
  },
  {
    "path": "docs/guide-pt-BR/start-installation.md",
    "content": "Instalando o Yii\n================\n\nVocê pode instalar o Yii de duas maneiras: usando o gerenciador de pacotes [Composer](https://getcomposer.org/)\nou baixando um arquivo compactado. O primeiro modo é o preferido, já que permite\nque você instale novas [extensões](structure-extensions.md) ou atualize o\nYii simplesmente executando um único comando.\n\nA instalação padrão do Yii resulta no download e instalação tanto do framework\nquanto de um template de projetos.\nUm template de projeto é um projeto funcional do Yii que implementa alguns recursos básicos, tais como: autenticação, formulário de contato, etc.\nEste código é organizado de uma forma recomendada. Portanto, ele pode servir\ncomo ponto de partida para seus projetos.\n\nNesta e nas próximas seções, vamos descrever como instalar o *Template Básico\nde Projetos* do Yii e como implementar novas funcionalidades sobre este template.\nO Yii fornece ainda outro template chamado [Template Avançado de Projetos](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide-pt-BR/README.md) que é melhor usado em um ambiente de desenvolvimento em equipe e para desenvolver\naplicações com multiplas camadas.\n\n> Info: O Template Básico de Projetos é adequado para o desenvolvimento de\ncerca de 90% das aplicações Web. Ele difere do Template Avançado de\nProjetos principalmente em como o seu código é organizado. Se você é\nnovo no Yii, recomendamos fortemente escolher o Template Básico de Projetos\npela sua simplicidade e por manter suficientes funcionalidades.\n\n\nInstalando via Composer <span id=\"installing-via-composer\"></span>\n-----------------------\n\n### Instalando o Composer\n\nSe você ainda não tem o Composer instalado, você pode instalá-lo seguindo as instruções\nem [getcomposer.org](https://getcomposer.org/download/). No Linux e no Mac OS X,\nvocê executará os seguintes comandos:\n\n    curl -sS https://getcomposer.org/installer | php\n    mv composer.phar /usr/local/bin/composer\n\nNo Windows, você baixará e executará o [Composer-Setup.exe](https://getcomposer.org/Composer-Setup.exe).\n\nPor favor, consulte a seção de [Resolução de Problemas do Composer](https://getcomposer.org/doc/articles/troubleshooting.md) se você encontrar dificuldades. Se você é novo no assunto, nós também recomendamos que leia pelo menos a seção [Uso Básico](https://getcomposer.org/doc/01-basic-usage.md) na documentação do Composer.\n\nNeste guia, todos os comandos do composer assumem que você o tem instaldo [globalmente](https://getcomposer.org/doc/00-intro.md#globally) de modo que ele seja acessível através do comando `composer`. Se em vez disso estiver usando o `composer.phar` no diretório local, você tem que ajustar os comandos de exemplo de acordo.\n\nSe você já tem o Composer instalado, certifique-se de usar uma versão atualizada.\nVocê pode atualizar o Composer executando o comando `composer self-update`.\n\n> Note: Durante a instalação do Yii, o Composer precisará solicitar muitas informações da API do Github.\n> A quantidade de solicitações depende do número de dependências que sua aplicação possui e pode extrapolar a\n> **taxa limite da API do Github**. Se você atingir esse limite, o Composer pode pedir a você suas credenciais de login para obter um\n> token de acesso à API Github. Em conexões rápidas você pode atingir esse limite antes que o Composer consiga lidar com a situação, então, recomendamos\n> configurar um token de acesso antes de instalar o Yii.\n> Por favor, consulte a [documentação do Composer sobre tokens da API Github](https://getcomposer.org/doc/articles/troubleshooting.md#api-rate-limit-and-oauth-tokens)\n> para instruções de como fazer isso.\n\n\n### Instalando o Yii <span id=\"installing-from-composer\"></span>\n\nCom o Composer instalado, você pode instalar o Yii executando o seguinte comando\nem um diretório acessível pela Web:\n\n```bash\ncomposer create-project --prefer-dist yiisoft/yii2-app-basic basico\n```\n\nIsto vai instalar a versão estável mais recente do Yii em um diretório chamado `basico`.\nVocê pode especificar um nome de diretório diferente se quiser.\n\n> Info: Se o comando `composer create-project` falhar, você pode consultar a\n> [seção de Resolução de Problemas na documentação do Composer](https://getcomposer.org/doc/articles/troubleshooting.md)\n> para verificar erros comuns. Quando tiver corrigido o erro, você pode continuar a instalação abortada por executar o comando\n> `composer update` dentro do diretório `basico`.\n\n> Tip: Se em disso você quiser instalar a versão em desenvolvimento mais recente do Yii, use o comando a seguir\n> que adiciona uma [opção de estabilidade](https://getcomposer.org/doc/04-schema.md#minimum-stability):\n>\n> ```bash\n> composer create-project --prefer-dist --stability=dev yiisoft/yii2-app-basic basic\n> ```\n>\n> Note que a versão do Yii em desenvolvimento não deve ser usada em produção visto que pode quebrar seu código funcional.\n\n\nInstalando a partir de um Arquivo Compactado <span id=\"installing-from-archive-file\"></span>\n--------------------------------------------\n\nA instalação do Yii a partir de um arquivo compactado envolve três passos:\n\n1. Baixe o arquivo compactado em [yiiframework.com](https://www.yiiframework.com/download/).\n2. Descompacte o arquivo baixado em um diretório acessível pela Web.\n3. Modifique o arquivo `config/web.php` informando uma chave secreta no item de\nconfiguração `cookieValidationKey` (isto é feito automaticamente se você instalar\no Yii pelo Composer):\n\n    ```php\n   // !!! Informe a chave secreta no item a seguir (se estiver vazio) - isto é requerido para a validação do cookie\n   'cookieValidationKey' => 'enter your secret key here',\n   ```\n\n\nOutras Opções de Instalação <span id=\"other-installation-options\"></span>\n---------------------------\n\nAs instruções de instalação acima mostram como instalar o Yii, que também cria\numa aplicação Web básica que funciona imediatamente sem qualquer configuração ou\nmodificação (*out of the box*).\nEsta abordagem é um bom ponto de partida para a maioria dos projetos, seja ele\npequeno ou grande. É especialmente adequado se você acabou de começar a aprender Yii.\n\nNo entanto, existem outras opções de instalação disponíveis:\n\n* Se você só quer instalar o núcleo (*core*) do framework e gostaria de construir uma aplicação\n  inteira do zero, você pode seguir as instruções em\n  [Construindo uma Aplicação a Partir do Zero](tutorial-start-from-scratch.md).\n* Se você quiser começar com uma aplicação mais sofisticada, mais adequada ao\n  ambiente de desenvolvimento em equipe, você pode considerar instalar o\n  [Template Avançado de Projetos](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide-pt-BR/README.md).\n\n\nInstalando Recursos Estáticos (Assets) <span id=\"installing-assets\"></span>\n-----------------\n\nYii utiliza os pacotes [Bower](https://bower.io/) e/ou [NPM](https://www.npmjs.com/) para a instalação das bibliotecas de recursos estáticos (CSS and JavaScript).\nEle usa composer para obter essa bibliotecas, permitindo que versões de pacotes PHP, CSS e Javascrtip possam ser definidas/instaladas ao mesmo tempo.\nIsto é possível por usar ou [asset-packagist.org](https://asset-packagist.org) ou [composer asset plugin](https://github.com/fxpio/composer-asset-plugin).\nPor favor, consulta a [documentação sobre Assets](structure-assets.md) para mais detalhes.\n\nVocê pode querer gerenciar assets através de clientes nativos do Bower ou NPM, pode querer utilizar CDNs ou até evitar completamente a instalação de recursos estáticos.\nPara evitar que recursos estáticos sejam instalados via Composer, adicione o seguinte código ao seu `composer.json`:\n\n```json\n\"replace\": {\n    \"bower-asset/jquery\": \">=1.11.0\",\n    \"bower-asset/inputmask\": \">=3.2.0\",\n    \"bower-asset/punycode\": \">=1.3.0\",\n    \"bower-asset/yii2-pjax\": \">=2.0.0\"\n},\n```\n\n> Note: caso a instalação de recursos estáticos via Composer seja evitada, caberá a você instalar e resolver conflitos de versão ao instalar recursos estáticos (assets).\n> Esteja preparado para possíveis inconsistências entre arquivos de recursos estáticos de diferentes extensões.\n\n\nVerificando a Instalação <span id=\"verifying-installation\"></span>\n------------------------\n\nApós a instalação ser concluída, você pode tanto configurar seu servidor web (veja na próxima seção) como usar o\n[servidor web embutido do PHP](https://www.php.net/manual/pt_BR/features.commandline.webserver.php) executando o seguinte comando de console no diretório `web`:\n\n```bash\nphp yii serve\n```\n\n> Note: Por padrão o servidor HTTP vai ouvir na porta 8080. Contudo, se essa porta já estiver em uso ou se você pretende servir múltiplas aplicações desta forma, você pode querer especificar qual porta será usada. Para isso,\nbasta adicionar o argumento `--port`:\n\n```bash\nphp yii serve --port=8888\n```\n\nVocê pode usar seu navegador para acessar a aplicação instalada por meio da seguinte URL:\n\n```\nhttp://localhost:8080/\n```\n\n![Yii Instalado com Sucesso](images/start-app-installed.png)\n\nVocê deverá ver a página de parabenização acima em seu navegador. Se não a vir, por favor, verifique se sua instalação PHP satisfaz os requisitos do Yii. Você pode verificar se os requisitos mínimos são atingidos usando uma das seguintes abordagens:\n\n* Copiar `/requirements.php` para `/web/requirements.php` e então usar um navegador para acessá-lo por meio da URL `http://localhost/requirements.php`\n* Executar os seguintes comandos:\n\n  ```bash\n  cd basico\n  php requirements.php\n  ```\n\nVocê deve configurar sua instalação PHP de forma a atingir os requisitos mínimos do Yii. A versão mínima do PHP que você deve ter é a 5.4. Mas o ideal seria utilizar a versão mais recente, PHP 7.\nSe sua aplicação precisa de um banco de dados, você também deve instalar a [Extensão PDO PHP](https://www.php.net/manual/pt_BR/pdo.installation.php) e o driver de banco de dados correspondente (tal como `pdo_mysql` para bancos de dados MySQL).\n\n\nConfigurando Servidores Web <span id=\"configuring-web-servers\"></span>\n------------------------------\n\n> Info: Você pode pular essa subseção por enquanto se estiver fazendo somente um test drive do Yii sem a intenção de publicá-lo em um servidor de produção.\n\nA aplicação instalada de acordo com as instruções acima deve funcionar imediatamente\ncom um [Servidor HTTP Apache](https://httpd.apache.org/) ou um [Servidor HTTP Nginx](https://nginx.org/),\nno Windows, Mac OS X ou Linux usando PHP 5.4 ou superior. O Yii 2.0 também é compatível\ncom o [HHVM](https://hhvm.com/) do Facebook. No entanto, existem alguns casos extremos em que o HHVM se comporta diferentemente do PHP nativo, então você terá que ter um cuidado extra quando usar o HHVM.\n\nEm um servidor de produção, você pode querer configurar o seu servidor Web de\nmodo que a aplicação possa ser acessada pela URL `https://www.example.com/index.php`\nao invés de `https://www.example.com/basico/web/index.php`. Tal configuração requer que\nvocê aponte a raiz dos documentos de seu servidor Web para o diretório `basico/web`.\nVocê também pode querer ocultar o `index.php` da URL, conforme descrito na seção\n[Roteamento e Criação de URL](runtime-routing.md). Nessa sub-seção, você\naprenderá como configurar o seu servidor Apache ou Nginx para atingir estes\nobjetivos.\n\n> Info: Definindo `basico/web` como a raiz dos documentos, você também evita que\n  usuários finais acessem o código privado de sua aplicação e os arquivos de\n  dados sensíveis que estão armazenados em diretórios no mesmo nível de `basico/web`.\n  Negar o acesso a estes outros diretórios é uma melhoria de segurança.\n\n> Info: Se a sua aplicação rodará em um ambiente de hospedagem compartilhada\n  onde você não tem permissão para alterar a configuração do seu servidor Web,\n  você ainda pode ajustar a estrutura de sua aplicação para uma melhor segurança.\n  Por favor, consulte a seção [Ambiente de Hospedagem Compartilhada](tutorial-shared-hosting.md)\n  para mais detalhes.\n\n\n### Configuração do Apache Recomendada <span id=\"recommended-apache-configuration\"></span>\n\nUse a seguinte configuração no arquivo `httpd.conf` do Apache ou em uma\nconfiguração de virtual host. Perceba que você pode deve substituir `caminho/para/basico/web`\ncom o caminho real para `basico/web`.\n\n```apache\n# Torna \"basico/web\" a raíz de documentos\nDocumentRoot \"caminho/para/basico/web\"\n\n<Directory \"caminho/para/basico/web\">\n    # Usa mod_rewrite para suporte a URLs amigáveis\n    RewriteEngine on\n\n    # Se $showScriptName for \"false\" no UrlManager, impede o acesso a URLs que tenham o nome do script (index.php)\n    RewriteRule ^index.php/ - [L,R=404]\n\n    # Se um arquivo ou diretório existe, usa a solicitação diretamente\n    RewriteCond %{REQUEST_FILENAME} !-f\n    RewriteCond %{REQUEST_FILENAME} !-d\n\n    # Caso contrário, redireciona para index.php\n    RewriteRule . index.php\n\n    # ... outras configurações ...\n</Directory>\n```\n\n\n### Configuração do Nginx Recomendada <span id=\"recommended-nginx-configuration\"></span>\n\nPara usar o [Nginx](https://wiki.nginx.org/), você deve ter instalado o PHP como um [FPM SAPI](https://www.php.net/manual/pt_BR/install.fpm.php). Use a seguinte configuração do Nginx,\nsubstituindo `caminho/para/basico/web` com o caminho real para `basico/web` e `mysite.test` com o nome de host real a servir.\n\n```nginx\nserver {\n    charset utf-8;\n    client_max_body_size 128M;\n\n    listen 80; ## listen for ipv4\n    #listen [::]:80 default_server ipv6only=on; ## listen for ipv6\n\n    server_name mysite.test;\n    root        /caminho/para/basico/web;\n    index       index.php;\n\n    access_log  /caminho/para/basico/log/access.log;\n    error_log   /caminho/para/basico/log/error.log;\n\n    location / {\n        # Redireciona tudo que não é um arquivo real para index.php\n        try_files $uri $uri/ /index.php$is_args$args;\n    }\n\n    # Descomente para evitar processar chamadas feitas pelo Yii a arquivos estáticos não existentes\n    #location ~ \\.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ {\n    #    try_files $uri =404;\n    #}\n    #error_page 404 /404.html;\n\n    # Nega acesso a arquivos php no diretório /assets\n    location ~ ^/assets/.*\\.php$ {\n        deny all;\n    }\n\n    location ~ \\.php$ {\n        include fastcgi_params;\n        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;\n        fastcgi_pass 127.0.0.1:9000;\n        #fastcgi_pass unix:/var/run/php5-fpm.sock;\n        try_files $uri =404;\n    }\n\n    location ~* /\\. {\n        deny all;\n    }\n}\n```\n\nAo usar esta configuração, você também deve definir `cgi.fix_pathinfo=0` no arquivo `php.ini`\nde modo a evitar muitas chamadas desnecessárias ao comando `stat()` do sistema.\n\nTambém perceba que ao rodar um servidor HTTPS, você precisa adicionar `fastcgi_param HTTPS on;`,\nde modo que o Yii possa detectar adequadamente se uma conexão é segura.\n"
  },
  {
    "path": "docs/guide-pt-BR/start-looking-ahead.md",
    "content": "Seguindo em Frente\n==================\n\nSe você leu toda a seção \"Primeiros Passos\", você criou uma aplicação Yii\ncompleta. No processo, você aprendeu como implementar algumas funcionalidades comumente necessárias, tais como obter dados de usuários através de um formulário\nHTML, consultar dados de um banco de dados e exibir os dados de forma paginada.\nVocê também aprendeu a usar o [Gii](tool-gii.md) para gerar código automaticamente.\nUsar o Gii para a geração de código torna a carga do seu processo de desenvolvimento\nWeb uma tarefa tão simples quanto preencher alguns formulários.\n\nEsta seção resume recursos Yii disponíveis que o ajudarão a ser mais produtivo usando o framework.\n\n* Documentação\n    - [O Guia Definitivo](https://www.yiiframework.com/doc-2.0/guide-README.html):\n      Conforme o nome indica, o guia define precisamente como o Yii deve funcionar\n      e fornece orientações gerais sobre como usá-lo. É o tutorial mais importante e que você deveria ler antes de escrever qualquer código Yii.\n    - [A Referência de Classes](https://www.yiiframework.com/doc-2.0/index.html):\n      Especifica o uso de todas as classes fornecidas pelo Yii. Deve ser\n      principalmente usada quando você estiver escrevendo código e quiser entender o uso de uma classe, método ou propriedade específicos. O uso da referência\n      de classes é idealmente melhor somente depois de um entendimento contextual do framework como um todo.\n    - [Os Artigos Wiki](https://www.yiiframework.com/wiki/?tag=yii2):\n      Os artigos wiki são escritos por usuários do Yii baseados em suas próprias experiências. A maioria deles são escritos como receitas de bolo e mostram\n      como resolver problemas específicos usando o Yii. Ainda que a qualidade desses artigos possa não ser tão boa quanto a do Guia Definitivo, eles são úteis porque são abrangem assuntos adicionais e frequentemente fornecem\n      soluções prontas para serem usadas.\n    - [Livros](https://www.yiiframework.com/books)\n* [Extensões](https://www.yiiframework.com/extensions/):\n  O Yii ostenta uma biblioteca de milhares de extensões contribuídas por usuários que podem facilmente ser plugadas em suas aplicações, tornando\n  o seu desenvolvimento ainda mais rápido e mais fácil.\n* Comunidade\n    - Fórum: <https://forum.yiiframework.com/>\n    - Chat IRC: O canal #yii na rede Libera (<ircs://irc.libera.chat:6697/yii>)\n    - Canal Slack: <https://yii.slack.com>\n    - Chat Gitter: <https://gitter.im/yiisoft/yii2>\n    - GitHub: <https://github.com/yiisoft/yii2>\n    - Facebook: <https://www.facebook.com/groups/yiitalk/>\n    - Twitter: <https://twitter.com/yiiframework>\n    - LinkedIn: <https://www.linkedin.com/groups/yii-framework-1483367>\n    - Stackoverflow: <https://stackoverflow.com/questions/tagged/yii2>\n"
  },
  {
    "path": "docs/guide-pt-BR/start-prerequisites.md",
    "content": "# O que você precisa saber\n\nA curva de aprendizado no Yii não é tão íngreme como em outros frameworks PHP mas, ainda assim, há algumas coisas que você devia aprender antes de começar.\n\n## PHP\n\nYii é um framework PHP. Portanto, certifique-se de [ler e entender a referência da linguagem](https://www.php.net/manual/pt_BR/langref.php).\nQuando estiver desenvolvendo com Yii, você estará escrevendo código orientado a objetos, então, certifique-se de que está familiarizado tanto com [Classes e Objetos](https://www.php.net/manual/pt_BR/language.oop5.basic.php) como com [namespaces](https://www.php.net/manual/pt_BR/language.namespaces.php).\n\n## Programação orientada a objetos\n\nÉ necessário ter conhecimentos básicos de programação orientada a objetos. Se você não está familizarizado com esse tipo de programação, acesse um dos muitos tutoriais disponíveis, como [este do tuts+](https://code.tutsplus.com/tutorials/object-oriented-php-for-beginners--net-12762).\n\nNote que, quanto mais complicado for seu projeto ou aplicação, mais você precisará de conceitos avançados de POO (Programação Orientada a Objetos) para ser bem sucedido em tratar essa complexidade.\n\n## Linha de comando and composer\n\nYii usa extensivamente o gerenciador de pacotes mais utilizado do PHP, o [Composer](https://getcomposer.org/), então certifique-se de ler e entender seu [guia](https://getcomposer.org/doc/01-basic-usage.md). Se você não tem familiaridade com a linha de comando é hora de começar a experimentar. Quando tiver aprendido o básico, nunca mais vai querer trabalhar sem ela.\n\n"
  },
  {
    "path": "docs/guide-pt-BR/start-workflow.md",
    "content": "Executando Aplicações\n=====================\n\nApós instalar o Yii, você tem uma aplicação Yii funcional que pode ser acessada\npela URL `https://hostname/basico/web/index.php` ou `https://hostname/index.php`,\ndependendo de sua configuração. Esta seção introduzirá a funcionalidade embutida\nda aplicação, como o código é organizado e como a aplicação trata as requisições em geral.\n\n> Info: Por questões de simplicidade, por todo este tutorial de \"Primeiros Passos\"\n  assume-se que você definiu `basico/web` como a raiz de documentos do seu\n  servidor Web e configurou a URL de acesso de sua aplicação como `https://hostname/index.php`\n  ou algo semelhante.\n  Por favor, ajuste as URLs em nossas descrições conforme necessário.\n\nObserve que, ao contrário do framework em si, após o template de projeto ser instalado, ele é todo seu. Você está livre para adicionar ou remover código e modificar o template conforme precisar.\n\n\nFuncionalidade <span id=\"functionality\"></span>\n--------------\n\nO template básico de projetos instalado contém quatro páginas:\n\n* A página inicial, exibida quando você acessa a URL `https://hostname/index.php`,\n* a página \"About\" (Sobre),\n* a página \"Contact\" (Contato), que exibe um formulário de contato que permite\n  que usuários finais entrem em contato com você via e-mail,\n* e a página \"Login\", que exibe um formulário de login que pode ser usado para aurenticar usuários finais.\n  Tente fazer o login com \"admin/admin\", e você perceberá que o item do menu principal \"Login\" mudará para \"Logout\".\n\nEssas páginas compartilham o mesmo cabeçalho e rodapé. O cabeçalho contém uma barra de menu principal que permite a navegação entre as diferentes páginas.\n\nVocê também deverá ver uma barra de ferramentas no rodapé da janela do navegador.\nEssa é uma [ferramenta de depuração](tool-debugger.md) muito útil fornecida pelo Yii para\nregistrar e exibir várias informações de depuração, tais como: mensagens de logs, status de respostas, as consultas de banco de dados executadas, e assim por diante.\n\nAlém da aplicação Web, existe um script de console chamado `yii`, que está localizado no diretório raiz da aplicação.\nEsse script pode ser usado para executar rotinas em segundo plano e tarefas de manutenção da aplicação que são descritas na seção [Comandos de Console](tutorial-console.md).\n\n\nEstrutura da Aplicação <span id=\"application-structure\"></span>\n----------------------\n\nOs diretórios e arquivos mais importantes em sua aplicação, assumindo que o diretório raiz dela é o `basico`, são:\n\n```\nbasico/                  caminho base de sua aplicação\n    composer.json       usado pelo Composer, descreve informações de pacotes\n    config/             contém as configurações da aplicação e outras\n        console.php     a configuração da aplicação de console\n        web.php         a configuração da aplicação Web\n    commands/           contém classes de comandos do console\n    controllers/        contém classes de controllers (controladores)\n    models/             contém classes de models (modelos)\n    runtime/            contém arquivos gerados pelo Yii durante o tempo de execução, tais como logs e arquivos de cache\n    vendor/             contém os pacotes do Composer instalados, incluindo o próprio Yii framework\n    views/              contém arquivos de views (visões)\n    web/                raiz da aplicação Web, contém os arquivos acessíveis pela Web\n        assets/         contém os arquivos de assets (javascript e css) publicados pelo Yii\n        index.php       o script de entrada (ou bootstrap) para a aplicação\n    yii                 o script de execução dos comandos de console do Yii\n```\n\nEm geral, os arquivos na aplicação podem ser divididos em dois tipos: aqueles em `basico/web` e aqueles em outros diretórios.\nOs primeiros podem ser acessados diretamente via HTTP (ou seja, em um navegador), enquanto os demais não podem e deveriam ser acessados.\n\nO Yii implementa o padrão de arquitetura [modelo-visão-controlador (MVC)](https://wikipedia.org/wiki/Model-view-controller),\nque se reflete na organização de diretórios acima. O diretório `models` contém\ntodas as [classes de modelos](structure-models.md), o diretório `views` contém todos\nos [scripts de visões](structure-views.md), e o diretório `controllers` contém\ntodas as [classes de controladores](structure-controllers.md).\n\nO diagrama a seguir demonstra a estrutura estática de uma aplicação.\n\n![Estrutura Estática de uma Aplicação](images/application-structure.png)\n\nCada aplicação tem um script de entrada `web/index.php` que é o único script PHP\nacessível pela Web na aplicação. O script de entrada recebe uma requisição e cria uma instância de [aplicação](structure-applications.md) para tratar a requisição.\nA [aplicação](structure-applications.md) resolve (\"traduz\") a requisição com a ajuda de seus [componentes](concept-components.md) e despacha a requisição para os elementos do MVC. São usados [Widgets](structure-widgets.md) nas [views](structure-views.md) para ajudar a construir elementos de interface de usuário complexos e dinâmicos.\n\n\nCiclo de Vida da Requisição <span id=\"request-lifecycle\"></span>\n---------------------------\n\nO diagrama a seguir demonstra como uma aplicação trata uma requisição.\n\n![Ciclo de Vida da Requisição](images/request-lifecycle.png)\n\n1. Um usuário faz uma requisição ao [script de entrada](structure-entry-scripts.md) `web/index.php`.\n2. O script de entrada carrega a [configuração](concept-configurations.md) da\n   aplicação e cria uma instância de [aplicação](structure-applications.md) para\n   tratar a requisição.\n3. A aplicação resolve (\"traduz\") a [rota](runtime-routing.md) solicitada com a ajuda do componente [request](runtime-requests.md) da aplicação.\n4. A aplicação cria uma instância de um [controller](structure-controllers.md)\n   para tratar a requisição.\n5. O controller cria uma instância de uma [action](structure-controllers.md) (ação) e aplica os filtros para a ação.\n6. Se qualquer filtro falhar, a ação é cancelada.\n7. Se todos os filtros passarem, a ação é executada.\n8. A ação carrega alguns modelos (models) de dados, possivelmente a partir de um banco de dados.\n9. A ação renderiza uma view, passando a ela os modelos de dados.\n10. O resultado renderizado é retornado pelo componente\n    [response](runtime-responses.md) (resposta) da aplicação.\n11. O componente response envia o resultado renderizado para o navegador do usuário.\n\n"
  },
  {
    "path": "docs/guide-pt-BR/structure-application-components.md",
    "content": "Componentes de Aplicação\n========================\n\nAplicações são [service locators](concept-service-locator.md). Elas hospedam um\nconjunto de assim chamados *componentes de aplicação* que fornecem diferentes\nserviços para o processamento de requisições. Por exemplo, o componente\n`urlManager` é responsável pelo roteamento de requisições Web aos controllers\nadequados; o componente `db` fornece serviços relacionados a bancos de dados; e\nassim por diante.\n\nCada componente de aplicação tem um ID que o identifica de maneira única dentre\nos outros componentes de uma mesma aplicação. Você pode acessar um componente de\naplicação através da expressão\n\n```php\n\\Yii::$app->componentID\n```\n\nPor exemplo, você pode usar `\\Yii::$app->db` para obter a [[yii\\db\\Connection|conexão do BD]],\ne `\\Yii::$app->cache` para obter o [[yii\\caching\\Cache|cache primário]] registrado\ncom a aplicação.\n\nUm componente de aplicação é criado na primeira vez em que é acessado através\nda expressão acima. Quaisquer acessos posteriores retornarão a mesma instância\ndo componente.\n\nComponentes de aplicação podem ser quaisquer objetos. Você pode registrá-los\nconfigurando a propriedade [[yii\\base\\Application::components]] nas\n[configurações da aplicação](structure-applications.md#application-configurations).\nPor exemplo,\n\n```php\n[\n    'components' => [\n        // registra o componente \"cache\" usando um nome de classe\n        'cache' => 'yii\\caching\\ApcCache',\n\n        // registra o componente \"db\" usando um array de configuração\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=demo',\n            'username' => 'root',\n            'password' => '',\n        ],\n\n        // registra o componente \"search\" usando uma função anônima\n        'search' => function () {\n            return new app\\components\\SolrService;\n        },\n    ],\n]\n```\n\n> Informação: Embora você possa registrar quantos componentes de aplicação você quiser,\n  você deveria fazer isso com juízo. Componentes de aplicação são como variáveis\n  globais. Usar componentes de aplicação demais pode tornar seu código\n  potencialmente mais difícil de testar e manter. Em muitos casos, você pode\n  simplesmente criar um componente local e utilizá-lo quando necessário.\n\n\n## Components de Inicialização <span id=\"bootstrapping-components\"></span>\n\nConforme mencionado acima, um componente de aplicação só será instanciado quando\nele estiver sendo acessado pela primeira vez. Se ele nunca for acessado durante\numa requisição, ele não será instanciado. No entanto, algumas vezes você pode\nquerer instanciar um componente de aplicação em todas as requisições, mesmo que\nele não seja explicitamente acessado. Para fazê-lo, você pode listar seu ID na\npropriedade [[yii\\base\\Application::bootstrap|bootstrap]] da aplicação.\n\nPor exemplo, a configuração de aplicação a seguir assegura-se que o componente\n`log` sempre esteja carregado:\n\n```php\n[\n    'bootstrap' => [\n        'log',\n    ],\n    'components' => [\n        'log' => [\n            // configuração para o componente \"log\"\n        ],\n    ],\n]\n```\n\n\n## Componentes de Aplicação do Core <span id=\"core-application-components\"></span>\n\nO yii define um conjunto de componentes de aplicação do **core** com IDs fixos\ne configurações padrão. Por exemplo, o componente [[yii\\web\\Application::request|request]]\né usado para coletar as informações sobre uma requisição do usuário e resolvê-la\nem uma [rota](runtime-routing.md); o componente [[yii\\base\\Application::db|db]]\nrepresenta uma conexão do banco de dados através da qual você pode realizar\nconsultas. É com a ajuda destes componentes de aplicação do core que as aplicações\nYii conseguem tratar as requisições dos usuários.\n\nSegue abaixo uma lista dos componentes de aplicação pré-definidos do core. Você\npode configurá-los e personalizá-los como você faz com componentes de aplicação\nnormais. Quando você estiver configurando um componente de aplicação do core,\nse você não especificar sua classe, a padrão será utilizada.\n\n* [[yii\\web\\AssetManager|assetManager]]: gerencia os asset bundles e a publicação\n  de assets. Por favor consulte a seção [Gerenciando Assets](structure-assets.md)\n  para mais detalhes.\n* [[yii\\db\\Connection|db]]: representa uma conexão do banco de dados através da\n  qual você poderá realizar consultas. Perceba que quando você configura esse\n  componente, você precisa especificar a classe do componente bem como as outras\n  propriedades obrigatórios, tais como [[yii\\db\\Connection::dsn]]. Por favor\n  consulte a seção [Data Access Objects](db-dao.md) (Objeto de Acesso a Dados)\n  para mais detalhes.\n* [[yii\\base\\Application::errorHandler|errorHandler]]: manipula erros e exceções\n  do PHP. Por favor consulte a seção [Tratamento de Erros](runtime-handling-errors.md)\n  para mais detalhes.\n* [[yii\\i18n\\Formatter|formatter]]: formata dados quando são exibidos aos\n  usuários finais. Por exemplo, um número pode ser exibido com um separador de\n  milhares, uma data pode ser formatada em um formato longo. Por favor consulte\n  a seção [Formatação de Dados](output-formatting.md) para mais detalhes.\n* [[yii\\i18n\\I18N|i18n]]: suporta a tradução e formatação de mensagens. Por favor\n  consulte a seção [Internacionalização](tutorial-i18n.md) para mais detalhes.\n* [[yii\\log\\Dispatcher|log]]: gerencia alvos de logs. Por favor consulte a seção\n  [Gerenciamento de Logs](runtime-logging.md) para mais detalhes.\n* [[yii\\swiftmailer\\Mailer|mail]]: suporta a composição e envio de e-mails. Por\n  favor consulte a seção [Enviando E-mails](tutorial-mailing.md) para mais\n  detalhes.\n* [[yii\\base\\Application::response|response]]: representa a resposta sendo enviada\n  para os usuários finais. Por favor consulte a seção [Respostas](runtime-responses.md)\n  para mais detalhes.\n* [[yii\\base\\Application::request|request]]: representa a requisição recebida dos\n  usuários finais. Por favor consulte a seção [Requisições](runtime-requests.md)\n  para mais detalhes.\n* [[yii\\web\\Session|session]]: representa as informações da sessão. Esse componente\n  só está disponível em [[yii\\web\\Application|aplicações Web]]. Por favor consulte\n  a seção [Sessões e Cookies](runtime-sessions-cookies.md) para mais detalhes.\n* [[yii\\web\\UrlManager|urlManager]]: suporta a análise e criação de URLs. Por\n  favor consulte a seção [Análise e Geração de URLs](runtime-routing.md)\n  para mais detalhes.\n* [[yii\\web\\User|user]]: representa as informações de autenticação do usuário.\n  Esse componente só está disponível em [[yii\\web\\Application|aplicações Web]].\n  Por favor consulte a seção [Autenticação](security-authentication.md) para\n  mais detalhes.\n* [[yii\\web\\View|view]]: suporta a renderização de views. Por favor consulte a\n  seção [Views](structure-views.md) para mais detalhes.\n"
  },
  {
    "path": "docs/guide-pt-BR/structure-applications.md",
    "content": "Aplicações\n==========\n\nAplicações são objetos que regem a estrutura e ciclo de vida gerais de\naplicações em Yii. Cada aplicação contém um único objeto Application que é criado no\n[script de entrada](structure-entry-scripts.md) e que pode ser acessado\nglobalmente pela expressão `\\Yii::$app`.\n\n> Informação: Dependendo do contexto, quando dizemos \"uma aplicação\", pode significar\n  tanto um objeto Application quanto um sistema.\n\nExistem dois tipos de aplicações: [[yii\\web\\Application|aplicações Web]] e\n[[yii\\console\\Application|aplicações console]]. Como o próprio nome indica,\no primeiro manipula requisições Web enquanto o segundo trata requisições de\ncomandos do console.\n\n\n## Configurações da Aplicação <span id=\"application-configurations\"></span>\n\nQuando um [script de entrada](structure-entry-scripts.md) cria uma aplicação, ele\ncarregará uma [configuração](concept-configurations.md) e a aplicará à aplicação,\nda seguinte forma:\n\n```php\nrequire __DIR__ . '/../vendor/autoload.php';\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n\n// carrega a configuração da aplicação\n$config = require __DIR__ . '/../config/web.php';\n\n// instancia e configura a aplicação\n(new yii\\web\\Application($config))->run();\n```\n\nTal como [configurações](concept-configurations.md) normais, as configurações da\naplicação especificam como inicializar as propriedades de objetos Application.\nUma vez que geralmente são muito complexas, elas normalmente são mantidas em \n[arquivos de configuração](concept-configurations.md#configuration-files),\ncomo o arquivo `web.php` no exemplo acima.\n\n\n## Propriedades da Aplicação <span id=\"application-properties\"></span>\n\nExistem muitas propriedades importantes da aplicação que deveriam ser configuradas.\nEssas propriedades tipicamente descrevem o ambiente em que as aplicaçõe estão\nrodando. Por exemplo, as aplicações precisam saber como carregar os\n[controllers](structure-controllers.md), onde armazenar os arquivos temporários,\netc. A seguir resumiremos essas propriedades.\n\n\n### Propriedades Obrigatórias <span id=\"required-properties\"></span>\n\nEm qualquer aplicação, você deve pelo menos configurar duas propriedades:\n[[yii\\base\\Application::id|id]] e [[yii\\base\\Application::basePath|basePath]].\n\n\n#### [[yii\\base\\Application::id|id]] <span id=\"id\"></span>\n\nA propriedade [[yii\\base\\Application::id|id]] especifica um ID único que diferencia\numa aplicação das outras. É usado principalmente programaticamente. Apesar de não\nser obrigatório, para melhor interoperabilidade recomenda-se que você só use\ncaracteres alfanuméricos ao especificar um ID de aplicação.\n\n\n#### [[yii\\base\\Application::basePath|basePath]] <span id=\"basePath\"></span>\n\nA propriedade [[yii\\base\\Application::basePath|basePath]] especifica o diretório\nraiz de um sistema. É o diretório que contém todo o código fonte protegido de um\nsistema. Sob este diretório, você normalmente verá subdiretórios tais como\n`models`, `views` e `controllers`, que contém o código fonte correspondente ao\npadrão MVC.\n\nVocê pode configurar a propriedade [[yii\\base\\Application::basePath|basePath]]\nusando um [alias de caminho](concept-aliases.md). Em ambas as formas, o diretório\ncorrespondente precisa existir, doutra forma será lançada uma exceção. O caminho\nserá normnalizado chamando-se a função `realpath()`.\n\nA propriedade [[yii\\base\\Application::basePath|basePath]] frequentemente é\nusada para derivar outros caminhos importantes (por exemplo, o diretório de\nruntime). Por esse motivo, um alias de caminho `@app` é pré-definido para\nrepresentar esse caminho. Assim os caminhos derivados podem ser formados usando\nesse alias (por exemplo, `@app/runtime` para referenciar o diretório runtime).\n\n\n### Propriedades Importantes <span id=\"important-properties\"></span>\n\nAs propriedades descritas nesta subseção frequentemente precisam ser\nconfiguradas porque elas variam em diferentes aplicações.\n\n\n#### [[yii\\base\\Application::aliases|aliases]] <span id=\"aliases\"></span>\n\nEsta propriedade permite que você defina um conjunto de\n[aliases](concept-aliases.md) em termos de um array. As chaves do array representam\nos nomes de alias, e os valores são as definições correspondentes. Por exemplo:\n\n```php\n[\n    'aliases' => [\n        '@name1' => 'path/to/path1',\n        '@name2' => 'path/to/path2',\n    ],\n]\n```\n\nEsta propriedade é fornecida para que você possa definir aliases na configuração \nda aplicação ao invés de chamar o método [[Yii::setAlias()]].\n\n\n#### [[yii\\base\\Application::bootstrap|bootstrap]] <span id=\"bootstrap\"></span>\n\nEsta é uma propriedade muito útil. Ela permite que você especifique um array de\ncomponentes que devem ser executados durante o [[yii\\base\\Application::bootstrap()|processo de inicialização]]\nda aplicação. Por exemplo, se você quer que um [módulo](structure-modules.md)\npersonalize as [regras de URL](runtime-routing.md), você pode listar seu\nID como um elemento nesta propriedade.\n\nCada componente listado nesta propriedade deve ser especificado em um dos\nseguintes formatos:\n\n- o ID de um componente de aplicação conforme especifcado via [components](#components).\n- o ID de um módulo conforme especificado via [modules](#modules).\n- o nome de uma classe.\n- um array de configuração.\n- uma função anônima que cria e retorna um componente.\n\nPor exemplo:\n\n```php\n[\n    'bootstrap' => [\n        // o ID de uma aplicação ou de um módulo\n        'demo',\n\n        // um nome de classe\n        'app\\components\\Profiler',\n\n        // um array de configuração\n        [\n            'class' => 'app\\components\\Profiler',\n            'level' => 3,\n        ],\n\n        // uma função anônima\n        function () {\n            return new app\\components\\Profiler();\n        }\n    ],\n]\n```\n\n> Informação: Se o ID de um módulo é o mesmo que o ID de um componente da aplicação,\n  o componente será usado durante o processo de inicialização. Se você quiser\n  usar o módulo ao invés dele, você pode especificá-lo usando uma função anônima\n  conforme a seguir:\n> ```php\n[\n    function () {\n        return Yii::$app->getModule('user');\n    },\n]\n```\n\n\nDurante o processo de inicialização, cada componente será instanciado. Se a classe\ndo componente implementa [[yii\\base\\BootstrapInterface]], seu método [[yii\\base\\BootstrapInterface::bootstrap()|bootstrap()]]\ntambém será chamado.\n\nOutro exemplo prático está na configuração do [Template Básico de Projetos](start-installation.md),\nonde os módulos `debug` e `gii` estão configurados como componentes de inicialização\nquando a aplicação está rodando no ambiente de desenvolvimento:\n\n```php\nif (YII_ENV_DEV) {\n    // ajustes da configuração para o ambiente 'dev'\n    $config['bootstrap'][] = 'debug';\n    $config['modules']['debug'] = 'yii\\debug\\Module';\n\n    $config['bootstrap'][] = 'gii';\n    $config['modules']['gii'] = 'yii\\gii\\Module';\n}\n```\n\n> Observação: Colocar componentes demais em `bootstrap` degradará o desempenho de sua\n  aplicação, porque para cada requisição o mesmo conjunto de componentes precisará\n  ser carregado. Desta forma, use os componentes de inicialização com juízo.\n\n\n#### [[yii\\web\\Application::catchAll|catchAll]] <span id=\"catchAll\"></span>\n\nEssa propriedade só é suportada por [[yii\\web\\Application|aplicações Web]]. Ela\nespecifica uma [action de um controller](structure-controllers.md) que deve\nmanipular todas as requisições. Isso é geralmente usado quando a aplicação está\nem modo de manutenção e precisa tratar todas as requisições através de uma\núnica action.\n\nA configuração é um array, cujo primeiro elemento especifica a rota para a action.\nO restante dos elementos do array (pares de chave-valor) especificam os parâmetros\nque devem ser atrelados à action. Por exemplo:\n\n```php\n[\n    'catchAll' => [\n        'offline/notice',\n        'param1' => 'value1',\n        'param2' => 'value2',\n    ],\n]\n```\n\n\n#### [[yii\\base\\Application::components|components]] <span id=\"components\"></span>\n\nEssa é a propriedade mais importante. Ela permite que você registre uma lista\nde componentes chamados [componentes de aplicação](structure-application-components.md)\nque você pode usar em outros lugares. Por exemplo:\n\n```php\n[\n    'components' => [\n        'cache' => [\n            'class' => 'yii\\caching\\FileCache',\n        ],\n        'user' => [\n            'identityClass' => 'app\\models\\User',\n            'enableAutoLogin' => true,\n        ],\n    ],\n]\n```\n\nCada componente da aplicação é especiifcado como um par de chave-valor em um array.\nA chave representa o ID do componente, enquanto o valor representa o nome ou a\n[configuração](concept-configurations.md) da classe do componente.\n\nVocê pode registrar qualquer componente com uma aplicação, e o componente depois\npoderá ser acessado globalmente através da expressão `\\Yii::$app->IDdoComponente`.\n\nPor favor leia a seção [Componentes de Aplicação](structure-application-components.md)\npara mais detalhes.\n\n\n#### [[yii\\base\\Application::controllerMap|controllerMap]] <span id=\"controllerMap\"></span>\n\nEssa propriedade permite que você mapeie um ID de um controller a uma classe\nde controller arbitrária. Por padrão, o Yii mapeia os IDs de controllers a classes\nde controllers baseado em uma [convenção](#controllerNamespace) (por exemplo, o\nID `post` será mapeado para `app\\controllers\\PostController`). Ao configurar essa\npropriedade, você pode quebrar a convenção de controllers em específico. No exemplo\na seguir, `account` será meapeado para `app\\controllers\\UserController`,\nenquanto `article` será mapeado para `app\\controllers\\PostController`.\n\n```php\n[\n    'controllerMap' => [\n        'account' => 'app\\controllers\\UserController',\n        'article' => [\n            'class' => 'app\\controllers\\PostController',\n            'enableCsrfValidation' => false,\n        ],\n    ],\n]\n```\n\nAs chaves do array dessa propriedade representam os IDs dos controllers, enquanto\nos valores do array representam o nome ou as [configurações](concept-configurations.md)\nda classe do controller.\n\n\n#### [[yii\\base\\Application::controllerNamespace|controllerNamespace]] <span id=\"controllerNamespace\"></span>\n\nEssa propriedade especifica o namespace padrão sob o qual as classes dos\ncontrollers deverão ser localizadas. Seu valor padrão é `app\\controllers`. Se um\nID de um controller for `post`, por convenção o nome da classe de controller\ncorrespondente (sem namespace) seria `PostController`, e o nome da classe\ncompleto e qualificado seria `app\\controllers\\PostController`.\n\nAs classes de controllers também podem estar localizadas em subdiretórios do\ndiretório correspondente ao namespace. Por exemplo, dado um ID de controller\n`admin/post`, a classe completa e qualificada correspondente seria\n`app\\controllers\\admin\\PostController`.\n\nÉ importante que as classes completas e qualificadas possam ser [carregadas automaticamente](concept-autoloading.md)\ne que o namespace das suas classes de controller correspondam ao valor dessa\npropriedade. Doutra forma, você receberia um erro de \"Página Não Encontrada\" ao\nacessar a aplicação.\n\nCaso você queira quebrar a convenção conforme descrito acima, você pode configurar\na propriedade [controllerMap](#controllerMap).\n\n\n#### [[yii\\base\\Application::language|language]] <span id=\"language\"></span>\n\nEssa propriedade especifica o idioma no qual a aplicação deve exibir o conteúdo\naos usuários finais. O valor padrão dessa propriedade é `en`, significando inglês.\nVocê deve configurar essa propriedade se a sua aplicação suportar múltiplos\nidiomas.\n\nO valor dessa propriedade determina vários aspectos da [internacionalização](tutorial-i18n.md),\nincluindo tradução de mensagens, formato de datas, formato de números, etc. Por\nexemplo, o widget [[yii\\jui\\DatePicker]] usará o valor dessa propriedade por\npadrão para determinar em qual idioma o calendário deverá ser exibido e\ncomo a data deve ser formatada.\n\nRecomenda-se que você especifique um idioma em termos de um [código de idioma IETF](https://en.wikipedia.org/wiki/IETF_language_tag).\nPor exenplo, `en` corresponde ao inglês, enquanto `en-US` significa inglês dos\nEstados Unidos.\n\nMais detalhes sobre essa propriedade podem ser encontrados na seção\n[Internacionalização](tutorial-i18n.md).\n\n\n#### [[yii\\base\\Application::modules|modules]] <span id=\"modules\"></span>\n\nEssa propriedade especifica os [módulos](structure-modules.md) que uma aplicação\ncontém.\n\nA propriedade recebe um array de classes de módulos ou [configurações](concept-configurations.md)\ncom as chaves do array sendo os IDs dos módulos. Por exemplo:\n\n```php\n[\n    'modules' => [\n        // um módulo \"booking\" especificado com a classe do módulo\n        'booking' => 'app\\modules\\booking\\BookingModule',\n\n        // um módulo \"comment\" especificado com um array de configurações\n        'comment' => [\n            'class' => 'app\\modules\\comment\\CommentModule',\n            'db' => 'db',\n        ],\n    ],\n]\n```\n\nPor favor consulte a seção [Módulos](structure-modules.md) para mais detalhes.\n\n\n#### [[yii\\base\\Application::name|name]] <span id=\"name\"></span>\n\nEssa propriedade especifica o nome da aplicação que pode ser exibido aos\nusuários finais. Ao contrário da propriedade [[yii\\base\\Application::id|id]] que\ndeveria receber um valor único, o valor desta propriedade serve principalmente\npara fins de exibição e não precisa ser único.\n\nVocê nem sempre precisa configurar essa propriedade se nenhuma parte do código\na estiver usando.\n\n\n#### [[yii\\base\\Application::params|params]] <span id=\"params\"></span>\n\nEssa propriedade especifica um array de parâmetros da aplicação que podem ser\nacessados globalmente. Ao invés de usar números e strings fixos espalhados por\ntoda parte no seu código, é uma boa prática defini-los como parâmetros da\naplicação em um único lugar e usá-los nos lugares onde for necessário. Por\nexemplo, você pode definir o tamanho de uma miniatura de imagem como um parâmetro\nconforme a seguir:\n\n```php\n[\n    'params' => [\n        'thumbnail.size' => [128, 128],\n    ],\n]\n```\n\nEntão no seu código onde você precisar usar o valor do tamanho, você pode\nsimplesmente usar o código conforme a seguir:\n\n```php\n$size = \\Yii::$app->params['thumbnail.size'];\n$width = \\Yii::$app->params['thumbnail.size'][0];\n```\n\nMais tarde, se você decidir mudar o tamanho da miniatura, você só precisa\nmodificá-lo na configuração da aplicação sem tocar em quaisquer códigos\ndependentes.\n\n\n#### [[yii\\base\\Application::sourceLanguage|sourceLanguage]] <span id=\"sourceLanguage\"></span>\n\nEssa propriedade especifica o idioma no qual o código da aplicação foi escrito.\nO valor padrão é `'en-US'`, significando inglês dos Estados Unidos. Você deve\nconfigurar essa propriedade se o conteúdo do texto no seu código não estiver\nem inglês.\n\nConforme a propriedade [language](#language), você deve configurar essa propriedade\nem termos de um [código de idioma IETF](https://en.wikipedia.org/wiki/IETF_language_tag).\nPor exemplo, `en` corresponde ao inglês, enquanto `en-US` significa inglês dos\nEstados Unidos.\n\nMais detalhes sobre essa propriedade podem ser encontrados na seção\n[Internacionalização](tutorial-i18n.md).\n\n\n#### [[yii\\base\\Application::timeZone|timeZone]] <span id=\"timeZone\"></span>\n\nEssa propriedade é disponibilizada como uma maneira alternativa de definir a\ntimezone do PHP em tempo de execução. Ao confiugrar essa propriedade, você está\nessencialmente chamando a função\n[date_default_timezone_set()](https://www.php.net/manual/en/function.date-default-timezone-set.php)\ndo PHP. Por exemplo:\n\n```php\n[\n    'timeZone' => 'America/Los_Angeles',\n]\n```\n\n\n#### [[yii\\base\\Application::version|version]] <span id=\"version\"></span>\n\nEssa propriedade especifica a versão da aplicação. Seu valor padrão é `'1.0'`.\nVocê não precisa configurar esta propriedade se nenhuma parte do seu código\nestiver utilizando-a.\n\n\n### Propriedades Úteis <span id=\"useful-properties\"></span>\n\nAs propriedades descritas nesta subseção não são comumente configuradas porque\nseus valores padrão estipulam convenções comuns. No entanto, você pode ainda\nconfigurá-las no caso de querer quebrar as convenções.\n\n\n#### [[yii\\base\\Application::charset|charset]] <span id=\"charset\"></span>\n\nEssa propriedade especifica o charset que a aplicação usa. O valor padrão é\n`'UTF-8'`, que deveria ser mantido como está para a maioria das aplicações, a\nmenos que você esteja trabalhando com sistemas legados que usam muitos dados que\nnão são unicode.\n\n\n#### [[yii\\base\\Application::defaultRoute|defaultRoute]] <span id=\"defaultRoute\"></span>\n\nEssa propriedade especifica a [rota](runtime-routing.md) que uma aplicação deveria\nusar quando uma requisição não especifica uma. A rota pode consistir de um ID de\nmódulo, ID de controller e/ou ID de action. Por exemplo, `help`, `post/create`,\n`admin/post/create`. Se não for passado um ID de action, ele assumirá o valor\nconforme especificado em [[yii\\base\\Controller::defaultAction]].\n\nPara [[yii\\web\\Application|aplicações Web]], o valor padrão dessa propriedade é\n`'site'`, o que significa que deve usar o controller `SiteController` e sua\naction padrão. Como resultado disso, se você acessar a aplicação sem especificar\numa rota, ele exibirá o resultado de `app\\controllers\\SiteController::actionIndex()`.\n\nPara [[yii\\console\\Application|aplicações do console]], o valor padrão é `'help'`,\no que significado que deve usar o comando do core\n[[yii\\console\\controllers\\HelpController::actionIndex()]]. Como resultado, se\nvocê executar o comando `yii` sem fornecer quaisquer argumentos, ele exibirá a\ninformação de ajuda.\n\n\n#### [[yii\\base\\Application::extensions|extensions]] <span id=\"extensions\"></span>\n\nEssa propriedade especifica a lista de [extensões](structure-extensions.md) que\nestão instaladas e são usadas pela aplicação. Por padrão, ela receberá o array\nretornado pelo arquivo `@vendor/yiisoft/extensions.php`. O arquivo `extensions.php`\né gerado e mantido automaticamente quando você usa o [Composer](https://getcomposer.org)\npara instalar extensões. Então na maioria dos casos você não precisa configurar\nessa propriedade.\n\nNo caso especial de você querer manter extensões manualmente, você pode configurar\nessa propriedade da seguinte forma:\n\n```php\n[\n    'extensions' => [\n        [\n            'name' => 'extension name',\n            'version' => 'version number',\n            'bootstrap' => 'BootstrapClassName',  // opcional, também pode ser um array de configuração\n            'alias' => [  // opcional\n                '@alias1' => 'to/path1',\n                '@alias2' => 'to/path2',\n            ],\n        ],\n\n        // ... mais extensões conforme acima ...\n\n    ],\n]\n```\n\nComo você pode ver, a propriedade recebe um array de especificações de extensões.\nCada extensão é especificada com um array composto pelos elementos `name` e\n`version`. Se uma extensão precisa executar durante o \n[processo de inicialização](runtime-bootstrapping.md), um elemento `bootstrap` pode ser\nespecificado com um nome de uma classe de inicialização ou um array de\n[configuração](concept-configurations.md). Uma extensão também pode definir\nalguns [aliases](concept-aliases.md).\n\n\n#### [[yii\\base\\Application::layout|layout]] <span id=\"layout\"></span>\n\nEssa propriedade especifica o nome do layout padrão que deverá ser usado ao\nrenderizar uma [view](structure-views.md). O valor padrão é `'main'`, significando\nque o arquivo de layout `main.php` sob o [caminho dos layouts](#layoutPath) deverá\nser usado. Se tanto o [caminho do layout](#layoutPath) quanto o\n[caminho da view](#viewPath) estiverem recebendo os valores padrão, o arquivo de\nlayout padrão pode ser representado como o alias de caminho `@app/views/layouts/main.php`.\n\nVocê pode configurar esta propriedade como `false` se você quiser desativar o\nlayout por padrão, embora isso seja muito raro.\n\n\n#### [[yii\\base\\Application::layoutPath|layoutPath]] <span id=\"layoutPath\"></span>\n\nEssa propriedade especifica o caminho onde os arquivos de layout devem ser\nprocurados. O valor padrão é o subdiretório `layouts` dentro do diretório do\n[caminho das views](#viewPath). Se o [caminho das views](#viewPath) estiver\nrecebendo seu valor padrão, o caminho padrão dos layouts pode ser representado\ncomo o alias de caminho `@app/views/layouts`.\n\nVocê pode configurá-la como um diretório ou um [alias](concept-aliases.md) de\ncaminho.\n\n\n#### [[yii\\base\\Application::runtimePath|runtimePath]] <span id=\"runtimePath\"></span>\n\nEssa propriedade especifica o caminho onde os arquivos temporários, tais como\narquivos de log e de cache, podem ser gerados. O valor padrão é o diretório\nrepresentado pelo alias `@app/runtime`.\n\nVocê pode configurá-la como um diretório ou [alias](concept-aliases.md) de\ncaminho. Perceba que o caminho de runtime precisa ter permissão de escrita para\no processo que executa a aplicação. E o caminho deveria ser protegido para não\nser acessado pelos usuários finais, porque os arquivos temporários dentro dele\npodem conter informações sensíveis.\n\nPara simplificar o acesso a esse caminho, o Yii possui um alias de caminho\npré-definido chamado `@runtime` para ele.\n\n\n#### [[yii\\base\\Application::viewPath|viewPath]] <span id=\"viewPath\"></span>\n\nEssa propriedade especifica o diretório raiz onde os arquivos de views estão\nlocalizados. O valor padrão do diretório é representado pelo alias `@app/views`.\nVocê pode configurá-lo como um diretório ou [alias](concept-aliases.md) de\ncaminho.\n\n\n#### [[yii\\base\\Application::vendorPath|vendorPath]] <span id=\"vendorPath\"></span>\n\nEssa propriedade especifica o diretório vendor gerenciado pelo [Composer](https://getcomposer.org).\nEle contém todas as bibliotecas de terceiros usadas pela sua aplicação, incluindo\no framework do Yii. O valor padrão é o diretório representado pelo alias `@app/vendor`.\n\nVocê pode configurar essa propriedade como um diretório ou [alias](concept-aliases.md)\nde caminho. Quando você modificar essa propriedade, assegure-se de ajustar a\nconfiguração do Composer de acordo.\n\nPara simplificar o acesso a esse caminho, o Yii tem um alias de caminho pré-definido\npara ele chamado de `@vendor`.\n\n\n#### [[yii\\console\\Application::enableCoreCommands|enableCoreCommands]] <span id=\"enableCoreCommands\"></span>\n\nEssa propriedade só é suportada por [[yii\\console\\Application|aplicações do console]].\nEla especifica se os comandos do core inclusos no pacote do Yii devem estar\nativos. O valor padrão é `true`.\n\n\n## Eventos da Aplicação <span id=\"application-events\"></span>\n\nUma aplicação dispara muitos eventos durante o ciclo de vida de manipulação de\numa requisição. Você pode vincular manipuladores a esses eventos nas\nconfigurações da aplicação do seguinte modo,\n\n```php\n[\n    'on beforeRequest' => function ($event) {\n        // ...\n    },\n]\n```\n\nA sintaxe de uso de `on eventName` é descrita na seção\n[Configurações](concept-configurations.md#configuration-format).\n\nAlternativamente, você pode vincular manipuladores de evento durante o\n[processo de inicialização](runtime-bootstrapping.md) após a instância da aplicação\nser criada. Por exemplo:\n\n```php\n\\Yii::$app->on(\\yii\\base\\Application::EVENT_BEFORE_REQUEST, function ($event) {\n    // ...\n});\n```\n\n### [[yii\\base\\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_REQUEST]] <span id=\"beforeRequest\"></span>\n\nEste evento é disparado *antes* de uma aplicação manipular uma requisição. O nome\ndo evento é `beforeRequest`.\n\nQuando esse evento é disparado, a instância da aplicação foi configurada e\ninicializada. Então é um bom lugar para inserir código personalizado por meio\ndo mecanismo de eventos para interceptar o processo de tratamento da requisição.\nPor exemplo, no manipulador de eventos, você pode definir dinamicamente a\npropriedade [[yii\\base\\Application::language]] baseado em alguns parâmetros.\n\n\n### [[yii\\base\\Application::EVENT_AFTER_REQUEST|EVENT_AFTER_REQUEST]] <span id=\"afterRequest\"></span>\n\nEste evento é disparado *depois* que uma aplicação finaliza o tratamento da\nrequisição, mas *antes* de enviar a resposta. O nome do evento é `afterRequest`.\n\nQuando este evento é disparado, o tratamento da requisição está completo e você\npode aproveitar essa ocasião para fazer um pós-processamento da requisição ou\npersonalizar a resposta.\n\nPerceba que o componente [[yii\\web\\Response|response]] também dispara alguns\neventos enquanto está enviando conteúdo de resposta para os usuários finais. \nEsses eventos são disparados *depois* deste evento.\n\n\n### [[yii\\base\\Application::EVENT_BEFORE_ACTION|EVENT_BEFORE_ACTION]] <span id=\"beforeAction\"></span>\n\nEste evento é disparado *antes* de executar cada [action de controller](structure-controllers.md).\nO nome do evento é `beforeAction`.\n\nO parâmetro do evento é uma instância de [[yii\\base\\ActionEvent]]. Um manipulador\nde evento pode definir o valor da propriedade [[yii\\base\\ActionEvent::isValid]]\ncomo `false` para interromper a execução da action. Por exemplo:\n\n```php\n[\n    'on beforeAction' => function ($event) {\n        if (alguma condição) {\n            $event->isValid = false;\n        } else {\n        }\n    },\n]\n```\n\nPerceba que o mesmo evento `beforeAction` também é disparado pelos [módulos](structure-modules.md)\ne [controllers](structure-controllers.md). Os objetos Application são os primeiros\na disparar este evento, seguidos pelos módulos (se houver algum) e finalmente pelos\ncontrollers. Se um manipulador de evento definir [[yii\\base\\ActionEvent::isValid]]\ncomo `false`, todos os eventos seguintes NÃO serão disparados.\n\n\n### [[yii\\base\\Application::EVENT_AFTER_ACTION|EVENT_AFTER_ACTION]] <span id=\"afterAction\"></span>\n\nEste evento é disparado *depois* de executar cada [action de controller](structure-controllers.md).\nO nome do evento é `afterAction`.\n\nO parâmetro do evento é uma instância de [[yii\\base\\ActionEvent]]. Através da\npropriedade [[yii\\base\\ActionEvent::result]], um manipulador de evento pode\nacessar ou modificar o resultado da action. Por exemplo:\n\n```php\n[\n    'on afterAction' => function ($event) {\n        if (alguma condição) {\n            // modifica $event->result\n        } else {\n        }\n    },\n]\n```\n\nPerceba que o mesmo evento `afterAction` também é disparado pelos [módulos](structure-modules.md)\ne [controllers](structure-controllers.md). Estes objetos disparam esse evento\nna order inversa da do `beforeAction`. Ou seja, os controllers são os primeiros\nobjetos a disparar este evento, seguidos pelos módulos (se houver algum) e\nfinalmente pelos objetos Application.\n\n\n## Ciclo de Vida da  Aplicação <span id=\"application-lifecycle\"></span>\n\nQuando um [script de entrada](structure-entry-scripts.md) estiver sendo executado\npara manipular uma requisição, uma aplicação passará pelo seguinte ciclo de vida:\n\n1. O script de entrada carrega a configuração da aplicação como um array.\n2. O script de entrada cria uma nova instância da aplicação:\n  * [[yii\\base\\Application::preInit()|preInit()]] é chamado, que configura algumas\n    propriedades da aplicação de alta prioridade, tais como\n    [[yii\\base\\Application::basePath|basePath]].\n  * Registra o [[yii\\base\\Application::errorHandler|manipulador de erros]].\n  * Configura as propriedades da aplicação.\n  * [[yii\\base\\Application::init()|init()]] é chamado, que por sua vez chama\n    [[yii\\base\\Application::bootstrap()|bootstrap()]] para rodar os componentes\n    de inicialização.\n3. O script de entrada chama [[yii\\base\\Application::run()]] para executar a aplicação:\n  * Dispara o evento [[yii\\base\\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_REQUEST]].\n  * Trata a requisição: resolve a requisição em uma [rota](runtime-routing.md)\n    e os parâmetros associados; cria os objetos do módulo, do controller e da\n    action conforme especificado pela rota; e executa a action.\n  * Dispara o evento [[yii\\base\\Application::EVENT_AFTER_REQUEST|EVENT_AFTER_REQUEST]].\n  * Envia a resposta para o usuário final.\n4. O script de entrada recebe o status de saída da aplicação e completa o\n   processamento da requisição.\n"
  },
  {
    "path": "docs/guide-pt-BR/structure-assets.md",
    "content": "Assets\n======\n\nUm asset no Yii é um arquivo que pode ser referenciado em uma página Web. Pode \nser um arquivo CSS, JavaScript, imagem, vídeo, etc. Os assets estão localizados \nem um diretório acessível pela Web e estão diretamente disponibilizados por \nservidores Web.\n\nMuitas vezes, é preferível gerenciá-los programaticamente. Por exemplo, quando \nvocê usar o widget [[yii\\jui\\DatePicker]] em uma página, será incluído \nautomaticamente os arquivos CSS e JavaScript requeridos, ao invés de pedir para \nvocê encontrar estes arquivos e incluí-los manualmente. E quando você atualizar \no widget para uma nova versão, automaticamente usará a nova versão dos arquivos \nde assets. Neste tutorial, iremos descrever esta poderosa capacidade de gerência \nde assets fornecidas pelo Yii.\n\n\n## Asset Bundles <span id=\"asset-bundles\"></span>\n\nO Yii gerencia os assets na unidade de *asset bundle*. Um asset bundle é \nsimplesmente uma coleção de assets localizados em um diretório. Quando você \nregistrar um asset bundle em uma [view (visão)](structure-views.md), serão  \nincluídos os arquivos CSS e JavaScript do bundle na página Web renderizada.\n\n\n## Definindo os Asset Bundles <span id=\"defining-asset-bundles\"></span>\n\nOs asset bundles são especificados como classes PHP que estendem de \n[[yii\\web\\AssetBundle]]. O nome de um bundle corresponde simplesmente a um nome \nde classe PHP totalmente qualificada (sem a primeira barra invertida). Uma classe \nde asset bundle deve ser [autoloadable](concept-autoloading.md). Geralmente é \nespecificado onde os asset estão localizados, quais arquivos CSS e JavaScript \npossuem e como o bundle depende de outro bundles.\n\nO código a seguir define o asset bundle principal que é usado pelo \n[template básico de projetos](start-installation.md):\n\n```php\n<?php\n\nnamespace app\\assets;\n\nuse yii\\web\\AssetBundle;\n\nclass AppAsset extends AssetBundle\n{\n    public $basePath = '@webroot';\n    public $baseUrl = '@web';\n    public $css = [\n        'css/site.css',\n    ];\n    public $js = [\n    ];\n    public $depends = [\n        'yii\\web\\YiiAsset',\n        'yii\\bootstrap\\BootstrapAsset',\n    ];\n}\n```\n\nA classe `AppAsset` acima, especifica que os arquivos de assets estão localizadas \nsob o diretório `@webroot` que corresponde à URL `@web`;\nO bundle contém um único arquivo CSS `css/site.css` e nenhum arquivo JavaScript;\nO bundle depende de outros dois bundles: [[yii\\web\\YiiAsset]] e \n[[yii\\bootstrap\\BootstrapAsset]]. Mais detalhes sobre as propriedades do \n[[yii\\web\\AssetBundle]] serão encontradas a seguir:\n\n* [[yii\\web\\AssetBundle::sourcePath|sourcePath]]: especifica o diretório que \n  contém os arquivos de assets neste bundle. Esta propriedade deve ser definida \n  se o diretório root não for acessível pela Web. Caso contrário, você deve definir \n  as propriedades [[yii\\web\\AssetBundle::basePath|basePath]] e \n  [[yii\\web\\AssetBundle::baseUrl|baseUrl]]. Os [alias de caminhos](concept-aliases.md) \n  podem ser usados nesta propriedade.\n* [[yii\\web\\AssetBundle::basePath|basePath]]: especifica um diretório acessível \n  pela Web que contém os arquivos de assets neste bundle. Quando você especificar \n  a propriedade [[yii\\web\\AssetBundle::sourcePath|sourcePath]], o \n  [gerenciador de asset](#asset-manager) publicará os assets deste bundle para um \n  diretório acessível pela Web e sobrescreverá a propriedade `basePath` para ficar \n  em conformidade. Você deve definir esta propriedade caso os seus arquivos de \n  asset já estejam em um diretório acessível pela Web e não precisam ser publicados.  \n  As [alias de caminhos](concept-aliases.md) podem ser usados aqui.\n* [[yii\\web\\AssetBundle::baseUrl|baseUrl]]: especifica a URL correspondente ao \n  diretório [[yii\\web\\AssetBundle::basePath|basePath]]. Assim como a propriedade \n  [[yii\\web\\AssetBundle::basePath|basePath]], se você especificar a propriedade \n  [[yii\\web\\AssetBundle::sourcePath|sourcePath]], o \n  [gerenciador de asset](#asset-manager) publicará os assets e sobrescreverá esta \n  propriedade para entrar em conformidade. Os [alias de caminhos](concept-aliases.md) \n  podem ser usados aqui.\n* [[yii\\web\\AssetBundle::js|js]]: um array listando os arquivos JavaScript contidos \n  neste bundle. Observe que apenas a barra \"/\" pode ser usada como separadores de \n  diretórios. Cada arquivo JavaScript deve ser especificado em um dos dois seguintes \n  formatos:\n  - um caminho relativo representando um local do arquivo JavaScript (por exemplo,  \n    `js/main.js`). O caminho real do arquivo pode ser determinado pela precedência \n    do [[yii\\web\\AssetManager::basePath]] no caminho relativo e a URL real do \n    arquivo pode ser determinado pela precedência do [[yii\\web\\AssetManager::baseUrl]] \n    no caminho relativo.\n  - uma URL absoluta representando um arquivo JavaScript externo. Por exemplo, \n    `https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js` ou \n    `//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js`.\n* [[yii\\web\\AssetBundle::css|css]]: uma array listando os arquivos CSS contidos \n  neste bundle. O formato deste array é igual ao que foi mencionado no \n  [[yii\\web\\AssetBundle::js|js]].\n* [[yii\\web\\AssetBundle::depends|depends]]: um array listando os nomes dos asset \n  bundles que este bundle depende (será explicado em breve).\n* [[yii\\web\\AssetBundle::jsOptions|jsOptions]]: especifica as opções que serão \n  passadas para o método [[yii\\web\\View::registerJsFile()]] quando for chamado \n  para registrar *cada* arquivo JavaScript neste bundle.\n* [[yii\\web\\AssetBundle::cssOptions|cssOptions]]: especifica as opções que serão \n  passadas para o método [[yii\\web\\View::registerCssFile()]] quando for chamado \n  para registrar *cada* arquivo CSS neste bundle.\n* [[yii\\web\\AssetBundle::publishOptions|publishOptions]]: especifica as opções \n  que serão passadas para o método [[yii\\web\\AssetManager::publish()]] quando for \n  chamado para publicar os arquivos de asset para um diretório Web. Este é usado \n  apenas se você especificar a propriedade [[yii\\web\\AssetBundle::sourcePath|sourcePath]].\n\n\n### Localização dos Assets <span id=\"asset-locations\"></span>\n\nOs assets, com base em sua localização, podem ser classificados como:\n\n* assets fonte: os arquivos de asset estão localizados juntos ao código fonte \n  PHP que não podem ser acessados diretamente na Web. Para utilizar os assets \n  fonte em uma página, devem ser copiados para um diretório Web a fim de \n  torna-los como os chamados assets publicados. Este processo é chamado de \n  *publicação de asset* que será descrito em detalhes ainda nesta seção.\n* assets publicados: os arquivos de asset estão localizados em um diretório Web \n  e podendo, assim, serem acessados diretamente na Web.\n* assets externos: os arquivos de asset estão localizados em um servidor Web \n  diferente do que a aplicação está hospedada. \n\nAo definir uma classe de asset bundle e especificar a propriedade \n[[yii\\web\\AssetBundle::sourcePath|sourcePath]], significará que quaisquer assets \nlistados usando caminhos relativos serão considerados como assets fonte. Se você \nnão especificar esta propriedade, significará que estes assets serão assets \npublicados (portanto, você deve especificar as propriedades \n[[yii\\web\\AssetBundle::basePath|basePath]] e [[yii\\web\\AssetBundle::baseUrl|baseUrl]] \npara deixar o Yii saber onde eles estão localizados).\n\nÉ recomendado que você coloque os assets da aplicação em um diretório Web para \nevitar o processo de publicação de assets desnecessários. É por isso que o \n`AppAsset` do exemplo anterior especifica a propriedade \n[[yii\\web\\AssetBundle::basePath|basePath]] ao invés da propriedade \n[[yii\\web\\AssetBundle::sourcePath|sourcePath]].\n\nPara as [extensões](structure-extensions.md), por seus assets estarem localizados \njuntamente com seus códigos fonte em um diretório não acessível pela Web, você \nterá que especificar a propriedade [[yii\\web\\AssetBundle::sourcePath|sourcePath]] \nao definir as classes de asset bundle.\n\n> Observação: Não use o `@webroot/assets` como o [[yii\\web\\AssetBundle::sourcePath|caminho da fonte]]. \n  Este diretório é usado por padrão pelo [[yii\\web\\AssetManager|gerenciador de asset]] \n  para salvar os arquivos de asset publicados a partir de seu local de origem. \n  Qualquer conteúdo deste diretório será considerado como temporário e podem \n  estar sujeitos a serem deletados.\n\n\n### Dependências de Assets <span id=\"asset-dependencies\"></span>\n\nAo incluir vários arquivos CSS ou JavaScript em uma página Web, devem seguir uma \ndeterminada ordem para evitar problemas de sobrescritas. Por exemplo, se você \nestiver usando um widget JQuery UI em um página, você deve garantir que o arquivo \nJavaScript do JQuery esteja incluído antes que o arquivo JavaScript do JQuery UI. \nChamamos esta tal ordenação de dependência entre os assets.\n\nA dependência de assets são especificados principalmente através da propriedade \n[[yii\\web\\AssetBundle::depends]]. No exemplo do `AppAsset`, o asset bundle depende \nde outros dois asset bundles: [[yii\\web\\YiiAsset]] e [[yii\\bootstrap\\BootstrapAsset]], \no que significa que os arquivos CSS e JavaScript do `AppAsset` serão incluídos \n*após* a inclusão dos arquivos dos dois bundles dependentes.\n\nAs dependências de assets são transitivas. Isto significa que se um asset bundle \nA depende de B e que o B depende de C, o A também dependerá de C.\n\n\n### Opções do Asset <span id=\"asset-options\"></span>\n\nVocê pode especificar as propriedades [[yii\\web\\AssetBundle::cssOptions|cssOptions]] \ne [[yii\\web\\AssetBundle::jsOptions|jsOptions]] para personalizar o modo que os \narquivos CSS e JavaScript serão incluídos em uma página. Os valores destas propriedades \nserão passadas respectivamente para os métodos [[yii\\web\\View::registerCssFile()]] \ne [[yii\\web\\View::registerJsFile()]], quando forem chamados pela \n[view (visão)](structure-views.md) para incluir os arquivos CSS e JavaScript.\n\n> Observação: As opções definidas em uma classe bundle aplicam-se para *todos* \nos arquivos CSS/JavaScript de um bundle. Se você quiser usar opções diferentes \npara arquivos diferentes, você deve criar asset bundles separados e usar um \nconjunto de opções para cada bundle.\n\nPor exemplo, para incluir condicionalmente um arquivo CSS para navegadores IE9 \nou mais antigo, você pode usar a seguinte opção:\n\n```php\npublic $cssOptions = ['condition' => 'lte IE9'];\n```\n\nIsto fara com que um arquivo CSS do bundle seja incluído usando as seguintes tags \nHTML:\n\n```html\n<!--[if lte IE9]>\n<link rel=\"stylesheet\" href=\"path/to/foo.css\">\n<![endif]-->\n```\n\nPara envolver as tags links do CSS dentro do `<noscript>`, você poderá configurar \no `cssOptions` da seguinte forma,\n\n```php\npublic $cssOptions = ['noscript' => true];\n```\n\nPara incluir um arquivo JavaScript na seção `<head>` de uma página (por padrão, \nos arquivos JavaScript são incluídos no final da seção `<body>`, use a seguinte \nopção:\n\n```php\npublic $jsOptions = ['position' => \\yii\\web\\View::POS_HEAD];\n```\n\nPor padrão, quando um asset bundle está sendo publicado, todo o conteúdo do \ndiretório especificado pela propriedade [[yii\\web\\AssetBundle::sourcePath]] serão \npublicados. Para você personalizar este comportamento configurando a propriedade \n[[yii\\web\\AssetBundle::publishOptions|publishOptions]]. Por exemplo, para publicar \napenas um ou alguns subdiretórios do [[yii\\web\\AssetBundle::sourcePath]], você \npode fazer a seguinte classe de asset bundle.\n\n```php\n<?php\nnamespace app\\assets;\n\nuse yii\\web\\AssetBundle;\n\nclass FontAwesomeAsset extends AssetBundle \n{\n    public $sourcePath = '@bower/font-awesome'; \n    public $css = [ \n        'css/font-awesome.min.css', \n    ]; \n    \n    public function init()\n    {\n        parent::init();\n        $this->publishOptions['beforeCopy'] = function ($from, $to) {\n            $dirname = basename(dirname($from));\n            return $dirname === 'fonts' || $dirname === 'css';\n        };\n    }\n}  \n```\n\nO exemplo anterior define um asset bundle para o \n[pacode de \"fontawesome\"](https://fontawesome.com/). Ao especificar a opção de \npublicação `beforeCopy`, apenas os subdiretórios `fonts` e `css` serão publicados.\n\n\n### Assets do Bower e NPM<span id=\"bower-npm-assets\"></span>\n\nA maioria dos pacotes JavaScript/CSS são gerenciados pelo [Bower](https://bower.io/) \ne/ou [NPM](https://www.npmjs.com/).\nSe sua aplicação ou extensão estiver usando um destes pacotes, é recomendado que \nvocê siga os passos a seguir para gerenciar os assets na biblioteca:\n\n1. Modifique o arquivo de sua aplicação ou extensão e informe os pacotes na \n   entrada `require`. Você deve usar `bower-asset/PackageName` (para pacotes Bower) \n   ou `npm-asset/PackageName` (para pacotes NPM) para referenciar à biblioteca.\n2. Crie uma classe asset bundle e informe os arquivos JavaScript/CSS que você \n   pretende usar em sua aplicação ou extensão. Você deve especificar a propriedade \n   [[yii\\web\\AssetBundle::sourcePath|sourcePath]] como `@bower/PackageName` ou \n   `@npm/PackageName`. Isto porque o Composer irá instalar os pacotes Bower ou \n   NPM no diretório correspondente a estas alias.\n\n> Observação: Alguns pacotes podem colocar todos os seus arquivos distribuídos \n  em um subdiretório. Se este for o caso, você deve especificar o subdiretório \n  como o valor da propriedade [[yii\\web\\AssetBundle::sourcePath|sourcePath]]. \n  Por exemplo, o [[yii\\web\\JqueryAsset]] usa `@bower/jquery/dist` ao invés de \n  `@bower/jquery`.\n\n\n## Usando Asset Bundles <span id=\"using-asset-bundles\"></span>\n\nPara usar um asset bundle, registre uma [view (visão)](structure-views.md) \nchamando o método [[yii\\web\\AssetBundle::register()]]. Por exemplo, no template \nda view (visão) você pode registrar um asset bundle conforme o exemplo a seguir:\n\n```php\nuse app\\assets\\AppAsset;\nAppAsset::register($this);  // $this representa o objeto da view (visão)\n```\n\n> Informação: O método [[yii\\web\\AssetBundle::register()]] retorna um objeto \n  asset bundle contendo informações sobre os assets publicados, tais como o \n  [[yii\\web\\AssetBundle::basePath|basePath]] ou [[yii\\web\\AssetBundle::baseUrl|baseUrl]].\n\nSe você estiver registrando um asset bundle em outros lugares, você deve fornecer \no objeto da view (visão) necessário. Por exemplo, para registrar um asset bundle \nem uma classe [widget](structure-widgets.md), você pode obter o objeto da view \n(visão) pelo `$this->view`.\n\nQuando um asset bundle for registrado em um view (visão), o Yii registrará todos \nos seus asset bundles dependentes. E, se um asset bundle estiver localizado em \num diretório inacessível pela Web, será publicado em um diretório Web.\nEm seguida, quando a view (visão) renderizar uma página, será gerado as tags \n`<link>` e `<script>` para os arquivos CSS e JavaScript informados nos bundles \nregistrados. A ordem destas tags são determinados pelas dependências dos bundles \nregistrados e pela ordem dos assets informados nas propriedades \n[[yii\\web\\AssetBundle::css]] e [[yii\\web\\AssetBundle::js]].\n\n\n### Personalizando os Asset Bundles <span id=\"customizing-asset-bundles\"></span>\n\nO Yii gerencia os asset bundles através do componente de aplicação chamado \n`assetManager` que é implementado pelo [[yii\\web\\AssetManager]]. \nAo configurar a propriedade [[yii\\web\\AssetManager::bundles]], é possível \npersonalizar o comportamento de um asset bundle.\nPor exemplo, o asset bundle padrão [[yii\\web\\JqueryAsset]] usa o arquivo \n`jquery.js` do pacote JQuery instalado pelo Bower. Para melhorar a disponibilidade \ne o desempenho, você pode querer usar uma versão hospedada pelo Google. \nIsto pode ser feito configurando o `assetManager` na configuração da aplicação \nconforme o exemplo a seguir:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'bundles' => [\n                'yii\\web\\JqueryAsset' => [\n                    'sourcePath' => null,   // do not publish the bundle\n                    'js' => [\n                        '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js',\n                    ]\n                ],\n            ],\n        ],\n    ],\n];\n```\n\nVocê pode configurar diversos asset bundles de forma semelhante através da \npropriedade [[yii\\web\\AssetManager::bundles]]. As chaves do array devem ser os \nnomes das classes (sem a barra invertida) dos asset bundles e os valores do \narray devem corresponder aos [arrays de configuração](concept-configurations.md).\n\n> Dica: Você pode, de forma condicional, escolher os assets que queira usar em \n> um asset bundle. O exemplo a seguir mostra como usar o `jquery.js` no ambiente \n> de desenvolvimento e o `jquery.min.js` em outra situação:\n>\n> ```php\n> 'yii\\web\\JqueryAsset' => [\n>     'js' => [\n>         YII_ENV_DEV ? 'jquery.js' : 'jquery.min.js'\n>     ]\n> ],\n> ```\n\nVocê pode desabilitar um ou vários asset bundles, associando `false` aos nomes \ndos asset bundles que queira ser desabilitado. Ao registrar um asset bundle \ndesabilitado em um view (visão), nenhuma das suas dependências serão registradas \ne a view (visão) também não incluirá quaisquer assets do bundle na página renderizada.\nPor exemplo, para desabilitar o [[yii\\web\\JqueryAsset]], você pode usando a \nseguinte configuração.\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'bundles' => [\n                'yii\\web\\JqueryAsset' => false,\n            ],\n        ],\n    ],\n];\n```\n\nVocê também pode desabilitar *todos* os asset bundles definindo o \n[[yii\\web\\AssetManager::bundles]] como `false`.\n\n\n### Mapeando Asset <span id=\"asset-mapping\"></span>\n\nÀs vezes, você pode querer \"corrigir\" os caminhos dos arquivos de asset \nincorretos/incompatíveis em vários asset bundles. Por exemplo, o bundle A usa o \n`jquery.min.js` com a versão 1.11.1 e o bundle B usa o `jquery.js` com a versão \n2.1.1. Embora você possa corrigir o problema personalizando cada bundle, existe \num modo mais simples usando o recurso de *mapeamento de asset* para mapear todos \nos assets incorretos para os assets desejados de uma vez. Para fazer isso, \nconfigure a propriedade [[yii\\web\\AssetManager::assetMap]] conforme o exemplo a \nseguir:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'assetMap' => [\n                'jquery.js' => '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js',\n            ],\n        ],\n    ],\n];\n```\n\nAs chaves do array [[yii\\web\\AssetManager::assetMap|assetMap]] são os nomes dos \nassets que você deseja corrigir e o valor são os caminhos dos assets desejados. \nAo registrar um asset bundle em uma view (visão), cada arquivo de asset relativo \naos arrays [[yii\\web\\AssetBundle::css|css]] e [[yii\\web\\AssetBundle::js|js]] \nserão examinados a partir deste mapeamento.\nSe qualquer uma das chaves forem encontradas para serem a última parte de um \narquivo de asset (que é prefixado com o [[yii\\web\\AssetBundle::sourcePath]] se \ndisponível), o valor correspondente substituirá o asset a ser registrado na view \n(visão). Por exemplo, o arquivo de asset `my/path/to/jquery.js` corresponde a \nchave a chave `jquery.js`.\n\n> Observação: Apenas os assets especificados usando caminhos relativos estão  \n  sujeitos ao mapeamento de assets. O caminho dos assets devem ser URLs absolutas \n   ou caminhos relativos ao caminho da propriedade [[yii\\web\\AssetManager::basePath]].\n\n\n### Publicação de Asset <span id=\"asset-publishing\"></span>\n\nComo mencionado anteriormente, se um asset bundle for localizado em um diretório\n que não é acessível pela Web, os seus assets serão copiados para um diretório \nWeb quando o bundle estiver sendo registrado na view (visão). Este processo é \nchamado de *publicação de asset* e é feito automaticamente pelo \n[[yii\\web\\AssetManager|gerenciador de asset]].\n\nPor padrão, os assets são publicados para o diretório `@webroot/assets` que \ncorresponde a URL `@web/assets`. Você pode personalizar este local configurando \nas propriedades [[yii\\web\\AssetManager::basePath|basePath]] e \n[[yii\\web\\AssetManager::baseUrl|baseUrl]].\n\nAo invés de publicar os assets pela cópia de arquivos, você pode considerar o uso \nde links simbólicos, caso o seu sistema operacional e o servidor Web permita-os. \nEste recurso pode ser habilitado definindo o \n[[yii\\web\\AssetManager::linkAssets|linkAssets]] como `true`.\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'linkAssets' => true,\n        ],\n    ],\n];\n```\n\nCom a configuração acima, o gerenciador de asset irá criar um link simbólico para \no caminho fonte de um asset bundle quando estiver sendo publicado. Isto é mais \nrápido que a cópia de arquivos e também pode garantir que os assets publicados \nestejam sempre atualizados.\n\n### Cache Busting <span id=\"cache-busting\"></span>\n\nPara aplicações Web que estam rodando no modo produção, é uma prática comum habilitar\no cache HTTP paraassets e outros recursos estáticos. A desvantagem desta prática é que \nsempre que você modificar um asset e implantá-lo em produção, um cliente pode ainda\nestar usando a versão antiga, devido ao cache HTTP. Para superar esta desvantagem, \nvocê pode utilizar o recurso cache busting, que foi implementado na versão 2.0.3, \nconfigurando o [[yii\\web\\AssetManager]] como mostrado a seguir:\n  \n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'appendTimestamp' => true,\n        ],\n    ],\n];\n```\n\nAo fazer isto, a URL de cada asset publicado será anexada ao seu último horário de \nmodificação. Por exemplo, a URL do `yii.js` pode parecer com `/assets/5515a87c/yii.js?v=1423448645\"`,\nonde o parâmetro `V` representa o último horário de modificação do arquivo `yii.js`.\nAgora se você modificar um asset, a sua URL será alterada, fazendo com que o cliente\nbusque a versão mais recente do asset.\n\n## Asset Bundles de Uso Comum <span id=\"common-asset-bundles\"></span>\n\nO código nativo do Yii definiu vários asset bundles. Entre eles, os bundles a \nseguir são de uso comum e podem ser referenciados em sua aplicação ou no código \nde extensão.\n\n- [[yii\\web\\YiiAsset]]: Inclui principalmente o arquivo `yii.js` que implementa \n  um mecanismo para organizar os códigos JavaScript em módulos. Ele também fornece \n  um suporte especial para os atributos `data-method` e `data-confirm` e outros \n  recursos úteis.\n- [[yii\\web\\JqueryAsset]]: Inclui o arquivo `jquery.js` do pacote jQuery do Bower.\n- [[yii\\bootstrap\\BootstrapAsset]]: Inclui o arquivo CSS do framework Twitter \n  Bootstrap.\n- [[yii\\bootstrap\\BootstrapPluginAsset]]: Inclui o arquivo JavaScript do framework \n  Twitter Bootstrap para dar suporte aos plug-ins JavaScript do Bootstrap.\n- [[yii\\jui\\JuiAsset]]: Inclui os arquivos CSS e JavaScript do biblioteca jQuery UI.\n\nSe o seu código depende do jQuery, jQuery UI ou Bootstrap, você deve usar estes \nasset bundles predefinidos ao invés de criar suas próprias versões. Se a definição \npadrão destes bundles não satisfazer o que precisa, você pode personaliza-los \nconforme descrito na subseção [Personalizando os Asset Bundles](#customizing-asset-bundles). \n\n\n## Conversão de Assets <span id=\"asset-conversion\"></span>\n\nAo invés de escrever diretamente códigos CSS e/ou JavaScript, os desenvolvedores \ngeralmente os escrevem em alguma sintaxe estendida e usam ferramentas especiais \npara converte-los em CSS/JavaScript. Por exemplo, para o código CSS você pode \nusar [LESS](https://lesscss.org/) ou [SCSS](https://sass-lang.com/); e para o \nJavaScript você pode usar o [TypeScript](https://www.typescriptlang.org/).\n\nVocê pode listar os arquivos de asset em sintaxe estendida nas propriedades \n[[yii\\web\\AssetBundle::css|css]] e [[yii\\web\\AssetBundle::js|js]] de um asset \nbundle. Por exemplo, \n\n```php\nclass AppAsset extends AssetBundle\n{\n    public $basePath = '@webroot';\n    public $baseUrl = '@web';\n    public $css = [\n        'css/site.less',\n    ];\n    public $js = [\n        'js/site.ts',\n    ];\n    public $depends = [\n        'yii\\web\\YiiAsset',\n        'yii\\bootstrap\\BootstrapAsset',\n    ];\n}\n```\n\nAo registrar um determinado asset bundle em uma view (visão), o \n[[yii\\web\\AssetManager|gerenciador de asset]] automaticamente rodará as \nferramentas de pré-processamento para converter os assets de sintaxe estendida \nem CSS/JavaScript. Quando a view (visão) finalmente renderizar uma página, será \nincluído os arquivos CSS/JavaScript convertidos ao invés dos arquivos de assets \noriginais em sintaxe estendida.\n\nO Yii usa as extensões dos nomes de arquivos para identificar se é um asset com \nsintaxe estendida. Por padrão, o Yii reconhecerá as seguintes sintaxes e extensões \nde arquivos:\n\n- [LESS](https://lesscss.org/): `.less`\n- [SCSS](https://sass-lang.com/): `.scss`\n- [Stylus](https://stylus-lang.com/): `.styl`\n- [CoffeeScript](https://coffeescript.org/): `.coffee`\n- [TypeScript](https://www.typescriptlang.org/): `.ts`\n\nO Yii conta com ferramentas de pré-processamento instalados para converter os \nassets. Por exemplo, para usar o [LESS](https://lesscss.org/) você deve instalar \no comando de pré-processamento `lessc`.\n\nVocê pode personalizar os comandos de pré-processamento e o da sintaxe estendida \nsuportada configurando o [[yii\\web\\AssetManager::converter]] conforme o exemplo \na seguir:\n\n```php\nreturn [\n    'components' => [\n        'assetManager' => [\n            'converter' => [\n                'class' => 'yii\\web\\AssetConverter',\n                'commands' => [\n                    'less' => ['css', 'lessc {from} {to} --no-color'],\n                    'ts' => ['js', 'tsc --out {to} {from}'],\n                ],\n            ],\n        ],\n    ],\n];\n```\n\nNo exemplo acima, especificamos a sintaxe estendida suportada pela propriedade \n[[yii\\web\\AssetConverter::commands]]. As chaves do array correspondem a extensão \ndos arquivos (sem o ponto a esquerda) e o valor do array possui a extensão do \narquivo de asset resultante e o comando para executar a conversão do asset. Os \ntokens `{from}` e `{to}` nos comandos serão substituídos pelo caminho do arquivo \nde asset fonte e pelo caminho do arquivo de asset de destino.\n\n> Informação: Existem outros modos de trabalhar com assets em sintaxe estendida, \n  além do descrito acima. Por exemplo, você pode usar ferramentas de compilação \n  tais como o [grunt](https://gruntjs.com/) para monitorar e automatizar a conversão \n  de assets em sintaxe estendidas. Neste caso, você deve listar os arquivos de \n  CSS/JavaScript resultantes nos asset bundles ao invés dos arquivos originais.\n\n\n## Combinando e Comprimindo Assets <span id=\"combining-compressing-assets\"></span>\n\nUma página Web pode incluir muitos arquivos CSS e/ou JavaScript. Para reduzir o \nnúmero de requisições HTTP e o tamanho total de downloads destes arquivos, uma \nprática comum é combinar e comprimir vários arquivos CSS/JavaScript em um ou em \npoucos arquivos e em seguida incluir estes arquivos comprimidos nas páginas Web \nao invés dos originais.\n\n> Informação: A combinação e compressão de assets normalmente são necessárias \n  quando uma aplicação está em modo de produção. No modo de desenvolvimento, \n  usar os arquivos CSS/JavaScript originais muitas vezes são mais convenientes \n  para depuração.\n\nA seguir, apresentaremos uma abordagem para combinar e comprimir arquivos de \nassets sem precisar modificar o código da aplicação existente.\n\n1. Localize todos os asset bundles em sua aplicação que você deseja combinar e \n   comprimir.\n2. Divida estes bundles em um ou alguns grupos. Observe que cada bundle pode \n   apenas pertencer a um único grupo.\n3. Combinar/Comprimir os arquivos CSS de cada grupo em um único arquivo. Faça \n   isto de forma semelhante para os arquivos JavaScript.\n4. Defina um novo asset bundle para cada grupo:\n   * Defina as propriedade [[yii\\web\\AssetBundle::css|css]] e \n     [[yii\\web\\AssetBundle::js|js]] com os arquivos CSS e JavaScript combinados, \n     respectivamente.\n   * Personalize os asset bundles de cada grupo definindo as suas propriedades \n     [[yii\\web\\AssetBundle::css|css]] e [[yii\\web\\AssetBundle::js|js]] como vazias \n     e definindo a sua propriedade [[yii\\web\\AssetBundle::depends|depends]] para \n     ser o novo asset bundle criado para o grupo.\n\nUsando esta abordagem, quando você registrar um asset bundle em uma view (visão), \nfará com que registre automaticamente o novo asset bundle do grupo que o bundle \noriginal pertence. E, como resultado, os arquivos de asset combinados/comprimidos \nserão incluídos na página, ao invés dos originais.\n\n\n### Um Exemplo <span id=\"example\"></span>\n\nVamos usar um exemplo para explicar melhor o exemplo acima: \n\nAssuma que sua aplicação possua duas páginas, X e Y. A página X usa os asset \nbundles A, B e C, enquanto a página Y usa os asset bundles B, C e D. \n\nVocê tem duas maneiras de dividir estes asset bundles. Uma delas é a utilização \nde um único grupo para incluir todos os asset bundles e a outra é colocar o A no \nGrupo X, o D no Grupo Y e (B, C) no Grupo S. Qual deles é o melhor? Isto depende. \nA primeira maneira tem a vantagem de ambas as páginas compartilharem os mesmos \narquivos CSS e JavaScript combinados, o que torna o cache HTTP mais eficaz. Por \noutro lado, pelo fato de um único grupo conter todos os bundles, o tamanho dos \narquivos CSS e JavaScript combinados será maior e, assim, aumentará o tempo de \ncarregamento inicial. Para simplificar este exemplo, vamos usar a primeira \nmaneira, ou seja, usaremos um único grupo para conter todos os bundles.\n\n> Informação: Dividir os asset bundles em grupos não é uma tarefa trivial. \nGeralmente requer que análise sobre o real trafego de dados de diversos assets \nem páginas diferentes. Para começar, você pode usar um único grupo para simplificar.\n\nUse as ferramentas existentes (por exemplo, \n[Closure Compiler](https://developers.google.com/closure/compiler/), \n[YUI Compressor](https://github.com/yui/yuicompressor/)) para combinar e \ncomprimir os arquivos CSS e JavaScript em todos os bundles. Observer que os \narquivos devem ser combinados na ordem que satisfaça as dependências entre os \nbundles. Por exemplo, se o bundle A depende do B e que dependa tanto do C quanto \ndo D, você deve listar os arquivos de asset a partir do C e D, em seguida pelo B \ne finalmente pelo A.\n\nDepois de combinar e comprimir, obteremos um arquivo CSS e um arquivo JavaScript. \nSuponha que os arquivos serão chamados de `all-xyz.css` e `all-xyz.js`, onde o \n`xyz` significa um timestamp ou um hash que é usado para criar um nome de arquivo \núnico para evitar problemas de cache HTTP.\n\nNós estamos na última etapa agora. Configure o \n[[yii\\web\\AssetManager|gerenciador de asset]] como o seguinte na configuração da \naplicação:\n\n```php\nreturn [\n    'components' => [\n        'assetManager' => [\n            'bundles' => [\n                'all' => [\n                    'class' => 'yii\\web\\AssetBundle',\n                    'basePath' => '@webroot/assets',\n                    'baseUrl' => '@web/assets',\n                    'css' => ['all-xyz.css'],\n                    'js' => ['all-xyz.js'],\n                ],\n                'A' => ['css' => [], 'js' => [], 'depends' => ['all']],\n                'B' => ['css' => [], 'js' => [], 'depends' => ['all']],\n                'C' => ['css' => [], 'js' => [], 'depends' => ['all']],\n                'D' => ['css' => [], 'js' => [], 'depends' => ['all']],\n            ],\n        ],\n    ],\n];\n```\n\nComo foi explicado na subseção [Personalizando os Asset Bundles](#customizing-asset-bundles), \na configuração acima altera o comportamento padrão de cada bundle. Em particular, \nos bundles A, B, C e D não precisam mais de arquivos de asset.\nAgora todos dependem do bundle `all` que contém os arquivos `all-xyz.css` e \n`all-xyz.js` combinados. Consequentemente, para a página X, ao invés de incluir \nos arquivos fontes originais dos bundles A, B e C, apenas estes dois arquivos \ncombinados serão incluídos; a mesma coisa acontece com a página Y.\n\nExiste um truque final para fazer o trabalho da abordagem acima de forma mais \nsimples. Ao invés de modificar diretamente o arquivo de configuração da aplicação, \nvocê pode colocar o array de personalização do bundle em um arquivo separado e \ncondicionalmente incluir este arquivo na configuração da aplicação. Por exemplo, \n\n```php\nreturn [\n    'components' => [\n        'assetManager' => [\n            'bundles' => require __DIR__ . '/' . (YII_ENV_PROD ? 'assets-prod.php' : 'assets-dev.php'),  \n        ],\n    ],\n];\n```\n\nOu seja, o array de configuração do asset bundle será salvo no arquivo \n`assets-prod.php` quando estiver em modo de produção e o arquivo `assets-dev.php` \nquando não estiver em produção.\n\n\n### Usando o Comando `asset` <span id=\"using-asset-command\"></span>\n\nO Yii fornece um comando console chamado `asset` para automatizar a abordagem que \nacabamos de descrever.\n\nPara usar este comando, você deve primeiro criar um arquivo de configuração para \ndescrever quais asset bundles devem ser combinados e como devem ser agrupados. \nVocê pode usar o subcomando `asset/template` para gerar um template para que \npossa modificá-lo para atender as suas necessidades.\n\n```\nyii asset/template assets.php\n```\n\nO comando gera um arquivo chamado `assets.php` no diretório onde foi executado. \nO conteúdo deste arquivo assemelha-se ao seguinte:\n\n```php\n<?php\n/**\n * Arquivo de configuração para o comando console \"yii asset\".\n * Observer que no ambiente de console, alguns caminhos de alias como '@webroot' e o '@web' podem não existir.\n * Por favor, defina os caminhos de alias inexistentes.\n */\nreturn [\n    // Ajuste do comando/call-back para a compressão os arquivos JavaScript:\n    'jsCompressor' => 'java -jar compiler.jar --js {from} --js_output_file {to}',\n    // Ajuste de comando/callback para a compressão dos arquivos CSS:\n    'cssCompressor' => 'java -jar yuicompressor.jar --type css {from} -o {to}',\n    // A lista de asset bundles que serão comprimidos:\n    'bundles' => [\n        // 'yii\\web\\YiiAsset',\n        // 'yii\\web\\JqueryAsset',\n    ],\n    // Asset bundle do resultado da compressão:\n    'targets' => [\n        'all' => [\n            'class' => 'yii\\web\\AssetBundle',\n            'basePath' => '@webroot/assets',\n            'baseUrl' => '@web/assets',\n            'js' => 'js/all-{hash}.js',\n            'css' => 'css/all-{hash}.css',\n        ],\n    ],\n    // Configuração do gerenciados de asset:\n    'assetManager' => [\n    ],\n];\n```\n\nVocê deve modificar este arquivo e especificar quais bundles você deseja combinar \nna opção `bundles`. Na opção `targets` você deve especificar como os bundles \ndevem ser divididos em grupos. Você pode especificar um ou vários grupos, como \nmencionado anteriormente.\n\n> Observação: Como as alias `@webroot` e `@web` não estão disponíveis na aplicação \n  console, você deve defini-los explicitamente na configuração.\n\nOs arquivos JavaScript são combinados, comprimidos e escritos no arquivo \n`js/all-{hash}.js` onde {hash} será substituído pelo hash do arquivo resultante.\n\nAs opções `jsCompressor` e `cssCompressor` especificam os comando ou callbacks \nPHP para realizar a combinação/compressão do JavaScript e do CSS. Por padrão, o \nYii usa o [Closure Compiler](https://developers.google.com/closure/compiler/) \npara combinar os arquivos JavaScript e o \n[YUI Compressor](https://github.com/yui/yuicompressor/) para combinar os arquivos CSS.\nVocê deve instalar estas ferramentas manualmente ou ajustar estas opções para \nusar as suas ferramentas favoritas.\n\nCom o arquivo de configuração, você pode executar o comando `asset` para combinar\ne comprimir os arquivos de asset e em seguida gerar um novo arquivo de configuração \nde asset bundle `assets-prod.php`:\n\n```\nyii asset assets.php config/assets-prod.php\n```\n\nO arquivo de configuração gerado pode ser incluído na configuração da aplicação, \nconforme descrito na última subseção.\n\n\n> Informação: O uso do comando `asset` não é a única opção para automatizar o \n  processo de combinação e compressão de asset. Você pode usar a excelente \n  ferramenta chamada [grunt](https://gruntjs.com/) para atingir o mesmo objetivo.\n\n### Agrupando Asset Bundles <span id=\"grouping-asset-bundles\"></span>\n\nNa última subseção, nós explicamos como combinar todos os asset bundles em um\núnico bundle, a fim de minimizar as requisições HTTP de arquivos de asset referenciados\nem uma aplicação. Porém, isto nem sempre é desejável na prática. Por exemplo, imagine \nque sua aplicação possua um \"front end\", bem como um \"back end\", cada um possuindo um \nconjunto diferente de JavaScript e CSS. Neste caso, combinando todos os asset bundles\nde ambas as extremidades em um único bundle não faz sentido, porque os asset bundles \npata o \"front end\" não são utilizados pelo \"back end\" e seria um desperdício de uso \nde banda de rede para enviar os assets do \"back end\" quando uma página de \"front end\"\nfor solicitada.\n\nPara resolver este problema, você pode dividir asset bundles e, grupos e combinar \nasset bundles para cada grupo. A configuração a seguir mostra como pode agrupar \nos asset bundles:\n\n```php\nreturn [\n    ...\n    // Especifique o bundle de saída com os grupos:\n    'targets' => [\n        'allShared' => [\n            'js' => 'js/all-shared-{hash}.js',\n            'css' => 'css/all-shared-{hash}.css',\n            'depends' => [\n                // Inclua todos os asset que serão compartilhados entre o 'backend' e o 'frontend'\n                'yii\\web\\YiiAsset',\n                'app\\assets\\SharedAsset',\n            ],\n        ],\n        'allBackEnd' => [\n            'js' => 'js/all-{hash}.js',\n            'css' => 'css/all-{hash}.css',\n            'depends' => [\n                // Inclua apenas os assets do 'backend':\n                'app\\assets\\AdminAsset'\n            ],\n        ],\n        'allFrontEnd' => [\n            'js' => 'js/all-{hash}.js',\n            'css' => 'css/all-{hash}.css',\n            'depends' => [], // Inclua todos os asset restantes\n        ],\n    ],\n    ...\n];\n```\n\nComo você pode ver, os asset bundles são divididos em três grupos: `allShared`, `allBackEnd` e `allFrontEnd`.\nCada um deles dependem de um conjunto de asset bundles. Por exemplo, o `allBackEnd` depende de `app\\assets\\AdminAsset`.\nAo executar o comando `asset` com essa configuração, será combinado os asset bundles de acordo com as especificações acima.\n\n> Informação: Voce pode deixar a configuração `depends` em branco para um determinado bundle.\nAo fazer isso, esse asset bundle dependerá de todos os asset bundles restantes que outros\ndeterminados bundles não dependam.\n"
  },
  {
    "path": "docs/guide-pt-BR/structure-controllers.md",
    "content": "Controllers (Controladores)\n===========\n\nOs controllers (controladores) fazem parte da arquitetura [MVC](https://pt.wikipedia.org/wiki/MVC).\nSão objetos de classes que estendem de [[yii\\base\\Controller]] e são responsáveis\npelo processamento das requisições e por gerar respostas. Em particular, após\nassumir o controle de [applications](structure-applications.md), controllers\nanalisarão os dados de entradas obtidos pela requisição, passarão estes dados\npara os [models](structure-models.md) (modelos), incluirão os resultados dos models\n(modelos) nas [views](structure-views.md) (visões) e finalmente gerarão as respostas\nde saída.\n\n\n## Actions (Ações) <span id=\"actions\"></span>\n\nOs controllers são compostos por unidades básicas chamadas de *ações* que podem\nser tratados pelos usuários finais a fim de realizar a sua execução.\n\nNo exemplo a seguir mostra um controller `post` com duas ações: `view` e `create`:\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse app\\models\\Post;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\n\nclass PostController extends Controller\n{\n    public function actionView($id)\n    {\n        $model = Post::findOne($id);\n        if ($model === null) {\n            throw new NotFoundHttpException;\n        }\n\n        return $this->render('view', [\n            'model' => $model,\n        ]);\n    }\n\n    public function actionCreate()\n    {\n        $model = new Post;\n\n        if ($model->load(Yii::$app->request->post()) && $model->save()) {\n            return $this->redirect(['view', 'id' => $model->id]);\n        } else {\n            return $this->render('create', [\n                'model' => $model,\n            ]);\n        }\n    }\n}\n```\n\nNa ação `view` (definido pelo método `actionView()`), o primeiro código carrega o\n[model](structure-models.md) conforme o ID solicitado; Se o model for devidamente\ncarregado, a ação irá exibi-lo utilizado a [view](structure-views.md) chamada de `view`.\nCaso contrário, a ação lançará uma exceção.\n\nNa ação `create` (definido pelo método `actionCreate()`), o código é parecido.\nPrimeiro ele tenta popular o [model](structure-models.md) usando os dados da requisição\nem seguida os salva. Se ambos forem bem sucedidos, a ação redirecionará o navegador\npara a ação `view` com o novo ID criado pelo model. Caso contrário, a ação exibirá\na view `create` na qual os usuário poderão fornecer os dados necessários.\n\n\n## Routes (Rotas) <span id=\"routes\"></span>\n\nOs usuários finais abordarão as ações por meio de *rotas*. Uma rota é uma string composta\npelas seguintes partes:\n\n* um ID do módulo: serve apenas se o controller pertencer a um [módulo](structure-modules.md) que não seja da aplicação;\n* um ID do controller: uma string que identifica exclusivamente o controller dentre todos os controllers da mesma aplicação (ou do mesmo módulo, caso o controller pertença a um módulo);\n* um ID da ação: uma string que identifica exclusivamente uma ação dentre todas as ações de um mesmo controller.\n\nAs rotas seguem o seguinte formato:\n\n```\nIDdoController/IDdoAction\n```\n\nou o seguinte formato se o controller estiver em um módulo:\n\n```php\nIDdoModule/IDdoController/IDdoAction\n```\n\nPortanto, se um usuário fizer uma requisição com a URL `https://hostname/index.php?r=site/index`,\na ação `index` do controller `site` será executada. Para mais detalhes sobre como\nas ações são resolvidas pelas rotas, por favor consulte a seção [Roteamento e Criação de URL](runtime-routing.md).\n\n\n## Criando Controllers <span id=\"creating-controllers\"></span>\n\nEm [[yii\\web\\Application|aplicações Web]], os controllers devem estender de [[yii\\web\\Controller]]\nou de suas classes filhas. De forma semelhante, em [[yii\\console\\Application|aplicaçoes console]],\nos controllers devem estender de [[yii\\console\\Controller]] ou de suas classes filhos. O código a seguir define um controller `site`:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n}\n```\n\n\n### IDs de Controllers <span id=\"controller-ids\"></span>\n\nNormalmente, um controller é projetado para tratar as requisições relativos a\num determinado tipo de recurso. Por esta razão, os IDs dos controllers geralmente\nsão substantivos que referenciam-se ao tipo de recurso que será tratado.\nPor exemplo, você pode usar o `article` como o ID do um controller para tratar\ndados de artigos.\n\nPor padrão, os IDs dos controllers devem conter apenas esses caracteres:\nletras inglesas em caixa baixa, números, underscores (underline), hífens e barras.\nPor exemplo, `article` e `post-comment` são ambos IDs de controllers válidos,\nenquanto `article?`, `PostComment`, `admin\\post` não são.\n\nUm ID de controller também pode conter um prefixo para o subdiretório. Por exemplo,\n`admin/article` representa um controller `article` em um subdiretório `admin` sob\no [[yii\\base\\Application::controllerNamespace|namespace do controller]]\nOs caracteres válidos para os prefixos de subdiretórios incluem: letras inglesas\nem caixa alto ou caixa baixa, números, underscores (underline) e barras, onde as\nbarras são usadas para separar os níveis dos subdiretórios (por exemplo, `panels/admin`).\n\n\n### Nomenclatura da Classe do Controller <span id=\"controller-class-naming\"></span>\n\nOs nomes da classes dos controllers podem ser derivadas dos IDs dos controllers\nde acordo com as seguintes procedimentos:\n\n1. Colocar em caixa alta a primeira letra de cada palavra separadas por traço.\n   Observe que se o ID do controller possuir barras, a regra é aplicada apenas na\n   parte após a última barra no ID.\n2. Remover os traços e substituir todas as barras por barras invertidas.\n3. Adicionar `Controller` como sufixo.\n4. Preceder ao [[yii\\base\\Application::controllerNamespace|namespace do controller]].\n\nSegue alguns exemplos, assumindo que o [[yii\\base\\Application::controllerNamespace|namespace do controller]]\ntenha por padrão o valor `app\\controllers`:\n\n* `article` torna-se `app\\controllers\\ArticleController`;\n* `post-comment` torna-se `app\\controllers\\PostCommentController`;\n* `admin/post-comment` torna-se `app\\controllers\\admin\\PostCommentController`;\n* `adminPanels/post-comment` torna-se `app\\controllers\\adminPanels\\PostCommentController`.\n\nAs classes dos controllers devem ser [autoloadable](concept-autoloading.md).\nPor esta razão, nos exemplos anteriores, o controller `article` deve ser salvo\nno arquivo cuja [alias](concept-aliases.md) é `@app/controllers/ArticleController.php`;\nenquanto o controller `admin/post-comment` deve ser salvo no `@app/controllers/admin/PostCommentController.php`.\n\n> Informação: No último exemplo `admin/post-comment`, mostra como você pode colocar\num controller em um subdiretório do [[yii\\base\\Application::controllerNamespace|namespace controller]]. Isto é útil quando você quiser organizar seus controllers em diversas\ncategorias e não quiser usar [módulos](structure-modules.md).\n\n\n### Mapeando Controllers <span id=\"controller-map\"></span>\n\nVocê pode configurar um [[yii\\base\\Application::controllerMap|mapeamento de controllers]]\npara superar as barreiras impostas pelos IDs de controllers e pelos nomes de classes\ndescritos acima. Isto é útil principalmente quando quiser esconder controllers\nde terceiros na qual você não tem controle sobre seus nomes de classes.\n\nVocê pode configurar o [[yii\\base\\Application::controllerMap|mapeamento de controllers]]\nna [configuração da aplicação](structure-applications.md#application-configurations). Por exemplo:\n\n```php\n[\n    'controllerMap' => [\n        // declara o controller \"account\" usando um nome de classe\n        'account' => 'app\\controllers\\UserController',\n\n        // declara o controller \"article\" usando uma configuração em array\n        'article' => [\n            'class' => 'app\\controllers\\PostController',\n            'enableCsrfValidation' => false,\n        ],\n    ],\n]\n```\n\n\n### Controller Padrão <span id=\"default-controller\"></span>\n\nCada aplicação tem um controller padrão que é especificado pela propriedade [[yii\\base\\Application::defaultRoute]].\nQuando uma requisição não especificar uma [rota](#id-da-rota), será utilizada a\nrota especificada pela propriedade.\nPara as [[yii\\web\\Application|aplicações Web]], este valor é `'site'`, enquanto\npara as [[yii\\console\\Application|aplicações console]] é `help`. Portanto, se uma\nURL for `https://hostname/index.php`, o controller `site` será utilizado nesta requisição.\n\nVocê pode alterar o controller padrão como a seguinte [configuração da aplicação](structure-applications.md#application-configurations):\n\n```php\n[\n    'defaultRoute' => 'main',\n]\n```\n\n\n## Criando Ações <span id=\"creating-actions\"></span>\n\nCriar ações pode ser tão simples como a definição dos chamados *métodos de ação*\nem uma classe controller. Um método de ação é um método *público* cujo nome inicia\ncom a palavra `action`. O valor de retorno representa os dados de resposta a serem\nenviados aos usuário finais. O código a seguir define duas ações, `index` e `hello-world`:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actionIndex()\n    {\n        return $this->render('index');\n    }\n\n    public function actionHelloWorld()\n    {\n        return 'Hello World';\n    }\n}\n```\n\n\n### IDs de Actions <span id=\"action-ids\"></span>\n\nUma ação muitas vezes é projetada para realizar uma manipulação em particular sobre\num recurso. Por esta razão, os IDs das ações geralmente são verbos, tais como `view`, `update`, etc.\n\nPor padrão, os IDs das ações devem conter apenas esses caracteres: letras inglesas\nem caixa baixa, números, underscores (underline) e traços. Os traços em um ID da\nação são usados para separar palavras. Por exemplo, `view`, `update2` e `comment-post`\nsão IDs válidos, enquanto `view?` e `Update` não são.\n\nVocê pode criar ações de duas maneiras: ações inline (em sequência) e\nações standalone (autônomas). Uma ação inline é definida pelo método\nde uma classe controller, enquanto uma ação standalone é uma classe que estende de\n[[yii\\base\\Action]] ou de uma classe-filha. As ações inline exigem menos esforço\npara serem criadas e muitas vezes as preferidas quando não se tem a intenção de\nreutilizar estas ações. Ações standalone, por outro lado, são criados principalmente\npara serem utilizados em diferentes controllers ou para serem distribuídos como\n[extensions](structure-extensions.md).\n\n\n### Ações Inline <span id=\"inline-actions\"></span>\n\nAs ações inline referem-se a os chamados métodos de ação, que foram descritos anteriormente.\n\nOs nomes dos métodos de ações são derivadas dos IDs das ações de acordo com os\nseguintes procedimentos:\n\n1. Colocar em caixa alta a primeira letra de cada palavra do ID da ação;\n2. Remover os traços;\n3. Adicionar o prefixo `action`.\n\nPor exemplo, `index` torna-se `actionIndex` e `hello-world` torna-se `actionHelloWorld`.\n\n> Observação: Os nomes dos métodos de ações são *case-sensitive*. Se você tiver\n  um método chamado `ActionIndex`, não será considerado como um método de ação e\n  como resultado, o pedido para a ação `index` lançará uma exceção. Observe também\n  que os métodos de ações devem ser públicas. Um método privado ou protegido NÃO\n  será definido como ação inline.\n\nAs ações inline normalmente são as mais utilizadas pois demandam pouco esforço\npara serem criadas. No entanto, se você deseja reutilizar algumas ações em diferentes\nlugares ou se deseja distribuir uma ação, deve considerar defini-la como uma *ação standalone*.\n\n\n### Ações Standalone <span id=\"standalone-actions\"></span>\n\nAções standalone são definidas por classes de ações que estendem de [[yii\\base\\Action]]\nou de uma classe-filha.\nPor example, nas versões do Yii, existe a [[yii\\web\\ViewAction]] e a [[yii\\web\\ErrorAction]], ambas são ações standalone.\n\nPara usar uma ação standalone, você deve *mapear as ações* sobrescrevendo o método\n[[yii\\base\\Controller::actions()]] em suas classes controllers como o seguinte:\n\n```php\npublic function actions()\n{\n    return [\n        // declara a ação \"error\" usando um nome de classe\n        'error' => 'yii\\web\\ErrorAction',\n\n        // declara a ação \"view\" usando uma configuração em array\n        'view' => [\n            'class' => 'yii\\web\\ViewAction',\n            'viewPrefix' => '',\n        ],\n    ];\n}\n```\n\nComo pode ver, o método `actions()` deve retornar um array cujas chaves são os IDs\ndas ações e os valores correspondentes ao nome da classe da ação ou [configurações](concept-configurations.md). Ao contrário das ações inline, os IDs das ações standalone\npodem conter caracteres arbitrários desde que sejam mapeados no método `actions()`.\n\n\nPara criar uma classe de ação standalone, você deve estender de [[yii\\base\\Action]] ou de duas classes filhas e implementar um método público chamado `run()`. A regra para o método `run()`\né semelhante ao de um método de ação. Por exemplo,\n\n```php\n<?php\nnamespace app\\components;\n\nuse yii\\base\\Action;\n\nclass HelloWorldAction extends Action\n{\n    public function run()\n    {\n        return \"Hello World\";\n    }\n}\n```\n\n\n### Resultados da Ação <span id=\"action-results\"></span>\n\nO valor de retorno do método de ação ou do método `run()` de uma ação standalone\nsão importantes. Eles representam o resultado da ação correspondente.\n\nO valor de retorno pode ser um objeto de [resposta](runtime-responses.md) que\nserá enviado como resposta aos usuários finais.\n\n* Para [[yii\\web\\Application|aplicações Web]], o valor de retorno também poder\n  ser algum dado arbitrário que será atribuído à propriedade [[yii\\web\\Response::data]]\n  e ainda ser convertido em uma string para representar o corpo da resposta.\n* Para [[yii\\console\\Application|aplicações console]], o valor de retorno também\n  poder ser um inteiro representando o [[yii\\console\\Response::exitStatus|exit status]]\n  (status de saída) da execução do comando.\n\nNos exemplos acima, todos os resultados são strings que serão tratados como o\ncorpo das respostas para serem enviados aos usuários finais. No exemplo a seguir,\nmostra como uma ação pode redirecionar o navegador do usuário para uma nova URL\nretornando um objeto de resposta (o método [[yii\\web\\Controller::redirect()|redirect()]]\nretorna um objeto de resposta):\n\n```php\npublic function actionForward()\n{\n    // redireciona o navegador do usuário para https://example.com\n    return $this->redirect('https://example.com');\n}\n```\n\n\n### Parâmetros da Ação <span id=\"action-parameters\"></span>\n\nOs métodos de ações para as ações inline e os métodos `run()` para as ações\nstandalone podem receber parâmetros, chamados *parâmetros da ação*.\nSeus valores são obtidos a partir das requisições. Para\n[[yii\\web\\Application|aplicações Web]], o valor de cada parâmetro da ação são\nobtidos pelo `$_GET` usando o nome do parâmetro como chave; para\n[[yii\\console\\Application|aplicações console]], eles correspondem aos argumentos\nda linha de comando.\n\nNo exemplo a seguir, a ação `view` (uma ação inline) possui dois parâmetros declarados:\n`$id` e `$version`.\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass PostController extends Controller\n{\n    public function actionView($id, $version = null)\n    {\n        // ...\n    }\n}\n```\n\nA seguir, os parâmetros da ação serão populados em diferentes requisições:\n\n* `https://hostname/index.php?r=post/view&id=123`: o parâmetro `$id` receberá\n  o valor `'123'`, enquanto o `$version` continuará com o valor nulo porque não\n  existe o parâmetro `version` na URL.\n* `https://hostname/index.php?r=post/view&id=123&version=2`: os parâmetros `$id`\n  e `$version` serão receberão os valores `'123'` e `'2'`, respectivamente.\n* `https://hostname/index.php?r=post/view`: uma exceção [[yii\\web\\BadRequestHttpException]]\n  será lançada porque o parâmetro obrigatório `$id` não foi informado na requisição.\n* `https://hostname/index.php?r=post/view&id[]=123`: uma exceção [[yii\\web\\BadRequestHttpException]]\n  será lançada porque o parâmetro `$id` foi informado com um valor array `['123']`\n  na qual não era esperado.\n\nSe você quiser que um parâmetro da ação aceite valores arrays, deverá declara-lo\nexplicitamente com `array`, como mostro a seguir:\n\n```php\npublic function actionView(array $id, $version = null)\n{\n    // ...\n}\n```\n\nAgora, se a requisição for `https://hostname/index.php?r=post/view&id[]=123`, o\nparâmetro `$id` receberá o valor `['123']`. Se a requisição for\n`https://hostname/index.php?r=post/view&id=123`, o parâmetro `$id` ainda receberá\num array como valor pois o valor escalar `'123'` será convertido automaticamente\nem um array.\n\nOs exemplo acima mostram, principalmente, como os parâmetros da ação trabalham em\naplicações Web. Para aplicações console, por favor, consulte a seção\n[Comandos de Console](tutorial-console.md) para mais detalhes.\n\n\n### Default Action <span id=\"default-action\"></span>\n\nCada controller tem uma ação padrão especificado pela propriedade\n[[yii\\base\\Controller::defaultAction]].\nQuando uma [rota](#id-da-rota) contém apenas o ID do controller, implica que a\nação padrão do controller seja solicitada.\n\nPor padrão, a ação padrão é definida como `index`. Se quiser alterar o valor padrão,\nsimplesmente sobrescreva esta propriedade na classe controller, como o seguinte:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public $defaultAction = 'home';\n\n    public function actionHome()\n    {\n        return $this->render('home');\n    }\n}\n```\n\n\n## Ciclo de Vida do Controller <span id=\"controller-lifecycle\"></span>\n\nAo processar uma requisição, a [aplicação](structure-applications.md) criará\num controller baseada na [rota](#routes) solicitada. O controller, então, se submeterá\nao seguinte ciclo de vida para concluir a requisição:\n\n1. O método [[yii\\base\\Controller::init()]] é chamado após o controller ser criado e configurado.\n2. O controller cria um objeto da ação baseada no ID da ação solicitada:\n   * Se o ID da ação não for especificado, o [[yii\\base\\Controller::defaultAction|ID da ação padrão]] será utilizada.\n   * Se o ID da ação for encontrada no [[yii\\base\\Controller::actions()|mapeamento das ações]], uma ação standalone será criada;\n   * Se o ID da ação for encontrada para corresponder a um método de ação, uma ação inline será criada;\n   * Caso contrário, uma exceção [[yii\\base\\InvalidRouteException]] será lançada.\n3. De forma sequencial, o controller chama o método `beforeAction()` da aplicação, o módulo (se o controller pertencer a um módulo) e o controller.\n   * Se uma das chamadas retornar `false`, o restante dos métodos subsequentes `beforeAction()` serão ignoradas e a execução da ação será cancelada.\n   * Por padrão, cada método `beforeAction()` desencadeia a execução de um evento chamado `beforeAction` na qual você pode associar a uma função (handler).\n4. O controller executa a ação:\n   * Os parâmetros da ação serão analizados e populados a partir dos dados obtidos pela requisição;\n5. De forma sequencial, o controller chama o método `afterAction()` do controller, o módulo (se o controller pertencer a um módulo) e a aplicação.\n   * Por padrão, cada método `afterAction()` desencadeia a execução de um evento chamado `afterAction` na qual você pode associar a uma função (handler).\n6. A aplicação obterá o resultado da ação e irá associá-lo na [resposta](runtime-responses.md).\n\n\n## Boas Práticas <span id=\"best-practices\"></span>\n\nEm uma aplicação bem projetada, frequentemente os controllers são bem pequenos na\nqual cada ação possui poucas linhas de códigos.\nSe o controller for um pouco complicado, geralmente indica que terá que refaze-lo\ne passar algum código para outro classe.\n\nSegue algumas boas práticas em destaque. Os controllers:\n\n* podem acessar os dados de uma [requisição](runtime-requests.md);\n* podem chamar os métodos dos [models](structure-models.md) e outros componentes\n  de serviço com dados da requisição;\n* podem usar as [views](structure-views.md) para compor as respostas;\n* NÃO devem processar os dados da requisição - isto deve ser feito na [camada model (modelo)](structure-models.md);\n* devem evitar inserir códigos HTML ou outro código de apresentação - é melhor\n  que sejam feitos nas [views](structure-views.md).\n"
  },
  {
    "path": "docs/guide-pt-BR/structure-entry-scripts.md",
    "content": "Scripts de Entrada\n==================\n\nScripts de entrada são o primeiro passo no processo de inicialização da aplicação.\nUma aplicação (seja uma aplicação Web ou uma aplicação console) possui um único script de\nentrada. Os usuários finais fazem requisições nos scripts de entrada que criam\nas instâncias da aplicação e redirecionam as requisições para elas.\n\nOs scripts de entrada para aplicações Web devem estar armazenados em diretórios\nacessíveis pela Web, de modo que eles possam ser acessados pelos usuários finais.\nFrequentemente são chamados de `index.php`, mas também podem usar outros nomes,\ndesde que os servidores Web consigam localizá-los.\n\nOs scripts de entrada para aplicações do console são geralmente armazenados no\n[caminho base](structure-applications.md) das aplicações e são chamados de `yii`\n(com o sufixo `.php`). Eles devem ser tornados executáveis para que os usuários\npossam executar aplicações do console através do comando\n`./yii <rota> [argumentos] [opções]`.\n\nO trabalho principal dos scripts de entrada é o seguinte:\n\n* Definir constantes globais;\n* Registrar o [autoloader do Composer](https://getcomposer.org/doc/01-basic-usage.md#autoloading);\n* Incluir o arquivo da classe [[Yii]];\n* Carregar a configuração da aplicação;\n* Criar e configurar uma instância da [aplicação](structure-applications.md);\n* Chamar [[yii\\base\\Application::run()]] para processar as requisições que chegam.\n\n\n## Aplicações Web <span id=\"web-applications\"></span>\n\nEste é o código no script de entrada para o [Template Básico de Projetos](start-installation.md).\n\n```php\n<?php\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n\n// registra o autoloader do Composer\nrequire __DIR__ . '/../vendor/autoload.php';\n\n// inclui o arquivo da classe Yii\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n\n// carrega a configuração da aplicação\n$config = require __DIR__ . '/../config/web.php';\n\n// cria, configura e executa a aplicação\n(new yii\\web\\Application($config))->run();\n```\n\n\n## Aplicações Console <span id=\"console-applications\"></span>\n\nDe forma semelhante, o seguinte é o código do script de entrada de uma aplicação\ndo console:\n\n```php\n#!/usr/bin/env php\n<?php\n/**\n * Yii console bootstrap file.\n *\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\n\n// registra o autoloader do Composer\nrequire __DIR__ . '/vendor/autoload.php';\n\n// inclui o arquivo da classe Yii\nrequire __DIR__ . '/vendor/yiisoft/yii2/Yii.php';\n\n// carrega a configuração da aplicação\n$config = require __DIR__ . '/config/console.php';\n\n$application = new yii\\console\\Application($config);\n$exitCode = $application->run();\nexit($exitCode);\n```\n\n\n## Definindo Constantes <span id=\"defining-constants\"></span>\n\nOs scrips de entrada são o melhor lugar para definir as constantes globais. O\nYii suporta as seguintes três constantes:\n\n* `YII_DEBUG`: especifica se a aplicação está rodando no modo de depuração. No\n  modo de depuração, uma aplicação manterá mais informações de log, e revelará\n  stacks de chamadas de erros detalhadas se forem lançadas exceções. Por este\n  motivo, o modo de depuração deveria ser usado principalmente durante o\n  desenvolvimento. O valor padrão de `YII_DEBUG` é `false`.\n* `YII_ENV`: especifica em qual ambiente a aplicação está rodando. Isso foi\n  descrito em maiores detalhes na seção [Configurações](concept-configurations.md#environment-constants).\n  O valor padrão de `YII_ENV` é `'prod'`, significando que a aplicação está\n  executando em ambiente de produção.\n* `YII_ENABLE_ERROR_HANDLER`: especifica se deve ativar o manipulador de erros\n  fornecido pelo Yii. O valor padrão desta constante é `true`.\n\nAo definir uma constante, frequentemente usamos código como o a seguir:\n\n```php\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\n```\n\nque é equivalente ao seguinte código:\n\n```php\nif (!defined('YII_DEBUG')) {\n    define('YII_DEBUG', true);\n}\n```\n\nClaramente o primeiro é mais sucinto e fácil de entender.\n\nA definição de constantes deveria ser feita logo no início de um script de entrada,\nde modo que obtenha efeito quando outros arquivos PHP estiverem sendo inclusos.\n"
  },
  {
    "path": "docs/guide-pt-BR/structure-extensions.md",
    "content": "Extensões \n=========\n\nAs extensões são pacotes de software redistribuíveis especialmente projetadas \npara serem usadas em aplicações Yii e fornecem recursos prontos para o uso. Por \nexemplo, a extensão [yiisoft/yii2-debug](tool-debugger.md) adiciona uma barra de \nferramentas de depuração na parte inferior de todas as páginas em sua aplicação \npara ajudar a compreender mais facilmente como as páginas são geradas. Você pode \nusar as extensões para acelerar o processo de desenvolvimento. Você também pode \nempacotar seus códigos como extensões para compartilhar com outras pessoas o seu \nbom trabalho.\n\n> Informação: Usamos o termo \"extensão\" para referenciar os pacotes de software \n  específicos do Yii. Para propósito geral, os pacotes de software que podem ser \n  usados sem o Yii, referenciamos sob o termo de \"pacote\" ou \"biblioteca\".\n\n\n## Usando Extensões <span id=\"using-extensions\"></span>\n\nPara usar uma extensão, você precisa instalá-lo primeiro. A maioria das extensões \nsão distribuídas como pacotes do [Composer](https://getcomposer.org/) que podem \nser instaladas seguindo dois passos:\n\n1. modifique o arquivo `composer.json` de sua aplicação e especifique quais \n   extensões (pacotes do Composer) você deseja instalar.\n2. execute `composer install` para instalar as extensões especificadas.\n\nObserve que você pode precisa instalar o [Composer](https://getcomposer.org/) \ncaso você não tenha feito isto antes.\n\nPor padrão, o Composer instala pacotes registados no [Packagist](https://packagist.org/) - \no maior repositório open source de pacotes do Composer. Você também pode [criar \no seu próprio repositório](https://getcomposer.org/doc/05-repositories.md#repository) \ne configurar o Composer para usá-lo. Isto é útil caso você desenvolva extensões \nprivadas que você deseja compartilhar apenas em seus projetos.\n\nAs extensões instaladas pelo Composer são armazenadas no diretório `BasePath/vendor`, \nonde o `BasePath` refere-se ao [caminho base](structure-applications.md#basePath) \nda aplicação. Como o Composer é um gerenciador de dependências, quando ele instala \num pacote, também instala todos os pacotes dependentes.\n\nPor exemplo, para instalar a extensão `yiisoft/yii2-imagine`, modifique seu \n`composer.json` conforme o seguinte exemplo:\n\n```json\n{\n    // ...\n\n    \"require\": {\n        // ... other dependencies\n\n        \"yiisoft/yii2-imagine\": \"~2.0.0\"\n    }\n}\n```\n\nDepois da instalação, você deve enxergar o diretório `yiisoft/yii2-imagine` sob \no diretório `BasePath/vendor`. Você também deve enxergar outro diretório \n`imagine/imagine` que contém os pacotes dependentes instalados. \n\n> Informação: O `yiisoft/yii2-imagine` é uma extensão nativa desenvolvida e mantida \n  pela equipe de desenvolvimento do Yii. Todas as extensões nativas estão hospedadas \n  no [Packagist](https://packagist.org/) e são nomeadas como `yiisoft/yii2-xyz`, \n  onde `xyz` varia para cada extensão.\n\nAgora, você pode usar as extensões instaladas como parte de sua aplicação. O \nexemplo a seguir mostra como você pode usar a classe `yii\\imagine\\Image` \nfornecido pela extensão `yiisoft/yii2-imagine`:\n\n```php\nuse Yii;\nuse yii\\imagine\\Image;\n\n// gera uma imagem thumbnail \nImage::thumbnail('@webroot/img/test-image.jpg', 120, 120)\n    ->save(Yii::getAlias('@runtime/thumb-test-image.jpg'), ['quality' => 50]);\n```\n\n> Informação: As classes de extensão são carregadas automaticamente pela \n  [classe autoloader do Yii](concept-autoloading.md).\n\n\n### Instalando Extensões Manualmente <span id=\"installing-extensions-manually\"></span>\n\nEm algumas raras ocasiões, você pode querer instalar algumas ou todas extensões \nmanualmente, ao invés de depender do Composer.\nPara fazer isto, você deve:\n\n1. fazer o download da extensão com os arquivos zipados e os dezipe no diretório `vendor`\n2. instalar as classes autoloaders fornecidas pela extensão, se houver.\n3. fazer o download e instalar todas as extensões dependentes que foi instruído.\n\nSe uma extensão não tiver uma classe autoloader seguindo a \n[norma PSR-4](https://www.php-fig.org/psr/psr-4/), você pode usar a classe \nautoloader fornecida pelo Yii para carregar automaticamente as classes de \nextensão. Tudo o que você precisa fazer é declarar uma \n[alias root](concept-aliases.md#defining-aliases) para o diretório root da \nextensão. Por exemplo, assumindo que você instalou uma extensão no diretório \n`vendor/mycompany/myext` e que a classe da extensão está sob o namespace `myext`, \nvocê pode incluir o código a seguir na configuração de sua aplicação:\n\n```php\n[\n    'aliases' => [\n        '@myext' => '@vendor/mycompany/myext',\n    ],\n]\n```\n\n\n## Criando Extensões <span id=\"creating-extensions\"></span>\n\nVocê pode considerar criar uma extensão quando você sentir a necessidade de \ncompartilhar o seu bom código para outras pessoas.\nUma extensão pode conter qualquer código que você deseja, tais como uma classe \nhelper, um widget, um módulo, etc.\n\nÉ recomendado que você crie uma extensão através do \n[pacote doComposer](https://getcomposer.org/) de modo que possa ser mais \nfacilmente instalado e usado por outros usuário, como descrito na última subseção.\n\nAbaixo estão as básicas etapas que você pode seguir para criar uma extensão como \num pacote do Composer.\n\n1. Crie uma projeto para sua extensão e guarde-o em um repositório CVS, como o \n   [github.com](https://github.com). O trabalho de desenvolvimento e de manutenção \n   deve ser feito neste repositório.\n2. Sob o diretório root do projeto, crie um arquivo chamado `composer.json` como \n   o requerido pelo Composer. Por favor, consulte a próxima subseção para mais \n   detalhes.\n3. Registre sua extensão no repositório do Composer, como o \n   [Packagist](https://packagist.org/), de modo que outros usuário possam achar \n   e instalar suas extensões usando o Composer.\n\n\n### `composer.json` <span id=\"composer-json\"></span>\n\nCada pacote do Composer deve ter um arquivo `composer.json` no diretório root. O \narquivo contém os metadados a respeito do pacote. Você pode achar a especificação \ncompleta sobre este arquivo no [Manual do Composer](https://getcomposer.org/doc/01-basic-usage.md#composer-json-project-setup).\nO exemplo a seguir mostra o arquivo `composer.json` para a extensão `yiisoft/yii2-imagine`:\n\n```json\n{\n    // nome do pacote\n    \"name\": \"yiisoft/yii2-imagine\",\n\n    // tipo de pacote\n    \"type\": \"yii2-extension\",\n\n    \"description\": \"The Imagine integration for the Yii framework\",\n    \"keywords\": [\"yii2\", \"imagine\", \"image\", \"helper\"],\n    \"license\": \"BSD-3-Clause\",\n    \"support\": {\n        \"issues\": \"https://github.com/yiisoft/yii2/issues?labels=ext%3Aimagine\",\n        \"forum\": \"https://forum.yiiframework.com/\",\n        \"wiki\": \"https://www.yiiframework.com/wiki/\",\n        \"irc\": \"ircs://irc.libera.chat:6697/yii\",\n        \"source\": \"https://github.com/yiisoft/yii2\"\n    },\n    \"authors\": [\n        {\n            \"name\": \"Antonio Ramirez\",\n            \"email\": \"amigo.cobos@gmail.com\"\n        }\n    ],\n\n    // dependências do pacote\n    \"require\": {\n        \"yiisoft/yii2\": \"~2.0.0\",\n        \"imagine/imagine\": \"v0.5.0\"\n    },\n\n    // especifica as classes autoloading \n    \"autoload\": {\n        \"psr-4\": {\n            \"yii\\\\imagine\\\\\": \"\"\n        }\n    }\n}\n```\n\n\n#### Nome do Pacote <span id=\"package-name\"></span>\n\nCada pacote do Composer deve ter um nome que identifica unicamente o pacote \nentre todos os outros. Os nomes dos pacotes devem seguir o formato \n`vendorName/projectName`. Por exemplo, no nome do pacote `yiisoft/yii2-imagine`, \no nome do vendor e o nome do projeto são `yiisoft` e `yii2-imagine`, \nrespectivamente.\n\nNÃO utilize `yiisoft` como nome do seu vendor já que ele é usado pelo Yii para \nos códigos nativos.\n\nRecomendamos que você use o prefixo `yii2-` para o nome do projeto dos pacotes \nde extensões em Yii 2, por exemplo, `myname/yii2-mywidget`. Isto permitirá que \nos usuários encontrem mais facilmente uma extensão Yii 2.\n\n\n#### Tipo de Pacote <span id=\"package-type\"></span>\n\nÉ importante que você especifique o tipo de pacote de sua extensão como \n`yii2-extension`, de modo que o pacote possa ser reconhecido como uma extensão \ndo Yii quando for instalado.\n\nQuando um usuário executar `composer install` para instalar uma extensão, o \narquivo `vendor/yiisoft/extensions.php` será atualizada automaticamente para \nincluir informações referentes a nova extensão. A partir deste arquivo, as \naplicações Yii podem saber quais extensões estão instaladas (a informação pode \nser acessada através da propriedade [[yii\\base\\Application::extensions]]).\n\n\n#### Dependências <span id=\"dependencies\"></span>\n\nSua extensão depende do Yii (claro!). Sendo assim, você deve listar (`yiisoft/yii2`) \nna entrada `require` do `composer.json`. Se sua extensão também depender de outras \nextensões ou de bibliotecas de terceiros, você deve lista-los também. Certifique-se \nde listar as constantes de versões apropriadas (por exemplo, `1.*`, `@stable`) \npara cada pacote dependente. Utilize dependências estáveis quando sua extensão \nestiver em uma versão estável.\n\nA maioria dos pacotes JavaScript/CSS são gerenciados pelo [Bower](https://bower.io/) \ne/ou pelo [NPM](https://www.npmjs.com/), ao invés do Composer. O Yii usa o \n[plugin de asset do Composer](https://github.com/fxpio/composer-asset-plugin) \npara habilitar a gerência destes tipos de pacotes através do Composer. Se sua \nextensão depender do pacote do Bower, você pode simplesmente listar a dependência \nno `composer.json` conforme o exemplo a seguir:\n\n```json\n{\n    // package dependencies\n    \"require\": {\n        \"bower-asset/jquery\": \">=1.11.*\"\n    }\n}\n```\n\nO código anterior indica que a extensão depende do pacote `jquery` do Bower. Em \ngeral, no `composer.json`, você pode usar o `bower-asset/PackageName` para \nreferenciar um pacote do Bower no `composer.json`, e usar o `npm-asset/PackageName` \npara referenciar um pacote do NPM, por padrão o conteúdo do pacote será instalado \nsob os diretórios `@vendor/bower/PackageName` e `@vendor/npm/Packages`, \nrespectivamente.\nEstes dois diretórios podem ser referenciados para usar alias mais curtas como \n`@bower/PackageName` e `@npm/PackageName`.\n\nPara mais detalhes sobre o gerenciamento de asset, por favor, consulte a seção \n[Assets](structure-assets.md#bower-npm-assets).\n\n#### Classe Autoloading <span id=\"class-autoloading\"></span>\n\nPara que suas classes sejam carregadas automaticamente pela classe autoloader do \nYii ou da classe autoloader do Composer, você deve especificar a entrada `autoload` \nno arquivo `composer.json`, conforme mostrado a seguir:\n\n```json\n{\n    // ....\n\n    \"autoload\": {\n        \"psr-4\": {\n            \"yii\\\\imagine\\\\\": \"\"\n        }\n    }\n}\n```\n\nVocê pode listar um ou vários namespaces e seus caminhos de arquivos correspondentes.\n\nQuando a extensão estiver instalada em uma aplicação, o Yii irá criar para cada \nnamespace listada uma [alias](concept-aliases.md#extension-aliases) que se \nreferenciará ao diretório correspondente ao namespace.\nPor exemplo, a declaração acima do `autoload` corresponderá a uma alias chamada \n`@yii/imagine`.\n\n\n### Práticas Recomendadas <span id=\"recommended-practices\"></span>\n\nComo as extensões são destinadas a serem usadas por outras pessoas, você precisará, \npor muitas vezes, fazer um esforço extra durante o desenvolvimento. A seguir, \napresentaremos algumas práticas comuns e recomendadas na criação de extensões de \nalta qualidade.\n\n\n#### Namespaces <span id=\"namespaces\"></span>\n\nPara evitar conflitos de nomes e criar classes autocarregáveis em sua extensão, \nvocê deve usar namespaces e nomear as classes seguindo o \n[padrão PSR-4](https://www.php-fig.org/psr/psr-4/) ou o \n[padrão PSR-0](https://www.php-fig.org/psr/psr-0/).\n\nSeus namespaces de classes devem iniciar com `vendorName\\extensionName`, onde a \n`extensionName` é semelhante ao nome da extensão, exceto que ele não deve conter \no prefixo `yii2-`. Por exemplo, para a extensão `yiisoft/yii2-imagine`, usamos o \n`yii\\imagine` como namespace para suas classes.\n\nNão use `yii`, `yii2` ou `yiisoft` como nome do seu vendor. Estes nomes são \nreservados para serem usados para o código nativo do Yii.\n\n\n#### Inicialização das Classes <span id=\"bootstrapping-classes\"></span>\n\nAs vezes, você pode querer que sua extensão execute algum código durante o \n[processo de inicialização](runtime-bootstrapping.md) de uma aplicação. Por \nexemplo, a sua extensão pode querer responder ao evento `beginRequest` da \naplicação para ajustar alguma configuração do ambiente. Embora você possa \ninstruir os usuários que usam a extensão para associar explicitamente a sua \nfunção ao evento `beginRequest`, a melhor maneira é fazer isso é automaticamente.\n\nPara atingir este objetivo, você pode criar uma *classe de inicialização* \nimplementando o [[yii\\base\\BootstrapInterface]].\nPor exemplo,\n\n```php\nnamespace myname\\mywidget;\n\nuse yii\\base\\BootstrapInterface;\nuse yii\\base\\Application;\n\nclass MyBootstrapClass implements BootstrapInterface\n{\n    public function bootstrap($app)\n    {\n        $app->on(Application::EVENT_BEFORE_REQUEST, function () {\n             // fazer alguma coisa aqui\n        });\n    }\n}\n```\n\nEm seguida, liste esta classe no arquivo `composer.json` de sua extensão conforme \no seguinte,\n\n```json\n{\n    // ...\n\n    \"extra\": {\n        \"bootstrap\": \"myname\\\\mywidget\\\\MyBootstrapClass\"\n    }\n}\n```\n\nQuando a extensão for instalada em uma aplicação, o Yii instanciará \nautomaticamente a classe de inicialização e chamará o método \n[[yii\\base\\BootstrapInterface::bootstrap()|bootstrap()]] durante o processo de \ninicialização para cada requisição.\n\n\n#### Trabalhando com Banco de Dados <span id=\"working-with-databases\"></span>\n\nSua extensão pode precisar acessar banco de dados. Não pressupunha que as \naplicações que usam sua extensão SEMPRE usam o `Yii::$db` como a conexão do \nbanco de dados. Em vez disso, você deve declarar a propriedade `db` para as \nclasses que necessitam acessar o banco de dados.\nA propriedade permitirá que os usuários de sua extensão personalizem quaisquer \nconexão de banco de dados que gostariam de usar.\nComo exemplo, você pode consultar a classe [[yii\\caching\\DbCache]] e ver como \ndeclara e usa a propriedade `db`.\n\nSe sua extensão precisar criar uma tabela específica no banco de dados ou fazer \nalterações no esquema do banco de dados, você deve: \n\n- fornecer [migrations](db-migrations.md) para manipular o esquema do banco de \n  dados, ao invés de usar arquivos simples de SQL;\n- tentar criar migrations aplicáveis em diferentes SGDB;\n- evitar o uso de [Active Record](db-active-record.md) nas migrations.\n\n\n#### Usando Assets <span id=\"using-assets\"></span>\n\nSe sua extensão usar um widget ou um módulo, pode ter grandes chances de requerer \nalgum [assets](structure-assets.md) para funcionar.\nPor exemplo, um módulo pode exibir algumas páginas que contém imagens, JavaScript \ne CSS. Como os arquivos de uma extensão estão todos sob o diretório que não é \nacessível pela Web quando instalado em uma aplicação, você tem duas escolhas \npara tornar estes arquivos de asset diretamente acessíveis pela Web:\n\n- informe aos usuários da extensão copiar manualmente os arquivos de asset para \n  uma pasta determinada acessível pela Web;\n- declare um [asset bundle](structure-assets.md) e conte com o mecanismo de \n  publicação de asset para copiar automaticamente os arquivos listados no asset \n  bundle para uma pasta acessível pela Web.\n\nRecomendamos que você use a segunda abordagem de modo que sua extensão possa ser \nusada com mais facilidade pelos usuários. Por favor, consulte a seção \n[Assets](structure-assets.md) para mais detalhes sobre como trabalhar com assets \nem geral.\n\n\n#### Internacionalização e Localização <span id=\"i18n-l10n\"></span>\n\nSua extensão pode ser usada por aplicações que suportam diferentes idiomas! \nPortanto, se sua extensão exibir conteúdo para os usuários finais, você deve \ntentar usar [internacionalização e localização](tutorial-i18n.md). Em particular,\n\n- Se a extensão exibir mensagens aos usuários finais, as mensagens devem usadas \n  por meio do método `Yii::t()` de modo que eles possam ser traduzidas. As \n  mensagens voltadas para os desenvolvedores (como mensagens internas de exceções) \n  não precisam ser traduzidas.\n- Se a extensão exibir números, datas, etc., devem ser formatadas usando a classe \n  [[yii\\i18n\\Formatter]] com as regras de formatação apropriadas. \n\nPara mais detalhes, por favor, consulte a seção [Internacionalização](tutorial-i18n.md).\n\n\n#### Testes <span id=\"testing\"></span>\n\nVocê quer que sua extensão execute com perfeição sem trazer problemas para outras \npessoas. Para alcançar este objetivo, você deve testar sua extensão antes de \nliberá-lo ao público.\n\nÉ recomendado que você crie várias unidades de testes para realizar simulações \nno código de sua extensão ao invés de depender de testes manuais.\nToda vez que liberar uma nova versão de sua extensão, você pode simplesmente \nrodar as unidades de teste para garantir que tudo esteja em boas condições. O \nYii fornece suporte para testes, que podem ajuda-los a escrever mais facilmente \ntestes unitários, testes de aceitação e testes funcionais. Para mais detalhes, \npor favor, consulte a seção [Testing](test-overview.md).\n\n\n#### Versionamento <span id=\"versioning\"></span>\n\nVocê deve dar para cada liberação de sua extensão um numero de versão (por exemplo, \n`1.0.1`). Recomendamos que você siga a prática [versionamento semântico](https://semver.org) \nao determinar qual número de versão será usado.\n\n\n#### Liberando Versões <span id=\"releasing\"></span>\n\nPara que outras pessoas saibam sobre sua extensão, você deve liberá-lo ao público.\n\nSe é a primeira vez que você está liberando uma extensão, você deve registrá-lo \nno repositório do Composer, como o [Packagist](https://packagist.org/). Depois \ndisso, tudo o que você precisa fazer é simplesmente criar uma tag de liberação \n(por exemplo, `v1.0.1`) no repositório CVS de sua extensão e notificar o \nrepositório do Composer sobre a nova liberação. As pessoas, então, serão capazes \nde encontrar a nova versão e instalá-lo ou atualizá-lo através do repositório do \nComposer.\n\nAs versões de sua extensão, além dos arquivos de códigos, você deve também \nconsiderar a inclusão de roteiros para ajudar as outras pessoas aprenderem a usar \na sua extensão:\n\n* Um arquivo readme no diretório root do pacote: descreve o que sua extensão faz \n  e como faz para instalá-lo e usá-lo. Recomendamos que você escreva no formato \n  [Markdown](https://daringfireball.net/projects/markdown/) e o nome do arquivo \n  como `readme.md`.\n* Um arquivo changelog no diretório root do pacote: lista quais mudanças foram \n  feitas em cada versão. O arquivo pode ser escrito no formato Markdown e \n  nomeado como `changelog.md`.\n* Uma arquivo de atualização no diretório root do pacote: fornece as instruções \n  de como atualizar a extensão a partir de versões antigas. O arquivo deve ser \n  escrito no formato Markdown e nomeado como `upgrade.md`.\n* Tutoriais, demos, screenshots, etc.: estes são necessários se sua extensão \n  fornece muitos recursos que podem não ser totalmente cobertos no arquivo readme.\n* Documentação da API: seu código deve ser bem documentado para permitir que \n  outros usuários possam ler e entender mais facilmente.\n  Você pode consultar o [arquivo da classe BaseObject](https://github.com/yiisoft/yii2/blob/master/framework/base/BaseObject.php) \n  para aprender como documentar o seu código.\n\n> Informação: Os seus comentários no código podem ser escritos no formato Markdown. \n  A extensão `yiisoft/yii2-apidoc` fornece uma ferramenta para gerar uma documentação \n  da API com base nos seus comentários.\n\n> Informação: Embora não seja um requisito, sugerimos que sua extensão se conforme \n  a determinados estilos de codificação. Você pode consultar o \n  [estilo de codificação do framework](https://github.com/yiisoft/yii2/blob/master/docs/internals/core-code-style.md).\n\n\n## Extensões Nativas <span id=\"core-extensions\"></span>\n\nO Yii fornece as seguintes extensões que são desenvolvidas e mantidas pela equipe \nde desenvolvimento do Yii. Todos são registrados no [Packagist](https://packagist.org/) \ne podem ser facilmente instalados como descrito na subseção [Usando Extensões](#using-extensions).\n\n- [yiisoft/yii2-apidoc](https://github.com/yiisoft/yii2-apidoc):\n  fornece um gerador de API de documentação extensível e de alto desempenho. \n  Também é usado para gerar a API de documentação do framework.\n- [yiisoft/yii2-authclient](https://github.com/yiisoft/yii2-authclient):\n  fornece um conjunto comum de autenticadores de clientes, como Facebook OAuth2 \n  client, GitHub OAuth2 client.\n- [yiisoft/yii2-bootstrap](https://github.com/yiisoft/yii2-bootstrap):\n  fornece um conjunto de widgets que encapsulam os componentes e plug-ins do \n  [Bootstrap](https://getbootstrap.com/).\n- [yiisoft/yii2-debug](https://github.com/yiisoft/yii2-debug):\n  fornece suporte a depuração para aplicações Yii. Quando esta extensão é usada, \n  uma barra de ferramenta de depuração aparecerá na parte inferior de cada página. \n  A extensão também fornece um conjunto de páginas independentes para exibir mais \n  detalhes das informações de depuração.\n- [yiisoft/yii2-elasticsearch](https://github.com/yiisoft/yii2-elasticsearch):\n  fornece suporte para o uso de [Elasticsearch](https://www.elastic.co/). \n  Este inclui suporte a consultas/pesquisas básicas e também implementa o padrão \n  [Active Record](db-active-record.md) que permite que você armazene os active \n  records no Elasticsearch.\n- [yiisoft/yii2-faker](https://github.com/yiisoft/yii2-faker):\n  fornece suporte para o uso de [Faker](https://github.com/fzaninotto/Faker) para \n  gerar dados falsos para você.\n- [yiisoft/yii2-gii](https://github.com/yiisoft/yii2-gii):\n  fornece um gerador de código baseado na Web que é altamente extensível e pode \n  ser usado para gerar rapidamente models (modelos), formulários, módulos, CRUD, etc.\n- [yiisoft/yii2-httpclient](https://github.com/yiisoft/yii2-httpclient):\n  provides an HTTP client.\n- [yiisoft/yii2-imagine](https://github.com/yiisoft/yii2-imagine):\n  fornece funções de manipulação de imagens comumente utilizados com base no \n  [Imagine](https://imagine.readthedocs.org/).\n- [yiisoft/yii2-jui](https://github.com/yiisoft/yii2-jui):\n  fornece um conjunto de widgets que encapsulam as interações e widgets do \n  [JQuery UI](https://jqueryui.com/).\n- [yiisoft/yii2-mongodb](https://github.com/yiisoft/yii2-mongodb):\n  fornece suporte para o uso do [MongoDB](https://www.mongodb.com/). Este inclui \n  recursos como consultas básicas, Active Record, migrations, cache, geração de \n  códigos, etc.\n- [yiisoft/yii2-redis](https://github.com/yiisoft/yii2-redis):\n  fornece suporte para o uso do [redis](https://redis.io/). Este inclui recursos \n  como consultas básicas, Active Record, cache, etc.\n- [yiisoft/yii2-smarty](https://github.com/yiisoft/yii2-smarty):\n  fornece um motor de template baseado no [Smarty](https://www.smarty.net/).\n- [yiisoft/yii2-sphinx](https://github.com/yiisoft/yii2-sphinx):\n  fornece suporte para o uso do [Sphinx](https://sphinxsearch.com). Este inclui \n  recursos como consultas básicas, Active Record, geração de códigos, etc.\n- [yiisoft/yii2-swiftmailer](https://github.com/yiisoft/yii2-swiftmailer):\n  fornece recursos para envio de e-mails baseados no [swiftmailer](https://swiftmailer.org/).\n- [yiisoft/yii2-twig](https://github.com/yiisoft/yii2-twig):\n  fornece um motor de template baseado no [Twig](https://twig.symfony.com/).\n"
  },
  {
    "path": "docs/guide-pt-BR/structure-filters.md",
    "content": "Filtros\n=======\n\nOs filtros são objetos que são executados antes e/ou depois das \n[ações do controller (controlador)](structure-controllers.md#actions). Por exemplo, \num filtro de controle de acesso pode ser executado antes das ações para garantir \nque um determinado usuário final tenha autorização de acessá-lo; um filtro de \ncompressão de conteúdo pode ser executado depois das ações para comprimir o \nconteúdo da resposta antes de enviá-los aos usuários finais.\n\nUm filtro pode ser composto por um pré-filtro (lógicas de filtragem que são \naplicadas *antes* que as ações) e/ou um pós-filtro (lógica aplicada *depois* \nque as ações).\n\n\n## Usando os Filtros <span id=\"using-filters\"></span>\n\nOs filtros são, essencialmente, um tipo especial de \n[behaviors (comportamento)](concept-behaviors.md). No entanto, o uso dos filtros \né igual ao [uso dos behaviors](concept-behaviors.md#attaching-behaviors). Você \npode declarar os filtros em uma classe controller (controlador) sobrescrevendo o \nmétodo [[yii\\base\\Controller::behaviors()|behaviors()]] conforme o exemplo a seguir:\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\HttpCache',\n            'only' => ['index', 'view'],\n            'lastModified' => function ($action, $params) {\n                $q = new \\yii\\db\\Query();\n                return $q->from('user')->max('updated_at');\n            },\n        ],\n    ];\n}\n```\n\nPor padrão, os filtros declarados em uma classe controller (controlador) serão \naplicados em *todas* as ações deste controller (controlador). Você pode, no \nentanto, especificar explicitamente em quais ações os filtros serão aplicados \npela configuração da propriedade [[yii\\base\\ActionFilter::only|only]]. No exemplo \nanterior, o filtro `HttpCache` só se aplica às ações `index` e `view`. Você também \npode configurar a propriedade [[yii\\base\\ActionFilter::except|except]] para montar \num blacklist, a fim de  barrar algumas ações que estão sendo filtradas.\n\nAlém dos controllers (controladores), você também poderá declarar filtros em um \n[módulo](structure-modules.md) ou na [aplicação](structure-applications.md).\nQuando você faz isso, os filtros serão aplicados em *todos* as ações do controller \n(controlador) que pertençam a esse módulo ou a essa aplicação, a menos que você \nconfigure as propriedades [[yii\\base\\ActionFilter::only|only]] e \n[[yii\\base\\ActionFilter::except|except]] do filtro conforme descrito anteriormente.\n\n> Observação: Ao declarar os filtros em módulos ou em aplicações, você deve usar \n[rotas](structure-controllers.md#routes) ao invés de IDs das ações nas propriedades \n[[yii\\base\\ActionFilter::only|only]] e [[yii\\base\\ActionFilter::except|except]]. \nIsto porque os IDs das ações não podem, por si só, especificar totalmente as ações \nno escopo de um módulo ou de uma aplicação.\n\nQuando muitos filtros são configurados para uma única ação, devem ser aplicados \nde acordo com as seguintes regras:\n\n* Pré-filtragem:\n    - Aplica os filtros declarados na aplicação na ordem que foram listados no método `behaviors()`.\n    - Aplica os filtros declarados no módulo na ordem que foram listados no método `behaviors()`.\n    - Aplica os filtros declarados no controller (controlador) na ordem que foram listados no método `behaviors()`.\n    - Se qualquer um dos filtros cancelarem a execução da ação, os filtros (tanto os pré-filtros quanto os pós-filtros) subsequentes não serão aplicados.\n* Executa a ação se passar pela pré-filtragem.\n* Pós-filtragem\n    - Aplica os filtros declarados no controller (controlador) na ordem inversa ao que foram listados no método `behaviors()`.\n    - Aplica os filtros declarados nos módulos na ordem inversa ao que foram listados no método `behaviors()`.\n    - Aplica os filtros declarados na aplicação na ordem inversa ao que foram listados no método `behaviors()`.\n\n\n## Criando Filtros <span id=\"creating-filters\"></span>\n\nPara criar um novo filtro de ação, deve estender a classe [[yii\\base\\ActionFilter]] \ne sobrescrever os métodos [[yii\\base\\ActionFilter::beforeAction()|beforeAction()]] \ne/ou [[yii\\base\\ActionFilter::afterAction()|afterAction()]]. O primeiro método \nserá executado antes que uma ação seja executada enquanto o outro método será \nexecutado após uma ação seja executada. \nO valor de retorno no método [[yii\\base\\ActionFilter::beforeAction()|beforeAction()]] \ndetermina se uma ação deve ser executada ou não. Se retornar `false`, os filtros \nsubsequentes serão ignorados e a ação não será executada.\n\nO exemplo a seguir mostra um filtro que guarda o log do tempo de execução das ações:\n\n```php\nnamespace app\\components;\n\nuse Yii;\nuse yii\\base\\ActionFilter;\n\nclass ActionTimeFilter extends ActionFilter\n{\n    private $_startTime;\n\n    public function beforeAction($action)\n    {\n        $this->_startTime = microtime(true);\n        return parent::beforeAction($action);\n    }\n\n    public function afterAction($action, $result)\n    {\n        $time = microtime(true) - $this->_startTime;\n        Yii::debug(\"Action '{$action->uniqueId}' spent $time second.\");\n        return parent::afterAction($action, $result);\n    }\n}\n```\n\n\n## Filtros Nativos <span id=\"core-filters\"></span>\n\nO Yii fornece um conjunto de filtros que normalmente são usados, localizados sob \no namespace `yii\\filters`. A seguir, iremos realizar uma breve apresentação \ndestes filtros.\n\n\n### [[yii\\filters\\AccessControl|AccessControl]] <span id=\"access-control\"></span>\n\nO filtro AccessControl fornece um controle de acesso simples, baseado em um \nconjunto de [[yii\\filters\\AccessControl::rules|regras]].\nEm particular, antes que uma ação seja executada, o AccessControl analisará as \nregras listadas e localizará o primeiro que corresponda às variáveis do contexto \natual (como o IP do usuário, o status do login, etc). A regra correspondente \ndeterminará se vai permitir ou não a execução da ação solicitada. Se nenhuma \nregra for localizada, o acesso será negado.\n\nO exemplo a seguir mostra como faz para permitir aos usuários autenticados \nacessarem as ações `create` e `update` enquanto todos os outros não autenticados \nnão consigam acessá-las. \n\n```php\nuse yii\\filters\\AccessControl;\n\npublic function behaviors()\n{\n    return [\n        'access' => [\n            'class' => AccessControl::class,\n            'only' => ['create', 'update'],\n            'rules' => [\n                // permite aos usuários autenticados\n                [\n                    'allow' => true,\n                    'roles' => ['@'],\n                ],\n                // todos os outros usuários são negados por padrão \n            ],\n        ],\n    ];\n}\n```\n\nDe modo geral, para mais detalhes sobre o controle de acesso, por favor, consulte \na seção [Autorização](security-authorization.md).\n\n\n### Métodos de Autenticação por Filtros <span id=\"auth-method-filters\"></span>\n\nO método de autenticação por filtros são usados para autenticar um usuário usando \nvários métodos, tais como \n[HTTP Basic Auth](https://en.wikipedia.org/wiki/Basic_access_authentication), \n[OAuth 2](https://oauth.net/2/). Todas estas classes de filtros estão localizadas \nsob o namespace `yii\\filters\\auth`.\n\nO exemplo a seguir mostra como você pode usar o filtro \n[[yii\\filters\\auth\\HttpBasicAuth]] para autenticar um usuário usando um acesso \nbaseado em token pelo método HTTP Basic Auth. Observe que, para isto funcionar, \nsua [[yii\\web\\User::identityClass|classe de identidade do usuário]] deve \nimplementar o método [[yii\\web\\IdentityInterface::findIdentityByAccessToken()|findIdentityByAccessToken()]].\n\n```php\nuse yii\\filters\\auth\\HttpBasicAuth;\n\npublic function behaviors()\n{\n    return [\n        'basicAuth' => [\n            'class' => HttpBasicAuth::class,\n        ],\n    ];\n}\n```\n\nOs métodos de autenticação por filtros geralmente são utilizados na implementação \nde APIs RESTful. Para mais detalhes, por favor, consulte a seção RESTful \n[Autenticação](rest-authentication.md).\n\n\n### [[yii\\filters\\ContentNegotiator|ContentNegotiator]] <span id=\"content-negotiator\"></span>\n\nO filtro ContentNegotiator suporta a identificação de formatos de respostas e o \nidioma da aplicação. Este filtro tentar determinar o formato de resposta e o \nidioma analisando os parâmetros `GET` e o `Accept` do cabeçalho HTTP.\n\nNo exemplo a seguir, o ContentNegotiator está sendo configurado para suportar os \nformatos de resposta JSON e XML, e os idiomas Inglês (Estados Unidos) e Alemão.\n\n```php\nuse yii\\filters\\ContentNegotiator;\nuse yii\\web\\Response;\n\npublic function behaviors()\n{\n    return [\n        [\n            'class' => ContentNegotiator::class,\n            'formats' => [\n                'application/json' => Response::FORMAT_JSON,\n                'application/xml' => Response::FORMAT_XML,\n            ],\n            'languages' => [\n                'en-US',\n                'de',\n            ],\n        ],\n    ];\n}\n```\n\nOs formatos de resposta e os idiomas muitas vezes precisam ser determinados muito \nmais cedo no [ciclo de vida da aplicação](structure-applications.md#application-lifecycle). \nPor este motivo, o ContentNegotiator foi projetado para ser usado de outras formas, \nonde pode ser usado tanto como um \n[componente de inicialização](structure-applications.md#bootstrap) quanto um filtro. \nPor exemplo, você pode configura-lo na \n[configuração da aplicação](structure-applications.md#application-configurations) \nconforme o exemplo a seguir:\n\n```php\nuse yii\\filters\\ContentNegotiator;\nuse yii\\web\\Response;\n\n[\n    'bootstrap' => [\n        [\n            'class' => ContentNegotiator::class,\n            'formats' => [\n                'application/json' => Response::FORMAT_JSON,\n                'application/xml' => Response::FORMAT_XML,\n            ],\n            'languages' => [\n                'en-US',\n                'de',\n            ],\n        ],\n    ],\n];\n```\n\n> Informação: Nos casos do formato de resposta e do idioma não serem determinados \n  pela requisição, o primeiro formato e idioma listados em [[formats]] e \n  [[languages]] serão utilizados.\n\n\n### [[yii\\filters\\HttpCache|HttpCache]] <span id=\"http-cache\"></span>\n\nO filtro HttpCache implementa no lado do cliente (client-side) o cache pela \nutilização dos parâmetros `Last-Modified` e `Etag` do cabeçalho HTTP.\nPor exemplo,\n\n```php\nuse yii\\filters\\HttpCache;\n\npublic function behaviors()\n{\n    return [\n        [\n            'class' => HttpCache::class,\n            'only' => ['index'],\n            'lastModified' => function ($action, $params) {\n                $q = new \\yii\\db\\Query();\n                return $q->from('user')->max('updated_at');\n            },\n        ],\n    ];\n}\n```\n\nPor favor, consulte a seção [Cache HTTP](caching-http.md) para mais detalhes \nsobre o uso do HttpCache.\n\n\n### [[yii\\filters\\PageCache|PageCache]] <span id=\"page-cache\"></span>\n\nO filtro PageCache implementa no lado do servidor (server-side) o cache das \npáginas. No exemplo a seguir, o PageCache é aplicado para a ação `index` guardar \no cache da página inteira por no máximo 60 segundos ou até que a quantidade de \nregistros na tabela `post` seja alterada. Este filtro também guarda diferentes \nversões da página, dependendo do idioma da aplicação escolhido.\n\n```php\nuse yii\\filters\\PageCache;\nuse yii\\caching\\DbDependency;\n\npublic function behaviors()\n{\n    return [\n        'pageCache' => [\n            'class' => PageCache::class,\n            'only' => ['index'],\n            'duration' => 60,\n            'dependency' => [\n                'class' => DbDependency::class,\n                'sql' => 'SELECT COUNT(*) FROM post',\n            ],\n            'variations' => [\n                \\Yii::$app->language,\n            ]\n        ],\n    ];\n}\n```\n\nPor favor, consulte a seção [Cache de Página](caching-page.md) para mais \ndetalhes sobre o uso do PageCache.\n\n\n### [[yii\\filters\\RateLimiter|RateLimiter]] <span id=\"rate-limiter\"></span>\n\nO filtro RateLimiter implementa um limitador de acesso baseado no \n[algoritmo do balde furado (leaky bucket)](https://pt.wikipedia.org/wiki/Leaky_Bucket).\nÉ usado principalmente na implementação de APIs RESTful. Por favor, consulte a \nseção [Limitador de Acesso](rest-rate-limiting.md) para mais detalhes sobre o \nuso deste filtro.\n\n\n### [[yii\\filters\\VerbFilter|VerbFilter]] <span id=\"verb-filter\"></span>\n\nO filtro VerbFilter verifica se os métodos de requisição HTTP são permitidos para \nas ações solicitadas. Se não for, será lançada uma exceção HTTP 405. No exemplo \na seguir, o VerbFilter é declarado para especificar um conjunto de métodos de \nrequisição permitidos para as ações CRUD.\n\n```php\nuse yii\\filters\\VerbFilter;\n\npublic function behaviors()\n{\n    return [\n        'verbs' => [\n            'class' => VerbFilter::class,\n            'actions' => [\n                'index'  => ['get'],\n                'view'   => ['get'],\n                'create' => ['get', 'post'],\n                'update' => ['get', 'put', 'post'],\n                'delete' => ['post', 'delete'],\n            ],\n        ],\n    ];\n}\n```\n\n### [[yii\\filters\\Cors|Cors]] <span id=\"cors\"></span>\n\nO compartilhamento de recursos cross-origin \n[CORS](https://developer.mozilla.org/pt-BR/docs/Web/HTTP/CORS) é um \nmecanismo que permite vários recursos (por exemplo, fontes, JavaScript, etc) \nna página Web sejam solicitados por outros domínios. Em particular, as chamadas \nAJAX do JavaScript podem usar o mecanismo XMLHttpRequest. Estas chamadas \n\"cross-domain\" são proibidas pelos navegadores Web, por desrespeitarem a \npolitica de segurança de origem. \nO CORS define um modo em que o navegador e o servidor possam interagir para \ndeterminar se deve ou não permitir as requisições cross-origin.\n\nO [[yii\\filters\\Cors|filtro Cors]] deve ser definido antes dos filtros de \nAutenticação/Autorização para garantir que os cabeçalhos CORS sejam sempre \nenviados.\n\n```php\nuse yii\\filters\\Cors;\nuse yii\\helpers\\ArrayHelper;\n\npublic function behaviors()\n{\n    return ArrayHelper::merge([\n        [\n            'class' => Cors::class,\n        ],\n    ], parent::behaviors());\n}\n```\n\nA filtragem da classe Cors pode ser ajustado pela propriedade `cors`.\n\n* `cors['Origin']`: array usado para definir as origens permitidas. Pode ser \n  `['*']` (qualquer um) ou `['https://www.myserver.net', 'https://www.myotherserver.com']`.\n  O padrão é `['*']`.\n* `cors['Access-Control-Request-Method']`: array com os métodos de requisição \n  permitidos, tais como `['GET', 'OPTIONS', 'HEAD']`. O padrão é \n  `['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']`.\n* `cors['Access-Control-Request-Headers']`: array com os cabeçalhos permitidos. \n  Pode ser `['*']` para todos os cabeçalhos ou um especifico como `['X-Request-With']`. \n  O padrão é `['*']`.\n* `cors['Access-Control-Allow-Credentials']`: define se a requisição atual pode  \n  ser feita usando credenciais. Pode ser `true`, `false` ou `null` (não definida). \n  O padrão é `null`.\n* `cors['Access-Control-Max-Age']`: define o tempo de vida do pré-processamento \n  (pre-flight) da requisição. O padrão é `86400`.\n\nPor exemplo, permitindo CORS para a origem: `https://www.myserver.net` com os \nmétodos `GET`, `HEAD` e `OPTIONS`:\n\n```php\nuse yii\\filters\\Cors;\nuse yii\\helpers\\ArrayHelper;\n\npublic function behaviors()\n{\n    return ArrayHelper::merge([\n        [\n            'class' => Cors::class,\n            'cors' => [\n                'Origin' => ['https://www.myserver.net'],\n                'Access-Control-Request-Method' => ['GET', 'HEAD', 'OPTIONS'],\n            ],\n        ],\n    ], parent::behaviors());\n}\n```\n\nVocê pode ajustar os cabeçalhos do CORS sobrescrevendo os parâmetros padrão para \ncada ação. Por exemplo, para adicionar o parâmetro `Access-Control-Allow-Credentials` \nsomente na ação `login`, você poderia fazer conforme a seguir:\n\n```php\nuse yii\\filters\\Cors;\nuse yii\\helpers\\ArrayHelper;\n\npublic function behaviors()\n{\n    return ArrayHelper::merge([\n        [\n            'class' => Cors::class,\n            'cors' => [\n                'Origin' => ['https://www.myserver.net'],\n                'Access-Control-Request-Method' => ['GET', 'HEAD', 'OPTIONS'],\n            ],\n            'actions' => [\n                'login' => [\n                    'Access-Control-Allow-Credentials' => true,\n                ]\n            ]\n        ],\n    ], parent::behaviors());\n}\n```\n"
  },
  {
    "path": "docs/guide-pt-BR/structure-models.md",
    "content": "Models (Modelos)\n================\n\nOs models (modelos) fazem parte da arquitetura [MVC](https://pt.wikipedia.org/wiki/MVC).\nEles representam os dados, as regras e a lógica de negócio.\n\nVocê pode criar uma classe model estendendo de [[yii\\base\\Model]] ou de seus filhos.\nA classe base [[yii\\base\\Model]] suporta muitos recursos úteis:\n\n* [Atributos](#attributes): representa os dados de negócio e podem ser acessados \n  normalmente como uma propriedade de objeto ou como um elemento de array;\n* [Labels dos atributos](#attribute-labels): especifica os labels de exibição dos \n  atributos;\n* [Atribuição em massa](#massive-assignment): suporta popular vários atributos em \n  uma única etapa;\n* [Regras de validação](#validation-rules): garante que os dados de entrada sejam \n  baseadas nas regras de validação que foram declaradas;\n* [Data Exporting](#data-exporting): permite que os dados de model a serem exportados \n  em array possuam formatos personalizados.\n\nA classe `Model` também é a classe base para models mais avançados, como o [Active Record](db-active-record.md).\nPor favor, consulte a documentação relevante para mais detalhes sobre estes models mais avançados.\n\n> Informação: Você não é obrigado basear suas classe model em [[yii\\base\\Model]]. \n> No entanto, por existir muitos componentes do Yii construídos para suportar o \n> [[yii\\base\\Model]], normalmente é a classe base preferível para um model.\n\n\n## Atributos <span id=\"attributes\"></span>\n\nOs models representam dados de negócio por meio de *atributos*. Cada atributo é \numa propriedade publicamente acessível de um model. O método [[yii\\base\\Model::attributes()]] \nespecifica quais atributos de uma classe model possuirá.\n\nVocê pode acessar um atributo como fosse uma propriedade normal de um objeto:\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// \"name\" é um atributo de ContactForm\n$model->name = 'example';\necho $model->name;\n```\n\nVocê também pode acessar os atributos como elementos de um array, graças ao suporte \nde [ArrayAccess](https://www.php.net/manual/en/class.arrayaccess.php) e \n[ArrayIterator](https://www.php.net/manual/en/class.arrayiterator.php) pelo \n[[yii\\base\\Model]]:\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// acessando atributos como elementos de array\n$model['name'] = 'example';\necho $model['name'];\n\n// iterando sobre os atributos\nforeach ($model as $name => $value) {\n    echo \"$name: $value\\n\";\n}\n```\n\n\n### Definindo Atributos <span id=\"defining-attributes\"></span>\n\nPor padrão, se a classe model estender diretamente de [[yii\\base\\Model]], todas \nas suas variáveis públicas e não estáticas serão atributos. Por exemplo, a classe \nmodel `ContactForm` a seguir possui quatro atributos: `name`, `email`, `subject` \ne `body`. O model `ContactForm` é usado para representar os dados de entrada obtidos \na partir de um formulário HTML.\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\n\nclass ContactForm extends Model\n{\n    public $name;\n    public $email;\n    public $subject;\n    public $body;\n}\n```\n\n\nVocê pode sobrescrever o método [[yii\\base\\Model::attributes()]] para definir \natributos de uma forma diferente. Este método deve retornar os nomes dos atributos \nem um model. Por exemplo, o [[yii\\db\\ActiveRecord]] faz com que o método retorne \nos nomes das colunas da tabela do banco de dados como nomes de atributos.\nObserve que também poderá sobrescrever os métodos mágicos tais como `__get()` e \n`__set()`, para que os atributos poderem ser acessados como propriedades normais \nde objetos.\n\n\n### Labels dos Atributos <span id=\"attribute-labels\"></span>\n\nAo exibir valores ou obter dados de entrada dos atributos, muitas vezes é necessário \nexibir alguns labels associados aos atributos. Por exemplo, dado um atributo chamado \n`firstName`, você pode querer exibir um label `First Name` que é mais amigável \nquando exibido aos usuários finais como em formulários e mensagens de erro.\n\nVocê pode obter o label de um atributo chamando o método [[yii\\base\\Model::getAttributeLabel()]]. \nPor exemplo,\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// displays \"Name\"\necho $model->getAttributeLabel('name');\n```\n\nPor padrão, os labels dos atributos automaticamente serão gerados com os nomes dos \natributos. Isto é feito pelo método [[yii\\base\\Model::generateAttributeLabel()]]. \nEle transforma os nomes camel-case das variáveis em várias palavras, colocando em \ncaixa alta a primeira letra de cada palavra. Por exemplo, `username` torna-se \n`Username`, enquanto `firstName` torna-se `First Name`.\n\nSe você não quiser usar esta geração automática do labels, poderá sobrescrever o \nmétodo [[yii\\base\\Model::attributeLabels()]] declarando explicitamente os labels \ndos atributos. Por exemplo,\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\n\nclass ContactForm extends Model\n{\n    public $name;\n    public $email;\n    public $subject;\n    public $body;\n\n    public function attributeLabels()\n    {\n        return [\n            'name' => 'Your name',\n            'email' => 'Your email address',\n            'subject' => 'Subject',\n            'body' => 'Content',\n        ];\n    }\n}\n```\n\nPara aplicações que suportam vários idiomas, você pode querer traduzir os labels \ndos atributos. Isto também é feito no método [[yii\\base\\Model::attributeLabels()|attributeLabels()]], \nconforme o exemplo a seguir:\n\n```php\npublic function attributeLabels()\n{\n    return [\n        'name' => \\Yii::t('app', 'Your name'),\n        'email' => \\Yii::t('app', 'Your email address'),\n        'subject' => \\Yii::t('app', 'Subject'),\n        'body' => \\Yii::t('app', 'Content'),\n    ];\n}\n```\n\nVocê pode até definir condicionalmente os labels dos atributos. Por exemplo, baseado \nno [cenário](#scenarios) que o model estiver utilizando, você pode retornar diferentes \nlabels para o mesmo atributo.\n\n> Informação: Estritamente falando, os labels dos atributos fazem parte das \n[views](structure-views.md) (visões). Mas ao declarar os labels em models (modelos), \nfrequentemente tornam-se mais convenientes e podem resultar um código mais limpo \ne reutilizável.\n\n\n## Cenários <span id=\"scenarios\"></span>\n\nUm model (modelo) pode ser usado em diferentes *cenários*. Por exemplo, um model \n`User` pode ser usado para obter dados de entrada de login, mas também pode ser \nusado com a finalidade de registrar o usuário. Em diferentes cenários, um model \npode usar diferentes regras e lógicas de negócio. Por exemplo, um atributo `email` \npode ser obrigatório durante o cadastro do usuário, mas não durante ao login.\n\nUm model (modelo) usa a propriedade [[yii\\base\\Model::scenario]] para identificar \no cenário que está sendo usado.\nPor padrão, um model (modelo) suporta apenas um único cenário chamado `default`. \nO código a seguir mostra duas formas de definir o cenário de um model (modelo):\n\n```php\n// o cenário é definido pela propriedade\n$model = new User;\n$model->scenario = 'login';\n\n// o cenário é definido por meio de configuração\n$model = new User(['scenario' => 'login']);\n```\n\nPor padrão, os cenários suportados por um model (modelo) são determinados pelas \n[regras de validação](#validation-rules) declaradas no próprio model (modelo).\nNo entanto, você pode personalizar este comportamento sobrescrevendo o método \n[[yii\\base\\Model::scenarios()]], conforme o exemplo a seguir:\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass User extends ActiveRecord\n{\n    public function scenarios()\n    {\n        return [\n            'login' => ['username', 'password'],\n            'register' => ['username', 'email', 'password'],\n        ];\n    }\n}\n```\n\n> Informação: Nos exemplos anteriores, as classes model (model) são estendidas de \n[[yii\\db\\ActiveRecord]] por usarem diversos cenários para auxiliarem as classes \n[Active Record](db-active-record.md) classes.\n\nO método `scenarios()` retorna um array cujas chaves são os nomes dos cenários e \nos valores que correspondem aos *active attributes* (atributo ativo). Um atributo \nativo podem ser [atribuídos em massa](#massive-assignment) e é sujeito a \n[validação](#validation-rules). No  exemplo anterior, os atributos `username` e \n`password` são ativos no cenário `login`; enquanto no cenário `register`, além \ndos atribitos `username` e `password`, o atributo `email` passará a ser ativo.\n\nA implementação padrão do método `scenarios()` retornará todos os cenários encontrados \nnas regras de validação declaradas no método [[yii\\base\\Model::rules()]]. Ao \nsobrescrever o método `scenarios()`, se quiser introduzir novos cenários, além \ndos cenários padrão, poderá escrever um código conforme o exemplo a seguir:\n \n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass User extends ActiveRecord\n{\n    public function scenarios()\n    {\n        $scenarios = parent::scenarios();\n        $scenarios['login'] = ['username', 'password'];\n        $scenarios['register'] = ['username', 'email', 'password'];\n        return $scenarios;\n    }\n}\n```\n\nO recurso de cenários são usados principalmente para [validação](#validation-rules) \ne para [atribuição em massa](#massive-assignment).\nVocê pode, no entanto, usá-lo para outros fins. Por exemplo, você pode declarar \ndiferentes [labels para os atributos](#attribute-labels) baseados no cenário atual.\n\n\n## Regras de Validação <span id=\"validation-rules\"></span>\n\nQuando os dados para um model (modelo) são recebidos de usuários finais, devem ser \nvalidados para garantir que satisfazem as regras (*regras de validação*, também \nconhecidos como *regras de negócio*). Por exemplo, considerando um model (modelo) \n`ContactForm`, você pode querer garantir que todos os atributos não sejam vazios e \nque o atributo `email` contenha um e-mail válido.\nSe o valor de algum atributo não satisfizer a regra de negócio correspondente, \nmensagens apropriadas de erros serão exibidas para ajudar o usuário a corrigi-los.\n\nVocê pode chamar o método [[yii\\base\\Model::validate()]] para validar os dados \nrecebidos. O método usará as regras de validação declaradas em [[yii\\base\\Model::rules()]] \npara validar todos os atributos relevantes. Se nenhum erro for encontrado, o método \nretornará `true`. Caso contrário, o método irá manter os erros na propriedade \n[[yii\\base\\Model::errors]] e retornará `false`. Por exemplo,\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// os atributos do model serão populados pelos dados fornecidos pelo usuário\n$model->attributes = \\Yii::$app->request->post('ContactForm');\n\nif ($model->validate()) {\n    // todos os dados estão válidos \n} else {\n    // a validação falhou: $errors é um array contendo as mensagens de erro\n    $errors = $model->errors;\n}\n```\n\n\nPara declarar as regras de validação em um model (modelo), sobrescreva o método \n[[yii\\base\\Model::rules()]] retornando as regras que os atributos do model (modelo) \ndevem satisfazer. O exemplo a seguir mostra as regras de validação sendo declaradas \nno model (modelo) `ContactForm`:\n\n```php\npublic function rules()\n{\n    return [\n        // os atributos name, email, subject e body são obrigatórios\n        [['name', 'email', 'subject', 'body'], 'required'],\n\n        // o atributo email deve ter um e-mail válido\n        ['email', 'email'],\n    ];\n}\n```\n\nUma regra pode ser usada para validar um ou vários atributos e, um atributo pode \nser validado por uma ou várias regras.\nPor favor, consulte a seção [Validação de Dados](input-validation.md) para mais \ndetalhes sobre como declarar regras de validação.\n\nÀs vezes, você pode querer que uma regra se aplique apenas em determinados \n[cenários](#scenarios). Para fazer isso, você pode especificar a propriedade \n`on` de uma regra, como o seguinte:\n\n```php\npublic function rules()\n{\n    return [\n        // os atributos username, email e password são obrigatórios no cenario \"register\"\n        [['username', 'email', 'password'], 'required', 'on' => 'register'],\n\n        // os atributos username e password são obrigatórios no cenario \"login\"\n        [['username', 'password'], 'required', 'on' => 'login'],\n    ];\n}\n```\n\nSe você não especificar a propriedade `on`, a regra será aplicada em todos os \ncenários. Uma regra é chamada de *active rule* (regra ativa), se ela puder ser \naplicada no [[yii\\base\\Model::scenario|cenário]] atual.\n\nUm atributo será validado, se e somente se, for um atributo ativo declarado no \nmétodo `scenarios()` e estiver associado a uma ou várias regras declaradas no método `rules()`.\n\n\n## Atribuição em Massa <span id=\"massive-assignment\"></span>\n\nAtribuição em massa é a forma conveniente para popular um model (modelo) com os \ndados de entrada do usuário usando uma única linha de código.\nEle popula os atributos de um model (modelo) atribuindo os dados de entrada diretamente \nna propriedade [[yii\\base\\Model::$attributes]]. Os dois códigos a seguir são \nequivalentes, ambos tentam atribuir os dados do formulário enviados pelos usuários \nfinais para os atributos do model (modelo) `ContactForm`.  Evidentemente, a \nprimeira forma, que utiliza a atribuição em massa, é a mais limpa e o menos \npropenso a erros do que a segunda forma:\n\n```php\n$model = new \\app\\models\\ContactForm;\n$model->attributes = \\Yii::$app->request->post('ContactForm');\n```\n\n```php\n$model = new \\app\\models\\ContactForm;\n$data = \\Yii::$app->request->post('ContactForm', []);\n$model->name = isset($data['name']) ? $data['name'] : null;\n$model->email = isset($data['email']) ? $data['email'] : null;\n$model->subject = isset($data['subject']) ? $data['subject'] : null;\n$model->body = isset($data['body']) ? $data['body'] : null;\n```\n\n\n### Atributos Seguros <span id=\"safe-attributes\"></span>\n\nA atribuição em massa só se aplica aos chamados *safe attributes* (atributos seguros), \nque são os atributos listados no [[yii\\base\\Model::scenarios()]] para o \n[[yii\\base\\Model::scenario|cenário]] atual de um model (modelo).\nPor exemplo, se o model (modelo) `User` declarar o cenário como o código a seguir, \nquando o cenário atual for `login`, apenas os atributos `username` e `password` \npodem ser atribuídos em massa. Todos os outros atributos permanecerão inalterados.\n\n```php\npublic function scenarios()\n{\n    return [\n        'login' => ['username', 'password'],\n        'register' => ['username', 'email', 'password'],\n    ];\n}\n```\n\n> Informação: A razão da atribuição em massa só se aplicar para os atributos seguros \né para que você tenha o controle de quais atributos podem ser modificados pelos \ndados dos usuário finais. Por exemplo, se o model (modelo) tiver um atributo \n`permission` que determina a permissão atribuída ao usuário, você gostará que \napenas os administradores possam modificar este atributo através de uma interface backend.\n\nComo a implementação do método [[yii\\base\\Model::scenarios()]] retornará todos os \ncenários e atributos encontrados em [[yii\\base\\Model::rules()]], se não quiser \nsobrescrever este método, isto significa que um atributo é seguro desde que esteja \nmencionado em uma regra de validação ativa.\n\nPor esta razão, uma alias especial de validação chamada `safe`, será fornecida \npara que você possa declarar um atributo seguro, sem ser validado. Por exemplo, \na declaração da regra a seguir faz com que tanto o atributo `title` quanto o\n`description` sejam seguros.\n\n```php\npublic function rules()\n{\n    return [\n        [['title', 'description'], 'safe'],\n    ];\n}\n```\n\n\n### Atributos não Seguros <span id=\"unsafe-attributes\"></span>\n\nComo descrito anteriormente, o método [[yii\\base\\Model::scenarios()]] serve para \ndois propósitos: determinar quais atributos devem ser validados e quais atributos \nsão seguros. Em alguns casos raros, você pode quer validar um atributo sem marca-lo \ncomo seguro. Para fazer isto, acrescente um ponto de exclamação `!` como prefixo \ndo nome do atributo ao declarar no método `scenarios()`, como o que foi feito no \natributo `secret` no exemplo a seguir:\n\n```php\npublic function scenarios()\n{\n    return [\n        'login' => ['username', 'password', '!secret'],\n    ];\n}\n```\n\nQuando o model (modelo) estiver no cenário `login`, todos os três atributos serão \nvalidados. No entanto, apenas os atributos `username` e `password` poderão ser \natribuídos em massa. Para atribuir um valor de entrada no atributo `secret`, terá \nque fazer isto explicitamente da seguinte forma:\n\n```php\n$model->secret = $secret;\n```\n\n\n## Exportação de Dados <span id=\"data-exporting\"></span>\n\nMuitas vezes os models (modelos) precisam ser exportados em diferentes tipos de \nformatos. Por exemplo, você pode querer converter um conjunto de models (modelos) \nno formato JSON ou Excel. O processo de exportação pode ser divido em duas etapas independentes.\nNa primeira etapa, os models (modelos) serão convertidos em arrays; na segunda \netapa, os arrays serão convertidos em um determinado formato. Se concentre apenas \nna primeira etapa, uma vez que a segunda etapa pode ser alcançada por formatadores \nde dados genéricos, tais como o [[yii\\web\\JsonResponseFormatter]].\n\nA maneira mais simples de converter um model (modelo) em um array consiste no uso \nda propriedade [[yii\\base\\Model::$attributes]].\nPor exemplo,\n\n```php\n$post = \\app\\models\\Post::findOne(100);\n$array = $post->attributes;\n```\n\nPor padrão, a propriedade [[yii\\base\\Model::$attributes]] retornará os valores de \ntodos os atributos declarados no método [[yii\\base\\Model::attributes()]].\n\nUma maneira mais flexível e poderosa de converter um model (modelo) em um array é \natravés do método [[yii\\base\\Model::toArray()]]. O seu comportamento padrão é o \nmesmo do [[yii\\base\\Model::$attributes]]. No entanto, ele permite que você escolha \nquais itens de dados, chamados de *fields* (campos), devem ser mostrados no array \nresultante e como eles devem vir formatados.\nNa verdade, é a maneira padrão de exportação de models (modelos) no desenvolvimento \nde Web services RESTful, como descrito na seção [Formatando Respostas](rest-response-formatting.md).\n\n\n### Campos <span id=\"fields\"></span>\n\nUm campo é simplesmente um elemento nomeado no array obtido pela chamada do método \n[[yii\\base\\Model::toArray()]] de um model (modelo).\n\nPor padrão, os nomes dos campos são iguais aos nomes dos atributos. No entanto, \nvocê pode alterar este comportamento sobrescrevendo os métodos \n[[yii\\base\\Model::fields()|fields()]] e/ou [[yii\\base\\Model::extraFields()|extraFields()]]. \nAmbos os métodos devem retornar uma lista dos campos definidos. Os campos definidos \npelo método `fields()` são os campos padrão, o que significa que o `toArray()` \nretornará estes campos por padrão. O método `extraFields()` define, de forma adicional, \nos campos disponíveis que também podem ser retornados pelo `toArray()`, contanto \nque sejam especificados através do parâmetro `$expand`. Por exemplo, o código a \nseguir retornará todos os campos definidos em `fields()` incluindo os campos \n`prettyName` e `fullAddress`, a menos que estejam definidos no `extraFields()`.\n\n```php\n$array = $model->toArray([], ['prettyName', 'fullAddress']);\n```\n\nVocê poderá sobrescrever o método `fields()` para adicionar, remover, renomear ou \nredefinir os campos. O valor de retorno do `fields()` deve ser um array. As chaves \ndo array não os nomes dos campos e os valores correspondem ao nome do atributo \ndefinido, na qual, podem ser tanto os nomes de propriedades/atributos quanto funções \nanônimas que retornam o valor dos campos correspondentes. Em um caso especial, \nquando o nome do campo for igual ao nome do atributo definido, você poderá omitir \na chave do array. Por exemplo,\n\n```php\n// usar uma lista explicita de todos os campos lhe garante que qualquer mudança \n// em sua tabela do banco de dados ou atributos do model (modelo) não altere os \n// nomes de seus campos (para manter compatibilidade com versões anterior da API).\npublic function fields()\n{\n    return [\n        // o nome do campos é igual ao nome do atributo\n        'id',\n\n        // o nome do campo é \"email\", o nome do atributo correspondente é \"email_address\"\n        'email' => 'email_address',\n\n        // o nome do campo é \"name\", o seu valor é definido por uma função call-back do PHP\n        'name' => function () {\n            return $this->first_name . ' ' . $this->last_name;\n        },\n    ];\n}\n\n// filtra alguns campos, é bem usado quando você quiser herdar a implementação \n// da classe pai e remover alguns campos delicados.\npublic function fields()\n{\n    $fields = parent::fields();\n\n    // remove os campos que contém informações delicadas\n    unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);\n\n    return $fields;\n}\n```\n\n> Atenção: Como, por padrão, todos os atributos de um model (modelo) serão \n>incluídos no array exportado, você deve examinar seus dados para ter certeza \n>que não possuem informações delicadas. Se existir, deverá sobrescrever o método \n>`fields()` para remove-los. No exemplo anterior, nós decidimos remover os \n>campos `auth_key`, `password_hash` e `password_reset_token`.\n\n\n## Boas Práticas <span id=\"best-practices\"></span>\n\nA representação dos dados, regras e lógicas de negócios estão centralizados nos \nmodels (modelos). Muitas vezes precisam ser reutilizadas em lugares diferentes. \nEm um aplicativo bem projetado, models (modelos) geralmente são muitos maiores \nque os [controllers](structure-controllers.md)\n\nEm resumo, os models (modelos):\n\n* podem conter atributos para representar os dados de negócio;\n* podem conter regras de validação para garantir a validade e integridade dos dados;\n* podem conter métodos para implementar lógicas de negócio;\n* NÃO devem acessar diretamente as requisições, sessões ou quaisquer dados do \nambiente do usuário. Os models (modelos) devem receber estes dados a partir dos \n[controllers (controladores)](structure-controllers.md);\n* devem evitar inserir HTML ou outros códigos de apresentação – isto deve ser \nfeito nas [views (visões)](structure-views.md);\n* devem evitar ter muitos [cenários](#scenarios) em um único model (modelo).\n\nVocê deve considerar em utilizar com mais frequência a última recomendação acima \nquando desenvolver sistemas grandes e complexos.\nNestes sistemas, os models (modelos) podem ser bem grandes, pois são usados em \nmuitos lugares e podendo, assim, conter muitas regras e lógicas de negócio. \nNestes casos, a manutenção do código de um model (modelo) pode se transformar \nem um pesadelo, na qual uma simples mudança no código pode afetar vários lugares \ndiferentes. Para desenvolver um model (modelo) manutenível, você pode seguir a \nseguinte estratégia: \n\n* Definir um conjunto de classes model (modelo) base que são compartilhados por \ndiferentes [aplicações](structure-applications.md) ou [módulos](structure-modules.md). \nEstas classes model (modelo) base deve contem um conjunto mínimo de regras e lógicas \nde negocio que são comuns entre os locais que as utilizem.\n* Em cada [aplicação](structure-applications.md) ou [módulo](structure-modules.md) \nque usa um model (modelo), deve definir uma classe model (modelo) concreta que \nestenderá a classe model (modelo) base que a corresponde. A classe model (modelo) \nconcreta irá conter apenas as regras e lógicas que são específicas de uma aplicação \nou módulo.\n\nPor exemplo, no [Template Avançado de Projetos](tutorial-advanced-app.md), você \npode definir uma classe model (modelo) base `common\\models\\Post`. Em seguida, \npara a aplicação front-end, você define uma classe model (modelo) concreta \n`frontend\\models\\Post` que estende de `common\\models\\Post`. E de forma similar \npara a aplicação back-end, você define a `backend\\models\\Post`. Com essa estratégia, \nvocê garantirá que o `frontend\\models\\Post` terá apenas códigos específicos da \naplicação front-end e, se você fizer qualquer mudança nele, não precisará se \npreocupar se esta mudança causará erros na aplicação back-end.\n"
  },
  {
    "path": "docs/guide-pt-BR/structure-modules.md",
    "content": "Módulos\n=======\n\nOs módulos são unidades independentes de software que são compostos de \n[models (modelos)](structure-models.md), [views (visões)](structure-views.md), \n[controllers (controladores)](structure-controllers.md) e outros componentes de \napoio. Os usuários finais podem acessar os controllers (controladores) de um \nmódulo caso esteja implementado na [aplicação](structure-applications.md). Por \nestas razões, os módulos são muitas vezes enxergados como mini-aplicações. Os \nmódulos diferem das [aplicações](structure-applications.md) pelo fato de não \npoderem ser implementados sozinhos e que devem residir dentro das aplicações.\n\n\n## Criando Módulos <span id=\"creating-modules\"></span>\n\nUm módulo é organizado como um diretório que é chamado de \n[[yii\\base\\Module::basePath|caminho base]] do módulo. Dentro deste diretório, \nexistem subdiretório, como o `controllers`, `models`, `views` que mantêm os \ncontrollers (controladores), models (modelos), views (visões) e outros códigos \nassim como em uma aplicação. O exemplo a seguir mostra o conteúdo dentro de um \nmódulo:\n\n```\nforum/\n    Module.php                   o arquivo da classe do módulo \n    controllers/                 contém os arquivos da classe controller\n        DefaultController.php    o arquivo da classe controller padrão\n    models/                      contém os arquivos da classe model\n    views/                       contém a view do controller e os arquivos de layout\n        layouts/                 contém os arquivos de layout\n        default/                 contém os arquivos de view para o DefaultController\n            index.php            o arquivo de view index \n```\n\n\n### Classe do Módulo <span id=\"module-classes\"></span>\n\nCada módulo deve ter uma única classe que estende de [[yii\\base\\Module]]. Esta \nclasse deve estar localizada diretamente sob o [[yii\\base\\Module::basePath|caminho base]] \ndo módulo e deve ser [autoloadable](concept-autoloading.md). Quando um módulo \nestiver sendo acessado, uma única instância da classe módulo correspondente será \ncriada. Assim como as [instâncias da aplicação](structure-applications.md), as \ninstâncias do módulo são usadas para compartilhar dados e componentes para os \ncódigos dentro dos módulos.\n\nSegue um exemplo de uma classe de módulo:\n\n```php\nnamespace app\\modules\\forum;\n\nclass Module extends \\yii\\base\\Module\n{\n    public function init()\n    {\n        parent::init();\n\n        $this->params['foo'] = 'bar';\n        // ...  outros códigos de inicialização ...\n    }\n}\n```\n\nSe o método `init()` inicializar muitas propriedades do módulo, você poderá \nsalva-los em um arquivo de [configuração](concept-configurations.md) e carregá-los \ncomo mostro no código a seguir no método `init()`:\n\n```php\npublic function init()\n{\n    parent::init();\n    // inicializa o módulo com as configurações carregadas de config.php\n    \\Yii::configure($this, require __DIR__ . '/config.php');\n}\n```\n\nO arquivo de configuração `config.php` pode conter o conteúdo a seguir, que é \nsemelhante ao de uma [configuração de aplicação](structure-applications.md#application-configurations).\n\n```php\n<?php\nreturn [\n    'components' => [\n        // lista de configuração de componentes\n    ],\n    'params' => [\n        // lista de parâmetros\n    ],\n];\n```\n\n\n### Controllers em Módulos <span id=\"controllers-in-modules\"></span>\n\nAo criar controllers (controladores) em um módulo, uma convenção é colocar as \nclasses dos controllers (controladores) sob o sub-namespace `controllers` do \nnamespace do módulo da classe. Isto também significa que os arquivos da classe \ncontroller (controlador) devem ser colocadas no diretório `controllers` dentro \ndo [[yii\\base\\Module::basePath|caminho base]] do módulo. Por exemplo, para criar \num controller (controlador) `post` no módulo `forum` mostrado na última subseção, \nvocê deve declarar a classe controller (controlador) conforme o seguinte exemplo:\n\n```php\nnamespace app\\modules\\forum\\controllers;\n\nuse yii\\web\\Controller;\n\nclass PostController extends Controller\n{\n    // ...\n}\n```\n\nVocê pode personalizar o namespace da classe do controller (controlador) \nconfigurando a propriedade [[yii\\base\\Module::controllerNamespace]]. No caso de \nalguns controllers (controladores) estiverem fora do namespace, você poderá \ntorna-los acessíveis pela configuração da propriedade [[yii\\base\\Module::controllerMap]], \nde forma similar ao [que você fez na aplicação](structure-applications.md#controller-map).\n\n\n### Views em Módulos <span id=\"views-in-modules\"></span>\n\nAs views (visões) devem ser colocadas no diretório `views` dentro do \n[[yii\\base\\Module::basePath|caminho base]] do módulo. Para as views (visões) \nrenderizadas por um controller (controlador) no módulo, devem ser colocadas sob \no diretório `views/IDdoController`, onde o `IDdoController` refere-se ao \n[ID do controller (controlador)](structure-controllers.md#routes). Por exemplo, \nse a classe do controller (controlador) for `PostController`, o diretório será \n`views/post` dentro do [[yii\\base\\Module::basePath|caminho base]] do módulo.\n\nUm módulo pode especificar um [layout](structure-views.md#layouts) que será \naplicado pelas views (visões) renderizadas pelos controllers (controladores) do \nmódulo. Por padrão, o layout deve ser colocado no diretório `views/layouts` e \ndeve configurar a propriedade [[yii\\base\\Module::layout]] para apontar o nome do \nlayout. Se você não configurar a propriedade `layout`, o layout da aplicação será \nusada em seu lugar.\n\n\n## Usando os Módulos <span id=\"using-modules\"></span>\n\nPara usar um módulo em uma aplicação, basta configurar a aplicação, listando o \nmódulo na propriedade [[yii\\base\\Application::modules|modules]] da aplicação. O \ncódigo da [configuração da aplicação](structure-applications.md#application-configurations) \na seguir faz com que o módulo `forum` seja aplicado:\n\n```php\n[\n    'modules' => [\n        'forum' => [\n            'class' => 'app\\modules\\forum\\Module',\n            // ... outras configurações do módulo ...\n        ],\n    ],\n]\n```\n\nA propriedade [[yii\\base\\Application::modules|modules]] é composto por um array \nde configurações de módulos. Cada chave do array representa um *ID do módulo* \nque identifica exclusivamente o módulo entre todos módulos da aplicação e o valor \ndo array correspondente é uma [configuração](concept-configurations.md) para a \ncriação do módulo.\n\n\n### Rotas <span id=\"routes\"></span>\n\nAssim como acessar os controllers (controladores) em uma aplicação, as \n[rotas](structure-controllers.md#routes) são usadas para tratar os controllers \n(controladores) em um módulo. Uma rota para um controller (controlador) dentro \nde um módulo deve iniciar com o ID do módulo seguido pelo ID do controller \n(controlador) e pelo ID da ação. Por exemplo, se uma aplicação usar um modulo \nchamado `forum`, então a rota `forum/post/index` representará a ação `index` do \ncontroller (controlador) `post` no módulo. Se a rota conter apenas o ID do módulo, \nentão a propriedade [[yii\\base\\Module::defaultRoute]], na qual o valor padrão é \n`default`, determinará qual controller/action deverá ser usado. Isto significa \nque a rota `forum` representará o controller (controlador) `default` no módulo `forum`.\n\n\n### Acessando os Módulos <span id=\"accessing-modules\"></span>\n\nDentro de um módulo, você poderá precisar muitas vezes obter a instância do \n[módulo da classe](#module-classes) para que você possa acessar o ID, os parâmetros, \nos componentes e etc do módulo. Você pode fazer isso usando a seguinte declaração:\n\n```php\n$module = MyModuleClass::getInstance();\n```\n\nO `MyModuleClass` refere-se ao nome da classe do módulo que você está interessado. \nO método `getInstance()` retornará a instância que foi solicitada pela requisição. \nSe o módulo não for solicitado pela requisição, o método retornará `null`. Observe \nque você não vai querer criar uma nova instância manualmente da classe do módulo \npois será diferente do criado pelo Yii em resposta a uma requisição.\n\n> Informação: Ao desenvolver um módulo, você não deve assumir que o módulo usará \n  um ID fixo. Isto porque um módulo pode ser associado a um ID arbitrário quando \n  usado em uma aplicação ou dentro de outro módulo. A fim de obter o ID do módulo, \n  você deve usar a abordagem anterior para obter primeiramente a instância do módulo \n  e em seguida obter o ID através de `$module->id`.\n\nVocê também pode acessar a instância do módulo usando as seguintes abordagens:\n\n```php\n// obter o módulo cujo ID é \"forum\"\n$module = \\Yii::$app->getModule('forum');\n\n// obter o módulo pelo controller solicitado pela requisição\n$module = \\Yii::$app->controller->module;\n```\n\nA primeira abordagem só é útil quando você sabe o ID do módulo, enquanto a segunda \nabordagem é melhor utilizada quando você sabe sobre os controllers (controladores) \nque está sendo solicitado.\n\nUma vez tendo a instância do módulo, você pode acessar as propriedade e componentes \nregistrados no módulo. Por exemplo,\n\n```php\n$maxPostCount = $module->params['maxPostCount'];\n```\n\n\n### Inicializando os Módulos <span id=\"bootstrapping-modules\"></span>\n\nAlguns módulos precisam ser executados a cada requisição. O módulo \n[[yii\\debug\\Module|debug]] é um exemplo desta necessidade. Para isto, você deverá \nlistar os IDs deste módulos na propriedade [[yii\\base\\Application::bootstrap|bootstrap]] \nda aplicação. \n\nPor exemplo, a configuração da aplicação a seguir garante que o módulo `debug` \nseja sempre carregado:\n\n```php\n[\n    'bootstrap' => [\n        'debug',\n    ],\n\n    'modules' => [\n        'debug' => 'yii\\debug\\Module',\n    ],\n]\n```\n\n\n## Módulos Aninhados <span id=\"nested-modules\"></span>\n\nOs módulos podem ser aninhados em níveis ilimitados. Isto é, um módulo pode conter \num outro módulo que pode conter ainda um outro módulo. Nós chamamos dos anteriores \nde *módulo parente* enquanto os próximos de *módulo filho*. Os módulos filhos devem \nser declarados na propriedade [[yii\\base\\Module::modules|modules]] de seus módulos \nparentes. Por exemplo,\n\n```php\nnamespace app\\modules\\forum;\n\nclass Module extends \\yii\\base\\Module\n{\n    public function init()\n    {\n        parent::init();\n\n        $this->modules = [\n            'admin' => [\n                // você pode considerar em usar um namespace mais curto aqui!\n                'class' => 'app\\modules\\forum\\modules\\admin\\Module',\n            ],\n        ];\n    }\n}\n```\n\nPara um controller (controlador) dentro de um módulo aninhado, a rota deve incluir \nos IDs de todos os seus módulos ancestrais. Por exemplo, a rota \n`forum/admin/dashboard/index` representa a ação `index` do controller (controlador) \n`dashboard` no módulo `admin` que é um módulo filho do módulo `forum`.\n\n> Informação: O método [[yii\\base\\Module::getModule()|getModule()]] retorna apenas \n  o módulo filho diretamente pertencente ao seu módulo parente. A propriedade \n  [[yii\\base\\Application::loadedModules]] mantém uma lista dos módulos carregados, \n  incluindo os módulos filhos e parentes aninhados, indexados pelos seus nomes de \n  classes.\n\n\n## Boas Práticas <span id=\"best-practices\"></span>\n\nOs módulos são melhores usados em aplicações de larga escala, cujas características \npodem ser divididas em vários grupos, cada um constituídas por um conjuntos de \nrecursos relacionados. Cada grupo de recurso pode ser desenvolvido em um módulo \nque é desenvolvido e mantido por um desenvolvedor ou equipe específica. \n\nMódulos também são uma boa maneira de reutilizar códigos no nível de grupo de \nrecurso. Algumas recursos comumente utilizados, tais como gerenciamento de usuários, \ngerenciamento de comentários, podem ser todos desenvolvidos em módulos, para que \npossam ser utilizados em projetos futuros.\n"
  },
  {
    "path": "docs/guide-pt-BR/structure-overview.md",
    "content": "Visão Geral\n===========\n\nAs aplicações do Yii são organizadas de acordo com o padrão de projeto\n[model-view-controller (MVC)](https://pt.wikipedia.org/wiki/MVC)\n(modelo-visão-controlador). Os [models](structure-models.md) representam dados,\nlógica e regras de negócio; as [views](structure-views.md) são a representação\nda saída dos modelos; e os [controllers](structure-controllers.md) recebem entradas\ne as convertem em comandos para os [models](structure-models.md) e as [views](structure-views.md).\n\nAlém do MVC, as aplicações do Yii também possuem as seguintes entidades:\n\n* [scripts de entrada](structure-entry-scripts.md): são scripts PHP que são\n  diretamente acessíveis aos usuários finais. São responsáveis por iniciar o\n  ciclo de tratamento de uma requisição.\n* [aplicações](structure-applications.md): são objetos globalmente acessíveis que\n  gerenciam os componentes da aplicação e os coordenam para atender às requisições.\n* [componentes da aplicação](structure-application-components.md): são objetos\n  registrados com as aplicações e fornecem vários serviços para atender às\n  requisições.\n* [módulos](structure-modules.md): são pacotes auto-contidos que contém um MVC\n  completo por si sós. Uma aplicação pode ser organizada em termos de múltiplos\n  módulos.\n* [filtros](structure-filters.md): representam código que precisa ser chamado\n  pelos controllers antes e depois do tratamento propriamente dito de cada\n  requisição.\n* [widgets](structure-widgets.md): são objetos que podem ser embutidos em\n  [views](structure-views.md). Podem conter lógica de controller e podem ser\n  reutilizados em diferentes views.\n\nO diagrama a seguir demonstra a estrutura estática de uma aplicação:\n\n![Estrutura Estática da Aplicação](images/application-structure.png)\n"
  },
  {
    "path": "docs/guide-pt-BR/structure-views.md",
    "content": "Visões (Views)\n===========\n\nAs views fazem parte da arquitetura [MVC](https://pt.wikipedia.org/wiki/MVC).\nSão a parte do código responsável por apresentar dados aos usuários finais. Em um aplicação Web,\nviews geralmente são criadas em termos de *view templates* (modelos de view)\n que são arquivos PHP contendo principalmente códigos HTML e\ncódigos PHP de apresentação.\nOs modelos de view são gerenciados pelo [componente da aplicação](structure-application-components.md)\n[[yii\\web\\View|view]] que fornece métodos comumente utilizados para facilitar\na montagem e a renderização da view em si. Para simplificar, geralmente chamamos os modelos de view ou seus arquivos simplesmente\nde view.\n\n\n## Criando Views  <span id=\"creating-views\"></span>\n\nConforme já mencionado, uma view é simplesmente um arquivo PHP\ncomposto por HTML ou códigos PHP. O código a seguir representa uma view que exibe um formulário\nde login. Como você pode ver, o código PHP é utilizado para gerar\nas partes de conteúdo dinâmicas, tais como o título da página e o formulário, enquanto o código HTML dispõe os itens na página de uma forma apresentável.\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n/**\n * @var \\yii\\web\\View $this\n * @var \\yii\\widgets\\ActiveForm $form\n * @var \\app\\models\\LoginForm $model\n */\n\n$this->title = 'Login';\n?>\n<h1><?= Html::encode($this->title) ?></h1>\n\n<p>Por favor, preencha os seguintes campos para entrar:</p>\n\n<?php $form = ActiveForm::begin(); ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n    <?= Html::submitButton('Entrar') ?>\n<?php ActiveForm::end(); ?>\n```\n\nEm uma view, você pode acessar a variável `$this` que referencia o\n[[yii\\web\\View|componente view]] responsável por gerenciar e renderizar a view\n em questão.\n\nAlém de `$this`, pode haver outras variáveis predefinidas na view, tal como\n`$model` no exemplo acima. Essas variáveis representam\nos dados que foram enviados à view por meio dos\n[controllers](structure-controllers.md) ou de outros objetos que\ndesencadeiam a [renderização da view ](#rendering-views).\n\n> Dica: As variáveis predefinidas são listadas em um bloco de comentário no inicio\n  de uma view para que possam ser reconhecidas pelas IDEs. Além de ser também\n  uma ótima maneira de documentar suas views.\n\n\n### Segurança <span id=\"security\"></span>\n\nAo criar views que geram páginas HTML, é importante que você codifique\ne/ou filtre os dados que vêm de usuários antes de exibí-los. Caso contrário,\nsua aplicação poderá estar sujeita a um ataque de\n[cross-site scripting](https://pt.wikipedia.org/wiki/Cross-site_scripting).\n\nPara exibir um texto simples, codifique-o antes por chamar o método\n[[yii\\helpers\\Html::encode()]]. Por exemplo, o código a seguir codifica o nome do\nusuário antes de exibí-lo:\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n\n<div class=\"username\">\n    <?= Html::encode($user->name) ?>\n</div>\n```\n\nPara exibir conteúdo HTML, use [[yii\\helpers\\HtmlPurifier]] para\nfiltrar o conteúdo primeiro. Por exemplo, o código a seguir filtra o conteúdo de `$post->text` antes de exibí-lo:\n\n```php\n<?php\nuse yii\\helpers\\HtmlPurifier;\n?>\n\n<div class=\"post\">\n    <?= HtmlPurifier::process($post->text) ?>\n</div>\n```\n\n> Dica: Embora o HTMLPurifier faça um excelente trabalho em tornar a saída de dados\n  segura, ele não é rápido. Você deveria considerar guardar em [cache](caching-overview.md)\n  o resultado filtrado se sua aplicação precisa de alta performance.\n\n\n### Organizando as Views  <span id=\"organizing-views\"></span>\n\nAssim como para os [controllers](structure-controllers.md) e para os\n[models](structure-models.md), existem convenções para organizar as\nviews.\n\n* Views renderizadas por um controller deveriam ser colocadas sob o diretório `@app/views/IDdoController` por padrão, onde `IDdoController` refere-se ao [ID do controller](structure-controllers.md#routes).\n  Por exemplo, se a classe do controller for `PostController`, o diretório\n  será `@app/views/post`; se for `PostCommentController`, o diretório será\n  `@app/views/post-comment`. Caso o controller pertença a um\n  módulo, o diretório seria `views/IDdoController` sob o [[yii\\base\\Module::basePath|diretório do módulo]].\n* Views renderizadas em um [widget](structure-widgets.md) deveriam ser\n  colocadas sob o diretório `WidgetPath/views` por padrão, onde `WidgetPath`\n  é o diretório o arquivo da classe do widget.\n* Para views renderizadas por outros objetos, é recomendado\n  que você siga a convenção semelhante à dos widgets.\n\nVocê pode personalizar os diretórios padrões das views sobrescrevendo\no método [[yii\\base\\ViewContextInterface::getViewPath()]] dos controllers ou dos widgets.\n\n\n## Renderizando Views  <span id=\"rendering-views\"></span>\n\nVocê pode renderizar views em\n[controllers](structure-controllers.md), em [widgets](structure-widgets.md) ou em qualquer outro lugar chamando os métodos de renderização da view. Esses métodos compartilham uma assinatura similar, como a seguir:\n\n```\n/**\n * @param string $view nome da view ou caminho do arquivo, dependendo do método de renderização\n * @param array $params os dados passados para a view\n * @return string resultado da renderização\n */\nmethodName($view, $params = [])\n```\n\n\n### Renderização em Controllers <span id=\"rendering-in-controllers\"></span>\n\nNos [controllers](structure-controllers.md), você pode chamar os\nseguintes métodos para renderizar as views:\n\n* [[yii\\base\\Controller::render()|render()]]: renderiza uma [view nomeada](#named-views) e aplica um [layout](#layouts) ao resultado da renderização.\n* [[yii\\base\\Controller::renderPartial()|renderPartial()]]: renderiza\n  uma [view nomeada](#named-views) sem qualquer layout.\n* [[yii\\web\\Controller::renderAjax()|renderAjax()]]: renderiza uma [view nomeada](#named-views) sem qualquer layout\n  e injeta todos os arquivos JS/CSS registrados. É geralmente utilizado\n  em respostas de requisições Web Ajax.\n* [[yii\\base\\Controller::renderFile()|renderFile()]]: renderiza uma view a partir\n  de um caminho de arquivo ou a partir de um [alias](concept-aliases.md).\n* [[yii\\base\\Controller::renderContent()|renderContent()]]: renderiza um conteúdo\n  estático que será incorporado no [layout](#layouts) selecionado. Este método\n  está disponível desde a versão 2.0.1.\n\nPor exemplo,\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse app\\models\\Post;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\n\nclass PostController extends Controller\n{\n    public function actionView($id)\n    {\n        $model = Post::findOne($id);\n        if ($model === null) {\n            throw new NotFoundHttpException;\n        }\n\n        // renderiza uma view chamada \"exibir\" e aplica um layout a ela\n        return $this->render('exibir', [\n            'model' => $model,\n        ]);\n    }\n}\n```\n\n\n### Renderização em Widgets <span id=\"rendering-in-widgets\"></span>\n\nNos [widgets](structure-widgets.md), você pode chamar os seguintes métodos do\nwidget para renderizar views:\n\n* [[yii\\base\\Widget::render()|render()]]: renderiza uma [view nomeada](#named-views).\n* [[yii\\base\\Widget::renderFile()|renderFile()]]: renderiza uma view a partir de\n  um caminho de arquivo ou a partir de um [alias](concept-aliases.md).\n\nPor exemplo,\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Widget;\nuse yii\\helpers\\Html;\n\nclass ListWidget extends Widget\n{\n    public $items = [];\n\n    public function run()\n    {\n        // renderiza uma view chamada \"listar\"\n        return $this->render('listar', [\n            'items' => $this->items,\n        ]);\n    }\n}\n```\n\n\n### Renderização em Views  <span id=\"rendering-in-views\"></span>\n\nVocê pode renderizar uma view dentro de outra chamando um dos seguintes\nmétodos fornecidos pelo [[yii\\base\\View|componente view]]:\n\n* [[yii\\base\\Controller::render()|render()]]: renderiza uma [view nomeada](#named-views).\n* [[yii\\web\\Controller::renderAjax()|renderAjax()]]: renderiza uma [view nomeada](#named-views) sem qualquer layout\n  e injeta todos os arquivos JS/CSS registrados. É geralmente utilizado\n  em respostas de requisições Web Ajax.\n* [[yii\\base\\Controller::renderFile()|renderFile()]]: renderiza uma view a partir\n  de um caminho de arquivo ou a partir de um [alias](concept-aliases.md).\n\nPor exemplo, no código a seguir, uma view qualquer renderiza outro arquivo\nde view chamado `_visao-geral.php` que encontram-se em seu mesmo diretório.\nLembre-se que `$this` na view referencia o componente [[yii\\base\\View|view]]:\n\n```php\n<?= $this->render('_visao-geral') ?>\n```\n\n\n### Renderização em Outros Lugares <span id=\"rendering-in-other-places\"></span>\n\nEm qualquer lugar, você pode acessar o componente de aplicação [[yii\\base\\View|view]]\npela expressão `Yii::$app->view` e então chamar qualquer método mencionado anteriormente\npara renderizar uma view. Por exemplo,\n\n```php\n// exibe a view \"@app/views/site/license.php\"\necho \\Yii::$app->view->renderFile('@app/views/site/license.php');\n```\n\n\n### Views Nomeadas <span id=\"named-views\"></span>\n\nAo renderizar uma view, você pode especificá-la usando seu nome, ou o caminho do arquivo, ou um alias. Na maioria dos casos,\nvocê usará a primeira maneira por ser mais concisa e flexível. Quando especificamos views por nome, chamamos essas views de *views nomeadas*.\n\nUm nome de view é convertido no caminho de arquivo da view correspondente de\nacordo com as seguintes regras:\n\n* Um nome de view pode omitir a extensão do arquivo. Neste caso, o `.php`\n  será usado como extensão. Por exemplo, a view chamada `sobre` corresponderá ao\n  arquivo `sobre.php`.\n* Se o nome da view iniciar com barras duplas `//`, o caminho correspondente\n  seria `@app/views/ViewName`. Ou seja, a view será localizada sob o\n  [[yii\\base\\Application::viewPath|diretório das views da aplicação]]. Por exemplo,\n  `//site/sobre` corresponderá ao `@app/views/site/sobre.php`.\n* Se o nome da view iniciar com uma barra simples `/`, o caminho do arquivo da view\n  será formado pelo nome da view com o [[yii\\base\\Module::viewPath|diretório da view]]\n  do [módulo](structure-modules.md) ativo. Se não houver um módulo ativo, o\n  `@app/views/ViewName` será usado. Por exemplo, `/usuario/criar` corresponderá\n  a `@app/modules/user/views/usuario/criar.php` caso o módulo ativo seja `user`.\n  Se não existir um módulo ativo, o caminho do arquivo da view será\n  `@app/views/usuario/criar.php`.\n* Se a view for renderizada com um [[yii\\base\\View::context|contexto]] e\n  que implemente [[yii\\base\\ViewContextInterface]], o caminho do arquivo\n  da view será formado por prefixar o [[yii\\base\\ViewContextInterface::getViewPath()|diretório da view]] do contexto ao nome da view.\n  Isto se aplica principalmente às views renderizadas em controllers e widgets. Por exemplo,\n  `sobre` corresponderá a `@app/views/site/sobre.php` caso o contexto seja o controller\n  `SiteController`.\n* Se uma view for renderizada dentro de outra, o diretório que contém esta\n  outra view será usado para formar o caminho de seu arquivo.\n  Por exemplo, `item` corresponderá a `@app/views/post/item.php` se ela for\n  renderizada dentro da view `@app/views/post/index.php`.\n\nDe acordo com as regras acima, chamar `$this->render('exibir')` em um controller `app\\controllers\\PostController` vai realmente renderizar o arquivo de view\n `@app/views/post/exibir.php` e, chamar `$this->render('_visaogeral')` nessa view (`exibir.php`) vai renderizar o arquivo de visão `@app/views/post/_visaogeral.php`.\n\n\n### Acessando Dados em Views  <span id=\"accessing-data-in-views\"></span>\n\nExistem duas abordagens para acessar dados em uma view : *push* e *pull*.\n\nAo passar os dados como o segundo parâmetro nos métodos de renderização de view, você estará usando a abordagem *push*.\nOs dados devem ser representados por um array com pares de nome-valor. Quando a\nview estiver sendo renderizada, a função `extract()` do PHP será executada sobre essa array a fim de extrair seus dados em variáveis na view.\nPor exemplo, o renderização da view a seguir, em um controller, disponibilizará (pela\nabordagem *push*) duas variáveis para a view  `relatorio`: `$foo = 1` e `$bar = 2`.\n\n```php\necho $this->render('relatorio', [\n    'foo' => 1,\n    'bar' => 2,\n]);\n```\n\nA abordagem *pull* ativamente obtém os dados do\n[[yii\\base\\View|componente view]] ou de outros objetos acessíveis nas views\n(por exemplo, `Yii::$app`). Usando o código a seguir como exemplo, dentro da view você pode acessar seu objeto controller usando a expressão `$this->context`.\nE como resultado, será possível acessar quaisquer propriedades ou\nmétodos do controller, como o seu ID, na view `relatorio`:\n\n```php\nO ID do controller é: <?= $this->context->id ?>\n?>\n```\n\nA abordagem *push* normalmente é a forma preferida de acessar dados nas views\npor que as torna menos dependentes de objetos de contexto. A\ndesvantagem é que você precisa montar manualmente os dados em um array o tempo\ntodo, o que poderia se tornar tedioso e propenso a erros se uma view for\ncompartilhada e renderizada em lugares diferentes.\n\n\n### Compartilhando Dados entre as Views  <span id=\"sharing-data-among-views\"></span>\n\nO [[yii\\base\\View|componente view]] fornece a propriedade\n[[yii\\base\\View::params|params]] que você pode usar para compartilhar dados entre\nas views.\n\nPor exemplo, em uma view `sobre`, você pode ter o seguinte código que especifica\no seguimento atual do \"rastro de navegação\" (breadcrumbs):\n\n```php\n$this->params['breadcrumbs'][] = 'Sobre nós';\n```\n\nEm seguida, no arquivo [layout](#layouts), que também é uma view, você pode\nexibir o \"rastro de navegação\" (breadcrumbs) usando os dados passados pela\npropriedade [[yii\\base\\View::params|params]]:\n\n```php\n<?= yii\\widgets\\Breadcrumbs::widget([\n    'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],\n]) ?>\n```\n\n\n## Layouts <span id=\"layouts\"></span>\n\nLayouts são um tipo especial de view que representam as partes comuns\nde múltiplas views. Por exemplo, as páginas da maioria das aplicações Web\ncompartilham o mesmo cabeçalho e rodapé. Embora você possa repetir o mesmo\ncabeçalho e rodapé em todas as view, a melhor maneira é fazer isso apenas uma vez\nno layout e incorporar o resultado da renderização de uma view em um lugar\napropriado no layout.\n\n\n### Criando Layouts <span id=\"creating-layouts\"></span>\n\nVisto que os layouts também são views, eles podem ser criados de\nforma semelhante às views normais. Por padrão, layouts são salvos\nno diretório `@app/views/layouts`. Layouts usados em um\n[módulo](structure-modules.md) devem ser salvos no diretório `views/layouts`\nsob o [[yii\\base\\Module::basePath|diretório do módulo]].\nVocê pode personalizar o diretório padrão de layouts configurando a propriedade\n[[yii\\base\\Module::layoutPath]] da aplicação ou do módulo.\n\nO exemplo a seguir mostra como é um layout. Observe que, para fins ilustrativos,\nsimplificamos bastante o código do layout. Na prática, você pode querer adicionar mais conteúdos a ele, tais\ncomo tags no head, menu principal, etc.\n\n```php\n<?php\nuse yii\\helpers\\Html;\n\n/**\n * @var \\yii\\web\\View $this\n * @var string $content\n */\n?>\n<?php $this->beginPage() ?>\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\"/>\n    <?= Html::csrfMetaTags() ?>\n    <title><?= Html::encode($this->title) ?></title>\n    <?php $this->head() ?>\n</head>\n<body>\n<?php $this->beginBody() ?>\n    <header>Minha Empresa</header>\n    <?= $content ?>\n    <footer>&copy; 2014 por Minhas Empresa</footer>\n<?php $this->endBody() ?>\n</body>\n</html>\n<?php $this->endPage() ?>\n```\n\nConforme pode ver, o layout gera as tags HTML que são comuns a todas as páginas. Na\nseçao `<body>`, o layout vai inserir a variável `$content` que representa o\nresultado da renderização do conteúdo das views e é enviado ao layout\nquando método [[yii\\base\\Controller::render()]] for chamado.\n\nA maioria dos layouts devem chamar os métodos listados a seguir, conforme ocorreu no código acima. Estes métodos essencialmente desencadeiam eventos referentes ao processo\nde renderização para que scripts e tags registrados em outros lugares possam ser\ninseridos nos locais onde eles (os métodos) forem chamados.\n\n- [[yii\\base\\View::beginPage()|beginPage()]]: Este método deve ser chamado no início\n  do layout. Ele dispara o evento [[yii\\base\\View::EVENT_BEGIN_PAGE|EVENT_BEGIN_PAGE]]\n  que indica o início de uma página.\n- [[yii\\base\\View::endPage()|endPage()]]: Este método deve ser chamado no final\n  do layout. Ele dispara o evento [[yii\\base\\View::EVENT_END_PAGE|EVENT_END_PAGE]]\n  que indica o fim de uma página.\n- [[yii\\web\\View::head()|head()]]: Este método deve ser chamado na seção `<head>`\n  de uma página HTML. Ele gera um marcador que será substituído por código HTML (por exemplo, tags `<link>` e `<meta>`) quando a página termina a renderização.\n- [[yii\\web\\View::beginBody()|beginBody()]]: Este método deve ser chamado no início\n  da seção `<body>` . Ele dispara o evento [[yii\\web\\View::EVENT_BEGIN_BODY|EVENT_BEGIN_BODY]] e gera um marcador que será substituído por código HTML que estiver registrado para essa posição (por exemplo, algum código JavaScript).\n- [[yii\\web\\View::endBody()|endBody()]]: Este método deve ser chamado no final da\n  seção `<body>`. Ele dispara o evento [[yii\\web\\View::EVENT_END_BODY|EVENT_END_BODY]]\n  e gera um marcador que será substituído por código HTML que estiver registrado para essa posição (por exemplo, algum código JavaScript).\n\n\n### Acessando Dados nos Layouts <span id=\"accessing-data-in-layouts\"></span>\n\nDentro de um layout, você tem acesso a duas variáveis predefinidas: `$this` e\n`$content`. A primeira se refere ao componente [[yii\\base\\View|view]] como em views normais, enquanto a segunda contém o resultado da renderização do conteúdo\nde uma view que é gerada por chamar o método [[yii\\base\\Controller::render()|render()]]\nno controller.\n\nSe você quiser acessar outros dados nos layouts, você terá de usar a abordagem\n*pull* conforme descrito na subseção\n[Acessando Dados em Views ](#accessing-data-in-views). Se você quiser\npassar os dados do conteúdo da view para um layout, poderá usar o método descrito\nna subseção [Compartilhando Dados entre as Views ](#sharing-data-among-views).\n\n\n### Usando Layouts <span id=\"using-layouts\"></span>\n\nConforme descrito na subseção [Renderização em Controllers](#rendering-in-controllers),\nquando você renderiza uma view chamando o método [[yii\\base\\Controller::render()|render()]]\nem um controller, será aplicado um layout ao resultado da renderização. Por padrão, o layout `@app/views/layouts/main.php` será usado.\n\nVocê pode usar um layout diferente configurando ou a propriedade\n[[yii\\base\\Application::layout]] ou a [[yii\\base\\Controller::layout]].\nA primeira especifica o layout usado por todos os controllers,\nenquanto a segunda é usada para controllers de forma individual, sobrescrevendo a primeira.\nPor exemplo, o código a seguir faz com que o controller `post` usar o\n`@app/views/layouts/post.php` como layout quando renderizar as suas views.\nOutros controllers, assumindo que a propriedade `layout` da\naplicação não tenha sido alterada, usarão o layout padrão `@app/views/layouts/main.php`.\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass PostController extends Controller\n{\n    public $layout = 'post';\n\n    // ...\n}\n```\n\nPara os controllers que pertencem a um módulo, você também pode configurar\na propriedade [[yii\\base\\Module::layout|layout]] do módulo para usar um layout em\nparticular para esses controllers.\n\nVisto que a propriedade `layout` pode ser configurada em diferentes níveis\n(controllers, módulos, aplicação), por trás das cortinas o Yii determina, em duas\netapas, qual arquivo de layout será usado por um controller em particular.\n\nNa primeira etapa, o Yii determina o valor da propriedade do layout e o módulo de contexto:\n\n- Se a propriedade [[yii\\base\\Controller::layout]] do controller\n  não for `null`, ela será usada e o [[yii\\base\\Controller::module|módulo]]\n  do controller será usado como módulo de contexto.\n- Se a propriedade [[yii\\base\\Controller::layout|layout]] for `null`, o Yii pesquisará\n  através de todos os módulos ancestrais do\n  controller (incluindo a própria aplicação) até encontrar o primeiro módulo cuja propriedade\n  [[yii\\base\\Module::layout|layout]] não for `null`. O módulo encontrado será\n  usado como módulo de contexto e o valor de sua propriedade [[yii\\base\\Module::layout|layout]]\n  como o layout escolhido. Se nenhum módulo for encontrado, nenhum\n  layout será aplicado.\n\nNa segunda etapa, o Yii determina o real arquivo de layout de acordo com o valor da propriedade layout e com o modulo de contexto obtidos na primeira etapa. O valor da propriedade layout pode ser:\n\n- uma alias de caminho (por exemplo, `@app/views/layouts/main`).\n- um caminho absoluto (por exemplo, `/main`): o valor começa com uma\n  barra. O arquivo de layout será procurado sob o\n  [[yii\\base\\Application::layoutPath|diretório de layouts]] da aplicação, cujo valor padrão é `@app/views/layouts`.\n- um caminho relativo (por exemplo, `main`): o arquivo de layout será procurado\n  sob o [[yii\\base\\Module::layoutPath|diretório de layouts]] do módulo de contexto,\n  cujo valor padrão é `views/layouts` sob o [[yii\\base\\Module::basePath|diretório do módulo]].\n- um valor booleano `false`: nenhum layout será aplicado.\n\nSe o valor da propriedade layout não tiver uma extensão de arquivo, será usada a extensão `.php` por padrão.\n\n\n### Layouts Aninhados <span id=\"nested-layouts\"></span>\n\nÀs vezes, você pode querer que um layout seja usado dentro de outro. Por\nexemplo, em diferentes seções de um site, você pode querer usar diferentes layouts, e todos esses layouts compartilharão o mesmo layout básico a fim de produzir\ntoda a estrutura da página HTML. Você pode fazer isso por chamar\nos métodos [[yii\\base\\View::beginContent()|beginContent()]] e\n[[yii\\base\\View::endContent()|endContent()]] nos layouts filhos, como no\nexemplo a seguir:\n\n```php\n<?php $this->beginContent('@app/views/layouts/base.php'); ?>\n\n...conteúdo do layout filho aqui...\n\n<?php $this->endContent(); ?>\n```\n\nComo mostrado acima, o conteúdo do layout filho deve ser inserido entre os métodos [[yii\\base\\View::beginContent()|beginContent()]] e\n[[yii\\base\\View::endContent()|endContent()]]. O parâmetro passado para o\n[[yii\\base\\View::beginContent()|beginContent()]] indica qual é o layout pai. Ele\npode ser um arquivo de layout ou mesmo um alias.\n\nUsando a abordagem acima, você pode aninhar os layouts em mais de um nível.\n\n\n### Usando Blocos <span id=\"using-blocks\"></span>\n\nBlocos permitem que você especifique o conteúdo da view em um local e o exiba\nem outro. Geralmente são usados em conjunto com os layouts. Por exemplo, você pode\ndefinir um bloco no conteúdo de uma view e exibi-lo no layout.\n\nPara definir um bloco, chame os métodos [[yii\\base\\View::beginBlock()|beginBlock()]]\ne [[yii\\base\\View::endBlock()|endBlock()]].\nO bloco pode então ser acessado via `$view->blocks[$blockID]`, onde o `$blockID` é o\nidentificador único que você associou ao bloco quando o definiu.\n\nO exemplo a seguir mostra como você pode usar blocos para personalizar as partes\nespecificas de um layout pelo conteúdo da view.\n\nPrimeiramente, no conteúdo da view, defina um ou vários blocos:\n\n```php\n...\n\n<?php $this->beginBlock('bloco1'); ?>\n\n...conteúdo do bloco1...\n\n<?php $this->endBlock(); ?>\n\n...\n\n<?php $this->beginBlock('bloco3'); ?>\n\n... conteúdo do bloco3...\n\n<?php $this->endBlock(); ?>\n```\n\nEm seguida, no layout, renderize os blocos se estiverem disponíveis\nou exiba um conteúdo padrão se não estiverem.\n\n```php\n...\n<?php if (isset($this->blocks['bloco1'])): ?>\n    <?= $this->blocks['bloco1'] ?>\n<?php else: ?>\n    ... conteúdo padrão para o bloco1 ...\n<?php endif; ?>\n\n...\n\n<?php if (isset($this->blocks['bloco2'])): ?>\n    <?= $this->blocks['bloco2'] ?>\n<?php else: ?>\n    ... conteúdo padrão para o bloco2 ...\n<?php endif; ?>\n\n...\n\n<?php if (isset($this->blocks['bloco3'])): ?>\n    <?= $this->blocks['bloco3'] ?>\n<?php else: ?>\n    ... conteúdo padrão para o bloco3 ...\n<?php endif; ?>\n...\n```\n\n\n## Usando Componentes View  <span id=\"using-view-components\"></span>\n\nOs [[yii\\base\\View|componentes view]] fornecem muitos recursos. Embora você possa obtê-los por criar instancias individuais de [[yii\\base\\View]]\nou de suas classes filhas, na maioria dos casos você usará o\ncomponente `view` da aplicação. Você pode configurar este componente nas\n[configurações da aplicação](structure-applications.md#application-configurations)\nconforme o exemplo a seguir:\n\n```php\n[\n    // ...\n    'components' => [\n        'view' => [\n            'class' => 'app\\components\\View',\n        ],\n        // ...\n    ],\n]\n```\n\nComponentes de view fornecem úteis recursos relacionados. Cada um deles está descrito com mais detalhes em seções separadas:\n\n* [temas](output-theming.md): permite que você desenvolva e altere temas para\n  o seu site.\n* [fragmento de cache](caching-fragment.md): permite que você guarde em cache um\n  fragmento de uma página.\n* [manipulação de client scripts](output-client-scripts.md): permite que você registre e renderize CSS e JavaScript.\n* [manipulando asset bundle](structure-assets.md): permite que você registre e renderize [recursos estáticos (asset bundles)](structure-assets.md).\n* [template engines alternativos](tutorial-template-engines.md): permite que você use outros template engines, tais como o [Twig](https://twig.symfony.com/)\n  e [Smarty](https://www.smarty.net/).\n\nVocê também pode usar os seguintes recursos que, embora simples, são úteis quando estiver desenvolvendo suas páginas.\n\n\n### Configurando Títulos de Página <span id=\"setting-page-titles\"></span>\n\nCada página deve ter um título. Normalmente, a tag `<title>` é exibida em um [layout](#layouts). Mas, na prática, o título é muitas vezes determinado\nno conteúdo das views, em vez de nos layouts. Para resolver este\nproblema, a classe [[yii\\web\\View]] fornece a propriedade [[yii\\web\\View::title|title]] para você passar o título a partir das views para o layout.\n\nPara fazer uso deste recurso, em cada view, você pode definir o título da\npágina conforme o exemplo a seguir:\n\n```php\n<?php\n$this->title = 'Título da Minha Página';\n?>\n```\n\nE, no layout, certifique-se de ter o seguinte código dentro da seção `<head>`:\n\n```php\n<title><?= Html::encode($this->title) ?></title>\n```\n\n\n### Registrando os Meta Tags <span id=\"registering-meta-tags\"></span>\n\nPáginas Web geralmente precisam gerar variadas meta tags necessárias a\ndiversas finalidades. Assim como os títulos, as meta tags precisam estar na seção\n`<head>` e normalmente são geradas nos layouts.\n\nSe você quiser especificar quais meta tags gerar no conteúdo das views, poderá chamar o método [[yii\\web\\View::registerMetaTag()]]\nna view, conforme o exemplo a seguir:\n\n```php\n<?php\n$this->registerMetaTag(['name' => 'keywords', 'content' => 'yii, framework, php']);\n?>\n```\n\nO código acima registrará uma meta tag \"keywords\" com o componente view.\nA meta tag registrada será renderizadas depois de o layout finalizar sua renderização. O código HTML a seguir será gerado e inserido no local onde você chama [[yii\\web\\View::head()]] no layout:\n\n```php\n<meta name=\"keywords\" content=\"yii, framework, php\">\n```\n\nObserve que se você chamar o método [[yii\\web\\View::registerMetaTag()]] muitas vezes, ele registrará diversas meta tags, independente se forem as mesmas ou não.\n\nPara garantir que exista apenas uma única instância de um tipo de meta tag,\nvocê pode especificar uma chave no segundo parâmetro ao chamar o método.\nPor exemplo, o código a seguir registra dois meta tags \"description\". No entanto,\napenas o segundo será renderizado.\n\n```php\n$this->registerMetaTag(['name' => 'description', 'content' => 'Este é o meu website feito com Yii!'], 'descricao');\n$this->registerMetaTag(['name' => 'description', 'content' => 'Este website é sobre coisas divertidas.'], 'descricao');\n```\n\n\n### Registrando Tags Link <span id=\"registering-link-tags\"></span>\n\nAssim como as [meta tags](#registering-meta-tags), as tags link são úteis em muitos\ncasos, tais como a personalização do favicon, apontamento para feed RSS ou delegação do OpenID para outros servidores.\nVocê pode trabalhar com as tags link de forma similar às meta tags, usando o método [[yii\\web\\View::registerLinkTag()]]. Por\nexemplo, na view, você pode registrar uma tag link como segue:\n\n```php\n$this->registerLinkTag([\n    'title' => 'Notícias sobre o Yii',\n    'rel' => 'alternate',\n    'type' => 'application/rss+xml',\n    'href' => 'https://www.yiiframework.com/rss.xml/',\n]);\n```\n\nO código acima resultará em\n\n```html\n<link title=\"Notícias sobre o Yii\" rel=\"alternate\" type=\"application/rss+xml\" href=\"https://www.yiiframework.com/rss.xml/\">\n```\n\nAssim como no método [[yii\\web\\View::registerMetaTag()|registerMetaTags()]],\nvocê também pode especificar uma chave quando chamar o método\n[[yii\\web\\View::registerLinkTag()|registerLinkTag()]] para evitar a criação de\ntags link repetidas.\n\n\n## Eventos da View  <span id=\"view-events\"></span>\n\n[[yii\\base\\View|Componentes view]] disparam vários eventos durante\no processo de renderização da view. Você pode usar estes eventos para\ninserir conteúdo nas views ou processar os resultados da renderização\nantes de serem enviados para os usuários finais.\n\n- [[yii\\base\\View::EVENT_BEFORE_RENDER|EVENT_BEFORE_RENDER]]: disparado no\n  início da renderização de um arquivo em um controller. Funções registradas para esse evento (handlers) podem\n  definir a propriedade [[yii\\base\\ViewEvent::isValid]] como `false` para\n  cancelar o processo de renderização.\n- [[yii\\base\\View::EVENT_AFTER_RENDER|EVENT_AFTER_RENDER]]: disparado depois\n  da renderização de um arquivo pela chamada de [[yii\\base\\View::afterRender()]].\n  Funções registradas para esse evento (handlers) podem capturar o resultado da renderização por meio da propriedade\n  [[yii\\base\\ViewEvent::output]] e podem modificá-lo para alterar o resultado final.\n- [[yii\\base\\View::EVENT_BEGIN_PAGE|EVENT_BEGIN_PAGE]]: disparado pela chamada\n  do método [[yii\\base\\View::beginPage()]] nos layouts.\n- [[yii\\base\\View::EVENT_END_PAGE|EVENT_END_PAGE]]: disparado pela chamada do\n  método [[yii\\base\\View::endPage()]] nos layouts.\n- [[yii\\web\\View::EVENT_BEGIN_BODY|EVENT_BEGIN_BODY]]: disparado pela chamada\n  do método [[yii\\web\\View::beginBody()]] nos layouts.\n- [[yii\\web\\View::EVENT_END_BODY|EVENT_END_BODY]]: disparado pela chamada do\n  método [[yii\\web\\View::endBody()]] nos layouts.\n\nPor exemplo, o código a seguir insere a data atual no final do corpo da página:\n\n```php\n\\Yii::$app->view->on(View::EVENT_END_BODY, function () {\n    echo date('Y-m-d');\n});\n```\n\n\n## Renderizando Páginas Estáticas <span id=\"rendering-static-pages\"></span>\n\nPáginas estáticas referem-se a páginas cujo principal conteúdo é, na maior parte,\nestático, sem a necessidade de acessar dados dinâmicos provenientes dos controllers.\n\nVocê pode retornar páginas estáticas por colocar seu código na view e então, em um controller, usar o código a seguir:\n\n```php\npublic function actionAbout()\n{\n    return $this->render('about');\n}\n```\n\nSe o site contiver muitas páginas estáticas, seria tedioso repetir\nos códigos similares muitas vezes.\nPara resolver este problema, você pode inserir uma\n[action \"externa\" (standalone action)](structure-controllers.md#standalone-actions) chamando a classe\n[[yii\\web\\ViewAction]] em um controller. Por exemplo:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actions()\n    {\n        return [\n            'page' => [\n                'class' => 'yii\\web\\ViewAction',\n            ],\n        ];\n    }\n}\n```\n\nAgora, se você criar uma view chamada `sobre` no diretório\n`@app/views/site/pages`, poderá exibir por meio da\nseguinte URL:\n\n```\nhttp://localhost/index.php?r=site/page&view=sobre\n```\n\nO parâmetro `view` passado via `GET` informa à classe [[yii\\web\\ViewAction]]\nqual view foi solicitada. A action, então, irá procurar essa view informada\ndentro do diretório `@app/views/site/pages`. Você pode configurar a propriedade\n[[yii\\web\\ViewAction::viewPrefix]] para alterar o diretório onde as views\nserão procuradas.\n\n\n## Boas Práticas <span id=\"best-practices\"></span>\n\nViews são responsáveis por apresentar models (modelos) no formato que os\nusuários finais desejam. Em geral, views:\n\n* devem conter principalmente código de apresentação, tal como o HTML, e trechos\n  simples de PHP para percorrer, formatar e renderizar dados.\n* não devem conter código de consulta ao banco de dados. Consultas assim devem ser feitas nos models.\n* devem evitar acessar diretamente os dados da requisição, tais como `$_GET` e\n  `$_POST` pois essa tarefa cabe aos controllers.\n  Se os dados da requisição forem necessários, deverão ser fornecidos às views\n  pelos controllers.\n* podem ler as propriedades dos models, mas não devem alterá-las.\n\nPara tornar as views mais gerenciáveis, evite criar views muito complexas\nou que contenham muito código redundante. Você pode usar as seguintes técnicas\npara atingir este objetivo:\n\n* use [layouts](#layouts) para representar as seções de apresentação comuns\n  (por exemplo, cabeçalho e rodapé).\n* divida uma view complicada em varias outras menores. As views\n  menores podem ser renderizadas e montadas em uma maior usando os métodos descritos anteriormente.\n* crie e use [widgets](structure-widgets.md) como blocos de construção das views.\n* crie e use as classes helper (auxiliares) para transformar e formatar os dados nas views.\n"
  },
  {
    "path": "docs/guide-pt-BR/structure-widgets.md",
    "content": "Widgets\n=======\n\nOs widgets são blocos de construção reutilizáveis usados nas \n[views (visões)](structure-views.md) para criar e configurar complexos elementos \nde interface do usuário sob uma modelagem orientada a objetos. Por exemplo, um \nwidget datapicker pode gerar um calendário que permite aos usuários selecionarem \numa data que desejam inserir em um formulário. Tudo o que você precisa fazer é \napenas inserir um código na view (visão) conforme o seguinte:\n\n```php\n<?php\nuse yii\\jui\\DatePicker;\n?>\n<?= DatePicker::widget(['name' => 'date']) ?>\n```\n\nExiste uma quantidade considerável de widgets empacotados no Yii, como o \n[[yii\\widgets\\ActiveForm|active form]], o [[yii\\widgets\\Menu|menu]], o \n[jQuery UI widgets](widget-jui.md), o [Twitter Bootstrap widgets](widget-bootstrap.md), etc.\nA seguir, iremos introduzir os conhecimentos básicos sobre os widgets. Por favor,\nconsulte a documentação de classes da API se você quiser saber mais sobre o uso de um determinado widget.\n\n\n## Usando Widgets <span id=\"using-widgets\"></span>\n\nOs widgets são usados principalmente nas [views (visões)](structure-views.md). \nVocê pode chamar o método [[yii\\base\\Widget::widget()]] para usar um widget em \numa view (visão). O método possui um array de [configuração](concept-configurations.md) \npara inicializar o widget e retornar o resultado da renderização do widget. Por \nexemplo, o código a seguir insere um widget datapicker configurado para usar o \nidioma Russo e manter a data selecionada no atributo `from_date` do `$model`.\n\n```php\n<?php\nuse yii\\jui\\DatePicker;\n?>\n<?= DatePicker::widget([\n    'model' => $model,\n    'attribute' => 'from_date',\n    'language' => 'ru',\n    'clientOptions' => [\n        'dateFormat' => 'yy-mm-dd',\n    ],\n]) ?>\n```\n\nAlguns widgets podem ter um bloco de conteúdo que deve ser colocado entre as \nchamadas dos métodos [[yii\\base\\Widget::begin()]] e [[yii\\base\\Widget::end()]]. \nPor exemplo, o código a seguir usa o widget [[yii\\widgets\\ActiveForm]] para gerar\num formulário de login. O widget irá gerar as tags de abertura e de fechamento \ndo `<form>` respectivamente nos lugares onde os métodos `begin()` e `end()` foram \nchamados. Qualquer conteúdo entre estes métodos serão renderizados entre as tags \nde abertura e de fechamento do `<form>`.\n\n```php\n<?php\nuse yii\\widgets\\ActiveForm;\nuse yii\\helpers\\Html;\n?>\n\n<?php $form = ActiveForm::begin(['id' => 'login-form']); ?>\n\n    <?= $form->field($model, 'username') ?>\n\n    <?= $form->field($model, 'password')->passwordInput() ?>\n\n    <div class=\"form-group\">\n        <?= Html::submitButton('Login') ?>\n    </div>\n\n<?php ActiveForm::end(); ?>\n```\n\nObserve que ao contrário do [[yii\\base\\Widget::widget()]] que retorna a \nrenderização de um widget, o método [[yii\\base\\Widget::begin()]] retorna uma \ninstância do widget que pode ser usado para construir o seu conteúdo.\n\n\n## Criando Widgets <span id=\"creating-widgets\"></span>\n\nPara criar um widget, estenda a classe [[yii\\base\\Widget]] e sobrescreva os \nmétodos [[yii\\base\\Widget::init()]] e/ou [[yii\\base\\Widget::run()]]. Normalmente, \no método `init()` deve conter os códigos que normalizam as propriedade do widget, \nenquanto o método `run()` deve conter o código que gera o resultado da renderização \ndo widget. O resultado da renderização pode ser feito diretamente dando \"echo\" ou \npelo retorno de uma string no método `run()`.\n\nNo exemplo a seguir, o `HelloWidget` codifica o HTML e exibe o conteúdo atribuído \nà sua propriedade `message`. Se a propriedade não for definida, será exibido \n\"Hello World\" como padrão.\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Widget;\nuse yii\\helpers\\Html;\n\nclass HelloWidget extends Widget\n{\n    public $message;\n\n    public function init()\n    {\n        parent::init();\n        if ($this->message === null) {\n            $this->message = 'Hello World';\n        }\n    }\n\n    public function run()\n    {\n        return Html::encode($this->message);\n    }\n}\n```\n\nPara usar este widget, simplesmente insira o código a seguir em uma view (visão):\n\n```php\n<?php\nuse app\\components\\HelloWidget;\n?>\n<?= HelloWidget::widget(['message' => 'Good morning']) ?>\n```\n\nO `HelloWidget` abaixo é uma variante que pega o conteúdo entre as chamadas de \n`begin()` e `end()`, codifica o HTML e em seguida os exibe.\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Widget;\nuse yii\\helpers\\Html;\n\nclass HelloWidget extends Widget\n{\n    public function init()\n    {\n        parent::init();\n        ob_start();\n    }\n\n    public function run()\n    {\n        $content = ob_get_clean();\n        return Html::encode($content);\n    }\n}\n```\n\nComo você pode ver, o buffer de saída do PHP é iniciado no método `init()` para \nque qualquer conteúdo entre as chamadas de `init()` e `run()` possam ser capturadas, \nprocessadas e retornadas em `run()`.\n\n> Informação: Ao chamar o [[yii\\base\\Widget::begin()]], uma nova instância do \n  widget será criada e o método `init()` será chamado logo ao final de seu construtor.\n  Ao chamar o [[yii\\base\\Widget::end()]], o método `run()` será chamado cujo o \n  resultado da renderização será dado *echo* pelo `end()`.\n\nO código a seguir mostra como você pode usar esta nova variante do `HelloWidget`:\n\n```php\n<?php\nuse app\\components\\HelloWidget;\n?>\n<?php HelloWidget::begin(); ?>\n\n    um conteúdo qualquer...\n\n<?php HelloWidget::end(); ?>\n```\n\nAlgumas vezes, um widget pode precisar renderizar um grande conteúdo. Enquanto \nvocê pode inserir todo este conteúdo no método `run()`, uma boa prática é \ncolocá-lo em uma [view (visão)](structure-views.md) e chamar o \n[[yii\\base\\Widget::render()]] para renderizá-lo. Por exemplo,\n\n```php\npublic function run()\n{\n    return $this->render('hello');\n}\n```\n\nPor padrão, as views (visões) para um widget devem ser armazenadas em arquivos \nsob o diretório `WidgetPath/views`, onde o `WidgetPath` significa o diretório \nque contém os arquivo da classe do widget. Portanto, o exemplo anterior irá \nrenderizar o arquivo de view (visão) `@app/components/views/hello.php`, assumindo \nque a classe widget está localizada sob o diretório `@app/components`. Você pode \nsobrescrever o método [[yii\\base\\Widget::getViewPath()]] para personalizar o \ndiretório que conterá os arquivos de views (visões) do widget.\n\n\n## Boas Práticas <span id=\"best-practices\"></span>\n\nOs widgets são uma maneira orientada a objetos de reutilizar códigos de view (visão).\n\nAo criar os widgets, você ainda deve seguir o padrão MVC. Em geral, você deve \nmanter a lógica nas classes widgets e manter as apresentações nas \n[views (visões)](structure-views.md).\n\nOs widgets devem ser projetados para serem autossuficientes. Isto é, ao utilizar \num widget, você deverá ser capaz de removê-lo de uma view (visão) sem fazer \nqualquer outra coisa. Isto pode ser complicado se um widget requerer recursos \nexternos, tais como CSS, JavaScript, imagens, etc. Felizmente, o Yii fornece o \nsuporte para [asset bundles](structure-assets.md), que pode ser utilizado para \nresolver este problema.\n\nQuando um widget contiver somente código de view (visão), será bem semelhante a \numa [view (visão)](structure-views.md). Na verdade, neste caso, a única diferença \né que um widget é uma classe para ser redistribuída, enquanto uma view é apenas \num simples script PHP que você preferirá manter em sua aplicação\n"
  },
  {
    "path": "docs/guide-pt-BR/test-overview.md",
    "content": "Testes\n=======\n\nO teste é uma parte importante do desenvolvimento de software. Se estamos conscientes disso ou não, Realizamos testes continuamente.\nPor exemplo, enquanto escrevemos uma classe PHP, podemos depurá-lo passo a passo ou simplismente usar declarações echo ou die para verificar se a implantação está de acordo com nosso plano inicial. \nNo caso de uma aplicação web, estamos entrando em alguns  testes de dados em forma de assegurar que a página interage com a gente como esperado.\n\nO processo de teste pode ser automatizado de modo que cada momento em que precisamos para verificar alguma coisa, só precisamos chamar o código que faz isso por nós. O código que verifica o resultado coincide com o que temos planejado é chamado de teste e o processo de sua criação e posterior execução é conhecido como teste automatizado,\nque é o principal tema destes capítulos de testes.\n\n\nDesenvolvendo com testes\n------------------\n\nTest-Driven Development (TDD), and Behavior-Driven Development (BDD) são abordagens de desenvolvimento de software, \ndescrevendo o comportamento de um trecho de código ou todo o recurso como um conjunto de cenários ou testes antes de escrever \ncódigo real e só então criar a aplicação que permite que estes testes passem verificando se comportamento a que se destina é conseguido.\n\nO processo de desenvolvimento de uma funcionalidade é a seguinte:\n\n- Criar um novo teste que descreve uma funcionalidade a ser implementada.\n- Execute o novo teste e verifique se ele falha. isto é esperado já que não há nenhuma implementação ainda.\n- Escrever um código simples para fazer o novo teste passar.\n- Executar todos os testes e garantir que todos eles passam.\n- Melhorar código e certificar-se de testes ainda estão OK.\n\nDepois feito o processo é repetido novamente para outras funcionalidades ou melhorias. \nSe uma funcionalidade existente deve ser alterada, os testes devem ser mudadas também.\n\n> Dica:  Se você sentir que você está perdendo tempo fazendo um monte de pequenas e simples iterações, experimente cobrindo mais por você.\n> Cenários de teste é para que você faça mais antes de executar testes novamente. Se você está depurando muito, tente fazer o oposto.\n\nA razão para criar testes antes de fazer qualquer implementação é que ela nos permite focar no que queremos alcançar e totalmente mergulhar \"como fazê-lo\" depois.\nNormalmente, leva a melhores abstrações e manutenção de teste mais fácil quando se trata de ajustes na funcionalidade ou de menos componentes acoplados.\n  \nAssim, para resumir as vantagens de tal abordagem são as seguintes:\n\n- Mantém-se focado em uma coisa de cada vez que resulta em uma melhor planejamento e implementação.\n- Resultados cobertos por testes para mais funcionalidade em maior detalhe, ou seja, se os testes são OK provavelmente nada está quebrado.\n\nNo longo prazo, geralmente, dá-lhe um boa melhoria na produtividade.\n\n> Dica: Se você quiser saber mais sobre os princípios de levantamento de requisitos de software e modelagem do assunto\n> esta é uma referência boa para aprender [Domain Driven Development (DDD)] (https://en.wikipedia.org/wiki/Domain-driven_design).\n\nQuando e como testar\n------------------\n\nEnquanto a primeira abordagem de teste descrito acima faz sentido em longo prazo para projetos relativamente complexos e que seria um exagero\npara os mais simples. Existem alguns indicadores de quando é apropriado:\n\n- Projeto já é grande e complexo.\n- Requisitos do projeto estão começando a ficarem complexos. Projeto cresce constantemente.\n- Projeto pretende ser a longo prazo.\n- O custo da falha é muito alta.\n\nNão há nada errado na criação de testes que abrangem o comportamento de implementação existente.\n\n- Projeto é um legado para ser gradualmente renovada.\n- Você tem um projeto para trabalhar e não tem testes.\n\nEm alguns casos, qualquer forma de teste automatizado poderia ser um exagero:\n\n- Projeto é simples e não está ficando mais complexo.\n- É um projeto emporal eque deixarão de trabalhar nele.\n\nAinda assim, se você tiver tempo é bom automatizar testes nestes casos também.\n\nOutras leituras\n-------------\n\n- Test Driven Development: By Example / Kent Beck. ISBN: 0321146530.\n"
  },
  {
    "path": "docs/guide-pt-BR/translators.json",
    "content": "[\n  \"Alcir Monteiro\",\n  \"Alan Michel Willms Quinot\",\n  \"Davidson Alencar\",\n  \"Gustavo G. Andrade\",\n  \"Jan Silva\",\n  \"Lucas Barros\",\n  \"Raphael de Almeida\",\n  \"Sidney da Silva Lins\",\n  \"Wanderson Bragança\",\n  \"Anthony Tesche\"\n]\n"
  },
  {
    "path": "docs/guide-pt-BR/tutorial-core-validators.md",
    "content": "Validadores Nativos\n==================\n\nO Yii fornece um conjunto de validadores nativos bastante utilizados, encontrados principalmente sob o namespace `yii\\validators`. Em vez de usar nomes longos nas classes de validadores, você pode usar *aliases* para especificar o uso desses validadores. Por exemplo, você pode usar o alias `required` para referenciar a classe [[yii\\validators\\RequiredValidator]]:\n\n```php\npublic function rules()\n{\n  return [\n      [['email', 'password'], 'required'],\n  ];\n}\n```\n\nA propriedade [[yii\\validators\\Validator::builtInValidators]] declara todos os aliases de validação suportados.\n\nA seguir, descreveremos o uso principal e as propriedades de cada um desses validadores.\n\n\n## [[yii\\validators\\BooleanValidator|boolean]] <span id=\"boolean\"></span>\n\n```php\n[\n  // Verifica se \"selected\" é 0 ou 1, independentemente do tipo de dados\n  ['selected', 'boolean'],\n\n  // verifica se \"deleted\" é um tipo boolean, e se é verdadeiro ou falso\n  ['deleted', 'boolean', 'trueValue' => true, 'falseValue' => false, 'strict' => true],\n]\n```\n\nEste validador verifica se o valor de entrada é um booleano.\n\n- `trueValue`: o valor representando *true*. O padrão é `'1'`.\n- `falseValue`: o valor representando *false*. O padrão é `'0'`.\n- `strict`: se o tipo do valor de entrada deve corresponder ao `trueValue` e `falseValue`. O padrão é `false`.\n\n\n> Observação: Como a entrada de dados enviados através de formulários HTML são todos strings, normalmente deverá deixar a propriedade [[yii\\validators\\BooleanValidator::strict|strict]] como `false`.\n\n\n## [[yii\\captcha\\CaptchaValidator|captcha]] <span id=\"captcha\"></span>\n\n```php\n[\n  ['verificationCode', 'captcha'],\n]\n```\n\nEste validador é geralmente usado junto com [[yii\\captcha\\CaptchaAction]] e [[yii\\captcha\\Captcha]] para garantir que a entrada de dados seja igual ao código de verificação exibido pelo widget [[yii\\captcha\\Captcha|CAPTCHA]].\n\n- `caseSensitive`: se a comparação da verificação de código for case sensitivo. O padrão é `false`.\n- `captchaAction`: a [rota](structure-controllers.md#routes) correspondente à [[yii\\captcha\\CaptchaAction|ação CAPTCHA]] que renderiza as imagens. O padrão é `'site/captcha'`.\n- `skipOnEmpty`: se a validação pode ser ignorada se a entrada estiver vazia. O padrão é `false`,\no que significa que a entrada é obrigatória.\n\n\n## [[yii\\validators\\CompareValidator|compare]] <span id=\"compare\"></span>\n\n```php\n[\n  // valida se o valor do atributo \"password\"  é igual a \"password_repeat\"\n  ['password', 'compare'],\n\n  // valida se a idade é maior do que ou igual a 30\n  ['age', 'compare', 'compareValue' => 30, 'operator' => '>='],\n]\n```\n\nEste validador compara o valor de entrada especificado com um outro e certifica se a sua relação está como especificado pela propriedade `operator`.\n\n- `compareAttribute`: o nome do atributo cujo valor deve ser comparado. Quando o validador está sendo usado para validar um atributo, o valor padrão dessa propriedade seria o nome do atributo com o sufixo `_repeat`. Por exemplo, se o atributo que está sendo validado é `password`, então esta propriedade será por padrão `password_repeat`.\n- `compareValue`: um valor constante com o qual o valor de entrada deve ser comparado. Quando esta propriedade e a propriedade `compareAttribute` forem especificadas, a propriedade `compareValue` terá precedência.\n- `operator`: o operador de comparação. O padrão é `==`, ou seja, verificar se o valor de entrada é igual ao do `compareAttribute` ou `compareValue`. Os seguintes operadores são suportados:\n   * `==`: verifica se dois valores são iguais. A comparação é feita no modo non-strict.\n   * `===`: verifica se dois valores são iguais. A comparação é feita no modo strict.\n   * `!=`: verifica se dois valores NÃO são iguais. A comparação é feita no modo non-strict.\n   * `!==`: verifica se dois valores NÃO são iguais. A comparação é feita no modo strict.\n   * `>`: verifica se o valor que está sendo validado é maior do que o valor que está sendo comparado.\n   * `>=`: verifica se o valor que está sendo validado é maior ou igual ao valor que está sendo comparado.\n   * `<`: verifica se o valor que está sendo validado é menor do que o valor que está sendo comparado.\n   * `<=`: verifica se o valor que está sendo validado menor ou igual ao valor que está sendo comparado.\n\n\n## [[yii\\validators\\DateValidator|date]] <span id=\"date\"></span>\n\n```php\n[\n  [['from_date', 'to_date'], 'date'],\n]\n```\n\nEste validador verifica se o valor de entrada é uma data, hora ou data e hora em um formato adequado. Opcionalmente, pode converter o valor de entrada para um UNIX timestamp ou outro formato legível e armazená-lo em um atributo especificado via [[yii\\validators\\DateValidator::timestampAttribute|timestampAttribute]].\n\n- `format`: o formato date/time que o valor que está sendo validado deve ter. Este pode ser um padrão de data e hora conforme descrito no [ICU manual] (https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax). Alternativamente esta pode ser uma string com o prefixo `php:` representando um formato que pode ser reconhecido pela classe PHP `Datetime`. Por favor, consulte <https://www.php.net/manual/pt_BR/datetime.createfromformat.php> para formatos suportados. Se isso não for definido, ele terá o valor de `Yii::$app->formatter->dateFormat`. Consulte a [[yii\\validators\\DateValidator::$format|documentação da API]] para mais detalhes.\n\n- `timestampAttribute`: o nome do atributo para que este validador possa atribuir o UNIX  timestamp convertido a partir da entrada de data / hora. Este pode ser o mesmo atributo que está sendo validado. Se este for o caso,  valor original será substituído pelo valor timestamp após a validação. Veja a seção [\"Manipulando Datas com DatePicker\"] (https://github.com/yiisoft/yii2-jui/blob/master/docs/guide/topics-date-picker.md) para exemplos de uso.\n\nDesde a versão 2.0.4, um formato e um fuso horário podem ser especificados utilizando os atributos [[yii\\validators\\DateValidator::$timestampAttributeFormat|$timestampAttributeFormat]] e [[yii\\validators\\DateValidator::$timestampAttributeTimeZone|$timestampAttributeTimeZone]], respectivamente.\n\n- Desde a versão 2.0.4 também é possível definir um timestamp [[yii\\validators\\DateValidator::$min|minimum]] ou [[yii\\validators\\DateValidator::$max|maximum]].\n\nCaso a entrada de dados seja opcional (preenchimento não obrigatório), você também pode querer adicionar um [filtro chamado default](#default) para o validador de data garantir que entradas vazias sejam armazenadas com `NULL`. De outra forma você pode terminar com datas como `0000-00-00` no seu banco de dados ou `1970-01-01` no campo de entrada de um *date picker*.\n\n```php\n[\n  [['from_date', 'to_date'], 'default', 'value' => null],\n  [['from_date', 'to_date'], 'date'],\n],\n```\n\n\n## [[yii\\validators\\DefaultValueValidator|default]] <span id=\"default\"></span>\n\n```php\n[\n  // configura \"age\" para ser null se este for vazio\n  ['age', 'default', 'value' => null],\n\n  // configura \"country\" para ser \"USA\" se este for vazio\n  ['country', 'default', 'value' => 'USA'],\n\n  // atribui \"from\" e \"to\" com uma data de 3 dias e 6 dias a partir de hoje, se estiverem vazias\n  [['from', 'to'], 'default', 'value' => function ($model, $attribute) {\n      return date('Y-m-d', strtotime($attribute === 'to' ? '+3 days' : '+6 days'));\n  }],\n]\n```\n\nEste validador não valida dados. Em vez disso, atribui um valor padrão para os atributos que estão sendo validados caso estejam vazios.\n\n- `value`: o valor padrão ou um PHP callable que retorna o valor padrão que será atribuído aos atributos que estão sendo validados caso estejam vazios. A assinatura do PHP callable deve ser como a seguir,\n\n```php\nfunction foo($model, $attribute) {\n  // ... computar $value ...\n  return $value;\n}\n```\n\n> Observação: Como determinar se um valor está vazio ou não é um tópico separado descrito na seção [Valores Vazios](input-validation.md#handling-empty-inputs).\n\n\n## [[yii\\validators\\NumberValidator|double]] <span id=\"double\"></span>\n\n```php\n[\n  // verifica se o \"salary\" é um double\n  ['salary', 'double'],\n]\n```\n\nEste validador verifica se o valor de entrada é um double. É equivalente ao validador [number](#number).\n\n- `max`: o limite superior do valor (inclusive). Se não configurado, significa que o validador não verifica o limite superior.\n- `min`: o limite inferior do valor (inclusive). Se não configurado, significa que o validador não verifica o limite inferior.\n\n\n## [[yii\\validators\\EachValidator|each]] <span id=\"each\"></span>\n\n> Observação: Este validador está disponível desde a versão 2.0.4.\n\n```php\n[\n  // verifica se todas as categorias 'categoryIDs' são 'integer'\n  ['categoryIDs', 'each', 'rule' => ['integer']],\n]\n```\n\nEste validador só funciona com um atributo array. Ele valida *todos* os elementos do array com uma regra de validação especificada. No exemplo acima, o atributo `categoryIDs` deve ter um array e cada elemento do array será validado pela regra de validação  `integer`.\n\n- `rule`: um array especificando as regras de validação. O primeiro elemento do array determina o nome da classe ou o alias do validador. O restante dos pares nome-valor no array são utilizados para configurar o objeto do validador.\n- `allowMessageFromRule`: se pretende usar a mensagem de erro retornada pela regra de validação incorporada. Padrão é `true`. Se for `false`, ele usará `message` como a mensagem de erro.\n\n> Observação: Se o valor do atributo não for um array, a validação será considerada como falha e a `mensagem` será retornada como erro.\n\n\n## [[yii\\validators\\EmailValidator|email]] <span id=\"email\"></span>\n\n```php\n[\n  // verifica se o \"email\" é um endereço de e-mail válido\n  ['email', 'email'],\n]\n```\n\nEste validador verifica se o valor de entrada é um endereço de email válido.\n\n- `allowName`: permitir nome no endereço de email (ex. `John Smith <john.smith@example.com>`). O padrão é `false`;\n- `checkDNS`, para verificar se o domínio do e-mail existe e tem tanto um A ou registro MX. Esteja ciente de que esta verificação pode falhar devido a problemas de DNS temporários, mesmo se o endereço de e-mail for realmente válido. O padrão é `false`;\n- `enableIDN`, se o processo de validação deve verificar uma conta IDN (internationalized domain names). O padrão é `false`. Observe que para usar a validação IDN você deve instalar e habilitar a extensão PHP `intl`, caso contrário uma exceção será lançada.\n\n\n## [[yii\\validators\\ExistValidator|exist]] <span id=\"exist\"></span>\n\n```php\n[\n  // a1 precisa existir na coluna representada pelo atributo \"a1\"\n  ['a1', 'exist'],\n\n  // a1 precisa existir, mas seu valor usará a2 para verificar a existência\n  ['a1', 'exist', 'targetAttribute' => 'a2'],\n\n  // a1 e a2 precisam existir juntos, e ambos receberão mensagem de erro\n  [['a1', 'a2'], 'exist', 'targetAttribute' => ['a1', 'a2']],\n\n  // a1 e a2 precisam existir juntos, somente a1 receberá mensagem de erro\n  ['a1', 'exist', 'targetAttribute' => ['a1', 'a2']],\n\n  // a1 precisa existir, verificando a existência de ambos A2 e A3 (usando o valor de a1)\n  ['a1', 'exist', 'targetAttribute' => ['a2', 'a1' => 'a3']],\n\n  // a1 precisa existir. Se a1 for um array, então todos os seus elementos devem existir.\n  ['a1', 'exist', 'allowArray' => true],\n]\n```\n\nEste validador verifica se o valor de entrada pode ser encontrado em uma coluna representada por um atributo [Active Record](db-active-record.md). Você pode usar `targetAttribute` para especificar o atributo [Active Record](db-active-record.md) e `targetClass` a classe [Active Record](db-active-record.md) correspondente. Se você não especificá-los, eles receberão os valores do atributo e a classe model (modelo) que está sendo validada.\n\nVocê pode usar este validador para validar uma ou várias colunas (ex., a combinação de múltiplos valores de atributos devem existir).\n\n- `targetClass`: o nome da classe [Active Record](db-active-record.md) que deve ser usada para procurar o valor de entrada que está sendo validado. Se não for configurada, a atual classe do model (modelo) que está sendo validado será usada.\n- `targetAttribute`: o nome do atributo em `targetClass` que deve ser utilizado para validar a existência do valor de entrada. Se não for configurado, será usado o nome do atual atributo que está sendo validado. Você pode utilizar um array para validar a existência de múltiplas colunas ao mesmo tempo. Os valores do array são os atributos que serão utilizados para validar a existência, enquanto as chaves são os atributos cujos valores devem ser validados. Se a chave e o valor forem os mesmos, você pode especificar apenas o valor.\n- `filter`: filtro adicional para ser aplicado na consulta do banco de dados utilizada para verificar a existência do valor de entrada. Pode ser uma string ou um array representando a condição da consulta adicional (consulte o formato de condição [[yii\\db\\Query::where()]]), ou uma função anônima com a assinatura `function ($query)`, onde `$query` é o objeto  [[yii\\db\\Query|Query]] que você pode modificar.\n- `allowArray`: se permitir que o valor de entrada seja um array. Padrão é `false`. Se esta propriedade for definida como `true` e a entrada for um array, então, cada elemento do array deve existir na coluna destinada. Observe que essa propriedade não pode ser definida como `true` se você estiver validando várias colunas configurando `targetAttribute` como um array.\n\n\n## [[yii\\validators\\FileValidator|file]] <span id=\"file\"></span>\n\n```php\n[\n  // verifica se \"primaryImage\" é um arquivo de imagem carregado no formato PNG, JPG ou GIF.\n  // o tamanho do arquivo deve ser inferior a 1MB\n  ['primaryImage', 'file', 'extensions' => ['png', 'jpg', 'gif'], 'maxSize' => 1024*1024],\n]\n```\n\nEste validador verifica se o dados de entrada é um arquivo válido.\n\n- `extensions`: uma lista de extensões de arquivos que são permitidos para upload. Pode ser utilizado tanto um array quanto uma string constituída de extensões de arquivos separados por espaços ou por vírgulas (Ex. \"gif, jpg\"). Os nomes das extensões são case-insensitive. O padrão é `null`, significa que todas as extensões são permitidas.\n- `mimeTypes`: uma lista de tipos de arquivos MIME que são permitidos no upload. Pode ser utilizado tanto um array quanto uma string constituída de tipos MIME separados por espaços ou por virgulas (ex. \"image/jpeg, image/png\"). Os nomes dos tipos MIME são case-insensitivo. O padrão é `null`, significa que todos os tipos MIME são permitidos. Para mais detalhes, consulte o artigo [common media types](https://pt.wikipedia.org/wiki/Tipo_de_m%C3%ADdia_da_Internet).\n- `minSize`: o número mínimo de bytes exigido para o arquivo carregado. O padrão é `null`, significa não ter limite mínimo.\n- `maxSize`: o número máximo de bytes exigido para o arquivo carregado. O padrão é `null`, significa não ter limite máximo.\n- `maxFiles`: o número máximo de arquivos que o atributo pode receber. O padrão é 1, ou seja, a entrada de dados deve ser composto de um único arquivo. Se o `maxFiles` for maior que 1, então a entrada de dados deve ser composto por um array constituído de no máximo `maxFiles` arquivos.\n- `checkExtensionByMimeType`: verificação da extensão do arquivo por tipo MIME do arquivo. Se a extensão produzido pela verificação do tipo MIME difere da extensão do arquivo carregado, o arquivo será considerado inválido. O padrão é `true`, o que significa realizar tal verificação.\n\n`FileValidator` é usado junto com [[yii\\web\\UploadedFile]]. Consulte a seção [Upload de Arquivos](input-file-upload.md) para mais informações sobre o upload de arquivos e de uma validação sobre os arquivos carregados.\n\n\n## [[yii\\validators\\FilterValidator|filter]] <span id=\"filter\"></span>\n\n```php\n[\n  // trima as entradas \"username\" e \"email\"\n  [['username', 'email'], 'filter', 'filter' => 'trim', 'skipOnArray' => true],\n\n  // normaliza a entrada \"phone\"\n  ['phone', 'filter', 'filter' => function ($value) {\n      // normaliza a entrada phone aqui\n      return $value;\n  }],\n]\n```\n\nEste validador não valida dados. Em vez disso, aplica um filtro no valor de entrada e retorna para o atributo que está sendo validado.\n\n- `filter`: um PHP callback que define um filtro. Pode ser um nome de função global, uma função anônima, etc. A assinatura da função deve ser `function ($value) { return $newValue; }`. Esta propriedade deve ser definida.\n- `skipOnArray`: para ignorar o filtro se o valor de entrada for um array. O padrão é `false`. Observe que se o filtro não puder manipular a entrada de array, você deve configurar esta propriedade como `true`. De outra forma algum erro do PHP deve ocorrer.\n\n> Dica: Se você quiser trimar valores de entrada, você deve utilizar o validador [trim](#trim).\n\n> Dica: Existem várias funções PHP que tem a assinatura esperada para o callback do `filter`.\n> Por exemplo, para aplicar a conversão de tipos (usando por exemplo [intval](https://www.php.net/manual/pt_BR/function.intval.php),\n> [boolval](https://www.php.net/manual/pt_BR/function.boolval.php), ...) para garantir um tipo específico para um atributo,\n> você pode simplesmente especificar os nomes das funções do filtro sem a necessidade de envolvê-los em um closure:\n>\n> ```php\n> ['property', 'filter', 'filter' => 'boolval'],\n> ['property', 'filter', 'filter' => 'intval'],\n> ```\n\n\n## [[yii\\validators\\ImageValidator|image]] <span id=\"image\"></span>\n\n```php\n[\n  // verifica se \"primaryImage\" é uma imagem válida com as proporções adequadas\n  ['primaryImage', 'image', 'extensions' => 'png, jpg',\n      'minWidth' => 100, 'maxWidth' => 1000,\n      'minHeight' => 100, 'maxHeight' => 1000,\n  ],\n]\n```\n\nEste validador verifica se o valor de entrada representa um arquivo de imagem válido. Por estender o validador [file](#file), ele herda todas as suas propriedades. Além disso, suporta as seguintes propriedades adicionais específicas para fins de validação de imagem:\n\n- `minWidth`: a largura mínima da imagem. O padrão é `null`, significa não ter limite mínimo.\n- `maxWidth`: a largura máxima da imagem. O padrão é `null`, significa não ter limite máximo.\n- `minHeight`: a altura mínima da imagem. O padrão é `null`, significa não ter limite mínimo.\n- `maxHeight`: a altura máxima da imagem. O padrão é `null`, significa não ter limite máximo.\n\n\n## [[yii\\validators\\RangeValidator|in]] <span id=\"in\"></span>\n\n```php\n[\n  // verifica se o \"level\" é 1, 2 ou 3\n  ['level', 'in', 'range' => [1, 2, 3]],\n]\n```\n\nEste validador verifica se o valor de entrada pode ser encontrado entre os valores da lista fornecida.\n\n- `range`: uma lista de determinados valores dentro da qual o valor de entrada deve ser procurado.\n- `strict`: se a comparação entre o valor de entrada e os valores dados devem ser strict\n(o tipo e o valor devem ser idênticos). O padrão é `false`.\n- `not`: se o resultado de validação deve ser invertido. O padrão é `false`. Quando esta propriedade é definida como `true`, o validador verifica se o valor de entrada NÃO está entre os valores da lista fornecida.\n- `allowArray`: para permitir que o valor de entrada seja um array. Quando esta propriedade é marcada como `true` e o valor de entrada é um array, todos os elementos neste array devem ser encontrados na lista de valores fornecida, caso contrário a validação falhará.\n\n\n## [[yii\\validators\\NumberValidator|integer]] <span id=\"integer\"></span>\n\n```php\n[\n  // verifica se \"age\" é um inteiro\n  ['age', 'integer'],\n]\n```\n\nEste validador verifica se o valor de entrada é um inteiro.\n\n- `max`: limite máximo (inclusive) do valor. Se não for configurado, significa que não tem verificação de limite máximo.\n- `min`: o limite mínimo (inclusive) do valor. Se não for configurado, significa que não tem verificação de limite mínimo.\n\n\n## [[yii\\validators\\RegularExpressionValidator|match]] <span id=\"match\"></span>\n\n```php\n[\n  // verifica se \"username\" começa com uma letra e contém somente caracteres\n  ['username', 'match', 'pattern' => '/^[a-z]\\w*$/i']\n]\n```\n\nEste validador verifica se o valor de entrada atende a expressão regular especificada.\n\n- `pattern`: a expressão regular que o valor de entrada deve corresponder. Esta propriedade deve ser configurada, caso contrário uma exceção será lançada.\n- `not`: para inverter o resultado da validação. O padrão é `false`, significa que a validação terá sucesso apenas se o valor de entrada corresponder ao padrão definido. Se for configurado como `true` a validação terá sucesso apenas se o valor de entrada NÃO corresponder ao padrão definido.\n\n\n## [[yii\\validators\\NumberValidator|number]] <span id=\"number\"></span>\n\n```php\n[\n  // verifica se \"salary\" é um number\n  ['salary', 'number'],\n]\n```\n\nEste validador verifica se o valor de entrada é um number. É equivalente ao validador [double](#double).\n\n- `max`: limite máximo (inclusive) do valor. Se não for configurado, significa que não tem verificação de limite máximo.\n- `min`: o limite mínimo (inclusive) do valor. Se não for configurado, significa que não tem verificação de limite mínimo.\n\n\n## [[yii\\validators\\RequiredValidator|required]] <span id=\"required\"></span>\n\n```php\n[\n  // verifica se ambos \"username\" e \"password\" não estão vazios\n  [['username', 'password'], 'required'],\n]\n```\n\nEste validador verifica se o valor de entrada foi fornecido e não está vazio.\n\n- `requiredValue`: o valor desejado que a entrada deve ter. Se não configurado, significa que o valor de entrada apenas não deve estar vazio.\n- `strict`: para verificar os tipos de dados ao validar um valor. O padrão é `false`. Quando `requiredValue` não é configurado, se esta propriedade for `true`, o validador verificará se o valor de entrada não é estritamente nulo; Se esta propriedade for `false`, o validador usará uma regra solta para determinar se o valor está vazio ou não. Quando `requiredValue` está configurado, a comparação entre o valor de entrada e `requiredValue` também verificará os tipos de dados se esta propriedade for `true`.\n\n> Observação: Como determinar se um valor está vazio ou não é um tópico separado descrito na seção [Valores Vazios](input-validation.md#handling-empty-inputs).\n\n\n## [[yii\\validators\\SafeValidator|safe]] <span id=\"safe\"></span>\n\n```php\n[\n  // marca o \"description\" como um atributo seguro\n  ['description', 'safe'],\n]\n```\n\nEste validador não executa validação de dados. Em vez disso, ele é usado para marcar um atributo para ser um [atributo seguro](structure-models.md#safe-attributes).\n\n\n## [[yii\\validators\\StringValidator|string]] <span id=\"string\"></span>\n\n```php\n[\n  // verifica se \"username\" é uma string cujo tamanho está entre 4 e 24\n  ['username', 'string', 'length' => [4, 24]],\n]\n```\n\nEste validador verifica se o valor de entrada é uma string válida com um determinado tamanho.\n\n- `length`: especifica o limite do comprimento da string de entrada que está sendo validada. Este pode ser especificado em uma das seguintes formas:\n   * um inteiro: o comprimento exato que a string deverá ter;\n   * um array de um elemento: o comprimento mínimo da string de entrada (ex. `[8]`). Isso substituirá `min`.\n   * um array de dois elementos: o comprimento mínimo e máximo da string de entrada (ex. `[8, 128]`). Isso substituirá ambos `min` e `max`.\n- `min`: o comprimento mínimo da string de entrada. Se não configurado, significa não ter limite para o comprimento mínimo.\n- `max`: o comprimento máximo da string de entrada. Se não configurado, significa não ter limite para o comprimento máximo.\n- `encoding`: a codificação da string de entrada a ser validada. se não configurado, será usado o valor de [[yii\\base\\Application::charset|charset]] da aplicação que por padrão é  `UTF-8`.\n\n\n## [[yii\\validators\\FilterValidator|trim]] <span id=\"trim\"></span>\n\n```php\n[\n  // trima os espaços em branco ao redor de \"username\" e \"email\"\n  [['username', 'email'], 'trim'],\n]\n```\n\nEste validador não executa validação de dados. Em vez disso, ele vai retirar os espaços em branco ao redor do valor de entrada. Observe que se o valor de entrada for um array, ele será ignorado pelo validador.\n\n\n## [[yii\\validators\\UniqueValidator|unique]] <span id=\"unique\"></span>\n\n```php\n[\n // a1 precisa ser único na coluna representada pelo atributo  \"a1\"\n ['a1', 'unique'],\n\n // a1 precisa ser único, mas a coluna a2 será usada para verificar a singularidade do valor de a1\n ['a1', 'unique', 'targetAttribute' => 'a2'],\n\n // a1 e a2 precisam ser únicos, e ambos receberão mensagem de erro\n [['a1', 'a2'], 'unique', 'targetAttribute' => ['a1', 'a2']],\n\n  // a1 e a2 precisam ser únicos, mas somente ‘a1’ receberá mensagem de erro\n ['a1', 'unique', 'targetAttribute' => ['a1', 'a2']],\n\n // a1 precisa ser único verificando a singularidade de ambos a2 e a3 (usando o valor de a1)\n ['a1', 'unique', 'targetAttribute' => ['a2', 'a1' => 'a3']],\n]\n```\n\nEste validador verifica se o valor de entrada é único na coluna da tabela. Ele só trabalha com atributos dos models (modelos) [Active Record](db-active-record.md). Suporta a validação de uma única coluna ou de várias.\n\n- `targetClass`: o nome da classe [Active Record](db-active-record.md) que deve ser usada para procurar o valor de input que está sendo validado. Se não for configurado, a classe model atual que está sendo validado será usada.\n- `targetAttribute`: o nome do atributo em `targetClass` que deve ser usado para validar a singularidade do valor de entrada. Se não for configurado, este usará o nome do atributo atual que está sendo validado. Você pode usar um array para validar a singularidade de várias colunas ao mesmo tempo. Os valores do array são os atributos que serão utilizados para validar a singularidade, enquanto as chaves do array são os atributos cujos valores serão validados. Se a chave e o valor forem os mesmos, você pode apenas especificar o valor.\n- `filter`: filtro adicional para ser aplicado na query do banco de dados para validar a singularidade do valor de entrada. Este pode ser uma string ou um array representando a condição adicional da query (consulte o formato de condição [[yii\\db\\Query::where()]]) ou uma função anônima com a assinatura `function ($query)`, onde `$query` é o objeto [[yii\\db\\Query|Query]] que você pode modificar na função.\n\n\n## [[yii\\validators\\UrlValidator|url]] <span id=\"url\"></span>\n\n```php\n[\n  // verifica se \"website\" é uma URL válida. Coloca \"http://\" no atributo \"website\"\n  // e ele não tiver um esquema da URI\n  ['website', 'url', 'defaultScheme' => 'http'],\n]\n```\n\nEste validador verifica se o valor de entrada é uma URL válida.\n\n- `validSchemes`: um array especificando o esquema da URI que deve ser considerada válida. O padrão é `['http', 'https']`, significa que ambas URLs `http` e `https` são considerados como válidos.\n- `defaultScheme`: o esquema padrão da URI para ser anexado à entrada, se a parte do esquema não for informada na entrada. O padrão é `null`, significa que o valor de entrada não será modificado.\n- `enableIDN`: se o validador deve ter uma conta IDN (internationalized domain names). O padrão é `false`. Observe que para usar a validação IDN você tem que instalar e ativar a extenção PHP `intl`, caso contrário uma exceção será lançada.\n\n\n"
  },
  {
    "path": "docs/guide-pt-BR/tutorial-docker.md",
    "content": "Yii e Docker\n==============\n\nPara o desenvolvimento e implantação de aplicativos Yii, eles podem ser executados como contêineres Docker. Um contêiner é como uma máquina virtual isolada e leve que mapeia seus serviços para as portas do host, ou seja, um servidor da web em um contêiner na porta 80 está disponível na porta 8888 do seu (local) host.\n\nOs contêineres podem resolver muitos problemas, como ter versões idênticas de software no computador do desenvolvedor e no servidor, implantações rápidas ou simulação de arquitetura multi-servidor durante o desenvolvimento.\n\nVocê pode ler mais sobre contêineres Docker em [docker.com](https://www.docker.com/why-docker).\n\n## Requisitos\n\n- `docker`\n- `docker-compose`\n\nVisite a [página de download](https://www.docker.com/products/container-runtime) para obter as ferramentas do Docker.\n\n## Instalação\n\nApós a instalação, você deve ser capaz de executar o comando docker ps e ver uma saída semelhante a esta:\n\n```\nCONTAINER ID   IMAGE   COMMAND   CREATED   STATUS   PORTS\n```\n\nIsso significa que o seu daemon Docker está em execução.\n\nAlém disso, execute o comando docker-compose version, a saída deve ser semelhante a esta:\n\n```\ndocker-compose version 1.20.0, build unknown\ndocker-py version: 3.1.3\nCPython version: 3.6.4\nOpenSSL version: OpenSSL 1.1.0g  2 Nov 2017\n```\n\nCom o Compose, você pode configurar e gerenciar todos os serviços necessários para a sua aplicação, como bancos de dados e cache.\n\n## Recursos\n\n- As imagens base do PHP para Yii podem ser encontradas em [yii2-docker](https://github.com/yiisoft/yii2-docker)\n- Suporte do Docker para [yii2-app-basic](https://github.com/yiisoft/yii2-app-basic#install-with-docker)\n- Suporte do Docker para [yii2-app-advanced](https://github.com/yiisoft/yii2-app-advanced/pull/347) está em desenvolvimento\n\n## Uso\n\nOs comandos básicos do Docker são\n\n    docker-compose up -d\n    \npara iniciar todos os serviços em sua pilha, em segundo plano\n\n    docker-compose ps\n    \npara listar os serviços em execução\n\n    docker-compose logs -f\n    \npara visualizar os logs de todos os serviços continuamente\n\n    docker-compose stop\n    \npara interromper todos os serviços em sua pilha de forma elegante\n\n    docker-compose kill\n    \npara interromper todos os serviços em sua pilha imediatamente\n\n    docker-compose down -v\n\npara parar e remover todos os serviços, **atenção à perda de dados ao não usar volumes do host**\n\nPara executar comandos em um contêiner:\n\n    docker-compose run --rm php composer install\n    \nexecuta a instalação do Composer em um novo contêiner\n\n    docker-compose exec php bash\n    \nexecuta um shell bash em um serviço php que está em *execução*.\n\n\n## Tópicos avançados\n\n### Testes do framework Yii\n\nVocê pode executar os testes do framework Yii em um contêiner Docker, conforme descrito [aqui](https://github.com/yiisoft/yii2/blob/master/tests/README.md#dockerized-testing).\n\n### Database administration tools\n\nAo executar o MySQL como (`mysql`), você pode adicionar um contêiner do phpMyAdmin à sua pilha, como mostrado abaixo:\n\n```\n    phpmyadmin:\n        image: phpmyadmin/phpmyadmin\n        ports:\n            - '8888:80'\n        environment:\n            - PMA_ARBITRARY=1\n            - PMA_HOST=mysql\n        depends_on:\n            - mysql\n```\n"
  },
  {
    "path": "docs/guide-pt-BR/tutorial-shared-hosting.md",
    "content": "Ambiente de Hospedagem Compartilhada\n==========================\n\nAmbientes de hospedagem compartilhada geralmente são muito limitados com relação a configuração e estrutura de diretórios. Ainda assim, na maioria dos casos, você pode executar Yii 2.0 em um ambiente de hospedagem compartilhada com poucos ajustes.\n\nImplantação do Template Básico\n---------------------------\n\nUma vez que em um ambiente de hospedagem compartilhada geralmente não há apenas um webroot, use o template básico, se puder. Consulte o [Capítulo Instalando o Yii](start-installation.md) e instale o modelo de projeto básico localmente. Depois de ter sua aplicação funcionando localmente, vamos fazer alguns ajustes para que possa ser hospedado em seu servidor de hospedagem compartilhada.\n\n### Renomear webroot <span id=\"renaming-webroot\"></span>\n\nAo conectar no seu servidor compartilhado através de FTP ou outros meios, você provavelmente verá algo como a seguir:\n \n```\nconfig\nlogs\nwww\n```\n\nNo exemplo acima, `www` é seu diretório raíz do servidor web e pode possuir nomes diferente. Nomes comuns são: `www`,` htdocs`, e `public_html`.\n\nO diretório raíz de nosso template básico é chamado `web`. Antes de enviar a aplicação para seu servidor renomeie seu diretório raiz local de acordo com o do seu servidor, ou seja, de `web` para `www`, `public_html` ou qualquer que seja o nome do seu diretório raíz na hospedagem.\n\n### Diretório raiz FTP precisa ser gravável\n\nSe você possui permissão de escrita no diretório raíz, (onde estão `config`, `logs` e `www`), então faça upload de `assets`, `commands` etc.\n\n### Recursos extras para servidor web <span id=\"add-extras-for-webserver\"></span>\n\nSe o seu servidor web é Apache você precisará adicionar um arquivo `.htaccess` com o seguinte conteúdo em `web` (ou `public_html` ou qualquer outro) (onde o arquivo` index.php` está localizado):\n\n```\nOptions +FollowSymLinks\nIndexIgnore */*\n\nRewriteEngine on\n\n# if a directory or a file exists, use it directly\nRewriteCond %{REQUEST_FILENAME} !-f\nRewriteCond %{REQUEST_FILENAME} !-d\n\n# otherwise forward it to index.php\nRewriteRule . index.php\n```\n\nSe o seu servidor web é Nginx, você não precisa de nenhuma configuração extra.\n\n### Verifique os requisitos\n\nPara executar Yii, o seu servidor web deve atender alguns requisitos. O requisito mínimo é PHP 5.4. Para verificar os requisitos copie o arquivo `requirements.php` da raíz da aplicação para o diretório raíz do servidor web e execute-o através do navegador usando o endereço `https://example.com/requirements.php`. Não se esqueça de apagar o arquivo depois.\n\nImplantação do Template Avançado\n---------------------------------\n\nA implantação do Template Avançado para a hospedagem compartilhada é um pouco mais complicada do que o Template Básico, porque ele tem duas webroots, que servidores web da hospedagem compartilhada não suportam. Vamos precisar de ajustar a estrutura de diretórios.\n\n### Mova os scripts de entrada para única raíz\n\nPrimeiro de tudo, precisamos de um diretório raíz. Crie um novo diretório e nomeie-o para corresponder à raíz de sua hospedagem, conforme descrito no [Renomear webroot](#renaming-webroot), por exemplo `www` ou `public_html` ou semelhante. Em seguida, crie a seguinte estrutura onde `www` é o diretório raíz de sua hospedagem que você acabou de criar:\n\n```\nwww\n    admin\nbackend\ncommon\nconsole\nenvironments\nfrontend\n...\n```\n\n`www` será nosso diretório frontend, então mova o conteúdo de `frontend/web` para ele. Mova o conteúdo de `backend/web` para `www/admin`. Em ambos os casos você precisará ajustar os caminhos em `index.php` and `index-test.php`.\n\n### Sessões e cookies separados\n\nOriginalmente, o backend e frontend destinam-se a ser executado em diferentes domínios. Quando movemos tudo para o mesmo domínio, tanto frontend quanto backend estarão partilhando os mesmos cookies, criando um problema. Para corrigi-lo, ajuste sua configuração backend `backend/config/main.php` da seguinte forma:\n\n```php\n'components' => [\n    'request' => [\n        'csrfParam' => '_backendCSRF',\n        'csrfCookie' => [\n            'httpOnly' => true,\n            'path' => '/admin',\n        ],\n    ],\n    'user' => [\n        'identityCookie' => [\n            'name' => '_backendIdentity',\n            'path' => '/admin',\n            'httpOnly' => true,\n        ],\n    ],\n    'session' => [\n        'name' => 'BACKENDSESSID',\n        'cookieParams' => [\n            'path' => '/admin',\n        ],\n    ],\n],\n```\n"
  },
  {
    "path": "docs/guide-pt-BR/tutorial-yii-integration.md",
    "content": "Trabalhando com Códigos de Terceiros\n=============================\n\nDe tempos em tempos, você pode precisar usar algum código de terceiro na sua aplicação Yii. Ou você pode querer utilizar o Yii como uma biblioteca em alguns sistemas de terceiros. Nesta seção, vamos mostrar como fazer isto.\n\n\nUsando Bibliotecas de Terceiros no Yii <span id=\"using-libs-in-yii\"></span>\n----------------------------------\n\nPara utilizar bibliotecas de terceiros em uma aplicação Yii, você precisa primeiramente garantir que as classes na biblioteca estão devidamente incluídas ou se podem ser carregadas por demanda.\n\n\n### Usando Pacotes Composer <span id=\"using-composer-packages\"></span>\n\nMuitas bibliotecas de terceiros gerenciam suas versões através de pacotes do [Composer](https://getcomposer.org/). Você pode instalar tais bibliotecas realizando os dois seguintes passos:\n\n1. Modifique o arquivo `composer.json` da sua aplicação e informe quais pacotes Composer você deseja instalar.\n2. Execute `composer install` para instalar os pacotes especificados.\n\nAs classes nos pacotes Composer instalados podem ser carregadas automaticamente usando o autoloader do Composer. Certifique-se que o [script de entrada](structure-entry-scripts.md) da sua aplicação contém as seguintes linhas para instalar o autoloader do Composer:\n\n```php\n// Instala o Composer autoloader\nrequire __DIR__ . '/../vendor/autoload.php';\n\n// faz o include da classe Yii\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n```\n\n\n### Usando Bibliotecas baixadas <span id=\"using-downloaded-libs\"></span>\n\nSe a biblioteca não foi lançada como um pacote Composer, você deve seguir as instruções de instalação para instalá-la. Na maioria dos casos, você precisará baixar manualmente o arquivo de liberação da biblioteca e descompactá-lo no diretório `BasePath/vendor`, onde `BasePath` representa o [caminho base](structure-applications.md#basePath) da sua aplicação.\n\nSe uma biblioteca possui o seu próprio carregador automático, você pode instalá-la no [script de entrada](structure-entry-scripts.md) de sua aplicação. Recomenda-se que a instalação seja feita antes de incluir o arquivo `Yii.php`. Isto porque a classe autoloader Yii pode ter precedência nas classes de carregamento automático da biblioteca a ser instalada.\n\nSe uma biblioteca não oferece um carregador automático de classe, mas seus nomes seguem o padrão [PSR-4](https://www.php-fig.org/psr/psr-4/), você pode usar a classe de autoloader do  Yii para carregar as classes. Tudo que você precisa fazer é apenas declarar um [alias](concept-aliases.md#defining-aliases) para cada namespace raiz utilizados em suas classes. Por exemplo, suponha que você tenha instalado uma biblioteca no diretório `vendor/foo/bar` e as classes de bibliotecas estão sob o namespace raiz `xyz`. Você pode incluir o seguinte código na configuração da sua aplicação:\n\n```php\n[\n   'aliases' => [\n       '@xyz' => '@vendor/foo/bar',\n   ],\n]\n```\n\n\nSe não for nenhuma das opções acima, é provável que a biblioteca necessite fazer um include PHP com algum caminho específico para localizar corretamente os arquivos das classes. Basta seguir as instruções de como configurar o *include path* do PHP.\n\n\nNo pior dos casos, quando a biblioteca exige explicitamente a inclusão de cada arquivo de classe, você pode usar o seguinte método para incluir as classes por demanda:\n\n* Identificar quais as classes da biblioteca contém.\n* Liste as classes e os caminhos dos arquivos correspondentes em `Yii::$classMap` no [script de entrada](structure-entry-scripts.md) da aplicação. Por exemplo:\n\n```php\nYii::$classMap['Class1'] = 'path/to/Class1.php';\nYii::$classMap['Class2'] = 'path/to/Class2.php';\n```\n\n\nUsando o Yii em Sistemas de Terceiros <span id=\"using-yii-in-others\"></span>\n--------------------------------\n\nComo o Yii fornece muitas características excelentes, algumas vezes você pode querer utilizar algumas destas características como suporte ao desenvolvimento ou melhorias em sistemas de terceiros, tais como WordPress, Joomla ou aplicações desenvolvidas utilizando outros frameworks PHP. Por exemplo, você pode querer utilizar a classe [[yii\\helpers\\ArrayHelper]] ou usar o recurso de [Active Record](db-active-record.md) em um sistema de terceiros. Para alcançar este objetivo, você primeiramente precisa realizar dois passos: instale o Yii, e o bootstrap Yii.\n\nSe o sistema em questão utilizar o Composer para gerenciar suas dependências, você pode simplesmente executar o seguinte comando para instalar o Yii:\n\n    composer global require \"fxp/composer-asset-plugin:^1.4.1\"\n    composer require yiisoft/yii2\n    composer install\n\nO primeiro comando instala o [Composer asset plugin](https://github.com/fxpio/composer-asset-plugin)\nque permite gerenciar o bower e dependências de pacotes npm através do Composer. Mesmo que você apenas queira utilizar a camada de banco de dados ou outros recursos não-ativos relacionados do Yii, isto é necessário para instalar o pacote Composer do Yii.\nVeja também a seção [sobre a instalação do Yii](start-installation.md#installing-via-composer) para obter mais informações do Composer e solução para os possíveis problemas que podem surgir durante a instalação.\n\nCaso contrário, você pode fazer o [download](https://www.yiiframework.com/download/) do Yii e descompactá-lo no diretório `BasePath/vendor`.\n\nEm seguida, você deve modificar o script de entrada do sistema de terceiros incluindo o seguinte código no início:\n\n```php\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n\n$yiiConfig = require __DIR__ . '/../config/yii/web.php';\nnew yii\\web\\Application($yiiConfig); // NÃO execute o método run() aqui\n```\n\nComo você pode ver, o código acima é muito semelhante ao que existe no [script de entrada](structure-entry-scripts.md) de uma aplicação típica do Yii. A única diferença é que depois que a instância da aplicação é criada, o método `run()` não é chamado. Isto porque chamando `run()`, Yii vai assumir o controle do fluxo das requisições que não é necessário neste caso e já é tratado pela aplicação existente.\n\nComo na aplicação Yii, você deve configurar a instância da aplicação com base no ambiente de execução do sistema em questão. Por exemplo, para usar o recurso de [Active Record](db-active-record.md), você precisa configurar o [componente da aplicação](structure-application-components.md) `db` com a configuração de conexão do banco de dados utilizada pelo sistema.\n\nAgora você pode usar a maioria dos recursos fornecidos pelo Yii. Por exemplo, você pode criar classes Active Record e usá-las para trabalhar com o banco de dados.\n\n\nUsando Yii 2 com Yii 1 <span id=\"using-both-yii2-yii1\"></span>\n----------------------\n        \nSe você estava usando o Yii 1 anteriormente, é provável que você tenha uma aplicação rodando com Yii 1. Em vez de reescrever toda a aplicação em Yii 2, você pode apenas querer melhorá-lo usando apenas alguns dos recursos disponíveis no Yii 2. Isto pode ser alcançado seguindo as instuções a seguir.\n\n> Observação: Yii 2 requer PHP 5.4 ou superior. Você deve certificar-se que o seu servidor e a sua aplicação suportem estes requisitos.\n\nPrimeiro, instale o Yii 2 na sua aplicação existente seguindo as instruções dadas na [última subseção](#using-yii-in-others).\n\nSegundo, altere o script de entrada da sua aplicação como a seguir,\n\n```php\n// incluir a classe Yii personalizado descrito abaixo\nrequire __DIR__ . '/../components/Yii.php';\n\n// configuração para aplicação Yii 2\n$yii2Config = require __DIR__ . '/../config/yii2/web.php';\nnew yii\\web\\Application($yii2Config); // NÃO execute o método run() aqui\n\n// configuração para aplicação Yii 1\n$yii1Config = require __DIR__ . '/../config/yii1/main.php';\nYii::createWebApplication($yii1Config)->run();\n```\n\nUma vez que ambos Yii 1 e Yii 2 possuem a classe `Yii`, você deve criar uma versão personalizada para combiná-los. O código acima inclui o arquivo de classe personalizado `Yii`, que pode ser criado conforme o exemplo abaixo.\n\n```php\n$yii2path = '/path/to/yii2';\nrequire $yii2path . '/BaseYii.php'; // Yii 2.x\n\n$yii1path = '/path/to/yii1';\nrequire $yii1path . '/YiiBase.php'; // Yii 1.x\n\nclass Yii extends \\yii\\BaseYii\n{\n   // copie e cole o código de YiiBase (1.x) aqui\n}\n\nYii::$classMap = include($yii2path . '/classes.php');\n// registrar o autoloader do Yii 2 através do Yii 1\nYii::registerAutoloader(['Yii', 'autoload']);\n// criar o contêiner de injeção de dependência\nYii::$container = new yii\\di\\Container;\n```\n\nIsto é tudo! Agora, em qualquer parte do seu código, você pode usar `Yii::$app` para acessar a instância da aplicação Yii 2, enquanto `Yii::app()` lhe dará a instância da aplicação Yii 1:\n\n```php\necho get_class(Yii::app()); // retorna'CWebApplication'\necho get_class(Yii::$app);  // retorna 'yii\\web\\Application'\n\n\n"
  },
  {
    "path": "docs/guide-ru/README.md",
    "content": "Полное руководство по Yii 2.0\n=============================\n\nДанное руководство выпущено в соответствии с [положениями о документации Yii](https://www.yiiframework.com/doc/terms/).\n\nAll Rights Reserved.\n\n2014 © Yii Software LLC.\n\n\nВведение\n--------\n\n* [О Yii](intro-yii.md)\n* [Обновление с версии 1.1](intro-upgrade-from-v1.md)\n\n\nПервое знакомство\n-----------------\n\n* [Что нужно знать](start-prerequisites.md)\n* [Установка Yii](start-installation.md)\n* [Запуск приложения](start-workflow.md)\n* [Говорим «привет»](start-hello.md)\n* [Работа с формами](start-forms.md)\n* [Работа с базами данных](start-databases.md)\n* [Генерация кода при помощи Gii](start-gii.md)\n* [Что дальше?](start-looking-ahead.md)\n\n\nСтруктура приложения\n--------------------\n\n* [Обзор](structure-overview.md)\n* [Входные скрипты](structure-entry-scripts.md)\n* [Приложения](structure-applications.md)\n* [Компоненты приложения](structure-application-components.md)\n* [Контроллеры](structure-controllers.md)\n* [Модели](structure-models.md)\n* [Представления](structure-views.md)\n* [Модули](structure-modules.md)\n* [Фильтры](structure-filters.md)\n* [Виджеты](structure-widgets.md)\n* [Ресурсы](structure-assets.md)\n* [Расширения](structure-extensions.md)\n\n\nОбработка запросов\n------------------\n\n* [Обзор](runtime-overview.md)\n* [Bootstrapping](runtime-bootstrapping.md)\n* [Разбор и генерация URL](runtime-routing.md)\n* [Запросы](runtime-requests.md)\n* [Ответы](runtime-responses.md)\n* [Сессии и куки](runtime-sessions-cookies.md)\n* [Обработка ошибок](runtime-handling-errors.md)\n* [Логирование](runtime-logging.md)\n\n\nОсновные понятия\n----------------\n\n* [Компоненты](concept-components.md)\n* [Свойства](concept-properties.md)\n* [События](concept-events.md)\n* [Поведения](concept-behaviors.md)\n* [Конфигурации](concept-configurations.md)\n* [Псевдонимы](concept-aliases.md)\n* [Автозагрузка классов](concept-autoloading.md)\n* [Service Locator](concept-service-locator.md)\n* [Dependency Injection Container](concept-di-container.md)\n\n\nРабота с базами данных\n----------------------\n\n* [Объекты доступа к данным (DAO)](db-dao.md) - Соединение с базой данных, простые запросы, транзакции и работа со схемой.\n* [Построитель запросов](db-query-builder.md) - Запросы к базе данных через простой слой абстракции.\n* [Active Record](db-active-record.md) - Получение объектов AR, работа с ними и определение связей.\n* [Миграции](db-migrations.md) - Контроль версий схемы данных при работе в команде.\n* [Sphinx](https://github.com/yiisoft/yii2-sphinx/blob/master/docs/guide-ru/README.md)\n* [Redis](https://github.com/yiisoft/yii2-redis/blob/master/docs/guide-ru/README.md)\n* [MongoDB](https://github.com/yiisoft/yii2-mongodb/blob/master/docs/guide-ru/README.md)\n* [ElasticSearch](https://github.com/yiisoft/yii2-elasticsearch/blob/master/docs/guide-ru/README.md)\n\n\nПолучение данных от пользователя\n--------------------------------\n\n* [Создание форм](input-forms.md)\n* [Валидация](input-validation.md)\n* [Загрузка файлов](input-file-upload.md)\n* [Табличный ввод](input-tabular-input.md)\n* [Работа с несколькими моделями](input-multiple-models.md)\n* [Расширение ActiveForm на стороне клиента](input-form-javascript.md)\n\n\nОтображение данных\n------------------\n\n* [Форматирование данных](output-formatting.md)\n* [Постраничная разбивка](output-pagination.md)\n* [Сортировка](output-sorting.md)\n* [Провайдеры данных](output-data-providers.md)\n* [Виджеты для данных](output-data-widgets.md)\n* [Работа с клиентскими скриптами](output-client-scripts.md)\n* [Темизация](output-theming.md)\n\n\nБезопасность\n------------\n\n* [Обзор](security-overview.md)\n* [Аутентификация](security-authentication.md)\n* [Авторизация](security-authorization.md)\n* [Работа с паролями](security-passwords.md)\n* [Криптография](security-cryptography.md)\n* [Клиенты авторизации](https://github.com/yiisoft/yii2-authclient/blob/master/docs/guide-ru/README.md)\n* [Лучшие практики](security-best-practices.md)\n\n\nКеширование\n-----------\n\n* [Обзор](caching-overview.md)\n* [Кэширование данных](caching-data.md)\n* [Кэширование фрагментов](caching-fragment.md)\n* [Кэширование страниц](caching-page.md)\n* [HTTP кэширование](caching-http.md)\n\n\nВеб-сервисы REST\n----------------\n\n* [Быстрый старт](rest-quick-start.md)\n* [Ресурсы](rest-resources.md)\n* [Контроллеры](rest-controllers.md)\n* [Фильтрация коллекций](rest-filtering-collections.md)\n* [Роутинг](rest-routing.md)\n* [Форматирование ответа](rest-response-formatting.md)\n* [Аутентификация](rest-authentication.md)\n* [Ограничение частоты запросов](rest-rate-limiting.md)\n* [Версионирование](rest-versioning.md)\n* [Обработка ошибок](rest-error-handling.md)\n\n\nИнструменты разработчика\n------------------------\n\n* [Отладочная панель и отладчик](https://github.com/yiisoft/yii2-debug/blob/master/docs/guide/README.md)\n* [Генерация кода с Gii](https://github.com/yiisoft/yii2-gii/blob/master/docs/guide/README.md)\n* [Генератор документации API](https://github.com/yiisoft/yii2-apidoc)\n\n\nТестирование\n------------\n\n* [Обзор](test-overview.md)\n* [Настройка тестового окружения](test-environment-setup.md)\n* [Модульные тесты](test-unit.md)\n* [Функциональные тесты](test-functional.md)\n* [Приёмочные тесты](test-acceptance.md)\n* [Фикстуры](test-fixtures.md)\n\n\nСпециальные темы\n----------------\n\n* [Шаблон приложения advanced](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md)\n* [Создание приложения с нуля](tutorial-start-from-scratch.md)\n* [Консольные команды](tutorial-console.md)\n* [Docker](tutorial-docker.md)\n* [Встроенные валидаторы](tutorial-core-validators.md)\n* [Интернационализация](tutorial-i18n.md)\n* [Отправка почты](tutorial-mailing.md)\n* [Оптимизация производительности](tutorial-performance-tuning.md)\n* [Окружение виртуального хостинга](tutorial-shared-hosting.md)\n* [Шаблонизаторы](tutorial-template-engines.md)\n* [Работа со сторонним кодом](tutorial-yii-integration.md)\n* [Использование Yii в качестве микро-framework'а](tutorial-yii-as-micro-framework.md)\n\n\nВиджеты\n-------\n\n* [GridView](https://www.yiiframework.com/doc-2.0/yii-grid-gridview.html)\n* [ListView](https://www.yiiframework.com/doc-2.0/yii-widgets-listview.html)\n* [DetailView](https://www.yiiframework.com/doc-2.0/yii-widgets-detailview.html)\n* [ActiveForm](https://www.yiiframework.com/doc-2.0/guide-input-forms.html#activerecord-based-forms-activeform)\n* [Pjax](https://www.yiiframework.com/doc-2.0/yii-widgets-pjax.html)\n* [Menu](https://www.yiiframework.com/doc-2.0/yii-widgets-menu.html)\n* [LinkPager](https://www.yiiframework.com/doc-2.0/yii-widgets-linkpager.html)\n* [LinkSorter](https://www.yiiframework.com/doc-2.0/yii-widgets-linksorter.html)\n* [Виджеты Bootstrap](https://github.com/yiisoft/yii2-bootstrap/blob/master/docs/guide-ru/README.md)\n* [Виджеты Jquery UI](https://github.com/yiisoft/yii2-jui/blob/master/docs/guide-ru/README.md)\n\n\nХелперы\n-------\n\n* [Обзор](helper-overview.md)\n* [ArrayHelper](helper-array.md)\n* [Html](helper-html.md)\n* [Json](helper-json.md)\n* [Url хелпер](helper-url.md)\n"
  },
  {
    "path": "docs/guide-ru/blocktypes.json",
    "content": "{\n  \"Warning:\": \"Внимание:\",\n  \"Note:\": \"Примечание:\",\n  \"Info:\": \"Информация:\",\n  \"Tip:\": \"Подсказка:\"\n}\n"
  },
  {
    "path": "docs/guide-ru/caching-data.md",
    "content": "Кэширование данных\n==================\n\nКэширование данных заключается в сохранении некоторой переменной PHP в кэше и её последующем извлечении. Оно является основой для расширенных возможностей, таких как [кэширование запросов](#query-caching)\nи [кэширование страниц](caching-page.md).\n\nПриведённый ниже код является типичным случаем кэширования данных, где `$cache` указывает на [компонент кэширования](#cache-components):\n\n```php\n// Пробуем извлечь $data из кэша.\n$data = $cache->get($key);\n\nif ($data === false) {\n    // $data нет в кэше, вычисляем заново\n    $data = $this->calculateSomething();\n\n    // Сохраняем значение $data в кэше. Данные можно получить в следующий раз.\n    $cache->set($key, $data);\n}\n\n// Значение $data доступно здесь.\n```\n\nНачиная с версии 2.0.11, [компонент кэширования](#cache-components) предоставляет метод\n[[yii\\caching\\Cache::getOrSet()|getOrSet()]], который упрощает код при получении, вычислении и сохранении данных.\nПриведённый ниже код делает в точности то же самое, что и код в предыдущем примере:\n\n```php\n$data = $cache->getOrSet($key, function () {\n    return $this->calculateSomething();\n});\n```\n\nЕсли в кэше есть данные по ключу `$key`, они будут сразу возвращены.\nИначе, будет вызвана переданная анонимная функция, вычисляющая значение, которое будет сохранено в кэш и возвращено\nиз метода.\n\nВ случае, когда анонимной функции требуются данные из внешней области видимости, можно передать их с помощью\nоператора `use`. Например:\n\n```php\n$user_id = 42;\n$data = $cache->getOrSet($key, function () use ($user_id) {\n    return $this->calculateSomething($user_id);\n});\n```\n\n> Note: В [[yii\\caching\\Cache::getOrSet()|getOrSet()]] можно передать срок действия и зависимости кэша.\n  Прочтите [Срок действия кэша](#cache-expiration) и [Зависимости кеша](#cache-dependencies) чтобы узнать больше.\n\n\n## Компоненты кэширования <span id=\"cache-components\"></span>\n\nКэширование данных опирается на *компоненты кэширования*, которые представляют различные хранилища, такие как память, файлы и базы данных.\n\nКэш-компоненты, как правило, зарегистрированы в качестве [компонентов приложения](structure-application-components.md), так что их можно настраивать и обращаться к ним глобально. Следующий код показывает, как настроить компонент приложения `cache` для использования [Memcached](https://memcached.org/) с двумя серверами:\n\n```php\n'components' => [\n    'cache' => [\n        'class' => 'yii\\caching\\MemCache',\n        'servers' => [\n            [\n                'host' => 'server1',\n                'port' => 11211,\n                'weight' => 100,\n            ],\n            [\n                'host' => 'server2',\n                'port' => 11211,\n                'weight' => 50,\n            ],\n        ],\n    ],\n],\n```\n\nВы можете получить доступ к компоненту кэша, используя выражение `Yii::$app->cache`.\n\nПоскольку все компоненты кэша поддерживают единый API-интерфейс - вы можете менять основной компонент кэша на другой через конфигурацию приложения. Код, использующий кэш, при этом не меняется. Например, можно изменить конфигурацию выше для использования [[yii\\caching\\ApcCache|APC cache]] следующим образом:\n\n\n```php\n'components' => [\n    'cache' => [\n        'class' => 'yii\\caching\\ApcCache',\n    ],\n],\n```\n\n> Tip: Вы можете зарегистрировать несколько кэш-компонентов приложения. Компонент с именем `cache` используется по умолчанию многими классами (например, [[yii\\web\\UrlManager]]).\n   \n   \n### Поддерживаемые хранилища <span id=\"supported-cache-storage\"></span>\n\nYii поддерживает множество хранилищ кэша:\n\n* [[yii\\caching\\ApcCache]]: использует расширение PHP [APC](https://www.php.net/manual/ru/book.apcu.php). Эта опция считается самой быстрой при работе с кэшем в «толстом» централизованном приложении (т.е. один сервер, без выделенного балансировщика нагрузки и т.д.);\n* [[yii\\caching\\DbCache]]: использует таблицу базы данных для хранения кэшированных данных. Чтобы использовать этот кэш, вы должны создать таблицу так, как это описано в [[yii\\caching\\DbCache::cacheTable]];\n* [[yii\\caching\\DummyCache]]: является кэшем-пустышкой, не реализующим реального кэширования. Смысл этого компонента в упрощении кода, проверяющего наличие кэша. Вы можете использовать данный тип кэша и переключиться на реальное кэширование позже. Примеры: использование при разработке; если сервер не поддерживает кэш. Для извлечения данных в этом случае используется один и тот же код `Yii::$app->cache->get($key)`. При этом можно не беспокоиться, что `Yii::$app->cache` может быть `null`;\n* [[yii\\caching\\FileCache]]: использует обычные файлы для хранения кэшированных данных. Замечательно подходит для кэширования больших кусков данных, таких как содержимое страницы;\n* [[yii\\caching\\MemCache]]: использует расширения PHP [memcache](https://www.php.net/manual/ru/book.memcache.php) и [memcached](https://www.php.net/manual/ru/book.memcached.php). Этот вариант может рассматриваться как самый быстрый при работе в распределенных приложениях (например, с несколькими серверами, балансировкой нагрузки и так далее);\n* [[yii\\redis\\Cache]]: реализует компонент кэша на основе [Redis](https://redis.io/), хранилища ключ-значение (требуется Redis версии 2.6.12 или выше);\n* [[yii\\caching\\WinCache]]: использует расширение PHP [WinCache](https://iis.net/downloads/microsoft/wincache-extension) ([смотрите также](https://www.php.net/manual/ru/book.wincache.php));\n\n> Tip: Вы можете использовать разные способы хранения кэша в одном приложении. Общая стратегия заключается в использовании памяти под хранение небольших часто используемых данных (например, статистические данные). Для больших и реже используемых данных (например, содержимое страницы) лучше использовать файлы или базу данных.\n  \n## Кэш API <span id=\"cache-apis\"></span>\n\nУ всех компонентов кэша имеется один базовый класс [[yii\\caching\\Cache]] со следующими методами:\n\n* [[yii\\caching\\Cache::get()|get()]]: возвращает данные по указанному ключу. Если данные не найдены или устарели, то\n  значение `false` будет возвращено;\n* [[yii\\caching\\Cache::set()|set()]]: сохраняет данные по ключу;\n* [[yii\\caching\\Cache::add()|add()]]: сохраняет данные по ключу если такого ключа ещё нет;\n* [[yii\\caching\\Cache::getOrSet()|getOrSet()]]: возвращает данные по указанному ключу или выполняет переданную\n  анонимную функцию для вычисления значения, а полученные данные сохраняет в кэш и возвращает;  \n* [[yii\\caching\\Cache::multiGet()|multiGet()]]: извлекает сразу несколько элементов данных из кэша по заданным ключам;\n* [[yii\\caching\\Cache::multiSet()|multiSet()]]: сохраняет несколько элементов данных. Каждый элемент идентифицируется ключом;\n* [[yii\\caching\\Cache::multiAdd()|multiAdd()]]: сохраняет несколько элементов данных. Каждый элемент идентифицируется ключом.\n  Если ключ уже существует, то сохранения не происходит;\n* [[yii\\caching\\Cache::exists()|exists()]]: есть ли указанный ключ в кэше;\n* [[yii\\caching\\Cache::delete()|delete()]]: удаляет указанный ключ;\n* [[yii\\caching\\Cache::flush()|flush()]]: удаляет все данные.\n\n> Note: Не кэшируйте непосредственно значение `false`, потому что [[yii\\caching\\Cache::get()|get()]] использует\n`false` для случая, когда данные не найдены в кэше. Вы можете обернуть `false` в массив и закэшировать его, чтобы\nизбежать данной проблемы.\n\nНекоторые кэш-хранилища, например, MemCache или APC, поддерживают получение нескольких значений в пакетном режиме, что\nможет сократить накладные расходы на получение данных. Данную возможность можно использовать при помощи\n[[yii\\caching\\Cache::multiGet()|multiGet()]] и [[yii\\caching\\Cache::multiAdd()|multiAdd()]]. В случае, если хранилище\nне поддерживает эту функцию, она будет имитироваться.\n\nТак как [[yii\\caching\\Cache]] реализует `ArrayAccess` - следовательно компонент кэша можно использовать как массив:\n\n```php\n$cache['var1'] = $value1;  // эквивалентно: $cache->set('var1', $value1);\n$value2 = $cache['var2'];  // эквивалентно: $value2 = $cache->get('var2');\n```\n\n\n### Ключи кэша <span id=\"cache-keys\"></span>\n\nКаждый элемент данных, хранящийся в кэше, идентифицируется ключом. Когда вы сохраняете элемент данных в кэше, необходимо указать для него ключ. Позже, когда вы извлекаете элемент данных из кэша, вы также должны предоставить соответствующий ключ.\n\nВы можете использовать строку или произвольное значение в качестве ключа кэша. Если ключ не строка, то он будет автоматически сериализован в строку.\n\nОбычно ключ задаётся массивом всех значимых частей. Например, для хранения информации о таблице в [[yii\\db\\Schema]] используются следующие части для ключа:\n\n```php\n[\n    __CLASS__,              // Название класса схемы.\n    $this->db->dsn,         // Данные для соединения с базой.\n    $this->db->username,    // Логин для соединения с базой.\n    $name,                  // Название таблицы.\n];\n```\n\nКак вы можете видеть, ключ строится так, чтобы однозначно идентифицировать данные таблицы.\n\nЕсли одно хранилище кэша используется несколькими приложениями, стоит указать префикс ключа во избежание конфликтов. Сделать это можно путём настройки [[yii\\caching\\Cache::keyPrefix]]:\n\n```php\n'components' => [\n    'cache' => [\n        'class' => 'yii\\caching\\ApcCache',\n        'keyPrefix' => 'myapp', // уникальный префикс ключей кэша\n    ],\n],\n```\n\nДля обеспечения совместимости должны быть использованы только алфавитно-цифровые символы.\n\n### Срок действия кэша <span id=\"cache-expiration\"></span>\n\nЭлементы данных, хранимые в кэше, остаются там навсегда и могут быть удалены только из-за особенностей функционирования хранилища (например, место для кэширования заполнено и старые данные удаляются). Чтобы изменить этот режим, вы можете передать истечение срока действия ключа при вызове метода [[yii\\caching\\Cache::set()|set()]]. Параметр указывает на то,  сколько секунд элемент кэша может считаться актуальным. Если срок годности ключа истёк, [[yii\\caching\\Cache::get()|get()]] вернёт `false`:\n\n```php\n// Хранить данные в кэше не более 45 секунд\n$cache->set($key, $data, 45);\n\nsleep(50);\n\n$data = $cache->get($key);\nif ($data === false) {\n    // срок действия истек или ключ не найден в кэше\n}\n```\n\nНачиная с версии 2.0.11 вы можете изменить значение по умолчанию (бесконечность) для длительности кэширования задав\n[[yii\\caching\\Cache::$defaultDuration|defaultDuration]] в конфигурации компонента кэша. Таким образом, можно будет\nне передавать значение `duration` в [[yii\\caching\\Cache::set()|set()]] каждый раз.\n\n### Зависимости кэша <span id=\"cache-dependencies\"></span>\n\nВдобавок к изменению срока действия ключа элемент может быть признан недействительным из-за *изменения зависимостей*. К примеру, [[yii\\caching\\FileDependency]] представляет собой зависимость от времени изменения файла. Когда это время изменяется, любые устаревшие данные, найденные в кэше, должны быть признаны недействительным, а [[yii\\caching\\Cache::get()|get()]] в этом случае должен вернуть `false`.\n\nЗависимости кэша представлены в виде объектов потомков класса [[yii\\caching\\Dependency]]. Когда вы вызываете [[yii\\caching\\Cache::set()|set()]] метод, чтобы сохранить элемент данных в кэше, вы можете передать туда зависимость.\n\nНапример:\n\n```php\n// Создать зависимость от времени модификации файла example.txt.\n$dependency = new \\yii\\caching\\FileDependency(['fileName' => 'example.txt']);\n\n// Данные устаревают через 30 секунд.\n// Данные могут устареть и раньше, если example.txt будет изменён.\n$cache->set($key, $data, 30, $dependency);\n\n// Кэш будет проверен, если данные устарели.\n// Он также будет проверен, если указанная зависимость была изменена.\n// Вернется `false`, если какое-либо из этих условий выполнено.\n$data = $cache->get($key);\n```\n\nНиже приведен список доступных зависимостей кэша:\n\n- [[yii\\caching\\ChainedDependency]]: зависимость меняется, если любая зависимость в цепочке изменяется;\n- [[yii\\caching\\DbDependency]]: зависимость меняется, если результат некоторого определенного SQL запроса изменён;\n- [[yii\\caching\\ExpressionDependency]]: зависимость меняется, если результат определенного PHP выражения изменён;\n- [[yii\\caching\\CallbackDependency]]: зависимость меняется, если результат коллбэк функции изменён;\n- [[yii\\caching\\FileDependency]]: зависимость меняется, если изменилось время последней модификации файла;\n- [[yii\\caching\\TagDependency]]: Связывает кэшированные данные элемента с одним или несколькими тегами. Вы можете аннулировать кэширование данных элементов с заданным тегом(тегами) по вызову. [[yii\\caching\\TagDependency::invalidate()]];\n\n## Кэширование запросов <span id=\"query-caching\"></span>\n\nКэширование запросов - это специальная функция, построенная на основе кэширования данных.\nОна предназначена для кэширования результатов запросов к базе данных.\n\nКэширование запросов требует [[yii\\db\\Connection|DB connection]] действительный `cache` компонента приложения. Предполагая, что `$db` это [[yii\\db\\Connection]] экземпляр, простое использование запросов кэширования происходит следующим образом:\n\n```php\n$result = $db->cache(function ($db) {\n\n    // Результат SQL запроса будет возвращен из кэша, если\n    // кэширование запросов включено, и результат запроса присутствует в кэше.\n    return $db->createCommand('SELECT * FROM customer WHERE id=1')->queryOne();\n\n});\n```\n\nКэширование запросов может быть использовано как для [DAO](db-dao.md), так и для [ActiveRecord](db-active-record.md):\n\n```php\n$result = Customer::getDb()->cache(function ($db) {\n    return Customer::find()->where(['id' => 1])->one();\n});\n```\n\n> Info: Некоторые СУБД (например, [MySQL](https://dev.mysql.com/doc/refman/5.6/en/query-cache.html)) поддерживают кэширование запросов любого механизма на стороне сервера БД. КЗ описано разделом выше. Оно имеет безусловное преимущество, поскольку, благодаря ему, можно указать гибкие зависимости кэша и это более эффективно.\n\n\n### Очистка кэша <span id=\"cache-flushing\"></span>\n\nДля очистки всего кэша, вы можете вызвать [[yii\\caching\\Cache::flush()]].\n\nТакже вы можете очистить кэш из консоли, вызвав `yii cache/flush`.\n - `yii cache`: отображает список доступных кэширующих компонентов приложения\n - `yii cache/flush cache1 cache2`: очищает кэш в компонентах `cache1`, `cache2` (можно передать несколько названий\n компонентов кэширования, разделяя их пробелом)\n - `yii cache/flush-all`: очищает кэш во всех кэширующих компонентах приложения\n - `yii cache/flush-schema db`: очищает кеш схемы базы данных для данного компонента соединения\n\n> Info: Консольное приложение использует отдельный конфигурационный файл по умолчанию. Для получения должного\nрезультата, убедитесь, что в конфигурациях консольного и веб-приложения у вас одинаковые компоненты кэширования.\n\n\n### Конфигурации <span id=\"query-caching-configs\"></span>\n\nКэширование запросов имеет три глобальных конфигурационных параметра через [[yii\\db\\Connection]]:\n\n* [[yii\\db\\Connection::enableQueryCache|enableQueryCache]]: включить или выключить кэширование запросов; По умолчанию `true`. Стоит отметить, что для использования кэширования вам может понадобиться компонент cache, как показано в [[yii\\db\\Connection::queryCache|queryCache]];\n* [[yii\\db\\Connection::queryCacheDuration|queryCacheDuration]]: количество секунд кэширования результата. Для бесконечного кэша используйте `0`. Именно это значение выставляется [[yii\\db\\Connection::cache()]], если не указать время явно;\n* [[yii\\db\\Connection::queryCache|queryCache]]: ID компонента кэширования. По умолчанию `'cache'`. Кэширования запросов работает только в том случае, если используется компонент приложения кэш.\n\n### Использование <span id=\"query-caching-usages\"></span>\n\nВы можете использовать [[yii\\db\\Connection::cache()]], если у вас есть несколько SQL запросов, которые необходимо закэшировать:\n\n```php\n$duration = 60;     // кэширование результата на 60 секунд\n$dependency = ...;  // параметры зависимости\n\n$result = $db->cache(function ($db) {\n\n    // ... выполнять SQL запросы здесь ...\n\n    return $result;\n\n}, $duration, $dependency);\n```\n\nЛюбые SQL запросы в анонимной функции будут кэшироваться в течение указанного промежутка времени и с помощью заданной зависимости. Если результат запроса в кэше актуален, запрос будет пропущен, и вместо этого из кэша будет возвращен результат. Если вы не укажете параметр `$duration`, то значение [[yii\\db\\Connection::queryCacheDuration|queryCacheDuration]] будет использоваться вместо него.\n\nИногда в пределах `cache()` вы можете захотеть отключить кэширование запроса. В этом случае вы можете использовать [[yii\\db\\Connection::noCache()]].\n\n```php\n$result = $db->cache(function ($db) {\n\n    // SQL запросы, которые используют кэширование\n\n    $db->noCache(function ($db) {\n\n        // SQL запросы, которые не используют кэширование\n\n    });\n\n    // ...\n\n    return $result;\n});\n```\n\nЕсли вы просто хотите использовать кэширование для одного запроса, вы можете вызвать [[yii\\db\\Command::cache()]] при построении команды. Например:\n\n```php\n// использовать кэширование запросов и установить срок действия кэша на 60 секунд\n$customer = $db->createCommand('SELECT * FROM customer WHERE id=1')->cache(60)->queryOne();\n```\n\nВы также можете использовать [[yii\\db\\Command::noCache()]] для отключения кэширования запросов для одной команды. Например:\n\n```php\n$result = $db->cache(function ($db) {\n\n    // Используется кэширование SQL запросов\n\n    // не использовать кэширование запросов для этой команды\n    $customer = $db->createCommand('SELECT * FROM customer WHERE id=1')->noCache()->queryOne();\n\n    // ...\n\n    return $result;\n});\n```\n\n\n### Ограничения <span id=\"query-caching-limitations\"></span>\n\nКэширование запросов не работает с результатами запросов, которые содержат обработчики ресурсов. Например, при использовании столбца типа `BLOB` в некоторых СУБД, в качестве результата запроса будет возвращен указатель на ресурс для этого столбца данных.\n\nНекоторые кэш хранилища имеют ограничение в размере данных. Например, Memcache ограничивает максимальный размер каждой записи до 1 Мб. Таким образом, если результат запроса превышает этот предел, данные не будут закэшированы.\n"
  },
  {
    "path": "docs/guide-ru/caching-fragment.md",
    "content": "Кэширование фрагментов\n================\n\nКэширование фрагментов относится к кэшированию фрагментов страницы. Например, если страница отображает в таблице суммарные годовые продажи, мы можем сохранить эту таблицу в кэше с целью экономии времени, требуемого для создания таблицы при каждом запросе. Кэширование фрагментов основано на [кэшировании данных](caching-data.md).\n\nДля кэширования фрагментов используйте следующий код в [представлении](structure-views.md):\n\n```php\nif ($this->beginCache($id)) {\n\n    // ... здесь создаём содержимое ...\n\n    $this->endCache();\n}\n```\n\nТаким образом заключите то, что вы хотите закэшировать между вызовом [[yii\\base\\View::beginCache()|beginCache()]] и\n[[yii\\base\\View::endCache()|endCache()]]. Если содержимое будет найдено в кэше, [[yii\\base\\View::beginCache()|beginCache()]]\nотобразит закэшированное содержимое и вернёт `false`, минуя генерацию содержимого.\nВ противном случае будет выполнен код генерации контента и, когда будет вызван [[yii\\base\\View::endCache()|endCache()]], то сгенерированное содержимое будет записано и сохранено в кэше.\n\nТак же как для [кэширования данных](caching-data.md), для кэширования фрагментов требуется уникальный идентификатор для определения кэшируемого фрагмента.\n\n\n## Параметры кэширования <span id=\"caching-options\"></span>\n\nВызывая метод [[yii\\base\\View::beginCache()|beginCache()]], мы можем передать в качестве второго аргумента массив, содержащий параметры кэширования для управления кэшированием фрагмента. Заглядывая за кулисы, можно увидеть, что этот массив будет использоваться для настройки виджета [[yii\\widgets\\FragmentCache]], который реализует фактическое кэширование фрагментов.\n\n### Срок хранения <span id=\"duration\"></span>\n\nНаверное, наиболее часто используемым параметром является [[yii\\widgets\\FragmentCache::duration|duration]].\nОн определяет, какое количество секунд содержимое будет оставаться действительным (корректным). Следующий код помещает фрагмент в кэш не более, чем на час:\n\n```php\nif ($this->beginCache($id, ['duration' => 3600])) {\n\n    // ... здесь создаём содержимое ...\n\n    $this->endCache();\n}\n```\n\nЕсли мы не установим длительность (срок хранения), она будет равна значению по умолчанию (60 секунд). Это значит, что кэшированное содержимое станет недействительным через 60 секунд.\n\n\n### Зависимости <span id=\"dependencies\"></span>\n\nТак же как и [кэширование данных](caching-data.md#cache-dependencies), кэшируемое содержимое фрагмента тоже может иметь зависимости. Например, отображение содержимого сообщения зависит от того, изменено или нет это сообщение.\n\nДля определения зависимости мы устанавливаем параметр [[yii\\widgets\\FragmentCache::dependency|dependency]], который может быть либо объектом [[yii\\caching\\Dependency]], либо массивом настроек, который может быть использован для создания объекта [[yii\\caching\\Dependency]]. Следующий код определяет содержимое фрагмента, зависящее от изменения значения столбца `updated_at`:\n\n```php\n$dependency = [\n    'class' => 'yii\\caching\\DbDependency',\n    'sql' => 'SELECT MAX(updated_at) FROM post',\n];\n\nif ($this->beginCache($id, ['dependency' => $dependency])) {\n\n    // ... здесь создаём содержимое ...\n\n    $this->endCache();\n}\n```\n\n\n### Вариации <span id=\"variations\"></span>\n\nКэшируемое содержимое может быть изменено в соответствии с некоторыми параметрами. Например, для веб-приложений, поддерживающих несколько языков, одна и та же часть кода может создавать содержимое на нескольких языках. Поэтому у вас может возникнуть желание кэшировать содержимое в зависимости от текущего языка приложения.\n\nЧтобы задать вариации кэша, установите параметр [[yii\\widgets\\FragmentCache::variations|variations]], который должен быть массивом, содержащим скалярные значения, каждое из которых представляет определенный коэффициент вариации. Например,\nчтобы кэшировать содержимое в зависимости от языка приложения, вы можете использовать следующий код:\n\n```php\nif ($this->beginCache($id, ['variations' => [Yii::$app->language]])) {\n\n    // ... здесь создаём содержимое ...\n\n    $this->endCache();\n}\n```\n\n\n### Переключение кэширования <span id=\"toggling-caching\"></span>\n\nИногда может потребоваться включать кэширование фрагментов только для определённых условий. Например, страницу с формой мы хотим кэшировать только тогда, когда обращение к ней произошло впервые (посредством GET запроса). Любое последующее отображение формы (посредством POST запроса) не должно быть кэшировано, потому что может содержать данные, введённые пользователем. Для этого мы задаём параметр [[yii\\widgets\\FragmentCache::enabled|enabled]]:\n\n```php\nif ($this->beginCache($id, ['enabled' => Yii::$app->request->isGet])) {\n\n    // ... здесь создаём содержимое ...\n\n    $this->endCache();\n}\n```\n\n\n## Вложенное кэширование <span id=\"nested-caching\"></span>\n\nКэширование фрагментов может быть вложенным. Это значит, что кэшируемый фрагмент окружён более крупным фрагментом (содержится в нём), который также кэшируется. Например, комментарии кэшируются во внутреннем фрагменте кэша, и они же кэшируются вместе с содержимым сообщения во внешнем фрагменте кэша. Следующий код демонстрирует, как два фрагмента кэша могут быть вложенными:\n\n```php\nif ($this->beginCache($id1)) {\n\n    // ...логика создания контента...\n\n    if ($this->beginCache($id2, $options2)) {\n\n        // ...логика создания контента...\n\n        $this->endCache();\n    }\n\n    // ...логика создания контента...\n\n    $this->endCache();\n}\n```\n\nПараметры кэширования могут быть различными для вложенных кэшей. Например, внутренний и внешний кэши в вышеприведённом примере могут иметь разные сроки хранения. Даже когда данные внешнего кэша уже не являются актуальными, внутренний кэш может содержать актуальный фрагмент. Тем не менее, обратное неверно. Если внешний кэш актуален, данные будут отдаваться из него, даже если внутренний кэш содержит устаревшие данные. Следует проявлять осторожность при выставлении срока хранения и задания зависимостей для вложенных кэшей. В противном случае вы можете получить устаревшие данные.\n\n\n## Динамическое содержимое <span id=\"dynamic-content\"></span>\n\nКогда используется кэширование фрагментов, вы можете столкнуться с ситуацией, когда большой фрагмент содержимого статичен, за исключением одного или нескольких мест. Например, заголовок страницы может отображаться в главном меню вместе с\nименем текущего пользователя. Еще одна проблема в том, что содержимое, которое было закэшировано, может содержать PHP код, который должен выполняться для каждого запроса (например, код для регистрации в asset bundle). Обе проблемы могут быть решены с помощью так называемой функции *динамического содержимого*.\n\nДинамическое содержимое значит, что часть вывода не будет закэширована, даже если она заключена в кэширование фрагментов. Чтобы сделать содержимое динамическим постоянно, оно должно быть создано, используя специальный PHP код.\n\nВы можете вызвать [[yii\\base\\View::renderDynamic()]] в пределах кэширования фрагмента для вставки динамического содержимого\nв нужное место, как в примере ниже:\n\n```php\nif ($this->beginCache($id1)) {\n\n    // ...логика создания контента...\n\n    echo $this->renderDynamic('return Yii::$app->user->identity->name;');\n\n    // ...логика создания контента...\n\n    $this->endCache();\n}\n```\n\nМетод [[yii\\base\\View::renderDynamic()|renderDynamic()]] принимает некоторую часть PHP кода как параметр.\nВозвращаемое значение этого кода будет вставлено в динамическое содержимое. Этот PHP код будет выполняться для каждого запроса независимо от того, находится ли он внутри кэширования фрагмента или нет.\n"
  },
  {
    "path": "docs/guide-ru/caching-http.md",
    "content": "HTTP кэширование\n============\n\nКроме серверного кэширования, которое мы описали в предыдущих разделах, веб-приложения также могут использовать кэширование на стороне клиента, чтобы сэкономить время для формирования и передачи одного и того же содержания страницы.\n\nЧтобы использовать кэширование на стороне клиента, вы можете настроить [[yii\\filters\\HttpCache]] в качестве фильтра для действия контроллера, отображающего результат, который может быть закэширован на стороне клиента. [[yii\\filters\\HttpCache|HttpCache]] работает только для `GET` и `HEAD` запросов. Для этих запросов он может обрабатывать три вида HTTP заголовков, относящихся к кэшированию:\n\n* [[yii\\filters\\HttpCache::lastModified|Last-Modified]]\n* [[yii\\filters\\HttpCache::etagSeed|Etag]]\n* [[yii\\filters\\HttpCache::cacheControlHeader|Cache-Control]]\n\n\n## Заголовок `Last-Modified` <span id=\"last-modified\"></span>\n\nЗаголовок `Last-Modified` использует временную метку timestamp, чтобы показать, была ли страница изменена после того, как клиент закэшировал её.\n\nВы можете настроить свойство [[yii\\filters\\HttpCache::lastModified]], чтобы включить отправку заголовка `Last-Modified`. Свойство должно содержать PHP-функцию, возвращающую временную метку UNIX timestamp времени последнего изменения страницы. Сигнатура PHP-функции должна совпадать со следующей:\n\n```php\n/**\n * @param Action $action объект действия, которое в настоящее время обрабатывается\n * @param array $params значение свойства \"params\"\n * @return int временная метка UNIX timestamp, возвращающая время последнего изменения страницы\n */\nfunction ($action, $params)\n```\n\nНиже приведён пример использования заголовка `Last-Modified`:\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\HttpCache',\n            'only' => ['index'],\n            'lastModified' => function ($action, $params) {\n                $q = new \\yii\\db\\Query();\n                return $q->from('post')->max('updated_at');\n            },\n        ],\n    ];\n}\n```\n\nПриведенный выше код устанавливает, что HTTP кэширование должно быть включено только для действия `index`. Он\nгенерирует `Last-Modified` HTTP заголовок на основе времени последнего сообщения. Когда браузер в первый раз посещает страницу `index`, то страница будет сгенерирована на сервере и отправлена в браузер; если браузер снова зайдёт на эту страницу и с тех пор ни один пост не обновится, то сервер не будет пересоздавать страницу и браузер будет использовать закэшированную на стороне клиента версию. В результате будет пропущено как создание страницы на стороне сервера, так и передача содержания страницы клиенту.\n\n\n## Заголовок `ETag` <span id=\"etag\"></span>\n\nЗаголовок \"Entity Tag\" (или коротко `ETag`) используется для передачи хэша содержания страницы. Если страница была изменена, то хэш страницы тоже изменится. Сравнивая хэш на стороне клиента с хэшем, генерируемым на стороне сервера, кэш может определить, была ли станица изменена и требуется ли её передавать заново.\n\nВы можете настроить свойство [[yii\\filters\\HttpCache::etagSeed]], чтобы включить передачу заголовка `ETag`. Свойство должно содержать PHP-функцию, возвращающий seed для генерации ETag хэша. Сигнатура PHP-функции должна совпадать со следующей:\n\n```php\n/**\n * @param Action $action объект действия, которое в настоящее время обрабатывается\n * @param array $params значение свойства \"params\"\n * @return string строка, используемая как seed для генерации ETag хэша\n */\nfunction ($action, $params)\n```\n\nНиже приведён пример использования заголовка `ETag`:\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\HttpCache',\n            'only' => ['view'],\n            'etagSeed' => function ($action, $params) {\n                $post = $this->findModel(\\Yii::$app->request->get('id'));\n                return serialize([$post->title, $post->content]);\n            },\n        ],\n    ];\n}\n```\n\nПриведенный выше код устанавливает, что HTTP кэширование должно быть включено только для действия `view`. Он\nгенерирует `ETag` HTTP заголовок на основе заголовка и содержания последнего сообщения. Когда браузер в первый раз посещает страницу `view`, то страница будет сгенерирована на сервере и отправлена в браузер; если браузер снова зайдёт на эту страницу и с тех пор ни один пост не обновится, то сервер не будет пересоздавать страницу и браузер будет использовать закэшированную на стороне клиента версию. В результате будет пропущено как создание страницы на стороне сервера, так и передача содержание страницы клиенту.\n\nETags позволяет применять более сложные и/или более точные стратегии кэширования, чем заголовок `Last-Modified`.\nНапример, ETag станет невалидным (некорректным), если на сайте была включена другая тема\n\nРесурсоёмкая генерация ETag может противоречить цели использования `HttpCache` и внести излишнюю нагрузку,\nт.к. он должен пересоздаваться при каждом запросе. Попробуйте найти простое выражение, которое инвалидирует кэш, если содержание страницы было изменено.\n\n> Note: В соответствии с [RFC 7232](https://datatracker.ietf.org/doc/html/rfc7232#section-2.4),\n  `HttpCache` будет отправлять как `ETag` заголовок, так и `Last-Modified` заголовок, если они оба были настроены.\n  И если клиент отправляет как `If-None-Match` заголовок, так и `If-Modified-Since` заголовок, то только первый из них будет принят.\n\n\n## Заголовок `Cache-Control` <span id=\"cache-control\"></span>\n\nЗаголовок `Cache-Control` определяет общую политику кэширования страниц. Вы можете включить его отправку, настроив свойство [[yii\\filters\\HttpCache::cacheControlHeader]]. По-умолчанию будет отправлен следующий заголовок:\n\n```\nCache-Control: public, max-age=3600\n```\n\n## Ограничитель кэша сессий <span id=\"session-cache-limiter\"></span>\n\nКогда на странице используются сессии, PHP автоматически отправляет некоторые связанные с кэшем HTTP заголовки, определённые в настройке `session.cache_limiter` в `php.ini`. Эти заголовки могут вмешиваться или отключать кэширование, которое вы ожидаете от `HttpCache`. Чтобы предотвратить эту проблему, по умолчанию `HttpCache` будет автоматически отключать отправку этих заголовков. Если вы хотите изменить это поведение, вы должны настроить свойство [[yii\\filters\\HttpCache::sessionCacheLimiter]]. Это свойство может принимать строковое значение, включая `public`, `private`, `private_no_expire` и `nocache`. Пожалуйста, обратитесь к руководству PHP о [session_cache_limiter()](https://www.php.net/manual/ru/function.session-cache-limiter.php)\nдля объяснения этих значений.\n\n\n## SEO подтекст <span id=\"seo-implications\"></span>\n\nПоисковые боты, как правило, с уважением относятся к заголовкам кэширования. Поскольку некоторые из поисковых систем имеют ограничение на количество страниц для одного домена, которые они обрабатывают в течение определенного промежутка времени, то предоставление заголовков кэширования может помочь индексации, поскольку будет уменьшено число обрабатываемых страниц.\n"
  },
  {
    "path": "docs/guide-ru/caching-overview.md",
    "content": "Кэширование\n==========\n\nКэширование — это простой и эффективный способ повысить производительность веб-приложения. Сохраняя относительно\nстатичные данные в кэше и извлекая их из кэша, когда потребуется, мы экономим время, затрачиваемое на генерацию\nданных с нуля каждый раз.\n\nКэширование может использоваться на различных уровнях и в различных местах веб-приложения. На стороне сервера, на более\nнизком уровне мы используем кэширование для хранения основных данных, таких как список последних полученных из базы\nданных статьей. На более высоком уровне кэш может использоваться для хранения фрагментов или целых веб-страниц. Таких,\nнапример, как результат рендеринга последних статьей. На стороне клиента для сохранения содержимого недавно посещенных\nстраниц в кэше браузера может использоваться HTTP-кэширование.\n\nYii поддерживает все эти механизмы кэширования:\n\n* [Кэширование данных](caching-data.md)\n* [Кэширование фрагментов](caching-fragment.md)\n* [Кэширование страниц](caching-page.md)\n* [HTTP-кэширование](caching-http.md)\n"
  },
  {
    "path": "docs/guide-ru/caching-page.md",
    "content": "Кэширование страниц\n=================\n\nКэширование страниц — это кэширование всего содержимого страницы на стороне сервера. Позже, когда эта страница\nбудет снова запрошена, сервер вернет её из кэша вместо того, чтобы генерировать её заново.\n\nКэширование страниц осуществляется при помощи [фильтра действия](structure-filters.md) [[yii\\filters\\PageCache]] и\nможет быть использовано в классе контроллера следующим образом:\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\PageCache',\n            'only' => ['index'],\n            'duration' => 60,\n            'variations' => [\n                \\Yii::$app->language,\n            ],\n            'dependency' => [\n                'class' => 'yii\\caching\\DbDependency',\n                'sql' => 'SELECT COUNT(*) FROM post',\n            ],\n        ],\n    ];\n}\n```\n\nПриведённый код задействует кэширование только для действия `index`. Содержимое страницы кэшируется максимум на 60 секунд\nи варьируется в зависимости от текущего языка приложения. Кэшированная страница должна быть признана просроченной, если\nобщее количество постов изменилось.\n\nКэширование страниц очень похоже на [кэширование фрагментов](caching-fragment.md). В обоих случаях поддерживаются параметры\n`duration` (продолжительность), `dependencies` (зависимости), `variations` (вариации), и `enabled` (включен). Главное\nотличие заключается в том, что кэширование страницы реализовано в виде [фильтра действия](structure-filters.md),\nа кэширование фрагмента в виде [виджета](structure-widgets.md).\n\nВы можете использовать вместе [кэширование фрагмента](caching-fragment.md),\n[динамическое содержимое](caching-fragment.md#dynamic-content) и кэширование страницы.\n"
  },
  {
    "path": "docs/guide-ru/concept-aliases.md",
    "content": "Псевдонимы\n=========\n\nПсевдонимы используются для обозначения путей к файлам или URL адресов и помогают избежать использования абсолютных путей\nили URL в коде. Для того, чтобы не перепутать псевдоним с обычным путём к файлу или URL, он должен начинаться с `@`. В Yii\nимеется множество заранее определённых псевдонимов. Например, `@yii` указывает на директорию, в которую был установлен\nYii framework, а `@web` можно использовать для получения базового URL текущего приложения.\n\n\nСоздание псевдонимов <span id=\"defining-aliases\"></span>\n----------------------------------------------\n\nДля создания псевдонима пути к файлу или URL используется метод [[Yii::setAlias()]]:\n\n```php\n// псевдоним пути к файлу\nYii::setAlias('@foo', '/path/to/foo');\n\n// псевдоним URL\nYii::setAlias('@bar', 'https://www.example.com');\n\n// псевдоним конкретного файла, содержащего класс \\foo\\Bar\nYii::setAlias('@foo/Bar.php', '/definitely/not/foo/Bar.php');\n```\n\n> Note: псевдоним пути к файлу или URL *не* обязательно указывает на существующий файл или ресурс.\n\nИспользуя уже заданный псевдоним, вы можете получить на основе него новый без вызова [[Yii::setAlias()]]. Сделать это\nможно, добавив в его конец `/`, за которым следует один или более сегментов пути. Псевдонимы, определённые при помощи\n[[Yii::setAlias()]], являются *корневыми псевдонимами*, в то время как полученные из них называются *производными\nпсевдонимами*. К примеру, `@foo` является корневым псевдонимом, а `@foo/bar/file.php` — производным.\n\nВы можете задать новый псевдоним, используя ранее созданный псевдоним (не важно, корневой он или производный):\n\n```php\nYii::setAlias('@foobar', '@foo/bar');\n```\n\nКорневые псевдонимы, как правило, создаются на этапе [предварительной загрузки (bootstrapping)](runtime-bootstrapping.md).\nНапример, вы можете вызвать [[Yii::setAlias()]] во [входном скрипте](structure-entry-scripts.md). Для удобства, в\n[приложении (Application)](structure-applications.md) предусмотрено свойство `aliases`, которое можно задать через\n[конфигурацию приложения](concept-configurations.md):\n\n```php\nreturn [\n    // ...\n    'aliases' => [\n        '@foo' => '/path/to/foo',\n        '@bar' => 'https://www.example.com',\n    ],\n];\n```\n\n\nПреобразование псевдонимов <span id=\"resolving-aliases\"></span>\n----------------------------------------------------\n\nМетод [[Yii::getAlias()]] преобразует корневой псевдоним в путь к файлу или URL, который этот псевдоним представляет.\nЭтот же метод может работать и с производными псевдонимами:\n\n```php\necho Yii::getAlias('@foo');               // выведет: /path/to/foo\necho Yii::getAlias('@bar');               // выведет: https://www.example.com\necho Yii::getAlias('@foo/bar/file.php');  // выведет: /path/to/foo/bar/file.php\n```\n\nПуть или URL, представленный производным псевдонимом, определяется путём замены в нём части, соответствующей корневому\nпсевдониму, на соответствующий ему путь или URL.\n\n> Note: Метод [[Yii::getAlias()]] не проверяет фактического существования получаемого пути или URL.\n\nКорневой псевдоним может содержать знаки `/`. При этом метод [[Yii::getAlias()]] корректно определит, какая часть\nпсевдонима является корневой и верно сформирует путь или URL:\n\n```php\nYii::setAlias('@foo', '/path/to/foo');\nYii::setAlias('@foo/bar', '/path2/bar');\nYii::getAlias('@foo/test/file.php');  // выведет: /path/to/foo/test/file.php\nYii::getAlias('@foo/bar/file.php');   // выведет: /path2/bar/file.php\n```\n\nЕсли бы `@foo/bar` не был объявлен корневым псевдонимом, последняя строка вывела бы `/path/to/foo/bar/file.php`.\n\n\nИспользование псевдонимов <span id=\"using-aliases\"></span>\n------------------------------------------------\n\nПсевдонимы распознаются во многих частях Yii без необходимости предварительно вызывать [[Yii::getAlias()]] для\nполучения пути или URL. Например, [[yii\\caching\\FileCache::cachePath]] принимает как обычный путь к файлу, так и\nпсевдоним пути благодаря префиксу `@`, который позволяет их различать.\n\n```php\nuse yii\\caching\\FileCache;\n\n$cache = new FileCache([\n    'cachePath' => '@runtime/cache',\n]);\n```\n\nДля того, чтобы узнать, поддерживает ли метод или свойство псевдонимы, обратитесь к документации API.\n\n\nЗаранее определённые псевдонимы <span id=\"predefined-aliases\"></span>\n----------------------------------------------------------\n\nВ Yii заранее определены псевдонимы для часто используемых путей к файлам и URL:\n\n- `@yii`: директория, в которой находится файл `BaseYii.php` (директория фреймворка).\n- `@app`: [[yii\\base\\Application::basePath|базовый путь]] текущего приложения.\n- `@runtime`: [[yii\\base\\Application::runtimePath|директория runtime]] текущего приложения.\n- `@vendor`: [[yii\\base\\Application::vendorPath|директория vendor Composer]].\n- `@webroot`: вебрут текущего веб приложения (там где находится [входной скрипт](structure-entry-scripts.md) `index.php`).\n- `@web`: базовый URL текущего приложения.\n\nПсевдоним `@yii` задаётся в момент подключения файла `Yii.php` во [входном скрипте](structure-entry-scripts.md).\nОстальные псевдонимы задаются в конструкторе приложения в момент применения [конфигурации](concept-configurations.md).\n\n\nПсевдонимы расширений <span id=\"extension-aliases\"></span>\n------------------------------------------------\n\nДля каждого [расширения](structure-extensions.md), устанавливаемого через Composer, автоматически задаётся псевдоним.\nЕго имя соответствует корневому пространству имён расширения в соответствии с его `composer.json`. Псевдоним представляет\nпуть к корневой директории пакета. Например, если вы установите расширение `yiisoft/yii2-jui`, то вам автоматически станет\nдоступен псевдоним `@yii/jui`. Он создаётся на этапе [первоначальной загрузки (bootstrapping)](runtime-bootstrapping.md)\nпримерно так:\n\n```php\nYii::setAlias('@yii/jui', 'VendorPath/yiisoft/yii2-jui');\n```\n"
  },
  {
    "path": "docs/guide-ru/concept-autoloading.md",
    "content": "Автозагрузка классов\n=================\n\nПоиск и подключение файлов классов в Yii реализовано при помощи\n[автозагрузки классов](https://www.php.net/manual/ru/language.oop5.autoload.php). Фреймворк предоставляет свой быстрый,\nсовместимый с [PSR-4](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md)\nавтозагрузчик, который устанавливается в момент подключения `Yii.php`.\n\n> Note: Для простоты повествования, в этом разделе мы будем говорить только об автозагрузке классов. Тем не менее,\n  всё описанное применимо к интерфейсам и трейтам.\n\nКак использовать автозагрузчик Yii <span id=\"using-yii-autoloader\"></span>\n--------------------------------------------------------------\n\nПри использовании автозагрузчика классов Yii следует соблюдать два простых правила создания и именования классов:\n\n* Каждый класс должен принадлежать [пространству имён](https://www.php.net/manual/ru/language.namespaces.php)\n  (то есть `foo\\bar\\MyClass`).\n* Каждый класс должен находиться в отдельном файле, путь к которому определятся следующим правилом:\n\n```php\n// $className — это абсолютное имя класса без начального \"\\\"\n$classFile = Yii::getAlias('@' . str_replace('\\\\', '/', $className) . '.php');\n```\n\nНапример, если абсолютное имя класса `foo\\bar\\MyClass`, то [псевдоним пути](concept-aliases.md) данного файла будет\n`@foo/bar/MyClass.php`. Для того, чтобы данный псевдоним можно было преобразовать в путь к файлу, необходимо чтобы\nлибо `@foo` либо `@foo/bar` являлся [корневым псевдонимом](concept-aliases.md#defining-aliases).\n\nПри использовании [шаблона приложения basic](start-installation.md) вы можете хранить свои классы в пространстве имён `app`.\nВ этом случае они будут загружаться автоматически, без создания нового псевдонима. Это работает, потому что `@app`\nявляется [заранее определённым псевдонимом](concept-aliases.md#predefined-aliases), и такое имя класса, как\n`app\\components\\MyClass`, в соответствии с описанным выше алгоритмом, преобразуется в путь\n`директорияПриложения/components/MyClass.php`.\n\nВ [шаблоне приложения advanced](tutorial-advanced-app.md) каждый уровень приложения обладает собственным корневым\nпсевдонимом. Например, для frontend корневым псевдонимом является `@frontend`, а для backend — `@backend`. Это позволяет\nразместить классы frontend в пространство имён `frontend`, а классы backend в пространство имён `backend`. При этом\nклассы будут загружены автоматически.\n\n\nКарта классов <span id=\"class-map\"></span>\n---------------------------------\n\nАвтозагрузчик Yii поддерживает *карту классов*. Эта возможность позволяет указать путь к файлу для каждого имени класса.\nПри загрузке класса автозагрузчик проверяет наличие класса в карте. Если он там есть, соответствующий файл будет загружен\nнапрямую без каких-либо дополнительных проверок. Это делает автозагрузку очень быстрой. Все классы самого фреймворка\nзагружаются именно этим способом.\n\nВы можете добавить класс в карту `Yii::$classMap` следующим образом:\n\n```php\nYii::$classMap['foo\\bar\\MyClass'] = 'path/to/MyClass.php';\n```\n\nДля указания путей к файлам классов можно использовать [псевдонимы](concept-aliases.md). Карту классов необходимо\nсформировать в процессе [первоначальной загрузки](runtime-bootstrapping.md), так как она должна быть готова до\nиспользования классов.\n\n\nИспользование других автозагрузчиков <span id=\"using-other-autoloaders\"></span>\n------------------------------------------------------------------\n\nТак как Yii использует Composer в качестве менеджера зависимостей, рекомендуется дополнительно установить его автозагрузчик.\nЕсли вы используете какие-либо сторонние библиотеки, в которых есть свои автозагрузчики, эти автозагрузчики также необходимо\nустановить.\n\nПри использовании дополнительных автозагрузчиков файл `Yii.php` должен быть подключен *после* их установки. Это позволит\nавтозагрузчику Yii первым пробовать загрузить класс. К примеру, приведённый ниже код взят из\n[входного скрипта](structure-entry-scripts.md) [шаблона приложения basic](start-installation.md). Первая строка устанавливает\nавтозагрузчик Composer, а вторая — автозагрузчик Yii:\n\n```php\nrequire __DIR__ . '/../vendor/autoload.php';\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n```\n\nВы можете использовать автозагрузчик Composer без автозагрузчика Yii. Однако скорость автозагрузки в этом случае\nможет уменьшиться. Также вам будет необходимо следовать правилам автозагрузчика Composer.\n\n> Info: Если вы не хотите использовать автозагрузчик Yii, создайте свою версию файла `Yii.php`\n  и подключите его в [входном скрипте](structure-entry-scripts.md).\n\n\nАвтозагрузка классов расширений <span id=\"autoloading-extension-classes\"></span>\n-------------------------------------------------------------------\n\nАвтозагрузчик Yii может автоматически загружать классы [расширений](structure-extensions.md) в том случае, если соблюдается\nединственное правило: расширение должно правильно описать раздел `autoload` в файле `composer.json`. Более подробно об\nэтом можно узнать из [официальной документации Composer](https://getcomposer.org/doc/04-schema.md#autoload).\n\nЕсли вы не используете автозагрузчик Yii, то классы расширений могут быть автоматически загружены с помощью\nавтозагрузчика Composer.\n"
  },
  {
    "path": "docs/guide-ru/concept-behaviors.md",
    "content": "Поведения\n=========\n\nПоведения (behaviors) — это экземпляры класса [[yii\\base\\Behavior]] или класса, унаследованного от него. Поведения,\nтакже известные как [примеси](https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%81%D1%8C_(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)), позволяют расширять \nфункциональность существующих [[yii\\base\\Component|компонентов]] без необходимости изменения дерева наследования.\nПосле прикрепления поведения к компоненту, его методы и свойства \"внедряются\" в компонент, и становятся доступными\nтак же, как если бы они были объявлены в самом классе компонента. Кроме того, поведение может реагировать на\n[события](concept-events.md), создаваемые компонентом, что позволяет тонко настраивать или модифицировать\nобычное выполнение кода компонента.\n\n\nСоздание поведений <span id=\"defining-behaviors\"></span>\n----------------------------------------------\n\nПоведения создаются путём расширения базового класса [[yii\\base\\Behavior]] или его наследников. Например,\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Behavior;\n\nclass MyBehavior extends Behavior\n{\n    public $prop1;\n\n    private $_prop2;\n\n    public function getProp2()\n    {\n        return $this->_prop2;\n    }\n\n    public function setProp2($value)\n    {\n        $this->_prop2 = $value;\n    }\n\n    public function foo()\n    {\n        // ...\n    }\n}\n```\n\nВ приведённом выше примере объявлен класс поведения `app\\components\\MyBehavior`, содержащий свойства\n`prop1` и `prop2`, а также метод `foo()`. Обратите внимание: свойство `prop2` объявлено с использованием геттера\n`getProp2()` и сеттера `setProp2()`. Это возможно, так как [[yii\\base\\Behavior]] является дочерним классом для\n[[yii\\base\\BaseObject]], который предоставляет возможность определения [свойств](concept-properties.md) через геттеры и сеттеры.\n\nТак как этот класс является поведением, то компоненту, к которому он прикреплён, будут доступны свойства `prop1`\nи `prop2`, а также метод `foo()`.\n\n> Tip: Внутри поведения возможно обращаться к компоненту, к которому оно прикреплено, используя свойство\n  [[yii\\base\\Behavior::owner]].\n\n> Note: При переопределении метода поведения [[yii\\base\\Behavior::__get()]] и/или [[yii\\base\\Behavior::__set()]] необходимо\nтакже переопределить [[yii\\base\\Behavior::canGetProperty()]] и/или [[yii\\base\\Behavior::canSetProperty()]].\n\nОбработка событий компонента\n-------------------------\n\nЕсли поведению требуется реагировать на события компонента, к которому оно прикреплено, то необходимо переопределить\nметод [[yii\\base\\Behavior::events()]]. Например,\n\n```php\nnamespace app\\components;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\base\\Behavior;\n\nclass MyBehavior extends Behavior\n{\n    // ...\n\n    public function events()\n    {\n        return [\n            ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',\n        ];\n    }\n\n    public function beforeValidate($event)\n    {\n        // ...\n    }\n}\n```\n\nМетод [[yii\\base\\Behavior::events()|events()]] должен возвращать список событий и соответствующих им обработчиков.\nВ приведённом выше примере объявлено событие [[yii\\db\\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]]\nи его обработчик `beforeValidate()`. Указать обработчик события можно одним из следующих способов:\n\n* строка с именем метода текущего поведения, как в примере выше;\n* массив, содержащий объект или имя класса, и имя метода, например, `[$object, 'methodName']`;\n* анонимная функция.\n\nФункция обработчика события должна выглядеть, как показано ниже, где `$event` содержит параметр\nсобытия. Более детальная информация приведена в разделе [События](concept-events.md).\n\n```php\nfunction ($event) {\n}\n```\n\nПрикрепление поведений <span id=\"attaching-behaviors\"></span>\n---------------------------------------------------\n\nПрикрепить поведение к [[yii\\base\\Component|компоненту]] можно как статически, так и динамически. На практике\nчаще используется статическое прикрепление.\n\nДля того чтобы прикрепить поведение статически, необходимо переопределить метод [[yii\\base\\Component::behaviors()|behaviors()]]\nкомпонента, к которому его планируется прикрепить. Метод [[yii\\base\\Component::behaviors()|behaviors()]] должен возвращать\nсписок [конфигураций](concept-configurations.md) поведений. Конфигурация поведения представляет собой либо имя класса поведения,\nлибо массив его настроек:\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\nuse app\\components\\MyBehavior;\n\nclass User extends ActiveRecord\n{\n    public function behaviors()\n    {\n        return [\n            // анонимное поведение, прикрепленное по имени класса\n            MyBehavior::class,\n\n            // именованное поведение, прикрепленное по имени класса\n            'myBehavior2' => MyBehavior::class,\n\n            // анонимное поведение, сконфигурированное с использованием массива\n            [\n                'class' => MyBehavior::class,\n                'prop1' => 'value1',\n                'prop2' => 'value2',\n            ],\n\n            // именованное поведение, сконфигурированное с использованием массива\n            'myBehavior4' => [\n                'class' => MyBehavior::class,\n                'prop1' => 'value1',\n                'prop2' => 'value2',\n            ]\n        ];\n    }\n}\n```\n\nВы можете связать имя с поведением, указав его в качестве ключа элемента массива, соответствующего конфигурации\nповедения. В таком случае, поведение называется *именованным*. В примере выше,\nдва именованных поведения: `myBehavior2` и `myBehavior4`. Если с поведением не связано имя, такое поведение называется\n*анонимным*.\n\nДля того, чтобы прикрепить поведение динамически, необходимо вызвать метод [[yii\\base\\Component::attachBehavior()]]\nтребуемого компонента:\n\n```php\nuse app\\components\\MyBehavior;\n\n// прикрепляем объект поведения\n$component->attachBehavior('myBehavior1', new MyBehavior);\n\n// прикрепляем по имени класса поведения\n$component->attachBehavior('myBehavior2', MyBehavior::class);\n\n// прикрепляем, используя массив настроек\n$component->attachBehavior('myBehavior3', [\n    'class' => MyBehavior::class,\n    'prop1' => 'value1',\n    'prop2' => 'value2',\n]);\n```\n\nИспользование метода [[yii\\base\\Component::attachBehaviors()]] позволяет прикрепить несколько поведений за раз.\nНапример,\n\n```php\n$component->attachBehaviors([\n    'myBehavior1' => new MyBehavior,  // именованное поведение\n    MyBehavior::class,          // анонимное поведение\n]);\n```\n\nПрикрепить поведение к компоненту можно также через [конфигурацию](concept-configurations.md), как показано ниже:\n\n```php\n[\n    'as myBehavior2' => MyBehavior::class,\n\n    'as myBehavior3' => [\n        'class' => MyBehavior::class,\n        'prop1' => 'value1',\n        'prop2' => 'value2',\n    ],\n]\n```\n\nБолее детальная информация приведена в разделе [Конфигурации](concept-configurations.md#configuration-format).\n\nИспользование поведений <span id=\"using-behaviors\"></span>\n------------------------------------------------\n\nДля использования поведения, его необходимо прикрепить к [[yii\\base\\Component|компоненту]], как описано выше. После того,\nкак поведение прикреплено к компоненту, его использование не вызывает сложностей.\n\nВы можете обращаться к *публичным* переменным или [свойствам](concept-properties.md), объявленным с использованием\nгеттеров и сеттеров в поведении, через компонент, к которому оно прикреплено:\n\n```php\n// публичное свойство \"prop1\", объявленное в классе поведения\necho $component->prop1;\n$component->prop1 = $value;\n```\n\nАналогично, вы можете вызывать *публичные* методы поведения:\n\n```php\n// публичный метод \"foo()\", объявленный в классе поведения\n$component->foo();\n```\n\nОбратите внимание, хотя `$component` не имеет свойства `prop1` и метода `foo()`, они могут быть использованы,\nкак будто являются членами этого класса.\n\nВ случае, когда два поведения, имеющие свойства или методы с одинаковыми именами, прикреплены к одному компоненту,\nпреимущество будет у поведения, прикрепленного раньше.\n\nЕсли при прикреплении поведения к компоненту указано имя, можно обращаться к поведению по этому имени, как показано ниже:\n\n```php\n$behavior = $component->getBehavior('myBehavior');\n```\n\nТакже можно получить все поведения, прикреплённые к компоненту:\n\n```php\n$behaviors = $component->getBehaviors();\n```\n\nОтвязывание поведений <span id=\"detaching-behaviors\"></span>\n-------------------------------------------------\n\nЧтобы отвязать поведение от компонента, необходимо вызвать метод [[yii\\base\\Component::detachBehavior()]], указав имя,\nсвязанное с поведением:\n\n```php\n$component->detachBehavior('myBehavior1');\n```\n\nТак же, возможно отвязать *все* поведения:\n\n```php\n$component->detachBehaviors();\n```\n\n\nИспользование поведения `TimestampBehavior` <span id=\"using-timestamp-behavior\"></span>\n--------------------------------------------------------------------------\n\nВ заключение, давайте рассмотрим [[yii\\behaviors\\TimestampBehavior]] — поведение, которое позволяет автоматически\nобновлять атрибуты с метками времени при сохранении [[yii\\db\\ActiveRecord|Active Record]] моделей через `insert()`,\n`update()` или `save()`.\n\nДля начала, необходимо прикрепить поведение к классу [[yii\\db\\ActiveRecord|Active Record]], в котором это необходимо:\n\n```php\nnamespace app\\models\\User;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\behaviors\\TimestampBehavior;\n\nclass User extends ActiveRecord\n{\n    // ...\n\n    public function behaviors()\n    {\n        return [\n            [\n                'class' => TimestampBehavior::class,\n                'attributes' => [\n                    ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],\n                    ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'],\n                ],\n                // если вместо метки времени UNIX используется datetime:\n                // 'value' => new Expression('NOW()'),\n            ],\n        ];\n    }\n}\n```\n\nКонфигурация выше описывает следующее:\n\n* при вставке новой записи поведение должно присвоить текущую метку времени UNIX атрибутам `created_at` и `updated_at`;\n* при обновлении существующей записи поведение должно присвоить текущую метку времени UNIX атрибуту `updated_at`.\n\n> Note: Для того, чтобы приведённая выше конфигурация работала с MySQL, тип `created_at` и `updated_at` должен быть\n  `int(11)`. В нём будет храниться UNIX timestamp.\n\nТеперь, если сохранить объект `User`, то в его атрибуты `created_at` и `updated_at` будут автоматически установлены\nзначения метки времени UNIX на момент сохранения записи:\n\n```php\n$user = new User;\n$user->email = 'test@example.com';\n$user->save();\necho $user->created_at;  // выведет метку времени на момент сохранения записи\n```\n\nПоведение [[yii\\behaviors\\TimestampBehavior|TimestampBehavior]] так же содержит полезный метод\n[[yii\\behaviors\\TimestampBehavior::touch()|touch()]], который устанавливает текущую метку времени указанному атрибуту\nи сохраняет его в базу данных:\n\n```php\n$user->touch('login_time');\n```\n\n\nДругие поведения\n----------------\n\nКроме затронутых выше, есть и другие уже реализованные поведения. Как встроенные, так и\nсторонние:\n\n- [[yii\\behaviors\\BlameableBehavior]] - автоматически заполняет указанные атрибуты ID текущего пользователя.\n- [[yii\\behaviors\\SluggableBehavior]] - автоматически заполняет указанный атрибут пригодным для URL текстом, получаемым\n  из 1 или нескольких других атрибутов.\n- [[yii\\behaviors\\AttributeBehavior]] - автоматически задаёт указанное значение одному или нескольким атрибутам\n  ActiveRecord при срабатывании определённых событий.\n- [yii2tech\\ar\\softdelete\\SoftDeleteBehavior](https://github.com/yii2tech/ar-softdelete) - предоставляет методы для\n  «мягкого» удаления и восстановления ActiveRecord. То есть выставляет статус или флаг, который показывает, что\n  запись удалена.\n- [yii2tech\\ar\\position\\PositionBehavior](https://github.com/yii2tech/ar-position) - позволяет управлять порядком\n  записей через специальные методы. Информация сохраняется в целочисленном поле.\n\n\nСравнение с трейтами <span id=\"comparison-with-traits\"></span>\n---------------------------------------------------\n\nНесмотря на то, что поведения схожи с [трейтами](https://www.php.net/manual/ru/language.oop5.traits.php) тем, что\n\"внедряют\" свои свойства и методы в основной класс, они имеют множество отличий. Они оба имеют свои плюсы и минусы,\nи, скорее, дополняют друг друга, а не заменяют.\n\n\n### Плюсы поведений <span id=\"pros-for-behaviors\"></span>\n\nПоведения, как и любые другие классы, поддерживают наследование. Трейты можно рассматривать как копипейст\nна уровне языка. Они наследование не поддерживают.\n\nПоведения могут быть прикреплены и отвязаны от компонента динамически, без необходимости модифицирования класса\nкомпонента. Для использования трейтов необходимо модифицировать класс.\n\nПоведения, в отличие от трейтов, можно настраивать.\n\nПоведения можно настраивать таким образом, чтобы они реагировали на события компонента.\n\nКонфликты имен свойств и методов поведений, прикрепленных к компоненту, разрешаются на основе порядка их подключения.\nКонфликты имен, вызванные различными трейтами, требуют ручного переименования конфликтующих свойств или методов.\n\n\n### Плюсы трейтов <span id=\"pros-for-traits\"></span>\n\nТрейты являются гораздо более производительными, чем поведения, которые, являясь объектами, требуют\nдополнительного времени и памяти.\n\nМногие IDE поддерживают работу с трейтами, так как они являются стандартными конструкциями языка.\n"
  },
  {
    "path": "docs/guide-ru/concept-components.md",
    "content": "Компоненты\n=========\n\nКомпоненты — это главные строительные блоки приложений основанных на Yii. Компоненты наследуются от класса\n[[yii\\base\\Component]] или его наследников. Три главные возможности, которые компоненты предоставляют для других классов:\n\n* [Свойства](concept-properties.md).\n* [События](concept-events.md).\n* [Поведения](concept-behaviors.md).\n\nКак по отдельности, так и вместе, эти возможности делают классы Yii более простыми в настройке и использовании.\nНапример, пользовательские компоненты, включающие в себя [[yii\\jui\\DatePicker|виджет выбора даты]], могут быть\nиспользованы в [представлении](structure-views.md) для генерации интерактивных элементов выбора даты:\n\n```php\nuse yii\\jui\\DatePicker;\n\necho DatePicker::widget([\n    'language' => 'ru',\n    'name'  => 'country',\n    'clientOptions' => [\n        'dateFormat' => 'yy-mm-dd',\n    ],\n]);\n```\n\nСвойства виджета легко доступны для записи потому, что его класс унаследован от класса [[yii\\base\\Component]].\n\nКомпоненты — очень мощный инструмент. Но в то же время они немного тяжелее обычных объектов, потому что на поддержку\n[событий](concept-events.md) и [поведений](concept-behaviors.md) тратится дополнительные память и процессорное время.\nЕсли ваши компоненты не нуждаются в этих двух возможностях, вам стоит унаследовать их от [[yii\\base\\BaseObject]],\nа не от [[yii\\base\\Component]]. Поступив так, вы сделаете ваши компоненты такими же эффективными, как и обычные PHP объекты,\nно с поддержкой [свойств](concept-properties.md).\n\nПри наследовании ваших классов от [[yii\\base\\Component]] или [[yii\\base\\BaseObject]], рекомендуется следовать некоторым\nсоглашениям:\n\n- Если вы переопределяете конструктор, то добавьте *последним* аргументом параметр `$config` и затем передайте его\n  в конструктор предка.\n- Всегда вызывайте конструктор предка *в конце* вашего переопределенного конструктора.\n- Если вы переопределяете метод [[yii\\base\\BaseObject::init()]], убедитесь, что вы вызываете родительскую реализацию этого\n  метода *в начале* вашего метода `init()`.\n\nПример:\n\n```php\n<?php\n\nnamespace yii\\components;\n\nuse yii\\base\\BaseObject;\n\nclass MyClass extends BaseObject\n{\n    public $prop1;\n    public $prop2;\n\n    public function __construct($param1, $param2, $config = [])\n    {\n        // ... инициализация происходит перед тем, как будет применена конфигурация.\n\n        parent::__construct($config);\n    }\n\n    public function init()\n    {\n        parent::init();\n\n        // ... инициализация происходит после того, как была применена конфигурация.\n    }\n}\n```\n\nСледуя этому руководству вы позволите [настраивать](concept-configurations.md) ваш компонент при создании. Например:\n\n```php\n$component = new MyClass(1, 2, ['prop1' => 3, 'prop2' => 4]);\n// альтернативный способ\n$component = \\Yii::createObject([\n    'class' => MyClass::class,\n    'prop1' => 3,\n    'prop2' => 4,\n], [1, 2]);\n```\n\n> Info: Способ инициализации через вызов [[Yii::createObject()]] выглядит более сложным. Но в то же время он более\n  мощный из-за того, что он реализован на самом верху [контейнера внедрения зависимостей](concept-di-container.md).\n\nЖизненный цикл объектов класса [[yii\\base\\BaseObject]] содержит следующие этапы:\n\n1. Предварительная инициализация в конструкторе. Здесь вы можете установить значения свойств по умолчанию.\n2. Конфигурация объекта с помощью `$config`. Во время конфигурации могут быть перезаписаны значения свойств по умолчанию,\n   установленные в конструкторе.\n3. Конфигурация после инициализации в методе [[yii\\base\\BaseObject::init()|init()]]. Вы можете переопределить этот метод,\n   для проверки готовности объекта и нормализации свойств.\n4. Вызов методов объекта.\n\nПервые три шага всегда выполняются из конструктора объекта. Это значит, что если вы получите экземпляр объекта, он уже\nбудет проинициализирован и готов к работе.\n"
  },
  {
    "path": "docs/guide-ru/concept-configurations.md",
    "content": "Конфигурации\n============\n\nКонфигурации широко используются в Yii при создании новых объектов или при инициализации уже существующих объектов. \nОбычно конфигурации включают в себя названия классов создаваемых объектов и список первоначальных значений,\nкоторые должны быть присвоены [свойствам](concept-properties.md) объекта. Также в конфигурациях можно указать список\n[обработчиков событий](concept-events.md) объекта и/или список [поведений](concept-behaviors.md) объекта.\n\nПример конфигурации подключения к базе данных и дальнейшей инициализации подключения: \n\n```php\n$config = [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n];\n\n$db = Yii::createObject($config);\n```\n\nМетод [[Yii::createObject()]] принимает в качестве аргумента массив с конфигурацией и создаёт объект указанного в них класса.\nПри этом оставшаяся часть конфигурации используется для инициализации свойств, обработчиков событий и поведений объекта.\n\nЕсли объект уже создан, вы можете использовать [[Yii::configure()]] для того, чтобы инициализировать свойства объекта\nмассивом с конфигурацией:\n\n```php\nYii::configure($object, $config);\n```\n\nОбратите внимание, что в этом случае массив с конфигурацией не должен содержать ключ `class`.\n\n\n## Формат конфигурации <span id=\"configuration-format\"></span>\n\nФормат конфигурации выглядит следующим образом:\n\n```php\n[\n    'class' => 'ClassName',\n    'propertyName' => 'propertyValue',\n    'on eventName' => $eventHandler,\n    'as behaviorName' => $behaviorConfig,\n]\n```\n\nгде\n\n* Элемент `class` указывает абсолютное имя класса создаваемого объекта.\n* Элементы `propertyName` указывают первоначальные значения свойств создаваемого объекта. Ключи являются именами свойств\n  создаваемого объекта, а значения — начальными значениями свойств создаваемого объекта.\n  Таким способом могут быть установлены только публичные переменные объекта и его [свойства](concept-properties.md),\n  созданные через геттеры и сеттеры.\n* Элементы `on eventName` указывают на то, какие обработчики должны быть прикреплены к [событиям](concept-events.md) объекта.\n  Обратите внимание, что ключи массива начинаются с `on `. Чтобы узнать весь список поддерживаемых видов\n  обработчиков событий, обратитесь в раздел [события](concept-events.md)\n* Элементы `as behaviorName` указывают на то, какие [поведения](concept-behaviors.md) должны быть внедрены в объект.\n  Обратите внимание, что ключи массива начинаются с `as `; а `$behaviorConfig` представляет собой конфигурацию для\n  создания [поведения](concept-behaviors.md), похожую на все остальные конфигурации.\n\nПример конфигурации с установкой первоначальных значений свойств объекта, обработчика событий и поведения:\n\n```php\n[\n    'class' => 'app\\components\\SearchEngine',\n    'apiKey' => 'xxxxxxxx',\n    'on search' => function ($event) {\n        Yii::info(\"Keyword searched: \" . $event->keyword);\n    },\n    'as indexer' => [\n        'class' => 'app\\components\\IndexerBehavior',\n        // ... начальные значения свойств ...\n    ],\n]\n```\n\n\n## Использование конфигурации <span id=\"using-configurations\"></span>\n\nКонфигурации повсеместно используются в Yii. В самом начале данной главы мы узнали как\nсоздать объект с необходимыми параметрами, используя метод [[Yii::createObject()]].\nВ данном разделе речь пойдет о конфигурации приложения и конфигурациях виджетов — двух основных способов\nиспользования конфигурации. \n\n\n### Конфигурация приложения <span id=\"application-configurations\"></span>\n\nКонфигурация [приложения](structure-applications.md), пожалуй, самая сложная из используемых в фреймворке.\nПричина в том, что класс [[yii\\web\\Application|application]] содержит большое количество конфигурируемых\nсвойств и событий. Более того, свойство приложения [[yii\\web\\Application::components|components]]\nможет принимать массив с конфигурацией для создания компонентов, регистрируемых на уровне приложения.\nПример конфигурации приложения для [шаблона приложения basic](start-installation.md).\n\n```php\n$config = [\n    'id' => 'basic',\n    'basePath' => dirname(__DIR__),\n    'extensions' => require __DIR__ . '/../vendor/yiisoft/extensions.php',\n    'components' => [\n        'cache' => [\n            'class' => 'yii\\caching\\FileCache',\n        ],\n        'mailer' => [\n            'class' => 'yii\\swiftmailer\\Mailer',\n        ],\n        'log' => [\n            'class' => 'yii\\log\\Dispatcher',\n            'traceLevel' => YII_DEBUG ? 3 : 0,\n            'targets' => [\n                [\n                    'class' => 'yii\\log\\FileTarget',\n                ],\n            ],\n        ],\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=stay2',\n            'username' => 'root',\n            'password' => '',\n            'charset' => 'utf8',\n        ],\n    ],\n];\n```\n\nКлюч `class` в данной конфигурации не указывается. Причина в том, что класс вызывается по полному имени во\n[входном скрипте](structure-entry-scripts.md):\n\n```php\n(new yii\\web\\Application($config))->run();\n```\n\nЗа более подробной документацией о настройках свойства `components` в конфигурации приложения обратитесь к главам\n[приложения](structure-applications.md) и [Service Locator](concept-service-locator.md).\n\nНачиная с версии 2.0.11, можно настраивать [контейнер зависимостей](concept-di-container.md) через конфигурацию\nприложения. Для этого используется свойство `container`:\n\n```php\n$config = [\n    'id' => 'basic',\n    'basePath' => dirname(__DIR__),\n    'extensions' => require __DIR__ . '/../vendor/yiisoft/extensions.php',\n    'container' => [\n        'definitions' => [\n            'yii\\widgets\\LinkPager' => ['maxButtonCount' => 5]\n        ],\n        'singletons' => [\n            // Конфигурация для единожды создающихся объектов\n        ]\n    ]\n];\n```\n\nЧтобы узнать о возможных значениях `definitions` и `singletons`, а также о реальных примерах использования,\nпрочитайте подраздел [Более сложное практическое применение](concept-di-container.md#advanced-practical-usage) раздела\n[Контейнер внедрения зависимостей](concept-di-container.md).\n\n\n### Конфигурации виджетов <span id=\"widget-configurations\"></span>\n\nПри использовании [виджетов](structure-widgets.md) часто возникает необходимость изменить параметры виджета с помощью\nконфигурации. Для создания виджета можно использовать два метода: [[yii\\base\\Widget::widget()]] и \n[[yii\\base\\Widget::begin()]]. Оба метода принимают конфигурацию в виде PHP-массива:\n\n```php\nuse yii\\widgets\\Menu;\n\necho Menu::widget([\n    'activateItems' => false,\n    'items' => [\n        ['label' => 'Home', 'url' => ['site/index']],\n        ['label' => 'Products', 'url' => ['product/index']],\n        ['label' => 'Login', 'url' => ['site/login'], 'visible' => Yii::$app->user->isGuest],\n    ],\n]);\n```\n\nДанный код создает виджет `Menu` и устанавливает параметр виджета `activeItems` в значение `false`.\nТакже устанавливается параметр `items`, состоящий из элементов меню.\n\nОбратите внимание, что параметр `class` НЕ передается, так как полное имя уже указано.\n\n\n## Конфигурационные файлы <span id=\"configuration-files\"></span>\n\nЕсли конфигурация очень сложная, то её, как правило, разделяют по нескольким PHP-файлам. Такие файлы называют\n*конфигурационными файлами*. Конфигурационный файл возвращает массив PHP, являющийся конфигурацией.\nНапример, конфигурацию приложения можно хранить в отдельном файле `web.php`, как показано ниже:\n\n```php\nreturn [\n    'id' => 'basic',\n    'basePath' => dirname(__DIR__),\n    'extensions' => require __DIR__ . '/../vendor/yiisoft/extensions.php',\n    'components' => require __DIR__ . '/components.php',\n];\n```\n\nПараметр `components` также имеет сложную конфигурацию, поэтому можно его хранить в файле `components.php`\nи подключать в файл `web.php` используя `require` как и показано выше. \nСодержимое файла `components.php`:\n\n```php\nreturn [\n    'cache' => [\n        'class' => 'yii\\caching\\FileCache',\n    ],\n    'mailer' => [\n        'class' => 'yii\\swiftmailer\\Mailer',\n    ],\n    'log' => [\n        'class' => 'yii\\log\\Dispatcher',\n        'traceLevel' => YII_DEBUG ? 3 : 0,\n        'targets' => [\n            [\n                'class' => 'yii\\log\\FileTarget',\n            ],\n        ],\n    ],\n    'db' => [\n        'class' => 'yii\\db\\Connection',\n        'dsn' => 'mysql:host=localhost;dbname=stay2',\n        'username' => 'root',\n        'password' => '',\n        'charset' => 'utf8',\n    ],\n];\n```\n\nЧтобы получить конфигурацию, хранящуюся в файле, достаточно подключить файл с помощью `require`:\n\n```php\n$config = require 'path/to/web.php';\n(new yii\\web\\Application($config))->run();\n```\n\n\n## Значения конфигурации по умолчанию <span id=\"default-configurations\"></span>\n\nМетод [[Yii::createObject()]] реализован с использованием [контейнера внедрения зависимостей](concept-di-container.md).\nЭто позволяет задавать так называемые *значения конфигурации по умолчанию*, которые будут применены ко ВСЕМ экземплярам классов во время их инициализации методом [[Yii::createObject()]]. Значения конфигурации по умолчанию указываются с помощью метода `Yii::$container->set()` на этапе [предварительной загрузки](runtime-bootstrapping.md).\n\nНапример, если мы хотим изменить виджет [[yii\\widgets\\LinkPager]] так, чтобы все виджеты данного вида показывали максимум\n5 кнопок на странице вместо 10 (как это установлено изначально), можно использовать следующий код:\n\n```php\n\\Yii::$container->set('yii\\widgets\\LinkPager', [\n    'maxButtonCount' => 5,\n]);\n```\n\nБез использования значений конфигурации по умолчанию, при использовании LinkPager, вам пришлось бы каждый раз\nзадавать значение `maxButtonCount`.\n\n\n## Константы окружения <span id=\"environment-constants\"></span>\n\nКонфигурации могут различаться в зависимости от режима, в котором происходит запуск приложения. Например,\nв окружении разработчика (development) вы используете базу данных `mydb_dev`, а в эксплуатационном (production) окружении\nбазу данных `mydb_prod`. Для упрощения смены окружений в Yii существует константа `YII_ENV`.  Вы можете указать её во \n[входном скрипте](structure-entry-scripts.md) своего приложения:\n\n```php\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n```\n\n`YII_ENV` может принимать следующие значения:\n\n- `prod`: окружение production, т.е. эксплуатационный режим сервера. Константа `YII_ENV_PROD` установлена в `true`.\n   Значение по умолчанию.\n- `dev`: окружение development, т.е. режим для разработки. Константа `YII_ENV_DEV` установлена в `true`.\n- `test`: окружение testing, т.е. режим для тестирования. Константа `YII_ENV_TEST` установлена в `true`.\n\nИспользуя эти константы, вы можете задать в конфигурации значения параметров зависящие от текущего окружения.\nНапример, чтобы включить [отладочную панель и отладчик](tool-debugger.md) в режиме разработки, вы можете использовать\nследующий код в конфигурации приложения: \n\n```php\n$config = [...];\n\nif (YII_ENV_DEV) {\n    // значения параметров конфигурации для окружения разработки 'dev'\n    $config['bootstrap'][] = 'debug';\n    $config['modules']['debug'] = 'yii\\debug\\Module';\n}\n\nreturn $config;\n```\n"
  },
  {
    "path": "docs/guide-ru/concept-di-container.md",
    "content": "Контейнер внедрения зависимостей\n==============================\n\nКонтейнер внедрения зависимостей — это объект, который знает, как создать и настроить экземпляр класса и зависимых от него объектов.\n[Статья Мартина Фаулера](https://martinfowler.com/articles/injection.html) хорошо объясняет, почему контейнер внедрения зависимостей является полезным. Здесь, преимущественно, будет объясняться использование контейнера внедрения зависимостей, предоставляемого в Yii.\n\n\nВнедрение зависимостей <span id=\"dependency-injection\"></span>\n--------------------\n\nYii обеспечивает функционал контейнера внедрения зависимостей через класс [[yii\\di\\Container]]. Он поддерживает следующие виды внедрения зависимостей:\n\n* Внедрение зависимости через конструктор;\n* Внедрение зависимости через метод;\n* Внедрение зависимости через сеттер и свойство;\n* Внедрение зависимости через PHP callback;\n\n\n### Внедрение зависимости через конструктор <span id=\"constructor-injection\"></span>\n\nКонтейнер внедрения зависимостей поддерживает внедрение зависимости через конструктор при помощи указания типов для параметров конструктора.\nУказанные типы сообщают контейнеру, какие классы или интерфейсы зависят от него при создании нового объекта.\nКонтейнер попытается получить экземпляры зависимых классов или интерфейсов, а затем передать их в новый объект через конструктор. Например,\n\n```php\nclass Foo\n{\n    public function __construct(Bar $bar)\n    {\n    }\n}\n\n$foo = $container->get('Foo');\n// что равносильно следующему:\n$bar = new Bar;\n$foo = new Foo($bar);\n```\n\n### Внедрение зависимости через метод <span id=\"method-injection\"></span>\n\nОбычно зависимости класса передаются в конструктор и становятся доступными внутри класса в течение всего времени\nего существования. При помощи инъекции через метод возможно задать зависимость, которая необходима в единственном\nметоде класса. Передавать такую зависимость через конструктор либо невозможно, либо это влечёт за собой ненужные\nнакладные расходы в большинстве случаев.\n\nМетод класса может быть определён так же, как `doSomething()` в примере ниже:\n\n```php\nclass MyClass extends \\yii\\base\\Component\n{\n    public function __construct(/*Легковесные зависимости тут*/, $config = [])\n    {\n        // ...\n    }\n\n    public function doSomething($param1, \\my\\heavy\\Dependency $something)\n    {\n        // Работаем с $something\n    }\n}\n```\n\nМетод можно вызвать либо передав экземпляр `\\my\\heavy\\Dependency` самостоятельно, либо использовав\n[[yii\\di\\Container::invoke()]]:\n\n```php\n$obj = new MyClass(/*...*/);\nYii::$container->invoke([$obj, 'doSomething'], ['param1' => 42]); // $something будет предоставлено DI-контейнером\n```\n\n### Внедрение зависимости через сеттер и свойство <span id=\"setter-and-property-injection\"></span>\n\nВнедрение зависимости через сеттер и свойство поддерживается через [конфигурации](concept-configurations.md).\nПри регистрации зависимости или при создании нового объекта, вы можете предоставить конфигурацию, которая\nбудет использована контейнером для внедрения зависимостей через соответствующие сеттеры или свойства.\nНапример,\n\n```php\nuse yii\\base\\BaseObject;\n\nclass Foo extends BaseObject\n{\n    public $bar;\n\n    private $_qux;\n\n    public function getQux()\n    {\n        return $this->_qux;\n    }\n\n    public function setQux(Qux $qux)\n    {\n        $this->_qux = $qux;\n    }\n}\n\n$container->get('Foo', [], [\n    'bar' => $container->get('Bar'),\n    'qux' => $container->get('Qux'),\n]);\n```\n\n> Info: Метод [[yii\\di\\Container::get()]] третьим аргументом принимает массив конфигурации, которым инициализируется создаваемый объект. Если класс реализует интерфейс [[yii\\base\\Configurable]] (например, [[yii\\base\\BaseObject]]), то массив конфигурации передается в последний параметр конструктора класса. Иначе конфигурация применяется уже *после* создания объекта.\n\n\nБолее сложное практическое применение <span id=\"advanced-practical-usage\"></span>\n---------------\n\nДопустим, мы работаем над API и у нас есть:\n\n- `app\\components\\Request`, наследуемый от `yii\\web\\Request` и реализующий дополнительные возможности.\n- `app\\components\\Response`, наследуемый от `yii\\web\\Response` со свойством `format`, по умолчанию инициализируемом как `json`.\n- `app\\storage\\FileStorage` и `app\\storage\\DocumentsReader`, где реализована некая логика для работы с документами в\n  неком файловом хранилище:\n  \n  ```php\n  class FileStorage\n  {\n      public function __construct($root) {\n          // делаем что-то\n      }\n  }\n  \n  class DocumentsReader\n  {\n      public function __construct(FileStorage $fs) {\n          // делаем что-то\n      }\n  }\n  ```\n\nВозможно настроить несколько компонентов сразу передав массив конфигурации в метод \n[[yii\\di\\Container::setDefinitions()|setDefinitions()]] или [[yii\\di\\Container::setSingletons()|setSingletons()]].\nВнутри метода фреймворк обойдёт массив конфигурации и вызовет для каждого элемента [[yii\\di\\Container::set()|set()]] или\n[[yii\\di\\Container::setSingleton()|setSingleton()]] соответственно.\n\nФормат массива конфигурации следующий:\n\n - Ключ: имя класса, интерфейса или псевдонима. Ключ передаётся в первый аргумент `$class` метода\n [[yii\\di\\Container::set()|set()]].\n - Значение: конфигурация для класса. Возможные значения описаны в документации параметра `$definition` метода\n [[yii\\di\\Container::set()|set()]]. Значение передаётся в аргумент `$definition` метода [[set()]].\n\nДля примера, давайте настроим наш контейнер:\n\n```php\n$container->setDefinitions([\n    'yii\\web\\Request' => 'app\\components\\Request',\n    'yii\\web\\Response' => [\n        'class' => 'app\\components\\Response',\n        'format' => 'json'\n    ],\n    'app\\storage\\DocumentsReader' => function ($container, $params, $config) {\n        $fs = new app\\storage\\FileStorage('/var/tempfiles');\n        return new app\\storage\\DocumentsReader($fs);\n    }\n]);\n\n$reader = $container->get('app\\storage\\DocumentsReader');\n// Создаст объект DocumentReader со всеми зависимостями \n```\n\n> Tip: Начиная с версии 2.0.11 контейнер может быть настроен в декларативном стиле через конфигурацию приложения. \nКак это сделать ищите в подразделе [Конфигурация приложения](concept-configurations.md#application-configurations)\nраздела [Конфигурации](concept-configurations.md).\n\nВроде всё работает, но если нам необходимо создать экземпляр класса `DocumentWriter`, придётся скопировать код, \nсоздающий экземпляр`FileStorage`, что, очевидно, не является оптимальным.\n\nКак описано в подразделе [Разрешение зависимостей](#resolving-dependencies), [[yii\\di\\Container::set()|set()]]\nи [[yii\\di\\Container::setSingleton()|setSingleton()]] могут опционально принимать третьим аргументов параметры\nдля конструктора. Формат таков:\n\n - Ключ: имя класса, интерфейса или псевдонима. Ключ передаётся в первый аргумент `$class` метода [[yii\\di\\Container::set()|set()]].\n - Значение: массив из двух элементов. Первый элемент передаётся в метод [[yii\\di\\Container::set()|set()]] вторым\n  аргументом `$definition`, второй элемент — аргументом `$params`.\n\nИсправим наш пример:\n\n```php\n$container->setDefinitions([\n    'tempFileStorage' => [ // для удобства мы задали псевдоним\n        ['class' => 'app\\storage\\FileStorage'],\n        ['/var/tempfiles']\n    ],\n    'app\\storage\\DocumentsReader' => [\n        ['class' => 'app\\storage\\DocumentsReader'],\n        [Instance::of('tempFileStorage')]\n    ],\n    'app\\storage\\DocumentsWriter' => [\n        ['class' => 'app\\storage\\DocumentsWriter'],\n        [Instance::of('tempFileStorage')]\n    ]\n]);\n\n$reader = $container->get('app\\storage\\DocumentsReader'); \n// Код будет работать ровно так же, как и в предыдущем примере.\n```\n\nВы могли заметить вызов `Instance::of('tempFileStorage')`. Он означает, что [[yii\\di\\Container|Container]]\nнеявно предоставит зависимость, зарегистрированную с именем `tempFileStorage` и передаст её первым аргументом \nв конструктор `app\\storage\\DocumentsWriter`.\n\n> Note: Методы [[yii\\di\\Container::setDefinitions()|setDefinitions()]] и [[yii\\di\\Container::setSingletons()|setSingletons()]]\n  доступны с версии 2.0.11.\n  \nЕщё один шаг по оптимизации конфигурации — регистрировать некоторые зависимости как синглтоны. Зависимость, регистрируемая \nчерез метод [[yii\\di\\Container::set()|set()]], будет создаваться каждый раз при обращении к ней. Некоторые классы не меняют\nсвоего состояния на протяжении всей работы приложения, поэтому могут быть зарегистрированы как синглтоны. Это увеличит\nпроизводительность приложения. \n\nХорошим примером может быть класс `app\\storage\\FileStorage`, который выполняет некие операции над файловой системой\nчерез простой API: `$fs->read()`, `$fs->write()`. Обе операции не меняют внутреннее состояние класса, поэтому мы можем\nсоздать класс один раз и далее использовать его.\n\n```php\n$container->setSingletons([\n    'tempFileStorage' => [\n        ['class' => 'app\\storage\\FileStorage'],\n        ['/var/tempfiles']\n    ],\n]);\n\n$container->setDefinitions([\n    'app\\storage\\DocumentsReader' => [\n        ['class' => 'app\\storage\\DocumentsReader'],\n        [Instance::of('tempFileStorage')]\n    ],\n    'app\\storage\\DocumentsWriter' => [\n        ['class' => 'app\\storage\\DocumentsWriter'],\n        [Instance::of('tempFileStorage')]\n    ]\n]);\n\n$reader = $container->get('app\\storage\\DocumentsReader');\n```\n\n### Внедрение зависимости через PHP callback <span id=\"php-callable-injection\"></span>\n\nВ данном случае, контейнер будет использовать зарегистрированный PHP callback для создания новых экземпляров класса.\nКаждый раз при вызове [[yii\\di\\Container::get()]] вызывается соответствующий callback.\nCallback отвечает за разрешения зависимостей и внедряет их в соответствии с вновь создаваемыми объектами. Например,\n\n```php\n$container->set('Foo', function ($container, $params, $config) {\n    $foo = new Foo(new Bar);\n    // ... дополнительная инициализация\n    return $foo;\n});\n\n$foo = $container->get('Foo');\n```\n\nДля того, чтобы скрыть сложную логику инициализации нового объекта, можно использовать статический метод, возвращающий\ncallable:\n\n```php\nclass FooBuilder\n{\n    public static function build($container, $params, $config)\n    {\n        $foo = new Foo(new Bar);\n        // ... дополнительная инициализация ...\n        return $foo;\n    }\n}\n\n$container->set('Foo', ['app\\helper\\FooBuilder', 'build']);\n\n$foo = $container->get('Foo');\n```\n\nТеперь тот, кто будет настраивать класс `Foo`, не обязан знать, как этот класс устроен.\n\n\nРегистрация зависимостей <span id=\"registering-dependencies\"></span>\n------------------------\n\nВы можете использовать [[yii\\di\\Container::set()]] для регистрации зависимостей. При регистрации требуется имя зависимости, а также определение зависимости. Именем зависимости может быть имя класса, интерфейса или алиас, \nтак же определением зависимости может быть имя класса, конфигурационным массивом, или PHP callback'ом.\n\n```php\n$container = new \\yii\\di\\Container;\n\n// регистрация имени класса, как есть. Это может быть пропущено.\n$container->set('yii\\db\\Connection');\n\n// регистрация интерфейса\n// Когда класс зависит от интерфейса, соответствующий класс\n// будет использован в качестве зависимости объекта\n$container->set('yii\\mail\\MailInterface', 'yii\\swiftmailer\\Mailer');\n\n// регистрация алиаса. Вы можете использовать $container->get('foo')\n// для создания экземпляра Connection\n$container->set('foo', 'yii\\db\\Connection');\n\n// регистрация класса с конфигурацией. Конфигурация\n// будет применена при создании экземпляра класса через get()\n$container->set('yii\\db\\Connection', [\n    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n]);\n\n// регистрация алиаса с конфигурацией класса\n// В данном случае, параметр \"class\" требуется для указания класса\n$container->set('db', [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n]);\n\n// регистрация PHP callback'a\n// Callback будет выполняться каждый раз при вызове $container->get('db')\n$container->set('db', function ($container, $params, $config) {\n    return new \\yii\\db\\Connection($config);\n});\n\n// регистрация экземпляра компонента\n// $container->get('pageCache') вернёт тот же экземпляр при каждом вызове\n$container->set('pageCache', new FileCache);\n```\n\n> Tip: Если имя зависимости такое же, как и определение соответствующей зависимости, то её повторная регистрация в контейнере внедрения зависимостей не нужна.\n\nЗависимость, зарегистрированная через `set()` создаёт экземпляр каждый раз, когда зависимость необходима.\nВы можете использовать [[yii\\di\\Container::setSingleton()]] для регистрации зависимости, которая создаст только один экземпляр:\n\n```php\n$container->setSingleton('yii\\db\\Connection', [\n    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n]);\n```\n\n\nРазрешение зависимостей <span id=\"resolving-dependencies\"></span>\n----------------------\nПосле регистрации зависимостей, вы можете использовать контейнер внедрения зависимостей для создания новых объектов,\nи контейнер автоматически разрешит зависимости их экземпляра и их внедрений во вновь создаваемых объектах. Разрешение\nзависимостей рекурсивно, то есть если зависимость имеет другие зависимости, эти зависимости также будут автоматически\nразрешены.\n\nВы можете использовать [[yii\\di\\Container::get()]] для создания или получения объектов. Метод принимает имя зависимости,\nкоторым может быть имя класса, имя интерфейса или псевдоним. Имя зависимости может быть зарегистрировано через\n`set()` или `setSingleton()`. Вы можете опционально предоставить список параметров конструктора класса и\n[конфигурацию](concept-configurations.md) для настройки созданного объекта.\n\nНапример:\n\n```php\n// \"db\" ранее зарегистрированный псевдоним\n$db = $container->get('db');\n\n// эквивалентно: $engine = new \\app\\components\\SearchEngine($apiKey, $apiSecret, ['type' => 1]);\n$engine = $container->get('app\\components\\SearchEngine', [$apiKey, $apiSecret], ['type' => 1]);\n\n// эквивалентно: $api = new \\app\\components\\Api($host, $apiKey);\n$api = $container->get('app\\components\\Api', ['host' => $host, 'apiKey' => $apiKey]);\n```\n\nЗа кулисами, контейнер внедрения зависимостей делает гораздо больше работы, чем просто создание нового объекта.\nПрежде всего, контейнер, осмотрит конструктор класса, чтобы узнать имя зависимого класса или интерфейса, а затем\nавтоматически разрешит эти зависимости рекурсивно.\n\nСледующий код демонстрирует более сложный пример. Класс `UserLister` зависит от объекта, реализующего интерфейс\n`UserFinderInterface`; класс `UserFinder` реализует этот интерфейс и зависит от объекта `Connection`. Все эти зависимости\nбыли объявлены через тип подсказки параметров конструктора класса. При регистрации зависимости через свойство, контейнер\nвнедрения зависимостей позволяет автоматически разрешить эти зависимости и создаёт новый экземпляр `UserLister` простым\nвызовом `get('userLister')`.\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\BaseObject;\nuse yii\\db\\Connection;\nuse yii\\di\\Container;\n\ninterface UserFinderInterface\n{\n    function findUser();\n}\n\nclass UserFinder extends BaseObject implements UserFinderInterface\n{\n    public $db;\n\n    public function __construct(Connection $db, $config = [])\n    {\n        $this->db = $db;\n        parent::__construct($config);\n    }\n\n    public function findUser()\n    {\n    }\n}\n\nclass UserLister extends BaseObject\n{\n    public $finder;\n\n    public function __construct(UserFinderInterface $finder, $config = [])\n    {\n        $this->finder = $finder;\n        parent::__construct($config);\n    }\n}\n\n$container = new Container;\n$container->set('yii\\db\\Connection', [\n    'dsn' => '...',\n]);\n$container->set('app\\models\\UserFinderInterface', [\n    'class' => 'app\\models\\UserFinder',\n]);\n$container->set('userLister', 'app\\models\\UserLister');\n\n$lister = $container->get('userLister');\n\n// что эквивалентно:\n\n$db = new \\yii\\db\\Connection(['dsn' => '...']);\n$finder = new UserFinder($db);\n$lister = new UserLister($finder);\n```\n\n\nПрактическое применение <span id=\"practical-usage\"></span>\n---------------\n\nYii создаёт контейнер внедрения зависимостей когда вы подключаете файл `Yii.php` во [входном скрипте](structure-entry-scripts.md) \nвашего приложения. Контейнер внедрения зависимостей доступен через [[Yii::$container]]. При вызове [[Yii::createObject()]],\nметод на самом деле вызовет метод контейнера [[yii\\di\\Container::get()|get()]], чтобы создать новый объект.\nКак упомянуто выше, контейнер внедрения зависимостей автоматически разрешит зависимости (если таковые имеются) и внедрит их\nполучаемый объект. Поскольку Yii использует [[Yii::createObject()]] в большей части кода своего ядра для создания новых\nобъектов, это означает, что вы можете настроить глобальные объекты, имея дело с [[Yii::$container]].\n\nНапример, давайте настроим количество кнопок в пейджере [[yii\\widgets\\LinkPager]] по умолчанию глобально:\n\n```php\n\\Yii::$container->set('yii\\widgets\\LinkPager', ['maxButtonCount' => 5]);\n```\n\nТеперь, если вы вызовете в представлении виджет, используя следующий код, то свойство `maxButtonCount` будет инициализировано как 5 вместо значения по умолчанию 10, как это определено в классе.\n\n```php\necho \\yii\\widgets\\LinkPager::widget();\n```\n\nХотя вы всё ещё можете переопределить установленное значение через контейнер внедрения зависимостей:\n\n```php\necho \\yii\\widgets\\LinkPager::widget(['maxButtonCount' => 20]);\n```\nДругим примером является использование автоматического внедрения зависимости через конструктор контейнера внедрения зависимостей.\nПредположим, ваш класс контроллера зависит от ряда других объектов, таких как сервис бронирования гостиницы. Вы\nможете объявить зависимость через параметр конструктора и позволить контейнеру внедрения зависимостей, разрешить её за вас.\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\nuse app\\components\\BookingInterface;\n\nclass HotelController extends Controller\n{\n    protected $bookingService;\n\n    public function __construct($id, $module, BookingInterface $bookingService, $config = [])\n    {\n        $this->bookingService = $bookingService;\n        parent::__construct($id, $module, $config);\n    }\n}\n```\n\nЕсли у вас есть доступ к этому контроллеру из браузера, вы увидите сообщение об ошибке, который жалуется на то, что `BookingInterface`\nне может быть создан. Это потому что вы должны указать контейнеру внедрения зависимостей, как обращаться с этой зависимостью:\n\n```php\n\\Yii::$container->set('app\\components\\BookingInterface', 'app\\components\\BookingService');\n```\n\nТеперь, если вы попытаетесь получить доступ к контроллеру снова, то экземпляр `app\\components\\BookingService` будет создан и введён в качестве 3-го параметра конструктора контроллера.\n\n\nКогда следует регистрировать зависимости <span id=\"when-to-register-dependencies\"></span>\n-----------------------------\n\nПоскольку зависимости необходимы тогда, когда создаются новые объекты, то их регистрация должна быть сделана\nкак можно раньше. Ниже приведены рекомендуемые практики:\n\n* Если вы разработчик приложения, то вы можете зарегистрировать зависимости в конфигурации вашего приложения.\n  Как это сделать описано в подразделе [Конфигурация приложения](concept-configurations.md#application-configurations) \n  раздела [Конфигурации](concept-configurations.md).\n* Если вы разработчик распространяемого [расширения](structure-extensions.md), то вы можете зарегистрировать зависимости\n  в загрузочном классе расширения.\n\n\nИтог <span id=\"summary\"></span>\n-------\nКак dependency injection, так и [service locator](concept-service-locator.md) являются популярными паттернами проектирования, которые позволяют \nсоздавать программное обеспечение в слабосвязанной и более тестируемой манере.\nМы настоятельно рекомендуем к прочтению\n[статью Мартина Фаулера](https://martinfowler.com/articles/injection.html), для более глубокого понимания dependency injection и service locator. \n\nYii реализует свой [service locator](concept-service-locator.md) поверх контейнера внедрения зависимостей.\nКогда service locator пытается создать новый экземпляр объекта, он перенаправляет вызов на контейнер внедрения зависимостей.\nПоследний будет разрешать зависимости автоматически, как описано выше.\n\n"
  },
  {
    "path": "docs/guide-ru/concept-events.md",
    "content": "События\n======\n\nСобытия - это механизм, внедряющий элементы собственного кода в существующий код в определенные моменты его исполнения. К событию можно присоединить собственный код, который будет выполняться автоматически при срабатывании события. Например, объект, отвечающий за почту, может инициировать событие `messageSent` при успешной отправке сообщения. При этом если нужно отслеживать успешно отправленные сообщения, достаточно присоединить соответствующий код к событию `messageSent`.\n\nДля работы с событиями Yii использует базовый класс [[yii\\base\\Component]]. Если класс должен инициировать события, его нужно унаследовать от [[yii\\base\\Component]] или потомка этого класса.\n\n\nОбработчики событий <span id=\"event-handlers\"></span>\n--------------\n\nОбработчик события - это [callback-функция PHP](https://www.php.net/manual/ru/language.types.callable.php), которая выполняется при срабатывании события, к которому она присоединена. Можно использовать следующие callback-функции:\n\n- глобальную функцию PHP, указав строку с именем функции (без скобок), например, `'trim'`;\n- метод объекта, указав массив, содержащий строки с именами объекта и метода (без скобок), например, `[$object, 'methodName']`;\n- статический метод класса, указав массив, содержащий строки с именами класса и метода (без скобок), например, `['ClassName', 'methodName']`;\n- анонимную функцию, например, `function ($event) { ... }`.\n\nСигнатура обработчика события выглядит следующим образом:\n\n```php\nfunction ($event) {\n    // $event - это объект класса yii\\base\\Event или его потомка\n}\n```\n\nЧерез параметр `$event` обработчик события может получить следующую информацию о возникшем событии:\n\n- [[yii\\base\\Event::name|event name]]\n- [[yii\\base\\Event::sender|event sender]]: объект, метод `trigger()` которого был вызван\n- [[yii\\base\\Event::data|custom data]]: данные, которые были предоставлены во время присоединения обработчика события (будет описано ниже)\n\n\nПрисоединение обработчиков событий <span id=\"attaching-event-handlers\"></span>\n------------------------\n\nОбработчики события присоединяются с помощью метода [[yii\\base\\Component::on()]]. Например:\n\n```php\n$foo = new Foo;\n\n// обработчик - глобальная функция\n$foo->on(Foo::EVENT_HELLO, 'function_name');\n\n// обработчик - метод объекта\n$foo->on(Foo::EVENT_HELLO, [$object, 'methodName']);\n\n// обработчик - статический метод класса\n$foo->on(Foo::EVENT_HELLO, ['app\\components\\Bar', 'methodName']);\n\n// обработчик - анонимная функция\n$foo->on(Foo::EVENT_HELLO, function ($event) {\n    // логика обработки события\n});\n```\n\nТакже обработчики событий можно присоединять с помощью [конфигураций](concept-configurations.md). Дополнительную информацию см. в разделе [Конфигурации](concept-configurations.md#configuration-format).\n\n\nПрисоединяя обработчик события, можно передать дополнительные данные с помощью третьего параметра метода [[yii\\base\\Component::on()]]. Эти данные будут доступны в обработчике, когда сработает событие и он будет вызван. Например:\n\n```php\n// Следующий код выводит \"abc\" при срабатывании события\n// так как в $event->data содержатся данные, которые переданы в качестве третьего аргумента метода \"on\"\n$foo->on(Foo::EVENT_HELLO, 'function_name', 'abc');\n\nfunction function_name($event) {\n    echo $event->data;\n}\n```\n\nПорядок обработки событий\n-------------------\n\nК одному событию можно присоединить несколько обработчиков. При срабатывании события обработчики будут вызываться в том порядке, в котором они присоединялись к событию. Чтобы запретить в обработчике вызов всех следующих за ним обработчиков, необходимо установить свойство [[yii\\base\\Event::handled]] параметра `$event` в `true`:\n\n```php\n$foo->on(Foo::EVENT_HELLO, function ($event) {\n    $event->handled = true;\n});\n```\n\nПо умолчанию, новые обработчики присоединяются к концу очереди обработчиков, уже существующей у события.\nВ результате при срабатывании события обработчик выполнится последним.\nЧтобы обработчик присоединился к началу очереди и запускался первым, при вызове [[yii\\base\\Component::on()]] в качестве четвертого параметра `$append` следует передать `false`:\n\n```php\n$foo->on(Foo::EVENT_HELLO, function ($event) {\n    // ...\n}, $data, false);\n```\n\nИнициирование событий <span id=\"triggering-events\"></span>\n-----------------\n\nСобытия инициируются при вызове метода [[yii\\base\\Component::trigger()]]. Методу нужно передать *имя события*, а при необходимости - объект события, в котором описываются параметры, передаваемые обработчикам событий. Например:\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Component;\nuse yii\\base\\Event;\n\nclass Foo extends Component\n{\n    const EVENT_HELLO = 'hello';\n\n    public function bar()\n    {\n        $this->trigger(self::EVENT_HELLO);\n    }\n}\n```\n\nПоказанный выше код инициирует событие `hello` при каждом вызове метода `bar()`.\n\n> Tip: Желательно для обозначения имен событий использовать константы класса. В предыдущем примере константа `EVENT_HELLO` обозначает событие `hello`. У такого подхода три преимущества. Во-первых, исключаются опечатки. Во-вторых, для событий работает автозавершение в различных средах разработки. В-третьих, чтобы узнать, какие события поддерживаются классом, достаточно проверить константы, объявленные в нем.\n\nИногда при инициировании события может понадобиться передать его обработчику дополнительную информацию. Например, объекту, отвечающему за почту, может понадобиться передать обработчику события `messageSent` определенные данные, раскрывающие смысл отправленных почтовых сообщений. Для этого в качестве второго параметра методу [[yii\\base\\Component::trigger()]] передается объект события. Объект события должен быть экземпляром класса [[yii\\base\\Event]] или его потомка. Например:\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Component;\nuse yii\\base\\Event;\n\nclass MessageEvent extends Event\n{\n    public $message;\n}\n\nclass Mailer extends Component\n{\n    const EVENT_MESSAGE_SENT = 'messageSent';\n\n    public function send($message)\n    {\n        // ...отправка $message...\n\n        $event = new MessageEvent;\n        $event->message = $message;\n        $this->trigger(self::EVENT_MESSAGE_SENT, $event);\n    }\n}\n```\n\nПри вызове метода [[yii\\base\\Component::trigger()]] будут вызваны все обработчики, присоединенные к указанному событию.\n\n\nОтсоединение обработчиков событий <span id=\"detaching-event-handlers\"></span>\n------------------------\n\nДля отсоединения обработчика от события используется метод [[yii\\base\\Component::off()]]. Например:\n\n```php\n// обработчик - глобальная функция\n$foo->off(Foo::EVENT_HELLO, 'function_name');\n\n// обработчик - метод объекта\n$foo->off(Foo::EVENT_HELLO, [$object, 'methodName']);\n\n// обработчик - статический метод класса\n$foo->off(Foo::EVENT_HELLO, ['app\\components\\Bar', 'methodName']);\n\n// обработчик - анонимная функция\n$foo->off(Foo::EVENT_HELLO, $anonymousFunction);\n```\n\nУчтите, что в общем случае отсоединять обработчики - анонимные функции можно только если они где-то сохраняются в момент присоединения к событию. В предыдущем примере предполагается, что анонимная функция сохранена в переменной `$anonymousFunction`.\n\nЧтобы отсоединить ВСЕ обработчики от события, достаточно вызвать [[yii\\base\\Component::off()]] без второго параметра:\n\n```php\n$foo->off(Foo::EVENT_HELLO);\n```\n\n\nОбработчики событий на уровне класса <span id=\"class-level-event-handlers\"></span>\n--------------------------\n\nВо всех предыдущих примерах мы рассматривали присоединение событий *на уровне экземпляров*. Есть случаи, когда необходимо обрабатывать события, которые инициируются *любым* экземпляром класса, а не только конкретным экземпляром. В таком случае присоединять обработчик события к каждому экземпляру класса не нужно. Достаточно присоединить обработчик *на уровне класса*, вызвав статический метод [[yii\\base\\Event::on()]].\n\nНапример, объект [Active Record](db-active-record.md) инициирует событие [[yii\\db\\BaseActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]] после добавления в базу данных новой записи. Чтобы отслеживать записи, добавленные в базу данных *каждым* объектом [Active Record](db-active-record.md), можно использовать следующий код:\n\n```php\nuse Yii;\nuse yii\\base\\Event;\nuse yii\\db\\ActiveRecord;\n\nEvent::on(ActiveRecord::class, ActiveRecord::EVENT_AFTER_INSERT, function ($event) {\n    Yii::debug(get_class($event->sender) . ' добавлен');\n});\n```\n\nОбработчик будет вызван при срабатывании события [[yii\\db\\BaseActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]] в экземплярах класса [[yii\\db\\ActiveRecord|ActiveRecord]] или его потомков. В обработчике можно получить доступ к объекту, который инициировал событие, с помощью свойства `$event->sender`.\n\nПри срабатывании события будут в первую очередь вызваны обработчики на уровне экземпляра, а затем - обработчики на уровне класса.\n\nИнициировать событие *на уровне класса* можно с помощью статического метода [[yii\\base\\Event::trigger()]]. Событие на уровне класса не связано ни с одним конкретным объектом. В таком случае будут вызваны только обработчики события на уровне класса. Например:\n\n```php\nuse yii\\base\\Event;\n\nEvent::on(Foo::class, Foo::EVENT_HELLO, function ($event) {\n    var_dump($event->sender);  // выводит \"null\"\n});\n\nEvent::trigger(Foo::class, Foo::EVENT_HELLO);\n```\n\nОбратите внимание, что в данном случае `$event->sender` имеет значение `null` вместо экзепляра класса, который инициировал событие.\n\n> Note: Поскольку обработчики на уровне класса отвечают на события, инициируемые всеми экземплярами этого класса и всех его потомков, их следует использовать с осторожностью, особенно в случае базовых классов низкого уровня, таких как [[yii\\base\\BaseObject]].\n\nОтсоединить обработчик события на уровне класса можно с помощью метода [[yii\\base\\Event::off()]]. Например:\n\n```php\n// отсоединение $handler\nEvent::off(Foo::class, Foo::EVENT_HELLO, $handler);\n\n// отсоединяются все обработчики Foo::EVENT_HELLO\nEvent::off(Foo::class, Foo::EVENT_HELLO);\n```\n\nОбработчики событий на уровне интерфейсов <span id=\"interface-level-event-handlers\"></span>\n-------------\n\nСуществует еще более абстрактный способ обработки событий.\nВы можете создать отдельный интерфейс для общего события и реализовать его в классах, где это необходимо.\n\nНапример, создадим следующий интерфейс:\n\n```php\nnamespace app\\interfaces;\n\ninterface DanceEventInterface\n{\n    const EVENT_DANCE = 'dance';\n}\n```\n\nИ два класса, которые его реализуют:\n\n```php\nclass Dog extends Component implements DanceEventInterface\n{\n    public function meetBuddy()\n    {\n        echo \"Woof!\";\n        $this->trigger(DanceEventInterface::EVENT_DANCE);\n    }\n}\n\nclass Developer extends Component implements DanceEventInterface\n{\n    public function testsPassed()\n    {\n        echo \"Yay!\";\n        $this->trigger(DanceEventInterface::EVENT_DANCE);\n    }\n}\n```\n\nДля обработки события `EVENT_DANCE`, инициализированного любым из этих классов,\nвызовите [[yii\\base\\Event::on()|Event:on()]], передав ему в качестве первого параметра имя интерфейса.\n\n```php\nEvent::on('app\\interfaces\\DanceEventInterface', DanceEventInterface::EVENT_DANCE, function ($event) {\n    Yii::debug(get_class($event->sender) . ' just danced'); // Оставит запись в журнале о том, что кто-то танцевал\n});\n```\n\nВы можете также инициализировать эти события:\n\n```php\n// trigger event for Dog class\nEvent::trigger(Dog::class, DanceEventInterface::EVENT_DANCE);\n\n// trigger event for Developer class\nEvent::trigger(Developer::class, DanceEventInterface::EVENT_DANCE);\n```\n\nОднако невозможно инициализировать событие во всех классах, которые реализуют интерфейс:\n\n```php\n// НЕ БУДЕТ РАБОТАТЬ\nEvent::trigger('app\\interfaces\\DanceEventInterface', DanceEventInterface::EVENT_DANCE);\n```\n\nОтсоединить обработчик события можно с помощью метода [[yii\\base\\Event::off()|Event::off()]]. Например:\n\n```php\n// отсоединяет $handler\nEvent::off('app\\interfaces\\DanceEventInterface', DanceEventInterface::EVENT_DANCE, $handler);\n\n// отсоединяются все обработчики DanceEventInterface::EVENT_DANCE\nEvent::off('app\\interfaces\\DanceEventInterface', DanceEventInterface::EVENT_DANCE);\n```\n\nГлобальные события <span id=\"global-events\"></span>\n-------------\n\nYii поддерживает так называемые *глобальные события*, которые на самом деле основаны на нестандартном использовании описанного выше механизма событий. Для глобальных событий нужен глобально доступный объект-синглтон, например, экземпляр приложения - [application](structure-applications.md).\n\nЧтобы создать глобальное событие, отправитель сообщения вызывает метод `trigger()` синглтона, а не свой собственный метод `trigger()`. Аналогичным образом обработчики события также присоединяются к событиям синглтона. Например:\n\n```php\nuse Yii;\nuse yii\\base\\Event;\nuse app\\components\\Foo;\n\nYii::$app->on('bar', function ($event) {\n    echo get_class($event->sender);  // выводит \"app\\components\\Foo\"\n});\n\nYii::$app->trigger('bar', new Event(['sender' => new Foo]));\n```\n\nПреимущество глобальных событий в том, что им не нужен объект, к событию которого бы присоединялся обработчик и объект, с помощью которого бы это событие инициировалось. Вместо этого и для присоединения обработчика, и для инициирования события используется синглтон (например, экземпляр приложения).\n\nТем не менее, так как пространство имен глобальных событий едино для всего приложения, их имена нельзя назначать бездумно. Например, полезными могут быть искусственные пространства имен (\"frontend.mail.sent\", \"backend.mail.sent\").\n"
  },
  {
    "path": "docs/guide-ru/concept-properties.md",
    "content": "Свойства\n========\n\nВ PHP, переменные-члены класса называются *свойства*. Эти переменные являются частью объявления класса и используются для\nхранения состояния объектов этого класса (т.е. именно этим отличается один экземпляр класса от другого). На практике\nвам часто придётся производить чтение и запись свойств особым образом. Например, вам может понадобиться обрезать строку\nпри её записи в поле `label`. Для этого вы *можете* использовать следующий код:\n\n```php\n$object->label = trim($label);\n```\n\nНедостатком приведённого выше кода является то, что вам придется вызывать функцию `trim()` во всех местах, где вы\nприсваиваете значение полю `label`. Если в будущем понадобится производить еще какие-либо действия, например, преобразовать первую букву в верхний регистр, то вам придётся изменить каждый участок кода, где производится присваивание значения\nполю `label`. Повторение кода приводит к ошибкам и его необходимо избегать всеми силами.\n\nЧтобы решить эту проблему, в Yii был добавлен базовый класс [[yii\\base\\BaseObject]], который реализует работу со свойствами\nчерез *геттеры* и *сеттеры*. Если вашему классу нужна такая возможность, необходимо унаследовать его от\n[[yii\\base\\BaseObject]] или его потомка.\n\n> Info: Почти все внутренние классы Yii наследуются от [[yii\\base\\BaseObject]] или его потомков.\n  Это значит, что всякий раз, когда вы встречаете геттер или сеттер в классах фреймворка, вы можете обращаться к нему\n  как к свойству.\n\nГеттер — это метод, чьё название начинается со слова `get`. Имя сеттера начинается со слова  `set`. Часть названия после\n`get` или `set` определяет имя свойства. Например, геттер `getLabel()` и/или сеттер `setLabel()` определяют свойство\n`label`, как показано в коде ниже:\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\BaseObject;\n\nclass Foo extends BaseObject\n{\n    private $_label;\n\n    public function getLabel()\n    {\n        return $this->_label;\n    }\n\n    public function setLabel($value)\n    {\n        $this->_label = trim($value);\n    }\n}\n```\n\nВ коде выше геттер и сеттер реализуют свойство `label`, значение которого хранится в private свойстве `_label`.\n\nСвойства, определенные с помощью геттеров и сеттеров, можно использовать как обычные свойства класса. Главное отличие\nв том, что когда происходит чтение такого свойства, вызывается соответствующий геттер, при присвоении значения такому\nсвойству запускается соответствующий сеттер. Например:\n\n```php\n// Идентично вызову $label = $object->getLabel();\n$label = $object->label;\n\n// Идентично вызову $object->setLabel('abc');\n$object->label = 'abc';\n```\n\nСвойство, для которого объявлен только геттер без сеттера, может использоваться *только для чтения*. Попытка присвоить\nему значение вызовет [[yii\\base\\InvalidCallException|InvalidCallException]]. Точно так же, свойство для которого объявлен\nтолько сеттер без геттера может использоваться *только для записи*. Попытка получить его значение так же вызовет\nисключение. Свойства, предназначенные только для чтения, встречаются не часто.\n\nПри определении свойств класса при помощи геттеров и сеттеров нужно помнить о некоторых правилах и ограничениях:\n\n* Имена таких свойств *регистронезависимы*. Таким образом, `$object->label` и `$object->Label` — одно и то же.\n  Это обусловлено тем, что имена методов в PHP регистронезависимы.\n* Если имя такого свойства уже используется переменной-членом класса, то последнее будет иметь более высокий приоритет.\n  Например, если в классе `Foo` объявлено свойство `label`, то при вызове `$object->label = 'abc'` будет напрямую изменено\n  значение свойства `label`. А метод `setLabel()` не будет вызван.\n* Свойства, объявленные таким образом, не поддерживают модификаторы видимости. Это значит, что объявление геттера или\n  сеттера как public, protected или private никак не скажется на области видимости свойства.\n* Свойства могут быть объявлены только с помощью *не статичных* геттеров и/или сеттеров. Статичные методы не будут\n  обрабатываться подобным образом.\n* Обычный вызов `property_exists()` не работает для магических свойств. Для них необходимо использовать\n  [[yii\\base\\BaseObject::canGetProperty()|canGetProperty()]] или [[yii\\base\\BaseObject::canSetProperty()|canSetProperty()]].\n\nВозвращаясь к проблеме необходимости вызова функции `trim()` во всех местах, где присваивается значение свойству `label`,\nописанной в начале этого руководства, функцию `trim()` теперь необходимо вызывать только один раз — в методе `setLabel()`.\nПри возникновении нового требования о возведение первой буквы в верхний регистр, можно быстро поправить метод `setLabel()`\nне затрагивая остальной код. Эта правка будет распространяться на все присвоения значения свойству `label`.\n"
  },
  {
    "path": "docs/guide-ru/concept-service-locator.md",
    "content": "Service Locator\n=============\n\nService Locator является объектом, предоставляющим всевозможные сервисы (или компоненты), которые могут понадобиться\nприложению. В Service Locator каждый компонент представлен единственным экземпляром, имеющим уникальный ID.\nУникальный идентификатор (ID) может быть использован для получения компонента из Service Locator.\n\nВ Yii Service Locator является экземпляром класса [[yii\\di\\ServiceLocator]] или его дочернего класса.\n\nНаиболее часто используемый Service Locator в Yii — это объект *приложения*, который можно получить через `\\Yii::$app`.\nПредоставляемые им службы такие, как компоненты `request`, `response`, `urlManager`, называют *компонентами приложения*.\nБлагодаря Service Locator вы легко можете настроить эти компоненты или даже заменить их собственными реализациями.\n\nПомимо объекта приложения, объект каждого модуля также является Service Locator.\n\nПри использовании Service Locator первым шагом является регистрация компонентов. Компонент может быть зарегистрирован\nс помощью метода [[yii\\di\\ServiceLocator::set()]]. Следующий код демонстрирует различные способы регистрации компонентов:\n\n```php\nuse yii\\di\\ServiceLocator;\nuse yii\\caching\\FileCache;\n\n$locator = new ServiceLocator;\n\n// регистрирует \"cache\", используя имя класса, которое может быть использовано для создания компонента.\n$locator->set('cache', 'yii\\caching\\ApcCache');\n\n// регистрирует \"db\", используя конфигурационный массив, который может быть использован для создания компонента.\n$locator->set('db', [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=localhost;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n]);\n\n// регистрирует \"search\", используя анонимную функцию, которая создаёт компонент\n$locator->set('search', function () {\n    return new app\\components\\SolrService;\n});\n\n// регистрирует \"pageCache\", используя компонент\n$locator->set('pageCache', new FileCache);\n```\n\nПосле того, как компонент зарегистрирован, вы можете обращаться к нему по его ID одним из двух следующих способов:\n\n```php\n$cache = $locator->get('cache');\n// или\n$cache = $locator->cache;\n```\n\nКак видно выше, [[yii\\di\\ServiceLocator]] позволяет обратиться к компоненту как к свойству, используя его ID.\nПри первом обращении к компоненту, [[yii\\di\\ServiceLocator]] создаст новый экземпляр компонента на основе регистрационной\nинформации и вернёт его. При повторном обращении к компоненту Service Locator вернёт тот же экземпляр.\n\n\nЧтобы проверить, был ли идентификатор компонента уже зарегистрирован, можно использовать [[yii\\di\\ServiceLocator::has()]].\nЕсли вы вызовете [[yii\\di\\ServiceLocator::get()]] с несуществующим ID, будет выброшено исключение.\n\n\nПоскольку Service Locator часто используется с [конфигурациями](concept-configurations.md), в нём имеется доступное\nдля записи свойство [[yii\\di\\ServiceLocator::setComponents()|components]]. Это позволяет настроить и зарегистрировать\nсразу несколько компонентов. Следующий код демонстрирует конфигурационный массив, который может использоваться\nдля регистрации компонентов `db`, `cache`, `tz` и `search` в Service Locator (то есть в [приложении](structure-applications.md)):\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=demo',\n            'username' => 'root',\n            'password' => '',\n        ],\n        'cache' => 'yii\\caching\\ApcCache',\n        'tz' => function() {\n            return new \\DateTimeZone(Yii::$app->formatter->defaultTimeZone);\n        },\n        'search' => function () {\n            $solr = new app\\components\\SolrService('127.0.0.1');\n            // ... дополнительная инициализация ...\n            return $solr;\n        },\n    ],\n];\n```\n\nЕсть альтернативный приведённому выше способ настройки компонента `search`. Вместо анонимной функции, которая\nотдаёт экземпляр `SolrService`, можно использовать статический метод, возвращающий такую анонимную функцию:\n\n```php\nclass SolrServiceBuilder\n{\n    public static function build($ip)\n    {\n        return function () use ($ip) {\n            $solr = new app\\components\\SolrService($ip);\n            // ... дополнительная инициализация ...\n            return $solr;\n        };\n    }\n}\n\nreturn [\n    // ...\n    'components' => [\n        // ...\n        'search' => SolrServiceBuilder::build('127.0.0.1'),\n    ],\n];\n```\n\nЭто особенно полезно, если вы создаёте компонент для Yii, являющийся обёрткой над какой-либо сторонней библиотекой.\nПодобный приведённому выше статический метод позволяет скрыть от конечного пользователя сложную логику настройки\nсторонней библиотеки. Пользователю будет достаточно вызвать статический метод.\n\n"
  },
  {
    "path": "docs/guide-ru/db-active-record.md",
    "content": "Active Record\n=============\n\n[Active Record](https://ru.wikipedia.org/wiki/ActiveRecord) обеспечивает объектно-ориентированный интерфейс для доступа\nи манипулирования данными, хранящимися в базах данных. Класс Active Record соответствует таблице в базе данных, объект\nActive Record соответствует строке этой таблицы, а *атрибут* объекта Active Record представляет собой значение\nотдельного столбца строки. Вместо непосредственного написания SQL-выражений вы сможете получать доступ к атрибутам\nActive Record и вызывать методы Active Record для доступа и манипулирования данными, хранящимися в таблицах базы данных.\n\nДля примера предположим, что `Customer` - это класс Active Record, который сопоставлен с таблицей `customer`, а `name` -\nстолбец в таблице `customer`. Тогда вы можете написать следующий код для вставки новой строки в таблицу `customer`:\n\n```php\n$customer = new Customer();\n$customer->name = 'Qiang';\n$customer->save();\n```\n\nВышеприведённый код аналогичен использованию следующего SQL-выражения в MySQL, которое менее интуитивно, потенциально\nможет вызвать ошибки и даже проблемы совместимости, если вы используете различные виды баз данных:\n\n```php\n$db->createCommand('INSERT INTO `customer` (`name`) VALUES (:name)', [\n    ':name' => 'Qiang',\n])->execute();\n```\n\nYii поддерживает работу с Active Record для следующих реляционных баз данных:\n\n* MySQL 4.1 и выше: посредством [[yii\\db\\ActiveRecord]]\n* PostgreSQL 7.3 и выше: посредством [[yii\\db\\ActiveRecord]]\n* SQLite 2 и 3: посредством [[yii\\db\\ActiveRecord]]\n* Microsoft SQL Server 2008 и выше: посредством [[yii\\db\\ActiveRecord]]\n* Oracle: посредством [[yii\\db\\ActiveRecord]]\n* CUBRID 9.3 и выше: посредством [[yii\\db\\ActiveRecord]] (Имейте в виду, что вследствие\n  [бага](http://jira.cubrid.org/browse/APIS-658) в PDO-расширении для CUBRID, заключение значений в кавычки не работает,\n  поэтому необходимо использовать CUBRID версии 9.3 как на клиентской стороне, так и на сервере)\n* Sphinx: посредством [[yii\\sphinx\\ActiveRecord]], потребуется расширение `yii2-sphinx`\n* ElasticSearch: посредством [[yii\\elasticsearch\\ActiveRecord]], потребуется расширение `yii2-elasticsearch`\n\nКроме того Yii поддерживает использование Active Record со следующими NoSQL базами данных:\n\n* Redis 2.6.12 и выше: посредством [[yii\\redis\\ActiveRecord]], потребуется расширение `yii2-redis`\n* MongoDB 1.3.0 и выше: посредством [[yii\\mongodb\\ActiveRecord]], потребуется расширение `yii2-mongodb`\n\nВ этом руководстве мы в основном будем описывать использование Active Record для реляционных баз данных. Однако большая\nчасть этого материала также применима при использовании Active Record с NoSQL базами данных.\n\n\n## Объявление классов Active Record <span id=\"declaring-ar-classes\"></span>\n\nДля начала объявите свой собственный класс, унаследовав класс [[yii\\db\\ActiveRecord]].\n\n### Настройка имени таблицы\n\nПо умолчанию каждый класс Active Record ассоциирован с таблицей в базе данных. Метод\n[[yii\\db\\ActiveRecord::tableName()|tableName()]] получает имя таблицы из имени класса с помощью [[yii\\helpers\\Inflector::camel2id()]].\nЕсли таблица не названа соответственно, вы можете переопределить данный метод.\n\nТакже может быть применён [[yii\\db\\Connection::$tablePrefix|tablePrefix]] по умолчанию. Например, если \n[[yii\\db\\Connection::$tablePrefix|tablePrefix]] задан как `tbl_`, `Customer` преобразуется в `tbl_customer`, а\n`OrderItem` в `tbl_order_item`. \n\nЕсли имя таблицы указано в формате `{{%TableName}}`, символ `%` заменяется префиксом. Например `{{%post}}` становится\n`{{tbl_post}}`. Фигуриные скобки используются для [экранирования в SQL-запросах](db-dao.md#quoting-table-and-column-names).\n\nВ нижеследующем примере мы объявляем класс Active Record с названием `Customer` для таблицы `customer`.\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass Customer extends ActiveRecord\n{\n    const STATUS_INACTIVE = 0;\n    const STATUS_ACTIVE = 1;\n    \n    /**\n     * @return string название таблицы, сопоставленной с этим ActiveRecord-классом.\n     */\n    public static function tableName()\n    {\n        return '{{customer}}';\n    }\n}\n```\n\n### Классы Active record называются \"моделями\"\n\nОбъекты Active Record являются [моделями](structure-models.md). Именно поэтому мы обычно задаём классам Active Record\nпространство имён `app\\models` (или другое пространство имён, предназначенное для моделей). \n\nТ.к. класс [[yii\\db\\ActiveRecord]] наследует класс [[yii\\base\\Model]], он обладает *всеми* возможностями\n[моделей](structure-models.md), такими как атрибуты, правила валидации, способы сериализации данных и т.д.\n\n\n## Подключение к базам данных <span id=\"db-connection\"></span>\n\nПо умолчанию Active Record для доступа и манипулирования данными БД использует\n[компонент приложения](structure-application-components.md) `db` в качестве компонента\n[[yii\\db\\Connection|DB connection]]. Как сказано в разделе [Объекты доступа к данным (DAO)](db-dao.md), вы можете\nнастраивать компонент `db` на уровне конфигурации приложения как показано ниже:\n\n```php\nreturn [\n    'components' => [\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=testdb',\n            'username' => 'demo',\n            'password' => 'demo',\n        ],\n    ],\n];\n```\nЕсли вы хотите использовать для подключения к базе данных другой компонент подключения, отличный от `db`, вам нужно\nпереопределить метод [[yii\\db\\ActiveRecord::getDb()|getDb()]]:\n\n```php\nclass Customer extends ActiveRecord\n{\n    // ...\n\n    public static function getDb()\n    {\n        // использовать компонент приложения \"db2\"\n        return \\Yii::$app->db2;  \n    }\n}\n```\n\n\n## Получение данных <span id=\"querying-data\"></span>\n\nПосле объявления класса Active Record вы можете использовать его для получения данных из соответствующей таблицы базы\nданных. Этот процесс, как правило, состоит из следующих трёх шагов:\n\n1. Создать новый объект запроса вызовом метода [[yii\\db\\ActiveRecord::find()]];\n2. Настроить объект запроса вызовом [методов построения запросов](db-query-builder.md#building-queries);\n3. Вызвать один из [методов получения данных](db-query-builder.md#query-methods) для извлечения данных в виде объектов\nActive Record.\n\nКак вы могли заметить, эти шаги очень похожи на работу с [построителем запросов](db-query-builder.md). Различие лишь в\nтом, что для создания объекта запроса вместо оператора `new` используется метод [[yii\\db\\ActiveRecord::find()]],\nвозвращающий новый объект запроса, являющийся представителем класса [[yii\\db\\ActiveQuery]].\n\nНиже приведено несколько примеров использования Active Query для получения данных:\n\n```php\n// возвращает покупателя с идентификатором 123\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::find()\n    ->where(['id' => 123])\n    ->one();\n\n// возвращает всех активных покупателей, сортируя их по идентификаторам\n// SELECT * FROM `customer` WHERE `status` = 1 ORDER BY `id`\n$customers = Customer::find()\n    ->where(['status' => Customer::STATUS_ACTIVE])\n    ->orderBy('id')\n    ->all();\n\n// возвращает количество активных покупателей\n// SELECT COUNT(*) FROM `customer` WHERE `status` = 1\n$count = Customer::find()\n    ->where(['status' => Customer::STATUS_ACTIVE])\n    ->count();\n\n// возвращает всех покупателей массивом, индексированным их идентификаторами\n// SELECT * FROM `customer`\n$customers = Customer::find()\n    ->indexBy('id')\n    ->all();\n```\n\nВ примерах выше `$customer` - это объект класса `Customer`, в то время как `$customers` - это массив таких объектов. Все\nэти объекты заполнены данными таблицы `customer`.\n\n> Info: Т.к. класс [[yii\\db\\ActiveQuery]] наследует [[yii\\db\\Query]], вы можете использовать в нём *все* методы\n  построения запросов и все методы класса Query как описано в разделе [Построитель запросов](db-query-builder.md).\n\nТ.к. извлечение данных по первичному ключу или значениям отдельных столбцов достаточно распространённая задача, Yii\nпредоставляет два коротких метода для её решения:\n\n- [[yii\\db\\ActiveRecord::findOne()]]: возвращает один объект Active Record, заполненный первой строкой результата запроса.\n- [[yii\\db\\ActiveRecord::findAll()]]: возвращает массив объектов Active Record, заполненных *всеми* полученными результатами запроса.\n\nОба метода могут принимать параметры в одном из следующих форматов:\n\n- скалярное значение: значение интерпретируется как первичный ключ, по которому следует искать. Yii прочитает\n  информацию о структуре базы данных и автоматически определит, какой столбец таблицы содержит первичные ключи.\n- массив скалярных значений: массив интерпретируется как набор первичных ключей, по которым следует искать.\n- ассоциативный массив: ключи массива интерпретируются как названия столбцов, а значения - как содержимое столбцов,\n  которое следует искать. За подробностями вы можете обратиться к разделу [Hash Format](db-query-builder.md#hash-format)\n  \nНижеследующий код демонстрирует, каким образом эти методы могут быть использованы:\n\n```php\n// возвращает покупателя с идентификатором 123\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// возвращает покупателей с идентификаторами 100, 101, 123 и 124\n// SELECT * FROM `customer` WHERE `id` IN (100, 101, 123, 124)\n$customers = Customer::findAll([100, 101, 123, 124]);\n\n// возвращает активного покупателя с идентификатором 123\n// SELECT * FROM `customer` WHERE `id` = 123 AND `status` = 1\n$customer = Customer::findOne([\n    'id' => 123,\n    'status' => Customer::STATUS_ACTIVE,\n]);\n\n// возвращает всех неактивных покупателей\n// SELECT * FROM `customer` WHERE `status` = 0\n$customers = Customer::findAll([\n    'status' => Customer::STATUS_INACTIVE,\n]);\n```\n\n> Warning: Если вам нужно передать в эти методы данные, полученные от пользователя, убедитесь что передаваемое значение – это скаляр,\n> а если необходимо указать условия в формате массива – убедитесь, что пользовательские данные не могут изменить структуру этого массива.\n>\n> ```php\n> // yii\\web\\Controller гарантирует, что $id будет скаляром\n> public function actionView($id)\n> {\n>     $model = Post::findOne($id);\n>     // ...\n> }\n>\n> // явное указание имени столбца для поиска гарантирует поиск по столбцу `id`,\n> // и возвращение одной записи как для массива, так и для скаляра в принятом от пользователя поле `id` \n> $model = Post::findOne(['id' => Yii::$app->request->get('id')]);\n>\n> // НЕ используйте этот код! Пользователь может передать в параметр `id` массив\n> // и осуществить поиск по имени столбца, которое не должно быть использовано для поиска по логике вашего приложения.\n> $model = Post::findOne(Yii::$app->request->get('id'));\n> ```\n\n> Note: Ни метод [[yii\\db\\ActiveRecord::findOne()]], ни [[yii\\db\\ActiveQuery::one()]] не добавляет условие `LIMIT 1` к\n  генерируемым SQL-запросам. Если ваш запрос может вернуть много строк данных, вы должны вызвать метод `limit(1)` явно\n  в целях улучшения производительности, например: `Customer::find()->limit(1)->one()`.\n\nПомимо использования методов построения запросов вы можете также писать запросы на \"чистом\" SQL для получения данных и\nзаполнения ими объектов Active Record. Вы можете делать это посредством метода [[yii\\db\\ActiveRecord::findBySql()]]:\n\n```php\n// возвращает всех неактивных покупателей\n$sql = 'SELECT * FROM customer WHERE status=:status';\n$customers = Customer::findBySql($sql, [':status' => Customer::STATUS_INACTIVE])->all();\n```\n\nНе используйте дополнительные методы построения запросов после вызова метода\n[[yii\\db\\ActiveRecord::findBySql()|findBySql()]], т.к. они будут проигнорированы.\n\n\n## Доступ к данным <span id=\"accessing-data\"></span>\n\nКак сказано выше, получаемые из базы данные заполняют объекты Active Record и каждая строка результата запроса\nсоответствует одному объекту Active Record. Вы можете получить доступ к значениям столбцов с помощью атрибутов этих\nобъектов. Например так:\n\n```php\n// \"id\" и \"email\" - названия столбцов в таблице \"customer\"\n$customer = Customer::findOne(123);\n$id = $customer->id;\n$email = $customer->email;\n```\n\n> Note: Атрибуты объекта Active Record названы в соответствии с названиями столбцов связной таблицы с учётом\n  регистра. Yii автоматически объявляет для каждого столбца связной таблицы атрибут в Active Record. Вы НЕ должны\n  переопределять какие-либо из этих атрибутов. \n\nАтрибуты Active Record названы в соответствии с именами столбцов таблицы. Если столбцы вашей таблицы именуются через\nнижнее подчёркивание, то может оказаться, что вам придётся писать PHP-код вроде этого: `$customer->first_name` - в нём\nбудет использоваться нижнее подчёркивание для разделения слов в названиях атрибутов. Если вы обеспокоены единообразием\nстиля кодирования, вам придётся переименовать столбцы вашей таблицы соответствующим образом (например, назвать столбцы\nв стиле camelCase).\n\n\n### Преобразование данных <span id=\"data-transformation\"></span>\n\nЧасто бывает так, что данные вводятся и/или отображаются в формате, который отличается от формата их хранения в базе\nданных. Например, в базе данных вы храните дни рождения покупателей в формате UNIX timestamp (что, кстати говоря, не\nявляется хорошим дизайном), в то время как во многих случаях вы хотите манипулировать днями рождения в виде строк\nформата `'ДД.ММ.ГГГГ'`. Для достижения этой цели, вы можете объявить методы *преобразования данных* в\nActiveRecord-классе `Customer` как показано ниже:\n\n```php\nclass Customer extends ActiveRecord\n{\n    // ...\n\n    public function getBirthdayText()\n    {\n        return date('d.m.Y', $this->birthday);\n    }\n    \n    public function setBirthdayText($value)\n    {\n        $this->birthday = strtotime($value);\n    }\n}\n```\n\nТеперь в своём PHP коде вместо доступа к `$customer->birthday`, вы сможете получить доступ к `$customer->birthdayText`,\nчто позволить вам вводить и отображать дни рождения покупателей в формате `'ДД.ММ.ГГГГ'`.\n\n> Tip: Вышеприведённый пример демонстрирует общий способ преобразования данных в различные форматы. Если вы\n  работаете с датами и временем, вы можете использовать [DateValidator](tutorial-core-validators.md#date) и\n  [[yii\\jui\\DatePicker|DatePicker]], которые проще в использовании и являются более мощными инструментами.\n\n\n### Получение данных в виде массива <span id=\"data-in-arrays\"></span>\n\nНесмотря на то, что получение данных в виде Active Record объектов является удобным и гибким, этот способ не всегда\nподходит при получении большого количества данных из-за больших накладных расходов памяти. В этом случае вы можете\nполучить данные в виде PHP-массива, используя перед выполнением запроса метод\n[[yii\\db\\ActiveQuery::asArray()|asArray()]]:\n\n```php\n// возвращает всех покупателей\n// каждый покупатель будет представлен в виде ассоциативного массива\n$customers = Customer::find()\n    ->asArray()\n    ->all();\n```\n\n> Note: В то время как этот способ бережёт память и улучшает производительность, он ближе к низкому слою\n  абстракции базы данных и вы потеряете многие возможности Active Record. Важное отличие заключается в типах данных\n  значений столбцов. Когда вы получаете данные в виде объектов Active Record, значения столбцов автоматически приводятся\n  к типам, соответствующим типам столбцов; с другой стороны, когда вы получаете данные в массивах, значения столбцов\n  будут строковыми (до тех пор, пока они являются результатом работы PDO-слоя без какой-либо обработки), несмотря на\n  настоящие типы данных соответствующих столбцов.\n  \n\n### Пакетное получение данных <span id=\"data-in-batches\"></span>\n\nВ главе [Построитель запросов](db-query-builder.md) мы объясняли, что вы можете использовать *пакетную выборку* для\nснижения расходов памяти при получении большого количества данных из базы. Вы можете использовать такой же подход при\nработе с Active Record. Например:\n\n```php\n// получить 10 покупателей одновременно\nforeach (Customer::find()->batch(10) as $customers) {\n    // $customers - это массив, в котором находится 10 или меньше объектов класса Customer\n}\n\n// получить одновременно десять покупателей и перебрать их одного за другим\nforeach (Customer::find()->each(10) as $customer) {\n    // $customer - это объект класса Customer\n}\n\n// пакетная выборка с жадной загрузкой\nforeach (Customer::find()->with('orders')->each() as $customer) {\n    // $customer - это объект класса Customer\n}\n```\n\n\n## Сохранение данных <span id=\"inserting-updating-data\"></span>\n\nИспользуя Active Record, вы легко можете сохранить данные в базу данных, осуществив следующие шаги:\n\n1. Подготовьте объект Active Record;\n2. Присвойте новые значения атрибутам Active Record;\n3. Вызовите метод [[yii\\db\\ActiveRecord::save()]] для сохранения данных в базу данных.\n\nНапример:\n\n```php\n// вставить новую строку данных\n$customer = new Customer();\n$customer->name = 'James';\n$customer->email = 'james@example.com';\n$customer->save();\n\n// обновить имеющуюся строку данных\n$customer = Customer::findOne(123);\n$customer->email = 'james@newexample.com';\n$customer->save();\n```\n\nМетод [[yii\\db\\ActiveRecord::save()|save()]] может вставить или обновить строку данных в зависимости от состояния\nActive Record объекта. Если объект создан с помощью оператора `new`, вызов метода [[yii\\db\\ActiveRecord::save()|save()]]\nприведёт к вставке новой строки данных; если объект был получен с помощью запроса на получение данных, вызов \n[[yii\\db\\ActiveRecord::save()|save()]] обновит строку таблицы, соответствующую объекту Active Record.\n\nВы можете различать два состояния Active Record объекта с помощью проверки значения его свойства\n[[yii\\db\\ActiveRecord::isNewRecord|isNewRecord]]. Это свойство также используется внутри метода\n[[yii\\db\\ActiveRecord::save()|save()]] как показано ниже:\n\n```php\npublic function save($runValidation = true, $attributeNames = null)\n{\n    if ($this->getIsNewRecord()) {\n        return $this->insert($runValidation, $attributeNames);\n    } else {\n        return $this->update($runValidation, $attributeNames) !== false;\n    }\n}\n```\n\n> Tip: Вы можете вызвать [[yii\\db\\ActiveRecord::insert()|insert()]] или [[yii\\db\\ActiveRecord::update()|update()]]\n  непосредственно, чтобы вставить или обновить строку данных в таблице.\n  \n\n### Валидация данных <span id=\"data-validation\"></span>\n\nТ.к. класс [[yii\\db\\ActiveRecord]] наследует класс [[yii\\base\\Model]], он обладает такими же возможностями\n[валидации данных](input-validation.md). Вы можете объявить правила валидации переопределив метод\n[[yii\\db\\ActiveRecord::rules()|rules()]] и осуществлять валидацию данных посредством вызовов метода\n[[yii\\db\\ActiveRecord::validate()|validate()]].\n\nКогда вы вызываете метод [[yii\\db\\ActiveRecord::save()|save()]], по умолчанию он автоматически вызывает метод\n[[yii\\db\\ActiveRecord::validate()|validate()]]. Только после успешного прохождения валидации происходит сохранение\nданных; в ином случае метод [[yii\\db\\ActiveRecord::save()|save()]] просто возвращает `false`, и вы можете проверить\nсвойство [[yii\\db\\ActiveRecord::errors|errors]] для получения сообщений об ошибках валидации.\n\n> Tip: Если вы уверены, что ваши данные не требуют валидации (например, данные пришли из доверенного источника),\n  вы можете вызвать `save(false)`, чтобы пропустить валидацию.\n\n\n### Массовое присваивание <span id=\"massive-assignment\"></span>\n\nКак и обычные [модели](structure-models.md), объекты Active Record тоже обладают\n[возможностью массового присваивания](structure-models.md#massive-assignment). Как будет показано ниже, используя эту\nвозможность, вы можете одним PHP выражением присвоить значения множества атрибутов Active Record объекту. Запомните\nоднако, что только [безопасные атрибуты](structure-models.md#safe-attributes) могут быть массово присвоены.\n\n```php\n$values = [\n    'name' => 'James',\n    'email' => 'james@example.com',\n];\n\n$customer = new Customer();\n\n$customer->attributes = $values;\n$customer->save();\n```\n\n\n### Обновление счётчиков <span id=\"updating-counters\"></span>\n\nРаспространённой задачей является инкремент или декремент столбца в таблице базы данных. Назовём такие столбцы\nстолбцами-счётчиками. Вы можете использовать метод [[yii\\db\\ActiveRecord::updateCounters()|updateCounters()]] для\nобновления одного или нескольких столбцов-счётчиков. Например:\n\n```php\n$post = Post::findOne(100);\n\n// UPDATE `post` SET `view_count` = `view_count` + 1 WHERE `id` = 100\n$post->updateCounters(['view_count' => 1]);\n```\n\n> Note: Если вы используете метод [[yii\\db\\ActiveRecord::save()]] для обновления столбца-счётчика, вы можете\n  прийти к некорректному результату, т.к. вполне вероятно, что этот же счётчик был сохранён сразу несколькими запросами,\n  которые читают и записывают этот же столбец-счётчик.\n\n\n### Dirty-атрибуты <span id=\"dirty-attributes\"></span>\n\nКогда вы вызываете [[yii\\db\\ActiveRecord::save()|save()]] для сохранения Active Record объекта, сохраняются только \n*dirty-атрибуты*. Атрибут считается *dirty-атрибутом*, если его значение было изменено после чтения из базы данных или\nже он был сохранён в базу данных совсем недавно. Заметьте, что валидация данных осуществляется независимо от того,\nимеются ли dirty-атрибуты в объекте Active Record или нет.\n\nActive Record автоматически поддерживает список dirty-атрибутов. Это достигается за счёт хранения старых значений\nатрибутов и сравнения их с новыми. Вы можете вызвать метод [[yii\\db\\ActiveRecord::getDirtyAttributes()]] для получения\nтекущего списка dirty-атрибутов. Вы также можете вызвать [[yii\\db\\ActiveRecord::markAttributeDirty()]], чтобы явно\nпометить атрибут в качестве dirty-атрибута.\n\nЕсли вам нужны значения атрибутов, какими они были до их изменения, вы можете вызвать\n[[yii\\db\\ActiveRecord::getOldAttributes()|getOldAttributes()]] или\n[[yii\\db\\ActiveRecord::getOldAttribute()|getOldAttribute()]].\n\n> Note: Сравнение старых и новых значений будет осуществлено с помощью оператора `===`, так что значение будет\n  считаться dirty-значением даже в том случае, если оно осталось таким же, но изменило свой тип. Это часто происходит,\n  когда модель получает пользовательский ввод из HTML-форм, где каждое значение представлено строкой. Чтобы убедиться в\n  корректности типа данных, например для целых значений, вы можете применить\n  [фильтрацию данных](input-validation.md#data-filtering): `['attributeName', 'filter', 'filter' => 'intval']`.\n\n\n### Значения атрибутов по умолчанию <span id=\"default-attribute-values\"></span>\n\nНекоторые столбцы ваших таблиц могут иметь значения по умолчанию, объявленные в базе данных. Иногда вы можете захотеть\nпредварительно заполнить этими значениями вашу веб-форму, которая соответствует Active Record объекту. Чтобы избежать\nповторного указания этих значений, вы можете вызвать метод\n[[yii\\db\\ActiveRecord::loadDefaultValues()|loadDefaultValues()]] для заполнения соответствующих Active Record атрибутов\nзначениями по умолчанию, объявленными в базе данных:\n\n```php\n$customer = new Customer();\n$customer->loadDefaultValues();\n// $customer->xyz получит значение по умолчанию, которое было указано при объявлении столбца \"xyz\"\n```\n\n\n### Приведение типов атрибутов <span id=\"attributes-typecasting\"></span>\n\nПри заполнении результатами запроса [[yii\\db\\ActiveRecord]] производит автоматическое приведение типов для значений\nатрибутов на основе информации из [схемы базы данны](db-dao.md#database-schema). Это позволяет данным, полученным из\nколонки таблицы объявленной как целое, заноситься в экземпляр ActiveRecord как значение целого типа PHP, булево как\nбулево и т.д.\nОднако, механизм приведения типов имеет несколько ограничений:\n\n* Числа с плавающей точкой не будут обработаны, а будут представленны как строки, в противном случае они могут потерять точность.\n* Конвертация целых чисел зависит от разрядности используемой операционной системы. В частности: значения колонок, объявленных\n  как 'unsigned integer' или 'big integer' будут приведены к целому типу PHP только на 64-х разрядных системах, в то время\n  как на 32-х разрядных - они будут представленны как строки.\n\nИмейте в виду, что преобразование типов производиться только в момент заполнения экземпляра ActiveRecord данными из результата\nзапроса. При заполнении данных из HTTP запроса или непосредственно через механизм доступа к полям - автоматическая конвертация\nне производтся.\nСхема таблицы базы данных также используется при построении SQL запроса для сохранения данных ActiveRecord, обеспечивая\nсоответсвие типов связываемых параметров в запросе. Однако, над атрибутами объекта ActiveRecord не будет производиться\nприведение типов в процессе сохранения.\n\n> Совет: вы можете использовать поведение [[yii\\behaviors\\AttributeTypecastBehavior]] для того, чтобы производить\n  приведение типов для ActiveRecord во время валидации или сохранения.\n  \nНачиная с 2.0.14, Yii ActiveRecord поддерживает сложные типы данных, такие как JSON или многомерные массивы.\n\n#### JSON в MySQL и PostgreSQL\nПосле заполнения данных, значение из столбца JSON будет автоматически декодировано из JSON в соответствии со стандартными правилами декодирования JSON.\n\nЧтобы сохранить значение атрибута в столбец JSON, ActiveRecord автоматически создаст объект [[yii\\db\\JsonExpression|JsonExpression]], который будет закодирован в строку JSON на уровне [QueryBuilder](db-query-builder.md).\n\n#### Массивы в PostgreSQL\nПосле заполнения данных значение из столбца `Array` будет автоматически декодировано из нотации PgSQL в объект [[yii\\db\\ArrayExpression|ArrayExpression]]. Он реализует интерфейс PHP `ArrayAccess`, так что вы можете использовать его в качестве массива, или вызвать `->getValue ()`, чтобы получить сам массив.\n\nЧтобы сохранить значение атрибута в столбец массива, ActiveRecord автоматически создаст объект [[yii\\db\\Array Expression|ArrayExpression]], который будет закодирован [QueryBuilder](db-query-builder.md) в строковое представление массива PgSQL.\n\nМожно также использовать условия для столбцов JSON:\n\n```php\n$query->andWhere(['=', 'json', new ArrayExpression(['foo' => 'bar'])])\n```\nДополнительные сведения о системе построения выражений см. [Query Builder – добавление пользовательских условий и выражений](db-query-builder.md#adding-custom-conditions-and-expressions)\n\n### Обновление нескольких строк данных <span id=\"updating-multiple-rows\"></span>\n\nМетоды, представленные выше, работают с отдельными Active Record объектами, инициируя вставку или обновление данных для\nотдельной строки таблицы. Вместо них для обновления нескольких строк одновременно можно использовать метод\n[[yii\\db\\ActiveRecord::updateAll()|updateAll()]], который является статическим.\n\n```php\n// UPDATE `customer` SET `status` = 1 WHERE `email` LIKE `%@example.com%`\nCustomer::updateAll(['status' => Customer::STATUS_ACTIVE], ['like', 'email', '@example.com']);\n```\n\nПодобным образом можно использовать метод [[yii\\db\\ActiveRecord::updateAllCounters()|updateAllCounters()]] для\nобновления значений столбцов-счётчиков в нескольких строках одновременно.\n\n```php\n// UPDATE `customer` SET `age` = `age` + 1\nCustomer::updateAllCounters(['age' => 1]);\n```\n\n\n## Удаление данных <span id=\"deleting-data\"></span>\n\nДля удаления одной отдельной строки данных сначала получите Active Record объект, соответствующий этой строке, а затем\nвызовите метод [[yii\\db\\ActiveRecord::delete()]].\n\n```php\n$customer = Customer::findOne(123);\n$customer->delete();\n```\n\nВы можете вызвать [[yii\\db\\ActiveRecord::deleteAll()]] для удаления всех или нескольких строк данных одновременно.\nНапример:\n\n```php\nCustomer::deleteAll(['status' => Customer::STATUS_INACTIVE]);\n```\n\n> Note: будьте очень осторожны, используя метод [[yii\\db\\ActiveRecord::deleteAll()|deleteAll()]], потому что он\n  может полностью удалить все данные из вашей таблицы, если вы сделаете ошибку при указании условий удаления.\n\n\n## Жизненные циклы Active Record <span id=\"ar-life-cycles\"></span>\n\nВажно понимать как устроены жизненные циклы Active Record при использовании Active Record для различных целей.\nВ течение каждого жизненного цикла вызывается определённая последовательность методов, которые вы можете переопределять,\nчтобы получить возможность тонкой настройки жизненного цикла. Для встраивания своего кода вы также можете отвечать на\nконкретные события Active Record, которые срабатывают в течение жизненного цикла. Эти события особенно полезны, когда\nвы разрабатываете [поведения](concept-behaviors.md), которые требуют тонкой настройки жизненных циклов Active Record.\n\nНиже мы подробно опишем различные жизненные циклы Active Record и методы/события, которые участвуют в жизненных циклах.\n\n\n### Жизненный цикл создания нового объекта <span id=\"new-instance-life-cycle\"></span>\n\nКогда создаётся новый объект Active Record с помощью оператора `new`, следующий жизненный цикл имеет место:\n\n1. Вызывается конструктор класса;\n2. Вызывается [[yii\\db\\ActiveRecord::init()|init()]]:\n   инициируется событие [[yii\\db\\ActiveRecord::EVENT_INIT|EVENT_INIT]].\n\n\n### Жизненный цикл получения данных <span id=\"querying-data-life-cycle\"></span>\n\nКогда происходит получение данных посредством одного из [методов получения данных](#querying-data), каждый вновь\nсоздаваемый объект Active Record при заполнении данными проходит следующий жизненный цикл:\n\n1. Вызывается конструктор класса.\n2. Вызывается [[yii\\db\\ActiveRecord::init()|init()]]: инициируется событие\n   [[yii\\db\\ActiveRecord::EVENT_INIT|EVENT_INIT]].\n3. Вызывается [[yii\\db\\ActiveRecord::afterFind()|afterFind()]]: инициируется событие\n   [[yii\\db\\ActiveRecord::EVENT_AFTER_FIND|EVENT_AFTER_FIND]].\n\n\n### Жизненный цикл сохранения данных <span id=\"saving-data-life-cycle\"></span>\n\nКогда вызывается метод [[yii\\db\\ActiveRecord::save()|save()]] для вставки или обновления объекта Active Record,\nследующий жизненный цикл имеет место:\n\n1. Вызывается [[yii\\db\\ActiveRecord::beforeValidate()|beforeValidate()]]: инициируется событие \n   [[yii\\db\\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]]. Если метод возвращает `false` или свойство\n   события [[yii\\base\\ModelEvent::isValid]] равно `false`, оставшиеся шаги не выполняются.\n2. Осуществляется валидация данных. Если валидация закончилась неудачей, после 3-го шага остальные шаги не выполняются.\n3. Вызывается [[yii\\db\\ActiveRecord::afterValidate()|afterValidate()]]: инициируется событие \n   [[yii\\db\\ActiveRecord::EVENT_AFTER_VALIDATE|EVENT_AFTER_VALIDATE]].\n4. Вызывается [[yii\\db\\ActiveRecord::beforeSave()|beforeSave()]]: инициируется событие \n   [[yii\\db\\ActiveRecord::EVENT_BEFORE_INSERT|EVENT_BEFORE_INSERT]] или событие\n   [[yii\\db\\ActiveRecord::EVENT_BEFORE_UPDATE|EVENT_BEFORE_UPDATE]]. Если метод возвращает `false` или свойство события\n   [[yii\\base\\ModelEvent::isValid]] равно `false`, оставшиеся шаги не выполняются.\n5. Осуществляется фактическая вставка или обновление данных в базу данных;\n6. Вызывается [[yii\\db\\ActiveRecord::afterSave()|afterSave()]]: инициируется событие\n   [[yii\\db\\ActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]] или событие\n   [[yii\\db\\ActiveRecord::EVENT_AFTER_UPDATE|EVENT_AFTER_UPDATE]].\n   \n\n### Жизненный цикл удаления данных <span id=\"deleting-data-life-cycle\"></span>\n\nКогда вызывается метод [[yii\\db\\ActiveRecord::delete()|delete()]] для удаления объекта Active Record, следующий\nжизненный цикл имеет место:\n\n1. Вызывается [[yii\\db\\ActiveRecord::beforeDelete()|beforeDelete()]]: инициируется событие\n   [[yii\\db\\ActiveRecord::EVENT_BEFORE_DELETE|EVENT_BEFORE_DELETE]]. Если метод возвращает `false` или свойство события\n   [[yii\\base\\ModelEvent::isValid]] равно `false`, остальные шаги не выполняются.\n2. Осуществляется фактическое удаление данных из базы данных.\n3. Вызывается [[yii\\db\\ActiveRecord::afterDelete()|afterDelete()]]: инициируется событие\n   [[yii\\db\\ActiveRecord::EVENT_AFTER_DELETE|EVENT_AFTER_DELETE]].\n\n\n> Note: Вызов следующих методов НЕ инициирует ни один из вышеприведённых жизненных циклов:\n> - [[yii\\db\\ActiveRecord::updateAll()]]\n> - [[yii\\db\\ActiveRecord::deleteAll()]]\n> - [[yii\\db\\ActiveRecord::updateCounters()]] \n> - [[yii\\db\\ActiveRecord::updateAllCounters()]] \n\n\n## Работа с транзакциями <span id=\"transactional-operations\"></span>\n\nЕсть два способа использования [транзакций](db-dao.md#performing-transactions) при работе с Active Record.\n\nПервый способ заключается в том, чтобы явно заключить все вызовы методов Active Record в блок транзакции как показано\nниже:\n\n```php\n$customer = Customer::findOne(123);\n\nCustomer::getDb()->transaction(function($db) use ($customer) {\n    $customer->id = 200;\n    $customer->save();\n    // ...другие операции с базой данных...\n});\n\n// или по-другому\n\n$transaction = Customer::getDb()->beginTransaction();\ntry {\n    $customer->id = 200;\n    $customer->save();\n    // ...другие операции с базой данных...\n    $transaction->commit();\n} catch(\\Exception $e) {\n    $transaction->rollBack();\n    throw $e;\n} catch(\\Throwable $e) {\n    $transaction->rollBack();\n    throw $e;\n}\n```\n\n> Note: в коде выше ради совместимости с PHP 5.x и PHP 7.x использованы два блока catch. \n> `\\Exception` реализует интерфейс [`\\Throwable` interface](https://www.php.net/manual/ru/class.throwable.php)\n> начиная с PHP 7.0. Если вы используете только PHP 7 и новее, можете пропустить блок с `\\Exception`.\n\nВторой способ заключается в том, чтобы перечислить операции с базой данных, которые требуют тразнакционного выполнения,\nв методе [[yii\\db\\ActiveRecord::transactions()]]. Например:\n\n```php\nclass Customer extends ActiveRecord\n{\n    public function transactions()\n    {\n        return [\n            'admin' => self::OP_INSERT,\n            'api' => self::OP_INSERT | self::OP_UPDATE | self::OP_DELETE,\n            // вышеприведённая строка эквивалентна следующей:\n            // 'api' => self::OP_ALL,\n        ];\n    }\n}\n```\n\nМетод [[yii\\db\\ActiveRecord::transactions()]] должен возвращать массив, ключи которого являются именами \n[сценариев](structure-models.md#scenarios), а значения соответствуют операциям, которые должны быть выполнены с помощью\nтранзакций. Вы должны использовать следующие константы для обозначения различных операций базы данных:\n\n* [[yii\\db\\ActiveRecord::OP_INSERT|OP_INSERT]]: операция вставки, осуществляемая с помощью метода\n  [[yii\\db\\ActiveRecord::insert()|insert()]];\n* [[yii\\db\\ActiveRecord::OP_UPDATE|OP_UPDATE]]: операция обновления, осуществляемая с помощью метода\n  [[yii\\db\\ActiveRecord::update()|update()]];\n* [[yii\\db\\ActiveRecord::OP_DELETE|OP_DELETE]]: операция удаления, осуществляемая с помощью метода\n  [[yii\\db\\ActiveRecord::delete()|delete()]].\n\nИспользуйте операторы `|` для объединения вышеприведённых констант при обозначении множества операций. Вы можете также\nиспользовать вспомогательную константу [[yii\\db\\ActiveRecord::OP_ALL|OP_ALL]], чтобы обозначить одной константой все три\nвышеприведённые операции.\n\n\n## Оптимистическая блокировка <span id=\"optimistic-locks\"></span>\n\nОптимистическая блокировка - это способ предотвращения конфликтов, которые могут возникать, когда одна и та же строка\nданных обновляется несколькими пользователями. Например, пользователь A и пользователь B одновременно редактируют одну и\nту же wiki-статью. После того, как пользователь A сохранит свои изменения, пользователь B нажимает на кнопку \"Сохранить\"\nв попытке также сохранить свои изменения. Т.к. пользователь B работал с фактически-устаревшей версией статьи, было бы\nнеплохо иметь способ предотвратить сохранение его варианта статьи и показать ему некоторое сообщение с подсказкой о том,\nчто произошло.\n\nОптимистическая блокировка решает вышеприведённую проблему за счёт использования отдельного столбца для сохранения\nномера версии каждой строки данных. Когда строка данных сохраняется с использованием устаревшего номера версии,\nвыбрасывается исключение [[yii\\db\\StaleObjectException]], которое предохраняет строку от сохранения. Оптимистическая\nблокировка поддерживается только тогда, когда вы обновляете или удаляете существующую строку данных, используя методы\n[[yii\\db\\ActiveRecord::update()]] или [[yii\\db\\ActiveRecord::delete()]] соответственно.\n\nДля использования оптимистической блокировки:\n\n1. Создайте столбец в таблице базы данных, ассоциированной с классом Active Record, для сохранения номера версии каждой\n   строки данных. Столбец должен быть типа big integer (в Mysql это будет `BIGINT DEFAULT 0`).\n2. Переопределите метод [[yii\\db\\ActiveRecord::optimisticLock()]] таким образом, чтобы он возвращал название этого\n   столбца.\n3. В веб-форме, которая принимает пользовательский ввод, добавьте скрытое поле для сохранения текущей версии обновляемой\n   строки. Убедитесь, что для вашего атрибута с версией объявлены правила валидации, и валидация проходит успешно.\n4. В действии контроллера, которое занимается обновлением строки данных с использованием Active Record, оберните в блок\n   try...catch код и перехватывайте исключение [[yii\\db\\StaleObjectException]]. Реализуйте необходимую бизнес-логику\n   (например, возможность слияния изменений, подсказку о том, что данные устарели) для разрешения возникшего конфликта.\n   \nНапример, предположим, что столбец с версией называется `version`. Вы можете реализовать оптимистическую блокировку с \nпомощью подобного кода:\n\n```php\n// ------ код представления -------\n\nuse yii\\helpers\\Html;\n\n// ...другие поля ввода\necho Html::activeHiddenInput($model, 'version');\n\n\n// ------ код контроллера -------\n\nuse yii\\db\\StaleObjectException;\n\npublic function actionUpdate($id)\n{\n    $model = $this->findModel($id);\n\n    try {\n        if ($model->load(Yii::$app->request->post()) && $model->save()) {\n            return $this->redirect(['view', 'id' => $model->id]);\n        } else {\n            return $this->render('update', [\n                'model' => $model,\n            ]);\n        }\n    } catch (StaleObjectException $e) {\n        // логика разрешения конфликта версий\n    }\n}\n```\n\n\n## Работа со связными данными <span id=\"relational-data\"></span>\n\nПомимо работы с отдельными таблицами баз данных, Active Record также имеет возможность объединять связные данные, что\nделает их легко-доступными для получения через основные объекты данных. Например, данные покупателя связаны с данными\nзаказов, потому что один покупатель может осуществить один или несколько заказов. С помощью объявления этой связи вы\nможете получить возможность доступа к информации о заказе покупателя с помощью выражения `$customer->orders`, которое\nвозвращает информацию о заказе покупателя в виде массива объектов класса `Order`, которые являются Active Record\nобъектами.\n\n\n### Объявление связей <span id=\"declaring-relations\"></span>\n\nДля работы со связными данными посредством Active Record вы прежде всего должны объявить связи в классе Active Record.\nЭта задача решается простым объявлением *методов получения связных данных* для каждой интересующей вас связи как\nпоказано ниже:\n\n```php\nclass Customer extends ActiveRecord\n{\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n}\n\nclass Order extends ActiveRecord\n{\n    public function getCustomer()\n    {\n        return $this->hasOne(Customer::class, ['id' => 'customer_id']);\n    }\n}\n```\n\nВ вышеприведённом коде мы объявили связь `orders` для класса `Customer` и связь `customer` для класса `Order`. \n\nКаждый метод получения связных данных должен быть назван в формате `getXyz`. Мы называем `xyz` (первая буква в нижнем\nрегистре) *именем связи*. Помните, что имена связей чувствительны к регистру.\n\nПри объявлении связи, вы должны указать следующую информацию:\n\n- кратность связи: указывается с помощью вызова метода [[yii\\db\\ActiveRecord::hasMany()|hasMany()]] или метода\n  [[yii\\db\\ActiveRecord::hasOne()|hasOne()]]. В вышеприведённом примере вы можете легко увидеть в объявлениях связей,\n  что покупатель может иметь много заказов в то время, как заказ может быть сделан лишь одним покупателем.\n- название связного Active Record класса: указывается в качестве первого параметра для метода \n  [[yii\\db\\ActiveRecord::hasMany()|hasMany()]] или для метода [[yii\\db\\ActiveRecord::hasOne()|hasOne()]]. Рекомендуется\n  использовать код `Xyz::class`, чтобы получить строку с именем класса, при этом вы сможете воспользоваться\n  возможностями авто-дополнения кода, встроенного в IDE, а также получите обработку ошибок на этапе компиляции.\n- связь между двумя типами данных: указываются столбцы с помощью которых два типа данных связаны. Значения массива - это\n  столбцы  основного объекта данных (представлен классом Active Record, в котором объявляется связь), в то время как\n  ключи массива - столбцы связанных данных.\n\n  Есть простой способ запомнить это правило: как вы можете увидеть в примере выше, столбец связной Active Record\n  указывается сразу после указания самого класса Active Record. Вы видите, что `customer_id` - это свойство класса\n  `Order`, а `id` - свойство класса `Customer`.\n  \n> Warning: Имя связи `relation` зарезервировано. Его использование приведёт к ошибке `ArgumentCountError`.\n\n### Доступ к связным данным <span id=\"accessing-relational-data\"></span>\n\nПосле объявления связей вы можете получать доступ к связным данным с помощью имён связей. Это происходит таким же\nобразом, каким осуществляется доступ к [свойству](concept-properties.md) объекта объявленному с помощью метода получения\nсвязных данных. По этой причине, мы называем его *свойством связи*. Например:\n\n```php\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123\n// $orders - это массив объектов Order\n$orders = $customer->orders;\n```\n\n> Info: когда вы объявляете связь с названием `xyz` посредством геттера `getXyz()`, у вас появляется возможность\n  доступа к свойству `xyz` подобно [свойству объекта](concept-properties.md). Помните, что название связи чувствительно\n  к регистру.\n  \nЕсли связь объявлена с помощью метода [[yii\\db\\ActiveRecord::hasMany()|hasMany()]], доступ к свойству связи вернёт\nмассив связных объектов Active Record; если связь объявлена с помощью метода [[yii\\db\\ActiveRecord::hasOne()|hasOne()]],\nдоступ к свойству связи вернёт связный Active Record объект или `null`, если связные данные не найдены.\n\nКогда вы запрашиваете свойство связи в первый раз, выполняется SQL-выражение как показано в примере выше. Если то же\nсамое свойство запрашивается вновь, будет возвращён результат предыдущего SQL-запроса без повторного выполнения\nSQL-выражения. Для принудительного повторного выполнения SQL-запроса, вы можете удалить свойство связи с помощью\nоперации: `unset($customer->orders)`.\n\n> Note: Несмотря на то, что эта концепция выглядит похожей на концепцию [свойств объектов](concept-properties.md),\n> между ними есть важное различие. Для обычных свойств объектов значения свойств имеют тот же тип, который возвращает\n> геттер. Однако метод получения связных данных возвращает объект [[yii\\db\\ActiveQuery]], в то время как доступ к\n> свойству связи возвращает объект [[yii\\db\\ActiveRecord]] или массив таких объектов.\n> ```php\n> $customer->orders; // массив объектов `Order`\n> $customer->getOrders(); // объект ActiveQuery\n> ```\n> Это полезно при тонкой настройке запросов к связным данным, что будет описано в следующем разделе.\n\n\n### Динамические запросы связных данных <span id=\"dynamic-relational-query\"></span>\n\nТ.к. метод получения связных данных возвращает объект запроса [[yii\\db\\ActiveQuery]], вы можете в дальнейшем перед его\nотправкой в базу данных настроить этот запрос, используя методы построения запросов. Например:\n\n```php\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123 AND `subtotal` > 200 ORDER BY `id`\n$orders = $customer->getOrders()\n    ->where(['>', 'subtotal', 200])\n    ->orderBy('id')\n    ->all();\n```\nВ отличие от доступа к данным с помощью свойства связи, каждый раз при выполнении такого динамического запроса\nпосредством метода получения связных данных будет выполняться SQL-запрос, даже если тот же самый динамический запрос был\nотправлен ранее.\n\nИногда вы можете даже захотеть настроить объявление связи таким образом, чтобы вы могли более просто осуществлять \nдинамические запросы связных данных. Например, вы можете объявить связь `bigOrders` как показано ниже: \n\n```php\nclass Customer extends ActiveRecord\n{\n    public function getBigOrders($threshold = 100)\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id'])\n            ->where('subtotal > :threshold', [':threshold' => $threshold])\n            ->orderBy('id');\n    }\n}\n```\n\nПосле этого вы сможете выполнять следующие запросы связных данных:\n\n```php\n// SELECT * FROM `order` WHERE `customer_id` = 123 AND `subtotal` > 200 ORDER BY `id`\n$orders = $customer->getBigOrders(200)->all();\n\n// SELECT * FROM `order` WHERE `customer_id` = 123 AND `subtotal` > 100 ORDER BY `id`\n$orders = $customer->bigOrders;\n```\n\n\n### Связывание посредством промежуточной таблицы <span id=\"junction-table\"></span>\n\nПри проектировании баз данных, когда между двумя таблицами имеется кратность связи many-to-many, обычно вводится \n[промежуточная таблица](https://en.wikipedia.org/wiki/Junction_table). Например, таблицы `order` и `item` могут быть\nсвязаны посредством промежуточной таблицы с названием `order_item`. Один заказ будет соотноситься с несколькими товарами,\nв то время как один товар будет также соотноситься с несколькими заказами.\n\nПри объявлении подобных связей вы можете пользоваться методом [[yii\\db\\ActiveQuery::via()|via()]] или методом\n[[yii\\db\\ActiveQuery::viaTable()|viaTable()]] для указания промежуточной таблицы. Разница между методами\n[[yii\\db\\ActiveQuery::via()|via()]] и [[yii\\db\\ActiveQuery::viaTable()|viaTable()]] заключается в том, что первый\nметод указывает промежуточную таблицу с помощью названия связи, в то время как второй метод непосредственно указывает\nпромежуточную таблицу. Например:\n\n```php\nclass Order extends ActiveRecord\n{\n    public function getItems()\n    {\n        return $this->hasMany(Item::class, ['id' => 'item_id'])\n            ->viaTable('order_item', ['order_id' => 'id']);\n    }\n}\n```\n\nили по-другому:\n\n```php\nclass Order extends ActiveRecord\n{\n    public function getOrderItems()\n    {\n        return $this->hasMany(OrderItem::class, ['order_id' => 'id']);\n    }\n\n    public function getItems()\n    {\n        return $this->hasMany(Item::class, ['id' => 'item_id'])\n            ->via('orderItems');\n    }\n}\n```\n\nИспользовать связи, объявленные с помощью промежуточных таблиц, можно точно также, как и обычные связи. Например:\n\n```php\n// SELECT * FROM `order` WHERE `id` = 100\n$order = Order::findOne(100);\n\n// SELECT * FROM `order_item` WHERE `order_id` = 100\n// SELECT * FROM `item` WHERE `item_id` IN (...)\n// возвращает массив объектов Item\n$items = $order->items;\n```\n\n\n### Отложенная и жадная загрузка <span id=\"lazy-eager-loading\"></span>\n\nВ разделе [Доступ к связным данным](#accessing-relational-data), мы показывали, что вы можете получать доступ к свойству\nсвязи объекта Active Record точно также, как получаете доступ к свойству обычного объекта. SQL-запрос будет выполнен\nтолько во время первого доступа к свойству связи. Мы называем подобный способ получения связных данных *отложенной\nзагрузкой*. Например:\n\n```php\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123\n$orders = $customer->orders;\n\n// SQL-запрос не выполняется\n$orders2 = $customer->orders;\n```\n\nОтложенная загрузка очень удобна в использовании. Однако этот метод может вызвать проблемы производительности, когда вам\nпонадобится получить доступ к тем же самым свойствам связей для нескольких объектов Active Record. Рассмотрите\nследующий пример кода. Сколько SQL-запросов будет выполнено?\n\n```php\n// SELECT * FROM `customer` LIMIT 100\n$customers = Customer::find()->limit(100)->all();\n\nforeach ($customers as $customer) {\n    // SELECT * FROM `order` WHERE `customer_id` = ...\n    $orders = $customer->orders;\n}\n```\n\nКак вы могли заметить по вышеприведённым комментариям кода, будет выполнен 101 SQL-запрос! Это произойдёт из-за того,\nчто каждый раз внутри цикла будет выполняться SQL-запрос при получении доступа к свойству связи `orders` каждого\nотдельного объекта `Customer`.\n\nДля решения этой проблемы производительности вы можете, как показано ниже, использовать подход, который называется\n*жадная загрузка*:\n\n```php\n// SELECT * FROM `customer` LIMIT 100;\n// SELECT * FROM `orders` WHERE `customer_id` IN (...)\n$customers = Customer::find()\n    ->with('orders')\n    ->limit(100)\n    ->all();\n\nforeach ($customers as $customer) {\n    // SQL-запрос не выполняется\n    $orders = $customer->orders;\n}\n```\n\nПосредством вызова метода [[yii\\db\\ActiveQuery::with()]], вы указываете объекту Active Record вернуть заказы первых 100\nпокупателей с помощью одного SQL-запроса. В результате снижаете количество выполняемых SQL-запросов от 101 до 2!\n\nВы можете жадно загружать одну или несколько связей. Вы можете даже жадно загружать *вложенные связи*. Вложенная связь -\nэто связь, которая объявлена внутри связного Active Record класса. Например, `Customer` связан с `Order` посредством\nсвязи `orders`, а `Order` связан с `Item` посредством связи `items`. При формировании запроса для `Customer`, вы можете\nжадно загрузить `items`, используя нотацию вложенной связи `orders.items`. \n\nНиже представлен код, который показывает различные способы использования метода [[yii\\db\\ActiveQuery::with()|with()]].\nМы полагаем, что класс `Customer` имеет две связи: `orders` и `country` - в то время как класс `Order` имеет лишь одну\nсвязь `items`.\n\n```php\n// жадная загрузка \"orders\" и \"country\" одновременно\n$customers = Customer::find()->with('orders', 'country')->all();\n// аналог с использованием синтаксиса массива\n$customers = Customer::find()->with(['orders', 'country'])->all();\n// SQL-запрос не выполняется\n$orders= $customers[0]->orders;\n// SQL-запрос не выполняется\n$country = $customers[0]->country;\n\n// жадная загрузка связи \"orders\" и вложенной связи \"orders.items\"\n$customers = Customer::find()->with('orders.items')->all();\n// доступ к деталям первого заказа первого покупателя \n// SQL-запрос не выполняется\n$items = $customers[0]->orders[0]->items;\n```\n\nВы можете жадно загрузить более глубокие вложенные связи, такие как `a.b.c.d`. Все родительские связи будут жадно\nзагружены. Таким образом, когда вы вызываете метод [[yii\\db\\ActiveQuery::with()|with()]] с параметром `a.b.c.d`, вы\nжадно загрузите связи `a`, `a.b`, `a.b.c` и `a.b.c.d`.\n\n> Info: В целом, когда жадно загружается `N` связей, среди которых `M` связей объявлено с помощью\n  [промежуточной таблицы](#junction-table), суммарное количество выполняемых SQL-запросов будет равно `N+M+1`. Заметьте,\n  что вложенная связь `a.b.c.d` насчитывает 4 связи.\n\nКогда связь жадно загружается, вы можете настроить соответствующий запрос получения связных данных с использованием\nанонимной функции. Например:\n\n```php\n// найти покупателей и получить их вместе с их странами и активными заказами\n// SELECT * FROM `customer`\n// SELECT * FROM `country` WHERE `id` IN (...)\n// SELECT * FROM `order` WHERE `customer_id` IN (...) AND `status` = 1\n$customers = Customer::find()->with([\n    'country',\n    'orders' => function ($query) {\n        $query->andWhere(['status' => Order::STATUS_ACTIVE]);\n    },\n])->all();\n```\n\nКогда настраивается запрос на получение связных данных для какой-либо связи, вы можете указать название связи в виде\nключа массива и использовать анонимную функцию в качестве соответствующего значения этого массива. Анонимная функция\nполучит параметр `$query`, который представляет собой объект [[yii\\db\\ActiveQuery]], используемый для выполнения запроса\nна получение связных данных для данной связи. В вышеприведённом примере кода мы изменили запрос на получение связных\nданных, наложив на него дополнительное условие выборки статуса заказов.\n\n> Note: Если вы вызываете метод [[yii\\db\\Query::select()|select()]] в процессе жадной загрузки связей, вы должны\n> убедиться, что будут выбраны столбцы, участвующие в объявлении связей. Иначе связные модели будут загружены\n> неправильно. Например:\n>\n> ```php\n> $orders = Order::find()->select(['id', 'amount'])->with('customer')->all();\n> // $orders[0]->customer всегда равно null. Для исправления проблемы вы должны сделать следующее:\n> $orders = Order::find()->select(['id', 'amount', 'customer_id'])->with('customer')->all();\n> ```\n\n\n### Использование JOIN со связями <span id=\"joining-with-relations\"></span>\n\n> Note: Материал этого раздела применим только к реляционным базам данных, таким как MySQL, PostgreSQL, и т.д.\n\nЗапросы на получение связных данных, которые мы рассмотрели выше, ссылаются только на столбцы основной таблицы при\nизвлечении основной информации. На самом же деле нам часто нужно ссылаться в запросах на столбцы связных таблиц.\nНапример, мы можем захотеть получить покупателей, для которых имеется хотя бы один активный заказ. Для решения этой\nпроблемы мы можем построить запрос с использованием JOIN как показано ниже:\n\n```php\n// SELECT `customer`.* FROM `customer`\n// LEFT JOIN `order` ON `order`.`customer_id` = `customer`.`id`\n// WHERE `order`.`status` = 1\n// \n// SELECT * FROM `order` WHERE `customer_id` IN (...)\n$customers = Customer::find()\n    ->select('customer.*')\n    ->leftJoin('order', '`order`.`customer_id` = `customer`.`id`')\n    ->where(['order.status' => Order::STATUS_ACTIVE])\n    ->with('orders')\n    ->all();\n```\n\n> Note: Важно однозначно указывать в SQL-выражениях имена столбцов при построении запросов на получение связных\n  данных с участием оператора JOIN. Наиболее распространённая практика - предварять названия столбцов с помощью имён\n  соответствующих им таблиц.\n\nОднако лучшим подходом является использование имеющихся объявлений связей с помощью вызова метода\n[[yii\\db\\ActiveQuery::joinWith()]]:\n\n```php\n$customers = Customer::find()\n    ->joinWith('orders')\n    ->where(['order.status' => Order::STATUS_ACTIVE])\n    ->all();\n```\n\nОба подхода выполняют одинаковый набор SQL-запросов. Однако второй подход более прозрачен и прост.\n\nПо умолчанию, метод [[yii\\db\\ActiveQuery::joinWith()|joinWith()]] будет использовать конструкцию `LEFT JOIN` для\nобъединения основной таблицы со связной. Вы можете указать другой тип операции JOIN (например, `RIGHT JOIN`) с помощью\nтретьего параметра этого метода - `$joinType`. Если вам нужен `INNER JOIN`, вы можете вместо этого просто вызвать\nметод [[yii\\db\\ActiveQuery::innerJoinWith()|innerJoinWith()]].\n\nВызов метода [[yii\\db\\ActiveQuery::joinWith()|joinWith()]] будет [жадно загружать](#lazy-eager-loading) связные данные\nпо умолчанию. Если вы не хотите получать связные данные, вы можете передать во втором параметре `$eagerLoading` значение\n`false`. \n\nПодобно методу [[yii\\db\\ActiveQuery::with()|with()]] вы можете объединять данные с одной или несколькими связями; вы \nможете настроить запрос на получение связных данных \"на лету\"; вы можете объединять данные с вложенными связями; вы\nможете смешивать использование метода [[yii\\db\\ActiveQuery::with()|with()]] и метода \n[[yii\\db\\ActiveQuery::joinWith()|joinWith()]]. Например:\n\n```php\n$customers = Customer::find()->joinWith([\n    'orders' => function ($query) {\n        $query->andWhere(['>', 'subtotal', 100]);\n    },\n])->with('country')\n    ->all();\n```\n\nИногда во время объединения двух таблиц вам может потребоваться указать некоторые дополнительные условия рядом с\nоператором `ON` во время выполнения JOIN-запроса. Это можно сделать с помощью вызова метода \n[[yii\\db\\ActiveQuery::onCondition()]] как показано ниже:\n\n```php\n// SELECT `customer`.* FROM `customer`\n// LEFT JOIN `order` ON `order`.`customer_id` = `customer`.`id` AND `order`.`status` = 1 \n// \n// SELECT * FROM `order` WHERE `customer_id` IN (...)\n$customers = Customer::find()->joinWith([\n    'orders' => function ($query) {\n        $query->onCondition(['order.status' => Order::STATUS_ACTIVE]);\n    },\n])->all();\n```\n\nВышеприведённый запрос вернёт *всех* покупателей и для каждого покупателя вернёт все активные заказы. Заметьте, что это\nповедение отличается от нашего предыдущего примера, в котором возвращались только покупатели, у которых был как минимум\nодин активный заказ.\n\n> Info: Когда в объекте [[yii\\db\\ActiveQuery]] указано условие выборки с помощью метода\n  [[yii\\db\\ActiveQuery::onCondition()|onCondition()]], это условие будет размещено в конструкции `ON`, если запрос\n  содержит оператор JOIN. Если же запрос не содержит оператор JOIN, такое условие будет автоматически размещено в\n  конструкции `WHERE`.\n  \n#### Псевдонимы связанных таблиц <span id=\"relation-table-aliases\"></span>\n\nКак уже было отмечено, при использовании в запросе JOIN-ов, приходится явно решать конфликты имён. Поэтому часто таблицам\nдают псевдонимы. Задать псевдоним для реляционного запроса можно следующим образом:\n\n```php\n$query->joinWith([\n  'orders' => function ($q) {\n      $q->from(['o' => Order::tableName()]);\n  },\n])\n```\n\nВыглядит это довольно сложно. Либо приходится задавать явно имена таблиц, либо вызывать `Order::tableName()`.\nНачиная с версии 2.0.7 вы можете задать и использовать псевдоним для связанной таблицы следующим образом:\n\n```php\n// join the orders relation and sort the result by orders.id\n$query->joinWith(['orders o'])->orderBy('o.id');\n```\n\nЭтот синтаксис работает для простых связей. Если необходимо использовать связующую таблицу, например \n`$query->joinWith(['orders.product'])`, то вызовы joinWith вкладываются друг в друга:\n\n```php\n$query->joinWith(['orders o' => function($q) {\n      $q->joinWith('product p');\n  }])\n  ->where('o.amount > 100');\n```\n\n\n### Обратные связи <span id=\"inverse-relations\"></span>\n\nОбъявления связей часто взаимны между двумя Active Record классами. Например, `Customer` связан с `Order` посредством\nсвязи `orders`, а `Order` взаимно связан с `Customer` посредством связи `customer`.\n\n```php\nclass Customer extends ActiveRecord\n{\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n}\n\nclass Order extends ActiveRecord\n{\n    public function getCustomer()\n    {\n        return $this->hasOne(Customer::class, ['id' => 'customer_id']);\n    }\n}\n```\n\nТеперь рассмотрим следующий участок кода:\n\n```php\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123\n$order = $customer->orders[0];\n\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer2 = $order->customer;\n\n// выведет \"not the same\"\necho $customer2 === $customer ? 'same' : 'not the same';\n```\n\nМы думали, что `$customer` и `$customer2` эквивалентны, но оказалось, что нет! Фактически они содержат одинаковые\nданные, но являются разными объектами. Когда мы получаем доступ к данным посредством `$order->customer`, выполняется\nдополнительный SQL-запрос для заполнения нового объекта `$customer2`.\n\nЧтобы избежать избыточного выполнения последнего SQL-запроса в вышеприведённом примере, мы должны подсказать Yii, что\n`customer` - *обратная связь* относительно `orders`, и сделаем это с помощью вызова метода\n[[yii\\db\\ActiveQuery::inverseOf()|inverseOf()]] как показано ниже:\n\n```php\nclass Customer extends ActiveRecord\n{\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id'])->inverseOf('customer');\n    }\n}\n```\n\nТеперь, после этих изменений в объявлении связи, получим:\n\n```php\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123\n$order = $customer->orders[0];\n\n// SQL-запрос не выполняется\n$customer2 = $order->customer;\n\n// выведет \"same\"\necho $customer2 === $customer ? 'same' : 'not the same';\n```\n\n> Note: обратные связи не могут быть объявлены для связей, использующих [промежуточную таблицу](#junction-table).\n  То есть, если связь объявлена с помощью методов [[yii\\db\\ActiveQuery::via()|via()]] или\n  [[yii\\db\\ActiveQuery::viaTable()|viaTable()]], вы не должны вызывать после этого метод\n  [[yii\\db\\ActiveQuery::inverseOf()|inverseOf()]].\n\n\n## Сохранение связных данных <span id=\"saving-relations\"></span>\n\nВо время работы со связными данными вам часто требуется установить связи между двумя разными видами данных или удалить\nсуществующие связи. Это требует установки правильных значений для столбцов, с помощью которых заданы связи. При\nиспользовании Active Record вам может понадобится завершить участок кода следующим образом:\n\n```php\n$customer = Customer::findOne(123);\n$order = new Order();\n$order->subtotal = 100;\n// ...\n\n// установка атрибута, которой задаёт связь \"customer\" в объекте Order\n$order->customer_id = $customer->id;\n$order->save();\n```\n\nActive Record предоставляет метод [[yii\\db\\ActiveRecord::link()|link()]], который позволяет выполнить эту задачу\nболее красивым способом:\n\n```php\n$customer = Customer::findOne(123);\n$order = new Order();\n$order->subtotal = 100;\n// ...\n\n$order->link('customer', $customer);\n```\n\nМетод [[yii\\db\\ActiveRecord::link()|link()]] требует указать название связи и целевой объект Active Record, с которым\nдолжна быть установлена связь. Метод изменит значения атрибутов, которые связывают два объекта Active Record, и сохранит\nих в базу данных. В вышеприведённом примере, метод присвоит атрибуту `customer_id` объекта `Order` значение атрибута\n`id` объекта `Customer` и затем сохранит его в базу данных.\n\n> Note: Невозможно связать два свежесозданных объекта Active Record.\n\nПреимущество метода [[yii\\db\\ActiveRecord::link()|link()]] становится ещё более очевидным, когда связь объявлена\nпосредством [промежуточной таблицы](#junction-table). Например, вы можете использовать следующий код, чтобы связать\nобъект `Order` с объектом `Item`:\n\n```php\n$order->link('items', $item);\n```\n\nВышеприведённый код автоматически вставит строку данных в промежуточную таблицу `order_item`, чтобы связать объект \n`order` с объектом `item`.\n\n> Info: Метод [[yii\\db\\ActiveRecord::link()|link()]] не осуществляет какую-либо валидацию данных во время\n  сохранения целевого объекта Active Record. На вас лежит ответственность за валидацию любых введённых данных перед\n  вызовом этого метода.\n\nСуществует противоположная операция для [[yii\\db\\ActiveRecord::link()|link()]] - это операция\n[[yii\\db\\ActiveRecord::unlink()|unlink()]], она снимает существующую связь с двух объектов Active Record. Например:\n\n```php\n$customer = Customer::find()->with('orders')->where(['id' => 123])->one();\n$customer->unlink('orders', $customer->orders[0]);\n```\n\nПо умолчанию метод [[yii\\db\\ActiveRecord::unlink()|unlink()]] задаст вторичному ключу (или ключам), который определяет\nсуществующую связь, значение `null`. Однако вы можете запросить удаление строки таблицы, которая содержит значение\nвторичного ключа, передав значение `true` в параметре `$delete` для этого метода.\n \nЕсли связь построена на основе промежуточной таблицы, вызов метода [[yii\\db\\ActiveRecord::unlink()|unlink()]] инициирует\nочистку вторичных ключей в промежуточной таблице, или же удаление соответствующей строки данных в промежуточной таблице,\nесли параметр `$delete` равен `true`.\n\n\n## Связывание объектов из разных баз данных <span id=\"cross-database-relations\"></span> \n\nActive Record позволяет вам объявить связи между классами Active Record, которые относятся к разным базам данных. Базы\nданных могут быть разных типов (например, MySQL и PostgreSQL или MS SQL и MongoDB), и они могут быть запущены на разных\nсерверах. Вы можете использовать тот же самый синтаксис для осуществления запросов выборки связных данных. Например:\n\n```php\n// Объект Customer соответствует таблице \"customer\" в реляционной базе данных (например MySQL)\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public static function tableName()\n    {\n        return 'customer';\n    }\n\n    public function getComments()\n    {\n        // у покупателя может быть много комментариев\n        return $this->hasMany(Comment::class, ['customer_id' => 'id']);\n    }\n}\n\n// Объект Comment соответствует коллекции \"comment\" в базе данных MongoDB\nclass Comment extends \\yii\\mongodb\\ActiveRecord\n{\n    public static function collectionName()\n    {\n        return 'comment';\n    }\n\n    public function getCustomer()\n    {\n        // комментарий принадлежит одному покупателю\n        return $this->hasOne(Customer::class, ['id' => 'customer_id']);\n    }\n}\n\n$customers = Customer::find()->with('comments')->all();\n```\n\nВы можете использовать большую часть возможностей запросов получения связных данных, которые были описаны в этой главе.\n\n> Note: Применимость метода [[yii\\db\\ActiveQuery::joinWith()|joinWith()]] ограничена базами данных, которые\n  позволяют выполнять запросы между разными базами с использованием оператора JOIN. По этой причине вы не можете\n  использовать этот метод в вышеприведённом примере, т.к. MongoDB не поддерживает операцию JOIN.\n\n\n## Тонкая настройка классов Query <span id=\"customizing-query-classes\"></span>\n\nПо умолчанию все запросы данных для Active Record поддерживаются с помощью класса [[yii\\db\\ActiveQuery]]. Для\nиспользования собственного класса запроса вам необходимо переопределить метод [[yii\\db\\ActiveRecord::find()]] и\nвозвращать из него объект вашего собственного класса запроса. Например:\n \n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\db\\ActiveQuery;\n\nclass Comment extends ActiveRecord\n{\n    public static function find()\n    {\n        return new CommentQuery(get_called_class());\n    }\n}\n\nclass CommentQuery extends ActiveQuery\n{\n    // ...\n}\n```\n\nТеперь, когда вы будете осуществлять получение данных (например, выполните `find()`, `findOne()`) или объявите связь\n(например, `hasOne()`) с объектом `Comment`, вы будете работать с объектом класса `CommentQuery` вместо `ActiveQuery`.\n\n> Tip: В больших проектах рекомендуется использовать собственные классы запросов, которые будут содержать в себе\n  большую часть кода, связанного с настройкой запросов, таким образом классы Active Record удастся сохранить более\n  чистыми.\n  \nВы можете настроить класс запроса большим количеством различных способов для улучшения методик построения запросов.\nНапример, можете объявить новые методы построения запросов в собственном классе запросов:\n\n```php\nclass CommentQuery extends ActiveQuery\n{\n    public function active($state = true)\n    {\n        return $this->andWhere(['active' => $state]);\n    }\n}\n```\n\n> Note: Вместо вызова метода [[yii\\db\\ActiveQuery::where()|where()]] старайтесь во время объявления новых методов\n  построения запросов использовать [[yii\\db\\ActiveQuery::andWhere()|andWhere()]] или\n  [[yii\\db\\ActiveQuery::orWhere()|orWhere()]] для добавления дополнительных условий, в этом случае уже заданные условия\n  выборок не будут перезаписаны.\n\nЭто позволит вам писать код построения запросов как показано ниже:\n \n```php\n$comments = Comment::find()->active()->all();\n$inactiveComments = Comment::find()->active(false)->all();\n```\n\nВы также можете использовать новые методы построения запросов, когда объявляете связи для класса `Comment` или\nосуществляете запрос для выборки связных данных:\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public function getActiveComments()\n    {\n        return $this->hasMany(Comment::class, ['customer_id' => 'id'])->active();\n    }\n}\n\n$customers = Customer::find()->with('activeComments')->all();\n\n// или по-другому:\n \n$customers = Customer::find()->with([\n    'comments' => function($q) {\n        $q->active();\n    }\n])->all();\n```\n\n> Info: В Yii версии 1.1 была концепция с названием *scope*. Она больше не поддерживается в Yii версии 2.0, и вы\n  можете использовать собственные классы запросов и собственные методы построения запросов, чтобы добиться той же самой\n  цели.\n  \n\n## Получение дополнительных атрибутов\n\nКогда объект Active Record заполнен результатами запроса, его атрибуты заполнены значениями соответствующих столбцов\nиз полученного набора данных.\n\nВы можете получить дополнительные столбцы или значения с помощью запроса и сохранить их внутри объекта Active Record.\nНапример, предположим, что у нас есть таблица 'room', которая содержит информацию о доступных в отеле комнатах. Каждая\nкомната хранит информацию о её геометрических размерах с помощью атрибутов 'length', 'width', 'height'. Представьте, что\nвам требуется получить список всех доступных комнат, отсортированных по их объёму в порядке убывания. В этом случае вы\nне можете вычислять объём с помощью PHP, потому что нам требуется сортировать записи по объёму, но вы также хотите\nотображать объем в списке. Для достижения этой цели, вам необходимо объявить дополнительный атрибут в вашем Active\nRecord классе 'Room', который будет хранить значение 'volume':\n\n```php\nclass Room extends \\yii\\db\\ActiveRecord\n{\n    public $volume;\n\n    // ...\n}\n```\n\nДалее вам необходимо составить запрос, который вычисляет объём комнаты и выполняет сортировку:\n\n```php\n$rooms = Room::find()\n    ->select([\n        '{{room}}.*', // получить все столбцы\n        '([[length]] * [[width]] * [[height]]) AS volume', // вычислить объём\n    ])\n    ->orderBy('volume DESC') // отсортировать\n    ->all();\n\nforeach ($rooms as $room) {\n    echo $room->volume; // содержит значение, вычисленное с помощью SQL-запроса\n}\n```\n\nВозможность выбирать дополнительные атрибуты может быть особенно полезной для агрегирующих запросов. Представьте, что\nвам необходимо отображать список покупателей с количеством их заказов. Прежде всего вам потребуется объявить класс \n`Customer` со связью 'orders' и дополнительным атрибутом для хранения расчётов:\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public $ordersCount;\n\n    // ...\n\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n}\n```\n\nПосле этого вы сможете составить запрос, который объединяет заказы и вычисляет их количество:\n\n```php\n$customers = Customer::find()\n    ->select([\n        '{{customer}}.*', // получить все атрибуты покупателя\n        'COUNT({{order}}.id) AS ordersCount' // вычислить количество заказов\n    ])\n    ->joinWith('orders') // обеспечить построение промежуточной таблицы\n    ->groupBy('{{customer}}.id') // сгруппировать результаты, чтобы заставить агрегацию работать\n    ->all();\n```\n\nНедостаток этого подхода заключается в том, что если данные для поля не загружены по результатам SQL запроса, то они\nдолжны быть вычисленны отдельно. Это означает, что запись, полученная посредством обычного запроса без дополнительных полей в\nразделе 'select', не может вернуть реальное значения для дополнительного поля. Это же касается и только что сохранненой\nзаписи.\n\n```php\n$room = new Room();\n$room->length = 100;\n$room->width = 50;\n$room->height = 2;\n\n$room->volume; // значение будет равно `null`, т.к. поле не было заполнено\n```\n\nИспользование магических методов [[yii\\db\\BaseActiveRecord::__get()|__get()]] и [[yii\\db\\BaseActiveRecord::__set()|__set()]]\nпозволяет эмулировать поведение обычного поля:\n\n```php\nclass Room extends \\yii\\db\\ActiveRecord\n{\n    private $_volume;\n\n    public function setVolume($volume)\n    {\n        $this->_volume = (float) $volume;\n    }\n\n    public function getVolume()\n    {\n        if (empty($this->length) || empty($this->width) || empty($this->height)) {\n            return null;\n        }\n\n        if ($this->_volume === null) {\n            $this->setVolume(\n                $this->length * $this->width * $this->height\n            );\n        }\n\n        return $this->_volume;\n    }\n\n    // ...\n}\n```\n\nЕсли результат запроса на выборку данных не содержит поле 'volume', то модель сможет расчитать его автоматически\nиспользуя имеющиеся атрибуты.\n\nВы также можете вычислять агрегируемые поля используя объявленные отношения:\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    private $_ordersCount;\n\n    public function setOrdersCount($count)\n    {\n        $this->_ordersCount = (int) $count;\n    }\n\n    public function getOrdersCount()\n    {\n        if ($this->isNewRecord) {\n            return null; // нет смысла выполнять запрос на поиск по пустым ключам\n        }\n\n        if ($this->_ordersCount === null) {\n            $this->setOrdersCount($this->getOrders()->count()); // вычисляем агрегацию по требованию из отношения\n        }\n\n        return $this->_ordersCount;\n    }\n\n    // ...\n\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n}\n```\n\nПри такой реализации, в случае когда 'ordersCount' присутсвует в разделе 'select' - значение 'Customer::ordersCount' будет\nзаполнено из результатов запроса, в противном случае - оно будет вычислено по первому требованию на основании отношения `Customer::orders`.\n\nЭтот подход также можно использовать для быстрого доступа к некоторым данным отношений, в особенности для агрегации.\nНапример:\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    /**\n     * Объявляет виртуальное свойство для агрегируемых данных, доступное только на чтение.\n     */\n    public function getOrdersCount()\n    {\n        if ($this->isNewRecord) {\n            return null; // нет смысла выполнять запрос на поиск по пустым ключам\n        }\n\n        return $this->ordersAggregation[0]['counted'];\n    }\n\n    /**\n     * Объявляет обычное отношение 'orders'.\n     */\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n\n    /**\n     * Объявляет новое отношение, основанное на 'orders', которое предоставляет агрегацию.\n     */\n    public function getOrdersAggregation()\n    {\n        return $this->getOrders()\n            ->select(['customer_id', 'counted' => 'count(*)'])\n            ->groupBy('customer_id')\n            ->asArray(true);\n    }\n\n    // ...\n}\n\nforeach (Customer::find()->with('ordersAggregation')->all() as $customer) {\n    echo $customer->ordersCount; // выводит агрегируемые данные из отношения без дополнительного запроса благодаря жадной загрузке\n}\n\n$customer = Customer::findOne($pk);\n$customer->ordersCount; // выводит агрегируемые данные отношения через ленивую загрузку\n```\n"
  },
  {
    "path": "docs/guide-ru/db-dao.md",
    "content": "Объекты доступа к данным (DAO)\n==============================\n\nПостроенные поверх [PDO](https://www.php.net/manual/ru/book.pdo.php), Yii DAO (объекты доступа к данным) обеспечивают\nобъектно-ориентированный API для доступа к реляционным базам данных. Это основа для других, более продвинутых, методов\nдоступа к базам данных, включая [построитель запросов](db-query-builder.md) и [active record](db-active-record.md).\n\nПри использовании Yii DAO вы в основном будете использовать чистый SQL и массивы PHP. Как результат, это самый\nэффективный способ доступа к базам данных. Тем не менее, так как синтаксис SQL может отличаться для разных баз данных,\nиспользуя Yii DAO вам нужно будет приложить дополнительные усилия, чтобы сделать приложение не зависящим от конкретной\nбазы данных.\n\nYii DAO из коробки поддерживает следующие базы данных:\n\n- [MySQL](https://www.mysql.com/)\n- [MariaDB](https://mariadb.com/)\n- [SQLite](https://sqlite.org/)\n- [PostgreSQL](https://www.postgresql.org/): версии 8.4 или выше.\n- [CUBRID](https://www.cubrid.org/): версии 9.3 или выше.\n- [Oracle](https://www.oracle.com/database/)\n- [MSSQL](https://www.microsoft.com/en-us/sqlserver/default.aspx): версии 2008 или выше.\n\n\n> Note: Новая версия pdo_oci для PHP 7 на данный момент существует только в форме исходного кода. Используйте\n  [инструкции сообщества по компиляции](https://github.com/yiisoft/yii2/issues/10975#issuecomment-248479268).\n\n## Создание подключения к базе данных <span id=\"creating-db-connections\"></span>\n\nДля доступа к базе данных, вы сначала должны подключится к ней, создав экземпляр класса [[yii\\db\\Connection]]:\n\n```php\n$db = new yii\\db\\Connection([\n    'dsn' => 'mysql:host=localhost;dbname=example',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n]);\n```\n\nТак как подключение к БД часто нужно в нескольких местах, распространённой практикой является его настройка как\n[компонента приложения](structure-application-components.md):\n\n```php\nreturn [\n    // ...\n    'components' => [\n        // ...\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=example',\n            'username' => 'root',\n            'password' => '',\n            'charset' => 'utf8',\n        ],\n    ],\n    // ...\n];\n```\n\nТеперь вы можете получить доступ к подключению к БД с помощью выражения `Yii::$app->db`.\n\n> Tip: Вы можете настроить несколько компонентов подключения, если в вашем приложении используется несколько баз данных.\n\nПри настройке подключения, вы должны обязательно указывать Имя Источника Данных (DSN) через параметр [[yii\\db\\Connection::dsn|dsn]].\nФормат DSN отличается для разных баз данных. Дополнительное описание смотрите в [справочнике PHP](https://www.php.net/manual/ru/pdo.construct.php).\nНиже представлены несколько примеров:\n \n* MySQL, MariaDB: `mysql:host=localhost;dbname=mydatabase`\n* SQLite: `sqlite:/path/to/database/file`\n* PostgreSQL: `pgsql:host=localhost;port=5432;dbname=mydatabase`\n* CUBRID: `cubrid:dbname=demodb;host=localhost;port=33000`\n* MS SQL Server (via sqlsrv driver): `sqlsrv:Server=localhost;Database=mydatabase`\n* MS SQL Server (via dblib driver): `dblib:host=localhost;dbname=mydatabase`\n* MS SQL Server (via mssql driver): `mssql:host=localhost;dbname=mydatabase`\n* Oracle: `oci:dbname=//localhost:1521/mydatabase`\n\nЗаметьте, что если вы подключаетесь к базе данных через ODBC, вам необходимо указать свойство [[yii\\db\\Connection::driverName]],\nчтобы Yii знал какой тип базы данных используется. Например,\n\n```php\n'db' => [\n    'class' => 'yii\\db\\Connection',\n    'driverName' => 'mysql',\n    'dsn' => 'odbc:Driver={MySQL};Server=localhost;Database=test',\n    'username' => 'root',\n    'password' => '',\n],\n```\n\nКроме свойства [[yii\\db\\Connection::dsn|dsn]], вам необходимо указать [[yii\\db\\Connection::username|username]]\nи [[yii\\db\\Connection::password|password]]. Смотрите [[yii\\db\\Connection]] для того, чтоб посмотреть полный список свойств. \n\n> Info: При создании экземпляра соединения к БД, фактическое соединение с базой данных будет установлено только\n  при выполнении первого SQL запроса или при явном вызове метода [[yii\\db\\Connection::open()|open()]].\n\n> Tip: Иногда может потребоваться выполнить некоторые запросы сразу после соединения с базой данных, для инициализации\n> переменных окружения. Например, чтобы задать часовой пояс или кодировку. Сделать это можно зарегистрировав обработчик\n> для события [[yii\\db\\Connection::EVENT_AFTER_OPEN|afterOpen]] в конфигурации приложения:\n> \n> ```php\n> 'db' => [\n>     // ...\n>     'on afterOpen' => function($event) {\n>         // $event->sender содержит соединение с базой данных\n>         $event->sender->createCommand(\"SET time_zone = 'UTC'\")->execute();\n>     }\n> ]\n> ```\n\n## Выполнение SQL запросов <span id=\"executing-sql-queries\"></span>\n\nПосле создания экземпляра соединения, вы можете выполнить SQL запрос, выполнив следующие шаги:\n \n1. Создать [[yii\\db\\Command]] из запроса SQL;\n2. Привязать параметры (не обязательно);\n3. Вызвать один из методов выполнения SQL из [[yii\\db\\Command]].\n\nСледующий пример показывает различные способы получения данных из базы дынных:\n \n```php\n// возвращает набор строк. каждая строка - это ассоциативный массив с именами столбцов и значений.\n// если выборка ничего не вернёт, то будет получен пустой массив.\n$posts = Yii::$app->db->createCommand('SELECT * FROM post')\n            ->queryAll();\n\n// вернёт одну строку (первую строку)\n// false, если ничего не будет выбрано\n$post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=1')\n           ->queryOne();\n\n// вернёт один столбец (первый столбец)\n// пустой массив, при отсутствии результата\n$titles = Yii::$app->db->createCommand('SELECT title FROM post')\n             ->queryColumn();\n\n// вернёт скалярное значение\n// или false, при отсутствии результата\n$count = Yii::$app->db->createCommand('SELECT COUNT(*) FROM post')\n             ->queryScalar();\n```\n\n> Note: Чтобы сохранить точность, данные извлекаются как строки, даже если тип поля в базе данных является числовым.\n\n### Привязка параметров <span id=\"binding-parameters\"></span>\n\nПри создании команды из SQL запроса с параметрами, вы почти всегда должны использовать привязку параметров для\nпредотвращения атак через SQL инъекции. Например,\n\n```php\n$post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status')\n           ->bindValue(':id', $_GET['id'])\n           ->bindValue(':status', 1)\n           ->queryOne();\n```\n\nВ SQL запрос, вы можете встраивать один или несколько маркеров (например `:id` в примере выше). Маркеры должны быть \nстрокой, начинающейся с двоеточия. Далее вам нужно вызвать один из следующих методов для привязки значений к параметрам:\n\n* [[yii\\db\\Command::bindValue()|bindValue()]]: привязка одного параметра по значению \n* [[yii\\db\\Command::bindValues()|bindValues()]]: привязка нескольких параметров в одном вызове\n* [[yii\\db\\Command::bindParam()|bindParam()]]: похоже на [[yii\\db\\Command::bindValue()|bindValue()]], но привязка\n  происходит по ссылке.\n\nСледующий пример показывает альтернативный путь привязки параметров:\n\n```php\n$params = [':id' => $_GET['id'], ':status' => 1];\n\n$post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status')\n           ->bindValues($params)\n           ->queryOne();\n           \n$post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status', $params)\n           ->queryOne();\n```\n\nПривязка переменных реализована через [подготавливаемые запросы](https://www.php.net/manual/ru/mysqli.quickstart.prepared-statements.php).\nПомимо предотвращения атак путём SQL инъекций, это увеличивает производительность, так как запрос подготавливается\nодин раз, а потом выполняется много раз с разными параметрами. Например,\n\n```php\n$command = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id');\n\n$post1 = $command->bindValue(':id', 1)->queryOne();\n$post2 = $command->bindValue(':id', 2)->queryOne();\n// ...\n```\n\nТак как [[yii\\db\\Command::bindParam()|bindParam()]] поддерживает привязку параметров по ссылке, следующий код может\nбыть написан следующим образом:\n\n```php\n$command = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id')\n              ->bindParam(':id', $id);\n\n$id = 1;\n$post1 = $command->queryOne();\n\n$id = 2;\n$post2 = $command->queryOne();\n// ...\n```\n\nОбратите внимание что вы связываете маркер `$id` с переменной перед выполнением запроса, и затем меняете это значение\nперед каждым последующим выполнением (часто это делается в цикле). Выполнение запросов таким образом может быть значительно\nболее эффективным, чем выполнение запроса для каждого значения параметра.\n\n### Выполнение Не-SELECT запросов <span id=\"non-select-queries\"></span>\n\nВ методах `queryXyz()`, описанных в предыдущих разделах, вызываются SELECT запросы для извлечения данных из базы.\nДля запросов не возвращающих данные, вы должны использовать метод [[yii\\db\\Command::execute()]]. Например,\n\n```php\nYii::$app->db->createCommand('UPDATE post SET status=1 WHERE id=1')\n   ->execute();\n```\n\nМетод [[yii\\db\\Command::execute()]] возвращает количество строк обработанных SQL запросом.\n\nДля запросов INSERT, UPDATE и DELETE, вместо написания чистого SQL, вы можете вызвать методы [[yii\\db\\Command::insert()|insert()]],\n[[yii\\db\\Command::update()|update()]], [[yii\\db\\Command::delete()|delete()]], соответственно, для создания указанных\nSQL конструкций. Например,\n\n```php\n// INSERT (table name, column values)\nYii::$app->db->createCommand()->insert('user', [\n    'name' => 'Sam',\n    'age' => 30,\n])->execute();\n\n// UPDATE (table name, column values, condition)\nYii::$app->db->createCommand()->update('user', ['status' => 1], 'age > 30')->execute();\n\n// DELETE (table name, condition)\nYii::$app->db->createCommand()->delete('user', 'status = 0')->execute();\n```\n\nВы можете также вызвать [[yii\\db\\Command::batchInsert()|batchInsert()]] для вставки множества строк за один вызов.\nЭто более эффективно чем вставлять записи по одной за раз:\n\n```php\n// table name, column names, column values\nYii::$app->db->createCommand()->batchInsert('user', ['name', 'age'], [\n    ['Tom', 30],\n    ['Jane', 20],\n    ['Linda', 25],\n])->execute();\n```\n\nОбратите внимание, что перечисленные методы лишь создают запрос. Чтобы его выполнить нужно вызывать\n[[yii\\db\\Command::execute()|execute()]].\n\n\n## Экранирование имён таблиц и столбцов <span id=\"quoting-table-and-column-names\"></span>\n\nПри написании независимого от базы данных кода, правильно экранировать имена таблиц и столбцов довольно трудно, так как\nв разных базах данных правила экранирования разные. Чтоб преодолеть данную проблему вы можете использовать следующий\nсинтаксис экранирования используемый в Yii:\n\n* `[[column name]]`: заключайте имя столбца в двойные квадратные скобки; \n* `{{table name}}`: заключайте имя таблицы в двойные фигурные скобки.\n\nYii DAO будет автоматически преобразовывать подобные конструкции в SQL в правильно экранированные имена таблиц и столбцов.\nНапример,\n\n```php\n// executes this SQL for MySQL: SELECT COUNT(`id`) FROM `employee`\n$count = Yii::$app->db->createCommand(\"SELECT COUNT([[id]]) FROM {{employee}}\")\n            ->queryScalar();\n```\n\n\n### Использование префиксов таблиц <span id=\"using-table-prefix\"></span>\n\nЕсли большинство ваших таблиц использует общий префикс в имени, вы можете использовать свойство Yii DAO для указания префикса.\n\nСначала, укажите префикс таблиц через свойство [[yii\\db\\Connection::tablePrefix]]:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        // ...\n        'db' => [\n            // ...\n            'tablePrefix' => 'tbl_',\n        ],\n    ],\n];\n```\n\nЗатем в коде, когда вам нужно ссылаться на таблицу, имя которой содержит такой префикс, используйте синтаксис `{{%table name}}`.\nСимвол процента будет автоматически заменён на префикс таблицы, который вы указали во время конфигурации соединения с\nбазой данных. Например,\n\n```php\n// для MySQL будет выполнен следующий SQL: SELECT COUNT(`id`) FROM `tbl_employee`\n$count = Yii::$app->db->createCommand(\"SELECT COUNT([[id]]) FROM {{%employee}}\")\n            ->queryScalar();\n```\n\n\n## Исполнение транзакций <span id=\"performing-transactions\"></span>\n\nКогда вы выполняете несколько зависимых запросов последовательно, вам может потребоваться обернуть их в транзакцию\nдля обеспечения целостности вашей базы данных. Если в любом из запросов произойдёт ошибка, база данных откатится на\nсостояние, которое было до выполнения запросов.\n\nСледующий код показывает типичное использование транзакций:\n\n```php\nYii::$app->db->transaction(function($db) {\n    $db->createCommand($sql1)->execute();\n    $db->createCommand($sql2)->execute();\n    // ... executing other SQL statements ...\n});\n```\n\nКод выше эквивалентен приведённому ниже. Разница в том, что в данном случае мы получаем больше контроля над обработкой\nошибок:\n\n```php\n$db = Yii::$app->db;\n$transaction = $db->beginTransaction();\n\ntry {\n    $db->createCommand($sql1)->execute();\n    $db->createCommand($sql2)->execute();\n    // ... executing other SQL statements ...\n    \n    $transaction->commit();\n} catch(\\Exception $e) {\n    $transaction->rollBack();\n    throw $e;\n} catch(\\Throwable $e) {\n    $transaction->rollBack();\n}\n```\n\n> Note: в коде выше ради совместимости с PHP 5.x и PHP 7.x использованы два блока catch. \n> `\\Exception` реализует интерфейс [`\\Throwable` interface](https://www.php.net/manual/ru/class.throwable.php)\n> начиная с PHP 7.0. Если вы используете только PHP 7 и новее, можете пропустить блок с `\\Exception`.\n\nПри вызове метода [[yii\\db\\Connection::beginTransaction()|beginTransaction()]], будет запущена новая транзакция.\nТранзакция представлена объектом [[yii\\db\\Transaction]] сохранённым в переменной `$transaction`. Потом, запросы будут\nвыполняться в блоке `try...catch...`. Если запросы будут выполнены удачно, будет выполнен метод [[yii\\db\\Transaction::commit()|commit()]].\nИначе, будет вызвано исключение, и будет вызван метод [[yii\\db\\Transaction::rollBack()|rollBack()]] для отката\nизменений сделанных до неудачно выполненного запроса внутри транзакции.\n\n### Указание уровня изоляции <span id=\"specifying-isolation-levels\"></span>\n\nYii поддерживает настройку [уровня изоляции] для ваших транзакций. По умолчанию, при старте транзакции, будет использован\nуровень изоляции настроенный в вашей базе данных. Вы можете переопределить уровень изоляции по умолчанию, как\nуказано ниже:\n\n```php\n$isolationLevel = \\yii\\db\\Transaction::REPEATABLE_READ;\n\nYii::$app->db->transaction(function ($db) {\n    ....\n}, $isolationLevel);\n \n// или\n\n$transaction = Yii::$app->db->beginTransaction($isolationLevel);\n```\n\nYii предоставляет четыре константы для наиболее распространённых уровней изоляции:\n\n- [[\\yii\\db\\Transaction::READ_UNCOMMITTED]] - низший уровень, «Грязное» чтение, не повторяющееся чтение и фантомное чтение.\n- [[\\yii\\db\\Transaction::READ_COMMITTED]] - предотвращает «Грязное» чтение.\n- [[\\yii\\db\\Transaction::REPEATABLE_READ]] - предотвращает «Грязное» чтение и не повторяющееся чтение.\n- [[\\yii\\db\\Transaction::SERIALIZABLE]] - высший уровень, предотвращает все вышеуказанные проблемы.\n\nПомимо использования приведённых выше констант для задания уровня изоляции, вы можете, также, использовать строки\nподдерживаемые вашей СУБД. Например, в PostgreSQL, вы можете использовать `SERIALIZABLE READ ONLY DEFERRABLE`.\n\nЗаметьте что некоторые СУБД допускают настраивать уровень изоляции только для всего соединения. Следующие транзакции\nбудут получать тот же уровень изоляции, даже если вы его не укажете. При использовании этой функции может потребоваться\nустановить уровень изоляции для всех транзакций, чтоб избежать явно конфликтующих настроек.\nНа момент написания этой статьи страдали от этого ограничения только MSSQL и SQLite.\n\n> Note: SQLite поддерживает только два уровня изоляции, таким образом вы можете использовать только\n`READ UNCOMMITTED` и `SERIALIZABLE`. Использование других уровней изоляции приведёт к генерации исключения.\n\n> Note: PostgreSQL не допускает установки уровня изоляции до старта транзакции, так что вы не сможете установить\nуровень изоляции прямо при старте транзакции. Вы можете использовать [[yii\\db\\Transaction::setIsolationLevel()]] в\nтаком случае после старта транзакции.\n\n[Уровни изоляции]: https://ru.wikipedia.org/wiki/%D0%A3%D1%80%D0%BE%D0%B2%D0%B5%D0%BD%D1%8C_%D0%B8%D0%B7%D0%BE%D0%BB%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D1%81%D1%82%D0%B8_%D1%82%D1%80%D0%B0%D0%BD%D0%B7%D0%B0%D0%BA%D1%86%D0%B8%D0%B9\n\n\n### Вложенные транзакции <span id=\"nesting-transactions\"></span>\n\nЕсли ваша СУБД поддерживает Savepoint, вы можете вкладывать транзакции как показано ниже:\n\n```php\nYii::$app->db->transaction(function ($db) {\n    // внешняя транзакция\n    \n    $db->transaction(function ($db) {\n        // внутренняя транзакция\n    });\n});\n```\n\nИли так,\n\n```php\n$db = Yii::$app->db;\n$outerTransaction = $db->beginTransaction();\ntry {\n    $db->createCommand($sql1)->execute();\n\n    $innerTransaction = $db->beginTransaction();\n    try {\n        $db->createCommand($sql2)->execute();\n        $innerTransaction->commit();\n    } catch (\\Exception $e) {\n        $innerTransaction->rollBack();\n    } catch (\\Throwable $e) {\n        $innerTransaction->rollBack();\n        throw $e;\n    }\n\n    $outerTransaction->commit();\n} catch (\\Exception $e) {\n    $outerTransaction->rollBack();\n} catch (\\Throwable $e) {\n    $innerTransaction->rollBack();\n    throw $e;\n}\n```\n\n\n## Репликация и разделение запросов на чтение и запись <span id=\"read-write-splitting\"></span>\n\nМногие СУБД поддерживают [репликацию баз данных](https://ru.wikipedia.org/wiki/%D0%A0%D0%B5%D0%BF%D0%BB%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8F_(%D0%B2%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F_%D1%82%D0%B5%D1%85%D0%BD%D0%B8%D0%BA%D0%B0))\nдля лучшей доступности базы данных и уменьшения времени ответа сервера. С репликацией базы данных, данные копируются\nиз *master servers* на *slave servers*. Все вставки и обновления должны происходить на основном сервере, хотя чтение\nможет производится и с подчинённых серверов.\n\nЧтоб воспользоваться преимуществами репликации и достичь разделения чтения и записи, вам необходимо настроить компонент\n[[yii\\db\\Connection]] как указано ниже:\n\n```php\n[\n    'class' => 'yii\\db\\Connection',\n\n    // настройки для мастера\n    'dsn' => 'dsn for master server',\n    'username' => 'master',\n    'password' => '',\n\n    // общие настройки для подчинённых\n    'slaveConfig' => [\n        'username' => 'slave',\n        'password' => '',\n        'attributes' => [\n            // используем небольшой таймаут для соединения\n            PDO::ATTR_TIMEOUT => 10,\n        ],\n    ],\n\n    // список настроек для подчинённых серверов\n    'slaves' => [\n        ['dsn' => 'dsn for slave server 1'],\n        ['dsn' => 'dsn for slave server 2'],\n        ['dsn' => 'dsn for slave server 3'],\n        ['dsn' => 'dsn for slave server 4'],\n    ],\n]\n```\n\nВышеуказанная конфигурация определяет систему с одним мастером и несколькими подчинёнными. Один из подчинённых\nбудет подключен и использован для чтения, в то время как мастер будет использоваться для запросов записи.\nТакое разделение чтения и записи будет осуществлено автоматически с указанной конфигурацией. Например,\n\n```php\n// создание экземпляра соединения, использующего вышеуказанную конфигурацию\nYii::$app->db = Yii::createObject($config);\n\n// запрос к одному из подчинённых\n$rows = Yii::$app->db->createCommand('SELECT * FROM user LIMIT 10')->queryAll();\n\n// запрос к мастеру\nYii::$app->db->createCommand(\"UPDATE user SET username='demo' WHERE id=1\")->execute();\n```\n\n> Info: Запросы выполненные через [[yii\\db\\Command::execute()]] определяются как запросы на запись, а все\n  остальные запросы через один из \"query\" методов [[yii\\db\\Command]] воспринимаются как запросы на чтение.\n  Вы можете получить текущий статус соединения к подчинённому серверу через `$db->slave`.\n\nКомпонент `Connection` поддерживает балансировку нагрузки и переключение при сбое для подчинённых серверов.\nПри выполнении первого запроса на чтение, компонент `Connection` будет случайным образом выбирать подчинённый сервер\nи попытается подключиться к нему. Если сервер окажется \"мёртвым\", он попробует подключиться к другому. Если ни один\nиз подчинённых серверов не будет доступен, он подключится к мастеру. Если настроить\n[[yii\\db\\Connection::serverStatusCache|кеш статуса серверов]], то недоступность серверов может быть запомнена, чтоб не\nиспользоваться в течении [[yii\\db\\Connection::serverRetryInterval|заданного промежутка времени]].\n\n> Info: В конфигурации выше, таймаут соединения к подчинённому серверу настроен на 10 секунд.\n  Это означает, что если сервер не ответит за 10 секунд, он будет считаться \"мёртвым\". Вы можете отрегулировать\n  этот параметр исходя из настроек вашей среды.\n\nВы также можете настроить несколько основных и несколько подчинённых серверов. Например,\n\n```php\n[\n    'class' => 'yii\\db\\Connection',\n\n    // общая конфигурация для основных серверов\n    'masterConfig' => [\n        'username' => 'master',\n        'password' => '',\n        'attributes' => [\n            // используем небольшой таймаут для соединения\n            PDO::ATTR_TIMEOUT => 10,\n        ],\n    ],\n\n    // список настроек для основных серверов\n    'masters' => [\n        ['dsn' => 'dsn for master server 1'],\n        ['dsn' => 'dsn for master server 2'],\n    ],\n\n    // общие настройки для подчинённых\n    'slaveConfig' => [\n        'username' => 'slave',\n        'password' => '',\n        'attributes' => [\n            // используем небольшой таймаут для соединения\n            PDO::ATTR_TIMEOUT => 10,\n        ],\n    ],\n\n    // список настроек для подчинённых серверов\n    'slaves' => [\n        ['dsn' => 'dsn for slave server 1'],\n        ['dsn' => 'dsn for slave server 2'],\n        ['dsn' => 'dsn for slave server 3'],\n        ['dsn' => 'dsn for slave server 4'],\n    ],\n]\n```\n\nКонфигурация выше, определяет два основных и четыре подчинённых серверов. Компонент `Connection` поддерживает \nбалансировку нагрузки и переключение при сбое между основными серверами, так же как и между подчинёнными. Различие\nзаключается в том, что когда ни к одному из основных серверов не удастся подключиться будет выброшено исключение.\n\n> Note: Когда вы используете свойство [[yii\\db\\Connection::masters|masters]] для настройки одного или нескольких\n  основных серверов, все остальные свойства для настройки соединения с базой данных (такие как `dsn`, `username`, `password`)\n  будут проигнорированы компонентом `Connection`.\n\nПо умолчанию, транзакции используют соединение с основным сервером. И в рамках транзакции, все операции с БД будут\nиспользовать соединение с основным сервером. Например,\n\n```php\n$db = Yii::$app->db;\n// Транзакция запускается на основном сервере\n$transaction = $db->beginTransaction();\n\ntry {\n    // оба запроса выполняются на основном сервере\n    $rows = $db->createCommand('SELECT * FROM user LIMIT 10')->queryAll();\n    $db->createCommand(\"UPDATE user SET username='demo' WHERE id=1\")->execute();\n\n    $transaction->commit();\n} catch(\\Exception $e) {\n    $transaction->rollBack();\n    throw $e;\n} catch (\\Throwable $e) {\n    $innerTransaction->rollBack();\n    throw $e;\n}\n```\n\nЕсли вы хотите запустить транзакцию на подчинённом сервере, вы должны указать это явно, как показано ниже:\n\n```php\n$transaction = Yii::$app->db->slave->beginTransaction();\n```\n\nИногда может потребоваться выполнить запрос на чтение через подключение к основному серверу. Это может быть достигнуто\nс использованием метода `useMaster()`:\n\n```php\n$rows = Yii::$app->db->useMaster(function ($db) {\n    return $db->createCommand('SELECT * FROM user LIMIT 10')->queryAll();\n});\n```\n\nВы также можете явно установить `$db->enableSlaves` в ложь, чтоб направлять все запросы к соединению с мастером.\n\n\n## Работа со схемой базы данных <span id=\"database-schema\"></span>\n\nYii DAO предоставляет целый набор методов для управления схемой базы данных, таких как создание новых таблиц, удаление\nстолбцов из таблицы, и т.д. Эти методы описаны ниже:\n\n* [[yii\\db\\Command::createTable()|createTable()]]: создание таблицы\n* [[yii\\db\\Command::renameTable()|renameTable()]]: переименование таблицы\n* [[yii\\db\\Command::dropTable()|dropTable()]]: удаление таблицы\n* [[yii\\db\\Command::truncateTable()|truncateTable()]]: удаление всех записей в таблице\n* [[yii\\db\\Command::addColumn()|addColumn()]]: добавление столбца\n* [[yii\\db\\Command::renameColumn()|renameColumn()]]: переименование столбца\n* [[yii\\db\\Command::dropColumn()|dropColumn()]]: удаление столбца\n* [[yii\\db\\Command::alterColumn()|alterColumn()]]: преобразование столбца\n* [[yii\\db\\Command::addPrimaryKey()|addPrimaryKey()]]: добавление первичного ключа\n* [[yii\\db\\Command::dropPrimaryKey()|dropPrimaryKey()]]: удаление первичного ключа\n* [[yii\\db\\Command::addForeignKey()|addForeignKey()]]: добавление внешнего ключа\n* [[yii\\db\\Command::dropForeignKey()|dropForeignKey()]]: удаление внешнего ключа\n* [[yii\\db\\Command::createIndex()|createIndex()]]: создания индекса\n* [[yii\\db\\Command::dropIndex()|dropIndex()]]: удаление индекса\n\nЭти методы могут быть использованы, как указано ниже:\n\n```php\n// CREATE TABLE\nYii::$app->db->createCommand()->createTable('post', [\n    'id' => 'pk',\n    'title' => 'string',\n    'text' => 'text',\n]);\n```\n\nВы также сможете получить описание схемы таблицы через вызов метода [[yii\\db\\Connection::getTableSchema()|getTableSchema()]].\nНапример,\n\n```php\n$table = Yii::$app->db->getTableSchema('post');\n```\n\nМетод вернёт объект [[yii\\db\\TableSchema]], который содержит информацию о столбцах таблицы, первичных ключах, внешних\nключах, и т.д. Вся эта информация используется главным образом для [построителя запросов](db-query-builder.md) и\n[active record](db-active-record.md), чтоб помочь вам писать независимый от базы данных код.\n"
  },
  {
    "path": "docs/guide-ru/db-migrations.md",
    "content": "Миграции баз данных\n===================\n\nВ ходе разработки и ведения баз данных приложений, которые управляют данными, структуры используемых баз данных развиваются, как и исходный код приложений. Например, при разработке приложения, в будущем может оказаться необходимой новая таблица; уже после того, как приложение будет развернуто в рабочем режиме (продакшене), также может быть обнаружено, что для повышения производительности запросов должен быть создан определённый индекс; и так далее.\nВ связи с тем, что изменение структуры базы данных часто требует изменение исходного кода, yii поддерживает так\nназываемую возможность *миграции баз данных*, которая позволяет отслеживать изменения в базах данных при помощи терминов *миграции баз данных*, которые являются системой контроля версий вместе с исходным кодом.\n\nСледующие шаги показывают, как миграции базы данных могут быть использованы командой разработчиков в процессе разработки:\n\n1. Илья создает новую миграцию (например, создается новая таблица или изменяется определение столбца и т.п.).\n2. Илья фиксирует новую миграцию в системе управления версиями (например, в Git, Mercurial).\n3. Алексей обновляет свой репозиторий из системы контроля версий и получает новую миграцию.\n4. Алексей применяет миграцию к своей локальной базе данных, тем самым синхронизируя свою базу данных, для того чтобы отразить изменения, которые сделал Илья.\n\nА следующие шаги показывают, как развернуть новый релиз с миграциями баз данных в рабочем режиме (продакшена):\n\n1. Сергей создаёт новую версию проекта репозитория, которая содержит некоторые новые миграции баз данных.\n2. Сергей обновляет исходный код на рабочем сервере до новой версии.\n3. Сергей применяет любую из накопленных миграций баз данных в рабочую базу данных.\n\nYii предоставляет набор инструментов для миграций из командной строки, которые позволяют:\n\n* создавать новые миграции;\n* применять миграции;\n* отменять миграции;\n* применять миграции повторно;\n* показывать историю и статус миграций;\n\nВсе эти инструменты доступны через команду `yii migrate`. В этом разделе мы опишем подробно, как выполнять различные задачи, используя эти инструменты. Вы также можете сами посмотреть как использовать каждый отдельный инструмент при помощи команды `yii help migrate`.\n\n> Tip: Миграции могут не только изменять схему базы данных, но и приводить данные в соответствие с новой схемой, создавать иерархию RBAC или очищать кеш.\n\n\n## Создание миграций <span id=\"creating-migrations\"></span>\n\nЧтобы создать новую миграцию, выполните следующую команду:\n\n```\nyii migrate/create <name>\n```\n\nТребуемый аргумент `name` даёт краткое описание новой миграции. Например, если миграция о создании новой таблицы с именем *news*, вы можете использовать имя `create_news_table` и выполнить следующую команду:\n\n```\nyii migrate/create create_news_table\n```\n\n> Note: Поскольку аргумент `name` будет использован как часть имени класса создаваемой миграции, он должен содержать только буквы, цифры и/или символы подчеркивания.\n\nПриведенная выше команда создаст новый PHP класс с именем файла `m150101_185401_create_news_table.php` в директории `@app/migrations`. Файл содержит следующий код, который главным образом декларирует класс миграции `m150101_185401_create_news_table` со следующим каркасом кода:\n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function up()\n    {\n\n    }\n\n    public function down()\n    {\n        echo \"m101129_185401_create_news_table cannot be reverted.\\n\";\n\n        return false;\n    }\n\n    /*\n    // Use safeUp/safeDown to run migration code within a transaction\n    public function safeUp()\n    {\n    }\n\n    public function safeDown()\n    {\n    }\n    */\n}\n```\n\nКаждая миграция базы данных определяется как PHP класс расширяющийся от [[yii\\db\\Migration]]. Имя класса миграции автоматически создается в формате `m<YYMMDD_HHMMSS>_<Name>` (`m<ГодМесяцДень_ЧасыМинутыСекунды>_<Имя>`), где\n\n* `<YYMMDD_HHMMSS>` относится к UTC дате-времени при котором команда создания миграции была выполнена.\n* `<Name>` это то же самое значение аргумента `name` которое вы прописываете в команду.\n\nВ классе миграции, вы должны прописать код в методе `up()` когда делаете изменения в структуре базы данных.\nВы также можете написать код в методе `down()`, чтобы отменить сделанные `up()` изменения. Метод `up` вызывается для обновления базы данных с помощью данной миграции, а метод `down()` вызывается для отката изменений базы данных.\nСледующий код показывает как можно реализовать класс миграции, чтобы создать таблицу `news`:\n\n```php\n<?php\n\nuse yii\\db\\Schema;\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function up()\n    {\n        $this->createTable('news', [\n            'id' => Schema::TYPE_PK,\n            'title' => Schema::TYPE_STRING . ' NOT NULL',\n            'content' => Schema::TYPE_TEXT,\n        ]);\n    }\n\n    public function down()\n    {\n        $this->dropTable('news');\n    }\n}\n```\n\n> Info: Не все миграции являются обратимыми. Например, если метод `up()` удаляет строку из таблицы, возможно что у вас уже не будет возможности вернуть эту строку методом `down()`. Иногда Вам может быть просто слишком лень реализовывать метод `down()`, в связи с тем, что это не очень распространено - откатывать миграции базы данных. В этом случае вы должны в методе `down()` вернуть `false`, чтобы указать, что миграция не является обратимой.\n\nБазовый класс миграций [[yii\\db\\Migration]] предоставляет подключение к базе данных через свойство [[yii\\db\\Migration::db|db]]. Вы можете использовать его для манипулирования схемой базы данных используя методы описанные в [работе со схемой базы данных](db-dao.md#database-schema).\n\nВместо использования физических типов данных, при создании таблицы или столбца, следует использовать *абстрактные типы* для того, чтобы ваша миграция являлась независимой от конкретной СУБД. Класс [[yii\\db\\Schema]] определяет набор констант для предоставления поддержки абстрактных типов. Эти константы называются в следующем формате `TYPE_<Name>`. Например,\n`TYPE_PK` относится к типу автоинкремента (AUTO_INCREMENT) первичного ключа;\n`TYPE_STRING` относится к строковому типу.\nКогда миграция применяется к конкретной базе данных, абстрактные типы будут переведены в соответствующие физические типы.\nВ случае с MySQL, `TYPE_PK` будет превращено в `int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY`, а `TYPE_STRING` станет `varchar(255)`.\n\nВы можете добавить дополнительные ограничения при использовании абстрактных типов. В приведенном выше примере, ` NOT NULL` добавляется к `Schema::TYPE_STRING` чтобы указать, что столбец не может быть `NULL`.\n\n> Info: Сопоставление абстрактных типов и физических типов определяется свойством [[yii\\db\\QueryBuilder::$typeMap|$typeMap]] в каждом конкретном `QueryBuilder` классе.\n\nНачиная с версии 2.0.6, появился новый построитель схем, который является более удобным инструментом для описания структуры столбцов.\nТеперь, при написании миграций, можно использовать такой код:\n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function up()\n    {\n        $this->createTable('news', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string()->notNull(),\n            'content' => $this->text(),\n        ]);\n    }\n\n    public function down()\n    {\n        $this->dropTable('news');\n    }\n}\n```\n\nВесь список методов описания типов столбцов доступен в API документации [[yii\\db\\SchemaBuilderTrait]].\n\n## Генерация миграций <span id=\"generating-migrations\"></span>\n\nНачиная с версии 2.0.7 появился удобный способ создания миграций из консоли.\n\nВ том случае, если миграция названа особым образом, таким как, например, `create_xxx_table` или `drop_xxx_table` сгенерированный\nфайл миграции будет содержать дополнительный код.\n\n### Создание таблицы\n\n```php\nyii migrate/create create_post_table\n```\n\nсгенерирует\n\n```php\nclass m150811_220037_create_post_table extends Migration\n{\n    public function up()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey()\n        ]);\n    }\n\n    public function down()\n    {\n        $this->dropTable('post');\n    }\n}\n```\n\nЧтобы сразу создать поля таблицы, укажите их через опцию `--fields`.\n\n```php\nyii migrate/create create_post_table --fields=title:string,body:text\n```\n\nсгенерирует\n\n```php\nclass m150811_220037_create_post_table extends Migration\n{\n    public function up()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string(),\n            'body' => $this->text()\n        ]);\n    }\n\n    public function down()\n    {\n        $this->dropTable('post');\n    }\n}\n```\n\nМожно указать дополнительные параметры.\n\n```php\nyii migrate/create create_post_table --fields=title:string(12):notNull:unique,body:text\n```\n\nсгенерирует\n\n```php\nclass m150811_220037_create_post_table extends Migration\n{\n    public function up()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string(12)->notNull()->unique(),\n            'body' => $this->text()\n        ]);\n    }\n\n    public function down()\n    {\n        $this->dropTable('post');\n    }\n}\n```\n\n> Note: первичный ключ добавляется автоматически и по умолчанию называется `id`. Если вам необходимо другое имя,\n> указать его можно через опцию `--fields=name:primaryKey`.\n\n### Внешние ключи\n\nНачиная с версии 2.0.8, генератор поддерживает создание внешних ключей через ключевое слово `foreignKey`.\n\n```php\nyii migrate/create create_post_table --fields=\"author_id:integer:notNull:foreignKey(user),category_id:integer:defaultValue(1):foreignKey,title:string,body:text\"\n```\n\nсгенерирует\n\n```php\n/**\n * Handles the creation for table `post`.\n * Has foreign keys to the tables:\n *\n * - `user`\n * - `category`\n */\nclass m160328_040430_create_post_table extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey(),\n            'author_id' => $this->integer()->notNull(),\n            'category_id' => $this->integer()->defaultValue(1),\n            'title' => $this->string(),\n            'body' => $this->text(),\n        ]);\n\n        // creates index for column `author_id`\n        $this->createIndex(\n            'idx-post-author_id',\n            'post',\n            'author_id'\n        );\n\n        // add foreign key for table `user`\n        $this->addForeignKey(\n            'fk-post-author_id',\n            'post',\n            'author_id',\n            'user',\n            'id',\n            'CASCADE'\n        );\n\n        // creates index for column `category_id`\n        $this->createIndex(\n            'idx-post-category_id',\n            'post',\n            'category_id'\n        );\n\n        // add foreign key for table `category`\n        $this->addForeignKey(\n            'fk-post-category_id',\n            'post',\n            'category_id',\n            'category',\n            'id',\n            'CASCADE'\n        );\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        // drops foreign key for table `user`\n        $this->dropForeignKey(\n            'fk-post-author_id',\n            'post'\n        );\n\n        // drops index for column `author_id`\n        $this->dropIndex(\n            'idx-post-author_id',\n            'post'\n        );\n\n        // drops foreign key for table `category`\n        $this->dropForeignKey(\n            'fk-post-category_id',\n            'post'\n        );\n\n        // drops index for column `category_id`\n        $this->dropIndex(\n            'idx-post-category_id',\n            'post'\n        );\n\n        $this->dropTable('post');\n    }\n}\n```\n\nПоложение ключевого слова `foreignKey` в описании поля не изменяет сгенерированный код. Это значит, что:\n\n- `author_id:integer:notNull:foreignKey(user)`\n- `author_id:integer:foreignKey(user):notNull`\n- `author_id:foreignKey(user):integer:notNull`\n\nГенерируют один и тот же код.\n\nКлючевое слово `foreignKey` может принимать в скобках параметр, который будет использован в качестве имени таблицы для внешнего ключа. Если параметр не был передан, то имя таблицы будет извлечено из названия поля.\n\nВ приведенном выше примере `author_id:integer:notNull:foreignKey(user)` будет генерировать поле `author_id` с внешним ключом на таблицу `user`, а `category_id:integer:defaultValue(1):foreignKey` сгенерирует поле `category_id` с внешним ключом на таблицу `category`.\n\nНачиная с версии 2.0.11, ключевое слово `foreignKey` может принимать второй параметр, который следует указывать через пробел.\nЭтот параметр будет использован в качестве имени поля внешнего ключа.\nВ случае, если второй параметр не будет передан, то поле для внешнего ключа будет извлечено из схемы таблицы.\nТем не менее, это справедливо только в тех случаях, когда таблица имеется в базе данных, первичный ключ задан и не является составным.\nВ иных случаях будет использоваться имя по умолчанию `id`.\n\n### Удаление таблицы\n\n```php\nyii migrate/create drop_post_table --fields=title:string(12):notNull:unique,body:text\n```\n\nсгенерирует\n\n```php\nclass m150811_220037_drop_post_table extends Migration\n{\n    public function up()\n    {\n        $this->dropTable('post');\n    }\n\n    public function down()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string(12)->notNull()->unique(),\n            'body' => $this->text()\n        ]);\n    }\n}\n```\n\n### Добавление столбца\n\nЕсли имя миграции задано как `add_xxx_column_to_yyy_table`, файл будет содержать необходимые методы `addColumn` и `dropColumn`.\n\nДля добавления столбца:\n\n```php\nyii migrate/create add_position_column_to_post_table --fields=position:integer\n```\n\nсгенерирует\n\n```php\nclass m150811_220037_add_position_column_to_post_table extends Migration\n{\n    public function up()\n    {\n        $this->addColumn('post', 'position', $this->integer());\n    }\n\n    public function down()\n    {\n        $this->dropColumn('post', 'position');\n    }\n}\n```\n\n### Удаление столбца\n\nЕсли имя миграции задано как `drop_xxx_column_from_yyy_table`, файл будет содержать необходимые методы `addColumn` и `dropColumn`.\n\n```php\nyii migrate/create drop_position_column_from_post_table --fields=position:integer\n```\n\nсгенерирует\n\n```php\nclass m150811_220037_drop_position_column_from_post_table extends Migration\n{\n    public function up()\n    {\n        $this->dropColumn('post', 'position');\n    }\n\n    public function down()\n    {\n        $this->addColumn('post', 'position', $this->integer());\n    }\n}\n```\n\n### Добавление промежуточной таблицы\n\nЕсли имя миграции задано как `create_junction_table_for_xxx_and_yyy_tables`, файл будет содержать код для создания промежуточной таблицы.\n\n```php\nyii migrate/create create_junction_table_for_post_and_tag_tables\n```\n\nсгенерирует\n\n```php\n/**\n * Handles the creation for table `post_tag`.\n * Has foreign keys to the tables:\n *\n * - `post`\n * - `tag`\n */\nclass m160328_041642_create_junction_table_for_post_and_tag_tables extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $this->createTable('post_tag', [\n            'post_id' => $this->integer(),\n            'tag_id' => $this->integer(),\n            'created_at' => $this->dateTime(),\n            'PRIMARY KEY(post_id, tag_id)',\n        ]);\n\n        // creates index for column `post_id`\n        $this->createIndex(\n            'idx-post_tag-post_id',\n            'post_tag',\n            'post_id'\n        );\n\n        // add foreign key for table `post`\n        $this->addForeignKey(\n            'fk-post_tag-post_id',\n            'post_tag',\n            'post_id',\n            'post',\n            'id',\n            'CASCADE'\n        );\n\n        // creates index for column `tag_id`\n        $this->createIndex(\n            'idx-post_tag-tag_id',\n            'post_tag',\n            'tag_id'\n        );\n\n        // add foreign key for table `tag`\n        $this->addForeignKey(\n            'fk-post_tag-tag_id',\n            'post_tag',\n            'tag_id',\n            'tag',\n            'id',\n            'CASCADE'\n        );\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        // drops foreign key for table `post`\n        $this->dropForeignKey(\n            'fk-post_tag-post_id',\n            'post_tag'\n        );\n\n        // drops index for column `post_id`\n        $this->dropIndex(\n            'idx-post_tag-post_id',\n            'post_tag'\n        );\n\n        // drops foreign key for table `tag`\n        $this->dropForeignKey(\n            'fk-post_tag-tag_id',\n            'post_tag'\n        );\n\n        // drops index for column `tag_id`\n        $this->dropIndex(\n            'idx-post_tag-tag_id',\n            'post_tag'\n        );\n\n        $this->dropTable('post_tag');\n    }\n}\n```\n\nНачиная с версии 2.0.11, имена полей для внешних ключей промежуточной таблицы будут извлечены из объединяемых таблиц.\nТем не менее, это справедливо только в тех случаях, когда таблица имеется в базе данных, первичный ключ задан и не является составным.\nВ других иных случаях для поля будет сгенерировано значение по умолчанию `id`.\n\n### Транзакции Миграций <span id=\"transactional-migrations\"></span>\n\nПри выполнении сложных миграций баз данных, важно обеспечить каждую миграцию либо успехом, либо ошибкой, в целом так, чтобы база данных могла поддерживать целостность и непротиворечивость. Для достижения данной цели рекомендуется, заключить операции каждой миграции базы данных в [транзакции](db-dao.md#performing-transactions).\n\nСамый простой способ реализации транзакций миграций это прописать код миграций в методы `safeUp()` и `safeDown()`. Эти два метода отличаются от методов `up()` и `down()` тем, что они неявно заключены в транзакции. В результате если какая-либо операция в этих методах не удается, все предыдущие операции будут отменены автоматически.\n\nВ следующем примере, помимо создания таблицы `news` мы также вставляем в этой таблице начальную строку.\n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function safeUp()\n    {\n        $this->createTable('news', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string()->notNull(),\n            'content' => $this->text(),\n        ]);\n\n        $this->insert('news', [\n            'title' => 'test 1',\n            'content' => 'content 1',\n        ]);\n    }\n\n    public function safeDown()\n    {\n        $this->delete('news', ['id' => 1]);\n        $this->dropTable('news');\n    }\n}\n```\n\nОбратите внимание, что обычно при выполнении нескольких операций в базе данных при помощи метода `safeUp()`, вы должны\nреализовать обратный порядок исполнения в методе `safeDown()`. В приведенном выше примере мы сначала создали таблицу,\nа затем вставили строку в `safeUp()`; а в `safeDown()` мы сначала удаляем строку и затем удаляем таблицу.\n\n> Note: Не все СУБД поддерживают транзакции. И некоторые запросы к базам данных не могут быть введены в транзакции.\nДля различных примеров, пожалуйста, обратитесь к [негласным обязательствам](https://dev.mysql.com/doc/refman/5.7/en/implicit-commit.html).\nВ этом случае вместо этих методов вы должны реализовать методы `up()` и `down()`.\n\n### Методы доступа к базе данных <span id=\"db-accessing-methods\"></span>\n\nБазовый класс миграции [[yii\\db\\Migration]] предоставляет набор методов, которые позволяют Вам получить доступ и управлять\nбазами данных. Вы можете найти эти методы, их названия аналогичны [методам DAO](db-dao.md), предоставленным в классе [[yii\\db\\Command]].\nНапример, метод [[yii\\db\\Migration::createTable()]] позволяет создать новую таблицу, подобно методу [[yii\\db\\Command::createTable()]].\n\nПреимущество методов, описанных при помощи [[yii\\db\\Migration]] заключается в том, что Вам не нужно явно создавать\nэкземпляр/копию [[yii\\db\\Command]] и исполнение каждого метода будет автоматически отображать полезные сообщения\nговорящие вам, что операции с базой данных выполняются и сколько они идут.\n\nНиже представлен список всех этих методов доступа к базам данных:\n\n* [[yii\\db\\Migration::execute()|execute()]]: выполнение SQL инструкции\n* [[yii\\db\\Migration::insert()|insert()]]: вставка одной строки\n* [[yii\\db\\Migration::batchInsert()|batchInsert()]]: вставка нескольких строк\n* [[yii\\db\\Migration::update()|update()]]: обновление строк\n* [[yii\\db\\Migration::delete()|delete()]]: удаление строк\n* [[yii\\db\\Migration::createTable()|createTable()]]: создание таблицы\n* [[yii\\db\\Migration::renameTable()|renameTable()]]: переименование таблицы\n* [[yii\\db\\Migration::dropTable()|dropTable()]]: удаление таблицы\n* [[yii\\db\\Migration::truncateTable()|truncateTable()]]: удаление всех строк в таблице\n* [[yii\\db\\Migration::addColumn()|addColumn()]]: добавление столбца\n* [[yii\\db\\Migration::renameColumn()|renameColumn()]]: переименование столбца\n* [[yii\\db\\Migration::dropColumn()|dropColumn()]]: удаление столбца\n* [[yii\\db\\Migration::alterColumn()|alterColumn()]]: изменения столбца\n* [[yii\\db\\Migration::addPrimaryKey()|addPrimaryKey()]]: добавление первичного ключа\n* [[yii\\db\\Migration::dropPrimaryKey()|dropPrimaryKey()]]: удаление первичного ключа\n* [[yii\\db\\Migration::addForeignKey()|addForeignKey()]]: добавление внешнего ключа\n* [[yii\\db\\Migration::dropForeignKey()|dropForeignKey()]]: удаление внешнего ключа\n* [[yii\\db\\Migration::createIndex()|createIndex()]]: создание индекса\n* [[yii\\db\\Migration::dropIndex()|dropIndex()]]: удаление индекса\n\n> Info: [[yii\\db\\Migration]] не предоставляет методы запросов к базе данных. Это потому, что обычно не требуется отображать дополнительные сообщения об извлечении данных из базы данных. Это также, потому, что можно использовать более мощный [Построитель Запросов](db-query-builder.md) для построения и выполнения сложных запросов.\n\n> Note: при обработке данных внутри миграции, может показаться, что использование существующих классов [Active Record](db-active-record.md), со всей их готовой бизнес логикой, будет разумным решением и упросит код миграции. Однако следует помнить, что код миграций не должен меняться, по определению. В отличие от миграций, бизнес логика приложений часто изменяется. Это может привести к нарушению работы миграции при определённых изменениях на уровне Active Record. Поэтому рекомендуется делать миграции независимыми от других частей приложения, таких как классы Active Record.\n\n\n## Применение Миграций <span id=\"applying-migrations\"></span>\n\nДля обновления базы данных до последней структуры, вы должны применить все новые миграции, используя следующую команду:\n\n```\nyii migrate\n```\n\nЭта команда выведет список всех миграций, которые не применялись до сих пор. Если вы подтвердите, что вы хотите применить эти миграций, то этим самым запустите метод `up()` или `safeUp()` в каждом новом классе миграции, один за другим, в порядке их временного значения timestamp.\n\nДля каждой миграции которая была успешно проведена, эта команда будет вставлять строку в таблицу базы данных с именем\n`migration` записав успешное проведение миграции. Это позволяет инструменту миграции выявлять какие миграции были применены, а какие - нет.\n\n> Note: Инструмент миграции автоматически создаст таблицу `migration` в базе данных указанной в параметре [[yii\\console\\controllers\\MigrateController::db|db]]. По умолчанию база данных определяется как [компонент приложения](structure-application-components.md) `db`.\n\nИногда, необходимо применить одну или несколько новых миграций, вместо всех доступных миграций. Это возможно сделать, указав, при выполнении команды, количество миграций, которые необходимо применить. Например, следующая команда будет пытаться применить следующие три доступные миграции:\n\n```\nyii migrate 3\n```\n\nТакже можно явно указать конкретную миграцию, которая должна быть применена к базе данных, это можно сделать при помощи команды `migrate/to` в одном из следующих форматов:\n\n```\nyii migrate/to 150101_185401                      # используя временную метку определяющую миграцию\nyii migrate/to \"2015-01-01 18:54:01\"              # используя строку, которая может быть получена путем использования функции strtotime()\nyii migrate/to m150101_185401_create_news_table   # используя полное имя\nyii migrate/to 1392853618                         # используя временную метку UNIX\n```\n\nЕсли раньше имелись какие-либо не применённые миграции, до указанной конкретной миграции, то все они будут применены до данной миграции.\nА если указанная миграция уже применялась ранее, то любые более поздние версии данной прикладной миграции будут отменены.\n\n## Отмена Миграций <span id=\"reverting-migrations\"></span>\n\nЧтобы отменить (откатить) одну или несколько миграций, которые применялись ранее, нужно запустить следующую команду:\n\n```\nyii migrate/down     # отменяет самую последнюю применённую миграцию\nyii migrate/down 3   # отменяет 3 последних применённых миграции\n```\n\n> Note: Не все миграции являются обратимыми. При попытке отката таких миграций произойдёт ошибка и остановится весь процесс отката.\n\n## Перезагрузка Миграций <span id=\"redoing-migrations\"></span>\n\nПод перезагрузкой миграций подразумевается, сначала последовательный откат определённых миграций, а потом применение их снова. Это может быть сделано следующим образом:\n\n```\nyii migrate/redo        # перезагрузить последнюю применённую миграцию\nyii migrate/redo 3      # перезагрузить 3 последние применённые миграции\n```\n\n> Note: Если миграция не является обратимой, вы не сможете её перезагрузить.\n\n## Список Миграций <span id=\"listing-migrations\"></span>\n\nЧтобы посмотреть какие миграции были применены, а какие нет, используйте следующие команды:\n\n```\nyii migrate/history     # показать последних 10 применённых миграций\nyii migrate/history 5   # показать последних 5 применённых миграций\nyii migrate/history all # показать все применённые миграции\n\nyii migrate/new         # показать первых 10 новых миграций\nyii migrate/new 5       # показать первых 5 новых миграций\nyii migrate/new all     # показать все новые миграции\n```\n\n## Изменение Истории Миграций <span id=\"modifying-migration-history\"></span>\n\nВместо применения или отката миграций, есть возможность просто <b>отметить</b>, что база данных была обновлена до определенной миграции. Это часто используется при ручном изменении базы данных в конкретное состояние и Вам не нужно применять миграции для того, чтобы это изменение было повторно применено позже. Этой цели можно добиться с помощью следующей команды:\n\n```\nyii migrate/mark 150101_185401                      # используя временную метку определённой миграции\nyii migrate/mark \"2015-01-01 18:54:01\"              # используя строку, которая может быть получена путем использования функции strtotime()\nyii migrate/mark m150101_185401_create_news_table   # используя полное имя\nyii migrate/mark 1392853618                         # используя временную метку UNIX\n```\n\nЭта команда изменит таблицу `migration` добавив или удалив определенные строки, тем самым указав, что к базе данных была применена указанная миграция. Никаких миграций не будет применяться или отменяться по этой команде.\n\n## Настройка Миграций <span id=\"customizing-migrations\"></span>\n\nЕсть несколько способов настроить команду миграции.\n\n### Используя параметры командной строки<span id=\"using-command-line-options\"></span>\n\nВ команду миграций входит несколько параметров командной строки, которые могут использоваться, для того, чтобы настроить\nповедение миграции:\n\n* `interactive`: логический тип - boolean (по умолчанию `true`). Указывает, следует ли выполнять миграцию в интерактивном\n  режиме. Если это значение является - `true`, то пользователю будет выдан запрос, перед выполнением командой определенных\n  действий. Вы можете установить это значение в `false` если команда используется в фоновом режиме.\n\n* `migrationPath`: строка - string (по умолчанию `@app/migrations`). Указывает каталог для хранения всех файлов классов\n  миграций. Этот параметр может быть определён либо как путь до директории, либо как [псевдоним](concept-aliases.md) пути.\n  Обратите внимание, что данный каталог должен существовать, иначе команда будет выдавать ошибку.\n\n* `migrationTable`: строка - string (по умолчанию `migration`). Определяет имя таблицы в базе данных в которой хранится\n  информация об истории миграций. Эта таблица будет автоматически создана командой миграции, если её не существует.\n  Вы также можете создать её вручную, используя структуру `version varchar(255) primary key, apply_time integer`.\n\n* `db`: строка - string (по умолчанию `db`). Определяет ID базы данных [компонента приложения](structure-application-components.md).\n  Этот параметр представляет собой базу данных, которая подвергается миграциям при помощи команды миграций.\n\n* `templateFile`: строка - string (по умолчанию `@yii/views/migration.php`). Указывает путь до файла шаблона, который\n  используется для формирования скелета класса файлов миграции. Этот параметр может быть определён либо как путь до файла,\n  либо как [псевдоним](concept-aliases.md) пути. Файл шаблона - это PHP скрипт, в котором можно использовать\n  предопределенную переменную с именем `$className` для того, чтобы получить имя класса миграции.\n\n* `generatorTemplateFiles`: массив (по умолчанию `[\n        'create_table' => '@yii/views/createTableMigration.php',\n        'drop_table' => '@yii/views/dropTableMigration.php',\n        'add_column' => '@yii/views/addColumnMigration.php',\n        'drop_column' => '@yii/views/dropColumnMigration.php',\n        'create_junction' => '@yii/views/createTableMigration.php'\n]`), в котором указаны файлы шаблонов для генерации миграций. Подробнее в разделе «[Генерация миграций](#generating-migrations)».\n\n* `fields`: массив конфигураций столбцов, который используется для генерации кода миграции. По умолчанию пуст. Формат\n  каждой конфигурации `ИМЯ_СТОЛБЦА:ТИП_СТОЛБЦА:ДЕКОРАТОР_СТОЛБЦА`. Например, `--fields=name:string(12):notNull` даст нам\n  столбец типа строка размера 12 с ограничением `not null`.\n\n\nВ следующем примере показано, как можно использовать эти параметры.\n\nНапример, если мы хотим перенести модуль `forum`, чьи файлы миграций расположены в каталоге `migrations` данного модуля,\nдля этого мы можем использовать следующую команду:\n\n```\n# не интерактивная миграция модуля форума\nyii migrate --migrationPath=@app/modules/forum/migrations --interactive=0\n```\n\n### Глобальная настройка команд <span id=\"configuring-command-globally\"></span>\n\nВместо того, чтобы каждый раз вводить одинаковые значения параметров миграции, когда вы запускаете команду миграции,\nможно настроить её раз и навсегда в конфигурации приложения, как показано ниже:\n\n```php\nreturn [\n    'controllerMap' => [\n        'migrate' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationTable' => 'backend_migration',\n        ],\n    ],\n];\n```\n\nС приведённой выше конфигурацией, каждый раз при запуске команды миграции, таблица `backend_migration` будет использована для записи истории миграций. И Вам больше не нужно указывать её через параметр `migrationTable` в командной строке.\n\n\n### Миграции с пространством имен <span id=\"namespaced-migrations\"></span>\n\nНачиная с версии 2.0.10 вы можете использовать пространства имен при объявлении класса миграции. Вы можете указать список пространств\nимен миграций через [[yii\\console\\controllers\\MigrateController::migrationNamespaces|migrationNamespaces]]. Использование пространств\nимен для классов миграции позволяет сочетать несколько источников миграций. Например:\n\n```php\nreturn [\n    'controllerMap' => [\n        'migrate' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationNamespaces' => [\n                'app\\migrations', // Общие миграции приложения\n                'module\\migrations', // Миграции одного из модулей проекта\n                'some\\extension\\migrations', // Миграции одного из расширений\n            ],\n        ],\n    ],\n];\n```\n\n> Замечание: миграции из различных пространств имен образуют **единую** историю, т.е. вы не сможете\n  применить или откатить миграции из одного конкретного пространства имен.\n\nРаботая с миграциями по пространствам имен: при создании, отмене и т.д., следует указывать полное имя пространства имен\nперед именем миграции. Имейте в виду, что символ обратного слеша (`\\`), как правило, является специальным символом в консоли,\nтак что вам придется экранировать его соответствующим образом во избежание ошибок или неверного поведения. Например:\n\n```\nyii migrate/create 'app\\\\migrations\\\\createUserTable'\n```\n\n> Замечание: миграции заданные через [[yii\\console\\controllers\\MigrateController::migrationPath|migrationPath]] не могут содержать\n  пространство имен, миграции, объявленные с пространством имен, могут быть применены только используя свойство [[yii\\console\\controllers\\MigrateController::migrationNamespaces]].\n\n\n### Отдельностоящие Миграции <span id=\"separated-migrations\"></span>\n\nИногда использование единой истории для всех миграций проекта не желательно. Например: вы можете установить расширение\n'blog', которое содержит полностью независимый функционал и содержит собственные миграции, которые не должны затрагивать\nмиграции связанные с основной функциональностью проекта.\n\nЕсли необходимо, чтобы миграции из разных источников были независимы друг от друга, вы можете сконфигурировать\nнесколько команд миграции, которые будут использовать разные пространства имён и разные таблицы для хранения истории\nмиграций:\n\n```php\nreturn [\n    'controllerMap' => [\n        // Общие миграции приложения\n        'migrate-app' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationNamespaces' => ['app\\migrations'],\n            'migrationTable' => 'migration_app',\n        ],\n        // Миграции одного из модулей проекта\n        'migrate-module' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationNamespaces' => ['module\\migrations'],\n            'migrationTable' => 'migration_module',\n        ],\n        // Миграции одного из расширений\n        'migrate-rbac' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationPath' => '@yii/rbac/migrations',\n            'migrationTable' => 'migration_rbac',\n        ],\n    ],\n];\n```\n\nУчтите, что для синхронизации базы данных при такой конфигурации потребуется вызвать несколько команд вместо одной:\n\n```\nyii migrate-app\nyii migrate-module\nyii migrate-rbac\n```\n\n\n## Миграции в Несколько Баз Данных <span id=\"migrating-multiple-databases\"></span>\n\nПо умолчанию, миграции применяются для базы данных, указанной в `db` [компоненте приложения](structure-application-components.md).\nЕсли вы хотите применить миграцию к другой базе данных, вы можете определить параметр `db` в командной строке как показано ниже,\n\n```\nyii migrate --db=db2\n```\n\nПриведенная выше команда применит миграции к базе данных `db2`.\n\nИногда может случиться так, что вы захотите применить *некоторые* из миграций к одной базе данных, а некоторые другие к другой базе данных. Для достижения этой цели, при реализации класса миграции, необходимо явно указать идентификатор ID компонента базы данных, который миграция будет использовать, следующим образом:\n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function init()\n    {\n        $this->db = 'db2';\n        parent::init();\n    }\n}\n```\n\nВышеуказанная миграция будет применена к `db2` даже если указать другую базу данных через параметр `db` командной строки. Обратите внимание, что история миграций в этом случае будет записана в базу данных, указанную в параметре `db` командной строки.\n\nЕсли у вас есть несколько миграций, которые используют ту же другую базу данных, то рекомендуется создать базовый класс миграций выше кода `init()`. Затем каждый класс миграции может расширяться от этого базового класса.\n\n> Совет: Кроме установки свойства [[yii\\db\\Migration::db|db]], вы также можете работать с разными базами данных путем создания нового соединения с конкретной базой данных в классе Вашей миграции. Можно использовать [DAO методы](db-dao.md) с этими соединениями для манипулирования различными базами данных.\n\nДругая стратегия, которую вы можете выбрать, чтобы перенести (мигрировать) несколько баз данных - это сохранить миграции различных баз данных в разные директории. Затем вы можете перенести эти базы данных в нужные базы следующими командами:\n\n```\nyii migrate --migrationPath=@app/migrations/db1 --db=db1\nyii migrate --migrationPath=@app/migrations/db2 --db=db2\n...\n```\n\nПервая команда применит миграции в директории `@app/migrations/db1` к базе данных `db1`, а вторая команда применит миграции в директории `@app/migrations/db2` к базе данных `db2` и так далее.\n"
  },
  {
    "path": "docs/guide-ru/db-query-builder.md",
    "content": "Построитель запросов\n====================\n\nПостроенный поверх [DAO](db-dao.md), построитель запросов позволяет конструировать SQL выражения в программируемом и\nнезависимом от СУБД виде. В сравнении с написанием чистого SQL выражения, использование построителя помогает \nвам писать более читаемый связанный с SQL код и генерировать более безопасные SQL выражения.\n\nИспользование построителя запросов, как правило, включает два этапа:\n\n1. Создание объекта [[yii\\db\\Query]] представляющего различные части (такие как `SELECT`, `FROM`) SQL выражения SELECT.\n2. Выполнить запрос методом [[yii\\db\\Query]] (таким как `all()`) для извлечения данных из базы данных.\n\nСледующий код показывает обычное использование построителя запросов:\n\n```php\n$rows = (new \\yii\\db\\Query())\n    ->select(['id', 'email'])\n    ->from('user')\n    ->where(['last_name' => 'Smith'])\n    ->limit(10)\n    ->all();\n```\n\nПриведённый выше код создаёт и выполняет следующее SQL выражение, где параметр `:last_name` привязывается к строке `'Smith'`.\n\n```sql\nSELECT `id`, `email` \nFROM `user`\nWHERE `last_name` = :last_name\nLIMIT 10\n```\n\n> Info: В основном вы будете работать с [[yii\\db\\Query]] вместо [[yii\\db\\QueryBuilder]]. Последний вызывается\n  неявно при вызове одного из методов запроса. [[yii\\db\\QueryBuilder]] это класс, отвечающий за генерацию зависимого\n  от СУБД SQL выражения (такие как экранирование имён таблиц/столбцов) из независимых от СУБД объектов [[yii\\db\\Query]]. \n\n\n## Построение запросов <span id=\"building-queries\"></span>\n\nСоздав объект [[yii\\db\\Query]], вы можете вызвать различные методы для создания различных частей SQL выражения.\nИмена методов напоминают ключевые слова SQL, используемые в соответствующих частях SQL запроса. Например,\nчтобы указать `FROM` часть запроса, вам нужно вызвать метод [[yii\\db\\Query::from()|from()]]. Все методы построителя\nзапросов возвращают свой объект, который позволяет объединять несколько вызовов в цепочку.\n\nДалее будет описание каждого метода построителя запросов.\n\n### [[yii\\db\\Query::select()|select()]] <span id=\"select\"></span>\n\nМетод [[yii\\db\\Query::select()|select()]] определяет фрагмент `SELECT` SQL запроса. Вы можете указать столбцы, которые\nдолжны быть выбраны, они должны быть указаны в виде массива или строки. Имена столбцов автоматически экранируются\nпри создании SQL-запроса при его генерации из объекта [[yii\\db\\Query]].\n \n```php\n$query->select(['id', 'email']);\n\n// эквивалентно:\n\n$query->select('id, email');\n```\n\nИмена столбцов могут быть выбраны вместе с префиксами таблиц и/или алиасами столбцов, также как при записи обычного SQL выражения.\nНапример,\n\n```php\n$query->select(['user.id AS user_id', 'email']);\n\n// эквивалентно:\n\n$query->select('user.id AS user_id, email');\n```\n\nЕсли вы используете формат массива для указания столбцов, вы можете также указать ключи массива для указания алиасов столбцов.\nНапример, приведённый выше код может быть переписан:\n\n```php\n$query->select(['user_id' => 'user.id', 'email']);\n```\n\nЕсли вы не вызываете метод [[yii\\db\\Query::select()|select()]] при создании запроса, будет использована `*`, что означает\nвыбрать *все* столбцы.\n\nКроме имён столбцов, вы можете также использовать SQL выражения. Вы должны использовать формат массива для использования\nвыражений, которые содержат запятые для предотвращения некорректного автоматического экранирования. Например,\n\n```php\n$query->select([\"CONCAT(first_name, ' ', last_name) AS full_name\", 'email']); \n```\n\nНачиная с версии 2.0.1, вы также можете использовать подзапросы. Вы должны указывать каждый подзапрос в выражении как\nобъект [[yii\\db\\Query]]. Например,\n \n```php\n$subQuery = (new Query())->select('COUNT(*)')->from('user');\n\n// SELECT `id`, (SELECT COUNT(*) FROM `user`) AS `count` FROM `post`\n$query = (new Query())->select(['id', 'count' => $subQuery])->from('post');\n```\n\nЧтоб выбрать конкретные строки, вы можете вызвать метод [[yii\\db\\Query::distinct()|distinct()]]:\n\n```php\n// SELECT DISTINCT `user_id` ...\n$query->select('user_id')->distinct();\n```\n\nВы можете вызвать [[yii\\db\\Query::addSelect()|addSelect()]] для добавления полей. Например,\n\n```php\n$query->select(['id', 'username'])\n    ->addSelect(['email']);\n```\n\n\n### [[yii\\db\\Query::from()|from()]] <span id=\"from\"></span>\n\nМетод [[yii\\db\\Query::from()|from()]] указывает фрагмент `FROM` SQL запроса. Например,\n\n```php\n// SELECT * FROM `user`\n$query->from('user');\n```\n\nВы можете указать имена таблиц в виде строки или массива. Имена таблиц могут содержать префикс схемы и/или алиасы\nтаблиц, как при написании обычного SQL выражения. Например,\n\n```php\n$query->from(['public.user u', 'public.post p']);\n\n// эквивалентно:\n\n$query->from('public.user u, public.post p');\n```\n\nЕсли вы используете формат массива, вы можете использовать ключи массива для указания алиасов:\n\n```php\n$query->from(['u' => 'public.user', 'p' => 'public.post']);\n```\n\nКроме имён таблиц, вы можете так же, как и в select, указывать подзапросы в виде объекта [[yii\\db\\Query]].\n\n```php\n$subQuery = (new Query())->select('id')->from('user')->where('status=1');\n\n// SELECT * FROM (SELECT `id` FROM `user` WHERE status=1) u \n$query->from(['u' => $subQuery]);\n```\n\n#### Префиксы\n\nТакже может применяться [[yii\\db\\Connection::$tablePrefix|tablePrefix]] по умолчанию. Подробное описание смотрите\nв подразделе [«Экранирование имён таблиц и столбцов» раздела «Объекты доступа к данным (DAO)»](db-dao.md#quoting-table-and-column-names).\n\n### [[yii\\db\\Query::where()|where()]] <span id=\"where\"></span>\n\nМетод [[yii\\db\\Query::where()|where()]] определяет фрагмент `WHERE` SQL выражения. Вы можете использовать один из\nтрёх форматов:\n\n- строковый формат, Например, `'status=1'`\n- формат массива, Например, `['status' => 1, 'type' => 2]`\n- формат операторов, Например, `['like', 'name', 'test']`\n\n\n#### Строковый формат <span id=\"string-format\"></span>\n\nСтроковый формат - это лучший выбор для простых условий. Он работает так, будто вы просто пишете SQL запрос. Например,\n\n```php\n$query->where('status=1');\n\n// или используя привязку параметров\n$query->where('status=:status', [':status' => $status]);\n```\n\nНе встраивайте переменные непосредственно в условие, особенно если значение переменной получено от пользователя,\nпотому что это делает ваше приложение подверженным атакам через SQL инъекции.\n \n```php\n// Опасность! Не делайте так если вы не уверены, что $status это действительно число.\n$query->where(\"status=$status\");\n```\n\nПри использовании привязки параметров, вы можете вызывать [[yii\\db\\Query::params()|params()]] или\n[[yii\\db\\Query::addParams()|addParams()]] для раздельного указания параметров.\n\n```php\n$query->where('status=:status')\n    ->addParams([':status' => $status]);\n```\n\n\n#### Формат массива <span id=\"hash-format\"></span>\n\nФормат массива лучше всего использовать для указания нескольких объединяемых через `AND` условий, каждое из которых\nявляется простым равенством. Он описывается в виде массива, ключами которого являются имена столбцов, а значения\nсоответствуют значениям столбцов.\n\n```php\n// ...WHERE (`status` = 10) AND (`type` IS NULL) AND (`id` IN (4, 8, 15))\n$query->where([\n    'status' => 10,\n    'type' => null,\n    'id' => [4, 8, 15],\n]);\n```\n\nКак вы можете видеть, построитель запросов достаточно умен, чтобы правильно обрабатывать значения `null` или массивов.\n\nВы также можете использовать подзапросы:\n\n```php\n$userQuery = (new Query())->select('id')->from('user');\n\n// ...WHERE `id` IN (SELECT `id` FROM `user`)\n$query->where(['id' => $userQuery]);\n```\n\nИспользуя формат массива, Yii автоматически привязывает значения массива как параметры, потому в отличие от [строкового формата](#string-format),\nпривязывать параметры вручную не требуется. Обратите внимание, что Yii никогда НЕ экранирует имена столбцов (ключи массива),\nпотому если вы используете как ключ массива переменную, полученную от пользователя без дополнительной проверки, ваше приложение\nстановится подверженным атаке через SQL инъекцию. Чтобы избежать этого, используйте для имён столбцов только проверененные данные,\nпредпочитая фильтрацию по «белому списку». Если вам нужно использовать имя столбца, полученное от пользователя,\nвам может быть полезным раздел [Фильтрация данных](output-data-widgets.md#filtering-data). Ниже приведён пример уязвимого кода:\n\n```php\n// Уязвимый код:\n$column = $request->get('column');\n$value = $request->get('value');\n$query->where([$column => $value]);\n// $value будет безопасно привязано как параметр, но $column – нет!\n```\n\n\n#### Формат операторов <span id=\"operator-format\"></span>\n\nФормат оператора позволяет задавать произвольные условия в программном стиле. Он имеет следующий вид:\n\n```php\n[operator, operand1, operand2, ...]\n```\n\nОперанды могут быть заданы в виде строкового формата, формата массива или формата операторов рекурсивно, в то время\nкак оператор может быть одним из следующих:\n\n- `and`: операнды должны быть объединены с помощью оператора `AND`. Например,\n  `['and', 'id=1', 'id=2']` сгенерирует `id=1 AND id=2`. Если операнд массив,\n  он будет сконвертирован в строку по правилам описанным ниже. Например,\n  `['and', 'type=1', ['or', 'id=1', 'id=2']]` сгенерирует `type=1 AND (id=1 OR id=2)`.\n  Этот метод не производит никакого экранирования.\n\n- `or`: похож на оператор `and` за исключением того, что будет использоваться оператор `OR`.\n\n- `between`: первый операнд должен быть именем столбца, а второй и третий оператор должны быть начальным и конечным\n   значением диапазона. Например, `['between', 'id', 1, 10]` сгенерирует `id BETWEEN 1 AND 10`.\n\n- `not between`: похож на `between` за исключением того, что `BETWEEN` заменяется на `NOT BETWEEN`\n  в сгенерированном условии.\n\n- `in`: первый операнд должен быть столбцом или выражением БД. Второй операнд может быть либо массивом, либо объектом `Query`.\n  Будет сгенерировано условие `IN`. Если второй операнд массив, он будет представлять набор значений, которым может быть\n  равен столбец или выражение БД; Если второй операнд объект `Query`, будет сформирован подзапрос, который будет использован\n  как диапазон для столбца или выражения БД. Например, `['in', 'id', [1, 2, 3]]` сформирует `id IN (1, 2, 3)`.\n  Метод будет правильно экранировать имя столбца и значения диапазона. Оператор `in` также поддерживает составные столбцы.\n  В этом случае, первый операнд должен быть массивом названий столбцов, в то время как операнд 2 должен быть массивом\n  массивов или объектом `Query` представляющим диапазоны для столбцов.\n\n- `not in`: похож на оператор `in`, кроме того что `IN` будет заменён на `NOT IN` в сформированном условии.\n\n- `like`: первый операнд должен быть столбцом или выражением БД, а второй операнд будет строкой или массивом представляющим\n  значения, на которые должны быть похожи столбцы или выражения БД. Например, `['like', 'name', 'tester']`\n  сформирует `name LIKE '%tester%'`. \n  Когда диапазон значений задан в виде массива, несколько `LIKE` утверждений будут сформированы и соединены с помощью `AND`.\n  Например, `['like', 'name', ['test', 'sample']]` сформирует `name LIKE '%test%' AND name LIKE '%sample%'`.\n  Вы также можете передать третий необязательный операнд, для указания способа экранирования специальных символов в значениях.\n  Операнд должен быть представлен массивом соответствия специальных символов их экранированным аналогам. Если этот\n  операнд не задан, то будет использовано соответствие по умолчанию. Вы можете также использовать значение `false`\n  или пустой массив, чтоб указать что значения уже экранированы. Обратите внимание, что при использовании массива соответствия\n  экранирования (или если третий операнд не передан), значения будут автоматически заключены в символы процентов.\n\n  > Note: При использовании PostgreSQL вы можете использовать также [`ilike`](https://www.postgresql.org/docs/8.3/functions-matching.html#FUNCTIONS-LIKE)\n  > вместо `like` для регистронезависимого поиска.\n\n- `or like`: похож на оператор `like`, только утверждения `LIKE` будут объединяться с помощью оператора `OR`, если\n  второй операнд будет представлен массивом.\n\n- `not like`: похож на оператор `like`, только `LIKE` будет заменён на `NOT LIKE` в сгенерированном условии.\n\n- `or not like`: похож на оператор `not like`, только утверждения `NOT LIKE` будут объединены с помощью `OR`.\n\n- `exists`: требует один операнд, который должен быть экземпляром [[yii\\db\\Query]] представляющим подзапрос. Будет\n  сгенерировано выражение `EXISTS (sub-query)`.\n\n- `not exists`: похож на оператор `exists` и сформирует выражение `NOT EXISTS (sub-query)`.\n\n- `>`, `<=`, или другие валидные операторы БД, которые требуют двух операндов: первый операнд должен быть именем\n  столбца, второй операнд это значение. Например, `['>', 'age', 10]` сформирует `age>10`.\n\nИспользуя формат операторов, Yii автоматически привязывает значения для сравнения как параметры, потому в отличие от [строкового формата](#string-format),\nпривязывать параметры вручную не требуется. Обратите внимание, что Yii никогда НЕ экранирует имена столбцов,\nпотому если вы используете как имя столбца переменную, полученную от пользователя без дополнительной проверки, ваше приложение\nстановится подверженным атаке через SQL инъекцию. Чтобы избежать этого, используйте для имён столбцов только проверененные данные,\nпредпочитая фильтрацию по «белому списку». Если вам нужно использовать имя столбца, полученное от пользователя,\nвам может быть полезным раздел [Фильтрация данных](output-data-widgets.md#filtering-data). Ниже приведён пример уязвимого кода:\n\n```php\n// Уязвимый код:\n$column = $request->get('column');\n$value = $request->get('value');\n$query->where(['=', $column, $value]);\n// $value будет безопасно привязано как параметр, но $column – нет!\n```\n\n#### Добавление условий <span id=\"appending-conditions\"></span>\n\nВы можете использовать [[yii\\db\\Query::andWhere()|andWhere()]] или [[yii\\db\\Query::orWhere()|orWhere()]] для добавления\nдополнительных условий. Вы можете использовать эти вызовы несколько раз для добавления нескольких условий.\nНапример,\n\n```php\n$status = 10;\n$search = 'yii';\n\n$query->where(['status' => $status]);\n\nif (!empty($search)) {\n    $query->andWhere(['like', 'title', $search]);\n}\n```\n\nЕсли `$search` не пустое, то будет сформировано следующее условие WHERE:\n\n```sql\nWHERE (`status` = 10) AND (`title` LIKE '%yii%')\n```\n\n\n#### Условия для фильтров <span id=\"filter-conditions\"></span>\n\nКогда условие `WHERE` формируется на основе пользовательского ввода, обычно, хочется проигнорировать не заданные значения.\nНапример, в форме поиска, которая позволяет осуществлять поиск по имени пользователя или email, вы хотели бы игнорировать\nusername/email условие, если пользователь ничего не ввёл в поле ввода. Вы можете достичь этого используя метод\n[[yii\\db\\Query::filterWhere()|filterWhere()]].\n\n```php\n// $username и $email вводит пользователь\n$query->filterWhere([\n    'username' => $username,\n    'email' => $email,\n]);\n```\n\nЕдинственное отличие между [[yii\\db\\Query::filterWhere()|filterWhere()]] и [[yii\\db\\Query::where()|where()]] \nзаключается в игнорировании пустых значений, переданных в условие в [форме массива](#hash-format). Таким образом\nесли `$email` будет пустым, а `$username` нет, то приведённый выше код сформирует условие `WHERE username=:username`.\n\n> Info: значение признаётся пустым, если это `null`, пустой массив, пустая строка или строка состоящая из одних пробельных символов.\n\nТакже вместо [[yii\\db\\Query::andWhere()|andWhere()]] и [[yii\\db\\Query::orWhere()|orWhere()]], вы можете использовать\n[[yii\\db\\Query::andFilterWhere()|andFilterWhere()]] и [[yii\\db\\Query::orFilterWhere()|orFilterWhere()]]\nдля добавления дополнительных условий фильтрации.\n\n\n### [[yii\\db\\Query::orderBy()|orderBy()]] <span id=\"order-by\"></span>\n\nМетод [[yii\\db\\Query::orderBy()|orderBy()]] определяет фрагмент `ORDER BY` SQL выражения. Например,\n\n```php\n// ... ORDER BY `id` ASC, `name` DESC\n$query->orderBy([\n    'id' => SORT_ASC,\n    'name' => SORT_DESC,\n]);\n```\n\nВ данном коде, ключи массива - это имена столбцов, а значения массива - это соответствующее направление сортировки.\nPHP константа `SORT_ASC` определяет сортировку по возрастанию и `SORT_DESC` сортировку по убыванию.\n\nЕсли `ORDER BY` содержит только простые имена столбцов, вы можете определить их с помощью столбцов, также\nкак и при написании обычного SQL. Например,\n\n```php\n$query->orderBy('id ASC, name DESC');\n```\n\n> Note: Вы должны использовать массив для указания `ORDER BY` содержащих выражения БД.\n\nВы можете вызывать [[yii\\db\\Query::addOrderBy()|addOrderBy()]] для добавления столбцов во фрагмент `ORDER BY`.\n\n```php\n$query->orderBy('id ASC')\n    ->addOrderBy('name DESC');\n```\n\n\n### [[yii\\db\\Query::groupBy()|groupBy()]] <span id=\"group-by\"></span>\n\nМетод [[yii\\db\\Query::groupBy()|groupBy()]] определяет фрагмент `GROUP BY` SQL запроса.\n\n```php\n// ... GROUP BY `id`, `status`\n$query->groupBy(['id', 'status']);\n```\n\nЕсли фрагмент `GROUP BY` содержит только простые имена столбцов, вы можете указать их используя строку, также как в\nобычном SQL выражении.\n\n```php\n$query->groupBy('id, status');\n```\n\n> Note: Вы должны использовать массив для указания `GROUP BY` содержащих выражения БД.\n \nВы можете вызывать [[yii\\db\\Query::addGroupBy()|addGroupBy()]] для добавления имён столбцов во фрагмент `GROUP BY`.\nНапример,\n\n```php\n$query->groupBy(['id', 'status'])\n    ->addGroupBy('age');\n```\n\n\n### [[yii\\db\\Query::having()|having()]] <span id=\"having\"></span>\n\nМетод [[yii\\db\\Query::having()|having()]] определяет фрагмент `HAVING` SQL запроса. Он принимает условия, которое\nможет быть определено тем же способом, что и для [where()](#where).\n\n```php\n// ... HAVING `status` = 1\n$query->having(['status' => 1]);\n```\n\nПожалуйста, обратитесь к документации для [where()](#where) для более подробной информации об определении условий.\n\nВы можете вызывать [[yii\\db\\Query::andHaving()|andHaving()]] или [[yii\\db\\Query::orHaving()|orHaving()]] для добавления\nдополнительных условий во фрагмент `HAVING`.\n\n```php\n// ... HAVING (`status` = 1) AND (`age` > 30)\n$query->having(['status' => 1])\n    ->andHaving(['>', 'age', 30]);\n```\n\n\n### [[yii\\db\\Query::limit()|limit()]] и [[yii\\db\\Query::offset()|offset()]] <span id=\"limit-offset\"></span>\n\nМетоды [[yii\\db\\Query::limit()|limit()]] и [[yii\\db\\Query::offset()|offset()]] определяют фрагменты `LIMIT`\nи `OFFSET` SQL запроса.\n \n```php\n// ... LIMIT 10 OFFSET 20\n$query->limit(10)->offset(20);\n```\n\nЕсли вы определяете неправильный limit или offset (например отрицательное значение), они будут проигнорированы.\n\n> Info: Для СУБД, которые не поддерживают `LIMIT` и `OFFSET` (такие как MSSQL), построитель запросов будет\n  генерировать SQL выражения, которые эмулирует поведение `LIMIT`/`OFFSET`.\n\n\n### [[yii\\db\\Query::join()|join()]] <span id=\"join\"></span>\n\nМетод [[yii\\db\\Query::join()|join()]] определяет фрагмент `JOIN` SQL запроса.\n\n```php\n// ... LEFT JOIN `post` ON `post`.`user_id` = `user`.`id`\n$query->join('LEFT JOIN', 'post', 'post.user_id = user.id');\n```\n\nМетод [[yii\\db\\Query::join()|join()]] принимает четыре параметра:\n\n- `$type`: тип объединения, например, `'INNER JOIN'`, `'LEFT JOIN'`.\n- `$table`: имя таблицы, которая должна быть присоединена.\n- `$on`: необязательное условие объединения, то есть фрагмент `ON`. Пожалуйста, обратитесь к документации для\n  [where()](#where) для более подробной информации об определении условий. Отметим, что синтаксис массивов **не работает**\n  для задания условий для столбцов, то есть `['user.id' => 'comment.userId']` будет означать условие, где ID пользователя\n  должен быть равен строке `'comment.userId'`. Вместо этого стоит указывать условие в виде строки `'user.id = comment.userId'`.\n- `$params`: необязательные параметры присоединяемые к условию объединения.\n\nВы можете использовать следующие сокращающие методы для указания `INNER JOIN`, `LEFT JOIN` и `RIGHT JOIN`, в указанном порядке.\n\n- [[yii\\db\\Query::innerJoin()|innerJoin()]]\n- [[yii\\db\\Query::leftJoin()|leftJoin()]]\n- [[yii\\db\\Query::rightJoin()|rightJoin()]]\n\nНапример,\n\n```php\n$query->leftJoin('post', 'post.user_id = user.id');\n```\n\nДля соединения с несколькими таблицами, вызовите вышеуказанные методы несколько раз.\n\nКроме соединения с таблицами, вы можете также присоединять подзапросы. Чтобы это сделать, укажите объединяемый подзапрос\nкак объект [[yii\\db\\Query]].\n\n```php\n$subQuery = (new \\yii\\db\\Query())->from('post');\n$query->leftJoin(['u' => $subQuery], 'u.id = author_id');\n```\n\nВ этом случае вы должны передать подзапросы в массиве и использовать ключи для определения алиасов.\n\n\n### [[yii\\db\\Query::union()|union()]] <span id=\"union\"></span>\n\nМетод [[yii\\db\\Query::union()|union()]] определяет фрагмент `UNION` SQL запроса.\n\n```php\n$query1 = (new \\yii\\db\\Query())\n    ->select(\"id, category_id AS type, name\")\n    ->from('post')\n    ->limit(10);\n\n$query2 = (new \\yii\\db\\Query())\n    ->select('id, type, name')\n    ->from('user')\n    ->limit(10);\n\n$query1->union($query2);\n```\n\nВы можете вызвать [[yii\\db\\Query::union()|union()]] несколько раз для присоединения фрагментов `UNION`. \n\n\n## Методы выборки <span id=\"query-methods\"></span>\n\n[[yii\\db\\Query]] предоставляет целый набор методов для разных вариантов выборки:\n\n- [[yii\\db\\Query::all()|all()]]: возвращает массив строк, каждая из которых это ассоциативный массив пар ключ-значение.\n- [[yii\\db\\Query::one()|one()]]: возвращает первую строку запроса.\n- [[yii\\db\\Query::column()|column()]]: возвращает первый столбец результата.\n- [[yii\\db\\Query::scalar()|scalar()]]: возвращает скалярное значение первого столбца первой строки результата.\n- [[yii\\db\\Query::exists()|exists()]]: возвращает значение указывающее, что выборка содержит результат.\n- [[yii\\db\\Query::count()|count()]]: возвращает результат `COUNT` запроса.\n- Другие методы агрегирования запросов, включая [[yii\\db\\Query::sum()|sum($q)]], [[yii\\db\\Query::average()|average($q)]],\n  [[yii\\db\\Query::max()|max($q)]], [[yii\\db\\Query::min()|min($q)]]. Параметр `$q` обязателен для этих методов и могут\n  содержать либо имя столбца, либо выражение БД.\n\nНапример,\n\n```php\n// SELECT `id`, `email` FROM `user`\n$rows = (new \\yii\\db\\Query())\n    ->select(['id', 'email'])\n    ->from('user')\n    ->all();\n    \n// SELECT * FROM `user` WHERE `username` LIKE `%test%`\n$row = (new \\yii\\db\\Query())\n    ->from('user')\n    ->where(['like', 'username', 'test'])\n    ->one();\n```\n\n> Note: метод [[yii\\db\\Query::one()|one()]] вернёт только первую строку результата запроса. Он НЕ добавляет\n  `LIMIT 1` в генерируемый SQL. Это хорошо и предпочтительно если вы знаете, что запрос вернёт только одну или несколько\n  строк данных (например, при запросе по первичному ключу). Однако если запрос потенциально может вернут много\n  строк данных, вы должны вызвать `limit(1)` для повышения производительности, Например, \n  `(new \\yii\\db\\Query())->from('user')->limit(1)->one()`.\n\nВсе методы выборки могут получать необязательный параметр `$db`, представляющий [[yii\\db\\Connection|соединение с БД]],\nкоторое должно использоваться, чтобы выполнить запрос к БД. Если вы упускаете этот параметр, будет использоваться\n[компонент приложения](structure-application-components.md) `$db`. Ниже приведён ещё один пример использования метода\n[[yii\\db\\Query::count()|count()]]:\n\n```php\n// executes SQL: SELECT COUNT(*) FROM `user` WHERE `last_name`=:last_name\n$count = (new \\yii\\db\\Query())\n    ->from('user')\n    ->where(['last_name' => 'Smith'])\n    ->count();\n```\n\nПри вызове методов выборки [[yii\\db\\Query]], внутри на самом деле проводится следующая работа:\n\n* Вызывается [[yii\\db\\QueryBuilder]] для генерации SQL запроса на основе текущего [[yii\\db\\Query]];\n* Создаёт объект [[yii\\db\\Command]] со сгенерированным SQL запросом;\n* Вызывается выбирающий метод (например [[yii\\db\\Command::queryAll()|queryAll()]]) из [[yii\\db\\Command]] для выполнения SQL запроса и извлечения данных.\n\nИногда вы можете захотеть увидеть или использовать SQL запрос построенный из объекта [[yii\\db\\Query]]. Этой цели можно\nдобиться с помощью следующего кода: \n\n```php\n$command = (new \\yii\\db\\Query())\n    ->select(['id', 'email'])\n    ->from('user')\n    ->where(['last_name' => 'Smith'])\n    ->limit(10)\n    ->createCommand();\n    \n// показать SQL запрос\necho $command->sql;\n// показать привязываемые параметры\nprint_r($command->params);\n\n// возвращает все строки запроса\n$rows = $command->queryAll();\n```\n\n\n### Индексация результатов запроса <span id=\"indexing-query-results\"></span>\n\nПри вызове [[yii\\db\\Query::all()|all()]] возвращается массив строк индексированный последовательными целыми числами.\nИногда вам может потребоваться индексировать его по-другому, например, сделать индекс по указанному столбцу или\nзначением выражения. Вы можете реализовать такое поведение через вызов [[yii\\db\\Query::indexBy()|indexBy()]] перед\nвызовом [[yii\\db\\Query::all()|all()]].\n\n```php\n// возвращает [100 => ['id' => 100, 'username' => '...', ...], 101 => [...], 103 => [...], ...]\n$query = (new \\yii\\db\\Query())\n    ->from('user')\n    ->limit(10)\n    ->indexBy('id')\n    ->all();\n```\n\nДля индексации по значению выражения, передайте анонимную функцию в метод [[yii\\db\\Query::indexBy()|indexBy()]]:\n\n```php\n$query = (new \\yii\\db\\Query())\n    ->from('user')\n    ->indexBy(function ($row) {\n        return $row['id'] . $row['username'];\n    })->all();\n```\n\nАнонимная функция должна принимать параметр `$row`, который содержит текущую строку запроса и должна вернуть скалярное\nзначение, которое будет использоваться как значение индекса для текущей строки.\n\n\n### Пакетная выборка <span id=\"batch-query\"></span>\n\nПри работе с большими объемами данных, методы наподобие [[yii\\db\\Query::all()]] не подходят, потому что они требуют\nзагрузки всех данных в память. Чтобы сохранить требования к памяти минимальными, Yii предоставляет поддержку\nтак называемых пакетных выборок. Пакетная выборка делает возможным курсоры данных и выборку данных пакетами.\n\nПакетная выборка может использоваться следующим образом:\n\n```php\nuse yii\\db\\Query;\n\n$query = (new Query())\n    ->from('user')\n    ->orderBy('id');\n\nforeach ($query->batch() as $users) {\n    // $users это массив из 100 или менее строк из таблицы пользователей\n}\n\n// или если вы хотите перебрать все строки по одной\nforeach ($query->each() as $user) {\n    // $user представляет одну строку из выборки\n}\n```\n\nМетод [[yii\\db\\Query::batch()]] и [[yii\\db\\Query::each()]] возвращает объект [[yii\\db\\BatchQueryResult]], который\nреализует интерфейс `Iterator` и может использоваться в конструкции `foreach`. Во время первой итерации будет выполнен\nSQL запрос к базе данных. Данные будут выбираться пакетами в следующих итерациях. По умолчанию, размер пакета имеет\nразмер 100, то есть при каждой выборке будет выбираться по 100 строк. Вы можете изменить размер пакета, передав\nпервый параметр в метод `batch()` или `each()`.\n\nПо сравнению с [[yii\\db\\Query::all()]], пакетная выборка загружает только по 100 строк данных за раз в память.\nЕсли вы обрабатываете данные и затем сразу выбрасываете их, пакетная выборка может помочь уменьшить использование памяти.\n\nЕсли указать индексный столбец через [[yii\\db\\Query::indexBy()]], в пакетной выборке индекс будет сохраняться.\nНапример,\n\n```php\n$query = (new \\yii\\db\\Query())\n    ->from('user')\n    ->indexBy('username');\n\nforeach ($query->batch() as $users) {\n    // $users индексируется по столбцу \"username\"\n}\n\nforeach ($query->each() as $username => $user) {\n    // ...\n}\n```\n"
  },
  {
    "path": "docs/guide-ru/glossary.md",
    "content": "# A\n\n## alias\n\nAlias - это строка, с помощью которой Yii указывает на класс или директорию, например '@app/vendor'.\n\n## application\n\nПриложение является центральным объектом на протяжении HTTP запроса. Оно содержит несколько компонентов и с ними \nполучает информацию из запроса и отправляет ее для дальнейшей обработки.\n\nОбъект приложения создается в виде Singleton(шаблон проектирования Одиночка) входным скриптом. \nОбъект приложения доступен из любого места через `\\Yii::$app`.\n\n## assets\n\nAsset относится к файлу ресурса. Обычно он содержит JavaScript или CSS код, но может быть чем-то другим, к чему есть доступ по HTTP.\n\n## attribute\n\nАтрибут - это свойство модели (переменная класса или магическое свойство, определенное через `__get()`/`__set()`) в котором хранится **бизнес логика**.\n\n# B\n\n## bundle\n\nBundle, известный как пакет в Yii 1.1, относится к ряду ресурсов и конфигурационному файлу, который описывает зависимости и списки ресурсов.\n\n# C\n\n## configuration\n\nКонфигурация может относиться как к процессу установки свойств объекта либо к конфигурации файла, хранящего настройки объекта или класса объектов.\n\n# E\n\n## extension\n\nРасширения - это набор классов, комплект ресурсов и конфигураций, которые добавляют приложению функциональность.\n\n# I\n\n## installation\n\nУстановка - это процесс подготовки чего-либо к работе либо путем чтения readme файла или выполнением подготовленного сценария. В случае Yii он устанавливает разрешения и необходимые зависимости.\n\n# M\n\n## module\n\nМодуль - это подпрограмма, которая содержит элементы MVC: модели, представления, контроллеры и т.д. Может быть использована без главного приложения. Обычно пробрасывая запросы в модуль вместо обработки контроллером.\n\n# N\n\n## namespace\n\nПространство имен ссылка на [PHP language feature](https://www.php.net/manual/en/language.namespaces.php) который активно используется в Yii 2.\n\n# P\n\n## package\n\n[Смотри bundle](#bundle).\n\n# V\n\n## vendor\n\nVendor - это организация или отдельный разработчик, распространяющие код в форме расширений, модулей или библиотек.\n"
  },
  {
    "path": "docs/guide-ru/helper-array.md",
    "content": "ArrayHelper\n===========\nВдобавок к [богатому набору функций](https://www.php.net/manual/ru/book.array.php)  для работы с массивами, которые есть в самом PHP, хелпер Yii Array предоставляет свои статические функции - возможно они могут быть вам полезны.\n\n\n## Получение значений <span id=\"getting-values\"></span>\n\nИзвлечение значений из массива, объекта или структуры состоящей из них обоих с помощью стандартных средств PHP является довольно скучным занятием. Сначала вам нужно проверить, есть ли соответствующий ключ с помощью `isset`, и если есть – получить, если нет – подставить значение по умолчанию.\n\n```php\nclass User\n{\n    public $name = 'Alex';\n}\n\n$array = [\n    'foo' => [\n        'bar' => new User(),\n    ]\n];\n\n$value = isset($array['foo']['bar']->name) ? $array['foo']['bar']->name : null;\n```\n\nYii предлагает очень удобный метод для таких случаев:\n\n```php\n$value = ArrayHelper::getValue($array, 'foo.bar.name');\n```\n\nПервый аргумент – массив или объект из которого мы извлекаем значение. Второй аргумент определяет как будут извлекаться данные и может выглядеть как один из таких вариантов:\n- Имя ключа массива или свойства объекта, значение которого нужно вернуть\n- Путь к нужному значению, разделенный точками, как в примере выше\n- Callback-функция, возвращающая значение\n\nCallback-функция должна выглядеть примерно так:\n\n```php\n$fullName = ArrayHelper::getValue($user, function ($user, $defaultValue) {\n    return $user->firstName . ' ' . $user->lastName;\n});\n```\n\nТретий необязательный аргумент определяет значение по умолчанию. Если не установлен – равен `null`. Используется так:\n\n```php\n$username = ArrayHelper::getValue($comment, 'user.username', 'Unknown');\n```\n\n\n## Запись значений <span id=\"setting-values\"></span>\n\n```php\n$array = [\n    'key' => [\n        'in' => ['k' => 'value']\n    ]\n];\n\nArrayHelper::setValue($array, 'key.in', ['arr' => 'val']);\n// путь для записи значения в `$array` можно указать как массив\nArrayHelper::setValue($array, ['key', 'in'], ['arr' => 'val']);\n```\n\nВ результате исходное значение `$array['key']['in']` будет перезаписано новым\n\n```php\n[\n    'key' => [\n        'in' => ['arr' => 'val']\n    ]\n]\n```\n\nЕсли путь содержит несуществующий ключ, то он будет создан\n\n```php\n// Если `$array['key']['in']['arr0']` не пустой, то значение будет добавлено в массив\nArrayHelper::setValue($array, 'key.in.arr0.arr1', 'val');\n\n// если необходимо полностью переопределить значение `$array['key']['in']['arr0']`\nArrayHelper::setValue($array, 'key.in.arr0', ['arr1' => 'val']);\n```\n\nРезультатом будет следующим:\n\n```php\n[\n    'key' => [\n        'in' => [\n            'k' => 'value',\n            'arr0' => ['arr1' => 'val']\n        ]\n    ]\n]\n```\n\n\n## Изъять значение из массива <span id=\"removing-values\"></span>\n\nЕсли вы хотите получить значение и тут же удалить его из массива, вы можете использовать метод `remove`\n\n```php\n$array = ['type' => 'A', 'options' => [1, 2]];\n$type = ArrayHelper::remove($array, 'type');\n```\n\nПосле выполнения этого кода переменная `$array` будет содержать `['options' => [1, 2]]`, а в переменной `$type` будет значение `А`. В отличие от метода `getValue`, метод `remove` поддерживает только простое имя ключа.\n\n\n## Проверка наличия ключей <span id=\"checking-existence-of-keys\"></span>\n\n`ArrayHelper::keyExists` работает так же, как и стандартный [array_key_exists](https://www.php.net/manual/ru/function.array-key-exists.php),\nно также может проверять ключи без учёта регистра:\n\n```php\n$data1 = [\n    'userName' => 'Alex',\n];\n\n$data2 = [\n    'username' => 'Carsten',\n];\n\nif (!ArrayHelper::keyExists('username', $data1, false) || !ArrayHelper::keyExists('username', $data2, false)) {\n    echo \"Пожалуйста, укажите имя пользователя.\";\n}\n```\n\n## Извлечение столбцов <span id=\"retrieving-columns\"></span>\n\nЧасто нужно извлечь столбец значений из многомерного массива или объекта. Например, список ID.\n\n```php\n$array = [\n    ['id' => '123', 'data' => 'abc'],\n    ['id' => '345', 'data' => 'def'],\n];\n$ids = ArrayHelper::getColumn($array, 'id');\n```\n\nРезультатом будет `['123', '345']`.\n\nЕсли нужны какие-то дополнительные трансформации или способ получения значения специфический, вторым аргументом может быть анонимная функция:\n\n```php\n$result = ArrayHelper::getColumn($array, function ($element) {\n    return $element['id'];\n});\n```\n\n## Переиндексация массивов <span id=\"reindexing-arrays\"></span>\n\n\nЧтобы проиндексировать массив в соответствии с определенным ключом, используется метод `index`. Входящий массив должен\nбыть многомерным или массивом объектов. Ключом может быть имя ключа вложенного массива, имя свойства объекта или\nанонимная функция, которая будет возвращать значение ключа по переданному элементу индексируемого массива (то есть по\nвложенному массиву или объекту).\n\nЕсли значение ключа равно `null`, то соответствующий элемент массива будет опущен и не попадет в результат.\n\n```php\n$array = [\n    ['id' => '123', 'data' => 'abc'],\n    ['id' => '345', 'data' => 'def'],\n];\n$result = ArrayHelper::index($array, 'id');\n// результат будет таким:\n// [\n//     '123' => ['id' => '123', 'data' => 'abc'],\n//     '345' => ['id' => '345', 'data' => 'def'],\n// ]\n\n// использование анонимной функции\n$result = ArrayHelper::index($array, function ($element) {\n    return $element['id'];\n});\n```\n\n\n\n## Получение пар ключ-значение <span id=\"building-maps\"></span>\n\nДля получения пар ключ-значение из многомерного массива или из массива объектов вы можете использовать метод `map`.\n\nПараметры `$from` и `$to` определяют имена ключей или свойств, которые будут использованы в map. Также третьим необязательным параметром вы можете задать правила группировки.\n\n```php\n$array = [\n    ['id' => '123', 'name' => 'aaa', 'class' => 'x'],\n    ['id' => '124', 'name' => 'bbb', 'class' => 'x'],\n    ['id' => '345', 'name' => 'ccc', 'class' => 'y'],\n);\n\n$result = ArrayHelper::map($array, 'id', 'name');\n// результат будет таким:\n// [\n//     '123' => 'aaa',\n//     '124' => 'bbb',\n//     '345' => 'ccc',\n// ]\n\n$result = ArrayHelper::map($array, 'id', 'name', 'class');\n// результат будет таким:\n// [\n//     'x' => [\n//         '123' => 'aaa',\n//         '124' => 'bbb',\n//     ],\n//     'y' => [\n//         '345' => 'ccc',\n//     ],\n// ]\n```\n\n\n## Многомерная сортировка <span id=\"multidimensional-sorting\"></span>\n\nМетод `multisort` помогает сортировать массивы объектов или вложенные массивы по одному или нескольким ключам. Например:\n\n```php\n$data = [\n    ['age' => 30, 'name' => 'Alexander'],\n    ['age' => 30, 'name' => 'Brian'],\n    ['age' => 19, 'name' => 'Barney'],\n];\nArrayHelper::multisort($data, ['age', 'name'], [SORT_ASC, SORT_DESC]);\n```\n\nПосле сортировки мы получим:\n\n```php\n[\n    ['age' => 19, 'name' => 'Barney'],\n    ['age' => 30, 'name' => 'Brian'],\n    ['age' => 30, 'name' => 'Alexander'],\n];\n```\n\nВторой аргумент, определяющий ключи для сортировки может быть строкой, если это один ключ, массивом, если используются несколько ключей или анонимной функцией, как в примере ниже:\n\n```php\nArrayHelper::multisort($data, function($item) {\n    return isset($item['age']) ? ['age', 'name'] : 'name';\n});\n```\nТретий аргумент определяет способ сортировки – от большего к меньшему или от меньшего к большему. В случае, если мы сортируем по одному ключу, передаем  `SORT_ASC`  или  `SORT_DESC`. Если сортировка осуществляется по нескольким ключам, вы можете назначить направление сортировки для каждого из них с помощью массива.\n\nПоследний аргумент – это флаг, который используется в стандартной функции PHP `sort()`. Посмотреть его возможные значения можно [тут](https://www.php.net/manual/ru/function.sort.php).\n\n\n## Определение типа массива <span id=\"detecting-array-types\"></span>\n\nУдобный способ для определения, является массив индексным или ассоциативным. Вот пример:\n\n```php\n// ключи не определены\n$indexed = ['Qiang', 'Paul'];\necho ArrayHelper::isIndexed($indexed);\n\n// все ключи - строки\n$associative = ['framework' => 'Yii', 'version' => '2.0'];\necho ArrayHelper::isAssociative($associative);\n```\n\n\n## HTML-кодирование и HTML-декодирование значений <span id=\"html-encoding-values\"></span>\n\n\nДля того, чтобы закодировать или раскодировать специальные символы в массиве строк в HTML-сущности, вы можете пользоваться методами ниже:\n\n```php\n$encoded = ArrayHelper::htmlEncode($data);\n$decoded = ArrayHelper::htmlDecode($data);\n```\n\nПо умолчанию кодируются только значения. Если установить второй параметр в `false`, то ключи массива будут так же кодированы. Кодирование использует кодировку приложения, которая может быть изменена с помощью третьего аргумента.\n\n## Слияние массивов <span id=\"merging-arrays\"></span>\n\nСлияние двух или больше массивов в один рекурсивно.\nЕсли каждый массив имеет одинаковый ключ, последний будет перезаписывать предыдущий (в отличие от функции array_merge_recursive).\nРекурсивное слияние проводится когда все массивы имеют элемент одного и того же типа с одним и тем же ключом. Для элементов, ключом которого является значение типа integer, элементы из последнего будут добавлены к предыдущим массивам. Вы можете добавлять дополнительные массивы для слияния третьим, четвертым, пятым (и так далее) параметром.\n\n```php\nArrayHelper::merge($a, $b);\n```\n\n## Получение массива из объекта <span id=\"converting-objects-to-arrays\"></span>\n\nЧасто нужно конвертировать объект в массив. Наиболее распространенный случай – конвертация модели Active Record в массив.\n\n```php\n$posts = Post::find()->limit(10)->all();\n$data = ArrayHelper::toArray($posts, [\n    'app\\models\\Post' => [\n        'id',\n        'title',\n        // ключ в результирующем массиве => имя свойства\n        'createTime' => 'created_at',\n        // ключ в результирующем массиве => анонимная функция\n        'length' => function ($post) {\n            return strlen($post->content);\n        },\n    ],\n]);\n```\n\nПервый аргумент содержит данные, которые вы хотите конвертировать. В нашем случае это модель Active Record `Post`.\n\nВторой аргумент служит для управления процессом конвертации и может быть трех видов:\n\n- просто имя поля\n- пара ключ-значение, где ключ определяет ключ в результирующем массиве, а значение – название поля в модели, откуда берется значение.\n- пара ключ-значение, где в качестве значения передается callback-функция, которая возвращает значение.\n\nРезультат конвертации будет таким:\n\n```php\n[\n    'id' => 123,\n    'title' => 'test',\n    'createTime' => '2013-01-01 12:00AM',\n    'length' => 301,\n]\n```\n\nВы можете определить способ конвертации из объекта в массив по-умолчанию реализовав интерфейс\n[[yii\\base\\Arrayable|Arrayable]] в этом классе\n\n\n## Проверка на присутствие в массиве <span id=\"testing-arrays\"></span>\n\nЧасто необходимо проверить, содержится ли элемент в массиве, или является ли массив подмножеством другого массива.\nК сожалению, PHP-функция `in_array()` не поддерживает подмножества объектов, реализующих интерфейс `\\Traversable`.\n\nДля таких случаев [[yii\\helpers\\ArrayHelper]] предоставляет [[yii\\helpers\\ArrayHelper::isIn()|isIn()]] и\n[[yii\\helpers\\ArrayHelper::isSubset()|isSubset()]]. Методы принимают такие же параметры, что и\n[in_array()](https://www.php.net/manual/ru/function.in-array.php).\n\n```php\n// true\nArrayHelper::isIn('a', ['a']);\n// true\nArrayHelper::isIn('a', new(ArrayObject['a']));\n\n// true\nArrayHelper::isSubset(new(ArrayObject['a', 'c']), new(ArrayObject['a', 'b', 'c'])\n\n```\n\n## Преобразование многомерных массивов <span id=\"flattening-arrays\"></span>\n\nМетод `ArrayHelper::flatten()` позволяет преобразовать многомерный массив в одномерный, объединяя ключи.\n\n### Основное использование\n\nЧтобы преобразовать вложенный массив, просто передайте массив в метод `flatten()`:\n\n```php\n$array = [\n    'a' => [\n        'b' => [\n            'c' => 1,\n            'd' => 2,\n        ],\n        'e' => 3,\n    ],\n    'f' => 4,\n];\n\n$flattenedArray = ArrayHelper::flatten($array);\n// Результат:\n// [\n//     'a.b.c' => 1,\n//     'a.b.d' => 2,\n//     'a.e' => 3,\n//     'f' => 4,\n// ]\n```\n\n### Пользовательский разделитель\n\nВы можете указать пользовательский (т.е. отличный от значения по умолчанию: `.`) разделитель для объединения ключей:\n\n```php\n$array = [\n    'a' => [\n        'b' => [\n            'c' => 1,\n            'd' => 2,\n        ],\n        'e' => 3,\n    ],\n    'f' => 4,\n];\n\n$flattenedArray = ArrayHelper::flatten($array, '_');\n// Результат:\n// [\n//     'a_b_c' => 1,\n//     'a_b_d' => 2,\n//     'a_e' => 3,\n//     'f' => 4,\n// ]\n```\n\n### Обработка специальных символов в ключах\n\nМетод `flatten()` может обрабатывать ключи со специальными символами:\n\n```php\n$array = [\n    'a.b' => [\n        'c.d' => 1,\n    ],\n    'e.f' => 2,\n];\n\n$flattenedArray = ArrayHelper::flatten($array);\n// Результат:\n// [\n//     'a.b.c.d' => 1,\n//     'e.f' => 2,\n// ]\n```\n\n### Смешанные типы данных\n\nМетод `flatten()` работает с массивами, содержащими различные типы данных:\n\n```php\n$array = [\n    'a' => [\n        'b' => 'string',\n        'c' => 123,\n        'd' => true,\n        'e' => null,\n    ],\n    'f' => [1, 2, 3],\n];\n\n$flattenedArray = ArrayHelper::flatten($array);\n// Результат:\n// [\n//     'a.b' => 'string',\n//     'a.c' => 123,\n//     'a.d' => true,\n//     'a.e' => null,\n//     'f.0' => 1,\n//     'f.1' => 2,\n//     'f.2' => 3,\n// ]\n```\n\n### Краевые случаи\n\nМетод `flatten()` обрабатывает различные краевые случаи, такие как пустые массивы и значения, не являющиеся массивами:\n\n```php\n// Пустой массив\n$array = [];\n$flattenedArray = ArrayHelper::flatten($array);\n// Результат: []\n\n// Значение, не являющееся массивом\n$array = 'string';\n$flattenedArray = ArrayHelper::flatten($array);\n// Результат:\n// yii\\base\\InvalidArgumentException: Argument $array must be an array or implement Traversable\n```\n\n### Коллизии ключей\n\nКогда ключи совпадают, метод `flatten()` перезапишет предыдущее значение:\n\n```php\n$array = [\n    'a' => [\n        'b' => 1,\n    ],\n    'a.b' => 2,\n];\n\n$flattenedArray = ArrayHelper::flatten($array);\n// Результат: ['a.b' => 2]\n```\n"
  },
  {
    "path": "docs/guide-ru/helper-html.md",
    "content": "Html-помощник\n=============\n\nКаждое веб-приложение формирует большое количество HTML-разметки. Если разметка статическая, её можно эффективно\nсформировать [смешиванием PHP и HTML в одном файле](https://www.php.net/manual/ru/language.basic-syntax.phpmode.php), но\nкогда разметка динамическая, становится сложно формировать её без дополнительной помощи. Yii предоставляет такую помощь\nв виде Html-помощника, который обеспечивает набор статических методов для обработки часто-используемых HTML тэгов, их\nатрибутов и содержимого.\n\n> Note: Если ваша разметка близка к статической, лучше использовать непосредственно HTML. Нет никакой\n  необходимости в том, чтобы всё подряд оборачивать вызовами Html-помощника.\n\n\n## Основы <span id=\"basics\"></span>\n\nТак как формирование динамической HTML-разметки при помощи конкатенации строк очень быстро вносит хаос в проект, Yii \nпредоставляет набор методов для управления атрибутами тэгов и формирования тэгов на основе этих атрибутов.\n\n\n### Формирование тэгов <span id=\"generating-tags\"></span>\n\nКод формирования тэга выглядит следующим образом:\n\n```php\n<?= Html::tag('p', Html::encode($user->name), ['class' => 'username']) ?>\n```\n\nЗдесь первый аргумент - это название тэга. Второй - содержимое, которое будет заключено между открывающим и закрывающим\nтэгами. Заметьте, что мы используем `Html::encode`. Это связано с тем, что содержимое не экранируется автоматически,\nчтобы можно было по-необходимости использовать чистый HTML. Третий аргумент - это массив настроек для HTML-кода, а\nдругими словами - массив атрибутов для тэга. В этом массиве ключи являются названиями атрибутов, например `class`,\n`href` или `target`, а значения в массиве являются значениями этих атрибутов.\n\nВышеприведённый код сформирует следующую HTML-разметку:\n\n```html\n<p class=\"username\">samdark</p>\n```\n\nВ тех случаях, когда вам необходимо только закрыть или открыть тэг, можно использовать методы `Html::beginTag()` и\n`Html::endTag()`.\n\nДополнительные атрибуты используются во многих методах Html-помощника и в различных виджетах. Во всех этих случаях в\nдело вступают механизмы дополнительной обработки данных, о которых следует знать:\n\n- Если значение равно `null`, соответствующий атрибут не будет выведен.\n- Атрибуты, значения которых имеют тип boolean, будут интерпретированы как\n  [логические атрибуты](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#boolean-attributes).\n- Значения атрибутов будут экранированы с использованием метода [[yii\\helpers\\Html::encode()|Html::encode()]].\n- Если в качестве значения атрибута передан массив, он будет обработан следующим образом:\n\n   * Если атрибут является одним из атрибутов данных, указанных в [[yii\\helpers\\Html::$dataAttributes]], например `data`\n     или `ng`, то будет сформирован список атрибутов по одному для каждого элемента массива. Например, код\n     `'data' => ['id' => 1, 'name' => 'yii']` сформирует `data-id=\"1\" data-name=\"yii\"`; а код \n     `'data' => ['params' => ['id' => 1, 'name' => 'yii'], 'status' => 'ok']` сформирует\n     `data-params='{\"id\":1,\"name\":\"yii\"}' data-status=\"ok\"`. Заметьте, что в последнем примере используется формат JSON\n     для формирования вывода вложенного массива.\n     \n   * Если атрибут НЕ является атрибутом данных, значение будет сконвертировано в формат JSON. Например, код\n     `['params' => ['id' => 1, 'name' => 'yii']` сформирует `params='{\"id\":1,\"name\":\"yii\"}'`.\n\n\n### Формирование CSS классов и стилей <span id=\"forming-css\"></span>\n\nПри формировании атрибутов для HTML-тэгов часто приходится начинать с некоторых атрибутов по умолчанию, которые затем\nнеобходимо изменять. Для того, чтобы добавить или удалить CSS-класс, можно использовать следующий подход:\n\n```php\n$options = ['class' => 'btn btn-default'];\n\nif ($type === 'success') {\n    Html::removeCssClass($options, 'btn-default');\n    Html::addCssClass($options, 'btn-success');\n}\n\necho Html::tag('div', 'Всё получилось!', $options);\n\n// в случае, если $type содержит строку 'success', будет сформирован вывод\n// <div class=\"btn btn-success\">Всё получилось!</div>\n```\n\nМожно указать несколько CSS-классов, используя синтаксис массива:\n\n```php\n$options = ['class' => ['btn', 'btn-default']];\n\necho Html::tag('div', 'Сохранить', $options);\n// выведет '<div class=\"btn btn-default\">Сохранить</div>'\n```\n\nПри добавлении или удалении классов тоже можно использовать массивы:\n\n```php\n$options = ['class' => 'btn'];\n\nif ($type === 'success') {\n    Html::addCssClass($options, ['btn-success', 'btn-lg']);\n}\n\necho Html::tag('div', 'Сохранить', $options);\n// выведет '<div class=\"btn btn-success btn-lg\">Сохранить</div>'\n```\n\n`Html::addCssClass()` предотвращает дублирование классов, поэтому можно не беспокоиться, что какой-либо класс\nбудет добавлен дважды:\n\n```php\n$options = ['class' => 'btn btn-default'];\n\nHtml::addCssClass($options, 'btn-default'); // класс 'btn-default' уже добавлен\n\necho Html::tag('div', 'Сохранить', $options);\n// выведет '<div class=\"btn btn-default\">Сохранить</div>'\n```\n\nЕсли CSS-класс задаётся с помощью массива, можно использовать именованные ключи массива для обозначения логического\nпредназначения класса. В этом случае класс с таким же ключом будет проигнорирован во время использования\n`Html::addCssClass()`:\n\n```php\n$options = [\n    'class' => [\n        'btn',\n        'theme' => 'btn-default',\n    ]\n];\n\nHtml::addCssClass($options, ['theme' => 'btn-success']); // ключ 'theme' уже использован\n\necho Html::tag('div', 'Сохранить', $options);\n// отобразит '<div class=\"btn btn-default\">Сохранить</div>'\n```\n\nCSS-стили могут быть установлены схожим образом с помощью атрибута `style`:\n\n```php\n$options = ['style' => ['width' => '100px', 'height' => '100px']];\n\n// в результате будет style=\"width: 100px; height: 200px; position: absolute;\"\nHtml::addCssStyle($options, 'height: 200px; position: absolute;');\n\n// в результате будет style=\"position: absolute;\"\nHtml::removeCssStyle($options, ['width', 'height']);\n```\n\nПри использовании метода [[yii\\helpers\\Html::addCssStyle()|addCssStyle()]] можно указать массив, пары ключ-значение\nкоторого соответствуют названиям и значениям CSS-свойств, или строку, например `width: 100px; height: 200px;`. Эти два\nформата могут быть преобразованы друг в друга с помощью методов\n[[yii\\helpers\\Html::cssStyleFromArray()|cssStyleFromArray()]] и\n[[yii\\helpers\\Html::cssStyleToArray()|cssStyleToArray()]]. Метод\n[[yii\\helpers\\Html::removeCssStyle()|removeCssStyle()]] принимает на вход массив атрибутов, которые следует удалить.\nЕсли удаляется всего один атрибут, его можно передать строкой.\n\n\n### Экранирование контента <span id=\"encoding-and-decoding-content\"></span>\n\nДля корректного и безопасного отображения контента специальные символы в HTML-коде должны быть экранированы. В чистом\nPHP это осуществляется с помощью функций [htmlspecialchars](https://www.php.net/manual/ru/function.htmlspecialchars.php)\nи [htmlspecialchars_decode](https://www.php.net/manual/ru/function.htmlspecialchars-decode.php). Проблема использования\nэтих функций заключается в том, что приходится указывать кодировку и дополнительные флаги во время каждого вызова.\nПоскольку флаги всё время одинаковы, а кодировка остаётся одной и той же в пределах приложения, Yii в целях\nбезопасности предоставляет два компактных и простых в использовании метода:\n\n```php\n$userName = Html::encode($user->name);\necho $userName;\n\n$decodedUserName = Html::decode($userName);\n```\n\n\n## Формы <span id=\"forms\"></span>\n\nРазметка форм состоит из повторяющихся действий и часто приводит к ошибкам, поэтому есть целый набор методов, которые\nпомогают справиться с этой задачей.\n\n> Note: Рассмотрите возможность использования [[yii\\widgets\\ActiveForm|ActiveForm]], если работаете с моделями и\n  нуждаетесь в валидации данных.\n\n\n### Создание форм <span id=\"creating-forms\"></span>\n\nОткрывающий тэг формы может быть выведен с помощью метода [[yii\\helpers\\Html::beginForm()|beginForm()]] как показано\nниже:\n\n```php\n<?= Html::beginForm(['order/update', 'id' => $id], 'post', ['enctype' => 'multipart/form-data']) ?>\n```\n\nПервый аргумент - это URL-адрес, на который будет отправлена форма. Он может быть задан в виде Yii-маршрута и\nпараметров, подходящих для передачи в метод [[yii\\helpers\\Url::to()|Url::to()]]. Второй аргумент - способ отправки\nданных: по умолчанию это `post`. Третий аргумент - массив атрибутов формы. В данном примере мы изменяем способ\nкодирования данных в POST-запросе на `multipart/form-data`. Это необходимо для загрузки файлов.\n\nЗакрыть тэг формы можно простым кодом:\n\n```php\n<?= Html::endForm() ?>\n```\n\n\n### Кнопки <span id=\"buttons\"></span>\n\nДля формирования кнопок можно использовать следующий код:\n\n```php\n<?= Html::button('Нажми меня!', ['class' => 'teaser']) ?>\n<?= Html::submitButton('Отправить', ['class' => 'submit']) ?>\n<?= Html::resetButton('Сбросить', ['class' => 'reset']) ?>\n```\n\nПервый аргумент во всех трёх методах - это название кнопки, а второй - её атрибуты. Название кнопки не экранируется,\nпоэтому при получении данных от конечных пользователей экранируйте их с помощью метода\n[[yii\\helpers\\Html::encode()|Html::encode()]].\n\n\n### Поля ввода <span id=\"input-fields\"></span>\n\nМетоды формирования полей ввода делятся на две группы. Первые начинаются со слова `active` и называются \"active inputs\",\nа вторые не содержат в своём названии слова `active`. \"Active inputs\" формируют поля ввода, которые получают данные из\nуказанного атрибута модели, в то время как обычные методы формирования полей ввода непосредственно принимают данные.\n\nНаиболее общие методы для формирования полей ввода:\n\n```php\n// тип, название поля ввода, установленное в поле значение, атрибуты поля ввода\n<?= Html::input('text', 'username', $user->name, ['class' => $username]) ?>\n\n// тип, модель, атрибут модели, атрибуты поля ввода\n<?= Html::activeInput('text', $user, 'name', ['class' => $username]) ?>\n```\n\nЕсли вам заранее известен тип поля ввода, удобнее будет пользоваться этими вспомогательными методами:\n\n- [[yii\\helpers\\Html::buttonInput()]]\n- [[yii\\helpers\\Html::submitInput()]]\n- [[yii\\helpers\\Html::resetInput()]]\n- [[yii\\helpers\\Html::textInput()]], [[yii\\helpers\\Html::activeTextInput()]]\n- [[yii\\helpers\\Html::hiddenInput()]], [[yii\\helpers\\Html::activeHiddenInput()]]\n- [[yii\\helpers\\Html::passwordInput()]] / [[yii\\helpers\\Html::activePasswordInput()]]\n- [[yii\\helpers\\Html::fileInput()]], [[yii\\helpers\\Html::activeFileInput()]]\n- [[yii\\helpers\\Html::textarea()]], [[yii\\helpers\\Html::activeTextarea()]]\n\nСигнатура методов для формирования радио-переключателей и чекбоксов немного отличается: \n\n```php\n<?= Html::radio('agree', true, ['label' => 'Я согласен']) ?>\n<?= Html::activeRadio($model, 'agree', ['class' => 'agreement']) ?>\n\n<?= Html::checkbox('agree', true, ['label' => 'Я согласен']) ?>\n<?= Html::activeCheckbox($model, 'agree', ['class' => 'agreement']) ?>\n```\n\nВыпадающие и обычные списки могут быть сформированы следующим образом:\n\n```php\n<?= Html::dropDownList('list', $currentUserId, ArrayHelper::map($userModels, 'id', 'name')) ?>\n<?= Html::activeDropDownList($users, 'id', ArrayHelper::map($userModels, 'id', 'name')) ?>\n\n<?= Html::listBox('list', $currentUserId, ArrayHelper::map($userModels, 'id', 'name')) ?>\n<?= Html::activeListBox($users, 'id', ArrayHelper::map($userModels, 'id', 'name')) ?>\n```\n\nПервый аргумент - это значение атрибута `name` для поля ввода, второй - выбранное в нём значение и, наконец, \nтретий аргумент - это массив, ключами которого является список значений для формирования списка, а значениями массива\nявляются названия пунктов в нём.\n\nЕсли вы хотите сделать в списке доступными для выбора несколько вариантов, хорошим выбором будет список чекбоксов:\n\n```php\n<?= Html::checkboxList('roles', [16, 42], ArrayHelper::map($roleModels, 'id', 'name')) ?>\n<?= Html::activeCheckboxList($user, 'role', ArrayHelper::map($roleModels, 'id', 'name')) ?>\n```\n\nЕсли нет, используйте радио-переключатель:\n\n```php\n<?= Html::radioList('roles', [16, 42], ArrayHelper::map($roleModels, 'id', 'name')) ?>\n<?= Html::activeRadioList($user, 'role', ArrayHelper::map($roleModels, 'id', 'name')) ?>\n```\n\n\n### Тэги label и отображение ошибок <span id=\"labels-and-errors\"></span>\n\nТак же как и для полей ввода, есть два метода формирования тэгов label для форм. Есть \"active label\", считывающий\nданные из модели и обычный тэг \"label\", принимающий на вход непосредственно сами данные:\n\n```php\n<?= Html::label('Имя пользователя', 'username', ['class' => 'label username']) ?>\n<?= Html::activeLabel($user, 'username', ['class' => 'label username'])\n```\n\nДля отображения общего списка ошибок формы на основе модели или моделей можно использовать следующий подход:\n\n```php\n<?= Html::errorSummary($posts, ['class' => 'errors']) ?>\n```\n\nДля отображения одной отдельной ошибки:\n\n```php\n<?= Html::error($post, 'title', ['class' => 'error']) ?>\n```\n\n\n### Атрибуты name и value для полей ввода <span id=\"input-names-and-values\"></span>\n\nТакже имеются методы для получения значений атрибутов id, name и value для полей ввода, сформированных на основе\nмоделей. Эти методы используются в основном внутренними механизмами, но иногда могут оказаться подходящими и для прямого\nиспользования:\n\n```php\n// Post[title]\necho Html::getInputName($post, 'title');\n\n// post-title\necho Html::getInputId($post, 'title');\n\n// моё первое сообщение\necho Html::getAttributeValue($post, 'title');\n\n// $post->authors[0]\necho Html::getAttributeValue($post, '[0]authors[0]');\n```\n\nВ вышеприведённом коде первый аргумент - это модель, а второй аргумент - выражение для поиска атрибута модели. В самом\nпростом случае оно представляет собой название атрибута модели, а вообще это может быть название, которому предшествует\n(и/или после которого следует) индекс массива. Такой формат используется в основном для табличного ввода данных:\n\n- `[0]content` используется для табличного ввода данных, чтобы указать на атрибут \"content\" первой модели табличного\n  ввода;\n- `dates[0]` указывает на первый элемент массива, с помощью которого задан атрибут модели \"dates\";\n- `[0]dates[0]` указывает на первый элемент массива, с помощью которого задан атрибут \"dates\" первой модели табличного\n  ввода.\n\nДля того, чтобы получить название атрибута модели в чистом виде (без суффиксов и префиксов), можно использовать\nследующий подход:\n\n```php\n// dates\necho Html::getAttributeName('dates[0]');\n```\n\n\n## Подключение встроенных стилей и скриптов <span id=\"styles-and-scripts\"></span>\n\nДля формирования встроенных скриптов и стилей есть два метода:\n\n```php\n<?= Html::style('.danger { color: #f00; }', ['media' => 'print']) ?>\n\nРезультатом будет:\n\n<style media=\"print\">.danger { color: #f00; }</style>\n\n\n<?= Html::script('alert(\"Привет!\");') ?>\n\nРезультатом будет:\n\n<script>alert(\"Привет!\");</script>\n```\n\nЕсли вы хотите подключить внешний CSS-файл:\n\n```php\n<?= Html::cssFile('@web/css/ie5.css', ['condition' => 'IE 5']) ?>\n\nВ результате получится:\n\n<!--[if IE 5]>\n    <link href=\"https://example.com/css/ie5.css\" />\n<![endif]-->\n```\n\nПервый аргумент - URL-адрес. Второй - массив настроек. Помимо обычных настроек можно указать следующие:\n\n- `condition` для оборачивания тэга `<link` с помощью условных комментариев с определённым условием. Надеемся, что вам\n  никогда не понадобятся условные комментарии ;)\n- `noscript` может быть установлен в значение `true`, чтобы обернуть тэг `<link` с помощью тэга `<noscript>`, таким\n  образом скрипт будет подключён только в том случае, если у пользователя в браузере нет поддержки JavaScript или же \n  пользователь сам отключил его.\n\nДля подключения JavaScript-файла используйте код:\n\n```php\n<?= Html::jsFile('@web/js/main.js') ?>\n```\n\nКак и в случае с CSS, первый аргумент указывает ссылку на файл, который должен быть подключен. Настройки задаются во\nвтором аргументе. Можно указать настройку `condition` таким же образом, каким она указывается для метода `cssFile`.\n\n\n## Ссылки <span id=\"hyperlinks\"></span>\n\nСуществует удобный метод формирования ссылок:\n\n```php\n<?= Html::a('Профиль', ['user/view', 'id' => $id], ['class' => 'profile-link']) ?>\n```\n\nПервый аргумент - это текст ссылки. Он не экранируется, поэтому при использовании данных, полученных от конечных \nпользователей, необходимо экранировать его с помощью `Html::encode()`. Второй аргумент - это содержимое атрибута `href`\nтэга `<a`. Смотрите [Url::to()](helper-url.md) для получения подробностей относительно значений, которые могут быть\nпереданы в качестве второго аргумента. Третий аргумент - массив атрибутов для тэга.\n\nЕсли нужно сформировать ссылку `mailto`, можно использовать следующий подход:\n\n```php\n<?= Html::mailto('Обратная связь', 'admin@example.com') ?>\n```\n\n\n## Изображения <span id=\"images\"></span>\n\nДля формирования тэга изображения используйте следующий код:\n\n```php\n<?= Html::img('@web/images/logo.png', ['alt' => 'Наш логотип']) ?>\n\nв результате получится:\n\n<img src=\"https://example.com/images/logo.png\" alt=\"Наш логотип\" />\n```\n\nПомимо [псевдонимов](concept-aliases.md) первый аргумент может принимать маршруты, параметры и URL-адреса таким же образом\nкак и метод [Url::to()](helper-url.md).\n\n\n## Списки <span id=\"lists\"></span>\n\nНеупорядоченные списки могут быть сформированы следующим образом:\n\n```php\n<?= Html::ul($posts, ['item' => function($item, $index) {\n    return Html::tag(\n        'li',\n        $this->render('post', ['item' => $item]),\n        ['class' => 'post']\n    );\n}]) ?>\n```\n\nДля формирования упорядоченных списков используйте `Html::ol()`.\n"
  },
  {
    "path": "docs/guide-ru/helper-json.md",
    "content": "Json хелпер\n===========\n\nJson хелпер предоставляет набор статических методов для кодирования и декодирования JSON.\nОн обрабатывает ошибки кодирования, а метод [[yii\\helpers\\Json::encode()]] не кодирует JavaScript-выражения,\nпредставленные в виде объектов [[yii\\web\\JsExpression]].\nПо умолчанию кодирование выполняется с опциями `JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE`.\nПодробнее см. [PHP:json_encode](https://www.php.net/manual/ru/function.json-encode.php).\n\n## Форматированный вывод <span id=\"pretty-print\"></span>\n\nПо умолчанию [[yii\\helpers\\Json::encode()]] выводит неотформатированный JSON (без пробелов).\nЧтобы сделать его удобочитаемым, можно включить форматированный вывод.\n\n> Note: Форматированный вывод полезен при отладке во время разработки, но не рекомендуется в production-окружении.\n\nЧтобы включить форматированный вывод в одном вызове, укажите соответствующую опцию:\n\n```php\n$data = ['a' => 1, 'b' => 2];\n$json = yii\\helpers\\Json::encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);\n```\n\nТакже можно включить форматированный вывод глобально. Например, в конфигурации или в index.php:\n\n```php\nyii\\helpers\\Json::$prettyPrint = YII_DEBUG; // форматированный вывод в режиме отладки\n```\n"
  },
  {
    "path": "docs/guide-ru/helper-overview.md",
    "content": "Хелперы\n=======\n\n> Note: Этот раздел находиться в стадии разработки.\n\nYii предоставляет много классов, которые помогают упростить общие задачи программирования, такие как манипуляция со строками или массивами, генерация HTML-кода, и так далее. Все helper-классы организованы в рамках пространства имен `yii\\helpers` и являются статическими классами\n (это означает, что они содержат в себе только статические свойства и методы, и объекты статического класса создать нельзя).\n\nВы можете использовать helper-класс с помощью вызова одного из статических методов, как показано ниже:\n\n```php\nuse yii\\helpers\\Html;\n\necho Html::encode('Test > test');\n```\n\n> Note: Помощь в [настройке helper-классов](#customizing-helper-classes), в Yii каждый основной helper состоит из двух классов: базовый класс (например, `BaseArrayHelper`) и конкретный класс (например, `ArrayHelper`).\n  Когда вы используете helper, вы должны использовать только конкретные версии классов и никогда не использовать базовые классы.\n\n\nВстроенные хелперы\n------------------\n\nВ этой версии Yii предоставляются следующие основные helper-классы:\n\n- [ArrayHelper](helper-array.md)\n- Console\n- FileHelper\n- FormatConverter\n- [Html](helper-html.md)\n- HtmlPurifier\n- Imagine (provided by yii2-imagine extension)\n- Inflector\n- Json\n- Markdown\n- StringHelper\n- [Url](helper-url.md)\n- VarDumper\n\n\nНастройка хелперов <span id=\"customizing-helper-classes\"></span>\n--------------------------\n\nДля настройки основных helper-классов (например, [[yii\\helpers\\ArrayHelper]]), вы должны создать расширяющийся класс из помощников соответствующих базовых классов (например, [[yii\\helpers\\BaseArrayHelper]]) и дать похожее название вашему классу с соответствующим конкретному классу (например, [[yii\\helpers\\ArrayHelper]]), в том числе его пространство имен. Тогда созданный класс заменит оригинальную реализацию в фреймворке.\n\nВ следующих примерах показывается как настроить метод [[yii\\helpers\\ArrayHelper::merge()|merge()]]\n[[yii\\helpers\\ArrayHelper]] класса:\n\n```php\n<?php\n\nnamespace yii\\helpers;\n\nclass ArrayHelper extends BaseArrayHelper\n{\n    public static function merge($a, $b)\n    {\n        // ваша собственная реализация\n    }\n}\n```\n\nСохраните ваш класс в файле с именем `ArrayHelper.php`. Файл должен находиться в другой директории, например `@app/components`.\n\nДалее, в приложении [входной скрипт](structure-entry-scripts.md), добавьте следующую строчку кода\nпосле подключения `yii.php` файла, которая сообщит [автозагрузка классов Yii](concept-autoloading.md) загрузить\nваш класс вместо оригинального helper-класса фреимворка:\n\n```php\nYii::$classMap['yii\\helpers\\ArrayHelper'] = '@app/components/ArrayHelper.php';\n```\n\nОбратите внимание что пользовательская настройка helper-классов полезна только, если вы хотите изменить поведение существующей функции helper-классов. Если вы хотите добавить дополнительные функции, для использования в вашем приложении, будет лучше создать отдельный helper.\n"
  },
  {
    "path": "docs/guide-ru/helper-url.md",
    "content": "Url\n==========\n\nХелпер Url предоставляет набор статических методов для управления URL.\n\n\n## Получение общих URL <span id=\"getting-common-urls\"></span>\n\nВы можете использовать два метода получения общих URL: домашний URL (Home) и базовый URL (Base) текущего запроса.\nИспользуйте следующий код, чтобы получить домашний URL:\n\n```php\n$relativeHomeUrl = Url::home();\n$absoluteHomeUrl = Url::home(true);\n$httpsAbsoluteHomeUrl = Url::home('https');\n```\n\nЕсли вы не передали параметров, то получите относительный URL. Вы можете передать `true`, чтобы получить абсолютный URL\nдля текущего протокола или явно указать протокол (`https`, `http`).\n\nЧтобы получить базовый URL текущего запроса:\n\n```php\n$relativeBaseUrl = Url::base();\n$absoluteBaseUrl = Url::base(true);\n$httpsAbsoluteBaseUrl = Url::base('https');\n```\n\nЕдинственный параметр данного метода работает так же как и `Url::home()`.\n\n## Создание URL <span id=\"creating-urls\"></span>\n\nЧтобы создать URL для соответствующего роута, используйте метод `Url::toRoute()`. Метод использует [[\\yii\\web\\UrlManager]].\nДля того чтобы создать URL:\n\n```php\n$url = Url::toRoute(['product/view', 'id' => 42]);\n```\n \nВы можете задать роут строкой, например, `site/index`. А также вы можете использовать массив, если хотите задать\nдополнительные параметры запроса для URL. Формат массива должен быть следующим:\n\n```php\n// сгенерирует: /index.php?r=site/index&param1=value1&param2=value2\n['site/index', 'param1' => 'value1', 'param2' => 'value2']\n```\n\nЕсли вы хотите создать URL с якорем, то вы можете использовать параметр массива с ключом `#`. Например:\n\n```php\n// сгенерирует: /index.php?r=site/index&param1=value1#name\n['site/index', 'param1' => 'value1', '#' => 'name']\n```\n\nРоут может быть и абсолютным, и относительным. Абсолютный URL начинается со слеша (например, `/site/index`),\nотносительный - без (например, `site/index` or `index`). Относительный URL будет сконвертирован в абсолютный по следующим\nправилам:\n\n- Если роут пустая строка, то будет использовано текущее значение [[\\yii\\web\\Controller::route|route]];\n- Если роут не содержит слешей (например, `index`), то он будет считаться экшеном текущего контроллера и будет определен\n  с помощью [[\\yii\\web\\Controller::uniqueId]];\n- Если роут начинается не со слеша (например, `site/index`), то он будет считаться относительным роутом текущего модуля\n  и будет определен с помощью [[\\yii\\base\\Module::uniqueId|uniqueId]].\n\nНачиная с версии 2.0.2, вы можете задавать роуты с помощью [псевдонимов](concept-aliases.md). В этом случае сначала\nпсевдоним будет сконвертирован в соответствующий роут, который будет преобразован в абсолютный в соответствии с вышеописанными\nправилами.\n\nПримеры использования метода:\n\n```php\n// /index.php?r=site/index\necho Url::toRoute('site/index');\n\n// /index.php?r=site/index&src=ref1#name\necho Url::toRoute(['site/index', 'src' => 'ref1', '#' => 'name']);\n\n// /index.php?r=post/edit&id=100     псевдоним \"@postEdit\" задан как \"post/edit\"\necho Url::toRoute(['@postEdit', 'id' => 100]);\n\n// https://www.example.com/index.php?r=site/index\necho Url::toRoute('site/index', true);\n\n// https://www.example.com/index.php?r=site/index\necho Url::toRoute('site/index', 'https');\n```\n\nДругой метод `Url::to()` очень похож на [[toRoute()]]. Единственное отличие: входным параметром должен быть массив.\nЕсли будет передана строка, то она будет воспринята как URL.\n\nПервый аргумент может быть:\n\n- массивом: будет вызван [[toRoute()]], чтобы сгенерировать URL. Например: `['site/index']`, `['post/index', 'page' => 2]`.\n  В разделе [[toRoute()]] подробно описано как задавать роут;\n- Строка, начинающаяся с `@`, будет обработана как псевдоним. Будет возвращено соответствующее значение псевдонима;\n- Пустая строка: вернет текущий URL;\n- Обычная строка: вернет строку без изменений.\n\nКогда у метода задан второй параметр `$scheme` (строка или `true`), то сгенерированный URL будет с протоколом\n(полученным из [[\\yii\\web\\UrlManager::hostInfo]]). Если в `$url` указан протокол, то его значение будет заменено.\n\nПример использования:\n\n```php\n// /index.php?r=site/index\necho Url::to(['site/index']);\n\n// /index.php?r=site/index&src=ref1#name\necho Url::to(['site/index', 'src' => 'ref1', '#' => 'name']);\n\n// /index.php?r=post/edit&id=100     псевдоним \"@postEdit\" задан как \"post/edit\"\necho Url::to(['@postEdit', 'id' => 100]);\n\n// Текущий URL\necho Url::to();\n\n// /images/logo.gif\necho Url::to('@web/images/logo.gif');\n\n// images/logo.gif\necho Url::to('images/logo.gif');\n\n// https://www.example.com/images/logo.gif\necho Url::to('@web/images/logo.gif', true);\n\n// https://www.example.com/images/logo.gif\necho Url::to('@web/images/logo.gif', 'https');\n```\n\nНачиная с версии 2.0.3, вы можете использовать [[yii\\helpers\\Url::current()]], чтобы создавать URL на основе текущего\nзапрошенного роута и его GET-параметров. Вы можете изменить, удалить или добавить новые GET-параметры, передав в метод\nпараметр `$params`. Например:\n\n```php\n// предположим $_GET = ['id' => 123, 'src' => 'google'], а текущий роут \"post/view\"\n\n// /index.php?r=post/view&id=123&src=google\necho Url::current();\n\n// /index.php?r=post/view&id=123\necho Url::current(['src' => null]);\n// /index.php?r=post/view&id=100&src=google\necho Url::current(['id' => 100]);\n```\n\n\n## Запоминание URL <span id=\"remember-urls\"></span>\n\nСуществуют задачи, когда вам необходимо запомнить URL и потом использовать его в процессе одного или нескольких\nпоследовательных запросов. Это может быть достигнуто следующим образом:\n\n```php\n// Запомнить текущий URL\nUrl::remember();\n\n// Запомнить определенный URL. Входные параметры смотрите на примере Url::to().\nUrl::remember(['product/view', 'id' => 42]);\n\n// Запомнить URL под определенным именем\nUrl::remember(['product/view', 'id' => 42], 'product');\n```\n\nВ следующем запросе мы можем получить сохраненный URL следующим образом:\n\n```php\n$url = Url::previous();\n$productUrl = Url::previous('product');\n```\n                        \n## Проверить относительность URL <span id=\"checking-relative-urls\"></span>\n\nЧтобы проверить относительный URL или нет (например, если в нем не содержится информации о хосте), вы можете использовать\nследующий код:\n\n```php\n$isRelative = Url::isRelative('test/it');\n```\n"
  },
  {
    "path": "docs/guide-ru/images/application-structure.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.13-->\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d0\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key for=\"graphml\" id=\"d7\" yfiles.type=\"resources\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d0\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"872.1807999999999\" y=\"-14.764159999999947\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"31.9375\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"83.330078125\" x=\"8.3349609375\" y=\"1.53125\">компонент\nприложения<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"-91.97375999999994\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"31.9375\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"56.798828125\" x=\"21.6005859375\" y=\"1.53125\">входной\nскрипт<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"-14.764159999999947\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"83.494140625\" x=\"8.2529296875\" y=\"8.515625\">приложение<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"62.44544000000004\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"78.724609375\" x=\"10.6376953125\" y=\"8.515625000000007\">контроллер<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"872.1807999999999\" y=\"62.44544000000005\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"51.408203125\" x=\"24.2958984375\" y=\"8.515625\">фильтр<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"532.664\" y=\"23.901600000000087\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"50.53515625\" x=\"24.732421875\" y=\"8.515625\">модуль<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"618.4047999999991\" y=\"139.65504000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"27.16796875\" x=\"36.416015625\" y=\"8.515625\">вид<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"786.161599999999\" y=\"139.65504000000004\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"50.81640625\" x=\"24.591796875\" y=\"8.515625\">модель<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n8\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"532.664\" y=\"216.86464000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"52.3515625\" x=\"23.82421875\" y=\"8.515625\">виджет<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n9\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"216.86464000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"31.9375\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.328125\" x=\"19.3359375\" y=\"1.53125\">пакет\nресурсов<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n2\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"23.3125\" x=\"2.369325683593729\" y=\"-25.50056340427648\">1:1<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.025600000000054\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.17992697197425173\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n0\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-49.11136368282337\" y=\"-23.00997508216852\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.025599999999994\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5470891790487767\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n5\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"15.0445484043222\" y=\"30.756096771861735\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"44.894496863129426\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.25416574623968574\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n3\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"2.6225397460937074\" y=\"-25.9149367156797\">1..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"15.254400000000032\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.2090245199765919\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n3\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-57.41127147104078\" y=\"-63.734752657990555\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"43.47658308018222\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.881412889692355\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n5\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"582.664\" y=\"-3.598399999999913\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-18.037919628906252\" y=\"-42.33644914245597\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"16.483200000000004\" distanceToCenter=\"true\" position=\"right\" ratio=\"5.822600000000001\" segment=\"-2\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n6\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-12.453120279866653\" y=\"-24.611373055849555\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"11.385360101663515\" distanceToCenter=\"true\" position=\"left\" ratio=\"0.10670293331985262\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e7\" source=\"n7\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-9.3884511336189\" y=\"-25.947659244081734\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.669798038586146\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.1670192242704811\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e8\" source=\"n9\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-9.871202356268668\" y=\"-25.673070518330803\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.15599378957306\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.15481212573620068\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e9\" source=\"n8\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-15.868632344503453\" y=\"-23.891138042957834\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"13.24330808446041\" distanceToCenter=\"true\" position=\"left\" ratio=\"0.05506723556297327\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e10\" source=\"n4\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-47.487035253906356\" y=\"-22.600377199706998\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"13.616000000000007\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e11\" source=\"n9\" target=\"n8\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-44.441396598124356\" y=\"-19.52837428222651\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.543999999999983\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.4117077892835431\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e12\" source=\"n7\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-44.9806249718132\" y=\"-19.528379555664003\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.543999999999983\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.4531592446547025\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d7\">\n    <y:Resources/>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-ru/images/rbac-access-check-1.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-ru/images/rbac-access-check-2.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-ru/images/rbac-access-check-3.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-ru/images/rbac-hierarchy-1.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n5\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-ru/images/rbac-hierarchy-2.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-ru/images/request-lifecycle.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.13-->\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d0\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key for=\"graphml\" id=\"d7\" yfiles.type=\"resources\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d0\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"70.13700103759766\" width=\"56.558998107910156\" x=\"198.38633947503078\" y=\"261.099458694458\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"26.279499053955078\" y=\"74.13700103759766\">\n            <y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"-0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"0.5\" offsetX=\"0.0\" offsetY=\"4.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"91.193359375\" x=\"-95.193359375\" y=\"26.084125518798828\">пользователь<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.5\" labelRatioY=\"0.0\" nodeRatioX=\"-0.5\" nodeRatioY=\"0.0\" offsetX=\"-4.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"637.1271178722382\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"50.81640625\" x=\"34.091796875\" y=\"13.515625\">модель<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"46.887996673583984\" width=\"39.527000427246094\" x=\"828.8018617630005\" y=\"544.9831195354463\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"84.220703125\" x=\"-22.346851348876953\" y=\"-29.990825653076172\">база данных<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-12.022075653076172\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"17.763500213623047\" y=\"21.443998336791992\">\n            <y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"713.6271178722382\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"27.16796875\" x=\"45.916015625\" y=\"13.515625\">вид<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n4\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"376.1217568159104\" width=\"219.27949905395508\" x=\"527.4919853210449\" y=\"408.8421789169311\"/>\n              <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.4609375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"219.27949905395508\" x=\"0.0\" y=\"0.0\">контроллер</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"2\" leftF=\"1.7335329055786133\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"412.2765645980835\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.4609375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"65.201171875\" x=\"-7.6005859375\" y=\"0.0\">Folder 1</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n4:\">\n        <node id=\"n4::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"445.3031164169311\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"125.96875\" x=\"12.295124053955078\" y=\"6.015625\">создание действия<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n4::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"45.0\" width=\"159.91864204406738\" x=\"558.5326642990112\" y=\"521.8166599273682\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"31.9375\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"75.431640625\" x=\"42.24350070953369\" y=\"6.53125\">наложение\nфильтров<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"diamond\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n4::n2\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"161.79775536060333\" width=\"187.54596614837646\" x=\"544.2255182266235\" y=\"608.1661803722382\"/>\n                  <y:Fill color=\"#99336635\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#993366\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.4609375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#FFFFFF\" visible=\"true\" width=\"187.54596614837646\" x=\"0.0\" y=\"0.0\">действие</y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"4\" bottomF=\"3.8368178606033325\" left=\"4\" leftF=\"3.9869680404663086\" right=\"3\" rightF=\"3.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.4609375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"65.201171875\" x=\"-7.6005859375\" y=\"0.0\">Folder 3</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n4::n2:\">\n            <node id=\"n4::n2::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"644.6271178722382\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"111.09765625\" x=\"19.730670928955078\" y=\"6.015625\">загрузка модели<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n4::n2::n1\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"721.1271178722382\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"97.333984375\" x=\"26.612506866455078\" y=\"6.015625\">создание вида<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n      </graph>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"96.0\" x=\"189.47636032104495\" y=\"713.6271178722382\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"39.77734375\" x=\"28.111328125\" y=\"13.515625\">ответ<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"254.50096702575684\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"47.13671875\" x=\"35.931640625\" y=\"13.515625\">запрос<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"145.154203414917\" width=\"219.27949905395508\" x=\"527.4919853210449\" y=\"222.81551361083984\"/>\n              <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.4609375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"219.27949905395508\" x=\"0.0\" y=\"0.0\">приложение</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"21\" leftF=\"20.720500946044922\" right=\"18\" rightF=\"18.0\" top=\"2\" topF=\"1.7557659149169922\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.4609375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"65.201171875\" x=\"-7.6005859375\" y=\"0.0\">Folder 2</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n7:\">\n        <node id=\"n7::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"262.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"31.9375\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"87.947265625\" x=\"31.305866241455078\" y=\"-0.96875\">определение\nмаршрута<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n7::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"322.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"31.9375\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"86.078125\" x=\"32.24043655395508\" y=\"-0.96875\">создание\nконтроллера<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <node id=\"n8\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"142.4296875\" width=\"160.59051704406738\" x=\"312.9619140625\" y=\"224.57127952575684\"/>\n              <y:Fill color=\"#FFCC0024\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FFCC00\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.4609375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"160.59051704406738\" x=\"0.0\" y=\"0.0\"> входной скрипт</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"1\" leftF=\"0.9186420440673828\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"225.33495140075684\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.4609375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"65.201171875\" x=\"-7.6005859375\" y=\"0.0\">Folder 4</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n8:\">\n        <node id=\"n8::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"129.0\" x=\"329.2164936065674\" y=\"262.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"31.9375\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"96.63671875\" x=\"16.181640625\" y=\"-0.96875\">загрузка\nконфигурации<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n8::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"129.0\" x=\"329.2164936065674\" y=\"322.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"129.671875\" x=\"-0.3359375\" y=\"6.015625\">запуск приложения<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <edge id=\"e0\" source=\"n2\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n5\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-10.81052179205907\" sy=\"-22.46484374999998\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"28.000006009454637\" y=\"-193.20499098300934\">\n            <y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"19.26953125\" x=\"-9.634759615545363\" y=\"-200.18936598300934\">11<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n6\" target=\"n7::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.634765625\" x=\"-30.24242594828081\" y=\"-9.983417510986328\">3<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"0.9990329742431641\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.25395048761528816\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n7::e0\" source=\"n7::n0\" target=\"n7::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n0\" target=\"n8\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"-79.9882439360955\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.634765625\" x=\"36.12034335201736\" y=\"-9.169280329692242\">1<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n7::n1\" target=\"n4\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"2.408647025755731\" ty=\"-181.21658369302781\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.634765625\" x=\"-5.4386998761101495\" y=\"19.43622140884395\">4<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n3\" target=\"n4::n2::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.634765625\" x=\"-30.80765317377586\" y=\"-8.98439335823059\">9<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.26448415477215953\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n4::n2::n1\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"-2.2737367544323206E-13\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"19.26953125\" x=\"-93.53556463732264\" y=\"-8.984393358230705\">10<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.2786124840137136\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e7\" source=\"n8::n1\" target=\"n7\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"55.750299312644984\" sy=\"0.355224609375\" tx=\"-109.63961141576266\" ty=\"42.066115379333496\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.634765625\" x=\"28.845541954040527\" y=\"-8.93880986215106\">2<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e8\" source=\"n1\" target=\"n4::n2::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.634765625\" x=\"-31.95652327480775\" y=\"-9.98439335823059\">8<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"1.0\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.2858946861565087\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e9\" source=\"n4::n1\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-79.9882439360951\" sy=\"2.2737367544323206E-13\" tx=\"24.24112858184396\" ty=\"-1.4999999999999991\">\n            <y:Point x=\"261.71748890288893\" y=\"544.3166599273684\"/>\n          </y:Path>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.634765625\" x=\"-83.69880788074641\" y=\"-8.984365463256609\">6<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.23459266172695797\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::e0\" source=\"n4::n0\" target=\"n4::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFEFD6\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.634765625\" x=\"-5.817401885986328\" y=\"4.999985313415493\">5<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.0\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::e1\" source=\"n4::n1\" target=\"n4::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"-81.01828954355172\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFEFD6\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.634765625\" x=\"-5.926145785867902\" y=\"5.063791275024414\">7<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.0\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::n2::e0\" source=\"n4::n2::n0\" target=\"n4::n2::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n8::e0\" source=\"n8::n0\" target=\"n8::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d7\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"66px\" viewBox=\"0 0 57 66\" enable-background=\"new 0 0 57 66\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3799\" y1=\"-2276.8809\" x2=\"27.6209\" y2=\"-2306.6792\" gradientTransform=\"matrix(1 0 0 -1 0.2803 -2252.9199)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_13_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t&lt;path fill=\"#2068A3\" stroke=\"#2068A3\" d=\"M28.106,33.487c-8.112,0-12.688,4.312-12.688,10.437c0,7.422,12.688,10.438,12.688,10.438\n\t\ts14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.487,28.106,33.487z M26.288,53.051c0,0-7.135-2.093-8.805-7.201\n\t\tc-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"14.2417\" cy=\"9.1006\" r=\"53.247\" gradientTransform=\"matrix(1 0 0 -1 0.04 65.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#74AEEE\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#2068A3\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#2068A3\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.022,7.807-14.022,7.807s-10.472-2.484-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path fill=\"#5491CF\" stroke=\"#2068A3\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397c-0.514,1.027-1.669,4.084-1.669,5.148\n\t\tc0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.335-2.36c-3.601-1.419-4.071-3.063-5.89-4.854\n\t\tC12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t&lt;path fill=\"#5491CF\" stroke=\"#2068A3\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617c0.516,1.025,3.617,3.693,3.617,6.617\n\t\tc0,5.186-10.27,8.576-16.698,9.145c1.429,4.938,11.372,1.293,13.804-0.313c3.563-2.354,4.563-5.133,7.854-3.705\n\t\tC47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t&lt;path fill=\"none\" stroke=\"#2068A3\" stroke-linecap=\"round\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t&lt;path fill=\"none\" stroke=\"#2068A3\" stroke-linecap=\"round\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.623\" cy=\"-2278.646\" r=\"23.425\" fx=\"23.0534\" fy=\"-2281.1357\" gradientTransform=\"matrix(1 0 0 -1 0.2803 -2252.9199)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"5761.7578\" y1=\"11330.6484\" x2=\"5785.3872\" y2=\"11424.0977\" gradientTransform=\"matrix(0.275 0 0 0.2733 -1558.9874 -3088.4209)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M27.958,6.333c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.083,13.952,36.271,6.268,27.958,6.333z\"/&gt;\n\t&lt;path id=\"Hair_Young_Brown_1_\" fill=\"#CC9869\" stroke=\"#99724F\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n\t&lt;path fill=\"#4B4B4B\" stroke=\"#4B4B4B\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" d=\"M28.105,2\n\t\tC22.464,2,20.2,4.246,18.13,5.533C29.753,2.865,41.152,10.375,44.46,20.5C44.459,16.875,44.459,2,28.105,2z\"/&gt;\n\t&lt;path fill=\"#9B9B9B\" stroke=\"#4B4B4B\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" d=\"M11.151,17.751\n\t\tC12.878,8.25,18.686,6.309,25.273,7.127C31.295,7.875,36.93,10.491,44.459,20.5C37.777,7.125,20.278-3.375,9.903,3.921\n\t\tC5.569,6.97,4.903,13.375,11.151,17.751z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\"\n\t xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\t x=\"0px\" y=\"0px\" width=\"41px\" height=\"48px\" viewBox=\"-0.875 -0.887 41 48\" enable-background=\"new -0.875 -0.887 41 48\"\n\t xml:space=\"preserve\"&gt;\n&lt;defs&gt;\n&lt;/defs&gt;\n&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-979.1445\" x2=\"682.0508\" y2=\"-979.1445\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_1_)\" d=\"M19.625,36.763C8.787,36.763,0,34.888,0,32.575v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,34.888,30.464,36.763,19.625,36.763z\"/&gt;\n&lt;linearGradient id=\"SVGID_2_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-973.1445\" x2=\"682.0508\" y2=\"-973.1445\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_2_)\" d=\"M19.625,36.763c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.927-18.396,3.927\n\tc-9.481,0-17.396-1.959-18.396-3.927l-1.229,2C0,34.888,8.787,36.763,19.625,36.763z\"/&gt;\n&lt;path fill=\"#3C89C9\" d=\"M19.625,26.468c10.16,0,19.625,2.775,19.625,2.775c-0.375,2.721-5.367,5.438-19.554,5.438\n\tc-12.125,0-18.467-2.484-19.541-4.918C-0.127,29.125,9.465,26.468,19.625,26.468z\"/&gt;\n&lt;linearGradient id=\"SVGID_3_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-965.6948\" x2=\"682.0508\" y2=\"-965.6948\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_3_)\" d=\"M19.625,23.313C8.787,23.313,0,21.438,0,19.125v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,21.438,30.464,23.313,19.625,23.313z\"/&gt;\n&lt;linearGradient id=\"SVGID_4_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-959.6948\" x2=\"682.0508\" y2=\"-959.6948\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_4_)\" d=\"M19.625,23.313c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.926-18.396,3.926\n\tc-9.481,0-17.396-1.959-18.396-3.926l-1.229,2C0,21.438,8.787,23.313,19.625,23.313z\"/&gt;\n&lt;path fill=\"#3C89C9\" d=\"M19.476,13.019c10.161,0,19.625,2.775,19.625,2.775c-0.375,2.721-5.367,5.438-19.555,5.438\n\tc-12.125,0-18.467-2.485-19.541-4.918C-0.277,15.674,9.316,13.019,19.476,13.019z\"/&gt;\n&lt;linearGradient id=\"SVGID_5_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-952.4946\" x2=\"682.0508\" y2=\"-952.4946\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_5_)\" d=\"M19.625,10.113C8.787,10.113,0,8.238,0,5.925v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,8.238,30.464,10.113,19.625,10.113z\"/&gt;\n&lt;linearGradient id=\"SVGID_6_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-946.4946\" x2=\"682.0508\" y2=\"-946.4946\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_6_)\" d=\"M19.625,10.113c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.926-18.396,3.926\n\tc-9.481,0-17.396-1.959-18.396-3.926L0,5.925C0,8.238,8.787,10.113,19.625,10.113z\"/&gt;\n&lt;linearGradient id=\"SVGID_7_\" gradientUnits=\"userSpaceOnUse\" x1=\"644.0293\" y1=\"-943.4014\" x2=\"680.8223\" y2=\"-943.4014\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;ellipse fill=\"url(#SVGID_7_)\" cx=\"19.625\" cy=\"3.926\" rx=\"18.396\" ry=\"3.926\"/&gt;\n&lt;path opacity=\"0.24\" fill=\"#FFFFFF\" enable-background=\"new    \" d=\"M31.04,45.982c0,0-4.354,0.664-7.29,0.781\n\tc-3.125,0.125-8.952,0-8.952,0l-2.384-10.292l0.044-2.108l-1.251-1.154L9.789,23.024l-0.082-0.119L9.5,20.529l-1.65-1.254\n\tL5.329,8.793c0,0,4.213,0.903,7.234,1.07s8.375,0.25,8.375,0.25l3,9.875l-0.25,1.313l1.063,2.168l2.312,9.645l-0.521,1.416\n\tl1.46,1.834L31.04,45.982z\"/&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-ru/input-file-upload.md",
    "content": "Загрузка файлов\n===============\n\nЗагрузка файлов в Yii, обычно, выполняется при помощи класса [[yii\\web\\UploadedFile]], который представляет каждый\nзагруженный файл в виде объекта `UploadedFile`. Используя [[yii\\widgets\\ActiveForm]] и [модели](structure-models.md)\nможно легко создать безопасный механизм загрузки файлов.\n\n\n## Создание моделей <span id=\"creating-models\"></span>\n\nКак и в случае с обработкой текстового ввода, для загрузки файла можно создать класс модели и использовать его атрибут\nдля хранения экземпляра объекта `UploadedFile`, содержащего параметры загруженного файла. Так же, возможно\nиспользование правил валидации модели для проверки загруженного файла. Например,\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\nuse yii\\web\\UploadedFile;\n\nclass UploadForm extends Model\n{\n    /**\n     * @var UploadedFile\n     */\n    public $imageFile;\n\n    public function rules()\n    {\n        return [\n            [['imageFile'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg'],\n        ];\n    }\n    \n    public function upload()\n    {\n        if ($this->validate()) {\n            $this->imageFile->saveAs('uploads/' . $this->imageFile->baseName . '.' . $this->imageFile->extension);\n            return true;\n        } else {\n            return false;\n        }\n    }\n}\n```\n\nВ примере выше атрибут `imageFile` используется для хранения экземпляра  загруженного файла. Правило валидации `file`,\nкоторое, при помощи валидатора [[yii\\validators\\FileValidator]], проверяет расширение загруженного файла на\nсоответствие с `png` или `jpg`. Метод `upload()` выполняет валидацию и сохраняет загруженный файл на сервере.\n\nВалидатор `file` позволяет проверять расширение, размер, тип MIME и другие параметры загруженного файла.\nПодробности в разделе [Встроенные валидаторы](tutorial-core-validators.md#file).\n\n> Tip: При загрузке изображений лучше использовать соответствующий валидатор `image`. Данный валидатор\nреализован классом [[yii\\validators\\ImageValidator]] и позволяет проверить корректность загруженного\nизображения при помощи [расширения Imagine](https://github.com/yiisoft/yii2-imagine).\n\n\n## Представление <span id=\"rendering-file-input\"></span>\n\nТеперь можно создать представление, отображающее поле загрузки файла:\n\n```php\n<?php\nuse yii\\widgets\\ActiveForm;\n?>\n\n<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?>\n\n    <?= $form->field($model, 'imageFile')->fileInput() ?>\n\n    <button>Submit</button>\n\n<?php ActiveForm::end() ?>\n```\n\nВажно помнить, что для корректной загрузки файла необходим параметр формы `enctype`. Метод `fileInput()`\nвыведет тег `<input type=\"file\">`, позволяющий пользователю выбрать файл для загрузки.\n\n> Tip: начиная с версии 2.0.8, [[yii\\widgets\\ActiveField::fileInput|fileInput]] автоматически добавляет\n  к форме свойство `enctype`, если в ней есть поле для загрузки файла.\n\n## Загрузка <span id=\"wiring-up\"></span>\n\nТеперь напишем код действия контроллера, который объединит модель и представление.\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\nuse app\\models\\UploadForm;\nuse yii\\web\\UploadedFile;\n\nclass SiteController extends Controller\n{\n    public function actionUpload()\n    {\n        $model = new UploadForm();\n\n        if (Yii::$app->request->isPost) {\n            $model->imageFile = UploadedFile::getInstance($model, 'imageFile');\n            if ($model->upload()) {\n                // file is uploaded successfully\n                return;\n            }\n        }\n\n        return $this->render('upload', ['model' => $model]);\n    }\n}\n```\n\nПри получении данных, отправленных из формы, для создания из загруженного файла экземпляра объекта `UploadedFile`,\nвызывается метод [[yii\\web\\UploadedFile::getInstance()]]. Далее всю работу по валидации и сохранению загруженного\nфайла на сервере берет на себя модель.\n\n\n## Загрузка нескольких файлов <span id=\"uploading-multiple-files\"></span>\n\nДля загрузки нескольких файлов достаточно внести в предыдущий код несколько небольших изменений.\n\nСначала нужно добавить в правило валидации `file` параметр `maxFiles` для ограничения максимального количества\nзагружаемых одновременно файлов. Установка `maxFiles` равным `0` означает снятие ограничений на количество файлов,\nкоторые могут быть загружены одновременно. Максимально разрешенное количество одновременно закачиваемых файлов\nтакже ограничивается директивой PHP [`max_file_uploads`](https://www.php.net/manual/ru/ini.core.php#ini.max-file-uploads),\nи по умолчанию равно 20. Метод `upload()` нужно изменить для сохранения загруженных файлов по одному.\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\nuse yii\\web\\UploadedFile;\n\nclass UploadForm extends Model\n{\n    /**\n     * @var UploadedFile[]\n     */\n    public $imageFiles;\n\n    public function rules()\n    {\n        return [\n            [['imageFiles'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg', 'maxFiles' => 4],\n        ];\n    }\n    \n    public function upload()\n    {\n        if ($this->validate()) { \n            foreach ($this->imageFiles as $file) {\n                $file->saveAs('uploads/' . $file->baseName . '.' . $file->extension);\n            }\n            return true;\n        } else {\n            return false;\n        }\n    }\n}\n```\n\nВ представлении, в вызов метода `fileInput()`, нужно добавить параметр `multiple` для того, чтобы поле *input* позволяло выбирать несколько файлов одновременно. Необходимо изменить `imageFiles` на `imageFiles[]` чтобы атрибут передавался в виде массива:\n \n```php\n<?php\nuse yii\\widgets\\ActiveForm;\n?>\n\n<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?>\n\n    <?= $form->field($model, 'imageFiles[]')->fileInput(['multiple' => true, 'accept' => 'image/*']) ?>\n\n    <button>Submit</button>\n\n<?php ActiveForm::end() ?>\n```\n\nВ действии контроллера нужно заменить вызов `UploadedFile::getInstance()` на `UploadedFile::getInstances()` для присвоения атрибуту модели `imageFiles` массива объектов `UploadedFile`.\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\nuse app\\models\\UploadForm;\nuse yii\\web\\UploadedFile;\n\nclass SiteController extends Controller\n{\n    public function actionUpload()\n    {\n        $model = new UploadForm();\n\n        if (Yii::$app->request->isPost) {\n            $model->imageFiles = UploadedFile::getInstances($model, 'imageFiles');\n            if ($model->upload()) {\n                // file is uploaded successfully\n                return;\n            }\n        }\n\n        return $this->render('upload', ['model' => $model]);\n    }\n}\n```\n"
  },
  {
    "path": "docs/guide-ru/input-form-javascript.md",
    "content": "Расширение ActiveForm на стороне клиента\n=======================================\n\nВиджет [[yii\\widgets\\ActiveForm]] поставляется с набором JavaScript методов, которые используются для проверки на стороне клиента.\nЕго реализация очень гибкая и позволяет расширять его различными способами.\n\n## ActiveForm события\n\nActiveForm запускает серию специальных событий. Используя код, подобный следующему, вы можете подписаться на эти события и обрабатывать их:\n\n```javascript\n$('#contact-form').on('beforeSubmit', function (e) {\n\tif (!confirm(\"Everything is correct. Submit?\")) {\n\t\treturn false;\n\t}\n\treturn true;\n});\n```\n\nНиже мы рассмотрим доступные события.\n\n### `beforeValidate`\n\n`beforeValidate` запускается перед проверкой всей формы.\n\nСигнатура обработчика событий должна быть:\n\n```javascript\nfunction (event, messages, deferreds) {}\n```\n\nгде\n\n- `event`: объект `Event`\n- `messages`: ассоциативный массив с ключами, являющимися идентификаторами атрибутов, и значениями, являющимися массивами сообщений об ошибках для соответствующих атрибутов\n- `deferreds`: массив отложенных объектов. Вы можете использовать `deferreds.add(callback)`, чтобы добавить новую отложенную проверку\n\nЕсли обработчик возвращает логическое `false`, он остановит дальнейшую проверку формы после этого события. И, как результат, событие `afterValidate` не будет запущено.\n\n### `afterValidate`\n\n`afterValidate` событие запускается после проверки всей формы.\n\nСигнатура обработчика события должна быть:\n\n```javascript\nfunction (event, messages, errorAttributes) {}\n```\n\nгде\n\n- `event`: объект `Event`\n- `messages`: ассоциативный массив с ключами, являющимися идентификаторами атрибутов, и значениями, являющимися массивами сообщений об ошибках для соответствующих атрибутов\n- `errorAttributes`: массив атрибутов, которые имеют ошибки проверки. Пожалуйста, обратитесь к `attributeDefaults` для просмотра структуры этого параметра\n\n### `beforeValidateAttribute`\n\n`beforeValidateAttribute` событие инициируется перед проверкой атрибута.\n\nСигнатура обработчика события должна быть:\n\n```javascript\nfunction (event, attribute, messages, deferreds)\n```\n\nгде\n\n- `event`: объект `Event`\n- `attribute`: атрибут для проверки. Пожалуйста, обратитесь к `attributeDefaults` для просмотра структуры этого параметра\n- `messages`: массив, в который вы можете добавить сообщения об ошибках проверки для указанного атрибута\n- `deferreds`: массив отложенных объектов. Вы можете использовать `deferreds.add (callback)`, чтобы добавить новую отложенную проверку\n\nЕсли обработчик возвращает логическое `false`, он остановит дальнейшую проверку указанного атрибута.\nИ, как результат, событие `afterValidateAttribute` не будет запущено.\n\n### `afterValidateAttribute`\n\n`afterValidateAttribute` событие запускается после проверки всей формы и каждого атрибута.\n\nСигнатура обработчика события должна быть:\n\n```javascript\nfunction (event, attribute, messages)\n```\n\nгде\n\n- `event`: объект `Event`\n- `attribute`: проверяемый атрибут. Пожалуйста, обратитесь к `attributeDefaults` для просмотра структуры этого параметра\n- `messages`: массив, в который вы можете добавить дополнительные сообщения об ошибках валидации для указанного атрибута\n\n### `beforeSubmit`\n\n`beforeSubmit` событие запускается перед отправкой формы после того, как все проверки пройдены.\n\nСигнатура обработчика события должна быть:\n\n```javascript\nfunction (event)\n```\n\nгде событие является объектом `Event`.\n\nЕсли обработчик возвращает логическое значение `false`, он остановит отправку формы.\n\n### `ajaxBeforeSend`\n         \n`ajaxBeforeSend` событие инициируется перед отправкой AJAX запроса для проверки основанной на AJAX.\n\nСигнатура обработчика события должна быть:\n\n```javascript\nfunction (event, jqXHR, settings)\n```\n\nгде\n\n- `event`: объект `Event`\n- `jqXHR`: объект `jqXHR`\n- `settings`: настройки для AJAX запроса\n\n### `ajaxComplete`\n\n`ajaxComplete` событие запускается после выполнения AJAX запроса для проверки основанной на AJAX.\n\nСигнатура обработчика события должна быть:\n\n```javascript\nfunction (event, jqXHR, textStatus)\n```\n\nгде\n\n- `event`: объект `Event`\n- `jqXHR`: объект `jqXHR`\n- `textStatus`: статус запроса (\"success\", \"notmodified\", \"error\", \"timeout\", \"abort\", или \"parsererror\")\n\n## Отправка формы через AJAX\n\nХотя проверка может быть выполнена на стороне клиента или с помощью AJAX-запроса, сама отправка формы, по умолчанию, выполняется как обычный запрос.\nЕсли вы хотите, чтобы форма была отправлена через AJAX, вы можете достичь этого, обработав событие `beforeSubmit` формы следующим образом:\n\n```javascript\nvar $form = $('#formId');\n$form.on('beforeSubmit', function() {\n    var data = $form.serialize();\n    $.ajax({\n        url: $form.attr('action'),\n        type: 'POST',\n        data: data,\n        success: function (data) {\n            // Implement successful\n        },\n        error: function(jqXHR, errMsg) {\n            alert(errMsg);\n        }\n     });\n     return false; // prevent default submit\n});\n```\nЧтобы узнать больше о jQuery функции `ajax()`, обратитесь к документации [jQuery documentation](https://api.jquery.com/jQuery.ajax/).\n\n\n## Добавления полей динамически\n\nВ современных веб-приложениях часто возникает необходимость изменения формы после ее отображения пользователю.\nЭто может быть, например, добавление новых полей после нажатия на значок \"плюс\".\nЧтобы включить проверку, на стороне клиента, для этих полей, они должны быть зарегистрированы с помощью JavaScript плагина Active Form.\n\nВы должны добавить само поле, а затем добавить его в список проверки:\n\n```javascript\n$('#contact-form').yiiActiveForm('add', {\n    id: 'address',\n    name: 'address',\n    container: '.field-address',\n    input: '#address',\n    error: '.help-block',\n    validate:  function (attribute, value, messages, deferred, $form) {\n        yii.validation.required(value, messages, {message: \"Validation Message Here\"});\n    }\n});\n```\n\nДля удаления поля из списка проверки, чтобы оно не было проверено, выполните следующие действия:\n\n```javascript\n$('#contact-form').yiiActiveForm('remove', 'address');\n```\n"
  },
  {
    "path": "docs/guide-ru/input-forms.md",
    "content": "Создание форм\n=============\n\nОсновным способом использования форм в Yii является использование [[yii\\widgets\\ActiveForm]]. Этот подход должен быть\nприменён, когда форма основана на модели. Кроме того, имеются дополнительные методы в [[yii\\helpers\\Html]], которые\nиспользуются для добавления кнопок и текстовых подсказок к любой форме.\n\nФорма, которая отображается на стороне клиента в большинстве случаев соответствует [модели](structure-models.md).\nМодель, в свою очередь, проверяет данные из элементов формы на сервере (смотрите раздел [Валидация](input-validation.md)\nдля более подробных сведений). Когда создаётся форма, основанная на модели, необходимо определить, что же является моделью.\nМодель может основываться на классе [Active Record](db-active-record.md), который описывает некоторые данные из базы данных,\nили же на базовом классе Model (происходит от [[yii\\base\\Model]]), который позволяет использовать\nпроизвольный набор элементов формы (например, форма входа).\n\nВ следующем примере показано, как создать модель формы, основанной на базовом классе Model:\n\n```php\n<?php\n\nclass LoginForm extends \\yii\\base\\Model\n{\n    public $username;\n    public $password;\n\n    public function rules()\n    {\n        return [\n            // тут определяются правила валидации\n        ];\n    }\n}\n```\n\nВ контроллере будем передавать экземпляр этой модели в представление для виджета [[yii\\widgets\\ActiveForm|ActiveForm]], который генерирует форму.\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n$form = ActiveForm::begin([\n    'id' => 'login-form',\n    'options' => ['class' => 'form-horizontal'],\n]) ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n\n    <div class=\"form-group\">\n        <div class=\"col-lg-offset-1 col-lg-11\">\n            <?= Html::submitButton('Вход', ['class' => 'btn btn-primary']) ?>\n        </div>\n    </div>\n<?php ActiveForm::end() ?>\n```\n\nВ вышеприведённом коде [[yii\\widgets\\ActiveForm::begin()|ActiveForm::begin()]] не только создаёт экземпляр формы, но\nтакже и знаменует её начало. Весь контент, расположенный между [[yii\\widgets\\ActiveForm::begin()|ActiveForm::begin()]]\nи [[yii\\widgets\\ActiveForm::end()|ActiveForm::end()]], будет завёрнут в HTML-тег `<form>`. Вы можете изменить некоторые \nнастройки виджета через передачу массива в его `begin` метод, так же как и в любом другом виджете. В этом случае дополнительный CSS-класс и идентификатор будет прикреплён к открывающемуся тегу `<form>`. Для просмотра всех доступных настроек, пожалуйста, обратитесь к документации API [[yii\\widgets\\ActiveForm]].\n\nДля создания в форме элемента с меткой и любой применимой валидацией с помощью JavaScript, вызывается [[yii\\widgets\\ActiveForm::field()|ActiveForm::field()]], который возвращает экземпляр [[yii\\widgets\\ActiveField]]. Когда этот метод вызывается непосредственно, то результатом будет текстовый элемент (`input type=\"text\"`). Для того, чтобы настроить элемент, можно вызвать один за другим дополнительные методы [[yii\\widgets\\ActiveField|ActiveField]]:\n\n```php\n// элемент формы для ввода пароля\necho $form->field($model, 'password')->passwordInput();\n// добавлена подсказка (hint) и настроена метка (label)\necho $form->field($model, 'username')->textInput()->hint('Пожалуйста, введите имя')->label('Имя');\n// создание элемента HTML5 для ввода email \necho $form->field($model, 'email')->input('email');\n```\n\nВпоследствии будут созданы `<label>`, `<input>` и другие теги в соответствии с [[yii\\widgets\\ActiveField::$template|template]], который определён в элементе. Имя элемента формы определяется автоматически из моделей [[yii\\base\\Model::formName()|form name]] \nи их атрибутов. Например, имя элемента для атрибута `username` в коде, приведённом выше, будет `LoginForm[username]`.\nЭто правило именования будет учитываться на стороне сервера при получении массива результатов `$_POST['LoginForm']`\nдля всех элементов формы входа (Login Form).\n\n> Tip: Если в форме только одна модель, и вы хотите упростить имена полей ввода, то можете сделать это, переопределив метод [[yii\\base\\Model::formName()|formName()]] модели так, чтобы он возвращал пустую строку. Это может пригодиться для получения более красивых URL при фильтрации моделей в [GridView](output-data-widgets.md#grid-view).\n\nСпецифический атрибут модели может быть задан более сложным способом. Например, при загрузке файлов или выборе\nнескольких значений из списка, в качестве значений атрибуту модели нужно передать массив, для этого к имени можно добавить\n`[]`:\n\n```php\n// поддерживает загрузку нескольких файлов:\necho $form->field($model, 'uploadFile[]')->fileInput(['multiple'=>'multiple']);\n\n// поддерживает выбор нескольких значений:\necho $form->field($model, 'items[]')->checkboxList(['a' => 'Item A', 'b' => 'Item B', 'c' => 'Item C']);\n```\n\nИмена элементов форм следует выбирать, учитывая, что могут возникнуть конфликты. Подробнее об этом в [документации jQuery](https://api.jquery.com/submit/):\n\n> Имена и идентификаторы форм и их элементов не должны совпадать с элементами форм, такими как `submit`, `length` или `method`. Конфликты имен могут вызывать трудно диагностируемые ошибки. Подробнее о способах избегания подобных проблем смотрите [DOMLint](https://kangax.github.io/domlint/).\n\nДополнительные HTML-элементы можно добавить к форме, используя обычный HTML или методы из класса помощника [[yii\\helpers\\Html|Html]], как это было сделано с помощью [[yii\\helpers\\Html::submitButton()|Html::submitButton()]] в примере, приведённом выше. \n\n> Tip: Если вы используете Twitter Bootstrap CSS в своём приложении, то воспользуйтесь [[yii\\bootstrap\\ActiveForm]] вместо [[yii\\widgets\\ActiveForm]]. Он добавит к ActiveForm дополнительные стили, которые сработают в рамках bootstrap CSS.\n\n> Tip: для добавления \"звёздочки\" к обязательным элементам формы, воспользуйтесь следующим CSS:\n>\n> ```css\n> div.required label.control-label:after {\n>     content: \" *\";\n>     color: red;\n> }\n> ```\n\n\nСоздание выпадающего списка <span id=\"creating-activeform-dropdownlist\"></span>\n---------------------\n\nДля создания выпадающего списка можно использовать метод ActiveForm [[yii\\widgets\\ActiveField::dropDownList()|dropDownList()]]:\n\n```php\nuse app\\models\\ProductCategory;\n\n/**\n * @var \\yii\\web\\View $this\n * @var \\yii\\widgets\\ActiveForm $form\n * @var \\app\\models\\Product $model\n */\n\necho $form->field($model, 'product_category')->dropdownList(\n    ProductCategory::find()->select(['category_name', 'id'])->indexBy('id')->column(),\n    ['prompt'=>'Select Category']\n);\n```\n\nТекущее значение поля модели будет автоматически выбрано в списке.\n\nРабота с Pjax <span id=\"working-with-pjax\"></span>\n--------------\n\nВиджет [[yii\\widgets\\Pjax|Pjax]] позволяет обновлять определённую область страницы вместо\nперезагрузки всей страницы. Вы можете использовать его для обновления формы после её отсылки.\n\nДля того, чтобы задать, какая из форм будет работать через PJAX, можно воспользоваться\nопцией [[yii\\widgets\\Pjax::$formSelector|$formSelector]]. Если значение не задано, все формы\nс атрибутом `data-pjax` внутри PJAX-контента будут работать через PJAX.\n\n```php\nuse yii\\widgets\\Pjax;\nuse yii\\widgets\\ActiveForm;\n\nPjax::begin([\n    // Опции Pjax\n]);\n    $form = ActiveForm::begin([\n        'options' => ['data' => ['pjax' => true]],\n        // остальные опции ActiveForm\n    ]);\n\n        // Содержимое ActiveForm\n\n    ActiveForm::end();\nPjax::end();\n```\n> Tip: Будьте осторожны со ссылками внутри виджета [[yii\\widgets\\Pjax|Pjax]] так как ответ будет\n> также отображаться внутри виджета. Чтобы ссылка работала без PJAX, добавьте к ней HTML-атрибут\n> `data-pjax=\"0\"`.\n\n#### Значения кнопок отправки и загрузка файлов\n\nВ `jQuery.serializeArray()` имеются определённые проблемы\n[при работе с файлами](https://github.com/jquery/jquery/issues/2321) и\n[значениями кнопок типа submit](https://github.com/jquery/jquery/issues/2321).\nОни не будут исправлены и признаны устаревшими в пользу класса`FormData` из HTML5.\n\nЭто означет, что поддержка файлов и значений submit-кнопок через AJAX или виджет\n[[yii\\widgets\\Pjax|Pjax]] зависит от \n[поддержки в браузере](https://developer.mozilla.org/ru/docs/Web/API/FormData#%D1%81%D0%BE%D0%B2%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D1%8C)\nкласса `FormData`.\n\n\nЕщё по теме <span id=\"further-reading\"></span>\n---------------\n\nСледующая глава [Валидация](input-validation.md) описывает валидацию отправленной формы как на стороне сервера,\nтак и на стороне клиента.\n\nЕсли вы хотите более подробно изучить информацию по использованию форм, то обратитесь к главам:\n\n- [Табличный ввод](input-tabular-input.md) - получение данных нескольких моделей одного вида.\n- [Работа с несколькими моделями](input-multiple-models.md) - обработка нескольких разных моделей в рамках одной формы.\n- [Загрузка файлов](input-file-upload.md) - использование форм для загрузки файлов.\n"
  },
  {
    "path": "docs/guide-ru/input-multiple-models.md",
    "content": "Работа с несколькими моделями\n=============================\n\nКогда имеешь дело со сложными данными, иногда может потребоваться использовать несколько разных моделей для обработки данных, введенных\nпользователем. Для примера, предположим, что информация пользователя для входа хранится в таблице `user`, а данные профиля\nхранятся в таблице `profile`, и вы можете захотеть обрабатывать входные данные о пользователе через модели `User` и `Profile`.\nУчитывая поддержку Yii моделей и форм, вы можете решить данную задачу способом, не сильно отличающимся от обработки одинарной модели.\n\nДалее мы покажем, как можно создать форму, которая позволила бы вам собирать данные для обеих моделей `User` и `Profile`.\n\nДействие контроллера для обработки данных пользователя и данных профиля может быть написано следующим образом,\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\base\\Model;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\nuse app\\models\\User;\nuse app\\models\\Profile;\n\nclass UserController extends Controller\n{\n    public function actionUpdate($id)\n    {\n        $user = User::findOne($id);\n        $profile = Profile::findOne($id);\n        \n        if (!isset($user, $profile)) {\n            throw new NotFoundHttpException(\"The user was not found.\");\n        }\n        \n        $user->scenario = 'update';\n        $profile->scenario = 'update';\n        \n        if ($user->load(Yii::$app->request->post()) && $profile->load(Yii::$app->request->post())) {\n            $isValid = $user->validate();\n            $isValid = $profile->validate() && $isValid;\n            if ($isValid) {\n                $user->save(false);\n                $profile->save(false);\n                return $this->redirect(['user/view', 'id' => $id]);\n            }\n        }\n        \n        return $this->render('update', [\n            'user' => $user,\n            'profile' => $profile,\n        ]);\n    }\n}\n```\n\nВ действии `update`, мы сначала загружаем из базы модели `$user` и `$profile`. Затем мы вызываем метод [[yii\\base\\Model::load()]] \nдля заполнения этих двух моделей данными, введенными пользователем. В случае успеха мы проверяем модели и сохраняем их. В противном случае \nмы рендерим представление `update`, которое содержит следующий контент:\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n$form = ActiveForm::begin([\n    'id' => 'user-update-form',\n    'options' => ['class' => 'form-horizontal'],\n]) ?>\n    <?= $form->field($user, 'username') ?>\n\n    ...other input fields...\n    \n    <?= $form->field($profile, 'website') ?>\n\n    <?= Html::submitButton('Update', ['class' => 'btn btn-primary']) ?>\n<?php ActiveForm::end() ?>\n```\n\nКак вы можете видеть, в представлении `update` рендерятся поля ввода для двух моделей `$user` и `$profile`.\n"
  },
  {
    "path": "docs/guide-ru/input-tabular-input.md",
    "content": "Табличный ввод\n========================\n\nИногда возникает необходимость обработки нескольких моделей одного вида в одной форме. Например, несколько параметров, каждый из которых сохраняется как пара имя-значение и представляется моделью [Active Record](db-active-record.md) `Setting`.\nТакой тип форм часто называют \"табличным вводом\".\nОбработка данных нескольких моделей разных видов в одной форме описана в разделе [Работа с несколькими моделями](input-multiple-models.md).\n\nДальше будет рассмотрен вариант реализации табличного ввода при помощи Yii.\n\nВыделим три сценария, которые потребуют немного разных подходов:\n- Изменение фиксированного набора записей из базы данных;\n- Создание произвольного набора записей;\n- Изменение, создание и удаление записей на одной странице.\n\nВ отличие от форм с одной моделью, рассмотренных ранее, теперь будем иметь дело с массивом моделей. Этот массив передается в представление и для каждой модели отображаются поля ввода в табличном виде. Для загрузки и валидации нескольких моделей сразу будем использовать вспомогательные методы класса [[yii\\base\\Model]]:\n\n- [[yii\\base\\Model::loadMultiple()|Model::loadMultiple()]] загружает POST-данные в массив моделей;\n- [[yii\\base\\Model::validateMultiple()|Model::validateMultiple()]] валидирует массив моделей.\n\n### Изменение фиксированного набора записей\n\nНачнем с действия контроллера:\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\base\\Model;\nuse yii\\web\\Controller;\nuse app\\models\\Setting;\n\nclass SettingsController extends Controller\n{\n    // ...\n\n    public function actionUpdate()\n    {\n        $settings = Setting::find()->indexBy('id')->all();\n\n        if (Model::loadMultiple($settings, Yii::$app->request->post()) && Model::validateMultiple($settings)) {\n            foreach ($settings as $setting) {\n                $setting->save(false);\n            }\n            return $this->redirect('index');\n        }\n\n        return $this->render('update', ['settings' => $settings]);\n    }\n}\n```\n\nВ коде выше, для получения из базы данных массива моделей, индексированного по главному ключу, использован метод [[yii\\db\\ActiveQuery::indexBy()|indexBy()]]. В дальнейшем будем использовать это для идентификации полей формы. Метод [[yii\\base\\Model::loadMultiple()|Model::loadMultiple()]] загружает данные запроса POST в массив моделей, а метод [[yii\\base\\Model::validateMultiple()|Model::validateMultiple()]] проводит валидацию всех моделей. Так как модели уже прошли валидацию, мы передаем методу [[yii\\db\\ActiveRecord::save()|save()]] параметр `false` для отключения повторной валидации.\n\nТеперь займемся формой в представлении `update`:\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n$form = ActiveForm::begin();\n\nforeach ($settings as $index => $setting) {\n    echo $form->field($setting, \"[$index]value\")->label($setting->name);\n}\n\nActiveForm::end();\n```\n\nДля каждого элемента массива `$settings` генерируется имя и поле ввода значения. Важно указать правильный индекс в имени поля ввода значения, так как [[yii\\base\\Model::loadMultiple()|Model::loadMultiple()]] определяет модель по этому индексу.\n\n\n### Создание произвольного набора записей\n\nПроцесс создания новых записей похож на их изменение, за исключением части, где создаются новые модели:\n\n```php\npublic function actionCreate()\n{\n    $count = count(Yii::$app->request->post('Setting', []));\n    $settings = [new Setting()];\n    for($i = 1; $i < $count; $i++) {\n        $settings[] = new Setting();\n    }\n\n    // ...\n}\n```\n\nСначала создается массив `$settings`, содержащий один экземпляр модели, так что по умолчанию в представлении всегда будет отображено хотя бы одно поле. Дополнительно, добавляются модели для каждой полученной строки ввода.\n\nВ представлении возможно использовать JavaScript для добавления новых полей динамически.\n\n\n### Изменение, создание и удаление записей на одной странице\n\n> Note: Раздел находится в разработке\n\nTBD\n"
  },
  {
    "path": "docs/guide-ru/input-validation.md",
    "content": "Проверка входящих данных\n================\n\nКак правило, вы никогда не должны доверять данным, полученным от пользователей и всегда проверять их прежде, чем работать с ними и добавлять в базу данных.\n\nУчитывая [модель](structure-models.md) данных которые должен заполнить пользователь, можно проверить эти данные на валидность воспользовавшись методом [[yii\\base\\Model::validate()]]. Метод возвращает логическое значение с результатом валидации ложь/истина. Если данные не валидны, ошибку можно получить воспользовавшись свойством [[yii\\base\\Model::errors]]. Рассмотрим пример:\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// заполняем модель пользовательскими данными\n$model->load(\\Yii::$app->request->post());\n// аналогично следующей строке:\n// $model->attributes = \\Yii::$app->request->post('ContactForm');\n\nif ($model->validate()) {\n    // все данные корректны\n} else {\n    // данные не корректны: $errors - массив содержащий сообщения об ошибках\n    $errors = $model->errors;\n}\n```\n\n\n## Правила проверки <span id=\"declaring-rules\"></span>\n\nДля того, чтобы  `validate()` действительно работал, нужно объявить правила проверки атрибутов.\nПравила для проверки нужно указать в методе [[yii\\base\\Model::rules()]]. В следующем примере показано, как\nправила для проверки модели `ContactForm`, нужно объявлять:\n\n```php\npublic function rules()\n{\n    return [\n        // атрибут required указывает, что name, email, subject, body обязательны для заполнения\n        [['name', 'email', 'subject', 'body'], 'required'],\n\n        // атрибут email указывает, что в переменной email должен быть корректный адрес электронной почты\n        ['email', 'email'],\n    ];\n}\n```\n\nМетод [[yii\\base\\Model::rules()|rules()]] должен возвращать массив правил, каждое из которых является массивом в следующем формате:\n\n```php\n[\n    // обязательный, указывает, какие атрибуты должны быть проверены по этому правилу.\n    // Для одного атрибута, вы можете использовать имя атрибута не создавая массив\n    ['attribute1', 'attribute2', ...],\n\n    // обязательный, указывает тип правила.\n    // Это может быть имя класса, псевдоним валидатора, или метод для проверки\n    'validator',\n\n    // необязательный, указывает, в каком случае(ях) это правило должно применяться\n    // если не указан, это означает, что правило применяется ко всем сценариям\n    // Вы также можете настроить \"except\" этот вариант применяет правило ко всем\n    // сценариям кроме перечисленных\n    'on' => ['scenario1', 'scenario2', ...],\n\n    // необязательный, задает дополнительные конфигурации для объекта validator\n    'property1' => 'value1', 'property2' => 'value2', ...\n]\n```\n\nДля каждого правила необходимо указать, по крайней мере, какие атрибуты относится к этому правилу и тип правила.\nВы можете указать тип правила в одном из следующих форматов:\n\n* Псевдонимы основного валидатора, например `required`, `in`, `date` и другие. Пожалуйста, обратитесь к списку\n  [Основных валидаторов](tutorial-core-validators.md) за более подробной информацией.\n* Название метода проверки в модели класса, или анонимную функцию. Пожалуйста, обратитесь к разделу\n  [Встроенных валидаторов](#inline-validators) за более подробной информацией.\n* Полное имя класса валидатора. Пожалуйста, обратитесь к разделу [Автономных валидаторов](#standalone-validators)\n  за более подробной информацией.\n\nПравило может использоваться для проверки одного или нескольких атрибутов. Атрибут может быть проверен одним или несколькими правилами.\nПравило может быть применено только к определенным [сценариям](structure-models.md#scenarios) указав свойство `on`.\nЕсли вы не укажите свойство `on`, это означает, что правило будет применяться ко всем сценариям.\n\nКогда вызывается  метод `validate()` для проверки, он выполняет следующие действия:\n\n1. Определяет, какие атрибуты должны проверяться путем получения списка атрибутов от [[yii\\base\\Model::scenarios()]]\n   используя текущий [[yii\\base\\Model::scenario|scenario]]. Эти атрибуты называются - *активными атрибутами*.\n2. Определяет, какие правила проверки должны использоваться, получив список правил от [[yii\\base\\Model::rules()]]\n   используя текущий [[yii\\base\\Model::scenario|scenario]]. Эти правила называются - *активными правилами*.\n3. Каждое активное правило проверяет каждый активный атрибут, который ассоциируется с правилом.\n   Правила проверки выполняются в том порядке, как они перечислены.\n\nСогласно вышеизложенным пунктам, атрибут будет проверяться, если и только если он является\nактивным атрибутом, объявленным в `scenarios()` и связан с одним или несколькими активными правилами,\nобъявленными в `rules()`.\n\n> Note: Правилам валидации полезно давать имена. Например:\n>\n> ```php\n> public function rules()\n> {\n>     return [\n>         // ...\n>         'password' => [['password'], 'string', 'max' => 60],\n>     ];\n> }\n> ```\n>\n> В случае наследования предыдущей модели, именованные правила можно модифицировать или удалить:\n>\n> ```php\n> public function rules()\n> {\n>     $rules = parent::rules();\n>     unset($rules['password']);\n>     return $rules;\n> }\n\n### Настройка сообщений об ошибках <span id=\"customizing-error-messages\"></span>\n\nБольшинство валидаторов имеют сообщения об ошибках по умолчанию, которые будут добавлены к модели когда его атрибуты не проходят проверку.\nНапример, [[yii\\validators\\RequiredValidator|required]] валидатор добавляет к модели сообщение об ошибке \"Имя пользователя не может быть пустым.\" когда атрибут `username` не удовлетворил правилу этого валидатора.\n\nВы можете настроить сообщение об ошибке для каждого правила, указав свойство `message` при объявлении правила, следующим образом:\n\n```php\npublic function rules()\n{\n    return [\n        ['username', 'required', 'message' => 'Please choose a username.'],\n    ];\n}\n```\n\nНекоторые валидаторы могут поддерживать дополнительные сообщения об ошибках, чтобы более точно описать причину ошибки.\nНапример, [[yii\\validators\\NumberValidator|number]] валидатор поддерживает\n[[yii\\validators\\NumberValidator::tooBig|tooBig]] и [[yii\\validators\\NumberValidator::tooSmall|tooSmall]]\nдля описания ошибки валидации, когда проверяемое значение является слишком большим и слишком маленьким, соответственно.\nВы можете настроить эти сообщения об ошибках, как в настройках валидаторов, так и непосредственно в правилах проверки.\n\n\n### События валидации <span id=\"validation-events\"></span>\n\nКогда вызывается метод [[yii\\base\\Model::validate()]] он инициализирует вызов двух методов,\nкоторые можно переопределить, чтобы настроить процесс проверки:\n\n* [[yii\\base\\Model::beforeValidate()]]: выполнение по умолчанию вызовет [[yii\\base\\Model::EVENT_BEFORE_VALIDATE]]\n  событие. Вы можете переопределить этот метод, или обрабатывать это событие, чтобы сделать некоторую предобработку данных (например, форматирование входных данных), метод вызывается до начала валидации. Этот метод должен возвращать логическое значение, указывающее, следует ли продолжать проверку или нет.\n* [[yii\\base\\Model::afterValidate()]]: выполнение по умолчанию вызовет  [[yii\\base\\Model::EVENT_AFTER_VALIDATE]]\n  событие. Вы можете либо переопределить этот метод или обрабатывать это событие, чтобы сделать некоторую  постобработку данных (например, отформатировать данные удобным для дальнейшей обработки образом), метод вызывается после валидации.\n\n### Условные валидации <span id=\"conditional-validation\"></span>\n\nДля проверки атрибутов только при выполнении определенных условий, например если один атрибут зависит от значения другого атрибута можно использовать [[yii\\validators\\Validator::when|when]] свойство, чтобы определить такие условия. Например:\n\n```php\n    ['state', 'required', 'when' => function($model) {\n        return $model->country == 'USA';\n    }],\n```\n\nЭто свойство [[yii\\validators\\Validator::when|when]] принимает PHP callable функцию с следующим описанием:\n\n```php\n/**\n * @param Model $model модель используемая для проверки\n * @param string $attribute атрибут для проверки\n * @return bool следует ли применять правило\n */\nfunction ($model, $attribute)\n```\n\nЕсли вам нужна поддержка условной проверки на стороне клиента, вы должны настроить свойство метода\n[[yii\\validators\\Validator::whenClient|whenClient]], которое принимает строку, представляющую JavaScript\nфункцию, возвращаемое значение определяет, следует ли применять правило или нет. Например:\n\n```php\n    ['state', 'required', 'when' => function ($model) {\n        return $model->country == 'USA';\n    }, 'whenClient' => \"function (attribute, value) {\n        return $('#country').val() == 'USA';\n    }\"]\n```\n\n\n### Фильтрация данных <span id=\"data-filtering\"></span>\n\nПользователь часто вводит данные которые нужно предварительно отфильтровать или предварительно обработать(очистить).\nНапример, вы хотите обрезать пробелы вокруг `username`. Вы можете использовать правила валидации для\nдостижения этой цели.\n\nВ следующих примерах показано, как обрезать пробелы во входных данных и превратить пустые входные данные в `NULL`\nс помощью [trim](tutorial-core-validators.md#trim) и указать значения по умолчанию с помощью свойства\n[default](tutorial-core-validators.md#default) основного валидатора:\n\n```php\nreturn [\n    [['username', 'email'], 'trim'],\n    [['username', 'email'], 'default'],\n];\n```\n\nВы также можете использовать более сложные фильтрации данных с помощью анонимной функции\n подробнее об этом [filter](tutorial-core-validators.md#filter).\n\nКак видите, эти правила валидации на самом деле не проверяют входные данные. Вместо этого,\nони будут обрабатывать значения и обратно возвращать результат работы. Фильтры по сути выполняют предобработку входящих данных.\n\n\n### Обработка пустых входных данных <span id=\"handling-empty-inputs\"></span>\n\nЕсли входные данные представлены из HTML-формы, часто нужно присвоить некоторые значения\nпо умолчанию для входных данных, если они не заполнены. Вы можете сделать это с помощью\nвалидатора [default](tutorial-core-validators.md#default). Например:\n\n```php\nreturn [\n    // установим \"username\" и \"email\" как NULL, если они пустые\n    [['username', 'email'], 'default'],\n\n    // установим \"level\" как 1 если он пустой\n    ['level', 'default', 'value' => 1],\n];\n```\n\nПо умолчанию входные данные считаются пустыми, если их значением является пустая строка, пустой массив или `null`.\nВы можете реализовать свою логику определения пустых входящих данных путем переопределения метода [[yii\\validators\\Validator::isEmpty]]\nиспользуя анонимную функцию. Например:\n\n```php\n    ['agree', 'required', 'isEmpty' => function ($value) {\n        return empty($value);\n    }]\n```\n\n> Note: большинство валидаторов не обрабатывает пустые входные данные, если их\n  [[yii\\base\\Validator::skipOnEmpty]] свойство принимает значение по умолчанию `true`.\n  Они просто будут пропущены во время проверки, если связанные с ними атрибуты являются пустыми.\n  Среди [основных валидаторов](tutorial-core-validators.md), только `captcha`, `default`, `filter`,\n  `required`, и `trim` будут обрабатывать пустые входные данные.\n\n\n## Специальная валидация <span id=\"ad-hoc-validation\"></span>\n\nИногда вам нужно сделать специальную валидацию для значений, которые не связаны с какой-либо моделью.\n\nЕсли необходимо выполнить только один тип проверки (например, проверка адреса электронной почты),\nвы можете вызвать метод [[yii\\validators\\Validator::validate()|validate()]] нужного валидатора.\nНапример:\n\n```php\n$email = 'test@example.com';\n$validator = new yii\\validators\\EmailValidator();\n\nif ($validator->validate($email, $error)) {\n    echo 'Email is valid.';\n} else {\n    echo $error;\n}\n```\n\n> Note: Не все валидаторы поддерживают такой тип проверки. Примером может служить\n[unique](tutorial-core-validators.md#unique) валидатор, который предназначен для работы с моделью.\n\n> Note: Свойство [[yii\\base\\Validator::skipOnEmpty]] используется только в [[yii\\base\\Model]] и использование его отдельно не будет иметь никакого эффекта.\n\nЕсли необходимо выполнить несколько проверок в отношении нескольких значений,\nвы можете использовать [[yii\\base\\DynamicModel]], который поддерживает объявление, как\nатрибутов так и правил \"на лету\". Его использование выглядит следующим образом:\n\n```php\npublic function actionSearch($name, $email)\n{\n    $model = DynamicModel::validateData(compact('name', 'email'), [\n        [['name', 'email'], 'string', 'max' => 128],\n        ['email', 'email'],\n    ]);\n\n    if ($model->hasErrors()) {\n        // валидация завершилась с ошибкой\n    } else {\n        // Валидация успешно выполнена\n    }\n}\n```\n\nМетод [[yii\\base\\DynamicModel::validateData()]] создает экземпляр `DynamicModel`, определяет\nатрибуты, используя приведенные данные (`name` и `email` в этом примере), и затем вызывает\n[[yii\\base\\Model::validate()]]\nс данными правилами.\n\nКроме того, вы можете использовать следующий \"классический\" синтаксис для выполнения специальной проверки данных:\n\n```php\npublic function actionSearch($name, $email)\n{\n    $model = new DynamicModel(compact('name', 'email'));\n    $model->addRule(['name', 'email'], 'string', ['max' => 128])\n        ->addRule('email', 'email')\n        ->validate();\n\n    if ($model->hasErrors()) {\n        // валидация завершилась с ошибкой\n    } else {\n        // Валидация успешно выполнена\n    }\n}\n```\nПосле валидации, вы можете проверить успешность выполнения вызвав\nметод [[yii\\base\\DynamicModel::hasErrors()|hasErrors()]] и затем получить ошибки проверки вызвав\nметод [[yii\\base\\DynamicModel::errors|errors]] как это делают нормальные модели.\nВы можете также получить доступ к динамическим атрибутам, определенным через экземпляр модели, например,\n`$model->name` и `$model->email`.\n\n\n## Создание Валидаторов <span id=\"creating-validators\"></span>\n\nКроме того, используя [основные валидаторы](tutorial-core-validators.md), включенные в релизы Yii, вы также можете\nсоздавать свои собственные валидаторы. Вы можете создавать встроенные валидаторы или автономные валидаторы.\n\n\n### Встроенные Валидаторы <span id=\"inline-validators\"></span>\n\nВстроенный валидатор наследует методы модели или использует анонимную функцию.\nОписание метода/функции:\n\n```php\n/**\n * @param string $attribute атрибут проверяемый в настоящее время\n * @param array $params дополнительные пары имя-значение, заданное в правиле\n * @param \\yii\\validators\\InlineValidator $validator связь с экземпляром InlineValidator\n */\nfunction ($attribute, $params, $validator)\n```\n\nЕсли атрибут не прошел проверку, метод/функция должна вызвать [[yii\\base\\Model::addError()]],\nчтобы сохранить сообщение об ошибке в модели, для того чтобы позже можно было получить сообщение об ошибке для\nпредставления конечным пользователям.\n\nНиже приведены некоторые примеры:\n\n```php\nuse yii\\base\\Model;\n\nclass MyForm extends Model\n{\n    public $country;\n    public $token;\n\n    public function rules()\n    {\n        return [\n            // встроенный валидатор определяется как модель метода validateCountry()\n            ['country', 'validateCountry'],\n\n            // встроенный валидатор определяется как анонимная функция\n            ['token', function ($attribute, $params) {\n                if (!ctype_alnum($this->$attribute)) {\n                    $this->addError($attribute, 'Токен должен содержать буквы или цифры.');\n                }\n            }],\n        ];\n    }\n\n    public function validateCountry($attribute, $params)\n    {\n        if (!in_array($this->$attribute, ['USA', 'Indonesia'])) {\n            $this->addError($attribute, 'Страна должна быть либо \"USA\" или \"Indonesia\".');\n        }\n    }\n}\n```\n\n> Note: по умолчанию, встроенные валидаторы не будут применяться, если связанные с ними атрибуты\nполучат пустые входные данные, или если они уже не смогли пройти некоторые правила валидации.\nЕсли вы хотите, чтобы, это правило применялось всегда, вы можете настроить свойства\n[[yii\\validators\\Validator::skipOnEmpty|skipOnEmpty]] и/или [[yii\\validators\\Validator::skipOnError|skipOnError]]\nсвойства `false` в правиле объявления. Например:\n>\n> ```php\n> [\n>     ['country', 'validateCountry', 'skipOnEmpty' => false, 'skipOnError' => false],\n> ]\n> ```\n\n\n### Автономные валидаторы <span id=\"standalone-validators\"></span>\n\nАвтономный валидатор - это класс, расширяющий [[yii\\validators\\Validator]] или его дочерний класс.\nВы можете реализовать свою логику проверки путем переопределения метода\n[[yii\\validators\\Validator::validateAttribute()]]. Если атрибут не прошел проверку, вызвать\n[[yii\\base\\Model::addError()]],\nчтобы сохранить сообщение об ошибке в модели, как это делают [встроенные валидаторы](#inline-validators).\n\nВалидация может быть помещена в отдельный класс [[components/validators/CountryValidator]]. В этом случае можно использовать метод  [[yii\\validators\\Validator::addError()]] для того, чтобы добавить своё сообщение об ошибке в модель:\n\n```php\nnamespace app\\components;\n\nuse yii\\validators\\Validator;\n\nclass CountryValidator extends Validator\n{\n    public function validateAttribute($model, $attribute)\n    {\n        if (!in_array($model->$attribute, ['USA', 'Indonesia'])) {\n            $this->addError($model, $attribute, 'Страна должна быть либо \"{country1}\" либо \"{country2}\".', ['country1' => 'USA', 'country2' => 'Indonesia']);\n        }\n    }\n}\n```\n\nЕсли вы хотите, чтобы ваш валидатор поддерживал проверку значений без модели, также необходимо переопределить\n[[yii\\validators\\Validator::validate()]]. Вы можете также\nпереопределить [[yii\\validators\\Validator::validateValue()]]\nвместо `validateAttribute()` и `validate()`, потому что по умолчанию последние два метода\nреализуются путем вызова `validateValue()`.\n\n\n## Валидация на стороне клиента <span id=\"client-side-validation\"></span>\n\nПроверка на стороне клиента на основе JavaScript целесообразна, когда конечные пользователи вводят\nвходные данные через HTML-формы, так как эта проверка позволяет пользователям узнать ошибки ввода\nбыстрее и таким образом улучшает ваш пользовательский интерфейс. Вы можете использовать или\nреализовать валидатор, который поддерживает валидацию на стороне клиента *в дополнение* к проверке на стороне сервера.\n\n> Info: Проверка на стороне клиента желательна, но необязательна. Её основная цель заключается в\nпредоставлении пользователям более удобного интерфейса. Так как входные данные, поступают от конечных\nпользователей, вы никогда не должны доверять верификации на стороне клиента. По этой причине, вы всегда\nдолжны выполнять верификацию на стороне сервера путем вызова [[yii\\base\\Model::validate()]],\nкак описано в предыдущих пунктах.\n\n\n### Использование валидации на стороне клиента <span id=\"using-client-side-validation\"></span>\n\nМногие [основные валидаторы](tutorial-core-validators.md) поддерживают проверку на стороне клиента out-of-the-box.\nВсе, что вам нужно сделать, это просто использовать [[yii\\widgets\\ActiveForm]] для построения HTML-форм.\n\nНапример, `LoginForm` ниже объявляет два правила: первое использует [required](tutorial-core-validators.md#required)\nосновной валидатор, который поддерживается на стороне клиента и сервера; второе использует `validatePassword`\nвстроенный валидатор, который поддерживается только на стороне сервера.\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\nuse app\\models\\User;\n\nclass LoginForm extends Model\n{\n    public $username;\n    public $password;\n\n    public function rules()\n    {\n        return [\n            // username и password обязательны для заполнения\n            [['username', 'password'], 'required'],\n\n            // проверке пароля с помощью validatePassword()\n            ['password', 'validatePassword'],\n        ];\n    }\n\n    public function validatePassword()\n    {\n        $user = User::findByUsername($this->username);\n\n        if (!$user || !$user->validatePassword($this->password)) {\n            $this->addError('password', 'Неправильное имя пользователя или пароль.');\n        }\n    }\n}\n```\n\nHTML-форма построена с помощью следующего кода, содержит поля для ввода `username` и `password`.\nЕсли вы отправите форму, не вводя ничего, вы получите сообщения об ошибках, требующих ввести данные.\nСообщения появятся сразу, без обращения к серверу.\n\n```php\n<?php $form = yii\\widgets\\ActiveForm::begin(); ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n    <?= Html::submitButton('Login') ?>\n<?php yii\\widgets\\ActiveForm::end(); ?>\n```\n\nКласс [[yii\\widgets\\ActiveForm]] будет читать правила проверки заявленные в модели и генерировать\nсоответствующий код JavaScript для валидаторов, которые поддерживают проверку на стороне клиента.\nКогда пользователь изменяет значение поля ввода или отправляет форму, JavaScript на стороне клиента\nбудет срабатывать и проверять введенные данные.\n\nЕсли вы хотите отключить проверку на стороне клиента полностью, вы можете настроить свойство\n[[yii\\widgets\\ActiveForm::enableClientValidation]] установив значение `false`. Вы также можете отключить\nпроверку на стороне клиента отдельных полей ввода, настроив их с помощью свойства\n[[yii\\widgets\\ActiveField::enableClientValidation]] установив значение `false`.\n\n\n### Реализация проверки на стороне клиента <span id=\"implementing-client-side-validation\"></span>\n\nЧтобы создать валидатор, который поддерживает проверку на стороне клиента, вы должны реализовать метод\n[[yii\\validators\\Validator::clientValidateAttribute()]] возвращающий фрагмент кода JavaScript,\nкоторый выполняет проверку на стороне клиента. В JavaScript-коде, вы можете использовать следующие предопределенные переменные:\n\n- `attribute`: имя атрибута для проверки.\n- `value`: проверяемое значение.\n- `messages`: массив, используемый для хранения сообщений об ошибках проверки значения атрибута.\n- `deferred`: массив, который содержит отложенные объекты (описано в следующем подразделе).\n\n В следующем примере мы создаем `StatusValidator` который проверяет значение поля на соответствие допустимым статусам.\n Валидатор поддерживает оба способа проверки и на стороне сервера и на стороне клиента.\n\n```php\nnamespace app\\components;\n\nuse yii\\validators\\Validator;\nuse app\\models\\Status;\n\nclass StatusValidator extends Validator\n{\n    public function init()\n    {\n        parent::init();\n        $this->message = 'Invalid status input.';\n    }\n\n    public function validateAttribute($model, $attribute)\n    {\n        $value = $model->$attribute;\n        if (!Status::find()->where(['id' => $value])->exists()) {\n            $model->addError($attribute, $this->message);\n        }\n    }\n\n    public function clientValidateAttribute($model, $attribute, $view)\n    {\n        $statuses = json_encode(Status::find()->select('id')->asArray()->column());\n        $message = json_encode($this->message, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);\n        return <<<JS\nif ($.inArray(value, $statuses) === -1) {\n    messages.push($message);\n}\nJS;\n    }\n}\n```\n\n> Tip: приведенный выше код даётся, в основном, чтобы продемонстрировать, как осуществляется\n> поддержка проверки на стороне клиента. На практике вы можете использовать\n> [in](tutorial-core-validators.md#in) основные валидаторы для достижения той же цели.\n> Вы можете написать проверку, как правило, например:\n>\n> ```php\n> [\n>     ['status', 'in', 'range' => Status::find()->select('id')->asArray()->column()],\n> ]\n> ```\n\n### Отложенная валидация <span id=\"deferred-validation\"></span>\n\nЕсли Вам необходимо выполнить асинхронную проверку на стороне клиента, вы можете создавать\n[Deferred objects](https://api.jquery.com/category/deferred-object/). Например, чтобы выполнить\nпользовательские AJAX проверки, вы можете использовать следующий код:\n\n```php\npublic function clientValidateAttribute($model, $attribute, $view)\n{\n    return <<<JS\n        deferred.push($.get(\"/check\", {value: value}).done(function(data) {\n            if ('' !== data) {\n                messages.push(data);\n            }\n        }));\nJS;\n}\n```\n\nВ примере выше переменная `deferred` предусмотренная Yii, которая является массивом Отложенных объектов.\n`$.get()` метод jQuery создает Отложенный объект, который помещается в массив `deferred`.\n\nТакже можно явно создать Отложенный объект и вызвать его методом `resolve()`, тогда выполняется асинхронный\nвызов к серверу. В следующем примере показано, как проверить размеры загружаемого файла изображения\nна стороне клиента.\n\n```php\npublic function clientValidateAttribute($model, $attribute, $view)\n{\n    return <<<JS\n        var def = $.Deferred();\n        var img = new Image();\n        img.onload = function() {\n            if (this.width > 150) {\n                messages.push('Изображение слишком широкое!');\n            }\n            def.resolve();\n        }\n        var reader = new FileReader();\n        reader.onloadend = function() {\n            img.src = reader.result;\n        }\n        reader.readAsDataURL(file);\n\n        deferred.push(def);\nJS;\n}\n```\n\n> Note: метод `resolve()` должен быть вызван после того, как атрибут был проверен.\nВ противном случае основная проверка формы не будет завершена.\n\nДля простоты работы с массивом `deferred`, существует упрощенный метод `add()`, который автоматически создает Отложенный объект и добавляет его в `deferred` массив. Используя этот метод, вы можете упростить пример выше, следующим образом:\n\n```php\npublic function clientValidateAttribute($model, $attribute, $view)\n{\n    return <<<JS\n        deferred.add(function(def) {\n            var img = new Image();\n            img.onload = function() {\n                if (this.width > 150) {\n                    messages.push('Изображение слишком широкое!');\n                }\n                def.resolve();\n            }\n            var reader = new FileReader();\n            reader.onloadend = function() {\n                img.src = reader.result;\n            }\n            reader.readAsDataURL(file);\n        });\nJS;\n}\n```\n\n\n### AJAX валидация <span id=\"ajax-validation\"></span>\n\nНекоторые проверки можно сделать только на стороне сервера, потому что только сервер имеет необходимую информацию.\nНапример, чтобы проверить логин пользователя на уникальность, необходимо проверить логин в\nбазе данных на стороне сервера. Вы можете использовать проверку на основе AJAX в этом случае.\nЭто вызовет AJAX-запрос в фоновом режиме, чтобы проверить логин пользователя, сохраняя при этом валидацию\nна стороне клиента. Выполняя её перед запросом к серверу.\n\nЧтобы включить AJAX-валидацию для одного поля, Вы должны свойство [[yii\\widgets\\ActiveField::enableAjaxValidation|enableAjaxValidation]] выбрать как `true` и указать уникальный `id` формы:\n\n```php\nuse yii\\widgets\\ActiveForm;\n\n$form = ActiveForm::begin([\n    'id' => 'registration-form',\n]);\n\necho $form->field($model, 'username', ['enableAjaxValidation' => true]);\n\n// ...\n\nActiveForm::end();\n```\n\nЧтобы включить AJAX-валидацию для всей формы, Вы должны свойство\n[[yii\\widgets\\ActiveForm::enableAjaxValidation|enableAjaxValidation]] выбрать как `true` для формы:\n\n```php\n$form = yii\\widgets\\ActiveForm::begin([\n    'id' => 'contact-form',\n    'enableAjaxValidation' => true,\n]);\n```\n\n> Note: В случае, если свойство `enableAjaxValidation` указано и у поля и у формы, первый вариант будет иметь приоритет.\n\nТакже необходимо подготовить сервер для обработки AJAX-запросов валидации. Это может быть достигнуто\nс помощью следующего фрагмента кода, в контроллере действий:\n\n```php\nif (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {\n    Yii::$app->response->format = Response::FORMAT_JSON;\n    return ActiveForm::validate($model);\n}\n```\n\nПриведенный выше код будет проверять, является ли текущий запрос AJAX. Если да,\nон будет отвечать на этот запрос, предварительно выполнив проверку и возвратит ошибки в\nслучае их появления в формате JSON.\n\n> Info: Вы также можете использовать [Deferred Validation](#deferred-validation) AJAX валидации.\nОднако, AJAX-функция проверки, описанная здесь более интегрированная и требует меньше усилий к написанию кода.\n"
  },
  {
    "path": "docs/guide-ru/intro-upgrade-from-v1.md",
    "content": "Обновление с версии 1.1\n====================\n\nМежду версиями 1.1 и 2.0 существует много различий, так как Yii был полностью переписан для версии 2.0.\nТаким образом, обновление с версии 1.1 не является таким же тривиальным как обновление между минорными версиями.\nВ данном руководстве приведены основные различия между двумя версиями.\n\nЕсли прежде вы не использовали Yii 1.1, вы можете сразу перейти к разделу «[Начало работы](start-installation.md)».\n\nТакже учтите, что в Yii 2.0 гораздо больше новых возможностей, чем описано далее. Настоятельно рекомендуется, изучить\nвсё руководство. Вполне возможно, что-то, что раньше приходилось разрабатывать самостоятельно теперь является частью\nфреймворка.\n\n\nУстановка\n--------\n\nYii 2.0 широко использует [Composer](https://getcomposer.org/), который является основным менеджером зависимостей для PHP.\nУстановка как фреймворка, так и расширений, осуществляется через Composer. Подробно об установке Yii 2.0 вы можете узнать\nиз раздела «[Установка Yii](start-installation.md)». О том, как создавать расширения для Yii 2.0 или адаптировать\nуже имеющиеся расширения от версии 1.1, вы можете узнать из раздела «[Создание расширений](structure-extensions.md#creating-extensions)».\n\n\nТребования PHP\n-------------\n\nДля работы Yii 2.0 необходим PHP 5.4 или выше. Данная версия включает большое количество улучшений по сравнению с\nверсией 5.2, которая использовалась Yii 1.1. Таким образом, существует много различий в языке, которые вы должны принимать\nво внимание:\n\n- [Пространства имён](https://www.php.net/manual/ru/language.namespaces.php);\n- [Анонимные функции](https://www.php.net/manual/ru/functions.anonymous.php);\n- Использование короткого синтаксиса для массивов: `[...элементы...]` вместо `array(...элементы...)`;\n- Использование короткого echo `<?=` для вывода в файлах представлений. С версии PHP 5.4 данную возможность можно\n  использовать не опасаясь;\n- [Классы и интерфейсы SPL](https://www.php.net/manual/ru/book.spl.php);\n- [Позднее статическое связывание (LSB)](https://www.php.net/manual/ru/language.oop5.late-static-bindings.php);\n- [Классы для дат и времени](https://www.php.net/manual/ru/book.datetime.php);\n- [Трейты](https://www.php.net/manual/ru/language.oop5.traits.php);\n- [Интернационализация (intl)](https://www.php.net/manual/ru/book.intl.php); Yii 2.0 использует расширение PHP `intl`\n  для различного функционала интернационализации.\n\n\nПространства имён\n---------------\n\nОдним из основных изменений в Yii 2.0 является использование пространств имён. Почти каждый класс фреймворка\nнаходится в пространстве имён, например, `yii\\web\\Request`. Префикс \"С\" в именах классов больше не используется.\nИмена классов соответствуют структуре директорий. Например, `yii\\web\\Request` указывает, что соответствующий класс\nнаходится в файле `web/Request.php` в директории фреймворка.\n\nБлагодаря загрузчику классов Yii, вы можете использовать любой класс фреймворка без необходимости непосредственно\nподключать его.\n\n\nКомпонент и объект\n----------------\n\nВ Yii 2.0 класс `CComponent` из версии 1.1 был разделён на два класса: [[yii\\base\\BaseObject]] и [[yii\\base\\Component]].\nКласс [[yii\\base\\BaseObject|BaseObject]] является простым базовым классом, который позволяет использовать\n[геттеры и сеттеры](concept-properties.md) для свойств. Класс [[yii\\base\\Component|Component]] наследуется от\nкласса [[yii\\base\\BaseObject|BaseObject]] и поддерживает [события](concept-events.md) и [поведения](concept-behaviors.md).\n\nЕсли вашему классу не нужны события или поведения, вы можете использовать [[yii\\base\\BaseObject|BaseObject]] в качестве\nбазового класса. В основном это относится к классам, представляющим собой базовые структуры данных.\n\n\nКонфигурация объекта\n------------------\n\nКласс [[yii\\base\\BaseObject|BaseObject]] предоставляет единый способ конфигурирования объектов. Любой дочерний класс\n[[yii\\base\\BaseObject|BaseObject]] может определить конструктор (если нужно) как показано ниже. Это позволит конфигурировать\nего универсально:\n\n\n```php\nclass MyClass extends \\yii\\base\\BaseObject\n{\n    public function __construct($param1, $param2, $config = [])\n    {\n        // ... инициализация до того, как конфигурация будет применена\n\n        parent::__construct($config);\n    }\n\n    public function init()\n    {\n        parent::init();\n\n        // ... инициализация после того, как конфигурация была применена\n    }\n}\n```\n\nВ примере выше, последний параметр конструктора должен быть массивом конфигурации, который содержит пары в формате\nключ-значение для инициализации свойств объекта. Вы можете переопределить метод [[yii\\base\\BaseObject::init()|init()]] для\nинициализации объекта после того, как конфигурация была применена к нему.\n\nСледуя этому соглашению, вы сможете создавать и конфигурировать новые объекты с помощью массива конфигурации:\n\n```php\n$object = Yii::createObject([\n    'class' => 'MyClass',\n    'property1' => 'abc',\n    'property2' => 'cde',\n], [$param1, $param2]);\n```\n\nБолее подробная информация о конфигурации представлена в разделе «[Настройки](concept-configurations.md)».\n\n\nСобытия\n-------\n\nВ Yii 1, события создавались с помощью объявления метода `on` (например, `onBeforeSave`). В Yii 2 вы можете использовать\nлюбое имя события. Вызывать события можно при помощи метода [[yii\\base\\Component::trigger()|trigger()]].\n\n```php\n$event = new \\yii\\base\\Event;\n$component->trigger($eventName, $event);\n```\n\nДля прикрепления обработчика события используйте метод [[yii\\base\\Component::on()|on()]].\n\n```php\n$component->on($eventName, $handler);\n// убираем обработчик\n// $component->off($eventName, $handler);\n```\n\nЕсть и другие улучшения по части событий, подробно описанные в разделе «[События](concept-events.md)».\n\n\nПсевдонимы пути\n-------------\n\nВ Yii 2.0 псевдонимы используются более широко и применяются как к путям в файловой системе, так и к URL. Теперь, для\nтого, чтобы отличать псевдонимы от обычных путей и URL, требуется, чтобы имя псевдонима начиналось с символа `@`.\nНапример, псевдоним `@yii` соответствует директории, в которую установлен Yii. Псевдонимы пути используются во многих\nместах. Например, значение свойства [[yii\\caching\\FileCache::cachePath]] может быть как псевдонимом пути так и\nобычным путём к папке.\n\nПсевдонимы пути тесно связаны с пространством имён классов. Рекомендуется определять псевдоним пути для каждого корневого\nпространства имён, что позволяет использовать загрузчик классов Yii без какой-либо дополнительной настройки. Например,\nтак как `@yii` соответствует директории, в которую установлен фреймворк, класс `yii\\web\\Request` может быть загружен\nавтоматически. Если вы используете сторонние библиотеки, например, из Zend Framework, вы можете определить псевдоним\nпути `@Zend` как директорию, в которую установлен этот фреймворк. После этого Yii будет способен автоматически загружать\nлюбой класс Zend Framework.\n\nПодробнее о псевдонимах пути можно узнать из раздела «[Псевдонимы пути](concept-aliases.md)».\n\n\nПредставления\n-----------\n\nОдним из основных изменений в Yii 2 является то, что специальная переменная `$this` в представлении, больше не соответствует\nтекущему контроллеру или виджету. Вместо этого, `$this` теперь соответствует объекту *представления*, новой возможности\nвведённой в версии 2.0. Объект представления имеет тип [[yii\\web\\View]], который представляет собой часть *view* в\nшаблоне проектирования MVC. Если вы хотите получить доступ к контроллеру или виджету, используйте выражение `$this->context`.\n\nДля рендеринга частичных представлений теперь используется метод `$this->render()`, а не `$this->renderPartial()`.\nРезультат вызова метода `render` теперь должен быть выведен напрямую, так как `render` возвращает результат рендеринга,\nа не отображает его сразу:\n\n```php\necho $this->render('_item', ['item' => $item]);\n```\n\nКроме использования PHP в качестве основного шаблонизатора, Yii 2.0 также предоставляет официальные расширения для двух\nпопулярных шаблонизаторов: Smarty и Twig. Шаблонизатор Prado больше не поддерживается. Для использования данных\nшаблонизаторов необходимо настроить компонент приложения `view` задав свойство [[yii\\base\\View::$renderers|View::$renderers]].\nПодробнее об этом можно прочитать в разделе «[Шаблонизаторы](tutorial-template-engines.md)».\n\nМодели\n------\n\nYii 2.0 использует в качестве базового класса для моделей [[yii\\base\\Model]], аналогичный классу `CModel` в версии 1.1.\nКласс `CFormModel` удалён. Вместо него для создания модели формы в Yii 2.0 вы должны напрямую наследоваться от [[yii\\base\\Model]].\n\nПоявился новый метод [[yii\\base\\Model::scenarios()|scenarios()]] для объявления поддерживаемых сценариев,\nи для обозначения в каком сценарии атрибуты должны проверяться, считаться безопасными и т.п. Например,\n\n```php\npublic function scenarios()\n{\n    return [\n        'backend' => ['email', 'role'],\n        'frontend' => ['email', '!role'],\n    ];\n}\n```\n\nВ примере выше, объявлено два сценария: `backend` и `frontend`. Для `backend` сценария, оба атрибута `email` и `role` являются\nбезопасными, и могут быть массово присвоены. Для сценария `frontend`, атрибут `email` может быть массово присвоен, а атрибут `role` нет.\nОба атрибута `email` и `role` должны быть проверены с помощью правил валидации.\n\nМетод [[yii\\base\\Model::rules()|rules()]] по-прежнему используется для объявления правил валидации. Обратите внимание, что в связи с\nпоявлением нового метода [[yii\\base\\Model::scenarios()|scenarios()]], больше не поддерживается валидатор `unsafe`.\n\nВ большинстве случаев вам не нужно переопределять метод [[yii\\base\\Model::scenarios()|scenarios()]], если метод [[yii\\base\\Model::rules()|rules()]]\nполностью указывает все существующие сценарии, и если нет надобности в объявлении атрибутов небезопасными.\n\nБолее детальная информация представлена в разделе «[Модели](structure-models.md)».\n\n\nКонтроллеры\n-----------\n\nВ качестве базового класса для контроллеров в Yii 2.0 используется [[yii\\web\\Controller]], аналогичный `CController`\nв Yii 1.1. Базовым классом для всех действий является [[yii\\base\\Action]].\n\nОдним из основных изменений является то, что действие контроллера теперь должно вернуть результат вместо того, чтобы\nнапрямую выводить его:\n\n```php\npublic function actionView($id)\n{\n    $model = \\app\\models\\Post::findOne($id);\n    if ($model) {\n        return $this->render('view', ['model' => $model]);\n    } else {\n        throw new \\yii\\web\\NotFoundHttpException;\n    }\n}\n```\n\nБолее детальная информация представлена в разделе «[Контроллеры](structure-controllers.md)».\n\n\nВиджеты\n-------\n\nВ Yii 2.0 класс [[yii\\base\\Widget]] используется в качестве базового класса для виджетов, аналогично `CWidget` в Yii 1.1.\n\nДля лучшей поддержки фреймворка в IDE, Yii 2.0 использует новый синтаксис для виджетов. Новые статические методы \n[[yii\\base\\Widget::begin()|begin()]], [[yii\\base\\Widget::end()|end()]], и [[yii\\base\\Widget::widget()|widget()]]\nиспользуются следующим образом:\n\n```php\nuse yii\\widgets\\Menu;\nuse yii\\widgets\\ActiveForm;\n\n// Обратите внимание что вы должны выводить результат\necho Menu::widget(['items' => $items]);\n\n// Указываем массив для конфигурации свойств объекта\n$form = ActiveForm::begin([\n    'options' => ['class' => 'form-horizontal'],\n    'fieldConfig' => ['inputOptions' => ['class' => 'input-xlarge']],\n]);\n... поля формы ...\nActiveForm::end();\n```\n\nБолее детальная информация представлена в разделе «[Виджеты](structure-widgets.md)».\n\n\nТемы\n----\n\nВ Yii 2.0 темы работают совершенно по-другому. Теперь они основаны на механизме сопоставления путей исходного файла\nпредставления с темизированным файлом. Например, если используется сопоставление путей `['/web/views' => '/web/themes/basic']`,\nто темизированная версия файла представления `/web/views/site/index.php` будет находиться в `/web/themes/basic/site/index.php`.\nПо этой причине темы могут быть применены к любому файлу представления, даже к представлению, отрендеренному внутри контекста\nконтроллера или виджета. Также, больше не существует компонента `CThemeManager`. Вместо этого, `theme` является конфигурируемым\nсвойством компонента приложения `view`.\n\nБолее детальная информация представлена в разделе «[Темизация](output-theming.md)».\n\n\nКонсольные приложения\n---------------------\n\nКонсольные приложения теперь организованы как контроллеры, аналогично веб приложениям. Консольные контроллеры\nдолжны быть унаследованы от класса [[yii\\console\\Controller]], аналогичного `CConsoleCommand` в версии 1.1.\n\nДля выполнения консольной команды, используйте `yii <маршрут>`, где `<маршрут>` это маршрут контроллера (например, `sitemap/index`).\nДополнительные анонимные аргументы будут переданы в качестве параметров соответствующему действию контроллера, в то время как\nименованные аргументы будут переданы в соответствие с объявлениями в [[yii\\console\\Controller::options()]].\n\nYii 2.0 поддерживает автоматическую генерацию справочной информации из блоков комментариев.\n\nБолее детальная информация представлена в разделе «[Консольные команды](tutorial-console.md)».\n\n\nI18N\n----\n\nВ Yii 2.0 встроенные форматтеры времени и чисел были убраны в пользу [PECL расширения PHP intl](https://pecl.php.net/package/intl).\n\nПеревод сообщений теперь осуществляется через компонент приложения `i18n`. Данный компонент управляет множеством\nисходных хранилищ сообщений, что позволяет вам использовать разные хранилища для исходных сообщений в зависимости\nот категории сообщения.\n\nБолее детальная информация представлена в разделе «[Интернационализация](tutorial-i18n.md)».\n\n\nФильтры действий\n----------------\n\nФильтры действий теперь сделаны с помощью поведений. Для определения нового фильтра, унаследуйтесь от [[yii\\base\\ActionFilter]].\nДля использования фильтра, прикрепите его к контроллеру в качестве поведения. Например, для использования фильтра [[yii\\filters\\AccessControl]],\nследует сделать следующее:\n\n```php\npublic function behaviors()\n{\n    return [\n        'access' => [\n            'class' => 'yii\\filters\\AccessControl',\n            'rules' => [\n                ['allow' => true, 'actions' => ['admin'], 'roles' => ['@']],\n            ],\n        ],\n    ];\n}\n```\n\nБолее детальная информация представлена в разделе «[Фильтры](structure-filters.md)».\n\n\nРесурсы\n-------\n\nВ Yii 2.0 представлена новая возможность *связка ресурсов*, которая заменяет концепт пакетов скриптов в Yii 1.1.\n\nСвязка ресурсов — это коллекция файлов ресурсов (например, JavaScript файлы, CSS файлы, файлы изображений, и т.п.) в\nопределенной директории. Каждая связка ресурсов представлена классом, унаследованным от [[yii\\web\\AssetBundle]].\nСвязка ресурсов становится доступной через веб после её регистрации методом [[yii\\web\\AssetBundle::register()]].\nВ отличие от Yii 1.1, страница, регистрирующая связку ресурсов, автоматически будет содержать ссылки на JavaScript и CSS\nфайлы, указанные в связке.\n\n\nБолее детальная информация представлена в разделе «[Ресурсы](structure-assets.md)».\n\n\nХелперы\n-------\n\nВ Yii 2.0 включено много широко используемых статичных классов.\n\n* [[yii\\helpers\\Html]]\n* [[yii\\helpers\\ArrayHelper]]\n* [[yii\\helpers\\StringHelper]]\n* [[yii\\helpers\\FileHelper]]\n* [[yii\\helpers\\Json]]\n\nБолее детальная информация представлена в разделе «[Хелперы](helper-overview.md)».\n\n\nФормы\n-----\n\nYii 2.0 вводит новое понятие *поле* для построения форм с помощью [[yii\\widgets\\ActiveForm]]. Поле — это\nконтейнер, содержащий подпись, поле ввода, сообщение об ошибке и/или вспомогательный текст.\nПоле представлено объектом [[yii\\widgets\\ActiveField|ActiveField]]. Используя поля, вы можете строить\nформы гораздо проще чем это было раньше:\n\n```php\n<?php $form = yii\\widgets\\ActiveForm::begin(); ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n    <div class=\"form-group\">\n        <?= Html::submitButton('Login') ?>\n    </div>\n<?php yii\\widgets\\ActiveForm::end(); ?>\n```\n\nБолее детальная информация представлена в разделе «[Работа с формами](input-forms.md)».\n\n\nПостроитель запросов\n--------------------\n\nВ версии 1.1, построение запроса было разбросано среди нескольких классов, включая `CDbCommand`,\n`CDbCriteria`, и `CDbCommandBuilder`. В Yii 2.0 запрос к БД представлен в рамках объекта [[yii\\db\\Query|Query]],\nкоторый может быть превращён в SQL выражение с помощью [[yii\\db\\QueryBuilder|QueryBuilder]]. Например,\n\n```php\n$query = new \\yii\\db\\Query();\n$query->select('id, name')\n      ->from('user')\n      ->limit(10);\n\n$command = $query->createCommand();\n$sql = $command->sql;\n$rows = $command->queryAll();\n```\n\nЛучшим способом использования данных методов является работа с [Active Record](db-active-record.md).\n\nБолее детальная информация представлена в разделе «[Построитель запросов](db-query-builder.md)».\n\n\nActive Record\n-------------\n\nВ Yii 2.0 внесено множество изменений в работу [Active Record](db-active-record.md). Два основных из них включают в себя\nпостроение запросов и работу со связями.\n\nКласс `CDbCriteria` версии 1.1 был заменен [[yii\\db\\ActiveQuery]]. Этот класс наследуется от [[yii\\db\\Query]] и таким\nобразом получает все методы, необходимые для построения запроса. Чтобы начать строить запрос, следует вызвать метод\n[[yii\\db\\ActiveRecord::find()]]:\n\n```php\n// Получаем всех *активных* клиентов и сортируем их по ID\n$customers = Customer::find()\n    ->where(['status' => $active])\n    ->orderBy('id')\n    ->all();\n```\n\nДля объявления связи следует просто объявить геттер, который возвращает объект [[yii\\db\\ActiveQuery|ActiveQuery]].\nИмя свойства, определённое геттером, представляет собой название связи. Например, следующий код объявляет связь\n`orders` (в версии 1.1, вам нужно было бы объявить связи в одном месте — методе `relations()`):\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public function getOrders()\n    {\n        return $this->hasMany('Order', ['customer_id' => 'id']);\n    }\n}\n```\n\nТеперь вы можете использовать выражение `$customer->orders` для получения всех заказов клиента из связанной таблицы. Вы также\nможете использовать следующий код, чтобы применить нужные условия «на лету»:\n\n```php\n$orders = $customer->getOrders()->andWhere('status=1')->all();\n```\n\nYii 2.0 осуществляет жадную загрузку связи не так, как это было в 1.1. В частности, в версии 1.1 для выбора данных из\nосновной и связанной таблиц будет использован запрос JOIN. В Yii 2.0 будут выполнены два запроса без использования JOIN:\nпервый запрос возвращает данные для основной таблицы, а второй, осуществляющий фильтрацию по первичным ключами основной\nтаблицы — для связанной.\n\nВместо того, чтобы при выборке большого количества записей возвращать объекты [[yii\\db\\ActiveRecord|ActiveRecord]], вы\nможете использовать в построении запроса метод [[yii\\db\\ActiveQuery::asArray()|asArray()]]. Это заставит вернуть\nрезультат запроса в виде массива, что при большом количестве записей может существенно снизить затрачиваемое процессорное\nвремя и объём потребляемой памяти. Например:\n\n```php\n$customers = Customer::find()->asArray()->all();\n```\n\nЕщё одно изменение связано с тем, что вы больше не можете определять значения по умолчанию через public свойства.\nВы должны установить их в методе `init` вашего класса, если это требуется.\n\n```php\npublic function init()\n{\n    parent::init();\n    $this->status = self::STATUS_NEW;\n}\n```\n\nТакже в версии 1.1 были некоторые проблемы с переопределением конструктора ActiveRecord. Данные проблемы отсутствуют\nв версии 2.0. Обратите внимание, что при добавлении параметров в конструктор, вам, возможно, понадобится переопределить метод\n[[yii\\db\\ActiveRecord::instantiate()]].\n\nСуществует также множество других улучшений в ActiveRecord. Подробнее о них можно узнать в разделе\n«[Active Record](db-active-record.md)».\n\nПоведения Active Record\n-----------------------\n\nВ версии 2.0 отсутствует базовый класс для поведений `CActiveRecordBehavior`. Если вам необходимо создать поведение для\nActive Record, стоит наследовать его класс напрямую от `yii\\base\\Behavior`. Если поведение должно реагировать на какие-либо\nсобытия, необходимо перекрыть метод `events()` следующим образом:\n\n```php\nnamespace app\\components;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\base\\Behavior;\n\nclass MyBehavior extends Behavior\n{\n    // ...\n\n    public function events()\n    {\n        return [\n            ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',\n        ];\n    }\n\n    public function beforeValidate($event)\n    {\n        // ...\n    }\n}\n```\n\n\nUser и IdentityInterface\n---------------------------\n\nКласс `CWebUser` из версии 1.1 теперь заменён классом [[yii\\web\\User]]. Также больше не существует класса `CUserIdentity`.\nВы должны реализовать интерфейс [[yii\\web\\IdentityInterface]], что гораздо проще. Пример реализации представлен в шаблоне\nприложения advanced.\n\nБолее подробная информация представлена в разделах «[Аутентификация](security-authentication.md)»,\n«[Авторизация](security-authorization.md)» и «[Шаблон приложения advanced](tutorial-advanced-app.md)».\n\n\nРазбор и генерация URL\n----------------------\n\nРабота с URL в Yii 2.0 аналогична той, что была в версии 1.1. Основное изменение заключается в том, что теперь\nподдерживаются дополнительные параметры. Например, если у вас имеется правило, объявленное следующим образом, то\nоно совпадет с `post/popular` и `post/1/popular`. В версии 1.1, вам пришлось бы использовать два правила, для достижения\nтого же результата.\n\n```php\n[\n    'pattern' => 'post/<page:\\d+>/<tag>',\n    'route' => 'post/index',\n    'defaults' => ['page' => 1],\n]\n```\n\nБолее детальная информация представлена в разделе «[Разбор и генерация URL](runtime-routing.md)».\n\nИспользование Yii 1.1 вместе с 2.x\n----------------------------------\n\nИнформация об использовании кода для Yii 1.1 вместе с Yii 2.0 представлена в разделе\n«[Одновременное использование Yii 1.1 и 2.0](tutorial-yii-integration.md#using-both-yii2-yii1)».\n"
  },
  {
    "path": "docs/guide-ru/intro-yii.md",
    "content": "Что такое Yii?\n==============\n\nYii – это высокопроизводительный компонентный PHP фреймворк, предназначенный для быстрой разработки современных веб-приложений. Слово Yii (произносится как `Йи` `[ji:]`) в китайском языке означает «простой и эволюционирующий». Также Yii\nможет расшифровываться как акроним **Yes It Is**!\n\n\nДля каких задач больше всего подходит Yii?\n------------------------------------------\n\nYii – это универсальный фреймворк и может быть задействован во всех типах веб-приложений. Благодаря его компонентной\nструктуре и отличной поддержке кэширования, фреймворк особенно подходит для разработки таких крупных проектов, как\nпорталы, форумы, CMS, магазины или RESTful-приложения.\n\n\nСравнение Yii с другими фреймворками\n------------------------------------\n\nЕсли вы уже знакомы с другими фреймворками, вам наверняка будет интересно сравнить их с Yii.\n\n- Как и многие другие PHP фреймворки, для организации кода Yii использует архитектурный паттерн MVC (Model-View-Controller).\n- Yii придерживается философии простого и элегантного кода, не пытаясь усложнять дизайн только ради следования каким-либо\n  шаблонам проектирования.\n- Yii является full-stack фреймворком и включает в себя проверенные и хорошо зарекомендовавшие себя возможности, такие как\n  ActiveRecord для реляционных и NoSQL баз данных, поддержку REST API, многоуровневое кэширование и другие.\n- Yii отлично расширяем. Вы можете настроить или заменить практически любую часть основного кода. Используя архитектуру расширений, легко делиться кодом или использовать код сообщества.\n- Одна из главных целей Yii – производительность.\n\nYii — не проект одного человека. Он поддерживается и развивается [сильной командой](https://www.yiiframework.com/team/) и большим сообществом разработчиков,\nкоторые ей помогают. Авторы фреймворка следят за тенденциями веб-разработки и развитием других проектов. Наиболее\nподходящие возможности и лучшие практики регулярно внедряются в фреймворк в виде простых и элегантных интерфейсов.\n\nВерсии Yii\n----------\n\nНа данный момент существует две основные ветки Yii: 1.1 и 2.0. Ветка 1.1 является предыдущим поколением и находится\nв состоянии поддержки. Версия 2.0 – это полностью переписанный Yii, использующий последние технологии и протоколы, такие\nкак Composer, PSR, пространства имен, трейты и многое другое. 2.0 — текущее поколение фреймворка. На этой версии\nбудут сосредоточены основные усилия несколько следующих лет. Данное руководство именно о версии 2.0.\n\n\nТребования к ПО и знаниям\n-------------------------\n\nYii 2.0 требует PHP 7.4.0 и выше и наилучшим образом работает на последней версии PHP. Чтобы узнать требования для отдельных возможностей, вы можете запустить скрипт проверки\nтребований, который поставляется с каждым релизом фреймворка.\n\nДля разработки на Yii потребуется общее понимание ООП, так как фреймворк полностью следует этой парадигме. Также стоит\nизучить такие современные возможности PHP как [пространства имён](https://www.php.net/manual/ru/language.namespaces.php)\nи [трейты](https://www.php.net/manual/ru/language.oop5.traits.php). Понимание этих концепций позволит вам более легко освоиться c Yii 2.0.\n\n"
  },
  {
    "path": "docs/guide-ru/output-client-scripts.md",
    "content": "Работа с клиентскими скриптами\n===========================\n\nСовременные веб-приложения, помимо статических HTML-страниц, содержат JavaScript, который используется для изменения страницы в браузере путем манипулирования существующими элементами или загрузки нового контента используя AJAX.\n\nВ этом разделе описываются методы, предоставляемые Yii для добавления JavaScript и CSS на веб-сайт, а также их динамическая настройка.\n\n## Регистрация скриптов <span id=\"register-scripts\"></span>\n\nПри работе с объектом [[yii\\web\\View]] можно динамически регистрировать интерфейсные скрипты.\nДля этого есть два специальных метода:\n\n- [[yii\\web\\View::registerJs()|registerJs()]] для встраиваемых, в тело страницы, скриптов\n- [[yii\\web\\View::registerJsFile()|registerJsFile()]] для подключаемых, из внешних файлов, скриптов\n\n### Регистрация встраиваемых скриптов <span id=\"inline-scripts\"></span>\n\nВстраиваемые скрипты полезны для конфигурации динамически генерируемого кода и небольших повторно используемых фрагментов интерфейса, содержащимся в [виджетах](structure-widgets.md).\nДля их добавления можно использовать метод [[yii\\web\\View::registerJs()|registerJs()]] следующим образом:\n\n```php\n$this->registerJs(\n    \"$('#myButton').on('click', function() { alert('Button clicked!'); });\",\n    View::POS_READY,\n    'my-button-handler'\n);\n```\n\nПервый аргумент - это JavaScript-код, который мы хотим вставить на страницу. Он будет обёрнут в тег `<script>`. Второй аргумент определяет, в какой позиции скрипт должен быть вставлен на страницу.\nВозможные значения:\n\n- [[yii\\web\\View::POS_HEAD|View::POS_HEAD]] в `<head>`\n- [[yii\\web\\View::POS_BEGIN|View::POS_BEGIN]] сразу после открытия тега `<body>`\n- [[yii\\web\\View::POS_END|View::POS_END]] сразу после закрытия тега `</body>`\n- [[yii\\web\\View::POS_READY|View::POS_READY]] для выполнения кода сразу после того, как DOM полностью загрузился. Этому соответствует [событие `ready`](https://learn.jquery.com/using-jquery-core/document-ready/). При этом автоматически зарегистрируется [[yii\\web\\JqueryAsset|jQuery]]. Код будет обёрнут в соответствующий код jQuery. `POS_READY` является позицией по умолчанию.\n- [[yii\\web\\View::POS_LOAD|View::POS_LOAD]] для выполнения кода после того, как DOM полностью загрузился (включая картинки). [Событие `load`](https://learn.jquery.com/using-jquery-core/document-ready/). Так же, как и выше, при этом автоматически зарегистрируется [[yii\\web\\JqueryAsset|jQuery]]\n\nПоследний аргумент — это уникальный ID, который используется для идентификации блока со скриптом. При повторной регистрации происходит замена существующего скрипта на новый. Если вы не зададите ID, вместо него будет использоваться сам код. Это помогает избежать регистрации одного и того же кода несколько раз.\n\n### Регистрация внешнего файла скриптов <span id=\"script-files\"></span>\n\nАргументы для [[yii\\web\\View::registerJsFile()|registerJsFile()]] аналогичны аргументам для [[yii\\web\\View::registerCssFile()|registerCssFile()]]. В следующем примере мы регистрируем `main.js` с зависимостью от [[yii\\web\\JqueryAsset]]. Это означает, что `main.js` будет добавлен после `jquery.js`. Без подобного указания зависимостей относительный порядок между `main.js` и `jquery.js` будет неопределенным, и код не будет работать.\n\nВнешний скрипт может быть добавлен следующим образом:\n\n```php\n$this->registerJsFile(\n    '@web/js/main.js',\n    ['depends' => [\\yii\\web\\JqueryAsset::class]]\n);\n```\n\nВместо [[yii\\web\\View::registerJsFile()|registerJsFile()]] для регистрации внешних JS-файлов настоятельно рекомендуется использовать [пакеты ресурсов](structure-assets.md), поскольку они обеспечивают лучшую гибкость и более детальную конфигурацию зависимостей. Использование пакетов ресурсов также позволяет объединять и сжимать несколько JS файлов, что желательно для веб-сайтов с высоким трафиком.\n\n## Регистрация CSS <span id=\"register-css\"></span>\n\nПодобно JavaScript, вы можете зарегистрировать CSS используя [[yii\\web\\View::registerCss()|registerCss()]] или [[yii\\web\\View::registerCssFile()|registerCssFile()]].\nПервый регистрирует блок кода CSS, а второй регистрирует внешний файл CSS.\n\n### Регистрация встроенного CSS <span id=\"inline-css\"></span>\n\n```php\n$this->registerCss(\"body { background: #f00; }\");\n```\n\nПриведенный выше код добавит следующий код в секцию `<head>` страницы:\n\n```html\n<style>\nbody { background: #f00; }\n</style>\n```\n\nЕсли вы хотите задать дополнительные свойства тега style, передайте массив вида «имя => значение» как второй аргумент.\nПоследний аргумент — это уникальный ID, который используется для идентификации блока стиля и обеспечения его добавления только один раз в случае, если один и тот же стиль зарегистрирован в разных местах кода.\n\n### Регистрация CSS файлов <span id=\"css-files\"></span>\n\nЗарегистрировать CSS файл можно следующим способом:\n\n```php\n$this->registerCssFile(\"@web/css/themes/black-and-white.css\", [\n    'depends' => [\\yii\\bootstrap\\BootstrapAsset::class],\n    'media' => 'print',\n], 'css-print-theme');\n```\n\nПриведенный выше код добавит ссылку на CSS файл `/css/themes/black-and-white.css` в секцию `<head>` страницы.\n\n* Первый аргумент указывает CSS-файл для регистрации. `@web` в этом примере является [псевдонимом для базового URL-адреса приложения](concept-aliases.md#predefined-aliases).\n* Второй аргумент указывает атрибуты HTML для результирующего тега `<link>`. Опция `depends` обрабатывается специально. Она указывает, от каких пакетов ресурсов зависит этот CSS файл. В этом случае зависимым пакетом ресурсов является [[yii\\bootstrap\\BootstrapAsset|BootstrapAsset]]. Это означает, что CSS файл будет добавлен *после* CSS файлов из [[yii\\bootstrap\\BootstrapAsset|BootstrapAsset]].\n* Последний аргумент указывает ID, идентифицирующий этот CSS файл. Если он не указан, вместо него будет использоваться URL-адрес CSS файла.\n\nДля регистрации внешних CSS файлов вместо [[yii\\web\\View::registerCssFile()|registerCssFile()]] настоятельно рекомендуется использовать [пакеты ресурсов](structure-assets.md). Это позволяет комбинировать и сжимать несколько CSS файлов, что желательно для сайтов с высоким трафиком. Также обеспечивается большая гибкость, поскольку все зависимости ресурсов вашего приложения настраиваются в одном месте.\n\n## Регистрация пакетов ресурсов <span id=\"asset-bundles\"></span>\n\nКак упоминалось ранее, рекомендуется использовать пакеты ресурсов вместо регистрации CSS файлов и JavaScript напрямую.\nВы можете получить подробную информацию о том, как определить пакеты ресурсов в разделе [\"Ресурсы\"](structure-assets.md).\n\nГотовый пакет ресурсов можно использовать так:\n\n```php\n\\frontend\\assets\\AppAsset::register($this);\n```\n\nВ приведенном выше коде, в контексте файла представления, пакет `AppAsset` зарегистрирован в текущем представлении (`$this`).\nПри регистрации пакетов ресурсов из виджета вы должны передать [[yii\\base\\Widget::$view|$view]] виджета, вместо (`$this->view`).\n\n## Генерация динамического Javascript <span id=\"dynamic-js\"></span>\n\nЧасто в файлах шаблонов представлений, HTML-код не записывается напрямую, а генерируется неким PHP-кодом, зависящим от переменных представления.\nДля того, чтобы сгенерированный HTML мог работать с Javascript, код JS также должен содержать динамические части, например идентификаторы селекторов jQuery.\n\nЧтобы вставить переменные PHP в код JS, их значения должны быть корректно экранированы. Особенно, когда код JS вставляется в HTML, а не находится в выделенном файле JS.\nДля этой цели Yii предоставляет метод [[yii\\helpers\\Json::htmlEncode()|htmlEncode()]] хелпера [[yii\\helpers\\Json|Json]]. Его использование будет показано в следующих примерах.\n\n### Регистрация глобальной конфигурации JavaScript <span id=\"js-configuration\"></span>\n\nВ этом примере мы используем массив для передачи глобальных параметров конфигурации из PHP-части приложения в код интерфейса JS.\n\n```php\n$options = [\n    'appName' => Yii::$app->name,\n    'baseUrl' => Yii::$app->request->baseUrl,\n    'language' => Yii::$app->language,\n    // ...\n];\n$this->registerJs(\n    \"var yiiOptions = \".\\yii\\helpers\\Json::htmlEncode($options).\";\",\n    View::POS_HEAD,\n    'yiiOptions'\n);\n```\n\nПриведенный выше код зарегистрирует тег `<script>`, содержащий определение переменной JavaScript, например:\n\n```javascript\nvar yiiOptions = {\"appName\":\"My Yii Application\",\"baseUrl\":\"/basic/web\",\"language\":\"en\"};\n```\n\nТеперь в вашем JavaScript коде вы можете получить к ним доступ, например: `yiiOptions.baseUrl` или `yiiOptions.language`.\n\n### Передача сообщений перевода <span id=\"translated-messages\"></span>\n\nВы можете столкнуться со случаем, когда ваш JavaScript должен вывести сообщение, реагирующее на какое-то событие. В приложении, которое работает с несколькими языками, эта строка должна быть переведена на текущий язык приложения.\nОдин из способов достичь этого — использовать [интернационализацию](tutorial-i18n.md#message-translation), предоставляемую Yii и передавать результат в код JavaScript.\n\n```php\n$message = \\yii\\helpers\\Json::htmlEncode(\n    \\Yii::t('app', 'Button clicked!')\n);\n$this->registerJs(<<<JS\n    $('#myButton').on('click', function() { alert( $message ); });\nJS\n);\n```\n\nПриведенный выше пример кода использует PHP [синтаксис Heredoc](https://www.php.net/manual/ru/language.types.string.php#language.types.string.syntax.heredoc) для лучшей читаемости. Это также обеспечивает лучшую подсветку синтаксиса в большинстве IDE, поэтому это предпочтительный способ написания встроенного JavaScript, особенно полезный для кода, более длинного чем однострочный. Переменная `$message` создается PHP и благодаря [[yii\\helpers\\Json::htmlEncode|Json::htmlEncode]] содержит строку в допустимом синтаксисе JS, которую можно вставить в JavaScript код, чтобы поместить динамическую строку в вызов функции `alert()`.\n\n> Note: При использовании Heredoc, будьте осторожны с именами переменных в коде JS, поскольку переменные, начинающиеся с `$`, могут интерпретироваться как переменные PHP, которые будут заменены их содержимым.\n> jQuery функция в форме `$(` или `$.` не интерпретируется как переменная PHP и может безопасно использоваться.\n\n## Скрипт `yii.js`<span id=\"yii.js\"></span>\n\n> Note: Этот раздел еще не написан. Он должен содержать объяснение функциональности, предоставляемой `yii.js`:\n> \n> - JavaScript модули Yii\n> - Обработчик параметра CSRF\n> - Обработчик `data-confirm`\n> - Обработчик `data-method`\n> - Фильтрация скриптов\n> - Обработка перенаправления\n"
  },
  {
    "path": "docs/guide-ru/output-data-providers.md",
    "content": "Провайдеры данных\n==============\n\nВ разделах [Постраничное разделение данных](output-pagination.md) и [Сортировка](output-sorting.md) было описано, \nкак сделать возможность для конечных пользователей, чтобы они могли выбирать определённую страницу для вывода данных и\nсортировку их по некоторым колонкам.\n\nПровайдер данных это класс, который реализует [[yii\\data\\DataProviderInterface]]. Такая реализация поддерживает в основном \nразбивку на страницы и сортировку. Они обычно используются для работы [виджетов данных](output-data-widgets.md), что позволяет\nконечным пользователям интерактивно использовать сортировку данных и их разбивку на страницы.\n\nВ Yii реализованы следующие классы провайдеров данных:\n\n* [[yii\\data\\ActiveDataProvider]]: использует [[yii\\db\\Query]] или [[yii\\db\\ActiveQuery]] для запроса данных из базы данных,\nвозвращая их в виде массива или экземпляров [Active Record](db-active-record.md).\n* [[yii\\data\\SqlDataProvider]]: выполняет запрос SQL к базе данных и возвращает результат в виде массива.\n* [[yii\\data\\ArrayDataProvider]]: принимает большой массив и возвращает выборку из него с возможностью сортировки и разбивки\n на страницы.\n\nИспользование всех этих провайдеров данных имеет общую закономерность:\n\n```php\n// создание провайдера данных с конфигурацией для сортировки и постраничной разбивки\n$provider = new XyzDataProvider([\n    'pagination' => [...],\n    'sort' => [...],\n]);\n\n// Получение данных с разбивкой на страницы и сортировкой.\n$models = $provider->getModels();\n\n// получение количества данных на текущей странице\n$count = $provider->getCount();\n\n// получение общего количества данных на всех страницах\n$totalCount = $provider->getTotalCount();\n```\n\nОпределение поведений сортировки и разбивки для провайдера данных устанавливается через его свойства\n[[yii\\data\\BaseDataProvider::pagination|pagination]] и [[yii\\data\\BaseDataProvider::sort|sort]], которые соответствуют\nнастройкам [[yii\\data\\Pagination]] and [[yii\\data\\Sort]]. Вы можете отключить сортировку и разбивку на страницы путём\nвыставления их настроек в `false`.\n\n[Виджеты данных](output-data-widgets.md), такие как [[yii\\grid\\GridView]], имеют свойство `dataProvider`, которое может\nпринимать экземпляр провайдера данных для отображения его данных. Например:\n\n```php\necho yii\\grid\\GridView::widget([\n    'dataProvider' => $dataProvider,\n]);\n```\n\nЭти провайдеры данных в некоторой степени различаются по использовании, в зависимости от источника данных. Далее \nопишем более подробно использование каждого провайдера данных.\n\n## ActiveDataProvider <span id=\"active-data-provider\"></span> \n\nДля использования [[yii\\data\\ActiveDataProvider]], необходимо настроить его свойство [[yii\\data\\ActiveDataProvider::query|query]].\nОно принимает любой объект [[yii\\db\\Query]] или [[yii\\db\\ActiveQuery]]. Если использовать первый, то данные будут возвращены в \nвиде массивов, если второй - данные также могут быть возвращены в виде массивов, а также в виде экземпляров \n[Active Record](db-active-record.md). Например:\n\n```php\nuse yii\\data\\ActiveDataProvider;\n\n$query = Post::find()->where(['status' => 1]);\n\n$provider = new ActiveDataProvider([\n    'query' => $query,\n    'pagination' => [\n        'pageSize' => 10,\n    ],\n    'sort' => [\n        'defaultOrder' => [\n            'created_at' => SORT_DESC,\n            'title' => SORT_ASC, \n        ]\n    ],\n]);\n\n// возвращает массив Post объектов\n$posts = $provider->getModels();\n```\n\nЕсли изменить `$query` в этом примере на следующий код, то будут возвращены сырые массивы.\n\n```php\nuse yii\\db\\Query;\n\n$query = (new Query())->from('post')->where(['status' => 1]); \n```\n\n> Note: Если query содержит условия сортировки в `orderBy`, то новые условия, полученные от конечных пользователей\n (через настройки `sort`) будут добавлены к существующим условиям в `orderBy`. Любые условия в `limit` и `offset` \n будут переписаны запросом конечного пользователя к различным страницам (через конфигурацию  `pagination`).\n\nПо умолчанию [[yii\\data\\ActiveDataProvider]] использует компонент приложения `db` для подключения к базе данных. Можно\nиспользовать разные базы данных, настроив подключение через конфигурацию свойства [[yii\\data\\ActiveDataProvider::db]].\n\n## SqlDataProvider <span id=\"sql-data-provider\"></span>\n\n[[yii\\data\\SqlDataProvider]] работает с сырыми запросами SQL, которые используются для извлечение необходимых данных.\nОсновываясь на спецификации из [[yii\\data\\SqlDataProvider::sort|sort]] и  [[yii\\data\\SqlDataProvider::pagination|pagination]],\nпровайдер данных будет добавлять конструкции `ORDER BY` и `LIMIT` к SQL-запросу, для возврата только запрошенной \nстраницы данных с учётом определённой сортировки.\n\nДля использования [[yii\\data\\SqlDataProvider]], необходимо настроить свойства [[yii\\data\\SqlDataProvider::sql|sql]] и\n[[yii\\data\\SqlDataProvider::totalCount|totalCount]]. Например:\n\n```php\nuse yii\\data\\SqlDataProvider;\n\n$count = Yii::$app->db->createCommand('\n    SELECT COUNT(*) FROM post WHERE status=:status\n', [':status' => 1])->queryScalar();\n\n$provider = new SqlDataProvider([\n    'sql' => 'SELECT * FROM post WHERE status=:status',\n    'params' => [':status' => 1],\n    'totalCount' => $count,\n    'pagination' => [\n        'pageSize' => 10,\n    ],\n    'sort' => [\n        'attributes' => [\n            'title',\n            'view_count',\n            'created_at',\n        ],\n    ],\n]);\n\n// возвращает массив данных\n$models = $provider->getModels();\n```\n\n> Совет: Свойство [[yii\\data\\SqlDataProvider::totalCount|totalCount]] обязательно только тогда, когда вам нужна разбивка\nна страницы. Всё потому, что запрос SQL [[yii\\data\\SqlDataProvider::sql|sql]] будет изменяться провайдером данных для возврата\nтолько текущей запрошенной страницы. Провайдеру необходимо знать общее количество данных в запросе для корректного \nвычисления разбивки на доступные страницы.\n\n## ArrayDataProvider <span id=\"array-data-provider\"></span>\n\n[[yii\\data\\ArrayDataProvider]] лучше использовать для работы с большим массивом. Этот провайдер помогает вернуть выборку\nиз большого массива с сортировкой по одному или нескольким колонкам. Для использования [[yii\\data\\ArrayDataProvider]]\nнеобходимо определить свойство [[yii\\data\\ArrayDataProvider::allModels|allModels]], как большой массив. Элементы в \nбольшом массиве могут быть ассоциативными массивами (например, результаты выборки из [DAO](db-dao.md)) или объекты (экземпляры [Active Record](db-active-record.md)). Например:\n\n```php\nuse yii\\data\\ArrayDataProvider;\n\n$data = [\n    ['id' => 1, 'name' => 'name 1', ...],\n    ['id' => 2, 'name' => 'name 2', ...],\n    ...\n    ['id' => 100, 'name' => 'name 100', ...],\n];\n\n$provider = new ArrayDataProvider([\n    'allModels' => $data,\n    'pagination' => [\n        'pageSize' => 10,\n    ],\n    'sort' => [\n        'attributes' => ['id', 'name'],\n    ],\n]);\n\n// получает строки для текущей запрошенной странице\n$rows = $provider->getModels();\n``` \n\n> Note: По сравнению с [Active Data Provider](#active-data-provider) и [SQL Data Provider](#sql-data-provider),\nArrayDataProvider менее эффективный, потому что требует загрузки *всех* данных в память.\n\n\n## Принципы работы с ключами данных <span id=\"working-with-keys\"></span>\n\nПри возврате данных с помощью провайдера, часто требуется идентификация каждого элемента по уникальному ключу. Например,\nесли данные - это какая-то информация по клиенту, то возможно понадобится использовать ID клиента, как ключ для данных по \nкаждому клиенту. Провайдер данных через [[yii\\data\\DataProviderInterface::getModels()]] может вернуть список из ключей\nи соответствующего набора данных. Например,\n\n```php\nuse yii\\data\\ActiveDataProvider;\n\n$query = Post::find()->where(['status' => 1]);\n\n$provider = new ActiveDataProvider([\n    'query' => $query,\n]);\n\n// возвращает массив объектов Post\n$posts = $provider->getModels();\n\n// возвращает значения первичного ключа в соответствии с $posts\n$ids = $provider->getKeys();\n```\n\nВ вышеописанном примере, так как [[yii\\data\\ActiveDataProvider]] предоставляется один [[yii\\db\\ActiveQuery]] объект, то\nв этом случае провайдер достаточно умён, чтобы вернуть значения первичных ключей в качестве идентификатора. Также есть\nвозможность настроить способ вычисления значения идентификатора, через настройку [[yii\\data\\ActiveDataProvider::key]], как\nимя колонки или функцию вычисления значений ключа. Например:\n\n```php\n// в качестве ключа используется столбец \"slug\"\n$provider = new ActiveDataProvider([\n    'query' => Post::find(),\n    'key' => 'slug',\n]);\n\n// в качестве ключа используется md5(id)\n$provider = new ActiveDataProvider([\n    'query' => Post::find(),\n    'key' => function ($model) {\n        return md5($model->id);\n    }\n]);\n```\n\n\n## Создание своего провайдера данных <span id=\"custom-data-provider\"></span>\n\nДля создания своих классов провайдера данных, необходимо реализовать [[yii\\data\\DataProviderInterface]]. Простой способ \nсделать это - наследовать [[yii\\data\\BaseDataProvider]], который помогает сфокусироваться на логике ядра провайдера данных.\nВ основном необходимо реализовать следующие методы:\n                                                   \n- [[yii\\data\\BaseDataProvider::prepareModels()|prepareModels()]]: подготавливает модели данных, которые будут доступны\n в текущей странице и возвращает их в виде массива.\n- [[yii\\data\\BaseDataProvider::prepareKeys()|prepareKeys()]]: принимает массив имеющихся в настоящее время моделей \nданных и возвращает ключи, связанные с ними.\n- [[yii\\data\\BaseDataProvider::prepareTotalCount()|prepareTotalCount]]: возвращает значение, указывающее общее количество\n моделей данных в провайдере данных.\n\nНиже приведён пример провайдера данных, который эффективно считывает данные из CSV:\n\n```php\n<?php\nuse yii\\data\\BaseDataProvider;\n\nclass CsvDataProvider extends BaseDataProvider\n{\n    /**\n     * @var string имя CSV-файла для чтения\n     */\n    public $filename;\n    \n    /**\n     * @var string|callable имя столбца с ключом или callback-функция, возвращающие его\n     */\n    public $key;\n    \n    /**\n     * @var SplFileObject\n     */\n    protected $fileObject; // с помощью SplFileObject очень удобно искать конкретную строку в файле\n    \n \n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        \n        // открыть файл\n        $this->fileObject = new SplFileObject($this->filename);\n    }\n \n    /**\n     * {@inheritdoc}\n     */\n    protected function prepareModels()\n    {\n        $models = [];\n        $pagination = $this->getPagination();\n \n        if ($pagination === false) {\n            // в случае отсутствия разбивки на страницы - прочитать все строки\n            while (!$this->fileObject->eof()) {\n                $models[] = $this->fileObject->fgetcsv();\n                $this->fileObject->next();\n            }\n        } else {\n            // в случае, если разбивка на страницы есть - прочитать только одну страницу\n            $pagination->totalCount = $this->getTotalCount();\n            $this->fileObject->seek($pagination->getOffset());\n            $limit = $pagination->getLimit();\n \n            for ($count = 0; $count < $limit; ++$count) {\n                $models[] = $this->fileObject->fgetcsv();\n                $this->fileObject->next();\n            }\n        }\n \n        return $models;\n    }\n \n    /**\n     * {@inheritdoc}\n     */\n    protected function prepareKeys($models)\n    {\n        if ($this->key !== null) {\n            $keys = [];\n \n            foreach ($models as $model) {\n                if (is_string($this->key)) {\n                    $keys[] = $model[$this->key];\n                } else {\n                    $keys[] = call_user_func($this->key, $model);\n                }\n            }\n \n            return $keys;\n        } else {\n            return array_keys($models);\n        }\n    }\n \n    /**\n     * {@inheritdoc}\n     */\n    protected function prepareTotalCount()\n    {\n        $count = 0;\n \n        while (!$this->fileObject->eof()) {\n            $this->fileObject->next();\n            ++$count;\n        }\n \n        return $count;\n    }\n}\n```\n"
  },
  {
    "path": "docs/guide-ru/output-data-widgets.md",
    "content": "Виджеты для данных\n============\n\nYii предоставляет набор [виджетов](structure-widgets.md), которые могут быть использованы для отображения данных.\nВ то время как виджет [DetailView](#detail-view) может быть использован для отображения данных по одной записи, то\nвиджеты [ListView](#list-view) и [GridView](#grid-view) могут быть использованы для показа данных в виде списка или\nтаблицы с возможностью сортировки, фильтрации и разбивки данных постранично.\n\n\nDetailView <span id=\"detail-view\"></span>\n----------\n\nВиджет [[yii\\widgets\\DetailView|DetailView]] отображает детали по данным для одной [[yii\\widgets\\DetailView::$model|model]].\n\nЭтот виджет лучше использовать для отображения данных модели в обычном формате(т.е. каждый атрибут модели будет представлен\nв виде строки в таблице). Модель может быть либо объектом класса [[\\yii\\base\\Model]] или его наследником, таких как\n[active record](db-active-record.md) , либо ассоциативным массивом.\n\nDetailView использует свойство [[yii\\widgets\\DetailView::$attributes|$attributes]] для определений, какие атрибуты модели\nдолжны быть показаны и в каком формате. Обратитесь к разделу [Форматирование данных](output-formatting.md) за возможными\nнастройками форматирования.\n\nОбычное использование DetailView сводится к следующему коду:\n\n```php\necho DetailView::widget([\n    'model' => $model,\n    'attributes' => [\n        'title',                                           // title свойство (обычный текст)\n        'description:html',                                // description свойство, как HTML\n        [                                                  // name свойство зависимой модели owner\n            'label' => 'Owner',\n            'value' => $model->owner->name,            \n            'contentOptions' => ['class' => 'bg-red'],     // настройка HTML атрибутов для тега, соответсвующего value\n            'captionOptions' => ['tooltip' => 'Tooltip'],  // настройка HTML атрибутов для тега, соответсвующего label\n        ],\n        'created_at:datetime',                             // дата создания в формате datetime\n    ],\n]);\n```\n\nListView <span id=\"list-view\"></span>\n--------\n\nВиджет [[yii\\widgets\\ListView|ListView]] использует для отображения информации [провайдера данных](output-data-providers.md).\nКаждая модель отображается, используя определённый [[yii\\widgets\\ListView::$itemView|вид]]. Поскольку провайдер включает\nв себя разбивку на страницы, сортировку и фильтрацию, то его использование удобно для отображения информации конечному\nпользователю и создания интерфейса управления данными.\n\nОбычное использование сводится к следующему коду:\n\n```php\nuse yii\\widgets\\ListView;\nuse yii\\data\\ActiveDataProvider;\n\n$dataProvider = new ActiveDataProvider([\n    'query' => Post::find(),\n    'pagination' => [\n        'pageSize' => 20,\n    ],\n]);\necho ListView::widget([\n    'dataProvider' => $dataProvider,\n    'itemView' => '_post',\n]);\n```\n\n`_post` файл вид, который может содержать следующее:\n\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\helpers\\HtmlPurifier;\n?>\n<div class=\"post\">\n    <h2><?= Html::encode($model->title) ?></h2>\n\n    <?= HtmlPurifier::process($model->text) ?>\n</div>\n```\n\nВ вышеописанном коде текущая модель доступна как `$model`. Кроме этого доступны дополнительные переменные:\n\n- `$key`: mixed, значение ключа в соответствии с данными.\n- `$index`: integer, индекс элемента данных в массиве элементов, возвращенных поставщику данных, который начинается с 0.\n- `$widget`: ListView, это экземпляр виджета.\n\nЕсли необходимо послать дополнительные данные в каждый вид, то можно использовать свойство [[yii\\widgets\\ListView::$viewParams|$viewParams]]\nкак ключ-значение, например:\n\n```php\necho ListView::widget([\n    'dataProvider' => $dataProvider,\n    'itemView' => '_post',\n    'viewParams' => [\n        'fullView' => true,\n        'context' => 'main-page',\n        // ...\n    ],\n]);\n```\n\nОни также станут доступны в виде в качестве переменных.\n\n\nGridView <span id=\"grid-view\"></span>\n--------\n\nТаблица данных или GridView - это один из сверхмощных Yii виджетов. Он может быть полезен, если необходимо быстро создать\nадминистративный раздел системы. GridView использует данные, как [провайдер данных](output-data-providers.md) и отображает\nкаждую строку используя [[yii\\grid\\GridView::columns|columns]] для предоставления данных в таблице.\n\nКаждая строка из таблицы представлена данными из одиночной записи и колонка, как правило, представляет собой атрибут\nзаписи (некоторые столбцы могут соответствовать сложным выражениям атрибутов или статическому тексту).\n\nМинимальный код, который необходим для использования GridView:\n\n```php\nuse yii\\grid\\GridView;\nuse yii\\data\\ActiveDataProvider;\n\n$dataProvider = new ActiveDataProvider([\n    'query' => Post::find(),\n    'pagination' => [\n        'pageSize' => 20,\n    ],\n]);\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n]);\n```\n\nВ вышеприведённом коде сначала создаётся провайдер данных и затем используется GridView для отображения атрибутов для\nкаждого элемента из провайдера данных. Отображенная таблица оснащена функционалом сортировки и разбивки на страницы из\nкоробки.\n\n### Колонки таблицы\n\nКолонки таблицы настраиваются с помощью определённых [[yii\\grid\\Column]] классов, которые настраиваются в свойстве\n[[yii\\grid\\GridView::columns|columns]] виджета GridView. В зависимости от типа колонки и их настроек, данные отображаются\nпо разному. По умолчанию это класс [[yii\\grid\\DataColumn]], который представляет атрибут модели с возможностью сортировки\nи фильтрации по нему.\n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'columns' => [\n        ['class' => 'yii\\grid\\SerialColumn'],\n        // Обычные поля определенные данными содержащимися в $dataProvider.\n        // Будут использованы данные из полей модели.\n        'id',\n        'username',\n        // Более сложный пример.\n        [\n            'class' => 'yii\\grid\\DataColumn', // может быть опущено, поскольку является значением по умолчанию\n            'value' => function ($data) {\n                return $data->name; // $data['name'] для массивов, например, при использовании SqlDataProvider.\n            },\n        ],\n    ],\n]);\n```\n\nУчтите, что если [[yii\\grid\\GridView::columns|columns]] не сконфигурирована, то Yii попытается отобразить все возможные\nколонки из провайдера данных.\n\n### Классы колонок\n\nКолонки таблицы могут быть настроены, используя различные классы колонок:\n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'columns' => [\n        [\n            'class' => 'yii\\grid\\SerialColumn', // <-- тут\n            // тут можно настроить дополнительные свойства\n        ],\n```\n\nВ дополнение к классам колонок от Yii, вы можете самостоятельно создать свой собственный класс.\n\nКаждый класс колонки наследуется от [[yii\\grid\\Column]], так что есть некоторые общие параметры, которые можно установить\nпри настройке колонок.\n\n- [[yii\\grid\\Column::header|header]] позволяет установить содержание для строки заголовка.\n- [[yii\\grid\\Column::footer|footer]] позволяет установить содержание для \"подвала\".\n- [[yii\\grid\\Column::visible|visible]] определяет, должен ли столбец быть видимым.\n- [[yii\\grid\\Column::content|content]] позволяет передавать действительный обратный вызов, который будет возвращать данные для строки.Формат следующий:\n\n  ```php\n  function ($model, $key, $index, $column) {\n      return 'a string';\n  }\n  ```\n\nВы можете задать различные параметры контейнера HTML через массивы:\n\n- [[yii\\grid\\Column::headerOptions|headerOptions]]\n- [[yii\\grid\\Column::footerOptions|footerOptions]]\n- [[yii\\grid\\Column::filterOptions|filterOptions]]\n- [[yii\\grid\\Column::contentOptions|contentOptions]]\n\n\n#### DataColumn <span id=\"data-column\"></span>\n\n[[yii\\grid\\DataColumn|Data column]] используется для отображения и сортировки данных. По умолчанию этот тип\nиспользуется для всех колонок.\n\nОсновная настройка этой колонки - это свойство [[yii\\grid\\DataColumn::format|format]]. Значение этого свойства посылается\nв методы `formatter` [компонента](structure-application-components.md), который по умолчанию [[\\yii\\i18n\\Formatter|Formatter]]\n\n```php\necho GridView::widget([\n    'columns' => [\n        [\n            'attribute' => 'name',\n            'format' => 'text'\n        ],\n        [\n            'attribute' => 'birthday',\n            'format' => ['date', 'php:Y-m-d']\n        ],\n        'created_at:datetime', // короткий вид записи формата\n        [\n            'label' => 'Education',\n            'attribute' => 'education',\n            'filter' => ['0' => 'Elementary', '1' => 'Secondary', '2' => 'Higher'],\n            'filterInputOptions' => ['prompt' => 'All educations', 'class' => 'form-control', 'id' => null]\n        ],\n    ],\n]);\n```\n\nВ вышеприведённом коде  `text` соответствует [[\\yii\\i18n\\Formatter::asText()]]. В качестве первого аргумента для этого\nметода будет передаваться значение колонки. Во второй колонки описано  `date`, которая соответствует [[\\yii\\i18n\\Formatter::asDate()]].\nВ качестве первого аргумента, опять же, будет передаваться значение колонки, в то время как второй аргумент будет\n'php:Y-m-d'.\n\nДоступный список форматов смотрите в разделе [Форматирование данных](output-formatting.md).\n\nДля конфигурации колонок данных также доступен короткий вид записи, который описан в API документации для [[yii\\grid\\GridView::columns|колонок]].\n\nИспользуйте [[yii\\grid\\DataColumn::filter|filter]] и [[yii\\grid\\DataColumn::filterInputOptions|filterInputOptions]] для\nнастройки HTML кода фильтра.\n\nПо умолчанию заголовки колонок генерируются используя [[yii\\data\\Sort::link]]. Это можно изменить через свойство\n[[yii\\grid\\Column::header]]. Для изменения заголовка нужно задать [[yii\\grid\\DataColumn::$label]], как в\nпримере выше. По умолчанию текст будет взят из модели данных. Подробное описание ищите в [[yii\\grid\\DataColumn::getHeaderCellLabel]].\n\n#### ActionColumn\n\n[[yii\\grid\\ActionColumn|ActionColumn]] отображает кнопки действия, такие как изменение или удаление для каждой строки.\n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'columns' => [\n        [\n            'class' => 'yii\\grid\\ActionColumn',\n            // вы можете настроить дополнительные свойства здесь.\n        ],\n```\n\nДоступные свойства для конфигурации:\n\n- [[yii\\grid\\ActionColumn::controller|controller]] это идентификатор контроллера, который должен обрабатывать действия.\n Если не установлен, то будет использоваться текущий активный контроллер.\n- [[yii\\grid\\ActionColumn::template|template]] определяет шаблон для каждой ячейки в колонке действия. Маркеры заключённые\n в фигурные скобки являются ID действием контроллера (также называются *именами кнопок* в контексте колонки действия).\n Они могут быть заменены, через свойство [[yii\\grid\\ActionColumn::$buttons|buttons]]. Например, маркер `{view}` будет\n заменён результатом из функции, определённой в `buttons['view']`. Если такая функция не может быть найдена, то маркер\n заменяется на пустую строку. По умолчанию шаблон имеет вид `{view} {update} {delete}`.\n- [[yii\\grid\\ActionColumn::buttons|buttons]] массив из функций для отображения кнопок. Ключи массива представлены как\n имена кнопок (как описывалось выше), а значения представлены в качестве анонимных функций, которые выводят кнопки. Замыкания\n должны использоваться в следующем виде:\n\n  ```php\n  function ($url, $model, $key) {\n      // возвращаем HTML код для кнопки\n  }\n  ```\n  где, `$url` - это URL, который будет повешен как ссылка на кнопку, `$model` - это объект модели для текущей строки и\n  `$key` - это ключ для модели из провайдера данных.\n\n- [[yii\\grid\\ActionColumn::urlCreator|urlCreator]] замыкание, которое создаёт URL используя информацию из модели. Вид\n замыкания должен быть таким же как и в [[yii\\grid\\ActionColumn::createUrl()]]. Если свойство не задано, то URL для кнопки\n будет создана используя метод [[yii\\grid\\ActionColumn::createUrl()]].\n- [[yii\\grid\\ActionColumn::visibleButtons|visibleButtons]] это массив условий видимости каждой из кнопок.\n Ключи массива представлены как имена кнопок (как описывалось выше), а значения представлены как булево значение или\n анонимная функция. Если имя кнопки не описано в массиве, она будет отображена по умолчанию.\n Замыкания должны использоваться в следующем виде:\n\n ```php\n function ($model, $key, $index) {\n   return $model->status === 'editable'; // отображать ли кнопку\n }\n ```\n\n Или вы можете передать булево значение:\n\n ```php\n [\n     'update' => \\Yii::$app->user->can('update')\n ]\n ```\n\n#### CheckboxColumn\n\n[[yii\\grid\\CheckboxColumn|Checkbox column]] отображает колонку как флаг (сheckbox).\n\nДля добавления CheckboxColumn в виджет GridView, необходимо добавить его в  [[yii\\grid\\GridView::$columns|columns]]:\n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'columns' => [\n        // ...\n        [\n            'class' => 'yii\\grid\\CheckboxColumn',\n            // вы можете настроить дополнительные свойства здесь.\n        ],\n    ],\n```\n\nПользователи могут нажимать на флаги для выделения строк в таблице. Отмеченные строки могут быть обработаны с помощью\nJavaScript кода:\n\n```javascript\nvar keys = $('#grid').yiiGridView('getSelectedRows');\n// массив ключей для отмеченных строк\n```\n\n#### SerialColumn\n\n[[yii\\grid\\SerialColumn|Serial column]] выводит в строках номера начиная с `1` и увеличивая их по мере вывода строк.\n\nИспользование очень простое :\n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'columns' => [\n        ['class' => 'yii\\grid\\SerialColumn'], // <-- тут\n        // ...\n```\n\n\n### Сортировка данных\n\n> Note: Эта секция под разработкой\n>\n> - https://github.com/yiisoft/yii2/issues/1576\n\n### Фильтрация данных\n\nДля фильтрации данных в GridView необходима [модель](structure-models.md), которая описывает форму для фильтрации, внося\nусловия в запрос поиска для провайдера данных.\nОбщепринятой практикой считается использование [active records](db-active-record.md) и создание для неё класса модели для\nпоиска, которая содержит необходимую функциональность(может быть сгенерирована через [Gii](start-gii.md)). Класс модели\nдля поиска должен описывать правила валидации и реализовать метод `search()`, который будет возвращать провайдер данных.\n\nДля поиска возможных `Post` моделей, можно создать `PostSearch` наподобие следующего примера:\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse Yii;\nuse yii\\base\\Model;\nuse yii\\data\\ActiveDataProvider;\n\nclass PostSearch extends Post\n{\n    public function rules()\n    {\n        // только поля определенные в rules() будут доступны для поиска\n        return [\n            [['id'], 'integer'],\n            [['title', 'creation_date'], 'safe'],\n        ];\n    }\n\n    public function scenarios()\n    {\n        // bypass scenarios() implementation in the parent class\n        return Model::scenarios();\n    }\n\n    public function search($params)\n    {\n        $query = Post::find();\n\n        $dataProvider = new ActiveDataProvider([\n            'query' => $query,\n        ]);\n\n        // загружаем данные формы поиска и производим валидацию\n        if (!($this->load($params) && $this->validate())) {\n            return $dataProvider;\n        }\n\n        // изменяем запрос добавляя в его фильтрацию\n        $query->andFilterWhere(['id' => $this->id]);\n        $query->andFilterWhere(['like', 'title', $this->title])\n              ->andFilterWhere(['like', 'creation_date', $this->creation_date]);\n\n        return $dataProvider;\n    }\n}\n\n```\n\nТеперь можно использовать этот метод в контроллере, чтобы получить провайдер данных для GridView:\n\n```php\n$searchModel = new PostSearch();\n$dataProvider = $searchModel->search(Yii::$app->request->get());\n\nreturn $this->render('myview', [\n    'dataProvider' => $dataProvider,\n    'searchModel' => $searchModel,\n]);\n```\n\nи в виде присвоить их  `$dataProvider` и `$searchModel` в виджете GridView:\n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'filterModel' => $searchModel,\n    'columns' => [\n        // ...\n    ],\n]);\n```\n\n### Отдельная форма фильтрации\n\nФильтров в шапке GridView достаточно для большинства задач, но добавление отдельной формы фильтрации не представляет\nособой сложности. Она бывает полезна в случае необходимости фильтрации по полям, которые не отображаются в GridView\nили особых условий фильтрации, например по диапазону дат.\n\nСоздайте частичное представление `_search.php` со следующим содержимым:\n\n```php\n<?php\n\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n/**\n * @var \\yii\\web\\View $this\n * @var \\app\\models\\PostSearch $model\n * @var \\yii\\widgets\\ActiveForm $form\n */\n?>\n\n<div class=\"post-search\">\n    <?php $form = ActiveForm::begin([\n        'action' => ['index'],\n        'method' => 'get',\n    ]); ?>\n\n    <?= $form->field($model, 'title') ?>\n\n    <?= $form->field($model, 'creation_date') ?>\n\n    <div class=\"form-group\">\n        <?= Html::submitButton('Искать', ['class' => 'btn btn-primary']) ?>\n        <?= Html::resetButton('Сбросить', ['class' => 'btn btn-default']) ?>\n    </div>\n\n    <?php ActiveForm::end(); ?>\n</div>\n```\n\nи добавьте его отображение в `index.php` таким образом:\n\n```php\n<?= $this->render('_search', ['model' => $searchModel]) ?>\n```\n\n> Note: если вы используете Gii для генерации CRUD кода, отдельная форма фильтрации (`_search.php`)\nгенерируется по умолчанию, но закомментирована в представлении `index.php`. Вам остается только раскомментировать\nэту строку и форма готова к использованию!\n\nДля фильтра по диапазону дат мы можем добавить дополнительные атрибуты `createdFrom` и `createdTo` в поисковую модель\n(их нет в соответствующей таблице модели):\n\n```php\nclass PostSearch extends Post\n{\n    /**\n     * @var string\n     */\n    public $createdFrom;\n\n    /**\n     * @var string\n     */\n    public $createdTo;\n}\n```\n\nРасширим условия запроса в методе `search()`:\n\n```php\n$query->andFilterWhere(['>=', 'creation_date', $this->createdFrom])\n      ->andFilterWhere(['<=', 'creation_date', $this->createdTo]);\n```\n\nИ добавим соответствующие поля в форму фильтрации:\n\n```php\n<?= $form->field($model, 'creationFrom') ?>\n\n<?= $form->field($model, 'creationTo') ?>\n```\n\n### Отображение зависимых моделей\n\nБывают случаи, когда необходимо в GridView вывести в колонке значения из зависимой модели для active records, например\nимя автора новости, вместо его `id`. Для этого необходимо задать [[yii\\grid\\GridView::$columns]] как `author.name`, если\nже модель `Post` содержит зависимость с именем `author` и имя автора хранится в атрибуте `name`. GridView отобразит\nимя автора, но вот сортировка и фильтрации по этому полю будет не доступна. Необходимо дополнить некоторый функционал в\n`PostSearch` модель, которая была упомянута в предыдущем разделе.\n\nДля включения сортировки по зависимой колонки необходимо присоединить зависимую таблицу и добавить правило в компонент\nSort для провайдера данных.:\n\n```php\n$query = Post::find();\n$dataProvider = new ActiveDataProvider([\n    'query' => $query,\n]);\n\n// присоединяем зависимость `author` которая является связью с таблицей `users`\n// и устанавливаем алиас таблицы в значение `author`\n$query->joinWith(['author' => function($query) { $query->from(['author' => 'users']); }]);\n// добавляем сортировку по колонке из зависимости\n$dataProvider->sort->attributes['author.name'] = [\n    'asc' => ['author.name' => SORT_ASC],\n    'desc' => ['author.name' => SORT_DESC],\n];\n\n// ...\n```\n\nФильтрации также необходим вызов joinWith, как описано выше. Также необходимо определить для поиска столбец в атрибутах\nи правилах:\n\n```php\npublic function attributes()\n{\n    // делаем поле зависимости доступным для поиска\n    return array_merge(parent::attributes(), ['author.name']);\n}\n\npublic function rules()\n{\n    return [\n        [['id'], 'integer'],\n        [['title', 'creation_date', 'author.name'], 'safe'],\n    ];\n}\n```\n\nВ `search()` просто добавляется другое условие фильтрации:\n\n```php\n$query->andFilterWhere(['LIKE', 'author.name', $this->getAttribute('author.name')]);\n```\n\n> Info: В коде, что выше, используется такая же строка, как и имя зависимости и псевдонима таблицы.\n> Однако, когда ваш псевдоним и имя связи различаются, вы должны обратить внимание, где вы используете псевдоним,\n> а где имя связи. Простым правилом для этого является использование псевдонима в каждом месте, которое используется\n> для построения запроса к базе данных, и имя связи во всех других определениях, таких как `attributes()`, `rules()` и т.д.\n>\n> Например, если вы используете псевдоним `au` для связи с таблицей автора, то joinWith будет выглядеть так:\n>\n> ```php\n> $query->joinWith(['author' => function($query) { $query->from(['au' => 'users']); }]);\n> ```\n> Это также возможно вызвать как `$query->joinWith(['author']);`, когда псевдоним определен в определении отношения.\n>\n> Псевдоним должен быть использован в состоянии фильтра, но имя атрибута остается неизменным:\n>\n> ```php\n> $query->andFilterWhere(['LIKE', 'au.name', $this->getAttribute('author.name')]);\n> ```\n>\n> То же самое верно и для определения сортировки:\n>\n> ```php\n> $dataProvider->sort->attributes['author.name'] = [\n>      'asc' => ['au.name' => SORT_ASC],\n>      'desc' => ['au.name' => SORT_DESC],\n> ];\n> ```\n>\n> Кроме того, при определении [[yii\\data\\Sort::defaultOrder|defaultOrder]] для сортировки необходимо использовать имя\n> зависимости вместо псевдонима:\n>\n> ```php\n> $dataProvider->sort->defaultOrder = ['author.name' => SORT_ASC];\n> ```\n\n> Info: Для подробной информации по `joinWith` и запросам, выполняемым в фоновом режиме, обратитесь к\n> [active record документации](db-active-record.md#joining-with-relations).\n\n#### Использование SQL видов для вывода данных, их сортировки и фильтрации.\n\nСуществует и другой подход, который быстре и более удобен - SQL виды. Например, если необходимо показать таблицу из\nпользователей и их профилей, то можно выбрать такой путь:\n\n```sql\nCREATE OR REPLACE VIEW vw_user_info AS\n    SELECT user.*, user_profile.lastname, user_profile.firstname\n    FROM user, user_profile\n    WHERE user.id = user_profile.user_id\n```\n\nТеперь вам необходимо создать ActiveRecord, через который будут доступны данные из вида выше:\n\n```php\n\nnamespace app\\models\\views\\grid;\n\nuse yii\\db\\ActiveRecord;\n\nclass UserView extends ActiveRecord\n{\n\n    /**\n     * {@inheritdoc}\n     */\n    public static function tableName()\n    {\n        return 'vw_user_info';\n    }\n\n    public static function primaryKey()\n    {\n        return ['id'];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function rules()\n    {\n        return [\n            // здесь определяйте ваши правила\n        ];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function attributeLabels()\n    {\n        return [\n            // здесь определяйте ваши метки атрибутов\n        ];\n    }\n}\n```\n\nПосле этого вы можете использовать UserView в модели поиска, без каких-либо дополнительных условий по сортировке и фильтрации.\nВсе атрибуты будут работать из коробки. Но такая реализация имеет свои плюсы и минусы:\n\n- вам не надо определять условия сортировок и фильтраций. Всё работает из коробки;\n- это намного быстрее данных, так как некоторые запросы уже выполнены (т.е. для каждой зависимости не нужно выполнять дополнительные запросы)\n- поскольку это простое отображение данных из sql вида, то в модели будет отсутствовать некоторая доменная логика, например\nтакие методы как `isActive`, `isDeleted`, необходимо продублировать в классе, который описывает вид.\n\n### Несколько GridViews на одной странице\n\nВы можете использовать больше одной GridView на одной странице. Для этого нужно внести некоторые дополнительные настройки\nдля того, чтобы они друг другу не мешали.\nПри использовании нескольких экземпляров GridView вы должны настроить различные имена параметров для сортировки и ссылки\nдля разбиения на страницы так, чтобы каждый GridView имел свою индивидуальную сортировку и разбиение на страницы.\nСделать это возможно через настройку [[yii\\data\\Sort::sortParam|sortParam]] и [[yii\\data\\Pagination::pageParam|pageParam]]\nсвойств провайдеров данных [[yii\\data\\BaseDataProvider::$sort|sort]] и [[yii\\data\\BaseDataProvider::$pagination|pagination]]\n\nДопустим мы хотим список моделей `Post` и `User`, для которых мы уже подготовили провайдеры данных `$userProvider` и\n`$postProvider`, тогда код будет выглядеть следующим образом:\n\n```php\nuse yii\\grid\\GridView;\n\n$userProvider->pagination->pageParam = 'user-page';\n$userProvider->sort->sortParam = 'user-sort';\n\n$postProvider->pagination->pageParam = 'post-page';\n$postProvider->sort->sortParam = 'post-sort';\n\necho '<h1>Users</h1>';\necho GridView::widget([\n    'dataProvider' => $userProvider,\n]);\n\necho '<h1>Posts</h1>';\necho GridView::widget([\n    'dataProvider' => $postProvider,\n]);\n```\n\n### Использование GridView с Pjax\n\n> Note: Секция находится в стадии разработки\n\nTBD\n"
  },
  {
    "path": "docs/guide-ru/output-formatting.md",
    "content": "Форматирование данных\n=====================\n\nДля форматирования вывода Yii предоставляет класс, преобразующий данные в человеко-понятный формат.\n[[yii\\i18n\\Formatter]] это класс-помощник, который зарегистрирован как\n[компонент приложения](structure-application-components.md), по умолчанию под именем `formatter`.\n\nОн предоставляет набор методов для форматирования таких данных как дата/время, числа и другие часто используемые в целях\nлокализации форматы.\nFormatter может быть использован двумя различными способами.\n\n1. Напрямую, используя методы форматирования (все методы форматирования имеют префикс `as`):\n\n   ```php\n   echo Yii::$app->formatter->asDate('2014-01-01', 'long'); // выведет: January 1, 2014\n   echo Yii::$app->formatter->asPercent(0.125, 2); // выведет: 12.50%\n   echo Yii::$app->formatter->asEmail('cebe@example.com'); // выведет: <a href=\"mailto:cebe@example.com\">cebe@example.com</a>\n   echo Yii::$app->formatter->asBoolean(true); // выведет: Yes\n   // он также умеет отображать null значения:\n   echo Yii::$app->formatter->asDate(null); // выведет: (not set)\n   ```\n\n2. Используя метод [[yii\\i18n\\Formatter::format()|format()]] и имя формата.\n   Этот метод также используется в виджетах наподобие [[yii\\grid\\GridView]] и [[yii\\widgets\\DetailView]], в которых\n   вы можете задать формат отображения данных в колонке через конфигурацию виджета.\n\n   ```php\n   echo Yii::$app->formatter->format('2014-01-01', 'date'); // выведет: January 1, 2014\n   // вы также можете использовать массивы для настроек метода форматирования:\n   // `2` это значение для $decimals параметра метода asPercent().\n   echo Yii::$app->formatter->format(0.125, ['percent', 2]); // выведет: 12.50%\n   ```\n\nВсе данные, отображаемые через компонент formatter, будут локализованы, если\n[расширение PHP intl](https://www.php.net/manual/ru/book.intl.php) было установлено. Для этого вы можете настроить свойство\n[[yii\\i18n\\Formatter::locale|locale]]. Если оно не было настроено, то в качестве локали будет использован\n[[yii\\base\\Application::language|язык приложения]]. Подробнее смотрите в разделе «[интернационализация](tutorial-i18n.md)».\nКомпонент форматирования будет выбирать корректный формат для даты и чисел в соответствии с локалью, включая имена\nмесяцев и дней недели, переведённые на текущий язык.\nФорматирование дат также зависит от [[yii\\i18n\\Formatter::timeZone|часового пояса]], который будет взят из одноимённого свойства [[yii\\base\\Application::timeZone|timeZone]] приложения, если не был задан явно. В свою очередь [[yii\\base\\Application::timeZone|timeZone]] устанавливает / читает временную зону PHP.\n\nНапример, форматирование даты, вызванное с разной локалью, отобразит разные результаты::\n\n```php\nYii::$app->formatter->locale = 'en-US';\necho Yii::$app->formatter->asDate('2014-01-01'); // выведет: January 1, 2014\nYii::$app->formatter->locale = 'de-DE';\necho Yii::$app->formatter->asDate('2014-01-01'); // выведет: 1. January 2014\nYii::$app->formatter->locale = 'ru-RU';\necho Yii::$app->formatter->asDate('2014-01-01'); // выведет: 1 января 2014 г.\n```\n\n> Обратите внимание, что форматирование может различаться между различными версиями библиотеки ICU, собранных с PHP,\n> а также на основе того установлено ли [расширение PHP intl](https://www.php.net/manual/ru/book.intl.php) или нет.\n> Таким образом, чтобы гарантировать, что ваш сайт будет одинаково отображать данные во всех окружениях рекомендуется\n> установить расширение PHP intl во всех окружениях и проверить, что версия библиотеки ICU совпадает.\n> См. также: [Настройка PHP окружения для интернационализации](tutorial-i18n.md#setup-environment).\n>\n> Отметим также, что даже если установлено расширение PHP intl, форматирование даты и времени для значений года >=2038\n> или <=1901 на 32-ух разрядных системах будет обращаться к реализации PHP, которая не обеспечивает локализованные\n> имена месяца и дня, потому что в этом случае intl будет использовать 32-ух битный UNIX timestamp. На 64-битной системе\n> intl formatter будет работать во всех случаях, если, конечно, intl был установлен.\n\n\nНастройка форматирования <span id=\"configuring-format\"></span>\n--------------------------------------------------------------\n\nФорматы по умолчанию, используемые в методах форматирования, можно настраивать через свойства\n[[yii\\i18n\\Formatter|класса форматирования]]. Вы можете задать форматирование по умолчанию для всего приложения, настроив\nкомпонент `formatter` в вашей [конфигурации приложения](concept-configurations.md#application-configurations). Ниже\nприведён пример конфигурации. Чтобы узнать больше о доступных свойствах см. [[yii\\i18n\\Formatter|API документацию к классу Formatter]]\nи следующие подсекции.\n\n```php\n'components' => [\n    'formatter' => [\n        'dateFormat' => 'dd.MM.yyyy',\n        'decimalSeparator' => ',',\n        'thousandSeparator' => ' ',\n        'currencyCode' => 'EUR',\n   ],\n],\n```\n\nФорматирование значений даты и времени <span id=\"date-and-time\"></span>\n-----------------------------------------------------------------------\n\nКласс форматирования предоставляет различные методы для форматирования значений даты и времени. Например:\n\n- [[yii\\i18n\\Formatter::asDate()|date]] — значение будет отформатировано как дата, например `January 01, 2014`.\n- [[yii\\i18n\\Formatter::asTime()|time]] — значение будет отформатировано как время, например `14:23`.\n- [[yii\\i18n\\Formatter::asDatetime()|datetime]] — значение будет отформатировано как дата и время, например\n  `January 01, 2014 14:23`.\n- [[yii\\i18n\\Formatter::asTimestamp()|timestamp]] — значение будет отформатировано как\n  [unix timestamp](https://ru.wikipedia.org/wiki/UNIX-время), например, `1412609982`.\n- [[yii\\i18n\\Formatter::asRelativeTime()|relativeTime]] — значение будет отформатировано как временной промежуток между\n  заданной датой и текущим временем в человеко понятном формате, например: `1 час назад`.\n- [[yii\\i18n\\Formatter::asDuration()|duration]]: значение будет отформатировано как продолжительность в человеко-понятном\n  формате, например `1 день, 2 минуты`.\n\nФорматирование даты и времени для методов [[yii\\i18n\\Formatter::asDate()|date]], [[yii\\i18n\\Formatter::asTime()|time]] и \n[[yii\\i18n\\Formatter::asDatetime()|datetime]] может быть задано глобально через конфигурацию свойств форматирования\n[[yii\\i18n\\Formatter::$dateFormat|$dateFormat]], [[yii\\i18n\\Formatter::$timeFormat|$timeFormat]] и\n[[yii\\i18n\\Formatter::$datetimeFormat|$datetimeFormat]].\n\nПо умолчанию, форматирование использует сокращенный формат, который интерпретируется по-разному в зависимости от активной\nв данный момент локали. Поэтому дата и время будут отформатированы наиболее часто используемым способом в стране и языке\nпользователя. Доступны 4 разных сокращенных формата:\n\n- `short` в локали `en_GB` отобразит, например, `06/10/2014` для даты и `15:58` для времени, в то время как\n- `medium` будет отображать `6 Oct 2014` и `15:58:42` соответственно,\n- `long` будет отображать `6 October 2014` и `15:58:42 GMT` соответственно и\n- `full` будет отображать `Monday, 6 October 2014` и `15:58:42 GMT` соответственно.\n\nДополнительно вы можете задать специальный формат, используя синтаксис, заданный [ICU Project](https://icu.unicode.org/),\nкоторый описан в руководстве ICU по следующему адресу:\n<https://unicode-org.github.io/icu/userguide/format_parse/datetime/>. Также вы можете использовать синтаксис, который распознаётся\nPHP-функцией [date()](https://www.php.net/manual/ru/function.date.php), используя строку с префиксом `php:`.\n\n```php\n// ICU форматирование\necho Yii::$app->formatter->asDate('now', 'yyyy-MM-dd'); // 2014-10-06\n// PHP date() форматирование\necho Yii::$app->formatter->asDate('now', 'php:Y-m-d'); // 2014-10-06\n```\n\n### Часовые пояса <span id=\"time-zones\"></span>\n\nДля форматирования значений даты и времени Yii будет преобразовывать их в соответствии с\n[[yii\\i18n\\Formatter::timeZone|настроенным часовым поясом]]. Поэтому предполагается, что входные значения будут в UTC,\nесли часовой пояс не был указан явно. По этой причине рекомендуется хранить все значения даты и времени в формате UTC,\nпредпочтительно в виде UNIX timestamp, которая всегда в часовом поясе UTC по определению. Если входное значение\nнаходится в часовом поясе, отличном от UTC, часовой пояс должен быть указан явно, как в следующем примере:\n\n```php\n// при условии Yii::$app->timeZone = 'Europe/Berlin';\necho Yii::$app->formatter->asTime(1412599260); // 14:41:00\necho Yii::$app->formatter->asTime('2014-10-06 12:41:00'); // 14:41:00\necho Yii::$app->formatter->asTime('2014-10-06 14:41:00 CEST'); // 14:41:00\n```\n\nЕсли [[yii\\i18n\\Formatter::timeZone|часовой пояс форматтера]] не задан явно, используется\n[[yii\\base\\Application::timeZone|часовой пояс приложения]], то есть тот же, что задан в\nконфигурации PHP.\n\n> Note: Поскольку правила для часовых поясов принимаются различными правительствами и могут часто меняться,\n> вероятно, информация в базе данных часовых поясов на вашем сервере не самая свежая.\n> Как обновить базу вы можете узнать из [руководства ICU](https://userguide.icu-project.org/datetime/timezone#TOC-Updating-the-Time-Zone-Data).\n> Смотрите также: [Настройка вашего PHP окружения для интернационализации](tutorial-i18n.md#setup-environment).\n\nФорматирование чисел <span id=\"numbers\"></span>\n-----------------------------------------------\n\nДля форматирования числовых значений класс форматирования предоставляет следующие методы:\n\n- [[yii\\i18n\\Formatter::asInteger()|integer]] — значение будет отформатировано как целое число, например `42`.\n- [[yii\\i18n\\Formatter::asDecimal()|decimal]] — значение будет отформатировано как дробное число, состоящее из целого и\n  дробной части, например: `2,542.123` или `2.542,123`.\n- [[yii\\i18n\\Formatter::asPercent()|percent]] — значение будет отформатировано как процентное значение, например `42%`.\n- [[yii\\i18n\\Formatter::asScientific()|scientific]] — значение будет отформатировано в научном формате, например: `4.2E4`.\n- [[yii\\i18n\\Formatter::asCurrency()|currency]] — значение будет отформатировано в денежном формате, например: `£420.00`.\n  Обратите внимание, чтобы эта функция работала правильно, локаль должна включать в себя часть со страной, например: `en_GB`\n  или `en_US` (потому что указание только языка будет неоднозначным в этом случае).\n- [[yii\\i18n\\Formatter::asSize()|size]] — значение будет отформатировано как количество байт в человеко-понятном формате,\n  например: `410 kibibytes`.\n- [[yii\\i18n\\Formatter::asShortSize()|shortSize]] — сокращённая версия [[yii\\i18n\\Formatter::asSize()|size]], например:\n  `410 KiB`.\n\nФорматирование чисел может быть скорректировано с помощью [[yii\\i18n\\Formatter::decimalSeparator|дробного разделителя]] и\n[[yii\\i18n\\Formatter::thousandSeparator|тысячного разделителя]], которые были заданы в соответствии с локалью.\n\nДля более сложной конфигурации [[yii\\i18n\\Formatter::numberFormatterOptions]] и [[yii\\i18n\\Formatter::numberFormatterTextOptions]]\nмогут быть использованы для настройки внутренне используемого [класса NumberFormatter](https://www.php.net/manual/ru/class.numberformatter.php).\n\nНапример, чтобы настроить максимальное и минимальное количество знаков после запятой, вы можете настроить свойство\n[[yii\\i18n\\Formatter::numberFormatterOptions]] как в примере ниже:\n\n```php\n'numberFormatterOptions' => [\n    NumberFormatter::MIN_FRACTION_DIGITS => 0,\n    NumberFormatter::MAX_FRACTION_DIGITS => 2,\n]\n```\n\nОстальное форматирование  <span id=\"other\"></span>\n--------------------------------------------------\n\nКроме форматирование даты, времени и чисел, Yii предоставляет набор других полезных средств форматирования для различных\nситуаций:\n\n- [[yii\\i18n\\Formatter::asRaw()|raw]] — значение будет отображено как есть, это псевдо-форматирование, которое не даёт\n  никакого эффекта,\n  кроме значений `null`, которые будет отформатированы в соответствии с [[yii\\i18n\\Formatter::nullDisplay|nullDisplay]].\n- [[yii\\i18n\\Formatter::asText()|text]] — значением будет экранированный от HTML текст.\n  Это формат по умолчанию, используемый в [GridView DataColumn](output-data-widgets.md#data-column).\n- [[yii\\i18n\\Formatter::asNtext()|ntext]] — значением будет экранированный от HTML текст с новыми строками,\n  сконвертированными в разрывы строк.\n- [[yii\\i18n\\Formatter::asParagraphs()|paragraphs]] — значением будет экранированный от HTML текст с параграфами,\n  обрамлёнными в `<p>` теги.\n- [[yii\\i18n\\Formatter::asHtml()|html]] — значение будет очищено, используя [[yii\\helpers\\HtmlPurifier|HtmlPurifier]], с целью предотвратить XSS\n  атаки. Вы можете задать дополнительные параметры, такие как `['html', ['Attr.AllowedFrameTargets' => ['_blank']]]`.\n- [[yii\\i18n\\Formatter::asEmail()|email]] — значение будет отформатировано как ссылка `mailto`.\n- [[yii\\i18n\\Formatter::asImage()|image]] — значение будет отформатировано как тег картинки.\n- [[yii\\i18n\\Formatter::asUrl()|url]] — значение будет отформатировано как ссылка <a>.\n- [[yii\\i18n\\Formatter::asBoolean()|boolean]] — значение форматируется как логическое. По умолчанию `true` будет\n  отображено как `Yes` и `false` как `No`, переведенное на язык приложения. Вы можете настроить это через свойство\n  [[yii\\i18n\\Formatter::booleanFormat]].\n\n`null` значения <span id=\"null-values\"></span>\n----------------------------------------------\n\nДля значений `null` в PHP класс форматирования будет отображать вместо пустой строки маркер, по умолчанию это\n`(not set)`, переведенный на язык приложения. Вы можете настроить свойство [[yii\\i18n\\Formatter::nullDisplay|nullDisplay]]\nдля установки собственного маркера. Если вы не хотите обрабатывать `null` значения, то установите свойство\n[[yii\\i18n\\Formatter::nullDisplay|nullDisplay]] в `null`.\n"
  },
  {
    "path": "docs/guide-ru/output-pagination.md",
    "content": "Постраничное разделение данных\n==========\n\nВ случае когда требуется отобразить слишком много данных на одной странице, эта страница зачастую \nразделяется на несколько частей, каждая из которых содержит и отображает только часть данных за один раз. \nТакие части называются страницами, а сам процесс называется постраничным разделением данных.\n  \nЕсли вы используете [провайдер данных](output-data-providers.md) с одним из [виджетов данных](output-data-widgets.md), \nто в этом случае будет автоматически использовано постраничное разделение данных. В противном случае вам требуется создать объект [[\\yii\\data\\Pagination]],\nзаполнить его такими данными как [[\\yii\\data\\Pagination::$totalCount|общее количество элементов]],\n[[\\yii\\data\\Pagination::$pageSize|количество элементов на одной странице]] и [[\\yii\\data\\Pagination::$page|текущая страница]], затем применить\nего к запросу и передать в [[\\yii\\widgets\\LinkPager|элемент нумерации страниц]].\n\n\nПервым делом в действии контроллера мы создаем объект постраничного разделения данных и заполняем его данными:\n\n```php\nfunction actionIndex()\n{\n    $query = Article::find()->where(['status' => 1]);\n    $countQuery = clone $query;\n    $pages = new Pagination(['totalCount' => $countQuery->count()]);\n    $models = $query->offset($pages->offset)\n        ->limit($pages->limit)\n        ->all();\n\n    return $this->render('index', [\n         'models' => $models,\n         'pages' => $pages,\n    ]);\n}\n```\n\nЗатем в представлении мы выводим модели для текущей страницы и передаем объект постраничного разделения данных в элемент нумерации страниц:\n\n```php\nforeach ($models as $model) {\n    // отображаем здесь $model\n}\n\n// отображаем ссылки на страницы\necho LinkPager::widget([\n    'pagination' => $pages,\n]);\n```\n"
  },
  {
    "path": "docs/guide-ru/output-sorting.md",
    "content": "Сортировка\n==========\n\nПри выводе нескольких строк данных часто требуется сортировка по определённым столбцам,\nуказанным пользователем. Yii использует объект [[yii\\data\\Sort]] для представления информации о схеме сортировки.\nВ частности,\n\n* [[yii\\data\\Sort::$attributes|attributes]] определяет *атрибуты*, по которым данные могут быть отсортированы.\n  Атрибут может соответствовать простому [атрибуту модели](structure-models.md#attributes) или быть составным,\n  объединяющим несколько атрибутов модели или столбцов БД. Подробности приведены ниже.\n* [[yii\\data\\Sort::$attributeOrders|attributeOrders]] содержит текущие направления сортировки для каждого атрибута.\n* [[yii\\data\\Sort::$orders|orders]] содержит направления сортировки в терминах низкоуровневых столбцов.\n\nЧтобы использовать [[yii\\data\\Sort]], объявите доступные для сортировки атрибуты, затем получите текущие параметры\nсортировки из [[yii\\data\\Sort::$attributeOrders|attributeOrders]] или [[yii\\data\\Sort::$orders|orders]]\nи используйте их для настройки запроса данных. Например:\n\n```php\nuse yii\\data\\Sort;\n\n$sort = new Sort([\n    'attributes' => [\n        'age',\n        'name' => [\n            'asc' => ['first_name' => SORT_ASC, 'last_name' => SORT_ASC],\n            'desc' => ['first_name' => SORT_DESC, 'last_name' => SORT_DESC],\n            'default' => SORT_DESC,\n            'label' => 'Name',\n        ],\n    ],\n]);\n\n$articles = Article::find()\n    ->where(['status' => 1])\n    ->orderBy($sort->orders)\n    ->all();\n```\n\nВ примере выше для объекта [[yii\\data\\Sort|Sort]] объявлены два атрибута: `age` и `name`.\n\nАтрибут `age` - это *простой* атрибут, соответствующий атрибуту `age` класса Active Record `Article`.\nОн эквивалентен следующему объявлению:\n\n```php\n'age' => [\n    'asc' => ['age' => SORT_ASC],\n    'desc' => ['age' => SORT_DESC],\n    'default' => SORT_ASC,\n    'label' => Inflector::camel2words('age'),\n]\n```\n\nАтрибут `name` - это *составной* атрибут, определённый через `first_name` и `last_name` модели `Article`.\nОн объявлен с помощью следующей структуры массива:\n\n- Элементы `asc` и `desc` задают порядок сортировки по атрибуту по возрастанию и убыванию соответственно.\n  Их значения представляют реальные столбцы и направления сортировки. Можно указать один или несколько столбцов\n  для простой или составной сортировки.\n- Элемент `default` задаёт направление сортировки при первом запросе.\n  По умолчанию - по возрастанию: если атрибут ранее не сортировался и пользователь запросил сортировку по нему,\n  данные будут отсортированы по возрастанию.\n- Элемент `label` задаёт метку, используемую при вызове [[yii\\data\\Sort::link()]] для создания ссылки сортировки.\n  Если не указан, для генерации метки из имени атрибута будет вызван [[yii\\helpers\\Inflector::camel2words()]].\n  Метка не кодируется в HTML.\n\n> Info: Значение [[yii\\data\\Sort::$orders|orders]] можно передавать напрямую в запрос к базе данных\n  для построения секции `ORDER BY`. Не используйте для этого [[yii\\data\\Sort::$attributeOrders|attributeOrders]],\n  так как некоторые атрибуты могут быть составными и не будут распознаны запросом к БД.\n\nДля создания гиперссылки, по которой пользователь может запросить сортировку по указанному атрибуту,\nвызовите [[yii\\data\\Sort::link()]]. Для создания URL сортировки используйте [[yii\\data\\Sort::createUrl()]].\nНапример:\n\n```php\n// задаёт маршрут, который будет использоваться в URL\n// если не указан, используется текущий маршрут\n$sort->route = 'article/index';\n\n// отображение ссылок сортировки по name и age\necho $sort->link('name') . ' | ' . $sort->link('age');\n\n// выводит: /index.php?r=article%2Findex&sort=age\necho $sort->createUrl('age');\n```\n\n[[yii\\data\\Sort]] проверяет query-параметр `sort`, чтобы определить, по каким атрибутам запрошена сортировка.\nСортировку по умолчанию (при отсутствии параметра) можно задать через [[yii\\data\\Sort::defaultOrder]].\nИмя query-параметра настраивается через свойство [[yii\\data\\Sort::sortParam|sortParam]].\n"
  },
  {
    "path": "docs/guide-ru/output-theming.md",
    "content": "Темизация\n=========\n\nТемизация — это способ заменить один набор [представлений](structure-views.md) другим без переписывания кода, что\nзамечательно подходит для изменения внешнего вида приложения.\n\nДля того, чтобы начать использовать темизацию, настройте свойство [[yii\\base\\View::theme|theme]] компонента\nприложения `view`. Конфигурация настраивает объект [[yii\\base\\Theme]], который отвечает за то, как именно\nзаменяются файлы отображений. Главным образом, стоит настроить следующие свойства [[yii\\base\\Theme]]:\n\n- [[yii\\base\\Theme::basePath]]: базовая директория, в которой размещены темизированные ресурсы (CSS, JS, изображения,\n  и так далее).\n- [[yii\\base\\Theme::baseUrl]]: базовый URL для доступа к темизированным ресурсам.\n- [[yii\\base\\Theme::pathMap]]: правила замены файлов представлений. Подробно описаны далее.\n \nНапример, если вы вызываете `$this->render('about')` в `SiteController`, то будет использоваться файл отображения\n`@app/views/site/about.php`. Тем не менее, если вы включите темизацию как показано ниже, то вместо него будет\nиспользоваться `@app/themes/basic/site/about.php`. \n\n```php\nreturn [\n    'components' => [\n        'view' => [\n            'theme' => [\n                'basePath' => '@app/themes/basic',\n                'baseUrl' => '@web/themes/basic',\n                'pathMap' => [\n                    '@app/views' => '@app/themes/basic',\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n> Info: При настройке тем поддерживаются псевдонимы пути. При замене отображений они преобразуются в реальные\n  пути в файловой системе или URL.\n\nВы можете обратиться к объекту [[yii\\base\\Theme]] через свойство [[yii\\base\\View::theme]]. Например,\nв файле отображения, это будет выглядеть следующим образом (объект view доступен как `$this`):\n\n```php\n$theme = $this->theme;\n\n// возвращает: $theme->baseUrl . '/img/logo.gif'\n$url = $theme->getUrl('img/logo.gif');\n\n// возвращает: $theme->basePath . '/img/logo.gif'\n$file = $theme->getPath('img/logo.gif');\n```\n\nСвойство [[yii\\base\\Theme::pathMap]] определяет то, как заменяются файлы представлений. Свойство принимает массив пар \nключ-значение, где ключи являются путями к оригинальным файлам, которые мы хотим заменить, а значения — соответствующие \nпути к файлам из темы. Замена основана на частичном совпадении: если путь к представлению начинается с любого из ключей \nмассива [[yii\\base\\Theme::pathMap|pathMap]], то соответствующая ему часть будет заменена значением из того же массива.\nДля приведённой выше конфигурации `@app/views/site/about.php` частично совпадает с ключом `@app/views` и будет\nзаменён на `@app/themes/basic/site/about.php`.\n\n\n## Темизация модулей <span id=\"theming-modules\"></span>\n\nДля того, чтобы темизировать модули, свойство [[yii\\base\\Theme::pathMap]] может быть настроено следующим образом:\n\n```php\n'pathMap' => [\n    '@app/views' => '@app/themes/basic',\n    '@app/modules' => '@app/themes/basic/modules', // <-- !!!\n],\n```\n\nЭто позволит вам темизировать `@app/modules/blog/views/comment/index.php` в `@app/themes/basic/modules/blog/views/comment/index.php`.\n\n\n## Темизация виджетов <span id=\"theming-widgets\"></span>\n\nДля того, чтобы темизировать виджеты вы можете настроить свойство [[yii\\base\\Theme::pathMap]] следующим образом:\n\n```php\n'pathMap' => [\n    '@app/views' => '@app/themes/basic',\n    '@app/widgets' => '@app/themes/basic/widgets', // <-- !!!\n],\n```\n\nЭто позволит вам темизировать `@app/widgets/currency/views/index.php` в `@app/themes/basic/widgets/currency/views/index.php`.\n\n\n## Наследование тем <span id=\"theme-inheritance\"></span>\n\nИногда требуется создать базовую тему, задающую общий вид приложения и далее изменять этот вид в зависимости, например,\nот сегодняшнего праздника. Добиться этого можно при помощи наследования тем. При этом один путь к файлу ставится в \nсоответствие нескольким путям из темы:\n\n```php\n'pathMap' => [\n    '@app/views' => [\n        '@app/themes/christmas',\n        '@app/themes/basic',\n    ],\n]\n```\n\nВ этом случае представление `@app/views/site/index.php` темизируется либо в `@app/themes/christmas/site/index.php`, \nлибо в `@app/themes/basic/site/index.php` в зависимости от того, в какой из тем есть нужный файл. Если файлы присутствуют\nи там и там, используется первый из них. На практике большинство темизированных файлов будут расположены\nв `@app/themes/basic`, а их версии для праздников в `@app/themes/christmas`.\n"
  },
  {
    "path": "docs/guide-ru/rest-authentication.md",
    "content": "Аутентификация\n==============\n\nВ отличие от веб-приложений, RESTful API обычно не сохраняют информацию о состоянии, а это означает, что сессии и куки\nиспользовать не следует. Следовательно, раз состояние аутентификации пользователя не может быть сохранено в сессиях или куках,\nкаждый запрос должен приходить вместе с определенным видом параметров аутентификации. Общепринятая практика состоит в том,\nчто для аутентификации пользователя с каждым запросом отправляется секретный токен доступа. Так как токен доступа\nможет использоваться для уникальной идентификации и аутентификации пользователя, **запросы к API всегда должны отсылаться\nчерез протокол HTTPS, чтобы предотвратить атаки «человек посередине» (англ. \"man-in-the-middle\", MitM)**.\n\nЕсть различные способы отправки токена доступа:\n\n* [HTTP Basic Auth](https://en.wikipedia.org/wiki/Basic_access_authentication): токен доступа\n  отправляется как имя пользователя. Такой подход следует использовать только в том случае, когда токен доступа может быть безопасно сохранен\n  на стороне пользователя API. Например, если API используется программой, запущенной на сервере.\n* Параметр запроса: токен доступа отправляется как параметр запроса в URL-адресе API, т.е. примерно таким образом:\n  `https://example.com/users?access-token=xxxxxxxx`. Так как большинство веб-серверов сохраняют параметры запроса в своих логах,\n  такой подход следует применять только при работе с `JSONP`-запросами, которые не могут отправлять токены доступа\n в HTTP-заголовках.\n* [OAuth 2](https://oauth.net/2/): токен доступа выдается пользователю API сервером авторизации\n  и отправляется API-серверу через [HTTP Bearer Tokens](https://datatracker.ietf.org/doc/html/rfc6750),\n  в соответствии с протоколом OAuth2.\n\nYii поддерживает все выше перечисленные методы аутентификации. Вы также можете легко создавать новые методы аутентификации.\n\nЧтобы включить аутентификацию для ваших API, выполните следующие шаги:\n\n1. У [компонента приложения](structure-application-components.md) `user` установите свойство\n   [[yii\\web\\User::enableSession|enableSession]] равным `false`.\n2. Укажите, какие методы аутентификации вы планируете использовать, настроив поведение `authenticator`\n   в ваших классах REST-контроллеров.\n3. Реализуйте метод [[yii\\web\\IdentityInterface::findIdentityByAccessToken()]] в вашем [[yii\\web\\User::identityClass|классе UserIdentity]].\n\nШаг 1 не обязателен, но рекомендуется его всё-таки выполнить, так как RESTful API не должен сохранять информацию о\nсостоянии клиента. Когда свойство [[yii\\web\\User::enableSession|enableSession]] установлено в `false`, состояние\nаутентификации пользователя НЕ БУДЕТ сохраняться между запросами с использованием сессий. Вместо этого аутентификация\nбудет выполняться для каждого запроса, что достигается шагами 2 и 3.\n\n> Tip: если вы разрабатываете RESTful API в пределах приложения, вы можете настроить свойство\n> [[yii\\web\\User::enableSession|enableSession]] компонента приложения `user` в конфигурации приложения. Если вы\n> разрабатываете RESTful API как модуль, можете добавить следующую строчку в метод `init()` модуля:\n>\n> ```php\n> public function init()\n> {\n>     parent::init();\n>     \\Yii::$app->user->enableSession = false;\n> }\n> ```\n\nНапример, для использования HTTP Basic Auth, вы можете настроить свойство `authenticator` следующим образом:\n\n```php\nuse yii\\filters\\auth\\HttpBasicAuth;\n\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n    $behaviors['authenticator'] = [\n        'class' => HttpBasicAuth::class,\n    ];\n    return $behaviors;\n}\n```\n\nЕсли вы хотите включить поддержку всех трёх описанных выше методов аутентификации, можете использовать `CompositeAuth`:\n\n```php\nuse yii\\filters\\auth\\CompositeAuth;\nuse yii\\filters\\auth\\HttpBasicAuth;\nuse yii\\filters\\auth\\HttpBearerAuth;\nuse yii\\filters\\auth\\QueryParamAuth;\n\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n    $behaviors['authenticator'] = [\n        'class' => CompositeAuth::class,\n        'authMethods' => [\n            HttpBasicAuth::class,\n            HttpBearerAuth::class,\n            QueryParamAuth::class,\n        ],\n    ];\n    return $behaviors;\n}\n```\n\nКаждый элемент в массиве `authMethods` должен быть названием класса метода аутентификации или массивом настроек.\n\n\nРеализация метода `findIdentityByAccessToken()` определяется особенностями приложения. Например, в простом варианте,\nкогда у каждого пользователя есть только один токен доступа, вы можете хранить этот токен в поле `access_token`\nтаблицы пользователей. В этом случае метод `findIdentityByAccessToken()` может быть легко реализован в классе `User` следующим образом:\n\n```php\nuse yii\\db\\ActiveRecord;\nuse yii\\web\\IdentityInterface;\n\nclass User extends ActiveRecord implements IdentityInterface\n{\n    public static function findIdentityByAccessToken($token, $type = null)\n    {\n        return static::findOne(['access_token' => $token]);\n    }\n}\n```\n\nПосле включения аутентификации описанным выше способом при каждом запросе к API запрашиваемый контроллер\nбудет пытаться аутентифицировать пользователя в своем методе `beforeAction()`.\n\nЕсли аутентификация прошла успешно, контроллер выполнит другие проверки (ограничение частоты запросов, авторизация)\nи затем выполнит действие. Информация об аутентифицированном пользователе может быть получена из объекта `Yii::$app->user->identity`.\n\nЕсли аутентификация прошла неудачно, будет возвращен ответ с HTTP-кодом состояния 401 вместе с другими необходимыми заголовками\n(такими, как заголовок `WWW-Authenticate` для HTTP Basic Auth).\n\n\n## Авторизация <span id=\"authorization\"></span>\n\nПосле аутентификации пользователя вы, вероятно, захотите проверить, есть ли у него или у неё разрешение на выполнение запрошенного\nдействия с запрошенным ресурсом. Этот процесс называется *авторизацией* и подробно описан\nв разделе «[Авторизация](security-authorization.md)».\n\nЕсли ваши контроллеры унаследованы от [[yii\\rest\\ActiveController]], вы можете переопределить\nметод [[yii\\rest\\Controller::checkAccess()|checkAccess()]] для выполнения авторизации. Этот метод будет вызываться\nвстроенными действиями, предоставляемыми контроллером [[yii\\rest\\ActiveController]].\n"
  },
  {
    "path": "docs/guide-ru/rest-controllers.md",
    "content": "Контроллеры\n===========\n\nПосле создания классов ресурсов и настройки способа форматирования ресурсных данных следующим шагом \nявляется создание действий контроллеров для предоставления ресурсов конечным пользователям через RESTful API.\n\nВ Yii есть два базовых класса контроллеров для упрощения вашей работы по созданию RESTful-действий:\n[[yii\\rest\\Controller]] и [[yii\\rest\\ActiveController]]. Разница между этими двумя контроллерами в том,\nчто у последнего есть набор действий по умолчанию, который специально создан для работы с ресурсами,\nпредставленными [Active Record](db-active-record.md). Так что если вы используете [Active Record](db-active-record.md)\nи вас устраивает предоставленный набор встроенных действий, вы можете унаследовать классы ваших контроллеров\nот [[yii\\rest\\ActiveController]], что позволит вам создать полноценные RESTful API, написав минимум кода.\n\n[[yii\\rest\\Controller]] и [[yii\\rest\\ActiveController]] имеют следующие возможности, некоторые из которых\nбудут подробно описаны в следующих разделах:\n\n* Проверка HTTP-метода;\n* [Согласование содержимого и форматирование данных](rest-response-formatting.md);\n* [Аутентификация](rest-authentication.md);\n* [Ограничение частоты запросов](rest-rate-limiting.md).\n\n[[yii\\rest\\ActiveController]], кроме того, предоставляет следующие возможности:\n\n* Набор наиболее часто используемых действий: `index`, `view`, `create`, `update`, `delete` и `options`;\n* Авторизация пользователя для запрашиваемых действия и ресурса.\n\n\n## Создание классов контроллеров <span id=\"creating-controller\"></span>\n\nПри создании нового класса контроллера в имени класса обычно используется\nназвание типа ресурса в единственном числе. Например, контроллер, отвечающий за предоставление информации о пользователях,\nможно назвать `UserController`.\n\nСоздание нового действия похоже на создание действия для Web-приложения. Единственное отличие в том,\nчто в RESTful-действиях вместо рендера результата в представлении с помощью вызова метода `render()`\nвы просто возвращаете данные. Выполнение преобразования исходных данных в запрошенный формат ложится на\n[[yii\\rest\\Controller::serializer|сериализатор]] и [[yii\\web\\Response|объект ответа]].\nНапример:\n\n```php\npublic function actionView($id)\n{\n    return User::findOne($id);\n}\n```\n\n\n## Фильтры <span id=\"filters\"></span>\n\nБольшинство возможностей RESTful API, предоставляемых [[yii\\rest\\Controller]], реализовано на основе [фильтров](structure-filters.md).\nВ частности, следующие фильтры будут выполняться в том порядке, в котором они перечислены:\n\n* [[yii\\filters\\ContentNegotiator|contentNegotiator]]: обеспечивает согласование содержимого, более подробно описан \n  в разделе [Форматирование ответа](rest-response-formatting.md);\n* [[yii\\filters\\VerbFilter|verbFilter]]: обеспечивает проверку HTTP-метода;\n* [[yii\\filters\\auth\\AuthMethod|authenticator]]: обеспечивает аутентификацию пользователя, более подробно описан\n  в разделе [Аутентификация](rest-authentication.md);\n* [[yii\\filters\\RateLimiter|rateLimiter]]: обеспечивает ограничение частоты запросов, более подробно описан \n  в разделе [Ограничение частоты запросов](rest-rate-limiting.md).\n\nЭти именованные фильтры объявлены в методе [[yii\\rest\\Controller::behaviors()|behaviors()]].\nВы можете переопределить этот метод для настройки отдельных фильтров, отключения каких-либо из них или для добавления\nваших собственных фильтров. Например, если вы хотите использовать только базовую HTTP-аутентификацию, вы можете\nнаписать такой код:\n\n```php\nuse yii\\filters\\auth\\HttpBasicAuth;\n\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n    $behaviors['authenticator'] = [\n        'class' => HttpBasicAuth::class,\n    ];\n    return $behaviors;\n}\n```\n\n\n## Наследование от `ActiveController` <span id=\"extending-active-controller\"></span>\n\nЕсли ваш класс контроллера наследуется от [[yii\\rest\\ActiveController]], вам следует установить\nзначение его свойства [[yii\\rest\\ActiveController::modelClass|modelClass]] равным имени класса ресурса,\nкоторый вы планируете обслуживать с помощью этого контроллера. Класс ресурса должен быть унаследован от [[yii\\db\\ActiveRecord]].\n\n\n### Настройка действий <span id=\"customizing-actions\"></span>\n\nПо умолчанию [[yii\\rest\\ActiveController]] предоставляет набор из следующих действий:\n\n* [[yii\\rest\\IndexAction|index]]: постраничный список ресурсов;\n* [[yii\\rest\\ViewAction|view]]: возвращает подробную информацию об указанном ресурсе;\n* [[yii\\rest\\CreateAction|create]]: создание нового ресурса;\n* [[yii\\rest\\UpdateAction|update]]: обновление существующего ресурса;\n* [[yii\\rest\\DeleteAction|delete]]: удаление указанного ресурса;\n* [[yii\\rest\\OptionsAction|options]]: возвращает поддерживаемые HTTP-методы.\n\nВсе эти действия объявляются в методе [[yii\\rest\\ActiveController::actions()|actions()]].\nВы можете настроить эти действия или отключить какие-то из них, переопределив метод `actions()`, как показано ниже:\n\n```php\npublic function actions()\n{\n    $actions = parent::actions();\n\n    // отключить действия \"delete\" и \"create\"\n    unset($actions['delete'], $actions['create']);\n\n    // настроить подготовку провайдера данных с помощью метода \"prepareDataProvider()\"\n    $actions['index']['prepareDataProvider'] = [$this, 'prepareDataProvider'];\n\n    return $actions;\n}\n\npublic function prepareDataProvider()\n{\n    // подготовить и вернуть провайдер данных для действия \"index\"\n}\n```\n\nЧтобы узнать, какие опции доступны для настройки классов отдельных действий, обратитесь к соответствующим разделам справочника классов.\n\n\n### Выполнение контроля доступа <span id=\"performing-access-check\"></span>\n\nПри предоставлении ресурсов через RESTful API часто бывает нужно проверять, имеет ли текущий пользователь разрешение\nна доступ к запрошенному ресурсу (или ресурсам) и манипуляцию им (или ими). Для [[yii\\rest\\ActiveController]] эта задача\nможет быть решена переопределением метода [[yii\\rest\\ActiveController::checkAccess()|checkAccess()]] следующим образом:\n\n```php\n/**\n * Проверяет права текущего пользователя.\n *\n * Этот метод должен быть переопределен, чтобы проверить, имеет ли текущий пользователь\n * право выполнения указанного действия над указанной моделью данных.\n * Если у пользователя нет доступа, следует выбросить исключение [[ForbiddenHttpException]].\n *\n * @param string $action ID действия, которое надо выполнить\n * @param \\yii\\base\\Model $model модель, к которой нужно получить доступ. Если `null`, это означает, что модель, к которой нужно получить доступ, отсутствует.\n * @param array $params дополнительные параметры\n * @throws ForbiddenHttpException если у пользователя нет доступа\n */\npublic function checkAccess($action, $model = null, $params = [])\n{\n    // проверить, имеет ли пользователь доступ к $action и $model\n    // выбросить ForbiddenHttpException, если доступ следует запретить\n    if ($action === 'update' || $action === 'delete') {\n        if ($model->author_id !== \\Yii::$app->user->id)\n            throw new \\yii\\web\\ForbiddenHttpException(sprintf('You can only %s articles that you\\'ve created.', $action));\n    }\n}\n```\n\nМетод `checkAccess()` будет вызван действиями по умолчанию контроллера [[yii\\rest\\ActiveController]]. Если вы создаёте\nновые действия и хотите в них выполнять контроль доступа, вы должны вызвать этот метод явно в своих новых действиях.\n\n> Tip: вы можете реализовать метод `checkAccess()` с помощью [\"Контроля доступа на основе ролей\" (RBAC)](security-authorization.md).\n"
  },
  {
    "path": "docs/guide-ru/rest-error-handling.md",
    "content": "Обработка ошибок\n================\n\nЕсли при обработке запроса к RESTful API в запросе пользователя обнаруживается ошибка или происходит \nчто-то непредвиденное на сервере, вы можете просто выбрасывать исключение, чтобы уведомить пользователя о нештатной ситуации.\nЕсли вы можете установить конкретную причину ошибки (например, запрошенный ресурс не существует), вам следует подумать\nо том, чтобы выбрасывать исключение с соответствующим кодом состояния HTTP (например, [[yii\\web\\NotFoundHttpException]],\nсоответствующее коду состояния 404). Yii отправит ответ с соответствующим \nHTTP-кодом и текстом. Он также включит в тело ответа сериализованное представление \nисключения. Например:\n\n```\nHTTP/1.1 404 Not Found\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"name\": \"Not Found Exception\",\n    \"message\": \"The requested resource was not found.\",\n    \"code\": 0,\n    \"status\": 404\n}\n```\n\nСводный список кодов состояния HTTP, используемых REST-фреймворком Yii:\n\n* `200`: OK. Все сработало именно так, как и ожидалось.\n* `201`: Ресурс был успешно создан в ответ на `POST`-запрос. Заголовок `Location`\n   содержит URL, указывающий на только что созданный ресурс.\n* `204`: Запрос обработан успешно, и в ответе нет содержимого (для запроса `DELETE`, например).\n* `304`: Ресурс не изменялся. Можно использовать закэшированную версию.\n* `400`: Неверный запрос. Может быть связано с разнообразными проблемами на стороне пользователя, такими как неверные JSON-данные\n   в теле запроса, неправильные параметры действия, и т.д.\n* `401`: Аутентификация завершилась неудачно.\n* `403`: Аутентифицированному пользователю не разрешен доступ к указанной точке входа API.\n* `404`: Запрошенный ресурс не существует.\n* `405`: Метод не поддерживается. Сверьтесь со списком поддерживаемых HTTP-методов в заголовке `Allow`.\n* `415`: Не поддерживаемый тип данных. Запрашивается неправильный тип данных или номер версии.\n* `422`: Проверка данных завершилась неудачно (в ответе на `POST`-запрос, например). Подробные сообщения об ошибках смотрите в теле ответа.\n* `429`: Слишком много запросов. Запрос отклонен из-за превышения ограничения частоты запросов.\n* `500`: Внутренняя ошибка сервера. Возможная причина — ошибки в самой программе.\n\n## Свой формат ответа с ошибкой <span id=\"customizing-error-response\"></span>\n\nВам может понадобиться изменить формат ответа с ошибкой. Например, вместо использования разных статусов ответа HTTP\nдля разных ошибок, вы можете всегда отдавать статус 200, а реальный код статуса отдавать как часть JSON ответа:\n\n```\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"success\": false,\n    \"data\": {\n        \"name\": \"Not Found Exception\",\n        \"message\": \"The requested resource was not found.\",\n        \"code\": 0,\n        \"status\": 404\n    }\n}\n```\n\nДля этого можно использовать событие `beforeSend` компонента `response` прямо в конфигурации приложения:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'response' => [\n            'class' => 'yii\\web\\Response',\n            'on beforeSend' => function ($event) {\n                $response = $event->sender;\n                if ($response->data !== null && !empty(Yii::$app->request->get('suppress_response_code'))) {\n                    $response->data = [\n                        'success' => $response->isSuccessful,\n                        'data' => $response->data,\n                    ];\n                    $response->statusCode = 200;\n                }\n            },\n        ],\n    ],\n];\n```\n\nПриведённый выше код изменит формат ответа (как для удачного запроса, так и для ошибок) если передан `GET`-параметр\n`suppress_response_code`.\n"
  },
  {
    "path": "docs/guide-ru/rest-filtering-collections.md",
    "content": "Фильтрация коллекций\n=====================\n\nКоллекции ресурсов можно фильтровать с помощью компонента [[yii\\data\\DataFilter]], доступного начиная с версии 2.0.13.\nОн позволяет валидировать и формировать условия фильтрации, переданные в запросе, а его расширенная версия\n[[yii\\data\\ActiveDataFilter]] приводит их к формату, пригодному для [[yii\\db\\QueryInterface::where()]].\n\n\n## Настройка провайдера данных для фильтрации <span id=\"configuring-data-provider-for-filtering\"></span>\n\nКак упоминалось в разделе [Коллекции](rest-resources.md#collections), для вывода отсортированного и\nпостранично разбитого списка ресурсов можно использовать [провайдер данных](output-data-providers.md).\nЕго же можно использовать для фильтрации.\n\n```php\n$filter = new ActiveDataFilter([\n    'searchModel' => 'app\\models\\PostSearch',\n]);\n\n$filterCondition = null;\n// Фильтры можно загрузить из любого источника. Например,\n// если вы предпочитаете JSON в теле запроса,\n// используйте Yii::$app->request->getBodyParams() ниже:\nif ($filter->load(Yii::$app->request->get())) {\n    $filterCondition = $filter->build();\n    if ($filterCondition === false) {\n        // Serializer извлечёт из него ошибки\n        return $filter;\n    }\n}\n\n$query = Post::find();\nif ($filterCondition !== null) {\n    $query->andWhere($filterCondition);\n}\n\nreturn new ActiveDataProvider([\n    'query' => $query,\n]);\n```\n\nМодель `PostSearch` определяет, какие свойства и значения допустимы для фильтрации:\n\n```php\nuse yii\\base\\Model;\n\nclass PostSearch extends Model\n{\n    public $id;\n    public $title;\n\n    public function rules()\n    {\n        return [\n            ['id', 'integer'],\n            ['title', 'string', 'min' => 2, 'max' => 200],\n        ];\n    }\n}\n```\n\nВместо отдельной модели для правил поиска можно использовать [[yii\\base\\DynamicModel]], если специальная\nбизнес-логика не требуется.\n\n```php\n$filter = new ActiveDataFilter([\n    'searchModel' => (new DynamicModel(['id', 'title']))\n        ->addRule(['id'], 'integer')\n        ->addRule(['title'], 'string', ['min' => 2, 'max' => 200]),\n]);\n```\n\nОпределение `searchModel` обязательно - оно контролирует, какие условия фильтрации доступны конечному пользователю.\n\n\n## Запрос фильтрации <span id=\"filtering-request\"></span>\n\nОбычно от конечного пользователя ожидается передача необязательных условий фильтрации в запросе одним или несколькими\nдопустимыми способами (которые должны быть описаны в документации API). Например, если фильтрация выполняется через\nPOST-запрос с использованием JSON, запрос может выглядеть так:\n\n```json\n{\n    \"filter\": {\n        \"id\": {\"in\": [2, 5, 9]},\n        \"title\": {\"like\": \"cheese\"}\n    }\n}\n```\n\nУсловия выше означают:\n- `id` должен быть 2, 5 или 9, **И**\n- `title` должен содержать слово `cheese`.\n\nТе же условия в GET-запросе:\n\n```\n?filter[id][in][]=2&filter[id][in][]=5&filter[id][in][]=9&filter[title][like]=cheese\n```\n\nКлючевое слово `filter` можно изменить через свойство [[yii\\data\\DataFilter::$filterAttributeName]].\n\n\n## Ключевые слова фильтрации <span id=\"filter-control-keywords\"></span>\n\nПо умолчанию допустимы следующие ключевые слова:\n\n| ключевое слово | соответствует |\n|:--------------:|:-------------:|\n|     `and`      |     `AND`     |\n|      `or`      |     `OR`      |\n|     `not`      |     `NOT`     |\n|      `lt`      |      `<`      |\n|      `gt`      |      `>`      |\n|     `lte`      |     `<=`      |\n|     `gte`      |     `>=`      |\n|      `eq`      |      `=`      |\n|     `neq`      |     `!=`      |\n|      `in`      |     `IN`      |\n|     `nin`      |   `NOT IN`    |\n|     `like`     |    `LIKE`     |\n\nСписок можно расширить через свойство [[yii\\data\\DataFilter::$filterControls]], например, добавив несколько\nпсевдонимов для одного оператора:\n\n```php\n[\n    'eq' => '=',\n    '=' => '=',\n    '==' => '=',\n    '===' => '=',\n    // ...\n]\n```\n\nУчтите, что любое не указанное ключевое слово не будет распознано как оператор фильтрации и будет воспринято как\nимя атрибута - избегайте конфликтов между ключевыми словами и именами атрибутов (например, если есть оператор\n`like` и атрибут `like`, задать условие для такого атрибута будет невозможно).\n\n> Note: При определении ключевых слов учитывайте формат обмена данными вашего API.\n  Каждое ключевое слово должно быть валидным для этого формата. Например, в XML имя тега может начинаться\n  только с буквы, поэтому операторы вроде `>`, `=` или `$gt` нарушат XML-схему.\n\n> Note: При добавлении нового ключевого слова проверьте, не нужно ли также обновить\n  [[yii\\data\\DataFilter::$conditionValidators]] и/или [[yii\\data\\DataFilter::$operatorTypes]], чтобы\n  получить корректный результат запроса с учётом сложности оператора и его логики работы.\n\n\n## Обработка значений null <span id=\"handling-the-null-values\"></span>\n\nХотя `null` легко использовать в JSON, передать его через GET-запрос невозможно без путаницы между литералом\n`null` и строкой `\"null\"`. Начиная с версии 2.0.40 свойство [[yii\\data\\DataFilter::$nullValue]] позволяет\nнастроить слово-заменитель для литерала `null` (по умолчанию `\"NULL\"`).\n\n\n## Псевдонимы атрибутов <span id=\"aliasing-attributes\"></span>\n\nЕсли нужно задать псевдоним для атрибута или фильтровать по связанной таблице, используйте\n[[yii\\data\\DataFilter::$attributeMap]]:\n\n```php\n[\n    'carPart' => 'car_part', // carPart будет фильтровать по свойству car_part\n    'authorName' => '{{author}}.[[name]]', // authorName будет фильтровать по полю name связанной таблицы author\n]\n```\n\n## Настройка фильтров для `ActiveController` <span id=\"configuring-filters-for-activecontroller\"></span>\n\n[[yii\\rest\\ActiveController]] содержит набор готовых REST-действий, которые также можно настроить для использования\nфильтров через свойство [[yii\\rest\\IndexAction::$dataFilter]]. Один из способов - через\n[[yii\\rest\\ActiveController::actions()]]:\n\n```php\npublic function actions()\n{\n    $actions = parent::actions();\n\n    $actions['index']['dataFilter'] = [\n        'class' => \\yii\\data\\ActiveDataFilter::class,\n        'attributeMap' => [\n            'clockIn' => 'clock_in',\n        ],\n        'searchModel' => (new DynamicModel(['id', 'clockIn']))->addRule(['id', 'clockIn'], 'integer', ['min' => 1]),\n    ];\n\n    return $actions;\n}\n```\n\nТеперь коллекцию (доступную через действие `index`) можно фильтровать по свойствам `id` и `clockIn`.\n"
  },
  {
    "path": "docs/guide-ru/rest-quick-start.md",
    "content": "Быстрый старт\n===========\n\nYii включает полноценный набор средств для упрощённой реализации [RESTful API](https://ru.wikipedia.org/wiki/REST).\nВ частности это следующие возможности:\n\n* Быстрое создание прототипов с поддержкой распространенных API к [Active Record](db-active-record.md);\n* Настройка формата ответа (JSON и XML реализованы по умолчанию);\n* Получение сериализованных объектов с нужной вам выборкой полей;\n* Надлежащее форматирование данных и ошибок при их валидации;\n* Коллекция пагинаций, фильтров и сортировок;\n* Поддержка [HATEOAS](https://ru.wikipedia.org/wiki/HATEOAS);\n* Эффективная маршрутизация с надлежащей проверкой HTTP методов;\n* Встроенная поддержка методов `OPTIONS` и `HEAD`;\n* Аутентификация и авторизация;\n* HTTP кэширование и кэширование данных;\n* Настройка ограничения для частоты запросов (Rate limiting);\n\n\nРассмотрим пример, как можно настроить Yii под RESTful API, приложив при этом минимум усилий.\n\nПредположим, вы захотели RESTful API для данных по пользователям. Эти данные хранятся в базе данных и для работы с ними\nвами была ранее создана модель [[yii\\db\\ActiveRecord|ActiveRecord]]  (класс `app\\models\\User`).\n\n\n## Создание контроллера <span id=\"creating-controller\"></span>\n\nВо-первых, создадим класс контроллера `app\\controllers\\UserController`:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\rest\\ActiveController;\n\nclass UserController extends ActiveController\n{\n    public $modelClass = 'app\\models\\User';\n}\n```\n\nКласс контроллера наследуется от [[yii\\rest\\ActiveController]]. Мы задали [[yii\\rest\\ActiveController::modelClass|modelClass]]\nкак `app\\models\\User`, тем самым указав контроллеру, к какой модели ему необходимо обращаться для редактирования или\nвыборки данных.\n\n\n## Настройка правил URL <span id=\"configuring-url-rules\"></span>\n\nДалее изменим настройки компонента `urlManager` в конфигурации приложения:\n\n```php\n'urlManager' => [\n    'enablePrettyUrl' => true,\n    'enableStrictParsing' => true,\n    'showScriptName' => false,\n    'rules' => [\n        ['class' => 'yii\\rest\\UrlRule', 'controller' => 'user'],\n    ],\n]\n```\n\nНастройки выше добавляют правило для контроллера `user`, которое предоставляет доступ к данным пользователя через красивые\nURL и логичные глаголы HTTP.\n\n\n## Включение JSON на прием данных<span id=\"enabling-json-input\"></span>\n\nДля того чтобы API мог принимать данные в формате JSON, сконфигурируйте [[yii\\web\\Request::$parsers|parsers]] свойство у компонента `request` [application component](structure-application-components.md) на использование [[yii\\web\\JsonParser]] JSON данных на входе:\n\n```php\n'request' => [\n    'parsers' => [\n        'application/json' => 'yii\\web\\JsonParser',\n    ]\n]\n```\n\n> Note: Конфигурация, приведенная выше необязательна. Без приведенной выше конфигурации, API сможет определить только\n  `application/x-www-form-urlencoded` и `multipart/form-data` форматы.\n\n\n## Пробуем <span id=\"trying-it-out\"></span>\n\nВот так просто мы и создали RESTful API для доступа к данным пользователя. API нашего сервиса сейчас включает в себя:\n\n* `GET /users`: получение постранично списка всех пользователей;\n* `HEAD /users`: получение метаданных листинга пользователей;\n* `POST /users`: создание нового пользователя;\n* `GET /users/123`: получение информации по конкретному пользователю с id равным 123;\n* `HEAD /users/123`: получение метаданных по конкретному пользователю с id равным 123;\n* `PATCH /users/123` и `PUT /users/123`: изменение информации по пользователю с id равным 123;\n* `DELETE /users/123`: удаление пользователя с id равным 123;\n* `OPTIONS /users`: получение поддерживаемых методов, по которым можно обратится к `/users`;\n* `OPTIONS /users/123`: получение поддерживаемых методов, по которым можно обратится к `/users/123`.\n\nПробуем получить ответы по API используя `curl`: \n\n```\n$ curl -i -H \"Accept:application/json\" \"http://localhost/users\"\n\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nX-Powered-By: PHP/5.4.20\nX-Pagination-Total-Count: 1000\nX-Pagination-Page-Count: 50\nX-Pagination-Current-Page: 1\nX-Pagination-Per-Page: 20\nLink: <http://localhost/users?page=1>; rel=self, \n      <http://localhost/users?page=2>; rel=next, \n      <http://localhost/users?page=50>; rel=last\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n[\n    {\n        \"id\": 1,\n        ...\n    },\n    {\n        \"id\": 2,\n        ...\n    },\n    ...\n]\n```\n\nПопробуйте изменить заголовок допустимого формата ресурса на `application/xml`\nи в ответ вы получите результат в формате XML:\n\n```\n$ curl -i -H \"Accept:application/xml\" \"http://localhost/users\"\n\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nX-Powered-By: PHP/5.4.20\nX-Pagination-Total-Count: 1000\nX-Pagination-Page-Count: 50\nX-Pagination-Current-Page: 1\nX-Pagination-Per-Page: 20\nLink: <http://localhost/users?page=1>; rel=self, \n      <http://localhost/users?page=2>; rel=next, \n      <http://localhost/users?page=50>; rel=last\nTransfer-Encoding: chunked\nContent-Type: application/xml\n\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<response>\n    <item>\n        <id>1</id>\n        ...\n    </item>\n    <item>\n        <id>2</id>\n        ...\n    </item>\n    ...\n</response>\n```\n\n> Tip: Вы можете получить доступ к API через веб-браузер, введя адрес `http://localhost/users`. Но в этом случае\n  для передачи определённых заголовков вам, скорее всего, потребуются дополнительные плагины для браузера.\n\nЕсли внимательно посмотреть результат ответа, то можно обнаружить, что в заголовках есть информация об общем числе записей,\nколичестве страниц и т. д. Тут так же можно обнаружить ссылки на другие страницы, как, например,\n`http://localhost/users?page=2`. Перейдя по ней можно получить вторую страницу данных пользователей.\n\nИспользуя параметры `fields` и `expand` в URL, можно указать, какие поля должны быть включены в результат. Например,\nпо адресу `http://localhost/users?fields=id,email` мы получим информацию по пользователям, которая будет содержать\nтолько `id` и `email`.\n\n\n> Info: Вы наверное заметили, что при обращении к `http://localhost/users` мы получаем информацию с полями,\n> которые нежелательно показывать, такими как `password_hash` и `auth_key`. Вы можете и должны удалить эти поля как\n> описано в разделе «[Ресурсы](rest-resources.md)».\n\nДополнительно, вы можете отсортировать коллекции как `http://localhost/users?sort=email` или\n`http://localhost/users?sort=-email`. Фильтрация коллекций как `http://localhost/users?filter[id]=10` или\n`http://localhost/users?filter[email][like]=gmail.com` возможна при использовании\nфильтров данных. Подробнее в секции [Resources](rest-resources.md#filtering-collections).\n\n## Резюме <span id=\"summary\"></span>\n\nИспользуя Yii в качестве RESTful API фреймворка, мы реализуем точки входа API как действия контроллеров.\nКонтроллер используется для организации действий, которые относятся к определённому типу ресурса.\n\nРесурсы представлены в виде моделей данных, которые наследуются от класса [[yii\\base\\Model]].\nЕсли необходима работа с базами данных (как с реляционными, так и с NoSQL), рекомендуется использовать для представления\nресурсов [[yii\\db\\ActiveRecord|ActiveRecord]].\n\nВы можете использовать [[yii\\rest\\UrlRule]] для упрощения маршрутизации точек входа API.\n\nХоть это не обязательно, рекомендуется отделять RESTful APIs приложение от основного веб-приложения. Такое разделение\nлегче обслуживается.\n"
  },
  {
    "path": "docs/guide-ru/rest-rate-limiting.md",
    "content": "Ограничение частоты запросов\n===============================\n\nЧтобы избежать злоупотреблений, вам следует подумать о добавлении ограничения частоты запросов к вашим API. Например,\nвы можете ограничить использование API до 100 вызовов в течение 10 минут для каждого пользователя. Если от пользователя\nв течение этого периода времени приходит большее количество запросов, будет возвращаться ответ с кодом состояния 429\n(«слишком много запросов»).\n\nЧтобы включить ограничение частоты запросов, *[[yii\\web\\User::identityClass|класс user identity]]* должен реализовывать\nинтерфейс [[yii\\filters\\RateLimitInterface]]. Этот интерфейс требует реализации следующих трех методов:\n\n* `getRateLimit()`: возвращает максимальное количество разрешенных запросов и период времени, например `[100, 600]`, что\n  означает не более 100 вызовов API в течение 600 секунд.\n* `loadAllowance()`: возвращает оставшееся количество разрешенных запросов и *UNIX-timestamp* последней проверки\n  ограничения.\n* `saveAllowance()`: сохраняет оставшееся количество разрешенных запросов и текущий *UNIX-timestamp*.\n\nВы можете использовать два столбца в таблице user для хранения количества разрешённых запросов и времени последней проверки.\nВ методах `loadAllowance()` и `saveAllowance()` можно реализовать чтение и сохранение значений этих столбцов в соответствии\nс данными текущего аутентифицированного пользователя. Для улучшения производительности можно попробовать хранить эту\nинформацию в кэше или NoSQL хранилище.\n\nРеализация в модели `User` может быть, например, такой:\n\n```php\npublic function getRateLimit($request, $action)\n{\n    return [$this->rateLimit, 1]; // $rateLimit запросов в секунду\n}\n\npublic function loadAllowance($request, $action)\n{\n    return [$this->allowance, $this->allowance_updated_at];\n}\n\npublic function saveAllowance($request, $action, $allowance, $timestamp)\n{\n    $this->allowance = $allowance;\n    $this->allowance_updated_at = $timestamp;\n    $this->save();\n}\n```\n\n\nКак только соответствующий интерфейс будет реализован в классе identity, Yii начнёт автоматически проверять ограничения\nчастоты запросов при помощи [[yii\\filters\\RateLimiter]], фильтра действий для [[yii\\rest\\Controller]]. При превышении\nограничений будет выброшено исключение [[yii\\web\\TooManyRequestsHttpException]].\n\nВы можете настроить ограничитель частоты запросов в ваших классах REST-контроллеров следующим образом:\n\n```php\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n    $behaviors['rateLimiter']['enableRateLimitHeaders'] = false;\n    return $behaviors;\n}\n```\n\nПри включенном ограничении частоты запросов каждый ответ, по умолчанию, возвращается со следующими HTTP-заголовками,\nсодержащими информацию о текущих ограничениях:\n\n* `X-Rate-Limit-Limit`: максимальное количество запросов, разрешённое в течение периода времени;\n* `X-Rate-Limit-Remaining`: оставшееся количество разрешённых запросов в текущем периоде времени;\n* `X-Rate-Limit-Reset`: количество секунд, которое нужно подождать до получения максимального количества разрешённых\n  запросов.\n\nВы можете отключить эти заголовки, установив свойство [[yii\\filters\\RateLimiter::enableRateLimitHeaders]] в `false`,\nкак показано в примере кода выше.\n"
  },
  {
    "path": "docs/guide-ru/rest-resources.md",
    "content": "Ресурсы\n=========\n\nRESTful API строятся вокруг доступа к *ресурсам* и управления ими. Вы можете думать о ресурсах как\nо [моделях](structure-models.md) из [MVC](https://ru.wikipedia.org/wiki/Model-View-Controller).\n\nХотя не существует никаких ограничений на то, как представить ресурс, в Yii ресурсы обычно представляются\nкак объекты [[yii\\base\\Model]] или дочерние классы (например [[yii\\db\\ActiveRecord]]), потому как:\n\n* [[yii\\base\\Model]] реализует интерфейс [[yii\\base\\Arrayable]], который позволяет задать способ отдачи данных\n  ресурса через RESTful API.\n* [[yii\\base\\Model]] поддерживает [валидацию](input-validation.md), что полезно для RESTful API реализующего ввод данных.\n* [[yii\\db\\ActiveRecord]] даёт мощную поддержку работы с БД, что актуально если данные ресурса хранятся в ней.\n\nВ этом разделе, мы сосредоточимся на том, как при помощи класса ресурса, наследуемого от [[yii\\base\\Model]]\n(или дочерних классов) задать какие данные будут возвращаться RESTful API. Если класс ресурса не наследуется от\n[[yii\\base\\Model]], возвращаются все его public свойства.\n\n\n## Поля <span id=\"fields\"></span>\n\nКогда ресурс включается в ответ RESTful API, необходимо сериализовать его в строку. Yii разбивает этот процесс на два этапа.\nСначала ресурс конвертируется в массив при помощи [[yii\\rest\\Serializer]]. На втором этапе массив сериализуется в строку\nзаданного формата (например, JSON или XML) при помощи [[yii\\web\\ResponseFormatterInterface|форматтера ответа]].\nИменно на этом стоит сосредоточиться при разработке класса ресурса.\n\nВы можете указать какие данные включать в представление ресурса в виде массива путём переопределения методов\n[[yii\\base\\Model::fields()|fields()]] и/или [[yii\\base\\Model::extraFields()|extraFields()]]. Разница между ними в том,\nчто первый определяет набор полей, которые всегда будут включены в массив, а второй определяет дополнительные поля, которые\nпользователь может запросить через параметр `expand`:\n\n```\n// вернёт все поля объявленные в fields()\nhttp://localhost/users\n\n// вернёт только поля id и email, если они объявлены в методе fields()\nhttp://localhost/users?fields=id,email\n\n// вернёт все поля объявленные в fields() и поле profile если оно указано в extraFields()\nhttp://localhost/users?expand=profile\n\n// вернёт только id, email и profile, если они объявлены в fields() и extraFields()\nhttp://localhost/users?fields=id,email&expand=profile\n```\n\n\n### Переопределение `fields()` <span id=\"overriding-fields\"></span>\n\nПо умолчанию, [[yii\\base\\Model::fields()]] возвращает все атрибуты модели как поля, а\n[[yii\\db\\ActiveRecord::fields()]] возвращает только те атрибуты, которые были объявлены в схеме БД.\n\nВы можете переопределить `fields()` для того, чтобы добавить, удалить, переименовать или переобъявить поля. Значение,\nвозвращаемое `fields()`, должно быть массивом. Его ключи — это названия полей. Значения могут быть либо именами\nсвойств/атрибутов, либо анонимными функциями, которые возвращают значение соответствующих свойств. Когда\nназвание поля совпадает с именем аттрибута вы можете опустить ключ массива:\n\n```php\n// явное перечисление всех атрибутов лучше всего использовать когда вы хотите быть уверенным что изменение\n// таблицы БД или атрибутов модели не повлияет на изменение полей, отдаваемых API (что важно для поддержки обратной\n// совместимости API).\npublic function fields()\n{\n    return [\n        // название поля совпадает с именем атрибута\n        'id',\n        // название поля \"email\", атрибут \"email_address\"\n        'email' => 'email_address',\n        // название поля \"name\", значение определяется callback-ом PHP\n        'name' => function () {\n            return $this->first_name . ' ' . $this->last_name;\n        },\n    ];\n}\n\n// отбрасываем некоторые поля. Лучше всего использовать в случае наследования\npublic function fields()\n{\n    $fields = parent::fields();\n\n    // удаляем небезопасные поля\n    unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);\n\n    return $fields;\n}\n```\n\n> Warning: По умолчанию все атрибуты модели будут включены в ответы API. Вы должны убедиться в том, что отдаются\n> только безопасные данные. В противном случае для исключения небезопасных полей необходимо переопределить метод\n> `fields()`. В приведённом выше примере мы исключаем `auth_key`, `password_hash` и `password_reset_token`.\n\n\n### Переопределение `extraFields()` <span id=\"overriding-extra-fields\"></span>\n\nПо умолчанию, [[yii\\base\\Model::extraFields()]] и [[yii\\db\\ActiveRecord::extraFields()]] возвращают пустой массив.\n\nФормат возвращаемых `extraFields()` данных такой же как у `fields()`. Как правило, `extraFields()`\nиспользуется для указания полей, значения которых являются объектами. Например учитывая следующее объявление полей\n\n```php\npublic function fields()\n{\n    return ['id', 'email'];\n}\n\npublic function extraFields()\n{\n    return ['profile'];\n}\n```\n\nзапрос `http://localhost/users?fields=id,email&expand=profile` может возвращать следующие JSON данные:\n\n```php\n[\n    {\n        \"id\": 100,\n        \"email\": \"100@example.com\",\n        \"profile\": {\n            \"id\": 100,\n            \"age\": 30,\n        }\n    },\n    ...\n]\n```\n\n\n## Ссылки <span id=\"links\"></span>\n\nСогласно [HATEOAS](https://ru.wikipedia.org/wiki/HATEOAS), расшифровывающемуся как Hypermedia as the Engine of Application State,\nRESTful API должны возвращать достаточно информации для того, чтобы клиенты могли определить возможные действия над ресурсами.\nКлючевой момент HATEOAS заключается в том, чтобы возвращать вместе с данными набора гиперссылок, указывающих на связанную\nс ресурсом информацию.\n\nПоддержку HATEOAS в ваши классы ресурсов можно добавить реализовав интерфейс [[yii\\web\\Linkable]]. Этот интерфейс\nсодержит единственный метод [[yii\\web\\Linkable::getLinks()|getLinks()]], который возвращает список [[yii\\web\\Link|ссылок]].\nОбычно вы должны вернуть хотя бы ссылку `self` с  URL самого ресурса:\n\n```php\nuse yii\\db\\ActiveRecord;\nuse yii\\web\\Link;\nuse yii\\web\\Linkable;\nuse yii\\helpers\\Url;\n\nclass User extends ActiveRecord implements Linkable\n{\n    public function getLinks()\n    {\n        return [\n            Link::REL_SELF => Url::to(['user/view', 'id' => $this->id], true),\n        ];\n    }\n}\n```\n\nПри отправке ответа объект `User` содержит поле `_links`, значение которого — ссылки, связанные с объектом:\n\n```\n{\n    \"id\": 100,\n    \"email\": \"user@example.com\",\n    // ...\n    \"_links\" => {\n        \"self\": {\n            \"href\": \"https://example.com/users/100\"\n        }\n    }\n}\n```\n\n\n## Коллекции <span id=\"collections\"></span>\n\nОбъекты ресурсов могут группироваться в *коллекции*. Каждая коллекция содержит список объектов ресурсов одного типа.\n\nНесмотря на то, что коллекции можно представить в виде массива, удобнее использовать\n[провайдеры данных](output-data-providers.md) так как они поддерживают сортировку и постраничную разбивку.\nДля RESTful APIs, которые работают с коллекциями, данные возможности используются довольно часто. Например, следующее\nдействие контроллера возвращает провайдер данных для ресурса постов:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\rest\\Controller;\nuse yii\\data\\ActiveDataProvider;\nuse app\\models\\Post;\n\nclass PostController extends Controller\n{\n    public function actionIndex()\n    {\n        return new ActiveDataProvider([\n            'query' => Post::find(),\n        ]);\n    }\n}\n```\n\nПри отправке ответа RESTful API, [[yii\\rest\\Serializer]] сериализует массив объектов ресурсов для текущей страницы.\nКроме того, он добавит HTTP заголовки, содержащие информацию о страницах:\n\n* `X-Pagination-Total-Count`: общее количество ресурсов;\n* `X-Pagination-Page-Count`: количество страниц;\n* `X-Pagination-Current-Page`: текущая страница (начиная с 1);\n* `X-Pagination-Per-Page`: количество ресурсов на страницу;\n* `Link`: набор ссылок, позволяющий клиенту пройти все страницы ресурсов.\n\nПримеры вы можете найти в разделе «[быстрый старт](rest-quick-start.md#trying-it-out)».\n"
  },
  {
    "path": "docs/guide-ru/rest-response-formatting.md",
    "content": "Форматирование ответа\n===================\n\nПри обработке RESTful API запросов приложение обычно выполняет следующие шаги, связанные с форматированием ответа:\n\n1. Определяет различные факторы, которые могут повлиять на формат ответа, такие как media type, язык, версия и т.д.\n   Этот процесс также известен как [согласование содержимого](https://en.wikipedia.org/wiki/Content_negotiation).\n2. Конвертирует объекты ресурсов в массивы, как описано в секции [Ресурсы](rest-resources.md).\n   Этим занимается [[yii\\rest\\Serializer]].\n3. Конвертирует массивы в строки исходя из формата, определенного на этапе согласование содержимого. Это задача для\n   [[yii\\web\\ResponseFormatterInterface|форматтера ответов]], регистрируемого с помощью компонента приложения\n   [[yii\\web\\Response::formatters|response]].\n\n\n## Согласование содержимого <span id=\"content-negotiation\"></span>\n\nYii поддерживает согласование содержимого с помощью фильтра [[yii\\filters\\ContentNegotiator]]. Базовый класс\nконтроллера RESTful API - [[yii\\rest\\Controller]] - использует этот фильтр под именем `contentNegotiator`.\nФильтр обеспечивает соответствие формата ответа и определяет используемый язык. Например, если RESTful API запрос\nсодержит следующий заголовок:\n\n```\nAccept: application/json; q=1.0, */*; q=0.1\n```\n\nОн получит ответ в JSON-формате такого вида:\n\n```\n$ curl -i -H \"Accept: application/json; q=1.0, */*; q=0.1\" \"http://localhost/users\"\n\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nX-Powered-By: PHP/5.4.20\nX-Pagination-Total-Count: 1000\nX-Pagination-Page-Count: 50\nX-Pagination-Current-Page: 1\nX-Pagination-Per-Page: 20\nLink: <http://localhost/users?page=1>; rel=self,\n      <http://localhost/users?page=2>; rel=next,\n      <http://localhost/users?page=50>; rel=last\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n[\n    {\n        \"id\": 1,\n        ...\n    },\n    {\n        \"id\": 2,\n        ...\n    },\n    ...\n]\n```\n\nПод капотом происходит следующее: прежде, чем *действие* RESTful API контроллера будет выполнено, фильтр\n[[yii\\filters\\ContentNegotiator]] проверит HTTP-заголовок `Accept` в запросе и установит, что\n[[yii\\web\\Response::format|формат ответа]] должен быть в `'json'`. После того, как *действие* будет выполнено и вернет\nитоговый объект ресурса или коллекцию, [[yii\\rest\\Serializer]] конвертирует результат в массив.\nИ, наконец, [[yii\\web\\JsonResponseFormatter]] сериализует массив в строку в формате JSON и включит ее в тело ответа.\n\nПо умолчанию, RESTful API поддерживает и JSON, и XML форматы. Для того, чтобы добавить поддержку нового формата,\nвы должны установить свою конфигурацию для свойства [[yii\\filters\\ContentNegotiator::formats|formats]] у фильтра\n`contentNegotiator`, например, с использованием поведения такого вида:\n\n```php\nuse yii\\web\\Response;\n\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n    $behaviors['contentNegotiator']['formats']['text/html'] = Response::FORMAT_HTML;\n    return $behaviors;\n}\n```\n\nКлючи свойства `formats` - это поддерживаемые MIME-типы, а их значения должны соответствовать именам\nформатов ответа, которые установлены в [[yii\\web\\Response::formatters]].\n\n\n## Сериализация данных <span id=\"data-serializing\"></span>\n\nКак уже описывалось выше, [[yii\\rest\\Serializer]] - это центральное место, отвечающее за конвертацию объектов ресурсов\nили коллекций в массивы. Он реализует интерфейсы [[yii\\base\\Arrayable]] и [[yii\\data\\DataProviderInterface]].\nДля объектов ресурсов как правило реализуется интерфейс [[yii\\base\\Arrayable]], а для коллекций -\n[[yii\\data\\DataProviderInterface]].\n\nВы можете переконфигурировать сериализатор с помощью настройки свойства [[yii\\rest\\Controller::serializer]], используя\nконфигурационный массив. Например, иногда вам может быть нужно помочь упростить разработку клиентской части\nприложения с помощью добавления информации о пагинации непосредственно в тело ответа. Чтобы сделать это,\nпереконфигурируйте свойство [[yii\\rest\\Serializer::collectionEnvelope]] следующим образом:\n\n\n```php\nuse yii\\rest\\ActiveController;\n\nclass UserController extends ActiveController\n{\n    public $modelClass = 'app\\models\\User';\n    public $serializer = [\n        'class' => 'yii\\rest\\Serializer',\n        'collectionEnvelope' => 'items',\n    ];\n}\n```\n\nТогда вы можете получить следующий ответ на запрос `http://localhost/users`:\n\n```\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nX-Powered-By: PHP/5.4.20\nX-Pagination-Total-Count: 1000\nX-Pagination-Page-Count: 50\nX-Pagination-Current-Page: 1\nX-Pagination-Per-Page: 20\nLink: <http://localhost/users?page=1>; rel=self,\n      <http://localhost/users?page=2>; rel=next,\n      <http://localhost/users?page=50>; rel=last\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"items\": [\n        {\n            \"id\": 1,\n            ...\n        },\n        {\n            \"id\": 2,\n            ...\n        },\n        ...\n    ],\n    \"_links\": {\n        \"self\": {\n            \"href\": \"http://localhost/users?page=1\"\n        },\n        \"next\": {\n            \"href\": \"http://localhost/users?page=2\"\n        },\n        \"last\": {\n            \"href\": \"http://localhost/users?page=50\"\n        }\n    },\n    \"_meta\": {\n        \"totalCount\": 1000,\n        \"pageCount\": 50,\n        \"currentPage\": 1,\n        \"perPage\": 20\n    }\n}\n```\n\n### Настройка форматирования JSON\n\nОтвет в формате JSON генерируется при помощи класса [[yii\\web\\JsonResponseFormatter|JsonResponseFormatter]], который\nиспользует внутри [[yii\\helpers\\Json|хелпер JSON]]. Данный форматтер гибко настраивается. Например, \nопция [[yii\\web\\JsonResponseFormatter::$prettyPrint|$prettyPrint]] полезна на время разработки так как при\nеё использовании ответы получаются более читаемыми. [[yii\\web\\JsonResponseFormatter::$encodeOptions|$encodeOptions]]\nможет пригодиться для более тонкой настройки кодирования.\n\nСвойство [[yii\\web\\Response::formatters|formatters]] компонента приложения `response` может быть\n[сконфигурировано](concept-configurations.md) следующим образом:\n\n```php\n'response' => [\n    // ...\n    'formatters' => [\n        \\yii\\web\\Response::FORMAT_JSON => [\n            'class' => 'yii\\web\\JsonResponseFormatter',\n            'prettyPrint' => YII_DEBUG, // используем \"pretty\" в режиме отладки\n            'encodeOptions' => JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE,\n            // ...\n        ],\n    ],\n],\n```\n\nПри работе с базой данных через [DAO](db-dao.md) все данные представляются в виде строк, что не всегда корректно.\nОсобенно учитывая, что в JSON для чисел есть соответствующий тип. При использовании ActiveRecord значения числовых\nстолбцов приводятся к integer на этапе выборки из базы: [[yii\\db\\ActiveRecord::populateRecord()]].\n"
  },
  {
    "path": "docs/guide-ru/rest-routing.md",
    "content": "Маршрутизация\n=============\n\nИмея готовые классы ресурсов и контроллеров, можно получить доступ к ресурсам, используя URL вроде\n`http://localhost/index.php?r=user/create`, подобно тому, как вы это делаете с обычными Web-приложениями.\n\nНа деле вам обычно хочется включить «красивые» URL-адреса и использовать все преимущества HTTP-методов (HTTP-verbs).\nНапример, чтобы запрос `POST /users` означал обращение к действию `user/create`.\nЭто может быть легко сделано с помощью настройки [компонента приложения](structure-application-components.md)\n`urlManager` в конфигурации приложения следующим образом:\n\n```php\n'urlManager' => [\n    'enablePrettyUrl' => true,\n    'enableStrictParsing' => true,\n    'showScriptName' => false,\n    'rules' => [\n        ['class' => 'yii\\rest\\UrlRule', 'controller' => 'user'],\n    ],\n]\n```\n\nГлавная новинка в коде выше по сравнению с управлением URL-адресами в Web-приложениях состоит в использовании\n[[yii\\rest\\UrlRule]] для маршрутизации запросов к RESTful API. Этот особый класс URL-правил будет\nсоздавать целый набор дочерних URL-правил для поддержки маршрутизации и создания URL-адресов для указанного контроллера (или контроллеров).\nНапример, приведенный выше код является приближенным аналогом следующего набора правил:\n\n```php\n[\n    'PUT,PATCH users/<id>' => 'user/update',\n    'DELETE users/<id>' => 'user/delete',\n    'GET,HEAD users/<id>' => 'user/view',\n    'POST users' => 'user/create',\n    'GET,HEAD users' => 'user/index',\n    'users/<id>' => 'user/options',\n    'users' => 'user/options',\n]\n```\n\nЭтим правилом поддерживаются следующие точки входа в API:\n\n* `GET /users`: разбитый на страницы список всех пользователей;\n* `HEAD /users`: общая информация по списку пользователей;\n* `POST /users`: создание нового пользователя;\n* `GET /users/123`: подробная информация о пользователе 123;\n* `HEAD /users/123`: общая информация о пользователе 123;\n* `PATCH /users/123` и `PUT /users/123`: обновление пользователя 123;\n* `DELETE /users/123`: удаление пользователя 123;\n* `OPTIONS /users`: список HTTP-методов, поддерживаемые точкой входа `/users`;\n* `OPTIONS /users/123`: список HTTP-методов, поддерживаемые точкой входа `/users/123`.\n\nВы можете настроить опции `only` и `except`, явно указав для них список действий, которые поддерживаются или\n должны быть отключены, соответственно. Например:\n\n```php\n[\n    'class' => 'yii\\rest\\UrlRule',\n    'controller' => 'user',\n    'except' => ['delete', 'create', 'update'],\n],\n```\n\nВы также можете настроить опции `patterns` или `extraPatterns` для переопределения существующих шаблонов или добавления новых шаблонов, поддерживаемых этим правилом.\nНапример, для включения нового действия `search` в точке входа `GET /users/search` настройте опцию `extraPatterns` следующим образом:\n\n```php\n[\n    'class' => 'yii\\rest\\UrlRule',\n    'controller' => 'user',\n    'extraPatterns' => [\n        'GET search' => 'search',\n    ],\n]\n```\n\nКак вы могли заметить, ID контроллера `user` в этих точках входа используется в форме множественного числа (как `users`).\nЭто происходит потому, что [[yii\\rest\\UrlRule]] автоматически приводит идентификаторы контроллеров к множественной форме.\nВы можете отключить такое поведение, назначив свойству [[yii\\rest\\UrlRule::pluralize]] значение `false`.\n\n> Info: Приведение ID контроллера к множественной форме производится в методе [[yii\\helpers\\Inflector::pluralize()]].\n  При этом соблюдаются правила английского языка. Например, `box` будет преобразован в `boxes`, а не в `boxs`.\n\nВ том случае, если автоматическое приведение к множественному числу вам не подходит, вы можете настроить \nсвойство [[yii\\rest\\UrlRule::controller]], где указать явное соответствие имени в URL и ID контроллера.\nНапример, код ниже ставит в соответствие имя `u` и ID контроллера `user`.  \n\n```php\n[\n    'class' => 'yii\\rest\\UrlRule',\n    'controller' => ['u' => 'user'],\n]\n```\n"
  },
  {
    "path": "docs/guide-ru/rest-versioning.md",
    "content": "Версионирование\n===============\n\nХороший API должен быть *версионирован*: изменения и новые возможности реализуются в новых версиях API, а не в одной и\nтой же версии. В отличие от Web-приложений, где у вас есть полный контроль и над серверным, и над клиентским кодом,\nAPI используются клиентами, код которых вы не контролируете. Поэтому, обратная совместимость (BC) должна по возможности\nсохраняться. Если ломающее её изменение необходимо, делать его нужно в новой версии API. Существующие клиенты могут\nпродолжать использовать старую, совместимую с ними версию API. Новые или обновлённые клиенты могут использовать новую\nверсию. \n\n> Tip: Чтобы узнать больше о выборе версий обратитесь к [Semantic Versioning](https://semver.org/).\n\nОбщей практикой при реализации версионирования API является включение номера версии в URL-адрес вызова API-метода.\nНапример, `https://example.com/v1/users` означает вызов API `/users` версии 1. Другой способ версионирования API,\nполучивший недавно широкое распространение, состоит в добавлении номера версии в HTTP-заголовки запроса, \nобычно в заголовок `Accept`:\n\n```\n// как параметр\nAccept: application/json; version=v1\n// как тип содержимого, определенный поставщиком API\nAccept: application/vnd.company.myapp-v1+json\n```\n\nОба способа имеют достоинства и недостатки, и вокруг них много споров. Ниже мы опишем реально работающую стратегию\nверсионирования API, которая является некоторой смесью этих двух способов:\n\n* Помещать каждую мажорную версию реализации API в отдельный модуль, чей ID является номером мажорной версии (например, `v1`, `v2`).\n  Естественно, URL-адреса API будут содержать в себе номера мажорных версий.\n* В пределах каждой мажорной версии (т.е. внутри соответствующего модуля) использовать HTTP-заголовок `Accept`\n  для определения номера минорной версии и писать условный код для соответствующих минорных версий.\n\nВ каждый модуль, обслуживающий мажорную версию, следует включать классы ресурсов и контроллеров,\nобслуживающих эту конкретную версию. Для лучшего разделения ответственности кода вы можете составить общий набор \nбазовых классов ресурсов и контроллеров, и субклассировать их в каждом отдельно взятом модуле версии. Внутри дочерних классов\nреализуйте конкретный код вроде метода `Model::fields()`.\n\nВаш код может быть организован примерно следующим образом:\n\n```\napi/\n    common/\n        controllers/\n            UserController.php\n            PostController.php\n        models/\n            User.php\n            Post.php\n    modules/\n        v1/\n            controllers/\n                UserController.php\n                PostController.php\n            models/\n                User.php\n                Post.php\n            Module.php\n        v2/\n            controllers/\n                UserController.php\n                PostController.php\n            models/\n                User.php\n                Post.php\n            Module.php\n```\n\nКонфигурация вашего приложения могла бы выглядеть так:\n\n```php\nreturn [\n    'modules' => [\n        'v1' => [\n            'class' => 'app\\modules\\v1\\Module',\n        ],\n        'v2' => [\n            'class' => 'app\\modules\\v2\\Module',\n        ],\n    ],\n    'components' => [\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            'enableStrictParsing' => true,\n            'showScriptName' => false,\n            'rules' => [\n                ['class' => 'yii\\rest\\UrlRule', 'controller' => ['v1/user', 'v1/post']],\n                ['class' => 'yii\\rest\\UrlRule', 'controller' => ['v2/user', 'v2/post']],\n            ],\n        ],\n    ],\n];\n```\n\nВ результате `https://example.com/v1/users` возвратит список пользователей API версии 1, в то время как\n`https://example.com/v2/users` вернет список пользователей версии 2.\n\nБлагодаря использованию модулей код API различных мажорных версий может быть хорошо изолирован. И по-прежнему возможно\nповторное использование кода между модулями через общие базовые классы и другие разделяемые классы.\n\nДля работы с минорными номерами версий вы можете использовать преимущества согласования содержимого,\nпредоставляемого поведением [[yii\\filters\\ContentNegotiator|contentNegotiator]].\nОпределив тип поддерживаемого содержимого, поведение `contentNegotiator` установит значение \nсвойства [[yii\\web\\Response::acceptParams]].\n\nНапример, если запрос посылается с HTTP-заголовком `Accept: application/json; version=v1`, то после согласования содержимого\nсвойство [[yii\\web\\Response::acceptParams]] будет содержать значение `['version' => 'v1']`.\n\nНа основе информации о версии из `acceptParams` вы можете выбирать поведение в действиях, классах ресурсов,\nсериализаторах и т.д.\n\nТак как минорные версии требуют поддержания обратной совместимости, будем надеяться, что в вашем коде не так уж много\nпроверок на номер версии. В противном случае велики шансы, что вам нужна новая мажорная версия.\n"
  },
  {
    "path": "docs/guide-ru/runtime-bootstrapping.md",
    "content": "Предзагрузка\n============\n\nПредзагрузка это процесс настройки рабочей среды до того, как будет запущено приложение и обработан входящий запрос. \nПредзагрузка осуществляется в двух местах: [во входном скрипте](structure-entry-scripts.md) и в [приложении](structure-applications.md).\n\nВо [входном скрипте](structure-entry-scripts.md), регистрируются автозагрузчики классов различных библиотек. Этот процесс\nвключает в себя автозагрузчик классов Composer через `autoload.php` файл и автозагрузчик классов Yii через его `Yii` файл. \nЗатем входной скрипт загружает [конфигурацию](concept-configurations.md) приложения и создает объект [приложения](structure-applications.md).\n\nВ конструкторе приложения происходит следующий процесс предзагрузки:\n\n1. Вызывается метод [[yii\\base\\Application::preInit()|preInit()]], который конфигурирует свойства приложения, имеющие\n   наивысший приоритет, такие как [[yii\\base\\Application::basePath|basePath]];\n2. Регистрируется [[yii\\base\\Application::errorHandler|обработчик ошибок]];\n3. Происходит инициализация свойств приложения согласно заданной конфигурации;\n4. Вызывается метод [[yii\\base\\Application::init()|init()]], который в свою очередь вызывает метод [[yii\\base\\Application::bootstrap()|bootstrap()]] для\n   запуска компонентов предзагрузки.\n   - Подключается файл манифеста `vendor/yiisoft/extensions.php`;\n   - Создаются и запускаются [компоненты предзагрузки](structure-extensions.md#bootstrapping-classes) объявленные в расширениях;\n   - Создаются и запускаются [компоненты приложения](structure-application-components.md) и/или [модули](structure-modules.md), объявленные\n     в свойстве [предзагрузка](structure-applications.md#bootstrap) приложения.\n\nПоскольку предзагрузка осуществляется прежде чем будет обработан *каждый* запрос, то очень важно, чтобы этот процесс был легким и максимально оптимизированным.\n\nСтарайтесь не регистрировать слишком много компонентов в предзагрузке. Компонент предзагрузки нужен только тогда, когда он должен\nучаствовать в полном жизненном цикле процесса обработки запроса. Например, если модуль должен зарегистрировать дополнительные правила парсинга URL, \nто он должен быть указан в свойстве [предзагрузка](structure-applications.md#bootstrap), чтобы новые правила URL были учтены при обработке запроса.\n\nВ производственном режиме включите байткод кэшеры, такие как [PHP OPcache] или [APC], для минимизации времени \nподключения и парсинг php файлов.\n\n[PHP OPcache]: https://www.php.net/manual/ru/intro.opcache.php\n[APC]: https://www.php.net/manual/ru/book.apcu.php\n\nНекоторые большие приложения могут иметь сложную [конфигурацию](concept-configurations.md), которая разделена на несколько мелких файлов.\nЕсли это тот самый случай, возможно вам стоит кэшировать весь конфигурационный файл и загружать его прямо из кэша до создания объекта \nприложения во входном скрипте.\n"
  },
  {
    "path": "docs/guide-ru/runtime-handling-errors.md",
    "content": "Обработка ошибок\n================\n\nВ состав Yii входит встроенный [[yii\\web\\ErrorHandler|обработчик ошибок]], делающий работу с ошибками гораздо более\nприятным занятием. А именно:\n\n* Все не фатальные ошибки PHP (то есть warning, notice) конвертируются в исключения, которые можно перехватывать.\n* Исключения и фатальные ошибки PHP отображаются в режиме отладки с детальным стеком вызовов и исходным кодом.\n* Можно использовать для отображения ошибок [действие контроллера](structure-controllers.md#actions).\n* Поддерживаются различные форматы ответа.\n\nПо умолчанию [[yii\\web\\ErrorHandler|обработчик ошибок]] включен. Вы можете выключить его объявив константу\n`YII_ENABLE_ERROR_HANDLER` со значением `false` во [входном скрипте](structure-entry-scripts.md) вашего приложения.\n\n\n## Использование обработчика ошибок <span id=\"using-error-handler\"></span>\n\n[[yii\\web\\ErrorHandler|Обработчик ошибок]] регистрируется в качестве [компонента приложения](structure-application-components.md)\nс именем `errorHandler`. Вы можете настраивать его следующим образом:\n\n```php\nreturn [\n    'components' => [\n        'errorHandler' => [\n            'maxSourceLines' => 20,\n        ],\n    ],\n];\n```\n\nС приведённой выше конфигурацией на странице ошибки будет отображаться до 20 строк исходного кода.\n\nКак уже было упомянуто, обработчик ошибок конвертирует все не фатальные ошибки PHP в перехватываемые исключения.\nЭто означает что можно поступать с ошибками следующим образом:\n\n```php\nuse Yii;\nuse yii\\base\\ErrorException;\n\ntry {\n    10/0;\n} catch (ErrorException $e) {\n    Yii::warning(\"Деление на ноль.\");\n}\n\n// можно продолжать выполнение\n```\n\nЕсли вам необходимо показать пользователю страницу с ошибкой, говорящей ему о том, что его запрос не верен или не\nдолжен был быть сделан, вы можете выкинуть [[yii\\web\\HttpException|исключение HTTP]], такое как \n[[yii\\web\\NotFoundHttpException]]. Обработчик ошибок корректно выставит статус код HTTP для ответа и использует\nподходящий вид страницы ошибки.\n\n```php\nuse yii\\web\\NotFoundHttpException;\n \nthrow new NotFoundHttpException();\n```\n\n## Настройка отображения ошибок <span id=\"customizing-error-display\"></span>\n\n[[yii\\web\\ErrorHandler|Обработчик ошибок]] меняет отображение ошибок в зависимости от значения константы `YII_DEBUG`.\nПри `YII_DEBUG` равной `true` (режим отладки), обработчик ошибок будет отображать для облегчения отладки детальный стек\nвызовов и исходный код. При `YII_DEBUG` равной `false` отображается только сообщение об ошибке, тем самым не позволяя\nполучить информацию о внутренностях приложения.\n\n> Info: Если исключение является наследником [[yii\\base\\UserException]], стек вызовов не отображается вне\n  зависимости от значения `YII_DEBUG` так как такие исключения считаются ошибками пользователя и исправлять что-либо\n  разработчику не требуется.\n\nПо умолчанию [[yii\\web\\ErrorHandler|обработчик ошибок]] показывает ошибки используя два [представления](structure-views.md):\n\n* `@yii/views/errorHandler/error.php`: используется для отображения ошибок БЕЗ стека вызовов.\n  При `YII_DEBUG` равной `false` используется только это преставление.\n* `@yii/views/errorHandler/exception.php`: используется для отображения ошибок СО стеком вызовов.\n \nВы можете настроить свойства [[yii\\web\\ErrorHandler::errorView|errorView]] и [[yii\\web\\ErrorHandler::exceptionView|exceptionView]]\nдля того, чтобы использовать свои представления.\n  \n### Использование действий для отображения ошибок <span id=\"using-error-actions\"></span>\n\nЛучшим способом изменения отображения ошибок является использование [действий](structure-controllers.md) путём\nконфигурирования свойства [[yii\\web\\ErrorHandler::errorAction|errorAction]] компонента `errorHandler`:\n\n```php\n// ...\n'components' => [\n    // ...\n    'errorHandler' => [\n        'errorAction' => 'site/error',\n    ],\n]\n```\n\nСвойство [[yii\\web\\ErrorHandler::errorAction|errorAction]] принимает [маршрут](structure-controllers.md#routes)\nдействия. Конфигурация выше означает, что для отображения ошибки без стека вызовов будет использовано действие `site/error`.\n\nСамо действие можно реализовать следующим образом:\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actions()\n    {\n        return [\n            'error' => [\n                'class' => 'yii\\web\\ErrorAction',\n            ],\n        ];\n    }\n}\n```\n\nПриведённый выше код задаёт действие `error` используя класс [[yii\\web\\ErrorAction]], который рендерит ошибку используя\nотображение `error`.\n\nВместо использования [[yii\\web\\ErrorAction]] вы можете создать действие `error` как обычный метод:\n\n```php\npublic function actionError()\n{\n    $exception = Yii::$app->errorHandler->exception;\n    if ($exception !== null) {\n        return $this->render('error', ['exception' => $exception]);\n    }\n}\n```\n\nВы должны создать файл представления `views/site/error.php`. В этом файле, если используется [[yii\\web\\ErrorAction]],\nвам доступны следующие переменные:\n\n* `name`: имя ошибки;\n* `message`: текст ошибки;\n* `exception`: объект исключения, из которого можно получить дополнительную информацию, такую как статус HTTP,\n  код ошибки, стек вызовов и т.д.\n \n> Info: Если вы используете шаблоны приложения [basic](start-installation.md) или [advanced](tutorial-advanced-app.md),\n  действие error и файл представления уже созданы за вас.\n  \n### Изменение формата ответа <span id=\"error-format\"></span>\n\nОбработчик ошибок отображает ошибки в соответствии с выбранным форматом [ответа](runtime-responses.md).\nЕсли [[yii\\web\\Response::format|формат ответа]] задан как `html`, будут использованы представления для ошибок и\nисключений, как описывалось ранее. Для остальных форматов ответа обработчик ошибок присваивает массив данных,\nпредставляющий ошибку свойству [[yii\\web\\Response::data]]. Оно далее конвертируется в необходимый формат. Например,\nесли используется формат ответа `json`, вы получите подобный ответ:\n\n```\nHTTP/1.1 404 Not Found\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"name\": \"Not Found Exception\",\n    \"message\": \"The requested resource was not found.\",\n    \"code\": 0,\n    \"status\": 404\n}\n```\n\nИзменить формат можно в обработчике события `beforeSend` компонента `response` в конфигурации приложения:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'response' => [\n            'class' => 'yii\\web\\Response',\n            'on beforeSend' => function ($event) {\n                $response = $event->sender;\n                if ($response->data !== null) {\n                    $response->data = [\n                        'success' => $response->isSuccessful,\n                        'data' => $response->data,\n                    ];\n                    $response->statusCode = 200;\n                }\n            },\n        ],\n    ],\n];\n```\n\nПриведённый код изменит формат ответа на подобный:\n\n```\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"success\": false,\n    \"data\": {\n        \"name\": \"Not Found Exception\",\n        \"message\": \"The requested resource was not found.\",\n        \"code\": 0,\n        \"status\": 404\n    }\n}\n```\n"
  },
  {
    "path": "docs/guide-ru/runtime-logging.md",
    "content": "Логирование\n============\n\nYii предоставляет мощную, гибко настраиваемую и легко расширяемую систему логирования. Эта система логирования позволяет удобным способом сохранять сообщения разных типов и фильтровать их. Сообщения могут быть сохранены в файлы, базы данных или отправлены на email.\n\nИспользование Системы логирования Yii включает следующие шаги:\n* Запись [сообщений лога](#log-messages) в различных частях кода приложения;\n* Настройка [целей лога](#log-targets) в конфигурации приложения;\n* Изучение отфильтрованных сообщений лога, например, при помощи [Отладчика Yii](tool-debugger.md).\n\nВ данном разделе будем рассматривать первые два шага.\n\n## Сообщения лога <span id=\"log-messages\"></span>\n\nЗапись сообщений лога осуществляется вызовом одного из следующих методов:\n\n* [[Yii::debug()]]: записывает сообщения для отслеживания выполнения кода приложения. Используется, в основном, при разработке.\n* [[Yii::info()]]: записывает сообщение, содержащее какую-либо полезную информацию.\n* [[Yii::warning()]]: записывает *тревожное* сообщение при возникновении неожиданного события.\n* [[Yii::error()]]: записывает критическую ошибку, на которую нужно, как можно скорее, обратить внимание.\n\nЭти методы позволяют записывать сообщения разных *уровней важности* и *категорий*. Они имеют одинаковое описание функции `function ($message, $category = 'application')`, где `$message` передает сообщение для записи, а `$category` - категорию сообщения. В следующем примере будет записано *trace* сообщение с категорией по умолчанию `application`:\n\n```php\nYii::debug('начало вычисления среднего дохода');\n```\n\n> Note: Сообщение может быть как строкой, так и объектом или массивом. За корректную работу с содержимым сообщения отвечают [цели лога](#log-targets). По умолчанию, если сообщение не является строкой, оно будет приведено к строковому типу при помощи [[yii\\helpers\\VarDumper::export()]].\n\nДля упрощения работы с сообщениями лога и их фильтрации, рекомендуется явно указывать подходящую категорию для каждого сообщения. Возможно использование иерархической системы именования категорий, что значительно упростит [целям лога](#log-targets) фильтрацию сообщений по категориям. Простым и эффективным способом именования категорий является использование магической PHP-константы `__METHOD__`. Такой подход используется в ядре фреймворка Yii. Например,\n\n```php\nYii::debug('начало вычисления среднего дохода', __METHOD__);\n```\n\nКонстанта `__METHOD__` вычисляется как имя метода (включая полное имя класса), в котором она использована. Например, её значение будет вычислено как `'app\\controllers\\RevenueController::calculate'`, если показанный выше код вызывается в соответствующем методе.\n\n> Info: методы логирования, описанные выше являются, на самом деле, ярлыками для метода [[yii\\log\\Logger::log()|log()]] [[yii\\log\\Logger|объекта логгера]], который доступен как синглтон `Yii::getLogger()`.\nПри определенном количестве записанных сообщений или завершении приложения, объект логгера вызывает [[yii\\log\\Dispatcher|message dispatcher]] для отправки записанных сообщений зарегистрированным [целям логов](#log-targets).\n\n\n## Цели логов <span id=\"log-targets\"></span>\n\nЦель логов - это экземпляр класса [[yii\\log\\Target]] или класса, унаследованного от него. Цель фильтрует сообщения логов по уровню важности и категории, а затем выгружает их в соответствующее хранилище. Например, [[yii\\log\\DbTarget|database target]] выгружает отфильтрованные сообщения логов в таблицу базы данных, а [[yii\\log\\EmailTarget|email target]] отправляет сообщения логов на заданные адреса email.\n\nПри помощи компонента приложения `log` возможна регистрация нескольких целей логов. Пример конфигурации приложения:\n\n```php\nreturn [\n    // Компонент \"log\" должен быть загружен на этапе предзагрузки\n    'bootstrap' => ['log'],\n    // Компонент \"log\" обрабатывает сообщения с меткой времени timestamp.\n    // Задайте временную зону для создания корректных меток времени.\n    'timeZone' => 'Europe/Moscow',\n    'components' => [\n        'log' => [\n            'targets' => [\n                [\n                    'class' => 'yii\\log\\DbTarget',\n                    'levels' => ['error', 'warning'],\n                ],\n                [\n                    'class' => 'yii\\log\\EmailTarget',\n                    'levels' => ['error'],\n                    'categories' => ['yii\\db\\*'],\n                    'message' => [\n                       'from' => ['log@example.com'],\n                       'to' => ['admin@example.com', 'developer@example.com'],\n                       'subject' => 'Ошибки базы данных на сайте example.com',\n                    ],\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n> Note: Компонент `log` должен быть загружен в процессе [предзагрузки](runtime-bootstrapping.md), тогда он сможет оперативно передавать сообщения целям логов. Поэтому он указан в массиве `bootstrap`.\n\nВ приведенном выше коде в свойстве [[yii\\log\\Dispatcher::targets]] зарегистрированы две цели логов:\n\n* первая цель выбирает ошибки и предупреждения и сохраняет их в базу данных;\n* вторая цель выбирает ошибки с категорией, имя которой начинается с `yii\\db\\` и шлет сразу на два адреса email `admin@example.com` и `developer@example.com`.\n\nНа данный момент, Yii содержит следующие встроенные цели логов. В документации по API подробно описана настройка и использование этих классов.\n\n* [[yii\\log\\DbTarget]]: сохраняет сообщения логов в таблицу базы данных.\n* [[yii\\log\\EmailTarget]]: шлет сообщения логов на заранее указанный email.\n* [[yii\\log\\FileTarget]]: сохраняет сообщения логов в файлы.\n* [[yii\\log\\SyslogTarget]]: сохраняет сообщения логов в системный лог используя функцию PHP `syslog()`.\n\nДальше рассмотрим общие для этих четырех классов возможности.\n\n\n### Фильтрация сообщений <span id=\"message-filtering\"></span>\n\nДля каждой цели можно настроить свойства [[yii\\log\\Target::levels|levels]] и [[yii\\log\\Target::categories|categories]], которые указывают уровни важности и категории сообщений логов, которые цель должна обрабатывать.\n\nСвойство [[yii\\log\\Target::levels|levels]] принимает массив, содержащий одно или несколько следующих значений:\n\n* `error`: соответствует сообщениям, сохраненным методом [[Yii::error()]].\n* `warning`: соответствует сообщениям, сохраненным методом [[Yii::warning()]].\n* `info`: соответствует сообщениям, сохраненным методом [[Yii::info()]].\n* `trace`: соответствует сообщениям, сохраненным методом [[Yii::debug()]].\n* `profile`: соответствует сообщениям, сохраненным методами [[Yii::beginProfile()]] и [[Yii::endProfile()]], подробнее о которых написано в подразделе [Профилирование производительности](#performance-profiling).\n\nЕсли свойство [[yii\\log\\Target::levels|levels]] не задано, цель логов будет обрабатывать сообщения с *любым* уровнем важности.\n\nСвойство [[yii\\log\\Target::categories|categories]] принимает массив, содержащий имена категорий или шаблоны.\nЦель будет обрабатывать только те сообщения, категория которых совпадает с одним из значений или шаблонов этого массива. Шаблон категории должен состоять из префикса имени категории и звездочки `*` на конце. Имя категории совпадает с шаблоном, если оно начинается с префикса шаблона. Например, `yii\\db\\Command::execute` и `yii\\db\\Command::query` используются в качестве имен категорий сообщений, записанных в классе [[yii\\db\\Command]]. Оба они совпадают с шаблоном `yii\\db\\*`.\n\nЕсли свойство [[yii\\log\\Target::categories|categories]] не задано, цель будет обрабатывать сообщения любой категории.\n\nКроме списка включаемый категорий, заданного свойством [[yii\\log\\Target::categories|categories]], при помощи свойства [[yii\\log\\Target::except|except]] возможно задать список исключаемых категорий. Если категория сообщения совпадает со значением или шаблоном из списка исключаемых категорий, такое сообщение не будет обработано.\n\nВ следующем примере показан вариант конфигурации цели логов, которая должна обрабатывать только сообщения об ошибках и предупреждениях в категориях `yii\\db\\*` и `yii\\web\\HttpException:*`, за исключением `yii\\web\\HttpException:404`.\n\n```php\n[\n    'class' => 'yii\\log\\FileTarget',\n    'levels' => ['error', 'warning'],\n    'categories' => [\n        'yii\\db\\*',\n        'yii\\web\\HttpException:*',\n    ],\n    'except' => [\n        'yii\\web\\HttpException:404',\n    ],\n]\n```\n\n> Note: При обработке HTTP-исключения [обработчиком ошибок](runtime-handling-errors.md), сообщение будет сохранено с категорией вида `yii\\web\\HttpException:ErrorCode`. Например, исключение [[yii\\web\\NotFoundHttpException]] вызовет сообщение об ошибке с категорией `yii\\web\\HttpException:404`.\n\n\n### Форматирование сообщений <span id=\"message-formatting\"></span>\n\nЦели логов выгружают отфильтрованные сообщения в определенном формате. Например, цель класса [[yii\\log\\FileTarget]] сохранит сообщение следующего формата в файле `runtime/log/app.log`:\n\n```\n2014-10-04 18:10:15 [::1][][-][trace][yii\\base\\Module::getModule] Loading module: debug\n```\n\nПо умолчанию сообщения логов форматируются методом [[yii\\log\\Target::formatMessage()]]:\n\n```\nВременная метка [IP-адрес][ID пользователя][ID сессии][Уровень важности][Категория] Текст сообщения\n```\n\nЭтот формат может быть изменен при помощи свойства [[yii\\log\\Target::prefix]], которое получает анонимную функцию, возвращающую нужный префикс сообщения. Например, следующий код позволяет настроить вывод идентификатора текущего пользователя в качестве префикса для всех сообщений.\n\n```php\n[\n    'class' => 'yii\\log\\FileTarget',\n    'prefix' => function ($message) {\n        $user = Yii::$app->has('user', true) ? Yii::$app->get('user') : null;\n        $userID = $user ? $user->getId(false) : '-';\n        return \"[$userID]\";\n    }\n]\n```\n\nКроме префиксов сообщений, также возможно добавление общей информации для каждого набора сообщений лога.\nПо умолчанию включаются значения следующих глобальных PHP-переменных: `$_GET`, `$_POST`, `$_FILES`, `$_COOKIE`,\n`$_SESSION` и `$_SERVER`. Эта возможность настраивается при помощи свойства [[yii\\log\\Target::logVars]], содержащего массив имен переменных, которые необходимо включить в лог. Например, следующий код позволяет настроить цель логов так, чтобы к сообщениям присоединялось только содержимое переменной `$_SERVER`.\n\n```php\n[\n    'class' => 'yii\\log\\FileTarget',\n    'logVars' => ['_SERVER'],\n]\n```\n\nПри задании значением свойства `logVars` пустого массива, общая информация не будет выводиться.\nДля определения собственного алгоритма подключения общей информации, следует переопределить метод [[yii\\log\\Target::getContextMessage()]].\n\nЕсли некоторые из полей вашего запроса содержат конфиденциальную информацию, которую вы не хотели бы логировать (например, пароли, токены доступа),\nвы можете дополнительно настроить свойство `maskVars`, которое может содержать как точные значения, так и шаблоны (без учета регистра).\nПо умолчанию следующие параметры запроса будут замаскированы с помощью `***`:\n`$_SERVER[HTTP_AUTHORIZATION]`, `$_SERVER[PHP_AUTH_USER]`, `$_SERVER[PHP_AUTH_PW]`, но вы можете задать свои собственные. Например:\n\n```php\n[\n    'class' => 'yii\\log\\FileTarget',\n    'logVars' => ['_SERVER'],\n    'maskVars' => [\n        '_SERVER.HTTP_X_PASSWORD',\n        '_SERVER.*_SECRET', // соответствует всем, заканчивающимся на \"_SECRET\"\n        '_SERVER.SECRET_*', // соответствует всем, начинающимся с \"SECRET_\"\n        '_SERVER.*SECRET*', // соответствует всем содержащим \"SECRET\"\n    ]\n]\n```\n\n### Уровень отслеживания выполнения кода <span id=\"trace-level\"></span>\n\nВ процессе разработки, часто бывает необходимость видеть источники сообщений. Для этого нужно использовать свойство [[yii\\log\\Dispatcher::traceLevel|traceLevel]] компонента `log`. Например,\n\n```php\nreturn [\n    'bootstrap' => ['log'],\n    'components' => [\n        'log' => [\n            'traceLevel' => YII_DEBUG ? 3 : 0,\n            'targets' => [...],\n        ],\n    ],\n];\n```\n\nПри такой настройке свойство [[yii\\log\\Dispatcher::traceLevel|traceLevel]] будет равно 3 при `YII_DEBUG` равном `true`  и 0 при `YII_DEBUG` равном `false`. Это означает, что при включенном `YII_DEBUG`, каждое сообщение лога будет содержать до трех уровней стека вызовов, а при выключенном `YII_DEBUG` информация о стеке вызовов не будет включаться в лог.\n\n> Info: Получение информации стека вызовов является не простым процессом. Поэтому такую возможность следует использовать только при разработке или отладке приложения.\n\n\n### Передача на обработку и выгрузка сообщений <span id=\"flushing-exporting\"></span>\n\nКак упоминалось выше, сообщения логов обрабатываются в массиве [[yii\\log\\Logger|объектом логгера]]. Для ограничения объема памяти, занятого этим массивом, при накоплении определенного числа сообщений, логгер передает их на обработку [целям логов](#log-targets). Максимальное количество сообщений определяется свойством [[yii\\log\\Dispatcher::flushInterval|flushInterval]] компонента `log`:\n\n```php\nreturn [\n    'bootstrap' => ['log'],\n    'components' => [\n        'log' => [\n            'flushInterval' => 100,   // по умолчанию 1000\n            'targets' => [...],\n        ],\n    ],\n];\n```\n\n> Info: При завершении приложения, так же происходит передача сообщений на обработку.\n\nПосле передачи сообщений [[yii\\log\\Logger|объектом логгера]] в [цели логов](#log-targets), сообщения не выгружаются немедленно. Вместо этого, выгрузка сообщений происходит когда цель логов накопит определенное количество фильтрованных сообщений. Максимальное количество сообщений определяется свойством [[yii\\log\\Target::exportInterval|exportInterval]] [цели логов](#log-targets). Например,\n\n```php\n[\n    'class' => 'yii\\log\\FileTarget',\n    'exportInterval' => 100,  // по умолчанию 1000\n]\n```\n\nИз-за того, что значения максимального количества сообщений для передачи и выгрузки по умолчанию достаточно велико, при вызове метода `Yii::debug()` или любого другого метода логирования, сообщение не появится сразу в файле или таблице базы данных. Такое поведение может стать проблемой, например, в консольных приложениях с большим временем исполнения. Для того, чтобы все сообщения логов сразу попадали в лог, необходимо установить значения свойств [[yii\\log\\Dispatcher::flushInterval|flushInterval]] и [[yii\\log\\Target::exportInterval|exportInterval]] равными 1, например так:\n\n```php\nreturn [\n    'bootstrap' => ['log'],\n    'components' => [\n        'log' => [\n            'flushInterval' => 1,\n            'targets' => [\n                [\n                    'class' => 'yii\\log\\FileTarget',\n                    'exportInterval' => 1,\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n> Note: Частая передача и выгрузка сообщений может сильно снизить производительность приложения.\n\n\n### Переключение целей логов <span id=\"toggling-log-targets\"></span>\n\nСвойство [[yii\\log\\Target::enabled|enabled]] отвечает за включение или отключение цели логов. Возможно управлением этим свойством как в конфигурации приложения, так и при помощи следующего PHP-кода:\n\n```php\nYii::$app->log->targets['file']->enabled = false;\n```\n\nВ данном примере используется цель логов `file`, которая может быть настроена в конфигурации приложения следующим образом:\n\n```php\nreturn [\n    'bootstrap' => ['log'],\n    'components' => [\n        'log' => [\n            'targets' => [\n                'file' => [\n                    'class' => 'yii\\log\\FileTarget',\n                ],\n                'db' => [\n                    'class' => 'yii\\log\\DbTarget',\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n\n### Создание новых целей <span id=\"new-targets\"></span>\n\nСоздание новой цели логов не является сложной задачей. В общем случае нужно реализовать метод [[yii\\log\\Target::export()]], выгружающий массив [[yii\\log\\Target::messages]] в место хранения логов. Возможно использование метода [[yii\\log\\Target::formatMessage()]] для форматирования сообщения. Детали реализации можно подсмотреть в исходном коде любого из классов целей логов, включенных в состав Yii.\n\n\n## Профилирование производительности <span id=\"performance-profiling\"></span>\n\nПрофилирование производительности - это специальный тип сообщений логов, используемый для измерения времени выполнения определенных участков кода и определения проблем производительности. Например, класс [[yii\\db\\Command]] использует профилирование производительности для определения времени исполнения каждого запроса базы данных.\n\nДля использования профилирования производительности нужно определить участок кода для измерения и *обернуть* его вызовами методов [[Yii::beginProfile()]] и [[Yii::endProfile()]]. Например,\n\n```php\n\\Yii::beginProfile('myBenchmark');\n\n// участок кода для профилирования...\n\n\\Yii::endProfile('myBenchmark');\n```\n\nгде `myBenchmark` является уникальным идентификатором данного измеряемого участка кода. \nВ дальнейшем, при изучении результатов профилирования, уникальный идентификатор поможет определить время выполнения соответствующего участка кода.\n\nОчень важно соблюдать уровни вложенности пар `beginProfile` и `endProfile`. Например, \n\n```php\n\\Yii::beginProfile('block1');\n\n    // код для профилирования\n\n    \\Yii::beginProfile('block2');\n        // другой код для профилирования\n    \\Yii::endProfile('block2');\n\n\\Yii::endProfile('block1');\n```\n\nЕсли пропустить `\\Yii::endProfile('block1')` или поменять местами `\\Yii::endProfile('block1')` и\n`\\Yii::endProfile('block2')`, профилирование производительности не будет работать.\n\nДля каждого участка кода, будет записано сообщение лога с уровнем важности `profile`. Для сбора таких сообщений можно настроить [цель логов](#log-targets) или воспользоваться [Отладчиком Yii](tool-debugger.md), который имеет встроенную панель профилирования производительности, отображающую результаты измерений.\n"
  },
  {
    "path": "docs/guide-ru/runtime-overview.md",
    "content": "Обзор\n========\n\nВсе запросы, обрабатываемые Yii приложением, проходят подобный путь.\n\n1. Пользователь создает запрос ко [входному скрипту](structure-entry-scripts.md) `web/index.php`.\n2. Входной скрипт загружает [конфигурацию](concept-configurations.md) и создает экземпляр [приложения](structure-applications.md) для обработки запроса.\n3. Приложение определяет запрошенный [маршрут](runtime-routing.md) при помощи компонента [request](runtime-requests.md).\n4. Приложение создает экземпляр [контроллера](structure-controllers.md) для обработки запроса.\n5. Контроллер создает экземпляр [действия](structure-controllers.md) и выполняет фильтры для этого действия.\n6. При неудачном выполнении любого [фильтра](structure-filters.md), действие не выполняется.\n7. При успешном выполнении всех фильтров, выполняется действие.\n8. Действие загружает [модель](structure-models.md) данных, возможно, из базы данных.\n9. Действие рендерит [представление](structure-views.md) и передает ему модель данных.\n10. Результат рендеринга передается в компонент приложения [response](runtime-responses.md).\n11. Компонент response посылает готовые данные пользователю.\n\nНиже представлена диаграмма обработки запроса приложением.\n\n![Request Lifecycle](images/request-lifecycle.png)\n\nВ данном разделе описаны подробности некоторых этапов обработки запроса.\n"
  },
  {
    "path": "docs/guide-ru/runtime-requests.md",
    "content": "Запросы\n========\n\nЗапросы, сделанные к приложению, представлены в терминах [[yii\\web\\Request]] объектов, которые предоставляют информацию о параметрах запроса, HTTP заголовках, cookies и т.д. Для получения доступа к текущему запросу вы должны обратиться к объекту `request` [application component](structure-application-components.md), который по умолчанию является экземпляром [[yii\\web\\Request]].\n\n\n## Параметры запроса <span id=\"request-parameters\"></span>\n\nЧтобы получить параметры запроса, вы должны вызвать методы [[yii\\web\\Request::get()|get()]] и [[yii\\web\\Request::post()|post()]] компонента `request`. Они возвращают значения переменных `$_GET` и `$_POST` соответственно. Например,\n\n```php\n$request = Yii::$app->request;\n\n$get = $request->get(); \n// эквивалентно: $get = $_GET;\n\n$id = $request->get('id');   \n// эквивалентно: $id = isset($_GET['id']) ? $_GET['id'] : null;\n\n$id = $request->get('id', 1);   \n// эквивалентно: $id = isset($_GET['id']) ? $_GET['id'] : 1;\n\n$post = $request->post(); \n// эквивалентно: $post = $_POST;\n\n$name = $request->post('name');   \n// эквивалентно: $name = isset($_POST['name']) ? $_POST['name'] : null;\n\n$name = $request->post('name', '');   \n// эквивалентно: $name = isset($_POST['name']) ? $_POST['name'] : '';\n```\n\n> Info: Вместо того, чтобы обращаться напрямую к переменным `$_GET` и `$_POST` для получения параметров запроса, рекомендуется\n  чтобы вы обращались к ним через компонент `request` как было показано выше. Это упростит написание тестов, поскольку вы можете создать mock компонент запроса с не настоящими данными запроса.\n\nПри реализации [RESTful API](rest-quick-start.md), зачастую вам требуется получить параметры, которые были отправлены через PUT, PATCH или другие [методы запроса](#request-methods). Вы можете получить эти параметры, вызвав метод [[yii\\web\\Request::getBodyParam()]]. Например,\n\n```php\n$request = Yii::$app->request;\n\n// возвращает все параметры\n$params = $request->bodyParams;\n\n// возвращает параметр \"id\"\n$param = $request->getBodyParam('id');\n```\n\n> Info: В отличие от `GET` параметров, параметры, которые были переданы через `POST`, `PUT`, `PATCH` и д.р. отправляются в теле запроса.\n  Компонент `request` будет обрабатывать эти параметры, когда вы попробуете к ним обратиться через методы, описанные выше.\n  Вы можете настроить способ обработки этих параметров через настройку свойства [[yii\\web\\Request::parsers]].\n  \n\n## Методы запроса <span id=\"request-methods\"></span>\n\nВы можете получить названия HTTP метода, используемого в текущем запросе, обратившись к выражению  `Yii::$app->request->method`.\nТакже имеется целый набор логических свойств для проверки соответствует ли текущий метод определённому типу запроса.\nНапример,\n\n```php\n$request = Yii::$app->request;\n\nif ($request->isAjax) { /* текущий запрос является AJAX запросом */ }\nif ($request->isGet)  { /* текущий запрос является GET запросом */ }\nif ($request->isPost) { /* текущий запрос является POST запросом */ }\nif ($request->isPut)  { /* текущий запрос является PUT запросом */ }\n```\n\n## URL запроса <span id=\"request-urls\"></span>\n\nКомпонент `request` предоставляет множество способов изучения текущего запрашиваемого URL. \n\nЕсли предположить, что URL запроса будет `https://example.com/admin/index.php/product?id=100`, то вы можете получить различные части этого адреса так как это показано ниже:\n\n* [[yii\\web\\Request::url|url]]: вернёт адрес `/admin/index.php/product?id=100`, который содержит URL без информации об имени хоста. \n* [[yii\\web\\Request::absoluteUrl|absoluteUrl]]: вернёт адрес `https://example.com/admin/index.php/product?id=100`,\n  который содержит полный URL, включая имя хоста.\n* [[yii\\web\\Request::hostInfo|hostInfo]]: вернёт адрес `https://example.com`, который содержит только имя хоста.\n* [[yii\\web\\Request::pathInfo|pathInfo]]: вернёт адрес `/product`, который содержит часть между адресом начального скрипта и параметрами запроса, идущих после знака вопроса.\n* [[yii\\web\\Request::queryString|queryString]]: вернёт адрес `id=100`, который содержит часть URL после знака вопроса. \n* [[yii\\web\\Request::baseUrl|baseUrl]]: вернёт адрес `/admin`, который является частью URL после информации о хосте и перед именем входного скрипта.\n* [[yii\\web\\Request::scriptUrl|scriptUrl]]: вернёт адрес `/admin/index.php`, который содержит URL без информации о хосте и параметрах запроса.\n* [[yii\\web\\Request::serverName|serverName]]: вернёт адрес `example.com`, который содержит имя хоста в URL.\n* [[yii\\web\\Request::serverPort|serverPort]]: вернёт 80, что является адресом порта, который использует веб-сервер.\n\n\n## HTTP заголовки <span id=\"http-headers\"></span> \n\nВы можете получить информацию о HTTP заголовках через [[yii\\web\\HeaderCollection|header collection]], возвращаемыми свойством [[yii\\web\\Request::headers]]. Например,\n\n```php\n// переменная $headers является объектом yii\\web\\HeaderCollection \n$headers = Yii::$app->request->headers;\n\n// возвращает значения заголовка Accept\n$accept = $headers->get('Accept');\n\nif ($headers->has('User-Agent')) { /* в запросе есть заголовок User-Agent */ }\n```\n\nКомпонент `request` также предоставляет доступ к некоторым часто используемым заголовкам, включая\n\n* [[yii\\web\\Request::userAgent|userAgent]]: возвращает значение заголовка `User-Agent`.\n* [[yii\\web\\Request::contentType|contentType]]: возвращает значение заголовка `Content-Type`, который указывает на MIME тип данных в теле запроса.\n* [[yii\\web\\Request::acceptableContentTypes|acceptableContentTypes]]: возвращает список MIME типов данных, которые принимаются пользователем.\n  Возвращаемый список типов будет отсортирован по показателю качества. Типы с более высокими показателями будут первыми в списке.\n* [[yii\\web\\Request::acceptableLanguages|acceptableLanguages]]: возвращает языки, которые поддерживает пользователь.\n  Список языков будет отсортирован по уровню предпочтения. Наиболее предпочитаемый язык будет первым в списке.\n\nЕсли ваше приложение поддерживает множество языков и вы хотите показать страницу на языке, который предпочитает пользователь, \nто вы можете воспользоваться языковым методом согласования (negotiation) [[yii\\web\\Request::getPreferredLanguage()]].\nЭтот метод принимает список поддерживаемых языков в вашем приложении, сравнивает их с [[yii\\web\\Request::acceptableLanguages|acceptableLanguages]] \nи возвращает наиболее подходящий язык.\n\n> Tip: Вы также можете использовать фильтр [[yii\\filters\\ContentNegotiator|ContentNegotiator]] для динамического определения\n  какой тип содержимого и язык должен использоваться в ответе. Фильтр реализует согласование содержимого на основе свойств и методов, описанных выше.\n\n\n## Информация о клиенте <span id=\"client-information\"></span>\n\nВы можете получить имя хоста и IP адрес пользователя через свойства [[yii\\web\\Request::userHost|userHost]]\nи [[yii\\web\\Request::userIP|userIP]] соответственно. Например,\n\n```php\n$userHost = Yii::$app->request->userHost;\n$userIP = Yii::$app->request->userIP;\n```\n\n## Доверенные прокси и заголовки <span id=\"trusted-proxies\"></span>\n\nВ предыдущем разделе вы видели, как получить информацию о пользователе, такую как хост и IP-адрес.\nЭто будет работать из коробки в обычной установке, где один веб-сервер используется для обслуживания веб-сайта.\nОднако если ваше приложение работает за обратным прокси-сервером, вам нужно дополнить конфигурацию, поскольку клиентом теперь является прокси-сервер, а IP-адрес пользователя передаётся приложению с помощью заголовка, установленного им.\n\nВы не должны слепо доверять заголовкам, предоставленным прокси, если вы явно не доверяете прокси.\nНачиная с 2.0.13, Yii поддерживает настройку доверенных прокси через следующие свойства компонента `request`:\n[[yii\\web\\Request::trustedHosts|trustedHosts]],\n[[yii\\web\\Request::secureHeaders|secureHeaders]], \n[[yii\\web\\Request::ipHeaders|ipHeaders]] и\n[[yii\\web\\Request::secureProtocolHeaders|secureProtocolHeaders]]\n\nНиже приведена конфигурация компонента `request` для приложения, которое работает за рядом обратных прокси, расположенных в IP-сети `10.0.2.0/24`:\n\n```php\n'request' => [\n    // ...\n    'trustedHosts' => [\n        '10.0.2.0/24',\n    ],\n],\n```\nIP-адрес, по умолчанию, отправляется прокси-сервером в заголовке `X-Forwarded-For`, а протокол (`http` или `https`) отправляется в `X-Forwarded-Proto`.\nЕсли ваши прокси используют другие заголовки, вы можете использовать конфигурацию компонента `request` для их настройки, например:\n\n```php\n'request' => [\n    // ...\n    'trustedHosts' => [\n        '10.0.2.0/24' => [\n            'X-ProxyUser-Ip',\n            'Front-End-Https',\n        ],\n    ],\n    'secureHeaders' => [\n        'X-Forwarded-For',\n        'X-Forwarded-Host',\n        'X-Forwarded-Proto',\n        'X-Proxy-User-Ip',\n        'Front-End-Https',\n    ],\n    'ipHeaders' => [\n        'X-Proxy-User-Ip',\n    ],\n    'secureProtocolHeaders' => [\n        'Front-End-Https' => ['on']\n    ],\n],\n```\nВ приведенной выше конфигурации все заголовки, перечисленные в `secureHeaders`, отфильтровываются из запроса, кроме заголовков `X-ProxyUser-Ip` и `Front-End-Https` в случае, если запрос создан прокси.\nВ этом случае первый используется для получения IP-адреса пользователя, настроенного в `ipHeaders`, а последний будет использоваться для определения результата [[yii\\web\\Request::getIsSecureConnection()]].\n"
  },
  {
    "path": "docs/guide-ru/runtime-responses.md",
    "content": "Ответы\n======\n\nКогда приложение заканчивает обработку [запроса](runtime-requests.md), оно генерирует объект [[yii\\web\\Response|ответа]]\nи отправляет его пользователю. Объект ответа содержит такие данные, как HTTP-код состояния, HTTP-заголовки и тело ответа.\nКонечная цель разработки Web-приложения состоит в создании объектов ответа на различные запросы.\n\nВ большинстве случаев вам придется иметь дело с [компонентом приложения](structure-application-components.md) `response`,\nкоторый по умолчанию является экземпляром класса [[yii\\web\\Response]]. Однако Yii также позволяет вам создавать собственные\nобъекты ответа и отправлять их пользователям. Это будет рассмотрено ниже.\n\nВ данном разделе мы опишем, как составлять ответы и отправлять их пользователям. \n\n\n## Код состояния <span id=\"status-code\"></span>\n\nПервое, что вы делаете при построении ответа, — определяете, был ли успешно обработан запрос. Это реализуется заданием\nсвойству [[yii\\web\\Response::statusCode]] значения, которое может быть одним из валидных\n[HTTP-кодов состояния](https://tools.ietf.org/html/rfc2616#section-10). Например, чтобы показать, что запрос был\nуспешно обработан, вы можете установить значение кода состояния равным 200:\n\n```php\nYii::$app->response->statusCode = 200;\n```\n\nОднако в большинстве случаев явная установка не требуется так как значение [[yii\\web\\Response::statusCode]] \nпо умолчанию равно 200. Если вам нужно показать, что запрос не удался, вы можете выбросить соответствующее\nHTTP-исключение:\n\n```php\nthrow new \\yii\\web\\NotFoundHttpException;\n```\n\nКогда [обработчик ошибок](runtime-handling-errors.md) поймает исключение, он извлечёт код состояния \nиз исключения и назначит его ответу. Исключение [[yii\\web\\NotFoundHttpException]] в коде выше \nпредставляет HTTP-код состояния 404. В Yii предопределены следующие HTTP-исключения:\n\n* [[yii\\web\\BadRequestHttpException]]: код состояния 400.\n* [[yii\\web\\ConflictHttpException]]: код состояния 409.\n* [[yii\\web\\ForbiddenHttpException]]: код состояния 403.\n* [[yii\\web\\GoneHttpException]]: код состояния 410.\n* [[yii\\web\\MethodNotAllowedHttpException]]: код состояния 405.\n* [[yii\\web\\NotAcceptableHttpException]]: код состояния 406. \n* [[yii\\web\\NotFoundHttpException]]: код состояния 404.\n* [[yii\\web\\ServerErrorHttpException]]: код состояния 500.\n* [[yii\\web\\TooManyRequestsHttpException]]: код состояния 429.\n* [[yii\\web\\UnauthorizedHttpException]]: код состояния 401.\n* [[yii\\web\\UnsupportedMediaTypeHttpException]]: код состояния 415.\n\nЕсли в приведённом выше списке нет исключения, которое вы хотите выбросить, вы можете создать его, расширив класс\n[[yii\\web\\HttpException]], или выбросить его напрямую с кодом состояния, например:\n \n```php\nthrow new \\yii\\web\\HttpException(402);\n```\n\n\n## HTTP-заголовки <span id=\"http-headers\"></span> \n\nВы можете отправлять HTTP-заголовки, работая с [[yii\\web\\Response::headers|коллекцией заголовков]] компонента `response`:\n\n```php\n$headers = Yii::$app->response->headers;\n\n// добавить заголовок Pragma. Уже имеющиеся Pragma-заголовки НЕ будут перезаписаны.\n$headers->add('Pragma', 'no-cache');\n\n// установить заголовок Pragma. Любые уже имеющиеся Pragma-заголовки будут сброшены.\n$headers->set('Pragma', 'no-cache');\n\n// удалить заголовок (или заголовки) Pragma и вернуть их значения массивом\n$values = $headers->remove('Pragma');\n```\n\n> Info: названия заголовков не чувствительны к регистру символов. Заново зарегистрированные заголовки не отсылаются\n  пользователю до вызова [[yii\\web\\Response::send()]].\n\n\n## Тело ответа <span id=\"response-body\"></span>\n\nБольшинство ответов должны иметь тело, содержащее то, что вы хотите показать пользователям.\n\nЕсли у вас уже имеется отформатированная строка для тела, вы можете присвоить её свойству [[yii\\web\\Response::content]] \nобъекта запроса:\n\n```php\nYii::$app->response->content = 'hello world!';\n```\n\nЕсли ваши данные перед отправкой конечным пользователям нужно привести к определённому формату, вам следует установить значения \nдвух свойств: [[yii\\web\\Response::format|format]] и [[yii\\web\\Response::data|data]]. Свойство [[yii\\web\\Response::format|format]]\nопределяет, в каком формате следует возвращать данные из [[yii\\web\\Response::data|data]]. Например:\n\n```php\n$response = Yii::$app->response;\n$response->format = \\yii\\web\\Response::FORMAT_JSON;\n$response->data = ['message' => 'hello world'];\n```\n\nYii из коробки имеет поддержку следующих форматов, каждый из которых реализован классом [[yii\\web\\ResponseFormatterInterface|форматтера]].\nВы можете настроить эти форматтеры или добавить новые через свойство [[yii\\web\\Response::formatters]].\n\n* [[yii\\web\\Response::FORMAT_HTML|HTML]]: реализуется классом [[yii\\web\\HtmlResponseFormatter]].\n* [[yii\\web\\Response::FORMAT_XML|XML]]: реализуется классом [[yii\\web\\XmlResponseFormatter]].\n* [[yii\\web\\Response::FORMAT_JSON|JSON]]: реализуется классом [[yii\\web\\JsonResponseFormatter]].\n* [[yii\\web\\Response::FORMAT_JSONP|JSONP]]: реализуется классом [[yii\\web\\JsonResponseFormatter]].\n\nХотя тело запроса может быть явно установлено показанным выше способом, в большинстве случаев вы можете задавать его неявно \nчерез возвращаемое значение методов [действий](structure-controllers.md). Типичный пример использования:\n \n```php\npublic function actionIndex()\n{\n    return $this->render('index');\n}\n```\n\nДействие `index` в коде выше возвращает результат рендеринга представления `index`. Возвращаемое значение будет взято \nкомпонентом `response`, отформатировано и затем отправлено пользователям.\n\nТак как по умолчанию форматом ответа является [[yii\\web\\Response::FORMAT_HTML|HTML]], в методе действия следует\nвернуть строку. Если вы хотите использовать другой формат ответа, необходимо настроить его перед отправкой данных:\n\n```php\npublic function actionInfo()\n{\n    \\Yii::$app->response->format = \\yii\\web\\Response::FORMAT_JSON;\n    return [\n        'message' => 'hello world',\n        'code' => 100,\n    ];\n}\n```\n\nКак уже было сказано, кроме использования стандартного компонента приложения `response` вы также можете создавать свои\nобъекты ответа и отправлять их конечным пользователям. Вы можете сделать это, возвращая такой объект в методе действия:\n\n```php\npublic function actionInfo()\n{\n    return \\Yii::createObject([\n        'class' => 'yii\\web\\Response',\n        'format' => \\yii\\web\\Response::FORMAT_JSON,\n        'data' => [\n            'message' => 'hello world',\n            'code' => 100,\n        ],\n    ]);\n}\n```\n\n> Note: создавая собственные объекты ответов, вы не сможете воспользоваться конфигурацией компонента `response`,\n  настроенной вами в конфигурации приложения. Тем не менее, вы можете воспользоваться \n  [внедрением зависимости](concept-di-container.md), чтобы применить общую конфигурацию к вашим новым объектам ответа.\n\n\n## Перенаправление браузера <span id=\"browser-redirection\"></span>\n\nПеренаправление браузера основано на отправке HTTP-заголовка `Location`. Так как данная возможность широко применяется,\nYii имеет средства для её использования.\n\nВы можете перенаправить браузер пользователя на URL-адрес, вызвав метод [[yii\\web\\Response::redirect()]]. Этот метод\nиспользует указанный URL-адрес в качестве значения заголовка `Location` и возвращает сам объект ответа. В методе действия\nвы можете вызвать короткую версию этого метода — [[yii\\web\\Controller::redirect()]]. Например:\n\n```php\npublic function actionOld()\n{\n    return $this->redirect('https://example.com/new', 301);\n}\n```\n\nВ приведённом выше коде метод действия возвращает результат `redirect()`. Как говорилось выше, объект ответа,\nвозвращаемый методом действия, будет использоваться в качестве ответа конечным пользователям.\n\nВ коде, находящемся вне методов действий, следует использовать [[yii\\web\\Response::redirect()]] и непосредственно после\nнего — метод [[yii\\web\\Response::send()]]. Так можно быть уверенным, что к ответу не будет добавлено нежелательное\nсодержимое.\n\n```php\n\\Yii::$app->response->redirect('https://example.com/new', 301)->send();\n```\n\n> Info: По умолчанию метод [[yii\\web\\Response::redirect()]] устанавливает код состояния ответа равным 302, сообщая\n  браузеру, что запрашиваемый ресурс *временно* находится по другому URI-адресу. Вы можете передать код состояния \n  301, чтобы сообщить браузеру, что ресурс перемещён *навсегда*.\n\nЕсли текущий запрос является AJAX-запросом, отправка заголовка `Location` не заставит браузер автоматически \nосуществить перенаправление. Чтобы решить эту задачу, метод [[yii\\web\\Response::redirect()]] устанавливает значение\nзаголовка `X-Redirect` равным URL для перенаправления. На стороне клиента вы можете написать JavaScript-код для чтения\nзначения этого заголовка и перенаправления браузера соответственно.\n\n> Info: Yii поставляется с JavaScript-файлом `yii.js`, который предоставляет набор часто используемых\n  JavaScript-утилит, включая и перенаправление браузера на основе заголовка `X-Redirect`. Следовательно, если вы\n  используете этот JavaScript-файл (зарегистрировав пакет ресурсов [[yii\\web\\YiiAsset]]), вам не нужно писать\n  дополнительный код для поддержки AJAX-перенаправления.\n\n\n## Отправка файлов <span id=\"sending-files\"></span>\n\nКак и перенаправление браузера, отправка файлов является ещё одной возможностью, основанной на определённых HTTP-заголовках.\nYii предоставляет набор методов для решения различных задач по отправке файлов. Все они поддерживают HTTP-заголовок range.\n\n* [[yii\\web\\Response::sendFile()]]: отправляет клиенту существующий файл.\n* [[yii\\web\\Response::sendContentAsFile()]]: отправляет клиенту строку как файл.\n* [[yii\\web\\Response::sendStreamAsFile()]]: отправляет клиенту существующий файловый поток как файл.\n\nЭти методы имеют одинаковую сигнатуру и возвращают объект ответа. Если отправляемый файл очень велик, следует\nиспользовать [[yii\\web\\Response::sendStreamAsFile()]], так как он более эффективно использует оперативную память.\nСледующий пример показывает, как отправить файл в действии контроллера:\n\n```php\npublic function actionDownload()\n{\n    return \\Yii::$app->response->sendFile('path/to/file.txt');\n}\n```\n\nЧтобы быть уверенным, что к ответу не будет добавлено никакое\nнежелательное содержимое, при вызове метода [[yii\\web\\Response::sendFile()]] вне методов action, следует вызвать сразу после него [[yii\\web\\Response::send()]].\n\n```php\n\\Yii::$app->response->sendFile('path/to/file.txt')->send();\n```\n\nНекоторые Web-серверы поддерживают особый режим отправки файлов, который называется *X-Sendfile*. Идея в том, чтобы \nперенаправить запрос файла Web-серверу, который отдаст файл пользователю самостоятельно. В результате Web-приложение\nможет завершиться раньше, пока Web-сервер ещё пересылает файл. Чтобы использовать эту возможность, воспользуйтесь\nметодом [[yii\\web\\Response::xSendFile()]]. Далее приведены ссылки на то, как включить `X-Sendfile` для популярных\nWeb-серверов:\n\n- Apache: [X-Sendfile](https://tn123.org/mod_xsendfile)\n- Lighttpd v1.4: [X-LIGHTTPD-send-file](https://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file)\n- Lighttpd v1.5: [X-Sendfile](https://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file)\n- Nginx: [X-Accel-Redirect](https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile/)\n- Cherokee: [X-Sendfile and X-Accel-Redirect](https://www.cherokee-project.com/doc/other_goodies.html#x-sendfile)\n\n\n## Отправка ответа <span id=\"sending-response\"></span>\n\nСодержимое ответа не отправляется пользователю до вызова метода [[yii\\web\\Response::send()]]. По умолчанию он вызывается\nавтоматически в конце метода [[yii\\base\\Application::run()]]. Однако чтобы ответ был отправлен немедленно, вы можете\nвызвать этот метод явно.\n\nДля отправки ответа метод [[yii\\web\\Response::send()]] выполняет следующие шаги:\n\n1. Инициируется событие [[yii\\web\\Response::EVENT_BEFORE_SEND]].\n2. Для форматирования [[yii\\web\\Response::data|данных ответа]] в [[yii\\web\\Response::content|содержимое ответа]]\n   вызывается метод [[yii\\web\\Response::prepare()]] .\n3. Инициируется событие [[yii\\web\\Response::EVENT_AFTER_PREPARE]].\n4. Для отправки зарегистрированных HTTP-заголовков вызывается метод [[yii\\web\\Response::sendHeaders()]].\n5. Для отправки тела ответа вызывается метод [[yii\\web\\Response::sendContent()]].\n6. Инициируется событие [[yii\\web\\Response::EVENT_AFTER_SEND]].\n\nПовторный вызов [[yii\\web\\Response::send()]] игнорируется. Это означает, что если ответ уже отправлен, то к нему уже\nничего не добавить.\n\nКак видно, метод [[yii\\web\\Response::send()]] инициирует несколько полезных событий. Реагируя на\nэти события, можно настраивать или декорировать ответ.\n"
  },
  {
    "path": "docs/guide-ru/runtime-routing.md",
    "content": "Разбор и генерация URL\n============\n\nПри обработке запрошенного URL, Yii приложение первым делом разбирает URL в [маршрут](structure-controllers.md#marsruty). Полученный маршрут используется при создании\nсоответствующего экземпляра действия контроллера для обработки запроса. Этот процесс называется *роутинг*.\n\nОбратный роутингу процесс называется *Создание URL*, он отвечает за создание URL из заданного маршрута и соответствующих параметров запроса. При необходимости, созданный URL всегда может быть преобразован в \nпервоначальные маршрут и параметры запроса.\n\nВ основе роутинга и создания URL лежит использование [[yii\\web\\UrlManager|URL manager]], \nзарегистрированного в качестве [компонента приложения](structure-application-components.md) `urlManager`. \n[[yii\\web\\UrlManager|URL manager]] содержит метод [[yii\\web\\UrlManager::parseRequest()|parseRequest()]] \nдля разбора входящего запроса на маршрут и параметры запроса, и метод [[yii\\web\\UrlManager::createUrl()|createUrl()]] \nдля создания URL из заданного маршрута и параметров запроса.\n\nНастройка компонента `urlManager` в конфигурации приложения, позволяет приложению распознавать различные \nформаты URL без внесения изменений в существующий код приложения. Например, для \nсоздания URL для действия `post/view`, можно использовать следующий код:\n\n```php\nuse yii\\helpers\\Url;\n\n// Url::to() вызывает UrlManager::createUrl() для создания URL\n$url = Url::to(['post/view', 'id' => 100]);\n```\n\nВ зависимости от настройки `urlManager`, URL может быть создан в одном из следующих форматов (или любом другом формате). При последующем запросе URL в таком формате, он будет разобран на исходные маршрут и параметры запроса.\n\n```\n/index.php?r=post/view&id=100\n/index.php/post/100\n/post/100\n```\n\n## Форматы URL <span id=\"url-formats\"></span>\n\n[[yii\\web\\UrlManager|URL manager]] поддерживает два формата URL:\n\n- Обычный.\n- Человекопонятные URL. \n\nОбычный формат URL использует параметр `r` для передачи маршрута и любые другие параметры для передачи остальных параметров запроса. Например, URL `/index.php?r=post/view&id=100` задает маршрут `post/view` и параметр `id`, равный 100. Данный формат не требует специальной конфигурации [[yii\\web\\UrlManager|URL manager]] и работает с любыми настройками веб-сервера.\n\nЧеловекопонятный формат URL представляет собой дополнительный путь, следующий за именем входного скрипта, описывающий маршрут и остальные параметров запроса. Например, дополнительный путь в URL `/index.php/post/100` - это `/post/100`, который может представлять маршрут `post/view` и параметр `id` со значением равным 100, при наличии соответствующего [[yii\\web\\UrlManager::rules|правила]]. Для использования ЧПУ, необходимо создать набор правил, соответствующих требованиям к URL.\n\nПереключение между двумя форматами URL осуществляется при помощи свойства [[yii\\web\\UrlManager::enablePrettyUrl|enablePrettyUrl]] компонента [[yii\\web\\UrlManager|URL manager]] без внесения изменений в код приложения.\n\n## Роутинг <span id=\"routing\"></span>\n\nРоутинг осуществляется в два этапа:\n\n- Входящий запрос разбирается в маршрут и параметры запроса.\n- Для обработки запроса создается [действие контроллера](structure-controllers.md#actions), соответствующее полученному\n  маршруту.\n\nПри использовании простого формата URL, получение маршрута из запроса заключается в получении параметра `r` из массива `GET`.\n\nПри использовании ЧПУ, компонент [[yii\\web\\UrlManager|URL manager]] ищет среди зарегистрированных [[yii\\web\\UrlManager::rules|правил]] подходящее для разрешения запроса в маршрут.\nЕсли такое правило не найдено, вызывается исключение [[yii\\web\\NotFoundHttpException]].\n\nПосле того, как из запроса получен маршрут, самое время создать действие контроллера, соответствующее этому маршруту.\nМаршрут разделяется на несколько частей, метками деления служат прямые слеши. Например, маршрут `site/index` будет разделен на `site` и `index`. Каждая из частей представляет собой *идентификатор*, который может ссылаться на модуль, контроллер или действие. Начиная с первой части маршрута, приложение следует следующему алгоритму для создания модуля (если есть), контроллера и действия:\n\n1. Текущим модулем считаем приложение.\n2. Проверяем, содержит ли [[yii\\base\\Module::controllerMap|карта контроллеров]] текущего модуля текущий *идентификатор*.\n   Если содержит, в соответствии с конфигурацией контроллера, найденной в карте, создаем объект контроллера и переходим в п. 5 для обработки оставшейся части маршрута.\n3. Проверяем, есть ли модуль, соответствующий *идентификатору* в списке модулей (свойство [[yii\\base\\Module::modules|modules]]) текущего модуля. Если есть, в соответствии с конфигурацией модуля, найденной в списке модулей, создаем модуль и переходим в п. 2, считая только что созданный модуль текущим.\n4. Рассматриваем *идентификатор* как [идентификатор контроллера](structure-controllers.md#id-kontrollerov) и создаем объект контроллера. Для оставшейся части маршрута выполняем п. 5.\n5. Контроллер ищет текущий *идентификатор* в его [[yii\\base\\Controller::actions()|карте действий]]. В случае нахождения, контроллер создает действие, в соответствии с конфигурацией, найденной в карте. Иначе, контроллер пытается создать встроенное действие, описанное методом, соответствующим текущему [идентификатору действия](structure-controllers.md#id-dejstvij).\n\nПри возникновении ошибок на любом из описанных выше этапов, вызывается исключение [[yii\\web\\NotFoundHttpException]], указывающее на ошибку в процессе роутинга.\n\n### Маршрут по умолчанию <span id=\"default-route\"></span>\n\nВ случае, если в результате разбора запроса  получен пустой маршрут, вместо него будет использован, так называемый, маршрут по умолчанию. Изначально, маршрут по умолчанию имеет значение `site/index`, и указывает на действие `index` контроллера `site`. Указать свое значение можно при помощи свойства приложения [[yii\\web\\Application::defaultRoute|defaultRoute]], например так:\n\n```php\n[\n    // ...\n    'defaultRoute' => 'main/index',\n];\n```\n\nВ добавок к маршруту по умолчанию приложения, существует маршрут по умолчанию модулей. Например, если у нас есть модуль \n`user` и запрос разбирается в маршрут `user`, [[yii\\base\\Module::defaultRoute|defaultRoute]] модуля используется для\nопределения контроллера. По умолчанию имя контроллера —`default`. Если действие не задано в [[yii\\base\\Module::defaultRoute|defaultRoute]],\nто для его определения используется свойство [[yii\\base\\Controller::defaultAction|defaultAction]] контроллера.\nВ данном примере полный маршрут будет `user/default/index`.\n\n\n### Маршрут `catchAll` <span id=\"catchall-route\"></span>\n\nИногда возникает необходимость временно перевести приложение в режим обслуживания и отображать одно информационное сообщение для всех запросов. Существует много вариантов реализации этой задачи. Но одним из самых простых, является использование свойства [[yii\\web\\Application::catchAll]], например так:\n\n```php\n[\n    // ...\n    'catchAll' => ['site/offline'],\n];\n```\n\nВ данном случае, действие `site/offline` будет обрабатывать все входящие запросы.\n\nСвойство `catchAll` должно принимать массив, первый элемент которого определяет маршрут, а остальные элементы (пары ключ-значение) определяют параметры, [передаваемые действию](structure-controllers.md#parametry-dejstvij).\n\n## Создание URL <span id=\"creating-urls\"></span>\n\nДля создания разных видов URL из заданных маршрутов и параметров, Yii предоставляет метод-помощник [[yii\\helpers\\Url::to()]]. Примеры:\n\n```php\nuse yii\\helpers\\Url;\n\n// создает URL для маршрута: /index.php?r=post/index\necho Url::to(['post/index']);\n\n// создает URL для маршрута с параметрами: /index.php?r=post/view&id=100\necho Url::to(['post/view', 'id' => 100]);\n\n// создает якорный URL: /index.php?r=post/view&id=100#content\necho Url::to(['post/view', 'id' => 100, '#' => 'content']);\n\n// создает абсолютный URL: https://www.example.com/index.php?r=post/index\necho Url::to(['post/index'], true);\n\n// создает абсолютный URL с использованием схемы https: https://www.example.com/index.php?r=post/index\necho Url::to(['post/index'], 'https');\n```\n\nОбратите внимание, что в последнем примере подразумевается использование обычного формата URL. При использовании ЧПУ, будут созданы другие URL, соответствующие [[yii\\web\\UrlManager::rules|правилам создания URL]].\n\nМаршрут, переданный методу [[yii\\helpers\\Url::to()]], является контекстно-зависимым. Он может быть *относительным* или *абсолютным*, в зависимости от следующих правил:\n\n- Если маршрут является пустой строкой, будет использован текущий [[yii\\web\\Controller::route|маршрут]];\n- Если маршрут не содержит слешей вообще, он рассматривается как *идентификатор* действия текущего контроллера и будет дополнен значением [[\\yii\\web\\Controller::uniqueId|uniqueId]] текущего контроллера в качестве префикса;\n- Если маршрут не содержит слеша в начале, он будет рассматриваться как маршрут относительно текущего модуля и будет дополнен значением [[\\yii\\base\\Module::uniqueId|uniqueId]] текущего модуля, в качестве префикса.\n\nНачиная с версии 2.0.2, при составлении маршрутов, стало возможным использовать [псевдонимы](concept-aliases.md). В таком случае, псевдоним будет преобразован в маршрут, который будет использован для создания URL по правилам, указанным выше.\n\nДля примера, будем считать, что текущим модулем является `admin`, а текущим контроллером - `post`,\n\n```php\nuse yii\\helpers\\Url;\n\n// запрошенный маршрут: /index.php?r=admin/post/index\necho Url::to(['']);\n\n// относительный маршрут с указанием только идентификатора действия: /index.php?r=admin/post/index\necho Url::to(['index']);\n\n// относительный маршрут: /index.php?r=admin/post/index\necho Url::to(['post/index']);\n\n// абсолютный маршрут: /index.php?r=post/index\necho Url::to(['/post/index']);\n\n// /index.php?r=post/index     псевдоним \"@posts\" определен как \"/post/index\"\necho Url::to(['@posts']);\n```\n\nВ основе реализации метода [[yii\\helpers\\Url::to()]] лежит использование двух методов компонента [[yii\\web\\UrlManager|URL manager]]: [[yii\\web\\UrlManager::createUrl()|createUrl()]] и [[yii\\web\\UrlManager::createAbsoluteUrl()|createAbsoluteUrl()]]. Ниже будут рассмотрены способы конфигурации [[yii\\web\\UrlManager|URL manager]] для создания URL в различных форматах.\n\nМетод [[yii\\helpers\\Url::to()]] также поддерживает создание URL, не связанных с маршрутами приложения.\nВ данном случае, нужно передать в качестве первого параметра строку, а не массив. Например,\n \n```php\nuse yii\\helpers\\Url;\n\n// запрошенный URL: /index.php?r=admin/post/index\necho Url::to();\n\n// URL из псевдонима: https://example.com\nYii::setAlias('@example', 'https://example.com/');\necho Url::to('@example');\n\n// абсолютный URL: https://example.com/images/logo.gif\necho Url::to('/images/logo.gif', true);\n```\n\nКроме метода `to()`, класс [[yii\\helpers\\Url]] предоставляет и другие удобные методы для создания URL. Например,\n\n```php\nuse yii\\helpers\\Url;\n\n// домашний URL: /index.php?r=site/index\necho Url::home();\n\n// базовый URL, удобно использовать в случае, когда приложение расположено в подкаталоге\n// относительно корневого каталога веб-сервера\necho Url::base();\n\n// канонический URL запрошенного URL\n// подробнее https://support.google.com/webmasters/answer/139066?hl=ru\necho Url::canonical();\n\n// запомнить запрошенный URL и восстановить его при следующих запросах\nUrl::remember();\necho Url::previous();\n```\n\n## Использование человекопонятных URL <span id=\"using-pretty-urls\"></span>\n\nДля активации ЧПУ, необходимо настроить компонент `urlManager` в конфигурации приложения следующим образом:\n\n```php\n[\n    'components' => [\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            'showScriptName' => false,\n            'enableStrictParsing' => false,\n            'rules' => [\n                // ...\n            ],\n        ],\n    ],\n]\n```\n\nСвойство [[yii\\web\\UrlManager::enablePrettyUrl|enablePrettyUrl]] является ключевым, активирует формат ЧПУ.\nОстальные свойства необязательные. Однако в примере выше показан самый популярный вариант конфигурации ЧПУ.\n\n* [[yii\\web\\UrlManager::showScriptName|showScriptName]]: это свойство определяет необходимость включения имени входного скрипта в создаваемый URL. Например, при его значении `false`, вместо `/index.php/post/100`, будет сгенерирован URL `/post/100`.\n* [[yii\\web\\UrlManager::enableStrictParsing|enableStrictParsing]]: это свойство позволяет включить строгий разбор URL. Если строгий разбор URL включен, запрошенный URL должен соответствовать хотя бы одному из [[yii\\web\\UrlManager::rules|правил]], иначе будет вызвано исключение [[yii\\web\\NotFoundHttpException]]. Если строгий разбор URL отключен и ни одно из [[yii\\web\\UrlManager::rules|правил]] не подходит для разбора запрошенного URL, часть этого URL, представляющая путь, будет использована как маршрут.\n* [[yii\\web\\UrlManager::rules|rules]]: это свойство содержит набор правил для разбора и создания URL. Это основное свойство, с которым нужно работать, чтобы URL создавались в формате, соответствующем требованиям приложения.\n\n> Note: Для того, чтобы скрыть имя входного скрипта в создаваемых URL, кроме установки значения свойства [[yii\\web\\UrlManager::showScriptName|showScriptName]] в `false`, необходимо настроить веб-сервер, чтобы он мог правильно определять PHP-скрипт, который должен быть запущен, если в запрошенном URL он не указан явно. Рекомендованные настройки для Apache и Nginx описаны в разделе [Установка Yii](start-installation.md#rekomenduemye-nastrojki-apache).\n\n### Правила URL <span id=\"url-rules\"></span>\n\nПравила URL - это экземпляр класса [[yii\\web\\UrlRule]] или класса, унаследованного от него. Каждое правило состоит из шаблона, используемого для поиска пути в запрошенном URL, маршрута и нескольких параметров запроса. Правило может быть использовано для разбора запроса в том случае, если шаблон правила совпадает с запрошенным URL. Правило может быть использовано для создания URL в том случае, если его маршрут и параметры запроса совпадают с заданными.\n\nПри включенном режиме ЧПУ, компонент [[yii\\web\\UrlManager|URL manager]] использует правила URL, содержащиеся в его свойстве [[yii\\web\\UrlManager::rules|rules]], для разбора входящих запросов и создания URL. Обычно, при разборе входящего запроса, [[yii\\web\\UrlManager|URL manager]] проверяет все правила в порядке их следования, до *первого* правила, соответствующего запрошенному URL. Найденное правило используется для разбора URL на маршрут и параметры запроса. Аналогично для создания URL компонент [[yii\\web\\UrlManager|URL manager]] ищет первое правило, соответствующее заданному маршруту и параметрам и использует его для создания URL.\n\n[[yii\\web\\UrlManager::rules|Правила]] задаются ассоциативным массивом, где ключи определяют шаблоны, а значения соответствующие маршруты. Каждая пара шаблон-маршрут составляет правило разбора URL. Например, следующие [[yii\\web\\UrlManager::rules|правила]] определяют два правила разбора URL. Первое правило задает соответствие URL `posts` маршруту `post/index`. Второе правило задает соответствие URL, соответствующего регулярному выражению `post/(\\d+)` маршруту `post/view` и параметру `id`.\n\n```php\n[\n    'posts' => 'post/index', \n    'post/<id:\\d+>' => 'post/view',\n]\n```\n> Note: Шаблон правила используется для поиска соответствия с частью URL, определяющей путь. Например, в URL `/index.php/post/100?source=ad` путь определяет часть `post/100` (начальный и конечный слеши игнорируются), соответствующая регулярному выражению `post/(\\d+)`.\n\nПравила URL можно определять не только в виде пар шаблон-маршрут, но и в виде массива. Каждый массив используется для определения одного правила. Такой вид определения правил используется в случаях, когда необходимо указать другие параметры правила URL. Например,\n\n```php\n[\n    // ...другие правила URL...\n    \n    [\n        'pattern' => 'posts',\n        'route' => 'post/index',\n        'suffix' => '.json',\n    ],\n]\n```\n\nПо умолчанию, если в конфигурации правила URL не указан явно параметр `class`, будет создано правило класса [[yii\\web\\UrlRule]].\n\n\n### Именованные параметры <span id=\"named-parameters\"></span>\n\nПравило URL может содержать несколько именованных параметров запроса, которые указываются в шаблоне в следующем формате: `<ParamName:RegExp>`, где `ParamName` определяет имя параметра, а `RegExp` - необязательное регулярное выражение, используемое для определения значения параметра. В случае, если `RegExp` не указан, значением параметра будет любая последовательность символов кроме слешей.\n\n> Note: Возможно указание только регулярного выражения для параметров. В таком случае остальная часть шаблона будет считаться простым текстом.\n\nПосле разбора URL, параметры запроса, соответствующие шаблону правила, будут доступны в массиве `$_GET` через компонент приложения `request`.\nПри создании URL, значения указанных параметров будут вставлены в URL в соответствии с шаблоном правила.\n\nРассмотрим несколько примеров работы с именованными параметрами. Допустим, мы определили следующие три правила URL:\n\n```php\n[\n    'posts/<year:\\d{4}>/<category>' => 'post/index',\n    'posts' => 'post/index',\n    'post/<id:\\d+>' => 'post/view',\n]\n```\n\nПри разборе следующих URL:\n\n- `/index.php/posts` будет разобран в маршрут `post/index` при помощи второго правила;\n- `/index.php/posts/2014/php` будет разобран на маршрут `post/index` и параметры `year` со значением 2014, `category` со значением `php` при помощи первого правила;\n- `/index.php/post/100` будет разобран на маршрут `post/view` и параметр `id` со значением 100 при помощи третьего правила;\n- `/index.php/posts/php` вызовет исключение [[yii\\web\\NotFoundHttpException]], если [[yii\\web\\UrlManager::enableStrictParsing]] имеет значение `true`, так как правило для разбора данного URL отсутствует. Если [[yii\\web\\UrlManager::enableStrictParsing]] имеет значение `false` (по умолчанию), значение `posts/php` будет возвращено в качестве маршрута.\n\nПри создании URL:\n\n- `Url::to(['post/index'])` создаст `/index.php/posts` при помощи второго правила;\n- `Url::to(['post/index', 'year' => 2014, 'category' => 'php'])` создаст `/index.php/posts/2014/php` при помощи первого правила;\n- `Url::to(['post/view', 'id' => 100])` создаст `/index.php/post/100` при помощи третьего правила;\n- `Url::to(['post/view', 'id' => 100, 'source' => 'ad'])` создаст `/index.php/post/100?source=ad` при помощи третьего правила.\n  Параметр `source` не указан в правиле, поэтому он добавлен в созданный URL в качестве параметра запроса.\n- `Url::to(['post/index', 'category' => 'php'])` создаст `/index.php/post/index?category=php` без использования правил. При отсутствии подходящего правила, URL будет создан простым соединением маршрута, как части пути, и параметров, как части запроса.\n\n### Параметры в маршрутах <span id=\"parameterizing-routes\"></span>\n\nВ маршруте правила URL возможно указание имен параметров. Это позволяет использовать правило URL для обработки нескольких маршрутов. Например, следующие правила содержат параметры `controller` и `action` в маршрутах.\n\n```php\n[\n    '<controller:(post|comment)>/<id:\\d+>/<action:(create|update|delete)>' => '<controller>/<action>',\n    '<controller:(post|comment)>/<id:\\d+>' => '<controller>/view',\n    '<controller:(post|comment)>s' => '<controller>/index',\n]\n```\n\nДля разбора URL `/index.php/comment/100/create` будет использовано первое правило, которое установит значения параметров `controller` равным `comment` и `action` равным `create`. Таким образом, маршрут `<controller>/<action>` будет разрешен в `comment/create`.\n\nАналогично, для маршрута `comment/index`, при помощи третьего правила, будет создан URL `comment/index`.\n\n> Note: Использование параметров в маршрутах позволяет значительно уменьшить количество правил URL и улучшить производительность компонента [[yii\\web\\UrlManager|URL manager]].\n\nПо умолчанию все параметры, указанные в правиле, являются обязательными. Если запрошенный URL не содержит обязательный параметр, или если URL создается без обязательного параметра, данное правило не будет применено. Свойство [[yii\\web\\UrlRule::defaults]] позволяет сделать нужные параметры не обязательными. Параметры, перечисленные в данном свойстве, будут иметь заданные значения, в случае если они пропущены.\n\nВ следующем правиле описаны необязательные параметры `page` и `tag`, которые примут значения `1` и `пустая строка` в случае, если они будут пропущены.\n\n```php\n[\n    // ...другие правила...\n    [\n        'pattern' => 'posts/<page:\\d+>/<tag>',\n        'route' => 'post/index',\n        'defaults' => ['page' => 1, 'tag' => ''],\n    ],\n]\n```\n\nВышеприведенное правило может быть использовано для разбора или создания следующих URL:\n\n* `/index.php/posts`: `page` равно 1, `tag` равно ''.\n* `/index.php/posts/2`: `page` равно 2, `tag` равно ''.\n* `/index.php/posts/2/news`: `page` равно 2, `tag` равно `'news'`.\n* `/index.php/posts/news`: `page` равно 1, `tag` равно `'news'`.\n\nБез использования необязательных параметров понадобилось бы создать 4 правила для достижения того же результата.\n\n\n### Правила с именами серверов <span id=\"rules-with-server-names\"></span>\n\nСуществует возможность включать имена серверов в шаблон правил URL. Главным образом, это удобно, когда требуется разное поведение приложения, в зависимости от разных имен веб-серверов. Например, следующее правило позволит разобрать URL `https://admin.example.com/login` в маршрут `admin/user/login` и `https://www.example.com/login` в `site/login`.\n\n```php\n[\n    'https://admin.example.com/login' => 'admin/user/login',\n    'https://www.example.com/login' => 'site/login',\n]\n```\n\nТакже возможно комбинирование параметров и имени сервера для динамического извлечения данных из него. Например, следующее правило позволит разобрать URL `https://en.example.com/posts` на маршрут и параметр `language=en`.\n\n```php\n[\n    'http://<language:\\w+>.example.com/posts' => 'post/index',\n]\n```\n\n> Note: Правила, содержащие имя сервера, НЕ должны содержать в шаблоне подкаталог пути ко входному скрипту. Например, если приложение расположено в `https://www.example.com/sandbox/blog`, шаблон должен быть `https://www.example.com/posts`, вместо `https://www.example.com/sandbox/blog/posts`. Это позволит изменять расположение приложения без необходимости внесения изменений в его код.\n\n### Суффиксы в URL <span id=\"url-suffixes\"></span>\n\nКомпонент предоставляет возможность добавления к URL суффиксов. Например, можно добавить к URL `.html`, чтобы они выглядели как статические HTML-страницы; можно добавить к URL суффикс `.json`, для указания на ожидаемый тип данных ответа. Настроить суффиксы в URL можно при помощи соответствующего свойства [[yii\\web\\UrlManager::suffix]] в конфигурации приложения:\n\n```php\n[\n    'components' => [\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            'showScriptName' => false,\n            'enableStrictParsing' => true,\n            'suffix' => '.html',\n            'rules' => [\n                // ...\n            ],\n        ],\n    ],\n]\n```\n\nДанная конфигурация позволяет компоненту [[yii\\web\\UrlManager|URL manager]] разбирать и создавать URL с суффиксом `.html`.\n\n> Tip: При установке суффикса `/`, все URL будут заканчиваться слешем.\n\n> Note: При настроенном суффиксе, все URL не содержащие этот суффикс будут расценены как неизвестные URL. Такое поведение рекомендовано для SEO (поисковая оптимизация).\n\nИногда возникает необходимость использовать разные суффиксы для разных URL. Добиться этого можно настройкой свойства [[yii\\web\\UrlRule::suffix|suffix]] у каждого правила. Когда это свойство установлено, оно имеет приоритет перед общей конфигурацией компонента [[yii\\web\\UrlManager|URL manager]]. Например, следующая конфигурация содержит правило URL, которое использует `.json` в качестве суффикса вместо глобального `.html`.\n\n```php\n[\n    'components' => [\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            'showScriptName' => false,\n            'enableStrictParsing' => true,\n            'suffix' => '.html',\n            'rules' => [\n                // ...\n                [\n                    'pattern' => 'posts',\n                    'route' => 'post/index',\n                    'suffix' => '.json',\n                ],\n            ],\n        ],\n    ],\n]\n```\n\n\n### Нормализация URL <span id=\"url-normalization\"></span>\n\nНачиная с версии 2.0.10 [[yii\\web\\UrlManager|UrlManager]] может быть настроен на использование [[yii\\web\\UrlNormalizer|UrlNormalizer]],\nчто позволяет справиться с вариациями одного и того же URL с присутствующим или отсутствующим слешем в конце.\nТехнически `https://example.com/path` и `https://example.com/path/` являются разными URL, отдача одинакового содержимого\nв обоих вариантах может негативно повлиять на SEO. По умолчанию нормализатор заменяет повторяющиеся слеши на один и либо\nубирает, либо добавляет завершающие слеши в зависимости от суффикса и производит [редирект 301](https://ru.wikipedia.org/wiki/HTTP_301)\nна нормализованный URL. Нормализатор может быть настроен как глобально для менеджера URL, так и индивидуально для\nкаждого правила. По умолчанию все правила используют нормализатор, заданный в менеджере URL. Вы можете выставить\n[[yii\\web\\UrlRule::$normalizer|UrlRule::$normalizer]] в `false` для отключения нормализации для конкретного правила.\n\nНиже преведён пример конфигурации UrlNormalizer:\n\n```php\n[\n    'components' => [\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            'showScriptName' => false,\n            'enableStrictParsing' => true,\n            'suffix' => '.html',\n            'normalizer' => [\n                'class' => 'yii\\web\\UrlNormalizer',\n                'action' => UrlNormalizer::ACTION_REDIRECT_TEMPORARY, // используем временный редирект вместо постоянного\n            ],\n            'rules' => [\n                // ...\n                [\n                    'pattern' => 'posts',\n                    'route' => 'post/index',\n                    'suffix' => '/',\n                    'normalizer' => false, // отключаем нормализатор для этого правила\n                ],\n                [\n                    'pattern' => 'tags',\n                    'route' => 'tag/index',\n                    'normalizer' => [\n                        'collapseSlashes' => false, // не убираем дублирующиеся слеши для этого правила\n                    ],\n                ],\n            ],\n        ],\n    ],\n]\n```\n\n> Note: По умолчанию [[yii\\web\\UrlManager::$normalizer|UrlManager::$normalizer]] отключен. Чтобы использовать\n  нормализацию, его необходимо сконфигурировать.\n\n### Методы HTTP <span id=\"http-methods\"></span>\n\nПри реализации RESTful API, зачастую бывает необходимость в том, чтобы один и тот же URL был разобран в разные маршруты, в зависимости от HTTP-метода запроса. Это легко достигается указанием HTTP-методов, поддерживаемых правилом в начале шаблона. Если правило поддерживает несколько HTTP-методов, их имена разделяются запятыми. Например, следующие правила имеют шаблон `post/<id:\\d+>` с разными поддерживаемыми HTTP-методами. Запрос `PUT post/100` будет разобран в маршрут `post/create`, в то время, как запрос `GET post/100` будер разобран в `post/view`.\n\n```php\n[\n    'PUT,POST post/<id:\\d+>' => 'post/create',\n    'DELETE post/<id:\\d+>' => 'post/delete',\n    'post/<id:\\d+>' => 'post/view',\n]\n```\n\n> Note: Если правило URL содержит HTTP-метод в шаблоне, это правило будет использовано только при разборе URL. Такое правило не будет учитываться компонентом [[yii\\web\\UrlManager|URL manager]] при создании URL.\n\n> Tip: Для упрощения маршрутизации RESTful API, Yii предоставляет специальный класс [[yii\\rest\\UrlRule]], который достаточно эффективен и предоставляет такие удобные возможности, как автоматическое приведение идентификаторов контроллеров к множественной форме. Более подробную информацию можно найти в разделе Веб-сервисы REST [Роутинг](rest-routing.md).\n\n\n### Гибкая настройка правил <span id=\"customizing-rules\"></span>\n\nВ предыдущих примерах, преимущественно, приводились правила URL, заданные парами шаблон-маршрут. Это самый распространенный, краткий формат. В некоторых случаях возникает необходимость более гибкой настройки правил, например указание суффикса при помощи свойства [[yii\\web\\UrlRule::suffix]]. Пример конфигурации правила URL при помощи массива был рассмотрен в главе [Суффиксы в URL](#suffiksy-v-url):\n\n```php\n[\n    // ...другие правила URL...\n    \n    [\n        'pattern' => 'posts',\n        'route' => 'post/index',\n        'suffix' => '.json',\n    ],\n]\n```\n\n> Info: По умолчанию, если в конфигурации правила явно не задан параметр `class`, будет создано правило класса [[yii\\web\\UrlRule]].\n\n\n### Добавление правил URL динамически <span id=\"adding-rules\"></span>\n\nПравила URL могут быть динамически добавлены в компонент [[yii\\web\\UrlManager|URL manager]]. Часто это необходимо подключаемым [модулям](structure-modules.md) для настройки своих правил URL. Для того чтобы динамически добавленные правила могли влиять на процесс роутинга, они должны быть добавлены в процессе [предзагрузки](runtime-bootstrapping.md). В частности, модули должны реализовываться интерфейс [[yii\\base\\BootstrapInterface]] и добавлять правила в методе [[yii\\base\\BootstrapInterface::bootstrap()|bootstrap()]], например:\n\n```php\npublic function bootstrap($app)\n{\n    $app->getUrlManager()->addRules([\n        // правила URL описываются здесь\n    ], false);\n}\n```\n\nТакже необходимо включить данный модуль в [[yii\\web\\Application::bootstrap]], чтобы он смог участвовать в процессе [предзагрузки](runtime-bootstrapping.md).\n\n\n### Создание классов правил <span id=\"creating-rules\"></span>\n\nНесмотря на то, что встроенный класс [[yii\\web\\UrlRule]] достаточно функционален для большинства проектов, иногда возникает необходимость в создании своего класса правил URL. Например, на сайте продавца автомобилей существует необходимость поддержки URL в таком формате: `/Manufacturer/Model`, где и `Manufacturer` и `Model` должны соответствовать данным, хранящимся в базе данных. Стандартный класс [[yii\\web\\UrlRule]] не подойдет, так как он рассчитан на работу со статичными шаблонами.\n\nДля решения данной проблемы можно создать такой класс правила URL.\n\n```php\nnamespace app\\components;\n\nuse yii\\web\\UrlRuleInterface;\nuse yii\\base\\BaseObject;\n\nclass CarUrlRule extends BaseObject implements UrlRuleInterface\n{\n\n    public function createUrl($manager, $route, $params)\n    {\n        if ($route === 'car/index') {\n            if (isset($params['manufacturer'], $params['model'])) {\n                return $params['manufacturer'] . '/' . $params['model'];\n            } elseif (isset($params['manufacturer'])) {\n                return $params['manufacturer'];\n            }\n        }\n        return false;  // данное правило не применимо\n    }\n\n    public function parseRequest($manager, $request)\n    {\n        $pathInfo = $request->getPathInfo();\n        if (preg_match('%^(\\w+)(/(\\w+))?$%', $pathInfo, $matches)) {\n            // Ищем совпадения $matches[1] и $matches[3] \n            // с данными manufacturer и model в базе данных\n            // Если нашли, устанавливаем $params['manufacturer'] и/или $params['model']\n            // и возвращаем ['car/index', $params]\n        }\n        return false;  // данное правило не применимо\n    }\n}\n```\n\nИ использовать новый класс [[yii\\web\\UrlManager::rules]] при определении правил URL:\n\n```php\n[\n    // ...другие правила...\n    \n    [\n        'class' => 'app\\components\\CarUrlRule', \n        // ...настройка других параметров правила...\n    ],\n]\n```\n\n## Производительность <span id=\"performance-consideration\"></span>\n\nПри разработке сложных веб-приложений, важно оптимизировать правила URL так, чтобы разбор запросов и создание URL занимали минимальное время.\n\nИспользование параметров в маршрутах позволяет уменьшить количество правил, что значительно увеличивает производительность.\n\nПри разборе или создании URL, компонент [[yii\\web\\UrlManager|URL manager]] проверяет правила в порядке их определения. Поэтому следует более узконаправленные и/или часто используемые правила размещать раньше прочих.\n\nВ случае, если несколько правил имеют один и тот же префикс в шаблоне или маршруте, можно рассмотреть использование [[yii\\web\\GroupUrlRule]], что позволит компоненту [[yii\\web\\UrlManager|URL manager]] более эффективно обрабатывать правила группами. Часто это бывает полезно в случае, если приложение состоит из модулей, каждый из которых имеет свой набор правил с идентификатором модуля в качестве общего префикса.\n"
  },
  {
    "path": "docs/guide-ru/runtime-sessions-cookies.md",
    "content": "Сессии и куки\n====================\n\nСессии и куки позволяют сохранять пользовательские данные между запросами. При использовании чистого PHP можно получить доступ к этим данным через глобальные переменные `$_SESSION` и `$_COOKIE`, соответственно. Yii инкапсулирует сессии и куки в объекты, что дает возможность обращаться к ним в объектно-ориентированном стиле и дает дополнительное удобство в работе.\n\n\n## Сессии <span id=\"sessions\"></span>\n\nПо аналогии с [запросами](runtime-requests.md) и [ответами](runtime-responses.md), к сессии можно получить доступ через `session` [компонент приложения](structure-application-components.md), который по умолчанию является экземпляром [[yii\\web\\Session]].\n\n\n### Открытие и закрытие сессии <span id=\"opening-closing-sessions\"></span>\n\nОткрыть и закрыть сессию можно следующим образом:\n\n```php\n$session = Yii::$app->session;\n\n// проверяем что сессия уже открыта\nif ($session->isActive) ...\n\n// открываем сессию\n$session->open();\n\n// закрываем сессию\n$session->close();\n\n// уничтожаем сессию и все связанные с ней данные.\n$session->destroy();\n```\n\nМожно вызывать [[yii\\web\\Session::open()|open()]] и [[yii\\web\\Session::close()|close()]] многократно без возникновения ошибок; внутри компонента все методы проверяют сессию на факт того, открыта она или нет.\n\n\n### Доступ к данным сессии <span id=\"access-session-data\"></span>\n\nПолучить доступ к сохраненным в сессию данным можно следующим образом:\n\n```php\n$session = Yii::$app->session;\n\n// получение переменной из сессии. Следующие способы использования эквивалентны:\n$language = $session->get('language');\n$language = $session['language'];\n$language = isset($_SESSION['language']) ? $_SESSION['language'] : null;\n\n// запись переменной в сессию. Следующие способы использования эквивалентны:\n$session->set('language', 'en-US');\n$session['language'] = 'en-US';\n$_SESSION['language'] = 'en-US';\n\n// Удаление переменной из сессии. Следующие способы использования эквивалентны:\n$session->remove('language');\nunset($session['language']);\nunset($_SESSION['language']);\n\n// проверка на существование переменной в сессии. Следующие способы использования эквивалентны:\nif ($session->has('language')) ...\nif (isset($session['language'])) ...\nif (isset($_SESSION['language'])) ...\n\n// Обход всех переменных в сессии. Следующие способы использования эквивалентны:\nforeach ($session as $name => $value) ...\nforeach ($_SESSION as $name => $value) ...\n```\n\n> Info: При получении данных из сессии через компонент `session`, сессия будет автоматически открыта, если она не была открыта до этого. В этом заключается отличие от получения данных из глобальной переменной `$_SESSION`, которое требует обязательного вызова  `session_start()`.\n\nПри работе с сессионными данными, являющимися массивами, компонент `session` имеет ограничение, запрещающее прямую модификацию отдельных элементов массива. Например,\n\n```php\n$session = Yii::$app->session;\n\n// следующий код НЕ БУДЕТ работать\n$session['captcha']['number'] = 5;\n$session['captcha']['lifetime'] = 3600;\n\n// а этот будет:\n$session['captcha'] = [\n    'number' => 5,\n    'lifetime' => 3600,\n];\n\n// этот код также будет работать:\necho $session['captcha']['lifetime'];\n```\n\nДля решения этой проблемы можно использовать следующие обходные приемы:\n\n```php\n$session = Yii::$app->session;\n\n// прямое использование $_SESSION (убедитесь, что Yii::$app->session->open() был вызван)\n$_SESSION['captcha']['number'] = 5;\n$_SESSION['captcha']['lifetime'] = 3600;\n\n// получите весь массив, модифицируйте и сохраните обратно в сессию\n$captcha = $session['captcha'];\n$captcha['number'] = 5;\n$captcha['lifetime'] = 3600;\n$session['captcha'] = $captcha;\n\n// используйте ArrayObject вместо массива\n$session['captcha'] = new \\ArrayObject;\n...\n$session['captcha']['number'] = 5;\n$session['captcha']['lifetime'] = 3600;\n\n// записывайте данные с ключами, имеющими одинаковый префикс\n$session['captcha.number'] = 5;\n$session['captcha.lifetime'] = 3600;\n```\n\nДля улучшения производительности и читаемости кода рекомендуется использовать последний прием. Другими словами, вместо того, чтобы хранить массив как одну переменную сессии, мы сохраняем каждый элемент массива как обычную сессионную переменную с общим префиксом.\n\n\n### Пользовательское хранилище для сессии <span id=\"custom-session-storage\"></span>\n\nПо умолчанию класс [[yii\\web\\Session]] сохраняет данные сессии в виде файлов на сервере. Однако Yii предоставляет ряд классов, которые реализуют различные способы хранения данных сессии:\n\n* [[yii\\web\\DbSession]]: сохраняет данные сессии в базе данных.\n* [[yii\\web\\CacheSession]]: хранение данных сессии в предварительно сконфигурированном компоненте кэша [кэш](caching-data.md#cache-components).\n* [[yii\\redis\\Session]]: хранение данных сессии в [redis](https://redis.io/).\n* [[yii\\mongodb\\Session]]: хранение сессии в [MongoDB](https://www.mongodb.com/).\n\nВсе эти классы поддерживают одинаковый набор методов API. В результате вы можете переключаться между различными хранилищами сессий без модификации кода приложения.\n\n> Note: Если вы хотите получить данные из переменной `$_SESSION` при использовании пользовательского хранилища, вы должны быть уверены, что сессия уже стартовала [[yii\\web\\Session::open()]], в связи с тем, что обработчики хранения пользовательских сессий регистрируются в этом методе.\n\nЧтобы узнать, как настроить и использовать эти компоненты, обратитесь к документации по API. Ниже приведен пример конфигурации [[yii\\web\\DbSession]] для использования базы данных для хранения сессии:\n\n```php\nreturn [\n    'components' => [\n        'session' => [\n            'class' => 'yii\\web\\DbSession',\n            // 'db' => 'mydb',  // ID компонента для взаимодействия с БД. По умолчанию 'db'.\n            // 'sessionTable' => 'my_session', // название таблицы для хранения данных сессии. По умолчанию 'session'.\n        ],\n    ],\n];\n```\n\nТакже необходимо создать таблицу для хранения данных сессии:\n\n```sql\nCREATE TABLE session\n(\n    id CHAR(40) NOT NULL PRIMARY KEY,\n    expire INTEGER,\n    data BLOB\n)\n```\n\nгде 'BLOB' соответствует типу данных предпочитаемой вами DBMS. Ниже приведены примеры соответствия типов BLOB в наиболее популярных DBMS:\n\n- MySQL: LONGBLOB\n- PostgreSQL: BYTEA\n- MSSQL: BLOB\n\n> Note: В зависимости от настроек параметра `session.hash_function` в вашем php.ini, может понадобиться изменить длину поля `id`. Например, если `session.hash_function=sha256`, нужно  установить длину поля в 64 вместо 40.\n\n\n### Flash-сообщения <span id=\"flash-data\"></span>\n\nFlash-сообщения - это особый тип данных в сессии, которые устанавливаются один раз во время запроса и доступны только на протяжении следующего запроса, затем они автоматически удаляются. Такой способ хранения информации в сессии наиболее часто используется для реализации сообщений, которые будут отображены конечному пользователю один раз, например подтверждение об успешной отправке формы.\n\nУстановить и получить flash-сообщения можно через компонент приложения `session`. Например:\n\n```php\n$session = Yii::$app->session;\n\n// Запрос #1\n// установка flash-сообщения с названием \"postDeleted\"\n$session->setFlash('postDeleted', 'Вы успешно удалили пост.');\n\n// Запрос #2\n// отображение flash-сообщения \"postDeleted\"\necho $session->getFlash('postDeleted');\n\n// Запрос #3\n// переменная $result будет иметь значение false, так как flash-сообщение было автоматически удалено\n$result = $session->hasFlash('postDeleted');\n```\n\nТак как flash-сообщения хранятся в сессии как обычные данные, в них можно записывать произвольную информацию, и она будет доступна лишь в следующем запросе.\n\nПри вызове [[yii\\web\\Session::setFlash()]], происходит перезаписывание flash-сообщений c таким же названием.\nДля того, чтобы добавить новые данные к уже существующему flash-сообщению, необходимо вызвать [[yii\\web\\Session::addFlash()]].\nНапример:\n\n```php\n$session = Yii::$app->session;\n\n// Запрос #1\n// добавить новое flash-сообщение с названием \"alerts\"\n$session->addFlash('alerts', 'Вы успешно удалили пост.');\n$session->addFlash('alerts', 'Вы успешно добавили нового друга.');\n$session->addFlash('alerts', 'Благодарим.');\n\n// Запрос #2\n// Переменная $alerts теперь содержит массив flash-сообщений с названием \"alerts\"\n$alerts = $session->getFlash('alerts');\n```\n\n> Note: Старайтесь не использовать [[yii\\web\\Session::setFlash()]] совместно с [[yii\\web\\Session::addFlash()]] для flash-сообщений с одинаковым названием. Это связано с тем, что последний метод автоматически преобразует хранимые данные в массив, чтобы иметь возможность хранить и добавлять новые данные в flash-сообщения с тем же названием. В результате, при вызове [[yii\\web\\Session::getFlash()]] можно обнаружить, что возвращается массив, в то время как ожидалась строка.\n\n\n## Куки <span id=\"cookies\"></span>\n\nYii представляет каждую куку как объект [[yii\\web\\Cookie]]. Оба компонента приложения [[yii\\web\\Request]] и [[yii\\web\\Response]]\nподдерживают коллекции кук через свойство `cookies`. В первом случае коллекция кук является их представлением из HTTP-запроса, во втором - представляет куки, которые будут отправлены пользователю.\n\n\n### Чтение кук <span id=\"reading-cookies\"></span>\n\nПолучить куки из текущего запроса можно следующим образом:\n\n```php\n// получение коллекции кук (yii\\web\\CookieCollection) из компонента \"request\"\n$cookies = Yii::$app->request->cookies;\n\n// получение куки с названием \"language. Если кука не существует, \"en\"  будет возвращено как значение по-умолчанию.\n$language = $cookies->getValue('language', 'en');\n\n// альтернативный способ получения куки \"language\"\nif (($cookie = $cookies->get('language')) !== null) {\n    $language = $cookie->value;\n}\n\n// теперь переменную $cookies можно использовать как массив\nif (isset($cookies['language'])) {\n    $language = $cookies['language']->value;\n}\n\n// проверка на существование куки \"language\"\nif ($cookies->has('language')) ...\nif (isset($cookies['language'])) ...\n```\n\n\n### Отправка кук <span id=\"sending-cookies\"></span>\n\nОтправить куку конечному пользователю можно следующим образом:\n\n```php\n// получение коллекции (yii\\web\\CookieCollection) из компонента \"response\"\n$cookies = Yii::$app->response->cookies;\n\n// добавление новой куки в HTTP-ответ\n$cookies->add(new \\yii\\web\\Cookie([\n    'name' => 'language',\n    'value' => 'zh-CN',\n]));\n\n// удаление куки...\n$cookies->remove('language');\n// ...что эквивалентно следующему:\nunset($cookies['language']);\n```\n\nКроме свойств [[yii\\web\\Cookie::name|name]] и [[yii\\web\\Cookie::value|value]], класс [[yii\\web\\Cookie]] также предоставляет ряд свойств для получения информации о куках: [[yii\\web\\Cookie::domain|domain]], [[yii\\web\\Cookie::expire|expire]]. Эти свойства можно сконфигурировать и затем добавить куку в коллекцию для HTTP-ответа.\n\n> Note: Для большей безопасности значение свойства [[yii\\web\\Cookie::httpOnly]] по умолчанию установлено в `true`. Это уменьшает риски доступа к защищенной куке на клиентской стороне (если браузер поддерживает такую возможность). Вы можете обратиться к [httpOnly wiki](https://owasp.org/www-community/HttpOnly) для дополнительной информации.\n\n### Валидация кук <span id=\"cookie-validation\"></span>\n\nВо время записи и чтения кук через компоненты `request` и `response`, как будет показано в двух последующих подразделах, фреймворк предоставляет автоматическую валидацию, которая обеспечивает защиту кук от модификации на стороне клиента. Это достигается за счет подписи каждой куки секретным ключом, позволяющим приложению распознать куку, которая была модифицирована на клиентской стороне. В таком случае кука НЕ БУДЕТ доступна через свойство [[yii\\web\\Request::cookies|cookie collection]] компонента `request`.\n\n> Note: Валидация кук защищает только от их модификации. Если валидация не была пройдена, получить доступ к кукам все еще можно через глобальную переменную `$_COOKIE`. Это связано с тем, что дополнительные пакеты и библиотеки могут манипулировать куками без вызова валидации, которую обеспечивает Yii.\n\nПо-умолчанию валидация кук включена. Её можно отключить, установив свойство [[yii\\web\\Request::enableCookieValidation]]\nв `false`, однако мы настоятельно не рекомендуем это делать.\n\n> Note: Куки, которые напрямую читаются/пишутся через `$_COOKIE` и `setcookie()` НЕ БУДУТ валидироваться.\n\nПри использовании валидации кук необходимо указать значение свойства [[yii\\web\\Request::cookieValidationKey]], которое будет использовано для генерации вышеупомянутого секретного ключа. Это можно сделать, настроив компонент `request` в конфигурации приложения:\n\n```php\nreturn [\n    'components' => [\n        'request' => [\n            'cookieValidationKey' => 'fill in a secret key here',\n        ],\n    ],\n];\n```\n\n> Note: Свойство [[yii\\web\\Request::cookieValidationKey|cookieValidationKey]] является секретным значением и должно быть известно только людям, которым вы доверяете. Не помещайте эту информацию под систему контроля версий.\n\n"
  },
  {
    "path": "docs/guide-ru/security-authentication.md",
    "content": "Аутентификация\n==============\n\nАутентификация — это процесс проверки подлинности пользователя. Обычно используется идентификатор\n(например, `username` или адрес электронной почты) и секретный токен (например, пароль или ключ доступа), чтобы судить о\nтом, что пользователь именно тот, за кого себя выдаёт. Аутентификация является основной функцией формы входа.\n\nYii предоставляет фреймворк авторизации с различными компонентами, обеспечивающими процесс входа.\nДля использования этого фреймворка вам нужно проделать следующее:\n\n* Настроить компонент приложения [[yii\\web\\User|user]];\n* Создать класс, реализующий интерфейс [[yii\\web\\IdentityInterface]].\n\n\n## Настройка [[yii\\web\\User]] <span id=\"configuring-user\"></span>\n\nКомпонент [[yii\\web\\User|user]] управляет статусом аутентификации пользователя.\nОн требует, чтобы вы указали [[yii\\web\\User::identityClass|identity class]], который будет содержать\nтекущую логику аутентификации. В следующей конфигурации приложения, [[yii\\web\\User::identityClass|identity class]] для\n[[yii\\web\\User|user]] задан как `app\\models\\User`, реализация которого будет объяснена в следующем разделе:\n\n```php\nreturn [\n    'components' => [\n        'user' => [\n            'identityClass' => 'app\\models\\User',\n        ],\n    ],\n];\n```\n\n\n## Реализация [[yii\\web\\IdentityInterface]] <span id=\"implementing-identity\"></span>\n\n[[yii\\web\\User::identityClass|identity class]] должен реализовывать [[yii\\web\\IdentityInterface]],\nкоторый содержит следующие методы:\n\n* [[yii\\web\\IdentityInterface::findIdentity()|findIdentity()]]: Этот метод находит экземпляр `identity class`,\n  используя ID пользователя. Этот метод используется, когда необходимо поддерживать состояние аутентификации через сессии.\n* [[yii\\web\\IdentityInterface::findIdentityByAccessToken()|findIdentityByAccessToken()]]: Этот метод находит экземпляр `identity class`,\n  используя токен доступа. Метод используется, когда требуется аутентифицировать пользователя\n  только по секретному токену (например в RESTful приложениях, не сохраняющих состояние между запросами).\n* [[yii\\web\\IdentityInterface::getId()|getId()]]: Этот метод возвращает ID пользователя, представленного данным экземпляром `identity`.\n* [[yii\\web\\IdentityInterface::getAuthKey()|getAuthKey()]]: Этот метод возвращает ключ, используемый для основанной на `cookie` аутентификации.\n  Ключ сохраняется в аутентификационной cookie и позже сравнивается с версией, находящейся на сервере,\n  чтобы удостоверится, что аутентификационная `cookie` верная.\n* [[yii\\web\\IdentityInterface::validateAuthKey()|validateAuthKey()]]: Этот метод реализует логику проверки ключа\n  для основанной на `cookie` аутентификации.\n\nЕсли какой-то из методов не требуется, то можно реализовать его с пустым телом. Для примера,\nесли у вас RESTful приложение, не сохраняющее состояние между запросами, вы можете реализовать только\n[[yii\\web\\IdentityInterface::findIdentityByAccessToken()|findIdentityByAccessToken()]]\nи [[yii\\web\\IdentityInterface::getId()|getId()]], тогда как остальные методы оставить пустыми.\n\nВ следующем примере, [[yii\\web\\User::identityClass|identity class]] реализован\nкак класс [Active Record](db-active-record.md), связанный с таблицей `user`.\n\n```php\n<?php\n\nuse yii\\db\\ActiveRecord;\nuse yii\\web\\IdentityInterface;\n\nclass User extends ActiveRecord implements IdentityInterface\n{\n    public static function tableName()\n    {\n        return 'user';\n    }\n\n    /**\n     * Finds an identity by the given ID.\n     *\n     * @param string|int $id the ID to be looked for\n     * @return IdentityInterface|null the identity object that matches the given ID.\n     */\n    public static function findIdentity($id)\n    {\n        return static::findOne($id);\n    }\n\n    /**\n     * Finds an identity by the given token.\n     *\n     * @param string $token the token to be looked for\n     * @return IdentityInterface|null the identity object that matches the given token.\n     */\n    public static function findIdentityByAccessToken($token, $type = null)\n    {\n        return static::findOne(['access_token' => $token]);\n    }\n\n    /**\n     * @return int|string current user ID\n     */\n    public function getId()\n    {\n        return $this->id;\n    }\n\n    /**\n     * @return string current user auth key\n     */\n    public function getAuthKey()\n    {\n        return $this->auth_key;\n    }\n\n    /**\n     * @param string $authKey\n     * @return bool if auth key is valid for current user\n     */\n    public function validateAuthKey($authKey)\n    {\n        return $this->getAuthKey() === $authKey;\n    }\n}\n```\n\nКак объяснялось ранее, вам нужно реализовать только `getAuthKey()` и `validateAuthKey()`, если ваше приложение использует\nтолько аутентификацию основанную на `cookie`. В этом случае вы можете использовать следующий код для генерации\nключа аутентификации для каждого пользователя и хранения его в таблице `user`:\n\n```php\nclass User extends ActiveRecord implements IdentityInterface\n{\n    ......\n\n    public function beforeSave($insert)\n    {\n        if (parent::beforeSave($insert)) {\n            if ($this->isNewRecord) {\n                $this->auth_key = \\Yii::$app->security->generateRandomString();\n            }\n            return true;\n        }\n        return false;\n    }\n}\n```\n\n> Note: Не путайте `identity` класс `User` с классом [[yii\\web\\User]]. Первый является классом, реализующим\n  логику аутентификации пользователя. Он часто реализуется как класс [Active Record](db-active-record.md), связанный\n  с некоторым постоянным хранилищем, где лежит информация о пользователях. Второй — это класс компонента приложения,\n  отвечающий за управление состоянием аутентификации пользователя.\n\n\n## Использование [[yii\\web\\User]] <span id=\"using-user\"></span>\n\nВ основном класс [[yii\\web\\User]] используют как компонент приложения `user`.\n\nМожно получить `identity` текущего пользователя, используя выражение `Yii::$app->user->identity`. Оно вернёт экземпляр\n[[yii\\web\\User::identityClass|identity class]], представляющий текущего аутентифицированного пользователя,\nили `null`, если текущий пользователь не аутентифицирован (например, гость). Следующий код показывает, как получить\nдругую связанную с аутентификацией информацию из [[yii\\web\\User]]:\n\n```php\n// `identity` текущего пользователя. `Null`, если пользователь не аутентифицирован.\n$identity = Yii::$app->user->identity;\n\n// ID текущего пользователя. `Null`, если пользователь не аутентифицирован.\n$id = Yii::$app->user->id;\n\n// проверка на то, что текущий пользователь гость (не аутентифицирован)\n$isGuest = Yii::$app->user->isGuest;\n```\n\nДля залогинивания пользователя вы можете использовать следующий код:\n\n```php\n// найти identity с указанным username.\n// замечание: также вы можете проверить и пароль, если это нужно\n$identity = User::findOne(['username' => $username]);\n\n// логиним пользователя\nYii::$app->user->login($identity);\n```\n\nМетод [[yii\\web\\User::login()]] устанавливает `identity` текущего пользователя в [[yii\\web\\User]]. Если сессии\n[[yii\\web\\User::enableSession|включены]], то `identity` будет сохраняться в сессии, так что состояние\nстатуса аутентификации будет поддерживаться на всём протяжении сессии. Если [[yii\\web\\User::enableAutoLogin|включен]] вход, основанный на cookie (так называемый \"запомни меня\" вход), то `identity` также будет сохранена\nв `cookie` так, чтобы статус аутентификации пользователя мог быть восстановлен на протяжении всего времени жизни `cookie`.\n\nДля включения входа, основанного на `cookie`, вам нужно установить [[yii\\web\\User::enableAutoLogin]] в `true`\nв конфигурации приложения. Вы также можете настроить время жизни, передав его при вызове метода [[yii\\web\\User::login()]].\n\nДля выхода пользователя, просто вызовите\n\n```php\nYii::$app->user->logout();\n```\n\nОбратите внимание: выход пользователя имеет смысл только если сессии включены. Метод сбрасывает статус аутентификации\nсразу и из памяти и из сессии. И по умолчанию, будут также уничтожены *все* сессионные данные пользователя.\nЕсли вы хотите сохранить сессионные данные, вы должны вместо этого вызвать `Yii::$app->user->logout(false)`.\n\n\n## События аутентификации <span id=\"auth-events\"></span>\n\nКласс [[yii\\web\\User]] вызывает несколько событий во время процессов входа и выхода.\n\n* [[yii\\web\\User::EVENT_BEFORE_LOGIN|EVENT_BEFORE_LOGIN]]: вызывается перед вызовом [[yii\\web\\User::login()]].\n  Если обработчик устанавливает свойство [[yii\\web\\UserEvent::isValid|isValid]] объекта в `false`,\n  процесс входа будет прерван.\n* [[yii\\web\\User::EVENT_AFTER_LOGIN|EVENT_AFTER_LOGIN]]: вызывается после успешного входа.\n* [[yii\\web\\User::EVENT_BEFORE_LOGOUT|EVENT_BEFORE_LOGOUT]]: вызывается перед вызовом [[yii\\web\\User::logout()]].\n  Если обработчик устанавливает свойство [[yii\\web\\UserEvent::isValid|isValid]] объекта в `false`,\n  процесс выхода будет прерван.\n* [[yii\\web\\User::EVENT_AFTER_LOGOUT|EVENT_AFTER_LOGOUT]]: вызывается после успешного выхода.\n\nВы можете использовать эти события для реализации функции аудита входа, сбора статистики онлайн пользователей. Например,\nв обработчике для [[yii\\web\\User::EVENT_AFTER_LOGIN|EVENT_AFTER_LOGIN]] вы можете сделать запись о времени и IP\nадресе входа в таблицу `user`.\n"
  },
  {
    "path": "docs/guide-ru/security-authorization.md",
    "content": "Авторизация\n===========\n\n> Note: этот раздел находится на стадии разработки.\n\nАвторизация — это процесс проверки того, что пользователь имеет достаточно прав, чтобы выполнить какие-то действия. Yii предоставляет два метода авторизации: фильтры контроля доступа (ACF) и контроль доступа на основе ролей (RBAC).\n\n\nФильтры контроля доступа\n------------------------\n\nФильтры контроля доступа (ACF) являются простым методом, который лучше всего использовать в приложениях с простым\nконтролем доступа. Как видно из их названия, ACF — это фильтры, которые могут присоединяться к контроллеру\nили модулю как поведение. ACF проверяет набор [[yii\\filters\\AccessControl::rules|правил доступа]], чтобы убедиться,\nчто пользователь имеет доступ к запрошенному действию.\n\nКод ниже показывает, как использовать ACF фильтр, реализованный в [[yii\\filters\\AccessControl]]:\n\n```php\nuse yii\\filters\\AccessControl;\n\nclass SiteController extends Controller\n{\n    public function behaviors()\n    {\n        return [\n            'access' => [\n                'class' => AccessControl::class,\n                'only' => ['login', 'logout', 'signup'],\n                'rules' => [\n                    [\n                        'allow' => true,\n                        'actions' => ['login', 'signup'],\n                        'roles' => ['?'],\n                    ],\n                    [\n                        'allow' => true,\n                        'actions' => ['logout'],\n                        'roles' => ['@'],\n                    ],\n                ],\n            ],\n        ];\n    }\n    // ...\n}\n```\n\nКод выше показывает ACF фильтр, связанный с контроллером `site` через поведение. Это типичный способ использования фильтров действий.\nПараметр `only` указывает, что фильтр ACF нужно применять только к действиям `login`, `logout` и `signup`.\nПараметр `rules` задаёт [[yii\\filters\\AccessRule|правила доступа]], которые означают следующее:\n\n- Разрешить всем гостям (ещё не прошедшим авторизацию) доступ к действиям `login` и `signup`.\n  Опция `roles` содержит знак вопроса `?`, это специальный токен обозначающий \"гостя\".\n- Разрешить аутентифицированным пользователям доступ к действию `logout`. Символ `@` — это другой специальный токен,\n  обозначающий аутентифицированного пользователя.\n\nКогда фильтр ACF проводит проверку авторизации, он проверяет правила по одному сверху вниз, пока не найдёт совпадение.\nЗначение опции `allow` выбранного правила указывает, авторизовывать пользователя или нет. Если ни одно из правил\nне совпало, то пользователь считается НЕавторизованным, и фильтр ACF останавливает дальнейшее выполнение действия.\n\nПо умолчанию, когда у пользователя отсутствует доступ к текущему действию, ACF делает следующее:\n\n* Если пользователь гость, вызывается [[yii\\web\\User::loginRequired()]], который перенаправляет браузер на страницу входа.\n* Если пользователь авторизован, генерируется исключение [[yii\\web\\ForbiddenHttpException]].\n\nВы можете переопределить это поведение, настроив свойство [[yii\\filters\\AccessControl::denyCallback]]:\n\n```php\n[\n    'class' => AccessControl::class,\n    'denyCallback' => function ($rule, $action) {\n        throw new \\Exception('У вас нет доступа к этой странице');\n    }\n]\n```\n\n[[yii\\filters\\AccessRule|Правила доступа]] поддерживают набор свойств. Ниже дано краткое описание поддерживаемых опций.\nВы также можете расширить [[yii\\filters\\AccessRule]], чтобы создать свой собственный класс правил доступа.\n\n * [[yii\\filters\\AccessRule::allow|allow]]: задаёт какое это правило, \"allow\" или \"deny\".\n\n * [[yii\\filters\\AccessRule::actions|actions]]: задаёт действия, соответствующие этому правилу.\nЗначение должно быть массивом идентификаторов действий. Сравнение — регистрозависимо. Если свойство пустое или не задано,\nто правило применяется ко всем действиям.\n\n * [[yii\\filters\\AccessRule::controllers|controllers]]: задаёт контроллеры, которым соответствует правило.\nЗначение должно быть массивом с идентификаторами контроллеров. Сравнение регистрозависимо. Если свойство\nпустое или не задано, то правило применяется ко всем контроллерам.\n\n * [[yii\\filters\\AccessRule::roles|roles]]: задаёт роли пользователей, соответствующих этому правилу.\n   Распознаются две специальные роли, которые проверяются с помощью [[yii\\web\\User::isGuest]]:\n\n     - `?`: соответствует гостевому пользователю (не аутентифицирован),\n     - `@`: соответствует аутентифицированному пользователю.\n\n   Использование других имён ролей будет приводить к вызову метода [[yii\\web\\User::can()]], который требует включения\n   RBAC (будет описано дальше). Если свойство пустое или не задано, то правило применяется ко всем ролям.\n\n * [[yii\\filters\\AccessRule::ips|ips]]: задаёт [[yii\\web\\Request::userIP|IP адреса пользователей]], для которых применяется это правило. IP адрес может содержать `*` в конце, так чтобы он соответствовал IP адресу с таким же префиксом.\nДля примера, '192.168.*' соответствует всем IP адресам в сегменте '192.168.'. Если свойство пустое или не задано,\nто правило применяется ко всем IP адресам.\n\n * [[yii\\filters\\AccessRule::verbs|verbs]]: задаёт http метод (например, `GET`, `POST`), соответствующий правилу.\nСравнение — регистронезависимо.\n\n * [[yii\\filters\\AccessRule::matchCallback|matchCallback]]: задаёт PHP колбек, который вызывается для определения,\nчто правило должно быть применено.\n\n * [[yii\\filters\\AccessRule::denyCallback|denyCallback]]: задаёт PHP колбек, который будет вызван, если доступ будет\nзапрещён при вызове этого правила.\n\nНиже показан пример, показывающий использование опции `matchCallback`, которая позволяет писать произвольную\nлогику проверки доступа:\n\n```php\nuse yii\\filters\\AccessControl;\n\nclass SiteController extends Controller\n{\n    public function behaviors()\n    {\n        return [\n            'access' => [\n                'class' => AccessControl::class,\n                'only' => ['special-callback'],\n                'rules' => [\n                    [\n                        'actions' => ['special-callback'],\n                        'allow' => true,\n                        'matchCallback' => function ($rule, $action) {\n                            return date('d-m') === '31-10';\n                        }\n                    ],\n                ],\n            ],\n        ];\n    }\n\n    // Колбек сработал! Эта страница может быть отображена только 31-ого октября\n    public function actionSpecialCallback()\n    {\n        return $this->render('happy-halloween');\n    }\n}\n```\n\n\nКонтроль доступа на основе ролей (RBAC)\n---------------------------------------\n\nУправление доступом на основе ролей (RBAC) обеспечивает простой, но мощный централизованный контроль доступа.\nПожалуйста, обратитесь к [Wikipedia](https://ru.wikipedia.org/wiki/%D0%A3%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B4%D0%BE%D1%81%D1%82%D1%83%D0%BF%D0%BE%D0%BC_%D0%BD%D0%B0_%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%B5_%D1%80%D0%BE%D0%BB%D0%B5%D0%B9)\nдля получения информации о сравнении RBAC с другими, более традиционными, системами контроля доступа.\n\nYii реализует общую иерархическую RBAC, следуя [NIST RBAC model](https://csrc.nist.gov/CSRC/media/Publications/conference-paper/1992/10/13/role-based-access-controls/documents/ferraiolo-kuhn-92.pdf).\nОбеспечивается функциональность RBAC через [компонент приложения](structure-application-components.md) [[yii\\rbac\\ManagerInterface|authManager]].\n\nИспользование RBAC состоит из двух частей. Первая часть — это создание RBAC данных авторизации, и вторая часть — это\nиспользование данных авторизации для проверки доступа в том месте, где это нужно.\n\nДля облегчения последующего описания, мы сначала введём некоторые основные понятия RBAC.\n\n\n### Основные концепции\n\nРоль представляет собой набор разрешений (*permissions*) (например, создание сообщения, обновление сообщения).\nРоль может быть назначена на одного или многих пользователей. Чтобы проверить, имеет ли пользователь указанные\nразрешения, мы должны проверить, назначена ли пользователю роль, которая содержит данное разрешение.\n\nС каждой ролью или разрешением может быть связано правило (*rule*). Правило представляет собой кусок кода, который будет\nвыполняться в ходе проверки доступа для определения может ли быть применена соответствующая роль или разрешение\nк текущему пользователю. Например, разрешение \"обновление поста\" может иметь правило, которое проверяет является ли\nтекущий пользователь автором поста. Во время проверки доступа, если пользователь не является автором поста, он/она будет\nсчитаться не имеющими разрешения \"обновление поста\".\n\nИ роли, и разрешения могут быть организованы в иерархию. В частности, роль может содержать другие роли или разрешения; и\nразрешения могут содержать другие разрешения. Yii реализует *частично упорядоченную* иерархию, которая включает в себя\nспециальные *деревья* иерархии. Роль может содержать разрешение, но обратное не верно.\n\n\n### Настройка RBAC Manager\n\nПеред определением авторизационных данных и проверкой прав доступа, мы должны настроить компонент приложения\n[[yii\\base\\Application::authManager|authManager]]. Yii предоставляет два типа менеджеров авторизации:\n[[yii\\rbac\\PhpManager]] и [[yii\\rbac\\DbManager]]. Первый использует файл с PHP скриптом для хранения данных авторизации,\nвторой сохраняет данные в базе данных. Вы можете использовать первый, если ваше приложение не требует слишком динамичного\nуправления ролями и разрешениями.\n\n#### Настройка authManager с помощью `PhpManager`\n\nСледующий код показывает как настроить в конфигурации приложения `authManager` с использованием класса [[yii\\rbac\\PhpManager]]:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'authManager' => [\n            'class' => 'yii\\rbac\\PhpManager',\n        ],\n        // ...\n    ],\n];\n```\n\nТеперь `authManager` может быть доступен через `\\Yii::$app->authManager`.\n\n> Замечание: По умолчанию, [[yii\\rbac\\PhpManager]] сохраняет данные RBAC в файлах в директории `@app/rbac/`. Убедитесь\n  что данная директория и файлы в них доступны для записи Web серверу, если иерархия разрешений должна меняться онлайн.\n\n#### Настройка authManager с помощью `DbManager`\n\nСледующий код показывает как настроить в конфигурации приложения `authManager` с использованием класса [[yii\\rbac\\DbManager]]:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'authManager' => [\n            'class' => 'yii\\rbac\\DbManager',\n        ],\n        // ...\n    ],\n];\n```\n\n> Примечание: Если вы используете шаблон проекта basic, компонент `authManager` необходимо настроить как в `config/web.php`, так и в\n> [конфигурации консольного приложения](tutorial-console.md#configuration) `config/console.php`.\n> При использовании шаблона проекта advanced `authManager` достаточно настроить единожды в `common/config/main.php`.\n\n`DbManager` использует четыре таблицы для хранения данных:\n\n- [[yii\\rbac\\DbManager::$itemTable|itemTable]]: таблица для хранения авторизационных элементов. По умолчанию \"auth_item\".\n- [[yii\\rbac\\DbManager::$itemChildTable|itemChildTable]]: таблица для хранения иерархии элементов. По умолчанию \"auth_item_child\".\n- [[yii\\rbac\\DbManager::$assignmentTable|assignmentTable]]: таблица для хранения назначений элементов авторизации. По умолчанию \"auth_assignment\".\n- [[yii\\rbac\\DbManager::$ruleTable|ruleTable]]: таблица для хранения правил. По умолчанию \"auth_rule\".\n\nПрежде чем вы начнёте использовать этот менеджер, вам нужно создать таблицы в базе данных. Чтобы сделать это,\nвы можете использовать миграцию хранящуюся в файле `@yii/rbac/migrations`:\n\n`yii migrate --migrationPath=@yii/rbac/migrations`\n\nТеперь `authManager` может быть доступен через `\\Yii::$app->authManager`.\n\n### Создание данных авторизации\n\nДля создания данных авторизации нужно выполнить следующие задачи:\n\n- определение ролей и разрешений;\n- установка отношений между ролями и правами доступа;\n- определение правил;\n- связывание правил с ролями и разрешениями;\n- назначение ролей пользователям.\n\nВ зависимости от требований к гибкости авторизации перечисленные задачи могут быть выполнены разными путями.\n\nЕсли иерархия прав не меняется, и количество пользователей зафиксировано, вы можете создать\n[консольную команду](tutorial-console.md#create-command), которая будет единожды инициализировать данные\nчерез APIs, предоставляемое `authManager`:\n\n```php\n<?php\nnamespace app\\commands;\n\nuse Yii;\nuse yii\\console\\Controller;\n\nclass RbacController extends Controller\n{\n    public function actionInit()\n    {\n        $auth = Yii::$app->authManager;\n\n        // добавляем разрешение \"createPost\"\n        $createPost = $auth->createPermission('createPost');\n        $createPost->description = 'Create a post';\n        $auth->add($createPost);\n\n        // добавляем разрешение \"updatePost\"\n        $updatePost = $auth->createPermission('updatePost');\n        $updatePost->description = 'Update post';\n        $auth->add($updatePost);\n\n        // добавляем роль \"author\" и даём роли разрешение \"createPost\"\n        $author = $auth->createRole('author');\n        $auth->add($author);\n        $auth->addChild($author, $createPost);\n\n        // добавляем роль \"admin\" и даём роли разрешение \"updatePost\"\n        // а также все разрешения роли \"author\"\n        $admin = $auth->createRole('admin');\n        $auth->add($admin);\n        $auth->addChild($admin, $updatePost);\n        $auth->addChild($admin, $author);\n\n        // Назначение ролей пользователям. 1 и 2 это IDs возвращаемые IdentityInterface::getId()\n        // обычно реализуемый в модели User.\n        $auth->assign($author, 2);\n        $auth->assign($admin, 1);\n    }\n}\n```\n\n> Note: Если вы используете шаблон проекта advanced, `RbacController` необходимо создать в директории `console/controllers`\n  и сменить пространство имён на `console\\controllers`.\n\nПосле выполнения команды `yii rbac/init` мы получим следующую иерархию:\n\n![Простая иерархия RBAC](images/rbac-hierarchy-1.png \"Простая иерархия RBAC\")\n\nАвтор может создавать пост, администратор может обновлять пост и делать всё, что может делать автор.\n\nЕсли ваше приложение позволяет регистрировать пользователей, то вам необходимо сразу назначать роли этим новым пользователям.\nНапример, для того, чтобы все вошедшие пользователи могли стать авторами в расширенном шаблоне проекта, вы должны изменить\n`frontend\\models\\SignupForm::signup()` как показано ниже:\n\n```php\npublic function signup()\n{\n    if ($this->validate()) {\n        $user = new User();\n        $user->username = $this->username;\n        $user->email = $this->email;\n        $user->setPassword($this->password);\n        $user->generateAuthKey();\n        $user->save(false);\n\n        // нужно добавить следующие три строки:\n        $auth = Yii::$app->authManager;\n        $authorRole = $auth->getRole('author');\n        $auth->assign($authorRole, $user->getId());\n\n        return $user;\n    }\n\n    return null;\n}\n```\n\nДля приложений, требующих комплексного контроля доступа с динамически обновляемыми данными авторизации, существуют\nспециальные пользовательские интерфейсы (так называемые админ-панели), которые могут быть разработаны с\nиспользованием API, предлагаемого `authManager`.\n\n\n### Использование правил\n\nКак упомянуто выше, правила добавляют дополнительные ограничения на роли и разрешения. Правила — это классы, расширяющие\n[[yii\\rbac\\Rule]]. Они должны реализовывать метод [[yii\\rbac\\Rule::execute()|execute()]]. В иерархии, созданной нами ранее,\nавтор не может редактировать свой пост. Давайте исправим это. Сначала мы должны создать правило, проверяющее\nчто пользователь является автором поста:\n\n```php\nnamespace app\\rbac;\n\nuse yii\\rbac\\Rule;\n\n/**\n * Проверяем authorID на соответствие с пользователем, переданным через параметры\n */\nclass AuthorRule extends Rule\n{\n    public $name = 'isAuthor';\n\n    /**\n     * @param string|int $user the user ID.\n     * @param Item $item the role or permission that this rule is associated width.\n     * @param array $params parameters passed to ManagerInterface::checkAccess().\n     * @return bool a value indicating whether the rule permits the role or permission it is associated with.\n     */\n    public function execute($user, $item, $params)\n    {\n        return isset($params['post']) ? $params['post']->createdBy == $user : false;\n    }\n}\n```\n\nПравило выше проверяет, что `post` был создан `$user`. Мы создадим специальное разрешение `updateOwnPost` в команде,\nкоторую мы использовали ранее:\n\n```php\n$auth = Yii::$app->authManager;\n\n// add the rule\n$rule = new \\app\\rbac\\AuthorRule;\n$auth->add($rule);\n\n// добавляем разрешение \"updateOwnPost\" и привязываем к нему правило.\n$updateOwnPost = $auth->createPermission('updateOwnPost');\n$updateOwnPost->description = 'Update own post';\n$updateOwnPost->ruleName = $rule->name;\n$auth->add($updateOwnPost);\n\n// \"updateOwnPost\" будет использоваться из \"updatePost\"\n$auth->addChild($updateOwnPost, $updatePost);\n\n// разрешаем \"автору\" обновлять его посты\n$auth->addChild($author, $updateOwnPost);\n```\n\nТеперь мы имеем следующую иерархию:\n\n![Иерархия RBAC с правилом](images/rbac-hierarchy-2.png \"Иерархия RBAC с правилом\")\n\n### Проверка доступа\n\nС готовыми авторизационными данными проверка доступа — это просто вызов метода [[yii\\rbac\\ManagerInterface::checkAccess()]].\nТак как большинство проверок доступа относятся к текущему пользователю, для удобства Yii предоставляет сокращённый метод\n[[yii\\web\\User::can()]], который можно использовать как показано ниже:\n\n```php\nif (\\Yii::$app->user->can('createPost')) {\n    // create post\n}\n```\n\nЕсли текущий пользователь Jane с ID=1, мы начнём с `createPost` и попробуем добраться до `Jane`:\n\n![Проверка доступа](images/rbac-access-check-1.png \"Проверка доступа\")\n\nДля того чтобы проверить, может ли пользователь обновить пост, нам надо передать дополнительный параметр,\nнеобходимый для правила `AuthorRule`, описанного ранее:\n\n```php\nif (\\Yii::$app->user->can('updatePost', ['post' => $post])) {\n    // update post\n}\n```\n\nВот что происходит если текущим пользователем является John:\n\n![Проверка доступа](images/rbac-access-check-2.png \"Проверка доступа\")\n\nМы начинаем с `updatePost` и переходим к `updateOwnPost`. Для того чтобы это произошло, правило `AuthorRule` должно вернуть\n`true` при вызове метода `execute`. Метод получает `$params`, переданный при вызове метода `can`, значение которого равно\n`['post' => $post]`. Если всё правильно, мы увидим, что `author` привязан к John.\n\nВ случае Jane это немного проще, потому что она admin:\n\n![Проверка доступа](images/rbac-access-check-3.png \"Проверка доступа\")\n\nЕсть несколько способов реализовать авторизацию в контроллере. Если вам необходимы отдельные права на\nдобавление и удаление, то проверку стоит делать в каждом действии. Вы можете либо использовать условие выше в каждом\nметоде действия, либо использовать [[yii\\filters\\AccessControl]]:\n\n```php\npublic function behaviors()\n{\n    return [\n        'access' => [\n            'class' => AccessControl::class,\n            'rules' => [\n                [\n                    'allow' => true,\n                    'actions' => ['index'],\n                    'roles' => ['managePost'],\n                ],\n                [\n                    'allow' => true,\n                    'actions' => ['view'],\n                    'roles' => ['viewPost'],\n                ],\n                [\n                    'allow' => true,\n                    'actions' => ['create'],\n                    'roles' => ['createPost'],\n                ],\n                [\n                    'allow' => true,\n                    'actions' => ['update'],\n                    'roles' => ['updatePost'],\n                ],\n                [\n                    'allow' => true,\n                    'actions' => ['delete'],\n                    'roles' => ['deletePost'],\n                ],\n            ],\n        ],\n    ];\n}\n```\n\nЕсли права на все CRUD операции выдаются вместе, то лучшее решение в этом случае — завести одно разрешение\nвроде `managePost` и проверять его в [[yii\\web\\Controller::beforeAction()]].\n\n### Использование роли по умолчанию\n\nРоль по умолчанию — это роль, которая *неявно* присваивается *всем* пользователям. Вызов метода\n[[yii\\rbac\\ManagerInterface::assign()]] не требуется, и авторизационные данные не содержат информации о назначении.\n\nРоль по умолчанию обычно связывают с правилом, определяющим к какой роли принадлежит каждый пользователь.\n\nРоли по умолчанию обычно используются в приложениях, которые уже имеют какое-то описание ролей. Для примера, приложение\nможет иметь столбец \"group\" в таблице пользователей, и каждый пользователь принадлежит к какой-то группе. Если каждая\nгруппа может быть сопоставлена роли в модели RBAC, вы можете использовать роль по умолчанию для автоматического назначения\nкаждому пользователю роли RBAC. Давайте используем пример, чтобы понять как это можно сделать.\n\nПредположим что в таблице пользователей у вас есть столбец `group`, в котором значение 1 представляет группу \"администратор\",\nа 2 — группу \"автор\". Вы планируете иметь две RBAC роли: `admin` и `author`, представляющие разрешения для двух\nсоответствующих групп. Вы можете настроить данные роли как показано ниже.\n\n```php\nnamespace app\\rbac;\n\nuse Yii;\nuse yii\\rbac\\Rule;\n\n/**\n * Checks if user group matches\n */\nclass UserGroupRule extends Rule\n{\n    public $name = 'userGroup';\n\n    public function execute($user, $item, $params)\n    {\n        if (!Yii::$app->user->isGuest) {\n            $group = Yii::$app->user->identity->group;\n            if ($item->name === 'admin') {\n                return $group == 1;\n            } elseif ($item->name === 'author') {\n                return $group == 1 || $group == 2;\n            }\n        }\n        return false;\n    }\n}\n\n$auth = Yii::$app->authManager;\n\n$rule = new \\app\\rbac\\UserGroupRule;\n$auth->add($rule);\n\n$author = $auth->createRole('author');\n$author->ruleName = $rule->name;\n$auth->add($author);\n// ... add permissions as children of $author ...\n\n$admin = $auth->createRole('admin');\n$admin->ruleName = $rule->name;\n$auth->add($admin);\n$auth->addChild($admin, $author);\n// ... add permissions as children of $admin ...\n```\n\nОбратите внимание, так как \"author\" добавлен как дочерняя роль к \"admin\", следовательно, в реализации метода `execute()`\nкласса правила вы должны учитывать эту иерархию. Именно поэтому для роли \"author\" метод `execute()` вернёт истину,\nесли пользователь принадлежит к группам 1 или 2 (это означает, что пользователь находится в группе\nадминистраторов или авторов)\n\nДалее настроим `authManager` с помощью перечисления ролей в свойстве [[yii\\rbac\\BaseManager::$defaultRoles]]:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'authManager' => [\n            'class' => 'yii\\rbac\\PhpManager',\n            'defaultRoles' => ['admin', 'author'],\n        ],\n        // ...\n    ],\n];\n```\n\nТеперь, если вы выполните проверку доступа, для обоих ролей `admin` и `author` будет выполнена проверка правила,\nасоциированного с ними. Если правило вернёт истину, это будет означать, что роль применяется к текущему пользователю.\nНа основании правила, реализованного выше: если пользователь входит в группу 1, пользователю будет назначена роль `admin`;\nи если значение `group` равно 2, будет применена роль `author`.\n"
  },
  {
    "path": "docs/guide-ru/security-best-practices.md",
    "content": "Лучшие практики безопасности\n============================\n\nНиже мы рассмотрим основные принципы безопасности и опишем, как избежать угроз при разработке на Yii.\n\nОсновные принципы\n-----------------\n\nЕсть два основных принципа безопасности, независимо от того, какое приложение разрабатывается:\n\n1. Фильтрация ввода.\n2. Экранирование вывода.\n\n\n### Фильтрация ввода\n\nФильтрация ввода означает, что входные данные никогда не должны считаться безопасными и вы всегда должны проверять,\nявляются ли полученные данные допустимыми. Например, если мы знаем, что сортировка может быть осуществлена только\nпо трём полям `title`, `created_at` и `status`, и поле может передаваться через ввод пользователем, лучше проверить\nзначение там, где мы его получили:\n\n```php\n$sortBy = $_GET['sort'];\nif (!in_array($sortBy, ['title', 'created_at', 'status'])) {\n\tthrow new Exception('Invalid sort value.');\n}\n```\n\nВ Yii, вы, скорее всего, будете использовать [валидацию форм](input-validation.md), чтоб делать такие проверки.\n\n\n### Экранирование вывода\n\nЭкранирование вывода означает, что данные в зависимости от контекста должны экранироваться, например в контексте\nHTML вы должны экранировать `<`, `>` и похожие специальные символы. В контексте JavaScript или SQL будет другой набор\nсимволов. Так как ручное экранирование чревато ошибками, Yii предоставляет различные утилиты для экранирования в\nразличных контекстах.\n\nКак избежать SQL-иньекций\n-------------------------\n\nSQL-иньекции происходят, когда текст запроса формируется склеиванием не экранированных строк, как показано ниже:\n\n```php\n$username = $_GET['username'];\n$sql = \"SELECT * FROM user WHERE username = '$username'\";\n```\n\nВместо того, чтобы подставлять корректное имя пользователя, злоумышленник может передать вам в приложение что-то вроде\n`'; DROP TABLE user; --`.\nВ результате SQL будет следующий:\n\n```sql\nSELECT * FROM user WHERE username = ''; DROP TABLE user; --'\n```\n\nЭто валидный запрос, который сначала будет искать пользователей с пустым именем, а затем удалит таблицу `user`.\nСкорее всего будет сломано приложение и будут потеряны данные (вы ведь делаете регулярное резервное копирование?).\n\nБольшинство запросов к базе данных в Yii происходит через [Active Record](db-active-record.md), который правильно\nиспользует подготовленные запросы PDO внутри. При использовании подготовленных запросов невозможно манипулировать\nзапросом как это показано выше.\n\nТем не менее, иногда нужны [сырые запросы](db-dao.md) или [построитель запросов](db-query-builder.md). В этом случае\nвы должны использовать безопасные способы передачи данных. Если данные используются для сравнения со значением\nстолбцов предпочтительнее использовать подготовленные запросы:\n\n```php\n// query builder\n$userIDs = (new Query())\n    ->select('id')\n    ->from('user')\n    ->where('status=:status', [':status' => $status])\n    ->all();\n\n// DAO\n$userIDs = $connection\n    ->createCommand('SELECT id FROM user where status=:status')\n    ->bindValues([':status' => $status])\n    ->queryColumn();\n```\n\nЕсли данные используются в качестве имён столбцов или таблиц, то лучший путь - это разрешить только предопределённый\nнабор значений:\n \n```php\nfunction actionList($orderBy = null)\n{\n    if (!in_array($orderBy, ['name', 'status'])) {\n        throw new BadRequestHttpException('Only name and status are allowed to order by.')\n    }\n    \n    // ...\n}\n```\n\nЕсли это невозможно, то имена столбцов и таблиц должны экранироваться. Yii использует специальный синтаксис\nдля экранирования для всех поддерживаемых баз данных:\n\n```php\n$sql = \"SELECT COUNT([[$column]]) FROM {{table}}\";\n$rowCount = $connection->createCommand($sql)->queryScalar();\n```\n\nВы можете получить подробную информацию о синтаксисе в [Экранирование имён таблиц и столбцов](db-dao.md#quoting-table-and-column-names).\n\n\nКак избежать XSS\n----------------\n\nXSS или кросс-сайтинговый скриптинг становится возможен, когда не экранированный выходной HTML попадает в браузер.\nНапример, если пользователь должен ввести своё имя, но вместо `Alexander` он вводит `<script>alert('Hello!');</script>`, то\nвсе страницы, которые его выводят без экранирования, будут выполнять JavaScript `alert('Hello!');`, и в результате\nбудет выводиться окно сообщения в браузере. В зависимости от сайта, вместо невинных скриптов с выводом всплывающего\nhello, злоумышленниками могут быть отправлены скрипты, похищающие личные данные пользователей сайта,\nлибо выполняющие операции от их имени.\n\nВ Yii избежать XSS легко. На месте вывода текста необходимо выбрать один из двух вариантов:\n\n1. Вы хотите вывести данные в виде обычного текста.\n2. Вы хотите вывести данные в виде HTML.\n\nЕсли вам нужно вывести простой текст, то экранировать лучше следующим образом:\n\n```php\n<?= \\yii\\helpers\\Html::encode($username) ?>\n```\n\nЕсли нужно вывести HTML, вам лучше воспользоваться HtmlPurifier:\n\n```php\n<?= \\yii\\helpers\\HtmlPurifier::process($description) ?>\n```\n\nОбратите внимание, что обработка с помощью HtmlPurifier довольно тяжела, так что неплохо бы задуматься о кешировании.\n\nКак избежать CSRF\n-----------------\n\nCSRF - это аббревиатура для межсайтинговой подмены запросов. Идея заключается в том, что многие приложения предполагают,\nчто запросы, приходящие от браузера, отправляются самим пользователем. Это может быть неправдой.\n\nНапример, сайт `an.example.com` имеет URL `/logout`, который, используя простой GET, разлогинивает пользователя. Пока\nэто запрос выполняется самим пользователем - всё в порядке, но в один прекрасный день злоумышленники размещают код\n`<img src=\"https://an.example.com/logout\">` на форуме с большой посещаемостью. Браузер не делает никаких отличий\nмежду запросом изображения и запросом страницы, так что когда пользователь откроет страницу с таким тегом `img`, браузер отправит GET запрос на указанный адрес, и пользователь будет разлогинен.\n\nВот основная идея. Можно сказать, что в разлогировании пользователя нет ничего серьёзного, но с помощью этой уязвимости, можно выполнять опасные операции. Представьте, что существует страница https://an.example.com/purse/transfer?to=anotherUser&amount=2000, обращение к которой с помощью GET запроса, приводит к перечислению 2000 единиц валюты со счета авторизированного пользователя на счет пользователя с логином anotherUser. Учитывая, что браузер для загрузки контента отправляет GET запросы, можно подумать, что разрешение на выполнение такой операции только POST запросом на 100% обезопасит от проблем. К сожалению, это не совсем правда. Учитывайте, что вместо тега <img>, злоумышленник может внедрить JavaScript код, который будет отправлять нужные POST запросы на этот URL.\n\nДля того, чтоб избежать CSRF вы должны всегда:\n\n1. Следуйте спецификациям HTTP, например запрос GET не должен менять состояние приложения.\n2. Держите защиту CSRF в Yii включенной.\n\n\nКак избежать нежелательного доступа к файлам\n--------------------------------------------\n\nПо умолчанию, webroot сервера указывает на каталог `web`, где лежит `index.php`. В случае использования виртуального\nхостинга, это может быть недостижимо, в конечном итоге весь код, конфиги и логи могут оказаться в webroot сервера.\n\nЕсли это так, то нужно запретить доступ ко всему, кроме директории `web`. Если на вашем хостинге такое невозможно,\nрассмотрите возможность смены хостинга.\n\nКак избежать вывода информации отладки и инструментов в рабочем режиме\n----------------------------------------------------------------------\n\nВ режиме отладки, Yii отображает довольно подробные ошибки, которые полезны во время разработки. Дело в том, что\nподробные ошибки удобны для нападающего, так как могут раскрыть структуру базы данных, параметров конфигурации и части\nвашего кода. Никогда не запускайте приложения в рабочем режиме с `YII_DEBUG` установленным в `true` в вашем `index.php`.\n\nВы никогда не должны включать Gii или Debug панель в рабочем режиме. Это может быть использованно для получения информации о структуре базы данных, кода и может позволить заменить файлы, генерируемые Gii автоматически.\n\nСледует избегать включения в рабочем режиме панели отладки, если только в этом нет острой необходимости.\nОна раскрывает всё приложение и детали конфигурации. Если Вам всё-таки нужно запустить панель отладки в рабочем режиме,\nпроверьте дважды, что доступ ограничен только вашими IP-адресами.\n\nДалее по теме читайте:\n\n- <https://owasp.org/www-project-.net/articles/Exception_Handling.md>\n- <https://owasp.org/www-pdf-archive/OWASP_Top_10_2007.pdf> (A6 - Information Leakage and Improper Error Handling)\n\n\nИспользование безопасного подключения через TLS\n-----------------------------------------------\n\nYii предоставляет функции, которые зависят от куки-файлов и/или сессий PHP. Они могут быть уязвимыми, если Ваше соединение\nскомпрометированно. Риск снижается, если приложение использует безопасное соединение через TLS (часто называемое как [SSL](https://ru.wikipedia.org/wiki/TLS)).\n\nИнструкции по настройке смотрите в документации к Вашему веб-серверу. Вы также можете проверить примеры конфигураций\nпредоставленные проектом H5BP:\n\n- [Nginx](https://github.com/h5bp/server-configs-nginx)\n- [Apache](https://github.com/h5bp/server-configs-apache).\n- [IIS](https://github.com/h5bp/server-configs-iis).\n- [Lighttpd](https://github.com/h5bp/server-configs-lighttpd).\n\n\nБезопасная конфигурация сервера\n-------------------------------\n\nЦель этого раздела - выявить риски, которые необходимо учитывать при создании\nконфигурации сервера для обслуживания веб-сайта на основе Yii. Помимо перечисленных здесь пунктов есть и\nдругие параметры, связанные с безопасностью, которые необходимо учитывать, поэтому не рассматривайте этот раздел как завершенный.\n\n### Как избежать атаки типа `Host`-header\n\nКлассы типа [[yii\\web\\UrlManager]] и [[yii\\helpers\\Url]] могут использовать [[yii\\web\\Request::getHostInfo()|запрашиваемое имя хоста]]] для генерации ссылок. Если веб-сервер настроен на обслуживание одного и того же сайта независимо от значения заголовка `Host`, эта информация может быть ненадежной и может быть подделана пользователем, отправляющим HTTP-запрос. В таких ситуациях Вы должны либо исправить конфигурацию своего веб-сервера, чтобы обслуживать сайт только для указанных имен узлов, либо явно установить или отфильтровать значение, установив свойство [[yii\\web\\Request::setHostInfo()|hostInfo]] компонента приложения `request`.\n\nДополнительные сведения о конфигурации сервера смотрите в документации Вашего веб-сервера:\n\n- Apache 2: <https://httpd.apache.org/docs/trunk/vhosts/examples.html#defaultallports>\n- Nginx: <https://www.nginx.com/resources/wiki/start/topics/examples/server_blocks/>\n\nЕсли у Вас нет доступа к конфигурации сервера, Вы можете настроить фильтр [[yii\\filters\\HostControl]] уровня приложения для защиты от такого рода атак:\n\n```php\n// Файл конфигурации веб-приложения\nreturn [\n    'as hostControl' => [\n        'class' => 'yii\\filters\\HostControl',\n        'allowedHosts' => [\n            'example.com',\n            '*.example.com',\n        ],\n        'fallbackHostInfo' => 'https://example.com',\n    ],\n    // ...\n];\n```\n\n> Note: Вы всегда должны предпочесть конфигурацию веб-сервера для защиты от `атак заголовков узла` вместо использования фильтра.\n    [[yii\\filters\\HostControl]] следует использовать, только если настройка конфигурации сервера недоступна.\n"
  },
  {
    "path": "docs/guide-ru/security-cryptography.md",
    "content": "Криптография\n============\n\nВ этом разделе мы рассмотрим следующие аспекты безопасности:\n\n- Генерация случайных данных\n- Шифрование и дешифрование\n- Подтверждение целостности данных\n\nГенерация псевдослучайных данных\n----------------------------\n\nПсевдослучайные данные полезны во многих ситуациях. Например, при сбросе пароля по электронной почте вам необходимо сгенерировать токен, сохранить его в базе данных и отправить его по электронной почте конечному пользователю, что, в свою очередь, позволит ему подтвердить право собственности на эту учетную запись. Очень важно, чтобы этот токен был уникальным и его было трудно угадать, иначе есть вероятность, что злоумышленник сможет предсказать значение токена и сбросить пароль пользователя.\n\nПомощник по безопасности Yii упрощает генерацию псевдослучайных данных:\n\n```php\n$key = Yii::$app->getSecurity()->generateRandomString();\n```\n\nШифрование и дешифрование\n-------------------------\n\nYii предоставляет удобные вспомогательные функции, которые позволяют шифровать или расшифровывать данные с помощью секретного ключа. Данные передаются через функцию шифрования, так что только человек, у которого есть секретный ключ, сможет расшифровать его.\nНапример, нам нужно сохранить некоторую информацию в нашей базе данных, но мы должны убедиться, что только пользователь, у которого есть секретный ключ, может ее просмотреть (даже если база данных приложения скомпрометирована):\n\n```php\n// $data и $secretKey получены из формы\n$encryptedData = Yii::$app->getSecurity()->encryptByPassword($data, $secretKey);\n// $encryptedData хранится в БД\n```\n\nВпоследствии, когда пользователь хочет прочитать данные:\n\n```php\n// $secretKey введен пользователем, $encryptedData получен из БД\n$data = Yii::$app->getSecurity()->decryptByPassword($encryptedData, $secretKey);\n```\n\nКроме того, можно использовать ключ вместо пароля через [[\\yii\\base\\Security::encryptByKey()]] и [[\\yii\\base\\Security::decryptByKey()]].\n\nПодтверждение целостности данных\n-------------------------\n\nЕсть ситуации, в которых вам нужно убедиться, что ваши данные не были изменены третьей стороной или даже повреждены каким-либо образом. Yii обеспечивает простой способ для подтверждения целостности данных в виде двух вспомогательных функций.\n\nПрефикс данных генерируются из секретного ключа и данных:\n\n```php\n// $secretKey нашего приложения или пользователя, $genuineData полученые из надежного источника\n$data = Yii::$app->getSecurity()->hashData($genuineData, $secretKey);\n```\n\nПроверка, нарушена ли целостность данных:\n\n```php\n// $secretKey - нашего приложения или пользователя, $data - данные полученные из ненадежного источника\n$data = Yii::$app->getSecurity()->validateData($data, $secretKey);\n```\n"
  },
  {
    "path": "docs/guide-ru/security-overview.md",
    "content": "Безопасность\n========\nХорошая безопасность жизненно важна для успеха любого приложения. К сожалению, многие разработчики не уделяют ей должного внимания. Происходит это либо из-за недостатка понимания, либо из-за того, что это слишком сложно. Чтобы сделать ваше приложение максимально безопасным, в Yii включено несколько превосходных и простых в использовании функций безопасности.\n\n* [Аутентификация](security-authentication.md)\n* [Авторизация](security-authorization.md)\n* [Работа с паролями](security-passwords.md)\n* [Криптография](security-cryptography.md)\n* [Безопасность видов](structure-views.md#security)\n* [Клиенты авторизации](https://github.com/yiisoft/yii2-authclient/blob/master/docs/guide/README.md)\n* [Лучшие практики](security-best-practices.md)\n* [Доверенные прокси и заголовки](runtime-requests.md#trusted-proxies)"
  },
  {
    "path": "docs/guide-ru/security-passwords.md",
    "content": "Работа с паролями\n=================\n\nМногие разработчики знают, что хранить пароль открытым текстом нельзя, но многие до сих пор считают безопасным\nиспользование для хеширования паролей `md5` или `sha1`. Раньше упомянутых алгоритмов было достаточно, но современное\nоборудование позволяет подобрать эти хеши очень быстро, методом простого перебора.\n\nДля того, чтобы обеспечить повышенную безопасность паролей ваших пользователей даже в худшем случае (ваше\nприложение взломано), нужно использовать алгоритм шифрования, устойчивый к атаке перебором. Лучший вариант в текущий\nмомент `bcrypt`. В PHP вы можете использовать хеши `bcrypt` через [функцию crypt](https://www.php.net/manual/ru/function.crypt.php).\nYii обеспечивает две вспомогательные функции, которые упрощают использование функции `crypt` для генерации и проверки\nпароля.\n\nКогда пользователь задаёт пароль (например во время регистрации), пароль должен быть захеширован:\n\n\n```php\n$hash = Yii::$app->getSecurity()->generatePasswordHash($password);\n```\n\nХеш можно связать с соответствующим атрибутом модели, так чтобы он сохранялся в базе для последующего использования.\n\nКогда пользователь попытается войти, отправленный пароль должен быть хеширован и сравнён с ранее сохранённым хешем:\n\n```php\nif (Yii::$app->getSecurity()->validatePassword($password, $hash)) {\n    // всё хорошо, пользователь может войти\n} else {\n    // неправильный пароль\n}\n```\n"
  },
  {
    "path": "docs/guide-ru/start-databases.md",
    "content": "Работа с базами данных\n======================\n\nЭтот раздел расскажет о том, как создать новую страницу, отображающую данные по странам, полученные из таблицы `countries` базы данных. Для достижения этой цели вам будет необходимо настроить подключение к базе данных, создать класс [Active Record](db-active-record.md), определить [action](structure-controllers.md) и создать [view](structure-views.md).\n\nИзучив эту часть, вы научитесь:\n\n* Настраивать подключение к БД.\n* Определять класс Active Record.\n* Запрашивать данные, используя класс Active Record.\n* Отображать данные во view с использованием пагинации.\n\nОбратите внимание, чтобы усвоить этот раздел, вы должны иметь базовые знания и навыки использования баз данных. \nВ частности, вы должны знать, как создать базу данных и как выполнять SQL запросы, используя клиентские инструменты для работы с БД.\n\nПодготавливаем базу данных <span id=\"preparing-database\"></span>\n----------------------------------------------------------------\n\nДля начала создайте базу данных под названием `yii2basic`, из которой вы будете получать данные в вашем приложении.\nВы можете создать базу данных SQLite, MySQL, PostgreSQL, MSSQL или Oracle, так как Yii имеет встроенную поддержку для многих баз данных. Для простоты, в дальнейшем описании будет подразумеваться MySQL.\n\nПосле этого создайте в базе данных таблицу `country` и добавьте в неё немного демонстрационных данных. Вы можете запустить следующую SQL инструкцию, чтобы сделать это:\n\n```sql\nCREATE TABLE `country` (\n  `code` CHAR(2) NOT NULL PRIMARY KEY,\n  `name` CHAR(52) NOT NULL,\n  `population` INT(11) NOT NULL DEFAULT '0'\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nINSERT INTO `country` VALUES ('AU','Australia',24016400);\nINSERT INTO `country` VALUES ('BR','Brazil',205722000);\nINSERT INTO `country` VALUES ('CA','Canada',35985751);\nINSERT INTO `country` VALUES ('CN','China',1375210000);\nINSERT INTO `country` VALUES ('DE','Germany',81459000);\nINSERT INTO `country` VALUES ('FR','France',64513242);\nINSERT INTO `country` VALUES ('GB','United Kingdom',65097000);\nINSERT INTO `country` VALUES ('IN','India',1285400000);\nINSERT INTO `country` VALUES ('RU','Russia',146519759);\nINSERT INTO `country` VALUES ('US','United States',322976000);\n```\n\nНа данный момент у вас есть база данных под названием `yii2basic` и внутри неё таблица `country` с тремя столбцами, содержащими десять строк данных.\n\nНастраиваем подключение к БД <span id=\"configuring-db-connection\"></span>\n-------------------------------------------------------------------------\n\nПеред продолжением убедитесь, что у вас установлены PHP-расширение [PDO](https://www.php.net/manual/ru/book.pdo.php) и драйвер PDO для используемой вами базы данных (например, `pdo_mysql` для MySQL). Это базовое требование в случае использования вашим приложением реляционной базы данных.\nПосле того, как они установлены, откройте файл `config/db.php` и измените параметры на верные для вашей базы данных. По умолчанию этот файл содержит следующее:\n\n```php\n<?php\n\nreturn [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=localhost;dbname=yii2basic',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n];\n```\n\nФайл `config/db.php` — типичный [конфигурационный](concept-configurations.md) инструмент, базирующийся на файлах. Данный конфигурационный файл определяет параметры, необходимые для создания и инициализации экземпляра [[yii\\db\\Connection]], через который вы можете делать SQL запросы к подразумеваемой базе данных.\n\nПодключение к БД, настроенное выше, доступно в коде приложения через выражение `Yii::$app->db`.\n\n> Info: файл `config/db.php` будет подключен главной конфигурацией приложения `config/web.php`,\n  описывающей то, как экземпляр [приложения](structure-applications.md) должен быть инициализирован.\n  Для детальной информации, пожалуйста, обратитесь к разделу [Конфигурации](concept-configurations.md).\n\nЕсли вам необходимо работать с базами данных, поддержка которых не включена непосредственно в фреймворк, стоит обратить\nвнимание на следующие расширения:\n\n- [Informix](https://github.com/edgardmessias/yii2-informix)\n- [IBM DB2](https://github.com/edgardmessias/yii2-ibm-db2)\n- [Firebird](https://github.com/edgardmessias/yii2-firebird)\n\n\nСоздаём потомка Active Record <span id=\"creating-active-record\"></span>\n-----------------------------------------------------------------------\n\nЧтобы представлять и получать данные из таблицы `country`, создайте класс — потомок [Active Record](db-active-record.md), под названием `Country` и сохраните его в файле `models/Country.php`.\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass Country extends ActiveRecord\n{\n}\n```\n\nКласс `Country` наследуется от [[yii\\db\\ActiveRecord]]. Вам не нужно писать ни строчки кода внутри него! С кодом, приведённым выше, Yii свяжет имя таблицы с именем класса.\n\n> Info: Если нет возможности задать прямую зависимость между именем таблицы и именем класса, вы можете переопределить\n  метод [[yii\\db\\ActiveRecord::tableName()]], чтобы явно задать имя связанной таблицы.\n\nИспользуя класс `Country`, вы можете легко манипулировать данными в таблице `country`, как показано в этих фрагментах:\n\n```php\nuse app\\models\\Country;\n\n// получаем все строки из таблицы \"country\" и сортируем их по \"name\"\n$countries = Country::find()->orderBy('name')->all();\n\n// получаем строку с первичным ключом \"US\"\n$country = Country::findOne('US');\n\n// отобразит \"United States\"\necho $country->name;\n\n// меняем имя страны на \"U.S.A.\" и сохраняем в базу данных\n$country->name = 'U.S.A.';\n$country->save();\n```\n\n> Info: Active Record — мощный способ доступа и манипулирования данными БД в объектно-ориентированном стиле.\n  Вы можете найти подробную информацию в разделе [Active Record](db-active-record.md). В качестве альтернативы, вы также можете взаимодействовать с базой данных, используя более низкоуровневый способ доступа, называемый [Data Access Objects](db-dao.md).\n\n\nСоздаём Action <span id=\"creating-action\"></span>\n-------------------------------------------------\n\nДля того, чтобы показать данные по странам конечным пользователям, вам надо создать новый action. Вместо размещения нового action'a в контроллере `site`, как вы делали в предыдущих разделах, будет иметь больше смысла создать новый контроллер специально для всех действий, относящихся к данным по странам. Назовите новый контроллер `CountryController`, и создайте action `index` внутри него, как показано ниже.\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\nuse yii\\data\\Pagination;\nuse app\\models\\Country;\n\nclass CountryController extends Controller\n{\n    public function actionIndex()\n    {\n        $query = Country::find();\n\n        $pagination = new Pagination([\n            'defaultPageSize' => 5,\n            'totalCount' => $query->count(),\n        ]);\n\n        $countries = $query->orderBy('name')\n            ->offset($pagination->offset)\n            ->limit($pagination->limit)\n            ->all();\n\n        return $this->render('index', [\n            'countries' => $countries,\n            'pagination' => $pagination,\n        ]);\n    }\n}\n```\n\nСохраните код выше в файле `controllers/CountryController.php`.\n\nAction `index` вызывает `Country::find()`. Данный метод Active Record строит запрос к БД и извлекает все данные из таблицы `country`.\nЧтобы ограничить количество стран, возвращаемых каждым запросом, запрос разбивается на страницы с помощью объекта [[yii\\data\\Pagination]]. Объект `Pagination` служит двум целям:\n* Устанавливает пункты `offset` и `limit` для SQL инструкции, представленной запросом, чтобы она возвращала только одну страницу данных за раз (в нашем случае максимум 5 строк на страницу).\n* Он используется во view для отображения пагинатора, состоящего из набора кнопок с номерами страниц, это будет разъяснено в следующем подразделе.\n\nВ конце кода action `index` выводит view с именем `index`, и передаёт в него данные по странам вместе c информацией о пагинации.\n\nСоздаём View <span id=\"creating-view\"></span>\n---------------------------------------------\n\nПервым делом создайте поддиректорию с именем `country` внутри директории `views`. Эта папка будет использоваться для хранения всех view, выводимых контроллером `country`. Внутри директории `views/country` создайте файл с именем `index.php`, содержащий следующий код:\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\LinkPager;\n?>\n<h1>Countries</h1>\n<ul>\n<?php foreach ($countries as $country): ?>\n    <li>\n        <?= Html::encode(\"{$country->code} ({$country->name})\") ?>:\n        <?= $country->population ?>\n    </li>\n<?php endforeach; ?>\n</ul>\n\n<?= LinkPager::widget(['pagination' => $pagination]) ?>\n```\n\nView имеет 2 части относительно отображения данных по странам. В первой части предоставленные данные по странам выводятся как неупорядоченный HTML-список.\nВо второй части выводится виджет [[yii\\widgets\\LinkPager]], используя информацию о пагинации, переданную из action во view. Виджет `LinkPager` отображает набор постраничных кнопок. Клик по любой из них обновит данные по странам в соответствующей странице.\n\n\nИспытываем в действии <span id=\"trying-it-out\"></span>\n------------------------------------------------------\n\nЧтобы увидеть, как работает весь вышеприведённый код, перейдите по следующей ссылке в своём браузере:\n\n```\nhttps://hostname/index.php?r=country%2Findex\n```\n\n![Список Стран](images/start-country-list.png)\n\nВ начале вы увидите страницу, показывающую пять стран. Под странами вы увидите пагинатор с четырьмя кнопками. Если вы кликните по кнопке \"2\", то увидите страницу, отображающую другие пять стран из базы данных: вторая страница записей.\nПосмотрев внимательней, вы увидите, что URL в браузере тоже сменилось на\n\n```\nhttps://hostname/index.php?r=country%2Findex&page=2\n```\n\nЗа кадром [[yii\\data\\Pagination|Pagination]] предоставляет всю необходимую функциональность для постраничной разбивки набора данных:\n* В начале [[yii\\data\\Pagination|Pagination]] показывает первую страницу, которая отражает SELECT запрос стран с параметрами `LIMIT 5 OFFSET 0`. Как результат, первые пять стран будут получены и отображены.\n* Виджет [[yii\\widgets\\LinkPager|LinkPager]] выводит кнопки страниц используя URL'ы, созданные [[yii\\data\\Pagination::createUrl()|Pagination]]. Эти URL'ы будут содержать параметр запроса `page`, который представляет различные номера страниц.\n* Если вы кликните по кнопке \"2\", сработает и обработается новый запрос для маршрута `country/index`. Таким образом новый запрос стран будет иметь параметры `LIMIT 5 OFFSET 5` и вернет следующие пять стран для отображения.\n\nЗаключение <span id=\"summary\"></span>\n-------------------------------------\n\nВ этом разделе вы научились работать с базой данных. Также вы научились получать и отображать данные с постраничной разбивкой с помощью [[yii\\data\\Pagination]] и [[yii\\widgets\\LinkPager]].\n\nВ следующем разделе вы научитесь использовать мощный инструмент генерации кода, называемый [Gii](start-gii.md), чтобы с его помощью быстро осуществлять некоторые часто используемые функции, такие как операции Create-Read-Update-Delete (CRUD) для работы с данными в таблице базы данных. На самом деле код, который вы только что написали, в Yii может быть полностью сгенерирован автоматически с использованием Gii.\n"
  },
  {
    "path": "docs/guide-ru/start-forms.md",
    "content": "Работа с формами\n================\n\nВ данном разделе мы обсудим получение данных от пользователя. На странице будет располагаться форма с полями для ввода\nимени и email. Полученные данные будут показаны на странице для их подтверждения.\n\nЧтобы достичь этой цели, помимо создания [действия](structure-controllers.md) и двух [представлений](structure-views.md)\nвы создадите [модель](structure-models.md).\n\nВ данном руководстве вы изучите:\n\n* Как создать [модель](structure-models.md) для данных, введённых пользователем;\n* Как объявить правила проверки введённых данных;\n* Как создать HTML-форму в [представлении](structure-views.md).\n\n\nСоздание модели <span id=\"creating-model\"></span>\n---------------------------------------------\n\nВ файле `models/EntryForm.php` создайте класс модели `EntryForm` как показано ниже. Он будет использоваться для\nхранения данных, введённых пользователем. Подробно об именовании файлов классов читайте в разделе\n«[Автозагрузка классов](concept-autoloading.md)».\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse yii\\base\\Model;\n\nclass EntryForm extends Model\n{\n    public $name;\n    public $email;\n\n    public function rules()\n    {\n        return [\n            [['name', 'email'], 'required'],\n            ['email', 'email'],\n        ];\n    }\n}\n```\n\nДанный класс расширяет класс [[yii\\base\\Model]], который является частью фреймворка и обычно используется для работы\nс данными форм.\n\nКласс содержит 2 публичных свойства `name` и `email`, которые используются для хранения данных, введённых пользователем.\nОн также содержит метод `rules()`, который возвращает набор правил проверки данных. Правила, объявленные в коде выше\nозначают следующее:\n\n* Поля `name` и `email` обязательны для заполнения;\n* В поле `email` должен быть правильный адрес email.\n\nЕсли объект `EntryForm` заполнен пользовательскими данными, то для их проверки вы можете вызвать метод\n[[yii\\base\\Model::validate()|validate()]]. В случае неудачной проверки свойство [[yii\\base\\Model::hasErrors|hasErrors]]\nстанет равным `true`. С помощью [[yii\\base\\Model::getErrors|errors]] можно узнать, какие именно ошибки возникли.\n\n\nСоздание действия <span id=\"creating-action\"></span>\n------------------------------------------------\n\nДалее создайте действие `entry` в контроллере `site`, точно так же, как вы делали это ранее.\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\nuse app\\models\\EntryForm;\n\nclass SiteController extends Controller\n{\n    // ...существующий код...\n\n    public function actionEntry()\n    {\n        $model = new EntryForm();\n\n        if ($model->load(Yii::$app->request->post()) && $model->validate()) {\n            // данные в $model удачно проверены\n\n            // делаем что-то полезное с $model ...\n \n            return $this->render('entry-confirm', ['model' => $model]);\n        } else {\n            // либо страница отображается первый раз, либо есть ошибка в данных\n            return $this->render('entry', ['model' => $model]);\n        }\n    }\n}\n```\n\nДействие создает объект `EntryForm`. Затем оно пытается заполнить модель данными из массива `$_POST`, доступ\nк которому обеспечивает Yii при помощи [[yii\\web\\Request::post()]]. Если модель успешно заполнена, то есть пользователь\nотправил данные из HTML-формы, то для проверки данных будет вызван метод [[yii\\base\\Model::validate()|validate()]].\n\nЕсли всё в порядке, действие отобразит представление `entry-confirm`, которое показывает пользователю введенные им данные.\nВ противном случае будет отображено представление `entry`, которое содержит HTML-форму и ошибки проверки данных, если\nони есть.\n\n> Info: `Yii::$app` представляет собой глобально доступный экземпляр-одиночку\n[приложения](structure-applications.md) (singleton). Одновременно это [Service Locator](concept-service-locator.md),\nдающий доступ к компонентам вроде `request`, `response`, `db` и так далее. В коде выше для доступа к данным из `$_POST`\nбыл использован компонент `request`.\n\n\nСоздание представления <span id=\"creating-views\"></span>\n----------------------------------------------------\n\nВ заключение создаём два представления с именами `entry-confirm` и `entry`, которые отображаются действием `entry` из\nпредыдущего подраздела.\n\nПредставление `entry-confirm` просто отображает имя и email. Оно должно быть сохранено в файле `views/site/entry-confirm.php`.\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n<p>Вы ввели следующую информацию:</p>\n\n<ul>\n    <li><label>Name</label>: <?= Html::encode($model->name) ?></li>\n    <li><label>Email</label>: <?= Html::encode($model->email) ?></li>\n</ul>\n```\n\nПредставление `entry` отображает HTML-форму. Оно должно быть сохранено в файле `views/site/entry.php`.\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n?>\n<?php $form = ActiveForm::begin(); ?>\n\n    <?= $form->field($model, 'name') ?>\n\n    <?= $form->field($model, 'email') ?>\n\n    <div class=\"form-group\">\n        <?= Html::submitButton('Отправить', ['class' => 'btn btn-primary']) ?>\n    </div>\n\n<?php ActiveForm::end(); ?>\n```\n\nДля построения HTML-формы представление использует мощный [виджет](structure-widgets.md) [[yii\\widgets\\ActiveForm|ActiveForm]].\nМетоды `begin()` и `end()` выводят открывающий и закрывающий теги формы. Между этими вызовами создаются поля ввода при\nпомощи метода [[yii\\widgets\\ActiveForm::field()|field()]]. Первым идёт поле для \"name\", вторым — для \"email\".\nДалее для генерации кнопки отправки данных вызывается метод [[yii\\helpers\\Html::submitButton()]].\n\n\nПопробуем <span id=\"trying-it-out\"></span>\n--------------------------------------\n\nЧтобы увидеть всё созданное в работе, откройте в браузере следующий URL:\n\n```\nhttps://hostname/index.php?r=site%2Fentry\n```\n\nВы увидите страницу с формой и двумя полями для ввода. Перед каждым полем имеется подпись, которая указывает, какую\nинформацию следует вводить. Если вы нажмёте на кнопку отправки без ввода данных или если вы введете email в неверном\nформате, вы увидите сообщение с ошибкой рядом с каждым проблемным полем.\n\n![Форма с ошибками](images/start-form-validation.png)\n\nПосле ввода верных данных и их отправки, вы увидите страницу с данными, которые вы только что ввели.\n\n![Подтверждение введённых данных](images/start-entry-confirmation.png)\n\n\n\n### Как работает вся эта «магия» <span id=\"magic-explained\"></span>\n\nВы, скорее всего, задаётесь вопросом о том, как же эта HTML-форма работает на самом деле. Весь процесс может показаться\nнемного волшебным: то как показываются подписи к полям, ошибки проверки данных при некорректном вводе и то, что всё это\nпроисходит без перезагрузки страницы.\n\nДа, проверка данных на самом деле происходит и на стороне клиента при помощи JavaScript, и на стороне сервера.\n[[yii\\widgets\\ActiveForm]] достаточно продуман, чтобы взять правила проверки, которые вы объявили в `EntryForm`,\nпреобразовать их в JavaScript код и использовать его для проведения проверок. На случай отключения JavaScript в браузере\nвалидация проводится и на стороне сервера как показано в методе `actionEntry()`. Это даёт уверенность в том, что данные\nкорректны при любых обстоятельствах.\n\nПодписи для полей генерируются методом `field()`, на основе имён свойств модели. Например, подпись `Name` генерируется\nдля свойства `name`. Вы можете модифицировать подписи следующим образом:\n\n```php\n<?= $form->field($model, 'name')->label('Ваше имя') ?>\n<?= $form->field($model, 'email')->label('Ваш Email') ?>\n```\n\n> Info: В Yii есть множество виджетов, позволяющих быстро строить сложные и динамичные представления.\n  Как вы узнаете позже, разрабатывать новые виджеты очень просто. Многое из представлений можно вынести в виджеты, чтобы\n  использовать это повторно в других местах и упростить тем самым разработку в будущем.\n\nЗаключение <span id=\"summary\"></span>\n-----------------------------\n\nВ данном разделе вы попробовали каждую часть шаблона проектирования MVC. Вы изучили как создавать классы моделей\nдля обработки и проверки пользовательских данных.\n\nТакже, вы изучили как получать данные от пользователя и как показать данные пользователю. Это задача может занимать в\nпроцессе разработки значительное время. Yii предоставляет мощные виджеты, которые делают задачу максимально простой.\n\nВ следующем разделе вы изучите как работать с базами данных, что требуется в большинстве приложений.\n\n"
  },
  {
    "path": "docs/guide-ru/start-gii.md",
    "content": "Генерация кода при помощи Gii\n========================\n\nВ этом разделе мы опишем, как использовать [Gii](https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide) для автоматической генерации кода,\nреализующего некоторые общие функции вебсайта. Для достижения этой цели всё, что вам нужно, это просто ввести необходимую информацию в соответствии с инструкциями, отображаемыми на веб-страницах Gii.\n\nВ этом руководстве вы узнаете:\n\n* Как активировать Gii в приложении;\n* Как использовать Gii для создания Active Record класса;\n* Как использовать Gii для генерации кода, реализующего CRUD для таблицы БД.\n* Как настроить код, генерируемый Gii.\n\n\nЗапускаем Gii <span id=\"starting-gii\"></span>\n------------\n\n[Gii](https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide) представлен в Yii как [модуль](structure-modules.md). Вы можете активировать Gii,\nнастроив его в свойстве [[yii\\base\\Application::modules|modules]]. В зависимости от того, каким образом вы создали приложение, вы можете удостовериться в наличии следующего кода в конфигурационном файле `config/web.php`,\n\n```php\n$config = [ ... ];\n\nif (YII_ENV_DEV) {\n    $config['bootstrap'][] = 'gii';\n    $config['modules']['gii'] = [\n        'class' => 'yii\\gii\\Module',\n    ];\n}\n```\n\nПриведенная выше конфигурация показывает, что находясь в [режиме разработки](concept-configurations.md#environment-constants),\nприложение должно включать в себя модуль с именем `gii`, который реализует класс [[yii\\gii\\Module]].\n\nЕсли вы посмотрите [входной скрипт](structure-entry-scripts.md) `web/index.php` вашего приложения, вы\nувидите следующую строку, устанавливающую константу `YII_ENV_DEV` в значение `true`.\n\n```php\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n```\n\nБлагодаря этой строке ваше приложение находится в режиме разработки, и Gii уже активирован в соответствии с описанной выше конфигурацией. Теперь вы можете получить доступ к Gii по следующему адресу:\n\n```\nhttps://hostname/index.php?r=gii\n```\n> Note: Если вы пытаетесь получить доступ к Gii не с локального хоста, по умолчанию, в целях обеспечения безопасности,\n> доступ будет запрещён. Вы можете изменить настройки Gii, чтобы добавить разрешённые IP адреса, как указано ниже\n\n```php\n'gii' => [\n    'class' => 'yii\\gii\\Module',\n    'allowedIPs' => ['127.0.0.1', '::1', '192.168.0.*', '192.168.178.20'] // регулируйте в соответствии со своими нуждами\n],\n```\n![Gii](images/start-gii.png)\n\n\nГенерация класса Active Record <span id=\"generating-ar\"></span>\n---------------------------------\n\nЧтобы использовать Gii для генерации класса Active Record, выберите \"Генератор модели\" (нажав на ссылку на главной странице Gii). И заполните форму следующим образом:\n\n* Имя таблицы: `country`\n* Класс модели : `Country`\n\n![Генератор модели](images/start-gii-model.png)\n\nЗатем нажмите на кнопку \"Предварительный просмотр\". Вы увидите, что `models/Country.php` перечислен в результатах создаваемых файлов классов. Вы можете нажать на имя файла класса для просмотра его содержимого.\n\nЕсли вы уже создали такой же файл и хотите перезаписать его, нажмите кнопку `diff` рядом с именем файла, чтобы увидеть различия между генерируемым кодом и существующей версией.\n\n![Предварительный просмотр генератора модели](images/start-gii-model-preview.png)\n\nДля перезаписи существующего файла установите флажок рядом с \"overwrite\" и нажмите кнопку \"Generate\". Для создания нового файла вы можете просто нажать \"Generate\".\n\nПосле этого вы увидите страницу подтверждения, указывающую на то, что код был успешно сгенерирован. Если файл существовал до этого, вы также увидите сообщение о том, что он был перезаписан заново сгенерированным кодом.\n\n\nСоздание CRUD кода <span id=\"generating-crud\"></span>\n--------------------\n\nCRUD расшифровывается как Create, Read, Update и Delete, предоставляющий четыре основные функции, выполняемые над данными на большинстве веб-сайтов. Чтобы создать функциональность CRUD используя Gii, выберите \"CRUD Генератор\" (нажав на ссылку на главной странице Gii). Для нашей таблицы «country» заполните полученную форму следующим образом:\n\n* Model Class: `app\\models\\Country`\n* Search Model Class: `app\\models\\CountrySearch`\n* Controller Class: `app\\controllers\\CountryController`\n\n![CRUD генератор](images/start-gii-crud.png)\n\nЗатем нажмите на кнопку \"Preview\". Вы увидите список файлов, которые будут созданы, как показано ниже.\n\n![CRUD генератор: предпросмотр](images/start-gii-crud-preview.png)\n\nЕсли вы уже создали файлы `controllers/CountryController.php` и `views/country/index.php` (в разделе о базах данных), установите флажок \"overwrite\", чтобы заменить их. (Предыдущие версии не поддерживают CRUD полностью)\n\n\nИспытываем в действии <span id=\"trying-it-out\"></span>\n-------------\n\nЧтобы увидеть как всё это работает, перейдите по следующему URL, используя ваш браузер:\n\n```\nhttps://hostname/index.php?r=country%2Findex\n```\n\nВы увидите таблицу, показывающую страны из таблицы БД. Вы можете сортировать, а также фильтровать данные, указывая условия фильтрации в заголовках столбцов.\n\nДля каждой отображающейся в таблице страны вы можете просмотреть подробную информацию, обновить или удалить её.\nВы также можете нажать на кнопку \"Создать страну\" в верхней части таблицы для получения формы создания новой страны.\n\n![Таблица данных стран](images/start-gii-country-grid.png)\n\n![Обновление страны](images/start-gii-country-update.png)\n\nНиже приведен список файлов, созданных с помощью Gii, в том случае, если вы захотите исследовать реализацию этих функций, или изменить их:\n\n* Контроллер: `controllers/CountryController.php`\n* Модели: `models/Country.php` и `models/CountrySearch.php`\n* Вид: `views/country/*.php`\n\n> Info: Gii разработан как тонконастраиваемый и расширяемый инструмент генерации кода. Используя его с умом, вы можете значительно ускорить скорость разработки приложений. Для более подробной информации, пожалуйста, обратитесь к разделу [Gii](https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide).\n\n\nЗаключение <span id=\"summary\"></span>\n-------\n\nВ этом разделе вы узнали, как использовать Gii для генерации кода, реализующего полную функциональность CRUD для данных, хранящихся в таблице базы данных.\n"
  },
  {
    "path": "docs/guide-ru/start-hello.md",
    "content": "Говорим «Привет»\n================\n\nВ этом разделе рассмотрим как создать новую страницу с надписью «Привет». В процессе решения задачи вы создадите\n[действие контроллера](structure-controllers.md) и [представление](structure-views.md):\n\n* Приложение обработает запрос и передаст управление соответствующему действию;\n* Действие, в свою очередь, отобразит представление с надписью \"Привет\" конечному пользователю.\n\nС помощью данного руководства вы изучите\n\n* Как создавать [действие](structure-controllers.md), чтобы отвечать на запросы;\n* Как создавать [представление](structure-views.md), чтобы формировать содержимое ответа;\n* Как приложение отсылает запросы к [действию](structure-controllers.md).\n\n\nСоздание Действия <span id=\"creating-action\"></span>\n------------------------------------------------\n\nДля нашей задачи потребуется [действие](structure-controllers.md) `say`, которое читает параметр `message` из\nзапроса и отображает его значение пользователю. Если в запросе не содержится параметра `message`, то действие будет\nвыводить «Привет».\n\n> Info: [Действия](structure-controllers.md) могут быть запущены непосредственно пользователем и сгруппированы в\n  [контроллеры](structure-controllers.md). Результатом выполнения действия является ответ, который получает пользователь.\n\nДействия объявляются в [контроллерах](structure-controllers.md). Для простоты, вы можете объявить действие\n`say` в уже существующем контроллере `SiteController`, который определен в файле класса `controllers/SiteController.php`:\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    // ...существующий код...\n\n    public function actionSay($message = 'Привет')\n    {\n        return $this->render('say', ['message' => $message]);\n    }\n}\n```\n\nВ приведенном коде действие `say` объявлено как метод `actionSay` в классе `SiteController`.\nYii использует префикс `action` чтобы различать методы-действия и обычные методы. Название после префикса `action`\nсчитается идентификатором соответствующего действия.\n\n> Info: Идентификаторы действий задаются в нижнем регистре. Если идентификатор состоит из нескольких слов, они\n  соединяются дефисами, то есть `create-comment`. Имена методов действий получаются путём удаления дефисов\n  из идентификатора, преобразования первой буквы каждого слова в верхний регистр и добавления префикса `action`.\n  Например, идентификатор действия `create-comment` соответствует методу `actionCreateComment`.\n\nМетод действия принимает параметр `$message`, который по умолчанию равен `\"Привет\"`. Когда приложение получает запрос\nи определяет, что действие `say` ответственно за его обработку, параметр заполняется одноимённым значением из запроса.\n\nВнутри метода действия, для вывода отображения [представления](structure-views.md) с именем `say`, используется метод\n[[yii\\web\\Controller::render()|render()]]. Для того, чтобы вывести сообщение, в отображение передаётся параметр `message`.\nРезультат отображения при помощи `return` передаётся приложению, которое отдаёт его пользователю.\n\n\nСоздание представления <span id=\"creating-view\"></span>\n---------------------------------------------------\n\n[Представления](structure-views.md) являются скриптами, которые используются для формирования тела ответа. Для нашего\nприложения вы создадите представление `say`, которое будет выводить параметр `message`, полученный из метода действия:\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n<?= Html::encode($message) ?>\n```\n\nПредставление `say` должно быть сохранено в файле `views/site/say.php`. Когда метод [[yii\\web\\Controller::render()|render()]]\nвызывается в действии, он будет искать PHP файл с именем вида `views/ControllerID/ViewName.php`.\n\nСтоит отметить, что в коде выше параметр `message` [[yii\\helpers\\Html::encode()|экранируется для HTML]] перед выводом.\nЭто обязательно так как параметр приходит от пользователя, который может попытаться провести\n[XSS атаку](https://ru.wikipedia.org/wiki/%D0%9C%D0%B5%D0%B6%D1%81%D0%B0%D0%B9%D1%82%D0%BE%D0%B2%D1%8B%D0%B9_%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D0%B8%D0%BD%D0%B3)\nпутём вставки зловредного JavaScript кода.\n\nВы можете дополнить представление `say` HTML тегами, текстом или кодом PHP. Фактически, представление `say` является\nпростым PHP скриптом, который выполняется методом [[yii\\web\\Controller::render()|render()]]. Содержимое, выводимое\nскриптом представления, будет передано пользователю приложением.\n\n\nПопробуем <span id=\"trying-it-out\"></span>\n--------------------------------------\n\nПосле создания действия и представления вы можете перейти на новую страницу по следующему URL:\n\n```\nhttps://hostname/index.php?r=site%2Fsay&message=Привет+мир\n```\n\n![Привет, мир](images/start-hello-world.png)\n\nБудет отображена страница с надписью «Привет мир». Она использует ту же шапку и футер, что и остальные страницы приложения.\nЕсли вы не укажете параметр `message`, то увидите на странице «Привет». Это происходит потому, как `message` передаётся\nв метод `actionSay()` и значение по умолчанию — «Привет».\n\n> Info: Новая страница использует ту же шапку и футер, что и другие страницы, потому что метод\n  [[yii\\web\\Controller::render()|render()]] автоматически вставляет результат представления `say` в, так называемый, \n  [макет](structure-views.md) `views/layouts/main.php`.\n\nПараметр `r` в нашем URL требует дополнительных пояснений. Он связан с [маршрутом (route)](runtime-routing.md), который представляет\nсобой уникальный идентификатор, указывающий на действие. Его формат `ControllerID/ActionID`. Когда приложение получает\nзапрос, оно проверяет параметр `r` и, используя `ControllerID`, определяет какой контроллер использовать для\nобработки запроса. Затем, контроллер использует часть `ActionID`, чтобы определить, какое действие выполняет реальную работу.\nВ нашем случае маршрут `site/say` будет соответствовать контроллеру `SiteController` и его действию `say`. \nВ результате для обработки запроса будет вызван метод `SiteController::actionSay()`.\n\n> Info: Как и действия, контроллеры также имеют идентификаторы, которые однозначно определяют их в приложении.\n  Идентификаторы контроллеров используют те же правила именования, что и идентификаторы действий. Имена классов\n  контроллеров получаются путём удаления дефисов из идентификатора, преобразования первой буквы каждого слова в\n  верхний регистр и добавления в конец `Controller`. Например, идентификатор контроллера `post-comment` соответствует\n  имени класса контроллера `PostCommentController`.\n\n\nЗаключение <span id=\"summary\"></span>\n-----------------------------\n\nВ этом разделе вы затронули тему контроллеров и представлений в паттерне MVC. Вы создали действие как часть контроллера,\nобрабатывающего запросы, и представление, участвующее в формировании ответа. В этом процессе никак не была задействована\nмодель, так как в качестве данных выступает лишь простой параметр `message`.\n\nТакже вы познакомились с концепцией маршрутизации, которая является связующим звеном между запросом пользователя и\nдействием контроллера.\n\nВ следующем разделе вы узнаете как создавать модели и добавлять новые страницы с HTML формами.\n\n"
  },
  {
    "path": "docs/guide-ru/start-installation.md",
    "content": "Установка Yii <span id=\"installing-from-composer\"></span>\n==============\n\nВы можете установить Yii двумя способами: используя [Composer](https://getcomposer.org/) или скачав архив.\nПервый способ предпочтительнее так как позволяет установить новые [расширения](structure-extensions.md)\nили обновить Yii одной командой.\n\n> Note: В отличие от Yii 1, после стандартной установки Yii 2 мы получаем как фреймворк, так и шаблон приложения.\n\n\nУстановка при помощи Composer <span id=\"installing-via-composer\"></span>\n-----------------------\n\n### Установка Composer\n\nЕсли Composer еще не установлен это можно сделать по инструкции на\n[getcomposer.org](https://getcomposer.org/download/), или одним из нижеперечисленных способов. На Linux или Mac \nиспользуйте следующую команду:\n\n```bash\ncurl -sS https://getcomposer.org/installer | php\nmv composer.phar /usr/local/bin/composer\n```\n\nНа Windows, скачайте и запустите [Composer-Setup.exe](https://getcomposer.org/Composer-Setup.exe).\n\nВ случае возникновения проблем читайте\n[раздел \"Troubleshooting\" в документации Composer](https://getcomposer.org/doc/articles/troubleshooting.md).\nЕсли вы только начинаете использовать Composer, рекомендуем прочитать как минимум\n[раздел \"Basic usage\"](https://getcomposer.org/doc/01-basic-usage.md).\n\nВ данном руководстве предполагается, что Composer установлен [глобально](https://getcomposer.org/doc/00-intro.md#globally).\nТо есть он доступен через команду `composer`. Если вы используете `composer.phar` из локальной директории,\nизменяйте команды соответственно.\n\nЕсли у вас уже установлен Composer, обновите его при помощи `composer self-update`.\n\n> Note: Во время установки Yii Composer запрашивает довольно большое количество информации через Github API.\n> Количество запросов варьируется в зависимости от количества зависимостей вашего проекта и может превысить\n> ограничения **Github API**. Если это произошло, Composer спросит логин и пароль от Github. Это необходимо для\n> получения токена для Github API. На быстрых соединениях это может прозойти ещё до того, как Composer сможет\n> обработать ошибку, поэтому мы рекомендум настроить токен доступа до установки Yii.\n> Инструкции приведены в [документации Composer о токенах Github API](https://getcomposer.org/doc/articles/troubleshooting.md#api-rate-limit-and-oauth-tokens).\n\nПосле установки Composer устанавливать Yii можно запустив следующую команду в папке доступной через веб:\n\n### Установка Yii\n\n```bash\ncomposer create-project --prefer-dist yiisoft/yii2-app-basic basic\n```\n\nЭта команда устанавливает последнюю стабильную версию Yii в директорию `basic`. Если хотите, можете выбрать другое\nимя директории.\n\n> Info: Если команда `composer create-project` не выполняется нормально, попробуйте обратиться к\n> [разделу \"Troubleshooting\" документации Composer](https://getcomposer.org/doc/articles/troubleshooting.md).\n> Там описаны другие типичные ошибки. После того, как вы исправили ошибку, запустите `composer update` в директории `basic`.\n\n> Tip: Если вы хотите установить последнюю нестабильную ревизию Yii, можете использовать следующую команду,\n> в которой присутствует [опция stability](https://getcomposer.org/doc/04-schema.md#minimum-stability):\n>\n> ```bash\n> composer create-project --prefer-dist --stability=dev yiisoft/yii2-app-basic basic\n> ```\n>\n> Старайтесь не использовать нестабильную версию Yii на рабочих серверах потому как она может внезапно поломать код.\n\n\nУстановка из архива <span id=\"installing-from-archive-file\"></span>\n-------------------------------\n\nУстановка Yii из архива состоит из трёх шагов:\n\n1. Скачайте архив с [yiiframework.com](https://www.yiiframework.com/download/);\n2. Распакуйте скачанный архив в папку, доступную из Web.\n3. В файле `config/web.php` добавьте секретный ключ в значение `cookieValidationKey` (при установке через Composer\n   это происходит автоматически):\n\n```php\n// !!! insert a secret key in the following (if it is empty) - this is required by cookie validation\n'cookieValidationKey' => 'enter your secret key here',\n```\n\nДругие опции установки <span id=\"other-installation-options\"></span>\n--------------------------\n\nВыше приведены инструкции по установке Yii в виде базового приложения готового к работе.\nЭто отличный вариант для небольших проектов или для тех, кто только начинает изучать Yii.\n\nЕсть два основных варианта такой установки:\n\n* Если вам нужен только сам фреймворк и вы хотели бы создать приложение с нуля, воспользуйтесь инструкцией, описанной в\nразделе «[Создание приложения с нуля](tutorial-start-from-scratch.md)».\n* Если хотите начать с более продвинутого приложения, хорошо подходящего для работы в команде, используйте\n[шаблон приложения advanced](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md).\n\n\nПроверка установки <span id=\"verifying-installation\"></span>\n----------------------\n\nПосле установки приложение будет доступно по следующему URL:\n\n```\nhttp://localhost/basic/web/index.php\n```\n\nЗдесь подразумевается, что вы установили приложение в директорию `basic` в корневой директории вашего веб сервера\nсервер работает локально (`localhost`). Вам может потребоваться предварительно его настроить.\n\n![Успешно установленный Yii](images/start-app-installed.png)\n\nВы должны увидеть страницу приветствия «Congratulations!». Если нет — проверьте требования Yii одним из способов:\n\n* Браузером перейдите по адресу `http://localhost/basic/requirements.php`\n* Или выполните команду в консоли: \n\n```bash\ncd basic\nphp requirements.php\n```\n\nДля корректной работы фреймворка вам необходима установка PHP, соответствующая его минимальным требованиям. Основное\nтребование — PHP версии 5.4 и выше. Если ваше приложение работает с базой данных, необходимо установить\n[расширение PHP PDO](https://www.php.net/manual/ru/pdo.installation.php) и соответствующий драйвер \n(например, `pdo_mysql` для MySQL).\n\n\nНастройка веб сервера <span id=\"configuring-web-servers\"></span>\n-----------------------\n\n> Info: можете пропустить этот подраздел если вы только начали знакомиться с фреймворком и пока не разворачиваете\n  его на рабочем сервере.\n\nПриложение, установленное по инструкциям, приведённым выше, будет работать сразу как с [Apache](https://httpd.apache.org/),\nтак и с [Nginx](https://nginx.org/) под Windows и Linux с установленным PHP 5.4 и выше. Yii 2.0 также совместим с\n[HHVM](https://hhvm.com/). Тем не менее, в некоторых случаях поведение при работе с HHVM отличается от обычного PHP.\nБудьте внимательны.\n\nНа рабочем сервере вам наверняка захочется изменить URL приложения с `https://www.example.com/basic/web/index.php`\nна `https://www.example.com/index.php`. Для этого необходимо изменить корневую директорию в настройках веб сервера так,\nчтобы та указывала на `basic/web`. Дополнительно можно спрятать `index.php` следуя описанию в разделе\n«[Разбор и генерация URL](runtime-routing.md)». Далее будет показано как настроить Apache и Nginx.\n\n> Info: Устанавливая `basic/web` корневой директорией веб сервера вы защищаете от нежелательного доступа код и данные,\n  находящиеся на одном уровне с `basic/web`. Это делает приложение более защищенным.\n\n> Info: Если приложение работает на хостинге где нет доступа к настройкам веб сервера, то можно изменить структуру\n  приложения как описано в разделе «[Работа на Shared хостинге](tutorial-shared-hosting.md)».\n\n\n### Рекомендуемые настройки Apache <span id=\"recommended-apache-configuration\"></span>\n\nДобавьте следующее в `httpd.conf` Apache или в конфигурационный файл виртуального хоста. Не забудьте заменить\n`path/to/basic/web` на корректный путь к `basic/web`.\n\n```\n# Устанавливаем корневой директорией \"basic/web\"\nDocumentRoot \"path/to/basic/web\"\n\n<Directory \"path/to/basic/web\">\n    RewriteEngine on\n\n    # Если запрашиваемая в URL директория или файл существуют обращаемся к ним напрямую\n    RewriteCond %{REQUEST_FILENAME} !-f\n    RewriteCond %{REQUEST_FILENAME} !-d\n    # Если нет - перенаправляем запрос на index.php\n    RewriteRule . index.php\n\n    # ...прочие настройки...\n</Directory>\n```\n\n\n### Рекомендуемые настройки Nginx <span id=\"recommended-nginx-configuration\"></span>\n\nPHP должен быть установлен как [FPM SAPI](https://www.php.net/manual/ru/install.fpm.php) для [Nginx](https://wiki.nginx.org/).\nИспользуйте следующие параметры Nginx и не забудьте заменить `path/to/basic/web` на корректный путь к `basic/web` и\n`mysite.test` на ваше имя хоста.\n\n```\nserver {\n    charset utf-8;\n    client_max_body_size 128M;\n\n    listen 80; ## listen for ipv4\n    #listen [::]:80 default_server ipv6only=on; ## слушаем ipv6\n\n    server_name mysite.test;\n    root        /path/to/basic/web;\n    index       index.php;\n\n    access_log  /path/to/project/log/access.log;\n    error_log   /path/to/project/log/error.log;\n\n    location / {\n        # Перенаправляем все запросы к несуществующим директориям и файлам на index.php\n        try_files $uri $uri/ /index.php?$args;\n    }\n\n    # раскомментируйте строки ниже во избежание обработки Yii обращений к несуществующим статическим файлам\n    #location ~ \\.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ {\n    #    try_files $uri =404;\n    #}\n    #error_page 404 /404.html;\n\n    location ~ \\.php$ {\n        include fastcgi.conf;\n        fastcgi_pass   127.0.0.1:9000;\n        #fastcgi_pass unix:/var/run/php5-fpm.sock;\n    }\n\n    location ~ /\\.(ht|svn|git) {\n        deny all;\n    }\n}\n```\n\nИспользуя данную конфигурацию установите `cgi.fix_pathinfo=0` в `php.ini` чтобы предотвратить лишние системные\nвызовы `stat()`.\n\nУчтите, что используя HTTPS необходимо задавать `fastcgi_param HTTPS on;` чтобы Yii мог корректно определять защищенное\nсоединение.\n"
  },
  {
    "path": "docs/guide-ru/start-looking-ahead.md",
    "content": "Взгляд в будущее\n=============\n\nВ итоге вы создали полноценное приложение на Yii и узнали, как реализовать некоторые \nнаиболее часто используемые функции, такие, как получение данных от пользователя \nпри помощи HTML-форм, выборки данных из базы данных и их отображения в разбитом на страницы виде. \nТакже вы узнали,  как использовать [Gii](https://github.com/yiisoft/yii2-gii/blob/master/docs/guide/README.md) для\nавтоматической генерации кода, что превращает программирование в настолько простую задачу, как простое заполнение\nкакой-либо формы. В этом разделе мы обобщим ресурсы о Yii, которые помогут вам быть более продуктивным при использовании Yii.\n\n* Документация\n    - Подробное руководство:\n      как следует из названия, руководство точно определяет, \n\t  как Yii должен работать и дает вам общие указания по его использованию. \n\t  Это самый важный учебник по Yii, который вы должны прочитать, прежде чем писать различный Yii код.\n    - Описание классов:\n      определяет использование каждого класса, представленного в Yii. \n\t  Им следует пользоваться, когда вы пишете код и хотите разобраться в использовании конкретного класса, метода, свойства.\n    - Вики-статьи:\n      написаны пользователями Yii на основе их собственного опыта. \n\t  Большинство из них составлены для сборника рецептов, показывая, как решить конкретные проблемы с использованием Yii. \n\t  Причём качество этих статей может быть таким же хорошим, как в Подробном руководстве. \n\t  Они полезны тем, что охватывают более широкие темы и часто могут предоставить вам готовые решения для дальнейшего использования.\n    - Книги\n* [Расширения](https://www.yiiframework.com/extensions/):\n  Yii гордится библиотекой из тысяч внесённых пользователями расширений, \n  которые могут быть легко подключены в ваши приложения и сделать разработку приложений ещё быстрее и проще.\n* Сообщество\n    - [Форум](https://forum.yiiframework.com/)\n    - [Чат Gitter](https://gitter.im/yiisoft/yii2/rus)\n    - [GitHub](https://github.com/yiisoft/yii2)\n    - [Facebook](https://www.facebook.com/groups/yiitalk/)\n    - [Twitter](https://twitter.com/yiiframework)\n    - [LinkedIn](https://www.linkedin.com/groups/yii-framework-1483367)\n"
  },
  {
    "path": "docs/guide-ru/start-prerequisites.md",
    "content": "Что нужно знать\n===============\n\nКривая обучения Yii не так крута, как у других PHP фреймворков, но всё же есть ряд вещей, которые стоит изучить перед началом работы.\n\n## PHP\n\nYii - это PHP фреймворк, поэтому убедитесь, что вы [читали и понимаете справочник языка](https://www.php.net/manual/ru/langref.php).\nПри разработке на Yii вы будете писать код в объектно-ориентированном стиле, поэтому убедитесь, что знакомы с [классами и объектами](https://www.php.net/manual/ru/language.oop5.basic.php), а также с [пространствами имён](https://www.php.net/manual/ru/language.namespaces.php).\n\n## Объектно-ориентированное программирование\n\nНеобходимо базовое понимание объектно-ориентированного программирования. Если вы с ним не знакомы, пройдите один из\nмногочисленных учебных курсов, например [курс на tutsplus.com](https://code.tutsplus.com/tutorials/object-oriented-php-for-beginners--net-12762).\n\nУчтите, что чем сложнее ваше приложение, тем более продвинутые концепции ООП необходимо освоить\nдля успешного управления этой сложностью.\n\n## Командная строка и Composer\n\nYii активно использует стандартный менеджер пакетов PHP - [Composer](https://getcomposer.org/), поэтому убедитесь, что прочитали\nи поняли его [руководство](https://getcomposer.org/doc/01-basic-usage.md). Если вы не знакомы с командной строкой - самое время начать. Освоив основы, вы уже не захотите работать без неё.\n"
  },
  {
    "path": "docs/guide-ru/start-workflow.md",
    "content": "Запуск приложения\n====================\n\n После установки Yii базовое приложение будет доступно либо по URL `https://hostname/basic/web/index.php`, либо по `https://hostname/index.php`, в зависимости от настроек Web сервера. Данный раздел - общее введение в организацию кода, встроенный функционал и обработку обращений приложением Yii.\n\n> Info: далее в данном руководстве предполагается, что Yii установлен в директорию `basic/web`, которая, в свою очередь, установлена как корневой каталог в настройках Web сервера. В результате, обратившись по URL `https://hostname/index.php`, Вы получите доступ к приложению, расположенному в `basic/web`. Детальнее с процессом начальной настройки можно познакомиться в разделе [Установка Yii](start-installation.md).\n\nОтметим, что в отличие от самого фреймворка как только приложение установлено, оно становится целиком вашим. Вы можете изменять его код как угодно.\n\nФункциональность <span id=\"functionality\"></span>\n---------------\n\nУстановленный шаблон простого приложения состоит из четырех страниц:\n\n* домашняя страница, отображается при переходе по URL `https://hostname/index.php`\n* страница \"About\" (\"О нас\")\n* на странице \"Contact\" находится форма обратной связи, на которой пользователь может обратиться к разработчику по e-mail\n* на странице \"Login\" отображается форма авторизации. Попытайтесь авторизоваться с логином/паролем \"admin/admin\".\n  Обратите внимание на изменение раздела \"Login\" в главном меню на \"Logout\".\n\nЭти страницы используют смежный хедер (шапка сайта) и футер (подвал). В \"шапке\" находится главное меню, при помощи\nкоторого пользователь перемещается по сайту. В \"подвале\" - копирайт и общая информация.\n\nВ самой нижней части окна Вы будете видеть системные сообщения Yii - журнал, отладочную информацию, сообщения об ошибках,\nзапросы к базе данных и т.п. Выводом данной информации руководит\n[встроенный отладчик](https://github.com/yiisoft/yii2-debug/blob/master/docs/guide/README.md), он записывает и отображает\nинформацию о ходе выполнения приложения.\n\nВ дополнение к веб приложению имеется консольный скрипт с названием `yii`, который находится в базовой директории приложения.\nЭтот скрипт может быть использован для выполнения фоновых задач и обслуживания приложения. Всё это описано в разделе\n[Консольные команды](tutorial-console.md).\n\nСтруктура приложения Yii <span id=\"application-structure\"></span>\n---------------------\n\nНиже приведен список основных директорий и файлов вашего приложения (считаем, что приложение установлено в директорию `basic`):\n\n```\nbasic/                  корневой каталог приложения\n    composer.json       используется Composer'ом, содержит описание приложения\n    config/             конфигурационные файлы\n        console.php     конфигурация консольного приложения\n        web.php         конфигурация Web приложения\n    commands/           содержит классы консольных команд\n    controllers/        контроллеры\n    models/             модели\n    runtime/            файлы, которые генерирует Yii во время выполнения приложения (логи, кэш и т.п.)\n    vendor/             содержит пакеты Composer'а и, собственно, сам фреймворк Yii\n    views/              виды приложения\n    web/                корневая директория Web приложения. Содержит файлы, доступные через Web\n        assets/         скрипты, используемые приложением (js, css)\n        index.php       точка входа в приложение Yii. С него начинается выполнение приложения\n    yii                 скрипт выполнения консольного приложения Yii\n```\n\nВ целом, приложение Yii можно разделить на две категории файлов: расположенные в `basic/web` и расположенные в других директориях. Первая категория доступна через Web (например, браузером), вторая не доступна извне и не должна быть доступной т.к. содержит служебную информацию.\n\nВ Yii реализован [архитектурный паттерн MVC](https://ru.wikipedia.org/wiki/Model-View-Controller),\nкоторая соответствует структуре директорий приложения. В директории `models` находятся [Модели](structure-models.md),\nв `views` расположены [Виды](structure-views.md), а в каталоге `controllers` все [Контроллеры](structure-controllers.md) приложения.\n\nДиаграмма ниже демонстрирует внутреннее устройство приложения.\n\n![внутреннее устройство приложения](images/application-structure.png)\n\nВ каждом приложении Yii есть точка входа в приложение, `web/index.php` это единственный PHP-скрипт доступный для выполнения из Web. Он принимает входящий запрос и создает экземпляр [приложения](structure-applications.md).\n[Приложение](structure-applications.md) обрабатывает входящие запросы при помощи [компонентов](concept-components.md) и отправляет запрос контроллеру. [Виджеты](structure-widgets.md) используются в [Видах](structure-views.md) для построения динамических интерфейсов сайта.\n\n\nЖизненный цикл пользовательского запроса <span id=\"request-lifecycle\"></span>\n-----------------\n\nНа диаграмме показано как приложение обрабатывает запрос.\n\n![Жизненный цикл запроса](images/request-lifecycle.png)\n\n1. Пользователь обращается к [точке входа](structure-entry-scripts.md) `web/index.php`.\n2. Скрипт загружает конфигурацию [configuration](concept-configurations.md) и создает экземпляр [приложения](structure-applications.md) для дальнейшей обработки запроса.\n3. Приложение определяет [маршрут](runtime-routing.md) запроса при помощи компонента приложения [запрос](runtime-requests.md).\n4. Приложение создает экземпляр [контроллера](structure-controllers.md) для выполнения запроса.\n5. Контроллер, в свою очередь, создает [действие](structure-controllers.md) и накладывает на него фильтры.\n6. Если хотя бы один фильтр дает сбой, выполнение приложения останавливается.\n7. Если все фильтры пройдены - приложение выполняется.\n8. Действие загружает модель данных. Вероятнее всего из базы данных.\n9. Действие генерирует вид, отображая в нем данные (в т.ч. и полученные из модели).\n10. Сгенерированный вид приложения передается как компонент [ответ](runtime-responses.md).\n11. Компонент \"ответ\" отправляет готовый результат работы приложения браузеру пользователя.\n\n"
  },
  {
    "path": "docs/guide-ru/structure-application-components.md",
    "content": "Компоненты приложения\n=====================\n\nПриложения являются [сервис локаторами](concept-service-locator.md). Они хранят множество так называемых\n*компонентов приложения*, которые предоставляют различные средства для обработки запросов. Например, \nкомпонент `urlManager` ответственен за маршрутизацию веб запросов к нужному контроллеру; компонент `db` предоставляет\nсредства для работы с базой данных; и т. д.\n\nКаждый компонент приложения имеет свой уникальный ID, который позволяет идентифицировать его среди других различных компонентов\nв одном и том же приложении. Вы можете получить доступ к компоненту следующим образом:\n\n```php\n\\Yii::$app->componentID\n```\n\nНапример, вы можете использовать `\\Yii::$app->db` для получения [[yii\\db\\Connection|соединения с БД]],\nи `\\Yii::$app->cache` для получения доступа к основному компоненту [[yii\\caching\\Cache|кэша]], зарегистрированному в приложении.\n\nКомпонент приложения будет создан при первом обращении к нему через вышеуказанное выражение. Любые дальнейшие обращения будут возвращать тот же экземпляр компонента.\n\nКомпонентами приложения могут быть любые объекты. Вы можете зарегистрировать их с помощью  \nсвойства [[yii\\base\\Application::components]] в [конфигурации](structure-applications.md#application-configurations) приложения.\nНапример,\n\n```php\n[\n    'components' => [\n        // регистрация \"cache\" компонента с помощью имени класса\n        'cache' => 'yii\\caching\\ApcCache',\n\n        // регистрация \"db\" компонента с помощью массива конфигурации\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=demo',\n            'username' => 'root',\n            'password' => '',\n        ],\n\n        // регистрация \"search\" компонента с помощью анонимной функции\n        'search' => function () {\n            return new app\\components\\SolrService;\n        },\n    ],\n]\n```\n\n> Info: Хотя вы можете зарегистрировать столько компонентов в приложении сколько вам нужно,\n  все таки стоит это делать разумно. Компоненты приложения похожи на глобальные переменные. Использование слишком\n  большого количества компонентов приложения может потенциально сделать ваш код сложным для разработки и тестирования.\n  В большинстве случаев вы можете просто создать локальный компонент и использовать его при необходимости.\n\n## Компоненты начальной загрузки <span id=\"bootstrapping-components\"></span>\n\nКак упоминалось выше, компонент приложения будет создан только при первом обращении к нему. Однако может возникнуть необходимость в наличии созданного компонента при каждом запросе, даже если напрямую к нему ни разу не обращались. Для этого необходимо указать ID компонента в качестве элемента свойства [[yii\\base\\Application::bootstrap|bootstrap]].\n\nК примеру, при данной конфигурации компонент `log` всегда подгружается при загрузке:\n\n```php\n[\n    'bootstrap' => [\n        'log',\n    ],\n    'components' => [\n        'log' => [\n            // конфигурация для компонента `log`\n        ],\n    ],\n]\n```\n\n## Встроенные компоненты приложения <span id=\"core-application-components\"></span>\n\nВ Yii есть несколько *встроенных* компонентов приложения, с фиксированными ID и конфигурациями по умолчанию. Например,\nкомпонент [[yii\\web\\Application::request|request]] используется для сбора информации о запросе пользователя и разбора его в\nопределенный [маршрут](runtime-routing.md); компонент [[yii\\base\\Application::db|db]] представляет собой соединение с базой данных,\nчерез которое вы можете выполнять запросы. Именно с помощью этих встроенных компонентов Yii приложения могут обработать\nзапрос пользователя.\n\nНиже представлен список встроенных компонентов приложения. Вы можете конфигурировать их также как и другие компоненты приложения.\nКогда вы конфигурируете встроенный компонент приложения и не указываете класс этого компонента, то значение по умолчанию будет использовано.\n\n* [[yii\\web\\AssetManager|assetManager]]: используется для управления и опубликования ресурсов приложения. \n  Более детальная информация представлена в разделе [Ресурсы](structure-assets.md);\n* [[yii\\db\\Connection|db]]: представляет собой соединение с базой данных, через которое вы можете выполнять запросы.\n  Обратите внимание, что когда вы конфигурируете данный компонент, вы должны указать класс компонента также как и остальные\n  необходимые параметры, такие как [[yii\\db\\Connection::dsn]].\n  Более детальная информация представлена в разделе [Объекты доступа к данным (DAO)](db-dao.md);\n* [[yii\\base\\Application::errorHandler|errorHandler]]: осуществляет обработку PHP ошибок и исключений.\n  Более детальная информация представлена в разделе [Обработка ошибок](runtime-handling-errors.md);\n* [[yii\\i18n\\Formatter|formatter]]: форматирует данные для отображения их конечному пользователю. Например, число может\n  быть отображено с различными разделителями, дата может быть отображена в формате `long`.\n  Более детальная информация представлена в разделе [Форматирование данных](output-formatting.md);\n* [[yii\\i18n\\I18N|i18n]]: используется для перевода сообщений и форматирования.\n  Более детальная информация представлена в разделе [Интернационализация](tutorial-i18n.md);\n* [[yii\\log\\Dispatcher|log]]: обработка и маршрутизация логов.\n  Более детальная информация представлена в разделе [Логирование](runtime-logging.md);\n* [[yii\\swiftmailer\\Mailer|mailer]]: предоставляет возможности для составления и рассылки писем.\n  Более детальная информация представлена в разделе [Отправка почты](tutorial-mailing.md);\n* [[yii\\base\\Application::response|response]]: представляет собой данные от сервера, которые будет направлены пользователю.\n  Более детальная информация представлена в разделе [Ответы](runtime-responses.md);\n* [[yii\\base\\Application::request|request]]: представляет собой запрос, полученный от конечных пользователей.\n  Более детальная информация представлена в разделе [Запросы](runtime-requests.md);\n* [[yii\\web\\Session|session]]: информация о сессии. Данный компонент доступен только в [[yii\\web\\Application|веб приложениях]].\n  Более детальная информация представлена в разделе [Сессии и куки](runtime-sessions-cookies.md);\n* [[yii\\web\\UrlManager|urlManager]]: используется для разбора и создания URL.\n  Более детальная информация представлена в разделе [Разбор и генерация URL](runtime-routing.md);\n* [[yii\\web\\User|user]]: представляет собой информацию аутентифицированного пользователя. \n  Данный компонент доступен только в [[yii\\web\\Application|веб приложениях]].\n  Более детальная информация представлена в разделе [Аутентификация](security-authentication.md);\n* [[yii\\web\\View|view]]: используется для отображения представлений.\n  Более детальная информация представлена в разделе [Представления](structure-views.md).\n"
  },
  {
    "path": "docs/guide-ru/structure-applications.md",
    "content": "Приложения\n==========\n\nПриложения это объекты, которые управляют всей структурой и жизненным циклом прикладной системы Yii.\nКаждая прикладная система Yii включает в себя один объект приложения, который создается во [входном скрипте](structure-entry-scripts.md)\nи глобально доступен через `\\Yii::$app`.\n\n> Info: В зависимости от контекста, когда мы говорим \"приложение\", это может означать как объект приложения так и\n  приложение как прикладную систему в целом.\n\nСуществует два вида приложений: [[yii\\web\\Application|веб приложения]] и [[yii\\console\\Application|консольные приложения]].\nКак можно догадаться по названию, первый тип в основном занимается обработкой веб запросов, в то время как последний - консольных команд.\n\n\n## Конфигурации приложения <span id=\"application-configurations\"></span>\n\nКогда [входной скрипт](structure-entry-scripts.md) создаёт приложение, он загрузит [конфигурацию](concept-configurations.md)\nи применит её к приложению, например:\n\n```php\nrequire __DIR__ . '/../vendor/autoload.php';\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n\n// загрузка конфигурации приложения\n$config = require __DIR__ . '/../config/web.php';\n\n// создание объекта приложения и его конфигурирование\n(new yii\\web\\Application($config))->run();\n```\n\nТакже как и обычные [конфигурации](concept-configurations.md), конфигурации приложения указывают как следует инициализировать\nсвойства объектов приложения. Из-за того, что конфигурация приложения часто является очень сложной, она разбивается на несколько\n[конфигурационных файлов](concept-configurations.md#configuration-files), например, `web.php` - файл в приведённом выше примере.\n\n\n## Свойства приложений <span id=\"application-properties\"></span>\n\nСуществует много важных свойств приложения, которые вы настраиваете в конфигурациях приложения. Эти свойства обычно\nописывают окружение, в котором работает приложение. Например, приложение должно знать каким образом загружать [контроллеры](structure-controllers.md),\nгде хранить временные файлы, и т. д. Ниже мы рассмотрим данные свойства.\n\n\n### Обязательные свойства <span id=\"required-properties\"></span>\n\nВ любом приложении, вы должны настроить минимум два свойства: [[yii\\base\\Application::id|id]]\nи [[yii\\base\\Application::basePath|basePath]].\n\n\n#### [[yii\\base\\Application::id|id]] <span id=\"id\"></span>\n\nСвойство [[yii\\base\\Application::id|id]] это уникальный индекс приложения, который отличает его от других приложений.\nВ основном это используется внутрисистемно. Хоть это и не обязательно, но для лучшей совместимости рекомендуется использовать\nбуквенно-цифровые символы при указании индекса приложения.\n\n\n#### [[yii\\base\\Application::basePath|basePath]] <span id=\"basePath\"></span>\n\nСвойство [[yii\\base\\Application::basePath|basePath]] указывает на корневую директорию приложения. Эта директория содержит\nвесь защищенный исходный код приложения. В данной директории обычно могут находиться поддиректории `models`, `views`,\n`controllers`, содержащие код, соответствующий шаблону проектирования MVC.\n\nВы можете задать свойство [[yii\\base\\Application::basePath|basePath]] используя путь к директории или используя\n[псевдоним пути](concept-aliases.md). В обоих случаях, указанная директория должна существовать, иначе будет выброшено\nисключение. Путь будет нормализован функцией `realpath()`.\n\nСвойство [[yii\\base\\Application::basePath|basePath]] часто используется для указания других важных путей (например, путь к \nдиректории runtime, используемой приложением). По этой причине, псевдоним пути `@app` предустановлен и содержит данный путь.\nПроизводные пути могут быть получены с использованием этого псевдонима пути (например, `@app/runtime` указывает на\nвременную директорию runtime).\n\n\n### Важные свойства <span id=\"important-properties\"></span>\n\nСвойства, указанные в этом подразделе, часто нуждаются в предварительной настройке т.к. они разнятся от приложения к приложению.\n\n\n#### [[yii\\base\\Application::aliases|aliases]] <span id=\"aliases\"></span>\n\nЭто свойство позволяет настроить вам множество [псевдонимов](concept-aliases.md) в рамках массива.\nКлючами массива являются имена псевдонимов, а значениями массива - соответствующие значения пути. Например,\n\n```php\n[\n    'aliases' => [\n        '@name1' => 'path/to/path1',\n        '@name2' => 'path/to/path2',\n    ],\n]\n```\n\nЭто свойство доступно таким образом, чтобы вы могли указывать псевдонимы в рамках конфигурации приложения, \nа не вызовов метода [[Yii::setAlias()]].\n\n\n#### [[yii\\base\\Application::bootstrap|bootstrap]] <span id=\"bootstrap\"></span>\n\n\nДанное свойство является очень удобным, оно позволяет указать массив компонентов, которые должны быть загружены\nв процессе  [[yii\\base\\Application::bootstrap()|начальной загрузки]] приложения. Например, если вы хотите, чтобы\n[модуль](structure-modules.md) производил тонкую настройку [URL правил](runtime-routing.md), вы можете указать его\nID в качестве элемента данного свойства.\n\nКаждый из элементов данного свойства, может быть указан в одном из следующих форматов:\n\n- ID, указанный в [компонентах](#components);\n- ID модуля, указанный в [модулях](#modules);\n- название класса;\n- массив конфигурации;\n- анонимная функция, которая создаёт и возвращает компонент.\n\nНапример,\n\n```php\n[\n    'bootstrap' => [\n        // ID компонента приложения или модуля\n        'demo',\n\n        // название класса\n        'app\\components\\Profiler',\n\n        // массив конфигурации\n        [\n            'class' => 'app\\components\\Profiler',\n            'level' => 3,\n        ],\n\n        // анонимная функция\n        function () {\n            return new app\\components\\Profiler();\n        }\n    ],\n]\n```\n> Info: Если ID модуля такой же, как идентификатор компонента приложения, то в процессе [начальной загрузки](runtime-bootstrapping.md)\n> будет использован компонент приложения. Если Вы вместо этого хотите использовать модуль, то можете указать его при\n> помощи анонимной функции похожей на эту:\n> ```php\n> [\n>     function () {\n>         return Yii::$app->getModule('user');\n>     },\n> ]\n> ```\n\nВ процессе [начальной загрузки](runtime-bootstrapping.md), каждый компонент будет создан. Если класс компонента имеет\nинтерфейс [[yii\\base\\BootstrapInterface]], то также будет вызван метод [[yii\\base\\BootstrapInterface::bootstrap()|bootstrap()]].\n\nЕще одним практическим примером является конфигурация [базового шаблона приложения](start-installation.md), в котором\nмодули `debug` и `gii` указаны как `bootstrap` компоненты, когда приложение находится в отладочном режиме.\n\n```php\nif (YII_ENV_DEV) {\n    // настройка конфигурации для окружения 'dev'\n    $config['bootstrap'][] = 'debug';\n    $config['modules']['debug'] = 'yii\\debug\\Module';\n\n    $config['bootstrap'][] = 'gii';\n    $config['modules']['gii'] = 'yii\\gii\\Module';\n}\n```\n\n> Note: Указывание слишком большого количества компонентов в [`bootstrap`](runtime-bootstrapping.md) приведет\nк снижению производительности приложения, потому что для каждого запроса одно и то же количество компонентов должно\nбыть загружено. Таким образом вы должны использовать начальную загрузку разумно.\n\n\n#### [[yii\\web\\Application::catchAll|catchAll]] <span id=\"catchAll\"></span>\n\nДанное свойство поддерживается только [[yii\\web\\Application|веб приложениями]]. Оно указывает\n[действие контроллера](structure-controllers.md), которое должно обрабатывать все входящие запросы от пользователя.\nВ основном это используется, когда приложения находится в режиме обслуживания и должно обрабатывать все запросы через\nодно действие.\n\nКонфигурация это массив, первый элемент которого, определяет маршрут действия. Остальные элементы в формате пара\nключ-значение задают дополнительные параметры, которые должны быть переданы действию (методу контроллера actionXXX). \nНапример,\n\n```php\n[\n    'catchAll' => [\n        'offline/notice',\n        'param1' => 'value1',\n        'param2' => 'value2',\n    ],\n]\n```\n\n\n#### [[yii\\base\\Application::components|components]] <span id=\"components\"></span>\n\nДанное свойство является наиболее важным. Оно позволяет вам зарегистрировать список именованных компонентов, называемых\n[компоненты приложения](structure-application-components.md), которые Вы можете использовать в других местах.\nНапример,\n\n```php\n[\n    'components' => [\n        'cache' => [\n            'class' => 'yii\\caching\\FileCache',\n        ],\n        'user' => [\n            'identityClass' => 'app\\models\\User',\n            'enableAutoLogin' => true,\n        ],\n    ],\n]\n```\n\nКаждый компонент приложения указан массивом в формате ключ-значение. Ключ представляет собой ID компонента приложения,\nв то время как значение представляет собой название класса или [конфигурацию](concept-configurations.md).\n\nВы можете зарегистрировать любой компонент в приложении, позже этот компонент будет глобально доступен через\nвыражение `\\Yii::$app->componentID`.\n\nБолее подробная информация приведена в разделе [Компоненты приложения](structure-application-components.md).\n\n\n#### [[yii\\base\\Application::controllerMap|controllerMap]] <span id=\"controllerMap\"></span>\n\nДанное свойство позволяет вам задавать соответствия(mapping) между ID контроллера и произвольным классом контроллера.\nПо-умолчанию, Yii задает соответствие между ID контроллера и его классом согласно данному [соглашению](#controllerNamespace)\n(таким образом, ID `post` будет соответствовать `app\\controllers\\PostController` ). Задавая эти свойства вы можете\nпереопределить соответствия для необходимых контроллеров. В приведенном ниже примере, `account` будет соответствовать\nконтроллеру `app\\controllers\\UserController`, в то время как `article` будет соответствовать контроллеру\n`app\\controllers\\PostController`.\n\n```php\n[\n    'controllerMap' => [\n        'account' => 'app\\controllers\\UserController',\n        'article' => [\n            'class' => 'app\\controllers\\PostController',\n            'enableCsrfValidation' => false,\n        ],\n    ],\n]\n```\n\nКлючами данного свойства являются ID контроллеров, а значениями являются соответствующие названия \nклассов(полное название класса с пространством имен) контроллера или [конфигурация](concept-configurations.md).\n\n\n#### [[yii\\base\\Application::controllerNamespace|controllerNamespace]] <span id=\"controllerNamespace\"></span>\n\nДанное свойство указывает пространство имен, в котором по умолчанию должны находится названия классов контроллеров.\nПо-умолчанию значение равно `app\\controllers`. Если ID контроллера `post`, то согласно соглашению, соответствующий класс\nконтроллера (без пространства имен) будет равен `PostController`, а полное название класса будет равно `app\\controllers\\PostController`.\n\nКласс контроллера может также находиться в поддиректории директории, соответствующей этому пространству имен.\nНапример, ID контроллера `admin/post`, будет соответствовать полное имя класса контроллера `app\\controllers\\admin\\PostController`.\n\nОчень важно, чтобы полное имя класса контроллера могло быть использовано [автозагрузкой](concept-autoloading.md) и\nсоответствующее пространство имен вашего контроллера соответствовало данному свойству. Иначе, Вы получите ошибку\n\"Страница не найдена\", при доступе к приложению.\n\nВ случае, если вы хотите переопределить соответствия как описано выше, вы можете настроить свойство [controllerMap](#controllerMap).\n\n\n#### [[yii\\base\\Application::language|language]] <span id=\"language\"></span>\n\nДанное свойство указывает язык приложения, на котором содержимое страницы должно быть отображено конечному пользователю.\nПо-умолчанию значение данного свойства равно `en`, что означает \"Английский\". Если ваше приложение должно поддерживать\nнесколько языков, вы должны настроить данное свойство.\n\nЗначение данного свойства определяется различными аспектами [интернационализации](tutorial-i18n.md), в том числе\nпереводом сообщений, форматированием дат, форматированием чисел, и т. д. Например, виджет [[yii\\jui\\DatePicker]]\nиспользует данное свойство для определения по умолчанию языка, на котором должен быть отображен календарь и формат данных\nдля календаря.\n\nРекомендуется что вы будете указывать язык в рамках стандарта [IETF](https://en.wikipedia.org/wiki/IETF_language_tag).\nНапример, для английского языка используется `en`, в то время как для английского в США - `en-US`.\n\nБолее детальная информация приведена в разделе [Интернационализация](tutorial-i18n.md).\n\n\n#### [[yii\\base\\Application::modules|modules]] <span id=\"modules\"></span>\n\nДанное свойство указывает [модули](structure-modules.md), которые содержатся в приложении.\n\nЗначениями свойства могут быть массивы имен классов модулей или [конфигураций](concept-configurations.md), а ключами -\nID модулей. Например,\n\n```php\n[\n    'modules' => [\n        // a \"booking\" module specified with the module class\n        'booking' => 'app\\modules\\booking\\BookingModule',\n\n        // a \"comment\" module specified with a configuration array\n        'comment' => [\n            'class' => 'app\\modules\\comment\\CommentModule',\n            'db' => 'db',\n        ],\n    ],\n]\n```\n\nБолее детальная информация приведена в разделе [Модули](structure-modules.md).\n\n\n#### [[yii\\base\\Application::name|name]] <span id=\"name\"></span>\n\nСвойство указывает название приложения, которое может быть показано конечным пользователям. В отличие от\nсвойства [[yii\\base\\Application::id|id]], которое должно быть уникальным, значение данного свойства нужно в\nосновном для отображения и не обязательно должно быть уникальным.\n\nЕсли ваш код не использует данное свойство, то вы можете не настраивать его.\n\n\n#### [[yii\\base\\Application::params|params]] <span id=\"params\"></span>\n\nДанное свойство указывает массив глобально доступных параметров приложения. Вместо того, чтобы использовать\nжестко фиксированные числа и строки в вашем коде, лучше объявить их параметрами приложения в едином месте и \nиспользовать в нужных вам местах кода. Например, вы можете определить размер превью для изображений следующим образом:\n\n```php\n[\n    'params' => [\n        'thumbnail.size' => [128, 128],\n    ],\n]\n```\n\nЗатем, когда вам нужно использовать данные значения в вашем коде, вы делаете это как представлено ниже:\n\n```php\n$size = \\Yii::$app->params['thumbnail.size'];\n$width = \\Yii::$app->params['thumbnail.size'][0];\n```\n\nЕсли позже вам понадобится изменить размер превью изображений, вам нужно только изменить это значение в настройке\nприложения, не касаясь зависимого кода.\n\n\n#### [[yii\\base\\Application::sourceLanguage|sourceLanguage]] <span id=\"sourceLanguage\"></span>\n\nДанное свойство указывает язык на котором написан код приложения. По-умолчанию значение равно `'en-US'`, что означает\n\"Английский\" (США). Вы должны настроить данное свойство соответствующим образом, если содержимое в вашем коде является не\nанглийским языком.\n\nАналогично свойству [language](#language), вы должны указать данное свойство в рамках стандарта [IETF](https://en.wikipedia.org/wiki/IETF_language_tag).\nНапример, для английского языка используется `en`, в то время как для английского в США - `en-US`.\n\nБолее детальная информация приведена в разделе [Интернационализация](tutorial-i18n.md).\n\n\n#### [[yii\\base\\Application::timeZone|timeZone]] <span id=\"timeZone\"></span>\n\nДанное свойство предоставляет альтернативный способ установки временной зоны в процессе работы приложения.\nПутем указания данного свойства, вы по существу вызываете PHP функцию \n[date_default_timezone_set()](https://www.php.net/manual/ru/function.date-default-timezone-set.php). Например,\n\n```php\n[\n\t// Europe/Moscow для России (прим. пер.)\n    'timeZone' => 'America/Los_Angeles',\n]\n```\n\n#### [[yii\\base\\Application::version|version]] <span id=\"version\"></span>\n\nДанное свойство указывает версию приложения. По-умолчанию значение равно `'1.0'`. Вы можете не настраивать это свойство, если\nваш код не использует его.\n\n\n### Полезные свойства <span id=\"useful-properties\"></span>\n\nСвойства, указанные в данном подразделе, не являются часто конфигурируемыми, т. к. их значения по умолчанию\nсоответствуют общепринятым соглашениям. Однако, вы можете их настроить, если вам нужно использовать другие\nсоглашения.\n\n\n#### [[yii\\base\\Application::charset|charset]] <span id=\"charset\"></span>\n\nСвойство указывает кодировку, которую использует приложение. По-умолчанию значение равно `'UTF-8'` - должно быть\nоставлено как есть для большинства приложения, только если вы не работаете с устаревшим кодом, который использует большее\nколичество данных не юникода.\n\n\n#### [[yii\\base\\Application::defaultRoute|defaultRoute]] <span id=\"defaultRoute\"></span>\n\nСвойство указывает [маршрут](runtime-routing.md), который должно использовать приложение, когда он не указан\nво входящем запросе. Маршрут может состоять из ID модуля, ID контроллера и/или ID действия. Например, `help`,\n`post/create`, `admin/post/create`. Если действие не указано, то будет использовано значение по умолчанию\nуказанное в [[yii\\base\\Controller::defaultAction]].\n\nДля [[yii\\web\\Application|веб приложений]], значение по умолчанию для данного свойства равно `'site'`, что означает\nконтроллер `SiteController` и его действие по умолчанию должно быть использовано. Таким образом, если вы попытаетесь\nполучить доступ к приложению не указав маршрут, оно покажет вам результат действия `app\\controllers\\SiteController::actionIndex()`.\n\nДля [[yii\\console\\Application|консольных приложений]], значение по умолчанию равно `'help'`, означающее, что встроенная\nкоманда [[yii\\console\\controllers\\HelpController::actionIndex()]] должна быть использована. Таким образом, если вы\nвыполните команду `yii` без аргументов, вам будет отображена справочная информация.\n\n\n#### [[yii\\base\\Application::extensions|extensions]] <span id=\"extensions\"></span>\n\nДанное свойство указывает список [расширений](structure-extensions.md), которые установлены и используются приложением.\nПо-умолчанию, значением данного свойства будет массив, полученный из файла `@vendor/yiisoft/extensions.php`. Файл `extensions.php`\nгенерируется и обрабатывается автоматически, когда вы используете [Composer](https://getcomposer.org) для установки расширений.\nТаким образом, в большинстве случаев вам не нужно настраивать данное свойство.\n\nВ особых случаях, когда вы хотите обрабатывать расширения в ручную, вы можете указать данное свойство следующим образом:\n\n```php\n[\n    'extensions' => [\n        [\n            'name' => 'extension name',\n            'version' => 'version number',\n            'bootstrap' => 'BootstrapClassName',  // опционально, может быть также массив конфигурации\n            'alias' => [  // опционально\n                '@alias1' => 'to/path1',\n                '@alias2' => 'to/path2',\n            ],\n        ],\n\n        // ... аналогично для остальных расширений ...\n\n    ],\n]\n```\n\nСвойство является массивом спецификаций расширений. Каждое расширение указано массивом, состоящим из элементов `name` и `version`.\nЕсли расширение должно быть выполнено в процессе [начальной загрузки](runtime-bootstrapping.md), то следует указать `bootstrap`\nэлемент, который может являться именем класса или [конфигурацией](concept-configurations.md).\nРасширение также может определять несколько [псевдонимов](concept-aliases.md).\n\n\n#### [[yii\\base\\Application::layout|layout]] <span id=\"layout\"></span>\n\nДанное свойство указывает имя шаблона по умолчанию, который должен быть использовать при формировании [представлений](structure-views.md).\nЗначение по умолчанию равно `'main'`, означающее, что должен быть использован шаблон `main.php` в [папке шаблонов](#layoutPath).\nЕсли оба свойства [папка шаблонов](#layoutPath) и [папка представлений](#viewPath) имеют значение по умолчанию,\nто файл шаблона по умолчанию может быть представлен псевдонимом пути как `@app/views/layouts/main.php`.\n\nДля отключения использования шаблона, вы можете указать данное свойство как `false`, хотя это используется очень редко.\n\n\n#### [[yii\\base\\Application::layoutPath|layoutPath]] <span id=\"layoutPath\"></span>\n\nСвойство указывает путь, по которому следует искать шаблоны. Значение по умолчанию равно `layouts`, означающее подпапку\nв [папке представлений](#viewPath). Если значение [папки представлений](#viewPath) является значением по умолчанию, то\nпапка шаблонов по умолчанию может быть представлена псевдонимом пути как `@app/views/layouts`.\n\nВы можете настроить данное свойство как папку так и как [псевдоним](concept-aliases.md).\n\n\n#### [[yii\\base\\Application::runtimePath|runtimePath]] <span id=\"runtimePath\"></span>\n\nСвойство указывает путь, по которому хранятся временные файлы, такие как: лог файлы, кэш файлы.\nПо-умолчанию значение равно папке, которая представлена псевдонимом пути `@app/runtime`.\n\nВы можете настроить данное свойство как папку или как [псевдоним](concept-aliases.md) пути. Обратите внимание,\nчто данная папка должна быть доступна для записи, процессом, который запускает приложение. Также папка должна быть\nзащищена от доступа конечными пользователями, хранимые в ней временные файлы могут содержать важную информацию.\n\nДля упрощения работы с данной папкой, Yii предоставляет предопределенный псевдоним пути `@runtime`.\n\n\n#### [[yii\\base\\Application::viewPath|viewPath]] <span id=\"viewPath\"></span>\n\nДанное свойство указывает базовую папку,где содержаться все файлы представлений. Значение по умолчанию представляет\nсобой псевдоним `@app/views`. Вы можете настроить данное свойство как папку так и как [псевдоним](concept-aliases.md).\n\n\n#### [[yii\\base\\Application::vendorPath|vendorPath]] <span id=\"vendorPath\"></span>\n\nСвойство указывает папку сторонних библиотек, которые используются и управляются [Composer](https://getcomposer.org).\nОна содержит все сторонние библиотеки используемые приложением, включая Yii фреймворк. Значение по умолчанию\nпредставляет собой псевдоним `@app/vendor`.\n\nВы можете настроить данное свойство как папку так и как [псевдоним](concept-aliases.md). При изменении данного свойства,\nубедитесь что вы также изменили соответствующим образом настройки Composer.\n\nДля упрощения работы с данной папкой, Yii предоставляет предопределенный псевдоним пути `@vendor`.\n\n\n#### [[yii\\console\\Application::enableCoreCommands|enableCoreCommands]] <span id=\"enableCoreCommands\"></span>\n\nДанное свойство поддерживается только [[yii\\console\\Application|консольными приложениями]]. Оно указывает\nнужно ли использовать встроенные в Yii консольные команды. Значение по умолчанию равно `true`.\n\n\n## События приложения <span id=\"application-events\"></span>\n\nВ течение жизненного цикла приложения, возникает несколько событий. Вы можете назначать обработчики событий в\nконфигурации приложения следующим образом:\n\n```php\n[\n    'on beforeRequest' => function ($event) {\n        // ...\n    },\n]\n```\n\nИспользование синтаксиса `on eventName` детально описано в разделе [Конфигурации](concept-configurations.md#configuration-format).\n\nТакже вы можете назначить обработчики событий в процессе начальной [загрузки приложения](runtime-bootstrapping.md), сразу после того\nкак приложение будет создано. Например,\n\n```php\n\\Yii::$app->on(\\yii\\base\\Application::EVENT_BEFORE_REQUEST, function ($event) {\n    // ...\n});\n```\n\n### [[yii\\base\\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_REQUEST]] <span id=\"beforeRequest\"></span>\n\nДанное событие возникает *до* того, как приложение начинает обрабатывать входящий запрос. \nНастоящее имя события - `beforeRequest`.\n\nНа момент возникновения данного события, объект приложения уже создан и проинициализирован. Таким образом, это\nявляется хорошим местом для вставки вашего кода с помощью событий, для перехвата управления обработкой запроса.\nНапример, обработчик события, может динамически подставлять язык приложения [[yii\\base\\Application::language]] в зависимости\nот некоторых параметров.\n\n\n### [[yii\\base\\Application::EVENT_AFTER_REQUEST|EVENT_AFTER_REQUEST]] <span id=\"afterRequest\"></span>\n\nДанное событие возникает *после* того, как приложение заканчивает обработку запроса, но *до* того как произойдет \nотправка ответа. Настоящее имя события - `afterRequest`.\n\nНа момент возникновения данного события, обработка запроса завершена и вы можете воспользоваться этим для произведения постобработки\nзапроса, с целью настройки ответа.\n\nОбратите внимание, что в компоненте [[yii\\web\\Response|response]] также возникают события в процессе отправки данных\nконечному пользователю. Эти события возникают *после* текущего события.\n\n\n### [[yii\\base\\Application::EVENT_BEFORE_ACTION|EVENT_BEFORE_ACTION]] <span id=\"beforeAction\"></span>\n\nСобытие возникает *до* того, как будет выполнено [действие контроллера](structure-controllers.md).\nНастоящее имя события - `beforeAction`.\n\nСобытие является объектом [[yii\\base\\ActionEvent]]. Обработчик события может устанавливать\nсвойство [[yii\\base\\ActionEvent::isValid]] равным `false` для предотвращения выполнения действия.\n\nНапример,\n\n```php\n[\n    'on beforeAction' => function ($event) {\n        if (некоторое условие) {\n            $event->isValid = false;\n        } else {\n        }\n    },\n]\n```\n\nОбратите внимание что то же самое событие `beforeAction` возникает в [модулях](structure-modules.md) и\n[контроллерах](structure-controllers.md). Объекты приложения являются первыми, кто возбуждает данные события,\nследуя за модулями (если таковые имеются) и в конце контроллерами. Если обработчик события устанавливает\nсвойство [[yii\\base\\ActionEvent::isValid]] равным `false`, все последующие события не возникнут.\n\n\n### [[yii\\base\\Application::EVENT_AFTER_ACTION|EVENT_AFTER_ACTION]] <span id=\"afterAction\"></span>\n\nСобытие возникает *после* выполнения [действия контроллера](structure-controllers.md).\nНастоящее имя события - `afterAction`.\n\nСобытие является объектом [[yii\\base\\ActionEvent]]. Через свойство [[yii\\base\\ActionEvent::result]] обработчик события\nможет получить доступ и изменить значение выполнения действия контроллера.\n\nНапример,\n\n```php\n[\n    'on afterAction' => function ($event) {\n        if (некоторое условие) {\n            // обрабатываем $event->result\n        } else {\n        }\n    },\n]\n```\n\nОбратите внимание, что то же самое событие `afterAction` возникает в [модулях](structure-modules.md) и\n[контроллерах](structure-controllers.md). Эти объекты возбуждают событие в обратном порядке, если сравнивать с `beforeAction`.\nТаким образом, контроллеры являются первыми, где возникает данное событие, затем в модулях (если таковые имеются),\nи наконец в приложениях.\n\n\n## Жизненный цикл приложения <span id=\"application-lifecycle\"></span>\n\nКогда [входной скрипт](structure-entry-scripts.md) выполняется для обработки запроса, приложение\nбудет развиваться согласно следующему жизненному циклу:\n\n1. Входной скрипт загружает конфигурацию приложения в качестве массива;\n2. Входной скрипт создаёт новый объект приложения:\n  * Вызывается метод [[yii\\base\\Application::preInit()|preInit()]], который настраивает некоторые \n    жизненно важные свойства приложения, такие как [[yii\\base\\Application::basePath|basePath]];\n  * Регистрируется [[yii\\base\\Application::errorHandler|обработчик ошибок]];\n  * Настраиваются свойства приложения;\n  * Вызывается метод [[yii\\base\\Application::init()|init()]], который затем вызывает метод\n    [[yii\\base\\Application::bootstrap()|bootstrap()]] для начальной загрузки компонентов.\n3. Входной скрипт вызывает метод [[yii\\base\\Application::run()]] для запуска приложения:\n  * Возникает событие [[yii\\base\\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_REQUEST]];\n  * Обработка запроса: разбор информации запроса в [маршрут](runtime-routing.md) с соответствующими параметрами;\n    создание объектов модуля, контроллера и действия согласно указанному маршруту; запуск действия;\n  * Возникает событие [[yii\\base\\Application::EVENT_AFTER_REQUEST|EVENT_AFTER_REQUEST]];\n  * Ответ отсылается конечному пользователю.\n4. Входной скрипт получает значение статуса выхода от приложения и заканчивает обработку запроса.\n"
  },
  {
    "path": "docs/guide-ru/structure-assets.md",
    "content": "Ресурсы\n======\n\nРесурс в Yii это файл который может быть задан в Web странице. Это может быть CSS файл, JavaScript файл, изображение или видео файл и т.д. Ресурсы располагаются в Web доступных директориях и обслуживаются непосредственно Web серверами.\n\nЖелательно, управлять ресурсами программно. Например, при использовании виджета [[yii\\jui\\DatePicker]] в странице, автоматически включаются необходимые CSS и JavaScript файлы, вместо того чтобы просить Вас вручную найти эти файлы и включить их. И когда Вы обновляете виджет до новой версии, будут автоматически использованы новые версии файлов-ресурсов. В этом руководстве будет описана мощная возможность управления ресурсами представленная в Yii.\n\n## Комплекты ресурсов <span id=\"asset-bundles\"></span>\n\nYii управляет ресурсами как единицей *комплекта ресурсов*. Комплект ресурсов - это простой набор ресурсов расположенных в директории. Когда Вы регистрируете комплект ресурсов в [представлении](structure-views.md), в отображаемой Web странице включается набор CSS и JavaScript файлов.\n\n## Задание Комплекта Ресурсов<span id=\"defining-asset-bundles\"></span>\n\nКомплект ресурсов определяется как PHP класс расширяющийся от [[yii\\web\\AssetBundle]]. Имя комплекта соответствует полному имени PHP класса (без ведущей обратной косой черты - backslash \"\\\"). Класс комплекта ресурсов должен быть в состоянии [возможности автозагрузки](concept-autoloading.md). При задании комплекта ресурсов обычно указывается где ресурсы находятся, какие CSS и JavaScript файлы содержит комплект, и как комплект зависит от других комплектов.\n\nСледующий код задаёт основной комплект ресурсов используемый в [шаблоне базового приложения](start-installation.md):\n\n```php\n<?php\n\nnamespace app\\assets;\n\nuse yii\\web\\AssetBundle;\n\nclass AppAsset extends AssetBundle\n{\n    public $basePath = '@webroot';\n    public $baseUrl = '@web';\n    public $css = [\n        'css/site.css',\n    ];\n    public $js = [\n    ];\n    public $depends = [\n        'yii\\web\\YiiAsset',\n        'yii\\bootstrap\\BootstrapAsset',\n    ];\n}\n```\n\nВ коде выше класс `AppAsset` указывает, что файлы ресурса находятся в директории `@webroot`, которой соответствует URL `@web`; комплект содержит единственный CSS файл `css/site.css` и не содержит JavaScript файлов; комплект зависит от двух других комплектов: [[yii\\web\\YiiAsset]] и [[yii\\bootstrap\\BootstrapAsset]]. Более детальное объяснение о свойствах [[yii\\web\\AssetBundle]] может быть найдено ниже:\n\n* [[yii\\web\\AssetBundle::sourcePath|sourcePath]]: задаёт корневую директорию содержащую файлы ресурса в этом комплекте. Это свойство должно быть установлено если корневая директория не доступна из Web. В противном случае, Вы должны установить [[yii\\web\\AssetBundle::basePath|basePath]] свойство и [[yii\\web\\AssetBundle::baseUrl|baseUrl]] свойство вместо текущего. Здесь могут быть использованы [псевдонимы путей](concept-aliases.md).\n* [[yii\\web\\AssetBundle::basePath|basePath]]: задаёт Web доступную директорию, которая содержит файлы ресурсов текущего комплекта. Когда Вы задаёте свойство [[yii\\web\\AssetBundle::sourcePath|sourcePath]] [Менеджер ресурсов](#asset-manager) опубликует ресурсы текущего комплекта в Web доступную директорию и перезапишет соответственно данное свойство. Вы должны задать данное свойство если Ваши файлы ресурсов уже в Web доступной директории и не нужно опубликовывать ресурсы. Здесь могут быть использованы [псевдонимы путей](concept-aliases.md).\n* [[yii\\web\\AssetBundle::baseUrl|baseUrl]]: задаёт URL соответствующий директории [[yii\\web\\AssetBundle::basePath|basePath]]. Также как и для [[yii\\web\\AssetBundle::basePath|basePath]], если Вы задаёте свойство [[yii\\web\\AssetBundle::sourcePath|sourcePath]] [Менеджер ресурсов](#asset-manager) опубликует ресурсы и перезапишет это свойство соответственно. Здесь могут быть использованы [псевдонимы путей](concept-aliases.md).\n* [[yii\\web\\AssetBundle::js|js]]: массив, перечисляющий JavaScript файлы, содержащиеся в данном комплекте. Заметьте, что только прямая косая черта (forward slash - \"/\") может быть использована, как разделитель директорий. Каждый JavaScript файл может быть задан в одном из следующих форматов:\n  - относительный путь, представленный локальным JavaScript файлом (например `js/main.js`). Актуальный путь файла может быть определён путём добавления [[yii\\web\\AssetManager::basePath]] к относительному пути, и актуальный URL файла может быть определён путём добавления [[yii\\web\\AssetManager::baseUrl]] к относительному пути.\n  - абсолютный URL, представленный внешним JavaScript файлом. Например,\n    `https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js` или\n    `//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js`.\n* [[yii\\web\\AssetBundle::css|css]]: массив, перечисляющий CSS файлы, содержащиеся в данном комплекте. Формат этого массива такой же, как и у [[yii\\web\\AssetBundle::js|js]].\n* [[yii\\web\\AssetBundle::depends|depends]]: массив, перечисляющий имена комплектов ресурсов, от которых зависит данный комплект.\n* [[yii\\web\\AssetBundle::jsOptions|jsOptions]]: задаёт параметры, которые будут относится к методу [[yii\\web\\View::registerJsFile()]], когда он вызывается для регистрации *каждого* JavaScript файла данного комплекта.\n* [[yii\\web\\AssetBundle::cssOptions|cssOptions]]: задаёт параметры, которые будут приняты методом [[yii\\web\\View::registerCssFile()]], когда он вызывается для регистрации *каждого* CSS файла данного комплекта.\n* [[yii\\web\\AssetBundle::publishOptions|publishOptions]]: задаёт параметры, которые будут приняты методом [[yii\\web\\AssetManager::publish()]], когда метод будет вызван, опубликуются исходные файлы ресурсов в Web директории. Этот параметр используется только в том случае, если задаётся свойство [[yii\\web\\AssetBundle::sourcePath|sourcePath]].\n\n### Расположение ресурсов<span id=\"asset-locations\"></span>\n\nРесурсы, в зависимости от их расположения, могут быть классифицированы как:\n\n* исходные ресурсы: файлы ресурсов, расположенные вместе с исходным кодом PHP, которые не могут быть непосредственно доступны через Web. Для того, чтобы использовать исходные ресурсы на странице, они должны быть скопированы в Web директорию и превратиться в так называемые опубликованные ресурсы. Этот процесс называется *публикацией ресурсов*, который более подробно описан ниже\n* опубликованные ресурсы: файлы ресурсов, расположенные в Web директории и, таким образом, могут быть напрямую доступны через Web.\n* внешние ресурсы: файлы ресурсов, расположенные на другом Web сервере, отличного от веб-хостинга вашего приложения.\n\nПри определении класса комплекта ресурсов, если Вы задаёте свойство [[yii\\web\\AssetBundle::sourcePath|sourcePath]], это означает, что любые перечисленные ресурсы, используя относительные пути, будут рассматриваться как исходные ресурсы. Если Вы не задаёте данное свойство, это означает, что эти ресурсы - это опубликованные ресурсы (в этом случае Вам следует указать [[yii\\web\\AssetBundle::basePath|basePath]] и [[yii\\web\\AssetBundle::baseUrl|baseUrl]], чтобы дать знать Yii где ресурсы располагаются).\n\nРекомендуется размещать ресурсы, принадлежащие приложению, в Web директорию, для того, чтобы избежать не нужного процесса публикации ресурсов. Вот почему `AppAsset` в предыдущем примере задаёт [[yii\\web\\AssetBundle::basePath|basePath]] вместо [[yii\\web\\AssetBundle::sourcePath|sourcePath]].\n\nДля [расширений](structure-extensions.md), в связи с тем, что их ресурсы располагаются вместе с их исходным кодом в директориях, которые не являются веб-доступными, необходимо указать свойство [[yii\\web\\AssetBundle::sourcePath|sourcePath]] при задании класса комплекта ресурсов для них.\n\n> Note: Не используйте `@webroot/assets` как [[yii\\web\\AssetBundle::sourcePath|source path]]. Эта директория по умолчанию используется менеджером ресурсов [[yii\\web\\AssetManager|asset manager]] для сохранения файлов ресурсов, опубликованных из их исходного месторасположения. Любое содержимое этой директории расценивается как временное и может быть удалено.\n\n### Зависимости ресурсов <span id=\"asset-dependencies\"></span>\n\nКогда Вы включаете несколько CSS или JavaScript файлов в Web страницу, они должны следовать в определенном порядке, <b> чтобы избежать переопределения при выдаче</b>. Например, если Вы используете виджет jQuery UI в Web странице, вы должны убедиться, что jQuery JavaScript файл был включен до jQuery UI JavaScript файла. Мы называем такой порядок зависимостью между ресурсами.\n\nЗависимости ресурсов в основном указываются через свойство [[yii\\web\\AssetBundle::depends]]. Например в `AppAsset`, комплект ресурсов зависит от двух других комплектов ресурсов: [[yii\\web\\YiiAsset]] и [[yii\\bootstrap\\BootstrapAsset]], что обозначает, что CSS и JavaScript файлы `AppAsset` будут включены *после* файлов этих двух комплектов зависимостей.\n\nЗависимости ресурсов являются также зависимыми. Это значит, что если комплект А зависит от В, который зависит от С, то А тоже зависит от С.\n\n### Параметры ресурсов <span id=\"asset-options\"></span>\n\nВы можете задать свойства [[yii\\web\\AssetBundle::cssOptions|cssOptions]] и [[yii\\web\\AssetBundle::jsOptions|jsOptions]], чтобы настроить путь для включения CSS и JavaScript файлов в страницу. Значения этих свойств будут приняты методами [[yii\\web\\View::registerCssFile()]] и [[yii\\web\\View::registerJsFile()]] соответственно, когда они (методы) вызываются [представлением](structure-views.md) происходит включение CSS и JavaScript файлов.\n\n> Note: Параметры, заданные в комплекте класса применяются для *каждого* CSS/JavaScript-файла в комплекте. Если Вы хотите использовать различные параметры для разных файлов, Вы должны создать раздельные комплекты ресурсов, и использовать одну установку параметров для каждого комплекта.\n\nНапример, условно включим CSS файл для браузера IE9 или ниже. Для этого Вы можете использовать следующий параметр:\n\n```php\npublic $cssOptions = ['condition' => 'lte IE9'];\n```\n\nЭто вызовет CSS файл из комплекта, который будет включен в страницу, используя следующие HTML теги:\n\n```html\n<!--[if lte IE9]>\n<link rel=\"stylesheet\" href=\"path/to/foo.css\">\n<![endif]-->\n```\n\nДля того чтобы обернуть созданную CSS ссылку в тег `<noscript>`, Вы можете настроить `cssOptions` следующим образом:\n\n```php\npublic $cssOptions = ['noscript' => true];\n```\n\nДля включения JavaScript файла в head раздел страницы (по умолчанию, JavaScript файлы включаются в конец раздела body) используйте следующий параметр:\n\n```php\npublic $jsOptions = ['position' => \\yii\\web\\View::POS_HEAD];\n```\n\nПо умолчанию, когда комплект ресурсов публикуется, всё содержимое в заданной директории [[yii\\web\\AssetBundle::sourcePath]] будет опубликовано. Вы можете настроить это поведение, сконфигурировав свойство [[yii\\web\\AssetBundle::publishOptions|publishOptions]]. Например, опубликовать одну или несколько поддиректорий [[yii\\web\\AssetBundle::sourcePath]] в классе комплекта ресурсов Вы можете в следующим образом:\n\n```php\n<?php\nnamespace app\\assets;\n\nuse yii\\web\\AssetBundle;\n\nclass FontAwesomeAsset extends AssetBundle \n{\n    public $sourcePath = '@bower/font-awesome'; \n    public $css = [ \n        'css/font-awesome.min.css', \n    ]; \n    public $publishOptions = [\n        'only' => [\n            'fonts/*',\n            'css/*',\n        ]\n    ];\n}  \n```\n\nБолее сложную логику можно реализовать с помощью переопределения `init()`. Ниже указан пример публикации поддиректорий этим способом:\n\n```php\n<?php\nnamespace app\\assets;\n\nuse yii\\web\\AssetBundle;\n\nclass FontAwesomeAsset extends AssetBundle \n{\n    public $sourcePath = '@bower/font-awesome'; \n    public $css = [ \n        'css/font-awesome.min.css', \n    ]; \n    \n    public function init()\n    {\n        parent::init();\n        $this->publishOptions['beforeCopy'] = function ($from, $to) {\n            if (basename(dirname($from)) !== 'font-awesome') {\n                return true;\n            }\n            $dirname = basename($from);\n            return $dirname === 'fonts' || $dirname === 'css';\n        };\n    }\n}  \n```\n\nВ выше указанном примере определён комплект ресурсов для [пакета \"fontawesome\"](https://fontawesome.com/). Задан параметр публикации `beforeCopy`, здесь только `fonts` и `css` поддиректории будут опубликованы.\n\n### Установка ресурсов Bower и NPM<span id=\"bower-npm-assets\"></span>\n\nБольшинство JavaScript/CSS пакетов управляются [Bower](https://bower.io/) и/или [NPM](https://www.npmjs.com/).\nВ мире PHP мы испольуем Composer для управления зависимостями, но он не позволяет устанавливать пакеты Bower и NPM, просто указывая их в `composer.json`.\n\nЧтобы получить такую возможность, нужно немного настроить Composer. Существует два варианта:\n\n_____\n\n##### Используя репозиторий asset-packagist\n\nЭтот способ удовлетворяет потребности большинства проектов, которым нужны Bower или NPM пакеты.\n\n> Note: Начиная с версии 2.0.13, Basic и Advanced шаблоны приложений уже сконфигурированы для использования asset-packagist,\n  так что этот раздел можно пропустить.\n\nВ файле `composer.json` вашего проекта, добавьте следующие строки:\n\n```json\n\"repositories\": [\n    {\n        \"type\": \"composer\",\n        \"url\": \"https://asset-packagist.org\"\n    }\n]\n```\n\nНастройте [алиасы](concept-aliases.md) `@npm` и `@bower` в файле [конфигурации вашего приложения](concept-configurations.md):\n\n```php\n$config = [\n    ...\n    'aliases' => [\n        '@bower' => '@vendor/bower-asset',\n        '@npm'   => '@vendor/npm-asset',\n    ],\n    ...\n];\n```\n\nПосетите [asset-packagist.org](https://asset-packagist.org) чтобы узнать, как это работает.\n\n##### Используя fxp/composer-asset-plugin\n\nПо сравнению с asset-packagist, composer-asset-plugin не требует изменять конфигурацию приложения. Вместо этого, требуется\nустановить специальный глобальный пакет Composer, выполнив следующую команду:\n\n```bash\ncomposer global require \"fxp/composer-asset-plugin:^1.4.1\"\n```\n\nЭта команда устанавливает [composer asset plugin](https://github.com/fxpio/composer-asset-plugin) глобально,\nчто позволит устанавливать зависимости из Bower и NPM. После установки все проекты на вашем комьютере будут поддерживать\nустановку Bower и NPM пакетов, описанных в `composer.json`.\n\nДобавьте следующие строки в `composer.json` вашего проекта, чтобы указать директории, в которые будут установлены\nнеобходимые Bower и NPM пакеты:\n\n```json\n\"extra\": {\n    \"asset-installer-paths\": {\n        \"npm-asset-library\": \"vendor/npm\",\n        \"bower-asset-library\": \"vendor/bower\"\n    }\n}\n```\n\n> Note: `fxp/composer-asset-plugin` выполняет команду `composer update` существенно дольше, по сравнению с asset-packagist.\n\n____\n\nПосле настройки Composer для поддержки Bower и NPM пакетов:\n\n1. Исправьте файл `composer.json` Вашего приложения или расширения и включите пакет в список в раздел `require`. Следует использовать `bower-asset/PackageName` (для Bower пакетов) или `npm-asset/PackageName` (для NPM пакетов) для обращения к соответствующей библиотеке.\n2. Выполните `composer update`\n3. Создайте класс комплекта ресурсов и перечислите JavaScript/CSS файлы, которые Вы планируете использовать в Вашем приложении или расширении. Вы должны задать свойство [[yii\\web\\AssetBundle::sourcePath|sourcePath]] как `@bower/PackageName` или `@npm/PackageName`.\n\nЭто происходит потому, что Composer устанавливает Bower или NPM пакет в директорию, соответствующую этим псевдонимам.\n\n> Note: В некоторых пакетах файлы дистрибутива могут находиться в поддиректории. В этом случае, Вы должны задать поддиректорию как значение [[yii\\web\\AssetBundle::sourcePath|sourcePath]]. Например, [[yii\\web\\JqueryAsset]] использует `@bower/jquery/dist` вместо `@bower/jquery`.\n\n\n## Использование Комплекта Ресурсов<span id=\"using-asset-bundles\"></span>\n\nДля использования комплекта ресурсов, зарегистрируйте его в [представлении](structure-views.md) вызвав метод [[yii\\web\\AssetBundle::register()]]. Например, комплект ресурсов в представлении может быть зарегистрирован следующим образом:\n\n```php\nuse app\\assets\\AppAsset;\nAppAsset::register($this);  // $this - представляет собой объект представления\n```\n\n> Info: Метод [[yii\\web\\AssetBundle::register()]] возвращает объект комплекта ресурсов, содержащий информацию о публикуемых ресурсах, таких как [[yii\\web\\AssetBundle::basePath|basePath]] или [[yii\\web\\AssetBundle::baseUrl|baseUrl]].\n\nЕсли Вы регистрируете комплект ресурсов в других местах (т.е. не в представлении), Вы должны обеспечить необходимый объект представления. Например, при регистрации комплекта ресурсов в классе [widget](structure-widgets.md), Вы можете взять за объект представления `$this->view`.\n\nКогда комплект ресурсов регистрируется в представлении, Yii регистрирует все зависимые от него комплекты ресурсов. И, если комплект ресурсов расположен в директории не доступной из Web, то он будет опубликован в Web директории. Затем, когда представление отображает страницу, сгенерируются теги `<link>` и `<script>` для CSS и JavaScript файлов, перечисленных в регистрируемых комплектах. Порядок этих тегов определён зависимостью среди регистрируемых комплектов, и последовательность ресурсов перечислена в [[yii\\web\\AssetBundle::css]] и [[yii\\web\\AssetBundle::js]] свойствах.\n\n\n### Динамические Комплекты Ресурсов <span id=\"dynamic-asset-bundles\"></span>\n\nПоскольку комплект ресурсов это обычный PHP класс, он может содержать дополнительную логику, связанную с ним, и может\nкорректировать свои внутренние параметры динамически. Например, вы можете использовать сложную JavaScript библиотеку,\nкоторая предоставляет интернационализацию через отдельные исходные файлы: по одному на каждый поддерживаемый язык.\nТаким образом, вам нужно добавить определенный '.js' файл на вашу страницу, чтобы применить перевод для библиотеки.\nЭтого можно достичь, переопределив метод [[yii\\web\\AssetBundle::init()]]:\n\n```php\nnamespace app\\assets;\n\nuse yii\\web\\AssetBundle;\nuse Yii;\n\nclass SophisticatedAssetBundle extends AssetBundle\n{\n    public $sourcePath = '/path/to/sophisticated/src';\n    public $js = [\n        'sophisticated.js' // file, which is always used\n    ];\n\n    public function init()\n    {\n        parent::init();\n        $this->js[] = 'i18n/' . Yii::$app->language . '.js'; // dynamic file added\n    }\n}\n```\n\nКонкретный комплект ресурсов может быть также изменен через его экземпляр, возвращенный методом [[yii\\web\\AssetBundle::register()]].\nНапример:\n\n```php\nuse app\\assets\\SophisticatedAssetBundle;\nuse Yii;\n\n$bundle = SophisticatedAssetBundle::register(Yii::$app->view);\n$bundle->js[] = 'i18n/' . Yii::$app->language . '.js'; // dynamic file added\n```\n\n> Замечание: несмотря на то, что динамическая корректировка комплекта ресурсов поддерживается, ее использование - это\n  **плохая** практика, которая может привести к неожиданным побочных эффектам, и которой следует избегать.\n\n\n### Настройка Комплектов Ресурсов <span id=\"customizing-asset-bundles\"></span>\n\nYii управляет комплектами ресурсов через компонент приложения называемый `assetManager`, который реализован в [[yii\\web\\AssetManager]]. Путём настройки свойства [[yii\\web\\AssetManager::bundles]], возможно настроить поведение комплекта ресурсов. Например, комплект ресурсов [[yii\\web\\JqueryAsset]] по умолчанию использует `jquery.js` файл из установленного jquery Bower пакета. Для повышения доступности и производительности, можно использовать версию jquery на Google хостинге.\nЭто может быть достигнуто, настроив `assetManager` в конфигурации приложения следующим образом:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'bundles' => [\n                'yii\\web\\JqueryAsset' => [\n                    'sourcePath' => null,   // не опубликовывать комплект\n                    'js' => [\n                        '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js',\n                    ]\n                ],\n            ],\n        ],\n    ],\n];\n```\n\nМожно сконфигурировать несколько комплектов ресурсов аналогично через [[yii\\web\\AssetManager::bundles]]. Ключи массива должны быть именами класса (без впереди стоящей обратной косой черты) комплектов ресурсов, а значения массивов должны соответствовать [конфигурации массивов](concept-configurations.md).\n\n> Совет: Можно условно выбрать, какой из ресурсов будет использован в комплекте ресурсов. Следующий пример показывает, как можно использовать в разработке окружения `jquery.js` или `jquery.min.js` в противном случае:\n\n> ```php\n> 'yii\\web\\JqueryAsset' => [\n>     'js' => [\n>         YII_ENV_DEV ? 'jquery.js' : 'jquery.min.js'\n>     ]\n> ],\n> ```\n\nМожно запретить один или несколько комплектов ресурсов, связав `false` с именами комплектов ресурсов, которые Вы хотите сделать недоступными. Когда Вы регистрируете недоступный комплект ресурсов в представлении, обратите внимание, что зависимость комплектов будет зарегистрирована, и представление также не включит ни один из ресурсов комплекта в отображаемую страницу. Например, для запрета [[yii\\web\\JqueryAsset]] можно использовать следующую конфигурацию: \n\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'bundles' => [\n                'yii\\web\\JqueryAsset' => false,\n            ],\n        ],\n    ],\n];\n```\n\nМожно также запретить *все* комплекты ресурсов, установив [[yii\\web\\AssetManager::bundles]] как `false`.\n\nИмейте в виду, что настройки, установленные через [[yii\\web\\AssetManager::bundles]], применяются в момент создания комплекта\nресурсов, т.е. в момент срабатывания конструктора. Таким образом, любые изменения, которые произведены над экземпляром\nкомплекта ресурсов после этого, перекроют настройки, установленные на уровне [[yii\\web\\AssetManager::bundles]].\nВ частности, изменения, произведенные внутри метода [[yii\\web\\AssetBundle::init()]] или после регистрации комплекта ресурсов,\nимеют приоритет над настройками `AssetManager`.\nНиже приведены примеры, в которых значения, установленные через [[yii\\web\\AssetManager::bundles]] не возымеют никакого эффекта:\n\n```php\n// Program source code:\n\nnamespace app\\assets;\n\nuse yii\\web\\AssetBundle;\nuse Yii;\n\nclass LanguageAssetBundle extends AssetBundle\n{\n    // ...\n\n    public function init()\n    {\n        parent::init();\n        $this->baseUrl = '@web/i18n/' . Yii::$app->language; // can NOT be handled by `AssetManager`!\n    }\n}\n// ...\n\n$bundle = \\app\\assets\\LargeFileAssetBundle::register(Yii::$app->view);\n$bundle->baseUrl = YII_DEBUG ? '@web/large-files': '@web/large-files/minified'; // can NOT be handled by `AssetManager`!\n\n\n// Application config :\n\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'bundles' => [\n                'app\\assets\\LanguageAssetBundle' => [\n                    'baseUrl' => 'https://some.cdn.com/files/i18n/en' // makes NO effect!\n                ],\n                'app\\assets\\LargeFileAssetBundle' => [\n                    'baseUrl' => 'https://some.cdn.com/files/large-files' // makes NO effect!\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n\n### Привязка ресурсов<span id=\"asset-mapping\"></span>\n\nИногда необходимо исправить пути до файлов ресурсов, в нескольких комплектах ресурсов. Например, комплект А использует `jquery.min.js` версии 1.11.1, а комплект В использует `jquery.js` версии 2.1.1. Раньше Вы могли решить данную проблему, настраивая каждый комплект ресурсов по отдельности, но более простой способ - использовать *asset map* возможность, чтобы найти неверные ресурсы и исправить их. Сделать это можно, сконфигурировав свойство [[yii\\web\\AssetManager::assetMap]] следующим образом:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'assetMap' => [\n                'jquery.js' => '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js',\n            ],\n        ],\n    ],\n];\n```\n\nКлючи [[yii\\web\\AssetManager::assetMap|assetMap]] - это имена ресурсов, которые Вы хотите исправить, а значения - это требуемые пути для ресурсов. Когда регистрируется комплект ресурсов в представлении, каждый соответствующий файл ресурса в [[yii\\web\\AssetBundle::css|css]] или [[yii\\web\\AssetBundle::js|js]] массивах будет рассмотрен в соответствии с этой привязкой. И, если какой-либо из ключей найден, как последняя часть пути до файла ресурса (путь на который начинается с [[yii\\web\\AssetBundle::sourcePath]] по возможности), то соответствующее значение заменит ресурс и будет зарегистрировано в представлении. Например, путь до файла ресурса `my/path/to/jquery.js` - это соответствует ключу `jquery.js`.\n\n> Note: Ресурсы заданные только с использованием относительного пути могут использоваться в привязке ресурсов. Пути ресурсов должны быть абсолютные URLs или путь относительно [[yii\\web\\AssetManager::basePath]].\n\n\n### Публикация Ресурсов<span id=\"asset-publishing\"></span>\n\nКак уже было сказано выше, если комплект ресурсов располагается в директории которая не доступна из Web, эти ресурсы будут скопированы в Web директорию, когда комплект будет зарегистрирован в представлении. Этот процесс называется *публикацией ресурсов*, его автоматически выполняет [[yii\\web\\AssetManager|asset manager]].\n\nПо умолчанию, ресурсы публикуются в директорию `@webroot/assets` которая соответствует URL `@web/assets`. Можно настроить это местоположение сконфигурировав свойства [[yii\\web\\AssetManager::basePath|basePath]] и [[yii\\web\\AssetManager::baseUrl|baseUrl]].\n\nВместо публикации ресурсов путём копирования файлов, можно рассмотреть использование символических ссылок, если Ваша операционная система или Web сервер это разрешают. Эта функция может быть включена путем установки [[yii\\web\\AssetManager::linkAssets|linkAssets]] в `true`.\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'linkAssets' => true,\n        ],\n    ],\n];\n```\n\nС конфигурацией, установленной выше, менеджер ресурсов будет создавать символические ссылки на исходные пути комплекта ресурсов когда он будет публиковаться. Это быстрее, чем копирование файлов, а также может гарантировать, что опубликованные ресурсы всегда up-to-date(обновлённые/свежие).\n\n\n### Перебор Кэша<span id=\"cache-busting\"></span>\n\nДля Web приложения запущенного в режиме продакшена, считается нормальной практикой разрешить HTTP кэширование для ресурсов и других статичных источников. Недостаток такой практики в том, что всякий раз, когда изменяется ресурс и разворачивается продакшен, пользователь может по-прежнему использовать старую версию ресурса вследствие HTTP кэширования. Чтобы избежать этого, можно использовать возможность перебора кэша, которая была добавлена в версии 2.0.3, для этого можно настроить [[yii\\web\\AssetManager]] следующим образом:\n  \n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'appendTimestamp' => true,\n        ],\n    ],\n];\n```\n\nДелая таким образом, к URL каждого опубликованного ресурса будет добавляться временная метка его последней модификации. Например, URL для `yii.js` может выглядеть как `/assets/5515a87c/yii.js?v=1423448645\"`, где параметр `v` представляет собой временную метку последней модификации файла `yii.js`. Теперь если изменить ресурс, его URL тоже будет изменен, это означает что клиент получит последнюю версию ресурса.\n\n## Обычное Использование Комплекта Ресурсов<span id=\"common-asset-bundles\"></span>\n\nКод ядра Yii содержит большое количество комплектов ресурсов. Среди них, следующие комплекты широко используются и могут упоминаться в Вашем приложении или коде расширения:\n\n- [[yii\\web\\YiiAsset]]: Включает основной `yii.js` файл который реализует механизм организации JavaScript кода в модулях. Также обеспечивает специальную поддержку для `data-method` и `data-confirm` атрибутов и содержит другие полезные функции.\n- [[yii\\web\\JqueryAsset]]: Включает `jquery.js` файл из jQuery Bower пакета.\n- [[yii\\bootstrap\\BootstrapAsset]]: Включает CSS файл из Twitter Bootstrap фреймворка.\n- [[yii\\bootstrap\\BootstrapPluginAsset]]: Включает JavaScript файл из Twitter Bootstrap фреймворка для поддержки Bootstrap JavaScript плагинов.\n- [[yii\\jui\\JuiAsset]]: Включает CSS и JavaScript файлы из jQuery UI библиотеки.\n\nЕсли Ваш код зависит от jQuery, jQuery UI или Bootstrap, Вам необходимо использовать эти предопределенные комплекты ресурсов, а не создавать свои собственные варианты. Если параметры по умолчанию этих комплектов не удовлетворяют Вашим нуждам, Вы можете настроить их как описано в подразделе [Настройка Комплектов Ресурсов](#customizing-asset-bundles).\n\n## Преобразование Ресурсов<span id=\"asset-conversion\"></span>\n\nВместо того, чтобы напрямую писать CSS и/или JavaScript код, разработчики часто пишут его в некотором <b>расширенном синтаксисе</b> и используют специальные инструменты конвертации в CSS/JavaScript. Например, для CSS кода можно использовать [LESS](https://lesscss.org/) или [SCSS](https://sass-lang.com/); а для JavaScript можно использовать [TypeScript](https://www.typescriptlang.org/).\n\nМожно перечислить файлы ресурсов в <b>расширенном синтаксисе</b> в [[yii\\web\\AssetBundle::css|css]] и [[yii\\web\\AssetBundle::js|js]] свойствах из комплекта ресурсов. Например,\n\n```php\nclass AppAsset extends AssetBundle\n{\n    public $basePath = '@webroot';\n    public $baseUrl = '@web';\n    public $css = [\n        'css/site.less',\n    ];\n    public $js = [\n        'js/site.ts',\n    ];\n    public $depends = [\n        'yii\\web\\YiiAsset',\n        'yii\\bootstrap\\BootstrapAsset',\n    ];\n}\n```\n\nКогда Вы регистрируете такой комплект ресурсов в представлении, [[yii\\web\\AssetManager|asset manager]] автоматически запустит нужные инструменты препроцессора и конвертирует ресурсы в CSS/JavaScript, если их расширенный синтаксис распознан. Когда представление окончательно отобразит страницу, в неё будут включены файлы CSS/JavaScript, вместо оригинальных ресурсов в расширенном синтаксисе.\n\nYii использует имена расширений файлов для идентификации расширенного синтаксиса внутри ресурса. По умолчанию признаны следующие синтаксисы и имена расширений файлов:\n\n- [LESS](https://lesscss.org/): `.less`\n- [SCSS](https://sass-lang.com/): `.scss`\n- [Stylus](https://stylus-lang.com/): `.styl`\n- [CoffeeScript](https://coffeescript.org/): `.coffee`\n- [TypeScript](https://www.typescriptlang.org/): `.ts`\n\nYii ориентируется на установленные инструменты конвертации ресурсов препроцессора. Например, используя [LESS](https://lesscss.org/), Вы должны установить команду `lessc` препроцессора.\n\nВы можете настроить команды препроцессора и поддерживать расширенный синтаксис сконфигурировав [[yii\\web\\AssetManager::converter]] следующим образом:\n\n```php\nreturn [\n    'components' => [\n        'assetManager' => [\n            'converter' => [\n                'class' => 'yii\\web\\AssetConverter',\n                'commands' => [\n                    'less' => ['css', 'lessc {from} {to} --no-color'],\n                    'ts' => ['js', 'tsc --out {to} {from}'],\n                ],\n            ],\n        ],\n    ],\n];\n```\n\nВ примере выше, Вы задали поддержку расширенного синтаксиса через [[yii\\web\\AssetConverter::commands]] свойство.\nКлючи массива - это имена расширений файлов (без ведущей точки), а значения массива - это образующийся файл ресурса имён расширений и команд для выполнения конвертации ресурса. Маркеры `{from}` и `{to}` в командах будут заменены соответственно исходным путём файла ресурсов и путём назначения файла ресурсов.\n\n> Note: Существуют другие способы работы с ресурсами расширенного синтаксиса, кроме того, который указан выше.\nНапример, Вы можете использовать инструменты построения, такие как [grunt](https://gruntjs.com/) для отслеживания и автоматической конвертации ресурсов расширенного синтаксиса. В этом случае, Вы должны перечислить конечные CSS/JavaScript файлы в комплекте ресурсов вместо исходных файлов.\n\n## Объединение и Сжатие Ресурсов<span id=\"combining-compressing-assets\"></span>\n\nWeb страница может включать много CSS и/или JavaScript файлов. Чтобы сократить количество HTTP запросов и общий размер загрузки этих файлов, общепринятой практикой является объединение и сжатие нескольких CSS/JavaScript файлов в один или в более меньшее количество, а затем включение этих сжатых файлов вместо исходных в Web страницы.\n \n> Note: Комбинирование и сжатие ресурсов обычно необходимо, когда приложение находится в режиме продакшена.\nВ режиме разработки, использование исходных CSS/JavaScript файлов часто более удобно для отладочных целей.\n\nДалее, мы представим подход комбинирования и сжатия файлов ресурсов без необходимости изменения Вашего существующего кода приложения.\n\n1. Найдите все комплекты ресурсов в Вашем приложении, которые Вы планируете скомбинировать и сжать.\n2. Распределите эти комплекты в одну или несколько групп. Обратите внимание, что каждый комплект может принадлежать только одной группе.\n3. Скомбинируйте/сожмите CSS файлы каждой группы в один файл. Сделайте то же самое для JavaScript файлов.\n4. Определите новый комплект ресурсов для каждой группы:\n* Или установите [[yii\\web\\AssetBundle::css|css]] и [[yii\\web\\AssetBundle::js|js]] свойства. Соответствующие CSS и JavaScript файлы будут объединены.\n* Или настройте комплекты ресурсов каждой группы, установив их [[yii\\web\\AssetBundle::css|css]] и [[yii\\web\\AssetBundle::js|js]] свойства как пустые, и установите их [[yii\\web\\AssetBundle::depends|depends]] свойство как новый комплект ресурсов, созданный для группы.\n\nИспользуя этот подход, при регистрации комплекта ресурсов в представлении, автоматически регистрируется новый комплект ресурсов для группы, к которому исходный комплект принадлежит. В результате скомбинированные/сжатые файлы ресурсов включаются в страницу вместо исходных.\n\n### Пример <span id=\"example\"></span>\n\nДавайте рассмотрим пример, чтобы объяснить вышеуказанный подход.\n\nПредположим, ваше приложение имеет две страницы, X и Y. Страница X использует комплект ресурсов A, B и C, в то время, как страница Y использует комплект ресурсов, B, C и D.\n\nУ Вас есть два пути, чтобы разделить эти комплекты ресурсов. Первый - использовать одну группу, включающую в себя все комплекты ресурсов. Другой путь - положить комплект А в группу Х, D в группу Y, а (B, C) в группу S. Какой из этих вариантов лучше? Первый способ имеет преимущество в том, что в обоих страницах одинаково скомбинированы файлы CSS и JavaScript, что делает HTTP кэширование более эффективным. С другой стороны, поскольку одна группа содержит все комплекты, размер скомбинированных CSS и JavaScript файлов будет больше, и таким образом увеличится время отдачи файла (загрузки страницы). Для простоты в этом примере, мы будем использовать первый способ, то есть использовать единую группу, содержащую все пакеты.\n\n> Note: Разделение комплекта ресурсов на группы это не тривиальная задача. Это, как правило, требует анализа реальных данных о трафике различных ресурсов на разных страницах. В начале вы можете начать с одной группы, для простоты.\n\nИспользуйте существующие инструменты (например [Closure Compiler](https://developers.google.com/closure/compiler/), \n[YUI Compressor](https://github.com/yui/yuicompressor/)) для объединения и сжатия CSS и JavaScript файлов во всех комплектах. Обратите внимание, что файлы должны быть объединены в том порядке, который удовлетворяет зависимости между комплектами. Например, если комплект A зависит от В, который зависит от С и D, то Вы должны перечислить файлы ресурсов начиная с С и D, затем B, и только после этого А.\n\nПосле объединения и сжатия, Вы получите один CSS файл и один JavaScript файл. Предположим, они названы как `all-xyz.css` и `all-xyz.js`, где `xyz` это временная метка или хэш, который используется, чтобы создать уникальное имя файла, чтобы избежать проблем с HTTP кэшированием.\n\nСейчас мы находимся на последнем шаге. Настройте [[yii\\web\\AssetManager|asset manager]] в конфигурации вашего приложения, как показано ниже:\n\n```php\nreturn [\n    'components' => [\n        'assetManager' => [\n            'bundles' => [\n                'all' => [\n                    'class' => 'yii\\web\\AssetBundle',\n                    'basePath' => '@webroot/assets',\n                    'baseUrl' => '@web/assets',\n                    'css' => ['all-xyz.css'],\n                    'js' => ['all-xyz.js'],\n                ],\n                'A' => ['css' => [], 'js' => [], 'depends' => ['all']],\n                'B' => ['css' => [], 'js' => [], 'depends' => ['all']],\n                'C' => ['css' => [], 'js' => [], 'depends' => ['all']],\n                'D' => ['css' => [], 'js' => [], 'depends' => ['all']],\n            ],\n        ],\n    ],\n];\n```\n\nКак объяснено в подразделе [Настройка Комплектов Ресурсов](#customizing-asset-bundles), приведенная выше конфигурация\nизменяет поведение по умолчанию каждого комплекта. В частности, комплекты A, B, C и D не имеют больше никаких файлов ресурсов. Теперь они все зависят от `all` комплекта, который содержит скомбинированные `all-xyz.css` и `all-xyz.js` файлы. Следовательно, для страницы X, вместо включения исходных файлов ресурсов из комплектов A, B и C, только два этих объединённых файла будут включены, то же самое произойдёт и со страницей Y.\n\nЕсть еще один трюк, чтобы сделать работу вышеуказанного подхода более отлаженной. Вместо изменения конфигурационного файла приложения напрямую, можно поставить комплект массива настроек в отдельный файл, и условно включить этот файл в конфигурацию приложения. Например,\n\n```php\nreturn [\n    'components' => [\n        'assetManager' => [\n            'bundles' => require __DIR__ . '/' . (YII_ENV_PROD ? 'assets-prod.php' : 'assets-dev.php'),  \n        ],\n    ],\n];\n```\n\nТо есть, массив конфигурации комплекта ресурсов сохраняется в `assets-prod.php` для режима продакшена, и в `assets-dev.php` для режима не продакшена (разработки).\n\n> Замечание: этот механизм объединения комплектов ресурсов основан на способности [[yii\\web\\AssetManager::bundles]] перекрывать\n  поля регистрируемых комплектов ресурсов. Однако, как уже было сказано выше, эта возможность не распространяется на\n  изменения, внесенные в комплекты ресурсов на уровне метода [[yii\\web\\AssetBundle::init()]] или после регистрации. Вам\n  следует избегать использования динамических комплектов ресурсов в процессе объединения.\n\n\n### Использование команды `asset`<span id=\"using-asset-command\"></span>\n\nYii предоставляет консольную команду с именем `asset` для автоматизации подхода, который мы только что описали.\n\nЧтобы использовать эту команду, Вы должны сначала создать файл конфигурации для описания того, как комплекты ресурсов должны быть скомбинированы, и как они должны быть сгруппированы. Затем Вы можете использовать подкоманду `asset/template`, чтобы сгенерировать первый шаблон и затем отредактировать его под свои нужды.\n\n```\nyii asset/template assets.php\n```\n\nДанная команда сгенерирует файл с именем `assets.php` в текущей директории. Содержание этого файла можно увидеть ниже:\n\n```php\n<?php\n/**\n * Файл конфигурации команды консоли \"yii asset\".\n * Обратите внимание, что в консольной среде, некоторые псевдонимы путей, такие как \"@webroot' и '@web \",\n * не могут быть использованы.\n * Пожалуйста, определите отсутствующие псевдонимы путей.\n */\nreturn [\n    // Настроить команду/обратный вызов для сжатия файлов JavaScript:\n    'jsCompressor' => 'java -jar compiler.jar --js {from} --js_output_file {to}',\n    // Настроить команду/обратный вызов для сжатия файлов CSS:\n    'cssCompressor' => 'java -jar yuicompressor.jar --type css {from} -o {to}',\n    // Whether to delete asset source after compression:\n    'deleteSource' => false,\n    // Список комплектов ресурсов для сжатия:\n    'bundles' => [\n        // 'yii\\web\\YiiAsset',\n        // 'yii\\web\\JqueryAsset',\n    ],\n    // Комплект ресурса после сжатия:\n    'targets' => [\n        'all' => [\n            'class' => 'yii\\web\\AssetBundle',\n            'basePath' => '@webroot/assets',\n            'baseUrl' => '@web/assets',\n            'js' => 'js/all-{hash}.js',\n            'css' => 'css/all-{hash}.css',\n        ],\n    ],\n    // Настройка менеджера ресурсов:\n    'assetManager' => [\n    ],\n];\n```\n\nВы должны изменить этот файл и указать в `bundles` параметре, какие комплекты Вы планируете объединить. В параметре `targets` вы должны указать, как комплекты должны быть поделены в группы. Вы можете указать одну или несколько групп, как уже было сказано выше.\n\n> Note: Так как псевдонимы путей `@webroot` и `@web` не могут быть использованы в консольном приложении, Вы должны явно задать их в файле конфигурации.\n\nJavaScript файлы объединены, сжаты и записаны в `js/all-{hash}.js`, где {hash} перенесён из хэша результирующего файла.\n\nПараметры `jsCompressor` и `cssCompressor` указывают на консольные команды или обратный вызов PHP, выполняющие JavaScript и CSS объединение/сжатие. По умолчанию Yii использует [Closure Compiler](https://developers.google.com/closure/compiler/) для объединения JavaScript файлов и [YUI Compressor](https://github.com/yui/yuicompressor/) для объединения CSS файлов. Вы должны установить эти инструменты вручную или настроить данные параметры, чтобы использовать ваши любимые инструменты.\n\nВы можете запустить команду `asset` с файлом конфигурации для объединения и сжатия файлов ресурсов, а затем создать новый файл конфигурации комплекта ресурса `assets-prod.php`:\n \n```\nyii asset assets.php config/assets-prod.php\n```\n\nСгенерированный файл конфигурации может быть включен в конфигурацию приложения, как описано в последнем подразделе.\n\n> Примечание: в случае если вы перенастраиваете комплекты ресурсов через [[yii\\web\\AssetManager::bundles]] или\n  [[yii\\web\\AssetManager::assetMap]], и хотите, чтобы эти настройки применились для исходных файлов для сжатия,\n  вы должны занести эти опции в раздел `assetManager` файла кофигурации для команды `asset`.\n\n> Замечание: составляя набор исходных комплектов ресурсов для сжатия, следует избегать использования таких, чьи параметры\n  могут изменяться динамически (т.е. на уровне метода `init()` или после регистрации), поскольку они могут функционировать\n  неправильно после сжатия.\n\n\n> Для справки: Команда `asset` является не единственной опцией для автоматического процесса объединения и сжатия ресурсов.\n  Вы можете также использовать такой замечательный инструмент запуска приложений как [grunt](https://gruntjs.com/) для достижения той же цели.\n\n\n### Группировка Комплектов Ресурсов <span id=\"grouping-asset-bundles\"></span>\n\nВ последнем подразделе, мы пояснили, как объединять все комплекты ресурсов в единый в целях минимизации HTTP запросов для файлов ресурсов, упоминавшихся в приложении. Это не всегда желательно на практике. Например, представьте себе, что Ваше приложение содержит \"front end\", а также и \"back end\", каждый из которых использует свой набор JavaScript и CSS файлов. В этом случае, объединение всех комплектов ресурсов с обеих сторон в один не имеет смысла потому, что комплекты ресурсов для \"front end\" не используются в \"back end\", и это будет бесполезной тратой трафика - отправлять \"back end\" ресурсы, когда страница из \"front end\" будет запрошена.\n\nДля решения вышеуказанной проблемы, вы можете разделить комплекты по группам и объединить комплекты ресурсов для каждой группы. Следующая конфигурация показывает, как Вы можете объединять комплекты ресурсов:\n\n```php\nreturn [\n    ...\n    // Укажите выходной комплект для групп:\n    'targets' => [\n        'allShared' => [\n            'js' => 'js/all-shared-{hash}.js',\n            'css' => 'css/all-shared-{hash}.css',\n            'depends' => [\n                // Включаем все ресурсы поделённые между 'backend' и 'frontend'\n                'yii\\web\\YiiAsset',\n                'app\\assets\\SharedAsset',\n            ],\n        ],\n        'allBackEnd' => [\n            'js' => 'js/all-{hash}.js',\n            'css' => 'css/all-{hash}.css',\n            'depends' => [\n                // Включаем только 'backend' ресурсы:\n                'app\\assets\\AdminAsset'\n            ],\n        ],\n        'allFrontEnd' => [\n            'js' => 'js/all-{hash}.js',\n            'css' => 'css/all-{hash}.css',\n            'depends' => [], // Включаем все оставшиеся ресурсы\n        ],\n    ],\n    ...\n];\n```\n\nКак вы можете видеть, комплекты ресурсов поделены на три группы: `allShared`, `allBackEnd` и `allFrontEnd`. Каждая из которых зависит от соответствующего набора комплектов ресурсов. Например, `allBackEnd` зависит от `app\\assets\\AdminAsset`. При запуске команды `asset` с данной конфигурацией будут объединены комплекты ресурсов согласно приведенной выше спецификации.\n\n> Для справки: Вы можете оставить `depends` конфигурацию пустой для одного из намеченных комплектов. Поступая таким образом, данный комплект ресурсов будет зависеть от всех остальных комплектов ресурсов, от которых другие целевые комплекты не зависят.\n"
  },
  {
    "path": "docs/guide-ru/structure-controllers.md",
    "content": "Контроллеры\n===========\n\nКонтроллеры являются частью [MVC](https://ru.wikipedia.org/wiki/Model-View-Controller) архитектуры. Это объекты классов, унаследованных\nот [[yii\\base\\Controller]], отвечающие за обработку запроса и генерирование ответа. В сущности, после обработки запроса [приложениями](structure-applications.md),\nконтроллеры проанализируют входные данные, передадут их в [модели](structure-models.md), вставят результаты модели в [представления](structure-views.md),\nи в конечном итоге сгенерируют исходящие ответы.\n\n\n## Действия <span id=\"actions\"></span>\n\nКонтроллеры состоят из *действий*, которые являются основными блоками, к которым может обращаться конечный пользователь и запрашивать исполнение того или иного\nфункционала. В контроллере может быть одно или несколько действий.\n\nСледующий пример показывает `post` контроллер с двумя действиями: `view` и `create`:\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse app\\models\\Post;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\n\nclass PostController extends Controller\n{\n    public function actionView($id)\n    {\n        $model = Post::findOne($id);\n        if ($model === null) {\n            throw new NotFoundHttpException;\n        }\n\n        return $this->render('view', [\n            'model' => $model,\n        ]);\n    }\n\n    public function actionCreate()\n    {\n        $model = new Post;\n\n        if ($model->load(Yii::$app->request->post()) && $model->save()) {\n            return $this->redirect(['view', 'id' => $model->id]);\n        } else {\n            return $this->render('create', [\n                'model' => $model,\n            ]);\n        }\n    }\n}\n```\n\nВ действии `view` (определенном методом `actionView()`), код сначала загружает [модель](structure-models.md)\nсогласно запрошенному ID модели; Если модель успешно загружена, то код отобразит ее с помощью [представления](structure-views.md)\nпод названием `view`. В противном случае будет брошено исключение.\n\nВ действии `create` (определенном методом `actionCreate()`), код аналогичен. Он сначала пытается загрузить [модель](structure-models.md)\nс помощью данных из запроса и сохранить модель. Если все прошло успешно, то код перенаправляет браузер на действие `view` с ID только\nчто созданной модели. В противном случае он отобразит представление `create`, через которое пользователь может заполнить нужные данные.\n\n\n## Маршруты <span id=\"routes\"></span>\n\nКонечные пользователи обращаются к действиям через так называемые *маршруты*. Маршрут это строка, состоящая из следующих частей:\n\n* ID модуля: он существует, только если контроллер принадлежит не приложению, а [модулю](structure-modules.md);\n* ID контроллера: строка, которая уникально идентифицирует контроллер среди всех других контроллеров одного и того же приложения\n  (или одного и того же модуля, если контроллер принадлежит модулю);\n* ID действия: строка, которая уникально идентифицирует действие среди всех других действия одного и того же контроллера.\n\nМаршруты могут иметь следующий формат:\n\n```php\nControllerID/ActionID\n```\n\nили следующий формат, если контроллер принадлежит модулю:\n\n```php\nModuleID/ControllerID/ActionID\n```\n\nТаким образом, если пользователь запрашивает URL `https://hostname/index.php?r=site/index`, то `index` действие в `site` контроллере будет вызвано.\nСекция [Маршрутизация](runtime-routing.md) содержит более подробную информацию о том как маршруты сопоставляются с действиями.\n\n\n## Создание контроллеров <span id=\"creating-controllers\"></span>\n\nВ [[yii\\web\\Application|Веб приложениях]], контроллеры должны быть унаследованы от [[yii\\web\\Controller]] или его потомков.\nАналогично для [[yii\\console\\Application|консольных приложений]], контроллеры должны быть унаследованы от [[yii\\console\\Controller]] или\nего потомков. Следующий код определяет `site` контроллер:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n}\n```\n\n\n### ID контроллеров <span id=\"controller-ids\"></span>\n\nОбычно контроллер сделан таким образом, что он должен обрабатывать запросы, связанные с определенным ресурсом.\nИменно по этим причинам, ID контроллеров обычно являются существительные, ссылающиеся на ресурс, который они обрабатывают.\nНапример, вы можете использовать `article` в качестве ID контроллера, которые отвечает за обработку данных статей.\n\nПо-умолчанию, ID контроллеров должны содержать только следующие символы: Английские буквы в нижнем регистре, цифры, подчеркивания,\nтире и слэш. Например, оба `article` и `post-comment` являются допустимыми ID контроллеров, в то время как `article?`, `PostComment`,\n`admin\\post` не являются таковыми.\n\nID контроллеров также могут содержать префикс подпапки. Например, в `admin/article` часть `article` является контроллером в \nподпапке `admin` в [[yii\\base\\Application::controllerNamespace|пространстве имен контроллеров]].\nДопустимыми символами для префиксов подпапок являются: Английские буквы в нижнем  и верхнем регистре, символы подчеркивания и\nслэш, где слэш используется в качестве разграничителя для многовложенных подпапок (например `panels/admin`).\n\n\n\n### Правила наименования классов контроллеров <span id=\"controller-class-naming\"></span>\n\nНазвания классов контроллеров могут быть получены из ID контроллеров следующими способами:\n\n* Привести в верхний регистр первый символ в каждом слове, разделенном дефисами. Обратите внимание что, если ID контроллера\n  содержит слэш, то данное правило распространяется только на часть после последнего слэша в ID контроллера;\n* Убрать дефисы и заменить любой прямой слэш на обратный;\n* Добавить суффикс `Controller`;\n* Добавить в начало [[yii\\base\\Application::controllerNamespace|пространство имен контроллеров]].\n\nНиже приведены несколько примеров, с учетом того, что [[yii\\base\\Application::controllerNamespace|пространство имен контроллеров]]\nимеет значение по умолчанию равное `app\\controllers`:\n\n* `article` соответствует `app\\controllers\\ArticleController`;\n* `post-comment` соответствует `app\\controllers\\PostCommentController`;\n* `admin/post-comment` соответствует `app\\controllers\\admin\\PostCommentController`;\n* `adminPanels/post-comment` соответствует `app\\controllers\\adminPanels\\PostCommentController`.\n\nКлассы контроллеров должны быть [автозагружаемыми](concept-autoloading.md). Именно по этой причине, в вышеприведенном примере,\nконтроллер `article` должен быть сохранен в файл, [псевдоним](concept-aliases.md) которого `@app/controllers/ArticleController.php`;\nв то время как контроллер `admin/post-comment` должен находиться в файле `@app/controllers/admin/PostCommentController.php`.\n\n> Info: Последний пример `admin/post-comment` показывает каким образом вы можете расположить контроллер в подпапке\n  [[yii\\base\\Application::controllerNamespace|пространства имен контроллеров]]. Это очень удобно, когда вы хотите организовать свои контроллеры\n  в несколько категорий и не хотите использовать [модули](structure-modules.md).\n\n\n### Карта контроллеров <span id=\"controller-map\"></span>\n\nВы можете сконфигурировать [[yii\\base\\Application::controllerMap|карту контроллеров]] для того, чтобы преодолеть\nописанные выше ограничения именования ID контроллеров и названий классов. В основном это очень удобно, когда вы используете\nсторонние контроллеры, именование которых вы не можете контролировать.\n\nВы можете сконфигурировать [[yii\\base\\Application::controllerMap|карту контроллеров]] в [настройках приложения](structure-applications.md#application-configurations)\nследующим образом:\n\n```php\n[\n    'controllerMap' => [\n        // объявляет \"account\" контроллер, используя название класса\n        'account' => 'app\\controllers\\UserController',\n\n        // объявляет \"article\" контроллер, используя массив конфигурации\n        'article' => [\n            'class' => 'app\\controllers\\PostController',\n            'enableCsrfValidation' => false,\n        ],\n    ],\n]\n```\n\n### Контроллер по умолчанию <span id=\"default-controller\"></span>\n\nКаждое приложение имеет контроллер по умолчанию, указанный через свойство [[yii\\base\\Application::defaultRoute]].\nКогда в запросе не указан [маршрут](#routes), тогда будет использован маршрут указанный в данном свойстве.\nДля [[yii\\web\\Application|Веб приложений]], это значение `'site'`, в то время как для [[yii\\console\\Application|консольных приложений]],\nэто `'help'`. Таким образом, если задан URL `https://hostname/index.php`, это означает, что контроллер `site` выполнит обработку запроса.\n\nВы можете изменить контроллер по умолчанию следующим образом в [настройках приложения](structure-applications.md#application-configurations):\n\n```php\n[\n    'defaultRoute' => 'main',\n]\n```\n\n\n## Создание действий <span id=\"creating-actions\"></span>\n\nСоздание действий не представляет сложностей так же как и объявление так называемых *методов действий* в классе контроллера. Метод действия\nэто *public* метод, имя которого начинается со слова `action`. Возвращаемое значение метода действия представляет собой ответные данные,\nкоторые будут высланы конечному пользователю. Приведенный ниже код определяет два действия `index` и `hello-world`:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actionIndex()\n    {\n        return $this->render('index');\n    }\n\n    public function actionHelloWorld()\n    {\n        return 'Hello World';\n    }\n}\n```\n\n\n### ID действий <span id=\"action-ids\"></span>\n\nВ основном действие разрабатывается для какой-либо конкретной обработки ресурса. По этой причине, ID действий в основном\nявляются глаголами, такими как `view`, `update`, и т. д.\n\nПо-умолчанию, ID действия должен содержать только следующие символы: Английские буквы в нижнем регистре, цифры,\nподчеркивания и дефисы. Дефисы в ID действий используются для разделения слов. Например, `view`, `update2`, `comment-post` являются\nдопустимыми ID действий, в то время как `view?`, `Update` не являются таковыми.\n\nВы можете создавать действия двумя способами: встроенные действия и отдельные действия. Встроенное действие является методом, определенным\nв классе контроллера, в то время как отдельное действие является экземпляром класса, унаследованного от [[yii\\base\\Action]] или его потомков.\nВстроенные действия требуют меньше усилий для создания и в основном используются если у вас нет надобности в повторном использовании действий.\nОтдельные действия, с другой стороны, в основном создаются для использования в различных контроллерах или при использовании в [расширениях](structure-extensions.md).\n\n\n### Встроенные действия <span id=\"inline-actions\"></span>\n\nВстроенные действия это те действия, которые определены в рамках методов контроллера, как мы это уже обсудили.\n\nНазвания методов действий могут быть получены из ID действий следующим образом:\n\n* Привести первый символ каждого слова в ID действия в верхний регистр;\n* Убрать дефисы;\n* Добавить префикс `action`.\n\nНапример, `index` соответствует `actionIndex`, а `hello-world` соответствует `actionHelloWorld`.\n\n> Note: Названия имен действий являются *регистрозависимыми*. Если у вас есть метод `ActionIndex`, он не будет\n  учтен как метод действия, таким образом, запрос к действию `index` приведет к выбросу исключения. Также следует учесть, что методы действий\n  должны иметь область видимости public. Методы имеющие область видимости private или protected НЕ определяют методы встроенных действий.\n\n\nВстроенные действия в основном используются, потому что для их создания не нужного много усилий. Тем не менее, если вы планируете повторно\nиспользовать некоторые действия в различных местах, или если вы хотите перераспределить действия, вы должны определить его как *отдельное действие*.\n\n\n### Отдельные действия <span id=\"standalone-actions\"></span>\n\nОтдельные действия определяются в качестве классов, унаследованных от [[yii\\base\\Action]] или его потомков.\nНапример, в Yii релизах, присутствуют [[yii\\web\\ViewAction]] и [[yii\\web\\ErrorAction]], оба из которых являются\nотдельными действиями.\n\nДля использования отдельного действия, вы должны указать его в *карте действий*, с помощью переопределения метода\n[[yii\\base\\Controller::actions()]] в вашем классе контроллера, следующим образом:\n\n```php\npublic function actions()\n{\n    return [\n        // объявляет \"error\" действие с помощью названия класса\n        'error' => 'yii\\web\\ErrorAction',\n\n        // объявляет \"view\" действие с помощью конфигурационного массива\n        'view' => [\n            'class' => 'yii\\web\\ViewAction',\n            'viewPrefix' => '',\n        ],\n    ];\n}\n```\n\nКак вы можете видеть, метод `actions()` должен вернуть массив, ключами которого являются ID действий, а значениями - соответствующие\nназвания класса действия или [конфигурация](concept-configurations.md). В отличие от встроенных действий, ID отдельных действий могут\nсодержать произвольные символы, до тех пор пока они определены в методе `actions()`.\n\nДля создания отдельного действия, вы должны наследоваться от класса [[yii\\base\\Action]] или его потомков, и реализовать\nметод `run()` с областью видимости public. Роль метода `run()` аналогична другим методам действий. Например,\n\n```php\n<?php\nnamespace app\\components;\n\nuse yii\\base\\Action;\n\nclass HelloWorldAction extends Action\n{\n    public function run()\n    {\n        return \"Hello World\";\n    }\n}\n```\n\n\n### Результаты действий <span id=\"action-results\"></span>\n\nВозвращаемое значение методов действий или метода `run()` отдельного действия очень важно. Оно является результатом\nвыполнения соответствующего действия.\n\nВозвращаемое значение может быть объектом [response](runtime-responses.md), который будет отослан конечному пользователю \nв качестве ответа.\n\n* Для [[yii\\web\\Application|Веб приложений]], возвращаемое значение также может быть произвольными данными, которые будут\n  присвоены [[yii\\web\\Response::data]], а затем конвертированы в строку, представляющую тело ответа.\n* Для [[yii\\console\\Application|Консольных приложений]], возвращаемое значение также может быть числом, представляющим\n  [[yii\\console\\Response::exitStatus|статус выхода]] исполнения команды.\n\nВ вышеприведенных примерах, все результаты действий являются строками, которые будут использованы в качестве тела ответа,\nвысланного пользователю. Следующий пример, показывает как действие может перенаправить браузер пользователя на новый URL, с помощью\nвозврата response объекта (т. к. [[yii\\web\\Controller::redirect()|redirect()]] метод возвращает response объект):\n\n```php\npublic function actionForward()\n{\n    // перенаправляем браузер пользователя на https://example.com\n    return $this->redirect('https://example.com');\n}\n```\n\n\n### Параметры действий <span id=\"action-parameters\"></span>\n\nМетоды действий для встроенных действий и методы `run()` для отдельных действий могут принимать параметры,\nназываемые *параметры действий*. Их значения берутся из запросов. Для [[yii\\web\\Application|Веб приложений]],\nзначение каждого из параметров действия берется из `$_GET`, используя название параметра в качестве ключа;\nдля [[yii\\console\\Application|консольных приложений]], они соответствуют аргументам командной строки.\n\nВ приведенном ниже примере, действие `view` (встроенное действие) определяет два параметра: `$id` и `$version`.\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass PostController extends Controller\n{\n    public function actionView($id, $version = null)\n    {\n        // ...\n    }\n}\n```\n\nДля разных запросов параметры действий будут определены следующим образом:\n\n* `https://hostname/index.php?r=post/view&id=123`: параметр `$id` будет присвоено значение `'123'`, в то время\n  как `$version` будет иметь значение `null`, т. к. строка запроса не содержит параметра `version`;\n* `https://hostname/index.php?r=post/view&id=123&version=2`: параметрам `$id` и `$version` будут присвоены\n  значения `'123'` и `'2'` соответственно;\n* `https://hostname/index.php?r=post/view`: будет брошено исключение [[yii\\web\\BadRequestHttpException]], т. к.\n  обязательный параметр `$id` не был указан в запросе;\n* `https://hostname/index.php?r=post/view&id[]=123`: будет брошено исключение [[yii\\web\\BadRequestHttpException]], т. к.\n  параметр `$id` получил неверное значение `['123']`.\n\nЕсли вы хотите, чтобы параметр действия принимал массив значений, вы должны использовать type-hint значение `array`, как показано ниже:\n\n```php\npublic function actionView(array $id, $version = null)\n{\n    // ...\n}\n```\n\nТеперь, если запрос будет содержать URL `https://hostname/index.php?r=post/view&id[]=123`, то параметр `$id` примет значение\n`['123']`. Если запрос будет содержать URL `https://hostname/index.php?r=post/view&id=123`, то параметр `$id` все равно будет\nсодержать массив, т. к. скалярное значение `'123'` будет автоматически конвертировано в массив.\n\nВышеприведенные примеры в основном показывают как параметры действий работают для Веб приложений. Больше информации\nо параметрах консольных приложений представлено в секции [Консольные команды](tutorial-console.md).\n\n\n### Действие по умолчанию <span id=\"default-action\"></span>\n\nКаждый контроллер имеет действие, указанное через свойство [[yii\\base\\Controller::defaultAction]].\nКогда [маршрут](#routes) содержит только ID контроллера, то подразумевается, что действие контроллера по умолчанию\nбыло запрошено.\n\nПо-умолчанию, это действие имеет значение `index`. Если вы хотите изменить это значение, просто переопределите данное\nсвойство в классе контроллера следующим образом:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public $defaultAction = 'home';\n\n    public function actionHome()\n    {\n        return $this->render('home');\n    }\n}\n```\n\n\n## Жизненный цикл контроллера <span id=\"controller-lifecycle\"></span>\n\nПри обработке запроса, [приложение](structure-applications.md) создаст контроллер, основываясь на \nзапрошенном [маршруте](#routes). Для выполнения запроса, контроллер пройдет через следующие этапы\nжизненного цикла:\n\n1. Метод [[yii\\base\\Controller::init()]] будет вызван после того как контроллер будет создан и сконфигурирован;\n2. Контроллер создает объект действия, основываясь на запрошенном ID действия:\n   * Если ID действия не указан, то будет использовано [[yii\\base\\Controller::defaultAction|ID действия по умолчанию]];\n   * Если ID действия найдено в [[yii\\base\\Controller::actions()|карте действий]], то отдельное действие будет создано;\n   * Если ID действия соответствует методу действия, то встроенное действие будет создано;\n   * В противном случае, будет выброшено исключение [[yii\\base\\InvalidRouteException]].\n3. Контроллер последовательно вызывает метод `beforeAction()` приложения, модуля (если контроллер принадлежит модулю) и\n   самого контроллера.\n   * Если один из методов вернул `false`, то остальные, не вызванные методы `beforeAction` будут пропущены, а выполнение\n     действия будет отменено;\n   * По-умолчанию, каждый вызов метода `beforeAction()` вызовет событие `beforeAction`, на которое вы можете назначить обработчики.\n4. Контроллер запускает действие:\n   * Параметры действия будут проанализированы и заполнены из данных запроса.\n5. Контроллер последовательно вызывает методы `afterAction` контроллера, модуля (если контроллер принадлежит модулю) и приложения.\n   * По-умолчанию, каждый вызов метода `afterAction()` вызовет событие `afterAction`, на которое вы можете назначить обработчики.\n6. Приложение, получив результат выполнения действия, присвоит его объекту [response](runtime-responses.md).\n\n\n## Лучшие практики <span id=\"best-practices\"></span>\n\nВ хорошо организованных приложениях контроллеры обычно очень тонкие и содержат лишь несколько строк кода.\nЕсли ваш контроллер слишком сложный, то обычно это означает, что вам надо провести его рефакторинг и перенести часть кода\nв другие места.\n\nВ целом, контроллеры\n\n* могут иметь доступ к данным [запроса](runtime-requests.md);\n* могут вызывать методы [моделей](structure-models.md) и других компонентов системы с данными запроса;\n* могут использовать [представления](structure-views.md) для формирования ответа;\n* не должны заниматься обработкой данных, это должно происходить в [слое моделей](structure-models.md);\n* должны избегать использования HTML или другой разметки, лучше это делать в [представлениях](structure-views.md).\n"
  },
  {
    "path": "docs/guide-ru/structure-entry-scripts.md",
    "content": "Входные скрипты\n===============\n\nВходные скрипты это первое звено в процессе начальной загрузки приложения. Приложение (веб приложение или консольное приложение)\nвключает единый входной скрипт. Конечные пользователи делают запросы к входному скрипту,\nкоторый создает объекты приложения и перенаправляет запрос к ним.\n\nВходные скрипты для веб приложений должны быть сохранены в папках, доступных из веб, таким образом они могут быть\nдоступны конечным пользователям. Такие скрипты обычно именуются `index.php`, но так же могут использовать другие имена,\nкоторые могут быть распознаны используемыми веб-серверами.\n\nВходные скрипты для консольных приложений обычно расположены в [базовой папке](structure-applications.md) приложений и имеют название \n`yii` (с суффиксом `.php`). Они должны иметь права на выполнение, таким образом пользователи смогут запускать консольные приложения \nчерез команду `./yii <маршрут> [аргументы] [опции]`.\n\nВходные скрипты в основном делают следующую работу:\n\n* Объявляют глобальные константы;\n* Регистрируют загрузчик классов [Composer](https://getcomposer.org/doc/01-basic-usage.md#autoloading);\n* Подключают файл класса [[Yii]];\n* Загружают конфигурацию приложения;\n* Создают и конфигурируют объект [приложения](structure-applications.md);\n* Вызывают метод [[yii\\base\\Application::run()]] приложения для обработки входящего запроса.\n\n\n## Веб приложения <span id=\"web-applications\"></span>\n\nНиже представлен код входного скрипта для [базового шаблона приложения](start-installation.md).\n\n```php\n<?php\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n\n// регистрация загрузчика классов Composer\nrequire __DIR__ . '/../vendor/autoload.php';\n\n// подключение файла класса Yii\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n\n// загрузка конфигурации приложения\n$config = require __DIR__ . '/../config/web.php';\n\n// создание и конфигурация приложения, а также вызов метода для обработки входящего запроса\n(new yii\\web\\Application($config))->run();\n```\n\n\n## Консольные приложения <span id=\"console-applications\"></span>\n\nНиже представлен аналогичный код входного скрипта консольного приложения:\n\n```php\n#!/usr/bin/env php\n<?php\n/**\n * Yii console bootstrap file.\n *\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\n\n// регистрация загрузчика классов Composer\nrequire __DIR__ . '/vendor/autoload.php';\n\n// подключение файла класса Yii\nrequire __DIR__ . '/vendor/yiisoft/yii2/Yii.php';\n\n// загрузка конфигурации приложения\n$config = require __DIR__ . '/config/console.php';\n\n$application = new yii\\console\\Application($config);\n$exitCode = $application->run();\nexit($exitCode);\n```\n\n\n## Объявление констант <span id=\"defining-constants\"></span>\n\nВходные скрипты являются наилучшим местом для объявления глобальных констант. Yii поддерживают следующие три константы:\n\n* `YII_DEBUG`: указывает работает ли приложение в отладочном режиме. Находясь в отладочном режиме, приложение будет собирать\n  больше информации в логи и покажет детальный стек вызовов если возникнет исключение. По этой причине, отладочный режим должен\n  быть использован только в процессе разработки. По-умолчанию значение `YII_DEBUG` равно `false`;\n* `YII_ENV`: указывает в каком окружении запущено приложение. Данная тема подробно описана в разделе [Конфигурации](concept-configurations.md#environment-constants).\n  По-умолчанию значение `YII_ENV` равно `'prod'`, означающие, что приложение запущено в производственном режиме;\n* `YII_ENABLE_ERROR_HANDLER`: указывает нужно ли включать имеющийся в Yii обработчик ошибок. По-умолчанию значение данной константы\n  равно `true`.\n\nПри определении константы, мы обычно используем следующий код:\n\n```php\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\n```\n\nкоторый равнозначен коду, приведенному ниже:\n\n```php\nif (!defined('YII_DEBUG')) {\n    define('YII_DEBUG', true);\n}\n```\n\nПервый способ является более кратким и понятным.\n\nКонстанты должны быть определены как можно раньше, в самом начале входного скрипта, таким образом они могут оказать влияние,\nкогда остальные PHP файлы будут подключены.\n"
  },
  {
    "path": "docs/guide-ru/structure-extensions.md",
    "content": "Расширения\n==========\n\nРасширения - это распространяемые программные пакеты, специально разработанные для использования в приложениях Yii и\nсодержащие готовые функции. Например, расширение [yiisoft/yii2-debug](https://github.com/yiisoft/yii2-debug) добавляет удобную отладочную\nпанель в нижнюю часть каждой страницы вашего приложения, чтобы помочь вам разобраться в том, как генерируются\nстраницы. Вы можете использовать расширения для ускорения процесса разработки. Вы также можете оформить ваш код как\nрасширение, чтобы поделиться с другими людьми результатами вашей работы.\n\n> Info: Мы используем термин \"расширение\" для специфичных для Yii программных пакетов. Программные пакеты\n  общего назначения, которые могут быть использованы без Yii, мы будем называть \"пакет\" или \"библиотека\".\n\n\n## Использование расширений <span id=\"using-extensions\"></span>\n\nЧтобы использовать расширение, вам необходимо установить его. Большинство расширений распространяются как пакеты\n[Composer](https://getcomposer.org/), которые могут быть установлены посредством следующих двух шагов:\n\n1. Отредактируйте файл вашего приложения `composer.json`, указав, какие расширения (пакеты Composer) вы хотите\n   установить.\n2. Выполните команду `php composer.phar install`, чтобы установить указанные расширения.\n\nОбратите внимание, что вам может потребоваться установить [Composer](https://getcomposer.org/), если у вас его нет.\n\nПо умолчанию, Composer устанавливает пакеты, зарегистрированные на [Packagist](https://packagist.org/) - крупнейшем\nрепозитории для пакетов Composer с открытым исходным кодом. Вы также можете\n[создать свой репозиторий](https://getcomposer.org/doc/05-repositories.md#repository) и настроить Composer для его\nиспользования. Это полезно, если вы разрабатываете закрытые расширения и хотите использовать их в нескольких своих\nпроектах.\n\nРасширения, установленные Composer'ом, хранятся в директории `BasePath/vendor`, где `BasePath` -\n[базовая директория](structure-applications.md#basePath) приложения. Composer - это менеджер зависимостей, и поэтому\nпосле установки пакета он также установит все зависимые пакеты.\n\nНапример, для установки расширения `yiisoft/yii2-imagine` нужно отредактировать ваш `composer.json` как показано далее:\n\n```json\n{\n    // ...\n\n    \"require\": {\n        // ... другие зависимости\n\n        \"yiisoft/yii2-imagine\": \"~2.0.0\"\n    }\n}\n```\n\nПосле установки вы можете увидеть директорию `yiisoft/yii2-imagine`, находящуюся по пути `BasePath/vendor`. Также вы\nможете увидеть директорию `imagine/imagine`, которая содержит зависимый пакет.\n\n> Info: `yiisoft/yii2-imagine` является базовым расширением, которое разрабатывает и поддерживает команда\n  разработчиков Yii. Все базовые расширения размещены на [Packagist](https://packagist.org/) и называются\n  `yiisoft/yii2-xyz`, где `xyz` является названием расширения.\n\nТеперь вы можете использовать установленное расширение как часть вашего приложения. Следующий пример показывает, как вы\nможете использовать класс `yii\\imagine\\Image`, который содержится в расширении `yiisoft/yii2-imagine`.\n\n```php\nuse Yii;\nuse yii\\imagine\\Image;\n\n// генерация миниатюры изображения\nImage::thumbnail('@webroot/img/test-image.jpg', 120, 120)\n    ->save(Yii::getAlias('@runtime/thumb-test-image.jpg'), ['quality' => 50]);\n```\n\n> Info: Классы расширений автоматически загружаются [автозагрузчиком классов Yii](concept-autoloading.md).\n\n\n### Ручная установка расширений <span id=\"installing-extensions-manually\"></span>\n\nВ некоторых редких случаях вы можете захотеть установить некоторые расширения вручную, а не полагаться на Composer.\nЧтобы сделать это, вы должны\n\n1. загрузить архив с файлами расширения и распаковать его в директорию `vendor`.\n2. установить автозагрузчики классов, предоставляемые расширениями, если таковые имеются.\n3. загрузить и установить все зависимые расширения в соответствии с инструкциями.\n\nЕсли расширение не имеет автозагрузчика классов, но следует [стандарту PSR-4](https://www.php-fig.org/psr/psr-4/), то вы\nможете использовать автозагрузчик классов, предоставленный Yii для загрузки классов расширений. Всё, что вам нужно\nсделать, это объявить [псевдоним](concept-aliases.md#defining-aliases) для корневого каталога расширения. Например,\nесли вы установили расширение в директорию `vendor/mycompany/myext` и классы расширения находятся в пространстве имён\n`myext`, то вы можете включить следующий код в конфигурацию вашего приложения:\n\n```php\n[\n    'aliases' => [\n        '@myext' => '@vendor/mycompany/myext',\n    ],\n]\n```\n\n\n## Создание расширений <span id=\"creating-extensions\"></span>\n\nВы можете захотеть создать расширение, когда чувствуете необходимость поделиться своим хорошим кодом с другими людьми.\nРасширение может содержать любой код, который вам нравится, например, класс-помощник, виджет, модуль и т.д.\n\nРекомендуется создавать расширение как [пакет Composer](https://getcomposer.org/), для того, чтобы его можно было\nлегко установить и использовать, как описано в предыдущей главе.\n\nНиже приведены основные шаги, которым нужно следовать, чтобы создать пакет Composer.\n\n1. Создайте проект для вашего расширения и разместите его в VCS репозитории, таком как [github.com](https://github.com).\n   Разработка и поддержка расширения должна выполняться в этом репозитории.\n2. В корневой директории проекта создайте файл под названием `composer.json`, в соответствии с требованиями Composer.\n   Вы можете обратиться к следующему разделу за более подробной информацией.\n3. Зарегистрируйте ваше расширение в репозитории Composer, таком как [Packagist](https://packagist.org/), чтобы другие\n   пользователи могли найти и установить ваше расширение, используя Composer.\n\n\n### `composer.json` <span id=\"composer-json\"></span>\n\nКаждый пакет Composer должен иметь файл `composer.json` в своей корневой директории. Этот файл содержит метаданные о\nпакете. Вы можете найти полную спецификацию по этому файлу в\n[Руководстве Composer](https://getcomposer.org/doc/01-basic-usage.md#composer-json-project-setup). Следующий пример\nдемонстрирует файл `composer.json` для расширения `yiisoft/yii2-imagine`:\n\n```json\n{\n    // название пакета\n    \"name\": \"yiisoft/yii2-imagine\",\n\n    // тип пакета\n    \"type\": \"yii2-extension\",\n\n    \"description\": \"The Imagine integration for the Yii framework\",\n    \"keywords\": [\"yii2\", \"imagine\", \"image\", \"helper\"],\n    \"license\": \"BSD-3-Clause\",\n    \"support\": {\n        \"issues\": \"https://github.com/yiisoft/yii2/issues?labels=ext%3Aimagine\",\n        \"forum\": \"https://forum.yiiframework.com/\",\n        \"wiki\": \"https://www.yiiframework.com/wiki/\",\n        \"irc\": \"ircs://irc.libera.chat:6697/yii\",\n        \"source\": \"https://github.com/yiisoft/yii2\"\n    },\n    \"authors\": [\n        {\n            \"name\": \"Antonio Ramirez\",\n            \"email\": \"amigo.cobos@gmail.com\"\n        }\n    ],\n\n    // зависимости пакета\n    \"require\": {\n        \"yiisoft/yii2\": \"~2.0.0\",\n        \"imagine/imagine\": \"v0.5.0\"\n    },\n\n    // указание автозагрузчика классов\n    \"autoload\": {\n        \"psr-4\": {\n            \"yii\\\\imagine\\\\\": \"\"\n        }\n    }\n}\n```\n\n\n#### Название пакета <span id=\"package-name\"></span>\n\nКаждый пакет Composer должен иметь название, которое однозначно идентифицирует пакет среди остальных. Название пакета\nимеет формат `имяРазработчика/названиеПроекта`. Например, в пакете `yiisoft/yii2-imagine`, `yiisoft` является именем\nразработчика, а `yii2-imagine` - названием пакета.\n\nНЕ используйте `yiisoft` в качестве имени разработчика, так как оно зарезервировано для использования в коде ядра Yii.\n\nМы рекомендуем использовать префикс `yii2-` в названии проекта для пакетов, являющихся расширениями Yii 2, например,\n`моёИмя/yii2-mywidget`. Это позволит пользователям легче определить, что пакет является расширением Yii 2.\n\n\n#### Тип пакета <span id=\"package-type\"></span>\n\nВажно указать тип пакета вашего расширения как `yii2-extension`, чтобы пакет можно было распознать как расширение Yii во\nвремя установки.\n\nКогда пользователь запускает команду `php composer.phar install` для установки расширения, файл\n`vendor/yiisoft/extensions.php` будет автоматически обновлён, чтобы включить информацию о новом расширении. Из этого\nфайла приложение Yii может узнать, какие расширения установлены (информацию можно получить с помощью\n[[yii\\base\\Application::extensions]]).\n\n\n#### Зависимости <span id=\"dependencies\"></span>\n\nВаше расширение зависит от Yii (естественно). Вы можете посмотреть список зависимостей в секции `require`, входящей в\nфайл `composer.json`. Если ваше расширение зависит от других расширений или сторонних библиотек, то вы также должны их\nперечислить. Убедитесь, что в ограничениях вы указали соответствующую версию (например, `1.*`, `@stable`) для каждой\nзависимости. Используйте стабильные версии зависимостей, когда будет выпущена стабильная версия вашего расширения.\n\n\n#### Автозагрузка классов <span id=\"class-autoloading\"></span>\n\nДля того, чтобы ваши классы были загружены автозагрузчиком классов Yii или автозагрузчиком классов Composer, вы должны\nвнести секцию `autoload` в файл `composer.json`, как показано ниже:\n\n```json\n{\n    // ....\n\n    \"autoload\": {\n        \"psr-4\": {\n            \"yii\\\\imagine\\\\\": \"\"\n        }\n    }\n}\n```\n\nВы можете перечислить один или несколько корневых пространств имён и соответствующие им пути.\n\nКогда расширение установлено в приложение, Yii для каждого указанного корневого пространства имён создаст\n[псевдоним](concept-aliases.md#extension-aliases), который указывает на директорию, соответствующую пространству имён.\nНапример, указанная в секции `autoload` запись будет соответствовать псевдониму `@yii/imagine`.\n\n\n### Рекомендованные практики <span id=\"recommended-practices\"></span>\n\nПоскольку расширения предназначены для использования другими людьми, вам придётся приложить дополнительные усилия в\nпроцессе разработки. Ниже приведены некоторые общие и рекомендованные практики для создания высококачественных\nрасширений.\n\n\n#### Пространства имён <span id=\"namespaces\"></span>\n\nВо избежание конфликтов имён, а также для того, чтобы ваши классы были автозагружаемыми, вы должны следовать\n[стандарту PSR-4](https://www.php-fig.org/psr/psr-4/) или [стандарту PSR-0](https://www.php-fig.org/psr/psr-0/) в\nиспользовании пространств имён и названии классов вашего расширения.\n\nПространства имён в ваших классах должны начинаться с `имяРазработчика\\названиеРасширения`, где `названиеРасширения`\nсовпадает с названием проекта в названии пакета, за исключением того, что оно не должно содержать префикса `yii2-`.\nНапример, для расширения `yiisoft/yii2-imagine` мы используем `yii\\imagine` в качестве пространства имён.\n\nНе используйте `yii`, `yii2` или `yiisoft` в качестве имени разработчика. Эти имена являются зарезервированными для\nиспользования в коде ядра Yii.\n\n\n#### Классы начальной загрузки <span id=\"bootstrapping-classes\"></span>\n\nИногда вы можете захотеть выполнить некоторый код своего расширения в стадии\n[начальной загрузки](runtime-bootstrapping.md) приложения. Например, ваше расширение может ответить на событие\nприложения `beginRequest`, чтобы установить некоторые настройки окружения. Вы можете в инструкции по установке вашего\nприложения написать, что необходимо назначить обработчик события `beginRequest`, но лучшим способом будет сделать это\nавтоматически.\n\nДля достижения этой цели вы можете создать так называемый *класс начальной загрузки*, реализовав интерфейс\n[[yii\\base\\BootstrapInterface]]. Например,\n\n```php\nnamespace myname\\mywidget;\n\nuse yii\\base\\BootstrapInterface;\nuse yii\\base\\Application;\n\nclass MyBootstrapClass implements BootstrapInterface\n{\n    public function bootstrap($app)\n    {\n        $app->on(Application::EVENT_BEFORE_REQUEST, function () {\n             // остальной код\n        });\n    }\n}\n```\n\nЗатем нужно добавить этот класс в файл `composer.json` вашего расширения, как показано далее,\n\n```json\n{\n    // ...\n\n    \"extra\": {\n        \"bootstrap\": \"myname\\\\mywidget\\\\MyBootstrapClass\"\n    }\n}\n```\n\nКогда расширение будет установлено в приложение, Yii автоматически инициирует экземпляр класса начальной загрузки и\nвызовет его метод [[yii\\base\\BootstrapInterface::bootstrap()|bootstrap()]]  в процессе начальной загрузки каждого\nзапроса.\n\n\n#### Работа с базами данных <span id=\"working-with-databases\"></span>\n\nВаше расширение может иметь доступ к базам данных. Не думайте, что приложения, которые используют ваше расширение,\nвсегда используют `Yii::$db` в качестве соединения с БД. Вместо этого вам следует объявить свойство `db` в классах,\nкоторым необходим доступ в БД. Это свойство позволит пользователям вашего расширения настроить соединение с БД,\nкоторое они будут использовать в вашем расширении. В качестве примера вы можете обратиться к классу\n[[yii\\caching\\DbCache]] и посмотреть, как он объявляет и использует свойство `db`.\n\nЕсли в вашем приложении необходимо создать определённые таблицы БД или сделать изменения в схеме БД, вы должны\n\n- создать файлы [миграций](db-migrations.md) для изменения схемы БД вместо простых SQL-файлов;\n- попытаться сделать миграции, применимые к различным СУБД;\n- избегать использования [Active Record](db-active-record.md) в миграциях.\n\n\n#### Использование ресурсов <span id=\"using-assets\"></span>\n\nЕсли ваше расширение является виджетом или модулем, то есть вероятность, что оно потребует некоторых\n[ресурсов](structure-assets.md) для работы. Например, модуль может отображать некоторые страницы, которые содержат\nизображения, JavaScript и CSS. Так как все файлы расширения находятся в директории, недоступной из интернета, у вас\nесть два варианта сделать директорию ресурсов непосредственно доступной из интернета:\n\n- попросить пользователей расширения вручную скопировать файлы ресурсов в определённую, доступную из интернета папку;\n- объявить [связку ресурсов](structure-assets.md) и полагаться на механизм публикации ресурсов, который автоматически\n  копирует файлы, описанные в связке ресурсов в папку, доступную из интернета.\n\nМы рекомендуем вам использовать второй подход, чтобы ваше расширение было более простым в использовании для других\nлюдей.\n\n\n### Интернационализация и локализация <span id=\"i18n-l10n\"></span>\n\nВаше расширение может быть использовано в приложениях, поддерживающих разные языки! Поэтому, если ваше расширение\nотображает содержимое конечному пользователю, вы должны попробовать\n[интернационализовать и локализовать](tutorial-i18n.md) его. В частности,\n\n- Если расширение отображает сообщения, предназначенные для конечных пользователей, сообщения должны быть обернуты в\n  метод `Yii::t()` так, чтобы они могли быть переведены. Сообщения, предназначенные для разработчиков (например,\n  внутренние сообщения исключений), не нужно переводить.\n- Если расширение отображает числа, даты и т.п., они должны быть отформатированы, используя [[yii\\base\\Formatter]] с\n  соответствующими правилами форматирования.\n\nДля более подробной информации вы можете обратиться к разделу [Интернационализация](tutorial-i18n.md)\n\n\n#### Тестирование <span id=\"testing\"></span>\n\nВы хотите, чтобы ваше расширение было стабильным и не приносило проблем другим людям. Для достижения этой цели вы\nдолжны протестировать ваше расширение перед его публикацией.\n\nРекомендуется создавать различные тесты для покрытия кода вашего расширения, а не вручную тестировать его. Каждый раз\nперед тем, как выпустить новую версию расширения, вы можете просто запустить эти тесты чтобы убедиться, что всё\nработает правильно. Yii имеет поддержку тестирования, которая может помочь вам легче писать модульные, приёмочные и\nфункциональные тесты. Для более подробной информации вы можете обратиться в раздел [Тестирование](test-overview.md).\n\n\n#### Версионирование <span id=\"versioning\"></span>\n\nВы можете давать каждому выпуску вашего расширения номер версии (например, `1.0.1`). Мы рекомендуем вам придерживаться\nпрактик [семантического версионирования](https://semver.org) при определении, какой номер версии должен использоваться.\n\n#### Публикация <span id=\"releasing\"></span>\n\nЧтобы позволить другим людям узнать о вашем расширении, необходимо опубликовать его.\n\nЕсли это первый выпуск вашего расширения, вы должны зарегистрировать его в репозитории Composer, таком, как\n[Packagist](https://packagist.org/). После этого вам остаётся только создать тег выпуска (например, `v1.0.1`) в VCS\nрепозитории вашего расширения и уведомить репозиторий Composer о новом выпуске. Люди смогут найти новую версию и\nустановить или обновить расширение через репозиторий Composer.\n\nВ выпусках вашего расширения помимо файлов с кодом вы также должны рассмотреть вопрос о включении следующих файлов,\nкоторые помогут людям изучить и использовать ваше расширение:\n\n* Файл readme в корневой директории пакета: он описывает, что ваше расширение делает, а также как его установить и\n  использовать. Мы рекомендуем вам написать его в формате [Markdown](https://daringfireball.net/projects/markdown/) и\n  дать ему название `readme.md`.\n* Файл changelog в корневой директории пакета: он описывает, какие изменения произошли в каждом выпуске. Этот файл\n  может быть написан в формате Markdown и назван `changelog.md`.\n* Файл upgrade в корневой директории пакета: он даёт инструкции о том, как обновить старые версии расширения. Этот\n  файл может быть написан в формате Markdown и назван `upgrade.md`.\n* Руководства пользователя, демо-версии, скриншоты и т.д.: они необходимы, если ваше расширение предоставляет много\n  возможностей, которые невозможно полностью описать в файле readme.\n* Документация API: ваш код должен быть документирован, чтобы позволить другим людям легко читать и понимать его. Вы\n  можете обратиться к [файлу класса BaseObject](https://github.com/yiisoft/yii2/blob/master/framework/base/BaseObject.php),\n  чтобы узнать, как нужно документировать код.\n\n> Info: Ваши комментарии к коду могут быть написаны в формате Markdown. Расширение `yiisoft/yii2-apidoc`\n  предоставляет инструмент для генерации документации API на основе ваших комментариев.\n\n> Info: Пока это не обязательно, но мы всё-таки рекомендуем вам придерживаться определённого стиля кодирования.\n  Вы можете обратиться к [стилю кодирования фреймворка](https://github.com/yiisoft/yii2/blob/master/docs/internals/core-code-style.md).\n\n\n## Базовые расширения <span id=\"core-extensions\"></span>\n\nYii предоставляет следующие базовые расширения, (или [\"Official Extensions\"](https://www.yiiframework.com/extensions/official)) которые разрабатывает и поддерживает команда разработчиков Yii. Они все\nзарегистрированы на [Packagist](https://packagist.org/) и могут быть легко установлены, как описано в подразделе\n[Использование расширений](#using-extensions).\n\n- [yiisoft/yii2-apidoc](https://www.yiiframework.com/extension/yiisoft/yii2-apidoc):\n  предоставляет расширяемый и высокопроизводительный генератор документации API. Оно также используется для генерации\n  документации API фреймворка.\n- [yiisoft/yii2-authclient](https://www.yiiframework.com/extension/yiisoft/yii2-authclient):\n  предоставляет набор наиболее часто используемых клиентов авторизации, таких, как Facebook OAuth2 клиент и GitHub\n  OAuth2 клиент.\n- [yiisoft/yii2-bootstrap](https://www.yiiframework.com/extension/yiisoft/yii2-bootstrap):\n  предоставляет набор виджетов, которые являются компонентами и плагинами [Bootstrap](https://getbootstrap.com/).\n- [yiisoft/yii2-debug](https://www.yiiframework.com/extension/yiisoft/yii2-debug):\n  предоставляет поддержку отладки в приложениях Yii. Когда это расширение используется, отладочная панель появится в\n  нижней части каждой страницы. Это расширение также предоставляет набор отдельных страниц для отображения более\n  подробной отладочной информации.\n- [yiisoft/yii2-elasticsearch](https://www.yiiframework.com/extension/yiisoft/yii2-elasticsearch):\n  предоставляет поддержку использования [Elasticsearch](https://www.elastic.co/). Оно включает в себя поддержку\n  основных поисковых запросов, а также реализует шаблон проектирования [Active Record](db-active-record.md), который\n  позволяет хранить записи Active Record в Elasticsearch.\n- [yiisoft/yii2-faker](https://github.com/yiisoft/yii2-faker):\n  предоставляет поддержку использования [Faker](https://github.com/fzaninotto/Faker) для генерации фиктивных данных.\n- [yiisoft/yii2-gii](https://github.com/yiisoft/yii2-gii):\n  предоставляет веб-интерфейс для генерации кода, который является весьма расширяемым и может быть использован для\n  быстрой генерации моделей, форм, модулей, CRUD и т.д.\n- [yiisoft/yii2-httpclient](https://github.com/yiisoft/yii2-httpclient):\n  предоставляет HTTP клиент.\n- [yiisoft/yii2-imagine](https://github.com/yiisoft/yii2-imagine):\n  предоставляет часто используемые функции для работы с изображениями, основанные на библиотеке\n  [Imagine](https://imagine.readthedocs.org/).\n- [yiisoft/yii2-jui](https://github.com/yiisoft/yii2-jui):\n  предоставляет набор виджетов, основанный на взаимодействиях и виджетах [JQuery UI](https://jqueryui.com/).\n- [yiisoft/yii2-mongodb](https://github.com/yiisoft/yii2-mongodb):\n  предоставляет поддержку использования [MongoDB](https://www.mongodb.com/). Оно включает такие возможности, как\n  базовые запросы, Active Record, миграции, кэширование, генерация кода и т.д.\n- [yiisoft/yii2-redis](https://github.com/yiisoft/yii2-redis):\n  предоставляет поддержку использования [redis](https://redis.io/). Оно включает такие возможности, как базовые запросы,\n  Active Record, кэширование и т.д.\n- [yiisoft/yii2-smarty](https://github.com/yiisoft/yii2-smarty):\n  предоставляет шаблонизатор, основанный на [Smarty](https://www.smarty.net/).\n- [yiisoft/yii2-sphinx](https://github.com/yiisoft/yii2-sphinx):\n  предоставляет поддержку использования [Sphinx](https://sphinxsearch.com). Оно включает такие возможности, как базовые\n  запросы, Active Record, генерация кода и т.д.\n- [yiisoft/yii2-swiftmailer](https://github.com/yiisoft/yii2-swiftmailer):\n  предоставляет возможности отправки email, основанные на [swiftmailer](https://swiftmailer.org/).\n- [yiisoft/yii2-twig](https://github.com/yiisoft/yii2-twig):\n  предоставляет шаблонизатор, основанный на [Twig](https://twig.symfony.com/).\n"
  },
  {
    "path": "docs/guide-ru/structure-filters.md",
    "content": "Фильтры\n=======\n\nФильтры — это объекты, которые могут запускаться как перед так и после [действий контроллера](structure-controllers.md#actions).\nНапример, фильтр управления доступом может запускаться перед действиями удостовериться, что запросившему их пользователю\nразрешен доступ; фильтр сжатия содержимого может запускаться после действий для сжатия содержимого ответа перед отправкой\nего конечному пользователю.\n\nФильтр может состоять из *пре-фильтра* (фильтрующая логика применяется *перед* действиями) и/или\n*пост-фильтра* (логика, применяемая *после* действий).\n\n## Использование фильтров <span id=\"using-filters\"></span>\n\nФильтры являются особым видом [поведений](concept-behaviors.md). Их использование ничем не отличается от\n[использования поведений](concept-behaviors.md#attaching-behaviors). Вы можете объявлять фильтры в классе контроллера\nпутём перекрытия метода [[yii\\base\\Controller::behaviors()|behaviors()]]:\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\HttpCache',\n            'only' => ['index', 'view'],\n            'lastModified' => function ($action, $params) {\n                $q = new \\yii\\db\\Query();\n                return $q->from('user')->max('updated_at');\n            },\n        ],\n    ];\n}\n```\n\nПо умолчанию фильтры, объявленные в классе контроллера, будут применяться ко *всем* его действиям. Тем не менее, вы можете\nявно указать и конкретные действия задав свойство [[yii\\base\\ActionFilter::only|only]]. В примере выше фильтр `HttpCache`\nприменяется только к действиям `index` и `view`. Вы можете настроить свойство [[yii\\base\\ActionFilter::except|except]]\nчтобы указать действия, к которым фильтр применяться не должен.\n\nКроме контроллеров, можно объявлять фильтры в [модуле](structure-modules.md) или в [приложении](structure-applications.md).\nВ этом случае они применяются ко *всем* действиям контроллеров, находящихся в этом модуле или приложении если не заданы\nсвойства [[yii\\base\\ActionFilter::only|only]] и [[yii\\base\\ActionFilter::except|except]] как было описано выше.\n\n> Note: При объявлении фильтров в модулях или приложениях, следует использовать [маршруты](structure-controllers.md#routes)\n  вместо идентификаторов действий в свойствах [[yii\\base\\ActionFilter::only|only]] и [[yii\\base\\ActionFilter::except|except]]\n  так как сами по себе, идентификаторы действий не могут полностью идентифицировать действие в контексте модуля или приложения.\n\nКогда несколько фильтров указываются для одного действия, они применяются согласно следующим правилам:\n\n* Пре-фильтрация\n    - Применяются фильтры, объявленные в приложении в том порядке, в котором они перечислены в `behaviors()`.\n    - Применяются фильтры, объявленные в модуле в том порядке, в котором они перечислены в `behaviors()`.\n    - Применяются фильтры, объявленные в контроллере в том порядке, в котором они перечислены в `behaviors()`.\n    - Если, какой-либо из фильтров отменяет выполнение действия, оставшиеся фильтры (как пре-фильтры, так и пост-фильтры) не будут применены.\n* Выполняется действие, если оно прошло пре-фильтрацию.\n* Пост-фильтрация\n    - Применяются фильтры объявленные в контроллере, в порядке обратном, перечисленному в `behaviors()`.\n    - Применяются фильтры объявленные в модуле, в порядке обратном, перечисленному в `behaviors()`.\n    - Применяются фильтры объявленные в приложении, в порядке обратном, перечисленному в `behaviors()`.\n\n\n## Создание фильтров <span id=\"creating-filters\"></span>\n\nПри создании нового фильтра действия, необходимо наследоваться от [[yii\\base\\ActionFilter]] и переопределить методы\n[[yii\\base\\ActionFilter::beforeAction()|beforeAction()]] и/или [[yii\\base\\ActionFilter::afterAction()|afterAction()]].\nПервый из них будет вызван перед выполнением действия, а второй после. Возвращаемое\n[[yii\\base\\ActionFilter::beforeAction()|beforeAction()]] значение определяет, будет ли действие выполняться или нет.\nЕсли вернётся `false`, то оставшиеся фильтры не будут применены и действие выполнено не будет.\n\nПример ниже показывает фильтр, который выводит время выполнения действия:\n\n```php\nnamespace app\\components;\n\nuse Yii;\nuse yii\\base\\ActionFilter;\n\nclass ActionTimeFilter extends ActionFilter\n{\n    private $_startTime;\n\n    public function beforeAction($action)\n    {\n        $this->_startTime = microtime(true);\n        return parent::beforeAction($action);\n    }\n\n    public function afterAction($action, $result)\n    {\n        $time = microtime(true) - $this->_startTime;\n        Yii::debug(\"Action '{$action->uniqueId}' spent $time second.\");\n        return parent::afterAction($action, $result);\n    }\n}\n```\n\n\n## Стандартные фильтры <span id=\"core-filters\"></span>\n\nYii предоставляет набор часто используемых фильтров, которые находятся, в основном, в пространстве имен `yii\\filters`.\nДалее вы будете кратко ознакомлены с ними.\n\n\n### [[yii\\filters\\AccessControl|AccessControl]] <span id=\"access-control\"></span>\n\nФильтр `AccessControl` обеспечивает простое управление доступом, основанное на наборе правил [[yii\\filters\\AccessControl::rules|rules]].\nВ частности, перед тем как действие начинает выполнение, фильтр `AccessControl` проверяет список указанных правил, пока не\nнайдёт соответствующее текущему контексту переменных (таких как IP адрес пользователя, статус аутентификации и так далее).\nНайденное правило указывает, разрешить или запретить выполнение запрошенного действия. Если ни одно из правил не подходит,\nто доступ будет запрещён.\n\nВ следующем примере авторизованным пользователям разрешен доступ к действиям `create` и `update`, в то время как всем\nдругим пользователям доступ запрещён.\n\n```php\nuse yii\\filters\\AccessControl;\n\npublic function behaviors()\n{\n    return [\n        'access' => [\n            'class' => AccessControl::class,\n            'only' => ['create', 'update'],\n            'rules' => [\n                // разрешаем аутентифицированным пользователям\n                [\n                    'allow' => true,\n                    'roles' => ['@'],\n                ],\n                // всё остальное по умолчанию запрещено\n            ],\n        ],\n    ];\n}\n```\n\nБолее подробно об управлении доступом вы можете прочитать в разделе [Авторизация](security-authorization.md).\n\n\n### Фильтр метода аутентификации<span id=\"auth-method-filters\"></span>\n\nФильтр метода аутентификации используется для аутентификации пользователя различными способами, такими как\n[HTTP Basic Auth](https://en.wikipedia.org/wiki/Basic_access_authentication), [OAuth 2](https://oauth.net/2/).\nКлассы данных фильтров находятся в пространстве имён `yii\\filters\\auth`.\n\nСледующий пример показывает, как использовать [[yii\\filters\\auth\\HttpBasicAuth]] для аутентификации пользователя с помощью\nтокена доступа, основанного на методе basic HTTP auth. Обратите внимание, что для того чтобы это работало, ваш класс\n[[yii\\web\\User::identityClass|user identity class]] должен реализовывать метод\n[[yii\\web\\IdentityInterface::findIdentityByAccessToken()|findIdentityByAccessToken()]].\n\n```php\nuse yii\\filters\\auth\\HttpBasicAuth;\n\npublic function behaviors()\n{\n    return [\n        'basicAuth' => [\n            'class' => HttpBasicAuth::class,\n        ],\n    ];\n}\n```\n\nФильтры метода аутентификации часто используются при реализации RESTful API. Более подробную информацию о технологии \nRESTful, смотрите в разделе [Authentication](rest-authentication.md).\n\n\n### [[yii\\filters\\ContentNegotiator|ContentNegotiator]] <span id=\"content-negotiator\"></span>\n\nContentNegotiator поддерживает согласование формата ответа и языка приложения. Он пытается определить формат ответа\nи/или язык, путём проверки `GET` параметров и HTTP заголовка `Accept`.\n\nВ примере ниже, ContentNegotiator сконфигурирован чтобы поддерживать форматы ответа JSON и XML, а также Английский (США)\nи Немецкий языки.\n\n```php\nuse yii\\filters\\ContentNegotiator;\nuse yii\\web\\Response;\n\npublic function behaviors()\n{\n    return [\n        [\n            'class' => ContentNegotiator::class,\n            'formats' => [\n                'application/json' => Response::FORMAT_JSON,\n                'application/xml' => Response::FORMAT_XML,\n            ],\n            'languages' => [\n                'en-US',\n                'de',\n            ],\n        ],\n    ];\n}\n```\n\nЧасто требуется, чтобы форматы ответа и языки приложения были определены как можно раньше в его\n[жизненном цикле](structure-applications.md#application-lifecycle). По этой причине, ContentNegotiator разработан так, что\nпомимо фильтра может использоваться как [компонент предварительной загрузки](structure-applications.md#bootstrap). Например,\nвы можете настроить его в [конфигурации приложения](structure-applications.md#application-configurations):\n\n```php\nuse yii\\filters\\ContentNegotiator;\nuse yii\\web\\Response;\n\n[\n    'bootstrap' => [\n        [\n            'class' => ContentNegotiator::class,\n            'formats' => [\n                'application/json' => Response::FORMAT_JSON,\n                'application/xml' => Response::FORMAT_XML,\n            ],\n            'languages' => [\n                'en-US',\n                'de',\n            ],\n        ],\n    ],\n];\n```\n\n> Info: В случае, если предпочтительный тип содержимого и язык не могут быть определены из запроса, будут\n  использованы первый формат и язык, описанные в [[formats]] и [[languages]].\n\n\n\n### [[yii\\filters\\HttpCache|HttpCache]] <span id=\"http-cache\"></span>\n\nФильтр HttpCache реализовывает кэширование на стороне клиента, используя HTTP заголовки `Last-Modified` и `Etag`:\n\n```php\nuse yii\\filters\\HttpCache;\n\npublic function behaviors()\n{\n    return [\n        [\n            'class' => HttpCache::class,\n            'only' => ['index'],\n            'lastModified' => function ($action, $params) {\n                $q = new \\yii\\db\\Query();\n                return $q->from('user')->max('updated_at');\n            },\n        ],\n    ];\n}\n```\n\nПодробнее об использовании HttpCache можно прочитать в разделе [HTTP Кэширование](caching-http.md).\n\n\n### [[yii\\filters\\PageCache|PageCache]] <span id=\"page-cache\"></span>\n\nФильтр PageCache реализует кэширование целых страниц на стороне сервера. В следующем примере PageCache применяется только\nв действии `index` для кэширования всей страницы в течение не более чем 60 секунд или пока количество записей в таблице `post`\nне изменится. Он также хранит различные версии страницы в зависимости от выбранного языка приложения.\n\n```php\nuse yii\\filters\\PageCache;\nuse yii\\caching\\DbDependency;\n\npublic function behaviors()\n{\n    return [\n        'pageCache' => [\n            'class' => PageCache::class,\n            'only' => ['index'],\n            'duration' => 60,\n            'dependency' => [\n                'class' => DbDependency::class,\n                'sql' => 'SELECT COUNT(*) FROM post',\n            ],\n            'variations' => [\n                \\Yii::$app->language,\n            ]\n        ],\n    ];\n}\n```\n\nПодробнее об использовании PageCache читайте в разделе [Кэширование страниц](caching-page.md).\n\n\n### [[yii\\filters\\RateLimiter|RateLimiter]] <span id=\"rate-limiter\"></span>\n\nОграничитель количества запросов в единицу времени *(RateLimiter)* реализует алгоритм ограничения запросов, основанный на\n[алгоритме leaky bucket](https://en.wikipedia.org/wiki/Leaky_bucket). В основном, он используется при создании RESTful API.\nПодробнее об использовании данного фильтра можно прочитать в разделе [Ограничение запросов](rest-rate-limiting.md).\n\n\n### [[yii\\filters\\VerbFilter|VerbFilter]] <span id=\"verb-filter\"></span>\n\nФильтр по типу запроса *(VerbFilter)* проверяет, разрешено ли запросам HTTP выполнять затребованные ими действия.\nЕсли нет, то будет выброшено исключение HTTP с кодом 405. В следующем примере в фильтре по типу запроса указан обычный\nнабор разрешённых методов запроса при выполнении CRUD операций.\n\n```php\nuse yii\\filters\\VerbFilter;\n\npublic function behaviors()\n{\n    return [\n        'verbs' => [\n            'class' => VerbFilter::class,\n            'actions' => [\n                'index'  => ['get'],\n                'view'   => ['get'],\n                'create' => ['get', 'post'],\n                'update' => ['get', 'put', 'post'],\n                'delete' => ['post', 'delete'],\n            ],\n        ],\n    ];\n}\n```\n\n### [[yii\\filters\\Cors|Cors]] <span id=\"cors\"></span>\n\nСовместное использование разными источниками [CORS](https://developer.mozilla.org/ru/docs/Web/HTTP/CORS)\n- это механизм, который позволяет использовать различные ресурсы (шрифты, скрипты, и т.д.) с отличных от основного сайта\nдоменов. В частности, AJAX вызовы JavaScript могут использовать механизм XMLHttpRequest. В противном случае, такие\n\"междоменные\" запросы были бы запрещены из-за политики безопасности same origin. CORS задаёт способ взаимодействия\nсервера и браузера, определяющий возможность делать междоменные запросы.\n\nФильтр [[yii\\filters\\Cors|Cors filter]] следует определять перед фильтрами Аутентификации / Авторизации, для того чтобы\nбыть уверенными, что заголовки CORS будут всегда посланы.\n\n```php\nuse yii\\filters\\Cors;\nuse yii\\helpers\\ArrayHelper;\n\npublic function behaviors()\n{\n    return ArrayHelper::merge([\n        [\n            'class' => Cors::class,\n        ],\n    ], parent::behaviors());\n}\n```\n\nЕсли вам необходимо добавить CORS-фильтрацию к [[yii\\rest\\ActiveController]] в вашем API, обратитесь к разделу\n[Контроллеры](rest-controllers.md#cors).\n\nФильтрация Cors может быть настроена с помощью свойства [[yii\\filters\\Cors::$cors|$cors]].\n\n* `cors['Origin']`: массив, используемый для определения источников. Может принимать значение `['*']` (все) или\n  `['https://www.myserver.net', 'https://www.myotherserver.com']`. По умолчанию значение равно `['*']`.\n* `cors['Access-Control-Request-Method']`: массив разрешенных типов запроса, таких как `['GET', 'OPTIONS', 'HEAD']`.\n  Значение по умолчанию `['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']`.\n* `cors['Access-Control-Request-Headers']`: массив разрешенных заголовков. Может быть `['*']` то есть все заголовки или\n  один из указанных `['X-Request-With']`. Значение по умолчанию `['*']`.\n* `cors['Access-Control-Allow-Credentials']`: определяет, может ли текущий запрос быть сделан с использованием авторизации.\n  Может принимать значения `true`, `false` или `null` (не установлено). Значение по умолчанию `null`.\n* `cors['Access-Control-Max-Age']`: определяет *срок жизни запроса, перед его началом*. По умолчанию `86400`.\n\nНапример, разрешим CORS для источника : `https://www.myserver.net` с методами `GET`, `HEAD` и `OPTIONS` :\n\n```php\nuse yii\\filters\\Cors;\nuse yii\\helpers\\ArrayHelper;\n\npublic function behaviors()\n{\n    return ArrayHelper::merge([\n        [\n            'class' => Cors::class,\n            'cors' => [\n                'Origin' => ['https://www.myserver.net'],\n                'Access-Control-Request-Method' => ['GET', 'HEAD', 'OPTIONS'],\n            ],\n        ],\n    ], parent::behaviors());\n}\n```\n\nВы можете настроить заголовки CORS переопределения параметров по умолчанию *для каждого из действий.*\n\nНапример, добавление `Access-Control-Allow-Credentials` для действия  `login` может быть сделано так :\n\n```php\nuse yii\\filters\\Cors;\nuse yii\\helpers\\ArrayHelper;\n\npublic function behaviors()\n{\n    return ArrayHelper::merge([\n        [\n            'class' => Cors::class,\n            'cors' => [\n                'Origin' => ['https://www.myserver.net'],\n                'Access-Control-Request-Method' => ['GET', 'HEAD', 'OPTIONS'],\n            ],\n            'actions' => [\n                'login' => [\n                    'Access-Control-Allow-Credentials' => true,\n                ]\n            ]\n        ],\n    ], parent::behaviors());\n}\n```\n"
  },
  {
    "path": "docs/guide-ru/structure-models.md",
    "content": "Модели\n======\n\nМодели являются частью архитектуры [MVC](https://ru.wikipedia.org/wiki/Model-View-Controller) (Модель-Вид-Контроллер). Они представляют собой объекты бизнес данных, правил и логики.\n\nВы можете создавать классы моделей путём расширения класса [[yii\\base\\Model]] или его дочерних классов. Базовый класс [[yii\\base\\Model]] поддерживает много полезных функций:\n\n* [Атрибуты](#attributes): представляют собой рабочие данные и могут быть доступны как обычные свойства объекта или элементы массива;\n* [Метки атрибутов](#attribute-labels): задают отображение атрибута;\n* [Массовое присвоение](#massive-assignment): поддержка заполнения нескольких атрибутов в один шаг;\n* [Правила проверки](#validation-rules): обеспечивают ввод данных на основе заявленных правил проверки;\n* [Экспорт Данных](#data-exporting): разрешает данным модели быть экспортированными в массивы с настройкой форматов.\n\nКласс `Model` также является базовым классом для многих расширенных моделей, таких как [Active Record](db-active-record.md). Пожалуйста, обратитесь к соответствующей документации для более подробной информации об этих расширенных моделях.\n\n> Info: Вы не обязаны основывать свои классы моделей на [[yii\\base\\Model]]. Однако, поскольку в yii есть много компонентов, созданных для поддержки [[yii\\base\\Model]], обычно так делать предпочтительнее для базового класса модели.\n\n## Атрибуты <span id=\"attributes\"></span>\n\nМодели предоставляют рабочие данные в терминах *атрибутах*. Каждый атрибут представляет собой публично доступное свойство модели. Метод [[yii\\base\\Model::attributes()]] определяет какие атрибуты имеет класс модели.\n\nВы можете получить доступ к атрибуту как к обычному свойству объекта:\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// \"name\" - это атрибут модели ContactForm\n$model->name = 'example';\necho $model->name;\n```\n\nТакже возможно получить доступ к атрибутам как к элементам массива, спасибо поддержке [ArrayAccess](https://www.php.net/manual/ru/class.arrayaccess.php) и [Traversable](https://www.php.net/manual/ru/class.traversable.php)\nв [[yii\\base\\Model]]:\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// доступ к атрибутам как к элементам массива\n$model['name'] = 'example';\necho $model['name'];\n\n// Модель является обходимой(traversable) с использованием foreach.\nforeach ($model as $name => $value) {\n    echo \"$name: $value\\n\";\n}\n```\n\n\n### Определение Атрибутов <span id=\"defining-attributes\"></span>\n\nПо умолчанию, если ваш класс модели расширяется напрямую от [[yii\\base\\Model]], то все *не статичные публичные* переменные являются атрибутами. Например, у класса модели `ContactForm` , который находится ниже, четыре атрибута: `name`, `email`, `subject` и `body`. Модель `ContactForm` используется для представления входных данных, полученных из HTML формы.\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\n\nclass ContactForm extends Model\n{\n    public $name;\n    public $email;\n    public $subject;\n    public $body;\n}\n```\n\nВы можете переопределить метод [[yii\\base\\Model::attributes()]], чтобы определять атрибуты другим способом. Метод должен возвращать имена атрибутов в модели. Например [[yii\\db\\ActiveRecord]] делает так, возвращая имена столбцов из связанной таблицы базы данных в качестве имён атрибутов. Также может понадобиться переопределить магические методы, такие как `__get()`, `__set()` для того, чтобы атрибуты могли быть доступны как обычные свойства объекта.\n\n\n### Метки атрибутов <span id=\"attribute-labels\"></span>\n\nПри отображении значений или при получении ввода значений атрибутов, часто требуется отобразить некоторые надписи, связанные с атрибутами. Например, если атрибут назван `firstName`, Вы можете отобразить его как `First Name`, что является более удобным для пользователя, в тех случаях, когда атрибут отображается конечным пользователям в таких местах, как форма входа и сообщения об ошибках.\n\nВы можете получить метку атрибута, вызвав [[yii\\base\\Model::getAttributeLabel()]]. Например,\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// отобразит \"Name\"\necho $model->getAttributeLabel('name');\n```\n\nПо умолчанию, метки атрибутов автоматически генерируются из названия атрибута. Генерация выполняется методом [[yii\\base\\Model::generateAttributeLabel()]]. Он превращает первую букву каждого слова в верхний регистр, если имена переменных состоят из нескольких слов. Например, `username` станет `Username`, а `firstName` станет `First Name`.\n\nЕсли Вы не хотите использовать автоматически сгенерированные метки, Вы можете переопределить метод [[yii\\base\\Model::attributeLabels()]], чтобы явно объявить метку атрибута. Например,\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\n\nclass ContactForm extends Model\n{\n    public $name;\n    public $email;\n    public $subject;\n    public $body;\n\n    public function attributeLabels()\n    {\n        return [\n            'name' => 'Your name',\n            'email' => 'Your email address',\n            'subject' => 'Subject',\n            'body' => 'Content',\n        ];\n    }\n}\n```\n\nДля приложений поддерживающих мультиязычность, Вы можете перевести метки атрибутов. Это можно сделать в методе [[yii\\base\\Model::attributeLabels()|attributeLabels()]] как показано ниже:\n\n```php\npublic function attributeLabels()\n{\n    return [\n        'name' => \\Yii::t('app', 'Your name'),\n        'email' => \\Yii::t('app', 'Your email address'),\n        'subject' => \\Yii::t('app', 'Subject'),\n        'body' => \\Yii::t('app', 'Content'),\n    ];\n}\n```\n\nМожно даже условно определять метки атрибутов. Например, на основе [сценариев](#scenarios) и использованной в нём модели , Вы можете возвращать различные метки для одного и того же атрибута.\n\n> Для справки: Строго говоря, метки атрибутов являются частью [видов](structure-views.md). Но объявление меток в моделях часто очень удобно и приводит к чистоте кода и повторному его использованию.\n\n## Сценарии <span id=\"scenarios\"></span>\n\nМодель может быть использована в различных *сценариях*. Например, модель `User` может быть использована для коллекции входных логинов пользователей, а также может быть использована для цели регистрации пользователей.  \t\nВ различных сценариях, модель может использовать различные бизнес-правила и логику. Например, атрибут `email` может потребоваться во время регистрации пользователя, но не во время входа пользователя в систему.\n\nМодель использует свойство [[yii\\base\\Model::scenario]], чтобы отслеживать сценарий, в котором она используется. По умолчанию, модель поддерживает только один сценарий с именем `default`. В следующем коде показано два способа установки сценария модели:\n\n```php\n// сценарий задается как свойство\n$model = new User;\n$model->scenario = User::SCENARIO_LOGIN;\n\n// сценарий задается через конфигурацию\n$model = new User(['scenario' => User::SCENARIO_LOGIN]);\n```\n\nПо умолчанию сценарии, поддерживаемые моделью, определяются [правилами валидации](#validation-rules) объявленными\nв модели. Однако Вы можете изменить это поведение путем переопределения метода [[yii\\base\\Model::scenarios()]] как показано ниже:\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass User extends ActiveRecord\n{\n    const SCENARIO_LOGIN = 'login';\n    const SCENARIO_REGISTER = 'register';\n\n    public function scenarios()\n    {\n        return [\n            self::SCENARIO_LOGIN => ['username', 'password'],\n            self::SCENARIO_REGISTER => ['username', 'email', 'password'],\n        ];\n    }\n}\n```\n\n> Info: В приведенном выше и следующих примерах, классы моделей расширяются от [[yii\\db\\ActiveRecord]] потому, что использование нескольких сценариев обычно происходит от классов [Active Record](db-active-record.md).\n\nМетод `scenarios()` возвращает массив, ключами которого являются имена сценариев, а значения - соответствующие *активные атрибуты*. Активные атрибуты могут быть [массово присвоены](#massive-assignment) и подлежат [валидации](#validation-rules). В приведенном выше примере, атрибуты `username` и `password` это активные атрибуты сценария `login`, а в сценарии `register` так же активным атрибутом является `email` вместе с `username` и `password`.\n\nПо умолчанию реализация `scenarios()` вернёт все найденные сценарии в правилах валидации, задекларированных в методе [[yii\\base\\Model::rules()]]. При переопределении метода `scenarios()`, если Вы хотите ввести новые сценарии помимо стандартных, Вы можете написать код на основе следующего примера:\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass User extends ActiveRecord\n{\n    const SCENARIO_LOGIN = 'login';\n    const SCENARIO_REGISTER = 'register';\n\n    public function scenarios()\n    {\n        $scenarios = parent::scenarios();\n        $scenarios[self::SCENARIO_LOGIN] = ['username', 'password'];\n        $scenarios[self::SCENARIO_REGISTER] = ['username', 'email', 'password'];\n        return $scenarios;\n    }\n}\n```\n\nВозможности сценариев в основном используются [валидацией](#validation-rules) и [массовым присвоением атрибутов](#massive-assignment). Однако, Вы можете использовать их и для других целей. Например, Вы можете различным образом объявлять [метки атрибутов](#attribute-labels) на основе текущего сценария.\n\n\n##  Правила валидации <span id=\"validation-rules\"></span>\n\nКогда данные модели, получены от конечных пользователей, они должны быть проверены, для того чтобы убедиться, что данные удовлетворяют определенным правилам (так называемым *правилам валидации* также известными как *бизнес-правила*). Например, дана модель `ContactForm`, возможно Вы захотите убедиться, что все атрибуты являются не пустыми значениями, а атрибут `email` содержит допустимый адрес электронной почты. Если значения нескольких атрибутов не удовлетворяют соответствующим бизнес-правилам, то должны быть показаны соответствующие сообщения об ошибках, чтобы помочь конечному пользователю исправить допущенные ошибки.\n\nВы можете вызвать [[yii\\base\\Model::validate()]] для проверки полученных данных. Данный метод будет использовать\nправила валидации определённые в [[yii\\base\\Model::rules()]] для проверки каждого соответствующего атрибута. Если ошибок не найдено, то возвращается `true`, в противном случае возвращается `false`, а ошибки содержит свойство [[yii\\base\\Model::errors]]. Например,\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// модель заполнения атрибутов данными, вводимыми пользователем\n$model->attributes = \\Yii::$app->request->post('ContactForm');\n\nif ($model->validate()) {\n    // все данные верны\n} else {\n    // проверка не удалась:  $errors - это массив содержащий сообщения об ошибках\n    $errors = $model->errors;\n}\n```\n\nОбъявляем правила валидации связанные с моделью, переопределяем метод [[yii\\base\\Model::rules()]] возврата правил атрибутов модели которые следует удовлетворить. В следующем примере показаны правила проверки объявленные в модели `ContactForm`:\n\n```php\npublic function rules()\n{\n    return [\n        // name, email, subject и body атрибуты обязательны\n        [['name', 'email', 'subject', 'body'], 'required'],\n\n        // атрибут email должен быть правильным email адресом\n        ['email', 'email'],\n    ];\n}\n```\n\nПравило может использоваться для проверки одного или нескольких атрибутов, также и атрибут может быть проверен одним или несколькими правилами. Пожалуйста, обратитесь к разделу [Проверка входных значений](input-validation.md) для более подробной информации о том, как объявлять правила проверки.\n\nИногда необходимо, чтобы правила применялись только в определенных [сценариях](#scenarios). Чтобы это сделать необходимо указать свойство `on` в правилах, следующим образом:\n\n```php\npublic function rules()\n{\n    return [\n        // username, email и password требуются в сценарии \"register\"\n        [['username', 'email', 'password'], 'required', 'on' => self::SCENARIO_REGISTER],\n\n        // username и password требуются в сценарии \"login\"\n        [['username', 'password'], 'required', 'on' => self::SCENARIO_LOGIN],\n    ];\n}\n```\n\nЕсли не указать свойство `on`, то правило применяется во всех сценариях. Правило называется *активным правилом* если оно может быть применено в текущем сценарии [[yii\\base\\Model::scenario|scenario]].\n\nАтрибут будет проверяться тогда и только тогда если он является активным атрибутом объявленным в `scenarios()` и\nсвязанным с одним или несколькими активными правилами, объявленными в `rules()`.\n\n## Массовое Присвоение <span id=\"massive-assignment\"></span>\n\nМассовое присвоение - это удобный способ заполнения модели данными вводимыми пользователем с помощью одной строки кода. Он заполняет атрибуты модели путем присвоения входных данных непосредственно свойству [[yii\\base\\Model::$attributes]]. Следующие два куска кода эквивалентны, они оба пытаются присвоить данные из формы представленные конечными пользователями атрибутам модели `ContactForm`. Ясно, что первый код гораздо чище и менее подвержен ошибкам, чем второй:\n\n```php\n$model = new \\app\\models\\ContactForm;\n$model->attributes = \\Yii::$app->request->post('ContactForm');\n```\n\n```php\n$model = new \\app\\models\\ContactForm;\n$data = \\Yii::$app->request->post('ContactForm', []);\n$model->name = isset($data['name']) ? $data['name'] : null;\n$model->email = isset($data['email']) ? $data['email'] : null;\n$model->subject = isset($data['subject']) ? $data['subject'] : null;\n$model->body = isset($data['body']) ? $data['body'] : null;\n```\n\n\n### Безопасные Атрибуты <span id=\"safe-attributes\"></span>\n\nМассовое присвоение применяется только к так называемым *безопасным атрибутам*, которые являются атрибутами, перечисленными в [[yii\\base\\Model::scenarios()]] в текущем сценарии [[yii\\base\\Model::scenario|scenario]] модели. Например, если модель `User` имеет следующий заданный сценарий, в данном случае это сценарий `login`, то только `username` и `password` могут быть массово присвоены. Любые другие атрибуты останутся нетронутыми.\n\n```php\npublic function scenarios()\n{\n    return [\n        self::SCENARIO_LOGIN => ['username', 'password'],\n        self::SCENARIO_REGISTER => ['username', 'email', 'password'],\n    ];\n}\n```\n\n> Info: Причиной того, что массовое присвоение атрибутов применяется только к безопасным атрибутам, является то, что необходимо контролировать какие атрибуты могут быть изменены конечными пользователями. Например, если модель `User` имеет атрибут `permission`, который определяет разрешения, назначенные пользователю, то необходимо быть уверенным, что данный атрибут может быть изменён только администраторами через бэкэнд-интерфейс.\n\nПо умолчанию [[yii\\base\\Model::scenarios()]] будет возвращать все сценарии и атрибуты найденные в [[yii\\base\\Model::rules()]], если не переопределить этот метод, атрибут будет считаться безопасным только в случае, если он участвует в любом из активных правил проверки.\n\nПо этой причине существует специальный валидатор с псевдонимом `safe`, он предоставляет возможность объявить атрибут безопасным без фактической его проверки. Например, следующие правила определяют, что оба атрибута `title` и `description` являются безопасными атрибутами.\n\n```php\npublic function rules()\n{\n    return [\n        [['title', 'description'], 'safe'],\n    ];\n}\n```\n\n\n### Небезопасные атрибуты <span id=\"unsafe-attributes\"></span>\n\nКак сказано выше, метод [[yii\\base\\Model::scenarios()]] служит двум целям: определения, какие атрибуты должны быть проверены, и определения, какие атрибуты являются безопасными (т.е. не требуют проверки). В некоторых случаях необходимо проверить атрибут не объявляя его безопасным. Вы можете сделать это с помощью префикса восклицательный знак `!` в имени атрибута при объявлении его в `scenarios()` как атрибут `secret` в следующем примере:\n\n```php\npublic function scenarios()\n{\n    return [\n        self::SCENARIO_LOGIN => ['username', 'password', '!secret'],\n    ];\n}\n```\n\nКогда модель будет присутствовать в сценарии `login`, то все три эти атрибута будут проверены. Однако, только атрибуты `username` и `password` могут быть массово присвоены. Назначить входное значение атрибуту `secret` нужно явно следующим образом,\n\n```php\n$model->secret = $secret;\n```\n\n\n## Экспорт Данных <span id=\"data-exporting\"></span>\n\nЧасто нужно экспортировать модели в различные форматы. Например, может потребоваться преобразовать коллекцию моделей в JSON или Excel формат. Процесс экспорта может быть разбит на два самостоятельных шага. На первом этапе модели преобразуются в массивы; на втором этапе массивы преобразуются в целевые форматы. Вы можете сосредоточиться только на первом шаге потому, что второй шаг может быть достигнут путем универсального инструмента форматирования данных, такого как [[yii\\web\\JsonResponseFormatter]].\n\nСамый простой способ преобразования модели в массив - использовать свойство [[yii\\base\\Model::$attributes]].\nНапример\n\n```php\n$post = \\app\\models\\Post::findOne(100);\n$array = $post->attributes;\n```\n\nПо умолчанию свойство [[yii\\base\\Model::$attributes]] возвращает значения *всех* атрибутов объявленных в [[yii\\base\\Model::attributes()]].\n\nБолее гибкий и мощный способ конвертирования модели в массив - использовать метод [[yii\\base\\Model::toArray()]]. Его поведение по умолчанию такое же как и у [[yii\\base\\Model::$attributes]]. Тем не менее, он позволяет выбрать, какие элементы данных, называемые *полями*, поставить в результирующий массив и как они должны быть отформатированы. На самом деле, этот способ экспорта моделей по умолчанию применяется при разработке в RESTful Web service, как описано в [Response Formatting](rest-response-formatting.md).\n\n### Поля <span id=\"fields\"></span>\n\nПоле - это просто именованный элемент в массиве, который может быть получен вызовом метода [[yii\\base\\Model::toArray()]] модели.\n\nПо умолчанию имена полей эквивалентны именам атрибутов. Однако, это поведение можно изменить, переопределив методы\n[[yii\\base\\Model::fields()|fields()]] и/или [[yii\\base\\Model::extraFields()|extraFields()]]. Оба метода должны возвращать список определенных полей. Поля определённые `fields()` являются полями по умолчанию, это означает, что `toArray()` будет возвращать эти поля по умолчанию. Метод `extraFields()` определяет дополнительно доступные поля, которые также могут быть возвращены `toArray()` так много, как Вы укажите их через параметр `$expand`. Например, следующий код будет возвращать все поля определённые в `fields()`, а также поля `prettyName` и `fullAddress`, если они определены в `extraFields()`.\n\n```php\n$array = $model->toArray([], ['prettyName', 'fullAddress']);\n```\n\nВы можете переопределить `fields()` чтобы добавить, удалить, переименовать или переопределить поля. Возвращаемым значением `fields()` должен быть массив. Ключами массива являются имена полей, а значениями - соответствующие определения полей, которые могут быть либо именами свойств/атрибутов, либо анонимными функциями, возвращающими соответствующие значения полей. В частном случае, когда имя поля совпадает с именем его атрибута, возможно опустить ключ массива. Например,\n\n```php\n// использовать явное перечисление всех полей, лучше всего тогда, когда вы хотите убедиться,\n// что изменения в вашей таблице базы данных или атрибуте модели не вызывают изменение вашего поля\n// (для поддержания обратной совместимости API интерфейса).\n\npublic function fields()\n{\n    return [\n        // здесь имя поля совпадает с именем атрибута\n        'id',\n\n        // здесь имя поля - \"email\", соответствующее ему имя атрибута - \"email_address\"\n        'email' => 'email_address',\n\n        // здесь имя поля - \"name\", а значение определяется обратным вызовом PHP\n        'name' => function () {\n            return $this->first_name . ' ' . $this->last_name;\n        },\n    ];\n}\n\n// использовать фильтрование нескольких полей лучше тогда, когда вы хотите наследовать\n// родительскую реализацию и черный список некоторых \"чувствительных\" полей.\n\npublic function fields()\n{\n    $fields = parent::fields();\n\n    // удаляем поля, содержащие конфиденциальную информацию\n    unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);\n\n    return $fields;\n}\n```\n\n> Warning: по умолчанию все атрибуты модели будут включены в экспортируемый массив, вы должны проверить ваши данные и убедиться, что они не содержат конфиденциальной информации. Если такая информация присутствует, вы должны переопределить `fields()` и отфильтровать поля. В приведенном выше примере мы выбираем и отфильтровываем `auth_key`, `password_hash` и `password_reset_token`.\n\n## Лучшие практические методики разработки моделей <span id=\"best-practices\"></span>\n\nМодели являются центральным местом представления бизнес-данных, правил и логики. Они часто повторно используются в разных местах. В хорошо спроектированном приложении, модели, как правило, намного больше, чем [контроллеры](structure-controllers.md).\n\nВ целом, модели\n\n* могут содержать атрибуты для представления бизнес-данных;\n* могут содержать правила проверки для обеспечения целостности и достоверности данных;\n* могут содержать методы с реализацией бизнес-логики;\n* не следует напрямую задавать запрос на доступ, либо сессии, либо любые другие данные об окружающей среде. Эти данные должны быть введены [контроллерами](structure-controllers.md) в модели;\n* следует избегать встраивания HTML или другого отображаемого кода - это лучше делать в [видах](structure-views.md);\n* избегайте слишком большого количества [сценариев](#scenarios) в одной модели.\n\nРекомендации выше обычно учитываются при разработке больших сложных систем. В таких системах, модели могут быть очень большими, в связи с тем, что они используются во многих местах и поэтому могут содержать множество наборов правил и бизнес-логики. Это часто заканчивается кошмаром при поддержании кода модели, поскольку одним касанием кода можно повлиять на несколько разных мест. Чтобы сделать код модели более легким в обслуживании, Вы можете предпринять следующую стратегию:\n\n* Определить набор базовых классов моделей, которые являются общими для разных [приложений](structure-applications.md) или [модулей](structure-modules.md). Эти классы моделей должны содержать минимальный набор правил и логики, которые являются общими среди всех используемых приложений или модулей.\n* В каждом [приложении](structure-applications.md) или [модуле](structure-modules.md) в котором используется модель, определить конкретный класс модели (или классы моделей), отходящий от соответствующего базового класса модели. Конкретный класс модели должен содержать правила и логику, которые являются специфическими для данного приложения или модуля.\n\nНапример, в [шаблоне приложения advanced](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md), Вы можете определить базовым классом модели `common\\models\\Post`. Тогда для frontend приложения, Вы определяете и используете конкретный класс модели `frontend\\models\\Post`, который расширяется от `common\\models\\Post`. И аналогичным образом для backend приложения, Вы определяете `backend\\models\\Post`. С помощью такой стратегии, можно быть уверенным, что код в `frontend\\models\\Post` используется только для конкретного frontend приложения, и если делаются любые изменения в нём, то не нужно беспокоиться, что изменения могут сломать backend приложение.\n"
  },
  {
    "path": "docs/guide-ru/structure-modules.md",
    "content": "Модули\n=======\n\nМодули - это законченные программные блоки, состоящие из [моделей](structure-models.md), [представлений](structure-views.md), [контроллеров](structure-controllers.md) и других вспомогательных компонентов. При установке модулей в [приложение](structure-applications.md), конечный пользователь получает доступ к их контроллерам. По этой причине модули часто рассматриваются как миниатюрные приложения. В отличие от [приложений](structure-applications.md), модули нельзя развертывать отдельно. Модули должны находиться внутри приложений.\n\n\n## Создание модулей <span id=\"creating-modules\"></span>\n\nМодуль помещается в директорию, которая называется [[yii\\base\\Module::basePath|базовым путем]] модуля. Так же как и в\nдиректории приложения, в этой директории существуют поддиректории `controllers`, `models`, `views` и другие, в которых\nразмещаются контроллеры, модели, представления и другие элементы. В следующем примере показано примерное содержимое модуля:\n\n```\nforum/\n    Module.php                   файл класса модуля\n    controllers/                 содержит файлы классов контроллеров\n        DefaultController.php    файл класса контроллера по умолчанию\n    models/                      содержит файлы классов моделей\n    views/                       содержит файлы представлений контроллеров и шаблонов\n        layouts/                 содержит файлы представлений шаблонов\n        default/                 содержит файлы представления контроллера DefaultController\n            index.php            файл основного представления\n```\n\n\n### Классы модулей <span id=\"module-classes\"></span>\n\nКаждый модуль объявляется с помощью уникального класса, который наследуется от [[yii\\base\\Module]]. Этот класс должен\nбыть помещен в корне [[yii\\base\\Module::basePath|базового пути]] модуля и поддерживать [автозагрузку](concept-autoloading.md).\nВо время доступа к модулю будет создан один экземпляр соответствующего класса модуля. Как и\n[экземпляры приложения](structure-applications.md), экземпляры модулей нужны, чтобы код модулей мог получить общий\nдоступ к данным и компонентам.\n\nПриведем пример того, как может выглядеть класс модуля:\n\n```php\nnamespace app\\modules\\forum;\n\nclass Module extends \\yii\\base\\Module\n{\n    public function init()\n    {\n        parent::init();\n\n        $this->params['foo'] = 'bar';\n        // ... остальной инициализирующий код ...\n    }\n}\n```\n\nЕсли метод `init()` стал слишком громоздким из-за кода, который задает свойства модуля, эти свойства можно сохранить\nв виде [конфигурации](concept-configurations.md), а затем загрузить в методе `init()` следующим образом:\n\n```php\npublic function init()\n{\n    parent::init();\n    // инициализация модуля с помощью конфигурации, загруженной из config.php\n    \\Yii::configure($this, require __DIR__ . '/config.php');\n}\n```\n\nПри этом в конфигурационном файле `config.php` может быть код следующего вида, аналогичный\n[конфигурации приложения](structure-applications.md#application-configurations):\n\n```php\n<?php\nreturn [\n    'components' => [\n        // список конфигураций компонентов\n    ],\n    'params' => [\n        // список параметров\n    ],\n];\n```\n\n\n### Контроллеры в модулях <span id=\"controllers-in-modules\"></span>\n\nПри создании контроллеров модуля принято помещать классы контроллеров в подпространство `controllers` пространства\nимён класса модуля. Это также подразумевает, что файлы классов контроллеров должны располагаться в директории `controllers`\n[[yii\\base\\Module::basePath|базового пути]] модуля. Например, чтобы описать контроллер `post` в модуле `forum` из\nпредыдущего примера, класс контроллера объявляется следующим образом:\n\n```php\nnamespace app\\modules\\forum\\controllers;\n\nuse yii\\web\\Controller;\n\nclass PostController extends Controller\n{\n    // ...\n}\n```\n\nИзменить пространство имен классов контроллеров можно задав свойство [[yii\\base\\Module::controllerNamespace]]. Если\nкакие-либо контроллеры выпадают из этого пространства имен, доступ к ним можно осуществить, настроив свойство\n[[yii\\base\\Module::controllerMap]], аналогично тому, [как это делается в приложении](structure-applications.md#controller-map).\n\n\n### Представления в модулях <span id=\"views-in-modules\"></span>\n\nПредставления модуля также следует поместить в поддиректорию `views` [[yii\\base\\Module::basePath|базового пути]]\nмодуля. Виды, которые рендерит контроллер модуля, должны располагаться в директории `views/ControllerID`, где `ControllerID`\nсоответствует [идентификатору контроллера](structure-controllers.md#routes). Например, если контроллер реализуется\nклассом `PostController`, представления следует разместить в поддиректории `views/post`\n[[yii\\base\\Module::basePath|базового пути]] модуля.\n\nВ модуле можно задать [шаблон](structure-views.md#layouts), который будет использоваться для рендеринга всех представлений\nконтроллерами модуля. По умолчанию шаблон помещается в директорию `views/layouts`, а свойство [[yii\\base\\Module::layout]]\nдолжно указывать на имя этого шаблона. Если не задать свойство `layout`, модуль будет использовать шаблон, заданный\nв приложении.\n\n### Консольные команды в модулях <span id=\"console-commands-in-modules\"></span>\n\nВаш модуль также может объявлять команды, которые будут доступны через [консоль](tutorial-console.md).\n\nДля того, чтобы команда стала доступна, надо изменить свойство [[yii\\base\\Module::controllerNamespace]] для консольного\nрежима так, чтобы оно содержало пространство имён ваших команд.\n\nЭтого можно добиться проверяя класс экземпляра приложения Yii в методе `init` модуля:\n\n```php\npublic function init()\n{\n    parent::init();\n    if (Yii::$app instanceof \\yii\\console\\Application) {\n        $this->controllerNamespace = 'app\\modules\\forum\\commands';\n    }\n}\n```\n\nВаши команды будут доступны из командной строки как:\n\n```\nyii <module_id>/<command>/<sub_command>\n```\n\n\n\n## Использование модулей <span id=\"using-modules\"></span>\n\nЧтобы задействовать модуль в приложении, достаточно включить его в свойство [[yii\\base\\Application::modules|modules]]\nв конфигурации приложения. Следующий код в [конфигурации приложения](structure-applications.md#application-configurations)\nзадействует модуль `forum`:\n\n```php\n[\n    'modules' => [\n        'forum' => [\n            'class' => 'app\\modules\\forum\\Module',\n            // ... другие настройки модуля ...\n        ],\n    ],\n]\n```\n\n> Info: Для подключения консольных команд вашего модуля,\n> нужно также включить его в [конфигурации консольного приложения](tutorial-console.md#configuration)\n\nСвойству [[yii\\base\\Application::modules|modules]] присваивается массив, содержащий конфигурацию модуля. Каждый ключ массива\nпредставляет собой *идентификатор модуля*, который однозначно определяет модуль среди других модулей приложения,\nа соответствующий массив - это [конфигурация](concept-configurations.md) для создания модуля.\n\n\n### Маршруты <span id=\"routes\"></span>\n\nКак маршруты приложения используются для обращения к контроллерам приложения, [маршруты](structure-controllers.md#routes)\nмодуля используются, чтобы обращаться к контроллерам этого модуля. Маршрут контроллера в модуле должен начинаться с\nидентификатора модуля, за которым следуют [идентификатор контроллера](structure-controllers.md#controller-ids) и\n[идентификатор действия](structure-controllers.md#action-ids). Например, если в приложении задействован модуль `forum`,\nто маршрут `forum/post/index` соответствует действию `index` контроллера `post` этого модуля. Если маршрут состоит только\nиз идентификатора модуля, то контроллер и действие определяются исходя из свойства [[yii\\base\\Module::defaultRoute]],\nкоторое по умолчанию равно `default`. Таким образом, маршрут `forum` соответствует контроллеру `default` модуля `forum`.\n\nПравила в URL manager для модулей должны быть добавленны перед началом работы [[yii\\web\\UrlManager::parseRequest()]], что не \nпозволяет размещать код добавления правил модуля в `init()`, так как инициализация происходит уже после обработки маршрутов. Таким образом, добавление маршрутов необходимо осуществить в [предзагрузке\nмодуля](structure-extensions.md#bootstrapping-classes). Хорошей практикой, также, будет объединение всех правил модуля\nпри помощи [[\\yii\\web\\GroupUrlRule]].  \n\nЕсли же вы используете модуль для [версионирования API](rest-versioning.md), URL правила необходимо добавлять\nнепосредственно в конфигурации `urlManager` приложения.\n\n### Получение доступа к модулям <span id=\"accessing-modules\"></span>\n\nЗачастую внутри модуля может потребоваться доступ к экземпляру [класса модуля](#module-classes), через который получаются\nидентификатор модуля, его параметры, компоненты, и т. п. Это можно сделать с помощью следующей конструкции:\n\n```php\n$module = MyModuleClass::getInstance();\n```\n\nгде `MyModuleClass` соответствует имени класса модуля, доступ к которому нужно получить. Метод `getInstance()` возвращает\nзапрошенный в данный момент экземпляр класса модуля. Если модуль не запрошен, метод вернет `null`. Учтите, что обычно\nэкземпляры класса модуля вручную не создаются, так как созданный вручную экземпляр будет отличаться от экземпляра,\nсозданного Yii в качестве ответа на запрос.\n\n> Info: При разработке модуля нельзя исходить из предположения, что модулю будет назначен конкретный идентификатор.\nЭто связано с тем, что идентификатор, назначаемый модулю при использовании в приложении или в другом модуле, может быть\nвыбран совершенно произвольно. Чтобы получить идентификатор модуля, нужно вначале выбрать экземпляр модуля, как это\nописано выше, а затем получить доступ к идентификатору через свойство `$module->id`.\n\nДоступ к экземпляру модуля можно получить следующими способами:\n\n```php\n// получение дочернего модуля с идентификатором \"forum\"\n$module = \\Yii::$app->getModule('forum');\n\n// получение модуля, к которому принадлежит запрошенный в настоящее время контроллер\n$module = \\Yii::$app->controller->module;\n```\n\nПервый подход годится только если известен идентификатор модуля, а второй подход наиболее полезен, если известно,\nкакой контроллер запрошен.\n\nИмея экземпляр модуля можно получить доступ к параметрам и компонентам, зарегистрированным в модуле. Например,\n\n```php\n$maxPostCount = $module->params['maxPostCount'];\n```\n\n\n### Предзагрузка модулей <span id=\"bootstrapping-modules\"></span>\n\nМожет потребоваться запускать некоторые модули при каждом запросе. Модуль [[yii\\debug\\Module|debug]] - один из таких\nмодулей. Для этого список идентификаторов таких модулей необходимо указать в свойстве\n[[yii\\base\\Application::bootstrap|bootstrap]] приложения.\n\nНапример, следующая конфигурация приложения обеспечивает загрузку модуля `debug` при каждом запросе:\n\n```php\n[\n    'bootstrap' => [\n        'debug',\n    ],\n\n    'modules' => [\n        'debug' => 'yii\\debug\\Module',\n    ],\n]\n```\n\n\n## Вложенные модули <span id=\"nested-modules\"></span>\n\nМодули могут вкладываться друг в друга без ограничений по глубине. Иными словами, в модуле содержится модуль, в который\nвходит еще один модуль, и т. д. Первый модуль называется *родительским*, остальные - *дочерними*. Дочерние модули\nобъявляются в свойстве [[yii\\base\\Module::modules|modules]] родительских модулей. Например,\n\n```php\nnamespace app\\modules\\forum;\n\nclass Module extends \\yii\\base\\Module\n{\n    public function init()\n    {\n        parent::init();\n\n        $this->modules = [\n            'admin' => [\n                // здесь имеет смысл использовать более лаконичное пространство имен\n                'class' => 'app\\modules\\forum\\modules\\admin\\Module',\n            ],\n        ];\n    }\n}\n```\n\nМаршрут к контроллеру вложенного модуля должен содержать идентификаторы всех его предков. Например, маршрут\n`forum/admin/dashboard/index` соответствует действию `index` контроллера `dashboard` модуля `admin`, который в свою\nочередь является дочерним модулем модуля `forum`.\n\n> Info: Метод [[yii\\base\\Module::getModule()|getModule()]] возвращает только те дочерние модули, которые\nпринадлежат родительскому модулю непосредственно. В свойстве [[yii\\base\\Application::loadedModules]] содержится\nсписок загруженных модулей, в том числе прямых и косвенных потомков, с индексированием по имени класса.\n\n\n## Лучшие практики<span id=\"best-practices\"></span>\n\nМодули лучше всего подходят для крупных приложений, функционал которых можно разделить на несколько групп, в каждой из\nкоторых функции тесно связаны между собой. Каждая группа функций может разрабатываться в виде модуля, над которым работает\nодин разработчик или одна команда.\n\nМодули - это хороший способ повторно использовать код на уровне групп функций. В виде модулей можно реализовать такую\nфункциональность, как управление пользователями или управление комментариями, а затем использовать эти модули в будущих\nразработках.\n"
  },
  {
    "path": "docs/guide-ru/structure-overview.md",
    "content": "Обзор\n=====\n\nYii приложения организованы согласно шаблону проектирования [модель-представление-контроллер (MVC)](https://ru.wikipedia.org/wiki/Model-View-Controller).\n[Модели](structure-models.md) представляют собой данные, бизнес логику и бизнес правила; [представления](structure-views.md)\nотвечают за отображение информации, в том числе и на основе данных, полученных из моделей; [контроллеры](structure-controllers.md) \nпринимают входные данные от пользователя и преобразовывают их в понятный для [моделей](structure-models.md) формат и команды, а также отвечают за отображение \nнужного представления.\n\nКроме MVC, Yii приложения также имеют следующие сущности:\n\n* [входные скрипты](structure-entry-scripts.md): это PHP скрипты, которые доступны напрямую конечному пользователю приложения.\n  Они ответственны за запуск и обработку входящего запроса;\n* [приложения](structure-applications.md): это глобально доступные объекты, которые осуществляют корректную работу различных \n  компонентов приложения и их координацию для обработки запроса;\n* [компоненты приложения](structure-application-components.md): это объекты, зарегистрированные в приложении и предоставляющие\n  различные возможности для обработки текущего запроса;\n* [модули](structure-modules.md): это самодостаточные пакеты, которые включают в себя полностью все средства для MVC.\n  Приложение может быть организованно с помощью нескольких модулей;\n* [фильтры](structure-filters.md): это код, который должен быть выполнен до и после обработки запроса контроллерами;\n* [виджеты](structure-widgets.md): это объекты, которые могут включать в себя [представления](structure-views.md).\n  Они могут содержать различную логику и быть использованы в различных представлениях.\n\nНиже на диаграмме представлена структурная схема приложения:\n\n![Static Structure of Application](images/application-structure.png)\n"
  },
  {
    "path": "docs/guide-ru/structure-views.md",
    "content": "Виды\n====\n\nВиды - это часть [MVC](https://ru.wikipedia.org/wiki/Model-View-Controller) архитектуры, это код, который отвечает за представление данных \nконечным пользователям. В веб приложениях виды создаются обычно в виде *видов - шаблонов*, которые суть PHP скрипты, в основном содержащие HTML код \nи код PHP, отвечающий за представление и внешний вид. Виды управляются компонентом приложения [[yii\\web\\View|view]], который содержит часто используемые \nметоды для упорядочивания видов и их рендеринга. Для упрощения, мы будем называть виды - шаблоны просто видами.\n\n## Создание видов <span id=\"creating-views\"></span>\n\nКак мы упоминали ранее, вид - это просто PHP скрипт, состоящий из  PHP и HTML кодa. В примере ниже - вид, который представляет форму авторизации. \nКак видите, PHP код здесь генерирует динамический контент, как, например, заголовок страницы и саму форму, тогда как HTML организует полученные данные в готовую html страницу.\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n/**\n * @var \\yii\\web\\View $this\n * @var \\yii\\widgets\\ActiveForm $form\n * @var \\app\\models\\LoginForm $model\n */\n\n$this->title = 'Вход';\n?>\n<h1><?= Html::encode($this->title) ?></h1>\n\n<p>Пожалуйста, заполните следующие поля для входа на сайт:</p>\n\n<?php $form = ActiveForm::begin(); ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n    <?= Html::submitButton('Login') ?>\n<?php ActiveForm::end(); ?>\n```\n\nВнутри вида, вы можете использовать `$this`, которое представляет собой [[yii\\web\\View|компонент вид]], управляющий этим шаблоном и обеспечивающий \nего рендеринг.\n\nКроме `$this`, в виде могут быть доступны другие переменные, такие как `$form` и `$model` из примера выше. Эти переменные представляют собой данные, которые передаются в вид [контроллерами](structure-controllers.md) или другими объектами, которые вызывают [рендеринг вида](#rendering-views).\n\n> Совет: Переданные переменные могут быть перечислены в блоке комментария в начале скрипта, чтобы их смогли распознать IDE. К тому же, это хороший способ добавления документации в вид.  \n\n\n### Безопасность <span id=\"security\"></span>\n\nПри создании видов, которые генерируют HTML страницы, важно кодировать и/или фильтровать данные, которые приходят от пользователей перед тем как их показывать. В противном случае ваше приложение может стать жертвой атаки типа [межсайтовый скриптинг](https://ru.wikipedia.org/wiki/%D0%9C%D0%B5%D0%B6%D1%81%D0%B0%D0%B9%D1%82%D0%BE%D0%B2%D1%8B%D0%B9_%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D0%B8%D0%BD%D0%B3)\n\nЧтобы показать обычный текст, сначала кодируйте его с помощью [[yii\\helpers\\Html::encode()]]. В примере ниже имя пользователя кодируется перед выводом:\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n\n<div class=\"username\">\n    <?= Html::encode($user->name) ?>\n</div>\n```\n\nЧтобы показать HTML содержимое, используйте [[yii\\helpers\\HtmlPurifier]] для того, чтобы отфильтровать потенциально опасное содержимое. В примере ниже содержимое поста фильтруется перед показом:\n\n```php\n<?php\nuse yii\\helpers\\HtmlPurifier;\n?>\n\n<div class=\"post\">\n    <?= HtmlPurifier::process($post->text) ?>\n</div>\n```\n\n> Tip: Несмотря на то, что HTMLPurifier отлично справляется с тем, чтобы сделать вывод безопасным, работает он довольно медленно. Если от приложения требуется высокая производительность, рассмотрите возможность [кэширования](caching-overview.md) отфильтрованного результата \n\n\n### Организация видов <span id=\"organizing-views\"></span>\n\nКак и для [контроллеров](structure-controllers.md), и [моделей](structure-models.md), для видов тоже есть определенные соглашения в их организации.\n\n* Виды, которые рендерятся из контроллера, по умолчанию должны располагаться в папке `@app/views/ControllerID`, где `ControllerID` это [ID контроллера](structure-controllers.md#routes) . Например, если класс контроллера - `PostController`, то папка будет `@app/views/post`; если контроллер - `PostCommentController`, то папка будет `@app/views/post-comment`. В случае, если контроллер принадлежит модулю, папка будет `views/ControllerID` в [[yii\\base\\Module::basePath|подпапке модуля]].\n* Виды, которые рендерятся из виджетов, должны располагаться в `ПутьВиджета/views`, где `ПутьВиджета` - это папка, которая содержит класс виджета.\n* С видами, которые рендерятся из других объектов рекомендуется поступать по той же схеме, что и с видами виджетов.\n\nВ контроллерах и виджетах вы можете изменить папки видов по умолчанию, переопределив метод [[yii\\base\\ViewContextInterface::getViewPath()]].\n\n\n## Рендеринг видов <span id=\"rendering-views\"></span>\n\nВы можете рендерить виды в [контроллерах](structure-controllers.md), [widgets](structure-widgets.md), или из любого другого места, вызывая методы рендеринга видов. Методы вызываются приблизительно так, как это показано в примере ниже,\n\n```\n/**\n * @param string $view название вида или путь файла, в зависимости от того, какой метод рендеринга используется \n * @param array $params данные, которые передаются виду\n * @return string результат рендеринга\n */\nmethodName($view, $params = [])\n```\n\n\n### Рендеринг в контроллерах <span id=\"rendering-in-controllers\"></span>\n\nВнутри [контроллеров](structure-controllers.md) можно вызывать следующие методы рендеринга видов:\n\n* [[yii\\base\\Controller::render()|render()]]: рендерит [именованный вид](#named-views) и применяет [шаблон](#layouts)\n  к результату рендеринга.\n* [[yii\\base\\Controller::renderPartial()|renderPartial()]]: рендерит [именованный вид](#named-views) без шаблона.\n* [[yii\\web\\Controller::renderAjax()|renderAjax()]]: рендерит [именованный вид](#named-views) без шаблона,\n  и добавляет все зарегистрированные JS/CSS скрипты и стили. Обычно этот метод применяется для рендеринга результата AJAX запроса.\n* [[yii\\base\\Controller::renderFile()|renderFile()]]: рендерит вид, заданный как путь к файлу или \n  [алиас](concept-aliases.md).\n\nНапример,\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse app\\models\\Post;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\n\nclass PostController extends Controller\n{\n    public function actionView($id)\n    {\n        $model = Post::findOne($id);\n        if ($model === null) {\n            throw new NotFoundHttpException;\n        }\n\n        // рендерит вид с названием `view` и применяет к нему шаблон\n        return $this->render('view', [\n            'model' => $model,\n        ]);\n    }\n}\n```\n\n\n### Рендеринг в виджетах <span id=\"rendering-in-widgets\"></span>\nВнутри [виджетов](structure-widgets.md), вы можете вызывать следующие методы для рендеринга видов.\n\n* [[yii\\base\\Widget::render()|render()]]: рендерит [именованный вид](#named-views).\n* [[yii\\base\\Widget::renderFile()|renderFile()]]: рендерит вид, заданный как путь файла или \n  [алиас](concept-aliases.md).\n\nНапример,\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Widget;\nuse yii\\helpers\\Html;\n\nclass ListWidget extends Widget\n{\n    public $items = [];\n\n    public function run()\n    {\n        // рендерит вид с названием `list`\n        return $this->render('list', [\n            'items' => $this->items,\n        ]);\n    }\n}\n```\n\n\n### Рендеринг в видах <span id=\"rendering-in-views\"></span>\n\nВы можете рендерить вид внутри другого вида используя методы, которые предоставляет [[yii\\base\\View|компонент вида]]:\n\n* [[yii\\base\\View::render()|render()]]: рендерит [именованный вид](#named-views).\n* [[yii\\web\\View::renderAjax()|renderAjax()]]: рендерит [именованный вид](#named-views) и добавляет зарегистрированные JS/CSS скрипты и стили. Обычно используется для рендеринга результата AJAX запроса.\n* [[yii\\base\\View::renderFile()|renderFile()]]: рендерит вид, заданный как путь к файлу или \n  [алиас](concept-aliases.md).\n\nНапример, следующий код рендерит `_overview.php` файл вида, который находится в той же папке что и вид, который рендерится в текущий момент. Помните, что  `$this` в виде - это [[yii\\base\\View|компонент вида]] (а не контроллер, как это было в Yii1):\n\n```php\n<?= $this->render('_overview') ?>\n```\n\n\n### Рендеринг в других местах <span id=\"rendering-in-other-places\"></span>\n\nВы может получить доступ к  [[yii\\base\\View|виду]] как компоненту приложения вот так: \n`Yii::$app->view`,  а затем вызвать вышеупомянутые методы, чтобы отрендерить вид. Например,\n\n```php\n// показывает файл \"@app/views/site/license.php\"\necho \\Yii::$app->view->renderFile('@app/views/site/license.php');\n```\n\n\n### Именованные виды <span id=\"named-views\"></span>\n\nПри рендеринге вида, вы можете указать нужный вид, используя как имя вида, так и путь к файлу/алиас. В большинстве случаев вы будете использовать первый вариант, т.к. он более нагляден и гибок. Мы называем виды, которые были вызваны с помощью сокращенного имени *именованные виды*.\n\nИмя вида преобразуется в соответствующий ему путь файла в соответствии со следующими правилами:\n\n* Имя вида можно указывать без расширения. В таком случае в качестве расширения будет использоваться `.php`. К примеру, имя вида `about` соответствует файлу `about.php`.\n* Если имя вида начинается с двойного слеша `//`, соответствующий ему путь будет `@app/views/ViewName`.\n Т.е. вид будет искаться в [[yii\\base\\Application::viewPath|папке видов приложения по умолчанию]]. Например, `//site/about` будет преобразован в `@app/views/site/about.php`.\n* Если имя вида начинается с одинарного слеша `/`, то вид будет искаться в [[yii\\base\\Module::viewPath|папке видов по умолчанию]] текущего [модуля](structure-modules.md) . Если активного модуля на данный момент нет, будет использована папка видов приложения по умолчанию, т.е. вид будет искаться в `@app/views`, как в одном из примеров выше.\n* Если вид рендеринтся с помощью [[yii\\base\\View::context|контекста]] и контекст реализует интерфейс [[yii\\base\\ViewContextInterface]],  путь к виду образуется путем присоединения [[yii\\base\\ViewContextInterface::getViewPath()|пути видов]] контекста к имени вида. В основном это применимо к видам, которые рендерятся из контроллеров и виджетов. Например,\n  `about` будет преобразован в `@app/views/site/about.php` если контекстом является контроллер `SiteController`.\n* Если вид рендерится из другого вида, папка, в которой находится текущий вид будет добавлена к пути вложенного вида. Например, `item` будет преобразован в `@app/views/post/item`\n  если он рендерится из вида `@app/views/post/index.php`.\n\nВ соответствии с вышесказанным, вызов `$this->render('view')` в контроллере `app\\controllers\\PostController` будет рендерить файл `@app/views/post/view.php`, а вызов `$this->render('_overview')` в этом виде будет рендерить файл `@app/views/post/_overview.php`.\n\n\n### Доступ к данным из видов <span id=\"accessing-data-in-views\"></span>\n\nДанные можно передавать в вид явно или подгружать их динамически, обращаясь к контексту из вида.\n\nПередавая данные через второй параметр методов рендеринга вида, вы явно передаете данные в вид. \nДанные должны быть представлены как обычный массив: ключ-значение. При рендеринге вида, php вызывает встроенную функцию PHP `extract()` на переданном массиве, чтобы переменные из массива \"распаковались\" в переменные вида. Например, следующий код в контроллере передаст две переменные виду `report` :\n `$foo = 1` и `$bar = 2`.\n\n```php\necho $this->render('report', [\n    'foo' => 1,\n    'bar' => 2,\n]);\n```\n\nДругой подход, подход контекстного доступа, извлекает данные из [[yii\\base\\View|компонента вида]] или других объектов, доступных в виде (например через глобальный контейнер `Yii::$app`). Внутри вида вы можете вызывать объект контроллера таким образом: `$this->context` (см пример снизу), и, таким образом, получить доступ к его свойствам и методам, например, как указано в примере, вы можете получить ID контроллера:\n\n```php\nID контроллера: <?= $this->context->id ?>\n```\n\nЯвная передача данных в вид обычно более предпочтительна, т.к. она делает виды независимыми от контекста. Однако, у нее есть недостаток - необходимость каждый раз вручную строить массив данных, что может быть довольно утомительно и привести к ошибкам, если вид рендерится в разных местах.\n\n\n### Передача данных между видами <span id=\"sharing-data-among-views\"></span>\n\n[[yii\\base\\View|Компонент вида]] имеет свойство [[yii\\base\\View::params|params]], которое вы можете использовать для обмена данными между видами.\n\nНапример, в виде `about` вы можете указать текущий сегмент хлебных крошек с помощью следующего кода.\n\n```php\n$this->params['breadcrumbs'][] = 'О нас';\n```\n\nЗатем, в [шаблоне](#layouts), который также является видом, вы можете отобразить хлебные крошки используя данные, переданные через [[yii\\base\\View::params|params]].\n\n```php\n<?= yii\\widgets\\Breadcrumbs::widget([\n    'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],\n]) ?>\n```\n\n\n## Шаблоны <span id=\"layouts\"></span>\n\nШаблоны - особый тип видов, которые представляют собой общие части разных видов. Например, у большинства страниц веб приложений одинаковые верх и низ (хедер и футер). Можно, конечно, указать их и в каждом виде, однако лучше сделать это один раз, в шаблоне, и затем, при рендеринге, включать уже отрендеренный вид в заданное место шаблона.\n\n### Создание шаблонов <span id=\"creating-layouts\"></span>\n\nПоскольку шаблоны это виды, их можно создавать точно так же, как и обычные виды. По умолчанию шаблоны хранятся в папке `@app/views/layouts`. Шаблоны, которые используются в конкретном [модуле](structure-modules.md), хранятся в подпапке `views/layouts` [[yii\\base\\Module::basePath|папки модуля]]. Вы можете изменить папку шаблонов по умолчанию, используя свойство [[yii\\base\\Module::layoutPath]] приложения или модулей.\n\nПример ниже показывает как выглядит шаблон. Для лучшего понимания мы сильно упростили код шаблона. На практике, однако, в нем часто содержится больше кода, например, тэги `<head>`, главное меню и т.д. \n\n```php\n<?php\nuse yii\\helpers\\Html;\n\n/**\n * @var \\yii\\web\\View $this\n * @var string $content\n */\n?>\n<?php $this->beginPage() ?>\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\"/>\n    <?= Html::csrfMetaTags() ?>\n    <title><?= Html::encode($this->title) ?></title>\n    <?php $this->head() ?>\n</head>\n<body>\n<?php $this->beginBody() ?>\n    <header>Моя компания</header>\n    <?= $content ?>\n    <footer>Моя компания &copy; 2014</footer>\n<?php $this->endBody() ?>\n</body>\n</html>\n<?php $this->endPage() ?>\n```\n\nКак видите, шаблон генерирует HTML тэги, которые присутствуют на всех страницах. Внутри секции `<body>`, шаблон выводит переменную `$content`, которая  содержит  результат рендеринга видов контента, который передается в шаблон, при работе метода  [[yii\\base\\Controller::render()]].\n\nБольшинство шаблонов вызывают методы, аналогично тому, как это сделано в примере выше, чтобы скрипты и тэги, зарегистрированные в других местах приложения могли быть правильно отображены в местах вызова (например, в шаблоне).\n\n- [[yii\\base\\View::beginPage()|beginPage()]]: Этот метод нужно вызывать в самом начале шаблона.\n  Он вызывает событие [[yii\\base\\View::EVENT_BEGIN_PAGE|EVENT_BEGIN_PAGE]], которое происходит при начале обработки страницы.\n- [[yii\\base\\View::endPage()|endPage()]]: Этот метод нужно вызывать в конце страницы.\n  Он вызывает событие [[yii\\base\\View::EVENT_END_PAGE|EVENT_END_PAGE]] . Оно указывает на обработку конца страницы.\n- [[yii\\web\\View::head()|head()]]: Этот метод нужно вызывать в `<head>` секции страницы html. \nОн генерирует метку, которая будет заменена зарегистрированным ранее кодом HTML (тэги `link`, мета тэги), когда рендеринг страницы будет завершен.\n- [[yii\\web\\View::beginBody()|beginBody()]]: Этот метод нужно вызывать в начале секции `<body>`.\n  Он вызывает событие [[yii\\web\\View::EVENT_BEGIN_BODY|EVENT_BEGIN_BODY]] и генерирует метку, которая будет заменена зарегистрированным HTML кодом (например, Javascript'ом), который нужно разместить в начале `<body>` страницы.\n- [[yii\\web\\View::endBody()|endBody()]]: Этот метод нужно вызывать в конце секции `<body>`.\n  Он вызывает событие [[yii\\web\\View::EVENT_END_BODY|EVENT_END_BODY]] и генерирует метку, которая будет заменена зарегистрированным HTML кодом (например, Javascript'ом), который нужно разместить в конце `<body>` страницы.\n\n\n### Доступ к данным в шаблонах <span id=\"accessing-data-in-layouts\"></span>\n\nВнутри шаблона, у вас есть доступ к двум предопределенным переменным: `$this` и `$content`. Первая представляет собой \n [[yii\\base\\View|вид]] компонент, как и в обычных видах, тогда как последняя содержит результат рендеринга вида, который рендерится при вызове метода [[yii\\base\\Controller::render()|render()]] в контроллерах.\n\nЕсли вы хотите получить доступ к другим данным из шаблона, используйте метод явной передачи (он описан в секции [Доступ к данным в видах](#accessing-data-in-views) настоящего документа). Если вы хотите \nпередать данные из вида шаблону, вы можете использовать метод, описанный в [передаче данных между видами](#sharing-data-among-views).\n\n\n### Использование шаблонов <span id=\"using-layouts\"></span>\n\nКак было описано в секции [Рендеринг в контроллерах](#rendering-in-controllers), когда вы рендерите вид, вызывая метод [[yii\\base\\Controller::render()|render()]] из контроллера, к результату рендеринга будет применен шаблон. По умолчанию будет использован шаблон `@app/views/layouts/main.php` .\n\nВы можете использовать разные шаблоны, конфигурируя [[yii\\base\\Application::layout]] или [[yii\\base\\Controller::layout]]. \nПервый переопределяет шаблон, который используется по умолчанию всеми контроллерами, а второй переопределяет шаблон в отдельном контроллере. \nНапример, код внизу показывает, как можно сделать так, чтобы контроллер использовал шаблон `@app/views/layouts/post.php` при рендеринге вида. Другие контроллеры, если их свойство `layout` не переопределено, все еще будут использовать `@app/views/layouts/main.php` как шаблон.\n \n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass PostController extends Controller\n{\n    public $layout = 'post';\n    \n    // ...\n}\n```\n\nДля контроллеров, принадлежащих модулю, вы также можете переопределять свойство модуля [[yii\\base\\Module::layout|layout]], чтобы \nиспользовать особый шаблон для этих контроллеров.\n\nПоскольку свойство `layout` может быть сконфигурировано на разных уровнях приложения (контроллеры, модули, само приложение), \nYii определяет какой шаблон использовать для контроллера в два этапа.\n\nНа первом этапе определяется значение шаблона и контекстный модуль.\n\n- Если [[yii\\base\\Controller::layout]] свойство контроллера отлично от `null`, используется оно, и [[yii\\base\\Controller::module|модуль]] \nконтроллера как контекстный модуль.\n- Если [[yii\\base\\Controller::layout|layout]] равно `null` (не задано), происходит поиск среди родительских модулей контроллера, включая само приложение (которое по умолчанию является родительским модулем для контроллеров, не принадлежащих модулям) и \nнаходится первый модуль, свойство [[yii\\base\\Module::layout|layout]] которого не равно `null` . Тогда используется найденное значение `layout` этого модуля \nи сам модуль в качестве контекста. Если такой модуль не найден, значит шаблон применен не будет. \n\nНа втором этапе определяется сам файл шаблона для рендеринга на основании значения `layout` и контекстного модуля. \nЗначением `layout` может быть:\n\n- Алиас пути (например, `@app/views/layouts/main`).\n- Абсолютный путь (например `/main`): значение `layout` начинается со слеша. Будет искаться шаблон из [[yii\\base\\Application::layoutPath|папки шаблонов]] приложения, по умолчанию это `@app/views/layouts`. \n- Относительный путь (например `main`): Будет искаться шаблон из [[yii\\base\\Module::layoutPath|папки шаблонов контекстного модуля]], по умолчанию это `views/layouts` в [[yii\\base\\Module::basePath|папке модуля]].\n- Булево значение `false`: шаблон не будет применен. \n\nЕсли у значения `layout` нет расширения, будет использовано расширение по умолчанию - `.php`.\n\n### Вложенные шаблоны <span id=\"nested-layouts\"></span>\n\nИногда нужно вложить один шаблон в другой. Например, в разных разделах сайта используются разные шаблоны, но у всех \nэтих шаблонов есть основная разметка, которая определяет HTML5 структуру страницы. Вы можете использовать вложенные шаблоны, \nвызывая [[yii\\base\\View::beginContent()|beginContent()]] и [[yii\\base\\View::endContent()|endContent()]] в дочерних \nшаблонах таким образом:\n\n```php\n<?php $this->beginContent('@app/views/layouts/base.php'); ?>\n\n...код дочернего шаблона...\n\n<?php $this->endContent(); ?>\n```\n\nВ коде выше дочерний шаблон заключается в [[yii\\base\\View::beginContent()|beginContent()]] и [[yii\\base\\View::endContent()|endContent()]].\nПараметр, передаваемый в метод [[yii\\base\\View::beginContent()|beginContent()]] определяет родительский шаблон. Это может быть как \nпуть к файлу, так и алиас.\n\nИспользуя подход выше, вы можете вкладывать шаблоны друг в друга в несколько уровней.\n\n\n### Использование блоков <span id=\"using-blocks\"></span>\n\nБлоки позволяют \"записывать\" контент в одном месте, а показывать в другом. Они часто используются совместно с шаблонами. \nНапример, вы определяете (записываете) блок в виде и отображаете его в шаблоне.\n\nДля определения блока вызываются методы [[yii\\base\\View::beginBlock()|beginBlock()]] и [[yii\\base\\View::endBlock()|endBlock()]]. \nПосле определения, блок доступен через `$view->blocks[$blockID]`, где `$blockID` - это уникальный ID, который вы присваиваете блоку\nв начале определения. \n\nВ примере ниже показано, как можно использовать блоки, определенные в виде, чтобы динамически изменять фрагменты шаблона.\n\nСначала, в виде, вы записываете один или несколько блоков:\n\n```php\n...\n\n<?php $this->beginBlock('block1'); ?>\n\n...содержимое блока 1...\n\n<?php $this->endBlock(); ?>\n\n...\n\n<?php $this->beginBlock('block3'); ?>\n\n...содержимое блока 3...\n\n<?php $this->endBlock(); ?>\n```\n\nЗатем, в шаблоне, рендерите блоки если они есть, или показываете контент по умолчанию, если блок не определен.\n\n```php\n...\n<?php if (isset($this->blocks['block1'])): ?>\n    <?= $this->blocks['block1'] ?>\n<?php else: ?>\n    ... контент по умолчанию для блока 1 ...\n<?php endif; ?>\n\n...\n\n<?php if (isset($this->blocks['block2'])): ?>\n    <?= $this->blocks['block2'] ?>\n<?php else: ?>\n    ... контент по умолчанию для блока 2 ...\n<?php endif; ?>\n\n...\n\n<?php if (isset($this->blocks['block3'])): ?>\n    <?= $this->blocks['block3'] ?>\n<?php else: ?>\n    ... контент по умолчанию для блока 3 ...\n<?php endif; ?>\n...\n```\n\n\n## Использование компонентов вида <span id=\"using-view-components\"></span>\n\n[[yii\\base\\View|Компоненты вида]] дают много возможностей. Несмотря на то, что существует возможность создавать индивидуальные экземпляры [[yii\\base\\View]] или дочерних классов, в большинстве случаев используется \nсам компонент `view` приложения. Вы можете сконфигурировать компонент в [конфигурации приложения](structure-applications.md#application-configurations) таким образом:\n\n```php\n[\n    // ...\n    'components' => [\n        'view' => [\n            'class' => 'app\\components\\View',\n        ],\n        // ...\n    ],\n]\n```\n\nКомпоненты вида предоставляют широкие возможности по работе с видами, они описаны в отдельных секциях документации: \n\n* [темы](output-theming.md): позволяет менять темы оформления для сайта.\n* [кэширование фрагментов](caching-fragment.md): позволяет кэшировать фрагменты веб-страниц.\n* [работа с клиентскими скриптами](output-client-scripts.md): Поддерживает регистрацию и рендеринг CSS и Javascript.\n* [управление связками](structure-assets.md): позволяет регистрацию и управление [связками клиентского кода](structure-assets.md).\n* [альтернативные движки шаблонов](tutorial-template-engines.md): позволяет использовать другие шаблонные движки, такие как \n  [Twig](https://twig.symfony.com/), [Smarty](https://www.smarty.net/).\n\n\nТакже удобно пользоваться мелкими, но удобными фичами при разработке веб страниц, которые приведены ниже.\n\n\n### Установка заголовков страниц <span id=\"setting-page-titles\"></span>\n\nУ каждой страницы должен быть заголовок. Обычно заголовок выводится в [шаблоне](#layouts). Однако на практике \nзаголовок часто определяется в видах, а не в шаблонах. Чтобы передать заголовок из вида в шаблон, используется свойство [[yii\\web\\View::title|title]]. \n\nВ виде можно задать заголовок таким образом:\n\n```php\n<?php\n$this->title = 'Мой заголовок страницы';\n?>\n```\n\nВ шаблоне заголовок выводится следующим образом, (убедитесь, что в `<head>` у вас соответствующий код):\n\n```php\n<title><?= Html::encode($this->title) ?></title>\n```\n\n\n### Регистрация мета-тэгов <span id=\"registering-meta-tags\"></span>\n\nНа веб страницах обычно есть мета-тэги, которые часто используются различными сервисами. Как и заголовки страниц, \nмета-тэги выводятся в `<head>` и обычно генерируются в шаблонах.\n\nЕсли вы хотите указать, какие мета-тэги генерировать в видах, вы можете вызвать метод [[yii\\web\\View::registerMetaTag()]] в виде так, \nкак в примере ниже: \n\n```php\n<?php\n$this->registerMetaTag(['name' => 'keywords', 'content' => 'yii, framework, php']);\n?>\n```\n\nЭтот код зарегистрирует мета тэг \"keywords\" в виде. Зарегистрированные мета тэги рендерятся после того, как закончен \nрендеринг шаблона. Они вставляются в то место, где в шаблоне вызван метод [[yii\\web\\View::head()]]. Результатом рендеринга \nмета тэгов является следующий код:\n\n```php\n<meta name=\"keywords\" content=\"yii, framework, php\">\n```\n\nОбратите внимание, что при вызове метода [[yii\\web\\View::registerMetaTag()]] несколько раз мета тэги будут регистрироваться \nкаждый раз без проверки на уникальность.\n\nЧтобы убедиться, что зарегистрирован только один экземпляр одного типа мета тэгов, вы можете указать ключ мета тэга в качестве второго \nпараметра при вызове метода.\nК примеру, следующий код регистрирует два мета тэга \"description\", однако отрендерен будет только второй.\n\n```php\n$this->registerMetaTag(['name' => 'description', 'content' => 'Мой сайт сделан с помощью Yii!'], 'description');\n$this->registerMetaTag(['name' => 'description', 'content' => 'Это сайт о забавных енотах.'], 'description');\n```\n\n\n### Регистрация тэгов link <span id=\"registering-link-tags\"></span>\n\nКак и [мета тэги](#adding-meta-tags), link тэги полезны во многих случаях, как, например, задание уникальной favicon, указание на RSS фид или указание OpenID сервера для авторизации. С link тэгами можно работать аналогично работе с мета тэгами, вызывая метод [[yii\\web\\View::registerLinkTag()]]. Например, \nвы можете зарегистрировать link тэг в виде таким образом:\n\n```php\n$this->registerLinkTag([\n    'title' => 'Сводка новостей по Yii',\n    'rel' => 'alternate',\n    'type' => 'application/rss+xml',\n    'href' => 'https://www.yiiframework.com/rss.xml/',\n]);\n```\n\nЭтот код выведет\n\n```html\n<link title=\"Сводка новостей по Yii\" rel=\"alternate\" type=\"application/rss+xml\" href=\"https://www.yiiframework.com/rss.xml/\">\n```\n\nКак и в случае с [[yii\\web\\View::registerMetaTag()|registerMetaTag()]], вы можете указать ключ вторым параметром при вызове \n[[yii\\web\\View::registerLinkTag()|registerLinkTag()]] чтобы избежать дублирования link тэгов одного типа.\n\n\n## События в видах <span id=\"view-events\"></span>\n\n[[yii\\base\\View|Компонент вида]] вызывает несколько событий во время рендеринга. \nВы можете задавать обработчики для этих событий чтобы добавлять контент \nв вид или делать пост-обработку результатов рендеринга до того, как они будут отправлены конечным пользователям.\n\n- [[yii\\base\\View::EVENT_BEFORE_RENDER|EVENT_BEFORE_RENDER]]: вызывается в начале рендеринга файла в контроллере. \nОбработчики этого события могут придать атрибуту [[yii\\base\\ViewEvent::isValid]] значение `false`, чтобы отменить процесс рендеринга.\n- [[yii\\base\\View::EVENT_AFTER_RENDER|EVENT_AFTER_RENDER]]: событие инициируется после рендеринга файла вызовом [[yii\\base\\View::afterRender()]].\n  Обработчики события могут получать результат рендеринга через [[yii\\base\\ViewEvent::output]] и могут изменять это свойство для изменения \nрезультата рендеринга.\n- [[yii\\base\\View::EVENT_BEGIN_PAGE|EVENT_BEGIN_PAGE]]: инициируется вызовом [[yii\\base\\View::beginPage()]] в шаблонах.\n- [[yii\\base\\View::EVENT_END_PAGE|EVENT_END_PAGE]]: инициируется вызовом [[yii\\base\\View::endPage()]] в шаблонах.\n- [[yii\\web\\View::EVENT_BEGIN_BODY|EVENT_BEGIN_BODY]]: инициируется вызовом [[yii\\web\\View::beginBody()]] в шаблонах.\n- [[yii\\web\\View::EVENT_END_BODY|EVENT_END_BODY]]: инициируется вызовом [[yii\\web\\View::endBody()]] в шаблонах.\n\nНапример, следующий код вставляет дату в конец `body` страницы:\n\n```php\n\\Yii::$app->view->on(View::EVENT_END_BODY, function () {\n    echo date('Y-m-d');\n});\n```\n\n\n## Рендеринг статических страниц <span id=\"rendering-static-pages\"></span>\n\nСтатическими страницами мы считаем страницы, которые содержат в основном статические данные и для формирования \nкоторых не нужно строить динамические данные в контроллерах.\n\nВы можете выводить статические страницы, сохраняя их в видах, а затем используя подобный код в контроллере:\n\n```php\npublic function actionAbout()\n{\n    return $this->render('about');\n}\n```\n\nЕсли сайт содержит много статических страниц, описанный выше подход не вполне подходит - его использование \nприведет к многократному повторению похожего кода. Вместо этого вы можете использовать [отдельное действие](structure-controllers.md#standalone-actions)  [[yii\\web\\ViewAction]] в контроллере. Например,\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actions()\n    {\n        return [\n            'page' => [\n                'class' => 'yii\\web\\ViewAction',\n            ],\n        ];\n    }\n}\n```\n\nТеперь, если вы создадите вид `about` в папке `@app/views/site/pages`, он будет отображаться по такому адресу: \n\n```\nhttp://localhost/index.php?r=site%2Fpage&view=about\n```\n\n`GET` параметр `view` сообщает [[yii\\web\\ViewAction]] какой вид затребован. Действие будет искать этот вид в папке `@app/views/site/pages`. \nВы можете сконфирурировать параметр [[yii\\web\\ViewAction::viewPrefix]] чтобы изменить папку в которой ищется вид.\n\n\n## Полезные советы <span id=\"best-practices\"></span>\n\nВиды отвечают за представление данных моделей в формате, понятным конечным пользователям. В целом, виды\n\n* должны в основном содержать код, отвечающий за представление, такой как HTML и простой PHP для обхода, форматирования и рендеринга данных.\n* не должны содержать кода, который производит запросы к БД. Такими запросами должны заниматься модели.\n* должны избегать прямого обращения к данным запроса, таким как `$_GET`, `$_POST`. Разбором запроса должны заниматься контроллеры. Если \nданные запросов нужны для построения вида, они должны явно передаваться в вид контроллерами.\n* могут читать свойства моделей, но не должны их изменять.\n\nЧтобы сделать виды более управляемыми, избегайте создания видов, которые содержат слишком сложную логику или большое количество кода. \nИспользуйте следующие подходы для их упрощения:\n\n\n* используйте [шаблоны](#layouts) для отображения основных секций разметки сайта (верхняя часть (хедер), нижняя часть (футер) и т.п.)\n* разбивайте сложный вид на несколько видов попроще. Меньшие виды можно рендерить и объединять в больший используя методы рендеринга, описанный в \nнастоящем документе.\n* создавайте и используйте [виджеты](structure-widgets.md) как строительный материал для видов.\n* создавайте и используйте классы-хелперы для изменения и форматирования данных в видах.\n"
  },
  {
    "path": "docs/guide-ru/structure-widgets.md",
    "content": "Виджеты\n=======\n\nВиджеты представляют собой многоразовые строительные блоки, используемые в [представлениях](structure-views.md)\nдля создания сложных и настраиваемых элементов пользовательского интерфейса в рамках объектно-ориентированного\nподхода. Например, виджет выбора даты (date picker) позволяет генерировать интерактивный интерфейс для выбора дат,\nпредоставляя пользователям приложения удобный способ для ввода данных такого типа. Все, что нужно для\nподключения виджета - это добавить следующий код в представление:\n\n```php\n<?php\nuse yii\\bootstrap\\DatePicker;\n?>\n<?= DatePicker::widget(['name' => 'date']) ?>\n```\n\nВ комплект Yii входит большое количество виджетов, например: [[yii\\widgets\\ActiveForm|active form]],\n[[yii\\widgets\\Menu|menu]], [виджеты jQuery UI](https://github.com/yiisoft/yii2-jui/blob/master/docs/guide/README.md), [виджеты Twitter Bootstrap](https://github.com/yiisoft/yii2-bootstrap/blob/master/docs/guide/usage-widgets.md).\nДалее будут представлены базовые сведения о виджетах. Для получения сведений относительно использования\nконкретного виджета, следует обратиться к документации соответствующего класса.\n\n\n## Использование Виджетов <span id=\"using-widgets\"></span>\n\nГлавным образом, виджеты применяют в [представлениях](structure-views.md). Для того, чтобы использовать виджет\nв представлении, достаточно вызвать метод [[yii\\base\\Widget::widget()]]. Метод принимает массив [настроек](concept-configurations.md)\nдля инициализации виджета и возвращает результат его рендеринга. Например, следующий\nкод добавляет виджет для выбора даты, сконфигурированный для использования русского в качестве языка интерфейса\nвиджета и хранения вводимых данных в атрибуте `from_date` модели `$model`.\n\n```php\n<?php\nuse yii\\bootstrap\\DatePicker;\n?>\n<?= DatePicker::widget([\n    'model' => $model,\n    'attribute' => 'from_date',\n    'language' => 'ru',\n    'clientOptions' => [\n        'dateFormat' => 'yy-mm-dd',\n    ],\n]) ?>\n```\n\nНекоторые виджеты могут иметь внутреннее содержимое, которое следует располагать между вызовами методов\n[[yii\\base\\Widget::begin()]] и [[yii\\base\\Widget::end()]]. Например, для генерации формы входа, в следующем\nфрагменте кода используется виджет [[yii\\widgets\\ActiveForm]]. Этот виджет сгенерирует открывающий и закрывающий\nтэги `<form>` в местах вызова методов `begin()` и `end()` соответственно. При этом, содержимое, расположенное\nмежду вызовами указанных методов будет выведено без каких-либо изменений.\n\n```php\n<?php\nuse yii\\widgets\\ActiveForm;\nuse yii\\helpers\\Html;\n?>\n\n<?php $form = ActiveForm::begin(['id' => 'login-form']); ?>\n\n    <?= $form->field($model, 'username') ?>\n\n    <?= $form->field($model, 'password')->passwordInput() ?>\n\n    <div class=\"form-group\">\n        <?= Html::submitButton('Login') ?>\n    </div>\n\n<?php ActiveForm::end(); ?>\n```\n\nОбратите внимание на то, что в отличие от метода [[yii\\base\\Widget::widget()]], который возвращает результат\nрендеринга, метод [[yii\\base\\Widget::begin()]] возвращает экземпляр виджета, который может быть\nиспользован в дальнейшем для формирования его внутреннего содержимого.\n\n### Задание глобальных умолчаний\n\nГлобальные умолчания для определённого типа виджета могут быть заданы через DI контейнер:\n\n```php\n\\Yii::$container->set('yii\\widgets\\LinkPager', ['maxButtonCount' => 5]);\n```\n\nПодробнее это описано в [подразделе «Практическое использование» раздела «Контейнер внедрения зависимостей»](concept-di-container.md#practical-usage).\n\n## Создание Виджетов <span id=\"creating-widgets\"></span>\n\nДля того, чтобы создать виджет, следует унаследовать класс [[yii\\base\\Widget]] и переопределить методы\n[[yii\\base\\Widget::init()]] и/или [[yii\\base\\Widget::run()]]. Как правило, метод `init()` должен содержать\nкод, выполняющий нормализацию свойств виджета, а метод `run()` - код, возвращающий результат рендеринга виджета.\nРезультат рендеринга может быть выведен непосредственно с помощью конструкции \"echo\" или же возвращен\nв строке методом `run()`.\n\nВ следующем примере, виджет `HelloWidget` HTML-кодирует и отображает содержимое, присвоенное свойству `message`.\nВ случае, если указанное свойство не установлено, виджет, в качестве значения по умолчанию отобразит строку \"Hello World\".\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Widget;\nuse yii\\helpers\\Html;\n\nclass HelloWidget extends Widget\n{\n    public $message;\n\n    public function init()\n    {\n        parent::init();\n        if ($this->message === null) {\n            $this->message = 'Hello World';\n        }\n    }\n\n    public function run()\n    {\n        return Html::encode($this->message);\n    }\n}\n```\n\nДля того, чтобы использовать этот виджет, достаточно добавить в представление следующий код:\n\n```php\n<?php\nuse app\\components\\HelloWidget;\n?>\n<?= HelloWidget::widget(['message' => 'Good morning']) ?>\n```\n\nНиже представлен вариант виджета `HelloWidget`, который принимает содержимое, обрамленное вызовами методов\n`begin()` и `end()`, HTML-кодирует его и выводит.\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Widget;\nuse yii\\helpers\\Html;\n\nclass HelloWidget extends Widget\n{\n    public function init()\n    {\n        parent::init();\n        ob_start();\n    }\n\n    public function run()\n    {\n        $content = ob_get_clean();\n        return Html::encode($content);\n    }\n}\n```\n\nКак Вы можете видеть, в методе `init()` происходит включение буферизации вывода PHP таким образом, что весь вывод\nмежду вызовами `init()` и `run()` может быть перехвачен, обработан и возвращен в `run()`.\n\n> Info: При вызове метода [[yii\\base\\Widget::begin()]] будет создан новый экземпляр виджета, при этом\nвызов метода `init()` произойдет сразу после выполнения остального кода в конструкторе виджета.\nПри вызове метода [[yii\\base\\Widget::end()]], будет вызван метод `run()`, а возвращенное им значение будет выведено\nметодом `end()`.\n\nСледующий фрагмент кода содержит пример использования модифицированного варианта `HelloWidget`:\n\n```php\n<?php\nuse app\\components\\HelloWidget;\n?>\n<?php HelloWidget::begin(); ?>\n\n    content that may contain <tag>'s\n\n<?php HelloWidget::end(); ?>\n```\n\nВ некоторых случаях, виджету может потребоваться вывести крупный блок содержимого. И хотя это содержимое может\nбыть встроено непосредственно в метод `run()`, целесообразней поместить его в [представление](structure-views.md)\nи вызвать метод [[yii\\base\\Widget::render()]] для его рендеринга. Например,\n\n```php\npublic function run()\n{\n    return $this->render('hello');\n}\n```\n\nПо умолчанию, файлы представлений виджетов должны находиться в директории `WidgetPath/views`, где `WidgetPath` -\nдиректория, содержащая файл класса виджета. Таким образом, в приведенном выше примере, для виджета будет\nиспользован файл представления `@app/components/views/hello.php`, при этом файл с классом виджета расположен в\n`@app/components`. Для того, чтобы изменить директорию, в которой содержатся файлы-представления для виджета,\nследует переопределить метод [[yii\\base\\Widget::getViewPath()]].\n\n\n## Лучшие Практики <span id=\"best-practices\"></span>\n\nВиджеты представляют собой объектно-ориентированный подход к повторному использованию кода пользовательского\nинтерфейса.\n\nПри создании виджетов, следует придерживаться основных принципов концепции MVC. В общем случае, основную логику\nследует располагать в классе виджета, разделяя при этом код, отвечающий за разметку в [представления](structure-views.md).\n\nРазрабатываемые виджеты должны быть самодостаточными. Это означает, что для их использования должно быть\nдостаточно всего лишь добавить виджет в представление. Добиться этого бывает затруднительно в том случае,\nкогда для его функционирования требуются внешние ресурсы, такие как CSS, JavaScript, изображения и т.д.\nК счастью, Yii предоставляет поддержку механизма для работы с ресурсами [asset bundles](structure-assets.md),\nкоторый может быть успешно использован для решения данной проблемы.\n\nВ случае, когда виджет не содержит логики, а содержит только код, отвечающий за вывод разметки, он мало\nотличается от [представления](structure-views.md). В действительности, единственное его отличие состоит в том, что\nвиджет представляет собой отдельный и удобный для распространения класс, в то время как представление - это\nобычный PHP скрипт, подходящий для использования только лишь в конкретном приложении.\n"
  },
  {
    "path": "docs/guide-ru/test-acceptance.md",
    "content": "Приёмочное тестирование\n=======================\n\nПриемочный тест проверяет сценарий с точки зрения конечного пользователя. К тестируемому приложению получают доступ либо через PhpBrowser, либо через реальный браузер. В обоих случаях браузеры взаимодействуют с приложением через HTTP, поэтому приложение следует запускать посредством веб-сервера. \n\nПриемочное тестирование реализуется с помощью фреймворка Codeception, который имеет отличную документацию:\n\n- [Codeception for Yii framework](https://codeception.com/for/yii)\n- [Codeception Acceptance Tests](https://codeception.com/docs/03-AcceptanceTests)\n\nЗапуск тестов в шаблонах проектов basic и advanced\n--------------------------------------------------\n\nЕсли вы начали с шаблона advanced, пожалуйста, обратитесь к руководству по [\"Тестированию\"](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/start-testing.md) для получения более детальной информации о запускаемых тестах.  \n\nЕсли вы начали с шаблона basic, обратитесь к разделу [\"Тестирование\"](https://github.com/yiisoft/yii2-app-basic/blob/master/README.md#testing) в его README.\n"
  },
  {
    "path": "docs/guide-ru/test-environment-setup.md",
    "content": "Настройка тестового окружения\n=============================\n\n> Note: Данный раздел находится в разработке.\n\nYii 2 официально поддерживает интеграцию с фреймворком для тестирования [`Codeception`](https://github.com/Codeception/Codeception),\nкоторый позволяет вам проводить следующие типы тестов:\n\n- [Модульное тестирование](test-unit.md) - проверяет что отдельный модуль кода работает верно;\n- [Функциональное тестирование](test-functional.md) - проверяет пользовательские сценарии через эмуляцию браузера;\n- [Приёмочное тестирование](test-acceptance.md) - проверяет пользовательские сценарии в браузере.\n\nВсе три типа тестов представлены в шаблонах проектов\n[`yii2-basic`](https://github.com/yiisoft/yii2-app-basic) и\n[`yii2-advanced`](https://github.com/yiisoft/yii2-app-advanced).\n\nДля того, чтобы запустить тесты, необходимо установить [Codeception](https://github.com/Codeception/Codeception).\nСделать это можно как локально, то есть только для текущего проекта, так и глобально для компьютера разработчика.\n\nДля локальной установки используйте следующие команды:\n\n```\ncomposer require \"codeception/codeception=2.1.*\"\ncomposer require \"codeception/specify=*\"\ncomposer require \"codeception/verify=*\"\n```\n\nДля глобальной установки необходимо добавить директиву `global`:\n\n```\ncomposer global require \"codeception/codeception=2.1.*\"\ncomposer global require \"codeception/specify=*\"\ncomposer global require \"codeception/verify=*\"\n```\n\nЕсли вы никогда не пользовались Composer для установки глобальных пакетов, запустите `composer global status`.\nНа выходе вы должны получить:\n\n```\nChanged current directory to <directory>\n```\n\nЗатем `<directory>/vendor/bin` добавьте в переменную окружения `PATH`. После этого можно использовать `codecept` глобально\nиз командной строки.\n\n> Note: глобальная установка позволяет вам использовать Codeception для всех проектов на компьютере разработчика\n  путём запуска команды `codecept` без указания пути. Тем не менее, данный подход может не подойти. К примеру, в двух \n  разных проектах может потребоваться установить разные версии Codeception. Для простоты все команды в разделах про\n  тестирование используются так, будто Codeception установлен глобально.\n  \n### Настройка веб-сервера Apache\n\nЕсли вы используете Apache и настроили его как описано в разделе «[Установка Yii](start-installation.md)», то для тестов вам необходимо создать отдельный виртуальный хост который будет работать с той же папкой, но использовать входной скрипт `index-test.php`:\n\n```\n<VirtualHost *:80>\n    DocumentRoot \"path/to/basic/web\"\n    ServerName mysite-test\n    <Directory \"path/to/basic/web\">\n        Order Allow,Deny\n        Allow from all\n        AddDefaultCharset utf-8\n        DirectoryIndex index-test.php\n        RewriteEngine on\n        RewriteCond %{REQUEST_FILENAME} !-f\n        RewriteCond %{REQUEST_FILENAME} !-d\n        RewriteRule . index-test.php\n    </Directory>\n</VirtualHost>\n```\n\nТак мы укажем веб серверу перенаправлять все запросы на скрипт `index-test.php`.\n> Note: Обратите внимание, что здесь мы указываем параметр `DirectoryIndex`, помимо тех параметров, которые были указаны для первого хоста. Это сделано с той целью, чтобы при обращении к главной странице по адресу `mysite-test` также использовался бы скрипт `index-test.php`.\n"
  },
  {
    "path": "docs/guide-ru/test-fixtures.md",
    "content": "Фикстуры\n========\n\nФикстуры (англ. fixtures) - это важная составляющая тестирования. Их основная задача заключается в подготовке окружения\nс заранее фиксированным/известным состоянием для гарантии повторяемости процесса тестирования. Yii предоставляет\nфреймворк, который позволяет легко и точно определять фикстуры и использовать их в ваших тестах.\n\nКлючевым понятием в фреймворке фикстур Yii является так называемый *объект фикстуры*. Объект фикстуры представляет собой \nособый аспект тестового окружения, который наследуется от [[yii\\test\\Fixture]] или его наследников. Например, вы можете\nиспользовать `UserFixture` для того, чтобы быть уверенным, что таблица пользователей содержит известный набор данных. Вы\nзагружаете один или несколько объектов фикстур перед запуском теста и выгружаете их после его завершения.\n\nФикстура может зависеть от других фикстур, заданных через свойство [[yii\\test\\Fixture::depends]].\nКогда фикстура загружается, фикстуры, от которых она зависит, будут автоматически загружены ДО нее, а когда она \nвыгружается все зависимые фикстуры будут выгружены ПОСЛЕ нее.\n \n\nОбъявление фикстуры\n-------------------\n\nДля объявления фикстуры создайте новый класс унаследованный от [[yii\\test\\Fixture]] или [[yii\\test\\ActiveFixture]].\nПервый лучше всего подходит для фикстур общего назначения, в то время как последний имеет расширенные функции, \nспециально предназначенные для работы с базой данных и ActiveRecord.\n\nСледующий код показывает как объявить фикстуру для модели ActiveRecord `User`, которая соответствует таблице пользователей.\n\n\n```php\n<?php\nnamespace app\\tests\\fixtures;\n\nuse yii\\test\\ActiveFixture;\n\nclass UserFixture extends ActiveFixture\n{\n    public $modelClass = 'app\\models\\User';\n}\n```\n\n> Tip: каждая `ActiveFixture` предназначена для подготовки таблицы базы данных для тестирования. Вы можете указать\n> таблицу как через свойство [[yii\\test\\ActiveFixture::tableName]], так и через свойство [[yii\\test\\ActiveFixture::modelClass]].\n> Если последнее, то в этом случае имя таблицы будет взято из модели `ActiveRecord`, указанной в `modelClass`.\n\n> Note: [[yii\\test\\ActiveFixture]] используется только для реляционных баз данных. Для NoSQL-решений Yii\n> предоставляет следующие классы `ActiveFixture`:\n>\n> - MongoDB: [[yii\\mongodb\\ActiveFixture]]\n> - Elasticsearch: [[yii\\elasticsearch\\ActiveFixture]] (начиная с версии 2.0.2)\n\nДанные для фикстуры `ActiveFixture`, как правило, находятся в файле `FixturePath/data/TableName.php`,  \nгде `FixturePath` указывает на директорию, в которой располагается файл класса фикстуры, а `TableName` на имя таблицы,\nс которой она ассоциируется. Для примера выше, данные должны быть в файле `@app/tests/fixtures/data/user.php`. \nДанный файл должен вернуть массив данных для строк, которые будут вставлены в таблицу пользователей. Например  \n\n```php\n<?php\nreturn [\n    'user1' => [\n        'username' => 'lmayert',\n        'email' => 'strosin.vernice@jerde.com',\n        'auth_key' => 'K3nF70it7tzNsHddEiq0BZ0i-OU8S3xV',\n        'password' => '$2y$13$WSyE5hHsG1rWN2jV8LRHzubilrCLI5Ev/iK0r3jRuwQEs2ldRu.a2',\n    ],\n    'user2' => [\n        'username' => 'napoleon69',\n        'email' => 'aileen.barton@heaneyschumm.com',\n        'auth_key' => 'dZlXsVnIDgIzFgX4EduAqkEPuphhOh9q',\n        'password' => '$2y$13$kkgpvJ8lnjKo8RuoR30ay.RjDf15bMcHIF7Vz1zz/6viYG5xJExU6',\n    ],\n];\n```\n\nВы можете задать псевдоним строке для того, чтобы в будущем вы могли ссылаться на нее в ваших тестах. В примере выше \n2 строки имеют псевдонимы `user1` и `user2`, соответственно.\n\nТакже вам не нужно указывать данные для столбцов с автоинкрементом. Yii автоматически заполнит значения данных столбцов\nв момент загрузки фикстуры.\n\n> Tip: вы можете указать свой путь до файла данных через свойство [[yii\\test\\ActiveFixture::dataFile]].\n> Вы также можете переопределить метод [[yii\\test\\ActiveFixture::getData()]], чтобы предоставить данные.\n\nКак мы описали ранее, фикстура может зависеть от других фикстур. Например, для `UserProfileFixture` возможно потребуется\nзависимость от `UserFixture` так как таблица пользовательских профилей содержит внешний ключ, указывающий на таблицу пользователей.\nЗависимость указывается через свойство [[yii\\test\\Fixture::depends]], как в следующем примере\n\n```php\nnamespace app\\tests\\fixtures;\n\nuse yii\\test\\ActiveFixture;\n\nclass UserProfileFixture extends ActiveFixture\n{\n    public $modelClass = 'app\\models\\UserProfile';\n    public $depends = ['app\\tests\\fixtures\\UserFixture'];\n}\n```\n\nЗависимость также гарантирует, что фикстуры загружаются и выгружаются в определенном порядке. В предыдущем примере `UserFixture`\nбудет автоматически загружена до `UserProfileFixture`, тем самым гарантируя существование всех внешних ключей, и будет выгружена\nпосле того как выгрузится `UserProfileFixture` по тем же причинам. \n\nВыше мы показали как объявить фикстуру для таблицы базы данных. Для объявления фикстуры, не связанной с базой данных (например,\nфикстуры для определенных файлов и директорий), вам следует унаследовать ее от класса [[yii\\test\\Fixture]] \nи переопределить методы [[yii\\test\\Fixture::load()|load()]] и [[yii\\test\\Fixture::unload()|unload()]].\n\n\nИспользование фикстур\n---------------------\n\nЕсли вы используете [Codeception](https://codeception.com/) для тестирования вашего кода, вам следует рассмотреть вопрос\nоб использовании расширения `yii2-codeception`, которое имеет встроенную поддержку загрузки фикстур и доступа к ним.\nЕсли вы используете другой фреймворк для тестирования, вы можете использовать [[yii\\test\\FixtureTrait]] в ваших тестах для \nэтих целей.\n\nДалее мы опишем как написать класс модульного тестирования для модели `UserProfile` с использованием расширения `yii2-codeception`.\n\nОбъявите какие фикстуры вы хотите использовать в методе [[yii\\test\\FixtureTrait::fixtures()|fixtures()]] вашего класса модульного\nтестирования, унаследованного от [[yii\\codeception\\DbTestCase]] или [[yii\\codeception\\TestCase]]. Например,\n\n```php\nnamespace app\\tests\\unit\\models;\n\nuse yii\\codeception\\DbTestCase;\nuse app\\tests\\fixtures\\UserProfileFixture;\n\nclass UserProfileTest extends DbTestCase\n{\n    public function fixtures()\n    {\n        return [\n            'profiles' => UserProfileFixture::class,\n        ];\n    }\n\n    // ...методы тестирования...\n}\n```\n\nФикстуры перечисленные в методе `fixtures()` будут автоматически загружены перед выполнением каждого метода тестирования тест-кейса\nи выгружены после завершения каждого метода тестирования. И, как мы описали ранее, когда фикстура загружается, все зависимые от нее\nфикстуры будут автоматически загружены в первую очередь. В приведенном выше примере, при выполнении любого метода тестирования\nв тест-кейсе последовательно будут загружены две фикстуры: `UserFixture` и `UserProfileFixture`, поскольку `UserProfileFixture` \nзависит от `UserFixture`.\n\nДля определения фикстур в методе `fixtures()` вы можете использовать либо имя класса, либо массив настроек. С помощью массива\nнастроек вы можете настроить свойства фикстуры, которые будут установлены при ее загрузке.\n\nВы также можете назначить фикстуре псевдоним. В примере выше, `profiles` является псевдонимом фикстуры `UserProfileFixture`.\nС помощью псевдонима вы можете получить объект фикстуры в ваших методах тестирования. Например, `$this->profiles` вернет\nобъект `UserProfileFixture`.\n\nПоскольку `UserProfileFixture` наследуется от `ActiveFixture`, вы можете также использовать следующий синтаксис для доступа к\nданным фикстуры:\n\n```php\n// вернет строку данных для псевдонима 'user1'\n$row = $this->profiles['user1'];\n// вернет модель UserProfile, соответствующую строке данных для псевдонима 'user1'\n$profile = $this->profiles('user1');\n// обход данных фикстуры в цикле\nforeach ($this->profiles as $row) ...\n```\n\n> Info: `$this->profiles` продолжает быть объектом класса `UserProfileFixture`. Указанные особенности доступа реализуются\nчерез магические методы PHP.\n\n\nОпределение и использование глобальных фикстур\n----------------------------------------------\n\nФикстуры, описанные выше, в основном используются в рамках определенных тест-кейсов. В большинстве случаев, вам также нужны \nглобальные фикстры, которые применяются во ВСЕХ или большинстве тест-кейсов. Примером является фикстура [[yii\\test\\InitDbFixture]], \nкоторая делает 2 вещи:\n\n* Запускает скрипт `@app/tests/fixtures/initdb.php` для выполнения ряда общих задач инициализации тестового окружения;\n* Отключает проверку целостности данных перед загрузкой остальных фикстур, и включает ее обратно после того как все остальные фикстуры будут выгружены.\n\nИспользование глобальных фикстур схоже с использованием не глобальных. Единственное отличие в том, что вы должны объявить эти\nфикстуры в методе [[yii\\codeception\\TestCase::globalFixtures()]], а не `fixtures()`. Когда тест-кейс загружает фикстуры, сначала\nзагружаются глобальные фикстуры, затем все остальные.\n \nПо умолчанию фикстура `InitDbFixture` уже обяъвлена в методе `globalFixtures()` класса [[yii\\codeception\\DbTestCase]].\nЭто означает, что вы должны работать только с файлом `@app/tests/fixtures/initdb.php`, если вы хотите чтобы перед каждым тестом\nвыполнялись определенные подготовительные работы. В противном случае вы просто можете сфокусироваться на разработке \nконкретных тест-кейсов и соответствующих фикстур.\n\n\nОрганизация классов фикстур и файлов с данными\n----------------------------------------------\n\nПо умолчанию классы фикстур ищут соответствующие файлы данных в директории `data`, которая является подпапкой папки, содержащей \nфайлы классов фикстур. Вы можете следовать этому соглашению при работе над простыми проектами. Есть вероятность, что на больших \nпроектах вам потребуется менять набор данных для одного и того же класса фикстур в разных тестах. Таким образом, мы рекомендуем\nвам организовать файлы данных иерархически, подобно пространству имен ваших классов. Например,\n\n```\n# в папке tests\\unit\\fixtures\n\ndata\\\n    components\\\n        fixture_data_file1.php\n        fixture_data_file2.php\n        ...\n        fixture_data_fileN.php\n    models\\\n        fixture_data_file1.php\n        fixture_data_file2.php\n        ...\n        fixture_data_fileN.php\n# и так далее\n```\n\nТаким образом вы избежите коллизий файлов данных фикстур между тестами и будете использовать их, как вам нужно.\n\n> Note: в примере выше файлы данных фикстур названы так только в качестве примера. В реальных жизни вам следует\n> называть их в соответствии с тем от какого класса наследуется ваш класс фикстуры. Например, при наследовании от [[yii\\test\\ActiveFixture]]\n> для фикстур БД вам следует использовать имя таблицы в качестве имени файла данных; при наследовании от [[yii\\mongodb\\ActiveFixture]]\n> для фикстур MongoDB вам следует использовать имя коллекции в качестве имени файла.\n\nВы можете использовать похожую иерархию для организации файлов классов фикстур. Чтобы избежать конфликта с файлами данных \nвы можете использовать в качестве корневой директории `fixtures` вместо `data`.  \n\n\nРезюме\n------\n\n> Note: Этот раздел находится в разработке.\n\nВыше мы описали как объявлять и использовать фикстуры. Ниже приведен типовой сценарий выполнения модульных тестов, связанных с БД:\n\n1. Используйте команду `yii migrate` для обновления тестовой БД до последней версии;\n2. Выполнить тест-кейс:\n   - Загрузка фикстур: очистка соответствующих таблиц БД и заполнение их данными фикстур;\n   - Выполнение теста;\n   - Выгрузка фикстур.\n3. Повторение шага 2 до тех пор, пока не выполнятся все тесты.\n\n\n**Будет доработано**\n\nУправление фикстурами\n=====================\n\n> Note: Данный раздел находится в разработке.\n>\n> todo: данный раздел может быть объединен с предыдущими частями test-fixtures.md\n\nФикстуры являются важной составляющей тестирования. Их основная задача в предоставлении набора данных, необходимого для тестирования\nразличных сценариев работы вашего приложения. С этими данными использование ваших тестов становятся более эффективным и полезным.\n\nYii поддерживает фикстуры через утилиту командной строки `yii fixture`. Эта утилита поддерживает:\n\n* Загрузку фикстур в различные хранилища, такие как: RDBMS, NoSQL и другие;\n* Выгрузку фикстур разными способами (как правило очищает хранилище);\n* Автоматическую генерацию фикстур и наполнение их случайными данными\n\n\nФормат фикстуры\n---------------\n\nФикстуры - это объекты с различными методами и конфигурацией, с которыми вы можете ознакомиться в официальной [документации](https://github.com/yiisoft/yii2/blob/master/docs/guide/test-fixture.md).\n\nДавайте предположим, что у нас есть следующий набор данных фикстуры для загрузки:\n\n```\n# файл users.php в директории файлов данных фикстур, по умолчанию @tests\\unit\\fixtures\\data\n\nreturn [\n    [\n        'name' => 'Chase',\n        'login' => 'lmayert',\n        'email' => 'strosin.vernice@jerde.com',\n        'auth_key' => 'K3nF70it7tzNsHddEiq0BZ0i-OU8S3xV',\n        'password' => '$2y$13$WSyE5hHsG1rWN2jV8LRHzubilrCLI5Ev/iK0r3jRuwQEs2ldRu.a2',\n    ],\n    [\n        'name' => 'Celestine',\n        'login' => 'napoleon69',\n        'email' => 'aileen.barton@heaneyschumm.com',\n        'auth_key' => 'dZlXsVnIDgIzFgX4EduAqkEPuphhOh9q',\n        'password' => '$2y$13$kkgpvJ8lnjKo8RuoR30ay.RjDf15bMcHIF7Vz1zz/6viYG5xJExU6',\n    ],\n];\n```\n\nЕсли вы используете фикстуру, которая загружает данные в базу данных, то эти строки будут применены к таблице `users`. \nЕсли вы используете фикстуру для загрузки данных в nosql, например, фикстура для `mongodb`, то данные будут применены к коллекции `users`.\nДля того, чтобы узнать о реализации различных сценариях загрузки фикстур, обратитесь к официальной [документации](https://github.com/yiisoft/yii2/blob/master/docs/guide/test-fixture.md). \nПредыдущий пример фикстуры был сгенерирован автоматически с использованием расширения `yii2-faker`, подробнее про это читайте в этом [разделе](#auto-generating-fixtures).\nИмя класса фикстуры должно быть в единственном числе.\n\nЗагрузка фикстур\n----------------\n\nКласс фикстур должны содержать суффикс `Fixture`. По умолчанию поиск фикстур выполняется в пространстве имен `tests\\unit\\fixtures`, но вы можете изменить это поведение\nчерез конфигурационный файл или параметры команды. Вы можете исключить некоторые фикстуры из загрузки или выгрузки добавив `-` перед их именем, например `-User`.\n\nЧтобы загрузить фикстуру, выполните следующую команду:\n\n```\nyii fixture/load <fixture_name>\n```\n\nОбязательный параметр `fixture_name` указываем на имя фикстуры, которая должна быть загружена. Вы можете загрузить несколько фикстур за раз.\nНиже указаны примеры корректного использования данной команды:\n\n```\n// загрузить фикстуру `User`\nyii fixture/load User\n\n// то же что и выше, т.к. \"load\" является действием по умолчанию для команды \"fixture\"\nyii fixture User\n\n// загрузить нескольких фикстур\nyii fixture \"User, UserProfile\"\n\n// загрузить все фикстуры\nyii fixture/load \"*\"\n\n// то же что и выше\nyii fixture \"*\"\n\n// загрузить все фикстуры кроме указанной\nyii fixture \"*, -DoNotLoadThisOne\"\n\n// загрузка фикстур, но искать их следует в другом пространстве имен. Пространство имен по умолчанию: tests\\unit\\fixtures.\nyii fixture User --namespace='alias\\my\\custom\\namespace'\n\n// загрузить глобальную фикстуру `some\\name\\space\\CustomFixture` перед загрузкой остальных фикстур.\n// По умолчанию данный параметр установлен в `InitDbFixture` для включения/отключения проверки целостности данных.\n// Вы можете задать несколько глобальных фикстур, указав их через запятую\nyii fixture User --globalFixtures='some\\name\\space\\Custom'\n```\n\nВыгрузка фикстур\n----------------\n\nДля выгрузки фикстур выполните следующую команду:\n\n```\n// выгрузить фикстуру `Users`, по умолчанию будут удалены все данные из таблицы \"users\" или из коллекции \"users\", если это фикстура mongodb\nyii fixture/unload User\n\n// выгрузить несколько фикстур\nyii fixture/unload \"User, UserProfile\"\n\n// выгрузить все фикстуры\nyii fixture/unload \"*\"\n\n// выгрузить все фикстуры за исключением указанной\nyii fixture/unload \"*, -DoNotUnloadThisOne\"\n\n```\n\nПри выгрузке фикстур вы также можете использовать параметры `namespace` и `globalFixtures`.\n\nГлобальная настройка команды\n----------------------------\n\nХотя параметры командой строки и позволяют нам настраивать команду миграции на лету, иногда нам может понадобиться настроить \nкоманду один раз для всех сценариев запуска. Например, вы можете настроить различные пути до файлов с фикстурами как в примере ниже:\n\n```\n'controllerMap' => [\n    'fixture' => [\n        'class' => 'yii\\console\\controllers\\FixtureController',\n        'namespace' => 'myalias\\some\\custom\\namespace',\n        'globalFixtures' => [\n            'some\\name\\space\\Foo',\n            'other\\name\\space\\Bar'\n        ],\n    ],\n]\n```\n\nАвтоматическая генерация фикстур\n--------------------------------\n\nYii также может автоматически генерировать для вас фикстуры на основе некоторого шаблона. Вы можете генерировать фикстуры с \nразличным набором данных на разных языках и в разных форматах. Данная возможность основана на использовании библиотеки [Faker](https://github.com/fzaninotto/Faker)\nи расширения `yii2-faker`. \n\nДля получения дополнительной информации ознакомьтесь с [руководством](https://github.com/yiisoft/yii2-faker).\n"
  },
  {
    "path": "docs/guide-ru/test-functional.md",
    "content": "Функциональные тесты\n====================\n\nФункциональный тест проверяет сценарии с точки зрения пользователя. Он похож на [приёмочный тест](test-acceptance.md),\nно вместо взаимодействия через HTTP заполняет окружение (параметры POST и GET) и запускает экземпляр приложения\nпрямо из кода.\n\nФункциональные тесты, как правило, быстрее приёмочных и предоставляют подробные stack trace при ошибках.\nРекомендуется отдавать им предпочтение, если только у вас нет специфической настройки веб-сервера или сложного UI\nна JavaScript.\n\nФункциональное тестирование реализуется с помощью фреймворка Codeception, который имеет отличную документацию:\n\n- [Codeception for Yii framework](https://codeception.com/for/yii)\n- [Codeception Functional Tests](https://codeception.com/docs/04-FunctionalTests)\n\n## Запуск тестов в шаблонах проектов basic и advanced\n\nЕсли вы начали с шаблона advanced, обратитесь к руководству по [\"Тестированию\"](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/start-testing.md) для получения более детальной информации о запуске тестов.\n\nЕсли вы начали с шаблона basic, обратитесь к разделу [\"Тестирование\"](https://github.com/yiisoft/yii2-app-basic/blob/master/README.md#testing) в его README.\n"
  },
  {
    "path": "docs/guide-ru/test-overview.md",
    "content": "Тестирование\n============\n\nТестирование является важной составляющей разработки программного обеспечения. Мы проводим тестирование непрерывно, осознаем мы это или нет. Например, когда мы пишем класс на языке PHP, мы можем отлаживать его шаг за шагом или просто использовать `echo` или `die` для проверки, что реализация работает в соответствии с намеченным планом. В случае веб приложения, мы вводим некоторые тестовые данные в форму для того, чтобы убедиться, что страница взаимодействует с нами, как ожидается.\n\nПроцесс тестирования может быть автоматизирован так, что каждый раз, когда нам нужно что-то проверить, мы просто должны \nвызвать код, который сделает это за нас. Код, который проверяет, что результат совпадает с тем, что мы планировали, называется тестом, а процесс создания тестов и их последующего использования - автоматизированным тестированием, что и является главной темой данного раздела.\n\nРазработка с тестами\n--------------------\n\nРазработка через тестирование (TDD) и разработка через поведение (BDD) - это подходы разработки программного обеспечения, в рамках которых поведение части кода или целая фича описывается в виде набора сценариев или тестов ДО написания фактического кода и только\nзатем создается реализация. Тем самым мы можем использовать данные тесты для проверки, что достигается заданное поведение.\n\nПроцесс разработки фичи следующий:\n\n- Создать новый тест, описывающий функцию, которая будет реализована.\n- Запустить новый тест и убедиться, что он терпит неудачу. Это ожидаемо, т.к. на данный момент еще нет конкретной реализации.\n- Написать простой код, чтобы новый тест отрабатывал без ошибок.\n- Запустить все тесты и убедиться, что они отрабатывают без ошибок\n- Улучшить код и убедиться, что все тесты все еще отрабатывают без ошибок\n\nПосле того как это завершено, процесс повторяется снова для другой фичи или улучшения. Если существующая фича должна быть изменена, то и тесты также должны быть изменены.\n\n> Tip: Если вы чувствуете, что вы теряете время выполняя много мелких и простых итераций, попробуйте покрыть это\n> вашим тестовым сценарием перед следующим выполнением тестов. Если вы слишком много отлаживаете, попробуйте сделать обратное.\n\nНаписание тестов до реализации конкретного функционала позволяет нам сосредоточиться на том, что мы хотим достичь и полностью\nпогрузиться в \"как это сделать\" впоследствии. \n\nОбычно это приводит к лучшим абстракциям и более легкой поддержке тестов, когда речь идет о корректировке фичи или уменьшении связанности компонентов.\n\nТаким образом, плюсы этого подхода следующие:\n\n- Позволяет вам сосредоточиться на одной вещи, что в свою очередь приводит к улучшению планирования и реализации.\n- Более подробное покрытие тестами функционала. Таким образом, если все тесты отрабатывают без ошибок, скорее всего, ничего не сломано.\n\nВ долгосрочной перспективе это, как правило, дает вам хороший эффект экономии времени.\n\n> Tip: Если вы хотите узнать больше о принципах сбора требования программного обеспечения и моделирования\n> предметной области, рекомендуем изучить [Проблемно-ориентированное проектирование (DDD)](https://en.wikipedia.org/wiki/Domain-driven_design).\n\nКогда и как тестировать\n-----------------------\n\nПринцип разработки, описанный выше, имеет смысл применять для долгосрочных и относительно сложных проектов, в то время как для простых это может быть излишним. Есть несколько показателей того, когда данный подход уместен:\n\n- Проект уже большой и сложный.\n- Требования к проекту начинают усложняться. Проект постоянно растет.\n- Долгосрочный проект.\n- Цена ошибки очень высока\n\nНет ничего плохого в создании тестов, покрывающих поведение существующей реализации.\n\n- Legacy-проект который постоянно обновляется.\n- Вам поручили работу над проектом, в котором нет ни одного теста.\n\nВ некоторых случаях автоматизированное тестирование может быть излишним:\n\n- Проект простой и не станет более сложным.\n- Это одноразовый проект, который больше не будет дорабатываться.\n\nТем не менее, если у вас есть время, было бы хорошо автоматизировать тестирование и в этих случаях.\n\nЧто почитать\n------------\n\n- Экстремальное программирование. Разработка через тестирование / Кент Бек. ISBN: 0321146530.\n"
  },
  {
    "path": "docs/guide-ru/test-unit.md",
    "content": "Модульные тесты\n===============\n\n> Note: Данный раздел находится в разработке.\n\nМодульный тест проверяет что отдельный модуль кода работает верно. В ООП самым базовым модулем является класс. То есть\nмодульный тест проверяет все методы интерфейса класса. На вход подаются различные параметры и тест проверяет, что методы\nвозвращают ожидаемые значения. Модульные тесты обычно пишутся тем же, кто реализует тестируемый класс.\n\nМодульное тестирование в Yii использует PHPUnit и, опционально, Codeception. Рекомендуется проверить его документацию:\n\n- [Документация PHPUnit начиная с главы 2](https://phpunit.de/manual/current/en/writing-tests-for-phpunit.html).\n- [Codeception Unit Tests](https://codeception.com/docs/05-UnitTests).\n\nЗапуск тестов шаблонов проектов basic и advanced\n------------------------------------------------\n\n- [Инструкции для шаблона advanced](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/start-testing.md).  \n- [Инструкции для шаблона basic](https://github.com/yiisoft/yii2-app-basic/blob/master/README.md#testing).\n\nМодульные тесты фреймворка\n--------------------------\n\nЕсли вам необходимо запустить набор модульных тестов для самого Yii, прочитайте\n\"[Подготовка к разработке Yii 2](https://github.com/yiisoft/yii2/blob/master/docs/internals-ru/getting-started.md)\".\n"
  },
  {
    "path": "docs/guide-ru/translators.json",
    "content": "[\n  \"Alexander Makarov\",\n  \"Timur Melnikov\",\n  \"Dmitry Brusenskiy\",\n  \"Vadim Belorussov\",\n  \"Dmitry Naumenko\",\n  \"Ivan Bagaev\",\n  \"Vasiliy Baukin\",\n  \"Alexander Roman\",\n  \"Pavel Ivanov\",\n  \"Alexandr Kalashnikov\",\n  \"Mark Ragazzo\",\n  \"Dmitry Korolev\",\n  \"Evgeniy Tkachenko\",\n  \"keltstr\",\n  \"Sergey Anisimov\",\n  \"Aleksandr Safonov\",\n  \"Dmitriy Makarov\",\n  \"Evgeniy Moiseenko\",\n  \"Maxim Chistyakov\",\n  \"Anton Abramov\",\n  \"Peter Efremov\",\n  \"Taras Gudz\",\n  \"metheoryt\",\n  \"Aleksandr Bushlanov\",\n  \"Disassem\",\n  \"Eugene Tupikov\",\n  \"Viktor Pikaev\",\n  \"Linux2000\",\n  \"Alex Solomaha\",\n  \"Alexey Rogachev\",\n  \"Anton Anisimov\",\n  \"Beowulfenator\",\n  \"Egor Verbitsky\",\n  \"Nazar Golubovsky\",\n  \"Nepster\",\n  \"Artyom Egorov\",\n  \"Vadym Chenin\",\n  \"Vladimir Vyachin\",\n  \"maxmirazh33\",\n  \"sergey144010\",\n  \"Andrey Opeykin\",\n  \"Andrei Radzihov\",\n  \"AnegvE\",\n  \"Arthur Khachaturov\",\n  \"Eugene Cloverfield\",\n  \"Ivan Artamonov\",\n  \"Paul Klimov\",\n  \"LAV45\",\n  \"Maksim Pestov\",\n  \"PendalF89\",\n  \"Max Alexandrov\",\n  \"and others\"\n]\n"
  },
  {
    "path": "docs/guide-ru/tutorial-console.md",
    "content": "Консольное приложение\n=====================\n\nКроме богатых возможностей для построения веб приложений, Yii также имеет полноценную поддержку консольных приложений,\nкоторые обычно используются для создания фоновых и служебных задач, поддерживающих сайт.\n\nСтруктура консольных приложений очень похожа на структуру веб приложения. Она состоит из одного и более классов\n[[yii\\console\\Controller]], которые часто называют командами в консольной среде. Каждый контроллер может иметь одно\nили более действий, как и веб контроллеры.\n\nВ обоих шаблонах проектов уже есть консольное приложение.\nВы можете запустить его, вызвав скрипт yii, который находится в основной директории вашего приложения.\nВы получите список доступных команд, если вызовете его без параметров:\n\n![Запуск команды ./yii для вывода помощи](images/tutorial-console-help.png)\n\nКак вы можете видеть на скриншоте, в Yii уже определён набор доступных по умолчанию команд:\n\n- [[yii\\console\\controllers\\AssetController|AssetController]] - Позволяет вам объединять и сжимать ваши JavaScript и CSS файлы.\n  Больше об этой команде вы можете узнать в [Assets Section](structure-assets.md#using-asset-bundles).\n- [[yii\\console\\controllers\\CacheController|CacheController]] - Позволяет вам сбрасывать кеш приложения.\n- [[yii\\console\\controllers\\FixtureController|FixtureController]] - Управляет загрузкой и выгрузкой данных фикстур для тестирования.\n  Данная команда более подробно описана в [Testing Section about Fixtures](test-fixtures.md#managing-fixtures).\n- [[yii\\console\\controllers\\HelpController|HelpController]] - Обеспечивает справочную информацию о консольных командах,\n  это команда по умолчанию и она печатает текст, который вы видели выше.\n- [[yii\\console\\controllers\\MessageController|MessageController]] - Извлекает сообщения для перевода из файлов с исходными тестами.\n  Больше об этой команде вы можете узнать в [I18N Section](tutorial-i18n.md#message-command).\n- [[yii\\console\\controllers\\MigrateController|MigrateController]] - Управление миграциями приложения.\n  Миграции базы данных более детально описаны в [Database Migration Section](db-migrations.md).\n- [[yii\\console\\controllers\\ServeController|ServeController]] - Позволяет запускать встроенный вебсервер PHP.\n\n\nИспользование <span id=\"usage\"></span>\n-------------\n\nВы можете запустить действие консольного контроллера, используя следующий синтаксис:\n\n```\nyii <route> [--option1=value1 --option2=value2 ... argument1 argument2 ...]\n```\n\nВ приведённом выше примере, `<route>` относится к действию контроллера. Параметры будут подставляться в свойства\nкласса и в аргументы метода действия.\n\nДля примера, [[yii\\console\\controllers\\MigrateController::actionUp()|MigrateController::actionUp()]]\nс [[yii\\console\\controllers\\MigrateController::$migrationTable|MigrateController::$migrationTable]] установкой `migrations`\nи лимитом в 5 миграций может быть вызвано следующим образом:\n\n```\nyii migrate/up 5 --migrationTable=migrations\n```\n\n> Note: При использовании в консоли `*`, не забудьте поместить её в кавычки `\"*\"` чтобы избежать её интерпретации\n> и замены на все имена файлов в данной директории.\n\n\nВходной скрипт <span id=\"entry-script\"></span>\n--------------\n\nВходной скрипт консольного приложения - это подобие файла `index.php`, используемого в веб приложении.\nВходной скрипт консоли, как правило, называется `yii` и располагается в основной директории приложения.\nОн содержит код похожий на следующее:\n\n```php\n#!/usr/bin/env php\n<?php\n/**\n * Yii console bootstrap file.\n */\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n\nrequire __DIR__ . '/vendor/autoload.php';\nrequire __DIR__ . '/vendor/yiisoft/yii2/Yii.php';\n\n$config = require __DIR__ . '/config/console.php';\n\n$application = new yii\\console\\Application($config);\n$exitCode = $application->run();\nexit($exitCode);\n```\n\nЭтот скрипт будет создан как часть вашего приложения; вы можете его редактировать, если вам это необходимо.\n`YII_DEBUG` можете установить в `false` если вам не нужно видеть отладочный вывод при ошибке, и/или если вы хотите\nулучшить общую производительность. В обоих шаблонах приложения, во входном скрипте приложения отладка включена по\nумолчанию для обеспечения более дружественного к разработчику окружения.\n\n\nНастройка <span id=\"configuration\"></span>\n---------\n\nКак видно из приведённого выше кода, консольное приложение использует свой собственный файл конфигурации, названый `console.php`.\nВ этом файле вы должны произвести настройку различных [компонентов приложения](structure-application-components.md) и\nсвойств консольного приложения.\n\nЕсли ваше веб и консольное приложение имеет много общих параметров конфигурации, вы можете выделить общую часть в \nотдельный файл, и включить его в оба файла конфигурации (веб и консоль).\nВы можете посмотреть пример в \"продвинутом\" шаблоне проекта.\n\n> Tip: Иногда, вам может потребоваться запустить консольную команду используя конфигурацию, отличную от той, что\n> указано во входном скрипте. Для примера, вы можете использовать команду `yii migrate` для обновления тестовой\n> базы данных, которая настраивается для каждого отдельного набора тестов. Для изменения файла конфигурации,\n> просто укажите свой конфигурационный файл через опцию `appconfig` при запуске команды:\n> \n> ```\n> yii <route> --appconfig=path/to/config.php ...\n> ```\n\nАвтодополнение консольных команд <span id=\"console-command-completion\"></span>\n---------------\n\nАвтодополнение аргументов команд является полезной возможностью при работе в командной строке.\nНачиная с версии 2.0.11, команда `./yii` поддерживает автодополнение для Bash и ZSH. \n\n### Автодополнение для Bash\n\nУбедитесь, что средства автодополнения для Bash установлены. В большинстве дистрибутивов они поставляются по умолчанию.\n\nСохраните скрипт для автодополнения в директорию `/etc/bash_completion.d/`:\n\n     curl -L https://raw.githubusercontent.com/yiisoft/yii2/master/contrib/completion/bash/yii -o /etc/bash_completion.d/yii\n\nДля временного использования, вы можете сохранить файл в произвольную директорию и подключить его на время работы сессии,\nвызвав команду `source yii`.\n\nЕсли скрипт был установлен глобально, вам потребуется перезапустить терминал или выполнить команду `source ~/.bashrc` \nдля активации автодополнения.\n\nОбратитесь к [инструкции по автодополнению в Bash](https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion.html)\nчтобы узнать о других способах подключения скриптов автодополнения в ваше окружение.\n\n### Автодополнение для ZSH\n\nСохраните скрипт автодополнения в директорию для скриптов автодополнения. Например, `~/.zsh/completion/`\n\n```\nmkdir -p ~/.zsh/completion\ncurl -L https://raw.githubusercontent.com/yiisoft/yii2/master/contrib/completion/zsh/_yii -o ~/.zsh/completion/_yii\n```\n\nДобавьте эту директорию в переменную среды `$fpath`, например добавив в конец `~/.zshrc` следующую строку:\n\n```\nfpath=(~/.zsh/completion $fpath)\n```\n\nУбедитесь, что программа `compinit` запущена. Если это не так - добавьте в `~/.zshrc` следующие строки:\n\n```\nautoload -Uz compinit && compinit -i\n```\n\nЗатем перезапустите ваш терминал, либо выполните команду\n\n```\nexec $SHELL -l\n```\n\nСоздание ваших собственных команд <span id=\"create-command\"></span>\n----------------------------------\n\n### Консольный контроллер и действие\n\nКонсольная команда определяется как класс контроллера расширяющий [[yii\\console\\Controller]]. В классе контроллера,\nвы определяете одно или несколько действий, которые соответствуют суб-командам контроллера. В каждом действии\nвы пишете код, который реализует соответствующие данной суб-команде задачи.\n\nПри запуске команды, вам необходимо указать маршрут к действию. Например, маршрут `migrate/create` вызывает суб-команду,\nкоторая соответствует методу [[yii\\console\\controllers\\MigrateController::actionCreate()|MigrateController::actionCreate()]].\nЕсли маршрут, предложенный при вызове команды, не содержит указания идентификатора действия, будет вызвано действие\nпо умолчанию (так же как и в веб приложении).\n\n### Опции\n\nДля переопределения [[yii\\console\\Controller::options()]] метода, вы можете указать опции, которые доступны в консольной\nкоманде (controller/actionID). Метод должен возвращать список публичных атрибутов класса. При запуске команды вы\nможете указать значение опций, используя синтаксис `--OptionName=OptionValue`. Это свяжет `OptionValue` с атрибутом\n`OptionName` класса контроллера.\n\nЕсли значение по умолчанию опции - это массив, то при установке этой опции, при выполнении команды, значение будет\nпреобразовано в массив путём разделения входящей строки по запятым.\n\n### Аргументы\n\nКроме опций, команда может получать аргументы. Аргументы будут переданы в качестве параметров в метод действия,\nсоответствующего запрошенной суб-команде. Первый аргумент соответствует первому параметру, второй соответственно второму,\nи так далее. Если переданных аргументов при вызове команды будет недостаточно, то параметрам будут назначены по\nумолчанию, если они определены. Если значения по умолчанию не определены, и не были переданы, команда завершит\nвыполнение с ошибкой.\n\nВы можете использовать указание типа `array`, чтобы указать, что аргумент должен рассматриваться как массив. Массив\nбудет сгенерирован путём разделения входной строки по запятым.\n\n### Псевдонимы опций\n\nНачиная с версии 2.0.8 в классе консольной команды  доступен метод [[yii\\console\\Controller::optionAliases()]],\nпозволяющий добавлять псевдонимы для опций.\n\nДля того, чтобы задать псевдоним, перекройте метод [[yii\\console\\Controller::optionAliases()]] в вашем контроллере:\n\n```php\nnamespace app\\commands;\n\nuse yii\\console\\Controller;\n\nclass HelloController extends Controller\n{\n    public $message;\n    \n    public function options($actionID)\n    {\n        return ['message'];\n    }\n    \n    public function optionAliases()\n    {\n        return ['m' => 'message'];\n    }\n    \n    public function actionIndex()\n    {\n        echo $this->message . \"\\n\";\n    }\n}\n```\n\nТеперь для запуска команды можно использовать следующий синтаксис:\n\n```\nyii hello -m=hello\n```\n\n\nСледующий пример показывает как описывать аргументы:\n\n```php\nclass ExampleController extends \\yii\\console\\Controller\n{\n    // Команда \"yii example/create test\" вызовет \"actionCreate('test')\"\n    public function actionCreate($name) { ... }\n\n    // Команда \"yii example/index city\" вызовет \"actionIndex('city', 'name')\"\n    // Команда \"yii example/index city id\" вызовет \"actionIndex('city', 'id')\"\n    public function actionIndex($category, $order = 'name') { ... }\n\n    // Команда \"yii example/add test\" вызовет \"actionAdd(['test'])\"\n    // Команда \"yii example/add test1,test2\" вызовет \"actionAdd(['test1', 'test2'])\"\n    public function actionAdd(array $name) { ... }\n}\n```\n\n\n### Код возврата\n\nПри разработке консольного приложения принято использовать код возврата. Принято, код `0` означает, что команда выполнилась\nудачно. Если команда вернула код больше нуля, то это говорит об ошибке. Номер, который был возвращён при ошибке,\nпотенциально может быть использован для поиска более детальной информации об ошибке.\nДля примера `1` может указывать на неизвестную ошибку, а все коды выше могут быть зарезервированы под специфичные\nошибки: ошибки ввода, повреждённые файлы, и что-то другое.\n\nДля того, чтобы ваша консольная команда возвращала код возврата, просто верните целое число в методе действия контроллера:\n\n```php\npublic function actionIndex()\n{\n    if (/* возникла проблема */) {\n        echo \"Возникла проблема!\\n\";\n        return 1;\n    }\n    // делаем что-нибудь\n    return 0;\n}\n```\n\nЕсть несколько предопределённых констант, которые вы можете использовать:\n\n- [[yii\\console\\ExitCode::OK|ExitCode::OK]] со значением `0`;\n- [[yii\\console\\ExitCode::UNSPECIFIED_ERROR|ExitCode::UNSPECIFIED_ERROR]] со значением `1`.\n\nХорошая практика, определять значимые для вашего контроллера константы в случае, если вы используете больше типов ошибок.\n\n### Форматирование и цвета\n\nКонсоль Yii поддерживает форматирование вывода, который автоматически деградирует до не форматированного, если это не поддерживается\nв терминале, где запускается команда.\n\nВывод форматированных строк прост. Вот как можно вывести некоторый жирный текст:\n\n```php\n$this->stdout(\"Hello?\\n\", Console::BOLD);\n```\n\nЕсли вам нужно собрать строку динамически объединяя несколько стилей, лучше использовать\n[[yii\\helpers\\Console::ansiFormat()|ansiFormat()]]:\n\n```php\n$name = $this->ansiFormat('Alex', Console::FG_YELLOW);\necho \"Hello, my name is $name.\";\n```\n"
  },
  {
    "path": "docs/guide-ru/tutorial-core-validators.md",
    "content": "Встроенные валидаторы\n===============\n\nYii предоставляет встроенный набор часто используемых валидаторов, расположенных, в первую очередь,\nв пространстве имен `yii\\validators`. Вместо того, чтобы использовать длинные имена классов валидаторов,\nвы можете использовать *псевдонимы*, чтобы указать на использование этих валидаторов.\nНапример, вы можете использовать псевдоним `required`, чтобы сослаться на класс [[yii\\validators\\RequiredValidator]]:\n\n```php\npublic function rules()\n{\n    return [\n        [['email', 'password'], 'required'],\n    ];\n}\n```\n\nВсе поддерживаемые псевдонимы валидаторов можно увидеть в свойстве [[yii\\validators\\Validator::builtInValidators]].\n\nНиже мы опишем основные способы использования и свойства всех встроенных валидаторов.\n\n## [[yii\\validators\\BooleanValidator|boolean]] <span id=\"boolean\"></span>\n\n```php\n[\n    // Проверяет 'selected' на равенство 0 или 1, без учета типа данных\n    ['selected', 'boolean'],\n\n    // Проверяет, что \"deleted\" - это тип данных boolean и содержит true или false\n    ['deleted', 'boolean', 'trueValue' => true, 'falseValue' => false, 'strict' => true],\n]\n```\n\nЭтот валидатор проверяет, что второе значение является *boolean*.\n\n- `trueValue`: значение, соответствующее `true`. По умолчанию - `'1'`.\n- `falseValue`: значение, соответствующее `false`. По умолчанию - `'0'`.\n- `strict`: должна ли проверка учитывать соответствие типов данных `trueValue` или `falseValue`. По умолчанию - `false`.\n\n> Note: Из-за того, что как правило данные, полученные из HTML-форм, представляются в виде строки, обычно вам стоит\nоставить свойство [[yii\\validators\\BooleanValidator::strict|strict]] равным `false`.\n\n\n## [[yii\\captcha\\CaptchaValidator|captcha]] <span id=\"captcha\"></span>\n\n```php\n[\n    ['verificationCode', 'captcha'],\n]\n```\nЭтот валидатор обычно используется вместе с [[yii\\captcha\\CaptchaAction]] и [[yii\\captcha\\Captcha]], чтобы\nубедиться, что данные в инпуте соответствуют верификационному коду, отображенному с помощью виджета\n[[yii\\captcha\\Captcha|CAPTCHA]].\n\n- `caseSensitive`: необходимо ли учитывать чувствительность к регистру при сравнении. По умолчанию - `false`.\n- `captchaAction`: [маршрут](structure-controllers.md#routes), соответствующий\n  [[yii\\captcha\\CaptchaAction|CAPTCHA action]], который рендерит изображение с *CAPTCHA*. По умолчанию - `'site/captcha'`.\n- `skipOnEmpty`: может ли валидация быть пропущена, если *input* пустой. По умолчанию - `false`,\n  что означает, что *input* обязателен.\n\n\n## [[yii\\validators\\CompareValidator|compare]] <span id=\"compare\"></span>\n\n```php\n[\n    // проверяет, является ли значение атрибута \"password\" таким же, как \"password_repeat\"\n    ['password', 'compare'],\n    \n    // то же, что и выше, но атрибут для сравнения указан явно\n    ['password', 'compare', 'compareAttribute' => 'password_repeat'],\n\n    // проверяет, что возраст больше или равен 30\n    ['age', 'compare', 'compareValue' => 30, 'operator' => '>=', 'type' => 'number'],\n]\n```\n\nЭтот валидатор сравнивает значение указанного атрибута с другим, чтобы удостовериться, что их отношение\nсоответствует описанному в свойстве `operator`.\n\n- `compareAttribute`: имя атрибута, с которым нужно сравнить значение. Когда валидатор используется\n  для проверки атрибута, значением по умолчанию для этого свойства будет имя этого же атрибута\n  с суффиксом `_repeat`. Например, если проверяющийся атрибут - `password`,\n  то значение свойства по умолчанию будет `password_repeat`.\n- `compareValue`: постоянное значение, с которым будут сравниваться входящие данные. Когда одновременно указаны\n   это свойство и `compareAttribute`, это свойство получит приоритет.\n- `operator`: оператор сравнения. По умолчанию `==`, что означает проверку на эквивалентность входящих данных и в\n  `compareAttribute`, и в `compareValue`. Поддерживаются следующие операторы:\n     * `==`: проверяет два значения на эквивалентность. Сравнение не учитывает тип данных.\n     * `===`: проверяет два значения на эквивалентность. Сравнение строгое и учитывает тип данных.\n     * `!=`: проверяет, что два значения не эквивалентны. Сравнение не учитывает тип данных.\n     * `!==`: проверяет, что два значения не эквивалентны. Сравнение строгое и учитывает тип данных.\n     * `>`: проверяет, что валидируемое значение больше, чем то, с которым происходит сравнение.\n     * `>=`: проверяет, что валидируемое значение больше или равно тому, с которым происходит сравнение.\n     * `<`: проверяет, что валидируемое значение меньше, чем то, с которым происходит сравнение.\n     * `<=`: проверяет, что валидируемое значение меньше или равно тому, с которым происходит сравнение.\n- `type`: По умолчанию при сравнении используется тип '[[yii\\validators\\CompareValidator::TYPE_STRING|string]]'. То есть\n   значения сравниваются побайтово. При сравнении чисел необходимо задать [[yii\\validators\\CompareValidator::$type|$type]]\n   как '[[yii\\validators\\CompareValidator::TYPE_NUMBER|number]]'.     \n\n\n## [[yii\\validators\\DateValidator|date]] <span id=\"date\"></span>\n\nВалидатор [[yii\\validators\\DateValidator|date]] можно использовать тремя способами:\n\n```php\n[\n    [['from', 'to'], 'date'],\n    [['from_datetime', 'to_datetime'], 'datetime'],\n    [['some_time'], 'time'],\n]\n```\n\nЭтот валидатор проверяет соответствие входящих данных форматам *date*, *time* или *datetime*.\nОпционально, он может конвертировать входящее значение в UNIX timestamp и сохранить в атрибуте,\nописанном здесь: [[yii\\validators\\DateValidator::timestampAttribute|timestampAttribute]].\n\n- `format`: формат даты/времени, согласно которому должна быть сделана проверка.\n   Значение может быть паттерном, описанным в [руководстве ICU](https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax)\n   или форматом PHP префиксированным `php:`. Формат должен приниматься классом `Datetime`. Подробнее о нём можно\n   прочитать в [руководстве PHP по date_create_from_format()](https://www.php.net/manual/ru/datetime.createfromformat.php).\n   Если значение не задано, используется `Yii::$app->formatter->dateFormat`.\n   Подробнее читайте в [[yii\\validators\\DateValidator::$format|документации по API]].\n- `timestampAttribute`: имя атрибута, которому данный валидатор может присваивать значение UNIX timestamp,\n   получаемое из входных даты и времени. Это может быть как тот же атрибут, что валидируется в данный момент. Если это так,\n   после валидации оригинальное значение будет перезаписано значеним timestamp.\n   Дополнительные примеры вы можете найти в разделе [\"Handling date input with the DatePicker\"](https://github.com/yiisoft/yii2-jui/blob/master/docs/guide/topics-date-picker.md).\n\n  Начиная с версии 2.0.4, для атрибута могут быть заданы формат и часовой пояс через\n  [[yii\\validators\\DateValidator::$timestampAttributeFormat|$timestampAttributeFormat]] и\n  [[yii\\validators\\DateValidator::$timestampAttributeTimeZone|$timestampAttributeTimeZone]] соответственно.\n  \n  При использовании `timestampAttribute`, входное значение будет конвертировано в unix timestamp, который по определению\n  содержит время в UTC. То есть будет произведено преобразование из [[yii\\validators\\DateValidator::timeZone|входного часового пояса]]\n  в UTC.\n\n- Начиная с версии 2.0.4 также можно задать [[yii\\validators\\DateValidator::$min|минимальное]] и\n  [[yii\\validators\\DateValidator::$max|максимальное]] значение timestamp.\n\nВ том случае, если ввод не обязателен, вам может понадобится добавить [фильтр значения по умолчанию](#default) в\nдополнение к валидатору даты для того, чтобы пустой ввод сохранялся как `null`. В противном случае вы можете получить\nдаты вроде `0000-00-00` в базе данных или `1970-01-01` в полях ввода виджета date picker.\n\n```php\n[\n    [['from_date', 'to_date'], 'default', 'value' => null],\n    [['from_date', 'to_date'], 'date'],\n],\n```\n\n\n## [[yii\\validators\\DefaultValueValidator|default]] <span id=\"default\"></span>\n\n```php\n[\n    // установить null для \"age\" в качестве значения по умолчанию\n    ['age', 'default', 'value' => null],\n\n    // установить \"USA\" в качестве значения по умолчанию для \"country\"\n    ['country', 'default', 'value' => 'USA'],\n\n    // установить в \"from\" и \"to\" дату 3 дня и 6 дней от сегодняшней, если они пустые\n    [['from', 'to'], 'default', 'value' => function ($model, $attribute) {\n        return date('Y-m-d', strtotime($attribute === 'to' ? '+3 days' : '+6 days'));\n    }],\n]\n```\n\nЭтот валидатор не проверяет данные. Вместо этого он присваивает значения по умолчанию проходящим проверку\nатрибутам, если они пусты.\n\n- `value`: значение по умолчанию или функция обратного вызова, которая возвращает значение по умолчанию,\n  которое будет присвоено проверяемому атрибуту, если он пустой. Функция обратного вызова должна выглядеть так:\n\n```php\nfunction foo($model, $attribute) {\n    // ... вычисление $value ...\n    return $value;\n}\n```\n\n> Info: Как определить, является значение пустым или нет, более подробно описано в отдельной статье\n  в секции [Пустые значения](input-validation.md#handling-empty-inputs).\n\n\n## [[yii\\validators\\NumberValidator|double]] <span id=\"double\"></span>\n\n```php\n[\n    // проверяет, является ли \"salary\" числом типа double\n    ['salary', 'double'],\n]\n```\n\nЭтот валидатор проверяет, что входящее значение является корректным *double* числом. Он идентичен\nвалидатору [number](#number). (Прим. пер.: корректным float числом).\n\n- `max`: верхний лимит (включительно) для значений. Если не установлен, значит, валидатор не будет проверять верхний лимит.\n- `min`: Нижний лимит (включительно) для значений. Если не установлен, валидатор не будет проверять нижний лимит.\n\n\n## [[yii\\validators\\EmailValidator|email]] <span id=\"email\"></span>\n\n```php\n[\n    // проверяет, что \"email\" - это корректный email-адрес\n    ['email', 'email'],\n]\n```\nВалидатор проверяет, что значение входящих данных является корректным email-адресом.\n\n- `allowName`: можно ли передавать в атрибут имя (пример: `John Smith <john.smith@example.com>`). По умолчанию - `false`.\n- `checkDNS`, проверяет, существует ли доменное имя для введенного адреса (и A, и MX запись).\n  Учтите, что проверка может закончится неудачей, что может быть вызвано временными проблемами с DNS, даже если\n  email-адрес корректен. По умолчанию - `false`.\n- `enableIDN`, нужно ли учитывать IDN (многоязычные доменные имена). По умолчанию - `false`. Учтите, что для использования\n  IDN-валидации вам нужно установить и включить PHP расширение `intl`, иначе будет выброшено исключение.\n\n\n## [[yii\\validators\\ExistValidator|exist]] <span id=\"exist\"></span>\n\n```php\n[\n    // a1 должно существовать в столбце, который представляется атрибутом \"a1\"\n    ['a1', 'exist'],\n\n    // a1 должно существовать, но его значение будет использовать a2 для проверки существования\n    ['a1', 'exist', 'targetAttribute' => 'a2'],\n\n    // и a1, и a2 должны существовать, в противном случае оба атрибута будут возвращать ошибку\n    [['a1', 'a2'], 'exist', 'targetAttribute' => ['a1', 'a2']],\n\n    // и a1, и a2 должны существовать, но только атрибут a1 будет возвращать ошибку\n    ['a1', 'exist', 'targetAttribute' => ['a1', 'a2']],\n\n    // a1 требует проверки существования a2 и a3 (используя значение a1)\n    ['a1', 'exist', 'targetAttribute' => ['a2', 'a1' => 'a3']],\n\n    // a1 должен существовать. Если a1 - массив, то каждый его элемент должен существовать\n    ['a1', 'exist', 'allowArray' => true],\n]\n```\n\nЭтот валидатор ищет входящие данные в столбце таблицы. Он работает только с атрибутами\nмодели [Active Record](db-active-record.md). Он поддерживает проверку и одного столбца, и нескольких.\n\n- `targetClass`: имя класса [Active Record](db-active-record.md), который должен быть использован для проверки\n  входящего значения. Если не установлен, будет использован класс текущей модели.\n- `targetAttribute`: имя атрибута в `targetClass` который должен быть использован для проверки существования\n  входящего значения. Если не установлен, будет использовано имя атрибута, который проверяется в данный момент.\n  Вы можете использовать массив для валидации нескольких столбцов одновременно. Значения массива являются атрибутами,\n  которые будут использованы для проверки существования, тогда как ключи массива будут являться атрибутами, чьи значения\n  будут проверены. Если ключ и значения одинаковы, вы можете указать только значение.\n- `filter`: дополнительный фильтр, который будет добавлен к запросу в базу данных для проверки на существование значения.\n  Это может быть строка или массив, представляющие дополнительные условия в запросе (подробнее о формате\n  значений запроса: [[yii\\db\\Query::where()]]), или анонимная функция с сигнатурой `function ($query)`,\n  где `$query` - это [[yii\\db\\Query|Query]] объект, который вы можете модифицировать в функции.\n- `allowArray`: разрешать ли значению быть массивом. По умолчанию - `false`. Если свойство установлено в `true`\n  и тип входящих данных - массив, тогда каждый его элемент должен существовать в соответствующем столбце таблицы.\n  Помните, что это свойство не может быть установлено в `true`, если вы валидируете несколько столбцов, передавая\n  их в `targetAttribute` как массив.\n\n\n## [[yii\\validators\\FileValidator|file]] <span id=\"file\"></span>\n\n```php\n[\n    // проверяет, что \"primaryImage\" - это загруженное изображение в формате PNG, JPG или GIF\n    // размер файла должен быть меньше 1MB\n    ['primaryImage', 'file', 'extensions' => ['png', 'jpg', 'gif'], 'maxSize' => 1024*1024],\n]\n```\n\nЭтот валидатор проверяет, что input является корректным загруженным файлом.\n\n- `extensions`: список имен расширений, которые допустимы для загрузки. Это также может быть или массив, или\n  строка, содержащая имена файловых расширений, разделенных пробелом или запятой (пр.: \"gif, jpg\").\n  Имя расширения не чувствительно к регистру. По умолчанию - `null`, что значит, что все имена файловых расширений\n  допустимы.\n- `mimeTypes`: список MIME-типов, которые допустимы для загрузки. Это может быть или массив, или строка,\n  содержащая MIME-типы файлов, разделенные пробелом или запятой (пример: \"image/jpeg, image/png\").\n  В именах MIME-типов допустимо использовать символ `*` для выбора группы mime-типов.\n  Например, `image/*` разрешит все типы, которы начинаются с `image/` (пример: `image/jpeg`, `image/png`).\n  Имена MIME-типов не чувствительны к регистру. По умолчанию - `null`, что значит, что допустимы все MIME-типы.\n  Более детальную информацию можно найти в [списке MIME-типов](https://ru.wikipedia.org/wiki/Список_MIME-типов).\n- `minSize`: минимальный размер файла в байтах, разрешенный для загрузки. По умолчанию - `null`, что значит, что нет\n  минимального лимита.\n- `maxSize`: максимальный размер файла в байтах, разрешенный для загрузки. По умолчанию - `null`, что значит, что нет\n  максимального лимита.\n- `maxFiles`: максимальное количество файлов, которое может быть передано в атрибут. По умолчанию 1, что значит, что\n  input должен быть файлом в единственном экземпляре. Если больше, чем 1, то атрибут должен быть массивом,\n  состоящим из не более, чем `maxFiles` загруженных файлов.\n- `checkExtensionByMimeType`: нужно ли проверять расширение файла исходя из его MIME-типа. Если они не соответствуют\n  друг другу, то файл будет считаться некорректным. По умолчанию - `true`, то есть проверка будет произведена.\n\n`FileValidator` используется вместе с [[yii\\web\\UploadedFile]]. Пожалуйста, посмотрите раздел\n[Загрузка файлов](input-file-upload.md) для более полного понимания загрузки и проверки файлов.\n\n\n## [[yii\\validators\\FilterValidator|filter]] <span id=\"filter\"></span>\n\n```php\n[\n    // обрезает пробелы вокруг \"username\" и \"email\"\n    [['username', 'email'], 'filter', 'filter' => 'trim', 'skipOnArray' => true],\n\n    // нормализует значение \"phone\"\n    ['phone', 'filter', 'filter' => function ($value) {\n        // нормализация значения происходит тут\n        return $value;\n    }],\n    \n    // нормализует значение \"phone\" используя функцию \"normalizePhone\"\n    ['phone', 'filter', 'filter' => [$this, 'normalizePhone']],\n        \n    public function normalizePhone($value) {\n        return $value;\n    }\n]\n```\n\nЭтот валидатор не проверяет данные. Вместо этого он применяет указанный фильтр к входящему значению и\nприсваивает результат применения фильтра атрибуту.\n\n- `filter`: PHP-callback, осуществляющий фильтрацию. Это может быть глобальная php функция, анонимная функция\n  и т.д. Функция должна выглядеть как `function ($value) { return $newValue; }`. Это свойство обязательно должно\n  быть установлено.\n- `skipOnArray`: нужно ли пропускать валидацию, если входящим значением является массив. По умолчанию - `false`.\n  Помните, что если фильтр не может принимать массив, вы должны установить это значение в `true`. Иначе могут\n  произойти различные ошибки PHP.\n\n> Трюк: Если вы хотите удалить пробелы вокруг значений атрибута, вы можете использовать валидатор [trim](#trim).\n\n## [[yii\\validators\\ImageValidator|image]] <span id=\"image\"></span>\n\n```php\n[\n    // проверяет, что \"primaryImage\" - это валидное изображение с указанными размерами\n    ['primaryImage', 'image', 'extensions' => 'png, jpg',\n        'minWidth' => 100, 'maxWidth' => 1000,\n        'minHeight' => 100, 'maxHeight' => 1000,\n    ],\n]\n```\n\nЭтот валидатор проверяет, что входящие данные являются корректным файлом изображения. Он расширяет [file](#file)\nвалидатор и наследует все его свойства. Кроме того, он поддерживает следующие дополнительные свойства, специфичные\nдля валидации изображений:\n\n- `minWidth`: минимальная ширина изображения. По умолчанию `null`, что значит, что нет нижнего лимита.\n- `maxWidth`: максимальная ширина изображения. По умолчанию `null`, что значит, что нет верхнего лимита.\n- `minHeight`: минимальная высота изображения. По умолчанию `null`, что значит, что нет нижнего лимита.\n- `maxHeight`: максимальная высота изображения. По умолчанию `null`, что значит, что нет верхнего лимита.\n\n\n## [[yii\\validators\\IpValidator|ip]] <span id=\"ip\"></span>\n```php\n[\n    // проверяет, что \"ip_address\" - это валидный IPv4 или IPv6 адрес\n    ['ip_address', 'ip'],\n\n\n    // проверяет, что \"ip_address\" - это валидный IPv6 адрес или подсеть,\n    // значение будет развернуто в формат полной записи IPv6 адреса\n    ['ip_address', 'ip', 'ipv4' => false, 'subnet' => null, 'expandIPv6' => true],\n\n    // проверяет, что \"ip_address\" - это валидный IPv4 или IPv6 адрес,\n    // разрешает использования символа отрацания `!`\n    ['ip_address', 'ip', 'negation' => true],\n]\n```\n\nЭтот валидатор проверяет, является ли входящее значение валидным IPv4/IPv6 адресом или подсетью.\nОн также может изменять значение атрибута, если включена нормализация или развертывание IPv6 адресов.\n\nВалидатор имеет такие свойства:\n\n- `ipv4`: может ли проверяемое значение быть IPv4 адрессом. По умолчанию `true`.\n- `ipv6`: может ли проверяемое значение быть IPv6 адрессом. По умолчанию `true`.\n- `subnet`: может ли проверяемое значение быть IP адресом с CIDR (подсетью), например `192.168.10.0/24`\n     * `true` - указание подсети обязательно;\n     * `false` - указание подсети запрещено;\n     * `null` - указание подсети возможно, но не обязательно.\n\n    По умолчанию `false`.\n- `normalize`: нормализировать ли проверяемый IP адрес без CIDR к IP адресу с наименьшим CIDR\n(32 для IPv4 или 128 для IPv6). Свойство действует только если `subnet` не установлен в `false`. Например:\n    * `10.0.1.5` будет приведен к `10.0.1.5/32`\n    * `2008:db0::1` будет приведен к `2008:db0::1/128`\n- `negation`: может ли проверяемое значение иметь символ отрицания `!` в начале, например `!192.168.0.1`.\nПо умолчанию `false`.\n- `expandIPv6`: разворачивать ли IPv6 адрес в формат полной записи.\nНапример, IPv6 адрес `2008:db0::1` будет развернут в `2008:0db0:0000:0000:0000:0000:0000:0001`. По умолчанию `false`.\n- `ranges`: массив IPv4 или IPv6 диапазонов, которые разрешены или запрещены.\n\n    Если свойство не установлено, все IP адреса разрешены. Иначе, правила будут проверяться последовательно до первого\nвхождения. IP адрес будет запрещен, если не подпадет ни под одно правило. Например:\n    ```php\n    [\n         'client_ip', 'ip', 'ranges' => [\n             '192.168.10.128'\n             '!192.168.10.0/24',\n             'any' // разрешает все остальные IP адреса\n         ]\n    ]\n    ```\nВ этом примере, доступ разрешен для всех IPv4 и IPv6 адресов кроме подсети `192.168.10.0/24`.\nIPv4 адрес `192.168.10.128` также разрешен, так как находится перед запрещающим правилом.\n- `networks`: массив псевдониимов, которые могут быть использованы в `ranges`. Формат массива:\n    * ключ - имя псевдонима\n    * значение - массив строк. Строка может быть как диапазоном адресов, так и другим псведонимом. Строка может иметь\n    символ отрицания `!` в начале (не зависит от свойства `negation`).\n\n    Следующие псевдонимы определены по умолчанию:\n    \n    * `*`: `any`\n    * `any`: `0.0.0.0/0, ::/0`\n    * `private`: `10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fd00::/8`\n    * `multicast`: `224.0.0.0/4, ff00::/8`\n    * `linklocal`: `169.254.0.0/16, fe80::/10`\n    * `localhost`: `127.0.0.0/8', ::1`\n    * `documentation`: `192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24, 2001:db8::/32`\n    * `system`: `multicast, linklocal, localhost, documentation`\n\n> Info: Этот валидатор стал доступным начиная с версии 2.0.7.\n\n\n## [[yii\\validators\\RangeValidator|in]] <span id=\"in\"></span>\n\n```php\n[\n    // проверяет, что значение \"level\" равно 1, 2 или 3\n    ['level', 'in', 'range' => [1, 2, 3]],\n]\n```\n\nЭтот валидатор проверяет, что входящее значение соответствует одному из значений, указанных в `range`.\n\n- `range`: список значений, с которыми будет сравниваться входящее значение.\n- `strict`: должно ли сравнение входящего значения со списком значений быть строгим (учитывать тип данных).\n  По умолчанию `false`.\n- `not`: должен ли результат проверки быть инвертирован. По умолчанию - `false`. Если свойство установлено в `true`,\n  валидатор проверяет, что входящее значение НЕ соответствует ни одному из значений, указанных в `range`.\n- `allowArray`: устанавливает, допустимо ли использовать массив в качестве входных данных. Если установлено в `true`\n  и входящие данные являются массивом, для каждого элемента входящего массива должно быть найдено соответствие в\n  `range`.\n\n\n## [[yii\\validators\\NumberValidator|integer]] <span id=\"integer\"></span>\n\n```php\n[\n    // проверяет \"age\" на то, что это integer значение\n    ['age', 'integer'],\n]\n```\n\nПроверяет, что входящее значение является integer значением.\n\n- `max`: верхний лимит (включительно) для числа. Если не установлено, валидатор не будет проверять верхний лимит.\n- `min`: нижний лимит (включительно) для числа. Если не установлено, валидатор не будет проверять нижний лимит.\n\n\n## [[yii\\validators\\RegularExpressionValidator|match]] <span id=\"match\"></span>\n\n```php\n[\n    // проверяет, что \"username\" начинается с буквы и содержит только буквенные символы,\n    // числовые символы и знак подчеркивания\n    ['username', 'match', 'pattern' => '/^[a-z]\\w*$/i']\n]\n```\n\nЭтот валидатор проверяет, что входящее значение совпадает с указанным регулярным выражением.\n\n- `pattern`: регулярное выражение, с которым должно совпадать входящее значение. Это свойство должно быть установлено,\n  иначе будет выброшено исключение.\n- `not`: инвертирует регулярное выражение. По умолчанию `false`, что значит, что валидация будет успешна,\n   только если входящее значение совпадают с шаблоном. Если установлено в `true`, валидация будет успешна,\n   только если входящее значение НЕ совпадает с шаблоном.\n\n\n## [[yii\\validators\\NumberValidator|number]] <span id=\"number\"></span>\n\n```php\n[\n    // проверяет, является ли \"salary\" числом\n    ['salary', 'number'],\n]\n```\n\nЭтот валидатор проверяет, являются ли входящие значения числовыми. Он эквивалентен валидатору [double](#double).\n\n- `max`: верхний лимит (включительно) для числа. Если не установлено, валидатор не будет проверять верхний лимит.\n- `min`: нижний лимит (включительно) для числа. Если не установлено, валидатор не будет проверять нижний лимит.\n\n\n## [[yii\\validators\\RequiredValidator|required]] <span id=\"required\"></span>\n\n```php\n[\n    // проверяет, являются ли \"username\" и \"password\" не пустыми\n    [['username', 'password'], 'required'],\n]\n```\n\nЭтот валидатор проверяет, являются ли входящие значения не пустыми.\n\n- `requiredValue`: желаемое значение, которому должны соответствовать проверяемые данные. Если не установлено,\n  это значит, что данные должны быть не пусты.\n- `strict`: учитывать или нет соответствие типу данных при валидации (строгое сравнение).\n  Если `requiredValue` не установлено, а это свойство установлено в `true`, валидатор проверит, что входящее значение\n  строго не соответствует `null`; если свойство установлено в `false`, валидатор будет проверять значение на пустоту с\n  приведением типов.\n  Если `requiredValue` установлено, сравнение между входящими данными и `requiredValue` будет также учитывать тип\n  данных, если это свойство установлено в `true`.\n\n> Info: как определить, является ли значение пустым или нет, подробнее рассказывается\n  в секции [Пустые значения](input-validation.md#handling-empty-inputs).\n\n\n## [[yii\\validators\\SafeValidator|safe]] <span id=\"safe\"></span>\n\n```php\n[\n    // обозначает \"description\" как safe атрибут\n    ['description', 'safe'],\n]\n```\n\nЭтот валидатор не проверяет данные. Вместо этого он указывает, что атрибут является\n[безопасным атрибутом](structure-models.md#safe-attributes).\n\n\n## [[yii\\validators\\StringValidator|string]] <span id=\"string\"></span>\n\n```php\n[\n    // проверяет, что \"username\" это строка с длиной от 4 до 24 символов\n    ['username', 'string', 'length' => [4, 24]],\n]\n```\n\nЭтот валидатор проверяет, что входящее значение - это корректная строка с указанной длиной.\n\n- `length`: описывает длину для строки, проходящей валидацию. Может быть определен следующими\n   способами:\n     * числом: точная длина, которой должна соответствовать строка;\n     * массив с одним элементом: минимальная длина входящей строки (напр.: `[8]`). Это перезапишет `min`.\n     * массив с двумя элементами: минимальная и максимальная длина входящей строки (напр.: `[8, 128]`).\n     Это перезапишет и `min`, и `max`.\n- `min`: минимальная длина входящей строки. Если не установлено, то не будет ограничения на минимальную длину.\n- `max`: максимальная длина входящей строки. Если не установлено, то не будет ограничения на максимальную длину.\n- `encoding`: кодировка входящей строки. Если не установлено, будет использовано значение из\n  [[yii\\base\\Application::charset|charset]], которое по умолчанию установлено в `UTF-8`.\n\n\n## [[yii\\validators\\FilterValidator|trim]] <span id=\"trim\"></span>\n\n```php\n[\n    // обрезает пробелы вокруг \"username\" и \"email\"\n    [['username', 'email'], 'trim'],\n]\n```\n\nЭтот валидатор не производит проверки данных. Вместо этого он будет обрезать пробелы вокруг входящих данных.\nПомните, что если входящие данные являются массивом, то они будут проигнорированы этим валидатором.\n\n\n## [[yii\\validators\\UniqueValidator|unique]] <span id=\"unique\"></span>\n\n```php\n[\n    // a1 должен быть уникальным в столбце, который представляет \"a1\" атрибут\n    ['a1', 'unique'],\n\n    // a1 должен быть уникальным, но для проверки на уникальность\n    // будет использован столбец a2\n    ['a1', 'unique', 'targetAttribute' => 'a2'],\n\n    // a1 и a2 вместе должны быть уникальны, и каждый из них\n    // будет получать сообщения об ошибке\n    [['a1', 'a2'], 'unique', 'targetAttribute' => ['a1', 'a2']],\n\n    // a1 и a2 вместе должны быть уникальны, но только a1 будет получать сообщение об ошибке\n    ['a1', 'unique', 'targetAttribute' => ['a1', 'a2']],\n\n    // a1 должен быть уникальным, что устанавливается проверкой уникальности a2 и a3\n    // (используя значение a1)\n    ['a1', 'unique', 'targetAttribute' => ['a2', 'a1' => 'a3']],\n]\n```\n\nЭтот валидатор проверяет входящие данные на уникальность в столбце таблицы. Он работает только с\nатрибутами модели [Active Record](db-active-record.md). Он поддерживает проверку либо одного столбца,\nлибо нескольких.\n\n- `targetClass`: имя класса [Active Record](db-active-record.md), который должен быть использован\n  для проверки значения во входящих данных. Если не установлен, будет использован класс модели, которая\n  в данный момент проходит проверку.\n- `targetAttribute`: имя атрибута в `targetClass`, который должен быть использован для проверки на\n  уникальность входящего значения. Если не установлено, будет использован атрибут, проверяемый\n  в данный момент.\n  Вы можете использовать массив для проверки нескольких столбцов таблицы на уникальность. Значения массива -\n  это атрибуты, которые будут использованы для валидации, а ключи массива - это атрибуты, которые предоставляют\n  данные для валидации. Если ключ и значение одинаковые, вы можете указать только значение.\n- `filter`: дополнительный фильтр, который можно присоединить к запросу в БД, чтобы использовать его при\n  проверке значения на уникальность. Это может быть строка или массив, представляющие дополнительные условия для запроса\n  (см. [[yii\\db\\Query::where()]] о формате условий в запросе), или анонимная функция вида `function ($query)`,\n  где `$query` это объект [[yii\\db\\Query|Query]], который вы можете изменить в функции.\n\n\n## [[yii\\validators\\UrlValidator|url]] <span id=\"url\"></span>\n\n```php\n[\n    // Проверяет, что \"website\" является корректным URL. Добавляет http:// к атрибуту \"website\".\n    // если у него нет URI схемы\n    ['website', 'url', 'defaultScheme' => 'http'],\n]\n```\n\nЭтот валидатор проверяет, что входящее значение является корректным URL.\n\n- `validSchemes`: массив с указанием на URI-схему, которая должна считаться корректной. По умолчанию\n  `['http', 'https']`, что означает, что и `http`, и `https` URI будут считаться корректными.\n- `defaultScheme`: схема URI, которая будет присоединена к входящим данным, если в них отсутствует URI-схема.\n  По умолчанию `null`, что значит, что входящие данные не будут изменены.\n- `enableIDN`: должна ли валидация учитывать IDN (интернационализованные доменные имена).\n  По умолчанию - `false`. Учтите, что для того, чтобы IDN валидация работала корректно, вы должны установить `intl`\n  PHP расширение, иначе будет выброшено исключение.\n\n> Note: Валидатор проверяет, что протокол и хост в URL являются корректными. Валидатор НЕ проверяет другие части URL\nи НЕ предназначен для защиты от XSS или любых других видов атак. Обратитесь к секции\n[Лучшие практики безопасности](security-best-practices.md) чтобы узнать больше о том, как предтвращать известные угрозы\nпри разработке приложений.\n"
  },
  {
    "path": "docs/guide-ru/tutorial-docker.md",
    "content": "Yii и Docker\n=============\n\nДля разработки и деплоя приложения Yii можно запускать в Docker-контейнерах. Контейнер - это легковесная изолированная виртуальная машина, которая пробрасывает свои сервисы на порты хоста, т.е. веб-сервер в контейнере на порту 80 доступен на порту 8888 вашего (локального) хоста.\n\nКонтейнеры решают множество проблем: одинаковые версии ПО на машине разработчика и сервере, быстрый деплой, имитация многосерверной архитектуры при разработке.\n\nПодробнее о Docker-контейнерах можно прочитать на [docker.com](https://www.docker.com/why-docker).\n\n## Требования\n\n- `docker`\n- `docker-compose`\n\nДля установки Docker перейдите на [страницу загрузки](https://www.docker.com/products/container-runtime).\n\n## Установка\n\nПосле установки команда `docker ps` должна вывести примерно следующее:\n\n```\nCONTAINER ID   IMAGE   COMMAND   CREATED   STATUS   PORTS\n```\n\nЭто значит, что демон Docker запущен и работает.\n\nТакже выполните `docker-compose version`, вывод должен быть похож на:\n\n```\ndocker-compose version 1.20.0, build unknown\ndocker-py version: 3.1.3\nCPython version: 3.6.4\nOpenSSL version: OpenSSL 1.1.0g  2 Nov 2017\n```\n\nС помощью Compose можно настраивать и управлять всеми сервисами, необходимыми приложению, - базами данных, кэшированием и т.д.\n\n## Ресурсы\n\n- Базовые PHP-образы для Yii: [yii2-docker](https://github.com/yiisoft/yii2-docker)\n- Поддержка Docker для [yii2-app-basic](https://github.com/yiisoft/yii2-app-basic#install-with-docker)\n- Поддержка Docker для [yii2-app-advanced](https://github.com/yiisoft/yii2-app-advanced/pull/347) в разработке\n\n## Использование\n\nОсновные команды Docker:\n\n    docker-compose up -d\n\nзапуск всех сервисов стека в фоновом режиме\n\n    docker-compose ps\n\nсписок запущенных сервисов\n\n    docker-compose logs -f\n\nнепрерывный просмотр логов всех сервисов\n\n    docker-compose stop\n\nкорректная остановка всех сервисов стека\n\n    docker-compose kill\n\nнемедленная остановка всех сервисов стека\n\n    docker-compose down -v\n\nостановка и удаление всех сервисов, **будьте осторожны с потерей данных при отсутствии host-volumes**\n\nЗапуск команд в контейнере:\n\n    docker-compose run --rm php composer install\n\nзапуск установки Composer в новом контейнере\n\n    docker-compose exec php bash\n\nзапуск bash в *работающем* сервисе `php`\n\n\n## Продвинутые темы\n\n### Тесты фреймворка Yii\n\nВы можете запустить тесты фреймворка Yii в Docker, как описано [здесь](https://github.com/yiisoft/yii2/blob/master/tests/README.md#dockerized-testing).\n\n### Инструменты администрирования баз данных\n\nПри использовании MySQL (`mysql`) можно добавить контейнер phpMyAdmin в стек следующим образом:\n\n```\n    phpmyadmin:\n        image: phpmyadmin/phpmyadmin\n        ports:\n            - '8888:80'\n        environment:\n            - PMA_ARBITRARY=1\n            - PMA_HOST=mysql\n        depends_on:\n            - mysql\n```\n"
  },
  {
    "path": "docs/guide-ru/tutorial-i18n.md",
    "content": "Интернационализация\n====================\n\n> Note: Этот раздел находится в разработке\n\nИнтернационализация (I18N) является частью процесса разработки приложения, которое может быть адаптировано для \nнескольких языков без изменения программной логики. Это особенно важно для веб-приложений, так как потенциальные\nпользователи могут приходить из разных стран.\n\nYii располагает несколькими средствами, призванными помочь с интернационализацией веб-приложения: [переводом \nсообщений](#message-translation), [форматированием чисел и дат](#date-number).\n\nЛокализация и языки <span id=\"locale-language\"></span>\n------------------------------------------------------\n\nВ Yii приложении определены два языка: [[yii\\base\\Application::$sourceLanguage|исходный язык]] и [[yii\\base\\Application::$language|язык перевода]].\n\nНа \"исходном языке\" написаны сообщения в коде приложения. Если мы определяем исходным языком английский, то\nв коде можно использовать конструкцию:\n\n```php\necho \\Yii::t('app', 'I am a message!');\n```\n\nЯзык перевода определяет, в каком виде будет отображаться текущая страница, т.е. на какой язык будут переведены\nоригинальные сообщения. Этот параметр определяется в конфигурации приложения:\n\n```php\nreturn [\n    'id' => 'applicationID',\n    'basePath' => dirname(__DIR__),\n    // ...\n    'language' => 'ru-RU', // <- здесь!\n    // ...\n]\n```\n\n> Tip: значение по умолчанию для [[yii\\base\\Application::$sourceLanguage|исходного языка]] — английский.\n\nВы можете установить значение текущего языка в самом приложении в соответствии с языком, который выбрал пользователь.\nЭто необходимо сделать до того, как будет сгенерирован какой-либо вывод, чтобы не возникло проблем с его \nкорректностью. Используйте простое переопределение свойства на нужное значение:\n\n```php\n\\Yii::$app->language = 'ru-RU';\n```\n\nФормат для установки языка/локали: `ll-CC`, где `ll` — это двух или трёхбуквенный код языка в нижнем регистре в \nсоответствии со стандартом [ISO-639](https://www.loc.gov/standards/iso639-2/), а `CC` — это код страны в соответствии\nсо стандартом [ISO-3166](https://ru.wikipedia.org/wiki/ISO_3166-1#Список_кодов_по_ISO_3166).\n\n> Note: больше информации о синтаксисе и концепции локалей можно получить в [документации проекта ICU](https://unicode-org.github.io/icu/userguide/locale/#the-locale-concept).\n\nПеревод сообщений <span id=\"message-translation\"></span>\n--------------------------------------------------------\n\nПеревод используется для локализации сообщений, которые будут выведены в приложении в соответствии с языком,\nкоторый выбрал пользователь.\n\nПо сути, Yii просто находит в файле с сообщениями на выбранном языке строку, соответствующую сообщению на исходном \nязыке приложения. Для перевода сообщений необходимо в самом приложении заключать их в метод [[Yii::t()]]. Первый \nаргумент метода — это категория, которая позволяет группировать сообщения по определённому признаку, а второй — само \nсообщение.\n\n```php\necho \\Yii::t('app', 'This is a string to translate!');\n```\n\nYii попытается загрузить файл перевода сообщений, соответствующий  текущему [[yii\\base\\Application::$language|языку приложения]]\nиз одного из источников, определённых в `i18n` [компонентах приложения](concept-components.md). Сообщения — это набор\nфайлов или база данных, которая содержит переведённые строки. Следующая конфигурация определяет, что сообщения \nдолжны браться из PHP-файлов:\n\n```php\n'components' => [\n    // ...\n    'i18n' => [\n        'translations' => [\n            'app*' => [\n                'class' => 'yii\\i18n\\PhpMessageSource',\n                //'basePath' => '@app/messages',\n                //'sourceLanguage' => 'en-US',\n                'fileMap' => [\n                    'app'       => 'app.php',\n                    'app/error' => 'error.php',\n                ],\n            ],\n        ],\n    ],\n],\n```\n\nВ приведённой конфигурации, `app*` — это шаблон, который определяет, какие категории обрабатываются источником. В нашем \nслучае мы обрабатываем все, что начинается с `app`. Файлы с сообщениями находятся в `@app/messages` (папке `messages`\nв вашем приложении). Массив [[yii\\i18n\\PhpMessageSource::fileMap|fileMap]] определяет, какой файл будет подключаться для\nопределённой категории. Если вы не хотите конфигурировать `fileMap`, можно положиться на соглашение, что название \nкатегории является именем файла. Например, категория `app/error` относится к файлу `app/error.php` в рамках [[yii\\i18n\\PhpMessageSource::basePath|basePath]].\n\nПереводя сообщение `\\Yii::t('app', 'This is a string to translate!')` при установленном языке приложения `ru-RU`, Yii\nсначала будет искать файл `@app/messages/ru-RU/app.php`, чтобы получить список доступных переводов. Если такого файла нет в папке\n`ru-RU`, Yii также попробует поискать в папке `ru` перед тем, как примет решение, что попытка перевода не удалась.\n\nКроме хранения в PHP-файлах (используя [[yii\\i18n\\PhpMessageSource|PhpMessageSource]]), Yii предоставляет ещё два\nкласса:\n\n- [[yii\\i18n\\GettextMessageSource]], использующий GNU Gettext для MO или PO файлов.\n- [[yii\\i18n\\DbMessageSource]], использующий базу данных.\n\n\n### Именованные указатели\n\nВы можете добавлять параметры в строку для перевода, которые в выводе будут заменены соответствующими значениями, \nзаключая параметр в фигурные скобки:\n\n```php\n$username = 'Alexander';\necho \\Yii::t('app', 'Hello, {username}!', [\n    'username' => $username,\n]);\n```\n\nОбратите внимание, что в операции присваивания фигурные скобки не используются.\n\n### Позиционные указатели \n\n```php\n$sum = 42;\necho \\Yii::t('app', 'Balance: {0}', $sum);\n```\n\n> Tip: старайтесь сохранять читаемость сообщений и избегать избыточного использования позиционных\nпараметров. Помните, что переводчик, скорее всего, будет располагать только файлом со строками и для него\nдолжно быть очевидно, на что будет заменён тот или иной указатель.\n\n### Указатели с расширенным форматированием\n\nЧтобы использовать расширенные возможности, вам необходимо установить и включить [PHP-расширение intl](https://www.php.net/manual/ru/intro.intl.php).\nПосле этого вам станет доступен расширенный синтаксис указателей, а также сокращённая запись `{placeholderName, argumentType}`,\nэквивалентная форме `{placeholderName, argumentType, argumentStyle}`, позволяющая определять стиль форматирования.\n\nПолная документация доступна на [сайте ICU](https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classMessageFormat.html), но далее в \nдокументации будут приведены примеры использования расширенных возможностей интернационализации.\n\n#### Числа\n\n```php\n$sum = 42;\necho \\Yii::t('app', 'Balance: {0, number}', $sum);\n```\n\nВы можете использовать один из встроенных форматов (`integer`, `currency`, `percent`):\n\n```php\n$sum = 42;\necho \\Yii::t('app', 'Balance: {0, number, currency}', $sum);\n```\n\nИли определить свой формат:\n\n```php\n$sum = 42;\necho \\Yii::t('app', 'Balance: {0, number, ,000,000000}', $sum);\n```\n\n[Описание форматирования](https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classDecimalFormat.html).\n\n#### Даты\n\n```php\necho \\Yii::t('app', 'Today is {0, date}', time());\n```\n\nВстроенные форматы — это `short`, `medium`, `long` и `full`:\n\n```php\necho \\Yii::t('app', 'Today is {0, date, short}', time());\n```\n\nИспользуя свой формат:\n\n```php\necho \\Yii::t('app', 'Today is {0, date,yyyy-MM-dd}', time());\n```\n\n[Описание форматирования](https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1SimpleDateFormat.html#details).\n\n#### Время\n\n```php\necho \\Yii::t('app', 'It is {0, time}', time());\n```\n\nВстроенные форматы — это `short`, `medium`, `long` и `full`:\n\n```php\necho \\Yii::t('app', 'It is {0, time, short}', time());\n```\n\nИспользуя свой формат:\n\n```php\necho \\Yii::t('app', 'It is {0, date,HH:mm}', time());\n```\n\n[Описание форматирования](https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1SimpleDateFormat.html#details).\n\n\n#### Числа прописью\n\n```php\necho \\Yii::t('app', 'Число {n,number} прописью: {n, spellout}', ['n' => 42]);\n```\n\n#### Порядковые числительные\n\n```php\necho \\Yii::t('app', 'Вы — {n, ordinal} посетитель!', ['n' => 42]);\n```\n\nВыведет сообщение \"Вы — 42-й посетитель!\".\n\n#### Продолжительность\n\n```php\necho \\Yii::t('app', 'Вы находитесь здесь уже {n, duration}', ['n' => 47]);\n```\n\nВыведет сообщение \"Вы находитесь здесь уже 47 сек.\".\n\n#### Множественное число\n\nВ каждом языке используется свой способ склонения порядковых числительных. Некоторые правила весьма сложны,\nтак что очень удобно, что использование функционала i18n не требует определения правил склонения. Требуется\nтолько указать формы склоняемого слова в различных ситуациях:\n\n```php\necho \\Yii::t(\n    'app',\n    'На диване {n, plural, =0{нет кошек} =1{лежит одна кошка} one{лежит # кошка} few{лежит # кошки} many{лежит # кошек} other{лежит # кошки}}!', \n    ['n' => 0]\n);\n```\n\nВыведет сообщение \"На диване нет кошек!\".\n\nВ данном правиле \n\n* `=0` означает ноль; \n* `=1` соответствует ровно `1`; \n* `one` — `21`, `31`, `41` и так далее; \n* `few` — от `2` до `4`, от `22` до `24` и так далее; \n* `many` — `0`, от `5` до `20`, от `25` до `30` и так далее; \n* `other` — для всех прочих чисел (например, дробных). \n* Решётка `#` заменяется на значение аргумента `n`. \n\nДля некоторых языков правила могут быть более простыми. Например, для английского будет достаточно указать:\n\n```php\necho \\Yii::t('app', 'There {n, plural, =0{are no cats} =1{is one cat} other{are # cats}}!', ['n' => 0]);\n```\n\nСледует помнить, что если вы используете указатель дважды, и в первый раз он используется как `plural`,\nвторой раз он должен быть использован как `number`, иначе вы получите ошибку \"Inconsistent types declared \nfor an argument: U_ARGUMENT_TYPE_MISMATCH\":\n\n```\nВ корзине: {count, number} {count, plural, one{товар} few{товара} other{товаров}}.\n```\n\nПодробная документация о формах склонений для различных языков доступна на сайте \n[unicode.org](https://cldr.unicode.org/index/cldr-spec/plural-rules).\n\n#### Вариации\n\nВы можете указывать критерии форматирования сообщений в зависимости от ключевых слов. Приведённый пример\nдемонстрирует возможность подстановки корректного рода в зависимости от параметра:\n\n```php\necho \\Yii::t('app', '{name} — {gender} и {gender, select, женщина{ей} мужчина{ему} other{ему}} нравится Yii!', [\n    'name'   => 'Василий',\n    'gender' => 'мужчина',\n]);\n```\n\nВыведет сообщение \"Василий — мужчина и ему нравится Yii!\".\n\nВ приведённом выражении, `мужчина` и `женщина` — это возможные варианты пола. На всякий случай, `other`\nобработает случай, если значение не совпадает с первыми двумя вариантами. Строки в скобках являются вторичными\nвыражениями и могут быть просто строкой или строкой, содержащей дополнительные указатели.\n\n### Определение перевода по умолчанию\n\nВы можете определить переводы, которые будут использованы как переводы по умолчанию для категорий, которые\nне попадают в другие переводы. Этот перевод должен быть помечен звёздочкой `*` и указан в конфигурации\nприложения как:\n\n```php\n// конфигурация i18n компонента\n\n'i18n' => [\n    'translations' => [\n        '*' => [\n            'class' => 'yii\\i18n\\PhpMessageSource'\n        ],\n    ],\n],\n```\n\nТеперь можно использовать категории без необходимости конфигурировать каждую из них, что похоже на \nспособ, которым была реализована поддержка интернационализации в Yii 1.1. Сообщения для категории будут\nзагружаться из файла с переводом по умолчанию из `basePath`, т.е. `@app/messages`:\n\n```php\necho Yii::t('not_specified_category', 'message from unspecified category');\n```\n\nСообщение будет загружено из файла `@app/messages/<LanguageCode>/not_specified_category.php`\n\n### Перевод сообщений модулей\n\nЕсли вы хотите перевести сообщения в модуле и при этом не сгружать их все в один файл, можете прибегнуть\nк следующему приёму:\n\n```php\n<?php\n\nnamespace app\\modules\\users;\n\nuse Yii;\n\nclass Module extends \\yii\\base\\Module\n{\n    public $controllerNamespace = 'app\\modules\\users\\controllers';\n\n    public function init()\n    {\n        parent::init();\n        $this->registerTranslations();\n    }\n\n    public function registerTranslations()\n    {\n        Yii::$app->i18n->translations['modules/users/*'] = [\n            'class'          => 'yii\\i18n\\PhpMessageSource',\n            'sourceLanguage' => 'en-US',\n            'basePath'       => '@app/modules/users/messages',\n            'fileMap'        => [\n                'modules/users/validation' => 'validation.php',\n                'modules/users/form'       => 'form.php',\n                ...\n            ],\n        ];\n    }\n\n    public static function t($category, $message, $params = [], $language = null)\n    {\n        return Yii::t('modules/users/' . $category, $message, $params, $language);\n    }\n\n}\n```\n\nВ приведённом примере мы использовали маску для поиска совпадений, и последующую фильтрацию по\nкатегориям для искомого файла. Вместо использования `fileMap`, вы можете прибегнуть к соглашению,\nчто имя категории совпадает с именем файла и писать `Module::t('validation', 'your custom validation message')` или\n`Module::t('form', 'some form label')` напрямую.\n\n### Перевод сообщений виджетов\n\nДля виджетов применимо такое же правило, как и для модулей:\n\n```php\n<?php\n\nnamespace app\\widgets\\menu;\n\nuse yii\\base\\Widget;\nuse Yii;\n\nclass Menu extends Widget\n{\n\n    public function init()\n    {\n        parent::init();\n        $this->registerTranslations();\n    }\n\n    public function registerTranslations()\n    {\n        $i18n = Yii::$app->i18n;\n        $i18n->translations['widgets/menu/*'] = [\n            'class'          => 'yii\\i18n\\PhpMessageSource',\n            'sourceLanguage' => 'en-US',\n            'basePath'       => '@app/widgets/menu/messages',\n            'fileMap'        => [\n                'widgets/menu/messages' => 'messages.php',\n            ],\n        ];\n    }\n\n    public function run()\n    {\n        echo $this->render('index');\n    }\n\n    public static function t($category, $message, $params = [], $language = null)\n    {\n        return Yii::t('widgets/menu/' . $category, $message, $params, $language);\n    }\n\n}\n```\nВместо использования `fileMap`, вы можете прибегнуть к соглашению, что имя категории совпадает с \nименем файла и писать `Menu::t('messages', 'new messages {messages}', ['{messages}' => 10])` напрямую.\n\n> Note: для виджетов вы можете использовать i18n представления. На них распространяются\n> те же правила, что и на контроллеры.\n\n\n### Перевод сообщений фреймворка\n\nYii поставляется с набором сообщений по умолчанию для ошибок валидации и некоторых других строк. Эти \nсообщения принадлежат категории `yii`. Если возникает необходимость изменить сообщения по умолчанию, \nпереопределите `i18n` [компонент приложения](concept-components.md):\n\n```php\n'i18n' => [\n    'translations' => [\n        'yii' => [\n            'class' => 'yii\\i18n\\PhpMessageSource',\n            'sourceLanguage' => 'en-US',\n            'basePath' => '@app/messages'\n        ],\n    ],\n],\n```\n\nПосле этого разместите изменённые строки в файле `@app/messages/<language>/yii.php`.\n\n### Обработка недостающих переводов\n\nЕсли в источнике перевода отсутствует необходимое сообщение, Yii отобразит исходное содержимое сообщения.\nДанное поведение тем оправданнее, чем вы более стремитесь писать в исходном коде понятный текст сообщений.\nТем не  менее, иногда этого недостаточно, и может потребоваться произвольная обработка возникшей ситуации, \nкогда источник не содержит искомой строки. Для этого следует использовать обработку события \n[[yii\\i18n\\MessageSource::EVENT_MISSING_TRANSLATION|missingTranslation]] компонента [[yii\\i18n\\MessageSource]].\n\nНапример, чтобы отметить все непереведённые строки, чтобы их было легче находить на странице, необходимо \nсоздать обработчик события. Изменим конфигурацию приложения:\n\n```php\n'components' => [\n    // ...\n    'i18n' => [\n        'translations' => [\n            'app*' => [\n                'class' => 'yii\\i18n\\PhpMessageSource',\n                'fileMap' => [\n                    'app' => 'app.php',\n                    'app/error' => 'error.php',\n                ],\n                'on missingTranslation' => ['app\\components\\TranslationEventHandler', 'handleMissingTranslation']\n            ],\n        ],\n    ],\n],\n```\n\nСоздадим обработчик события:\n\n```php\n<?php\n\nnamespace app\\components;\n\nuse yii\\i18n\\MissingTranslationEvent;\n\nclass TranslationEventHandler\n{\n    public static function handleMissingTranslation(MissingTranslationEvent $event) {\n        $event->translatedMessage = \"@MISSING: {$event->category}.{$event->message} FOR LANGUAGE {$event->language} @\";\n    }\n}\n```\n\nЕсли [[yii\\i18n\\MissingTranslationEvent::translatedMessage]] установлен как обработчик события, на странице будет\nвыведен соответствующий результат перевода.\n\n> Warning: каждый источник обрабатывает недостающие переводы самостоятельно. Если вы используете несколько разных\n> источников сообщений и хотите обрабатывать недостающие переводы одинаково для всех, назначьте соответствующий\n> обработчик события для каждого источника.\n\n\nИспользование команды `message` <span id=\"message-command\"></span>\n--------------------------------------------------------------\n\nПереводы могут храниться в [[yii\\i18n\\PhpMessageSource|PHP-файлах]], [[yii\\i18n\\GettextMessageSource|файлах .po]] или в [[yii\\i18n\\DbMessageSource|базе данных]]. См. соответствующие классы для дополнительных опций.\n\nПрежде всего, вам необходимо создать конфигурационный файл. Решите, где вы хотите хранить его и затем выполните команду\n\n```bash\n./yii message/config-template path/to/config.php\n```\n\nОткройте созданный файл и настройте параметры в соответствии со своими потребностями. Уделите особое внимание следующим параметрам:\n\n* `languages`: массив, содержащий языки, на которые ваше приложение должно быть переведено;\n* `messagePath`: путь для хранения файлов сообщений, который должен соответствовать параметру `basePath`, указанному в конфигурации компонента `i18n`.\n\nВы также можете использовать команду `./yii message/config`, чтобы динамически сгенерировать конфигурационный файл с указанными опциями с помощью командной строки.\nНапример, вы можете установить параметры `languages` и `messagePath` следующим образом:\n\n```bash\n./yii message/config --languages=de,ja --messagePath=messages path/to/config.php\n```\n\nЧтобы получить список доступных параметров, выполните следующую команду:\n\n```bash\n./yii help message/config\n```\n\nКак только вы закончили с файлом конфигурации, вы можете, наконец, извлечь свои сообщения с помощью команды:\n\n```bash\n./yii message path/to/config.php\n```\n\nТакже вы можете использовать параметры, чтобы динамически менять настройки извлечения.\n\nВ результате вы найдете свои файлы (если вы выбрали перевод с помощью файлов) в своей директории `messagePath`.\n\n\nПредставления <span id=\"view-translation\"></span>\n-------------------------------------------------\n\nВместо того, чтобы переводить сообщения так, как указано в предыдущем разделе, вы можете использовать `i18n` в ваших\nпредставлениях, чтобы обеспечить поддержку нескольких языков. Например, если существует представление `views/site/index.php`\nи для перевода его на русский язык необходимо отдельное представление, создайте папку `ru-RU` в папке с представлением\nтекущего контроллера или виджета и создайте файл для русского языка: `views/site/ru-RU/index.php`. Yii загрузит файл\nдля текущего языка, если он существует, или использует исходный `views/site/index.php`, если не сможет найти локализацию.\n\n> Note: если язык был определён, как `en-US` и соответствующих представлений не было найдено, Yii попробует\n> найти представления в папке `en` перед тем, как использовать исходные.\n\n\nФорматирование чисел и дат <span id=\"date-number\"></span>\n---------------------------------------------------------\n\nСм. описание [форматирования дат](output-formatting.md).\n\n\nНастройка PHP-окружения <span id=\"setup-environment\"></span>\n------------------------------------------------------------\n\nДля работы с большей частью функций интернационализации Yii использует [PHP-расширение intl](https://www.php.net/manual/ru/book.intl.php).\nНапример, это расширение используют классы, отвечающие за форматирование чисел и дат [[yii\\i18n\\Formatter]] и за форматирование\nстрок [[yii\\i18n\\MessageFormatter]]. Оба класса поддерживают базовый функционал даже в том случае, если расширение `intl` не\nустановлено. Однако этот запасной вариант более или менее будет работать только для сайтов на английском языке, хотя даже для\nних большая часть широких возможностей расширения `intl` не будет доступна, поэтому его установка настоятельно рекомендуется.\n\n[PHP-расширение intl](https://www.php.net/manual/ru/book.intl.php) основано на [библиотеке ICU](https://icu.unicode.org/), которая\nописывает правила форматирования для различных локалей. Поэтому следует помнить, что форматирование чисел и дат вместе с\nсинтаксисом форматирования может отличаться в зависимости от версии библиотеки ICU, которая была скомпилирована в вашем\nдистрибутиве PHP.\n\nЧтобы сайт работал одинаково во всех окружениях, рекомендуется устанавливать одинаковую версию расширения intl, при этом \nудостоверяясь, что везде используется одинаковая версия библиотеки ICU.\n\nЧтобы узнать, какая версия ICU используется текущим PHP интерпретатором, используйте следующий скрипт:\n\n```php\n<?php\necho \"PHP: \" . PHP_VERSION . \"\\n\";\necho \"ICU: \" . INTL_ICU_VERSION . \"\\n\";\n```\n\nЧтобы иметь доступ ко всем возможностям, описанным в документации, мы рекомендуем использовать ICU версии 49 или новее.\nВ более ранних версиях отсутствует указатель `#` в правилах склонений. На сайте <https://icu.unicode.org/download>\nвы можете ознакомиться со списком доступных версий ICU. Обратите внимание, что схема нумерации версий изменилась после\nверсии 4.8 и последовательность версий выглядит так: ICU 4.8, ICU 49, ICU 50, ICU 51 и так далее.\n\nИзвестные проблемы <span id=\"known-problems\"></span>\n----------------------------------------------------\n\n - В ICU версии 52.1 было испорчено форматирование множественных чисел (`plural`) в русском языке. Проблема решается обновлением ICU до версии 53.1 или старше.\n"
  },
  {
    "path": "docs/guide-ru/tutorial-mailing.md",
    "content": "Отправка почты\n=======\n\n> Note: Этот раздел находится в стадии разработки.\n\nYii позволяет оформлять и посылать E-mail сообщения. Однако, ядро фреимворка предоставляет только\nфункциональность оформления и основной интерфейс. Фактический механизм отправки почты должен быть предоставлен с помощью расширения, потому что различным проектам могут потребоваться различные реализации и обычно они зависят от внешних сервисов и бибилотек.\n\nДля наиболее распространенных ситуаций вы можете использовать официальное расширение [yii2-swiftmailer](https://github.com/yiisoft/yii2-swiftmailer).\n\n\nНастройка\n-------------\n\nНастройка почтового компонента зависит от расширения, которое вы выбрали.\nВ целом настройка вашего приложения должна выглядеть так:\n\n```php\nreturn [\n    //....\n    'components' => [\n        'mailer' => [\n            'class' => 'yii\\swiftmailer\\Mailer',\n        ],\n    ],\n];\n```\n\n\nОсновы использования\n-----------\n\nКогда 'mailer' компонент настроен, вы можете использовать следующий код, чтобы отправить почтовое сообщение:\n\n```php\nYii::$app->mailer->compose()\n    ->setFrom('from@domain.com')\n    ->setTo('to@domain.com')\n    ->setSubject('Тема сообщения')\n    ->setTextBody('Текст сообщения')\n    ->setHtmlBody('<b>текст сообщения в формате HTML</b>')\n    ->send();\n```\n\nВ показанном выше примере метод `compose()` создает экземпляр почтового сообщения, который затем заполняется и отправляется.\nВы можете использовать более сложную логику в этом процессе, если вам понадобится:\n\n```php\n$message = Yii::$app->mailer->compose();\nif (Yii::$app->user->isGuest) {\n    $message->setFrom('from@domain.com')\n} else {\n    $message->setFrom(Yii::$app->user->identity->email)\n}\n$message->setTo(Yii::$app->params['adminEmail'])\n    ->setSubject('Тема сообщения')\n    ->setTextBody('Текст сообщения')\n    ->send();\n```\n\n> Note: каждое 'mailer' расширение имеет два главных класса: 'Mailer' и 'Message'. 'Mailer' всегда знает имя класса и специфику 'Message'. Не пытайтесь создать экземпляр объекта 'Message' напрямую -\n  всегда используйте для этого метод `compose()`.\n\nВы также можете послать несколько сообщений за раз:\n\n```php\n$messages = [];\nforeach ($users as $user) {\n    $messages[] = Yii::$app->mailer->compose()\n        // ...\n        ->setTo($user->email);\n}\nYii::$app->mailer->sendMultiple($messages);\n```\n\nВ некоторых почтовых расширениях этот подход может быть полезен, так как использует одиночное сетевое сообщение.\n\n\nКомпоновка почтовых сообщений\n----------------------\n\nYii предоставляет возможность оформления содержания почтовых сообщений через специальные файлы виды.\nПо умолчанию эти файлы должны быть расположены в директории '@app/mail'.\n\nПример содержания почтового файла вида:\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\helpers\\Url;\n\n\n/**\n * @var \\yii\\web\\View $this view component instance\n * @var \\yii\\mail\\BaseMessage $message instance of newly created mail message\n */\n\n?>\n<h2>This message allows you to visit our site home page by one click</h2>\n<?= Html::a('Go to home page', Url::home('http')) ?>\n```\n\nДля того, чтобы оформить содержание сообщения через файл вида, просто передайте название файла вида в `compose()` метод:\n\n```php\nYii::$app->mailer->compose('home-link') // здесь устанавливается результат рендеринга вида в тело сообщения\n    ->setFrom('from@domain.com')\n    ->setTo('to@domain.com')\n    ->setSubject('Message subject')\n    ->send();\n```\n\nВы можете передать допольнительный параметр, относящийся к виду в `compose()` метод, который будет доступен внутри файла вида:\n\n```php\nYii::$app->mailer->compose('greetings', [\n    'user' => Yii::$app->user->identity,\n    'advertisement' => $adContent,\n]);\n```\n\nВы можете указать разные файлы видов для HTML и простого текста в содержании сообщения:\n\n```php\nYii::$app->mailer->compose([\n    'html' => 'contact-html',\n    'text' => 'contact-text',\n]);\n```\n\nЕсли вы укажете название вида как строку, результат рендеринга в теле сообщения будет использоваться как HTML, в то время как при обычном тексте в теле сообщения при компоновке будут удаляться все HTML теги.\n\nРезультат рендеринга вида может быть вставлен в макет (layout), который может быть установлен, используя [[yii\\mail\\BaseMailer::htmlLayout]]\nи [[yii\\mail\\BaseMailer::textLayout]]. Это будет работать аналогично макетам в обычном веб приложении.\nМакет может использовать CSS стили или другие общие элементы страниц для использования в сообщении:\n\n```php\n<?php\nuse yii\\helpers\\Html;\n\n/**\n * @var \\yii\\web\\View $this view component instance\n * @var \\yii\\mail\\MessageInterface $message the message being composed\n * @var string $content main view render result\n */\n?>\n<?php $this->beginPage() ?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n<head>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=<?= Yii::$app->charset ?>\" />\n    <style type=\"text/css\">\n        .heading {...}\n        .list {...}\n        .footer {...}\n    </style>\n    <?php $this->head() ?>\n</head>\n<body>\n    <?php $this->beginBody() ?>\n    <?= $content ?>\n    <div class=\"footer\">With kind regards, <?= Yii::$app->name ?> team</div>\n    <?php $this->endBody() ?>\n</body>\n</html>\n<?php $this->endPage() ?>\n```\n\n\nПрикрепление файлов\n---------------\n\nВы можете прикрепить вложения к сообщению с помощью методов `attach()` и `attachContent()`:\n\n```php\n$message = Yii::$app->mailer->compose();\n\n// Прикрепление файла из локальной файловой системы:\n$message->attach('/path/to/source/file.pdf');\n\n// Прикрепить файл на лету\n$message->attachContent('Attachment content', ['fileName' => 'attach.txt', 'contentType' => 'text/plain']);\n```\n\n\nВложение изображений\n----------------\n\nВы можете вставить изображения в содержание сообщения через `embed()` метод. Этот метод возвращает id прикрепленной картинки,\nкоторые должны быть доступны в 'img' тегах.\nЭтот метод легко использовать, когда сообщение составляется через файлы представления:\n\n```php\nYii::$app->mailer->compose('embed-email', ['imageFileName' => '/path/to/image.jpg'])\n    // ...\n    ->send();\n```\n\nВнутри файла представления вы можете использовать следующий код:\n\n```php\n<img src=\"<?= $message->embed($imageFileName); ?>\">\n```\n\n\nТестирование и отладка\n---------------------\n\nРазработчикам часто надо проверять, что почтовые сообщения отправляются из приложения, их содержание и так далее.\nТакая возможность предоставляется в Yii через `yii\\mail\\BaseMailer::useFileTransport`. Если это опция включена, то она принудительно сохраняет данные почтовых сообщений в локальный файл вместо его отправки. Эти файлы будут сохранены в директории\n`yii\\mail\\BaseMailer::fileTransportPath`, которая по умолчанию '@runtime/mail'.\n\n> Note: вы можете либо сохранить сообщения в файл, либо послать его фактическим получателям, но не используйте оба варианта одновременно.\n\nФайл почтового сообщения может быть открыт обычным текстовым редактором, также вы можете просматривать фактические заголовки сообщений, их содержание и так далее.\nЭтот механизм может понадобиться во время отладки приложения, либо прогонки unit тестов.\n\n> Note: содержание файла почтового сообщения формируется через `\\yii\\mail\\MessageInterface::toString()`, правда это зависит от почтового расширения, которое вы используете в своем приложении.\n\n\nСоздание вашего собственного решения\n-------------------------------\n\nДля того, чтобы создать свое собственное решение, вам надо будет создать два класса: одно для 'Mailer' и другое для 'Message'.\nВы можете использовать `yii\\mail\\BaseMailer` и `yii\\mail\\BaseMessage` как базовые классы для вашего решения. Эти классы уже содержат базовую логику, которая описана в этом руководстве. Однако, их использование не обязательно, достаточно унаследоваться от `yii\\mail\\MailerInterface` и `yii\\mail\\MessageInterface` интерфейсов.\nЗатем вам необходимо обеспечить выполнение всех абстрактных методов этих интерфейсов для построения вашего решения.\n"
  },
  {
    "path": "docs/guide-ru/tutorial-performance-tuning.md",
    "content": "Оптимизация производительности\n==================\n\nСуществует много факторов, влияющих на производительность веб-приложения. Какие-то относятся к окружению, какие-то \nк вашему коду, а какие-то к самому Yii. В этом разделе мы перечислим большинство из них и объясним, как можно улучшить \nпроизводительность приложения, регулируя эти факторы.\n\n\n## Оптимизация окружения PHP <span id=\"optimizing-php\"></span>\n\nХорошо сконфигурированное окружение PHP очень важно. Для получения максимальной производительности,\n\n- Используйте последнюю стабильную версию PHP. Мажорные релизы PHP могут принести значительные улучшения производительности.\n- Включите кеширование байткода в [Opcache](https://www.php.net/manual/ru/book.opcache.php) (PHP 5.5 и старше) или [APC](https://www.php.net/manual/ru/book.apc.php) \n  (PHP 5.4 и более ранние версии). Кеширование байткода позволяет избежать затрат времени на обработку и подключение PHP \n  скриптов при каждом входящем запросе.\n\n## Отключение режима отладки <span id=\"disable-debug\"></span>\n\nПри запуске приложения в производственном режиме, вам нужно отключить режим отладки. Yii использует значение константы\n`YII_DEBUG` чтобы указать, следует ли включить режим отладки. Когда режим отладки включен, Yii тратит дополнительное \nвремя, чтобы создать и записать отладочную информацию.\n\nВы можете разместить следующую строку кода в начале [входного скрипта](structure-entry-scripts.md) чтобы \nотключить режим отладки:\n\n```php\ndefined('YII_DEBUG') or define('YII_DEBUG', false);\n```\n\n> Info: Значение по умолчанию для константы `YII_DEBUG` — `false`. \nТак что, если вы уверены, что не изменяете значение по умолчанию где-то в коде приложения, можете просто удалить эту \nстроку, чтобы отключить режим отладки.\n  \n\n## Использование техник кеширования <span id=\"using-caching\"></span>\n\nВы можете использовать различные техники кеширования чтобы значительно улучшить производительность вашего приложения. \nНапример, если ваше приложение позволяет пользователям вводить текст в формате Markdown, вы можете закешировать \nразобранное содержимого Markdown, чтобы избежать разбора одной и той же разметки Markdown неоднократно \nпри каждом запросе. Пожалуйста, обратитесь к разделу [Кеширование](caching-overview.md) чтобы узнать о поддержке \nкеширования, которую предоставляет Yii.\n\n\n## Включение кеширования схемы <span id=\"enable-schema-caching\"></span>\n\nКэширование схемы - это специальный *тип кеширования*, который должен быть включен при использовании [Active Record](db-active-record.md).\nКак вы знаете, Active Record достаточно умен, чтобы обнаружить информацию о схеме (например, имена столбцов, типы столбцов, \nограничения) таблицы БД без необходимости описывать ее вручную. Active Record получает эту информацию, выполняя \nдополнительные SQL запросы. При включении кэширования схемы, полученная информация о схеме будет сохранена в кэше и \nповторно использована при последующих запросах.\n\nЧтобы включить кеширование схемы, сконфигурируйте [компонент приложения](structure-application-components.md) `cache` \nдля хранения информации о схеме и установите [[yii\\db\\Connection::enableSchemaCache]] в `true` в [конфигурации приложения](concept-configurations.md):\n\n```php\nreturn [\n    // ...\n    'components' => [\n        // ...\n        'cache' => [\n            'class' => 'yii\\caching\\FileCache',\n        ],\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=mydatabase',\n            'username' => 'root',\n            'password' => '',\n            'enableSchemaCache' => true,\n\n            // Продолжительность кеширования схемы.\n            'schemaCacheDuration' => 3600,\n\n            // Название компонента кеша, используемого для хранения информации о схеме\n            'schemaCache' => 'cache',\n        ],\n    ],\n];\n```\n\n\n## Объединение и минимизация ресурсов <span id=\"optimizing-assets\"></span>\n\nСложные веб-страницы часто подключают много CSS и/или JavaScript файлов. Для уменьшения числа HTTP запросов\nи общего размера загрузки этих ресурсов, вы должны рассмотреть вопрос об их объединении в один файл и его сжатии.\nЭто может сильно увеличить скорость загрузки страницы и снизить нагрузку на сервер. Для получения более подробной\nинформации обратитесь, пожалуйста, к разделу [Ресурсы](structure-assets.md)\n\n\n## Оптимизация хранилища сессий <span id=\"optimizing-session\"></span>\n\nПо умолчанию данные сессий хранятся в файлах. Это удобно для разработки или в маленьких проектах.\nНо когда дело доходит до обработки множества параллельных запросов, то лучше использовать более сложные хранилища, \nтакие как базы данных. Yii поддерживает различные хранилища \"из коробки\". \nВы можете использовать эти хранилища, сконфигурировав компонент `session` в\n[конфигурации приложения](concept-configurations.md) как показано ниже,\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'session' => [\n            'class' => 'yii\\web\\DbSession',\n\n            // Установите следующее, если вы хотите использовать компонент БД, с названием\n            // отличным от значения по умолчанию 'db'.\n            // 'db' => 'mydb',\n\n            // Чтобы перезаписать таблицу сессий, заданную по умолчанию, установите\n            // 'sessionTable' => 'my_session',\n        ],\n    ],\n];\n```\n\nПриведенная выше конфигурация использует таблицу базы данных для хранения сессионных данных. По умолчанию, используется \nкомпонент приложения `db` для подключения к базе данных и сохранения сессионных данных в таблице `session`. Вам нужно будет \nсоздать таблицу `session` заранее:\n\n```sql\nCREATE TABLE session (\n    id CHAR(40) NOT NULL PRIMARY KEY,\n    expire INTEGER,\n    data BLOB\n)\n```\n\nВы также можете хранить сессионные данные в кеше с помощью [[yii\\web\\CacheSession]]. Теоретически, вы можете использовать \nлюбое поддерживаемое [хранилище кеша](caching-data.md#supported-cache-storage). Тем не менее, помните, что некоторые \nхранилища кеша могут *сбрасывать* закешированные данные при достижении лимитов хранилища. По этой причине, вы должны в \nосновном использовать хранилища кеша, которые не имеют таких лимитов.\n\nЕсли на вашем сервере установлен [Redis](https://redis.io/), настоятельно рекомендуется выбрать его в качестве \nхранилища сессий используя [[yii\\redis\\Session]].\n\n\n## Оптимизация базы данных <span id=\"optimizing-databases\"></span>\n\nВыполнение запросов к БД и выборки данных часто являются узким местом производительности веб-приложения. \nХотя использование техник [кэширования данных](caching-data.md) может *смягчить* снижение производительности, оно не \nрешает проблему полностью. Когда база данных содержит огромное количество данных, и данные в кэше невалидны, получение \nсвежих данных без правильного проектирования БД и запросов может быть чрезмерно ресурсоемкой операцией.\n\nОбщей методикой для повышения производительности запросов к БД является создание индексов для тех столбцов таблицы, по которым делается выборка.\nНапример, если вам нужно найти запись о пользователе по `username`, вам надо создать индекс на `username`. \nОбратите внимание, что в то время как индексирование может сделать SELECT запросы намного быстрее, оно будет замедлять INSERT, UPDATE и DELETE запросы.\n\nДля сложных запросов к БД рекомендуется создавать представления базы данных (views), чтобы сэкономить время подготовки и разбора запросов.\n\nПоследнее, хотя и не менее важное: используйте `LIMIT` в ваших `SELECT` запросах. Это позволяет избежать извлечения \nбольшого количество данных из базы данных и исчерпания памяти, выделенной для PHP.\n\n\n## Использование обычных массивов <span id=\"using-arrays\"></span>\n\nХотя [Active Record](db-active-record.md) очень удобно использовать, это не так эффективно, как использование простых \nмассивов, когда вам нужно получить большое количество данных из БД. В этом случае вы можете вызвать `asArray()` при \nиспользовании Active Record для получения данных, чтобы извлеченные данные были представлены в виде массивов вместо \nгромоздких записей Active Record. Например,\n\n```php\nclass PostController extends Controller\n{\n    public function actionIndex()\n    {\n        $posts = Post::find()->limit(100)->asArray()->all();\n        \n        return $this->render('index', ['posts' => $posts]);\n    }\n}\n```\n\nВ приведенном выше коде, `$posts` будет заполнего массивом строк из таблицы. Каждая строка - это обычный массив. Чтобы \nполучить доступ к столбцу `title` в i-й строке, вы можете использовать выражение `$posts[$i]['title']`.\n\nВы также можете использовать [DAO](db-dao.md) для создания запросов и извлечения данных в виде обычных массивов. \n\n\n## Оптимизация автозагрузчика Composer <span id=\"optimizing-autoloader\"></span>\n\n\nПоскольку автозагрузчик Composer'а используется для подключения большого количества файлов сторонних классов, вы должны \nоптимизировать его, выполнив следующую команду:\n\n```\ncomposer dumpautoload -o\n```\n\n\n## Асинхронная обработка данных <span id=\"processing-data-offline\"></span>\n\nКогда запрос включает в себя некоторые ресурсоёмкие операции, вы должны подумать о том, чтобы выполнить эти операции \nасинхронно, не заставляя пользователя ожидать их окончания.\n\nСуществует два метода асинхронной обработки данных: pull и push. \n\nВ методе pull, всякий раз, когда запрос включает в себя некоторые сложные операции, вы создаете задачу и сохраняете ее в \nпостоянном хранилище, таком как база данных. Затем в отдельном процессе (таком как задание cron) получаете эту задачу и \nобрабатываете ее.\n\nЭтот метод легко реализовать, но у него есть некоторые недостатки. Например, задачи надо периодически забирать из \nместа их хранения. Если делать это слишком редко, задачи будут обрабатываться с большой задержкой, а если слишком часто - \nэто будет создавать большие накладные расходы.\n\nВ методе push, вы можете использовать очереди сообщений (например, RabbitMQ, ActiveMQ, Amazon SQS, и т.д.) для управления задачами.\nВсякий раз, когда новая задача попадает в очередь, это инициирует обработку этой задачи обработчиком.\n\n\n## Профилирование производительности <span id=\"performance-profiling\"></span>\n\nВы должны профилировать код, чтобы определить узкие места в производительности и принять соответствующие меры.\nСледующие инструменты для профилирования могут оказаться полезными:\n\n- [Отладочный тулбар Yii и отладчик](https://github.com/yiisoft/yii2-debug/blob/master/docs/guide/README.md)\n- [Профайлер XDebug](https://xdebug.org/docs/profiler)\n- [XHProf](https://www.php.net/manual/ru/book.xhprof.php)\n"
  },
  {
    "path": "docs/guide-ru/tutorial-shared-hosting.md",
    "content": "Окружение виртуального хостинга\n==========================\n\nЗачастую окружение виртуальных хостингов весьма ограничено как в настройках конфигурации, так и в настройках структуры директорий. В большинстве случаев, однако, возможно запустить Yii 2 на виртуальном хостинге, внеся некоторые корректировки.\n\nУстановка приложения Basic.\n---------------------------\n\nПоскольку на виртуальном хостинге обычно только один webroot, то лучше использовать шаблонное приложение Basic. Прочитайте раздел [Установка Yii](start-installation.md) и локально установите приложение. После того как оно начнет работать, можно внести необходимые корректировки, которые помогут разместить Basic на виртуальном хостинге.\n\n### Переименование webroot <span id=\"renaming-webroot\"></span>\n\nПодключитесь к вашему виртуальному хостингу, используя FTP или другой способ. Скорее всего вы увидите следующее: \n \n```\nconfig\nlogs\nwww\n```\n\nВ приведенном выше описании `www` - это webroot директория веб-сервера. Она может называться по-другому. Возможные названия: `www`, `htdocs` или `public_html`.\n\nВ Basic webroot называется `web`. Перед загрузкой своего приложения на виртуальный хостинг, переименуйте локальный webroot на название webroot виртуального хостинга. Например, `web` в `www` или `public_html`, в зависимости от наименования webroot вашего хостинга.\n\n### Корневая директория FTP доступна для записи\n\nЕсли вы можете записать в корневую директорию, где располагаются `config`, `logs` и `www`, то загрузите сюда же `assets`, `commands` и остальные директории, так же, как и у вас, локально.\n\n### Добавим настройки для веб-сервера <span id=\"add-extras-for-webserver\"></span>\n\nВ случае, если ваш сервер Apache, добавьте в директорию `web` или аналогичную, где располагается `index.php`, файл `.htaccess` со следующим содержимым:\n\n```\nOptions +FollowSymLinks\nIndexIgnore */*\n\nRewriteEngine on\n\n# if a directory or a file exists, use it directly\nRewriteCond %{REQUEST_FILENAME} !-f\nRewriteCond %{REQUEST_FILENAME} !-d\n\n# otherwise forward it to index.php\nRewriteRule . index.php\n```\n\nВ случае использования nginx не требуется каких-либо дополнительных настроек.\n\n### Проверка требований\n\nДля того чтобы запустить Yii, ваш веб-сервер должен соответствовать его требованиям. Минимальное требование к PHP - это его версия 5.4. Для того чтобы проверить требования, скопируйте `requirements.php` из корневого каталога в каталог webroot и запустите его с помощью браузера, используя url `https://example.com/requirements.php`. Не забудьте после проверки требований удалить файл `requirements.php`.\n\nУстановка шаблона приложения Advanced\n---------------------------------\n\nУстановка шаблона Advanced немного сложнее, чем установка Basic, из-за того, что в Advanced имеются две директории webroot, работа с которыми на виртуальном хостинге не поддерживается. По этой причине нам потребуется внести изменения в структуру директорий.\n\n### Перемещение входных скриптов в одну директорию webroot\n\nДля начала нам необходима директория webroot. Создайте новую директорию и назовите её так же, как на виртуальном хостинге, например, `www` или `public_html`, как описывалось выше в разделе [Переименование webroot](#renaming-webroot). Затем создайте следующую структуру в `www`:\n\n```\nwww\n    admin\nbackend\ncommon\nconsole\nenvironments\nfrontend\n...\n```\n\nНашей фронтенд директорией будет `www`. Переместите в неё всё из `frontend/web`. Так же поступите и для `backend/web`, скопировав всё в `www/admin`. В каждом случае нужно настроить пути внутри файлов `index.php` и `index-test.php`.\n\n### Отдельные сессии и куки\n  \nИзначально подразумевалось, что приложения бекенд и фронтенд располагаются на разных доменах. Теперь, когда мы перенесли всё на один домен, куки и сессии из бекенда и фронтенда стали пересекаться. Для решения этой проблемы требуется внести следующие настройки в конфигурацию бекенд-приложения `backend/config/main.php`:\n\n```php\n'components' => [\n    'request' => [\n        'csrfParam' => '_backendCSRF',\n        'csrfCookie' => [\n            'httpOnly' => true,\n            'path' => '/admin',\n        ],\n    ],\n    'user' => [\n        'identityCookie' => [\n            'name' => '_backendIdentity',\n            'path' => '/admin',\n            'httpOnly' => true,\n        ],\n    ],\n    'session' => [\n        'name' => 'BACKENDSESSID',\n        'cookieParams' => [\n            'path' => '/admin',\n        ],\n    ],\n],\n```\n"
  },
  {
    "path": "docs/guide-ru/tutorial-start-from-scratch.md",
    "content": "Создание своей структуры приложения\n=======================================\n\n> Note: Этот раздел находится на стадии разработки.\n\nПока шаблоны проектов [basic](https://github.com/yiisoft/yii2-app-basic) и [advanced](https://github.com/yiisoft/yii2-app-advanced) великолепно справляются с большинством ваших потребностей, но вы можете захотеть создать свой собственный шаблон проекта, с которого будете начинать делать ваши проекты.\n\nШаблоны проектов в Yii - это просто репозитарии, содержащие `composer.json` файл, и зарегистрированные как Composer пакет.\nЛюбые репозитарии, которые могут быть определены как Composer пакеты, становятся установочными через Composer команду `create-project`.\n\nЧтобы построить весь свой шаблон с нуля, нужно затратить много энергии, поэтому лучше использовать один из встроенных шаблонов, как базовый.\n\nКлонирование базового шаблона\n----------------------------------------\n\nПервый шаг для клонирования базового Yii шаблона из Git репозитария:\n\n```bash\ngit clone git@github.com:yiisoft/yii2-app-basic.git\n```\n\nЗатем необходимо подождать, чтобы репозитарий загрузился на ваш компьютер. Поскольку измененный шаблон не должен быть \"запушен\" (push) обратно, вы можете удалить из загруженного директорию `.git` со всем её содержимым.\n\nИзмените файлы\n------------\n\nСледующее, вам надо изменить `composer.json` в соответствии с вашим шаблоном. Измените значения `name`(имя), `description`(описание), `keywords`(ключевые слова), `homepage`(адрес домашней страницы), `license`(лицензия), и `support`(поддержка)\nдля описания вашего нового шаблона. Также установите `require`(зависимости фреимворка), `require-dev`(зависимости от расширений), `suggest`, и другие опции, которые необходимы для вашего шаблона.\n\n> Note: В файле `composer.json` используйте `writable` параметр внутри `extra`, чтобы указать\n> права доступа к файлам, которые будут установлены, после создания приложения на основе данного шаблона.\n\nСледующее, измените структуру и содержание приложения, по вашему вкусу. В заключении обновите README файл, чтобы он соответствовал конечному варианту вашего шаблона.\n\nСоздание пакета\n--------------\n\nСоздайте Git репозитарий из созданного шаблона, и запушьте(push) его. Если вы собираетесь сделать ваш шаблон с открытым исходным кодом, [Github](https://github.com) - это лучшее место, чтобы разместить его. Если вы собираетесь сохранить ваш шаблон для личных целей, используйте любой Git, предназначенный для этих целей.\n\nЗатем, вам необходимо зарегистрировать ваш пакет в Composer. Пакет с публичным шаблоном должен быть зарегистрирован на [Packagist](https://packagist.org/).\nДля приватных шаблонов, зарегистрировать шаблона немного сложнее. Для получения инструкции загляните в [Composer documentation](https://getcomposer.org/doc/05-repositories.md#hosting-your-own).\n\nИспользование шаблона\n------\n\nЭто все, что требуется для создания нового шаблона проекта в Yii. Сейчас вы можете создавать проекты, использующие ваш шаблон:\n\n```\ncomposer global require \"fxp/composer-asset-plugin:^1.4.1\"\ncomposer create-project --prefer-dist --stability=dev mysoft/yii2-app-coolone new-project\n```\n"
  },
  {
    "path": "docs/guide-ru/tutorial-template-engines.md",
    "content": "Использование шаблонизаторов\n======================\n\nПо умолчанию, Yii использует PHP в шаблонах, но вы можете настроить Yii на поддержку других шаблонизаторов, таких как\n[Twig](https://twig.symfony.com/) или [Smarty](https://www.smarty.net/), которые доступны в расширениях.\n\nКомпонент `view`, отвественный за генерацию видов. Вы можете добавить шаблонизатор с помощью перенастройки поведения компонента:\n\n```php\n[\n    'components' => [\n        'view' => [\n            'class' => 'yii\\web\\View',\n            'renderers' => [\n                'tpl' => [\n                    'class' => 'yii\\smarty\\ViewRenderer',\n                    //'cachePath' => '@runtime/Smarty/cache',\n                ],\n                'twig' => [\n                    'class' => 'yii\\twig\\ViewRenderer',\n                    'cachePath' => '@runtime/Twig/cache',\n                    // Массив опций twig:\n                    'options' => [\n                        'auto_reload' => true,\n                    ],\n                    'globals' => ['html' => '\\yii\\helpers\\Html'],\n                    'uses' => ['yii\\bootstrap'],\n                ],\n                // ...\n            ],\n        ],\n    ],\n]\n```\n\nВ коде, показанном выше, оба шаблонизатора Smarty и Twig настроены, чтобы использоваться в файле вида. Но чтобы добавить эти расширения в ваш проект, вам необходимо также изменить ваш `composer.json` файл. Добавить в него:\n\n```\n\"yiisoft/yii2-smarty\": \"~2.0.0\",\n\"yiisoft/yii2-twig\": \"~2.0.0\",\n```\nЭто код вставляется в секцию `require` файла `composer.json`. После изменения и сохранения этого файла, вы можете установить расширение, запустив `composer update --prefer-dist` в командной строке.\n\nДля получения подробной информации об использовании конкретного шаблонизатора обратитесь в их документации:\n\n- [Руководство по Twig (англ.)](https://github.com/yiisoft/yii2-twig/tree/master/docs/guide)\n- [Руководство Smarty (англ.)](https://github.com/yiisoft/yii2-smarty/tree/master/docs/guide)\n"
  },
  {
    "path": "docs/guide-ru/tutorial-yii-as-micro-framework.md",
    "content": "# Использование Yii в качестве микро-framework'а\n\nYii можно легко использовать без функций, включенных в базовый и расширенный шаблоны приложений. Другими словами Yii уже является микро-каркасом. Не требуется иметь структуру каталогов предоставляемую этими шаблонами при работе с Yii.\n\nЭто особенно удобно, когда Вам не нужен весь пред-установленный шаблонный код, такой как `Assets` или `Views`. Одним из таких случаев является создание JSON API. В следующих разделах будет показано, как это сделать.\n\n## Установка Yii\n\nСоздайте каталог для файлов проекта и смените рабочий каталог на этот путь. В примерах используются команды Unix, но аналогичные команды существуют и в Windows.\n\n```bash\nmkdir micro-app\ncd micro-app\n```\n\n> Note: Для продолжения требуется немного знаний о Composer. Если Вы еще не знаете, как использовать Composer, пожалуйста, найдите время, чтобы прочитать [Руководство Composer](https://getcomposer.org/doc/00-intro.md).\n\nСоздайте файл `composer.json` в каталоге `micro-app` с помощью Вашего любимого редактора и добавьте следующее:\n\n```json\n{\n    \"require\": {\n        \"yiisoft/yii2\": \"~2.0.0\"\n    },\n    \"repositories\": [\n        {\n            \"type\": \"composer\",\n            \"url\": \"https://asset-packagist.org\"\n        }\n    ]\n}\n```\n\nСохраните файл и запустите команду `composer install`. Это установит framework со всеми его зависимостями.\n\n## Создание структуры проекта\n\nПосле того как Вы установили фреймворк, пришло время создать [входную точку](structure-entry-scripts.md) приложения. Точка входа - это самый первый файл, который будет выполнен при попытке открыть приложение. По соображениям безопасности рекомендуется поместить файл точки входа в отдельный каталог и сделать каталог корнем веб директории.\n\nСоздайте каталог `web` и поместите в него файл `index.php` со следующим содержимым:\n\n```php \n<?php\n\n// закомментируйте следующие две строки при использовании в рабочем режиме\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n\nrequire(__DIR__ . '/../vendor/autoload.php');\nrequire(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');\n\n$config = require __DIR__ . '/../config.php';\n(new yii\\web\\Application($config))->run();\n```\n\nТакже создайте файл с именем `config.php`, который будет содержать всю конфигурацию приложения:\n\n```php\n<?php\nreturn [\n    'id' => 'micro-app',\n    // basePath (базовый путь) приложения будет каталог `micro-app`\n    'basePath' => __DIR__,\n    // это пространство имен где приложение будет искать все контроллеры\n    'controllerNamespace' => 'micro\\controllers',\n    // установим псевдоним '@micro', чтобы включить автозагрузку классов из пространства имен 'micro'\n    'aliases' => [\n        '@micro' => __DIR__,\n    ],\n];\n```\n\n> Info: Несмотря на то, что конфигурация приложения может находиться в файле `index.php` рекомендуется\n> содержать её в отдельном файле. Таким образом её можно также использовать и для консольного приложения, как показано ниже.\n\nТеперь Ваш проект готов к наполнению кодом. Вы можете выбрать любую структуру каталогов, соответствующую пространству имен.\n\n## Создание первого контроллера\n\nСоздайте каталог `controllers` и добавьте туда файл `SiteController.php`, который является контроллером по умолчанию, он будет обрабатывать запрос без пути.\n\n```php\n<?php\n\nnamespace micro\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actionIndex()\n    {\n        return 'Hello World!';\n    }\n}\n```\n\nЕсли Вы хотите использовать другое имя для этого контроллера, Вы можете изменить его настроив [[yii\\base\\Application::$defaultRoute]].\nНапример, для `DefaultController` будет соответственно `'defaultRoute' => 'default/index'`.\n\nНа данный момент структура проекта должна выглядеть так:\n\n```\nmicro-app/\n├── composer.json\n├── web/\n    └── index.php\n└── controllers/\n    └── SiteController.php\n```\n\nЕсли Вы еще не настроили веб-сервер, Вы можете взглянуть на [примеры конфигурационных файлов веб-серверов](start-installation.md#configuring-web-servers).\nДругой возможностью является использование команды `yii serve`, которая будет использовать встроенный веб-сервер PHP. Вы можете запустить её из каталога `micro-app/` через:\n\n    vendor/bin/yii serve --docroot=./web\n\nПри открытии URL приложения в браузере, он теперь должен печатать \"Hello World!\", который был возвращен из `SiteController::actionIndex()`.\n\n> Info: В нашем примере мы изменили пространство имен по умолчанию приложения с `app` на` micro`, чтобы продемонстрировать\n> что Вы не привязаны к этому имени (в случае, если Вы считали, что это так), а затем скорректировали\n> [[yii\\base\\Application::$controllerNamespace|controllers namespace]] и установили правильный псевдоним.\n\n## Создание REST API\n\nЧтобы продемонстрировать использование нашей \"микроархитектуры\", мы создадим простой REST API для сообщений.\n\nЧтобы у API были данные для работы, нам нужна база данных. Добавим конфигурацию подключения базы данных\nк конфигурации приложения:\n\n```php\n'components' => [\n    'db' => [\n        'class' => 'yii\\db\\Connection',\n        'dsn' => 'sqlite:@micro/database.sqlite',\n    ],\n],\n```\n\n> Info: Для простоты мы используем базу данных sqlite. Дополнительную информацию см. в [Руководство по базам данных](db-dao.md).\n\nЗатем, добавим [миграции базы данных](db-migrations.md) для создания таблицы сообщений.\nУбедитесь, что у Вас есть отдельный файл конфигурации, как описано выше, нам это нужно для того, чтобы запустить консольные команды.\nЗапуск следующих команд создаст файл миграции и применит миграцию к базе данных:\n\n    vendor/bin/yii migrate/create --appconfig=config.php create_post_table --fields=\"title:string,body:text\"\n    vendor/bin/yii migrate/up --appconfig=config.php\n\nСоздайте каталог `models` и файл` Post.php` в этом каталоге. Это код модели:\n\n```php\n<?php\n\nnamespace micro\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass Post extends ActiveRecord\n{ \n    public static function tableName()\n    {\n        return '{{posts}}';\n    }\n}\n```\n\n> Info: Созданная модель наследует класс ActiveRecord и представляет данные из таблицы `posts`.\n> Для получения дополнительной информации обратитесь к [active record руководству](db-active-record.md).\n\nЧтобы обслуживать сообщения в нашем API, добавьте `PostController` в` controllers`:\n\n```php\n<?php\n\nnamespace micro\\controllers;\n\nuse yii\\rest\\ActiveController;\n\nclass PostController extends ActiveController\n{\n    public $modelClass = 'micro\\models\\Post';\n\n    public function behaviors()\n    {\n        // удаляем rateLimiter, требуется для аутентификации пользователя\n        $behaviors = parent::behaviors();\n        unset($behaviors['rateLimiter']);\n        return $behaviors;\n    }\n}\n```\n\nНа этом этапе наш API предоставляет следующие URL-адреса:\n\n- `/index.php?r=post` - список всех сообщений\n- `/index.php?r=post/view&id=1` - просмотр сообщения с ID 1\n- `/index.php?r=post/create` - создание сообщения\n- `/index.php?r=post/update&id=1` - обновление сообщения с ID 1\n- `/index.php?r=post/delete&id=1` - удаление сообщения с ID 1\n\nНачиная с этого момента Вы можете посмотреть следующие руководства для дальнейшего развития своего приложения:\n\n- API в настоящий момент принимает только urlencoded данные на вход. Чтобы сделать его настоящим JSON API, Вам\n  необходимо настроить [[yii\\web\\JsonParser]].\n- Чтобы сделать URL более дружественным, вам необходимо настроить маршрутизацию.\n  См. [Руководство по маршрутизации REST](rest-routing.md) о том, как это сделать.\n- Дополнительную информацию см. в разделе [Взгляд в будущее](start-looking-ahead.md).\n"
  },
  {
    "path": "docs/guide-ru/tutorial-yii-integration.md",
    "content": "Работа со сторонним кодом\n=============================\n\nИногда необходимо использовать сторонний код в приложениях Yii. Или же есть потребность использовать Yii в качестве библиотеки в сторонних системах. В этом разделе мы рассмотрим, как это происходит.\n\n\nИспользование сторонних библиотек в Yii <span id=\"using-libs-in-yii\"></span>\n----------------------------------\n\nПеред тем, как использовать стороннюю библиотеку в приложении Yii, в первую очередь следует убедиться, что в ней либо явно настроена загрузка классов, либо классы могут загружаться автоматически.\n\n### Использование пакетов Composer <span id=\"using-composer-packages\"></span>\n\nМногие сторонние библиотеки поставляются в виде пакетов [Composer](https://getcomposer.org/).\nДля установки таких библиотек достаточно проделать два простых шага:\n\n1. Изменить файл `composer.json` своего приложения и указать, какие пакеты Composer нужно устанавливать.\n2. Выполнить команду `composer install`, чтобы установить указанные пакеты.\n\nКлассы установленных пакетов Composer поддерживают автозагрузку с помощью автозагрузчика Composer. Убедитесь, что во [входном скрипте](structure-entry-scripts.md) приложения присутствуют следующие строки, подключающие автозагрузчик Composer:\n\n```php\n// подключение автозагрузчика Composer\nrequire __DIR__ . '/../vendor/autoload.php';\n\n// подключение файла класса Yii\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n```\n\n### Использование отдельных библиотек <span id=\"using-downloaded-libs\"></span>\n\nЕсли библиотека не поставляется в виде пакета Composer, необходимо установить ее согласно ее руководству по установке.\nВ большинстве случаев потребуется вручную скачать файл с релизом и распаковать его в директорию `BasePath/vendor`, где `BasePath` соответствует [базовому пути](structure-applications.md#basePath) приложения.\n\nЕсли библиотека использует собственный автозагрузчик классов, его можно подключить во [входном скрипте](structure-entry-scripts.md) приложения. Желательно подключить его до того, как подключается файл `Yii.php`, чтобы при автоматической загрузке классов у автозагрузчика классов Yii был приоритет.\n\nЕсли библиотека не поставляется с автозагрузчиком классов, но конвенция именования ее классов соответствует [PSR-4](https://www.php-fig.org/psr/psr-4/), для загрузки ее классов можно использовать автозагрузчик Yii. Для этого достаточно для каждого корневого пространства имен, которые используются в ее классах, объявить [корневой псевдоним](concept-aliases.md#defining-aliases). Предположим, что библиотека установлена в директорию `vendor/foo/bar`, а ее классы объявлены в корневом пространстве имен `xyz`. В конфигурации приложения можно использовать следующий код:\n\n```php\n[\n    'aliases' => [\n        '@xyz' => '@vendor/foo/bar',\n    ],\n]\n```\n\nЕсли ни один из предыдущих вариантов не подходит, скорее всего для использования библиотеки нужно настроить в конфигурации PHP директиву `include_path`. Настройте ее, следуя инструкциям, которые поставляются с библиотекой.\n\nВ наихудшем случае библиотека требует явного подключения всех файлов, содержащих классы. При этом для подключения классов по требованию можно проделать следующее:\n\n* Определить, какие классы входят в состав библиотеки.\n* Перечислить классы и пути к соответствующим файлам в `Yii::$classMap` во [входном скрипте](structure-entry-scripts.md) приложения. Например,\n```php\nYii::$classMap['Class1'] = 'path/to/Class1.php';\nYii::$classMap['Class2'] = 'path/to/Class2.php';\n```\n\n\nИспользование Yii в сторонних системах <span id=\"using-yii-in-others\"></span>\n--------------------------------\n\nПоскольку в Yii реализована масса полезных функций, они могут пригодиться при разработке или расширении сторонних систем, таких как WordPress и Joomla, или приложений, разработанных с помощью других PHP-фреймворков. Например, в сторонней системе можно задействовать класс [[yii\\helpers\\ArrayHelper]] или использовать функционал [Active Record](db-active-record.md). Для этого обычно нужно сделать две вещи: установить Yii и подключить Yii.\n\nЕсли сторонняя система использует для управления зависимостями Composer, Yii можно просто установить с помощью следующей команды:\n\n```bash\ncomposer require yiisoft/yii2\n```\n\nЕсли вам интересны возможности Yii, например, по работе с базами данных, но вы не планируете использование\n[ресурсов](structure-assets.md), вы можете установить специальный пакет, который предотвращает загрузку Bower и NPM\nпакетов. Смотрите [cebe/assetfree-yii2](https://github.com/cebe/assetfree-yii2) для деталей.\n\nПервая команда устанавливает [composer asset plugin](https://github.com/fxpio/composer-asset-plugin), который позволяет управлять зависимостями пакетов bower и npm через Composer. Даже если вы хотите воспользоваться слоем абстракции баз данных или другими элементами Yii, не связанными с ресурсами, этот плагин все равно придется установить, так как без него не установится пакет Yii.\nВ разделе [об установке Yii](start-installation.md#installing-via-composer) более подробно описана работа с Composer и даны решения проблем, которые могут возникнуть при установке.\n\nТакже можно [скачать](https://www.yiiframework.com/download/) файл релиза Yii и распаковать его в директорию `BasePath/vendor`.\n\nДалее следует изменить входной скрипт сторонней системы, поместив в его начало следующий код:\n\n```php\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n\n$yiiConfig = require __DIR__ . '/../config/yii/web.php';\nnew yii\\web\\Application($yiiConfig); // НЕ ВЫЗЫВАЙТЕ run() в этом месте\n```\n\nКак видите, этот код очень похож на код [входного скрипта](structure-entry-scripts.md) типичного приложения Yii. Единственное отличие заключается в том, что после создания экземпляра приложения не вызывается метод `run()`. Это связано с тем, что при вызове `run()` Yii захватывает контроль над процессом обработки запроса, что в данном случае не требуется, так как эту задачу выполняет существующее приложение.\n\nКак и в случае с приложением Yii, нужно настроить экземпляр приложения исходя из окружения запущенной сторонней системы. Например, чтобы воспользоваться функционалом [Active Record](db-active-record.md), нужно передать в [компонент приложения](structure-application-components.md) `db` настройки для подключения к базе данных, которую использует сторонняя система.\n\nЭто позволит задействовать большинство функционала, который предоставляет Yii. Например, можно будет создавать классы типа Active Record, и с их помощью взаимодействовать с базой данных.\n\n\nИспользование Yii 2 в связке с Yii 1 <span id=\"using-both-yii2-yii1\"></span>\n----------------------\n\nЕсли в прошлом вам приходилось использовать Yii 1, не исключено, что у вас до сих пор где-то используются приложения на этой платформе. Вместо того, чтобы переписывать все приложение под Yii 2, может быть целесообразно расширить его используя отдельные функции, которые появились в Yii 2.\nДля этого нужно выполнить следующие действия.\n\n> Note: Yii 2 требует PHP 5.4 или выше. Убедитесь, что и сервер, и существующее приложение поддерживают это.\n\nВо-первых, установите Yii 2 в существующем приложении, выполняя действия, описанные в [предыдущем подразделе](#using-yii-in-others).\n\nВо-вторых, внесите следующие изменения во входной скрипт приложения:\n\n```php\n// подключение модифицированного класса Yii, описанного ниже\nrequire __DIR__ . '/../components/Yii.php';\n\n// настройка приложения Yii 2\n$yii2Config = require __DIR__ . '/../config/yii2/web.php';\nnew yii\\web\\Application($yii2Config); // НЕ ВЫЗЫВАЙТЕ run()\n\n// настройка приложения Yii 1\n$yii1Config = require __DIR__ . '/../config/yii1/main.php';\nYii::createWebApplication($yii1Config)->run();\n```\n\nТак как класс `Yii` используется и в Yii 1, и в Yii 2, нужно будет создать его модифицированную версию, обслуживающую обе версии фреймворка.\nВ приведенном выше коде подключается модифицированный файл класса `Yii` со следующим содержимым:\n\n```php\n$yii2path = '/path/to/yii2';\nrequire $yii2path . '/BaseYii.php'; // Yii 2.x\n\n$yii1path = '/path/to/yii1';\nrequire $yii1path . '/YiiBase.php'; // Yii 1.x\n\nclass Yii extends \\yii\\BaseYii\n{\n    // скопируйте и вставьте код из YiiBase (1.x)\n}\n\nYii::$classMap = include($yii2path . '/classes.php');\n// регистрация автозагрузчика Yii 2 через Yii 1\nYii::registerAutoloader(['Yii', 'autoload']);\n// создание контейнера внедрения зависимостей\nYii::$container = new yii\\di\\Container;\n```\n\nВот и все! Теперь в любом месте кода можно с помощью конструкции `Yii::$app` получить доступ к экземпляру приложения Yii 2, а с помощью конструкции `Yii::app()` - к экземпляру приложения Yii 1:\n\n\n```php\necho get_class(Yii::app()); // выводит 'CWebApplication'\necho get_class(Yii::$app);  // выводит 'yii\\web\\Application'\n```\n"
  },
  {
    "path": "docs/guide-tr/README.md",
    "content": "Yii 2.0 için Açıklayıcı Rehber\n==============================\n\nBu döküman, [Yii Dökümantasyon Koşulları](https://www.yiiframework.com/doc/terms/) altında yayınlandı.\n\nTüm hakları saklıdır.\n\n2014 (c) Yii Software LLC.\n\n\nGiriş\n------------\n\n* [Yii hakkında](intro-yii.md)\n* [Sürüm 1.1'den yükseltme](intro-upgrade-from-v1.md)\n\n\nBaşlarken\n---------------\n\n* [Ne bilmeye ihtiyacın var](start-prerequisites.md)\n* [Yii Kurulumu](start-installation.md)\n* [Uygulamaları Çalıştırmak](start-workflow.md)\n* [Merhaba deyin](start-hello.md)\n* [Formlarla Çalışırken](start-forms.md)\n* [Veritabanıyla Çalışırken](start-databases.md)\n* [Gii ile Kod Oluşturmak](start-gii.md)\n* [İleriye Bakmak](start-looking-ahead.md)\n\n\nUygulama Yapısı\n---------------------\n\n* [Uygulama Yapısına Genel Bakış](structure-overview.md)\n* [Giriş Komutları](structure-entry-scripts.md)\n* [Uygulamalar](structure-applications.md)\n* [Uygulama Bileşenleri](structure-application-components.md)\n* [Kontrolcüler](structure-controllers.md)\n* [Modeller](structure-models.md)\n* [Görünümler](structure-views.md)\n* [Modüller](structure-modules.md)\n* [Filtreler](structure-filters.md)\n* [Araçlar](structure-widgets.md)\n* [Varlıklar](structure-assets.md)\n* [Uzantılar](structure-extensions.md)\n\n\nİstekleri İşlemek\n-----------------\n\n* [İstekleri İşlemeye Genel Bakış](runtime-overview.md)\n* [Bootstrapping](runtime-bootstrapping.md)\n* [Yönlendirme ve URL Oluşturma](runtime-routing.md)\n* [İstekler](runtime-requests.md)\n* [Yanıtlar](runtime-responses.md)\n* [Oturumlar ve Çerezler](runtime-sessions-cookies.md)\n* [Hataları İşleme](runtime-handling-errors.md)\n* [Logging](runtime-logging.md)\n\n\nAnahtar Kavramlar\n------------\n\n* [Bileşenler](concept-components.md)\n* [Özellikler](concept-properties.md)\n* [Olaylar](concept-events.md)\n* [Davranışlar](concept-behaviors.md)\n* [Yapılandırmalar](concept-configurations.md)\n* [Takma Adlar](concept-aliases.md)\n* [Sınıf Otomatik Yüklemesi](concept-autoloading.md)\n* [Servis Bulucu](concept-service-locator.md)\n* [Dependency Injection Container](concept-di-container.md)\n\n\nVeritabanıyla Çalışırken\n----------------------\n\n* [Veritabanı Erişim Nesneleri](db-dao.md): Bir veritabanına bağlanmak, basit sorgular, işlemler ve şema manipilasyonu\n* [Sorgu Oluşturucu](db-query-builder.md): Basit bir ayırma katmanı kullanarak veritabanı sorgulama\n* [Active Record](db-active-record.md): Active Record ORM, kayıtları almak, değiştirmek ve ilişkileri tanımlama\n* [Taşıma İşlemleri](db-migrations.md): Bir takım geliştirme ortamında sürüm kontrolünü veritabanlarına uygula\n* [Sphinx](https://www.yiiframework.com/extension/yiisoft/yii2-sphinx/doc/guide)\n* [Redis](https://www.yiiframework.com/extension/yiisoft/yii2-redis/doc/guide)\n* [MongoDB](https://www.yiiframework.com/extension/yiisoft/yii2-mongodb/doc/guide)\n* [ElasticSearch](https://www.yiiframework.com/extension/yiisoft/yii2-elasticsearch/doc/guide)\n\n\nKullanıcılardan Veri Alırken\n-----------------------\n\n* [Form Oluşturma](input-forms.md)\n* [Veri Kontrolü](input-validation.md)\n* [Dosya Yükleme](input-file-upload.md)\n* [Collecting Tabular Input](input-tabular-input.md)\n* [Birden fazla Model için Veri Alma](input-multiple-models.md)\n* [Extending ActiveForm on the Client Side](input-form-javascript.md)\n\n\nVeriyi Gösterirken\n---------------\n\n* [Veri Tipini Değiştirme](output-formatting.md)\n* [Sayfalama](output-pagination.md)\n* [Sıralama](output-sorting.md)\n* [Veri Sağlayıcıları](output-data-providers.md)\n* [Veri Araçları](output-data-widgets.md)\n* [Scriptlerle Çalışmak](output-client-scripts.md)\n* [Tema Kullanımı](output-theming.md)\n\n\nGüvenlik\n--------\n\n* [Güvenliğe Genel Bakış](security-overview.md)\n* [Kimlik Denetleme](security-authentication.md)\n* [Yetkilendirme](security-authorization.md)\n* [Şifrelerle Çalışma](security-passwords.md)\n* [Kriptografi](security-cryptography.md)\n* [Auth Clients](https://www.yiiframework.com/extension/yiisoft/yii2-authclient/doc/guide)\n* [Egzersizler](security-best-practices.md)\n\n\nCache Almak\n-------\n\n* [Cache Genel Bakış](caching-overview.md)\n* [Veriyi Cache Alma](caching-data.md)\n* [Sayfanın Sadece Bir Kısmını Cache Alma](caching-fragment.md)\n* [Sayfanın Tamamını Cache Alma](caching-page.md)\n* [HTTP Cache Alma](caching-http.md)\n\n\nRESTful Web Servisleri\n--------------------\n\n* [Hızlı Başlangıç](rest-quick-start.md)\n* [Kaynaklar](rest-resources.md)\n* [Kontrolcüler](rest-controllers.md)\n* [Rota Yöntemleri](rest-routing.md)\n* [Yanıt Formatlama](rest-response-formatting.md)\n* [Kimlik Denetleme](rest-authentication.md)\n* [İstek Sınırlama](rest-rate-limiting.md)\n* [Sürümlere Ayırma](rest-versioning.md)\n* [Hataları Kullanma](rest-error-handling.md)\n\n\nGeliştirici Araçları\n-----------------\n\n* [Geliştirici Aracı ve Debugger](https://www.yiiframework.com/extension/yiisoft/yii2-debug/doc/guide)\n* [Gii'yi Kullanarak Kod Oluşturmak](https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide)\n* [API Dökümanı Oluşturma](https://www.yiiframework.com/extension/yiisoft/yii2-apidoc)\n\n\nTest\n-------\n\n* [Testlere Genel Bakış](test-overview.md)\n* [Testler için Ortam Kurulumu](test-environment-setup.md)\n* [Unit Testleri](test-unit.md)\n* [Fonksiyonel Testler](test-functional.md)\n* [Kabul Testleri](test-acceptance.md)\n* [Fixtures](test-fixtures.md)\n\n\nÖzel Başlıklar\n--------------\n\n* [Gelişmiş Proje Şablonu](https://www.yiiframework.com/extension/yiisoft/yii2-app-advanced/doc/guide)\n* [Building Application from Scratch](tutorial-start-from-scratch.md)\n* [Konsol Komutları](tutorial-console.md)\n* [Core Validators](tutorial-core-validators.md)\n* [Docker](tutorial-docker.md)\n* [Uluslararası Hale Getirme](tutorial-i18n.md)\n* [Mail Gönderme](tutorial-mailing.md)\n* [Performans Ayarları](tutorial-performance-tuning.md)\n* [Paylaşımlı Sunucu Ortamı](tutorial-shared-hosting.md)\n* [Şablon Motoru](tutorial-template-engines.md)\n* [3. Parti Kodlarla Çalışma](tutorial-yii-integration.md)\n* [Yii'yi Mikro Kütüphane Gibi Kullanma](tutorial-yii-as-micro-framework.md)\n\n\nAraçlar\n-------\n\n* [GridView](https://www.yiiframework.com/doc-2.0/yii-grid-gridview.html)\n* [ListView](https://www.yiiframework.com/doc-2.0/yii-widgets-listview.html)\n* [DetailView](https://www.yiiframework.com/doc-2.0/yii-widgets-detailview.html)\n* [ActiveForm](https://www.yiiframework.com/doc-2.0/guide-input-forms.html#activerecord-based-forms-activeform)\n* [Pjax](https://www.yiiframework.com/doc-2.0/yii-widgets-pjax.html)\n* [Menu](https://www.yiiframework.com/doc-2.0/yii-widgets-menu.html)\n* [LinkPager](https://www.yiiframework.com/doc-2.0/yii-widgets-linkpager.html)\n* [LinkSorter](https://www.yiiframework.com/doc-2.0/yii-widgets-linksorter.html)\n* [Bootstrap Widgets](https://www.yiiframework.com/extension/yiisoft/yii2-bootstrap/doc/guide)\n* [jQuery UI Widgets](https://www.yiiframework.com/extension/yiisoft/yii2-jui/doc/guide)\n\n\nYardımcılar\n-------\n\n* [Helpers Overview](helper-overview.md)\n* [ArrayHelper](helper-array.md)\n* [Html](helper-html.md)\n* [Url](helper-url.md)\n\n"
  },
  {
    "path": "docs/guide-tr/blocktypes.json",
    "content": "{\n    \"Warning:\": \"Uyarı:\",\n    \"Note:\": \"Not:\",\n    \"Info:\": \"Bilgi:\",\n    \"Tip:\": \"İpucu:\"\n}"
  },
  {
    "path": "docs/guide-tr/intro-yii.md",
    "content": "Yii nedir?\n===========\n\nYii, yüksek performanslı ve modern Web uygulamalarını hızlı geliştirmeyi sağlayan bileşen tabanlı bir PHP kütüphanesidir. Yii kelimesinin anlamı Çince de **basit ve evrimseldir.** Ayrıca, **Yes It Is** (Evet, İşte bu) kısaltması olarak da düşünülebilir.\n\nYii'nin en iyi olduğu şey nedir?\n---------------------\n\nYii genel bir Web programlama kütüphanesidir yani, PHP kullanılan her türlü Web uygulaması geliştirilebilir. Çünkü bileşen tabanlı mimari ve sofistike cache almayı destekler, büyük çaplı uygulamalar için özellikle uygundur; portallar, forumlar, içerik yönetim sistemleri (CMS), e-ticaret projeleri, RESTful Web servisleri ve dahası.\n\nYii'yi diğer kütüphanelerle nasıl karşılaştırabilirim?\n-------------------------------------------\n\nEğer zaten başka bir kütüphane ailesine dahilseniz, Yii'yi karşılaştırmak için anlamanız gereken şeyler:\n\n- Çoğu PHP kütüphaneleri gibi, Yii  MVC (Model-View-Controller) desenini uygular ve bu desene göre kod organizasyonunu teşvik eder.\n- Yii, kodun basit ama zarif bir şekilde yazılması gerektiğini felsefe edinir. Yii, asla bazı tasarım desenlerini sıkı takip etmek amacıyla herşeyi aşırı tasarlamayacaktır.\n- Yii, kanıtlanmış full-stack bir kütüphanedir ve şu özellikleri kullanıma hazırdır: sorgu mimarı ve ActiveRecord her ikisi için ilişkisel ve NoSQL veritabanlarını; RESTful API geliştirmeyi; multi-tier (çok katmanlı) cache desteği ve dahasını destekler.\n- Yii son derece genişletilebilir. Neredeyse her bir çekirdek kodunu kişiselleştirebilir veya değiştirebilirsiniz. Yeniden dağıtılabilir uzantıları kullanmak veya geliştirmek için Yii'nin katı uzantı mimarisinden de yararlanabilirsiniz.\n- Yüksek performans her zaman Yii'nin öncelikli hedefidir.\n\nYii tek kişilik bir gösteri değildir, [güçlü çekirdek geliştirici ekibi](https://www.yiiframework.com/team/) ve Yii'nin gelişimine sürekli katkıda bulunan büyük bir profesyonel geliştirici topluluğu tarafından da destekleniyor. Yii geliştirici ekibi, en yeni Web geliştirme teknikleri ile diğer kütüphaneler ve projelerde bulunan en iyi uygulamalara ve özelliklere yakından göz atmaktadır. Basit ve zarif arayüzler aracılığıyla, başka yerde bulunan en uygun, en iyi uygulamalar ve özellikler, düzenli olarak çekirdek kütüphaneye dahil edilir.\n\n\nYii'nin Sürümleri\n------------\n\nYii'nin şuan da 1.1 ve 2.0 olmak üzere 2 tane major sürümü vardır. 1.1 sürümü eski nesil ve şuan da bakım modundadır.Sürüm 2.0, Yii'nin Composer, PSR, namespaceler, traitler ve benzerlerini içeren en son teknolojileri ve protokolleri benimseyen eksiksiz bir yeniden yazımıdır. Sürüm 2.0, kütüphanenin mevcut sürümünü temsil eder ve önümüzdeki birkaç yıl boyunca geliştirilmeye devam edecektir.\n\nBu kılavuz esas olarak sürüm 2.0 ile ilgilidir.\n\nGereksinimler ve Önkoşullar\n------------------------------\n\nYii 2.0, PHP 7.4.0 veya üstü sürüm gerektirir ve PHP 'nin en son sürümü ile en iyi şekilde çalışır. Her bir Yii sürümünde yer alan gereksinim denetleyicisini çalıştırarak, daha ayrıntılı gereksinimleri ayrı ayrı özellikler için bulabilirsiniz.\n\nYii OOP temelli bir kütüphane olduğu için Yii'yi kullanmak, nesne yönelimli programlama (OOP) hakkında temel bilgi gerektirir. Yii 2.0 ayrıca PHP'nin [namespaceler](https://www.php.net/manual/en/language.namespaces.php) ve [traitler](https://www.php.net/manual/en/language.oop5.traits.php) gibi son özelliklerinden de yararlanır. Bu kavramları anlamak, Yii 2.0'ı daha kolay anlamanıza yardımcı olacaktır.\n"
  },
  {
    "path": "docs/guide-tr/start-prerequisites.md",
    "content": "# Ne bilmeye ihtiyacın var\n\nYii'yi öğrenmesi, diğer PHP kütüphaneleri kadar zor olmasada, yine de başlamadan önce öğrenmeniz gereken bazı şeyler var.\n\n## PHP\n\nYii bir PHP kütüphanesidir, bu yüzden [PHP Dil Başvuru Kılavuzunu](https://www.php.net/manual/tr/langref.php) okuduğunuzdan ve anladığınızdan emin olun. Yii ile geliştirirken, nesne yönelimli bir şekilde kod yazacaksınız, bu yüzden [Sınıflar ve Nesneler](https://www.php.net/manual/tr/language.oop5.basic.php) gibi [ad alanları](https://www.php.net/manual/en/language.namespaces.php)na aşina olduğunuzdan emin olun.\n\n## Nesne Yönelimli Programlama (OOP)\n\nNesne yönelimli programlamanın temelini öğrenmeniz gerekmektedir. Eğer aşina değilseniz, [tuts+'daki gibi](https://code.tutsplus.com/tutorials/object-oriented-php-for-beginners--net-12762) benzeri derslere göz atabilirsiniz.\n\nUygulamanız ne kadar karmaşıksa, o karmaşıklığı başarılı bir şekilde yönetmek için öğrenmeniz gereken daha gelişmiş OOP kavramları olduğunu unutmayın.\n\n## Komut satırı ve composer\n\nYii, de-facto standart PHP paket yöneticisi [Composer](https://getcomposer.org/)'ı kullanır. Bu yüzden [rehberini](https://getcomposer.org/doc/01-basic-usage.md) okuyup anladığınızdan emin olun. Komut satırını kullanmayı bilmiyorsanız, şimdi denemeye başlamanın tam zamanı. Temelleri öğrendikten sonra asla onsuz çalışmak istemeyeceksiniz.\n"
  },
  {
    "path": "docs/guide-tr/translators.json",
    "content": "[\n  \"Serhat Özleş\"\n]"
  },
  {
    "path": "docs/guide-uk/README.md",
    "content": "Повний посібник з Yii 2.0\n=========================\n\nДаний посібник випущено відповідно до [положень про документацію Yii](https://www.yiiframework.com/doc/terms/).\n\nAll Rights Reserved.\n\n2014 (c) Yii Software LLC.\n\n\nВведення\n--------\n\n* [Про Yii](intro-yii.md)\n* [Оновлення із версії 1.1](intro-upgrade-from-v1.md)\n\n\nПерше знайомство\n----------------\n\n* [Встановлення Yii](start-installation.md)\n* [Виконання додатків](start-workflow.md)\n* [Говоримо \"Привіт\"](start-hello.md)\n* [Робота з формами](start-forms.md)\n* [Робота з базами даних](start-databases.md)\n* [Генерування коду за допомогою Gii](start-gii.md)\n* [Наступні кроки](start-looking-ahead.md)\n\n\nСтруктура додатка\n-----------------\n\n* [Огляд](structure-overview.md)\n* [Вхідні скрипти](structure-entry-scripts.md)\n* [Додатки](structure-applications.md)\n* [Компоненти додатка](structure-application-components.md)\n* [Контролери](structure-controllers.md)\n* [Моделі](structure-models.md)\n* **TBD** [Представлення](structure-views.md)\n* **TBD** [Модулі](structure-modules.md)\n* **TBD** [Фільтри](structure-filters.md)\n* **TBD** [Віджети](structure-widgets.md)\n* **TBD** [Ресурси](structure-assets.md)\n* **TBD** [Розширення](structure-extensions.md)\n\n\nОбробка запитів\n---------------\n\n* **TBD** [Огляд](runtime-overview.md)\n* **TBD** [Початкове завантаження](runtime-bootstrapping.md)\n* **TBD** [Маршрутизація та створення URL](runtime-routing.md)\n* **TBD** [Запити](runtime-requests.md)\n* **TBD** [Відповіді](runtime-responses.md)\n* **TBD** [Сесії та кукі](runtime-sessions-cookies.md)\n* **TBD** [Обробка помилок](runtime-handling-errors.md)\n* **TBD** [Журналювання](runtime-logging.md)\n\n\nОсновні поняття\n---------------\n\n* **TBD** [Компоненти](concept-components.md)\n* **TBD** [Властивості](concept-properties.md)\n* **TBD** [Події](concept-events.md)\n* **TBD** [Поведінки](concept-behaviors.md)\n* **TBD** [Конфігурації](concept-configurations.md)\n* [Псевдоніми](concept-aliases.md)\n* [Автозавантаження класів](concept-autoloading.md)\n* **TBD** [Локатор служб](concept-service-locator.md)\n* **TBD** [Dependency Injection Container](concept-di-container.md)\n\n\nРобота з базами даних\n---------------------\n\n* **TBD** [Обʼєкти доступу до даних (DAO)](db-dao.md): Зʼєднання з базою даних, прості запити, транзакції і робота зі схемою\n* **TBD** [Конструктор запитів](db-query-builder.md): Запити до бази даних через простий шар абстракції\n* **TBD** [Active Record](db-active-record.md): Отримання обʼєктів AR, робота з ними та визначення звʼязків\n* **TBD** [Міграції](db-migrations.md): Контроль версій схеми даних при роботі в команді\n* [Sphinx](https://github.com/yiisoft/yii2-sphinx/blob/master/docs/guide/README.md)\n* [Redis](https://github.com/yiisoft/yii2-redis/blob/master/docs/guide/README.md)\n* [MongoDB](https://github.com/yiisoft/yii2-mongodb/blob/master/docs/guide/README.md)\n* [ElasticSearch](https://github.com/yiisoft/yii2-elasticsearch/blob/master/docs/guide/README.md)\n\n\nОтримання даних від користувача\n-------------------------------\n\n* **TBD** [Створення форм](input-forms.md)\n* **TBD** [Перевірка вводу](input-validation.md)\n* **TBD** [Завантаження файлів](input-file-uploading.md)\n* **TBD** [Збір табличного вводу](input-tabular-input.md)\n* **TBD** [Робота з декількома моделями](input-multiple-models.md)\n\n\nВідображення даних\n------------------\n\n* **TBD** [Форматування даних](output-formatting.md)\n* **TBD** [Розділення на сторінки](output-pagination.md)\n* **TBD** [Сортування](output-sorting.md)\n* **TBD** [Провайдери даних](output-data-providers.md)\n* **TBD** [Віджети даних](output-data-widgets.md)\n* **TBD** [Робота з клієнтськими скриптами](output-client-scripts.md)\n* **TBD** [Темізація](output-theming.md)\n\n\nБезпека\n-------\n\n* **TBD** [Аутентифікація](security-authentication.md)\n* **TBD** [Авторизація](security-authorization.md)\n* **TBD** [Робота з паролями](security-passwords.md)\n* [Клієнти авторизації](https://github.com/yiisoft/yii2-authclient/blob/master/docs/guide/README.md)\n* **TBD** [Кращі практики](security-best-practices.md)\n\n\nКешування\n---------\n\n* **TBD** [Огляд](caching-overview.md)\n* **TBD** [Кешування даних](caching-data.md)\n* [Кешування фрагментів](caching-fragment.md)\n* **TBD** [Кешування сторінок](caching-page.md)\n* **TBD** [HTTP кешування](caching-http.md)\n\n\nВеб-сервіси RESTful\n-------------------\n\n* [Швидкий старт](rest-quick-start.md)\n* **TBD** [Ресурси](rest-resources.md)\n* **TBD** [Контролери](rest-controllers.md)\n* **TBD** [Маршрутизація](rest-routing.md)\n* **TBD** [Форматування відповіді](rest-response-formatting.md)\n* **TBD** [Аутентифікація](rest-authentication.md)\n* [Обмеження частоти запитів](rest-rate-limiting.md)\n* **TBD** [Версіонування](rest-versioning.md)\n* **TBD** [Обробка помилок](rest-error-handling.md)\n\n\nІнструменти розробника\n----------------------\n\n* [Панель налагодження та налагоджувач](https://github.com/yiisoft/yii2-debug/blob/master/docs/guide-uk/README.md)\n* [Генерування коду за допомогою Gii](https://github.com/yiisoft/yii2-gii/blob/master/docs/guide-uk/README.md)\n* **TBD** [Генерування документації API](https://github.com/yiisoft/yii2-apidoc)\n\n\nТестування\n----------\n\n* **TBD** [Огляд](test-overview.md)\n* **TBD** [Налаштування середовища тестування](test-environment-setup.md)\n* **TBD** [Модульні тести](test-unit.md)\n* **TBD** [Функціональні тести](test-functional.md)\n* **TBD** [Приймальні тести](test-acceptance.md)\n* **TBD** [Фікстури](test-fixtures.md)\n\n\nСпеціальні теми\n---------------\n\n* **TBD** [Розширений шаблон проекту](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide-uk/README.md)\n* [Створення додатка з нуля](tutorial-start-from-scratch.md)\n* [Консольні додатки](tutorial-console.md)\n* **TBD** [Основні валідатори](tutorial-core-validators.md)\n* **TBD** [Інтернаціоналізація](tutorial-i18n.md)\n* **TBD** [Робота з поштою](tutorial-mailing.md)\n* **TBD** [Покращення швидкодії](tutorial-performance-tuning.md)\n* **TBD** [Робота на віртуальному хостингу](tutorial-shared-hosting.md)\n* [Шаблонізатори](tutorial-template-engines.md)\n* [Робота із стороннім кодом](tutorial-yii-integration.md)\n\n\nВіджети\n-------\n\n* GridView: **TBD** link to demo page\n* ListView: **TBD** link to demo page\n* DetailView: **TBD** link to demo page\n* ActiveForm: **TBD** link to demo page\n* Pjax: **TBD** link to demo page\n* Menu: **TBD** link to demo page\n* LinkPager: **TBD** link to demo page\n* LinkSorter: **TBD** link to demo page\n* [Віджети Bootstrap](https://github.com/yiisoft/yii2-bootstrap/blob/master/docs/guide-uk/README.md)\n* [Віджети jQuery UI](https://github.com/yiisoft/yii2-jui/blob/master/docs/guide-uk/README.md)\n\n\nХелпери\n-------\n\n* **TBD** [Огляд хелперів](helper-overview.md)\n* **TBD** [ArrayHelper](helper-array.md)\n* **TBD** [Html](helper-html.md)\n* **TBD** [Url](helper-url.md)\n"
  },
  {
    "path": "docs/guide-uk/blocktypes.json",
    "content": "{\n  \"Warning:\": \"Увага:\",\n  \"Note:\": \"Примітка:\",\n  \"Info:\": \"Інформація:\",\n  \"Tip:\": \"Підказка:\"\n}\n"
  },
  {
    "path": "docs/guide-uk/caching-fragment.md",
    "content": "Кешування фрагментів\n====================\n\nКешування фрагментів відноситься до кешування фрагментів веб-сторінки. Наприклад, якщо сторінка відображає в таблиці сумарні \nрічні продажі, ви можете зберегти цю таблицю в кеші з метою економії часу, необхідного для створення таблиці при кожному запиті. \nКешування фрагментів засноване на [кешуванні даних](caching-data.md).\n\nДля кешування фрагментів використовуйте наступний код у [представленні](structure-views.md):\n\n```php\nif ($this->beginCache($id)) {\n\n    // ... тут створюємо вміст ...\n\n    $this->endCache();\n}\n```\n\nТаким чином, розташуйте логіку генерування вмісту у комбінацію викликів [[yii\\base\\View::beginCache()|beginCache()]] та\n[[yii\\base\\View::endCache()|endCache()]]. Якщо вміст буде знайдено у кеші, [[yii\\base\\View::beginCache()|beginCache()]]\nвідобразить закешований вміст і поверне `false`, оминаючи генерування вмісту.\nВ іншому випадку, буде виконано логіку генерування вмісту і з викликом [[yii\\base\\View::endCache()|endCache()]]\nзгенерований вміст буде записаний до кешу.\n\nПодібно до [кешування даних](caching-data.md), для кешування фрагментів необхідний унікальний ідентифікатор \nдля визначення кешувального фрагмента.\n\n\n## Параметри кешування <span id=\"caching-options\"></span>\n\nВи можете вказати додаткові параметри про кешуванні фрагментів, передавши масив опцій в якості другого параметра метода\n[[yii\\base\\View::beginCache()|beginCache()]]. За лаштунками, цей масив параметрів буде використано для налаштування віджета\n[[yii\\widgets\\FragmentCache]], який реалізує фактичну функціональність кешування фрагментів.\n\n\n### Тривалість <span id=\"duration\"></span>\n\nМабуть найбільш часто використовуваною опцією кешування фрагмента є [[yii\\widgets\\FragmentCache::duration|duration]].\nВона визначає на скільки секунд вміст може залишатися дійсним у кеші. Код нижче кешує фрагмент не більше, ніж на одну:\n\n```php\nif ($this->beginCache($id, ['duration' => 3600])) {\n\n    // ... тут створюємо вміст ...\n\n    $this->endCache();\n}\n```\n\nЯкщо опцію тривалості не задано, то вона прийме значенне за замовчування (60), що означає, що вміст в кеші стане недійсним через 60 секунд.\n\n\n### Залежності <span id=\"dependencies\"></span>\n\nПодібно до [кешування даних](caching-data.md#cache-dependencies), фрагмент вмісту у кеші також може мати залежності.\nНаприклад, вміст поста, що відображається, залежить від того, чи був він змінений.\n\nДля визначення залежності, встановіть опцію [[yii\\widgets\\FragmentCache::dependency|dependency]], \nяка може бути або обʼєктом [[yii\\caching\\Dependency]] або масивом налаштувань для створення обʼєкта залежностей.\nНаступний код визначає, що вміст фрагмента залежить від зміни значення стовпця `updated_at`:\n\n```php\n$dependency = [\n    'class' => 'yii\\caching\\DbDependency',\n    'sql' => 'SELECT MAX(updated_at) FROM post',\n];\n\nif ($this->beginCache($id, ['dependency' => $dependency])) {\n\n    // ... тут створюємо вміст ...\n\n    $this->endCache();\n}\n```\n\n\n### Варіації <span id=\"variations\"></span>\n\nВміст, що кешується, може бути змінено відповідно до деяких параметрів.\nНаприклад, для веб-додатка із підтримкою декількох мов, один і той же шматок представлення коду може генерувати контент на різних мовах.\nТаким чином, ви можете змінювати кешований вміст відповідно до поточної мови додатка.\n\nЩоб вказати варіації кешу, встановіть опцію [[yii\\widgets\\FragmentCache::variations|variations]], \nяка повинна бути масивом скалярних значень, кожне з яких представляє певний коефіцієнт варіації.\nНаприклад, щоб кешувати вміст у залежності від мови додатка, ви можете використовувати наступний код:\n\n```php\nif ($this->beginCache($id, ['variations' => [Yii::$app->language]])) {\n\n    // ... тут створюємо вміст ...\n\n    $this->endCache();\n}\n```\n\n\n### Перемикання кешування <span id=\"toggling-caching\"></span>\n\nІноді, ви можете захотіти увімкнути кешування фрагмента тільки за певних умов. Наприклад, для сторінки, яка відображає форму, \nви хочете кешувати тільки форму при її початковому запиті (через GET-запит). Будь-яке подальше відображення (через POST-запит)\nформи не повинне кешуватися, тому що форма може містити дані, що ввів користувач.\nЩоб зробити це, ви можете встановити опцію [[yii\\widgets\\FragmentCache::enabled|enabled]] наступним чином:\n\n```php\nif ($this->beginCache($id, ['enabled' => Yii::$app->request->isGet])) {\n\n    // ... тут створюємо вміст ...\n\n    $this->endCache();\n}\n```\n\n\n## Вкладене кешування <span id=\"nested-caching\"></span>\n\nКешування фрагментів може бути вкладеним. Тобто, кешований фрагмент може бути укладений в інший фрагмент, що також кешується.\nНаприклад, коментарі кешируются у внутрішньому фрагменті кешу, і вони ж кешуються разом із вмістом поста у зовнішньому \nфрагменті кеша. Наступний код показує, як два кеша фрагментів можуть бути вкладеними:\n\n```php\nif ($this->beginCache($id1)) {\n\n    // ...логіка створення контента...\n\n    if ($this->beginCache($id2, $options2)) {\n\n        // ...логіка створення контента...\n\n        $this->endCache();\n    }\n\n    // ...логіка створення контента...\n\n    $this->endCache();\n}\n```\n\nДля вкладених кешів можут бути встановлені різні опції кешування. Наприклад, внутрішні кеші і зовнішні кеші можуть використовувати\nрізні значення тривалості кеша. Навіть коли дані зовнішнього кеша вже є недійсними, внутрішній кеш все ще може містити актуальний фрагмент.\nТим не менш, це не є вірним у зворотньому напрямку. Якщо зовнішній кеш є дійсним, то він буде продовжувати віддавати кешовану копію,\nнавіть якщо внутрішній кеш є недійсним. Таким чином, ви повинні бути обережні у визначенні тривалості або залежностей вкладених кешей, \nв іншому випадку застарілі внутрішні фрагменти можуть зберігатися в зовнішньому фрагменті.\n\n\n## Динамічний вміст <span id=\"dynamic-content\"></span>\n\nПри використанні кешування фрагментів, ви можете зіткнутися із ситуацією, коли великий фрагмент вмісту є відносно статичним,\nза винятком одного або декількох місць. Наприклад, заголовок сторінки може відображатися у головному меню разом \nз імʼям поточного користувача. Інша проблема в тому, що закешований вміст може містити код PHP, який повинен \nвиконуватися при кожному запиті (наприклад, код для реєстрації колекції ресурсів). Обидві ці проблеми можуть бути \nвирішені за допомогою так званої функції *динамічного вмісту*.\n\nДинамічний вміст означає фрагмент виведення, який не повинен кешуватися, навіть якщо він укладений у кешування фрагментів.\nДля того, щоб зробити вміст динамічним постійно, він повинен бути створений за допомогою деякого коду PHP для кожного запиту,\nнавіть якщо вміст віддаєтся із кеша.\n\nВи можете викликати [[yii\\base\\View::renderDynamic()]] в кеші фрагмента для вставки динамічного контенту у потрібне місце,\nяк у прикладі нижче:\n\n```php\nif ($this->beginCache($id1)) {\n\n    // ...логіка створення контента...\n\n    echo $this->renderDynamic('return Yii::$app->user->identity->name;');\n\n    // ...логіка створення контента...\n\n    $this->endCache();\n}\n```\n\nМетод [[yii\\base\\View::renderDynamic()|renderDynamic()]] бере деяку частину коду PHP в якості параметра.\nЗначення, що повертається від коду PHP, трактується як динамічний вміст. Цей код PHP буде виконуватися при кожному запиті,\nнезалежно від того, чи віддається фрагмент із кешу або ні.\n"
  },
  {
    "path": "docs/guide-uk/concept-aliases.md",
    "content": "Псевдоніми\n==========\n\nПсевдоніми використовуються для представлення шляхів до файлів або URL адрес \nі допомагають уникнути використання абсолютних шляхів або URL у коді. \nДля того, щоб не переплутати псевдонім із звичайним шляхом до файлу або URL, \nвін повинен починатися із символу `@`. Якщо псевдонім не починається із\nсимволу `@` - його буде додано автоматично.\n\nВ Yii є багато заздалегідь визначених псевдонімів. Наприклад, \n`@yii` вказує на директорію, в яку був встановлений  Yii framework, \nа `@web` можна використовувати для отримання базового URL поточного додатку.\n \nСтворення псевдонімів <span id=\"defining-aliases\"></span>\n---------------------\n\nДля створення псевдоніма шляху до файлу або URL використовується метод [[Yii::setAlias()]]:\n\n```php\n// псевдонім шляху до файлу\nYii::setAlias('@foo', '/path/to/foo');\n\n// псевдонім URL\nYii::setAlias('@bar', 'https://www.example.com');\n```\n\n> Note: псевдонім шляху до файлу або URL *не* обовʼязково вказує на наявний файл або ресурс.\n\nВикористовуючи вже заданий псевдонім, ви можете отримати на основі нього новий (без виклику [[Yii::setAlias()]]),\nдодавши в його кінець `/`, за яким слідує один або більше сегментів шляху. Псевдоніми, визначені за допомогою\n[[Yii::setAlias()]], є *кореневими псевдонімами*, в той час як отримані з них називаються *похідними псевдонімами*.\nНа приклад, `@foo` є кореневим псевдонімом, а `@foo/bar/file.php` — похідним.\n\nВи можете задати новий псевдонім, використовуючи раніше створений псевдонім (не важливо, кореневий він чи похідний):\n\n```php\nYii::setAlias('@foobar', '@foo/bar');\n```\n\nКореневі псевдоніми, як правило, створюються на етапі [попереднього завантаження (bootstrapping)](runtime-bootstrapping.md).\nНаприклад, ви можете викликати [[Yii::setAlias()]] у [вхідному скрипті](structure-entry-scripts.md). Для зручності, в\n[додатку (Application)](structure-applications.md) передбачено властивість `aliases`, яке можна задати через\n[конфігурацію додатку](concept-configurations.md):\n\n```php\nreturn [\n    // ...\n    'aliases' => [\n        '@foo' => '/path/to/foo',\n        '@bar' => 'https://www.example.com',\n    ],\n];\n```\n\n\nПеретворення псевдонімів <span id=\"resolving-aliases\"></span>\n------------------------\n\nВиклик [[Yii::getAlias()]] перетворює кореневий псевдонім в шлях до файлу або URL, який цей псевдонім представляє.\nЦей же метод може працювати і з похідними псевдонімами:\n\n```php\necho Yii::getAlias('@foo');               // виведе: /path/to/foo\necho Yii::getAlias('@bar');               // виведе: https://www.example.com\necho Yii::getAlias('@foo/bar/file.php');  // виведе: /path/to/foo/bar/file.php\n```\n\nШлях або URL, представлений похідним псевдонімом, визначається шляхом заміни в ньому частині, що відповідає кореневого псевдоніму, \nна відповідний йому шлях або URL.\n\n> Note: Метод [[Yii::getAlias()]] не перевіряє фактичного існування одержуваного шляху або URL.\n\nКореневий псевдонім може містити знаки `/`. При цьому метод [[Yii::getAlias()]] коректно визначить, яка частина \nпсевдоніма є кореневої і вірно сформує шлях або URL:\n\n```php\nYii::setAlias('@foo', '/path/to/foo');\nYii::setAlias('@foo/bar', '/path2/bar');\nYii::getAlias('@foo/test/file.php');  // виведе: /path/to/foo/test/file.php\nYii::getAlias('@foo/bar/file.php');   // виведе: /path2/bar/file.php\n```\n\nЯкби `@foo/bar` не був оголошений кореневим псевдонімом, остання строка вивела б `/path/to/foo/bar/file.php`.\n\n\nВикористання псевдонімів <span id=\"using-aliases\"></span>\n------------------------\n\nПсевдоніми розпізнаються в багатьох частинах Yii без необхідності попереднього виклику [[Yii::getAlias()]]\nдля отримання шляху або URL. Наприклад, [[yii\\caching\\FileCache::cachePath]] приймає як звичайний шлях до файлу, \nтак і псевдонім шляху завдяки префіксу `@`, який дозволяє їх розрізняти.\n\n```php\nuse yii\\caching\\FileCache;\n\n$cache = new FileCache([\n    'cachePath' => '@runtime/cache',\n]);\n```\n\nДля того, щоб дізнатися чи підтримує метод або властивість псевдоніми, зверніться до документації API.\n\n\nЗаздалегідь визначені псевдоніми <span id=\"predefined-aliases\"></span>\n--------------------------------\n\nВ Yii заздалегідь визначені псевдоніми для часто використовуваних шляхів до файлів і URL:\n\n- `@yii`: директорія, в якій знаходиться файл `BaseYii.php` (директорія фреймворку).\n- `@app`: [[yii\\base\\Application::basePath|базовий шлях]] поточного додатку.\n- `@runtime`: [[yii\\base\\Application::runtimePath|директорія runtime]] поточного додатку. За замовчуванням `@app/runtime`.\n- `@webroot`, коренева веб-директорія поточного веб-додатку. Визначається на основі директорії розташування [вхідного скрипта](structure-entry-scripts.md).\n- `@web`, базовий URL поточного додатку. Має таке ж значення, як і [[yii\\web\\Request::baseUrl]].\n- `@vendor`, [[yii\\base\\Application::vendorPath|директорія vendor Composer]]. За замовчуванням `@app/vendor`.\n- `@bower`, директорія, що містить [пакунки Bower](https://bower.io/). За замовчуванням `@vendor/bower`.\n- `@npm`, директорія, що містить [пакунки NPM](https://www.npmjs.com/). За замовчуванням `@vendor/npm`.\n\nПсевдонім `@yii` задається в момент підключення файлу `Yii.php` у [вхідному скрипті](structure-entry-scripts.md).\nРешта псевдонімів задаються в конструкторі додатка в момент застосування [конфигурації](concept-configurations.md).\n\n\nПсевдоніми розширень <span id=\"extension-aliases\"></span>\n--------------------\n\nДля кожного [розширення](structure-extensions.md), що встановлюється через Composer, автоматично задається псевдонім. \nЙого імʼя відповідає кореневому простору імен розширення відповідно до його `composer.json`, і кожен псевдонім представляє \nшлях до кореневої директорії пакунка. Наприклад, якщо ви встановите розширення `yiisoft/yii2-jui`, \nто вам автоматично стане доступним псевдонім `@yii/jui`, який будет створено на етапі \n[первинного завантаження (bootstrapping)](runtime-bootstrapping.md) наступним чином:\n\n```php\nYii::setAlias('@yii/jui', 'VendorPath/yiisoft/yii2-jui');\n```\n"
  },
  {
    "path": "docs/guide-uk/concept-autoloading.md",
    "content": "Автозавантаження класів\n=======================\n\nПошук і підключення файлів класів в Yii реалізовано за допомогою\n[автозавантаження класів](https://www.php.net/manual/ru/language.oop5.autoload.php).\nФреймворк надає власний швидкісний автозавантажувач, що сумісний з \n[PSR-4](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md),\nякий встановлюється в момент підключення файлу `Yii.php`.\n\n> Note: Для простоти опису, в цьому розділі ми будемо говорити тільки про автозавантаження класів.\nТим не менш, все описане також стосується до інтерфейсів і трейтів.\n\n\nВикористання автозавантажувача Yii <span id=\"using-yii-autoloader\"></span>\n----------------------------------\n\nДля використання автозавантажувача класів Yii слід дотримуватися два простих правила створення і іменування класів:\n\n* Кожен клас повинен належати до [простору імен](https://www.php.net/manual/en/language.namespaces.php) (наприклад, `foo\\bar\\MyClass`)\n* Кожен клас повинен знаходитися в окремому файлі, шлях до якого визначаться наступним правилом:\n\n```php\n// $className — це абсолютне імʼя класу без початкового \"\\\"\n$classFile = Yii::getAlias('@' . str_replace('\\\\', '/', $className) . '.php');\n```\n\nНаприклад, якщо абсолютне імʼя класу `foo\\bar\\MyClass`, то [псевдонім шляху](concept-aliases.md) даного файлу класу буде\n`@foo/bar/MyClass.php`. Для того, щоб даний псевдонім можна було перетворити в шлях до файлу, необхідно щоб або `@foo` \nабо `@foo/bar` був [кореневим псевдонімом](concept-aliases.md#defining-aliases).\n\nПри використанні [базового шаблону додатка](start-installation.md) ви можете зберігати свої класи в просторі імен \nверхнього рівня `app`, щоб вони могли бути автоматично завантажені Yii без створення нового псевдоніма. \nЦе працює, тому як `@app` є [заздалегідь визначеним псевдонімом](concept-aliases.md#predefined-aliases) і таке імʼя класу,\nяк `app\\components\\MyClass` відповідно до описаного вище алготімом перетвориться в шлях `AppBasePath/components/MyClass.php`.\n\nУ [розширеному шаблоні додатка](tutorial-advanced-app.md) кожен рівень додатку володіє власним кореневим псевдонімом. \nНаприклад, для frontend кореневим псевдонімом є `@frontend`, а для backend — `@backend`. Це дозволяє розмістити класи \nfrontend в простір імен `frontend`, а класи backend - в простір імен `backend`. \nЦе дозволить вказаним класам бути автоматично завантаженими засобами Yii.\n\nМапа класів <span id=\"class-map\"></span>\n-----------\n\nАвтозавантажувач Yii підтримує *мапу класів*. Ця можливість дозволяє вказати шлях до файлу для кожного імені класу. \nПри завантаженні класу автозавантажувач перевіряє наявність класу в мапі. Якщо він там є, відповідний файл буде завантажений \nбезпосередньо без будь-яких додаткових перевірок. Це робить автозагрузку дуже швидкою. Всі класи самого фреймворку \nзавантажуються саме цим способом.\n\nВи маєте можливість додати клас в мапу `Yii::$classMap` наступним чином:\n\n```php\nYii::$classMap['foo\\bar\\MyClass'] = 'path/to/MyClass.php';\n```\n\nДля вказівки шляхів до файлів класів можна використовувати [псевдоніми](concept-aliases.md). Мапу класів необхідно сформувати\nв процесі [початкового завантаження](runtime-bootstrapping.md), бо вона повинна бути сформована до використання класів.\n\n\nВикористання інших автозавантажувачів <span id=\"using-other-autoloaders\"></span>\n-------------------------------------\n\nОскільки Yii використовує Composer у якості менеджера залежностей, рекомендується додатково встановити його автозавантажувач.\nЯкщо ви використовуєте які-небудь сторонні бібліотеки із власними автозавантажувачами, то ці автозавантажувачі також необхідно \nвстановити.\n\nПри використанні додаткових автозавантажувачів, файл `Yii.php` повинен бути підключений *після* їх установки. \nЦе дозволить автозавантажувачу Yii першим намагатися завантажити клас. Наприклад, наведений нижче код взятий з\n[вхідного скрипта](structure-entry-scripts.md) [базового шаблону додатка](start-installation.md). \nПерший рядок встановлює автозавантажувач Composer, а другий - автозавантажувач Yii:\n\n```php\nrequire __DIR__ . '/../vendor/autoload.php';\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n```\n\nВи можете використовувати автозавантажувач Composer без автозавантажувачa Yii, однак, швидкість автозавантаження в \nцьому випадку може зменшиться. До того ж вам буде необхідно слідувати правилам автозавантажувача Composer.\n\n> Info: Якщо ви не хочете використовувати автозавантажувач Yii, створіть свою версію файлу `Yii.php`\nі підключіть його у [вхідному скрипті](structure-entry-scripts.md).\n\n\nАвтозавантаження класів розширень <span id=\"autoloading-extension-classes\"></span>\n---------------------------------\n\nАвтозавантажувач Yii може автоматично завантажувати класи [розширень](structure-extensions.md), при умові виконання вимоги\nкоректного визначення секції `autoload` у файлі `composer.json`. Більш докладно про це можна дізнатися з \n[офіційної документації Composer](https://getcomposer.org/doc/04-schema.md#autoload).\n\nЯкщо ви не використовуєте автозавантажувач Yii, то класи розширень можуть бути автоматично завантажені з допомогою автозавантажувач Composer.\n"
  },
  {
    "path": "docs/guide-uk/images/application-lifecycle.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:java=\"http://www.yworks.com/xml/yfiles-common/1.0/java\" xmlns:sys=\"http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0\" xmlns:x=\"http://www.yworks.com/xml/yfiles-common/markup/2.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.14.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"570.5239308675131\" width=\"763.2772213171534\" x=\"-1269.9373595143054\" y=\"-206.25105539957679\"/>\n              <y:Fill color=\"#FFCC0024\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FFCC00\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.453125\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"763.2772213171534\" x=\"0.0\" y=\"0.0\">Вхідний скрипт (index.php або yii)</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"225.33495140075684\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.453125\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"68.19677734375\" x=\"-9.098388671875\" y=\"0.0\">Folder 4</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n0:\">\n        <node id=\"n0::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"324.9258883570935\" x=\"-1249.511914911339\" y=\"-169.79793039957679\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"233.646484375\" x=\"45.63970199104665\" y=\"6.015625\">Завантаження конфігурації додатка<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n0::n1\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"308.45312499999994\" width=\"330.35133296005984\" x=\"-1254.9373595143054\" y=\"36.196215311686274\"/>\n                  <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.453125\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"330.35133296005984\" x=\"0.0\" y=\"0.0\">Створення екземпляру додатка</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"19\" bottomF=\"19.15489692687993\" left=\"5\" leftF=\"5.425444602966309\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.453125\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"68.19677734375\" x=\"-9.098388671875\" y=\"0.0\">Folder 5</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n0::n1:\">\n            <node id=\"n0::n1::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"72.64934031168627\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"55.01171875\" x=\"119.95708480354665\" y=\"6.015625\">preInit()<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n1\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"122.64524027506516\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"202.978515625\" x=\"45.97368636604665\" y=\"6.015625\">Реєстрація обробника помилок<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n2\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"174.96110788981125\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"242.13671875\" x=\"26.39458480354665\" y=\"6.015625\">Налаштування властивостей додатка<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n3\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"226.56779181162517\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"33.58984375\" x=\"130.66802230354665\" y=\"6.015625\">init()<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n4\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.9258883570935\" x=\"-1234.511914911339\" y=\"280.4944433848063\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"71.869140625\" x=\"111.52837386604665\" y=\"6.015625\">bootstrap()<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n        <node id=\"n0::n2\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"411.7397511800131\" width=\"324.9258883570935\" x=\"-846.5860265542456\" y=\"-168.87459055582679\"/>\n                  <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.453125\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"324.9258883570935\" x=\"0.0\" y=\"0.0\">Виконання додатка</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"1.1368683772161603E-13\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.453125\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"68.19677734375\" x=\"-9.098388671875\" y=\"0.0\">Folder 3</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n0::n2:\">\n            <node id=\"n0::n2::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.9258883570935\" x=\"-831.5860265542456\" y=\"-132.42146555582667\"/>\n                  <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"176.95703125\" x=\"58.98442855354676\" y=\"6.015625\">EVENT_BEFORE_REQUEST<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n2::n1\" yfiles.foldertype=\"group\">\n              <data key=\"d4\"/>\n              <data key=\"d6\">\n                <y:ProxyAutoBoundsNode>\n                  <y:Realizers active=\"0\">\n                    <y:GroupNode>\n                      <y:Geometry height=\"203.453125\" width=\"294.9258883570935\" x=\"-831.5860265542456\" y=\"-77.87459055582679\"/>\n                      <y:Fill color=\"#99336635\" transparent=\"false\"/>\n                      <y:BorderStyle hasColor=\"false\" type=\"dashed\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#993366\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.453125\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#FFFFFF\" visible=\"true\" width=\"294.9258883570935\" x=\"0.0\" y=\"0.0\">Обробка запиту</y:NodeLabel>\n                      <y:Shape type=\"roundrectangle\"/>\n                      <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                      <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                      <y:BorderInsets bottom=\"8\" bottomF=\"7.929194132486941\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                    </y:GroupNode>\n                    <y:GroupNode>\n                      <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                      <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.453125\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"68.19677734375\" x=\"-9.098388671875\" y=\"0.0\">Folder 4</y:NodeLabel>\n                      <y:Shape type=\"roundrectangle\"/>\n                      <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                      <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                      <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                    </y:GroupNode>\n                  </y:Realizers>\n                </y:ProxyAutoBoundsNode>\n              </data>\n              <graph edgedefault=\"directed\" id=\"n0::n2::n1:\">\n                <node id=\"n0::n2::n1::n0\">\n                  <data key=\"d6\">\n                    <y:ShapeNode>\n                      <y:Geometry height=\"30.0\" width=\"264.9258883570935\" x=\"-816.5860265542456\" y=\"-41.421465555826785\"/>\n                      <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"261.44921875\" x=\"1.7383348035467634\" y=\"6.015625\">Розбір запиту на маршрут та параметри<y:LabelModel>\n                          <y:SmartNodeLabelModel distance=\"4.0\"/>\n                        </y:LabelModel>\n                        <y:ModelParameter>\n                          <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                        </y:ModelParameter>\n                      </y:NodeLabel>\n                      <y:Shape type=\"rectangle\"/>\n                    </y:ShapeNode>\n                  </data>\n                </node>\n                <node id=\"n0::n2::n1::n1\">\n                  <data key=\"d6\">\n                    <y:ShapeNode>\n                      <y:Geometry height=\"30.0\" width=\"264.9258883570935\" x=\"-816.5860265542456\" y=\"18.578534444173215\"/>\n                      <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"240.109375\" x=\"12.408256678546763\" y=\"6.015625\">Створення модуля, контролера та дії<y:LabelModel>\n                          <y:SmartNodeLabelModel distance=\"4.0\"/>\n                        </y:LabelModel>\n                        <y:ModelParameter>\n                          <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                        </y:ModelParameter>\n                      </y:NodeLabel>\n                      <y:Shape type=\"rectangle\"/>\n                    </y:ShapeNode>\n                  </data>\n                </node>\n                <node id=\"n0::n2::n1::n2\">\n                  <data key=\"d6\">\n                    <y:ShapeNode>\n                      <y:Geometry height=\"30.0\" width=\"264.9258883570935\" x=\"-816.5860265542456\" y=\"72.64934031168627\"/>\n                      <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"93.138671875\" x=\"85.89360824104676\" y=\"6.015625\">Виконання дії<y:LabelModel>\n                          <y:SmartNodeLabelModel distance=\"4.0\"/>\n                        </y:LabelModel>\n                        <y:ModelParameter>\n                          <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                        </y:ModelParameter>\n                      </y:NodeLabel>\n                      <y:Shape type=\"rectangle\"/>\n                    </y:ShapeNode>\n                  </data>\n                </node>\n              </graph>\n            </node>\n            <node id=\"n0::n2::n2\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.9258883570935\" x=\"-831.5860265542456\" y=\"149.20206960042316\"/>\n                  <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"166.2109375\" x=\"64.35747542854676\" y=\"6.015625\">EVENT_AFTER_REQUEST<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n2::n3\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-831.5860265542456\" y=\"196.89641062418633\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"31.9375\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"152.072265625\" x=\"71.42681136604676\" y=\"-0.96875\">Відправлення відповіді кінцевому користувачу<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n        <node id=\"n0::n3\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"324.9258883570935\" x=\"-846.5860265542456\" y=\"319.2728754679363\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"185.095703125\" x=\"69.91509261604676\" y=\"6.015625\">Завершення обробки запиту<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <edge id=\"e0\" source=\"n0\" target=\"n0::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-179.83580569315376\" sy=\"-180.3944529152355\" tx=\"-13.869777491410105\" ty=\"-154.8008369539754\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::e0\" source=\"n0::n0\" target=\"n0::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#99CCFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"128.259765625\" x=\"-141.86749983556274\" y=\"79.01269531250006\">Масив конфігурації<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"77.04379339868619\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e0\" source=\"n0::n1::n0\" target=\"n0::n1::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e1\" source=\"n0::n1::n1\" target=\"n0::n1::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e2\" source=\"n0::n1::n2\" target=\"n0::n1::n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e3\" source=\"n0::n1::n3\" target=\"n0::n1::n4\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::e1\" source=\"n0::n1\" target=\"n0::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"2.8060680342755404\" sy=\"-48.37646484374994\" tx=\"-162.49660512430125\" ty=\"105.53540293375653\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::e0\" source=\"n0::n2::n0\" target=\"n0::n2::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::n1::e0\" source=\"n0::n2::n1::n0\" target=\"n0::n2::n1::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::n1::e1\" source=\"n0::n2::n1::n1\" target=\"n0::n2::n1::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::e1\" source=\"n0::n2::n1\" target=\"n0::n2::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::e2\" source=\"n0::n2::n2\" target=\"n0::n2::n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::e2\" source=\"n0::n2\" target=\"n0::n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#99CCFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"94.404296875\" x=\"-111.20212290304266\" y=\"28.228032788725613\">Статус виходу<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"63.99999999999999\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.47945569632951074\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources/>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-uk/images/application-structure.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:java=\"http://www.yworks.com/xml/yfiles-common/1.0/java\" xmlns:sys=\"http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0\" xmlns:x=\"http://www.yworks.com/xml/yfiles-common/markup/2.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.14.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"872.1807999999999\" y=\"-14.764159999999947\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"31.9375\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"73.017578125\" x=\"13.4912109375\" y=\"1.53125\">компонент\nдодатка<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"-91.97375999999994\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"31.9375\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"52.744140625\" x=\"23.6279296875\" y=\"1.53125\">вхідний\nскрипт<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"-14.764159999999947\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"54.51953125\" x=\"22.740234375\" y=\"8.515625\">додаток<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"62.44544000000004\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"70.673828125\" x=\"14.6630859375\" y=\"8.515625000000007\">контролер<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"872.1807999999999\" y=\"62.44544000000005\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"45.70703125\" x=\"27.146484375\" y=\"8.515625\">фільтр<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"532.664\" y=\"23.901600000000087\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"49.1640625\" x=\"25.41796875\" y=\"8.515625\">модуль<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"618.4047999999991\" y=\"139.65504000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"99.73046875\" x=\"0.134765625\" y=\"8.515625\">представлення<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"786.161599999999\" y=\"139.65504000000004\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"49.205078125\" x=\"25.3974609375\" y=\"8.515625\">модель<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n8\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"532.664\" y=\"216.86464000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"46.76171875\" x=\"26.619140625\" y=\"8.515625\">віджет<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n9\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"216.86464000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"31.9375\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"60.07421875\" x=\"19.962890625\" y=\"1.53125\">колекція\nресурсів<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n2\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"23.3125\" x=\"2.369325683593729\" y=\"-25.50056340427648\">1:1<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.025600000000054\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.17992697197425173\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n0\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-49.11136368282337\" y=\"-23.00997508216852\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.025599999999994\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5470891790487767\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n5\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"15.0445484043222\" y=\"30.756096771861735\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"44.894496863129426\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.25416574623968574\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n3\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"2.6225397460937074\" y=\"-25.9149367156797\">1..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"15.254400000000032\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.2090245199765919\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n3\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-57.41127147104078\" y=\"-63.734752657990555\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"43.47658308018222\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.881412889692355\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n5\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"582.664\" y=\"-3.598399999999913\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-18.037919628906252\" y=\"-42.33644914245597\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"16.483200000000004\" distanceToCenter=\"true\" position=\"right\" ratio=\"5.822600000000001\" segment=\"-2\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n6\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-12.453120279866653\" y=\"-24.611373055849555\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"11.385360101663515\" distanceToCenter=\"true\" position=\"left\" ratio=\"0.10670293331985262\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e7\" source=\"n7\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-9.3884511336189\" y=\"-25.947659244081734\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.669798038586146\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.1670192242704811\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e8\" source=\"n9\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-9.871202356268668\" y=\"-25.673070518330803\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.15599378957306\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.15481212573620068\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e9\" source=\"n8\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-15.868632344503453\" y=\"-23.891138042957834\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"13.24330808446041\" distanceToCenter=\"true\" position=\"left\" ratio=\"0.05506723556297327\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e10\" source=\"n4\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-47.487035253906356\" y=\"-22.600377199706998\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"13.616000000000007\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e11\" source=\"n9\" target=\"n8\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-44.441396598124356\" y=\"-19.52837428222651\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.543999999999983\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.4117077892835431\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e12\" source=\"n7\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-44.9806249718132\" y=\"-19.528379555664003\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.543999999999983\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.4531592446547025\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources/>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-uk/images/request-lifecycle.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"70.13700103759766\" width=\"56.558998107910156\" x=\"198.38633947503078\" y=\"261.099458694458\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"26.279499053955078\" y=\"74.13700103759766\">\n            <y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"-0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"0.5\" offsetX=\"0.0\" offsetY=\"4.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"27.34375\" x=\"-31.34375\" y=\"25.717914581298828\">користувач<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.5\" labelRatioY=\"0.0\" nodeRatioX=\"-0.5\" nodeRatioY=\"0.0\" offsetX=\"-4.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"637.1271178722382\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"41.158203125\" y=\"13.1494140625\">модель<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"46.887996673583984\" width=\"39.527000427246094\" x=\"828.8018617630005\" y=\"544.9831195354463\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"53.376953125\" x=\"-6.924976348876953\" y=\"-30.723247528076172\">база даних<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-12.022075653076172\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"17.763500213623047\" y=\"21.443998336791992\">\n            <y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"713.6271178722382\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"28.005859375\" x=\"45.4970703125\" y=\"13.1494140625\">представлення<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n4\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"377.0372841596604\" width=\"219.27949905395508\" x=\"527.4919853210449\" y=\"407.9266515731811\"/>\n              <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"219.27949905395508\" x=\"0.0\" y=\"0.0\">контролер</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"2\" leftF=\"1.7335329055786133\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"412.2765645980835\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 1</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n4:\">\n        <node id=\"n4::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"445.3031164169311\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"72.70703125\" x=\"38.92598342895508\" y=\"5.6494140625\">створення дії<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n4::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"45.0\" width=\"159.91864204406738\" x=\"558.5326642990112\" y=\"521.8166599273682\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"77.34765625\" x=\"41.28549289703369\" y=\"13.1494140625\">накладання\nфільтрів<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"diamond\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n4::n2\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"162.71328270435333\" width=\"187.54596614837646\" x=\"544.2255182266235\" y=\"607.2506530284882\"/>\n                  <y:Fill color=\"#99336635\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#993366\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#FFFFFF\" visible=\"true\" width=\"187.54596614837646\" x=\"0.0\" y=\"0.0\">дія</y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"4\" bottomF=\"3.8368178606033325\" left=\"4\" leftF=\"3.9869680404663086\" right=\"3\" rightF=\"3.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 3</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n4::n2:\">\n            <node id=\"n4::n2::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"644.6271178722382\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.705078125\" x=\"43.92695999145508\" y=\"5.6494140625\">завантаження моделі<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n4::n2::n1\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"721.1271178722382\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"66.02734375\" x=\"42.26582717895508\" y=\"5.6494140625\">відображення\nпредставлення<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n      </graph>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"198.3863394750308\" y=\"713.6271178722382\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"116.072265625\" x=\"1.4638671875000284\" y=\"13.1494140625\">компонент\nвідповіді<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"254.50096702575684\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"106.732421875\" x=\"6.1337890625\" y=\"13.1494140625\">компонент\nзапиту<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"144.132230758667\" width=\"219.27949905395508\" x=\"527.4919853210449\" y=\"222.86873626708984\"/>\n              <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"219.27949905395508\" x=\"0.0\" y=\"0.0\">додаток</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"21\" leftF=\"20.720500946044922\" right=\"18\" rightF=\"18.0\" top=\"2\" topF=\"1.7557659149169922\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 2</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n7:\">\n        <node id=\"n7::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"262.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"73.369140625\" x=\"38.59492874145508\" y=\"5.6494140625\">визначення маршруту<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n7::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"322.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"90.0390625\" x=\"30.259967803955078\" y=\"5.6494140625\">створення контролера<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <node id=\"n8\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"142.37646484375\" width=\"159.91864204406738\" x=\"313.2978515625\" y=\"224.62450218200684\"/>\n              <y:Fill color=\"#FFCC0024\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FFCC00\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"159.91864204406738\" x=\"0.0\" y=\"0.0\">вхідний скрипт</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"1\" leftF=\"0.9186420440673828\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"225.33495140075684\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 4</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n8:\">\n        <node id=\"n8::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"129.0\" x=\"329.2164936065674\" y=\"262.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"85.3984375\" x=\"21.80078125\" y=\"5.6494140625\">завантаження\nконфігурації<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n8::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"129.0\" x=\"329.2164936065674\" y=\"322.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"82.052734375\" x=\"23.4736328125\" y=\"5.6494140625\">запуск додатка<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <edge id=\"e0\" source=\"n2\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n5\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-31.22050094604495\" sy=\"-22.49430537223816\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"28.00000600945461\" y=\"-193.17557203769684\">\n            <y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"17.34765625\" x=\"-8.673822115545391\" y=\"-200.52615797519684\">11<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n6\" target=\"n7::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-29.525518994973595\" y=\"-10.349628448486328\">3<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"0.9990329742431641\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.25395048761528816\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n7::e0\" source=\"n7::n0\" target=\"n7::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n0\" target=\"n8\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"-79.9882439360955\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"36.76878085201736\" y=\"-9.523307113000556\">1<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n7::n1\" target=\"n4\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"2.408647025755731\" ty=\"-181.21658369302781\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-4.962140296002758\" y=\"18.61224679946895\">4<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n3\" target=\"n4::n2::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-30.100868416252297\" y=\"-9.35060429573059\">9<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.26448415477215953\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n4::n2::n1\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"-2.2737367544323206E-13\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"17.34765625\" x=\"-83.18526519030615\" y=\"-9.350604295730818\">10<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.2786124840137136\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e7\" source=\"n8::n1\" target=\"n7\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"55.750299312644984\" sy=\"0.355224609375\" tx=\"-109.63961141576266\" ty=\"42.066115379333496\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"29.326010704040527\" y=\"-9.508390885372137\">2<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e8\" source=\"n1\" target=\"n4::n2::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-31.270312699786246\" y=\"-10.35060429573059\">8<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"1.0\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.2858946861565087\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e9\" source=\"n4::n1\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-79.9882439360951\" sy=\"2.2737367544323206E-13\" tx=\"30.048898971244075\" ty=\"-1.4999999999999991\">\n            <y:Point x=\"287.93523844627487\" y=\"544.3166599273684\"/>\n          </y:Path>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-76.70009317381192\" y=\"-9.350576400756609\">6<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.23459266172695797\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::e0\" source=\"n4::n0\" target=\"n4::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFEFD6\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-5.336933135986328\" y=\"4.999985313415493\">5<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.0\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::e1\" source=\"n4::n1\" target=\"n4::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"-81.01828954355172\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFEFD6\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-5.4491733540852465\" y=\"5.039560317993164\">7<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.0\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::n2::e0\" source=\"n4::n2::n0\" target=\"n4::n2::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n8::e0\" source=\"n8::n0\" target=\"n8::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"66px\" viewBox=\"0 0 57 66\" enable-background=\"new 0 0 57 66\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3799\" y1=\"-2276.8809\" x2=\"27.6209\" y2=\"-2306.6792\" gradientTransform=\"matrix(1 0 0 -1 0.2803 -2252.9199)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_13_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t&lt;path fill=\"#2068A3\" stroke=\"#2068A3\" d=\"M28.106,33.487c-8.112,0-12.688,4.312-12.688,10.437c0,7.422,12.688,10.438,12.688,10.438\n\t\ts14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.487,28.106,33.487z M26.288,53.051c0,0-7.135-2.093-8.805-7.201\n\t\tc-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"14.2417\" cy=\"9.1006\" r=\"53.247\" gradientTransform=\"matrix(1 0 0 -1 0.04 65.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#74AEEE\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#2068A3\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#2068A3\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.022,7.807-14.022,7.807s-10.472-2.484-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path fill=\"#5491CF\" stroke=\"#2068A3\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397c-0.514,1.027-1.669,4.084-1.669,5.148\n\t\tc0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.335-2.36c-3.601-1.419-4.071-3.063-5.89-4.854\n\t\tC12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t&lt;path fill=\"#5491CF\" stroke=\"#2068A3\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617c0.516,1.025,3.617,3.693,3.617,6.617\n\t\tc0,5.186-10.27,8.576-16.698,9.145c1.429,4.938,11.372,1.293,13.804-0.313c3.563-2.354,4.563-5.133,7.854-3.705\n\t\tC47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t&lt;path fill=\"none\" stroke=\"#2068A3\" stroke-linecap=\"round\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t&lt;path fill=\"none\" stroke=\"#2068A3\" stroke-linecap=\"round\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.623\" cy=\"-2278.646\" r=\"23.425\" fx=\"23.0534\" fy=\"-2281.1357\" gradientTransform=\"matrix(1 0 0 -1 0.2803 -2252.9199)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"5761.7578\" y1=\"11330.6484\" x2=\"5785.3872\" y2=\"11424.0977\" gradientTransform=\"matrix(0.275 0 0 0.2733 -1558.9874 -3088.4209)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M27.958,6.333c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.083,13.952,36.271,6.268,27.958,6.333z\"/&gt;\n\t&lt;path id=\"Hair_Young_Brown_1_\" fill=\"#CC9869\" stroke=\"#99724F\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n\t&lt;path fill=\"#4B4B4B\" stroke=\"#4B4B4B\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" d=\"M28.105,2\n\t\tC22.464,2,20.2,4.246,18.13,5.533C29.753,2.865,41.152,10.375,44.46,20.5C44.459,16.875,44.459,2,28.105,2z\"/&gt;\n\t&lt;path fill=\"#9B9B9B\" stroke=\"#4B4B4B\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" d=\"M11.151,17.751\n\t\tC12.878,8.25,18.686,6.309,25.273,7.127C31.295,7.875,36.93,10.491,44.459,20.5C37.777,7.125,20.278-3.375,9.903,3.921\n\t\tC5.569,6.97,4.903,13.375,11.151,17.751z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\"\n\t xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\t x=\"0px\" y=\"0px\" width=\"41px\" height=\"48px\" viewBox=\"-0.875 -0.887 41 48\" enable-background=\"new -0.875 -0.887 41 48\"\n\t xml:space=\"preserve\"&gt;\n&lt;defs&gt;\n&lt;/defs&gt;\n&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-979.1445\" x2=\"682.0508\" y2=\"-979.1445\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_1_)\" d=\"M19.625,36.763C8.787,36.763,0,34.888,0,32.575v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,34.888,30.464,36.763,19.625,36.763z\"/&gt;\n&lt;linearGradient id=\"SVGID_2_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-973.1445\" x2=\"682.0508\" y2=\"-973.1445\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_2_)\" d=\"M19.625,36.763c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.927-18.396,3.927\n\tc-9.481,0-17.396-1.959-18.396-3.927l-1.229,2C0,34.888,8.787,36.763,19.625,36.763z\"/&gt;\n&lt;path fill=\"#3C89C9\" d=\"M19.625,26.468c10.16,0,19.625,2.775,19.625,2.775c-0.375,2.721-5.367,5.438-19.554,5.438\n\tc-12.125,0-18.467-2.484-19.541-4.918C-0.127,29.125,9.465,26.468,19.625,26.468z\"/&gt;\n&lt;linearGradient id=\"SVGID_3_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-965.6948\" x2=\"682.0508\" y2=\"-965.6948\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_3_)\" d=\"M19.625,23.313C8.787,23.313,0,21.438,0,19.125v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,21.438,30.464,23.313,19.625,23.313z\"/&gt;\n&lt;linearGradient id=\"SVGID_4_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-959.6948\" x2=\"682.0508\" y2=\"-959.6948\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_4_)\" d=\"M19.625,23.313c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.926-18.396,3.926\n\tc-9.481,0-17.396-1.959-18.396-3.926l-1.229,2C0,21.438,8.787,23.313,19.625,23.313z\"/&gt;\n&lt;path fill=\"#3C89C9\" d=\"M19.476,13.019c10.161,0,19.625,2.775,19.625,2.775c-0.375,2.721-5.367,5.438-19.555,5.438\n\tc-12.125,0-18.467-2.485-19.541-4.918C-0.277,15.674,9.316,13.019,19.476,13.019z\"/&gt;\n&lt;linearGradient id=\"SVGID_5_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-952.4946\" x2=\"682.0508\" y2=\"-952.4946\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_5_)\" d=\"M19.625,10.113C8.787,10.113,0,8.238,0,5.925v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,8.238,30.464,10.113,19.625,10.113z\"/&gt;\n&lt;linearGradient id=\"SVGID_6_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-946.4946\" x2=\"682.0508\" y2=\"-946.4946\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_6_)\" d=\"M19.625,10.113c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.926-18.396,3.926\n\tc-9.481,0-17.396-1.959-18.396-3.926L0,5.925C0,8.238,8.787,10.113,19.625,10.113z\"/&gt;\n&lt;linearGradient id=\"SVGID_7_\" gradientUnits=\"userSpaceOnUse\" x1=\"644.0293\" y1=\"-943.4014\" x2=\"680.8223\" y2=\"-943.4014\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;ellipse fill=\"url(#SVGID_7_)\" cx=\"19.625\" cy=\"3.926\" rx=\"18.396\" ry=\"3.926\"/&gt;\n&lt;path opacity=\"0.24\" fill=\"#FFFFFF\" enable-background=\"new    \" d=\"M31.04,45.982c0,0-4.354,0.664-7.29,0.781\n\tc-3.125,0.125-8.952,0-8.952,0l-2.384-10.292l0.044-2.108l-1.251-1.154L9.789,23.024l-0.082-0.119L9.5,20.529l-1.65-1.254\n\tL5.329,8.793c0,0,4.213,0.903,7.234,1.07s8.375,0.25,8.375,0.25l3,9.875l-0.25,1.313l1.063,2.168l2.312,9.645l-0.521,1.416\n\tl1.46,1.834L31.04,45.982z\"/&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-uk/intro-upgrade-from-v1.md",
    "content": "Оновлення із версії 1.1\n=======================\n\nМіж версіями 1.1 і 2.0 існує багато відмінностей, оскільки Yii був повністю переписаний для версії 2.0.\nТаким чином, оновлення з версії 1.1 не є таким же тривіальним, як оновлення між мінорними версіями.\nУ цьому посібнику з оновлення наведено основні відмінності між двома версіями.\n\nЯкщо раніше ви не використовували Yii 1.1, ви можете пропустити цей розділ і перейти до розділу\n\"[Встановлення Yii](start-installation.md)\".\n\nТакож врахуйте, що Yii 2.0 надає більше нового функціоналу, ніж той, що буде описано тут. Наполегливо рекомендується\nпрочитати посібник повністю, щоб дізнатися весь доступний функціонал. Можливо, що необхідний функціонал,\nякий ви до цього розробляли самі, тепер є частиною фреймворку.\n\n\nВстановлення\n------------\n\nYii 2.0 використовує [Composer](https://getcomposer.org/), який де-факто є менеджером пакунків для PHP.\nВстановлення фреймворку, також як і розширень, здійснюється через Composer. Більш детальні відомості по встановленню Yii 2.0\nприведені в розділі [Встановлення Yii](start-installation.md). Відомості про те, як створювати розширення для Yii 2.0\nабо адаптувати вже наявні розширення для версії 1.1 під версію 2.0, наведені в розділі\n[Створення розширень](structure-extensions.md#creating-extensions).\n\n\nВимоги PHP\n----------\n\nYii 2.0 використовує PHP 5.4 або вище, який включає велику кількість поліпшень в порівнянні з версією 5.2,\nяка використовувалася Yii 1.1. Таким чином, існує багато відмінностей у мові, які ви повинні приймати до уваги.\nНижче наведені основні зміни в PHP:\n\n- [Простори імен](https://www.php.net/manual/en/language.namespaces.php);\n- [Анонімні функції](https://www.php.net/manual/en/functions.anonymous.php);\n- Використання короткого синтаксису для масивів: `[...елементи...]` замість `array(...елементи...)`;\n- Використання тегів `<?=` для вивода у файлах представлень.\n  З версії PHP 5.4 дану можливість можна використовувати без побоювань;\n- [Класи та інтерфейси SPL](https://www.php.net/manual/en/book.spl.php);\n- [Пізнє статичне звʼязування (LSB)](https://www.php.net/manual/en/language.oop5.late-static-bindings.php);\n- [Класи для дати та часу](https://www.php.net/manual/en/book.datetime.php);\n- [Трейти](https://www.php.net/manual/en/language.oop5.traits.php);\n- [Інтернаціонализація (intl)](https://www.php.net/manual/en/book.intl.php); Yii 2.0 використовує розширення PHP `intl`\n  для різного функціоналу інтернаціоналізації.\n\n\nПростори імен\n-------------\n\nОднією з основних змін в Yii 2.0 є використання просторів імен. Майже кожен клас фреймворку знаходиться у просторі імен,\nнаприклад, `yii\\web\\Request`. Префікс \"С\" більше не використовується в іменах класів. Схема іменування\nзараз відповідає структурі директорій. Наприклад, `yii\\web\\Request` означає, що відповідний клас знаходиться\nу файлі `web/Request.php` в директорії фреймворку. (Завдяки завантажувачу класів Yii, ви можете використовувати\nбудь-який клас фреймворку без необхідності безпосередньо підключати його).\n\n\nКомпонент та Обʼєкт\n-------------------\n\nВ Yii 2.0 клас `CComponent` із версії 1.1 був розділений на два класи: [[yii\\base\\BaseObject]] і [[yii\\base\\Component]].\nКлас [[yii\\base\\BaseObject|BaseObject]] є простим базовим класом, який дозволяє керувати [властивостями обʼєкта](concept-properties.md)\nза допомогою геттерів та сеттерів. Клас [[yii\\base\\Component|Component]] наслідується\nвід класа [[yii\\base\\BaseObject|BaseObject]] та підтримує [події](concept-events.md) та [поведінки](concept-behaviors.md).\n\nЯкщо вашому класу не потрібно використовувати функціонал подій та поведінок, ви можете використати\n[[yii\\base\\BaseObject|BaseObject]] у якості базового класу. В основному, це випадки, коли класи представляють собою базові\nструктури даних.\n\n\nКонфігурація обʼєкта\n--------------------\n\nКлас [[yii\\base\\BaseObject|BaseObject]] надає єдиний спосіб конфігурування обʼєктів. Будь-який дочірній клас\n[[yii\\base\\BaseObject|BaseObject]] може визначити конструктор (якщо потрібно) для своєї конфігурації наступним чином:\n\n```php\nclass MyClass extends \\yii\\base\\BaseObject\n{\n    public function __construct($param1, $param2, $config = [])\n    {\n        // ... ініціализація до того, як буде застосована конфігурація\n\n        parent::__construct($config);\n    }\n\n    public function init()\n    {\n        parent::init();\n\n        // ... ініціализація після того, як була застосована конфігурація\n    }\n}\n```\n\nУ прикладі вище, останній параметр конструктора повинен бути масивом конфігурації, який містить пари у форматі\nключ-значення для ініціалізації властивостей обʼєкта. Ви можете перевизначити метод [[yii\\base\\BaseObject::init()|init()]]\nдля ініціалізації обʼєкту після того, як до нього була застосована конфігурація.\n\nДотримуючись цієї угоди, ви зможете створювати і конфігурувати нові обʼєкти за допомогою масиву конфігурації:\n\n```php\n$object = Yii::createObject([\n    'class' => 'MyClass',\n    'property1' => 'abc',\n    'property2' => 'cde',\n], [$param1, $param2]);\n```\n\nБільш детальна інформація про конфігурацію представлена у розділі [Конфігурації](concept-configurations.md).\n\n\nПодії\n-----\n\nВ Yii 1, події створювалися за допомогою оголошення методу `on` (наприклад, `onBeforeSave`).\nВ Yii 2 ви можете тепер використовувати будь-яке імʼя події. Ви ініціюєте подію за допомогою виклику методу\n[[yii\\base\\Component::trigger()|trigger()]].\n\n```php\n$event = new \\yii\\base\\Event;\n$component->trigger($eventName, $event);\n```\n\nДля приєднання обробника події використовуйте метод [[yii\\base\\Component::on()|on()]].\n\n```php\n$component->on($eventName, $handler);\n// прибрати обробник:\n// $component->off($eventName, $handler);\n```\n\nЄ також інші покращення у функціоналі подій. Більш детальна інформація представлена у розділі [Події](concept-events.md).\n\n\nПсевдоніми шляху\n----------------\n\nYii 2.0 розширює спосіб використання псевдонімів шляху як для файлів та директорій, так і для URL.\nУ Yii 2.0 тепер також потрібно, щоб імʼя псевдоніма починалося із символу `@`, для розмежування псевдонімів від\nзвичайних шляхів файлів/директорій і URL. Наприклад, псевдонім `@yii` відповідає директорії встановлення Yii.\nПсевдоніми шляху підтримуються в багатьох місцях коду Yii. Наприклад, [[yii\\caching\\FileCache::cachePath]]\nможе мати значення як псевдоніму шляху, так і звичайного шляху до директорії.\n\nПсевдоніми шляху тісно повʼязані з простором імен класів. Рекомендується призначити псевдонім шляху\nдля кожного базового простору імен, таким чином завантажувач класів Yii може використовуватися без будь-якої\nдодаткової конфігурації. Наприклад, оскільки `@yii` посилається на директорію де встановленно Yii, клас `yii\\web\\Request`\nможе бути завантажений автоматично. Якщо ви використовуєте сторонні бібліотеки, такі як Zend Framework, ви можете також визначити\nпсевдонім шляху `@Zend`, який відповідає директорії встановлення цього фреймворку. Після чого Yii буде\nздатний автоматично завантажувати будь-який клас Zend Framework.\n\nБільш детальна інформація про псевдоніми шляху представлена у розділі [Псевдоніми](concept-aliases.md).\n\n\nПредставлення\n-------------\n\nОднією із основних змін в Yii 2 є те, що спеціальна змінна `$this` у представленні більше не відповідає\nпоточному контролеру або віджету. Замість цього, `$this` тепер відповідає обʼєкту *представлення*, новій можливості,\nяка була введена у версії 2.0. Обʼєкт представлення має тип [[yii\\web\\View]], який являє собою частину *представлення*\nу шаблоні проектування Модель-Представления-Контролер (MVC). Якщо ви хочете отримати доступ до контролера або віджету, то використовуйте вираз `$this->context`.\n\nДля формування часткових представлень тепер використовується метод `$this->render()`, а не `$this->renderPartial()`.\nРезультат виклику методу `render` тепер повинен бути виведений безпосередньо, тому що `render` повертає результат,\nа не відображає його одразу. Наприклад,\n\n```php\necho $this->render('_item', ['item' => $item]);\n```\n\nКрім використання PHP у якості основного шаблонізатору, Yii 2.0 також включає офіційні розширення для основних\nпопулярних шаблонізаторів: Smarty і Twig. Шаблонізатор Prado більше не підтримується. Для використання вказаних\nшаблонізаторів вам необхідно сконфігурувати компонент додатка `view` за допомогою налаштування властивості\n[[yii\\base\\View::$renderers|View::$renderers]].\n\nБільш детальна інформація представлена у розділі [Шаблонізатори](tutorial-template-engines.md).\n\n\nМоделі\n------\n\nYii 2.0 використовує основний клас [[yii\\base\\Model]] для моделей, аналогічний класу `CModel` у версії 1.1.\nКлас `CFormModel` більше не підтримується. Замість цього, для створення моделі форми у Yii 2.0 ви повинні\nбезпосередньо успадковуватися від класу [[yii\\base\\Model]].\n\nУ Yii 2.0 зʼявився новий метод [[yii\\base\\Model::scenarios()|scenarios()]] для оголошення сценаріїв, які підтримуються,\nі для позначення в якому сценарії атрибути повинні перевірятися, вважатися безпечними і т. п. Наприклад,\n\n```php\npublic function scenarios()\n{\n    return [\n        'backend' => ['email', 'role'],\n        'frontend' => ['email', '!role'],\n    ];\n}\n```\n\nУ прикладі вище, оголошено два сценарії: `backend` і `frontend`. Для сценарію `backend` обидва атрибута `email` і `role`\nє безпечними та можуть бути масово призначені. Для сценарію `frontend` атрибут `email` може бути масово призначений,\nале атрибут `role` - ні. Обидва атрибути `email` та `role` повинні бути перевірені за допомогою правил валідації.\n\nМетод [[yii\\base\\Model::rules()|rules()]] як і раніше використовується для оголошення правил валідації.\nЗверніть увагу, що у звʼязку з появою нового методу [[yii\\base\\Model::scenarios()|scenarios()]],\nбільше не підтримується валідатор `unsafe`.\n\nУ більшості випадків вам не потрібно перевизначати метод [[yii\\base\\Model::scenarios()|scenarios()]],\nякщо метод [[yii\\base\\Model::rules()|rules()]] повністю вказує всі сценарії, що існують, та якщо немає потреби\nв оголошенні атрибутів небезпечними.\n\nБільш детальна інформація представлена у розділі [Моделі](structure-models.md).\n\n\nКонтролери\n----------\n\nВ якості базового класу для контролерів в Yii 2.0 використовується [[yii\\web\\Controller]], який є\nаналогічним `CController` у Yii 1.1. Базовим класом для всіх дій є [[yii\\base\\Action]].\n\nОднією із основних змін є те, що дія контролера тепер має повернути результат замість того, щоб безпосередньо виводити його:\n\n\n```php\npublic function actionView($id)\n{\n    $model = \\app\\models\\Post::findOne($id);\n    if ($model) {\n        return $this->render('view', ['model' => $model]);\n    } else {\n        throw new \\yii\\web\\NotFoundHttpException;\n    }\n}\n```\n\nБільш детальна інформація представлена у розділі [Контролери](structure-controllers.md).\n\n\nВіджети\n-------\n\nУ Yii 2.0 клас [[yii\\base\\Widget]] використовується як базовий клас для віджетів, аналогічно `CWidget` у Yii 1.1.\n\nДля кращої підтримки фреймворку в IDE, Yii 2.0 використовує новий синтаксис для віджетів. Нові статичні методи \n[[yii\\base\\Widget::begin()|begin()]], [[yii\\base\\Widget::end()|end()]], та [[yii\\base\\Widget::widget()|widget()]]\nвикористовуються таким чином:\n\n```php\nuse yii\\widgets\\Menu;\nuse yii\\widgets\\ActiveForm;\n\n// Зверніть увагу, що ви повинні виводити результат\necho Menu::widget(['items' => $items]);\n\n// Вказуємо масив для конфігурації властивостей обʼєкта\n$form = ActiveForm::begin([\n    'options' => ['class' => 'form-horizontal'],\n    'fieldConfig' => ['inputOptions' => ['class' => 'input-xlarge']],\n]);\n... поля форми ...\nActiveForm::end();\n```\n\nБільш детальна інформація представлена у розділі [Віджети](structure-widgets.md).\n\n\nТеми\n----\n\nУ Yii 2.0 теми працюють абсолютно по-іншому. Тепер вони засновані на механізмі співставлення шляхів вихідного файлу\nпредставлення із темізованим файлом. Наприклад, якщо використовується співставлення шляхів\n`['/web/views' => '/web/themes/basic']`, то темізована версія файлу представлення `/web/views/site/index.php` буде\nзнаходитися у `/web/themes/basic/site/index.php`. З цієї причини теми можуть бути застосовані до будь-якого файлу\nпредставлення, навіть до представлення, яке сформоване за межами контексту контролера або віджету.\n\nТакож, більше не існує компонента `CThemeManager`. Замість цього є властивість `theme` компонента додатка `view`,\nяку можна сконфігурувати.\n\nБільш детальна інформація представлена у розділі [Темізація](output-theming.md).\n\n\nКонсольні додатки\n-----------------\n\nКонсольні додатки тепер організовані як контролери, аналогічно веб-додаткам. Консольні контролери\nповинні бути успадковані від класу [[yii\\console\\Controller]], аналогічного `CConsoleCommand` у версії 1.1.\n\nДля виконання консольної команди, використовуйте `yii <маршрут>`, де `<маршрут>` це маршрут контролера\n(наприклад, `sitemap/index`). Додаткові анонімні аргументи будуть передані у якості параметрів відповідній дії\nконтролера, у той час, як іменовані аргументи будуть передані у відповідності із оголошеннями у\n[[yii\\console\\Controller::options()]].\n\nYii 2.0 підтримує автоматичне генерування довідкової інформації із блоків коментарів.\n\nБільш детальна інформація представлена у розділі [Консольні команди](tutorial-console.md).\n\n\nI18N\n----\n\nУ Yii 2.0 були прибрані вбудовані форматтери часу та чисел на користь \n[PECL розширення PHP intl](https://pecl.php.net/package/intl).\n\nПереклад повідомлень тепер здійснюється через компонент додатка `i18n`. Даний компонент управляє наборами джерел\nповідомлень, що дозволяє вам використовувати різні джерела повідомлень, залежно від категорії повідомлення.\n\nБільш детальна інформація представлена у розділі [Інтернаціоналізація](tutorial-i18n.md).\n\n\nФільтри дій\n-----------\n\nФільтри дій тепер зроблені за допомогою поведінок. Новостворюваний фільтр необхідно успадкувати від [[yii\\base\\ActionFilter]].\nДля використання фільтра - приєднайте його до контролера у якості поведінки. Наприклад, для використання фільтра\n[[yii\\filters\\AccessControl]] слід зробити наступне:\n\n```php\npublic function behaviors()\n{\n    return [\n        'access' => [\n            'class' => 'yii\\filters\\AccessControl',\n            'rules' => [\n                ['allow' => true, 'actions' => ['admin'], 'roles' => ['@']],\n            ],\n        ],\n    ];\n}\n```\n\nБільш детальна інформація представлена у розділі [Фільтри](structure-filters.md).\n\n\nРесурси\n-------\n\nУ Yii 2.0 представлена нова можливість *колекції ресурсів*, яка замінює концепт пакунків скриптів у Yii 1.1.\n\nКолекція ресурсів - це колекція файлів ресурсів (наприклад: Javascript файли, CSS файли, файли зображень, і т. п.)\nу певній директорії. Кожна колекція ресурсів представлена​ класом, успадкованим від [[yii\\web\\AssetBundle]].\nКолекція ресурсів стає доступною через веб, за допомогою реєстрації її методом [[yii\\web\\AssetBundle::register()]]. \nНа відміну від Yii 1.1, сторінка, яка реєструє колекцію ресурсів, автоматично буде містити посилання на \nJavascript і CSS файли, які зазначені у колекції.\n\nБільш детальна інформація представлена у розділі [Ресурси](structure-assets.md).\n\n\nХелпери\n-------\n\nУ Yii 2.0 включено багато широко використовуваних статичних класів.\n\n* [[yii\\helpers\\Html]]\n* [[yii\\helpers\\ArrayHelper]]\n* [[yii\\helpers\\StringHelper]]\n* [[yii\\helpers\\FileHelper]]\n* [[yii\\helpers\\Json]]\n\nБільш детальна інформація представлена у розділі [Огляд хелперів](helper-overview.md).\n\n\nФорми\n-----\n\nYii 2.0 вводить нове поняття *поле* для побудови форм за допомогою [[yii\\widgets\\ActiveForm]]. Поле - це\nконтейнер, що містить лейбл, поле введення, повідомлення про помилку і/або допоміжний текст.\nПоле представлено обʼєктом [[yii\\widgets\\ActiveField|ActiveField]]. Використовуючи поля, ви можете будувати\nформи набагато простіше, ніж це було раніше:\n\n```php\n<?php $form = yii\\widgets\\ActiveForm::begin(); ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n    <div class=\"form-group\">\n        <?= Html::submitButton('Login') ?>\n    </div>\n<?php yii\\widgets\\ActiveForm::end(); ?>\n```\n\nБільш детальна інформація представлена у розділі [Створення форм](input-forms.md).\n\n\nКонструктор запитів\n-------------------\n\nУ версії 1.1, побудова запиту була розкидана серед декількох класів, включаючи `CDbCommand`,\n`CDbCriteria` та `CDbCommandBuilder`. У Yii 2.0 запит до БД представлений в рамках обʼєкта [[yii\\db\\Query|Query]],\nякий може бути перетворений у SQL-вираз за допомогою [[yii\\db\\QueryBuilder|QueryBuilder]]. Наприклад,\n\n```php\n$query = new \\yii\\db\\Query();\n$query->select('id, name')\n      ->from('user')\n      ->limit(10);\n\n$command = $query->createCommand();\n$sql = $command->sql;\n$rows = $command->queryAll();\n```\n\nНайкращим способом використання даних методів є робота з [Active Record](db-active-record.md).\n\nБільш детальна інформація представлена у розділі [Конструктор запитів](db-query-builder.md).\n\n\nActive Record\n-------------\n\nУ Yii 2.0 внесено безліч змін в роботу [Active Record](db-active-record.md). Основними двома є\nпобудова запитів і робота із звʼязками.\n\nКлас `CDbCriteria` у версії 1.1 був замінений на [[yii\\db\\ActiveQuery]] у Yii 2.0. Цей клас успадковується від\n[[yii\\db\\Query]] і таким чином отримує всі методи, які необхідні для побудови запиту. Для побудови запиту вам слід\nвикликати метод  [[yii\\db\\ActiveRecord::find()]]:\n\n```php\n// Отримуємо всіх *активних* клієнтів і сортуємо їх по ID\n$customers = Customer::find()\n    ->where(['status' => $active])\n    ->orderBy('id')\n    ->all();\n```\n\nДля оголошення звʼязку слід просто призначити геттер, який повертає обʼєкт [[yii\\db\\ActiveQuery|ActiveQuery]].\nІмʼя властивості, яке визначене геттером, представляє собою назву звʼязку. Наприклад, наступний код оголошує звʼязок\n`orders` (у версії 1.1 вам потрібно було б оголосити звʼязки в одному центральному місці - `relations()`):\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public function getOrders()\n    {\n        return $this->hasMany('Order', ['customer_id' => 'id']);\n    }\n}\n```\n\nТепер ви можете використовувати вираз `$customer->orders` для отримання всіх замовлень клієнта із звʼязаної таблиці.\nВи також можете використовувати наступний код, щоб застосувати потрібні умови \"на льоту\":\n\n```php\n$orders = $customer->getOrders()->andWhere('status=1')->all();\n```\n\nYii 2.0 здійснює жадібне завантаження (eager loading) звʼязку по-іншому, на відміну від версії 1.1. Зокрема,\nу версії 1.1 для вибору даних із основної і звʼязаної таблиць буде використаний запит JOIN. У Yii 2.0 будуть виконані\nдва запити без використання JOIN: перший запит повертає дані для основної таблиці, а другий - для звʼязаної,\nза допомогою фільтрації по первинних ключах основної таблиці.\n\nЗамість того, щоб повертати обʼєкти [[yii\\db\\ActiveRecord|ActiveRecord]], ви можете використовувати метод\n[[yii\\db\\ActiveQuery::asArray()|asArray()]] при побудові запиту, для вибірки великої кількості записів.\nЦе змусить повернути результат запиту у якості масиву, що може суттєво знизити витрати процесорного часу і памʼяті\nпри великій кількості записів. Наприклад:\n\n```php\n$customers = Customer::find()->asArray()->all();\n```\n\nЩе одна зміна повʼязана з тим, що ви більше не можете визначати значення атрибутів за замовчуванням з допомогою public властивостей.\nВи можете встановити їх у методі `init` вашого класу запису, якщо це потрібно.\n\n```php\npublic function init()\n{\n    parent::init();\n    $this->status = self::STATUS_NEW;\n}\n```\n\nТакож у версії 1.1 були деякі проблеми із перевизначенням конструктора ActiveRecord. Дані проблеми відсутні\nу версії 2.0. Зверніть увагу, що при додаванні параметрів у конструктор, вам, можливо, знадобиться перевизначити метод\n[[yii\\db\\ActiveRecord::instantiate()]]. Перевизначення може не знадобитися, якщо параметри, передані в конструктор\nматимуть значення за умовчанням, наприклад `null`.\n\nЄ також безліч інших змін та покращень у ActiveRecord. Більш детальна інформація про них представлена у розділі\n[Active Record](db-active-record.md).\n\n\nПоведінки Active Record\n-----------------------\n\nУ версії 2.0 розробники позбулися класу базової поведінки `CActiveRecordBehavior`. Новостворюваний клас поведінки Active Record\nмає бути успадкованим безпосередньо від класу `yii\\base\\Behavior`. Якщо класу поведінки необхідно реагувати на \nдеякі події власника, потрібно перевизначити метод `events()`, як показано нижче,\n\n```php\nnamespace app\\components;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\base\\Behavior;\n\nclass MyBehavior extends Behavior\n{\n    // ...\n\n    public function events()\n    {\n        return [\n            ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',\n        ];\n    }\n\n    public function beforeValidate($event)\n    {\n        // ...\n    }\n}\n```\n\n\nUser та IdentityInterface\n-------------------------\n\nКлас `CWebUser` у версії 1.1 тепер замінений класом [[yii\\web\\User]], а також більше не існує класу `CUserIdentity`.\nЗамість цього, необхідно реалізувати інтерфейс [[yii\\web\\IdentityInterface]], що набагато простіше у використанні.\n\nБільш детальна інформація представлена у розділах [Аутентифікація](security-authentication.md),\n[Авторизація](security-authorization.md) та [Розширений шаблон додатка](https://www.yiiframework.com/extension/yiisoft/yii2-app-advanced/doc/guide).\n\n\nКерування URL\n-------------\n\nРобота з URL в Yii 2.0 аналогічна тій, що була у версії 1.1. Основне покращення полягає в тому, що тепер підтримуються\nдодаткові параметри. Наприклад, якщо у вас є правило, оголошене нижче, то воно співпаде з `post/popular` та\n`post/1/popular`. У версії 1.1 вам довелося б використовувати два правила для отримання того ж результату.\n\n```php\n[\n    'pattern' => 'post/<page:\\d+>/<tag>',\n    'route' => 'post/index',\n    'defaults' => ['page' => 1],\n]\n```\n\nБільш детальна інформація представлена у розділі [Маршрутизація та створення URL](runtime-routing.md).\n\n\nВикористання Yii 1.1 разом із 2.x\n---------------------------------\n\nІнформація про використання коду для Yii 1.1 разом із Yii 2.0 представлена у розділі\n[Робота із стороннім кодом](tutorial-yii-integration.md).\n"
  },
  {
    "path": "docs/guide-uk/intro-yii.md",
    "content": "Що таке Yii?\n============\n\nYii – це високопродуктивний компонентний PHP-фреймворк, призначений для швидкої розробки сучасних веб-додатків.\nСлово Yii (вимовляється як `Йі` `[ji:]`) в китайській мові означає \"простий та еволюційний\".\nТакож Yii може розшифровуватись як акронім для **Yes It Is**!\n\n\nДля яких завдань найбільше підходить Yii?\n-----------------------------------------\n\nYii – це універсальний фреймворк і може бути задіяний у всіх типах веб-додатків, що використовують PHP. \nЗавдяки його компонентній структурі і відмінній підтримці кешування, фреймворк особливо підходить для розробки \nтаких великих проектів як портали, форуми, системи керування вмістом (CMS), інтернет-магазини або RESTful-додатки.\n\n\nПорівняння Yii з іншими фреймворками\n------------------------------------\n\nЯкщо ви вже знайомі з іншими фреймворками, вам напевно буде цікаво порівняти їх із Yii:\n\n- Як і багато інших PHP-фреймворків, Yii втілює архітектурний шаблон MVC (Model-View-Controller) та\n  сприяє організації коду відповідно до вимог шаблону.\n- Yii дотримується філософії простого й елегантного коду. Yii ніколи не буде намагатись пере-ускладнювати дизайн\n  тільки заради слідування будь-яким шаблонам проектування.\n- Yii є full-stack фреймворком і включає в себе перевірені можливості, які добре себе зарекомендували:\n  конструктори запитів та ActiveRecord для реляційних та NoSQL баз даних, підтримка REST API, \n  багаторівневе кешування та інші.\n- Yii надзвичайно розширюваний. Ви можете налаштувати або замінити практично будь-яку частину основного коду.\n  Завдяки надійній архітектурі розширень Yii, досить легко використовувати або розробляти поширюванні розширення.\n- Висока швидкодія завжди є головною ціллю Yii.\n\nYii — не проект однієї людини. Він підтримується і розвивається [сильною командою](https://www.yiiframework.com/team/) і великою спільнотою розробників,\nякі їй допомагають. Команда розробників фреймворку Yii стежать за тенденціями веб-розробки і розвитком інших проектів.\nНайбільш значимі можливості та кращі практики регулярно впроваджуються у фреймворк у вигляді простих й елегантних інтерфейсів.\n\nВерсії Yii\n----------\n\nНа даний момент існує дві основні версії Yii: 1.1 та 2.0. Версія 1.1 є попереднім поколінням і знаходиться у стані підтримки.\nВерсія 2.0 - це повністю переписаний Yii, що використовує останні технології і протоколи, такі як Composer, PSR, простори імен,\nтрейти і багато іншого. 2.0 - поточне покоління фреймворку. На цій версії будуть зосереджені основні зусилля\nкілька наступних років. Даний посібник призначений в основному для версії 2.0.\n\n\nВимоги до ПЗ і знань\n--------------------\n\nYii 2.0 потребує PHP 7.4.0 та вище. Щоб дізнатися вимоги для окремих можливостей ви можете запустити скрипт перевірки вимог,\nякий поставляється із кожним релізом фреймворку.\n\nДля розробки на Yii необхідне загальне розуміння ООП, оскільки фреймворк повністю слідує цій парадигмі.\nТакож слід вивчити такі сучасні можливості PHP як [простори імен](https://www.php.net/manual/en/language.namespaces.php)\nі [трейти](https://www.php.net/manual/en/language.oop5.traits.php).\n"
  },
  {
    "path": "docs/guide-uk/rest-quick-start.md",
    "content": "Швидкий старт\n===========\n\nYii включає повноцінний набір засобів для спрощеної реалізації [RESTful API](https://ru.wikipedia.org/wiki/REST).\nЗокрема, це такі можливості:\n\n* Швидке створення прототипів за допомогою поширених API до [Active Record](db-active-record.md);\n* Налаштування формату відповіді (JSON та XML реалізовані за замовчуванням);\n* Отримання серіалізованих об'єктів із необхідною вам вибіркою полів;\n* Належне форматування даних та помилок при їх валідації;\n* Колекція пагінацій, фільтрів та сортувань;\n* Підтримка [HATEOAS](https://uk.wikipedia.org/wiki/HATEOAS);\n* Ефективна маршрутизація з належною перевіркою методів HTTP;\n* Вбудована підтримка методів `OPTIONS` та `HEAD`;\n* Аутентифікація та авторизація;\n* HTTP кешування та кешування даних;\n* Налаштування обмеження для частоти запитів ([Rate limiting](rest-rate-limiting.md));\n\n\nРозглянемо приклад, як можна налаштувати Yii під RESTful API, доклавши при цьому мінімум зусиль.\n\nПрипустимо, ви захотіли RESTful API для даних по користувачам. Ці дані зберігаються в базі даних та для роботи з ними\nвами була раніше створена модель [[yii\\db\\ActiveRecord|ActiveRecord]]  (клас `app\\models\\User`).\n\n\n## Створення контролера <span id=\"creating-controller\"></span>\n\nПо-перше, створимо клас контролера `app\\controllers\\UserController`:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\rest\\ActiveController;\n\nclass UserController extends ActiveController\n{\n    public $modelClass = 'app\\models\\User';\n}\n```\n\nКлас контролера успадковується від [[yii\\rest\\ActiveController]]. Ми задали [[yii\\rest\\ActiveController::modelClass|modelClass]]\nяк `app\\models\\User`, цим вказавши контролеру, до якої моделі йому необхідно звертатися для редагування чи\nвибірки даних.\n\n\n## Налаштування правил URL <span id=\"configuring-url-rules\"></span>\n\nДалі змінимо налаштування компонента `urlManager` у конфігурації додатку:\n\n```php\n'urlManager' => [\n    'enablePrettyUrl' => true,\n    'enableStrictParsing' => true,\n    'showScriptName' => false,\n    'rules' => [\n        ['class' => 'yii\\rest\\UrlRule', 'controller' => 'user'],\n    ],\n]\n```\n\nУстанови вище додають правило для контролера `user`, яке надає доступ до даних користувача через красиві URL та логічні методи запитів HTTP.\n\n\n## Увімкнення JSON на прийом даних<span id=\"enabling-json-input\"></span>\n\nДля того, щоб API міг приймати дані у форматі JSON, налаштуйтє [[yii\\web\\Request::$parsers|parsers]] властивість у компонента `request` [application component](structure-application-components.md) на використання [[yii\\web\\JsonParser]] JSON даних на вході:\n\n```php\n'request' => [\n    'parsers' => [\n        'application/json' => 'yii\\web\\JsonParser',\n    ]\n]\n```\n\n> Note: Конфігурація, наведена вище, необов'язкова. Без наведеної вище конфігурації, API зможе визначити лише\n  `application/x-www-form-urlencoded` и `multipart/form-data` формати.\n\n\n## Пробуємо <span id=\"trying-it-out\"></span>\n\nОсь так просто ми створили RESTful API для доступу до даних користувача. API нашого сервісу зараз включає в себе:\n\n* `GET /users`: отримання посторінкового списку всіх користувачів;\n* `HEAD /users`: отримання метаданих лістингу користувачів;\n* `POST /users`: створення нового користувача;\n* `GET /users/123`: отримання інформації щодо конкретного користувача з id рівним 123;\n* `HEAD /users/123`: отримання метаданих за конкретним користувачем з id рівним 123;\n* `PATCH /users/123` та `PUT /users/123`: редагування інформації щодо користувача з id рівним 123;\n* `DELETE /users/123`: видалення користувача з id рівним 123;\n* `OPTIONS /users`: отримання підтримуваних методів, за якими можна звернутися до `/users`;\n* `OPTIONS /users/123`: отримання підтримуваних методів, за якими можна звернутися до `/users/123`.\n\nПробуємо отримати відповіді по API використовуючи `curl`: \n\n```\n$ curl -i -H \"Accept:application/json\" \"http://localhost/users\"\n\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nX-Powered-By: PHP/5.4.20\nX-Pagination-Total-Count: 1000\nX-Pagination-Page-Count: 50\nX-Pagination-Current-Page: 1\nX-Pagination-Per-Page: 20\nLink: <http://localhost/users?page=1>; rel=self, \n      <http://localhost/users?page=2>; rel=next, \n      <http://localhost/users?page=50>; rel=last\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n[\n    {\n        \"id\": 1,\n        ...\n    },\n    {\n        \"id\": 2,\n        ...\n    },\n    ...\n]\n```\n\nСпробуйте змінити заголовок допустимого формату ресурсу на `application/xml` і у відповідь ви отримаєте результат у форматі XML:\n\n```\n$ curl -i -H \"Accept:application/xml\" \"http://localhost/users\"\n\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nX-Powered-By: PHP/5.4.20\nX-Pagination-Total-Count: 1000\nX-Pagination-Page-Count: 50\nX-Pagination-Current-Page: 1\nX-Pagination-Per-Page: 20\nLink: <http://localhost/users?page=1>; rel=self, \n      <http://localhost/users?page=2>; rel=next, \n      <http://localhost/users?page=50>; rel=last\nTransfer-Encoding: chunked\nContent-Type: application/xml\n\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<response>\n    <item>\n        <id>1</id>\n        ...\n    </item>\n    <item>\n        <id>2</id>\n        ...\n    </item>\n    ...\n</response>\n```\n\n> Tip: Ви можете отримати доступ до API через веб-браузер, ввівши адресу `http://localhost/users`. Але в цьому випадку\nдля передачі певних заголовків вам, швидше за все, потрібні додаткові плагіни для браузера.\n\nЯкщо уважно подивитися результат відповіді, то можна виявити, що в заголовках є інформація про загальну кількість записів,\nкількості сторінок і т. д. Тут також можна виявити посилання на інші сторінки, як, наприклад,\n`http://localhost/users?page=2`. Перейшовши по ній, можна отримати другу сторінку даних користувачів.\n\nВикористовуючи параметри `fields` та `expand` в URL, можна вказати, які поля мають бути включені до результату. Наприклад,\nза адресою `http://localhost/users?fields=id,email` ми отримаємо інформацію щодо користувачів, яка міститиме\nтільки `id` та `email`.\n\n> Info: Ви, напевно, помітили, що при зверненні до `http://localhost/users` ми отримуємо інформацію з полями, \n> які небажано показувати, такими як `password_hash` та `auth_key`. Ви можете і повинні видалити ці поля, як описано у \n> розділі «[Ресурси](rest-resources.md)».\n\nДодатково ви можете відсортувати колекції як `http://localhost/users?sort=email` або\n`http://localhost/users?sort=-email`. Фільтрування колекцій як `http://localhost/users?filter[id]=10` або\n`http://localhost/users?filter[email][like]=gmail.com` можлива при використанні\nфільтрів даних. Докладніше у розділі [Resources](rest-resources.md#filtering-collections).\n\n## Резюме <span id=\"summary\"></span>\n\nВикористовуючи Yii, як RESTful API фреймворк, ми реалізуємо точки входу API як дії контролерів.\nКонтролер використовується для організації дій, що належать до певного типу ресурсу.\n\nРесурси представлені як моделі даних, які успадковуються від класу [[yii\\base\\Model]].\nЯкщо потрібна робота з базами даних (як із реляційними, так і з NoSQL), рекомендується використовувати для представлення\nресурсів [[yii\\db\\ActiveRecord|ActiveRecord]].\n\nВи можете використовувати [[yii\\rest\\UrlRule]] для спрощення маршрутизації точок входу API.\n\nХоча це не обов'язково, рекомендується відокремлювати RESTful APIs додаток від основного веб-додатку. Такий поділ\nлегше підтримувати.\n"
  },
  {
    "path": "docs/guide-uk/rest-rate-limiting.md",
    "content": "Обмеження частоти запитів\n===============================\n\nДля того, щоб уникнути зловживань, вам слід подумати про додавання обмеження частоти запитів до вашого API. Наприклад,\nви можете обмежити використання вашого API до 100 запитів протягом 10 хвилин для кожного користувача. Якщо від користувача\nпротягом цього періода часу надходить більша кількість запитів, буде повернута відповідь з кодом 429\n(\"занадто багато запитів\").\n\nДля того, щоб увімкнути обмеження частоти запитів, *[[yii\\web\\User::identityClass|клас user identity]]* повинен реалізовувати\nінтерфейс [[yii\\filters\\RateLimitInterface]]. Цей інтерфейс вимагає реалізації наступних трьох методів:\n\n* `getRateLimit()`: повертає максимальну кількість дозволених запитів та період часу, наприклад `[100, 600]`, що\n  означає не більше 100 викликів API прогятом 600 секунд.\n* `loadAllowance()`: повертає кількість дозволених запитів, що залишились, та мітку часу *UNIX* останньої перевірки обмеження.\n* `saveAllowance()`: зберігає кількість дозволених запитів та поточну мітку часу *UNIX*.\n\nВи можете використовувати два стовпці в таблиці user для зберігання кількості дозволених запитів та час останньої перевірки.\nУ методах `loadAllowance()` та `saveAllowance()` можна реалізувати зчитування та зберігання значень цих стовбців відповідно\nдо даних поточного аутентифікованого користувача. Для покращення швидкодії можна спробувати зберігати цю\nінформацію в кеш чи NoSQL-сховищі.\n\nРеалізація у моделі `User` може виглядати наступним чином:\n\n```php\npublic function getRateLimit($request, $action)\n{\n    return [$this->rateLimit, 1]; // $rateLimit запитів на секунду\n}\n\npublic function loadAllowance($request, $action)\n{\n    return [$this->allowance, $this->allowance_updated_at];\n}\n\npublic function saveAllowance($request, $action, $allowance, $timestamp)\n{\n    $this->allowance = $allowance;\n    $this->allowance_updated_at = $timestamp;\n    $this->save();\n}\n```\n\nЯк тільки відповідний інтерфейс буде реалізований у класі identity, Yii почне автоматично перевіряти обмеження\nчастоти запитів за допомогою фільтра дій [[yii\\filters\\RateLimiter]] для [[yii\\rest\\Controller]]. При перевищенні\nобмежень буде викинуто виключення [[yii\\web\\TooManyRequestsHttpException]].\n\nВи можете налаштувати обмеження частоти викликів у ваших класах REST-контролерів наступним чином:\n\n```php\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n    $behaviors['rateLimiter']['enableRateLimitHeaders'] = false;\n    return $behaviors;\n}\n```\n\nПри увімкненому обмеженні частоти запитів кожна відповідь, за замовчуванням, повертається з наступними HTTP-заголовками,\nщо містять таку інформацію про поточні обмеження:\n\n* `X-Rate-Limit-Limit`: максимальна кількість запитів, дозволена протягом періоду часу\n* `X-Rate-Limit-Remaining`: скільки залишилось дозволених запитів в поточний період часу\n* `X-Rate-Limit-Reset`: скільки часу у секундах потрібно почекати до отримання максимальної кількості дозволених запитів\n\nВи можете відключити ці заголовки, встановивши властивість [[yii\\filters\\RateLimiter::enableRateLimitHeaders]] у `false`,\nяк показано у прикладі вище.\n"
  },
  {
    "path": "docs/guide-uk/runtime-sessions-cookies.md",
    "content": "Сесії та кукі\n====================\n\nСесії та кукі дозволяють зберігати користувацькі дані між запитами. При використанні чистого PHP можна отримати доступ до цих даних через глобальні змінні `$_SESSION` та `$_COOKIE`, відповідно. Yii інкапсулює сесії та кукі в об'єкти, що дає можливість звертатися до них в об'єктноорієнтованому стилі та забезпечує додаткову зручність в роботі.\n\n\n## Сесії <span id=\"sessions\"></span>\n\nЗа аналогією з [запитами](runtime-requests.md) та [відповідями](runtime-responses.md), до сесій можна отримати доступ через `session` [компонент додатка](structure-application-components.md), який за замовчуванням є екземпляром [[yii\\web\\Session]].\n\n\n### Відкриття та закриття сесії <span id=\"opening-closing-sessions\"></span>\n\nВідкрити та закрити сесію можна наступним чином:\n\n```php\n$session = Yii::$app->session;\n\n// перевіряєм що сесія вже відкрита\nif ($session->isActive) ...\n\n// відкиваєм сесію\n$session->open();\n\n// закриваємо сесію\n$session->close();\n\n// знищуємо сесію і всі пов'язані з нею дані.\n$session->destroy();\n```\n\nМожна викликати [[yii\\web\\Session::open()|open()]] і [[yii\\web\\Session::close()|close()]] багаторазово без виникнення помилок; всередині компонента всі методи перевіряють сесію на те, відкрита вона чи ні.\n\n\n### Доступ до даних сесії <span id=\"access-session-data\"></span>\n\nОтримати доступ до збережених в сесію даних можна наступним чином:\n\n```php\n$session = Yii::$app->session;\n\n// отримання змінної з сесії. Наступні способи використання еквівалентні:\n$language = $session->get('language');\n$language = $session['language'];\n$language = isset($_SESSION['language']) ? $_SESSION['language'] : null;\n\n// запис змінної в сесію. Наступні способи використання еквівалентні:\n$session->set('language', 'en-US');\n$session['language'] = 'en-US';\n$_SESSION['language'] = 'en-US';\n\n// видалення змінної з сесії. Наступні способи використання еквівалентні:\n$session->remove('language');\nunset($session['language']);\nunset($_SESSION['language']);\n\n// перевірка на існування змінної в сесії. Наступні способи використання еквівалентні:\nif ($session->has('language')) ...\nif (isset($session['language'])) ...\nif (isset($_SESSION['language'])) ...\n\n// обхід усіх змінних у сесії. Наступні способи використання еквівалентні:\nforeach ($session as $name => $value) ...\nforeach ($_SESSION as $name => $value) ...\n```\n\n> Info: При отриманні даних з сесії через компонент `session`, сесія буде автоматично відкрита, якщо вона не була відкрита до цього. У цьому полягає відмінність від отримання даних з глобальної змінної `$_SESSION`, що вимагає обов'язкового виклику `session_start()`.\n\nПри роботі з сесійними даними, які є масивами, компонент `session` має обмеження, що забороняє пряму модифікацію окремих елементів масиву. Наприклад,\n\n```php\n$session = Yii::$app->session;\n\n// наступний код НЕ БУДЕ працювати\n$session['captcha']['number'] = 5;\n$session['captcha']['lifetime'] = 3600;\n\n// а цей буде:\n$session['captcha'] = [\n    'number' => 5,\n    'lifetime' => 3600,\n];\n\n// цей код також буде працювати:\necho $session['captcha']['lifetime'];\n```\n\nДля вирішення цієї проблеми можна використовувати такі обхідні прийоми:\n\n```php\n$session = Yii::$app->session;\n\n// пряме використання $_SESSION (переконайтеся, що Yii::$app->session->open() був викликаний)\n$_SESSION['captcha']['number'] = 5;\n$_SESSION['captcha']['lifetime'] = 3600;\n\n// отримайте весь масив, модифікуйте і збережіть назад у сесію\n$captcha = $session['captcha'];\n$captcha['number'] = 5;\n$captcha['lifetime'] = 3600;\n$session['captcha'] = $captcha;\n\n// використовуйте ArrayObject замість масиву\n$session['captcha'] = new \\ArrayObject;\n...\n$session['captcha']['number'] = 5;\n$session['captcha']['lifetime'] = 3600;\n\n// записуйте дані з ключами, які мають однаковий префікс\n$session['captcha.number'] = 5;\n$session['captcha.lifetime'] = 3600;\n```\n\nДля покращення продуктивності та читабельності коду рекомендується використовувати останній прийом. Іншими словами, замість того, щоб зберігати масив як одну змінну сесії, ми зберігаємо кожен елемент масиву як звичайну сесійну змінну зі спільним префіксом.\n\n\n### Користувацьке сховище для сесії <span id=\"custom-session-storage\"></span>\n\nЗа замовчуванням клас [[yii\\web\\Session]] зберігає дані сесії у вигляді файлів на сервері. Однак Yii надає ряд класів, які реалізують різні способи зберігання даних сесії:\n\n* [[yii\\web\\DbSession]]: зберігає дані сесії в базі даних.\n* [[yii\\web\\CacheSession]]: зберігання даних сесії в попередньо сконфігурованому компоненті кешу [кеш](caching-data.md#cache-components).\n* [[yii\\redis\\Session]]: зберігання даних сесії в [redis](https://redis.io/).\n* [[yii\\mongodb\\Session]]: зберігання сесії в [MongoDB](https://www.mongodb.com/).\n\nУсі ці класи підтримують однаковий набір методів API. В результаті ви можете перемикатися між різними сховищами сесій без модифікації коду додатку.\n\n> Note: Якщо ви хочете отримати дані з змінної `$_SESSION` при використанні користувацького сховища, ви повинні бути впевнені, що сесія вже стартувала [[yii\\web\\Session::open()]], оскільки обробники зберігання користувацьких сесій реєструються в цьому методі.\n\nЩоб дізнатися, як налаштувати і використовувати ці компоненти, зверніться до документації по API. Нижче наведено приклад конфігурації [[yii\\web\\DbSession]] для використання бази даних для зберігання сесії:\n\n```php\nreturn [\n    'components' => [\n        'session' => [\n            'class' => 'yii\\web\\DbSession',\n            // 'db' => 'mydb',  // ID компонента для взаємодії з БД. По замовчуванню 'db'.\n            // 'sessionTable' => 'my_session', // назва таблиці для даних сесії. По замовчуванню 'session'.\n        ],\n    ],\n];\n```\n\nТакож необхідно створити таблицю для зберігання даних сесії:\n\n```sql\nCREATE TABLE session\n(\n    id CHAR(40) NOT NULL PRIMARY KEY,\n    expire INTEGER,\n    data BLOB\n)\n```\n\nде 'BLOB' відповідає типу даних вашої DBMS. Нижче наведені приклади відповідності типів BLOB у найбільш популярних DBMS:\n\n- MySQL: LONGBLOB\n- PostgreSQL: BYTEA\n- MSSQL: BLOB\n\n> Note: В залежності від налаштувань параметра `session.hash_function` у вашому php.ini, може знадобитися змінити довжину поля `id`. Наприклад, якщо `session.hash_function=sha256`, потрібно встановити довжину поля на 64 замість 40.\n\n### Flash-повідомлення <span id=\"flash-data\"></span>\n\nFlash-повідомлення - це особливий тип даних у сесії, які встановлюються один раз під час запиту і доступні лише протягом наступного запиту, після чого вони автоматично видаляються. Такий спосіб зберігання інформації в сесії найчастіше використовується для реалізації повідомлень, які будуть відображені кінцевому користувачу один раз, наприклад, підтвердження про успішну відправку форми.\n\nВстановити та отримати flash-повідомлення можна через компонент програми `session`. Наприклад:\n\n```php\n$session = Yii::$app->session;\n\n// Запит #1\n// встановлення flash-повідомлення з назвою \"postDeleted\"\n$session->setFlash('postDeleted', 'Ви успішно видалили пост.');\n\n// Запит #2\n// відображення flash-повідомлення \"postDeleted\"\necho $session->getFlash('postDeleted');\n\n// Запит #3\n// змінна $result буде мати значення false, оскільки flash-повідомлення було автоматично видалено\n$result = $session->hasFlash('postDeleted');\n```\n\nОскільки flash-повідомлення зберігаються в сесії як звичайні дані, в них можна записувати довільну інформацію, і вона буде доступна лише в наступному запиті.\n\nПри виклику [[yii\\web\\Session::setFlash()]] відбувається перезаписування flash-повідомлень з таким же назвою. Для того, щоб додати нові дані до вже існуючого flash-повідомлення, необхідно викликати [[yii\\web\\Session::addFlash()]]. \nНаприклад:\n\n```php\n$session = Yii::$app->session;\n\n// Запит #1\n// додати нове flash-повідомлення з назвою \"alerts\"\n$session->addFlash('alerts', 'Ви успішно видалили пост.');\n$session->addFlash('alerts', 'Ви успішно додали нового друга.');\n$session->addFlash('alerts', 'Дякуємо.');\n\n// Запит #2\n// Змінна $alerts тепер містить масив flash-повідомлень з назвою \"alerts\"\n$alerts = $session->getFlash('alerts');\n```\n\n> Note: Намагайтеся не використовувати [[yii\\web\\Session::setFlash()]] спільно з [[yii\\web\\Session::addFlash()]] для flash-повідомлень з однаковою назвою. Це пов'язано з тим, що останній метод автоматично перетворює збережені дані в масив, щоб мати можливість зберігати та додавати нові дані в flash-повідомлення з тією ж назвою. В результаті, при виклику [[yii\\web\\Session::getFlash()]] можна виявити, що повертається масив, тоді як очікувалася строка.\n\n## Кукі <span id=\"cookies\"></span>\n\nYii представляє кожну з cookie як об'єкт [[yii\\web\\Cookie]]. Обидва компоненти програми [[yii\\web\\Request]] і [[yii\\web\\Response]] \nпідтримують колекції кукі через своє властивість cookies. У першому випадку колекція кукі є їх представленням з HTTP-запиту, у другому — представляє кукі, які будуть відправлені користувачу.\n\n### Читання кукі <span id=\"reading-cookies\"></span>\n\nОтримати кукі з поточного запиту можна наступним чином:\n\n```php\n// отримання колекції кукі (yii\\web\\CookieCollection) з компонента \"request\"\n$cookies = Yii::$app->request->cookies;\n\n// отримання кукі з назвою \"language\". Якщо кукі не існує, \"en\" буде повернуто як значення за замовчуванням.\n$language = $cookies->getValue('language', 'en');\n\n// альтернативний спосіб отримання кукі \"language\"\nif (($cookie = $cookies->get('language')) !== null) {\n    $language = $cookie->value;\n}\n\n// тепер змінну $cookies можна використовувати як масив\nif (isset($cookies['language'])) {\n    $language = $cookies['language']->value;\n}\n\n// перевірка на існування кукі \"language\"\nif ($cookies->has('language')) ...\nif (isset($cookies['language'])) ...\n```\n\n\n### Відправка кукі <span id=\"sending-cookies\"></span>\n\nВідправити кукі кінцевому користувачу можна наступним чином:\n\n```php\n// отримання колекції (yii\\web\\CookieCollection) з компонента \"response\"\n$cookies = Yii::$app->response->cookies;\n\n// додавання нової кукі в HTTP-відповідь\n$cookies->add(new \\yii\\web\\Cookie([\n    'name' => 'language',\n    'value' => 'zh-CN',\n]));\n\n// видалення кукі...\n$cookies->remove('language');\n// ...що еквівалентно наступному:\nunset($cookies['language']);\n```\n\nКрім властивостей [[yii\\web\\Cookie::name|name]] та [[yii\\web\\Cookie::value|value]], клас [[yii\\web\\Cookie]] також надає ряд властивостей для отримання інформації про куки: [[yii\\web\\Cookie::domain|domain]], [[yii\\web\\Cookie::expire|expire]]. Ці властивості можна сконфігурувати, а потім додати кукі в колекцію для HTTP-відповіді.\n\n> Note: Для більшої безпеки значення властивості [[yii\\web\\Cookie::httpOnly]] за замовчуванням встановлено в `true`. Це зменшує ризики доступу до захищеної кукі на клієнтській стороні (якщо браузер підтримує таку можливість). Ви можете звернутися до [httpOnly wiki](https://owasp.org/www-community/HttpOnly) для додаткової інформації.\n\n### Валідація кукі <span id=\"cookie-validation\"></span>\n\nПід час запису та читання куків через компоненти `request` та `response`, як буде показано в двох наступних підрозділах, фреймворк надає автоматичну валідацію, яка забезпечує захист кукі від модифікації на стороні клієнта. Це досягається завдяки підписанню кожної кукі секретним ключем, що дозволяє додатку розпізнавати кукі, які були модифіковані на клієнтській стороні. У такому випадку кукі НЕ БУДЕ доступна через властивість [[yii\\web\\Request::cookies|cookie collection]] компонента `request`.\n\n> Note: Валідація кукі захищає тільки від їх модифікації. Якщо валідація не була пройдена, отримати доступ до кукі все ще можна через глобальну змінну `$_COOKIE`. Це пов'язано з тим, що додаткові пакети та бібліотеки можуть маніпулювати кукі без виклику валідації, яку забезпечує Yii.\n\n\nЗа замовчуванням валідація кукі увімкнена. Її можна вимкнути, встановивши властивість [[yii\\web\\Request::enableCookieValidation]] в `false`, однак ми настійливо не рекомендуємо цього робити.\n\n> Note: Кукі, які безпосередньо читаються/пишуться через `$_COOKIE` та `setcookie()`, НЕ БУДУТЬ валідовуватися.\n\nПри використанні валідації кукі необхідно вказати значення властивості [[yii\\web\\Request::cookieValidationKey]], яке буде використано для генерації згаданого вище секретного ключа. Це можна зробити, налаштувавши компонент `request` у конфігурації додатка:\n\n```php\nreturn [\n    'components' => [\n        'request' => [\n            'cookieValidationKey' => 'fill in a secret key here',\n        ],\n    ],\n];\n```\n\n> Note: Властивість [[yii\\web\\Request::cookieValidationKey|cookieValidationKey]] є секретним значенням і повинно бути відомо лише тим, кому ви довіряєте. Не розміщуйте цю інформацію в системі контролю версій.\n"
  },
  {
    "path": "docs/guide-uk/start-databases.md",
    "content": "Робота з базами даних\n=====================\n\nЦей розділ описує, як створити нову сторінку, яка буде відображати дані країни, отримані з\nтаблиці `country` бази даних. Для цього, вам необхідно буде налаштувати зʼєднання з базою даних,\nстворити клас [Active Record](db-active-record.md), визначити [дію](structure-controllers.md)\nта створити [представлення](structure-views.md).\n\nВ даному розділі ви дізнаєтесь як:\n\n* налаштувати зʼєднання з базою даних;\n* визначити клас Active Record;\n* запитувати дані за допомогою класу Active Record;\n* відображати дані у представленні із розділенням на сторінки.\n\nЗверніть увагу, що для того, щоб закінчити цей розділ, ви повинні мати базові знання і досвід використання баз даних. \nЗокрема, ви повинні знати, як створювати бази даних та як виконувати SQL-запити за допомогою клієнтських додатків баз даних.\n\n\nПідготовка бази даних <span id=\"preparing-database\"></span>\n---------------------\n\nДля початку, створіть базу даних `yii2basic`, з якої і будете надалі отримувати дані. \nВи можете створити базу даних SQLite, MySQL, PostgreSQL, MSSQL або Oracle, Yii має вбудовану підтримку для багатьох баз даних.\nДля простоти, у подальшому описі будемо вважати що використовується MySQL.\n\nДалі, створіть таблицю `country` у базі даних, і внесіть декілька зразків даних. Можете використати наступний SQL-запит, як приклад:\n\n```sql\nCREATE TABLE `country` (\n  `code` CHAR(2) NOT NULL PRIMARY KEY,\n  `name` CHAR(52) NOT NULL,\n  `population` INT(11) NOT NULL DEFAULT '0'\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nINSERT INTO `country` VALUES ('AU','Australia',24016400);\nINSERT INTO `country` VALUES ('BR','Brazil',205722000);\nINSERT INTO `country` VALUES ('CA','Canada',35985751);\nINSERT INTO `country` VALUES ('CN','China',1375210000);\nINSERT INTO `country` VALUES ('DE','Germany',81459000);\nINSERT INTO `country` VALUES ('FR','France',64513242);\nINSERT INTO `country` VALUES ('GB','United Kingdom',65097000);\nINSERT INTO `country` VALUES ('IN','India',1285400000);\nINSERT INTO `country` VALUES ('RU','Russia',146519759);\nINSERT INTO `country` VALUES ('US','United States',322976000);\n```\n\nНа даний момент, у вас є база даних `yii2basic` і таблиця `country` з трьома колонками, що містить десять рядків даних.\n\nНалаштування підключення до БД <span id=\"configuring-db-connection\"></span>\n------------------------------\n\nПерш ніж продовжити, переконайтеся, що у вас встановлено розширення PHP [PDO](https://www.php.net/manual/en/book.pdo.php)\nта драйвер PDO для вашої БД (наприклад `pdo_mysql` для MySQL). Це є основною вимогою,\nякщо ваш додаток використовує реляційну базу даних.\n\nКоли вони встановлені, відкрийте файл `config/db.php` і змініть параметри для відповідності вашій БД.\nЗа замовчуванням, файл містить наступне:\n\n```php\n<?php\n\nreturn [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=localhost;dbname=yii2basic',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n];\n```\n\nФайл конфігурації `config/db.php` є типовим інструментом [налаштування](concept-configurations.md) на основі файлів. \nДаний файл конфігурації визначає параметри, які необхідні для створення та ініціалізації екземпляру [[yii\\db\\Connection]],\nчерез який ви можете робити SQL-запити до зазначеної бази даних.\n\nЗ’єднання з БД, описане вище, може бути доступне в коді додатка за допомогою виразу `Yii::$app->db`.\n\n> Info: Файл конфігурації `config/db.php` буде включений до конфігурації головного додатка `config/web.php`,\nякий визначає як має бути проініціалізований екземпляр [додатку](structure-applications.md).\nДля отримання додаткової інформації, будь ласка, зверніться до розділу [Конфігурації](concept-configurations.md).\n\n\nСтворення Active Record <span id=\"creating-active-record\"></span>\n-----------------------\n\nДля репрезентації та отримання даних з таблиці `country` створіть похідний від [Active Record](db-active-record.md)\nклас з іменем `Country`, і збережіть його в файл `models/Country.php`.\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass Country extends ActiveRecord\n{\n}\n```\n\nКлас `Country` наслідує [[yii\\db\\ActiveRecord]]. Вам не потрібно писати ніякого коду всередині нього! \nВсього лише за допомогою описаного вище коду, Yii самостійно вгадає відповідне імʼя таблиці з імені класу.\n\n> Info: Якщо неможливо отримати прямої відповідності імені класу до імені таблиці, ви можете\nперевизначити метод [[yii\\db\\ActiveRecord::tableName()]], щоб точно задати відповідне імʼя таблиці.\n\nВикористовуючи клас `Country`, ви можете легко маніпулювати даними з таблиці `country`, як показано у цих фрагментах коду:\n\n```php\nuse app\\models\\Country;\n\n// отримати всі рядки з таблиці country і відсортувати їх по \"name\"\n$countries = Country::find()->orderBy('name')->all();\n\n// отримати рядок, по основному ключу \"US\"\n$country = Country::findOne('US');\n\n// відобразити \"United States\"\necho $country->name;\n\n// змінити назву країни на \"U.S.A.\" і зберегти в БД\n$country->name = 'U.S.A.';\n$country->save();\n```\n\n> Info: Active Record є потужним засобом для доступу і управління даними бази даних в обʼєктно-орієнтованому стилі.\nВи можете знайти більш детальну інформацію в розділі [Active Record](db-active-record.md). Крім того, ви також можете\nвзаємодіяти з базою даних, використовуючи для доступу метод передачі даних нижнього рівня під назвою [Data Access Objects](db-dao.md).\n\n\nСтворення дії <span id=\"creating-action\"></span>\n-------------\n\nЩоб відобразити дані про країну кінцевим користувачам, необхідно створити нову дію. Замість розміщення нової дії \nу контролері `site`, який ви використовували в попередніх розділах, є сенс створити новий контролер спеціально для всіх дій, \nпов’язаних з даними країн. Створіть новий контролер з іменем `CountryController` і дію `index`, як показано нижче:\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\nuse yii\\data\\Pagination;\nuse app\\models\\Country;\n\nclass CountryController extends Controller\n{\n    public function actionIndex()\n    {\n        $query = Country::find();\n\n        $pagination = new Pagination([\n            'defaultPageSize' => 5,\n            'totalCount' => $query->count(),\n        ]);\n\n        $countries = $query->orderBy('name')\n            ->offset($pagination->offset)\n            ->limit($pagination->limit)\n            ->all();\n\n        return $this->render('index', [\n            'countries' => $countries,\n            'pagination' => $pagination,\n        ]);\n    }\n}\n```\n\nЗбережіть цей код у файл `controllers/CountryController.php`.\n\nДія `index` викликає метод `Country::find()`. Цей метод Active Record будує запит до бази даних і отримує всі дані з таблиці `country`.\nЩоб обмежити кількість країн, які будуть отримуватись в кожному запиті, сам запит розділяється на сторінки, за допомогою об’єкта\n[[yii\\data\\Pagination]]. Об’єкт `Pagination` служить двом цілям:\n\n* Встановлює директиви `offset` і `limit` для SQL-запиту так, щоб він повертав лише одну сторінку даних за один раз \n  (не більше 5 рядків на сторінці).\n* Використовується у представленнях для відображення пейджера, який складається із переліку кнопок переходу по сторінках,\n  про що буде описано у наступному підрозділі.\n\nНаприкінці, дія `index` формує представлення з іменем `index` та передає до нього дані по країнах  із інформацією про посторінкове\nрозділення.\n\n\nСтворення представлення <span id=\"creating-view\"></span>\n-----------------------\n\nВ директорії `views` створіть спочатку під-директорію `country`. Цей каталог буде використовуватись для всіх\nпредставлень контролера `country`. В директорії `views/country`, створіть файл з іменем `index.php`,\nщо містить наступне:\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\LinkPager;\n?>\n<h1>Країни</h1>\n<ul>\n<?php foreach ($countries as $country): ?>\n    <li>\n        <?= Html::encode(\"{$country->name} ({$country->code})\") ?>:\n        <?= $country->population ?>\n    </li>\n<?php endforeach; ?>\n</ul>\n\n<?= LinkPager::widget(['pagination' => $pagination]) ?>\n```\n\nДане представлення містить дві секції для відображення даних про країни. У першій частині, відображаються передані дані про країни \nу вигляді невпорядкованого списку HTML. У другій частині, віджет [[yii\\widgets\\LinkPager]] з використанням інформації \nпро нумерацію сторінок. Віджет `LinkPager` відображається у вигляді переліку кнопок. \nПри натисканні на будь-якій з них будуть виводитись дані країн для відповідної сторінки.\n\n\nСпробуємо <span id=\"trying-it-out\"></span>\n---------\n\nЩоб побачити як весь вищезазначений код працює, відкрийте в браузері наступний URL:\n\n```\nhttps://hostname/index.php?r=country%2Findex\n```\n\n![Перелік країн](images/start-country-list.png)\n\nСпочатку, ви побачите сторінку з переліком пʼяти країн. Нижче країн, ви побачите пейджер з чотирма кнопками. \nЯкщо ви натиснете на кнопку \"2\", ви побачите сторінку з іншими пʼятьма країнами з бази даних: другу сторінку записів. \nПридивившись більш уважно, ви побачите, що URL в браузері також змінюється на\n\n```\nhttps://hostname/index.php?r=country%2Findex&page=2\n```\n\nЗа лаштунками, [[yii\\data\\Pagination|Pagination]] надає всю необхідну функціональність для розділення набору даних на сторінки:\n\n* Спочатку, [[yii\\data\\Pagination|Pagination]] представляє першу сторінку, яка відображає країни запитом SELECT \n  з умовою `LIMIT 5 OFFSET 0`. В результаті, будуть відображені перші знайдені пʼять країн.\n* Віджет [[yii\\widgets\\LinkPager|LinkPager]] відображає кнопки сторінок з URL-адресами створеними за допомогою \n  [[yii\\data\\Pagination::createUrl()|Pagination]]. URL-адреси будуть містити параметр запиту `page`, \n  який представляє різні номери сторінок.\n* Якщо натиснути кнопку \"2\", новий запит для маршруту `country/index` спрацьовує і обробляється.\n  [[yii\\data\\Pagination|Pagination]] зчитає параметр `page` з URL-запиту і встановить для поточної сторінки номер 2.\n  Таким чином, новий запит до БД буде мати умову `LIMIT 5 OFFSET 5` і поверне наступні пʼять країн для відображення.\n\n\nПідсумок <span id=\"summary\"></span>\n--------\n\nВ цьому розділі ви дізналися, як працювати з базою даних. Ви також дізналися, як вибирати і відображати дані на сторінках \nза допомогою [[yii\\data\\Pagination]] і [[yii\\widgets\\LinkPager]].\n\nУ наступному розділі ви дізнаєтеся, як використовувати потужний інструмент генерування коду, що називається [Gii](https://github.com/yiisoft/yii2-gii/blob/master/docs/guide-uk/README.md), \nякий допоможе вам швидко створювати деякі часто необхідні функції, такі як Create-Read-Update-Delete (CRUD) \nоперації для роботи з даними в таблицях баз даних. Насправді, код, який ви щойно написали,\nв Yii можливо автоматично згенерувати за допомогою інструменту Gii.\n"
  },
  {
    "path": "docs/guide-uk/start-forms.md",
    "content": "Робота з формами\n================\n\nВ даному розділі буде описано як створити нову сторінку з формою для отримання даних від користувачів.\nНа сторінці буде розміщена форма з полями, де можна буде вказати ім’я та адресу електронної пошти.\nПісля отримання цих двох фрагментів інформації від користувача, сторінка відобразить введені значення знову для підтвердження.\n\nДля досягнення даної цілі, крім створення [дії](structure-controllers.md) і\nдвох [представлень](structure-views.md), ви також створите [модель](structure-models.md).\n\nВ даному керівництві ви дізнаєтесь як:\n\n* створити [модель](structure-models.md) для даних, введених користувачем через форму;\n* оголосити правила перевірки введених даних;\n* створити HTML-форму в [представленні](structure-views.md).\n\n\nСтворення моделі <span id=\"creating-model\"></span>\n----------------\n\nДані, отримувані від користувача, будуть представлятись моделлю класу `EntryForm` як показано нижче, який\nзберігатиметься у файлі `models/EntryForm.php`. Детальніше про присвоєння імен файлам класів читайте в розділі\n[Автозавантаження класів](concept-autoloading.md).\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse Yii;\nuse yii\\base\\Model;\n\nclass EntryForm extends Model\n{\n    public $name;\n    public $email;\n\n    public function rules()\n    {\n        return [\n            [['name', 'email'], 'required'],\n            ['email', 'email'],\n        ];\n    }\n}\n```\n\nДаний клас успадкований від класу [[yii\\base\\Model]], який є складовою частиною фреймворку і зазвичай використовується для\nроботи з даними форм.\n\n> Info: Клас [[yii\\base\\Model]] використовується як батьківський клас для класів моделей, які *не* асоційовані з таблицями бази даних.\nКлас [[yii\\db\\ActiveRecord]] зазвичай є батьківським для класів моделей, що відповідають таблицям бази даних.\n\nКлас `EntryForm` містить дві публічні властивості `name` та `email`, які використовуються для зберігання\nданих, вказаних користувачем. Він також містить метод `rules()`, який повертає набір\nправил перевірки даних. Правила перевірки, оголошені у вищезазначеному коді означають наступне:\n\n* поля `name` і `email` обов’язкові для заповнення;\n* поле `email` повинно містити правильну адресу електронної пошти.\n\nЯкщо об’єкт `EntryForm` заповнений даними користувача, то для їх перевірки ви можете викликати метод\nцього об’єкту [[yii\\base\\Model::validate()|validate()]]. У випадку\nневдалої перевірки властивість [[yii\\base\\Model::hasErrors|hasErrors]] дорівнюватиме `true`.\nЗа допомогою [[yii\\base\\Model::getErrors|errors]] можна дізнатись, які саме помилки перевірки виникли.\n\n```php\n<?php\n$model = new EntryForm();\n$model->name = 'Qiang';\n$model->email = 'bad';\nif ($model->validate()) {\n    // Все добре!\n} else {\n    // Невдача!\n    // Використовуйте $model->getErrors()\n}\n```\n\n\nСтворення дії <span id=\"creating-action\"></span>\n-------------\n\nДалі необхідно створити дію `entry` в контролері `site`, яка буде використовувати нову модель. Процес \nстворення та використання дій описано у розділі [Говоримо \"Привіт\"](start-hello.md).\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\nuse app\\models\\EntryForm;\n\nclass SiteController extends Controller\n{\n    // ...наявний код...\n\n    public function actionEntry()\n    {\n        $model = new EntryForm();\n\n        if ($model->load(Yii::$app->request->post()) && $model->validate()) {\n            // дані в $model успішно перевірені\n\n            // тут робимо щось корисне з $model ...\n \n            return $this->render('entry-confirm', ['model' => $model]);\n        } else {\n            // або сторінка відображається вперше, або ж є помилка в даних\n            return $this->render('entry', ['model' => $model]);\n        }\n    }\n}\n```\n\nДія спочатку створює об’єкт `EntryForm`. Потім вона намагається заповнити модель\nданими із масиву `$_POST`, доступ до якого забезпечується в Yii за допомогою [[yii\\web\\Request::post()]].\nЯкщо модель успішно заповнена, тобто користувач відправив дані з HTML-форми,\nто для перевірки даних буде викликаний метод [[yii\\base\\Model::validate()|validate()]].\n\n> Info: `Yii::$app` являє собою глобально доступний екземпляр-одинак [додатка](structure-applications.md)\n(singleton). Одночасно це є [Service Locator](concept-service-locator.md), який\nнадає компоненти, типу  `request`, `response`, `db` і так далі для доступу до специфічної функціональності.\nУ вищезазначеному коді для доступу до даних з `$_POST` був використаний компонент екземпляру додатка `request`.\n\nЯкщо все гаразд, дія зобразить представлення `entry-confirm`, яке покаже користувачу вказані ним дані.\nЯкщо дані не передані або містять помилки, буде зображено представлення `entry` з\nHTML-формою та повідомленнями про присутні помилки перевірки даних.\n\n> Note: У цьому дуже простому прикладі ми просто відобразили сторінку підтвердження відправлення даних. На практиці,\nви повинні розглянути використання [[yii\\web\\Controller::refresh()|refresh()]] або [[yii\\web\\Controller::redirect()|redirect()]]\nдля уникнення [проблеми повторного відправлення даних форми](https://en.wikipedia.org/wiki/Post/Redirect/Get).\n\n\nСтворення представлення <span id=\"creating-views\"></span>\n-----------------------\n\nНа завершення, створюємо два файли представлення з іменами `entry-confirm` і `entry`, котрі зображаються дією `entry`\nз минулого підрозділу.\n\nПредставлення `entry-confirm` просто зображає ім’я та адресу електронної пошти. Воно мусить бути збережене у файлі `views/site/entry-confirm.php`.\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n<p>Ви вказали наступну інформацію:</p>\n\n<ul>\n    <li><label>Ім’я</label>: <?= Html::encode($model->name) ?></li>\n    <li><label>Адреса електронної пошти</label>: <?= Html::encode($model->email) ?></li>\n</ul>\n```\n\nПредставлення `entry` відображає HTML-форму. Воно мусить бути збережене у файлі `views/site/entry.php`.\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n?>\n<?php $form = ActiveForm::begin(); ?>\n\n    <?= $form->field($model, 'name') ?>\n\n    <?= $form->field($model, 'email') ?>\n\n    <div class=\"form-group\">\n        <?= Html::submitButton('Надіслати', ['class' => 'btn btn-primary']) ?>\n    </div>\n\n<?php ActiveForm::end(); ?>\n```\n\nДля побудови HTML-форми представлення використовує потужний [віджет](structure-widgets.md)\n[[yii\\widgets\\ActiveForm|ActiveForm]]. Методи `begin()` і `end()` виводять відкриваючий і закриваючий теги форми\nвідповідно. Між цими викликами створюються поля для заповнення за допомогою метода\n[[yii\\widgets\\ActiveForm::field()|field()]]. Першим іде поле \"name\",\nдругим — \"email\". Далі для генерування кнопки відправлення даних викликається метод\n[[yii\\helpers\\Html::submitButton()]].\n\n\nСпробуємо <span id=\"trying-it-out\"></span>\n---------\n\nЩоб побачити, як це працює, відкрийте в браузері наступний URL:\n\n```\nhttps://hostname/index.php?r=site%2Fentry\n```\n\nВи побачите сторінку з формою і двома полями для заповнення. Перед кожним полем є надпис, який вказує, яку саме\nінформацію слід вказувати. Якщо ви натиснете на кнопку відправлення даних без самих даних або якщо вкажете адресу\nелектронної пошти в невірному форматі, то ви побачите повідомлення про помилку біля кожного проблемного поля.\n\n![Форма з помилками перевірки](images/start-form-validation.png)\n\nПісля введення вірних даних і їх відправлення, ви побачите нову сторінку з даними, які щойно вказали.\n\n![Підтвердження введених даних](images/start-entry-confirmation.png)\n\n\n### Як працює вся ця \"магія\" <span id=\"magic-explained\"></span>\n\nВи можливо здивовані роботою HTML-форми поза лаштунками, тому що може здаватись магічним те, як\nзображається підпис до кожного поля та виводяться повідомлення про помилки при некоректному заповненні\nй те що все це відбувається без перезавантаження сторінки.\n\nТак, початкова перевірка даних дійсно проходить на стороні клієнта за допомогою JavaScript, і повторно перевіряється\nна стороні сервера (PHP). [[yii\\widgets\\ActiveForm]] достатньо продуманий, щоб взяти правила перевірки, які ви \nоголосили в `EntryForm`, перетворити їх в JavaScript код і використовувати його для проведення перевірки. \nНа випадок, якщо в браузері буде вимкнено JavaScript перевірка проходить і на стороні сервера, як показано в методі \n`actionEntry()`. Це дає впевненість в тому, що дані коректні за будь-яких обставин.\n\n> Warning: Перевірка даних на стороні клієнта - це зручність, яка забезпечує краще сприйняття користувачем.\nПеревірка на стороні сервера завжди обов’язкова, не зважаючи на клієнтську.\n\nПідписи для полів генеруються методом [[yii\\widgets\\ActiveForm::field()|field()]], на основі імен властивостей моделі.\nНаприклад, підпис `Name` генерується для властивості `name`.\n\nВи можете модифікувати підписи в межах представлення\nнаступним чином:\n\n```php\n<?= $form->field($model, 'name')->label('Ваше ім’я') ?>\n<?= $form->field($model, 'email')->label('Ваша адреса електронної пошти') ?>\n```\n\n> Info: У Yii є велика кількість віджетів, які дозволяють швидко будувати складні та динамічні представлення.\nЯк ви дізнаєтесь пізніше, розробляти нові віджети доволі просто. Багато із представлень можна винести у віджети,\nщоб використовувати їх повторно в інших частинах і тим самим спростити розробку в майбутньому.\n\nПідсумок <span id=\"summary\"></span>\n--------\n\nВ даному розділі ви випробували кожну частину шаблона проектування MVC. Ви дізналися, як\nстворити клас моделі для репрезентації даних, вказаних користувачем, і як перевіряти ці дані.\n\nТакож ви дізналися, як отримувати дані від користувачів та як повертати їх для відображення у браузері. Ця задача може займати \nбагато часу в процесі розробки. Yii надає потужні віджети, які роблять задачу максимально простою.\n\nВ наступному розділі ви дізнаєтесь як працювати з базами даних, що необхідно в більшості додатків.\n"
  },
  {
    "path": "docs/guide-uk/start-gii.md",
    "content": "Генерування коду за допомогою Gii\n=================================\n\nЦей розділ описує, як використовувати [Gii](https://github.com/yiisoft/yii2-gii/blob/master/docs/guide-uk/README.md)\nдля автоматичного генерування коду, який реалізовує найбільш поширений функціонал для веб-сайту. Використання Gii для \nавтоматичного створення коду є простою процедурою введення правильної інформації згідно з інструкціями, які містяться\nна веб-сторінках Gii.\n\nВ даному керівництві ви дізнаєтесь як:\n\n* увімкнути Gii у вашому додатку;\n* використовувати Gii для генерування класів Active Record;\n* використовувати Gii для генерування коду, який реалізовує операції CRUD для таблиці бази даних;\n* налаштовувати код, що був згенерований Gii.\n\n\nПідготовка Gii <span id=\"starting-gii\"></span>\n--------------\n\n[Gii](https://github.com/yiisoft/yii2-gii/blob/master/docs/guide-uk/README.md) постачається в Yii як [модуль](structure-modules.md). \nВи можете підключати модуль Gii, налаштувавши відповідну властивість [[yii\\base\\Application::modules|modules]] додатка. В залежності\nвід того, як ви створили ваш додаток, наступний код можливо вже присутній в конфігураційному файлі `config/web.php`:\n\n```php\n$config = [ ... ];\n\nif (YII_ENV_DEV) {\n    $config['bootstrap'][] = 'gii';\n    $config['modules']['gii'] = [\n        'class' => 'yii\\gii\\Module',\n    ];\n}\n```\n\nНаведена вище конфігурація підключає модуль `gii`, представлений класом [[yii\\gii\\Module]], у тому випадку,\nколи ваш додаток знаходиться в [середовищі розробки](concept-configurations.md#environment-constants).\n\nЯкщо ви перевірите [вхідний скрипт](structure-entry-scripts.md) `web/index.php` вашого додатку, то\nзнайдете наступний рядок, який визначає `YII_ENV_DEV`.\n\n```php\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n```\n\nЗавдяки даному рядку, ваш додаток знаходиться в режимі розробки, і буде підключати Gii, із вищевказаною конфігурацією.\nТепер ви можете отримати доступ до Gii за наступною URL-адресою:\n\n```\nhttps://hostname/index.php?r=gii\n```\n\n> Note: Якщо ви звертаєтесь до Gii від не локальної машини, доступ буде заборонений за замовчуванням із міркувань\nбезпеки. Ви можете налаштувати Gii, додавши дозволені IP-адреси, як показано нижче\n```php\n'gii' => [\n    'class' => 'yii\\gii\\Module',\n    'allowedIPs' => ['127.0.0.1', '::1', '192.168.0.*', '192.168.178.20'] // налаштувати для ваших потреб\n],\n```\n\n![Gii](images/start-gii.png)\n\n\nГенерування класу Active Record <span id=\"generating-ar\"></span>\n-------------------------------\n\nВикористовуючи Gii для генерування класу Active Record, виберіть \"Model Generator\" (натиснувши на посилання на сторінці Gii).\nДалі заповніть форму наступними даними:\n\n* Ім’я таблиці: `country`\n* Клас моделі: `Country`\n\n![Генератор моделі](images/start-gii-model.png)\n\nДалі, натисніть на кнопку \"Перегляду\" (\"Preview\"). Ви побачите файл `models/Country.php`, який буде створено в результаті\nданих дій. Ви можете натиснути на ім’я файлу класу для перегляду його вмісту.\n\nЯкщо при використанні Gii, раніше вже був створений файл моделі, то він буде перезаписаний. Для того, щоб переглянути \nвідмінності в коді натисніть на кнопку `diff` поруч з ім’ям файлу.\n\n![Генератор моделі: попередній перегляд](images/start-gii-model-preview.png)\n\nПри перезаписі наявного файлу, позначте пункт \"перезаписати\" (\"overwrite\"), а потім натисніть кнопку \"Створити\" (\"Generate\"). При створенні нового файлу, ви можете просто натиснути на кнопку \"Створити\" (\"Generate\").\n\nДалі, ви побачите сторінку підтвердження із відображенням коду, який був успішно згенерований. Якщо ви перезаписували вже наявний \nфайл, то побачите повідомлення про те, що він був переписаний і замінений на щойно згенерований код.\n\n\nГенерування коду CRUD <span id=\"generating-crud\"></span>\n---------------------\n\nCRUD - це акронім від англійських слів Create, Read, Update, Delete (Створити, Прочитати, Оновити, Видалити), що представляє чотири основні операції над даними на більшості веб-сайтів.\nЩоб реалізувати функціонал CRUD використовуючи Gii, оберіть \"CRUD Generator\" (натиснувши відповідну кнопку на сторінці Gii). Наприклад, для таблиці \"country\", заповніть форму наступним чином:\n\n* Клас моделі: `app\\models\\Country`\n* Клас моделі пошуку: `app\\models\\CountrySearch`\n* Клас контролера: `app\\controllers\\CountryController`\n\n![Генератор CRUD](images/start-gii-crud.png)\n\nДалі, натисніть на кнопку \"Перегляду\" (\"Preview\"). Ви побачите список файлів, які будуть створені, як показано нижче.\n\n![Генератор CRUD: попередній перегляд](images/start-gii-crud-preview.png)\n\nЯкщо ви попередньо створили контролер `controllers/CountryController.php` і файл представлення `views/country/index.php` \n(в розділі \"Робота з базами даних\" даного посібника), позначте пункт \"перезаписати\" і замініть їх. (Попередні версії файлів на мають повного функціоналу CRUD.)\n\n\nСпробуємо <span id=\"trying-it-out\"></span>\n---------\n\nЩоб побачити як це працює, відкрийте в браузері наступний URL:\n\n```\nhttps://hostname/index.php?r=country%2Findex\n```\n\nВи побачите таблицю даних, що показує країни з таблиці бази даних. Ви можете відсортувати дані,\nабо відфільтрувати їх, вказавши умови фільтрації в заголовках колонок.\n\nДля кожної країни, що відображається в таблиці, ви можете використати функції перегляду деталей, оновлення даних, або взагалі видалити її.\nВи також можете натиснути на кнопку \"Створити країну\" (\"Create Country\") над таблицею, яка переадресує вас на форму створення нової країни.\n\n![Таблиця даних країн](images/start-gii-country-grid.png)\n\n![Оновлення даних країни](images/start-gii-country-update.png)\n\nНижче наведено перелік файлів, згенерованих Gii, у тому разі, якщо ви захочете дослідити як реалізовані ці можливості, \nабо налаштувати їх під свої потреби:\n\n* Контролер: `controllers/CountryController.php`\n* Моделі: `models/Country.php` і `models/CountrySearch.php`\n* Представлення: `views/country/*.php`\n\n> Info: Gii - це гнучкий і розширюваний інструмент для генерування коду. При правильному використані, він дозволить\nвам значно прискорити розробку ваших додатків. Для більш докладної інформації, будь ласка, зверніться до розділу\n[Gii](https://github.com/yiisoft/yii2-gii/blob/master/docs/guide-uk/README.md).\n\n\nПідсумок <span id=\"summary\"></span>\n--------\n\nВ цьому розділі ви дізналися як, використовуючи Gii, генерувати код, який реалізує повну функціональність CRUD для \nманіпулювання даними, що зберігаються в таблицях баз даних.\n"
  },
  {
    "path": "docs/guide-uk/start-hello.md",
    "content": "Говоримо \"Привіт\"\n=================\n\nВ даному розділі розглянемо як створити нову сторінку з надписом \"Привіт\".\nУ процесі вирішення задачі ви створите [дію контролера](structure-controllers.md#creating-actions) і\n[представлення](structure-views.md):\n\n* Додаток опрацює запит і передасть управління відповідній дії,\n* а дія, у свою чергу, відобразить представлення з надписом \"Привіт\" кінцевому користувачу.\n\nВ даному керівництві ви дізнаєтесь як:\n\n1. створити [дію](structure-controllers.md#creating-actions), яка буде відповідати на запити;\n2. створити [представлення](structure-views.md), щоб скомпонувати вміст відповіді;\n3. додаток відправляє запити до [дії](structure-controllers.md#creating-actions).\n\n\nСтворення дії <span id=\"creating-action\"></span>\n-------------\n\nДля нашої задачі знадобиться [дія](structure-controllers.md#creating-actions) `say`, яка зчитує\nпараметр `message` із запиту і відображає його значення користувачу. Якщо в запиті\nвідсутній параметр `message`, то дія буде відображати \"Привіт\".\n\n> Info: [Дії](structure-controllers.md#creating-actions) являються об’єктами, на які користувач може посилатись для\n  виконання. Дії згруповані в [контролери](structure-controllers.md). Результатом виконання\n  дії є відповідь, яку отримує користувач.\n\nДії повинні оголошуватись в [контролерах](structure-controllers.md). Для простоти, ви можете\nоголосити дію `say` в уже наявному контролері `SiteController`, який визначений\nу файлі класу `controllers/SiteController.php`:\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    // ...наявний код...\n\n    public function actionSay($message = 'Привіт')\n    {\n        return $this->render('say', ['message' => $message]);\n    }\n}\n```\n\nВ наведеному коді дія `say` визначена як метод `actionSay` в класі `SiteController`.\nYii використовує префікс `action` для того, щоб відрізняти методи-дії від звичайних методів у класі контролеру.\nНазва після префікса `action` вважається ідентифікатором відповідної дії.\n\nКоли черга доходить до іменування дій, слід розуміти як Yii поводиться з ідентифікаторами дій. Ідентифікатори дій можуть\nбути лише в нижньому регістрі. Якщо ідентифікатор складається з декількох слів, вони з’єднуються дефісами\n(наприклад, `create-comment`). Імена методів дій отримуються шляхом видалення дефісів з ідентифікатора,\nперетворення першої літери кожного слова у верхній регістр і додавання префікса `action`. Наприклад,\nідентифікатор дії `create-comment` відповідає методу `actionCreateComment`.\n\nМетод дії у нашому прикладі приймає параметр `$message`, який за замовчуванням визначено як `\"Привіт\"` (так само,\nяк ви звикли визначати значення за замовчуванням для аргументів функції у PHP). Коли додаток\nотримує запит і визначає, що дія `say` відповідає за його опрацювання, додаток призначає\nцьому параметру значення однойменного параметра із запиту. Іншими словами, якщо запит містить\nпараметр `message` зі значенням `\"До побачення\"`, то змінній `$message` всередині дії буде призначено це значення.\n\nВсередині метода дії, для відображення [представлення](structure-views.md) з ім’ям `say`, використовується метод\n[[yii\\web\\Controller::render()|render()]]. Для того, щоб вивести повідомлення,\nу представлення передається параметр `message`. Результат формування повертається методом дії. Цей результат приймається\nдодатком та відображається кінцевому користувачу в браузері (як частина цілої HTML-сторінки).\n\n\nСтворення представлення <span id=\"creating-view\"></span>\n-----------------------\n\n[Представлення](structure-views.md) є скриптами, які використовуються для генерування вмісту відповіді.\nДля нашого додатку ви створите представлення `say`, яке буде виводити параметр `message`, отриманий із методу дії:\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n<?= Html::encode($message) ?>\n```\n\nПредставлення `say` мусить бути збережено у файлі `views/site/say.php`. Коли метод [[yii\\web\\Controller::render()|render()]]\nвикликається в дії, він буде шукати PHP-файл з ім’ям виду `views/ControllerID/ViewName.php`.\n\nВарто зазначити, що у вищезазначеному коді параметр `message` [[yii\\helpers\\Html::encode()|екранується для HTML]]\nперед відображенням. Це обов’язково, бо параметр приходить від користувача, котрий може спробувати провести\n[XSS атаку](https://uk.wikipedia.org/wiki/%D0%9C%D1%96%D0%B6%D1%81%D0%B0%D0%B9%D1%82%D0%BE%D0%B2%D0%B8%D0%B9_%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D1%96%D0%BD%D0%B3),\nшляхом вставки небезпечного JavaScript кода.\n\nВи можете доповнити представлення `say` HTML-тегами, текстом або кодом PHP.\nФактично, представлення `say` є простим PHP-скриптом, який виконується методом [[yii\\web\\Controller::render()|render()]].\nВміст, який формується скриптом представлення, буде передано додатком користувачу.\n\n\nСпробуємо <span id=\"trying-it-out\"></span>\n---------\n\nПісля створення дії і представлення, ви можете перейти на нову сторінку по наступному URL:\n\n```\nhttps://hostname/index.php?r=site%2Fsay&message=Привіт+світ\n```\n\n![Привіт, світ](images/start-hello-world.png)\n\nБуде відображена сторінка з надписом \"Привіт світ\". Вона використовує ту ж шапку та футер, що і решта сторінок додатка.\n\nЯкщо ви не вкажете параметр `message` в URL, то побачите на сторінці лише \"Привіт\". Це відбувається тому, що `message` передається як параметр в метод `actionSay()`, а коли він не вказаний,\nвикористовується значення за замовчуванням \"Привіт\".\n\n> Info: Нова сторінка використовує ту ж шапку та футер, що й решта сторінок, тому що метод [[yii\\web\\Controller::render()|render()]]\nавтоматично підставляє результат формування представлення `say` в, так званий, [макет](structure-views.md#layouts), який в даному\nвипадку розміщено у `views/layouts/main.php`.\n\nПараметр `r` у вищезазначеному URL потребує додаткових пояснень. Він вказує [маршрут](runtime-routing.md), що являє собою унікальний для додатка ідентифікатор,\nякий вказує на дію. Його формат `ControllerID/ActionID`. Коли додаток отримує запит,\nвін перевіряє цей параметр і, використовуючи частину `ControllerID`, визначає який контролер\nслід використовувати для опрацювання запиту. Потім, контролер використовує частину `ActionID`,\nщоб визначити яку дію використовувати для виконання реальної роботи. В нашому випадку маршрут `site/say`\nбуде відповідати контролеру `SiteController` і його дії `say`. В результаті,\nдля опрацювання запиту буде викликано метод `SiteController::actionSay()`.\n\n> Info: Як і дії, контролери також мають ідентифікатори, котрі однозначно визначають їх в додатку.\nІдентифікатори контролерів використовують ті ж самі правила іменування, що і ідентифікатори дій. Імена класів контролерів\nотримуються шляхом видалення дефісів з ідентифікатора, перетворення першої літери кожного слова у верхній регістр\nі додавання в кінець слова `Controller`. Наприклад, ідентифікатор контролера `post-comment` відповідає\nімені класу контролера `PostCommentController`.\n\n\nПідсумок <span id=\"summary\"></span>\n--------\n\nВ даному розділі ви торкнулися таких частин шаблону проектування MVC як контролери та представлення.\nВи створили дію, як частину контролера, для опрацювання специфічного запиту. А також ви створили представлення\nдля формування вмісту відповіді. В цьому процесі ніяк не використовувалась модель, оскільки в ролі даних виступає тільки простий параметр `message`.\n\nТакож ви ознайомились із концепцією маршрутизації, котра є сполучною ланкою між запитом користувача і дією контролера.\n\nВ наступному розділі ви дізнаєтесь як створити модель та додати нову сторінку з HTML-формою.\n"
  },
  {
    "path": "docs/guide-uk/start-installation.md",
    "content": "Встановлення Yii\n================\n\nВи можете встановити Yii двома шляхами: використовуючи менеджер пакунків [Composer](https://getcomposer.org/) або завантаживши файл архіву.\nПерший варіант є бажанішим, тому що дозволяє встановлювати нові [розширення](structure-extensions.md) або оновлювати Yii простим виконанням однієї команди.\n\nПісля стандартного встановлення Yii ми отримуємо як фреймворк, так і шаблон проекту. Шаблон проекту - це робочий проект Yii,\nв якому реалізовано деякий базовий функціонал, такий як система входу/виходу користувачів, форма зворотнього зв’язку і т. д.\nЙого код організовано в рекомендований спосіб. Таким чином, це може слугувати гарною відправною точкою для ваших проектів.\n\nУ цьому та у наступних кількох розділах буде описано, як встановити Yii з так званим *Базовим шаблоном проекту* та\nяк реалізувати нові можливості на базі цього шаблону. Також Yii надає інший шаблон із назвою\n[Розширений шаблон проекту](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide-uk/README.md), який краще використовувати у середовищі розробки в команді\nдля розробки складних додатків.\n\n> Info: Базовий шаблон проекту підходить для розробки 90 відсотків веб-додатків. Він відрізняється\n  від Розширеного шаблону проекту в основному організацією коду. Якщо ви малознайомі з Yii, наполегливо\n  рекомендується використовувати Базовий шаблон проекту із-за його простоти, але й достатньої функціональності.\n\n\nВстановлення за допомогою Composer <span id=\"installing-via-composer\"></span>\n----------------------------------\n\nЯкщо у вас все ще не встановлено Composer, то це можна зробити за допомогою інструкції на\n[getcomposer.org](https://getcomposer.org/download/). Користувачам Linux та Mac OS X потрібно виконати наступні команди:\n\n```bash\n    curl -sS https://getcomposer.org/installer | php\n    mv composer.phar /usr/local/bin/composer\n```\n\nПри роботі з Windows, необхідно завантажити та запустити [Composer-Setup.exe](https://getcomposer.org/Composer-Setup.exe).\n\nВ разі наявності проблем або якщо вам необхідна додаткова інформація,\nзверніться до [документації Composer](https://getcomposer.org/doc/).\n\nЯкщо ж Composer вже було встановлено раніше, переконайтесь, що використовуєте його останню версію.\nВи можете оновити Composer простою командою `composer self-update`.\n\nПісля встановлення Composer, встановити Yii можна виконавши наступну команду з директорії, яка доступна через Web:\n\n```bash\n    composer global require \"fxp/composer-asset-plugin:^1.4.1\"\n    composer create-project --prefer-dist yiisoft/yii2-app-basic basic\n```\n\nПерша команда встановить [плагін ресурсів composer (composer-asset-plugin)](https://github.com/fxpio/composer-asset-plugin),\nщо дозволить керувати залежностями пакунків Bower та NPM за допомогою Composer. Цю команду потрібно виконати лише\nодин раз. Друга команда встановить Yii у директорію під назвою `basic`. За бажанням, ви можете обрати інше ім’я для директорії.\n\n> Note: Під час встановлення може статися так, що Composer запитає облікові дані від вашого профілю на GitHub,\nчерез встановлені обмеження запитів GitHub API. Це є нормальним, оскільки Composer повинен отримати багато інформації\nдля всіх пакунків із GitHub. Надання облікових даних профілю GitHub збільшить кількість запитів до API, потрібних для\nподальшої роботи Composer. Для більш детальної інформації, будь ласка, зверніться до\n[документації Composer](https://getcomposer.org/doc/articles/troubleshooting.md#api-rate-limit-and-oauth-tokens).\n\n\n> Tip: Якщо ви хочете встановити останню нестабільну версію Yii, ви можете виконати наступну команду,\nяка додає опцію [stability](https://getcomposer.org/doc/04-schema.md#minimum-stability):\n```bash\n    composer create-project --prefer-dist --stability=dev yiisoft/yii2-app-basic basic\n```\nВарто зауважити, що нестабільну версію Yii не можна використовувати на робочому сервері, оскільки вона може порушити\nвиконання робочого коду.\n\n\nВстановлення з архіву <span id=\"installing-from-archive-file\"></span>\n---------------------\n\nВстановлення Yii з архіву складається з трьох кроків:\n\n1. Завантажте архів за адресою [yiiframework.com](https://www.yiiframework.com/download/);\n2. Розпакуйте архів в директорію, доступну через Web.\n3. Відредагуйте файл конфігурації `config/web.php` - необхідно ввести таємний ключ до пункту `cookieValidationKey`\n   (це виконується автоматично при вставленні Yii через Composer):\n\n   ```php\n   // !!! встановити таємний ключ до наступного пункту (якщо порожній) - це необхідно для перевірки кукі\n   'cookieValidationKey' => 'enter your secret key here',\n   ```\n\n\nІнші параметри встановлення <span id=\"other-installation-options\"></span>\n---------------------------\n\nВище наведені інструкції показують як встановити Yii та створити базовий веб-додаток, який працює \"з коробки\".\nЦей підхід є гарною відправною точкою для більшості проектів, як малих так і великих. Це особливо підходить для тих, хто тільки\nрозпочинає вивчати Yii.\n\nАле є ще й інші варіанти встановлення:\n\n* Якщо вам потрібен тільки один фреймворк і ви хотіли б створити додаток з нуля, використовуйте інструкцію, \n  що описана у розділі [Створення додатка з нуля](tutorial-start-from-scratch.md).\n* Якщо ви хочете розпочати з більш складного додатка, який краще підходить для роботи в команді, використовуйте\n  [Розширений шаблон проекту](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide-uk/README.md).\n\n\nПеревірка встановлення <span id=\"verifying-installation\"></span>\n----------------------\n\nПісля успішного встановлення ви можете налаштувати свій веб-сервер (див. наступний розділ)\nабо використати [вбудований веб-сервер PHP](https://www.php.net/manual/en/features.commandline.webserver.php),\nвиконавши наступну консольну команду із директорії `web`:\n\n```bash\nphp yii serve\n```\n\n> Note: За замовчуванням, HTTP-server буде прослуховувати порт 8080. Проте, \nякщо цей порт вже використовується, або ви бажаєте таким чинов використовувати \nкілька додатків одразу - ви можете встановити, який саме порт використовувати.\nТільки додайтие аргумент --port:\n\n```bash\nphp yii serve --port=8888\n```\n\nТепер ви можете використати свій браузер для доступу до встановленого Yii додатку\nза наступним посиланням:\n\n```\nhttp://localhost:8080/\n```\n\n![Успішно встановлений Yii](images/start-app-installed.png)\n\nВи повинні побачити сторінку із привітанням \"Congratulations!\" у вашому браузері. Якщо ж ні, будь ласка, перевірте, чи задовольняють\nналаштування PHP вимоги Yii. Це можна зробити одним із наведених способів:\n\n* Скопіюйте файл `/requirements.php` до `/web/requirements.php` та використайте браузер для доступу до URL `http://localhost/requirements.php`\n* Виконайте наступні команди в консолі: \n\n  ```bash\n  cd basic\n  php requirements.php\n  ```\n\nНеобхідно налаштувати PHP таким чином, щоб він відповідав мінімальним вимогам Yii. Основна вимога — PHP версії 5.4 або вище.\nЯкщо ваш додаток працює з базою даних, необхідно встановити [розширення PHP PDO](https://www.php.net/manual/en/pdo.installation.php)\nта відповідний драйвер (наприклад, `pdo_mysql` для MySQL).\n\n\nНалаштування веб-серверів <span id=\"configuring-web-servers\"></span>\n-------------------------\n\n> Info: можете пропустити даний підрозділ, якщо ви тільки розпочали знайомитися з фреймворком\n  і не розгортаєте його на робочому сервері.\n\nДодаток, встановлений за інструкціями, наведеними вище, буде працювати одразу як\nз [Apache HTTP server](https://httpd.apache.org/), так і з [Nginx HTTP server](https://nginx.org/) на\nWindows, Mac OS X чи Linux із встановленим PHP 5.4 або вище. Yii 2.0 також сумісний із віртуальною машиною Фейсбука\n[HHVM](https://hhvm.com/), однак є деякі крайні випадки, де HHVM поводиться інакше,\nніж рідний PHP, тому ви повинні бути дуже уважними при використанні HHVM.  \n\nНа робочому сервері вам напевно захочеться змінити URL додатку з `https://www.example.com/basic/web/index.php`\nна `https://www.example.com/index.php`. Для цього необхідно змінити кореневу директорію в налаштуваннях веб-сервера на `basic/web`.\nДодатково можна сховати `index.php` із URL, як це описано у розділі [Маршрутизація та створення URL](runtime-routing.md). \nДалі буде описано як налаштувати Apache або Nginx для цих цілей.\n\n> Info: Встановлюючи `basic/web` кореневою директорією веб-сервера, ви забороняєте кінцевим користувачам доступ\n  до приватного коду додатка та важливих даних, які знаходяться на одному рівні\n  з `basic/web`. Це робить додаток більш захищеним.\n\n> Info: Якщо додаток працює на хостингу, де немає доступу\n  до налаштувань сервера, ви все ще можете змінити структуру додатка для покращення безпеки,\n  як описано в розділі [Робота на віртуальному хостингу](tutorial-shared-hosting.md).\n\n\n### Рекомендовані налаштування Apache <span id=\"recommended-apache-configuration\"></span>\n\nДодайте наступний код до файлу конфігурації `httpd.conf` веб-сервера Apache або в конфігурацію віртуального хоста. \nНе забудьте замінити `path/to/basic/web` на коректний шлях до `basic/web`.\n\n```\n# Встановлюємо кореневою директорією \"basic/web\"\nDocumentRoot \"path/to/basic/web\"\n\n<Directory \"path/to/basic/web\">\n    # використаємо mod_rewrite для підтримки гарних URL\n    RewriteEngine on\n    # Якщо запитуваний файл або директорія існують - звертаємось до них напряму\n    RewriteCond %{REQUEST_FILENAME} !-f\n    RewriteCond %{REQUEST_FILENAME} !-d\n    # Якщо ні - перенаправляємо запит на index.php\n    RewriteRule . index.php\n\n    # ...інші налаштування...\n</Directory>\n```\n\n\n### Рекомендовані налаштування Nginx <span id=\"recommended-nginx-configuration\"></span>\n\nДля використання [Nginx](https://wiki.nginx.org/) вам потрібно встановити PHP як [FPM SAPI](https://www.php.net/install.fpm).\nВикористовуйте наступні параметри Nginx, замінивши `path/to/basic/web` на коректний шлях до\n`basic/web`, а `mysite.test` на актуальний домен.\n\n```\nserver {\n    charset utf-8;\n    client_max_body_size 128M;\n\n    listen 80; ## \"слухаємо порт\" для ipv4\n    #listen [::]:80 default_server ipv6only=on; ## \"слухаємо порт\" для ipv6\n\n    server_name mysite.test;\n    root        /path/to/basic/web;\n    index       index.php;\n\n    access_log  /path/to/basic/log/access.log;\n    error_log   /path/to/basic/log/error.log;\n\n    location / {\n        # Перенаправляємо всі запити на index.php, якщо це не наявна директорія або файл\n        try_files $uri $uri/ /index.php?$args;\n    }\n\n    # розкоментуйте рядки нижче для запобігання обробки звернень Yii до не наявних статичних файлів\n    #location ~ \\.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ {\n    #    try_files $uri =404;\n    #}\n    #error_page 404 /404.html;\n\n    location ~ \\.php$ {\n        include fastcgi.conf;\n        fastcgi_pass   127.0.0.1:9000;\n        #fastcgi_pass unix:/var/run/php5-fpm.sock;\n        try_files $uri =404;\n    }\n\n    location ~ /\\.(ht|svn|git) {\n        deny all;\n    }\n}\n```\n\nВикористовуючи дану конфігурацію встановіть `cgi.fix_pathinfo=0` у файлі `php.ini`,\nщоб запобігти зайвим системним викликам `stat()`.\n\nВрахуйте також, що при використанні HTTPS необхідно задавати `fastcgi_param HTTPS on;` щоб Yii\nміг коректно визначати захищене з’єднання.\n"
  },
  {
    "path": "docs/guide-uk/start-looking-ahead.md",
    "content": "Наступні кроки\n==============\n\nЯкщо ви прочитали всю главу \"Перше знайомство\", то, напевно, ви створили повноцінний додаток Yii. У процесі ви дізналися, як реалізувати деякі найбільш часто\nвикористовувані функції, такі, як отримання даних від користувачів за допомогою HTML-форми, вибірка даних з бази даних\nі відображення даних із розділенням на сторінки. Також ви дізналися, як використовувати [Gii](https://github.com/yiisoft/yii2-gii/blob/master/docs/guide-uk/README.md)\nдля автоматичного генерування коду, що перетворює більшу частину процесу веб-розробки у завдання настільки просте, як заповнення деяких форм.\n\nУ цьому розділі зібрані ресурси пов’язані з Yii, які допоможуть вам бути більш продуктивними при використанні фреймворку.\n\n* Документація\n    - [Докладний Посібник](https://www.yiiframework.com/doc-2.0/guide-README.html):\n      Як випливає з назви, посібник точно визначає, як Yii повинен працювати і дає вам загальні вказівки\n      щодо його застосування. Це найважливіший Yii підручник, з яким ви маєте ознайомитись, перш ніж писати Yii код.\n    - [Довідник класів](https://www.yiiframework.com/doc-2.0/index.html):\n      Визначає використання кожного класу, представленого в Yii. Ним слід користуватися, коли ви пишете\n      код і хочете розібратися у використанні конкретного класу, методу, властивості. Довідник класів найкраще використовувати після контекстного розуміння всього фреймворку.\n    - [Wiki-статті](https://www.yiiframework.com/wiki/?tag=yii2):\n      Wiki-статті написані користувачами Yii на основі їх власного досвіду. Більшість з них написані\n      як рецепти з куховарської книги, які показують, як вирішити конкретні проблеми, використовуючи Yii. \n      Варто зауважити, що якість даних статей може бути не такою гарною, як у Докладному Посібнику, але вони корисні тим,\n      що охоплюють ширші теми і часто можуть забезпечити готовими рішеннями для подальшого використання.\n    - [Книги](https://www.yiiframework.com/books)\n* [Розширення](https://www.yiiframework.com/extensions/):\n  Yii пишається бібліотекою із тисяч розширень, внесених користувачами, які можуть бути легко підключені у ваші додатки та зробити розробку додатків ще швидшим і простішим.\n* Спільнота\n    - Форум: <https://forum.yiiframework.com/>\n    - Чат IRC: Канал #yii мережі Libera (<ircs://irc.libera.chat:6697/yii>)\n    - Чат Gitter: <https://gitter.im/yiisoft/yii2>\n    - GitHub: <https://github.com/yiisoft/yii2>\n    - Фейсбук: <https://www.facebook.com/groups/yiitalk/>\n    - Твіттер: <https://twitter.com/yiiframework>\n    - LinkedIn: <https://www.linkedin.com/groups/yii-framework-1483367>\n    - Stackoverflow: <https://stackoverflow.com/questions/tagged/yii2>\n"
  },
  {
    "path": "docs/guide-uk/start-workflow.md",
    "content": "Виконання додатків\n==================\n\nПісля встановлення Yii, базовий додаток буде доступний або по URL `https://hostname/basic/web/index.php`,\nабо по `https://hostname/index.php`, в залежності від налаштування веб-сервера. Даний розділ - загальне введення в\nорганізацію коду, вбудований функціонал і опрацювання запитів додатком Yii.\n\n> Info: Для спрощення, далі в даному посібнику припускається, що Yii встановлений в директорію `basic/web`,\n  яка, в свою чергу, встановлена, як коренева директорія в налаштуваннях веб-сервера. В результаті, звернувшись до URL\n  на зразок `https://hostname/index.php`, ви отримаєте доступ до додатку.\n  Будь ласка, відредагуйте URL-адреси наведені у прикладах відповідно до ваших потреб.\n\n\nФункціонал <span id=\"functionality\"></span>\n----------\n\nВстановлений базовий додаток складається з чотирьох сторінок:\n\n* домашня сторінка, відображається при переході по URL `https://hostname/index.php`;\n* сторінка \"About\" (\"Про нас\");\n* сторінка \"Contact\", що відображає форму зворотнього зв’язку, через яку користувач може звернутися до розробника за допомогою електронної пошти;\n* сторінка \"Login\", на якій відображається форма для аутентифікації користувачів. Спробуйте увійти з логіном/паролем\n  \"admin/admin\". Зверніть увагу на зміну пункту \"Login\" в головному меню на \"Logout\".\n\nЦі сторінки використовують спільну шапку та футер. Шапка містить головне меню, за\nдопомогою якого здійснюється навігація по сайту.\n\nУ нижній частині вікна ви зможете бачити системні повідомлення Yii - налагоджувальну інформацію,\nповідомлення про помилки, запити до бази даних і т. п. Відображенням даної інформацію керує\n[вбудований налагоджувач](https://github.com/yiisoft/yii2-debug/blob/master/docs/guide-uk/README.md), він записує і відображає інформацію про хід виконання додатку.\n\nКрім веб-додатка, існує консольний скрипт `yii`, що розташований в базовій директорії додатка.\nЦей скрипт може бути використаний для виконання фонових завдань або завдань обслуговування додатка.\nВсе це описано у розділі [Консольні команди](tutorial-console.md).\n\n\nСтруктура додатка Yii <span id=\"application-structure\"></span>\n---------------------\n\nНижче наведений перелік основних директорій і файлів вашого додатку (вважаємо, що додаток встановлений в директорію `basic`):\n\n```\nbasic/                  базова директорія додатка\n    composer.json       використовується Composer'ом, містить опис додатку\n    config/             містить конфігураційні файли\n        console.php     конфігурація консольного додатка\n        web.php         конфігурація веб-додатка\n    commands/           містить класи консольних команд\n    controllers/        містить класи контролерів\n    models/             містить класи моделей\n    runtime/            містить файли, які генерує Yii під час виконання додатку (журнали, кеш і т. п.)\n    vendor/             містить пакунки Composer'а і, власне, сам фреймворк Yii\n    views/              містить представлення додатку\n    web/                базова веб-директорія, містить файли, доступні через Web\n        assets/         містить файли ресурсів (javascript та css)\n        index.php       вхідний (або bootsrap) скрипт для додатку Yii, з нього розпочинається виконання додатку\n    yii                 скрипт виконання консольних команд Yii\n```\n\nВ цілому, додаток Yii можна розділити на дві категорії файлів: розміщенні в `basic/web` і розміщенні в інших директоріях.\nПерша категорія доступна через HTTP (наприклад, браузером), друга недоступна зовні, та і не повинна бути, оскільки містить службову інформацію.\n\nВ Yii реалізований шаблон проектування [модель-представлення-контролер (MVC)](https://uk.wikipedia.org/wiki/Model-View-Controller),\nщо відображається на вищезазначеній структурі директорій додатка. В директорії `models` знаходяться всі класи [моделей](structure-models.md),\nу `views` розміщені всі скрипти [представлень](structure-views.md), а в директорії `controllers`\nвсі класи [контролерів](structure-controllers.md) додатка.\n\nДіаграма нижче демонструє статичну структуру додатка.\n\n![Статична структура додатка](images/application-structure.png)\n\nВ кожному додатку Yii є місце входження в додаток, `web/index.php` - це єдиний PHP-скрипт доступний для виконання через Web.\nВін отримує вхідний запит і створює екземпляр [додатку](structure-applications.md).\n[Додаток](structure-applications.md) опрацьовує запит з допомогою його [компонентів](concept-components.md)\nі відправляє запит елементам MVC. [Віджети](structure-widgets.md) використовуються у [представленнях](structure-views.md)\nдля побудови складних та динамічних елементів інтерфейсу користувача.\n\n\nЖиттєвий цикл запиту <span id=\"request-lifecycle\"></span>\n--------------------\n\nНа діаграмі показано, як додаток відпрацьовує запит.\n\n![Життєвий цикл запиту](images/request-lifecycle.png)\n\n1. Користувач робить запит до [вхідного скрипту](structure-entry-scripts.md) `web/index.php`.\n2. Вхідний скрипт завантажує [конфігурацію](concept-configurations.md) додатка та створює екземпляр\n   [додатку](structure-applications.md) для наступного опрацювання запиту.\n3. Додаток визначає [маршрут](runtime-routing.md) запиту за допомогою компонента [запиту](runtime-requests.md) додатка.\n4. Додаток створює екземпляр [контролера](structure-controllers.md) для виконання запиту.\n5. Контролер, в свою чергу, створює [дію](structure-controllers.md) і накладає на неї фільтри.\n6. Якщо хоч один фільтр поверне помилку - виконання додатку зупиняється.\n7. Якщо всі фільтри пройдені - додаток виконується.\n8. Дія завантажує модель даних, скоріше за все із бази даних.\n9. Дія формує представлення та відображає в ньому дані (в т. ч. і отримані із моделі).\n10. Сформований результат передається компоненту [відповіді](runtime-responses.md) додатка.\n11. Компонент \"відповіді\" відправляє готовий результат роботи додатка браузеру користувача.\n"
  },
  {
    "path": "docs/guide-uk/structure-application-components.md",
    "content": "Компоненти додатка\n==================\n\nДодатки є [Локаторами служб](concept-service-locators.md). Вони зберігають багато так званих\n*компонентів додатку*, які надають різноманітні служби для обробки запитів. Наприклад,\nкомпонент `urlManager` відповідає за маршрутизацію веб-запитів до потрібного контролера; \nкомпонент `db` надає служби для роботи з базою даних; і т. д.\n\nКожний компонент додатка має свій унікальний ідентифікатор, який дозволяє ідентифікувати його серед інших різноманітних компонентів\nв одному і тому ж додатку. Ви можете отримати доступ до компонента за допомогою виразу:\n\n```php\n\\Yii::$app->componentID\n```\n\nНаприклад, ви можете використовувати `\\Yii::$app->db` для отримання [[yii\\db\\Connection|з’єднання з БД]],\nі `\\Yii::$app->cache` для отримання доступу до основного компонента [[yii\\caching\\Cache|кешу]], зареєстрованих в додатку.\n\nКомпонент додатка створюється при першому звертанні через попередній вираз. \nБудь-які подальші звертання будуть повертати той же екземпляр компонента.\n\nКомпонентами додатку можуть бути будь-які об’єкти. Ви можете зареєструвати їх за допомогою властивості \n[[yii\\base\\Application::components]] в [конфігурації додатка](structure-applications.md#application-configurations).\nНаприклад,\n\n```php\n[\n    'components' => [\n        // реєстрація компонента \"cache\" за допомогою назви класу\n        'cache' => 'yii\\caching\\ApcCache',\n\n        // реєстрація компонента \"db\" за допомогою масиву конфігурації\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=demo',\n            'username' => 'root',\n            'password' => '',\n        ],\n\n        // реєстрація компонента \"search\" за допомогою анонімної функції\n        'search' => function () {\n            return new app\\components\\SolrService;\n        },\n    ],\n]\n```\n\n> Info: Хоча ви можете зареєструвати стільки компонентів в додатку скільки вам потрібно, все ж таки варто робити це осмислено.\n  Компоненти додатку схожі на глобальні змінні. Використання дуже великої кількості компонентів додатку може потенційно зробити\n  ваш код складним для тестування і подальшої підтримки. У більшості випадків ви можете просто створити локальний компонент\n  і використовувати його при необхідності.\n\n\n## Попереднє завантаження компонентів <span id=\"bootstrapping-components\"></span>\n\nЯк згадувалося вище, компонент додатка буде створено при першому звертанні.\nЯкщо до нього не буде зроблено звертань під час запиту - його взагалі не буде створено.\nОднак, за необхідності, ви можете створювати екземпляр компонента додатка для кожного запиту, навіть якщо до нього не зверталися явно.\nЩоб зробити це, ви можете додати ідентифікатор компонента до властивості [[yii\\base\\Application::bootstrap|bootstrap]] додатка.\n\nНаприклад, наступна конфігурація додатка завжди гарантує створення компонента `log`:\n\n```php\n[\n    'bootstrap' => [\n        'log',\n    ],\n    'components' => [\n        'log' => [\n            // конфігурація для компонента \"log\"\n        ],\n    ],\n]\n```\n\n\n## Вбудовані компоненти додатку <span id=\"core-application-components\"></span>\n\nВ Yii є декілька *вбудованих* компонентів додатку із фіксованими ідентифікаторами та конфігураціями за замовчуванням. \nНаприклад, компонент [[yii\\web\\Application::request|request]] використовується для збору інформації про запит \nкористувача і розбору його у певний [маршрут](runtime-routing.md); компонент [[yii\\base\\Application::db|db]] \nявляє собою з’єднання з базою даних, через яке ви можете виконувати запити до бази даних. \nСаме з допомогою цих вбудованих компонентів Yii додатки можуть обробляти запити користувача.\n\nНижче наведено перелік вбудованих компонентів додатку. Ви можете налаштовувати їх так само, як і інші компоненти додатку.\nКоли ви сконфігуруєте вбудований компонент додатку і не вкажете клас цього компонента, то буде використовуватись \nзначення за замовчуванням.\n\n* [[yii\\web\\AssetManager|assetManager]]: керує колекціями ресурсів та публікацією ресурсів.\n  Більш детальна інформація наведена у розділі [Ресурси](structure-assets.md).\n* [[yii\\db\\Connection|db]]: являє собою з’єднання з базою даних, через яке ви можете виконувати запити до бази даних.\n  Зверніть увагу, що, при конфігурації даного компоненту, ви маєте вказати клас компонента, разом із рештою \n  необхідних параметрів, наприклад [[yii\\db\\Connection::dsn]].\n  Більш детальна інформація наведена у розділі [Об’єкти доступу до даних (DAO)](db-dao.md).\n* [[yii\\base\\Application::errorHandler|errorHandler]]: здійснює обробку помилок і виключень PHP.\n  Більш детальна інформація наведена у розділі [Обробка помилок](runtime-handling-errors.md).\n* [[yii\\i18n\\Formatter|formatter]]: форматує дані для відображення їх кінцевому користувачу. Наприклад, число може\n  бути відображене із роздільниками тисячних розрядів, дата може бути зображена у розширеному форматі.\n  Більш детальна інформація наведена у розділі [Форматування даних](output-formatting.md).\n* [[yii\\i18n\\I18N|i18n]]: підтримує переклад і форматування повідомлень.\n  Більш детальна інформація наведена у розділі [Інтернаціоналізація](tutorial-i18n.md).\n* [[yii\\log\\Dispatcher|log]]: керує призначенням журналу.\n  Більш детальна інформація наведена у розділі [Журналювання](runtime-logging.md).\n* [[yii\\swiftmailer\\Mailer|mail]]: надає можливості для побудови і відправлення поштових повідомлень.\n  Більш детальна інформація наведена у розділі [Робота з поштою](tutorial-mailing.md).\n* [[yii\\base\\Application::response|response]]: являє собою об’єкт відповіді сервера кінцевим користувачам.\n  Більш детальна інформація наведена у розділі [Відповіді](runtime-responses.md).\n* [[yii\\base\\Application::request|request]]: являє собою об’єкт запиту, який сервер отримує від кінцевих користувачів.\n  Більш детальна інформація наведена у розділі [Запити](runtime-requests.md).\n* [[yii\\web\\Session|session]]: надає інформацію про сесію. \n  Даний компонент доступний тільки у [[yii\\web\\Application|веб-додатках]].\n  Більш детальна інформація наведена у розділі [Сесії та кукі](runtime-sessions-cookies.md).\n* [[yii\\web\\UrlManager|urlManager]]: використовується для розбору і побудови URL.\n  Більш детальна інформація наведена у розділі [Маршрутизація та створення URL](runtime-routing.md).\n* [[yii\\web\\User|user]]: надає інформацію про аутентифікацію користувача.\n  Даний компонент доступний тільки у [[yii\\web\\Application|веб-додатках]].\n  Більш детальна інформація наведена у розділі [Аутентифікація](security-authentication.md).\n* [[yii\\web\\View|view]]: використовується для формування представлень.\n  Більш детальна інформація наведена у розділі [Представлення](structure-views.md).\n"
  },
  {
    "path": "docs/guide-uk/structure-applications.md",
    "content": "Додатки\n=======\n\nДодатки це об’єкти, які керують всією структурою і життєвим циклом прикладної системи Yii.\nКожна прикладна система Yii містить у собі один об’єкт додатка, який створюється у \n[вхідному скрипті](structure-entry-scripts.md) і глобально доступний через вираз `\\Yii::$app`.\n\n> Info: Термін \"додаток\", в залежності від контексту, в якому він використовується, може означати як об’єкт додатка,\n  так і прикладну систему додатка в цілому.\n\nІснує два типи додатків: [[yii\\web\\Application|веб-додатки]] та [[yii\\console\\Application|консольні додатки]].\nЯк можна здогадатися із назв, перший тип, в основному, займається обробкою веб-запитів, а другий обробляє\nзапити консольних команд.\n\n\n## Конфігурації додатка <span id=\"application-configurations\"></span>\n\nКоли [вхідний скрипт](structure-entry-scripts.md) створює додаток, він завантажить \n[конфігурацію](concept-configurations.md) та застосує її до додатка, наприклад:\n\n```php\nrequire __DIR__ . '/../vendor/autoload.php';\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n\n// завантаження конфігурації додатка\n$config = require __DIR__ . '/../config/web.php';\n\n// створення екземпляру додатка і його налаштування\n(new yii\\web\\Application($config))->run();\n```\n\nЯк і звичайні [конфігурації](concept-configurations.md), конфігурації додатка вказують як саме\nслід ініціалізувати властивості об’єктів додатка. Через те, що конфігурації додатків часто\nє складними, вони зберігаються у декількох [конфігураційних файлах](concept-configurations.md#configuration-files),\nнаприклад, файл `web.php` у наведеному вище прикладі.\n\n\n## Властивості додатка <span id=\"application-properties\"></span>\n\nІснує багато важливих властивостей додатка, які ви будете налаштовувати в конфігураціях додатка. \nЦі властивості, зазвичай, описують середовище, у якому працює додаток. Наприклад, \nдодаток мусить знати яким чином завантажувати [контролери](structure-controllers.md), \nде зберігати тимчасові файли, і т. п. Нижче ми розглянемо дані властивості.\n\n\n### Обов'язкові властивості <span id=\"required-properties\"></span>\n\nВ кожному додатку, ви маєте налаштувати мінімум дві властивості: [[yii\\base\\Application::id|id]]\nта [[yii\\base\\Application::basePath|basePath]].\n\n\n#### [[yii\\base\\Application::id|id]] <span id=\"id\"></span>\n\nВластивість [[yii\\base\\Application::id|id]] є унікальним ідентифікатором додатка, який відрізняє його від решти інших додатків.\nЗдебільшого, це використовується всередині системи. Хоч і не є обов’язковим, але для кращої сумісності рекомендується \nвикористовувати буквено-цифрові символи при налаштуванні ідентифікатора додатка.\n\n\n#### [[yii\\base\\Application::basePath|basePath]] <span id=\"basePath\"></span>\n\nВластивість [[yii\\base\\Application::basePath|basePath]] вказує на кореневу директорію додатка. Це директорія, \nяка містить весь код прикладної системи додатка. В цій директорії, зазвичай, знаходяться під-директорії `models`,\n`views`, та `controllers`, які містять код, що відповідає шаблону проектування MVC.\n\nВи можете налаштувати властивість [[yii\\base\\Application::basePath|basePath]], вказавши прямий шлях до директорії \nабо через [псевдонім шляху](concept-aliases.md). В обох випадках, вказана директорія має існувати, інакше буде отримано \nвиключення. Шлях буде нормалізовано за допомогою виклику функції `realpath()`.\n\nВластивість [[yii\\base\\Application::basePath|basePath]] часто використовується для отримання інших важливих шляхів \n(наприклад, шлях до директорії runtime). Саме з цієї причини, псевдонім шляху `@app` визначений представляти цей\nшлях. Похідні шляхи потім можуть бути сформовані за допомогою цього псевдоніму шляху (наприклад, `@app/runtime` для\nзвертання до директорії runtime).\n\n\n### Важливі властивості <span id=\"important-properties\"></span>\n\nВластивості, перелічені в даному підрозділі, частіш за все повинні бути визначені, тому що вони можуть\nвідрізнятися у різних додатках.\n\n\n#### [[yii\\base\\Application::aliases|aliases]] <span id=\"aliases\"></span>\n\nДана властивість дозволяє налаштувати вам набір [псевдонімів](concept-aliases.md) у рамках масиву.\nКлючами масиву є імена псевдонімів, а значеннями - відповідні визначення шляхів.\nНаприклад,\n\n```php\n[\n    'aliases' => [\n        '@name1' => 'path/to/path1',\n        '@name2' => 'path/to/path2',\n    ],\n]\n```\n\nЦя властивість призначена для того, щоб ви мали змогу вказувати псевдоніми в рамках конфігурації додатка, \nа не викликаючи метод [[Yii::setAlias()]].\n\n\n#### [[yii\\base\\Application::bootstrap|bootstrap]] <span id=\"bootstrap\"></span>\n\nДана властивість є дуже зручною, вона дозволяє вказувати масив компонентів, які повинні бути виконані\nу процесі [[yii\\base\\Application::bootstrap()|початкового завантаження]] додатка. Наприклад, якщо вам потрібен\n[модуль](structure-modules.md), який виконує тонке налаштування [URL-правил](runtime-routing.md), \nви можете вказати його ідентифікатор в якості елемента даної властивості.\n\nКожен з елементів даної властивості може вказуватись в одному із наступних форматів:\n\n- ідентифікатор компонента додатка, що вказаний у [компонентах](#components);\n- ідентифікатор модуля, що вказаний у [модулях](#modules);\n- назва класу;\n- масив конфігурації;\n- анонімна функція, яка створює та повертає об’єкт компонента.\n\nНаприклад,\n\n```php\n[\n    'bootstrap' => [\n        // ідентифікатор компонента додатка або модуля\n        'demo',\n\n        // назва класу\n        'app\\components\\Profiler',\n\n        // масив конфігурації\n        [\n            'class' => 'app\\components\\Profiler',\n            'level' => 3,\n        ],\n\n        // анонімна функція\n        function () {\n            return new app\\components\\Profiler();\n        }\n    ],\n]\n```\n\n> Note: Якщо ідентифікатор модуля збігається з ідентифікатором компонента додатка, перевага буде віддана компоненту додатка\nпід час початкового завантаження. Якщо ж ви хочете використати модуль, вам потрібно визначити анонімну функцію,\nяк показано нижче:\n```php\n[\n    function () {\n        return Yii::$app->getModule('user');\n    },\n]\n```\n\n\nПід час початкового завантаження, буде створено екземпляр кожного компоненту. Якщо клас компонента реалізує\nінтерфейс [[yii\\base\\BootstrapInterface]], то його метод [[yii\\base\\BootstrapInterface::bootstrap()|bootstrap()]]\nбуде також викликаний.\n\nЩе одним практичним прикладом є конфігурація додатку в [базовому шаблоні проекту](start-installation.md),\nу якій модулі `debug` та `gii` визначені як компоненти початкового завантаження, коли додаток знаходиться\nв середовищі розробки:\n\n```php\nif (YII_ENV_DEV) {\n    // налаштування конфігурації для середовища розробки ('dev')\n    $config['bootstrap'][] = 'debug';\n    $config['modules']['debug'] = 'yii\\debug\\Module';\n\n    $config['bootstrap'][] = 'gii';\n    $config['modules']['gii'] = 'yii\\gii\\Module';\n}\n```\n\n> Note: Якщо вказувати велику кількість компонентів у `bootstrap` - це негативно позначиться на швидкодії\n  додатка, оскільки для кожного запиту буде виконуватись один й той самий набір компонентів. Таким чином, потрібно\n  розсудливо використовувати компоненти початкового завантаження.\n\n\n#### [[yii\\web\\Application::catchAll|catchAll]] <span id=\"catchAll\"></span>\n\nДана властивість підтримується тільки [[yii\\web\\Application|веб-додатками]]. Вона вказує на \n[дію контролера](structure-controllers.md), яка мусить обробляти всі вхідні запити від користувача. Переважно, \nце використовується, коли додаток знаходиться в режимі обслуговування і повинен обробити всі запити через одну дію.\n\nКонфігурація є масивом, перший елемент якого вказує на маршрут дії. Решта елементів масиву у форматі ключ-значення\nвказують на додаткові параметри, які мають бути передані дії. Наприклад,\n\n```php\n[\n    'catchAll' => [\n        'offline/notice',\n        'param1' => 'value1',\n        'param2' => 'value2',\n    ],\n]\n```\n\n\n#### [[yii\\base\\Application::components|components]] <span id=\"components\"></span>\n\nДана властивість є найважливішою. Вона дозволяє вам зареєструвати список іменованих компонентів, так званих \n[компонентів додатку](structure-application-components.md), які ви можете використовувати в інших місцях. Наприклад,\n\n```php\n[\n    'components' => [\n        'cache' => [\n            'class' => 'yii\\caching\\FileCache',\n        ],\n        'user' => [\n            'identityClass' => 'app\\models\\User',\n            'enableAutoLogin' => true,\n        ],\n    ],\n]\n```\n\nКожен компонент додатка визначений у форматі ключ-значення в масиві. Ключ являє собою ідентифікатор компонента, \nу той час як значення являє собою назву класу або [конфігурацію](concept-configurations.md).\n\nВи можете зареєструвати будь-який компонент у додатку, який потім буде доступний глобально \nза допомогою виразу `\\Yii::$app->componentID`.\n\nБільш детальна інформація наведена в розділі [Компоненти додатка](structure-application-components.md).\n\n\n#### [[yii\\base\\Application::controllerMap|controllerMap]] <span id=\"controllerMap\"></span>\n\nДана властивість дозволяє вам встановлювати відповідність між ідентифікатором контролера та його класом. За замовчуванням, \nYii встановлює відповідність між ідентифікатором контролера та його класом згідно [домовленості](#controllerNamespace) \n(наприклад, ідентифікатор `post` буде відповідати класу `app\\controllers\\PostController`). За допомогою налаштування\nданої властивості ви можете обійти домовленість для необхідних контролерів. У наведеному прикладі, `account` буде \nвідповідати `app\\controllers\\UserController`, в той час, як `article` буде відповідати `app\\controllers\\PostController`.\n\n```php\n[\n    'controllerMap' => [\n        'account' => 'app\\controllers\\UserController',\n        'article' => [\n            'class' => 'app\\controllers\\PostController',\n            'enableCsrfValidation' => false,\n        ],\n    ],\n]\n```\n\nКлючами масиву даної властивості є ідентифікатори контролерів, а значеннями є назви відповідних \nкласів контролерів або [конфігурації](concept-configurations.md).\n\n\n#### [[yii\\base\\Application::controllerNamespace|controllerNamespace]] <span id=\"controllerNamespace\"></span>\n\nДана властивість вказує на простір імен за замовчуванням, в якому повинні знаходитись класи контролерів. За замовчуванням, \nце значення рівне `app\\controllers`. Якщо ідентифікатор контролера є `post`, то, за домовленістю, відповідна назва класу \nконтролера (без простору імен) буде `PostController`, а повна назва класу буде `app\\controllers\\PostController`.\n\nКласи контролерів можуть також знаходитись у під-директоріях директорії, відповідно до їх простору імен.\nНаприклад, ідентифікатору контролера `admin/post` відповідає повне ім’я класу контролера\n`app\\controllers\\admin\\PostController`.\n\nДуже важливо, щоб повне ім’я класу контролера могло бути використане [автозавантаженням](concept-autoloading.md)\nі фактичний простір імен вашого контролера відповідав цій властивості. В іншому випадку, ви отримаєте помилку\nіз заголовком \"Сторінка не знайдена\" (\"Page Not Found\"), коли спробуєте отримати доступ до додатка.\n\nУ випадку, якщо ви хочете обійти домовленість, яку розглянуто вище, ви можете використовувати властивість \n[controllerMap](#controllerMap).\n\n\n#### [[yii\\base\\Application::language|language]] <span id=\"language\"></span>\n\nДана властивість вказує мову, якою додаток повинен відображати вміст кінцевому користувачу.\nЗа замовчуванням значення даної властивості рівне `en`, що означає англійську мову. Необхідно налаштувати дану властивість,\nякщо ваш додаток потребує підтримки декількох мов.\n\nЗначення даної властивості визначає кілька різних аспектів [інтернаціоналізації](tutorial-i18n.md), в тому числі \nпереклади повідомлень, форматування дат, форматування чисел, і т. п. Наприклад, віджет [[yii\\jui\\DatePicker]] \nвикористовує значення даної властивості для визначення мови, на якій має бути зображений календар і який\nформат дати повинен бути.\n\nРекомендується вказувати мову у рамках стандарту [IETF](https://en.wikipedia.org/wiki/IETF_language_tag).\nНаприклад, для англійської мови використовується `en`, в той час як для англійської в США - `en-US`.\n\nБільш детальна інформація про цю властивість може бути знайдена у розділі [Інтернаціоналізація](tutorial-i18n.md).\n\n\n#### [[yii\\base\\Application::modules|modules]] <span id=\"modules\"></span>\n\nДана властивість визначає [модулі](structure-modules.md), які містить додаток.\n\nЗначенням властивості є масив імен класів модулів або [конфігурацій](concept-configurations.md), а ключами цього масиву\nвиступають ідентифікатори модулів. Наприклад,\n\n```php\n[\n    'modules' => [\n        // модуль \"booking\" визначено класом модуля\n        'booking' => 'app\\modules\\booking\\BookingModule',\n\n        // модуль \"comment\" визначено масивом конфігурації\n        'comment' => [\n            'class' => 'app\\modules\\comment\\CommentModule',\n            'db' => 'db',\n        ],\n    ],\n]\n```\n\nБільш детальна інформація наведена у розділі [Модулі](structure-modules.md).\n\n\n#### [[yii\\base\\Application::name|name]] <span id=\"name\"></span>\n\nДана властивість вказує ім’я додатка, яке може бути відображене кінцевому користувачу. На відміну від властивості\n[[yii\\base\\Application::id|id]], яка має бути унікальною, значення даної властивості потрібне в основному для\nвідображення і не повинно обов’язково бути унікальним.\n\nЯкщо ваш код не використовує дану властивість, то ви можете не налаштовувати її.\n\n\n#### [[yii\\base\\Application::params|params]] <span id=\"params\"></span>\n\nДана властивість визначає масив глобально доступних параметрів додатка. Замість того, щоб використовувати жорстко \nфіксовані числа і текстові рядки у вашому коді, краще оголосити їх параметрами додатка в одному місці і використовувати \nв необхідних місцях коду. Наприклад, ви можете визначити розмір мініатюр зображень як параметр наступним чином:\n\n```php\n[\n    'params' => [\n        'thumbnail.size' => [128, 128],\n    ],\n]\n```\n\nПотім, коли вам потрібно використати задані значення у вашому коді, ви можете зробити це наступним чином:\n\n```php\n$size = \\Yii::$app->params['thumbnail.size'];\n$width = \\Yii::$app->params['thumbnail.size'][0];\n```\n\nЯкщо пізніше вам знадобиться змінити розмір мініатюр зображень, то вам потрібно буде змінити це значення лише у\nконфігураційному файлі додатка, не змінюючи будь-який залежний код.\n\n\n#### [[yii\\base\\Application::sourceLanguage|sourceLanguage]] <span id=\"sourceLanguage\"></span>\n\nДана властивість вказує мову, на якій написаний код додатка. За замовчуванням значення рівне `'en-US'`, \nщо означає англійську мову (США). Ви повинні змінити дану властивість, якщо мовою текстового вмісту у вашому коді\nє не англійська мова.\n\nАналогічно властивості [language](#language), ви повинні вказати дану властивість у рамках стандарту \n[IETF](https://en.wikipedia.org/wiki/IETF_language_tag). Наприклад, для англійської мови \nвикористовується `en`, в той час як для англійської в США - `en-US`.\n\nБільш детальна інформація про цю властивість може бути знайдена у розділі [Інтернаціоналізація](tutorial-i18n.md).\n\n\n#### [[yii\\base\\Application::timeZone|timeZone]] <span id=\"timeZone\"></span>\n\nДана властивість надає альтернативний спосіб встановлення часової зони за замовчуванням у процесі роботи додатка.\nТаким чином, вказуючи дану властивість, ви, по суті, викликаєте PHP-функцію \n[date_default_timezone_set()](https://www.php.net/manual/en/function.date-default-timezone-set.php). Наприклад,\n\n```php\n[\n    // 'Europe/Kiev' для України\n    'timeZone' => 'America/Los_Angeles',\n]\n```\n\n#### [[yii\\base\\Application::version|version]] <span id=\"version\"></span>\n\nДана властивість вказує версію додатка. За замовчуванням значення рівне `'1.0'`. Ви можете не змінювати\nдану властивість, якщо ваш код не використовує її.\n\n\n### Корисні властивості <span id=\"useful-properties\"></span>\n\nВластивості, які перераховані в даному підрозділі, не є часто змінюваними, оскільки їх значення за замовчуванням\nвідповідають загальноприйнятим домовленостям. Однак, ви можете їх налаштувати, якщо вам потрібно використовувати \nінші значення.\n\n\n#### [[yii\\base\\Application::charset|charset]] <span id=\"charset\"></span>\n\nВластивість вказує кодування, яке використовує додаток. За замовчуванням значення рівне `'UTF-8'`,\nяке має бути незмінним для більшості додатків, тільки якщо ви не працюєте із застарілою системою, яка використовує \nзначний об’єм даних закодованих не в Unicode.\n\n\n#### [[yii\\base\\Application::defaultRoute|defaultRoute]] <span id=\"defaultRoute\"></span>\n\nВластивість вказує [маршрут](runtime-routing.md), який повинен використовувати додаток, коли жодного не вказано у \nвхідному запиті. Маршрут може складатись із ідентифікатора модуля, ідентифікатора контролера і/або ідентифікатора дії.\nНаприклад, `help`, `post/create` або `admin/post/create`. Якщо ідентифікатор дії не вказано, то буде використано значення\nза замовчуванням, що вказане у [[yii\\base\\Controller::defaultAction]].\n\nДля [[yii\\web\\Application|веб-додатків]] значення за замовчуванням даної властивості рівне `'site'`, яке означає\nконтролер `SiteController` і буде використовуватись його дія за замовчуванням. Таким чином, якщо ви спробуєте отримати доступ \nдо додатка, не вказавши маршрут - буде відображено результат дії `app\\controllers\\SiteController::actionIndex()`.\n\nДля [[yii\\console\\Application|консольних додатків]] значення за замовчуванням рівне `'help'`, яке означає,\nщо повинна використовуватись вбудована команда [[yii\\console\\controllers\\HelpController::actionIndex()]].\nТаким чином, якщо ви виконаєте команду `yii` без аргументів, вам буде відображена довідкова інформація.\n\n\n#### [[yii\\base\\Application::extensions|extensions]] <span id=\"extensions\"></span>\n\nДана властивість визначає перелік [розширень](structure-extensions.md), які встановлені і використовуються додатком. \nЗа замовчуванням значенням даної властивості буде масив, отриманий із файлу `@vendor/yiisoft/extensions.php`. \nФайл `extensions.php` генерується і підтримується автоматично, коли ви використовуєте \n[Composer](https://getcomposer.org) для встановлення розширень.\nТаким чином, у більшості випадків, вам не потрібно налаштовувати дану властивість.\n\nВ особливих випадках, коли ви хочете керувати розширеннями власноруч, ви можете вказати дану властивість наступним чином:\n\n```php\n[\n    'extensions' => [\n        [\n            'name' => 'extension name',\n            'version' => 'version number',\n            'bootstrap' => 'BootstrapClassName',  // не обов'язково; може бути також масив конфігурації\n            'alias' => [  // не обов'язково\n                '@alias1' => 'to/path1',\n                '@alias2' => 'to/path2',\n            ],\n        ],\n\n        // ... аналогічно для решти розширень ...\n\n    ],\n]\n```\n\nЯк бачите, властивість є масивом специфікацій розширень. Кожне розширення вказано масивом, який складається з елементів\n`name` і `version`. Якщо розширення має бути виконано в процесі [початкового завантаження](runtime-bootstrapping.md), \nто слід вказати елемент `bootstrap`, який може містити ім’я класу початкового завантаження або масив [конфігурації](concept-configurations.md).\nРозширення також може визначати декілька [псевдонімів](concept-aliases.md).\n\n\n#### [[yii\\base\\Application::layout|layout]] <span id=\"layout\"></span>\n\nДана властивість визначає ім’я макету за замовчуванням, який мусить бути використаний при формуванні [представлення](structure-views.md).\nЗначення за замовчуванням рівне `'main'`, яке означає, що має бути використаний файл макету `main.php` з [директорії макетів](#layoutPath).\nЯкщо обидві властивості [директорія макетів](#layoutPath) та [директорія представлень](#viewPath) мають значення за замовчуванням,\nто файл макету за замовчуванням може бути представлений псевдонімом шляху як `@app/views/layouts/main.php`.\n\nДля заборони використання макету, ви можете вказати дану властивість як `false`, однак це є дуже рідкісним випадком.\n\n\n#### [[yii\\base\\Application::layoutPath|layoutPath]] <span id=\"layoutPath\"></span>\n\nДана властивість визначає шлях, де слід шукати макети. Значення за замовчуванням рівне `layouts`, \nщо означає під-директорію у [директорії представлень](#viewPath). Якщо значення [директорії представлень](#viewPath) \nє значенням за замовчуванням, то директорія шаблонів за замовчуванням може бути представлена псевдонімом шляху як `@app/views/layouts`.\n\nВи можете налаштувати дану властивість як директорію або як [псевдонім шляху](concept-aliases.md).\n\n\n#### [[yii\\base\\Application::runtimePath|runtimePath]] <span id=\"runtimePath\"></span>\n\nДана властивість визначає шлях, де зберігаються тимчасові файли, такі як: файли журналів, кеш-файли. \nЗа замовчуванням це значення рівне директорії, яка представлена псевдонімом шляху `@app/runtime`.\n\nВи можете налаштувати дану властивість як директорію або як [псевдонім шляху](concept-aliases.md). Зверніть увагу, \nщо дана директорія має бути доступна для запису процесом, який виконує додаток. Також директорія має \nбути захищена від доступу для кінцевих користувачів, оскільки файли, які зберігаються в ній, можуть містити важливу інформацію.\n\nДля спрощення роботи з даною директорією, Yii надає попередньо визначений псевдонім шляху `@runtime`.\n\n\n#### [[yii\\base\\Application::viewPath|viewPath]] <span id=\"viewPath\"></span>\n\nДана властивість визначає базову директорію, де містяться всі файли представлень. Значення за замовчуванням являє \nсобою псевдонім `@app/views`. Ви можете налаштувати дану властивість як директорію або як [псевдонім шляху](concept-aliases.md).\n\n\n#### [[yii\\base\\Application::vendorPath|vendorPath]] <span id=\"vendorPath\"></span>\n\nДана властивість визначає директорію сторонніх бібліотек, які використовуються і керуються за допомогою\n[Composer](https://getcomposer.org). Вона містить всі сторонні бібліотеки, які використовуються додатком, \nвключаючи сам фреймворк Yii. Значення за замовчуванням являє собою псевдонім `@app/vendor`.\n\nВи можете налаштувати дану властивість як директорію або як [псевдонім шляху](concept-aliases.md). \nПри зміні даної властивості, переконайтесь, що ви також змінили відповідним чином налаштування Composer.\n\nДля спрощення роботи з даною директорією, Yii надає попередньо визначений псевдонім шляху `@vendor`.\n\n\n#### [[yii\\console\\Application::enableCoreCommands|enableCoreCommands]] <span id=\"enableCoreCommands\"></span>\n\nДана властивість підтримується тільки [[yii\\console\\Application|консольними додатками]]. Вона вказує чи потрібно\nвикористовувати вбудовані в Yii консольні команди. Значення за замовчуванням рівне `true`.\n\n\n## Події додатка <span id=\"application-events\"></span>\n\nДодаток викликає декілька подій під час життєвого циклу обробки запиту. \nВи можете приєднати обробники подій в конфігурації додатка наступним чином:\n\n```php\n[\n    'on beforeRequest' => function ($event) {\n        // ...\n    },\n]\n```\n\nВикористання синтаксису `on eventName` детально описано у розділі\n[Конфігурації](concept-configurations.md#configuration-format).\n\nІншим методом є приєднання обробників подій у процесі [початкового завантаження додатку](runtime-bootstrapping.md),\nодразу після того, як буде створено екземпляр додатку. Наприклад:\n\n```php\n\\Yii::$app->on(\\yii\\base\\Application::EVENT_BEFORE_REQUEST, function ($event) {\n    // ...\n});\n```\n\n### [[yii\\base\\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_REQUEST]] <span id=\"beforeRequest\"></span>\n\nДана подія виникає *до* того, як додаток починає обробляти вхідний запит. Справжнє ім’я події - `beforeRequest`.\n\nКоли виникає ця подія, екземпляр додатка вже сконфігурований і проініціалізований. Таким чином, це є коректним місцем\nдля додавання вашого коду за допомогою подій для перехоплення управління обробки запиту. Наприклад,\nв обробнику події ви можете динамічно визначати властивість [[yii\\base\\Application::language]] в залежності від деяких параметрів.\n\n\n### [[yii\\base\\Application::EVENT_AFTER_REQUEST|EVENT_AFTER_REQUEST]] <span id=\"afterRequest\"></span>\n\nДана подія виникає *після* закінчення обробки запиту додатком, але *до* відправлення відповіді.\nСправжнє ім’я події - `afterRequest`.\n\nНа момент виникнення даної події обробка запиту завершена і ви можете використати це для після-обробки запиту, \nз метою налаштування відповіді.\n\nЗверніть увагу, що компонент [[yii\\web\\Response|response]] також викликає події в процесі відправлення даних кінцевому\nкористувачу. Ці події виникають *після* поточної події.\n\n\n### [[yii\\base\\Application::EVENT_BEFORE_ACTION|EVENT_BEFORE_ACTION]] <span id=\"beforeAction\"></span>\n\nПодія виникає *до* виконання кожної [дії контролера](structure-controllers.md).\nСправжнє ім’я події - `beforeAction`.\n\nПараметр події є екземпляром [[yii\\base\\ActionEvent]]. Обробник події може встановлювати значення властивості\n[[yii\\base\\ActionEvent::isValid]] рівним `false` для зупинки виконання дії.\nНаприклад:\n\n```php\n[\n    'on beforeAction' => function ($event) {\n        if (деяка умова) {\n            $event->isValid = false;\n        } else {\n        }\n    },\n]\n```\n\nЗверніть увагу на те, що така ж подія `beforeAction` викликається також [модулями](structure-modules.md) та \n[контролерами](structure-controllers.md). Об’єкти додатку є першими, хто ініціює дані події,\nза ними модулі (якщо такі є), і в кінці - контролери. Якщо обробник події встановлює значення властивості\n[[yii\\base\\ActionEvent::isValid]] рівним `false`, то всі наступні події НЕ будуть викликані.\n\n\n### [[yii\\base\\Application::EVENT_AFTER_ACTION|EVENT_AFTER_ACTION]] <span id=\"afterAction\"></span>\n\nЦя подія виникає *після* виконання кожної [дії контролера](structure-controllers.md).\nСправжнє ім’я події - `afterAction`.\n\nПараметр події є екземпляром [[yii\\base\\ActionEvent]]. Через властивість [[yii\\base\\ActionEvent::result]]\nобробник події може отримати доступ або змінити результат дії контролера.\nНаприклад:\n\n```php\n[\n    'on afterAction' => function ($event) {\n        if (деяка умова) {\n            // змінення $event->result\n        } else {\n        }\n    },\n]\n```\n\nЗверніть увагу на те, що така ж подія `afterAction` викликається також [модулями](structure-modules.md) та \n[контролерами](structure-controllers.md). Ці об’єкти ініціюють події у зворотньому порядку, порівнюючи з `beforeAction`.\nТаким чином, контролери є першими, хто ініціює дану подію, далі йдуть модулі (якщо такі є), і врешті - об’єкти додатків.\n\n\n## Життєвий цикл додатка <span id=\"application-lifecycle\"></span>\n\n![Життєвий цикл додатка](images/application-lifecycle.png)\n\nКоли [вхідний скрипт](structure-entry-scripts.md) виконується для обробки запиту, додаток пройде наступний\nжиттєвий цикл:\n\n1. Вхідний скрипт завантажує конфігурацію додатка у якості масиву.\n2. Вхідний скрипт створює новий екземпляр додатка:\n  * Викликається метод [[yii\\base\\Application::preInit()|preInit()]], який налаштовує деякі життєво важливі властивості \n    додатка, як наприклад [[yii\\base\\Application::basePath|basePath]].\n  * Реєструється [[yii\\base\\Application::errorHandler|обробник помилок]].\n  * Налаштовуються властивості додатку.\n  * Викликається метод [[yii\\base\\Application::init()|init()]], який далі викликає метод \n    [[yii\\base\\Application::bootstrap()|bootstrap()]] для початкового завантаження компонентів.\n3. Вхідний скрипт викликає метод [[yii\\base\\Application::run()]] для запуску додатка:\n  * Ініціюється подія [[yii\\base\\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_REQUEST]].\n  * Обробляється запит: розбір інформації запиту на [маршрут](runtime-routing.md) та відповідні параметри;\n    створення об’єктів модуля, контролера та дії, згідно вказаного маршруту; виконання дії контролера.\n  * Ініціюється подія [[yii\\base\\Application::EVENT_AFTER_REQUEST|EVENT_AFTER_REQUEST]].\n  * Надсилається відповідь кінцевому користувачу.\n4. Вхідний скрипт отримує значення статусу виходу із додатка та завершує обробку запиту.\n"
  },
  {
    "path": "docs/guide-uk/structure-controllers.md",
    "content": "Контролери\n==========\n\nКонтролери є частиною архітектури [MVC](https://uk.wikipedia.org/wiki/Модель-вид-контролер). Це об’єкти класів,\nуспадкованих від [[yii\\base\\Controller]] та відповідають за обробку запитів і генерування відповідей.\nЗокрема, після отримання контролю від [додатків](structure-applications.md), контролери проаналізують \nвхідні дані, передадуть їх у [моделі](structure-models.md), додадуть результати моделі у\n[представлення](structure-views.md), і на сам кінець згенерують вихідні відповіді.\n\n\n## Дії <span id=\"actions\"></span>\n\nКонтролери складаються з *дій*, які є основними блоками, до яких може звертатись кінцевий користувач і запитувати\nвиконання того або іншого функціоналу. В контролері може бути одна або декілька дій.\n\nНаступний приклад показує контролер `post` з двома діями: `view` та `create`:\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse app\\models\\Post;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\n\nclass PostController extends Controller\n{\n    public function actionView($id)\n    {\n        $model = Post::findOne($id);\n        if ($model === null) {\n            throw new NotFoundHttpException;\n        }\n\n        return $this->render('view', [\n            'model' => $model,\n        ]);\n    }\n\n    public function actionCreate()\n    {\n        $model = new Post;\n\n        if ($model->load(Yii::$app->request->post()) && $model->save()) {\n            return $this->redirect(['view', 'id' => $model->id]);\n        } else {\n            return $this->render('create', [\n                'model' => $model,\n            ]);\n        }\n    }\n}\n```\n\nУ дії `view` (визначеній методом `actionView()`) код спочатку завантажує [модель](structure-models.md),\nвідповідно до запитуваного ідентифікатора моделі; Якщо модель успішно завантажена, то код відобразить її за допомогою\n[представлення](structure-views.md), під назвою `view`. В іншому випадку - буде отримане виключення.\n\nУ дії `create` (визначеній методом `actionCreate()`) подібний код. Він спочатку намагається завантажити \n[модель](structure-models.md) за допомогою даних із запиту і зберегти модель. Якщо все пройшло успішно, \nто код перенаправить браузер на дію `view` із ідентифікатором щойно створеної моделі. В іншому випадку - він відобразить \nпредставлення `create`, через яке користувач зможе вказати необхідні дані.\n\n\n## Маршрути <span id=\"routes\"></span>\n\nКінцеві користувачі звертаються до дій з допомогою так названих  *маршрутів*. Маршрут це текстовий рядок, який складається з наступних частин:\n\n* ідентифікатор модуля: він існує, тільки якщо контролер належить не додатку, а [модулю](structure-modules.md);\n* [ідентифікатор контролера](#controller-ids): текстовий рядок, який унікально ідентифікує контролер серед всіх інших контролерів одного і того ж додатка\n  (або одного й того ж модуля, якщо контролер належить модулю);\n* [ідентифікатор дії](#action-ids): текстовий рядок, який унікально ідентифікує дію серед всіх інших дій одного й того ж контролера.\n\nМаршрути можуть мати наступний формат:\n\n```\nControllerID/ActionID\n```\n\nабо наступний формат, якщо контролер належить модулю:\n\n```\nModuleID/ControllerID/ActionID\n```\n\nТаким чином, якщо користувач звертається до URL `https://hostname/index.php?r=site/index`, то буде викликано дію `index`\nу контролері `site`. Розділ [Маршрутизація та створення URL](runtime-routing.md) містить більш детальну інформацію\nпро те, як маршрути співвідносяться із діями.\n\n\n## Створення контролерів <span id=\"creating-controllers\"></span>\n\nУ [[yii\\web\\Application|веб-додатках]] контролери повинні бути успадкованими від класу [[yii\\web\\Controller]]\nабо його нащадків. Аналогічно для [[yii\\console\\Application|консольних додатків]], контролери повинні бути\nуспадкованими від класу [[yii\\console\\Controller]] або його нащадків. Наступний код визначає контролер `site`:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n}\n```\n\n\n### Ідентифікатори контролерів <span id=\"controller-ids\"></span>\n\nЗазвичай контролер зроблений таким чином, що він повинен обробляти запити, які пов’язані з певним ресурсом.\nСаме з цієї причини, ідентифікатори контролерів зазвичай є іменниками, які посилаються на ресурс, який вони обробляють.\nНаприклад, ви можете використовувати `article` в якості ідентифікатора контролера, який відповідає за обробку даних статей.\n\nЗа замовчуванням, ідентифікатори контролерів мають містити тільки наступні символи: англійські букви в нижньому регістрі, цифри,\nпідкреслення, тире і слеш. Наприклад, обидва ідентифікатори контролера `article` та `post-comment` є прийнятними, в той час,\nяк `article?`, `PostComment`, `admin\\post` не є такими.\n\nІдентифікатор контролера також може містити префікс під-директорії. Наприклад, у `admin/article` частина `article` відповідає\nконтролеру в під-директорії `admin` [[yii\\base\\Application::controllerNamespace|простору імен контролера]].\nДопустимими символами для префіксів під-директорій є: англійські букви в нижньому і верхньому регістрах, цифри, символи підкреслення і \nслеш, де слеш використовується в якості роздільника для багаторівневих під-директорій (наприклад, `panels/admin`).\n\n\n### Іменування класів контролерів <span id=\"controller-class-naming\"></span>\n\nНазви класів контролерів можуть бути отримані із ідентифікаторів контролерів наступним чином:\n\n1. Привести у верхній регістр перший символ в кожному слові, розділеному дефісами. Зверніть увагу, що, якщо ідентифікатор\n  контролера містить слеш, то дане правило поширюється тільки на частину після останнього слеша в ідентифікаторі контролера.\n2. Прибрати дефіси і замінити будь-який прямий слеш на зворотний.\n3. Додати суфікс `Controller`.\n4. Додати на початок [[yii\\base\\Application::controllerNamespace|простір імен контролера]].\n\nНижче наведено декілька прикладів, з урахуванням того, що \n[[yii\\base\\Application::controllerNamespace|простір імен контролера]] має значення за замовчуванням `app\\controllers`:\n\n* `article` відповідає `app\\controllers\\ArticleController`;\n* `post-comment` відповідає `app\\controllers\\PostCommentController`;\n* `admin/post-comment` відповідає `app\\controllers\\admin\\PostCommentController`;\n* `adminPanels/post-comment` відповідає `app\\controllers\\adminPanels\\PostCommentController`.\n\nКласи контролерів мають бути [автоматично завантаженими](concept-autoloading.md). Саме з цієї причини у вищенаведених прикладах\nконтролер `article` має бути збереженим у файл, [псевдонім шляху](concept-aliases.md) якого є\n`@app/controllers/ArticleController.php`; в той час, як контролер `admin/post-comment` має знаходитись у файлі \n`@app/controllers/admin/PostCommentController.php`.\n\n> Info: Останній приклад `admin/post-comment` показує яким чином ви можете розташувати контролер в під-директорії\n  [[yii\\base\\Application::controllerNamespace|простору імен контролера]]. Це дуже зручно, коли ви хочете організувати \n  свої контролери у декілька категорій і не хочете використовувати [модулі](structure-modules.md).\n\n\n### Мапа контролерів <span id=\"controller-map\"></span>\n\nВи можете налаштувати [[yii\\base\\Application::controllerMap|мапу контролерів]] для того, щоб подолати\nописані вище обмеження іменування ідентифікаторів контролерів і назв класів. В основному, це дуже зручно, коли ви використовуєте\nсторонні контролери, іменування яких ви не можете контролювати.\n\nВи можете налаштувати [[yii\\base\\Application::controllerMap|мапу контролерів]] в\n[налаштуваннях додатка](structure-applications.md#application-configurations) наступним чином:\n\n```php\n[\n    'controllerMap' => [\n        // оголошує контролер \"account\", використовуючи назву класу\n        'account' => 'app\\controllers\\UserController',\n\n        // оголошує контролер \"article\", використовуючи масив конфігурації\n        'article' => [\n            'class' => 'app\\controllers\\PostController',\n            'enableCsrfValidation' => false,\n        ],\n    ],\n]\n```\n\n### Контролер за замовчуванням <span id=\"default-controller\"></span>\n\nКожний додаток має контролер за замовчуванням, вказаний через властивість [[yii\\base\\Application::defaultRoute]].\nКоли в запиті не вказано [маршрут](#routes), то буде використано маршрут із зазначеної властивості.\nДля [[yii\\web\\Application|веб-додатків]] це значення рівне `'site'`, у той час, як для \n[[yii\\console\\Application|консольних додатків]], це - `'help'`. Таким чином, якщо вказаний URL \n`https://hostname/index.php`, це значить, що контролер `site` виконає обробку запиту.\n\nВи можете змінити контролер за замовчуванням наступним чином в [налаштуваннях додатку](structure-applications.md#application-configurations):\n\n```php\n[\n    'defaultRoute' => 'main',\n]\n```\n\n\n## Створення дій <span id=\"creating-actions\"></span>\n\nСтворення дій може бути настільки ж простим, як і оголошення так званих *методів дій* у класі контролера. Метод дії це\n*публічний* метод, ім’я якого починається зі слова `action`. Значення, яке повертається методом дії, представляє дані\nвідповіді, які будуть відправлені кінцевому користувачу. Наведений нижче код визначає дві дії `index` і `hello-world`:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actionIndex()\n    {\n        return $this->render('index');\n    }\n\n    public function actionHelloWorld()\n    {\n        return 'Hello World';\n    }\n}\n```\n\n\n### Ідентифікатори дій <span id=\"action-ids\"></span>\n\nЧастіше за все, дія розробляється для певної маніпуляції над ресурсом. З цієї причини ідентифікатори дій, в основному,\nє дієсловами, такими як `view`, `update`, і т. д.\n\nЗа замовчуванням, ідентифікатори дій повинні містити тільки такі символи: англійські букви в нижньому регістрі, цифри,\nпідкреслення і дефіси. (Дефіси можуть використовуються для поділу слів.) Наприклад, `view`, `update2` і\n`comment-post` є допустимими ідентифікаторами дій, в той час, як `view?` та `Update` не є такими.\n\nВи можете створювати дії двома способами: вбудовані дії і автономні дії. Вбудована дія є методом, визначеним\nв класі контролера, тоді як автономна дія є класом, успадкованим від [[yii\\base\\Action]] або його нащадків.\nВбудовані дії вимагають менше зусиль для створення і, в основному, використовуються якщо у вас немає потреби \nу повторному використанні цих дій. Автономні дії, з іншого боку, в основному створюються для використання в різних \nконтролерах або для розподілення у вигляді [розширень](structure-extensions.md).\n\n\n### Вбудовані дії <span id=\"inline-actions\"></span>\n\nВбудовані дії це ті дії, які визначені у рамках методів контролера, як це було щойно описано.\n\nНазви методів дій можуть бути отримані із ідентифікаторів дій наступним чином:\n\n1. Привести перший символ кожного слова в ідентифікаторі дії у верхній регістр.\n2. Прибрати дефіси.\n3. Додати префікс `action`.\n\nНаприклад, `index` перетвориться у `actionIndex`, а `hello-world` перетвориться у `actionHelloWorld`.\n\n> Note: Назви імен методів дій є *регістр-залежними*. Якщо у вас є метод `ActionIndex`, то його не буде враховано\n  як метод дії, і в результаті, запит до дії `index` призведе до отримання виключення. Також слід врахувати, \n  що методи дій повинні бути публічними (\"public\"). Приватні (\"private\") або захищені (\"protected\") методи\n  НЕ визначають вбудованих дій.\n\n\nВ основному використовуються вбудовані дії, оскільки для їх створення не потрібного багато зусиль. \nТим не менше, якщо ви плануєте повторно використовувати деякі дії у різних місцях або якщо ви хочете \nперерозподілити дії, ви повинні визначити їх як *автономні дії*.\n\n\n### Автономні дії <span id=\"standalone-actions\"></span>\n\nАвтономні дії визначаються в якості класів, успадкованих від [[yii\\base\\Action]] або його нащадків.\nНаприклад, в релізах Yii присутні [[yii\\web\\ViewAction]] та [[yii\\web\\ErrorAction]], обидва класи\nє окремими діями.\n\nДля використання автономної дії, ви маєте вказати її у *мапі дій* за допомогою перевизначення методу\n[[yii\\base\\Controller::actions()|actions()]] у вашому класі контролера, наступним чином:\n\n```php\npublic function actions()\n{\n    return [\n        // оголошує дію \"error\" за допомогою назви класу\n        'error' => 'yii\\web\\ErrorAction',\n\n        // оголошує дію \"view\" за допомогою конфігураційного масиву\n        'view' => [\n            'class' => 'yii\\web\\ViewAction',\n            'viewPrefix' => '',\n        ],\n    ];\n}\n```\n\nЯк ви можете бачити, метод `actions()` повинен повернути масив, ключами якого є ідентифікатори дій, а значеннями - відповідні \nназви класів дій або [конфігурації](concept-configurations.md). На відміну від вбудованих дій, ідентифікатори автономних дій \nможуть містити довільні символи, доки вони визначені у методі `actions()`.\n\nДля створення класу автономної дії, ви повинні успадкуватись від класу [[yii\\base\\Action]] або його нащадків, і реалізувати\nпублічний (\"public\") метод [[yii\\base\\Action::run()|run()]]. Роль метода [[yii\\base\\Action::run()|run()]] аналогічна\nіншим методам дій. Наприклад,\n\n```php\n<?php\nnamespace app\\components;\n\nuse yii\\base\\Action;\n\nclass HelloWorldAction extends Action\n{\n    public function run()\n    {\n        return \"Hello World\";\n    }\n}\n```\n\n\n### Результати дій <span id=\"action-results\"></span>\n\nЗначення, що повертається від методу дії або методу [[yii\\base\\Action::run()|run()]] автономної дії дуже важливе.\nВоно є результатом виконання відповідної дії.\n\nЗначення, що повертається, може бути об’єктом [відповіді](runtime-responses.md), яке буде відправлено кінцевому користувачу.\n\n* Для [[yii\\web\\Application|веб-додатків]], значення, що повертається, також може бути довільними даними,\n  яке буде призначене до [[yii\\web\\Response::data]], а потім конвертоване у текстовий рядок, що представляє тіло відповіді.\n* Для [[yii\\console\\Application|консольних додатків]], значення, що повертається, також може бути числом, що\n  представляє [[yii\\console\\Response::exitStatus|статус виходу]] виконання команди.\n\nУ вищенаведених прикладах всі результати дій є текстовими рядками, які будуть використані у якості тіла відповіді для\nвідправлення кінцевому користувачу. Наступний приклад показує як дія може перенаправити браузер користувача на новий URL\nза допомогою повернення об’єкта відповіді (оскільки метод [[yii\\web\\Controller::redirect()|redirect()]] повертає \nоб’єкт response):\n\n```php\npublic function actionForward()\n{\n    // перенаправляємо браузер користувача на https://example.com\n    return $this->redirect('https://example.com');\n}\n```\n\n\n### Параметри дій <span id=\"action-parameters\"></span>\n\nМетоди дій для вбудованих дій і методи [[yii\\base\\Action::run()|run()]] для автономних дій можуть приймати, так звані,\n*параметри дії*. Їх значення беруться із запитів. Для [[yii\\web\\Application|веб-додатків]], значення кожного з\nпараметрів дії береться із `$_GET`, використовуючи назву параметра у якості ключа;\nдля [[yii\\console\\Application|консольних додатків]] - вони відповідають аргументам командного рядка.\n\nВ наступному прикладі, дія `view` (вбудована дія) оголошує два параметри: `$id` і `$version`.\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass PostController extends Controller\n{\n    public function actionView($id, $version = null)\n    {\n        // ...\n    }\n}\n```\n\nПараметри дії будуть заповнені для різних запитів наступним чином:\n\n* `https://hostname/index.php?r=post/view&id=123`: параметру `$id` буде присвоєне значення `'123'`, у той час,\n  як `$version` буде мати значення `null`, бо рядок запиту не містить параметра `version`.\n* `https://hostname/index.php?r=post/view&id=123&version=2`: параметрам `$id` і `$version` будуть присвоєні\n  значення `'123'` і `'2'` відповідно.\n* `https://hostname/index.php?r=post/view`: буде отримане виключення [[yii\\web\\BadRequestHttpException]], оскільки\n  обов’язковий параметр `$id` не було вказано у запиті.\n* `https://hostname/index.php?r=post/view&id[]=123`: буде отримане виключення [[yii\\web\\BadRequestHttpException]],\n  оскільки параметр `$id` отримав невірне значення `['123']`.\n\nЯкщо ви хочете, щоб параметр дії приймав масив значень, ви повинні вказати тип `array` для параметра метода, як наведено нижче:\n\n```php\npublic function actionView(array $id, $version = null)\n{\n    // ...\n}\n```\n\nТепер, якщо запит буде містити URL `https://hostname/index.php?r=post/view&id[]=123`, то параметр `$id` отримає\nзначення `['123']`. Якщо запит буде містити URL `https://hostname/index.php?r=post/view&id=123`, то параметр\n`$id` все одно отримає масив, оскільки скалярне значення `'123'` буде автоматично перетворено у масив.\n\nВищенаведені приклади в основному показують як параметри дій працюють для веб-додатків. Більше інформації\nпро параметри консольних додатків наведено в розділі [Консольні команди](tutorial-console.md).\n\n\n### Дія за замовчуванням <span id=\"default-action\"></span>\n\nКожний контролер містить дію за замовчуванням, визначену через властивість [[yii\\base\\Controller::defaultAction]].\nКоли [маршрут](#routes) містить тільки ідентифікатор контролера, то розуміється, що було запитана дія контролера\nза замовчуванням.\n\nЗа замовчуванням, ця дія має значення `index`. Для зміни цього значення необхідно просто перевизначити дану\nвластивість у класі контролера наступним чином:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public $defaultAction = 'home';\n\n    public function actionHome()\n    {\n        return $this->render('home');\n    }\n}\n```\n\n\n## Життєвий цикл контролера <span id=\"controller-lifecycle\"></span>\n\nПри обробці запиту, [додаток](structure-applications.md) створить контролер, базуючись на [маршруті](#routes), \nякий було запитано. Для виконання запиту, контролер пройде через наступні етапи життєвого циклу:\n\n1. Метод [[yii\\base\\Controller::init()]] буде викликаний після того, як контролер був створений і сконфігурований.\n2. Контролер створить об’єкт дії, базуючись на ідентифікаторі дії, яку було запитано:\n   * Якщо ідентифікатор дії не вказано, то буде використано [[yii\\base\\Controller::defaultAction|ідентифікатор дії за замовчуванням]];\n   * Якщо ідентифікатор дії знайдено у [[yii\\base\\Controller::actions()|мапі дій]], то буде створено автономну дію;\n   * Якщо ідентифікатор дії відповідає методу дії, то буде створено вбудовану дію;\n   * В іншому випадку, буде отримане виключення [[yii\\base\\InvalidRouteException]].\n3. Контролер послідовно викликає метод `beforeAction()` додатка, модуля (якщо контролер належить модулю) і\n   самого контролера:\n   * Якщо один із методів повернув `false`, то решта невикликаних методів `beforeAction` будуть пропущені, \n     а виконання дії буде скасовано;\n   * За замовчуванням, кожний виклик метода `beforeAction()` викликає подію `beforeAction`, на яку ви можете призначити обробник.\n4. Контролер виконує дію:\n   * Параметри дії будуть проаналізовані та заповнені із даних запиту.\n5. Контролер послідовно викликає методи `afterAction` контролера, модуля (якщо контролер належить модулю) і додатка:\n   * За замовчуванням, кожний виклик метода `afterAction()` викликає подію `afterAction`, на яку ви можете призначити обробник.\n6. Додаток, отримавши результат виконання дії, привласнює його об’єкту [response](runtime-responses.md).\n\n\n## Кращі практики <span id=\"best-practices\"></span>\n\nВ добре організованому додатку контролери, зазвичай дуже малі, з діями, що містять лише декілька рядків коду.\nЯкщо ваш контролер дуже складний, це зазвичай означає, що вам потрібно провести його рефакторинг і\nперенести деякий код в інші класи.\n\nВ цілому, контролери\n\n* можуть мати доступ до даних [запиту](runtime-requests.md);\n* можуть викликати методи [моделей](structure-models.md) та інших компонентів системи із даними запиту;\n* можуть використовувати [представлення](structure-views.md) для формування відповіді;\n* НЕ повинні займатись обробкою даних - це має відбуватися на [рівні моделей](structure-models.md);\n* мають уникати використання HTML або іншої розмітки - краще це робити у [представленнях](structure-views.md).\n"
  },
  {
    "path": "docs/guide-uk/structure-entry-scripts.md",
    "content": "Вхідні скрипти\n==============\n\nВхідні скрипти це перша ланка в процесі початкового завантаження додатку. Додаток (веб-додаток або консольний додаток)\nмає єдиний вхідний скрипт. Кінцеві користувачі роблять запити до вхідного скрипту, який створює екземпляри додатка та\nперенаправляє запит до них.\n\nВхідні скрипти для веб-додатків повинні бути збережені в директоріях, доступних через веб, таким чином, вони можуть бути\nдоступними кінцевим користувачам. Зазвичай вони називаються `index.php`, але також можуть використовуватись й інші\nімена, які можуть бути розпізнані веб-серверами.\n\nВхідні скрипти для консольних додатків зазвичай розміщенні у [базовій директорії](structure-applications.md)\nдодатку і мають назву `yii` (з суфіксом `.php`). Вони повинні мати права на виконання, щоб користувачі мали змогу\nзапускати консольні додатки через команду `./yii <маршрут> [аргументи] [опції]`.\n\nВхідні скрипти в основному виконують наступну роботу:\n\n* Визначають глобальні константи;\n* Реєструють автозавантажувач класів [Composer](https://getcomposer.org/doc/01-basic-usage.md#autoloading);\n* Підключають файл класу [[Yii]];\n* Завантажують конфігурацію додатка;\n* Створюють і налаштовують екземпляр [додатка](structure-applications.md);\n* Викликають метод [[yii\\base\\Application::run()]] додатка для обробки вхідного запиту.\n\n\n## Веб-додатки <span id=\"web-applications\"></span>\n\nНижче наведений код вхідного скрипту для [базового шаблону проекту](start-installation.md).\n\n```php\n<?php\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n\n// реєстрація автозавантажувача класів Composer\nrequire __DIR__ . '/../vendor/autoload.php';\n\n// підключення файлу класу Yii\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n\n// завантаження конфігурації додатка\n$config = require __DIR__ . '/../config/web.php';\n\n// створення, конфігурація та виконання додатка\n(new yii\\web\\Application($config))->run();\n```\n\n\n## Консольні додатки <span id=\"console-applications\"></span>\n\nНижче наведений аналогічний код вхідного скрипту консольного додатка:\n\n```php\n#!/usr/bin/env php\n<?php\n/**\n * Yii console bootstrap file.\n *\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\n\n// реєстрація автозавантажувача класів Composer\nrequire __DIR__ . '/vendor/autoload.php';\n\n// підключення файлу класу Yii\nrequire __DIR__ . '/vendor/yiisoft/yii2/Yii.php';\n\n// завантаження конфігурації додатка\n$config = require __DIR__ . '/config/console.php';\n\n$application = new yii\\console\\Application($config);\n$exitCode = $application->run();\nexit($exitCode);\n```\n\n\n## Визначення констант <span id=\"defining-constants\"></span>\n\nВхідні скрипти є найкращим місцем для оголошення глобальних констант. Yii підтримує наступні три константи:\n\n* `YII_DEBUG`: вказує чи працює додаток у режимі налагодження (\"debug mode\"), перебуваючи у якому, додаток\n  буде зберігати більше інформації в журналі та покаже більш детальний стек викликів при отриманні виключення. З цієї причини,\n  режим налагодження повинен використовуватись здебільшого в процесі розробки. За замовчуванням значення `YII_DEBUG` дорівнює `false`.\n* `YII_ENV`: вказує в якому середовищі працює додаток. Дана тема детально розглянута у розділі \n  [Конфігурації](concept-configurations.md#environment-constants). За замовчуванням значення `YII_ENV` дорівнює\n  `'prod'`, яке означає, що додаток працює у робочому (\"production\") середовищі.\n* `YII_ENABLE_ERROR_HANDLER`: вказує чи потрібно увімкнути наявний у Yii обробник помилок.\n  За замовчуванням значення даної константи дорівнює `true`.\n\nПри визначенні константи, розробники фреймворку зазвичай використовують код подібний до наступного:\n\n```php\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\n```\n\nякий рівнозначний коду, наведеному нижче:\n\n```php\nif (!defined('YII_DEBUG')) {\n    define('YII_DEBUG', true);\n}\n```\n\nПерший варіант є більш коротким і зрозумілим.\n\nКонстанти мають бути визначені якомога раніше, на самому початку вхідного скрипту, щоб вони могли вплинути на решту\nPHP-файлів, які будуть підключатись.\n"
  },
  {
    "path": "docs/guide-uk/structure-models.md",
    "content": "Моделі\n======\n\nМоделі є частиною архітектури [MVC](https://uk.wikipedia.org/wiki/Модель-вид-контролер).\nЦе об’єкти, які представляють бізнес-дані, бізнес-правила та бізнес-логіку.\n\nВи можете створювати класи моделей шляхом розширення класу [[yii\\base\\Model]] або його нащадків. Базовий клас\n[[yii\\base\\Model]] підтримує багато корисних можливостей:\n\n* [Атрибути](#attributes): представляють бізнес-дані та можуть бути доступними як звичайні властивості об’єкту\n  або як елементи масиву;\n* [Мітки атрибутів](#attribute-labels): визначають мітки, які використовуються при відображенні атрибутів;\n* [Масове призначення](#massive-assignment): підтримується заповнення декількох атрибутів одним кроком;\n* [Правила перевірки](#validation-rules): забезпечують ввід даних на основі оголошених правил перевірки;\n* [Експортування даних](#data-exporting): дозволяє даним моделі бути експортованими у масиви з налаштуванням форматів.\n\nКлас `Model` також є базовим класом для більш розширених моделей, таких як [Active Record](db-active-record.md).\nБудь ласка, зверніться до відповідної документації для більш детальної інформації про ці розширені моделі.\n\n> Info: Необов’язково створювати класи моделей на базі класу [[yii\\base\\Model]]. Однак, оскільки багато компонентів Yii\nпобудовані так, щоб підтримувати [[yii\\base\\Model]], краще використовувати його як базовий клас для моделей.\n\n\n## Атрибути <span id=\"attributes\"></span>\n\nМоделі представляють бізнес-дані у вигляді *атрибутів*. Кожний атрибут є публічно доступною властивістю\nмоделі. Метод [[yii\\base\\Model::attributes()]] визначає, які атрибути має клас моделі.\n\nВи можете отримати доступ до атрибута як до звичайної властивості об’єкту:\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// \"name\" - це атрибут моделі ContactForm\n$model->name = 'example';\necho $model->name;\n```\n\nВи також можете отримувати доступ до атрибутів як до елементів масиву, завдяки підтримці\n[ArrayAccess](https://www.php.net/manual/en/class.arrayaccess.php) та [ArrayIterator](https://www.php.net/manual/en/class.arrayiterator.php)\nу класі [[yii\\base\\Model]]:\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// доступ до атрибутів як до елементів масиву\n$model['name'] = 'example';\necho $model['name'];\n\n// перебір атрибутів\nforeach ($model as $name => $value) {\n    echo \"$name: $value\\n\";\n}\n```\n\n\n### Визначення атрибутів <span id=\"defining-attributes\"></span>\n\nЗа замовчуванням, якщо ваш клас моделі успадкований безпосередньо від [[yii\\base\\Model]], усі його *не статичні публічні*\nзмінні є атрибутами. Наприклад, клас моделі `ContactForm`, наведений нижче, має чотири атрибути: `name`, `email`,\n`subject` і `body`. Модель `ContactForm` використовується для репрезентації вхідних даних, отриманих з HTML-форми.\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\n\nclass ContactForm extends Model\n{\n    public $name;\n    public $email;\n    public $subject;\n    public $body;\n}\n```\n\n\nВи можете перевизначити метод [[yii\\base\\Model::attributes()]] для визначення атрибутів в інший спосіб. Метод повинен\nповертати імена атрибутів у моделі. Наприклад, [[yii\\db\\ActiveRecord]] повертає\nімена колонок відповідної таблиці бази даних як імена атрибутів. Також можливо знадобиться перевизначити\nмагічні методи, такі як `__get()` і `__set()`, щоб атрибути могли бути доступними як\nзвичайні властивості об’єкту.\n\n\n### Мітки атрибутів <span id=\"attribute-labels\"></span>\n\nПри відображенні значень чи при отриманні вводу для атрибутів, часто необхідно відобразити деякі написи, пов’язані з\nатрибутами. Наприклад, якщо атрибут має ім’я `firstName`, ви можете відобразити мітку `First Name`, яка є більш зручною\nдля користувача при відображенні кінцевим користувачам у таких місцях як поля форми чи повідомлення про помилки.\n\nВи можете отримати мітку атрибуту викликом методу [[yii\\base\\Model::getAttributeLabel()|getAttributeLabel()]]. Наприклад,\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// відобразить \"Name\"\necho $model->getAttributeLabel('name');\n```\n\nЗа замовчуванням мітки атрибутів генеруються автоматично з імен атрибутів. Генерування виконується\nметодом [[yii\\base\\Model::generateAttributeLabel()|generateAttributeLabel()]]. Він перетворить імена змінних зі стилю\n\"camel-case\" у декілька слів з першою літерою у кожному слові у верхньому регістрі. Наприклад, `username` стане\n`Username`, а `firstName` стане `First Name`.\n\nЯкщо ви не хочете використовувати автоматично згенеровані мітки, ви можете перевизначити\n[[yii\\base\\Model::attributeLabels()|generateAttributeLabel()]] для точного визначення міток атрибутів. Наприклад,\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\n\nclass ContactForm extends Model\n{\n    public $name;\n    public $email;\n    public $subject;\n    public $body;\n\n    public function attributeLabels()\n    {\n        return [\n            'name' => 'Your name',\n            'email' => 'Your email address',\n            'subject' => 'Subject',\n            'body' => 'Content',\n        ];\n    }\n}\n```\n\nДля додатків, що підтримують декілька мов, ви можливо захочете перекласти мітки атрибутів. Це можливо зробити\nу методі [[yii\\base\\Model::attributeLabels()|attributeLabels()]] також, як показано нижче:\n\n```php\npublic function attributeLabels()\n{\n    return [\n        'name' => \\Yii::t('app', 'Your name'),\n        'email' => \\Yii::t('app', 'Your email address'),\n        'subject' => \\Yii::t('app', 'Subject'),\n        'body' => \\Yii::t('app', 'Content'),\n    ];\n}\n```\n\nВи можете навіть умовно визначати мітки атрибутів. Наприклад, на основі [сценарію](#scenarios), в якому модель\nвикористовується, ви можете повертати різні мітки для одного й того ж атрибуту.\n\n> Info: Строго кажучи, мітки атрибутів є частиною [представлень](structure-views.md). Але оголошення міток\nу моделях є часто дуже зручним та призводить до чистоти коду та можливості його повторного використання.\n\n\n## Сценарії <span id=\"scenarios\"></span>\n\nМодель може бути використана у різних *сценаріях*. Наприклад, модель `User` може бути використана для збору вводу при вході користувача,\nале також вона може бути використана з метою реєстрації користувача. У різних сценаріях модель може використовувати різні\nбізнес-правила та бізнес-логіку. Наприклад, атрибут `email` може бути обов’язковим у процесі реєстрації користувача,\nале не потрібним у процесі входу користувача.\n\nМодель використовує властивість [[yii\\base\\Model::scenario]] для того, щоб відслідковувати сценарій, в якому вона використовується.\nЗа замовчуванням модель підтримує тільки один сценарій іменований `default`. Наступний код показує два шляхи\nпризначення сценарію моделі:\n\n```php\n// сценарій призначено як властивість\n$model = new User;\n$model->scenario = 'login';\n\n// сценарій призначено через конфігурацію\n$model = new User(['scenario' => 'login']);\n```\n\nЗа замовчуванням сценарії, які підтримуються моделлю, обумовлюються [правилами перевірки](#validation-rules) оголошеними\nу моделі. Однак, ви можете змінити цю поведінку, перевизначивши метод [[yii\\base\\Model::scenarios()|scenarios()]]\nяк показано нижче:\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass User extends ActiveRecord\n{\n    public function scenarios()\n    {\n        return [\n            'login' => ['username', 'password'],\n            'register' => ['username', 'email', 'password'],\n        ];\n    }\n}\n```\n\n> Info: У вищенаведеному та у наступних прикладах класи моделей успадковані від [[yii\\db\\ActiveRecord]],\nтому що використання декількох сценаріїв зазвичай відбувається з класами [Active Record](db-active-record.md).\n\nМетод `scenarios()` повертає масив, ключами якого є імена сценаріїв, а значеннями - відповідні\n*активні атрибути*. Активні атрибути можуть бути [масово призначеними](#massive-assignment) та підлягають\n[перевірці](#validation-rules). У вищенаведеному прикладі, атрибути `username` і `password` є активними\nу сценарії `login`; в той час як у сценарії `register`, також активним є атрибут `email` разом з `username` і `password`.\n\nСтандартна реалізація методу `scenarios()` повертає усі сценарії, знайдені у правилах перевірки, оголошених\nметодом [[yii\\base\\Model::rules()||rules()]]. При перевизначенні методу [[yii\\base\\Model::scenarios()||scenarios()]],\nякщо ви хочете ввести нові сценарії на додачу до тих сценаріїв, необхідно написати код подібний до наступного:\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass User extends ActiveRecord\n{\n    public function scenarios()\n    {\n        $scenarios = parent::scenarios();\n        $scenarios['login'] = ['username', 'password'];\n        $scenarios['register'] = ['username', 'email', 'password'];\n        return $scenarios;\n    }\n}\n```\n\nМожливості сценаріїв в основному використовуються при [перевірці](#validation-rules) та [масовому призначенні](#massive-assignment) атрибутів.\nОднак, ви можете використовувати їх для інших цілей. Наприклад, ви можете оголошувати [мітки атрибутів](#attribute-labels)\nпо-різному, в залежності від поточного сценарію.\n\n\n## Правила перевірки <span id=\"validation-rules\"></span>\n\nДані для моделі, які отримуються від кінцевих користувачів, повинні пройти перевірку, щоб пересвідчитись, що вони задовольняють\nпевні правила (названі *правилами перевірки*, також відомі як *бізнес-правила*). Наприклад, дано модель `ContactForm`,\nви, можливо, хочете пересвідчитись, що всі атрибути заповнені та атрибут `email` містить коректну адресу електронної пошти.\nЯкщо значення для деяких атрибутів не задовольняють відповідні бізнес-правила, то будуть відображенні належні\nповідомлення про помилки, щоб допомогти користувачу виправити їх.\n\nВи можете викликати [[yii\\base\\Model::validate()|validate()]] для перевірки отриманих даних. Даний метод використає\nправила перевірки, оголошені у [[yii\\base\\Model::rules()|rules()]], для перевірки кожного необхідного атрибуту. При відсутності\nпомилок він поверне `true`. В іншому випадку, він збереже помилки у властивості [[yii\\base\\Model::errors]]\nта поверне `false`. Наприклад:\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// заповнення атрибутів моделі даними від користувача\n$model->attributes = \\Yii::$app->request->post('ContactForm');\n\nif ($model->validate()) {\n    // усі дані є коректними\n} else {\n    // невдала перевірка: $errors - масив, що містить повідомлення про помилки\n    $errors = $model->errors;\n}\n```\n\n\nДля оголошення правил перевірки, пов’язаних з моделлю, необхідно перевизначити метод [[yii\\base\\Model::rules()|rules()]],\nповернувши правила, які атрибути моделі повинні задовольнити. Наступний приклад показує правила перевірки, оголошені\nдля моделі `ContactForm`:\n\n```php\npublic function rules()\n{\n    return [\n        // атрибути name, email, subject і body є обов’язковими\n        [['name', 'email', 'subject', 'body'], 'required'],\n\n        // атрибут email повинен бути коректною адресою електронної пошти\n        ['email', 'email'],\n    ];\n}\n```\n\nПравило може використовуватись для перевірки одного або кількох атрибутів, також атрибут може перевірятись одним або кількома правилами.\nБудь ласка, зверніться до розділу [Перевірка вводу](input-validation.md) для більш детальної інформації про те, як оголошувати\nправила перевірки.\n\nІноді необхідно, щоб правила застосовувались лише у певних [сценаріях](#scenarios). Для цього ви можете\nвизначати властивість `on` для правила, як наведено нижче:\n\n```php\npublic function rules()\n{\n    return [\n        // username, email і password є обов’язковими у сценарії \"register\"\n        [['username', 'email', 'password'], 'required', 'on' => 'register'],\n\n        // username і password є обов’язковими у сценарії \"login\"\n        [['username', 'password'], 'required', 'on' => 'login'],\n    ];\n}\n```\n\nЯкщо ви не визначите властивість `on`, то правило буде застосоване у всіх сценаріях. Правило називається\n*активним правилом*, якщо воно може бути застосоване у поточному [[yii\\base\\Model::scenario|сценарії]].\n\nАтрибут буде перевірятись тоді й лише тоді, якщо він є активним атрибутом, оголошеним у `scenarios()` і\nпов’язаний з одним або кількома активними правилами, оголошеними у `rules()`.\n\n\n## Масове призначення <span id=\"massive-assignment\"></span>\n\nМасове призначення - це зручний спосіб заповнення моделі даними, введеними користувачем, використовуючи один рядок коду.\nАтрибути моделі заповнюються шляхом призначення вхідних даних безпосередньо властивості [[yii\\base\\Model::$attributes]].\nНаступні два приклади коду є еквівалентними, обидва намагаються призначити дані з форми, представлені кінцевими користувачами,\nатрибутам моделі `ContactForm`. Явно, що перший приклад, який використовує масове призначення, є набагато чистішим\nй менш схильним до помилок, ніж другий:\n\n```php\n$model = new \\app\\models\\ContactForm;\n$model->attributes = \\Yii::$app->request->post('ContactForm');\n```\n\n```php\n$model = new \\app\\models\\ContactForm;\n$data = \\Yii::$app->request->post('ContactForm', []);\n$model->name = isset($data['name']) ? $data['name'] : null;\n$model->email = isset($data['email']) ? $data['email'] : null;\n$model->subject = isset($data['subject']) ? $data['subject'] : null;\n$model->body = isset($data['body']) ? $data['body'] : null;\n```\n\n\n### Безпечні атрибути <span id=\"safe-attributes\"></span>\n\nМасові призначення застосовуються лише до, так званих *безпечних атрибутів*, які є атрибутами, що перелічені у\n[[yii\\base\\Model::scenarios()]] для поточного [[yii\\base\\Model::scenario|сценарію]] моделі.\nНаприклад, якщо модель `User` має нижченаведене оголошення сценарію, потім, коли поточним сценарієм\nбуде `login`, лише `username` і `password` можуть бути масово призначеними. Усі інші атрибути\nбудуть проігноровані.\n\n```php\npublic function scenarios()\n{\n    return [\n        'login' => ['username', 'password'],\n        'register' => ['username', 'email', 'password'],\n    ];\n}\n```\n\n> Info: Причиною того, що масове призначення застосовується лише для безпечних атрибутів є необхідність\n  контролювати те, які атрибути можуть змінюватись даними від кінцевого користувача. Наприклад, якщо модель `User`\n  має атрибут `permission`, який визначає дозволи призначені користувачу, то необхідно бути впевненим,\n  що цей атрибут може змінюватись лише адміністраторами через back-end інтерфейс.\n\nСтандартна реалізація методу [[yii\\base\\Model::scenarios()|scenarios()]] повертає усі сценарії та атрибути,\nзнайдені у [[yii\\base\\Model::rules()|rules()]], якщо ви не перевизначили цей метод. Це означає, що атрибут є безпечним,\nпоки знаходиться в одному із активних правил перевірки.\n\nЗ цієї причини, надається спеціальний валідатор з псевдонімом `safe`, що дозволяє оголошувати атрибут\nбезпечним без фактичної його перевірки. Наприклад, наступне правило оголошує обидва атрибути `title`\nі `description` безпечними:\n\n```php\npublic function rules()\n{\n    return [\n        [['title', 'description'], 'safe'],\n    ];\n}\n```\n\n\n### Небезпечні атрибути <span id=\"unsafe-attributes\"></span>\n\nЯк описано вище, метод [[yii\\base\\Model::scenarios()]] слугує двом цілям: вирішенню, які атрибути\nповинні бути перевірені, і вирішенню, які атрибути є безпечними. У деяких рідких випадках, існує необхідність перевірити\nатрибут, але не позначати його безпечним. Цього можна досягнути за допомогою префіксу знак оклику `!` у імені\nатрибуту при його оголошені в `scenarios()`, як у атрибута `secret` в наведеному прикладі:\n\n```php\npublic function scenarios()\n{\n    return [\n        'login' => ['username', 'password', '!secret'],\n    ];\n}\n```\n\nКоли модель буде присутня у сценарії `login`, усі три атрибути будуть перевірені. Однак, лише атрибути\n`username` і `password` можуть бути масово призначеними. Для призначення атрибуту `secret` вхідного значення\nнеобхідно зробити це явно, як наведено нижче:\n\n```php\n$model->secret = $secret;\n```\n\n\n## Експортування даних <span id=\"data-exporting\"></span>\n\nЧасто існує потреба експортувати моделі у різні формати. Наприклад, може виникнути необхідність в перетворенні набору\nмоделей у формат JSON або Excel. Процес експортування може бути розділений на два незалежні кроки.\nПершим кроком, моделі перетворюються у масиви; другим кроком, масиви перетворюються у\nвідповідні формати. Ви можете зосередитись лише на першому кроці, оскільки другий крок можна виконати за допомогою\nзагальних форматтерів даних, одним з яких є [[yii\\web\\JsonResponseFormatter]].\n\nНайпростіший спосіб перетворення моделі у масив - це використання властивості [[yii\\base\\Model::$attributes]].\nНаприклад:\n\n```php\n$post = \\app\\models\\Post::findOne(100);\n$array = $post->attributes;\n```\n\nЗа замовчуванням властивість [[yii\\base\\Model::$attributes]] поверне значення *усіх* атрибутів,\nоголошених у [[yii\\base\\Model::attributes()]].\n\nБільш гнучкий та потужний спосіб перетворення моделі в масив - використання методу [[yii\\base\\Model::toArray()]].\nЙого типова поведінка така ж як у [[yii\\base\\Model::$attributes]]. Однак, він дозволяє обирати,\nякі елементи даних, що називаються *полями*, включати до масиву та як вони повинні бути форматовані.\nНасправді, це стандартний спосіб експортування моделей при розробці веб-сервісів RESTful, як описано у\nрозділі [Форматування відповіді](rest-response-formatting.md).\n\n\n### Поля <span id=\"fields\"></span>\n\nПоле - це просто іменований елемент у масиві, отриманому через виклик методу [[yii\\base\\Model::toArray()]]\nмоделі.\n\nЗа замовчуванням імена полів є еквівалентними іменам атрибутів. Однак, можливо змінити цю поведінку за допомогою перевизначення\nметодів [[yii\\base\\Model::fields()|fields()]] і/або [[yii\\base\\Model::extraFields()|extraFields()]]. Обидва методи\nповинні повертати перелік визначень полів. Поля, визначені у `fields()` є полями за замовчуванням, це означає, що\n`toArray()` повертатиме ці поля за замовчуванням. Метод `extraFields()` визначає додатково доступні поля,\nякі також можуть бути повернутими через `toArray()`, якщо будуть зазначені у параметрі `$expand`. Наприклад,\nнаступний код буде повертати усі поля, визначені у `fields()`, а також поля `prettyName` і `fullAddress`,\nякщо вони визначені у `extraFields()`:\n\n```php\n$array = $model->toArray([], ['prettyName', 'fullAddress']);\n```\n\nВи можете перевизначити `fields()`, щоб додати, видалити, перейменувати або перевизначити поля. Значенням, яке повертатиме `fields()`,\nповинен бути масив. Ключами масиву є імена полів, а значеннями масиву - відповідні\nвизначення полів, які можуть бути або іменами властивостей/атрибутів, або анонімними функціями, що повертають\nвідповідні значення полів. В окремому випадку, коли ім’я поля збігається з ім’ям його атрибуту,\nви можете пропустити ключ масиву. Наприклад:\n\n```php\n// точний перелік кожного поля найкраще використовувати тоді, коли ви хочете бути впевненим, що зміни\n// у вашій таблиці БД або у атрибутах моделі не викликають змін ваших полів (для збереження зворотної сумісності API)\npublic function fields()\n{\n    return [\n        // ім’я поля збігається з ім’ям атрибуту\n        'id',\n\n        // ім’я поля - \"email\", ім’я відповідного атрибуту - \"email_address\"\n        'email' => 'email_address',\n\n        // ім’я поля - \"name\", його значення визначене за допомогою анонімної PHP-функції\n        'name' => function () {\n            return $this->first_name . ' ' . $this->last_name;\n        },\n    ];\n}\n\n// відфільтровування деяких полів найкраще використовувати тоді, коли ви хочете\n// успадкувати батьківську реалізацію та виключити деякі \"чутливі\" поля\npublic function fields()\n{\n    $fields = parent::fields();\n\n    // вилучити поля, які містять конфіденційну інформацію\n    unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);\n\n    return $fields;\n}\n```\n\n> Warning: Оскільки за замовчуванням усі атрибути моделі будуть включені до масиву, що експортується, ви повинні\nперевірити ваші дані, щоб бути впевненим, що вони не містять конфіденційної інформації. Якщо ж така інформація присутня,\nвам потрібно перевизначити метод `fields()` та відфільтрувати її. У вищенаведеному прикладі були\nвідфільтровані поля `auth_key`, `password_hash` і `password_reset_token`.\n\n\n## Кращі практики <span id=\"best-practices\"></span>\n\nМоделі є центральним місцем репрезентації бізнес-даних, бізнес-правил і бізнес-логіки. Вони часто потребують повторного\nвикористання у різних місцях. В добре організованому додатку моделі зазвичай набагато більші, ніж\n[контролери](structure-controllers.md).\n\nВ цілому, моделі\n\n* можуть містити атрибути для репрезентації бізнес-даних;\n* можуть містити правила перевірки для забезпечення коректності та цілісності даних;\n* можуть містити методи, які реалізують бізнес-логіку;\n* НЕ повинні безпосередньо отримувати доступ до запиту, сесії або будь-яких інших даних середовища. Ці дані повинні бути введені\n  у моделі за допомогою [контролерів](structure-controllers.md);\n* повинні уникати вкладеного HTML- або іншого презентаційного коду - це краще робити у [представленнях](structure-views.md);\n* мають уникати завеликої кількості [сценаріїв](#scenarios) в одній моделі.\n\nЯк правило, ви можете враховувати останню рекомендацію, з наведених вище, тоді, коли розробляєте великі складні системи.\nУ цих системах моделі можуть бути дуже великими, оскільки вони використовуються у багатьох місцях й тому можуть містити\nбагато наборів правил і бізнес-логіки. Це часто перетворюється у кошмар під час супроводу коду моделі,\nчерез те, що невелика зміна коду може зачіпати декілька різних місць. Щоб полегшити супровід коду моделі,\nвам необхідно дотримуватися наступної стратегії:\n\n* Визначити набір базових класів моделей, які є спільними для різних [додатків](structure-applications.md) або\n  [модулів](structure-modules.md). Ці класи моделей повинні містити мінімальний набір правил та логіки, які\n  є загальними для усіх додатків та модулів, які їх використовують.\n* У кожному [додатку](structure-applications.md) або [модулі](structure-modules.md), який використовує модель,\n  визначити конкретний клас моделі, що розширює відповідний базовий клас моделі. Конкретні класи моделей\n  повинні містити правила та логіку, які є специфічними для додатка чи модуля.\n\nНаприклад, у [розширеному шаблоні проекту](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide-uk/README.md), ви можете визначати базовий клас\nмоделі `common\\models\\Post`. Потім для front-end додатку ви визначаєте та використовуєте конкретний клас моделі\n`frontend\\models\\Post`, який розширює клас `common\\models\\Post`. Аналогічно для back-end додатку,\nви визначаєте `backend\\models\\Post`. Подібна стратегія дає вам впевненість в тому, що код у `frontend\\models\\Post`\nє специфічним тільки для front-end додатку, та якщо ви внесете будь-які зміни до нього, то не має потреби турбуватись, що\nзміни можуть порушити back-end додаток.\n"
  },
  {
    "path": "docs/guide-uk/structure-overview.md",
    "content": "Огляд\n=====\n\nДодатки Yii організовані згідно архітектурного шаблону [Модель-Представлення-Контролер (MVC)](https://uk.wikipedia.org/wiki/Модель-вид-контролер).\n[Моделі](structure-models.md) являють собою дані, бізнес-логіку та бізнес-правила; \n[представлення](structure-views.md) відповідають за відображення даних моделей;\n[контролери](structure-controllers.md) приймають вхідні дані від користувача і перетворюють їх у команди для\n[моделей](structure-models.md) та [представлень](structure-views.md).\n\nОкрім MVC, Yii додаток також має наступні сутності:\n\n* [вхідні скрипти](structure-entry-scripts.md): це PHP-скрипти, які доступні напряму кінцевому користувачу додатка.\n  Вони відповідають за запуск циклу обробки запиту.\n* [додатки](structure-applications.md): це глобально доступні об’єкти, які відповідають за коректну роботу різних \n  компонентів додатка і їх координацію для обробки запиту.\n* [компоненти додатку](structure-application-components.md): це об’єкти, зареєстровані в додатку і які надають\n  різноманітні можливості для обробки запитів.\n* [модулі](structure-modules.md): це самодостатні пакунки, що включають в себе повністю всі ресурси для MVC.\n  Додаток може бути організовано за допомогою декількох модулів.\n* [фільтри](structure-filters.md): це код, який повинен бути виконаний до і після обробки запиту контролерами.\n* [віджети](structure-widgets.md): це об’єкти, які можуть бути вбудованими у [представлення](structure-views.md).\n  Вони можуть містити логіку контролера і можуть бути повторно використаними у різних представленнях.\n\nНа наступній діаграмі наведена структурна схема додатку:\n\n![Статична структура додатку](images/application-structure.png)\n"
  },
  {
    "path": "docs/guide-uk/structure-views.md",
    "content": "Представлення\n=============\n\nПредставлення є частиною архітектури [MVC](https://uk.wikipedia.org/wiki/Модель-вид-контролер).\nЦе код, який відповідає за відображення даних кінцевим користувачам. У веб-додатку представлення зазвичай створені\nу вигляді *шаблонів представлень*, які є файлами скриптів PHP, що містять переважно HTML-код та презентаційний PHP-код.\nУправління ними здійснюється за допомогою [компонента додатку](structure-application-components.md) [[yii\\web\\View|view]], який надає часто використовувані методи\nдля полегшення компонування та формування представлення. Для простоти, часто шаблони представлень або файли шаблонів представлень називаються\nпредставленнями.\n\n\n## Створення представлень <span id=\"creating-views\"></span>\n\nЯк зазначено вище, представлення є простим скриптом PHP, який містить HTML- та PHP-код. Далі наведено представлення,\nщо представляє форму входу користувача. Як можна побачити, PHP-код використовується для генерування динамічного вмісту, такого як\nзаголовку сторінки та форми, тоді як HTML-код організовує їх у презентабельну HTML-сторінку.\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n/**\n * @var \\yii\\web\\View $this\n * @var \\yii\\widgets\\ActiveForm $form\n * @var \\app\\models\\LoginForm $model\n */\n\n$this->title = 'Вхід';\n?>\n<h1><?= Html::encode($this->title) ?></h1>\n\n<p>Будь ласка, заповніть наступні поля для входу:</p>\n\n<?php $form = ActiveForm::begin(); ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n    <?= Html::submitButton('Увійти') ?>\n<?php ActiveForm::end(); ?>\n```\n\nВсередині представлення ви можете використовувати `$this`, що відноситься до [[yii\\web\\View|компоненту представлення]], який забезпечує керування\nцим шаблоном та його формування.\n\nОкрім `$this`, у представленні можуть бути інші попередньо визначені змінні, такі як `$model` у прикладі\nвище. Ці змінні представляють дані, які були *передані* у представлення через [контролери](structure-controllers.md)\nабо інші об’єкти, які спричинили [формування представлення](#rendering-views).\n\n> Tip: Попередньо визначені змінні перелічені в блоці коментаря у початку представлення, так що вони можуть\n  бути розпізнані в інтегрованих середовищах розробки (IDE). Це також є хорошим способом документування ваших представлень.\n\n\n### Безпека <span id=\"security\"></span>\n\nПри створенні представлень, які генерують HTML-сторінки, важливо кодувати і/або фільтрувати дані, що надходять\nвід кінцевих користувачів, перед їх відображенням. В протилежному випадку ваш додаток може стати предметом\nатак типу [міжсайтовий скриптінг (XSS)](https://uk.wikipedia.org/wiki/Міжсайтовий_скриптінг).\n\nДля відображення звичайного тексту спершу закодуйте його за допомогою [[yii\\helpers\\Html::encode()]]. Наприклад, в наступному коді\nперед відображенням кодується ім’я користувача:\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n\n<div class=\"username\">\n    <?= Html::encode($user->name) ?>\n</div>\n```\n\nДля відображення HTML-вмісту використовуйте [[yii\\helpers\\HtmlPurifier]], щоб відфільтрувати потенційно небезпечний вміст. Наприклад, в наступному\nкоді фільтрується вміст публікації перед відображенням:\n\n```php\n<?php\nuse yii\\helpers\\HtmlPurifier;\n?>\n\n<div class=\"post\">\n    <?= HtmlPurifier::process($post->text) ?>\n</div>\n```\n\n> Tip: В той час як HTMLPurifier бездоганно виконує роботу, щоб зробити вивід безпечним, він не є швидким. Вам потрібно розглянути\n  можливість [кешування](caching-overview.md) результатів фільтрування, якщо ваш додаток потребує високої швидкодії.\n\n\n### Впорядкування представлень <span id=\"organizing-views\"></span>\n\nПодібно до [контролерів](structure-controllers.md) та [моделей](structure-models.md), існують домовленості щодо впорядкування представлень.\n\n* Представлення, які формуються контролером, повинні бути розміщені в директорії `@app/views/ControllerID` за замовчуванням,\n  де `ControllerID` відповідає [ідентифікатору контролера](structure-controllers.md#routes). Наприклад, для\n  класу контролера `PostController` директорія повинна бути `@app/views/post`; для класу `PostCommentController`\n  директорія повинна бути `@app/views/post-comment`. У випадку, коли контролер належить модулю, директорія\n  повинна бути `views/ControllerID` у [[yii\\base\\Module::basePath|директорії модуля]].\n* Представлення, які формуються у [віджетах](structure-widgets.md), повинні бути розміщені в директорії `WidgetPath/views` за\n  замовчуванням, де `WidgetPath` означає шлях до директорії, в якій знаходиться файл класу віджету.\n* Для представлень, які формуються іншими об’єктами, рекомендується виконувати домовленості подібно до віджетів.\n\nВи можете налаштувати ці типові директорії представлень, перевизначивши метод [[yii\\base\\ViewContextInterface::getViewPath()]]\nконтролерів або віджетів.\n\n\n## Формування представлень <span id=\"rendering-views\"></span>\n\nВи можете формувати представлення в [контролерах](structure-controllers.md), [віджетах](structure-widgets.md) чи в будь-яких\nінших місцях за допомогою виклику методів формування представлення. Усі ці методи мають подібну сигнатуру, що наведена нижче:\n\n```\n/**\n * @param string $view ім’я представлення або шлях до файлу, в залежності від того, який метод формування використовується\n * @param array $params дані, які передаються представленню\n * @return string результат формування\n */\nmethodName($view, $params = [])\n```\n\n\n### Формування в контролерах <span id=\"rendering-in-controllers\"></span>\n\nВсередині [контролерів](structure-controllers.md), ви можете викликати наступні методи контролера для формування представлень:\n\n* [[yii\\base\\Controller::render()|render()]]: формує [іменоване представлення](#named-views) та застосовує [макет](#layouts)\n  до результату формування.\n* [[yii\\base\\Controller::renderPartial()|renderPartial()]]: формує [іменоване представлення](#named-views) без будь-якого макету.\n* [[yii\\web\\Controller::renderAjax()|renderAjax()]]: формує [іменоване представлення](#named-views) без будь-якого макету\n  та включає усі зареєстровані скрипти і файли JS/CSS. Це зазвичай використовується у відповіді на веб-запити AJAX.\n* [[yii\\base\\Controller::renderFile()|renderFile()]]: формує представлення визначене шляхом файлу представлення або\n  [псевдонімом](concept-aliases.md).\n* [[yii\\base\\Controller::renderContent()|renderContent()]]: формує статичний текстовий рядок, включаючи його в\n  поточний застосовний [макет](#layouts). Цей метод доступний починаючи з версії 2.0.1.\n\nНаприклад:\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse app\\models\\Post;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\n\nclass PostController extends Controller\n{\n    public function actionView($id)\n    {\n        $model = Post::findOne($id);\n        if ($model === null) {\n            throw new NotFoundHttpException;\n        }\n\n        // формує представлення з ім’ям \"view\" та застосовує макет до нього\n        return $this->render('view', [\n            'model' => $model,\n        ]);\n    }\n}\n```\n\n\n### Формування у віджетах <span id=\"rendering-in-widgets\"></span>\n\nВсередині [віджетів](structure-widgets.md), ви можете викликати наступні методи віджету для формування представлень.\n\n* [[yii\\base\\Widget::render()|render()]]: формує [іменоване представлення](#named-views).\n* [[yii\\base\\Widget::renderFile()|renderFile()]]: формує представлення визначене шляхом файлу представлення або\n  [псевдонімом](concept-aliases.md).\n\nНаприклад:\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Widget;\nuse yii\\helpers\\Html;\n\nclass ListWidget extends Widget\n{\n    public $items = [];\n\n    public function run()\n    {\n        // формує представлення з ім’ям \"list\"\n        return $this->render('list', [\n            'items' => $this->items,\n        ]);\n    }\n}\n```\n\n\n### Формування у представленнях <span id=\"rendering-in-views\"></span>\n\nВи можете формувати представлення всередині іншого представлення за допомогою виклику одного з наступних методів наданих [[yii\\base\\View|компонентом представлення]]:\n\n* [[yii\\base\\View::render()|render()]]: формує [іменоване представлення](#named-views).\n* [[yii\\web\\View::renderAjax()|renderAjax()]]: формує [іменоване представлення](#named-views) та включає усі зареєстровані\n  скрипти і файли JS/CSS. Це зазвичай використовується у відповіді на веб-запити AJAX.\n* [[yii\\base\\View::renderFile()|renderFile()]]: формує представлення визначене шляхом файлу представлення або\n  [псевдонімом](concept-aliases.md).\n\nНаприклад, наступний код у представленні формує файл представлення `_overview.php`, який знаходиться в тій самій директорії,\nщо й представлення, яке зараз формується. Запам’ятайте, що `$this` у представленні відноситься до компонента [[yii\\base\\View|представлення]]:\n\n```php\n<?= $this->render('_overview') ?>\n```\n\n\n### Формування в інших місцях <span id=\"rendering-in-other-places\"></span>\n\nВ будь-якому місці ви можете отримати доступ до компонента додатка [[yii\\base\\View|представлення]] за допомогою виразу\n`Yii::$app->view` та потім викликати його вищезгадані методи для формування представлення. Наприклад:\n\n```php\n// відображає файл представлення \"@app/views/site/license.php\"\necho \\Yii::$app->view->renderFile('@app/views/site/license.php');\n```\n\n\n### Іменовані представлення <span id=\"named-views\"></span>\n\nПри формуванні представлення ви можете визначити його, використовуючи ім’я представлення або шлях/псевдонім до файлу представлення. У більшості випадків\nви будете використовувати перший варіант, тому що він більш лаконічний та гнучкий. Представлення, визначені за допомогою імен називаються *іменованими представленнями*.\n\nІм’я представлення перетворюється у відповідний шлях до файлу представлення за наступними правилами:\n\n* Ім’я представлення можна вказувати без розширення імені файлу. У цьому випадку в якості розширення буде використовуватись `.php`. Наприклад,\n  ім’я представлення `about` відповідає імені файлу `about.php`.\n* Якщо ім’я представлення починається з двох слешів `//`, то відповідним шляхом до файлу представлення буде `@app/views/ViewName`.\n  Це означає, що представлення шукається в [[yii\\base\\Application::viewPath|директорії представлень додатку]].\n  Наприклад, `//site/about` буде перетворено в `@app/views/site/about.php`.\n* Якщо ім’я представлення починається з одного слешу `/`, то шлях до файлу представлення складатиметься з імені представлення та префіксу\n  у вигляді [[yii\\base\\Module::viewPath|директорії представлень]] поточного активного [модулю](structure-modules.md).\n  Якщо немає активного модулю, то буде використовуватись `@app/views/ViewName`. Наприклад, `/user/create` буде перетворено в\n  `@app/modules/user/views/user/create.php`, якщо поточним активним модулем є `user`. Якщо немає активного модулю,\n  то шляхом до файлу представлення буде `@app/views/user/create.php`.\n* Якщо представлення формується з [[yii\\base\\View::context|контекстом]] і контекст має реалізований інтерфейс [[yii\\base\\ViewContextInterface]],\n  то шлях до файлу представлення складатиметься з префікса у вигляді [[yii\\base\\ViewContextInterface::getViewPath()|директорії представлень]]\n  контексту та з імені представлення. Це частіше застосовується до представлень, які формуються всередині контролерів або віджетів. Наприклад,\n  `about` буде перетворено в `@app/views/site/about.php`, якщо контекстом є контролер `SiteController`.\n* Якщо представлення формується в середині іншого представлення, директорія, що містить інше представлення буде додана перед\n  іменем представлення, що формується, для створення актуального шляху до файлу. Наприклад, `item` перетвориться в `@app/views/post/item.php`,\n  якщо буде формуватись із представлення `@app/views/post/index.php`.\n\nЗгідно з наведеними правилами, виклик `$this->render('view')` у контролері `app\\controllers\\PostController` буде\nформувати представлення з файлу `@app/views/post/view.php`, в той час як виклик `$this->render('_overview')` у тому представленні\nбуде формувати представлення з файлу `@app/views/post/_overview.php`.\n\n\n### Доступ до даних у представленнях <span id=\"accessing-data-in-views\"></span>\n\nЄ два підходи при доступові до даних в середині представлення: \"вштовхування\" та \"витягування\".\n\nПередаючи дані другим параметром у методи формування представлення, ви використовуєте підхід \"вштовхування\".\nДані повинні бути надані у вигляді масиву пар ключ-значення. Під час формування представлення буде викликана функція PHP\n`extract()` на цьому масиві, видобувши таким чином змінні у представлення.\nНаприклад, наступний код формування представлення у контролері \"вштовхне\" дві змінні до представлення `report`:\n`$foo = 1` та `$bar = 2`.\n\n```php\necho $this->render('report', [\n    'foo' => 1,\n    'bar' => 2,\n]);\n```\n\nПідхід \"витягування\" активно здобуває дані з [[yii\\base\\View|компоненту представлення]] або інших об’єктів, доступних\nу представленнях (наприклад, `Yii::$app`). Використовуючи нижче наведений код як приклад, всередині представлення можна отримати об’єкт\nза допомогою виразу `$this->context`. У результаті, стає можливим доступ до усіх властивостей та методів\nконтролера у представленні `report`, як, наприклад, ідентифікатору контролера, що наведений у прикладі:\n\n```php\nІдентифікатор контролера: <?= $this->context->id ?>\n```\n\nПідхід \"вштовхування\" є бажанішим шляхом для доступу до даних у представленнях, тому що він робить представлення менш залежними\nвід об’єктів контексту, але його недоліком є необхідність весь час будувати масив даних вручну, що може\nстати стомливим та збільшує вірогідність допустити помилку, якщо представлення використовується та формується у різних місцях.\n\n\n### Обмін даними між представленнями <span id=\"sharing-data-among-views\"></span>\n\n[[yii\\base\\View|Компонент представлення]] надає властивість [[yii\\base\\View::params|params]], яку можна використовувати\nдля обміну даними між представленнями.\n\nНаприклад, в представленні `about`, ви можете мати наступний код, який визначає поточну частину\n\"хлібних крихт\".\n\n```php\n$this->params['breadcrumbs'][] = 'Про нас';\n```\n\nПотім у файлі [макету](#layouts), який також є представленням, ви можете відобразити \"хлібні крихти\", використовуючи дані\nпередані через [[yii\\base\\View::params|params]]:\n\n```php\n<?= yii\\widgets\\Breadcrumbs::widget([\n    'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],\n]) ?>\n```\n\n\n## Макети <span id=\"layouts\"></span>\n\nМакети - особливий тип представлень, які представляють спільні частини для інших представлень. Наприклад, сторінки\nбільшості веб-додатків мають однакові шапку та футер. Ви можете повторювати однакові шапку та футер сторінки\nу кожному представленні, але краще зробити це один раз у макеті та розмістити результат формування вкладеного представлення\nу відповідному місці макету.\n\n\n### Створення макетів <span id=\"creating-layouts\"></span>\n\nОскільки макети є представленнями, вони можуть бути створенні тим самим шляхом, як і звичайні представлення. За замовчуванням, макети\nзберігаються в директорії `@app/views/layouts`. Макети, які використовуються у [модулі](structure-modules.md),\nповинні зберігатись в директорії `views/layouts` під [[yii\\base\\Module::basePath|директорією модуля]].\nВи можете налаштувати типову директорію для макетів сконфігурувавши властивість [[yii\\base\\Module::layoutPath]]\nдодатку або модулів.\n\nНаступний приклад показує як виглядає макет. Майте на увазі, що для кращого сприйняття код у макеті дуже спрощений.\nНа практиці, ви можливо захочете додати більше вмісту до нього, такого як теги секції `<head>`, головне меню та інше.\n\n```php\n<?php\nuse yii\\helpers\\Html;\n\n/**\n * @var \\yii\\web\\View $this\n * @var string $content\n */\n?>\n<?php $this->beginPage() ?>\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\"/>\n    <?= Html::csrfMetaTags() ?>\n    <title><?= Html::encode($this->title) ?></title>\n    <?php $this->head() ?>\n</head>\n<body>\n<?php $this->beginBody() ?>\n    <header>Моя компанія</header>\n    <?= $content ?>\n    <footer>Моя компанія &copy; 2014</footer>\n<?php $this->endBody() ?>\n</body>\n</html>\n<?php $this->endPage() ?>\n```\n\nЯк ви можете бачити, макет генерує HTML-теги, які є спільними для всіх сторінок. У секції `<body>`\nмакет виводить змінну `$content`, яка містить результат формування вкладених представлень та \"вштовхується\"\nу макет під час виклику методу [[yii\\base\\Controller::render()]].\n\nВ більшості макетів наступні методи будуть викликатись як показано у вищенаведеному коді. Ці методи здебільшого породжують події,\nпов’язані з процесом формування, щоб скрипти та теги, які зареєстровані в інших місцях могли бути правильно включенні в\nмісцях, де ці методи викликаються.\n\n- [[yii\\base\\View::beginPage()|beginPage()]]: Цей метод повинен викликатись на самому початку макету.\n  Він породжує подію [[yii\\base\\View::EVENT_BEGIN_PAGE|EVENT_BEGIN_PAGE]], яка слугує ознакою початку сторінки.\n- [[yii\\base\\View::endPage()|endPage()]]: Цей метод повинен викликатись в кінці макету.\n  Він породжує подію [[yii\\base\\View::EVENT_END_PAGE|EVENT_END_PAGE]], яка слугує ознакою кінця сторінки.\n- [[yii\\web\\View::head()|head()]]: Цей метод повинен викликатись всередині секції `<head>` HTML-сторінки.\n  Він генерує заповнювач, який буде замінено зареєстрованим HTML-кодом (наприклад, теги link і meta),\n  коли сторінка буде повністю сформована.\n- [[yii\\web\\View::beginBody()|beginBody()]]: Цей метод повинен викликатись на початку секції `<body>`.\n  Він породжує подію [[yii\\web\\View::EVENT_BEGIN_BODY|EVENT_BEGIN_BODY]] та генерує заповнювач, який\n  буде замінено зареєстрованим HTML-кодом (наприклад, JavaScript) на початку секції `<body>`.\n- [[yii\\web\\View::endBody()|endBody()]]: Цей метод повинен викликатись у кінці секції `<body>`.\n  Він породжує подію [[yii\\web\\View::EVENT_END_BODY|EVENT_END_BODY]] та генерує заповнювач, який\n  буде замінено зареєстрованим HTML-кодом (наприклад, JavaScript) у кінці секції `<body>`.\n\n\n### Доступ до даних у макетах <span id=\"accessing-data-in-layouts\"></span>\n\nВсередині макету ви маєте доступ до двох попередньо визначених змінних: `$this` та `$content`. Перша посилається на\nкомпонент [[yii\\base\\View|представлення]], як і у звичайних представленнях, в той час як друга містить результат формування вкладеного\nпредставлення, який формується викликом методу [[yii\\base\\Controller::render()|render()]] у контролерах.\n\nЯкщо ви бажаєте мати доступ до інших даних у макетах, то вам потрібно використовувати підхід \"витягування\", описаний в\nпідрозділі [Доступ до даних у представленнях](#accessing-data-in-views). Якщо ви бажаєте отримати дані з вкладеного представлення\nу макеті, то можете використати підхід описаний у підрозділі [Обмін даними між представленнями](#sharing-data-among-views).\n\n\n### Використання макетів <span id=\"using-layouts\"></span>\n\nЯк описано в підрозділі [Формування в контролерах](#rendering-in-controllers), під час формування представлення\nчерез виклик методу [[yii\\base\\Controller::render()|render()]] у контролері, буде застосовний макет\nдо результату формування. За замовчуванням, буде використовуватись макет `@app/views/layouts/main.php`.\n\nВи можете використовувати інший макет сконфігурувавши властивість [[yii\\base\\Application::layout]] або [[yii\\base\\Controller::layout]].\nПерша відповідає за макет, який використовується усіма контролерами, друга ж перекриває першу для окремих контролерів.\nНаприклад, наступний код налаштовує контролер `post` на використання макету `@app/views/layouts/post.php`\nпід час формування його представлень. Інші контролери, за умови, що їх властивість `layout` не встановлена, будуть надалі\nвикористовувати типовий макет `@app/views/layouts/main.php`.\n \n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass PostController extends Controller\n{\n    public $layout = 'post';\n    \n    // ...\n}\n```\n\nДля контролерів, що належать модулю, ви можете також сконфігурувати властивість модуля [[yii\\base\\Module::layout|layout]], щоб\nвикористовувати окремий макет для цих контролерів.\n\nОскільки властивість `layout` може бути сконфігурована на різних рівнях (контролери, моделі, додаток),\n\"за лаштунками\" Yii виконує два кроки, щоб визначити, який файл макету є актуальним для окремого контролера.\n\nНа першому кроці, визначається значення `layout` та контекстний модуль:\n\n- Якщо властивість контролера [[yii\\base\\Controller::layout]] відмінна від `null`, використовується вона і\n  [[yii\\base\\Controller::module|модуль]] контролера як контекстний модуль.\n- Якщо [[yii\\base\\Controller::layout|layout]] не визначено (дорівнює `null`), здійснюється пошук у всіх батьківських модулях (включаючи сам додаток) контролера та\n  знаходиться перший модуль, властивість [[yii\\base\\Module::layout|layout]] якого не дорівнює `null`. Використовується значення цього модуля\n  [[yii\\base\\Module::layout|layout]] та сам модуль як контекстний модуль.\n  Якщо такого модуля не знайдено, це означає, що макет не буде застосовано.\n  \nНа другому кроці, визначається актуальний файл макету відповідно до значення `layout` та контекстного модулю,\nвизначених на першому кроці. Значенням `layout` може бути:\n\n- псевдонім шляху (наприклад, `@app/views/layouts/main`);\n- абсолютний шлях (наприклад, `/main`): значення `layout` починається зі слешу. Актуальний файл макету буде\n  шукатись під [[yii\\base\\Application::layoutPath|директорією макетів]] додатка, яка типово дорівнює\n  `@app/views/layouts`;\n- відносний шлях (наприклад, `main`): актуальний файл макету буде шукатись під\n  [[yii\\base\\Module::layoutPath|директорією макетів]] контекстного модуля, яка типово є директорією `views/layouts` під\n  [[yii\\base\\Module::basePath|директорією модуля]];\n- логічне значення `false`: макет не буде застосовано.\n\nЯкщо значення `layout` не містить розширення файлу, то буде використане типове розширення `.php`.\n\n\n### Вкладені макети <span id=\"nested-layouts\"></span>\n\nІноді потрібно вкласти один макет в інший. Наприклад, у різних розділах веб-сайту ви\nзахочете використовувати різні макети, які мають однаковий базовий макет, що генерує загальну\nструктуру HTML5-сторінки. Це можна зробити за допомогою викликів [[yii\\base\\View::beginContent()|beginContent()]] та\n[[yii\\base\\View::endContent()|endContent()]] в дочірніх макетах як наведено нижче:\n\n```php\n<?php $this->beginContent('@app/views/layouts/base.php'); ?>\n\n...вміст дочірнього макету...\n\n<?php $this->endContent(); ?>\n```\n\nЯк показано вище, вміст дочірнього макету повинен бути замкнений між [[yii\\base\\View::beginContent()|beginContent()]] та\n[[yii\\base\\View::endContent()|endContent()]]. Параметр, який передається в [[yii\\base\\View::beginContent()|beginContent()]]\nвизначає батьківський макет. Цей може бути файл макету чи псевдонім шляху.\n\nВикористовуючи цей підхід, можна вкладати макети більше, ніж на один рівень.\n\n\n### Використання блоків <span id=\"using-blocks\"></span>\n\nБлоки дозволяють визначати вміст представлення в одному місці, а відображати в іншому. Вони часто використовуються разом\nз макетами. Наприклад, ви можете визначити блок у вкладеному представленні та відобразити його у макеті.\n\nВиклики [[yii\\base\\View::beginBlock()|beginBlock()]] та [[yii\\base\\View::endBlock()|endBlock()]] визначають блок.\nПотім блок може бути доступним через `$view->blocks[$blockID]`, де `$blockID` означає унікальний ідентифікатор, який ви призначаєте\nблоку під час його визначення.\n\nНижченаведений приклад показує як ви можете використовувати блоки у вкладеному представленні для налаштовування окремих частин макету.\n\nСпершу, у вкладеному представленні, визначається один чи більше блоків:\n\n```php\n...\n\n<?php $this->beginBlock('block1'); ?>\n\n...вміст блоку 1...\n\n<?php $this->endBlock(); ?>\n\n...\n\n<?php $this->beginBlock('block3'); ?>\n\n...вміст блоку 3...\n\n<?php $this->endBlock(); ?>\n```\n\nПотім, у макеті, формуються блоки, якщо вони присутні, або відображається деякий типовий вміст, якщо блок\nне визначено.\n\n```php\n...\n<?php if (isset($this->blocks['block1'])): ?>\n    <?= $this->blocks['block1'] ?>\n<?php else: ?>\n    ... типовий вміст для блоку 1 ...\n<?php endif; ?>\n\n...\n\n<?php if (isset($this->blocks['block2'])): ?>\n    <?= $this->blocks['block2'] ?>\n<?php else: ?>\n    ... типовий вміст для блоку 2 ...\n<?php endif; ?>\n\n...\n\n<?php if (isset($this->blocks['block3'])): ?>\n    <?= $this->blocks['block3'] ?>\n<?php else: ?>\n    ... типовий вміст для блоку 3 ...\n<?php endif; ?>\n...\n```\n\n\n## Використання компонентів представлення <span id=\"using-view-components\"></span>\n\n[[yii\\base\\View|Компоненти представлення]] надають багато можливостей, пов’язаних із представленням. Ви можете отримувати компоненти представлення\nза допомогою створення індивідуальних екземплярів [[yii\\base\\View]] або його дочірніх класів, але у більшості випадків ви переважно будете використовувати\nкомпонент `view` додатку. Ви можете сконфігурувати цей компонент у [конфігураціях додатку](structure-applications.md#application-configurations)\nяк наведено нижче:\n\n```php\n[\n    // ...\n    'components' => [\n        'view' => [\n            'class' => 'app\\components\\View',\n        ],\n        // ...\n    ],\n]\n```\n\nКомпоненти представлення надають наступні корисні можливості, пов’язані з представленням, які описані більш детально в окремих розділах:\n\n* [темізація](output-theming.md): дозволяє проектувати та змінювати тему для веб-сайту;\n* [кешування фрагментів](caching-fragment.md): дозволяє кешувати фрагменти веб-сторінки;\n* [опрацювання клієнтських скриптів](output-client-scripts.md): підтримує реєстрацію та формування CSS та JavaScript;\n* [опрацювання колекції ресурсів](structure-assets.md): підтримує реєстрацію та формування [колекцій ресурсів](structure-assets.md);\n* [альтернативні шаблонізатори](tutorial-template-engines.md): дозволяє використовувати інші шаблонізатори, такі як\n  [Twig](https://twig.symfony.com/), [Smarty](https://www.smarty.net/).\n\nВи також можете часто використовувати наступні другорядні, але корисні, можливості в процесі розробки веб-сторінок.\n\n\n### Встановлення заголовків сторінок <span id=\"setting-page-titles\"></span>\n\nКожна веб-сторінка повинна мати заголовок. Звичайно тег заголовку виводиться в [макеті](#layouts). Однак, на практиці\nзаголовок часто визначається у вкладених представленнях, а не в макетах. Для вирішення цієї проблеми, компонент [[yii\\web\\View]] надає\nвластивість [[yii\\web\\View::title|title]], за допомогою якої можна передавати з вкладеного представлення до макетів інформацію заголовку.\n\nДля використання цієї можливості, в кожному вкладеному представленні ви можете задати заголовок як наведено нижче:\n\n```php\n<?php\n$this->title = 'Мій заголовок сторінки';\n?>\n```\n\nПотім переконайтесь, що маєте наступний код в секції `<head>` у макеті:\n\n```php\n<title><?= Html::encode($this->title) ?></title>\n```\n\n\n### Реєстрація мета-тегів <span id=\"registering-meta-tags\"></span>\n\nДля веб-сторінок зазвичай потрібно генерувати різноманітні мета-теги, які мають різне цільове призначення. Подібно до заголовків сторінок, мета-теги\nфігурують в секції `<head>` та зазвичай генеруються в макетах.\n\nЯкщо ви бажаєте визначити, які мета-теги генерувати у вкладених представленнях, ви можете викликати [[yii\\web\\View::registerMetaTag()]]\nу вкладеному представленні, подібно до наведеного:\n\n```php\n<?php\n$this->registerMetaTag(['name' => 'keywords', 'content' => 'yii, framework, php']);\n?>\n```\n\nВ коді вище реєструється мета-тег \"keywords\" у компоненті представлення. Зареєстрований мета-тег\nформується після закінчення формування макету. Наступний HTML-код буде згенеровано та вставлено\nв місці, де ви викличете [[yii\\web\\View::head()]] у макеті:\n\n```php\n<meta name=\"keywords\" content=\"yii, framework, php\">\n```\n\nЗауважте, якщо викликати [[yii\\web\\View::registerMetaTag()]] декілька разів, в результаті цього зареєструється кілька мета-тегів,\nне зважаючи на те, чи мета-теги однакові чи ні.\n\nЩоб мати лише один екземпляр специфічного типу мета-тегу, потрібно визначати ключ в другому параметрі під час виклику методу.\nНаприклад, наступний код реєструє два мета-теги \"description\". Однак, лише другий буде сформовано.\n\n```html\n$this->registerMetaTag(['name' => 'description', 'content' => 'Це мій класний веб-сайт, який створено за допомогою Yii!'], 'description');\n$this->registerMetaTag(['name' => 'description', 'content' => 'Цей веб-сайт про смішних єнотів.'], 'description');\n```\n\n\n### Реєстрація тегів link <span id=\"registering-link-tags\"></span>\n\nТак само як і [мета-теги](#registering-meta-tags), теги link є корисними у багатьох випадках, як, наприклад, налаштування favicon, посилання на\nстрічку новин (RSS) або делегування OpenID іншому серверу. Ви можете працювати з тегами link подібним шляхом як і з мета-тегами,\nвикористовуючи [[yii\\web\\View::registerLinkTag()]]. Наприклад, у вкладеному представленні, ви можете зареєструвати тег link наступним чином,\n\n```php\n$this->registerLinkTag([\n    'title' => 'Свіжі новини про Yii',\n    'rel' => 'alternate',\n    'type' => 'application/rss+xml',\n    'href' => 'https://www.yiiframework.com/rss.xml/',\n]);\n```\n\nРезультатом вищенаведеного коду буде\n\n```html\n<link title=\"Свіжі новини про Yii\" rel=\"alternate\" type=\"application/rss+xml\" href=\"https://www.yiiframework.com/rss.xml/\">\n```\n\nПодібно до [[yii\\web\\View::registerMetaTag()|registerMetaTags()]], ви можете визначати ключ під час виклику\n[[yii\\web\\View::registerLinkTag()|registerLinkTag()]] для запобігання генерування повторних тегів link одного типу.\n\n\n## Події у представленнях <span id=\"view-events\"></span>\n\n[[yii\\base\\View|Компоненти представлення]] породжують кілька подій в процесі формування представлення. Ви можете призначити обробники\nна ці події для вставлення вмісту в представлення або для опрацювання сформованих результатів перед їх відправленням кінцевим користувачам.\n\n- Подія [[yii\\base\\View::EVENT_BEFORE_RENDER|EVENT_BEFORE_RENDER]]: породжується на початку формування файлу\n  в контролері. Обробники цієї події можуть встановлювати значення властивості [[yii\\base\\ViewEvent::isValid]] рівним `false` для скасування процесу формування.\n- Подія [[yii\\base\\View::EVENT_AFTER_RENDER|EVENT_AFTER_RENDER]]: породжується після формування файлу викликом [[yii\\base\\View::afterRender()]].\n  Обробники цієї події можуть отримати результат формування через властивість [[yii\\base\\ViewEvent::output]] та можуть модифікувати\n  її для зміни результату формування.\n- Подія [[yii\\base\\View::EVENT_BEGIN_PAGE|EVENT_BEGIN_PAGE]]: породжується викликом [[yii\\base\\View::beginPage()]] у макетах.\n- Подія [[yii\\base\\View::EVENT_END_PAGE|EVENT_END_PAGE]]: породжується викликом [[yii\\base\\View::endPage()]] у макетах.\n- Подія [[yii\\web\\View::EVENT_BEGIN_BODY|EVENT_BEGIN_BODY]]: породжується викликом [[yii\\web\\View::beginBody()]] у макетах.\n- Подія [[yii\\web\\View::EVENT_END_BODY|EVENT_END_BODY]]: породжується викликом [[yii\\web\\View::endBody()]] у макетах.\n\nНаприклад, наступний код вставить поточну дату в кінці секції `<body>` сторінки:\n\n```php\n\\Yii::$app->view->on(View::EVENT_END_BODY, function () {\n    echo date('Y-m-d');\n});\n```\n\n\n## Формування статичних сторінок <span id=\"rendering-static-pages\"></span>\n\nДо статичних сторінок відносяться ті веб-сторінки, в яких основний вміст здебільшого статичний та не потребує доступу\nдо динамічних даних, що передаються з контролерів.\n\nМожна виводити статичні сторінки, розмістивши їх код у представленні, а потім використовуючи код подібний до наведеного у контролері:\n\n```php\npublic function actionAbout()\n{\n    return $this->render('about');\n}\n```\n\nЯкщо веб-сайт містить багато статичних сторінок, було б дуже стомливим повторювати схожий код багато разів.\nДля вирішення цієї проблеми можна впровадити [автономну дію](structure-controllers.md#standalone-actions)\n[[yii\\web\\ViewAction]] у контролері. Наприклад,\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actions()\n    {\n        return [\n            'page' => [\n                'class' => 'yii\\web\\ViewAction',\n            ],\n        ];\n    }\n}\n```\n\nТепер, якщо створити представлення з ім’ям `about` у директорії `@app/views/site/pages`, стане можливим\nвідображення цього представлення за наступною URL-адресою:\n\n```\nhttp://localhost/index.php?r=site%2Fpage&view=about\n```\n\nПараметр `view` із запиту `GET` вказує автономній дії [[yii\\web\\ViewAction]], яке представлення запитане. Потім дія здійснює пошук\nцього представлення в директорії `@app/views/site/pages`. Ви можете сконфігурувати [[yii\\web\\ViewAction::viewPrefix]],\nщоб змінити директорію для пошуку цих представлень.\n\n\n## Кращі практики <span id=\"best-practices\"></span>\n\nПредставлення відповідають за репрезентацію моделей у форматі зрозумілому кінцевим користувачам. В цілому, представлення\n\n* повинні здебільшого містити презентаційний код, такий як HTML та простий PHP-код для подання, форматування та формування даних;\n* не повинні містити код, який виконує запити до БД. Такий код повинен бути в моделях;\n* повинні уникати безпосереднього доступу до даних запиту, таких як `$_GET`, `$_POST`. Це обов’язок контролерів.\n  У випадку необхідності дані запиту повинні бути передані в представлення через контролери;\n* можуть читати властивості моделі, але не повинні змінювати їх.\n\nЩоб зробити представлення більш контрольованими, уникайте створення представлень, які є дуже складними або містять забагато надлишкового коду.\nВикористовуйте наступні техніки, щоб досягнути цього:\n\n* використовуйте [макети](#layouts) для відображення спільних презентаційних секцій (наприклад, шапка та футер сторінки);\n* розділяйте складне представлення на декілька менших. Менші представлення можуть бути сформовані та складені у більше\n  за допомогою методів формування описаних раніше у цьому розділі;\n* створюйте та використовуйте [віджети](structure-widgets.md) як будівельні блоки представлень;\n* створюйте та використовуйте класи-хелпери для перетворення та форматування даних у представленнях.\n\n"
  },
  {
    "path": "docs/guide-uk/tutorial-console.md",
    "content": "Консольні додатки\n=================\n\nОкрім багатьох можливостей для побудови веб-додатків, Yii також має повноцінну підтримку консольних додатків,\nякі в основному використовуються для реалізації фонових або супроводжувальних задач, які необхідно виконати для веб-сайту.\n\nСтруктура консольного додатка дуже подібна до структури веб-додатка Yii. Він складається з одного\nабо декількох класів [[yii\\console\\Controller]], які у середовищі консолі часто називають \"командами\".\nКожний контролер може також мати одну або декілька дій, так само як і веб-контролери.\n\nОбидва шаблони проекту вже мають консольний додаток у комплекті.\nВи можете запустити його, викликавши скрипт `yii`, який розміщений у базовій директорії додатка.\nЦе дасть вам перелік доступних команд, коли ви запускаєте його без будь-яких додаткових параметрів:\n\n![Виконання команди ./yii для відображення довідки](images/tutorial-console-help.png)\n\nЯк видно на знімку екрану, Yii має вже визначений набір команд, які є доступними \"з коробки\":\n\n- [[yii\\console\\controllers\\AssetController|AssetController]] - Дозволяє вам комбінувати та стискати ваші файли JavaScript і CSS.\n  Ви можете дізнатись більше про цю команду у розділі [Ресурси](structure-assets.md#using-the-asset-command).\n- [[yii\\console\\controllers\\CacheController|CacheController]] - Дозволяє вам оновити кеш додатка.\n- [[yii\\console\\controllers\\FixtureController|FixtureController]] - Керує завантаженням та вивантаженням даних фікстур для цілей тестування.\n  Ця команда описана більш детально у [розділі тестування про фікстури](test-fixtures.md#managing-fixtures).\n- [[yii\\console\\controllers\\HelpController|HelpController]] - Надає довідкову інформацію про консольні команди, ця команда використовується за замовчуванням\n  та виводить те, що ви побачили у вищенаведеному виводі.\n- [[yii\\console\\controllers\\MessageController|MessageController]] - Здобуває повідомлення для перекладу з файлів коду.\n  Щоб дізнатись більше про цю команду, будь ласка, зверніться до розділу [Інтернаціоналізація](tutorial-i18n.md#message-command).\n- [[yii\\console\\controllers\\MigrateController|MigrateController]] - Управляє міграціями додатка.\n  Міграції баз даних описані більш детально у розділі про [міграції баз даних](db-migrations.md).\n\n\nВикористання <span id=\"usage\"></span>\n------------\n\nДля виконання дії консольного контролера використовуйте наступний синтаксис:\n\n```\nyii <route> [--option1=value1 --option2=value2 ... argument1 argument2 ...]\n```\n\nУ вищенаведеному прикладі, `<route>` означає маршрут до дії контролера. Опції будуть заповнювати\nвластивості класу, а аргументи є параметрами для методу дії.\n\nНаприклад, дія [[yii\\console\\controllers\\MigrateController::actionUp()|MigrateController::actionUp()]]\nз властивістю [[yii\\console\\controllers\\MigrateController::$migrationTable|MigrateController::$migrationTable]], що має значення `migrations`,\nта обмеженням у 5 міграцій може бути викликана так:\n\n```\nyii migrate/up 5 --migrationTable=migrations\n```\n\n> Note: Коли використовуєте символ `*` в консолі, не забувайте замикати його в лапки, як `\"*\"`, для запобігання його інтерпретування як\n> спецсимволу shell-оболонки, який заміщується іменами файлів поточної директорії.\n\n\nВхідний скрипт <span id=\"entry-script\"></span>\n--------------\n\nВхідний скрипт консольного додатку є еквівалентним до файлу початкового завантаження `index.php` для веб-додатків.\nКонсольний вхідний скрипт, як правило, називається `yii`, та розміщений у базовій директорії вашого додатку.\nВін містить код подібний до наступного:\n\n```php\n#!/usr/bin/env php\n<?php\n/**\n * Yii console bootstrap file.\n */\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\n\nrequire __DIR__ . '/vendor/autoload.php';\nrequire __DIR__ . '/vendor/yiisoft/yii2/Yii.php';\n\n$config = require __DIR__ . '/config/console.php';\n\n$application = new yii\\console\\Application($config);\n$exitCode = $application->run();\nexit($exitCode);\n```\n\nЦей скрипт буде створено як частину вашого додатку; ви можете вільно змінювати його для відповідності вашим потребам. Значення константи `YII_DEBUG` може бути встановлено у `false`,\nякщо ви не хочете бачити стек трасування при помилці, і/або якщо ви хочете підвищити загальну швидкодію. І в базовому, і в розширеному шаблонах\nпроекту у вхідному скрипті консольного додатку за замовчуванням включено режим налагодження для забезпечення більш дружнього до розробника середовища.\n\nКонфігурація <span id=\"configuration\"></span>\n------------\n\nЯк видно у вищенаведеному коді, консольний додаток використовує свій власний файл конфігурації з іменем `console.php`. У цьому файлі\nви можете налаштовувати різні [компоненти додатка](structure-application-components.md) та властивості для консольного додатка зокрема.\n\nЯкщо ваш веб-додаток та консольний додаток мають багато спільних параметрів конфігурації з однаковими значеннями, ви можете відокремити\nспільні частини в окремий файл та включити цей файл до обох конфігурацій додатків (веб- та консольного).\nВи можете побачити приклад цього у \"розширеному\" шаблоні проекту.\n\n> Tip: Іноді, існує необхідність виконувати консольну команду, використовуючи конфігурацію додатка, яка\nвідрізняється від заданої у вхідному скрипті. Наприклад, якщо ви хочете використати команду `yii migrate` для\nоновлення ваших тестових баз даних, які налаштовані кожна в окремому наборі тестів. Для динамічної зміни\nконфігурації просто вкажіть потрібний файл конфігурації додатка\nза допомогою опції `appconfig`, коли виконуєте команду:\n\n```\nyii <route> --appconfig=path/to/config.php ...\n```\n\nСтворення власних консольних команд <span id=\"create-command\"></span>\n-----------------------------------\n\n### Консольні контролер та дія\n\nКонсольна команда визначена класом контролера успадкованого від [[yii\\console\\Controller]]. У класі контролера\nвизначаються одна або більше дій, які відповідають під-командам контролера. Всередині кожної дії міститься код, який реалізує відповідні завдання для окремої під-команди.\n\nПри виконанні команди необхідно вказати маршрут до дії контролера. Наприклад,\nмаршрут `migrate/create` викликає під-команду, яка відповідає методу дії\n[[yii\\console\\controllers\\MigrateController::actionCreate()|MigrateController::actionCreate()]].\nЯкщо маршрут, запропонований при виконанні, не містить ідентифікатора дії, то буде виконана стандартна дія (так само як у веб-контролері).\n\n### Опції\n\nЧерез перевизначення методу [[yii\\console\\Controller::options()]] ви можете визначити опції, які будуть доступними\nдля консольної команди (controller/actionID). Метод повинен повертати перелік публічних властивостей класу контролера.\nПри виконанні команди можна задати значення опції, використовуючи синтаксис `--OptionName=OptionValue`.\nЦе призначить значення `OptionValue` властивості `OptionName` класу контролера.\n\nЯкщо значення за замовчуванням для опції є масивом і ви задаєте цю опцію під час виконання команди,\nто значення опції буде перетворене у масив розділенням вхідного текстового рядка за комами.\n\n### Псевдоніми опцій\n\nПочинаючи із версії 2.0.8, консольна команда надає метод [[yii\\console\\Controller::optionAliases()]] \nдля створення псевдонімів для опцій.\n\nЩоб визначити псевдонім, потрібно перевизначити метод [[yii\\console\\Controller::optionAliases()]]\nу вашому контролері, наприклад:\n\n```php\nnamespace app\\commands;\n\nuse yii\\console\\Controller;\n\nclass HelloController extends Controller\n{\n    public $message;\n    \n    public function options($actionID)\n    {\n        return ['message'];\n    }\n    \n    public function optionAliases()\n    {\n        return ['m' => 'message'];\n    }\n    \n    public function actionIndex()\n    {\n        echo $message . \"\\n\";\n    }\n}\n```\n\nТепер ви зможете використовувати наступний синтакс для запуску команди:\n\n```\n./yii hello -m=hola\n```\n\n### Аргументи\n\nОкрім опцій, команда також може приймати аргументи. Аргументи будуть передані як параметри до методу дії\nвідповідно до запитуваної під-команди. Перший аргумент відповідає першому параметру, другий\nвідповідає другому і т. д. Якщо при виклику команди надано не достатньо аргументів, то відповідним параметрам\nбудуть призначені типові значення, якщо попередньо визначені. Якщо типове значення не визначено і не передано значення під час виконання, то команда буде завершена з помилкою.\n\nВи можете використовувати вказівку типу `array` для позначення аргументу, з яким потрібно обходитись як з масивом.\nМасив буде згенерований розділенням вхідного текстового рядку за комами.\n\nНаступний приклад показує як оголошувати аргументи:\n\n```php\nclass ExampleController extends \\yii\\console\\Controller\n{\n    // Команда \"yii example/create test\" викличе \"actionCreate('test')\"\n    public function actionCreate($name) { ... }\n\n    // Команда \"yii example/index city\" викличе \"actionIndex('city', 'name')\"\n    // Команда \"yii example/index city id\" викличе \"actionIndex('city', 'id')\"\n    public function actionIndex($category, $order = 'name') { ... }\n\n    // Команда \"yii example/add test\" викличе \"actionAdd(['test'])\"\n    // Команда \"yii example/add test1,test2\" викличе \"actionAdd(['test1', 'test2'])\"\n    public function actionAdd(array $name) { ... }\n}\n```\n\n\n### Код виходу\n\nВикористання кодів виходу є найкращою практикою для розробки консольного додатку. Прийнято, якщо команда повертає `0`, це означає, що\nвсе добре. Якщо команда повертає число більше за нуль, це вважається показником помилки. Повернуте число буде кодом\nпомилки, яке потенційно може використовуватись для пошуку деталей про помилку.\nНаприклад, число `1`, як правило, може означати невідому помилку, а усі коди вище можуть бути зарезервовані для специфічних випадків: помилки вводу, відсутні файли і так далі.\n\nДля того, щоб ваша консольна команда повертала код виходу, просто поверніть ціле число з методу дії\nконтролера:\n\n```php\npublic function actionIndex()\n{\n    if (/* деяка проблема */) {\n        echo \"A problem occurred!\\n\";\n        return 1;\n    }\n    // щось виконується\n    return 0;\n}\n```\n\nЄ декілька попередньо визначених констант, які ви можете використовувати:\n\n- `Controller::EXIT_CODE_NORMAL` зі значенням `0`;\n- `Controller::EXIT_CODE_ERROR` зі значенням `1`.\n\nХорошою практикою є визначення значущих констант для вашого контролера у випадку, якщо ви маєте більше типів помилок.\n\n### Форматування та кольори\n\nКонсоль Yii підтримує форматований вивід, який автоматично стане не форматованим, якщо він не підтримується\nтерміналом, в якому виконується команда.\n\nВиводити форматовані текстові рядки просто. Ось як вивести деякий жирний текст:\n\n```php\n$this->stdout(\"Hello?\\n\", Console::BOLD);\n```\n\nЯкщо необхідно побудувати текстовий рядок, динамічно комбінуючи декілька стилів, то найкраще використовувати `ansiFormat`:\n\n```php\n$name = $this->ansiFormat('Alex', Console::FG_YELLOW);\necho \"Hello, my name is $name.\";\n```\n"
  },
  {
    "path": "docs/guide-uk/tutorial-start-from-scratch.md",
    "content": "Створення своєї власної структури додатку\n=========================================\n\n> Note: Цей розділ знаходиться в розробці.\n\nУ той час, як [базовий](https://github.com/yiisoft/yii2-app-basic) і [розширений](https://github.com/yiisoft/yii2-app-advanced)\nшаблони додатків прекрасно підходять для більшості ваших потреб, ви також можете сворити свій власний шаблон додатку, з яким\nпрацюватиме ваш проект.\n\nШаблони додатку в Yii це простий репозиторій, що міститься у файлі `composer.json` і зареєстрований у якості пакету Composer.\nБудь-який репозиторій може бути ідентифікований як Composer пакет, що робить можливим його встановлення\nза допомогою команди Composer `create-project`.\n\nОскільки це потребує дещо більше початкової роботи для створення власного шаблону з нуля, краще використовувати один із \nвбудованих шаблонів в якості основи. Давайте використаємо тут базовий шаблон.\n\nКлонування базового шаблону\n---------------------------\n\nПершим кроком буде клонування базового шаблону Yii з Git репозиторія:\n\n```bash\ngit clone git@github.com:yiisoft/yii2-app-basic.git\n```\n\nТоді, почекайте поки репозиторій завантажеться на ваш комп’ютер. Щоб зміни, які внесені в шаблон не були перезаписані, \nвам необхідно видалити каталог `.git` і весь її зміст після завантаження.\n\nЗаміна файлів\n-------------\n\nДалі вам потрібно змінити файл `composer.json`, щоб показати ваш шаблон.\nЗмініть значення `name`, `description`, `keywords`, `homepage`, `license` і `support`, щоб описати ваш новий шаблон.\nТакож налаштуйте `require`, `require-dev`, `suggest` та інші параметри відповідно до вимог вашого шаблону.\n\n> Note: В файлі `composer.json` використовуйте параметр `writable` в розділі `extra`, щоб\n> вказати права доступу до файлів, які необхідно встановити після створення додатку з використанням вашого шаблону.\n\nДалі внесіть зміни у структуру та зміст додатку на той, який ви би хотіли бачити за замовчуванням. \nВ кінці, оновіть файл інструкції README для застосування вашого шаблону.\n\nСтворення пакету\n----------------\n\nЗ визначенням шаблону, створіть Git репозиторій та завантажте туди свої файли. Якщо ви, збираєтесь використовувати свій шаблон,\nяк *open source*, то [Github](https://github.com) є кращим місцем для його розташування.\nЯкщо ви не бажаєте публічно розміщувати свій шаблон, то підійде будь-який сайт сервісу Git.\n\nДалі, вам необхідно зареєструвати свій Composer пакет. Для публічних шаблонів, ваш пакет необхідно зареєструвати \nв [Packagist](https://packagist.org/). Для приватних шаблонів, зареєструвати пакет трішки складніше.\nДля цього слідуйте інструкціями у [Документації Composer](https://getcomposer.org/doc/05-repositories.md#hosting-your-own).\n\nВикористання шаблону\n--------------------\n\nЦе все, що потрібно для створення власного шаблону для Yii додатку.\nТепер ви можете створювати проекти, використовуючи свій шаблон:\n\n```\ncomposer global require \"fxp/composer-asset-plugin:^1.4.1\"\ncomposer create-project --prefer-dist --stability=dev mysoft/yii2-app-coolone new-project\n```\n"
  },
  {
    "path": "docs/guide-uk/tutorial-template-engines.md",
    "content": "Використання шаблонізаторів\n===========================\n\nЗа замовчуванням, Yii використовує PHP в якості мови шаблонів, але ви можете налаштувати Yii для підтримки інших шаблонізаторів,\nтаких як [Twig](https://twig.symfony.com/) або [Smarty](https://www.smarty.net/), які доступні в якості розширеннь.\n\nКомпонент `View` відповідає за рендиренг видів. Ви можете додати користувальницький шаблон, шляхом зміни конфігурації поведінки\nцього компонента:\n\n```php\n[\n    'components' => [\n        'view' => [\n            'class' => 'yii\\web\\View',\n            'renderers' => [\n                'tpl' => [\n                    'class' => 'yii\\smarty\\ViewRenderer',\n                    //'cachePath' => '@runtime/Smarty/cache',\n                ],\n                'twig' => [\n                    'class' => 'yii\\twig\\ViewRenderer',\n                    'cachePath' => '@runtime/Twig/cache',\n                    // Array of twig options:\n                    'options' => [\n                        'auto_reload' => true,\n                    ],\n                    'globals' => ['html' => '\\yii\\helpers\\Html'],\n                    'uses' => ['yii\\bootstrap'],\n                ],\n                // ...\n            ],\n        ],\n    ],\n]\n```\n\nУ наведеному вище коді, як Smarty так і Twig налаштовані так, щоб використовуватись файлами представлень.\nАле для того, щоб підключити ці розширення у ваш проект, вам також необхідно змінити файл `composer.json`, щоб включити їх:\n\n```\n\"yiisoft/yii2-smarty\": \"~2.0.0\",\n\"yiisoft/yii2-twig\": \"~2.0.0\",\n```\n\nЦей код потрібно додати у розділ `require` файлу `composer.json`. Після внесення цих змін і збереження файлу,\nви можете встановити розширення, виконавши команду `composer update --prefer-dist` через командний рядок.\n\nДля отримання детальної інформації про використання конкретного шаблонізатора, будь ласка, зверніться до його документації:\n\n- [Документація Twig](https://github.com/yiisoft/yii2-twig/tree/master/docs/guide)\n- [Документація Smarty](https://github.com/yiisoft/yii2-smarty/tree/master/docs/guide)\n"
  },
  {
    "path": "docs/guide-uk/tutorial-yii-integration.md",
    "content": "Робота із стороннім кодом\n=========================\n\nЧас від часу, вам, можливо, буде потрібно використовувати деякий сторонній код в своїх Yii додатках.\nАбо ви маєте потребу використовувати Yii, як бібліотеку в деяких сторонніх системах.\nУ цьому розділі, ми покажемо як досягти цих цілей.\n\n\nВикористання сторонніх бібліотек в Yii <span id=\"using-libs-in-yii\"></span>\n--------------------------------------\n\nДля використання сторонньої бібліотеки в Yii додатку, ви, в основному, повинні переконатися, що класи в бібліотеці\nправильно підключені або можуть завантажуватися автоматично.\n\n### Використання пакунків Composer <span id=\"using-composer-packages\"></span>\n\nБагато сторонніх бібліотек випущені у вигляді пакунків [Composer](https://getcomposer.org/).\nВи можете встановити такі бібліотеки, виконавши два прості кроки:\n\n1. змінити файл `composer.json` вашого додатку і вказати, які пакунки Composer необхідно встановити.\n2. виконати команду `composer install` для встановлення зазначених пакунків.\n\nКласи встановлених пакунків Composer можуть бути автоматично завантажені, використовуючи автозавантажувача Composer.\nПереконайтися, що [вхідний скрипт](structure-entry-scripts.md) вашого додатку містить наступні рядки для\nвстановлення автозавантажувача Composer:\n\n```php\n// встановлення автозавантажувача Composer\nrequire __DIR__ . '/../vendor/autoload.php';\n\n// підключення файлу класа Yii\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n```\n\n### Використовуйте завантажені бібліотеки <span id=\"using-downloaded-libs\"></span>\n\nЯкщо бібліотека не випущена в якості пакунку Composer, ви повинні слідувати згідно її інструкції по встановленню.\nУ більшості випадків, вам потрібно буде завантажити випущений файл вручну і розархівувати його в каталог `BasePath/vendor`,\nде `BasePath` представляє собою [основний шлях](structure-applications.md#basePath) вашого додатку.\n\nЯкщо бібліотека використовує свій власний автозавантажувач класів, ви можете підключити його у\n[вхідному скрипті](structure-entry-scripts.md) вашого додатку. Рекомендується підключити його перед тим, \nяк буде підключено файл `Yii.php`, щоб автозавантажувач Yii мав приорітет при автоматичному завантаженні класів.\n\nЯкщо бібліотека не надає автозавантажувача класів, але іменування її класів відповідає\n[PSR-4](https://www.php-fig.org/psr/psr-4/) - ви можете використовувати автозавантажувач Yii для завантаження її класів. \nВсе, що вам потрібно зробити, це тільки оголосити [системний псевдонім](concept-aliases.md#defining-aliases) \nдля кожного кореневого простору імен, що використовується в цих класах. Для прикаду, припустимо, що ви хочете встановити \nбібліотеку в каталог `vendor/foo/bar`, і класи бібліотеки знаходяться в кориневому просторі імен `xyz`.\nВи можете включити наступний код в конфігурації вашого додатку:\n\n```php\n[\n    'aliases' => [\n        '@xyz' => '@vendor/foo/bar',\n    ],\n]\n```\n\nЯкщо жоден з варіантів не підійшов, цілком ймовірно, що для використання бібліотеки потрібно налаштувати в конфігурації\nдирективу PHP `include_path` для коректного пошуку та підключення файлів класів. \nПросто дотримуйтесь її інструкції з налаштування директиви PHP `include_path`.\n\nВ гіршому випадку, бібліотека вимагає явного підключення каждого файлу класу, тому ви можете використати наступний метод\nдля підключення класів за вимогою:\n\n* Визначіть, які класи входять до складу бібліотеки.\n* Перерахуйте класи і шляхи до відповідних файлів в `Yii::$classMap` у [вхідному скрипті](structure-entry-scripts.md)\n  додатку. Наприклад,\n```php\nYii::$classMap['Class1'] = 'path/to/Class1.php';\nYii::$classMap['Class2'] = 'path/to/Class2.php';\n```\n\n\nВикористання Yii в сторонніх системах <span id=\"using-yii-in-others\"></span>\n-------------------------------------\n\nОскільки в Yii організовано безліч корисних функцій, іноді вони можуть знадобитися при розробці або розширенні сторонніх систем,\nтаких як WordPress, Joomla, або додатки, розроблені з використанням іншого PHP-фреймворку.\nНаприклад, ви можете використовувати клас [[yii\\helpers\\ArrayHelper]] або можливості [Active Record](db-active-record.md)\nв сторонніх системах. Для цього необхідно виконати два кроки: встановити Yii та bootstrap Yii.\n\nЯкщо стороння система використовує управління залежностями Composer, ви можете встановити Yii за допомогою наступних команд:\n\n```bash\n    composer global require \"fxp/composer-asset-plugin:^1.4.1\"\n    composer require yiisoft/yii2\n    composer install\n```\n\nПерша команда встановлює [composer asset plugin](https://github.com/fxpio/composer-asset-plugin),\nякий дозволяє керувати залежностями пакунків Bower і NPM через Composer. Навіть якщо ви захочете використовувати тільки\nпрошарки бази даних або інші, не повʼязані ресурсами, можливості Yii, вам все-одно необхідно встановити даний пакунок composer.\n\nЯкщо ви бажаєте використовувати [можливість публікації ресурсів Yii](structure-assets.md),\nвам також слід додати наступну конфігурацію до розділу `extra` вашого файлу `composer.json`:\n\n```json\n{\n    ...\n    \"extra\": {\n        \"asset-installer-paths\": {\n            \"npm-asset-library\": \"vendor/npm\",\n            \"bower-asset-library\": \"vendor/bower\"\n        }\n    }\n}\n```\n\nДивіться також загальний [розділ про встановлення Yii](start-installation.md#installing-via-composer), для отримання додаткової\nінформації про Composer та проблеми, які можуть виникнути під час встановлення.\n\nВ іншому випадку, ви можете [завантажити](https://www.yiiframework.com/download/) файли релізу Yii і розархівувати його\nв каталог `BasePath/vendor`.\n\nДалі вам необхідно змінити вхідний скрипт сторонньої системи помістивши на його початок наступний код:\n\n```php\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n\n$yiiConfig = require __DIR__ . '/../config/yii/web.php';\nnew yii\\web\\Application($yiiConfig); // НЕ ВИКЛИКАЙТЕ run() в цьому місці\n```\n\nЯк ви бачите, цей код дуже схожий на код [вхідного скрипта](structure-entry-scripts.md) типового додатку Yii.\nЄдина відмінність заключається в тому, що після створення екземпляру додатку, метод `run()` не викликається.\nЦе звʼязано з тим, що при виклику `run()`, Yii захоплює контроль над процесом обробки запиту, що в даному випадку\nне є потрібним, оскільки цю задачу виконує вже наявний додаток.\n\nЯк і у випадку з Yii додатком, вам необхідно налаштувати екземпляр додатку, виходячи із середовища запущеної сторонньої системи.\nНаприклад, щоб скористатися можливостями [Active Record](db-active-record.md), необхідно налаштувати\n[компонент додатку](structure-application-components.md) `db` з налаштування підключення бази данних,\nяка використовується сторонньою системою.\n\nТепер ви можете використовувати більшість функцій фреймворку Yii. Наприклад, ви можете створювати класи Active Record і\nвикористовувати їх для роботи з базами даних.\n\n\nВикористання Yii 2 з Yii 1 <span id=\"using-both-yii2-yii1\"></span>\n--------------------------\n\nЯкщо попередньо ви використовували Yii 1, цілком ймовірно, що у вас є робочий додаток Yii 1.\nЗамість того, щоб переписувати цілий додаток під Yii 2, ви можете просто його покращити, використовуючи деякі функції, \nщо доступні тільки в Yii 2. Для цього потрібно виконати наступні дії.\n\n> Note: Yii 2 вимагає версію PHP 5.4 або вищу. Переконайтися, що і сервер і наявний додаток підтримують її.\n\nПо-перше, встановіть Yii 2 до вашого поточного додатку, виконавши дії, описані в [попередньому підрозділі](#using-yii-in-others).\n\nПо-друге, змініть вхідний скрипт додатку наступним чином,\n\n```php\n// підключення модифікованого класу Yii, описаного нижче\nrequire __DIR__ . '/../components/Yii.php';\n\n// налаштування додатку Yii 2\n$yii2Config = require __DIR__ . '/../config/yii2/web.php';\nnew yii\\web\\Application($yii2Config); // не викликайте run()\n\n// налаштування додатку Yii 1\n$yii1Config = require __DIR__ . '/../config/yii1/main.php';\nYii::createWebApplication($yii1Config)->run();\n```\n\nОскільки Yii 1 та Yii 2 використовують клас `Yii`, вам необхідно створити модифіковану версію, щоб обʼєднати їх.\nНаведений нижче код підключить модифікований файл класу `Yii`, який може бути створений наступним чином.\n\n```php\n$yii2path = '/path/to/yii2';\nrequire $yii2path . '/BaseYii.php'; // Yii 2.x\n\n$yii1path = '/path/to/yii1';\nrequire $yii1path . '/YiiBase.php'; // Yii 1.x\n\nclass Yii extends \\yii\\BaseYii\n{\n    // скопіюйте та вставте код з YiiBase (1.x) сюди\n}\n\nYii::$classMap = include($yii2path . '/classes.php');\n// реєстрація автозавантажувача Yii 2 через Yii 1\nYii::registerAutoloader(['Yii', 'autoload']);\n// створення контейнера впровадження залежностей\nYii::$container = new yii\\di\\Container;\n```\n\nОт і все! Тепер в будь-якому місці коду можна використовувати конструкцію `Yii::$app` для отримання доступу до\nекземпляру класу додатку Yii 2, а з допомогою конструкції `Yii::app()` - до екземпляру класу додатку Yii 1:\n\n```php\necho get_class(Yii::app()); // виводить 'CWebApplication'\necho get_class(Yii::$app);  // виводить 'yii\\web\\Application'\n```\n"
  },
  {
    "path": "docs/guide-uz/README.md",
    "content": "Yii 2.0 bo'yicha to'liq qo'llanma\n=================================\n\nUshbu qo'llanma [Yii qo'llanmalarining holati](https://www.yiiframework.com/doc/terms/) bilan mos holda yo'lga qo'yildi.\n\nAll Rights Reserved.\n\n2014 © Yii Software LLC.\n\nKirish\n------\n\n* [Yii haqida](intro-yii.md)\n* [1.1 dan keyingi yangilanishlar](intro-upgrade-from-v1.md)\n\n\nBirinchi tanishuv\n-----------------\n\n* [Yii ni o'rnatish](start-installation.md)\n* [Ilovani ishga tushirish](start-workflow.md)\n* [«Salom» deymiz](start-hello.md)\n* [Formalar bilan ishlash](start-forms.md)\n* [Ma'lumotlar ombori bilan ishlash](start-databases.md)\n* [Gii yordamida kodlarni generatsiya qilish](start-gii.md)\n* [Keyin nima?](start-looking-ahead.md)\n\n\nIlova strukturasi\n-----------------\n\n* [Sharh](structure-overview.md)\n* [Kirish skriptlari](structure-entry-scripts.md)\n* [Ilova](structure-applications.md)\n* [Ilova komponentlari](structure-application-components.md)\n* [Kontrollyorlar](structure-controllers.md)\n* [Namoyish](structure-views.md)\n* [Modellar](structure-models.md)\n* **TBD** [Filtrlar](structure-filters.md)\n* **TBD** [Vidjetlar](structure-widgets.md)\n* **TBD** [Modullar](structure-modules.md)\n* [Resurslar](structure-assets.md)\n* **TBD** [Kengaytmalar](structure-extensions.md)\n\n\nSo'rovlarni qayta ishlash\n-------------------------\n\n* **TBD** [Bootstrapping](runtime-bootstrapping.md)\n* **TBD** [Routing](runtime-routing.md)\n* **TBD** [So'rovlar](runtime-requests.md)\n* **TBD** [Javoblar](runtime-responses.md)\n* **TBD** [Sessiyalar va kuklar](runtime-sessions-cookies.md)\n* [URL ni tahlil va generatsiya qilish](runtime-url-handling.md)\n* [Xatoliklarni qayta ishlash](runtime-handling-errors.md)\n* [Jurnallarga yozish](runtime-logging.md)\n\n\nAsosiy tushunchalar\n-------------------\n\n* [Komponentlar](concept-components.md)\n* [Xususiyat](concept-properties.md)\n* [Xodisa](concept-events.md)\n* [O'zini tutish](concept-behaviors.md)\n* [Muhim sozlashlar](concept-configurations.md)\n* [Taxalluslar](concept-aliases.md)\n* [Sinflarni avtoyuklash](concept-autoloading.md)\n* [Service Locator](concept-service-locator.md)\n* [Dependency Injection Container](concept-di-container.md)\n\n\nMa'lumotlar ombori bilan ishlash\n--------------------------------\n\n* [Ma'lumotlar bilan ishlashga imkon beruvchi obektlar(DAO)](db-dao.md) - Ma'lumotlar ombori bilan bog'lanish, oddiy so'rovlar, tranzaksiya va sxema bilan ishlash.\n* [So'rovlarni yaratuvchi](db-query-builder.md) - Ma'lumotlar omboriga abstraksiyaning oddiy qatlamidan so'rovlar.\n* [Active Record](db-active-record.md) - AR obektlarini  olish, ular bilan ishlash va bog'lanishlarni aniqlash.\n* [Migratsiyalar](db-migrations.md) - Komandada (jamoada) ishlaganda ma'lumotlar sxemasini talqinlarini boshqarish.\n* **TBD** [Sphinx](db-sphinx.md)\n* **TBD** [Redis](db-redis.md)\n* **TBD** [MongoDB](db-mongodb.md)\n* **TBD** [ElasticSearch](db-elastic-search.md)\n\n\nFoydalanuvchidan ma'lumotlarni qabul qilish\n-------------------------------------------\n\n* [Formani yaratish](input-forms.md)\n* [Validatsiya](input-validation.md)\n* **TBD** [Fayllarni yuklash](input-file-uploading.md)\n* **TBD** [Bir nechta modellar bilan ishlash](input-multiple-models.md)\n\n\nMa'lumotlarni namoyish etish\n----------------------------\n\n* **TBD** [Ma'lumotlarni formatlash](output-formatting.md)\n* **TBD** [Sahifalar bo'yicha ajratish](output-pagination.md)\n* **TBD** [Saralash](output-sorting.md)\n* [Ma'lumotlar provayderlari](output-data-providers.md)\n* [Ma'lumotlar uchun vidjetlar](output-data-widgets.md)\n* [Mavzulashtirish](output-theming.md)\n\n\nXavfsizlik\n----------\n\n* [Autentifikatsiya](security-authentication.md)\n* [Avtorizatsiya](security-authorization.md)\n* [Parollar bilan ishlash](security-passwords.md)\n* **TBD** [Avtorizatsiya mijozlari](security-auth-clients.md)\n* **TBD** [Eng yaxshi amaliyotlar](security-best-practices.md)\n\n\nKeshlash\n--------\n\n* [Sharh](caching-overview.md)\n* [Ma'lumotlarni keshlash](caching-data.md)\n* [Fragmentlarni keshlash](caching-fragment.md)\n* [Sahifalarni keshlash](caching-page.md)\n* [HTTP ni keshlash](caching-http.md)\n\n\nREST web-xizmatlari\n-------------------\n\n* [Tezkor boshlash](rest-quick-start.md)\n* [Resurslar](rest-resources.md)\n* [Kontrollyorlar](rest-controllers.md)\n* [Routing](rest-routing.md)\n* [Javoblarni formatlash](rest-response-formatting.md)\n* [Autentifikatsiya](rest-authentication.md)\n* [So'rovlarni chastotasini chegaralash](rest-rate-limiting.md)\n* [Talqin yaratish](rest-versioning.md)\n* [Xatoliklarni qayta ishlash](rest-error-handling.md)\n\n\nIshlab chiquvchining uskunalari\n---------------------------\n\n* [Qayta ishlash paneli va qayta ishlovchi](tool-debugger.md)\n* [Gii bilan kodni generatsiya qilish](tool-gii.md)\n* **TBD** [API qo'llanmani generatori](tool-api-doc.md)\n\n\nTest o'tkazish\n--------------\n\n* [Sharh](test-overview.md)\n* **TBD** [MOdulli testlar](test-unit.md)\n* **TBD** [Funksional testlar](test-functional.md)\n* **TBD** [Qabul qiluvchi testlar](test-acceptance.md)\n* [Fiksturalar](test-fixtures.md)\n\n\nYii kengaytmalari\n-----------------\n\n* [Kengaytmani yaratish](extend-creating-extensions.md)\n* [Freymvork kodini kengaytirish](extend-customizing-core.md)\n* [Tashqi kutubxonalarni qo'llash](extend-using-libs.md)\n* **TBD** [Tashqi tizimlarda Yii integratsiyasi](extend-embedding-in-others.md)\n* **TBD** [Yii 1.1 va 2.0 larni bir vaqtda ishlatish](extend-using-v1-v2.md)\n* [Composer ni ishlatish](extend-using-composer.md)\n\n\nMaxsus mavzular\n---------------\n\n* [advanced ilova shabloni](tutorial-advanced-app.md)\n* [Ilovani noldan yaratish](tutorial-start-from-scratch.md)\n* [Konsol komandalari](tutorial-console.md)\n* [Xalqarolashtirish](tutorial-i18n.md)\n* [Pochta yuborish](tutorial-mailing.md)\n* [Ish inumdorligini oshirish](tutorial-performance-tuning.md)\n* **TBD** [shared xostingida ishlash](tutorial-shared-hosting.md)\n* [Shablonlashtiruvchilar](tutorial-template-engines.md)\n\n\nVidjetlar\n---------\n\n* GridView: link to demo page\n* ListView: link to demo page\n* DetailView: link to demo page\n* ActiveForm: link to demo page\n* Pjax: link to demo page\n* Menu: link to demo page\n* LinkPager: link to demo page\n* LinkSorter: link to demo page\n* [Bootstrap vidjetlari](bootstrap-widgets.md)\n* **TBD** [Jquery UI vidjetlari](jui-widgets.md)\n\n\nXelperlar\n---------\n\n* [Sharh](helper-overview.md)\n* **TBD** [ArrayHelper](helper-array.md)\n* **TBD** [Html](helper-html.md)\n* **TBD** [Url](helper-url.md)\n* **TBD** [Security](helper-security.md)\n"
  },
  {
    "path": "docs/guide-uz/blocktypes.json",
    "content": "{\n  \"Info:\": \"Ma'lumot uchun:\"\n}\n"
  },
  {
    "path": "docs/guide-uz/caching-page.md",
    "content": "Sahifalarni keshlash\n=================\n\nSahifalarni keshlash — bu sahifadagi butun ma'lumotni server tomonida keshda saqlashga aytiladi. Keginchalik sahifani serverdan \ntalab qilsak bizga sahifadagi ma'lumotni keshdan qaytaradi. \n\nSahifalarni keshlash [harakat filtrlari](structure-filters.md) [[yii\\filters\\PageCache]] yordamida amalga oshiriladi va \nkontroller sinfida quyidagi shaklda ishlatilishi mumkun:\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\PageCache',\n            'only' => ['index'],\n            'duration' => 60,\n            'variations' => [\n                \\Yii::$app->language,\n            ],\n            'dependency' => [\n                'class' => 'yii\\caching\\DbDependency',\n                'sql' => 'SELECT COUNT(*) FROM post',\n            ],\n        ],\n    ];\n}\n```\n\nKeltirilgan kodda kesh faqat indeks harakati uchun amalga oshiriladi. Nazarda tutulgan sahifa  60 sekundga \nkesh qilinadi va dasturning joriy tiliga ko'ra o'zgarib turadi. Kesh qilingan sahifa muddati o'z o'zidan \no'zgaradi. Agarki shahrlarning umumiy soni o'zgarsa.\n\nSahifalarni keshlash [fragmentlarni keshlashga](caching-fragment.md) juda o'hshaydi. Ikki holatda ham `duration` `dependencies`\n`variations` va `enabled` parametrlari qo'llaniladi. Asosiy farqi shundaki sahifalarni keshlash [harakat filtri](structure-filters.md) shaklida \namalga oshiriladi. Fragmentlarni keshlash esa [vidjet shaklida](structure-widgets.md) amalga oshiriladi.\n"
  },
  {
    "path": "docs/guide-uz/images/application-structure.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.13-->\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d0\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key for=\"graphml\" id=\"d7\" yfiles.type=\"resources\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d0\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"872.1807999999999\" y=\"-14.764159999999947\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"31.9375\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"83.330078125\" x=\"8.3349609375\" y=\"1.53125\">компонент\nприложения<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"-91.97375999999994\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"31.9375\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"56.798828125\" x=\"21.6005859375\" y=\"1.53125\">входной\nскрипт<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"-14.764159999999947\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"83.494140625\" x=\"8.2529296875\" y=\"8.515625\">приложение<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"62.44544000000004\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"78.724609375\" x=\"10.6376953125\" y=\"8.515625000000007\">контроллер<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"872.1807999999999\" y=\"62.44544000000005\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"51.408203125\" x=\"24.2958984375\" y=\"8.515625\">фильтр<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"532.664\" y=\"23.901600000000087\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"50.53515625\" x=\"24.732421875\" y=\"8.515625\">модуль<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"618.4047999999991\" y=\"139.65504000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"27.16796875\" x=\"36.416015625\" y=\"8.515625\">вид<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"786.161599999999\" y=\"139.65504000000004\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"50.81640625\" x=\"24.591796875\" y=\"8.515625\">модель<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n8\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"532.664\" y=\"216.86464000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"52.3515625\" x=\"23.82421875\" y=\"8.515625\">виджет<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n9\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"216.86464000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"31.9375\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.328125\" x=\"19.3359375\" y=\"1.53125\">пакет\nресурсов<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n2\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"23.3125\" x=\"2.369325683593729\" y=\"-25.50056340427648\">1:1<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.025600000000054\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.17992697197425173\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n0\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-49.11136368282337\" y=\"-23.00997508216852\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.025599999999994\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5470891790487767\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n5\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"15.0445484043222\" y=\"30.756096771861735\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"44.894496863129426\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.25416574623968574\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n3\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"2.6225397460937074\" y=\"-25.9149367156797\">1..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"15.254400000000032\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.2090245199765919\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n3\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-57.41127147104078\" y=\"-63.734752657990555\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"43.47658308018222\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.881412889692355\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n5\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"582.664\" y=\"-3.598399999999913\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-18.037919628906252\" y=\"-42.33644914245597\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"16.483200000000004\" distanceToCenter=\"true\" position=\"right\" ratio=\"5.822600000000001\" segment=\"-2\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n6\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-12.453120279866653\" y=\"-24.611373055849555\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"11.385360101663515\" distanceToCenter=\"true\" position=\"left\" ratio=\"0.10670293331985262\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e7\" source=\"n7\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-9.3884511336189\" y=\"-25.947659244081734\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.669798038586146\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.1670192242704811\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e8\" source=\"n9\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-9.871202356268668\" y=\"-25.673070518330803\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.15599378957306\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.15481212573620068\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e9\" source=\"n8\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-15.868632344503453\" y=\"-23.891138042957834\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"13.24330808446041\" distanceToCenter=\"true\" position=\"left\" ratio=\"0.05506723556297327\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e10\" source=\"n4\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-47.487035253906356\" y=\"-22.600377199706998\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"13.616000000000007\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e11\" source=\"n9\" target=\"n8\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-44.441396598124356\" y=\"-19.52837428222651\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.543999999999983\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.4117077892835431\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e12\" source=\"n7\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"25.263671875\" x=\"-44.9806249718132\" y=\"-19.528379555664003\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.543999999999983\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.4531592446547025\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d7\">\n    <y:Resources/>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-uz/images/rbac-access-check-1.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-uz/images/rbac-access-check-2.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-uz/images/rbac-access-check-3.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-uz/images/rbac-hierarchy-1.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n5\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-uz/images/rbac-hierarchy-2.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-uz/images/request-lifecycle.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.13-->\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d0\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key for=\"graphml\" id=\"d7\" yfiles.type=\"resources\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d0\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"70.13700103759766\" width=\"56.558998107910156\" x=\"198.38633947503078\" y=\"261.099458694458\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"26.279499053955078\" y=\"74.13700103759766\">\n            <y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"-0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"0.5\" offsetX=\"0.0\" offsetY=\"4.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"91.193359375\" x=\"-95.193359375\" y=\"26.084125518798828\">пользователь<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.5\" labelRatioY=\"0.0\" nodeRatioX=\"-0.5\" nodeRatioY=\"0.0\" offsetX=\"-4.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"637.1271178722382\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"50.81640625\" x=\"34.091796875\" y=\"13.515625\">модель<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"46.887996673583984\" width=\"39.527000427246094\" x=\"828.8018617630005\" y=\"544.9831195354463\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"84.220703125\" x=\"-22.346851348876953\" y=\"-29.990825653076172\">база данных<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-12.022075653076172\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"17.763500213623047\" y=\"21.443998336791992\">\n            <y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"713.6271178722382\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"27.16796875\" x=\"45.916015625\" y=\"13.515625\">вид<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n4\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"376.1217568159104\" width=\"219.27949905395508\" x=\"527.4919853210449\" y=\"408.8421789169311\"/>\n              <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.4609375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"219.27949905395508\" x=\"0.0\" y=\"0.0\">контроллер</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"2\" leftF=\"1.7335329055786133\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"412.2765645980835\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.4609375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"65.201171875\" x=\"-7.6005859375\" y=\"0.0\">Folder 1</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n4:\">\n        <node id=\"n4::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"445.3031164169311\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"125.96875\" x=\"12.295124053955078\" y=\"6.015625\">создание действия<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n4::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"45.0\" width=\"159.91864204406738\" x=\"558.5326642990112\" y=\"521.8166599273682\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"31.9375\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"75.431640625\" x=\"42.24350070953369\" y=\"6.53125\">наложение\nфильтров<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"diamond\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n4::n2\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"161.79775536060333\" width=\"187.54596614837646\" x=\"544.2255182266235\" y=\"608.1661803722382\"/>\n                  <y:Fill color=\"#99336635\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#993366\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.4609375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#FFFFFF\" visible=\"true\" width=\"187.54596614837646\" x=\"0.0\" y=\"0.0\">действие</y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"4\" bottomF=\"3.8368178606033325\" left=\"4\" leftF=\"3.9869680404663086\" right=\"3\" rightF=\"3.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.4609375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"65.201171875\" x=\"-7.6005859375\" y=\"0.0\">Folder 3</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n4::n2:\">\n            <node id=\"n4::n2::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"644.6271178722382\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"111.09765625\" x=\"19.730670928955078\" y=\"6.015625\">загрузка модели<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n4::n2::n1\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"721.1271178722382\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"97.333984375\" x=\"26.612506866455078\" y=\"6.015625\">создание вида<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n      </graph>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"96.0\" x=\"189.47636032104495\" y=\"713.6271178722382\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"39.77734375\" x=\"28.111328125\" y=\"13.515625\">ответ<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"254.50096702575684\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"47.13671875\" x=\"35.931640625\" y=\"13.515625\">запрос<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"145.154203414917\" width=\"219.27949905395508\" x=\"527.4919853210449\" y=\"222.81551361083984\"/>\n              <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.4609375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"219.27949905395508\" x=\"0.0\" y=\"0.0\">приложение</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"21\" leftF=\"20.720500946044922\" right=\"18\" rightF=\"18.0\" top=\"2\" topF=\"1.7557659149169922\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.4609375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"65.201171875\" x=\"-7.6005859375\" y=\"0.0\">Folder 2</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n7:\">\n        <node id=\"n7::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"262.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"31.9375\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"87.947265625\" x=\"31.305866241455078\" y=\"-0.96875\">определение\nмаршрута<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n7::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"322.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"31.9375\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"86.078125\" x=\"32.24043655395508\" y=\"-0.96875\">создание\nконтроллера<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <node id=\"n8\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"142.4296875\" width=\"160.59051704406738\" x=\"312.9619140625\" y=\"224.57127952575684\"/>\n              <y:Fill color=\"#FFCC0024\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FFCC00\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.4609375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"160.59051704406738\" x=\"0.0\" y=\"0.0\"> входной скрипт</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"1\" leftF=\"0.9186420440673828\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"225.33495140075684\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"21.4609375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"65.201171875\" x=\"-7.6005859375\" y=\"0.0\">Folder 4</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n8:\">\n        <node id=\"n8::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"129.0\" x=\"329.2164936065674\" y=\"262.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"31.9375\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"96.63671875\" x=\"16.181640625\" y=\"-0.96875\">загрузка\nконфигурации<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n8::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"129.0\" x=\"329.2164936065674\" y=\"322.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"129.671875\" x=\"-0.3359375\" y=\"6.015625\">запуск приложения<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <edge id=\"e0\" source=\"n2\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n5\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-10.81052179205907\" sy=\"-22.46484374999998\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"28.000006009454637\" y=\"-193.20499098300934\">\n            <y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"19.26953125\" x=\"-9.634759615545363\" y=\"-200.18936598300934\">11<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n6\" target=\"n7::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.634765625\" x=\"-30.24242594828081\" y=\"-9.983417510986328\">3<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"0.9990329742431641\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.25395048761528816\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n7::e0\" source=\"n7::n0\" target=\"n7::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n0\" target=\"n8\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"-79.9882439360955\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.634765625\" x=\"36.12034335201736\" y=\"-9.169280329692242\">1<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n7::n1\" target=\"n4\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"2.408647025755731\" ty=\"-181.21658369302781\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.634765625\" x=\"-5.4386998761101495\" y=\"19.43622140884395\">4<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n3\" target=\"n4::n2::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.634765625\" x=\"-30.80765317377586\" y=\"-8.98439335823059\">9<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.26448415477215953\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n4::n2::n1\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"-2.2737367544323206E-13\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"19.26953125\" x=\"-93.53556463732264\" y=\"-8.984393358230705\">10<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.2786124840137136\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e7\" source=\"n8::n1\" target=\"n7\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"55.750299312644984\" sy=\"0.355224609375\" tx=\"-109.63961141576266\" ty=\"42.066115379333496\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.634765625\" x=\"28.845541954040527\" y=\"-8.93880986215106\">2<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e8\" source=\"n1\" target=\"n4::n2::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.634765625\" x=\"-31.95652327480775\" y=\"-9.98439335823059\">8<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"1.0\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.2858946861565087\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e9\" source=\"n4::n1\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-79.9882439360951\" sy=\"2.2737367544323206E-13\" tx=\"24.24112858184396\" ty=\"-1.4999999999999991\">\n            <y:Point x=\"261.71748890288893\" y=\"544.3166599273684\"/>\n          </y:Path>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.634765625\" x=\"-83.69880788074641\" y=\"-8.984365463256609\">6<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.23459266172695797\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::e0\" source=\"n4::n0\" target=\"n4::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFEFD6\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.634765625\" x=\"-5.817401885986328\" y=\"4.999985313415493\">5<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.0\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::e1\" source=\"n4::n1\" target=\"n4::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"-81.01828954355172\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFEFD6\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"17.96875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"11.634765625\" x=\"-5.926145785867902\" y=\"5.063791275024414\">7<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.0\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::n2::e0\" source=\"n4::n2::n0\" target=\"n4::n2::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n8::e0\" source=\"n8::n0\" target=\"n8::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d7\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"66px\" viewBox=\"0 0 57 66\" enable-background=\"new 0 0 57 66\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3799\" y1=\"-2276.8809\" x2=\"27.6209\" y2=\"-2306.6792\" gradientTransform=\"matrix(1 0 0 -1 0.2803 -2252.9199)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_13_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t&lt;path fill=\"#2068A3\" stroke=\"#2068A3\" d=\"M28.106,33.487c-8.112,0-12.688,4.312-12.688,10.437c0,7.422,12.688,10.438,12.688,10.438\n\t\ts14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.487,28.106,33.487z M26.288,53.051c0,0-7.135-2.093-8.805-7.201\n\t\tc-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"14.2417\" cy=\"9.1006\" r=\"53.247\" gradientTransform=\"matrix(1 0 0 -1 0.04 65.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#74AEEE\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#2068A3\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#2068A3\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.022,7.807-14.022,7.807s-10.472-2.484-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path fill=\"#5491CF\" stroke=\"#2068A3\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397c-0.514,1.027-1.669,4.084-1.669,5.148\n\t\tc0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.335-2.36c-3.601-1.419-4.071-3.063-5.89-4.854\n\t\tC12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t&lt;path fill=\"#5491CF\" stroke=\"#2068A3\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617c0.516,1.025,3.617,3.693,3.617,6.617\n\t\tc0,5.186-10.27,8.576-16.698,9.145c1.429,4.938,11.372,1.293,13.804-0.313c3.563-2.354,4.563-5.133,7.854-3.705\n\t\tC47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t&lt;path fill=\"none\" stroke=\"#2068A3\" stroke-linecap=\"round\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t&lt;path fill=\"none\" stroke=\"#2068A3\" stroke-linecap=\"round\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.623\" cy=\"-2278.646\" r=\"23.425\" fx=\"23.0534\" fy=\"-2281.1357\" gradientTransform=\"matrix(1 0 0 -1 0.2803 -2252.9199)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"5761.7578\" y1=\"11330.6484\" x2=\"5785.3872\" y2=\"11424.0977\" gradientTransform=\"matrix(0.275 0 0 0.2733 -1558.9874 -3088.4209)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M27.958,6.333c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.083,13.952,36.271,6.268,27.958,6.333z\"/&gt;\n\t&lt;path id=\"Hair_Young_Brown_1_\" fill=\"#CC9869\" stroke=\"#99724F\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n\t&lt;path fill=\"#4B4B4B\" stroke=\"#4B4B4B\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" d=\"M28.105,2\n\t\tC22.464,2,20.2,4.246,18.13,5.533C29.753,2.865,41.152,10.375,44.46,20.5C44.459,16.875,44.459,2,28.105,2z\"/&gt;\n\t&lt;path fill=\"#9B9B9B\" stroke=\"#4B4B4B\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" d=\"M11.151,17.751\n\t\tC12.878,8.25,18.686,6.309,25.273,7.127C31.295,7.875,36.93,10.491,44.459,20.5C37.777,7.125,20.278-3.375,9.903,3.921\n\t\tC5.569,6.97,4.903,13.375,11.151,17.751z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\"\n\t xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\t x=\"0px\" y=\"0px\" width=\"41px\" height=\"48px\" viewBox=\"-0.875 -0.887 41 48\" enable-background=\"new -0.875 -0.887 41 48\"\n\t xml:space=\"preserve\"&gt;\n&lt;defs&gt;\n&lt;/defs&gt;\n&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-979.1445\" x2=\"682.0508\" y2=\"-979.1445\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_1_)\" d=\"M19.625,36.763C8.787,36.763,0,34.888,0,32.575v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,34.888,30.464,36.763,19.625,36.763z\"/&gt;\n&lt;linearGradient id=\"SVGID_2_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-973.1445\" x2=\"682.0508\" y2=\"-973.1445\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_2_)\" d=\"M19.625,36.763c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.927-18.396,3.927\n\tc-9.481,0-17.396-1.959-18.396-3.927l-1.229,2C0,34.888,8.787,36.763,19.625,36.763z\"/&gt;\n&lt;path fill=\"#3C89C9\" d=\"M19.625,26.468c10.16,0,19.625,2.775,19.625,2.775c-0.375,2.721-5.367,5.438-19.554,5.438\n\tc-12.125,0-18.467-2.484-19.541-4.918C-0.127,29.125,9.465,26.468,19.625,26.468z\"/&gt;\n&lt;linearGradient id=\"SVGID_3_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-965.6948\" x2=\"682.0508\" y2=\"-965.6948\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_3_)\" d=\"M19.625,23.313C8.787,23.313,0,21.438,0,19.125v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,21.438,30.464,23.313,19.625,23.313z\"/&gt;\n&lt;linearGradient id=\"SVGID_4_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-959.6948\" x2=\"682.0508\" y2=\"-959.6948\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_4_)\" d=\"M19.625,23.313c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.926-18.396,3.926\n\tc-9.481,0-17.396-1.959-18.396-3.926l-1.229,2C0,21.438,8.787,23.313,19.625,23.313z\"/&gt;\n&lt;path fill=\"#3C89C9\" d=\"M19.476,13.019c10.161,0,19.625,2.775,19.625,2.775c-0.375,2.721-5.367,5.438-19.555,5.438\n\tc-12.125,0-18.467-2.485-19.541-4.918C-0.277,15.674,9.316,13.019,19.476,13.019z\"/&gt;\n&lt;linearGradient id=\"SVGID_5_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-952.4946\" x2=\"682.0508\" y2=\"-952.4946\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_5_)\" d=\"M19.625,10.113C8.787,10.113,0,8.238,0,5.925v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,8.238,30.464,10.113,19.625,10.113z\"/&gt;\n&lt;linearGradient id=\"SVGID_6_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-946.4946\" x2=\"682.0508\" y2=\"-946.4946\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_6_)\" d=\"M19.625,10.113c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.926-18.396,3.926\n\tc-9.481,0-17.396-1.959-18.396-3.926L0,5.925C0,8.238,8.787,10.113,19.625,10.113z\"/&gt;\n&lt;linearGradient id=\"SVGID_7_\" gradientUnits=\"userSpaceOnUse\" x1=\"644.0293\" y1=\"-943.4014\" x2=\"680.8223\" y2=\"-943.4014\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;ellipse fill=\"url(#SVGID_7_)\" cx=\"19.625\" cy=\"3.926\" rx=\"18.396\" ry=\"3.926\"/&gt;\n&lt;path opacity=\"0.24\" fill=\"#FFFFFF\" enable-background=\"new    \" d=\"M31.04,45.982c0,0-4.354,0.664-7.29,0.781\n\tc-3.125,0.125-8.952,0-8.952,0l-2.384-10.292l0.044-2.108l-1.251-1.154L9.789,23.024l-0.082-0.119L9.5,20.529l-1.65-1.254\n\tL5.329,8.793c0,0,4.213,0.903,7.234,1.07s8.375,0.25,8.375,0.25l3,9.875l-0.25,1.313l1.063,2.168l2.312,9.645l-0.521,1.416\n\tl1.46,1.834L31.04,45.982z\"/&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-uz/intro-upgrade-from-v1.md",
    "content": "1.1 dan keyingi yangilanishlar\n==============================\n\nYii 2.0 talqini uchun batamom boshqatdan yozilganligi bois 1.1 va 2.0 talqinlar orasida ko'p farqlar mavjud.\nShu tufayli 1.1 dan keyingi yangilanishlar minor talqinlar (talqinlarning bir biridan 1-xonasidan keyingi xonalaridagi sonlari farq qiladiganlari) orasidagi yangilanishlar kabi sodda ko'rinishda bo'lmaydi.\nUshbu qo'llanmada ikki talqin orasidagi asossiy farq yangilanishlari keltirilgan.\n\nAgar siz ilgari Yii 1.1 ni ishlatmagan bo'lsangiz u holda ushbu bo'limni tashlab, [Ishni boshlash][start-installation.md] bo'limiga o'tishingiz mumkin.\n\nShuningdek shuni unutmangki Yii 2.0 bu yerda yozilganidan ko'proq imkoniyatlarga ega. Qaysi imkoniyatlar qo'shilganini bilish uchun qo'llanmani o'qib chiqish tavsiya etiladi. Balkim siz bunga qadar o'zingiz uchun zarur deb bilib yaratgan imkoniyat endi freymvorkning bir qismidir. \n\n\nO'rnatish\n---------\n\nYii ning 2.0 talqini PHP uchun bog'liqliklarni boshqaruvchi hisoblangan [Composer](https://getcomposer.org/) ga butunlay asoslangan. \nFreymvorkni o'rnatish va shuningdek kengaytirish Composer orqali qilinadi. Yii 2.0 ni o'rnatish bo'yicha yanada batafsilroq ma'lumot \n[Yii ni o'rnatish](start-installation.md) bo'limida keltirilgan. Yii 2.0 uchun qanday qilib kengaytmalar yaratish yoki 1.1 talqindagi mavjud kengaytmalarni qanday qilib 2.0 talqinga adaptiatsiyalash ko'rsatmalari [Kengaytmalarni yaratish](extend-creating-extensions.md) bo'limida ko'rsatilgan.\n\n\nPHP talabi\n----------\n\nYii 2.0 talqin Yii 1.1 talqinda qo'llanilgan PHP 5.2 ga nisbatan ancha yaxshilangan PHP 5.4 yoki undan yuqorisini islatadi. \nShu tufayli siz nazarda tutishingiz kerak bo'lgan tildagi ko'p o'zgarishlar mavjud. \nQuyida PHP ning asosiy o'zgarishlari keltirilgan:\n\n- [Nomlar sohasi](https://www.php.net/manual/ru/language.namespaces.php);\n- [Anonim funksiyalar](https://www.php.net/manual/ru/functions.anonymous.php);\n- Massivlar uchun qisqa sintaksisni qo'llash: `[...elementlar...]` ni `array(...элементы...)` o'rniga;\n- Qisqartirilgan teglarni qo'llash `<?=` ko'rinish fayllarida chiqarish uchun.\n  PHP 5.4 talqinida ushbu imkoniyatni hech qanday sozlashlarsiz qo'llash mumkin;\n- [SPL ning klaslari va interfeyslari](https://www.php.net/manual/ru/book.spl.php);\n- [Kechroq statik bog'lash (LSB)](https://www.php.net/manual/ru/language.oop5.late-static-bindings.php);\n- [Sana va vaqt uchun klaslar](https://www.php.net/manual/ru/book.datetime.php);\n- [Treytlar](https://www.php.net/manual/ru/language.oop5.traits.php);\n- [Xalqarolashtirish (Intl)](https://www.php.net/manual/ru/book.intl.php); Xalqarolashtirish imkoniyatlaridan foydalanish maqsadida Yii 2.0 PHP ning `intl` kengaytmasini ishlatadi.\n\n\nNomlar sohasi\n-------------\n\nYii 2.0 ning asosiy o'zgarishlaridan biri bu nomlar sohasi hisoblanadi. Freymvorkning deyarli har bir sinfi nomlar sohasida joylashgan, masalan, `yii\\web\\Request`.\n\"C\" qo'shimchasi endi klaslar nomlarida ishlatilmaydi.\nKlaslarni nomlash kelishuvi direktoriyalar strukturasiga asoslanilgan. Masalan, `yii\\web\\Request` ushbu yozuv klasning yii freymvork direktoriyasidagi web/Request.php faylida joylashganini anglatadi.\n(Yii ning klaslarni yuklovchisi evaziga siz freymvork klaslarini hech qanday vositachisiz boglab qo'yishingiz mumkin).\n\n\nKomponent va obekt\n------------------\n\nYii 2.0 da 1.1 dagi `CComponent` klas ikkita klasga ajratilgan: [[yii\\base\\BaseObject]] va [[yii\\base\\Component]].\n[[yii\\base\\BaseObject|BaseObject]] klas oddiy asos klas bo'lib xususiyatlar uchun [getter va setter](concept-properties.md) larni ishlatishga imkon beradi. \n[[yii\\base\\Component|Component]] klas [[yii\\base\\BaseObject|BaseObject]] klasdan voris bo'lib [xodisalar](concept-events.md) va \n[o'zini tutish](concept-behaviors.md) larni qo'llab quvvatlaydi.\n\nAgar sizni klasingizga xodisalar funksiyalari yoki o'zini tutishlar kerak bo'lmasa asos klas sifatida [[yii\\base\\BaseObject|BaseObject]] ni qo'llashingiz mumkin. Ushbu holat asosan asos strukturali klaslar yaratilayotgan vaqtda yuz beradi.\n\n\nObekt sozlashlari\n-----------------\n\n[[yii\\base\\BaseObject|BaseObject]] klas obektlarni sozlashni yagona usulini tashkillashtiradi. Ixtiyoriy [[yii\\base\\BaseObject|BaseObject]] ga voris bo'lgan klas (agar kerak bo'lsa) o'zini sozlashi uchun quyidagi ko'rinishda o'ziga konstruktor yaratishi mumkin: \n\n```php\nclass MyClass extends \\yii\\base\\BaseObject\n{\n    public function __construct($param1, $param2, $config = [])\n    {\n        // ... sozlashlar qo'llanilishidan oldin initsializatsiyalash (e'lon qilish va qiymatlash) \n\n        parent::__construct($config);\n    }\n\n    public function init()\n    {\n        parent::init();\n\n        // ... sozlashlar qo'llanilganidan keyin initsializatsiyalash\n    }\n}\n```\n\nYuqoridagi misolda oxirgi parametr obekt xususiyatlarini qiymatlovchi sozlashlar massivi ya'ni kalit-qiymat formatidagi juftlikdan iborat bo'lishi kerak. Siz sozlashlar qo'llanilganidan keyin initsializatsiya ishini amalga oshirish uchun oldindan [[yii\\base\\BaseObject::init()|init()]] metod yaratib qo'yishingiz mumkin.\n\nUshbu kelishuvga asoslanib siz sozlash massivi yordamida yangi obektlarni yaratishingiz va sozlashingiz mumkin:\n\n```php\n$object = Yii::createObject([\n    'class' => 'MyClass',\n    'property1' => 'abc',\n    'property2' => 'cde',\n], [$param1, $param2]);\n```\n\nSozlashlar haqidagi batafsil ma'lumotlar [Obektlarni sozlash](concept-configurations.md) bo'limida keltirilgan.\n"
  },
  {
    "path": "docs/guide-uz/intro-yii.md",
    "content": "Yii nima o`zi?\n==============\n\nYii – bu tez ishlovchi komponentli PHP freymvork bo'lib, zamonaviy web ilovalarni tez yaratish uchun mo'ljallangan. Yii (`Yi` `[ji:]` kabi talaffuz qilinadi) so'zi xitoy tilida \"oddiy va evolyutsiyalovchi\" degan ma'noni anglatadi. Shuningdek Yii akronim sifatida qaralganda uning yoyilma matni **Yes It Is** tarzida qaralishi ham mumkin!\n\n\nYii ko'proq qanday masalalar uchun mos keladi?\n----------------------------------------------\n\nYii – bu universal freymvork va uni barcha turdagi web ilovalar uchun qo'llash mumkin. Uning komponentli strukturasi va keshlashni juda zo'r qo'llab-quvvatlashi evaziga freymvork asosan portallar, forumlar, CMS, magazinlar yoki RESTful ilovalar kabi katta proyektlar uchun qo'l keladi.\n\n\nYii ni boshqa freymvorklar bilan solishtirish\n---------------------------------------------\n\n- Boshqa ko'pgina PHP freymvorklar singari Yii ham kodni tashkillashtirish uchun MVC (Model-View-Controller) modelidan foydalanadi.\n- Yii faqat loyihalashtirishning ma'lum bir qolipiga ergashib dizaynni murakkablashtirmasdan sodda va elegantli kod yozish falsafasiga tayanadi.\n- Yii full-stack freymvork hisoblanadi. Shuningdek o'z ichiga tekshirilgan va o'zini yaxshi ko'rsatgan relatsion va NoSQL ma'lumotlar ombori uchun yaratilgan ActiveRecord, REST API ni qo'llab quvvatlash, ko'p qatlamli keshlash kabi imkoniyatlarni oladi.\n- Yii juda yaxhsi kengayishi mumkin. Siz asosiy kodni ixtiyoriy qismini almashtirishingiz yoki sozlashingiz mumkin. Kengaytirish arxitekturasiga bo'ysunib kodni boshqalar bilan ulashish yoki jamoatning kodidan foydalanish mumkin.\n- Yii ning asosiy maqsadlaridan biri - ishlash tezligi.\n\nYii — bir odamning loyihasi emas. U unga yordam berayotgan ishlab chiquvchilar [katta jamoa][yii_team]si  tomonidan qo'llab quvvatlanadi va rivojlantiriladi. Freymvork ishlab chiquvchilari web ishlab chiqish va boshqa ilovalarni maromini kuzatishadi. Ko'proq mos keluvchi imkoniyatlar va eng yaxshi sinalgan amaliyotlar freymvork sodda va elegantli interfeysi tarzida qo'llaniladi.\n\n[yii_team]: https://www.yiiframework.com/team\n\nYii talqinlari\n--------------\n\nAyni vaqtda Yii ning ikkita yo'nalishi mavjud: 1.1 va 2.0. 1.1 yo'nalishi avvalgi avlod hisoblanadi va qo'llab quvvatlash holatida. 2.0 talqini - bu Composer, PSR, nomlar sohasi, treytlar(traits) va boshqa shular kabi ko'pgina oxirgi texnologiyalarni va *qaydnoma*larni qo'llovchi Yii ning to'liq boshqatdan yozilgan talqini. Mana shu talqinda navbatdagi yillarda uni yanada kuchaytirish nazarda tutilgan. Ushbu qo'llanma aynan 2.0 talqin haqida.\n\n\nDT va bilimlarga talablar\n-------------------------\n\nYii 2.0 PHP 7.4.0 va undan yuqorisini talab qiladi. Boshqa imkoniyatlar uchun talablarni bilish uchun har bir alohida yo'lga qo'yilgan freymvork bilan birga mos o'rnatilgan talablar tekshiruv skriptini ishga tushirishingiz mumkin.\n\nFreymvork to'liq obektga mo'ljallangan dasturlashga (OMD) asoslanganligi bois Yii da ishlash uchun OMD ni umumiy tushunish talab etiladi. Shuningdek, PHP ning zamonaviy imkoniyatlari bo'lmish [nomlar soxasi](https://www.php.net/manual/ru/language.namespaces.php) va [treytlar](https://www.php.net/manual/ru/language.oop5.traits.php) ni o'rganish talab etiladi.\n"
  },
  {
    "path": "docs/guide-uz/start-databases.md",
    "content": "Ma'lumotlar ombori bilan ishlash\n======================\n\nBu bo'lim sizga yangi sahifani yaratishga yordam beradi, u sahifa esa `countries` jadvalidan kelgan ma'lumotlarni chiqarib beradi. Buning uchun esa siz ma'lumotlar ombori bilan aloqani sozlashingiz kerak, klass yaratish [Active Record](db-active-record.md),[amalni](structure-controllers.md) va [ko'rinish](structure-views.md) yaratishimiz.\n\nBu bo'limda siz nimalarni o'rganasiz:\n\n* Qandey qilib ma'lumotlar omboriga ulanish.\n* Active Record klassini yasash\n* Ma'lumotlarni olishni, Active Recordni ishlatishni.\n* viewda ma'lumotlarni sahifalarga bo'lib chiqarishni.\n\nEtibor bering, shu bo'limni o`zlashtirish uchun, siz ma'lumotlar ombori bilan ishlash bilimingiz bo'lishi kerak. \nAniqroq esa,siz hech ma'lumotlar omborini yasashni va unga SQL-so'rov jo'natishni, ma'lumotlar ombori klientlarini ishlatishni bilishingiz kerak.\n\nMa'lumotlar omborini tayyorlaymiz <span id=\"preparing-database\"></span>\n----------------------------------------------------------------\n\nBirinchi bo'lib `yii2basic` nomli ma'lumotlar ombri yarating, chunki undan siz web ilovadan ma'lumotlarni qabul qilasiz.\nMa'lumotlar omborini siz SQLite, MySQL, PostgreSQL, MSSQL yoki Oracle da yaratishingiz mumkin, chunki Yiida aytib o'tilgan barcha ma'lumotlar ombori bilan ishlay oladi. Soddalik uchun keyinchalik, ma'lumotlar ombori misol uchun MYSql haqida gap boradi.\n\nEndi esa ma'lumotlar omborida `country` degan jadval yarating va unga ozgina misol uchun ma'lumotlar qo'shing. Siz quydagi SQL-buyruqni bajarishingiz mumkin yuqorida aytilgan amalarni bajarish uchun:\n\n```sql\nCREATE TABLE `country` (\n  `code` CHAR(2) NOT NULL PRIMARY KEY,\n  `name` CHAR(52) NOT NULL,\n  `population` INT(11) NOT NULL DEFAULT '0'\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nINSERT INTO `country` VALUES ('AU','Australia',24016400);\nINSERT INTO `country` VALUES ('BR','Brazil',205722000);\nINSERT INTO `country` VALUES ('CA','Canada',35985751);\nINSERT INTO `country` VALUES ('CN','China',1375210000);\nINSERT INTO `country` VALUES ('DE','Germany',81459000);\nINSERT INTO `country` VALUES ('FR','France',64513242);\nINSERT INTO `country` VALUES ('GB','United Kingdom',65097000);\nINSERT INTO `country` VALUES ('IN','India',1285400000);\nINSERT INTO `country` VALUES ('RU','Russia',146519759);\nINSERT INTO `country` VALUES ('US','United States',322976000);\n```\n\nHozirda sizda `yii2basic` degan ma'lumotlar ombori bo'lishi kerak va unda 3 ta ustundan iborat `country` degan jadval bo'lishi kerak, o'nta satrdan iborat ma'lumot bilan.\n\nMa'lumotlar omborida bilan aloqani sozlaymiz<span id=\"configuring-db-connection\"></span>\n-------------------------------------------------------------------------\n\nIshni boshlashdan oldin sizda [PDO](https://www.php.net/manual/ru/book.pdo.php) kengaytmasi o'rnatilganiga amin bo'ling va PDO-boshqaruvchisi ma'lumotlar ombori bilan boshqara olishingiz uchun(misol uchun, `pdo_mysql` MYSql uchun). Bu siz aloqali ma'lumtolar ombori bilan ishlashingiz uchun assosiy talablardir.\nEndi esa, hammasini o'rnatib bo'lganingizdan so'ng, `config/db.php` oching va sozlamalarni o`zingizni ma'lumotlar omboringiz sozlamalariga o'zgartiring. Boshlanish sozlamalar quydagichadir:\n\n```php\n<?php\n\nreturn [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=localhost;dbname=yii2basic',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n];\n```\n\nFayl `config/db.php` — oddiy [sozlamalar](concept-configurations.md) vositasidir, faylarda assoslangan. Quydagi sozlama fayli [[yii\\db\\Connection]] klass nusxasini olish uchun kerak bo'lgan assosiy talablarga javob beradigan sozlamalardan iboratdir, undan esa siz SQL-buyruqlarni ma'lumotlar omboriga jonatishingiz mumkin.\n\nMa'lumotlar omboriga yuqoridagi sozlamalar sozlab bo'lgandan so'ng ulanish, undan foydalanish uchun `Yii::$app->db` orqali amalga oshiriladi.\n\n> Info: fayl `config/db.php` ilovaning assosiy sozlama fayli orqali ulanadi `config/web.php`,\n  u esa [web-ilova](structure-applications.md) nusxasini sozlamalarini o'z ichiga oladi.\n  qo'shimcha ma'lumot uchun, iltimos, ushbu [Sozlamalar](concept-configurations.md) bo'limga ko'rib chiqing.\n\nAgar siz frameworkda yo'q bo'lgan ma'lumotlar omborlari bilan ishlashingiz kerak bo'lsa quydagi kengaytmalarga etibor bering:\n\n- [Informix](https://github.com/edgardmessias/yii2-informix)\n- [IBM DB2](https://github.com/edgardmessias/yii2-ibm-db2)\n- [Firebird](https://github.com/edgardmessias/yii2-firebird)\n\n\nActive Record klassi (sinf) avlodini yaratamiz<span id=\"creating-active-record\"></span>\n-----------------------------------------------------------------------\n\n`country`dan ma'lumotlarni olib ishlatib ko'rish uchun, [Active Record](db-active-record.md)ni kengaytirgan klass yarating, nomi `Country` va uni `models/Country.php` fayliga saqlang.\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass Country extends ActiveRecord\n{\n}\n```\n\n`Country` klassi [[yii\\db\\ActiveRecord]]ni kengaytirmoqda. Siz uning ichida bir satr kod ham yozmasligingiz mumkin! Yuqoridagi kod assosida Yii o'zi ma'lumotlar omboridagi jadval va klass orasida aloqa yasaydi.\n\n> Info: Agar ma'lumotlar omboridagi nom va klassnomi orasida bir xillikni saqlash imkoni  bo'lmasa siz klassga aynan jadal ismini [[yii\\db\\ActiveRecord::tableName()]] usuli orqali yozishingiz mumkin.\n\n`Country` klassini ishlatganingizda, siz osonlikcha `country` jadvalidagi ma'lumotlar bilan boshqarishingiz mumkin, misol uchun pastdagi qatorlarda shuni ko'rishingiz mumkin:\n\n```php\nuse app\\models\\Country;\n\n// \"country\" jadvalidan hamma ma'lumotlarni qabul qilamiz va ularni \"name\" ustuni bo'yicha saralaymiz.\n$countries = Country::find()->orderBy('name')->all();\n\n// \"US\" jadval ka'liti bo'yicha ma'lumotni olib beradi.\n$country = Country::findOne('US');\n\n//\"United States\"\necho $country->name;\n\n// nomi o`zgartiramiz \"U.S.A.\"ga va ma'lumotlar omborida saqlaymiz.\n$country->name = 'U.S.A.';\n$country->save();\n```\n\n> Info: Active Record — bu ma'lumotlar ombori bilan ishlash uchun juda ham mukammal vosita hisoblanadi.\n  Yana ko'oproq ma'lumotni siz [Active Record](db-active-record.md) bo'limida topishingiz mumkin. Qo'shimcha tariqasida, siz yana boshqa yanada past qism vositasi [Data Access Objects](db-dao.md)dan foydalanishingiz mumkin.\n\n\nAmal yaratamiz <span id=\"creating-action\"></span>\n-------------------------------------------------\n\nMa'lumotlarni foydalanuvchiga ko'rsatish uchun siz amal yaratishingiz kerak. Oldingi misolarda farqli o'laroq `site` nazoratchisi ichida amal yasash o'rniga to'g'riroq buning uchun maxsus nazoratchi yasab unga hamma usularni yig'ish to'g'riroq bo'ladi,Yangi nazoratchini `CountryController` deb nomlang, va unda `index` degan amal yarating uning ichida pastda ko'rsatilgandek ma'lumot kiriting:\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\nuse yii\\data\\Pagination;\nuse app\\models\\Country;\n\nclass CountryController extends Controller\n{\n    public function actionIndex()\n    {\n        $query = Country::find();\n\n        $pagination = new Pagination([\n            'defaultPageSize' => 5,\n            'totalCount' => $query->count(),\n        ]);\n\n        $countries = $query->orderBy('name')\n            ->offset($pagination->offset)\n            ->limit($pagination->limit)\n            ->all();\n\n        return $this->render('index', [\n            'countries' => $countries,\n            'pagination' => $pagination,\n        ]);\n    }\n}\n```\n\nYuqoridagi kodni quydagicha saqlang `controllers/CountryController.php`.\n\n`index` amali `Country::find()`ni chaqirmoqda. Ushubu Active Record usuli `country` jadvalidan hamma ma'lumotlarni qaytaradi.\nkelayotgan ma'lumotlar bo'lish sahifalarga bo'lish uchun [[yii\\data\\Pagination]] klassi ishlatilmoqda. `Pagination` nusxasi ikkta maqsadga kerak:\n* `offset` va `limit` SQL-buyrug`i uchun belgilanadi, bu esa birmartada faqat bitta sahifani qaytarishga berilgan (bitta sahifada misl uchun 5 ta satr)\n* u esa viewda paginatsiya (pagination) chiqarilishi uchun ishlatilmoqda, turi esa sonlardan iborat, bularni hammasini oldinda tushuntirilib kelinadi.\n\nKodni oxirida esa `index` amali `index` ko'rinishini chiqarib beradi, unga hamma chiqarilishi kerak bo'lgan ma'lumotlarni va paginatsiyanni qaytarayapti.\n\nKo'rinish yaratamiz <span id=\"creating-view\"></span>\n---------------------------------------------\n\nBirinchi bo'lib `country` degan papka ochamiz `views` ichida. Bu papka hamma nazoratchi ko'rinishlarni saqlash uchun ishlatamiz. Ushbu papkada `views/country` fayl yaratamiz `index.php`, so'ng quydagi kodni yozamiz:\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\LinkPager;\n?>\n<h1>Countries</h1>\n<ul>\n<?php foreach ($countries as $country): ?>\n    <li>\n        <?= Html::encode(\"{$country->code} ({$country->name})\") ?>:\n        <?= $country->population ?>\n    </li>\n<?php endforeach; ?>\n</ul>\n\n<?= LinkPager::widget(['pagination' => $pagination]) ?>\n```\n\nKo'rinish ikkta qisimga bo'lingan ma'lumot chiqishiga nisbatan. Ko'rinishni birinchi qismida ma'lumotlar tartibsiz HTML-ro'yxati kabi chiqayapti.\nIkkinchisida esa [[yii\\widgets\\LinkPager]] vidjiti, u ko'rinishga qaytarilayotgan paginatsiya ma'lumoti assosida shaklanadi. Vidjet `LinkPager` sahifalar tugalarini chiqarib beradi. Ulardan birortasiga bosangiz sahifa yangilanib ma'lumotlar o'zgaradi\n\n\nKeling sinab ko'raylik <span id=\"trying-it-out\"></span>\n------------------------------------------------------\n\nYuqoridagi yozgan kodni ishlashini ko'rish uchun iltimos ushbu yo'l (url) bo'yicha o'ting:\n\n```\nhttps://hostname/index.php?r=country%2Findex\n```\n\n![Список Стран](images/start-country-list.png)\n\nBoshida siz ma'lumotlar omboridan beshta ma'lumotni chiqarib berganini ko'rishingiz mumkin. Ma'lumotlar ostida. Agar siz \"2\" tugmasiga bosangiz, keyingi ikkinchi sahifa ma'lumotlarini ko'rsatayotganini ko'rishingiz mumkin.\nEtibor bilan qarasangiz, URLda ham quydagi \"2\" sahifa chiqti.\n\n```\nhttps://hostname/index.php?r=country%2Findex&page=2\n```\n\n[[yii\\data\\Pagination|Pagination]] Ma'lumotlarni sahifalarga bo'lib qisman chiqarish umumiy ishlash tartibi esa quydagicha:\n* Boshida [[yii\\data\\Pagination|Pagination]] birinchi sahifani ko'rsatadi, uning SQL-buyrugi qismi  `LIMIT 5 OFFSET 0` bo'ladi. Natijada, birinchi beshta ma'lumot qaytarilib ko'rsatiladi.\n* Vidjet [[yii\\widgets\\LinkPager|LinkPager]] URLdagi ma'lumotda assosan tugmachalarni shaklantirib beradi, Urlni esa [[yii\\data\\Pagination::createUrl()|Pagination]]. Bu URLda `page` parametri mavjud bo'ladi, tugmachalarda esa xar xil page parametri mavjud.\n* Agar \"2\" tugmasiga bosangiz, sahifa yangilanib `country/index` uchun yangi so'rov amalga oshadi. Shundey qilib SQL-so'rovi `LIMIT 5 OFFSET 5` tashkil qiladi va keying beshtalikni qaytaradi.\n\nXulosa <span id=\"summary\"></span>\n-------------------------------------\n\nQuydagi bo'limda siz ma'lumotlar ombori bilan ishlashni o'rgandingiz. Shundey qilib siz yana ma'lumotlarni qismlarga bo'lib chiqarishni o'rgandingiz sizga [[yii\\data\\Pagination]] va [[yii\\widgets\\LinkPager]] yordam berdi.\n\nKeyingi bo'limda esa siz kodni yasash uchun juda kuchli [Gii](start-gii.md) vositasini ishlatishni o'rganasiz, uning yordamida siz juda ko'p ishlatiladigan masalalarni hal qilishingiz mumkin, Shulardan Create-Read-Update-Delete (CRUD) ma'lumotlar ombori jadvali bilan ishlash. Aslida siz hozir Gii yasaydigan kodni yuqorida yozib chiqdingiz, Yiida siz shularni avotmatik tarzda Gii yordamida kodni yaratishingiz mumkin.\n"
  },
  {
    "path": "docs/guide-uz/start-forms.md",
    "content": "Shaklar bilan ishlash\n================\n\nUshbu bo'limda biz foydalanuvchidan ma'lumotlarni qabul qilishni o'rganamiz. Sahifada ma'lumotlarni kiritish uchun ism va emailni kiritish uchun shakl bor. Sahifada kiritilgan ma'lumotlar tasdiqlanishi uchun ko'rsatiladi.\n\nShu maqsadga erishish uchun biz bitta [amal](structure-controllers.md) va ikkta [ko'rinish](structure-views.md) yasashdan tashqari\n[model](structure-models.md) yaratasiz.\n\nUshbu qo'lanmada siz quydagilarni o'rganasiz:\n\n* Qandey qilib foydalanuvchi tomonidan kiritilgan ma'lumotlar uchun [modelni](structure-models.md) yaratish mumkin;\n* Qandey qilib ma'lumotlarni tekshirish uchun mantiq yozishimiz mumkin;\n* Qandey qilib HTML-shaklni [ko'rinish](structure-views.md) ichida yaratishimiz mumkin.\n\n\nModel yaratilishi <span id=\"creating-model\"></span>\n---------------------------------------------\n\nFayl `models/EntryForm.php`da `EntryForm` pastda ko'rsatilganidek klassini yarating. U foydalanuvchidan kelgan ma'lumotlar saqlash uchun ishlatiladi. Klasslarni nomlari haqida siz \n«[Klasslarni avtoyuklanishi](concept-autoloading.md)» bo'limida o'qishingiz mumkin.\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse yii\\base\\Model;\n\nclass EntryForm extends Model\n{\n    public $name;\n    public $email;\n\n    public function rules()\n    {\n        return [\n            [['name', 'email'], 'required'],\n            ['email', 'email'],\n        ];\n    }\n}\n```\n\nUshbu klass quydagi klassni kengaytiradi [[yii\\base\\Model]], u esa frameworkni bir qismi hisoblanadi va oddatda shakl ma'lumotlari bilan ishlaydi.\n\nKlass 2-ta ochiq hossa `name` va `email` iborat, ular foydalanuvchilar ma'lumotlarni saqlash uchun ishlatiladi.\nuni yana `rules()` usuli bor, u esa ma'lumotlarni saqlash qoidalarini (tartibini) yozib o'tadi. Yuqorida yozilgan qoidalar quydagicha ta'riflasa bo'ladi:\n\n* Xossalar `name` va`email` to'ldirilishi shart;\n* Xossa`email` ma'lumoti email bo'lishi shart.\n\nAgar `EntryForm` nusxasi (obyekt) ma'lumotlar bilan to'ldirilgan bo'lsa, kiritilgan ma'lumotlarni kiritilgan ma'lumotlar ma'lumot qoidalari talabiga to'g'ri kelishini [[yii\\base\\Model::validate()|validate()]] tekshirishingiz mumkin. Agar ma'lumotlar tekshirishdan o'tmasa [[yii\\base\\Model::hasErrors|hasErrors]]\nxossasi `true` ga teng bo'lib qoladi. Ushbu xossa: [[yii\\base\\Model::getErrors|errors]] orqali siz qanaqa xatolar borligini ko'rishingiz mumkin.\n\n\nAmal yaratilishi <span id=\"creating-action\"></span>\n------------------------------------------------\n\nEndi esa huddi pastda ko'rsatilganidek `site` nazoratchisi ichida `entry` amalini yarating.\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\nuse app\\models\\EntryForm;\n\nclass SiteController extends Controller\n{\n    // ...yaratilgan kod...\n\n    public function actionEntry()\n    {\n        $model = new EntryForm();\n\n        if ($model->load(Yii::$app->request->post()) && $model->validate()) {\n            // $model ichidagi ma'lumotlar tekshirishdan muvaffaqiyatli o'tgan\n\n            // endi esa biror $model bilan mantiq...\n \n            return $this->render('entry-confirm', ['model' => $model]);\n        } else {\n            // Sahifaga kirilgan holatda yoki hato mavjud bo'ganida\n            return $this->render('entry', ['model' => $model]);\n        }\n    }\n}\n```\n\nAmal `EntryForm` nusxasini yaratadi. Keyin esa u `$_POST` dan kelgan ma'lumotlar bilan modelni to'ldirayapti, u ma'lumotlar Yiida\n[[yii\\web\\Request::post()]] usulu orqali olinadi. Agar model muvaffaqiyatli to'ldirilsa, foydalanuvchi ma'lumotlarni shakldan foydalanib\njonatib ma'lumotlar shu orqali kelganini nazarda tutayapmiz, keyin ma'lumotlar tekshirish uchun [[yii\\base\\Model::validate()|validate()]]\nusuli ishlatilayapti.\n\nAgar hammasi yaxshi o'tsa, amal `entry-confirm` ko'rinishini qaytaradi, u esa foydalanuvchi tomonidan kiritilgan ma'lumotlarini ko'rsatadi.\nAks holda esa `entry` ko'rinishi qaytariladi, u esa HTML-shaklni va xatolarni chiqarib beradi, agar ular bor bo'lsa.\n\n> Info: `Yii::$app` yagona global xossasi Yiining nusxasini o'z ichiga oladi\n[ilova](structure-applications.md) (singleton). Bir vaqtni o'zida u [Service Locator](concept-service-locator.md) ham hisoblanadi,\nundan esa quydagi komponentlar bilan foydalanish mumkin  `request`, `response`, `db` va boshqa. Yuqorida ko'rsatilgan kodda `$_POST`\nmassivini qabul qilish uchun biz `request` komponentini ishlatdik.\n\n\nKo'rinish yaratilishi <span id=\"creating-views\"></span>\n----------------------------------------------------\n\nYakunda biz `entry-confirm` va `entry` ko'rinishlarini yaratamiz, ular esa yuqorida yozilgan `entry` amali orqali ma'lum bir xolatlarda qaytariladi.\n\nKo'rinish `entry-confirm` kiritilgan email va ismni ko'rsatadi. U `views/site/entry-confirm.php` fayli ichiga saqlangan bo'lishi kerak.\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n<p>Siz quydagi ma'lumotlarni kiritingiz:</p>\n\n<ul>\n    <li><label>Name</label>: <?= Html::encode($model->name) ?></li>\n    <li><label>Email</label>: <?= Html::encode($model->email) ?></li>\n</ul>\n```\n\nKo'rinish `entry` HTML-shaklni yasaydi. U quydagi faylga saqlangan bo'lishi kerak `views/site/entry.php`.\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n?>\n<?php $form = ActiveForm::begin(); ?>\n\n    <?= $form->field($model, 'name') ?>\n\n    <?= $form->field($model, 'email') ?>\n\n    <div class=\"form-group\">\n        <?= Html::submitButton('Jonatish', ['class' => 'btn btn-primary']) ?>\n    </div>\n\n<?php ActiveForm::end(); ?>\n```\n\nHTML-shaklni yasash uchun juda mukammal [vidjet](structure-widgets.md) [[yii\\widgets\\ActiveForm|ActiveForm]] ishlatilmoqda.\nQuydagi usular `begin()` va `end()` shaklni ochilish va yopilish teglarini yasaydi. Bular orasida esa ma'lumotlar kiritilishi uchun\nkerak bo'lgan maydonlar [[yii\\widgets\\ActiveForm::field()|field()]] yaratilmoqda . Birinchi \"name\", ikkinchisi esa \"email\".\nKeyin esa shaklni ma'lumotlarni jonatish uchun tugma yasalishiga javob beradigan usul [[yii\\helpers\\Html::submitButton()]] chaqirilmoqda.\n\n\nKeling, sinab ko'raylik <span id=\"trying-it-out\"></span>\n--------------------------------------\n\nIsh jarayonida yaratilgan bor narsani ko'rish uchun, browserni ochib quydagi URLni kiritaylik:\n\n```\nhttps://hostname/index.php?r=site%2Fentry\n```\n\nSiz ikkta maydoni o'z ichiga olgan shaklni ko'rishingiz mumkin. Har bir maydon oldida esa yorlig' bor, u yorlig'lar, qanaqa\nma'lumotlar kiritilishi kerak bo'lishi haqida ma'lumot saqlaydi.\nAgar siz ma'lumotlarni kiritmasdan jonatish tugasiga bosangiz yoki email noto'g'ri kiritsangiz, siz har bir maydon oldida hatolarni ko'rishingiz mumkin bo'ladi.\n\n![Hatolar bilan shakl](images/start-form-validation.png)\n\nMa'lumot kiritilib jonatilgandan so'ng esa, siz hozirgina kiritilgan ma'lumotlar sahifasini ko'rasiz.\n\n![Kiritilgan tasdiqlangan ma'lumotlar](images/start-entry-confirmation.png)\n\n\n\n### Bu qandey ishlayapti? <span id=\"magic-explained\"></span>\n\nSiz, bu HTML-shakl aslida qandey qilib ishlayotganini haqida savollar borligi bo'lishi mumkin. Butun jarayon ozgina murakkab ko'rinishi mumkin:\nmaydonlar oldida yorlig'lar, ma'lumotlar noto'g'ri kiritilganda hatolar va bularni hammasi sahifa qayta yuklanmasdan ishlayotganiga ajablanishingiz mumkin.\n\nHa albatta, ma'lumotlar tekshirishi aslida Javascript tarafida ham Serverda ham amalga oshirilmoqda.\n[[yii\\widgets\\ActiveForm]] judda yaxshi o'langan, siz `EntryForm`da kirigan qoidalarni olish uchun.\nUlarni Javascript kodga aylantirib va tekshirish jarayonida ishlatiladi. Agar browserda Javascript o'chsa tekshirish \nserver tarafda ham `actionEntry()` usulida ko'rsatilganidek o'tmoqda. Bu esa foydalanuvchi tomonidan kiritilgan ma'lumotlar ishonchli ekanliginini ko'rishimiz mumkin.\n\nMoydonlar uchun yasalgan yorlig'lar xossalar nomlari assosida quydagi usul `field()` orqali yaratilmoqda. Misol uchun, yasalgan `Name` yorlig'i `name` xossasi uchun shaklangan. Siz yorlig'larni quydagicha o'zgartirishingiz mumkin:\n\n```php\n<?= $form->field($model, 'name')->label('Sizning ismingiz') ?>\n<?= $form->field($model, 'email')->label('Sizning elektron qutingiz') ?>\n```\n\n> Info: Yiida juda ham ko'p vidjetlar bor, ular sizga tez va murakkab ko'rinishlarni yasashga yordam beradi.\n  Vijetlarni yasash judda oson va sodda ekanligini keyin (oldinda) bilishingiz mumkin. Ko'rinishdagi narsalarni ko'p narsani vijetlarga chiqarsa bo'ladi, \n  nimaga? keyinchalik ham boshqa joylarda ishlatish uchun, bu esa ishni soddalashtiradi.\n\nXulosa <span id=\"summary\"></span>\n-----------------------------\n\nBu bo'limda siz MVC-arxitekturasidagi xar bir qismini ishlatib ko'rdingiz.Siz foydalanuvchidan kelgan ma'lumotlarni qabul qilib\ntekshirish uchun, model klasslarini yaratishni o'rgandingiz.\n\nShuningdek, siz foydalanuvchidan ma'lumotlarni qabul qilish va ularni ko'rsatishni o'rgandingiz. Aslida bu ish jarayonida ma'lum bir\nvaqtni talab qilishi mumkin. Yii esa juda mukammal vidjetlarni taqdim qiladi, ular esa sizga ish jarayonini tezlashtirishni yordam beradi.\n\nKeyingi bo'limda siz ma'lumotlar ombori bilan ishlashni o'rganasiz, bu esa juda ko'p ilovalarda talab qilinadi.\n\n"
  },
  {
    "path": "docs/guide-uz/start-hello.md",
    "content": "«Salomlashamiz»\n================\n\nUshbu bo'limda biz \"Salom\" so'zlari bilan yangi sahifani qanday yaratishni ko'rib chiqamiz. Siz masalani hal qilish jarayonida\n[nazoratchi amalini](structure-controllers.md) va [ko`rinishi](structure-views.md):ni yaratasiz\n\n* Web-ilova so'rovni bajaradi va nazoratni tegishli amalga o'tkazadi;\n* Amal, o'z navbatida, \"Salom\" deb nomlangan ko'rinishni foydalanuvchiga ko'rsatadi.\n\nUshbu qo'llanmani o'rganish jarayonida siz ushbu narsalarni o'rganib chiqasiz:\n\n* So'rovlarga javob berish uchun qanday [amalni](structure-controllers.md) yaratish kerak;\n* Web ilovaga javob mazmunini shakllantirish uchun qanday qilib [ko`rinish](structure-views.md), yaratish kerak;\n* Web ilova so'rovlarni qanday  qilib [amalga](structure-controllers.md). yuboradi.\n\n\nAmal yaratish <span id=\"creating-action\"></span>\n------------------------------------------------\n\n\nBizning vazifamiz uchun so'rovdan `message` parametrini o'qiydigan va uning qiymatini foydalanuvchiga ko'rsatadigan `say` deb ataladigan [amal](structure-controllers.md) kerak.\nAgar so'rovda `message` parametri bo'lmasa, [amal](structure-controllers.md) o'z novbatida \"Salom\" matnini qaytaradi.\n> Info: [amal](structure-controllers.md) to'g'ridan-to'g'ri foydalanuvchi tomonidan boshqarilishi va [nazoratchilar](structure-controllers.md) ichida guruhlarga bo'linishi mumkin. Amal natijasi foydalanuvchiga qaytariladi.\n\nAmallar [nazoratchilar](structure-controllers.md) (tomonidan) ichida e'lon qilinadi.\nOddiylik uchun siz mavjud bo'lgan SiteController nazoratchisi ichida `say` amalini yaratishingiz mumkin, Ushbu nazoratchi fayli `controllers/SiteControllers.php` yo'li bo`yicha joylashgan\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    // ...oldindan mavjud bo'lgan kod...\n\n    public function actionSay($message = 'Salom')\n    {\n        return $this->render('say', ['message' => $message]);\n    }\n}\n```\n\nQuyidagi kodda `say` amali `SiteController` sinfi (klassi) `actionSay` usuli (metodi) e'lon qilinmoqda.\nYii amallari usullari (metodlari) oldidan `action` qo'shiladi bu oddiy usullarni (metodlarni) ajratish uchun prefiksidan foydalanadi.\n\n> Info: Amalar nomlari kichik harflar berilishi kerak.Agar identifikator (nom) bir necha so'zdan iborat bo'lsa, ular\n  chiziq (minus -) bilan ajiraladi, yani `create-comment`. Amallar usullarining (metodlar) nomlari defislarni olib tashlash yo'li bilan olinib, har bir so'zni katta harfga aylantiradi va `action` prefiksi qo'shadi.\n  Misol uchun, amal nomi `create-comment` quydagi usullga to'g'ri keladi `actionCreateComment`.\n\nYukoridagi amal - `$message` parametrni qabul qiladi u esa, oldindan (agar hech narsa kiritilmasa) `\"Salom\"` soziga teng. Web ilova sorovni qabul qilib\n`say` amali unga javob berishini aniqlaganda, amal parametrlari va so'rovdan kelgan ma'lumotlar parametrlar ichida sorovdagi nomlarga teng bo`lgani topib bir xil ma'lumot bilan to'ldiradi.\n\nAmal usulida (metodida), kerakli ko'rinishni ko'rsatish uchun [[yii\\web\\Controller::render()|render()]] usuli (metodi) ishlatiladi. Xabarni foydalanuvchiga ko'rsatish uchun, ko'rinishga `message` parametri beriladi.\nko'rinish yaratgan ma'lumotni `return` orqali web ilovaga qaytaradi, u o'z novbatida ma'lumotni foydalanuvchiga qaytaradi.\n\n\nKo'rinish yaratish<span id=\"creating-view\"></span>\n---------------------------------------------------\n\n[ko'rinish](structure-views.md) - bu foydalanuvchi uchun javob qaytarish skriptlar. Yuqorida dastur uchun siz `say` ko'rinishini yaratishingiz kerak, u o'z ichiga `message` o'zgaruvchini uni chaqirgan amaldan qabul qiladi:\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n<?= Html::encode($message) ?>\n```\n\n`say` ko'rinishi `views/site/say.php` faylida saqlanishi kerak. Ushubu usul (metod) amalda chaqirilganda [[yii\\web\\Controller::render()|render()]]\n, u quydagi yo'l bo'yicha ko'rinish topishga harakat qiladi `views/ControllerID/ViewName.php`.\n\nAytib o'tish keraki yuqorida kodda `message` [[yii\\helpers\\Html::encode()|HTML himoyasi]] orqali filtrlanadi.\nBuni ishlatish shartdir chunki ma'lumot foydalanuvchidan kirib kelmoqda, u esa o'z yo'lida [XSS ziyon](https://ru.wikipedia.org/wiki/%D0%9C%D0%B5%D0%B6%D1%81%D0%B0%D0%B9%D1%82%D0%BE%D0%B2%D1%8B%D0%B9_%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D0%B8%D0%BD%D0%B3)\no'tkazishi o'tkazishi mumkin Javascript-skripti orqali.\n\nSiz `say` ko'rinishi HTML teglar, matn yoki PHP-kod bilan to'ldirishingiz mumkin. Aslini olganda `say` skripti [[yii\\web\\Controller::render()|render()]] orqali chaqirilayotgan sodda PHP-skript bo'lib chiqayotganini ko'rishimiz mumkin. Skript tomonidan shakilangan ma'lumot esa foydalanuvchiga qaytariladi.\n\n\nIshlatib ko'ramiz <span id=\"trying-it-out\"></span>\n--------------------------------------\n\nYuqorida aytib o'tilgan amalrni va amalar ko'rinishlarini yaratib bo'ganingizdan so'ng siz usu URL bo'yicha o'tishingiz mumkin:\n\n```\nhttps://hostname/index.php?r=site%2Fsay&message=Salom+dunyo\n```\n\n![Salom, dunyo](images/start-hello-world.png)\n\nSizga \"Salom, dunyo\" matni chiqishi kerak. Header (yuqori bo'lim) va  Footer (pastki bo'lim) hamma sahifalardek chiqadi.\nAgar siz `message` kiritmasangiz, siz «Salom» matnini ko'rishingiz mumkin. Bunaqa mantiq chiqishi sababi shundagi, `actionSay` usuli (metod)dagi `message` parametri oldindan \"Salom\" matni berilgan.\n\n> Info: Yangi sahifa hamma ishlatayotgan Header (yuqori bo'limi) va Footer (pastki bo'lim) ishlatadi, chunki \n  [[yii\\web\\Controller::render()|render()]] usuli `say` ko'rinishini o'zi [maketga](structure-views.md) `views/layouts/main.php`  topib qo'yadi.\n\nParametr `r` qo'shimcha tushuncha ta'lab qiladi. U [yo'nalishlar (route)](runtime-routing.md) bilan bo'liqdir va nomi boyicha kerakli amalni topib chiqaradi. Uning formati `ControllerID/ActionID`. Web-ilova so'rovni qabul qilishni boshlaganida u `r` paraterni tekshirishni boshlaydi va `ControllerID` ni ishlatadi, va nomi bo'yicha so'ralayotgan nazoratchini topib, `ActionID` bo'yicha shu topilgan nazoratchi ichidan so'raloyotgan amalni topib chiqaradi.\nBizning misolimiz bo'yicha `site/say` so'rovi boyicha `SiteController` nazoratchisi ichidan `say` amalini chiqaradi.\nNatijada esa, so'rovni qabul qilish `SiteController::actionSay()` topshiriladi.\n\n> Info: Nazoratchilar ham hudi amalar kabi web-ilovada yagona nomga (identifikator) ega.\n  Nazoratchilar nomlash tartibi hudi amalardaqadir. Klasslar nomlari So'larga bo'linib defizlar orqali aniqlanadi, so'zlarni birinchi katta harf bilan yozib o'tib va oxiriga `Controller` qo'shib qidirishni boshlaydi. Misol uchun, nazoratchi `post-comment` nomi `PostCommentController` klassiga teng bo`ladi.\n\n\nXulosa <span id=\"summary\"></span>\n-----------------------------\n\nBu bo'limda biz nozaratchilar va ularning ko'rinishlari mavzusini MVC-tuzilmasi ichida yoritib berishga harakat qildik. Siz nazoratchi qismi bo'lgan so'rovlarni qabul qiluvchi amalni yasab so'rovga kerakli ko'rinishni javob tariqasida shakilantirishni ko'rdingiz. Ushbu misolda hech ham modellar ishlatilmadi, chunki misolar juda ham sodda bo'lgan `message` parametri bo'yicha uni foydalanuvchiga qaytargan.\n\nShuningdek siz `yo'nalishlar (route)` tartibi bilan tanishtingiz, bu esa foydalanuvchi so'roviga kerakli nazoratchini topib foydalanuvchiga javob berish jarayonida juda ham muhim o'ringa egadir.\n\nKeyingi bo'limda siz modelar nimaligi va uni qandey yaratish kerakligini va HTML-shaklrar bilan ishlashni o'rganasiz.\n\n"
  },
  {
    "path": "docs/guide-uz/start-installation.md",
    "content": "Yiini o'rnatish <span id=\"installing-from-composer\"></span>\n==============\n\nSiz Yii'ni ikki usulda o'rnatishingiz mumkin: [Composer](https://getcomposer.org/) - dan foydalanishingiz mumkin yoki arxivdan yuklab olib ishlatishingiz mumkin. Birinchi usul afzalroq bo'ladi, chunki u yangi [kengaytmalar](structure-extensions.md) o'rnatish yoki Yii'ni bitta buyruq bilan yangilash imkonini beradi. \n\n\n> Eslatma: Yii 1 dan farqli o'laroq, Yii2ni standart yo`li bilan o`rnatsangiz siz ham shabloni va freymvorkni o`rnatasiz. \n\n\nComposer bilan o'rnatish usuli<span id=\"installing-via-composer\"></span>\n-----------------------\n\n### Composer-ni o'rnatish \n\nAgar Composer xali o'rnatilmagan bo'lsa, uni [getcomposer.org](https://getcomposer.org/download/) saytiga kirib ko'rsatmalarga rioya qilib o`rnatishingiz mumkin yoki quyidagi usullardan birini amalga oshirishingiz mumkin. Linux yoki Mac-da quyidagi buyruqni bajaring: \n\n```bash\ncurl -sS https://getcomposer.org/installer | php\nmv composer.phar /usr/local/bin/composer\n```\nWindowsda [Composer-Setup.exe](https://getcomposer.org/Composer-Setup.exe)-ni yuklab oling va ishga tushiring.\n\n\nMuammo bo'lsa, Composer hujjatida [\"Muammo bartaraf qilish\" bo'limini o'qing]((https://getcomposer.org/doc/articles/troubleshooting.md)). Agar siz faqat Composer dasturidan foydalanmoqchi bo'lsangiz, hech bo'lmasa [\"Foydalanish asoslari\"]((https://getcomposer.org/doc/01-basic-usage.md)) bo'limini o'qishni tavsiya etamiz. \n\nUshbu qo'llanma `composer` global tizim boyicha umumiy o'rnatilganligini ta'kidlaydi. Ya'ni, bu `composer` buyrug'i orqali mavjud. Agar local (ichki) katalogdan `composer.phar` foydalanayotgan bo'lsangiz, buyruqlar mos ravishda o'zgartiring. \n\nComposer o'rnatilgan bo'lsa, uni composer `self-update`. \n\n> Eslatma: O'rnatish vaqtida Yii Composer Github API orqali juda ko'p miqdordagi ma'lumotlarni yulaydi.\n>So'rovlar soni sizning loyihangizning boglanmalar soniga bog'liq bo'lib, **Github API** chegaralaridan oshib ketishi mumkin. Agar shunday bo'lsa qolsa, Composer Githubdan login va parolni so'raydi. Bu Github API uchun token olish uchun zarur.  Internet tezligi tez bo'lsa Composer xatolikni bartaraf qilishdan oldin ham sodir bo'lishi mumkin, shuning uchun Yii'ni o'rnatishdan avval kirish uchun tokeni o'rnatish tavsiya etamiz.\n>Ko'rsatmalar [Composer qo`lanmasida Github API'sining identifikatorlari bo'yicha](https://getcomposer.org/doc/articles/troubleshooting.md#api-rate-limit-and-oauth-tokens) taqdim etiladi.\n\n### Yiini o'rnatish\n\n```bash\ncomposer create-project --prefer-dist yiisoft/yii2-app-basic basic\n```\n\nUshbu buyruq Yining oxirgi ishlaydigan versiyasini `basic` katalogiga o'rnatadi. Agar xohlasangiz, boshqa katalog nomini tanlashingiz mumkin. \n\n> Info: Agar `composer create-project` buyrug'i yaxshi ishlamasa , Composer hujjatining [\"Muammo bartaraf qilish\"](https://getcomposer.org/doc/articles/troubleshooting.md) bo'limiga murojaat qiling . Yozib otilgan boshqa odatiy xatolar mavjud. Xatolikni bartaraf qilganingizdan so'ng, `basic` katalogda `composer update` ishga tushiring.\n\n> Maslahat: Agar siz Yii ning eng so`ngi tekshirilmagan taxririni o'rnatmoqchi bo'lsangiz, [stability](https://getcomposer.org/doc/04-schema.md#minimum-stability) sozlamasini ozgartirib quydagi quyidagi buyruqni ishlatishingiz mumkin::\n>\n> ```bash\n> composer create-project --prefer-dist --stability=dev yiisoft/yii2-app-basic basic\n> ```\n>\n> Ishlab serverlarida Yii'ning tekshirilmagan taxrirlarini foydalanmaslikka harakat qiling, chunki bu taxrir tekshirilmagan kodi to'satdan xatolik kelib chiqishi mumkin. \n\n\nArxivdan o'rnatish  <span id=\"installing-from-archive-file\"></span>\n-------------------------------\n\nYining arxivdan o'rnatilishi uch bosqichdan iborat:\n\n1. Yiiframework.com dan [arxivni](https://www.yiiframework.com/download/) yuklab oling.\n2. Yuklangan arxivni Internetdan kirib boladigan katalogiga tashlab, arxivni oching. \n3. `config/web.php` `cookieValidationKey` (Composer orqali o'rnatilganda, bu avtomatik tarzda amalga oshiriladi) uchun maxsus kalitni qo'shing:\n\n```php\n// !!! insert a secret key in the following (if it is empty) - this is required by cookie validation\n'cookieValidationKey' => 'enter your secret key here',\n```\n\nBoshqacha o'rnatish sozlamalari  <span id=\"other-installation-options\"></span>\n--------------------------\n\nYuqorida, Yii'ni ishlatish uchun tayyor bo'lgan asosiy dastur sifatida o'rnatish bo'yicha ko'rsatmalar mavjud. Bu kichik loyihalar uchun yoki \"Yii\" ni o'rgana boshlaganlar uchun ajoyib variant. \n\nBunday o'rnatish uchun ikkita asosiy variant mavjud: \n\n* Agar sizga faqatgina freymvork kerak bolsa va dasturni noldan yaratmoqchi bo'lsangiz, «[Noldan ilovani yaratish](tutorial-start-from-scratch.md)» bo'limida ko'rsatilgan ko'rsatmalardan foydalaning.\n* Agar siz jamoaviy ish uchun juda kengaytirilgan taxriri bilan boshlamoqchi bolsangiz, [murakkab shabloni](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md) ishlatishingiz mumkin.\n\n\nO`rnatilganligini tekshirish <span id=\"verifying-installation\"></span>\n----------------------\n\nO`rnatilgandan so'ng, dastur quyidagi URLda mavjud bo'ladi: \n\n```\nhttp://localhost/basic/web/index.php\n```\n\nUshbu dasturni veb-serveringizning ildiz katalogidagi `basic` katalogiga o'rnatgan deb hisoblaydi, server ichki `(localhost)` ishlayapti. Uni oldindan sozlashingiz kerak bo'lishi mumkin. \n![O'rnatilgan Yii](images/start-app-installed.png)\n\nSizni \"Tabriklaymiz!\" Xush kelibsiz sahifasini ko'rishingiz kerak. Agar shunday bo'lmasa - Yii talablarini quyidagicha tekshiring: \n\n* Browserda quydagi sahifani oching `http://localhost/basic/requirements.php`\n* Yoki quydagi buyrug'ni terminalda bajaring: \n\n```bash\ncd basic\nphp requirements.php\n```\n\nFreymvork to'g'ri ishlashi uchun PHPni minimal talablarga javob beradigan tarzda sozlashingiz kerak. Asosiy talablardan biri bu PHP versiyasi 5.4 va undan yuqori bolishi kerak. Agar veb-ilovangiz ma'lumotlar bazasi bilan ishlayotgan bo'lsa , [PHP PDO kengaytmasini](https://www.php.net/manual/ru/pdo.installation.php) va tegishli drayverni (masalan, MySQL uchun pdo_mysql ) o'rnatishingiz kerak.\n\n\nWeb-server sozlamasi<span id=\"configuring-web-servers\"></span>\n-----------------------\n\n> Info: Agar siz faqatgina freymvork bilan tanishishni boshlagan bo'lsangiz va uni ish  serveriga joylashtirmagan bo'lsangiz, ushbu bo'limni o'tkazib yuborishingiz mumkin. \n\nYuqoridagi ko'rsatmalarga muvofiq o'rnatilgan ilovalar Windows va Linux ostida PHP 5.4 va undan yuqori taxrirlarda o'rnatilgan [Apache](https://httpd.apache.org/) va [Nginx](https://nginx.org/) bilan ishlaydi.Yii 2.0 [HHVM](https://hhvm.com/) bilan ham mos keladi. Etiborli bo'ling, ba'zi hollarda, HHVM bilan ishlashda odatdagi PHPdan farq qiladi.\n\nIsh serverida siz `https://www.example.com/basic/web/index.php` dan `https://www.example.com/index.php` manziliga dastur URL manzilini o'zgartirishni xohlasangiz. \nBuni amalga oshirish uchun veb-server parametrlarida ildiz katalogini `basic/web` ga o'zgartiring. Bundan tashqari, [\"URL sozlamalari\"](runtime-routing.md) qismidagi malumotga ko'ra, `index.php` yashirishi mumkin. Keyinchalik Apache va Nginx ni qanday sozlashni ko'rsatamiz.\n\n> Info: Veb-serverning `basic/web` ildiz katalogini o'rnatib, siz ruxsat berilmagan kirish kodidan va `basic/web` sahifadagi ma'lumotlardan himoya qilasiz. Bu ilovani yanada xavfsiz holga keltiradi. \n\n> Info: Agar dastur veb-server sozlamalariga kirish imkoni bo'lmagan hosting bilan ishlayotgan bo'lsa, siz [\"Birgalikda Hosting ustida ishlash\"](tutorial-shared-hosting.md) bo'limida ko'rsatilganidek, ilovaning tuzilishini o'zgartirishingiz mumkin.\n\n\n### Ta'vsiya etilgan Apache sozlamalari <span id=\"recommended-apache-configuration\"></span>\n\nQuyidagilarni Apache `httpd.conf` yoki virtual sozlamalar faylga qo'shing. `path/to/basic/web` to'g'ri manzilga `path/to/basic/web` almashtirishni unutmang. \n\n```\n# Ildiz kotalogini o`rnatayapmiz \"basic/web\"\nDocumentRoot \"path/to/basic/web\"\n\n<Directory \"path/to/basic/web\">\n    RewriteEngine on\n\n    #Agar so`ralayotgan fayl yoki katalogiy bor bo`lsa unga to`g`ridan to`g`ri murojat qilamiz \n    RewriteCond %{REQUEST_FILENAME} !-f\n    RewriteCond %{REQUEST_FILENAME} !-d\n    # Agar yo`q bo`lsa index.php ga o`tqazib yuboramiz.\n    RewriteRule . index.php\n\n    # ...boshqa sozlamalar...\n</Directory>\n```\n\n\n### Tavsiya etilgan Nginx sozlamalari <span id=\"recommended-nginx-configuration\"></span>\n\nPHP Nginx uchun [FPM SAPI](https://www.php.net/manual/ru/install.fpm.php) sifatida o'rnatilishi kerak. Quyidagi [Nginx](https://wiki.nginx.org/) sozlamalaridan foydalaning va `basic/web` va `mysite.test` to'g'ri manzilini hostname-ga almashtirishni  va `path/to/basic/web` ni almashtirishni unutmang. \n\n```\nserver {\n    charset utf-8;\n    client_max_body_size 128M;\n\n    listen 80; ## listen for ipv4\n    #listen [::]:80 default_server ipv6only=on; ## слушаем ipv6\n\n    server_name mysite.test;\n    root        /path/to/basic/web;\n    index       index.php;\n\n    access_log  /path/to/project/log/access.log;\n    error_log   /path/to/project/log/error.log;\n\n    location / {\n        # Barcha so'rovlarni indeks bo'lmagan katalog va fayllarga yo'naltiramiz. Index.php\n        try_files $uri $uri/ /index.php?$args;\n    }\n\n    # Yii-ni ishlamaslik uchun mavjud bo'lmagan statik fayllarni chaqirish uchun quyidagi qatorlarni belgilang\n    #location ~ \\.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ {\n    #    try_files $uri =404;\n    #}\n    #error_page 404 /404.html;\n\n    location ~ \\.php$ {\n        include fastcgi.conf;\n        fastcgi_pass   127.0.0.1:9000;\n        #fastcgi_pass unix:/var/run/php5-fpm.sock;\n    }\n\n    location ~ /\\.(ht|svn|git) {\n        deny all;\n    }\n}\n```\n\nUshbu konfiguratsiya yordamida `php.ini` `cgi.fix_pathinfo=0` ni `stat()` tizimiga keraksiz tizim chaqiruvlariga yo'l qo'ymaslik uchun o'rnating. \n \nHTTPS-dan foydalanib, `fastcgi_param HTTPS on;` ni belgilash kerak `fastcgi_param HTTPS on;` Shunday qilib Yii xavfsiz ishlashini aniq belgilashi mumkin. \n"
  },
  {
    "path": "docs/guide-uz/start-looking-ahead.md",
    "content": "Oldinga nigoh\n=============\n\nOxir oqibat siz Yii to'liq va ko'p ishlatiladigan funksiyalarini bildingiz ulardan HTML-shakl yordamida foydalanuvchidan, ma'lumotlar omboridan ma'lumotlarni olib ularni sahifalarga bo'lib chiqarishni va yana siz  [Gii](https://github.com/yiisoft/yii2-gii/blob/master/docs/guide/README.md) bilan\navtomatik tarzda kod yaratishni o'rgandingiz, bu esa HTML-shaklni to'ldirishdek dasturlashni juda ham yengil va sodda mashg'ulotga aylantiradi. Bu bo'limda biz sizga Yii imkoniyatlarini umumiylashtirib beramiz, bu esa sizga Yiini mukammalroq ishlatishingizga yordam beradi.\n\n* Qo'llanma\n    - To'liq qo'llanma:\n      uni nomidan kelib chiqan holda, qo'llanma Yii ni ishlashi haqida, \n\t  to'liq ma'lumot berib uni qandey yaxshiroq qo'llashni yoritib beradi.\n\t  Bu Yii haqida juda ham muhim darslik hisoblanadi, ushbu darslikni oqimasdan siz kod yozishga kirishmasangiz to'g'ri bo'ladi.\n    - Klasslar tariflari:\n      Yiidagi har bir klassni ishlashini yozib o'tadi. \n\t  U bilan siz kod yozganingizda biror bir klassning hossalari yoki usulari to'g'risida ma'lumot olishingiz mumkin.\n    - Wiki-maqolalar:\n      Yii foydalanuvchisi tomonidan o'z tajribasi assosida. \n\t  Ulardan ko'pi Yii retsepti assosida yozilgan bo'lib, ular Yiining ma'lum bir masalalarini hal qilib beradi. \n\t  Shuni aytish keraki ularning sifati qo'lanmadagi yozilgan ma'lumotdaqa yaxshi yoritilib berilgani ham bor\n\t  Ularni shunisi yaxshiki ular mavzuni to'liq va amaliy yoritib berib sizga tayyor yechim berishi ham mumkin\n    - Kitoblar\n* [Kengaytmalar](https://www.yiiframework.com/extensions/):\n  Yiining foydalanuvchilar tomonidan yaratilgan minglab kengaytmalar ombori bor \n  ular ilovangizga ulashingiz va ishlanmani juda ham tez oborishingizga yordam beradi\n* Jamoa\n    - [Forum](https://forum.yiiframework.com/)\n    - [Chat Gitter](https://gitter.im/yiisoft/yii2/rus)\n    - [GitHub](https://github.com/yiisoft/yii2)\n    - [Facebook](https://www.facebook.com/groups/yiitalk/)\n    - [Twitter](https://twitter.com/yiiframework)\n    - [LinkedIn](https://www.linkedin.com/groups/yii-framework-1483367)\n"
  },
  {
    "path": "docs/guide-uz/start-workflow.md",
    "content": "Ilovani ishga tushirish\n====================\n\n`https://hostname/basic/web/index.php` o'rnatganingizdan so'ng, asosiy dastur `https://hostname/basic/web/index.php` URL manzilida yoki `https://hostname/index.php` orqali Veb-server sozlamalariga qarab mavjud bo'ladi. Ushbu bo'lim - kodni tashkil qilishning umumiy ta'rifi, o'rnatilgan funksionallik va murojaatlar bilan ishlash. \n\n> Info: Ushbu qo'llanmada Yii `basic/web` katalogiga o'rnatilganini inobatga oladi va u o'z navbatida veb-server sozlamalaridagi ildiz katalogi sifatida o'rnatiladi. Natijada `https://hostname/index.php` URL manziliga kirish orqali `basic/web` joylashgan ilovaga kirishingiz mumkin. Boshlang'ich konfiguratsiya jarayonida batafsil ma'lumotni [Yii-ni o'rnatish bo'limida](start-installation.md) topishingiz mumkin.\n\nFreymvorkdan farqli o'laroq, dastur o'rnatilgandan so'ng, u sizniki bolib qoladi. Siz o`zingizni kodingizni o'zingiz xohlagan tarzda o'zgartirishingiz mumkin. \n\nFunksionallik <span id=\"functionality\"></span>\n---------------\n\nOddiy dastur uchun o'rnatilgan shablon to'rtta sahifadan iborat:\n\n* `https://hostname/index.php` URL manziliga `https://hostname/index.php` ko'rsatiladigan asosiy sahifa \n* \"About\" (\"Biz haqimizda\") sahifasi\n* \"Kontakt\" sahifasida qayta aloqa shakli joylashgan ushbu shakl orqali u dasturchiga murojaat qilishi mumkin \n* \"Kirish\" sahifasida avtorizatsiya shakl ko'rsatiladi. Foydalanuvchi nomi / parol bilan \"login / admin\" bilan tizimga kirishingiz mumkin. Asosiy menyu \"Login\" menyusi \"Logout\" menyusiga o`zgarishiga etibor bering. \n\nUshbu sahifalar Sayt ustuni va Sayt pastini ishlatadi. \"Header\" da foydalanuvchining sayt orqali o'tadigan asosiy menyusi. \"Saytning pastki qismida\" da - mualliflik huquqi va umumiy ma'lumot joylashgan.\n\nOynaning eng quyi qismida tizim xabarlarini Yii - log, disk raskadrovka ma'lumotlarini, xato xabarlarini, ma'lumotlar bazasi so'rovlarini va boshqalarni ko'rasiz. Ushbu ma'lumotlarning chiqishi [ichki xotira nazoratchisi](https://github.com/yiisoft/yii2-debug/blob/master/docs/guide/README.md) tomonidan nazorat qilinadi, u dasturni bajarish jarayoni haqidagi ma'lumotni qayd qiladi va ko'rsatadi. \n\nVeb-ilovaga qo'shimcha ravishda, ilovaning asosiy katalogida joylashgan `yii` deb nomlangan konsoli script mavjud. Ushbu skript fon vazifalarni bajarish va dasturni saqlash uchun ishlatilishi mumkin. Bularning barchasi [Konsol buyruqlari](tutorial-console.md) bo'limida yozib o`tilgan.\n\nYii web-ilovasi tuzilmasi <span id=\"application-structure\"></span>\n---------------------\n\nQuyidagida veb-ilovaning tuzilmasi asosiy kataloglari va fayllari ro'yxati berilgan (ilovaning `basic` katalogga o'rnatilgan deb hisoblaymiz): \n\n```\nbasic/                  ilovaning ildiz katalogi\n    composer.json       Composer ishlatadigan fayl\n    config/             Sozlamalar (konfiguratsion) fayl\n        console.php     Konsol ilovasining sozlamalari\n        web.php         Veb-ilovasining sozlamalari\n    commands/           konsol ilovalarining klasslari joylashgan\n    controllers/        nazoratchilar\n    models/             modellar\n    runtime/            Ish jarayonida yii yasaydigan fayllar loglar va boshqa fayllar\n    vendor/             Composer paketlarini tashkil qilib Yii ni o`z ichiga oladi\n    views/              Web-ilovaning ko`rinish faylari\n    web/                Web-ilovaning ildiz katalogi,Veb-dan olish mumkin bo`lgan faylarni tashkil etadi.\n        assets/         Ilova orqali ishlatiladigan scriptlar (js, css)\n        index.php       Yii ga kirish joyi. Yii ishlashni boshlaydi\n    yii                 Konsol buyruqlarini ishlatadigan Yii scripti\n```\n\nUmuman olganda, Yii ilovasini ikki `basic/web` faylga bo'lish mumkin: `basic/web` joylashgan faylar va boshqa kataloglarda joylashgan faylar. Birinchisi `(basic/web)` internet orqali kirganda ochilishi kerak, ikkinchisi internet orqali ko'rinishi kerak bo'lmagan faylar, chunki ular ish faylari.\n\nYii-da [MVC-arxetekturasi](https://ru.wikipedia.org/wiki/Model-View-Controller) asosida faylar va kataloglar joylashgan. [models](structure-models.md)katalogida Modellar mavjud , [Ko'rishlar](structure-views.md) ko'rinishlarda joylashgan va barcha dastur [Nazoratchilar](structure-controllers.md) katalogida. \n \nQuyidagi diagrammada dasturning ichki tuzilmasi ko'rsatilgan. \n\n![Ilovaning ichki tuzilmasi](images/application-structure.png)\n\nHar bir dasturda Yii [ilovaga](structure-applications.md) kirish nuqtasi bor, web/index.php - web/index.php ishlash uchun mavjud bo'lgan yagona PHP skriptidir. Kiruvchi so'rovni qabul qiladi va [ilovani bittasini](structure-applications.md) yaratadi. [Ilova komponentlardan](concept-components.md) foydalangan holda qabul qilingan so'rovlarni bajaradi va controllerlarga so'rov yuboradi. [Vidjetlar](structure-views.md) ko'rinishda dinamik sayt interfeyslarini yaratish uchun ishlatiladi.\n\nFoydalanuvchi so'rovining ishlash tartibi <span id=\"request-lifecycle\"></span>\n-----------------\n\nDiagrammada dasturning so'rovni qanday qabul qilib ishlashini ko'rsatadi. \n\n![Жизненный цикл запроса](images/request-lifecycle.png)\n\n1. Foydalanuvchi web-ilovaning  [kirish nuqtasiga](structure-entry-scripts.md) `web/index.php` ga murojat qilmoqda.\n2. Dastur [sozlamalarni](concept-configurations.md) yuklamoqda  va so'rovni qabul qilish uchun [web-ilovadan](structure-applications.md) nusxa yaratmoqda.\n3. Dastur [yo'nalishni](runtime-routing.md) aniqlashtirib [so'rovni](runtime-requests.md) qabul qilishni boshlaydi.\n4. Dastur ilova nusxasini yaratmoqda so'rovni qabul qilish uchun [nazortchi](structure-controllers.md) chaqirilmoqda.\n5. Nazoratchi esa o`z novbatida [amalni](structure-controllers.md) yaratadi va unga filtrlarni ishlatadi.\n6. Filtrdan birortasi ham ishtan chiqsa amalgacha so'rov bormaydi.\n7. Agar hamma filterlar javob qaytarsa so'rov amalga borib javob qaytaradi.\n8. Amal model ma'lumotlarini yuklaydi. Ma'lumotlar omborini ishlatishi ham mumin.\n9. Amal ko'rinish ma'lumotlarini chiqarib beradi (shuningdek modeldan ham).\n10. Shakilangan ma'lumot esa ilovaga [javob](runtime-responses.md) komponentni tariqasida qaytariladi.\n11. \"javob\" komponentni esa foydalanuvchi browseriga javob qaytaradi.\n\n"
  },
  {
    "path": "docs/guide-uz/structure-controllers.md",
    "content": "Kontrolyor\n===========\n\nKontrolyor [MVC](https://ru.wikipedia.org/wiki/Model-View-Controller) arxitekturasining bir qismi hisoblanadi. Bu [[yii\\base\\Controller]] klassidan voris qilib yaratilgan klass obyektlaridir, ushbu obyektlar so'rovlarni (request) qayta ishlash va javoblarni (response) tayyorlash uchun mo'ljallangan. [Ilova (application)](structure-applications.md) so'rovlarni qayta ishlagandan so'ng, so'rovlarga mos kontrolyor obyektlarini yaratadi va bu obyektlar kiruvchi ma'lumotlarni tahlil qiladi hamda ushbu ma'lumotlarni [model](structure-models.md)ga jo'natadi so'ng modeldan olingan natijalarni [namoyish (view)](structure-views.md)ga joylashtiradi va oxirgi natijani hosil qiladi.\n\n## Amallar <span id=\"actions\"></span>\n\nKontrolyorlar *amallar (action)*dan tashkil topadi, *amallar* asosiy bloklar hisoblanib, foydalanuvchilar amallarga murojaat qilishi mumkin va ma'lum buyruqlar bajarilib natija hosil qilinadi hamda natija foydalanuvchiga qaytariladi. Bitta kontrolyorda bitta va undan ortiq amal bo'lishi mumkin.\n\nQuyidagi misolda ikkita amaldan (`view` va `create`) tashkil topgan `post` kontrolyori keltirilgan:\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse app\\models\\Post;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\n\nclass PostController extends Controller\n{\n    public function actionView($id)\n    {\n        $model = Post::findOne($id);\n        if ($model === null) {\n            throw new NotFoundHttpException;\n        }\n\n        return $this->render('view', [\n            'model' => $model,\n        ]);\n    }\n\n    public function actionCreate()\n    {\n        $model = new Post;\n\n        if ($model->load(Yii::$app->request->post()) && $model->save()) {\n            return $this->redirect(['view', 'id' => $model->id]);\n        } else {\n            return $this->render('create', [\n                'model' => $model,\n            ]);\n        }\n    }\n}\n```\n\n\nВ действии `view` (определенном методом `actionView()`), код сначала загружает [модель](structure-models.md)\nсогласно запрошенному ID модели; Если модель успешно загружена, то код отобразит ее с помощью [представления](structure-views.md)\nпод названием `view`. В противном случае будет брошено исключение.\n\nВ действии `create` (определенном методом `actionCreate()`), код аналогичен. Он сначала пытается загрузить [модель](structure-models.md)\nс помощью данных из запроса и сохранить модель. Если все прошло успешно, то код перенаправляет браузер на действие `view` с ID только\nчто созданной модели. В противном случае он отобразит представление `create`, через которое пользователь может заполнить нужные данные.\n\n\n## Маршруты <span id=\"routes\"></span>\n\nКонечные пользователи обращаются к действиям через так называемые *маршруты*. Маршрут это строка, состоящая из следующих частей:\n\n* ID модуля: он существует, только если контроллер принадлежит не приложению, а [модулю](structure-modules.md);\n* ID контроллера: строка, которая уникально идентифицирует контроллер среди всех других контроллеров одного и того же приложения\n  (или одного и того же модуля, если контроллер принадлежит модулю);\n* ID действия: строка, которая уникально идентифицирует действие среди всех других действия одного и того же контроллера.\n\nМаршруты могут иметь следующий формат:\n\n```\nControllerID/ActionID\n```\n\nили следующий формат, если контроллер принадлежит модулю:\n\n```php\nModuleID/ControllerID/ActionID\n```\n\nТаким образом, если пользователь запрашивает URL `https://hostname/index.php?r=site/index`, то `index` действие в `site` контроллере будет вызвано.\nСекция [Маршрутизация](runtime-routing.md) содержит более подробную информацию о том как маршруты сопоставляются с действиями.\n\n\n## Создание контроллеров <span id=\"creating-controllers\"></span>\n\nВ [[yii\\web\\Application|Веб приложениях]], контроллеры должны быть унаследованы от [[yii\\web\\Controller]] или его потомков.\nАналогично для [[yii\\console\\Application|консольных приложений]], контроллеры должны быть унаследованы от [[yii\\console\\Controller]] или\nего потомков. Следующий код определяет `site` контроллер:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n}\n```\n\n\n### ID контроллеров <span id=\"controller-ids\"></span>\n\nОбычно контроллер сделан таким образом, что он должен обрабатывать запросы, связанные с определенным ресурсом.\nИменно по этим причинам, ID контроллеров обычно являются существительные, ссылающиеся на ресурс, который они обрабатывают.\nНапример, вы можете использовать `article` в качестве ID контроллера, которые отвечает за обработку данных статей.\n\nПо-умолчанию, ID контроллеров должны содержать только следующие символы: Английские буквы в нижнем регистре, цифры, подчеркивания,\nтире и слэш. Например, оба `article` и `post-comment` являются допустимыми ID контроллеров, в то время как `article?`, `PostComment`,\n`admin\\post` не являются таковыми.\n\nID контроллеров также могут содержать префикс подпапки. Например, в `admin/article` часть `article` является контроллером в \nподпапке `admin` в [[yii\\base\\Application::controllerNamespace|пространстве имен контроллеров]].\nДопустимыми символами для префиксов подпапок являются: Английские буквы в нижнем  и верхнем регистре, символы подчеркивания и\nслэш, где слэш используется в качестве разграничителя для многовложенных подпапок (например `panels/admin`).\n\n\n\n### Правила наименования классов контроллеров <span id=\"controller-class-naming\"></span>\n\nНазвания классов контроллеров могут быть получены из ID контроллеров следующими способами:\n\n* Привести в верхний регистр первый символ в каждом слове, разделенном дефисами. Обратите внимание что, если ID контроллера\n  содержит слэш, то данное правило распространяется только на часть после последнего слэша в ID контроллера;\n* Убрать дефисы и заменить любой прямой слэш на обратный;\n* Добавить суффикс `Controller`;\n* Добавить в начало [[yii\\base\\Application::controllerNamespace|пространство имен контроллеров]].\n\nНиже приведены несколько примеров, с учетом того, что [[yii\\base\\Application::controllerNamespace|пространство имен контроллеров]]\nимеет значение по умолчанию равное `app\\controllers`:\n\n* `article` соответствует `app\\controllers\\ArticleController`;\n* `post-comment` соответствует `app\\controllers\\PostCommentController`;\n* `admin/post-comment` соответствует `app\\controllers\\admin\\PostCommentController`;\n* `adminPanels/post-comment` соответствует `app\\controllers\\adminPanels\\PostCommentController`.\n\nКлассы контроллеров должны быть [автозагружаемыми](concept-autoloading.md). Именно по этой причине, в вышеприведенном примере,\nконтроллер `article` должен быть сохранен в файл, [псевдоним](concept-aliases.md) которого `@app/controllers/ArticleController.php`;\nв то время как контроллер `admin/post-comment` должен находиться в файле `@app/controllers/admin/PostCommentController.php`.\n\n> Информация: Последний пример `admin/post-comment` показывает каким образом вы можете расположить контроллер в подпапке\n  [[yii\\base\\Application::controllerNamespace|пространства имен контроллеров]]. Это очень удобно, когда вы хотите организовать свои контроллеры\n  в несколько категорий и не хотите использовать [модули](structure-modules.md).\n\n\n### Карта контроллеров <span id=\"controller-map\"></span>\n\nВы можете сконфигурировать [[yii\\base\\Application::controllerMap|карту контроллеров]] для того, чтобы преодолеть\nописанные выше ограничения именования ID контроллеров и названий классов. В основном это очень удобно, когда вы используете\nсторонние контроллеры, именование которых вы не можете контролировать.\n\nВы можете сконфигурировать [[yii\\base\\Application::controllerMap|карту контроллеров]] в [настройках приложения](structure-applications.md#application-configurations)\nследующим образом:\n\n```php\n[\n    'controllerMap' => [\n        [\n            // объявляет \"account\" контроллер, используя название класса\n            'account' => 'app\\controllers\\UserController',\n\n            // объявляет \"article\" контроллер, используя массив конфигурации\n            'article' => [\n                'class' => 'app\\controllers\\PostController',\n                'enableCsrfValidation' => false,\n            ],\n        ],\n    ],\n]\n```\n\n### Контроллер по умолчанию <span id=\"default-controller\"></span>\n\nКаждое приложение имеет контроллер по умолчанию, указанный через свойство [[yii\\base\\Application::defaultRoute]].\nКогда в запросе не указан [маршрут](#routes), тогда будет использован маршрут указанный в данном свойстве.\nДля [[yii\\web\\Application|Веб приложений]], это значение `'site'`, в то время как для [[yii\\console\\Application|консольных приложений]],\nэто `'help'`. Таким образом, если задан URL `https://hostname/index.php`, это означает, что контроллер `site` выполнит обработку запроса.\n\nВы можете изменить контроллер по умолчанию следующим образом в [настройках приложения](structure-applications.md#application-configurations):\n\n```php\n[\n    'defaultRoute' => 'main',\n]\n```\n\n\n## Создание действий <span id=\"creating-actions\"></span>\n\nСоздание действий не представляет сложностей также как и объявление так называемых *методов действий* в классе контроллера. Метод действия\nэто *public* метод, имя которого начинается со слова `action`. Возвращаемое значение метода действия представляет собой ответные данные,\nкоторые будут высланы конечному пользователю. Приведенный ниже код определяет два действия `index` и `hello-world`:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actionIndex()\n    {\n        return $this->render('index');\n    }\n\n    public function actionHelloWorld()\n    {\n        return 'Hello World';\n    }\n}\n```\n\n\n### ID действий <span id=\"action-ids\"></span>\n\nВ основном действие разрабатывается для какой-либо конкретной обработки ресурса. По этой причине, ID действий в основном\nявляются глаголами, такими как `view`, `update`, и т. д.\n\nПо-умолчанию, ID действия должен содержать только следующие символы: Английские буквы в нижнем регистре, цифры,\nподчеркивания и дефисы. Дефисы в ID действий используются для разделения слов. Например, `view`, `update2`, `comment-post` являются\nдопустимыми ID действий, в то время как `view?`, `Update` не являются таковыми.\n\nВы можете создавать действия двумя способами: встроенные действия и отдельные действия. Встроенное действие является методом, определенным\nв классе контроллера, в то время как отдельное действие является экземпляром класса, унаследованного от [[yii\\base\\Action]] или его потомков.\nВстроенные действия требуют меньше усилий для создания и в основном используются если у вас нет надобности в повторном использовании действий.\nОтдельные действия, с другой стороны, в основном создаются для использования в различных контроллерах или при использовании в [расширениях](structure-extensions.md).\n\n\n### Встроенные действия <span id=\"inline-actions\"></span>\n\nВстроенные действия это те действия, которые определены в рамках методов контроллера, как мы это уже обсудили.\n\nНазвания методов действий могут быть получены из ID действий следующим образом:\n\n* Привести первый символ каждого слова в ID действия в верхний регистр;\n* Убрать дефисы;\n* Добавить префикс `action`.\n\nНапример, `index` соответствует `actionIndex`, а `hello-world` соответствует `actionHelloWorld`.\n\n> Примечание: Названия имен действий являются *регистрозависимыми*. Если у вас есть метод `ActionIndex`, он не будет\n  учтен как метод действия, таким образом, запрос к действию `index` приведет к выбросу исключению. Также следует учесть, что методы действий\n  должны иметь область видимости public. Методы имеющие область видимости private или protected НЕ определяют методы встроенных действий.\n\n\nВстроенные действия в основном используются, потому что для их создания не нужного много усилий. Тем не менее, если вы планируете повторно\nиспользовать некоторые действия в различных местах, или если вы хотите перераспределить действия, вы должны определить его как *отдельной действие*.\n\n\n### Отдельные действия <span id=\"standalone-actions\"></span>\n\nОтдельные действия определяются в качестве классов, унаследованных от [[yii\\base\\Action]] или его потомков.\nНапример, в Yii релизах, присутствуют [[yii\\web\\ViewAction]] и [[yii\\web\\ErrorAction]], оба из которых являются\nотдельными действиями.\n\nДля использования отдельного действия, вы должны указать его в *карте действий*, с помощью переопределения метода\n[[yii\\base\\Controller::actions()]] в вашем классе контроллера, следующим образом:\n\n```php\npublic function actions()\n{\n    return [\n        // объявляет \"error\" действие с помощью названия класса\n        'error' => 'yii\\web\\ErrorAction',\n\n        // объявляет \"view\" действие с помощью конфигурационного массива\n        'view' => [\n            'class' => 'yii\\web\\ViewAction',\n            'viewPrefix' => '',\n        ],\n    ];\n}\n```\n\nКак вы можете видеть, метод `actions()` должен вернуть массив, ключами которого являются ID действий, а значениями - соответствующие\nназвания класса действия или [конфигурация](concept-configurations.md). В отличие от встроенных действий, ID отдельных действий могут\nсодержать произвольные символы, до тех пор пока они определены в методе `actions()`.\n\nДля создания отдельного действия, вы должны наследоваться от класса [[yii\\base\\Action]] или его потомков, и реализовать\nметод `run()` с областью видимости public. Роль метода `run()` аналогична другим методам действий. Например,\n\n```php\n<?php\nnamespace app\\components;\n\nuse yii\\base\\Action;\n\nclass HelloWorldAction extends Action\n{\n    public function run()\n    {\n        return \"Hello World\";\n    }\n}\n```\n\n\n### Результаты действий <span id=\"action-results\"></span>\n\nВозвращаемое значение методов действий или метода `run()` отдельного действия очень важно. Оно является результатом\nвыполнения соответствующего действия.\n\nВозвращаемое значение может быть объектом [response](runtime-responses.md), который будет отослан конечному пользователю \nв качестве ответа.\n\n* Для [[yii\\web\\Application|Веб приложений]], возвращаемое значение также может быть произвольными данными, которые будут\n  присвоены [[yii\\web\\Response::data]], а затем сконвертированы в строку, представляющую тело ответа.\n* Для [[yii\\console\\Application|Консольных приложений]], возвращаемое значение также может быть числом, представляющим\n  [[yii\\console\\Response::exitStatus|статус выхода]] исполнения команды.\n\nВ вышеприведенных примерах, все результаты действий являются строками, которые будут использованы в качестве тела ответа,\nвысланного пользователю. Следующий пример, показывает действие может перенаправить браузер пользователя на новый URL, с помощью\nвозврата response объекта (т. к. [[yii\\web\\Controller::redirect()|redirect()]] метод возвращает response объект):\n\n```php\npublic function actionForward()\n{\n    // перенаправляем браузер пользователя на https://example.com\n    return $this->redirect('https://example.com');\n}\n```\n\n\n### Параметры действий <span id=\"action-parameters\"></span>\n\nМетоды действий для встроенных действий и методы `run()` для отдельных действий могут принимать параметры,\nназываемые *параметры действий*. Их значения берутся из запросов. Для [[yii\\web\\Application|Веб приложений]],\nзначение каждого из параметров действия берется из `$_GET`, используя название параметра в качестве ключа;\nдля [[yii\\console\\Application|консольных приложений]], они соответствуют аргументам командной строки.\n\nВ приведенном ниже примере, действие `view` (встроенное действие) определяет два параметра: `$id` и `$version`.\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass PostController extends Controller\n{\n    public function actionView($id, $version = null)\n    {\n        // ...\n    }\n}\n```\n\nДля разных запросов параметры действий будут определены следующим образом:\n\n* `https://hostname/index.php?r=post/view&id=123`: параметр `$id` будет присвоено значение `'123'`, в то время\n  как `$version` будет иметь значение `null`, т. к. строка запроса не содержит параметра `version`;\n* `https://hostname/index.php?r=post/view&id=123&version=2`: параметрам `$id` и `$version` будут присвоены\n  значения `'123'` и `'2'` соответственно;\n* `https://hostname/index.php?r=post/view`: будет брошено исключение [[yii\\web\\BadRequestHttpException]], т. к.\n  обязательный параметр `$id` не был указан в запросе;\n* `https://hostname/index.php?r=post/view&id[]=123`: будет брошено исключение [[yii\\web\\BadRequestHttpException]], т. к.\n  параметр `$id` получил неверное значение `['123']`.\n\nЕсли вы хотите, чтобы параметр действия принимал массив значений, вы должны использовать type-hint значение `array`, как показано ниже:\n\n```php\npublic function actionView(array $id, $version = null)\n{\n    // ...\n}\n```\n\nТеперь, если запрос будет содержать URL `https://hostname/index.php?r=post/view&id[]=123`, то параметр `$id` примет значение\n`['123']`. Если запрос будет содержать URL `https://hostname/index.php?r=post/view&id=123`, то параметр `$id` все равно будет\nсодержать массив, т. к. скалярное значение `'123'` будет автоматически сконвертировано в массив.\n\nВышеприведенные примеры в основном показывают как параметры действий работают для Веб приложений. Больше информации\nо параметрах консольных приложений представлено в секции [Консольные команды](tutorial-console.md).\n\n\n### Действие по умолчанию <span id=\"default-action\"></span>\n\nКаждый контроллер имеет действие, указанное через свойство [[yii\\base\\Controller::defaultAction]].\nКогда [маршрут](#routes) содержит только ID контроллера, то подразумевается, что действие контроллера по умолчанию\nбыло запрошено.\n\nПо-умолчанию, это действие имеет значение `index`. Если вы хотите изменить это значение, просто переопределите данное\nсвойство в классе контроллера следующим образом:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public $defaultAction = 'home';\n\n    public function actionHome()\n    {\n        return $this->render('home');\n    }\n}\n```\n\n\n## Жизненный цикл контроллера <span id=\"controller-lifecycle\"></span>\n\nПри обработке запроса, [приложение](structure-applications.md) создаст контроллер, основываясь на \nзапрошенном [маршруте](#routes). Для выполнения запроса, контроллер пройдет через следующие этапы\nжизненного цикла:\n\n1. Метод [[yii\\base\\Controller::init()]] будет вызван после того как контроллер будет создан и сконфигурирован;\n2. Контроллер создает объект действия, основываясь на запрошенном ID действия:\n   * Если ID действия не указан, то будет использовано [[yii\\base\\Controller::defaultAction|ID действия по умолчанию]];\n   * Если ID действия найдено в [[yii\\base\\Controller::actions()|карте действий]], то отдельное действие будет создано;\n   * Если ID действия соответствует методу действия, то встроенное действие будет создано;\n   * В противном случае, будет выброшено исключение [[yii\\base\\InvalidRouteException]].\n3. Контроллер последовательно вызывает метод `beforeAction()` приложения, модуля (если контроллер принадлежит модулю) и\n   самого контроллера.\n   * Если один из методов вернул `false`, то остальные, не вызванные методы `beforeAction` будут пропущены, а выполнение\n     действия будет отменено;\n   * По-умолчанию, каждый вызов метода `beforeAction()` вызовет событие `beforeAction`, на которое вы можете назначить обработчики.\n4. Контроллер запускает действие:\n   * Параметры действия будут проанализированы и заполнены из данных запроса.\n5. Контроллер последовательно вызывает методы `afterAction` контроллера, модуля (если контроллер принадлежит модулю) и приложения.\n   * По-умолчанию, каждый вызов метода `afterAction()` вызовет событие `afterAction`, на которое вы можете назначить обработчики.\n6. Приложение, получив результат выполнения действия, присвоит его объекту [response](runtime-responses.md).\n\n\n## Лучшие практики <span id=\"best-practices\"></span>\n\nВ хорошо организованных приложениях контроллеры обычно очень тонкие и содержат лишь несколько строк кода.\nЕсли ваш контроллер слишком сложный, то обычно это означает, что вам надо провести его рефакторинг и перенести часть кода\nв другие места.\n\nВ целом, контроллеры\n\n* могут иметь доступ к данным [запроса](runtime-requests.md);\n* могут вызывать методы [моделей](structure-models.md) и других компонентов системы с данными запроса;\n* могут использовать [представления](structure-views.md) для формирования ответа;\n* не должны заниматься обработкой данных, это должно происходить в [слое моделей](structure-models.md);\n* должны избегать использования HTML или другой разметки, лучше это делать в [представлениях](structure-views.md).\n"
  },
  {
    "path": "docs/guide-vi/README.md",
    "content": "The Definitive Guide to Yii 2.0\n===============================\n\nCác hướng dẫn được phát hành theo [Các điều khoản về tài liệu Yii](https://www.yiiframework.com/doc/terms/).\n\nTất cả bản quyền đã được bảo hộ (All Rights Reserved).\n\n2014 (c) Yii Software LLC.\n\n\nGiới thiệu\n------------\n\n* [Về Yii](intro-yii.md)\n* [Hướng dẫn nâng cấp lên từ phiên bản 1.1](intro-upgrade-from-v1.md)\n\n\nBắt đầu\n---------------\n\n* [Những gì bạn cần biết](start-prerequisites.md)\n* [Cài đặt Yii](start-installation.md)\n* [Thực hiện chạy ứng dụng](start-workflow.md)\n* [Viết chương trình đầu tiên](start-hello.md)\n* [Làm việc với Forms](start-forms.md)\n* [Làm việc với Databases](start-databases.md)\n* [Sử dụng Gii để sinh mã tự động](start-gii.md)\n* [Nâng cao](start-looking-ahead.md)\n\n\nKiến trúc ứng dụng (Application Structure)\n---------------------\n\n* [Tổng quan về kiến trúc ứng dụng](structure-overview.md)\n* [Mục Scripts](structure-entry-scripts.md)\n* [Ứng dụng (Applications)](structure-applications.md)\n* [Các thành phần bên trong ứng dụng](structure-application-components.md)\n* [Controllers](structure-controllers.md)\n* [Models](structure-models.md)\n* [Views](structure-views.md)\n* [Modules](structure-modules.md)\n* [Bộ lọc (Filters)](structure-filters.md)\n* [Widgets](structure-widgets.md)\n* [Assets](structure-assets.md)\n* [Phần mở rộng (Extensions)](structure-extensions.md)\n\n\nXử lý yêu cầu (Handling Requests)\n-----------------\n\n* [Tổng quan](runtime-overview.md)\n* [Khởi động](runtime-bootstrapping.md)\n* [Định tuyến (Routing) và khởi tạo đường dẫn (URL Creation)](runtime-routing.md)\n* [Yêu cầu (Requests)](runtime-requests.md)\n* [Kết quả (Responses)](runtime-responses.md)\n* [Sessions và Cookies](runtime-sessions-cookies.md)\n* [Xử lý lỗi (Handling Error)](runtime-handling-errors.md)\n* [Logging](runtime-logging.md)\n\n\nCác khái niệm chính\n------------\n\n* [Thành phần (Components)](concept-components.md)\n* [Thuộc tính (Properties)](concept-properties.md)\n* [Sự kiện (Events)](concept-events.md)\n* [Hành vi (Behaviors)](concept-behaviors.md)\n* [Cấu hình (Configurations)](concept-configurations.md)\n* [Bí danh (Aliases)](concept-aliases.md)\n* [Lớp tự động nạp (Autoloading)](concept-autoloading.md)\n* [Service Locator](concept-service-locator.md)\n* [Dependency Injection Container](concept-di-container.md)\n\n\nLàm việc với Databases\n----------------------\n\n* [Data Access Objects](db-dao.md): Kết nối cơ sở dữ liệu, truy vấn cơ bản, giao dịch và phương thức hoạt động\n* [Query Builder](db-query-builder.md): Sử dụng một truy vấn đơn giản, các lớp cơ sở dữ liệu trừu tượng\n* [Active Record](db-active-record.md): The Active Record ORM, truy vấn và thao tác với dữ liệu, định nghĩa các mối quan hệ giữa các bảng\n* [Migrations](db-migrations.md): Cung cấp cho đội dự án một công cụ dễ dàng trong việc quản lý những schema CSDL trong ứng dụng\n* [Sphinx](https://www.yiiframework.com/extension/yiisoft/yii2-sphinx/doc/guide)\n* [Redis](https://www.yiiframework.com/extension/yiisoft/yii2-redis/doc/guide)\n* [MongoDB](https://www.yiiframework.com/extension/yiisoft/yii2-mongodb/doc/guide)\n* [ElasticSearch](https://www.yiiframework.com/extension/yiisoft/yii2-elasticsearch/doc/guide)\n\n\nNhận dữ liệu từ user\n-----------------------\n\n* [Tạo mới Forms](input-forms.md)\n* [Kiểm tra dữ liệu đầu vào (Validating Input)](input-validation.md)\n* [File Upload](input-file-upload.md)\n* [Thu thập dữ liệu từ danh sách đầu vào (Đang phát triển)](input-tabular-input.md)\n* [Lấy dữ liệu cho nhiều Models (Chưa giải quyết)](input-multiple-models.md)\n* [Mở rộng ActiveForm ở phía Máy khách](input-form-javascript.md)\n\n\nHiển thị dữ liệu\n---------------\n\n* [Định dạng dữ liệu (Data Formatting)](output-formatter.md)\n* [Phân trang (Pagination)](output-pagination.md)\n* [Sắp xếp (Sorting)](output-sorting.md)\n* [Cung cấp dữ liệu ra (Data Providers)](output-data-providers.md)\n* [Dữ liệu Widgets](output-data-widgets.md)\n* [làm việc với Client Scripts](output-client-scripts.md)\n* [Giao diện (Theming)](output-theming.md)\n\n\nBảo mật (Security)\n--------\n\n* [Xác thực (Authentication)](security-authentication.md)\n* [Quyền (Authorization)](security-authorization.md)\n* [Các thao tác xử lý với Passwords (Đang phát triển)](security-passwords.md)\n* [Auth Clients](https://www.yiiframework.com/extension/yiisoft/yii2-authclient/doc/guide)\n* [Best Practices](security-best-practices.md)\n\n\nBộ nhớ Cache\n-------\n\n* [Tổng quan](caching-overview.md)\n* [Cache dữ liệu](caching-data.md)\n* [Fragment Caching](caching-fragment.md)\n* [Page Caching](caching-page.md)\n* [HTTP Caching](caching-http.md)\n\n\nRESTful Web Services\n--------------------\n\n* [Bắt đầu](rest-quick-start.md)\n* [Tài nguyên (Resources)](rest-resources.md)\n* [Bộ điều khiển (Controllers)](rest-controllers.md)\n* [Routing](rest-routing.md)\n* [Định dạng thông điệp gửi đi (Response Formatting)](rest-response-formatting.md)\n* [Xác thực (Authentication)](rest-authentication.md)\n* [Rate Limiting](rest-rate-limiting.md)\n* [Phiên bản (Version)](rest-versioning.md)\n* [Error Handling](rest-error-handling.md)\n\n\nCông cụ phát triển (Development Tools)\n-----------------\n\n* [Thanh công cụ gỡ lỗi và sửa lỗi (Debug Toolbar và Debugger)](https://www.yiiframework.com/extension/yiisoft/yii2-debug/doc/guide)\n* [Sử dụng Gii để tạo code](https://www.yiiframework.com/extension/yiisoft/yii2-gii/doc/guide)\n* [Tạo tài liệu về API ](https://www.yiiframework.com/extension/yiisoft/yii2-apidoc)\n\n\nTesting\n-------\n\n* [Tổng quan](test-overview.md)\n* [Thiết lập môi trường](test-environment-setup.md)\n* [Unit Tests](test-unit.md)\n* [Kiểm tra chức năng (Functional Tests)](test-functional.md)\n* [Acceptance Tests](test-acceptance.md)\n* [Fixtures](test-fixtures.md)\n\n\nChủ đề năng cao\n--------------\n\n* [Advanced Application Template](https://www.yiiframework.com/extension/yiisoft/yii2-app-advanced/doc/guide)\n* [Building Application from Scratch](tutorial-start-from-scratch.md)\n* [Giao diện dòng lệnh (Console Commands)](tutorial-console.md)\n* [Core Validators](tutorial-core-validators.md)\n* [Quốc tế hóa (Internationalization)](tutorial-i18n.md)\n* [Thư (Mailing)](tutorial-mailing.md)\n* [Tối ưu hiệu năng ứng dụng (Performance Tuning)](tutorial-performance-tuning.md)\n* [Shared Hosting Environment](tutorial-shared-hosting.md)\n* [Template Engines](tutorial-template-engines.md)\n* [Tích hợp mã nguồn của bên thứ ba (Working with Third-Party Code)](tutorial-yii-integration.md)\n* [Dùng Yii như các framework nhỏ](tutorial-yii-as-micro-framework.md)\n\n\nWidgets\n-------\n\n* [GridView](https://www.yiiframework.com/doc-2.0/yii-grid-gridview.html)\n* [ListView](https://www.yiiframework.com/doc-2.0/yii-widgets-listview.html)\n* [DetailView](https://www.yiiframework.com/doc-2.0/yii-widgets-detailview.html)\n* [ActiveForm](https://www.yiiframework.com/doc-2.0/guide-input-forms.html#activerecord-based-forms-activeform)\n* [Pjax](https://www.yiiframework.com/doc-2.0/yii-widgets-pjax.html)\n* [Menu](https://www.yiiframework.com/doc-2.0/yii-widgets-menu.html)\n* [LinkPager](https://www.yiiframework.com/doc-2.0/yii-widgets-linkpager.html)\n* [LinkSorter](https://www.yiiframework.com/doc-2.0/yii-widgets-linksorter.html)\n* [Bootstrap Widgets](https://www.yiiframework.com/extension/yiisoft/yii2-bootstrap/doc/guide)\n* [jQuery UI Widgets](https://www.yiiframework.com/extension/yiisoft/yii2-jui/doc/guide)\n\n\nHelpers\n-------\n\n* [Tổng quan](helper-overview.md)\n* [ArrayHelper](helper-array.md)\n* [Html](helper-html.md)\n* [Url](helper-url.md)\n\n"
  },
  {
    "path": "docs/guide-vi/blocktypes.json",
    "content": "{\n  \"Info:\": \"Lưu ý:\"\n}\n"
  },
  {
    "path": "docs/guide-vi/images/advanced-app-configs.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yFiles for Java 2.11-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Beschreibung\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"409.0\" width=\"148.0\" x=\"290.953299818952\" y=\"148.6669921875\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"148.0\" x=\"0.0\" y=\"0.0\">console</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 4</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n0:\"/>\n    </node>\n    <node id=\"n1\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"409.0\" width=\"148.0\" x=\"310.2342794220951\" y=\"167.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"148.0\" x=\"0.0\" y=\"0.0\">backend</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"310.2342794220951\" y=\"167.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 3</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n1:\"/>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"62.0\" width=\"118.0\" x=\"348.0\" y=\"628.9999999999999\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"32.6875\" x=\"42.65625\" y=\"21.6494140625\">index<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"rectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n3\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"402.37646484375\" width=\"148.0\" x=\"333.0\" y=\"188.62353515625\"/>\n              <y:Fill color=\"#DDFFDD\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#DDFFDD\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"148.0\" x=\"0.0\" y=\"0.0\">frontend</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 1</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n3:\">\n        <node id=\"n3::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"348.0\" y=\"226.0\"/>\n              <y:Fill color=\"#FFCCCC\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"44.013671875\" x=\"36.9931640625\" y=\"21.6494140625\">params<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n3::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"348.0\" y=\"322.0\"/>\n              <y:Fill color=\"#FFCCCC\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"72.689453125\" x=\"22.6552734375\" y=\"21.6494140625\">params-local<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n3::n2\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"348.0\" y=\"418.0\"/>\n              <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"30.009765625\" x=\"43.9951171875\" y=\"21.6494140625\">main<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n3::n3\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"348.0\" y=\"514.0\"/>\n              <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"58.685546875\" x=\"29.6572265625\" y=\"21.6494140625\">main-local<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"62.0\" width=\"223.21580824827026\" x=\"7.0\" y=\"628.9999999999999\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"41.353515625\" x=\"90.93114631163513\" y=\"21.6494140625\">aliases<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"rectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"306.37646484375\" width=\"311.0\" x=\"7.0\" y=\"188.62353515625\"/>\n              <y:Fill color=\"#DDFFDD\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#DDFFDD\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"311.0\" x=\"0.0\" y=\"0.0\">common</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"-8.0\" y=\"189.333984375\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 2</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n5:\">\n        <node id=\"n5::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"185.0\" y=\"226.0\"/>\n              <y:Fill color=\"#FFCCCC\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"72.689453125\" x=\"22.6552734375\" y=\"21.6494140625\">params-local<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n5::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"185.0\" y=\"418.0\"/>\n              <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"58.685546875\" x=\"29.6572265625\" y=\"21.6494140625\">main-local<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n5::n2\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"22.0\" y=\"226.0\"/>\n              <y:Fill color=\"#FFCCCC\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"44.013671875\" x=\"36.9931640625\" y=\"21.6494140625\">params<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n5::n3\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"22.0\" y=\"418.0\"/>\n              <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"30.009765625\" x=\"43.9951171875\" y=\"21.6494140625\">main<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <edge id=\"e0\" source=\"n3::n3\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"2.0\" y=\"10.125\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-111.48005839096935\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"3.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.25\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"33.47449351530429\" y=\"-2.0000000000001137\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n5::n1\" target=\"n3::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"5.6843418860808015E-14\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"218.8251533742331\" y=\"449.00000000000006\"/>\n            <y:Point x=\"218.8251533742331\" y=\"449.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"10.089752197265625\" y=\"-6.0\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n5::e0\" source=\"n5::n2\" target=\"n5::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"10.125\" y=\"-6.0\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n5::e1\" source=\"n5::n3\" target=\"n5::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"10.125\" y=\"-6.0\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n3::e0\" source=\"n3::n0\" target=\"n3::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"2.0\" y=\"10.125\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n3::e1\" source=\"n3::n1\" target=\"n3::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"2.0\" y=\"10.125\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n3::e2\" source=\"n3::n2\" target=\"n3::n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"2.0\" y=\"10.125\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n5::n0\" target=\"n3::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"-5.6843418860808015E-14\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"218.8251533742331\" y=\"256.99999999999994\"/>\n            <y:Point x=\"218.8251533742331\" y=\"257.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"10.089752197265625\" y=\"-6.0\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources/>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-vi/images/application-lifecycle.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.13-->\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d0\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key for=\"graphml\" id=\"d7\" yfiles.type=\"resources\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d0\"/>\n    <node id=\"n0\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"571.4472707112631\" width=\"763.2772213171534\" x=\"-1269.9373595143054\" y=\"-207.17439524332679\"/>\n              <y:Fill color=\"#FFCC0024\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FFCC00\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"763.2772213171534\" x=\"0.0\" y=\"0.0\">Entry script (index.php or yii)</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"225.33495140075684\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 4</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n0:\">\n        <node id=\"n0::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"324.9258883570935\" x=\"-1249.511914911339\" y=\"-169.79793039957679\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"126.759765625\" x=\"99.08306136604676\" y=\"5.6494140625\">Load application config<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n0::n1\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d5\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"309.37646484374994\" width=\"330.35133296005984\" x=\"-1254.9373595143054\" y=\"35.272875467936274\"/>\n                  <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"330.35133296005984\" x=\"0.0\" y=\"0.0\">Create application instance</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"19\" bottomF=\"19.15489692687993\" left=\"5\" leftF=\"5.425444602966309\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 5</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n0::n1:\">\n            <node id=\"n0::n1::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"72.64934031168627\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"45.34375\" x=\"124.79106917854676\" y=\"5.6494140625\">preInit()<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n1\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"122.64524027506516\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"120.71875\" x=\"87.10356917854676\" y=\"5.6494140625\">Register error handler<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n2\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"174.96110788981125\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"173.435546875\" x=\"60.74517074104676\" y=\"5.6494140625\">Configure application properties<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n3\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"226.56779181162517\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"27.33203125\" x=\"133.79692855354676\" y=\"5.6494140625\">init()<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n4\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.9258883570935\" x=\"-1234.511914911339\" y=\"280.4944433848063\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.025390625\" x=\"116.45024886604676\" y=\"5.6494140625\">bootstrap()<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n        <node id=\"n0::n2\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d5\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"411.6943410237631\" width=\"324.9258883570935\" x=\"-846.5860265542456\" y=\"-169.79793039957679\"/>\n                  <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"324.9258883570935\" x=\"0.0\" y=\"0.0\">Run application</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"1.1368683772161603E-13\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 3</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n0::n2:\">\n            <node id=\"n0::n2::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.9258883570935\" x=\"-831.5860265542456\" y=\"-132.42146555582667\"/>\n                  <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"164.705078125\" x=\"65.11040511604676\" y=\"5.6494140625\">EVENT_BEFORE_REQUEST<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n2::n1\" yfiles.foldertype=\"group\">\n              <data key=\"d4\"/>\n              <data key=\"d5\"/>\n              <data key=\"d6\">\n                <y:ProxyAutoBoundsNode>\n                  <y:Realizers active=\"0\">\n                    <y:GroupNode>\n                      <y:Geometry height=\"204.37646484375\" width=\"294.9258883570935\" x=\"-831.5860265542456\" y=\"-78.79793039957679\"/>\n                      <y:Fill color=\"#99336635\" transparent=\"false\"/>\n                      <y:BorderStyle hasColor=\"false\" type=\"dashed\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#993366\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#FFFFFF\" visible=\"true\" width=\"294.9258883570935\" x=\"0.0\" y=\"0.0\">Handle request</y:NodeLabel>\n                      <y:Shape type=\"roundrectangle\"/>\n                      <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                      <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                      <y:BorderInsets bottom=\"8\" bottomF=\"7.929194132486941\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                    </y:GroupNode>\n                    <y:GroupNode>\n                      <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                      <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 4</y:NodeLabel>\n                      <y:Shape type=\"roundrectangle\"/>\n                      <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                      <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                      <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                    </y:GroupNode>\n                  </y:Realizers>\n                </y:ProxyAutoBoundsNode>\n              </data>\n              <graph edgedefault=\"directed\" id=\"n0::n2::n1:\">\n                <node id=\"n0::n2::n1::n0\">\n                  <data key=\"d6\">\n                    <y:ShapeNode>\n                      <y:Geometry height=\"30.0\" width=\"264.9258883570935\" x=\"-816.5860265542456\" y=\"-41.421465555826785\"/>\n                      <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"231.4609375\" x=\"16.732475428546763\" y=\"5.6494140625\">Resolve request into route and parameters<y:LabelModel>\n                          <y:SmartNodeLabelModel distance=\"4.0\"/>\n                        </y:LabelModel>\n                        <y:ModelParameter>\n                          <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                        </y:ModelParameter>\n                      </y:NodeLabel>\n                      <y:Shape type=\"rectangle\"/>\n                    </y:ShapeNode>\n                  </data>\n                </node>\n                <node id=\"n0::n2::n1::n1\">\n                  <data key=\"d6\">\n                    <y:ShapeNode>\n                      <y:Geometry height=\"30.0\" width=\"264.9258883570935\" x=\"-816.5860265542456\" y=\"18.578534444173215\"/>\n                      <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"197.44140625\" x=\"33.74224105354676\" y=\"5.6494140625\">Create module, controller and action<y:LabelModel>\n                          <y:SmartNodeLabelModel distance=\"4.0\"/>\n                        </y:LabelModel>\n                        <y:ModelParameter>\n                          <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                        </y:ModelParameter>\n                      </y:NodeLabel>\n                      <y:Shape type=\"rectangle\"/>\n                    </y:ShapeNode>\n                  </data>\n                </node>\n                <node id=\"n0::n2::n1::n2\">\n                  <data key=\"d6\">\n                    <y:ShapeNode>\n                      <y:Geometry height=\"30.0\" width=\"264.9258883570935\" x=\"-816.5860265542456\" y=\"72.64934031168627\"/>\n                      <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.369140625\" x=\"101.77837386604676\" y=\"5.649414062500057\">Run action<y:LabelModel>\n                          <y:SmartNodeLabelModel distance=\"4.0\"/>\n                        </y:LabelModel>\n                        <y:ModelParameter>\n                          <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                        </y:ModelParameter>\n                      </y:NodeLabel>\n                      <y:Shape type=\"rectangle\"/>\n                    </y:ShapeNode>\n                  </data>\n                </node>\n              </graph>\n            </node>\n            <node id=\"n0::n2::n2\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.9258883570935\" x=\"-831.5860265542456\" y=\"149.20206960042316\"/>\n                  <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"154.697265625\" x=\"70.11431136604676\" y=\"5.6494140625\">EVENT_AFTER_REQUEST<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n2::n3\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-831.5860265542456\" y=\"196.89641062418633\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"148.099609375\" x=\"73.41313949104676\" y=\"5.6494140625\">Send response to end user<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n        <node id=\"n0::n3\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"324.9258883570935\" x=\"-846.5860265542456\" y=\"319.2728754679363\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"160.08203125\" x=\"82.42192855354676\" y=\"5.6494140625\">Complete request processing<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <edge id=\"e0\" source=\"n0\" target=\"n0::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-179.83580569315376\" sy=\"-180.3944529152355\" tx=\"-13.869777491410105\" ty=\"-154.8008369539754\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::e0\" source=\"n0::n0\" target=\"n0::n1\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#99CCFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"106.052734375\" x=\"-130.76131968438426\" y=\"78.18481445312506\">Configuration array<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"77.04379339868619\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e0\" source=\"n0::n1::n0\" target=\"n0::n1::n1\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e1\" source=\"n0::n1::n1\" target=\"n0::n1::n2\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e2\" source=\"n0::n1::n2\" target=\"n0::n1::n3\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e3\" source=\"n0::n1::n3\" target=\"n0::n1::n4\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::e1\" source=\"n0::n1\" target=\"n0::n2\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"2.8060680342755404\" sy=\"-48.37646484374994\" tx=\"-162.49660512430125\" ty=\"105.53540293375653\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::e0\" source=\"n0::n2::n0\" target=\"n0::n2::n1\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::n1::e0\" source=\"n0::n2::n1::n0\" target=\"n0::n2::n1::n1\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::n1::e1\" source=\"n0::n2::n1::n1\" target=\"n0::n2::n1::n2\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::e1\" source=\"n0::n2::n1\" target=\"n0::n2::n2\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::e2\" source=\"n0::n2::n2\" target=\"n0::n2::n3\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::e2\" source=\"n0::n2\" target=\"n0::n3\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#99CCFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"59.353515625\" x=\"-93.67673227804266\" y=\"28.318773905436274\">Exit status<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"63.99999999999999\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.47945569632951074\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d7\">\n    <y:Resources/>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-vi/images/application-structure.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"872.1807999999999\" y=\"-14.764159999999947\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"32.265625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"69.712890625\" x=\"15.1435546875\" y=\"1.3671875\">application\ncomponent<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"-91.97375999999994\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"70.28125\" x=\"14.859375\" y=\"8.43359375\">entry script<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"-14.764159999999947\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"68.21875\" x=\"15.890625\" y=\"8.43359375\">application<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"62.44544000000004\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"60.267578125\" x=\"19.8662109375\" y=\"8.433593750000007\">controller<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"872.1807999999999\" y=\"62.44544000000005\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"31.43359375\" x=\"34.283203125\" y=\"8.43359375\">filter<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"532.664\" y=\"23.901600000000087\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"47.728515625\" x=\"26.1357421875\" y=\"8.43359375\">module<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"618.4047999999991\" y=\"139.65504000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"29.611328125\" x=\"35.1943359375\" y=\"8.43359375\">view<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"786.161599999999\" y=\"139.65504000000004\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"40.28125\" x=\"29.859375\" y=\"8.43359375\">model<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n8\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"532.664\" y=\"216.86464000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"42.923828125\" x=\"28.5380859375\" y=\"8.43359375\">widget<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n9\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"216.86464000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"77.986328125\" x=\"11.0068359375\" y=\"8.43359375\">asset bundle<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n2\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.97265625\" x=\"2.5392475585936154\" y=\"-25.635106635436955\">1:1<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.025600000000054\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.17992697197425173\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n0\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-48.97602119140629\" y=\"-23.09200633216852\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.025599999999994\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5470891790487767\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n5\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"15.193962466822086\" y=\"30.674065521861735\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"44.894496863129426\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.25416574623968574\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n3\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"2.7719538085937074\" y=\"-26.04470613037104\">1..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"15.254400000000032\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.2090245199765919\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n3\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-57.39355288912964\" y=\"-63.846685518352906\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"43.47658308018222\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.881412889692355\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n5\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"582.664\" y=\"-3.598399999999913\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-17.888505566406252\" y=\"-42.41848039245597\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"16.483200000000004\" distanceToCenter=\"true\" position=\"right\" ratio=\"5.822600000000001\" segment=\"-2\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n6\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-12.223966589163297\" y=\"-24.76668258085064\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"11.385360101663515\" distanceToCenter=\"true\" position=\"left\" ratio=\"0.10670293331985262\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e7\" source=\"n7\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-9.313146217848043\" y=\"-26.098020948340803\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.669798038586146\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.1670192242704811\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e8\" source=\"n9\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-9.797233483694527\" y=\"-25.82443358614151\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.15599378957306\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.15481212573620068\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e9\" source=\"n8\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-15.633139430038\" y=\"-24.050683308790553\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"13.24330808446041\" distanceToCenter=\"true\" position=\"left\" ratio=\"0.05506723556297327\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e10\" source=\"n4\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-47.337621191406356\" y=\"-22.682408449706998\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"13.616000000000007\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e11\" source=\"n9\" target=\"n8\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-44.265598339843905\" y=\"-19.61040553222648\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.543999999999983\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.4117077892835431\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e12\" source=\"n7\" target=\"n6\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.1328125\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"24.96484375\" x=\"-44.81721357421975\" y=\"-19.610410805664003\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.543999999999983\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.4531592446547025\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources/>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-vi/images/rbac-access-check-1.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-vi/images/rbac-access-check-2.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-vi/images/rbac-access-check-3.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-vi/images/rbac-hierarchy-1.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n5\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-vi/images/rbac-hierarchy-2.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"33.158203125\" y=\"25.1494140625\">admin<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"38.025390625\" x=\"32.4873046875\" y=\"25.1494140625\">author<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.905467987060547\" y=\"-27.814727783203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.37109375\" x=\"-2.908496856689453\" y=\"-27.701656341552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.71484375\" x=\"19.142578125\" y=\"25.1494140625\">updatePost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"89.388671875\" x=\"6.8056640625\" y=\"25.1494140625\">updateOwnPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.36328125\" x=\"20.818359375\" y=\"25.1494140625\">createPost<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.03515625\" x=\"19.482421875\" y=\"5.4173431396484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-vi/images/request-lifecycle.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"70.13700103759766\" width=\"56.558998107910156\" x=\"198.38633947503078\" y=\"261.099458694458\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"26.279499053955078\" y=\"74.13700103759766\">\n            <y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"-0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"0.5\" offsetX=\"0.0\" offsetY=\"4.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"27.34375\" x=\"-31.34375\" y=\"25.717914581298828\">user<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.5\" labelRatioY=\"0.0\" nodeRatioX=\"-0.5\" nodeRatioY=\"0.0\" offsetX=\"-4.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"637.1271178722382\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"36.68359375\" x=\"41.158203125\" y=\"13.1494140625\">model<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"46.887996673583984\" width=\"39.527000427246094\" x=\"828.8018617630005\" y=\"544.9831195354463\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"53.376953125\" x=\"-6.924976348876953\" y=\"-30.723247528076172\">database<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-12.022075653076172\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"17.763500213623047\" y=\"21.443998336791992\">\n            <y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"713.6271178722382\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"28.005859375\" x=\"45.4970703125\" y=\"13.1494140625\">view<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n4\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"377.0372841596604\" width=\"219.27949905395508\" x=\"527.4919853210449\" y=\"407.9266515731811\"/>\n              <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"219.27949905395508\" x=\"0.0\" y=\"0.0\">controller</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"2\" leftF=\"1.7335329055786133\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"412.2765645980835\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 1</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n4:\">\n        <node id=\"n4::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"445.3031164169311\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"72.70703125\" x=\"38.92598342895508\" y=\"5.6494140625\">create action<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n4::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"45.0\" width=\"159.91864204406738\" x=\"558.5326642990112\" y=\"521.8166599273682\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"77.34765625\" x=\"41.28549289703369\" y=\"13.1494140625\">perform filters<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"diamond\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n4::n2\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"162.71328270435333\" width=\"187.54596614837646\" x=\"544.2255182266235\" y=\"607.2506530284882\"/>\n                  <y:Fill color=\"#99336635\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#993366\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#FFFFFF\" visible=\"true\" width=\"187.54596614837646\" x=\"0.0\" y=\"0.0\">action</y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"4\" bottomF=\"3.8368178606033325\" left=\"4\" leftF=\"3.9869680404663086\" right=\"3\" rightF=\"3.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 3</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n4::n2:\">\n            <node id=\"n4::n2::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"644.6271178722382\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.705078125\" x=\"43.92695999145508\" y=\"5.6494140625\">load model<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n4::n2::n1\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"721.1271178722382\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"66.02734375\" x=\"42.26582717895508\" y=\"5.6494140625\">render view<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n      </graph>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"198.3863394750308\" y=\"713.6271178722382\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"116.072265625\" x=\"1.4638671875000284\" y=\"13.1494140625\">response component<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"254.50096702575684\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"106.732421875\" x=\"6.1337890625\" y=\"13.1494140625\">request component<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"144.132230758667\" width=\"219.27949905395508\" x=\"527.4919853210449\" y=\"222.86873626708984\"/>\n              <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"219.27949905395508\" x=\"0.0\" y=\"0.0\">application</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"21\" leftF=\"20.720500946044922\" right=\"18\" rightF=\"18.0\" top=\"2\" topF=\"1.7557659149169922\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 2</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n7:\">\n        <node id=\"n7::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"262.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"73.369140625\" x=\"38.59492874145508\" y=\"5.6494140625\">resolve route<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n7::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"322.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"90.0390625\" x=\"30.259967803955078\" y=\"5.6494140625\">create controller<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <node id=\"n8\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"142.37646484375\" width=\"159.91864204406738\" x=\"313.2978515625\" y=\"224.62450218200684\"/>\n              <y:Fill color=\"#FFCC0024\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FFCC00\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"159.91864204406738\" x=\"0.0\" y=\"0.0\">entry script</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"1\" leftF=\"0.9186420440673828\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"225.33495140075684\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 4</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n8:\">\n        <node id=\"n8::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"129.0\" x=\"329.2164936065674\" y=\"262.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"85.3984375\" x=\"21.80078125\" y=\"5.6494140625\">load app config<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n8::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"129.0\" x=\"329.2164936065674\" y=\"322.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"82.052734375\" x=\"23.4736328125\" y=\"5.6494140625\">run application<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <edge id=\"e0\" source=\"n2\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n5\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-31.22050094604495\" sy=\"-22.49430537223816\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"28.00000600945461\" y=\"-193.17557203769684\">\n            <y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"17.34765625\" x=\"-8.673822115545391\" y=\"-200.52615797519684\">11<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n6\" target=\"n7::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-29.525518994973595\" y=\"-10.349628448486328\">3<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"0.9990329742431641\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.25395048761528816\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n7::e0\" source=\"n7::n0\" target=\"n7::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n0\" target=\"n8\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"-79.9882439360955\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"36.76878085201736\" y=\"-9.523307113000556\">1<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n7::n1\" target=\"n4\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"2.408647025755731\" ty=\"-181.21658369302781\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-4.962140296002758\" y=\"18.61224679946895\">4<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n3\" target=\"n4::n2::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-30.100868416252297\" y=\"-9.35060429573059\">9<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.26448415477215953\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n4::n2::n1\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"-2.2737367544323206E-13\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"17.34765625\" x=\"-83.18526519030615\" y=\"-9.350604295730818\">10<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.2786124840137136\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e7\" source=\"n8::n1\" target=\"n7\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"55.750299312644984\" sy=\"0.355224609375\" tx=\"-109.63961141576266\" ty=\"42.066115379333496\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"29.326010704040527\" y=\"-9.508390885372137\">2<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e8\" source=\"n1\" target=\"n4::n2::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-31.270312699786246\" y=\"-10.35060429573059\">8<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"1.0\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.2858946861565087\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e9\" source=\"n4::n1\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-79.9882439360951\" sy=\"2.2737367544323206E-13\" tx=\"30.048898971244075\" ty=\"-1.4999999999999991\">\n            <y:Point x=\"287.93523844627487\" y=\"544.3166599273684\"/>\n          </y:Path>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-76.70009317381192\" y=\"-9.350576400756609\">6<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.23459266172695797\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::e0\" source=\"n4::n0\" target=\"n4::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFEFD6\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-5.336933135986328\" y=\"4.999985313415493\">5<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.0\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::e1\" source=\"n4::n1\" target=\"n4::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"-81.01828954355172\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFEFD6\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-5.4491733540852465\" y=\"5.039560317993164\">7<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.0\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::n2::e0\" source=\"n4::n2::n0\" target=\"n4::n2::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n8::e0\" source=\"n8::n0\" target=\"n8::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"66px\" viewBox=\"0 0 57 66\" enable-background=\"new 0 0 57 66\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3799\" y1=\"-2276.8809\" x2=\"27.6209\" y2=\"-2306.6792\" gradientTransform=\"matrix(1 0 0 -1 0.2803 -2252.9199)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_13_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t&lt;path fill=\"#2068A3\" stroke=\"#2068A3\" d=\"M28.106,33.487c-8.112,0-12.688,4.312-12.688,10.437c0,7.422,12.688,10.438,12.688,10.438\n\t\ts14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.487,28.106,33.487z M26.288,53.051c0,0-7.135-2.093-8.805-7.201\n\t\tc-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"14.2417\" cy=\"9.1006\" r=\"53.247\" gradientTransform=\"matrix(1 0 0 -1 0.04 65.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#74AEEE\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#2068A3\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#2068A3\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.022,7.807-14.022,7.807s-10.472-2.484-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path fill=\"#5491CF\" stroke=\"#2068A3\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397c-0.514,1.027-1.669,4.084-1.669,5.148\n\t\tc0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.335-2.36c-3.601-1.419-4.071-3.063-5.89-4.854\n\t\tC12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t&lt;path fill=\"#5491CF\" stroke=\"#2068A3\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617c0.516,1.025,3.617,3.693,3.617,6.617\n\t\tc0,5.186-10.27,8.576-16.698,9.145c1.429,4.938,11.372,1.293,13.804-0.313c3.563-2.354,4.563-5.133,7.854-3.705\n\t\tC47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t&lt;path fill=\"none\" stroke=\"#2068A3\" stroke-linecap=\"round\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t&lt;path fill=\"none\" stroke=\"#2068A3\" stroke-linecap=\"round\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.623\" cy=\"-2278.646\" r=\"23.425\" fx=\"23.0534\" fy=\"-2281.1357\" gradientTransform=\"matrix(1 0 0 -1 0.2803 -2252.9199)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"5761.7578\" y1=\"11330.6484\" x2=\"5785.3872\" y2=\"11424.0977\" gradientTransform=\"matrix(0.275 0 0 0.2733 -1558.9874 -3088.4209)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M27.958,6.333c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.083,13.952,36.271,6.268,27.958,6.333z\"/&gt;\n\t&lt;path id=\"Hair_Young_Brown_1_\" fill=\"#CC9869\" stroke=\"#99724F\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n\t&lt;path fill=\"#4B4B4B\" stroke=\"#4B4B4B\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" d=\"M28.105,2\n\t\tC22.464,2,20.2,4.246,18.13,5.533C29.753,2.865,41.152,10.375,44.46,20.5C44.459,16.875,44.459,2,28.105,2z\"/&gt;\n\t&lt;path fill=\"#9B9B9B\" stroke=\"#4B4B4B\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" d=\"M11.151,17.751\n\t\tC12.878,8.25,18.686,6.309,25.273,7.127C31.295,7.875,36.93,10.491,44.459,20.5C37.777,7.125,20.278-3.375,9.903,3.921\n\t\tC5.569,6.97,4.903,13.375,11.151,17.751z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\"\n\t xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\t x=\"0px\" y=\"0px\" width=\"41px\" height=\"48px\" viewBox=\"-0.875 -0.887 41 48\" enable-background=\"new -0.875 -0.887 41 48\"\n\t xml:space=\"preserve\"&gt;\n&lt;defs&gt;\n&lt;/defs&gt;\n&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-979.1445\" x2=\"682.0508\" y2=\"-979.1445\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_1_)\" d=\"M19.625,36.763C8.787,36.763,0,34.888,0,32.575v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,34.888,30.464,36.763,19.625,36.763z\"/&gt;\n&lt;linearGradient id=\"SVGID_2_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-973.1445\" x2=\"682.0508\" y2=\"-973.1445\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_2_)\" d=\"M19.625,36.763c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.927-18.396,3.927\n\tc-9.481,0-17.396-1.959-18.396-3.927l-1.229,2C0,34.888,8.787,36.763,19.625,36.763z\"/&gt;\n&lt;path fill=\"#3C89C9\" d=\"M19.625,26.468c10.16,0,19.625,2.775,19.625,2.775c-0.375,2.721-5.367,5.438-19.554,5.438\n\tc-12.125,0-18.467-2.484-19.541-4.918C-0.127,29.125,9.465,26.468,19.625,26.468z\"/&gt;\n&lt;linearGradient id=\"SVGID_3_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-965.6948\" x2=\"682.0508\" y2=\"-965.6948\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_3_)\" d=\"M19.625,23.313C8.787,23.313,0,21.438,0,19.125v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,21.438,30.464,23.313,19.625,23.313z\"/&gt;\n&lt;linearGradient id=\"SVGID_4_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-959.6948\" x2=\"682.0508\" y2=\"-959.6948\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_4_)\" d=\"M19.625,23.313c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.926-18.396,3.926\n\tc-9.481,0-17.396-1.959-18.396-3.926l-1.229,2C0,21.438,8.787,23.313,19.625,23.313z\"/&gt;\n&lt;path fill=\"#3C89C9\" d=\"M19.476,13.019c10.161,0,19.625,2.775,19.625,2.775c-0.375,2.721-5.367,5.438-19.555,5.438\n\tc-12.125,0-18.467-2.485-19.541-4.918C-0.277,15.674,9.316,13.019,19.476,13.019z\"/&gt;\n&lt;linearGradient id=\"SVGID_5_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-952.4946\" x2=\"682.0508\" y2=\"-952.4946\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_5_)\" d=\"M19.625,10.113C8.787,10.113,0,8.238,0,5.925v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,8.238,30.464,10.113,19.625,10.113z\"/&gt;\n&lt;linearGradient id=\"SVGID_6_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-946.4946\" x2=\"682.0508\" y2=\"-946.4946\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_6_)\" d=\"M19.625,10.113c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.926-18.396,3.926\n\tc-9.481,0-17.396-1.959-18.396-3.926L0,5.925C0,8.238,8.787,10.113,19.625,10.113z\"/&gt;\n&lt;linearGradient id=\"SVGID_7_\" gradientUnits=\"userSpaceOnUse\" x1=\"644.0293\" y1=\"-943.4014\" x2=\"680.8223\" y2=\"-943.4014\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;ellipse fill=\"url(#SVGID_7_)\" cx=\"19.625\" cy=\"3.926\" rx=\"18.396\" ry=\"3.926\"/&gt;\n&lt;path opacity=\"0.24\" fill=\"#FFFFFF\" enable-background=\"new    \" d=\"M31.04,45.982c0,0-4.354,0.664-7.29,0.781\n\tc-3.125,0.125-8.952,0-8.952,0l-2.384-10.292l0.044-2.108l-1.251-1.154L9.789,23.024l-0.082-0.119L9.5,20.529l-1.65-1.254\n\tL5.329,8.793c0,0,4.213,0.903,7.234,1.07s8.375,0.25,8.375,0.25l3,9.875l-0.25,1.313l1.063,2.168l2.312,9.645l-0.521,1.416\n\tl1.46,1.834L31.04,45.982z\"/&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-vi/intro-upgrade-from-v1.md",
    "content": "Nâng cấp lên từ phiên bản 1.1\n==========================\n\nCó nhiều sự khác biệt giữa các phiên bản 1.1 và 2.0 của Yii khi cấu trúc framework được viết lại hoàn toàn cho 2.0.\nDo vậy, việc nâng cấp từ phiên bản 1.1 không dễ dàng như việc nâng cấp giữa các phiên bản nhỏ. Trong bản hướng dẫn này, bạn sẽ\nthấy sựa khác biệt chính giữa hai phiên bản.\n\nNếu bạn chưa sử dụng bản Yii 1.1, bạn có thể bỏ qua phần này một cách an toàn và chuyển trực tiếp qua mục \"[Getting started](start-installation.md)\".\n\nXin lưu ý rằng Yii 2.0 giới thiệu nhiều tính năng mới hơn so với trong phần tóm tắt này. Do vậy chúng tôi khuyến khíc bạn đọc qua toàn bộ bài hướng dẫn \nđể tìm hiểu sâu hơn về Yii 2.0. Rất có thể là một số tính năng mà trước đây bạn phải phát triển cho chính mình nhưng bây giờ là một phần của mã cốt lõi.\n\n\nCài đặt\n------------\n\nYii 2.0 được tích hợp hoàn toàn với [Composer](https://getcomposer.org/), trình quản lý các gói cho PHP. Việc cài đặt\ncác gói cốt lõi của framework, cũng như các thư viện, được thực hiện qua Composer. Vui lòng tham khảo mục\n[Hướng dẫn cài đặt Yii](start-installation.md) để tìm hiểu việc cài đặt Yii 2.0. Nếu bạn muốn tạo các thư viện\nmới, hoặc chuyển các thư viện từ phiên bản 1.1 hiện có của bạn cho tương thích với phiên bản 2.0, thì vui lòng tham khảo\nphần [Tạo các thư viện](structure-extensions.md#creating-extensions) của hướng dẫn.\n\n\nCác yêu cầu về PHP\n----------------\n\nYii 2.0 yêu cầu PHP 5.4 trở lên, đó là một cải tiến rất lớn so với phiên bản PHP 5.2 được Yii 1.1 yêu cầu.\nNhư được liệt kê dưới, có nhiều sự khác biệt về cấp độ ngôn ngữ mà bạn nên chú ý. \nDưới đây là tóm tắt về những thay đổi lớn liên quan đến PHP:\n\n- [Namespaces](https://php.net/manual/en/language.namespaces.php).\n- [Hàm ẩn danh (Anonymous functions)](https://php.net/manual/en/functions.anonymous.php).\n- Cú pháp khai báo mảng ngắn `[...elements...]` được dùng thay cho cú pháp kiểu `array(...elements...)`.\n- Thẻ echo ngắn `<?=` được dùng trong các tập tin của view. Điều này là an toàn để sử dụng bắt đầu từ PHP 5.4.\n- [SPL classes và interfaces](https://php.net/manual/en/book.spl.php).\n- [Late Static Bindings](https://php.net/manual/en/language.oop5.late-static-bindings.php).\n- [Ngày và Thời gian](https://php.net/manual/en/book.datetime.php).\n- [Traits](https://php.net/manual/en/language.oop5.traits.php).\n- [intl](https://php.net/manual/en/book.intl.php). Yii 2.0 sử dụng phần mở rộng PHP intl để hỗ trợ các tính năng quốc tế hóa.\n\n\nKhông gian tên (Namespace)\n---------\n\nThay đổi rõ ràng nhất trong Yii 2.0 là việc sử dụng các không gian tên. Hầu hết mọi lớp lõi đều được đặt không gian tên, ví dụ:\n, `yii\\web\\Request`. Tiền tố \"C\" không còn được sử dụng trong tên lớp. Sơ đồ đặt tên bây giờ theo cấu trúc thư mục. \nChẳng hạn, `yii\\web\\Request` chỉ ra rằng tệp lớp tương ứng là `web/Request.php` trong thư mục khung Yii.\n\n(Bạn có thể sử dụng bất kỳ lớp lõi nào mà không khai báo rõ ràng tệp lớp đó, điều này là nhờ trình tải lớp Yii.)\n\n\nCác Component và Object\n--------------------\n\nYii 2.0 chia lớp `CComponent` trong 1.1 thành hai lớp: [[yii\\base\\BaseObject]] và [[yii\\base\\Component]].\nThe [[yii\\base\\BaseObject|BaseObject]] class is a lightweight base class that allows defining [object properties](concept-properties.md)\nvia getters and setters. Lớp [[yii\\base\\Component|Component]] được kế thừa từ lớp [[yii\\base\\BaseObject|BaseObject]] và hỗ trợ thêm các \n[sự kiện (event)](concept-events.md) và các [hành vi (behaviors)](concept-behaviors.md).\n\nNếu lớp của bạn không cần tính năng sự kiện hoặc hành vi, bạn nên xem xét sử dụng \n[[yii\\base\\BaseObject|BaseObject]] làm lớp cơ sở. Đây thường là trường hợp cho các lớp đại diện cho cấu trúc dữ liệu cơ bản.\n\n\nObject Configuration\n--------------------\n\nLớp [[yii\\base\\BaseObject|BaseObject]] giới thiệu một cách thống nhất để cấu hình các đối tượng. Bất kỳ các lớp con của\nlớp [[yii\\base\\BaseObject|BaseObject]] nên khai báo các hàm khởi tạo (constructor) (nếu cần thiết)  theo cách sau để có thể được\ncấu hình đúng:\n\n```php\nclass MyClass extends \\yii\\base\\BaseObject\n{\n    public function __construct($param1, $param2, $config = [])\n    {\n        // ... khởi tạo trước khi cấu hình được áp dụng\n\n        parent::__construct($config);\n    }\n\n    public function init()\n    {\n        parent::init();\n\n        // ... khởi tạo sau khi cấu hình được áp dụng\n    }\n}\n```\n\nỞ trên, tham số cuối cùng của hàm tạo phải lấy một mảng cấu hình có chứa các cặp tên-giá trị để \nkhởi tạo các thuộc tính ở cuối hàm tạo. Bạn có thể ghi đè phương thức [[yii\\base\\BaseObject::init()|init()]]\nđể thực hiện công việc khởi tạo nên được thực hiện sau khi cấu hình được áp dụng.\n\nBằng cách tuân theo quy ước này, bạn sẽ có thể tạo và định cấu hình các đối tượng mới\nbằng cách sử dụng một mảng cấu hình:\n\n```php\n$object = Yii::createObject([\n    'class' => 'MyClass',\n    'property1' => 'abc',\n    'property2' => 'cde',\n], [$param1, $param2]);\n```\n\nThông tin chi tiết về cấu hình có thể được tìm thấy trong phần [Cấu hình](concept-configurations.md).\n\n\nSự kiện (Events)\n------\n\nTrong Yii 1, các sự kiện đã được tạo bằng cách xác định phương thức `on` (ví dụ, `onBeforeSave`). Trong Yii 2, bây giờ bạn có thể sử dụng bất kỳ tên sự kiện\n. Bạn kích hoạt một sự kiện bằng cách gọi phương thức [[yii\\base\\Component::trigger()|trigger()]]:\n\n```php\n$event = new \\yii\\base\\Event;\n$component->trigger($eventName, $event);\n```\n\nĐể đính kèm một trình xử lý vào một sự kiện, hãy sử dụng phương thức [[yii\\base\\Component::on()|on()]]:\n\n```php\n$component->on($eventName, $handler);\n// Để tách trình xử lý, sử dụng:\n// $component->off($eventName, $handler);\n```\n\nCó nhiều cải tiến cho các tính năng sự kiện. Để biết thêm chi tiết, vui lòng tham khảo phần [Sự kiện](concept-events.md).\n\n\nĐường dẫn cho bí danh (Aliases)\n------------\n\nYii 2.0 mở rộng việc sử dụng các bí danh đường dẫn cho cả đường dẫn tệp / thư mục và URL. Yii 2.0 hiện cũng yêu cầu một\ntên bí danh để bắt đầu bằng ký tự @, để phân biệt các bí danh với các đường dẫn tệp / thư mục thông thường hoặc URL.\nVí dụ, với bí danh `@yii` đề cập đến thư mục cài đặt Yii.Các bí danh đường dẫn được hỗ trợ ở hầu hết các vị trí trong\nmã lõi Yii. Ví dụ, [[yii\\caching\\FileCache::cachePath]] có thể lấy cả bí danh đường dẫn\nvà đường dẫn thư mục bình thường.\n\nMột bí danh đường dẫn cũng liên quan chặt chẽ đến một không gian tên lớp. Bạn nên xác định một bí danh đường dẫn cho từng không gian tên gốc, do đó\ncho phép bạn sử dụng trình tải tự động lớp Yii mà không cần cấu hình thêm. Ví dụ, vì bí danh `@yii` đề cập đến thư mục cài đặt Yii,\nmột lớp `yii\\web\\Request` có thể được tải tự động. Nếu bạn sử dụng thư viện của bên thứ ba,\nchẳng hạn như thư viện Zend Framework, bạn có thể định nghĩa một bí danh đường dẫn `@Zend` đề cập đến thư mục cài đặt của framework đó\n. Khi bạn đã thực hiện điều đó, Yii cũng sẽ có thể tự động tải bất kỳ lớp nào trong thư viện Zend Framework đó.\n\nThông tin thêm về các đường dẫn bí danh có thể được tìm thấy trong phần [Bí danh](concept-aliases.md).\n\n\nGiao diện (Views)\n-----\n\nThay đổi đáng kể nhất về views trong Yii 2 là biến `$this` trong view không còn đề cập đến\ncontroller và widget hiện tại. Mà, biến `$this` bây giờ được đề cập tới đối tượng *view*, một khái niệm mới được giới thiệu trong phiên bản 2.0.\nĐối tượng *view* là loại [[yii\\web\\View]], nó đại diện cho phần view\ncủa mô hình MVC. Nếu bạn muốn truy cập tới controller hoặc widget trong view, bạn có thể sử dụng cú pháp là `$this->context`.\n\nĐể được xuất bản một phần view trong một view khác, bạn dụng phương thức `$this->render()`, không phải là `$this->renderPartial()`.\nViệc gọi `xuất bản` bây giờ phải được lặp lại rõ ràng, như phương thức `render()` sẽ trả về kết quả việc xuất bản, thay vì\ntrực tiếp hiển thị nó. Ví dụ:\n\n```php\necho $this->render('_item', ['item' => $item]);\n```\n\nBên cạnh việc áp dụng PHP như là ngôn ngữ giao diện chính, Yii 2.0 cũng được trang bị hỗ trợ chính thức cho hai công cụ mẫu giao diện\nphổ biến là: Smarty và Twig. Công cụ mẫu của Prado không còn được hỗ trợ.\nĐể sử dụng các công cụ mẫu này, bạn cần thiết lập cấu hình ứng dụng cho `view` bằng việc thiết lập thuộc tính\n[[yii\\base\\View::$renderers|View::$renderers]]. Vui lòng tham khảo mục [Template Engines](tutorial-template-engines.md)\nđể biết thêm chi tiết.\n\n\nDữ liệu (Models)\n------\n\nYii 2.0 dùng [[yii\\base\\Model]] làm model cơ sở, tương tự như `CModel` trong 1.1.\nLớp `CFormModel` đã bị loại bỏ hoàn toàn. Thay vào đó, trong Yii 2 bạn nên kế thừa [[yii\\base\\Model]] để tạo lớp model mẫu.\n\nYii 2.0 giới thiệu một phương thức mới gọi là [[yii\\base\\Model::scenarios()|scenarios()]] để khai báo các kịch bản (scenarios) được hỗ trợ\n, và để chỉ ra kịch bản nào mà một thuộc tính cần được xác nhận, có thể được coi là an toàn hay không, v.v. Ví dụ:\n\n```php\npublic function scenarios()\n{\n    return [\n        'backend' => ['email', 'role'],\n        'frontend' => ['email', '!role'],\n    ];\n}\n```\n\nỞ trên, hai kịch bản được khai báo là: `backend` và `frontend`. Với kịch bản `backend`, cả 2 thuộc tính\n`email` và `role` là thuộc tính an toàn, và có thể được gán dữ liệu an toàn. Còn với kịch bản `frontend`, thuộc tính\n`email` có thể được gán dữ liệu an toàn trong khi đó thuộc tính `role` thì không. Cả 2 thuộc tính `email` và `role` nên được xác nhận bằng các quy tắc.\n\nPhức thức [[yii\\base\\Model::rules()|rules()]] vẫn được sử dụng để khai báo các quy tắc xác thực. Lưu ý rằng như giới thiệu về phương thức [[yii\\base\\Model::scenarios()|scenarios()]],\nthì việc xác nhận đối tượng `unsafe` không còn được hỗ trợ.\n\nTrong hầu hết các trường hợp, bạn không cần ghi đè phương thức [[yii\\base\\Model::scenarios()|scenarios()]]\nnếu trong phương thức [[yii\\base\\Model::rules()|rules()]] chỉ định đầy đủ các kịch bản sẽ tồn tại, và nếu không cần phải khai báo các thuộc tính\n` không an toàn`.\n\nĐể tìm hiểu thêm về models, vui lòng tham khảo mục [Models](structure-models.md) để biết thêm chi tiết.\n\n\nĐiều khiển (Controllers)\n-----------\n\nYii 2.0 sử dụng [[yii\\web\\Controller]] như lớp cơ sở cho controller, tương tự như lớp `CController` trong Yii 1.1.\n[[yii\\base\\Action]] là lớp cơ sở cho các lớp hành động.\n\nTác động rõ ràng nhất của những thay đổi này đối với mã của bạn là các action của bộ điều khiển sẽ trả về nội dung\nmà bạn muốn xuất bản thay vì hiển thị nó:\n\n```php\npublic function actionView($id)\n{\n    $model = \\app\\models\\Post::findOne($id);\n    if ($model) {\n        return $this->render('view', ['model' => $model]);\n    } else {\n        throw new \\yii\\web\\NotFoundHttpException;\n    }\n}\n```\n\nVui lòng tham khảo mục [Controllers](structure-controllers.md) để biết thêm chi tiết về controllers.\n\n\nWidgets\n-------\n\nYii 2.0 sử dụng [[yii\\base\\Widget]] như lớp cơ sở cho các widget, tương tự như `CWidget` trong Yii 1.1.\n\nĐể được hỗ trợ tốt hơn cho framework trong các IDEs, Yii 2.0 giới thiệu một cú pháp mới để sử dụng widgets. Các phương thức tĩnh\n[[yii\\base\\Widget::begin()|begin()]], [[yii\\base\\Widget::end()|end()]], và [[yii\\base\\Widget::widget()|widget()]]\nđã được giới thiệu, được sử dụng như vậy:\n\n```php\nuse yii\\widgets\\Menu;\nuse yii\\widgets\\ActiveForm;\n\n// Lưu ý rằng bạn phải \"echo\" kết quả để hiển thị nó\necho Menu::widget(['items' => $items]);\n\n// Truyền một mảng để khởi tạo các thuộc tính đối tượng\n$form = ActiveForm::begin([\n    'options' => ['class' => 'form-horizontal'],\n    'fieldConfig' => ['inputOptions' => ['class' => 'input-xlarge']],\n]);\n... các form input fields nằm tại đây ...\nActiveForm::end();\n```\n\nVui lòng tham khảo mục [Widgets](structure-widgets.md) để biết thêm thông tin.\n\n\nThemes\n------\n\nThemes hoạt động hoàn toàn khác nhau trong 2.0. Bây giờ các theme dựa trên cơ chế ánh xạ tới đường dẫn có liên kết tới nguồn\nđến đường dẫn file cho theme. Ví dụ, nếu đường dẫn liên kết cho theme là `['/web/views' => '/web/themes/basic']`, tiếp đến\nphiên bản cho các file view là `/web/views/site/index.php` sẽ là `/web/themes/basic/site/index.php`. Vì lý do này, các themes \nbây giờ có thể được áp dụng vào bất cứ file view, thậm chí một sự kiện được xuất bản bên ngoài của nội dung một controller hoặc một widget.\n\nNgoài ra, không còn component`CThemeManager`. Thay vào đó, `theme` bây giờ được cấu hình qua thuộc tính của thành phần `view`.\n\nVui lòng tham khảo mục [Theming](output-theming.md) để biết thêm thông tin chi tiết.\n\n\nỨng dụng Console\n--------------------\n\nCác ứng dụng Console hiện được tổ chức dưới dạng như controllers, như các ứng dụng Web. Các controller của ứng dụng Console\nnên được kế thừa từ [[yii\\console\\Controller]], tương tự như `CConsoleCommand` trong bản 1.1.\n\nĐể chạy lệnh console, nhập `yii <route>`, với `<route>` là viết tắt cho controller route\n(v.d. `sitemap/index`). Các tham số ẩn danh được truyền qua như những tham số tương ứng\ntrong phương thức hành động của controller, trong khi các đối số được đặt tên được phân tích cú pháp \ntheo các khai báo trong [[yii\\console\\Controller::options()]].\n\nYii 2.0 hỗ trợ tự động xuất nội dung của các lệnh trợ giúp từ các khối bình luận.\n\nVui lòng tham khảo mục [Console Commands](tutorial-console.md) để biết thêm thông tin chi tiết.\n\n\nI18N\n----\n\nYii 2.0 loại bỏ các phần định dạng ngày và phần định dạng số tích hợp có lợi cho [PECL intl PHP module](https://pecl.php.net/package/intl).\n\nDịch văn bản bây giờ được thực hiện qua thành phần ứng dụng `i18n`.\nThành phần này quản lý một tập hợp các nguồn văn bản, cho phép bạn sử dụng các nguồn văn bản khác nhau dựa trên các loại văn bản.\n\nVui lòng tham khảo mục [Internationalization](tutorial-i18n.md) để biết thêm thông tin chi tiết.\n\n\nBộ lọc Action\n--------------\n\nBộ lọc Action bây giờ được thực hiện thông qua các hành vi (behaviors. Để xác định một cái mới, tùy biến filter, kế thừa tư [[yii\\base\\ActionFilter]].\nĐể dùng các filter, gắn các lớp filter vào controller\nnhư các behavior. Ví dụ, để sử dụng filter [[yii\\filters\\AccessControl]], bạn sẽ có đoạn mã sau trong controller:\n\n```php\npublic function behaviors()\n{\n    return [\n        'access' => [\n            'class' => 'yii\\filters\\AccessControl',\n            'rules' => [\n                ['allow' => true, 'actions' => ['admin'], 'roles' => ['@']],\n            ],\n        ],\n    ];\n}\n```\n\nVui lòng tham khảo mục [Filtering](structure-filters.md) để biết thêm thông tin chi tiết.\n\n\nAssets\n------\n\nYii 2.0 giới thiệu một khái niệm mới gọi là *asset bundle* thay thế khái niệm gói script được tìm thấy trong Yii 1.1.\n\nMột asset bundle là bộ tổng hớp các file asset (v.d. JavaScript files, CSS files, image files, vv.)\ntrong một thư mục. Mỗi asset bundle được đại diện như một class kế thừa từ [[yii\\web\\AssetBundle]].\nBằng việc đăng ký một asset bundle qua [[yii\\web\\AssetBundle::register()]], bạn cho ra các\nassets nằm trong bundle có thể được truy cập từ Web. Không giống như trong Yii 1, các trang đăng ký các bundle sẽ tự động\ncó chứa các tham chiếu đến các file JavaScript và CSS files được quy định trong bundle đó.\n\nVui lòng tham khảo mục [Managing Assets](structure-assets.md) để biết thêm thông tin chi tiết.\n\n\nHelpers\n-------\n\nYii 2.0 giới thiệu thêm nhiều các lớp helper thường được sử dụng, bao gồm.\n\n* [[yii\\helpers\\Html]]\n* [[yii\\helpers\\ArrayHelper]]\n* [[yii\\helpers\\StringHelper]]\n* [[yii\\helpers\\FileHelper]]\n* [[yii\\helpers\\Json]]\n\nVui lòng tham khảo mục [Helper Overview](helper-overview.md) để biết thêm thông tin chi tiết.\n\nForms\n-----\n\nYii 2.0 giới thiệu khái niệm *field* cho việc xây dựng form sử dụng [[yii\\widgets\\ActiveForm]]. Một field\nbao gồm chứa một label, một input, một văn bản báo lỗi (error message), và/hoặc một trợ giúp văn bản (hint text).\nMột field được đại diện như một đối tượng [[yii\\widgets\\ActiveField|ActiveField]].\nĐể sử dụng các field, bạn có thể xây dựng một form gọn gàng hơn trước đây:\n\n```php\n<?php $form = yii\\widgets\\ActiveForm::begin(); ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n    <div class=\"form-group\">\n        <?= Html::submitButton('Login') ?>\n    </div>\n<?php yii\\widgets\\ActiveForm::end(); ?>\n```\n\nVui lòng tham khảo mục [Creating Forms](input-forms.md) để biết thêm thông tin chi tiết.\n\n\nXây dựng các truy vấn (Query Builder)\n-------------\n\nTrong 1.1, xây dựng truy vấn nằm rải rác trong một số lớp, bao gồm lớp `CDbCommand`,\n`CDbCriteria`, và `CDbCommandBuilder`. Yii 2.0 đại diện cho các truy vấn CSDL nằm trong các khoản của đối tượng [[yii\\db\\Query|Query]]\ncó thể chuyển qua các lệnh SQL với sự giúp đỡ của đối tượng [[yii\\db\\QueryBuilder|QueryBuilder]] đàng sau.\nVí dụ:\n\n```php\n$query = new \\yii\\db\\Query();\n$query->select('id, name')\n      ->from('user')\n      ->limit(10);\n\n$command = $query->createCommand();\n$sql = $command->sql;\n$rows = $command->queryAll();\n```\n\nCách tốt nhất, việc xây dựng các truy vấn có thể được dùng khi làm việc với [Active Record](db-active-record.md).\n\nVui lòng tham khảo mục [Query Builder](db-query-builder.md) để biết thêm thông tin chi tiết.\n\n\nActive Record\n-------------\n\nYii 2.0 giới thiệu rất nhiều thay đổi về [Active Record](db-active-record.md). The two most obvious ones involve\nquery building và relational query handling.\n\nLớp `CDbCriteria` trong bản 1.1 được thay thế bởi lớp [[yii\\db\\ActiveQuery]] trong Yii 2. Lớp này được kế thừa tư [[yii\\db\\Query]], và đồng thời\nkế thừa tất cả các phương thức xây dựng câu truy vấn. Bạn có thể thử gọi phương thức [[yii\\db\\ActiveRecord::find()]] để xây dựng câu truy vấn:\n\n```php\n// để nhận tất cả các danh sách khách hàng có điều kiện được *active* và sắp xếp theo thứ tự ID:\n$customers = Customer::find()\n    ->where(['status' => $active])\n    ->orderBy('id')\n    ->all();\n```\n\nĐể khai bao một quan hệ (relation), cách đơn giản là khai báo một phương thức getter có trả về đối tượng [[yii\\db\\ActiveQuery|ActiveQuery]].\nTên thuộc tính được xác định bởi getter đại diện cho tên quan hệ. Ví dụ, đoạn code sau khai báo một quan hệ\nlà `orders` (trong 1.1, bạn sẽ phải khai báo quan hệ ở vị trí nằm giữa phương thức `relations()`):\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public function getOrders()\n    {\n        return $this->hasMany('Order', ['customer_id' => 'id']);\n    }\n}\n```\n\nBây giờ bạn dùng `$customer->orders` để truy cập các orders trong khách hàng từ bảng quan hệ. Bạn có thể dùng đoạn mã sau\nđể thực hiện truy vấn quan hệ nhanh chóng với điều kiện truy vấn tùy chỉnh::\n\n```php\n$orders = $customer->getOrders()->andWhere('status=1')->all();\n```\n\nWhen eager loading a relation, Yii 2.0 does it differently from 1.1. In particular, in 1.1 a JOIN query\nwould be created to select both the primary and the relational records. In Yii 2.0, two SQL statements are executed\nwithout using JOIN: the first statement brings back the primary records and the second brings back the relational\nrecords by filtering with the primary keys of the primary records.\n\nInstead of returning [[yii\\db\\ActiveRecord|ActiveRecord]] objects, you may chain the [[yii\\db\\ActiveQuery::asArray()|asArray()]]\nmethod when building a query to return a large number of records. This will cause the query result to be returned\nas arrays, which can significantly reduce the needed CPU time and memory if large number of records . Ví dụ:\n\n```php\n$customers = Customer::find()->asArray()->all();\n```\n\nMột thay đổi khác là bạn không thể khai báo các giá trị mặc định thuộc tính thông qua các thuộc tính công khai nữa.\nNếu bạn cần những thứ đó, bạn nên đặt chúng trong phương thức init của lớp bản ghi của bạn.\n\n```php\npublic function init()\n{\n    parent::init();\n    $this->status = self::STATUS_NEW;\n}\n```\n\nCó một số vấn đề với việc ghi đè hàm tạo của lớp ActiveRecord trong 1.1. Các vấn đề này không còn xuất hiện trong bản\n2.0 nữa. Lưu ý rằng khi thêm tham số vào hàm tạo, bạn có thể phải ghi đè [[yii\\db\\ActiveRecord::instantiate()]].\n\nCó nhiều thay đổi và cải tiến khác đối với Active Record. Vui lòng tham khảo mục\n[Active Record](db-active-record.md) để biết thêm thông tin chi tiết.\n\n\nCác hành vi trong Active Record\n-----------------------\n\nTrong 2.0, chúng tôi đã lược bỏ lớp lớp base của hành vi `CActiveRecordBehavior`. Nếu bạn muốn tạo một hành vi cho Active Record,\nbạn cần được kế thừa trực tiếp từ lớp `yii\\base\\Behavior`. Nếu trong lớp hành vi cần được phản hồi một số sự kiện của lớp cha, bạn\ncần ghi đề phương thức `events()` như sau:\n\n```php\nnamespace app\\components;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\base\\Behavior;\n\nclass MyBehavior extends Behavior\n{\n    // ...\n\n    public function events()\n    {\n        return [\n            ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',\n        ];\n    }\n\n    public function beforeValidate($event)\n    {\n        // ...\n    }\n}\n```\n\n\nUser và IdentityInterface\n--------------------------\n\nLớp `CWebUser` trong 1.1 bây giời được thay thế bởi [[yii\\web\\User]], và không còn hỗ trợ lớp\n`CUserIdentity` nữa. Thay vì đó, bạn nên hiện thực lớp [[yii\\web\\IdentityInterface]] để được sử dụng được đơn giản hơn.\nTrong mẫu dự án advanced có tích hợp một vài ví dụ như vậy .\n\nVui lòng tham khảo mục [Authentication](security-authentication.md), [Authorization](security-authorization.md), và [Mẫu dự án Advanced](https://www.yiiframework.com/extension/yiisoft/yii2-app-advanced/doc/guide) để biết thêm thông tin chi tiết.\n\n\nQuản lý các URL\n--------------\n\nQuản lý các URL trong Yii 2 tương tự như trong 1.1. Một cải tiến lớn cho việc quản lý các URL bây giờ hỗ trợ tùy chọn các\ntham số. Ví dụ, nếu bạn có một quy tắc (rule) được khai báo như sau, tiếp sau đó quy tắc này phù hợp với cả hai\n`post/popular` và `post/1/popular`. Trong 1.1, bạn sẽ phải sử dụng hai quy tắc để đạt được cùng một mục tiêu.\n\n```php\n[\n    'pattern' => 'post/<page:\\d+>/<tag>',\n    'route' => 'post/index',\n    'defaults' => ['page' => 1],\n]\n```\n\nVui lòng tham khảo mục [Url manager docs](runtime-routing.md) để biết thêm thông tin chi tiết.\n\nMột thay đổi quan trọng trong quy ước đặt tên cho các routes và tên camel case của controllers\nvà actions hiện được chuyển đổi thành ký tự thấp mỗi từ được phân tách bằng một gạch ngang, bd. định danh cho controller\n`CamelCaseController` sẽ là `camel-case`.\nXem thêm ở mục [định danh controller](structure-controllers.md#controller-ids) và [định danh action](structure-controllers.md#action-ids) để biết thêm thông tin.\n\n\nDùng Yii 1.1 và 2.x cùng với nhau\n------------------------------\n\nNếu bạn có các đoạn mã Yii 1.1 trước đây mà bạn muốn sử dụng cùng với Yii 2.0, vui lòng tham khảo mục\n[Using Yii 1.1 and 2.0 Together](tutorial-yii-integration.md#using-both-yii2-yii1).\n\n"
  },
  {
    "path": "docs/guide-vi/intro-yii.md",
    "content": "Yii là gì\n===========\n\nYii là một PHP Framework mã nguồn mở và hoàn toàn miễn phí, có hiệu năng xử lý cao, phát triển tốt nhất trên các ứng dụng Web 2.0, sử dụng tối đa các thành phần (component-based PHP framework) để tăng tốc độ viết ứng dụng.\nTên Yii (được phát âm là `Yee` hoặc `[ji:]`) ở Trung Quốc có nghĩa là  \"thật đơn giản và luôn phát triển\" (Hán tự \"易\", âm \"dịch\"). Nghĩa thứ hai có thể đọc ngắn gọn là **Yes It Is**!\n\n\nYii thích hợp nhất để làm gì?\n---------------------\n\nYii, nói chung, là một framework phát triển ứng dụng Web nên có thể dùng để viết mọi loại ứng dụng Web \nvà sử dụng ngôn ngữ lập trình PHP. Yii rất nhẹ và được trang bị giải pháp cache tối ưu nên đặc biệt \nhữu dụng cho ứng dụng web có dung lượng dữ liệu trên đường truyền lớn như web portal, forum, CMS, e-commerce, \ncác dự án thương mại điện tử và các dịch vụ Web RESTful..\n\n\nSo sánh Yii Với các Frameworks khác?\n-------------------------------------------\n\nNếu bạn có kinh nghiệm làm việc với các framework khác, bạn sẽ rất vui mừng khi thấy những nỗ lực của Yii:\n\n- Giống như những PHP frameworks khác, Yii sử dụng mô hình MVC (Model-View-Controller) tổ chức code một cách hợp lý và có hệ thống.\n- Yii tạo ra code đơn giản và thanh lịch, đây là triết lý trong chương trình. Yii sẽ không bao giờ \n  cố gắng tạo ra những mấu thiết kế quá an toàn và ít có sự thay đổi.\n- Yii là framework hoàn chỉnh, cung cấp nhiều tính năng và được xác minh như: query builders, thao tác dữ liệu với\n  ActiveRecord được dùng cho CSDL quan hệ và NoSQL; hỗ trợ phát triển RESTful API; sự hỗ trợ đa bộ nhớ cache; và nhiều hơn.\n- Yii rất dễ mở rộng. Bạn có thể tùy chình hoặc thay thế bất kỳ một trong những bộ code chuẩn. Bạn cũng có thể\n  tận dụng lợi thế của kiến trúc mở rộng chuẩn Yii để sử dụng hoặc phát triển mở rộng phân phối..\n- Hiệu suất cao luôn luôn là một trong những mục tiêu chính của Yii.\n\nYii không chỉ được phát triển từ một người, nó được hỗ trợ bởi [đội ngũ phát triển cốt lõi mạnh mẽ][yii_team], cũng như một cộng đồng lớn, trong đó các chuyên gia liên tục\nđóng góp cho sự phát triển của Yii. Nhóm nghiên cứu phát triển Yii giữ một mắt đóng trên các xu hướng phát triển Web mới nhất và trên thực hành tốt nhất và \ncác tính năng được tìm thấy trong các khuôn khổ và các dự án khác. Các thực hành tốt nhất và các tính năng được tìm thấy ở những nơi khác có liên quan nhất thường xuyên \nđược đưa vào khuôn khổ lõi và tiếp xúc thông qua giao diện đơn giản và thanh lịch.\n\n[yii_team]: https://www.yiiframework.com/team\n\nCác phiên bản Yii\n------------\n\nYii Hiện nay có hai phiên bản chính: 1.1 và 2.0. Phiên bản 1.1 là phiên bản cũ và bây giờ là trong chế độ bảo trì. Tiếp đến, phiên bản 2.0 là phiên bản đuọc viết lại hoàn toàn Yii, sử dụng các\ncông nghệ mới và giao thức mới, bao gồm trình quản lý gói Composer, các tiêu chuẩn code PHP PSR, namespaces, traits, và như vậy. Phiên bản 2.0 đại diện cho sự hình thành của framework \nvà sẽ nhận được những nỗ lực phát triển chính trong vài năm tới. \nHướng dẫn này chủ yếu là về phiên bản 2.0.\n\n\nYêu cầu hệ thống và các điều kiện cần thiết\n------------------------------\n\nYii 2.0 đòi hỏi phiên bản PHP 7.4.0 hoặc cao hơn. Bạn có thể chạy bất kỳ gói Yii đi kèm với các yêu cầu hệ thống. \nkiểm tra xem những gì các đặc điểm cụ thể của từng cấu hình PHP.\n\nĐể tìm hiểu Yii, bạn cần có kiến thức cơ bản về lập trình hướng đối tượng (OOP), vì Yii là một framework hướng đối tượng\nthuần túy. Yii 2.0 cũng sử dụng các tính năng PHP mới nhất, chẳng hạn như [namespaces](https://www.php.net/manual/en/language.namespaces.php) và [traits](https://www.php.net/manual/en/language.oop5.traits.php).\nHiểu được những khái niệm này sẽ giúp bạn nhanh chóng nắm bắt Yii 2.0.\n"
  },
  {
    "path": "docs/guide-vi/start-databases.md",
    "content": "Làm việc với CSDL\n======================\n\nPhần này sẽ hướng dẫn làm thế nào để tạo mới trang có chức năng hiển thị dữ liệu các thành phố (country) và được lấy\ntừ bảng `country` nằm trong cơ sở dữ liệu. Để thực hành tốt bài hướng dẫn, bạn cần cấu hình các kết nối tới CSDL,\ntạo class [Active Record](db-active-record.md), taọ một [action](structure-controllers.md),\nvà tạo mới [view](structure-views.md).\n\nTóm tắt những nội dung chính:\n\n* Cấu hình kết nối tới CSDL\n* Đinh nghĩa lớp Active Record\n* Sử dụng lớp Active Record để truy vấn dữ liệu\n* Hiển thị và phân trang dữ liệu trên view\n\nLưu ý để thực hiện được bài hướng dẫn này, bạn cần có kiến thức về CSDL.\nRiêng ở phần này, bạn cần có kiến thức về tạo mới CSDL, và làm thế nào để thực thi các câu lệnh SQL sử dụng công cụ ở phía client.\n\n\nChuẩn bị <span id=\"preparing-database\"></span>\n----------------------\n\nĐầu tiên, bạn cần tạo mới CSDL tên là `yii2basic`, từ bây giờ bạn sẽ dùng CSDL này để lấy dữ liệu.\nYii hỗ trợ nhiều CSDL trong ứng dụng, bạn có thể dùng những CSDL như SQLite, MySQL, PostgreSQL, MSSQL hoặc Oracle. Để cho đơn giản, \nMySQL sẽ được dùng trong bài hướng dẫn này.\n\nTiếp đến, tạo mới bảng vào CSDL tên là `country` , đồng thời chèn thêm dữ liệu. You may run the following SQL statements to do so:\n\n```sql\nCREATE TABLE `country` (\n  `code` CHAR(2) NOT NULL PRIMARY KEY,\n  `name` CHAR(52) NOT NULL,\n  `population` INT(11) NOT NULL DEFAULT '0'\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nINSERT INTO `country` VALUES ('AU','Australia',24016400);\nINSERT INTO `country` VALUES ('BR','Brazil',205722000);\nINSERT INTO `country` VALUES ('CA','Canada',35985751);\nINSERT INTO `country` VALUES ('CN','China',1375210000);\nINSERT INTO `country` VALUES ('DE','Germany',81459000);\nINSERT INTO `country` VALUES ('FR','France',64513242);\nINSERT INTO `country` VALUES ('GB','United Kingdom',65097000);\nINSERT INTO `country` VALUES ('IN','India',1285400000);\nINSERT INTO `country` VALUES ('RU','Russia',146519759);\nINSERT INTO `country` VALUES ('US','United States',322976000);\n```\n\nĐến đây, bạn có CSDL là `yii2basic`, có chứa bảng `country` có 3 cột và 10 trường dữ liệu.\n\nCấu hình kết nối tới CSDL <span id=\"configuring-db-connection\"></span>\n---------------------------\n\nTrước tiên, hãy chắc chắn rằng bạn đã cài 2 gói PHP [PDO](https://www.php.net/manual/en/book.pdo.php) và\nPDO driver dành cho các CSDL mà đang sử dụng(ví dụ `pdo_mysql` cho MySQL). Đối với các CSDL quan hệ thì những gói này \ncần phải có.\n\nSau khi những yêu cầu trên được cài đặt, mở file `config/db.php` và thay đổi các tham số chính xác tới CSDL. Mặc định,\nfile sẽ có những đoạn code sau:\n\n```php\n<?php\n\nreturn [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=localhost;dbname=yii2basic',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n];\n```\n\nFile `config/db.php` là file điển hình dành cho cấu hình ứng dụng [configuration](concept-configurations.md). Các tham số được mô tả trong file\ncần thiết để tạo mới và khởi tạo các thể hiện [[yii\\db\\Connection]] và thực hiện các câu lệnh truy vấn\n\nCác thông tin cấu hình về CSDL ở trên được truy cập qua ứng dụng qua câu lệnh `Yii::$app->db`.\n\n> Lưu ý: File `config/db.php` sẽ chứa các thông tin chính trong việc cấu hình ứng dụng `config/web.php`, \n  Những thông tin làm thế nào để [ứng dụng](structure-applications.md) cần được khởi tạo.\n  Bạn có thể tham khảo thêm trong phần [cấu hình ứng dụng](concept-configurations.md) .\n\n\nTạo mới class Active Record <span id=\"creating-active-record\"></span>\n-------------------------\n\nĐể thể hiện và thao tác với bảng dữ liệu `country`, ta tạo mới class[Active Record](db-active-record.md)-\ntên là `Country`, và lưu vào file `models/Country.php`.\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass Country extends ActiveRecord\n{\n}\n```\n\nLớp `Country` được kế thừa từ [[yii\\db\\ActiveRecord]]. Bạn không cần viết bất kỳ đoạn mã nào vào phần này! Chỉ với đoạn mã trên, \nYii sẽ biết được tên bảng từ tên của lớp tương ứng. \n\n> Info: Nếu tên lớp không trùng với tên trong bảng dữ liệu, bạn có thể\nghi đè phương thức [[yii\\db\\ActiveRecord::tableName()]] để miêu tả rõ ràng về tên bảng dữ liệu.\n\nDùng lớp `Country`, bạn sẽ dễ dàng hơn trong việc thao tác với bảng `country`, ví dụ:\n\n```php\nuse app\\models\\Country;\n\n// lấy danh sách tử bảng country và sắp xếp theo thuộc tính \"name\"\n$countries = Country::find()->orderBy('name')->all();\n\n// lấy dữ liệu có khóa là \"US\"\n$country = Country::findOne('US');\n\n// in kết quả \"United States\"\necho $country->name;\n\n// thay đổi tên country thành \"U.S.A.\" và lưu vào csdl\n$country->name = 'U.S.A.';\n$country->save();\n```\n\n> Lưu ý: Active Record khá là mạnh cho việc truy cập csdl theo hướng lập trình hướng đối tượng.\nBạn có thể xem thêm ở mục [Active Record](db-active-record.md). Cách khác, bạn có thể thao tác với csdl ở mức độ đơn giản hơn bằng việc truy cập qua đối tượng [Data Access Objects](db-dao.md).\n\n\nTạo hành động (action) <span id=\"creating-action\"></span>\n------------------\n\nĐể hiển thị dữ liệu country tới người dùng, bạn cần tạo mới hành động. Thay vì đặt các hành động ở `site`\ncontroller, giống như đã làm ở phần trước, thì tạo controller mới có ý nghĩa hơn\nđặc biệt liên quan tới dữ liệu về coutry. Tên controller là `CountryController`, và tạo mới\nhành động `index` ở trong đó, bạn có thể tham khảo ở phần dưới.\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\nuse yii\\data\\Pagination;\nuse app\\models\\Country;\n\nclass CountryController extends Controller\n{\n    public function actionIndex()\n    {\n        $query = Country::find();\n\n        $pagination = new Pagination([\n            'defaultPageSize' => 5,\n            'totalCount' => $query->count(),\n        ]);\n\n        $countries = $query->orderBy('name')\n            ->offset($pagination->offset)\n            ->limit($pagination->limit)\n            ->all();\n\n        return $this->render('index', [\n            'countries' => $countries,\n            'pagination' => $pagination,\n        ]);\n    }\n}\n```\n\nLưu nội dung đoạn mã trên vào file `controllers/CountryController.php`.\n\nHành động `index` sẽ gọi các phương thức `Country::find()`. Đây là phương thức nằm ở Active Record nhằm xây dựng câu lệnh truy vấn và lấy tất cả dữ liệu trong bảng `country`.\nĐể hạn chế số lượng dữ liệu mỗi khi gửi yêu cầu, đối tượng [[yii\\data\\Pagination]] sẽ phân trang dữ liệu.\n Mục đích khi dùng đối tượng `Pagination` là:\n\n* Thiết lập điều kiện `offset` và `limit` cho câu lệnh mỗi khi  lấy liệu ra (mỗi lần chỉ hiển thị 5 kết quả).\n* Dữ liệu được nhúng vào view để hiển thị số trang và bao gồm danh sách các button, sẽ được giải thích ở phần sau.\n\nXem những dòng cuối, hành động `index` sẽ đổ ra view có tên là `index`, đồng thời gửi dữ liệu về country và thông tin về phân trang.\n\n\nTạo View <span id=\"creating-view\"></span>\n---------------\n\nTrong thư mục `views`, bước một tạo thư mục con là `country`. Thư mực này được dùng để giữ những view được đổ ra\ntừ controller `country`. Trong thư mục `views/country`, tạo mới file tên là `index.php`\nvà chứa đoạn mã sau:\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\LinkPager;\n?>\n<h1>Countries</h1>\n<ul>\n<?php foreach ($countries as $country): ?>\n    <li>\n        <?= Html::encode(\"{$country->name} ({$country->code})\") ?>:\n        <?= $country->population ?>\n    </li>\n<?php endforeach; ?>\n</ul>\n\n<?= LinkPager::widget(['pagination' => $pagination]) ?>\n```\n\nView trên có 2 phần liên quan tới hiển thị dữ liệu về country. Phần đầu tiên, cung cấp danh sách country và in ra dưới dạng danh sách .\nPhần tiếp, một widget [[yii\\widgets\\LinkPager]] được sinh ra và dùng các thông tin truyển từ action xuống để phân trang.\nĐối tượng  `LinkPager` là một widget có chức năng hiển thị danh sách các button. Mỗi khi click vào mỗi button này sẻ cập nhật lại dữ liệu country\nở mỗi trang tương ứng.\n\n\nXem demo <span id=\"trying-it-out\"></span>\n-------------\n\nTruy cập vào Url sau và xem kết quả:\n\n```\nhttps://hostname/index.php?r=country/index\n```\n\n![Danh sách Country](images/start-country-list.png)\n\nHãy xem, bạn sẽ thấy một trang hiển thị 4 country. Phần dưới danh sách các country, bạn sẽ thấy các nút phân trang.\nNếu bạn click vào button \"2\", bạn sẽ thấy 5 country khác trong CSDL: trang thứ 2 trong CSDL country.\nBạn để ý rằng URL trong trình duyệt cũng thay đổi.\n\n```\nhttps://hostname/index.php?r=country/index&page=2\n```\n\nTrong luồng xử lý này, đối tượng [[yii\\data\\Pagination|Pagination]] sẽ cung cấp tất cả những hàm cần thiết cho việc phân trang:\n\n* Khởi tạo, đối tượng [[yii\\data\\Pagination|Pagination]] hiển thị ở trang đầu tiên , điều này được thực hiện câu lệnh truy vấn SELECT từ country\n  với mệnh đề `LIMIT 5 OFFSET 0`. Như kết quả trên, 5 country đầu tiên sẽ lấy ra và hiển thị.\n* Widget [[yii\\widgets\\LinkPager|LinkPager]] sẽ tạo các buttons cùng với các URL( liên kết)\n  được tạo bởi phương thức [[yii\\data\\Pagination::createUrl()|Pagination]]. Các URL sẽ chứa tham số `page`, page sẽtương ứng với số trang khác nhau trong CSDL country.\n* Nếu bạn click vào button \"2\",  route `country/index` được gọi và đồng thời route sẽ được gắn và giữ.\n  Đối tượng [[yii\\data\\Pagination|Pagination]] đọc số trang từ tham số `page` từ Url và thiết lập trang trang hiện tại là 2.\n  Việc truy vấn dữ liệu country tương tự như mệnh đề `LIMIT 5 OFFSET 5` và trả về tiếp 5 country để hiển thị\n\n\nTổng kết <span id=\"summary\"></span>\n-------\n\nBài hướng dẫn này, giúp bạn tìm hiểu và làm việc với CSDL. Bạn cũng được tìm hiều đối tượng [[yii\\data\\Pagination]] và [[yii\\widgets\\LinkPager]]. giúp cho việc\nlấy và hiển thì dữ liệu trên trang. \n\nTrong phần tới, bạn sẽ được tìm hiểu về công cụ generate code khá mạnh, được gọi là [Gii](tool-gii.md),\ngiúp bạn nhanh chóng thực hiện một số tính năng tương tự, những hoạt động thao tác với các bảng trong CSDL như Tạo-Xem-Cập nhật-Xóa (CRUD)\n. Trong thực tế, những mã nguồn được viết có thể tự động generate trong Yii sử dụng công cụ Gii.\n"
  },
  {
    "path": "docs/guide-vi/start-forms.md",
    "content": "Làm việc với Forms\n==================\n\nỞ phần này sẽ hướng dẫn làm thế nào để tạo mới trang Web cho phép ứng dụng lấy các thông tin về user từ form.\nTrang này sẽ có chức năng hiển thị form cho user cùng với các input như name (tên người dùng) và email.\nSau khi nhận hai thông tin về user, trang web sẽ hiển thị thông tin tới user.\n\nĐể làm được trang web này, bên cạnh tạo ra [action](structure-controllers.md) và\nhai giao diện [views](structure-views.md), bạn cần phải tạo ra đối tượng [model](structure-models.md) để xử lý các nghiệp vụ logic truy xuất CSDL.\n\nTrong phần này, bạn sẽ được tìm hiểu về:\n\n* Tạo đối tượng [model](structure-models.md) nhận thông tin từ user được nhập từ form\n* Khai báo rules để xách minh dữ liệu nhập vào\n* Xây dựng form HTML ở [view](structure-views.md)\n\n\nTạo Model <span id=\"creating-model\"></span>\n----------------\n\nDữ liệu của user cần xử lý sẽ đại diện bởi lớp model `EntryForm` sau đây và\nđược lưu ở file `models/EntryForm.php`. Tham khảo thêm về phần [Class Autoloading](concept-autoloading.md)\nđể biết thêm chi tiết về quy tắc đặt tên cho các lớp.\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse yii\\base\\Model;\n\nclass EntryForm extends Model\n{\n    public $name;\n    public $email;\n\n    public function rules()\n    {\n        return [\n            [['name', 'email'], 'required'],\n            ['email', 'email'],\n        ];\n    }\n}\n```\n\nLớp trên được kế thừa từ lớp [[yii\\base\\Model]], lớp này được Yii cung cấp , thường được dùng cho việc xử lý dữ liệu từ form.\n\n> Lưu ý: [[yii\\base\\Model]] là lớp cơ sở cho việc tương tác với các lớp dữ liệu và nó *không* liên quan tới các bảng trong CSDL.\n[[yii\\db\\ActiveRecord]] là lớp thường được dùng với CSDL mỗi lớp này sẽ tương xứng với các bảng trong CSDL.\n\nLớp `EntryForm` chứa hai biến ở phạm vi toàn cục (public), `name` và `email`, Các biến này sẽ được dùng để lưu trữ dữ liệu\nkhi người dùng nhập và gửi lên. Lớp này đồng thời chứa phương thức là `rules()`, phương thức này trả về tập quy tắc để xác thực\ndữ liệu. Các quy tắc chứng thực được khai báo ở phần trên với ý nghĩa rằng.\n\n* cả hai giá trị `name` và `email` cần phải có\n* giá trị `email` phải đúng cú pháp là địa chỉ email\n\nNếu đã có đối tượng `EntryForm` cùng với dữ liệu user đã nhập, bạn có thể sử dụng phương thức\n[[yii\\base\\Model::validate()|validate()]] để xác thực dữ liệu mỗi khi user gửi lên. Việc xác thực dữ liệu sai sẽ\nthiết lập thuộc tính [[yii\\base\\Model::hasErrors|hasErrors]] thành \"true\", và bạn có thể xem thông tin về việc xác thực lỗi\ntừ phương thức [[yii\\base\\Model::getErrors|errors]].\n\n```php\n<?php\n$model = new EntryForm();\n$model->name = 'Qiang';\n$model->email = 'bad';\nif ($model->validate()) {\n    // Xác thực thành công!\n} else {\n    // Xác thực lỗi!\n    // Use $model->getErrors()\n}\n```\n\n\nTạo Action <span id=\"creating-action\"></span>\n------------------\n\nTiếp theo, trong controller `site` bạn sẽ tạo action là `entry` action này cần dùng tới model. Quy trình và cách tạo mới action\nđã được hướng dẫn ở mục [Saying Hello](start-hello.md).\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\nuse app\\models\\EntryForm;\n\nclass SiteController extends Controller\n{\n    // ...existing code...\n\n    public function actionEntry()\n    {\n        $model = new EntryForm();\n\n        if ($model->load(Yii::$app->request->post()) && $model->validate()) {\n            // valid data received in $model\n\n            // do something meaningful here about $model ...\n\n            return $this->render('entry-confirm', ['model' => $model]);\n        } else {\n            // either the page is initially displayed or there is some validation error\n            return $this->render('entry', ['model' => $model]);\n        }\n    }\n}\n```\n\nAction này sẽ tạo đối tượng `EntryForm`. Sau khi được khởi tạo, nó sẽ lấy các thông tin thông qua biến\n `$_POST`, biến này được Yii cung cấp [[yii\\web\\Request::post()]].\nNếu dữ liệu gửi đến cho model thành công(chẳng hạn., khi user gửi thông tin từ HTML form), action sẽ gọi phương thức\n[[yii\\base\\Model::validate()|validate()]] để chắc chắn rằng những giá trị được nhập vào là hợp lý.\n\n> Thông tin thêm: Thành phần `Yii::$app` được mô tả ở mục [application](structure-applications.md),\n  Thành phần này là một mẫu thiết kế singleton cho phép truy cập ở toàn cục. Được hoạt động như một [service locator](concept-service-locator.md) that\n  để cung cấp các thành phần như `request`, `response`, `db`, vv. nhằm để hỗ trợ thêm các chức năng đặc biệt.\n  Ở đoạn code trên, component `request`  được khởi tạo bỏi ứng dụng dùng để truy cập dữ liệu từ `$_POST`.\n\nNếu không có lỗi gì, action sẽ trả về (render) view tên là `entry-confirm` để xác nhận dữ liệu được gửi lên.\n. Nếu dữ liệu trống hoặc gặp lỗi, dữ liệu sẽ được gửi về view `entry`, chứa form HTML, cùng với các thông điệp ở việc xác thực bị lỗi.\n\n> Lưu ý: ở bài hướng dẫn này, chúng ta chỉ xác nhận trang khi có dũ liệu hợp lệ. Bài thực hành này,\n  bạn cần lưu ý việc sử dụng các phương thức [[yii\\web\\Controller::refresh()|refresh()]] hoặc [[yii\\web\\Controller::redirect()|redirect()]]\n  nhằm để tránh [form resubmission problems](https://en.wikipedia.org/wiki/Post/Redirect/Get).\n\n\nTạo Views <span id=\"creating-views\"></span>\n--------------\n\nCuối cùng, chúng ta tạo mới 2 tập tin view có tên là `entry-confirm` và `entry`. Những view này sẽ được trả về như được mô tả ở trên từ action `entry`.\n\nView `entry-confirm` đơn giản chỉ hiển thị dữ liệu cho 2 thuộc tính name và email . View này được lưu trữ ở tập tin `views/site/entry-confirm.php`.\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n<p>Bạn đã nhập với những thông tin như sau:</p>\n\n<ul>\n    <li><label>Name</label>: <?= Html::encode($model->name) ?></li>\n    <li><label>Email</label>: <?= Html::encode($model->email) ?></li>\n</ul>\n```\n\nView `entry` sẽ hiển thị một form chứa các mã HTML.  View này được lưu trữ ở tập tin `views/site/entry.php`.\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n?>\n<?php $form = ActiveForm::begin(); ?>\n\n    <?= $form->field($model, 'name') ?>\n\n    <?= $form->field($model, 'email') ?>\n\n    <div class=\"form-group\">\n        <?= Html::submitButton('Submit', ['class' => 'btn btn-primary']) ?>\n    </div>\n\n<?php ActiveForm::end(); ?>\n```\n\nNhững [widget](structure-widgets.md) gọi là [[yii\\widgets\\ActiveForm|ActiveForm]] to\nthường được dùng để xây dựng các Form. Các phương thức `begin()` và `end()` dùng để mở và đóng các tag tương ứng\n. Giữa hai phương thức này, phương thức [[yii\\widgets\\ActiveForm::field()|field()]] sẽ tạo mới các input của form . Input đầu tiên sẽ dùng cho trường dữ liệu \"name\",\nvà input thức hai sẽ dược dùng cho trường \"email\". Sau cùng của các input, phương thức [[yii\\helpers\\Html::submitButton()]] \nsẽ được gọi và tạo ra nút submit dùng để gửi dữ liệu.\n\n\nThử xem kết quả <span id=\"trying-it-out\"></span>\n-------------\n\nTruy cập vào URL sau để xem kết quả:\n\n```\nhttps://hostname/index.php?r=site/entry\n```\n\nBạn sẽ thấy trang Web cùng với việc hiển thị form chứa 2 trường để nhập dữ liệu . Trước mỗi trường nhập liệu, có nhãn được chỉ định những dữ liệu nhập vào . \nNếu bạn không nhập dữ liệu gì vào và nhấn nút submit, hoặc nếu bạn cung cấp địa chỉ email sai, bạn sẽ thấy thông điệp thông báo lỗi ở mỗi trường nhập liệu.\n\n![Form with Validation Errors](images/start-form-validation.png)\n\nSau khi nhập đúng các trường name và địa chỉ email đồng thời click vào nút submit, bạn sẽ thấy trang web mới\ncùng với dữ liệu bạn vừa nhập .\n\n![Confirmation of Data Entry](images/start-entry-confirmation.png)\n\n\n\n### Thông tin thêm <span id=\"magic-explained\"></span>\n\nChúng ta sẽ thắc mắc rằng làm sao các form HTMl được xây dựng lên, bởi vì nó dường như là những thủ thuật có thể\nhiển thị các nhãn (label) cho từng trường input và hiển thị thông báo lỗi nếu bạn không nhập dữ liệu chính xác mà không cần\ntải lại trang.\n\nĐúng vậy, việc xác nhận dữ liệu được thực hiện ở máy client sử dụng JavaScript, và tiếp đế được thực hiện ở máy chủ PHP.\nĐối tượng [[yii\\widgets\\ActiveForm]] rất hữu dụng cho việc xác nhận những quy tắc (rules) mà bạn đã khai báo ở model `EntryForm`,\nvà biến chúng thành những đoạn mã javaScript thực thi, và sử dụng javaScript để xác thực. Trường hợp bạn đã vô hiệu hóa\njavaScript trên trình duyệt, việc xác thực sẽ thực hiện ở phía server, nằm ở phương thức\n`actionEntry()`. Điều này đảm bảo tính hợp lệ dữ liệu trong mọi trường hợp.\n\n> Cảnh báo: Việc xác thực ở phía client thường cung cấp cho sự trải nghiệm của người dùng tốt hơn. Xác thực phía server\n  thì luôn luôn được thực thi, có thể có hoặc không việc xác thực ở phía client.\n\nCác nhãn (label) cho các input được tạo ra bởi phương thức `field()`, sử dụng tên của thuộc tính nằm trong model.\nChẳng hạn, tên nhãn `Name` sẽ được tạo bởi thuộc tính `name`. \n\nBạn có thể sửa tên nhãn ở đoạn code sau:\n\n```php\n<?= $form->field($model, 'name')->label('Tên của bạn Name') ?>\n<?= $form->field($model, 'email')->label('Địa chỉ Email') ?>\n```n\n\n> Thông tin thêm: Yii giúp bạn xây dụng nhanh chóng đối với các view phức tạp bằng việc cung cấp các widget.\n  Bạn sẽ được học ở phần sau, cách đơn giản nhất để viết một widget. Bạn nên chuyển những code ở view của bạn\n  sang dạng widget để đơn giản hơn sự phát triển ứng dụng và tái sử dụng nó.\n\n\nTóm lược <span id=\"summary\"></span>\n-------\n\nTrong phần hướng dẫn này, bạn đã làm việc với tất cả các thành phần trong mô hình MVC. Bạn đã học cách tạo mới model và xác thực dữ liệu.\n\nBạn đã tìm hiểu cách lấy dữ liệu từ user và hiển thị dữ liệu ra trình duyệt. Chức năng này có thể dành nhiều thời gian khi\nxây dựng ứng dụng, tuy nhiên Yii hỗ trợ các chức năng thật đơn giản bằng việc cung cấp những widget.\n\nTrong phần tiếp theo, bạn sẽ tìm hiều làm thể nào để làm việc với CSDL, điều cần thiết với những ứng dụng.\n"
  },
  {
    "path": "docs/guide-vi/start-gii.md",
    "content": "Sử dụng Gii để sinh code\n========================\n\nTrong phần này sẽ hướng dẫn sử dụng [Gii](tool-gii.md) để tự động sinh code, những mã nguồn tương tự với Web site\n. Sử dụng Gii để tự động tạo mã nguồn thật đơn giản, bạn chỉ việc nhập thông tin đúng theo các hướng dẫn hiển thị trên các trang Web Gii và mã \nnguỗn sẽ được sinh tự động.\n\nNội dung chính trong phần này:\n\n* Nhúng Gii vào ứng dụng\n* Dùng Gii để sinh Active Record\n* Dùng Gii để sinh các mã nguồn CRUD cho các bảng CSDL\n* Thay đổi các đoạn mã được sinh ra bởi Gii\n\n\nBắt đầu với Gii <span id=\"starting-gii\"></span>\n------------\n\nYii cung cấp [Gii](tool-gii.md) như một [module](structure-modules.md). Bạn có thể nhúng Gii\nbằng việc cấu hình các thuộc tính của ứng dụng ở phần [[yii\\base\\Application::modules|modules]] . Tùy thuộc vào ứng dụng của bạn, bạn có thể nhìn thấy được những đoạn mã sau được cung cấp trong file cấu hình `config/web.php`:\n\n```php\n$config = [ ... ];\n\nif (YII_ENV_DEV) {\n    $config['bootstrap'][] = 'gii';\n    $config['modules']['gii'] = [\n        'class' => 'yii\\gii\\Module',\n    ];\n}\n```\n\nPhần cấu hình trên được đề cập ở mục [Môi trường phát triển](concept-configurations.md#environment-constants),\nứng dụng bao gồm module tên là `gii`, nằm ở lớp [[yii\\gii\\Module]].\n\nNếu bạn xem qua file [entry script](structure-entry-scripts.md) `web/index.php` trong ứng dụng của bạn, bạn sẽ thấy dòng sau, \nĐiều này chủ yếu thiết lập tham số `YII_ENV_DEV` có giá trị `true`.\n\n```php\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n```\n\nDựa vào dòng này, ứng dụng sẽ được thiết lập ở chế độ phát triển, và sẵn sàng nhúng Gii vào ứng dụng, ở mỗi cấu hình trên.\nBây giờ bạn có thể truy cập Gii qua đường dẫn:\n\n```\nhttps://hostname/index.php?r=gii\n```\n\n> Lưu ý: Nếu bạn muốn truy cập Gii không chỉ từ localhost mà còn từ các máy khác, mặc định các truy cập sẽ bị từ chối\n> để đảm bảo sự an toàn hơn. Bạn có thể cấu hình Gii bằng việc thêm địa chỉ IP được phép gọi như sau,\n>\n```php\n'gii' => [\n    'class' => 'yii\\gii\\Module',\n    'allowedIPs' => ['127.0.0.1', '::1', '192.168.0.*', '192.168.178.20'] // thêm những địa chỉ ip\n],\n```\n\n![Gii](images/start-gii.png)\n\n\nSinh các lớp Active Record <span id=\"generating-ar\"></span>\n---------------------------------\n\nĐể dùng gii cho việc sinh các lớp Active Record, chọn \"Model Generator\" (bằng việc click vào link trên trang chính của Gii). Và điền các thông tin vào form như sau:\n\n* Tên bảng: `country`\n* Tên lớp: `Country`\n\n![Sinh Model](images/start-gii-model.png)\n\nTiếp đến, nhấn vào nút \"Preview\". Bạn sẽ thấy class `models/Country.php` ở danh sách các class được tạo ra. Bạn phải chọn vào tên class để xem nội dung.\n\nKhi sử dụng Gii, nếu bạn đã tạo file tương tự và cần được ghi đè lên,nhấp vào nút diff bên cạnh tên tập tin để thấy sự khác biệt giữa các mã được tạo ra và các phiên bản hiện tại.\n\n![Model Generator Preview](images/start-gii-model-preview.png)\n\nKhi bạn muốn ghi đè vào file đã có, kiểm tra tiếp ở ô \"overwrite\" và click vào nút \"Generate\". Nếu bạn tạo mới file, bạn chỉ việc click vào nút \"Generate\". \n\nTiếp theo, bạn sẽ thấy\nmột trang xác nhận cho thấy các mã đã được tạo thành công. Nếu đã tồn tại file, bạn cũng sẽ thấy một thông báo rằng nó đã được ghi đè bằng các mã mới được tạo ra.\n\n\nSinh các mã nguồn CRUD <span id=\"generating-crud\"></span>\n--------------------\n\nCRUD là chuẩn cho việc Tạo mới (Create), Xem (Read), Cập nhật (Update), và Xóa (Delete), representing the four common tasks taken with data on most Web sites. To create CRUD functionality using Gii, select the \"CRUD Generator\" (bằng việc nhấn vào). Ví dụ với bảng \"country\", điền các thông tin vào form như sau:\n\n* Lớp Model: `app\\models\\Country`\n* Lớp Search Model: `app\\models\\CountrySearch`\n* Lớp Controller: `app\\controllers\\CountryController`\n\n![CRUD Generator](images/start-gii-crud.png)\n\nTiếp đến, chọn vào nút \"Preview\". Bạn sẽ thấy một danh sách các tập tin được tạo ra, như hình dưới.\n\n![Xem CRUD Generator](images/start-gii-crud-preview.png)\n\nNếu bạn đã tạo các file `controllers/CountryController.php` và\n`views/country/index.php` trước đó (trong phần hướng dẫn về CSDL), kiểm tra nút \"overwrite\" để thay thế file đó. (Các phiên bản trước không hỗ trợ để sinh CRUD.)\n\n\nXem kết quả <span id=\"trying-it-out\"></span>\n-------------\n\nXem kết quả, dùng trình duyệt truy cập vào đường dẫn sau:\n\n```\nhttps://hostname/index.php?r=country/index\n```\n\nBạn sẽ thấy dữ liệu bảng được hiển thị chứa các thông tin trong CSDL country. Bạn có thể sắp xếp các bảng,\nhoặc lọc nội dụng bằng việc nhập các điều kiện cần lọc ở phần đầu bảng.\n\nMỗi dữ liệu country được hiển thị trên bảng, bạn có thể chọn để xem chi tiết, cập nhật, hoặc xóa.\nBạn cũng có thể \"tạo mới Country\", click vào button ở phần trên cùng của bảng.\n\n![Bảng Countries](images/start-gii-country-grid.png)\n\n![Cập nhật Country](images/start-gii-country-update.png)\n\nDanh sách các file sau được sinh bởi Gii, bạn có thể dùng trong ứng dụng, hoặc chỉnh sửa chúng:\n\n* Controller: `controllers/CountryController.php`\n* Models: `models/Country.php` and `models/CountrySearch.php`\n* Views: `views/country/*.php`\n\n> Thông tin: Công cụ Gii được xây dụng lên với việc phát triển ứng dụng nhanh và dễ mở rộng. Sử dụng nó một cách thích hợp\nrất có thể đẩy nhanh tốc độ phát triển ứng dụng của bạn. Biết thêm thông tin, xem thêm ở phần [Gii](tool-gii.md).\n\n\nTổng kết <span id=\"summary\"></span>\n-------\n\nỞ phần này, bạn đã học được cách sử dụng Gii để tạo ra mã thực hiện hoàn chỉnh\nchức năng CRUD cho nội dung được lưu trữ trong một bảng cơ sở dữ liệu.\n"
  },
  {
    "path": "docs/guide-vi/start-hello.md",
    "content": "Bắt đầu ứng dụng với lời chào Hello\n============\n\nPhần này sẽ mô tả làm thế nào để tạo ra một trang Web mới trong ứng dụng của bạn cùng với lời chào \"Hello\".\nĐể đạt được mục tiêu này. bạn sẽ cần tạo mới một [action](structure-controllers.md#creating-actions) và\nmột [view](structure-views.md):\n\n* Ứng dụng sẽ gửi đi các request từ trang Web để tới các action\n* và action sẽ tạo mới View để hiển thị lời chào \"Hello\" tới user.\n\nThông qua bài hướng dẫn này, bạn sẽ nắm vững ba điều:\n\n1. Làm thế nào để tạo ra một [action](structure-controllers.md) để đáp ứng các requests,\n2. Làm thế nào để tạo ra [view](structure-views.md) để xây dựng nội dung các thông điệp, và\n3. Cách ứng dụng gửi đi các request tới các [actions](structure-controllers.md#creating-actions).\n\n\nTạo Action <span id=\"creating-action\"></span>\n------------------\n\nVới nhiệm vụ tạo ra thông điệp \"Hello\", bạn sẽ tạo một  [action](structure-controllers.md#creating-actions) `say`, action này \nsẽ lấy các tham số `message` từ request và hiển thị thông điệp trở lại user. Nếu request không cung cấp tham số `message`, \naction sẽ mặc định hiển thị thông điệp  \"Hello\".\n\n> Info: [Hành động (Actions)](structure-controllers.md#creating-actions) là người dùng cuối có thể truy cập các đối tượng và thực hiện trực tiếp.\n Các Actions được nằm trong [bộ điều khiển (controllers)](structure-controllers.md).\n Các kết quả của một action là người sử dụng cuối cùng nhận được các thông điệp.\n\nCác Actions cần phải được khai báo ở [controllers](structure-controllers.md). Để cho đơn giản, bạn có thể khai báo\naction  `say` ở controller `SiteController`. Controller này được khai báo ở trong \nlớp `controllers/SiteController.php`. Action mới cần tạo nằm ở đoạn code sau:\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    // ...existing code...\n\n    public function actionSay($message = 'Hello')\n    {\n        return $this->render('say', ['message' => $message]);\n    }\n}\n```\n\nTrong đoạn code trên,  action `say` đinh nghĩa phương thức có tên là `actionSay` nằm trong lớp `SiteController`.\nYii sử dụng tiền tố `action` để phân biệt các phương thức thuộc action từ các phương thức không phải là action trong một lớp điều khiển.\nTên nằm sau `action` là tiền tố ánh xạ tới các action's ID.\n\nĐể hiểu được quy tắc đặt tên cho actions, Bạn nên hiểu cách hoạt động Yii xử lý với các action IDs. Mỗi Action IDs luôn luôn là những ký tự \nthường. Nếu action ID đòi hỏi nhiều từ, chúng ta sẽ nối những từ đó bằng dấu gạch ngang (ví dụ, `create-comment`). Tên phương thức của action \nsẽ được ánh xa tới action IDs bởi loại bỏ bất kỳ dấu gạch ngang từ IDs, dấu gạch ngang được thêm vào từ chữ cái in hoa đầu tiên trong mỗi từ, và từ đứng trước `action`. Ví dụ,\nvới action ID `create-comment` tương ứng tới action có phương thức tên là `actionCreateComment`.\n\nTrong ví dụ này, phương thức của action nhận tham số `$message`, mặc đinh giá trị là `\"Hello\"` (Như việc bạn có thể thiết\nlập các giá trị mặc định cho bất kỳ tham số cho các hàm hoặc phương thức trong PHP). Mỗi khi ứng dụng\nnhận request và xác đinh là action chịu trách nhiệm cho xử lý các yêu cầu là action `say` , ứng dung\nsẽ lưu trữ tham số này cùng với tên tham số được tìm thấy trong request. Nói cách khác, nếu request bao gồm\ntham số `message` theo cùng với giá trị `\"Goodbye\"`, biến `$message` tương ứng trong action sẽ được gán giá trị.\n\nPhương thức [[yii\\web\\Controller::render()|render()]] nằm trong mỗi action được gọi để trả về một [view](structure-views.md)\ncó tên là `say`. Tham số `message` luôn luôn được gửi qua view để xem nó có được dùng hay không. Kết quả việc render được\nthực hiện trong mỗi action. Ứng dụng sẽ nhận kết quả này và hiển thị tới user trên trình duyệt (như là một trang HTML đầy đủ). \n\n\nTạo mới View <span id=\"creating-view\"></span>\n---------------\n\n[Views](structure-views.md) đảm nhận việc hiển thị thông tin và tương tác với người dùng. Để thực hiện yêu câu hiển thị\nlời chào \"Hello\", bạn cần phải tạo một view `say` có chức năng hiển thị tham số `message`, tham số này được nhận từ action gửi đến:\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n<?= Html::encode($message) ?>\n```\n\nBạn cần lưu trữ view `say` nằm ở đường dẫn `views/site/say.php`. Mỗi khi phương thức [[yii\\web\\Controller::render()|render()]]\nđược gọi ở action, nó sẽ tìm kiếm tập tin PHP nằm ở đường dẫn `views/ControllerID/ViewName.php`.\n\nLưu ý rằng, đoạn code trên, biến `message` đã được phương thức [[yii\\helpers\\Html::encode()|HTML-encoded]]\nmã hóa trước khi được in ra. Việc mã hóa là cần thiết khi gửi các tham số tới user, các tham số này có thể bị tấn công qua\n[XSS (cross-site scripting)](https://en.wikipedia.org/wiki/Cross-site_scripting) đây là kỹ thuật tấn công bằng cách chèn chèn các \nthẻ HTML hoặc đoạn mã JavaScript độc hại.\n\nTất nhiên, bạn có thể thêm các nội dung ở view `say`.Nội dung bao gồm các thẻ HTML, dữ liệu văn bản, và cũng có thể là các câu lệnh PHP.\nTrên thực tế, view `say` chỉ là các đoạn mã PHP được thực thi bởi phương thức [[yii\\web\\Controller::render()|render()]].\nNội dung được gửi ra từ view sẽ được gửi tới ứng dụng (application) như những phản hồi kết quả. \nSau đó ứng dụng sẽ gửi kết quả tới user.\n\n\nTrying it Out <span id=\"trying-it-out\"></span>\n-------------\n\nSau khi đã tạo action và view, bạn có thể truy cập vào trang bởi việc truy cập vào URL sau:\n\n```\nhttps://hostname/index.php?r=site/say&message=Hello+World\n```\n\n![Hello World](images/start-hello-world.png)\n\nURL này sẽ trả về một trang và hiển thị lời chào \"Hello World\". Trang này có cùng phần header và footer như những trang khác trong ứng dụng. \n\nNếu bạn không nhập tham số `message` vào URL, bạn chỉ xem thấy mỗi dòng \"Hello\" được hiển thị. Bởi vì tham số `message` được thông qua phương thức `actionSay()`, và mỗi khi tham số này không được nhập,\nthì giá trị mặc đinh `\"Hello\"` sẽ được thay thế.\n\n> Info: Trang này có cùng phần header và footer như những trang khác là bởi vì phương thức [[yii\\web\\Controller::render()|render()]]\n  sẽ tự động nhúng nội dung của view `say` vào một [layout](structure-views.md#layouts) layout này nằm ở `views/layouts/main.php`.\n\nTham số `r` ở trên URL sẽ được giải thích thêm. Nó là chuẩn cho bộ định tuyến [route](runtime-routing.md), mỗi ứng dụng sẽ cung cấp ID\ntương ứng với từng action. Với các đinh dạng router `ControllerID/ActionID`. Khi ứng dụng nhận request, ứng dụng sẽ kiểm tra các tham số \ntheo cùng request đó, sử dụng `ControllerID` để xác định lớp điều khiển để xử các request. Sau đó, bộ điều khiển sẽ\nxác dịnh `ActionID` cần được khởi tạo để xử lý công việc. Trong ví dụ này, route `site/say`\nsẽ gán (ám chỉ tới) bộ điều khiển `SiteController` và action `say`. Điều này sẽ có kết quả là, phương thức `SiteController::actionSay()` sẽ được gọi để xử lý các request.\n\n> Info: Giống như actions, ứng dụng sử dụng các định danh ID để nhận diện các controller. Các Controller ID\n  có quy tắc đặt tên giống với các action IDs. Tên của controller được chuyển đổi từ các controller IDs \n  bằng việc loại bỏ dấu gạch ngang từ đinh danh ID, tận dụng các chữ cái đầu tiên trong mỗi từ,\n  và từ đứng trước `Controller`. Ví dụ, bộ điều khiển controller ID có tên là `post-comment` sẽ tương ứng\n  với controller là `PostCommentController`.\n\n\nTổng kết <span id=\"summary\"></span>\n-------\n\nQua phần này, bạn đã thao tác với phần controller và view nằm trong mẫu thiết kế MVC.\nBạn đã tạo một action thuộc phần của controller để xử lý các request . Và bạn cũng đã tạo được view cho việc \nhoàn thành nội dung trong thông điệp trả về . Trong ví dụ đơn giản này, không có model được sử dụng để thao tác dữ liệu mà chỉ sử dụng tham số `message`.\n\nBạn cũng đã học được router trong Yii, cái mà có vai trò quan trọng trong việc thiết lập kết nối giữa user và các controller actions.\n\nTrong phần tiếp , bạn sẽ tìm hiểu cách tạo một model, và thêm mới các trang có chứa HTML form.\n"
  },
  {
    "path": "docs/guide-vi/start-installation.md",
    "content": "Cài đặt Yii\n==============\n\nBạn có thể cài đặt Yii theo hai cách, dùng trình quản lý gói [Composer](https://getcomposer.org/) hoặc tải toàn bộ mã nguồn Yii về.\nCách thứ nhất thường được khuyến khích dùng hơn, vì nó cho phép bạn cài đặt thêm các [Gói mở rộng (extensions)](structure-extensions.md)  hoặc cập nhật Yii đơn giản chỉ mới một dòng lệnh.\n\nMặc định, sau khi cài đặt Yii sẽ cung cấp cho bạn một số tính năng cơ bản, như đăng nhập (login), form liên hệ (contact form), vv. \nNhững tính năng trên đều được khuyến khích và sử dụng rộng rãi, vì thế, nó có thể hữu ích cho các dự án của bạn.\n    \nTrong bài hướng dẫn này và các phần tiếp theo, chúng ta sẽ tìm hiều cách cài ứng dụng Yii với tên *Basic Application Template* và\nlàm thế nào để triển khai các tính năng mới trên mẫu ứng dụng này. Yii đồng thời cũng cung cấp mẫu ứng dụng tên là [Advanced Application Template](tutorial-advanced-app.md)\nTemplate này hướng đến những đội dự án cần phát triển ứng dụng có nhiều tầng (multiple tiers).\n\n> Lưu ý: *Basic Application Template* thích hợp đến 90% cho việc phát triển web. Nó khác\nvới [Advanced Application Template](tutorial-advanced-app.md) trong cách tổ chức mã nguồn. Nếu bạn là người mới tìm hiều về Yii, chúng tôi khuyến khích\nbạn bắt đầu với *Basic Application Template* , ứng dụng này đơn giản và ít chức năng. Thích hợp hơn cho việc tìm hiểu về Yii.\n\n\n\nCài đặt qua trinh quản lý gói Composer <span id=\"installing-via-composer\"></span>\n-----------------------\n\nNếu bạn chưa cài Composer, bạn có thể cài đặt theo đường link sau\n[getcomposer.org](https://getcomposer.org/download/). Đối với hệ điều hành Linux và Mac OS X, bạn có thể chạy các lệnh sau đây:\n\n    curl -s https://getcomposer.org/installer | php\n    mv composer.phar /usr/local/bin/composer\n\nCòn trên HĐH Windows, bạn có thể tải về và chạy [Composer-Setup.exe](https://getcomposer.org/Composer-Setup.exe).\n\nNếu bạn có bất kỳ thắc mắc hoặc muốn biết thêm và nghiên cứu chuyên sâu về Composer, vui lòng tham khảo [Tài liệu Composer](https://getcomposer.org/doc/) \n\nNếu bạn đã cài Composer rồi, hãy chắc chắn rằng bạn đang sử dụng phiên bản mới nhất. Bạn có thể cập nhật Composer bằng cách thực hiện lệnh\n `composer self-update`.\n\nSau khi cài đặt Composer, bạn có thể cài đặt Yii bằng cách chạy lệnh sau ở thư mục Web mà ứng dụng cần chạy:\n\n    composer global require \"fxp/composer-asset-plugin:^1.4.1\"\n    composer create-project --prefer-dist yiisoft/yii2-app-basic basic\n\nCâu lệnh đầu tiên sẽ cài đặt [composer asset plugin](https://github.com/fxpio/composer-asset-plugin)\nvà cho phép Composer có thể quản lý những package dependencies của bower và npm. Câu lệnh này chỉ cần chạy một lần.\nCâu lệnh thứ hai sẽ cài đặt phiên bản Yii có tên là  `basic`. Bạn có thể chọn một tên thư mục khác nếu bạn muốn.\n\n> Chú ý: Trong quá trình cài đặt Composer có thể yêu cầu thông tin đăng nhập từ tài khoản Github của bạn. điều này là bình thường bởi vì Composer \n> cần đầy đủ thông tin API rate-limit để lấy các thông tin gói phụ thuộc từ Github. Để biết thêm chi tiết,\n> xin vui lòng tham khảo  [Composer documentation](https://getcomposer.org/doc/articles/troubleshooting.md#api-rate-limit-and-oauth-tokens).\n\n> Thủ thuật: Nếu bạn muốn cài đặt phiên bản phát triển mới nhất của Yii, bạn có thể sử dụng lệnh sau để thay thế,\n> điều này chỉ cần thêm [stability option](https://getcomposer.org/doc/04-schema.md#minimum-stability):\n>\n>     composer create-project --prefer-dist --stability=dev yiisoft/yii2-app-basic basic\n>\n> Chú ý.  phiên bản phát triển của Yii(dev version) không nên sử dụng cho mô trường ứng dụng bởi vì nó có thể phá vỡ các hoạt động trong code.\n\n\nCài đặt từ tập tin lưu trữ <span id=\"installing-from-archive-file\"></span>\n-------------------------------\n\nViệc cài đặt Yii từ một tập tin lưu trữ bao gồm ba bước:\n\n1. Tải gói cài đặt từ [yiiframework.com](https://www.yiiframework.com/download/).\n2. Giải nén file tải về vào một thư mục Web của ứng dụng cần chạy.\n3. Sửa đồi file `config/web.php`  bởi nhập thông tin secret key `cookieValidationKey` ở mục cấu hình\n   (này được thực hiện tự động nếu bạn đang cài đặt Yii sử dụng Composer):\n\n   ```php\n   // !!! chèn một secret key trong phần sau (nếu rỗng) - Việc này là cần thiết để xác thực cookie trong ứng dụng\n   'cookieValidationKey' => 'Nhập secret key tuỳ chọn vào đây',\n   ```\n\n\nCác thiết lập cài đặt khác <span id=\"other-installation-options\"></span>\n--------------------------\n\nYii giới thiệu hai phương pháp cài đặt ở trên, những phương pháp này sẽ tạo ứng dụng Web.\n.Đối với các dự án nhỏ hoặc cho việc học để sử dụng, đây là một điểm khởi đầu tốt.\n\nNhưng cũng có những phương pháp cài đặt khác:\n\n* Nếu bạn chỉ muốn cài đặt các khung cốt lõi và muốn xây dựng toàn bộ một ứng dụng từ đầu,\n  bạn có thể làm theo hướng dẫn như đã hướng dẫn ở bài viết [Building Application from Scratch](tutorial-start-from-scratch.md).\n* Nếu bạn muốn bắt đầu với một ứng dụng phức tạp hơn, phù hợp hơn với môi trường phát triển trong team bạn,\n  bạn có thể xem xét việc cài đặt mẫu ứng dụng [Advanced Application Template](tutorial-advanced-app.md).\n\n\nKết quả cài đặt <span id=\"verifying-installation\"></span>\n--------------------------\n\nSau khi cài đặt, bạn có thể sử dụng trình duyệt để truy cập ứng dụng Yii được cài đặt với URL dưới đây:\n\n```\nhttp://localhost/basic/web/index.php\n```\n\nURL này giả sử bạn đã cài đặt Yii trong một thư mục có tên `basic`, trực tiếp dưới thư mục gốc tài liệu máy chủ Web của bạn,\nvà rằng các máy chủ Web đang chạy trên máy tính cục bộ của bạn (`localhost`). Bạn có thể cần phải điều chỉnh nó trong môi trường cài đặt.\n\n![Successful Installation of Yii](images/start-app-installed.png)\n\nBạn sẽ có thể thấy trang hiển thị \"Congratulations!\" ở trình duyệt của ban. Còn không, xin vui lòng kiểm tra xem PHP đáp ứng cài đặt của bạn\nCác yêu cầu Yii. Bạn có thể kiểm tra xem các yêu cầu tối thiểu được đáp ứng bằng một trong những phương pháp sau đây:\n\n* Sử dụng trình duyệt để truy cập vào URL `http://localhost/basic/requirements.php`\n* Chay câu lệnh như sau:\n\n  ```\n  cd basic\n  php requirements.php\n  ```\n\nBạn nên cấu hình cài đặt PHP của bạn để nó đáp ứng các yêu cầu tối thiểu của Yii. Diều quan trọng nhất, bạn nên có PHP 5.4 hoặc hơn. Bạn cũng nên cài đặt\ncác gói [PDO PHP Extension](https://www.php.net/manual/en/pdo.installation.php) và một trình điều khiển cơ sở dữ liệu tương ứng\n(như là `pdo_mysql` cho CSDL MySQL), nếu ứng dụng của bạn cần thao tác với CSLD.\n\n\nCấu hình máy chủ Web <span id=\"configuring-web-servers\"></span>\n-----------------------\n\n> Lưu ý: Lưu ý: Nếu bạn chỉ là chạy thử ứng dụng Yii thay vì được triển khai(deploying) trong một môi trường sản xuất,\n  bạn có thể bỏ qua phần này.\n\nCác ứng dụng được cài đặt theo phương pháp trên, được chạy trong Windows, Max OS X, Linux hoặc máy chủ [Apache HTTP](https://httpd.apache.org/) \nhoặc [Nginx HTTP server](https://nginx.org/) và PHP phiên bản 5.4 hoặc cao hơn đều có thể được chạy trực tiếp. Yii 2.0 cũng tương thích với HHVM, \ndo [HHVM](https://hhvm.com/)của Facebook và PHP tiêu chuẩn trên các khía cạnh trong một vài nơi một với trường hợp hơi khác nhau, \nkhi sử dụng HHVM đòi hỏi ít thay đổi.\n\nTrong môi trường máy chủ sản xuất, bạn có thể cấu hình máy chủ để ứng dụng có thể truy cập thông qua URL https://www.example.com/index.php \nthay vì https://www.example.com/basic/web/index.php. Cấu hình này đòi hỏi các thư mục gốc tài liệu của máy chủ Web vào thư mục basic/web. Bạn cũng có thể  ẩn index.php trên URL, \nchi tiết trên URL phân tích và tạo ra một chương trình chiếu, bạn sẽ tìm hiểu làm thế nào để cấu hình Apache hoặc Nginx máy chủ để đạt được những mục tiêu này.\n\n> Lưu ý: Thiết lập `basic/web` như thư mục gốc, bạn có thể ngăn chặn người dùng truy cập vào các dữ liệu cá nhân và các thông tin nhạy cảm được lưu trữ\n ở các thư mục con nằm trong `basic/web`. Từ chối truy cập vào các thư mục khác là một cải tiến bảo mật.\n\n> Lưu ý: Bạn nên điều chính cấu trúc ứng dụng của bạn để bảo mật tốt hơn, điều này cần thiếu nếu khi ứng dụng của ban chạy trên những hosting miễn phí, ở môi trường mà bạn\nkhông có quyền thay đổi các thiết lập ở server Web. Tham khảo thêm ở phần sau để biết thêm chi tiết [Shared Hosting Environment](tutorial-shared-hosting.md).\n\n\n### Các khuyến nghị khi cấu hình máy chủ Apache <span id=\"recommended-apache-configuration\"></span>\n\nSử dụng các cấu hình sau đây trong file `httpd.conf` của Apache hoặc trong một cấu hình máy chủ ảo. Lưu ý rằng bạn nên\nthay thế đường dẫn đường dẫn thực tế `path/to/basic/web` cho `basic/web`.\n\n```\n# Thiết lập document root tới đường dẫn \"basic/web\"\nDocumentRoot \"path/to/basic/web\"\n\n<Directory \"path/to/basic/web\">\n    # use mod_rewrite for pretty URL support\n    RewriteEngine on\n    # If a directory or a file exists, use the request directly\n    RewriteCond %{REQUEST_FILENAME} !-f\n    RewriteCond %{REQUEST_FILENAME} !-d\n    # Otherwise forward the request to index.php\n    RewriteRule . index.php\n\n    # ...other settings...\n</Directory>\n```\n\n\n### Các khuyến nghị khi cấu hình Nginx <span id=\"recommended-nginx-configuration\"></span>\n\nĐể sử dụng [Nginx](https://wiki.nginx.org/), bạn cần phải cài đặt [FPM SAPI](https://www.php.net/install.fpm).\nBạn có thể cấu hình Nginx như sau, thay thế đường dẫn `path/to/basic/web` với đường dẫn thực tế ở\n`basic/web` và  `mysite.test` thay thế bằng tên máy chủ thực tế để cung cấp dịch vụ.\n\n```\nserver {\n    charset utf-8;\n    client_max_body_size 128M;\n\n    listen 80; ## listen for ipv4\n    #listen [::]:80 default_server ipv6only=on; ## listen for ipv6\n\n    server_name mysite.test;\n    root        /path/to/basic/web;\n    index       index.php;\n\n    access_log  /path/to/basic/log/access.log;\n    error_log   /path/to/basic/log/error.log;\n\n    location / {\n        # Redirect everything that isn't a real file to index.php\n        try_files $uri $uri/ /index.php?$args;\n    }\n\n    # uncomment to avoid processing of calls to non-existing static files by Yii\n    #location ~ \\.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ {\n    #    try_files $uri =404;\n    #}\n    #error_page 404 /404.html;\n\n    location ~ \\.php$ {\n        include fastcgi_params;\n        fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;\n        fastcgi_pass   127.0.0.1:9000;\n        #fastcgi_pass unix:/var/run/php5-fpm.sock;\n        try_files $uri =404;\n    }\n\n    location ~ /\\.(ht|svn|git) {\n        deny all;\n    }\n}\n```\n\nKhi sử dụng cấu hình này, bạn cũng nên thiết lập `cgi.fix_pathinfo=0` ở file `php.ini`\nđể tránh nhiều hệ thống không cần thiết `stat()` khi gọi hệ thống.\n\nCũng lưu ý rằng khi bạn chạy một máy chủ HTTPS, bạn cần phải thêm dòng `fastcgi_param HTTPS on;` vào file cấu hình \nđể Yii có thể hiểu ra những kết nối là an toàn.\n"
  },
  {
    "path": "docs/guide-vi/start-looking-ahead.md",
    "content": "Looking Ahead\n=============\n\nNếu bạn đã đọc và thực hành các bài viết trong chuyên mục \"Getting Started\", thì bạn có thể xây dựng ứng dụng Yii hoàn chỉnh. Trong những phần trước, bạn đã được tìm hiều để thực hiện một số chức năng cơ bản\n, như lấy thông tin từ user qua form, thu thập thông tin từ CSDL, và hiển thị dữ liệu cùng với phân trang\n. Bạn cũng được tìm hiểu sử dụng [Gii](tool-gii.md) đểsinh code tự động. Sử dụng Gii thật đơn giản chỉ việc điển các thông tin vào các form và gii sẽ sinh code tự động, và giảm tải số lượng lơn\ncác chức năng tương tự.\n\nTrong phần này sẽ tổng hợp những tài nguyên về tìm hiểu Yii framework.\n\n* Tài liệu tham khảo\n    - [Giới thiệu tổng quan](https://www.yiiframework.com/doc-2.0/guide-README.html):\n      Như tiêu đề, các hướng dẫn sẽ cung cấp tổng quan về sử dụng Yii và cách thức Yii hoạt động\n      . Đây là các hướng dẫn quan trọng để bắt đầu với Yii, vì vậy bạn cần tìm hiểu trước khi đi sâu và viết code về Yii.\n    - [Thông tin chi tiết Class](https://www.yiiframework.com/doc-2.0/index.html):\n      Phần này sẽ mô tả và cách dùng các class được cung cấp bởi Yii. Phần này là quan trọng khi bạn viết code và muốn hiểu về cách dùng từng phần trong lớp, phương thức,\n      các thuộc tính. Tham khảo cách dùng các lớp sẽ giúp bạn hiểu sâu về từng phần trong framework.\n    - [Các bài hướng dẫn khác](https://www.yiiframework.com/wiki/?tag=yii2):\n      Các bài hướng dẫn được viết bởi những người có kinh nghiệm về lập trình Yii. Hầu hết trong số đó, được viết theo những kinh nghiệm lập trình lâu năm.\n      , và đưa ra các giải pháp về từng phần, vấn đề khi lập trình Yii. Các bài viết có thể không chi tiết và dễ hiểu như phần giới thiệu, tuy nhiên các bài viết có\n      những chủ đề rộng hơn và thường có những ví dụ và giải pháp với các vấn đề lập trình.\n    - [Sách](https://www.yiiframework.com/books)\n* [Extensions](https://www.yiiframework.com/extensions/):\n  Yii tự hào có một thư viện của hàng ngàn các phần mở rộng với sự đóng góp lớn của cộng động, bạn có thể dễ dàng tích hợp vào các ứng dụng của bạn, do đó làm cho phát triển ứng dụng của bạn nhanh hơn và dễ dàng hơn.\n* Cộng dồng\n    - Diễn đàn: <https://forum.yiiframework.com/>\n    - IRC chat: The #yii channel on the Libera (<ircs://irc.libera.chat:6697/yii>)\n    - GitHub: <https://github.com/yiisoft/yii2>\n    - Facebook: <https://www.facebook.com/groups/yiitalk/>\n    - Twitter: <https://twitter.com/yiiframework>\n    - LinkedIn: <https://www.linkedin.com/groups/yii-framework-1483367>\n    - Stackoverflow: <https://stackoverflow.com/questions/tagged/yii2>\n"
  },
  {
    "path": "docs/guide-vi/start-prerequisites.md",
    "content": "# Những gì bạn cần biết\n\nQuá trình học Yii không quá khó cũng như các framework PHP khác nhưng vẫn có một số điều bạn nên học trước khi bắt đầu với Yii.\n\n## PHP\n\nYii là một framework PHP nên hãy đảm bảo bạn đã [đọc và hiểu ngôn ngữ của nó](https://www.php.net/manual/en/langref.php).\nKhi phát triển với Yii, bạn sẽ viết code theo hướng đối tượng, vì vậy hãy đảm bảo rằng bạn đã quen với [Classes và Objects](https://www.php.net/manual/en/language.oop5.basic.php) cũng như [namespaces](https://www.php.net/manual/en/language.namespaces.php).\n\n## Lập trình hướng đối tượng\n\nHiểu cơ bản về lập trình hướng đối tượng là bắt buộc. Nếu bạn không biết, hãy học theo những hướng dẫn trên google, youtube, chẳng hạn như [the one from tuts+](https://code.tutsplus.com/tutorials/object-oriented-php-for-beginners--net-12762).\n\nLưu ý rằng ứng dụng của bạn càng phức tạp thì các khái niệm OOP nâng cao hơn mà bạn nên học để thành công quản lý sự phức tạp đó.\n\n## Command line và composer\n\nYii sử dụng rộng rãi trình quản lý gói PHP tiêu chuẩn de-facto, [Composer](https://getcomposer.org/) vì vậy hãy chắc chắn rằng bạn đọc\nvà hiểu [hướng dẫn](https://getcomposer.org/doc/01-basic-usage.md) của nó. Nếu bạn không quen với việc sử dụng command line thì đã đến lúc bắt đầu thử. Khi bạn học những cái cơ bản mà bạn sẽ không bao giờ muốn làm việc nếu thiếu nó.\n"
  },
  {
    "path": "docs/guide-vi/start-workflow.md",
    "content": "Chạy ứng dụng\n====================\n\nSau khi cài đặt Yii, ứng dụng Yii của bạn đã được chạy, tùy thuộc vào cấu hình bạn có thể truy cập qua URL `https://hostname/basic/web/index.php`\nhoặc `https://hostname/index.php`. Bài hướng dẫn này sẽ mô tả chức năng của ứng dụng và cách tổ chức code trong ứng dụng,\nvà làm thế nào để xử lý các yêu cầu của ứng dụng.\n\n> Lưu ý: Để đơn giản, xuyên suốt các bài hướng dẫn \"Getting Started\" này, giả sử rằng chúng ta đã thiết lập `basic/web`\n  như thư mục gốc trong máy chủ Web, và cấu hình URL dể truy cập vào ứng dụng của ban thành\n  `https://hostname/index.php` hoặc điều tương tự. Tùy theo yêu cầu của bạn, bạn hãy điều chình \n  URLs sao cho phù hợp với ứng dụng.\n\n\nChức năng <span id=\"functionality\"></span>\n-------------\n\nMẫu ứng dụng *Basic Application* bao gồm 4 trang cơ bản:\n\n* Trang chủ (homepage), được hiển thị khi bạn truy cập vào URL `https://hostname/index.php`,\n* Trang \"About\",\n* Trang \"Contact\", trang hiển thị form contact cho phép user liên hệ với bạn qua email,\n* Trang \"Login\", trang hiển thị form login cho phép bạn có thể xác thực user. Hãy thử đăng nhập với\n  thông tin \"admin/admin\", và bạn sẽ thấy tên \"Login\" ở menu chính thay đổi thành \"Logout\".\n\nNhững trang trên đều có cùng phần header và footer. Phần header chứa main menu bar cho phép điều hướng giữa các trang.\n\nỞ dưới cùng của trình duyệt, bạn có thể thấy một thanh công cụ. Đây là một công cụ gỡ lỗi [debugger tool](tool-debugger.md) rất hữu ích\nđược cung cấp bởi Yii, bạn có thể ghi lại và hiển thị một lượng lớn các thông tin gỡ lỗi, \nchẳng hạn như thông tin đăng nhập, tình trạng phản ứng, các câu lệnh truy vấn cơ sở dữ liệu, và như vậy.\n\nNgoài ra, các ứng dụng web có các script từ dòng lệnh (console) tên là *yii*, được nằm ở ứng dụng cơ sở. \nNhững script này có thể dùng chạy nền và bảo trì chức năng ứng dụng, thông tin mô tả thêm [Console Application Section](tutorial-console.md).\n\n\nCấu trúc ứng dụng (Application Structure) <span id=\"application-structure\"></span>\n---------------------\n\nNhững thư mục và tập tin quan trọng nhất của ứng dụng (giả sử thư mục gốc của ứng dụng tên là `basic`):\n\n```\nbasic/                  Thư mục gốc ứng dụng\n    composer.json       Tập tin cấu hình Composer, mô tả thông tin gói\n    config/             Chứa các cấu hình ứng dụng và cấu hình khác\n        console.php     thông tin cấu hình ứng dụng giao diện console\n        web.php         thông tin cấu hình ứng dụng Web\n    commands/           chứa các lớp lệnh console\n    controllers/        Chứa các lớp điều khiển (controller)\n    models/             Chứa các lớp model\n    runtime/            chứa các file được sinh ra bởi Yii trong quá trình chạy, chẳng hạn như đăng nhập và file của bộ nhớ cache\n    vendor/             chứa các gói cài đặt Composer Package, bao gồm cả Yii framework\n    views/              chứa các file về view\n    web/                Thư mục gốc ứng dụng Web, chứa các file truy cập Web\n        assets/         chứa tập tin tài nguyên Yii (javascript và css)\n        index.php       tập tin thực thi ứng dụng (hoặc bootstrap)\n    yii                 Giao diện điều khiển lệnh script (Yii console)\n```\n\nNói chung, những tập tin trong ứng dụng có thể chia thành hai loại: tập tin nằm trong thư mục `basic/web` và những tập\ntin nằm ở thư mục khác. Trước dây có thể truy cập trực tiếp qua HTTP (chẳng hạn như một trình duyệt), tuy nhiên sau này không thể truy cập và không nên có.\n\nYii thực thi theo mẫu thiết kế [model-view-controller (MVC)](https://wikipedia.org/wiki/Model-view-controller),\nĐiều này được phản ánh trong cấu trúc đường dẫn ở trên. Thư mục `models` chứa tất cả [lớp dữ liệu (model)](structure-models.md),\ncòn thư mục `views` sẽ chứa tất cả [view scripts](structure-views.md), và thư mục `controllers` chứa tất cả\n[lớp điều khiển (controller classes)](structure-controllers.md).\n\nBiểu đồ sau đây cho thấy cấu trúc tĩnh của một ứng dụng:\n\n![Static Structure of Application](images/application-structure.png)\n\nMỗi ứng dụng sẽ có một mục đầu vào (Entry Script) `web/index.php` như vậy việc truy cập vào ứng dụng Web chỉ được phép truy cập qua mục này.\nEntry script tiếp nhận các request và tạo mới [ứng dụng (application)](structure-applications.md) để xử lý.\nCác [ứng dụng](structure-applications.md) giải quyết các request cùng với các [thành phần (components)](concept-components.md),\nvà gửi các request tới các phần tử trong mô hình MVC. Các [Widgets](structure-widgets.md) sẽ được sử dụng ở [views](structure-views.md)\nđể đơn giản hơn việc xây dựng các giao diện phức tạp.\n\n\nChu trình xứ lý yêu cầu <span id=\"request-lifecycle\"></span>\n-----------------\n\nBiểu đồ dưới đây cho thấy làm thế nào một ứng dụng để xử lý các yêu cầu:\n\n![Request Lifecycle](images/request-lifecycle.png)\n\n1. User tạo yêu cầu (request) tới [mục script](structure-entry-scripts.md) `web/index.php`.\n2. Entry script tải các [cấu hình (configuration)](concept-configurations.md) ứng dụng và tạo mới\n   [ứng dụng](structure-applications.md) để khởi tạo để xử lý yêu cầu.\n3. Ứng dụng lấy thông tin [route](runtime-routing.md) được yêu cầu cùng với những thành phần (component)\n   cần xử lý các [request](runtime-requests.md).\n4. Ứng dụng tạo mới [controller](structure-controllers.md) khởi tạo để xử lý yêu cầu.\n5. Bộ điều khiển (controller) tạo mới các [action (hành động)](structure-controllers.md) khởi tạo và thực hiện các bộ lọc cho các hành động.\n6. Nếu bất kỳ bộ lọc nào bị lỗi, action sẽ bị hủy.\n7. Nếu bất kỳ bộ đạt, action sẽ được thực thi.\n8. Action sẽ tải dữ liệu từ data model, có thể từ CSDL.\n9. Action sẽ tạo mới View, đồng thời cung cấp dữ liệu cho nó .\n10. Kết quả việc tạo mới view sẽ trả vê một thành phần ứng dụng [response](runtime-responses.md) .\n11. Thành phần response gửi kết quả đến trình duyệt của người dùng và hiển thị kết quả.\n\n"
  },
  {
    "path": "docs/guide-vi/structure-application-components.md",
    "content": "Các thành phần ứng dụng\n======================\n\nMỗi ứng dụng là hiện thực của [mẫu thiết kế Service Locators](concept-service-locator.md). Mỗi ứng dụng sẽ chứa các thành phần\nđược gọi là *thành phần ứng dụng* giúp cung cấp các dịch vụ cho các tiến trình xử lý. Chẳng hạn,\nthành phần `urlManager` đảm nhiệm chức năng cho bộ định tuyến cho các yêu cầu xử lý tới các bộ điều khiển;\nthành phần `db` cung cấp các dịch vụ để giao tiếp với cơ sở dữ liệu (CSDL); và các thành phần khác.\n\nMỗi thành phần ứng dụng đều có một định danh ID giúp xác định thành phần duy nhất trong cùng một ứng dụng\n. Bạn có thể truy cập vào các thành phần ứng dụng qua câu lệnh sau.\n\n```php\n\\Yii::$app->componentID\n```\n\nVí dụ, sử dụng câu lệnh `\\Yii::$app->db` để lấy thông tin [[yii\\db\\Connection|kết nối tới CSDL]],\nvà câu lệnh `\\Yii::$app->cache` để lấy thông tin [[yii\\caching\\Cache|primary cache]] đã đăng ký trong ứng dụng.\n\nMỗi thành phần ứng dụng được tạo một lần và được truy cập trong ứng dụng. Và có bất kỳ sự truy cập nào\nsau đó đều trả về cùng một thể hiện của thành phần đó.\n\nBất kỳ đối tượng nào cũng có thể là thành phần ứng dụng. Bạn có thể đăng ký chúng bằng việc thiết lập các\nthuộc tính [[yii\\base\\Application::components]] vào trong [mục cấu hình ứng dụng](structure-applications.md#application-configurations).\nVí dụ,\n\n```php\n[\n    'components' => [\n        // Dung class để đăng ký thành phần \"cache\"\n        'cache' => 'yii\\caching\\ApcCache',\n\n        // Dùng mảng các tham số để đăng ký thành phần \"db\"\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=demo',\n            'username' => 'root',\n            'password' => '',\n        ],\n\n        // Dùng hàm để đăng ký thành phần \"search\"\n        'search' => function () {\n            return new app\\components\\SolrService;\n        },\n    ],\n]\n```\n\n> Lưu ý: Bạn cần đăng ký các thành phần ứng dụng một cách cẩn thận.\n  Các thành phần ứng dụng cũng như các biến có phạm vi toàn cục. Sử dụng quá nhiều các thành phần ứng dụng có thể khiến mã nguồn\n  khó kiểm tra và bảo trì. Cách tốt nhất, bạn nên khởi tạo các thành phần ở phạm vi cục bộ\n  và khi cần thiết có thể thêm vào ứng dụng.\n\n\n## Thành phần tải tự động (Bootstrapping) <span id=\"bootstrapping-components\"></span>\n\nNhư đề cập ở trên, các thành phần ứng dụng chỉ được khởi tạo khi nó được truy cập vào lần đầu tiên.\nNếu thành phần không được truy cập tại các yêu cầu xử lý, thì sẽ không được khởi tạo. Tuy vậy , thỉnh thoảng, các thành phần ứng dụng\ncó thể được khởi tạo ở mỗi yêu cầu, thậm chí nó không được truy cập.\nĐể làm được như vậy, bạn cần liệt kê các định danh vào trong thuộc tinh [[yii\\base\\Application::bootstrap|bootstrap]] của ứng dụng application.\n\nVí dụ, thông tin cấu hình sau sẽ chắc chắn rằng thành phần `log` luôn luôn được tải:\n\n```php\n[\n    'bootstrap' => [\n        'log',\n    ],\n    'components' => [\n        'log' => [\n            // Các thiết lập cho thành phần \"log\"\n        ],\n    ],\n]\n```\n\n\n## Các thành phần ứng dụng chính <span id=\"core-application-components\"></span>\n\nYii định nghĩa danh sách các thành phần ứng dụng chính cùng với nó là các định danh và thông tin cấu hình. Ví dụ,\nthành phần [[yii\\web\\Application::request|request]] được dùng để lấy thông tin về các yêu cầu từ user\nvà xác minh rồi gửi tới các [bộ định tuyến (route)](runtime-routing.md); thành phần [[yii\\base\\Application::db|db]]\ncó chức năng thiết lập các kết nối và thông qua đó bạn có thể thực hiện các truy vấn vào CSDL.\nNhư vậy, các thành phần ứng dụng sẽ giúp ứng dụng Yii tiếp nhận các yêu cầu từ user.\n\nPhần dưới là danh sách các thành phần ứng dụng chính được xác định trước. Bạn cần phải cấu hình và tùy biến chúng\nnhư những thành phần ứng dụng khác. Mỗi khi bạn cấu hình các thành phần này,\nnếu bạn không xác định các class, thì giá trị mặc định sẽ được dùng.\n\n* [[yii\\web\\AssetManager|assetManager]]: quản lý các file tài nguyên (asset) được đóng gói và chia sẽ.\n  Tham khảo thêm mục [Quản lý các file tài nguyên](structure-assets.md) để biết thêm chi tiết.\n* [[yii\\db\\Connection|db]]: thực hiện kết nối CSDL và dựa vào thành phần có thể thực hiện các câu lệnh truy vấn dữ liệu.\n  Lưu ý, khi bạn thiết lập thành phần này, bạn cần phải cung cấp các thông tin về các thuộc tính được yêu cầu\n  , như [[yii\\db\\Connection::dsn]].\n  Tham khảo thêm tại mục [Data Access Objects](db-dao.md) để biết thêm thông tin.\n* [[yii\\base\\Application::errorHandler|errorHandler]]: nắm giữ các ngoại lệ và lỗi của PHP.\n  Tham khảo thêm mục [Bắt lỗi](runtime-handling-errors.md) để biết thêm thông tin.\n* [[yii\\i18n\\Formatter|formatter]]: định dạng dữ liệu mỗi khi gửi tới user. Ví dụ, các số có thể được\n  hiển thị cùng với các dấu ngăn cách phần ngàn, ngày có thể được định dạng ở dạng ngày dài.\n  Tham khảo thêm tại mục [Định dạng dữ liệu](output-formatting.md) để biết thêm thông tin.\n* [[yii\\i18n\\I18N|i18n]]: hỗ trợ định dạng và dịch đa ngôn ngữ.\n  Tham khảo thêm tại mục [Internationalization](tutorial-i18n.md) để biết thêm thông tin.\n* [[yii\\log\\Dispatcher|log]]: quản lý mục log.\n  Tham khảo thêm tại mục [Logging](runtime-logging.md) để biết thêm thông tin.\n* [[yii\\swiftmailer\\Mailer|mail]]: hỗ trợ soạn thảo và gửi email.\n  Tham khảo thêm tại mục [Mailing](tutorial-mailing.md) để biết thêm thông tin..\n* [[yii\\base\\Application::response|response]]: represents the response being sent to end users.\n  Tham khảo thêm tại mục [Responses](runtime-responses.md) để biết thêm thông tin..\n* [[yii\\base\\Application::request|request]]: tiếp nhận các yêu cầu từ user.\n  Tham khảo thêm tại mục [Requests](runtime-requests.md) để biết thêm thông tin..\n* [[yii\\web\\Session|session]]: quản lý các phiên (session). Thành phần này chỉ được kích hoạt với\n  [[yii\\web\\Application|Ứng dụng Web]].\n  Tham khảo thêm tại mục [Sessions and Cookies](runtime-sessions-cookies.md) để biết thêm thông tin..\n* [[yii\\web\\UrlManager|urlManager]]: xử lý thông tin về URL.\n  Tham khảo thêm tại mục [URL Parsing and Generation](runtime-routing.md) để biết thêm thông tin..\n* [[yii\\web\\User|user]]: giúp xác thực người dùng. Thành phần này chỉ được kích hoạt với\n  [[yii\\web\\Application|Ứng dụng Web]]\n  Tham khảo thêm tại mục [Xác thực (Authentication)](security-authentication.md) để biết thêm thông tin..\n* [[yii\\web\\View|view]]: hỗ trợ giao diện.\n  Tham khảo thêm tại mục[Views](structure-views.md) để biết thêm thông tin..\n"
  },
  {
    "path": "docs/guide-vi/structure-applications.md",
    "content": "Ứng dụng\n============\n\nMỗi ứng dụng là một đối tượng giúp quản lý tổng thể cấu trúc và vòng đời của ứng dụng Yii.\nMỗi ứng dụng Yii đều chứa một đối tượng ứng dụng, đối tượng này được khởi tạo tại mục\n[entry script](structure-entry-scripts.md) và đồng thời được truy cập qua biểu thức `\\Yii::$app`.\n\n> Gợi ý: Phụ thuộc vào từng ngữ cảnh, có khi chúng ta gọi là \"một application\", có nghĩa là một đối tượng ứng dụng\n  hoặc một hệ thống ứng dụng.\n\nCó 2 kiểu ứng dụng: [[yii\\web\\Application|Ứng dụng Web]] và\n[[yii\\console\\Application|ứng dụng giao diện dòng lệnh]]. Tương tự như vậy, ứng dụng Web xử lý với các yêu cầu về Web,\n, ứng dụng còn lại sẽ xử lý với các yêu cầu ở giao diện dòng lệnh.\n\n\n## Cấu hình ứng dụng <span id=\"application-configurations\"></span>\n\nMỗi khi [entry script](structure-entry-scripts.md) tạo ứng dụng mới, nó sẽ tải thêm thông tin về\n[cấu hình](concept-configurations.md) và gán vào trong ứng dụng, như sau:\n\n```php\nrequire(__DIR__ . '/../vendor/autoload.php');\nrequire(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');\n\n// tải các cấu hình ứng dụng\n$config = require(__DIR__ . '/../config/web.php');\n\n// gán cấu hình và khởi tạo ứng dụng\n(new yii\\web\\Application($config))->run();\n```\n\nThông thường việc [cấu hình](concept-configurations.md), ứng dụng sẽ xác định làm thế nào để\nkhởi tạo các thuộc tính và đối tượng ứng dụng. Do việc cấu hình ứng dụng khá phức tạp nên vậy\n, chúng thường được lưu giữ tại [các file cấu hình](concept-configurations.md#configuration-files),\nnhư file `web.php` ở ví dụ trên.\n\n\n## Các thuộc tính của ứng dụng <span id=\"application-properties\"></span>\n\nCó nhiều thuộc tính quan trọng mà bạn cần phải cấu hình trong ứng dụng. Những thuộc tính này\nthường được mô tả về môi trường mà ứng dụng đang chạy. Chẳng hạn, ứng dụng cần biết làm thế nào để tải các [controllers](structure-controllers.md),\nnơi lưu trữ các file tạm, vv. Trong phần dưới này, chúng ta sẽ tổng hợp thông tin về thuộc tính.\n\n\n### Thuộc tính bắt buộc <span id=\"required-properties\"></span>\n\nỞ mỗi ứng dụng, bạn cần cấu hình ít nhất 2 thuộc tính là: [[yii\\base\\Application::id|id]]\nvà [[yii\\base\\Application::basePath|basePath]].\n\n\n#### [[yii\\base\\Application::id|id]] <span id=\"id\"></span>\n\nThuộc tính [[yii\\base\\Application::id|id]] giúp đặc tả một định danh ID để phân biệt với các ứng dụng khác\n. Thuộc tính chủ yếu được sử dụng trong chương trình. Mặc dù nó không được yêu cầu, để thích hợp cho khả năng tương tác\nnên chỉ sử dụng các chữ cái chữ số khi mô tả một định danh của ứng dụng.\n\n\n#### [[yii\\base\\Application::basePath|basePath]] <span id=\"basePath\"></span>\n\nThuộc tính [[yii\\base\\Application::basePath|basePath]] dùng để mô tả thư mục gốc của ứng dụng.\nNó là thư mục chứa tất cả mã nguồn của ứng dụng. Bên trong thư mục,\nbạn sẽ thấy các thư mục con như `models`, `views`, và `controllers`, các thư mục con này chứa các mã nguồn\ntương ứng với các thành phần trong mô hình MVC.\n\nBạn phải cấu hình thuộc tính [[yii\\base\\Application::basePath|basePath]] bằng sử dụng các đường dẫn trực tiếp\nhoặc [một bí danh](concept-aliases.md). Trong các trường hợp, các thư mục tương ứng phải tồn tại, nếu không sẽ phát sinh ra lỗi\n. Đường dẫn trực tiếp được lấy qua việc gọi hàm `realpath()` .\n\nThuộc tính [[yii\\base\\Application::basePath|basePath]] thường được dùng để lấy được các đường dẫn quan trọng khác\n(vd đường dẫn dành cho thực thi). Vì vậy, bí danh `@app` được xác định là đường dẫn gốc \n. Các đường dẫn trong ứng dụng được lấy từ bí danh (vd `@app/runtime` tương ứng tới đường dẫn mục runtime).\n\n\n### Các thuộc tính quan trọng <span id=\"important-properties\"></span>\n\nCác thuộc tính được mô tả trong phần này thường cần được cấu hình bởi vì mỗi ứng dụng có\ncác thuộc tính khác nhau.\n\n\n#### [[yii\\base\\Application::aliases|aliases]] <span id=\"aliases\"></span>\n\nThuộc tính cho phép khai báo các [bí danh(aliases)](concept-aliases.md) vào trong một mảng.\nCác khóa lưu trữ tên bí danh, và giá trị trong mảng tương ứng với đường dẫn được khai báo.\nVí dụ:\n\n```php\n[\n    'aliases' => [\n        '@name1' => 'path/to/path1',\n        '@name2' => 'path/to/path2',\n    ],\n]\n```\n\nThuộc tính này được cung cấp cho bạn việc khai báo các bí danh trong cấu hình ứng dụng thay vì gọi phương thức\n[[Yii::setAlias()]].\n\n\n#### [[yii\\base\\Application::bootstrap|bootstrap]] <span id=\"bootstrap\"></span>\n\nThuộc tính này khá quan trọng. Nó cung cấp cho bạn thông tin về mảng các thành phần (components) mà cần được \nchạy trong suốt chu trình ứng dụng [[yii\\base\\Application::bootstrap()|bootstrapping process]].\nVí dụ, nếu bạn muốn một [module](structure-modules.md) dùng để tùy biến các [URL](runtime-routing.md),\nbạn có thể tùy biến các ID như phần tử trong các thuộc tính.\n\nMỗi thành phần được liệt kê ra có thể khai báo một trong các định dạng sau:\n\n- một đinh danh về thành phần được tuân thủ qua [components](#components),\n- một định danh về module tuân thủ theo quy định về [modules](#modules),\n- một tên class,\n- một mảng các cấu hình,\n- một hàm dùng để khởi tạo và trả về một thành phần.\n\nVí dụ:\n\n```php\n[\n    'bootstrap' => [\n        // một định danh về thành phần hoặc module\n        'demo',\n\n        // tên class\n        'app\\components\\Profiler',\n\n        // mảng cấu hình\n        [\n            'class' => 'app\\components\\Profiler',\n            'level' => 3,\n        ],\n\n        // hàm trả về một thành phần\n        function () {\n            return new app\\components\\Profiler();\n        }\n    ],\n]\n```\n\n> Lưu ý: Nếu định danh của module trùng với định danh của thành phần , ứng dụng sẽ sử dụng\n> trong suốt tiền trình xử lý. Nếu bạn muốn chỉ sử dụng mỗi module, bạn cần lấy nó ở một hàm khác\n> như sau:\n>\n> ```php\n> [\n>     function () {\n>         return Yii::$app->getModule('user');\n>     },\n> ]\n> ```\n\n\nTrong suốt quá trình xử lý, mỗi thành phần sẽ được khởi tạo. nếu lớp thành phần được hiện thực từ giao diện\n[[yii\\base\\BootstrapInterface]], thì phương thức [[yii\\base\\BootstrapInterface::bootstrap()|bootstrap()]]\nsẽ đồng thời được gọi.\n\nMột ví dụ khác trong việc cấu hình ứng dụng trong [Mẫu Basic Project](start-installation.md),\nmodule `debug` và `gii`  được cấu hình như những thành phần khi ứng dụng khởi chạy\nở môi trường phát triển:\n\n```php\nif (YII_ENV_DEV) {\n    // cấu hình được thiết lập trong môi trường phát triển 'dev'\n    $config['bootstrap'][] = 'debug';\n    $config['modules']['debug'] = 'yii\\debug\\Module';\n\n    $config['bootstrap'][] = 'gii';\n    $config['modules']['gii'] = 'yii\\gii\\Module';\n}\n```\n\n> Lưu ý: Việc đưa quá nhiều các thành phần vào `bootstrap` sẽ làm giảm hiệu năng trong ứng dụng, bởi vì\n  mỗi khi có yêu cầu, các thành phần sẽ được chạy. Vì vậy việc sử dụng các thành phần cần sử dụng một cách khôn ngoan.\n\n\n#### [[yii\\web\\Application::catchAll|catchAll]] <span id=\"catchAll\"></span>\n\nThuộc tính này chỉ được hỗ trợ với [[yii\\web\\Application| ứng dụng Web]]. Nó mô tả một\n [hành động](structure-controllers.md) và nhận xử lý mọi yêu cầu. Thường được sử dụng mỗi khi\nứng dụng đang ở chế độ bảo trì và cần xử lý mọi yêu cầu được gửi tới.\n\nThông tin được cấu hình bao gồm mảng và chứa thông tin về router và action.\nCác thông tin mô tả các tham số (thông tin khóa-giá trị) để giới hạn các action. Ví dụ:\n\n```php\n[\n    'catchAll' => [\n        'offline/notice',\n        'param1' => 'value1',\n        'param2' => 'value2',\n    ],\n]\n```\n\n\n#### [[yii\\base\\Application::components|components]] <span id=\"components\"></span>\n\nĐây là thuộc tính quan trọng nhất. Nó cho phép đăng ký danh sách cách component để sử dụng ở các mục khác\nđược gọi là [application components](structure-application-components.md). Ví dụ:\n\n```php\n[\n    'components' => [\n        'cache' => [\n            'class' => 'yii\\caching\\FileCache',\n        ],\n        'user' => [\n            'identityClass' => 'app\\models\\User',\n            'enableAutoLogin' => true,\n        ],\n    ],\n]\n```\n\nMỗi thành phần ứng dụng đều xác định một mảng các thông tin chứa cặp key-value. Giá trị key đại diện cho định danh của thành phần,\ntrong khi đó value đại diện cho tên class hoặc thông tin về [cấu hình](concept-configurations.md).\n\nBạn có thể đăng ký bất kỳ thành phần nào vào ứng dụng, và các thành phần có thể truy cập ở phạm vi toàn cục\nqua biểu thức `\\Yii::$app->componentID`.\n\nXem thêm mục [Application Components](structure-application-components.md) để biết thêm thông tin.\n\n\n#### [[yii\\base\\Application::controllerMap|controllerMap]] <span id=\"controllerMap\"></span>\n\nThuộc tính này cho phép liên kết tới một định danh (ID) tới lớp của trình điều khiển. Mặc định, Yii sẽ liên kết\nID tới các lớp của trình điều khiển dựa trên [các nguyên tắc](#controllerNamespace) (chẳng hạn định danh ID của trình điều khiển `post` sẽ liên kết\ntới lớp `app\\controllers\\PostController`). Bằng việc cấu hình những thuộc tính này, bạn có thay đổi các nguyên tắc này cho các trình điều khiển cụ thể\n. Trong ví dụ sau, `account` sẽ được liên kết tới class\n`app\\controllers\\UserController`, trong khi đó `article` sẽ liên kết tới class `app\\controllers\\PostController`.\n\n```php\n[\n    'controllerMap' => [\n        [\n            'account' => 'app\\controllers\\UserController',\n            'article' => [\n                'class' => 'app\\controllers\\PostController',\n                'enableCsrfValidation' => false,\n            ],\n        ],\n    ],\n]\n```\n\nDanh sách khóa của các thuộc tính trên đại diện cho ID của trình điều khiển, giá trị của mỗi khóa sẽ đại diện về thông tin\ntên class của trình điều khiển hoặc [các thông tin về cấu hình](concept-configurations.md).\n\n\n#### [[yii\\base\\Application::controllerNamespace|controllerNamespace]] <span id=\"controllerNamespace\"></span>\n\nThuộc tính này xác định các thông tin tên lớp mặc định của trình điều khiển. Mặc định là\n`app\\controllers`. Nếu ID của trình điều khiển là `post`, theo quy ước thì tên class của trình điều khiển (không bao gồm\nkhông gian tên) sẽ là `PostController`, và tên lớp đầy đủ sẽ là `app\\controllers\\PostController`.\n\nCác lớp trình điều khiển thường được lưu trữ ở thư mục con của thư mục chính các không gian tên.\nChẳng hạn, với ID của trình điều khiển `admin/post`, tương ứng với tên lớp đầy đủ sẽ là\n `app\\controllers\\admin\\PostController`.\n\nĐiều này khá quan trọng vì các lớp điều khiển có thể được [tải tự động](concept-autoloading.md)\nvà các không gian tên của các lớp điều khiển sẽ khớp với giá trị của các thuộc tính. Nếu không thì,\nbạn sẽ nhận thông báo lỗi \"Không tìm thấy trang\" khi truy cập vào ứng dụng.\n\nTrong trường hợp khác, nếu bạn muốn bỏ các quy ước này như mổ tả ở trên, bạn có thể tùy chỉnh lại các thuộc tính trong phần [controllerMap](#controllerMap).\n\n\n#### [[yii\\base\\Application::language|language]] <span id=\"language\"></span>\n\nThuộc tính này mô tả thông tin về ngôn ngữ trong mỗi ứng dụng và nội dung được hiển thị tới user.\nGiá trị mặc định của thuộc tính là `en`, có nghĩa là tiếng Anh. Bạn có thể tùy chỉnh thuộc tính này\nrằng nếu ứng dụng của bạn hỗ trợ đa ngôn ngữ .\n\nGiá trị của thuộc tính được xác định theo chuẩn [quốc tế hóa](tutorial-i18n.md),\nbao gồm các thông tin, định dạng ngày giờ, số, vv. Ví dụ, widget [[yii\\jui\\DatePicker]] \nsẽ sử dụng các giá trị thuộc tính qua việc xác định ngôn ngữ nào cần được hiển thị và định dạng ngày giờ như thế nào.\n\nKhuyến khích bạn xác định các ngôn ngữ dựa theo [IETF language tag](https://en.wikipedia.org/wiki/IETF_language_tag).\nVí dụ, `en` là chuẩn cho tiếng anh, trong khi đó `en-US` chuẩn cho tiếng anh ở Mỹ (United States).\n\nXem thêm thông tin về thuộc tính này tại mục [Internationalization](tutorial-i18n.md).\n\n\n#### [[yii\\base\\Application::modules|modules]] <span id=\"modules\"></span>\n\nThuộc tính này mô tả các thông tin về [modules](structure-modules.md) được chứa trong ứng dụng.\n\nThuộc tính này chứa mảng các lớp về module hoặc thông tin về [cấu hình](concept-configurations.md) chứa mảng các khóa\nvề các định danh của module. Ví dụ:\n\n```php\n[\n    'modules' => [\n        //  \"booking\" mô tả tên class\n        'booking' => 'app\\modules\\booking\\BookingModule',\n\n        // \"comment\" được mô tả với mảng cấu hình\n        'comment' => [\n            'class' => 'app\\modules\\comment\\CommentModule',\n            'db' => 'db',\n        ],\n    ],\n]\n```\n\nTham khảo thêm ở phần [Modules](structure-modules.md) để biết thêm thông tin.\n\n\n#### [[yii\\base\\Application::name|name]] <span id=\"name\"></span>\n\nThuộc tính này mô tả tên của ứng dụng và hiển thị tới user. Khác với thuộc tính\n[[yii\\base\\Application::id|id]], cần phải là tên duy nhất, thì thuộc tính này dùng với mục đích để hiển thị tới user;\nkhông cần thiết phải là tên duy nhất.\n\nNếu trong mã nguồn bạn không cần phải dùng tới nó thì bạn không cần phải thiết lập.\n\n\n#### [[yii\\base\\Application::params|params]] <span id=\"params\"></span>\n\nThuộc tính này là mảng chứa các tham số mà có thể truy cập trong ứng dụng ở phạm vi toàn cầu. Thay vì trong mã\nnguồn của bạn cần được mã hóa bởi số và ký tự, đây là cách tốt để định nghĩa các tham số của ứng dụng, định nghĩa một lần \nvà có thể được truy cập ở mọi nơi. Ví dụ, bạn có thể định nghĩa kích thước ảnh thumbnail\nvới kích thước như sau:\n\n```php\n[\n    'params' => [\n        'thumbnail.size' => [128, 128],\n    ],\n]\n```\n\nBạn có thể thực hiện dòng lệnh sau để lấy tham số về kích thước ảnh thumbnail:\n\n```php\n$size = \\Yii::$app->params['thumbnail.size'];\n$width = \\Yii::$app->params['thumbnail.size'][0];\n```\n\nBạn có thể thay đổi kích thước ảnh thumbnail sau đó, bạn chỉ cần thay đổi vào trong mục cấu hình ứng dụng;\nbạn không cần phải đụng chạm vào mã nguồn của bạn.\n\n\n#### [[yii\\base\\Application::sourceLanguage|sourceLanguage]] <span id=\"sourceLanguage\"></span>\n\nThuộc tính mô tả về ngôn ngữ được sử dụng để viết mã nguồn của bạn. Giá trị mặc đinh là `'en-US'`,\nnghĩa là tiếng Anh Mỹ(United States). Bạn nên cấu hình thuộc tính này nếu nội dung trong mã nguồn của bạn không phải là tiếng Anh.\n\nGiống như thuộc tính [language](#language), you should configure this property in terms of\nan [IETF language tag](https://en.wikipedia.org/wiki/IETF_language_tag). Ví dụ, `en` chuẩn cho tiếng Anh,\ntrong khi `en-US` chuẩn cho tiếng Anh Mỹ (United States).\n\nXem thêm trong phần [Quốc tế hóa](tutorial-i18n.md) để hiểu thêm thuộc tính này.\n\n\n#### [[yii\\base\\Application::timeZone|timeZone]] <span id=\"timeZone\"></span>\n\nThuộc tính này cung cấp cách khác để thiết lập time zone trong PHP.\nQua việc cấu hình thuộc tính này, chủ yếu được gọi qua hàm\n[date_default_timezone_set()](https://www.php.net/manual/en/function.date-default-timezone-set.php). Ví dụ:\n\n```php\n[\n    'timeZone' => 'America/Los_Angeles',\n]\n```\n\n\n#### [[yii\\base\\Application::version|version]] <span id=\"version\"></span>\n\nThuộc tính mô tả về phiên bản của ứng dụng. Mặc định là `'1.0'`. Bạn không cần phải thiết lập thuộc tính này nếu như\ntrong mã nguồn của bạn không dùng tới.\n\n\n### Các thuộc tính thông dụng <span id=\"useful-properties\"></span>\n\nNhững thuộc tính được mô tả trong phần dưới thường có sự cấu hình khác nhau bởi vì các giá trị thường khác nhau\n. Tuy nhiên, nêu bạn muốn thay đổi giá trị mặc định, bạn có thể cấu hình theo cách của bạn.\n\n\n#### [[yii\\base\\Application::charset|charset]] <span id=\"charset\"></span>\n\nThuộc tính này mô tả các bộ ký tự mà ứng dụng sử dụng. Mặc định là `'UTF-8'`, hầu hết các ứng dụng đều sử dụng.\n\n\n#### [[yii\\base\\Application::defaultRoute|defaultRoute]] <span id=\"defaultRoute\"></span>\n\nThuộc tính này mô tả các [route](runtime-routing.md), ứng dụng sẽ dùng route này để thực hiện khi có yêu cầu\ngửi đến mà không được mô tả. Mỗi router gồm có các module ID, a controller ID, hoặc có thể là một action ID.\nVí dụ, `help`, `post/create`, hoặc `admin/post/create`. Nếu action ID không khai báo, thuộc tính sẽ lấy giá trị mặc định\nđược mô tả trong [[yii\\base\\Controller::defaultAction]].\n\nĐối với [[yii\\web\\Application| Ứng dụng Web ]], giá trị mặc định của thuộc tính là `'site'`, nghĩa là\ntrình điều khiển `SiteController` được gọi và một hành động mặc định được sử dụng. Như vậy, nếu bạn \ntruy cập vào ứng dụng mà không cung cấp thông tin route, thì ứng dụng mặc định sẽ trả về hành động `app\\controllers\\SiteController::actionIndex()`.\n\nĐối với [[yii\\console\\Application| Ứng dụng console]], thì giá trị mặc định là `'help'`, đồng nghĩa hành động\n[[yii\\console\\controllers\\HelpController::actionIndex()]] sẽ được gọi. Như vậy, nếu bạn chạy dòng lệnh `yii`\nmà không cung cấp các tham số nào khác, thì nó sẽ hiển thị lên màn hình trợ giúp tương ứng kết quả của action index của trình điều khiển HelpController.\n\n\n#### [[yii\\base\\Application::extensions|extensions]] <span id=\"extensions\"></span>\n\nThuộc tính này mô tả về danh sách các [thành phần mở rộng (extensions)](structure-extensions.md) đã được cài và sử dụng trong ứng dụng.\nMặc định, thuộc tính sẽ nhận mảng được trả về từ file `@vendor/yiisoft/extensions.php`. File `extensions.php` \nđược sinh tự động khi bạn sử dụng [Composer](https://getcomposer.org) để cài các thành phần mở rộng.\nỞ các trường hợp này, thuộc tính này có thể không cần cấu hình.\n\nTrong trường hợp, khi bạn muốn cấu hình các extension một cách thủ công, bạn có thể cấu hình thuộc tính như sau:\n\n```php\n[\n    'extensions' => [\n        [\n            'name' => 'tên extension',\n            'version' => 'phiên bản',\n            'bootstrap' => 'BootstrapClassName',  // mặc định, giá trị thường là mảng\n            'alias' => [  // mặc định\n                '@alias1' => 'to/path1',\n                '@alias2' => 'to/path2',\n            ],\n        ],\n\n        // ... các extensions khác ...\n\n    ],\n]\n```\n\nNhư bạn thấy ở phần trên, thuộc tính sẽ nhận thông tin bao gồm mảng các cấu hình. Mỗi extension được mô tả là mảng\nbao gồm các thành phần là`name` và `version`. Nêu muốn extension cần được chạy ở tiến trình [bootstrap](runtime-bootstrapping.md)\n, mỗi `bootstrap` cần được mô tả về tên lớp hoặc mảng giá trị về [cấu hình](concept-configurations.md)\n. Mỗi extension có thể định nghĩa thêm các [bí danh (aliases)](concept-aliases.md).\n\n\n#### [[yii\\base\\Application::layout|layout]] <span id=\"layout\"></span>\n\nThuộc tính này mô tả vê layout mặc định được dùng mỗi khi render dữ liệu ra [view](structure-views.md).\nGiá trị mặc định là `'main'`, nghĩa là file `main.php` nằm trong [đường dẫn layout](#layoutPath) được dùng.\nNếu giá trị [layout path](#layoutPath) và [view path](#viewPath) nhận giá trị là mặc định,\ngiá trị mặc định của file layout có thể được thay thế qua bí danh `@app/views/layouts/main.php`.\n\nBạn có thể cấu hình thuộc tính với giá trị `false` nếu bạn muốn tắt giá trị mặc định của layout.\n\n\n#### [[yii\\base\\Application::layoutPath|layoutPath]] <span id=\"layoutPath\"></span>\n\nThuộc tính mô tả đường dẫn nơi lưu trữ file layout. Giá trị mặc định sẽ là\n`layouts` thư mục con nằm trong [đường dẫn view](#viewPath). Nếu [đường dẫn view](#viewPath) nhận giá trị mặc định\n, giá trị mặc định tới đường dẫn layout được thay thế như một đường dẫn của bí danh `@app/views/layouts`.\n\nBạn có thể cấu hình như đường dẫn hoặc một [bí danh](concept-aliases.md).\n\n\n#### [[yii\\base\\Application::runtimePath|runtimePath]] <span id=\"runtimePath\"></span>\n\nĐường dẫn chứa đường dẫn tới các file tạm của ứng dụng, như file log và cache, cần được tạo ra.\nGiá trị mặc định của đường dẫn có thể lấy qua bí danh `@app/runtime`.\n\nBạn có thể cấu hình thuộc tính với đường dẫn hoặc một [bí danh](concept-aliases.md). Đường dẫn này cần được quyền ghi đè lên \ntrong quá trình ứng dụng được chạy. Và user không thể truy cập vào đường dẫn\n, bởi vì các tập tin này có thể chứa các thông tin nhạy cảm.\n\nYii cung cấp cách đơn giản nhất để truy cập vào đường dẫn này qua bí danh là `@runtime`.\n\n\n#### [[yii\\base\\Application::viewPath|viewPath]] <span id=\"viewPath\"></span>\n\nThuộc tính này chỉ định thư mục để lưu trữ những file view trong mô hình MVC. Giá trị mặc định là\nlà một bí danh `@app/views`. Bạn có thể cấu hình nó với thư mục hoặc đường dẫn [alias](concept-aliases.md).\n\n\n#### [[yii\\base\\Application::vendorPath|vendorPath]] <span id=\"vendorPath\"></span>\n\nThuộc tính này quy định cụ thể về thư mục được quản lý bởi [Composer](https://getcomposer.org). Thư mục này chứa các thư viện\nđược cung cấp bởi nhà phát triển và được dùng trong ứng dụng, bao gồm Yii framework. Giá trị mặc định là\nmột thư mục được cung cấp bởi bí danh `@app/vendor`.\n\nThuộc tính có thể được cấu hình là thư mục hoặc là đường dẫn [alias](concept-aliases.md). Mỗi khi bạn thay đổi thuộc tính này\n, bạn cần phải thay đổi thông tin cấu hình Composer cho phù hợp.\n\nYii cung cấp cách thức đơn giản để truy cập vào đường dẫn này qua bí danh là `@vendor`.\n\n\n#### [[yii\\console\\Application::enableCoreCommands|enableCoreCommands]] <span id=\"enableCoreCommands\"></span>\n\nThuộc tính này chỉ được hỗ trợ bởi [[yii\\console\\Application|ứng dụng console]]. Nó được xác định \nvị trí các dòng lệnh được kích hoạt lên trong phiên bản Yii. Giá trị mặc định là `true`.\n\n\n## Sự kiện <span id=\"application-events\"></span>\n\nỨng dụng trong chu trình hoạt động sẽ gán một vài sự kiện để nắm bắt các yêu cầu. Bạn cần liên kết tới các sự kiện\nvào trong ứng dụng như sau:\n\n```php\n[\n    'on beforeRequest' => function ($event) {\n        // ...\n    },\n]\n```\n\nCác mô tả về cú pháp của các sự kiện `on eventName` được mô tả ở trong mục [Cấu hình](concept-configurations.md#configuration-format).\n\nCách khác, bạn có thể nắm bắt các sự kiện tại [tiến trình bootstrapping](runtime-bootstrapping.md)\nmỗi khi ứng dụng được khởi tạo. Ví dụ:\n\n```php\n\\Yii::$app->on(\\yii\\base\\Application::EVENT_BEFORE_REQUEST, function ($event) {\n    // ...\n});\n```\n\n### [[yii\\base\\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_REQUEST]] <span id=\"beforeRequest\"></span>\n\nSự kiện được gán *trước lúc* ứng dụng nhận các yêu cầu. Tên sự kiện gọi là `beforeRequest`.\n\nMỗi nắm bắt được sự kiện, ứng dụng sẽ tải các thông tin cấu hình và khởi tạo. So it is a good place\nto insert your custom code via the event mechanism to intercept the request handling process. For example,\nin the event handler, you may dynamically set the [[yii\\base\\Application::language]] property based on some parameters.\n\n\n### [[yii\\base\\Application::EVENT_AFTER_REQUEST|EVENT_AFTER_REQUEST]] <span id=\"afterRequest\"></span>\n\nSự kiện được gán *sau khi* ứng dụng hoàn thành việc xử lý *trước lúc* đưa phản hồi.\nTên sự kiện là `afterRequest`.\n\nMỗi khi sự kiện được gán, việc nắm giữ yêu cầu xử lý thành công thì bạn có thể xử lý sau đó \ncác thông tin về yêu cầu xử lý hoặc nội dung phản hồi.\n\nLưu ý rằng thành phần [[yii\\web\\Response|response]] luôn được gán một vài sự kiện mỗi lúc gửi nội dung\ntới user. Những sự kiện được gán  *sau* sự kiện này là.\n\n\n### [[yii\\base\\Application::EVENT_BEFORE_ACTION|EVENT_BEFORE_ACTION]] <span id=\"beforeAction\"></span>\n\nSự kiện sẽ được gán *trước* khi thực hiện chạy một [hành động](structure-controllers.md).\nTên sự kiện là `beforeAction`.\n\nCác tham số của sự kiện được khởi tạo từ [[yii\\base\\ActionEvent]]. Các sự kiện cần thiết lập thuộc tính\n[[yii\\base\\ActionEvent::isValid]] về giá trị `false` để tạm ngưng hành động.\nVí dụ:\n\n```php\n[\n    'on beforeAction' => function ($event) {\n        if (some condition) {\n            $event->isValid = false;\n        } else {\n        }\n    },\n]\n```\n\nLưu ý, có một sự kiện tương tự `beforeAction` được gán bởi [modules](structure-modules.md)\nvà [controllers](structure-controllers.md). Ứng dụng sẽ nắm bắt sự kiện này trước\n, tiếp sau đó bởi modules (nếu có), và cuối cùng là trình điều khiển. Nếu sự kiện đều thiết lập tham số\n[[yii\\base\\ActionEvent::isValid]] là `false`, tất cả các yêu cầu nằm trong sự kiện sẽ không được nắm giữ.\n\n\n### [[yii\\base\\Application::EVENT_AFTER_ACTION|EVENT_AFTER_ACTION]] <span id=\"afterAction\"></span>\n\nTương tự, sự kiện này được gán *sau khi* khởi chạy một [hành động](structure-controllers.md).\nTên sự kiện là `afterAction`.\n\nCác tham số của sự kiện được khởi tạo từ [[yii\\base\\ActionEvent]]. Xem thuộc tính\n[[yii\\base\\ActionEvent::result]], sự kiện này có thể truy cập hoặc chỉnh sửa kết quả trả về.\nChẳng hạn:\n\n```php\n[\n    'on afterAction' => function ($event) {\n        if (điều kiện) {\n            // thay đổi kết quả $event->result\n        } else {\n        }\n    },\n]\n```\n\nLưu ý rằng với sự kiện `afterAction` được gán bởi [modules](structure-modules.md)\nvà [controllers](structure-controllers.md). Những đối tượng được gán vào trong sự kiện này được ưu tiên ngược lại\nnhư sự kiện `beforeAction`. Đó là, trình điều khiển sẽ nắm giữ trước,\ntiếp đến là module modules (nếu có), và cuối cùng là ứng dụng.\n\n\n## Vòng đời ứng dụng <span id=\"application-lifecycle\"></span>\n\n![Vòng đời ứng dụng](images/application-lifecycle.png)\n\nKhi một [entry script](structure-entry-scripts.md) được gọi và nắm giữ các yêu cầu,\nvòng đời của ứng dụng sẽ được thực hiện như sau:\n\n1. Entry script sẽ tải các thông tin cấu hình trong ứng dụng ra một mảng.\n2. Entry script sẽ khởi tạo mới một ứng dụng:\n  * Phương thức [[yii\\base\\Application::preInit()|preInit()]] sẽ được gọi, nhằm tải các thông tin cấu hình mà có sự ưu tiên cao\n    , như thuộc tính [[yii\\base\\Application::basePath|basePath]].\n  * Đăng ký một [[yii\\base\\Application::errorHandler|error handler]].\n  * Cấu hình các thuộc tính trong ứng dụng.\n  * Phương thức [[yii\\base\\Application::init()|init()]] sẽ được gọi và phương thức\n    [[yii\\base\\Application::bootstrap()|bootstrap()]] sẽ tải thành phần bootstrapping.\n3. Entry script sẽ gọi phương thức [[yii\\base\\Application::run()]] để chạy ứng dụng:\n  * Sự kiện [[yii\\base\\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_REQUEST]] sẽ được gán sau đó.\n  * Xử lý các yêu cầu: chuyển các yêu cầu vào [bộ định tuyến (route)](runtime-routing.md) và các tham số liên quan;\n    khởi tạo đối tượng module, controller, và action như phần mô tả ở bộ định tuyến; và khởi chạy action.\n  * Gán sự kiện [[yii\\base\\Application::EVENT_AFTER_REQUEST|EVENT_AFTER_REQUEST]].\n  * Gửi phản hồi tới user.\n4. Entry script tiếp nhận trạng thái kết thúc từ ứng dụng hoàn tất xử lý tiến trình.\n"
  },
  {
    "path": "docs/guide-vi/structure-controllers.md",
    "content": "Bộ điều khiển (Controller)\n===========\n\nController thuộc một phần trong mẫu thiết kế [MVC](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller).\nController là đối tượng được kế thừa từ class [[yii\\base\\Controller]] và chịu trách nhiệm xứ lý các yêu cầu và gửi phản hồi\n. Đặc biệt, sau khi tiếp nhận các yêu cầu điều khiển từ [ứng dụng](structure-applications.md),\ncontrollers sẽ phân tích thông tin yêu cầu được gửi đến, gửi dữ liệu qua [models](structure-models.md) để xử lý, và gán kết quả xử lý từ model\nvào [views](structure-views.md), và cuối cùng là gửi phản hồi.\n\n\n## Hành động (Actions) <span id=\"actions\"></span>\n\nMỗi Controller đều chứa các *action* để user có thế tìm thấy, gửi yêu cầu tới ứng dụng để xử lý\n. Mỗi bộ điều khiển có thể có nhiều hành động.\n\nVí dụ dưới mô tả Controller `post` cùng với 2 action là : `view` và `create`:\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse app\\models\\Post;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\n\nclass PostController extends Controller\n{\n    public function actionView($id)\n    {\n        $model = Post::findOne($id);\n        if ($model === null) {\n            throw new NotFoundHttpException;\n        }\n\n        return $this->render('view', [\n            'model' => $model,\n        ]);\n    }\n\n    public function actionCreate()\n    {\n        $model = new Post;\n\n        if ($model->load(Yii::$app->request->post()) && $model->save()) {\n            return $this->redirect(['view', 'id' => $model->id]);\n        } else {\n            return $this->render('create', [\n                'model' => $model,\n            ]);\n        }\n    }\n}\n```\n\nVới action `view` (được định nghĩa bởi phương thức `actionView()`), dòng đầu dùng [Model](structure-models.md)\nđể tải dữ liệu dựa theo định danh ID; Nếu Model được tải thành công, thì sẽ được hiển thị qua\n[view](structure-views.md) là  `view`. Còn không, ứng dụng sẽ thông báo ngoại lệ là không tìm thấy.\n\nVới action `create` (được định nghĩa bởi phương thức `actionCreate()`), tương tự như vậy. Trước tiên\nsẽ khởi tạo [Model](structure-models.md), Model sẽ thực hiện nhận dữ liệu và lưu thông tin. Nếu cả hai việc này thành công thì Controller\nsẽ điều hướng trình duyệt tới View `view` cùng với định danh ID vừa được tạo bởi Model. Còn không, Controller sẽ gọi\nView  `create` có chức năng hiển thị form nhập liệu.\n\n\n## Bộ định tuyến (Routes) <span id=\"routes\"></span>\n\nNgười dùng có thể tìm thấy các actions qua các bộ định tuyến gọi là *routes*. Mỗi Route là chuỗi bao gồm các thông tin:\n\n* Một định danh của Module:  chỉ tồn tại nếu bộ điều khiển thuộc về thành phần [module](structure-modules.md);\n* Một định danh của [Controller](#controller-ids): là một chuỗi xác định duy nhất của Controller trong ứng dụng\n  (hoặc có thể là Module nếu Controller tương ứng là một Module);\n* Một [Action ](#action-ids):là một chuỗi xác định duy nhất của Action trong ứng dụng.\n\nMỗi Route có định dạng như sau:\n\n```\nControllerID/ActionID\n```\n\nhoặc có định dạng sau nếu Controller được gán như một Module:\n\n```php\nModuleID/ControllerID/ActionID\n```\n\nNhư vậy nếu user truy cập vào đường dẫn sau `https://hostname/index.php?r=site/index`, thì hành động `index` nằm trong bộ điều khiển `site`\nsẽ được thực hiện. Để biết thêm thông tin về cách bộ định tuyến xác định các hành động, vui lòng tham khảo tại mục\n[Routing và URL Generation](runtime-routing.md).\n\n\n## Tạo Controller <span id=\"creating-controllers\"></span>\n\nTrong mỗi [[yii\\web\\Application|Ứng dụng Web]], Controllers cần được kế thừa từ class [[yii\\web\\Controller]] hoặc các lớp con của nó\n. Tương tự trong [[yii\\console\\Application|Ứng dụng console]], Controllers cần được kế thừa từ class\n[[yii\\console\\Controller]] hoặc các lớp con của nó. Đoạn code sau được định nghĩa trong Controller `site` :\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n}\n```\n\n\n### Định danh Controller (Controller ID) <span id=\"controller-ids\"></span>\n\nThông thường, Controller được thiết kế để xử lý các yêu cầu từ các nguồn tài nguyên cụ thể.\nVì lý do này, mỗi định danh của Controller thường là danh từ cập nhật tới kiểu tài nguyên được xử lý.\nVí dụ, sử dụng `article` như định danh của Controller nhằm lưu giữ các dữ liệu về bài viết.\n\nMặc định, Thông tin các định danh Controller nên chỉ chứa các ký tự : Các chữ cái viết thường, số,\ndấu gạch dưới, dấu nối, và dấu gạch nghiêng phải.  Ví dụ, `article` và `post-comment` đều là những định danh đúng,\ntrong khi đó `article?`, `PostComment`, `admin\\post` đều không hợp lý.\n\nCác định danh Controller ID nên chứa tiền tố trong thư mục con. Ví dụ, `admin/article` xác định cho Controller `article`\nnằm trong thư mục `admin` được dựa theo [[yii\\base\\Application::controllerNamespace|controller namespace]].\nCác ký tự hợp lệ dùng cho các tiền tố của thư mục con bao gồm: Các chữ cái viết thường, số, dấu gạch, và dấu gạch nghiêng phải\n, dấu gạch nghiêng phải được dùng để phân cách như các thư mục con (vd. `panels/admin`).\n\n\n### Tên lớp Controller <span id=\"controller-class-naming\"></span>\n\nTên lớp của các Controller được khởi tạo từ các định danh Controller theo các bước sau:\n\n1. Chuyển ký tự đầu tiên trong mỗi từ cách nhau bởi dấu gạch nối thành ký tự hoa. Lưu ý rằng nếu các định danh Controller \n  có chứa dấu gạch chéo, thì quy tắc này chỉ được áp dụng ở phần sau dấu gạch chéo cuối cùng trong các định danh.\n2. Xoá các dấu gạch nối và thay thế các dấu gạch chéo xuôi(/) thành dấu gạch chéo ngược (\\).\n3. Thêm hậu tố `Controller`.\n4. Thêm [[yii\\base\\Application::controllerNamespace|controller namespace]].\n\nXem ví dụ sau, giả sử [[yii\\base\\Application::controllerNamespace|controller namespace]]\nnhận giá trị mặc định là `app\\controllers`:\n\n* `article` thành `app\\controllers\\ArticleController`;\n* `post-comment` thành `app\\controllers\\PostCommentController`;\n* `admin/post-comment` thành `app\\controllers\\admin\\PostCommentController`;\n* `adminPanels/post-comment` thành `app\\controllers\\adminPanels\\PostCommentController`.\n\nCác lớp Controller cần được [tự động tải](concept-autoloading.md). Vì vậy, trong ví dụ trên,\nlớp của Controller `article` cần được lưu vào file có [bí danh](concept-aliases.md)\nlà `@app/controllers/ArticleController.php`; trong khi đó `admin/post-comment` cần được lưu vào file\nlà `@app/controllers/admin/PostCommentController.php`.\n\n> Lưu ý: Ở ví dụ với định danh Controller `admin/post-comment` hướng dẫn bạn đặt các Controller vào trong thư mục con\n  của [[yii\\base\\Application::controllerNamespace|không gian tên Controller]]. Thông tin này khá là hữu ích\n  mỗi khi bạn muốn quản lý các Controllers và các chuyên mục và bạn không muốn sử dụng thành phần [Modules](structure-modules.md).\n\n\n### Controller Map <span id=\"controller-map\"></span>\n\nBạn có thể cấu hình thông tin về mục  [[yii\\base\\Application::controllerMap|controller map]] để khắc phục những hạn chế về các định danh\nvà tên class của Controller được mô tả ở trên. Điều này khá hữu ích khi bạn muốn sử dụng\ncác Controller ở bên thứ ba và bạn không có quyền việc kiểm soát các class này.\n\nBạn có thể cấu hình [[yii\\base\\Application::controllerMap|controller map]] trong mục\n[cấu hình ứng dụng](structure-applications.md#application-configurations). Ví dụ:\n\n```php\n[\n    'controllerMap' => [\n        // mô tả Controller \"account\" được sử dụng\n        'account' => 'app\\controllers\\UserController',\n\n        // mô tả về cấu hình Controller\"article\" dạng mảng \n        'article' => [\n            'class' => 'app\\controllers\\PostController',\n            'enableCsrfValidation' => false,\n        ],\n    ],\n]\n```\n\n\n### Controller mặc định <span id=\"default-controller\"></span>\n\nMỗi ứng dụng đều có một Controller mặc định được mô tả qua thuộc tính [[yii\\base\\Application::defaultRoute]].\nKhi một yêu cầu không được mô tả cụ thể ở mục [route](#routes), thì route mặc định sẽ được gọi.\nChẳng hạn [[yii\\web\\Application|Web applications]], có giá trị là `'site'`, trong khi đó [[yii\\console\\Application|ứng dụng console]],\ncó route mặc định là `help`. Vì vậy, nếu truy cập vào URL sau `https://hostname/index.php`, thì Controller `site` sẽ được gọi và xử lý yêu cầu.\n\nBạn có thể thay đổi thông tin Controller mặc định tại mục [cấu hình ứng dung](structure-applications.md#application-configurations) như sau:\n\n```php\n[\n    'defaultRoute' => 'main',\n]\n```\n\n\n## Tạo Actions <span id=\"creating-actions\"></span>\n\nTạo mới một Action khá là đơn giản, bằng chỉ việc định nghĩa trong lớp Controller cùng với tên *action phương thức*. Các phương thức của mỗi Action\nđều có phạm vi *toàn cục* tên của phương thức được bắt đầu bằng từ `action`. Kết quả trả về của mỗi action sẽ tương ứng với\ndữ liệu được gửi tới user. Đoạn mã sau sẽ định nghĩa hai action là, `index` và `hello-world`:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actionIndex()\n    {\n        return $this->render('index');\n    }\n\n    public function actionHelloWorld()\n    {\n        return 'Hello World';\n    }\n}\n```\n\n\n### Định danh của Action (Action ID) <span id=\"action-ids\"></span>\n\nMỗi action được dùng cho nhiệm vụ với tài nguyên cụ thể. Vì lý do này, mỗi\naction ID thường là một đông từ, như `view`, `update`, vv.\n\nMặc định, mỗi action ID chỉ nên chứa các chữ cái: Chữ thường, số,\ndấu gạch dưới, và dấu gạch ngang. (Bạn cũng có thể sử dụng các dấu gạch ngang để nối các từ lại với nhau.) Ví dụ,\n`view`, `update2`, và `comment-post` đều là những định danh hợp lệ, còn `view?` và `Update` là không hợp lệ.\n\nCó hai cách để tạo mới các action: inline actions và standalone actions. Với inline action được định nghĩa\nnhư những phương thức trong lớp Controller, trong khi đó standalone action là lớp được kế thừa từ lớp\n[[yii\\base\\Action]] hoặc là lớp con. Nếu bạn không muốn tái sử dụng các action thì bạn có thể dùng inline actions\n, cách này thường hay được sử dụng hơn. Trong khi đó các standalone actions thường được tạo để sử dụng\nở những Controllers khác nhau và được dùng như [thành phần mở rộng](structure-extensions.md).\n\n\n### Inline Actions <span id=\"inline-actions\"></span>\n\nInline actions được định nghĩa vào trong các phương thức như chúng ta đã mô tả trên.\n\nTên của phương thức thường đặt tên theo định danh của action và theo các bước sau:\n\n1. Chuyển ký tự đầu tiên trong mỗi từ định danh của action thành ký tự in hoa.\n2. Xoá dấu gạch nối.\n3. Thêm tiền tố `action`.\n\nVí dụ, `index` thành `actionIndex`, và `hello-world` thành `actionHelloWorld`.\n\n> Lưu ý: Việc đặt tên của các phương thức cần phải *cẩn thận*. Nếu bạn có phương thức là `ActionIndex`,\n  thì phương thức của action sẽ không xác định, như vậy, khi có yêu cầu tới action `index`\n  thì sẽ sinh ra lỗi. Cũng lưu ý rằng các phương thức này phải ở phạm vi public (toàn cục). Các phạm vi private (riêng) hoặc protected (bảo vệ)\n  thì sẽ không được định nghĩa như một action.\n\n\nInline actions thường được hay sử dụng hơn bởi vì việc khởi tạo đơn giản hơn. Tuy nhiên,\nnếu bạn muốn tái sử dụng action ở những vị trí khác, bạn có thể tham khảo thêm ở mục *standalone action*.\n\n\n### Standalone Actions <span id=\"standalone-actions\"></span>\n\nStandalone actions được định nghĩa từ việc kế thừa từ class [[yii\\base\\Action]] hoặc các lớp con của nó.\nVí dụ, ở phiên bản Yii đã phát hành, các action [[yii\\web\\ViewAction]] và [[yii\\web\\ErrorAction]], đều là những\nstandalone actions.\n\nĐể sử dụng standalone action, bạn cần phải khai báo ở phần *liên kết các action* bằng việc ghi đè lên phương thức\n[[yii\\base\\Controller::actions()]] ở lớp Controller như sau:\n\n```php\npublic function actions()\n{\n    return [\n        // khai báo action \"error\" bằng việc sử dụng tên class\n        'error' => 'yii\\web\\ErrorAction',\n\n        // khai báo action \"view\" bằng thông tin cấu hình dạng mảng\n        'view' => [\n            'class' => 'yii\\web\\ViewAction',\n            'viewPrefix' => '',\n        ],\n    ];\n}\n```\n\nNhư vậy, phương thức `actions()` sẽ trả về một mảng và chứa các khoá của các định danh action và giá trị tương ứng\ntên class hoặc thông tin [cấu hình](concept-configurations.md). Không giống như inline actions, action ID được dùng cho standalone\nactions có thể chứa các ký tự tuỳ ý, miễn là chúng được khai báo trong phương thức `actions()`.\n\nĐể tạo các class standalone action, bạn nên kế thừa từ lớp [[yii\\base\\Action]] hoặc lớp con của nó, và hiện thực\nphương thức là `run()`. Vài trò của phương thức `run()` tương tự như một phương thức của action. Chẳng hạn,\n\n```php\n<?php\nnamespace app\\components;\n\nuse yii\\base\\Action;\n\nclass HelloWorldAction extends Action\n{\n    public function run()\n    {\n        return \"Hello World\";\n    }\n}\n```\n\n\n### Kết quả trả về của Action <span id=\"action-results\"></span>\n\nKết quả trả về của phương thức action hoặc phương thức `run()` của standalone action khá quan trọng. Nó là\nkết quả tương ứng của từng action.\n\nGiá trị trả về là đối tượng [phản hồi](runtime-responses.md) được gửi tới user như những phản hồi.\n\n* Chẳng hạn với [[yii\\web\\Application|Ứng dụng Web]], kết quả trả về bao gồm dữ liệu được gán vào thuộc tính\n  [[yii\\web\\Response::data]] và chuyển sang dữ liệu là string chuyển tới nội dung phản hồi kết quả.\n* Với [[yii\\console\\Application|ứng dụng console]], kết quả trả về là số nguyên tương ứng với thuộc tính\n  [[yii\\console\\Response::exitStatus|exit status]] của mỗi lần thực thi lệnh.\n\nỞ ví dụ dưới, action sẽ trả về là chuỗi dữ liệu và được xử lý như nội dung phản hồi tới user\n. Ví dụ dưới chỉ cách các action điều hướng tới trình duyệt một URL \nbằng việc gửi một đối tượng phản hồi (vì phương thức [[yii\\web\\Controller::redirect()|redirect()]] sẽ trả về\nmột đối tượng):\n\n```php\npublic function actionForward()\n{\n    // điều hướng tới URL https://example.com\n    return $this->redirect('https://example.com');\n}\n```\n\n\n### Các tham số của Action <span id=\"action-parameters\"></span>\n\nCác phương thức dành cho inline action và phương thức `run()` cho standalone actions có thể nhận các tham số,\nđược gọi là *các tham số action*. Giá trị nhận được từ các yêu cầu. Với [[yii\\web\\Application|Ứng dụng Web]],\ngiá trị của các tham số được nhận từ biến `$_GET` sử dụng các tham số như các khoá;\nvới [[yii\\console\\Application|ứng dụng console]], các tham số sẽ tương ứng với các đối số dòng lệnh.\n\nTrong ví dụ sau, action `view`  (là một inline action) được khai báo hai tham số là: `$id` và `$version`.\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass PostController extends Controller\n{\n    public function actionView($id, $version = null)\n    {\n        // ...\n    }\n}\n```\n\nCác tham số cho action sẽ được dùng như sau và tương ứng với các yêu cầu khác nhau:\n\n* `https://hostname/index.php?r=post/view&id=123`: biến `$id` sẽ nhận giá trị là\n  `'123'`,  trong khi đó tham số `$version` nhận giá trị null vì không có đối số `version` được truyền lên.\n* `https://hostname/index.php?r=post/view&id=123&version=2`: biến `$id` và `$version` sẽ nhận giá trị tương ứng là\n   `'123'` và `'2'`.\n* `https://hostname/index.php?r=post/view`: ngoại lệ [[yii\\web\\BadRequestHttpException]] sẽ được gửi ra\n  vì tham số `$id` không được gửi lên.\n* `https://hostname/index.php?r=post/view&id[]=123`: xảy ra ngoại lệ [[yii\\web\\BadRequestHttpException]] lý do vì\n  tham số `$id` nhận dữ liệu là một mảng do vậy không hợp lệ `['123']`.\n\nNếu bạn muốn tham số của action nhận dữ liệu là một mảng, bạn nên khai báo biên là `array`, như sau:\n\n```php\npublic function actionView(array $id, $version = null)\n{\n    // ...\n}\n```\n\nNếu yêu cầu là `https://hostname/index.php?r=post/view&id[]=123`, thì tham số `$id` sẽ nhận giá trị là\n `['123']`. Nếu yêu cầu là `https://hostname/index.php?r=post/view&id=123`, tham số `$id` sẽ chỉ nhận\ncác giá trị trong mảng là giống nhau bởi vì giá trị `'123'` không là mảng và sẽ tự động chuyển vào mảng.\n\nỞ ví dụ trên sẽ hướng dẫn các tham số trong mỗi action hoạt động trong ứng dụng Web. Với ứng dụng console,\nvui lòng tham khảo tại mục [Console Commands](tutorial-console.md) để biết thêm thông tin.\n\n\n### Action mặc định <span id=\"default-action\"></span>\n\nMỗi controller đều có các action mặc định và đợc mô tả ở thuộc tính [[yii\\base\\Controller::defaultAction]].\nMỗi khi [route](#routes) chỉ nhận giá trị là định danh của controller, router sẽ tự hiểu rằng action mặc định\ncủa controller sẽ được gọi.\n\nMặc định, action mặc định sẽ là `index`. nếu bạn muốn thay đổi giá trị này, cách đơn giản nhất là ghi đè thuộc tính\ntrong lớp controller, như sau:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public $defaultAction = 'home';\n\n    public function actionHome()\n    {\n        return $this->render('home');\n    }\n}\n```\n\n\n## Chu trình Controller <span id=\"controller-lifecycle\"></span>\n\nKhi xử lý yêu cầu, [ứng dụng](structure-applications.md) sẽ khởi tạo controller\ndựa theo các yêu cầu tại [route](#routes). Controller sẽ được xử lý qua các chu trình sau để xử lý các yêu cầu:\n\n1. Phương thức [[yii\\base\\Controller::init()]] sẽ được gọi sau khi controller được khởi tạo và thiết lập các cấu hình.\n2. Controller sẽ tạo đối tượng action dựa trên các yêu cầu qua các định danh của action:\n   * Nếu định danh của action không được chỉ rõ , thì  [[yii\\base\\Controller::defaultAction|action mặc định]] sẽ được sử dụng.\n   * Nếu định danh của action được tìm thấy trong phương thức [[yii\\base\\Controller::actions()|action map]], thì một standalone action\n     sẽ được khởi tạo;\n   * Nếu định danh của action được tìm thấy và khớp với phương thức của action, thì một inline action sẽ được;\n   * Mặt khác hệ thống sẽ gửi ngoại lê [[yii\\base\\InvalidRouteException]] ra.\n3. Controller sẽ lần lượt được gọi tại phương thức `beforeAction()` trong ứng dụng, trong module (nếu controller\n   thuộc một module), và trong controller.\n   * Nếu một trong các phương thức không đợc gọi, các phần chưa được gọi trong phương thức `beforeAction()` sẽ được bỏ qua\n     và việc thực hiện action sẽ bị huỷ bỏ.\n   * Mặc định, mỗi phương thức `beforeAction()` sẽ được gán vào sự kiện `beforeAction` tới các action mà bạn cần xử lý.\n4. Controller thực hiện chạy action.\n   * Các tham số của action sẽ được phân tích và gán từ các yêu cầu xử lý.\n5. Controller sẽ thực hiện tuần tự gọi phương thức `afterAction()` trong Controller, module (nếu Controller\n   là module), và trong ứng dụng.\n   * Mặc định, mỗi phương thức `afterAction()` sẽ gọi tới một sự kiện `afterAction` tới các action mà bạn cần xử lý.\n6. Ứng dụng sẽ nhận kết quả từ các action và chuyển tới thành phần [response](runtime-responses.md).\n\n\n## Thực hành <span id=\"best-practices\"></span>\n\nVới mỗi ứng dụng được thiết kế tốt, thì Controllers thường rất gọn nhẹ, mỗi action chỉ chứa khá ít dòng code.\nNếu Controller trong ứng dụng của bản khá phức tạp, thì bạn nên cấu trúc lại và chuyển sang một lớp khác.\n\nSau đây gợi ý vài thủ thuật. Controllers\n\n* có thể truy cập dữ liệu từ các [request](runtime-requests.md);\n* gọi các phương thức từ [models](structure-models.md) và các thành phần khác cùng với dữ liệu được gửi;\n* dùng thành phần [views](structure-views.md) để gửi phản hồi;\n* KHÔNG NÊN xử lý dữ liệu - nên xử lý ở tầng [model](structure-models.md);\n* không nên nhúng mã HTML vào hoặc đoạn mã khác - mã này nên nhũng ở thành phần [views](structure-views.md).\n"
  },
  {
    "path": "docs/guide-vi/structure-entry-scripts.md",
    "content": "Entry Scripts\n=============\n\nEntry script là tiến trình đầu tiên của ứng dụng. Một ứng dụng (hoặc\nứng dụng Web hoặc ứng dụng console) đều có một entry script. Người dùng đầu cuối tạo các request tới entry script, entry script\nsẽ khởi tạo ứng dụng và nhanh chóng chuyển các yêu cầu tới chúng.\n\nEntry script dành cho các ứng dụng web cần được thiết lập ở dưới thư mục truy cập Web để người dùng cuối có thể truy cập\n. Những mục này thường được đặt tên là `index.php`, tuy nhiên có thể sử dụng các tên khác,\nđược cung cấp và có thể xác định bởi các máy chủ Web.\n\nEntry script cho các ứng dụng console thông thường được nằm ở [đường dẫn cơ sở](structure-applications.md)\ncủa ứng dụng và có tên là `yii` (cùng với hậu tố `.php`). Chúng được xây dựng để thực thi các ứng dụng console\nthông qua dòng lệnh `./yii <route> [arguments] [options]`.\n\nEntry scripts có chức năng chính như sau:\n\n* Khai báo các hằng số ở phạm vi toàn cục;\n* Đăng ký [Composer autoloader](https://getcomposer.org/doc/01-basic-usage.md#autoloading);\n* Tải các file class của [[Yii]];\n* Tải cấu hình ứng dụng;\n* Tạo và cấu hình các phiên bản [application](structure-applications.md);\n* Gọi phương thức [[yii\\base\\Application::run()]] để xử lý các request được gọi tới.\n\n\n## Ứng dụng Web <span id=\"web-applications\"></span>\n\nMã nguồn dưới đây là các dòng lệnh trong mục script trong [Mẫu ứng dụng cơ bản](start-installation.md).\n\n```php\n<?php\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n\n// register Composer autoloader\nrequire(__DIR__ . '/../vendor/autoload.php');\n\n// include Yii class file\nrequire(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');\n\n// load application configuration\n$config = require(__DIR__ . '/../config/web.php');\n\n// create, configure and run application\n(new yii\\web\\Application($config))->run();\n```\n\n\n## Ứng dụng Console(dòng lệnh) <span id=\"console-applications\"></span>\n\nTương tự, Mã nguồn dưới đây là các dòng lệnh trong mục script của ứng dụng console:\n\n```php\n#!/usr/bin/env php\n<?php\n/**\n * Yii console bootstrap file.\n *\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\n\n// fcgi doesn't have STDIN and STDOUT defined by default\ndefined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));\ndefined('STDOUT') or define('STDOUT', fopen('php://stdout', 'w'));\n\n// register Composer autoloader\nrequire(__DIR__ . '/vendor/autoload.php');\n\n// include Yii class file\nrequire(__DIR__ . '/vendor/yiisoft/yii2/Yii.php');\n\n// load application configuration\n$config = require(__DIR__ . '/config/console.php');\n\n$application = new yii\\console\\Application($config);\n$exitCode = $application->run();\nexit($exitCode);\n```\n\n\n## Định nghĩa các hằng số <span id=\"defining-constants\"></span>\n\nEntry scripts thích hợp để định nghĩa các hằng ở phạm vi toàn cục. Yii hỗ trợ 3 hằng số sau:\n\n* `YII_DEBUG`: xác định xem ứng dụng đang chay trong chế độ debug (gỡ lỗi). Khi ở chế độ debug, ứng dụng\n  sẽ log các thông tin, và sẽ thông báo chi tiết về các lỗi nếu có các ngoại lệ được gửi ra. Vì lý do này\n  , chế độ debug nên được dùng thường xuyên trong quá trình xây dựng ứng dụng. Giá trị mặc định của hằng `YII_DEBUG` là false.\n* `YII_ENV`: xác định thông tin về môi trường của ứng dụng đang chạy (sản phẩm hay đang phát triển). Điều này sẽ mô tả chi tiết trong phần\n  [Cấu hình](concept-configurations.md#environment-constants). Giá trị mặc định của hằng số `YII_ENV` là `'prod'`, có nghĩa là ứng dụng đang chạy là phiển bản sản phẩm\n  đã phát hành.\n* `YII_ENABLE_ERROR_HANDLER`: mô tả nơi cho phép được giữ (handler) các lỗi được cung cấp bởi Yii. Giá trị mặc đình của hằng\n số là true.\n\nKhi định nghĩa các hằng số, chúng ta thường sử dụng đoạn mã như sau:\n\n```php\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\n```\n\nKhai báo trên tương đương với đoạn code sau:\n\n```php\nif (!defined('YII_DEBUG')) {\n    define('YII_DEBUG', true);\n}\n```\n\nTa thấy đoạn code trên ngắn gọi và dễ hiểu hơn nhiều.\n\nViệc định nghĩa các hằng số nên được thực hiện ở phần đầu của entry script để các hằng số này có thể được gọi\nở những file php khác.\n"
  },
  {
    "path": "docs/guide-vi/structure-models.md",
    "content": "Model\n======\n\nModel là phần trong mô hình [MVC](https://vi.wikipedia.org/wiki/MVC).\nLà đối tượng đại diện cho phần dữ liệu, phương thức xử lý và nghiệp vụ logic.\n\nBạn có thể tạo mới các lớp model bằng việc kế thừa từ lớp [[yii\\base\\Model]] hoặc các lớp con của nó. Lớp cơ sở\n[[yii\\base\\Model]] hỗ trợ nhiều tính năng như:\n\n* [Thuộc tính (Attributes)](#attributes): đại diện cho các dữ liệu nghiệp vụ và có thể truy cập như các thuộc tính\n  hoặc mảng các phần tử;\n* [Attribute labels](#attribute-labels): tên hiển thị cho các thuộc tính;\n* [Gán nhanh (Massive assignment)](#massive-assignment): hỗ trợ nhập dữ liệu cho thuộc tính trong một bước;\n* [Quy tắc xác nhận (Validation rules)](#validation-rules): khai báo các quy tắc và xác thực dữ liệu được nhập vào;\n* [Xuất dữ liệu (Data Exporting)](#data-exporting): cho phép xuất dữ liệu dưới dạng mảng hoặc tuỳ chọn khác.\n\nLớp `Model` thường dựa trên lớp để thực hiện chức năng nâng cao, chẳng hạn [Active Record](db-active-record.md).\nVui lòng tham khảo thêm tài liệu để biết thêm thông tin.\n\n> Lưu ý: Model của bạn không phải bắt buộc kế thừa từ lớp [[yii\\base\\Model]]. Tuy nhiên, vì Yii chứa nhiều thành phần\n  dựng lên và hỗ trợ cho [[yii\\base\\Model]], vì thế nó là lớp cơ sở cho các lớp Model.\n\n\n## Thuộc tính (Attribute) <span id=\"attributes\"></span>\n\nModel đại diện cho tầng xử lý nghiệp vụ và chứa các *thuộc tính*. Mỗi thuộc tính được truy cập toàn cục như phần tử của \nmodel. Phương thức [[yii\\base\\Model::attributes()]] sẽ mô tả các thuộc tính trong lớp model hiện có.\n\nBạn có thể truy cập vào thuộc tính như các phần tử của các đối tượng:\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// \"name\" là tên thuộc tính của ContactForm\n$model->name = 'example';\necho $model->name;\n```\n\nBạn có thể truy cập các thuộc tính như truy cập mảng các phần tử, nhờ sự hỗ trợ từ lớp\n[ArrayAccess](https://www.php.net/manual/en/class.arrayaccess.php) và [ArrayIterator](https://www.php.net/manual/en/class.arrayiterator.php)\nbởi [[yii\\base\\Model]]:\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// truy cập các thuộc tính như mảng các phần tử\n$model['name'] = 'example';\necho $model['name'];\n\n// iterate attributes\nforeach ($model as $name => $value) {\n    echo \"$name: $value\\n\";\n}\n```\n\n\n### Định nghĩa các thuộc tính <span id=\"defining-attributes\"></span>\n\nMặc định, nếu Model của bạn được kế thừa từ lớp [[yii\\base\\Model]], và tất cả các biến có phạm vi *toàn cục trong lớp*\n. Ví dụ, Model `ContactForm` sau có bốn thuộc tính là: `name`, `email`,\n`subject` và `body`. Model `ContactForm` dùng để nhận dữ liệu từ form HTML.\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\n\nclass ContactForm extends Model\n{\n    public $name;\n    public $email;\n    public $subject;\n    public $body;\n}\n```\n\n\nBạn có thể ghi đè phương thức [[yii\\base\\Model::attributes()]] để định nghĩa các thuộc tính theo các cách khác. Phương thức nên được trả\ntên của thuộc tính trong Model. Ví dụ, lớp [[yii\\db\\ActiveRecord]] trả về\ndanh sách tên của các cột liên quan tới các bảng trong CSDL như tên các thuộc tính. Bạn có thể ghi đè các phương thức như\n`__get()`, `__set()` để có thể truy cập các thuộc tính như các đối tượng thông thường.\n\n\n### Nhãn của thuộc tính <span id=\"attribute-labels\"></span>\n\nMỗi khi cần hiển thị giá trị hoặc nhận dữ liệu cho thuộc tính, bạn cần hiển thị nhãn tương ứng với các thuộc tính\n. Ví dụ, với thuộc tính `firstName`, bạn cần hiển thị nhãn `First Name`\nnhãn này sẽ thân thiện hơn khi hiển thị tới người dùng với việc nhập dữ liệu và hiện thông báo.\n\nBạn có thể lấy tên nhãn các thuộc tính quan việc gọi phương thức [[yii\\base\\Model::getAttributeLabel()]]. Ví dụ,\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// hiển thị \"Name\"\necho $model->getAttributeLabel('name');\n```\n\nMặc định, nhãn thuộc tính sẽ tự động tạo từ tên của thuộc tính.\nPhương thức [[yii\\base\\Model::generateAttributeLabel()]] sẽ tạo mới các nhãn cho các thuộc tính. Nó sẽ chuyển tên các biến thành các từ mới\nqua việc chuyển ký tự đầu tiên thành ký tự in hoa. Ví dụ, `username` thành `Username`,\nvà `firstName` thành `First Name`.\n\nNếu bạn không muốn việc tạo các nhản bằng cách tự động, bạn cần ghi đè phương thức [[yii\\base\\Model::attributeLabels()]]\nđể mô tả các thuộc tính. Chẳng hạn,\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\n\nclass ContactForm extends Model\n{\n    public $name;\n    public $email;\n    public $subject;\n    public $body;\n\n    public function attributeLabels()\n    {\n        return [\n            'name' => 'Tên liên hệ',\n            'email' => 'Địa chỉ email',\n            'subject' => 'Tiêu đề',\n            'body' => 'Nội dung',\n        ];\n    }\n}\n```\n\nVới ứng dụng cần hỗ trợ đa ngôn ngữ, bạn cần dịch lại nhãn của các thuộc tính. Xem trong phương thức\n[[yii\\base\\Model::attributeLabels()|attributeLabels()]] , như sau:\n\n```php\npublic function attributeLabels()\n{\n    return [\n        'name' => \\Yii::t('app', 'Tên liên hệ'),\n        'email' => \\Yii::t('app', 'Địa chỉ email'),\n        'subject' => \\Yii::t('app', 'Tiêu đề'),\n        'body' => \\Yii::t('app', 'Nội dung'),\n    ];\n}\n```\n\nBạn có thể gán nhãn cho các thuộc tính. Chẳng hạn, dựa vào [scenario](#scenarios)của Model\nđã được sử dụng , bạn có thể trả về các nhãn khác nhau cho các thuộc tính khác nhau.\n\n> Lưu ý: Chính xác rằng, nhãn của thuộc tính là một phần của [views](structure-views.md). Tuy nhiên việc khai báo các nhãn\n  vào Model thường rất tiện lợi, code dễ nhìn và tái sử dụng.\n\n\n## Kịch bản (Scenarios) <span id=\"scenarios\"></span>\n\nModel thường được sử dụng ở các *kịch bản* khác nhau  . Ví dụ, Model `User` dùng để xử lý việc đăng nhập,\nnhưng cũng có thể được dùng ở mục đăng ký. Ở các kịch bản khác nhau, Model có thể được dùng trong các nghiệp vụ\nvà xử lý logic khác nhau. Ví dụ,thuộc tính `email` có thể được yêu cầu trong mục đăng ký tài khoản mới,\nnhưng không được yêu cầu khi xử lý đăng nhập.\n\nMỗi Model sử dụng thuộc tính [[yii\\base\\Model::scenario]] để xử lý tuỳ theo kịch bản cần đợc dùng.\nMặc định, Model sẽ hỗ trợ kịch bản là `default`. Xem đoạn mã sau để hiểu 2 cách thiết lập kịch bản cho Model.\nsetting the scenario of a model:\n\n```php\n// kịch bản được thiết lập qua thuộc tính\n$model = new User;\n$model->scenario = User::SCENARIO_LOGIN;\n\n// kịch bản được thiết lập qua việc cấu hình khởi tạo\n$model = new User(['scenario' => User::SCENARIO_LOGIN]);\n```\n\nMặc định, các kịch bản được hỗ trợ bởi model được xác định qua [các nguyên tắc xác minh](#validation-rules) được\nmô tả ở Model. Tuy nhiên, bạn có thê tuỳ biến bằng cách ghi đè phương thức [[yii\\base\\Model::scenarios()]],\nnhư sau:\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass User extends ActiveRecord\n{\n    const SCENARIO_LOGIN = 'login';\n    const SCENARIO_REGISTER = 'register';\n\n    public function scenarios()\n    {\n        return [\n            self::SCENARIO_LOGIN => ['username', 'password'],\n            self::SCENARIO_REGISTER => ['username', 'email', 'password'],\n        ];\n    }\n}\n```\n\n> Lưu ý: Như phần trên và ví dụ vừa rồi, lớp Model được kế thừa từ lớp [[yii\\db\\ActiveRecord]]\n  bởi vì lớp [Active Record](db-active-record.md) thường được sử dụng nhiều kịch bản.\n\nPhương thức `scenarios()` trả về một mảng có chứa các khóa là tên các kịch bản và các giá trị tương ứng là các  \ndanh sách *thuộc tính được chọn*. An active attribute can be [massively assigned](#massive-assignment) và là đối tượng sẽ được\ndùng để [xác thực (validation)](#validation-rules). Chẳng hạn ở ví dụ trên, thuộc tính `username` và `password` sẽ được chọn\nở kịch bản `login`; còn ở kịch bản `register`, sẽ có thêm thuộc tính `email` ngoài 2 thuộc tính `username` và `password`.\n\nViệc triển khai phương thức `scenarios()` mặc định sẻ trả về các kịch bản tìm thấy trong phương thức\n[[yii\\base\\Model::rules()]]. Khi khi đè phương thức `scenarios()`, nếu bạn muốn khai báo các kịch bản mới, ngoài các kịch bản mặc định\nin addition to the default ones, bạn có thể viết mã như sau:\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass User extends ActiveRecord\n{\n    const SCENARIO_LOGIN = 'login';\n    const SCENARIO_REGISTER = 'register';\n\n    public function scenarios()\n    {\n        $scenarios = parent::scenarios();\n        $scenarios[self::SCENARIO_LOGIN] = ['username', 'password'];\n        $scenarios[self::SCENARIO_REGISTER] = ['username', 'email', 'password'];\n        return $scenarios;\n    }\n}\n```\n\nXây dựng các kịch bản được dùng vào việc [xác thực](#validation-rules) và [massive attribute assignment](#massive-assignment).\nTuy nhiên, bạn có thể dùng vào mục đích khác. Chẳng hạn, bạn có thể khai báo các [nhãn thuộc tính](#attribute-labels)\nkhác nhau được dựa trên kịch bản hiện tại.\n\n\n## Các quy tắc xác nhận (Validation Rules) <span id=\"validation-rules\"></span>\n\nKhi dữ liệu cho model được chuyển lên từ người dùng cuối, dữ liệu này cần được xác thực để chắc chắn rằng dữ liệu này là hợp lệ\n (được gọi là *quy tắc xác nhận*, có thể gọi *business rules*). Ví dụ, cho model `ContactForm`,\nbạn muốn tất cả các thuộc tính không được để trống và thuộc tính `email` phải là địa chỉ email hợp lệ.\nNếu các giá trị cho các thuộc tính không được thỏa mãn với các quy tắc xác nhận, các thông báo lỗi sẽ được\nđược hiển thị để giúp người dùng sửa lỗi.\n\nBạn có thể gọi phương thức [[yii\\base\\Model::validate()]] để xác thực các dữ liệu đã nhận. Phương thức sẽ dùng các quy tắc xác nhận\nđược khai báo ở phương thức [[yii\\base\\Model::rules()]] để xác thực mọi thuộc tính liên quan. Nếu không có lỗi nào tìm thấy\n, sẽ trả về giá trị `true`. Nếu không thì, phương thức sẽ giữ các thông báo lỗi tại thuộc tính [[yii\\base\\Model::errors]]\nvà trả kết quả`false`. Ví dụ,\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// gán các thuộc tính của model từ dữ liệu người dùng\n$model->attributes = \\Yii::$app->request->post('ContactForm');\n\nif ($model->validate()) {\n    // tất cả các dữ liệu nhập vào hợp lệ\n} else {\n    // xác nhận lỗi: biến $errors chứa mảng các nội dung thông báo lỗi\n    $errors = $model->errors;\n}\n```\n\n\nCác quy tắc xác nhận được gắn vào model, việc ghi đè phương thức [[yii\\base\\Model::rules()]] cùng với việc trả về\ncó chứa các thuộc tính an toàn cần được xác thực. Ví dụ sau đây sẽ cho thấy các quy tắc xác nhận được khai báo cho model\n`ContactForm`:\n\n```php\npublic function rules()\n{\n    return [\n        // the name, email, subject and body attributes are required\n        [['name', 'email', 'subject', 'body'], 'required'],\n\n        // the email attribute should be a valid email address\n        ['email', 'email'],\n    ];\n}\n```\n\nMỗi quy tắc được dùng để xác nhận một hoặc nhiều các thuộc tính, và một thuộc tính có thể được xác nhận một hoặc nhiều quy tắc.\nVui lòng tham khảo mục [Xác nhận đầu vào](input-validation.md) để biết thêm chi tiết về cách khai báo các quy tắc xác nhận.\n\nĐôi khi, bạn muốn các quy tắc chỉ được áp dụng chỉ trong một số [kịch bản](#scenarios). Để làm như vậy, bạn có thể\nthêm thông tin thuộc tính `on` ở mỗi quy tắc, giống như sau:\n\n```php\npublic function rules()\n{\n    return [\n        // thuộc tính username, email và password cần được nhập ở kịch bản \"register\"\n        [['username', 'email', 'password'], 'required', 'on' => self::SCENARIO_REGISTER],\n\n        // username và password cần được nhập ở kịch bản \"login\"\n        [['username', 'password'], 'required', 'on' => self::SCENARIO_LOGIN],\n    ];\n}\n```\n\nNếu bạn không chỉ định thuộc tính `on`, quy tắc sẽ áp dụng trong tất cả các kịch bản. Một quy tắc được gọi\nmột *quy tắc hoạt động* nếu nó được áp dụng với kịch bản hiện tại [[yii\\base\\Model::scenario|scenario]].\n\nMột thuộc tính được xác nhận nếu và chỉ nếu nó là thuộc tính được kích hoạt với khai báo tại phương thức `scenarios()` và\nđược liên kết với một hoặc nhiều quy tắc được khai báo ở phương thức `rules()`.\n\n\n## Gán nhanh (Massive Assignment) <span id=\"massive-assignment\"></span>\n\nGán nhanh là cách tiện lợi cho việc nhập dữ liệu vào model từ người dùng với một dòng mã.\nNó nhập vào các thuộc tính của model bằng việc gán dữ liệu nhập vào qua thuộc tính [[yii\\base\\Model::$attributes]]\n. 2 đoạn mã sau hoạt động giống nhau , cả 2 đều lấy dữ liệu trong form gửi lên từ người dùng \nvào các thuộc tính của model `ContactForm`. Nhanh gọn, cách trên, sẽ dùng gán nhanh, mã của bạn trông sạch và ít lỗi hơn cách sau đó:\n\n```php\n$model = new \\app\\models\\ContactForm;\n$model->attributes = \\Yii::$app->request->post('ContactForm');\n```\n\n```php\n$model = new \\app\\models\\ContactForm;\n$data = \\Yii::$app->request->post('ContactForm', []);\n$model->name = isset($data['name']) ? $data['name'] : null;\n$model->email = isset($data['email']) ? $data['email'] : null;\n$model->subject = isset($data['subject']) ? $data['subject'] : null;\n$model->body = isset($data['body']) ? $data['body'] : null;\n```\n\n\n### Thuộc tính an toàn (Safe Attributes) <span id=\"safe-attributes\"></span>\n\nGán nhanh chỉ gán dữ liệu cho những thuộc tính gọi là  *thuộc tính an toàn (safe attributes)* đó là các thuộc tính được liệt kê trong phương thức\n[[yii\\base\\Model::scenarios()]] cho thuộc tính [[yii\\base\\Model::scenario|scenario]] của model.\nChẳng hạn, nếu model `User` có các kịch bản mô tả như sau, tiếp đến kịch bản\n`login` đang được chọn, thì chỉ thuộc tính `username` và `password` có thể được gán nhanh. Bất kỳ các thuộc tính khác\nsẽ được giữ nguyên.\n\n```php\npublic function scenarios()\n{\n    return [\n        self::SCENARIO_LOGIN => ['username', 'password'],\n        self::SCENARIO_REGISTER => ['username', 'email', 'password'],\n    ];\n}\n```\n\n> Thông tin: Lý do việc gán nhanh chỉ gán dữ liệu cho các thuộc tính an toàn là bởi vì bạn muốn kiểm soát\n  những thuộc tính có thể được thay đổi bởi người dùng. Chẳng hạn, nếu model `User` \n  có thuộc tính `permission` nhằm xác định các quyền hạn của người dùng, bạn chỉ muốn\n  thuộc tính này chỉ được thay đổi bởi quản trị viên thông qua giao diện phụ trợ.\n\nBởi vì mặc định phương thức [[yii\\base\\Model::scenarios()]] sẽ trả về tất cả các kịch bản và thuộc tính\nnằm trong phương thức [[yii\\base\\Model::rules()]], nếu bạn không ghi đè phương thức này, có nghĩa là một thuộc tính là an toàn\nmiễn là có khai báo ở một trong các quy tắc xác nhận.\n\nVì lý do này, bí danh `safe` được đưa ra bạn có thể khai báo các thuộc tính an toàn\nmà không thực sự xác nhận nó. Chẳng hạn, các quy tắc sau đây khai báo thuộc tính `title`\nvà `description` là thuộc tính an toàn.\n\n```php\npublic function rules()\n{\n    return [\n        [['title', 'description'], 'safe'],\n    ];\n}\n```\n\n\n### Thuộc tính không an toàn (Unsafe Attributes) <span id=\"unsafe-attributes\"></span>\n\nNhư mô tả trên, khai báo phương thức [[yii\\base\\Model::scenarios()]] có 2 mục đích: liệt kê thuộc tính cần được xác nhận\n, và xác định các thuộc tính là an toàn. Trong một số trường hợp khác, bạn muốn xác nhận thuộc tính nhưng\nkhông muốn đánh dấu là an toàn. bạn có thể thực hiện bằng việc đặt dấu chấm than `!` vào tên thuộc tính\nkhi khai báo tại phương thức `scenarios()`, giốn như thuộc tính `secret` như sau:\n\n```php\npublic function scenarios()\n{\n    return [\n        self::SCENARIO_LOGIN => ['username', 'password', '!secret'],\n    ];\n}\n```\n\nKhi model đang ở kịch bản `login`, cả 3 thuộc tính sẽ được xác nhận. Tuy nhiên, chỉ có thuộc tính `username`\nvà `password` được gán nhanh. Để gán giá trị cho thuộc tính `secret`, bạn\ncần được gán trực tiếp như sau,\n\n```php\n$model->secret = $secret;\n```\n\nĐiều tương tự có thể được thực hiện trong phương thức `rules()`:\n\n```php\npublic function rules()\n{\n    return [\n        [['username', 'password', '!secret'], 'required', 'on' => 'login']\n    ];\n}\n```\n\nTrong trường hợp này các thuộc tính `username`, `password` và `secret` là yêu cầu nhập, nhưng thuộc tính `secret` phải cần được gán trực tiếp.\n\n\n## Xuất dữ liệu (Data Exporting) <span id=\"data-exporting\"></span>\n\nCác model thường được cần trích xuất ra các định dạng khác nhau. Chẳng hạn, bạn cần chuyển dữ liệu sang của\nmodels sang định dạng JSON hoặc Excel. Quá trình xuất có thể được chia nhỏ thành hai bước độc lập:\n\n- models cần được chuyển sang định dạng mảng;\n- các mảng cần được chuyển đổi thành các định dạng cần chuyển.\n\nBạn chỉ cần tập trung vào bước đầu tiên, bởi vì bước thứ 2 có thể được thực hiện bởi các trình định dạng dữ liệu\n, chẳng hạn như [[yii\\web\\JsonResponseFormatter]].\n\nCác đơn giản nhất để chuyển đổi model sang dạng mảng là sử dụng thuộc tính [[yii\\base\\Model::$attributes]].\nFor example,\n\n```php\n$post = \\app\\models\\Post::findOne(100);\n$array = $post->attributes;\n```\n\nBởi mặc định, thuộc tính [[yii\\base\\Model::$attributes]] sẽ trả về các giá trị của *tất cả* các thuộc tính\nđược khai báo trong phương thức [[yii\\base\\Model::attributes()]].\n\nCòn một cách linh hoạt và tiện lợi hơn trong việc chuyển đổi model sang định dạng mảng là sử dụng phương thức [[yii\\base\\Model::toArray()]]\n. Cách chuyển đổi cũng tương tự như trong cách của thuộc tính [[yii\\base\\Model::$attributes]]. Tuy nhiên, nó cho phép bạn chọn các dữ liệu\n, được gọi là *fields*, được đặt trong mảng kết quả và chúng được định dạng thế nào.\nTrong thực tế, đó là cách trích xuất mặc định của các model ở việc phát triển các dịch vụ RESTful Web, như được mô tả trong\nmục [Response Formatting](rest-response-formatting.md).\n\n\n### Các trường (Fields) <span id=\"fields\"></span>\n\nMột trường đơn giản là tên của thành phần thu được nằm trong mảng khi gọi phương thức [[yii\\base\\Model::toArray()]]\ncủa model.\n\nMặc định, tên trường sẽ tương đương với tên thuộc tính. Tuy nhiên, bạn có thể thay đổi bằng việc ghi đè\nqua phương thức [[yii\\base\\Model::fields()|fields()]] và/hoặc phương thức [[yii\\base\\Model::extraFields()|extraFields()]]. Cả 2 phương thức\ntrả về danh sách các khai báo trường. Các trường được định nghĩa bởi phương thức `fields()` là các trường mặc định, nghĩa là phương thức \n`toArray()` sẽ trả về những trường mặc định. Phương thức `extraFields()` sẽ khai báo thêm các trường bổ sung có thể được trả về\nbởi phương thức `toArray()` miễn là bạn chỉ định chugns qua tham số `$expand`. Chẳng hạn,\nđoạn mã sau sẽ trả về các trường được định nghĩa trong phương thức `fields()` và 2 trường `prettyName` và `fullAddress`\nnếu chúng được định nghĩa trong phương thức `extraFields()`.\n\n```php\n$array = $model->toArray([], ['prettyName', 'fullAddress']);\n```\n\nBạn có thể ghi đè phương thức `fields()` để thêm, xóa, cập nhật hoặc định nghĩa lại các trường. Phương thức `fields()`\nsẽ trả về dữ liệu dạng mảng. Mảng này có các khóa là tên các trường, và các giá trị của mảng tương ứng\nvới các trường đã định nghĩa giá trị có thể là tên các thuộc tính/biến hoặc một hàm trả về các giá trị trường tương ứng\n. Trong trường hợp đặc biệt khi tên trường giống với tên thuộc tính xác định của nó, bạn có thể bỏ qua khóa mảng. Ví dụ,\n\n```php\n// liệt kê rõ ràng các trường, sử dụng tốt nhất khi bạn nắm được các thay đổi\n// trong bản CSDL hoặc các thuộc tính của model, không gây ra sự thay đổi của trường (để giữ tương thích với API).\npublic function fields()\n{\n    return [\n        // tên trường giống với tên thuộc tính\n        'id',\n\n        // tên trường là \"email\", tương ứng với tên thuộc tính là \"email_address\"\n        'email' => 'email_address',\n\n        // tên trường là \"name\", giá trị được định nghĩa bởi hàm\n        'name' => function () {\n            return $this->first_name . ' ' . $this->last_name;\n        },\n    ];\n}\n\n// lọc ra một số trường, nên sử dụng khi bạn muốn kế thừa các trường\n// thêm vào blacklist một số trường không cần thiết.\npublic function fields()\n{\n    $fields = parent::fields();\n\n    // remove fields that contain sensitive information\n    unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);\n\n    return $fields;\n}\n```\n\n> Cảnh báo: Bởi vì theo mặc định tất cả các thuộc tính của model sẽ liệt kê trong mảng trích xuất, bạn nên\n> kiểm tra dữ liệu của bạn để chắc chắn rằng chúng không chứa các thông tin không cần thiết. Nếu có thông tin như vậy,\n> bạn nên ghi đè phương thức `fields()` để lọc chúng ra. Tại ví dụ trên, chúng ta chọn\n> các trường để lọc ra là `auth_key`, `password_hash` và `password_reset_token`.\n\n\n## Bài thực hành <span id=\"best-practices\"></span>\n\nCác model là phần trung tâm đại diện cho tầng dữ liệu, chứa các quy tắc và logic. Model thường được tái sử dụng\ntại một số nơi khác nhau. Với một ứng dụng được thiết kế tốt, thông thường các model được chú trọng hơn \n[controllers](structure-controllers.md).\n\nTổng hợp mục, models\n\n* có thể chứa các thuộc tính đại diện cho tầng dữ liệu (business data);\n* có thể chứa các quy tắc xác nhận để đảm bảo tính hợp lệ và tính toàn vẹn của dữ liệu;;\n* có thể chứa các phương thức tại tầng logic (business logic);\n* không nên trực tiếp xử lý các yêu cầu, session, hoặc bất cứ dữ liệu môi trường. Những dữ liệu này nên được tiến hành xử lý\n  bởi [controllers](structure-controllers.md) vào model;\n* tránh việc nhúng mã HTML hoặc các dữ liệu hiển thị - mã này nên được đặt tại [views](structure-views.md);\n* tránh có quá nhiều kịch bản [scenarios](#scenarios) trong một model.\n\nBạn cần có sự xem xét các đề nghị trên mỗi khi bạn triển khai hệ thống lớn và phức tạp.\nTrong các hệ thống này, cácmodel cần được chú trọng bởi vì chúng được sử dụng ở nhiều nơi và có thể chứa nhiều các quy tắc\nvà các xử lý nghiệp vụ. Điều này có sự ảnh hưởng tại tiến trình bảo trì mỗi thay đổi mã của bạn\ncó thể ảnh hưởng tới nhiều vị trí khác nhau. Để mã code của bạn dễ được bảo trì hơn,\nbạn có thể được thực hiện các chiến lược sau:\n\n* Định nghĩa tập các lớp model cơ sở (base model) lớp này được chia sẻ qua các [ứng dụng](structure-applications.md) hoặc\n  [modules](structure-modules.md) khác nhau. Các model này có thể chứa tập các quy tắc và logic có thể được\n  dùng rộng rãi ở các lớp cần được sử dụng.\n* Tại mỗi [ứng dụng](structure-applications.md) hoặc [module](structure-modules.md) có dùng model,\n  ta định nghĩa lớp khung model bằng việc kế thừa từ lớp model cơ sở. Lớp khung model này\n  có thể chứa các quy tắc logic được mô tả cụ thể chi ứng dụng hoặc module này.\n\nVí dụ, với [Mẫu dự án Advanced](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md), bạn có thể định nghĩa lớp cơ sở model\nlà `common\\models\\Post`. Tiếp đến tại ứng dụng front end, bạn định nghĩa lớp khung là\n`frontend\\models\\Post` lớp này kế thừa từ lớp `common\\models\\Post`. Và tương tự cho ứng dụng back end,\nbạn định nghĩa model `backend\\models\\Post`. Với cách giải quyết này, bạn sẽ chắc chắn rằng mã của bạn tại model `frontend\\models\\Post`\nchỉ dùng cho ứng dụng front end, và nếu bạn thực hiện với bất kỳ thay đổi nào, bạn không cần lo lắng về\nviệc thay đổi này có ảnh hưởng tới ứng dụng back end.\n"
  },
  {
    "path": "docs/guide-vi/structure-modules.md",
    "content": "Modules\n=======\n\nModules là các đơn vị phần mềm độc lập bao gồm các [models](structure-models.md), [views](structure-views.md),\n[controllers](structure-controllers.md), và các thành phần hỗ trợ khác. Người dùng cuối có thể truy cập các controller\ncủa module khi các controller đã được cài đặt trong [application](structure-applications.md). Vì những lý do này, thường được xem là\nứng dụng nhỏ. Modules khác với [applications](structure-applications.md) trong đó các mô-đun không thể tự được triển khai\nvà phải nằm trong các ứng dụng\n\n\n## Tạo Modules <span id=\"creating-modules\"></span>\n\nMột module được tổ chức dưới dạng như một thư mục được gọi là [[yii\\base\\Module::basePath|đường dẫn cơ sở]] của module.\nTrong thư mục, còn có các thư mục con, như là `controllers`, `models`, `views`, những thư mục này chứa các controllers,\nmodels, views, và các đoạn mã khác, giống như trong một ứng dụng. Ví dụ sau đây cho thấy nội dung trong một module:\n\n```\nforum/\n    Module.php                   tập tin lớp module\n    controllers/                 chứa các tệp controller\n        DefaultController.php    lớp controller mặc định\n    models/                      chứa các tệp model\n    views/                       chứa các tệp view và layout\n        layouts/                 chứa các tệp layout của view\n        default/                 chứa các file view cho DefaultController\n            index.php            tập tin index cho view\n```\n\n\n### Lớp Module <span id=\"module-classes\"></span>\n\nMỗi module nên có một lớp module cơ sở được kế thừa từ lớp [[yii\\base\\Module]]. Lớp nên được đặt ngay dưới\nmodule [[yii\\base\\Module::basePath|base path]] và nên được [autoloadable](concept-autoloading.md).\nKhi một module đang được truy cập, một phiên bản của lớp module tương ứng sẽ được tạo.\nGiống như các [application instances](structure-applications.md), các phiên bản của module được dùng để chia sẽ dữ liệu và các component\ncho mã trong các module.\n\nVí dụ sau cho thấy lớp module trông như thế nào:\n\n```php\nnamespace app\\modules\\forum;\n\nclass Module extends \\yii\\base\\Module\n{\n    public function init()\n    {\n        parent::init();\n\n        $this->params['foo'] = 'bar';\n        // ...  các đoạn mã khởi tạo khác ...\n    }\n}\n```\n\nNếu phương thức `init()` có chứa một số đoạn mã khởi tạo cho các thuộc tính module, bạn có thể lưu chúng theo các điều khoản\ncủa mục [cấu hình](concept-configurations.md) và tải chúng theo đoạn mã sau ở phương thức `init()`:\n\n```php\npublic function init()\n{\n    parent::init();\n    // khởi tạo module với các cấu hình được tải từ file config.php\n    \\Yii::configure($this, require __DIR__ . '/config.php');\n}\n```\n\nnơi tập tin cấu hình `config.php` có thể chứa nội dung sau, tương tự như trong\n[cấu hình ứng dụng](structure-applications.md#application-configurations).\n\n```php\n<?php\nreturn [\n    'components' => [\n        // danh sách các cấu hình thành phần\n    ],\n    'params' => [\n        // danh sách các parameter\n    ],\n];\n```\n\n\n### Controller trong Modules <span id=\"controllers-in-modules\"></span>\n\nKhi tạo controllers trong module, các quy ước đặt tên lớp controller theo `controllers` với\nkhông gian tên phụ của không gian tên của lớp module. Điều này cũng có nghĩa là các tệp lớp controller nên được đặt trong\nđường dẫn `controllers` nằm trong module [[yii\\base\\Module::basePath|đường dẫn cơ sở]].\nVí dụ, để tạo controller `post` nằm trong module `forum` thể hiện trong mục con, bạn nên\nkhai báo lớp controller như sau:\n\n```php\nnamespace app\\modules\\forum\\controllers;\n\nuse yii\\web\\Controller;\n\nclass PostController extends Controller\n{\n    // ...\n}\n```\n\nBạn có thể tùy chỉnh không gian tên của lớp controller bằng việc cấu hình thuộc tính [[yii\\base\\Module::controllerNamespace]]\n. Trong trường hợp một số controllers nằm ngoài không gian tên, bạn có thể làm cho chúng có thể truy cập bằng cách cấu hình\nqua thuộc tính [[yii\\base\\Module::controllerMap]], tương tự như [điều bạn làm cho ứng dụng](structure-applications.md#controller-map).\n\n\n### Views trong Modules <span id=\"views-in-modules\"></span>\n\nViews trong module nên đặt tại đường dẫn là `views` nằm trong module [[yii\\base\\Module::basePath|base path]].\nĐối với các views được xuất bản bởi controller trong module, chúng nên được đặt trong thư mục `views/ControllerID`,\nvới `ControllerID` đề cập đến [controller ID](structure-controllers.md#routes). Ví dụ, nếu lớp\ncontroller là `PostController`, thì đường dẫn sẽ là `views/post` nằm trong\n[[yii\\base\\Module::basePath|đường dẫn cơ sở]] của module.\n\nMột module có thể chỉ định [layout](structure-views.md#layouts) được áp dụng để views được xuất bản qua các controller trong module\n. Các layout nên mặc định đặt trong đường dẫn là `views/layouts`, và bạn nên cấu hình\nthuộc tính [[yii\\base\\Module::layout]] để đề cập tới tên của layout. Nếu bạn không cấu hình thuộc tính `layout`,\nthì layout trong ứng dụng sẽ được dùng thay thế.\n\n\n### Console commands trong Modules <span id=\"console-commands-in-modules\"></span>\n\nModule có thể được khai báo các command, sẽ có sẵn thông qua chế độ [Console](tutorial-console.md).\n\nĐể tiện ích command nhìn thấy các command của bạn, bạn sẽ cần thay đổi thuộc tính [[yii\\base\\Module::controllerNamespace]]\n, khi Yii được thực thi ở chế độ dòng lệnh, và trỏ nó vào không gian tên của các command.\n\nMột cách để đạt điều trên là kiểm tra các loại instance của ứng dụng Yii trong phương thức `init()` của module:\n\n```php\npublic function init()\n{\n    parent::init();\n    if (Yii::$app instanceof \\yii\\console\\Application) {\n        $this->controllerNamespace = 'app\\modules\\forum\\commands';\n    }\n}\n```\n\nCác lệnh của bạn sau đó sẽ có sẵn từ dòng lệnh bằng cách sử dụng route sau:\n\n```\nyii <module_id>/<command>/<sub_command>\n```\n\n## Sử dụng Module <span id=\"using-modules\"></span>\n\nĐể sử dụng module trong ứng dụng, chỉ cần cấu hình ứng dụng bằng cách liệt kê các module trong thuộc tính\n[[yii\\base\\Application::modules|modules]] của ứng dụng. Đoạn mã sau đây trong mục\n[cấu hình ứng dụng](structure-applications.md#application-configurations) sử dụng từ khóa `forum` module:\n\n```php\n[\n    'modules' => [\n        'forum' => [\n            'class' => 'app\\modules\\forum\\Module',\n            // ... các cấu hình khác cho module ...\n        ],\n    ],\n]\n```\n\nThuộc tính [[yii\\base\\Application::modules|modules]] nhận một mảng cấu hình cho các module. Mỗi key của mảng\nđại diện cho *định danh module* trong đó xác định duy nhất module trong số tất cả các module trong ứng dụng, và giá trị\ntương ứng của mảng là các [cấu hình](concept-configurations.md) cho việc tạo module.\n\n\n### Routes <span id=\"routes\"></span>\n\nGiống như truy cập các controller trong ứng dụng, [routes](structure-controllers.md#routes) được dùng để truy cập tới các\ncontroller trong module. Một route cho controller trong một module phải bắt đầu bằng định danh module theo sau là\n[định danh controller](structure-controllers.md#controller-ids) và [định danh action](structure-controllers.md#action-ids).\nVí dụ, nếu một ứng dụng sử dụng module là `forum`, sau đó route\n`forum/post/index` sẽ đại diện cho action `index` của controller `post` nằm trong module. Nếu route\nchỉ có chứa định danh module, và thuộc tính [[yii\\base\\Module::defaultRoute]], sẽ mặc định là `default`,\nsẽ xác định cho controller/action nào được sử dụng. Điều này có nghĩa là route `forum` sẽ đại diện cho controller `default`\ntrong module `forum`.\n\nCác quy tắc quản lý URL cho các module nên được thêm vào trước phương thức [[yii\\web\\UrlManager::parseRequest()]] được kích hoạt. Có nghĩa là \nthực hiện nó trong phương thức `init()` của module sẽ không hoạt động vì module sẽ được khởi tạo khi các routes được xử lý. Vì vậy, các quy tắc\nnên được thêm vào ở [giai đoạn bootstrap](structure-extensions.md#bootstrapping-classes). Đây cũng là cách hay để\nđể nhóm các quy tắc URL của module bằng [[\\yii\\web\\GroupUrlRule]].  \n\nTrong trường hợp một module được sử dụng để [version API](rest-versioning.md), quy tắc URL của nó nên được thêm trực tiếp vào mục `urlManager` \ncủa cấu hình ứng dụng.\n\n\n### Truy cập vào Module <span id=\"accessing-modules\"></span>\n\nTrong một module, bạn có thể cần lấy thông tin instance của [lớp module](#module-classes) do vậy bạn có thể truy cập qua\nđịnh danh của module, tham số của module, module components, vv. Bạn có thể làm như vậy bằng cách sử dụng câu lệnh sau:\n\n```php\n$module = MyModuleClass::getInstance();\n```\n\ntrong đó `MyModuleClass` đề cập đến tên của lớp module mà bạn đang quan tâm. Phương thức `getInstance()` method\nsẽ trả về instance được yêu cầu của lớp module. Nếu module không được yêu cầu, phương thức sẽ trả về là\n`null`. Lưu ý rằng bạn không muốn tự tạo một instance mới của lớp module vì nó sẽ khác với phiên bản được tạo bởi\nYii để đáp ứng các yêu cầu.\n\n> Thông tin: Khi phát triển một module, bạn không nên cho rằng module sẽ có định danh cố định. Điều này là do một module\n  có thể liên kế tới một định danh tùy ý khi được sử dụng trong một ứng dụng hoặc một module khác. Để có một định danh\n  module, bạn nên sử dụng cách tiếp cận ở trên để lấy một instance của module trước, và nhận thông tin định danh qua\n  `$module->id`.\n\nBạn cũng có thể truy cập vào instance của module bằng các phương pháp sau:\n\n```php\n// lấy module con có định danh là \"forum\"\n$module = \\Yii::$app->getModule('forum');\n\n// lấy module mà controller đang yêu cầu\n$module = \\Yii::$app->controller->module;\n```\n\nCách tiếp cận đầu tiên chỉ hữu ích khi bạn biết rõ định danh của module, trong khi cách tiếp cận thứ hai\nđược sử dụng tốt nhất khi bạn biết về các controller đang được yêu cầu.\n\nMột khi bạn có các instance module, bạn có thể truy cập các tham số và các components đã đăng ký với module. Ví dụ,\n\n```php\n$maxPostCount = $module->params['maxPostCount'];\n```\n\n\n### Tải trước các module <span id=\"bootstrapping-modules\"></span>\n\nMột số module có thể cần phải được chạy cho mọi yêu cầu. Module [[yii\\debug\\Module|debug]] là một ví dụ như vậy.\nĐể làm được như vậy, liệt kê ra các định danh của modules trong thuộc tính [[yii\\base\\Application::bootstrap|bootstrap]] của ứng dụng.\n\nVí dụ, cấu hình ứng dụng sau đây đảm bảo module `debug` luôn luôn được tải:\n\n```php\n[\n    'bootstrap' => [\n        'debug',\n    ],\n\n    'modules' => [\n        'debug' => 'yii\\debug\\Module',\n    ],\n]\n```\n\n\n## Các module con <span id=\"nested-modules\"></span>\n\nCác module có thể được lồng vào nhau với không giới hạn. Đó là, một module có thể chứa một module khác và module này có thể chứa\nmột module khác. Chúng tôi gọi module đầu là *module cha mẹ* trong khi module sau gọi là *module con*. Các module con phải được khai báo trong\nthuộc tính [[yii\\base\\Module::modules|modules]] của các module cha mẹ. Ví dụ,\n\n```php\nnamespace app\\modules\\forum;\n\nclass Module extends \\yii\\base\\Module\n{\n    public function init()\n    {\n        parent::init();\n\n        $this->modules = [\n            'admin' => [\n                // bạn nên xem xét sử dụng một không gian tên ngắn hơn ở đây!\n                'class' => 'app\\modules\\forum\\modules\\admin\\Module',\n            ],\n        ];\n    }\n}\n```\n\nĐối với controller trong các module con, các route của nó nên bao gồm các định danh của các module cha.\nVí dụ, với route `forum/admin/dashboard/index` đại diện cho action `index` của controller`dashboard` \nbên trong module `admin` đó là một module con của module`forum`.\n\n> Thông tin: Phương thức [[yii\\base\\Module::getModule()|getModule()]] chỉ trả về mô đun con trực tiếp thuộc về nó\n. Thuộc tính [[yii\\base\\Application::loadedModules]] giữ một danh sách các module được tải, bao gồm cả\nmodule trực tiếp và module lồng nhau, được đánh dầu bởi tên class.\n\n## Truy cập các thành phần từ bên trong các module\n\nKể từ phiên bản 2.0.13 modules hỗ trợ [tree traversal](concept-service-locator.md#tree-traversal). Điều này cho phép các nhà phát triển\nxây dựng module tham chiếu tới các thành phần (ứng dụng) thông qua service locator nằm trong module.\nĐiều này có nghĩa là nó được sử dụng `$module->get('db')` hơn là `Yii::$app->get('db')`.\nNgười dùng module có thể chỉ định thành phần cụ thể được sử dụng cho các module trong trường hợp thành phần (cấu hình) khác\nđược yêu cầu.\n\nVí dụ, xem xét một phần cấu hình ứng dụng này:\n\n```php\n'components' => [\n    'db' => [\n        'tablePrefix' => 'main_',\n        'class' => Connection::class,\n        'enableQueryCache' => false\n    ],\n],\n'modules' => [\n    'mymodule' => [\n        'components' => [\n            'db' => [\n                'tablePrefix' => 'module_',\n                'class' => Connection::class\n            ],\n        ],\n    ],\n],\n```\n\nCác bảng cơ sở dữ liệu ứng dụng sẽ có tiền tố là `main_`, trong khi đó các bảng trong module có tiền tố là `module_`.\nLưu ý rằng cấu hình ở trên không được hợp nhất; thành phần modules cho ví dụ trên sẽ có các bộ đệm truy vấn được bật \nvì nó là giá trị mặc định.\n\n## Bài thực hành <span id=\"best-practices\"></span>\n\nCác module được sử dụng tốt nhất trong các ứng dụng lớn có tính năng có thể được chia thành nhiều nhóm, mỗi bộ bao gồm một tập hợp các \ntính năng liên quan chặt chẽ. Mỗi nhóm tính năng như vậy có thể được phát triển dưới dạng một module được phát triển và duy trì bởi\nmột nhà phát triển hoặc nhóm cụ thể.\n\nCác module cũng là một cách tốt để sử dụng lại mã ở cấp độ nhóm tính năng. Một số tính năng thường được sử dụng,chẳng hạn như \nquản lý người dùng, quản lý bình luận, tất cả đều có thể được phát triển về mặt module để chúng \ncó thể được tái sử dụng dễ dàng trong các dự án trong tương lai.\n"
  },
  {
    "path": "docs/guide-vi/structure-overview.md",
    "content": "Tổng quan về kiến trúc ứng dụng\n========\n\nCác ứng dụng Yii được tổ chức dựa theo mẫu thiết kế [model-view-controller (MVC)](https://vi.wikipedia.org/wiki/MVC)\n. [Models](structure-models.md) chứa nghiệp vụ logic, truy xuất database và định nghĩa các quy tắc xác thực dữ liệu; [views](structure-views.md)\nđảm nhận việc hiển thị thôn tin của model; và [controllers](structure-controllers.md) có nhiệm vụ điều hướng các yêu cầu và chuyển các tương tác giữa\n[models](structure-models.md) và [views](structure-views.md).\n\nNgoài mô hình MVC, ứng dụng Yii có những phần phần sau đây:\n\n* [entry scripts](structure-entry-scripts.md): là file đầu tiên chứa các mã nguồn để tiếp nhận các request của người dùng.\n  Thành phần này có trách nhiệm bắt đầu về chu trình xử lý các yêu cầu trong ứng dụng.\n* [ứng dụng](structure-applications.md): là đối tượng có phạm vi truy cập toàn cục giúp quản lý các thành phần trong ứng dụng\n  và điều hướng chúng để thực hiện các yêu cầu.\n* [thành phần](structure-application-components.md): là đối tượng được đăng ký với ứng dụng\n  và cung cấp những dịch vụ cho các yêu cầu xử lý .\n* [modules](structure-modules.md): là những gói có chứa mô hình MVC hoàn chỉnh.\n  Một ứng dụng có thể được tổ chức dưới dạng nhiều module.\n* [filters](structure-filters.md): chứa những mã nguồn cần được gọi trước và sau việc xử lý của từng yêu cầu của bộ điều khiển\n  handling of each request by controllers.\n* [widgets](structure-widgets.md): các đối tượng được nhúng vào [views](structure-views.md). Các widget có thể chứa các nghiệp vụ logic\n  và có thể tái sử dụng ở những view khác.\n\nMô hình sau mô tả cấu trúc ứng dụng ở dạng tĩnh:\n\n![Static Structure of Application](images/application-structure.png)\n"
  },
  {
    "path": "docs/guide-vi/structure-views.md",
    "content": "Views (Giao diện)\n=====\n\nViews là phần trong mô hình [MVC](https://vi.wikipedia.org/wiki/MVC).\nThành phần này chịu trách nhiệm chính trong việc hiển thị dữ liệu tới người dùng. Tại ứng dụng Web, views thường được tạo\ncùng với các *bản mẫu giao diện (view template)* là những file kịch bản của PHP có chứa các mã HTML và mã PHP.\nCác file giao diện được quản lý bởi [[yii\\web\\View|view]] [là thành phần ứng dụng](structure-application-components.md) thành phần này có chứa các phương thức chung\nđể các giao diện được đóng gói và xuất bản. Để cho đơn giản, chúng ta thường gọi các bản mẫu giao diện hoặc các file bản mẫu giao diện như một giao diện.\n\n\n## Tạo mới Views <span id=\"creating-views\"></span>\n\nNhư đã nêu trên, một view đơn giản là một kịch bản PHP chưa hỗn hợp các mã HTML và PHP. Đoạn mã sau là một view\nđược thiết lập cho form đăng nhập. Như bạn thấy dưới, các mã PHP dùng trong việc sinh ra các nội dung động, chẳng hạn như tiêu đề\nvà các nội dung form, còn các mã HTML tổ chức thành các trang nội dung HTML.\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n/**\n * @var \\yii\\web\\View $this\n * @var \\yii\\widgets\\ActiveForm $form\n * @var \\app\\models\\LoginForm $model\n */\n\n$this->title = 'Login';\n?>\n<h1><?= Html::encode($this->title) ?></h1>\n\n<p>Please fill out the following fields to login:</p>\n\n<?php $form = ActiveForm::begin(); ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n    <?= Html::submitButton('Login') ?>\n<?php ActiveForm::end(); ?>\n```\n\nTại mỗi view, bạn có thể truy cập biến `$this` tương ứng với class [[yii\\web\\View|thành phân giao diện]] là để quản lý và xuất bản các bản mẫu giao diện.\n\nNgoài biến `$this`, ta cũng có thể khai báo thêm các biến ở trong view, như biến `$model` tại ví dụ trên\n. Những biến này có chứa dữ liệu đã được *thêm* vào view bởi [controllers](structure-controllers.md)\nhoặc các đối tượng khác được cho vào từ việc [xuất bản view](#rendering-views).\n\n> Mẹo: Các biến được định nghĩa trong ở các dòng comment ở đầu mục của view thường được quản lý bởi các\n  IDEs. Định nghĩa như vậy sẽ có các thông tin về tài liệu mô tả cho views.\n\n\n### Bảo mật dữ liệu (Security) <span id=\"security\"></span>\n\nKhi tạo views được sinh bởi các trang HTML, điều quan trọng là bạn mã hóa và/hoặc lọc dữ liệu đến từ người dùng\ncuối trước khi hiển thị lên. Nếu không thì, ứng dụng của bạn có thể bị tấn công bởi\n[cross-site scripting](https://vi.wikipedia.org/wiki/Cross-site_scripting).\n\nĐể hiển thị các nội dung thuần, hãy encode trước bằng việc gọi phương thức [[yii\\helpers\\Html::encode()]]. Ví dụ, đoạn mã sau sẽ\nencodes thông tin user name trước khi được hiển thị:\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n\n<div class=\"username\">\n    <?= Html::encode($user->name) ?>\n</div>\n```\n\nĐể hiển thị nội dung HTML, sử dụng lớp [[yii\\helpers\\HtmlPurifier]] để lọc ra các nội dung trước. chẳng hạn, đoạn mã sau sẽ\nlọc nội dung bài viết trước khi hiển thị ra:\n\n```php\n<?php\nuse yii\\helpers\\HtmlPurifier;\n?>\n\n<div class=\"post\">\n    <?= HtmlPurifier::process($post->text) ?>\n</div>\n```\n\n> Mẹo: Khi dùng trích xuất thông tin an toàn từ đối tượng HTMLPurifier là cách tốt, tuy nhiên nó không được nhanh. Bạn nên xem xét sử dụng kỹ thuật\n  [caching](caching-overview.md) cho việc lưu nội dung nếu ứng dụng có yêu cầu hiệu suất cao.\n\n\n### Tổ chức Views <span id=\"organizing-views\"></span>\n\nCũng giống như [controllers](structure-controllers.md) và [models](structure-models.md), có các quy tắc để quản lý các views.\n\n* Với views được xuất bản từ controller, mặc định các view được đặt trong thư mục `@app/views/ControllerID`,\n  với `ControllerID` tương ứng với [controller ID](structure-controllers.md#routes). Chẳng hạn, nếu\n  tên class của controller là `PostController`, đường dẫn sẽ là `@app/views/post`; nếu controller là `PostCommentController`,\n  thì đường dẫn sẽ là `@app/views/post-comment`. Trong trường hợp controller nằm trong module, thì đường dẫn\n  sẽ là `views/ControllerID` và đường dẫn nằm trong [[yii\\base\\Module::basePath|đường dẫn module]].\n* Với views được xuất bản từ các [widget](structure-widgets.md), mặc định chúng được đặt trong đường dẫn `WidgetPath/views`\n  , `WidgetPath` là tên đường dẫn có chứa các lớp của widget.\n* Với views được xuất bản từ các đối tượng khác, thì bạn nên tuân theo quy ước tương tự như đối với các tiện ích widgets.\n\nBạn có thể tùy biến giá trị mặc định của đường dẫn bằng việc ghi đè phương thức [[yii\\base\\ViewContextInterface::getViewPath()]]\ncủa controllers hoặc các widgets.\n\n\n## Xuất bản View (Rendering )<span id=\"rendering-views\"></span>\n\nView được xuất bản tại [controllers](structure-controllers.md), [widgets](structure-widgets.md), hoặc những nơi khác\nkhi được gọi phương thức xuất bản tới view. Những phương thức này có cùng nội dung như sau,\n\n```\n/**\n * @param string $view view name or file path, depending on the actual rendering method\n * @param array $params the data to be passed to the view\n * @return string rendering result\n */\nmethodName($view, $params = [])\n```\n\n\n### Xuất bản tại Controllers <span id=\"rendering-in-controllers\"></span>\n\nTrong các [controllers](structure-controllers.md), bạn có thể gọi những phương thức sau trong controller để xuất bản view:\n\n* [[yii\\base\\Controller::render()|render()]]: xuất bản [tên view](#named-views) và gán vào cùng [layout](#layouts)\n  để xuất bản nội dung.\n* [[yii\\base\\Controller::renderPartial()|renderPartial()]]: xuất bản [tên view](#named-views) không gán layout.\n* [[yii\\web\\Controller::renderAjax()|renderAjax()]]: xuất bản [tên view](#named-views) không gán layout,\n  vào cho vào tất cả các file kịch bản JS/CSS. Xuất bản này thường được dùng để phản hồi các yêu cầu của AJAX Web.\n* [[yii\\base\\Controller::renderFile()|renderFile()]]: xuất bản một view được mô tả trong các đường dẫn của view hoặc\n  [alias](concept-aliases.md).\n* [[yii\\base\\Controller::renderContent()|renderContent()]]: xuất bản chuỗi bằng việc nhúng view vào \n  [layout](#layouts) hiện tại. Phương thức này có từ phiên bản 2.0.1.\n\nVí dụ,\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse app\\models\\Post;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\n\nclass PostController extends Controller\n{\n    public function actionView($id)\n    {\n        $model = Post::findOne($id);\n        if ($model === null) {\n            throw new NotFoundHttpException;\n        }\n\n        // xuất bản view có tên \"view\" và gán layout vào nó\n        return $this->render('view', [\n            'model' => $model,\n        ]);\n    }\n}\n```\n\n\n### Xuất bản tại các Widget <span id=\"rendering-in-widgets\"></span>\n\nTại các [widgets](structure-widgets.md), bạn có thể gọi phương thức sau trong widget để xuất bản views.\n\n* [[yii\\base\\Widget::render()|render()]]: dùng để xuất bản cùng với [tên view](#named-views).\n* [[yii\\base\\Widget::renderFile()|renderFile()]]: xuất bản view được mô tả với đường dẫn file view hoặc một\n  [alias (bí danh)](concept-aliases.md).\n\nVí dụ,\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Widget;\nuse yii\\helpers\\Html;\n\nclass ListWidget extends Widget\n{\n    public $items = [];\n\n    public function run()\n    {\n        // renders a view named \"list\"\n        return $this->render('list', [\n            'items' => $this->items,\n        ]);\n    }\n}\n```\n\n\n### Xuất bản tại Views <span id=\"rendering-in-views\"></span>\n\nXuất bản view nằm trong view khác bằng việc gọi một phương trong những phương thức sau được cung cấp bởi [[yii\\base\\View|thành phần view]]:\n\n* [[yii\\base\\View::render()|render()]]: xuất bản ra [tên view](#named-views).\n* [[yii\\web\\View::renderAjax()|renderAjax()]]: xuất bản ra [tên view](#named-views) đăng ký cùng với những file kịch bản\n  JS/CSS. Các file này dùng trong các yêu cầu AJAX Web.\n* [[yii\\base\\View::renderFile()|renderFile()]]: xuất bản view được mô tả với đường dẫn view hoặc một\n  [alias](concept-aliases.md).\n\nVí dụ, đoạn mã sau nằm trong view sẽ xuất bản tên view là `_overview.php` view này nằm cùng đường dẫn như view hiện tại được xuất bản\n. Lưu ý rằng biến `$this` nằm trong view đã có trong thành phần của [[yii\\base\\View|view]] :\n\n```php\n<?= $this->render('_overview') ?>\n```\n\n\n### Xuất bản vị trí khác <span id=\"rendering-in-other-places\"></span>\n\nTại những vị trí khác, bạn có thể truy cập thành phần ứng dụng [[yii\\base\\View|view]] qua cú pháp\n`Yii::$app->view` và gọi nó qua các phương thức đã nói trên để xuất bản view. Ví dụ,\n\n```php\n// hiển thị ra file view \"@app/views/site/license.php\"\necho \\Yii::$app->view->renderFile('@app/views/site/license.php');\n```\n\n\n### Đặt tên Views <span id=\"named-views\"></span>\n\nKhi xuất bản view, bạn có thể chỉ định sử dụng tên view bao gồm tên view hoặc tên file của view qua đường dẫn/bí danh. Trong các trường hợp,\nbạn nên sử dụng các cách trước đây bởi vì nó ngắn gọn và linh hoạt hơn. Chúng ta gọi view được chỉ định sử dụng tên như *tên của views*.\n\nTên một view được gán vào đường dẫn file tương ứng dựa theo các quy tắc sau:\n\n* Tên một view có thể bỏ qua phần mở rộng file file. Trong trường hợp này, đuôi `.php` sẽ được dùng như một phần mở rộng. Ví dụ,\n  tên view là `about` sẽ tương ứng với tên file là `about.php`.\n* Nếu tên view được bắt đầu với 2 dấu gạch chéo `//`, đường dẫn view tương ứng sẽ là `@app/views/ViewName`.\n  Điều này, tên view sẽ không được phép dưới [[yii\\base\\Application::viewPath|application's view path]].\n  Ví dụ, `//site/about` sẽ được cập nhật thành `@app/views/site/about.php`.\n* Nếu tên view được bắt đầu với một dấu gạch chéo `/`, đường dẫn file của view có giá trị tương ứng với tiền tố tên view\n  cùng với [[yii\\base\\Module::viewPath|đường dẫn view]] của [module](structure-modules.md) đang được chọn.\n  Nếu không có module được chọn, `@app/views/ViewName` sẽ được dùng. Ví dụ, `/user/create` sẽ có nội dung là\n  `@app/modules/user/views/user/create.php`, nếu module được chọn `user`. Nếu không có module chọn,  \n  đường dẫn file view sẽ là `@app/views/user/create.php`.\n* Nếu view được xuất bản với [[yii\\base\\View::context|context]] và context được thực thi từ [[yii\\base\\ViewContextInterface]],\n  thì đường dẫn file của view được hình thành bằng tiền tố từ [[yii\\base\\ViewContextInterface::getViewPath()|đường dẫn view]] của\n  context tới tên view. Điều này quan trọng khi xuất bản views trong controllers và widgets. Ví dụ,\n  `about` sẽ tương ứng tới đường dẫn `@app/views/site/about.php` nếu context có controller là `SiteController`.\n* Nếu view được xuất bản trong các view khác, đường dẫn chứa file view khác sẽ chứa tên view mới\n  tới đường dẫn thực tế của đường dẫn view. Ví dụ, `item` sẽ tương ứng tới `@app/views/post/item.php`\n  nếu nó được xuất bản từ view `@app/views/post/index.php`.\n\nDựa theo những quy tắc trên, khi gọi `$this->render('view')` tại controller `app\\controllers\\PostController` thì thực ra\nsẽ gọi tớ file view `@app/views/post/view.php` để xuất bản, trong khi đó việc gọi `$this->render('_overview')` tại view sẽ xuất bản\nview file là `@app/views/post/_overview.php`.\n\n\n### Truy cập dữ liệu tại Views <span id=\"accessing-data-in-views\"></span>\n\nCó 2 cách để truy cập dữ liệu trong view là: đây và kéo.\n\nBằng cách truyền dữ liệu vào tham số thứ 2 vào phương thức xuất bản của view, bạn đang sử dụng cách đẩy.\nDữ liệu nên được lập như một mảng gồm cặp khóa-giá trị. Mỗi khi view được xuất bản, máy chủ PHP gọi hàm\n`extract()` cho mảng này vì vậy mảng được cho vào biến tới view.\nVí dụ, view sau sẽ xuất bản rendering tại controller và sẽ đẩy 2 biến vào view `report` :\n`$foo = 1` và `$bar = 2`.\n\n```php\necho $this->render('report', [\n    'foo' => 1,\n    'bar' => 2,\n]);\n```\n\nCách kéo dữ liệu sẽ nhận dữ liệu từ [[yii\\base\\View|view component]] hoặc các đối tượng khác được truy cập\ntrong views (vd. `Yii::$app`). Đoạn mã sau là ví dụ, trong view bạn có thể lấy dữ liệu từ controller qua\ncâu lệnh `$this->context`. Và kết quả là, trong view có thể truy cập bất cứ thuộc tính hoặc phương thức\nnào nằm trong controller tại view `report`, đoạn mã sau sẽ cho hiển thị định danh của controller:\n\n```php\nĐịnh danh của controller là: <?= $this->context->id ?>\n```\n\nCách đẩy thường được dùng hơn khi truy cập dữ liệu trong views, bởi vì nó làm views ít phụ thuộc hơn ở đối tượng\ncontext. Nó có hạn chế hơn khi bạn cần xây dựng dữ liệu thủ công ở các thời điểm, điều này có thể khiển nhẹ và dễ xảy ra lỗi hơn nếu view\nđược chia sẻ và xuất bản ở những vị trí khác nhau.\n\n\n### Chia sẽ dữ liệu giữa các Views <span id=\"sharing-data-among-views\"></span>\n\nThành phần [[yii\\base\\View]] cung cấp thuộc tính [[yii\\base\\View::params|params]] cho bạn có thể\nchia sẽ dữ liệu giữa các view.\n\nVí dụ, tại `about`, bạn có thể sử dụng mã code sau để thiết lập giá trị hiện tại của breadcrumbs.\n\n```php\n$this->params['breadcrumbs'][] = 'About Us';\n```\n\nSau đó, tại file [layout](#layouts), cũng là một view, bạn có thể hiển thị breadcrumbs sử dụng dữ liệu\nqua tham số [[yii\\base\\View::params|params]]:\n\n```php\n<?= yii\\widgets\\Breadcrumbs::widget([\n    'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],\n]) ?>\n```\n\n\n## Layouts (Bố cục) <span id=\"layouts\"></span>\n\nLayouts là một views đặc biệt và chứa những phần chung của các views khác. Ví dụ, những trang\ncủa các ứng dụng Web được chia sẻ cùng header và footer. Thay vì bạn lặp lại nội dunng các trang header và footer\ntại mỗi view, thì có cách tốt hơn là bạn chỉ thực hiện một lần ở layout và nhúng nội dung xuất bản của view tại\nvị trí thích hợp trong layout.\n\n\n### Tạo mới Layouts <span id=\"creating-layouts\"></span>\n\nBởi vì các layout cũng là view, do vậy các layout cũng được tạo như các view khác. Mặc định, layouts\nđược lưu ở đường dẫn `@app/views/layouts`. Với các layouts dùng trong [module](structure-modules.md),\nthì được lưu ở đường dẫn `views/layouts` nằm trong [[yii\\base\\Module::basePath|đường dẫn module]].\nBạn có thể tùy biến lại đường dẫn mặc định của layout bằng việc cấu hình vào thuộc tính [[yii\\base\\Module::layoutPath]]\ncủa ứng dụng hoặc module.\n\nVí dụ sau chỉ cho bạn cách khai báo một layout. Lưu ý rằng đây chỉ là ví dụ đơn giản, chúng ta đã đơn giản hóa các đoạn mã\nvào layout. Tuy nhiên trong thực tế, bạn có thể thêm nhiều nội dung vào layout, như các thẻ head, main menu, vv.\n\n```php\n<?php\nuse yii\\helpers\\Html;\n\n/**\n * @var \\yii\\web\\View $this\n * @var string $content\n */\n?>\n<?php $this->beginPage() ?>\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\"/>\n    <?= Html::csrfMetaTags() ?>\n    <title><?= Html::encode($this->title) ?></title>\n    <?php $this->head() ?>\n</head>\n<body>\n<?php $this->beginBody() ?>\n    <header>My Company</header>\n    <?= $content ?>\n    <footer>&copy; 2014 by My Company</footer>\n<?php $this->endBody() ?>\n</body>\n</html>\n<?php $this->endPage() ?>\n```\n\nNhư bạn thấy trên, layout sinh ra các thẻ HTML và được dùng chung ở tất cả các trang. Nằm trong section `<body>`,\nlayout xuất bản nội dung biến `$content` chứa nội dung của views khi xuất bản\nvà được đẩy vào layout khi phương thức [[yii\\base\\Controller::render()]] được gọi.\n\nHầu hết layouts có thể gọi các phương thức như liệt kê ở dưới sau. Những phương thức này chịu trách nhiệm chính trong liên kết các sự kiện\nvề quá trình xuất bản để các kịch bản và các thẻ được đăng ký ở những view khác có thể được được hiển thị ra\ntại các vị trí mà các phương thức được gọi.\n\n- [[yii\\base\\View::beginPage()|beginPage()]]: Phương thức được gọi ở mỗi đầu của layout.\n  Phương thức này sẽ triggers tới sự kiện [[yii\\base\\View::EVENT_BEGIN_PAGE|EVENT_BEGIN_PAGE]] cho biết đến phần đầu của trang.\n- [[yii\\base\\View::endPage()|endPage()]]: Phương thức được gọi ở mỗi cuối của layout.\n  Phương thức này sẽ triggers tới sự kiện [[yii\\base\\View::EVENT_END_PAGE|EVENT_END_PAGE]] cho biết đến phần cuối của trang.\n- [[yii\\web\\View::head()|head()]]: Phương thức được gọi nằm trong session `<head>` của trang HTML.\n  Phương thức sinh ra các placeholder sẽ được thay thế bằng các đoạn mã HTML (vd. các thẻ link, meta)\n  khi trang kết thúc việc xuất bản.\n- [[yii\\web\\View::beginBody()|beginBody()]]: Phương thức được gọi ở mỗi đầu của section `<body>`.\n  Phương thức triggers tới sự kiện [[yii\\web\\View::EVENT_BEGIN_BODY|EVENT_BEGIN_BODY]] và sinh các placeholder sẽ được thay \n  thế bởi các mã HTML đã đăng ký (vd. JavaScript) nhằm vào phần đầu của body.\n- [[yii\\web\\View::endBody()|endBody()]]: Phương thức được gọi ở mỗi cuối section `<body>`.\n  Phương thức triggers tới sự kiện [[yii\\web\\View::EVENT_END_BODY|EVENT_END_BODY]] và sinh các placeholder có được thay thế\n  bởi các mã HTML đã đăng ký (vd. JavaScript) nhằm vào phần cuối của body.\n\n\n### Truy cập dữ liệu trong Layouts <span id=\"accessing-data-in-layouts\"></span>\n\nTrong layout, bạn có thể truy cập dữ liệu qua 2 biến đã định nghĩa là: `$this` và `$content`. Thành phần đã được đề\ncập là [[yii\\base\\View|view]], cũng  giống như những view khác, trong khi dữ liệu cuối cùng được xuất bản ra\nview dữ liệu này được xuất bản bằng việc gọi phương thức [[yii\\base\\Controller::render()|render()]] tại controllers.\n\nNếu bạn muốn truy cập những dữ liệu khác trong layouts, bạn có thể sử dụng phương pháp pull như đã mô tả ở mục\n[Accessing Data in Views](#accessing-data-in-views). Nếu bạn chuyển dữ liệu từ content view\nvào layout, bạn có thể sử dụng phương thức được mô tả trong phần [Sharing Data among Views](#sharing-data-among-views).\n\n\n### Sử dụng Layout <span id=\"using-layouts\"></span>\n\nNhư mô tả ở mục [Xuất bản tại Controllers](#rendering-in-controllers), khi bạn xuất bản view\nbằng việc gọi phương thức [[yii\\base\\Controller::render()|render()]] tại controller, một layout sẽ được áp dụng\nđể xuất bản nội dung result. Mặc định, layout `@app/views/layouts/main.php` sẽ được sử dụng trong việc xuất bản nội dung. \n\nBạn có thể sử dụng những layout khác bằng việc cấu hình thuộc tính [[yii\\base\\Application::layout]] hoặc [[yii\\base\\Controller::layout]].\nThuộc tính trước layout được dùng tại tất cả các controller, trong khi đó thuộc tính sau được ghi đè trong mỗi controller.\nVí dụ, đoạn mã sau thiết lập controller `post` dùng layout `@app/views/layouts/post.php` để\nxuất bản nội dung ra views. Còn tại các controller khác, giả sử rằng thuộc tính `layout` không bị thay đổi, thì controller sử dụng layout mặc định là\n`@app/views/layouts/main.php` vào việc xuất bản nội dung.\n \n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass PostController extends Controller\n{\n    public $layout = 'post';\n    \n    // ...\n}\n```\n\nVới controller thuộc trong module, bạn có thể thiết lập thuộc tính [[yii\\base\\Module::layout|layout]] trong module để\nthiết lập layout cụ thể cho những controller. \n\nBởi vì thuộc tính `layout` có thể đã thiết lập ở những vị trí khác như (controllers, modules, application),\nphía nền Yii cũng thực hiện 2 bước để xác định rằnglayout file sẽ được dùng vào controller cụ thể.\n\nTại bước đầu tiên, Yii xác định giá trị layout và context module:\n\n- Nếu thuộc tính [[yii\\base\\Controller::layout]] tại controller có giá trị khác `null`, sẽ dùng nó như giá trị layout và\n  thuộc tính [[yii\\base\\Controller::module|module]] của controller như là một context module.\n- Nếu thuộc tính [[yii\\base\\Controller::layout]] của controller có giá trị là `null`, tìm kiếm tất cả trong các modules liên quan (bao gồm bên trong ứng dụng) của controller và \n  tìm kiếm module đầu tiên mà có thuộc tính [[yii\\base\\Module::layout|layout]] có giá trị khác `null`. Sử dụng nó như module và\n  giá trị [[yii\\base\\Module::layout|layout]] như một context module và chọn lấy giá trị layout.\n  Nếu một trong những module không được tìm thấy, đồng nghĩa với việc không có layout nào được chọn.\n  \nTại bước tiếp theo, nó xác định file layout được dùng dựa vào giá trị layout và context module\nđược xác định ở bước. Giá trị layout có thể là:\n\n- một đường dẫn bí danh (vd. `@app/views/layouts/main`).\n- một đường dẫn tuyệt đối (vd. `/main`): giá trị layout bắt đầu với dấu gách chéo. thì file layout thực tế sẽ được tìm\n  dưới đường dẫn của ứng dụng [[yii\\base\\Application::layoutPath|layout path]] có mặc định là\n  `@app/views/layouts`.\n- một đường dẫn tương đối (vd. `main`): thì file layout được tìm dưới các context module's\n  [[yii\\base\\Module::layoutPath|layout path]] có mặc định ở đường dẫn `views/layouts` dưới đường dẫn module\n  [[yii\\base\\Module::basePath|module directory]].\n- giá trị là `false`: không có layout nào được áp dụng.\n\nNếu tên layout không có chứa thông tin phần mở rộng tệp, thì Yii sử dụng đuôi mở rộng là `.php`.\n\n\n### Layout lồng nhau <span id=\"nested-layouts\"></span>\n\nĐôi lúc bạn muốn nhúng layout vào layout khác. Ví dụ, tại những mục khác nhau của Web site, bạn muốn\nsử dụng những layout khác, trong khi tất cả các layouts chia sẽ với layout chung được sinh bởi hầu hết các trang\nHTML5. Bạn có thể kết hợp các layout bằng việc gọi phương thức [[yii\\base\\View::beginContent()|beginContent()]] và\n[[yii\\base\\View::endContent()|endContent()]] tại những layout con như sau:\n\n```php\n<?php $this->beginContent('@app/views/layouts/base.php'); ?>\n\n...nội dung layout con nằm trong đây...\n\n<?php $this->endContent(); ?>\n```\n\nNhư mô tả trên, nội dung layout nên được nằm trong phương thức [[yii\\base\\View::beginContent()|beginContent()]] và phương thức\n[[yii\\base\\View::endContent()|endContent()]]. Các tham số được gán vào phương thức [[yii\\base\\View::beginContent()|beginContent()]]\ncó chỉ định các thông tin của layout cha. Nó có thể dùng cả các file layout hoặc alias.\n\nViệc sử dụng phương pháp trên, bạn có thể lồng các layout theo nhiều cấp độ.\n\n\n### Sử dụng các khối(Block) <span id=\"using-blocks\"></span>\n\nCác khối cho phép bạn xác định các nội dung ở view tại một vị trí khi muốn hiển thị khối đó tại nơi khác trong view. Chúng thường được dùng với\ncác layout. Ví dụ, bạn có thể định nghĩa các khối ở nội dung view và hiển thị nó vào layout.\n\nKhi gọi các phương thức [[yii\\base\\View::beginBlock()|beginBlock()]] và [[yii\\base\\View::endBlock()|endBlock()]] để định nghĩa các khối.\nKhối được truy cập qua phương thức `$view->blocks[$blockID]`, với `$blockID` là một định danh ID duy nhất mà bạn gán\nvào khối khi định nghĩa khối đó.\n\nVí dụ sau sẽ chỉ cho bạn các để sử dụng các khối để tùy biến các phần của layout tại nội dung view.\n\nĐầu tiên, tại trang nội dung của view, ta định nghĩa một hoặc nhiều khối:\n\n```php\n...\n\n<?php $this->beginBlock('block1'); ?>\n\n...nội dung của block1...\n\n<?php $this->endBlock(); ?>\n\n...\n\n<?php $this->beginBlock('block3'); ?>\n\n...nội dung của block3...\n\n<?php $this->endBlock(); ?>\n```\n\nTiếp đến, tại giao diện layout, sẽ xuất bản các khối nếu khối này có nội dung, còn không sẽ hiển thị các nội dung mặc định của khối nếu\ncác khối không được định nghĩa.\n\n```php\n...\n<?php if (isset($this->blocks['block1'])): ?>\n    <?= $this->blocks['block1'] ?>\n<?php else: ?>\n    ... nội dung mặc định cho khối block1 ...\n<?php endif; ?>\n\n...\n\n<?php if (isset($this->blocks['block2'])): ?>\n    <?= $this->blocks['block2'] ?>\n<?php else: ?>\n    ... nội dung mặc định cho khối block2 ...\n<?php endif; ?>\n\n...\n\n<?php if (isset($this->blocks['block3'])): ?>\n    <?= $this->blocks['block3'] ?>\n<?php else: ?>\n    ... nội dung mặc định cho khối block3 ...\n<?php endif; ?>\n...\n```\n\n\n## Sử dụng các thành phần View <span id=\"using-view-components\"></span>\n\n[[yii\\base\\View|View components]] cung cấp nhiều tính năng cho  phần giao diện. While you can get view components\nby creating individual instances of [[yii\\base\\View]] or its child class, in most cases you will mainly use\nthe `view` application component. Bạn có thể cấu hình các component trong mục [application configurations](structure-applications.md#application-configurations)\nnhư sau:\n\n```php\n[\n    // ...\n    'components' => [\n        'view' => [\n            'class' => 'app\\components\\View',\n        ],\n        // ...\n    ],\n]\n```\n\nCác thành phần View cung cấp các tính năng hữu ích được liệt kê dưới, mỗi mô tả có trong các trang chi tiết:\n\n* [theming](output-theming.md): cho phép bạn xây dựng và thay đổi theme cho các trang Web.\n* [fragment caching](caching-fragment.md): cho phép bạn xử lý cache các fragment trong các trang Web.\n* [client script handling](output-client-scripts.md): hỗ trợ đăng ký vào xuất bản các nội dung về CSS và JavaScript.\n* [asset bundle handling](structure-assets.md): hỗ trợ việc đăng ký và xuất bản các [asset bundles](structure-assets.md).\n* [alternative template engines](tutorial-template-engines.md): cho phép bạn sử dụng các bộ giao diện, chẳng hạn như\n  [Twig](https://twig.symfony.com/), [Smarty](https://www.smarty.net/).\n\nBạn cũng có thể thường xuyên sử dụng các tính năng nhỏ nhưng hữu ích sau đây khi bạn đang phát triển các trang Web.\n\n\n### Thiết lập tiêu đề trang <span id=\"setting-page-titles\"></span>\n\nTại mỗi trang Web cần có các tiêu đề. Thông thường các thẻ tiêu đề được hiển hị trong các [layout](#layouts). Tuy nhiên, trong bài thực hành này\ncác tiêu đề thường được xác định tại trang nội dung của view hơn là xác định tại các layout. Để làm được việc này, lớp [[yii\\web\\View]] cung cấp\nthuộc tính [[yii\\web\\View::title|title]] cho bạn đẩy thông tin tiêu đề từ nội dung view qua các layout.\n\nĐể thực hiện tính năng này, tại mỗi trang nội dung của view, bạn có thể thiết lập tiêu đề trang như sau:\n\n```php\n<?php\n$this->title = 'Tiêu đề trang';\n?>\n```\n\nTiếp đến tại layout, hãy chắc chắn rằng bạn đặt đoạn mã sau vào mục `<head>`:\n\n```php\n<title><?= Html::encode($this->title) ?></title>\n```\n\n\n### Thực hiện đăng ký các thẻ Meta Tags <span id=\"registering-meta-tags\"></span>\n\nCác trang web thường cần tạo các thẻ meta khác nhau cần thiết cho các trang khác nhau. Như các thẻ tiêu đề trang, các thẻ meta\nxuất hiện ở mục `<head>` và thường được tạo ở các layouts.\n\nNếu bạn muốn chỉ định thẻ meta nào sẽ tạo trong trang views, bạn có thể gọi phương thức [[yii\\web\\View::registerMetaTag()]]\ntrong nội dung của view, như đoạn sau:\n\n```php\n<?php\n$this->registerMetaTag(['name' => 'keywords', 'content' => 'yii, framework, php']);\n?>\n```\n\nĐoạn mã trên sẽ đăng ký thẻ mata là \"keywords\" với thành phần của view. Thẻ meta đã đăng ký được hiển thị sau \nviệc xuất bản layout được xong. Đoạn mã HTML sau sẽ xuất bản và chèn vào vị trí nơi bạn gọi phương thức\n[[yii\\web\\View::head()]] tại layout:\n\n```php\n<meta name=\"keywords\"\n``` content=\"yii, framework, php\">\n\nLưu ý rằng nếu bạn gọi phương thức [[yii\\web\\View::registerMetaTag()]] nhiều lần, nó sẽ đăng ký nhiều thẻ meta,\ncho dù bất kể các thẻ meta có giống nhau hay không.\n\nĐể đảm bảo chỉ có một phiên bản duy nhất của loại thẻ meta, bạn có thể chỉ định một khóa làm tham số thứ hai khi gọi phương thức.\nVí dụ, đoạn mã sau sẽ đăng ký 2 thẻ meta là \"description\". Tuy nhiên, chỉ có thẻ thứ 2 được hiển thị.\n\n```php\n$this->registerMetaTag(['name' => 'description', 'content' => 'This is my cool website made with Yii!'], 'description');\n$this->registerMetaTag(['name' => 'description', 'content' => 'This website is about funny raccoons.'], 'description');\n```\n\n\n### Đăng ký các thẻ liên kết (Link Tags) <span id=\"registering-link-tags\"></span>\n\nGiống như các [thẻ meta](#registering-meta-tags), các thẻ liên kết rất hữu ích trong nhiều trường hợp, như tùy biến favicon, trỏ đến nguồn cấp dữ liệu RSS hoặc ủy quyền OpenID\ncho máy chủ khác. Bạn có thể làm việc với các thẻ liên kết theo cách tương tự như thẻ meta bằng cách sử dụng phương thức [[yii\\web\\View::registerLinkTag()]].\nVí dụ, trong trang nội dung view, bạn có thể đăng ký một thẻ liên kết như sau,\n\n```php\n$this->registerLinkTag([\n    'title' => 'Live News for Yii',\n    'rel' => 'alternate',\n    'type' => 'application/rss+xml',\n    'href' => 'https://www.yiiframework.com/rss.xml/',\n]);\n```\n\nĐoạn mã trên sẽ ra kết quả như sau\n\n```html\n<link title=\"Live News for Yii\" rel=\"alternate\" type=\"application/rss+xml\" href=\"https://www.yiiframework.com/rss.xml/\">\n```\n\nGiống như phương thức [[yii\\web\\View::registerMetaTag()|registerMetaTag()]], bạn có thể chỉ định từ khóa khi gọi phương thức\n[[yii\\web\\View::registerLinkTag()|registerLinkTag()]] để tránh tạo ra các thẻ liên kết lặp đi lặp lại.\n\n\n## Sự kiện View <span id=\"view-events\"></span>\n\n[[thành phần yii\\base\\View|View]] kích hoạt một số sự kiện trong quá trình xuất bản view. Bạn có thể phản hồi các sự kiện này để đưa nội dung\nvào chế độ xem hoặc xử lý kết quả hiển thị trước khi chúng được gửi đến người dùng cuối.\n\n- [[yii\\base\\View::EVENT_BEFORE_RENDER|EVENT_BEFORE_RENDER]]: sự kiện được kích hoạt khi bắt đầu xuất bản file trong\n  controller. Người xử lý sự kiện này có thể thiết lập [[yii\\base\\ViewEvent::isValid]] giá trị là `false` để hủy quá trình xuất bản.\n- [[yii\\base\\View::EVENT_AFTER_RENDER|EVENT_AFTER_RENDER]]: được kích hoạt sau khi xuất bản một file bằng việc gọi phương thức [[yii\\base\\View::afterRender()]].\n  Những người xử lý sự kiện này có thể có được kết quả xuất bản thông qua thuộc tính [[yii\\base\\ViewEvent::output]] và có thể sửa đổi thuộc tính này\n  để thay đổi kết quả xuất bản.\n- [[yii\\base\\View::EVENT_BEGIN_PAGE|EVENT_BEGIN_PAGE]]: được kích hoạt khi gọi phương thức [[yii\\base\\View::beginPage()]] tại layouts.\n- [[yii\\base\\View::EVENT_END_PAGE|EVENT_END_PAGE]]: được kích hoạt khi gọi phương thức [[yii\\base\\View::endPage()]] tại layouts.\n- [[yii\\web\\View::EVENT_BEGIN_BODY|EVENT_BEGIN_BODY]]: được kích hoạt khi gọi phương thức [[yii\\web\\View::beginBody()]] tại layouts.\n- [[yii\\web\\View::EVENT_END_BODY|EVENT_END_BODY]]: được kích hoạt khi gọi phương thức [[yii\\web\\View::endBody()]] tại layouts.\n\nVí dụ, đoạn mã sau sẽ chèn nội dung ngày hiện tại vào cuối trang:\n\n```php\n\\Yii::$app->view->on(View::EVENT_END_BODY, function () {\n    echo date('Y-m-d');\n});\n```\n\n\n## Xuất bản các trang tĩnh <span id=\"rendering-static-pages\"></span>\n\nCác trang tĩnh đề cập đến các trang Web có nội dung chính chủ yếu là tĩnh mà không cần truy cập dữ liệu động được đẩy từ các\ncontroller.\n\nBạn có thể hiển thị trang tĩnh bằng cách đặt các đoạn mã vào view, và sử dụng đoạn mã sau ở controller:\n\n```php\npublic function actionAbout()\n{\n    return $this->render('about');\n}\n```\n\nNếu một trang web chứa nhiều trang tĩnh, nó sẽ không linh hoạt khi sử dụng các đoạn mã nhiều lần.\nĐể giải quyết vấn đề này, bạn có thể cho vào một [standalone action](structure-controllers.md#standalone-actions)\nđược gọi là [[yii\\web\\ViewAction]] trong controller. Ví dụ,\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actions()\n    {\n        return [\n            'page' => [\n                'class' => 'yii\\web\\ViewAction',\n            ],\n        ];\n    }\n}\n```\n\nBây giờ nếu bạn tạo một view đặt là `about` nằm dưới đường dẫn `@app/views/site/pages`, bạn có thể hiển thị\nview này qua URL sau:\n\n```\nhttp://localhost/index.php?r=site%2Fpage&view=about\n```\n\nTham số `GET` là `view` sẽ gọi đến hành động [[yii\\web\\ViewAction]] với view được yêu cầu. Hành động sau đó sẽ\ntìm kiếm view này trong thư mục `@app/views/site/pages`. Bạn có thể cấu hình [[yii\\web\\ViewAction::viewPrefix]]\nđể thay đổi thư mục tìm kiếm các view này.\n\n\n## Bài thực hành <span id=\"best-practices\"></span>\n\nCác View chịu trách nhiệm trong việc hiển thị dữ liệu từ model tới người dùng. Trong các trường hợp, view thường\n\n* nên chứa các mã code hiển thị, như HTML, và các câu lệnh PHP đơn giản để xử lý, định dạng và xuất bản dữ liệu.\n* không nên chứa các mã code có chứa các câu lệnh truy vấn vào CSDL. Những câu lệnh này nên đặt trong các model.\n* nên tranh các câu lệnh điều hướng yêu cầu dữ liệu, như `$_GET`, `$_POST`. Các lệnh này nên xử lý ở các controller.\n  Nếu cấn lấy dữ liệu, chúng nên được đẩy vào view qua controller.\n* nên có đọc các thuộc tính của model, nhưng không được sửa nội dung trong đó.\n\nĐể việc quản lý các view dễ dàng hơn, nên tránh việc tạo các view quá phức tạp hoặc chứa nhiều các mã code dự phòng.\nBạn có thể tham khảo các thủ thuật sau để đạt việc quản lý view tốt:\n\n* dùng [layouts](#layouts) để hiển thị cho các mục chung (vd. trang header, footer).\n* chia các view phức tạp thành các view nhỏ hơn. Các view nhỏ hơn có thể được hiển thị và lắp ráp thành một view lớn hơn\nbằng các phương thức xuất bản mà chúng tôi đã mô tả.\n* tạo và dùng các [widgets](structure-widgets.md) như việc xây dựng các khối của view.\n* tạo vào dung các lớp helper classes để định dạng và chuyển đổi các nội dung trong view.\n\n"
  },
  {
    "path": "docs/guide-zh-CN/README.md",
    "content": "Yii 2.0 权威指南\n===============================\n\n本教程的发布遵循 [Yii 文档使用许可](https://www.yiiframework.com/doc/terms/)。\n\n版权所有。\n\n2014 (c) Yii Software LLC。\n\n\n介绍（Introduction）\n------------------\n\n* [关于 Yii（About Yii）](intro-yii.md)\n* [从 Yii 1.1 升级（Upgrading from Version 1.1）](intro-upgrade-from-v1.md)\n\n\n入门（Getting Started）\n---------------------\n\n* [你需要了解什么（What do you need to know）](start-prerequisites.md)\n* [安装 Yii（Installing Yii）](start-installation.md)\n* [运行应用（Running Applications）](start-workflow.md)\n* [第一次问候（Saying Hello）](start-hello.md)\n* [使用 Forms（Working with Forms）](start-forms.md)\n* [玩转 Databases（Working with Databases）](start-databases.md)\n* [用 Gii 生成代码（Generating Code with Gii）](start-gii.md)\n* [更上一层楼（Looking Ahead）](start-looking-ahead.md)\n\n\n应用结构（Application Structure）\n------------------------------\n\n* [结构概述（Overview）](structure-overview.md)\n* [入口脚本（Entry Scripts）](structure-entry-scripts.md)\n* [应用（Applications）](structure-applications.md)\n* [应用组件（Application Components）](structure-application-components.md)\n* [控制器（Controllers）](structure-controllers.md)\n* [模型（Models）](structure-models.md)\n* [视图（Views）](structure-views.md)\n* [模块（Modules）](structure-modules.md)\n* [过滤器（Filters）](structure-filters.md)\n* [小部件（Widgets）](structure-widgets.md)\n* [前端资源（Assets）](structure-assets.md)\n* [扩展（Extensions）](structure-extensions.md)\n\n\n请求处理（Handling Requests）\n--------------------------\n\n* [运行概述（Overview）](runtime-overview.md)\n* [引导（Bootstrapping）](runtime-bootstrapping.md)\n* [路由引导与创建 URL（Routing and URL Creation）](runtime-routing.md)\n* [请求（Requests）](runtime-requests.md)\n* [响应（Responses）](runtime-responses.md)\n* [Sessions and Cookies](runtime-sessions-cookies.md)\n* [错误处理（Handling Errors）](runtime-handling-errors.md)\n* [日志（Logging）](runtime-logging.md)\n\n\n关键概念（Key Concepts）\n---------------------\n\n* [组件（Components）](concept-components.md)\n* [属性（Properties）](concept-properties.md)\n* [事件（Events）](concept-events.md)\n* [行为（Behaviors）](concept-behaviors.md)\n* [配置（Configurations）](concept-configurations.md)\n* [别名（Aliases）](concept-aliases.md)\n* [类自动加载（Class Autoloading）](concept-autoloading.md)\n* [服务定位器（Service Locator）](concept-service-locator.md)\n* [依赖注入容器（Dependency Injection Container）](concept-di-container.md)\n\n\n配合数据库工作（Working with Databases）\n------------------------------------\n\n* [数据库访问（Data Access Objects）](db-dao.md): 数据库连接、基本查询、事务和模式操作\n* [查询生成器（Query Builder）](db-query-builder.md): 使用简单抽象层查询数据库\n* [活动记录（Active Record）](db-active-record.md): 活动记录对象关系映射（ORM），检索和操作记录、定义关联关系\n* [数据库迁移（Migrations）](db-migrations.md): 在团体开发中对你的数据库使用版本控制\n* [Sphinx](https://www.yiiframework.com/extension/yiisoft/yii2-sphinx/doc/guide)\n* [Redis（yii2-redis）](https://www.yiiframework.com/extension/yiisoft/yii2-redis/doc/guide/2.0/zh-cn): yii2-redis 扩展详解\n* [MongoDB](https://www.yiiframework.com/extension/yiisoft/yii2-mongodb/doc/guide)\n* [ElasticSearch](https://www.yiiframework.com/extension/yiisoft/yii2-elasticsearch/doc/guide)\n\n\n接收用户数据（Getting Data from Users）\n------------------------------------\n\n* [创建表单（Creating Forms）](input-forms.md)\n* [输入验证（Validating Input）](input-validation.md)\n* [文件上传（Uploading Files）](input-file-upload.md)\n* [收集列表输入（Collecting Tabular Input）](input-tabular-input.md)\n* [多模型同时输入（Getting Data for Multiple Models）](input-multiple-models.md)\n* [在客户端扩展 ActiveForm（Extending ActiveForm on the Client Side）](input-form-javascript.md)\n\n\n显示数据（Displaying Data）\n------------------------\n\n* [格式化输出数据（Data Formatting）](output-formatting.md)\n* [分页（Pagination）](output-pagination.md)\n* [排序（Sorting）](output-sorting.md)\n* [数据提供器（Data Providers）](output-data-providers.md)\n* [数据小部件（Data Widgets）](output-data-widgets.md)\n* [操作客户端脚本（Working with Client Scripts）](output-client-scripts.md)\n* [主题（Theming）](output-theming.md)\n\n\n安全（Security）\n--------------\n\n* [概述（Overview）](security-overview.md)\n* [认证（Authentication）](security-authentication.md)\n* [授权（Authorization）](security-authorization.md)\n* [处理密码（Working with Passwords）](security-passwords.md)\n* [加密（Cryptography）](security-cryptography.md)\n* [客户端认证（Auth Clients）](https://github.com/yiisoft/yii2-authclient/blob/master/docs/guide-zh-CN/README.md)\n* [安全领域的最佳实践（Best Practices）](security-best-practices.md)\n\n\n缓存（Caching）\n-------------\n\n* [概述（Overview）](caching-overview.md)\n* [数据缓存（Data Caching）](caching-data.md)\n* [片段缓存（Fragment Caching）](caching-fragment.md)\n* [页面缓存（Page Caching）](caching-page.md)\n* [HTTP 缓存（HTTP Caching）](caching-http.md)\n\n\nRESTful Web 服务（RESTful Web Services）\n--------------------------------------\n\n* [快速入门（Quick Start）](rest-quick-start.md)\n* [资源（Resources）](rest-resources.md)\n* [控制器（Controllers）](rest-controllers.md)\n* [路由（Routing）](rest-routing.md)\n* [格式化响应（Response Formatting）](rest-response-formatting.md)\n* [授权验证（Authentication）](rest-authentication.md)\n* [速率限制（Rate Limiting）](rest-rate-limiting.md)\n* [版本化（Versioning）](rest-versioning.md)\n* [错误处理（Error Handling）](rest-error-handling.md)\n\n\n开发工具（Development Tools）\n--------------------------\n\n* [调试工具栏和调试器（Debug Toolbar and Debugger）](https://github.com/yiisoft/yii2-debug/blob/master/docs/guide-zh-CN/README.md)\n* [使用 Gii 生成代码（Generating Code using Gii）](https://github.com/yiisoft/yii2-gii/blob/master/docs/guide-zh-CN/README.md)\n* [生成 API 文档（Generating API Documentation）](https://github.com/yiisoft/yii2-apidoc)\n\n\n测试（Testing）\n-------------\n\n* [概述（Overview）](test-overview.md)\n* [搭建测试环境（Testing environment setup）](test-environment-setup.md)\n* [单元测试（Unit Tests）](test-unit.md)\n* [功能测试（Functional Tests）](test-functional.md)\n* [验收测试（Acceptance Tests）](test-acceptance.md)\n* [测试夹具（Fixtures）](test-fixtures.md)\n\n\n高级专题（Special Topics）\n-----------------------\n\n* [高级应用模版（Advanced Project Template）](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide-zh-CN/README.md)\n* [从头构建自定义模版（Building Application from Scratch）](tutorial-start-from-scratch.md)\n* [控制台命令（Console Commands）](tutorial-console.md)\n* [核心验证器（Core Validators）](tutorial-core-validators.md)\n* [Docker](tutorial-docker.md)\n* [国际化（Internationalization）](tutorial-i18n.md)\n* [收发邮件（Mailing）](tutorial-mailing.md)\n* [性能优化（Performance Tuning）](tutorial-performance-tuning.md)\n* [共享主机环境（Shared Hosting Environment）](tutorial-shared-hosting.md)\n* [模板引擎（Template Engines）](tutorial-template-engines.md)\n* [集成第三方代码（Working with Third-Party Code）](tutorial-yii-integration.md)\n* [使用 Yii 作为微框架（Using Yii as a micro framework）](tutorial-yii-as-micro-framework.md)\n\n\n小部件（Widgets）\n---------------\n\n* [GridView](https://www.yiiframework.com/doc-2.0/yii-grid-gridview.html)\n* [ListView](https://www.yiiframework.com/doc-2.0/yii-widgets-listview.html)\n* [DetailView](https://www.yiiframework.com/doc-2.0/yii-widgets-detailview.html)\n* [ActiveForm](https://www.yiiframework.com/doc-2.0/guide-input-forms.html#activerecord-based-forms-activeform)\n* [Pjax](https://www.yiiframework.com/doc-2.0/yii-widgets-pjax.html)\n* [Menu](https://www.yiiframework.com/doc-2.0/yii-widgets-menu.html)\n* [LinkPager](https://www.yiiframework.com/doc-2.0/yii-widgets-linkpager.html)\n* [LinkSorter](https://www.yiiframework.com/doc-2.0/yii-widgets-linksorter.html)\n* [Bootstrap Widgets](https://github.com/yiisoft/yii2-bootstrap/blob/master/docs/guide-zh-CN/README.md)\n* [jQuery UI Widgets](https://github.com/yiisoft/yii2-jui/blob/master/docs/guide-zh-CN/README.md)\n\n\n助手类（Helpers）\n---------------\n\n* [助手一览（Overview）](helper-overview.md)\n* [Array 助手（ArrayHelper）](helper-array.md)\n* [Html 助手（Html）](helper-html.md)\n* [Url 助手（Url）](helper-url.md)\n\n"
  },
  {
    "path": "docs/guide-zh-CN/blocktypes.json",
    "content": "{\n    \"Warning:\": \"警告：\",\n    \"Note:\": \"注意：\",\n    \"Info:\": \"信息：\",\n    \"Tip:\": \"提示：\"\n}\n"
  },
  {
    "path": "docs/guide-zh-CN/caching-data.md",
    "content": "数据缓存\n============\n\n数据缓存是指将一些 PHP 变量存储到缓存中，使用时再从缓存中取回。\n它也是更高级缓存特性的基础，例如[查询缓存](#query-caching)\n和[内容缓存](caching-content.md)。\n\n如下代码是一个典型的数据缓存使用模式。\n其中 `$cache` 指向[缓存组件](#cache-components)：\n\n```php\n// 尝试从缓存中取回 $data \n$data = $cache->get($key);\n\nif ($data === false) {\n\n    // $data 在缓存中没有找到，则重新计算它的值\n\n    // 将 $data 存放到缓存供下次使用\n    $cache->set($key, $data);\n}\n\n// 这儿 $data 可以使用了。\n```\n\n从 2.0.11 版本开始, [缓存组件](#cache-components) \n提供了 [[yii\\caching\\Cache::getOrSet()|getOrSet()]] 方法来简化数据的取回、计算和存储。\n下面的代码逻辑和上一个例子是完全一样的：\n\n```php\n$data = $cache->getOrSet($key, function () {\n    return $this->calculateSomething();\n});\n```\n\n当缓存中有关联 $key 的数据时，将返回这个缓存的值。\n否则就执行匿名函数来计算出将要缓存的数据并返回它。\n\n如果匿名函数需要作用域外的数据时，可以使用 `use` 语句把这些数据传递到匿名函数中。\n例如：\n\n```php\n$user_id = 42;\n$data = $cache->getOrSet($key, function () use ($user_id) {\n    return $this->calculateSomething($user_id);\n});\n```\n\n> Note: [[yii\\caching\\Cache::getOrSet()|getOrSet()]] 方法也支持缓存持续性和缓存依赖。\n  请看[缓存过期](#cache-expiration) 和 [缓存依赖](#cache-dependencies) 来了解详细信息。\n\n\n## 缓存组件 <span id=\"cache-components\"></span>\n\n数据缓存需要**缓存组件**提供支持，它代表各种缓存存储器，\n例如内存，文件，数据库。\n\n缓存组件通常注册为应用程序组件，这样它们就可以\n在全局进行配置与访问。如下代码演示了如何配置应用程序组件\n`cache` 使用两个 [memcached](https://memcached.org/) \n服务器：\n\n```php\n'components' => [\n    'cache' => [\n        'class' => 'yii\\caching\\MemCache',\n        'servers' => [\n            [\n                'host' => 'server1',\n                'port' => 11211,\n                'weight' => 100,\n            ],\n            [\n                'host' => 'server2',\n                'port' => 11211,\n                'weight' => 50,\n            ],\n        ],\n    ],\n],\n```\n\n然后就可以通过 `Yii::$app->cache` 访问上面的缓存组件了。\n\n由于所有缓存组件都支持同样的一系列 API ，并不需要修改使用缓存的业务代码\n就能直接替换为其他底层缓存组件，只需在应用配置中重新配置一下就可以。\n例如，你可以将上述配置修改为使用 [[yii\\caching\\ApcCache|APC cache]]：\n\n\n```php\n'components' => [\n    'cache' => [\n        'class' => 'yii\\caching\\ApcCache',\n    ],\n],\n```\n\n> Tip: 你可以注册多个缓存组件，很多依赖缓存的类默认调用\n名为 `cache` 的组件（例如 [[yii\\web\\UrlManager]]）。\n\n\n### 支持的缓存存储器 <span id=\"supported-cache-storage\"></span>\n\nYii 支持一系列缓存存储器，概况如下：\n\n* [[yii\\caching\\ApcCache]]：使用 PHP [APC](https://www.php.net/manual/zh/book.apcu.php) 扩展。\n  这个选项可以认为是集中式应用程序环境中\n （例如：单一服务器，没有独立的负载均衡器等）最快的缓存方案。\n* [[yii\\caching\\DbCache]]：使用一个数据库的表存储缓存数据。要使用这个缓存，\n  你必须创建一个与 [[yii\\caching\\DbCache::cacheTable]] 对应的表。\n* [[yii\\caching\\ArrayCache]]: 仅通过将值存储在数组中来为当前请求提供缓存。\n  为了增强 ArrayCache 的性能，您可以通过将\n  [[yii\\caching\\ArrayCache::$serializer]] 设置为 `false` 来禁用已存储数据的序列化。\n* [[yii\\caching\\DummyCache]]：仅作为一个缓存占位符，不实现任何真正的缓存功能。\n  这个组件的目的是为了简化那些需要查询缓存有效性的代码。例如，\n  在开发中如果服务器没有实际的缓存支持，用它配置一个缓存组件。\n  一个真正的缓存服务启用后，可以再切换为使用相应的缓存组件。\n  两种条件下你都可以使用同样的代码 \n  `Yii::$app->cache->get($key)` 尝试从缓存中取回数据而不用担心 \n  `Yii::$app->cache` 可能是 `null`。\n* [[yii\\caching\\FileCache]]：使用标准文件存储缓存数据。\n  这个特别适用于缓存大块数据，例如一个整页的内容。\n* [[yii\\caching\\MemCache]]：使用 PHP [memcache](https://www.php.net/manual/zh/book.memcache.php) \n  和 [memcached](https://www.php.net/manual/zh/book.memcached.php) 扩展。\n  这个选项被看作分布式应用环境中（例如：多台服务器，有负载均衡等）\n  最快的缓存方案。\n* [[yii\\redis\\Cache]]：实现了一个基于 [Redis](https://redis.io/) 键值对存储器的缓存组件\n （需要 redis 2.6.12 及以上版本的支持 ）。\n* [[yii\\caching\\WinCache]]：使用 PHP [WinCache](https://iis.net/downloads/microsoft/wincache-extension)\n （[另可参考](https://www.php.net/manual/zh/book.wincache.php)）扩展.\n\n\n> Tip: 你可以在同一个应用程序中使用不同的缓存存储器。一个常见的策略是使用基于内存的缓存存储器\n  存储小而常用的数据（例如：统计数据），使用基于文件或数据库的缓存存储器\n  存储大而不太常用的数据（例如：网页内容）。\n\n\n## 缓存 API <span id=\"cache-apis\"></span>\n\n所有缓存组件都有同样的基类 [[yii\\caching\\Cache]] ，因此都支持如下 API：\n\n* [[yii\\caching\\Cache::get()|get()]]：通过一个指定的键（key）从缓存中取回一项数据。\n  如果该项数据不存在于缓存中或者已经过期/失效，则返回值 false。\n* [[yii\\caching\\Cache::set()|set()]]：将一个由键指定的数据项存放到缓存中。\n* [[yii\\caching\\Cache::add()|add()]]：如果缓存中未找到该键，则将指定数据存放到缓存中。\n* [[yii\\caching\\Cache::getOrSet()|getOrSet()]]：返回由键指定的缓存项，或者执行回调函数，把函数的返回值用键来关联存储到缓存中，\n  最后返回这个函数的返回值。  \n* [[yii\\caching\\Cache::multiGet()|multiGet()]]：由指定的键获取多个缓存数据项。\n* [[yii\\caching\\Cache::multiSet()|multiSet()]]：一次存储多个数据项到缓存中，每个数据都由一个键来指明。\n* [[yii\\caching\\Cache::multiAdd()|multiAdd()]]：一次存储多个数据项到缓存中，每个数据都由一个键来指明。\n  如果某个键已经存在，则略过该数据项不缓存。\n* [[yii\\caching\\Cache::exists()|exists()]]：返回一个值，指明某个键是否存在于缓存中。\n* [[yii\\caching\\Cache::delete()|delete()]]：通过一个键，删除缓存中对应的值。\n* [[yii\\caching\\Cache::flush()|flush()]]：删除缓存中的所有数据。\n\n> Note: 千万别直接用 `false` 布尔值当做数据项缓存，因为 [[yii\\caching\\Cache::get()|get()]] 方法用\n`false` 作为返回值来表名对应的缓存项不存在。\n你可以把 `false` 放到一个数组里然后缓存这个数组来避免上述的混淆问题。 \n\n有些缓存存储器如 MemCache，APC 支持以批量模式取回缓存值，这样可以节省取回缓存数据的开支。 \n[[yii\\caching\\Cache::multiGet()|multiGet()]]\n和 [[yii\\caching\\Cache::multiAdd()|multiAdd()]] API提供对该特性的支持。\n如果底层缓存存储器不支持该特性，Yii 也会模拟实现。\n\n由于 [[yii\\caching\\Cache]] 实现了 PHP `ArrayAccess` 接口，\n缓存组件也可以像数组那样使用，下面是几个例子：\n\n```php\n$cache['var1'] = $value1;  // 等价于： $cache->set('var1', $value1);\n$value2 = $cache['var2'];  // 等价于： $value2 = $cache->get('var2');\n```\n\n\n### 缓存键 <span id=\"cache-keys\"></span>\n\n存储在缓存中的每项数据都通过键作唯一识别。\n当你在缓存中存储一项数据时，必须为它指定一个键，\n稍后从缓存中取回数据时，也需要提供相应的键。\n\n你可以使用一个字符串或者任意值作为一个缓存键。当键不是一个字符串时，\n它将会自动被序列化为一个字符串。\n\n定义一个缓存键常见的一个策略就是在一个数组中包含所有的决定性因素。\n例如，[[yii\\db\\Schema]] 使用如下键存储一个数据表的结构信息。\n\n```php\n[\n    __CLASS__,              // 结构类名\n    $this->db->dsn,         // 数据源名称\n    $this->db->username,    // 数据库登录用户名\n    $name,                  // 表名\n];\n```\n\n如你所见，该键包含了可唯一指定一个数据库表所需的所有必要信息。\n\n> Note: 通过 [[yii\\caching\\Cache::multiSet()|multiSet()]] 或者 [[yii\\caching\\Cache::multiAdd()|multiAdd()]] 方法缓存的数据项的键，它的类型只能是字符串或整型，\n如果你想使用较为复杂的键，可以通过\n[[yii\\caching\\Cache::set()|set()]] 或者 [[yii\\caching\\Cache::add()|add()]] 方法来存储。\n\n当同一个缓存存储器被用于多个不同的应用时，应该为每个应用指定一个唯一的缓存键前缀以避免缓存键冲突。\n可以通过配置 [[yii\\caching\\Cache::keyPrefix]] 属性实现。\n例如，在应用配置中可以编写如下代码：\n\n```php\n'components' => [\n    'cache' => [\n        'class' => 'yii\\caching\\ApcCache',\n        'keyPrefix' => 'myapp',       // 唯一键前缀\n    ],\n],\n```\n\n为了确保互通性，此处只能使用字母和数字。\n\n\n### 缓存过期 <span id=\"cache-expiration\"></span>\n\n默认情况下，缓存中的数据会永久存留，除非它被某些缓存策略强制移除（例如：缓存空间已满，最老的数据会被移除）。\n要改变此特性，你可以在调用 [[yii\\caching\\Cache::set()|set()]] 存储一项数据时提供一个过期时间参数。\n该参数代表这项数据在缓存中可保持有效多少秒。\n当你调用 [[yii\\caching\\Cache::get()|get()]] 取回数据时，\n如果它已经过了超时时间，该方法将返回 false，表明在缓存中找不到这项数据。\n例如：\n\n```php\n// 将数据在缓存中保留 45 秒\n$cache->set($key, $data, 45);\n\nsleep(50);\n\n$data = $cache->get($key);\nif ($data === false) {\n    // $data 已过期，或者在缓存中找不到\n}\n```\n\n从 2.0.11 开始，如果想自定义缓存的持续时间，你可以在缓存组件配置中设置 [[yii\\caching\\Cache::$defaultDuration|defaultDuration]] 成员属性的值。\n这样设置会覆盖默认的缓存持续时间，且在使用 [[yii\\caching\\Cache::set()|set()]] \n方法时不必每次都传递 `$duration` 参数。\n\n\n### 缓存依赖 <span id=\"cache-dependencies\"></span>\n\n除了超时设置，缓存数据还可能受到**缓存依赖**的影响而失效。\n例如，[[yii\\caching\\FileDependency]] 代表对一个文件修改时间的依赖。\n这个依赖条件发生变化也就意味着相应的文件已经被修改。\n因此，缓存中任何过期的文件内容都应该被置为失效状态，\n对 [[yii\\caching\\Cache::get()|get()]] 的调用都应该返回 false。\n\n缓存依赖用 [[yii\\caching\\Dependency]] 的派生类所表示。\n当调用 [[yii\\caching\\Cache::set()|set()]] 在缓存中存储一项数据时，\n可以同时传递一个关联的缓存依赖对象。例如：\n\n```php\n// 创建一个对 example.txt 文件修改时间的缓存依赖\n$dependency = new \\yii\\caching\\FileDependency(['fileName' => 'example.txt']);\n\n// 缓存数据将在30秒后超时\n// 如果 example.txt 被修改，它也可能被更早地置为失效状态。\n$cache->set($key, $data, 30, $dependency);\n\n// 缓存会检查数据是否已超时。\n// 它还会检查关联的依赖是否已变化。\n// 符合任何一个条件时都会返回 false。\n$data = $cache->get($key);\n```\n\n下面是可用的缓存依赖的概况：\n\n- [[yii\\caching\\ChainedDependency]]：如果依赖链上任何一个依赖产生变化，则依赖改变。\n- [[yii\\caching\\DbDependency]]：如果指定 SQL 语句的查询结果发生了变化，则依赖改变。\n- [[yii\\caching\\ExpressionDependency]]：如果指定的 PHP 表达式执行结果发生变化，则依赖改变。\n- [[yii\\caching\\CallbackDependency]]：如果指定的PHP回调结果发生变化，依赖性将改变。\n- [[yii\\caching\\FileDependency]]：如果文件的最后修改时间发生变化，则依赖改变。\n- [[yii\\caching\\TagDependency]]：将缓存的数据项与一个或多个标签相关联。 您可以通过调用\n  [[yii\\caching\\TagDependency::invalidate()]] 来检查指定标签的缓存数据项是否有效。\n\n> Note: 避免对带有缓存依赖的缓存项使用 [[yii\\caching\\Cache::exists()|exists()]] 方法，\n因为它不检测缓存依赖（如果有的话）是否有效，所以调用 [[yii\\caching\\Cache::get()|get()]]\n可能返回 `false` 而调用 [[yii\\caching\\Cache::exists()|exists()]] 却返回 `true`。\n  \n\n## 查询缓存 <span id=\"query-caching\"></span>\n\n查询缓存是一个建立在数据缓存之上的特殊缓存特性。\n它用于缓存数据库查询的结果。\n\n查询缓存需要一个 [[yii\\db\\Connection|数据库连接]] 和一个有效的 `cache` 应用组件。\n查询缓存的基本用法如下，假设 `$db` 是一个 [[yii\\db\\Connection]] 实例：\n\n```php\n$result = $db->cache(function ($db) {\n\n    // SQL 查询的结果将从缓存中提供\n    // 如果启用查询缓存并且在缓存中找到查询结果\n    return $db->createCommand('SELECT * FROM customer WHERE id=1')->queryOne();\n\n});\n```\n\n查询缓存可以用在[DAO](db-dao.md)和[ActiveRecord](db-active-record.md)上:\n\n```php\n$result = Customer::getDb()->cache(function ($db) {\n    return Customer::find()->where(['id' => 1])->one();\n});\n```\n\n> Info: 有些 DBMS （例如：[MySQL](https://dev.mysql.com/doc/refman/5.6/en/query-cache.html)）\n也支持数据库服务器端的查询缓存。\n你可以选择使用任一查询缓存机制。\n上文所述的查询缓存的好处在于你可以指定更灵活的缓存依赖因此可能更加高效。\n\n自 2.0.14 以后，您可以使用以下快捷方法：\n\n```php\n(new Query())->cache(7200)->all();\n// and\nUser::find()->cache(7200)->all();\n```\n\n\n### 配置 <span id=\"query-caching-configs\"></span>\n\n查询缓存通过 [[yii\\db\\Connection]] 有三个全局可配置选项：\n\n* [[yii\\db\\Connection::enableQueryCache|enableQueryCache]]：是否打开或关闭查询缓存。\n  它默认为 `true`。 请注意，要有效打开查询缓存，\n  您还需要有一个由 [[yii\\db\\Connection::queryCache|queryCache]] 所指定的有效缓存。\n* [[yii\\db\\Connection::queryCacheDuration|queryCacheDuration]]：这表示查询结果在缓存中保持有效的秒数。 \n  您可以使用 0 来表示查询结果永久保留在缓存中。\n  该属性是在未指定持续时间的情况下调用 [[yii\\db\\Connection::cache()]]\n  使用的默认值。\n* [[yii\\db\\Connection::queryCache|queryCache]]：缓存应用组件的 ID。默认为 `'cache'`。\n  只有在设置了一个有效的缓存应用组件时，查询缓存才会有效。\n\n\n### 使用 <span id=\"query-caching-usages\"></span>\n\n如果您有多个需要利用查询缓存的 SQL 查询，则可以使用 [[yii\\db\\Connection::cache()]]。\n用法如下，\n\n```php\n$duration = 60;     // 缓存查询结果 60 秒。\n$dependency = ...;  // 可选的依赖关系\n\n$result = $db->cache(function ($db) {\n\n    // ... 在这里执行 SQL 查询 ...\n\n    return $result;\n\n}, $duration, $dependency);\n```\n\n在匿名函数里的任何一个 SQL 查询都将使用指定的依赖项缓存指定的持续时间\n如果一个 SQL 查询的结果在缓存中有效，那么这个 SQl 语句将会被跳过而它的查询结果会直接从缓存中读取。\n如果你没有指明 `$duration` 参数，\n那么使用 [[yii\\db\\Connection::queryCacheDuration|queryCacheDuration]] 属性。\n\n有时在`cache()`里，你可能不想缓存某些特殊的查询，\n这时你可以用[[yii\\db\\Connection::noCache()]]。\n\n```php\n$result = $db->cache(function ($db) {\n\n    // 使用查询缓存的 SQL 查询\n\n    $db->noCache(function ($db) {\n\n        // 不使用查询缓存的 SQL 查询\n\n    });\n\n    // ...\n\n    return $result;\n});\n```\n\n如果您只想为单个查询使用查询缓存，则可以在构建命令时调用 [[yii\\db\\Command::cache()]]。\n例如，\n\n```php\n// 使用查询缓存并将查询缓存持续时间设置为 60 秒\n$customer = $db->createCommand('SELECT * FROM customer WHERE id=1')->cache(60)->queryOne();\n```\n\n您还可以使用 [[yii\\db\\Command::noCache()]] 禁用单个命令的查询缓存。例如，\n\n```php\n$result = $db->cache(function ($db) {\n\n    // 使用查询缓存的 SQL 查询\n\n    // 对此命令不使用查询缓存\n    $customer = $db->createCommand('SELECT * FROM customer WHERE id=1')->noCache()->queryOne();\n\n    // ...\n\n    return $result;\n});\n```\n\n\n### 限制条件 <span id=\"query-caching-limitations\"></span>\n\n当查询结果中含有资源句柄时，查询缓存无法使用。\n例如，在有些 DBMS 中使用了 `BLOB` 列的时候，\n缓存结果会为该数据列返回一个资源句柄。\n\n有些缓存存储器有大小限制。例如，memcache 限制每条数据最大为 1MB。\n因此，如果查询结果的大小超出了该限制，\n则会导致缓存失败。\n\n\n### 缓存冲刷 <span id=\"cache-flushing\"></span>\n\n当你想让所有的缓存数据失效时，可以调用 [[yii\\caching\\Cache::flush()]]。\n\n冲刷缓存数据，你还可以从控制台调用 `yii cache/flush`。\n - `yii cache`：列出应用中可用的缓存组件\n - `yii cache/flush cache1 cache2`：刷新缓存组件`cache1`，`cache2` \n (可以传递多个用空格分开的缓存组件）\n - `yii cache/flush-all`：刷新应用中所有的缓存组件\n - `yii cache/flush-schema db`：清除给定连接组件的数据库表结构缓存\n\n> Info: 默认情况下，控制台应用使用独立的配置文件。\n所以，为了上述命令发挥作用，请确保 Web 应用和控制台应用配置相同的缓存组件。\n"
  },
  {
    "path": "docs/guide-zh-CN/caching-fragment.md",
    "content": "片段缓存\n=======\n\n片段缓存指的是缓存页面内容中的某个片段。例如，一个页面显示了逐年销售额的摘要表格，\n可以把表格缓存下来，以消除每次请求都要重新生成表格的耗时。\n片段缓存是基于[数据缓存](caching-data.md)实现的。\n\n在[视图](structure-views.md)中使用以下结构启用片段缓存：\n\n```php\nif ($this->beginCache($id)) {\n\n    // ... 在此生成内容 ...\n\n    $this->endCache();\n}\n```\n\n调用 [[yii\\base\\View::beginCache()|beginCache()]] 和 [[yii\\base\\View::endCache()|endcache()]] 方法包裹内容生成逻辑。\n如果缓存中存在该内容，[[yii\\base\\View::beginCache()|beginCache()]] 方法将渲染内容并返回 false，\n因此将跳过内容生成逻辑。否则，内容生成逻辑被执行，\n一直执行到[[yii\\base\\View::endCache()|endCache()]] 时，\n生成的内容将被捕获并存储在缓存中。\n\n和[数据缓存](caching-data.md)一样，每个片段缓存也需要全局唯一的 `$id` 标记。\n\n\n## 缓存选项 <span id=\"caching-options\"></span>\n\n如果要为片段缓存指定额外配置项，\n请通过向 [[yii\\base\\View::beginCache()|beginCache()]] \n方法第二个参数传递配置数组。在框架内部，该数组将被用来配置一个 [[yii\\widget\\FragmentCache]] \n小部件用以实现片段缓存功能。\n\n### 过期时间（duration） <span id=\"duration\"></span>\n\n或许片段缓存中最常用的一个配置选项就是 [[yii\\widgets\\FragmentCache::duration|duration]] 了。\n它指定了内容被缓存的秒数。\n以下代码缓存内容最多一小时：\n\n```php\nif ($this->beginCache($id, ['duration' => 3600])) {\n\n    // ... 在此生成内容 ...\n\n    $this->endCache();\n}\n```\n\n如果该选项未设置，则它将采用默认值 60，这意味着缓存的内容将在 60 秒后过期。\n\n\n### 依赖 <span id=\"dependencies\"></span>\n\n和[数据缓存](caching-data.md)一样，片段缓存的内容一样可以设置缓存依赖。\n例如一段被缓存的文章，是否重新缓存取决于它是否被修改过。\n\n通过设置 [[yii\\widgets\\FragmentCache::dependency|dependency]] 选项来指定依赖，\n该选项的值可以是一个 [[yii\\caching\\Dependency]] 类的派生类，也可以是创建缓存对象的配置数组。\n以下代码指定了一个片段缓存，它依赖于 `update_at` 字段是否被更改过的。\n\n```php\n$dependency = [\n    'class' => 'yii\\caching\\DbDependency',\n    'sql' => 'SELECT MAX(updated_at) FROM post',\n];\n\nif ($this->beginCache($id, ['dependency' => $dependency])) {\n\n    // ... 在此生成内容 ...\n\n    $this->endCache();\n}\n```\n\n\n### 变化 <span id=\"variations\"></span>\n\n缓存的内容可能需要根据一些参数的更改而变化。\n例如一个 Web 应用支持多语言，同一段视图代码也许需要生成多个语言的内容。\n因此可以设置缓存根据应用当前语言而变化。\n\n通过设置 [[yii\\widgets\\FragmentCache::variations|variations]] 选项来指定变化，\n该选项的值应该是一个标量，每个标量代表不同的变化系数。\n例如设置缓存根据当前语言而变化可以用以下代码：\n\n```php\nif ($this->beginCache($id, ['variations' => [Yii::$app->language]])) {\n\n    // ... 在此生成内容 ...\n\n    $this->endCache();\n}\n```\n\n\n### 开关 <span id=\"toggling-caching\"></span>\n\n有时你可能只想在特定条件下开启片段缓存。例如，一个显示表单的页面，可能只需要在初次请求时缓存表单（通过 GET 请求）。\n随后请求所显示（通过 POST 请求）的表单不该使用缓存，因为此时表单中可能包含用户输入内容。\n鉴于此种情况，可以使用 [[yii\\widgets\\FragmentCache::enabled|enabled]] 选项来指定缓存开关，\n如下所示：\n\n```php\nif ($this->beginCache($id, ['enabled' => Yii::$app->request->isGet])) {\n\n    // ... 在此生成内容 ...\n\n    $this->endCache();\n}\n```\n\n\n## 缓存嵌套 <span id=\"nested-caching\"></span>\n\n片段缓存可以被嵌套使用。一个片段缓存可以被另一个包裹。\n例如，评论被缓存在里层，同时整个评论的片段又被缓存在外层的文章中。\n以下代码展示了片段缓存的嵌套使用：\n\n```php\nif ($this->beginCache($id1)) {\n\n    // ...在此生成内容...\n\n    if ($this->beginCache($id2, $options2)) {\n\n        // ...在此生成内容...\n\n        $this->endCache();\n    }\n\n    // ...在此生成内容...\n\n    $this->endCache();\n}\n```\n\n可以为嵌套的缓存设置不同的配置项。例如，内层缓存和外层缓存使用不同的过期时间。\n甚至当外层缓存的数据过期失效了，内层缓存仍然可能提供有效的片段缓存数据。\n但是，反之则不然。如果外层片段缓存没有过期而被视为有效，\n此时即使内层片段缓存已经失效，它也将继续提供同样的缓存副本。\n因此，你必须谨慎处理缓存嵌套中的过期时间和依赖，\n否则外层的片段很有可能返回的是不符合你预期的失效数据。\n> 译注：外层的失效时间应该短于内层，外层的依赖条件应该低于内层，以确保最小的片段，返回的是最新的数据。\n\n## 动态内容 <span id=\"dynamic-content\"></span>\n\n使用片段缓存时，可能会遇到一大段较为静态的内容中有少许动态内容的情况。\n例如，一个显示着菜单栏和当前用户名的页面头部。\n还有一种可能是缓存的内容可能包含每次请求\n都需要执行的 PHP 代码（例如注册资源包的代码）。\n这两个问题都可以使用**动态内容**功能解决。\n\n动态内容的意思是这部分输出的内容不该被缓存，即便是它被包裹在片段缓存中。\n为了使内容保持动态，每次请求都执行 PHP 代码生成，\n即使这些代码已经被缓存了。\n\n可以在片段缓存中调用 [[yii\\base\\View::renderDynamic()]] 去插入动态内容，\n如下所示：\n\n```php\nif ($this->beginCache($id1)) {\n\n    // ...在此生成内容...\n\n    echo $this->renderDynamic('return Yii::$app->user->identity->name;');\n\n    // ...在此生成内容...\n\n    $this->endCache();\n}\n```\n\n[[yii\\base\\View::renderDynamic()|renderDynamic()]] 方法接受一段 PHP 代码作为参数。\n代码的返回值被看作是动态内容。这段代码将在每次请求时都执行，\n无论其外层的片段缓存是否被存储。\n\n> Note: 从版本 2.0.14 开始，动态内容 API 通过 [[yii\\base\\DynamicContentAwareInterface]] 接口及其 [[yii\\base\\DynamicContentAwareTrait]] 特质开放。\n  举个例子，你可以参考 [[yii\\widgets\\FragmentCache]] 类。\n"
  },
  {
    "path": "docs/guide-zh-CN/caching-http.md",
    "content": "HTTP 缓存\n============\n\n除了前面章节讲到的服务器端缓存外， Web 应用还可以利用客户端缓存\n去节省相同页面内容的生成和传输时间。\n\n通过配置 [[yii\\filters\\HttpCache]] 过滤器，控制器操作渲染的内容就能缓存在客户端。\n[[yii\\filters\\HttpCache|HttpCache]] 过滤器仅对 `GET` 和 `HEAD` 请求生效，\n它能为这些请求设置三种与缓存有关的 HTTP 头。\n\n* [[yii\\filters\\HttpCache::lastModified|Last-Modified]]\n* [[yii\\filters\\HttpCache::etagSeed|Etag]]\n* [[yii\\filters\\HttpCache::cacheControlHeader|Cache-Control]]\n\n\n## `Last-Modified` 头 <span id=\"last-modified\"></span>\n\n`Last-Modified` 头使用时间戳标明页面自上次客户端缓存后是否被修改过。\n\n通过配置 [[yii\\filters\\HttpCache::lastModified]] 属性向客户端发送 `Last-Modified` 头。\n该属性的值应该为 PHP callable 类型，返回的是页面修改时的 Unix 时间戳。\n该 callable 的参数和返回值应该如下：\n\n```php\n/**\n * @param Action $action 当前处理的动作对象\n * @param array $params “params” 属性的值\n * @return int 页面修改时的 Unix 时间戳\n */\nfunction ($action, $params)\n```\n\n以下是使用 `Last-Modified` 头的示例：\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\HttpCache',\n            'only' => ['index'],\n            'lastModified' => function ($action, $params) {\n                $q = new \\yii\\db\\Query();\n                return $q->from('post')->max('updated_at');\n            },\n        ],\n    ];\n}\n```\n\n上述代码表明 HTTP 缓存只在 `index` 操作时启用。\n它会基于页面最后修改时间生成一个 `Last-Modified` HTTP 头。\n当浏览器第一次访问 `index` 页时，服务器将会生成页面并发送至客户端浏览器。\n之后客户端浏览器在页面没被修改期间访问该页，\n服务器将不会重新生成页面，浏览器会使用之前客户端缓存下来的内容。\n因此服务端渲染和内容传输都将省去。\n\n\n## `ETag` 头 <span id=\"etag\"></span>\n\n“Entity Tag”（实体标签，简称 ETag）使用一个哈希值表示页面内容。如果页面被修改过，\n哈希值也会随之改变。通过对比客户端的哈希值和服务器端生成的哈希值，\n浏览器就能判断页面是否被修改过，进而决定是否应该重新传输内容。\n\n通过配置 [[yii\\filters\\HttpCache::etagSeed]] 属性向客户端发送 `ETag` 头。\n该属性的值应该为 PHP callable 类型，返回的是一段种子字符用来生成 ETag 哈希值。\n该 callable 的参数和返回值应该如下：\n\n```php\n/**\n * @param Action $action 当前处理的动作对象\n * @param array $params “params” 属性的值\n * @return string 一段种子字符用来生成 ETag 哈希值\n */\nfunction ($action, $params)\n```\n\n以下是使用 `ETag` 头的示例：\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\HttpCache',\n            'only' => ['view'],\n            'etagSeed' => function ($action, $params) {\n                $post = $this->findModel(\\Yii::$app->request->get('id'));\n                return serialize([$post->title, $post->content]);\n            },\n        ],\n    ];\n}\n```\n\n上述代码表明 HTTP 缓存只在 `view` 操作时启用。\n它会基于用户请求的标题和内容生成一个 `ETag` HTTP 头。\n当浏览器第一次访问 `view` 页时，服务器将会生成页面并发送至客户端浏览器。\n之后客户端浏览器标题和内容没被修改在期间访问该页，服务器将不会重新生成页面，\n浏览器会使用之前客户端缓存下来的内容。\n因此服务端渲染和内容传输都将省去。\n\nETag 相比 `Last-Modified` 能实现更复杂和更精确的缓存策略。\n例如，当站点切换到另一个主题时可以使 ETag 失效。\n\n复杂的 Etag 生成种子可能会违背使用 `HttpCache` 的初衷而引起不必要的性能开销，\n因为响应每一次请求都需要重新计算 Etag。\n请试着找出一个最简单的表达式去触发 Etag 失效。\n\n> Note: 为了遵循 [RFC 7232（HTTP 1.1 协议）](https://datatracker.ietf.org/doc/html/rfc7232#section-2.4)，\n如果同时配置了 `ETag` 和 `Last-Modified` 头，`HttpCache` 将会同时发送它们。\n并且如果客户端同时发送 `If-None-Match` 头和 `If-Modified-Since` 头，\n则只有前者会被接受。\n\n\n## `Cache-Control` 头 <span id=\"cache-control\"></span>\n\n`Cache-Control` 头指定了页面的常规缓存策略。\n可以通过配置 [[yii\\filters\\HttpCache::cacheControlHeader]] \n属性发送相应的头信息。默认发送以下头：\n\n```\nCache-Control: public, max-age=3600\n```\n\n## 会话缓存限制器 <span id=\"session-cache-limiter\"></span>\n\n当页面使 session 时，PHP 将会按照 PHP.INI \n中所设置的 `session.cache_limiter` 值自动发送一些缓存相关的 HTTP 头。\n这些 HTTP 头有可能会干扰你原本设置的 `HttpCache` 或让其失效。\n为了避免此问题，默认情况下 `HttpCache` 禁止自动发送这些头。\n想改变这一行为，可以配置 [[yii\\filters\\HttpCache::sessionCacheLimiter]] 属性。\n该属性接受一个字符串值，包括 `public`，`private`，`private_no_expire`，和 `nocache`。\n请参考 PHP 手册中的[缓存限制器](https://www.php.net/manual/zh/function.session-cache-limiter.php)\n了解这些值的含义。\n\n\n## SEO 影响 <span id=\"seo-implications\"></span>\n\n搜索引擎趋向于遵循站点的缓存头。因为一些爬虫的抓取频率有限制，\n启用缓存头可以可以减少重复请求数量，增加爬虫抓取效率\n（译者：大意如此，但搜索引擎的排名规则不了解，好的缓存策略应该是可以为用户体验加分的）。\n\n"
  },
  {
    "path": "docs/guide-zh-CN/caching-overview.md",
    "content": "缓存\n=======\n\n缓存是提升 Web 应用性能简便有效的方式。\n通过将相对静态的数据存储到缓存并在收到请求时取回缓存，\n应用程序便节省了每次重新生成这些数据所需的时间。\n\n缓存可以应用在 Web 应用程序的任何层级任何位置。\n在服务器端，在较的低层面，缓存可能用于存储基础数据，例如从数据库中取出的最新文章列表；\n在较高的层面，缓存可能用于存储一段或整个 Web 页面，\n例如最新文章的渲染结果。在客户端，HTTP 缓存可能用于\n将最近访问的页面内容存储到浏览器缓存中。\n\nYii 支持如上所有缓存机制：\n\n* [数据缓存](caching-data.md)\n* [片段缓存](caching-fragment.md)\n* [页面缓存](caching-page.md)\n* [HTTP 缓存](caching-http.md)\n"
  },
  {
    "path": "docs/guide-zh-CN/caching-page.md",
    "content": "页面缓存\n============\n\n页面缓存指的是在服务器端缓存整个页面的内容。\n随后当同一个页面被请求时，内容将从缓存中取出，而不是重新生成。\n\n页面缓存由 [[yii\\filters\\PageCache]] 类提供支持，该类是一个[过滤器](structure-filters.md)。\n它可以像这样在控制器类中使用：\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\PageCache',\n            'only' => ['index'],\n            'duration' => 60,\n            'variations' => [\n                \\Yii::$app->language,\n            ],\n            'dependency' => [\n                'class' => 'yii\\caching\\DbDependency',\n                'sql' => 'SELECT COUNT(*) FROM post',\n            ],\n        ],\n    ];\n}\n```\n\n上述代码表示页面缓存只在 `index` 操作时启用，页面内容最多被缓存 60 秒，\n会随着当前应用的语言更改而变化。\n如果文章总数发生变化则缓存的页面会失效。\n\n如你所见，页面缓存和[片段缓存](caching-fragment.md)极其相似。\n它们都支持 `duration`，`dependencies`，`variations` 和 `enabled` 配置选项。\n它们的主要区别是页面缓存是由[过滤器](structure-filters.md)实现，而片段缓存则是一个[小部件](structure-widgets.md)。\n\n你可以在使用页面缓存的同时，\n使用[片段缓存](caching-fragment.md)和[动态内容](caching-fragment.md#dynamic-content)。\n\n"
  },
  {
    "path": "docs/guide-zh-CN/concept-aliases.md",
    "content": "别名（Aliases）\n=============\n\n别名用来表示文件路径和 URL，这样就避免了在代码中硬编码一些绝对路径和 URL。\n一个别名必须以 `@` 字符开头，以区别于传统的文件路径和 URL。\n没有前导 `@` 定义的别名将以 `@` 字符作为前缀。\n\nYii 预定义了大量可用的别名。例如，别名 `@yii` 指的是 Yii 框架本身的安装目录，\n而 `@web` 表示的是当前运行应用的根 URL。\n\n定义别名（Defining Aliases） <span id=\"defining-aliases\"></span>\n-------------------------\n\n你可以调用 [[Yii::setAlias()]] 来给文件路径或 URL 定义别名：\n\n```php\n// 文件路径的别名\nYii::setAlias('@foo', '/path/to/foo');\n\n// URL 的别名\nYii::setAlias('@bar', 'https://www.example.com');\n\n// 包含 \\foo\\Bar 类的具体文件的别名\nYii::setAlias('@foo/Bar.php', '/definitely/not/foo/Bar.php');\n```\n\n> Note: 别名所指向的文件路径或 URL 不一定是真实存在的文件或资源。\n\n可以通过在一个别名后面加斜杠 `/` 和一至多个路径分段生成新别名（无需调用 [[Yii::setAlias()]]）。\n们把通过 [[Yii::setAlias()]] 定义的别名称为**根别名**，\n而用他们衍生出去的别名成为**衍生别名**。例如，`@foo` 就是根别名，\n而 `@foo/bar/file.php` 是一个衍生别名。\n\n你还可以用别名去定义新别名（根别名与衍生别名均可）：\n\n```php\nYii::setAlias('@foobar', '@foo/bar');\n```\n\n根别名通常在[引导](runtime-bootstrapping.md)阶段定义。\n比如你可以在[入口脚本](structure-entry-scripts.md)里调用 [[Yii::setAlias()]]。为了方便起见，\n[应用](structure-applications.md)提供了一个名为 `aliases` 的可写属性，\n你可以在应用[配置](concept-configurations.md)中设置它，就像这样：\n\n```php\nreturn [\n    // ...\n    'aliases' => [\n        '@foo' => '/path/to/foo',\n        '@bar' => 'https://www.example.com',\n    ],\n];\n```\n\n\n解析别名（Resolving Aliases） <span id=\"resolving-aliases\"></span>\n--------------------------\n\n你可以调用 [[Yii::getAlias()]] 命令来解析根别名到对应的文件路径或 URL。\n同样的页面也可以用于解析衍生别名。例如：\n\n```php\necho Yii::getAlias('@foo');               // 输出：/path/to/foo\necho Yii::getAlias('@bar');               // 输出：https://www.example.com\necho Yii::getAlias('@foo/bar/file.php');  // 输出：/path/to/foo/bar/file.php\n```\n\n由衍生别名所解析出的文件路径和 URL \n是通过替换掉衍生别名中的根别名部分得到的。\n\n> Note: [[Yii::getAlias()]] 并不检查结果路径/URL 所指向的资源是否真实存在。\n\n\n根别名可能也会包含斜杠 `/`。\n[[Yii::getAlias()]] 足够智能到判断一个别名中的哪部分是根别名，因此能正确解析文件路径/URL。\n例如：\n\n```php\nYii::setAlias('@foo', '/path/to/foo');\nYii::setAlias('@foo/bar', '/path2/bar');\necho Yii::getAlias('@foo/test/file.php');  // 输出：/path/to/foo/test/file.php\necho Yii::getAlias('@foo/bar/file.php');   // 输出：/path2/bar/file.php\n```\n\n若 `@foo/bar` 未被定义为根别名，最后一行语句会显示为 `/path/to/foo/bar/file.php`。\n\n\n使用别名（Using Aliases） <span id=\"using-aliases\"></span>\n----------------------\n\n别名在 Yii 的很多地方都会被正确识别，\n无需调用 [[Yii::getAlias()]] 来把它们转换为路径/URL。\n例如，[[yii\\caching\\FileCache::cachePath]] 能同时接受文件路径或是指向文件路径的别名，\n因为通过 `@` 前缀能区分它们。\n\n```php\nuse yii\\caching\\FileCache;\n\n$cache = new FileCache([\n    'cachePath' => '@runtime/cache',\n]);\n```\n\n请关注 API 文档了解特定属性或方法参数是否支持别名。\n\n\n预定义的别名（Predefined Aliases） <span id=\"predefined-aliases\"></span>\n------------------------------\n\nYii 预定义了一系列别名来简化常用路径和 URL 的使用：\n\n- `@yii`，`BaseYii.php` 文件所在的目录（也被称为框架安装目录）。\n- `@app`，当前运行的应用 [[yii\\base\\Application::basePath|根路径（base path）]]。\n- `@runtime`，当前运行的应用的 [[yii\\base\\Application::runtimePath|运行环境（runtime）路径]]。默认为 `@app/runtime`。\n- `@webroot`，当前运行的Web应用程序的Web根目录。\n  它是根据包含 [入口脚本](structure-entry-scripts.md) 的目录确定的。\n- `@web`，当前运行的Web应用程序的 base URL。它的值与 [[yii\\web\\Request::baseUrl]] 相同。\n- `@vendor`，[[yii\\base\\Application::vendorPath|Composer vendor 目录]]。\n- `@bower`，包含 [bower 包](https://bower.io/) 的根目录。默认为 `@vendor/bower`。\n- `@npm`，包含 [npm 包](https://www.npmjs.com/) 的根目录。默认为 `@vendor/npm`。\n\n`@yii` 别名是在[入口脚本](structure-entry-scripts.md)里包含 `Yii.php` 文件时定义的，\n其他的别名都是在[配置应用](concept-configurations.md)的时候，\n于应用的构造方法内定义的。\n\n> Note: `@web` 和 `@webroot` 别名，因为它们的描述表明是在 [[yii\\web\\Application|Web application]] 中定义的，因此默认情况下不适用于 [[yii\\console\\Application|Console application]] 应用程序。\n\n扩展的别名（Extension Aliases） <span id=\"extension-aliases\"></span>\n----------------------------\n\n每一个通过 Composer 安装的 [扩展](structure-extensions.md) 都自动添加了一个别名。\n该别名会以该扩展在 `composer.json` 文件中所声明的根命名空间为名，\n且他直接代指该包的根目录。例如，如果你安装有 `yiisoft/yii2-jui` 扩展，会自动得到 `@yii/jui` 别名，\n它定义于[引导启动](runtime-bootstrapping.md)阶段：\n\n```php\nYii::setAlias('@yii/jui', 'VendorPath/yiisoft/yii2-jui');\n```\n"
  },
  {
    "path": "docs/guide-zh-CN/concept-autoloading.md",
    "content": "类自动加载（Autoloading）\n======================\n\nYii 依靠[类自动加载机制](https://www.php.net/manual/zh/language.oop5.autoload.php)来定位和包含所需的类文件。\n它提供一个高性能且完美支持[PSR-4 标准](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md)\n的自动加载器。\n该自动加载器会在引入框架文件 `Yii.php` 时安装好。\n\n> Note: 为了简化叙述，本篇文档中我们只会提及类的自动加载。\n  不过，要记得文中的描述同样也适用于接口和Trait（特质）的自动加载哦。\n\n\n使用 Yii 自动加载器 <span id=\"using-yii-autoloader\"></span>\n-----------------\n\n要使用 Yii  的类自动加载器，你需要在创建和命名类的时候遵循两个简单的规则：\n\n* 每个类都必须置于命名空间之下 (比如 `foo\\bar\\MyClass`)。\n* 每个类都必须保存为单独文件，且其完整路径能用以下算法取得：\n\n```php\n// $className 是一个开头包含反斜杠的完整类名（译注：请自行谷歌：fully qualified class name）\n$classFile = Yii::getAlias('@' . str_replace('\\\\', '/', $className) . '.php');\n```\n\n举例来说，若某个类名为 `foo\\bar\\MyClass`，对应类的文件路径[别名](concept-aliases.md)会是 `@foo/bar/MyClass.php`。\n为了让该别名能被正确解析为文件路径，`@foo` 或 `@foo/bar`\n中的一个必须是[根别名](concept-aliases.md#defining-aliases)。\n\n当我们使用[基本应用模版](start-installation.md)时，可以把你的类放置在顶级命名空间 `app` 下，这样它们就可以被 Yii 自动加载，\n而无需定义一个新的别名。这是因为 `@app` 本身是一个[预定义别名](concept-aliases.md#predefined-aliases)，\n且类似于 `app\\components\\MyClass` 这样的类名，\n基于我们刚才所提到的算法，可以正确解析出 `AppBasePath/components/MyClass.php` 路径。\n\n在[高级应用模版](tutorial-advanced-app.md)里，每一逻辑层级会使用他自己的根别名。\n比如，前端层会使用 `@frontend` 而后端层会使用 `@backend`。\n因此，你可以把前端的类放在 `frontend` 命名空间，而后端的类放在 `backend`。 \n这样这些类就可以被 Yii 自动加载了。\n\n要将自定义命名空间添加到自动加载器，您需要使用 [[Yii::setAlias()]] 为命名空间的根目录定义别名。\n例如，要加载位于 `path/to/foo` 目录中 `foo` 命名空间中的类，您将调用 `Yii::setAlias('@foo', 'path/to/foo')`。\n\n类映射表（Class Map） <span id=\"class-map\"></span>\n------------------\n\nYii 类自动加载器支持**类映射表**功能，该功能会建立一个从类的名字到类文件路径的映射。\n当自动加载器加载一个文件时，他首先检查映射表里有没有该类。\n如果有，对应的文件路径就直接加载了，省掉了进一步的检查。这让类的自动加载变得超级快。\n事实上所有的 Yii 核心类都是这样加载的。\n\n你可以用 `Yii::$classMap` 方法向映射表中添加类，\n\n```php\nYii::$classMap['foo\\bar\\MyClass'] = 'path/to/MyClass.php';\n```\n\n[别名](concept-aliases.md)可以被用于指定类文件的路径。你应该在[引导启动](runtime-bootstrapping.md)的过程中设置类映射表，\n这样映射表就可以在你使用具体类之前就准备好。\n\n\n用其他自动加载器 <span id=\"using-other-autoloaders\"></span>\n-----------------------\n\n因为 Yii 完全支持 Composer 管理依赖包，所以推荐你也同时安装 Composer 的自动加载器，\n如果你用了一些自带自动加载器的第三方类库，\n你应该也安装下它们。\n\n当你同时使用其他自动加载器和 Yii 自动加载器时，应该在其他自动加载器安装成功**之后**，\n再包含 `Yii.php` 文件。这将使 Yii 成为第一个响应任何类自动加载请求的自动加载器。\n举例来说，以下代码提取自[基本应用模版](start-installation.md)的\n[入口脚本](structure-entry-scripts.md) 。\n第一行安装了 Composer 的自动加载器，第二行才是 Yii 的自动加载器：\n\n```php\nrequire __DIR__ . '/../vendor/autoload.php';\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n```\n\n你也可以只使用 Composer 的自动加载，而不用 Yii 的自动加载。\n不过这样做的话，类的加载效率会下降，\n且你必须遵循 Composer 所设定的规则，从而让你的类满足可以被自动加载的要求。\n\n> Info: 若你不想要使用 Yii 的自动加载器，你必须创建一个你自己版本的 `Yii.php` 文件，\n并把它包含进你的[入口脚本](structure-entry-scripts.md)里。\n\n\n自动加载扩展类 <span id=\"autoloading-extension-classes\"></span>\n-----------------------------\n\nYii 自动加载器支持自动加载[扩展](structure-extensions.md)的类。唯一的要求是它需要在 `composer.json` 文件里正确地定义 `autoload` 部分。\n请参考 [Composer 文档](https://getcomposer.org/doc/04-schema.md#autoload)，\n来了解如何正确描述 `autoload` 的更多细节。\n\n在你不使用 Yii 的自动加载器时，Composer 的自动加载器仍然可以帮你自动加载扩展内的类。\n"
  },
  {
    "path": "docs/guide-zh-CN/concept-behaviors.md",
    "content": "行为\n===\n\n行为是 [[yii\\base\\Behavior]] 或其子类的实例。\n行为，也称为 [mixins](https://zh.wikipedia.org/wiki/Mixin)，\n可以无须改变类继承关系即可增强一个已有的 [[yii\\base\\Component|组件]] 类功能。\n当行为附加到组件后，它将“注入”它的方法和属性到组件，\n然后可以像访问组件内定义的方法和属性一样访问它们。\n此外，行为通过组件能响应被触发的[事件](basic-events.md)，从而自定义或调整组件正常执行的代码。\n\n\n定义行为 <span id=\"defining-behaviors\"></span>\n------\n\n要定义行为，通过继承 [[yii\\base\\Behavior]] 或其子类来建立一个类。如：\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Behavior;\n\nclass MyBehavior extends Behavior\n{\n    public $prop1;\n\n    private $_prop2;\n\n    public function getProp2()\n    {\n        return $this->_prop2;\n    }\n\n    public function setProp2($value)\n    {\n        $this->_prop2 = $value;\n    }\n\n    public function foo()\n    {\n        // ...\n    }\n}\n```\n\n以上代码定义了行为类 `app\\components\\MyBehavior` 并为要附加行为的组件提供了两个属性 `prop1` 、 `prop2` 和一个方法 `foo()` 。\n注意属性 `prop2` 是通过 getter `getProp2()` 和 setter `setProp2()` 定义的。\n能这样用是因为 [[yii\\base\\Object]] 是 [[yii\\base\\Behavior]] 的祖先类，此祖先类支持用 getter 和 setter 方法定义[属性](basic-properties.md)\n\n因为这是一个行为类，当它附加到一个组件时，该组件也将具有 `prop1` 和 `prop2` 属性和 `foo()` 方法。\n\n> Tip: 在行为内部可以通过 [[yii\\base\\Behavior::owner]] 属性访问行为已附加的组件。\n\n> Note: 如果 [[yii\\base\\Behavior::__get()]] 和/或 [[yii\\base\\Behavior::__set()]] 行为方法被覆盖，\n> 则需要覆盖 [[yii\\base\\Behavior::canGetProperty()]] 和/或 [[yii\\base\\Behavior::canSetProperty()]]。\n\n处理事件\n-------\n\n如果要让行为响应对应组件的事件触发，\n就应覆写 [[yii\\base\\Behavior::events()]] 方法，如：\n\n```php\nnamespace app\\components;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\base\\Behavior;\n\nclass MyBehavior extends Behavior\n{\n    // 其它代码\n\n    public function events()\n    {\n        return [\n            ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',\n        ];\n    }\n\n    public function beforeValidate($event)\n    {\n        // 处理器方法逻辑\n    }\n}\n```\n\n[[yii\\base\\Behavior::events()|events()]] 方法返回事件列表和相应的处理器。\n上例声明了 [[yii\\db\\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]] 事件和它的处理器 `beforeValidate()` 。\n当指定一个事件处理器时，要使用以下格式之一：\n\n* 指向行为类的方法名的字符串，如上例所示；\n* 对象或类名和方法名的数组，如 `[$object, 'methodName']`；\n* 匿名方法。\n\n处理器的格式如下，其中 `$event` 指向事件参数。\n关于事件的更多细节请参考[事件](basic-events.md)：\n\n```php\nfunction ($event) {\n}\n```\n\n附加行为 <span id=\"attaching-behaviors\"></span>\n----------\n\n可以静态或动态地附加行为到[[yii\\base\\Component|组件]]。前者在实践中更常见。\n\n要静态附加行为，覆写行为要附加的组件类的 [[yii\\base\\Component::behaviors()|behaviors()]] 方法即可。\n[[yii\\base\\Component::behaviors()|behaviors()]] 方法应该返回行为[配置](basic-configs.md)列表。\n每个行为配置可以是行为类名也可以是配置数组。如：\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\nuse app\\components\\MyBehavior;\n\nclass User extends ActiveRecord\n{\n    public function behaviors()\n    {\n        return [\n            // 匿名行为，只有行为类名\n            MyBehavior::class,\n\n            // 命名行为，只有行为类名\n            'myBehavior2' => MyBehavior::class,\n\n            // 匿名行为，配置数组\n            [\n                'class' => MyBehavior::class,\n                'prop1' => 'value1',\n                'prop2' => 'value2',\n            ],\n\n            // 命名行为，配置数组\n            'myBehavior4' => [\n                'class' => MyBehavior::class,\n                'prop1' => 'value1',\n                'prop2' => 'value2',\n            ]\n        ];\n    }\n}\n```\n\n通过指定行为配置数组相应的键可以给行为关联一个名称。这种行为称为**命名行为**。\n上例中，有两个命名行为：`myBehavior2` 和 `myBehavior4` 。如果行为没有指定名称就是**匿名行为**。\n\n\n要动态附加行为，在对应组件里调用 [[yii\\base\\Component::attachBehavior()]] 方法即可，如：\n\n```php\nuse app\\components\\MyBehavior;\n\n// 附加行为对象\n$component->attachBehavior('myBehavior1', new MyBehavior);\n\n// 附加行为类\n$component->attachBehavior('myBehavior2', MyBehavior::class);\n\n// 附加配置数组\n$component->attachBehavior('myBehavior3', [\n    'class' => MyBehavior::class,\n    'prop1' => 'value1',\n    'prop2' => 'value2',\n]);\n```\n\n可以通过 [[yii\\base\\Component::attachBehaviors()]] 方法一次附加多个行为：\n\n```php\n$component->attachBehaviors([\n    'myBehavior1' => new MyBehavior,  // 命名行为\n    MyBehavior::class,          // 匿名行为\n]);\n```\n\n还可以通过[配置](concept-configurations.md)去附加行为：\n\n```php\n[\n    'as myBehavior2' => MyBehavior::class,\n\n    'as myBehavior3' => [\n        'class' => MyBehavior::class,\n        'prop1' => 'value1',\n        'prop2' => 'value2',\n    ],\n]\n```\n\n详情请参考\n[配置](concept-configurations.md#configuration-format)章节。\n\n使用行为 <span id=\"using-behaviors\"></span>\n-------\n\n使用行为，必须像前文描述的一样先把它附加到 [[yii\\base\\Component|component]] 类或其子类。一旦行为附加到组件，就可以直接使用它。\n\n行为附加到组件后，可以通过组件访问一个行为的**公共**成员变量\n或 getter 和 setter 方法定义的[属性](concept-properties.md)：\n\n```php\n// \"prop1\" 是定义在行为类的属性\necho $component->prop1;\n$component->prop1 = $value;\n```\n\n类似地也可以调用行为的**公共**方法：\n\n```php\n// foo() 是定义在行为类的公共方法\n$component->foo();\n```\n\n如你所见，尽管 `$component` 未定义 `prop1` 和 `foo()` ，\n它们用起来也像组件自己定义的一样。\n\n如果两个行为都定义了一样的属性或方法，并且它们都附加到同一个组件，\n那么**首先**附加上的行为在属性或方法被访问时有优先权。\n\n附加行为到组件时的命名行为，可以使用这个名称来访问行为对象，\n如下所示：\n\n```php\n$behavior = $component->getBehavior('myBehavior');\n```\n\n也能获取附加到这个组件的所有行为：\n\n```php\n$behaviors = $component->getBehaviors();\n```\n\n\n移除行为 <span id=\"detaching-behaviors\"></span>\n-------\n\n要移除行为，可以调用 [[yii\\base\\Component::detachBehavior()]] 方法用行为相关联的名字实现：\n\n```php\n$component->detachBehavior('myBehavior1');\n```\n\n也可以移除*全部*行为：\n\n```php\n$component->detachBehaviors();\n```\n\n\n使用 `TimestampBehavior` <span id=\"using-timestamp-behavior\"></span>\n-----------------------\n\n最后以 [[yii\\behaviors\\TimestampBehavior]] 的讲解来结尾，\n这个行为支持在 [[yii\\db\\ActiveRecord|Active Record]] \n存储时自动更新它的时间戳属性。\n\n首先，附加这个行为到计划使用该行为的 [[yii\\db\\ActiveRecord|Active Record]] 类：\n\n```php\nnamespace app\\models\\User;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\behaviors\\TimestampBehavior;\n\nclass User extends ActiveRecord\n{\n    // ...\n\n    public function behaviors()\n    {\n        return [\n            [\n                'class' => TimestampBehavior::class,\n                'attributes' => [\n                    ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],\n                    ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'],\n                ],\n                // if you're using datetime instead of UNIX timestamp:\n                // 'value' => new Expression('NOW()'),\n            ],\n        ];\n    }\n}\n```\n\n以上指定的行为数组：\n\n* 当记录插入时，行为将当前时间戳赋值给 \n  `created_at` 和 `updated_at` 属性；\n* 当记录更新时，行为将当前时间戳赋值给 `updated_at` 属性。\n\n> Note: 对于上述实现使用MySQL数据库，请将列 (`created_at`, `updated_at`) 定义为 int(11) 作为 UNIX 时间戳。\n\n有了以上这段代码，如果你有一个 `User` 对象并且试图保存它，你会发现它的 `created_at` 和 `updated_at`\n被当前的UNIX时间戳自动填充：\n\n```php\n$user = new User;\n$user->email = 'test@example.com';\n$user->save();\necho $user->created_at;  // 显示当前时间戳\n```\n\n[[yii\\behaviors\\TimestampBehavior|TimestampBehavior]] 行为还提供了一个有用的方法 \n[[yii\\behaviors\\TimestampBehavior::touch()|touch()]]，\n这个方法能将当前时间戳赋值给指定属性并保存到数据库：\n\n```php\n$user->touch('login_time');\n```\n\n其它行为\n-------\n\n有几种内置和外部行为可用：\n\n- [[yii\\behaviors\\BlameableBehavior]] - 使用当前用户 ID 自动填充指定的属性。\n- [[yii\\behaviors\\SluggableBehavior]] - 自动填充指定的属性，其值可以在 URL\n  中用作 slug。\n- [[yii\\behaviors\\AttributeBehavior]] - 在发生特定事件时自动为 ActiveRecord 对象的一个或多个属性\n  指定一个指定的值。\n- [yii2tech\\ar\\softdelete\\SoftDeleteBehavior](https://github.com/yii2tech/ar-softdelete) - 提供软删除和软恢复 ActiveRecord 的\n  方法。即将记录标记为已删除的设置标记或状态。\n- [yii2tech\\ar\\position\\PositionBehavior](https://github.com/yii2tech/ar-position) - 允许通过提供重新排序方法来\n  管理整数字段中的记录顺序。\n\n比较行为与 Traits <span id=\"comparison-with-traits\"></span>\n----------------------\n\n虽然行为类似于 [traits](https://www.php.net/manual/zh/language.oop5.traits.php)，它们都将自己的属性和方法“注入”到主类中，\n但它们在许多方面有所不同。如下所述，他们都有优点和缺点。\n它们更像互补类而非替代类。\n\n\n### 使用行为的原因 <span id=\"pros-for-behaviors\"></span>\n\n行为类像普通类支持继承。另一方面，traits 可以视为 PHP 语言支持的复制粘贴功能，\n它不支持继承。\n\n行为无须修改组件类就可动态附加到组件或移除。\n要使用 traits，必须修改使用它的类。\n\n行为是可配置的，而 traits 则不可行。\n\n行为可以通过响应事件来定制组件的代码执行。\n\n当附属于同一组件的不同行为之间可能存在名称冲突时，\n通过优先考虑附加到该组件的行为，\n自动解决冲突。由不同 traits 引起的名称冲突需要通过\n重命名受影响的属性或方法进行手动解决。\n\n\n### 使用 Traits 的原因 <span id=\"pros-for-traits\"></span>\n\nTraits 比行为更有效，因为行为是既需要时间又需要内存的对象。\n\n因为 IDE 是一种本地语言结构，所以它们对 Traits 更友好。\n\n"
  },
  {
    "path": "docs/guide-zh-CN/concept-components.md",
    "content": "组件（Component）\n==============\n\n组件是 Yii 应用的主要基石。是 [[yii\\base\\Component]] 类或其子类的实例。\n三个用以区分它和其它类的主要功能有：\n\n* [属性（Property）](concept-properties.md)\n* [事件（Event）](concept-events.md)\n* [行为（Behavior）](concept-behaviors.md)\n \n或单独使用，或彼此配合，这些功能的应用让 Yii 的类变得更加灵活和易用。\n以小部件 [[yii\\jui\\DatePicker|日期选择器]] 来举例，\n这是个方便你在 [视图](structure-view.md) 中生成一个交互式日期选择器的 UI 组件：\n\n```php\nuse yii\\jui\\DatePicker;\n\necho DatePicker::widget([\n    'language' => 'zh-CN',\n    'name'  => 'country',\n    'clientOptions' => [\n        'dateFormat' => 'yy-mm-dd',\n    ],\n]);\n```\n\n这个小部件继承自 [[yii\\base\\Component]]，它的各项属性改写起来会很容易。\n\n正是因为组件功能的强大，他们比常规的对象（Object）稍微重量级一点，因为他们要使用额外的内存和 CPU 时间来处理\n[事件](concept-events.md) 和 [行为](concept-behaviors.md) 。\n如果你不需要这两项功能，可以继承 [[yii\\base\\Object]] \n而不是 [[yii\\base\\Component]]。这样组件可以像普通 PHP 对象一样高效，\n同时还支持[属性（Property）](concept-properties.md)功能。\n\n当继承 [[yii\\base\\Component]] 或 [[yii\\base\\Object]] 时，\n推荐你使用如下的编码风格：\n\n- 若你需要重写构造方法（Constructor），传入 `$config` 作为构造器方法*最后一个*参数，\n  然后把它传递给父类的构造方法。\n- 永远在你重写的构造方法*结尾处*调用一下父类的构造方法。\n- 如果你重写了 [[yii\\base\\BaseObject::init()]] 方法，请确保你在 `init` 方法的*开头处*调用了父类的 `init` 方法。\n\n例子如下：\n\n```php\n<?php\n\nnamespace yii\\components\\MyClass;\n\nuse yii\\base\\BaseObject;\n\nclass MyClass extends BaseObject\n{\n    public $prop1;\n    public $prop2;\n\n    public function __construct($param1, $param2, $config = [])\n    {\n        // ... 在应用配置之前初始化\n\n        parent::__construct($config);\n    }\n\n    public function init()\n    {\n        parent::init();\n\n        // ... 应用配置后进行初始化\n    }\n}\n```\n\n另外，为了让组件可以在创建实例时[能被正确配置](concept-configurations.md)，请遵照以下操作流程：\n\n```php\n$component = new MyClass(1, 2, ['prop1' => 3, 'prop2' => 4]);\n// 方法二：\n$component = \\Yii::createObject([\n    'class' => MyClass::class,\n    'prop1' => 3,\n    'prop2' => 4,\n], [1, 2]);\n```\n\n> Info: 尽管调用 [[Yii::createObject()]] 的方法看起来更加复杂，但这主要因为它更加灵活强大，\n> 它是基于[依赖注入容器](concept-di-container.md)实现的。\n  \n\n[[yii\\base\\BaseObject]] 类执行时的生命周期如下：\n\n1. 构造方法内的预初始化过程。你可以在这儿给各属性设置缺省值。\n2. 通过 `$config` 配置对象。配置的过程可能会覆盖掉先前在构造方法内设置的默认值。\n3. 在 [[yii\\base\\BaseObject::init()|init()]] 方法内进行初始化后的收尾工作。你可以通过重写此方法，进行一些良品检验，属性的初始化之类的工作。\n4. 对象方法调用。\n\n前三步都是在对象的构造方法内发生的。这意味着一旦你获得了一个对象实例（即一个对象），\n那么它就已经初始化就绪可供使用。\n"
  },
  {
    "path": "docs/guide-zh-CN/concept-configurations.md",
    "content": "配置（Configurations）\n====================\n\n在 Yii 中，创建新对象和初始化已存在对象时广泛使用配置。\n配置通常包含被创建对象的类名和一组将要赋值给对象\n[属性](concept-properties.md)的初始值。\n还可能包含一组将被附加到对象[事件](concept-events.md)上的句柄。\n和一组将被附加到对象上的[行为](concept-behaviors.md)。\n\n以下代码中的配置被用来创建并初始化一个数据库连接：\n\n```php\n$config = [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n];\n\n$db = Yii::createObject($config);\n```\n\n[[Yii::createObject()]] 方法接受一个配置数组并根据数组中指定的类名创建对象。\n对象实例化后，剩余的参数被用来初始化对象的属性，\n事件处理和行为。\n\n对于已存在的对象，可以使用 [[Yii::configure()]] 方法根据配置去初始化其属性，\n就像这样：\n\n```php\nYii::configure($object, $config);\n```\n\n请注意，如果配置一个已存在的对象，那么配置数组中不应该包含指定类名的 `class` 元素。\n\n\n## 配置的格式（Configuration Format） <span id=\"configuration-format\"></span>\n\n一个配置的格式可以描述为以下形式：\n\n```php\n[\n    'class' => 'ClassName',\n    'propertyName' => 'propertyValue',\n    'on eventName' => $eventHandler,\n    'as behaviorName' => $behaviorConfig,\n]\n```\n\n其中\n\n* `class` 元素指定了将要创建的对象的完全限定类名。\n* `propertyName` 元素指定了对象属性的初始值。键名是属性名，值是该属性对应的初始值。\n  只有公共成员变量以及通过 getter/setter 定义的\n  [属性](concept-properties.md)可以被配置。\n* `on eventName` 元素指定了附加到对象[事件](concept-events.md)上的句柄是什么。\n  请注意，数组的键名由 `on ` 前缀加事件名组成。\n  请参考[事件](concept-events.md)章节了解事件句柄格式。\n* `as behaviorName` 元素指定了附加到对象的[行为](concept-behaviors.md)。\n  请注意，数组的键名由 `as ` 前缀加行为名组成。`$behaviorConfig` \n  值表示创建行为的配置信息，格式与我们之前描述的配置格式一样。\n\n下面是一个配置了初始化属性值，事件句柄和行为的示例：\n\n```php\n[\n    'class' => 'app\\components\\SearchEngine',\n    'apiKey' => 'xxxxxxxx',\n    'on search' => function ($event) {\n        Yii::info(\"搜索的关键词： \" . $event->keyword);\n    },\n    'as indexer' => [\n        'class' => 'app\\components\\IndexerBehavior',\n        // ... 初始化属性值 ...\n    ],\n]\n```\n\n\n## 使用配置（Using Configurations） <span id=\"using-configurations\"></span>\n\nYii 中的配置可以用在很多场景。本章开头我们展示了如何使用 [[Yii::creatObject()]] \n根据配置信息创建对象。本小节将介绍配置的两种\n主要用法 —— 配置应用与配置小部件。\n\n\n### 应用的配置（Application Configurations） <span id=\"application-configurations\"></span>\n\n[应用](structure-applications.md)的配置可能是最复杂的配置之一。\n因为 [[yii\\web\\Application|application]] 类拥有很多可配置的属性和事件。\n更重要的是它的 [[yii\\web\\Application::components|components]] \n属性可以接收配置数组并通过应用注册为组件。\n以下是一个针对[基础应用模板](start-installation.md)的应用配置概要：\n\n```php\n$config = [\n    'id' => 'basic',\n    'basePath' => dirname(__DIR__),\n    'extensions' => require __DIR__ . '/../vendor/yiisoft/extensions.php',\n    'components' => [\n        'cache' => [\n            'class' => 'yii\\caching\\FileCache',\n        ],\n        'mailer' => [\n            'class' => 'yii\\swiftmailer\\Mailer',\n        ],\n        'log' => [\n            'class' => 'yii\\log\\Dispatcher',\n            'traceLevel' => YII_DEBUG ? 3 : 0,\n            'targets' => [\n                [\n                    'class' => 'yii\\log\\FileTarget',\n                ],\n            ],\n        ],\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=stay2',\n            'username' => 'root',\n            'password' => '',\n            'charset' => 'utf8',\n        ],\n    ],\n];\n```\n\n配置中没有 `class` 键的原因是这段配置应用在下面的入口脚本中，\n类名已经指定了。\n\n```php\n(new yii\\web\\Application($config))->run();\n```\n\n更多关于应用 `components` 属性配置的信息可以查阅[应用](structure-applications.md)\n以及[服务定位器](concept-service-locator.md)章节。\n\n自版本 2.0.11 开始，系统配置支持使用 `container` 属性来配置[依赖注入容器](concept-di-container.md)\n例如：\n\n```php\n$config = [\n    'id' => 'basic',\n    'basePath' => dirname(__DIR__),\n    'extensions' => require __DIR__ . '/../vendor/yiisoft/extensions.php',\n    'container' => [\n        'definitions' => [\n            'yii\\widgets\\LinkPager' => ['maxButtonCount' => 5]\n        ],\n        'singletons' => [\n            // 依赖注入容器单例配置\n        ]\n    ]\n];\n```\n\n请参考 [依赖注入容器](concept-di-container.md) 下面的 [高级应用实例](concept-di-container.md#advanced-practical-usage)\n获取更多 `definitions` 和 `singletons` 配置项和实际使用的例子。\n\n\n### 小部件的配置（Widget Configurations） <span id=\"widget-configurations\"></span>\n\n使用[小部件](structure-widgets.md)时，常常需要配置以便自定义其属性。\n[[yii\\base\\Widget::widget()]] 和  [[yii\\base\\Widget::begin()]] 方法都可以用来创建小部件。\n它们可以接受配置数组：\n\n```php\nuse yii\\widgets\\Menu;\n\necho Menu::widget([\n    'activateItems' => false,\n    'items' => [\n        ['label' => 'Home', 'url' => ['site/index']],\n        ['label' => 'Products', 'url' => ['product/index']],\n        ['label' => 'Login', 'url' => ['site/login'], 'visible' => Yii::$app->user->isGuest],\n    ],\n]);\n```\n\n上述代码创建了一个小部件 `Menu` 并将其 `activateItems` 属性初始化为 false。\n`item` 属性也配置成了将要显示的菜单条目。\n\n请注意，代码中已经给出了类名 `yii\\widgets\\Menu`，配置数组**不应该**再包含 `class` 键。\n\n\n## 配置文件（Configuration Files） <span id=\"configuration-files\"></span>\n\n当配置的内容十分复杂，通用做法是将其存储在一或多个 PHP 文件中，\n这些文件被称为*配置文件*。一个配置文件返回的是 PHP 数组。\n例如，像这样把应用配置信息存储在名为 `web.php` 的文件中：\n\n```php\nreturn [\n    'id' => 'basic',\n    'basePath' => dirname(__DIR__),\n    'extensions' => require __DIR__ . '/../vendor/yiisoft/extensions.php',\n    'components' => require __DIR__ . '/components.php',\n];\n```\n\n鉴于 `components` 配置也很复杂，上述代码把它们存储在单独的 `components.php` 文件中，并且包含在 `web.php` 里。\n`components.php` 的内容如下：\n\n```php\nreturn [\n    'cache' => [\n        'class' => 'yii\\caching\\FileCache',\n    ],\n    'mailer' => [\n        'class' => 'yii\\swiftmailer\\Mailer',\n    ],\n    'log' => [\n        'class' => 'yii\\log\\Dispatcher',\n        'traceLevel' => YII_DEBUG ? 3 : 0,\n        'targets' => [\n            [\n                'class' => 'yii\\log\\FileTarget',\n            ],\n        ],\n    ],\n    'db' => [\n        'class' => 'yii\\db\\Connection',\n        'dsn' => 'mysql:host=localhost;dbname=stay2',\n        'username' => 'root',\n        'password' => '',\n        'charset' => 'utf8',\n    ],\n];\n```\n\n仅仅需要 “require”，就可以取得一个配置文件的配置内容，像这样：\n\n```php\n$config = require 'path/to/web.php';\n(new yii\\web\\Application($config))->run();\n```\n\n\n## 默认配置（Default Configurations） <span id=\"default-configurations\"></span>\n\n[[Yii::createObject()]] 方法基于[依赖注入容器](concept-di-container.md)实现。\n使用 [[Yii::creatObject()]] 创建对象时，可以附加一系列**默认配置**到指定类的任何实例。\n默认配置还可以在[入口脚本](runtime-bootstrapping.md)\n中调用 `Yii::$container->set()` 来定义。\n\n例如，如果你想自定义 [[yii\\widgets\\LinkPager]] 小部件，以便让分页器最多只显示 5 个翻页按钮（默认是 10 个），\n你可以用下述代码实现：\n\n```php\n\\Yii::$container->set('yii\\widgets\\LinkPager', [\n    'maxButtonCount' => 5,\n]);\n```\n\n不使用默认配置的话，你就得在任何使用分页器的地方，\n都配置 `maxButtonCount` 的值。\n\n\n## 环境常量（Environment Constants） <span id=\"environment-constants\"></span>\n\n配置经常要随着应用运行的不同环境更改。例如在开发环境中，\n你可能使用名为 `mydb_dev` 的数据库，\n而生产环境则使用 `mydb_prod` 数据库。\n为了便于切换使用环境，Yii 提供了一个定义在入口脚本中的 `YII_ENV` 常量。\n如下：\n\n```php\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n```\n\n你可以把 `YII_ENV` 定义成以下任何一种值：\n\n- `prod`：生产环境。常量 `YII_ENV_PROD` 将被看作 true。\n  如果你没修改过，这就是 `YII_ENV` 的默认值。\n- `dev`：开发环境。常量 `YII_ENV_DEV` 将被看作 true。\n- `test`：测试环境。常量 `YII_ENV_TEST` 将被看作 true。\n\n有了这些环境常量，你就可以根据当下应用运行环境的不同，进行差异化配置。\n例如，应用可以包含下述代码只在开发环境中开启\n[调试工具](tool-debugger.md)。\n\n```php\n$config = [...];\n\nif (YII_ENV_DEV) {\n    // 根据 `dev` 环境进行的配置调整\n    $config['bootstrap'][] = 'debug';\n    $config['modules']['debug'] = 'yii\\debug\\Module';\n}\n\nreturn $config;\n```\n"
  },
  {
    "path": "docs/guide-zh-CN/concept-di-container.md",
    "content": "依赖注入容器（Dependency Injection Container）\n===========================================\n\n依赖注入（Dependency Injection，DI）容器就是一个对象，它知道怎样初始化并配置对象及其依赖的所有对象。\n[Martin 的文章](https://martinfowler.com/articles/injection.html) 已经解释了 DI 容器为什么很有用。\n这里我们主要讲解 Yii 提供的 DI 容器的使用方法。\n\n\n依赖注入（Dependency Injection） <span id=\"dependency-injection\"></span>\n-----------------------------\n\nYii 通过 [[yii\\di\\Container]] 类提供 DI 容器特性。\n它支持如下几种类型的依赖注入：\n\n* 构造方法注入;\n* 方法注入;\n* Setter 和属性注入;\n* PHP 回调注入.\n\n\n### 构造方法注入（Constructor Injection） <span id=\"constructor-injection\"></span>\n\n在参数类型提示的帮助下，DI 容器实现了构造方法注入。当容器被用于创建一个新对象时，\n类型提示会告诉它要依赖什么类或接口。\n容器会尝试获取它所依赖的类或接口的实例，\n然后通过构造器将其注入新的对象。例如：\n\n```php\nclass Foo\n{\n    public function __construct(Bar $bar)\n    {\n    }\n}\n\n$foo = $container->get('Foo');\n// 上面的代码等价于：\n$bar = new Bar;\n$foo = new Foo($bar);\n```\n\n\n### 方法注入（Method Injection） <span id=\"method-injection\"></span>\n\n通常，类的依赖关系传递给构造函数，并且在整个生命周期中都可以在类内部使用。\n通过方法注入，可以提供仅由类的单个方法需要的依赖关系，\n并将其传递给构造函数可能不可行，或者可能会在大多数用例中导致太多开销。\n\n类方法可以像下面例子中的 `doSomething()` 方法一样定义：\n\n```php\nclass MyClass extends \\yii\\base\\Component\n{\n    public function __construct(/*Some lightweight dependencies here*/, $config = [])\n    {\n        // ...\n    }\n\n    public function doSomething($param1, \\my\\heavy\\Dependency $something)\n    {\n        // do something with $something\n    }\n}\n```\n\n你可以自己通过一个实例 `\\my\\heavy\\Dependency` 调用这个方法或使用 [[yii\\di\\Container::invoke()]] 如下：\n\n```php\n$obj = new MyClass(/*...*/);\nYii::$container->invoke([$obj, 'doSomething'], ['param1' => 42]); // $something will be provided by the DI container\n```\n\n### Setter 和属性注入（Setter and Property Injection） <span id=\"setter-and-property-injection\"></span>\n\nSetter 和属性注入是通过[配置](concept-configurations.md)提供支持的。\n当注册一个依赖或创建一个新对象时，你可以提供一个配置，\n该配置会提供给容器用于通过相应的 Setter 或属性注入依赖。\n例如：\n\n```php\nuse yii\\base\\BaseObject;\n\nclass Foo extends BaseObject\n{\n    public $bar;\n\n    private $_qux;\n\n    public function getQux()\n    {\n        return $this->_qux;\n    }\n\n    public function setQux(Qux $qux)\n    {\n        $this->_qux = $qux;\n    }\n}\n\n$container->get('Foo', [], [\n    'bar' => $container->get('Bar'),\n    'qux' => $container->get('Qux'),\n]);\n```\n\n> Info: [[yii\\di\\Container::get()]] 方法将其第三个参数作为配置数组应用于正在创建的对象。\n  如果该类实现 [[yii\\base\\Configurable]] 接口（例如\n  [[yii\\base\\BaseObject]]），则配置数组将作为最后一个参数传递给类构造函数；\n  否则，将在创建对象*后*应用该配置。\n\n\n### PHP 回调注入（PHP Callable Injection） <span id=\"php-callable-injection\"></span>\n\n在这种情况下，容器将使用已注册的 PHP 回调来构建类的新实例。\n每次调用 [[yii\\di\\Container::get()]] ，相应的回调将被调用。\n调用方负责解析依赖项，并适当地将它们注入到新创建的对象中。\n例如,\n\n```php\n$container->set('Foo', function () {\n    $foo = new Foo(new Bar);\n    // ... 其他初始化 ...\n    return $foo;\n});\n\n$foo = $container->get('Foo');\n```\n\n要省略构建新对象的复杂逻辑，可以使用静态类方法作为可调用的方法。例如，\n\n```php\nclass FooBuilder\n{\n    public static function build()\n    {\n        $foo = new Foo(new Bar);\n        // ... 其他初始化 ...\n        return $foo;\n    }\n}\n\n$container->set('Foo', ['app\\helper\\FooBuilder', 'build']);\n\n$foo = $container->get('Foo');\n```\n\n这样做的话，想要配置 `Foo` 类的人不再需要知道它是如何构建的。\n\n\n注册依赖关系（Registering Dependencies） <span id=\"registering-dependencies\"></span>\n------------------------------------\n\n可以用 [[yii\\di\\Container::set()]] 注册依赖关系。注册会用到一个依赖关系名称和一个依赖关系的定义。\n依赖关系名称可以是一个类名，一个接口名或一个别名。\n依赖关系的定义可以是一个类名，一个配置数组，或者一个 PHP 回调。\n\n```php\n$container = new \\yii\\di\\Container;\n\n// 注册一个同类名一样的依赖关系，这个可以省略。\n$container->set('yii\\db\\Connection');\n\n// 注册一个接口\n// 当一个类依赖这个接口时，\n//相应的类会被初始化作为依赖对象。\n$container->set('yii\\mail\\MailInterface', 'yii\\swiftmailer\\Mailer');\n\n// 注册一个别名。\n// 你可以使用 $container->get('foo') 创建一个 Connection 实例\n$container->set('foo', 'yii\\db\\Connection');\n\n// 通过配置注册一个类\n// 通过 get() 初始化时，配置将会被使用。\n$container->set('yii\\db\\Connection', [\n    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n]);\n\n// 通过类的配置注册一个别名\n// 这种情况下，需要通过一个 “class” 元素指定这个类\n$container->set('db', [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n]);\n\n// 注册一个 PHP 回调\n// 每次调用 $container->get('db') 时，回调函数都会被执行。\n$container->set('db', function ($container, $params, $config) {\n    return new \\yii\\db\\Connection($config);\n});\n\n// 注册一个组件实例\n// $container->get('pageCache') 每次被调用时都会返回同一个实例。\n$container->set('pageCache', new FileCache);\n```\n\n> Tip: 如果依赖关系名称和依赖关系的定义相同，\n  则不需要通过 DI 容器注册该依赖关系。\n\n通过 `set()` 注册的依赖关系，在每次使用时都会产生一个新实例。\n可以使用 [[yii\\di\\Container::setSingleton()]] \n注册一个单例的依赖关系：\n\n```php\n$container->setSingleton('yii\\db\\Connection', [\n    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n]);\n```\n\n\n解决依赖关系（Resolving Dependencies） <span id=\"resolving-dependencies\"></span>\n----------------------------------\n\n注册依赖关系后，就可以使用 DI 容器创建新对象了。容器会自动解决依赖关系，\n将依赖实例化并注入新创建的对象。依赖关系的解决是递归的，\n如果一个依赖关系中还有其他依赖关系，\n则这些依赖关系都会被自动解决。\n\n可以使用 [[yii\\di\\Container::get()]] 创建新的对象。\n该方法接收一个依赖关系名称，它可以是一个类名，\n一个接口名或一个别名。依赖关系名或许是通过 `set()` 或 `setSingleton()` 注册的。\n你可以随意地提供一个类的构造器参数列表和一个\n[configuration](concept-configurations.md) 用于配置新创建的对象。\n\n例如：\n\n```php\n// \"db\" 是前面定义过的一个别名\n$db = $container->get('db');\n\n// 等价于： $engine = new \\app\\components\\SearchEngine($apiKey, ['type' => 1]);\n$engine = $container->get('app\\components\\SearchEngine', [$apiKey], ['type' => 1]);\n```\n\n代码背后，DI 容器做了比创建对象多的多的工作。\n容器首先将检查类的构造方法，找出依赖的类或接口名，\n然后自动递归解决这些依赖关系。\n\n如下代码展示了一个更复杂的示例。`UserLister` 类依赖一个实现了 `UserFinderInterface` 接口的对象；\n`UserFinder` 类实现了这个接口，并依赖于一个 `Connection` 对象。\n所有这些依赖关系都是通过类构造器参数的类型提示定义的。\n通过属性依赖关系的注册，DI 容器可以自动解决这些依赖关系并能通过一个\n简单的 `get('userLister')` 调用创建一个新的 `UserLister` 实例。\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\BaseObject;\nuse yii\\db\\Connection;\nuse yii\\di\\Container;\n\ninterface UserFinderInterface\n{\n    function findUser();\n}\n\nclass UserFinder extends BaseObject implements UserFinderInterface\n{\n    public $db;\n\n    public function __construct(Connection $db, $config = [])\n    {\n        $this->db = $db;\n        parent::__construct($config);\n    }\n\n    public function findUser()\n    {\n    }\n}\n\nclass UserLister extends BaseObject\n{\n    public $finder;\n\n    public function __construct(UserFinderInterface $finder, $config = [])\n    {\n        $this->finder = $finder;\n        parent::__construct($config);\n    }\n}\n\n$container = new Container;\n$container->set('yii\\db\\Connection', [\n    'dsn' => '...',\n]);\n$container->set('app\\models\\UserFinderInterface', [\n    'class' => 'app\\models\\UserFinder',\n]);\n$container->set('userLister', 'app\\models\\UserLister');\n\n$lister = $container->get('userLister');\n\n// 等价于:\n\n$db = new \\yii\\db\\Connection(['dsn' => '...']);\n$finder = new UserFinder($db);\n$lister = new UserLister($finder);\n```\n\n\n实践中的运用（Practical Usage） <span id=\"practical-usage\"></span>\n---------------------------\n\n当在应用程序的[入口脚本](structure-entry-scripts.md)中引入 `Yii.php` 文件时，\nYii 就创建了一个 DI 容器。这个 DI 容器可以通过 [[Yii::$container]] 访问。\n当调用 [[Yii::createObject()]] 时，此方法实际上会调用这个容器的 [[yii\\di\\Container::get()|get()]] 方法创建新对象。\n如上所述，DI 容器会自动解决依赖关系（如果有）并将其注入新创建的对象中。\n因为 Yii 在其多数核心代码中都使用了[[Yii::createObject()]] 创建新对象，\n所以你可以通过 [[Yii::$container]] 全局性地自定义这些对象。\n\n例如，你可以全局性自定义 [[yii\\widgets\\LinkPager]] 中分页按钮的默认数量:\n\n```php\n\\Yii::$container->set('yii\\widgets\\LinkPager', ['maxButtonCount' => 5]);\n```\n\n这样如果你通过如下代码在一个视图里使用这个挂件，\n它的 `maxButtonCount` 属性就会被初始化为 5 而不是类中定义的默认值 10。\n\n```php\necho \\yii\\widgets\\LinkPager::widget();\n```\n\n然而你依然可以覆盖通过 DI 容器设置的值：\n\n```php\necho \\yii\\widgets\\LinkPager::widget(['maxButtonCount' => 20]);\n```\n\n> Note: 在部件调用中给出的属性将始终覆盖DI容器中的定义。\n> 即使您指定了一个数组，例如 `'options' => ['id' => 'mypager']` 这些将不会与其他选项合并，\n> 而是替换它们。\n\n另一个例子是借用 DI 容器中自动构造方法注入带来的好处。\n假设你的控制器类依赖一些其他对象，例如一个旅馆预订服务。\n你可以通过一个构造器参数声明依赖关系，然后让 DI 容器帮你自动解决这个依赖关系。\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\nuse app\\components\\BookingInterface;\n\nclass HotelController extends Controller\n{\n    protected $bookingService;\n\n    public function __construct($id, $module, BookingInterface $bookingService, $config = [])\n    {\n        $this->bookingService = $bookingService;\n        parent::__construct($id, $module, $config);\n    }\n}\n```\n\n如果你从浏览器中访问这个控制器，你将看到一个报错信息，提醒你 `BookingInterface` 无法被实例化。\n这是因为你需要告诉 DI 容器怎样处理这个依赖关系。\n\n```php\n\\Yii::$container->set('app\\components\\BookingInterface', 'app\\components\\BookingService');\n```\n\n现在如果你再次访问这个控制器，一个 `app\\components\\BookingService` \n的实例就会被创建并被作为第三个参数注入到控制器的构造器中。\n\n高级实用性（Advanced Practical Usage） <span id=\"advanced-practical-usage\"></span>\n------------------------------------\n\n比如说我们在 API 应用方面工作：\n\n- `app\\components\\Request` 类继承了 `yii\\web\\Request` 并提供了额外的功能\n- `app\\components\\Response` 类继承了 `yii\\web\\Response` 并且在创建时应该将 `format`\n  属性设置为 `json`\n- `app\\storage\\FileStorage` 和 `app\\storage\\DocumentsReader`\n  用于处理位于某些文件存储中的文档的某些逻辑：\n  \n  ```php\n  class FileStorage\n  {\n      public function __construct($root) {\n          // whatever\n      }\n  }\n  \n  class DocumentsReader\n  {\n      public function __construct(FileStorage $fs) {\n          // whatever\n      }\n  }\n  ```\n\n可以一次配置多个定义，将配置数组传递给\n[[yii\\di\\Container::setDefinitions()|setDefinitions()]] 或 [[yii\\di\\Container::setSingletons()|setSingletons()]] 方法。\n遍历配置数组，将分别为每个对象分别调用 [[yii\\di\\Container::set()|set()]]\n或 [[yii\\di\\Container::setSingleton()|setSingleton()]] 方法。\n\n配置数组格式为：\n\n - `key`：类名称，接口名称或别名。 该 key 将作为第一个参数\n `$class` 传递给 [[yii\\di\\Container::set()|set()]] 方法。\n - `value`：与 `$class` 关联的定义。`$definition` 参数的值可能在 [[yii\\di\\Container::set()|set()]]\n 文档中描述。`$definition` 将作为第二个参数传递给 [[set()]]\n 方法。\n\n例如，让我们配置我们的容器以遵循上述要求：\n\n```php\n$container->setDefinitions([\n    'yii\\web\\Request' => 'app\\components\\Request',\n    'yii\\web\\Response' => [\n        'class' => 'app\\components\\Response',\n        'format' => 'json'\n    ],\n    'app\\storage\\DocumentsReader' => function () {\n        $fs = new app\\storage\\FileStorage('/var/tempfiles');\n        return new app\\storage\\DocumentsReader($fs);\n    }\n]);\n\n$reader = $container->get('app\\storage\\DocumentsReader'); \n// 将按照配置中的描述创建 DocumentReader 对象及其依赖关系\n```\n\n> Tip: 自 2.0.11 版以后，可以使用应用程序配置以声明式风格配置容器。\n查看[配置](concept-configurations.md)指南文章的\n[应用程序配置](concept-configurations.md#application-configurations)小节。\n\n一切正常，但如果我们需要创建 `Document Writer` 类，\n我们将复制粘贴创建 `FileStorage` 对象的行，这显然不是最聪明的方式。\n\n如 [解决依赖关系](#resolving-dependencies) 子节中所述，[[yii\\di\\Container::set()|set()]]\n和 [[yii\\di\\Container::setSingleton()|setSingleton()]] 可以选择将依赖项的构造函数参数作为第三个参数。\n要设置构造函数参数，可以使用以下配置数组格式：\n\n - `key`：类名称，接口名称或别名。该 key 将作为第一个参数\n `$class` 传递给 [[yii\\di\\Container::set()|set()]] 方法。\n - `value`：两个元素的数组。第一个元素将传递给 [[yii\\di\\Container::set()|set()]] 方法\n 作为第二个参数 `$definition`，第二个元素为 `$params`。\n\n让我们来修改我们的例子：\n\n```php\n$container->setDefinitions([\n    'tempFileStorage' => [ // 我们为了方便创建了一个别名\n        ['class' => 'app\\storage\\FileStorage'],\n        ['/var/tempfiles'] // 可以从一些配置文件中获取\n    ],\n    'app\\storage\\DocumentsReader' => [\n        ['class' => 'app\\storage\\DocumentsReader'],\n        [Instance::of('tempFileStorage')]\n    ],\n    'app\\storage\\DocumentsWriter' => [\n        ['class' => 'app\\storage\\DocumentsWriter'],\n        [Instance::of('tempFileStorage')]\n    ]\n]);\n\n$reader = $container->get('app\\storage\\DocumentsReader'); \n// 将与前面示例中的行为完全相同。\n```\n\n你可能会注意到 `Instance::of('tempFileStorage')` 符号。这意味着，[[yii\\di\\Container|Container]]\n将隐含地提供一个用 `tempFileStorage` 名称注册的依赖项， \n并将其作为 `app\\storage\\DocumentsWriter` 构造函数的第一个参数传递。\n\n> Note: [[yii\\di\\Container::setDefinitions()|setDefinitions()]] 和 [[yii\\di\\Container::setSingletons()|setSingletons()]]\n  方法从版本 2.0.11 开始可用。\n  \n配置优化的另一个步骤是将某些依赖项注册为单例。\n通过 [[yii\\di\\Container::set()|set()]] 注册的依赖项将在每次需要时实例化。\n某些类在运行时不会更改状态，\n因此它们可能会被注册为单例以提高应用程序的性能。\n\n一个很好的例子可以是 `app\\storage\\FileStorage` 类，它用一个简单的 API \n（例如 `$fs->read()`，`$fs->write()`）在文件系统上执行一些操作。\n这些操作不会更改内部类的状态，因此我们可以创建一次实例并多次使用它。\n\n```php\n$container->setSingletons([\n    'tempFileStorage' => [\n        ['class' => 'app\\storage\\FileStorage'],\n        ['/var/tempfiles']\n    ],\n]);\n\n$container->setDefinitions([\n    'app\\storage\\DocumentsReader' => [\n        ['class' => 'app\\storage\\DocumentsReader'],\n        [Instance::of('tempFileStorage')]\n    ],\n    'app\\storage\\DocumentsWriter' => [\n        ['class' => 'app\\storage\\DocumentsWriter'],\n        [Instance::of('tempFileStorage')]\n    ]\n]);\n\n$reader = $container->get('app\\storage\\DocumentsReader');\n```\n\n什么时候注册依赖关系（When to Register Dependencies） <span id=\"when-to-register-dependencies\"></span>\n------------------------------------------------\n\n由于依赖关系在创建新对象时需要解决，因此它们的注册应该尽早完成。\n如下是推荐的实践：\n\n* 如果你是一个应用程序的开发者，\n  你可以在应用程序的[入口脚本](structure-entry-scripts.md)\n  或者被入口脚本引入的脚本中注册依赖关系。\n* 如果你是一个可再分发[扩展](structure-extensions.md)的开发者，\n  你可以将依赖关系注册到扩展的引导类中。\n\n\n总结（Summary） <span id=\"summary\"></span>\n-------------\n\n依赖注入和[服务定位器](concept-service-locator.md)都是流行的设计模式，\n它们使你可以用充分解耦且更利于测试的风格构建软件。\n强烈推荐你阅读 [Martin 的文章](https://martinfowler.com/articles/injection.html) ，\n对依赖注入和服务定位器有个更深入的理解。\n\nYii 在依赖住入（DI）容器之上实现了它的[服务定位器](concept-service-locator.md)。\n当一个服务定位器尝试创建一个新的对象实例时，它会把调用转发到 DI 容器。\n后者将会像前文所述那样自动解决依赖关系。\n\n"
  },
  {
    "path": "docs/guide-zh-CN/concept-events.md",
    "content": "事件（Events）\n============\n\n事件可以将自定义代码“注入”到现有代码中的特定执行点。\n附加自定义代码到某个事件，当这个事件被触发时，这些代码就会自动执行。\n例如，邮件程序对象成功发出消息时可触发 `messageSent` 事件。\n如想追踪成功发送的消息，可以附加相应追踪代码到 `messageSent` 事件。\n\nYii 引入了名为 [[yii\\base\\Component]] 的基类以支持事件。\n如果一个类需要触发事件就应该继承 [[yii\\base\\Component]] 或其子类。\n\n\n事件处理器（Event Handlers） <span id=\"event-handlers\"></span>\n-------------------------\n\n事件处理器是一个[PHP 回调函数](https://www.php.net/manual/zh/language.types.callable.php)，\n当它所附加到的事件被触发时它就会执行。可以使用以下回调函数之一：\n\n- 字符串形式指定的 PHP 全局函数，如 `'trim'` ；\n- 对象名和方法名数组形式指定的对象方法，如 `[$object, $method]` ；\n- 类名和方法名数组形式指定的静态类方法，如 `[$class, $method]` ；\n- 匿名函数，如 `function ($event) { ... }` 。\n\n事件处理器的格式是：\n\n```php\nfunction ($event) {\n    // $event 是 yii\\base\\Event 或其子类的对象\n}\n```\n\n通过 `$event` 参数，事件处理器就获得了以下有关事件的信息：\n\n- [[yii\\base\\Event::name|event name]]：事件名\n- [[yii\\base\\Event::sender|event sender]]：调用 `trigger()` 方法的对象\n- [[yii\\base\\Event::data|custom data]]：附加事件处理器时传入的数据，默认为空，后文详述\n\n\n附加事件处理器（Attaching Event Handlers） <span id=\"attaching-event-handlers\"></span>\n--------------------------------------\n\n调用 [[yii\\base\\Component::on()]] 方法来附加处理器到事件上。如：\n\n```php\n$foo = new Foo;\n\n// 处理器是全局函数\n$foo->on(Foo::EVENT_HELLO, 'function_name');\n\n// 处理器是对象方法\n$foo->on(Foo::EVENT_HELLO, [$object, 'methodName']);\n\n// 处理器是静态类方法\n$foo->on(Foo::EVENT_HELLO, ['app\\components\\Bar', 'methodName']);\n\n// 处理器是匿名函数\n$foo->on(Foo::EVENT_HELLO, function ($event) {\n    //事件处理逻辑\n});\n```\n\n你也可以通过 [配置](concept-configurations.md) 附加事件处理器。 请\n参考 [配置的格式](concept-configurations.md#configuration-format) 小节了解更多.\n\n\n附加事件处理器时可以提供额外数据作为 [[yii\\base\\Component::on()]] 方法的第三个参数。\n数据在事件被触发和处理器被调用时能被处理器使用。如：\n\n```php\n// 当事件被触发时以下代码显示 \"abc\"\n// 因为 $event->data 包括被传递到 \"on\" 方法的数据\n$foo->on(Foo::EVENT_HELLO, 'function_name', 'abc');\n\nfunction function_name($event) {\n    echo $event->data;\n}\n```\n\n事件处理器顺序（Event Handler Order）\n---------------------------------\n\n可以附加一个或多个处理器到一个事件。当事件被触发，已附加的处理器将按附加次序依次调用。\n如果某个处理器需要停止其后的处理器调用，可以设置 `$event` 参数的 [[yii\\base\\Event::handled]] 属性为真，\n如下：\n\n```php\n$foo->on(Foo::EVENT_HELLO, function ($event) {\n    $event->handled = true;\n});\n```\n\n默认新附加的事件处理器排在已存在处理器队列的最后。\n因此，这个处理器将在事件被触发时最后一个调用。\n在处理器队列最前面插入新处理器将使该处理器最先调用，可以传递第四个参数 `$append` 为假并调用 [[yii\\base\\Component::on()]] 方法实现：\n\n```php\n$foo->on(Foo::EVENT_HELLO, function ($event) {\n    // 这个处理器将被插入到处理器队列的第一位...\n}, $data, false);\n```\n\n触发事件（Triggering Events） <span id=\"triggering-events\"></span>\n--------------------------\n\n事件通过调用 [[yii\\base\\Component::trigger()]] 方法触发，此方法须传递*事件名*，\n还可以传递一个事件对象，用来传递参数到事件处理器。如：\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Component;\nuse yii\\base\\Event;\n\nclass Foo extends Component\n{\n    const EVENT_HELLO = 'hello';\n\n    public function bar()\n    {\n        $this->trigger(self::EVENT_HELLO);\n    }\n}\n```\n\n以上代码当调用 `bar()` ，它将触发名为 `hello` 的事件。\n\n> Tip: 推荐使用类常量来表示事件名。上例中，常量 `EVENT_HELLO` 用来表示 `hello` 。\n  这有两个好处。第一，它可以防止拼写错误并支持 IDE 的自动完成。\n  第二，只要简单检查常量声明就能了解一个类支持哪些事件。\n\n有时想要在触发事件时同时传递一些额外信息到事件处理器。\n例如，邮件程序要传递消息信息到 `messageSent` 事件的处理器以便处理器了解哪些消息被发送了。\n为此，可以提供一个事件对象作为 [[yii\\base\\Component::trigger()]] 方法的第二个参数。\n这个事件对象必须是 [[yii\\base\\Event]] 类或其子类的实例。\n如：\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Component;\nuse yii\\base\\Event;\n\nclass MessageEvent extends Event\n{\n    public $message;\n}\n\nclass Mailer extends Component\n{\n    const EVENT_MESSAGE_SENT = 'messageSent';\n\n    public function send($message)\n    {\n        // ...发送 $message 的逻辑...\n\n        $event = new MessageEvent;\n        $event->message = $message;\n        $this->trigger(self::EVENT_MESSAGE_SENT, $event);\n    }\n}\n```\n\n当 [[yii\\base\\Component::trigger()]] 方法被调用时，\n它将调用所有附加到命名事件（trigger 方法第一个参数）的事件处理器。\n\n\n移除事件处理器（Detaching Event Handlers） <span id=\"detaching-event-handlers\"></span>\n--------------------------------------\n\n从事件移除处理器，调用 [[yii\\base\\Component::off()]] 方法。如：\n\n```php\n// 处理器是全局函数\n$foo->off(Foo::EVENT_HELLO, 'function_name');\n\n// 处理器是对象方法\n$foo->off(Foo::EVENT_HELLO, [$object, 'methodName']);\n\n// 处理器是静态类方法\n$foo->off(Foo::EVENT_HELLO, ['app\\components\\Bar', 'methodName']);\n\n// 处理器是匿名函数\n$foo->off(Foo::EVENT_HELLO, $anonymousFunction);\n```\n\n注意当匿名函数附加到事件后一般不要尝试移除匿名函数，\n除非你在某处存储了它。以上示例中，\n假设匿名函数存储为变量 `$anonymousFunction` 。\n\n移除事件的全部处理器，简单调用 [[yii\\base\\Component::off()]] 即可，不需要第二个参数：\n\n```php\n$foo->off(Foo::EVENT_HELLO);\n```\n\n\n类级别的事件处理器（Class-Level Event Handlers） <span id=\"class-level-event-handlers\"></span>\n-------------------------------------------\n\n以上部分，我们叙述了在*实例级别*如何附加处理器到事件。\n有时想要一个类的*所有*实例而不是一个指定的实例都响应一个被触发的事件，\n并不是一个个附加事件处理器到每个实例，\n而是通过调用静态方法 [[yii\\base\\Event::on()]] 在*类级别*附加处理器。\n\n例如，[活动记录](db-active-record.md)对象要在每次往数据库新增一条新记录时触发一个 \n[[yii\\db\\BaseActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]] 事件。\n要追踪每个[活动记录](db-active-record.md)对象的新增记录完成情况，应如下写代码：\n\n```php\nuse Yii;\nuse yii\\base\\Event;\nuse yii\\db\\ActiveRecord;\n\nEvent::on(ActiveRecord::class, ActiveRecord::EVENT_AFTER_INSERT, function ($event) {\n    Yii::debug(get_class($event->sender) . ' is inserted');\n});\n```\n\n每当 [[yii\\db\\BaseActiveRecord|ActiveRecord]] 或其子类的实例触发 \n[[yii\\db\\BaseActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]] 事件时，\n这个事件处理器都会执行。在这个处理器中，可以通过 `$event->sender` 获取触发事件的对象。\n\n当对象触发事件时，它首先调用实例级别的处理器，然后才会调用类级别处理器。\n\n可调用静态方法[[yii\\base\\Event::trigger()]]来触发一个*类级别*事件。\n类级别事件不与特定对象相关联。因此，它只会引起类级别事件处理器的调用。\n如：\n\n```php\nuse yii\\base\\Event;\n\nEvent::on(Foo::class, Foo::EVENT_HELLO, function ($event) {\n    var_dump($event->sender);  // 显示 \"null\"\n});\n\nEvent::trigger(Foo::class, Foo::EVENT_HELLO);\n```\n\n注意这种情况下 `$event->sender` 指向触发事件的类名而不是对象实例。\n\n> Note: 因为类级别的处理器响应类和其子类的所有实例触发的事件，\n  必须谨慎使用，尤其是底层的基类，如 [[yii\\base\\Object]]。\n\n移除类级别的事件处理器只需调用[[yii\\base\\Event::off()]]，如：\n\n```php\n// 移除 $handler\nEvent::off(Foo::class, Foo::EVENT_HELLO, $handler);\n\n// 移除 Foo::EVENT_HELLO 事件的全部处理器\nEvent::off(Foo::class, Foo::EVENT_HELLO);\n```\n\n\n使用接口事件（Events using interfaces） <span id=\"interface-level-event-handlers\"></span>\n-----------------------------------\n\n有更多的抽象方式来处理事件。你可以为特殊的事件创建一个独立的接口，\n然后在你需要的类中实现它。\n\n例如，我们可以先创建下面这个接口:\n\n```php\nnamespace app\\interfaces;\n\ninterface DanceEventInterface\n{\n    const EVENT_DANCE = 'dance';\n}\n```\n\n然后在两个类中实现:\n\n```php\nclass Dog extends Component implements DanceEventInterface\n{\n    public function meetBuddy()\n    {\n        echo \"Woof!\";\n        $this->trigger(DanceEventInterface::EVENT_DANCE);\n    }\n}\n\nclass Developer extends Component implements DanceEventInterface\n{\n    public function testsPassed()\n    {\n        echo \"Yay!\";\n        $this->trigger(DanceEventInterface::EVENT_DANCE);\n    }\n}\n```\n\n要处理由这些类触发的 `EVENT_DANCE` ，调用 [[yii\\base\\Event::on()|Event::on()]] \n并将接口类名作为第一个参数:\n\n```php\nEvent::on('app\\interfaces\\DanceEventInterface', DanceEventInterface::EVENT_DANCE, function ($event) {\n    Yii::trace(get_class($event->sender) . ' just danced'); // Will log that Dog or Developer danced\n});\n```\n\n你可以在这些类中触发这个事件：\n\n```php\n// trigger event for Dog class\nEvent::trigger(Dog::class, DanceEventInterface::EVENT_DANCE);\n\n// trigger event for Developer class\nEvent::trigger(Developer::class, DanceEventInterface::EVENT_DANCE);\n```\n\n但是请注意, 你不能让所有实现这个接口的类都触发事件：\n\n```php\n// 不会生效。实现此接口的类不会触发事件。\nEvent::trigger('app\\interfaces\\DanceEventInterface', DanceEventInterface::EVENT_DANCE);\n```\n\n调用 [[yii\\base\\Event::off()|Event::off()]] 移除事件处理器。例如：\n\n```php\n// 移除 $handler\nEvent::off('app\\interfaces\\DanceEventInterface', DanceEventInterface::EVENT_DANCE, $handler);\n\n// 移除所有 `DanceEventInterface::EVENT_DANCE` 的处理器\nEvent::off('app\\interfaces\\DanceEventInterface', DanceEventInterface::EVENT_DANCE);\n```\n\n\n全局事件（Global Events） <span id=\"global-events\"></span>\n----------------------\n\n所谓*全局事件*实际上是一个基于以上叙述的事件机制的戏法。它需要一个全局可访问的单例，\n如[应用](structure-applications.md)实例。\n\n事件触发者不调用其自身的 `trigger()` 方法，而是调用单例的 `trigger()` 方法来触发全局事件。\n类似地，事件处理器被附加到单例的事件。如：\n\n```php\nuse Yii;\nuse yii\\base\\Event;\nuse app\\components\\Foo;\n\nYii::$app->on('bar', function ($event) {\n    echo get_class($event->sender);  // 显示 \"app\\components\\Foo\"\n});\n\nYii::$app->trigger('bar', new Event(['sender' => new Foo]));\n```\n\n全局事件的一个好处是当附加处理器到一个对象要触发的事件时，\n不需要产生该对象。相反，处理器附加和事件触发都通过单例\n（如应用实例）完成。\n\n然而，因为全局事件的命名空间由各方共享，应合理命名全局事件，\n如引入一些命名空间（例：\"frontend.mail.sent\", \"backend.mail.sent\"）。\n\n\n通配符事件（Wildcard Events） <span id=\"wildcard-events\"></span>\n--------------------------\n\n自 2.0.14 以来，您可以为多个匹配通配符模式的事件设置事件处理程序。\n例如：\n\n```php\nuse Yii;\n\n$foo = new Foo();\n\n$foo->on('foo.event.*', function ($event) {\n    // 触发任何事件，该名称以 'foo.event.' 开头\n    Yii::debug('trigger event: ' . $event->name);\n});\n```\n\n通配符模式也可以用于类级别的事件。 例如：\n\n```php\nuse yii\\base\\Event;\nuse Yii;\n\nEvent::on('app\\models\\*', 'before*', function ($event) {\n    // 触发命名空间 'app\\models' 中的任何类的任何事件，名称以 'before' 开头。\n    Yii::debug('trigger event: ' . $event->name . ' for class: ' . get_class($event->sender));\n});\n```\n\n这允许您使用以下代码通过单个处理程序捕获所有应用程序事件：\n\n```php\nuse yii\\base\\Event;\nuse Yii;\n\nEvent::on('*', '*', function ($event) {\n    // 触发任何类的任何事件\n    Yii::debug('trigger event: ' . $event->name);\n});\n```\n\n> Note: 事件处理程序设置的使用通配符可能会降低应用程序的性能。\n  如果可能，最好避免。\n\n为了移除由通配符模式指定的事件处理程序，您应该在\n[[yii\\base\\Component::off()]] 或 [[yii\\base\\Event::off()]] 调用中重复相同的模式。\n请记住，在移除事件处理程序期间传递通配符将移除为此通配符指定的处理程序，\n而为常规事件名称附加的处理程序将保留，即使它们与模式匹配。 例如：\n\n```php\nuse Yii;\n\n$foo = new Foo();\n\n// 附加常规处理\n$foo->on('event.hello', function ($event) {\n    echo 'direct-handler'\n});\n\n// 附加通配符处理程序\n$foo->on('*', function ($event) {\n    echo 'wildcard-handler'\n});\n\n// 仅移除通配符处理程序！\n$foo->off('*');\n\n$foo->trigger('event.hello'); // 输出：'direct-handler'\n```\n"
  },
  {
    "path": "docs/guide-zh-CN/concept-properties.md",
    "content": "属性（Properties）\n================\n\n在 PHP 中，类的成员变量也被称为*属性*。它们是类定义的一部分，\n用来表现一个实例的状态（也就是区分类的不同实例）。\n在具体实践中，常常会想用一个稍微特殊些的方法实现属性的读写。\n例如，如果有需求每次都要对 `label` 属性执行 trim 操作，\n就可以用以下代码实现：\n\n```php\n$object->label = trim($label);\n```\n\n上述代码的缺点是只要修改 `label` 属性就必须再次调用 `trim()` 函数。\n若将来需要用其它方式处理 `label` 属性，比如首字母大写，\n就不得不修改所有给 `label` 属性赋值的代码。这种代码的重复会导致 bug，\n这种实践显然需要尽可能避免。\n\n为解决该问题，Yii 引入了一个名为 [[yii\\base\\BaseObject]] 的基类，\n它支持基于类内的 *getter* 和 *setter*（读取器和设定器）方法来定义属性。\n如果某类需要支持这个特性，只需要继承 [[yii\\base\\BaseObject]] 或其子类即可。\n\n> Info: 几乎每个 Yii 框架的核心类都继承自 [[yii\\base\\BaseObject]] 或其子类。\n  这意味着只要在核心类中见到 getter 或 setter 方法，就可以像调用属性一样调用它。\n\ngetter 方法是名称以 `get` 开头的方法，而 setter 方法名以 `set` 开头。\n方法名中 `get` 或 `set` 后面的部分就定义了该属性的名字。如下面代码所示，\ngetter 方法 `getLabel()` 和 setter 方法 `setLabel()` 操作的是 `label` 属性，：\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\BaseObject;\n\nclass Foo extends BaseObject\n{\n    private $_label;\n\n    public function getLabel()\n    {\n        return $this->_label;\n    }\n\n    public function setLabel($value)\n    {\n        $this->_label = trim($value);\n    }\n}\n```\n\n（详细解释：getter 和 setter 方法创建了一个名为 `label` 的属性，\n在这个例子里，它指向一个私有的内部属性 `_label`。）\n\ngetter/setter 定义的属性用法与类成员变量一样。两者主要的区别是：\n当这种属性被读取时，对应的 getter 方法将被调用；而当属性被赋值时，\n对应的 setter 方法就调用。如：\n\n```php\n// 等效于 $label = $object->getLabel();\n$label = $object->label;\n\n// 等效于 $object->setLabel('abc');\n$object->label = 'abc';\n```\n\n只定义了 getter 没有 setter 的属性是*只读属性*。\n尝试赋值给这样的属性将导致 [[yii\\base\\InvalidCallException|InvalidCallException]] （无效调用）异常。\n类似的，只有 setter 方法而没有 getter 方法定义的属性是*只写属性*，\n尝试读取这种属性也会触发异常。使用只写属性的情况几乎没有。\n\n通过 getter 和 setter 定义的属性也有一些特殊规则和限制：\n\n* 这类属性的名字是*不区分大小写*的。如，`$object->label` 和 `$object->Label` 是同一个属性。\n  因为 PHP 方法名是不区分大小写的。\n* 如果此类属性名和类成员变量相同，以后者为准。例如，\n  假设以上 `Foo` 类有个 `label` 成员变量，然后给 `$object->label = 'abc'` 赋值，\n  将赋给成员变量而不是 setter `setLabel()` 方法。\n* 这类属性不支持可见性（访问限制）。定义属性的 getter 和 setter 方法是 public、protected 还是 private 对属性的可见性没有任何影响。\n* 这类属性的 getter 和 setter 方法只能定义为*非静态*的，若定义为静态方法（static）则不会以相同方式处理。\n* 对不确定有无魔术方法（getter 或 setter）的属性正常调用 `property_exists()` 将不会生效。你应该分别调用 [[yii\\base\\BaseObject::canGetProperty()|canGetProperty()]] \n  或 [[yii\\base\\BaseObject::canSetProperty()|canSetProperty()]] 。  \n\n回到开头提到的问题，与其处处要调用 `trim()` 函数，\n现在我们只需在 setter `setLabel()` 方法内调用一次。\n如果 label 首字母变成大写的新要求来了，我们只需要修改`setLabel()` 方法，\n而无须接触任何其它代码。\n"
  },
  {
    "path": "docs/guide-zh-CN/concept-service-locator.md",
    "content": "服务定位器（Service Locator）\n==========================\n\n服务定位器是一个了解如何提供各种应用所需的服务（或组件）的对象。在服务定位器中，\n每个组件都只有一个单独的实例，并通过ID 唯一地标识。\n用这个 ID 就能从服务定位器中得到这个组件。\n\n在 Yii 中，服务定位器是 [[yii\\di\\ServiceLocator]] 或其子类的一个实例。\n\n最常用的服务定位器是*application（应用）*对象，可以通过 `\\Yii::$app` 访问。\n它所提供的服务被称为*application components（应用组件）*，\n比如：`request`、`response`、`urlManager` 组件。可以通过服务定位器所提供的功能，\n非常容易地配置这些组件，或甚至是用你自己的实现替换掉他们。\n\n除了 application 对象，每个模块对象本身也是一个服务定位器。\n\n要使用服务定位器，第一步是要注册相关组件。组件可以通过 [[yii\\di\\ServiceLocator::set()]] 方法进行注册。\n以下的方法展示了注册组件的不同方法：\n\n```php\nuse yii\\di\\ServiceLocator;\nuse yii\\caching\\FileCache;\n\n$locator = new ServiceLocator;\n\n// 通过一个可用于创建该组件的类名，注册 \"cache\" （缓存）组件。\n$locator->set('cache', 'yii\\caching\\ApcCache');\n\n// 通过一个可用于创建该组件的配置数组，注册 \"db\" （数据库）组件。\n$locator->set('db', [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=localhost;dbname=demo',\n    'username' => 'root',\n    'password' => '',\n]);\n\n// 通过一个能返回该组件的匿名函数，注册 \"search\" 组件。\n$locator->set('search', function () {\n    return new app\\components\\SolrService;\n});\n\n// 用组件注册 \"pageCache\" 组件\n$locator->set('pageCache', new FileCache);\n```\n\n一旦组件被注册成功，你可以任选以下两种方式之一，通过它的 ID 访问它：\n\n```php\n$cache = $locator->get('cache');\n// 或者\n$cache = $locator->cache;\n```\n\n如上所示， [[yii\\di\\ServiceLocator]] 允许通过组件 ID 像访问一个属性值那样访问一个组件。\n当你第一次访问某组件时，[[yii\\di\\ServiceLocator]] \n会通过该组件的注册信息创建一个该组件的实例，并返回它。\n之后，如果再次访问，则服务定位器会返回同一个实例。\n\n你可以通过 [[yii\\di\\ServiceLocator::has()]] 检查某组件 ID 是否被注册。\n若你用一个无效的 ID 调用 [[yii\\di\\ServiceLocator::get()]]，则会抛出一个异常。\n\n\n因为服务定位器，经常会在创建时附带[配置信息](concept-configurations.md)，\n因此我们提供了一个可写的属性，名为 [[yii\\di\\ServiceLocator::setComponents()|components]]，\n这样就可以配置该属性，或一次性注册多个组件。\n下面的代码展示了如何用一个配置数组，配置一个应用并注册\n`db`，`cache`，`tz` 和 `search` 组件：\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=demo',\n            'username' => 'root',\n            'password' => '',\n        ],\n        'cache' => 'yii\\caching\\ApcCache',\n        'tz' => function() {\n            return new \\DateTimeZone(Yii::$app->formatter->defaultTimeZone);\n        },\n        'search' => function () {\n            $solr = new app\\components\\SolrService('127.0.0.1');\n            // ... other initializations ...\n            return $solr;\n        },\n    ],\n];\n```\n\n在上面的代码中，有一个替代方法来配置 `search` 组件。\n而不是直接写一个 PHP 回调建立 `SolrService`实例，你可以使用一个静态类方法来返回这样的回调，\n如下所示：\n\n```php\nclass SolrServiceBuilder\n{\n    public static function build($ip)\n    {\n        return function () use ($ip) {\n            $solr = new app\\components\\SolrService($ip);\n            // ... other initializations ...\n            return $solr;\n        };\n    }\n}\n\nreturn [\n    // ...\n    'components' => [\n        // ...\n        'search' => SolrServiceBuilder::build('127.0.0.1'),\n    ],\n];\n```\n\n当你发布一个 Yii 组件封装一些非 Yii 第三方库时，这种替代方法是最好的。\n当您使用如上所示的静态方法来表示构建复杂逻辑的第三方对象时，\n您的组件用户只需要调用静态方法来配置组件。\n\n## 遍历树（Tree traversal） <span id=\"tree-traversal\"></span>\n\n模块允许任意嵌套; Yii 应用程序本质上是一个模块树。\n由于这些模块中的每一个都是服务定位器，所以子模块有权限访问其父模块。\n这允许模块使用 `$this->get('db')` 而不是引用根服务定位器 `Yii::$app->get('db')`。\n增加的好处是开发人员可以覆盖模块中的配置。\n\n如果模块无法满足要求，则从模块中检索服务的请求将被传递给它的父模块。\n\n请注意，模块中组件的配置决不会与来自父模块中组件的配置合并。Service Locator 模式允许我们定义命名服务，但不能假定具有相同名称的服务使用相同的配置参数。\n"
  },
  {
    "path": "docs/guide-zh-CN/db-active-record.md",
    "content": "活动记录（Active Record）\n======================\n\n[Active Record](https://zh.wikipedia.org/wiki/%E4%B8%BB%E5%8A%A8%E8%AE%B0%E5%BD%95) 提供了一个面向对象的接口，\n用以访问和操作数据库中的数据。Active Record 类与数据库表关联，\nActive Record 实例对应于该表的一行，\nActive Record 实例的*属性*表示该行中特定列的值。\n您可以访问 Active Record 属性并调用 Active Record 方法来访问和操作存储在数据库表中的数据，\n而不用编写原始 SQL 语句。\n\n例如，假定 `Customer` Active Record 类关联着 `customer` 表，\n且该类的 `name` 属性代表 `customer` 表的 `name` 列。\n你可以写以下代码来哉 `customer` 表里插入一行新的记录：\n\n```php\n$customer = new Customer();\n$customer->name = 'Qiang';\n$customer->save();\n```\n\n对于 MySQL，上面的代码和使用下面的原生 SQL 语句是等效的，但显然前者更直观，\n更不易出错，并且面对不同的数据库系统（DBMS, Database Management System）时更不容易产生兼容性问题。\n\n```php\n$db->createCommand('INSERT INTO `customer` (`name`) VALUES (:name)', [\n    ':name' => 'Qiang',\n])->execute();\n```\n\nYii 为以下关系数据库提供 Active Record 支持：\n\n* MySQL 4.1 及以上：通过 [[yii\\db\\ActiveRecord]] 支持\n* PostgreSQL 7.3 及以上：通过 [[yii\\db\\ActiveRecord]] 支持\n* SQLite 2 and 3：通过 [[yii\\db\\ActiveRecord]] 支持\n* Microsoft SQL Server 2008 及以上：通过 [[yii\\db\\ActiveRecord]] 支持\n* Oracle：通过 [[yii\\db\\ActiveRecord]] 支持\n* CUBRID 9.3 及以上：通过 [[yii\\db\\ActiveRecord]] 支持 (提示， 由于 CUBRID PDO 扩展的 [bug](http://jira.cubrid.org/browse/APIS-658)，\n  给变量加引用将不起作用，所以你得使用 CUBRID 9.3 客户端及服务端。\n* Sphinx：通过 [[yii\\sphinx\\ActiveRecord]] 支持，依赖 `yii2-sphinx` 扩展\n* ElasticSearch：通过 [[yii\\elasticsearch\\ActiveRecord]] 支持, 依赖 `yii2-elasticsearch` 扩展\n\n此外，Yii 的 Active Record 功能还支持以下 NoSQL 数据库：\n\n* Redis 2.6.12 及以上：通过 [[yii\\redis\\ActiveRecord]] 支持，依赖 `yii2-redis` 扩展\n* MongoDB 1.3.0 及以上：通过 [[yii\\mongodb\\ActiveRecord]] 支持，依赖 `yii2-mongodb` 扩展\n\n在本教程中，我们会主要描述对关系型数据库的 Active Record 用法。\n然而，绝大多数的内容在 NoSQL 的 Active Record 里同样适用。\n\n\n## 声明 Active Record 类（Declaring Active Record Classes） <span id=\"declaring-ar-classes\"></span>\n\n要想声明一个 Active Record 类，你需要声明该类继承 [[yii\\db\\ActiveRecord]]。\n\n### 设置表的名称（Setting a table name）\n\n默认的，每个 Active Record 类关联各自的数据库表。\n经过 [[yii\\helpers\\Inflector::camel2id()]] 处理，[[yii\\db\\ActiveRecord::tableName()|tableName()]] 方法默认返回的表名称是通过类名转换来得。 \n如果这个默认名称不正确，你得重写这个方法。\n\n此外，[[yii\\db\\Connection::$tablePrefix|tablePrefix]] 表前缀也会起作用。例如，如果\n[[yii\\db\\Connection::$tablePrefix|tablePrefix]] 表前缀是 `tbl_`，`Customer` 的类名将转换成 `tbl_customer` 表名，`OrderItem` 转换成 `tbl_order_item`。\n\n如果你定义的表名是 `{{%TableName}}`，百分比字符 `%` 会被替换成表前缀。\n例如，`{{%post}}` 会变成 `{{tbl_post}}`。表名两边的括号会被 [SQL 查询引用](db-dao.md#quoting-table-and-column-names) 处理。\n\n\n下面的例子中，我们给 `customer` 数据库表定义叫 `Customer` 的 Active Record 类。\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass Customer extends ActiveRecord\n{\n    const STATUS_INACTIVE = 0;\n    const STATUS_ACTIVE = 1;\n    \n    /**\n     * @return string Active Record 类关联的数据库表名称\n     */\n    public static function tableName()\n    {\n        return '{{customer}}';\n    }\n}\n```\n\n### 将 Active Record 称为模型（Active records are called \"models\"）\n\nActive Record 实例称为[模型](structure-models.md)。因此, 我们通常将 Active Record 类\n放在 `app\\models` 命名空间下（或者其他保存模型的命名空间）。\n\n因为 [[yii\\db\\ActiveRecord]] 继承了模型 [[yii\\base\\Model]]，它就拥有所有[模型](structure-models.md)特性，\n比如说属性（attributes），验证规则（rules），数据序列化（data serialization），等等。\n\n\n## 建立数据库连接（Connecting to Databases） <span id=\"db-connection\"></span>\n\n活动记录 Active Record 默认使用 `db` [组件](structure-application-components.md) \n作为连接器 [[yii\\db\\Connection|DB connection]] 访问和操作数据库数据。 \n基于[数据库访问](db-dao.md)中的解释，你可以在系统配置中\n这样配置 `db` 组件。\n\n```php\nreturn [\n    'components' => [\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=testdb',\n            'username' => 'demo',\n            'password' => 'demo',\n        ],\n    ],\n];\n```\n\n如果你要用不同的数据库连接，而不仅仅是 `db` 组件，\n你可以重写 [[yii\\db\\ActiveRecord::getDb()|getDb()]] 方法。\n\n```php\nclass Customer extends ActiveRecord\n{\n    // ...\n\n    public static function getDb()\n    {\n        // 使用 \"db2\" 组件\n        return \\Yii::$app->db2;  \n    }\n}\n```\n\n\n## 查询数据（Querying Data） <span id=\"querying-data\"></span>\n\n定义 Active Record 类后，你可以从相应的数据库表中查询数据。\n查询过程大致如下三个步骤：\n\n1. 通过 [[yii\\db\\ActiveRecord::find()]] 方法创建一个新的查询生成器对象；\n2. 使用[查询生成器的构建方法](db-query-builder.md#building-queries)来构建你的查询；\n3. 调用[查询生成器的查询方法](db-query-builder.md#query-methods)来取出数据到 Active Record 实例中。\n\n正如你看到的，是不是跟[查询生成器](db-query-builder.md)的步骤差不多。\n唯一有区别的地方在于你用 [[yii\\db\\ActiveRecord::find()]] 去获得一个新的查询生成器对象，这个对象是 [[yii\\db\\ActiveQuery]]，\n而不是使用 `new` 操作符创建一个查询生成器对象。\n\n下面是一些例子，介绍如何使用 Active Query 查询数据：\n\n```php\n// 返回 ID 为 123 的客户：\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::find()\n    ->where(['id' => 123])\n    ->one();\n\n// 取回所有活跃客户并以他们的 ID 排序：\n// SELECT * FROM `customer` WHERE `status` = 1 ORDER BY `id`\n$customers = Customer::find()\n    ->where(['status' => Customer::STATUS_ACTIVE])\n    ->orderBy('id')\n    ->all();\n\n// 取回活跃客户的数量：\n// SELECT COUNT(*) FROM `customer` WHERE `status` = 1\n$count = Customer::find()\n    ->where(['status' => Customer::STATUS_ACTIVE])\n    ->count();\n\n// 以客户 ID 索引结果集：\n// SELECT * FROM `customer`\n$customers = Customer::find()\n    ->indexBy('id')\n    ->all();\n```\n\n上述代码中，`$customer` 是个 `Customer` 对象，而 `$customers` 是个以 `Customer` 对象为元素的数组。\n它们两都是以 `customer` 表中取回的数据结果集填充的。\n\n> Tip: 由于 [[yii\\db\\ActiveQuery]] 继承 [[yii\\db\\Query]]，\n  你可以使用 [Query Builder](db-query-builder.md) 章节里所描述的*所有*查询方法。\n\n根据主键获取数据行是比较常见的操作，所以 Yii \n提供了两个快捷方法：\n\n- [[yii\\db\\ActiveRecord::findOne()]]：返回一个 Active Record 实例，填充于查询结果的第一行数据。\n- [[yii\\db\\ActiveRecord::findAll()]]：返回一个 Active Record 实例的数据，填充于查询结果的全部数据。\n\n这两个方法的传参格式如下：\n\n- 标量值：这个值会当作主键去查询。\n  Yii 会通过读取数据库模式信息来识别主键列。\n- 标量值的数组：这数组里的值都当作要查询的主键的值。\n- 关联数组：键值是表的列名，元素值是相应的要查询的条件值。\n  可以到 [哈希格式](db-query-builder.md#hash-format) 查看更多信息。\n  \n如下代码描述如何使用这些方法：\n\n```php\n// 返回 id 为 123 的客户 \n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// 返回 id 是 100, 101, 123, 124 的客户\n// SELECT * FROM `customer` WHERE `id` IN (100, 101, 123, 124)\n$customers = Customer::findAll([100, 101, 123, 124]);\n\n// 返回 id 是 123 的活跃客户\n// SELECT * FROM `customer` WHERE `id` = 123 AND `status` = 1\n$customer = Customer::findOne([\n    'id' => 123,\n    'status' => Customer::STATUS_ACTIVE,\n]);\n\n// 返回所有不活跃的客户\n// SELECT * FROM `customer` WHERE `status` = 0\n$customers = Customer::findAll([\n    'status' => Customer::STATUS_INACTIVE,\n]);\n```\n\n> Warning: 如果你需要将用户输入传递给这些方法，请确保输入值是标量或者是\n> 数组条件，确保数组结构不能被外部所改变：\n>\n> ```php\n> // yii\\web\\Controller 确保了 $id 是标量\n> public function actionView($id)\n> {\n>     $model = Post::findOne($id);\n>     // ...\n> }\n>\n> // 明确了指定要搜索的列，在此处传递标量或数组将始终只是查找出单个记录而已\n> $model = Post::findOne(['id' => Yii::$app->request->get('id')]);\n>\n> // 不要使用下面的代码！可以注入一个数组条件来匹配任意列的值！\n> $model = Post::findOne(Yii::$app->request->get('id'));\n> ```\n\n\n> Tip: [[yii\\db\\ActiveRecord::findOne()]] 和 [[yii\\db\\ActiveQuery::one()]] 都不会添加 `LIMIT 1` 到\n  生成的 SQL 语句中。如果你的查询会返回很多行的数据，\n  你明确的应该加上 `limit(1)` 来提高性能，比如 `Customer::find()->limit(1)->one()`。\n\n除了使用查询生成器的方法之外，你还可以书写原生的 SQL 语句来查询数据，并填充结果集到 Active Record 对象中。\n通过使用 [[yii\\db\\ActiveRecord::findBySql()]] 方法:\n\n```php\n// 返回所有不活跃的客户\n$sql = 'SELECT * FROM customer WHERE status=:status';\n$customers = Customer::findBySql($sql, [':status' => Customer::STATUS_INACTIVE])->all();\n```\n\n不要在 [[yii\\db\\ActiveRecord::findBySql()|findBySql()]] 方法后加其他查询方法了，\n多余的查询方法都会被忽略。\n\n\n## 访问数据（Accessing Data） <span id=\"accessing-data\"></span>\n\n如上所述，从数据库返回的数据被填充到 Active Record 实例中，\n查询结果的每一行对应于单个 Active Record 实例。\n您可以通过 Active Record 实例的属性来访问列值，例如，\n\n```php\n// \"id\" 和 \"email\" 是 \"customer\" 表中的列名\n$customer = Customer::findOne(123);\n$id = $customer->id;\n$email = $customer->email;\n```\n\n> Tip: Active Record 的属性以区分大小写的方式为相关联的表列命名的。\n  Yii 会自动为关联表的每一列定义 Active Record 中的一个属性。\n  您不应该重新声明任何属性。\n\n由于 Active Record 的属性以表的列名命名，可能你会发现你正在编写像这样的 PHP 代码：\n`$customer->first_name`，如果你的表的列名是使用下划线分隔的，那么属性名中的单词\n以这种方式命名。 如果您担心代码风格一致性的问题，那么你应当重命名相应的表列名\n（例如使用骆驼拼写法）。\n\n\n### 数据转换（Data Transformation） <span id=\"data-transformation\"></span>\n\n常常遇到，要输入或显示的数据是一种格式，而要将其存储在数据库中是另一种格式。\n例如，在数据库中，您将客户的生日存储为 UNIX 时间戳（虽然这不是一个很好的设计），\n而在大多数情况下，你想以字符串 `'YYYY/MM/DD'` 的格式处理生日数据。\n为了实现这一目标，您可以在 `Customer` 中定义 *数据转换* 方法\n定义 Active Record 类如下：\n\n```php\nclass Customer extends ActiveRecord\n{\n    // ...\n\n    public function getBirthdayText()\n    {\n        return date('Y/m/d', $this->birthday);\n    }\n    \n    public function setBirthdayText($value)\n    {\n        $this->birthday = strtotime($value);\n    }\n}\n```\n\n现在你的 PHP 代码中，你可以访问 `$customer->birthdayText`，\n来以 `'YYYY/MM/DD'` 的格式输入和显示客户生日，而不是访问 `$customer->birthday`。\n\n> Tip: 上述示例显示了以不同格式转换数据的通用方法。如果你正在使用\n> 日期值，您可以使用 [DateValidator](tutorial-core-validators.md#date) 和 [[yii\\jui\\DatePicker|DatePicker]] 来操作，\n> 这将更易用，更强大。\n\n\n### 以数组形式获取数据（Retrieving Data in Arrays） <span id=\"data-in-arrays\"></span>\n\n通过 Active Record 对象获取数据十分方便灵活，与此同时，当你需要返回大量的数据的时候，\n这样的做法并不令人满意，因为这将导致大量内存占用。在这种情况下，您可以\n在查询方法前调用 [[yii\\db\\ActiveQuery::asArray()|asArray()]] 方法，来获取 PHP 数组形式的结果：\n\n```php\n// 返回所有客户\n// 每个客户返回一个关联数组\n$customers = Customer::find()\n    ->asArray()\n    ->all();\n```\n\n> Tip: 虽然这种方法可以节省内存并提高性能，但它更靠近较低的 DB 抽象层\n  你将失去大部分的 Active Record 提供的功能。 一个非常重要的区别在于列值的数据类型。\n  当您在 Active Record 实例中返回数据时，列值将根据实际列类型，自动类型转换；\n  然而，当您以数组返回数据时，列值将为\n  字符串（因为它们是没有处理过的 PDO 的结果），不管它们的实际列是什么类型。\n   \n\n### 批量获取数据（Retrieving Data in Batches） <span id=\"data-in-batches\"></span>\n\n在 [查询生成器](db-query-builder.md) 中，我们已经解释说可以使用 *批处理查询* 来最小化你的内存使用，\n每当从数据库查询大量数据。你可以在 Active Record 中使用同样的技巧。例如，\n\n```php\n// 每次获取 10 条客户数据\nforeach (Customer::find()->batch(10) as $customers) {\n    // $customers 是个最多拥有 10 条数据的数组\n}\n\n// 每次获取 10 条客户数据，然后一条一条迭代它们\nforeach (Customer::find()->each(10) as $customer) {\n    // $customer 是个 `Customer` 对象\n}\n\n// 贪婪加载模式的批处理查询\nforeach (Customer::find()->with('orders')->each() as $customer) {\n    // $customer 是个 `Customer` 对象，并附带关联的 `'orders'`\n}\n```\n\n\n## 保存数据（Saving Data） <span id=\"inserting-updating-data\"></span>\n\n使用 Active Record，您可以通过以下步骤轻松地将数据保存到数据库：\n\n1. 准备一个 Active Record 实例\n2. 将新值赋给 Active Record 的属性\n3. 调用 [[yii\\db\\ActiveRecord::save()]] 保存数据到数据库中。\n\n例如，\n\n```php\n// 插入新记录\n$customer = new Customer();\n$customer->name = 'James';\n$customer->email = 'james@example.com';\n$customer->save();\n\n// 更新已存在的记录\n$customer = Customer::findOne(123);\n$customer->email = 'james@newexample.com';\n$customer->save();\n```\n\n[[yii\\db\\ActiveRecord::save()|save()]] 方法可能插入或者更新表的记录，这取决于 Active Record 实例的状态。\n如果实例通过 `new` 操作符实例化，调用 [[yii\\db\\ActiveRecord::save()|save()]] 方法将插入新记录；\n如果实例是一个查询方法的结果，调用 [[yii\\db\\ActiveRecord::save()|save()]] 方法\n将更新这个实例对应的表记录行。\n\n你可以通过检查 Active Record 实例的 [[yii\\db\\ActiveRecord::isNewRecord|isNewRecord]] 属性值来区分这两个状态。\n此属性也被使用在 [[yii\\db\\ActiveRecord::save()|save()]] 方法内部，\n代码如下：\n\n```php\npublic function save($runValidation = true, $attributeNames = null)\n{\n    if ($this->getIsNewRecord()) {\n        return $this->insert($runValidation, $attributeNames);\n    } else {\n        return $this->update($runValidation, $attributeNames) !== false;\n    }\n}\n```\n\n> Tip: 你可以直接调用 [[yii\\db\\ActiveRecord::insert()|insert()]] 或者 [[yii\\db\\ActiveRecord::update()|update()]]\n  方法来插入或更新一条记录。\n  \n\n### 数据验证（Data Validation） <span id=\"data-validation\"></span>\n\n因为 [[yii\\db\\ActiveRecord]] 继承于 [[yii\\base\\Model]]，它共享相同的 [输入验证](input-validation.md) 功能。\n你可以通过重写 [[yii\\db\\ActiveRecord::rules()|rules()]] 方法声明验证规则并执行，\n通过调用 [[yii\\db\\ActiveRecord::validate()|validate()]] 方法进行数据验证。\n  \n当你调用 [[yii\\db\\ActiveRecord::save()|save()]] 时，默认情况下会自动调用 [[yii\\db\\ActiveRecord::validate()|validate()]]。\n只有当验证通过时，它才会真正地保存数据; 否则将简单地返回 `false`，\n您可以检查 [[yii\\db\\ActiveRecord::errors|errors]] 属性来获取验证过程的错误消息。\n\n> Tip: 如果你确定你的数据不需要验证（比如说数据来自可信的场景），\n  你可以调用 `save(false)` 来跳过验证过程。\n\n\n### 块赋值（Massive Assignment） <span id=\"massive-assignment\"></span>\n\n和普通的 [模型](structure-models.md) 一样，你亦可以享受 Active Record 实例的 [块赋值](structure-models.md#massive-assignment) 特性。\n使用此功能，您可以在单个 PHP 语句中，给 Active Record 实例的多个属性批量赋值，\n如下所示。 记住，只有 [安全属性](structure-models.md#safe-attributes) 才可以批量赋值。\n\n```php\n$values = [\n    'name' => 'James',\n    'email' => 'james@example.com',\n];\n\n$customer = new Customer();\n\n$customer->attributes = $values;\n$customer->save();\n```\n\n\n### 更新计数（Updating Counters） <span id=\"updating-counters\"></span>\n\n在数据库表中增加或减少一个字段的值是个常见的任务。我们将这些列称为“计数列”。\n您可以使用 [[yii\\db\\ActiveRecord::updateCounters()|updateCounters()]] 更新一个或多个计数列。\n例如，\n\n```php\n$post = Post::findOne(100);\n\n// UPDATE `post` SET `view_count` = `view_count` + 1 WHERE `id` = 100\n$post->updateCounters(['view_count' => 1]);\n```\n\n> Note: 如果你使用 [[yii\\db\\ActiveRecord::save()]] 更新一个计数列，你最终将得到错误的结果，\n  因为可能发生这种情况，多个请求间并发读写同一个计数列。\n\n\n### 脏属性（Dirty Attributes） <span id=\"dirty-attributes\"></span>\n\n当您调用 [[yii\\db\\ActiveRecord::save()|save()]] 保存 Active Record 实例时，只有 *脏属性*\n被保存。如果一个属性的值已被修改，则会被认为是 *脏*，因为它是从 DB 加载出来的或者\n刚刚保存到 DB 。请注意，无论如何 Active Record 都会执行数据验证\n不管有没有脏属性。\n\nActive Record 自动维护脏属性列表。 它保存所有属性的旧值，\n并其与最新的属性值进行比较，就是酱紫个道理。你可以调用 [[yii\\db\\ActiveRecord::getDirtyAttributes()]] \n获取当前的脏属性。你也可以调用 [[yii\\db\\ActiveRecord::markAttributeDirty()]] \n将属性显式标记为脏。\n\n如果你有需要获取属性原先的值，你可以调用\n[[yii\\db\\ActiveRecord::getOldAttributes()|getOldAttributes()]] 或者 [[yii\\db\\ActiveRecord::getOldAttribute()|getOldAttribute()]]。\n\n> 注：属性新旧值的比较是用 `===` 操作符，所以一样的值但类型不同，\n> 依然被认为是脏的。当模型从 HTML 表单接收用户输入时，通常会出现这种情况，\n> 其中每个值都表示为一个字符串类型。\n> 为了确保正确的类型，比如，整型需要用[过滤验证器](input-validation.md#data-filtering)：\n> `['attributeName', 'filter', 'filter' => 'intval']`。其他 PHP 类型转换函数一样适用，像\n> [intval()](https://www.php.net/manual/zh/function.intval.php)， [floatval()](https://www.php.net/manual/zh/function.floatval.php)，\n> [boolval](https://www.php.net/manual/zh/function.boolval.php)，等等\n\n### 默认属性值（Default Attribute Values） <span id=\"default-attribute-values\"></span>\n\n某些表列可能在数据库中定义了默认值。有时，你可能想预先填充\n具有这些默认值的 Active Record 实例的 Web 表单。 为了避免再次写入相同的默认值，\n您可以调用 [[yii\\db\\ActiveRecord::loadDefaultValues()|loadDefaultValues()]] 来填充 DB 定义的默认值\n进入相应的 Active Record 属性：\n\n```php\n$customer = new Customer();\n$customer->loadDefaultValues();\n// $customer->xyz 将被 “zyz” 列定义的默认值赋值\n```\n\n\n### 属性类型转换（Attributes Typecasting） <span id=\"attributes-typecasting\"></span>\n\n在查询结果填充 [[yii\\db\\ActiveRecord]] 时，将自动对其属性值执行类型转换，基于\n[数据库表模式](db-dao.md#database-schema) 中的信息。 这允许从数据表中获取数据，\n声明为整型的，使用 PHP 整型填充 ActiveRecord 实例，布尔值（boolean）的也用布尔值填充，等等。\n但是，类型转换机制有几个限制：\n\n* 浮点值不被转换，并且将被表示为字符串，否则它们可能会使精度降低。\n* 整型值的转换取决于您使用的操作系统的整数容量。尤其是：\n  声明为“无符号整型”或“大整型”的列的值将仅转换为 64 位操作系统的 PHP 整型，\n  而在 32 位操作系统中 - 它们将被表示为字符串。\n\n值得注意的是，只有在从查询结果填充 ActiveRecord 实例时才执行属性类型转换。\n而从 HTTP 请求加载的值或直接通过属性访问赋值的，没有自动转换。\n在准备用于在 ActiveRecord 保存时，准备 SQL 语句还使用了表模式，以确保查询时\n值绑定到具有正确类型的。但是，ActiveRecord 实例的属性值不会\n在保存过程中转换。\n\n> Tip: 你可以使用 [[yii\\behaviors\\AttributeTypecastBehavior]] 来简化属性的类型转换\n  在 ActiveRecord 验证或者保存过程中。\n  \n从 2.0.14 开始，Yii ActiveRecord 支持了更多的复杂数据类型，例如 JSON 或多维数组。\n\n#### MySQL 和 PostgreSQL 中的 JSON（JSON in MySQL and PostgreSQL）\n\n数据填充后，基于 JSON 标准解码规则，\n来自 JSON 列的值将自动解码。\n\n另一方面，为了将属性值保存到 JSON 列中，ActiveRecord 会自动创建一个 [[yii\\db\\JsonExpression|JsonExpression]] 对象，\n这对象将在 [QueryBuilder](db-query-builder.md) 层被编码成 JSON 字符串。\n\n#### PostgreSQL 中的数组（Arrays in PostgreSQL）\n\n数据填充后，来自 Array 列的值将自动从 PgSQL 的编码值解码为 一个 [[yii\\db\\ArrayExpression|ArrayExpression]]\n对象。它继承于 PHP 的 `ArrayAccess` 接口，所以你可以把它当作一个数组用，或者调用 `->getValue()` 来获取数组本身。\n\n另一方面，为了将属性值保存到数组列，ActiveRecord 会自动创建一个 [[yii\\db\\ArrayExpression|ArrayExpression]] 对象，\n这对象将在 [QueryBuilder](db-query-builder.md) 中被编码成数组的 PgSQL 字符串表达式。\n\n你还可以这样使用 JSON 列的条件：\n\n```php\n$query->andWhere(['=', 'json', new ArrayExpression(['foo' => 'bar'])\n```\n\n要详细了解表达式构建系统，可以访问 [Query Builder – 增加自定义条件和语句](db-query-builder.md#adding-custom-conditions-and-expressions)\n文章。\n\n### 更新多个数据行（Updating Multiple Rows） <span id=\"updating-multiple-rows\"></span>\n\n上述方法都可以用于单个 Active Record 实例，以插入或更新单条\n表数据行。 要同时更新多个数据行，你应该调用 [[yii\\db\\ActiveRecord::updateAll()|updateAll()]]\n这是一个静态方法。\n\n```php\n// UPDATE `customer` SET `status` = 1 WHERE `email` LIKE `%@example.com%`\nCustomer::updateAll(['status' => Customer::STATUS_ACTIVE], ['like', 'email', '@example.com']);\n```\n\n同样，你可以调用 [[yii\\db\\ActiveRecord::updateAllCounters()|updateAllCounters()]] 同时更新多条记录的计数列。\n\n\n```php\n// UPDATE `customer` SET `age` = `age` + 1\nCustomer::updateAllCounters(['age' => 1]);\n```\n\n\n## 删除数据（Deleting Data） <span id=\"deleting-data\"></span>\n\n要删除单行数据，首先获取与该行对应的 Active Record 实例，然后调用\n[[yii\\db\\ActiveRecord::delete()]] 方法。\n\n```php\n$customer = Customer::findOne(123);\n$customer->delete();\n```\n\n你可以调用 [[yii\\db\\ActiveRecord::deleteAll()]] 方法删除多行甚至全部的数据。例如，\n\n```php\nCustomer::deleteAll(['status' => Customer::STATUS_INACTIVE]);\n```\n\n> Tip: 调用 [[yii\\db\\ActiveRecord::deleteAll()|deleteAll()]] 时要非常小心，因为如果在指定条件时出错，\n  它可能会完全擦除表中的所有数据。\n\n\n## Active Record 的生命周期（Active Record Life Cycles） <span id=\"ar-life-cycles\"></span>\n\n当你实现各种功能的时候，会发现了解 Active Record 的生命周期很重要。\n在每个生命周期中，一系列的方法将被调用执行，您可以重写这些方法\n以定制你要的生命周期。您还可以响应触发某些 Active Record 事件\n以便在生命周期中注入您的自定义代码。这些事件在开发 Active Record 的 [行为](concept-behaviors.md)时特别有用，\n通过行为可以定制 Active Record 生命周期的 。\n\n下面，我们将总结各种 Active Record 的生命周期，以及生命周期中\n所涉及的各种方法、事件。\n\n\n### 实例化生命周期（New Instance Life Cycle） <span id=\"new-instance-life-cycle\"></span>\n\n当通过 `new` 操作符新建一个 Active Record 实例时，会发生以下生命周期：\n\n1. 类的构造函数调用.\n2. [[yii\\db\\ActiveRecord::init()|init()]]：触发 [[yii\\db\\ActiveRecord::EVENT_INIT|EVENT_INIT]] 事件。\n\n\n### 查询数据生命周期（Querying Data Life Cycle） <span id=\"querying-data-life-cycle\"></span>\n\n当通过 [查询方法](#querying-data) 查询数据时，每个新填充出来的 Active Record 实例\n将发生下面的生命周期：\n\n1. 类的构造函数调用。\n2. [[yii\\db\\ActiveRecord::init()|init()]]：触发 [[yii\\db\\ActiveRecord::EVENT_INIT|EVENT_INIT]] 事件。\n3. [[yii\\db\\ActiveRecord::afterFind()|afterFind()]]：触发 [[yii\\db\\ActiveRecord::EVENT_AFTER_FIND|EVENT_AFTER_FIND]] 事件。\n\n\n### 保存数据生命周期（Saving Data Life Cycle） <span id=\"saving-data-life-cycle\"></span>\n\n当通过 [[yii\\db\\ActiveRecord::save()|save()]] 插入或更新 Active Record 实例时\n会发生以下生命周期：\n\n1. [[yii\\db\\ActiveRecord::beforeValidate()|beforeValidate()]]：触发 \n   [[yii\\db\\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]] 事件。如果这方法返回 `false` \n   或者 [[yii\\base\\ModelEvent::isValid]] 值为 `false`，接下来的步骤都会被跳过。\n2. 执行数据验证。如果数据验证失败，步骤 3 之后的步骤将被跳过。\n3. [[yii\\db\\ActiveRecord::afterValidate()|afterValidate()]]：触发\n   [[yii\\db\\ActiveRecord::EVENT_AFTER_VALIDATE|EVENT_AFTER_VALIDATE]] 事件。\n4. [[yii\\db\\ActiveRecord::beforeSave()|beforeSave()]]：触发\n   [[yii\\db\\ActiveRecord::EVENT_BEFORE_INSERT|EVENT_BEFORE_INSERT]] \n   或者 [[yii\\db\\ActiveRecord::EVENT_BEFORE_UPDATE|EVENT_BEFORE_UPDATE]] 事件。 如果这方法返回 `false` \n   或者 [[yii\\base\\ModelEvent::isValid]] 值为 `false`，接下来的步骤都会被跳过。\n5. 执行真正的数据插入或者更新。\n6. [[yii\\db\\ActiveRecord::afterSave()|afterSave()]]：触发\n   [[yii\\db\\ActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]] \n   或者 [[yii\\db\\ActiveRecord::EVENT_AFTER_UPDATE|EVENT_AFTER_UPDATE]] 事件。\n   \n\n### 删除数据生命周期（Deleting Data Life Cycle） <span id=\"deleting-data-life-cycle\"></span>\n\n当通过 [[yii\\db\\ActiveRecord::delete()|delete()]] 删除 Active Record 实例时，\n会发生以下生命周期：\n\n1. [[yii\\db\\ActiveRecord::beforeDelete()|beforeDelete()]]：触发\n   [[yii\\db\\ActiveRecord::EVENT_BEFORE_DELETE|EVENT_BEFORE_DELETE]] 事件。 如果这方法返回 `false` \n   或者 [[yii\\base\\ModelEvent::isValid]] 值为 `false`，接下来的步骤都会被跳过。\n2. 执行真正的数据删除。\n3. [[yii\\db\\ActiveRecord::afterDelete()|afterDelete()]]：触发\n   [[yii\\db\\ActiveRecord::EVENT_AFTER_DELETE|EVENT_AFTER_DELETE]] 事件。\n\n\n> Tip: 调用以下方法则不会启动上述的任何生命周期，\n> 因为这些方法直接操作数据库，而不是基于 Active Record 模型：\n>\n> - [[yii\\db\\ActiveRecord::updateAll()]] \n> - [[yii\\db\\ActiveRecord::deleteAll()]]\n> - [[yii\\db\\ActiveRecord::updateCounters()]] \n> - [[yii\\db\\ActiveRecord::updateAllCounters()]] \n\n### 刷新数据生命周期（Refreshing Data Life Cycle） <span id=\"refreshing-data-life-cycle\"></span>\n\n当通过 [[yii\\db\\ActiveRecord::refresh()|refresh()]] 刷新 Active Record 实例时，\n如刷新成功方法返回 `true`，那么 [[yii\\db\\ActiveRecord::EVENT_AFTER_REFRESH|EVENT_AFTER_REFRESH]] 事件将被触发。\n\n\n## 事务操作（Working with Transactions） <span id=\"transactional-operations\"></span>\n\nActive Record 有两种方式来使用[事务](db-dao.md#performing-transactions)。\n\n第一种方法是在事务块中显式地包含 Active Record 的各个方法调用，如下所示，\n\n```php\n$customer = Customer::findOne(123);\n\nCustomer::getDb()->transaction(function($db) use ($customer) {\n    $customer->id = 200;\n    $customer->save();\n    // ...其他 DB 操作...\n});\n\n// 或者\n\n$transaction = Customer::getDb()->beginTransaction();\ntry {\n    $customer->id = 200;\n    $customer->save();\n    // ...other DB operations...\n    $transaction->commit();\n} catch(\\Exception $e) {\n    $transaction->rollBack();\n    throw $e;\n} catch(\\Throwable $e) {\n    $transaction->rollBack();\n    throw $e;\n}\n```\n\n> Tip: 在上面的代码中，我们有两个catch块用于兼容\n> PHP 5.x 和 PHP 7.x。 `\\Exception` 继承于 [`\\Throwable` interface](https://www.php.net/manual/zh/class.throwable.php)\n> 由于 PHP 7.0 的改动，如果您的应用程序仅使用 PHP 7.0 及更高版本，您可以跳过 `\\Exception` 部分。\n\n第二种方法是在 [[yii\\db\\ActiveRecord::transactions()]] 方法中列出需要事务支持的 DB 操作。 \n例如，\n\n```php\nclass Customer extends ActiveRecord\n{\n    public function transactions()\n    {\n        return [\n            'admin' => self::OP_INSERT,\n            'api' => self::OP_INSERT | self::OP_UPDATE | self::OP_DELETE,\n            // 上面等价于：\n            // 'api' => self::OP_ALL,\n        ];\n    }\n}\n```\n\n[[yii\\db\\ActiveRecord::transactions()]] 方法应当返回以 [场景](structure-models.md#scenarios) 为键、\n以需要放到事务中的 DB 操作为值的数组。以下的常量\n可以表示相应的 DB 操作：\n\n* [[yii\\db\\ActiveRecord::OP_INSERT|OP_INSERT]]：插入操作用于执行 [[yii\\db\\ActiveRecord::insert()|insert()]]；\n* [[yii\\db\\ActiveRecord::OP_UPDATE|OP_UPDATE]]：更新操作用于执行 [[yii\\db\\ActiveRecord::update()|update()]]；\n* [[yii\\db\\ActiveRecord::OP_DELETE|OP_DELETE]]：删除操作用于执行 [[yii\\db\\ActiveRecord::delete()|delete()]]。\n\n使用 `|` 运算符连接上述常量来表明多个操作。您也可以使用\n快捷常量 [[yii\\db\\ActiveRecord::OP_ALL|OP_ALL]] 来指代上述所有的三个操作。\n\n这个事务方法的原理是：相应的事务在调用 [[yii\\db\\ActiveRecord::beforeSave()|beforeSave()]] 方法时开启，\n在调用 [[yii\\db\\ActiveRecord::afterSave()|afterSave()]] 方法时被提交。\n\n## 乐观锁（Optimistic Locks） <span id=\"optimistic-locks\"></span>\n\n乐观锁是一种防止此冲突的方法：一行数据\n同时被多个用户更新。例如，同一时间内，用户 A 和用户 B 都在编辑\n相同的 wiki 文章。用户 A 保存他的编辑后，用户 B 也点击“保存”按钮来\n保存他的编辑。实际上，用户 B 正在处理的是过时版本的文章，\n因此最好是，想办法阻止他保存文章并向他提示一些信息。\n\n乐观锁通过使用一个字段来记录每行的版本号来解决上述问题。\n当使用过时的版本号保存一行数据时，[[yii\\db\\StaleObjectException]] 异常\n将被抛出，这阻止了该行的保存。乐观锁只支持更新 [[yii\\db\\ActiveRecord::update()]] \n或者删除 [[yii\\db\\ActiveRecord::delete()]]\n已经存在的单条数据行。\n\n使用乐观锁的步骤，\n\n1. 在与 Active Record 类相关联的 DB 表中创建一个列，以存储每行的版本号。\n   这个列应当是长整型（在 MySQL 中是  `BIGINT DEFAULT 0`）。\n2. 重写 [[yii\\db\\ActiveRecord::optimisticLock()]] 方法返回这个列的命名。\n3. 在你的 Model 类里实现 [[\\yii\\behaviors\\OptimisticLockBehavior|OptimisticLockBehavior]] 行为（注：这个行为类在 2.0.16 版本加入），以便从请求参数里自动解析这个列的值。\n   然后从验证规则中删除 version 属性，因为 [[\\yii\\behaviors\\OptimisticLockBehavior|OptimisticLockBehavior]] 已经处理它了.\n4. 在用于用户填写的 Web 表单中，添加一个隐藏字段（hidden field）来存储正在更新的行的当前版本号。\n5. 在使用 Active Record 更新数据的控制器动作中，要捕获（try/catch） [[yii\\db\\StaleObjectException]] 异常。\n   实现一些业务逻辑来解决冲突（例如合并更改，提示陈旧的数据等等）。\n   \n例如，假定版本列被命名为 `version`。您可以使用下面的代码来实现乐观锁。\n\n\n```php\n// ------ 视图层代码 -------\n\nuse yii\\helpers\\Html;\n\n// ...其他输入栏\necho Html::activeHiddenInput($model, 'version');\n\n\n// ------ 控制器代码 -------\n\nuse yii\\db\\StaleObjectException;\n\npublic function actionUpdate($id)\n{\n    $model = $this->findModel($id);\n\n    try {\n        if ($model->load(Yii::$app->request->post()) && $model->save()) {\n            return $this->redirect(['view', 'id' => $model->id]);\n        } else {\n            return $this->render('update', [\n                'model' => $model,\n            ]);\n        }\n    } catch (StaleObjectException $e) {\n        // 解决冲突的代码\n    }\n}\n\n// ------ Model 代码 -------\n\nuse yii\\behaviors\\OptimisticLockBehavior;\n\npublic function behaviors()\n{\n    return [\n        OptimisticLockBehavior::class,\n    ];\n}\n\npublic function optimisticLock()\n{\n    return 'version';\n}\n\n```\n> Note: 因为 [[\\yii\\behaviors\\OptimisticLockBehavior|OptimisticLockBehavior]] 仅仅在保存记录的时候被确认，\n> 如果用户提交的有效版本号被直接解析 ：[[\\yii\\web\\Request::getBodyParam()|getBodyParam()]]，\n> 那么你的 Model 将扩展成这样：触发在步骤 3 中子类的行为，与此同时，调用步骤 2 中的父类的定义，\n> 这样你在把 Model 绑定到负责接收用户输入的控制器的同时，有一个专门用于内部逻辑处理的实例，\n> 或者，您可以通过配置其 [[\\yii\\behaviors\\OptimisticLockBehavior::$value|value]] 的属性来实现自己的逻辑。（注：这一堆都是在解释 Behaviors 的原理）\n\n\n## 使用关联数据（Working with Relational Data） <span id=\"relational-data\"></span>\n\n除了处理单个数据库表之外，Active Record 还可以将相关数据集中进来，\n使其可以通过原始数据轻松访问。 例如，客户数据与订单数据相关\n因为一个客户可能已经存放了一个或多个订单。这种关系通过适当的声明，\n你可以使用 `$customer->orders` 表达式访问客户的订单信息\n这表达式将返回包含 `Order` Active Record 实例的客户订单信息的数组。\n\n\n### 声明关联关系（Declaring Relations） <span id=\"declaring-relations\"></span>\n\n你必须先在 Active Record 类中定义关联关系，才能使用 Active Record 的关联数据。\n简单地为每个需要定义关联关系声明一个 *关联方法* 即可，如下所示，\n\n```php\nclass Customer extends ActiveRecord\n{\n    // ...\n\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n}\n\nclass Order extends ActiveRecord\n{\n    // ...\n\n    public function getCustomer()\n    {\n        return $this->hasOne(Customer::class, ['id' => 'customer_id']);\n    }\n}\n```\n\n上述的代码中，我们为 `Customer` 类声明了一个 `orders` 关联，\n和为 `Order` 声明了一个 `customer` 关联。\n\n每个关联方法必须这样命名：`getXyz`。然后我们通过 `xyz`（首字母小写）调用这个关联名。\n请注意关联名是大小写敏感的。\n\n当声明一个关联关系的时候，必须指定好以下的信息：\n\n- 关联的对应关系：通过调用 [[yii\\db\\ActiveRecord::hasMany()|hasMany()]]\n  或者 [[yii\\db\\ActiveRecord::hasOne()|hasOne()]] 指定。在上面的例子中，您可以很容易看出这样的关联声明：\n  一个客户可以有很多订单，而每个订单只有一个客户。\n- 相关联 Active Record 类名：用来指定为 [[yii\\db\\ActiveRecord::hasMany()|hasMany()]] 或者 \n  [[yii\\db\\ActiveRecord::hasOne()|hasOne()]] 方法的第一个参数。\n  推荐的做法是调用 `Xyz::class` 来获取类名称的字符串，以便您\n  可以使用 IDE 的自动补全，以及让编译阶段的错误检测生效。\n- 两组数据的关联列：用以指定两组数据相关的列（hasOne()/hasMany() 的第二个参数）。\n  数组的值填的是主数据的列（当前要声明关联的 Active Record 类为主数据），\n  而数组的键要填的是相关数据的列。\n\n  一个简单的口诀，先附表的主键，后主表的主键。\n  正如上面的例子，`customer_id` 是 `Order` 的属性，而 `id`是 `Customer` 的属性。\n  （译者注：hasMany() 的第二个参数，这个数组键值顺序不要弄反了）\n  \n\n### 访问关联数据（Accessing Relational Data） <span id=\"accessing-relational-data\"></span>\n\n定义了关联关系后，你就可以通过关联名访问相应的关联数据了。就像\n访问一个由关联方法定义的对象一样，具体概念请查看 [属性](concept-properties.md)。\n因此，现在我们可以称它为 *关联属性* 了。\n\n```php\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123\n// $orders 是由 Order 类组成的数组\n$orders = $customer->orders;\n```\n\n> Tip: 当你通过 getter 方法 `getXyz()` 声明了一个叫 `xyz` 的关联属性，你就可以像\n  [属性](concept-properties.md) 那样访问 `xyz`。注意这个命名是区分大小写的。\n  \n如果使用 [[yii\\db\\ActiveRecord::hasMany()|hasMany()]] 声明关联关系，则访问此关联属性\n将返回相关的 Active Record 实例的数组；\n如果使用 [[yii\\db\\ActiveRecord::hasOne()|hasOne()]] 声明关联关系，访问此关联属性\n将返回相关的 Active Record 实例，如果没有找到相关数据的话，则返回 `null`。\n\n当你第一次访问关联属性时，将执行 SQL 语句获取数据，如\n上面的例子所示。如果再次访问相同的属性，将返回先前的结果，而不会重新执行\nSQL 语句。要强制重新执行 SQL 语句，你应该先 unset 这个关联属性，\n如：`unset（$ customer-> orders）`。\n\n> Tip: 虽然这个概念跟 这个 [属性](concept-properties.md) 特性很像，\n> 但是还是有一个很重要的区别。普通对象属性的属性值与其定义的 getter 方法的类型是相同的。\n> 而关联方法返回的是一个 [[yii\\db\\ActiveQuery]] 活动查询生成器的实例。只有当访问关联属性的的时候，\n> 才会返回 [[yii\\db\\ActiveRecord]] Active Record 实例，或者 Active Record 实例组成的数组。\n> \n> ```php\n> $customer->orders; // 获得 `Order` 对象的数组\n> $customer->getOrders(); // 返回 ActiveQuery 类的实例\n> ```\n> \n> 这对于创建自定义查询很有用，下一节将对此进行描述。\n\n\n### 动态关联查询（Dynamic Relational Query） <span id=\"dynamic-relational-query\"></span>\n\n由于关联方法返回 [[yii\\db\\ActiveQuery]] 的实例，因此你可以在执行 DB 查询之前，\n使用查询构建方法进一步构建此查询。例如，\n\n```php\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123 AND `subtotal` > 200 ORDER BY `id`\n$orders = $customer->getOrders()\n    ->where(['>', 'subtotal', 200])\n    ->orderBy('id')\n    ->all();\n```\n\n与访问关联属性不同，每次通过关联方法执行动态关联查询时，\n都会执行 SQL 语句，即使你之前执行过相同的动态关联查询。\n\n有时你可能需要给你的关联声明传递参数，以便您能更方便地执行\n动态关系查询。例如，您可以声明一个 `bigOrders` 关联如下，\n\n```php\nclass Customer extends ActiveRecord\n{\n    public function getBigOrders($threshold = 100) // 老司机的提醒：$threshold 参数一定一定要给个默认值\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id'])\n            ->where('subtotal > :threshold', [':threshold' => $threshold])\n            ->orderBy('id');\n    }\n}\n```\n\n然后你就可以执行以下关联查询：\n\n```php\n// SELECT * FROM `order` WHERE `customer_id` = 123 AND `subtotal` > 200 ORDER BY `id`\n$orders = $customer->getBigOrders(200)->all();\n\n// SELECT * FROM `order` WHERE `customer_id` = 123 AND `subtotal` > 100 ORDER BY `id`\n$orders = $customer->bigOrders;\n```\n\n\n### 中间关联表（Relations via a Junction Table） <span id=\"junction-table\"></span>\n\n在数据库建模中，当两个关联表之间的对应关系是多对多时，\n通常会引入一个[连接表](https://zh.wikipedia.org/wiki/%E5%85%B3%E8%81%94%E5%AE%9E%E4%BD%93)。例如，`order` 表\n和 `item` 表可以通过名为 `order_item` 的连接表相关联。一个 order 将关联多个 order items，\n而一个 order item 也会关联到多个 orders。\n\n当声明这种表关联后，您可以调用 [[yii\\db\\ActiveQuery::via()|via()]] 或 [[yii\\db\\ActiveQuery::viaTable()|viaTable()]]\n指明连接表。[[yii\\db\\ActiveQuery::via()|via()]] 和 [[yii\\db\\ActiveQuery::viaTable()|viaTable()]] 之间的区别是\n前者是根据现有的关联名称来指定连接表，而后者直接使用\n连接表。例如，\n\n```php\nclass Order extends ActiveRecord\n{\n    public function getItems()\n    {\n        return $this->hasMany(Item::class, ['id' => 'item_id'])\n            ->viaTable('order_item', ['order_id' => 'id']);\n    }\n}\n```\n\n或者,\n\n```php\nclass Order extends ActiveRecord\n{\n    public function getOrderItems()\n    {\n        return $this->hasMany(OrderItem::class, ['order_id' => 'id']);\n    }\n\n    public function getItems()\n    {\n        return $this->hasMany(Item::class, ['id' => 'item_id'])\n            ->via('orderItems');\n    }\n}\n```\n\n使用连接表声明的关联和正常声明的关联是等同的，例如，\n\n```php\n// SELECT * FROM `order` WHERE `id` = 100\n$order = Order::findOne(100);\n\n// SELECT * FROM `order_item` WHERE `order_id` = 100\n// SELECT * FROM `item` WHERE `item_id` IN (...)\n// 返回 Item 类组成的数组\n$items = $order->items;\n```\n\n\n### 通过多个表来连接关联声明（Chaining relation definitions via multiple tables） <span id=\"multi-table-relations\"></span>\n\n通过使用 [[yii\\db\\ActiveQuery::via()|via()]] 方法，它还可以通过多个表来定义关联声明。\n再考虑考虑上面的例子，我们有 `Customer`，`Order` 和 `Item` 类。\n我们可以添加一个关联关系到 `Customer` 类，这个关联可以列出了 `Customer`（客户） 的订单下放置的所有 `Item`（商品），\n这个关联命名为 `getPurchasedItems()`，关联声明如下代码示例所示：\n\n```php\nclass Customer extends ActiveRecord\n{\n    // ...\n\n    public function getPurchasedItems()\n    {\n        // 客户的商品，将 Item 中的 'id' 列与 OrderItem 中的 'item_id' 相匹配\n        return $this->hasMany(Item::class, ['id' => 'item_id'])\n                    ->via('orderItems');\n    }\n\n    public function getOrderItems()\n    {\n        // 客户订单中的商品，将 `Order` 的 'id' 列和 OrderItem 的 'order_id' 列相匹配\n        return $this->hasMany(OrderItem::class, ['order_id' => 'id'])\n                    ->via('orders');\n    }\n\n    public function getOrders()\n    {\n        // 见上述列子\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n}\n```\n\n\n### 延迟加载和即时加载（Lazy Loading and Eager Loading） <span id=\"lazy-eager-loading\"></span>\n\n在 [访问关联数据](#accessing-relational-data) 中，我们解释说可以像问正常的对象属性那样\n访问 Active Record 实例的关联属性。SQL 语句仅在\n你第一次访问关联属性时执行。我们称这种关联数据访问方法为 *延迟加载*。\n例如，\n\n```php\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123\n$orders = $customer->orders;\n\n// 没有 SQL 语句被执行\n$orders2 = $customer->orders;\n```\n\n延迟加载使用非常方便。但是，当你需要访问相同的具有多个 Active Record 实例的关联属性时，\n可能会遇到性能问题。请思考一下以下代码示例。\n有多少 SQL 语句会被执行？\n\n```php\n// SELECT * FROM `customer` LIMIT 100\n$customers = Customer::find()->limit(100)->all();\n\nforeach ($customers as $customer) {\n    // SELECT * FROM `order` WHERE `customer_id` = ...\n    $orders = $customer->orders;\n}\n```\n\n你瞅瞅，上面的代码会产生 101 次 SQL 查询！\n这是因为每次你访问 for 循环中不同的 `Customer` 对象的 `orders` 关联属性时，SQL 语句\n都会被执行一次。\n\n为了解决上述的性能问题，你可以使用所谓的 *即时加载*，如下所示，\n\n```php\n// SELECT * FROM `customer` LIMIT 100;\n// SELECT * FROM `orders` WHERE `customer_id` IN (...)\n$customers = Customer::find()\n    ->with('orders')\n    ->limit(100)\n    ->all();\n\nforeach ($customers as $customer) {\n    // 没有任何的 SQL 执行\n    $orders = $customer->orders;\n}\n```\n\n通过调用 [[yii\\db\\ActiveQuery::with()]] 方法，你使 Active Record 在一条 SQL 语句里就返回了这 100 位客户的订单。\n结果就是，你把要执行的 SQL 语句从 101 减少到 2 条！\n\n你可以即时加载一个或多个关联。 你甚至可以即时加载 *嵌套关联* 。嵌套关联是一种\n在相关的 Active Record 类中声明的关联。例如，`Customer` 通过 `orders` 关联属性 与 `Order` 相关联，\n`Order` 与 `Item` 通过 `items` 关联属性相关联。 当查询 `Customer` 时，您可以即时加载\n通过嵌套关联符 `orders.items` 关联的 `items`。\n\n以下代码展示了 [[yii\\db\\ActiveQuery::with()|with()]] 的各种用法。我们假设 `Customer` 类\n有两个关联 `orders` 和 `country`，而 `Order` 类有一个关联 `items`。\n\n```php\n//  即时加载 \"orders\" and \"country\"\n$customers = Customer::find()->with('orders', 'country')->all();\n// 等同于使用数组语法 如下\n$customers = Customer::find()->with(['orders', 'country'])->all();\n// 没有任何的 SQL 执行\n$orders= $customers[0]->orders;\n// 没有任何的 SQL 执行\n$country = $customers[0]->country;\n\n// 即时加载“订单”和嵌套关系“orders.items”\n$customers = Customer::find()->with('orders.items')->all();\n// 访问第一个客户的第一个订单中的商品\n// 没有 SQL 查询执行\n$items = $customers[0]->orders[0]->items;\n```\n\n你也可以即时加载更深的嵌套关联，比如 `a.b.c.d`。所有的父关联都会被即时加载。\n那就是, 当你调用 [[yii\\db\\ActiveQuery::with()|with()]] 来 with `a.b.c.d`, 你将即时加载\n`a`, `a.b`, `a.b.c` and `a.b.c.d`。\n\n> Tip: 一般来说，当即时加载 `N` 个关联，另有 `M` 个关联\n  通过 [连接表](#junction-table) 声明，则会有 `N+M+1` 条 SQL 语句被执行。\n  请注意这样的的嵌套关联 `a.b.c.d` 算四个关联。\n\n当即时加载一个关联，你可以通过匿名函数自定义相应的关联查询。\n例如，\n\n```php\n// 查找所有客户，并带上他们国家和活跃订单\n// SELECT * FROM `customer`\n// SELECT * FROM `country` WHERE `id` IN (...)\n// SELECT * FROM `order` WHERE `customer_id` IN (...) AND `status` = 1\n$customers = Customer::find()->with([\n    'country',\n    'orders' => function ($query) {\n        $query->andWhere(['status' => Order::STATUS_ACTIVE]);\n    },\n])->all();\n```\n\n自定义关联查询时，应该将关联名称指定为数组的键\n并使用匿名函数作为相应的数组的值。匿名函数将接受一个 `$query` 参数\n它用于表示这个自定义的关联执行关联查询的 [[yii\\db\\ActiveQuery]] 对象。\n在上面的代码示例中，我们通过附加一个关于订单状态的附加条件来修改关联查询。\n\n> Tip: 如果你在即时加载的关联中调用 [[yii\\db\\Query::select()|select()]] 方法，你要确保\n> 在关联声明中引用的列必须被 select。否则，相应的模型（Models）可能\n> 无法加载。例如，\n>\n> ```php\n> $orders = Order::find()->select(['id', 'amount'])->with('customer')->all();\n> // $orders[0]->customer 会一直是 `null`。你应该这样写，以解决这个问题：\n> $orders = Order::find()->select(['id', 'amount', 'customer_id'])->with('customer')->all();\n> ```\n\n\n### 关联关系的 JOIN 查询（Joining with Relations） <span id=\"joining-with-relations\"></span>\n\n> Tip: 这小节的内容仅仅适用于关系数据库，\n  比如 MySQL，PostgreSQL 等等。\n\n到目前为止，我们所介绍的关联查询，仅仅是使用主表列\n去查询主表数据。实际应用中，我们经常需要在关联表中使用这些列。例如，\n我们可能要取出至少有一个活跃订单的客户。为了解决这个问题，我们可以\n构建一个 join 查询，如下所示：\n\n```php\n// SELECT `customer`.* FROM `customer`\n// LEFT JOIN `order` ON `order`.`customer_id` = `customer`.`id`\n// WHERE `order`.`status` = 1\n// \n// SELECT * FROM `order` WHERE `customer_id` IN (...)\n$customers = Customer::find()\n    ->select('customer.*')\n    ->leftJoin('order', '`order`.`customer_id` = `customer`.`id`')\n    ->where(['order.status' => Order::STATUS_ACTIVE])\n    ->with('orders')\n    ->all();\n```\n\n> Tip: 在构建涉及 JOIN SQL 语句的连接查询时，清除列名的歧义很重要。\n  通常的做法是将表名称作为前缀加到对应的列名称前。\n\n但是，更好的方法是通过调用 [[yii\\db\\ActiveQuery::joinWith()]] 来利用已存在的关联声明：\n\n```php\n$customers = Customer::find()\n    ->joinWith('orders')\n    ->where(['order.status' => Order::STATUS_ACTIVE])\n    ->all();\n```\n\n两种方法都执行相同的 SQL 语句集。然而，后一种方法更干净、简洁。\n\n默认的，[[yii\\db\\ActiveQuery::joinWith()|joinWith()]] 会使用 `LEFT JOIN` 去连接主表和关联表。\n你可以通过 `$joinType` 参数指定不同的连接类型（比如 `RIGHT JOIN`）。\n如果你想要的连接类型是 `INNER JOIN`，你可以直接用 [[yii\\db\\ActiveQuery::innerJoinWith()|innerJoinWith()]] 方法代替。\n\n调用 [[yii\\db\\ActiveQuery::joinWith()|joinWith()]] 方法会默认 [即时加载](#lazy-eager-loading) 相应的关联数据。\n如果你不需要那些关联数据，你可以指定它的第二个参数 `$eagerLoading` 为 `false`。\n\n> Note: 即使在启用即时加载的情况下使用 [[yii\\db\\ActiveQuery::joinWith()|joinWith()]] 或 [[yii\\db\\ActiveQuery::innerJoinWith()|innerJoinWith()]]，\n  相应的关联数据也**不会**从这个 `JOIN` 查询的结果中填充。 \n  因此，每个连接关系还有一个额外的查询，正如[即时加载](#lazy-eager-loading)部分所述。\n\n和 [[yii\\db\\ActiveQuery::with()|with()]] 一样，你可以 join 多个关联表；你可以动态的自定义\n你的关联查询；你可以使用嵌套关联进行 join。你也可以将 [[yii\\db\\ActiveQuery::with()|with()]]\n和 [[yii\\db\\ActiveQuery::joinWith()|joinWith()]] 组合起来使用。例如：\n\n```php\n$customers = Customer::find()->joinWith([\n    'orders' => function ($query) {\n        $query->andWhere(['>', 'subtotal', 100]);\n    },\n])->with('country')\n    ->all();\n```\n\n有时，当连接两个表时，你可能需要在 JOIN 查询的 `ON` 部分中指定一些额外的条件。\n这可以通过调用 [[yii\\db\\ActiveQuery::onCondition()]] 方法来完成，如下所示：\n\n```php\n// SELECT `customer`.* FROM `customer`\n// LEFT JOIN `order` ON `order`.`customer_id` = `customer`.`id` AND `order`.`status` = 1 \n// \n// SELECT * FROM `order` WHERE `customer_id` IN (...)\n$customers = Customer::find()->joinWith([\n    'orders' => function ($query) {\n        $query->onCondition(['order.status' => Order::STATUS_ACTIVE]);\n    },\n])->all();\n```\n\n以上查询取出 *所有* 客户，并为每个客户取回所有活跃订单。\n请注意，这与我们之前的例子不同，后者仅取出至少有一个活跃订单的客户。\n\n> Tip: 当通过 [[yii\\db\\ActiveQuery::onCondition()|onCondition()]] 修改 [[yii\\db\\ActiveQuery]] 时，\n  如果查询涉及到 JOIN 查询，那么条件将被放在 `ON` 部分。如果查询不涉及\n  JOIN ，条件将自动附加到查询的 `WHERE` 部分。\n  因此，它可以只包含 包含了关联表的列 的条件。（译者注：意思是 onCondition() 中可以只写关联表的列，主表的列写不写都行）\n\n#### 关联表别名（Relation table aliases） <span id=\"relation-table-aliases\"></span>\n\n如前所述，当在查询中使用 JOIN 时，我们需要消除列名的歧义。因此通常为一张表定义\n一个别名。可以通过以下列方式自定义关联查询来设置关联查询的别名：\n\n```php\n$query->joinWith([\n    'orders' => function ($q) {\n        $q->from(['o' => Order::tableName()]);\n    },\n])\n```\n\n然而，这看起来很复杂和耦合，不管是对表名使用硬编码或是调用 `Order::tableName()`。\n从 2.0.7 版本起，Yii 为此提供了一个快捷方法。您现在可以定义和使用关联表的别名，如下所示：\n\n```php\n// 连接 `orders` 关联表并根据 `orders.id` 排序\n$query->joinWith(['orders o'])->orderBy('o.id');\n```\n\n上述语法适用于简单的关联。如果在 join 嵌套关联时，\n需要用到中间表的别名，例如 `$query->joinWith(['orders.product'])`，\n你需要嵌套 joinWith 调用，如下例所示：\n\n```php\n$query->joinWith(['orders o' => function($q) {\n        $q->joinWith('product p');\n    }])\n    ->where('o.amount > 100');\n```\n\n### 反向关联（Inverse Relations） <span id=\"inverse-relations\"></span>\n\n两个 Active Record 类之间的关联声明往往是相互关联的。例如，`Customer` 是\n通过 `orders` 关联到 `Order` ，而`Order` 通过 `customer` 又关联回到了 `Customer`。\n\n```php\nclass Customer extends ActiveRecord\n{\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n}\n\nclass Order extends ActiveRecord\n{\n    public function getCustomer()\n    {\n        return $this->hasOne(Customer::class, ['id' => 'customer_id']);\n    }\n}\n```\n\n现在考虑下面的一段代码：\n\n```php\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123\n$order = $customer->orders[0];\n\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer2 = $order->customer;\n\n// 显示 \"not the same\"\necho $customer2 === $customer ? 'same' : 'not the same';\n```\n\n我们原本认为 `$customer` 和 `$customer2` 是一样的，但不是！其实他们确实包含相同的\n客户数据，但它们是不同的对象。 访问 `$order->customer` 时，需要执行额外的 SQL 语句，\n以填充出一个新对象 `$customer2`。\n\n为了避免上述例子中最后一个 SQL 语句被冗余执行，我们应该告诉 Yii \n`customer` 是 `orders` 的 *反向关联*，可以通过调用 [[yii\\db\\ActiveQuery::inverseOf()|inverseOf()]] 方法声明，\n如下所示：\n\n```php\nclass Customer extends ActiveRecord\n{\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id'])->inverseOf('customer');\n    }\n}\n```\n\n这样修改关联声明后：\n\n```php\n// SELECT * FROM `customer` WHERE `id` = 123\n$customer = Customer::findOne(123);\n\n// SELECT * FROM `order` WHERE `customer_id` = 123\n$order = $customer->orders[0];\n\n// No SQL will be executed\n$customer2 = $order->customer;\n\n// 输出 \"same\"\necho $customer2 === $customer ? 'same' : 'not the same';\n```\n\n> Note: 反向关联不能用在有 [连接表](#junction-table) 关联声明中。\n  也就是说，如果一个关联关系通过 [[yii\\db\\ActiveQuery::via()|via()]] 或 [[yii\\db\\ActiveQuery::viaTable()|viaTable()]] 声明，\n  你就不能再调用 [[yii\\db\\ActiveQuery::inverseOf()|inverseOf()]] 了。\n\n\n## 保存关联数据（Saving Relations） <span id=\"saving-relations\"></span>\n\n在使用关联数据时，您经常需要建立不同数据之间的关联或销毁\n现有关联。这需要为定义的关联的列设置正确的值。通过使用 Active Record，\n你就可以编写如下代码：\n\n```php\n$customer = Customer::findOne(123);\n$order = new Order();\n$order->subtotal = 100;\n// ...\n\n// 为 Order 设置属性以定义与 \"customer\" 的关联关系\n$order->customer_id = $customer->id;\n$order->save();\n```\n\nActive Record 提供了 [[yii\\db\\ActiveRecord::link()|link()]] 方法，可以更好地完成此任务：\n\n```php\n$customer = Customer::findOne(123);\n$order = new Order();\n$order->subtotal = 100;\n// ...\n\n$order->link('customer', $customer);\n```\n\n[[yii\\db\\ActiveRecord::link()|link()]] 方法需要指定关联名\n和要建立关联的目标 Active Record 实例。该方法将修改属性的值\n以连接两个 Active Record 实例，并将其保存到数据库。在上面的例子中，它将设置 `Order` 实例的 `customer_id` 属性\n为 `Customer` 实例的 `id` 属性的值，然后保存\n到数据库。\n\n> Note: 你不能关联两个新的 Active Record 实例。\n\n使用 [[yii\\db\\ActiveRecord::link()|link()]] 的好处在通过 [junction table](#junction-table) 定义关系时更加明显。\n例如，你可以使用以下代码关联 `Order` 实例\n和 `Item` 实例：\n\n```php\n$order->link('items', $item);\n```\n\n上述代码会自动在 `order_item` 关联表中插入一行，以关联 order 和 item 这两个数据记录。\n\n> Info: [[yii\\db\\ActiveRecord::link()|link()]] 方法在保存相应的 Active Record 实例时，\n  将不会执行任何数据验证。在调用此方法之前，\n  您应当验证所有的输入数据。\n\n[[yii\\db\\ActiveRecord::link()|link()]] 方法的反向操作是 [[yii\\db\\ActiveRecord::unlink()|unlink()]] 方法，\n这将可以断掉两个 Active Record 实例间的已经存在了的关联关系。例如，\n\n```php\n$customer = Customer::find()->with('orders')->where(['id' => 123])->one();\n$customer->unlink('orders', $customer->orders[0]);\n```\n\n默认情况下，[[yii\\db\\ActiveRecord::unlink()|unlink()]] 方法将设置指定的外键值，\n以把现有的关联指定为 `null`。此外，你可以选择通过将 `$delete` 参数设置为`true` 传递给方法，\n删除包含此外键值的表记录行。\n \n当关联关系中有连接表时，调用 [[yii\\db\\ActiveRecord::unlink()|unlink()]] 时，\n如果 `$delete` 参数是 `true` 的话，将导致\n连接表中的外键或相应的行被删除。\n\n\n## 跨数据库关联（Cross-Database Relations） <span id=\"cross-database-relations\"></span> \n\nActive Record 允许您在不同数据库驱动的 Active Record 类之间声明关联关系。\n这些数据库可以是不同的类型（例如 MySQL 和 PostgreSQL ，或是 MS SQL 和 MongoDB），它们也可以运行在\n不同的服务器上。你可以使用相同的语法来执行关联查询。例如，\n\n```php\n// Customer 对应的表是关系数据库中（比如 MySQL）的 \"customer\" 表\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public static function tableName()\n    {\n        return 'customer';\n    }\n\n    public function getComments()\n    {\n        // 一个 customer 有很多条评论（comments）\n        return $this->hasMany(Comment::class, ['customer_id' => 'id']);\n    }\n}\n\n// Comment 对应的是 MongoDB 数据库中的  \"comment\" 集合（译者注：MongoDB 中的集合相当于 MySQL 中的表）\nclass Comment extends \\yii\\mongodb\\ActiveRecord\n{\n    public static function collectionName()\n    {\n        return 'comment';\n    }\n\n    public function getCustomer()\n    {\n        // 一条评论对应一位 customer\n        return $this->hasOne(Customer::class, ['id' => 'customer_id']);\n    }\n}\n\n$customers = Customer::find()->with('comments')->all();\n```\n\n本节中描述的大多数关联查询功能，你都可以抄一抄。\n \n> Note: [[yii\\db\\ActiveQuery::joinWith()|joinWith()]] 这个功能限制于某些数据库是否支持跨数据库 JOIN 查询。\n  因此，你再上述的代码里就不能用此方法了，因为 MongoDB 不支持 JOIN 查询。\n\n\n## 自定义查询类（Customizing Query Classes） <span id=\"customizing-query-classes\"></span>\n\n默认情况下，[[yii\\db\\ActiveQuery]] 支持所有 Active Record 查询。要在 Active Record 类中使用自定义的查询类，\n您应该重写 [[yii\\db\\ActiveRecord::find()]] 方法并返回一个你自定义查询类的实例。 \n例如，\n\n```php\n// file Comment.php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass Comment extends ActiveRecord\n{\n    public static function find()\n    {\n        return new CommentQuery(get_called_class());\n    }\n}\n```\n\n现在，对于 `Comment` 类，不管你执行查询（比如 `find()`、`findOne()`），还是定义一个关联（比如 `hasOne()`），\n你都将调用到 `CommentQuery` 实例，而不再是 `ActiveQuery` 实例。\n\n现在你可以定义 `CommentQuery` 类了，发挥你的奇技淫巧，以改善查询构建体验。例如，\n\n```php\n// file CommentQuery.php\nnamespace app\\models;\n\nuse yii\\db\\ActiveQuery;\n\nclass CommentQuery extends ActiveQuery\n{\n    // 默认加上一些条件（可以跳过）\n    public function init()\n    {\n        $this->andOnCondition(['deleted' => false]);\n        parent::init();\n    }\n\n    // ... 在这里加上自定义的查询方法 ...\n\n    public function active($state = true)\n    {\n        return $this->andOnCondition(['active' => $state]);\n    }\n}\n```\n\n> Note: 作为 [[yii\\db\\ActiveQuery::onCondition()|onCondition()]] 方法的替代方案，你应当调用\n  [[yii\\db\\ActiveQuery::andOnCondition()|andOnCondition()]] 或 [[yii\\db\\ActiveQuery::orOnCondition()|orOnCondition()]] 方法来附加新增的条件，\n  不然在一个新定义的查询方法，已存在的条件可能会被覆盖。\n\n然后你就可以先下面这样构建你的查询了：\n\n```php\n$comments = Comment::find()->active()->all();\n$inactiveComments = Comment::find()->active(false)->all();\n```\n\n> Tip: 在大型项目中，建议您使用自定义查询类来容纳大多数与查询相关的代码，\n  以使 Active Record 类保持简洁。\n\n您还可以在 `Comment` 关联关系的定义中或在执行关联查询时，使用刚刚新建查询构建方法：\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public function getActiveComments()\n    {\n        return $this->hasMany(Comment::class, ['customer_id' => 'id'])->active();\n    }\n}\n\n$customers = Customer::find()->joinWith('activeComments')->all();\n\n// 或者这样\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public function getComments()\n    {\n        return $this->hasMany(Comment::class, ['customer_id' => 'id']);\n    }\n}\n\n$customers = Customer::find()->joinWith([\n    'comments' => function($q) {\n        $q->active();\n    }\n])->all();\n```\n\n> Tip: 在 Yii 1.1 中，有个概念叫做 *命名范围*。命名范围在 Yii 2.0 中不再支持，\n  你依然可以使用自定义查询类、查询方法来达到一样的效果。\n\n\n## 选择额外的字段（Selecting extra fields）\n\n当 Active Record 实例从查询结果中填充时，从数据结果集中，\n其属性的值将被相应的列填充。\n\n你可以从查询中获取其他列或值，并将其存储在 Active Record 活动记录中。\n例如，假设我们有一个名为 `room` 的表，其中包含有关酒店可用房间的信息。\n每个房间使用字段 `length`，`width`，`height` 存储有关其空间大小的信息。\n想象一下，我们需要检索出所有可用房间的列表，并按照体积大小倒序排列。\n你不可能使用 PHP 来计算体积，但是，由于我们需要按照它的值对这些记录进行排序，你依然需要 `volume` （体积）\n来显示在这个列表中。\n为了达到这个目标，你需要在你的 `Room` 活动记录类中声明一个额外的字段，它将存储 `volume` 的值：\n\n```php\nclass Room extends \\yii\\db\\ActiveRecord\n{\n    public $volume;\n\n    // ...\n}\n```\n\n然后，你需要撰写一个查询，它可以计算房间的大小并执行排序：\n\n```php\n$rooms = Room::find()\n    ->select([\n        '{{room}}.*', // select all columns\n        '([[length]] * [[width]] * [[height]]) AS volume', // 计算体积\n    ])\n    ->orderBy('volume DESC') // 使用排序\n    ->all();\n\nforeach ($rooms as $room) {\n    echo $room->volume; // 包含了由 SQL 计算出的值\n}\n```\n\n额外字段的特性对于聚合查询非常有用。\n假设您需要显示一系列客户的订单数量。\n首先，您需要使用 `orders` 关系声明一个 `Customer` 类，并指定额外字段来存储 count 结果：\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public $ordersCount;\n\n    // ...\n\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n}\n```\n\n然后你可以编写一个查询来 JOIN 订单表，并计算订单的总数：\n\n```php\n$customers = Customer::find()\n    ->select([\n        '{{customer}}.*', // select customer 表所有的字段\n        'COUNT({{order}}.id) AS ordersCount' // 计算订单总数\n    ])\n    ->joinWith('orders') // 连接表\n    ->groupBy('{{customer}}.id') // 分组查询，以确保聚合函数生效\n    ->all();\n```\n\n使用此方法的一个缺点是，如果数据不是从 SQL 查询上加载的，它必须再单独计算一遍。\n因此，如果你通过常规查询获取个别的数据记录时，它没有额外的 select 语句，那么它\n将无法返回额外字段的实际值。新保存的记录一样会发生这种情。\n\n```php\n$room = new Room();\n$room->length = 100;\n$room->width = 50;\n$room->height = 2;\n\n$room->volume; // 为 `null`, 因为它没有被声明（赋值）\n```\n\n通过 [[yii\\db\\BaseActiveRecord::__get()|__get()]] 和 [[yii\\db\\BaseActiveRecord::__set()|__set()]] 魔术方法\n我们可以将属性赋予行为特性：\n\n```php\nclass Room extends \\yii\\db\\ActiveRecord\n{\n    private $_volume;\n    \n    public function setVolume($volume)\n    {\n        $this->_volume = (float) $volume;\n    }\n    \n    public function getVolume()\n    {\n        if (empty($this->length) || empty($this->width) || empty($this->height)) {\n            return null;\n        }\n        \n        if ($this->_volume === null) {\n            $this->setVolume(\n                $this->length * $this->width * $this->height\n            );\n        }\n        \n        return $this->_volume;\n    }\n\n    // ...\n}\n```\n\n当 select 查询不提供 volume 体积时，这模型将能够自动计算体积的值出来，\n当访问模型的属性的时候。\n\n当定义关联关系的时候，你也可以计算聚合字段：\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    private $_ordersCount;\n\n    public function setOrdersCount($count)\n    {\n        $this->_ordersCount = (int) $count;\n    }\n\n    public function getOrdersCount()\n    {\n        if ($this->isNewRecord) {\n            return null; // 这样可以避免调用空主键进行查询\n        }\n\n        if ($this->_ordersCount === null) {\n            $this->setOrdersCount($this->getOrders()->count()); // 根据关联关系按需计算聚合字段\n        }\n\n        return $this->_ordersCount;\n    }\n\n    // ...\n\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n}\n```\n\n使用此代码，如果 'select' 语句中存在 'ordersCount' - 它会从查询结果集获取数据填充 `Customer::ordersCount` 属性，\n否则它会在被访问的时候，使用 `Customer::orders` 关联按需计算。\n\n这种方法也适用于创建一些关联数据的快捷访问方式，特别是对于聚合。\n例如：\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    /**\n     * 为聚合数据定义一个只读的虚拟属性\n     */\n    public function getOrdersCount()\n    {\n        if ($this->isNewRecord) {\n            return null; //  这样可以避免调用空主键进行查询\n        }\n        \n        return empty($this->ordersAggregation) ? 0 : $this->ordersAggregation[0]['counted'];\n    }\n\n    /**\n     * 声明一个常规的 'orders' 关联\n     */\n    public function getOrders()\n    {\n        return $this->hasMany(Order::class, ['customer_id' => 'id']);\n    }\n\n    /**\n     * 基于 'orders' 关联，声明一个用于查询聚合的新关联\n     */\n    public function getOrdersAggregation()\n    {\n        return $this->getOrders()\n            ->select(['customer_id', 'counted' => 'count(*)'])\n            ->groupBy('customer_id')\n            ->asArray(true);\n    }\n\n    // ...\n}\n\nforeach (Customer::find()->with('ordersAggregation')->all() as $customer) {\n    echo $customer->ordersCount; // 输出关联的聚合数据，而不需要额外的查询，因为我们用了即时加载\n}\n\n$customer = Customer::findOne($pk);\n$customer->ordersCount; // 从延迟加载的关联中，输出聚合数据\n```\n"
  },
  {
    "path": "docs/guide-zh-CN/db-dao.md",
    "content": "数据库访问对象（Database Access Objects）\n=====================================\n\nYii 包含了一个建立在 PHP PDO 之上的数据访问层 (DAO)。DAO为不同的数据库提供了一套统一的API。\n其中 `ActiveRecord` 提供了数据库与模型(MVC 中的 M,Model) 的交互，`QueryBuilder` 用于创建动态的查询语句。\nDAO提供了简单高效的SQL查询，可以用在与数据库交互的各个地方.\n\n使用 Yii DAO 时，你主要需要处理纯 SQL 语句和 PHP 数组。因此，这是访问数据库最高效的方法。\n然而，因为不同数据库之间的 SQL 语法往往是不同的，\n使用 Yii DAO 也意味着你必须花些额外的工夫来创建一个”数据库无关“的应用。\n\nYii DAO 支持下列现成的数据库：\n\n- [MySQL](https://www.mysql.com/)\n- [MariaDB](https://mariadb.com/)\n- [SQLite](https://sqlite.org/)\n- [PostgreSQL](https://www.postgresql.org/)：版本 8.4 或更高\n- [CUBRID](https://www.cubrid.org/)：版本 9.3 或更高。\n- [Oracle](https://www.oracle.com/database/)\n- [MSSQL](https://www.microsoft.com/en-us/sqlserver/default.aspx)：版本 2008 或更高。\n\n> Info: 在Yii 2.1及更高版本中，DAO 支持 CUBRID，Oracle 和 MSSQL\n  不再作为框架的内置核心组件提供。它们必须作为独离的 [扩展](structure-extensions.md) 安装。\n  [yiisoft/yii2-oracle](https://www.yiiframework.com/extension/yiisoft/yii2-oracle) 和\n  [yiisoft/yii2-mssql](https://www.yiiframework.com/extension/yiisoft/yii2-mssql) 都属于\n  [官方扩展](https://www.yiiframework.com/extensions/official)。\n\n> Note: 供 PHP 7 使用的新版 pdo_oci 扩展目前仅发布了源代码，如果你想编译使用请参照 \n  [社区用户提供的编译安装指引](https://github.com/yiisoft/yii2/issues/10975#issuecomment-248479268)。\n  或者你也可以在你的应用中使用 [PDO模拟层](https://github.com/taq/pdooci)。\n\n## 创建数据库连接（Creating DB Connections） <span id=\"creating-db-connections\"></span>\n\n想要访问数据库，你首先需要通过创建一个 [[yii\\db\\Connection]] 实例来与之建立连接。\n\n```php\n$db = new yii\\db\\Connection([\n    'dsn' => 'mysql:host=localhost;dbname=example',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n]);\n```\n\n因为数据库连接经常需要在多个地方使用到，\n一个常见的做法是以[应用组件](structure-application-components.md)的方式来配置它，如下:\n\n```php\nreturn [\n    // ...\n    'components' => [\n        // ...\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=example',\n            'username' => 'root',\n            'password' => '',\n            'charset' => 'utf8',\n        ],\n    ],\n    // ...\n];\n```\n\n之后你就可以通过语句 `Yii::$app->db` 来使用数据库连接了。\n\n> Tip: 如果你的应用需要访问多个数据库，你可以配置多个 DB 应用组件。\n\n配置数据库连接时， 你应该总是通过 [[yii\\db\\Connection::dsn|dsn]] 属性来指明它的数据源名称 (DSN) 。\n不同的数据库有着不同的 DSN 格式。\n请参考 [PHP manual](https://www.php.net/manual/zh/pdo.construct.php) 来获得更多细节。下面是一些例子：\n \n* MySQL, MariaDB: `mysql:host=localhost;dbname=mydatabase`\n* SQLite: `sqlite:/path/to/database/file`\n* PostgreSQL: `pgsql:host=localhost;port=5432;dbname=mydatabase`\n* CUBRID: `cubrid:dbname=demodb;host=localhost;port=33000`\n* MS SQL Server (via sqlsrv driver): `sqlsrv:Server=localhost;Database=mydatabase`\n* MS SQL Server (via dblib driver): `dblib:host=localhost;dbname=mydatabase`\n* MS SQL Server (via mssql driver): `mssql:host=localhost;dbname=mydatabase`\n* Oracle: `oci:dbname=//localhost:1521/mydatabase`\n\n请注意，如果你是通过 ODBC 来连接数据库，你应该配置 [[yii\\db\\Connection::driverName]] 属性，\n以便 Yii 能够知道实际的数据库种类。例如：\n\n```php\n'db' => [\n    'class' => 'yii\\db\\Connection',\n    'driverName' => 'mysql',\n    'dsn' => 'odbc:Driver={MySQL};Server=localhost;Database=test',\n    'username' => 'root',\n    'password' => '',\n],\n```\n\n除了 [[yii\\db\\Connection::dsn|dsn]] 属性，\n你常常需要配置 [[yii\\db\\Connection::username|username]] 和 [[yii\\db\\Connection::password|password]]。请参考 [[yii\\db\\Connection]] 来获取完整的可配置属性列表。\n\n> Info: 当你实例化一个 DB Connection 时，直到你第一次执行 SQL 或者你明确地调用 [[yii\\db\\Connection::open()|open()]] 方法时，\n  才建立起实际的数据库连接。\n\n> Tip: 有时你可能想要在建立起数据库连接时立即执行一些语句来初始化一些环境变量 (比如设置时区或者字符集),\n> 你可以通过为数据库连接的 [[yii\\db\\Connection::EVENT_AFTER_OPEN|afterOpen]] \n> 事件注册一个事件处理器来达到目的。\n> 你可以像这样直接在应用配置中注册处理器：\n>\n> ```php\n> 'db' => [\n>     // ...\n>     'on afterOpen' => function($event) {\n>         // $event->sender refers to the DB connection\n>         $event->sender->createCommand(\"SET time_zone = 'UTC'\")->execute();\n>     }\n> ],\n> ```\n\n\n## 执行 SQL 查询（Executing SQL Queries） <span id=\"executing-sql-queries\"></span>\n\n一旦你拥有了 DB Connection 实例，你可以按照下列步骤来执行 SQL 查询：\n \n1. 使用纯SQL查询来创建出 [[yii\\db\\Command]]；\n2. 绑定参数 (可选的)；\n3. 调用 [[yii\\db\\Command]] 里 SQL 执行方法中的一个。\n\n下列例子展示了几种不同的从数据库取得数据的方法：\n \n```php\n// 返回多行. 每行都是列名和值的关联数组.\n// 如果该查询没有结果则返回空数组\n$posts = Yii::$app->db->createCommand('SELECT * FROM post')\n            ->queryAll();\n\n// 返回一行 (第一行)\n// 如果该查询没有结果则返回 false\n$post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=1')\n           ->queryOne();\n\n// 返回一列 (第一列)\n// 如果该查询没有结果则返回空数组\n$titles = Yii::$app->db->createCommand('SELECT title FROM post')\n             ->queryColumn();\n\n// 返回一个标量值\n// 如果该查询没有结果则返回 false\n$count = Yii::$app->db->createCommand('SELECT COUNT(*) FROM post')\n             ->queryScalar();\n```\n\n> Note: 为了保持精度，即使对应的数据库列类型为数值型，\n> 所有从数据库取得的数据都被表现为字符串。\n\n\n### 绑定参数（Binding Parameters） <span id=\"binding-parameters\"></span>\n\n当使用带参数的 SQL 来创建数据库命令时，\n你几乎总是应该使用绑定参数的方法来防止 SQL 注入攻击，例如：\n\n```php\n$post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status')\n           ->bindValue(':id', $_GET['id'])\n           ->bindValue(':status', 1)\n           ->queryOne();\n```\n\n在 SQL 语句中，你可以嵌入一个或多个参数占位符(例如，上述例子中的 `:id` )。 \n一个参数占位符应该是以冒号开头的字符串。\n之后你可以调用下面绑定参数的方法来绑定参数值：\n\n* [[yii\\db\\Command::bindValue()|bindValue()]]：绑定一个参数值\n* [[yii\\db\\Command::bindValues()|bindValues()]]：在一次调用中绑定多个参数值\n* [[yii\\db\\Command::bindParam()|bindParam()]]：与 [[yii\\db\\Command::bindValue()|bindValue()]]\n  相似，但是也支持绑定参数引用。\n\n下面的例子展示了几个可供选择的绑定参数的方法：\n\n```php\n$params = [':id' => $_GET['id'], ':status' => 1];\n\n$post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status')\n           ->bindValues($params)\n           ->queryOne();\n           \n$post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status', $params)\n           ->queryOne();\n```\n\n绑定参数是通过 [预处理语句](https://www.php.net/manual/zh/mysqli.quickstart.prepared-statements.php) 实现的。\n除了防止 SQL 注入攻击，它也可以通过一次预处理 SQL 语句，\n使用不同参数多次执行，来提升性能。例如：\n\n```php\n$command = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id');\n\n$post1 = $command->bindValue(':id', 1)->queryOne();\n$post2 = $command->bindValue(':id', 2)->queryOne();\n// ...\n```\n\n因为 [[yii\\db\\Command::bindParam()|bindParam()]] 支持通过引用来绑定参数，\n上述代码也可以像下面这样写：\n\n```php\n$command = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id')\n              ->bindParam(':id', $id);\n\n$id = 1;\n$post1 = $command->queryOne();\n\n$id = 2;\n$post2 = $command->queryOne();\n// ...\n```\n\n请注意，在执行语句前你将占位符绑定到 `$id` 变量，\n然后在之后的每次执行前改变变量的值（这通常是用循环来完成的）。\n以这种方式执行查询比为每个不同的参数值执行一次新的查询要高效得多得多。\n\n> Info: 参数绑定仅用于需要将值插入到包含普通SQL的字符串中的地方。\n> 在 [Query Builder](db-query-builder.md) 和 [Active Record](db-active-record.md)\n> 等更高抽象层的许多地方，您经常会指定一组将被转换为 SQL 的值。\n> 在这些地方，参数绑定是由 Yii 内部完成的，因此不需要手动指定参数。\n\n\n### 执行非查询语句（Executing Non-SELECT Queries） <span id=\"non-select-queries\"></span>\n\n上面部分中介绍的 `queryXyz()` 方法都处理的是从数据库返回数据的查询语句。对于那些不取回数据的语句，\n你应该调用的是 [[yii\\db\\Command::execute()]] 方法。例如，\n\n```php\nYii::$app->db->createCommand('UPDATE post SET status=1 WHERE id=1')\n   ->execute();\n```\n\n[[yii\\db\\Command::execute()]] 方法返回执行 SQL 所影响到的行数。\n\n对于 INSERT, UPDATE 和 DELETE 语句，不再需要写纯SQL语句了， \n你可以直接调用 [[yii\\db\\Command::insert()|insert()]]、[[yii\\db\\Command::update()|update()]]、[[yii\\db\\Command::delete()|delete()]]，\n来构建相应的 SQL 语句。这些方法将正确地引用表和列名称以及绑定参数值。例如,\n\n```php\n// INSERT (table name, column values)\nYii::$app->db->createCommand()->insert('user', [\n    'name' => 'Sam',\n    'age' => 30,\n])->execute();\n\n// UPDATE (table name, column values, condition)\nYii::$app->db->createCommand()->update('user', ['status' => 1], 'age > 30')->execute();\n\n// DELETE (table name, condition)\nYii::$app->db->createCommand()->delete('user', 'status = 0')->execute();\n```\n\n你也可以调用 [[yii\\db\\Command::batchInsert()|batchInsert()]] 来一次插入多行，\n这比一次插入一行要高效得多：\n\n```php\n// table name, column names, column values\nYii::$app->db->createCommand()->batchInsert('user', ['name', 'age'], [\n    ['Tom', 30],\n    ['Jane', 20],\n    ['Linda', 25],\n])->execute();\n```\n\n另一个有用的方法是 [[yii\\db\\Command::upsert()|upsert()]]。Upsert 是一种原子操作，如果它们不存在（匹配唯一约束），则将行插入到数据库表中，\n或者在它们执行时更新它们：\n\n```php\nYii::$app->db->createCommand()->upsert('pages', [\n    'name' => 'Front page',\n    'url' => 'https://example.com/', // url is unique\n    'visits' => 0,\n], [\n    'visits' => new \\yii\\db\\Expression('visits + 1'),\n], $params)->execute();\n```\n\n上面的代码将插入一个新的页面记录或自动增加访问计数器。\n\n请注意，上述的方法只是构建出语句，\n你总是需要调用 [[yii\\db\\Command::execute()|execute()]] 来真正地执行它们。\n\n\n## 引用表和列名称（Quoting Table and Column Names） <span id=\"quoting-table-and-column-names\"></span>\n\n当写与数据库无关的代码时，正确地引用表和列名称总是一件头疼的事，\n因为不同的数据库有不同的名称引用规则，为了克服这个问题，\n你可以使用下面由 Yii 提出的引用语法。\n\n* `[[column name]]`: 使用两对方括号来将列名括起来; \n* `{{table name}}`: 使用两对大括号来将表名括起来。\n\nYii DAO 将自动地根据数据库的具体语法来将这些结构转化为对应的\n被引用的列或者表名称。\n例如，\n\n```php\n// 在 MySQL 中执行该 SQL : SELECT COUNT(`id`) FROM `employee`\n$count = Yii::$app->db->createCommand(\"SELECT COUNT([[id]]) FROM {{employee}}\")\n            ->queryScalar();\n```\n\n\n### 使用表前缀（Using Table Prefix） <span id=\"using-table-prefix\"></span>\n\n如果你的数据库表名大多都拥有一个共同的前缀，\n你可以使用 Yii DAO 所提供的表前缀功能。\n\n首先，通过应用配置中的 [[yii\\db\\Connection::tablePrefix]] 属性来指定表前缀：\n\n```php\nreturn [\n    // ...\n    'components' => [\n        // ...\n        'db' => [\n            // ...\n            'tablePrefix' => 'tbl_',\n        ],\n    ],\n];\n```\n\n接着在你的代码中，当你需要涉及到一张表名中包含该前缀的表时，\n应使用语法 `{{%table_name}}`。百分号将被自动地替换为你在配置 DB 组件时指定的表前缀。\n例如，\n\n```php\n// 在 MySQL 中执行该 SQL: SELECT COUNT(`id`) FROM `tbl_employee`\n$count = Yii::$app->db->createCommand(\"SELECT COUNT([[id]]) FROM {{%employee}}\")\n            ->queryScalar();\n```\n\n\n## 执行事务（Performing Transactions） <span id=\"performing-transactions\"></span>\n\n当顺序地执行多个相关的语句时， 你或许需要将它们包在一个事务中来保证数据库的完整性和一致性。\n如果这些语句中的任何一个失败了，\n数据库将回滚到这些语句执行前的状态。\n \n下面的代码展示了一个使用事务的典型方法：\n\n```php\nYii::$app->db->transaction(function($db) {\n    $db->createCommand($sql1)->execute();\n    $db->createCommand($sql2)->execute();\n    // ... executing other SQL statements ...\n});\n```\n\n上述代码等价于下面的代码，但是下面的代码给予了你对于错误处理代码的更多掌控：\n\n```php\n$db = Yii::$app->db;\n$transaction = $db->beginTransaction();\ntry {\n    $db->createCommand($sql1)->execute();\n    $db->createCommand($sql2)->execute();\n    // ... executing other SQL statements ...\n    \n    $transaction->commit();\n} catch(\\Exception $e) {\n    $transaction->rollBack();\n    throw $e;\n} catch(\\Throwable $e) {\n    $transaction->rollBack();\n    throw $e;\n}\n```\n\n通过调用 [[yii\\db\\Connection::beginTransaction()|beginTransaction()]] 方法，\n一个新事务开始了。\n事务被表示为一个存储在 `$transaction` 变量中的 [[yii\\db\\Transaction]] 对象。\n然后，被执行的语句都被包含在一个 `try...catch...` 块中。\n如果所有的语句都被成功地执行了，\n[[yii\\db\\Transaction::commit()|commit()]] 将被调用来提交这个事务。\n否则， 如果异常被触发并被捕获，\n[[yii\\db\\Transaction::rollBack()|rollBack()]] 方法将被调用，\n来回滚事务中失败语句之前所有语句所造成的改变。\n `throw $e` 将重新抛出该异常，\n就好像我们没有捕获它一样，\n因此正常的错误处理程序将处理它。\n\n### 指定隔离级别（Specifying Isolation Levels） <span id=\"specifying-isolation-levels\"></span>\n\nYii 也支持为你的事务设置[隔离级别]。默认情况下，当我们开启一个新事务，\n它将使用你的数据库所设定的隔离级别。你也可以向下面这样重载默认的隔离级别，\n\n```php\n$isolationLevel = \\yii\\db\\Transaction::REPEATABLE_READ;\n\nYii::$app->db->transaction(function ($db) {\n    ....\n}, $isolationLevel);\n \n// or alternatively\n\n$transaction = Yii::$app->db->beginTransaction($isolationLevel);\n```\n\nYii 为四个最常用的隔离级别提供了常量：\n\n- [[\\yii\\db\\Transaction::READ_UNCOMMITTED]] - 最弱的隔离级别，脏读、不可重复读以及幻读都可能发生。\n- [[\\yii\\db\\Transaction::READ_COMMITTED]] - 避免了脏读。\n- [[\\yii\\db\\Transaction::REPEATABLE_READ]] - 避免了脏读和不可重复读。\n- [[\\yii\\db\\Transaction::SERIALIZABLE]] - 最强的隔离级别， 避免了上述所有的问题。\n\n除了使用上述的常量来指定隔离级别，你还可以使用你的数据库所支持的具有有效语法的字符串。\n比如，在 PostgreSQL 中，你可以使用 `SERIALIZABLE READ ONLY DEFERRABLE`。 \n\n请注意，一些数据库只允许为整个连接设置隔离级别，\n即使你之后什么也没指定，后来的事务都将获得与之前相同的隔离级别。\n使用此功能时，你需要为所有的事务明确地设置隔离级别来避免冲突的设置。\n在本文写作之时，只有 MSSQL 和 SQLite 受这些限制的影响。\n\n> Note: SQLite 只支持两种隔离级别，所以你只能使用 `READ UNCOMMITTED` 和 `SERIALIZABLE`。\n使用其他级别将导致异常的抛出。\n\n> Note: PostgreSQL 不支持在事务开启前设定隔离级别，\n因此，你不能在开启事务时直接指定隔离级别。\n你必须在事务开始后再调用 [[yii\\db\\Transaction::setIsolationLevel()]]。\n\n[隔离级别]: https://zh.wikipedia.org/wiki/%E4%BA%8B%E5%8B%99%E9%9A%94%E9%9B%A2#.E9.9A.94.E7.A6.BB.E7.BA.A7.E5.88.AB\n\n\n### 嵌套事务（Nesting Transactions） <span id=\"nesting-transactions\"></span>\n\n如果你的数据库支持保存点，你可以像下面这样嵌套多个事务：\n\n```php\nYii::$app->db->transaction(function ($db) {\n    // outer transaction\n    \n    $db->transaction(function ($db) {\n        // inner transaction\n    });\n});\n```\n\n或者，\n\n```php\n$db = Yii::$app->db;\n$outerTransaction = $db->beginTransaction();\ntry {\n    $db->createCommand($sql1)->execute();\n\n    $innerTransaction = $db->beginTransaction();\n    try {\n        $db->createCommand($sql2)->execute();\n        $innerTransaction->commit();\n    } catch (\\Exception $e) {\n        $innerTransaction->rollBack();\n        throw $e;\n    } catch (\\Throwable $e) {\n        $innerTransaction->rollBack();\n        throw $e;\n    }\n\n    $outerTransaction->commit();\n} catch (\\Exception $e) {\n    $outerTransaction->rollBack();\n    throw $e;\n} catch (\\Throwable $e) {\n    $outerTransaction->rollBack();\n    throw $e;\n}\n```\n\n\n## 复制和读写分离（Replication and Read-Write Splitting） <span id=\"read-write-splitting\"></span>\n\n许多数据库支持[数据库复制](https://en.wikipedia.org/wiki/Replication_(computing)#Database_replication)来获得更好的数据库可用性，\n以及更快的服务器响应时间。通过数据库复制功能，\n数据从所谓的主服务器被复制到从服务器。所有的写和更新必须发生在主服务器上，\n而读可以发生在从服务器上。\n\n为了利用数据库复制并且完成读写分离，\n你可以按照下面的方法来配置 [[yii\\db\\Connection]] 组件：\n\n```php\n[\n    'class' => 'yii\\db\\Connection',\n\n    // 主库的配置\n    'dsn' => 'dsn for master server',\n    'username' => 'master',\n    'password' => '',\n\n    // 从库的通用配置\n    'slaveConfig' => [\n        'username' => 'slave',\n        'password' => '',\n        'attributes' => [\n            // 使用一个更小的连接超时\n            PDO::ATTR_TIMEOUT => 10,\n        ],\n    ],\n\n    // 从库的配置列表\n    'slaves' => [\n        ['dsn' => 'dsn for slave server 1'],\n        ['dsn' => 'dsn for slave server 2'],\n        ['dsn' => 'dsn for slave server 3'],\n        ['dsn' => 'dsn for slave server 4'],\n    ],\n]\n```\n\n上述的配置指定了一主多从的设置。\n这些从库其中之一将被建立起连接并执行读操作，而主库将被用来执行写操作。\n这样的读写分离将通过上述配置自动地完成。比如，\n\n```php\n// 使用上述配置来创建一个 Connection 实例\nYii::$app->db = Yii::createObject($config);\n\n// 在从库中的一个上执行语句\n$rows = Yii::$app->db->createCommand('SELECT * FROM user LIMIT 10')->queryAll();\n\n// 在主库上执行语句\nYii::$app->db->createCommand(\"UPDATE user SET username='demo' WHERE id=1\")->execute();\n```\n\n> Info: 通过调用 [[yii\\db\\Command::execute()]] 来执行的语句都被视为写操作，\n  而其他所有通过调用 [[yii\\db\\Command]] 中任一 \"query\" 方法来执行的语句都被视为读操作。\n  你可以通过 `Yii::$app->db->slave` 来获取当前有效的从库连接。\n\n`Connection` 组件支持从库间的负载均衡和失效备援，\n当第一次执行读操作时，`Connection` 组件将随机地挑选出一个从库并尝试与之建立连接，\n如果这个从库被发现为”挂掉的“，将尝试连接另一个从库。\n如果没有一个从库是连接得上的，那么将试着连接到主库上。\n通过配置 [[yii\\db\\Connection::serverStatusCache|server status cache]]，\n一个“挂掉的”服务器将会被记住，因此，在一个 yii\\db\\Connection::serverRetryInterval 内将不再试着连接该服务器。\n\n> Info: 在上面的配置中，\n  每个从库都共同地指定了 10 秒的连接超时时间，这意味着，如果一个从库在 10 秒内不能被连接上，它将被视为“挂掉的”。\n  你可以根据你的实际环境来调整该参数。\n\n\n你也可以配置多主多从。例如，\n\n\n```php\n[\n    'class' => 'yii\\db\\Connection',\n\n    // 主库通用的配置\n    'masterConfig' => [\n        'username' => 'master',\n        'password' => '',\n        'attributes' => [\n            // use a smaller connection timeout\n            PDO::ATTR_TIMEOUT => 10,\n        ],\n    ],\n\n    // 主库配置列表\n    'masters' => [\n        ['dsn' => 'dsn for master server 1'],\n        ['dsn' => 'dsn for master server 2'],\n    ],\n\n    // 从库的通用配置\n    'slaveConfig' => [\n        'username' => 'slave',\n        'password' => '',\n        'attributes' => [\n            // use a smaller connection timeout\n            PDO::ATTR_TIMEOUT => 10,\n        ],\n    ],\n\n    // 从库配置列表\n    'slaves' => [\n        ['dsn' => 'dsn for slave server 1'],\n        ['dsn' => 'dsn for slave server 2'],\n        ['dsn' => 'dsn for slave server 3'],\n        ['dsn' => 'dsn for slave server 4'],\n    ],\n]\n```\n\n上述配置指定了两个主库和两个从库。 \n`Connection` 组件在主库之间，也支持如从库间般的负载均衡和失效备援。\n唯一的差别是，如果没有主库可用，将抛出一个异常。\n\n> Note: 当你使用 [[yii\\db\\Connection::masters|masters]] 属性来配置一个或多个主库时，\n  所有其他指定数据库连接的属性 (例如 `dsn`, `username`, `password`) \n  与 `Connection` 对象本身将被忽略。\n\n\n默认情况下，事务使用主库连接，\n一个事务内，所有的数据库操作都将使用主库连接，例如，\n\n```php\n$db = Yii::$app->db;\n// 在主库上启动事务\n$transaction = $db->beginTransaction();\n\ntry {\n    // 两个语句都是在主库上执行的\n    $rows = $db->createCommand('SELECT * FROM user LIMIT 10')->queryAll();\n    $db->createCommand(\"UPDATE user SET username='demo' WHERE id=1\")->execute();\n\n    $transaction->commit();\n} catch(\\Exception $e) {\n    $transaction->rollBack();\n    throw $e;\n} catch(\\Throwable $e) {\n    $transaction->rollBack();\n    throw $e;\n}\n```\n\n如果你想在从库上开启事务，你应该明确地像下面这样做：\n\n```php\n$transaction = Yii::$app->db->slave->beginTransaction();\n```\n\n有时，你或许想要强制使用主库来执行读查询。\n这可以通过 `useMaster()` 方法来完成：\n\n```php\n$rows = Yii::$app->db->useMaster(function ($db) {\n    return $db->createCommand('SELECT * FROM user LIMIT 10')->queryAll();\n});\n```\n\n你也可以明确地将 `Yii::$app->db->enableSlaves` 设置为 false 来将所有的读操作指向主库连接。\n\n\n## 操纵数据库模式（Working with Database Schema） <span id=\"database-schema\"></span>\n\nYii DAO 提供了一套完整的方法来让你操纵数据库模式，\n如创建表、从表中删除一列，等等。这些方法罗列如下：\n\n* [[yii\\db\\Command::createTable()|createTable()]]：创建一张表\n* [[yii\\db\\Command::renameTable()|renameTable()]]：重命名一张表\n* [[yii\\db\\Command::dropTable()|dropTable()]]：删除一张表\n* [[yii\\db\\Command::truncateTable()|truncateTable()]]：删除一张表中的所有行\n* [[yii\\db\\Command::addColumn()|addColumn()]]：增加一列\n* [[yii\\db\\Command::renameColumn()|renameColumn()]]：重命名一列\n* [[yii\\db\\Command::dropColumn()|dropColumn()]]：删除一列\n* [[yii\\db\\Command::alterColumn()|alterColumn()]]：修改一列\n* [[yii\\db\\Command::addPrimaryKey()|addPrimaryKey()]]：增加主键\n* [[yii\\db\\Command::dropPrimaryKey()|dropPrimaryKey()]]：删除主键\n* [[yii\\db\\Command::addForeignKey()|addForeignKey()]]：增加一个外键\n* [[yii\\db\\Command::dropForeignKey()|dropForeignKey()]]：删除一个外键\n* [[yii\\db\\Command::createIndex()|createIndex()]]：增加一个索引\n* [[yii\\db\\Command::dropIndex()|dropIndex()]]：删除一个索引\n\n这些方法可以如下地使用：\n\n```php\n// CREATE TABLE\nYii::$app->db->createCommand()->createTable('post', [\n    'id' => 'pk',\n    'title' => 'string',\n    'text' => 'text',\n]);\n```\n\n上面的数组描述要创建的列的名称和类型。\n对于列的类型， Yii 提供了一套抽象数据类型来允许你定义出数据库无关的模式。\n这些将根据表所在数据库的种类，被转换为特定的类型定义。\n请参考 [[yii\\db\\Command::createTable()|createTable()]]-method 的 API 文档来获取更多信息。\n\n除了改变数据库模式，\n你也可以通过 DB Connection 的 [[yii\\db\\Connection::getTableSchema()|getTableSchema()]] 方法来检索某张表的定义信息。例如，\n\n```php\n$table = Yii::$app->db->getTableSchema('post');\n```\n\n该方法返回一个 [[yii\\db\\TableSchema]] 对象，\n它包含了表中的列、主键、外键，等等的信息。\n所有的这些信息主要被 [query builder](db-query-builder.md) 和 [active record](db-active-record.md) 所使用，来帮助你写出数据库无关的代码。\n"
  },
  {
    "path": "docs/guide-zh-CN/db-migrations.md",
    "content": "数据库迁移\n===========\n\n在开发和维护一个数据库驱动的应用程序时，\n数据库的结构会像代码一样不断演变。\n例如，在开发应用程序的过程中，会增加一张新表且必须得加进来；\n在应用程序被部署到生产环境后，需要建立一个索引来提高查询的性能等等。\n因为一个数据库结构发生改变的时候源代码也经常会需要做出改变，\nYii 提供了一个 *数据库迁移* 功能，该功能可以记录数据库的变化，\n以便使数据库和源代码一起受版本控制。\n\n如下的步骤向我们展示了数据库迁移工具是如何为开发团队所使用的：\n\n1. Tim 创建了一个新的迁移对象（例如，创建一张新表，改变字段的定义等）。\n2. Tim 将这个新的迁移对象提交到代码管理系统（例如，Git，Mercurial）。\n3. Doug 从代码管理系统当中更新版本并获取到这个新的迁移对象。\n4. Doug 把这个迁移对象提交到本地的开发数据库当中，\n   这样一来，Doug 同步了 Tim 所做的修改。\n\n如下的步骤向我们展示了如何发布一个附带数据库迁移的新版本到生产环境当中：\n\n1. Scott 为一个包含数据库迁移的项目版本创建了一个发布标签。\n2. Scott 把发布标签的源代码更新到生产环境的服务器上。\n3. Scott 把所有的增量数据库迁移提交到生产环境数据库当中。\n\nYii 提供了一整套的迁移命令行工具，通过这些工具你可以：\n\n* 创建新的迁移；\n* 提交迁移；\n* 恢复迁移；\n* 重新提交迁移；\n* 展示迁移历史和状态。\n\n所有的这些工具都可以通过 `yii migrate` 命令来进行操作。\n在这一章节，我们将详细的介绍如何使用这些工具来完成各种各样的任务。\n你也可以通过 `yii help migrate` 命令来获取每一种工具的具体使用方法。\n\n> Tip: 迁移不仅仅只作用于数据库表，\n  它同样会调整现有的数据来适应新的表单、创建 RBAC 分层、又或者是清除缓存。\n\n> Note: 当你使用迁移来操作数据时，你会发现使用 [活动记录](db-active-record.md) 类\n> 很有帮助，因为一些逻辑已经在那里实现了。\n> 然而别忘了，相比于天生保持恒定不变的迁移类中的代码，程序逻辑是随时变化的。\n> 所以当你在迁移中使用活动记录类时，活动记录层中逻辑的变化可能会意外打断现有的迁移。\n> 基于这个原因，\n> 迁移代码应该保持独立于其他程序逻辑，比如活动记录类。\n\n\n## 创建迁移 <span id=\"creating-migrations\"></span>\n\n使用如下命令来创建一个新的迁移：\n\n```\nyii migrate/create <name>\n```\n\n必填参数 `name` 的作用是对新的迁移做一个简要的描述。\n例如，如果这个迁移是用来创建一个叫做 *news* 的表，\n那么你可以使用 `create_news_table` 这个名称并运行如下命令：\n\n```\nyii migrate/create create_news_table\n```\n\n> Note: 因为 `name` 参数会被用来生成迁移的类名的一部分，\n  所以该参数应当只包含字母、数字和下划线。\n\n如上命令将会在 `@app/migrations` 目录下创建一个新的名为 `m150101_185401_create_news_table.php` 的 PHP 类文件。\n该文件包含如下的代码，它们用来声明一个迁移类 `m150101_185401_create_news_table`，\n并附有代码框架：\n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function up()\n    {\n\n    }\n\n    public function down()\n    {\n        echo \"m101129_185401_create_news_table cannot be reverted.\\n\";\n\n        return false;\n    }\n\n    /*\n    // Use safeUp/safeDown to run migration code within a transaction\n    public function safeUp()\n    {\n    }\n\n    public function safeDown()\n    {\n    }\n    */\n}\n```\n\n每个数据库迁移都会被定义为一个继承自 [[yii\\db\\Migration]] 的 PHP 类。\n类的名称按照 `m<YYMMDD_HHMMSS>_<Name>` 的格式自动生成，其中\n\n* `<YYMMDD_HHMMSS>` 指执行创建迁移命令的 UTC 时间。\n* `<Name>` 和你执行命令时所带的 `name` 参数值相同。\n\n在迁移类当中，你应当在 `up()` 方法中编写改变数据库结构的代码。\n你可能还需要在 `down()` 方法中编写代码来恢复由 `up()` 方法所做的改变。\n当你通过 migration 升级数据库时， `up()` 方法将会被调用，反之， `down()` 将会被调用。\n如下代码展示了如何通过迁移类来创建一张 `news` 表：\n\n```php\n\nuse yii\\db\\Schema;\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends \\yii\\db\\Migration\n{\n    public function up()\n    {\n        $this->createTable('news', [\n            'id' => Schema::TYPE_PK,\n            'title' => Schema::TYPE_STRING . ' NOT NULL',\n            'content' => Schema::TYPE_TEXT,\n        ]);\n    }\n\n    public function down()\n    {\n        $this->dropTable('news');\n    }\n\n}\n```\n\n> Note: 并不是所有迁移都是可恢复的。例如，如果 `up()` 方法删除了表中的一行数据，\n  这将无法通过 `down()`  方法来恢复这条数据。有时候，你也许只是懒得去执行 `down()` 方法了，\n  因为它在恢复数据库迁移方面并不是那么的通用。在这种情况下，\n  你应当在 `down()` 方法中返回 `false` 来表明这个 migration 是无法恢复的。\n\nmigration 的基类 [[yii\\db\\Migration]] 通过 [[yii\\db\\Migration::db|db]] 属性来连接数据库。\n你可以通过 [操作数据库模式（schema）](db-dao.md#database-schema)\n章节中所描述的那些方法来操作数据库模式（schema）。\n\n当你创建一张表或者一个字段的时候，你应该使用 *抽象类型* 而不是 *实体类型*，\n这样一来你的迁移对象就可以从特定的 DBMS 当中抽离出来。\n[[yii\\db\\Schema]] 类定义了一整套可用的抽象类型常量。这些常量的格式为 `TYPE_<Name>`。\n例如，`TYPE_PK` 指代自增主键类型；`TYPE_STRING` 指代字符串类型。\n当迁移对象被提交到某个特定的数据库的时候，这些抽象类型将会被转换成相对应的实体类型。\n以 MySQL 为例，`TYPE_PK` 将会变成 `int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY`，\n而 `TYPE_STRING` 则变成 `varchar(255)`。\n\n在使用抽象类型的时候，你可以添加额外的约束条件。在上面的例子当中，\n`NOT NULL` 被添加到 `Schema::TYPE_STRING` 当中来指定该字段不能为空。\n\n> Tip: 抽象类型和实体类型之间的映射关系是由每个具体的 `QueryBuilder` \n  类当中的 [[yii\\db\\QueryBuilder::$typeMap|$typeMap]] 属性所指定的。\n  \n从 2.0.6 版本开始，你可以使用能提供更便捷的方法定义字段模式（schema）的新模式（schema）构建器。\n这样上面的迁移就可以写成这样：\n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function up()\n    {\n        $this->createTable('news', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string()->notNull(),\n            'content' => $this->text(),\n        ]);\n    }\n\n    public function down()\n    {\n        $this->dropTable('news');\n    }\n}\n```\n\n所有用来定义字段类型的方法都可以到 [[yii\\db\\SchemaBuilderTrait]] 的 API 文档中查到。\n\n\n## 生成迁移 <span id=\"generating-migrations\"></span>\n\n从 2.0.7 版本开始，迁移终端提供了一种创建迁移的便捷方法。\n\n如果迁移对象的名称遵循某种特定的格式，比如 `create_xxx` 或者 `drop_xxx`，\n那么生成的迁移代码中将包含创建/删除表的额外代码。\n在下文中将描述该特型的所有变型。\n\n### 创建表\n\n```php\nyii migrate/create create_post\n```\n\n生成\n\n```php\n/**\n * Handles the creation for table `post`.\n */\nclass m150811_220037_create_post extends Migration\n{\n    /**\n     * @inheritdoc\n     */\n    public function up()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey()\n        ]);\n    }\n\n    /**\n     * @inheritdoc\n     */\n    public function down()\n    {\n        $this->dropTable('post');\n    }\n}\n```\n\n利用 `--fields` 选项指定字段参数，可以立即创建字段。\n\n```php\nyii migrate/create create_post --fields=\"title:string,body:text\"\n```\n\n生成\n\n```php\n/**\n * Handles the creation for table `post`.\n */\nclass m150811_220037_create_post extends Migration\n{\n    /**\n     * @inheritdoc\n     */\n    public function up()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string(),\n            'body' => $this->text(),\n        ]);\n    }\n\n    /**\n     * @inheritdoc\n     */\n    public function down()\n    {\n        $this->dropTable('post');\n    }\n}\n\n```\n\n你可以指定更多的字段参数。\n\n```php\nyii migrate/create create_post --fields=\"title:string(12):notNull:unique,body:text\"\n```\n\n生成\n\n```php\n/**\n * Handles the creation for table `post`.\n */\nclass m150811_220037_create_post extends Migration\n{\n    /**\n     * @inheritdoc\n     */\n    public function up()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string(12)->notNull()->unique(),\n            'body' => $this->text()\n        ]);\n    }\n\n    /**\n     * @inheritdoc\n     */\n    public function down()\n    {\n        $this->dropTable('post');\n    }\n}\n```\n\n> Note: 主键会被自动添加同时默认名称为 `id`。\n> 如果你想使用其他名称可以使用 `--fields=\"name:primaryKey\"` 来指定名称。\n\n#### 外键\n\n从 2.0.8 版本开始，生成器通过使用 `foreignKey` 关键字支持外键。\n\n```php\nyii migrate/create create_post --fields=\"author_id:integer:notNull:foreignKey(user),category_id:integer:defaultValue(1):foreignKey,title:string,body:text\"\n```\n\n生成\n\n```php\n/**\n * Handles the creation for table `post`.\n * Has foreign keys to the tables:\n *\n * - `user`\n * - `category`\n */\nclass m160328_040430_create_post extends Migration\n{\n    /**\n     * @inheritdoc\n     */\n    public function up()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey(),\n            'author_id' => $this->integer()->notNull(),\n            'category_id' => $this->integer()->defaultValue(1),\n            'title' => $this->string(),\n            'body' => $this->text(),\n        ]);\n\n        // creates index for column `author_id`\n        $this->createIndex(\n            'idx-post-author_id',\n            'post',\n            'author_id'\n        );\n\n        // add foreign key for table `user`\n        $this->addForeignKey(\n            'fk-post-author_id',\n            'post',\n            'author_id',\n            'user',\n            'id',\n            'CASCADE'\n        );\n\n        // creates index for column `category_id`\n        $this->createIndex(\n            'idx-post-category_id',\n            'post',\n            'category_id'\n        );\n\n        // add foreign key for table `category`\n        $this->addForeignKey(\n            'fk-post-category_id',\n            'post',\n            'category_id',\n            'category',\n            'id',\n            'CASCADE'\n        );\n    }\n\n    /**\n     * @inheritdoc\n     */\n    public function down()\n    {\n        // drops foreign key for table `user`\n        $this->dropForeignKey(\n            'fk-post-author_id',\n            'post'\n        );\n\n        // drops index for column `author_id`\n        $this->dropIndex(\n            'idx-post-author_id',\n            'post'\n        );\n\n        // drops foreign key for table `category`\n        $this->dropForeignKey(\n            'fk-post-category_id',\n            'post'\n        );\n\n        // drops index for column `category_id`\n        $this->dropIndex(\n            'idx-post-category_id',\n            'post'\n        );\n\n        $this->dropTable('post');\n    }\n}\n```\n\n`foreignKey` 关键字在字段描述中的位置不会影响生成的代码。\n那意味着：\n\n- `author_id:integer:notNull:foreignKey(user)`\n- `author_id:integer:foreignKey(user):notNull`\n- `author_id:foreignKey(user):integer:notNull`\n\n以上都会生成相同的代码。\n\n`foreignKey` 关键字可以在圆括号中间接收一个参数，\n这个参数将会成为生成外键所需的关联表的名称。\n如果不传入任何参数，那么表名将会根据字段名来生成。\n\n在上面的例子中，`author_id:integer:notNull:foreignKey(user)` 会生成一个\n带有关联 `user` 表的外键，名称为 `author_id` 的字段，\n`category_id:integer:defaultValue(1):foreignKey` 会生成一个\n带有关联 `category` 表的外键，名称为 `category_id` 的字段。\n\n从 2.0.11 版本开始，`foreignKey` 接收第二个参数，跟第一个参数用空格分开。\n这个参数表示生成的外键所关联字段的名称。\n如果不传入第二个参数，字段名将从表模式（schema）中获得。\n如果模式（schema）不存在，或者未设置主键，又或者是联合主键，字段将使用 `id` 作为默认名称。\n\n### 删除表\n\n```php\nyii migrate/create drop_post --fields=\"title:string(12):notNull:unique,body:text\"\n```\n\n生成\n\n```php\nclass m150811_220037_drop_post extends Migration\n{\n    public function up()\n    {\n        $this->dropTable('post');\n    }\n\n    public function down()\n    {\n        $this->createTable('post', [\n            'id' => $this->primaryKey(),\n            'title' => $this->string(12)->notNull()->unique(),\n            'body' => $this->text()\n        ]);\n    }\n}\n```\n\n### 添加字段\n\n如果迁移的名称遵循 `add_xxx_to_yyy` 这样的格式，\n生成的类文件将会包含必要的 `addColumn` 和 `dropColumn`。\n\n添加字段：\n\n```php\nyii migrate/create add_position_to_post --fields=\"position:integer\"\n```\n\n生成\n\n```php\nclass m150811_220037_add_position_to_post extends Migration\n{\n    public function up()\n    {\n        $this->addColumn('post', 'position', $this->integer());\n    }\n\n    public function down()\n    {\n        $this->dropColumn('post', 'position');\n    }\n}\n```\n\n你可以像如下这样指定多个字段：\n\n```\nyii migrate/create add_xxx_column_yyy_column_to_zzz_table --fields=\"xxx:integer,yyy:text\"\n```\n\n### 删除字段\n\n如果迁移的名称遵循 `drop_xxx_from_yyy` 这样的格式，\n生成的类文件将会包含必要的 `addColumn` 和 `dropColumn`。\n\n```php\nyii migrate/create drop_position_from_post --fields=\"position:integer\"\n```\n\n生成\n\n```php\nclass m150811_220037_drop_position_from_post extends Migration\n{\n    public function up()\n    {\n        $this->dropColumn('post', 'position');\n    }\n\n    public function down()\n    {\n        $this->addColumn('post', 'position', $this->integer());\n    }\n}\n```\n\n### 添加连接表\n\n如果迁移的名称遵循 `create_junction_xxx_and_yyy` 这样的格式，\n创建连接表的必要代码将会被生成。\n\n```php\nyii migrate/create create_junction_post_and_tag --fields=\"created_at:dateTime\"\n```\n\n生成\n\n```php\n/**\n * Handles the creation for table `post_tag`.\n * Has foreign keys to the tables:\n *\n * - `post`\n * - `tag`\n */\nclass m160328_041642_create_junction_post_and_tag extends Migration\n{\n    /**\n     * @inheritdoc\n     */\n    public function up()\n    {\n        $this->createTable('post_tag', [\n            'post_id' => $this->integer(),\n            'tag_id' => $this->integer(),\n            'created_at' => $this->dateTime(),\n            'PRIMARY KEY(post_id, tag_id)',\n        ]);\n\n        // creates index for column `post_id`\n        $this->createIndex(\n            'idx-post_tag-post_id',\n            'post_tag',\n            'post_id'\n        );\n\n        // add foreign key for table `post`\n        $this->addForeignKey(\n            'fk-post_tag-post_id',\n            'post_tag',\n            'post_id',\n            'post',\n            'id',\n            'CASCADE'\n        );\n\n        // creates index for column `tag_id`\n        $this->createIndex(\n            'idx-post_tag-tag_id',\n            'post_tag',\n            'tag_id'\n        );\n\n        // add foreign key for table `tag`\n        $this->addForeignKey(\n            'fk-post_tag-tag_id',\n            'post_tag',\n            'tag_id',\n            'tag',\n            'id',\n            'CASCADE'\n        );\n    }\n\n    /**\n     * @inheritdoc\n     */\n    public function down()\n    {\n        // drops foreign key for table `post`\n        $this->dropForeignKey(\n            'fk-post_tag-post_id',\n            'post_tag'\n        );\n\n        // drops index for column `post_id`\n        $this->dropIndex(\n            'idx-post_tag-post_id',\n            'post_tag'\n        );\n\n        // drops foreign key for table `tag`\n        $this->dropForeignKey(\n            'fk-post_tag-tag_id',\n            'post_tag'\n        );\n\n        // drops index for column `tag_id`\n        $this->dropIndex(\n            'idx-post_tag-tag_id',\n            'post_tag'\n        );\n\n        $this->dropTable('post_tag');\n    }\n}\n```\n\n从 2.0.11 版本开始，连接表的外键字段名将从表模式（schema）中获得。\n如果模式（schema）不存在，或者未设置主键，又或者是联合主键，字段将使用 `id` 作为默认名称。\n\n### 事务迁移 <span id=\"transactional-migrations\"></span>\n\n当需要实现复杂的数据库迁移的时候，确定每一个迁移的执行是否成功或失败就变得相当重要了，\n因为这将影响到数据库的完整性和一致性。为了达到这个目标，我们建议你把每个迁移里面的\n数据库操作都封装到一个 [transaction](db-dao.md#performing-transactions) 里面。\n \n实现事务迁移的一个更为简便的方法是把迁移的代码都放到 `safeUp()` 和 `safeDown()` 方法里面。\n它们与 `up()` 和 `down()` 的不同点就在于它们是被隐式的封装到事务当中的。\n如此一来，只要这些方法里面的任何一个操作失败了，那么所有之前的操作都会被自动的回滚。\n\n在如下的例子当中，除了创建 `news` 表以外，我们还插入了一行初始化数据到表里面。\n\n```php\n\nuse yii\\db\\Schema;\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function safeUp()\n    {\n        $this->createTable('news', [\n            'id' => $this->primaryKey(),,\n            'title' => $this->string()->notNull(),\n            'content' => $this->text(),\n        ]);\n        \n        $this->insert('news', [\n            'title' => 'test 1',\n            'content' => 'content 1',\n        ]);\n    }\n\n    public function safeDown()\n    {\n        $this->delete('news', ['id' => 1]);\n        $this->dropTable('news');\n    }\n}\n```\n\n需要注意的是，当你在 `safeUp()` 当中执行多个数据库操作的时候，你应该在 `safeDown()` 方法当中反转它们的执行顺序。\n在上面的例子当中，我们在 `safeUp()` 方法当中首先创建了一张表，然后插入了一条数据；而在 `safeDown()` 方法当中，\n我们首先删除那一行数据，然后才删除那张表。\n\n> Note: 并不是所有的数据库都支持事务。有些数据库查询也是不能被放到事务里面的。\n  在 [implicit commit](https://dev.mysql.com/doc/refman/5.7/en/implicit-commit.html) 章节当中有相关的例子可以参考。\n  如果遇到这种情况的话，那么你应该使用 `up()` 和 `down()` 方法进行替代。\n\n\n### 访问数据库的方法 <span id=\"db-accessing-methods\"></span>\n\n迁移的基类 [[yii\\db\\Migration]] 提供了一整套访问和操作数据库的方法。\n你可能会发现这些方法的命名和 [[yii\\db\\Command]] 类提供的 [DAO 方法](db-dao.md) 很类似。\n例如，[[yii\\db\\Migration::createTable()]] 方法可以创建一张新的表，\n这和 [[yii\\db\\Command::createTable()]] 的功能是一模一样的。\n\n使用 [[yii\\db\\Migration]] 所提供的方法的好处在于你不需要再显式的创建 [[yii\\db\\Command]] 实例，\n而且在执行每个方法的时候都会显示一些有用的信息来告诉我们数据库操作是不是都已经完成，\n还有它们完成这些操作花了多长时间等等。\n\n如下是所有这些数据库访问方法的列表：\n\n* [[yii\\db\\Migration::execute()|execute()]]：执行一条 SQL 语句\n* [[yii\\db\\Migration::insert()|insert()]]：插入单行数据\n* [[yii\\db\\Migration::batchInsert()|batchInsert()]]：插入多行数据\n* [[yii\\db\\Migration::update()|update()]]：更新数据\n* [[yii\\db\\Migration::delete()|delete()]]：删除数据\n* [[yii\\db\\Migration::createTable()|createTable()]]：创建表\n* [[yii\\db\\Migration::renameTable()|renameTable()]]：重命名表名\n* [[yii\\db\\Migration::dropTable()|dropTable()]]：删除一张表\n* [[yii\\db\\Migration::truncateTable()|truncateTable()]]：清空表中的所有数据\n* [[yii\\db\\Migration::addColumn()|addColumn()]]：加一个字段\n* [[yii\\db\\Migration::renameColumn()|renameColumn()]]：重命名字段名称\n* [[yii\\db\\Migration::dropColumn()|dropColumn()]]：删除一个字段\n* [[yii\\db\\Migration::alterColumn()|alterColumn()]]：修改字段\n* [[yii\\db\\Migration::addPrimaryKey()|addPrimaryKey()]]：添加一个主键\n* [[yii\\db\\Migration::dropPrimaryKey()|dropPrimaryKey()]]：删除一个主键\n* [[yii\\db\\Migration::addForeignKey()|addForeignKey()]]：添加一个外键\n* [[yii\\db\\Migration::dropForeignKey()|dropForeignKey()]]：删除一个外键\n* [[yii\\db\\Migration::createIndex()|createIndex()]]：创建一个索引\n* [[yii\\db\\Migration::dropIndex()|dropIndex()]]：删除一个索引\n* [[yii\\db\\Migration::addCommentOnColumn()|addCommentOnColumn()]]：添加字段的注释\n* [[yii\\db\\Migration::dropCommentFromColumn()|dropCommentFromColumn()]]：删除字段的注释\n* [[yii\\db\\Migration::addCommentOnTable()|addCommentOnTable()]]：添加表的注释\n* [[yii\\db\\Migration::dropCommentFromTable()|dropCommentFromTable()]]：删除表的注释\n\n> Tip: [[yii\\db\\Migration]] 并没有提供数据库的查询方法。\n> 这是因为通常你是不需要去数据库把数据一行一行查出来再显示出来的。\n> 另外一个原因是你完全可以使用强大的 [Query Builder 查询构建器](db-query-builder.md) 来构建和查询。  \n> 你可以像这样在迁移中使用查询构建器：\n>\n> ```php\n> // 更新所有用户的 status 字段\n> foreach((new Query)->from('users')->each() as $user) {\n>     $this->update('users', ['status' => 1], ['id' => $user['id']]);\n> }\n> ```\n\n\n## 提交迁移 <span id=\"applying-migrations\"></span>\n\n为了将数据库升级到最新的结构，你应该使用如下命令来提交所有新的迁移：\n\n```\nyii migrate\n```\n\n这条命令会列出迄今为止所有未提交的迁移。如果你确定你需要提交这些迁移，\n它将会按照类名当中的时间戳的顺序，一个接着一个的运行每个新的迁移类里面的 `up()` 或者是 `safeUp()` 方法。\n如果其中任意一个迁移提交失败了，\n那么这条命令将会退出并停止剩下的那些还未执行的迁移。\n\n> Tip: 如果你的服务器没有命令行，\n> 你可以尝试 [web shell](https://github.com/samdark/yii2-webshell) 这个扩展。\n\n对于每一个成功提交的迁移，这条命令都会在一个叫做 `migration` \n的数据库表中插入一条包含应用程序成功提交迁移的记录，\n该记录将帮助迁移工具判断哪些迁移已经提交，哪些还没有提交。\n\n> Info: 迁移工具将会自动在数据库当中创建 `migration` 表，\n  该数据库是在该命令的 [[yii\\console\\controllers\\MigrateController::db|db]] 选项当中指定的。\n  默认情况下，是由 `db` [application component](structure-application-components.md) 指定的。\n  \n有时，你可能只需要提交一个或者少数的几个迁移，\n你可以使用该命令指定需要执行的条数，而不是执行所有的可用迁移。\n例如，如下命令将会尝试提交前三个可用的迁移：\n\n```\nyii migrate 3\n```\n\n你也可以指定一个特定的迁移，按照如下格式使用 `migrate/to` 命令\n来指定数据库应该提交哪一个迁移：\n\n```\nyii migrate/to 150101_185401                      # using timestamp to specify the migration 使用时间戳来指定迁移\nyii migrate/to \"2015-01-01 18:54:01\"              # using a string that can be parsed by strtotime() 使用一个可以被 strtotime() 解析的字符串\nyii migrate/to m150101_185401_create_news_table   # using full name 使用全名\nyii migrate/to 1392853618                         # using UNIX timestamp 使用 UNIX 时间戳\n```\n\n如果在指定要提交的迁移前面还有未提交的迁移，那么在执行这个被指定的迁移之前，\n这些还未提交的迁移会先被提交。\n\n如果被指定提交的迁移在之前已经被提交过，那么在其之后的那些迁移将会被还原。\n\n\n## 还原迁移 <span id=\"reverting-migrations\"></span>\n\n你可以使用如下命令来还原其中一个或多个意见被提交过的迁移：\n\n```\nyii migrate/down     # revert the most recently applied migration 还原最近一次提交的迁移\nyii migrate/down 3   # revert the most 3 recently applied migrations 还原最近三次提交的迁移\n```\n\n> Note: 并不是所有的迁移都能被还原。\n  尝试还原这类迁移将可能导致报错甚至是终止所有的还原进程。\n\n\n## 重做迁移 <span id=\"redoing-migrations\"></span>\n\n重做迁移的意思是先还原指定的迁移，然后再次提交。\n如下所示：\n\n```\nyii migrate/redo        # 重做最近一次提交的迁移\nyii migrate/redo 3      # 重做最近三次提交的迁移\n```\n\n> Note: 如果一个迁移是不能被还原的，那么你将无法对它进行重做。\n\n## 刷新迁移 <span id=\"refreshing-migrations\"></span>\n\n从 2.0.13 版本开始，你可以从数据库中删除所有的表和外键，从头开始重新提交所有迁移。\n\n```\nyii migrate/fresh       # 清空数据库并从头开始应用所有迁移。\n```\n\n## 列出迁移 <span id=\"listing-migrations\"></span>\n\n你可以使用如下命令列出那些提交了的或者是还未提交的迁移：\n\n```\nyii migrate/history     # 显示最近10次提交的迁移\nyii migrate/history 5   # 显示最近5次提交的迁移\nyii migrate/history all # 显示所有已经提交过的迁移\n\nyii migrate/new         # 显示前10个还未提交的迁移\nyii migrate/new 5       # 显示前5个还未提交的迁移\nyii migrate/new all     # 显示所有还未提交的迁移\n```\n\n\n## 修改迁移历史 <span id=\"modifying-migration-history\"></span>\n\n有时候你也许需要简单的标记一下你的数据库已经升级到一个特定的迁移，\n而不是实际提交或者是还原迁移。\n这个经常会发生在你手动的改变数据库的一个特定状态，而又不想相应的迁移被重复提交。\n那么你可以使用如下命令来达到目的：\n\n```\nyii migrate/mark 150101_185401                      # 使用时间戳来指定迁移\nyii migrate/mark \"2015-01-01 18:54:01\"              # 使用一个可以被 strtotime() 解析的字符串\nyii migrate/mark m150101_185401_create_news_table   # 使用全名\nyii migrate/mark 1392853618                         # 使用 UNIX 时间戳\n```\n\n该命令将会添加或者删除 `migration` 表当中的某几行数据来表明数据库已经提交到了指定的某个迁移上。\n执行这条命令期间不会有任何的迁移会被提交或还原。\n\n\n## 自定义迁移 <span id=\"customizing-migrations\"></span>\n\n有很多方法可以自定义迁移命令。\n\n\n### 使用命令行选项 <span id=\"using-command-line-options\"></span>\n\n迁移命令附带了几个命令行选项，可以用来自定义它的行为：\n\n* `interactive`：boolean (默认值为 true)，指定是否以交互模式来运行迁移。\n  当被设置为 true 时，在命令执行某些操作前，会提示用户。如果你希望在后台执行该命令，\n  那么你应该把它设置成 false。\n\n* `migrationPath`：string|array (默认值为 `@app/migrations`)，指定存放所有迁移类文件的目录。该选项可以是一个目录的路径，\n  也可以是 [路径别名](concept-aliases.md)。需要注意的是指定的目录必选存在，\n  否则将会触发一个错误。\n  从 2.0.12 版本开始，可以用一个数组来指定从多个来源读取迁移类文件。\n\n* `migrationTable`：string (默认值为 `migration`)，指定用于存储迁移历史信息的数据库表名称。\n  如果这张表不存在，那么迁移命令将自动创建这张表。当然你也可以使用这样的字段结构：\n  `version varchar(255) primary key, apply_time integer` 来手动创建这张表。\n\n* `db`：string (默认值为 `db`)，指定数据库 [application component](structure-application-components.md) 的 ID。\n  它指的是将会被该命令迁移的数据库。\n\n* `templateFile`：string (默认值为 `@yii/views/migration.php`)，\n  指定生产迁移框架代码类文件的模版文件路径。\n  该选项即可以使用文件路径来指定，也可以使用路径 [别名](concept-aliases.md) 来指定。\n  该模版文件是一个可以使用预定义变量 `$className` 来获取迁移类名称的 PHP 脚本。\n\n* `generatorTemplateFiles`：array (defaults to `[\n        'create_table' => '@yii/views/createTableMigration.php',\n        'drop_table' => '@yii/views/dropTableMigration.php',\n        'add_column' => '@yii/views/addColumnMigration.php',\n        'drop_column' => '@yii/views/dropColumnMigration.php',\n        'create_junction' => '@yii/views/createJunctionMigration.php'\n  ]`)，指定生成迁移代码的模版文件，查看 \"[Generating Migrations](#generating-migrations)\"\n  了解更多细节。\n\n* `fields`：由用来创建迁移代码的多个字段定义字符串所组成的数组。默认是 `[]`。\n  字段定义的格式是 `COLUMN_NAME:COLUMN_TYPE:COLUMN_DECORATOR`。例如，`--fields=name:string(12):notNull` 会创建\n  一个长度为 12 的，非空的，字符串类型的字段。\n\n如下例子向我们展示了如何使用这些选项：\n\n例如，如果我们需要迁移一个 `forum` 模块，\n而该迁移文件放在该模块下的 `migrations` 目录当中，\n那么我们可以使用如下命令： \n\n```\n# 在 forum 模块中以非交互模式进行迁移\nyii migrate --migrationPath=@app/modules/forum/migrations --interactive=0\n```\n\n\n### 全局配置命令 <span id=\"configuring-command-globally\"></span>\n\n为了避免运行迁移命令的时候每次都要重复的输入一些同样的参数，\n你可以选择在应用程序配置当中进行全局配置，一劳永逸：\n\n```php\nreturn [\n    'controllerMap' => [\n        'migrate' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationTable' => 'backend_migration',\n        ],\n    ],\n];\n```\n\n如上所示配置，在每次运行迁移命令的时候，\n`backend_migration` 表将会被用来记录迁移历史。\n你再也不需要通过 `migrationTable` 命令行参数来指定这张历史纪录表了。\n\n\n### 使用命名空间的迁移 <span id=\"namespaced-migrations\"></span>\n\n自从 2.0.10 版本开始，你可以为迁移类使用命名空间。 \n你可以通过 [[yii\\console\\controllers\\MigrateController::migrationNamespaces|migrationNamespaces]] 来指定迁移会用到的命名空间。\n使用命名空间将允许你利用多个源的位置进行迁移。例如：\n\n```php\nreturn [\n    'controllerMap' => [\n        'migrate' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationPath' => null, // disable non-namespaced migrations if app\\migrations is listed below\n            'migrationNamespaces' => [\n                'app\\migrations', // Common migrations for the whole application\n                'module\\migrations', // Migrations for the specific project's module\n                'some\\extension\\migrations', // Migrations for the specific extension\n            ],\n        ],\n    ],\n];\n```\n\n> Note: 从不同命名空间提交的迁移会创建出一份**单一的**迁移历史，\n  比如你不能只从某个特定的命名空间去提交或者还原迁移。\n\n当你正在操作使用命名空间的迁移时：比如创建迁移，还原迁移等，你应当在迁移名称前指明完整的命名空间。\n要注意反斜线(`\\`)在 shell 中会被当成特殊字符，你应该对反斜线进行编码，\n避免 shell 报错或者产生不正确的结果。例如：\n\n```\nyii migrate/create 'app\\\\migrations\\\\createUserTable'\n```\n\n> Note: 通过 [[yii\\console\\controllers\\MigrateController::migrationPath|migrationPath]] 声明的迁移不能包含命名空间，\n  使用命名空间的迁移只能通过 [[yii\\console\\controllers\\MigrateController::migrationNamespaces]] \n  的属性来提交。\n\n从版本 2.0.12 开始，[[yii\\console\\controllers\\MigrateController::migrationPath|migrationPath]] 属性也接收一个数组作为参数,\n这个数组指明了没有使用命名空间的多个迁移的目录。\n这个参数主要用于已经从多个位置进行了迁移的现有项目。\n这些迁移主要来自外部资源，比如其他开发人员开发的 Yii 扩展，\n当使用新方法时，这些迁移很难改成使用命名空间。\n\n### 分离的迁移 <span id=\"separated-migrations\"></span>\n\n有时候我们并不想整个项目所有的迁移都记录到同一份迁移历史中。\n例如：你可能安装了 'blog' 扩展，它有自己独立的功能和迁移，\n不会影响到用于项目主要功能的其他扩展。\n\n如果你想完全分开提交和追踪多个迁移，\n你可以同时配置使用不同命名空间和历史记录表的多条迁移命令：\n\n```php\nreturn [\n    'controllerMap' => [\n        // Common migrations for the whole application\n        'migrate-app' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationNamespaces' => ['app\\migrations'],\n            'migrationTable' => 'migration_app',\n            'migrationPath' => null,\n        ],\n        // Migrations for the specific project's module\n        'migrate-module' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationNamespaces' => ['module\\migrations'],\n            'migrationTable' => 'migration_module',\n            'migrationPath' => null,\n        ],\n        // Migrations for the specific extension\n        'migrate-rbac' => [\n            'class' => 'yii\\console\\controllers\\MigrateController',\n            'migrationPath' => '@yii/rbac/migrations',\n            'migrationTable' => 'migration_rbac',\n        ],\n    ],\n];\n```\n\n请注意，要同步数据库，您现在需要运行多个命令而不是一个：\n\n```\nyii migrate-app\nyii migrate-module\nyii migrate-rbac\n```\n\n\n## 迁移多个数据库 <span id=\"migrating-multiple-databases\"></span>\n\n默认情况下，迁移将会提交到由 `db` [application component](structure-application-components.md) 所定义的同一个数据库当中。\n如果你需要提交到不同的数据库，你可以像下面那样指定 `db` 命令行选项，\n\n```\nyii migrate --db=db2\n```\n\n上面的命令将会把迁移提交到 `db2` 数据库当中。\n\n有些时候你需要提交 *一些* 迁移到一个数据库，而另外一些则提交到另一个数据库。\n为了达到这个目的，你应该在实现一个迁移类的时候指定需要用到的数据库组件的 ID ，\n如下所示：\n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m150101_185401_create_news_table extends Migration\n{\n    public function init()\n    {\n        $this->db = 'db2';\n        parent::init();\n    }\n}\n```\n\n即使你使用 `db` 命令行选项指定了另外一个不同的数据库，上面的迁移还是会被提交到 `db2` 当中。\n需要注意的是这个时候迁移的历史信息依然会被记录到 `db` 命令行选项所指定的数据库当中。\n\n如果有多个迁移都使用到了同一个数据库，那么建议你创建一个迁移的基类，里面包含上述的 `init()` 代码。\n然后每个迁移类都继承这个基类就可以了。\n\n> Tip: 除了在 [[yii\\db\\Migration::db|db]] 参数当中进行设置以外， \n  你还可以通过在迁移类中创建新的数据库连接来操作不同的数据库。\n  然后通过这些连接再使用 [DAO 方法](db-dao.md) 来操作不同的数据库。\n\n另外一个可以让你迁移多个数据库的策略是把迁移存放到不同的目录下，\n然后你可以通过如下命令分别对不同的数据库进行迁移：\n\n```\nyii migrate --migrationPath=@app/migrations/db1 --db=db1\nyii migrate --migrationPath=@app/migrations/db2 --db=db2\n...\n```\n\n第一条命令将会把 `@app/migrations/db1` 目录下的迁移提交到 `db1` 数据库当中，\n第二条命令则会把 `@app/migrations/db2` 下的迁移提交到 `db2` 数据库当中，以此类推。\n"
  },
  {
    "path": "docs/guide-zh-CN/db-query-builder.md",
    "content": "查询构建器\n=============\n\n查询构建器建立在 [Database Access Objects](db-dao.md) 基础之上，可让你创建\n程序化的、DBMS无关的SQL语句。相比于原生的SQL语句，查询构建器可以帮你\n写出可读性更强的SQL相关的代码，并生成安全性更强的SQL语句。\n\n使用查询构建器通常包含以下两个步骤：\n\n1. 创建一个 [[yii\\db\\Query]] 对象来代表一条 SELECT SQL 语句的不同子句（例如 `SELECT`, `FROM`）。\n2. 执行 [[yii\\db\\Query]] 的一个查询方法（例如：`all()`）从数据库当中检索数据。\n\n如下所示代码是查询构造器的一个典型用法：\n\n```php\n$rows = (new \\yii\\db\\Query())\n    ->select(['id', 'email'])\n    ->from('user')\n    ->where(['last_name' => 'Smith'])\n    ->limit(10)\n    ->all();\n```\n\n上面的代码将会生成并执行如下的SQL语句，其中 `:last_name` 参数绑定了\n字符串 `'Smith'`。\n\n```sql\nSELECT `id`, `email` \nFROM `user`\nWHERE `last_name` = :last_name\nLIMIT 10\n```\n\n> Tip: 你平时更多的时候会使用 [[yii\\db\\Query]] 而不是 [[yii\\db\\QueryBuilder]]。\n  当你调用其中一个查询方法时，后者将会被前者隐式的调用。[[yii\\db\\QueryBuilder]]主要负责将\n  DBMS 不相关的 [[yii\\db\\Query]] 对象转换成 DBMS 相关的 SQL 语句（例如，\n  以不同的方式引用表或字段名称）。\n\n\n## 创建查询 <span id=\"building-queries\"></span>\n\n为了创建一个 [[yii\\db\\Query]] 对象，你需要调用不同的查询构建方法来代表SQL语句的不同子句。\n这些方法的名称集成了在SQL语句相应子句中使用的关键字。例如，为了指定 SQL 语句当中的\n`FROM` 子句，你应该调用 `from()` 方法。所有的查询构建器方法返回的是查询对象本身，\n也就是说，你可以把多个方法的调用串联起来。\n\n接下来，我们会对这些查询构建器方法进行一一讲解：\n\n\n### [[yii\\db\\Query::select()|select()]] <span id=\"select\"></span>\n\n[[yii\\db\\Query::select()|select()]] 方法用来指定 SQL 语句当中的 `SELECT` 子句。\n你可以像下面的例子一样使用一个数组或者字符串来定义需要查询的字段。当 SQL 语句\n是由查询对象生成的时候，被查询的字段名称将会自动的被引号括起来。\n \n```php\n$query->select(['id', 'email']);\n\n// 等同于：\n\n$query->select('id, email');\n```\n\n就像写原生 SQL 语句一样，被选取的字段可以包含表前缀，以及/或者字段别名。\n例如： \n\n```php\n$query->select(['user.id AS user_id', 'email']);\n\n// 等同于：\n\n$query->select('user.id AS user_id, email');\n```\n\n如果使用数组格式来指定字段，你可以使用数组的键值来表示字段的别名。\n例如，上面的代码可以被重写为如下形式：\n\n```php\n$query->select(['user_id' => 'user.id', 'email']);\n```\n\n如果你在组建查询时没有调用 [[yii\\db\\Query::select()|select()]] 方法，那么选择的将是 `'*'` ，\n也即选取的是所有的字段。\n\n除了字段名称以外，你还可以选择数据库的表达式。当你使用到包含逗号的数据库表达式的时候，\n你必须使用数组的格式，以避免自动的错误的引号添加。例如：\n\n```php\n$query->select([\"CONCAT(first_name, ' ', last_name) AS full_name\", 'email']); \n```\n\n与所有涉及原始 SQL 的地方一样，当在 select 中编写 DB 表达式时，可以对表名和列名使用\n[与 DBMS 无关的引用语法](db-dao.md#quoting-table-and-column-names)。\n\n从 2.0.1 的版本开始你就可以使用子查询了。在定义每一个子查询的时候，\n你应该使用 [[yii\\db\\Query]] 对象。例如：\n \n```php\n$subQuery = (new Query())->select('COUNT(*)')->from('user');\n\n// SELECT `id`, (SELECT COUNT(*) FROM `user`) AS `count` FROM `post`\n$query = (new Query())->select(['id', 'count' => $subQuery])->from('post');\n```\n\n你应该调用 [[yii\\db\\Query::distinct()|distinct()]] 方法来去除重复行，如下所示：\n\n```php\n// SELECT DISTINCT `user_id` ...\n$query->select('user_id')->distinct();\n```\n\n你可以调用 [[yii\\db\\Query::addSelect()|addSelect()]] 方法来选取附加字段，例如：\n\n```php\n$query->select(['id', 'username'])\n    ->addSelect(['email']);\n```\n\n\n### [[yii\\db\\Query::from()|from()]] <span id=\"from\"></span>\n\n[[yii\\db\\Query::from()|from()]] 方法指定了 SQL 语句当中的 `FROM` 子句。例如：\n\n```php\n// SELECT * FROM `user`\n$query->from('user');\n```\n\n你可以通过字符串或者数组的形式来定义被查询的表名称。就像你写原生的 SQL 语句一样，\n表名称里面可包含数据库前缀，以及/或者表别名。例如：\n\n```php\n$query->from(['public.user u', 'public.post p']);\n\n// 等同于：\n\n$query->from('public.user u, public.post p');\n```\n\n如果你使用的是数组的格式，那么你同样可以用数组的键值来定义表别名，如下所示：\n\n```php\n$query->from(['u' => 'public.user', 'p' => 'public.post']);\n```\n\n除了表名以外，你还可以从子查询中再次查询，这里的子查询是由 [[yii\\db\\Query]] 创建的对象。\n例如：\n\n```php\n$subQuery = (new Query())->select('id')->from('user')->where('status=1');\n\n// SELECT * FROM (SELECT `id` FROM `user` WHERE status=1) u \n$query->from(['u' => $subQuery]);\n```\n\n#### 前缀\n`from` 还可以应用默认的 [[yii\\db\\Connection::$tablePrefix|tablePrefix]] 前缀，实现细节请参考\n[“数据库访问对象指南”的“Quoting Tables”章节](db-dao.md#quoting-table-and-column-names).\n\n### [[yii\\db\\Query::where()|where()]] <span id=\"where\"></span>\n\n[[yii\\db\\Query::where()|where()]] 方法定义了 SQL 语句当中的 `WHERE` 子句。\n你可以使用如下四种格式来定义 `WHERE` 条件：\n\n- 字符串格式，例如：`'status=1'`\n- 哈希格式，例如： `['status' => 1, 'type' => 2]`\n- 操作符格式，例如：`['like', 'name', 'test']`\n- 对象格式，例如：`new LikeCondition('name', 'LIKE', 'test')`\n\n#### 字符串格式 <span id=\"string-format\"></span>\n\n在定义非常简单的查询条件的时候，字符串格式是最合适的。\n它看起来和原生 SQL 语句差不多。例如：\n\n```php\n$query->where('status=1');\n\n// 或使用参数绑定来绑定动态参数值\n$query->where('status=:status', [':status' => $status]);\n\n// 原生 SQL 在日期字段上使用 MySQL YEAR() 函数\n$query->where('YEAR(somedate) = 2015');\n```\n\n千万不要像如下的例子一样直接在条件语句当中嵌入变量，特别是当这些变量来源于终端用户输入的时候，\n因为这样我们的软件将很容易受到 SQL 注入的攻击。\n \n```php\n// 危险！千万别这样干，除非你非常的确定 $status 是一个整型数值。\n$query->where(\"status=$status\");\n```\n\n当使用 `参数绑定` 的时候，你可以调用 [[yii\\db\\Query::params()|params()]] 或者 [[yii\\db\\Query::addParams()|addParams()]] 方法\n来分别绑定不同的参数。\n\n```php\n$query->where('status=:status')\n    ->addParams([':status' => $status]);\n```\n\n与涉及原生 SQL 的所有地方一样，在以字符串格式写入条件时，可以对表名和列名使用 \n[与 DBMS 无关的引用语法](db-dao.md#quoting-table-and-column-names)。\n\n#### 哈希格式 <span id=\"hash-format\"></span>\n\n哈希格式最适合用来指定多个 `AND` 串联起来的简单的\"等于断言\"子条件。\n它是以数组的形式来书写的，数组的键表示字段的名称，而数组的值则表示\n这个字段需要匹配的值。例如：\n\n```php\n// ...WHERE (`status` = 10) AND (`type` IS NULL) AND (`id` IN (4, 8, 15))\n$query->where([\n    'status' => 10,\n    'type' => null,\n    'id' => [4, 8, 15],\n]);\n```\n\n就像你所看到的一样，查询构建器非常的智能，能恰当地处理数值当中的空值和数组。\n\n你也可以像下面那样在子查询当中使用哈希格式： \n\n```php\n$userQuery = (new Query())->select('id')->from('user');\n\n// ...WHERE `id` IN (SELECT `id` FROM `user`)\n$query->where(['id' => $userQuery]);\n```\n\n使用哈希格式，Yii 在内部对相应的值进行参数绑定，与 [字符串格式](#string-format) 相比，\n此处你不需要手动添加参数绑定。但请注意，Yii 不会帮你转义列名，所以如果你\n从用户端获得的变量作为列名而没有进行任何额外的检查，对于 SQL 注入攻击，\n你的程序将变得很脆弱。为了保证应用程序的安全，请不要将变量用作列名\n或者你必须用白名单过滤变量。如果你实在需要从用户获取列名，请阅读 [过滤数据](output-data-widgets.md#filtering-data)\n章节。例如，以下代码易受攻击：\n\n```php\n// 易受攻击的代码：\n$column = $request->get('column');\n$value = $request->get('value');\n$query->where([$column => $value]);\n// $value 是安全的，但是 $column 名不会被转义处理！\n```\n\n#### 操作符格式 <span id=\"operator-format\"></span>\n\n操作符格式允许你指定类程序风格的任意条件语句，如下所示：\n\n```php\n[操作符, 操作数1, 操作数2, ...]\n```\n\n其中每个操作数可以是字符串格式、哈希格式或者嵌套的操作符格式，\n而操作符可以是如下列表中的一个：\n\n- `and`：操作数会被 `AND` 关键字串联起来。例如，`['and', 'id=1', 'id=2']` \n  将会生成 `id=1 AND id=2`。如果操作数是一个数组，它也会按上述规则转换成\n  字符串。例如，`['and', 'type=1', ['or', 'id=1', 'id=2']]` \n  将会生成 `type=1 AND (id=1 OR id=2)`。\n  这个方法不会自动加引号或者转义。\n  \n- `or`：用法和 `and` 操作符类似，这里就不再赘述。\n\n- `not`：只需要操作数 1，它将包含在 `NOT()` 中。例如，`['not'，'id = 1']` 将生成 `['not', 'id=1']`。操作数 1 也可以是个描述多个表达式的数组。例如 `['not', ['status' => 'draft', 'name' => 'example']]` 将生成 `NOT ((status='draft') AND (name='example'))`。\n\n- `between`：第一个操作数为字段名称，第二个和第三个操作数代表的是这个字段\n  的取值范围。例如，`['between', 'id', 1, 10]` 将会生成\n  `id BETWEEN 1 AND 10`。\n  如果你需要建立一个值在两列之间的查询条件（比如 `11 BETWEEN min_id AND max_id`），\n  你应该使用 [[yii\\db\\conditions\\BetweenColumnsCondition|BetweenColumnsCondition]]。\n  请参阅 [条件-对象格式](#object-format) 一章以了解有关条件的对象定义的更多信息。\n\n- `not between`：与 `between` 类似，除了 `BETWEEN` 被 `NOT BETWEEN` 替换\n  在生成条件时。\n\n- `in`：第一个操作数应为字段名称或者 DB 表达式。第二个操作符既可以是一个数组，\n  也可以是一个  `Query` 对象。它会转换成`IN` 条件语句。如果第二个操作数是一个\n  数组，那么它代表的是字段或 DB 表达式的取值范围。如果第二个操作数是 `Query` \n  对象，那么这个子查询的结果集将会作为第一个操作符的字段或者 DB 表达式的取值范围。\n  例如， `['in', 'id', [1, 2, 3]]` 将生成 `id IN (1, 2, 3)`。\n  该方法将正确地为字段名加引号以及为取值范围转义。`in` 操作符还支持组合字段，此时，\n  操作数1应该是一个字段名数组，而操作数2应该是一个数组或者 `Query` 对象，\n  代表这些字段的取值范围。\n\n- `not in`：用法和 `in` 操作符类似，这里就不再赘述。\n\n- `like`：第一个操作数应为一个字段名称或 DB 表达式，\n  第二个操作数可以使字符串或数组，\n  代表第一个操作数需要模糊查询的值。比如，`['like', 'name', 'tester']` 会生成\n  `name LIKE '%tester%'`。 如果范围值是一个数组，那么将会生成用 `AND` 串联起来的\n  多个 `like` 语句。例如，`['like', 'name', ['test', 'sample']]` 将会生成\n  `name LIKE '%test%' AND name LIKE '%sample%'`。\n  你也可以提供第三个可选的操作数来指定应该如何转义数值当中的特殊字符。\n  该操作数是一个从需要被转义的特殊字符到转义副本的数组映射。\n  如果没有提供这个操作数，将会使用默认的转义映射。如果需要禁用转义的功能，\n  只需要将参数设置为 `false` 或者传入一个空数组即可。需要注意的是，\n  当使用转义映射（又或者没有提供第三个操作数的时候），第二个操作数的值的前后\n  将会被加上百分号。\n\n> Note: 当使用 PostgreSQL 的时候你还可以使用 [`ilike`](https://www.postgresql.org/docs/8.3/functions-matching.html#FUNCTIONS-LIKE)，\n> 该方法对大小写不敏感。\n\n- `or like`：用法和 `like` 操作符类似，区别在于当第二个操作数为数组时，\n  会使用 `OR` 来串联多个 `LIKE` 条件语句。\n\n- `not like`：用法和 `like` 操作符类似，区别在于会使用 `NOT LIKE`\n  来生成条件语句。\n\n- `or not like`：用法和 `not like` 操作符类似，区别在于会使用 `OR` \n  来串联多个 `NOT LIKE` 条件语句。\n\n- `exists`：需要一个操作数，该操作数必须是代表子查询 [[yii\\db\\Query]] 的一个实例，\n  它将会构建一个 `EXISTS (sub-query)` 表达式。\n\n- `not exists`：用法和 `exists` 操作符类似，它将创建一个 `NOT EXISTS (sub-query)` 表达式。\n\n- `>`，`<=` 或者其他包含两个操作数的合法 DB 操作符：第一个操作数必须为字段的名称，\n  而第二个操作数则应为一个值。例如，`['>', 'age', 10]` 将会生成 `age>10`。\n\n使用操作符格式，Yii 在内部对相应的值进行参数绑定，因此与 [字符串格式](#string-format) 相比，\n此处你不需要手动添加参数。但请注意，Yii 不会帮你转义列名，所以如果你\n从用户端获得的变量作为列名而没有进行任何额外的检查，对于 SQL 注入攻击，\n你的程序将变得很脆弱。为了保证应用程序的安全，请不要将变量用作列名\n或者你必须用白名单过滤变量。如果你实在需要从用户获取列名，请阅读 [过滤数据](output-data-widgets.md#filtering-data)\n章节。例如，以下代码易受攻击：\n\n```php\n// 易受攻击的代码：\n$column = $request->get('column');\n$value = $request->get('value');\n$query->where(['=', $column, $value]);\n// $value 是安全的，但是 $column 名不会被转义处理！\n```\n\n#### 对象格式（Object Form） <span id=\"object-format\"></span>\n\n对象格式自 2.0.14 开始提供，是定义条件的最强大和最复杂的方法。\n如果你要在查询构建器上构建自己的抽象方法或者如果你要实现自己的复杂条件，\n你需要它（Object Form）\n\n条件类的实例是不可变的。他们唯一的用途是存储条件数据\n并为条件构建器提供 getters 属性。条件构建器是一个包含转换逻辑的类，\n它将存储的条件数据转换为 SQL 表达式。\n\n在内部，上面描述的格式在构建 SQL 之前被隐式转换为对象格式，\n因此可以在单一条件语句下组合适合的格式：\n\n```php\n$query->andWhere(new OrCondition([\n    new InCondition('type', 'in', $types),\n    ['like', 'name', '%good%'],\n    'disabled=false'\n]))\n```\n\n操作符格式与对象格式的对应关系是在\n[[yii\\db\\QueryBuilder::conditionClasses|QueryBuilder::conditionClasses]] 属性中定义，\n这里列举一些比较有代表性的映射关系：\n\n- `AND`, `OR` -> `yii\\db\\conditions\\ConjunctionCondition`\n- `NOT` -> `yii\\db\\conditions\\NotCondition`\n- `IN`, `NOT IN` -> `yii\\db\\conditions\\InCondition`\n- `BETWEEN`, `NOT BETWEEN` -> `yii\\db\\conditions\\BetweenCondition`\n\n等等\n\n使用对象格式可以定义自己的条件集，并且可以更容易维护别人定义的条件集。（注：这里是说对象比数组更可靠）\n更多细节请参考 [Adding Custom Conditions and Expressions](#adding-custom-conditions-and-expressions) 章节。\n\n\n#### 附加条件 <span id=\"appending-conditions\"></span>\n\n你可以使用 [[yii\\db\\Query::andWhere()|andWhere()]] 或者 [[yii\\db\\Query::orWhere()|orWhere()]] 在原有条件的基础上\n附加额外的条件。你可以多次调用这些方法来分别追加不同的条件。\n例如，\n\n```php\n$status = 10;\n$search = 'yii';\n\n$query->where(['status' => $status]);\n\nif (!empty($search)) {\n    $query->andWhere(['like', 'title', $search]);\n}\n```\n\n如果 `$search` 不为空，那么将会生成如下 SQL 语句：\n\n```sql\n... WHERE (`status` = 10) AND (`title` LIKE '%yii%')\n```\n\n\n#### 过滤条件 <span id=\"filter-conditions\"></span>\n\n当 `WHERE` 条件来自于用户的输入时，你通常需要忽略用户输入的空值。\n例如，在一个可以通过用户名或者邮箱搜索的表单当中，用户名或者邮箱\n输入框没有输入任何东西，这种情况下你想要忽略掉对应的搜索条件，\n那么你就可以使用 [[yii\\db\\Query::filterWhere()|filterWhere()]] 方法来实现这个目的：\n\n```php\n// $username 和 $email 来自于用户的输入\n$query->filterWhere([\n    'username' => $username,\n    'email' => $email,\t\t\n]);\n```\n\n[[yii\\db\\Query::filterWhere()|filterWhere()]] 和 [[yii\\db\\Query::where()|where()]] 唯一的不同就在于，前者\n将忽略在条件当中的[hash format](#hash-format)的空值。所以如果 `$email` 为空而 `$username` \n不为空，那么上面的代码最终将生产如下 SQL `...WHERE username=:username`。 \n\n> Tip: 当一个值为 `null`、空数组、空字符串或者一个只包含空格的字符串时，那么它将被判定为空值。\n\n类似于 [[yii\\db\\Query::andWhere()|andWhere()]] 和 [[yii\\db\\Query::orWhere()|orWhere()]]，\n你可以使用 [[yii\\db\\Query::andFilterWhere()|andFilterWhere()]] 和 [[yii\\db\\Query::orFilterWhere()|orFilterWhere()]] 方法\n来追加额外的过滤条件。\n\n此外，[[yii\\db\\Query::andFilterCompare()]]\n可以根据值中的内容智能地确定运算符：\n\n```php\n$query->andFilterCompare('name', 'John Doe');\n$query->andFilterCompare('rating', '>9');\n$query->andFilterCompare('value', '<=100');\n```\n\n您还可以显式指定运算符：\n\n```php\n$query->andFilterCompare('name', 'Doe', 'like');\n```\n\nYii 自 2.0.11 版起 ，提供了 `HAVING` 条件的一些构建方法：\n\n- [[yii\\db\\Query::filterHaving()|filterHaving()]]\n- [[yii\\db\\Query::andFilterHaving()|andFilterHaving()]]\n- [[yii\\db\\Query::orFilterHaving()|orFilterHaving()]]\n\n### [[yii\\db\\Query::orderBy()|orderBy()]] <span id=\"order-by\"></span>\n\n[[yii\\db\\Query::orderBy()|orderBy()]] 方法是用来指定 SQL 语句当中的 `ORDER BY` 子句的。例如，\n\n```php\n// ... ORDER BY `id` ASC, `name` DESC\n$query->orderBy([\n    'id' => SORT_ASC,\n    'name' => SORT_DESC,\n]);\n```\n\n如上所示，数组当中的键指代的是字段名称，而数组当中的值则表示的是排序的方式。\nPHP 的常量 `SORT_ASC` 指的是升序排列，`SORT_DESC` 指的则是降序排列。\n\n如果 `ORDER BY` 仅仅包含简单的字段名称，你可以使用字符串来声明它，\n就像写原生的 SQL 语句一样。例如，\n\n```php\n$query->orderBy('id ASC, name DESC');\n```\n\n> Note: 当 `ORDER BY` 语句包含一些 DB 表达式的时候，你应该使用数组的格式。\n\n你可以调用 [[yii\\db\\Query::addOrderBy()|addOrderBy()]] 来为 `ORDER BY` 片断添加额外的子句。\n例如，\n\n```php\n$query->orderBy('id ASC')\n    ->addOrderBy('name DESC');\n```\n\n\n### [[yii\\db\\Query::groupBy()|groupBy()]] <span id=\"group-by\"></span>\n\n[[yii\\db\\Query::groupBy()|groupBy()]] 方法是用来指定 SQL 语句当中的 `GROUP BY` 片断的。例如，\n\n```php\n// ... GROUP BY `id`, `status`\n$query->groupBy(['id', 'status']);\n```\n\n如果 `GROUP BY` 仅仅包含简单的字段名称，你可以使用字符串来声明它，\n就像写原生的 SQL 语句一样。例如，\n\n```php\n$query->groupBy('id, status');\n```\n\n> Note: 当 `GROUP BY` 语句包含一些 DB 表达式的时候，你应该使用数组的格式。\n\n你可以调用 [[yii\\db\\Query::addOrderBy()|addOrderBy()]] 来为 `GROUP BY` \n子句添加额外的字段。例如，\n\n```php\n$query->groupBy(['id', 'status'])\n    ->addGroupBy('age');\n```\n\n\n### [[yii\\db\\Query::having()|having()]] <span id=\"having\"></span>\n\n[[yii\\db\\Query::having()|having()]] 方法是用来指定 SQL 语句当中的 `HAVING` 子句。它带有一个条件，\n和 [where()](#where) 中指定条件的方法一样。例如，\n\n```php\n// ... HAVING `status` = 1\n$query->having(['status' => 1]);\n```\n\n请查阅 [where()](#where) 的文档来获取更多有关于如何指定一个条件的细节。\n\n你可以调用 [[yii\\db\\Query::andHaving()|andHaving()]] 或者 [[yii\\db\\Query::orHaving()|orHaving()]] \n方法来为 `HAVING` 子句追加额外的条件，例如，\n\n```php\n// ... HAVING (`status` = 1) AND (`age` > 30)\n$query->having(['status' => 1])\n    ->andHaving(['>', 'age', 30]);\n```\n\n\n### [[yii\\db\\Query::limit()|limit()]] 和 [[yii\\db\\Query::offset()|offset()]] <span id=\"limit-offset\"></span>\n\n[[yii\\db\\Query::limit()|limit()]] 和 [[yii\\db\\Query::offset()|offset()]] 是用来指定 SQL 语句当中\n的 `LIMIT` 和 `OFFSET` 子句的。例如，\n \n```php\n// ... LIMIT 10 OFFSET 20\n$query->limit(10)->offset(20);\n```\n\n如果你指定了一个无效的 limit 或者 offset（例如，一个负数），那么它将会被忽略掉。\n\n> Tip: 在不支持 `LIMIT` 和 `OFFSET` 的 DBMS 中（例如，MSSQL），\n  查询构建器将生成一条模拟 `LIMIT`/`OFFSET` 行为的 SQL 语句。\n\n\n### [[yii\\db\\Query::join()|join()]] <span id=\"join\"></span>\n\n[[yii\\db\\Query::join()|join()]] 是用来指定 SQL 语句当中的 `JOIN` 子句的。例如，\n\n```php\n// ... LEFT JOIN `post` ON `post`.`user_id` = `user`.`id`\n$query->join('LEFT JOIN', 'post', 'post.user_id = user.id');\n```\n\n[[yii\\db\\Query::join()|join()]] 带有四个参数：\n\n- `$type`：连接类型，例如，`'INNER JOIN'`，`'LEFT JOIN'`。\n- `$table`：将要连接的表名称。\n- `$on`：可选的，连接条件，即 `ON` 片段。有关指定条件的详细信息，请参阅 [where()](#where)。\n   请注意，数组语法 **不能** 用于指定基于列的条件，\n   例如，`['user.id' => 'comment.userId']` 将导致用户 id 必须等于字符串\n   `'comment.userId'` 的情况。您应该使用字符串语法，并将条件指定为\n   `'user.id = comment.userId'`。\n- `$params`：可选参数，与连接条件绑定的参数。\n\n你可以分别调用如下的快捷方法来指定 `INNER JOIN`, `LEFT JOIN` 和 `RIGHT JOIN`。\n\n- [[yii\\db\\Query::innerJoin()|innerJoin()]]\n- [[yii\\db\\Query::leftJoin()|leftJoin()]]\n- [[yii\\db\\Query::rightJoin()|rightJoin()]]\n\n例如，\n\n```php\n$query->leftJoin('post', 'post.user_id = user.id');\n```\n\n可以通过多次调用如上所述的连接方法来连接多张表，每连接一张表调用一次。\n\n除了连接表以外，你还可以连接子查询。方法如下，将需要被连接的子查询指定\n为一个 [[yii\\db\\Query]] 对象，例如，\n\n```php\n$subQuery = (new \\yii\\db\\Query())->from('post');\n$query->leftJoin(['u' => $subQuery], 'u.id = author_id');\n```\n\n在这个例子当中，你应该将子查询放到一个数组当中，而数组当中的键，则为这个子查询的别名。\n\n\n### [[yii\\db\\Query::union()|union()]] <span id=\"union\"></span>\n\n[[yii\\db\\Query::union()|union()]] 方法是用来指定 SQL 语句当中的 `UNION` 子句的。例如，\n\n```php\n$query1 = (new \\yii\\db\\Query())\n    ->select(\"id, category_id AS type, name\")\n    ->from('post')\n    ->limit(10);\n\n$query2 = (new \\yii\\db\\Query())\n    ->select('id, type, name')\n    ->from('user')\n    ->limit(10);\n\n$query1->union($query2);\n```\n\n你可以通过多次调用 [[yii\\db\\Query::union()|union()]] 方法来追加更多的 `UNION` 子句。\n\n\n## 查询方法 <span id=\"query-methods\"></span>\n\n[[yii\\db\\Query]] 提供了一整套的用于不同查询目的的方法。\n\n- [[yii\\db\\Query::all()|all()]]：将返回一个由行组成的数组，每一行是一个由名称和值构成的关联数组（译者注：省略键的数组称为索引数组）。\n- [[yii\\db\\Query::one()|one()]]：返回结果集的第一行。\n- [[yii\\db\\Query::column()|column()]]：返回结果集的第一列。\n- [[yii\\db\\Query::scalar()|scalar()]]：返回结果集的第一行第一列的标量值。\n- [[yii\\db\\Query::exists()|exists()]]：返回一个表示该查询是否包结果集的值。\n- [[yii\\db\\Query::count()|count()]]：返回 `COUNT` 查询的结果。\n- 其它集合查询方法：包括 [[yii\\db\\Query::sum()|sum($q)]], [[yii\\db\\Query::average()|average($q)]],\n  [[yii\\db\\Query::max()|max($q)]], [[yii\\db\\Query::min()|min($q)]] 等。`$q` 是一个必选参数，\n  既可以是一个字段名称，又可以是一个 DB 表达式。\n\n例如，\n\n```php\n// SELECT `id`, `email` FROM `user`\n$rows = (new \\yii\\db\\Query())\n    ->select(['id', 'email'])\n    ->from('user')\n    ->all();\n    \n// SELECT * FROM `user` WHERE `username` LIKE `%test%`\n$row = (new \\yii\\db\\Query())\n    ->from('user')\n    ->where(['like', 'username', 'test'])\n    ->one();\n```\n\n> Note: [[yii\\db\\Query::one()|one()]] 方法只返回查询结果当中的第一条数据，\n  条件语句中不会加上 `LIMIT 1` 条件。如果你清楚的知道查询将会只返回一行或几行数据\n  （例如， 如果你是通过某些主键来查询的），这很好也提倡这样做。但是，如果查询结果\n  有机会返回大量的数据时，那么你应该显示调用 `limit(1)` 方法，以改善性能。\n  例如，`(new \\yii\\db\\Query())->from('user')->limit(1)->one()`。\n\n所有的这些查询方法都有一个可选的参数 `$db`, 该参数指代的是 [[yii\\db\\Connection|DB connection]]，\n执行一个 DB 查询时会用到。如果你省略了这个参数，那么 `db` [application component](structure-application-components.md) 将会被用作\n默认的 DB 连接。 如下是另外一个使用 `count()` 查询的例子：\n\n```php\n// 执行 SQL: SELECT COUNT(*) FROM `user` WHERE `last_name`=:last_name\n$count = (new \\yii\\db\\Query())\n    ->from('user')\n    ->where(['last_name' => 'Smith'])\n    ->count();\n```\n\n当你调用 [[yii\\db\\Query]] 当中的一个查询方法的时候，实际上内在的运作机制如下： \n\n* 在当前 [[yii\\db\\Query]] 的构造基础之上，调用 [[yii\\db\\QueryBuilder]] 来生成一条 SQL 语句；\n* 利用生成的 SQL 语句创建一个 [[yii\\db\\Command]] 对象； \n* 调用 [[yii\\db\\Command]] 的查询方法（例如，`queryAll()`）来执行这条 SQL 语句，并检索数据。\n\n有时候，你也许想要测试或者使用一个由 [[yii\\db\\Query]] 对象创建的 SQL 语句。\n你可以使用以下的代码来达到目的：\n\n```php\n$command = (new \\yii\\db\\Query())\n    ->select(['id', 'email'])\n    ->from('user')\n    ->where(['last_name' => 'Smith'])\n    ->limit(10)\n    ->createCommand();\n    \n// 打印 SQL 语句\necho $command->sql;\n// 打印被绑定的参数\nprint_r($command->params);\n\n// 返回查询结果的所有行\n$rows = $command->queryAll();\n```\n\n\n### 索引查询结果 <span id=\"indexing-query-results\"></span>\n\n当你在调用 [[yii\\db\\Query::all()|all()]] 方法时，它将返回一个以连续的整型数值为索引的数组。\n而有时候你可能希望使用一个特定的字段或者表达式的值来作为索引结果集数组。那么你可以在调用 [[yii\\db\\Query::all()|all()]] \n之前使用 [[yii\\db\\Query::indexBy()|indexBy()]] 方法来达到这个目的。\n例如，\n\n```php\n// 返回 [100 => ['id' => 100, 'username' => '...', ...], 101 => [...], 103 => [...], ...]\n$query = (new \\yii\\db\\Query())\n    ->from('user')\n    ->limit(10)\n    ->indexBy('id')\n    ->all();\n```\n\n如需使用表达式的值做为索引，那么只需要传递一个匿名函数给 [[yii\\db\\Query::indexBy()|indexBy()]] 方法即可：\n\n```php\n$query = (new \\yii\\db\\Query())\n    ->from('user')\n    ->indexBy(function ($row) {\n        return $row['id'] . $row['username'];\n    })->all();\n```\n\n该匿名函数将带有一个包含了当前行的数据的 `$row` 参数，并且返回用作当前行索引的\n标量值（译者注：就是简单的数值或者字符串，而不是其他复杂结构，例如数组）。\n\n> Note: 与 [[yii\\db\\Query::groupBy()|groupBy()]] 或者 [[yii\\db\\Query::orderBy()|orderBy()]] 等查询方法不同，\n> 他们将转换为 SQL 查询语句的一部分，而这个方法（indexBy）在从数据库取回数据后才生效执行的。\n> 这意味着只能使用那些在你的 SELECT 查询中的列名。\n> 此外，你用表名连接取列名的时候，比如 `customer.id`，结果中将只包含 `id` 列，因此你必须调用\n> `->indexBy('id')` 不要带表名前缀。\n\n\n### 批处理查询 <span id=\"batch-query\"></span>\n\n当需要处理大数据的时候，像 [[yii\\db\\Query::all()]] 这样的方法就不太合适了，\n因为它们会把所有查询的数据都读取到客户端内存上。为了解决这个问题，\nYii 提供了批处理查询的支持。服务端先保存查询结果，然后客户端使用游标（cursor）\n每次迭代出固定的一批结果集回来。\n\n> Warning: MySQL 批处理查询的实现存在已知的局限性和变通方法。见下文。\n\n批处理查询的用法如下：\n\n```php\nuse yii\\db\\Query;\n\n$query = (new Query())\n    ->from('user')\n    ->orderBy('id');\n\nforeach ($query->batch() as $users) {\n    // $users 是一个包含100条或小于100条用户表数据的数组\n}\n\n// or to iterate the row one by one\nforeach ($query->each() as $user) {\n    // 数据从服务端中以 100 个为一组批量获取，\n    // 但是 $user 代表 user 表里的一行数据\n}\n```\n\n[[yii\\db\\Query::batch()]] 和 [[yii\\db\\Query::each()]] 方法将会返回一个实现了`Iterator` \n接口 [[yii\\db\\BatchQueryResult]]  的对象，可以用在 `foreach` 结构当中使用。在第一次迭代取数据的时候，\n数据库会执行一次 SQL 查询，然后在剩下的迭代中，将直接从结果集中批量获取数据。默认情况下，\n一批的大小为 100，也就意味着一批获取的数据是 100 行。你可以通过给 `batch()` \n或者 `each()` 方法的第一个参数传值来改变每批行数的大小。\n\n相对于 [[yii\\db\\Query::all()]] 方法，批处理查询每次只读取 100 行的数据到内存。\n\n如果你通过 [[yii\\db\\Query::indexBy()]] 方法为查询结果指定了索引字段，\n那么批处理查询将仍然保持相对应的索引方案，\n例如，\n\n\n```php\n$query = (new \\yii\\db\\Query())\n    ->from('user')\n    ->indexBy('username');\n\nforeach ($query->batch() as $users) {\n    // $users 的 “username” 字段将会成为索引\n}\n\nforeach ($query->each() as $username => $user) {\n    // ...\n}\n```\n\n#### MySQL中批量查询的局限性（Limitations of batch query in MySQL） <span id=\"batch-query-mysql\"></span>\n\nMySQL 是通过 PDO 驱动库实现批量查询的。默认情况下，MySQL 查询是 [`带缓存的`](https://www.php.net/manual/zh/mysqlinfo.concepts.buffering.php)，\n这违背了使用游标（cursor）获取数据的目的，\n因为它不阻止驱动程序将整个结果集加载到客户端的内存中。\n\n\n> Note: 当使用 `libmysqlclient` 时（PHP5 的标配），计算 PHP 的内存限制时，用于数据结果集的内存不会计算在内。\n  看上去批量查询是正确运行的，实际上整个数据集都被加载到了客户端的内存中，\n  而且这个使用量可能还会再增长。\n\n要禁用缓存并减少客户端内存的需求量，PDO 连接属性 `PDO::MYSQL_ATTR_USE_BUFFERED_QUERY` 必须设置为 `false`。\n这样，直到整个数据集被处理完毕前，通过此连接是无法创建其他查询的。\n这样的操作可能会阻碍 `ActiveRecord` 执行表结构查询。\n如果这不构成问题（表结构已被缓存过了），\n我们可以通过切换原本的连接到非缓存模式，然后在批量查询完成后再切换回来。\n\n\n```php\nYii::$app->db->pdo->setAttribute(\\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);\n\n// 执行批量查询\n\nYii::$app->db->pdo->setAttribute(\\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);\n```\n\n> Note: 对于 MyISAM，在执行批量查询的过程中，表可能将被锁，\n  将延迟或拒绝其他连接的写入操作。\n  当使用非缓存查询时，尽量缩短游标打开的时间。\n\n如果表结构没有被缓存，或在批量查询被处理过程中需要执行其他查询，\n你可以创建一个单独的非缓存链接到数据库：\n\n```php\n$unbufferedDb = new \\yii\\db\\Connection([\n    'dsn' => Yii::$app->db->dsn,\n    'username' => Yii::$app->db->username,\n    'password' => Yii::$app->db->password,\n    'charset' => Yii::$app->db->charset,\n]);\n$unbufferedDb->open();\n$unbufferedDb->pdo->setAttribute(\\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);\n```\n\n如果你除了 `PDO::MYSQL_ATTR_USE_BUFFERED_QUERY` 是 `false` 之外，\n要确保 `$unbufferedDb` 拥有和原来缓存 `$db` 完全一样的属性，\n请参阅[实现 `$db` 的深度拷贝](https://github.com/yiisoft/yii2/issues/8420#issuecomment-301423833)，\n手动方法将它设置为 false 即可。\n\n然后使用此连接正常创建查询，新连接用于运行批量查询，\n逐条或批量进行结果处理：\n\n```php\n// 获取 1000 为一组的批量数据\nforeach ($query->batch(1000, $unbufferedDb) as $users) {\n    // ...\n}\n\n\n// 每次从服务端批量获取1000个数据，但是逐个遍历进行处理\nforeach ($query->each(1000, $unbufferedDb) as $user) {\n    // ...\n}\n```\n\n当结果集已处理完毕不再需要连接时，可以关闭它：\n\n```php\n$unbufferedDb->close();\n```\n\n> Note: 非缓存查询在 PHP 端使用更少的缓存，但会增加 MySQL 服务器端的负载。\n  建议您使用生产实践设计自己的代码以获取额外的海量数据，[例如，将数字键分段，使用非缓存的查询遍历](https://github.com/yiisoft/yii2/issues/8420#issuecomment-296109257)。\n\n\n### 添加自定义查询条件和表达式（Adding custom Conditions and Expressions） <span id=\"adding-custom-conditions-and-expressions\"></span>\n\n我们在 [查询条件-对象格式](#object-format) 章节中提到过，可以创建自定义的查询条件类。\n举个栗子，我们需要创建一个查询条件，它可以检查某些字段小于特定值的情况。\n当使用操作符格式时，代码如下：\n\n```php\n[\n    'and',\n    '>', 'posts', $minLimit,\n    '>', 'comments', $minLimit,\n    '>', 'reactions', $minLimit,\n    '>', 'subscriptions', $minLimit\n]\n```\n\n当这样的查询条件仅被应用一次，没什么问题。当它在一个查询语句中被多次使用时，就有很多优化点了。\n我们创建一个自定义查询条件对象来证实它。\n\nYii 有 [[yii\\db\\conditions\\ConditionInterface|ConditionInterface]] 接口类，必须用它来标识这是一个表示查询条件的类。\n它需要实现 `fromArrayDefinition()` 方法，用来从数组格式创建查询条件。\n如果我们不需要它，抛出一个异常来完成此方法即可。\n\n创建自定义查询条件类，我们就可以构建最适合当前需求的 API。\n\n```php\nnamespace app\\db\\conditions;\n\nclass AllGreaterCondition implements \\yii\\db\\conditions\\ConditionInterface\n{\n    private $columns;\n    private $value;\n\n    /**\n     * @param string[] $columns 要大于 $value 的字段名数组\n     * @param mixed $value 每个 $column 要比较的数值\n     */\n    public function __construct(array $columns, $value)\n    {\n        $this->columns = $columns;\n        $this->value = $value;\n    }\n    \n    public static function fromArrayDefinition($operator, $operands)\n    {\n        throw new InvalidArgumentException('Not implemented yet, but we will do it later');\n    }\n    \n    public function getColumns() { return $this->columns; }\n    public function getValue() { return $this->vaule; }\n}\n```\n\n我们现在创建了一个查询条件对象：\n\n```php\n$conditon = new AllGreaterCondition(['col1', 'col2'], 42);\n```\n\n但是 `QueryBuilder` 还不知道怎样从此对象生成 SQL 查询条件。\n因此我们还需要为这个条件对象创建一个构建器（Builder）。\n这个构建器必须实现 [[yii\\db\\ExpressionBuilderInterface]] 接口和 `build()` 方法。\n\n```php\nnamespace app\\db\\conditions;\n\nclass AllGreaterConditionBuilder implements \\yii\\db\\ExpressionBuilderInterface\n{\n    use \\yii\\db\\ExpressionBuilderTrait; // Contains constructor and `queryBuilder` property.\n\n    /**\n     * @param ExpressionInterface $condition 要构建的查询条件对象\n     * @param array $params 绑定的参数\n     * @return AllGreaterCondition\n     */ \n    public function build(ExpressionInterface $expression, array &$params = [])\n    {\n        $value = $condition->getValue();\n        \n        $conditions = [];\n        foreach ($expression->getColumns() as $column) {\n            $conditions[] = new SimpleCondition($column, '>', $value);\n        }\n\n        return $this->queryBuilder->buildCondition(new AndCondition($conditions), $params);\n    }\n}\n```\n\n接下来，让 [[yii\\db\\QueryBuilder|QueryBuilder]] ]知道我们的新查询条件对象 – 添加一个映射到\n`expressionBuilders` 数组中，在应用配置（application config）中完成即可：\n\n```php\n'db' => [\n    'class' => 'yii\\db\\mysql\\Connection',\n    // ...\n    'queryBuilder' => [\n        'expressionBuilders' => [\n            'app\\db\\conditions\\AllGreaterCondition' => 'app\\db\\conditions\\AllGreaterConditionBuilder',\n        ],\n    ],\n],\n```\n\n现在我们可以在 `where()` 中使用此查询条件对象了：\n\n```php\n$query->andWhere(new AllGreaterCondition(['posts', 'comments', 'reactions', 'subscriptions'], $minValue));\n```\n\n如果我们想要自定义操作符查询条件，可以在 [[yii\\db\\QueryBuilder::conditionClasses|QueryBuilder::conditionClasses]] 中\n这样声明：\n\n```php\n'db' => [\n    'class' => 'yii\\db\\mysql\\Connection',\n    // ...\n    'queryBuilder' => [\n        'expressionBuilders' => [\n            'app\\db\\conditions\\AllGreaterCondition' => 'app\\db\\conditions\\AllGreaterConditionBuilder',\n        ],\n        'conditionClasses' => [\n            'ALL>' => 'app\\db\\conditions\\AllGreaterCondition',\n        ],\n    ],\n],\n```\n\n并在 `app\\db\\conditions\\AllGreaterCondition` 对象中实现 `AllGreaterCondition::fromArrayDefinition()`方法：\n\n\n```php\nnamespace app\\db\\conditions;\n\nclass AllGreaterCondition implements \\yii\\db\\conditions\\ConditionInterface\n{\n    // ... 这里省略其他方法的实现\n     \n    public static function fromArrayDefinition($operator, $operands)\n    {\n        return new static($operands[0], $operands[1]);\n    }\n}\n```\n    \n然后呢，我们就可以使用更简短的操作符格式来创建自定义查询条件了：\n\n```php\n$query->andWhere(['ALL>', ['posts', 'comments', 'reactions', 'subscriptions'], $minValue]);\n```\n\n你可能注意到了，这里使用到了两个概念：表达式对象和条件对象。表达式对象实现了 [[yii\\db\\ExpressionInterface]] 接口，\n它还依赖于一个表达式构建器（Expression Builder）来执行构建逻辑，而表达式构建器实现了 [[yii\\db\\ExpressionBuilderInterface]] 接口。\n而条件对象实现了 [[yii\\db\\condition\\ConditionInterface]] 接口，它是继承了 [[yii\\db\\ExpressionInterface|ExpressionInterface]] 接口，\n如上面的栗子所写的，它用于数组定义的条件的场景，当然条件对象也需要构建器。\n\n\n总结起来就是:\n\n- Expression – 表达式对象，是数据集的数据转换对象（DTO），它可以被编译为一些特定 SQL 语句 （操作符、字符串、数组、JSON等等）。\n\n- Condition – 条件对象，是表达式对象超集，它可以聚合多个表达式对象（或标量值），然后编译成一条 SQL 查询条件。\n\n\n你可以创建自己的类来实现 [[yii\\db\\ExpressionInterface|ExpressionInterface]] 接口，达到封装的目的：隐藏复杂的 SQL 语句拼装过程。\n想学习到更多关于表达式对象的实践\n请听 [下回分解](db-active-record.md)；\n"
  },
  {
    "path": "docs/guide-zh-CN/documentation_style_guide.md",
    "content": "# Yii 文档风格指南\n\n编写或编辑任何 Yii 文档时的准则。\n\n*此文还需要扩展。*\n\n## 总体风格\n\n* 尽量使用主动语态。\n* 使用简短的陈述性句子。\n* 尽可能使用代码展示想法。\n* 请勿使用“我们”。这指的是 Yii 开发团队或 Yii 核心团队。更好的是从框架或指南的角度来考虑。\n* 使用牛津逗号（例如，“这个，那个，另一个”不是“这个，那个和另一个”）。\n\n## 格式\n\n* 使用 *斜体* 强调，不要大写，粗体或下划线。\n\n## 列表\n\n* 数字列表应该是以句号结尾的完整句子。\n* 项目符号列表应该是以分号结尾的片段，除了最后一个项目，它应该以句点结尾。\n\n## 块\n\n块使用 Markdown 语法 `> Type: `。有四种块类型：\n\n* `Warning`, 对于不安全的事情和其他问题\n* `Note`, 强调关键概念，需要避免的事情\n* `Info`, 一般资料（旁白）; 不如“注意”表达强烈\n* `Tip`, 专业提示，附加提示，可能是有用的，但可能并非每个人都需要\n\n冒号后的句子应以大写字母开头。\n\n在翻译文档时，不应翻译这些 Block 标示符。\n将它们保持原样并仅翻译块内容。\n为了翻译 `Type` 这些词，每个指南翻译应该有一个 `blocktypes.json` 文件\n并包含翻译。以下显示了德语的一个示例：\n\n```json\n{\n    \"Warning:\": \"Achtung:\",\n    \"Note:\": \"Hinweis:\",\n    \"Info:\": \"Info:\",\n    \"Tip:\": \"Tipp:\"\n}\n```\n\n## 参考\n\n* Yii 2.0 或 Yii 2（不能是 Yii2 或 Yii2.0）。\n* 指南的每个“页面”都被称为“章节”。\n* 代码对象的引用：\n  - 请参阅使用完整的命名空间类：`yii\\base\\Model`\n  - 即使它们不是静态的，也可以使用静态语法引用类属性：`yii\\base\\Model::$validators`\n  - 即使它们不是静态的，也可以使用静态语法来引用类方法，并且使用括号来表示它是一种方法：`yii\\base\\Model::validate()`\n  - 对代码对象的引用应该写在 `[[]]` 中以生成指向API文档的链接。 例如，`[[yii\\base\\Model]]`，`[[yii\\base\\Model::$validators]]`，或 `[[yii\\base\\Model::validate()]]`。\n\n## 字母大小写\n\n* Web，不是 web\n* guide，不是 Guide\n\n## 验证文档\n\n以下脚本可帮助查找指南中的链接和其他问题：\n\n查找无效的链接（可能会出现一些误报）：\n\n    grep -rniP \"\\[\\[[^\\],']+?\\][^\\]]\"  docs/guide*\n    grep -rniP \"[^\\[]\\[[^\\]\\[,']+?\\]\\]\"  docs/guide*\n    \n## 译者的归属\n\n翻译人员的姓名将在翻译版本的\n指南的作者中列出。\n因此，在每个指南目录中使用与英语不同的语言时，应创建一个“translators.json”文件，\n其中包含参与翻译的人员的名称数组。\n\n```json\n[\n  \"Jane Doe\",\n  \"John Doe\"\n]\n```\n\n如果您对翻译有重要贡献，请随时发送 pull request 并添加您的名字。"
  },
  {
    "path": "docs/guide-zh-CN/glossary.md",
    "content": "\n\n## 排版规范\n\n> Info: 信息\n\n> Note: 注意\n\n> Tip: 提示\n\n> Warning: 警告\n\n\n## 翻译对照列表\n\n### A\n\n- Action 动作\n- Active Record 活动记录\n- Aliase 别名\n- Array 数组\n- Argument 参数\n- Assets 前端资源\n- Authorization 用户认证\n\n\n### B\n\n- Behavior 行为\n\n### C\n\n- Cache 缓存\n- Callback 回调\n- Classes 类\n- CLI 命令行界面\n- Command 命令\n- Command Line 命令行\n- Component 组件\n- Console 终端\n- Controller 控制器\n- Controller Action 控制器动作\n- constructor 构造函数\n- Cookie (不翻)\n- Composer (开源项目名称，不翻)\n- Closure 闭包\n- Configuration 配置信息\n\n### D\n\n- Database-Transactions  数据库事务\n- Dependency 依赖\n- Driver 驱动\n- Dependency Injection 依赖注入\n\n### E\n\n- Event 事件\n- Extend 拓展／继承\n- Extension 扩展\n- Exception 异常\n\n### F\n\n- Framework 框架\n- Filter 过滤器\n- Form 表单\n- Function 函数\n\n### G\n\n- Guide 指南\n\n### H\n\n- Helper 辅助函数（类）\n- Hash 哈希 (可不翻)\n- Header 标头\n\n### I\n\n- Instance 实例\n- inheritance 继承\n- implements 实现\n\n\n### K\n\n- Key 键\n\n### M\n\n- Method 方法\n- Migration 迁移\n- Model 模型\n- Module 模块\n\n### N\n\n- Namespace 命名空间\n\n### O\n\n- Object 对象\n\n### P\n\n- Package 扩展包\n- Packagist (开源项目名称，不翻)\n- Prefix 前缀\n- Propertie 属性\n\n### Q\n\n- Queue 队列\n- Query Builder 查询生成器\n\n### R\n\n- Routing 引导路由\n- Request 请求\n- Response 响应\n- Resolved 解析\n- Redirect 重定向\n- (Database's) Rollback 还原\n\n### S\n\n- Schema 数据库结构\n- Service 服务\n- Service Locator 服务定位器\n- Session 会话 (可不翻)\n\n### T\n\n- Tag 标签\n- Table 数据表\n- Templates 模板\n- Terminal 终端\n- Token 令牌\n- Timestamps 时间戳\n- Trait (不翻)\n- Ternary statement 三元运算符\n- Throw (Exception) 拋出（异常）\n\n### V\n\n- Vagrant (开源项目名称，不翻)\n- Vagrant Box (开源项目名称，不翻)\n- View 视图\n- Vendor 供应商 \n- \n\n### W\n\n- Widget 小部件\n"
  },
  {
    "path": "docs/guide-zh-CN/helper-array.md",
    "content": "数组助手类（ArrayHelper）\n======================\n\n除了 [PHP 中丰富的数组函数集](https://www.php.net/manual/zh/book.array.php)，\nYii 数组助手类提供了额外的静态方法，让你更高效地处理数组。\n\n\n## 获取值（Getting Values） <span id=\"getting-values\"></span>\n\n用原生PHP从一个对象、数组、或者包含这两者的一个复杂数据结构中获取数据是非常繁琐的。\n你首先得使用 `isset` 检查 key 是否存在, 然后如果存在你就获取它，如果不存在，\n则提供一个默认返回值：\n\n```php\nclass User\n{\n    public $name = 'Alex';\n}\n\n$array = [\n    'foo' => [\n        'bar' => new User(),\n    ]\n];\n\n$value = isset($array['foo']['bar']->name) ? $array['foo']['bar']->name : null;\n```\n\nYii 提供了一个非常方便的方法来做这件事：\n\n```php\n$value = ArrayHelper::getValue($array, 'foo.bar.name');\n```\n\n方法的第一个参数是我们从哪里获取值。第二个参数指定了如何获取数据，\n它可以是下述几种类型中的一个：\n\n- 数组键名或者欲从中取值的对象的属性名称；\n- 以点号分割的数组键名或者对象属性名称组成的字符串，上例中使用的参数类型就是该类型；\n- 返回一个值的回调函数。\n\n回调函数如下例所示：\n\n```php\n$fullName = ArrayHelper::getValue($user, function ($user, $defaultValue) {\n    return $user->firstName . ' ' . $user->lastName;\n});\n```\n\n第三个可选的参数如果没有给定值，则默认为 `null`，如下例所示：\n\n```php\n$username = ArrayHelper::getValue($comment, 'user.username', 'Unknown');\n```\n\n\n## 设定值（Setting values） <span id=\"setting-values\"></span>\n\n```php\n$array = [\n    'key' => [\n        'in' => ['k' => 'value']\n    ]\n];\n\nArrayHelper::setValue($array, 'key.in', ['arr' => 'val']);\n// 在 `$array` 中写入值的路径可以被指定为一个数组\nArrayHelper::setValue($array, ['key', 'in'], ['arr' => 'val']);\n```\n\n结果，`$array['key']['in']` 的初始值将被新值覆盖\n\n```php\n[\n    'key' => [\n        'in' => ['arr' => 'val']\n    ]\n]\n```\n\n如果路径包含一个不存在的键，它将被创建\n\n```php\n// 如果 `$array['key']['in']['arr0']` 不为空，则该值将被添加到数组中\nArrayHelper::setValue($array, 'key.in.arr0.arr1', 'val');\n\n// 如果你想完全覆盖值 `$array['key']['in']['arr0']`\nArrayHelper::setValue($array, 'key.in.arr0', ['arr1' => 'val']);\n```\n\n结果将是\n\n```php\n[\n    'key' => [\n        'in' => [\n            'k' => 'value',\n            'arr0' => ['arr1' => 'val']\n        ]\n    ]\n]\n```\n\n\n## 从数组中获取值（Take a value from an array） <span id=\"removing-values\"></span>\n\n如果你想获得一个值，然后立即从数组中删除它，你可以使用 `remove` 方法：\n\n```php\n$array = ['type' => 'A', 'options' => [1, 2]];\n$type = ArrayHelper::remove($array, 'type');\n```\n\n执行代码后，`$array` 将包含 `['options' => [1, 2]]` 且 `$type` 将包含 `A`。\n请注意，与 `getValue` 方法不同，`remove` 仅支持简单的键名称。\n\n\n## 检查键名的存在（Checking Existence of Keys） <span id=\"checking-existence-of-keys\"></span>\n\n`ArrayHelper::keyExists` 工作原理和 [array_key_exists](https://www.php.net/manual/zh/function.array-key-exists.php) 差不多，除了\n它还可支持大小写不敏感的键名比较，比如：\n\n```php\n$data1 = [\n    'userName' => 'Alex',\n];\n\n$data2 = [\n    'username' => 'Carsten',\n];\n\nif (!ArrayHelper::keyExists('username', $data1, false) || !ArrayHelper::keyExists('username', $data2, false)) {\n    echo \"Please provide username.\";\n}\n```\n\n## 检索列（Retrieving Columns） <span id=\"retrieving-columns\"></span>\n\n通常你要从多行数据或者多个对象构成的数组中获取某列的值，一个普通的例子是获取 id 值列表。\n\n```php\n$data = [\n    ['id' => '123', 'data' => 'abc'],\n    ['id' => '345', 'data' => 'def'],\n];\n$ids = ArrayHelper::getColumn($array, 'id');\n```\n\n结果将是 `['123', '345']`。\n\n如果需要额外的转换或者取值的方法比较复杂，\n第二参数可以指定一个匿名函数：\n\n```php\n$result = ArrayHelper::getColumn($array, function ($element) {\n    return $element['id'];\n});\n```\n\n\n## 重建数组索引（Re-indexing Arrays） <span id=\"reindexing-arrays\"></span>\n\n按一个指定的键名重新索引一个数组，可以用 `index` 方法。输入的数组应该是多维数组或者是一个对象数组。\n键名（译者注：第二个参数）可以是子数组的键名、对象的属性名，\n也可以是一个返回给定元素数组键值的匿名函数。\n\n`$groups` 属性是一个键数组，\n它将根据指定的键将输入数组分组为一个或多个子数组。\n\n如果 `$key` 属性或其特定元素的值为 null，并且未定义 `$groups`，\n则数组元素将被丢弃。否则，如果指定了 `$groups`，\n则数组元素将被添加到没有任何键的结果数组中。\n\n例如：\n\n```php\n$array = [\n    ['id' => '123', 'data' => 'abc', 'device' => 'laptop'],\n    ['id' => '345', 'data' => 'def', 'device' => 'tablet'],\n    ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone'],\n];\n$result = ArrayHelper::index($array, 'id');\n```\n\n结果将是一个关联数组，其中键是 `id` 属性的值\n\n```php\n[\n    '123' => ['id' => '123', 'data' => 'abc', 'device' => 'laptop'],\n    '345' => ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone']\n    // 原始数组的第二个元素由于相同的 ID 而被最后一个元素覆盖\n]\n```\n\n匿名函数作为 `$key` 传递，给出了相同的结果。\n\n```php\n$result = ArrayHelper::index($array, function ($element) {\n    return $element['id'];\n});\n```\n\n传递 `id` 作为第三个参数将 `id` 分配给 `$ array`：\n\n```php\n$result = ArrayHelper::index($array, null, 'id');\n```\n\n结果将是一个多维数组，它由第一级的 `id` 分组，并且不在第二级索引：\n\n```php\n[\n    '123' => [\n        ['id' => '123', 'data' => 'abc', 'device' => 'laptop']\n    ],\n    '345' => [ // all elements with this index are present in the result array\n        ['id' => '345', 'data' => 'def', 'device' => 'tablet'],\n        ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone'],\n    ]\n]\n```\n\n匿名函数也可用于分组数组中：\n\n```php\n$result = ArrayHelper::index($array, 'data', [function ($element) {\n    return $element['id'];\n}, 'device']);\n```\n\n结果将是一个多维数组，由第一级的 `id` 分组，第二级的 `device` 和第三级的\n`data` 索引：\n\n```php\n[\n    '123' => [\n        'laptop' => [\n            'abc' => ['id' => '123', 'data' => 'abc', 'device' => 'laptop']\n        ]\n    ],\n    '345' => [\n        'tablet' => [\n            'def' => ['id' => '345', 'data' => 'def', 'device' => 'tablet']\n        ],\n        'smartphone' => [\n            'hgi' => ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone']\n        ]\n    ]\n]\n```\n\n## 建立哈希表（Building Maps） <span id=\"building-maps\"></span>\n\n为了从一个多维数组或者一个对象数组中建立一个映射表（键值对），你可以使用\n`map` 方法。`$from` 和 `$to` 参数分别指定了欲构建的映射表的键名和属性名。\n根据需要，你可以按照一个分组字段 `$group` 将映射表进行分组，例如，\n\n```php\n$array = [\n    ['id' => '123', 'name' => 'aaa', 'class' => 'x'],\n    ['id' => '124', 'name' => 'bbb', 'class' => 'x'],\n    ['id' => '345', 'name' => 'ccc', 'class' => 'y'],\n];\n\n$result = ArrayHelper::map($array, 'id', 'name');\n// 结果是： \n// [\n//     '123' => 'aaa',\n//     '124' => 'bbb',\n//     '345' => 'ccc',\n// ]\n\n$result = ArrayHelper::map($array, 'id', 'name', 'class');\n// 结果是：\n// [\n//     'x' => [\n//         '123' => 'aaa',\n//         '124' => 'bbb',\n//     ],\n//     'y' => [\n//         '345' => 'ccc',\n//     ],\n// ]\n```\n\n\n## 多维排序（Multidimensional Sorting） <span id=\"multidimensional-sorting\"></span>\n\n`multisort` 方法可用来对嵌套数组或者对象数组进行排序，可按一到多个键名排序，比如，\n\n```php\n$data = [\n    ['age' => 30, 'name' => 'Alexander'],\n    ['age' => 30, 'name' => 'Brian'],\n    ['age' => 19, 'name' => 'Barney'],\n];\nArrayHelper::multisort($data, ['age', 'name'], [SORT_ASC, SORT_DESC]);\n```\n\n排序之后我们在 `$data` 中得到的值如下所示：\n\n```php\n[\n    ['age' => 19, 'name' => 'Barney'],\n    ['age' => 30, 'name' => 'Brian'],\n    ['age' => 30, 'name' => 'Alexander'],\n];\n```\n\n第二个参数指定排序的键名，如果是单键名的话可以是字符串，如果是多键名则是一个数组，\n或者是如下例所示的一个匿名函数：\n\n```php\nArrayHelper::multisort($data, function($item) {\n    return isset($item['age']) ? ['age', 'name'] : 'name';\n});\n```\n\n第三个参数表示增降顺序。单键排序时，它可以是 `SORT_ASC` 或者 \n`SORT_DESC` 之一。如果是按多个键名排序，你可以用一个数组为\n各个键指定不同的顺序。\n\n最后一个参数（译者注：第四个参数）是PHP的排序标识（sort flag），可使用的值和调用 PHP\n[sort()](https://www.php.net/manual/zh/function.sort.php) 函数时传递的值一样。\n\n\n## 检测数组类型（Detecting Array Types） <span id=\"detecting-array-types\"></span> \n\n想知道一个数组是索引数组还是联合数组很方便，这有个例子：\n\n```php\n// 不指定键名的数组\n$indexed = ['Qiang', 'Paul'];\necho ArrayHelper::isIndexed($indexed);\n\n// 所有键名都是字符串\n$associative = ['framework' => 'Yii', 'version' => '2.0'];\necho ArrayHelper::isAssociative($associative);\n```\n\n\n## HTML 编码和解码值（HTML Encoding and Decoding Values） <span id=\"html-encoding-values\"></span>\n\n为了将字符串数组中的特殊字符做 HTML 编解码，你可以使用下列方法：\n\n```php\n$encoded = ArrayHelper::htmlEncode($data);\n$decoded = ArrayHelper::htmlDecode($data);\n```\n\n默认情况只会对值做编码（译者注：原文中是编码，应为编解码）。通过给第二个参数传 `false` ，你也可以对键名做编码。\n编码将默认使用应用程序的字符集，你可以通过第三个参数指定该字符集。\n\n\n## 合并数组（Merging Arrays） <span id=\"merging-arrays\"></span>\n\n您可以使用 [[yii\\helpers\\ArrayHelper::merge()|ArrayHelper::merge()]] 将两个或多个数组合并成一个递归的数组。\n如果每个数组都有一个具有相同字符串键值的元素，则后者将覆盖前者\n（不同于 [array_merge_recursive()](https://www.php.net/manual/zh/function.array-merge-recursive.phpp)）。\n如果两个数组都有一个数组类型的元素并且具有相同的键，则将执行递归合并。\n对于整数键的元素，来自后一个数组的元素将被附加到前一个数组。\n您可以使用 [[yii\\helpers\\UnsetArrayValue]] 对象来取消前一个数组的值或\n[[yii\\helpers\\ReplaceArrayValue]] 以强制替换先前的值而不是递归合并。\n\n例如：\n\n```php\n$array1 = [\n    'name' => 'Yii',\n    'version' => '1.1',\n    'ids' => [\n        1,\n    ],\n    'validDomains' => [\n        'example.com',\n        'www.example.com',\n    ],\n    'emails' => [\n        'admin' => 'admin@example.com',\n        'dev' => 'dev@example.com',\n    ],\n];\n\n$array2 = [\n    'version' => '2.0',\n    'ids' => [\n        2,\n    ],\n    'validDomains' => new \\yii\\helpers\\ReplaceArrayValue([\n        'yiiframework.com',\n        'www.yiiframework.com',\n    ]),\n    'emails' => [\n        'dev' => new \\yii\\helpers\\UnsetArrayValue(),\n    ],\n];\n\n$result = ArrayHelper::merge($array1, $array2);\n```\n\n结果将是：\n\n```php\n[\n    'name' => 'Yii',\n    'version' => '2.0',\n    'ids' => [\n        1,\n        2,\n    ],\n    'validDomains' => [\n        'yiiframework.com',\n        'www.yiiframework.com',\n    ],\n    'emails' => [\n        'admin' => 'admin@example.com',\n    ],\n]\n```\n\n\n## 对象转换为数组（Converting Objects to Arrays） <span id=\"converting-objects-to-arrays\"></span>\n\n你经常要将一个对象或者对象的数组转换成一个数组，常见的情形是，为了通过 REST API 提供数据数组（或其他使用方式），\n将 AR 模型（活动记录模型）转换成数组。如下代码可完成这个工作：\n\n```php\n$posts = Post::find()->limit(10)->all();\n$data = ArrayHelper::toArray($posts, [\n    'app\\models\\Post' => [\n        'id',\n        'title',\n        // the key name in array result => property name\n        'createTime' => 'created_at',\n        // the key name in array result => anonymous function\n        'length' => function ($post) {\n            return strlen($post->content);\n        },\n    ],\n]);\n```\n\n第一个参数包含我们想要转换的数据，在本例中，我们要转换一个叫 `Post` 的 AR 模型。\n\n第二个参数是每个类的转换映射表，我们在此设置了一个 `Post` 模型的映射。\n每个映射数组包含一组的映射，每个映射可以是：\n\n- 一个要包含的照原样的字段名（和类中属性的名称一致）；\n- 一个由你可随意取名的键名和你想从中取值的模型列名组成的键值对；\n- 一个由你可随意取名的键名和有返回值的回调函数组成的键值对；\n\n这上面的转换结果将会是：\n\n\n```php\n[\n    'id' => 123,\n    'title' => 'test',\n    'createTime' => '2013-01-01 12:00AM',\n    'length' => 301,\n]\n```\n\n也可以在一个特定的类中实现 [[yii\\base\\Arrayable|Arrayable]] 接口，\n从而为其对象提供默认的转换成数组的方法。\n\n## 测试阵列（Testing against Arrays） <span id=\"testing-arrays\"></span>\n\n通常你需要检查一个元素是否在数组中，或者一组元素是另一个元素的子集。\n虽然PHP提供 `in_array()`，这不支持子集或 `\\Traversable` 对象。\n\n为了支持这些测试，[[yii\\base\\ArrayHelper]] 提供了 [[yii\\base\\ArrayHelper::isIn()|isIn()]]\n和 [[yii\\base\\ArrayHelper::isSubset()|isSubset()]] 与 [[in_array()]] 签名相同。\n\n```php\n// true\nArrayHelper::isIn('a', ['a']);\n// true\nArrayHelper::isIn('a', new(ArrayObject['a']));\n\n// true \nArrayHelper::isSubset(new(ArrayObject['a', 'c']), new(ArrayObject['a', 'b', 'c'])\n\n```\n"
  },
  {
    "path": "docs/guide-zh-CN/helper-html.md",
    "content": "Html 帮助类（Html helper）\n=======================\n\n任何一个 web 应用程序会生成很多 HTMl 超文本标记。如果超文本标记是静态的，\n那么[将 PHP 和 HTML 混合在一个文件里](https://www.php.net/manual/zh/language.basic-syntax.phpmode.php)\n这种做法是非常高效的。但是，如果这些超文本标记是动态生成的，那么如果没有额外的辅助工具，这个过程将会变得复杂。\nYii 通过 HTML  帮助类来提供生成超文本标记的方法。这个帮助类包含有一系列的用于处理通用的 HTML 标签和其属性以及内容的静态方法。\n\n> Note: 如果你的超文本标记接近静态的，那么最好是直接使用 HTML。\n没有必要把所有的超文本标记都用 HTML 辅助类来生成。\n\n\n## 基础（Basics） <span id=\"basics\"></span>\n\n由于通过字符串连接来生成动态的 HTML 会很容易变得凌乱，\nYii 提供了一系列的静态方法来操作标签配置并基于这些配置来创建对应的标签。\n\n\n### 生成标签（Generating Tags） <span id=\"generating-tags\"></span>\n\n生成一个标签的代码类似如下：\n\n```php\n<?= Html::tag('p', Html::encode($user->name), ['class' => 'username']) ?>\n```\n\n这个方法的第一个参数是标签名称。第二个是要装入到开始和结束标签间的内容。\n注意到我们使用 `Html::encode` 。那是因为内容不会被自动的转码以允许在有需要的时候嵌套 HTML。\n第三个参数是一个 HTML 配置数组，或者换言之，标签属性。在这个数组中，数组的下标是属性名称，\n比如 `class`，`href` 或者 `target`，而值则是对应属性的值。\n\n以上代码会生成如下 HTML ：\n\n```html\n<p class=\"username\">samdark</p>\n```\n\n如果你只需要开启一个标签或者关闭一个标签，你可以使用 `Html::beginTag()` 和 `Html::endTag()` 方法。\n\n标签属性（ Options ）在 Html 帮助类很多方法和大量的小部件中都有使用。在这些情况下，\n有一些额外的处理我们需要知道：\n\n- 如果一个值为 null ，那么对应的属性将不会被渲染。\n- 如果是布尔类型的值的属性，将会被当做 \n  [布尔属性](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#boolean-attributes) 来处理。\n- 属性的值将会用 [[yii\\helpers\\Html::encode()|Html::encode()]] 方法进行 HTML 转码处理。\n- 如果一个属性的值是一个数组，那么它将会被如下处理：\n    \n    * 如果这个属性是一个如 [[yii\\helpers\\Html::$dataAttributes]] 所列的数据属性，\n      比如 `data` 或者 `ng`，一系列的属性列表将会被渲染，每个代表值数组中的元素。\n      比如： `'data' => ['id' => 1, 'name' => 'yii']` 将会生成  `data-id=\"1\" data-name=\"yii\"`；\n      `'data' => ['params' => ['id' => 1, 'name' => 'yii'], 'status' => 'ok']` 生成\n      `data-params='{\"id\":1,\"name\":\"yii\"}' data-status=\"ok\"`。\n      注意后者 中，一个子数组被输出为 JSON 。\n    * 如果这个属性不是一个数据属性，那么值将会被 JSON-encoded。比如：`['params' => ['id' => 1, 'name' => 'yii']` \n      生成 `params='{\"id\":1,\"name\":\"yii\"}'`。\n\n\n### 生成 CSS 类和样式（Forming CSS Classes and Styles） <span id=\"forming-css\"></span>\n\n当开始构造一个 HTML 标签的属性时，我们经常需要对默认的属性进行修改。\n为了添加或者删除 CSS 类，你可以使用如下代码：\n\n```php\n$options = ['class' => 'btn btn-default'];\n\nif ($type === 'success') {\n    Html::removeCssClass($options, 'btn-default');\n    Html::addCssClass($options, 'btn-success');\n}\n\necho Html::tag('div', 'Pwede na', $options);\n\n// in case of $type of 'success' it will render\n// <div class=\"btn btn-success\">Pwede na</div>\n```\n\n基于同样的目的，针对 `style` 属性：\n\n```php\n$options = ['class' => ['btn', 'btn-default']];\n\necho Html::tag('div', 'Save', $options);\n// renders '<div class=\"btn btn-default\">Save</div>'\n```\n\n在添加或删除类时，您也可以使用数组格式：\n\n```php\n$options = ['class' => 'btn'];\n\nif ($type === 'success') {\n    Html::addCssClass($options, ['btn-success', 'btn-lg']);\n}\n\necho Html::tag('div', 'Save', $options);\n// renders '<div class=\"btn btn-success btn-lg\">Save</div>'\n```\n\n`Html::addCssClass()` 防止重复类，所以你不必担心同一个类可能会出现两次：\n\n```php\n$options = ['class' => 'btn btn-default'];\n\nHtml::addCssClass($options, 'btn-default'); // class 'btn-default' is already present\n\necho Html::tag('div', 'Save', $options);\n// renders '<div class=\"btn btn-default\">Save</div>'\n```\n\n如果通过数组格式指定 CSS 类选项，则可以使用命名键来标记该类的逻辑用途。\n在这种情况下，在 `Html::addCssClass()` 类中会忽略数组格式中具有相同键：\n\n```php\n$options = [\n    'class' => [\n        'btn',\n        'theme' => 'btn-default',\n    ]\n];\n\nHtml::addCssClass($options, ['theme' => 'btn-success']); // 'theme' 键已被使用\n\necho Html::tag('div', 'Save', $options);\n// renders '<div class=\"btn btn-default\">Save</div>'\n```\n\n可以使用 `style` 属性以类似的方式设置 CSS 样式：\n\n```php\n$options = ['style' => ['width' => '100px', 'height' => '100px']];\n\n// gives style=\"width: 100px; height: 200px; position: absolute;\"\nHtml::addCssStyle($options, 'height: 200px; position: absolute;');\n\n// gives style=\"position: absolute;\"\nHtml::removeCssStyle($options, ['width', 'height']);\n```\n\n当使用 [[yii\\helpers\\Html::addCssStyle()|addCssStyle()]] 方法时，你可以指定一个和 CSS 属性相关的名值对的数组，\n也可以直接是一个类似 `width: 100px; height: 200px;` 的字符串。这些格式将会自动的被 \n[[yii\\helpers\\Html::cssStyleFromArray()|cssStyleFromArray()]] 和[[yii\\helpers\\Html::cssStyleToArray()|cssStyleToArray()]] \n方法进行转换。方法 [[yii\\helpers\\Html::removeCssStyle()|removeCssStyle()]] 接收一个包含要被移除的属性数组作为参数。\n如果只想移除一个属性，你可以直接传递一个字符串。\n\n\n### 标签内容的转码和解码（Encoding and Decoding Content） <span id=\"encoding-and-decoding-content\"></span>\n\n为了让内容能够正确安全的显示，一些 HTML 特殊字符应该被转码。在 PHP 中，\n这个操作由 [htmlspecialchars](https://www.php.net/manual/zh/function.htmlspecialchars.php) 和\n[htmlspecialchars_decode](https://www.php.net/manual/zh/function.htmlspecialchars-decode.php) 完成。\n直接使用这些方法的问题是，你总是需要指定转码所需的额外标志。由于标志一般总是不变的，而内容转码的过程为了避免一些安全问题，\n需要和应用的默认过程匹配，\nYii 提供了两个简单可用的对 PHP 原生方法的封装：\n\n```php\n$userName = Html::encode($user->name);\necho $userName;\n\n$decodedUserName = Html::decode($userName);\n```\n\n\n## 表单（Forms） <span id=\"forms\"></span>\n\n处理表单标签是大量的重复性劳动并且易错。因此，\nYii 也提供了一系列的方法来辅助处理表单标签。\n\n> Note: 考虑在处理 models 以及需要验证的情形下，使用 [[yii\\widgets\\ActiveForm|ActiveForm]] 组件。\n\n\n### 创建表单（Creating Forms） <span id=\"creating-forms\"></span>\n\n表单可以用类似如下代码，使用 [[yii\\helpers\\Html::beginForm()|beginForm()]] 方法开启：\n\n```php\n<?= Html::beginForm(['order/update', 'id' => $id], 'post', ['enctype' => 'multipart/form-data']) ?>\n```\n\n方法的第一个参数为表单将要被提交的 URL 地址。它可以以 Yii 路由的形式被指定，并由 [[yii\\helpers\\Url::to()|Url::to()]] 来接收处理。\n第二个参数是使用的方法，默认为 `post` 方法。第三个参数为表单标签的属性数组。在上面的例子中，\n我们把编码 POST 请求中的表单数据的方式改为 `multipart/form-data`。\n如果是上传文件，这个调整是必须的。\n\n关闭表单标签非常简单：\n\n```php\n<?= Html::endForm() ?>\n```\n\n\n### 按钮（Buttons） <span id=\"buttons\"></span>\n\n你可以用如下代码生成按钮：\n\n```php\n<?= Html::button('Press me!', ['class' => 'teaser']) ?>\n<?= Html::submitButton('Submit', ['class' => 'submit']) ?>\n<?= Html::resetButton('Reset', ['class' => 'reset']) ?>\n```\n\n上述三个方法的第一个参数为按钮的标题，第二个是标签属性。标题默认没有进行转码，如果标题是由终端用输入的，\n那么请自行用 [[yii\\helpers\\Html::encode()|Html::encode()]] 方法进行转码。\n\n\n### 输入栏（Input Fields） <span id=\"input-fields\"></span>\n\ninput 相关的方法有两组：以 `active` 开头的被称为 active inputs，\n另一组则不以其开头。active inputs 依据指定的模型和属性获取数据，\n而普通 input 则是直接指定数据。\n\n一般用法如下：\n\n```php\ntype, input name, input value, options\n<?= Html::input('text', 'username', $user->name, ['class' => $username]) ?>\n\ntype, model, model attribute name, options\n<?= Html::activeInput('text', $user, 'name', ['class' => $username]) ?>\n```\n\n如果你知道 input 类型，更方便的做法是使用以下快捷方法：\n\n- [[yii\\helpers\\Html::buttonInput()]]\n- [[yii\\helpers\\Html::submitInput()]]\n- [[yii\\helpers\\Html::resetInput()]]\n- [[yii\\helpers\\Html::textInput()]], [[yii\\helpers\\Html::activeTextInput()]]\n- [[yii\\helpers\\Html::hiddenInput()]], [[yii\\helpers\\Html::activeHiddenInput()]]\n- [[yii\\helpers\\Html::passwordInput()]] / [[yii\\helpers\\Html::activePasswordInput()]]\n- [[yii\\helpers\\Html::fileInput()]], [[yii\\helpers\\Html::activeFileInput()]]\n- [[yii\\helpers\\Html::textarea()]], [[yii\\helpers\\Html::activeTextarea()]]\n\nRadios 和 checkboxes 在方法的声明上有一点点不同：\n\n```php\n<?= Html::radio('agree', true, ['label' => 'I agree']);\n<?= Html::activeRadio($model, 'agree', ['class' => 'agreement'])\n\n<?= Html::checkbox('agree', true, ['label' => 'I agree']);\n<?= Html::activeCheckbox($model, 'agree', ['class' => 'agreement'])\n```\n\nDropdown list 和 list box 将会如下渲染：\n\n```php\n<?= Html::dropDownList('list', $currentUserId, ArrayHelper::map($userModels, 'id', 'name')) ?>\n<?= Html::activeDropDownList($users, 'id', ArrayHelper::map($userModels, 'id', 'name')) ?>\n\n<?= Html::listBox('list', $currentUserId, ArrayHelper::map($userModels, 'id', 'name')) ?>\n<?= Html::activeListBox($users, 'id', ArrayHelper::map($userModels, 'id', 'name')) ?>\n```\n\n第一个参数是 input 的名称，第二个是当前选中的值，第三个则是一个下标为列表值，值为列表标签的名值对数组。\n\n如果你需要使用多项选择， checkbox list 应该能够符合你的需求：\n\n```php\n<?= Html::checkboxList('roles', [16, 42], ArrayHelper::map($roleModels, 'id', 'name')) ?>\n<?= Html::activeCheckboxList($user, 'role', ArrayHelper::map($roleModels, 'id', 'name')) ?>\n```\n\n否则，用 radio list ：\n\n```php\n<?= Html::radioList('roles', [16, 42], ArrayHelper::map($roleModels, 'id', 'name')) ?>\n<?= Html::activeRadioList($user, 'role', ArrayHelper::map($roleModels, 'id', 'name')) ?>\n```\n\n\n### Labels 和 Errors（Labels and Errors） <span id=\"labels-and-errors\"></span>\n\n如同 inputs 一样，Yii 也提供了两个方法用于生成表单 label。带 ative  方法用于从 model 中取数据，另外一个则是直接接收数据。\n\n```php\n<?= Html::label('User name', 'username', ['class' => 'label username']) ?>\n<?= Html::activeLabel($user, 'username', ['class' => 'label username'])\n```\n\n为了从一个或者一组 model 中显示表单的概要错误，你可以使用如下方法：\n\n```php\n<?= Html::errorSummary($posts, ['class' => 'errors']) ?>\n```\n\n为了显示单个错误：\n\n```php\n<?= Html::error($post, 'title', ['class' => 'error']) ?>\n```\n\n\n### Input 的名和值（Input Names and Values） <span id=\"input-names-and-values\"></span>\n\nYii 提供了方法用于从 model 中获取 input 的名称，ids，值。这些主要用于内部调用，\n但是有时候你也需要使用它们：\n\n```php\n// Post[title]\necho Html::getInputName($post, 'title');\n\n// post-title\necho Html::getInputId($post, 'title');\n\n// my first post\necho Html::getAttributeValue($post, 'title');\n\n// $post->authors[0]\necho Html::getAttributeValue($post, '[0]authors[0]');\n```\n\n在上面的例子中，第一个参数为模型，而第二个参数是属性表达式。在最简单的表单中，这个属性表达式就是属性名称，但是在一些多行输入的时候，它也可以是属性名以数组下标前缀或者后缀（也可能是同时）。\n\n- `[0]content` 代表多行输入时第一个 model 的 content 属性的数据值。\n- `dates[0]` 代表 dates 属性的第一个数组元素。\n- `[0]dates[0]` 代表多行输入时第一个 model 的 dates 属性的第一个数组元素。\n\n为了获取一个没有前缀或者后缀的属性名称，我们可以如下做：\n\n```php\n// dates\necho Html::getAttributeName('dates[0]');\n```\n\n\n## 样式表和脚本（Styles and Scripts） <span id=\"styles-and-scripts\"></span>\n\nYii 提供两个方法用于生成包含内联样式和脚本代码的标签。\n\n```php\n<?= Html::style('.danger { color: #f00; }') ?>\n\nGives you\n\n<style>.danger { color: #f00; }</style>\n\n\n<?= Html::script('alert(\"Hello!\");', ['defer' => true]);\n\nGives you\n\n<script defer>alert(\"Hello!\");</script>\n```\n\n如果你想要外联 css 样式文件，可以如下做：\n\n```php\n<?= Html::cssFile('@web/css/ie5.css', ['condition' => 'IE 5']) ?>\n\ngenerates\n\n<!--[if IE 5]>\n    <link href=\"https://example.com/css/ie5.css\" />\n<![endif]-->\n```\n\n第一个参数是 URL。第二个参数是标签属性数组。比普通的标签配置项额外多出的是，你可以指定：\n\n- `condition` 来让 `<link` 被条件控制注释包裹（ IE hacker ）。\n  希望你在未来不再需要条件控制注释。\n- `noscript` 可以被设置为 `true` ，这样 `<link`就会被 `<noscript>`包裹，如此那么这段代码只有在浏览器不支持 \n  JavaScript 或者被用户禁用的时候才会被引入进来。\n\n为了外联 JavaScript 文件：\n\n```php\n<?= Html::jsFile('@web/js/main.js') ?>\n```\n\n这个方法的第一个参数同 CSS 一样用于指定外联链接。第二个参数是一个标签属性数组。\n同 `cssFile` 一样，你可以指定 `condtion` 配置项。\n\n\n## 超链接（Hyperlinks） <span id=\"hyperlinks\"></span>\n\n有一个方法可以用于便捷的生成超链接：\n\n```php\n<?= Html::a('Profile', ['user/view', 'id' => $id], ['class' => 'profile-link']) ?>\n```\n\n第一个参数是超链接的标题。它不会被转码，所以如果是用户输入数据，\n你需要使用 `Html::encode()` 方法进行转码。第二个参数是 `<a` 标签的 `href` 属性的值。\n关于该参数能够接受的更详细的数据值，请参阅 [Url::to()](helper-url.md)。\n第三个参数是标签的属性数组。\n\n在需要的时候，你可以用如下代码生成 `mailto` 链接：\n\n```php\n<?= Html::mailto('Contact us', 'admin@example.com') ?>\n```\n\n\n## 图片（Images） <span id=\"images\"></span>\n\n为了生成图片标签，你可以如下做：\n\n```php\n<?= Html::img('@web/images/logo.png', ['alt' => 'My logo']) ?>\n\ngenerates\n\n<img src=\"https://example.com/images/logo.png\" alt=\"My logo\" />\n```\n\n除了 [aliases](concept-aliases.md) 之外，第一个参数可以接受 路由，查询，URLs。同 [Url::to()](helper-url.md) 一样。\n\n\n## 列表（Lists） <span id=\"lists\"></span>\n\n无序列表可以如下生成：\n\n```php\n<?= Html::ul($posts, ['item' => function($item, $index) {\n    return Html::tag(\n        'li',\n        $this->render('post', ['item' => $item]),\n        ['class' => 'post']\n    );\n}]) ?>\n```\n\n有序列表请使用 `Html::ol()` 方法。\n"
  },
  {
    "path": "docs/guide-zh-CN/helper-overview.md",
    "content": "助手类（Helpers）\n==============\n\n> Note: 这部分正在开发中。\n\nYii 提供许多类来简化常见编码，如对字条串或数组的操作，\nHTML 代码生成，等等。这些助手类被编写在命名空间 `yii\\helpers` 下，并且\n全是静态类 （就是说它们只包含静态属性和静态方法，而且不能实例化）。\n\n可以通过调用其中一个静态方法来使用助手类，如下：\n\n```php\nuse yii\\helpers\\Html;\n\necho Html::encode('Test > test');\n```\n\n> Note: 为了支持 [自定义助手类](#customizing-helper-classes)，Yii 将每一个助手类\n  分隔成两个类：一个基类 (例如 `BaseArrayHelper`) 和一个具体的类 (例如 `ArrayHelper`)。\n  当使用助手类时，应该仅使用具体的类版本而不使用基类。\n\n\n核心助手类（Core Helper Classes）\n-----------------------------\n\nYii 发布版中提供以下核心助手类：\n\n- [ArrayHelper](helper-array.md)\n- Console\n- FileHelper\n- [Html](helper-html.md)\n- HtmlPurifier\n- Imagine（由 yii2-imagine 扩展提供）\n- Inflector\n- Json\n- Markdown\n- Security\n- StringHelper\n- [Url](helper-url.md)\n- VarDumper\n\n\n自定义助手类（Customizing Helper Classes） <span id=\"customizing-helper-classes\"></span>\n--------------------------------------\n\n如果想要自定义一个核心助手类 (例如 [[yii\\helpers\\ArrayHelper]])，你应该创建一个新的类继承\nhelpers对应的基类 (例如 [[yii\\helpers\\BaseArrayHelper]]) 并同样的命\n名你的这个类 (例如 [[yii\\helpers\\ArrayHelper]])，包括它的命名空间。这个类\n会用来替换框架最初的实现。\n\n下面示例显示了如何自定义 [[yii\\helpers\\ArrayHelper]] 类的\n[[yii\\helpers\\ArrayHelper::merge()|merge()]] 方法：\n\n```php\n<?php\n\nnamespace yii\\helpers;\n\nclass ArrayHelper extends BaseArrayHelper\n{\n    public static function merge($a, $b)\n    {\n        // 你自定义的实现\n    }\n}\n```\n\n将你的类保存在一个名为 `ArrayHelper.php` 的文件中。该文件可以在任何目录，例如 `@app/components`。\n\n接下来，在你的应用程序 [入口脚本](structure-entry-scripts.md) 处，在引入的 `yii.php` 文件后面\n添加以下代码行，用 [Yii 自动加载器](concept-autoloading.md) 来加载自定义类\n代替框架的原始助手类：\n\n```php\nYii::$classMap['yii\\helpers\\ArrayHelper'] = '@app/components/ArrayHelper.php';\n```\n\n注意，自定义助手类仅仅用于如果你想要更改助手类中\n现有的函数的行为。如果你想为你的应用程序添加附加功能，最好为它创建一个单独的\n助手类。\n"
  },
  {
    "path": "docs/guide-zh-CN/helper-url.md",
    "content": "Url 助手类（Url Helper）\n=====================\n\nUrl 帮助类提供一系列的静态方法来帮助管理 URL。\n\n\n## 获得通用 URL（Getting Common URLs） <span id=\"getting-common-urls\"></span>\n\n有两种获取通用 URLS 的方法 ：当前请求的 home URL 和 base URL 。\n为了获取 home URL ，使用如下代码：\n\n```php\n$relativeHomeUrl = Url::home();\n$absoluteHomeUrl = Url::home(true);\n$httpsAbsoluteHomeUrl = Url::home('https');\n```\n\n如果没有传任何参数，这个方法将会生成相对 URL 。你可以传 `true` 来获得一个针对当前协议的绝对 URL；\n或者，你可以明确的指定具体的协议类型（ `https` , `http` ）。\n\n如下代码可以获得当前请求的 base URL：\n\n ```php\n$relativeBaseUrl = Url::base();\n$absoluteBaseUrl = Url::base(true);\n$httpsAbsoluteBaseUrl = Url::base('https');\n```\n\n这个方法的调用方式和 `Url::home()` 的完全一样。\n\n\n## 创建 URLs（Creating URLs） <span id=\"creating-urls\"></span>\n\n为了创建一个给定路由的 URL 地址，请使用 `Url::toRoute()`方法。 这个方法使用 [[\\yii\\web\\UrlManager]] \n来创建一个 URL ：\n\n```php\n$url = Url::toRoute(['product/view', 'id' => 42]);\n```\n\n你可以指定一个字符串来作为路由，如： `site/index` 。如果想要指定将要被创建的 URL 的附加查询参数，\n你同样可以使用一个数组来作为路由。数组的格式须为：\n\n```php\n// generates: /index.php?r=site/index&param1=value1&param2=value2\n['site/index', 'param1' => 'value1', 'param2' => 'value2']\n```\n\n如果你想要创建一个带有 anchor 的 URL ，你可以使用一个带有 `#` 参数的数组。比如：\n\n```php\n// generates: /index.php?r=site/index&param1=value1#name\n['site/index', 'param1' => 'value1', '#' => 'name']\n```\n  \n一个路由既可能是绝对的又可能是相对的。一个绝对的路由以前导斜杠开头（如： `/site/index`），\n而一个相对的路由则没有（比如： `site/index` 或者 `index`）。一个相对的路由将会按照如下规则转换为绝对路由：\n\n- 如果这个路由是一个空的字符串，将会使用当前 [[\\yii\\web\\Controller::route|route]] 作为路由；\n- 如果这个路由不带任何斜杠（比如 `index` ），它会被认为是当前控制器的一个 action ID，\n  然后将会把 [[\\yii\\web\\Controller::uniqueId]] 插入到路由前面。\n- 如果这个路由不带前导斜杠（比如： `site/index` ），它会被认为是相对当前模块（module）的路由，\n  然后将会把 [[\\yii\\base\\Module::uniqueId|uniqueId]] 插入到路由前面。\n\n从2.0.2版本开始，你可以用 [alias](concept-aliases.md) 来指定一个路由。\n在这种情况下， alias 将会首先转换为实际的路由，\n然后会按照上述规则转换为绝对路由。\n\n以下是该方法的一些例子：\n\n```php\n// /index.php?r=site/index\necho Url::toRoute('site/index');\n\n// /index.php?r=site/index&src=ref1#name\necho Url::toRoute(['site/index', 'src' => 'ref1', '#' => 'name']);\n\n// /index.php?r=post/edit&id=100     假设别名 \"@postEdit\" 被定义为 \"post/edit\"\necho Url::toRoute(['@postEdit', 'id' => 100]);\n\n// https://www.example.com/index.php?r=site/index\necho Url::toRoute('site/index', true);\n\n// https://www.example.com/index.php?r=site/index\necho Url::toRoute('site/index', 'https');\n```\n\n还有另外一个方法 `Url::to()` 和 [[toRoute()]] 非常类似。这两个方法的唯一区别在于，前者要求一个路由必须用数组来指定。\n如果传的参数为字符串，它将会被直接当做 URL 。\n\n`Url::to()` 的第一个参数可以是：\n\n- 数组：将会调用 [[toRoute()]] 来生成URL。比如：\n  `['site/index']`, `['post/index', 'page' => 2]` 。\n  详细用法请参考 [[toRoute()]] 。\n- 带前导 `@` 的字符串：它将会被当做别名，\n  对应的别名字符串将会返回。\n- 空的字符串：当前请求的 URL 将会被返回；\n- 普通的字符串：返回本身。\n\n当 `$scheme` 指定了（无论是字符串还是 true ），一个带主机信息（通过 [[\\yii\\web\\UrlManager::hostInfo]] 获得）\n的绝对 URL 将会被返回。如果 `$url` 已经是绝对 URL 了，\n它的协议信息将会被替换为指定的（ https 或者 http ）。\n\n以下是一些使用示例：\n\n```php\n// /index.php?r=site/index\necho Url::to(['site/index']);\n\n// /index.php?r=site/index&src=ref1#name\necho Url::to(['site/index', 'src' => 'ref1', '#' => 'name']);\n\n// /index.php?r=post/edit&id=100     假设别名 \"@postEdit\" 被定义为 \"post/edit\"\necho Url::to(['@postEdit', 'id' => 100]);\n\n// 当前请求的 URL\necho Url::to();\n\n// /images/logo.gif\necho Url::to('@web/images/logo.gif');\n\n// images/logo.gif\necho Url::to('images/logo.gif');\n\n// https://www.example.com/images/logo.gif\necho Url::to('@web/images/logo.gif', true);\n\n// https://www.example.com/images/logo.gif\necho Url::to('@web/images/logo.gif', 'https');\n```\n\n从2.0.3版本开始，你可以使用 [[yii\\helpers\\Url::current()]] 来创建一个基于当前请求路由和 GET 参数的 URL。\n你可以通过传递一个 `$params` 给这个方法来添加或者删除 GET 参数。\n例如：\n\n```php\n// 假设 $_GET = ['id' => 123, 'src' => 'google']，当前路由为 \"post/view\"\n\n// /index.php?r=post/view&id=123&src=google\necho Url::current();\n\n// /index.php?r=post/view&id=123\necho Url::current(['src' => null]);\n// /index.php?r=post/view&id=100&src=google\necho Url::current(['id' => 100]);\n```\n\n\n## 记住 URLs（Remember URLs） <span id=\"remember-urls\"></span>\n\n有时，你需要记住一个 URL 并在后续的请求处理中使用它。\n你可以用以下方式达到这个目的：\n \n```php\n// 记住当前 URL \nUrl::remember();\n\n// 记住指定的 URL。参数格式请参阅 Url::to()。\nUrl::remember(['product/view', 'id' => 42]);\n\n// 记住用给定名称指定的 URL\nUrl::remember(['product/view', 'id' => 42], 'product');\n```\n\n在后续的请求处理中，可以用如下方式获得记住的 URL：\n\n```php\n$url = Url::previous();\n$productUrl = Url::previous('product');\n```\n                        \n## 检查相对 URLs（Checking Relative URLs） <span id=\"checking-relative-urls\"></span>\n\n你可以用如下代码检测一个 URL 是否是相对的（比如，包含主机信息部分）。\n                             \n```php\n$isRelative = Url::isRelative('test/it');\n```\n"
  },
  {
    "path": "docs/guide-zh-CN/images/advanced-app-configs.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Beschreibung\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"409.0\" width=\"148.0\" x=\"290.953299818952\" y=\"147.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Segoe UI Semilight\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"23.951171875\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"148.0\" x=\"0.0\" y=\"0.0\">console</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 4</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n0:\"/>\n    </node>\n    <node id=\"n1\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"409.0\" width=\"148.0\" x=\"310.2342794220951\" y=\"167.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Segoe UI Semilight\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"23.951171875\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"148.0\" x=\"0.0\" y=\"0.0\">backend</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"310.2342794220951\" y=\"167.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 3</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n1:\"/>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"62.0\" width=\"118.0\" x=\"348.0\" y=\"628.9999999999999\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"35.154296875\" x=\"41.4228515625\" y=\"21.0810546875\">index<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"rectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n3\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"403.951171875\" width=\"148.0\" x=\"333.0\" y=\"187.048828125\"/>\n              <y:Fill color=\"#DDFFDD\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#DDFFDD\" borderDistance=\"0.0\" fontFamily=\"Segoe UI Semilight\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"23.951171875\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"148.0\" x=\"0.0\" y=\"0.0\">frontend</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 1</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n3:\">\n        <node id=\"n3::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"348.0\" y=\"226.0\"/>\n              <y:Fill color=\"#FFCCCC\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"46.310546875\" x=\"35.8447265625\" y=\"21.0810546875\">params<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n3::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"348.0\" y=\"322.0\"/>\n              <y:Fill color=\"#FFCCCC\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"78.16796875\" x=\"19.916015625\" y=\"21.0810546875\">params-local<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n3::n2\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"348.0\" y=\"418.0\"/>\n              <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"32.46484375\" x=\"42.767578125\" y=\"21.0810546875\">main<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n3::n3\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"348.0\" y=\"514.0\"/>\n              <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.322265625\" x=\"26.8388671875\" y=\"21.0810546875\">main-local<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"62.0\" width=\"223.21580824827026\" x=\"7.0\" y=\"628.9999999999999\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"35.67578125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"52.0\" x=\"85.60790412413513\" y=\"13.162109375\">aliases\n（别名）<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"rectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"307.951171875\" width=\"311.0\" x=\"7.0\" y=\"187.048828125\"/>\n              <y:Fill color=\"#DDFFDD\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#DDFFDD\" borderDistance=\"0.0\" fontFamily=\"Segoe UI Semilight\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"23.951171875\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"311.0\" x=\"0.0\" y=\"0.0\">common</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"-8.0\" y=\"189.333984375\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 2</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n5:\">\n        <node id=\"n5::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"185.0\" y=\"226.0\"/>\n              <y:Fill color=\"#FFCCCC\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"78.16796875\" x=\"19.916015625\" y=\"21.0810546875\">params-local<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n5::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"185.0\" y=\"418.0\"/>\n              <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.322265625\" x=\"26.8388671875\" y=\"21.0810546875\">main-local<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n5::n2\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"22.0\" y=\"226.0\"/>\n              <y:Fill color=\"#FFCCCC\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"35.67578125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"52.0\" x=\"33.0\" y=\"13.162109375\">params\n（参数）<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n5::n3\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"62.0\" width=\"118.0\" x=\"22.0\" y=\"418.0\"/>\n              <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"35.67578125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"52.0\" x=\"33.0\" y=\"13.162109375\">main\n（主要）<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <edge id=\"e0\" source=\"n3::n3\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"2.0\" y=\"10.125\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-111.48005839096935\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"3.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.25\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"33.47449351530429\" y=\"-6.000000000000114\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n5::n1\" target=\"n3::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"5.6843418860808015E-14\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"218.8251533742331\" y=\"449.00000000000006\"/>\n            <y:Point x=\"218.8251533742331\" y=\"449.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"10.089752197265625\" y=\"-6.0\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n5::e0\" source=\"n5::n2\" target=\"n5::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"10.125\" y=\"-6.0\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n5::e1\" source=\"n5::n3\" target=\"n5::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"10.125\" y=\"-6.0\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n3::e0\" source=\"n3::n0\" target=\"n3::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"2.0\" y=\"10.125\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n3::e1\" source=\"n3::n1\" target=\"n3::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"2.0\" y=\"10.125\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n3::e2\" source=\"n3::n2\" target=\"n3::n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"2.0\" y=\"10.125\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n5::n0\" target=\"n3::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"-5.6843418860808015E-14\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"218.8251533742331\" y=\"256.99999999999994\"/>\n            <y:Point x=\"218.8251533742331\" y=\"257.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"delta\"/>\n          <y:EdgeLabel alignment=\"center\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"side_slider\" preferredPlacement=\"right\" ratio=\"0.0\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"10.089752197265625\" y=\"-6.0\">\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"right\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources/>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-zh-CN/images/application-lifecycle.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.13-->\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d0\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key for=\"graphml\" id=\"d7\" yfiles.type=\"resources\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d0\"/>\n    <node id=\"n0\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"571.4472707112631\" width=\"763.2772213171534\" x=\"-1269.9373595143054\" y=\"-207.17439524332679\"/>\n              <y:Fill color=\"#FFCC0024\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FFCC00\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"763.2772213171534\" x=\"0.0\" y=\"0.0\">Entry script (index.php or yii)</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"225.33495140075684\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 4</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n0:\">\n        <node id=\"n0::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"324.9258883570935\" x=\"-1249.511914911339\" y=\"-169.79793039957679\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"126.759765625\" x=\"99.08306136604676\" y=\"5.6494140625\">Load application config<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n0::n1\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d5\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"309.37646484374994\" width=\"330.35133296005984\" x=\"-1254.9373595143054\" y=\"35.272875467936274\"/>\n                  <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"330.35133296005984\" x=\"0.0\" y=\"0.0\">Create application instance</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"19\" bottomF=\"19.15489692687993\" left=\"5\" leftF=\"5.425444602966309\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 5</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n0::n1:\">\n            <node id=\"n0::n1::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"72.64934031168627\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"45.34375\" x=\"124.79106917854676\" y=\"5.6494140625\">preInit()<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n1\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"122.64524027506516\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"120.71875\" x=\"87.10356917854676\" y=\"5.6494140625\">Register error handler<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n2\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"174.96110788981125\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"173.435546875\" x=\"60.74517074104676\" y=\"5.6494140625\">Configure application properties<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n3\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-1234.511914911339\" y=\"226.56779181162517\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"27.33203125\" x=\"133.79692855354676\" y=\"5.6494140625\">init()<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n1::n4\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.9258883570935\" x=\"-1234.511914911339\" y=\"280.4944433848063\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"62.025390625\" x=\"116.45024886604676\" y=\"5.6494140625\">bootstrap()<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n        <node id=\"n0::n2\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d5\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"411.6943410237631\" width=\"324.9258883570935\" x=\"-846.5860265542456\" y=\"-169.79793039957679\"/>\n                  <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"324.9258883570935\" x=\"0.0\" y=\"0.0\">Run application</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"1.1368683772161603E-13\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 3</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n0::n2:\">\n            <node id=\"n0::n2::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.9258883570935\" x=\"-831.5860265542456\" y=\"-132.42146555582667\"/>\n                  <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"164.705078125\" x=\"65.11040511604676\" y=\"5.6494140625\">EVENT_BEFORE_REQUEST<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n2::n1\" yfiles.foldertype=\"group\">\n              <data key=\"d4\"/>\n              <data key=\"d5\"/>\n              <data key=\"d6\">\n                <y:ProxyAutoBoundsNode>\n                  <y:Realizers active=\"0\">\n                    <y:GroupNode>\n                      <y:Geometry height=\"204.37646484375\" width=\"294.9258883570935\" x=\"-831.5860265542456\" y=\"-78.79793039957679\"/>\n                      <y:Fill color=\"#99336635\" transparent=\"false\"/>\n                      <y:BorderStyle hasColor=\"false\" type=\"dashed\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#993366\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#FFFFFF\" visible=\"true\" width=\"294.9258883570935\" x=\"0.0\" y=\"0.0\">Handle request</y:NodeLabel>\n                      <y:Shape type=\"roundrectangle\"/>\n                      <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                      <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                      <y:BorderInsets bottom=\"8\" bottomF=\"7.929194132486941\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                    </y:GroupNode>\n                    <y:GroupNode>\n                      <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                      <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 4</y:NodeLabel>\n                      <y:Shape type=\"roundrectangle\"/>\n                      <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                      <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                      <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                    </y:GroupNode>\n                  </y:Realizers>\n                </y:ProxyAutoBoundsNode>\n              </data>\n              <graph edgedefault=\"directed\" id=\"n0::n2::n1:\">\n                <node id=\"n0::n2::n1::n0\">\n                  <data key=\"d6\">\n                    <y:ShapeNode>\n                      <y:Geometry height=\"30.0\" width=\"264.9258883570935\" x=\"-816.5860265542456\" y=\"-41.421465555826785\"/>\n                      <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"231.4609375\" x=\"16.732475428546763\" y=\"5.6494140625\">Resolve request into route and parameters<y:LabelModel>\n                          <y:SmartNodeLabelModel distance=\"4.0\"/>\n                        </y:LabelModel>\n                        <y:ModelParameter>\n                          <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                        </y:ModelParameter>\n                      </y:NodeLabel>\n                      <y:Shape type=\"rectangle\"/>\n                    </y:ShapeNode>\n                  </data>\n                </node>\n                <node id=\"n0::n2::n1::n1\">\n                  <data key=\"d6\">\n                    <y:ShapeNode>\n                      <y:Geometry height=\"30.0\" width=\"264.9258883570935\" x=\"-816.5860265542456\" y=\"18.578534444173215\"/>\n                      <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"197.44140625\" x=\"33.74224105354676\" y=\"5.6494140625\">Create module, controller and action<y:LabelModel>\n                          <y:SmartNodeLabelModel distance=\"4.0\"/>\n                        </y:LabelModel>\n                        <y:ModelParameter>\n                          <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                        </y:ModelParameter>\n                      </y:NodeLabel>\n                      <y:Shape type=\"rectangle\"/>\n                    </y:ShapeNode>\n                  </data>\n                </node>\n                <node id=\"n0::n2::n1::n2\">\n                  <data key=\"d6\">\n                    <y:ShapeNode>\n                      <y:Geometry height=\"30.0\" width=\"264.9258883570935\" x=\"-816.5860265542456\" y=\"72.64934031168627\"/>\n                      <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                      <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                      <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"61.369140625\" x=\"101.77837386604676\" y=\"5.649414062500057\">Run action<y:LabelModel>\n                          <y:SmartNodeLabelModel distance=\"4.0\"/>\n                        </y:LabelModel>\n                        <y:ModelParameter>\n                          <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                        </y:ModelParameter>\n                      </y:NodeLabel>\n                      <y:Shape type=\"rectangle\"/>\n                    </y:ShapeNode>\n                  </data>\n                </node>\n              </graph>\n            </node>\n            <node id=\"n0::n2::n2\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.9258883570935\" x=\"-831.5860265542456\" y=\"149.20206960042316\"/>\n                  <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"154.697265625\" x=\"70.11431136604676\" y=\"5.6494140625\">EVENT_AFTER_REQUEST<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n0::n2::n3\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"294.92588835709347\" x=\"-831.5860265542456\" y=\"196.89641062418633\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"148.099609375\" x=\"73.41313949104676\" y=\"5.6494140625\">Send response to end user<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n        <node id=\"n0::n3\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"324.9258883570935\" x=\"-846.5860265542456\" y=\"319.2728754679363\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"160.08203125\" x=\"82.42192855354676\" y=\"5.6494140625\">Complete request processing<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <edge id=\"e0\" source=\"n0\" target=\"n0::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-179.83580569315376\" sy=\"-180.3944529152355\" tx=\"-13.869777491410105\" ty=\"-154.8008369539754\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::e0\" source=\"n0::n0\" target=\"n0::n1\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#99CCFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"106.052734375\" x=\"-130.76131968438426\" y=\"78.18481445312506\">Configuration array<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"77.04379339868619\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e0\" source=\"n0::n1::n0\" target=\"n0::n1::n1\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e1\" source=\"n0::n1::n1\" target=\"n0::n1::n2\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e2\" source=\"n0::n1::n2\" target=\"n0::n1::n3\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n1::e3\" source=\"n0::n1::n3\" target=\"n0::n1::n4\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::e1\" source=\"n0::n1\" target=\"n0::n2\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"2.8060680342755404\" sy=\"-48.37646484374994\" tx=\"-162.49660512430125\" ty=\"105.53540293375653\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::e0\" source=\"n0::n2::n0\" target=\"n0::n2::n1\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::n1::e0\" source=\"n0::n2::n1::n0\" target=\"n0::n2::n1::n1\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::n1::e1\" source=\"n0::n2::n1::n1\" target=\"n0::n2::n1::n2\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::e1\" source=\"n0::n2::n1\" target=\"n0::n2::n2\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::n2::e2\" source=\"n0::n2::n2\" target=\"n0::n2::n3\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n0::e2\" source=\"n0::n2\" target=\"n0::n3\">\n      <data key=\"d9\"/>\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#99CCFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"59.353515625\" x=\"-93.67673227804266\" y=\"28.318773905436274\">Exit status<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"63.99999999999999\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.47945569632951074\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d7\">\n    <y:Resources/>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-zh-CN/images/application-structure.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"872.1807999999999\" y=\"-14.764159999999947\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"52.0\" x=\"24.0\" y=\"7.5810546875\">应用组件<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"-91.97375999999994\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"52.0\" x=\"24.0\" y=\"7.5810546875\">入口脚本<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"-14.764159999999947\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"52.0\" x=\"24.0\" y=\"7.5810546875\">应用主体<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"62.44544000000004\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"72.02734375\" x=\"13.986328125\" y=\"7.581054687500007\">控制器（C）<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"872.1807999999999\" y=\"62.44544000000005\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"40.0\" x=\"30.0\" y=\"7.5810546875\">过滤器<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"532.664\" y=\"23.901600000000087\"/>\n          <y:Fill color=\"#FF9900\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"28.0\" x=\"36.0\" y=\"7.5810546875\">模块<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"618.4047999999991\" y=\"139.65504000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"60.115234375\" x=\"19.9423828125\" y=\"7.5810546875\">视图（V）<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"786.161599999999\" y=\"139.65504000000004\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"63.724609375\" x=\"18.1376953125\" y=\"7.5810546875\">模型（M）<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n8\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"532.664\" y=\"216.86464000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"40.0\" x=\"30.0\" y=\"7.5810546875\">小部件<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n9\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"35.0\" width=\"100.0\" x=\"702.4223999999999\" y=\"216.86464000000004\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"35.67578125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"86.39453125\" x=\"6.802734375\" y=\"-0.337890625\">前端资源包\n(Asset Bundle)<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n2\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"20.681640625\" x=\"3.684755371093729\" y=\"-26.101202829100025\">1:1<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.025600000000054\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.17992697197425173\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n0\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.01171875\" x=\"-47.638518923284664\" y=\"-23.37618601966852\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.025599999999994\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5470891790487767\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n5\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.01171875\" x=\"16.6705249668222\" y=\"30.389885834361735\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"44.894496863129426\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.25416574623968574\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n3\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.01171875\" x=\"4.248516308593707\" y=\"-26.494264459837467\">1..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"15.254400000000032\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.2090245199765919\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n3\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.01171875\" x=\"-57.21845160906594\" y=\"-64.42636347296329\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"43.47658308018222\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.881412889692355\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n5\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"582.664\" y=\"-3.598399999999913\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.01171875\" x=\"-16.411943066406252\" y=\"-42.70266007995597\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"16.483200000000004\" distanceToCenter=\"true\" position=\"right\" ratio=\"5.822600000000001\" segment=\"-2\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n6\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.01171875\" x=\"-10.471163234315782\" y=\"-25.304719149604495\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"11.385360101663515\" distanceToCenter=\"true\" position=\"left\" ratio=\"0.10670293331985262\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e7\" source=\"n7\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.01171875\" x=\"-8.09331897615914\" y=\"-26.61891685238112\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.669798038586146\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.1670192242704811\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e8\" source=\"n9\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.01171875\" x=\"-8.582034677365982\" y=\"-26.348798499628572\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"14.15599378957306\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.15481212573620068\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e9\" source=\"n8\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.01171875\" x=\"-13.858375192872018\" y=\"-24.603393693996793\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"13.24330808446041\" distanceToCenter=\"true\" position=\"left\" ratio=\"0.05506723556297327\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e10\" source=\"n4\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.01171875\" x=\"-45.861058691406356\" y=\"-22.966588137206998\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"13.616000000000007\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e11\" source=\"n9\" target=\"n8\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.01171875\" x=\"-42.528297905071895\" y=\"-19.894585219726537\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.543999999999983\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.4117077892835431\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e12\" source=\"n7\" target=\"n6\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"diamond\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"22.01171875\" x=\"-43.20232446859063\" y=\"-19.894590493164003\">0..*<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.543999999999983\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.4531592446547025\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources/>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-zh-CN/images/rbac-access-check-1.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"35.67578125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"48.015625\" x=\"27.4921875\" y=\"16.662109375\">admin\n(管理媛)<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"35.67578125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"52.0\" x=\"25.5\" y=\"16.662109375\">author\n（作者）<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"66.208984375\" x=\"-4.824413299560547\" y=\"-28.951446533203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.6328125\" x=\"-4.039356231689453\" y=\"-28.838375091552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"35.67578125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"76.0\" x=\"13.5\" y=\"16.662109375\">updatePost\n（编辑帖子）<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"35.67578125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"112.0\" x=\"-4.5\" y=\"16.662109375\">updateOwnPost\n（编辑自己的帖子）<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"35.67578125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"76.0\" x=\"13.5\" y=\"16.662109375\">createPost\n（创建新帖）<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"69.138671875\" x=\"16.9306640625\" y=\"4.8489837646484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-zh-CN/images/rbac-access-check-2.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"35.67578125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"48.015625\" x=\"27.4921875\" y=\"16.662109375\">admin\n(管理媛)<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"35.67578125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"52.0\" x=\"25.5\" y=\"16.662109375\">author\n（作者）<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"66.208984375\" x=\"-4.824413299560547\" y=\"-28.951446533203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.6328125\" x=\"-4.039356231689453\" y=\"-28.838375091552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"35.67578125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"76.0\" x=\"13.5\" y=\"16.662109375\">updatePost\n（编辑帖子）<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"35.67578125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"112.0\" x=\"-4.5\" y=\"16.662109375\">updateOwnPost\n（编辑自己的帖子）<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"35.67578125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"76.0\" x=\"13.5\" y=\"16.662109375\">createPost\n（创建新帖）<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"69.138671875\" x=\"16.9306640625\" y=\"4.8489837646484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-zh-CN/images/rbac-access-check-3.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"35.67578125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"48.015625\" x=\"27.4921875\" y=\"16.662109375\">admin\n(管理媛)<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"35.67578125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"52.0\" x=\"25.5\" y=\"16.662109375\">author\n（作者）<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"66.208984375\" x=\"-4.824413299560547\" y=\"-28.951446533203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.6328125\" x=\"-4.039356231689453\" y=\"-28.838375091552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"35.67578125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"76.0\" x=\"13.5\" y=\"16.662109375\">updatePost\n（编辑帖子）<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"35.67578125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"112.0\" x=\"-4.5\" y=\"16.662109375\">updateOwnPost\n（编辑自己的帖子）<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"35.67578125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"76.0\" x=\"13.5\" y=\"16.662109375\">createPost\n（创建新帖）<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"69.138671875\" x=\"16.9306640625\" y=\"4.8489837646484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#FF0000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-zh-CN/images/rbac-hierarchy-1.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"35.67578125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"48.015625\" x=\"27.4921875\" y=\"16.662109375\">admin\n(管理媛)<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"35.67578125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"52.0\" x=\"25.5\" y=\"16.662109375\">author\n（作者）<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"66.208984375\" x=\"-4.824413299560547\" y=\"-28.951446533203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.6328125\" x=\"-4.039356231689453\" y=\"-28.838375091552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"35.67578125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"76.0\" x=\"13.5\" y=\"16.662109375\">updatePost\n（编辑帖子）<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"35.67578125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"76.0\" x=\"13.5\" y=\"16.662109375\">createPost\n（创建新帖）<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n5\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-zh-CN/images/rbac-hierarchy-2.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"35.67578125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"48.015625\" x=\"27.4921875\" y=\"16.662109375\">admin\n(管理媛)<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"-11.5\"/>\n          <y:Fill color=\"#ADF4A6\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"35.67578125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"52.0\" x=\"25.5\" y=\"16.662109375\">author\n（作者）<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"64.53585815429688\" width=\"56.560157775878906\" x=\"216.21992111206055\" y=\"-132.03585815429688\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"66.208984375\" x=\"-4.824413299560547\" y=\"-28.951446533203125\">John, ID=2<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.113555908203125\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"66.76200103759766\" width=\"56.554100036621094\" x=\"57.22294998168945\" y=\"-133.14892959594727\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.6328125\" x=\"-4.039356231689453\" y=\"-28.838375091552734\">Jane, ID=1<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-9.000484466552734\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n4\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"34.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"35.67578125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"76.0\" x=\"13.5\" y=\"16.662109375\">updatePost\n（编辑帖子）<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"193.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"35.67578125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"112.0\" x=\"-4.5\" y=\"16.662109375\">updateOwnPost\n（编辑自己的帖子）<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"69.0\" width=\"103.0\" x=\"352.0\" y=\"197.5\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"35.67578125\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"76.0\" x=\"13.5\" y=\"16.662109375\">createPost\n（创建新帖）<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"29.535858154296875\" width=\"103.0\" x=\"193.0\" y=\"167.96414184570312\"/>\n          <y:Fill color=\"#FFCC00\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"69.138671875\" x=\"16.9306640625\" y=\"4.8489837646484375\">AuthorRule<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <edge id=\"e0\" source=\"n4\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n4\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n1\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n6\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\">\n            <y:Point x=\"403.5\" y=\"23.0\"/>\n          </y:Path>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n1\" target=\"n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n0\" target=\"n3\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n7\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"65px\" viewBox=\"0 0 57 65\" enable-background=\"new 0 0 57 65\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_18_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"22.6621\" cy=\"21.707\" r=\"17.7954\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#E55E03\" d=\"M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438\n\t\tc0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051\n\t\tc0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_3_\" cx=\"15.2056\" cy=\"831.1875\" r=\"32.3071\" gradientTransform=\"matrix(1 0 0 1 0.0801 -773.6914)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_3_)\" stroke=\"#E55E03\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_4_\" cx=\"17.0723\" cy=\"18.4907\" r=\"11.8931\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_4_)\" stroke=\"#E55E03\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397\n\t\tc-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359\n\t\tc-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_5_\" cx=\"31.8184\" cy=\"19.3525\" r=\"14.63\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_5_)\" stroke=\"#E55E03\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617\n\t\tc0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313\n\t\tc3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_6_\" cx=\"30.4893\" cy=\"4.8721\" r=\"5.2028\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_6_)\" stroke=\"#E55E03\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_7_\" cx=\"23.2871\" cy=\"5.3008\" r=\"5.5143\" gradientTransform=\"matrix(1 0 0 -1 0.04 64.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FCB57A\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FF8C36\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_7_)\" stroke=\"#E55E03\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.501\" y1=\"-12291.5195\" x2=\"6492.1304\" y2=\"-12384.9688\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Young_Black_1_\" fill=\"#5C5C5C\" stroke=\"#353535\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"67px\" viewBox=\"0 0 57 67\" enable-background=\"new 0 0 57 67\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3398\" y1=\"3115.7266\" x2=\"27.5807\" y2=\"3145.5239\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.5835\" cy=\"3117.4922\" r=\"23.425\" fx=\"23.0139\" fy=\"3115.0024\" gradientTransform=\"matrix(1 0 0 1 0.3203 -3091.7656)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"6468.5\" y1=\"-12286.8594\" x2=\"6492.1294\" y2=\"-12380.3086\" gradientTransform=\"matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z\"/&gt;\n\t&lt;path id=\"Hair_Female_1_Red_1_\" fill=\"#FAE1AA\" stroke=\"#E2B354\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M28.372,0.5\n\t\tC17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526\n\t\tc-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228\n\t\ts2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781\n\t\tc15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"body_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"95.9063\" y1=\"-3134.2153\" x2=\"31.5133\" y2=\"-3134.2153\" gradientTransform=\"matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#49AD33\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#C2DA92\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"body_8_\" fill=\"url(#body_1_)\" stroke=\"#008D33\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146\n\t\tc-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-zh-CN/images/request-lifecycle.graphml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:y=\"http://www.yworks.com/xml/graphml\" xmlns:yed=\"http://www.yworks.com/xml/yed/3\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd\">\n  <!--Created by yEd 3.12.2-->\n  <key for=\"graphml\" id=\"d0\" yfiles.type=\"resources\"/>\n  <key for=\"port\" id=\"d1\" yfiles.type=\"portgraphics\"/>\n  <key for=\"port\" id=\"d2\" yfiles.type=\"portgeometry\"/>\n  <key for=\"port\" id=\"d3\" yfiles.type=\"portuserdata\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"node\" id=\"d4\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"node\" id=\"d5\"/>\n  <key for=\"node\" id=\"d6\" yfiles.type=\"nodegraphics\"/>\n  <key attr.name=\"Description\" attr.type=\"string\" for=\"graph\" id=\"d7\"/>\n  <key attr.name=\"url\" attr.type=\"string\" for=\"edge\" id=\"d8\"/>\n  <key attr.name=\"description\" attr.type=\"string\" for=\"edge\" id=\"d9\"/>\n  <key for=\"edge\" id=\"d10\" yfiles.type=\"edgegraphics\"/>\n  <graph edgedefault=\"directed\" id=\"G\">\n    <data key=\"d7\"/>\n    <node id=\"n0\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"70.13700103759766\" width=\"56.558998107910156\" x=\"198.38633947503078\" y=\"261.099458694458\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"26.279499053955078\" y=\"74.13700103759766\">\n            <y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"-0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"0.5\" offsetX=\"0.0\" offsetY=\"4.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"bold\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"28.0\" x=\"-32.0\" y=\"25.149555206298828\">用户<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.5\" labelRatioY=\"0.0\" nodeRatioX=\"-0.5\" nodeRatioY=\"0.0\" offsetX=\"-4.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"1\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n1\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"637.1271178722382\"/>\n          <y:Fill color=\"#99CCFF\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"13\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"38.3154296875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"43.5966796875\" x=\"37.70166015625\" y=\"3.34228515625\">模型\nmodel<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n2\">\n      <data key=\"d6\">\n        <y:SVGNode>\n          <y:Geometry height=\"46.887996673583984\" width=\"39.527000427246094\" x=\"828.8018617630005\" y=\"544.9831195354463\"/>\n          <y:Fill color=\"#CCCCFF\" transparent=\"false\"/>\n          <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"bold\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"40.0\" x=\"-0.23649978637695312\" y=\"-31.859966278076172\">数据库<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.5\" nodeRatioX=\"0.0\" nodeRatioY=\"-0.5\" offsetX=\"0.0\" offsetY=\"-12.022075653076172\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"17.763500213623047\" y=\"21.443998336791992\">\n            <y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:SVGNodeProperties usingVisualBounds=\"true\"/>\n          <y:SVGModel svgBoundsPolicy=\"0\">\n            <y:SVGContent refid=\"2\"/>\n          </y:SVGModel>\n        </y:SVGNode>\n      </data>\n    </node>\n    <node id=\"n3\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"713.6271178722382\"/>\n          <y:Fill color=\"#99CC00\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"13\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"38.3154296875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"31.92333984375\" x=\"43.538330078125\" y=\"3.34228515625\">视图\nview<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n4\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"378.4581825971604\" width=\"219.27949905395508\" x=\"527.4919853210449\" y=\"406.5057531356811\"/>\n              <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"微软雅黑\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"23.79736328125\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"219.27949905395508\" x=\"0.0\" y=\"0.0\">控制器(Controller)</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"2\" leftF=\"1.7335329055786133\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"412.2765645980835\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 1</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n4:\">\n        <node id=\"n4::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"445.3031164169311\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"52.0\" x=\"49.27949905395508\" y=\"5.0810546875\">创建动作<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n4::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"45.0\" width=\"159.91864204406738\" x=\"558.5326642990112\" y=\"521.8166599273682\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"52.0\" x=\"53.95932102203369\" y=\"12.5810546875\">实施过滤<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"diamond\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n4::n2\" yfiles.foldertype=\"group\">\n          <data key=\"d4\"/>\n          <data key=\"d6\">\n            <y:ProxyAutoBoundsNode>\n              <y:Realizers active=\"0\">\n                <y:GroupNode>\n                  <y:Geometry height=\"164.13418114185333\" width=\"187.54596614837646\" x=\"544.2255182266235\" y=\"605.8297545909882\"/>\n                  <y:Fill color=\"#99336635\" transparent=\"false\"/>\n                  <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#993366\" borderDistance=\"0.0\" fontFamily=\"微软雅黑\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"23.79736328125\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#FFFFFF\" visible=\"true\" width=\"187.54596614837646\" x=\"0.0\" y=\"0.0\">动作(Action)</y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                  <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n                  <y:BorderInsets bottom=\"4\" bottomF=\"3.8368178606033325\" left=\"4\" leftF=\"3.9869680404663086\" right=\"3\" rightF=\"3.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n                <y:GroupNode>\n                  <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n                  <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 3</y:NodeLabel>\n                  <y:Shape type=\"roundrectangle\"/>\n                  <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n                  <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n                  <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n                </y:GroupNode>\n              </y:Realizers>\n            </y:ProxyAutoBoundsNode>\n          </data>\n          <graph edgedefault=\"directed\" id=\"n4::n2:\">\n            <node id=\"n4::n2::n0\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"644.6271178722382\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"52.0\" x=\"49.27949905395508\" y=\"5.0810546875\">加载模型<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n            <node id=\"n4::n2::n1\">\n              <data key=\"d6\">\n                <y:ShapeNode>\n                  <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"721.1271178722382\"/>\n                  <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n                  <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n                  <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"52.0\" x=\"49.27949905395508\" y=\"5.0810546875\">渲染视图<y:LabelModel>\n                      <y:SmartNodeLabelModel distance=\"4.0\"/>\n                    </y:LabelModel>\n                    <y:ModelParameter>\n                      <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                    </y:ModelParameter>\n                  </y:NodeLabel>\n                  <y:Shape type=\"rectangle\"/>\n                </y:ShapeNode>\n              </data>\n            </node>\n          </graph>\n        </node>\n      </graph>\n    </node>\n    <node id=\"n5\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"96.0\" x=\"189.47636032104495\" y=\"713.6271178722382\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"13\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"38.3154296875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"63.85205078125\" x=\"16.073974609375\" y=\"3.34228515625\">响应\nResponse<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n6\">\n      <data key=\"d6\">\n        <y:ShapeNode>\n          <y:Geometry height=\"45.0\" width=\"119.0\" x=\"789.0653619766235\" y=\"254.50096702575684\"/>\n          <y:Fill color=\"#FFCC99\" transparent=\"false\"/>\n          <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n          <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"13\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"38.3154296875\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"54.42578125\" x=\"32.287109375\" y=\"3.34228515625\">请求\nRequest<y:LabelModel>\n              <y:SmartNodeLabelModel distance=\"4.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n            </y:ModelParameter>\n          </y:NodeLabel>\n          <y:Shape type=\"roundrectangle\"/>\n        </y:ShapeNode>\n      </data>\n    </node>\n    <node id=\"n7\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"145.553129196167\" width=\"219.27949905395508\" x=\"527.4919853210449\" y=\"221.44783782958984\"/>\n              <y:Fill color=\"#FFEFD6\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FF9900\" borderDistance=\"0.0\" fontFamily=\"微软雅黑\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"23.79736328125\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"219.27949905395508\" x=\"0.0\" y=\"0.0\">应用主体</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"21\" leftF=\"20.720500946044922\" right=\"18\" rightF=\"18.0\" top=\"2\" topF=\"1.7557659149169922\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"0.0\" y=\"60.0\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 2</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n7:\">\n        <node id=\"n7::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"262.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"94.15234375\" x=\"28.203327178955078\" y=\"5.0810546875\">解析路由(Route)<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n7::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"150.55899810791016\" x=\"563.2124862670898\" y=\"322.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"64.0\" x=\"43.27949905395508\" y=\"5.0810546875\">创建控制器<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <node id=\"n8\" yfiles.foldertype=\"group\">\n      <data key=\"d4\"/>\n      <data key=\"d6\">\n        <y:ProxyAutoBoundsNode>\n          <y:Realizers active=\"0\">\n            <y:GroupNode>\n              <y:Geometry height=\"143.79736328125\" width=\"159.91864204406738\" x=\"313.2978515625\" y=\"223.20360374450684\"/>\n              <y:Fill color=\"#FFCC0024\" transparent=\"false\"/>\n              <y:BorderStyle hasColor=\"false\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"node_width\" backgroundColor=\"#FFCC00\" borderDistance=\"0.0\" fontFamily=\"微软雅黑\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"23.79736328125\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"159.91864204406738\" x=\"0.0\" y=\"0.0\">入口脚本</y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n              <y:State closed=\"false\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"15\" bottomF=\"15.0\" left=\"15\" leftF=\"15.0\" right=\"15\" rightF=\"15.0\" top=\"15\" topF=\"15.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"1\" leftF=\"0.9186420440673828\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n            <y:GroupNode>\n              <y:Geometry height=\"50.0\" width=\"50.0\" x=\"313.2978515625\" y=\"225.33495140075684\"/>\n              <y:Fill color=\"#F5F5F5\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"dashed\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"right\" autoSizePolicy=\"node_width\" backgroundColor=\"#EBEBEB\" borderDistance=\"0.0\" fontFamily=\"Dialog\" fontSize=\"15\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"22.37646484375\" modelName=\"internal\" modelPosition=\"t\" textColor=\"#000000\" visible=\"true\" width=\"59.02685546875\" x=\"-4.513427734375\" y=\"0.0\">Folder 4</y:NodeLabel>\n              <y:Shape type=\"roundrectangle\"/>\n              <y:State closed=\"true\" closedHeight=\"50.0\" closedWidth=\"50.0\" innerGraphDisplayEnabled=\"false\"/>\n              <y:Insets bottom=\"5\" bottomF=\"5.0\" left=\"5\" leftF=\"5.0\" right=\"5\" rightF=\"5.0\" top=\"5\" topF=\"5.0\"/>\n              <y:BorderInsets bottom=\"0\" bottomF=\"0.0\" left=\"0\" leftF=\"0.0\" right=\"0\" rightF=\"0.0\" top=\"0\" topF=\"0.0\"/>\n            </y:GroupNode>\n          </y:Realizers>\n        </y:ProxyAutoBoundsNode>\n      </data>\n      <graph edgedefault=\"directed\" id=\"n8:\">\n        <node id=\"n8::n0\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"129.0\" x=\"329.2164936065674\" y=\"262.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"76.0\" x=\"26.5\" y=\"5.0810546875\">加载应用配置<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n        <node id=\"n8::n1\">\n          <data key=\"d6\">\n            <y:ShapeNode>\n              <y:Geometry height=\"30.0\" width=\"129.0\" x=\"329.2164936065674\" y=\"322.00096702575684\"/>\n              <y:Fill color=\"#FFFFFF\" transparent=\"false\"/>\n              <y:BorderStyle color=\"#000000\" type=\"line\" width=\"1.0\"/>\n              <y:NodeLabel alignment=\"center\" autoSizePolicy=\"content\" fontFamily=\"微软雅黑\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" height=\"19.837890625\" modelName=\"custom\" textColor=\"#000000\" visible=\"true\" width=\"52.0\" x=\"38.5\" y=\"5.0810546875\">运行应用<y:LabelModel>\n                  <y:SmartNodeLabelModel distance=\"4.0\"/>\n                </y:LabelModel>\n                <y:ModelParameter>\n                  <y:SmartNodeLabelModelParameter labelRatioX=\"0.0\" labelRatioY=\"0.0\" nodeRatioX=\"0.0\" nodeRatioY=\"0.0\" offsetX=\"0.0\" offsetY=\"0.0\" upX=\"0.0\" upY=\"-1.0\"/>\n                </y:ModelParameter>\n              </y:NodeLabel>\n              <y:Shape type=\"rectangle\"/>\n            </y:ShapeNode>\n          </data>\n        </node>\n      </graph>\n    </node>\n    <edge id=\"e0\" source=\"n2\" target=\"n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e1\" source=\"n5\" target=\"n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-10.81052179205907\" sy=\"-22.46484374999998\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasBackgroundColor=\"false\" hasLineColor=\"false\" hasText=\"false\" height=\"4.0\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"4.0\" x=\"28.000006009454637\" y=\"-193.20499098300934\">\n            <y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"17.34765625\" x=\"-8.673822115545363\" y=\"-200.55557692050934\">11<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e2\" source=\"n6\" target=\"n7::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-29.525518994973595\" y=\"-10.349628448486328\">3<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"0.9990329742431641\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.25395048761528816\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n7::e0\" source=\"n7::n0\" target=\"n7::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e3\" source=\"n0\" target=\"n8\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"-79.9882439360955\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"36.76878085201736\" y=\"-9.86869987293727\">1<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e4\" source=\"n7::n1\" target=\"n4\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"2.408647025755731\" ty=\"-181.21658369302781\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-4.968363921444393\" y=\"17.90179758071895\">4<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e5\" source=\"n3\" target=\"n4::n2::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-30.100868416252297\" y=\"-9.35060429573059\">9<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"10.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.26448415477215953\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e6\" source=\"n4::n2::n1\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"-2.2737367544323206E-13\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"17.34765625\" x=\"-92.14914800503647\" y=\"-9.350604295730705\">10<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.2786124840137136\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e7\" source=\"n8::n1\" target=\"n7\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"55.750299312644984\" sy=\"0.355224609375\" tx=\"-109.63961141576266\" ty=\"42.066115379333496\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"29.326010704040527\" y=\"-9.824012225208321\">2<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.5\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e8\" source=\"n1\" target=\"n4::n2::n0\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-31.270312699786246\" y=\"-10.35060429573059\">8<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"1.0\" distanceToCenter=\"true\" position=\"right\" ratio=\"0.2858946861565087\" segment=\"-1\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"e9\" source=\"n4::n1\" target=\"n5\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"-79.9882439360951\" sy=\"2.2737367544323206E-13\" tx=\"24.24112858184396\" ty=\"-1.4999999999999991\">\n            <y:Point x=\"261.71748890288893\" y=\"544.3166599273684\"/>\n          </y:Path>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFFFFF\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-82.85058482368561\" y=\"-9.350576400756609\">6<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.23459266172695797\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::e0\" source=\"n4::n0\" target=\"n4::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFEFD6\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-5.336933135986328\" y=\"4.999985313415493\">5<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"0.0\" distance=\"30.0\" distanceToCenter=\"true\" position=\"center\" ratio=\"0.0\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::e1\" source=\"n4::n1\" target=\"n4::n2\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"-81.01828954355172\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:EdgeLabel alignment=\"center\" backgroundColor=\"#FFEFD6\" configuration=\"AutoFlippingLabel\" distance=\"2.0\" fontFamily=\"Dialog\" fontSize=\"12\" fontStyle=\"plain\" hasLineColor=\"false\" height=\"18.701171875\" modelName=\"custom\" preferredPlacement=\"anywhere\" ratio=\"0.5\" textColor=\"#000000\" visible=\"true\" width=\"10.673828125\" x=\"-5.450484052870365\" y=\"5.047372817993164\">7<y:LabelModel>\n              <y:SmartEdgeLabelModel autoRotationEnabled=\"false\" defaultAngle=\"0.0\" defaultDistance=\"10.0\"/>\n            </y:LabelModel>\n            <y:ModelParameter>\n              <y:SmartEdgeLabelModelParameter angle=\"6.283185307179586\" distance=\"30.0\" distanceToCenter=\"false\" position=\"center\" ratio=\"0.0\" segment=\"0\"/>\n            </y:ModelParameter>\n            <y:PreferredPlacementDescriptor angle=\"0.0\" angleOffsetOnRightSide=\"0\" angleReference=\"absolute\" angleRotationOnRightSide=\"co\" distance=\"-1.0\" frozen=\"true\" placement=\"anywhere\" side=\"anywhere\" sideReference=\"relative_to_edge_flow\"/>\n          </y:EdgeLabel>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n4::n2::e0\" source=\"n4::n2::n0\" target=\"n4::n2::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n    <edge id=\"n8::e0\" source=\"n8::n0\" target=\"n8::n1\">\n      <data key=\"d10\">\n        <y:PolyLineEdge>\n          <y:Path sx=\"0.0\" sy=\"0.0\" tx=\"0.0\" ty=\"0.0\"/>\n          <y:LineStyle color=\"#666666\" type=\"line\" width=\"2.0\"/>\n          <y:Arrows source=\"none\" target=\"standard\"/>\n          <y:BendStyle smoothed=\"false\"/>\n        </y:PolyLineEdge>\n      </data>\n    </edge>\n  </graph>\n  <data key=\"d0\">\n    <y:Resources>\n      <y:Resource id=\"1\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\" id=\"Ebene_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n\t width=\"57px\" height=\"66px\" viewBox=\"0 0 57 66\" enable-background=\"new 0 0 57 66\" xml:space=\"preserve\"&gt;\n&lt;g&gt;\n\t\n\t\t&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"26.3799\" y1=\"-2276.8809\" x2=\"27.6209\" y2=\"-2306.6792\" gradientTransform=\"matrix(1 0 0 -1 0.2803 -2252.9199)\"&gt;\n\t\t&lt;stop  offset=\"0.2711\" style=\"stop-color:#FFAB4F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFD28F\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path fill=\"url(#SVGID_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109\n\t\tV37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77\n\t\tc0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path id=\"body_13_\" fill=\"#ECECEC\" stroke=\"#9B9B9B\" stroke-miterlimit=\"10\" d=\"M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51\n\t\tc1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146\n\t\tc-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021\n\t\tC1.378,56.689,0.5,62.768,0.5,62.768z\"/&gt;\n\t&lt;path fill=\"#2068A3\" stroke=\"#2068A3\" d=\"M28.106,33.487c-8.112,0-12.688,4.312-12.688,10.437c0,7.422,12.688,10.438,12.688,10.438\n\t\ts14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.487,28.106,33.487z M26.288,53.051c0,0-7.135-2.093-8.805-7.201\n\t\tc-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387\n\t\tC40.445,49.917,26.288,53.051,26.288,53.051z\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"SVGID_2_\" cx=\"14.2417\" cy=\"9.1006\" r=\"53.247\" gradientTransform=\"matrix(1 0 0 -1 0.04 65.1543)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#74AEEE\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#2068A3\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path fill=\"url(#SVGID_2_)\" stroke=\"#2068A3\" stroke-miterlimit=\"10\" d=\"M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67\n\t\tc-2.854,5.51-14.022,7.807-14.022,7.807s-10.472-2.484-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25\n\t\tc-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492\n\t\tC56.055,62.768,54.211,55.906,49.529,51.225z\"/&gt;\n\t&lt;path fill=\"#5491CF\" stroke=\"#2068A3\" d=\"M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397c-0.514,1.027-1.669,4.084-1.669,5.148\n\t\tc0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.335-2.36c-3.601-1.419-4.071-3.063-5.89-4.854\n\t\tC12.523,47.135,12.878,45,13.404,44.173z\"/&gt;\n\t&lt;path fill=\"#5491CF\" stroke=\"#2068A3\" d=\"M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617c0.516,1.025,3.617,3.693,3.617,6.617\n\t\tc0,5.186-10.27,8.576-16.698,9.145c1.429,4.938,11.372,1.293,13.804-0.313c3.563-2.354,4.563-5.133,7.854-3.705\n\t\tC47.754,49.045,48.006,46.574,45.777,43.924z\"/&gt;\n\t&lt;path fill=\"none\" stroke=\"#2068A3\" stroke-linecap=\"round\" d=\"M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813\n\t\tc-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75\"/&gt;\n\t&lt;path fill=\"none\" stroke=\"#2068A3\" stroke-linecap=\"round\" d=\"M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3\n\t\tc0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333\"/&gt;\n\t\n\t\t&lt;radialGradient id=\"face_x5F_white_1_\" cx=\"27.623\" cy=\"-2278.646\" r=\"23.425\" fx=\"23.0534\" fy=\"-2281.1357\" gradientTransform=\"matrix(1 0 0 -1 0.2803 -2252.9199)\" gradientUnits=\"userSpaceOnUse\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFD28F\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFAB4F\"/&gt;\n\t&lt;/radialGradient&gt;\n\t&lt;path id=\"face_x5F_white_3_\" fill=\"url(#face_x5F_white_1_)\" stroke=\"#ED9135\" stroke-miterlimit=\"10\" d=\"M43.676,23.357\n\t\tc0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012\n\t\tC36.627,4.945,43.59,13.158,43.676,23.357z\"/&gt;\n\t\n\t\t&lt;linearGradient id=\"face_highlight_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"5761.7578\" y1=\"11330.6484\" x2=\"5785.3872\" y2=\"11424.0977\" gradientTransform=\"matrix(0.275 0 0 0.2733 -1558.9874 -3088.4209)\"&gt;\n\t\t&lt;stop  offset=\"0\" style=\"stop-color:#FFFFFF;stop-opacity:0.24\"/&gt;\n\t\t&lt;stop  offset=\"1\" style=\"stop-color:#FFFFFF;stop-opacity:0.16\"/&gt;\n\t&lt;/linearGradient&gt;\n\t&lt;path id=\"face_highlight_3_\" fill=\"url(#face_highlight_1_)\" d=\"M27.958,6.333c-6.035,0.047-10.747,4.493-12.787,10.386\n\t\tc-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247\n\t\tc2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.083,13.952,36.271,6.268,27.958,6.333z\"/&gt;\n\t&lt;path id=\"Hair_Young_Brown_1_\" fill=\"#CC9869\" stroke=\"#99724F\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M20.278,13.25\n\t\tc3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807\n\t\ts3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678\n\t\tL14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z\"/&gt;\n\t&lt;path fill=\"#4B4B4B\" stroke=\"#4B4B4B\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" d=\"M28.105,2\n\t\tC22.464,2,20.2,4.246,18.13,5.533C29.753,2.865,41.152,10.375,44.46,20.5C44.459,16.875,44.459,2,28.105,2z\"/&gt;\n\t&lt;path fill=\"#9B9B9B\" stroke=\"#4B4B4B\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" d=\"M11.151,17.751\n\t\tC12.878,8.25,18.686,6.309,25.273,7.127C31.295,7.875,36.93,10.491,44.459,20.5C37.777,7.125,20.278-3.375,9.903,3.921\n\t\tC5.569,6.97,4.903,13.375,11.151,17.751z\"/&gt;\n&lt;/g&gt;\n&lt;/svg&gt;\n</y:Resource>\n      <y:Resource id=\"2\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;svg version=\"1.1\"\n\t xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\t x=\"0px\" y=\"0px\" width=\"41px\" height=\"48px\" viewBox=\"-0.875 -0.887 41 48\" enable-background=\"new -0.875 -0.887 41 48\"\n\t xml:space=\"preserve\"&gt;\n&lt;defs&gt;\n&lt;/defs&gt;\n&lt;linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-979.1445\" x2=\"682.0508\" y2=\"-979.1445\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_1_)\" d=\"M19.625,36.763C8.787,36.763,0,34.888,0,32.575v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,34.888,30.464,36.763,19.625,36.763z\"/&gt;\n&lt;linearGradient id=\"SVGID_2_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-973.1445\" x2=\"682.0508\" y2=\"-973.1445\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_2_)\" d=\"M19.625,36.763c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.927-18.396,3.927\n\tc-9.481,0-17.396-1.959-18.396-3.927l-1.229,2C0,34.888,8.787,36.763,19.625,36.763z\"/&gt;\n&lt;path fill=\"#3C89C9\" d=\"M19.625,26.468c10.16,0,19.625,2.775,19.625,2.775c-0.375,2.721-5.367,5.438-19.554,5.438\n\tc-12.125,0-18.467-2.484-19.541-4.918C-0.127,29.125,9.465,26.468,19.625,26.468z\"/&gt;\n&lt;linearGradient id=\"SVGID_3_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-965.6948\" x2=\"682.0508\" y2=\"-965.6948\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_3_)\" d=\"M19.625,23.313C8.787,23.313,0,21.438,0,19.125v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,21.438,30.464,23.313,19.625,23.313z\"/&gt;\n&lt;linearGradient id=\"SVGID_4_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-959.6948\" x2=\"682.0508\" y2=\"-959.6948\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_4_)\" d=\"M19.625,23.313c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.926-18.396,3.926\n\tc-9.481,0-17.396-1.959-18.396-3.926l-1.229,2C0,21.438,8.787,23.313,19.625,23.313z\"/&gt;\n&lt;path fill=\"#3C89C9\" d=\"M19.476,13.019c10.161,0,19.625,2.775,19.625,2.775c-0.375,2.721-5.367,5.438-19.555,5.438\n\tc-12.125,0-18.467-2.485-19.541-4.918C-0.277,15.674,9.316,13.019,19.476,13.019z\"/&gt;\n&lt;linearGradient id=\"SVGID_5_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-952.4946\" x2=\"682.0508\" y2=\"-952.4946\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#3C89C9\"/&gt;\n\t&lt;stop  offset=\"0.1482\" style=\"stop-color:#60A6DD\"/&gt;\n\t&lt;stop  offset=\"0.3113\" style=\"stop-color:#81C1F0\"/&gt;\n\t&lt;stop  offset=\"0.4476\" style=\"stop-color:#95D1FB\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.636\" style=\"stop-color:#98D4FD\"/&gt;\n\t&lt;stop  offset=\"0.7293\" style=\"stop-color:#8DCAF6\"/&gt;\n\t&lt;stop  offset=\"0.8214\" style=\"stop-color:#79BBEB\"/&gt;\n\t&lt;stop  offset=\"0.912\" style=\"stop-color:#5EA5DC\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_5_)\" d=\"M19.625,10.113C8.787,10.113,0,8.238,0,5.925v10c0,2.313,8.787,4.188,19.625,4.188\n\tc10.839,0,19.625-1.875,19.625-4.188v-10C39.25,8.238,30.464,10.113,19.625,10.113z\"/&gt;\n&lt;linearGradient id=\"SVGID_6_\" gradientUnits=\"userSpaceOnUse\" x1=\"642.8008\" y1=\"-946.4946\" x2=\"682.0508\" y2=\"-946.4946\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"0.0039\" style=\"stop-color:#9DD7FF\"/&gt;\n\t&lt;stop  offset=\"0.2273\" style=\"stop-color:#BDE5FF\"/&gt;\n\t&lt;stop  offset=\"0.4138\" style=\"stop-color:#D1EEFF\"/&gt;\n\t&lt;stop  offset=\"0.5394\" style=\"stop-color:#D9F1FF\"/&gt;\n\t&lt;stop  offset=\"0.6155\" style=\"stop-color:#D5EFFE\"/&gt;\n\t&lt;stop  offset=\"0.6891\" style=\"stop-color:#C9E7FA\"/&gt;\n\t&lt;stop  offset=\"0.7617\" style=\"stop-color:#B6DAF3\"/&gt;\n\t&lt;stop  offset=\"0.8337\" style=\"stop-color:#9AC8EA\"/&gt;\n\t&lt;stop  offset=\"0.9052\" style=\"stop-color:#77B0DD\"/&gt;\n\t&lt;stop  offset=\"0.9754\" style=\"stop-color:#4D94CF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;path fill=\"url(#SVGID_6_)\" d=\"M19.625,10.113c10.839,0,19.625-1.875,19.625-4.188l-1.229-2c0,2.168-8.235,3.926-18.396,3.926\n\tc-9.481,0-17.396-1.959-18.396-3.926L0,5.925C0,8.238,8.787,10.113,19.625,10.113z\"/&gt;\n&lt;linearGradient id=\"SVGID_7_\" gradientUnits=\"userSpaceOnUse\" x1=\"644.0293\" y1=\"-943.4014\" x2=\"680.8223\" y2=\"-943.4014\" gradientTransform=\"matrix(1 0 0 -1 -642.8008 -939.4756)\"&gt;\n\t&lt;stop  offset=\"0\" style=\"stop-color:#9CD7FF\"/&gt;\n\t&lt;stop  offset=\"1\" style=\"stop-color:#3C89C9\"/&gt;\n&lt;/linearGradient&gt;\n&lt;ellipse fill=\"url(#SVGID_7_)\" cx=\"19.625\" cy=\"3.926\" rx=\"18.396\" ry=\"3.926\"/&gt;\n&lt;path opacity=\"0.24\" fill=\"#FFFFFF\" enable-background=\"new    \" d=\"M31.04,45.982c0,0-4.354,0.664-7.29,0.781\n\tc-3.125,0.125-8.952,0-8.952,0l-2.384-10.292l0.044-2.108l-1.251-1.154L9.789,23.024l-0.082-0.119L9.5,20.529l-1.65-1.254\n\tL5.329,8.793c0,0,4.213,0.903,7.234,1.07s8.375,0.25,8.375,0.25l3,9.875l-0.25,1.313l1.063,2.168l2.312,9.645l-0.521,1.416\n\tl1.46,1.834L31.04,45.982z\"/&gt;\n&lt;/svg&gt;\n</y:Resource>\n    </y:Resources>\n  </data>\n</graphml>\n"
  },
  {
    "path": "docs/guide-zh-CN/input-file-upload.md",
    "content": "文件上传\n============\n\n在Yii里上传文件通常使用 [[yii\\web\\UploadedFile]] 类，\n它把每个上传的文件封装成 `UploadedFile` 对象。\n结合 [[yii\\widgets\\ActiveForm]] 和 [models](structure-models.md)，你可以轻松实现安全的上传文件机制。\n\n\n## 创建模型 <span id=\"creating-models\"></span>\n\n和普通的文本输入框类似，当要上传一个文件时，你需要创建一个模型类并且用其中的某个属性来接收上传的文件实例。\n你还需要声明一条验证规则以验证上传的文件。\n举例来讲，\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\nuse yii\\web\\UploadedFile;\n\nclass UploadForm extends Model\n{\n    /**\n     * @var UploadedFile\n     */\n    public $imageFile;\n\n    public function rules()\n    {\n        return [\n            [['imageFile'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg'],\n        ];\n    }\n    \n    public function upload()\n    {\n        if ($this->validate()) {\n            $this->imageFile->saveAs('uploads/' . $this->imageFile->baseName . '.' . $this->imageFile->extension);\n            return true;\n        } else {\n            return false;\n        }\n    }\n}\n```\n\n在以上代码里，`imageFile` 属性用于接收上传的文件实例。它对应一条 `file` 验证规则，\n该规则使用 [[yii\\validators\\FileValidator]] 来确保只上传扩展名为 `png` 或 `jpg` 的文件。\n`upload()` 方法会执行该验证并且把上传的文件保存在服务器上。\n\n通过 `file` 验证器，你可以检查文件的扩展名，大小，MIME类型等等。详情请查阅\n[Core Validatators](tutorial-core-validators.md#file) 章节。\n\n> Tip: 如果你要上传的是一张图片，可以考虑使用 `image` 验证器。\n`image` 验证器是通过 [[yii\\validators\\ImageValidator]] 实现验证的，确保对应的模型属性\n收到的文件是有效的图片文件，然后才保存，或者使用扩展类 [Imagine Extension](https://github.com/yiisoft/yii2-imagine) 进行处理.\n\n\n## 渲染文件输入 <span id=\"rendering-file-input\"></span>\n\n接下来，在视图里创建一个文件输入控件\n\n```php\n<?php\nuse yii\\widgets\\ActiveForm;\n?>\n\n<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?>\n\n    <?= $form->field($model, 'imageFile')->fileInput() ?>\n\n    <button>Submit</button>\n\n<?php ActiveForm::end() ?>\n```\n\n需要注意的是要记得在表单选项里加入 `enctype` 属性以确保文件能被正常上传。\n`fileInput()` 方法会渲染一个 `<input type=\"file\">` 标签，让用户可以选择一个文件上传。\n\n> Tip: 自2.0.8版本开始，当使用文件输入字段时，[[yii\\web\\widgets\\ActiveField::fileInput|fileInput]]\n  会自动向表单添加 `enctype` 选项。\n\n## 视图和模型的连接 <span id=\"wiring-up\"></span>\n\n现在，在控制器方法里编写连接模型和视图的代码以实现文件上传。\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\nuse app\\models\\UploadForm;\nuse yii\\web\\UploadedFile;\n\nclass SiteController extends Controller\n{\n    public function actionUpload()\n    {\n        $model = new UploadForm();\n\n        if (Yii::$app->request->isPost) {\n            $model->imageFile = UploadedFile::getInstance($model, 'imageFile');\n            if ($model->upload()) {\n                // 文件上传成功\n                return;\n            }\n        }\n\n        return $this->render('upload', ['model' => $model]);\n    }\n}\n```\n\n在上面的代码里，当提交表单的时候，[[yii\\web\\UploadedFile::getInstance()]]方法就被调用，\n上传的文件用一个 `UploadedFile` 实例表示。然后，我们依靠模型的验证规则确保上传的文件是有效的，\n并将文件保存在服务器上。\n\n\n## 上传多个文件 <span id=\"uploading-multiple-files\"></span>\n\n将前面所述的代码做一些调整，也可以一次性上传多个文件。\n\n首先你得调整模型类，在 `file` 验证规则里增加一个 `maxFiles` 选项，用以限制一次上传文件的最大数量。\n`upload()`方法也得修改,以便一个一个地保存上传的文件。将 `maxFiles` 设置为 `0` 意味着可以同时上传的文件数量没有限制。\n允许同时上传的文件的最大数量也受到 PHP 指令\n[`max_file_uploads`](https://www.php.net/manual/zh/ini.core.php#ini.max-file-uploads)的限制，\n默认为20。还应该更新 `upload()` 方法以逐个保存上传的文件。\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\nuse yii\\web\\UploadedFile;\n\nclass UploadForm extends Model\n{\n    /**\n     * @var UploadedFile[]\n     */\n    public $imageFiles;\n\n    public function rules()\n    {\n        return [\n            [['imageFiles'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg', 'maxFiles' => 4],\n        ];\n    }\n    \n    public function upload()\n    {\n        if ($this->validate()) { \n            foreach ($this->imageFiles as $file) {\n                $file->saveAs('uploads/' . $file->baseName . '.' . $file->extension);\n            }\n            return true;\n        } else {\n            return false;\n        }\n    }\n}\n```\n\n在视图文件里，你需要把 `multiple` 选项添加到 `fileInput()` 函数调用里，\n这样文件输入控件就可以接收多个文件。\n\n```php\n<?php\nuse yii\\widgets\\ActiveForm;\n?>\n\n<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?>\n\n    <?= $form->field($model, 'imageFiles[]')->fileInput(['multiple' => true, 'accept' => 'image/*']) ?>\n\n    <button>Submit</button>\n\n<?php ActiveForm::end() ?>\n```\n\n最后，在控制器的 action 方法中，你应该调用 `UploadedFile::getInstances()` 而不是 `UploadedFile::getInstance()` 来把\n`UploadedFile` 实例数组赋值给 `UploadForm::imageFiles`。\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\nuse app\\models\\UploadForm;\nuse yii\\web\\UploadedFile;\n\nclass SiteController extends Controller\n{\n    public function actionUpload()\n    {\n        $model = new UploadForm();\n\n        if (Yii::$app->request->isPost) {\n            $model->imageFiles = UploadedFile::getInstances($model, 'imageFiles');\n            if ($model->upload()) {\n                // 文件上传成功\n                return;\n            }\n        }\n\n        return $this->render('upload', ['model' => $model]);\n    }\n}\n```\n"
  },
  {
    "path": "docs/guide-zh-CN/input-form-javascript.md",
    "content": "在客户端扩展 ActiveForm\n=====================\n\n[[yii\\widgets\\ActiveForm]] 小部件附带一组用于客户端验证的 JavaScript 方法。\n它的实现非常灵活，可以让你以不同的方式扩展它。\n下面我们来看描述。\n\n## ActiveForm 事件\n\nActiveForm 触发一系列专用事件。使用类似以下的代码，您可以订阅这些代码\n事件并处理它们：\n\n```javascript\n$('#contact-form').on('beforeSubmit', function (e) {\n\tif (!confirm(\"Everything is correct. Submit?\")) {\n\t\treturn false;\n\t}\n\treturn true;\n});\n```\n\n在下文中，我们将查看可用的事件。\n\n### `beforeValidate`\n\n`beforeValidate` 是在验证整个表单之前触发的。\n\n事件处理程序的签名应该是：\n\n```javascript\nfunction (event, messages, deferreds)\n```\n\n其中\n\n- `event`: 一个 Event 对象。\n- `messages`: 一个关联数组，其中键是属性ID，\n  值是相应属性的错误消息数组。\n- `deferreds`: 一个 Deferred 对象数组。你可以使用 `deferreds.add(callback)`\n  来添加一个新的延迟验证。\n\n如果处理程序返回一个布尔型 `false`，它将在此事件后停止进一步的表单验证。\n结果 `afterValidate` 事件将不会被触发。\n\n### `afterValidate`\n\n`afterValidate` 是在验证整个表单后触发的。\n\n事件处理程序的签名应该是：\n\n```javascript\nfunction (event, messages, errorAttributes)\n```\n\n其中\n\n- `event`: 一个 Event 对象。\n- `messages`: 一个关联数组，其中键是属性ID，\n  值是相应属性的错误消息数组。\n- `errorAttributes`: 一个具有验证错误的属性数组。有关此参数的结构，\n  请参阅 `attributeDefaults`。\n\n### `beforeValidateAttribute`\n\n`beforeValidateAttribute` 事件是在验证属性之前触发的。\n事件处理程序的签名应该是：\n\n```javascript\nfunction (event, attribute, messages, deferreds)\n```\n     \n其中\n\n- `event`: 一个 Event 对象。\n- `attribute`: 要验证的属性。 请参阅这个参数的 `attributeDefaults` \n  结构。\n- `messages`: 可以为其添加指定属性的验证错误消息的数组。\n- `deferreds`: 一个 Deferred 对象数组。你可以使用 `deferreds.add(callback)`\n  来添加一个新的延迟验证。\n\n如果处理程序返回布尔型 `false`，它将停止进一步验证指定的属性。\n结果，`afterValidateAttribute` 事件将不会被触发。\n\n### `afterValidateAttribute`\n\n`afterValidateAttribute` 事件在验证整个表单和每个属性后触发的。\n\n事件处理程序的签名应该是：\n\n```javascript\nfunction (event, attribute, messages)\n```\n\n其中\n\n- `event`: 一个 Event 对象。\n- `attribute`: 该属性会被验证。有关此参数的结构，\n  请参阅 `attributeDefaults`。\n- `messages`: 可以为其添加指定属性的其他验证错误消息的\n  数组。\n\n### `beforeSubmit`\n\n`beforeSubmit` 是在所有验证通过后且提交表单之前触发的。\n\n事件处理程序的签名应该是：\n\n```javascript\nfunction (event)\n```\n\n其中 `event` 是一个 Event 对象。\n\n如果处理返回布尔型“false”，它将停止表单提交。\n\n### `ajaxBeforeSend`\n         \n`ajaxBeforeSend` 事件是在发送用于基于AJAX的验证的AJAX请求之前触发的。\n\n事件处理程序的签名应该是：\n\n```javascript\nfunction (event, jqXHR, settings)\n```\n\n其中\n\n- `event`: 一个 Event 对象。\n- `jqXHR`: 一个 jqXHR 对象\n- `settings`: AJAX 请求的设置\n\n### `ajaxComplete`\n\n`ajaxComplete` 事件是在完成基于 AJAX 验证的 AJAX 请求后触发的。\n\n事件处理程序的签名应该是：\n\n```javascript\nfunction (event, jqXHR, textStatus)\n```\n\nwhere\n\n- `event`: 一个 Event 对象。\n- `jqXHR`: 一个 jqXHR 对象\n- `textStatus`: 请求的状态 (\"success\", \"notmodified\", \"error\", \"timeout\",\n\"abort\", or \"parsererror\")。\n\n## 通过 AJAX 提交表单\n\n虽然可以在客户端或通过 AJAX 请求进行验证，但作为默认的正常请求\n表单提交本身已完成。如果你想通过 AJAX 提交表单，你可以\n通过以下方式处理表单的 `beforeSubmit` 事件做到这一点：\n\n```javascript\nvar $form = $('#formId');\n$form.on('beforeSubmit', function() {\n    var data = $form.serialize();\n    $.ajax({\n        url: $form.attr('action'),\n        type: 'POST',\n        data: data,\n        success: function (data) {\n            // 执行成功\n        },\n        error: function(jqXHR, errMsg) {\n            alert(errMsg);\n        }\n     });\n     return false; // 防止默认提交\n});\n```\n\n要了解更多关于 jQuery `ajax()` 函数的信息，请参阅 [jQuery 文档](https://api.jquery.com/jQuery.ajax/)。\n\n\n## 动态添加字段\n\n在现代 Web 应用程序中，您经常需要在向用户显示表单后更改表单。\n这可以例如在点击“加号”图标之后添加新的字段。\n要为这些字段启用客户端验证，他们必须注册 ActiveForm JavaScript 插件。\n\n您必须自行添加一个字段，然后将其添加到验证列表中：\n\n```javascript\n$('#contact-form').yiiActiveForm('add', {\n    id: 'address',\n    name: 'address',\n    container: '.field-address',\n    input: '#address',\n    error: '.help-block',\n    validate:  function (attribute, value, messages, deferred, $form) {\n        yii.validation.required(value, messages, {message: \"Validation Message Here\"});\n    }\n});\n```\n\n要从验证列表中删除一个字段，使它不被验证，您可以执行以下操作：\n\n```javascript\n$('#contact-form').yiiActiveForm('remove', 'address');\n```\n"
  },
  {
    "path": "docs/guide-zh-CN/input-forms.md",
    "content": "创建表单\n========\n\n基于活动记录（ActiveRecord）的表单：ActiveForm\n-----------------------\n在yii中使用表单的主要方式是通过 [[yii\\widgets\\ActiveForm]]。\n当某个表单是基于一个模型时，应该首选这种方式。\n此外，在 [[yii\\helpers\\Html]] 中有很多实用的方法为表单添加按钮和帮助文档。\n\n在客户端显示的表单，大多数情况下都有一个相应的[模型](structure-models.md)，\n用来在服务器上验证其输入的数据（可在[输入验证](input-validation.md)一节获取关于验证的细节）。\n当创建一个基于模型的表单时，第一步是定义模型本身。该模型可以是一个基于[活动记录](db-active-record.md)的类，\n表示数据库中的数据，也可以是一个基于通用模型的类（继承自[[yii\\base\\Model]]），\n来获取任意的输入数据，如登录表单。\n\n> Tip: 如果一个表单的输入域与数据库的字段不匹配，或者它存在只适用于它的特殊的格式或者方法，\n> 则最好为它创建一个单独的继承自 [[yii\\base\\Model]] 的模型。\n\n在接下来的例子中，我们展示了通用模型如何用于登录表单：\n\n```php\n<?php\n\nclass LoginForm extends \\yii\\base\\Model\n{\n    public $username;\n    public $password;\n\n    public function rules()\n    {\n        return [\n            // 在这里定义验证规则\n        ];\n    }\n}\n```\n\n在控制器中，我们将传递一个模型的实例到视图，其中 [[yii\\widgets\\ActiveForm|ActiveForm]] \n小部件用来显示表单：\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n$form = ActiveForm::begin([\n    'id' => 'login-form',\n    'options' => ['class' => 'form-horizontal'],\n]) ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n\n    <div class=\"form-group\">\n        <div class=\"col-lg-offset-1 col-lg-11\">\n            <?= Html::submitButton('Login', ['class' => 'btn btn-primary']) ?>\n        </div>\n    </div>\n<?php ActiveForm::end() ?>\n```\n\n### 用 `begin()` 和 `end()` 包裹 <span id=\"wrapping-with-begin-and-end\"></span>\n在上面的代码中，[[yii\\widgets\\ActiveForm::begin()|ActiveForm::begin()]] 不仅创建了一个表单实例，同时也标志着表单的开始。\n放在 [[yii\\widgets\\ActiveForm::begin()|ActiveForm::begin()]] 与 [[yii\\widgets\\ActiveForm::end()|ActiveForm::end()]]\n之间的所有内容都被包裹在 HTML 的 `<form>` 标签中。\n与任何小部件一样，你可以指定一些选项，通过传递数组到 `begin` 方法中来配置该小部件。在这种情况下，\n一个额外的 CSS 类和 ID 会在 `<form>` 标签中使用。要查看所有可用的选项，\n请参阅 API 文档的 [[yii\\widgets\\ActiveForm]]。\n\n### ActiveField <span id=\"activefield\"></span>\n为了在表单中创建表单元素与元素的标签，以及任何适用的 JavaScript 验证，[[yii\\widgets\\ActiveForm::field()|ActiveForm::field()]]\n方法在调用时，会返回一个 [[yii\\widgets\\ActiveField]] 的实例。\n直接输出该方法时，结果是一个普通的（文本）输入。要自定义输出，可以附加上 [[yii\\widgets\\ActiveField|ActiveField]] \n的其它方法来一起调用：\n\n```php\n// 一个密码输入框\n<?= $form->field($model, 'password')->passwordInput() ?>\n// 增加一个提示标签\n<?= $form->field($model, 'username')->textInput()->hint('Please enter your name')->label('Name') ?>\n// 创建一个 HTML5 邮箱输入框\n<?= $form->field($model, 'email')->input('email') ?>\n```\n\n它会通过在 [[yii\\widgets\\ActiveField::$template|template]] 中定义的表单字段来创建 `<label>`，`<input>` 以及其它的标签。\ninput 输入框的 name 属性会自动地根据 [[yii\\base\\Model::formName()|form name]] 以及属性名来创建。\n例如，对于在上面的例子中 `username` 输入字段的 name 属性将是 `LoginForm[username]`。\n这种命名规则使所有属性的数组的登录表单在服务器端的 `$_POST['LoginForm']` 数组中是可用的。\n\n> Tip: 如果在表单中只有一个模型并且想要简化输入名称，则可以通过覆盖模型的\n> [[yii\\base\\Model::formName()|formName()]] 方法来返回一个空字符串。\n> 这对于 [GridView](output-data-widgets.md#grid-view) 中使用的过滤器模型来创建更好的URL很有用。\n\n指定模型的属性可以以更复杂的方式来完成。例如，当上传时，多个文件\n或选择多个项目的属性，可能需要一个数组值，你可以通过附加 `[]` 来\n指定它的属性名称：\n\n```php\n// 允许多个文件被上传：\necho $form->field($model, 'uploadFile[]')->fileInput(['multiple'=>'multiple']);\n\n// 允许进行选择多个项目：\necho $form->field($model, 'items[]')->checkboxList(['a' => 'Item A', 'b' => 'Item B', 'c' => 'Item C']);\n```\n\n命名表单元素，如提交按钮时要小心。在 [jQuery 文档](https://api.jquery.com/submit/) \n中有一些保留的名称，可能会导致冲突：\n\n> 表单和它们的子元素不应该使用与表单的属性冲突的 input name 或 id，\n> 例如 `submit`，`length`，或者 `method`。\n> 要检查你的标签是否存在这些问题，一个完整的规则列表详见 [DOMLint](https://kangax.github.io/domlint/)。\n\n额外的 HTML 标签可以使用纯 HTML 或者 [[yii\\helpers\\Html|Html]]-辅助类中的方法来添加到表单中，就如上面例子中的\n[[yii\\helpers\\Html::submitButton()|Html::submitButton()]]。\n\n\n> Tip: 如果你正在你的应用程序中使用 Twitter Bootstrap CSS 你可以使用[[yii\\bootstrap\\ActiveForm]] \n> 来代替 [[yii\\widgets\\ActiveForm]]。\n> 前者继承自后者并在生成表单字段时使用 Bootstrap 特有的样式。\n\n\n> Tip: 为了设计带星号的表单字段，你可以使用下面的 CSS：\n>\n> ```css\n> div.required label:after {\n>     content: \" *\";\n>     color: red;\n> }\n> ```\n\n创建下拉列表 <span id=\"creating-activeform-lists\"></span>\n------------\n\n有三种类型的列表：\n* 下拉列表\n* 单选列表\n* 复选框列表\n\n要创建一个列表，你必须准备这些项目。可以手动完成：\n\n```php\n$items = [\n    1 => 'item 1', \n    2 => 'item 2'\n]\n```\n\n或从 DB 中检索：\n\n```php\n$items = Category::find()\n        ->select(['label'])\n        ->indexBy('id')\n        ->column();\n```\n\n这些 `$items` 必须由不同的列表小部件处理。\n表单域（和当前活动项目）的值将由\n`$model` 属性的当前值自动设置。\n\n#### 创建一个下拉列表 <span id=\"creating-activeform-dropdownlist\"></span>\n\n我们可以使用 ActiveField [[\\yii\\widgets\\ActiveField::dropDownList()]] 方法创建一个下拉列表：\n\n```php\n/** @var \\yii\\widgets\\ActiveForm $form */\n\necho $form->field($model, 'category')->dropdownList([\n        1 => 'item 1', \n        2 => 'item 2'\n    ],\n    ['prompt'=>'Select Category']\n);\n```\n\n#### 创建一个单选列表 <span id=\"creating-activeform-radioList\"></span>\n\n我们可以使用 ActiveField [[\\yii\\widgets\\ActiveField::radioList()]] 方法创建一个单选列表：\n\n```php\n/** @var \\yii\\widgets\\ActiveForm $form */\n\necho $form->field($model, 'category')->radioList([\n    1 => 'radio 1', \n    2 => 'radio 2'\n]);\n```\n\n#### 创建一个复选框列表 <span id=\"creating-activeform-checkboxList\"></span>\n\n我们可以使用 ActiveField [[\\yii\\widgets\\ActiveField::checkboxList()]] 方法创建一个复选框列表：\n\n```php\n/** @var \\yii\\widgets\\ActiveForm $form */\n\necho $form->field($model, 'category')->checkboxList([\n    1 => 'checkbox 1', \n    2 => 'checkbox 2'\n]);\n```\n\n\n与 Pjax 一起工作 <span id=\"working-with-pjax\"></span>\n-----------------------\n\n[[yii\\widgets\\Pjax|Pjax]] 小部件允许您更新某个部分\n而不是重新加载整个页面。\n您可以使用它来仅更新表单并在提交后更换其内容。\n\n你可以配置 [[yii\\widgets\\Pjax::$formSelector|$formSelector]]\n来指定表单提交可能会触发 pjax。如果没有设置，所有封装 Pjax 内容的 `data-pjax`\n属性的表单都会触发 pjax 请求。\n\n```php\nuse yii\\widgets\\Pjax;\nuse yii\\widgets\\ActiveForm;\n\nPjax::begin([\n    // Pjax 配置项\n]);\n    $form = ActiveForm::begin([\n        'options' => ['data' => ['pjax' => true]],\n        // more ActiveForm options\n    ]);\n\n        // ActiveForm content\n\n    ActiveForm::end();\nPjax::end();\n```\n> Tip: 请小心处理 [[yii\\widgets\\Pjax|Pjax]] 小部件中的链接，\n> 因为响应也将在小部件内呈现。为了防止这种情况，\n> 使用 `data-pjax=\"0\"` HTML 属性。\n\n#### 提交按钮和文件上传中的值\n\n在处理 [文件](https://github.com/jquery/jquery/issues/2321) 和\n[提交按钮值](https://github.com/jquery/jquery/issues/2321)\n时使用 `jQuery.serializeArray()` \n有已知的问题，这将不会被解决，而是被弃用，\n以支持 HTML5 中引入的 FormData 类。\n\n这意味着对 ajax 或使用  [[yii\\widgets\\Pjax|Pjax]]\n小部件的文件和提交按钮值的唯一官方支持取决于\n`FormData` 类的\n[浏览器支持](https://developer.mozilla.org/zh-CN/docs/Web/API/FormData#%E6%B5%8F%E8%A7%88%E5%99%A8%E5%85%BC%E5%AE%B9%E6%80%A7)。\n\n延伸阅读 <span id=\"further-reading\"></span>\n---------------\n\n下一节 [输入验证](input-validation.md) 处理提交的表单数据的服务器端验证，以及 ajax 和客户端验证。\n\n要学会有关表格的更复杂的用法，你可以查看以下几节：\n\n- [收集列表输入](input-tabular-input.md) 同一种类型的多个模型的采集数据。\n- [多模型同时输入](input-multiple-models.md) 在同一窗口中处理多个不同的模型。\n- [文件上传](input-file-upload.md) 如何使用表格来上传文件。\n"
  },
  {
    "path": "docs/guide-zh-CN/input-multiple-models.md",
    "content": "多模型的复合表单\n==================================\n\n当需要处理复杂数据，很可能你需要使用多个不同的模型来收集用户提交的数据。\n举例来说，假设用户登录信息保存在 `user` 表，但是用户基本信息保存在 `profile` 表，\n你可能需要同时使用 `User` 模型和 `Profile` 模型来获取用户登录信息和基本信息。\n使用 Yii 提供的模型和表单支持，解决这样的问题和处理单一模型并不会有太大的区别。\n\n\n下面，我们将为你展示怎样创建一个表单并同时处理 `User` 和 `Profile` 这两个模型。\n\n\n首先，控制器中收集用户提交数据的动作(action)可以按照下面写的这样，\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\base\\Model;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\nuse app\\models\\User;\nuse app\\models\\Profile;\n\nclass UserController extends Controller\n{\n    public function actionUpdate($id)\n    {\n        $user = User::findOne($id);\n        if (!$user) {\n            throw new NotFoundHttpException(\"没有找到用户登录信息。\");\n        }\n        \n        $profile = Profile::findOne($user->profile_id);\n        \n        if (!$profile) {\n            throw new NotFoundHttpException(\"没有找到用户基本信息。\");\n        }\n        \n        $user->scenario = 'update';\n        $profile->scenario = 'update';\n        \n        if ($user->load(Yii::$app->request->post()) && $profile->load(Yii::$app->request->post())) {\n            $isValid = $user->validate();\n            $isValid = $profile->validate() && $isValid;\n            if ($isValid) {\n                $user->save(false);\n                $profile->save(false);\n                return $this->redirect(['user/view', 'id' => $id]);\n            }\n        }\n        \n        return $this->render('update', [\n            'user' => $user,\n            'profile' => $profile,\n        ]);\n    }\n}\n```\n\n在 `update` 动作中，我们首先从数据库中获取需要更新的 `$user` 和 `$profile` 这两个模型。\n我们可以调用 [[yii\\base\\Model::load()]] 方法把用户提交数据填充到两个模型中。\n如果加载成功，验证两个表单并保存 &mdash; 请注意我们使用了 `save(false)` 方法用来忽略内部保存时的二次验证，\n因为用户输入的数据已经手动验证过了。\n如果填充数据失败，直接显示下面的 `update` 视图内容：\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n$form = ActiveForm::begin([\n    'id' => 'user-update-form',\n    'options' => ['class' => 'form-horizontal'],\n]) ?>\n    <?= $form->field($user, 'username') ?>\n\n    ...other input fields...\n    \n    <?= $form->field($profile, 'website') ?>\n\n    <?= Html::submitButton('更新数据', ['class' => 'btn btn-primary']) ?>\n<?php ActiveForm::end() ?>\n```\n\n你可以看到，在 `update` 视图中，我们同时显示了两个模型 `$user` 和 `$profile` 的属性的输入栏。\n"
  },
  {
    "path": "docs/guide-zh-CN/input-tabular-input.md",
    "content": "收集列表输入\n=============\n\n有时你需要在一个表单中以单一的形式处理多个模型。例如，有多个设置，\n每个设置存储为一个 name-value，并通过 `Setting` [活动记录](db-active-record.md)\n模型来表示。这种形式也常被称为“列表输入”。与此相反，\n处理不同模型的不同类型，在\n[多模型同时输入](input-multiple-models.md)章节中介绍。\n\n下面展示了如何在 Yii 中收集列表输入。\n\n在三种不同的情况下，所需处理的略有不同：\n- 从数据库中更新一组固定的记录\n- 创建一个动态的新记录集\n- 更新、创建和删除一页记录\n\n与之前介绍的单一模型表单相反，我们现在用的是一个数组类的模型。这个数组将\n每个模型传递到视图并以一种类似于表格的方式来显示表单字段。\n我们使用 [[yii\\base\\Model]] 助手类方法来一次性地加载和验证多模型数据：\n\n- [[yii\\base\\Model::loadMultiple()|Model::loadMultiple()]] 将数据加载到一个数组中。\n- [[yii\\base\\Model::validateMultiple()|Model::validateMultiple()]] 验证一系列模型。\n\n### 更新一组固定的记录\n\n让我们从控制器的动作开始：\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\base\\Model;\nuse yii\\web\\Controller;\nuse app\\models\\Setting;\n\nclass SettingsController extends Controller\n{\n    // ...\n\n    public function actionUpdate()\n    {\n        $settings = Setting::find()->indexBy('id')->all();\n\n        if (Model::loadMultiple($settings, Yii::$app->request->post()) && Model::validateMultiple($settings)) {\n            foreach ($settings as $setting) {\n                $setting->save(false);\n            }\n            return $this->redirect('index');\n        }\n\n        return $this->render('update', ['settings' => $settings]);\n    }\n}\n```\n\n在上面的代码中，当用模型来从数据库获取数据时，我们使用 [[yii\\db\\ActiveQuery::indexBy()|indexBy()]] \n来让模型的主键成为一个数组的索引。其中 [[yii\\base\\Model::loadMultiple()|Model::loadMultiple()]] \n用于接收以 POST 方式提交的表单数据并填充多个模型，\n[[yii\\base\\Model::validateMultiple()|Model::validateMultiple()]] 一次验证多个模型。\n正如我们之前验证的模型，使用了 `validateMultiple()`，现在通过传递 `false` \n作为 [[yii\\db\\ActiveRecord::save()|save()]]的一个参数使其不会重复验证两次。\n\n现在在 `update` 视图的表单：\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n$form = ActiveForm::begin();\n\nforeach ($settings as $index => $setting) {\n    echo $form->field($setting, \"[$index]value\")->label($setting->name);\n}\n\nActiveForm::end();\n```\n\n在这里，我们为每个设置渲染了名字和一个带值的输入。重要的是给 input name 增加添加适当的索引，\n因为这是由 [[yii\\base\\Model::loadMultiple()|Model::loadMultiple()]] 来决定以哪些值来填补哪个模型。\n\n### 创建一组动态的新记录\n\n创造新的记录与修改记录很相似，除部分实例化模型不同之外：\n\n```php\npublic function actionCreate()\n{\n    $count = count(Yii::$app->request->post('Setting', []));\n    $settings = [new Setting()];\n    for($i = 1; $i < $count; $i++) {\n        $settings[] = new Setting();\n    }\n\n    // ...\n}\n```\n\n在这里，我们创建了一个初始的 `$settings` 数组包含一个默认的模型，所以始终至少有一个文本字段是可见的。\n此外，我们为每个可能会收到的输入行添加更多的模型。\n\n在视图中，可以使用 JavaScript 来动态地添加新的输入行。\n\n### 把更新，创建和删除结合在一个页面上\n\n> Note: 此章节正在开发中。\n>\n> 还没有内容。\n\nTBD\n"
  },
  {
    "path": "docs/guide-zh-CN/input-validation.md",
    "content": "输入验证\n================\n\n根据经验，您永远不应该信任从最终用户收到的数据，\n并且应该在充分利用之前对其进行验证。\n\n要给 [model](structure-models.md) 填充其所需的用户输入数据，你可以调用 [[yii\\base\\Model::validate()]] \n方法验证它们。该方法会返回一个布尔值，指明是否通过验证。若没有通过，你能通过 [[yii\\base\\Model::errors]] \n属性获取相应的报错信息。比如，\n\n```php\n$model = new \\app\\models\\ContactForm();\n\n// 根据用户的输入填充到模型的属性中\n$model->load(\\Yii::$app->request->post());\n// 等效于下面这样：\n// $model->attributes = \\Yii::$app->request->post('ContactForm');\n\nif ($model->validate()) {\n    // 所有输入通过验证\n} else {\n    // 验证失败: $errors 是一个包含错误信息的数组\n    $errors = $model->errors;\n}\n```\n\n\n## 声明规则（Rules） <span id=\"declaring-rules\"></span>\n\n要让 `validate()` 方法起作用，你需要声明与需验证模型特性相关的验证规则。\n为此，需要重写 [[yii\\base\\Model::rules()]] 方法。下面的例子展示了如何\n声明用于验证 `ContactForm` 模型的相关验证规则：\n\n```php\npublic function rules()\n{\n    return [\n        // name，email，subject 和 body 特性是 `require`（必填）的\n        [['name', 'email', 'subject', 'body'], 'required'],\n\n        // email 特性必须是一个有效的 email 地址\n        ['email', 'email'],\n    ];\n}\n```\n\n[[yii\\base\\Model::rules()|rules()]] 方法应返回一个由规则所组成的数组，\n每一个规则都呈现为以下这类格式的小数组：\n\n```php\n[\n    // 必填，指出哪个属性由这个规则验证。\n    // 如果是一个属性，可以直接写属性的名字\n    // 不必写在数组里。\n    ['attribute1', 'attribute2', ...],\n    \n    // 必填，指出规则的类型。\n    // 可以是一个类名，验证器别名，或者是一个验证器方法名\n    'validator',\n\n    // 可选，指出这个规则在哪个（些）场景下生效\n    // 如果没有给出，意味着这个规则在所有场景都生效\n    // 如果你希望在所有场景下生效，但是在排除的场景里不生效。\n    // 可以配置 \"except\" 选项\n    'on' => ['scenario1', 'scenario2', ...],\n\n    // 可选，为验证器对象指定额外的配置\n    'property1' => 'value1', 'property2' => 'value2', ...\n]\n```\n\n对于每个规则，你至少需要指定该规则适用于哪些特性，以及本规则的类型是什么。\n你可以指定以下的规则类型之一：\n\n* 核心验证器的昵称，比如 `required`、`in`、`date`，等等。请参考\n  [核心验证器](tutorial-core-validators.md)章节查看完整的核心验证器列表。\n* 模型类中的某个验证方法的名称，或者一个匿名方法。\n  请参考[行内验证器](#inline-validators)小节了解更多。\n* 验证器类的名称。\n  请参考[独立验证器](#standalone-validators)小节了解更多。\n\n一个规则可用于验证一个或多个模型特性，且一个特性可以被一个或多个规则所验证。\n一个规则可以施用于特定[场景（scenario）](structure-models.md#scenarios)，只\n要指定 `on` 选项。如果你不指定 `on` 选项，那么该规则会适配于所有场景。\n\n当调用 `validate()` 方法时，它将运行以下几个具体的验证步骤：\n\n1. 检查从声明自 [[yii\\base\\Model::scenarios()]] 方法的场景中所挑选出的当前[[yii\\base\\Model::scenario|场景]]的信息，\n   从而确定出那些特性需要被验证。这些特性被称为激活特性。\n2. 检查从声明自 [[yii\\base\\Model::rules()]] 方法的众多规则中所挑选出的适用于当前[[yii\\base\\Model::scenario|场景]]的规则，\n   从而确定出需要验证哪些规则。这些规则被称为激活规则。\n3. 用每个激活规则去验证每个\n   与之关联的激活特性。\n\n基于以上验证步骤，有且仅有声明在 `scenarios()` \n方法里的激活特性，且它还必须与一或多个声明自\n`rules()` 里的激活规则相关联才会被验证。\n\n> Note: 可以方便地给规则命名比如：\n>\n> ```php\n> public function rules()\n> {\n>     return [\n>         // ...\n>         'password' => [['password'], 'string', 'max' => 60],\n>     ];\n> }\n> ```\n>\n> 你可以在子模型中使用：\n>\n> ```php\n> public function rules()\n> {\n>     $rules = parent::rules();\n>     unset($rules['password']);\n>     return $rules;\n> }\n\n\n### 自定义错误信息 <span id=\"customizing-error-messages\"></span>\n\n大多数的验证器都有默认的错误信息，当模型的某个特性验证失败的时候，该错误信息会被返回给模型。\n比如，用 [[yii\\validators\\RequiredValidator|required]] 验证器的规则检验 `username` \n特性失败的话，会返还给模型 \"Username cannot be blank.\" 信息。\n\n你可以通过在声明规则的时候同时指定 `message` 属性，\n来定制某个规则的错误信息，比如这样：\n\n```php\npublic function rules()\n{\n    return [\n        ['username', 'required', 'message' => 'Please choose a username.'],\n    ];\n}\n```\n\n一些验证器还支持用于针对不同原因的验证失败返回更加准确的额外错误信息。比如，[[yii\\validators\\NumberValidator|number]] \n验证器就支持 [[yii\\validators\\NumberValidator::tooBig|tooBig]] 和 [[yii\\validators\\NumberValidator::tooSmall|tooSmall]] \n两种错误消息用于分别返回输入值是太大还是太小。你也可以像配置验证器的\n其他属性一样配置它们俩各自的错误信息。\n\n\n\n### 验证事件 <span id=\"validation-events\"></span>\n\n当调用 [[yii\\base\\Model::validate()]] 方法的过程里，它同时会调用两个特殊的方法，\n把它们重写掉可以实现自定义验证过程的目的：\n\n* [[yii\\base\\Model::beforeValidate()]]：在默认的实现中会触发 [[yii\\base\\Model::EVENT_BEFORE_VALIDATE]] 事件。\n  你可以重写该方法或者响应此事件，来在验证开始之前，先进行一些预处理的工作。\n （比如，标准化数据输入）该方法应该返回一个布尔值，用于标明验证是否通过。\n* [[yii\\base\\Model::afterValidate()]]：在默认的实现中会触发 [[yii\\base\\Model::EVENT_AFTER_VALIDATE]] 事件。\n  你可以重写该方法或者响应此事件，来在验证结束之后，\n  再进行一些收尾的工作。\n\n\n\n### 条件式验证 <span id=\"conditional-validation\"></span>\n\n若要只在某些条件满足时，才验证相关特性，比如：是否验证某特性取决于另一特性的值，\n你可以通过[[yii\\validators\\Validator::when|when]] \n属性来定义相关条件。举例而言，\n\n```php\n    ['state', 'required', 'when' => function($model) {\n        return $model->country == 'USA';\n    }]\n```\n\n[[yii\\validators\\Validator::when|when]] 属性会读入一个如下所示结构的 PHP callable 函数对象：\n\n```php\n/**\n * @param Model $model 要验证的模型对象\n * @param string $attribute 待测特性名\n * @return bool 返回是否启用该规则\n */\nfunction ($model, $attribute)\n```\n\n若你需要支持客户端的条件验证，你应该配置[[yii\\validators\\Validator::whenClient|whenClient]] 属性，\n它会读入一条包含有 JavaScript 函数的字符串。\n这个函数将被用于确定该客户端验证规则是否被启用。比如，\n\n```php\n    ['state', 'required', 'when' => function ($model) {\n        return $model->country == 'USA';\n    }, 'whenClient' => \"function (attribute, value) {\n        return $('#country').value == 'USA';\n    }\"]\n```\n\n\n### 数据预处理 <span id=\"data-filtering\"></span>\n\n用户输入经常需要进行数据过滤，或者叫预处理。比如你可能会需要先去掉 `username` 输入的收尾空格。\n你可以通过使用验证规则来实现此目的。\n\n下面的例子展示了如何去掉输入信息的首尾空格，并将空输入返回为 null。具体方法为通过调用 \n[trim](tutorial-core-validators.md#trim) 和 [default](tutorial-core-validators.md#default) 核心验证器：\n\n```php\nreturn [\n    [['username', 'email'], 'trim'],\n    [['username', 'email'], 'default'],\n];\n```\n\n也还可以用更加通用的 [filter（滤镜）](tutorial-core-validators.md#filter) \n核心验证器来执行更加复杂的数据过滤。\n\n如你所见，这些验证规则并不真的对输入数据进行任何验证。而是，对输入数据进行一些处理，\n然后把它们存回当前被验证的模型特性。\n\n下面的代码示例展示了对用户输入的完整处理，\n这将确保只将整数值存储在一个属性中：\n\n```php\n['age', 'trim'],\n['age', 'default', 'value' => null],\n['age', 'integer', 'integerOnly' => true, 'min' => 0],\n['age', 'filter', 'filter' => 'intval', 'skipOnEmpty' => true],\n```\n\n以上代码将对输入执行以下操作：\n\n1. 从输入值中去除前后空白。\n2. 确保空输入在数据库中存储为 `null`；我们区分 `未设置` 值和实际值为 `0` 之间的区别。\n   如果值不允许为 `null`，则可以在此处设置另一个默认值。\n3. 如果该值不为空，则验证该值是否为大于0的整数。大多数验证器的\n   [[yii\\validators\\Validator::$skipOnEmpty|$skipOnEmpty]] 属性都被设置为`true`。\n4. 确保该值为整数类型，例如将字符串 `'42'` 转换为整数 `42`。在这里，我们将 \n[[yii\\validators\\FilterValidator::$skipOnEmpty|$skipOnEmpty]] 设置为 `true`，默认情况下，在 \n[[yii\\validators\\FilterValidator|filter]] 验证器里这个属性是 `false`。\n\n### 处理空输入 <span id=\"handling-empty-inputs\"></span>\n\n当输入数据是通过 HTML 表单，你经常会需要给空的输入项赋默认值。你可以通过调整\n[default](tutorial-core-validators.md#default) 验证器来实现这一点。举例来说，\n\n```php\nreturn [\n    // 若 \"username\" 和 \"email\" 为空，则设为 null\n    [['username', 'email'], 'default'],\n\n    // 若 \"level\" 为空，则设其为 1\n    ['level', 'default', 'value' => 1],\n];\n```\n\n默认情况下，当输入项为空字符串，空数组，或 null 时，会被视为“空值”。\n你也可以通过配置[[yii\\validators\\Validator::isEmpty]] \n属性来自定义空值的判定规则。比如，\n\n```php\n    ['agree', 'required', 'isEmpty' => function ($value) {\n        return empty($value);\n    }]\n```\n\n> Note: 对于绝大多数验证器而言，若其 [[yii\\base\\Validator::skipOnEmpty]] 属性为默认值\n  true，则它们不会对空值进行任何处理。也就是当他们的关联特性接收到空值时，相关验证会被直接略过。在\n  [核心验证器](tutorial-core-validators.md) 之中，只有 `captcha`（验证码），`default`（默认值），\n `filter`（滤镜），`required`（必填），以及 `trim`（去首尾空格），这几个验证器会处理空输入。\n\n\n## 临时验证 <span id=\"ad-hoc-validation\"></span>\n\n有时，你需要对某些没有绑定任何模型类的值进行 **临时验证**。\n\n若你只需要进行一种类型的验证 (e.g. 验证邮箱地址)，你可以调用所需验证器的\n[[yii\\validators\\Validator::validate()|validate()]] 方法。像这样：\n\n```php\n$email = 'test@example.com';\n$validator = new yii\\validators\\EmailValidator();\n\nif ($validator->validate($email, $error)) {\n    echo '有效的 Email 地址。';\n} else {\n    echo $error;\n}\n```\n\n> Note: 不是所有的验证器都支持这种形式的验证。比如 [unique（唯一性）](tutorial-core-validators.md#unique) 核心验证器就就是一个例子，\n  它的设计初衷就是只作用于模型类内部的。\n\n若你需要针对一系列值执行多项验证，你可以使用 [[yii\\base\\DynamicModel]]\n。它支持即时添加特性和验证规则的定义。它的使用规则是这样的：\n\n```php\npublic function actionSearch($name, $email)\n{\n    $model = DynamicModel::validateData(compact('name', 'email'), [\n        [['name', 'email'], 'string', 'max' => 128],\n        ['email', 'email'],\n    ]);\n\n    if ($model->hasErrors()) {\n        // 验证失败\n    } else {\n        // 验证成功\n    }\n}\n```\n\n[[yii\\base\\DynamicModel::validateData()]] 方法会创建一个 `DynamicModel` 的实例对象，\n并通过给定数据定义模型特性（以 `name` 和 `email` 为例），\n之后用给定规则调用 [[yii\\base\\Model::validate()]] 方法。\n\n除此之外呢，你也可以用如下的更加“传统”的语法来执行临时数据验证：\n\n```php\npublic function actionSearch($name, $email)\n{\n    $model = new DynamicModel(compact('name', 'email'));\n    $model->addRule(['name', 'email'], 'string', ['max' => 128])\n        ->addRule('email', 'email')\n        ->validate();\n\n    if ($model->hasErrors()) {\n        // 验证失败\n    } else {\n        // 验证成功\n    }\n}\n```\n\n验证之后你可以通过调用 [[yii\\base\\DynamicModel::hasErrors()|hasErrors()]]\n方法来检查验证通过与否，并通过 [[yii\\base\\DynamicModel::errors|errors]]\n属性获得验证的错误信息，过程与普通模型类一致。\n你也可以访问模型对象内定义的动态特性，就像：\n`$model->name` 和 `$model->email`。\n\n\n## 创建验证器（Validators） <span id=\"creating-validators\"></span>\n\n除了使用 Yii 的发布版里所包含的[核心验证器](tutorial-core-validators.md)之外，你也可以创建你自己的验证器。\n自定义的验证器可以是**行内验证器**，也可以是**独立验证器**。\n\n\n### 行内验证器（Inline Validators） <span id=\"inline-validators\"></span>\n\n行内验证器是一种以模型方法或匿名函数的形式定义的验证器。\n这些方法/函数的结构如下：\n\n```php\n/**\n * @param string $attribute 当前被验证的特性\n * @param array $params 以名-值对形式提供的额外参数\n * @param \\yii\\validators\\InlineValidator $validator 相关的 InlineValidator 实例。\n * 此参数自版本 2.0.11 起可用。\n */\nfunction ($attribute, $params)\n```\n\n若某特性的验证失败了，该方法/函数应该调用 [[yii\\base\\Model::addError()]] 保存错误信息到模型内。\n这样这些错误就能在之后的操作中，被读取并展现给终端用户。\n\n下面是一些例子：\n\n```php\nuse yii\\base\\Model;\n\nclass MyForm extends Model\n{\n    public $country;\n    public $token;\n\n    public function rules()\n    {\n        return [\n            // 定义为模型方法 validateCountry() 的行内验证器\n            ['country', 'validateCountry'],\n\n            // 定义为匿名函数的行内验证器\n            ['token', function ($attribute, $params) {\n                if (!ctype_alnum($this->$attribute)) {\n                    $this->addError($attribute, 'The token must contain letters or digits.');\n                }\n            }],\n        ];\n    }\n\n    public function validateCountry($attribute, $params)\n    {\n        if (!in_array($this->$attribute, ['USA', 'Web'])) {\n            $this->addError($attribute, 'The country must be either \"USA\" or \"Web\".');\n        }\n    }\n}\n```\n\n> Note: 从 2.0.11 版本开始，你可以用 [[yii\\validators\\InlineValidator::addError()]] 方法添加错误信息到模型里。用这种方法\n> 的话，错误信息可以通过 [[yii\\i18n\\I18N::format()]] 格式化。 你还可以在错误信息里分别用 `{attribute}` 和 `{value}` 来引用\n> 属性的名字（不必手动去写）和属性的值：\n\n> ```php\n> $validator->addError($this, $attribute, 'The value \"{value}\" is not acceptable for {attribute}.');\n> ```\n\n> Note: 缺省状态下，行内验证器不会在关联特性的输入值为空或该特性已经在其他验证中失败的情况下起效。\n  若你想要确保该验证器始终启用的话，你可以在定义规则时，酌情将 \n  [[yii\\validators\\Validator::skipOnEmpty|skipOnEmpty]] 以及\n  [[yii\\validators\\Validator::skipOnError|skipOnError]]属性设为 false，比如，\n>\n> ```php\n> [\n>     ['country', 'validateCountry', 'skipOnEmpty' => false, 'skipOnError' => false],\n> ]\n> ```\n\n\n### 独立验证器（Standalone Validators） <span id=\"standalone-validators\"></span>\n\n独立验证器是继承自 [[yii\\validators\\Validator]] 或其子类的类。你可以通过重写\n[[yii\\validators\\Validator::validateAttribute()]] 来实现它的验证规则。若特性验证失败，可以调用\n[[yii\\base\\Model::addError()]] 以保存错误信息到模型内，\n操作与 [inline validators](#inline-validators) 所需操作完全一样。比如，\n\n\n比如上述行内验证器也可以转移到新的验证类 [[components/validators/CountryValidator]]。\n这种情况下，我们可以用 [[yii\\validators\\Validator::addError()]] 来给模型设置自定义的错误信息。\n\n```php\nnamespace app\\components;\n\nuse yii\\validators\\Validator;\n\nclass CountryValidator extends Validator\n{\n    public function validateAttribute($model, $attribute)\n    {\n        if (!in_array($model->$attribute, ['USA', 'Web'])) {\n            $this->addError($model, $attribute, 'The country must be either \"USA\" or \"Web\".');\n        }\n    }\n}\n```\n\n如果你希望验证器验证不使用Model的数据，你还可以重写[[yii\\validators\\Validator::validate()]] 方法。\n你也可以重写[[yii\\validators\\Validator::validateValue()]] 方法而不是\n去重写`validateAttribute()` 和 `validate()`这两个方法，因为默认状态下，\n后两者是通过调用 `validateValue()` 这个方法实现的。\n\n\n下面就是一个怎样把自定义验证器在模型中使用的例子。\n\n```php\nnamespace app\\models;\n\nuse Yii;\nuse yii\\base\\Model;\nuse app\\components\\validators\\CountryValidator;\n\nclass EntryForm extends Model\n{\n    public $name;\n    public $email;\n    public $country;\n\n    public function rules()\n    {\n        return [\n            [['name', 'email'], 'required'],\n            ['country', CountryValidator::class],\n            ['email', 'email'],\n        ];\n    }\n}\n```\n\n\n## 多属性验证 <span id=\"multiple-attributes-validation\"></span>\n\n某些情况下验证器可以包含多个属性。考虑下面的情况：\n\n```php\nclass MigrationForm extends \\yii\\base\\Model\n{\n    /**\n     * 一个成年人的最少花销\n     */\n    const MIN_ADULT_FUNDS = 3000;\n    /**\n     * 一个孩子的最小花销\n     */\n    const MIN_CHILD_FUNDS = 1500;\n\n    public $personalSalary;\n    public $spouseSalary;\n    public $childrenCount;\n    public $description;\n\n    public function rules()\n    {\n        return [\n            [['personalSalary', 'description'], 'required'],\n            [['personalSalary', 'spouseSalary'], 'integer', 'min' => self::MIN_ADULT_FUNDS],\n            ['childrenCount', 'integer', 'min' => 0, 'max' => 5],\n            [['spouseSalary', 'childrenCount'], 'default', 'value' => 0],\n            ['description', 'string'],\n        ];\n    }\n}\n```\n\n### 创建验证器 <span id=\"multiple-attributes-validator\"></span>\n\n比如我们需要检查下家庭收入是否足够给孩子们花销。此时我们可以创建一个行内验证器\n`validateChildrenFunds` 来解决这个问题，它仅仅在 `childrenCount` 大于 0 的时候才去检查。\n\n请注意，我们不要把所有需要验证的属性 (`['personalSalary', 'spouseSalary', 'childrenCount']`) 都附加到\n验证器上。因为这样做同一个验证器将会对每个属性都执行一遍验证（总共三次），但是实际上我们只需要对整个属性集\n执行一次验证而已。\n\n你可以使用属性集合里的任何一个（或者使用你认为最相关的那个属性）：\n\n```php\n['childrenCount', 'validateChildrenFunds', 'when' => function ($model) {\n    return $model->childrenCount > 0;\n}],\n```\n\n`validateChildrenFunds` 的实现可以是下面这样的：\n\n```php\npublic function validateChildrenFunds($attribute, $params)\n{\n    $totalSalary = $this->personalSalary + $this->spouseSalary;\n    // Double the minimal adult funds if spouse salary is specified\n    $minAdultFunds = $this->spouseSalary ? self::MIN_ADULT_FUNDS * 2 : self::MIN_ADULT_FUNDS;\n    $childFunds = $totalSalary - $minAdultFunds;\n    if ($childFunds / $this->childrenCount < self::MIN_CHILD_FUNDS) {\n        $this->addError('childrenCount', 'Your salary is not enough for children.');\n    }\n}\n```\n\n你可以忽略 `$attribute` 参数，因为这个验证过程不仅仅关联一个属性。\n\n\n### 添加错误信息 <span id=\"multiple-attributes-errors\"></span>\n\n在添加错误信息的时候，如果是多个属性，可以根据自己想要的格式使用多种情况：\n\n- 选择一个你认为最相关的字段把错误信息添加到它的属性里：\n\n```php\n$this->addError('childrenCount', 'Your salary is not enough for children.');\n```\n\n- 选择多个相关的属性乃至所有属性给它们添加同样的错误信息。在使用 `addError` 之前我们可以先把错误信息存储到\n一个独立的变量里，这样可以减少代码重复性。\n\n```php\n$message = 'Your salary is not enough for children.';\n$this->addError('personalSalary', $message);\n$this->addError('wifeSalary', $message);\n$this->addError('childrenCount', $message);\n```\n\n或者使用循环：\n\n```php\n$attributes = ['personalSalary', 'wifeSalary', 'childrenCount'];\nforeach ($attributes as $attribute) {\n    $this->addError($attribute, 'Your salary is not enough for children.');\n}\n```\n\n- 添加通用错误信息（不相关于特定的属性）。我们可以用一个不存在的属性名添加错误信息\n比如 `*`，因为这时是不检查属性的存在性的。\n\n```php\n$this->addError('*', 'Your salary is not enough for children.');\n```\n\n这种情况下，我们不会在表单域里看到错误信息。为了展示这个错误信息，我们可以在视图里使用错误汇总：\n\n```php\n<?= $form->errorSummary($model) ?>\n```\n\n> Note: 创建一次验证多个属性的验证器的参考说明在这里 [community cookbook](https://github.com/samdark/yii2-cookbook/blob/master/book/forms-validator-multiple-attributes.md).\n\n## 客户端验证 <span id=\"client-side-validation\"></span>\n\n当终端用户通过 HTML 表单提供输入数据时，基于 JavaScript 的客户端验证是可取的，\n因为它允许用户更快地找出输入错误，从而提供更好的用户体验。你可以尝试使用或者自己实现一个 *除了支持服务端验证* 之外\n还支持客户端验证的验证器。\n\n> Info: 尽管客户端验证是值得的，但它不是必须的。客户端验证的主要目的是给终端用户提供\n  一个较好的体验。正如 “永远不用相信终端用户的输入数据” 一样，你也不能完全信任客户端验证。\n  基于这个考虑的话，正如前文描述所说，你应该永远在服务端通过调用 [[yii\\base\\Model::validate()]] 方法\n  进行服务端验证。\n\n\n### 使用客户端验证 <span id=\"using-client-side-validation\"></span>\n\n许多 [核心验证器](tutorial-core-validators.md) 支持开箱即用的客户端验证。你需要做的\n就是使用 [[yii\\widgets\\ActiveForm]] 构建你的 HTML 表单。 比如，下面的 `LoginForm` 声明了两个\n规则：一个使用 [required](tutorial-core-validators.md#required) 核心验证器，它支持客户端的验证，也支持服务端的 \n验证；另一个使用 `validatePassword` 行内验证器，它只支持在服务端\n验证。\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\nuse app\\models\\User;\n\nclass LoginForm extends Model\n{\n    public $username;\n    public $password;\n\n    public function rules()\n    {\n        return [\n            // username 和 password 都是必填项\n            [['username', 'password'], 'required'],\n\n            // password 用 validatePassword() 方法验证\n            ['password', 'validatePassword'],\n        ];\n    }\n\n    public function validatePassword()\n    {\n        $user = User::findByUsername($this->username);\n\n        if (!$user || !$user->validatePassword($this->password)) {\n            $this->addError('password', 'Incorrect username or password.');\n        }\n    }\n}\n```\n\n下面的代码构建了包含 `username` 和 `password` 两个表单项的 HTML 表单。\n如果不输入任何内容直接提交表单，你就会发现提示你输入内容的错误信息立刻出现，\n而这并没有和服务端交互。\n\n```php\n<?php $form = yii\\widgets\\ActiveForm::begin(); ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n    <?= Html::submitButton('Login') ?>\n<?php yii\\widgets\\ActiveForm::end(); ?>\n```\n\n幕后的运作过程是这样的： [[yii\\widgets\\ActiveForm]] 读取在模型中声明的规则，然后\n生成验证器支持客户端验证对应的 JavaScript 代码。当用户\n改变表单项或者提交整个表单的时候，客户端验证的 JavaScript 就会触发。\n\n如果你想完全关闭客户端验证，你可以设置\n[[yii\\widgets\\ActiveForm::enableClientValidation]] 属性为 `false` 。你也可以通过设置它们的\n[[yii\\widgets\\ActiveField::enableClientValidation]] 属性为 `false` 来单独关闭某一个表单项。\n当在表单项级别和表单级别都设置了 `enableClientValidation` 的时候，\n前者（表单项）的级别优先生效。\n\n> Info: 从 2.0.11 版本开始，所有继承 [[yii\\validators\\Validator]] 的验证器都可以通过\n> - [[yii\\validators\\Validator::getClientOptions()]] 这个单独的方法接收客户端选项。可以这样使用：\n\n> - 如果你想自己实现自定义的客户端验证但是保留服务端的\n> 验证器选项；\n> - 继承或者自定义符合你特殊的需求：\n>\n> ```php\n> public function getClientOptions($model, $attribute)\n> {\n>     $options = parent::getClientOptions($model, $attribute);\n>     // 修改 $options \n>\n>     return $options;\n> }\n> ```\n\n### 实现客户端验证 <span id=\"implementing-client-side-validation\"></span>\n\n为了创建一个支持客户端验证的验证器，你应该实现\n[[yii\\validators\\Validator::clientValidateAttribute()]] 方法，该方法返回一段 JavaScript 代码\n用来在客户端执行验证。在这段 JavaScript 代码里，你可以使用下面几个\n预定义的变量：\n\n- `attribute`：被验证的属性名。\n- `value`：被验证的值。\n- `messages`：一个给属性保存验证错误信息的数组。\n- `deferred`：一个支持添加 deferred 对象的数组（下一部分再说）。\n\n下面的例子，我们创建了一个 `StatusValidator` 验证器，它用来验证一个输入和存在的状态相比， \n是否是有效的状态输入。这个验证器支持服务端验证也支持客户端验证。\n\n```php\nnamespace app\\components;\n\nuse yii\\validators\\Validator;\nuse app\\models\\Status;\n\nclass StatusValidator extends Validator\n{\n    public function init()\n    {\n        parent::init();\n        $this->message = 'Invalid status input.';\n    }\n\n    public function validateAttribute($model, $attribute)\n    {\n        $value = $model->$attribute;\n        if (!Status::find()->where(['id' => $value])->exists()) {\n            $model->addError($attribute, $this->message);\n        }\n    }\n\n    public function clientValidateAttribute($model, $attribute, $view)\n    {\n        $statuses = json_encode(Status::find()->select('id')->asArray()->column());\n        $message = json_encode($this->message, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);\n        return <<<JS\nif ($.inArray(value, $statuses) === -1) {\n    messages.push($message);\n}\nJS;\n    }\n}\n```\n\n> Tip: 上面给出的代码主要展示如何支持客户端验证。在实际使用中，\n> 你可以使用 [in](tutorial-core-validators.md#in) 核心验证器来实现同样的目标。你也可以\n> 像下面那样写验证规则：\n>\n> ```php\n> [\n>     ['status', 'in', 'range' => Status::find()->select('id')->asArray()->column()],\n> ]\n> ```\n\n> Tip: 如果你想手动调整客户端的验证，比如动态地添加表单项或者做一些自定义的 UI 逻辑，请参考\n> [Working with ActiveForm via JavaScript](https://github.com/samdark/yii2-cookbook/blob/master/book/forms-activeform-js.md)\n> 在 Yii 2.0 Cookbook。\n\n### Deferred 验证 <span id=\"deferred-validation\"></span>\n\n如果你需要执行异步客户端验证，你可以创建 [Deferred objects](https://api.jquery.com/category/deferred-object/)。 \n比如要执行一段自定义的 AJAX 验证，可以使用下面的代码：\n\n```php\npublic function clientValidateAttribute($model, $attribute, $view)\n{\n    return <<<JS\n        deferred.push($.get(\"/check\", {value: value}).done(function(data) {\n            if ('' !== data) {\n                messages.push(data);\n            }\n        }));\nJS;\n}\n```\n\n上面这个 `deferred` 变量是由 Yii 提供的，它是一个 Deferred 对象的数组。这个 `$.get()`\njQuery 方法用来产生一个 Deferred 对象然后推送到 `deferred` 数组里。\n\n你也可以明确地创建一个 `deferred` 对象，当异步回调触发的时候调用它的 `resolve()` 方法。\n下面的例子展示了如何在客户端验证一个上传图片的尺寸。\n\n```php\npublic function clientValidateAttribute($model, $attribute, $view)\n{\n    return <<<JS\n        var def = $.Deferred();\n        var img = new Image();\n        img.onload = function() {\n            if (this.width > 150) {\n                messages.push('Image too wide!!');\n            }\n            def.resolve();\n        }\n        var reader = new FileReader();\n        reader.onloadend = function() {\n            img.src = reader.result;\n        }\n        reader.readAsDataURL(file);\n\n        deferred.push(def);\nJS;\n}\n```\n\n> Note: `resolve()` 方法必须在所有属性都验证完之后调用。不然表单不会\n  完成整体的验证流程。\n\n为了简单起见，`deferred` 数组封装了一个快捷方法 `add()`，它可以自动创建 Deferred 对象\n然后把它添加到 `deferred` 数组里。用这个方法，你可以简化上面的例子：\n\n```php\npublic function clientValidateAttribute($model, $attribute, $view)\n{\n    return <<<JS\n        deferred.add(function(def) {\n            var img = new Image();\n            img.onload = function() {\n                if (this.width > 150) {\n                    messages.push('Image too wide!!');\n                }\n                def.resolve();\n            }\n            var reader = new FileReader();\n            reader.onloadend = function() {\n                img.src = reader.result;\n            }\n            reader.readAsDataURL(file);\n        });\nJS;\n}\n```\n\n\n## AJAX 验证 <span id=\"ajax-validation\"></span>\n\n一些验证器只能工作在服务端，因为只有服务端才有必要的信息。\n比如，验证一个用户名是否唯一，需要在服务端检查用户表。\n这时候你可以使用基于 AJAX 的验证。它会在背后触发一个 AJAX 请求用来验证输入项而且还能\n保持和通常客户端验证一样的用户体验。\n\n给一个单独的表单项开启 AJAX 验证，你只需要设置 [[yii\\widgets\\ActiveField::enableAjaxValidation|enableAjaxValidation]]\n属性为 `true`，然后指定一个唯一的表单 `id`：\n\n```php\nuse yii\\widgets\\ActiveForm;\n\n$form = ActiveForm::begin([\n    'id' => 'registration-form',\n]);\n\necho $form->field($model, 'username', ['enableAjaxValidation' => true]);\n\n// ...\n\nActiveForm::end();\n```\n\n如果要给所有的表单项开启 AJAX 验证，可以在表单级别设置 [[yii\\widgets\\ActiveForm::enableAjaxValidation|enableAjaxValidation]]\n属性为 `true` 就行：\n\n```php\n$form = ActiveForm::begin([\n    'id' => 'contact-form',\n    'enableAjaxValidation' => true,\n]);\n```\n\n> Note: 当在表单项级别和表单级别都设置了 `enableAjaxValidation` 属性的时候，\n  前者（表单项级别）优先生效。\n\n你也需要在服务端准备处理这样的 AJAX 请求。\n这个可以在控制器的动作里通过如下的代码片段来实现：\n\n```php\nif (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {\n    Yii::$app->response->format = Response::FORMAT_JSON;\n    return ActiveForm::validate($model);\n}\n```\n\n上述代码将会检测当前的请求是否源自 AJAX。如果是的话，它将运行验证过程, 然后\n返回一段 JSON 格式的错误信息来响应这次请求。\n\n> Info: 你也可以用 [Deferred 验证](#deferred-validation) 执行 AJAX 验证。 \n  话说回来，这里描述的 AJAX 验证还是比较系统并且需要较少的代码开销。\n\n当 `enableClientValidation` 和 `enableAjaxValidation` 都设置为 `true` 时，只有客户端验证成功之后\n才会触发 AJAX 的验证请求。注意，如果验证某个表单项的时候凑巧 `validateOnChange`，`validateOnBlur` 或者 `validateOnType`\n其中之一设置了 `true`,那么这个表单项在单独通过这样的客户端验证时，\n也会发起 AJAX 请求。\n"
  },
  {
    "path": "docs/guide-zh-CN/intro-upgrade-from-v1.md",
    "content": "从 Yii 1.1 升级\n===============\n\n2.0 版框架是完全重写的，在 1.1 和 2.0 两个版本之间存在相当多差异。\n因此从 1.1 版升级并不像小版本间的跨越那么简单，\n通过本指南你将会了解两个版本间主要的不同之处。\n\n如果你之前没有用过 Yii 1.1，可以跳过本章，直接从\"[入门篇](start-installation.md)\"开始读起。\n\n请注意，Yii 2.0 引入了很多本章并没有涉及到的新功能。\n强烈建议你通读整部权威指南来了解所有新特性。\n这样有可能会发现一些以前你要自己开发的功能，而现在已经被包含在核心代码中了。\n\n\n安装\n---\n\nYii 2.0 完全拥抱 [Composer](https://getcomposer.org/)，它是事实上的 PHP 依赖管理工具。\n核心框架以及扩展的安装都通过 Composer 来处理。想要了解更多如何安装 Yii 2.0 请参阅本指南的\n[安装 Yii](start-installation.md) 章节。如果你想创建新扩展，\n或者把你已有的 Yii 1.1 的扩展改写成兼容 2.0 的版本，\n你可以参考 [创建扩展](structure-extensions.md#creating-extensions) 章节。\n\n\nPHP 需求\n-------\n\nYii 2.0 需要 PHP 5.4 或更高版本，该版本相对于 Yii 1.1 所需求的 PHP 5.2 而言有巨大的改进。\n因此在语言层面上有很多的值得注意的不同之处。\n下面是 PHP 层的主要变化汇总：\n\n- [命名空间](https://www.php.net/manual/zh/language.namespaces.php)\n- [匿名函数](https://www.php.net/manual/zh/functions.anonymous.php)\n- 数组短语法 `[...元素...]` 用于取代 `array(...元素...)`\n- 视图文件中的短格式 echo 标签 `<?=`，自 PHP 5.4 起总会被识别并且合法，无论 short_open_tag 的设置是什么，可以安全使用。\n- [SPL 类和接口](https://www.php.net/manual/zh/book.spl.php)\n- [延迟静态绑定](https://www.php.net/manual/zh/language.oop5.late-static-bindings.php)\n- [日期和时间](https://www.php.net/manual/zh/book.datetime.php)\n- [Traits](https://www.php.net/manual/zh/language.oop5.traits.php)\n- [intl](https://www.php.net/manual/zh/book.intl.php) Yii 2.0 使用 PHP 扩展 `intl` \n  来支持国际化的相关功能。\n\n\n命名空间\n---------\n\nYii 2.0 里最明显的改动就数命名空间的使用了。几乎每一个核心类都引入了命名空间，\n比如 `yii\\web\\Request`。1.1 版类名前缀 “C” 已经不再使用。\n当前的命名方案与目录结构相吻合。例如，`yii\\web\\Request` \n就表明对应的类文件是 Yii 框架文件夹下的 `web/Request.php` 文件。\n\n（有了 Yii 的类自动加载器，\n你可以直接使用全部核心类而不需要显式包含具体文件。）\n\n\n组件（Component）与对象（BaseObject）\n---------------------------------\n\nYii 2.0 把 1.1 中的 `CComponent` 类拆分成了两个类：[[yii\\base\\Object]] 和 [[yii\\base\\Component]]。\n[[yii\\base\\Object|Object]] 类是一个轻量级的基类，你可以通过 getters 和 setters 来定义[对象的属性](concept-properties.md)。\n[[yii\\base\\Component|Component]] 类继承自 [[yii\\base\\Object|Object]]，\n同时进一步支持 [事件](concept-events.md) 和 [行为](concept-behaviors.md)。\n\n如果你不需要用到事件或行为，\n应该考虑使用 [[yii\\base\\Object|Object]] 类作为基类。\n这种类通常用来表示基本的数据结构。\n\n\n对象的配置\n---------\n\n[[yii\\base\\Object|Object]] 类引入了一种统一对象配置的方法。\n所有 [[yii\\base\\Object|Object]] 的子类都应该用以下方法声明它的构造方法（如果需要的话），\n以正确配置它自身：\n\n```php\nclass MyClass extends \\yii\\base\\BaseObject\n{\n    public function __construct($param1, $param2, $config = [])\n    {\n        // ... 配置生效前的初始化过程\n\n        parent::__construct($config);\n    }\n\n    public function init()\n    {\n        parent::init();\n\n        // ... 配置生效后的初始化过程\n    }\n}\n```\n\n在上面的例子里，构造方法的最后一个参数必须传入一个配置数组，\n包含一系列用于在方法结尾初始化相关属性的键值对。\n你可以重写 [[yii\\base\\Object::init()|init()]] \n方法来执行一些需要在配置生效后进行的初始化工作。\n\n你可以通过遵循以下约定俗成的编码习惯，\n来使用配置数组创建并配置新的对象：\n\n```php\n$object = Yii::createObject([\n    'class' => 'MyClass',\n    'property1' => 'abc',\n    'property2' => 'cde',\n], [$param1, $param2]);\n```\n\n更多有关配置的细节可以在[配置](concept-configurations.md)章节找到。\n\n\n事件（Event）\n-----------\n\n在 Yii 1 中，通常通过定义 `on` 开头的方法（例如 `onBeforeSave`）来创建事件。\n而在 Yii 2 中，你可以使用任意的事件名了。同时通过调用 [[yii\\base\\Component::trigger()|trigger()]] 方法来触发相关事件：\n\n```php\n$event = new \\yii\\base\\Event;\n$component->trigger($eventName, $event);\n```\n\n要给事件附加一个事件事件处理器，需要使用 [[yii\\base\\Component::on()|on()]] 方法：\n\n```php\n$component->on($eventName, $handler);\n// 解除事件处理器，使用 off 方法：\n// $component->off($eventName, $handler);\n```\n\n事件功能还有更多增强之处。要了解它们，请查看[事件](concept-events.md)章节。\n\n\n路径别名（Path Alias）\n------------\n\nYii 2.0 将路径别名的应用扩大至文件/目录路径和 URL。Yii 2.0 中路径别名必须以 `@` 符号开头，\n以区别于普通文件目录路径或 URL。例如 `@yii` 就是指向 Yii 安装目录的别名。\n绝大多数 Yii 核心代码都支持别名。\n例如 [[yii\\caching\\FileCache::cachePath]] \n就同时支持路径别名或普通的目录地址。\n\n路径别名也和类的命名空间密切相关。建议给每一个根命名空间定义一个路径别名，\n从而无须额外配置，便可启动 Yii 的类自动加载机制。\n例如，因为有 `@yii` 指向 Yii 安装目录，\n那类似 `yii\\web\\Request` 的类就能被 Yii 自动加载。同理，若你用了一个第三方的类库，\n如 Zend Framework，你只需定义一个名为 `@Zend` 的路径别名指向该框架的安装目录。\n之后 Yii 就可以自动加载任意 Zend Framework 中的类了。\n\n更多路径别名信息请参阅[路径别名](concept-aliases.md)章节。\n\n\n视图（View）\n----------\n\nYii 2 中视图最明显的改动是视图内的特殊变量 `$this` 不再指向当前控制器或小部件，\n而是指向**视图**对象，它是 2.0 中引入的全新概念。**视图**对象为 [[yii\\web\\View]] 的实例，\n他代表了 MVC 模式中的视图部分。如果你想要在视图中访问一个控制器或小部件，\n可以使用 `$this->context`。\n\n要在其他视图里渲染一个局部视图，使用 `$this->render()`，而不是 `$this->renderPartial()`。\n`render()` 现在只返回渲染结果，而不是直接显示它，所以现在你必须显式地把它 **echo** 出来。像这样：\n\n```php\necho $this->render('_item', ['item' => $item]);\n```\n\n除了使用 PHP 作为主要的模板语言，Yii 2.0 也装备了两种流行模板引擎的官方支持：Smarty 和 Twig。\n过去的 Prado 模板引擎不再被支持。要使用这些模板引擎，\n你需要配置 `view` 应用组件，\n给它设置 [[yii\\base\\View::$renderers|View::$renderers]] 属性。\n具体请参阅[模板引擎](tutorial-template-engines.md)章节。\n\n\n模型（Model）\n-----------\n\nYii 2.0 使用 [[yii\\base\\Model]] 作为模型基类，类似于 1.1 的 `CModel` 。\n`CFormModel` 被完全弃用了，现在要创建表单模型类，可以通过继承 [[yii\\base\\Model]] 类来实现。\n\nYii 2.0 引进了名为 [[yii\\base\\Model::scenarios()|scenarios()]] 的新方法来声明支持的场景，\n并指明在哪个场景下某属性必须经过验证，可否被视为安全值等等。如：\n\n```php\npublic function scenarios()\n{\n    return [\n        'backend' => ['email', 'role'],\n        'frontend' => ['email', '!role'],\n    ];\n}\n```\n\n上面的代码声明了两个场景：`backend` 和 `frontend` 。对于 `backend` 场景，`email` 和 `role` 属性值都是安全的，\n且能进行批量赋值。对于 `frontend` 场景，`email` 能批量赋值而 `role` 不能。\n`email` 和 `role` 都必须通过规则验证。\n\n[[yii\\base\\Model::rules()|rules()]] 方法仍用于声明验证规则。注意，由于引入了 [[yii\\base\\Model::scenarios()|scenarios()]]，现在已经没有 `unsafe` 验证器了。\n\n大多数情况下，如果 [[yii\\base\\Model::rules()|rules()]] 方法内已经完整地指定场景了，\n那就不必覆写 [[yii\\base\\Model::scenarios()|scenarios()]]，\n也不必声明 `unsafe` 属性值。\n\n要了解更多有关模型的细节，请参考[模型](structure-models.md)章节。\n\n\n控制器（Controller）\n-----------\n\nYii 2.0 使用 [[yii\\web\\Controller]] 作为控制器的基类，\n它类似于 1.1 的 `CController`。使用 [[yii\\base\\Action]] 作为操作类的基类。\n\n这些变化最明显的影响是，当你在写控制器操作的代码时，\n应该返回（return）要渲染的内容而不是输出（echo）它：\n\n```php\npublic function actionView($id)\n{\n    $model = \\app\\models\\Post::findOne($id);\n    if ($model) {\n        return $this->render('view', ['model' => $model]);\n    } else {\n        throw new \\yii\\web\\NotFoundHttpException;\n    }\n}\n```\n\n请查看[控制器（Controller）](structure-controllers.md)章节了解有关控制器的更多细节。\n\n\n小部件（Widget）\n-------------\n\nYii 2.0 使用 [[yii\\base\\Widget]] 作为小部件基类，类似于 1.1 的 `CWidget`。\n\n为了让框架获得更好的 IDE 支持，Yii 2.0 引进了一个调用小部件的新语法。\n包含 [[yii\\base\\Widget::begin()|begin()]]，[[yii\\base\\Widget::end()|end()]] \n和 [[yii\\base\\Widget::widget()|widget()]] 三个静态方法，用法如下：\n\n```php\nuse yii\\widgets\\Menu;\nuse yii\\widgets\\ActiveForm;\n\n// 注意必须 **\"echo\"** 结果以显示内容\necho Menu::widget(['items' => $items]);\n\n// 传递一个用于初始化对象属性的数组\n$form = ActiveForm::begin([\n    'options' => ['class' => 'form-horizontal'],\n    'fieldConfig' => ['inputOptions' => ['class' => 'input-xlarge']],\n]);\n... 表单输入栏都在这里 ...\nActiveForm::end();\n```\n\n更多细节请参阅[小部件](structure-widgets.md)章节。\n\n\n主题（Theme）\n-----------\n\n2.0 主题的运作方式跟以往完全不同了。它们现在基于**路径映射机制**，该机制会把一个源视图文件的路径映射到一个主题视图文件路径。\n举例来说，如果路径映射为 `['/web/views' => '/web/themes/basic']`，\n那么 `/web/views/site/index.php` 视图经过主题修饰的版本就会是 `/web/themes/basic/site/index.php`。\n也因此让主题现在可以应用在任何视图文件之上，\n甚至是渲染控制器上下文环境之外的视图文件或小部件。\n\n同样，`CThemeManager` 组件已经被移除了。\n取而代之的 `theme` 成为了 `view` 应用组件的一个可配置属性。\n\n更多细节请参考[主题](output-theming.md)章节。\n\n\n控制台应用（Console Application）\n------------------------------\n\n控制台应用现在如普通的 Web 应用程序一样，由控制器组成，\n控制台的控制器继承自 [[yii\\console\\Controller]]，类似于 1.1 的 `CConsoleCommand`。\n\n运行控制台命令使用 `yii <route>`，\n其中 `<route>` 代表控制器的路由（如 `sitemap/index`）。\n额外的匿名参数传递到对应的控制器操作方法，\n而有名的参数根据 [[yii\\console\\Controller::options()]] 的声明来解析。\n\nYii 2.0 支持基于代码注释自动生成相的关命令行帮助（help）信息。\n\n更多细节请参阅[控制台命令](tutorial-console.md)章节。\n\n\n国际化（I18N）\n------------\n\nYii 2.0 移除了原来内置的日期格式器和数字格式器，为了支持 [PECL intl PHP module](https://pecl.php.net/package/intl)（PHP 的国际化扩展）的使用。\n\n消息翻译现在由 `i18n` 应用组件执行。\n该组件管理一系列消息源，\n允许使用基于消息类别的不同消息源。\n\n更多细节请参阅[国际化（Internationalization）](tutorial-i18n.md)章节。\n\n\n动作过滤器（Action Filters）\n-------------------------\n\n操作的过滤现在通过行为（behavior）来实现。要定义一个新的，自定义的过滤器，请继承 [[yii\\base\\ActionFilter]] 类。\n要使用一个过滤器，需要把过滤器类作为一个 `behavior` 绑定到控制器上。\n例如，要使用 [[yii\\filters\\AccessControl]] 过滤器，你需要在控制器内添加如下代码：\n\n```php\npublic function behaviors()\n{\n    return [\n        'access' => [\n            'class' => 'yii\\filters\\AccessControl',\n            'rules' => [\n                ['allow' => true, 'actions' => ['admin'], 'roles' => ['@']],\n            ],\n        ],\n    ];\n}\n```\n\n更多细节请参考[过滤器](structure-filters.md)章节。\n\n\n前端资源（Assets）\n---------------\n\nYii 2.0 引入了一个新的概念，称为**资源包**（Asset Bundle），以代替 1.1 的脚本包概念。\n\n一个资源包是一个目录下的资源文件集合（如 JavaScript 文件、CSS 文件、图片文件等）。\n每一个资源包被表示为一个类，该类继承自 [[yii\\web\\AssetBundle]]。\n用 [[yii\\web\\AssetBundle::register()]] 方法注册一个资源包后，\n就使它的资源可被 Web 访问了，\n注册了资源包的页面会自动包含和引用资源包内指定的 JS 和 CSS 文件。\n\n更多细节请参阅 [前端资源管理（Asset）](structure-assets.md) 章节。\n\n\n助手类（Helpers）\n---------------\n\nYii 2.0 很多常用的静态助手类，包括：\n\n* [[yii\\helpers\\Html]]\n* [[yii\\helpers\\ArrayHelper]]\n* [[yii\\helpers\\StringHelper]]\n* [[yii\\helpers\\FileHelper]]\n* [[yii\\helpers\\Json]]\n\n请参考[助手一览](helper-overview.md) 章节来了解更多。\n\n表单（Forms）\n-----------\n\nYii 2.0 引进了**表单栏（field）**的概念，用来创建一个基于 [[yii\\widgets\\ActiveForm]] 的表单。\n一个表单栏是一个由标签、输入框、错误消息（可能还有提示文字）组成的容器，\n被表示为一个 [[yii\\widgets\\ActiveField|ActiveField]] 对象。\n使用表单栏建立表单的过程比以前更整洁利落：\n\n```php\n<?php $form = yii\\widgets\\ActiveForm::begin(); ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n    <div class=\"form-group\">\n        <?= Html::submitButton('Login') ?>\n    </div>\n<?php yii\\widgets\\ActiveForm::end(); ?>\n```\n\n请参考[创建表单](input-forms.md)章节来了解更多细节。\n\n\n查询生成器（Query Builder）\n------------------------\n\nYii 1.1 中，查询语句的生成分散在多个类中，包括 `CDbCommand`，`CDbCriteria` 以及 `CDbCommandBuilder`。\nYii 2.0 以 [[yii\\db\\Query|Query]] 对象的形式表示一个数据库查询，\n这个对象使用 [[yii\\db\\QueryBuilder|QueryBuilder]] 在幕后生成 SQL 语句。\n例如：\n\n```php\n$query = new \\yii\\db\\Query();\n$query->select('id, name')\n      ->from('user')\n      ->limit(10);\n\n$command = $query->createCommand();\n$sql = $command->sql;\n$rows = $command->queryAll();\n```\n\n最重要的是，这些查询生成方法还可以和[活动记录](db-active-record.md)配合使用。\n\n请参考[查询生成器（Query Builder）](db-query-builder.md)章节了解更多内容。\n\n\n活动记录（Active Record）\n----------------------\n\nYii 2.0 的[活动记录](db-active-record.md)改动了很多。\n两个最显而易见的改动分别涉及查询语句的生成（query building）和关联查询的处理（relational query handling）。\n\n1.1 中的 `CDbCriteria` 类在 Yii 2 中被 [[yii\\db\\ActiveQuery]] 所替代。\n这个类是继承自 [[yii\\db\\Query]]，因此也继承了所有查询生成方法。开始拼装一个查询可以调用 [[yii\\db\\ActiveRecord::find()]] 方法进行：\n\n```php\n// 检索所有“活动的”客户和订单，并以 ID 排序：\n$customers = Customer::find()\n    ->where(['status' => $active])\n    ->orderBy('id')\n    ->all();\n```\n\n要声明一个关联关系，只需简单地定义一个 getter 方法来返回一个 [[yii\\db\\ActiveQuery|ActiveQuery]] 对象。\ngetter 方法定义的属性名代表关联表名称。\n如，以下代码声明了一个名为 `orders` 的关系（1.1 中必须在 `relations()` 方法内声明关系）：\n\n```php\nclass Customer extends \\yii\\db\\ActiveRecord\n{\n    public function getOrders()\n    {\n        return $this->hasMany('Order', ['customer_id' => 'id']);\n    }\n}\n```\n\n现在你就可以通过调用 `$customer->orders` 来访问关联表中某用户的订单了。\n你还可以用以下代码进行一场指定条件的实时关联查询：\n\n```php\n$orders = $customer->getOrders()->andWhere('status=1')->all();\n```\n\n当贪婪加载一段关联关系时，Yii 2.0 和 1.1 的运作机理并不相同。\n具体来说，在 1.1 中使用一条 JOIN 语句同时查询主表和关联表记录。\n在 Yii 2.0 中会使用两个没有 JOIN 的 SQL 语句：第一条语句取回主表记录，\n第二条通过主表记录经主键筛选后查询关联表记录。\n\n当生成返回大量记录的查询时，可以链式书写 [[yii\\db\\ActiveQuery::asArray()|asArray()]] 方法，\n这样会以数组的形式返回查询结果，而不必返回\n[[yii\\db\\ActiveRecord|ActiveRecord]] 对象，这能显著降低因大量记录读取所消耗的 CPU 时间和内存。如：\n\n```php\n$customers = Customer::find()->asArray()->all();\n```\n\n另一个改变是你不能再通过公共变量定义属性（Attribute）的默认值了。\n如果你需要这么做的话，可以在你的记录类的 `init` 方法中设置它们。\n\n```php\npublic function init()\n{\n    parent::init();\n    $this->status = self::STATUS_NEW;\n}\n```\n\n曾几何时，在 1.1 中重写一个活动记录类的构造方法会导致一些问题。它们不会在 2.0 中出现了。\n需要注意的是，如果你需要在构造方法中添加一些参数，恐怕必须重写 [[yii\\db\\ActiveRecord::instantiate()]] 方法。\n\n活动记录方面还有很多其他的变化与改进，\n请参考[活动记录](db-active-record.md)章节以了解更多细节。\n\n\n活动记录行为（Active Record Behaviors）\n------------------------------------\n\n在 2.0 中遗弃了活动记录行为基类 `CActiveRecordBehavior`。\n如果你想创建活动记录行为，需要直接继承 `yii\\base\\Behavior`。\n如果行为类中需要表示一些事件，需要像这样覆写 `events()` 方法：\n\n```php\nnamespace app\\components;\n\nuse yii\\db\\ActiveRecord;\nuse yii\\base\\Behavior;\n\nclass MyBehavior extends Behavior\n{\n    // ...\n\n    public function events()\n    {\n        return [\n            ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',\n        ];\n    }\n\n    public function beforeValidate($event)\n    {\n        // ...\n    }\n}\n```\n\n\n用户及身份验证接口（IdentityInterface）\n-------------------------------------\n\n1.1 中的 `CWebUser` 类现在被 [[yii\\web\\User]] 所取代，随之 `CUserIdentity` 类也不在了。\n与之相对的，为达到相同目的，你可以实现 [[yii\\web\\IdentityInterface]] 接口，它使用起来更直观。\n在高级应用模版里提供了一个这样的一个例子。\n\n要了解更多细节请参考[认证（Authentication）](security-authentication.md)，[授权（Authorization）](security-authorization.md)以及[高级应用模版](tutorial-advanced-app.md) 这三个章节。\n\n\nURL 管理\n--------\n\nYii 2.0 的 URL 管理跟 1.1 中很像。一个主要的改进是现在的 URL 管理支持**可选参数**了。\n比如，如果你在 2.0 中定义了一个下面这样的规则，那么它可以同时匹配 \n`post/popular` 和 `post/1/popular` 两种 URL。\n而在 1.1 中为达成相同效果，必须要使用两条规则。\n\n```php\n[\n    'pattern' => 'post/<page:\\d+>/<tag>',\n    'route' => 'post/index',\n    'defaults' => ['page' => 1],\n]\n```\n\n请参考[URL 解析和生成](runtime-url-handling.md) 章节，以了解更多细节。.\n\n一个重要变化是路由命名的约定，\n现在的控制器和动作的驼峰命名会被转换成小写字母，\n其中每个单词由一个专有的连字号分隔，例如，`CamelCaseController` 将为 `camel-case`。\n有关更多详细信息，请参阅关于 [controller IDs](structure-controllers.md#controller-ids) 和 [action IDs](structure-controllers.md#action-ids) 的部分。\n\n\n同时使用 Yii 1.1 和 2.x\n----------------------\n\n如果你有一些遗留的 Yii 1.1 代码，需要跟 Yii 2.0 一起使用，\n可以参考 [1.1 和 2.0 共用](tutorial-yii-integration.md)章节。\n\n"
  },
  {
    "path": "docs/guide-zh-CN/intro-yii.md",
    "content": "Yii 是什么\n===========\n\nYii 是一个高性能，基于组件的 PHP 框架，用于快速开发现代 Web 应用程序。\n名字 Yii （读作 `易`）在中文里有“极致简单与不断演变”两重含义，\n也可看作 **Yes It Is**! 的缩写。\n\n\nYii 最适合做什么？\n---------------------\n\nYii 是一个通用的 Web 编程框架，即可以用于开发各种用 PHP 构建的 Web 应用。\n因为基于组件的框架结构和设计精巧的缓存支持，它特别适合开发大型应用，\n如门户网站、社区、内容管理系统（CMS）、\n电子商务项目和 RESTful Web 服务等。\n\n\nYii 和其他框架相比呢？\n-------------------------------------------\n\n如果你有其它框架使用经验，那么你会很开心看到 Yii 所做的努力：\n\n- 和其他 PHP 框架类似，Yii 实现了 MVC（Model-View-Controller）\n  设计模式并基于该模式组织代码。\n- Yii 的代码简洁优雅，这是它的编程哲学。\n  它永远不会为了刻板地遵照某种设计模式而对代码进行过度的设计。\n- Yii 是一个全栈框架，提供了大量久经考验，开箱即用的特性：\n  对关系型和 NoSQL 数据库都提供了查询生成器和 \n  ActiveRecord；RESTful API 的开发支持；多层缓存支持，等等。\n- Yii 非常易于扩展。你可以自定义或替换几乎任何一处核心代码。你还会受益于 Yii \n  坚实可靠的扩展架构，使用、再开发或再发布扩展。\n- 高性能始终是 Yii 的首要目标之一。\n\nYii 不是一场独角戏，它由一个[强大的开发者团队](https://www.yiiframework.com/team)提供支持，\n也有一个庞大的专家社区，持续不断地对 Yii 的开发作出贡献。\nYii 开发者团队始终对 Web 开发趋势和其他框架及项目中的最佳实践和特性保持密切关注，\n那些有意义的最佳实践及特性会被不定期的整合进核心框架中，\n并提供简单优雅的接口。\n\n\nYii 版本\n------------\n\nYii 当前有两个主要版本：1.1 和 2.0。 1.1 版是上代的老版本，现在处于维护状态。\n2.0 版是一个完全重写的版本，采用了最新的技术和协议，包括依赖包管理器 Composer、PHP 代码规范 PSR、命名空间、Traits（特质）等等。 \n2.0 版代表新一代框架，是未来几年中我们的主要开发版本。\n本指南主要基于 2.0 版编写。\n\n\n系统要求和先决条件\n------------------------------\n\nYii 2.0 需要 PHP 7.4.0 或以上版本支持。你可以通过运行任何\nYii 发行包中附带的系统要求检查器查看每个具体特性所需的 PHP 配置。\n\n使用 Yii 需要对面向对象编程（OOP）有基本了解，因为 Yii 是一个纯面向对象的框架。Yii 2.0 还使用了 PHP 的最新特性，\n例如[命名空间](https://www.php.net/manual/zh/language.namespaces.php)\n和[Trait（特质）](https://www.php.net/manual/zh/language.oop5.traits.php)。\n理解这些概念将有助于你更快地掌握 Yii 2.0。\n\n"
  },
  {
    "path": "docs/guide-zh-CN/output-client-scripts.md",
    "content": "客户端脚本使用\n===========================\n\n现代 Web 应用程序，\n除了呈现并发送到浏览器的静态 HTML 页面外，\n还包含 JavaScript，\n用于通过操纵现有元素或通过 AJAX 加载新内容来修改浏览器中的页面。\n本节介绍 Yii 提供的用于向网站添加 JavaScript 和 CSS 以及动态调整它们的方法。\n\n## 注册脚本 <span id=\"register-scripts\"></span>\n\n使用 [[yii\\web\\View]] 对象时，可以动态注册前端脚本。\n这里有两个专门的方法：\n\n- [[yii\\web\\View::registerJs()|registerJs()]] 用于内联脚本。\n- [[yii\\web\\View::registerJsFile()|registerJsFile()]] 用于注册引入外部脚本文件。\n\n### 注册内联脚本 <span id=\"inline-scripts\"></span>\n\n内联脚本对于配置，动态生成的代码以及由 [widgets](structure-widgets.md) 中包含的可重用前端代码创建的小片段非常有用。\n[[yii\\web\\View::registerJs()|registerJs()]] 添加这些方法可以像如下使用：\n\n```php\n$this->registerJs(\n    \"$('#myButton').on('click', function() { alert('Button clicked!'); });\",\n    View::POS_READY,\n    'my-button-handler'\n);\n```\n\n第一个参数是我们想插入的实际 JS 代码。\n它将被包含到 `<script>` 标签中。第二个参数确定脚本应插入页面的位置。\n可能的值是：\n\n- [[yii\\web\\View::POS_HEAD|View::POS_HEAD]] 用在 HEAD 部分。\n- [[yii\\web\\View::POS_BEGIN|View::POS_BEGIN]] 用在 `<body>` 标签的右边。\n- [[yii\\web\\View::POS_END|View::POS_END]] 用在 `</body>` 标签的左边。\n- [[yii\\web\\View::POS_READY|View::POS_READY]] 为了在 [`ready` 事件](https://learn.jquery.com/using-jquery-core/document-ready/) 中执行代码。\n  这里将自动注册 [[yii\\web\\JqueryAsset|jQuery]] 并将代码包装到适当的 jQuery 代码中。这是默认位置。\n- [[yii\\web\\View::POS_LOAD|View::POS_LOAD]] 为了在 [`load` 事件](https://learn.jquery.com/using-jquery-core/document-ready/) 中执行代码。\n  与上面相同，这也将自动注册 [[yii\\web\\JqueryAsset|jQuery]]。\n\n最后一个参数是一个唯一的脚本 ID，主要是用于标识一段代码块，在添加一段新的代码块时，如果当前页面已经存在同样 ID 代码块时，那么将会被新的替换。\n如果你不传这个参数，JS 代码本身将会作为 ID 来使用。\n\n### 注册脚本文件 <span id=\"script-files\"></span>\n\n[[yii\\web\\View::registerJsFile()|registerJsFile()]] 的参数类似于\n[[yii\\web\\View::registerCssFile()|registerCssFile()]]。在以下示例中，\n我们注册 `main.js` 文件，依赖于 [[yii\\web\\JqueryAsset]]。这意味着 `main.js`\n文件将在 `jquery.js` 之后添加。没有这样的依赖规范，\n`main.js` 和 `jquery.js` 之间的相对顺序将是未定义的，代码将不起作用。\n\n外部脚本的引入使用像下面这样：\n\n```php\n$this->registerJsFile(\n    '@web/js/main.js',\n    ['depends' => [\\yii\\web\\JqueryAsset::class]]\n);\n```\n\n这将为 [base URL](concept-aliases.md#predefined-aliases) 下的 `/js/main.js` 脚本添加一个标记。\n\n我们强烈建议使用 [asset bundles](structure-assets.md) 来注册外部 JS 文件而非使用 [[yii\\web\\View::registerJsFile()|registerJsFile()]] 来注册。 因为这些允许更好的灵活性和更精细的依赖配置。\n此外，使用资源包允许您组合和压缩多个 JS 文件，这对于高流量网站来说是比较理想的方式。\n\n## 注册 CSS <span id=\"register-css\"></span>\n\n与 JavaScript 类似，您可以使用注册 CSS 使用\n[[yii\\web\\View::registerCss()|registerCss()]] 或\n[[yii\\web\\View::registerCssFile()|registerCssFile()]]。\n前者注册一个 CSS 代码块，而后者注册一个外部 CSS 文件。\n\n### 注册内联 CSS <span id=\"inline-css\"></span>\n\n```php\n$this->registerCss(\"body { background: #f00; }\");\n```\n\n上面的代码将导致将以下内容添加到页面的 `<head>` 部分：\n\n```html\n<style>\nbody { background: #f00; }\n</style>\n```\n\n如果要指定样式标记的其他属性，请将 name-values 数组传递给第二个参数。\n最后一个参数是一个唯一的 ID，用于标识样式块，并确保仅在代码中的不同位置注册相同样式时仅添加一次。\n\n### 注册 CSS 文件 <span id=\"css-files\"></span>\n\n可以使用以下方法注册 CSS 文件：\n\n```php\n$this->registerCssFile(\"@web/css/themes/black-and-white.css\", [\n    'depends' => [\\yii\\bootstrap\\BootstrapAsset::class],\n    'media' => 'print',\n], 'css-print-theme');\n```\n\n上面的代码将把 `/css/themes/black-and-white.css` 文件的链接添加到页面的 `<head>` 部分。\n\n* 第一个参数指明被注册的 CSS 文件。\n* 第二个参数指明 `<link>` 标签的 HTML 属性，选项 `depends` 是专门处理\n  指明 CSS 文件依赖于哪个资源包。在这种情况下，依赖资源包就是\n  [[yii\\bootstrap\\BootstrapAsset|BootstrapAsset]]。这意味着 CSS 文件将\n  被添加在 [[yii\\bootstrap\\BootstrapAsset|BootstrapAsset]] *之后*。\n* 最后一个参数指明一个 ID 来标识这个 CSS 文件。\n  如果参数未提供，则将使用 CSS 文件的 URL。\n\n\n我们强烈建议使用 [asset bundles](structure-assets.md) 来注册外部 CSS 文件，\n而非使用 [[yii\\web\\View::registerCssFile()|registerCssFile()]] 来注册。\n使用资源包允许你合并并且压缩多个 CSS 文件，对于高流量的网站来说，这是比较理想的方式。\n它还提供了更大的灵活性，因为应用程序的所有资源依赖性都在一个位置配置。\n\n\n## 注册资源包 <span id=\"asset-bundles\"></span>\n\n如前所述，建议使用资源包而不是直接注册 CSS 和 JavaScript 文件。\n您可以获取有关如何定义资源包的详细信息在\n[\"Assets\" 部分](structure-assets.md).\n至于使用已定义的资源包，它非常简单：\n\n```php\n\\frontend\\assets\\AppAsset::register($this);\n```\n\n在上面的代码中，在视图文件的上下文中，`AppAsset` 包在当前视图上注册（由 `$this` 表示）。\n从小部件中注册资产包时，您将传递小部件的\n[[yii\\base\\Widget::$view|$view]] 来替代（`$this->view`）。\n\n\n## 生成动态 Javascript <span id=\"dynamic-js\"></span>\n\n在视图文件中，HTML 代码通常不直接写出，\n而是由某些依赖于视图变量的 PHP 代码生成。\n为了使用 Javascript 操作生成的 HTML，JS 代码也必须包含动态部分，例如 jQuery 选择器的 ID。\n\n要将PHP变量插入 JS 代码，\n必须正确转义它们的值。\n特别是当 JS 代码插入 HTML 而不是驻留在专用的 JS 文件中时。\n为此，Yii 提供了 [[yii\\helpers\\Json|Json]] 助手类的 [[yii\\helpers\\Json::htmlEncode()|htmlEncode()]] 方法，其用法将在以下示例中显示。\n\n### 注册全局 JavaScript 配置 <span id=\"js-configuration\"></span>\n\n在这个例子中，\n我们使用一个数组将全局配置参数从应用程序的 PHP 部分传递给 JS 前端代码。\n\n```php\n$options = [\n    'appName' => Yii::$app->name,\n    'baseUrl' => Yii::$app->request->baseUrl,\n    'language' => Yii::$app->language,\n    // ...\n];\n$this->registerJs(\n    \"var yiiOptions = \".\\yii\\helpers\\Json::htmlEncode($options).\";\",\n    View::POS_HEAD,\n    'yiiOptions'\n);\n```\n\n上面的代码将注册一个包含 JavaScript 的 `<script>` 标签定义，\n例如：\n\n```javascript\nvar yiiOptions = {\"appName\":\"My Yii Application\",\"baseUrl\":\"/basic/web\",\"language\":\"en\"};\n```\n\n在您的 JavaScript 代码中，您现在可以像 `yiiOptions.baseUrl` 或 `yiiOptions.language` 一样访问它们。\n\n### 传递翻译的消息 <span id=\"translated-messages\"></span>\n\n您可能会遇到 JavaScript 需要打印消息以响应某些事件的情况。在使用多种语言的应用程序中，此字符串必须转换为当前的应用程序语言。\n实现此目的的一种方法是使用 Yii 提供的\n[消息转换功能](tutorial-i18n.md#message-translation) 并将结果传递给 JavaScript 代码。\n\n```php\n$message = \\yii\\helpers\\Json::htmlEncode(\n    \\Yii::t('app', 'Button clicked!')\n);\n$this->registerJs(<<<JS\n    $('#myButton').on('click', function() { alert( $message ); });\nJS\n);\n```\n\n上面的示例代码使用 PHP\n[Heredoc 语法](https://www.php.net/manual/zh/language.types.string.php#language.types.string.syntax.heredoc) 以获得更好的可读性。\n这也可以在大多数 IDE 中实现更好的语法突出显示，因此它是编写内联 JavaScript 的首选方式，对于长于单行的代码尤其有用。变量 `$message` 是在 PHP\n中创建的，感谢 [[yii\\helpers\\Json::htmlEncode|Json::htmlEncode]]\n它包含有效 JS 语法中的字符串，可以将其插入到 JavaScript 代码中以放置 函数中的动态字符串调用 `alert()`。\n\n> Note: 使用 Heredoc 时，请注意 JS 代码中的变量命名，\n> 因为以 `$` 开头的变量可能会被解释为 PHP 变量，\n> 这些变量将被其内容替换。\n> jQuery 函数以 `$(` 或 `$.` 的形式不被解释为 PHP 变量，\n> 可以安全地使用。\n\n## `yii.js` 脚本 <span id=\"yii.js\"></span>\n\n> Note: 本节尚未编写。 它应该包含 `yii.js` 提供的功能的说明：\n> \n> - Yii JavaScript 模块\n> - CSRF 参数处理\n> - `data-confirm` 处理\n> - `data-method` 处理\n> - 脚本过滤\n> - 重定向处理\n\n"
  },
  {
    "path": "docs/guide-zh-CN/output-data-providers.md",
    "content": "数据提供者\n==============\n\n在 [Pagination](output-pagination.md) 和 [Sorting](output-sorting.md) 部分,\n我们已经介绍了如何允许终端用户选择一个特定的数据页面，根据一些字段对它们进行展现与排序。\n因为分页和排序数据的任务是很常见的，所以Yii提供了一组封装好的*data provider*类。\n\n数据提供者是一个实现了 [[yii\\data\\DataProviderInterface]] 接口的类。\n它主要用于获取分页和数据排序。它经常用在 [data widgets](output-data-widgets.md) \n小物件里，方便终端用户进行分页与数据排序。\n\n下面的数据提供者类都包含在Yii的发布版本里面：\n\n* [[yii\\data\\ActiveDataProvider]]：使用 [[yii\\db\\Query]] 或者 [[yii\\db\\ActiveQuery]] 从数据库查询数据并且以数组项的方式或者\n  [Active Record](db-active-record.md) 实例的方式返回。\n* [[yii\\data\\SqlDataProvider]]：执行一段SQL语句并且将数据库数据作为数组返回。\n* [[yii\\data\\ArrayDataProvider]]：将一个大的数组依据分页和排序规格返回一部分数据。\n\n\n所有的这些数据提供者遵守以下模式：\n\n```php\n// 根据配置的分页以及排序属性来创建一个数据提供者\n$provider = new XyzDataProvider([\n    'pagination' => [...],\n    'sort' => [...],\n]);\n\n// 获取分页和排序数据\n$models = $provider->getModels();\n\n// 在当前页获取数据项的数目\n$count = $provider->getCount();\n\n// 获取所有页面的数据项的总数\n$totalCount = $provider->getTotalCount();\n```\n\n你可以通过配置 [[yii\\data\\BaseDataProvider::pagination|pagination]] 和 \n[[yii\\data\\BaseDataProvider::sort|sort]]的属性来设定数据提供者的分页和排序行为。\n属性分别对应于 [[yii\\data\\Pagination]] 和 [[yii\\data\\Sort]]。\n你也可以对它们配置false来禁用分页和排序特性。\n\n[Data widgets](output-data-widgets.md),诸如 [[yii\\grid\\GridView]]，有一个属性名叫 `dataProvider` ，这个属性能够提供一个\n数据提供者的示例并且可以显示所提供的数据，例如，\n\n```php\necho yii\\grid\\GridView::widget([\n    'dataProvider' => $dataProvider,\n]);\n```\n\n这些数据提供者的主要区别在于数据源的指定方式上。在下面的部分，\n我们将详细介绍这些数据提供者的使用方法。\n\n\n## 活动数据提供者 <span id=\"active-data-provider\"></span>\n\n为了使用 [[yii\\data\\ActiveDataProvider]]，你应该配置其 [[yii\\data\\ActiveDataProvider::query|query]] 的属性。\n它既可以是一个 [[yii\\db\\Query]] 对象，又可以是一个 [[yii\\db\\ActiveQuery]] 对象。假如是前者，返回的数据将是数组；\n如果是后者，返回的数据可以是数组也可以是 [Active Record](db-active-record.md) 对象。\n例如，\n\n```php\nuse yii\\data\\ActiveDataProvider;\n\n$query = Post::find()->where(['status' => 1]);\n\n$provider = new ActiveDataProvider([\n    'query' => $query,\n    'pagination' => [\n        'pageSize' => 10,\n    ],\n    'sort' => [\n        'defaultOrder' => [\n            'created_at' => SORT_DESC,\n            'title' => SORT_ASC,\n        ]\n    ],\n]);\n\n// 返回一个Post实例的数组\n$posts = $provider->getModels();\n```\n\n假如在上面的例子中，`$query` 用下面的代码来创建，则数据提供者将返回原始数组。\n\n```php\nuse yii\\db\\Query;\n\n$query = (new Query())->from('post')->where(['status' => 1]);\n```\n\n> Note: 假如查询已经指定了 `orderBy` 从句，则终端用户给定的新的排序说明（通过 `sort` 来配置的）\n  将被添加到已经存在的从句中。任何已经存在的 `limit` 和 `offset` 从句都将被终端用户所请求的分页\n  （通过 `pagination` 所配置的）所重写。\n\n默认情况下，[[yii\\data\\ActiveDataProvider]]使用 `db` 应用组件来作为数据库连接。你可以通过配置 [[yii\\data\\ActiveDataProvider::db]]\n的属性来使用不同数据库连接。\n\n\n## SQL数据提供者 <span id=\"sql-data-provider\"></span>\n\n[[yii\\data\\SqlDataProvider]] 应用的时候需要结合需要获取数据的SQL语句。\n基于 [[yii\\data\\SqlDataProvider::sort|sort]] 和\n[[yii\\data\\SqlDataProvider::pagination|pagination]] 规格，\n数据提供者会根据所请求的数据页面（期望的顺序）来调整 `ORDER BY` 和 `LIMIT` 的SQL从句。\n\n为了使用 [[yii\\data\\SqlDataProvider]]，你应该指定 [[yii\\data\\SqlDataProvider::sql|sql]] 属性以及\n[[yii\\data\\SqlDataProvider::totalCount|totalCount]] 属性，例如，\n\n```php\nuse yii\\data\\SqlDataProvider;\n\n$count = Yii::$app->db->createCommand('\n    SELECT COUNT(*) FROM post WHERE status=:status\n', [':status' => 1])->queryScalar();\n\n$provider = new SqlDataProvider([\n    'sql' => 'SELECT * FROM post WHERE status=:status',\n    'params' => [':status' => 1],\n    'totalCount' => $count,\n    'pagination' => [\n        'pageSize' => 10,\n    ],\n    'sort' => [\n        'attributes' => [\n            'title',\n            'view_count',\n            'created_at',\n        ],\n    ],\n]);\n\n// 返回包含每一行的数组\n$models = $provider->getModels();\n```\n\n> Info: [[yii\\data\\SqlDataProvider::totalCount|totalCount]] 的属性只有你需要\n  分页数据的时候才会用到。这是因为通过 [[yii\\data\\SqlDataProvider::sql|sql]] \n  指定的SQL语句将被数据提供者所修改并且只返回当\n  前页面数据。数据提供者为了正确计算可用页面的数量仍旧需要知道数据项的总数。\n\n\n## 数组数据提供者 <span id=\"array-data-provider\"></span>\n\n[[yii\\data\\ArrayDataProvider]] 非常适用于大的数组。数据提供者允许你返回一个\n经过一个或者多个字段排序的数组数据页面。为了使用 [[yii\\data\\ArrayDataProvider]]，\n你应该指定 [[yii\\data\\ArrayDataProvider::allModels|allModels]] 属性作为一个大的数组。\n这个大数组的元素既可以是一些关联数组（例如：[DAO](db-dao.md)查询出来的结果）\n也可以是一些对象（例如：[Active Record](db-active-record.md)实例）\n例如,\n\n```php\nuse yii\\data\\ArrayDataProvider;\n\n$data = [\n    ['id' => 1, 'name' => 'name 1', ...],\n    ['id' => 2, 'name' => 'name 2', ...],\n    ...\n    ['id' => 100, 'name' => 'name 100', ...],\n];\n\n$provider = new ArrayDataProvider([\n    'allModels' => $data,\n    'pagination' => [\n        'pageSize' => 10,\n    ],\n    'sort' => [\n        'attributes' => ['id', 'name'],\n    ],\n]);\n\n// 获取当前请求页的每一行数据\n$rows = $provider->getModels();\n```\n\n> Note: 数组数据提供者与 [Active Data Provider](#active-data-provider) 和 [SQL Data Provider](#sql-data-provider) 这两者进行比较的话，\n  会发现数组数据提供者没有后面那两个高效，这是因为数组数据提供者需要加载*所有*的数据到内存中。\n\n\n## 数据键的使用 <span id=\"working-with-keys\"></span>\n\n当使用通过数据提供者返回的数据项的时候，你经常需要使用一个唯一键来标识每一个数据项。\n举个例子，假如数据项代表的是一些自定义的信息，你可能会使用自定义ID作为键去标识每一个自定义数据。\n数据提供者能够返回一个通过 [[yii\\data\\DataProviderInterface::getModels()]] 返回的键与数据项相对应的列表。\n例如，\n\n```php\nuse yii\\data\\ActiveDataProvider;\n\n$query = Post::find()->where(['status' => 1]);\n\n$provider = new ActiveDataProvider([\n    'query' => Post::find(),\n]);\n\n// 返回包含Post对象的数组\n$posts = $provider->getModels();\n\n// 返回与$posts相对应的主键值\n$ids = $provider->getKeys();\n```\n\n在上面的例子中，因为你提供给 [[yii\\data\\ActiveDataProvider]] 一个 [[yii\\db\\ActiveQuery]] 对象，\n它是足够智能地返回一些主键值作为键。你也可以明确指出键值应该怎样被计算出来，\n计算的方式是通过使用一个字段名或者一个可调用的计算键值来配置。\n例如，\n\n```php\n// 使用 \"slug\" 字段作为键值\n$provider = new ActiveDataProvider([\n    'query' => Post::find(),\n    'key' => 'slug',\n]);\n\n// 使用md5(id)的结果作为键值\n$provider = new ActiveDataProvider([\n    'query' => Post::find(),\n    'key' => function ($model) {\n        return md5($model->id);\n    }\n]);\n```\n\n\n## 创建自定义数据提供者 <span id=\"custom-data-provider\"></span>\n\n为了创建自定义的数据提供者类，你应该实现 [[yii\\data\\DataProviderInterface]] 接口。\n一个简单的方式是从 [[yii\\data\\BaseDataProvider]] 去扩展，这种方式允许你关注数据提供者的核心逻辑。\n这时，你主要需要实现下面的一些方法：\n\n- [[yii\\data\\BaseDataProvider::prepareModels()|prepareModels()]]：准备好在当前页面可用的数据模型，\n  并且作为一个数组返回它们。\n- [[yii\\data\\BaseDataProvider::prepareKeys()|prepareKeys()]]：接受一个当前可用的数据模型的数组，\n  并且返回一些与它们相关联的键。\n- [[yii\\data\\BaseDataProvider::prepareTotalCount()|prepareTotalCount]]: 在数据提供者中返回一个\n  标识出数据模型总数的值。\n\n下面是一个数据提供者的例子，这个数据提供者可以高效地读取CSV数据：\n\n```php\n<?php\nuse yii\\data\\BaseDataProvider;\n\nclass CsvDataProvider extends BaseDataProvider\n{\n    /**\n     * @var string 要读取的 CSV 文件的名称\n     */\n    public $filename;\n\n    /**\n     * @var string|callable 键列的名称或返回它的可调用列表\n     */\n    public $key;\n\n    /**\n     * @var SplFileObject\n     */\n    protected $fileObject; // SplFileObject 非常便于搜索文件中的特定行\n\n\n    /**\n     * @inheritdoc\n     */\n    public function init()\n    {\n        parent::init();\n\n        // open file\n        $this->fileObject = new SplFileObject($this->filename);\n    }\n\n    /**\n     * @inheritdoc\n     */\n    protected function prepareModels()\n    {\n        $models = [];\n        $pagination = $this->getPagination();\n\n        if ($pagination === false) {\n            // in case there's no pagination, read all lines\n            while (!$this->fileObject->eof()) {\n                $models[] = $this->fileObject->fgetcsv();\n                $this->fileObject->next();\n            }\n        } else {\n            // in case there's pagination, read only a single page\n            $pagination->totalCount = $this->getTotalCount();\n            $this->fileObject->seek($pagination->getOffset());\n            $limit = $pagination->getLimit();\n\n            for ($count = 0; $count < $limit; ++$count) {\n                $models[] = $this->fileObject->fgetcsv();\n                $this->fileObject->next();\n            }\n        }\n\n        return $models;\n    }\n\n    /**\n     * @inheritdoc\n     */\n    protected function prepareKeys($models)\n    {\n        if ($this->key !== null) {\n            $keys = [];\n\n            foreach ($models as $model) {\n                if (is_string($this->key)) {\n                    $keys[] = $model[$this->key];\n                } else {\n                    $keys[] = call_user_func($this->key, $model);\n                }\n            }\n\n            return $keys;\n        } else {\n            return array_keys($models);\n        }\n    }\n\n    /**\n     * @inheritdoc\n     */\n    protected function prepareTotalCount()\n    {\n        $count = 0;\n\n        while (!$this->fileObject->eof()) {\n            $this->fileObject->next();\n            ++$count;\n        }\n\n        return $count;\n    }\n}\n```\n\n## 使用数据过滤器过滤数据提供者 <span id=\"filtering-data-providers-using-data-filters\"></span>\n\n虽然您可以手动为活动数据提供构建条件，\n例如使用 [过滤数据](output-data-widgets.md#filtering-data) 和 [单独过滤表格](output-data-widgets.md#separate-filter-form) 数据小部件，\n但如果你需要灵活的过滤条件，Yii 的数据过滤器会非常有用。\n以下是数据过滤器使用的方式：\n\n```php\n$filter = new ActiveDataFilter([\n    'searchModel' => 'app\\models\\PostSearch'\n]);\n\n$filterCondition = null;\n\n// 您可以从任何来源加载过滤器。例如：\n// 如果你更喜欢请求体中的 JSON，\n// 使用 Yii::$app->request->getBodyParams() 如下：\nif ($filter->load(\\Yii::$app->request->get())) { \n    $filterCondition = $filter->build();\n    if ($filterCondition === false) {\n        // Serializer would get errors out of it\n        return $filter;\n    }\n}\n\n$query = Post::find();\nif ($filterCondition !== null) {\n    $query->andWhere($filterCondition);\n}\n\nreturn new ActiveDataProvider([\n    'query' => $query,\n]);\n```\n\n`PostSearch` 模型用于定义允许过滤的属性和值：\n\n```php\nuse yii\\base\\Model;\n\nclass PostSearch extends Model \n{\n    public $id;\n    public $title;\n    \n    public function rules()\n    {\n        return [\n            ['id', 'integer'],\n            ['title', 'string', 'min' => 2, 'max' => 200],            \n        ];\n    }\n}\n```\n\n数据过滤器非常灵活。您可以自定义构建的条件以及允许的运算符。\n更多的细节请查看 [[\\yii\\data\\DataFilter]] 的 API 文档。\n"
  },
  {
    "path": "docs/guide-zh-CN/output-data-widgets.md",
    "content": "数据小部件\n============\n\nYii提供了一套数据小部件 [widgets](structure-widgets.md) ，这些小部件可以用于显示数据。\n[DetailView](#detail-view) 小部件能够用于显示一条记录数据，\n[ListView](#list-view) 和 [GridView](#grid-view) 小部件能够用于显示一个拥有分页、\n排序和过滤功能的一个列表或者表格。\n\n\nDetailView <span id=\"detail-view\"></span>\n----------\n\n[[yii\\widgets\\DetailView|DetailView]] 小部件显示的是单一 [[yii\\widgets\\DetailView::$model|model]] 数据的详情。\n\n它非常适合用常规格式显示一个模型（例如在一个表格的一行中显示模型的每个属性）。\n这里说的模型可以是 [[\\yii\\base\\Model]] 或者其子类的一个实例，例如子类 [active record](db-active-record.md)，也可以是一个关联数组。\n\nDetailView使用 [[yii\\widgets\\DetailView::$attributes|$attributes]] 属性来决定显示模型哪些属性以及如何格式化。\n可用的格式化选项，见 [formatter section](output-formatting.md) 章节。\n\n一个典型的DetailView的使用方法如下：\n \n```php\necho DetailView::widget([\n    'model' => $model,\n    'attributes' => [\n        'title',                                           // title attribute (in plain text)\n        'description:html',                                // description attribute formatted as HTML\n        [                                                  // the owner name of the model\n            'label' => 'Owner',\n            'value' => $model->owner->name,            \n            'contentOptions' => ['class' => 'bg-red'],     // HTML attributes to customize value tag\n            'captionOptions' => ['tooltip' => 'Tooltip'],  // HTML attributes to customize label tag\n        ],\n        'created_at:datetime',                             // creation date formatted as datetime\n    ],\n]);\n```\n\n请记住，与处理一组模型的 [[yii\\widgets\\GridView|GridView]] 不同，\n[[yii\\widgets\\DetailView|DetailView]] 只处理一个。\n因为 `$model` 是唯一一个用于显示的模型，并且可以作为变量在视图中使用。\n\n但是有些情况下可以使闭包有用。\n例如指定了 `visible`，并且你不想让`value` 的结果为 `false`：\n\n```php\necho DetailView::widget([\n    'model' => $model,\n    'attributes' => [\n        [\n            'attribute' => 'owner',\n            'value' => function ($model) {\n                return $model->owner->name;\n            },\n            'visible' => \\Yii::$app->user->can('posts.owner.view'),\n        ],\n    ],\n]);\n```\n\nListView <span id=\"list-view\"></span>\n--------\n\n[[yii\\widgets\\ListView|ListView]] 小部件用于显示数据提供者 [data provider](output-data-providers.md) 提供的数据。\n每个数据模型用指定的视图文件 [[yii\\widgets\\ListView::$itemView|view file]] 来渲染。\n因为它提供开箱即用式的（译者注：封装好的）分页、排序以及过滤这样一些特性，\n所以它可以很方便地为最终用户显示信息并同时创建数据管理界面。\n\n一个典型的用法如下例所示：\n\n```php\nuse yii\\widgets\\ListView;\nuse yii\\data\\ActiveDataProvider;\n\n$dataProvider = new ActiveDataProvider([\n    'query' => Post::find(),\n    'pagination' => [\n        'pageSize' => 20,\n    ],\n]);\necho ListView::widget([\n    'dataProvider' => $dataProvider,\n    'itemView' => '_post',\n]);\n```\n\n`_post` 视图文件可包含如下代码：\n\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\helpers\\HtmlPurifier;\n?>\n<div class=\"post\">\n    <h2><?= Html::encode($model->title) ?></h2>\n    \n    <?= HtmlPurifier::process($model->text) ?>    \n</div>\n```\n\n在上面的视图文件中，当前的数据模型 `$model` 是可用的。另外，下面的这些变量也是可用的：\n\n- `$key`：混合类型，键的值与数据项相关联。\n- `$index`：整型，是由数据提供者返回的数组中以0起始的数据项的索引。\n- `$widget`：类型是ListView，是小部件的实例。\n\n假如你需要传递附加数据到每一个视图中，你可以像下面这样用 [[yii\\widgets\\ListView::$viewParams|$viewParams]] \n属性传递键值对：\n\n```php\necho ListView::widget([\n    'dataProvider' => $dataProvider,\n    'itemView' => '_post',\n    'viewParams' => [\n        'fullView' => true,\n        'context' => 'main-page',\n        // ...\n    ],\n]);\n```\n\n在视图中，上述这些附加数据也是可以作为变量来使用的。\n\n\nGridView <span id=\"grid-view\"></span>\n--------\n\n数据网格或者说 GridView 小部件是Yii中最强大的部件之一。如果你需要快速建立系统的管理后台，\nGridView 非常有用。它从数据提供者 [data provider](output-data-providers.md) 中取得数据并使用 \n[[yii\\grid\\GridView::columns|columns]] 属性的一组列配置，在一个表格中渲染每一行数据。\n\n表中的每一行代表一个数据项的数据，并且一列通常表示该项的属性\n（某些列可以对应于属性或静态文本的复杂表达式）。\n\n使用GridView的最少代码如下：\n\n```php\nuse yii\\grid\\GridView;\nuse yii\\data\\ActiveDataProvider;\n\n$dataProvider = new ActiveDataProvider([\n    'query' => Post::find(),\n    'pagination' => [\n        'pageSize' => 20,\n    ],\n]);\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n]);\n```\n\n上面的代码首先创建了一个数据提供者，然后使用GridView显示每一行的每个属性，每一行的数据是从数据提供者取来的。\n展现出来的表格封装了排序以及分页功能。\n\n\n### 表格列\n\n表格的列是通过 [[yii\\grid\\Column]] 类来配置的，这个类是通过 GridView 配置项中的 [[yii\\grid\\GridView::columns|columns]] \n属性配置的。根据列的类别和设置的不同，各列能够以不同方式展示数据。\n默认的列类是 [[yii\\grid\\DataColumn]]，用于展现模型的某个属性，\n并且可以排序和过滤。\n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'columns' => [\n        ['class' => 'yii\\grid\\SerialColumn'],\n        // 数据提供者中所含数据所定义的简单的列\n        // 使用的是模型的列的数据\n        'id',\n        'username',\n        // 更复杂的列数据\n        [\n            'class' => 'yii\\grid\\DataColumn', //由于是默认类型，可以省略 \n            'value' => function ($data) {\n                return $data->name; // 如果是数组数据则为 $data['name'] ，例如，使用 SqlDataProvider 的情形。\n            },\n        ],\n    ],\n]);\n```\n\n请注意，假如配置中没有指定 [[yii\\grid\\GridView::columns|columns]] 属性，\n那么 Yii 会试图显示数据提供者的模型中所有可能的列。\n\n\n### 列类\n\n通过使用不同类，网格列可以自定义：\n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'columns' => [\n        [\n            'class' => 'yii\\grid\\SerialColumn', // <-- 这里\n            // 你还可以在此配置其他属性\n        ],\n```\n\n除了我们下面将要展开讨论的Yii自带的列类，你还可以创建你自己的列类。\n\n每个列类是从 [[yii\\grid\\Column]] 扩展而来，\n从而在配置网格列的时候，你可以设置一些公共的选项。\n\n- [[yii\\grid\\Column::header|header]] 允许为头部行设置内容。\n- [[yii\\grid\\Column::footer|footer]] 允许为尾部行设置内容。\n- [[yii\\grid\\Column::visible|visible]] 定义某个列是否可见。\n- [[yii\\grid\\Column::content|content]] 允许你传递一个有效的PHP回调来为一行返回数据，格式如下：\n\n  ```php\n  function ($model, $key, $index, $column) {\n      return 'a string';\n  }\n  ```\n  \n你可以传递数组来指定各种容器式的HTML选项：\n\n- [[yii\\grid\\Column::headerOptions|headerOptions]]\n- [[yii\\grid\\Column::footerOptions|footerOptions]]\n- [[yii\\grid\\Column::filterOptions|filterOptions]]\n- [[yii\\grid\\Column::contentOptions|contentOptions]]\n\n\n#### 数据列 <span id=\"data-column\"></span>\n\n[[yii\\grid\\DataColumn|Data column]] 用于显示和排序数据。这是默认的列的类型，\n所以在使用 DataColumn 为列类时，可省略类的指定（译者注：不需要'class'选项的意思）。\n\n数据列的主要配置项是 [[yii\\grid\\DataColumn::format|format]] 属性。它的值对应于 `formatter` [application component](structure-application-components.md) 应用组件里面的一些方法，\n默认是使用 [[\\yii\\i18n\\Formatter|Formatter]] 应用组件：\n\n```php\necho GridView::widget([\n    'columns' => [\n        [\n            'attribute' => 'name',\n            'format' => 'text'\n        ],\n        [\n            'attribute' => 'birthday',\n            'format' => ['date', 'php:Y-m-d']\n        ],\n        'created_at:datetime', // shortcut format\n        [\n            'label' => 'Education',\n            'attribute' => 'education',\n            'filter' => ['0' => 'Elementary', '1' => 'Secondary', '2' => 'Higher'],\n            'filterInputOptions' => ['prompt' => 'All educations', 'class' => 'form-control', 'id' => null]\n        ],\n    ],\n]);\n```\n\n在上述代码中，`text` 对应于 [[\\yii\\i18n\\Formatter::asText()]]。列的值作为第一个参数传递。\n在第二列的定义中，`date` 对应于 [[\\yii\\i18n\\Formatter::asDate()]]。\n该列的值再次作为第一个参数传递同时 'php:Y-m-d' 被用作第二个参数。\n\n有关可用格式化程序的列表，请参阅 [关于数据格式的部分](output-formatting.md)。\n\n对于配置数据列，还有一种快捷方式格式，\n请参阅 API 文档 [[yii\\grid\\GridView::columns|columns]]。\n\n使用 [[yii\\grid\\DataColumn::filter|filter]] 和 [[yii\\grid\\DataColumn::filterInputOptions|filterInputOptions]]\n去控制过滤器输入的 HTML。\n\n默认情况下，列的头部有 [[yii\\data\\Sort::link]] 来呈现。它还可以使用 [[yii\\grid\\Column::header]] 来调整。\n要更改头部文本，您应该像上面的示例中那样设置 [[yii\\grid\\DataColumn::$label]]。\n默认情况下，标签应该从数据模型中填充。更多细节请参阅 [[yii\\grid\\DataColumn::getHeaderCellLabel]]。\n\n#### 动作列 \n\n[[yii\\grid\\ActionColumn|Action column]] 用于显示一些动作按钮，如每一行的更新、删除操作。\n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'columns' => [\n        [\n            'class' => 'yii\\grid\\ActionColumn',\n            // 您可以在此处配置其他属性\n        ],\n```\n\n可配置的属性如下：\n\n- [[yii\\grid\\ActionColumn::controller|controller]] 是应该执行这些动作的控制器ID。\n  如果没有设置，它将使用当前控制器。\n- [[yii\\grid\\ActionColumn::template|template]] 定义在动作列中使用的构建每个单元格的模板。\n  在大括号内括起来的的令牌被当做是控制器的 action 方法ID (在动作列的上下文中也称作*按钮名称*)。\n  它们将会被 [[yii\\grid\\ActionColumn::$buttons|buttons]] 中指定的对应按钮的关联的渲染回调函数替代。\n  例如，令牌 `{view}` 将被 `buttons['view']` 关联的渲染回调函数的返回结果所替换。\n  如果没有找到回调函数，令牌将被替换成一个空串。默认的令牌有 `{view} {update} {delete}` 。\n- [[yii\\grid\\ActionColumn::buttons|buttons]] 是一个按钮的渲染回调数数组。数组中的键是按钮的名字（没有花括号），并且值是对应的按钮渲染回调函数。\n  这些回调函数须使用下面这种原型：\n\n  ```php\n  function ($url, $model, $key) {\n      // return the button HTML code\n  }\n  ```\n\n  在上面的代码中，`$url` 是列为按钮创建的URL，`$model`是当前要渲染的模型对象，\n  并且 `$key` 是在数据提供者数组中模型的键。\n\n- [[yii\\grid\\ActionColumn::urlCreator|urlCreator]] 是使用指定的模型信息来创建一个按钮URL的回调函数。\n  该回调的原型和 [[yii\\grid\\ActionColumn::createUrl()]] 是一样的。\n  假如这个属性没有设置，按钮的URL将使用 [[yii\\grid\\ActionColumn::createUrl()]] 来创建。\n- [[yii\\grid\\ActionColumn::visibleButtons|visibleButtons]] 是控制每个按钮可见性条件的数组。\n  数组键是按钮名称 (没有大括号)，值是布尔值 true/false 或匿名函数。\n  如果在数组中没有指定按钮名称，将会按照默认的来显示。\n  回调必须像如下这样来使用：\n\n  ```php\n  function ($model, $key, $index) {\n      return $model->status === 'editable';\n  }\n  ```\n\n  或者你可以传递一个布尔值：\n\n  ```php\n  [\n      'update' => \\Yii::$app->user->can('update')\n  ]\n  ```\n\n#### 复选框列 \n\n[[yii\\grid\\CheckboxColumn|Checkbox column]] 显示一个复选框列。\n\n想要添加一个复选框到网格视图中，将它添加到 [[yii\\grid\\GridView::$columns|columns]] 的配置中，如下所示：\n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'columns' => [\n        // ...\n        [\n            'class' => 'yii\\grid\\CheckboxColumn',\n            // 你可以在这配置更多的属性\n        ],\n    ],\n```\n\n用户可点击复选框来选择网格中的一些行。被选择的行可通过调用下面的\nJavaScript代码来获得：\n\n```javascript\nvar keys = $('#grid').yiiGridView('getSelectedRows');\n// keys 为一个由与被选行相关联的键组成的数组\n```\n\n#### 序号列 \n\n[[yii\\grid\\SerialColumn|Serial column]] 渲染行号，以 `1` 起始并自动增长。\n\n使用方法和下面的例子一样简单：\n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'columns' => [\n        ['class' => 'yii\\grid\\SerialColumn'], // <-- here\n        // ...\n```\n\n\n### 数据排序\n\n> Note: 这部分正在开发中。\n>\n> - https://github.com/yiisoft/yii2/issues/1576\n\n### 数据过滤\n\n为了过滤数据的 GridView 需要一个模型 [model](structure-models.md) 来 \n从过滤表单接收数据，以及调整数据提供者的查询对象，以满足搜索条件。\n使用活动记录 [active records](db-active-record.md) 时，通常的做法是\n创建一个能够提供所需功能的搜索模型类（可以使用 [Gii](start-gii.md) 来生成）。\n这个类为搜索定义了验证规则并且提供了一个将会返回数据提供者对象\n的 `search()` 方法。\n\n为了给 `Post` 模型增加搜索能力，我们可以像下面的例子一样创建 `PostSearch` 模型：\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse Yii;\nuse yii\\base\\Model;\nuse yii\\data\\ActiveDataProvider;\n\nclass PostSearch extends Post\n{\n    public function rules()\n    {\n        // 只有在 rules() 函数中声明的字段才可以搜索\n        return [\n            [['id'], 'integer'],\n            [['title', 'creation_date'], 'safe'],\n        ];\n    }\n\n    public function scenarios()\n    {\n        // 旁路在父类中实现的 scenarios() 函数\n        return Model::scenarios();\n    }\n\n    public function search($params)\n    {\n        $query = Post::find();\n\n        $dataProvider = new ActiveDataProvider([\n            'query' => $query,\n        ]);\n\n        // 从参数的数据中加载过滤条件，并验证\n        if (!($this->load($params) && $this->validate())) {\n            return $dataProvider;\n        }\n\n        // 增加过滤条件来调整查询对象\n        $query->andFilterWhere(['id' => $this->id]);\n        $query->andFilterWhere(['like', 'title', $this->title])\n              ->andFilterWhere(['like', 'creation_date', $this->creation_date]);\n\n        return $dataProvider;\n    }\n}\n```\n\n> Tip: 请参阅 [Query Builder](db-query-builder.md) 尤其是 [Filter Conditions](db-query-builder.md#filter-conditions)\n> 去学习如何构建过滤查询。\n\n你可以在控制器中使用如下方法为网格视图获取数据提供者：\n\n```php\n$searchModel = new PostSearch();\n$dataProvider = $searchModel->search(Yii::$app->request->get());\n\nreturn $this->render('myview', [\n    'dataProvider' => $dataProvider,\n    'searchModel' => $searchModel,\n]);\n```\n\n然后你在视图中将 `$dataProvider` 和 `$searchModel` 对象分派给 GridView 小部件：\n\n```php\necho GridView::widget([\n    'dataProvider' => $dataProvider,\n    'filterModel' => $searchModel,\n    'columns' => [\n        // ...\n    ],\n]);\n```\n\n### 单独过滤表单\n\n大多数时候使用 GridView 标头过滤器就足够了，但是如果你需要一个单独的过滤器表单，你也可以很轻松的去添加。您可以使用以下内容创建部分视图 `_search.php`：\n\n\n```php\n<?php\n\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n/**\n * @var \\yii\\web\\View $this\n * @var \\app\\models\\PostSearch $model\n * @var \\yii\\widgets\\ActiveForm $form\n */\n?>\n\n<div class=\"post-search\">\n    <?php $form = ActiveForm::begin([\n        'action' => ['index'],\n        'method' => 'get',\n    ]); ?>\n\n    <?= $form->field($model, 'title') ?>\n\n    <?= $form->field($model, 'creation_date') ?>\n\n    <div class=\"form-group\">\n        <?= Html::submitButton('Search', ['class' => 'btn btn-primary']) ?>\n        <?= Html::submitButton('Reset', ['class' => 'btn btn-default']) ?>\n    </div>\n\n    <?php ActiveForm::end(); ?>\n</div>\n```\n\n并将其包含在 `index.php` 视图中，如下所示：\n\n```php\n<?= $this->render('_search', ['model' => $searchModel]) ?>\n```\n\n> Note: 如果使用 Gii 生成 CRUD 代码， 默认情况下会生成单独的过滤器表单（`_search.php`），\n  但是在 `index.php` 视图中已经被注释了。取消注释就可以用了!\n\n当您需要按字段过滤时，单独的过滤器表单很有用，这些字段不会在 GridView 中显示，也不适用于特殊筛选条件（如日期范围）。\n对于按日期范围过滤，\n我们可以将非 DB 属性 `createdFrom` 和 `createdTo` 添加到搜索模型：\n\n```php\nclass PostSearch extends Post\n{\n    /**\n     * @var string\n     */\n    public $createdFrom;\n\n    /**\n     * @var string\n     */\n    public $createdTo;\n}\n```\n\n在 `search()` 扩展查询条件的方法如下：\n\n```php\n$query->andFilterWhere(['>=', 'creation_date', $this->createdFrom])\n      ->andFilterWhere(['<=', 'creation_date', $this->createdTo]);\n```\n\n并将代表字段添加到过滤器表单：\n\n```php\n<?= $form->field($model, 'creationFrom') ?>\n\n<?= $form->field($model, 'creationTo') ?>\n```\n\n### 处理关系型模型\n\n当我们在一个网格视图中显示活动数据的时候，你可能会遇到这种情况，就是显示关联表的列的值，\n例如：发帖者的名字，而不是显示他的 `id`。当 `Post` 模型有一个关联的属性名（译者注： `Post` 模型中用 `hasOne` 定义 `getAuthor()` 函数）\n叫 `author` 并且作者模型（译者注：本例的作者模型是 `users` ）有一个属性叫 `name`，\n那么你可以通过在 [[yii\\grid\\GridView::$columns]] 中定义属性名为 `author.name` 来处理。\n这时的网格视图能显示作者名了，但是默认是不支持按作者名排序和过滤的。\n你需要调整上个章节介绍的 `PostSearch` 模型，以添加此功能。\n\n为了使关联列能够排序，你需要连接关系表，\n以及添加排序规则到数据提供者的排序组件中：\n\n```php\n$query = Post::find();\n$dataProvider = new ActiveDataProvider([\n    'query' => $query,\n]);\n\n// 连接与 `users` 表相关联的 `author` 表\n// 并将 `users` 表的别名设为 `author`\n$query->joinWith(['author' => function($query) { $query->from(['author' => 'users']); }]);\n// since version 2.0.7, the above line can be simplified to $query->joinWith('author AS author');\n// 使得关联字段可以排序\n$dataProvider->sort->attributes['author.name'] = [\n    'asc' => ['author.name' => SORT_ASC],\n    'desc' => ['author.name' => SORT_DESC],\n];\n\n// ...\n```\n\n过滤也需要像上面一样调用joinWith方法。你也需要在属性和规则中定义该列，就像下面这样：\n\n```php\npublic function attributes()\n{\n    // 添加关联字段到可搜索属性集合\n    return array_merge(parent::attributes(), ['author.name']);\n}\n\npublic function rules()\n{\n    return [\n        [['id'], 'integer'],\n        [['title', 'creation_date', 'author.name'], 'safe'],\n    ];\n}\n```\n\n然后在 `search()` 方法中，你仅需要添加一个额外过滤条件：\n\n```php\n$query->andFilterWhere(['LIKE', 'author.name', $this->getAttribute('author.name')]);\n```\n\n> Info: 在上面的代码中，我们使用相同的字符串作为关联名称和表别名；\n> 然而，当你的表别名和关联名称不相同的时候，你得注意在哪使用你的别名，在哪使用你的关联名称。\n> 一个简单的规则是在每个构建数据库查询的地方使用别名，而在所有其他和定义相关的诸如：\n> `attributes()` 和 `rules()` 等地方使用关联名称。\n> \n> 例如，你使用 `au` 作为作者关系表的别名，那么联查语句就要写成像下面这样：\n> \n> ```php\n> $query->joinWith(['author' => function($query) { $query->from(['au' => 'users']); }]);\n> ```\n>\n> 当别名已经在关联函数中定义了时，也可以只调用 `$query->joinWith(['author']);`。\n>\n> 在过滤条件中，别名必须使用，但属性名称保持不变：\n>\n> ```php\n> $query->andFilterWhere(['LIKE', 'au.name', $this->getAttribute('author.name')]);\n> ```\n> \n> 排序定义也同样如此：\n>\n> ```php\n> $dataProvider->sort->attributes['author.name'] = [\n>      'asc' => ['au.name' => SORT_ASC],\n>      'desc' => ['au.name' => SORT_DESC],\n> ];\n> ```\n>\n> 同样，当指定使用 [[yii\\data\\Sort::defaultOrder|defaultOrder]] 来排序的时候，\n>你需要使用关联名称替代别名：\n> \n> ```php\n> $dataProvider->sort->defaultOrder = ['author.name' => SORT_ASC];\n> ```\n\n> Info: 更多关于 `joinWith` 和在后台执行查询的相关信息，\n> 可以查看 [active record docs on joining with relations](db-active-record.md#joining-with-relations)。\n\n#### SQL 视图用于过滤、排序和显示数据\n\n还有另外一种方法可以更快、更有用的 SQL 视图。例如，我们要在 `GridView` \n中显示用户和他们的简介，可以这样创建 SQL 视图：\n\n```sql\nCREATE OR REPLACE VIEW vw_user_info AS\n    SELECT user.*, user_profile.lastname, user_profile.firstname\n    FROM user, user_profile\n    WHERE user.id = user_profile.user_id\n```\n\n然后你需要创建活动记录模型来代表这个视图：\n\n```php\n\nnamespace app\\models\\views\\grid;\n\nuse yii\\db\\ActiveRecord;\n\nclass UserView extends ActiveRecord\n{\n\n    /**\n     * @inheritdoc\n     */\n    public static function tableName()\n    {\n        return 'vw_user_info';\n    }\n\n    public static function primaryKey()\n    {\n        return ['id'];\n    }\n\n    /**\n     * @inheritdoc\n     */\n    public function rules()\n    {\n        return [\n            // 在这定义你的规则\n        ];\n    }\n\n    /**\n     * @inheritdoc\n     */\n    public static function attributeLabels()\n    {\n        return [\n            // 在这定义你的属性标签\n        ];\n    }\n\n\n}\n```\n\n之后你可以使用这个 UserView 活动记录和搜索模型，无需附加的排序和过滤属性的规则。\n所有属性都可开箱即用。请注意，这种方法有利有弊：\n\n- 你不需要指定不同排序和过滤条件，一切都包装好了；\n- 它可以更快，因为数据的大小，SQL 查询的执行（对于每个关联数据你都不需要额外的查询）都得到优化；\n- 因为在 SQL 视图中这仅仅是一个简单的映射UI，所以在你的实体中，它可能缺乏某方面的逻辑，所以，假如你有一些诸如 `isActive`、`isDeleted` 或者其他影响到 UI 的方法，\n  你也需要在这个类中复制他们。\n\n\n### 单个页面多个网格视图部件\n\n你可以在一个单独页面中使用多个网格视图，但是一些额外的配置是必须的，为的就是它们相互之间不干扰。\n当使用多个网格视图实例的时候，你必须要为生成的排序和分页对象配置不同的参数名，\n以便于每个网格视图有它们各自独立的排序和分页。\n你可以通过设置 [[yii\\data\\Sort::sortParam|sortParam]] 和 \n[[yii\\data\\Pagination::pageParam|pageParam]]，对应于数据提供者的\n[[yii\\data\\BaseDataProvider::$sort|sort]] 和 \n[[yii\\data\\BaseDataProvider::$pagination|pagination]] 实例。\n\n假如我们想要同时显示 `Post` 和 `User` 模型，这两个模型已经在 `$userProvider` 和 `$postProvider` 这两个数据提供者中准备好，\n具体做法如下：\n\n```php\nuse yii\\grid\\GridView;\n\n$userProvider->pagination->pageParam = 'user-page';\n$userProvider->sort->sortParam = 'user-sort';\n\n$postProvider->pagination->pageParam = 'post-page';\n$postProvider->sort->sortParam = 'post-sort';\n\necho '<h1>Users</h1>';\necho GridView::widget([\n    'dataProvider' => $userProvider,\n]);\n\necho '<h1>Posts</h1>';\necho GridView::widget([\n    'dataProvider' => $postProvider,\n]);\n```\n\n### 在 GridView 使用 Pjax\n\n[[yii\\widgets\\Pjax|Pjax]] 允许您更新页面的某个部分，\n而不是重新加载整个页面。\n使用过滤器时，可以使用它仅更新 [[yii\\grid\\GridView|GridView]] 内容。\n\n```php\nuse yii\\widgets\\Pjax;\nuse yii\\grid\\GridView;\n\nPjax::begin([\n    // PJax options\n]);\n    Gridview::widget([\n        // GridView options\n    ]);\nPjax::end();\n```\n\nPjax 也适用于 [[yii\\widgets\\Pjax|Pjax]] 小部件之间的链接以及\n[[yii\\widgets\\Pjax::$linkSelector|Pjax::$linkSelector]] 指定的链接。\n但是这可能是 [[yii\\grid\\ActionColumn|ActionColumn]] 链接的问题。\n要防止这种情况，请在编辑\n[[yii\\grid\\ActionColumn::$buttons|ActionColumn::$buttons]] 属性时将 HTML 属性 `data-pjax=\"0\"` 添加到链接中。\n\n#### 在 Gii 中使用 Pjax 的 GridView/ListView\n\n从 2.0.5 开始，[Gii](start-gii.md) 的 CRUD 生成器有一个 `$enablePjax` 选项，\n可以通过 web 界面或者命令行使用。\n\n```php\nyii gii/crud --controllerClass=\"backend\\\\controllers\\PostController\" \\\n  --modelClass=\"common\\\\models\\\\Post\" \\\n  --enablePjax=1\n```\n\n这会生成一个由 [[yii\\widgets\\Pjax|Pjax]] 小部件包含的\n[[yii\\grid\\GridView|GridView]] 或者 [[yii\\widgets\\ListView|ListView]]。\n\n延伸阅读\n---------------\n\n- [Rendering Data in Yii 2 with GridView and ListView](https://www.sitepoint.com/rendering-data-in-yii-2-with-gridview-and-listview/) by Arno Slatius.\n"
  },
  {
    "path": "docs/guide-zh-CN/output-formatting.md",
    "content": "数据格式器（Data Formatting）\n==========================\n\n你可以使用 `formatter` [application component](structure-application-components.md) 来格式化数据。\n默认 `fomatter` 由 [[yii\\i18n\\Formatter]] 来实现，这个组件提供了一系列关于日期/时间，数字，货币等的格式化方法。\n使用方法如下：\n\n```php\n$formatter = \\Yii::$app->formatter;\n\n// output: January 1, 2014\necho $formatter->asDate('2014-01-01', 'long');\n \n// output: 12.50%\necho $formatter->asPercent(0.125, 2);\n \n// output: <a href=\"mailto:cebe@example.com\">cebe@example.com</a>\necho $formatter->asEmail('cebe@example.com'); \n\n// output: Yes\necho $formatter->asBoolean(true); \n// it also handles display of null values:\n\n// output: (Not set)\necho $formatter->asDate(null); \n```\n\n我们可以看到，所有的方法都形似 `asXyz()`，这个 `Xzy` 就是所支持的格式化类型。\n当然你也可以使用类方法 [[yii\\i18n\\Formatter::format()|format()]] 来进行格式化，通过这个类方法，你可以更自由地控制格式化的数据，这时候，类方法通常配合 [[yii\\grid\\GridView]] 或者 [[yii\\widgets\\DetailView]] 来使用。\n\n举个例子：\n\n```php\n// output: January 1, 2014\necho Yii::$app->formatter->format('2014-01-01', 'date'); \n\n// 你可以在第二个参数指定一个数组，这个数组提供了一些配置的参数\n// 例如这个 2 就是 asPercent() 方法的 $decimals 参数\n// output: 12.50%\necho Yii::$app->formatter->format(0.125, ['percent', 2]); \n```\n\n> Note: `formatter` 组件用来格式化最终展示给用户的数据。 \n> 如果你想要将用户的输入进行格式化或者只是将一些别的日期数据进行格式化（这里的格式化说的是机器可读的格式化），\n> 不要使用这个组件，\n> 而应该使用 [[yii\\validators\\DateValidator]] 和 [[yii\\validators\\NumberValidator]] 进行用户输入格式化\n> 对于机器可读的日期和时间格式之间的简单转换，\n> PHP 方法 [date()](https://www.php.net/manual/zh/function.date.php) 就足够了。\n\n## 配置 Formatter（Configuring Formatter） <span id=\"configuring-formatter\"></span>\n\n可以对 `formatter` 组件在 [application configuration](concept-configurations.md#application-configurations) 中进行配置。\n例如，\n\n```php\nreturn [\n    'components' => [\n        'formatter' => [\n            'dateFormat' => 'dd.MM.yyyy',\n            'decimalSeparator' => ',',\n            'thousandSeparator' => ' ',\n            'currencyCode' => 'EUR',\n       ],\n    ],\n];\n```\n\n可以参考 [[yii\\i18n\\Formatter]] 的配置\n\n\n## 格式化时间/日期数据（Formatting Date and Time Values） <span id=\"date-and-time\"></span>\n\n默认支持一下几种格式化格式\n\n- [[yii\\i18n\\Formatter::asDate()|date]]：这个变量将被格式化为日期 `January 01, 2014`。\n- [[yii\\i18n\\Formatter::asTime()|time]]：这个变量将被格式化为时间 `14:23`。\n- [[yii\\i18n\\Formatter::asDatetime()|datetime]]：这个变量将被格式化为日期+时间 `January 01, 2014 14:23`。\n- [[yii\\i18n\\Formatter::asTimestamp()|timestamp]]：这个变量将被格式化为 UNIX 时间戳 [unix timestamp](https://zh.wikipedia.org/wiki/UNIX%E6%97%B6%E9%97%B4)，例如 `1412609982`。\n- [[yii\\i18n\\Formatter::asRelativeTime()|relativeTime]]：这个变量将被格式化为人类可读的\n  当前相对时间 `1 hour ago`。\n- [[yii\\i18n\\Formatter::asDuration()|duration]]：这个变量将被格式化为人类可读的时长 `1 day, 2 minutes`。\n\n时间/日期数据默认使用 [[yii\\i18n\\Formatter::asDate()|date]], [[yii\\i18n\\Formatter::asTime()|time]]， \n[[yii\\i18n\\Formatter::asDatetime()|datetime]] 方法进行格式化,\n你可以对他们进行一些自己的配置，只需在配置文件里配置 [[yii\\i18n\\Formatter::dateFormat|dateFormat]], \n[[yii\\i18n\\Formatter::timeFormat|timeFormat]], 和 [[yii\\i18n\\Formatter::datetimeFormat|datetimeFormat]] 即可。\n\n同时，你还可以配置它使用 [ICU syntax](https://unicode-org.github.io/icu/userguide/format_parse/datetime/)，\n同时你也可以配置它使用 [PHP date() 语法](https://www.php.net/manual/zh/function.date.php)，只需要加上 `php:` 前缀即可。\n例如，\n\n```php\n// ICU format\necho Yii::$app->formatter->asDate('now', 'yyyy-MM-dd'); // 2014-10-06\n\n// PHP date()-format\necho Yii::$app->formatter->asDate('now', 'php:Y-m-d'); // 2014-10-06\n```\n\n> Info: ICU 不支持 PHP 格式语法的某些字母，并且这些 PHP intl 扩展名不能在 Yii 格式化程序中使用。\n> 大多数（`w`，`t`，`L`，`B`，`u`，`I`，`Z`）对于格式化日期并不是真的有用，而是在进行数学计算时有用。\n> 然而 `S` 和 `U` 可能是有用的。他们的行为可以通过以下方式实现：\n>\n> - 比如 `S`，这是当月的英文序数后缀（例如：st，nd，rd 或 th。），可以使用以下代码来替换：\n>\n>   ```php\n>   $f = Yii::$app->formatter;\n>   $d = $f->asOrdinal($f->asDate('2017-05-15', 'php:j'));\n>   echo \"On the $d day of the month.\";  // 打印 \"On the 15th day of the month.\"\n>   ```\n>\n> - 比如 `U`，Unix 纪元，你能够使用 [[yii\\i18n\\Formatter::asTimestamp()|timestamp]] 来格式化。\n\n当使用需要支持多种语言的应用程序时，您经常需要为不同的区域设置指定不同的日期和时间格式。\n为了简化这项操作，你可以使用格式快捷键（例如：`long`，`short`）来代替。\n格式化程序将根据当前有效的地区的 [[yii\\i18n\\Formatter::locale|locale]] 将格式快捷方式转换为适当的程序。\n支持以下格式的快捷方式（示例是假设设置 locale 为 `en_GB` ）：\n\n- `short`：将输出日期 `06/10/2014` 和时间 `15:58`；\n- `medium`：将输出 `6 Oct 2014` 和 `15:58:42`；\n- `long`：将输出 `6 October 2014` 和 `15:58:42 GMT`；\n- `full`：将输出 `Monday, 6 October 2014` 和 `15:58:42 GMT`。\n\n版本 2.0.7 起，支持格式化日期为不同的系统时钟，\n请参阅格式化程序 [[yii\\i18n\\Formatter::$calendar|$calendar]] 的 API 文档 - 有关如何设置其他日历的属性。\n\n\n### 时区（Time Zones） <span id=\"time-zones\"></span>\n\n格式化时间/日期数据时，你会将他们转换成 [[yii\\i18n\\Formatter::timeZone|time zone]]\n这个时候，默认的时区为 UTC，除非你另外指定\n[[yii\\i18n\\Formatter::defaultTimeZone]]。\n\n下面使用 `Europe/Berlin` 作为默认 [[yii\\i18n\\Formatter::timeZone|time zone]] \n\n```php\n// formatting a UNIX timestamp as a time\necho Yii::$app->formatter->asTime(1412599260); // 14:41:00\n\n// formatting a datetime string (in UTC) as a time \necho Yii::$app->formatter->asTime('2014-10-06 12:41:00'); // 14:41:00\n\n// formatting a datetime string (in CEST) as a time\necho Yii::$app->formatter->asTime('2014-10-06 14:41:00 CEST'); // 14:41:00\n```\n\n如果 [[yii\\i18n\\Formatter::timeZone|time zone]] 未在格式化组件上明确设置，\n[[yii\\base\\Application::timeZone|time zone configured in the application]] 将会被使用，\n这与 PHP 配置中设置的时区相同。\n\n> 不同的政府和地区政策决定不同的时区，\n> 你在你的时区数据库中可能拿不到最新的数据。\n> 这时你可以戳 [ICU manual](https://unicode-org.github.io/icu/userguide/datetime/timezone/#updating-the-time-zone-data) 来查看如何更新时区。\n> 同时，这篇也可以作为参考 [Setting up your PHP environment for internationalization](tutorial-i18n.md#setup-environment)。\n\n\n## 格式化数字（Formatting Numbers） <span id=\"numbers\"></span>\n\n`formatter` 支持如下的方法\n\n- [[yii\\i18n\\Formatter::asInteger()|integer]]: 这个变量将被格式化为整形 e.g. `42`.\n- [[yii\\i18n\\Formatter::asDecimal()|decimal]]: 这个变量将被格式化为带着逗号的指定精度的浮点型，\n  例如：`2,542.123` 或 `2.542,123`。\n- [[yii\\i18n\\Formatter::asPercent()|percent]]: 这个变量将被格式化为百分比 e.g. `42%`.\n- [[yii\\i18n\\Formatter::asScientific()|scientific]]: 这个变量将被格式化为科学计数法 e.g. `4.2E4`.\n- [[yii\\i18n\\Formatter::asCurrency()|currency]]: 这个变量将被格式化为货币，\n  例如：`£420.00`。\n  使用这个方法前请确认是否已经正确配置 [[yii\\i18n\\Formatter::locale|locale]]\n- [[yii\\i18n\\Formatter::asSize()|size]]: 这个变量将被格式化为人类可读的字节数 e.g. `410 kibibytes`.\n- [[yii\\i18n\\Formatter::asShortSize()|shortSize]]: 这个变量将被格式化为人类可读的字节数（缩写） [[yii\\i18n\\Formatter::asSize()|size]]，例如：`410 KiB`。\n\n你可以使用 [[yii\\i18n\\Formatter::decimalSeparator|decimalSeparator]] 和 \n[[yii\\i18n\\Formatter::thousandSeparator|thousandSeparator]] 来进行调整。\n他们都会根据当前的 [[yii\\i18n\\Formatter::locale|locale]] 来进行格式化.\n\n如果你想要进行更高级的配置, 可以使用 [[yii\\i18n\\Formatter::numberFormatterOptions]] 和 \n[[yii\\i18n\\Formatter::numberFormatterTextOptions]]，\n[NumberFormatter class](https://www.php.net/manual/zh/class.numberformatter.php) 来进行格式化。\n例如，为了调整小数部分的最大值和最小值，你可以配置 [[yii\\i18n\\Formatter::numberFormatterOptions]] 如下：\n\n```php\n'numberFormatterOptions' => [\n    NumberFormatter::MIN_FRACTION_DIGITS => 0,\n    NumberFormatter::MAX_FRACTION_DIGITS => 2,\n]\n```\n\n\n## 其他的格式化（Other Formats） <span id=\"other\"></span>\n\n除了时间/日期和数字的格式化，Yii 还支持如下的常用格式化\n\n- [[yii\\i18n\\Formatter::asRaw()|raw]]：这个值会被原样输出，这是一个无效的伪格式化程序，\n  除了`null` 将使用 [[nullDisplay]] 来格式化。\n- [[yii\\i18n\\Formatter::asText()|text]]：这个值是 HTML 来编码的。\n  这是 [GridView DataColumn](output-data-widgets.md#data-column) 使用的默认格式。\n- [[yii\\i18n\\Formatter::asNtext()|ntext]]：这个值将会被格式化用 HTML 编码的纯文本，\n  其中新的一行将用换行符来隔开。\n- [[yii\\i18n\\Formatter::asParagraphs()|paragraphs]]：这个值被格式化为包含在\n   `<p>` 标签中的 HTML 编码的文本段落。\n- [[yii\\i18n\\Formatter::asHtml()|html]]：这个值使用 [[HtmlPurifier]] 来格式化以避免遭受 XSS 攻击。\n  你能够设置一切其他选项比如 `['html', ['Attr.AllowedFrameTargets' => ['_blank']]]`。\n- [[yii\\i18n\\Formatter::asEmail()|email]]：这个值被格式化为 `mailto`-链接。\n- [[yii\\i18n\\Formatter::asImage()|image]]：这个值被格式化为图像标签。\n- [[yii\\i18n\\Formatter::asUrl()|url]]：这个值被格式化为超链接。\n- [[yii\\i18n\\Formatter::asBoolean()|boolean]]：这个值被格式化为布尔值。\n  默认情况下，转化为当前的引用程序语言，`true` 表现为 `Yes` 并且 `false` 表现为 `No`。\n  您可以通过配置 [[yii\\i18n\\Formatter::booleanFormat]] 的属性来调整它。\n\n\n## 空值（Null Values） <span id=\"null-values\"></span>\n\n空值（`null`）会被特殊格式化。`fommater` 默认会将空值格式化为 `(not set)` 对应的当前的语言。\n你可以配置 [[yii\\i18n\\Formatter::nullDisplay|nullDisplay]]\n属性来进行个性化。\n\n\n## 本地日期格式化（Localizing Data Format） <span id=\"localizing-data-format\"></span>\n\n如上所述，格式化应用程序可以使用当前有效的 [[yii\\i18n\\Formatter::locale|locale]] 来确定\n如何格式化适用于目标国家/地区的值。\n例如，对于不同的区域设置，相同的日期可能会有不同的格式：\n\n```php\nYii::$app->formatter->locale = 'en-US';\necho Yii::$app->formatter->asDate('2014-01-01'); // output: January 1, 2014\n\nYii::$app->formatter->locale = 'de-DE';\necho Yii::$app->formatter->asDate('2014-01-01'); // output: 1. Januar 2014\n\nYii::$app->formatter->locale = 'ru-RU';\necho Yii::$app->formatter->asDate('2014-01-01'); // output: 1 января 2014 г.\n```\n\n默认配置下，当前 [[yii\\i18n\\Formatter::locale|locale]] 决定于 [[yii\\base\\Application::language]].\n你可以覆盖 [[yii\\i18n\\Formatter::locale]] 属性来满足不同的需要。\n\n> Note: Yii formatter 依赖 [PHP intl extension](https://www.php.net/manual/zh/book.intl.php) \n> 来进行本地数据格式化\n> 因为不同的 ICU 库可能会导致不同的输出，所以请在你的所有机器上保持 ICU 库的一致性。\n> 请参阅 [Setting up your PHP environment for internationalization](tutorial-i18n.md#setup-environment)。\n>\n> 如果 `intl` 扩展没有被安装，数据格式化不会考虑本地化。 \n> \n> 在 32 位系统中，1901 年前或者 2038 年后的日期数据将不会被本地化，\n> 因为 ICU 使用的是 32 位的 UNIX 时间戳。\n"
  },
  {
    "path": "docs/guide-zh-CN/output-pagination.md",
    "content": "分页\n==========\n\n当一次要在一个页面上显示很多数据时，通常需要将其分成几部分，\n每个部分都包含一些数据列表并且一次只显示一部分。这些部分在网页上被称为*分页*。\n  \nYii 使用 [[yii\\data\\Pagination]] 对象来代表分页方案的有关信息。特别地，\n\n* [[yii\\data\\Pagination::$totalCount|total count]] 指定数据条目的总数。\n  注意，这个数字通常远远大于需要在一个页面上展示的数据条目。\n* [[yii\\data\\Pagination::$pageSize|page size]] 指定每页包含多少数据条目。\n  默认值为 20。\n* [[yii\\data\\Pagination::$page|current page]] 给出当前的页码。默认值为 0，表示第一页。\n\n通过一个已经完全明确的 [[yii\\data\\Pagination]] 对象，你可以部分地检索并且展示数据。\n比如，如果你正在从数据库取回数据，你可以使用分页对象提供的对应值来指定 DB 查询语句中的 `OFFSET` 和 `LIMIT` 子句。\n下面是个例子，\n\n```php\nuse yii\\data\\Pagination;\n\n// 创建一个 DB 查询来获得所有 status 为 1 的文章\n$query = Article::find()->where(['status' => 1]);\n\n// 得到文章的总数（但是还没有从数据库取数据）\n$count = $query->count();\n\n// 使用总数来创建一个分页对象\n$pagination = new Pagination(['totalCount' => $count]);\n\n// 使用分页对象来填充 limit 子句并取得文章数据\n$articles = $query->offset($pagination->offset)\n    ->limit($pagination->limit)\n    ->all();\n```\n\n上述例子中，文章的哪一页将被返回？它取决于是否给出一个名为 `page` 的参数。\n默认情况下，分页对象将尝试将 [[yii\\data\\Pagination::$page|current page]] 设置为 `page` 参数的值。\n如果没有提供该参数，那么它将默认为 0。\n\n为了促成创建支持分页的 UI 元素，Yii 提供了 [[yii\\widgets\\LinkPager]] 挂件来展示一栏页码按钮，\n用户可以通过点击它来指示应该显示哪一页的数据。\n该挂件接收一个分页对象，因此它知道当前的页数和应该展示多少页码按钮。\n比如，\n\n```php\nuse yii\\widgets\\LinkPager;\n\necho LinkPager::widget([\n    'pagination' => $pagination,\n]);\n```\n\n如果你想手动地创建 UI 元素，你可以使用 [[yii\\data\\Pagination::createUrl()]] 来创建指向不同页面的 URLs。\n该方法需要一个页码来作为参数，并且将创建一个包含页码并且格式正确的 URL，\n例如，\n\n```php\n// 指定要被创建的 URL 应该使用的路由\n// 如果不指定，则使用当前被请求的路由\n$pagination->route = 'article/index';\n\n// 显示: /index.php?r=article%2Findex&page=100\necho $pagination->createUrl(100);\n\n// 显示: /index.php?r=article%2Findex&page=101\necho $pagination->createUrl(101);\n```\n\n> Tip: 创建分页对象时，你可以通过配置 [[yii\\data\\Pagination::pageParam|pageParam]]\n  属性来自定义查询参数 `page` 的名字。\n"
  },
  {
    "path": "docs/guide-zh-CN/output-sorting.md",
    "content": "排序\n=======\n\n展示多条数据时，通常需要对数据按照用户指定的列进行排序。\nYii 使用 [[yii\\data\\Sort]] 对象来代表排序方案的有关信息。\n特别地，\n\n* [[yii\\data\\Sort::$attributes|attributes]] 指定 *属性*，数据按照其排序。\n  一个属性可以就是简单的一个 [model attribute](structure-models.md#attributes)，\n  也可以是结合了多个 model 属性或者 DB 列的复合属性。下面将给出更多细节。\n* [[yii\\data\\Sort::$attributeOrders|attributeOrders]] 给出每个属性当前设置的\n  排序方向。\n* [[yii\\data\\Sort::$orders|orders]] 按照低级列的方式给出排序方向。\n\n使用 [[yii\\data\\Sort]]，首先要声明什么属性能进行排序。\n接着从 [[yii\\data\\Sort::$attributeOrders|attributeOrders]] 或者 [[yii\\data\\Sort::$orders|orders]] 取得当前设置的排序信息，\n然后使用它们来自定义数据查询。例如，\n\n```php\nuse yii\\data\\Sort;\n\n$sort = new Sort([\n    'attributes' => [\n        'age',\n        'name' => [\n            'asc' => ['first_name' => SORT_ASC, 'last_name' => SORT_ASC],\n            'desc' => ['first_name' => SORT_DESC, 'last_name' => SORT_DESC],\n            'default' => SORT_DESC,\n            'label' => 'Name',\n        ],\n    ],\n]);\n\n$articles = Article::find()\n    ->where(['status' => 1])\n    ->orderBy($sort->orders)\n    ->all();\n```\n\n上述例子中，为 [[yii\\data\\Sort|Sort]] 对象声明了两个属性： `age` 和 `name`。\n\n`age` 属性是 `Article` 与 Active Record 类中 `age` 属性对应的一个简单属性。\n上述声明与下述等同：\n\n```php\n'age' => [\n    'asc' => ['age' => SORT_ASC],\n    'desc' => ['age' => SORT_DESC],\n    'default' => SORT_ASC,\n    'label' => Inflector::camel2words('age'),\n]\n```\n\n`name` 属性是由 `Article` 的 `firsr_name` 和 `last_name` 定义的一个复合属性。\n使用下面的数组结构来对它进行声明：\n\n- `asc` 和 `desc` 元素指定了如何按照该属性进行升序和降序的排序。\n  它们的值代表数据真正地应该按照什么列和方向进行排序。\n  你可以指定一列或多列来指出到底是简单排序还是多重排序。\n- `default` 元素指定了当一次请求时，属性应该按照什么方向来进行排序。\n  它默认为升序方向，意味着如果之前没有进行排序，并且\n  你请求按照该属性进行排序，那么数据将按照该属性来进行升序排序。\n- `label` 元素指定了调用 [[yii\\data\\Sort::link()]] 来创建一个排序链接时应该使用什么标签。\n  如果不设置，将调用 [[yii\\helpers\\Inflector::camel2words()]] 来通过属性名生成一个标签。\n  注意，它并不是 HTML编码的。\n  \n> Info: 你可以将 [[yii\\data\\Sort::$orders|orders]] 的值直接提供给数据库查询来构建其 `ORDER BY` 子句。\n  不要使用 [[yii\\data\\Sort::$attributeOrders|attributeOrders]]，\n  因为一些属性可能是复合的，是不能被数据库查询识别的。\n\n你可以调用 [[yii\\data\\Sort::link()]] 来生成一个超链接，用户可以通过点击它来请求按照指定的属性对数据进行排序。\n你也可以调用 [[yii\\data\\Sort::createUrl()]] 来生成一个可排序的 URL。\n例如，\n\n```php\n// 指定被创建的 URL 应该使用的路由\n// 如果你没有指定，将使用当前被请求的路由\n$sort->route = 'article/index';\n\n// 显示链接，链接分别指向以 name 和 age 进行排序\necho $sort->link('name') . ' | ' . $sort->link('age');\n\n// 显示: /index.php?r=article%2Findex&sort=age\necho $sort->createUrl('age');\n```\n\n[[yii\\data\\Sort]] 查看 `sort` 查询参数来决定哪一个属性正在被请求来进行排序。\n当该参数不存在时，你可以通过 [[yii\\data\\Sort::defaultOrder]] 来指定默认的排序。\n你也可以通过配置 [[yii\\data\\Sort::sortParam|sortParam]] 属性来自定义该查询参数的名字。\n"
  },
  {
    "path": "docs/guide-zh-CN/output-theming.md",
    "content": "主题\n=======\n\n主题是一种将当前的一套视图 [views](structure-views.md) 替换为另一套视图，而无需更改视图渲染代码的方法。\n你可以使用主题来系统地更改应用的外观和体验。\n\n要使用主题，你得配置 `view` 应用组件的 [[yii\\base\\View::theme|theme]] 属性。\n这个属性配置了一个 [[yii\\base\\Theme]] 对象，这个对象用来控制视图文件怎样被替换。\n你主要应该指明下面的 [[yii\\base\\Theme]] 属性：\n\n- [[yii\\base\\Theme::basePath]]：指定包含主题资源（CSS, JS, images, 等等）的基准目录。\n- [[yii\\base\\Theme::baseUrl]]：指定主题资源的基准URL。\n- [[yii\\base\\Theme::pathMap]]：指定视图文件的替换规则。\n  更多细节将在下面介绍。\n\n例如，如果你在 `SiteController` 里面调用 `$this->render('about')`，那你将渲染\n视图文件 `@app/views/site/about.php` 。然而，如果你在下面的应用配置中开启了主\n题功能，那么 `@app/themes/basic/site/about.php` 文件将会被渲染。\n\n```php\nreturn [\n    'components' => [\n        'view' => [\n            'theme' => [\n                'basePath' => '@app/themes/basic',\n                'baseUrl' => '@web/themes/basic',\n                'pathMap' => [\n                    '@app/views' => '@app/themes/basic',\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n> Info: 主题支持路径别名。当我们在做视图替换的时候，\n  路径别名将被转换成实际的文件路径或者URL。\n\n你可以通过 [[yii\\base\\View::theme]] 属性访问 [[yii\\base\\Theme]] 对象。例如，在一个视图文件里，你可以写下面的代码，\n因为 `$this` 指向视图对象：\n\n```php\n$theme = $this->theme;\n\n// returns: $theme->baseUrl . '/img/logo.gif'\n$url = $theme->getUrl('img/logo.gif');\n\n// returns: $theme->basePath . '/img/logo.gif'\n$file = $theme->getPath('img/logo.gif');\n```\n\n[[yii\\base\\Theme::pathMap]] 属性控制如何替换视图文件。它是一个键值对数组，其中，\n键是原本的视图路径，而值是相应的主题视图路径。\n替换是基于部分匹配的：如果视图路径以 [[yii\\base\\Theme::pathMap|pathMap]] 数组的\n任何一个键为起始，那么匹配部分将被相应的值所替换。\n使用上面配置的例子，因为 `@app/views/site/about.php` 中的起始部分与键 `@app/views` 匹配，\n它将被替换成 `@app/themes/basic/site/about.php`。\n\n\n## 主题化模块 <span id=\"theming-modules\"></span>\n\n要主题化模块，[[yii\\base\\Theme::pathMap]] 可以配置成下面这样：\n\n```php\n'pathMap' => [\n    '@app/views' => '@app/themes/basic',\n    '@app/modules' => '@app/themes/basic/modules', // <-- !!!\n],\n```\n\n它允许你将 `@app/modules/blog/views/comment/index.php` 主题化成 `@app/themes/basic/modules/blog/views/comment/index.php`。\n\n\n## 主题化小部件 <span id=\"theming-widgets\"></span>\n\n要主题化小部件，你可以像下面这样配置 [[yii\\base\\Theme::pathMap]]：\n\n```php\n'pathMap' => [\n    '@app/views' => '@app/themes/basic',\n    '@app/widgets' => '@app/themes/basic/widgets', // <-- !!!\n],\n```\n\n这将允许你将 `@app/widgets/currency/views/index.php` 主题化成 `@app/themes/basic/widgets/currency/index.php`。\n\n\n## 主题继承 <span id=\"theme-inheritance\"></span>\n\n有的时候，你可能想要定义一个基本的主题，其中包含一个基本的应用外观和体验，然后根据当前的节日，你可能想要稍微地改变一下外观和体验。\n这个时候，你就可以使用主题继承实现这一目标，主题继承是通过一个单视图路径去映射多个目标，\n例如，\n\n```php\n'pathMap' => [\n    '@app/views' => [\n        '@app/themes/christmas',\n        '@app/themes/basic',\n    ],\n]\n```\n\n在这种情况下，视图 `@app/views/site/index.php` 将被主题化成\n`@app/themes/christmas/site/index.php` 或者 `@app/themes/basic/site/index.php`，\n这取决于哪个主题文件存在。假如都存在，那么第一个将被优先使用。在现实情况中，\n你会将大部分的主题文件放在 `@app/themes/basic` 里，而一些自定义的放在 `@app/themes/christmas`里。\n"
  },
  {
    "path": "docs/guide-zh-CN/rest-authentication.md",
    "content": "认证\n==============\n\n和 Web 应用不同，RESTful APIs 通常是无状态的，\n也就意味着不应使用 sessions 或 cookies，\n因此每个请求应附带某种授权凭证，因为用户授权状态可能没通过 sessions 或 cookies 维护，\n常用的做法是每个请求都发送一个秘密的 access token 来认证用户，\n由于 access token 可以唯一识别和认证用户，\n**API 请求应通过 HTTPS 来防止 man-in-the-middle（MitM）中间人攻击**。\n\n下面有几种方式来发送 access token：\n\n* [HTTP 基本认证](https://zh.wikipedia.org/wiki/HTTP%E5%9F%BA%E6%9C%AC%E8%AE%A4%E8%AF%81)：access token\n  当作用户名发送，应用在 access token 可安全存在 API 使用端的场景，\n  例如，API 使用端是运行在一台服务器上的程序。\n* 请求参数：access token 当作 API URL 请求参数发送，例如\n  `https://example.com/users?access-token=xxxxxxxx`，\n  由于大多数服务器都会保存请求参数到日志，\n  这种方式应主要用于`JSONP` 请求，因为它不能使用HTTP头来发送access token \n* [OAuth 2](https://oauth.net/2/)：使用者从认证服务器上获取基于 OAuth2 协议的 access token，\n  然后通过 [HTTP Bearer Tokens](https://datatracker.ietf.org/doc/html/rfc6750) \n  发送到 API 服务器。\n\nYii 支持上述的认证方式，你也可很方便的创建新的认证方式。\n\n为你的 APIs 启用认证，做以下步骤：\n\n1. 配置 `user` 应用组件：\n   - 设置 [[yii\\web\\User::enableSession|enableSession]] 属性为 `false`。\n   - 设置 [[yii\\web\\User::loginUrl|loginUrl]] 属性为`null` 显示一个HTTP 403 错误而不是跳转到登录界面。\n2. 在你的REST 控制器类中配置`authenticator` \n   行为来指定使用哪种认证方式\n3. 在你的[[yii\\web\\User::identityClass|user identity class]] 类中实现 [[yii\\web\\IdentityInterface::findIdentityByAccessToken()]] 方法。\n\n步骤 1 不是必要的，但是推荐配置，因为 RESTful APIs 应为无状态的，\n当 [[yii\\web\\User::enableSession|enableSession]] 为 false，\n请求中的用户认证状态就不能通过 session 来保持，每个请求的认证通过步骤 2 和 3 来实现。\n\n> Tip: 如果你将 RESTful APIs 作为应用开发，可以设置应用配置中 `user` 组件的\n> [[yii\\web\\User::enableSession|enableSession]]，\n> 如果将 RESTful APIs 作为模块开发，可以在模块的 `init()` 方法中增加如下代码，如下所示：\n\n> ```php\n> public function init()\n> {\n>     parent::init();\n>     \\Yii::$app->user->enableSession = false;\n> }\n> ```\n\n例如，为使用 HTTP Basic Auth，可配置 `authenticator` 行为，如下所示：\n\n```php\nuse yii\\filters\\auth\\HttpBasicAuth;\n\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n    $behaviors['authenticator'] = [\n        'class' => HttpBasicAuth::class,\n    ];\n    return $behaviors;\n}\n```\n\n如果你想支持上面解释的所有三种认证方法，可以使用 `CompositeAuth`，如下所示：\n\n```php\nuse yii\\filters\\auth\\CompositeAuth;\nuse yii\\filters\\auth\\HttpBasicAuth;\nuse yii\\filters\\auth\\HttpBearerAuth;\nuse yii\\filters\\auth\\QueryParamAuth;\n\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n    $behaviors['authenticator'] = [\n        'class' => CompositeAuth::class,\n        'authMethods' => [\n            HttpBasicAuth::class,\n            HttpBearerAuth::class,\n            QueryParamAuth::class,\n        ],\n    ];\n    return $behaviors;\n}\n```\n\n`authMethods` 中每个单元应为一个认证方法名或配置数组。\n\n\n`findIdentityByAccessToken()` 方法的实现是系统定义的，\n例如，一个简单的场景，当每个用户只有一个 access token，可存储 access token 到 user 表的 `access_token` 列中，\n方法可在 `User` 类中简单实现，如下所示：\n\n```php\nuse yii\\db\\ActiveRecord;\nuse yii\\web\\IdentityInterface;\n\nclass User extends ActiveRecord implements IdentityInterface\n{\n    public static function findIdentityByAccessToken($token, $type = null)\n    {\n        return static::findOne(['access_token' => $token]);\n    }\n}\n```\n\n在上述认证启用后，对于每个 API 请求，\n请求控制器都会在它的 `beforeAction()` 步骤中对用户进行认证。\n\n如果认证成功，控制器再执行其他检查(如频率限制，操作权限)，然后再执行动作，\n授权用户信息可使用 `Yii::$app->user->identity` 获取。\n\n如果认证失败，会发送一个 HTTP 状态码为 401 的响应，\n并带有其他相关信息头（如HTTP 基本认证会有 `WWW-Authenticate` 头信息）。\n\n\n## 授权 <span id=\"authorization\"></span>\n\n在用户认证成功后，你可能想要检查他是否有权限执行对应的操作来获取资源，\n这个过程称为 *authorization* ，\n详情请参考 [Authorization section](security-authorization.md)。\n\n如果你的控制器从 [[yii\\rest\\ActiveController]] 类继承，\n可覆盖 [[yii\\rest\\Controller::checkAccess()|checkAccess()]] 方法\n来执行授权检查，该方法会被 [[yii\\rest\\ActiveController]] 内置的操作调用。\n"
  },
  {
    "path": "docs/guide-zh-CN/rest-controllers.md",
    "content": "控制器\n===========\n\n在创建资源类和指定资源格输出式化后，\n下一步就是创建控制器操作将资源通过 RESTful APIs 展现给终端用户。\n\nYii 提供两个控制器基类来简化创建 RESTful \n操作的工作：[[yii\\rest\\Controller]] 和 [[yii\\rest\\ActiveController]]，\n两个类的差别是后者提供一系列将资源处理成 [Active Record](db-active-record.md) 的操作。\n因此如果使用 [Active Record](db-active-record.md) 内置的操作会比较方便，可考虑将控制器类\n继承 [[yii\\rest\\ActiveController]]，\n它会让你用最少的代码完成强大的 RESTful APIs。\n\n[[yii\\rest\\Controller]] 和 [[yii\\rest\\ActiveController]] 提供以下功能，\n一些功能在后续章节详细描述：\n\n* HTTP 方法验证；\n* [内容协商和数据格式化](rest-response-formatting.md)；\n* [认证](rest-authentication.md)；\n* [频率限制](rest-rate-limiting.md)。\n\n[[yii\\rest\\ActiveController]] 额外提供以下功能：\n\n* 一系列常用动作：`index`，`view`，`create`，`update`，`delete`，`options`；\n* 对动作和资源进行用户认证。\n\n\n## 创建控制器类 <span id=\"creating-controller\"></span>\n\n当创建一个新的控制器类，控制器类的命名最好使用资源名称的单数格式，\n例如，提供用户信息的控制器\n可命名为 `UserController`。\n\n创建新的操作和 Web 应用中创建操作类似，\n唯一的差别是 Web 应用中调用 `render()` 方法渲染一个视图作为返回值，\n对于 RESTful 操作直接返回数据，\n[[yii\\rest\\Controller::serializer|serializer]] 和 [[yii\\web\\Response|response object]] \n会处理原始数据到请求格式的转换，例如\n\n```php\npublic function actionView($id)\n{\n    return User::findOne($id);\n}\n```\n\n\n## 过滤器 <span id=\"filters\"></span>\n\n[[yii\\rest\\Controller]] 提供的大多数 RESTful API 功能通过[过滤器](structure-filters.md)实现。\n特别是以下过滤器会按顺序执行：\n\n* [[yii\\filters\\ContentNegotiator|contentNegotiator]]：支持内容协商，\n  在 [响应格式化](rest-response-formatting.md) 一节描述；\n* [[yii\\filters\\VerbFilter|verbFilter]]：支持 HTTP 方法验证；\n* [[yii\\filters\\auth\\AuthMethod|authenticator]]：支持用户认证，\n  在[认证](rest-authentication.md)一节描述；\n* [[yii\\filters\\RateLimiter|rateLimiter]]：支持频率限制，\n  在[频率限制](rest-rate-limiting.md) 一节描述。\n\n这些过滤器都在 [[yii\\rest\\Controller::behaviors()|behaviors()]] 方法中声明，\n可覆盖该方法来配置单独的过滤器，禁用某个或增加你自定义的过滤器。\n例如，如果你只想用 HTTP 基础认证，可编写如下代码：\n\n```php\nuse yii\\filters\\auth\\HttpBasicAuth;\n\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n    $behaviors['authenticator'] = [\n        'class' => HttpBasicAuth::class,\n    ];\n    return $behaviors;\n}\n```\n\n### CORS <span id=\"cors\"></span>\n\n将 [Cross-Origin Resource Sharing](structure-filters.md#cors) 过滤器添加到控制器比添加到上述其他过滤器中要复杂一些，\n因为必须在认证方法之前应用 CORS 过滤器，\n因此与其他过滤器相比，需要一些稍微不同的方式来实现。\n并且还要为 [CORS Preflight requests](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS#%E9%A2%84%E6%A3%80%E8%AF%B7%E6%B1%82) 禁用身份验证，\n这样浏览器就可以安全地确定是否可以事先做出请求，\n而无需发送身份验证凭据。\n下面显示了将 [[yii\\filters\\Cors]] 过滤器添加到从 [[yii\\rest\\ActiveController]] 扩展的控制器所需的代码：\n\n```php\nuse yii\\filters\\auth\\HttpBasicAuth;\n\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n\n    // remove authentication filter\n    $auth = $behaviors['authenticator'];\n    unset($behaviors['authenticator']);\n    \n    // add CORS filter\n    $behaviors['corsFilter'] = [\n        'class' => \\yii\\filters\\Cors::class,\n    ];\n    \n    // re-add authentication filter\n    $behaviors['authenticator'] = $auth;\n    // avoid authentication on CORS-pre-flight requests (HTTP OPTIONS method)\n    $behaviors['authenticator']['except'] = ['options'];\n\n    return $behaviors;\n}\n```\n\n\n## 继承 `ActiveController` <span id=\"extending-active-controller\"></span>\n\n如果你的控制器继承 [[yii\\rest\\ActiveController]]，\n应设置 [[yii\\rest\\ActiveController::modelClass|modelClass]] 属性\n为通过该控制器返回给用户的资源类名，该类必须继承 [[yii\\db\\ActiveRecord]]。\n\n\n### 自定义动作 <span id=\"customizing-actions\"></span>\n\n[[yii\\rest\\ActiveController]] 默认提供一下动作：\n\n* [[yii\\rest\\IndexAction|index]]：按页列出资源；\n* [[yii\\rest\\ViewAction|view]]：返回指定资源的详情；\n* [[yii\\rest\\CreateAction|create]]：创建新的资源；\n* [[yii\\rest\\UpdateAction|update]]：更新一个存在的资源；\n* [[yii\\rest\\DeleteAction|delete]]：删除指定的资源；\n* [[yii\\rest\\OptionsAction|options]]：返回支持的 HTTP 方法。\n\n所有这些动作通过 [[yii\\rest\\ActiveController::actions()|actions()]] 方法申明，可覆盖 `actions()` 方法配置或禁用这些动作，\n如下所示：\n\n```php\npublic function actions()\n{\n    $actions = parent::actions();\n\n    // 禁用 \"delete\" 和 \"create\" 动作\n    unset($actions['delete'], $actions['create']);\n\n    // 使用 \"prepareDataProvider()\" 方法自定义数据 provider \n    $actions['index']['prepareDataProvider'] = [$this, 'prepareDataProvider'];\n\n    return $actions;\n}\n\npublic function prepareDataProvider()\n{\n    // 为 \"index\" 动作准备和返回数据 provider\n}\n```\n\n请参考独立动作类的参考文档学习哪些配置项有用。\n\n\n### 执行访问检查 <span id=\"performing-access-check\"></span>\n\n通过 RESTful APIs 显示数据时，经常需要检查当前用户是否有权限访问和操作所请求的资源，\n在 [[yii\\rest\\ActiveController]] 中，\n可覆盖 [[yii\\rest\\ActiveController::checkAccess()|checkAccess()]] 方法来完成权限检查。\n\n```php\n/**\n * Checks the privilege of the current user.\n *\n * This method should be overridden to check whether the current user has the privilege\n * to run the specified action against the specified data model.\n * If the user does not have access, a [[ForbiddenHttpException]] should be thrown.\n *\n * @param string $action the ID of the action to be executed\n * @param \\yii\\base\\Model $model the model to be accessed. If `null`, it means no specific model is being accessed.\n * @param array $params additional parameters\n * @throws ForbiddenHttpException if the user does not have access\n */\npublic function checkAccess($action, $model = null, $params = [])\n{\n    // check if the user can access $action and $model\n    // throw ForbiddenHttpException if access should be denied\n    if ($action === 'update' || $action === 'delete') {\n        if ($model->author_id !== \\Yii::$app->user->id)\n            throw new \\yii\\web\\ForbiddenHttpException(sprintf('You can only %s articles that you\\'ve created.', $action));\n    }\n}\n```\n\n`checkAccess()` 方法默认会被 [[yii\\rest\\ActiveController]] 默认动作所调用，如果创建新的操作并想执行权限检查，\n应在新的动作中明确调用该方法。\n\n> Tip: 可使用 [Role-Based Access Control (RBAC) 基于角色权限控制组件](security-authorization.md) 实现 `checkAccess()`。\n"
  },
  {
    "path": "docs/guide-zh-CN/rest-error-handling.md",
    "content": "错误处理\n==============\n\n处理一个 RESTful API 请求时， 如果有一个用户请求错误或服务器发生意外时，\n你可以简单地抛出一个异常来通知用户出错了。\n如果你能找出错误的原因 (例如，所请求的资源不存在)，你应该\n考虑抛出一个适当的HTTP状态代码的异常 \n(例如， [[yii\\web\\NotFoundHttpException]]意味着一个404 HTTP状态代码)。 \nYii 将通过HTTP状态码和文本发送相应的响应。 \n它还将包括在响应主体异常的序列化表示形式。 例如，\n\n```\nHTTP/1.1 404 Not Found\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"name\": \"Not Found Exception\",\n    \"message\": \"The requested resource was not found.\",\n    \"code\": 0,\n    \"status\": 404\n}\n```\n\n下面的列表总结了Yii的REST框架的HTTP状态代码:\n\n* `200`: OK。一切正常。\n* `201`: 响应 `POST` 请求时成功创建一个资源。`Location` header\n   包含的URL指向新创建的资源。\n* `204`: 该请求被成功处理，响应不包含正文内容 (类似 `DELETE` 请求)。\n* `304`: 资源没有被修改。可以使用缓存的版本。\n* `400`: 错误的请求。可能通过用户方面的多种原因引起的，例如在请求体内有无效的JSON\n   数据，无效的操作参数，等等。\n* `401`: 验证失败。\n* `403`: 已经经过身份验证的用户不允许访问指定的 API 末端。\n* `404`: 所请求的资源不存在。\n* `405`: 不被允许的方法。 请检查 `Allow` header 允许的HTTP方法。\n* `415`: 不支持的媒体类型。 所请求的内容类型或版本号是无效的。\n* `422`: 数据验证失败 (例如，响应一个 `POST` 请求)。 请检查响应体内详细的错误消息。\n* `429`: 请求过多。 由于限速请求被拒绝。\n* `500`: 内部服务器错误。 这可能是由于内部程序错误引起的。\n\n\n## 自定义错误响应 <span id=\"customizing-error-response\"></span>\n\n有时你可能想自定义默认的错误响应格式。例如，你想一直使用HTTP状态码200，\n而不是依赖于使用不同的HTTP状态来表示不同的错误，\n并附上实际的HTTP状态代码为JSON结构的一部分的响应，就像以下所示，\n\n```\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"success\": false,\n    \"data\": {\n        \"name\": \"Not Found Exception\",\n        \"message\": \"The requested resource was not found.\",\n        \"code\": 0,\n        \"status\": 404\n    }\n}\n```\n\n为了实现这一目的，你可以响应该应用程序配置的 `response` 组件的 `beforeSend` 事件：\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'response' => [\n            'class' => 'yii\\web\\Response',\n            'on beforeSend' => function ($event) {\n                $response = $event->sender;\n                if ($response->data !== null && !empty(Yii::$app->request->get('suppress_response_code'))) {\n                    $response->data = [\n                        'success' => $response->isSuccessful,\n                        'data' => $response->data,\n                    ];\n                    $response->statusCode = 200;\n                }\n            },\n        ],\n    ],\n];\n```\n\n当 `suppress_response_code` 作为 `GET` 参数传递时，上面的代码\n将重新按照自己定义的格式响应（无论失败还是成功）。\n"
  },
  {
    "path": "docs/guide-zh-CN/rest-quick-start.md",
    "content": "快速入门\n===========\n\nYii 提供了一整套用来简化实现 RESTful 风格的 Web Service 服务的 API。\n特别是，Yii 支持以下关于 RESTful 风格的 API：\n\n* 支持 [Active Record](db-active-record.md) 类的通用 API 的快速原型；\n* 涉及的响应格式（在默认情况下支持 JSON 和 XML）；\n* 支持可选输出字段的定制对象序列化；\n* 适当的格式的数据采集和验证错误；\n* 集合分页，过滤和排序；\n* 支持 [HATEOAS](https://zh.wikipedia.org/wiki/HATEOAS)；\n* 有适当 HTTP 动词检查的高效的路由；\n* 内置 `OPTIONS` 和 `HEAD` 动词的支持；\n* 认证和授权；\n* 数据缓存和 HTTP 缓存；\n* 速率限制；\n\n\n如下， 我们用一个例子来说明如何用最少的编码来建立一套RESTful风格的API。\n\n假设你想通过 RESTful 风格的 API 来展示用户数据。用户数据被存储在用户DB表，\n你已经创建了 [[yii\\db\\ActiveRecord|ActiveRecord]] 类 `app\\models\\User` 来访问该用户数据.\n\n\n## 创建一个控制器 <span id=\"creating-controller\"></span>\n\n首先，创建一个控制器类 `app\\controllers\\UserController` 如下，\n\n```php\nnamespace app\\controllers;\n\nuse yii\\rest\\ActiveController;\n\nclass UserController extends ActiveController\n{\n    public $modelClass = 'app\\models\\User';\n}\n```\n\n控制器类扩展自 [[yii\\rest\\ActiveController]]。\n通过指定 [[yii\\rest\\ActiveController::modelClass|modelClass]]\n作为 `app\\models\\User`，控制器就能知道使用哪个模型去获取和处理数据。\n\n\n## 配置URL规则 <span id=\"configuring-url-rules\"></span>\n\n然后，修改有关在应用程序配置的`urlManager`组件的配置：\n\n```php\n'urlManager' => [\n    'enablePrettyUrl' => true,\n    'enableStrictParsing' => true,\n    'showScriptName' => false,\n    'rules' => [\n        ['class' => 'yii\\rest\\UrlRule', 'controller' => 'user'],\n    ],\n]\n```\n\n上面的配置主要是为`user`控制器增加一个 URL 规则。这样，\n用户的数据就能通过美化的 URL 和有意义的 http 动词进行访问和操作。\n\n> Note: Yii 将自动复数控制器名称以便在端点中使用（参见下面的 [Trying it Out](#trying-it-out) 部分）。\n> 您可以使用 [[yii\\rest\\UrlRule::$pluralize]] 属性配置。\n\n\n## 启用 JSON 输入 <span id=\"enabling-json-input\"></span>\n\n为了使 API 接收 JSON 格式的输入数据，配置 `request` 应用程序组件的 [[yii\\web\\Request::$parsers|parsers]]\n属性使用 [[yii\\web\\JsonParser]] 用于JSON输入： \n\n```php\n'request' => [\n    'parsers' => [\n        'application/json' => 'yii\\web\\JsonParser',\n    ]\n]\n```\n\n> Info: 上述配置是可选的。若未按上述配置，API 将仅可以分辨 \n  `application/x-www-form-urlencoded` 和 `multipart/form-data` 输入格式。\n\n\n## 尝试 <span id=\"trying-it-out\"></span>\n\n随着以上所做的最小的努力，你已经完成了创建用于访问用户数据\n的 RESTful 风格的 API。你所创建的 API 包括：\n\n* `GET /users`: 逐页列出所有用户\n* `HEAD /users`: 显示用户列表的概要信息\n* `POST /users`: 创建一个新用户\n* `GET /users/123`: 返回用户 123 的详细信息\n* `HEAD /users/123`: 显示用户 123 的概述信息\n* `PATCH /users/123` and `PUT /users/123`: 更新用户123\n* `DELETE /users/123`: 删除用户123\n* `OPTIONS /users`: 显示关于末端 `/users` 支持的动词\n* `OPTIONS /users/123`: 显示有关末端 `/users/123` 支持的动词\n\n你可以访问你的API用 `curl` 命令如下，\n\n```\n$ curl -i -H \"Accept:application/json\" \"http://localhost/users\"\n\nHTTP/1.1 200 OK\n...\nX-Pagination-Total-Count: 1000\nX-Pagination-Page-Count: 50\nX-Pagination-Current-Page: 1\nX-Pagination-Per-Page: 20\nLink: <http://localhost/users?page=1>; rel=self, \n      <http://localhost/users?page=2>; rel=next, \n      <http://localhost/users?page=50>; rel=last\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n[\n    {\n        \"id\": 1,\n        ...\n    },\n    {\n        \"id\": 2,\n        ...\n    },\n    ...\n]\n```\n\n试着改变可接受的内容类型为`application/xml`，\n你会看到结果以 XML 格式返回：\n\n```\n$ curl -i -H \"Accept:application/xml\" \"http://localhost/users\"\n\nHTTP/1.1 200 OK\n...\nX-Pagination-Total-Count: 1000\nX-Pagination-Page-Count: 50\nX-Pagination-Current-Page: 1\nX-Pagination-Per-Page: 20\nLink: <http://localhost/users?page=1>; rel=self, \n      <http://localhost/users?page=2>; rel=next, \n      <http://localhost/users?page=50>; rel=last\nTransfer-Encoding: chunked\nContent-Type: application/xml\n\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<response>\n    <item>\n        <id>1</id>\n        ...\n    </item>\n    <item>\n        <id>2</id>\n        ...\n    </item>\n    ...\n</response>\n```\n\n以下命令将创建一个新的用户通过发送JSON格式的用户数据的 POST 请求：\n\n```\n$ curl -i -H \"Accept:application/json\" -H \"Content-Type:application/json\" -XPOST \"http://localhost/users\" -d '{\"username\": \"example\", \"email\": \"user@example.com\"}'\n\nHTTP/1.1 201 Created\n...\nLocation: http://localhost/users/1\nContent-Length: 99\nContent-Type: application/json; charset=UTF-8\n\n{\"id\":1,\"username\":\"example\",\"email\":\"user@example.com\",\"created_at\":1414674789,\"updated_at\":1414674789}\n```\n\n> Tip: 你还可以通过 Web 浏览器中输入 URL `http://localhost/users` 来访问你的 API。\n  尽管如此，你可能需要一些浏览器插件来发送特定的 headers 请求。\n\n如你所见，在 headers 响应，有关于总数，页数的信息，等等。\n还有一些链接，让你导航到其他页面的数据。例如： `http://localhost/users?page=2`\n会给你的用户数据的下一个页面。\n\n使用 `fields` 和 `expand` 参数，你也可以指定哪些字段应该包含在结果内。\n例如：URL `http://localhost/users?fields=id,email` 将只返回 `id` 和 `email` 字段。\n\n\n> Info: 你可能已经注意到了 `http://localhost/users` 的结果包括一些敏感字段，\n> 例如 `password_hash`, `auth_key` 你肯定不希望这些出现在你的 API 结果中。\n> 你应该在 [响应格式](rest-response-formatting.md) 部分中过滤掉这些字段。\n\n此外，您可以对 `http://localhost/users?sort=email` 或\n`http://localhost/users?sort=-email` 等集合进行排序。 可以使用数据过滤器来实现过滤集合 `http://localhost/users?filter[id]=10` 或\n`http://localhost/users?filter[email][like]=gmail.com`。\n有关详细信息，请参阅 [Resources](rest-resources.md#filtering-collections) 部分。\n\n\n## 总结 <span id=\"summary\"></span>\n\n使用 Yii 框架的 RESTful 风格的 API, 在控制器的动作中实现 API 末端，使用\n控制器来组织末端接口为一个单一的资源类型。\n\n从 [[yii\\base\\Model]] 类扩展的资源被表示为数据模型。\n如果你在使用（关系或非关系）数据库，推荐你使用 [[yii\\db\\ActiveRecord|ActiveRecord]]\n来表示资源。\n\n你可以使用 [[yii\\rest\\UrlRule]] 简化路由到你的 API 末端。\n\n为了方便维护你的 WEB 前端和后端，建议你开发接口作为一个单独的应用程序，\n虽然这不是必须的。\n"
  },
  {
    "path": "docs/guide-zh-CN/rest-rate-limiting.md",
    "content": "限流 (Rate Limiting)\n==================\n\n为防止滥用，你应该考虑对您的 API 限流。\n例如，您可以限制每个用户 10 分钟内最多调用 API 100 次。\n如果在规定的时间内接收了一个用户大量的请求，将返回响应状态代码 429 (这意味着过多的请求)。\n\n要启用限流, [[yii\\web\\User::identityClass|user identity class]] 应该实现 [[yii\\filters\\RateLimitInterface]]。\n这个接口需要实现以下三个方法：\n\n* `getRateLimit()`：返回允许的请求的最大数目及时间，例如，`[100, 600]` 表示在 600 秒内最多 100 次的 API 调用。\n* `loadAllowance()`：返回剩余的允许的请求和最后一次速率限制检查时\n  相应的 UNIX 时间戳数。\n* `saveAllowance()`：保存剩余的允许请求数和当前的 UNIX 时间戳。\n\n你可以在 user 表中使用两列来记录容差和时间戳信息。\n`loadAllowance()` 和 `saveAllowance()` \n可以通过实现对符合当前身份验证的用户的这两列值的读和保存。\n为了提高性能，你也可以考虑使用缓存或 NoSQL 存储这些信息。\n\nImplementation in the `User` model could look like the following:\n\n```php\npublic function getRateLimit($request, $action)\n{\n    return [$this->rateLimit, 1]; // $rateLimit requests per second\n}\n\npublic function loadAllowance($request, $action)\n{\n    return [$this->allowance, $this->allowance_updated_at];\n}\n\npublic function saveAllowance($request, $action, $allowance, $timestamp)\n{\n    $this->allowance = $allowance;\n    $this->allowance_updated_at = $timestamp;\n    $this->save();\n}\n```\n\n一旦 identity 实现所需的接口，Yii 会自动使用 [[yii\\filters\\RateLimiter]]\n为 [[yii\\rest\\Controller]] 配置一个行为过滤器来执行速率限制检查。如果速度超出限制，\n该速率限制器将抛出一个 [[yii\\web\\TooManyRequestsHttpException]]。\n\n你可以参考以下代码\n在你的 REST 控制器类里配置速率限制：\n\n```php\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n    $behaviors['rateLimiter']['enableRateLimitHeaders'] = false;\n    return $behaviors;\n}\n```\n\n当速率限制被激活，默认情况下每个响应将包含以下\nHTTP 头发送目前的速率限制信息：\n\n- `X-Rate-Limit-Limit`：同一个时间段所允许的请求的最大数目；\n- `X-Rate-Limit-Remaining`：在当前时间段内剩余的请求的数量；\n- `X-Rate-Limit-Reset`：为了得到最大请求数所等待的秒数。\n\n你可以禁用这些头信息通过配置 [[yii\\filters\\RateLimiter::enableRateLimitHeaders]] 为 false，\n就像在上面的代码示例所示。\n"
  },
  {
    "path": "docs/guide-zh-CN/rest-resources.md",
    "content": "资源\n=========\n\nRESTful 的 API 都是关于访问和操作 *资源*，可将资源看成 MVC 模式中的\n[模型](structure-models.md)\n\n在如何代表一个资源没有固定的限定，在 Yii 中通常使用 \n[[yii\\base\\Model]] 或它的子类（如 [[yii\\db\\ActiveRecord]]）\n代表资源，是为以下原因：\n\n* [[yii\\base\\Model]] 实现了 [[yii\\base\\Arrayable]] 接口，\n  它允许你通过 RESTful API 自定义你想要公开的资源数据。\n* [[yii\\base\\Model]] 支持 [输入验证](input-validation.md),\n  在你的 RESTful API 需要支持数据输入时非常有用。\n* [[yii\\db\\ActiveRecord]] 提供了强大的数据库访问和操作方面的支持，\n  如资源数据需要存到数据库它提供了完美的支持。\n\n本节主要描述资源类如何从 [[yii\\base\\Model]] (或它的子类) 继承\n并指定哪些数据可通过 RESTful API 返回，如果资源类没有\n继承 [[yii\\base\\Model]] 会将它所有的公开成员变量返回。\n\n\n## 字段 <span id=\"fields\"></span>\n\n当 RESTful API 响应中包含一个资源时，该资源需要序列化成一个字符串。\nYii 将这个过程分成两步，首先，资源会被 [[yii\\rest\\Serializer]] 转换成数组，\n然后，该数组会通过 [[yii\\web\\ResponseFormatterInterface|response formatters]]\n根据请求格式（如 JSON，XML）被序列化成字符串。\n当开发一个资源类时应重点关注第一步。\n\n通过覆盖 [[yii\\base\\Model::fields()|fields()]] 和/或\n[[yii\\base\\Model::extraFields()|extraFields()]] 方法,\n可指定资源中称为 *字段* 的数据放入展现数组中，\n两个方法的差别为前者指定默认包含到展现数组的字段集合，\n后者指定由于终端用户的请求包含 `expand` 参数哪些额外的字段应被包含到展现数组，例如，\n\n```\n// 返回 fields() 方法中声明的所有字段\nhttp://localhost/users\n\n// 只返回 fields() 方法中声明的“id”和“email”字段\nhttp://localhost/users?fields=id,email\n\n// 返回 fields() 方法中声明的所有字段，以及 extraFields() 方法中的“profile”字段\nhttp://localhost/users?expand=profile\n\n// 返回 fields() 方法中声明的所有字段，以及 post 中的“author”\n// 如果它位于 post 模型的 extraFields() 中\nhttp://localhost/comments?expand=post.author\n\n// 返回 fields() 方法中的“id”，“email”，以及 extraFields() 方法中的“profile”字段\nhttp://localhost/users?fields=id,email&expand=profile\n```\n\n\n### 覆盖 `fields()` 方法 <span id=\"overriding-fields\"></span>\n\n[[yii\\base\\Model::fields()]] 默认返回模型的所有属性作为字段，\n[[yii\\db\\ActiveRecord::fields()]] 只返回和数据表关联的属性作为字段。\n\n可覆盖 `fields()` 方法来增加、删除、重命名、重定义字段，\n`fields()` 的返回值应为数组，数组的键为字段名\n数组的值为对应的字段定义，可为属性名或返回对应的字段值的匿名函数，\n特殊情况下，如果字段名和属性名相同，\n可省略数组的键，例如\n\n```php\n// 明确列出每个字段，适用于你希望数据表或\n// 模型属性修改时不导致你的字段修改（保持后端API兼容性）\npublic function fields()\n{\n    return [\n        // 字段名和属性名相同\n        'id',\n        // 字段名为\"email\", 对应的属性名为\"email_address\"\n        'email' => 'email_address',\n        // 字段名为\"name\", 值由一个PHP回调函数定义\n        'name' => function ($model) {\n            return $model->first_name . ' ' . $model->last_name;\n        },\n    ];\n}\n\n// 过滤掉一些字段，适用于你希望继承\n// 父类实现同时你想屏蔽掉一些敏感字段\npublic function fields()\n{\n    $fields = parent::fields();\n\n    // 删除一些包含敏感信息的字段\n    unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);\n\n    return $fields;\n}\n```\n\n> Warning: 模型的所有属性默认会被包含到 API 结果中，\n> 应检查数据确保没包含敏感数据，如果有敏感数据，\n> 应覆盖 `fields()` 过滤掉，在上述例子中，我们选择过滤掉 `auth_key`，\n> `password_hash` 和 `password_reset_token`。\n\n\n### 覆盖 `extraFields()` 方法 <span id=\"overriding-extra-fields\"></span>\n\n[[yii\\base\\Model::extraFields()]] 默认返回空值，\n[[yii\\db\\ActiveRecord::extraFields()]] 返回和数据表关联的属性。\n\n`extraFields()` 返回的数据格式和 `fields()` 相同，\n一般 `extraFields()` 主要用于指定哪些值为对象的字段，\n例如，给定以下字段申明\n\n```php\npublic function fields()\n{\n    return ['id', 'email'];\n}\n\npublic function extraFields()\n{\n    return ['profile'];\n}\n```\n\n`http://localhost/users?fields=id,email&expand=profile` 的请求可能返回如下 JSON 数据：\n\n```php\n[\n    {\n        \"id\": 100,\n        \"email\": \"100@example.com\",\n        \"profile\": {\n            \"id\": 100,\n            \"age\": 30,\n        }\n    },\n    ...\n]\n```\n\n\n## 链接 <span id=\"links\"></span>\n\n[HATEOAS](https://zh.wikipedia.org/wiki/HATEOAS), \n是 Hypermedia as the Engine of Application State的缩写,\n提升 RESTful API 应返回允许终端用户访问的资源操作的信息，\nHATEOAS 的目的是在API中返回包含相关链接信息的资源数据。 \n\n资源类通过实现 [[yii\\web\\Linkable]] 接口来支持 HATEOAS，\n该接口包含方法 [[yii\\web\\Linkable::getLinks()|getLinks()]] 来返回\n[[yii\\web\\Link|links]] 列表，典型情况下应返回包含代表本资源对象 URL 的 `self` 链接，例如\n\n```php\nuse yii\\base\\Model;\nuse yii\\web\\Link; // represents a link object as defined in JSON Hypermedia API Language.\nuse yii\\web\\Linkable;\nuse yii\\helpers\\Url;\n\nclass UserResource extends Model implements Linkable\n{\n    public $id;\n    public $email;\n\n    //...\n\n    public function fields()\n    {\n        return ['id', 'email'];\n    }\n\n    public function extraFields()\n    {\n        return ['profile'];\n    }\n\n    public function getLinks()\n    {\n        return [\n            Link::REL_SELF => Url::to(['user/view', 'id' => $this->id], true),\n            'edit' => Url::to(['user/view', 'id' => $this->id], true),\n            'profile' => Url::to(['user/profile/view', 'id' => $this->id], true),\n            'index' => Url::to(['users'], true),\n        ];\n    }\n}\n```\n\n当响应中返回一个 `User` 对象，\n它会包含一个 `_links` 单元表示和用户相关的链接，例如\n\n```\n{\n    \"id\": 100,\n    \"email\": \"user@example.com\",\n    // ...\n    \"_links\" => {\n        \"self\": {\n            \"href\": \"https://example.com/users/100\"\n        },\n        \"edit\": {\n            \"href\": \"https://example.com/users/100\"\n        },\n        \"profile\": {\n            \"href\": \"https://example.com/users/profile/100\"\n        },\n        \"index\": {\n            \"href\": \"https://example.com/users\"\n        }\n    }\n}\n```\n\n\n## 集合 <span id=\"collections\"></span>\n\n资源对象可以组成 *集合*，\n每个集合包含一组相同类型的资源对象。\n\n集合可被展现成数组，更多情况下展现成 [data providers](output-data-providers.md)。\n因为 data providers 支持资源的排序和分页，\n这个特性在 RESTful API 返回集合时也用到，\n例如，如下操作返回 post 资源的 data provider：\n\n```php\nnamespace app\\controllers;\n\nuse yii\\rest\\Controller;\nuse yii\\data\\ActiveDataProvider;\nuse app\\models\\Post;\n\nclass PostController extends Controller\n{\n    public function actionIndex()\n    {\n        return new ActiveDataProvider([\n            'query' => Post::find(),\n        ]);\n    }\n}\n```\n\n当在 RESTful API 响应中发送 data provider 时， \n[[yii\\rest\\Serializer]] 会取出资源的当前页并组装成资源对象数组，\n[[yii\\rest\\Serializer]] 也通过如下 HTTP 头包含页码信息：\n\n* `X-Pagination-Total-Count`：资源所有数量；\n* `X-Pagination-Page-Count`：页数；\n* `X-Pagination-Current-Page`：当前页（从 1 开始）；\n* `X-Pagination-Per-Page`：每页资源数量；\n* `Link`：允许客户端一页一页遍历资源的导航链接集合。\n\n由于 REST API 中的集合是 data provider，因此它共享所有 data provider 功能，即分页和排序。\n\n可在[快速入门](rest-quick-start.md#trying-it-out) 一节中找到样例。\n\n### 过滤集合 <span id=\"filtering-collections\"></span>\n\n从 2.0.13 版本开始，Yii 提供了过滤集合的工具。一个例子可以在\n[快速入门](rest-quick-start.md#trying-it-out) 指南中找到。如果您自己实现末端，\n可以按照 Data Providers 指南的\n[Filtering Data Providers using Data Filters](output-data-providers.md#filtering-data-providers-using-data-filters)\n部分中的描述进行过滤。\n"
  },
  {
    "path": "docs/guide-zh-CN/rest-response-formatting.md",
    "content": "响应格式\n===================\n\n当处理一个 RESTful API 请求时，一个应用程序通常需要如下步骤\n来处理响应格式：\n\n1. 确定可能影响响应格式的各种因素，例如媒介类型，语言，版本，等等。\n   这个过程也被称为 [content negotiation](https://zh.wikipedia.org/wiki/%E5%86%85%E5%AE%B9%E5%8D%8F%E5%95%86)。\n2. 资源对象转换为数组，如在 [Resources](rest-resources.md) 部分中所描述的。\n   通过 [[yii\\rest\\Serializer]] 来完成。\n3. 通过内容协商步骤将数组转换成字符串。\n   [[yii\\web\\ResponseFormatterInterface|response formatters]] 通过\n   [[yii\\web\\Response::formatters|response]] 应用程序\n   组件来注册完成。\n\n\n## 内容协商 <span id=\"content-negotiation\"></span>\n\nYii 提供了通过 [[yii\\filters\\ContentNegotiator]] 过滤器支持内容协商。RESTful API 基于\n控制器类 [[yii\\rest\\Controller]] 在 `contentNegotiator` 下配备这个过滤器。\n文件管理器提供了涉及的响应格式和语言。例如，如果一个 RESTful\nAPI 请求中包含以下 header，\n\n```\nAccept: application/json; q=1.0, */*; q=0.1\n```\n\n将会得到JSON格式的响应，如下：\n\n```\n$ curl -i -H \"Accept: application/json; q=1.0, */*; q=0.1\" \"http://localhost/users\"\n\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nX-Powered-By: PHP/5.4.20\nX-Pagination-Total-Count: 1000\nX-Pagination-Page-Count: 50\nX-Pagination-Current-Page: 1\nX-Pagination-Per-Page: 20\nLink: <http://localhost/users?page=1>; rel=self,\n      <http://localhost/users?page=2>; rel=next,\n      <http://localhost/users?page=50>; rel=last\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n[\n    {\n        \"id\": 1,\n        ...\n    },\n    {\n        \"id\": 2,\n        ...\n    },\n    ...\n]\n```\n\n幕后，执行一个 RESTful API 控制器动作之前，[[yii\\filters\\ContentNegotiator]]\nfilter 将检查 `Accept` HTTP header 在请求时和配置 [[yii\\web\\Response::format|response format]]\n为 `'json'`。 之后的动作被执行并返回得到的资源对象或集合，\n[[yii\\rest\\Serializer]] 将结果转换成一个数组。最后，[[yii\\web\\JsonResponseFormatter]]\n该数组将序列化为JSON字符串，并将其包括在响应主体。\n\n默认, RESTful APIs 同时支持JSON和XML格式。为了支持新的格式，你应该\n在 `contentNegotiator` 过滤器中配置 [[yii\\filters\\ContentNegotiator::formats|formats]] 属性，\n类似如下 API 控制器类:\n\n```php\nuse yii\\web\\Response;\n\npublic function behaviors()\n{\n    $behaviors = parent::behaviors();\n    $behaviors['contentNegotiator']['formats']['text/html'] = Response::FORMAT_HTML;\n    return $behaviors;\n}\n```\n\n`formats` 属性的 keys 支持 MIME 类型，而 values 必须在 [[yii\\web\\Response::formatters]]\n中支持被响应格式名称。\n\n\n## 数据序列化 <span id=\"data-serializing\"></span>\n\n正如我们上面所描述的，[[yii\\rest\\Serializer]] 负责转换资源的中间件\n对象或集合到数组。它将对象 [[yii\\base\\ArrayableInterface]] 作为\n[[yii\\data\\DataProviderInterface]]。前者主要由资源对象实现，\n而后者是资源集合。\n\n你可以通过设置 [[yii\\rest\\Controller::serializer]] 属性和一个配置数组。\n例如，有时你可能想通过直接在响应主体内包含分页信息来\n简化客户端的开发工作。这样做，按照如下规则配置 [[yii\\rest\\Serializer::collectionEnvelope]] \n属性：\n\n```php\nuse yii\\rest\\ActiveController;\n\nclass UserController extends ActiveController\n{\n    public $modelClass = 'app\\models\\User';\n    public $serializer = [\n        'class' => 'yii\\rest\\Serializer',\n        'collectionEnvelope' => 'items',\n    ];\n}\n```\n\n那么你的请求可能会得到的响应如下 `http://localhost/users`：\n\n```\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nX-Powered-By: PHP/5.4.20\nX-Pagination-Total-Count: 1000\nX-Pagination-Page-Count: 50\nX-Pagination-Current-Page: 1\nX-Pagination-Per-Page: 20\nLink: <http://localhost/users?page=1>; rel=self,\n      <http://localhost/users?page=2>; rel=next,\n      <http://localhost/users?page=50>; rel=last\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"items\": [\n        {\n            \"id\": 1,\n            ...\n        },\n        {\n            \"id\": 2,\n            ...\n        },\n        ...\n    ],\n    \"_links\": {\n        \"self\": {\n            \"href\": \"http://localhost/users?page=1\"\n        },\n        \"next\": {\n            \"href\": \"http://localhost/users?page=2\"\n        },\n        \"last\": {\n            \"href\": \"http://localhost/users?page=50\"\n        }\n    },\n    \"_meta\": {\n        \"totalCount\": 1000,\n        \"pageCount\": 50,\n        \"currentPage\": 1,\n        \"perPage\": 20\n    }\n}\n```\n\n### 控制 JSON 输出\n\nJSON 响应将由 [[yii\\web\\JsonResponseFormatter|JsonResponseFormatter]] 类来生成，\n并且将在内部使用 [[yii\\helpers\\Json|JSON helper]]。\n这个格式化程序可以配置不同的选项，\n比如 [[yii\\web\\JsonResponseFormatter::$prettyPrint|$prettyPrint]]，这对于开发更好的可读式响应更有用，\n或者用 [[yii\\web\\JsonResponseFormatter::$encodeOptions|$encodeOptions]] 去控制 JSON 编码的输出。\n\n格式化程序可以在 [configuration](concept-configurations.md) 的 `response` 应用程序组件 [[yii\\web\\Response::formatters|formatters]] 的属性进行配置，\n如下所示：\n\n```php\n'response' => [\n    // ...\n    'formatters' => [\n        \\yii\\web\\Response::FORMAT_JSON => [\n            'class' => 'yii\\web\\JsonResponseFormatter',\n            'prettyPrint' => YII_DEBUG, // use \"pretty\" output in debug mode\n            'encodeOptions' => JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE,\n            // ...\n        ],\n    ],\n],\n```\n\n当使用 [DAO](db-dao.md) 数据库层从数据库返回数据时，所有的数据将会表示成字符串，\n这不总是预期的结果，尤其是数值应该表现为 JSON 的中的数字时。\n当使用 ActiveRecord 层从数据库检索数据时，\n在 [[yii\\db\\ActiveRecord::populateRecord()]] 中从数据库中提取数据，数字列的值将会被转换为整数。\n"
  },
  {
    "path": "docs/guide-zh-CN/rest-routing.md",
    "content": "路由\n=======\n\n随着资源和控制器类准备，您可以使用 URL 如\n`http://localhost/index.php?r=user/create` 访问资源，类似于你可以用正常的 Web 应用程序做法。\n\n在实践中，你通常要用美观的 URL 并采取有优势的 HTTP 动词。\n例如，请求 `POST /users` 意味着访问 `user/create` 动作。\n这可以很容易地通过配置 `urlManager` 应用程序组件来完成\n如下所示：\n\n```php\n'urlManager' => [\n    'enablePrettyUrl' => true,\n    'enableStrictParsing' => true,\n    'showScriptName' => false,\n    'rules' => [\n        ['class' => 'yii\\rest\\UrlRule', 'controller' => 'user'],\n    ],\n]\n```\n\n相比于 URL 管理的 Web 应用程序，上述主要的新东西是通过 RESTful API\n请求 [[yii\\rest\\UrlRule]] 。这个特殊的 URL 规则类将会\n建立一整套子 URL 规则来支持路由和 URL 创建的指定的控制器。\n例如，上面的代码中是大致按照下面的规则：\n\n```php\n[\n    'PUT,PATCH users/<id>' => 'user/update',\n    'DELETE users/<id>' => 'user/delete',\n    'GET,HEAD users/<id>' => 'user/view',\n    'POST users' => 'user/create',\n    'GET,HEAD users' => 'user/index',\n    'users/<id>' => 'user/options',\n    'users' => 'user/options',\n]\n```\n\n该规则支持下面的 API 末端:\n\n* `GET /users`：逐页列出所有用户；\n* `HEAD /users`：显示用户列表的概要信息；\n* `POST /users`：创建一个新用户；\n* `GET /users/123`：返回用户为 123 的详细信息；\n* `HEAD /users/123`：显示用户 123 的概述信息；\n* `PATCH /users/123` 和 `PUT /users/123`：更新用户 123；\n* `DELETE /users/123`：删除用户 123；\n* `OPTIONS /users`：显示关于末端 `/users` 支持的动词；\n* `OPTIONS /users/123`：显示有关末端 `/users/123` 支持的动词。\n\n您可以通过配置 `only` 和 `except` 选项来明确列出哪些行为支持，\n哪些行为禁用。例如，\n\n```php\n[\n    'class' => 'yii\\rest\\UrlRule',\n    'controller' => 'user',\n    'except' => ['delete', 'create', 'update'],\n],\n```\n\n您也可以通过配置 `patterns` 或 `extraPatterns` 重新定义现有的模式或添加此规则支持的新模式。\n例如，通过末端 `GET /users/search` 可以支持新行为 `search`， 按照如下配置 `extraPatterns` 选项，\n\n```php\n[\n    'class' => 'yii\\rest\\UrlRule',\n    'controller' => 'user',\n    'extraPatterns' => [\n        'GET search' => 'search',\n    ],\n]\n```\n\n您可能已经注意到控制器 ID `user` 以复数形式出现在 `users` 末端。\n这是因为 [[yii\\rest\\UrlRule]] 能够为他们使用的末端全自动复数化控制器 ID。\n您可以通过设置 [[yii\\rest\\UrlRule::pluralize]] 为 false 来禁用此行为。\n\n> Info: 控制器的 ID 复数化由 [[yii\\helpers\\Inflector::pluralize()]] 完成。该方法遵循\n  特定的规则。举个例子，单词 `box` 会被复数化为 `boxes` 而不是 `boxs`。\n\n如果自动复数化不能满足你的需求，你也可以配置\n[[yii\\rest\\UrlRule::controller]] 属性来明确指定如何将端点URL中使用的名称映射到\n控制器ID。例如，以下代码将名称 `u` 映射到控制器ID `user`。 \n \n```php\n[\n    'class' => 'yii\\rest\\UrlRule',\n    'controller' => ['u' => 'user'],\n]\n```\n\n## 包含规则的额外配置\n\n在 [[yii\\rest\\UrlRule]] 中所包含的每个规则中，指定额外的配置可能很有用。\n一个很好的例子就是指定 `expand` 参数的默认值：\n\n```php\n[\n    'class' => 'yii\\rest\\UrlRule',\n    'controller' => ['user'],\n    'ruleConfig' => [\n        'class' => 'yii\\web\\UrlRule',\n        'defaults' => [\n            'expand' => 'profile',\n        ]\n    ],\n],\n```\n"
  },
  {
    "path": "docs/guide-zh-CN/rest-versioning.md",
    "content": "版本\n==========\n\n一个好的 API 设计应该是“版本化”的：变更和新的功能应该在 API 新版本中实现，而不是在一个版本上持续更改。与Web应用程序不同，您可以完全控制客户端和服务器端\n代码，APIs 是为了给超出控制的客户端使用。因此，\n应该尽可能的保持向后兼容性，如果有一些变化不能向后兼容，你应该在新版本的 API 中采用它同时增加版本号。现有客户端可以继续使用旧版本的 API；新的或升级的客户端可以在新的 API 版本中获得新的功能。\n\n> Tip: 可以参考 [Semantic Versioning](https://semver.org/) \n来获取更多关于设计 API 版本号的信息\n\n关于如何实现 API 版本，一个常见的做法是在 API 的 URL 中嵌入版本号。例如，\n`https://example.com/v1/users` 代表 `/users` 版本 1 的 API。\n\n另一种 API 版本化的方法，\n最近用的非常多的是把版本号放入 HTTP 请求头，通常是通过 `Accept` 头，如下：\n\n```\n// 通过参数\nAccept: application/json; version=v1\n// 通过vendor的内容类型\nAccept: application/vnd.company.myapp-v1+json\n```\n\n这两种方法都有优点和缺点， 而且关于他们也有很多争论。\n下面我们描述在一种 API 版本混合了这两种方法的一个实用的策略：\n\n* 把每个主要版本的 API 实现在一个单独的模块 ID 的主版本号 (例如 `v1`，`v2`)。\n  自然，API 的 url 将包含主要的版本号。\n* 在每一个主要版本 (在相应的模块)，使用 `Accept` HTTP 请求头\n  确定小版本号编写条件代码来响应相应的次要版本。\n\n为每个模块提供一个主要版本， 它应该包括资源类和控制器类\n为特定服务版本。 更好的分离代码， 你可以保存一组通用的\n基础资源和控制器类， 并用在每个子类版本模块。 在子类中，\n实现具体的代码例如 `Model::fields()`。\n\n你的代码可以类似于如下的方法组织起来：\n\n```\napi/\n    common/\n        controllers/\n            UserController.php\n            PostController.php\n        models/\n            User.php\n            Post.php\n    modules/\n        v1/\n            controllers/\n                UserController.php\n                PostController.php\n            models/\n                User.php\n                Post.php\n            Module.php\n        v2/\n            controllers/\n                UserController.php\n                PostController.php\n            models/\n                User.php\n                Post.php\n            Module.php\n```\n\n你的应用程序配置应该这样：\n\n```php\nreturn [\n    'modules' => [\n        'v1' => [\n            'class' => 'app\\modules\\v1\\Module',\n        ],\n        'v2' => [\n           'class' => 'app\\modules\\v2\\Module',\n        ],\n    ],\n    'components' => [\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            'enableStrictParsing' => true,\n            'showScriptName' => false,\n            'rules' => [\n                ['class' => 'yii\\rest\\UrlRule', 'controller' => ['v1/user', 'v1/post']],\n                ['class' => 'yii\\rest\\UrlRule', 'controller' => ['v2/user', 'v2/post']],\n            ],\n        ],\n    ],\n];\n```\n\n因此，`https://example.com/v1/users` 将返回版本 1 的用户列表，\n而 `https://example.com/v2/users` 将返回版本 2 的用户。\n\n使用模块， 将不同版本的代码隔离。 通过共用基类和其他类\n跨模块重用代码也是有可能的。\n\n为了处理次要版本号， 可以利用内容协商\n功能通过 [[yii\\filters\\ContentNegotiator|contentNegotiator]] 提供的行为。 \n`contentNegotiator` 行为可设置 \n[[yii\\web\\Response::acceptParams]] 属性当它确定支持哪些内容类型时。\n\n例如，如果一个请求通过 `Accept: application/json; version=v1` 被发送，\n内容交涉后，[[yii\\web\\Response::acceptParams]] 将包含值 `['version' => 'v1']`。\n\n基于 `acceptParams` 的版本信息，你可以写条件代码\n如 actions，resource classes，serializers 等等。\n\n由于次要版本需要保持向后兼容性，希望你的代码不会有\n太多的版本检查。否则，有机会你可能需要创建一个新的主要版本。\n"
  },
  {
    "path": "docs/guide-zh-CN/runtime-bootstrapping.md",
    "content": "启动引导（Bootstrapping）\n=============\n\n启动引导是指：在应用开始解析并处理新接受请求之前，一个预先准备环境的过程。\n启动引导会在两个地方具体进行：[入口脚本(Entry Script)](structure-entry-scripts.md)\n和 [应用主体（application）](structure-applications.md)。\n\n在[入口脚本](structure-entry-scripts.md)里，需注册各个类库的类文件自动加载器（Class Autoloader，简称自动加载器）。\n这主要包括通过其 `autoload.php` 文件加载的Composer 自动加载器，以及通过 `Yii` 类加载的 Yii 自动加载器。之后，\n入口脚本会加载应用的[配置（configuration）](concept-configurations.md)并创建一个\n[应用主体](structure-applications.md) 的实例。\n\n在应用主体的构造函数中，会执行以下引导工作：\n\n1. 调用 [[yii\\base\\Application::preInit()|preInit()]]（预初始化）方法，配置一些高优先级的应用属性，\n   比如 [[yii\\base\\Application::basePath|basePath]] 属性。\n2. 注册[[yii\\base\\Application::errorHandler|错误处理器（ErrorHandler）]]。\n3. 通过给定的应用配置初始化应用的各属性。\n4. 通过调用 [[yii\\base\\Application::init()|init()]]（初始化）方法，它会顺次调用\n   [[yii\\base\\Application::bootstrap()|bootstrap()]] 从而运行引导组件。\n   - 加载扩展清单文件(extension manifest file) `vendor/yiisoft/extensions.php`。\n   - 创建并运行各个扩展声明的 \n     [引导组件（bootstrap components）](structure-extensions.md#bootstrapping-classes)。\n   - 创建并运行各个 [应用组件](structure-application-components.md) \n     以及在应用的 [Bootstrap 属性](structure-applications.md#bootstrap)中声明的各个\n     [模块（modules）组件](structure-modules.md)（如果有）。\n\n因为引导工作必须在处理**每一次**请求之前都进行一遍，因此让该过程尽可能轻量化就异常重要，\n请尽可能地优化这一步骤。\n\n请尽量不要注册太多引导组件。只有他需要在 HTTP 请求处理的全部生命周期中都作用时才需要使用它。\n举一个用到它的范例：一个模块需要注册额外的 URL 解析规则，就应该把它列在应用的\n[bootstrap 属性](structure-applications.md#bootstrap)之中，\n这样该 URL 解析规则才能在解析请求之前生效。（译注：换言之，为了性能需要，除了 URL \n解析等少量操作之外，绝大多数组件都应该按需加载，而不是都放在引导过程中。）\n\n在生产环境中，可以开启字节码缓存，比如 APC，\n来进一步最小化加载和解析 PHP 文件所需的时间。\n\n[PHP OPcache]: https://www.php.net/manual/zh/intro.opcache.php\n[APC]: https://www.php.net/manual/zh/book.apcu.php\n\n一些大型应用都包含有非常复杂的应用[配置](concept-configurations.md)，\n它们会被分割到许多更小的配置文件中。\n此时，可以考虑将整个配置数组缓存起来，\n并在入口脚本创建应用实例之前直接从缓存中加载。\n"
  },
  {
    "path": "docs/guide-zh-CN/runtime-handling-errors.md",
    "content": "错误处理\n===============\n\nYii 内置了一个[[yii\\web\\ErrorHandler|error handler]]错误处理器，它使错误处理更方便，\nYii错误处理器做以下工作来提升错误处理效果：\n\n* 所有非致命PHP错误（如，警告，提示）会转换成可获取异常；\n* 异常和致命的PHP错误会被显示，\n  在调试模式会显示详细的函数调用栈和源代码行数。\n* 支持使用专用的 [控制器操作](structure-controllers.md#actions) 来显示错误；\n* 支持不同的错误响应格式；\n\n[[yii\\web\\ErrorHandler|error handler]] 错误处理器默认启用，\n可通过在应用的[入口脚本](structure-entry-scripts.md)中定义常量`YII_ENABLE_ERROR_HANDLER`来禁用。\n\n\n## 使用错误处理器 <span id=\"using-error-handler\"></span>\n\n[[yii\\web\\ErrorHandler|error handler]] 注册成一个名称为`errorHandler`[应用组件](structure-application-components.md)， \n可以在应用配置中配置它类似如下：\n\n```php\nreturn [\n    'components' => [\n        'errorHandler' => [\n            'maxSourceLines' => 20,\n        ],\n    ],\n];\n```\n\n使用如上代码，异常页面最多显示20条源代码。\n\n如前所述，错误处理器将所有非致命PHP错误转换成可获取异常，\n也就是说可以使用如下代码处理PHP错误：\n\n```php\nuse Yii;\nuse yii\\base\\ErrorException;\n\ntry {\n    10/0;\n} catch (ErrorException $e) {\n    Yii::warning(\"Division by zero.\");\n}\n\n// execution continues...\n```\n\n如果你想显示一个错误页面告诉用户请求是无效的或无法处理的，\n可简单地抛出一个 [[yii\\web\\HttpException|HTTP exception]]异常，\n如 [[yii\\web\\NotFoundHttpException]]。\n错误处理器会正确地设置响应的HTTP状态码并使用合适的错误视图页面来显示错误信息。\n\n```php\nuse yii\\web\\NotFoundHttpException;\n\nthrow new NotFoundHttpException();\n```\n\n\n## 自定义错误显示 <span id=\"customizing-error-display\"></span>\n\n[[yii\\web\\ErrorHandler|error handler]]错误处理器根据常量`YII_DEBUG`的值来调整错误显示，\n当`YII_DEBUG` 为 true (表示在调试模式)，\n错误处理器会显示异常以及详细的函数调用栈和源代码行数来帮助调试，\n当`YII_DEBUG` 为 false，只有错误信息会被显示以防止应用的敏感信息泄漏。\n\n> Info: 如果异常是继承 [[yii\\base\\UserException]]，\n  不管`YII_DEBUG`为何值，函数调用栈信息都不会显示，\n  这是因为这种错误会被认为是用户产生的错误，开发人员不需要去修正。\n\n[[yii\\web\\ErrorHandler|error handler]] 错误处理器默认使用两个[视图](structure-views.md)显示错误:\n\n* `@yii/views/errorHandler/error.php`: 显示不包含函数调用栈信息的错误信息是使用，\n  当`YII_DEBUG` 为 false时，所有错误都使用该视图。\n* `@yii/views/errorHandler/exception.php`: 显示包含函数调用栈信息的错误信息时使用。\n\n可以配置错误处理器的 [[yii\\web\\ErrorHandler::errorView|errorView]] 和 [[yii\\web\\ErrorHandler::exceptionView|exceptionView]] 属性\n使用自定义的错误显示视图。\n\n\n### 使用错误动作 <span id=\"using-error-actions\"></span>\n\n使用指定的错误[操作](structure-controllers.md) 来自定义错误显示更方便，\n为此，首先配置`errorHandler`组件的 [[yii\\web\\ErrorHandler::errorAction|errorAction]] 属性，\n类似如下： \n\n```php\nreturn [\n    'components' => [\n        'errorHandler' => [\n            'errorAction' => 'site/error',\n        ],\n    ]\n];\n```\n\n[[yii\\web\\ErrorHandler::errorAction|errorAction]] 属性使用\n[路由](structure-controllers.md#routes)到一个操作，\n上述配置表示不用显示函数调用栈信息的错误会通过执行`site/error`操作来显示。\n\n可以创建 `site/error` 动作如下所示：\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actions()\n    {\n        return [\n            'error' => [\n                'class' => 'yii\\web\\ErrorAction',\n            ],\n        ];\n    }\n}\n```\n\n上述代码定义`error` 操作使用[[yii\\web\\ErrorAction]] 类，\n该类渲染名为`error`视图来显示错误。\n\n除了使用[[yii\\web\\ErrorAction]], 可定义`error` 动作使用类似如下的操作方法：\n\n```php\npublic function actionError()\n{\n    $exception = Yii::$app->errorHandler->exception;\n    if ($exception !== null) {\n        return $this->render('error', ['exception' => $exception]);\n    }\n}\n```\n\n现在应创建一个视图文件为`views/site/error.php`，在该视图文件中，如果错误动作定义为[[yii\\web\\ErrorAction]]，\n可以访问该动作中定义的如下变量：\n\n* `name`: 错误名称\n* `message`: 错误信息\n* `exception`: 更多详细信息的异常对象，如HTTP 状态码，\n  错误码，错误调用栈等。\n\n> Note: 如果你使用 [基础应用模板](start-installation.md) 或 [高级应用模板](tutorial-advanced-app.md),\n错误动作和错误视图已经定义好了。\n\n> Note: 如果您需要在错误处理程序中重定向，请执行以下操作：\n>\n> ```php\n> Yii::$app->getResponse()->redirect($url)->send();\n> return;\n> ```\n\n\n### 自定义错误格式 <span id=\"error-format\"></span>\n\n错误处理器根据[响应](runtime-responses.md)设置的格式来显示错误，\n如果[[yii\\web\\Response::format|response format]] 响应格式为`html`, \n会使用错误或异常视图来显示错误信息，如上一小节所述。\n对于其他的响应格式，错误处理器会错误信息作为数组赋值\n给[[yii\\web\\Response::data]]属性，然后转换到对应的格式，\n例如，如果响应格式为`json`，可以看到如下响应信息：\n\n```\nHTTP/1.1 404 Not Found\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"name\": \"Not Found Exception\",\n    \"message\": \"The requested resource was not found.\",\n    \"code\": 0,\n    \"status\": 404\n}\n```\n\n可在应用配置中响应`response`组件的\n`beforeSend`事件来自定义错误响应格式。\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'response' => [\n            'class' => 'yii\\web\\Response',\n            'on beforeSend' => function ($event) {\n                $response = $event->sender;\n                if ($response->data !== null) {\n                    $response->data = [\n                        'success' => $response->isSuccessful,\n                        'data' => $response->data,\n                    ];\n                    $response->statusCode = 200;\n                }\n            },\n        ],\n    ],\n];\n```\n\n上述代码会重新格式化错误响应，类似如下：\n\n```\nHTTP/1.1 200 OK\nDate: Sun, 02 Mar 2014 05:31:43 GMT\nServer: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y\nTransfer-Encoding: chunked\nContent-Type: application/json; charset=UTF-8\n\n{\n    \"success\": false,\n    \"data\": {\n        \"name\": \"Not Found Exception\",\n        \"message\": \"The requested resource was not found.\",\n        \"code\": 0,\n        \"status\": 404\n    }\n}\n```\n"
  },
  {
    "path": "docs/guide-zh-CN/runtime-logging.md",
    "content": "日志\n=======\n\nYii提供了一个强大的日志框架，这个框架具有高度的可定制性和可扩展性。使用这个框架，\n你可以轻松地记录各种类型的消息，过滤它们，\n并且将它们收集到不同的目标，诸如文件，数据库，邮件。\n\n使用Yii日志框架涉及下面的几个步骤：\n \n* 在你代码里的各个地方记录 [log messages](#log-messages)；\n* 在应用配置里通过配置 [log targets](#log-targets) 来过滤和导出日志消息；\n* 检查由不同的目标导出的已过滤的日志消息（例如：[Yii debugger](tool-debugger.md)）。\n\n在这部分，我们主要描述前两个步骤。\n\n\n## 日志消息 <span id=\"log-messages\"></span>\n\n记录日志消息就跟调用下面的日志方法一样简单：\n\n* [[Yii::trace()]]：记录一条消息去跟踪一段代码是怎样运行的。这主要在开发的时候使用。\n* [[Yii::info()]]：记录一条消息来传达一些有用的信息。\n* [[Yii::warning()]]：记录一个警告消息用来指示一些已经发生的意外。\n* [[Yii::error()]]：记录一个致命的错误，这个错误应该尽快被检查。\n\n这些日志记录方法针对 *严重程度* 和 *类别* 来记录日志消息。\n它们共享相同的函数签名 `function ($message, $category = 'application')`，`$message`代表要被\n记录的日志消息，而 `$category` 是日志消息的类别。在下面的示例代码中，在默认的类别 `application` 下\n记录了一条跟踪消息：\n\n```php\nYii::trace('start calculating average revenue');\n```\n\n> Note: 日志消息可以是字符串，也可以是复杂的数据，诸如数组或者对象。\n[log targets](#log-targets) 的义务是正确处理日志消息。默认情况下，\n假如一条日志消息不是一个字符串，它将被导出为一个字符串，通过调用 [[yii\\helpers\\VarDumper::export()]]。\n\n为了更好地组织和过滤日志消息，我们建议您为每个日志消息指定一个适当的类别。您可以为类别选择一个分层命名方案，\n这将使得 [log targets](#log-targets) 在基于它们的分类来过滤消息变得更加容易。\n一个简单而高效的命名方案是使用PHP魔术常量 `__METHOD__` 作为分类名称。\n这种方式也在Yii框架的核心代码中得到应用，\n例如，\n\n```php\nYii::trace('start calculating average revenue', __METHOD__);\n```\n\n`__METHOD__` 常量计算值作为该常量出现的地方的方法名（完全限定的类名前缀）。\n例如，假如上面那行代码在这个方法内被调用，则它将等于字符串\n`'app\\controllers\\RevenueController::calculate'`。\n\n> Note: 上面所描述的日志方法实际上是 [[yii\\log\\Logger|logger object]] 对象（一个通过表达式 `Yii::getLogger()` 可访问的单例）\n的方法 [[yii\\log\\Logger::log()|log()]] 的一个快捷方式。当足够的消息被记录或者当应用结束时，\n日志对象将会调用一个 [[yii\\log\\Dispatcher|message dispatcher]]\n调度对象将已经记录的日志消息发送到已注册的 [log targets](#log-targets) 目标中。\n\n\n## 日志目标 <span id=\"log-targets\"></span>\n\n一个日志目标是一个 [[yii\\log\\Target]] 类或者它的子类的实例。\n它将通过他们的严重层级和类别来过滤日志消息，然后将它们导出到一些媒介中。\n例如，一个 [[yii\\log\\DbTarget|database target]] 目标导出已经过滤的日志消息到一个数据的表里面，\n而一个 [[yii\\log\\EmailTarget|email target]]目标将日志消息导出到指定的邮箱地址里。\n\n在一个应用里，通过配置在应用配置里的 `log` [application component](structure-application-components.md)，你可以注册多个日志目标。\n就像下面这样：\n\n```php\nreturn [\n    // 必须在引导期间加载“log”组件\n    'bootstrap' => ['log'],\n    // “log”组件处理带时间戳的消息。 设置 PHP 时区以创建正确的时间戳\n    'timeZone' => 'PRC',\n    'components' => [\n        'log' => [\n            'targets' => [\n                [\n                    'class' => 'yii\\log\\DbTarget',\n                    'levels' => ['error', 'warning'],\n                ],\n                [\n                    'class' => 'yii\\log\\EmailTarget',\n                    'levels' => ['error'],\n                    'categories' => ['yii\\db\\*'],\n                    'message' => [\n                       'from' => ['log@example.com'],\n                       'to' => ['admin@example.com', 'developer@example.com'],\n                       'subject' => 'Database errors at example.com',\n                    ],\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n> Note: `log` 组件必须在 [bootstrapping](runtime-bootstrapping.md) 期间就被加载，以便于它能够及时调度日志消息到目标里。\n这是为什么在上面的代码中，它被列在 `bootstrap` 数组中的原因。\n\n在上面的代码中，在 [[yii\\log\\Dispatcher::targets]] 属性里有两个日志目标被注册：\n\n* 第一个目标选择的是错误和警告层级的消息，并且在数据库表里保存他们；\n* 第二个目标选择的是错误层级的消息并且是在以 `yii\\db\\` 开头的分类下，并且在一个邮件里将它们发送到 `admin@example.com`\n  和 `developer@example.com`。\n\nYii配备了以下的内建日志目标。请参考关于这些类的API文档，\n并且学习怎样配置和使用他们。\n\n* [[yii\\log\\DbTarget]]：在数据库表里存储日志消息。\n* [[yii\\log\\EmailTarget]]：发送日志消息到预先指定的邮箱地址。\n* [[yii\\log\\FileTarget]]：保存日志消息到文件中.\n* [[yii\\log\\SyslogTarget]]：通过调用PHP函数 `syslog()` 将日志消息保存到系统日志里。\n\n下面，我们将描述所有日志目标的公共特性。\n  \n\n### 消息过滤 <span id=\"message-filtering\"></span>\n\n对于每一个日志目标，你可以配置它的 [[yii\\log\\Target::levels|levels]] 和 \n[[yii\\log\\Target::categories|categories]] 属性来指定哪个消息的严重程度和分类目标应该处理。\n\n[[yii\\log\\Target::levels|levels]] 属性是由一个或者若干个以下值组成的数组：\n\n* `error`：相应的消息通过 [[Yii::error()]] 被记录。\n* `warning`：相应的消息通过 [[Yii::warning()]] 被记录。\n* `info`：相应的消息通过 [[Yii::info()]] 被记录。\n* `trace`：相应的消息通过 [[Yii::trace()]] 被记录。\n* `profile`：相应的消息通过 [[Yii::beginProfile()]] 和 [[Yii::endProfile()]] 被记录。更多细节将在\n[Profiling](#performance-profiling) 分段解释。\n\n如果你没有指定 [[yii\\log\\Target::levels|levels]] 的属性，\n那就意味着目标将处理 *任何* 严重程度的消息。\n\n[[yii\\log\\Target::categories|categories]] 属性是一个包含消息分类名称或者模式的数组。\n一个目标将只处理那些在这个数组中能够找到对应的分类或者其中一个相匹配的模式的消息。\n一个分类模式是一个以星号 `*` 结尾的分类名前缀。假如一个分类名与分类模式具有相同的前缀，\n那么该分类名将和分类模式相匹配。例如，\n`yii\\db\\Command::execute` 和 `yii\\db\\Command::query` 都是作为分类名称运用在 [[yii\\db\\Command]] 类来记录日志消息的。\n它们都是匹配模式 `yii\\db\\*`。\n\n假如你没有指定 [[yii\\log\\Target::categories|categories]] 属性，\n这意味着目标将会处理 *任何* 分类的消息。 \n\n除了通过 [[yii\\log\\Target::categories|categories]] 属性设置白名单分类，你也可以通过 [[yii\\log\\Target::except|except]]\n属性来设置某些分类作为黑名单。假如一条消息的分类在这个属性中被发现或者是匹配其中一个，\n那么它将不会在目标中被处理。\n\n在下面的目标配置中指明了目标应该只处理错误和警告消息，当分类的名称匹配 `yii\\db\\*` 或者是 `yii\\web\\HttpException:*` 的时候，\n但是除了 `yii\\web\\HttpException:404`。\n\n```php\n[\n    'class' => 'yii\\log\\FileTarget',\n    'levels' => ['error', 'warning'],\n    'categories' => [\n        'yii\\db\\*',\n        'yii\\web\\HttpException:*',\n    ],\n    'except' => [\n        'yii\\web\\HttpException:404',\n    ],\n]\n```\n\n> Note: 当一个HTTP异常通过 [error handler](runtime-handling-errors.md) 被捕获的时候，一个错误消息将以 `yii\\web\\HttpException:ErrorCode`\n  这样的格式的分类名被记录下来。例如，[[yii\\web\\NotFoundHttpException]] 将会引发一个分类是 `yii\\web\\HttpException:404` 的\n  错误消息。\n\n\n### 消息格式化 <span id=\"message-formatting\"></span>\n\n日志目标以某种格式导出过滤过的日志消息。例如，\n假如你安装一个 [[yii\\log\\FileTarget]] 类的日志目标，\n你应该能找出一个日志消息类似下面的 `runtime/log/app.log` 文件：\n\n```\n2014-10-04 18:10:15 [::1][][-][trace][yii\\base\\Module::getModule] Loading module: debug\n```\n\n默认情况下，日志消息将被格式化，格式化的方式遵循 [[yii\\log\\Target::formatMessage()]]：\n\n```\nTimestamp [IP address][User ID][Session ID][Severity Level][Category] Message Text\n```\n\n你可以通过配置 [[yii\\log\\Target::prefix]] 的属性来自定义格式，这个属性是一个 PHP 可调用体返回的自定义消息前缀。\n例如，下面的代码配置了一个日志目标的前缀是每个日志消息中当前用户的 ID（IP 地址和 Session ID 被删除是由于隐私的原因）。\n\n\n```php\n[\n    'class' => 'yii\\log\\FileTarget',\n    'prefix' => function ($message) {\n        $user = Yii::$app->has('user', true) ? Yii::$app->get('user') : null;\n        $userID = $user ? $user->getId(false) : '-';\n        return \"[$userID]\";\n    }\n]\n```\n\n除了消息前缀以外，日志目标也可以追加一些上下文信息到每组日志消息中。\n默认情况下，这些全局的PHP变量的值被包含在：`$_GET`，`$_POST`，`$_FILES`，`$_COOKIE`，`$_SESSION` 和 `$_SERVER` 中。\n你可以通过配置 [[yii\\log\\Target::logVars]] 属性适应这个行为，\n这个属性是你想要通过日志目标包含的全局变量名称。\n举个例子，下面的日志目标配置指明了只有 `$_SERVER` 变量的值将被追加到日志消息中。\n\n```php\n[\n    'class' => 'yii\\log\\FileTarget',\n    'logVars' => ['_SERVER'],\n]\n```\n\n你可以将 `logVars` 配置成一个空数组来完全禁止上下文信息包含。\n或者假如你想要实现你自己提供上下文信息的方式，\n你可以重写 [[yii\\log\\Target::getContextMessage()]] 方法。\n\n\n### 消息跟踪级别 <span id=\"trace-level\"></span>\n\n在开发的时候，通常希望看到每个日志消息来自哪里。这个是能够被实现的，通过配置 `log` 组件的 [[yii\\log\\Dispatcher::traceLevel|traceLevel]] 属性，\n就像下面这样：\n\n```php\nreturn [\n    'bootstrap' => ['log'],\n    'components' => [\n        'log' => [\n            'traceLevel' => YII_DEBUG ? 3 : 0,\n            'targets' => [...],\n        ],\n    ],\n];\n```\n\n上面的应用配置设置了 [[yii\\log\\Dispatcher::traceLevel|traceLevel]] 的层级，假如 `YII_DEBUG` 开启则是3，否则是0。\n这意味着，假如 `YII_DEBUG` 开启，每个日志消息在日志消息被记录的时候，\n将被追加最多3个调用堆栈层级；假如 `YII_DEBUG` 关闭，\n那么将没有调用堆栈信息被包含。\n\n> Note: 获得调用堆栈信息并不是不重要。因此，\n你应该只在开发或者调试一个应用的时候使用这个特性。\n\n\n### 消息刷新和导出 <span id=\"flushing-exporting\"></span>\n\n如上所述，通过 [[yii\\log\\Logger|logger object]] 对象，日志消息被保存在一个数组里。\n为了这个数组的内存消耗，当数组积累了一定数量的日志消息，\n日志对象每次都将刷新被记录的消息到 [log targets](#log-targets) 中。\n你可以通过配置 `log` 组件的 [[yii\\log\\Dispatcher::flushInterval|flushInterval]] 属性来自定义数量：\n\n\n```php\nreturn [\n    'bootstrap' => ['log'],\n    'components' => [\n        'log' => [\n            'flushInterval' => 100,   // default is 1000\n            'targets' => [...],\n        ],\n    ],\n];\n```\n\n> Note: 当应用结束的时候，消息刷新也会发生，这样才能确保日志目标能够接收完整的日志消息。\n\n当 [[yii\\log\\Logger|logger object]] 对象刷新日志消息到 [log targets](#log-targets) 的时候，它们并\n不能立即获取导出的消息。相反，消息导出仅仅在一个日志目标累积了一定数量的过滤消息的时候才会发生。你可以通过配置\n个别的 [log targets](#log-targets) 的 [[yii\\log\\Target::exportInterval|exportInterval]] 属性来\n自定义这个数量，就像下面这样：\n\n```php\n[\n    'class' => 'yii\\log\\FileTarget',\n    'exportInterval' => 100,  // default is 1000\n]\n```\n\n因为刷新和导出层级的设置，默认情况下，当你调用 `Yii::trace()` 或者任何其他的记录方法，你将不能在日志目标中立即看到日志消息。\n这对于一些长期运行的控制台应用来说可能是一个问题。为了让每个日志消息在日志目标中能够立即出现，\n你应该设置 [[yii\\log\\Dispatcher::flushInterval|flushInterval]]\n和 [[yii\\log\\Target::exportInterval|exportInterval]] 都为1，\n就像下面这样：\n\n```php\nreturn [\n    'bootstrap' => ['log'],\n    'components' => [\n        'log' => [\n            'flushInterval' => 1,\n            'targets' => [\n                [\n                    'class' => 'yii\\log\\FileTarget',\n                    'exportInterval' => 1,\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n> Note: 频繁的消息刷新和导出将降低你的应用性能。\n\n\n### 切换日志目标 <span id=\"toggling-log-targets\"></span>\n\n你可以通过配置 [[yii\\log\\Target::enabled|enabled]] 属性来开启或者禁用日志目标。\n你可以通过日志目标配置去做，或者是在你的代码中放入下面的PHP申明：\n\n```php\nYii::$app->log->targets['file']->enabled = false;\n```\n\n上面的代码要求您将目标命名为 `file`，像下面展示的那样，\n在 `targets` 数组中使用字符串键：\n\n```php\nreturn [\n    'bootstrap' => ['log'],\n    'components' => [\n        'log' => [\n            'targets' => [\n                'file' => [\n                    'class' => 'yii\\log\\FileTarget',\n                ],\n                'db' => [\n                    'class' => 'yii\\log\\DbTarget',\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n从版本 2.0.13 开始，您可以使用可调用的配置 [[yii\\log\\Target::enabled|enabled]]，\n以定义是否应启用日志目标的动态条件。\n有关示例，请参阅 [[yii\\log\\Target::setEnabled()]] 文档。\n\n### 创建新的目标 <span id=\"new-targets\"></span>\n\n创建一个新的日志目标类非常地简单。你主要需要实现 [[yii\\log\\Target::export()]] \n方法来发送 [[yii\\log\\Target::messages]] 数组的\n内容到一个指定的媒体中。你可以调用 [[yii\\log\\Target::formatMessage()]] 方法去格式化每个消息。\n更多细节，你可以参考任何一个包含在 Yii 发行版中的日志目标类。\n\n> Tip: 您可以使用 [PSR log target extension](https://github.com/samdark/yii2-psr-log-target) 尝试任何兼容 PSR-3 的日志记录器，\n  例如 [Monolog](https://github.com/Seldaek/monolog)，\n  而不是创建自己的日志记录器。\n\n## 性能分析 <span id=\"performance-profiling\"></span>\n\n性能分析是一个特殊的消息记录类型，它通常用在测量某段代码块的时间，\n并且找出性能瓶颈是什么。举个例子，[[yii\\db\\Command]] 类\n使用性能分析找出每个数据库查询的时间。\n\n为了使用性能分析，首先确定需要进行分析的代码块。\n然后像下面这样围住每个代码块：\n\n```php\n\\Yii::beginProfile('myBenchmark');\n\n...code block being profiled...\n\n\\Yii::endProfile('myBenchmark');\n```\n\n这里的 `myBenchmark` 代表一个唯一标记来标识一个代码块。之后当你检查分析结果的时候，\n你将使用这个标记来定位对应的代码块所花费的时间。\n\n对于确保 `beginProfile` 和 `endProfile` 对能够正确地嵌套，这是很重要的。\n例如，\n\n```php\n\\Yii::beginProfile('block1');\n\n    // some code to be profiled\n\n    \\Yii::beginProfile('block2');\n        // some other code to be profiled\n    \\Yii::endProfile('block2');\n\n\\Yii::endProfile('block1');\n```\n\n假如你漏掉 `\\Yii::endProfile('block1')` 或者切换了 `\\Yii::endProfile('block1')` 和 `\\Yii::endProfile('block2')` 的\n顺序，那么性能分析将不会工作。\n\n对于每个被分析的代码块，一个带有严重程度为 `profile` 的日志消息将被记录。\n你可以配置一个 [log target](#log-targets) 去收集这些\n消息，并且导出他们。[Yii debugger](tool-debugger.md) 有一个内建的性能分析面板能够展示分析结果。\n"
  },
  {
    "path": "docs/guide-zh-CN/runtime-overview.md",
    "content": "运行机制概述\n========\n\n每一次 Yii 应用开始处理 HTTP 请求时，它都会进行一个近似的流程。\n\n1. 用户提交指向 [入口脚本](structure-entry-scripts.md) `web/index.php` 的请求。\n2. 入口脚本会加载 [配置数组](concept-configurations.md) 并创建一个\n   [应用](structure-applications.md) 实例用于处理该请求。\n3. 应用会通过 [request（请求）](runtime-requests.md) 应用组件\n   解析被请求的 [路由](runtime-routing.md)。\n4. 应用创建一个 [controller（控制器）](structure-controllers.md) 实例具体处理请求。\n5. 控制器会创建一个 [action（动作）](structure-controllers.md) 实例并为该动作执行相关的 Filters（访问过滤器）。\n6. 如果任何一个过滤器验证失败，该动作会被取消。\n7. 如果全部的过滤器都通过，该动作就会被执行。\n8. 动作会加载一个数据模型，一般是从数据库中加载。\n9. 动作会渲染一个 View（视图），并为其提供所需的数据模型。\n10. 渲染得到的结果会返回给 [response（响应）](runtime-responses.md) 应用组件。\n11. 响应组件会把渲染结果发回给用户的浏览器。\n\n下面的示意图展示了应用是如何处理一个请求的。\n\n![Request Lifecycle](images/request-lifecycle.png)\n\n在这个版块中，我们会更加详细地描述某些步骤的具体运作。\n"
  },
  {
    "path": "docs/guide-zh-CN/runtime-requests.md",
    "content": "请求\n========\n\n一个应用的请求是用 [[yii\\web\\Request]] 对象来表示的，该对象提供了诸如\n请求参数（译者注：通常是GET参数或者POST参数）、HTTP头、cookies等信息。\n默认情况下，对于一个给定的请求，你可以通过 `request` [application component](structure-application-components.md) 应用组件（[[yii\\web\\Request]] 类的实例）\n获得访问相应的请求对象。在本章节，我们将介绍怎样在你的应用中使用这个组件。\n\n\n## 请求参数 <span id=\"request-parameters\"></span>\n\n要获取请求参数，你可以调用 `request` 组件的 [[yii\\web\\Request::get()|get()]] 方法和 [[yii\\web\\Request::post()|post()]] 方法。\n他们分别返回 `$_GET` 和 `$_POST` 的值。例如，\n\n```php\n$request = Yii::$app->request;\n\n$get = $request->get(); \n// 等价于: $get = $_GET;\n\n$id = $request->get('id');   \n// 等价于: $id = isset($_GET['id']) ? $_GET['id'] : null;\n\n$id = $request->get('id', 1);   \n// 等价于: $id = isset($_GET['id']) ? $_GET['id'] : 1;\n\n$post = $request->post(); \n// 等价于: $post = $_POST;\n\n$name = $request->post('name');   \n// 等价于: $name = isset($_POST['name']) ? $_POST['name'] : null;\n\n$name = $request->post('name', '');   \n// 等价于: $name = isset($_POST['name']) ? $_POST['name'] : '';\n```\n\n> Info: 建议你像上面那样通过 `request` 组件来获取请求参数，而不是\n直接访问 `$_GET` 和 `$_POST`。\n这使你更容易编写测试用例，因为你可以伪造数据来创建一个模拟请求组件。\n\n当实现 [RESTful APIs](rest-quick-start.md) 接口的时候，你经常需要获取通过 PUT， \nPATCH 或者其他的 [request methods](#request-methods) \n请求方法提交上来的参数。你可以通过调用 [[yii\\web\\Request::getBodyParam()]] 方法来获取这些参数。例如，\n\n```php\n$request = Yii::$app->request;\n\n// 返回所有参数\n$params = $request->bodyParams;\n\n// 返回参数 \"id\"\n$param = $request->getBodyParam('id');\n```\n\n> Info: 不同于 `GET` 参数，`POST`，`PUT`，`PATCH` 等等这些提交上来的参数是在请求体中被发送的。\n当你通过上面介绍的方法访问这些参数的时候，`request` 组件会解析这些参数。\n你可以通过配置 [[yii\\web\\Request::parsers]] 属性来自定义怎样解析这些参数。\n  \n\n## 请求方法 <span id=\"request-methods\"></span>\n \n你可以通过 `Yii::$app->request->method` 表达式来获取当前请求使用的HTTP方法。\n这里还提供了一整套布尔属性用于检测当前请求是某种类型。\n例如，\n\n```php\n$request = Yii::$app->request;\n\nif ($request->isAjax) { /* 该请求是一个 AJAX 请求 */ }\nif ($request->isGet)  { /* 请求方法是 GET */ }\nif ($request->isPost) { /* 请求方法是 POST */ }\nif ($request->isPut)  { /* 请求方法是 PUT */ }\n```\n\n## 请求URLs <span id=\"request-urls\"></span>\n\n`request` 组件提供了许多方式来检测当前请求的 URL。\n\n假设被请求的 URL 是 `https://example.com/admin/index.php/product?id=100`，\n你可以像下面描述的那样获取 URL 的各个部分：\n\n* [[yii\\web\\Request::url|url]]：返回 `/admin/index.php/product?id=100`, 此 URL 不包括主机信息部分。\n* [[yii\\web\\Request::absoluteUrl|absoluteUrl]]：返回 `https://example.com/admin/index.php/product?id=100`,\n  包含host infode的整个URL。\n* [[yii\\web\\Request::hostInfo|hostInfo]]：返回 `https://example.com`, 只有主机信息部分。\n* [[yii\\web\\Request::pathInfo|pathInfo]]：返回 `/product`，\n  这个是入口脚本之后，问号之前（查询字符串）的部分。\n* [[yii\\web\\Request::queryString|queryString]]：返回 `id=100`，问号之后的部分。\n* [[yii\\web\\Request::baseUrl|baseUrl]]：返回 `/admin`，主机信息之后，\n  入口脚本之前的部分。\n* [[yii\\web\\Request::scriptUrl|scriptUrl]]：返回 `/admin/index.php`，没有路径信息和查询字符串部分。\n* [[yii\\web\\Request::serverName|serverName]]：返回 `example.com`，URL 中的主机名。\n* [[yii\\web\\Request::serverPort|serverPort]]：返回 80，这是 web 服务中使用的端口。\n\n\n## HTTP头 <span id=\"http-headers\"></span> \n\n你可以通过 [[yii\\web\\Request::headers]] 属性返回的 [[yii\\web\\HeaderCollection|header collection]] 获取HTTP头信息。\n例如，\n\n```php\n// $headers 是一个 yii\\web\\HeaderCollection 对象\n$headers = Yii::$app->request->headers;\n\n// 返回 Accept header 值\n$accept = $headers->get('Accept');\n\nif ($headers->has('User-Agent')) { /* 这是一个 User-Agent 头 */ }\n```\n\n请求组件也提供了支持快速访问常用头的方法，包括：\n\n* [[yii\\web\\Request::userAgent|userAgent]]：返回 `User-Agent` 头。\n* [[yii\\web\\Request::contentType|contentType]]：返回 `Content-Type` 头的值，\n  `Content-Type` 是请求体中MIME类型数据。\n* [[yii\\web\\Request::acceptableContentTypes|acceptableContentTypes]]：返回用户可接受的内容MIME类型。\n  返回的类型是按照他们的质量得分来排序的。得分最高的类型将被最先返回。\n* [[yii\\web\\Request::acceptableLanguages|acceptableLanguages]]：返回用户可接受的语言。\n  返回的语言是按照他们的偏好层次来排序的。第一个参数代表最优先的语言。\n\n假如你的应用支持多语言，并且你想在终端用户最喜欢的语言中显示页面，\n那么你可以使用语言协商方法 [[yii\\web\\Request::getPreferredLanguage()]]。\n这个方法通过 [[yii\\web\\Request::acceptableLanguages|acceptableLanguages]] \n在你的应用中所支持的语言列表里进行比较筛选，返回最适合的语言。\n\n> Tip: 你也可以使用 [[yii\\filters\\ContentNegotiator|ContentNegotiator]] \n  过滤器进行动态确定哪些内容类型和语言应该在响应中使用。\n  这个过滤器实现了上面介绍的内容协商的属性和方法。\n\n\n## 客户端信息 <span id=\"client-information\"></span>\n\n你可以通过 [[yii\\web\\Request::userHost|userHost]]\n和 [[yii\\web\\Request::userIP|userIP]] 分别获取主机名和客户机的 IP 地址，\n例如，\n\n```php\n$userHost = Yii::$app->request->userHost;\n$userIP = Yii::$app->request->userIP;\n```\n\n## 受信任的代理和报头 <span id=\"trusted-proxies\"></span>\n\n在上一节中，您已经了解了如何获取主机和 IP 地址等用户信息。\n这将在正常设置中开箱即用，其中使用单个网络服务器为网站提供服务。\n然而，如果您的 Yii 应用程序在反向代理后面运行，则需要添加其他配置来检索此信息，\n因为直接客户端现在是代理，\n并且用户 IP 地址通过代理设置的报头传递给 Yii 应用程序。\n\n除非您明确信任代理，否则不应盲目信任代理提供的报头。\n从 2.0.13 开始，Yii 支持通过 `request` 组件的\n[[yii\\web\\Request::trustedHosts|trustedHosts]]，\n[[yii\\web\\Request::secureHeaders|secureHeaders]]，\n[[yii\\web\\Request::ipHeaders|ipHeaders]] 和\n[[yii\\web\\Request::secureProtocolHeaders|secureProtocolHeaders]]\n属性配置可信代理。\n\n以下是在反向代理数组后面运行的应用程序的请求配置，\n它们位于 `10.0.2.0/24` IP 网络中：\n\n```php\n'request' => [\n    // ...\n    'trustedHosts' => [\n        '10.0.2.0/24',\n    ],\n],\n```\n\n默认情况下，IP 由代理在 `X-Forwarded-For` 头中发送，协议（“http”或“https”）在 `X-Forwarded-Proto` 中发送。\n\n如果您的代理使用不同的报头，您可以使用请求配置来调整它们，例如：\n\n```php\n'request' => [\n    // ...\n    'trustedHosts' => [\n        '10.0.2.0/24' => [\n            'X-ProxyUser-Ip',\n            'Front-End-Https',\n        ],\n    ],\n    'secureHeaders' => [\n        'X-Forwarded-For',\n        'X-Forwarded-Host',\n        'X-Forwarded-Proto',\n        'X-Proxy-User-Ip',\n        'Front-End-Https',\n    ],\n    'ipHeaders' => [\n        'X-Proxy-User-Ip',\n    ],\n    'secureProtocolHeaders' => [\n        'Front-End-Https' => ['on']\n    ],\n],\n```\n\n使用上面的配置，`secureHeaders` 中列出的所有报头都会从请求中过滤掉，\n除了 `X-ProxyUser-Ip` 和 `Front-End-Https` 报头，以防请求由代理发出。\n在这种情况下，前者用于检索 `ipHeaders` 中配置的用户IP，\n后者将用于确定 [[yii\\web\\Request::getIsSecureConnection()]] 的结果。\n"
  },
  {
    "path": "docs/guide-zh-CN/runtime-responses.md",
    "content": "响应\n=========\n\n当一个应用在处理完一个[请求](runtime-requests.md)后, 这个应用会生成一个 [[yii\\web\\Response|response]] 响应对象并把这个响应对象发送给终端用户\n这个响应对象包含的信息有 HTTP 状态码，HTTP 头和主体内容等, \n从本质上说，网页应用开发最终的目标就是根据不同的请求去构建这些响应对象。\n\n在大多数实际应用情况下，你应该主要地去处理 `response` 这个 [应用组件](structure-application-components.md)，\n在默认情况下，它是一个继承自 [[yii\\web\\Response]] 的实例\n然而，Yii 也允许你创建自己的响应对象并发送给终端用户，这方面在后续会阐述。\n\n在本节，我们将会讲述如何组装和构建响应并把它发送给终端用户。\n\n\n## 状态码 <span id=\"status-code\"></span>\n\n构建响应要做的第一件事就是声明请求是否被成功处理，我们可通过设置\n[[yii\\web\\Response::$statusCode]] 这个属性来做到这一点，该属性接受一个有效的\n[HTTP 状态码](https://tools.ietf.org/html/rfc2616#section-10)。例如，表明该请求已被成功处理，\n可以设置状态码为 200，如下所示：\n\n```php\nYii::$app->response->statusCode = 200;\n```\n\n尽管如此，大多数情况下不需要明确设置状态码，\n因为 [[yii\\web\\Response::statusCode]] 状态码默认为 200，\n如果需要指定请求失败，可抛出对应的 HTTP 异常，如下所示：\n\n```php\nthrow new \\yii\\web\\NotFoundHttpException;\n```\n\n当[错误处理器](runtime-handling-errors.md) 捕获到一个异常，会从异常中提取状态码并赋值到响应，\n对于上述的 [[yii\\web\\NotFoundHttpException]] 对应 HTTP 404 状态码，\n以下为 Yii 预定义的 HTTP 异常：\n\n* [[yii\\web\\BadRequestHttpException]]：状态码 400。\n* [[yii\\web\\ConflictHttpException]]：状态码 409。\n* [[yii\\web\\ForbiddenHttpException]]：状态码 403。\n* [[yii\\web\\GoneHttpException]]：状态码 410。\n* [[yii\\web\\MethodNotAllowedHttpException]]：状态码 405。\n* [[yii\\web\\NotAcceptableHttpException]]：状态码 406。\n* [[yii\\web\\NotFoundHttpException]]：状态码 404。\n* [[yii\\web\\ServerErrorHttpException]]：状态码 500。\n* [[yii\\web\\TooManyRequestsHttpException]]：状态码 429。\n* [[yii\\web\\UnauthorizedHttpException]]：状态码 401。\n* [[yii\\web\\UnsupportedMediaTypeHttpException]]：状态码 415。\n\n如果想抛出的异常不在如上列表中，可创建一个 [[yii\\web\\HttpException]] 异常，\n带上状态码抛出，如下：\n\n```php\nthrow new \\yii\\web\\HttpException(402);\n```\n\n\n## HTTP 头部 <span id=\"http-headers\"></span>\n\n可在 `response` 组件中操控 [[yii\\web\\Response::headers|header collection]] 来发送 HTTP 头部信息，\n例如：\n\n```php\n$headers = Yii::$app->response->headers;\n\n// 增加一个 Pragma 头，已存在的Pragma 头不会被覆盖。\n$headers->add('Pragma', 'no-cache');\n\n// 设置一个Pragma 头. 任何已存在的Pragma 头都会被丢弃\n$headers->set('Pragma', 'no-cache');\n\n// 删除Pragma 头并返回删除的Pragma 头的值到数组\n$values = $headers->remove('Pragma');\n```\n\n> Info: 头名称是大小写敏感的，在 [[yii\\web\\Response::send()]] 方法\n  调用前新注册的头信息并不会发送给用户。\n\n\n## 响应主体 <span id=\"response-body\"></span>\n\n大多是响应应有一个主体存放你想要显示给终端用户的内容。\n\n如果已有格式化好的主体字符串，可赋值到响应的 [[yii\\web\\Response::content]] 属性，\n例如：\n\n```php\nYii::$app->response->content = 'hello world!';\n```\n\n如果在发送给终端用户之前需要格式化，应设置\n[[yii\\web\\Response::format|format]] 和 [[yii\\web\\Response::data|data]] 属性，[[yii\\web\\Response::format|format]]\n属性指定 [[yii\\web\\Response::data|data]]中数据格式化后的样式，例如：\n\n```php\n$response = Yii::$app->response;\n$response->format = \\yii\\web\\Response::FORMAT_JSON;\n$response->data = ['message' => 'hello world'];\n```\n\nYii支持以下可直接使用的格式，每个实现了[[yii\\web\\ResponseFormatterInterface|formatter]] 类，\n可自定义这些格式器或通过配置 [[yii\\web\\Response::formatters]] 属性来增加格式器。\n\n* [[yii\\web\\Response::FORMAT_HTML|HTML]]：通过 [[yii\\web\\HtmlResponseFormatter]] 来实现。\n* [[yii\\web\\Response::FORMAT_XML|XML]]：通过 [[yii\\web\\XmlResponseFormatter]] 来实现。\n* [[yii\\web\\Response::FORMAT_JSON|JSON]]：通过 [[yii\\web\\JsonResponseFormatter]] 来实现。\n* [[yii\\web\\Response::FORMAT_JSONP|JSONP]]：通过 [[yii\\web\\JsonResponseFormatter]] 来实现。\n* [[yii\\web\\Response::FORMAT_RAW|RAW]]：如果要直接发送响应而不应用任何格式，请使用此格式。\n\n上述响应主体可明确地被设置，但是在大多数情况下是通过[操作](structure-controllers.md) \n方法的返回值隐式地设置，常用场景如下所示：\n \n```php\npublic function actionIndex()\n{\n    return $this->render('index');\n}\n```\n\n上述的 `index` 操作返回 `index` 视图渲染结果，\n返回值会被 `response` 组件格式化后发送给终端用户。\n\n因为响应格式默认为 [[yii\\web\\Response::FORMAT_HTML|HTML]]，只需要在操作方法中返回一个字符串，\n如果想使用其他响应格式，应在返回数据前先设置格式，\n例如：\n\n```php\npublic function actionInfo()\n{\n    \\Yii::$app->response->format = \\yii\\web\\Response::FORMAT_JSON;\n    return [\n        'message' => 'hello world',\n        'code' => 100,\n    ];\n}\n```\n\n如上所述，除了使用默认的 `response` 应用组件，也可创建自己的响应对象并发送给终端用户，\n可在操作方法中返回该响应对象，如下所示：\n\n```php\npublic function actionInfo()\n{\n    return \\Yii::createObject([\n        'class' => 'yii\\web\\Response',\n        'format' => \\yii\\web\\Response::FORMAT_JSON,\n        'data' => [\n            'message' => 'hello world',\n            'code' => 100,\n        ],\n    ]);\n}\n```\n\n> Note: 如果创建你自己的响应对象，将不能在应用配置中设置 `response` 组件，尽管如此，\n  可使用 [依赖注入](concept-di-container.md) \n  应用通用配置到你新的响应对象。\n\n\n## 浏览器跳转 <span id=\"browser-redirection\"></span>\n\n浏览器跳转依赖于发送一个 `Location` HTTP 头，因为该功能通常被使用，\nYii提供对它提供了特别的支持。\n\n可调用[[yii\\web\\Response::redirect()]] 方法将用户浏览器跳转到一个 URL 地址，该方法设置合适的\n带指定 URL 的 `Location` 头并返回它自己为响应对象，在操作的方法中，\n可调用缩写版 [[yii\\web\\Controller::redirect()]]，例如：\n\n```php\npublic function actionOld()\n{\n    return $this->redirect('https://example.com/new', 301);\n}\n```\n\n在如上代码中，操作的方法返回 `redirect()` 方法的结果，如前所述，\n操作的方法返回的响应对象会被当总响应发送给终端用户。\n\n除了动作方法外，可直接调用[[yii\\web\\Response::redirect()]] 再调用\n[[yii\\web\\Response::send()]] 方法来确保没有其他内容追加到响应中。\n\n```php\n\\Yii::$app->response->redirect('https://example.com/new', 301)->send();\n```\n\n> Info: [[yii\\web\\Response::redirect()]] 方法默认会设置响应状态码为 302，该状态码会告诉浏览器请求的资源\n  *临时* 放在另一个 URI 地址上，可传递一个 301 状态码告知浏览器请求\n  的资源已经 *永久* 重定向到新的 URId 地址。\n\n如果当前请求为 AJAX 请求，发送一个 `Location` 头不会自动使浏览器跳转，为解决这个问题，\n[[yii\\web\\Response::redirect()]] 方法设置一个值为要跳转的URL的 `X-Redirect` 头，\n在客户端可编写 JavaScript \n代码读取该头部值然后让浏览器跳转对应的 URL。\n\n> Info: Yii 配备了一个 `yii.js` JavaScript 文件提供常用 JavaScript 功能，\n  包括基于 `X-Redirect` 头的浏览器跳转，\n  因此，如果你使用该 JavaScript 文件（通过 [[yii\\web\\YiiAsset]] 资源包注册），\n  就不需要编写 AJAX 跳转的代码。\n\n## 发送文件 <span id=\"sending-files\"></span>\n\n和浏览器跳转类似，文件发送是另一个依赖指定 HTTP 头的功能，\nYii 提供方法集合来支持各种文件发送需求，它们对 HTTP 头都有内置的支持。\n\n- [[yii\\web\\Response::sendFile()]]：发送一个已存在的文件到客户端\n- [[yii\\web\\Response::sendContentAsFile()]]：发送一个文本字符串作为文件到客户端\n- [[yii\\web\\Response::sendStreamAsFile()]]：发送一个已存在的文件流作为文件到客户端\n\n这些方法都将响应对象作为返回值，如果要发送的文件非常大，应考虑使用\n[[yii\\web\\Response::sendStreamAsFile()]] 因为它更节约内存，\n以下示例显示在控制器操作中如何发送文件：\n\n```php\npublic function actionDownload()\n{\n    return \\Yii::$app->response->sendFile('path/to/file.txt');\n}\n```\n\n如果不是在操作方法中调用文件发送方法，在后面还应调用 \n[[yii\\web\\Response::send()]] 没有其他内容追加到响应中。\n\n```php\n\\Yii::$app->response->sendFile('path/to/file.txt')->send();\n```\n\n一些浏览器提供特殊的名为 *X-Sendfile* 的文件发送功能，\n原理为将请求跳转到服务器上的文件，\nWeb 应用可在服务器发送文件前结束，为使用该功能，\n可调用 [[yii\\web\\Response::xSendFile()]]，\n如下简要列出一些常用 Web 服务器如何启用 `X-Sendfile` 功能：\n\n- Apache: [X-Sendfile](https://tn123.org/mod_xsendfile)\n- Lighttpd v1.4: [X-LIGHTTPD-send-file](https://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file)\n- Lighttpd v1.5: [X-Sendfile](https://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file)\n- Nginx: [X-Accel-Redirect](https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile/)\n- Cherokee: [X-Sendfile and X-Accel-Redirect](https://www.cherokee-project.com/doc/other_goodies.html#x-sendfile)\n\n\n## 发送响应 <span id=\"sending-response\"></span>\n\n在 [[yii\\web\\Response::send()]] 方法调用前响应中的内容不会发送给用户，\n该方法默认在 [[yii\\base\\Application::run()]]\n结尾自动调用，尽管如此，可以明确调用该方法强制立即发送响应。\n\n[[yii\\web\\Response::send()]] 方法使用以下步骤来发送响应：\n\n1. 触发 [[yii\\web\\Response::EVENT_BEFORE_SEND]] 事件。\n2. 调用 [[yii\\web\\Response::prepare()]] 来格式化 [[yii\\web\\Response::data|response data]] 为 \n   [[yii\\web\\Response::content|response content]]。\n3. 触发 [[yii\\web\\Response::EVENT_AFTER_PREPARE]] 事件。\n4. 调用 [[yii\\web\\Response::sendHeaders()]] 来发送注册的HTTP头\n5. 调用 [[yii\\web\\Response::sendContent()]] 来发送响应主体内容\n6. 触发 [[yii\\web\\Response::EVENT_AFTER_SEND]] 事件。\n\n一旦 [[yii\\web\\Response::send()]] 方法被执行后，其他地方调用该方法会被忽略，\n这意味着一旦响应发出后，就不能再追加其他内容。\n\n如你所见 [[yii\\web\\Response::send()]] 触发了几个实用的事件，\n通过响应这些事件可调整或包装响应。\n"
  },
  {
    "path": "docs/guide-zh-CN/runtime-routing.md",
    "content": "路由\n=======\n\n当[入口脚本](structure-entry-scripts.md)在调用 [[yii\\web\\Application::run()|run()]]\n方法时，它进行的第一个操作就是解析输入的请求，然后实例化对应的[控制器动作](structure-controllers.md)处理这个请求。\n该过程就被称为*引导路由（routing）*。\n路由相反的操作会将给定的路由和参数生成一个可访问的URL地址，\n这个操作叫做*创建URL*。\n创建出来的URL被请求的时候，路由处理器可以解析成原始的路由信息和参数。\n\n\n负责路由解析和创建URL的组件是 [[yii\\web\\UrlManager|URL管理器]],\nURL管理器在[程序组件](structure-application-components.md)中被注册成 `urlManager`。\n[[yii\\web\\UrlManager|URL管理器]] 提供方法 [[yii\\web\\UrlManager::parseRequest()|parseRequest()]] 来\n解析请求的URL并返回路由信息和参数，\n方法 [[yii\\web\\UrlManager::createUrl()|createUrl()]] 用来根据提供的路由和参数创建一个可访问的URL。\n \n在程序配置中配置 `urlManager` 组件，可以让你的应用不改变现有代码的情况下\n识别任意的URL格式。\n例如使用下面的代码创建一个到 `post/view` 控制器的 URL：\n\n```php\nuse yii\\helpers\\Url;\n\n// Url::to() 将调用 UrlManager::createUrl() 来创建URL\n$url = Url::to(['post/view', 'id' => 100]);\n```\n\n根据 `urlManager` 中的配置，创建出来的URL可能看起来像是以下的一种格式（或者其它的格式）。\n如果此URL被访问，将被解析成原来的路由和参数。\n\n```\n/index.php?r=post%2Fview&id=100\n/index.php/post/100\n/posts/100\n```\n\n\n## URL 格式化 <span id=\"url-formats\"></span>\n\n[[yii\\web\\UrlManager|URL管理器]]提供两种URL格式：\n\n- 默认URL格式；\n- 美化URL格式。\n\n默认URL格式使用一个参数`r`表示路由，\n并且使用一般的参数格式表示请求参数。例如，`/index.php?r=post/view&id=100`表示路由为`post/view`，参数`id`为100。\n默认URL格式不需要为[[yii\\web\\UrlManager|URL管理器]]做任何配置，\n并且在任何Web服务器都可以正常使用。\n\n美化URL格式在脚本名称后面使用更多的路径信息表示路由和参数信息。\n例如，用适当的[[yii\\web\\UrlManager::rules|URL规则]]，`/index.php/post/100`中附加的路径信息`/post/100`表示\n路由为`post/view`，参数`id`为100。\n要使用美化的URL格式，你需要根据实际的需求\n设计一组[[yii\\web\\UrlManager::rules|URL规则]]来规定URL的样式。\n \n你可以仅设置[[yii\\web\\UrlManager|URL管理器]]中的[[yii\\web\\UrlManager::enablePrettyUrl|开启美化URL]]来切换两种URL格式，\n而不必改动任何程序代码。\n\n\n## 路由 <span id=\"routing\"></span>\n\n路由处理包含两个步骤：\n\n- 请求被解析成一个路由和关联的参数；\n- 路由相关的一个[控制器动作](structure-controllers.md#actions)被创建出来处理这个请求。\n\n\n如果使用默认URL格式，解析请求到路由只是简单的从`GET`请求中得到命名为`r`的参数。\n\n\n当使用用美化URL格式时，[[yii\\web\\UrlManager|URL管理器]]将检查注册的[[yii\\web\\UrlManager::rules|URL规则]]，\n找到一条可以匹配的将请求转到路由的规则。\n如果找不到任何匹配的规则，系统将抛出[[yii\\web\\NotFoundHttpException]]异常。\n\n一旦请求解析成路由，系统将马上根据路由信息创建一个控制器动作。\n路由信息根据`/`分解成多个部分。例如，`site/index`将被分解成`site`和`index`两部分。\n每个部分都可能被认为是一个模块、控制器或动作的ID。\n从路由的第一个部分开始，系统将执行以下步骤创建所需模块（如果有模块的话）、控制器和动作：\n\n\n1. 设置应用系统作为当前的模块。\n2. 检查当前模块中的[[yii\\base\\Module::controllerMap|控制器映射]]是否存在当前ID。\n   如果存在，根据控制器映射中的定义创建一个控制器实例，\n   跳到步骤5处理路由剩下的部分。\n3. 检查ID是否为当前模块下[[yii\\base\\Module::modules|modules]]定义的子模块\n   如果是，创建对应子模块，\n   跳到步骤2使用刚创建的子模块处理路由下一部分。\n4. 将ID作为一个[控制器ID](structure-controllers.md#controller-ids)并创建一个控制器实例，\n   并用来处理路由剩下的部分。\n5. 控制器在自己的[[yii\\base\\Controller::actions()|动作映射]]中查找当前ID。\n   如果找到，根据映射中的定义创建一个动作。\n   如果没找到，控制器将尝试根据[动作ID](structure-controllers.md#action-ids)定义的动作方法创建一个行内动作。\n\n在上面的步骤中，如果出现任何错误，系统将抛出一个[[yii\\web\\NotFoundHttpException]]异常，\n表示路由处理程序出现的错误信息。\n\n\n### 缺省路由 <span id=\"default-route\"></span>\n\n如果传入请求并没有提供一个具体的路由，（一般这种情况多为于对首页的请求）此时就会启用由\n[[yii\\web\\Application::defaultRoute]] 属性所指定的缺省路由。\n该属性的默认值为 `site/index`，它指向 `site` 控制器的 `index`\n动作。你可以像这样在应用配置中调整该属性的值：\n\n```php\nreturn [\n    // ...\n    'defaultRoute' => 'main/index',\n];\n```\n\n和应用系统中的缺省路由类似，模块中也存在一个缺省路由，所以如果\n有一个`user`模块，当请求到`user`模块时，模块的[[yii\\base\\Module::defaultRoute|缺省路由]]\n被用来决定缺省的控制器。默认的缺省控制器名称是`default`。如果在[[yii\\base\\Module::defaultRoute|缺省路由]]中没有指定任何动作，\n[[yii\\base\\Controller::defaultAction|缺省动作]]将被用来决定缺省的动作。\n在这个例子中，完整的路由应该是`user/default/index`。\n\n\n### `catchAll` 路由（全拦截路由） <span id=\"catchall-route\"></span>\n\n有时候，你会想要将你的 Web应用临时调整到维护模式，所有的请求下都会显示相同的信息页。\n当然，要实现这一点有很多种方法。这里面最简单快捷的方法就是在应用配置中设置\n[[yii\\web\\Application::catchAll]] 属性：\n\n```php\nreturn [\n    // ...\n    'catchAll' => ['site/offline'],\n];\n```\n\n根据上面的配置，`site/offline`控制器将被用来处理所有的请求。\n\n`catchAll`属性应该配置成一个数组，第一个元素为路由，\n剩下的元素为键值对格式的[动作的参数](structure-controllers.md#action-parameters)。\n\n> Info: 如果此属性被设置，开发环境中的[调试工具条](https://github.com/yiisoft/yii2-debug/blob/master/docs/guide/README.md)\n> 将被停用。\n\n\n## 创建 URLs <span id=\"creating-urls\"></span>\n\nYii提供了一个助手方法[[yii\\helpers\\Url::to()]]，用来根据提供的路由和参数创建各种各样的URL。 \n例如：\n\n```php\nuse yii\\helpers\\Url;\n\n// 创建一个普通的路由URL：/index.php?r=post%2Findex\necho Url::to(['post/index']);\n\n// 创建一个带路由参数的URL：/index.php?r=post%2Fview&id=100\necho Url::to(['post/view', 'id' => 100]);\n\n// 创建一个带锚定的URL：/index.php?r=post%2Fview&id=100#content\necho Url::to(['post/view', 'id' => 100, '#' => 'content']);\n\n// 创建一个绝对路径URL：https://www.example.com/index.php?r=post%2Findex\necho Url::to(['post/index'], true);\n\n// 创建一个带https协议的绝对路径URL：https://www.example.com/index.php?r=post%2Findex\necho Url::to(['post/index'], 'https');\n```\n\n注意上面的例子中，我们假定使用默认的URL格式。如果启用美化的URL格式，\n创建出来的URL根据使用的[[yii\\web\\UrlManager::rules|URL规则]]可能有所不同。\n\n方法[[yii\\helpers\\Url::to()]]传入的路由是上下文相关的。\n根据以下规则确认传入的路由是一个*相对的*路由还是*绝对的*路由：\n\n- 如果路由是一个空字符串，则使用当前请求的[[yii\\web\\Controller::route|路由]]；\n- 如果路由中不存在`/`，则被认为是一个当前控制器下的动作ID，\n  且路由被附加到当前控制器的[[\\yii\\web\\Controller::uniqueId|唯一ID]]后面；\n- 如果路由不以`/`开头，被认为是当前模块下的路由，\n  路由将被附加到当前模块的[[\\yii\\base\\Module::uniqueId|唯一ID]]后面。\n\n从版本 2.0.2 开始，你可以使用根据[别名](concept-aliases.md)中定义的别名路由。如果是这种情况，\n别名将首先被转化为实际的路由，然后根据上面的规则转化成一个绝对路由。\n\n\n例如，假设当前的模块为`admin`，当前控制器为`post`，\n\n```php\nuse yii\\helpers\\Url;\n\n// 当前请求路由：/index.php?r=admin%2Fpost%2Findex\necho Url::to(['']);\n\n// 只有动作ID的相对路由：/index.php?r=admin%2Fpost%2Findex\necho Url::to(['index']);\n\n// 相对路由：/index.php?r=admin%2Fpost%2Findex\necho Url::to(['post/index']);\n\n// 绝对路由：/index.php?r=post%2Findex\necho Url::to(['/post/index']);\n\n// 假设有一个\"/post/index\"的别名\"@posts\"：/index.php?r=post%2Findex\necho Url::to(['@posts']);\n```\n\n方法 [[yii\\helpers\\Url::to()]] 实际上调用了 [[yii\\web\\UrlManager|URL管理器]] 中的 [[yii\\web\\UrlManager::createUrl()|createUrl()]] 方法\n和 [[yii\\web\\UrlManager::createAbsoluteUrl()|createAbsoluteUrl()]] 方法。\n下面的介绍中，我们将介绍如何配置 [[yii\\web\\UrlManager|URL管理器]] 来创建自定义的 URL 格式。\n\n\n方法 [[yii\\helpers\\Url::to()]] 同时支持创建和任何路由不相关的 URL。\n这种情况下，第一个参数不再传入一个数组，而是传入一个字符串。例如：\n \n```php\nuse yii\\helpers\\Url;\n\n// 当前请求URL：/index.php?r=admin%2Fpost%2Findex\necho Url::to();\n\n// 设定了别名的URL：https://example.com\nYii::setAlias('@example', 'https://example.com/');\necho Url::to('@example');\n\n// 绝对URL：https://example.com/images/logo.gif\necho Url::to('/images/logo.gif', true);\n```\n\n除了 `to()` 方法，[[yii\\helpers\\Url]] 助手类同时提供了多个其它创建 URL 的方法。\n例如：\n\n```php\nuse yii\\helpers\\Url;\n\n// 主页URL：/index.php?r=site%2Findex\necho Url::home();\n\n// 根URL，如果程序部署到一个Web目录下的子目录时非常有用\necho Url::base();\n\n// 当前请求的权威规范URL\n// 参考 https://en.wikipedia.org/wiki/Canonical_link_element\necho Url::canonical();\n\n// 记住当前请求的URL并在以后获取\nUrl::remember();\necho Url::previous();\n```\n\n\n## 使用美化的 URL <span id=\"using-pretty-urls\"></span>\n\n要使用美化的URL，像下面这样在应用配置中配置`urlManager`组件：\n\n```php\n[\n    'components' => [\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            'showScriptName' => false,\n            'enableStrictParsing' => false,\n            'rules' => [\n                // ...\n            ],\n        ],\n    ],\n]\n```\n\n[[yii\\web\\UrlManager::enablePrettyUrl|开启美化URL]] 属性被用来切换是否启用美化URL格式。\n虽然除了[[yii\\web\\UrlManager::enablePrettyUrl|开启美化URL]] 属性以外其它属性都是可选的，但是上面的配置是最常用到的。\n\n* [[yii\\web\\UrlManager::showScriptName|是否显示脚本名称]]：\n  此属性决定创建的URL中是否包含入口脚本名称。\n  例如，默认的 `/index.php/post/100`，开启此属性后将创建成 `/post/100`。\n* [[yii\\web\\UrlManager::enableStrictParsing|是否开启严格解析]]：此属性决定是否开启严格的请求解析。\n  如果设置为启用，请求的URL必须至少匹配 [[yii\\web\\UrlManager::rules|规则]] 中设定的一条规则作为正确请求，\n  否则系统将抛出 [[yii\\web\\NotFoundHttpException]] 异常。\n  如果严格解析被关闭，当 [[yii\\web\\UrlManager::rules|规则]] 中没有任何一条匹配时，\n  请求URL中的路径信息将被作为请求路由使用。\n* [[yii\\web\\UrlManager::rules|规则]]：此属性包含一个规则列表，用来规定如何解析和创建URL。\n  这是一个主要属性，你应该根据特定的应用环境配置此属性用来生成特定格式的URL。\n\n\n> Note: 如果你想在URL中隐藏入口脚本名称，除了要设置 [[yii\\web\\UrlManager::showScriptName|showScriptName]] 为 false，\n  同时应该配置 Web 服务，处理当请求 URL 没有特殊指定入口脚本时确定要执行哪个PHP文件，\n  如果你使用 Apache Web server，你可以参考[安装](start-installation.md#recommended-apache-configuration)中推荐的配置。\n\n\n\n\n### URL规则 <span id=\"url-rules\"></span>\n\n一个URL规则是类 [[yii\\web\\UrlRule]] 或子类的一个实例。每个URL规则包含一个匹配URL中的路径、路由和少量参数的规则。\n如果请求地址匹配一个URL规则，则此规则可以用来解析此请求。\n如果生成URL时路由和参数符合一个URL规则，则此规则可以用来生成此URL。\n\n\n如果开启了美化URL格式，[[yii\\web\\UrlManager|URL管理器]]使用定义的[[yii\\web\\UrlManager::rules|规则]]解析请求和创建URL。\n尤其注意，[[yii\\web\\UrlManager|URL管理器]]按照规则中定义的顺序依次检测请求地址，\n当找到*第一条*匹配的规则时停止。\n匹配到的规则将被用来解析请求URL到指定的路由和参数。\n同样的，创建URL的时候，[[yii\\web\\UrlManager|URL管理器]] 查找\n第一条匹配的的规则并用来生成URL。\n\n你可以配置 [[yii\\web\\UrlManager::rules]] 为一个数组，键为匹配规则，值为路由。\n每条键值对为一条URL规则。例如，下面的 [[yii\\web\\UrlManager::rules|规则]]\n配置了两条URL规则。第一条规则匹配URL `posts` 映射到路由 `post/index`。\n第二条规则匹配符合正则表达式 `post/(\\d+)` 的URL并映射到路由 `post/view`，同时包含\n一个参数 `id`。\n\n```php\n[\n    'posts' => 'post/index', \n    'post/<id:\\d+>' => 'post/view',\n]\n```\n\n> Info: 规则中的匹配模式用来匹配URL中的路径信息。例如，\n  `/index.php/post/100?source=ad` 中的路径信息为\n  `post/100`（开始和结尾处的 `/` 将被忽略）和模式 `post/(\\d+)` 匹配。\n\n除了定义 URL 规则外，你还可以将规则定义为配置数组。\n每个配置数组用来配置一个单独的 URL 规则对象。如果你需要配置 URL 规则的其它参数时可以这样用。\n例如：\n\n```php\n[\n    // ...其它 URL 规则...\n    \n    [\n        'pattern' => 'posts',\n        'route' => 'post/index',\n        'suffix' => '.json',\n    ],\n]\n```\n\n如果你在URL规则中不配置 `class` 选项，默认将使用类 [[yii\\web\\UrlRule]]。\n\n\n\n### 命名参数 <span id=\"named-parameters\"></span>\n\n一条URL规则可以对匹配模式中的参数设置格式为 `<ParamName:RegExp>` 的命名，\n其中 `ParamName` 指定参数的名称，`RegExp` 是可选的用来匹配参数值得正则表达式。\n如果没有设置 `RegExp`，表示参数值为不包含 `/` 的字符串。\n\n\n> Note: 你可以仅针对参数设置正则表达式，其余部分设置普通文本。\n\n当一条规则用来匹配URL时，符合匹配规则的相关的参数值被填充到规则中，\n并且这些参数可以在 `request` 组件中使用 `$_GET` 获取到。\n当规则用来创建 URL 时，\n提供的参数值将被插入到规则定义的指定位置。\n\n让我们使用一些例子来说明命名参数是如何工作的。假设我们定义了以下三条 URL 规则：\n\n```php\n[\n    'posts/<year:\\d{4}>/<category>' => 'post/index',\n    'posts' => 'post/index',\n    'post/<id:\\d+>' => 'post/view',\n]\n```\n\n当规则用来解析 URL 时：\n\n- 根据第二条规则，`/index.php/posts` 被解析成路由 `post/index`；\n- 根据第一条规则，`/index.php/posts/2014/php` 被解析成路由 `post/index`，\n  参数 `year` 的值是 2014，参数 `category` 的值是 `php`；\n- 根据第三条规则，`/index.php/post/100` 被解析成路由 `post/view`，\n  参数 `id` 的值是 100；\n- 当[[yii\\web\\UrlManager::enableStrictParsing]] 设置为 `true` 时，`/index.php/posts/php` 将导致一个[[yii\\web\\NotFoundHttpException]] 异常，\n  因为无法匹配任何规则。如果 [[yii\\web\\UrlManager::enableStrictParsing]] 设为 `false`（默认值），\n  路径部分 `posts/php` 将被作为路由。\n\n当规则用来生成 URL 时：\n\n- 根据第二条规则 `Url::to(['post/index'])` 生成 `/index.php/posts`；\n- 根据第一条规则 `Url::to(['post/index', 'year' => 2014, 'category' => 'php'])` 生成 `/index.php/posts/2014/php`；\n- 根据第三条规则 `Url::to(['post/view', 'id' => 100])` 生成 `/index.php/post/100`；\n- 根据第三条规则 `Url::to(['post/view', 'id' => 100, 'source' => 'ad'])` 生成 `/index.php/post/100?source=ad`。\n  因为参数 `source` 在规则中没有指定，将被作为普通请求参数附加到生成的 URL 后面。\n- `Url::to(['post/index', 'category' => 'php'])` 生成 `/index.php/post/index?category=php`。\n  注意因为没有任何规则适用，将把路由信息当做路径信息来生成URL，\n  并且所有参数作为请求查询参数附加到 URL 后面。\n   \n\n### 参数化路由 <span id=\"parameterizing-routes\"></span>\n\n你可以在 URL 规则中嵌入参数名称，这样可以允许一个 URL 规则用来匹配多个路由。\n例如，下面的规则在路由中嵌入了 `controller` 和 `action` 两个参数。\n\n```php\n'rules' => [\n    '<controller:(post|comment)>/create' => '<controller>/create',\n    '<controller:(post|comment)>/<id:\\d+>/<action:(update|delete)>' => '<controller>/<action>',\n    '<controller:(post|comment)>/<id:\\d+>' => '<controller>/view',\n    '<controller:(post|comment)>s' => '<controller>/index',\n]\n```\n\n解析 URL `/index.php/comment/100/update` 时，第二条规则适用，设置参数 `controller` 为 `comment`，\n设置参数 `action` 为 `update`。自然的，路由 `<controller>/<action>` 变成了 `comment/update`。\n\n同样的，根据路由 `comment/index` 创建 URL 时，最后一条规则适用，将生成 URL `/index.php/comments`。\n\n> Info: 使用参数化的路由，可以显著的减少 URL 规则的数量，\n  可以显著提高[[yii\\web\\UrlManager|URL管理器]]的效率。\n\n### 默认参数值 <span id=\"default-parameter-values\"></span>\n\n默认的，所有规则中定义的参数都是必须的。如果一个请求 URL 不存在其中一个参数，\n或者创建URL时没有指定其中一个参数，则无法应用此规则。\n如果需要设置某些参数为可选的，必须设置规则的[[yii\\web\\UrlRule::defaults|默认值]]属性。\n此属性中列出的参数将变成可选的且在没有指定时会使用此处设置的默认值。\n\n下面设置的规则中，参数 `page` 和 `tag` 都是可选的，当没有指定时将分别使用值 1 和空字符串。\n\n\n```php\n[\n    // ...其它规则...\n    [\n        'pattern' => 'posts/<page:\\d+>/<tag>',\n        'route' => 'post/index',\n        'defaults' => ['page' => 1, 'tag' => ''],\n    ],\n]\n```\n\n上面的规则可以用来解析或创建下面的 URL：\n\n* `/index.php/posts`：`page` 为 1，`tag` 为 ''。\n* `/index.php/posts/2`：`page` 为 2，`tag` 为 ''。\n* `/index.php/posts/2/news`：`page` 为 2，`tag`为 `'news'`。\n* `/index.php/posts/news`：`page` 为 1，`tag` 为 `'news'`。\n\n如果不使用可选参数，你必须创建 4 条规则才可以实现相同的效果。\n\n> Note: 如果 [[yii\\web\\UrlRule::$pattern|pattern]] 中仅包含可选参数和斜杠，\n  只有所有参数被忽略时第一个参数才被忽略。\n\n\n### 带服务名称的规则 <span id=\"rules-with-server-names\"></span>\n\n可以在URL规则中设置Web服务的名称，如果你需要使你的应用程序在不同的Web服务名称下表现不同的话。\n例如，下面的规则将URL`https://admin.example.com/login`解析成路由`admin/user/login`，\nURL`https://www.example.com/login`解析成路由`site/login`。\n\n```php\n[\n    'https://admin.example.com/login' => 'admin/user/login',\n    'https://www.example.com/login' => 'site/login',\n]\n```\n\n你还可以在服务名称中嵌入参数用来动态的提取服务名称。例如，下面的规则\n将URL`https://en.example.com/posts`解析成路由`post/index`且参数`language=en`。\n\n```php\n[\n    'http://<language:\\w+>.example.com/posts' => 'post/index',\n]\n```\n\n从版本 2.0.11 开始，你还可以使用不带协议类型的模式来同时匹配 `http` 和 `https`。\n规则语法和上面相比只是忽略掉 `http:` 部分，例如：`'//www.example.com/login' => 'site/login'`。\n\n> Note: 带服务名称的规则**不应该**包含任何子目录。例如，如果程序入口脚本在 `https://www.example.com/sandbox/blog/index.php`，\n  应该使用 `https://www.example.com/posts` 代替 `https://www.example.com/sandbox/blog/posts`。\n  这样才可以将你的应用部署到任何目录而不需要更改 URL 规则。Yii 将会自动的检测应用程序所在的根目录。\n\n\n### URL 后缀 <span id=\"url-suffixes\"></span>\n\n你可能因为各种目的需要在 URL 后面添加后缀。例如，你可以在URL后面添加 `.html` 让其看起来像是一个 HTML 页面；\n也可以添加 `.json` 用来表明需要的返回值内容类型。\n可以参考下面的系统配置，\n通过设置 [[yii\\web\\UrlManager::suffix]] 属性来达到此目的:\n\n```php\n[\n    'components' => [\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            'showScriptName' => false,\n            'enableStrictParsing' => true,\n            'suffix' => '.html',\n            'rules' => [\n                // ...\n            ],\n        ],\n    ],\n]\n```\n\n上面的配置允许[[yii\\web\\UrlManager|URL管理器]]识别或生成带 `.html` 后缀的 URL。\n\n\n> Tip: 你可以设置URL后缀为 `/` 让所有的 URL 以斜线结束。\n\n> Note: 当你配置 URL 后缀时，如果请求的 URL 没有此后缀，系统将认为此 URL 无法识别。\n  这是 SEO（搜索引擎优化）的最佳实践。\n  \n有时你可能需要在不同的URL使用不同的后缀。可以通过在不同的URL规则下不同的设置[[yii\\web\\UrlRule::suffix|后缀]]属性。\nURL规则中此属性将覆盖在[[yii\\web\\UrlManager|URL管理器]]中设置的值。\n例如，下面的配置中全局使用 `.html` 后缀，但是定义了一个自定义的使用 `.json` 为后缀的规则。\n\n\n```php\n[\n    'components' => [\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            // ...\n            'suffix' => '.html',\n            'rules' => [\n                // ...\n                [\n                    'pattern' => 'posts',\n                    'route' => 'post/index',\n                    'suffix' => '.json',\n                ],\n            ],\n        ],\n    ],\n]\n```\n\n### HTTP 方法 <span id=\"http-methods\"></span>\n\n当使用 RESTful 接口时，经常需要根据 HTTP 请求方法将同样的URL解析到不同的路由。\n可以容易的通过将支持的 HTTP 方法设置为 URL 规则的前缀来实现这个目的。\n如果一个规则需要支持多种 HTTP 方法，可以将方法名称用逗号隔开。\n例如，下面的规则有相同的模式 `post/<id:\\d+>` 但是支持不同的 HTTP 方法。\n一个 `PUT post/100` 请求将被解析到 `post/update`，`GET post/100` 请求将被解析到 `post/view`。\n\n```php\n'rules' => [\n    'PUT,POST post/<id:\\d+>' => 'post/update',\n    'DELETE post/<id:\\d+>' => 'post/delete',\n    'post/<id:\\d+>' => 'post/view',\n]\n```\n\n> Note: 如果一个 URL 规则包含 HTTP 方法，这个规则将只能用来解析请求，除非 `GET` 请求明确被指定在 HTTP 方法中，\n  否则创建 URL 时此规则将被[[yii\\web\\UrlManager|URL管理器]]忽略。\n\n> Tip: 为了简化 RESTful 接口的路由定义，Yii 提供了一个特殊的URL规则类 [[yii\\rest\\UrlRule]]\n  支持高效的且支持一些设想中的功能，像自动多元化控制器 ID。\n  更多信息，请参考 RESTful 接口说明中的[路由](rest-routing.md)章节。\n\n\n### 动态添加规则 <span id=\"adding-rules\"></span>\n\nURL规则可以动态添加到[[yii\\web\\UrlManager|URL管理器]]。如果[模块](structure-modules.md)需要管理自己的URL规则时很有必要。\n如果需要使路由处理过程中动态添加的规则可用，\n你应该在应用程序[启动引导](runtime-bootstrapping.md)时添加。\n对模块来说，需要实现 [[yii\\base\\BootstrapInterface]] 接口的 [[yii\\base\\BootstrapInterface::bootstrap()|bootstrap()]] 方法，\n类似下面这样动态添加规则：\n\n```php\npublic function bootstrap($app)\n{\n    $app->getUrlManager()->addRules([\n        // 规则在这里定义\n    ], false);\n}\n```\n\n注意你需要同时在 [[yii\\web\\Application::bootstrap]] 中指定这些模块，这样模块才可以参与到\n[启动引导](runtime-bootstrapping.md)过程中。\n\n\n### 创建规则类 <span id=\"creating-rules\"></span>\n\n尽管默认的 [[yii\\web\\UrlRule]] 类已经足够灵活可以处理大部分项目了，\n有时还是会需要创建一个自定义的规则类。例如，在一个汽车经销网站，你可能会需要使用\n这样的URL格式 `/Manufacturer/Model`，`Manufacturer` 和 `Model` 必须同时匹配保存在数据库中的一些数据。\n默认的规则类只能使用静态定义而无法适应此种情况。\n\n我们可以创建一个自定义的 URL 规则类来解决这个问题。\n\n```php\n<?php\n\nnamespace app\\components;\n\nuse yii\\web\\UrlRuleInterface;\nuse yii\\base\\BaseObject;\n\nclass CarUrlRule extends BaseObject implements UrlRuleInterface\n{\n    public function createUrl($manager, $route, $params)\n    {\n        if ($route === 'car/index') {\n            if (isset($params['manufacturer'], $params['model'])) {\n                return $params['manufacturer'] . '/' . $params['model'];\n            } elseif (isset($params['manufacturer'])) {\n                return $params['manufacturer'];\n            }\n        }\n        return false; // this rule does not apply\n    }\n\n    public function parseRequest($manager, $request)\n    {\n        $pathInfo = $request->getPathInfo();\n        if (preg_match('%^(\\w+)(/(\\w+))?$%', $pathInfo, $matches)) {\n            // 检查 $matches[1] 和 $matches[3]\n            // 确认是否匹配到一个数据库中保存的厂家和型号。\n            // 如果匹配，设置参数 $params['manufacturer'] 和 / 或 $params['model']\n            // 返回 ['car/index', $params]\n        }\n        return false; // 本规则不会起作用\n    }\n}\n```\n\n在 [[yii\\web\\UrlManager::rules]] 配置中设置新定义的规则类：\n\n```php\n'rules' => [\n    // ...其它规则...\n    [\n        'class' => 'app\\components\\CarUrlRule',\n        // ...配置其它参数...\n    ],\n]\n```\n\n\n## URL规范化 <span id=\"url-normalization\"></span>\n\n从 2.0.10 版开始[[yii\\web\\UrlManager|Url管理器]]可以配置用[[yii\\web\\UrlNormalizer|URL规范器]]来处理\n相同URL的不同格式，例如是否带结束斜线。因为技术上来说 `https://example.com/path`\n和 `https://example.com/path/` 是完全不同的 URL，两个地址返回相同的内容会导致SEO排名降低。\n默认情况下 URL 规范器会合并连续的斜线，根据配置决定是否添加或删除结尾斜线，\n并且会使用[永久重定向](https://zh.wikipedia.org/wiki/HTTP_301)将地址重新跳转到规范化后的URL。\nURL规范器可以针对URL管理器全局配置，也可以针对规则单独配置 - 默认每个规则都使用URL管理器中的规范器。\n你可以针对特定的URL规则设置 [[yii\\web\\UrlRule::$normalizer|UrlRule::$normalizer]] 为 `false` 来关闭规范化。\n\n\n下面的例子显示了一个[[yii\\web\\UrlNormalizer|URL规范器]]的配置：\n\n```php\n'urlManager' => [\n    'enablePrettyUrl' => true,\n    'showScriptName' => false,\n    'enableStrictParsing' => true,\n    'suffix' => '.html',\n    'normalizer' => [\n        'class' => 'yii\\web\\UrlNormalizer',\n        // 调试时使用临时跳转代替永久跳转\n        'action' => UrlNormalizer::ACTION_REDIRECT_TEMPORARY,\n    ],\n    'rules' => [\n        // ...其它规则...\n        [\n            'pattern' => 'posts',\n            'route' => 'post/index',\n            'suffix' => '/',\n            'normalizer' => false, // 针对此规则关闭规范器\n        ],\n        [\n            'pattern' => 'tags',\n            'route' => 'tag/index',\n            'normalizer' => [\n                // 针对此规则不合并连续的斜线\n                'collapseSlashes' => false,\n            ],\n        ],\n    ],\n]\n```\n\n> Note: 默认 [[yii\\web\\UrlManager::$normalizer|UrlManager::$normalizer]] 规范器是关闭的。你需要明确配置其开启\n  来启用 URL 规范化。\n\n\n\n## 性能考虑 <span id=\"performance-consideration\"></span>\n\n在开发复杂的 Web 应用程序时，优化 URL 规则非常重要，以便解析请求和创建 URL 所需\n的时间更少。\n\n通过使用参数化路由，您可以减少 URL 规则的数量，这可以显著提高性能。\n\n当解析或创建URL时，[[yii\\web\\UrlManager|URL manager]] 按照它们声明的顺序检查 URL 规则。\n因此，您可以考虑调整 URL 规则的顺序，以便在较少使用的规则之前放置更具体和/或更常用的规则。\n\n如果多个 URL 规则使用相同的前缀，你可以考虑使用 [[yii\\web\\GroupUrlRule]]，\n这样作为一个组合，[[yii\\web\\UrlManager|URL管理器]]会更高效。\n特别是当应用程序由模块组合而成时，每个模块都有各自的 URL 规则且都有各自的模块 ID 作为前缀。\n"
  },
  {
    "path": "docs/guide-zh-CN/runtime-sessions-cookies.md",
    "content": "Sessions 和 Cookies\n====================\n\nSessions 和 cookies 允许数据在多次请求中保持，\n在纯 PHP 中，可以分别使用全局变量 `$_SESSION` 和 `$_COOKIE` 来访问，Yii 将 session 和 cookie 封装成对象并增加一些功能，\n可通过面向对象方式访问它们。\n\n\n## Sessions <span id=\"sessions\"></span>\n\n和 [请求](runtime-requests.md) 和 [响应](runtime-responses.md)类似，\n默认可通过为 [[yii\\web\\Session]] 实例的 `session` \n[应用组件](structure-application-components.md) 来访问 sessions。\n\n\n### 开启和关闭 Sessions <span id=\"opening-closing-sessions\"></span>\n\n可使用以下代码来开启和关闭 session。\n\n```php\n$session = Yii::$app->session;\n\n// 检查session是否开启 \nif ($session->isActive) ...\n\n// 开启session\n$session->open();\n\n// 关闭session\n$session->close();\n\n// 销毁session中所有已注册的数据\n$session->destroy();\n```\n\n多次调用[[yii\\web\\Session::open()|open()]] 和[[yii\\web\\Session::close()|close()]] 方法并不会产生错误，\n因为方法内部会先检查session是否已经开启。\n\n\n### 访问 Session 数据 <span id=\"access-session-data\"></span>\n\n可使用如下方式访问session中的数据：\n\n```php\n$session = Yii::$app->session;\n\n// 获取session中的变量值，以下用法是相同的：\n$language = $session->get('language');\n$language = $session['language'];\n$language = isset($_SESSION['language']) ? $_SESSION['language'] : null;\n\n// 设置一个session变量，以下用法是相同的：\n$session->set('language', 'en-US');\n$session['language'] = 'en-US';\n$_SESSION['language'] = 'en-US';\n\n// 删除一个session变量，以下用法是相同的：\n$session->remove('language');\nunset($session['language']);\nunset($_SESSION['language']);\n\n// 检查session变量是否已存在，以下用法是相同的：\nif ($session->has('language')) ...\nif (isset($session['language'])) ...\nif (isset($_SESSION['language'])) ...\n\n// 遍历所有session变量，以下用法是相同的：\nforeach ($session as $name => $value) ...\nforeach ($_SESSION as $name => $value) ...\n```\n\n> Info: 当使用 `session` 组件访问 session 数据时候，\n  如果 session 没有开启会自动开启，\n  这和通过 `$_SESSION` 不同，`$_SESSION` 要求先执行 `session_start()`。\n\n当 session 数据为数组时，`session` 组件会限制你直接修改数据中的单元项，\n例如：\n\n```php\n$session = Yii::$app->session;\n\n// 如下代码不会生效\n$session['captcha']['number'] = 5;\n$session['captcha']['lifetime'] = 3600;\n\n// 如下代码会生效：\n$session['captcha'] = [\n    'number' => 5,\n    'lifetime' => 3600,\n];\n\n// 如下代码也会生效：\necho $session['captcha']['lifetime'];\n```\n\n可使用以下任意一个变通方法来解决这个问题：\n\n```php\n$session = Yii::$app->session;\n\n// 直接使用$_SESSION (确保Yii::$app->session->open() 已经调用)\n$_SESSION['captcha']['number'] = 5;\n$_SESSION['captcha']['lifetime'] = 3600;\n\n// 先获取session数据到一个数组，修改数组的值，然后保存数组到session中\n$captcha = $session['captcha'];\n$captcha['number'] = 5;\n$captcha['lifetime'] = 3600;\n$session['captcha'] = $captcha;\n\n// 使用ArrayObject 数组对象代替数组\n$session['captcha'] = new \\ArrayObject;\n...\n$session['captcha']['number'] = 5;\n$session['captcha']['lifetime'] = 3600;\n\n// 使用带通用前缀的键来存储数组\n$session['captcha.number'] = 5;\n$session['captcha.lifetime'] = 3600;\n```\n\n为更好的性能和可读性，推荐最后一种方案，\n也就是不用存储 session 变量为数组，\n而是将每个数组项变成有相同键前缀的 session 变量。\n\n\n### 自定义 Session 存储 <span id=\"custom-session-storage\"></span>\n\n[[yii\\web\\Session]] 类默认存储 session 数据为文件到服务器上，\nYii 提供以下 session 类实现不同的 session 存储方式：\n\n* [[yii\\web\\DbSession]]：存储 session 数据在数据表中\n* [[yii\\web\\CacheSession]]：存储 session 数据到缓存中，缓存和配置中的[缓存组件](caching-data.md#cache-components)相关\n* [[yii\\redis\\Session]]：存储 session 数据到以 [redis](https://redis.io/) 作为存储媒介中\n* [[yii\\mongodb\\Session]]：存储 session 数据到 [MongoDB](https://www.mongodb.com/)。\n\n所有这些 session 类支持相同的 API 方法集，因此，\n切换到不同的 session 存储介质不需要修改项目使用 session 的代码。\n\n> Note: 如果通过`$_SESSION`访问使用自定义存储介质的session，\n  需要确保session已经用[[yii\\web\\Session::open()]] 开启，\n  这是因为在该方法中注册自定义session存储处理器。\n\n> Note: 如果使用自定义会话存储，则可能需要显式配置会话垃圾收集器。\n  一些安装的 PHP（例如 Debian）使用垃圾收集器概率为 0，并在计划任务中离线清理会话文件。\n  此过程不适用于您的自定义存储，\n  因此您需要配置 [[yii\\web\\Session::$GCProbability]] 以使用非零值。\n\n学习如何配置和使用这些组件类请参考它们的 API 文档，如下为一个示例\n显示如何在应用配置中配置 [[yii\\web\\DbSession]]\n将数据表作为 session 存储介质。\n\n```php\nreturn [\n    'components' => [\n        'session' => [\n            'class' => 'yii\\web\\DbSession',\n            // 'db' => 'mydb',  // 数据库连接的应用组件ID，默认为'db'.\n            // 'sessionTable' => 'my_session', // session 数据表名，默认为'session'.\n        ],\n    ],\n];\n```\n\n也需要创建如下数据库表来存储 session 数据：\n\n```sql\nCREATE TABLE session\n(\n    id CHAR(40) NOT NULL PRIMARY KEY,\n    expire INTEGER,\n    data BLOB\n)\n```\n\n其中 'BLOB' 对应你选择的数据库管理系统的 BLOB-type 类型，以下一些常用数据库管理系统的 BLOB 类型：\n\n- MySQL: LONGBLOB\n- PostgreSQL: BYTEA\n- MSSQL: BLOB\n\n> Note: 根据 php.ini 设置的 `session.hash_function`，你需要调整 `id` 列的长度，\n  例如，如果 `session.hash_function=sha256`，\n  应使用长度为 64 而不是 40 的 char 类型。\n\n或者，可以通过以下迁移完成：\n\n```php\n<?php\n\nuse yii\\db\\Migration;\n\nclass m170529_050554_create_table_session extends Migration\n{\n    public function up()\n    {\n        $this->createTable('{{%session}}', [\n            'id' => $this->char(64)->notNull(),\n            'expire' => $this->integer(),\n            'data' => $this->binary()\n        ]);\n        $this->addPrimaryKey('pk-id', '{{%session}}', 'id');\n    }\n\n    public function down()\n    {\n        $this->dropTable('{{%session}}');\n    }\n}\n```\n\n### Flash 数据 <span id=\"flash-data\"></span>\n\nFlash 数据是一种特别的 session 数据，它一旦在某个请求中设置后，\n只会在下次请求中有效，然后该数据就会自动被删除。\n常用于实现只需显示给终端用户一次的信息，\n如用户提交一个表单后显示确认信息。\n\n可通过 `session` 应用组件设置或访问 `session`，例如：\n\n```php\n$session = Yii::$app->session;\n\n// 请求 #1\n// 设置一个名为\"postDeleted\" flash 信息\n$session->setFlash('postDeleted', 'You have successfully deleted your post.');\n\n// 请求 #2\n// 显示名为\"postDeleted\" flash 信息\necho $session->getFlash('postDeleted');\n\n// 请求 #3\n// $result 为 false，因为flash信息已被自动删除\n$result = $session->hasFlash('postDeleted');\n```\n\n和普通 session 数据类似，可将任意数据存储为 flash 数据。\n\n当调用 [[yii\\web\\Session::setFlash()]] 时, 会自动覆盖相同名的已存在的任何数据，\n为将数据追加到已存在的相同名 flash 中，可改为调用 [[yii\\web\\Session::addFlash()]]。\n例如:\n\n```php\n$session = Yii::$app->session;\n\n// 请求 #1\n// 在名称为\"alerts\"的flash信息增加数据\n$session->addFlash('alerts', 'You have successfully deleted your post.');\n$session->addFlash('alerts', 'You have successfully added a new friend.');\n$session->addFlash('alerts', 'You are promoted.');\n\n// 请求 #2\n// $alerts 为名为'alerts'的flash信息，为数组格式\n$alerts = $session->getFlash('alerts');\n```\n\n> Note: 不要在相同名称的 flash 数据中使用 [[yii\\web\\Session::setFlash()]] 的同时也使用 [[yii\\web\\Session::addFlash()]]，\n  因为后一个防范会自动将 flash 信息转换为数组以使新的 flash 数据可追加进来，因此，\n  当你调用 [[yii\\web\\Session::getFlash()]] 时，\n  会发现有时获取到一个数组，有时获取到一个字符串，\n  取决于你调用这两个方法的顺序。\n\n> Tip: 要显示 Flash 消息，您可以通过以下方式使用 [[yii\\bootstrap\\Alert|bootstrap Alert]] 小部件：\n>\n> ```php\n> echo Alert::widget([\n>    'options' => ['class' => 'alert-info'],\n>    'body' => Yii::$app->session->getFlash('postDeleted'),\n> ]);\n> ```\n\n\n## Cookies <span id=\"cookies\"></span>\n\nYii 使用 [[yii\\web\\Cookie]] 对象来代表每个 cookie，\n[[yii\\web\\Request]] 和 [[yii\\web\\Response]]\n通过名为 'cookies' 的属性维护一个 cookie 集合，前者的 cookie 集合代表请求提交的 cookies，\n后者的 cookie 集合表示发送给用户的 cookies。\n\n控制器是直接处理请求和响应的部分。 因此, 应当在控制器中读取和发送 cookie 。\n(译者注：意思在控制器中处理 cookie 是安全的。)\n\n### 读取 Cookies <span id=\"reading-cookies\"></span>\n\n当前请求的 cookie 信息可通过如下代码获取：\n\n```php\n// 从 \"request\" 组件中获取 cookie 集合(yii\\web\\CookieCollection)\n$cookies = Yii::$app->request->cookies;\n\n// 获取名为 \"language\" cookie 的值，如果不存在，返回默认值 \"en\"\n$language = $cookies->getValue('language', 'en');\n\n// 另一种方式获取名为 \"language\" cookie 的值\nif (($cookie = $cookies->get('language')) !== null) {\n    $language = $cookie->value;\n}\n\n// 可将 $cookies 当作数组使用\nif (isset($cookies['language'])) {\n    $language = $cookies['language']->value;\n}\n\n// 判断是否存在名为 \"language\" 的 cookie\nif ($cookies->has('language')) ...\nif (isset($cookies['language'])) ...\n```\n\n\n### 发送 Cookies <span id=\"sending-cookies\"></span>\n\n可使用如下代码发送 cookie 到终端用户：\n\n```php\n// 从 \"response\" 组件中获取 cookie 集合(yii\\web\\CookieCollection)\n$cookies = Yii::$app->response->cookies;\n\n// 在要发送的响应中添加一个新的 cookie\n$cookies->add(new \\yii\\web\\Cookie([\n    'name' => 'language',\n    'value' => 'zh-CN',\n]));\n\n// 删除一个 cookie\n$cookies->remove('language');\n// 等同于以下删除代码\nunset($cookies['language']);\n```\n\n除了上述例子定义的 [[yii\\web\\Cookie::name|name]] 和 [[yii\\web\\Cookie::value|value]] 属性\n[[yii\\web\\Cookie]] 类也定义了其他属性来实现 cookie 的各种信息，如\n[[yii\\web\\Cookie::domain|domain]]，[[yii\\web\\Cookie::expire|expire]]\n可配置这些属性到 cookie 中并添加到响应的 cookie 集合中。\n\n> Note: 为安全起见 [[yii\\web\\Cookie::httpOnly]] 被设置为 true，\n  这可减少客户端脚本访问受保护 cookie（如果浏览器支持）的风险，\n  更多详情可阅读 [httpOnly wiki article](https://owasp.org/www-community/HttpOnly)。\n\n\n### Cookie 验证 <span id=\"cookie-validation\"></span>\n\n在上两节中，当通过 `request` 和 `response` 组件读取和发送 cookie 时，\n你会喜欢扩展的 cookie 验证的保障安全功能，它能\n使 cookie 不被客户端修改。该功能通过给每个 cookie 签发一个哈希字符串来告知服务端 cookie 是否在客户端被修改，\n如果被修改，通过 `request` 组件的\n[[yii\\web\\Request::cookies|cookie collection]] cookie 集合访问不到该 cookie。\n\n> Note: Cookie 验证只保护 cookie 值被修改，如果一个 cookie 验证失败，\n  仍然可以通过 `$_COOKIE` 来访问该 cookie，\n  因为这是第三方库对未通过 cookie 验证自定义的操作方式。\n\nCookie 验证默认启用，可以设置 [[yii\\web\\Request::enableCookieValidation]] 属性为 false 来禁用它，\n尽管如此，我们强烈建议启用它。\n\n> Note: 直接通过 `$_COOKIE` 和 `setcookie()` 读取和发送的 Cookie 不会被验证。\n\n当使用 cookie 验证时，必须指定 [[yii\\web\\Request::cookieValidationKey]]，它是用来生成上述的哈希值，\n可通过在应用配置中配置 `request` 组件。\n\n```php\nreturn [\n    'components' => [\n        'request' => [\n            'cookieValidationKey' => 'fill in a secret key here',\n        ],\n    ],\n];\n```\n\n> Info: [[yii\\web\\Request::cookieValidationKey|cookieValidationKey]] 对你的应用安全很重要，\n  应只被你信任的人知晓，请不要将它放入版本控制中。\n"
  },
  {
    "path": "docs/guide-zh-CN/security-authentication.md",
    "content": "认证\n==============\n\n认证是鉴定用户身份的过程。它通常使用一个标识符\n（如用户名或电子邮件地址）和一个加密令牌（比如密码或者存取令牌）来\n鉴别用户身份。认证是登录功能的基础。\n\nYii提供了一个认证框架，它连接了不同的组件以支持登录。欲使用这个框架，\n你主要需要做以下工作：\n \n* 设置用户组件 [[yii\\web\\User|user]] ;\n* 创建一个类实现 [[yii\\web\\IdentityInterface]] 接口。\n\n\n## 配置 [[yii\\web\\User]] <span id=\"configuring-user\"></span>\n\n用户组件 [[yii\\web\\User|user]] 用来管理用户的认证状态。这需要你\n指定一个含有实际认证逻辑的认证类 [[yii\\web\\User::identityClass|identity class]]。\n在以下web应用的配置项中，将用户用户组件 [[yii\\web\\User|user]] 的\n认证类 [[yii\\web\\User::identityClass|identity class]] 配置成\n模型类 `app\\models\\User`， 它的实现将在下一节中讲述。 \n  \n```php\nreturn [\n    'components' => [\n        'user' => [\n            'identityClass' => 'app\\models\\User',\n        ],\n    ],\n];\n```\n\n\n## 认证接口 [[yii\\web\\IdentityInterface]] 的实现 <span id=\"implementing-identity\"></span>\n\n认证类 [[yii\\web\\User::identityClass|identity class]] 必须实现包含以下方法的\n认证接口 [[yii\\web\\IdentityInterface]]：\n\n* [[yii\\web\\IdentityInterface::findIdentity()|findIdentity()]]：根据指定的用户ID查找\n  认证模型类的实例，当你需要使用session来维持登录状态的时候会用到这个方法。\n* [[yii\\web\\IdentityInterface::findIdentityByAccessToken()|findIdentityByAccessToken()]]：根据指定的存取令牌查找\n  认证模型类的实例，该方法用于\n  通过单个加密令牌认证用户的时候（比如无状态的RESTful应用）。\n* [[yii\\web\\IdentityInterface::getId()|getId()]]：获取该认证实例表示的用户的ID。\n* [[yii\\web\\IdentityInterface::getAuthKey()|getAuthKey()]]：获取基于 cookie 登录时使用的认证密钥。\n  认证密钥储存在 cookie 里并且将来会与服务端的版本进行比较以确保\n  cookie的有效性。\n* [[yii\\web\\IdentityInterface::validateAuthKey()|validateAuthKey()]] ：是基于 cookie 登录密钥的\n验证的逻辑的实现。\n\n用不到的方法可以空着，例如，你的项目只是一个\n无状态的 RESTful 应用，只需实现 [[yii\\web\\IdentityInterface::findIdentityByAccessToken()|findIdentityByAccessToken()]]\n和 [[yii\\web\\IdentityInterface::getId()|getId()]] ，其他的方法的函数体留空即可。\n\n下面的例子是一个通过结合了 `user` 数据表的 \nAR 模型 [Active Record](db-active-record.md) 实现的一个认证类 [[yii\\web\\User::identityClass|identity class]]。\n\n```php\n<?php\n\nuse yii\\db\\ActiveRecord;\nuse yii\\web\\IdentityInterface;\n\nclass User extends ActiveRecord implements IdentityInterface\n{\n    public static function tableName()\n    {\n        return 'user';\n    }\n\n    /**\n     * 根据给到的ID查询身份。\n     *\n     * @param string|integer $id 被查询的ID\n     * @return IdentityInterface|null 通过ID匹配到的身份对象\n     */\n    public static function findIdentity($id)\n    {\n        return static::findOne($id);\n    }\n\n    /**\n     * 根据 token 查询身份。\n     *\n     * @param string $token 被查询的 token\n     * @return IdentityInterface|null 通过 token 得到的身份对象\n     */\n    public static function findIdentityByAccessToken($token, $type = null)\n    {\n        return static::findOne(['access_token' => $token]);\n    }\n\n    /**\n     * @return int|string 当前用户ID\n     */\n    public function getId()\n    {\n        return $this->id;\n    }\n\n    /**\n     * @return string 当前用户的（cookie）认证密钥\n     */\n    public function getAuthKey()\n    {\n        return $this->auth_key;\n    }\n\n    /**\n     * @param string $authKey\n     * @return boolean if auth key is valid for current user\n     */\n    public function validateAuthKey($authKey)\n    {\n        return $this->getAuthKey() === $authKey;\n    }\n}\n```\n\n如上所述，如果你的应用利用 cookie 登录，\n你只需要实现 `getAuthKey()` 和 `validateAuthKey()` 方法。这样的话，你可以使用下面的代码在 \n`user` 表中生成和存储每个用户的认证密钥。\n\n```php\nclass User extends ActiveRecord implements IdentityInterface\n{\n    ......\n    \n    public function beforeSave($insert)\n    {\n        if (parent::beforeSave($insert)) {\n            if ($this->isNewRecord) {\n                $this->auth_key = \\Yii::$app->security->generateRandomString();\n            }\n            return true;\n        }\n        return false;\n    }\n}\n```\n\n> Note: 不要混淆 `user` 认证类和用户组件 [[yii\\web\\User]]。前者是实现\n  认证逻辑的类，通常用关联了\n  持久性存储的用户信息的AR模型 [Active Record](db-active-record.md) 实现。后者是负责管理用户认证状态的\n  应用组件。\n\n\n## 使用用户组件 [[yii\\web\\User]] <span id=\"using-user\"></span>\n\n在 `user` 应用组件方面，你主要用到 [[yii\\web\\User]] 。\n\n你可以使用表达式 `Yii::$app->user->identity` 检测当前用户身份。它返回\n一个表示当前登录用户的认证类 [[yii\\web\\User::identityClass|identity class]] 的实例，\n未认证用户（游客）则返回 null。下面的代码展示了如何从 [[yii\\web\\User]] \n获取其他认证相关信息：\n\n```php\n// 当前用户的身份实例。未认证用户则为 Null 。\n$identity = Yii::$app->user->identity;\n\n// 当前用户的ID。 未认证用户则为 Null 。\n$id = Yii::$app->user->id;\n\n// 判断当前用户是否是游客（未认证的）\n$isGuest = Yii::$app->user->isGuest;\n```\n\n你可以使用下面的代码登录用户：\n\n```php\n// 使用指定用户名获取用户身份实例。\n// 请注意，如果需要的话您可能要检验密码\n$identity = User::findOne(['username' => $username]);\n\n// 登录用户\nYii::$app->user->login($identity);\n```\n\n[[yii\\web\\User::login()]] 方法将当前用户的身份登记到 [[yii\\web\\User]]。如果 session 设置为 \n[[yii\\web\\User::enableSession|enabled]]，则使用 session 记录用户身份，用户的\n认证状态将在整个会话中得以维持。如果开启自动登录 [[yii\\web\\User::enableAutoLogin|enabled]] \n则基于 cookie 登录（如：记住登录状态），它将使用 cookie 保存用户身份，这样\n只要 cookie 有效就可以恢复登录状态。\n\n为了使用 cookie 登录，你需要在应用配置文件中将 [[yii\\web\\User::enableAutoLogin]] \n设为 true。你还需要在 [[yii\\web\\User::login()]] 方法中\n传递有效期（记住登录状态的时长）参数。\n\n注销用户：\n\n```php\nYii::$app->user->logout();\n```\n\n请注意，启用 session 时注销用户才有意义。该方法将从内存和 session 中\n同时清理用户认证状态。默认情况下，它还会注销所有的\n用户会话数据。如果你希望保留这些会话数据，可以换成 `Yii::$app->user->logout(false)` 。\n\n\n## 认证事件 <span id=\"auth-events\"></span>\n\n[[yii\\web\\User]] 类在登录和注销流程引发一些事件。\n\n* [[yii\\web\\User::EVENT_BEFORE_LOGIN|EVENT_BEFORE_LOGIN]]：在登录 [[yii\\web\\User::login()]] 时引发。\n  如果事件句柄将事件对象的 [[yii\\web\\UserEvent::isValid|isValid]] 属性设为 false，\n  登录流程将会被取消。\n* [[yii\\web\\User::EVENT_AFTER_LOGIN|EVENT_AFTER_LOGIN]]：登录成功后引发。\n* [[yii\\web\\User::EVENT_BEFORE_LOGOUT|EVENT_BEFORE_LOGOUT]]：注销 [[yii\\web\\User::logout()]] 前引发。\n  如果事件句柄将事件对象的 [[yii\\web\\UserEvent::isValid|isValid]] 属性设为 false，\n  注销流程将会被取消。\n* [[yii\\web\\User::EVENT_AFTER_LOGOUT|EVENT_AFTER_LOGOUT]]：成功注销后引发。\n\n你可以通过响应这些事件来实现一些类似登录统计、在线人数统计的功能。例如,\n在登录后 [[yii\\web\\User::EVENT_AFTER_LOGIN|EVENT_AFTER_LOGIN]] 的处理程序，你可以将用户的登录时间和IP记录到\n`user` 表中。\n"
  },
  {
    "path": "docs/guide-zh-CN/security-authorization.md",
    "content": "授权\n=============\n\n授权是指验证用户是否允许做某件事的过程。Yii提供两种授权方法：\n存取控制过滤器（ACF）和基于角色的存取控制（RBAC）。\n\n\n## 存取控制过滤器 <span id=\"access-control-filter\"></span>\n\n存取控制过滤器（ACF）是一种通过 [[yii\\filters\\AccessControl]] 类来实现的简单授权方法，\n非常适用于仅需要简单的存取控制的应用。正如其名称所指，ACF 是一种动作过滤器\n[filter](structure-filters.md)，可在控制器或者模块中使用。当一个用户请求一个动作时，\nACF会检查 [[yii\\filters\\AccessControl::rules|access rules]] 列表，判断该用户是否允许执\n行所请求的动作。\n\n下述代码展示如何在 `site` 控制器中使用 ACF：\n\n```php\nuse yii\\web\\Controller;\nuse yii\\filters\\AccessControl;\n\nclass SiteController extends Controller\n{\n    public function behaviors()\n    {\n        return [\n            'access' => [\n                'class' => AccessControl::class,\n                'only' => ['login', 'logout', 'signup'],\n                'rules' => [\n                    [\n                        'allow' => true,\n                        'actions' => ['login', 'signup'],\n                        'roles' => ['?'],\n                    ],\n                    [\n                        'allow' => true,\n                        'actions' => ['logout'],\n                        'roles' => ['@'],\n                    ],\n                ],\n            ],\n        ];\n    }\n    // ...\n}\n```\n\n上面的代码中 ACF 以行为 (behavior) 的形式附加到 `site` 控制器。\n这就是很典型的使用行动过滤器的方法。 `only` 选项指明 ACF 应当只\n对 `login`， `logout` 和 `signup` 方法起作用。所有其它的 `site` \n控制器中的方法不受存取控制的限制。 `rules` 选项列出了 [[yii\\filters\\AccessRule|存取规则 (access rules)]]，解读如下：\n\n- 允许所有访客（还未经认证的用户）执行 `login` 和 `signup` 动作。 \n  `roles` 选项包含的问号 `?` 是一个特殊的标识，代表”访客用户”。\n- 允许已认证用户执行 `logout` 动作。`@`是另一个特殊标识，\n  代表”已认证用户”。\n\nACF 自顶向下逐一检查存取规则，直到找到一个与当前\n欲执行的动作相符的规则。 然后该匹配规则中的 `allow` \n选项的值用于判定该用户是否获得授权。如果没有找到匹配的规则，\n意味着该用户没有获得授权。（译者注： `only` 中没有列出的动作，将无条件获得授权）\n\n当 ACF 判定一个用户没有获得执行当前动作的授权时，它的默认处理是：\n\n* 如果该用户是访客，将调用 [[yii\\web\\User::loginRequired()]] 将用户的浏览器重定向到登录页面。\n* 如果该用户是已认证用户，将抛出一个 [[yii\\web\\ForbiddenHttpException]] 异常。\n\n你可以通过配置 [[yii\\filters\\AccessControl::denyCallback]] 属性定制该行为：\n\n```php\n[\n    'class' => AccessControl::class,\n    ...\n    'denyCallback' => function ($rule, $action) {\n        throw new \\Exception('You are not allowed to access this page');\n    }\n]\n```\n\n[[yii\\filters\\AccessRule|Access rules]] 支持很多的选项。下列是所支持选项的总览。\n你可以派生 [[yii\\filters\\AccessRule]] 来创建自定义的存取规则类。\n\n * [[yii\\filters\\AccessRule::allow|allow]]： 指定该规则是 \"允许\" 还是 \"拒绝\" 。（译者注：true是允许，false是拒绝）\n\n * [[yii\\filters\\AccessRule::actions|actions]]：指定该规则用于匹配哪些动作。\n   它的值应该是动作方法的ID数组。匹配比较是大小写敏感的。如果该选项为空，或者不使用该选项，\n   意味着当前规则适用于所有的动作。\n\n * [[yii\\filters\\AccessRule::controllers|controllers]]：指定该规则用于匹配哪些控制器。\n   它的值应为控制器ID数组。匹配比较是大小写敏感的。如果该选项为空，或者不使用该选项，\n   则意味着当前规则适用于所有的动作。（译者注：这个选项一般是在控制器的自定义父类中使用才有意义）\n\n * [[yii\\filters\\AccessRule::roles|roles]]：指定该规则用于匹配哪些用户角色。\n   系统自带两个特殊的角色，通过 [[yii\\web\\User::isGuest]] 来判断：\n\n     - `?`： 用于匹配访客用户 （未经认证）\n     - `@`： 用于匹配已认证用户\n\n   使用其他角色名时，将触发调用 [[yii\\web\\User::can()]]，这时要求 RBAC 的支持 （在下一节中阐述）。\n   如果该选项为空或者不使用该选项，意味着该规则适用于所有角色。\n\n * [[yii\\filters\\AccessRule::roleParams|roleParams]]：指定将传递给 [[yii\\web\\User::can()]] 的参数。\n   请参阅下面描述RBAC规则的部分，了解如何使用它。 如果此选项为空或未设置，则不传递任何参数。\n   \n * [[yii\\filters\\AccessRule::ips|ips]]：指定该规则用于匹配哪些 [[yii\\web\\Request::userIP|客户端IP地址]] 。\n   IP 地址可在其末尾包含通配符 `*` 以匹配一批前缀相同的IP地址。\n   例如，`192.168.*` 匹配所有 `192.168.` 段的IP地址。\n   如果该选项为空或者不使用该选项，意味着该规则适用于所有角色。\n\n * [[yii\\filters\\AccessRule::verbs|verbs]]：指定该规则用于匹配哪种请求方法（例如`GET`，`POST`）。\n   这里的匹配大小写不敏感。\n\n * [[yii\\filters\\AccessRule::matchCallback|matchCallback]]：指定一个PHP回调函数用于\n   判定该规则是否满足条件。（译者注：此处的回调函数是匿名函数）\n\n * [[yii\\filters\\AccessRule::denyCallback|denyCallback]]: 指定一个PHP回调函数，\n   当这个规则不满足条件时该函数会被调用。（译者注：此处的回调函数是匿名函数）\n\n以下例子展示了如何使用 `matchCallback` 选项，\n可使你设计任意的访问权限检查逻辑：\n\n```php\nuse yii\\filters\\AccessControl;\n\nclass SiteController extends Controller\n{\n    public function behaviors()\n    {\n        return [\n            'access' => [\n                'class' => AccessControl::class,\n                'only' => ['special-callback'],\n                'rules' => [\n                    [\n                        'actions' => ['special-callback'],\n                        'allow' => true,\n                        'matchCallback' => function ($rule, $action) {\n                            return date('d-m') === '31-10';\n                        }\n                    ],\n                ],\n            ],\n        ];\n    }\n\n    // 匹配的回调函数被调用了！这个页面只有每年的10月31号能访问（译者注：原文在这里说该方法是回调函数不确切，读者不要和 `matchCallback` 的值即匿名的回调函数混淆理解）。\n    public function actionSpecialCallback()\n    {\n        return $this->render('happy-halloween');\n    }\n}\n```\n\n\n## 基于角色的存取控制 （RBAC） <span id=\"rbac\"></span>\n\n基于角色的存取控制 （RBAC） 提供了一个简单而强大的集中式存取控制机制。\n详细的关于 RBAC 和诸多传统的存取控制方案对比的详情，请参阅\n[Wikipedia](https://zh.wikipedia.org/wiki/%E4%BB%A5%E8%A7%92%E8%89%B2%E7%82%BA%E5%9F%BA%E7%A4%8E%E7%9A%84%E5%AD%98%E5%8F%96%E6%8E%A7%E5%88%B6)。\n\nYii 实现了通用的分层的 RBAC，遵循的模型是 [NIST RBAC model](https://csrc.nist.gov/CSRC/media/Publications/conference-paper/1992/10/13/role-based-access-controls/documents/ferraiolo-kuhn-92.pdf).\n它通过 [[yii\\rbac\\ManagerInterface|authManager]] [application component](structure-application-components.md) 提供 RBAC 功能。\n\n使用 RBAC 涉及到两部分工作。第一部分是建立授权数据，\n而第二部分是使用这些授权数据在需要的地方执行检查。\n\n为方便后面的讲述，这里先介绍一些 RBAC 的基本概念。\n\n\n### 基本概念 <span id=\"basic-concepts\"></span>\n\n角色是 *权限* 的集合 （例如：建贴、改贴）。一个角色\n可以指派给一个或者多个用户。要检查某用户是否有一个特定的权限，\n系统会检查该包含该权限的角色是否指派给了该用户。\n\n可以用一个规则 *rule* 与一个角色或者权限关联。一个规则用一段代码代表，\n规则的执行是在检查一个用户是否满足这个角色或者权限时进行的。例如，\"改帖\" 的权限\n可以使用一个检查该用户是否是帖子的创建者的规则。权限检查中，如果该用户\n不是帖子创建者，那么他（她）将被认为不具有 \"改帖\"的权限。\n\n角色和权限都可以按层次组织。特定情况下，一个角色可能由其他角色或权限构成，\n而权限又由其他的权限构成。Yii 实现了所谓的 *局部顺序* 的层次结构，包含更多的特定的 *树* 的层次。\n一个角色可以包含一个权限，反之则不行。（译者注：可理解为角色在上方，权限在下方，从上到下如果碰到权限那么再往下不能出现角色）\n\n\n### 配置 RBAC <span id=\"configuring-rbac\"></span>\n\n在开始定义授权数据和执行存取检查之前，需要先配置应用组件 [[yii\\base\\Application::authManager|authManager]] 。 \nYii 提供了两套授权管理器： [[yii\\rbac\\PhpManager]] \n和 [[yii\\rbac\\DbManager]]。前者使用 PHP 脚本存放授权数据，\n而后者使用数据库存放授权数据。 如果你的应用不要求大量的动态角色和权限管理，\n你可以考虑使用前者。\n\n\n#### 使用 `PhpManager` <span id=\"using-php-manager\"></span>\n\n以下代码展示使用 [[yii\\rbac\\PhpManager]] 时如何在应用配置文件中配置 `authManager`：\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'authManager' => [\n            'class' => 'yii\\rbac\\PhpManager',\n        ],\n        // ...\n    ],\n];\n```\n\n现在可以通过 `\\Yii::$app->authManager` 访问 `authManager` 。\n\n[[yii\\rbac\\PhpManager]] 默认将 RBAC 数据保存在 `@app/rbac` 目录下的文件中。\n如果权限层次数据在运行时会被修改，需确保WEB服务器进程对该目录和其中的文件有写权限。\n\n\n#### 使用 `DbManager` <span id=\"using-db-manager\"></span>\n\n以下代码展示使用 [[yii\\rbac\\DbManager]] 时如何在应用配置文件中配置 `authManager`：\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'authManager' => [\n            'class' => 'yii\\rbac\\DbManager',\n            // uncomment if you want to cache RBAC items hierarchy\n            // 'cache' => 'cache',\n        ],\n        // ...\n    ],\n];\n```\n> Note: 如果您使用的是 yii2-basic-app 模板，则有一个 `config/console.php` 配置文件，其中\n  `authManager` 需要另外声明在 `config/web.php`。\n> 在 yii2-advanced-app 的情况下，`authManager` 只能在 `common/config/main.php` 中声明一次。\n\n`DbManager` 使用4个数据库表存放它的数据：\n\n- [[yii\\rbac\\DbManager::$itemTable|itemTable]]： 该表存放授权条目（译者注：即角色和权限）。默认表名为 \"auth_item\" 。\n- [[yii\\rbac\\DbManager::$itemChildTable|itemChildTable]]： 该表存放授权条目的层次关系。默认表名为 \"auth_item_child\"。\n- [[yii\\rbac\\DbManager::$assignmentTable|assignmentTable]]： 该表存放授权条目对用户的指派情况。默认表名为 \"auth_assignment\"。\n- [[yii\\rbac\\DbManager::$ruleTable|ruleTable]]： 该表存放规则。默认表名为 \"auth_rule\"。\n\n继续之前，你需要在数据库中创建这些表。你可以使用存放在 `@yii/rbac/migrations` 目录中的数据库迁移文件来做这件事（译者注：根据本人经验，最好是将授权数据初始化命令也写到这个 RBAC 数据库迁移文件中）：\n\n`yii migrate --migrationPath=@yii/rbac/migrations`\n\n阅读 [Separated Migrations](db-migrations.md#separated-migrations)\n部分中有关处理来自不同名称空间的迁移的更多信息。\n\n现在可以通过 `\\Yii::$app->authManager` 访问 `authManager` 。\n\n\n### 建立授权数据 <span id=\"generating-rbac-data\"></span>\n\n所有授权数据相关的任务如下：\n\n- 定义角色和权限；\n- 建立角色和权限的关系；\n- 定义规则；\n- 将规则与角色和权限作关联；\n- 指派角色给用户。\n\n根据授权灵活性要求，上述任务可以通过不同的方式完成。\n如果您的权限层次结构仅由开发人员更改，则可以使用迁移或控制台命令。\nMigration pro 是可以与其他迁移一起执行的。\n控制台命令 pro 是您可以很好地了解代码中的层次结构，\n而不是分散在多个迁移中。\n\n无论哪种方式，最终都会得到以下 RBAC 层次结构：\n\n![Simple RBAC hierarchy](images/rbac-hierarchy-1.png \"简单的 RBAC 层次结构示意图\")\n\n如果您需要动态形成权限层次结构，则需要 UI 或控制台命令。 用于构建层次结构的\nAPI 本身不会有所不同。\n\n#### 使用迁移（Using migrations）\n\n您可以使用 [migrations](db-migrations.md)\n通过 `authManager` 提供的 API 初始化和修改层次结构。\n\n使用 `./yii migrate/create init_rbac` 创建新迁移，然后实现创建层次结构：\n\n```php\n<?php\nuse yii\\db\\Migration;\n\nclass m170124_084304_init_rbac extends Migration\n{\n    public function up()\n    {\n        $auth = Yii::$app->authManager;\n\n        // 添加 \"createPost\" 权限\n        $createPost = $auth->createPermission('createPost');\n        $createPost->description = 'Create a post';\n        $auth->add($createPost);\n\n        // 添加 \"updatePost\" 权限\n        $updatePost = $auth->createPermission('updatePost');\n        $updatePost->description = 'Update post';\n        $auth->add($updatePost);\n\n        // 添加 \"author\" 角色并赋予 \"createPost\" 权限\n        $author = $auth->createRole('author');\n        $auth->add($author);\n        $auth->addChild($author, $createPost);\n\n        // 添加 \"admin\" 角色并赋予 \"updatePost\" \n\t\t// 和 \"author\" 权限\n        $admin = $auth->createRole('admin');\n        $auth->add($admin);\n        $auth->addChild($admin, $updatePost);\n        $auth->addChild($admin, $author);\n\n        // 为用户指派角色。其中 1 和 2 是由 IdentityInterface::getId() 返回的id\n        // 通常在你的 User 模型中实现这个函数。\n        $auth->assign($author, 2);\n        $auth->assign($admin, 1);\n    }\n    \n    public function down()\n    {\n        $auth = Yii::$app->authManager;\n\n        $auth->removeAll();\n    }\n}\n```\n\n> 如果您不想硬编码哪些用户具有某些角色，请不要在迁移中使用 `->assign()` 调用。\n  相反，请创建UI或控制台命令来管理分配。\n\n迁移可以通过 `yii migrate` 使用。\n\n### 使用控制台命令（Using console command）\n\n如果您的权限层次根本没有改变，并且您拥有固定数量的用户，\n则可以创建一个[控制台命令](tutorial-console.md#create-command)，它将通过\n`authManager` 提供的 API 初始化授权数据一次：\n\n```php\n<?php\nnamespace app\\commands;\n\nuse Yii;\nuse yii\\console\\Controller;\n\nclass RbacController extends Controller\n{\n    public function actionInit()\n    {\n        $auth = Yii::$app->authManager;\n        $auth->removeAll();\n\n        // 添加 \"createPost\" 权限\n        $createPost = $auth->createPermission('createPost');\n        $createPost->description = 'Create a post';\n        $auth->add($createPost);\n\n        // 添加 \"updatePost\" 权限\n        $updatePost = $auth->createPermission('updatePost');\n        $updatePost->description = 'Update post';\n        $auth->add($updatePost);\n\n        // 添加 \"author\" 角色并赋予 \"createPost\" 权限\n        $author = $auth->createRole('author');\n        $auth->add($author);\n        $auth->addChild($author, $createPost);\n\n        // 添加 \"admin\" 角色并赋予 \"updatePost\" \n\t\t// 和 \"author\" 权限\n        $admin = $auth->createRole('admin');\n        $auth->add($admin);\n        $auth->addChild($admin, $updatePost);\n        $auth->addChild($admin, $author);\n\n        // 为用户指派角色。其中 1 和 2 是由 IdentityInterface::getId() 返回的id\n        // 通常在你的 User 模型中实现这个函数。\n        $auth->assign($author, 2);\n        $auth->assign($admin, 1);\n    }\n}\n```\n\n> Note: 如果您使用高级模板，则需要将 `RbacController` 放在 `console/controllers` 目录中，\n  并将命名空间更改为 `console/controllers`。\n\n上面的命令可以通过以下方式从控制台执行：\n\n```\nyii rbac/init\n```\n\n> 如果您不想硬编码用户具有某些角色，请不要将 `->assign()` 调用放入命令中。\n  相反，请创建UI或控制台命令来管理分配。\n\n## 为用户分配角色（Assigning roles to users）\n\n作者可以创建帖子，管理员可以更新帖子并可以做一切作者都能做的。\n\n如果您的应用程序允许用户注册，则需要为这些新用户分配一次角色。\n例如，为了让所有注册用户成为高级项目模板中的作者，您需要修改 `frontend\\models\\SignupForm::signup()`，\n如下所示：\n\n```php\npublic function signup()\n{\n    if ($this->validate()) {\n        $user = new User();\n        $user->username = $this->username;\n        $user->email = $this->email;\n        $user->setPassword($this->password);\n        $user->generateAuthKey();\n        $user->save(false);\n\n        // 增加了以下三行：\n        $auth = \\Yii::$app->authManager;\n        $authorRole = $auth->getRole('author');\n        $auth->assign($authorRole, $user->getId());\n\n        return $user;\n    }\n\n    return null;\n}\n```\n\n对于需要动态更新授权数据的复杂访问控制的应用程序，可能需要使用\n`authManager` 提供的 API 来开发特殊的用户界面（即管理面板）。\n\n\n### 使用规则 (Rules) <span id=\"using-rules\"></span>\n\n如前所述，规则给角色和权限增加额外的约束条件。规则是 [[yii\\rbac\\Rule]] 的派生类。\n它需要实现 [[yii\\rbac\\Rule::execute()|execute()]] 方法。在之前我们创建的层次结构中，作者不能编辑自己的帖子，我们来修正这个问题。\n首先我们需要一个规则来认证当前用户是帖子的作者：\n\n```php\nnamespace app\\rbac;\n\nuse yii\\rbac\\Rule;\nuse app\\models\\Post;\n\n/**\n * 检查 authorID 是否和通过参数传进来的 user 参数相符\n */\nclass AuthorRule extends Rule\n{\n    public $name = 'isAuthor';\n\n    /**\n     * @param string|integer $user 用户 ID.\n     * @param Item $item 该规则相关的角色或者权限\n     * @param array $params 传给 ManagerInterface::checkAccess() 的参数\n     * @return boolean 代表该规则相关的角色或者权限是否被允许\n     */\n    public function execute($user, $item, $params)\n    {\n        return isset($params['post']) ? $params['post']->createdBy == $user : false;\n    }\n}\n```\n\n上述规则检查 `post` 是否是 `$user` 创建的。我们还要在之前的命令中\n创建一个特别的权限 `updateOwnPost` ：\n\n```php\n$auth = Yii::$app->authManager;\n\n// 添加规则\n$rule = new \\app\\rbac\\AuthorRule;\n$auth->add($rule);\n\n// 添加 \"updateOwnPost\" 权限并与规则关联\n$updateOwnPost = $auth->createPermission('updateOwnPost');\n$updateOwnPost->description = 'Update own post';\n$updateOwnPost->ruleName = $rule->name;\n$auth->add($updateOwnPost);\n\n// \"updateOwnPost\" 权限将由 \"updatePost\" 权限使用\n$auth->addChild($updateOwnPost, $updatePost);\n\n// 允许 \"author\" 更新自己的帖子\n$auth->addChild($author, $updateOwnPost);\n```\n\n现在我们得到如下层次结构:\n\n![RBAC hierarchy with a rule](images/rbac-hierarchy-2.png \"有一个规则的 RBAC 层次结构\")\n\n\n### 存取检查 <span id=\"access-check\"></span>\n\n授权数据准备好后，存取检查简单到只需要一个方法调用 [[yii\\rbac\\ManagerInterface::checkAccess()]]。\n因为大多数存取检查都是针对当前用户而言，为方便起见， Yii 提供了一个快捷方法\n[[yii\\web\\User::can()]]，可以如下例所示来使用：\n\n```php\nif (\\Yii::$app->user->can('createPost')) {\n    // 建贴\n}\n```\n\n如果当前用户是 `ID=1` 的 Jane ，我们从图中的 `createPost` 开始，并试图到达 `Jane` 。 （译者注：参照图中红色路线所示的建贴授权流程）\n\n![Access check](images/rbac-access-check-1.png \"Access check\")\n\n为了检查某用户是否能更新帖子，我们需要传递一个额外的参数，该参数是 `AuthorRule` 要用的：\n\n```php\nif (\\Yii::$app->user->can('updatePost', ['post' => $post])) {\n    // 更新帖子\n}\n```\n\n下图所示为当前用户是 John 时所发生的事情：\n\n\n![Access check](images/rbac-access-check-2.png \"存取权限检查\")\n\n我们从图中的 `updatePost` 开始，经过 `updateOwnPost`。为通过检查，`Authorrule` \n规则的 `execute()` 方法应当返回 `true` 。该方法从 `can()` 方法调用接收到 `$params` 参数，\n因此它的值是 `['post' => $post]` 。如果一切顺利，我们会达到指派给 John 的 `author` 角色。\n\n对于 Jane 来说则更简单，因为她是管理员：\n\n![Access check](images/rbac-access-check-3.png \"Access check\")\n\n在您的控制器内部有几种实现授权的方式。\n如果您希望细化权限来分开添加和删除的访问权限，那么您需要检查每个操作的访问权限。\n您可以在每个操作方法中使用上述条件，或使用 [[yii\\filters\\AccessControl]]：\n\n```php\npublic function behaviors()\n{\n    return [\n        'access' => [\n            'class' => AccessControl::class,\n            'rules' => [\n                [\n                    'allow' => true,\n                    'actions' => ['index'],\n                    'roles' => ['managePost'],\n                ],\n                [\n                    'allow' => true,\n                    'actions' => ['view'],\n                    'roles' => ['viewPost'],\n                ],\n                [\n                    'allow' => true,\n                    'actions' => ['create'],\n                    'roles' => ['createPost'],\n                ],\n                [\n                    'allow' => true,\n                    'actions' => ['update'],\n                    'roles' => ['updatePost'],\n                ],\n                [\n                    'allow' => true,\n                    'actions' => ['delete'],\n                    'roles' => ['deletePost'],\n                ],\n            ],\n        ],\n    ];\n}\n```\n\n如果所有的CRUD操作都是一起管理的，那么使用 `managePost` 这样的单一权限并且在\n[[yii\\web\\Controller::beforeAction()]] 中检查它是个好主意。\n\n在上面的例子中，没有参数与指定的访问动作的角色一起传递，但是在\n`updatePost` 权限的情况下，我们需要传递 `post` 参数才能正常工作。\n您可以通过在访问规则中指定 [[yii\\filters\\AccessRule::roleParams|roleParams]] 将参数传递给\n[[yii\\web\\User::can()]]：\n\n```php\n[\n    'allow' => true,\n    'actions' => ['update'],\n    'roles' => ['updatePost'],\n    'roleParams' => function() {\n        return ['post' => Post::findOne(['id' => Yii::$app->request->get('id')])];\n    },\n],\n```\n\n在上面的例子中，[[yii\\filters\\AccessRule::roleParams|roleParams]] 是一个 Closure，\n将在检查访问规则时进行评估，因此模型只会在需要时加载。\n如果创建角色参数是一个简单的操作，那么您可以指定一个数组，如下所示：\n\n```php\n[\n    'allow' => true,\n    'actions' => ['update'],\n    'roles' => ['updatePost'],\n    'roleParams' => ['postId' => Yii::$app->request->get('id')];\n],\n```\n\n### 使用默认角色 <span id=\"using-default-roles\"></span>\n\n所谓默认角色就是 *隐式* 地指派给 *所有* 用户的角色。不需要调用 \n[[yii\\rbac\\ManagerInterface::assign()]] 方法做显示指派，并且授权数据中不包含指派信息。\n\n默认角色通常与一个规则关联，用以检查该角色是否符合被检查的用户。\n\n默认角色常常用于已经确立了一些角色的指派关系的应用（译者注：指派关系指的是应用业务逻辑层面，\n并非指授权数据的结构）。比如，一个应用的 user 表中有一个 `group` 字段，代表用户属于哪个特权组。\n如果每个特权组可以映射到 RBAC 的角色，你就可以采用默认角色自动地为每个用户指派一个 RBAC 角色。\n让我们用一个例子展示如何做到这一点。\n\n假设在 user 表中，你有一个 `group` 字段，用 1 代表管理员组，用 2 表示作者组。\n你规划两个 RBAC 角色 `admin` 和 `author` 分别对应这两个组的权限。\n你可以这样设置 RBAC 数据，\n\n\n```php\nnamespace app\\rbac;\n\nuse Yii;\nuse yii\\rbac\\Rule;\n\n/**\n * 检查是否匹配用户的组\n */\nclass UserGroupRule extends Rule\n{\n    public $name = 'userGroup';\n\n    public function execute($user, $item, $params)\n    {\n        if (!Yii::$app->user->isGuest) {\n            $group = Yii::$app->user->identity->group;\n            if ($item->name === 'admin') {\n                return $group == 1;\n            } elseif ($item->name === 'author') {\n                return $group == 1 || $group == 2;\n            }\n        }\n        return false;\n    }\n}\n```\n\n然后按 [in the previous section](#generating-rbac-data) 中的说明创建自己的 command/migration：\n\n```php\n$auth = Yii::$app->authManager;\n\n$rule = new \\app\\rbac\\UserGroupRule;\n$auth->add($rule);\n\n$author = $auth->createRole('author');\n$author->ruleName = $rule->name;\n$auth->add($author);\n// ... 添加$author角色的子项部分代码 ... （译者注：省略部分参照之前的控制台命令）\n\n$admin = $auth->createRole('admin');\n$admin->ruleName = $rule->name;\n$auth->add($admin);\n$auth->addChild($admin, $author);\n// ... 添加$admin角色的子项部分代码 ... （译者注：省略部分参照之前的控制台命令）\n```\n\n注意，在上述代码中，因为 \"author\" 作为 \"admin\" 的子角色，当你实现这个规则的 `execute()` 方法时， \n你也需要遵从这个层次结构。这就是为何当角色名为 \"author\" 的情况下（译者注：`$item->name`就是角色名）， \n`execute()` 方法在组为 1 或者 2 时均要返回 true \n（意思是用户属于 \"admin\" 或者 \"author\" 组 ）。\n\n接下来，在配置 `authManager` 时指定 [[yii\\rbac\\BaseManager::$defaultRoles]] 选项（译者注：在应用配置文件中的组件部分配置）：\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'authManager' => [\n            'class' => 'yii\\rbac\\PhpManager',\n            'defaultRoles' => ['admin', 'author'],\n        ],\n        // ...\n    ],\n];\n```\n\n现在如果你执行一个存取权限检查， 判定该规则时， `admin` 和 `author` \n两个角色都将会检查。如果规则返回 true ，意思是角色符合当前用户。基于上述规则\n的实现，意味着如果某用户的 `group` 值为 1 ， `admin` 角色将赋予该用户，\n如果 `group` 值是 2 则将赋予 `author` 角色。\n"
  },
  {
    "path": "docs/guide-zh-CN/security-best-practices.md",
    "content": "最佳安全实践\n=======================\n\n下面，我们将会回顾常见的安全原则，并介绍在使用 Yii 开发应用程序时，如何避免潜在安全威胁。\n大多数这些原则并非您独有，而是适用于网站或软件开发，\n因此，您还可以找到有关这些背后的一般概念的进一步阅读的链接。\n\n\n基本准则\n----------------\n\n无论是开发何种应用程序，我们都有两条基本的安全准则：\n\n1. 过滤输入\n2. 转义输出\n\n\n### 过滤输入\n\n过滤输入的意思是，用户输入不应该认为是安全的，你需要总是验证你获得的输入值是在允许范围内。\n比如，我们假设可以通过三个字段完成排序 `title`，`created_at` 和 `status`，然后，这个值是由用户输入提供的，\n那么，最好在我们接收参数的时候，检查一下这个值是否是指定的范围。\n对于基本的 PHP 而言，上述做法类似如下：\n\n```php\n$sortBy = $_GET['sort'];\nif (!in_array($sortBy, ['title', 'created_at', 'status'])) {\n\tthrow new Exception('Invalid sort value.');\n}\n```\n\n在 Yii 中，很大可能性，你会使用 [表单校验器](input-validation.md) 来执行类似的检查。\n\n进一步阅读该主题：\n\n- <https://owasp.org/www-community/vulnerabilities/Improper_Data_Validation>\n- <https://www.owasp.org/index.php/Input_Validation_Cheat_Sheet>\n\n\n### 转义输出\n\n转义输出的意思是，根据我们使用数据的上下文环境，数据需要被转义。比如：在 HTML 上下文，\n你需要转义 `<`，`>` 之类的特殊字符。在 JavaScript 或者 SQL 中，也有其他的特殊含义的字符串需要被转义。\n由于手动的给所用的输出转义容易出错，\nYii 提供了大量的工具来在不同的上下文执行转义。\n\n进一步阅读该话题：\n\n- <https://owasp.org/www-community/attacks/Command_Injection>\n- <https://owasp.org/www-community/attacks/Code_Injection>\n- <https://owasp.org/www-community/attacks/xss/>\n\n\n避免 SQL 注入\n-----------------------\n\nSQL 注入发生在查询语句是由连接未转义的字符串生成的场景，比如：\n\n```php\n$username = $_GET['username'];\n$sql = \"SELECT * FROM user WHERE username = '$username'\";\n```\n\n除了提供正确的用户名外，攻击者可以给你的应用程序输入类似 `'; DROP TABLE user; --` 的语句。\n这将会导致生成如下的 SQL：\n\n```sql\nSELECT * FROM user WHERE username = ''; DROP TABLE user; --'\n```\n\n这是一个合法的查询语句，并将会执行以空的用户名搜索用户操作，然后，删除 `user` 表。\n这极有可能导致网站出错，数据丢失。（你是否进行了规律的数据备份？）\n\n在 Yii 中，大部分的数据查询是通过 [Active Record](db-active-record.md) 进行的，\n而其是完全使用 PDO 预处理语句执行 SQL 查询的。在预处理语句中，上述示例中，构造 SQL 查询的场景是不可能发生的。\n\n有时，你仍需要使用 [raw queries](db-dao.md) 或者 [query builder](db-query-builder.md)。\n在这种情况下，你应该使用安全的方式传递参数。如果数据是提供给表列的值，最好使用预处理语句：\n\n```php\n// query builder\n$userIDs = (new Query())\n    ->select('id')\n    ->from('user')\n    ->where('status=:status', [':status' => $status])\n    ->all();\n\n// DAO\n$userIDs = $connection\n    ->createCommand('SELECT id FROM user where status=:status')\n    ->bindValues([':status' => $status])\n    ->queryColumn();\n```\n\n如果数据是用于指定列的名字，或者表的名字，最好的方式是只允许预定义的枚举值。\n\n```php\nfunction actionList($orderBy = null)\n{\n    if (!in_array($orderBy, ['name', 'status'])) {\n        throw new BadRequestHttpException('Only name and status are allowed to order by.')\n    }\n\n    // ...\n}\n```\n\n如果上述方法不行，表名或者列名应该被转义。Yii 针对这种转义提供了一个特殊的语法，\n这样可以在所有支持的数据库都使用一套方案。\n\n```php\n$sql = \"SELECT COUNT([[$column]]) FROM {{table}}\";\n$rowCount = $connection->createCommand($sql)->queryScalar();\n```\n\n你可以在 [Quoting Table and Column Names](db-dao.md#quoting-table-and-column-names) 中获取更多的语法细节。\n\n进一步阅读该话题：\n\n- <https://owasp.org/www-community/attacks/SQL_Injection>\n\n\n防止 XSS 攻击\n------------\n\nXSS 或者跨站脚本发生在输出 HTML 到浏览器时，输出内容没有正确的转义。\n例如，如果用户可以输入其名称，那么他输入 `<script>alert('Hello!');</script>` 而非其名字 `Alexander`，\n所有输出没有转义直接输出用户名的页面都会执行 JavaScript 代码 `alert('Hello!');`，\n这会导致浏览器页面上出现一个警告弹出框。就具体的站点而言，除了这种无意义的警告输出外，\n这样的脚本可以以你的名义发送一些消息到后台，甚至执行一些银行交易行为。\n\n避免 XSS 攻击在 Yii 中非常简单，有如下两种一般情况：\n\n1. 你希望数据以纯文本输出。\n2. 你希望数据以 HTML 形式输出。\n\n如果你需要的是纯文本，你可以如下简单的转义：\n\n\n```php\n<?= \\yii\\helpers\\Html::encode($username) ?>\n```\n\n如果是 HTML，我们可以用 HtmlPurifier 帮助类来执行：\n\n```php\n<?= \\yii\\helpers\\HtmlPurifier::process($description) ?>\n```\n\n注意 HtmlPurifier 帮助类的处理过程较为费时，建议增加缓存。\n\n进一步阅读该话题：\n\n- <https://owasp.org/www-community/attacks/xss/>\n\n\n防止 CSRF 攻击\n-------------\n\nCSRF 是跨站请求伪造的缩写。这个攻击思想源自许多应用程序假设来自用户的浏览器请求是由用户自己产生的，\n而事实并非如此。\n\n例如，网站 `an.example.com` 有一个 `/logout` 网址，当使用简单的 GET 请求访问时, 记录用户退出。\n只要用户的请求一切正常，但是有一天坏人们故意在用户经常访问的论坛上放上 `<img src=\"https://an.example.com/logout\">`。\n浏览器在请求图像或请求页面之间没有任何区别，\n所以当用户打开一个带有这样一个被操作过的 `<img>` 标签的页面时，\n浏览器将 GET 请求发送到该 URL，用户将从 `an.example.com` 注销。\n\n这是 CSRF 攻击如何运作的基本思路。可以说用户退出并不是一件严重的事情，\n然而这仅仅是一个例子，使用这种方法可以做更多的事情，例如触发付款或者是改变数据。\n想象一下如果某个网站有一个这样的 `https://an.example.com/purse/transfer?to=anotherUser&amount=2000` 网址。 \n使用 GET 请求访问它会导致从授权用户账户转账 $2000 给 `anotherUser`。\n我们知道，浏览器将始终发送 GET 请求来加载图像，\n所以我们可以修改代码以仅接受该 URL 上的 POST 请求。\n不幸的是，这并不会拯救我们，因为攻击者可以放置一些 JavaScript 代码而不是 `<img>` 标签，这样就可以向该 URL 发送 POST 请求。\n\n出于这个原因，Yii 应用其他机制来防止 CSRF 攻击。\n\n为了避免 CSRF 攻击，你总是需要：\n\n1. 遵循 HTTP 准则，比如 GET 不应该改变应用的状态。\n   有关详细信息，请参阅 [RFC2616](https://www.rfc-editor.org/rfc/rfc9110.html#name-method-definitions)。\n2. 保证 Yii CSRF 保护开启。\n\n有的时候你需要对每个控制器和/或方法使用禁用 CSRF。可以通过设置其属性来实现：\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public $enableCsrfValidation = false;\n\n    public function actionIndex()\n    {\n        // CSRF validation will not be applied to this and other actions\n    }\n\n}\n```\n\n要对每个自定义方法禁用 CSRF 验证，您可以使用：\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function beforeAction($action)\n    {\n        // ...set `$this->enableCsrfValidation` here based on some conditions...\n        // call parent method that will check CSRF if such property is true.\n        return parent::beforeAction($action);\n    }\n}\n```\n\n在 [standalone actions](structure-controllers.md#standalone-actions) 禁用 CSRF 必须在 `init()` 方法中设置。\n不要把这段代码放在 `beforeRun()` 方法中，因为它不会起任何作用。\n\n```php\n<?php\n\nnamespace app\\components;\n\nuse yii\\base\\Action;\n\nclass ContactAction extends Action\n{\n    public function init()\n    {\n        parent::init();\n        $this->controller->enableCsrfValidation = false;\n    }\n\n    public function run()\n    {\n          $model = new ContactForm();\n          $request = Yii::$app->request;\n          if ($request->referrer === 'yiipowered.com'\n              && $model->load($request->post())\n              && $model->validate()\n          ) {\n              $model->sendEmail();\n          }\n    }\n}\n```\n\n> Warning: 禁用 CSRF 将允许任何站点向您的站点发送 POST 请求。在这种情况下，实施额外验证非常重要，例如检查 IP 地址或秘密令牌。\n\n进一步阅读该话题：\n\n- <https://owasp.org/www-community/attacks/csrf>\n\n\n防止文件暴露\n----------------------\n\n默认的服务器 webroot 目录指向包含有 `index.php` 的 `web` 目录。在共享托管环境下，这样是不可能的，\n这样导致了所有的代码，配置，日志都在webroot目录。\n\n如果是这样，别忘了拒绝除了 `web` 目录以外的目录的访问权限。\n如果没法这样做，考虑将你的应用程序托管在其他地方。\n\n\n在生产环境关闭调试信息和工具\n-------------------------------------------\n\n在调试模式下， Yii 展示了大量的错误信息，这样是对开发有用的。\n同样，这些调试信息对于攻击者而言也是方便其用于破解数据结构，配置值，以及你的部分代码。\n永远不要在生产模式下将你的 `index.php` 中的 `YII_DEBUG` 设置为 `true`。\n\n你同样也不应该在生产模式下开启 Gii。它可以被用于获取数据结构信息，\n代码，以及简单的用 Gii 生成的代码覆盖你的代码。\n\n调试工具栏同样也应该避免在生产环境出现，除非非常有必要。它将会暴露所有的应用和配置的详情信息。\n如果你确定需要，反复确认其访问权限限定在你自己的 IP。\n\n进一步阅读该话题：\n\n- <https://owasp.org/www-project-.net/articles/Exception_Handling.md>\n- <https://owasp.org/www-pdf-archive/OWASP_Top_10_2007.pdf> (A6 - Information Leakage and Improper Error Handling)\n\n\n使用 TLS 上的安全连接\n--------------------------------\n\nYii 提供依赖 cookie 和/或 PHP 会话的功能。如果您的连接受到威胁，这些可能会很容易受到攻击。\n如果应用程序通过 TLS 使用安全连接，则风险会降低。\n\n有关如何配置它的说明，请参阅您的 Web 服务器文档。\n您还可以参考 H5BP 项目提供的示例配置：\n\n- [Nginx](https://github.com/h5bp/server-configs-nginx)。\n- [Apache](https://github.com/h5bp/server-configs-apache)。\n- [IIS](https://github.com/h5bp/server-configs-iis)。\n- [Lighttpd](https://github.com/h5bp/server-configs-lighttpd)。\n\n\n安全服务器配置\n---------------------------\n\n本节的目的是强调在为基于 Yii 的网站提供服务配置时需要考虑的风险。 \n除了这里涉及的要点之外，\n可能还有其他与安全相关的配置选项，\n所以不要认为这部分是完整的。\n\n### 避免 `Host`-header 攻击\n\n像 [[yii\\web\\UrlManager]] 和 [[yii\\helpers\\Url]] 这样的类会使用 \n[[yii\\web\\Request::getHostInfo()|currently requested host name]] 来生成链接。\n如果 Web 服务器配置为独立于 `Host` 标头的值提供相同的站点，这个信息并不可靠，\n并且 [可能由发送HTTP请求的用户伪造](https://www.acunetix.com/vulnerabilities/web/host-header-attack)。\n在这种情况下，您应该修复您的 Web 服务器配置以便仅为指定的主机名提供站点服务\n或者通过设置 `request` 应用程序组件的 [[yii\\web\\Request::setHostInfo()|hostInfo]] 属性来显式设置或过滤该值。\n\n有关于服务器配置的更多信息，请参阅您的 web 服务器的文档：\n\n- Apache 2：<https://httpd.apache.org/docs/trunk/vhosts/examples.html#defaultallports>\n- Nginx：<https://www.nginx.com/resources/wiki/start/topics/examples/server_blocks/>\n\n如果您无权访问服务器配置，您可以在应用程序级别设置 [[yii\\filters\\HostControl]] 过滤器，\n以防此类的攻击。\n\n```php\n// Web Application configuration file\nreturn [\n    'as hostControl' => [\n        'class' => 'yii\\filters\\HostControl',\n        'allowedHosts' => [\n            'example.com',\n            '*.example.com',\n        ],\n        'fallbackHostInfo' => 'https://example.com',\n    ],\n    // ...\n];\n```\n\n> Note: 您应该始更倾向于使用 web 服务器配置 'host header attack' 保护而不是使用过滤器。\n  仅当服务器配置设置不可用时 [[yii\\filters\\HostControl]] 才应该被使用。\n"
  },
  {
    "path": "docs/guide-zh-CN/security-cryptography.md",
    "content": "加密（Cryptography）\n==================\n\n在本节中，我们将回顾以下安全问题：\n\n- 生成随机数据\n- 加密和解密\n- 确认数据完整性\n\n生成伪随机数据（Generating Pseudorandom Data）\n----------------------------\n\n伪随机数据在很多情况下都很有用。 例如，当通过电子邮件重置密码时，\n您需要生成一个令牌，将其保存到数据库中，并通过电子邮件发送给最终用户，\n这反过来又会允许他们证明该帐户的所有权。\n这个令牌是独一无二且难以猜测的，否则攻击者可能会预测令牌的值并重置用户的密码。\n\nYii 安全助手类简单生成伪随机数据：\n\n\n```php\n$key = Yii::$app->getSecurity()->generateRandomString();\n```\n\n加密和解密（Encryption and Decryption）\n-------------------------\n\nYii 提供了便利的帮助功能，使您可以使用密钥 加密/解密 数据。 数据通过加密功能传递，以便只有拥有密钥的人才能解密。\n例如，我们需要在数据库中存储一些信息，但我们需要确保只有拥有密钥的用户才能查看它（即使应用程序数据库已被泄露）：\n\n\n```php\n// $data 和 $secretKey 从表单中获得\n$encryptedData = Yii::$app->getSecurity()->encryptByPassword($data, $secretKey);\n// 将 $encryptedData 存储到数据库\n```\n\n随后当用户想要读取数据时：\n\n```php\n// $secretKey 从用户输入获得，$encryptedData 来自数据库\n$data = Yii::$app->getSecurity()->decryptByPassword($encryptedData, $secretKey);\n```\n\n也可以通过 [[\\yii\\base\\Security::encryptByKey()]] 和\n[[\\yii\\base\\Security::decryptByKey()]] 使用密钥而不是密码。\n\n确认数据完整性（Confirming Data Integrity）\n-------------------------\n\n在某些情况下，您需要验证您的数据未被第三方篡改甚至以某种方式损坏。 Yii 提供了一种简单的方法用两个帮助功能的类确认数据完整性的。\n\n用密钥和数据生成的哈希前缀数据\n\n\n```php\n// $secretKey 是我们的应用程序或用户密钥，$genuineData 是从可靠来源获得的\n$data = Yii::$app->getSecurity()->hashData($genuineData, $secretKey);\n```\n\n检查数据完整性是否受到损害\n\n```php\n// $secretKey 我们的应用程序或用户密钥，$data 从不可靠的来源获得\n$data = Yii::$app->getSecurity()->validateData($data, $secretKey);\n```\n"
  },
  {
    "path": "docs/guide-zh-CN/security-overview.md",
    "content": "安全（Security）\n==============\n\n良好的安全性对于任何应用的健康和成功都至关重要。\n不幸的是，许多开发人员在安全方面偷工减料，要么是由于缺乏理解，要么是由于实现过于困难。 \n为了尽可能保证您的 Yii 应用程序的安全，Yii 包含了一些优秀且易于使用的安全功能。\n\n* [认证](security-authentication.md)\n* [授权](security-authorization.md)\n* [使用密码](security-passwords.md)\n* [加密](security-cryptography.md)\n* [视图安全](structure-views.md#security)\n* [身份验证客户端](https://github.com/yiisoft/yii2-authclient/blob/master/docs/guide/README.md)\n* [最佳实践](security-best-practices.md)\n* [受信任的代理和头文件](runtime-requests.md#trusted-proxies)\n"
  },
  {
    "path": "docs/guide-zh-CN/security-passwords.md",
    "content": "处理密码\n========\n\n大部分开发者知道密码不能以明文形式存储，但是许多开发者仍认为使用 `md5` 或者 `sha1` 来哈希化密码是安全的。\n一度，使用上述的哈希算法是足够安全的，但是，\n现代硬件的发展使得短时间内暴力破解上述算法生成的哈希串成为可能。\n\n为了即使在最糟糕的情况下（你的应用程序被破解了）也能给用户密码提供增强的安全性，\n你需要使用一个能够对抗暴力破解攻击的哈希算法。目前最好的选择是 `bcrypt`。在 PHP 中，\n你可以通过 [crypt 函数](https://www.php.net/manual/zh/function.crypt.php) 生成 `bcrypt` 哈希。\nYii 提供了两个帮助函数以让使用 `crypt` 来进行安全的哈希密码生成和验证更加容易。\n\n当一个用户为第一次使用，提供了一个密码时（比如：注册时），密码就需要被哈希化。\n\n\n```php\n$hash = Yii::$app->getSecurity()->generatePasswordHash($password);\n```\n\n哈希串可以被关联到对应的模型属性，这样，它可以被存储到数据库中以备将来使用。\n\n当一个用户尝试登录时，表单提交的密码需要使用之前的存储的哈希串来验证：\n\n\n```php\nif (Yii::$app->getSecurity()->validatePassword($password, $hash)) {\n    // all good, logging user in\n} else {\n    // wrong password\n}\n```\n"
  },
  {
    "path": "docs/guide-zh-CN/start-databases.md",
    "content": "使用数据库\n======================\n\n本章节将介绍如何创建一个从数据表 `country` 中读取国家数据并显示出来的页面。\n为了实现这个目标，你将会配置一个数据库连接，\n创建一个[活动记录](db-active-record.md)类，\n并且创建一个[操作](structure-controllers.md)及一个[视图](structure-views.md)。\n\n贯穿整个章节，你将会学到：\n\n* 配置一个数据库连接\n* 定义一个活动记录类\n* 使用活动记录从数据库中查询数据\n* 以分页方式在视图中显示数据\n\n请注意，为了掌握本章你应该具备最基本的数据库知识和使用经验。\n尤其是应该知道如何创建数据库，如何通过数据库终端执行 SQL 语句。\n\n\n准备数据库 <span id=\"preparing-database\"></span>\n--------------------\n\n首先创建一个名为 `yii2basic` 的数据库，应用将从这个数据库中读取数据。\n你可以创建 SQLite，MySQL，PostregSQL，MSSQL 或 Oracle 数据库，Yii 内置多种数据库支持。简单起见，后面的内容将以 MySQL 为例做演示。\n\n> Info: 虽然 MariaDB 曾经是 MySQL 的直接替代品，但现在已经不再完全正确。如果您希望在 MariaDB 中使用“JSON”支持等高级功能，请查看下面列出的 MariaDB 扩展。\n\n然后在数据库中创建一个名为 `country` 的表并插入简单的数据。可以执行下面的语句：\n\n```sql\nCREATE TABLE `country` (\n  `code` CHAR(2) NOT NULL PRIMARY KEY,\n  `name` CHAR(52) NOT NULL,\n  `population` INT(11) NOT NULL DEFAULT '0'\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nINSERT INTO `country` VALUES ('AU','Australia',18886000);\nINSERT INTO `country` VALUES ('BR','Brazil',170115000);\nINSERT INTO `country` VALUES ('CA','Canada',1147000);\nINSERT INTO `country` VALUES ('CN','China',1277558000);\nINSERT INTO `country` VALUES ('DE','Germany',82164700);\nINSERT INTO `country` VALUES ('FR','France',59225700);\nINSERT INTO `country` VALUES ('GB','United Kingdom',59623400);\nINSERT INTO `country` VALUES ('IN','India',1013662000);\nINSERT INTO `country` VALUES ('RU','Russia',146934000);\nINSERT INTO `country` VALUES ('US','United States',278357000);\n```\n\n此时便有了一个名为 `yii2basic` 的数据库，在这个数据库中有一个包含三个字段的数据表 `country`，表中有十行数据。\n\n配置数据库连接 <span id=\"configuring-db-connection\"></span>\n---------------------------\n\n开始之前，请确保你已经安装了 PHP [PDO](https://www.php.net/manual/zh/book.pdo.php) \n扩展和你所使用的数据库的 PDO 驱动（例如 MySQL 的 `pdo_mysql`）。\n对于使用关系型数据库来讲，这是基本要求。\n\n驱动和扩展安装可用后，打开 `config/db.php` 修改里面的配置参数对应你的数据库配置。\n该文件默认包含这些内容：\n\n```php\n<?php\n\nreturn [\n    'class' => 'yii\\db\\Connection',\n    'dsn' => 'mysql:host=localhost;dbname=yii2basic',\n    'username' => 'root',\n    'password' => '',\n    'charset' => 'utf8',\n];\n```\n\n`config/db.php` 是一个典型的基于文件的[配置](concept-configurations.md)工具。\n这个文件配置了数据库连接 [[yii\\db\\Connection]] 的创建和初始化参数，\n应用的 SQL 查询正是基于这个数据库。\n\n上面配置的数据库连接可以在应用中通过 `Yii::$app->db` 表达式访问。\n\n> Info: `config/db.php` 将被包含在应用配置文件 `config/web.php` 中，\n  后者指定了整个[应用](structure-applications.md)如何初始化。\n  请参考[配置](concept-configurations.md)章节了解更多信息。\n\n如果想要使用 Yii 没有捆绑支持的数据库，你可以查看以下插件：\n\n- [Informix](https://github.com/edgardmessias/yii2-informix)\n- [IBM DB2](https://github.com/edgardmessias/yii2-ibm-db2)\n- [Firebird](https://github.com/edgardmessias/yii2-firebird)\n- [MariaDB](https://github.com/sam-it/yii2-mariadb)\n\n\n创建活动记录 <span id=\"creating-active-record\"></span>\n-------------------------\n\n创建一个继承自[活动记录](db-active-record.md)类的类 `Country`，\n把它放在 `models/Country.php` 文件，去代表和读取 `country` 表的数据。\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass Country extends ActiveRecord\n{\n}\n```\n\n这个 `Country` 类继承自 [[yii\\db\\ActiveRecord]]。你不用在里面写任何代码。\n只需要像现在这样，Yii 就能根据类名去猜测对应的数据表名。\n\n> Info: 如果类名和数据表名不能直接对应，\n  可以覆写 [[yii\\db\\ActiveRecord::tableName()|tableName()]] 方法去显式指定相关表名。\n\n使用 `Country` 类可以很容易地操作 `country` 表数据，就像这段代码：\n\n```php\nuse app\\models\\Country;\n\n// 获取 country 表的所有行并以 name 排序\n$countries = Country::find()->orderBy('name')->all();\n\n// 获取主键为 “US” 的行\n$country = Country::findOne('US');\n\n// 输出 “United States”\necho $country->name;\n\n// 修改 name 为 “U.S.A.” 并在数据库中保存更改\n$country->name = 'U.S.A.';\n$country->save();\n```\n\n> Info: 活动记录是面向对象、功能强大的访问和操作数据库数据的方式。你可以在[活动记录](db-active-record.md)章节了解更多信息。\n  除此之外你还可以使用另一种更原生的被称做[数据访问对象](db-dao)的方法操作数据库数据。\n\n\n创建动作 <span id=\"creating-action\"></span>\n------------------\n\n为了向最终用户显示国家数据，你需要创建一个操作。相比之前小节掌握的在 `site` 控制器中创建操作，\n在这里为所有和国家有关的数据新建一个控制器更加合理。\n新控制器名为 `CountryController`，并在其中创建一个 `index` 操作，\n如下：\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\nuse yii\\data\\Pagination;\nuse app\\models\\Country;\n\nclass CountryController extends Controller\n{\n    public function actionIndex()\n    {\n        $query = Country::find();\n\n        $pagination = new Pagination([\n            'defaultPageSize' => 5,\n            'totalCount' => $query->count(),\n        ]);\n\n        $countries = $query->orderBy('name')\n            ->offset($pagination->offset)\n            ->limit($pagination->limit)\n            ->all();\n\n        return $this->render('index', [\n            'countries' => $countries,\n            'pagination' => $pagination,\n        ]);\n    }\n}\n```\n\n把上面的代码保存在 `controllers/CountryController.php` 文件中。\n\n`index` 操作调用了活动记录 `Country::find()` 方法，去生成查询语句并从 `country` 表中取回所有数据。\n为了限定每个请求所返回的国家数量，查询在 [[yii\\data\\Pagination]] 对象的帮助下进行分页。\n`Pagination` 对象的使命主要有两点：\n\n* 为 SQL 查询语句设置 `offset` 和 `limit` 从句，\n  确保每个请求只需返回一页数据（本例中每页是 5 行）。\n* 在视图中显示一个由页码列表组成的分页器，\n  这点将在后面的段落中解释。\n\n在代码末尾，`index` 操作渲染一个名为 `index` 的视图，\n并传递国家数据和分页信息进去。\n\n\n创建视图 <span id=\"creating-view\"></span>\n---------------\n\n在 `views` 目录下先创建一个名为 `country` 的子目录。\n这个目录存储所有由 `country` 控制器渲染的视图。在 `views/country` 目录下\n创建一个名为 `index.php` 的视图文件，内容如下：\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\LinkPager;\n?>\n<h1>Countries</h1>\n<ul>\n<?php foreach ($countries as $country): ?>\n    <li>\n        <?= Html::encode(\"{$country->name} ({$country->code})\") ?>:\n        <?= $country->population ?>\n    </li>\n<?php endforeach; ?>\n</ul>\n\n<?= LinkPager::widget(['pagination' => $pagination]) ?>\n```\n\n这个视图包含两部分用以显示国家数据。第一部分遍历国家数据并以无序 HTML 列表渲染出来。\n第二部分使用 [[yii\\widgets\\LinkPager]] 去渲染从操作中传来的分页信息。\n小部件 `LinkPager` 显示一个分页按钮的列表。\n点击任何一个按钮都会跳转到对应的分页。\n\n\n试运行 <span id=\"trying-it-out\"></span>\n-------------\n\n浏览器访问下面的 URL 看看能否工作：\n\n```\nhttps://hostname/index.php?r=country/index\n```\n\n![国家列表](images/start-country-list.png)\n\n首先你会看到显示着五个国家的列表页面。在国家下面，你还会看到一个包含四个按钮的分页器。\n如果你点击按钮 “2”，将会跳转到显示另外五个国家的页面，\n也就是第二页记录。如果观察仔细点你还会看到浏览器的 URL 变成了：\n\n```\nhttps://hostname/index.php?r=country/index&page=2\n```\n\n在这个场景里，[[yii\\data\\Pagination|Pagination]] 提供了为数据结果集分页的所有功能：\n\n* 首先 [[yii\\data\\Pagination|Pagination]] 把 SELECT 的子查询 `LIMIT 5 OFFSET 0` 数据表示成第一页。\n  因此开头的五条数据会被取出并显示。\n* 然后小部件 [[yii\\widgets\\LinkPager|LinkPager]] 使用 \n  [[yii\\data\\Pagination::createUrl()|Pagination::createUrl()]] 方法生成的 URL 去渲染翻页按钮。\n  URL 中包含必要的参数 `page` 才能查询不同的页面编号。\n* 如果你点击按钮 “2”，将会发起一个路由为 `country/index` 的新请求。\n  [[yii\\data\\Pagination|Pagination]] 接收到 URL 中\n  的 `page` 参数把当前的页码设为 2。\n  新的数据库请求将会以 `LIMIT 5 OFFSET 5` 查询并显示。\n\n\n总结 <span id=\"summary\"></span>\n-------\n\n本章节中你学到了如何使用数据库。你还学到了如何取出并使用 \n[[yii\\data\\Pagination]] 和 [[yii\\widgets\\LinkPager]] 显示数据。\n\n下一章中你会学到如何使用 Yii 中强大的代码生成器 [Gii](tool-gii.md)，\n去帮助你实现一些常用的功能需求，\n例如增查改删（CRUD）数据表中的数据。\n事实上你之前所写的代码全部都可以由 Gii 自动生成。\n"
  },
  {
    "path": "docs/guide-zh-CN/start-forms.md",
    "content": "使用表单\n==================\n\n本章节介绍如何创建一个让用户提交数据的表单页。\n该页将显示一个包含 name 输入框和 email 输入框的表单。\n当提交这两部分信息后，页面将会显示用户所输入的信息。\n\n为了实现这个目标，除了创建一个[操作](structure-controllers.md)和两个[视图](structure-views)外，\n还需要创建一个[模型](structure-models.md)。\n\n通过本教程，你将会学到：\n\n* 创建一个[模型](structure-models.md)代表用户通过表单输入的数据\n* 声明规则去验证输入的数据\n* 在[视图](structure-views.md)中生成一个 HTML 表单\n\n\n创建模型 <span id=\"creating-model\"></span>\n----------------\n\n模型类 `EntryForm` 代表从用户那请求的数据，\n该类如下所示并存储在 `models/EntryForm.php` 文件中。\n请参考[类自动加载](concept-autoloading.md)章节获取更多关于类命名约定的介绍。\n\n```php\n<?php\n\nnamespace app\\models;\n\nuse Yii;\nuse yii\\base\\Model;\n\nclass EntryForm extends Model\n{\n    public $name;\n    public $email;\n\n    public function rules()\n    {\n        return [\n            [['name', 'email'], 'required'],\n            ['email', 'email'],\n        ];\n    }\n}\n```\n\n该类继承自Yii 提供的一个基类 [[yii\\base\\Model]]，\n该基类通常用来表示数据。\n\n> Info: [[yii\\base\\Model]] 被用于普通模型类的父类并与数据表**无关**。[[yii\\db\\ActiveRecord]] \n  通常是普通模型类的父类但与数据表有关联（译注：[[yii\\db\\ActiveRecord]] 类其实也是继承自 [[yii\\base\\Model]]，增加了数据库处理）。\n\n`EntryForm` 类包含 `name` 和 `email` 两个公共成员，\n用来储存用户输入的数据。它还包含一个名为 `rules()` 的方法，\n用来返回数据验证规则的集合。上面声明的验证规则表示：\n\n* `name` 和 `email` 值都是必须的\n* `email` 的值必须满足email规则验证\n\n如果你有一个处理用户提交数据的 `EntryForm` 对象，\n你可以调用它的 [[yii\\base\\Model::validate()|validate()]] 方法触发数据验证。\n如果有数据验证失败，将把 [[yii\\base\\Model::hasErrors|hasErrors]] 属性设为 ture，\n想要知道具体发生什么错误就调用 [[yii\\base\\Model::getErrors|getErrors]]。\n\n```php\n<?php\n$model = new EntryForm();\n$model->name = 'Qiang';\n$model->email = 'bad';\nif ($model->validate()) {\n    // 验证成功！\n} else {\n    // 失败！\n    // 使用 $model->getErrors() 获取错误详情\n}\n```\n\n\n创建动作 <span id=\"creating-action\"></span>\n------------------\n\n下面你得在 `site` 控制器中创建一个 `entry` 操作用于新建的模型。\n操作的创建和使用已经在[说一声你好](start-hello.md)小节中解释了。\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\nuse app\\models\\EntryForm;\n\nclass SiteController extends Controller\n{\n    // ...现存的代码...\n\n    public function actionEntry()\n    {\n        $model = new EntryForm();\n\n        if ($model->load(Yii::$app->request->post()) && $model->validate()) {\n            // 验证 $model 收到的数据\n\n            // 做些有意义的事 ...\n\n            return $this->render('entry-confirm', ['model' => $model]);\n        } else {\n            // 无论是初始化显示还是数据验证错误\n            return $this->render('entry', ['model' => $model]);\n        }\n    }\n}\n```\n\n该操作首先创建了一个 `EntryForm` 对象。然后尝试从 `$_POST` 搜集用户提交的数据，\n由 Yii 的 [[yii\\web\\Request::post()]] 方法负责搜集。\n如果模型被成功填充数据（也就是说用户已经提交了 HTML 表单），\n操作将调用 [[yii\\base\\Model::validate()|validate()]] 去确保用户提交的是有效数据。\n\n> Info: 表达式 `Yii::$app` 代表[应用](structure-applications.md)实例，它是一个全局可访问的单例。\n  同时它也是一个[服务定位器](concept-service-locator.md)，\n  能提供 `request`，`response`，`db` 等等特定功能的组件。\n  在上面的代码里就是使用 `request` 组件来访问应用实例收到的 `$_POST` 数据。\n\n用户提交表单后，操作将会渲染一个名为 `entry-confirm` 的视图去确认用户输入的数据。\n如果没填表单就提交，或数据包含错误（译者：如 email 格式不对），\n`entry` 视图将会渲染输出，连同表单一起输出的还有验证错误的详细信息。\n\n> Note: 在这个简单例子里我们只是呈现了有效数据的确认页面。\n  实践中你应该考虑使用 [[yii\\web\\Controller::refresh()|refresh()]] \n  或 [[yii\\web\\Controller::redirect()|redirect()]] 去避免[表单重复提交问题](https://en.wikipedia.org/wiki/Post/Redirect/Get)。\n\n\n创建视图 <span id=\"creating-views\"></span>\n--------------\n\n最后创建两个视图文件 `entry-confirm` 和 `entry`。\n他们会被刚才创建的 `entry` 操作渲染。\n\n`entry-confirm` 视图简单地显示提交的 name 和 email 数据。视图文件应该保存在 `views/site/entry-confirm.php`。\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n<p>You have entered the following information:</p>\n\n<ul>\n    <li><label>Name</label>: <?= Html::encode($model->name) ?></li>\n    <li><label>Email</label>: <?= Html::encode($model->email) ?></li>\n</ul>\n```\n\n`entry` 视图显示一个 HTML 表单。视图文件应该保存在 `views/site/entry.php`。\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n?>\n<?php $form = ActiveForm::begin(); ?>\n\n    <?= $form->field($model, 'name') ?>\n\n    <?= $form->field($model, 'email') ?>\n\n    <div class=\"form-group\">\n        <?= Html::submitButton('Submit', ['class' => 'btn btn-primary']) ?>\n    </div>\n\n<?php ActiveForm::end(); ?>\n```\n\n视图使用了一个功能强大的[小部件](structure-widgets.md) [[yii\\widgets\\ActiveForm|ActiveForm]] \n去生成 HTML 表单。\n其中的 `begin()` 和 `end()` 分别用来渲染表单的开始和关闭标签。\n在这两个方法之间使用了 [[yii\\widgets\\ActiveForm::field()|field()]] 方法去创建输入框。\n第一个输入框用于 “name”，第二个输入框用于 “email”。\n之后使用 [[yii\\helpers\\Html::submitButton()]] 方法生成提交按钮。\n\n\n尝试下 <span id=\"trying-it-out\"></span>\n-------------\n\n用浏览器访问下面的 URL 看它能否工作：\n\n```\nhttps://hostname/index.php?r=site/entry\n```\n\n你会看到一个包含两个输入框的表单的页面。每个输入框的前面都有一个标签指明应该输入的数据类型。\n如果什么都不填就点击提交按钮，或填入格式不正确的 email 地址，将会看到在对应的输入框下显示错误信息。\n\n![验证错误的表单](images/start-form-validation.png)\n\n输入有效的 name 和 email 信息并提交后，\n将会看到一个显示你所提交数据的确认页面。\n\n![输入数据的确认页](images/start-entry-confirmation.png)\n\n\n\n### 效果说明 <span id=\"magic-explained\"></span>\n\n你可能会好奇 HTML 表单暗地里是如何工作的呢，\n看起来它可以为每个输入框显示文字标签，\n而当你没输入正确的信息时又不需要刷新页面就能给出错误提示，似乎有些神奇。\n\n是的，其实数据首先由客户端 JavaScript 脚本验证，然后才会提交给服务器通过 PHP 验证。\n[[yii\\widgets\\ActiveForm]] 足够智能到把你在 `EntryForm` \n模型中声明的验证规则转化成客户端 JavaScript 脚本去执行验证。\n如果用户浏览器禁用了 JavaScript， \n服务器端仍然会像 `actionEntry()` 方法里这样验证一遍数据。这保证了任何情况下用户提交的数据都是有效的。\n\n> Warning: 客户端验证是提高用户体验的手段。\n  无论它是否正常启用，服务端验证则都是必须的，请不要忽略它。\n\n输入框的文字标签是 `field()` 方法生成的，内容就是模型中该数据的属性名。\n例如模型中的 `name` 属性生成的标签就是 `Name`。\n\n你可以在视图中自定义标签\n按如下方法：\n\n```php\n<?= $form->field($model, 'name')->label('自定义 Name') ?>\n<?= $form->field($model, 'email')->label('自定义 Email') ?>\n```\n\n> Info: Yii 提供了相当多类似的小部件去帮你生成复杂且动态的视图。\n  在后面你还会了解到自己写小部件是多么简单。\n  你可能会把自己的很多视图代码转化成小部件以提高重用，加快开发效率。\n\n\n总结 <span id=\"summary\"></span>\n-------\n\n本章节指南中你接触了 MVC 设计模式的每个部分。\n学到了如何创建一个模型代表用户数据并验证它的有效性。\n\n你还学到了如何从用户那获取数据并在浏览器上回显给用户。\n这本来是开发应用的过程中比较耗时的任务，\n好在 Yii 提供了强大的小部件让它变得如此简单。\n\n在下一章节中，你将学习如何使用数据库，几乎每个应用都需要数据库。\n"
  },
  {
    "path": "docs/guide-zh-CN/start-gii.md",
    "content": "使用 Gii 生成代码\n========================\n\n本章将介绍如何使用 [Gii](tool-gii.md) 去自动生成 Web 站点常用功能的代码。使用 Gii 生成代码非常简单，\n只要按照 Gii 页面上的介绍输入正确的信息即可。\n\n贯穿本章节，你将会学到：\n\n* 在你的应用中开启 Gii\n* 使用 Gii 去生成活动记录类\n* 使用 Gii 去生成数据表操作的增查改删（CRUD）代码\n* 自定义 Gii 生成的代码\n\n\n开始 Gii <span id=\"starting-gii\"></span>\n------------\n\n[Gii](tool-gii.md) 是 Yii 中的一个[模块](structure-modules.md)。\n可以通过配置应用的 [[yii\\base\\Application::modules|modules]] 属性开启它。通常来讲在 `config/web.php` 文件中会有以下配置代码：\n\n```php\n$config = [ ... ];\n\nif (YII_ENV_DEV) {\n    $config['bootstrap'][] = 'gii';\n    $config['modules']['gii'] = [\n        'class' => 'yii\\gii\\Module',\n    ];\n}\n```\n\n这段配置表明，如果当前是[开发环境](concept-configurations.md#environment-constants)，\n应用会包含 `gii` 模块，模块类是 [[yii\\gii\\Module]]。\n\n如果你检查应用的[入口脚本](structure-entry-scripts.md) `web/index.php`，\n将看到这行代码将 `YII_ENV_DEV` 设为 true：\n\n```php\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n```\n\n鉴于这行代码的定义，应用处于开发模式下，按照上面的配置会打开 Gii 模块。你可以直接通过 URL 访问 Gii：\n\n```\nhttps://hostname/index.php?r=gii\n```\n\n> Info: 如果你通过本机以外的机器访问 Gii，请求会被出于安全原因拒绝。\n> 你可以配置 Gii 为其添加允许访问的 IP 地址：\n>\n```php\n'gii' => [\n    'class' => 'yii\\gii\\Module',\n    'allowedIPs' => ['127.0.0.1', '::1', '192.168.0.*', '192.168.178.20'] // 按需调整这里\n],\n```\n\n![Gii](images/start-gii.png)\n\n\n生成活动记录类 <span id=\"generating-ar\"></span>\n---------------------------------\n\n选择 “Model Generator” （点击 Gii 首页的链接）去生成活动记录类。并像这样填写表单：\n\n* Table Name: `country`\n* Model Class: `Country`\n\n![模型生成器](images/start-gii-model.png)\n\n然后点击 “Preview” 按钮。你会看到 `models/Country.php` 被列在将要生成的文件列表中。可以点击文件名预览内容。\n\n如果你已经创建过同样的文件，使用 Gii 会覆写它，\n点击文件名旁边的 `diff` 能查看现有文件与将要\n生成的文件的内容区别。\n\n![模型生成器预览](images/start-gii-model-preview.png)\n\n想要覆写已存在文件，选中 “overwrite” 下的复选框然后点击 “Generator”。如果是新文件，只点击 “Generator” 就好。\n\n接下来你会看到一个包含已生成文件的说明页面。如果生成过程中覆写过文件，\n还会有一条信息说明代码是重新生成覆盖的。\n\n\n生成 CRUD 代码 <span id=\"generating-crud\"></span>\n--------------------\n\nCRUD 代表增，查，改，删操作，这是绝大多数 Web 站点常用的数据处理方式。选择 Gii 中的 “CRUD Generator” （点击 Gii 首页的链接）去创建 CRUD 功能。本例 “country” 中需要这样填写表单：\n\n* Model Class: `app\\models\\Country`\n* Search Model Class: `app\\models\\CountrySearch`\n* Controller Class: `app\\controllers\\CountryController`\n\n![CRUD 生成器](images/start-gii-crud.png)\n\n然后点击 “Preview” 按钮。你会看到下述将要生成的文件列表。\n\n![CRUD 生成器预览](images/start-gii-crud-preview.png)\n\n如果你之前创建过 `controllers/CountryController.php` 和 `views/country/index.php` 文件（在指南的使用数据库章节），\n选中 “overwrite” 下的复选框覆写它们（之前的文件没能全部支持 CRUD）。\n\n\n试运行 <span id=\"trying-it-out\"></span>\n-------------\n\n用浏览器访问下面的 URL 查看生成代码的运行：\n\n```\nhttps://hostname/index.php?r=country/index\n```\n\n可以看到一个栅格显示着从数据表中读取的国家数据。支持在列头对数据进行排序，\n输入筛选条件进行筛选。\n\n可以浏览详情，编辑，或删除栅格中的每个国家。\n还可以点击栅格上方的 “Create Country” 按钮通过表单创建新国家。\n\n![国家的数据栅格](images/start-gii-country-grid.png)\n\n![编辑一个国家](images/start-gii-country-update.png)\n\n下面列出由 Gii 生成的文件，以便你研习功能和实现，\n或修改它们。\n\n* 控制器：`controllers/CountryController.php`\n* 模型：`models/Country.php` 和 `models/CountrySearch.php`\n* 视图：`views/country/*.php`\n\n> Info: Gii 被设计成高度可定制和可扩展的代码生成工具。\n  使用它可以大幅提高应用开发速度。\n  请参考 [Gii](tool-gii.md) 章节了解更多内容。\n\n\n总结 <span id=\"summary\"></span>\n-------\n\n本章学习了如何使用 Gii 去生成为数据表中\n数据实现完整 CRUD 功能的代码。\n"
  },
  {
    "path": "docs/guide-zh-CN/start-hello.md",
    "content": "说声 Hello\n============\n\n本章描述了如何在你的应用中创建一个新的 “Hello” 页面。为了实现这一目标，\n将会创建一个[操作](structure-controllers.md#creating-actions)\n和一个[视图](structure-views.md)：\n\n* 应用将会分派页面请求给动作\n* 动作将会依次渲染视图呈现 “Hello” 给最终用户\n\n贯穿整个章节，你将会掌握三件事：\n\n1. 如何创建一个[动作](structure-controllers.md)去响应请求，\n2. 如何创建一个[视图](structure-views.md)去构造响应内容，\n3. 以及一个应用如何分派请求给[动作](structure-controllers.md#creating-actions)。\n\n\n创建动作 <span id=\"creating-action\"></span>\n------------------\n\n为了 “Hello”，需要创建一个 `say` [操作](structure-controllers.md#creating-actions)，\n从请求中接收 `message` 参数并显示给最终用户。\n如果请求没有提供 `message` 参数，操作将显示默认参数 “Hello”。\n\n> Info: [操作](structure-controllers.md#creating-actions)是最终用户可以直接访问并执行的对象。\n  操作被组织在[控制器](structure-controllers.md)中。\n  一个操作的执行结果就是最终用户收到的响应内容。\n\n操作必须声明在[控制器](structure-controllers.md)中。为了简单起见，\n你可以直接在 `SiteController` 控制器里声明 `say` 操作。\n这个控制器是由文件 `controllers/SiteController.php` 定义的。以下是一个操作的声明：\n\n```php\n<?php\n\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    // ...现存的代码...\n\n    public function actionSay($message = 'Hello')\n    {\n        return $this->render('say', ['message' => $message]);\n    }\n}\n```\n\n在上述 `SiteController` 代码中，`say` 操作被定义为 `actionSay` 方法。\nYii 使用 `action` 前缀区分普通方法和操作。\n`action` 前缀后面的名称被映射为操作的 ID。\n\n涉及到给操作命名时，你应该理解 Yii 如何处理操作 ID。\n操作 ID 总是被以小写处理，如果一个操作 ID 由多个单词组成，\n单词之间将由连字符连接（如 `create-comment`）。\n操作 ID 映射为方法名时移除了连字符，将每个单词首字母大写，并加上 `action` 前缀。\n例子：操作 ID `create-comment` 相当于方法名 `actionCreateComment`。\n\n上述代码中的操作方法接受一个参数 `$message`，\n它的默认值是 `“Hello”`（就像你设置 PHP 中其它函数或方法的默认值一样）。\n当应用接收到请求并确定由 `say` 操作来响应请求时，应用将从请求的参数中寻找对应值传入进来。\n换句话说，如果请求包含一个 `message` 参数，\n它的值是 `“Goodbye”`， 操作方法中的 `$message` 变量也将被填充为 `“Goodbye”`。\n\n在操作方法中，[[yii\\web\\Controller::render()|render()]] 被用来渲染一个\n名为 `say` 的[视图](structure-views.md)文件。\n`message` 参数也被传入视图，这样就可以在里面使用。操作方法会返回渲染结果。\n结果会被应用接收并显示给最终用户的浏览器（作为整页 HTML 的一部分）。\n\n\n创建视图 <span id=\"creating-view\"></span>\n---------------\n\n[视图](structure-views.md)是你用来生成响应内容的脚本。为了说 “Hello”，\n你需要创建一个 `say` 视图，以便显示从操作方法中传来的 `message` 参数。\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n<?= Html::encode($message) ?>\n```\n\n`say` 视图应该存为 `views/site/say.php` 文件。当一个操作中调用了 [[yii\\web\\Controller::render()|render()]] 方法时，\n它将会按 `views/控制器 ID/视图名.php` 路径加载 PHP 文件。\n\n注意以上代码，`message` 参数在输出之前被 \n[[yii\\helpers\\Html::encode()|HTML-encoded]] 方法处理过。\n这很有必要，当参数来自于最终用户时，参数中可能隐含的恶意 JavaScript 代码会导致\n[跨站脚本（XSS）攻击](https://en.wikipedia.org/wiki/Cross-site_scripting)。\n\n当然了，你大概会在 `say` 视图里放入更多内容。内容可以由 HTML 标签，纯文本，\n甚至 PHP 语句组成。实际上 `say` 视图就是一个由 [[yii\\web\\Controller::render()|render()]] 执行的 PHP 脚本。\n视图脚本输出的内容将会作为响应结果返回给应用。应用将依次输出结果给最终用户。\n\n\n试运行 <span id=\"trying-it-out\"></span>\n-------------\n\n创建完动作和视图后，你就可以通过下面的 URL 访问新页面了：\n\n```\nhttps://hostname/index.php?r=site/say&message=Hello+World\n```\n\n![Hello World](images/start-hello-world.png)\n\n这个 URL 将会输出包含 “Hello World” 的页面，页面和应用里的其它页面使用同样的头部和尾部。\n\n如果你省略 URL 中的 `message` 参数，将会看到页面只显示 “Hello”。\n这是因为 `message` 被作为一个参数传给 `actionSay()` 方法，当省略它时，参数将使用默认的 `“Hello”` 代替。\n\n> Info: 新页面和其它页面使用同样的头部和尾部是因为 [[yii\\web\\Controller::render()|render()]] \n  方法会自动把 `say` 视图执行的结果嵌入称为[布局](structure-views.md#layouts)的文件中，\n  本例中是 `views/layouts/main.php`。\n\n上面 URL 中的参数 `r` 需要更多解释。\n它代表[路由](runtime-routing.md)，是整个应用级的，\n指向特定操作的独立 ID。路由格式是 `控制器 ID/操作 ID`。应用接受请求的时候会检查参数，\n使用控制器 ID 去确定哪个控制器应该被用来处理请求。\n然后相应控制器将使用操作 ID 去确定哪个操作方法将被用来做具体工作。\n上述例子中，路由 `site/say` 将被解析至 `SiteController` 控制器和其中的 `say` 操作。\n因此 `SiteController::actionSay()` 方法将被调用处理请求。\n\n> Info: 与操作一样，一个应用中控制器同样有唯一的 ID。\n  控制器 ID 和操作 ID 使用同样的命名规则。\n  控制器的类名源自于控制器 ID，\n  移除了连字符，每个单词首字母大写，并加上 `Controller` 后缀。\n  例子：控制器 ID `post-comment` 相当于控制器类名 `PostCommentController`。\n\n\n总结 <span id=\"summary\"></span>\n-------\n\n通过本章节你接触了 MVC 设计模式中的控制器和视图部分。\n创建了一个操作作为控制器的一部分去处理特定请求。\n然后又创建了一个视图去构造响应内容。在这个小例子中，没有模型调用，唯一涉及到数据的地方是 `message` 参数。\n\n你同样学习了 Yii 路由的相关内容，它是用户请求与控制器动作之间的桥梁。\n\n在下一章节中，你将学习如何创建一个模型，以及添加一个包含 HTML 表单的页面。\n"
  },
  {
    "path": "docs/guide-zh-CN/start-installation.md",
    "content": "安装 Yii\n==============\n\n你可以通过两种方式安装 Yii：使用 [Composer](https://getcomposer.org/) 或下载一个归档文件。\n推荐使用前者，这样只需执行一条简单的命令就可以安装新的[扩展](structure-extensions.md)或更新 Yii 了。\n\n标准安装完Yii之后，框架和一个项目模板两者都下载并安装好了。\n一个项目模板是实现了一些基本特性的一个 可行的Yii项目，比如登录，联系表单，等等。\n它的代码是以推荐的方式组织的。因此，它能够适合作为你项目的一个好的起点。\n\n在本章节和以后的章节，我们将会介绍如何去安装Yii和所谓的*基本的应用程序模板*和如何去实现这个模板上的新特性。\nYii当然也提供了其它模板叫\n[高级的应用程序模板](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide-zh-CN/README.md)，\n它是更好应用于在一个团队开发环境中去开发多层级的应用程序。\n\n> Info: 这个基本的应用程序模板是适合于开发90%的Web应用程序。\n  它不同于高级的应用程序模板主要地在如何使它们的代码是有组织的。\n  如果你是刚接触Yii，我们强烈建议你坚持使用简单并有足够的功能的基础的应用程序模板。\n\n\n通过 Composer 安装 <span id=\"installing-via-composer\"></span>\n-----------------------\n\n### 安装 Composer\n\n如果还没有安装 Composer，你可以按 [getcomposer.org](https://getcomposer.org/download/) 中的方法安装。\n在 Linux 和 Mac OS X 中可以运行如下命令：\n\n```bash\ncurl -sS https://getcomposer.org/installer | php\nmv composer.phar /usr/local/bin/composer\n```\n\n在 Windows 中，你需要下载并运行 [Composer-Setup.exe](https://getcomposer.org/Composer-Setup.exe)。\n\n如果遇到任何问题或者想更深入地学习 Composer，\n请参考 [Composer 文档](https://getcomposer.org/doc/)。\n如果你已经安装有 Composer 请确保使用的是最新版本，\n你可以用 `composer self-update` 命令更新 Composer 为最新版本。\n\n在本指南中，所有 composer 命令都假定您已经安装了[全局](https://getcomposer.org/doc/00-intro.md#globally) 的 composer，\n这样它可以作为 `composer` 命令。如果您在本地目录中使用 `composer.phar`，\n则必须相应地调整示例命令。\n\n如果您之前已安装 Composer，请确保使用最新版本。\n您可以通过运行 `composer self-update` 来更新Composer。\n\n> Note: 在安装 Yii 期间，Composer 需要从 Github API 请求很多信息。\n> 请求的数量取决于您的应用程序所依赖的数量，\n> 并可能大于 **Github API 速率限制**。如果达到此限制，Composer 可能会要求您提供 Github 登录凭据以获取\n> Github API 访问令牌。在快速连接上，您可能比 Composer 能够处理的时间早，\n> 因此我们建议您在安装 Yii 之前配置访问令牌。\n> 有关如何执行此操作的说明，请参阅\n> [Composer documentation about Github API tokens](https://getcomposer.org/doc/articles/troubleshooting.md#api-rate-limit-and-oauth-tokens)。\n\n### 安装 Yii <span id=\"installing-from-composer\"></span>\n\n安装 Composer 后，您可以通过在 Web 可访问的文件夹下运行以下命令来\n安装Yii应用程序模板：\n\n```bash\ncomposer create-project --prefer-dist yiisoft/yii2-app-basic basic\n```\n\n这将在一个名为 `basic` 的目录中安装Yii应用程序模板的最新稳定版本。\n如果需要，您可以选择不同的目录名称。\n\n> Info: 如果 `composer create-project` 命令失败，您也可以参考\n> [Composer 文档的疑难解答](https://getcomposer.org/doc/articles/troubleshooting.md)\n> 部分中的常见错误。修复错误后，\n> 您可以通过在 `basic` 目录内运行 `composer update` 来恢复中止安装。\n\n> Tip: 如果你想安装 Yii 的最新开发版本，可以使用以下命令代替，\n> 它添加了一个 [stability 选项](https://getcomposer.org/doc/04-schema.md#minimum-stability)：\n>\n> ```bash\n> composer create-project --prefer-dist --stability=dev yiisoft/yii2-app-basic basic\n> ```\n>\n> 请注意，Yii的开发版本不应该用于生产，因为它可能会破坏您的运行代码。\n\n\n通过归档文件安装 <span id=\"installing-from-archive-file\"></span>\n--------------\n\n通过归档文件安装 Yii 包括三个步骤：\n\n1. 从 [yiiframework.com](https://www.yiiframework.com/download/) 下载归档文件。\n2. 将下载的文件解压缩到 Web 访问的文件夹中。\n3. 修改 `config/web.php` 文件，给 `cookieValidationKey` 配置项\n   添加一个密钥（若你通过 Composer 安装，则此步骤会自动完成）：\n\n   ```php\n   // !!! 在下面插入一段密钥（若为空） - 以供 cookie validation 的需要\n   'cookieValidationKey' => '在此处输入你的密钥',\n   ```\n\n\n其他安装方式 <span id=\"other-installation-options\"></span>\n----------\n\n上文介绍了两种安装 Yii 的方法，\n安装的同时也会创建一个立即可用的 Web 应用程序。\n这个方法对大多数的大或者小的项目是一个不错的起点。如果你正好开始学习Yii，这是特别适合的。\n\n但是其他的安装方式也存在：\n\n* 如果你只想安装核心框架，然后从零开始构建整个属于你自己的应用程序模版，\n  可以参考[从头构建自定义模版](tutorial-start-from-scratch.md)一节的介绍。\n* 如果你要开发一个更复杂的应用，可以更好地适用于团队开发环境的，\n  你可以考虑安装[高级应用模版](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide-zh-CN/README.md)。\n\n\n安装 Assets <span id=\"installing-assets\"></span>\n-----------\n\nYii依靠 [Bower](https://bower.io/) 和/或 [NPM](https://www.npmjs.com/) 软件包来安装 asset（CSS 和 JavaScript）库。\n它使用Composer来获取这些库，允许 PHP 和 CSS/JavaScript 包版本同时解析。\n这可以通过使用 [asset-packagist.org](https://asset-packagist.org) 或 [composer asset plugin](https://github.com/fxpio/composer-asset-plugin) 来实现。\n有关更多详细信息，请参阅 [Assets 文档](structure-assets.md)。\n\n您可能希望通过本地 Bower/NPM 客户端管理您的 assets，使用 CDN 或完全避免 assets 的安装。\n为了防止通过 Composer 安装 assets，请将以下几行添加到您的 'composer.json' 中：\n\n```json\n\"replace\": {\n    \"bower-asset/jquery\": \">=1.11.0\",\n    \"bower-asset/inputmask\": \">=3.2.0\",\n    \"bower-asset/punycode\": \">=1.3.0\",\n    \"bower-asset/yii2-pjax\": \">=2.0.0\"\n},\n```\n\n> Note: 在通过 Composer 绕过 assets 安装的情况下，您负责 assets 的安装和解决版本冲突。\n> 准备来自不同扩展名的 assets 文件之间的可能不一致。\n\n\n验证安装的结果 <span id=\"verifying-installation\"></span>\n------------\n\n当安装完成之后，\n或配置你的Web服务器(看下面的文章)或使用[内置Web Server](https://www.php.net/manual/zh/features.commandline.webserver.php)，\n当在项目 `web` 目录下可以通过下面的命令:\n \n```bash\nphp yii serve\n```\n\n> Note: 默认情况下Https-server将监听8080。可是如果这个端口已经使用或者你想通过这个方式运行多个应用程序，你可以指定使用哪些端口。\n只加上 --port 参数：\n\n```bash\nphp yii serve --port=8888\n```\n\n安装完成后，就可以使用浏览器通过如下 URL 访问刚安装完的 Yii 应用了：\n\n```\nhttp://localhost:8080/\n```\n\n![Yii 安装成功](images/start-app-installed.png)\n\n你应该可以在浏览器中看到如上所示的 “Congratulations!” 页面。如果没有，\n请通过以下任意一种方式，检查当前 PHP 环境是否满足 Yii 最基本需求：\n\n* 复制 `/requirements.php` 到 `/web/requirements.php`，然后通过浏览器访问 URL `http://localhost/requirements.php`\n* 执行如下命令：\n\n  ```\n  cd basic\n  php requirements.php\n  ```\n\n你需要配置好 PHP 安装环境，使其符合 Yii 的最小需求。主要是需要 PHP 5.4 或 以上版本。\n如果应用需要用到数据库，那还要安装 [PDO PHP 扩展](https://www.php.net/manual/zh/pdo.installation.php) \n和相应的数据库驱动（例如访问 MySQL 数据库所需的 `pdo_mysql`）。\n\n\n配置 Web 服务器 <span id=\"configuring-web-servers\"></span>\n-----------------------\n\n> Tip: 如果你现在只是要试用 Yii 而不是将其部署到生产环境中，\n  本小节可以跳过。\n\n通过上述方法安装的应用程序在 Windows，Max OS X，\nLinux 中的 [Apache HTTP 服务器](https://httpd.apache.org/)\n或 [Nginx HTTP 服务器](https://nginx.org/)且PHP版本为5.4或更高都可以直接运行。\nYii 2.0 也兼容 Facebook 公司的 [HHVM](https://hhvm.com/)，\n由于 HHVM 和标准 PHP 在边界案例上有些地方略有不同，在使用 HHVM 时需稍作处理。\n\n在生产环境的服务器上，你可能会想配置服务器让应用程序可以通过\nURL `https://www.example.com/index.php` 访问而不是 `https://www.example.com/basic/web/index.php`。\n这种配置需要将 Web 服务器的文档根目录(document root)指向 `basic/web` 目录。\n可能你还会想隐藏掉 URL 中的 `index.php`，具体细节在 [URL 解析和生成](runtime-url-handling.md)一章中有介绍，\n你将学到如何配置 Apache 或 Nginx 服务器实现这些目标。\n\n> Info: 将 `basic/web` 设置为文档根目录(document root)，可以防止终端用户访问 `basic/web` 相邻目录中\n的私有应用代码和敏感数据文件。\n禁止对其他目录的访问是一个不错的安全改进。\n\n> Info: 如果你的应用程序将来要运行在共享虚拟主机环境中，\n没有修改其 Web 服务器配置的权限，你依然可以通过调整应用的结构来提升安全性。\n详情请参考[共享主机环境](tutorial-shared-hosting.md) 一章。\n\n> Info: 如果您在反向代理后面运行Yii应用程序，\n> 则可能需要在请求组件中配置 [Trusted proxies and headers](runtime-requests.md#trusted-proxies)。\n\n### 推荐使用的 Apache 配置 <span id=\"recommended-apache-configuration\"></span>\n\n在 Apache 的 `httpd.conf` 文件或在一个虚拟主机配置文件中使用如下配置。\n注意，你应该将 `path/to/basic/web` 替换为实际的 `basic/web` 目录。\n\n```\n# 设置文档根目录为 \"basic/web\"\nDocumentRoot \"path/to/basic/web\"\n\n<Directory \"path/to/basic/web\">\n    # 开启 mod_rewrite 用于美化 URL 功能的支持（译注：对应 pretty URL 选项）\n    RewriteEngine on\n    # 如果请求的是真实存在的文件或目录，直接访问\n    RewriteCond %{REQUEST_FILENAME} !-f\n    RewriteCond %{REQUEST_FILENAME} !-d\n    # 如果请求的不是真实文件或目录，分发请求至 index.php\n    RewriteRule . index.php\n\n    # if $showScriptName is false in UrlManager, do not allow accessing URLs with script name\n    RewriteRule ^index.php/ - [L,R=404]\n    \n    # ...其它设置...\n</Directory>\n```\n\n\n### 推荐使用的 Nginx 配置 <span id=\"recommended-nginx-configuration\"></span>\n\n为了使用 [Nginx](https://www.nginx.com/resources/wiki/)，你应该已经将 PHP 安装为 [FPM SAPI](https://www.php.net/manual/zh/install.fpm.php) 了。\n你可以使用如下 Nginx 配置，将 `path/to/basic/web` 替换为实际的 `basic/web` 目录，\n`mysite.local` 替换为实际的主机名以提供服务。\n\n```nginx\nserver {\n    charset utf-8;\n    client_max_body_size 128M;\n\n    listen 80; ## listen for ipv4\n    #listen [::]:80 default_server ipv6only=on; ## listen for ipv6\n\n    server_name mysite.test;\n    root        /path/to/basic/web;\n    index       index.php;\n\n    access_log  /path/to/basic/log/access.log;\n    error_log   /path/to/basic/log/error.log;\n\n    location / {\n        # Redirect everything that isn't a real file to index.php\n        try_files $uri $uri/ /index.php$is_args$args;\n    }\n\n    # uncomment to avoid processing of calls to non-existing static files by Yii\n    #location ~ \\.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ {\n    #    try_files $uri =404;\n    #}\n    #error_page 404 /404.html;\n\n    # deny accessing php files for the /assets directory\n    location ~ ^/assets/.*\\.php$ {\n        deny all;\n    }\n    \n    location ~ \\.php$ {\n        include fastcgi_params;\n        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;\n        fastcgi_pass 127.0.0.1:9000;\n        #fastcgi_pass unix:/var/run/php5-fpm.sock;\n        try_files $uri =404;\n    }\n\n    location ~* /\\. {\n        deny all;\n    }\n}\n```\n\n使用该配置时，你还应该在 `php.ini` 文件中设置 `cgi.fix_pathinfo=0` ，\n能避免掉很多不必要的 `stat()` 系统调用。\n\n还要注意当运行一个 HTTPS 服务器时，需要添加 `fastcgi_param HTTPS on;` 一行，\n这样 Yii 才能正确地判断连接是否安全。\n"
  },
  {
    "path": "docs/guide-zh-CN/start-looking-ahead.md",
    "content": "更上一层楼（Looking Ahead）\n========================\n\n通篇阅读完整个“入门”部分，你就完成了一个完整 Yii 应用的创建。在此过程中你学到了如何实现一些常用功能，\n例如通过 HTML 表单从用户那获取数据，从数据库中获取数据并以分页形式显示。\n你还学到了如何通过 [Gii](tool-gii.md) 去自动生成代码。\n使用 Gii 生成代码把 Web 开发中多数繁杂的过程转化为仅仅填写几个表单就行。\n\n本章将介绍一些有助于更好使用 Yii 的资源：\n\n* 文档\n    - 权威指南：顾名思义，指南详细描述了 Yii 的工作原理\n      并提供了如何使用它的常规引导。\n      这是最重要的 Yii 辅助资料，\n      强烈建议在开始写 Yii 代码之前阅读。\n    - 类参考手册：描述了 Yii 中每个类的用法。\n      在编码过程中这极为有用，能够帮你理清某个特定类，\n      方法，和属性的用法。类参考手册最好在整个框架的语境下去理解。\n    - Wiki 文章：Wiki 文章是 Yii 用户在其自身经验基础上分享出来的。\n      大多数是使用教程或如何使用 Yii 解决特定问题。\n      虽然这些文章质量可能并不如权威指南，\n      但它们往往覆盖了更广泛的话题，并常常提供解决方案，\n      所以它们也很有用。\n    - 书籍\n* [扩展](https://www.yiiframework.com/extensions/)：Yii 拥有数以千计用户提供的扩展，这些扩展能非常方便的插入到应用中，\n  使你的应用开发过程更加方便快捷。\n* 社区\n    - 官方论坛：<https://forum.yiiframework.com/>\n    - IRC 聊天室：Libera 网络上的 #yii 频道 (<ircs://irc.libera.chat:6697>)\n    - Slack chanel：<https://yii.slack.com>\n    - Gitter chat：<https://gitter.im/yiisoft/yii2>\n    - GitHub：<https://github.com/yiisoft/yii2>\n    - Facebook：<https://www.facebook.com/groups/yiitalk/>\n    - Twitter：<https://twitter.com/yiiframework>\n    - LinkedIn：<https://www.linkedin.com/groups/yii-framework-1483367>\n    - Stackoverflow：<https://stackoverflow.com/questions/tagged/yii2>\n"
  },
  {
    "path": "docs/guide-zh-CN/start-prerequisites.md",
    "content": "# 你需要了解什么（What do you need to know）\n\nYii 的学习曲线并不像其他 PHP 框架那样陡峭，但仍然需要一些基础知识。\n\n## PHP\n\nYii 是一个 PHP 框架，因此请确保您 [阅读并理解语言参考](https://www.php.net/manual/zh/langref.php)。\n用 Yii 进行开发时，您将以面向对象的方式编写代码，因此请确保您熟悉[类与对象](https://www.php.net/manual/en/language.oop5.basic.php)以及[命名空间](https://www.php.net/manual/en/language.namespaces.php)。\n\n## 面向对象编程（Object oriented programming）\n\n对面向对象编程的基本理解是必需的。如果您不熟悉它，请选中其中一个\n可用的教程，如 [tuts+](https://code.tutsplus.com/tutorials/object-oriented-php-for-beginners--net-12762)。\n\n请注意，您的应用程序越复杂，您需要学习的更高级的 OOP 概念才能成功\n掌握这种复杂度。\n\n## 命令行和 Composer（Command line and composer）\n\nYii 广泛使用了标准的 PHP 包管理器 [Composer](https://getcomposer.org/)，因此请确保您阅读\n并理解其指南。如果您不熟悉命令行，现在该开始尝试了。\n一旦你学会了基础知识，你就永远不想在没有它的情况下工作。\n\n"
  },
  {
    "path": "docs/guide-zh-CN/start-workflow.md",
    "content": "运行应用\n====================\n\n安装 Yii 后，就有了一个可运行的 Yii 应用，\n根据配置的不同，可以通过 `https://hostname/basic/web/index.php` 或 `https://hostname/index.php` 访问。\n本章节将介绍应用的内建功能，如何组织代码，\n以及一般情况下应用如何处理请求。\n\n> Info: 为简单起见，在整个“入门”板块都假定你已经把\n  `basic/web` 设为 Web 服务器根目录并配置完毕，\n  你访问应用的地址会是 `https://hostname/index.php` 或类似的。\n  请按需调整 URL。\n\n注意项目模板和框架完全不同，安装完之后全都归你了。你可以根据你的需要自由的添加或删除代码和\n修改全部的。\n\n\n功能 <span id=\"functionality\"></span>\n-------------\n\n一个安装完的基本应用包含四页：\n\n* 主页，当你访问 `https://hostname/index.php` 时显示,\n* “About”页，\n* “Contact”页， 显示一个联系表单，允许终端用户通过 Email 联系你，\n* “Login”页， 显示一个登录表单，用来验证终端用户。试着用“admin/admin”登录，\n  你可以看到当前是登录状态，已经可以“退出登录”了。\n\n这些页面使用同一个头部和尾部。\n头部包含了一个可以在不同页面间切换的导航栏。\n\n在浏览器底部可以看到一个工具栏。这是 Yii 提供的很有用的[调试工具](tool-debugger.md)，\n可以记录并显示大量的调试信息，例如日志信息，响应状态，数据库查询等等。\n\n除了 web 应用程序，还有一个控制台脚本叫 `yii` ,它位于应用程序根目录。\n它可以用于程序的后台运行和维护任务，在[控制台应用程序章节](tutorial-console.md)\n中描述。\n\n\n应用结构 <span id=\"application-structure\"></span>\n---------------------\n\n应用中最重要的目录和文件（假设应用根目录是 `basic`）：\n\n```\nbasic/                  应用根目录\n    composer.json       Composer 配置文件, 描述包信息\n    config/             包含应用配置及其它配置\n        console.php     控制台应用配置信息\n        web.php         Web 应用配置信息\n    commands/           包含控制台命令类\n    controllers/        包含控制器类\n    models/             包含模型类\n    runtime/            包含 Yii 在运行时生成的文件，例如日志和缓存文件\n    vendor/             包含已经安装的 Composer 包，包括 Yii 框架自身\n    views/              包含视图文件\n    web/                Web 应用根目录，包含 Web 入口文件\n        assets/         包含 Yii 发布的资源文件（javascript 和 css）\n        index.php       应用入口文件\n    yii                 Yii 控制台命令执行脚本\n```\n\n一般来说，应用中的文件可被分为两类：在 `basic/web` 下的和在其它目录下的。\n前者可以直接通过 HTTP 访问（例如浏览器），后者不能也不应该被直接访问。\n\nYii 实现了[模型-视图-控制器 (MVC)](https://wikipedia.org/wiki/Model-view-controller)设计模式，这点在上述目录结构中也得以体现。\n`models` 目录包含了所有[模型类](structure-models.md)，\n`views` 目录包含了所有[视图脚本](structure-views.md)，\n`controllers` 目录包含了所有[控制器类](structure-controllers.md)。\n\n以下图表展示了一个应用的静态结构：\n\n![应用静态结构](images/application-structure.png)\n\n每个应用都有一个入口脚本 `web/index.php`，这是整个应用中唯一可以访问的 PHP 脚本。\n入口脚本接受一个 Web 请求并创建[应用](structure-application.md)实例去处理它。 \n[应用](structure-applications.md)在它的[组件](concept-components.md)辅助下解析请求，\n并分派请求至 MVC 元素。[视图](structure-views.md)使用[小部件](structure-widgets.md)\n去创建复杂和动态的用户界面。\n\n\n请求生命周期 <span id=\"request-lifecycle\"></span>\n-----------------\n\n以下图表展示了一个应用如何处理请求：\n\n![请求生命周期](images/request-lifecycle.png)\n\n1. 用户向[入口脚本](structure-entry-scripts.md) `web/index.php` 发起请求。\n2. 入口脚本加载应用[配置](concept-configurations.md)并创建一个[应用](structure-applications.md)\n   实例去处理请求。\n3. 应用通过[请求](runtime-request.md)组件解析请求的\n   [路由](runtime-routing.md)。\n4. 应用创建一个[控制器](structure-controllers.md)实例去处理请求。\n5. 控制器创建一个[动作](structure-controllers.md)实例并针对操作执行过滤器。\n6. 如果任何一个过滤器返回失败，则动作取消。\n7. 如果所有过滤器都通过，动作将被执行。\n8. 动作会加载一个数据模型，或许是来自数据库。\n9. 动作会渲染一个视图，把数据模型提供给它。\n10. 渲染结果返回给[响应](runtime-responses.md)组件。\n11. 响应组件发送渲染结果给用户浏览器。\n\n"
  },
  {
    "path": "docs/guide-zh-CN/structure-application-components.md",
    "content": "应用组件\n======================\n\n应用主体是[服务定位器](concept-service-locator.md)，\n它部署一组提供各种不同功能的 *应用组件* 来处理请求。\n例如，`urlManager`组件负责处理网页请求路由到对应的控制器。\n`db`组件提供数据库相关服务等等。\n\n在同一个应用中，每个应用组件都有一个独一无二的 ID 用来区分其他应用组件，\n你可以通过如下表达式访问应用组件。\n\n```php\n\\Yii::$app->componentID\n```\n\n例如，可以使用 `\\Yii::$app->db` 来获取到已注册到应用的 [[yii\\db\\Connection|DB connection]]，\n使用 `\\Yii::$app->cache` 来获取到已注册到应用的 [[yii\\caching\\Cache|primary cache]]。\n\n第一次使用以上表达式时候会创建应用组件实例，\n后续再访问会返回此实例，无需再次创建。\n\n应用组件可以是任意对象，可以在 [应用主体配置](structure-applications.md#application-configurations)配置\n[[yii\\base\\Application::components]] 属性。\n例如：\n\n```php\n[\n    'components' => [\n        // 使用类名注册 \"cache\" 组件\n        'cache' => 'yii\\caching\\ApcCache',\n\n        // 使用配置数组注册 \"db\" 组件\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=demo',\n            'username' => 'root',\n            'password' => '',\n        ],\n\n        // 使用函数注册\"search\" 组件\n        'search' => function () {\n            return new app\\components\\SolrService;\n        },\n    ],\n]\n```\n\n> Info: 请谨慎注册太多应用组件，\n  应用组件就像全局变量，\n  使用太多可能加大测试和维护的难度。\n  一般情况下可以在需要时再创建本地组件。\n\n\n## 引导启动组件 <span id=\"bootstrapping-components\"></span>\n\n上面提到一个应用组件只会在第一次访问时实例化，\n如果处理请求过程没有访问的话就不实例化。\n有时你想在每个请求处理过程都实例化某个组件即便它不会被访问，\n可以将该组件ID加入到应用主体的 [[yii\\base\\Application::bootstrap|bootstrap]] 属性中。\n\n你还可以使用闭包来引导启动自定义的组件。不需要直接返回一个实例化的组件。\n在应用主体 [[yii\\base\\Application]] 实例化后，闭包也会被调用。\n\n例如, 如下的应用主体配置保证了 `log` 组件一直被加载。\n\n```php\n[\n    'bootstrap' => [\n        'log',\n        function($app){\n            return new ComponentX();\n        },\n        function($app){\n            // 可以写自定义的代码\n           return;\n        }\n    ],\n    'components' => [\n        'log' => [\n            // \"log\" 组件的配置\n        ],\n    ],\n]\n```\n\n\n## 核心应用组件 <span id=\"core-application-components\"></span>\n\nYii 定义了一组固定ID和默认配置的 *核心* 组件，\n例如 [[yii\\web\\Application::request|request]] 组件\n用来收集用户请求并解析 [路由](runtime-routing.md)；\n[[yii\\base\\Application::db|db]] 代表一个可以执行数据库操作的数据库连接。\n通过这些组件，Yii应用主体能处理用户请求。\n\n下面是预定义的核心应用组件列表，\n可以和普通应用组件一样配置和自定义它们。\n当你配置一个核心组件，不指定它的类名的话就会使用Yii默认指定的类。\n\n* [[yii\\web\\AssetManager|assetManager]]: 管理资源包和资源发布，\n  详情请参考 [管理资源](output-assets.md) 一节。\n* [[yii\\db\\Connection|db]]: 代表一个可以执行数据库操作的数据库连接，\n  注意配置该组件时必须指定组件类名和其他相关组件属性，\n  如[[yii\\db\\Connection::dsn]]。\n  详情请参考 [数据访问对象](db-dao.md) 一节。\n* [[yii\\base\\Application::errorHandler|errorHandler]]: 处理 PHP 错误和异常，\n  详情请参考 [错误处理](tutorial-handling-errors.md) 一节。\n* [[yii\\i18n\\Formatter|formatter]]: 格式化输出显示给终端用户的数据，例如数字可能要带分隔符，\n  日期使用长格式。\n  详情请参考 [格式化输出数据](output-formatting.md) 一节。\n* [[yii\\i18n\\I18N|i18n]]: 支持信息翻译和格式化。\n  详情请参考 [国际化](tutorial-i18n.md) 一节。\n* [[yii\\log\\Dispatcher|log]]: 管理日志对象。\n  详情请参考 [日志](tutorial-logging.md) 一节。\n* [[yii\\swiftmailer\\Mailer|mail]]: 支持生成邮件结构并发送，\n  详情请参考 [邮件](tutorial-mailing.md) 一节。\n* [[yii\\base\\Application::response|response]]: 代表发送给用户的响应，\n  详情请参考 [响应](runtime-responses.md) 一节。\n* [[yii\\base\\Application::request|request]]: 代表从终端用户处接收到的请求，\n  详情请参考 [请求](runtime-requests.md) 一节。\n* [[yii\\web\\Session|session]]: 代表会话信息，\n  仅在[[yii\\web\\Application|Web applications]] 网页应用中可用，\n  详情请参考 [Sessions (会话) and Cookies](runtime-sessions-cookies.md) 一节。\n* [[yii\\web\\UrlManager|urlManager]]: 支持URL地址解析和创建，\n  详情请参考 [URL 解析和生成](runtime-url-handling.md) 一节。\n* [[yii\\web\\User|user]]: 代表认证登录用户信息，\n  仅在[[yii\\web\\Application|Web applications]] 网页应用中可用，\n  详情请参考 [认证](security-authentication.md) 一节。\n* [[yii\\web\\View|view]]: 支持渲染视图，\n  详情请参考 [Views](structure-views.md) 一节。\n"
  },
  {
    "path": "docs/guide-zh-CN/structure-applications.md",
    "content": "应用主体\n============\n\n应用主体是管理 Yii 应用系统整体结构和生命周期的对象。\n每个 Yii 应用系统只能包含一个应用主体，应用主体在 \n[入口脚本](structure-entry-scripts.md) 中创建并能通过表达式 `\\Yii::$app` 全局范围内访问。\n\n> Info: 当我们说“一个应用”，它可能是一个应用主体对象，也可能是一个应用系统，\n  是根据上下文来决定[译：中文为避免歧义，Application 翻译为应用主体]。\n\nYii有两种应用主体: [[yii\\web\\Application|网页应用主体]] 和\n[[yii\\console\\Application|控制台应用主体]]， \n如名称所示，前者主要处理网页请求，后者处理控制台请求。\n\n\n## 应用主体配置 <span id=\"application-configurations\"></span>\n\n如下所示，当 [入口脚本](structure-entry-scripts.md) 创建了一个应用主体，\n它会加载一个 [配置](concept-configurations.md) 文件并传给应用主体。\n\n```php\nrequire __DIR__ . '/../vendor/autoload.php';\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n\n// 加载应用主体配置\n$config = require __DIR__ . '/../config/web.php';\n\n// 实例化应用主体、配置应用主体\n(new yii\\web\\Application($config))->run();\n```\n\n类似其他 [配置](concept-configurations.md) 文件, \n应用主体配置文件标明如何设置应用对象初始属性。\n由于应用主体配置比较复杂，一般保存在多个类似如上web.php的 \n[配置文件](concept-configurations.md#configuration-files) 当中。\n\n\n## 应用主体属性 <span id=\"application-properties\"></span>\n\n应用主体配置文件中有许多重要的属性要配置，这些属性指定应用主体的运行环境。\n比如，应用主体需要知道如何加载 [控制器](structure-controllers.md) ，\n临时文件保存到哪儿等等。\n以下我们简述这些属性。\n\n\n### 必要属性 <span id=\"required-properties\"></span>\n\n在一个应用中，至少要配置2个属性: \n[[yii\\base\\Application::id|id]] 和 [[yii\\base\\Application::basePath|basePath]]。\n\n\n#### [[yii\\base\\Application::id|id]] <span id=\"id\"></span>\n\n[[yii\\base\\Application::id|id]] 属性用来区分其他应用的唯一标识ID。主要给程序使用。\n为了方便协作，最好使用数字作为应用主体ID，\n但不强制要求为数字。\n\n\n#### [[yii\\base\\Application::basePath|basePath]] <span id=\"basePath\"></span>\n\n[[yii\\base\\Application::basePath|basePath]] 指定该应用的根目录。\n根目录包含应用系统所有受保护的源代码。\n在根目录下可以看到对应 MVC 设计模式的`models`，\n`views`，`controllers` 等子目录。\n\n可以使用路径或 [路径别名](concept-aliases.md) 来在配置 [[yii\\base\\Application::basePath|basePath]] 属性。\n两种格式所对应的目录都必须存在，否则系统会抛出一个异常。 \n系统会使用 `realpath()` 函数规范化配置的路径。\n\n[[yii\\base\\Application::basePath|basePath]] 属性经常用于派生一些其他重要路径（如 runtime 路径），\n因此，系统预定义 `@app` 代表这个路径。\n派生路径可以通过这个别名组成（如`@app/runtime`代表runtime的路径）。\n\n\n### 重要属性 <span id=\"important-properties\"></span>\n\n本小节所描述的属性通常需要设置，\n因为不同的应用属性不同。\n\n\n#### [[yii\\base\\Application::aliases|aliases]] <span id=\"aliases\"></span>\n\n该属性允许你用一个数组定义多个 [别名](concept-aliases.md)。\n数组的key为别名名称，值为对应的路径。\n例如：\n\n```php\n[\n    'aliases' => [\n        '@name1' => 'path/to/path1',\n        '@name2' => 'path/to/path2',\n    ],\n]\n```\n\n使用这个属性来定义别名，\n代替 [[Yii::setAlias()]] 方法来设置。\n\n\n#### [[yii\\base\\Application::bootstrap|bootstrap]] <span id=\"bootstrap\"></span>\n\n这个属性很实用，它允许你用数组指定启动阶段 [[yii\\base\\Application::bootstrap()|bootstrapping process]] 需要运行的组件。\n比如，如果你希望一个 [模块](structure-modules.md) \n自定义 [URL 规则](runtime-url-handling.md)，\n你可以将模块ID加入到bootstrap数组中。\n\n属性中的每个组件需要指定以下一项:\n\n- 应用 [组件](#components) ID.\n- [模块](#modules) ID.\n- 类名.\n- 配置数组.\n- 创建并返回一个组件的无名称函数.\n\n例如：\n\n```php\n[\n    'bootstrap' => [\n        // 应用组件ID或模块ID\n        'demo',\n\n        // 类名\n        'app\\components\\Profiler',\n\n        // 配置数组\n        [\n            'class' => 'app\\components\\Profiler',\n            'level' => 3,\n        ],\n\n        // 匿名函数\n        function () {\n            return new app\\components\\Profiler();\n        }\n    ],\n]\n```\n\n> Info: 如果模块 ID 和应用组件 ID 同名，优先使用应用组件 ID，\n> 如果你想用模块 ID，\n> 可以使用如下无名称函数返回模块 ID。\n> \n> ```php\n> [\n>     function () {\n>         return Yii::$app->getModule('user');\n>     },\n> ]\n> ```\n\n\n在启动阶段，每个组件都会实例化。如果组件类实现接口\n[[yii\\base\\BootstrapInterface]]，也会调用\n[[yii\\base\\BootstrapInterface::bootstrap()|bootstrap()]] 方法。\n\n举一个实际的例子，[Basic Application Template](start-installation.md) \n应用主体配置中，\n开发环境下会在启动阶段运行 `debug` 和 `gii` 模块。\n\n```php\nif (YII_ENV_DEV) {\n    // configuration adjustments for 'dev' environment\n    $config['bootstrap'][] = 'debug';\n    $config['modules']['debug'] = 'yii\\debug\\Module';\n\n    $config['bootstrap'][] = 'gii';\n    $config['modules']['gii'] = 'yii\\gii\\Module';\n}\n```\n\n> Note: 启动太多的组件会降低系统性能，因为每次请求都需要重新运行启动组件，\n> 因此谨慎配置启动组件。\n\n\n#### [[yii\\web\\Application::catchAll|catchAll]] <span id=\"catchAll\"></span>\n\n该属性仅 [[yii\\web\\Application|Web applications]] 网页应用支持。\n它指定一个要处理所有用户请求的 [控制器方法](structure-controllers.md)，\n通常在维护模式下使用，同一个方法处理所有用户请求。\n\n该配置为一个数组，第一项指定动作的路由，剩下的数组项(key-value 成对)指定传递给动作的参数，\n例如：\n\n```php\n[\n    'catchAll' => [\n        'offline/notice',\n        'param1' => 'value1',\n        'param2' => 'value2',\n    ],\n]\n```\n\n> Info: 当开启这个属性时，开发环境下的调试面板将不能工作。\n\n#### [[yii\\base\\Application::components|components]] <span id=\"components\"></span>\n\n这是最重要的属性，它允许你注册多个在其他地方使用的 [应用组件](#structure-application-components.md)。\n例如\n\n```php\n[\n    'components' => [\n        'cache' => [\n            'class' => 'yii\\caching\\FileCache',\n        ],\n        'user' => [\n            'identityClass' => 'app\\models\\User',\n            'enableAutoLogin' => true,\n        ],\n    ],\n]\n```\n\n每一个应用组件指定一个key-value对的数组，key代表组件ID，\nvalue代表组件类名或 [配置](concept-configurations.md)。\n\n在应用中可以任意注册组件，并可以通过表达式 \n`\\Yii::$app->ComponentID` 全局访问。\n\n详情请阅读 [应用组件](structure-application-components.md) 一节.\n\n\n#### [[yii\\base\\Application::controllerMap|controllerMap]] <span id=\"controllerMap\"></span>\n\n该属性允许你指定一个控制器 ID 到任意控制器类。\nYii 遵循一个默认的 [规则](#controllerNamespace) 指定控制器 ID 到任意控制器类（如 `post` 对应`app\\controllers\\PostController`）。\n通过配置这个属性，可以打破这个默认规则，在下面的例子中，\n`account`对应到`app\\controllers\\UserController`，\n`article` 对应到 `app\\controllers\\PostController`。\n\n```php\n[\n    'controllerMap' => [\n        'account' => 'app\\controllers\\UserController',\n        'article' => [\n            'class' => 'app\\controllers\\PostController',\n            'enableCsrfValidation' => false,\n        ],\n    ],\n]\n```\n\n数组的键代表控制器ID，\n数组的值代表对应的类名。\n\n\n#### [[yii\\base\\Application::controllerNamespace|controllerNamespace]] <span id=\"controllerNamespace\"></span>\n\n该属性指定控制器类默认的命名空间，默认为`app\\controllers`。\n比如控制器ID为 `post` 默认对应 `PostController`（不带命名空间），\n类全名为 `app\\controllers\\PostController`。\n\n控制器类文件可能放在这个命名空间对应目录的子目录下，\n例如，控制器 ID `admin/post` 对应的控制器类全名为 \n`app\\controllers\\admin\\PostController`。\n\n控制器类全面能被 [自动加载](concept-autoloading.md)，\n这点是非常重要的，控制器类的实际命名空间对应这个属性，\n否则，访问时你会收到“Page Not Found”。\n\n如果你想打破上述的规则，\n可以配置 [controllerMap](#controllerMap) 属性。\n\n\n#### [[yii\\base\\Application::language|language]] <span id=\"language\"></span>\n\n该属性指定应用展示给终端用户的语言，\n默认为 `en` 标识英文。\n如果需要之前其他语言可以配置该属性。\n\n该属性影响各种 [国际化](tutorial-i18n.md) ，\n包括信息翻译、日期格式、数字格式等。\n例如 [[yii\\jui\\DatePicker]] 小部件会根据该属性\n展示对应语言的日历以及日期格式。\n\n推荐遵循 [IETF language tag](https://zh.wikipedia.org/wiki/IETF%E8%AA%9E%E8%A8%80%E6%A8%99%E7%B1%A4) 来设置语言，\n例如 `en` 代表英文， `en-US` 代表英文(美国).\n\n该属性的更多信息可参考 [国际化](tutorial-i18n.md) 一节.\n\n\n#### [[yii\\base\\Application::modules|modules]] <span id=\"modules\"></span>\n\n该属性指定应用所包含的 [模块](structure-modules.md)。\n\n该属性使用数组包含多个模块类 [配置](concept-configurations.md)，数组的键为模块ID，\n例：\n\n```php\n[\n    'modules' => [\n        // \"booking\" 模块以及对应的类\n        'booking' => 'app\\modules\\booking\\BookingModule',\n\n        // \"comment\" 模块以及对应的配置数组\n        'comment' => [\n            'class' => 'app\\modules\\comment\\CommentModule',\n            'db' => 'db',\n        ],\n    ],\n]\n```\n\n更多详情请参考 [模块](structure-modules.md) 一节。\n\n\n#### [[yii\\base\\Application::name|name]] <span id=\"name\"></span>\n\n该属性指定你可能想展示给终端用户的应用名称，\n不同于需要唯一性的 [[yii\\base\\Application::id|id]] 属性，\n该属性可以不唯一，该属性用于显示应用的用途。\n\n如果其他地方的代码没有用到，可以不配置该属性。\n\n\n#### [[yii\\base\\Application::params|params]] <span id=\"params\"></span>\n\n该属性为一个数组，指定可以全局访问的参数，\n代替程序中硬编码的数字和字符，\n应用中的参数定义到一个单独的文件并随时可以访问是一个好习惯。\n例如用参数定义缩略图的长宽如下：\n\n```php\n[\n    'params' => [\n        'thumbnail.size' => [128, 128],\n    ],\n]\n```\n\n然后简单的使用如下代码即可获取到你需要的长宽参数：\n\n```php\n$size = \\Yii::$app->params['thumbnail.size'];\n$width = \\Yii::$app->params['thumbnail.size'][0];\n```\n\n以后想修改缩略图长宽，\n只需要修改该参数而不需要相关的代码。\n\n\n#### [[yii\\base\\Application::sourceLanguage|sourceLanguage]] <span id=\"sourceLanguage\"></span>\n\n该属性指定应用代码的语言，默认为 `'en-US'` 标识英文（美国），\n如果应用不是英文请修改该属性。\n\n和 [语言](#language) 属性类似，配置该属性需遵循 \n[IETF language tag](https://zh.wikipedia.org/wiki/IETF%E8%AA%9E%E8%A8%80%E6%A8%99%E7%B1%A4).\n例如 `en` 代表英文， `en-US` 代表英文(美国)。\n\n该属性的更多信息可参考 [国际化](tutorial-i18n.md) 一节.\n\n\n#### [[yii\\base\\Application::timeZone|timeZone]] <span id=\"timeZone\"></span>\n\n该属性提供一种方式修改 PHP 运行环境中的默认时区，配置该属性本质上就是调用 PHP 函数\n[date_default_timezone_set()](https://www.php.net/manual/zh/function.date-default-timezone-set.php)，\n例如：\n\n```php\n[\n    'timeZone' => 'America/Los_Angeles',\n]\n```\n\n有关设置时区含义的更多详细信息，请查看[关于日期格式的部分](output-formatting.md#time-zones)。\n\n#### [[yii\\base\\Application::version|version]] <span id=\"version\"></span>\n\n该属性指定应用的版本，默认为 `'1.0'`，\n其他代码不使用的话可以不配置。\n\n\n### 实用属性 <span id=\"useful-properties\"></span>\n\n本小节描述的属性不经常设置，通常使用系统默认值。\n如果你想改变默认值，可以配置这些属性。\n\n\n#### [[yii\\base\\Application::charset|charset]] <span id=\"charset\"></span>\n\n该属性指定应用使用的字符集，默认值为 `'UTF-8'`，\n绝大部分应用都在使用，除非已有的系统大量使用非unicode数据才需要更改该属性。\n\n\n#### [[yii\\base\\Application::defaultRoute|defaultRoute]] <span id=\"defaultRoute\"></span>\n\n该属性指定未配置的请求的响应 [路由](runtime-routing.md) 规则，\n路由规则可能包含模块 ID，控制器 ID，动作 ID。\n例如 `help`，`post/create`，`admin/post/create`，如果动作 ID 没有指定，\n会使用 [[yii\\base\\Controller::defaultAction]] 中指定的默认值。\n\n对于 [[yii\\web\\Application|Web applications]] 网页应用，\n默认值为 `'site'` 对应 `SiteController` 控制器，并使用默认的动作。\n因此你不带路由的访问应用，默认会显示 `app\\controllers\\SiteController::actionIndex()` 的结果。\n\n对于 [[yii\\console\\Application|console applications]] 控制台应用，\n默认值为 `'help'` 对应 [[yii\\console\\controllers\\HelpController::actionIndex()]]。\n因此，如果执行的命令不带参数，默认会显示帮助信息。\n\n\n#### [[yii\\base\\Application::extensions|extensions]] <span id=\"extensions\"></span>\n\n该属性用数组列表指定应用安装和使用的 [扩展](structure-extensions.md)，\n默认使用 `@vendor/yiisoft/extensions.php` 文件返回的数组。\n当你使用 [Composer](https://getcomposer.org) 安装扩展，`extensions.php` 会被自动生成和维护更新。\n所以大多数情况下，不需要配置该属性。\n\n特殊情况下你想自己手动维护扩展，可以参照如下配置该属性：\n\n```php\n[\n    'extensions' => [\n        [\n            'name' => 'extension name',\n            'version' => 'version number',\n            'bootstrap' => 'BootstrapClassName',  // 可选配，可为配置数组\n            'alias' => [  // 可选配\n                '@alias1' => 'to/path1',\n                '@alias2' => 'to/path2',\n            ],\n        ],\n\n        // ... 更多像上面的扩展 ...\n\n    ],\n]\n```\n\n如上所示，该属性包含一个扩展定义数组，每个扩展为一个包含 `name` 和 `version` 项的数组。\n如果扩展要在 [引导启动](runtime-bootstrapping.md) 阶段运行，\n需要配置 `bootstrap` 以及对应的引导启动类名或 [configuration](concept-configurations.md) 数组。\n扩展也可以定义 [别名](concept-aliases.md)\n\n\n#### [[yii\\base\\Application::layout|layout]] <span id=\"layout\"></span>\n\n该属性指定渲染 [视图](structure-views.md) 默认使用的布局名字，\n默认值为 `'main'` 对应[布局路径](#layoutPath)下的 `main.php` 文件，\n如果 [布局路径](#layoutPath) 和 [视图路径](#viewPath) 都是默认值，\n默认布局文件可以使用路径别名 `@app/views/layouts/main.php`\n\n如果不想设置默认布局文件，可以设置该属性为 `false`，这种做法比较罕见。\n\n\n#### [[yii\\base\\Application::layoutPath|layoutPath]] <span id=\"layoutPath\"></span>\n\n该属性指定查找布局文件的路径，默认值为 [视图路径](#viewPath) 下的 `layouts` 子目录。\n如果 [视图路径](#viewPath) 使用默认值，\n默认的布局路径别名为`@app/views/layouts`。\n\n该属性需要配置成一个目录或 路径 [别名](concept-aliases.md)。\n\n\n#### [[yii\\base\\Application::runtimePath|runtimePath]] <span id=\"runtimePath\"></span>\n\n该属性指定临时文件如日志文件、缓存文件等保存路径，\n默认值为带别名的 `@app/runtime`。\n\n可以配置该属性为一个目录或者路径 [别名](concept-aliases.md)，\n注意应用运行时有对该路径的写入权限，\n以及终端用户不能访问该路径因为临时文件可能包含一些敏感信息。\n\n为了简化访问该路径，Yii 预定义别名 `@runtime` 代表该路径。\n\n\n#### [[yii\\base\\Application::viewPath|viewPath]] <span id=\"viewPath\"></span>\n\n该路径指定视图文件的根目录，默认值为带别名的 `@app/views`，\n可以配置它为一个目录或者路径 [别名](concept-aliases.md).\n\n\n#### [[yii\\base\\Application::vendorPath|vendorPath]] <span id=\"vendorPath\"></span>\n\n该属性指定 [Composer](https://getcomposer.org) 管理的供应商路径，\n该路径包含应用使用的包括Yii框架在内的所有第三方库。\n默认值为带别名的 `@app/vendor` 。\n\n可以配置它为一个目录或者路径 [别名](concept-aliases.md)，\n当你修改时，务必修改对应的 Composer 配置。\n\n为了简化访问该路径，Yii 预定义别名 `@vendor` 代表该路径。\n\n\n#### [[yii\\console\\Application::enableCoreCommands|enableCoreCommands]] <span id=\"enableCoreCommands\"></span>\n\n该属性仅 [[yii\\console\\Application|console applications]] 控制台应用支持， \n用来指定是否启用 Yii 中的核心命令，默认值为 `true`。\n\n\n## 应用事件 <span id=\"application-events\"></span>\n\n应用在处理请求过程中会触发事件，可以在配置文件配置事件处理代码，\n如下所示：\n\n```php\n[\n    'on beforeRequest' => function ($event) {\n        // ...\n    },\n]\n```\n\n`on eventName` 语法的用法在 [Configurations](concept-configurations.md#configuration-format) \n一节有详细描述.\n\n另外，在应用主体实例化后，你可以在 [引导启动](runtime-bootstrapping.md) 阶段附加事件处理代码，\n例如：\n\n```php\n\\Yii::$app->on(\\yii\\base\\Application::EVENT_BEFORE_REQUEST, function ($event) {\n    // ...\n});\n```\n\n### [[yii\\base\\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_REQUEST]] <span id=\"beforeRequest\"></span>\n\n该事件在应用处理请求 *before* 之前，实际的事件名为 `beforeRequest`。\n\n在事件触发前，应用主体已经实例化并配置好了，\n所以通过事件机制将你的代码嵌入到请求处理过程中非常不错。\n例如在事件处理中根据某些参数动态设置 [[yii\\base\\Application::language]] 语言属性。\n\n\n### [[yii\\base\\Application::EVENT_AFTER_REQUEST|EVENT_AFTER_REQUEST]] <span id=\"afterRequest\"></span>\n\n该事件在应用处理请求 *after* 之后但在返回响应 *before* 之前触发，\n实际的事件名为 `afterRequest`。\n\n该事件触发时，请求已经被处理完，\n可以做一些请求后处理或自定义响应。\n\n注意 [[yii\\web\\Response|response]] 组件在发送响应给终端用户时也会触发一些事件，\n这些事件都在本事件 *after* 之后触发。\n\n\n### [[yii\\base\\Application::EVENT_BEFORE_ACTION|EVENT_BEFORE_ACTION]] <span id=\"beforeAction\"></span>\n\n该事件在每个 [控制器动作](structure-controllers.md) 运行*before*之前会被触发，\n实际的事件名为 `beforeAction`.\n\n事件的参数为一个 [[yii\\base\\ActionEvent]] 实例，\n事件处理中可以设置[[yii\\base\\ActionEvent::isValid]] 为 `false` 停止运行后续动作，\n例如：\n\n```php\n[\n    'on beforeAction' => function ($event) {\n        if (some condition) {\n            $event->isValid = false;\n        } else {\n        }\n    },\n]\n```\n\n注意 [模块](structure-modules.md) 和 [控制器](structure-controllers.md) 都会触发 `beforeAction` 事件。\n应用主体对象首先触发该事件，然后模块触发（如果存在模块），最后控制器触发。\n任何一个事件处理中设置 [[yii\\base\\ActionEvent::isValid]] \n设置为 `false` 会停止触发后面的事件。\n\n\n### [[yii\\base\\Application::EVENT_AFTER_ACTION|EVENT_AFTER_ACTION]] <span id=\"afterAction\"></span>\n\n该事件在每个 [控制器动作](structure-controllers.md) 运行 *after* 之后会被触发，\n实际的事件名为 `afterAction`。\n\n该事件的参数为 [[yii\\base\\ActionEvent]] 实例，\n通过 [[yii\\base\\ActionEvent::result]] 属性，\n事件处理可以访问和修改动作的结果。例如：\n\n```php\n[\n    'on afterAction' => function ($event) {\n        if (some condition) {\n            // 修改 $event->result\n        } else {\n        }\n    },\n]\n```\n\n注意 [模块](structure-modules.md) 和 \n[控制器](structure-controllers.md) 都会触发 `afterAction` 事件。\n这些对象的触发顺序和 `beforeAction` 相反，也就是说，\n控制器最先触发，然后是模块（如果有模块），最后为应用主体。\n\n\n## 应用主体生命周期 <span id=\"application-lifecycle\"></span>\n\n![Application Lifecycle](images/application-lifecycle.png)\n\n当运行 [入口脚本](structure-entry-scripts.md) 处理请求时，\n应用主体会经历以下生命周期:\n\n1. 入口脚本加载应用主体配置数组。\n2. 入口脚本创建一个应用主体实例：\n  * 调用 [[yii\\base\\Application::preInit()|preInit()]] 配置几个高级别应用主体属性，\n    比如 [[yii\\base\\Application::basePath|basePath]]。\n  * 注册 [[yii\\base\\Application::errorHandler|error handler]] 错误处理方法。\n  * 配置应用主体属性。\n  * 调用 [[yii\\base\\Application::init()|init()]] 初始化，该函数会调用 [[yii\\base\\Application::bootstrap()|bootstrap()]] \n    运行引导启动组件。\n3. 入口脚本调用 [[yii\\base\\Application::run()]] 运行应用主体:\n  * 触发 [[yii\\base\\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_REQUEST]] 事件。\n  * 处理请求：解析请求 [路由](runtime-routing.md) 和相关参数；\n    创建路由指定的模块、控制器和动作对应的类，并运行动作。\n  * 触发 [[yii\\base\\Application::EVENT_AFTER_REQUEST|EVENT_AFTER_REQUEST]] 事件。\n  * 发送响应到终端用户。\n4. 入口脚本接收应用主体传来的退出状态并完成请求的处理。\n"
  },
  {
    "path": "docs/guide-zh-CN/structure-assets.md",
    "content": "资源\n======\n\nYii 中的资源是和 Web 页面相关的文件，可为 CSS 文件，JavaScript 文件，图片或视频等，\n资源放在 Web 可访问的目录下，直接被 Web 服务器调用。\n\n通过程序自动管理资源更好一点，例如，当你在页面中使用 [[yii\\jui\\DatePicker]] 小部件时，\n它会自动包含需要的 CSS 和 JavaScript 文件，\n而不是要求你手工去找到这些文件并包含，\n当你升级小部件时，它会自动使用新版本的资源文件，\n在本教程中，我们会详述 Yii 提供的强大的资源管理功能。\n\n\n## 资源包 <span id=\"asset-bundles\"></span>\n\nYii 在*资源包*中管理资源，资源包简单的说就是放在一个目录下的资源集合，\n当在[视图](structure-views.md)中注册一个资源包，\n在渲染 Web 页面时会包含包中的 CSS 和 JavaScript 文件。\n\n\n## 定义资源包 <span id=\"defining-asset-bundles\"></span>\n\n资源包指定为继承 [[yii\\web\\AssetBundle]] 的 PHP 类，\n包名为可[自动加载](concept-autoloading.md)的 PHP 类名，\n在资源包类中，要指定资源所在位置，\n包含哪些 CSS 和 JavaScript 文件以及和其他包的依赖关系。\n\n如下代码定义[基础应用模板](start-installation.md)使用的主要资源包：\n\n```php\n<?php\n\nnamespace app\\assets;\n\nuse yii\\web\\AssetBundle;\n\nclass AppAsset extends AssetBundle\n{\n    public $basePath = '@webroot';\n    public $baseUrl = '@web';\n    public $css = [\n        'css/site.css',\n        ['css/print.css', 'media' => 'print'],\n    ];\n    public $js = [\n    ];\n    public $depends = [\n        'yii\\web\\YiiAsset',\n        'yii\\bootstrap\\BootstrapAsset',\n    ];\n}\n```\n\n如上 `AppAsset` 类指定资源文件放在 `@webroot` 目录下，对应的 URL 为\n`@web`，资源包中包含一个 CSS 文件 `css/site.css`，没有 JavaScript 文件，\n依赖其他两个包 [[yii\\web\\YiiAsset]] 和 [[yii\\bootstrap\\BootstrapAsset]]，\n关于 [[yii\\web\\AssetBundle]] 的属性的更多详细如下所述：\n\n* [[yii\\web\\AssetBundle::sourcePath|sourcePath]]：指定包包含资源文件的根目录，\n  当根目录不能被 Web 访问时该属性应设置，否则，应设置\n  [[yii\\web\\AssetBundle::basePath|basePath]] 属性和 [[yii\\web\\AssetBundle::baseUrl|baseUrl]]。\n  [路径别名](concept-aliases.md) 可在此处使用；\n* [[yii\\web\\AssetBundle::basePath|basePath]]：指定包含资源包中资源文件并可Web访问的目录，\n  当指定 [[yii\\web\\AssetBundle::sourcePath|sourcePath]] 属性，\n  [资源管理器](#asset-manager) 会发布包的资源到一个可 Web 访问并覆盖该属性，\n  如果你的资源文件在一个 Web 可访问目录下，应设置该属性，这样就不用再发布了。\n  [路径别名](concept-aliases.md) 可在此处使用。\n* [[yii\\web\\AssetBundle::baseUrl|baseUrl]]：指定对应到 [[yii\\web\\AssetBundle::basePath|basePath]] 目录的 URL，\n  和 [[yii\\web\\AssetBundle::basePath|basePath]] 类似，\n  如果你指定 [[yii\\web\\AssetBundle::sourcePath|sourcePath]] 属性，\n  [资源管理器](#asset-manager) 会发布这些资源并覆盖该属性，[路径别名](concept-aliases.md) 可在此处使用。\n* [[yii\\web\\AssetBundle::css|css]]：列出此包中包含的 CSS 文件的数组。\n  请注意，只应使用正斜杠“/”作为目录分隔符。每个文件都可以单独指定为字符串，\n  也可以与属性标记及其值一起指定在数组中。\n* [[yii\\web\\AssetBundle::js|js]]：列出此包中包含的 JavaScript 文件的数组。\n  请注意，只应使用正斜杠“/”作为目录分隔符。\n  每个 JavaScript 文件可指定为以下两种格式之一：\n  - 相对路径表示为本地 JavaScript 文件 (如 `js/main.js`)，文件实际的路径在该相对路径前加上\n    [[yii\\web\\AssetManager::basePath]]，文件实际的 URL\n    在该路径前加上 [[yii\\web\\AssetManager::baseUrl]]。\n  - 绝对 URL 地址表示为外部 JavaScript 文件，如\n    `https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js` 或 \n    `//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js`。\n* [[yii\\web\\AssetBundle::depends|depends]]：一个列出该资源包依赖的\n  其他资源包（后两节有详细介绍）。\n* [[yii\\web\\AssetBundle::jsOptions|jsOptions]]：当调用 [[yii\\web\\View::registerJsFile()]] 注册该包 *每个* JavaScript 文件时，\n  指定传递到该方法的选项。\n* [[yii\\web\\AssetBundle::cssOptions|cssOptions]]：当调用 [[yii\\web\\View::registerCssFile()]] 注册该包 *每个* CSS 文件时，\n  指定传递到该方法的选项。\n* [[yii\\web\\AssetBundle::publishOptions|publishOptions]]：当调用 [[yii\\web\\AssetManager::publish()]] 发布该包资源文件到 Web 目录时\n  指定传递到该方法的选项，仅在指定了\n  [[yii\\web\\AssetBundle::sourcePath|sourcePath]] 属性时使用。\n\n\n### 资源位置 <span id=\"asset-locations\"></span>\n\n资源根据它们的位置可以分为：\n\n* 源资源: 资源文件和 PHP 源代码放在一起，不能被 Web 直接访问，为了使用这些源资源，\n  它们要拷贝到一个可 Web 访问的 Web 目录中\n  成为发布的资源，这个过程称为*发布资源*，随后会详细介绍。\n* 发布资源: 资源文件放在可通过 Web 直接访问的 Web 目录中；\n* 外部资源: 资源文件放在与你的 Web 应用不同的\n  Web 服务器上；\n\n当定义资源包类时候，如果你指定了[[yii\\web\\AssetBundle::sourcePath|sourcePath]] 属性，\n就表示任何使用相对路径的资源会被当作源资源；\n如果没有指定该属性，就表示这些资源为发布资源（因此应指定[[yii\\web\\AssetBundle::basePath|basePath]] 和\n[[yii\\web\\AssetBundle::baseUrl|baseUrl]] 让 Yii 知道它们的位置）。\n\n推荐将资源文件放到 Web 目录以避免不必要的发布资源过程，这就是之前的例子：指定\n[[yii\\web\\AssetBundle::basePath|basePath]] \n而不是 [[yii\\web\\AssetBundle::sourcePath|sourcePath]].\n\n对于[扩展](structure-extensions.md)来说，\n由于它们的资源和源代码都在不能Web访问的目录下，\n在定义资源包类时必须指定[[yii\\web\\AssetBundle::sourcePath|sourcePath]]属性。\n\n> Note: [[yii\\web\\AssetBundle::sourcePath|source path]] 属性不要用 `@webroot/assets`，该路径默认为\n  [[yii\\web\\AssetManager|asset manager]] 资源管理器将源资源发布后存储资源的路径，\n  该路径的所有内容会认为是临时文件，\n  可能会被删除。\n\n\n### 资源依赖 <span id=\"asset-dependencies\"></span>\n\n当 Web 页面包含多个 CSS 或 JavaScript 文件时，\n它们有一定的先后顺序以避免属性覆盖，\n例如，Web 页面在使用 jQuery UI 小部件前必须确保 jQuery JavaScript 文件已经被包含了，\n我们称这种资源先后次序称为资源依赖。\n\n资源依赖主要通过 [[yii\\web\\AssetBundle::depends]] 属性来指定，\n在 `AppAsset` 示例中，资源包依赖其他两个资源包： \n[[yii\\web\\YiiAsset]] 和 [[yii\\bootstrap\\BootstrapAsset]]\n也就是该资源包的 CSS 和 JavaScript 文件要在这两个依赖包的文件包含*之后*才包含。\n\n资源依赖关系是可传递，也就是说 A 依赖 B，B 依赖 C，那么 A 也依赖 C。\n\n\n### 资源选项 <span id=\"asset-options\"></span>\n\n可指定 [[yii\\web\\AssetBundle::cssOptions|cssOptions]] 和 [[yii\\web\\AssetBundle::jsOptions|jsOptions]]\n属性来自定义页面包含 CSS 和 JavaScript 文件的方式，\n这些属性值会分别传递给 [[yii\\web\\View::registerCssFile()]] 和 [[yii\\web\\View::registerJsFile()]] 方法，\n在[视图](structure-views.md)调用这些方法包含 CSS 和 JavaScript 文件时。\n\n> Note: 在资源包类中设置的选项会应用到该包中 *每个* CSS/JavaScript 文件，\n  如果想对每个文件使用不同的选项，\n  应创建不同的资源包并在每个包中使用一个选项集。\n\n例如，只想 IE9 或更高的浏览器包含一个 CSS 文件，可以使用如下选项：\n\n```php\npublic $cssOptions = ['condition' => 'lte IE9'];\n```\n\n这会使包中的 CSS 文件使用以下 HTML 标签包含进来：\n\n```html\n<!--[if lte IE9]>\n<link rel=\"stylesheet\" href=\"path/to/foo.css\">\n<![endif]-->\n```\n\n为链接标签包含 `<noscript>` 可使用如下代码：\n\n```php\npublic $cssOptions = ['noscript' => true];\n```\n\n为使 JavaScript 文件包含在页面 head 区域（JavaScript 文件默认包含在 body 的结束处）\n使用以下选项：\n\n```php\npublic $jsOptions = ['position' => \\yii\\web\\View::POS_HEAD];\n```\n\n默认情况下，当发布资源包时，所有在 [[yii\\web\\AssetBundle::sourcePath]] 目录里的内容都会发布。\n你可以通过配置 [[yii\\web\\AssetBundle::publishOptions|publishOptions]] 属性来自定义这种行为。\n比如，为了只发布 [[yii\\web\\AssetBundle::sourcePath]] 其中的某些内容或子目录里的内容，\n可以在资源类中试试下面的做法：\n\n```php\n<?php\nnamespace app\\assets;\n\nuse yii\\web\\AssetBundle;\n\nclass FontAwesomeAsset extends AssetBundle \n{\n    public $sourcePath = '@bower/font-awesome'; \n    public $css = [ \n        'css/font-awesome.min.css', \n    ];\n    public $publishOptions = [\n        'only' => [\n            'fonts/',\n            'css/',\n        ]\n    ];\n}  \n```\n\n上述的代码为 [\"fontawesome\" package](https://fontawesome.com/) 定义了资源包。\n通过配置发布选项的 only 下标，只有 `fonts` 和 `css` 子目录会发布。\n\n\n### Bower 和 NPM 资源安装 <span id=\"bower-npm-assets\"></span>\n\n大多数 JavaScript/CSS 包使用 [Bower](https://bower.io/) 或 [NPM](https://www.npmjs.com/) 来管理。\n在 PHP 中，我们用 Composer 来管理 PHP 依赖。像 PHP 包一样，\n也可以使用 `composer.json` 管理 Bower 和 NPM 包。\n\n要实现这一点，需要配置一下 Composer 。有两种方法：\n\n___\n\n#### 使用 asset-packagist 库\n\n这种方式将满足大多数需要使用 NPM 或 Bower 包项目的要求。\n\n> Note: 从 2.0.13 开始，基本和高级应用程序模板都默认配置使用 asset-packagist ，\n  因此你可以跳过本节。\n\n在你项目 `composer.json` 文件中，添加下面几行代码：\n\n```json\n\"repositories\": [\n    {\n        \"type\": \"composer\",\n        \"url\": \"https://asset-packagist.org\"\n    }\n]\n```\n\n在你的 [配置数组](concept-configurations.md) 中设置 `@npm` 和 `@bower` 的 [别名](concept-aliases.md)：\n\n```php\n$config = [\n    ...\n    'aliases' => [\n        '@bower' => '@vendor/bower-asset',\n        '@npm'   => '@vendor/npm-asset',\n    ],\n    ...\n];\n```\n\n你可以访问 [asset-packagist.org](https://asset-packagist.org) 来了解它是如何工作的。\n\n#### 使用 fxp/composer-asset-plugin\n\n与 asset-packagist 相比，composer-asset-plugin 不需要对应用程序配置进行任何更改。 \n而是需要运行以下命令来全局安装一个特殊的 Composer 插件：\n\n```bash\ncomposer global require \"fxp/composer-asset-plugin:^1.4.1\"\n```\n\n这个命令会全局安装 [composer asset plugin](https://github.com/fxpio/composer-asset-plugin) 插件，\n以便使用 Composer 来管理对 Bower 和 NPM 包的依赖。 在这个插件安装后，\n你计算机上的每个项目都可以通过 `composer.json` 来管理 Bower 和 NPM 包。\n\n如果你想要通过 Yii 来发布它们，将以下行添加到项目的 `composer.json` 文件中，\n来调整 Bower 和 NPM 安装包的放置目录：\n\n```json\n\"config\": {\n    \"asset-installer-paths\": {\n        \"npm-asset-library\": \"vendor/npm\",\n        \"bower-asset-library\": \"vendor/bower\"\n    }\n}\n```\n\n> Note: 与 asset-packagist 相比，使用 `fxp/composer-asset-plugin` 的方式，\n  会显著减慢 `composer update` 命令的速度。\n \n____\n \n在配置好 Composer 支持使用 Bower 和 NPM 之后：\n\n1. 编辑项目或扩展中的 `composer.json` 文件，在该文件的 `require` 字段中列举出相关包。 \n   你应该使用 `bower-asset/PackageName`（对于 Bower 包）或者\n   `npm-asset/PackageName` （对于 NPM 包）的方式来引入这些包。\n2. 运行 `composer update`\n3. 创建资源类并列出在应用程序或扩展中所需使用的 JavaScript/CSS 文件。\n   你还需要配置 [[yii\\web\\AssetBundle::sourcePath|sourcePath]] 属性为 `@bower/PackageName` 或 `@npm/PackageName`。\n   这是因为 Composer 会将 Bower 或 NPM 软件包安装在与此别名对应的目录中。\n\n> Note: 某些包可能会将其所有发布文件放在子目录中。在这种情况下，\n  你应该把子目录设为 [[yii\\web\\AssetBundle::sourcePath|sourcePath]] 的值。例如，\n  在 [[yii\\web\\JqueryAsset]] 这个资源包中，使用 `@bower/jquery/dist` 而不是 `@bower/jquery`。\n\n\n## 使用资源包 <span id=\"using-asset-bundles\"></span>\n\n为使用资源包，先在[视图](structure-views.md)中调用 [[yii\\web\\AssetBundle::register()]] 方法注册资源，\n例如，在视图模板可使用如下代码注册资源包：\n\n```php\nuse app\\assets\\AppAsset;\nAppAsset::register($this);  // $this 代表视图对象\n```\n\n> Info: [[yii\\web\\AssetBundle::register()]] 方法返回资源包对象，该对象包含了发布资源的信息比如 \n[[yii\\web\\AssetBundle::basePath|basePath]] 或 [[yii\\web\\AssetBundle::baseUrl|baseUrl]]。\n\n如果在其他地方注册资源包，应提供视图对象，如在 [小部件](structure-widgets.md) 类中注册资源包，\n可以通过 `$this->view` 获取视图对象。\n\n当在视图中注册一个资源包时，在背后 Yii 会注册它所依赖的资源包，\n如果资源包是放在 Web 不可访问的目录下，会被发布到可访问的目录，\n后续当视图渲染页面时，\n会生成这些注册包包含的 CSS 和 JavaScript 文件对应的 `<link>` 和 `<script>` 标签，\n这些标签的先后顺序取决于资源包的依赖关系以及在\n[[yii\\web\\AssetBundle::css]] 和 [[yii\\web\\AssetBundle::js]] 的列出来的前后顺序。\n\n\n### 动态资源包 <span id=\"dynamic-asset-bundles\"></span>\n\n作为常规 PHP 类，资源包可以承担一些与之相关的额外逻辑，并可以动态调整其内部参数。\n比如：你也许用到某些复杂的 JavaScript 库，它提供一些用于国际化的包，并分成单独的源文件：每个文件对应于国际化中所支持的语言。\n为了让这个 JavaScript 库正常工作，你需要把这些单独的语言源文件加载到页面中。\n这可以通过覆盖 [[yii\\web\\AssetBundle::init()]] 方法来实现：\n\n```php\nnamespace app\\assets;\n\nuse yii\\web\\AssetBundle;\nuse Yii;\n\nclass SophisticatedAssetBundle extends AssetBundle\n{\n    public $sourcePath = '/path/to/sophisticated/src';\n    public $js = [\n        'sophisticated.js' // file, which is always used\n    ];\n\n    public function init()\n    {\n        parent::init();\n        $this->js[] = 'i18n/' . Yii::$app->language . '.js'; // dynamic file added\n    }\n}\n```\n\n特定的资源包也可以通过 [[yii\\web\\AssetBundle::register()]] 返回的实例进行调整。\n例如：\n\n```php\nuse app\\assets\\SophisticatedAssetBundle;\nuse Yii;\n\n$bundle = SophisticatedAssetBundle::register(Yii::$app->view);\n$bundle->js[] = 'i18n/' . Yii::$app->language . '.js'; // dynamic file added\n```\n\n> Note: 虽然支持资源包的动态调整，但这是一种**不好**的做法，\n  可能导致意想不到的副作用，应尽可能避免。\n\n\n### 自定义资源包 <span id=\"customizing-asset-bundles\"></span>\n\nYii 通过配置名为 `assetManager` 的应用组件来使用 [[yii\\web\\AssetManager]]，\n通过配置 [[yii\\web\\AssetManager::bundles]] 属性，可以自定义资源包的行为，\n例如，[[yii\\web\\JqueryAsset]] 资源包默认从 jquery Bower 包中使用 `jquery.js` 文件，\n为了提高可用性和性能，你可能需要从 CDN 服务器上获取 jquery 文件，\n可以在应用配置中配置 `assetManager`，如下所示：\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'bundles' => [\n                'yii\\web\\JqueryAsset' => [\n                    'sourcePath' => null,   // 一定不要发布该资源\n                    'js' => [\n                        '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js',\n                    ]\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n可通过类似 [[yii\\web\\AssetManager::bundles]] 配置多个资源包，\n数组的键应为资源包的类名（最开头不要反斜杠），\n数组的值为对应的[配置数组](concept-configurations.md).\n\n> Tip: 可以根据条件判断使用哪个资源，如下示例为如何在开发环境用 `jquery.js`，\n> 否则用`jquery.min.js`：\n>\n> ```php\n> 'yii\\web\\JqueryAsset' => [\n>     'js' => [\n>         YII_ENV_DEV ? 'jquery.js' : 'jquery.min.js'\n>     ]\n> ],\n> ```\n\n可以将资源包名称设置为 `false` 来禁用一个或多个资源包，\n当视图中注册一个禁用资源包，\n视图不会包含任何该包的资源以及不会注册它所依赖的包，\n例如，为禁用 [[yii\\web\\JqueryAsset]]，可以使用如下配置：\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'bundles' => [\n                'yii\\web\\JqueryAsset' => false,\n            ],\n        ],\n    ],\n];\n```\n\n可以设置 [[yii\\web\\AssetManager::bundles]] 为 `false` 来禁用 *所有* 的资源包。\n\n需要记住，使用 [[yii\\web\\AssetManager::bundles]] 进行自定义时，只在创建资源包时起作用，例如，在对象的构造函数阶段。\n这意味着在此之后对该资源包对象所做的任何调整都将覆盖在配置数组中 [[yii\\web\\AssetManager::bundles]] 的配置。\n特别是：在 [[yii\\web\\AssetBundle::init()]]\n方法内或在注册的资源包对象上进行的调整优先于 `AssetManager` 配置。\n下面给个例子来说明在配置数组中对 [[yii\\web\\AssetManager::bundles]] 的配置是不起作用的：\n\n```php\n// Program source code:\n\nnamespace app\\assets;\n\nuse yii\\web\\AssetBundle;\nuse Yii;\n\nclass LanguageAssetBundle extends AssetBundle\n{\n    // ...\n\n    public function init()\n    {\n        parent::init();\n        $this->baseUrl = '@web/i18n/' . Yii::$app->language; // 将不能通过 `AssetManager` 来管理！\n    }\n}\n// ...\n\n$bundle = \\app\\assets\\LargeFileAssetBundle::register(Yii::$app->view);\n$bundle->baseUrl = YII_DEBUG ? '@web/large-files': '@web/large-files/minified'; // 将不能通过 `AssetManager` 来管理！\n\n\n// Application config :\n\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'bundles' => [\n                'app\\assets\\LanguageAssetBundle' => [\n                    'baseUrl' => 'https://some.cdn.com/files/i18n/en' // 这行代码不起作用！\n                ],\n                'app\\assets\\LargeFileAssetBundle' => [\n                    'baseUrl' => 'https://some.cdn.com/files/large-files' // 这行代码不起作用！\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n\n### 资源映射 <span id=\"asset-mapping\"></span>\n\n有时你想“修复”多个资源包中资源文件的错误或者不兼容，例如包 A 使用 1.11.1 版本的 `jquery.min.js`，\n包 B 使用 2.1.1 版本的 `jquery.js`，可自定义每个包来解决这个问题，\n但更好的方式是使用*资源部署*的特性来把不正确的资源部署为想要的，\n为此，配置 [[yii\\web\\AssetManager::assetMap]] 属性，如下所示：\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'assetMap' => [\n                'jquery.js' => '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js',\n            ],\n        ],\n    ],\n];\n```\n\n[[yii\\web\\AssetManager::assetMap|assetMap]] 的键为你想要修复的资源名，值为你想要使用的资源路径，\n当视图注册资源包，在 [[yii\\web\\AssetBundle::css|css]] 和\n[[yii\\web\\AssetBundle::js|js]] 数组中每个相关资源文件会和该部署进行对比，\n如果数组任何键对比为资源文件的最后文件名\n（如果有的话前缀为 [[yii\\web\\AssetBundle::sourcePath]]），对应的值为替换原来的资源。\n例如，资源文件 `my/path/to/jquery.js` 匹配键 `jquery.js`.\n\n> Note: 只有相对路径指定的资源对应到资源部署，替换的资源路径可以为绝对路径，\n  也可为和 [[yii\\web\\AssetManager::basePath]] 相关的路径。\n\n\n### 资源发布 <span id=\"asset-publishing\"></span>\n\n如前所述，如果资源包放在 Web 不能访问的目录，\n当视图注册资源时资源会被拷贝到一个 Web 可访问的目录中，\n这个过程称为*资源发布*，[[yii\\web\\AssetManager|asset manager]] 会自动处理该过程。\n\n资源默认会发布到 `@webroot/assets` 目录，对应的 URL 为 `@web/assets`，\n可配置 [[yii\\web\\AssetManager::basePath|basePath]] 和 \n[[yii\\web\\AssetManager::baseUrl|baseUrl]] 属性自定义发布位置。\n\n除了拷贝文件方式发布资源，如果操作系统和 Web 服务器允许可以使用符号链接，该功能可以通过设置\n[[yii\\web\\AssetManager::linkAssets|linkAssets]] 为 `true` 来启用。\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'linkAssets' => true,\n        ],\n    ],\n];\n```\n\n使用以上配置，\n资源管理器会创建一个符号链接到要发布的资源包源路径，\n这比拷贝文件方式快并能确保发布的资源一直为最新的。\n\n\n### 清除缓存 <span id=\"cache-busting\"></span>\n\n对于运行在生产模式的 Web 应用程序，通常会为资源包和其他静态资源开启 HTTP 缓存。\n但这种做法有个不好的地方就是，当你更新某个资源并部署到生产环境时，\n客户端可能由于 HTTP 缓存而仍然使用旧版本的资源。\n为了克服该不足，你可以试试清除缓存特性，它由 2.0.3 版本引入，只需如下配置 [[yii\\web\\AssetManager]] 即可：\n  \n```php\nreturn [\n    // ...\n    'components' => [\n        'assetManager' => [\n            'appendTimestamp' => true,\n        ],\n    ],\n];\n```\n\n通过上述配置后，每个已发布资源的 URL 都会附加一个最后更新时间戳的信息。\n比如，`yii.js` 的 URL 可能是 `/assets/5515a87c/yii.js?v=1423448645\"`，\n这里的参数 v 表示 `yii.js` 文件的最后更新时间戳。\n现在一旦你更新了某个资源，它的 URL 也会改变进而强制客户端获取该资源的最新版本。\n\n\n## 常用资源包 <span id=\"common-asset-bundles\"></span>\n\nYii框架定义许多资源包，如下资源包是最常用，\n可在你的应用或扩展代码中引用它们。\n\n- [[yii\\web\\YiiAsset]]：主要包含 `yii.js` 文件，该文件完成模块 JavaScript 代码组织功能，\n  也为 `data-method` 和 `data-confirm` 属性提供特别支持和其他有用的功能。\n  有关 `yii.js` 的更多信息可以在 [客户端脚本部分](output-client-scripts.md#yii.js) 中找到。\n- [[yii\\web\\JqueryAsset]]：包含 jQuery Bower 包的 `jquery.js` 文件。 \n- [[yii\\bootstrap\\BootstrapAsset]]：包含 Twitter Bootstrap 框架的 CSS 文件。\n- [[yii\\bootstrap\\BootstrapPluginAsset]]：包含 Twitter Bootstrap 框架的 JavaScript 文件\n  来支持 Bootstrap JavaScript 插件。\n- [[yii\\jui\\JuiAsset]]：包含 jQuery UI 库的 CSS 和 JavaScript 文件。\n\n如果你的代码需要 jQuery，jQuery UI 或 Bootstrap，应尽量使用这些预定义资源包而非自己创建，\n如果这些包的默认配置不能满足你的需求，可以自定义配置，\n详情参考[自定义资源包](#customizing-asset-bundles)。\n\n\n## 资源转换 <span id=\"asset-conversion\"></span>\n\n除了直接编写 CSS 或 JavaScript 代码，开发人员经常使用扩展语法来编写，再使用特殊的工具将它们转换成 CSS/JavaScript。\n例如，对于 CSS 代码可使用 [LESS](https://lesscss.org/) 或 [SCSS](https://sass-lang.com/)，\n对于 JavaScript 可使用 [TypeScript](https://www.typescriptlang.org/)。\n\n可将使用扩展语法的资源文件列到资源包的 [[yii\\web\\AssetBundle::css|css]] 和 [[yii\\web\\AssetBundle::js|js]] 中，如下所示：\n\n```php\nclass AppAsset extends AssetBundle\n{\n    public $basePath = '@webroot';\n    public $baseUrl = '@web';\n    public $css = [\n        'css/site.less',\n    ];\n    public $js = [\n        'js/site.ts',\n    ];\n    public $depends = [\n        'yii\\web\\YiiAsset',\n        'yii\\bootstrap\\BootstrapAsset',\n    ];\n}\n```\n\n当在视图中注册一个这样的资源包，[[yii\\web\\AssetManager|asset manager]] 资源管理器会自动运行预处理工具将使用扩展语法\n的资源转换成 CSS/JavaScript，当视图最终渲染页面时，\n在页面中包含的是 CSS/Javascipt 文件，\n而不是原始的扩展语法代码文件。\n\nYii 使用文件扩展名来表示资源使用哪种扩展语法，\n默认可以识别如下语法和文件扩展名：\n\n- [LESS](https://lesscss.org/)：`.less`\n- [SCSS](https://sass-lang.com/)：`.scss`\n- [Stylus](https://stylus-lang.com/)：`.styl`\n- [CoffeeScript](https://coffeescript.org/)：`.coffee`\n- [TypeScript](https://www.typescriptlang.org/)：`.ts`\n\nYii 依靠安装的预处理工具来转换资源，例如，\n为使用 [LESS](https://lesscss.org/)，应安装 `lessc` 预处理命令。\n\n可配置 [[yii\\web\\AssetManager::converter]] 自定义预处理命令和支持的扩展语法，\n如下所示：\n\n```php\nreturn [\n    'components' => [\n        'assetManager' => [\n            'converter' => [\n                'class' => 'yii\\web\\AssetConverter',\n                'commands' => [\n                    'less' => ['css', 'lessc {from} {to} --no-color'],\n                    'ts' => ['js', 'tsc --out {to} {from}'],\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n如上所示，通过 [[yii\\web\\AssetConverter::commands]] 属性指定支持的扩展语法，\n数组的键为文件扩展名（前面不需要这样的一个小点：`.`），\n数组的值为目标资源文件扩展名和执行资源转换的命令，\n命令中的标记 `{from}` 和 `{to}` 会分别被源资源文件路径和目标资源文件路径替代。\n\n> Info: 除了以上方式，也有其他的方式来处理扩展语法资源，\n  例如，可使用编译工具如[grunt](https://gruntjs.com/)\n  来监控并自动转换扩展语法资源，此时，\n  应使用资源包中编译后的CSS/JavaScript文件而不是原始文件。\n\n\n## 合并和压缩资源 <span id=\"combining-compressing-assets\"></span>\n\n一个 Web 页面可以包含很多 CSS 和 JavaScript 文件，为减少 HTTP 请求和这些下载文件的大小，\n通常的方式是在页面中合并并压缩多个 CSS/JavaScript 文件为一个或很少的几个文件，\n并使用压缩后的文件而不是原始文件。\n \n> Info: 合并和压缩资源通常应用在产品上线模式，\n  在开发模式下使用原始的 CSS/JavaScript 更方便调试。\n\n接下来介绍一种合并和压缩资源文件\n而不需要修改已有代码的方式：\n\n1. 找出应用中所有你想要合并和压缩的资源包，\n2. 将这些包分成一个或几个组，注意每个包只能属于其中一个组，\n3. 合并/压缩每个组里 CSS 文件到一个文件，同样方式处理 JavaScript 文件，\n4. 为每个组定义新的资源包：\n   * 设置 [[yii\\web\\AssetBundle::css|css]] 和 [[yii\\web\\AssetBundle::js|js]] \n     属性分别为压缩后的 CSS 和 JavaScript 文件；\n   * 自定义设置每个组内的资源包，设置资源包的 [[yii\\web\\AssetBundle::css|css]] \n     和 [[yii\\web\\AssetBundle::js|js]] 属性为空, \n     并设置它们的 [[yii\\web\\AssetBundle::depends|depends]] 属性为每个组新创建的资源包。\n\n使用这种方式，当在视图中注册资源包时，会自动触发原始包所属的组资源包的注册，\n然后，页面就会包含以合并/压缩的资源文件，\n而不是原始文件。\n\n\n### 示例 <span id=\"example\"></span>\n\n使用一个示例来解释以上这种方式：\n\n假定你的应用有两个页面 X 和 Y，页面 X 使用资源包 A，B 和 C，页面 Y 使用资源包 B，C 和 D。\n\n有两种方式划分这些资源包，一种使用一个组包含所有资源包，\n另一种是将（A，B，C）放在组 X，（B，C，D）放在组 Y，\n哪种方式更好？第一种方式优点是两个页面使用相同的已合并 CSS 和 JavaScript 文件，从而使 HTTP 缓存更高效，\n另一方面，由于单个组包含所有文件，\n已合并的 CSS 和 JavaScipt 文件会更大，因此会增加文件传输时间，在这个示例中，\n我们使用第一种方式，也就是用一个组包含所有包。\n\n> Info: 将资源包分组并不是无意义的，通常要求分析现实中不同页面各种资源的数据量，\n  开始时为简便使用一个组。\n\n在所有包中使用工具(例如 [Closure Compiler](https://developers.google.com/closure/compiler/)，\n[YUI Compressor](https://github.com/yui/yuicompressor/)) 来合并和压缩 CSS 和 JavaScript 文件，\n注意合并后的文件满足包间的先后依赖关系，\n例如，如果包 A 依赖 B，B 依赖 C 和 D，那么资源文件列表以 C 和 D 开始，\n然后为 B 最后为 A。\n\n合并和压缩之后，会得到一个 CSS 文件和一个 JavaScript 文件，\n假定它们的名称为 `all-xyz.css` 和 `all-xyz.js`，\n`xyz` 为使文件名唯一以避免HTTP缓存问题的时间戳或哈希值。\n \n现在到最后一步了，在应用配置中配置 [[yii\\web\\AssetManager|asset manager]] \n资源管理器如下所示：\n\n```php\nreturn [\n    'components' => [\n        'assetManager' => [\n            'bundles' => [\n                'all' => [\n                    'class' => 'yii\\web\\AssetBundle',\n                    'basePath' => '@webroot/assets',\n                    'baseUrl' => '@web/assets',\n                    'css' => ['all-xyz.css'],\n                    'js' => ['all-xyz.js'],\n                ],\n                'A' => ['css' => [], 'js' => [], 'depends' => ['all']],\n                'B' => ['css' => [], 'js' => [], 'depends' => ['all']],\n                'C' => ['css' => [], 'js' => [], 'depends' => ['all']],\n                'D' => ['css' => [], 'js' => [], 'depends' => ['all']],\n            ],\n        ],\n    ],\n];\n```\n\n如 [自定义资源包](#customizing-asset-bundles) 小节中所述，如上配置改变每个包的默认行为，\n特别是包 A、B、C 和 D 不再包含任何资源文件，\n都依赖包含合并后的 `all-xyz.css` 和 `all-xyz.js` 文件的包 `all`，\n因此，对于页面X会包含这两个合并后的文件而不是包 A、B、C 的原始文件，\n对于页面 Y 也是如此。\n\n最后有个方法更好地处理上述方式，除了直接修改应用配置文件，\n可将自定义包数组放到一个文件，在应用配置中根据条件包含该文件，\n例如：\n\n```php\nreturn [\n    'components' => [\n        'assetManager' => [\n            'bundles' => require __DIR__ . '/' . (YII_ENV_PROD ? 'assets-prod.php' : 'assets-dev.php'),  \n        ],\n    ],\n];\n```\n\n如上所示，在产品上线模式下资源包数组存储在 `assets-prod.php` 文件中，\n不是产品上线模式存储在 `assets-dev.php` 文件中。\n\n> Note: 这种资源合并的机制是基于 [[yii\\web\\AssetManager::bundles]] 能够覆盖已经注册的资源包。\n  但是，正如前面提到的，\n  并不能覆盖到使用 [[yii\\web\\AssetBundle::init()]] 方法或在资源包对象上进行调整的资源包。\n  你应该避免在资源合并时使用此类动态捆绑的资源包。\n\n\n### 使用 `asset` 命令 <span id=\"using-asset-command\"></span>\n\nYii 提供一个名为 `asset` 控制台命令来使上述操作自动处理。\n\n为使用该命令，应先创建一个配置文件设置哪些资源包要合并以及分组方式，\n可使用 `asset/template` 子命令来生成一个模板，\n然后修改成你想要的模板。\n\n```\nyii asset/template assets.php\n```\n\n该命令在当前目录下生成一个名为 `assets.php` 的文件，文件的内容类似如下：\n\n```php\n<?php\n/**\n * 为控制台命令\"yii asset\"使用的配置文件\n * 注意在控制台环境下，一些路径别名如 '@webroot' 和 '@web' 不会存在\n * 请定义不存在的路径别名\n */\nreturn [\n    // 为JavaScript文件压缩修改 command/callback \n    'jsCompressor' => 'java -jar compiler.jar --js {from} --js_output_file {to}',\n    // 为CSS文件压缩修改command/callback \n    'cssCompressor' => 'java -jar yuicompressor.jar --type css {from} -o {to}',\n    // 是否在压缩后删除资源来源：\n    'deleteSource' => false,\n    // 要压缩的资源包列表\n    'bundles' => [\n        // 'yii\\web\\YiiAsset',\n        // 'yii\\web\\JqueryAsset',\n    ],\n    // 资源包压缩后的输出\n    'targets' => [\n        'all' => [\n            'class' => 'yii\\web\\AssetBundle',\n            'basePath' => '@webroot/assets',\n            'baseUrl' => '@web/assets',\n            'js' => 'js/all-{hash}.js',\n            'css' => 'css/all-{hash}.css',\n        ],\n    ],\n    // 资源管理器配置:\n    'assetManager' => [\n    ],\n];\n```\n\n应修改该文件的 `bundles` 的选项指定哪些包你想要合并，\n在 `targets` 选项中应指定这些包如何分组，\n如前述的可以指定一个或多个组。\n\n> Note: 由于在控制台应用别名 `@webroot` 和 `@web` 不可用，\n  应在配置中明确指定它们。\n\nJavaScript 文件会被合并压缩后写入到 `js/all-{hash}.js` 文件，\n其中 {hash} 会被结果文件的哈希值替换。\n\n`jsCompressor` 和 `cssCompressor` 选项指定控制台命令或PHP回调函数来执行 JavaScript 和 CSS 合并和压缩，\nYii 默认使用 [Closure Compiler](https://developers.google.com/closure/compiler/) 来合并 JavaScript 文件， \n使用 [YUI Compressor](https://github.com/yui/yuicompressor/) 来合并 CSS 文件，\n你应手工安装这些工具或修改选项使用你喜欢的工具。\n\n\n根据配置文件，可执行 `asset` 命令来合并和压缩资源文件\n并生成一个新的资源包配置文件 `assets-prod.php`：\n \n```\nyii asset assets.php config/assets-prod.php\n```\n\n生成的配置文件可以在应用配置中包含，\n如最后一小节所描述的。\n\n> Note: 如果你使用 [[yii\\web\\AssetManager::bundles]] 或 [[yii\\web\\AssetManager::assetMap]]\n  来自定义应用程序的资源包，并希望将此自定义应用于压缩的源文件中，\n  你应该在 asset 命令配置文件中的 `assetManager` 部分包含这些自定义的内容。\n\n> Note: 在指定压缩源时，应避免使用那些根据参数动态调整的资源包（即在 `init()` 方法或注册后根据参数进行动态调整的包），\n  因为在压缩后它们可能无法正常工作。\n\n\n> Info: 使用 `asset` 命令并不是合并和压缩资源的唯一方法。\n  你也可以使用能够自动运行设定任务的项目构建工具 [grunt](https://gruntjs.com/) 来实现同样的目的。\n\n\n### 资源包分组 <span id=\"grouping-asset-bundles\"></span>\n\n上一小节，介绍了如何压缩所有的资源包到一个文件，\n减少对应用中引用资源文件的 HTTP 请求数，但是在实践中很少这样做。\n比如，应用有一个“前端”和一个“后端”，\n每一个都用了一个不同 JavaScript 和 CSS 文件集合。\n在这种情况下，把所有的资源包压缩到一个文件毫无意义，“前端”不会用到“后端”的资源文件，\n当请求“前端”页面时，“后端”的资源文件也会被发送过来，浪费网络带宽。\n\n为了解决这个问题，可以吧资源包分成若干组，每个组里面有若干个资源包。\n下面的配置展示了如何对资源包分组：\n\n```php\nreturn [\n    ...\n    // Specify output bundles with groups:\n    'targets' => [\n        'allShared' => [\n            'js' => 'js/all-shared-{hash}.js',\n            'css' => 'css/all-shared-{hash}.css',\n            'depends' => [\n                // 包含由'backend' 和 'frontend' 共享的资源包\n                'yii\\web\\YiiAsset',\n                'app\\assets\\SharedAsset',\n            ],\n        ],\n        'allBackEnd' => [\n            'js' => 'js/all-{hash}.js',\n            'css' => 'css/all-{hash}.css',\n            'depends' => [\n                // 只包含 'backend' 资源:\n                'app\\assets\\AdminAsset'\n            ],\n        ],\n        'allFrontEnd' => [\n            'js' => 'js/all-{hash}.js',\n            'css' => 'css/all-{hash}.css',\n            'depends' => [], // 包含所有的剩余资源\n        ],\n    ],\n    ...\n];\n```\n\n如上所示，资源包分成了三个组：`allShared`，`allBackEnd` 和 `allFrontEnd`\n它们每个都依赖各自的资源包集合。 比如， `allBackEnd` 依赖 `app\\assets\\AdminAsset`。\n当对该配置运行 `asset` 命令时，将会根据各自依赖合并资源包。\n\n> Info: 你也可以把某个分组的 `depends` 配置留空。 这样做得话，\n  这个分组将会依赖剩余的资源包，剩余资源包是指不被其他分组依赖的那些资源包。\n"
  },
  {
    "path": "docs/guide-zh-CN/structure-controllers.md",
    "content": "控制器\n===========\n\n控制器是 [MVC](https://zh.wikipedia.org/wiki/MVC) 模式中的一部分，\n是继承[[yii\\base\\Controller]]类的对象，负责处理请求和生成响应。\n具体来说，控制器从[应用主体](structure-applications.md)\n接管控制后会分析请求数据并传送到[模型](structure-models.md)，\n传送模型结果到[视图](structure-views.md)，最后生成输出响应信息。\n\n\n## 动作 <span id=\"actions\"></span>\n\n控制器由 *操作* 组成，它是执行终端用户请求的最基础的单元，\n一个控制器可有一个或多个操作。\n\n如下示例显示包含两个动作`view` and `create` 的控制器`post`：\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse app\\models\\Post;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\n\nclass PostController extends Controller\n{\n    public function actionView($id)\n    {\n        $model = Post::findOne($id);\n        if ($model === null) {\n            throw new NotFoundHttpException;\n        }\n\n        return $this->render('view', [\n            'model' => $model,\n        ]);\n    }\n\n    public function actionCreate()\n    {\n        $model = new Post;\n\n        if ($model->load(Yii::$app->request->post()) && $model->save()) {\n            return $this->redirect(['view', 'id' => $model->id]);\n        } else {\n            return $this->render('create', [\n                'model' => $model,\n            ]);\n        }\n    }\n}\n```\n\n在操作 `view` (定义为 `actionView()` 方法)中， \n代码首先根据请求模型ID加载 [模型](structure-models.md)，\n如果加载成功，会渲染名称为`view`的[视图](structure-views.md)并显示，否则会抛出一个异常。\n\n在操作 `create` (定义为 `actionCreate()` 方法)中, 代码相似. \n先将请求数据填入[模型](structure-models.md)，\n然后保存模型，如果两者都成功，会跳转到ID为新创建的模型的`view`操作，\n否则显示提供用户输入的`create`视图。\n\n\n## 路由 <span id=\"routes\"></span>\n\n终端用户通过所谓的*路由*寻找到动作，路由是包含以下部分的字符串：\n\n* 模块ID: 仅存在于控制器属于非应用的[模块](structure-modules.md);\n* 控制器ID: 同应用（或同模块如果为模块下的控制器）\n  下唯一标识控制器的字符串;\n* 操作ID: 同控制器下唯一标识操作的字符串。\n\n路由使用如下格式:\n\n```\nControllerID/ActionID\n```\n\n如果属于模块下的控制器，使用如下格式：\n\n```php\nModuleID/ControllerID/ActionID\n```\n\n如果用户的请求地址为 `https://hostname/index.php?r=site/index`, \n会执行`site` 控制器的`index` 操作。\n更多关于处理路由的详情请参阅 [路由](runtime-routing.md) 一节。\n\n\n## 创建控制器 <span id=\"creating-controllers\"></span>\n\n在[[yii\\web\\Application|Web applications]]网页应用中，控制器应继承[[yii\\web\\Controller]] 或它的子类。\n同理在[[yii\\console\\Application|console applications]]控制台应用中，控制器继承[[yii\\console\\Controller]] 或它的子类。\n如下代码定义一个 `site` 控制器:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n}\n```\n\n\n### 控制器ID <span id=\"controller-ids\"></span>\n\n通常情况下，控制器用来处理请求有关的资源类型，\n因此控制器ID通常为和资源有关的名词。\n例如使用`article`作为处理文章的控制器ID。\n\n控制器ID应仅包含英文小写字母、数字、下划线、中横杠和正斜杠，\n例如 `article` 和 `post-comment` 是真是的控制器ID，\n`article?`, `PostComment`, `admin\\post`不是控制器ID。\n\n控制器Id可包含子目录前缀，例如 `admin/article` 代表\n[[yii\\base\\Application::controllerNamespace|controller namespace]]\n控制器命名空间下 `admin`子目录中 `article` 控制器。\n子目录前缀可为英文大小写字母、数字、下划线、正斜杠，其中正斜杠用来区分多级子目录(如 `panels/admin`)。\n\n\n### 控制器类命名 <span id=\"controller-class-naming\"></span>\n\n控制器ID遵循以下规则衍生控制器类名：\n\n1. 将用正斜杠区分的每个单词第一个字母转为大写。注意如果控制器ID包含正斜杠，\n   只将最后的正斜杠后的部分第一个字母转为大写；\n2. 去掉中横杠，将正斜杠替换为反斜杠;\n3. 增加`Controller`后缀;\n4. 在前面增加[[yii\\base\\Application::controllerNamespace|controller namespace]]控制器命名空间.\n\n下面为一些示例，假设[[yii\\base\\Application::controllerNamespace|controller namespace]]\n控制器命名空间为 `app\\controllers`:\n\n* `article` 对应 `app\\controllers\\ArticleController`;\n* `post-comment` 对应 `app\\controllers\\PostCommentController`;\n* `admin/post-comment` 对应 `app\\controllers\\admin\\PostCommentController`;\n* `adminPanels/post-comment` 对应 `app\\controllers\\adminPanels\\PostCommentController`.\n\n控制器类必须能被 [自动加载](concept-autoloading.md)，所以在上面的例子中，\n控制器`article` 类应在 [别名](concept-aliases.md) \n为`@app/controllers/ArticleController.php`的文件中定义，\n控制器`admin/post-comment`应在`@app/controllers/admin/PostCommentController.php`文件中。\n\n> Info: 最后一个示例 `admin/post-comment` 表示你可以将控制器放在\n  [[yii\\base\\Application::controllerNamespace|controller namespace]]控制器命名空间下的子目录中，\n  在你不想用 [模块](structure-modules.md) 的情况下给控制器分类，这种方式很有用。\n\n\n### 控制器部署 <span id=\"controller-map\"></span>\n\n可通过配置 [[yii\\base\\Application::controllerMap|controller map]] \n来强制上述的控制器ID和类名对应，\n通常用在使用第三方不能掌控类名的控制器上。\n\n配置 [应用配置](structure-applications.md#application-configurations) \n中的[application configuration](structure-applications.md#application-configurations)，如下所示：\n\n```php\n[\n    'controllerMap' => [\n        // 用类名申明 \"account\" 控制器\n        'account' => 'app\\controllers\\UserController',\n\n        // 用配置数组申明 \"article\" 控制器\n        'article' => [\n            'class' => 'app\\controllers\\PostController',\n            'enableCsrfValidation' => false,\n        ],\n    ],\n]\n```\n\n\n### 默认控制器 <span id=\"default-controller\"></span>\n\n每个应用有一个由[[yii\\base\\Application::defaultRoute]]属性指定的默认控制器；\n当请求没有指定 [路由](#ids-routes)，该属性值作为路由使用。\n对于[[yii\\web\\Application|Web applications]]网页应用，它的值为 `'site'`，对于 [[yii\\console\\Application|console applications]]\n控制台应用，它的值为 `help`，所以URL为 `https://hostname/index.php` 表示由 `site` 控制器来处理。\n\n可以在 [应用配置](structure-applications.md#application-configurations) 中修改默认控制器，如下所示：\n\n```php\n[\n    'defaultRoute' => 'main',\n]\n```\n\n\n## 创建动作 <span id=\"creating-actions\"></span>\n\n创建操作可简单地在控制器类中定义所谓的 *操作方法* 来完成，操作方法必须是以`action`开头的公有方法。\n操作方法的返回值会作为响应数据发送给终端用户，\n如下代码定义了两个操作 `index` 和 `hello-world`:\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actionIndex()\n    {\n        return $this->render('index');\n    }\n\n    public function actionHelloWorld()\n    {\n        return 'Hello World';\n    }\n}\n```\n\n\n### 动作ID <span id=\"action-ids\"></span>\n\n操作通常是用来执行资源的特定操作，因此，\n操作ID通常为动词，如`view`, `update`等。\n\n操作ID应仅包含英文小写字母、数字、下划线和中横杠，操作ID中的中横杠用来分隔单词。\n例如`view`, `update2`, `comment-post`是真实的操作ID，\n`view?`, `Update`不是操作ID.\n\n可通过两种方式创建操作ID，内联操作和独立操作. An inline action is\n内联操作在控制器类中定义为方法；独立操作是继承[[yii\\base\\Action]]或它的子类的类。\n内联操作容易创建，在无需重用的情况下优先使用；\n独立操作相反，主要用于多个控制器重用，\n或重构为[扩展](structure-extensions.md)。\n\n\n### 内联动作 <span id=\"inline-actions\"></span>\n\n内联动作指的是根据我们刚描述的操作方法。\n\n动作方法的名字是根据操作ID遵循如下规则衍生：\n\n1. 将每个单词的第一个字母转为大写;\n2. 去掉中横杠;\n3. 增加`action`前缀.\n\n例如`index` 转成 `actionIndex`, `hello-world` 转成 `actionHelloWorld`。\n\n> Note: 操作方法的名字*大小写敏感*，如果方法名称为`ActionIndex`不会认为是操作方法，\n  所以请求`index`操作会返回一个异常，\n  也要注意操作方法必须是公有的，\n  私有或者受保护的方法不能定义成内联操作。\n\n\n因为容易创建，内联操作是最常用的操作，\n但是如果你计划在不同地方重用相同的操作，\n或者你想重新分配一个操作，需要考虑定义它为*独立操作*。\n\n\n### 独立动作 <span id=\"standalone-actions\"></span>\n\n独立操作通过继承[[yii\\base\\Action]]或它的子类来定义。\n例如Yii发布的[[yii\\web\\ViewAction]]\n和[[yii\\web\\ErrorAction]]都是独立操作。\n\n要使用独立操作，需要通过控制器中覆盖[[yii\\base\\Controller::actions()]]方法在*action map*中申明，\n如下例所示：\n\n```php\npublic function actions()\n{\n    return [\n        // 用类来申明\"error\" 动作\n        'error' => 'yii\\web\\ErrorAction',\n\n        // 用配置数组申明 \"view\" 动作\n        'view' => [\n            'class' => 'yii\\web\\ViewAction',\n            'viewPrefix' => '',\n        ],\n    ];\n}\n```\n\n如上所示， `actions()` 方法返回键为操作ID、值为对应操作类名\n或数组[configurations](concept-configurations.md) 的数组。\n和内联操作不同，独立操作ID可包含任意字符，只要在`actions()` 方法中申明.\n\n为创建一个独立操作类，需要继承[[yii\\base\\Action]] 或它的子类，并实现公有的名称为`run()`的方法,\n`run()` 方法的角色和操作方法类似，例如：\n\n```php\n<?php\nnamespace app\\components;\n\nuse yii\\base\\Action;\n\nclass HelloWorldAction extends Action\n{\n    public function run()\n    {\n        return \"Hello World\";\n    }\n}\n```\n\n\n### 动作结果 <span id=\"action-results\"></span>\n\n操作方法或独立操作的`run()`方法的返回值非常重要，\n它表示对应操作结果。\n\n返回值可为 [响应](runtime-responses.md) 对象，作为响应发送给终端用户。\n\n* 对于[[yii\\web\\Application|Web applications]]网页应用，返回值可为任意数据, 它赋值给[[yii\\web\\Response::data]]，\n  最终转换为字符串来展示响应内容。\n* 对于[[yii\\console\\Application|console applications]]控制台应用，返回值可为整数，\n  表示命令行下执行的 [[yii\\console\\Response::exitStatus|exit status]] 退出状态。\n\n在上面的例子中，操作结果都为字符串，作为响应数据发送给终端用户，\n下例显示一个操作通过\n返回响应对象（因为[[yii\\web\\Controller::redirect()|redirect()]]方法返回一个响应对象）\n可将用户浏览器跳转到新的URL。\n\n```php\npublic function actionForward()\n{\n    // 用户浏览器跳转到 https://example.com\n    return $this->redirect('https://example.com');\n}\n```\n\n\n### 动作参数 <span id=\"action-parameters\"></span>\n\n内联动作的操作方法和独立动作的 `run()` 方法可以带参数，称为*动作参数*。\n参数值从请求中获取，对于[[yii\\web\\Application|Web applications]]网页应用，\n每个动作参数的值从`$_GET`中获得，参数名作为键；\n对于[[yii\\console\\Application|console applications]]控制台应用, 动作参数对应命令行参数。\n\n如下例，动作`view` (内联动作) 申明了两个参数 `$id` 和 `$version`。\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass PostController extends Controller\n{\n    public function actionView($id, $version = null)\n    {\n        // ...\n    }\n}\n```\n\n动作参数会被不同的参数填入，如下所示：\n\n* `https://hostname/index.php?r=post/view&id=123`: `$id` 会填入`'123'`，\n  `$version` 仍为 null 空因为没有`version`请求参数;\n* `https://hostname/index.php?r=post/view&id=123&version=2`: \n  $id` 和 `$version` 分别填入 `'123'` 和 `'2'`；\n* `https://hostname/index.php?r=post/view`: 会抛出[[yii\\web\\BadRequestHttpException]] 异常\n  因为请求没有提供参数给必须赋值参数`$id`；\n* `https://hostname/index.php?r=post/view&id[]=123`: 会抛出[[yii\\web\\BadRequestHttpException]] 异常\n  因为 `$id` 参数收到数组值 `['123']` 而不是字符串.\n\n如果你想要一个动作参数来接受数组值，你应该使用 `array` 来提示它，如下所示：\n\n```php\npublic function actionView(array $id, $version = null)\n{\n    // ...\n}\n```\n\n现在如果请求为 `https://hostname/index.php?r=post/view&id[]=123`, 参数 `$id` 会使用数组值 `['123']`，\n如果请求为 `https://hostname/index.php?r=post/view&id=123`，\n参数 `$id` 会获取相同数组值，因为无类型的 `'123'` 会自动转成数组。\n\n上述例子主要描述网页应用的操作参数，对于控制台应用，\n更多详情请参阅[控制台命令](tutorial-console.md)。\n\n\n### 默认动作 <span id=\"default-action\"></span>\n\n每个控制器都有一个由 [[yii\\base\\Controller::defaultAction]] 属性指定的默认操作，\n当[路由](#ids-routes) 只包含控制器ID，\n会使用所请求的控制器的默认操作。\n\n默认操作默认为 `index`，如果想修改默认操作，只需简单地在控制器类中覆盖这个属性，\n如下所示：\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public $defaultAction = 'home';\n\n    public function actionHome()\n    {\n        return $this->render('home');\n    }\n}\n```\n\n\n## 控制器生命周期 <span id=\"controller-lifecycle\"></span>\n\n处理一个请求时，[应用主体](structure-applications.md) 会根据请求\n[路由](#routes)创建一个控制器，\n控制器经过以下生命周期来完成请求：\n\n1. 在控制器创建和配置后，[[yii\\base\\Controller::init()]] 方法会被调用。\n2. 控制器根据请求操作ID创建一个操作对象:\n   * 如果操作ID没有指定，会使用[[yii\\base\\Controller::defaultAction|default action ID]]默认操作ID；\n   * 如果在[[yii\\base\\Controller::actions()|action map]]找到操作ID，\n     会创建一个独立操作；\n   * 如果操作ID对应操作方法，会创建一个内联操作；\n   * 否则会抛出[[yii\\base\\InvalidRouteException]]异常。\n3. 控制器按顺序调用应用主体、模块（如果控制器属于模块）、\n   控制器的 `beforeAction()` 方法；\n   * 如果任意一个调用返回false，后面未调用的`beforeAction()`会跳过并且操作执行会被取消；\n     action execution will be cancelled.\n   * 默认情况下每个 `beforeAction()` 方法会触发一个 `beforeAction` 事件，在事件中你可以追加事件处理操作；\n4. 控制器执行操作:\n   * 请求数据解析和填入到操作参数；\n5. 控制器按顺序调用控制器、模块（如果控制器属于模块）、应用主体的 `afterAction()` 方法；\n   * 默认情况下每个 `afterAction()` 方法会触发一个 `afterAction` 事件，\n   在事件中你可以追加事件处理操作；\n6. 应用主体获取操作结果并赋值给[响应](runtime-responses.md).\n\n\n## 最佳实践 <span id=\"best-practices\"></span>\n\n在设计良好的应用中，控制器很精练，包含的操作代码简短；\n如果你的控制器很复杂，通常意味着需要重构，\n转移一些代码到其他类中。\n\n归纳起来，控制器\n\n* 可访问 [请求](runtime-requests.md) 数据;\n* 可根据请求数据调用 [模型](structure-models.md) 的方法和其他服务组件;\n* 可使用 [视图](structure-views.md) 构造响应;\n* 不应处理应被[模型](structure-models.md)处理的请求数据;\n* 应避免嵌入HTML或其他展示代码，这些代码最好在 [视图](structure-views.md)中处理.\n"
  },
  {
    "path": "docs/guide-zh-CN/structure-entry-scripts.md",
    "content": "入口脚本\n=============\n\n入口脚本是应用启动流程中的第一环，\n一个应用（不管是网页应用还是控制台应用）只有一个入口脚本。\n终端用户的请求通过入口脚本实例化应用并将请求转发到应用。\n\nWeb 应用的入口脚本必须放在终端用户能够访问的目录下，\n通常命名为 `index.php`，\n也可以使用 Web 服务器能定位到的其他名称。\n\n控制台应用的入口脚本一般在应用根目录下命名为 `yii`（后缀为.php），\n该文件需要有执行权限，\n这样用户就能通过命令 `./yii <route> [arguments] [options]` 来运行控制台应用。\n\n入口脚本主要完成以下工作：\n\n* 定义全局常量；\n* 注册 [Composer 自动加载器](https://getcomposer.org/doc/01-basic-usage.md#autoloading)；\n* 包含 [[Yii]] 类文件；\n* 加载应用配置；\n* 创建一个[应用](structure-applications.md)实例并配置;\n* 调用 [[yii\\base\\Application::run()]] 来处理请求。\n\n\n## Web 应用 <span id=\"web-applications\"></span>\n\n以下是[基础应用模版](start-installation.md)入口脚本的代码：\n\n```php\n<?php\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n\n// 注册 Composer 自动加载器\nrequire __DIR__ . '/../vendor/autoload.php';\n\n// 包含 Yii 类文件\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n\n// 加载应用配置\n$config = require __DIR__ . '/../config/web.php';\n\n// 创建、配置、运行一个应用\n(new yii\\web\\Application($config))->run();\n```\n\n\n## 控制台应用 <span id=\"console-applications\"></span>\n\n以下是一个控制台应用的入口脚本：\n\n```php\n#!/usr/bin/env php\n<?php\n/**\n * Yii console bootstrap file.\n *\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n\n// 注册 Composer 自动加载器\nrequire __DIR__ . '/vendor/autoload.php';\n\n// 包含 Yii 类文件\nrequire __DIR__ . '/vendor/yiisoft/yii2/Yii.php';\n\n// 加载应用配置\n$config = require __DIR__ . '/config/console.php';\n\n$application = new yii\\console\\Application($config);\n$exitCode = $application->run();\nexit($exitCode);\n```\n\n\n## 定义常量 <span id=\"defining-constants\"></span>\n\n入口脚本是定义全局常量的最好地方，Yii 支持以下三个常量：\n\n* `YII_DEBUG`：标识应用是否运行在调试模式。当在调试模式下，应用会保留更多日志信息，\n  如果抛出异常，会显示详细的错误调用堆栈。\n  因此，调试模式主要适合在开发阶段使用，`YII_DEBUG` 默认值为 false。\n* `YII_ENV`：标识应用运行的环境，详情请查阅\n  [配置](concept-configurations.md#environment-constants)章节。\n  `YII_ENV` 默认值为 `'prod'`，表示应用运行在线上产品环境。\n* `YII_ENABLE_ERROR_HANDLER`：标识是否启用 Yii 提供的错误处理，\n  默认为 true。\n\n当定义一个常量时，通常使用类似如下代码来定义：\n\n```php\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\n```\n\n上面的代码等同于：\n\n```php\nif (!defined('YII_DEBUG')) {\n    define('YII_DEBUG', true);\n}\n```\n\n显然第一段代码更加简洁易懂。\n\n常量定义应该在入口脚本的开头，这样包含其他 PHP 文件时，\n常量就能生效。\n"
  },
  {
    "path": "docs/guide-zh-CN/structure-extensions.md",
    "content": "扩展\n==========\n\n扩展是专门设计的在 Yii 应用中随时可拿来使用的，\n并可重发布的软件包。例如， [yiisoft/yii2-debug](https://github.com/yiisoft/yii2-debug)\n扩展在你的应用的每个页面底部添加一个方便用于调试的工具栏，\n帮助你简单地抓取页面生成的情况。\n你可以使用扩展来加速你的开发过程。\n\n> Info: 本文中我们使用的术语 \"扩展\" 特指 Yii 软件包。而用术语\n  \"软件包\" 和 \"库\" 指代非 Yii 专用的通常意义上的软件包。\n\n\n## 使用扩展 <span id=\"using-extensions\"></span>\n\n要使用扩展，你要先安装它。大多数扩展以 [Composer](https://getcomposer.org/) 软件包的形式发布，\n这样的扩展可采取下述两个步骤来安装：\n\n1. 修改你的应用的 `composer.json` 文件，指明你要安装的是哪个扩展 （Composer 软件包）。\n2. 运行 `composer install` 来安装指定的扩展。\n\n注意如果你还没有安装 [Composer](https://getcomposer.org/) ，你需要先安装。\n\n默认情况，Composer安装的是在 [Packagist](https://packagist.org/) 中\n注册的软件包 - 最大的开源 Composer 代码库。你可以在 Packageist 中查找扩展。\n你也可以 [创建你自己的代码库](https://getcomposer.org/doc/05-repositories.md#repository) 然后配置 Composer 来使用它。\n如果是在开发私有的扩展，并且想只在你的其他工程中共享时，这样做是很有用的。\n\n通过 Composer 安装的扩展会存放在 `BasePath/vendor` 目录下，这里的 `BasePath` \n指你的应用的 [base path](structure-applications.md#basePath)。因为 Composer 还是一个依赖管理器，当它安装一个包时，\n也将安装这个包所依赖的所有软件包。\n\n例如想安装 `yiisoft/yii2-imagine` 扩展，可按如下示例修改你的 `composer.json` 文件：\n\n```json\n{\n    // ...\n\n    \"require\": {\n        // ... other dependencies\n\n        \"yiisoft/yii2-imagine\": \"~2.0.0\"\n    }\n}\n```\n\n安装完成后，你应该能在 `BasePath/vendor` 目录下见到 `yiisoft/yii2-imagine` 目录。你也应该\n见到另一个 `imagine/imagine` 目录，在其中安装了所依赖的包。\n\n> Info: `yiisoft/yii2-imagine` 是 Yii 由开发团队维护一个核心扩展，\n  所有核心扩展均由 [Packagist](https://packagist.org/) 集中管理，命名为\n  `yiisoft/yii2-xyz`，其中的 `xyz`， 不同扩展有不同名称。\n\n现在你可以使用安装好的扩展了，好比是应用的一部分。如下示例展示了如何使用 `yiisoft/yii2-imagine` 扩展\n提供的 `yii\\imagine\\Image` 类：\n\n```php\nuse Yii;\nuse yii\\imagine\\Image;\n\n// generate a thumbnail image\nImage::thumbnail('@webroot/img/test-image.jpg', 120, 120)\n    ->save(Yii::getAlias('@runtime/thumb-test-image.jpg'), ['quality' => 50]);\n```\n\n> Info: 扩展类由 [Yii class autoloader](concept-autoloading.md) 自动加载。\n\n\n### 手动安装扩展 <span id=\"installing-extensions-manually\"></span>\n\n在极少情况下，你可能需要手动安装一部分或者全部扩展，而不是依赖 Composer。\n想做到这一点，你应当：\n\n1. 下载扩展压缩文件，解压到 `vendor` 目录。\n2. 如果有，则安装扩展提供的自动加载器。\n3. 按指导说明下载和安装所有依赖的扩展。\n\n如果扩展没有提供类的自动加载器，但也遵循了 [PSR-4 standard](https://www.php-fig.org/psr/psr-4/) \n标准，那么你可以使用 Yii 提供的类自动加载器来加载扩展类。\n你需要做的仅仅是为扩展的根目录声明一个 [root alias](concept-aliases.md#defining-aliases)。\n例如，假设在 `vendor/mycompany/myext` 目录中安装了一个扩展，并且扩展类的命名空间为 `myext` ，\n那么你可以在应用配置文件中包含如下代码：\n\n```php\n[\n    'aliases' => [\n        '@myext' => '@vendor/mycompany/myext',\n    ],\n]\n```\n\n\n## 创建扩展 <span id=\"creating-extensions\"></span>\n\n在你需要将你的杰作分享给其他人的时候，你可能会考虑创建一个扩展。\n扩展可包括任何你喜欢的代码，例如助手类、挂件、模块，等等。\n\n建议你按照 [Composer package](https://getcomposer.org/) 的条款创建扩展，以便其他人更容易安装和使用。\n就像上面的章节讲述的那样。\n\n以下是将扩展创建为一个 Composer 软件包的需遵循的基本步骤。\n\n1. 为你的扩展建一个工程，并将它存放在版本控制代码库中，例如 [github.com](https://github.com) 。\n   扩展的开发和维护都应该在这个代码库中进行。\n2. 在工程的根目录下，建一个 Composer 所需的名为 `composer.json` 的文件。\n   详情请参考后面的章节。  \n3. 在一个 Composer 代码库中注册你的扩展，比如在 [Packagist](https://packagist.org/) 中，以便其他\n   用户能找到以及用 Composer 安装你的扩展。\n\n\n### `composer.json` <span id=\"composer-json\"></span>\n\n每个 Composer 软件包在根目录都必须有一个 `composer.json` 文件。该文件包含软件包的元数据。\n你可以在 [Composer手册](https://getcomposer.org/doc/01-basic-usage.md#composer-json-project-setup) 中找到完整关于该文件的规格。\n以下例子展示了 `yiisoft/yii2-imagine` 扩展的 `composer.json` 文件。\n\n```json\n{\n    // package name\n    \"name\": \"yiisoft/yii2-imagine\",\n\n    // package type\n    \"type\": \"yii2-extension\",\n\n    \"description\": \"The Imagine integration for the Yii framework\",\n    \"keywords\": [\"yii2\", \"imagine\", \"image\", \"helper\"],\n    \"license\": \"BSD-3-Clause\",\n    \"support\": {\n        \"issues\": \"https://github.com/yiisoft/yii2/issues?labels=ext%3Aimagine\",\n        \"forum\": \"https://forum.yiiframework.com/\",\n        \"wiki\": \"https://www.yiiframework.com/wiki/\",\n        \"irc\": \"ircs://irc.libera.chat:6697/yii\",\n        \"source\": \"https://github.com/yiisoft/yii2\"\n    },\n    \"authors\": [\n        {\n            \"name\": \"Antonio Ramirez\",\n            \"email\": \"amigo.cobos@gmail.com\"\n        }\n    ],\n\n    // package dependencies\n    \"require\": {\n        \"yiisoft/yii2\": \"~2.0.0\",\n        \"imagine/imagine\": \"v0.5.0\"\n    },\n\n    // class autoloading specs\n    \"autoload\": {\n        \"psr-4\": {\n            \"yii\\\\imagine\\\\\": \"\"\n        }\n    }\n}\n```\n\n\n#### 包名 <span id=\"package-name\"></span>\n\n每个 Composer 软件包都应当有一个唯一的包名以便能从其他的软件包中识别出来。\n包名的格式为 `vendorName/projectName` 。例如在包名 `yiisoft/yii2-imagine` 中，vendor 名和 project 名分别是\n `yiisoft` 和 `yii2-imagine` 。\n\n不要用 `yiisoft` 作为你的 vendor 名，由于它被 Yii 的核心代码预留使用了。\n\n我们推荐你用 `yii2-` 作为你的包名的前缀，表示它是 Yii 2 的扩展，例如，`myname/yii2-mywidget`。\n这更便于用户辨别是否是 Yii 2 的扩展。\n\n\n#### 包类型 <span id=\"package-type\"></span>\n\n将你的扩展指明为 `yii2-extension` 类型很重要，以便安装的时候\n能被识别出是一个 Yii 扩展。\n\n当用户运行 `composer install` 安装一个扩展时， `vendor/yiisoft/extensions.php` \n文件会被自动更新使之包含新扩展的信息。从该文件中， Yii 应用程序就能知道安装了\n哪些扩展 (这些信息可通过 [[yii\\base\\Application::extensions]] 访问)。\n\n\n#### 依赖 <span id=\"dependencies\"></span>\n\n你的扩展依赖于 Yii （理所当然）。因此你应当在 `composer.json` 文件中列出它\n(`yiisoft/yii2`)。如果你的扩展还依赖其他的扩展或者是第三方库，你也要一并列出来。\n确定你也为每一个依赖的包列出了适当的版本约束条件 (比如 `1.*`, `@stable`) 。\n当你发布一个稳定版本时，你所依赖的包也应当使用稳定版本。\n\n大多数 JavaScript/CSS 包是用 [Bower](https://bower.io/) 来管理的，而非 Composer。你可使用 \n[Composer asset 插件](https://github.com/fxpio/composer-asset-plugin) 使之可以\n通过 Composer 来管理这类包。如果你的扩展依赖 Bower 软件包，你可以如下例所示那样简单地\n在 `composer.json` 文件的依赖中列出它。\n\n```json\n{\n    // package dependencies\n    \"require\": {\n        \"bower-asset/jquery\": \">=1.11.*\"\n    }\n}\n```\n\n上述代码表明该扩展依赖于 `jquery` Bower 包。一般来说，你可以在 `composer.json` \n中用 `bower-asset/PackageName` 指定 Bower 包，用 `npm-asset/PackageName` 指定 NPM 包。\n当 Compower 安装 Bower 和 NPM 软件包时，包的内容默认会分别安装到 `@vendor/bower/PackageName`\n和 `@vendor/npm/Packages` 下。这两个目录还可以分别用 `@bower/PackageName` \n和 `@npm/PackageName` 别名指向。\n\n关于 asset 管理的详细情况，请参照 [Assets](structure-assets.md#bower-npm-assets) 章节。\n\n\n#### 类的自动加载 <span id=\"class-autoloading\"></span>\n\n为使你的类能够被 Yii 的类自动加载器或者 Composer 的类自动加载器自动加载，你应当在\n `composer.json` 中指定 `autoload` 条目，如下所示：\n\n```json\n{\n    // ....\n\n    \"autoload\": {\n        \"psr-4\": {\n            \"yii\\\\imagine\\\\\": \"\"\n        }\n    }\n}\n```\n\n你可以列出一个或者多个根命名空间和它们的文件目录。\n\n当扩展安装到应用中后，Yii 将为每个所列出根命名空间创建一个\n[别名](concept-aliases.md#extension-aliases) 指向命名空间对应的目录。\n例如，上述的 `autoload` 条目声明将对应于别名 `@yii/imagine`。\n\n\n### 推荐的做法 <span id=\"recommended-practices\"></span>\n\n扩展意味着会被其他人使用，你在开发中通常需要额外的付出。\n下面我们介绍一些通用的及推荐的做法，以创建高品质的扩展。\n\n\n#### 命名空间 <span id=\"namespaces\"></span>\n\n为避免冲突以及使你的扩展中的类能被自动加载，你的类应当使用命名空间，\n并使类的命名符合 [PSR-4 standard](https://www.php-fig.org/psr/psr-4/) 或者\n[PSR-0 standard](https://www.php-fig.org/psr/psr-0/) 标准。\n\n你的类的命名空间应以 `vendorName\\extensionName` 起始，其中 `extensionName` \n和项目名相同，除了它没有 `yii2-` 前缀外。例如，对 `yiisoft/yii2-imagine` 扩展\n来说，我们用 `yii\\imagine` 作为它的类的命名空间。\n\n不要使用 `yii`、`yii2` 或者 `yiisoft` 作为你的 vendor 名。这些名称已由 Yii 内核代码预留使用了。\n\n\n#### 类的自举引导 <span id=\"bootstrapping-classes\"></span>\n\n有时候，你可能想让你的扩展在应用的 [自举过程](runtime-bootstrapping.md) 中执行一些代码。\n例如，你的扩展可能想响应应用的 `beginRequest` 事件，做一些环境的设置工作。\n虽然你可以指导扩展的使用者显式地将你的扩展中的事件句柄附加（绑定）到 `beginRequest` 事件，\n但是更好的方法是自动完成。\n\n为实现该目标，你可以创建一个所谓 *bootstrapping class* （自举类）实现 [[yii\\base\\BootstrapInterface]] 接口。\n例如，\n\n```php\nnamespace myname\\mywidget;\n\nuse yii\\base\\BootstrapInterface;\nuse yii\\base\\Application;\n\nclass MyBootstrapClass implements BootstrapInterface\n{\n    public function bootstrap($app)\n    {\n        $app->on(Application::EVENT_BEFORE_REQUEST, function () {\n             // do something here\n        });\n    }\n}\n```\n\n然后你将这个类在 `composer.json` 文件中列出来，如下所示，\n\n```json\n{\n    // ...\n\n    \"extra\": {\n        \"bootstrap\": \"myname\\\\mywidget\\\\MyBootstrapClass\"\n    }\n}\n```\n\n当这个扩展安装到应用后，Yii 将在每一个请求的自举过程中\n自动实例化自举类并调用其 [[yii\\base\\BootstrapInterface::bootstrap()|bootstrap()]] \n方法。\n\n\n#### 操作数据库 <span id=\"working-with-databases\"></span>\n\n你的扩展可能要存取数据库。不要假设使用你的扩展的应用总是用 \n`Yii::$db` 作为数据库连接。你应当在需要访问数据库的类中申明一个 `db` 属性。\n这个属性允许你的扩展的用户可定制你的扩展使用哪个 DB 连接。例如，\n你可以参考 [[yii\\caching\\DbCache]] 类看一下它是如何申明和使用 `db` 属性的。\n\n如果你的扩展需要创建特定的数据库表，或者修改数据库结构，你应当\n\n- 提供 [数据迁移](db-migrations.md) 来操作数据库的结构修改，而不是使用SQL文本文件；\n- 尽量使迁移文件适用于不同的 DBMS；\n- 在迁移文件中避免使用 [Active Record](db-active-record.md)。\n\n\n#### 使用 Assets <span id=\"using-assets\"></span>\n\n如果你的扩展是挂件或者模块类型，它有可能需要使用一些 [assets](structure-assets.md) 。\n例如，一个模块可能要显示一些包含图片，JavaScript 和 CSS 的页面。因为扩展的文件\n都是放在同一个目录之下，安装之后 Web 无法读取，你有两个选择使得这些 asset 文件目录\n可以通过 Web 读取：\n\n- 让扩展的用户手动将这些 asset 文件拷贝到特定的 Web 可以读取的文件夹；\n- 申明一个 [asset bundle](structure-assets.md) 并依靠 asset 发布机制自动将这些文件（asset bundle 中列出的文件）\n  拷贝到 Web 可读的文件夹。\n\n我们推荐你使用第二种方法，以便其他人能更容易使用你的扩展。\n更详细的关于如何处理 assets ，请参照 [Assets](structure-assets.md) 章节。\n\n\n#### 国际化和本地化 <span id=\"i18n-l10n\"></span>\n\n你的扩展可能会在支持不同语言的应用中使用！因此，如果你的扩展要显示内容给终端用户，\n你应当试着实现 [国际化和本地化](tutorial-i18n.md)，特别地，\n\n- 如果扩展为终端用户显示信息，这些信息应该用 `Yii::t()` \n  包装起来，以便可以进行翻译。\n  只给开发者参考的信息（如内部异常信息）不需要做翻译。\n- 如果扩展显示数字、日期等，你应该用 [[yii\\i18n\\Formatter]] \n  中适当的格式化规则做格式化处理。\n\n更详细的信息，请参照 [Internationalization](tutorial-i18n.md) 章节。\n\n\n#### 测试 <span id=\"testing\"></span>\n\n你一定想让你的扩展可以无暇地运行而不会给其他人带来问题和麻烦。为达到这个目的，\n你应当在公开发布前做测试。\n\n推荐你创建测试用例，做全面覆盖的测试你的扩展，而不只是依赖于手动测试。\n每次发布新版本前，你只要简单地运行这些测试用例确保一切完好。\nYii 提供了测试支持，使你更容易写单元测试、验收测试和功能测试。\n详情请参照 [Testing](test-overview.md) 章节。\n\n\n#### 版本控制 <span id=\"versioning\"></span>\n\n你应该为每一个扩展定一个版本号（如 `1.0.1`）。我们推荐你命名版本号时参照\n[semantic versioning](https://semver.org) 决定用什么样的版本号。\n\n\n#### 发布 <span id=\"releasing\"></span>\n\n为使其他人知道你的扩展，你应该公开发布。\n\n如果你首次发布一个扩展，你应该在 Composer 代码库中注册它，例如\n[Packagist](https://packagist.org/)。之后，你所需要做的仅仅是在\n版本管理库中创建一个 tag （如`v1.0.1`），然后通知 Composer 代码库。\n其他人就能查找到这个新的发布了，并可通过 Composer 代码库安装和更新该扩展。\n\n在发布你的扩展时，除了代码文件，你还应该考虑包含如下内容\n帮助其他人了解和使用你的扩展：\n\n* 根目录下的 readme 文件：它描述你的扩展是干什么的以及如何安装和使用。\n  我们推荐你用 [Markdown](https://daringfireball.net/projects/markdown/) 的格式\n  来写并将文件命名为 `readme.md`。\n* 根目录下的修改日志文件：它列举每个版本的发布做了哪些更改。该文件可以用 Markdown 根式\n  编写并命名为 `changelog.md`。\n* 根目录下的升级文件：它给出如何从其他就版本升级该扩展的指导。该文件可以用 Markdown 根式\n  编写并命名为 `changelog.md`。\n* 入门指南、演示代码、截屏图示等：如果你的扩展提供了许多功能，在 readme 文件中不能完整\n  描述时，就要用到这些文件。\n* API 文档：你的代码应当做好文档，让其他人更容易阅读和理解。\n  你可以参照 [Object class file](https://github.com/yiisoft/yii2/blob/master/framework/base/Object.php)\n  学习如何为你的代码做文档。\n\n> Info: 你的代码注释可以写成 Markdown 格式。`yiisoft/yii2-apidoc` 扩展为你提供了一个从你的\n  代码注释生成漂亮的 API 文档。\n\n> Info: 虽然不做要求，我们还是建议你的扩展遵循某个编码规范。\n  你可以参照 [core framework code style](https://github.com/yiisoft/yii2/blob/master/docs/internals/core-code-style.md)。\n\n\n## 核心扩展 <span id=\"core-extensions\"></span>\n\nYii 提供了下列核心扩展，由 Yii 开发团队开发和维护。这些扩展全都在\n[Packagist](https://packagist.org/) 中注册，并像 [Using Extensions](#using-extensions) 章节描述\n的那样容易安装。\n\n- [yiisoft/yii2-apidoc](https://github.com/yiisoft/yii2-apidoc)：\n  提供了一个可扩展的、高效的 API 文档生成器。核心框架的 API \n  文档也是用它生成的。\n- [yiisoft/yii2-authclient](https://github.com/yiisoft/yii2-authclient)：\n  提供了一套常用的认证客户端，例如 Facebook OAuth2 客户端、GitHub OAuth2 客户端。\n- [yiisoft/yii2-bootstrap](https://github.com/yiisoft/yii2-bootstrap)：\n  提供了一套挂件，封装了 [Bootstrap](https://getbootstrap.com/) 的组件和插件。\n- [yiisoft/yii2-debug](https://github.com/yiisoft/yii2-debug)：\n  提供了对 Yii 应用的调试支持。当使用该扩展是，\n  在每个页面的底部将显示一个调试工具条。\n  该扩展还提供了一个独立的页面，以显示更详细的调试信息。\n- [yiisoft/yii2-elasticsearch](https://github.com/yiisoft/yii2-elasticsearch)：\n  提供对 [Elasticsearch](https://www.elastic.co/) 的使用支持。它包含基本的查询/搜索支持，\n  并实现了 [Active Record](db-active-record.md) 模式让你可以将活动记录\n  存储在 Elasticsearch 中。\n- [yiisoft/yii2-faker](https://github.com/yiisoft/yii2-faker)：\n  提供了使用 [Faker](https://github.com/fzaninotto/Faker) 的支持，为你生成模拟数据。\n- [yiisoft/yii2-gii](https://github.com/yiisoft/yii2-gii)：\n  提供了一个基于页面的代码生成器，具有高可扩展性，并能用来快速生成模型、\n  表单、模块、CRUD 等。\n- [yiisoft/yii2-httpclient](https://github.com/yiisoft/yii2-httpclient)：\n  提供 HTTP 客户端。\n- [yiisoft/yii2-imagine](https://github.com/yiisoft/yii2-imagine)：\n  提供了基于 [Imagine](https://imagine.readthedocs.org/) 的常用图像处理功能。\n- [yiisoft/yii2-jui](https://github.com/yiisoft/yii2-jui)：\n  提供了一套封装 [JQuery UI](https://jqueryui.com/) 的挂件以及它们的交互。\n- [yiisoft/yii2-mongodb](https://github.com/yiisoft/yii2-mongodb)：\n  提供了对 [MongoDB](https://www.mongodb.com/) 的使用支持。它包含基本\n  的查询、活动记录、数据迁移、缓存、代码生成等特性。\n- [yiisoft/yii2-queue](https://www.yiiframework.com/extension/yiisoft/yii2-queue)：\n  通过队列异步提供运行任务的支持。\n  它支持基于 DB，Redis，RabbitMQ，AMQP，Beanstalk 和 Gearman 的队列。\n- [yiisoft/yii2-redis](https://github.com/yiisoft/yii2-redis)：\n  提供了对 [redis](https://redis.io/) 的使用支持。它包含基本的\n  查询、活动记录、缓存等特性。\n- [yiisoft/yii2-shell](https://www.yiiframework.com/extension/yiisoft/yii2-shell)：\n  提供基于 [psysh](https://psysh.org/) 的交互式 shell。\n- [yiisoft/yii2-smarty](https://github.com/yiisoft/yii2-smarty)：\n  提供了一个基于 [Smarty](https://www.smarty.net/) 的模板引擎。\n- [yiisoft/yii2-sphinx](https://github.com/yiisoft/yii2-sphinx)：\n  提供了对 [Sphinx](https://sphinxsearch.com) 的使用支持。它包含基本的\n  查询、活动记录、代码生成等特性。\n- [yiisoft/yii2-swiftmailer](https://github.com/yiisoft/yii2-swiftmailer)：\n  提供了基于 [swiftmailer](https://swiftmailer.symfony.com/) 的邮件发送功能。\n- [yiisoft/yii2-twig](https://github.com/yiisoft/yii2-twig)：\n  提供了一个基于 [Twig](https://twig.symfony.com/) 的模板引擎。\n\n以下官方扩展适用于 Yii 2.1 及以上版本。\n您不需要为 Yii 2.0 安装它们，因为它们包含在核心框架中。\n\n- [yiisoft/yii2-captcha](https://www.yiiframework.com/extension/yiisoft/yii2-captcha)：\n  提供 CAPTCHA。\n- [yiisoft/yii2-jquery](https://www.yiiframework.com/extension/yiisoft/yii2-jquery)：\n  为 [jQuery](https://jquery.com/) 提供支持。\n- [yiisoft/yii2-maskedinput](https://www.yiiframework.com/extension/yiisoft/yii2-maskedinput)：\n  提供基于 [jQuery Input Mask plugin](https://robinherbots.github.io/Inputmask/) 的格式化输入小部件。\n- [yiisoft/yii2-mssql](https://www.yiiframework.com/extension/yiisoft/yii2-mssql)：\n  提供对使用 [MSSQL](https://www.microsoft.com/sql-server/) 的支持。\n- [yiisoft/yii2-oracle](https://www.yiiframework.com/extension/yiisoft/yii2-oracle)：\n  提供对使用 [Oracle](https://www.oracle.com/) 的支持。\n- [yiisoft/yii2-rest](https://www.yiiframework.com/extension/yiisoft/yii2-rest)：\n  提供对 REST API 的支持。\n"
  },
  {
    "path": "docs/guide-zh-CN/structure-filters.md",
    "content": "过滤器\n=======\n\n过滤器是 [控制器动作](structure-controllers.md#actions) 执行之前或之后执行的对象。\n例如访问控制过滤器可在动作执行之前来控制特殊终端用户是否有权限执行动作，\n内容压缩过滤器可在动作执行之后发给终端用户之前压缩响应内容。\n\n过滤器可包含预过滤（过滤逻辑在动作*之前*）或后过滤（过滤逻辑在动作*之后*），\n也可同时包含两者。\n\n\n## 使用过滤器 <span id=\"using-filters\"></span>\n\n过滤器本质上是一类特殊的 [行为](concept-behaviors.md)，\n所以使用过滤器和 [使用行为](concept-behaviors.md#attaching-behaviors)一样。\n可以在控制器类中覆盖它的 [[yii\\base\\Controller::behaviors()|behaviors()]] 方法来声明过滤器，如下所示：\n\n```php\npublic function behaviors()\n{\n    return [\n        [\n            'class' => 'yii\\filters\\HttpCache',\n            'only' => ['index', 'view'],\n            'lastModified' => function ($action, $params) {\n                $q = new \\yii\\db\\Query();\n                return $q->from('user')->max('updated_at');\n            },\n        ],\n    ];\n}\n```\n\n控制器类的过滤器默认应用到该类的 *所有* 动作，\n你可以配置 [[yii\\base\\ActionFilter::only|only]] 属性明确指定控制器应用到哪些动作。\n在上述例子中，`HttpCache` 过滤器只应用到 `index` 和 `view` 动作。\n也可以配置 [[yii\\base\\ActionFilter::except|except]] 属性\n使一些动作不执行过滤器。\n\n除了控制器外，可在 [模块](structure-modules.md)或[应用主体](structure-applications.md) 中申明过滤器。\n申明之后，过滤器会应用到所属该模块或应用主体的 *所有* 控制器动作，\n除非像上述一样配置过滤器的 [[yii\\base\\ActionFilter::only|only]] \n和 [[yii\\base\\ActionFilter::except|except]] 属性。\n\n> Note: 在模块或应用主体中申明过滤器，在[[yii\\base\\ActionFilter::only|only]] 和 [[yii\\base\\ActionFilter::except|except]]\n  属性中使用[路由](structure-controllers.md#routes) 代替动作 ID，\n  因为在模块或应用主体中只用动作ID并不能唯一指定到具体动作。\n\n当一个动作有多个过滤器时，根据以下规则先后执行：\n\n* 预过滤\n    - 按顺序执行应用主体中 `behaviors()` 列出的过滤器。\n    - 按顺序执行模块中 `behaviors()` 列出的过滤器。\n    - 按顺序执行控制器中 `behaviors()` 列出的过滤器。\n    - 如果任意过滤器终止动作执行，\n      后面的过滤器（包括预过滤和后过滤）不再执行。\n* 成功通过预过滤后执行动作。\n* 后过滤\n    - 倒序执行控制器中 `behaviors()` 列出的过滤器。\n    - 倒序执行模块中 `behaviors()` 列出的过滤器。\n    - 倒序执行应用主体中 `behaviors()` 列出的过滤器。\n\n\n## 创建过滤器 <span id=\"creating-filters\"></span>\n\n继承 [[yii\\base\\ActionFilter]] 类并覆盖\n[[yii\\base\\ActionFilter::beforeAction()|beforeAction()]] 或 [[yii\\base\\ActionFilter::afterAction()|afterAction()]]\n方法来创建动作的过滤器，前者在动作执行之前执行，后者在动作执行之后执行。\n[[yii\\base\\ActionFilter::beforeAction()|beforeAction()]] 返回值决定动作是否应该执行，\n如果为 false，之后的过滤器和动作不会继续执行。\n\n下面的例子申明一个记录动作执行时间日志的过滤器。\n\n```php\nnamespace app\\components;\n\nuse Yii;\nuse yii\\base\\ActionFilter;\n\nclass ActionTimeFilter extends ActionFilter\n{\n    private $_startTime;\n\n    public function beforeAction($action)\n    {\n        $this->_startTime = microtime(true);\n        return parent::beforeAction($action);\n    }\n\n    public function afterAction($action, $result)\n    {\n        $time = microtime(true) - $this->_startTime;\n        Yii::debug(\"Action '{$action->uniqueId}' spent $time second.\");\n        return parent::afterAction($action, $result);\n    }\n}\n```\n\n\n## 核心过滤器 <span id=\"core-filters\"></span>\n\nYii 提供了一组常用过滤器，在 `yii\\filters` 命名空间下，\n接下来我们简要介绍这些过滤器。\n\n\n### [[yii\\filters\\AccessControl|AccessControl]] <span id=\"access-control\"></span>\n\nAccessControl 提供基于 [[yii\\filters\\AccessControl::rules|rules]] 规则的访问控制。\n特别是在动作执行之前，访问控制会检测所有规则\n并找到第一个符合上下文的变量（比如用户 IP 地址、登录状态等等）的规则，\n来决定允许还是拒绝请求动作的执行，\n如果没有规则符合，访问就会被拒绝。\n\n如下示例表示表示允许已认证用户访问 `create` 和 `update` 动作，\n拒绝其他用户访问这两个动作。\n\n```php\nuse yii\\filters\\AccessControl;\n\npublic function behaviors()\n{\n    return [\n        'access' => [\n            'class' => AccessControl::class,\n            'only' => ['create', 'update'],\n            'rules' => [\n                // 允许认证用户\n                [\n                    'allow' => true,\n                    'roles' => ['@'],\n                ],\n                // 默认禁止其他用户\n            ],\n        ],\n    ];\n}\n```\n\n更多关于访问控制的详情请参阅 [授权](security-authorization.md) 一节。\n\n\n### 认证方法过滤器 <span id=\"auth-method-filters\"></span>\n\n认证方法过滤器通过 [HTTP Basic Auth](https://zh.wikipedia.org/wiki/HTTP%E5%9F%BA%E6%9C%AC%E8%AE%A4%E8%AF%81)\n或 [OAuth 2](https://oauth.net/2/)\n来认证一个用户，认证方法过滤器类在 `yii\\filters\\auth` 命名空间下。\n\n如下示例表示可使用 [[yii\\filters\\auth\\HttpBasicAuth]] 来认证一个用户，\n它使用基于 HTTP 基础认证方法的令牌。\n注意为了可运行，[[yii\\web\\User::identityClass|user identity class]] 类必须\n实现 [[yii\\web\\IdentityInterface::findIdentityByAccessToken()|findIdentityByAccessToken()]] 方法。\n\n```php\nuse yii\\filters\\auth\\HttpBasicAuth;\n\npublic function behaviors()\n{\n    return [\n        'basicAuth' => [\n            'class' => HttpBasicAuth::class,\n        ],\n    ];\n}\n```\n\n认证方法过滤器通常在实现 RESTful API中使用，\n更多关于访问控制的详情请参阅 RESTful [认证](rest-authentication.md) 一节。\n\n\n### [[yii\\filters\\ContentNegotiator|ContentNegotiator]] <span id=\"content-negotiator\"></span>\n\nContentNegotiator 支持响应内容格式处理和语言处理。\n通过检查 `GET` 参数和 `Accept` HTTP 头部来决定响应内容格式和语言。\n\n如下示例，配置 ContentNegotiator 支持 JSON 和 XML\n响应格式和英语（美国）和德语。\n\n```php\nuse yii\\filters\\ContentNegotiator;\nuse yii\\web\\Response;\n\npublic function behaviors()\n{\n    return [\n        [\n            'class' => ContentNegotiator::class,\n            'formats' => [\n                'application/json' => Response::FORMAT_JSON,\n                'application/xml' => Response::FORMAT_XML,\n            ],\n            'languages' => [\n                'en-US',\n                'de',\n            ],\n        ],\n    ];\n}\n```\n\n在[应用主体生命周期](structure-applications.md#application-lifecycle)过程中检测响应格式和语言简单很多，\n因此 ContentNegotiator 设计可被\n[引导启动组件](structure-applications.md#bootstrap)调用的过滤器。\n如下例所示可以将它配置在\n[应用主体配置](structure-applications.md#application-configurations)。\n\n```php\nuse yii\\filters\\ContentNegotiator;\nuse yii\\web\\Response;\n\n[\n    'bootstrap' => [\n        [\n            'class' => ContentNegotiator::class,\n            'formats' => [\n                'application/json' => Response::FORMAT_JSON,\n                'application/xml' => Response::FORMAT_XML,\n            ],\n            'languages' => [\n                'en-US',\n                'de',\n            ],\n        ],\n    ],\n];\n```\n\n> Info: 如果请求中没有检测到内容格式和语言，\n  使用 [[formats]] 和 [[languages]] 第一个配置项。\n\n\n\n### [[yii\\filters\\HttpCache|HttpCache]] <span id=\"http-cache\"></span>\n\nHttpCache 利用 `Last-Modified` 和 `Etag` HTTP 头实现客户端缓存。\n例如：\n\n```php\nuse yii\\filters\\HttpCache;\n\npublic function behaviors()\n{\n    return [\n        [\n            'class' => HttpCache::class,\n            'only' => ['index'],\n            'lastModified' => function ($action, $params) {\n                $q = new \\yii\\db\\Query();\n                return $q->from('user')->max('updated_at');\n            },\n        ],\n    ];\n}\n```\n\n更多关于使用 HttpCache 详情请参阅 [HTTP 缓存](caching-http.md) 一节。\n\n\n### [[yii\\filters\\PageCache|PageCache]] <span id=\"page-cache\"></span>\n\nPageCache 实现服务器端整个页面的缓存。如下示例所示，PageCache 应用在 `index` 动作，\n缓存整个页面 60 秒或 `post` 表的记录数发生变化。\n它也会根据不同应用语言保存不同的页面版本。\n\n```php\nuse yii\\filters\\PageCache;\nuse yii\\caching\\DbDependency;\n\npublic function behaviors()\n{\n    return [\n        'pageCache' => [\n            'class' => PageCache::class,\n            'only' => ['index'],\n            'duration' => 60,\n            'dependency' => [\n                'class' => DbDependency::class,\n                'sql' => 'SELECT COUNT(*) FROM post',\n            ],\n            'variations' => [\n                \\Yii::$app->language,\n            ]\n        ],\n    ];\n}\n```\n\n更多关于使用 PageCache 详情请参阅 [页面缓存](caching-page.md) 一节。\n\n\n### [[yii\\filters\\RateLimiter|RateLimiter]] <span id=\"rate-limiter\"></span>\n\nRateLimiter 根据 [漏桶算法](https://en.wikipedia.org/wiki/Leaky_bucket) 来实现速率限制。\n主要用在实现 RESTful APIs，更多关于该过滤器详情请参阅\n[Rate Limiting](rest-rate-limiting.md) 一节。\n\n\n### [[yii\\filters\\VerbFilter|VerbFilter]] <span id=\"verb-filter\"></span>\n\nVerbFilter 检查请求动作的 HTTP 请求方式是否允许执行，\n如果不允许，会抛出 HTTP 405异常。\n如下示例，VerbFilter 指定 CRUD 动作所允许的请求方式。\n\n```php\nuse yii\\filters\\VerbFilter;\n\npublic function behaviors()\n{\n    return [\n        'verbs' => [\n            'class' => VerbFilter::class,\n            'actions' => [\n                'index'  => ['get'],\n                'view'   => ['get'],\n                'create' => ['get', 'post'],\n                'update' => ['get', 'put', 'post'],\n                'delete' => ['post', 'delete'],\n            ],\n        ],\n    ];\n}\n```\n\n### [[yii\\filters\\Cors|Cors]] <span id=\"cors\"></span>\n\n跨域资源共享 [CORS](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS) \n机制允许一个网页的许多资源（例如字体、JavaScript等）\n这些资源可以通过其他域名访问获取。\n特别是 JavaScript 的 AJAX 调用可使用 XMLHttpRequest 机制，\n由于同源安全策略该跨域请求会被网页浏览器禁止。CORS 定义浏览器和服务器交互时哪些跨域请求允许和禁止。\n\n[[yii\\filters\\Cors|Cors filter]] 应在授权/认证过滤器之前定义，\n以保证 CORS 头部被发送。\n\n```php\nuse yii\\filters\\Cors;\nuse yii\\helpers\\ArrayHelper;\n\npublic function behaviors()\n{\n    return ArrayHelper::merge([\n        [\n            'class' => Cors::class,\n        ],\n    ], parent::behaviors());\n}\n```\n\n如果要将CORS过滤器添加到你的 API 中的 [[yii\\rest\\ActiveController]] 类，\n还要检查 [REST Controllers](rest-controllers.md#cors) 中的部分。\n\nCROS过滤器可以通过 [[yii\\filters\\Cors::$cors|$cors]] 属性进行调整。\n\n* `cors['Origin']`：定义允许来源的数组，可为 `['*']`（任何用户）或 `['https://www.myserver.net', 'https://www.myotherserver.com']`。 默认为 `['*']`。\n* `cors['Access-Control-Request-Method']`：允许动作数组如 `['GET', 'OPTIONS', 'HEAD']`。默认为 `['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']`。\n* `cors['Access-Control-Request-Headers']`：允许请求头部数组，可为 `['*']` 所有类型头部 或 `['X-Request-With']` 指定类型头部。默认为 `['*']`。\n* `cors['Access-Control-Allow-Credentials']`：定义当前请求是否使用证书，可为 `true`，`false` 或 `null`（不设置）。默认为 `null`。\n* `cors['Access-Control-Max-Age']`: 定义请求的有效时间，默认为 `86400`。\n\n例如，允许来源为 `https://www.myserver.net` 和方式为 `GET`，`HEAD` 和 `OPTIONS` 的 CORS 如下：\n\n```php\nuse yii\\filters\\Cors;\nuse yii\\helpers\\ArrayHelper;\n\npublic function behaviors()\n{\n    return ArrayHelper::merge([\n        [\n            'class' => Cors::class,\n            'cors' => [\n                'Origin' => ['https://www.myserver.net'],\n                'Access-Control-Request-Method' => ['GET', 'HEAD', 'OPTIONS'],\n            ],\n        ],\n    ], parent::behaviors());\n}\n```\n\n可以覆盖默认参数为每个动作调整 CORS 头部。例如，为 `login` 动作\n增加 `Access-Control-Allow-Credentials` 参数如下所示：\n\n```php\nuse yii\\filters\\Cors;\nuse yii\\helpers\\ArrayHelper;\n\npublic function behaviors()\n{\n    return ArrayHelper::merge([\n        [\n            'class' => Cors::class,\n            'cors' => [\n                'Origin' => ['https://www.myserver.net'],\n                'Access-Control-Request-Method' => ['GET', 'HEAD', 'OPTIONS'],\n            ],\n            'actions' => [\n                'login' => [\n                    'Access-Control-Allow-Credentials' => true,\n                ]\n            ]\n        ],\n    ], parent::behaviors());\n}\n```\n"
  },
  {
    "path": "docs/guide-zh-CN/structure-models.md",
    "content": "模型\n======\n\n模型是 [MVC](https://zh.wikipedia.org/wiki/MVC) 模式中的一部分，\n是代表业务数据、规则和逻辑的对象。\n\n可通过继承 [[yii\\base\\Model]] 或它的子类定义模型类，\n基类[[yii\\base\\Model]]支持许多实用的特性：\n\n* [属性](#attributes): 代表可像普通类属性或数组\n  一样被访问的业务数据;\n* [属性标签](#attribute-labels): 指定属性显示出来的标签;\n* [块赋值](#massive-assignment): 支持一步给许多属性赋值;\n* [验证规则](#validation-rules): 确保输入数据符合所申明的验证规则;\n* [数据导出](#data-exporting): 允许模型数据导出为自定义格式的数组。\n\n`Model` 类也是更多高级模型如[Active Record 活动记录](db-active-record.md)的基类，\n更多关于这些高级模型的详情请参考相关手册。\n\n> Info: 模型并不强制一定要继承[[yii\\base\\Model]]，但是由于很多组件支持[[yii\\base\\Model]]，\n  最好使用它做为模型基类。\n\n\n## 属性 <span id=\"attributes\"></span>\n\n模型通过 *属性* 来代表业务数据，每个属性像是模型的公有可访问属性，\n[[yii\\base\\Model::attributes()]] 指定模型所拥有的属性。\n\n可像访问一个对象属性一样访问模型的属性:\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// \"name\" 是ContactForm模型的属性\n$model->name = 'example';\necho $model->name;\n```\n\n也可像访问数组单元项一样访问属性，这要感谢[[yii\\base\\Model]]支持 \n[ArrayAccess 数组访问](https://www.php.net/manual/en/class.arrayaccess.php) \n和 [ArrayIterator 数组迭代器](https://www.php.net/manual/en/class.arrayiterator.php):\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// 像访问数组单元项一样访问属性\n$model['name'] = 'example';\necho $model['name'];\n\n// 迭代器遍历模型\nforeach ($model as $name => $value) {\n    echo \"$name: $value\\n\";\n}\n```\n\n\n### 定义属性 <span id=\"defining-attributes\"></span>\n\n默认情况下你的模型类直接从[[yii\\base\\Model]]继承，所有 *non-static public非静态公有* 成员变量都是属性。\n例如，下述`ContactForm` 模型类有四个属性`name`, `email`, `subject` and `body`，\n`ContactForm` 模型用来代表从HTML表单获取的输入数据。\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\n\nclass ContactForm extends Model\n{\n    public $name;\n    public $email;\n    public $subject;\n    public $body;\n}\n```\n\n\n另一种方式是可覆盖 [[yii\\base\\Model::attributes()]] \n来定义属性，该方法返回模型的属性名。\n例如 [[yii\\db\\ActiveRecord]] 返回对应数据表列名作为它的属性名，\n注意可能需要覆盖魔术方法如`__get()`,\n`__set()`使属性像普通对象属性被访问。\n\n\n### 属性标签 <span id=\"attribute-labels\"></span>\n\n当属性显示或获取输入时，经常要显示属性相关标签，\n例如假定一个属性名为`firstName`，\n在某些地方如表单输入或错误信息处，你可能想显示对终端用户来说更友好的 `First Name` 标签。\n\n可以调用 [[yii\\base\\Model::getAttributeLabel()]] 获取属性的标签，例如：\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// 显示为 \"Name\"\necho $model->getAttributeLabel('name');\n```\n\n默认情况下，属性标签通过[[yii\\base\\Model::generateAttributeLabel()]]方法自动从属性名生成. \n它会自动将驼峰式大小写变量名转换为多个首字母大写的单词，\n例如 `username` 转换为 `Username`，\n`firstName` 转换为 `First Name`。\n\n如果你不想用自动生成的标签，可以覆盖 [[yii\\base\\Model::attributeLabels()]] 方法明确指定属性标签，\n例如：\n\n```php\nnamespace app\\models;\n\nuse yii\\base\\Model;\n\nclass ContactForm extends Model\n{\n    public $name;\n    public $email;\n    public $subject;\n    public $body;\n\n    public function attributeLabels()\n    {\n        return [\n            'name' => 'Your name',\n            'email' => 'Your email address',\n            'subject' => 'Subject',\n            'body' => 'Content',\n        ];\n    }\n}\n```\n\n应用支持多语言的情况下，可翻译属性标签，\n可在 [[yii\\base\\Model::attributeLabels()|attributeLabels()]] 方法中定义，如下所示:\n\n```php\npublic function attributeLabels()\n{\n    return [\n        'name' => \\Yii::t('app', 'Your name'),\n        'email' => \\Yii::t('app', 'Your email address'),\n        'subject' => \\Yii::t('app', 'Subject'),\n        'body' => \\Yii::t('app', 'Content'),\n    ];\n}\n```\n\n甚至可以根据条件定义标签，例如通过使用模型的 [scenario场景](#scenarios)，\n可对相同的属性返回不同的标签。\n\n> Info: 属性标签是 [视图](structure-views.md)一部分，\n  但是在模型中声明标签通常非常方便，并可形成非常简洁可重用代码。\n\n\n## 场景 <span id=\"scenarios\"></span>\n\n模型可能在多个 *场景* 下使用，例如 `User` 模块可能会在收集用户登录输入，\n也可能会在用户注册时使用。在不同的场景下，\n模型可能会使用不同的业务规则和逻辑，\n例如 `email` 属性在注册时强制要求有，但在登陆时不需要。\n\n模型使用 [[yii\\base\\Model::scenario]] 属性保持使用场景的跟踪，\n默认情况下，模型支持一个名为 `default` 的场景，\n如下展示两种设置场景的方法:\n\n```php\n// 场景作为属性来设置\n$model = new User;\n$model->scenario = 'login';\n\n// 场景通过构造初始化配置来设置\n$model = new User(['scenario' => 'login']);\n```\n\n默认情况下，模型支持的场景由模型中申明的 [验证规则](#validation-rules) 来决定，\n但你可以通过覆盖[[yii\\base\\Model::scenarios()]]方法来自定义行为，\n如下所示：\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass User extends ActiveRecord\n{\n    const SCENARIO_LOGIN = 'login';\n    const SCENARIO_REGISTER = 'register';\n\n    public function scenarios()\n    {\n        return [\n            self::SCENARIO_LOGIN => ['username', 'password'],\n            self::SCENARIO_REGISTER => ['username', 'email', 'password'],\n        ];\n    }\n}\n```\n\n> Info: 在上述和下述的例子中，模型类都是继承[[yii\\db\\ActiveRecord]]，\n  因为多场景的使用通常发生在[Active Record](db-active-record.md) 类中.\n\n`scenarios()` 方法返回一个数组，数组的键为场景名，值为对应的 *active attributes活动属性*。\n活动属性可被 [块赋值](#massive-assignment) 并遵循[验证规则](#validation-rules)\n在上述例子中，`username` 和 `password` 在`login`场景中启用，在 `register` 场景中, \n除了 `username` and `password` 外 `email` 也被启用。\n\n`scenarios()` 方法默认实现会返回所有[[yii\\base\\Model::rules()]]方法申明的验证规则中的场景，\n当覆盖`scenarios()`时，如果你想在默认场景外使用新场景，\n可以编写类似如下代码：\n\n```php\nnamespace app\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass User extends ActiveRecord\n{\n    const SCENARIO_LOGIN = 'login';\n    const SCENARIO_REGISTER = 'register';\n\n    public function scenarios()\n    {\n        $scenarios = parent::scenarios();\n        $scenarios[self::SCENARIO_LOGIN] = ['username', 'password'];\n        $scenarios[self::SCENARIO_REGISTER] = ['username', 'email', 'password'];\n        return $scenarios;\n    }\n}\n```\n\n场景特性主要在[验证](#validation-rules) 和 [属性块赋值](#massive-assignment) 中使用。\n你也可以用于其他目的，\n例如可基于不同的场景定义不同的 [属性标签](#attribute-labels)。\n\n\n## 验证规则 <span id=\"validation-rules\"></span>\n\n当模型接收到终端用户输入的数据，\n数据应当满足某种规则(称为 *验证规则*, 也称为 *业务规则*)。\n例如假定`ContactForm`模型，你可能想确保所有属性不为空且 `email` 属性包含一个有效的邮箱地址，\n如果某个属性的值不满足对应的业务规则，\n相应的错误信息应显示，以帮助用户修正错误。\n\n可调用 [[yii\\base\\Model::validate()]] 来验证接收到的数据，\n该方法使用[[yii\\base\\Model::rules()]]申明的验证规则来验证每个相关属性，\n如果没有找到错误，会返回 true，\n否则它会将错误保存在 [[yii\\base\\Model::errors]] 属性中并返回false，例如：\n\n```php\n$model = new \\app\\models\\ContactForm;\n\n// 用户输入数据赋值到模型属性\n$model->attributes = \\Yii::$app->request->post('ContactForm');\n\nif ($model->validate()) {\n    // 所有输入数据都有效 all inputs are valid\n} else {\n    // 验证失败：$errors 是一个包含错误信息的数组\n    $errors = $model->errors;\n}\n```\n\n\n通过覆盖 [[yii\\base\\Model::rules()]] 方法指定模型\n属性应该满足的规则来申明模型相关验证规则。\n下述例子显示`ContactForm`模型申明的验证规则:\n\n```php\npublic function rules()\n{\n    return [\n        // name, email, subject 和 body 属性必须有值\n        [['name', 'email', 'subject', 'body'], 'required'],\n\n        // email 属性必须是一个有效的电子邮箱地址\n        ['email', 'email'],\n    ];\n}\n```\n\n一条规则可用来验证一个或多个属性，一个属性可对应一条或多条规则。\n更多关于如何申明验证规则的详情请参考 \n[验证输入](input-validation.md) 一节.\n\n有时你想一条规则只在某个 [场景](#scenarios) 下应用，为此你可以指定规则的 `on` 属性，\n如下所示:\n\n```php\npublic function rules()\n{\n    return [\n        // 在\"register\" 场景下 username, email 和 password 必须有值\n        [['username', 'email', 'password'], 'required', 'on' => 'register'],\n\n        // 在 \"login\" 场景下 username 和 password 必须有值\n        [['username', 'password'], 'required', 'on' => 'login'],\n    ];\n}\n```\n\n如果没有指定 `on` 属性，规则会在所有场景下应用， 在当前[[yii\\base\\Model::scenario|scenario]]\n下应用的规则称之为 *active rule活动规则*。\n\n一个属性只会属于`scenarios()`中定义的活动属性且在`rules()`\n申明对应一条或多条活动规则的情况下被验证。\n\n\n## 块赋值 <span id=\"massive-assignment\"></span>\n\n块赋值只用一行代码将用户所有输入填充到一个模型，非常方便，\n它直接将输入数据对应填充到 [[yii\\base\\Model::attributes]] 属性。\n以下两段代码效果是相同的，\n都是将终端用户输入的表单数据赋值到 `ContactForm` 模型的属性，\n明显地前一段块赋值的代码比后一段代码简洁且不易出错。\n\n```php\n$model = new \\app\\models\\ContactForm;\n$model->attributes = \\Yii::$app->request->post('ContactForm');\n```\n\n```php\n$model = new \\app\\models\\ContactForm;\n$data = \\Yii::$app->request->post('ContactForm', []);\n$model->name = isset($data['name']) ? $data['name'] : null;\n$model->email = isset($data['email']) ? $data['email'] : null;\n$model->subject = isset($data['subject']) ? $data['subject'] : null;\n$model->body = isset($data['body']) ? $data['body'] : null;\n```\n\n\n### 安全属性 <span id=\"safe-attributes\"></span>\n\n块赋值只应用在模型当前[[yii\\base\\Model::scenario|scenario]]\n场景[[yii\\base\\Model::scenarios()]]方法\n列出的称之为 *安全属性* 的属性上，例如，如果`User`模型申明以下场景，\n当当前场景为`login`时候，只有`username` and `password` 可被块赋值，\n其他属性不会被赋值。\n\n```php\npublic function scenarios()\n{\n    return [\n        'login' => ['username', 'password'],\n        'register' => ['username', 'email', 'password'],\n    ];\n}\n```\n\n> Info: 块赋值只应用在安全属性上，\n  因为你想控制哪些属性会被终端用户输入数据所修改，\n  例如，如果 `User` 模型有一个`permission`属性对应用户的权限，\n  你可能只想让这个属性在后台界面被管理员修改。\n\n由于默认[[yii\\base\\Model::scenarios()]]的实现会返回\n[[yii\\base\\Model::rules()]]所有属性和数据，\n如果不覆盖这个方法，表示所有只要出现在活动验证规则中的属性都是安全的。\n\n为此，提供一个特别的别名为 `safe` 的验证器来申明\n哪些属性是安全的不需要被验证，\n如下示例的规则申明 `title` 和 `description` 都为安全属性。\n\n```php\npublic function rules()\n{\n    return [\n        [['title', 'description'], 'safe'],\n    ];\n}\n```\n\n\n### 非安全属性 <span id=\"unsafe-attributes\"></span>\n\n如上所述，[[yii\\base\\Model::scenarios()]] 方法提供两个用处：定义哪些属性应被验证，定义哪些属性安全。\n在某些情况下，你可能想验证一个属性但不想让他是安全的，\n可在`scenarios()`方法中属性名加一个惊叹号 `!`。\n例如像如下的`secret`属性。\n\n```php\npublic function scenarios()\n{\n    return [\n        'login' => ['username', 'password', '!secret'],\n    ];\n}\n```\n\n当模型在 `login` 场景下，三个属性都会被验证，\n但只有 `username`和 `password` 属性会被块赋值，\n要对`secret`属性赋值，必须像如下例子明确对它赋值。\n\n```php\n$model->secret = $secret;\n```\n\nThe same can be done in `rules()` method:\n\n```php\npublic function rules()\n{\n    return [\n        [['username', 'password', '!secret'], 'required', 'on' => 'login']\n    ];\n}\n```\n\n在这种情况下，属性 `username`, `password` 和 `secret` 是必须的，但是 `secret`必须被明确指定。\n\n\n## 数据导出 <span id=\"data-exporting\"></span>\n\n模型通常要导出成不同格式，例如，你可能想将模型的一个集合转成JSON或Excel格式，\n导出过程可分解为两个步骤：\n\n- 模型转换成数组；\n- 数组转换成所需要的格式。\n\n你只需要关注第一步，因为第二步可被通用的\n数据转换器如[[yii\\web\\JsonResponseFormatter]]来完成。\n\n将模型转换为数组最简单的方式是使用 [[yii\\base\\Model::attributes]] 属性，\n例如：\n\n```php\n$post = \\app\\models\\Post::findOne(100);\n$array = $post->attributes;\n```\n\n[[yii\\base\\Model::attributes]] 属性会返回 *所有* \n[[yii\\base\\Model::attributes()]] 申明的属性的值。\n\n更灵活和强大的将模型转换为数组的方式是使用 [[yii\\base\\Model::toArray()]] 方法，\n它的行为默认和 [[yii\\base\\Model::attributes]] 相同，\n但是它允许你选择哪些称之为*字段*的数据项放入到结果数组中并同时被格式化。\n实际上，它是导出模型到 RESTful 网页服务开发的默认方法，\n详情请参阅[响应格式](rest-response-formatting.md).\n\n\n### 字段 <span id=\"fields\"></span>\n\n字段是模型通过调用[[yii\\base\\Model::toArray()]]\n生成的数组的单元名。\n\n默认情况下，字段名对应属性名，但是你可以通过覆盖\n[[yii\\base\\Model::fields()|fields()]] 和/或 \n[[yii\\base\\Model::extraFields()|extraFields()]] 方法来改变这种行为，\n两个方法都返回一个字段定义列表，`fields()` 方法定义的字段是默认字段，\n表示`toArray()`方法默认会返回这些字段。 `extraFields()`方法定义额外可用字段，\n通过`toArray()`方法指定`$expand`参数来返回这些额外可用字段。\n例如如下代码会返回`fields()`方法定义的所有字段和`extraFields()`方法定义的`prettyName` and `fullAddress`字段。\n\n```php\n$array = $model->toArray([], ['prettyName', 'fullAddress']);\n```\n\n可通过覆盖 `fields()` 来增加、删除、重命名和重定义字段，\n`fields()` 方法返回值应为数组，\n数组的键为字段名，数组的值为对应的可为属性名或匿名函数返回的字段定义对应的值。\n特使情况下，如果字段名和属性定义名相同，可以省略数组键，\n例如：\n\n```php\n// 明确列出每个字段，特别用于你想确保数据表或模型\n// 属性改变不会导致你的字段改变(保证后端的API兼容)。\npublic function fields()\n{\n    return [\n        // 字段名和属性名相同\n        'id',\n\n        // 字段名为 \"email\"，对应属性名为 \"email_address\"\n        'email' => 'email_address',\n\n        // 字段名为 \"name\", 值通过PHP代码返回\n        'name' => function () {\n            return $this->first_name . ' ' . $this->last_name;\n        },\n    ];\n}\n\n// 过滤掉一些字段，特别用于\n// 你想继承父类实现并不想用一些敏感字段\npublic function fields()\n{\n    $fields = parent::fields();\n\n    // 去掉一些包含敏感信息的字段\n    unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);\n\n    return $fields;\n}\n```\n\n> Warning: 由于模型的所有属性会被包含在导出数组，最好检查数据确保没包含敏感数据，\n> 如果有敏感数据，应覆盖 `fields()` 方法过滤掉，\n> 在上述列子中，我们选择过滤掉\n> `auth_key`, `password_hash` and `password_reset_token`。\n\n\n## 最佳实践 <span id=\"best-practices\"></span>\n\n模型是代表业务数据、规则和逻辑的中心地方，通常在很多地方重用，\n在一个设计良好的应用中，模型通常比\n[控制器](structure-controllers.md)代码多。\n\n归纳起来，模型\n\n* 可包含属性来展示业务数据;\n* 可包含验证规则确保数据有效和完整;\n* 可包含方法实现业务逻辑;\n* 不应直接访问请求，session和其他环境数据，\n  这些数据应该由[控制器](structure-controllers.md)传入到模型;\n* 应避免嵌入HTML或其他展示代码，这些代码最好在 [视图](structure-views.md)中处理;\n* 单个模型中避免太多的 [场景](#scenarios).\n\n在开发大型复杂系统时应经常考虑最后一条建议，\n在这些系统中，模型会很大并在很多地方使用，因此会包含需要规则集和业务逻辑，\n最后维护这些模型代码成为一个噩梦，\n因为一个简单修改会影响好多地方，\n为确保模型好维护，最好使用以下策略：\n\n* 定义可被多个 [应用主体](structure-applications.md) \n  或 [模块](structure-modules.md) 共享的模型基类集合。\n  这些模型类应包含通用的最小规则集合和逻辑。\n* 在每个使用模型的 [应用主体](structure-applications.md) 或 [模块](structure-modules.md)中，\n  通过继承对应的模型基类来定义具体的模型类，\n  具体模型类包含应用主体或模块指定的规则和逻辑。\n\n例如，在[高级应用模板](tutorial-advanced-app.md)，\n你可以定义一个模型基类`common\\models\\Post`，\n然后在前台应用中，定义并使用一个继承`common\\models\\Post`的具体模型类`frontend\\models\\Post`，\n在后台应用中可以类似地定义`backend\\models\\Post`。\n通过这种策略，你清楚`frontend\\models\\Post`只对应前台应用，如果你修改它，\n就无需担忧修改会影响后台应用。\n"
  },
  {
    "path": "docs/guide-zh-CN/structure-modules.md",
    "content": "模块\n=======\n\n模块是独立的软件单元，由[模型](structure-models.md)，[视图](structure-views.md)，\n[控制器](structure-controllers.md)和其他支持组件组成，\n终端用户可以访问在[应用主体](structure-applications.md)中已安装的模块的控制器，\n模块被当成小应用主体来看待，和[应用主体](structure-applications.md)不同的是，\n模块不能单独部署，必须属于某个应用主体。\n\n\n## 创建模块 <span id=\"creating-modules\"></span>\n\n模块被组织成一个称为 [[yii\\base\\Module::basePath|base path]] 的目录，\n在该目录中有子目录如 `controllers`，`models`，`views` 分别为对应控制器，模型，视图和其他代码，和应用非常类似。\n如下例子显示一个模型的目录结构：\n\n```\nforum/\n    Module.php                   模块类文件\n    controllers/                 包含控制器类文件\n        DefaultController.php    default 控制器类文件\n    models/                      包含模型类文件\n    views/                       包含控制器视图文件和布局文件\n        layouts/                 包含布局文件\n        default/                 包含 DefaultController 控制器视图文件\n            index.php            index 视图文件\n```\n\n\n### 模块类 <span id=\"module-classes\"></span>\n\n每个模块都有一个继承 [[yii\\base\\Module]] 的模块类，\n该类文件直接放在模块的 [[yii\\base\\Module::basePath|base path]] 目录下，\n并且能被 [自动加载](concept-autoloading.md)。当一个模块被访问，\n和 [应用主体实例](structure-applications.md)\n类似会创建该模块类唯一实例，模块实例用来帮模块内代码共享数据和组件。\n\n以下示例一个模块类大致定义：\n\n```php\nnamespace app\\modules\\forum;\n\nclass Module extends \\yii\\base\\Module\n{\n    public function init()\n    {\n        parent::init();\n\n        $this->params['foo'] = 'bar';\n        // ...  其他初始化代码 ...\n    }\n}\n```\n\n如果 `init()` 方法包含很多初始化模块属性代码，\n可将他们保存在[配置](concept-configurations.md) 并在 `init()` 中使用以下代码加载：\n\n```php\npublic function init()\n{\n    parent::init();\n    // 从config.php 加载配置来初始化模块\n    \\Yii::configure($this, require __DIR__ . '/config.php');\n}\n```\n\n`config.php` 配置文件可能包含以下内容，类似\n[应用主体配置](structure-applications.md#application-configurations)。\n\n```php\n<?php\nreturn [\n    'components' => [\n        // list of component configurations\n    ],\n    'params' => [\n        // list of parameters\n    ],\n];\n```\n\n\n### 模块中的控制器 <span id=\"controllers-in-modules\"></span>\n\n创建模块的控制器时，惯例是将控制器类放在模块类命名空间的 `controllers` 子命名空间中，\n也意味着要将控制器类文件放在模块\n[[yii\\base\\Module::basePath|base path]] 目录中的 `controllers` 子目录中。\n例如，上小节中要在 `forum` 模块中创建 `post` 控制器，\n应像如下申明控制器类：\n\n```php\nnamespace app\\modules\\forum\\controllers;\n\nuse yii\\web\\Controller;\n\nclass PostController extends Controller\n{\n    // ...\n}\n```\n\n可配置 [[yii\\base\\Module::controllerNamespace]] 属性来自定义控制器类的命名空间，\n如果一些控制器不再该命名空间下，可配置 [[yii\\base\\Module::controllerMap]] 属性让它们能被访问，\n这类似于 [应用主体配置](structure-applications.md#controller-map) 所做的。\n\n\n### 模块中的视图 <span id=\"views-in-modules\"></span>\n\n视图应放在模块的 [[yii\\base\\Module::basePath|base path]] 对应目录下的 `views` 目录，\n对于模块中控制器对应的视图文件应放在 `views/ControllerID` 目录下，\n其中 `ControllerID` 对应 [控制器 ID](structure-controllers.md#routes)。\n例如，假定控制器类为 `PostController`，\n目录对应模块[[yii\\base\\Module::basePath|base path]]目录下的 `views/post` 目录。\n\n模块可指定 [布局](structure-views.md#layouts)，它用在模块的控制器视图渲染。\n布局文件默认放在 `views/layouts` 目录下，\n可配置 [[yii\\base\\Module::layout]] 属性指定布局名，\n如果没有配置 `layout` 属性名，默认会使用应用的布局。\n\n\n### 模块中的控制台命令 <span id=\"console-commands-in-modules\"></span>\n\n您的模块也可以声明命令，这将通过 [控制台](tutorial-console.md) 模式可用。\n\n当 Yii 在控制台模式下执行并将其指向命令的命名空间时。想要在命令行中查看你的命令，\n你需要更改 [[yii\\base\\Module::controllerNamespace]] 属性。\n\n一种实现方法是在模块的 `init()` 方法中测试Yii应用程序的实例类型：\n\n```php\npublic function init()\n{\n    parent::init();\n    if (Yii::$app instanceof \\yii\\console\\Application) {\n        $this->controllerNamespace = 'app\\modules\\forum\\commands';\n    }\n}\n```\n\n然后您的命令将从命令行使用以下路由：\n\n```\nyii <module_id>/<command>/<sub_command>\n```\n\n## 使用模块 <span id=\"using-modules\"></span>\n\n要在应用中使用模块，只需要将模块加入到应用主体配置的[[yii\\base\\Application::modules|modules]]属性的列表中，\n如下代码的[应用主体配置](structure-applications.md#application-configurations) \n使用 `forum` 模块：\n\n```php\n[\n    'modules' => [\n        'forum' => [\n            'class' => 'app\\modules\\forum\\Module',\n            // ... 模块其他配置 ...\n        ],\n    ],\n]\n```\n\n[[yii\\base\\Application::modules|modules]] 属性使用模块配置数组，\n每个数组键为*模块 ID*，它标识该应用中唯一的模块，\n数组的值为用来创建模块的 [配置](concept-configurations.md)。\n\n\n### 路由 <span id=\"routes\"></span>\n\n和访问应用的控制器类似，[路由](structure-controllers.md#routes) \n也用在模块中控制器的寻址，\n模块中控制器的路由必须以模块 ID 开始，接下来为控制器 ID 和操作 ID。\n例如，假定应用使用一个名为 `forum` 模块，\n路由`forum/post/index` 代表模块中 `post` 控制器的 `index` 操作，\n如果路由只包含模块ID，默认为 `default` 的\n[[yii\\base\\Module::defaultRoute]] 属性来决定使用哪个控制器/操作，\n也就是说路由 `forum` 可能代表 `forum` 模块的 `default` 控制器。\n\n在 [[yii\\web\\UrlManager::parseRequest()]] 被触发之前应该添加模块 URL 管理器规则。\n这就意味着在模块的 `init()` 将不会起作用，因为模块将在路由开始处理时被初始化。\n因此应该在 [bootstrap stage](structure-extensions.md#bootstrapping-classes) 添加规则。 \n使用 [[\\yii\\web\\GroupUrlRule]] 去实现模块的 URL 规则也是一种很好的做法。  \n\n如果一个模块用于 [version API](rest-versioning.md)，\n它的 URL 规则应该直接添加到应用程序配置的 `urlManager` 中。\n\n\n### 访问模块 <span id=\"accessing-modules\"></span>\n\n在模块中，可能经常需要获取[模块类](#module-classes)的实例来访问模块ID，模块参数，模块组件等，\n可以使用如下语句来获取：\n\n```php\n$module = MyModuleClass::getInstance();\n```\n\n其中 `MyModuleClass` 对应你想要的模块类，\n`getInstance()` 方法返回当前请求的模块类实例，\n如果模块没有被请求，该方法会返回空，注意不需要手动创建一个模块类，\n因为手动创建的和Yii处理请求时自动创建的不同。\n\n> Info: 当开发模块时，你不能假定模块使用固定的 ID，\n  因为在应用或其他没模块中，模块可能会对应到任意的 ID，\n  为了获取模块 ID，应使用上述代码获取模块实例，\n  然后通过 `$module->id` 获取模块 ID。\n\n也可以使用如下方式访问模块实例:\n\n```php\n// 获取ID为 \"forum\" 的模块\n$module = \\Yii::$app->getModule('forum');\n\n// 获取处理当前请求控制器所属的模块\n$module = \\Yii::$app->controller->module;\n```\n\n第一种方式仅在你知道模块 ID 的情况下有效，\n第二种方式在你知道处理请求的控制器下使用。\n\n一旦获取到模块实例，可访问注册到模块的参数和组件，例如：\n\n```php\n$maxPostCount = $module->params['maxPostCount'];\n```\n\n\n### 引导启动模块 <span id=\"bootstrapping-modules\"></span>\n\n有些模块在每个请求下都有运行，[[yii\\debug\\Module|debug]] 模块就是这种，\n为此将这种模块加入到应用主体的 [[yii\\base\\Application::bootstrap|bootstrap]] 属性中。\n\n例如，如下示例的应用主体配置会确保 `debug` 模块每次都被加载：\n\n```php\n[\n    'bootstrap' => [\n        'debug',\n    ],\n\n    'modules' => [\n        'debug' => 'yii\\debug\\Module',\n    ],\n]\n```\n\n\n## 模块嵌套 <span id=\"nested-modules\"></span>\n\n模块可无限级嵌套，也就是说，模块可以包含另一个包含模块的模块，我们称前者为*父模块*，后者为*子模块*，\n子模块必须在父模块的 [[yii\\base\\Module::modules|modules]] 属性中申明，\n例如：\n\n```php\nnamespace app\\modules\\forum;\n\nclass Module extends \\yii\\base\\Module\n{\n    public function init()\n    {\n        parent::init();\n\n        $this->modules = [\n            'admin' => [\n                // 此处应考虑使用一个更短的命名空间\n                'class' => 'app\\modules\\forum\\modules\\admin\\Module',\n            ],\n        ];\n    }\n}\n```\n\n在嵌套模块中的控制器，它的路由应包含它所有祖先模块的ID，\n例如`forum/admin/dashboard/index` 代表\n在模块`forum`中子模块`admin`中`dashboard`控制器的`index`操作。\n\n> Info: [[yii\\base\\Module::getModule()|getModule()]] 方法只返回子模块的直属的\n父模块。[[yii\\base\\Application::loadedModules]] 保存了已加所有载模块的属性，包括两者的子模块和\n嵌套模块，并用他们的类名进行索引。\n\n## 从模块内部访问组件\n\n从 2.0.13 版本开始模块支持 [tree traversal](concept-service-locator.md#tree-traversal)。\n这允发模块开发人员通过作为其模块的服务定位器去引用（应用程序）组件。\n这意味着最好使用 `$module->get('db')` 而不是 `Yii::$app->get('db')`。\n在需要不同组件（配置）的情况下，\n模块开发者能够指定要用于模块的特定组件。\n\n例如，以下是部分应用程序的配置：\n\n```php\n'components' => [\n    'db' => [\n        'tablePrefix' => 'main_',\n        'class' => Connection::class,\n        'enableQueryCache' => false\n    ],\n],\n'modules' => [\n    'mymodule' => [\n        'components' => [\n            'db' => [\n                'tablePrefix' => 'module_',\n                'class' => Connection::class\n            ],\n        ],\n    ],\n],\n```\n\n应用程序数据表将会以 `main_` 作为前缀，而所有模块表都将以 `module_` 为前缀。\n注意上面的配置并未合并；例如模块的组件都将启用查询缓存，因为这是默认的设置。\n\n## 最佳实践 <span id=\"best-practices\"></span>\n\n模块在大型项目中常备使用，这些项目的特性可分组，\n每个组包含一些强相关的特性，\n每个特性组可以做成一个模块由特定的开发人员和开发组来开发和维护。\n\n在特性组上，使用模块也是重用代码的好方式，\n一些常用特性，如用户管理，评论管理，可以开发成模块，\n这样在相关项目中非常容易被重用。\n"
  },
  {
    "path": "docs/guide-zh-CN/structure-overview.md",
    "content": "总览\n========\n\nYii 应用参照[模型-视图-控制器 （MVC）](https://zh.wikipedia.org/wiki/MVC)\n设计模式来组织。 [模型](structure-models.md)代表数据、业务逻辑和规则；\n[视图](structure-views.md)展示模型的输出；[控制器](structure-controllers.md)\n接受出入并将其转换为[模型](structure-models.md)和[视图](structure-views.md)命令。\n\n除了 MVC, Yii 应用还有以下部分：\n\n* [入口脚本](structure-entry-scripts.md)：终端用户能直接访问的 PHP 脚本，\n  负责启动一个请求处理周期。\n* [应用](structure-applications.md)：能全局范围内访问的对象，\n  管理协调组件来完成请求.\n* [应用组件](structure-application-components.md)：在应用中注册的对象，\n  提供不同的功能来完成请求。\n* [模块](structure-modules.md)：包含完整 MVC 结构的独立包，\n  一个应用可以由多个模块组建。 \n* [过滤器](structure-filters.md)：控制器在处理请求之前或之后\n  需要触发执行的代码。\n* [小部件](structure-widgets.md)：可嵌入到[视图](structure-views.md)中的对象，\n  可包含控制器逻辑，可被不同视图重复调用。\n\n下面的示意图展示了 Yii 应用的静态结构：\n\n![Yii应用静态结构](images/application-structure.png)\n"
  },
  {
    "path": "docs/guide-zh-CN/structure-views.md",
    "content": "视图\n=====\n\n视图是 [MVC](https://zh.wikipedia.org/wiki/MVC) 模式中的一部分。\n它是展示数据到终端用户的代码，在网页应用中，\n根据*视图模板*来创建视图，视图模板为PHP脚本文件，\n主要包含HTML代码和展示类PHP代码，通过[[yii\\web\\View|view]]应用组件来管理，\n该组件主要提供通用方法帮助视图构造和渲染，\n简单起见，我们称视图模板或视图模板文件为视图。\n\n\n## 创建视图 <span id=\"creating-views\"></span>\n\n如前所述，视图为包含HTML和PHP代码的PHP脚本，如下代码为一个登录表单的视图，\n可看到PHP代码用来生成动态内容如页面标题和表单，\nHTML代码把它组织成一个漂亮的HTML页面。\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n/**\n * @var \\yii\\web\\View $this\n * @var \\yii\\widgets\\ActiveForm $form\n * @var \\app\\models\\LoginForm $model\n */\n\n$this->title = 'Login';\n?>\n<h1><?= Html::encode($this->title) ?></h1>\n\n<p>Please fill out the following fields to login:</p>\n\n<?php $form = ActiveForm::begin(); ?>\n    <?= $form->field($model, 'username') ?>\n    <?= $form->field($model, 'password')->passwordInput() ?>\n    <?= Html::submitButton('Login') ?>\n<?php ActiveForm::end(); ?>\n```\n\n在视图中，可访问 `$this` 指向 [[yii\\web\\View|view component]] \n来管理和渲染这个视图文件。\n\n除了 `$this`之外，上述示例中的视图有其他预定义变量如 `$model`，\n这些变量代表从[控制器](structure-controllers.md)\n或其他触发[视图渲染](#rendering-views)的对象 *传入* 到视图的数据。\n\n> Tip: 将预定义变量列到视图文件头部注释处，\n  这样可被IDE编辑器识别，也是生成视图文档的好方法。\n\n\n### 安全 <span id=\"security\"></span>\n\n当创建生成HTML页面的视图时，在显示之前将用户输入数据进行转码和过滤非常重要，\n否则，你的应用可能会被\n[跨站脚本](https://zh.wikipedia.org/wiki/%E8%B7%A8%E7%B6%B2%E7%AB%99%E6%8C%87%E4%BB%A4%E7%A2%BC) 攻击。\n\n要显示纯文本，先调用 [[yii\\helpers\\Html::encode()]] 进行转码，\n例如如下代码将用户名在显示前先转码：\n\n```php\n<?php\nuse yii\\helpers\\Html;\n?>\n\n<div class=\"username\">\n    <?= Html::encode($user->name) ?>\n</div>\n```\n\n要显示HTML内容，先调用 [[yii\\helpers\\HtmlPurifier]] 过滤内容，\n例如如下代码将提交内容在显示前先过滤：\n\n```php\n<?php\nuse yii\\helpers\\HtmlPurifier;\n?>\n\n<div class=\"post\">\n    <?= HtmlPurifier::process($post->text) ?>\n</div>\n```\n\n> Tip: HTMLPurifier在保证输出数据安全上做的不错，但性能不佳，如果你的应用需要高性能可考虑\n  [缓存](caching-overview.md) 过滤后的结果。\n\n\n### 组织视图 <span id=\"organizing-views\"></span>\n\n与 [控制器](structure-controllers.md) 和 [模型](structure-models.md) 类似，在组织视图上有一些约定：\n\n* 控制器渲染的视图文件默认放在 `@app/views/ControllerID` 目录下，\n  其中 `ControllerID` 对应 [控制器 ID](structure-controllers.md#routes),\n  例如控制器类为 `PostController`，视图文件目录应为 `@app/views/post`，\n  控制器类 `PostCommentController`对应的目录为 `@app/views/post-comment`，\n  如果是模块中的控制器，目录应为 [[yii\\base\\Module::basePath|module directory]] 模块目录下的 `views/ControllerID` 目录；\n* 对于 [小部件](structure-widgets.md) 渲染的视图文件默认放在 `WidgetPath/views` 目录，\n  其中 `WidgetPath` 代表小部件类文件所在的目录；\n* 对于其他对象渲染的视图文件，建议遵循和小部件相似的规则。\n\n可覆盖控制器或小部件的 [[yii\\base\\ViewContextInterface::getViewPath()]] \n方法来自定义视图文件默认目录。\n\n\n## 渲染视图 <span id=\"rendering-views\"></span>\n\n可在 [控制器](structure-controllers.md), [小部件](structure-widgets.md), 或其他地方调用渲染视图方法来渲染视图，\n该方法类似以下格式：\n\n```\n/**\n * @param string $view 视图名或文件路径，由实际的渲染方法决定\n * @param array $params 传递给视图的数据\n * @return string 渲染结果\n */\nmethodName($view, $params = [])\n```\n\n\n### 控制器中渲染 <span id=\"rendering-in-controllers\"></span>\n\n在 [控制器](structure-controllers.md) 中，可调用以下控制器方法来渲染视图：\n\n* [[yii\\base\\Controller::render()|render()]]: 渲染一个 [视图名](#named-views) 并使用一个 [布局](#layouts)\n  返回到渲染结果。\n* [[yii\\base\\Controller::renderPartial()|renderPartial()]]: 渲染一个 [视图名](#named-views) 并且不使用布局。\n* [[yii\\web\\Controller::renderAjax()|renderAjax()]]: 渲染一个 [视图名](#named-views) 并且不使用布局，\n  并注入所有注册的JS/CSS脚本和文件，通常使用在响应AJAX网页请求的情况下。\n* [[yii\\base\\Controller::renderFile()|renderFile()]]: 渲染一个视图文件目录或\n  [别名](concept-aliases.md)下的视图文件。\n* [[yii\\base\\Controller::renderContent()|renderContent()]]: renders a static string by embedding it into\n  the currently applicable [layout](#layouts). This method is available since version 2.0.1.\n\n例如：\n\n```php\nnamespace app\\controllers;\n\nuse Yii;\nuse app\\models\\Post;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\n\nclass PostController extends Controller\n{\n    public function actionView($id)\n    {\n        $model = Post::findOne($id);\n        if ($model === null) {\n            throw new NotFoundHttpException;\n        }\n\n        // 渲染一个名称为\"view\"的视图并使用布局\n        return $this->render('view', [\n            'model' => $model,\n        ]);\n    }\n}\n```\n\n\n### 小部件中渲染 <span id=\"rendering-in-widgets\"></span>\n\n在 [小部件](structure-widgets.md) 中，可调用以下小部件方法来渲染视图：\n\n* [[yii\\base\\Widget::render()|render()]]: 渲染一个 [视图名](#named-views).\n* [[yii\\base\\Widget::renderFile()|renderFile()]]: 渲染一个视图文件目录或\n  [别名](concept-aliases.md)下的视图文件。\n\n例如：\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Widget;\nuse yii\\helpers\\Html;\n\nclass ListWidget extends Widget\n{\n    public $items = [];\n\n    public function run()\n    {\n        // 渲染一个名为 \"list\" 的视图\n        return $this->render('list', [\n            'items' => $this->items,\n        ]);\n    }\n}\n```\n\n\n### 视图中渲染 <span id=\"rendering-in-views\"></span>\n\n可以在视图中渲染另一个视图，可以调用[[yii\\base\\View|view component]]视图组件提供的以下方法：\n\n- [[yii\\base\\View::render()|render()]]: 渲染一个 [视图名](#named-views).\n- [[yii\\web\\View::renderAjax()|renderAjax()]]: 渲染一个 [视图名](#named-views)\n  并注入所有注册的JS/CSS脚本和文件，通常使用在响应AJAX网页请求的情况下。\n* [[yii\\base\\View::renderFile()|renderFile()]]: 渲染一个视图文件目录或\n  [别名](concept-aliases.md)下的视图文件。\n\n例如，视图中的如下代码会渲染该视图所在目录下的 `_overview.php` 视图文件，\n记住视图中 `$this` 对应 [[yii\\base\\View|view]] 组件:\n\n```php\n<?= $this->render('_overview') ?>\n```\n\n\n### 其他地方渲染 <span id=\"rendering-in-other-places\"></span>\n\n在任何地方都可以通过表达式 `Yii::$app->view` 访问 [[yii\\base\\View|view]] 应用组件，\n调用它的如前所述的方法渲染视图，例如：\n\n```php\n// 显示视图文件 \"@app/views/site/license.php\"\necho \\Yii::$app->view->renderFile('@app/views/site/license.php');\n```\n\n\n### 视图名 <span id=\"named-views\"></span>\n\n渲染视图时，可指定一个视图名或视图文件路径/别名，大多数情况下使用前者因为前者简洁灵活，\n我们称用名字的视图为 *视图名*.\n\n视图名可以依据以下规则到对应的视图文件路径：\n\n- 视图名可省略文件扩展名，这种情况下使用 `.php` 作为扩展，\n  视图名 `about` 对应到 `about.php` 文件名；\n- 视图名以双斜杠 `//` 开头，对应的视图文件路径为 `@app/views/ViewName`，\n  也就是说视图文件在 [[yii\\base\\Application::viewPath|application's view path]] 路径下找，\n  例如 `//site/about` 对应到 `@app/views/site/about.php`。\n- 视图名以单斜杠`/`开始，视图文件路径以当前使用[模块](structure-modules.md) \n  的[[yii\\base\\Module::viewPath|view path]]开始，\n  如果不存在模块，使用`@app/views/ViewName`开始，例如，如果当前模块为`user`， `/user/create` 对应成\n  `@app/modules/user/views/user/create.php`, \n  如果不在模块中，`/user/create`对应`@app/views/user/create.php`。\n- 如果 [[yii\\base\\View::context|context]] 渲染视图 并且上下文实现了 [[yii\\base\\ViewContextInterface]],\n  视图文件路径由上下文的 [[yii\\base\\ViewContextInterface::getViewPath()|view path]] 开始，\n  这种主要用在控制器和小部件中渲染视图，例如\n  如果上下文为控制器`SiteController`，`site/about` 对应到 `@app/views/site/about.php`。\n- 如果视图渲染另一个视图，包含另一个视图文件的目录以当前视图的文件路径开始，\n  例如被视图`@app/views/post/index.php` 渲染的\n  `item` 对应到 `@app/views/post/item`。\n\n根据以上规则，在控制器中 `app\\controllers\\PostController` 调用 `$this->render('view')`，\n实际上渲染 `@app/views/post/view.php` 视图文件，当在该视图文件中调用 `$this->render('_overview')`\n会渲染 `@app/views/post/_overview.php` 视图文件。\n\n\n### 视图中访问数据 <span id=\"accessing-data-in-views\"></span>\n\n在视图中有两种方式访问数据：推送和拉取。\n\n推送方式是通过视图渲染方法的第二个参数传递数据，\n数据格式应为名称-值的数组，\n视图渲染时，调用PHP `extract()` 方法将该数组转换为视图可访问的变量。\n例如，如下控制器的渲染视图代码推送2个变量到 \n`report` 视图：`$foo = 1` 和 `$bar = 2`。\n\n```php\necho $this->render('report', [\n    'foo' => 1,\n    'bar' => 2,\n]);\n```\n\n拉取方式可让视图从[[yii\\base\\View|view component]]视图组件或其他对象中主动获得数据(如`Yii::$app`)，\n在视图中使用如下表达式`$this->context`可获取到控制器ID，\n可让你在`report`视图中获取控制器的任意属性或方法，\n如以下代码获取控制器ID。\n\n```php\nThe controller ID is: <?= $this->context->id ?>\n```\n\n推送方式让视图更少依赖上下文对象，是视图获取数据优先使用方式，\n缺点是需要手动构建数组，有些繁琐，\n在不同地方渲染时容易出错。\n\n\n### 视图间共享数据 <span id=\"sharing-data-among-views\"></span>\n\n[[yii\\base\\View|view component]]视图组件提供[[yii\\base\\View::params|params]]\n参数属性来让不同视图共享数据。\n\n例如在`about`视图中，\n可使用如下代码指定当前breadcrumbs的当前部分。\n\n```php\n$this->params['breadcrumbs'][] = 'About Us';\n```\n\n在[布局](#layouts)文件（也是一个视图）中，可使用依次加入到[[yii\\base\\View::params|params]]数组的值来\n生成显示breadcrumbs:\n\n```php\n<?= yii\\widgets\\Breadcrumbs::widget([\n    'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],\n]) ?>\n```\n\n\n## 布局 <span id=\"layouts\"></span>\n\n布局是一种特殊的视图，代表多个视图的公共部分，\n例如，大多数Web应用共享相同的页头和页尾，\n在每个视图中重复相同的页头和页尾，更好的方式是将这些公共放到一个布局中，\n渲染内容视图后在合适的地方嵌入到布局中。\n\n\n### 创建布局 <span id=\"creating-layouts\"></span>\n\n由于布局也是视图，它可像普通视图一样创建，布局默认存储在`@app/views/layouts`路径下，\n[模块](structure-modules.md)中使用的布局应存储在\n[[yii\\base\\Module::basePath|module directory]]模块目录\n下的`views/layouts`路径下，\n可配置[[yii\\base\\Module::layoutPath]]来自定义应用或模块的布局默认路径。\n\n如下示例为一个布局大致内容，注意作为示例，简化了很多代码，\n在实际中，你可能想添加更多内容，如头部标签，主菜单等。\n\n```php\n<?php\nuse yii\\helpers\\Html;\n\n/**\n * @var \\yii\\web\\View $this\n * @var string $content 字符串\n */\n?>\n<?php $this->beginPage() ?>\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\"/>\n    <?= Html::csrfMetaTags() ?>\n    <title><?= Html::encode($this->title) ?></title>\n    <?php $this->head() ?>\n</head>\n<body>\n<?php $this->beginBody() ?>\n    <header>My Company</header>\n    <?= $content ?>\n    <footer>&copy; 2014 by My Company</footer>\n<?php $this->endBody() ?>\n</body>\n</html>\n<?php $this->endPage() ?>\n```\n\n如上所示，布局生成每个页面通用的HTML标签，在`<body>`标签中，打印`$content`变量，\n`$content`变量代表当[[yii\\base\\Controller::render()]]\n控制器渲染方法调用时传递到布局的内容视图渲染结果。\n\n大多数视图应调用上述代码中的如下方法，\n这些方法触发关于渲染过程的事件，\n这样其他地方注册的脚本和标签会添加到这些方法调用的地方。\n\n- [[yii\\base\\View::beginPage()|beginPage()]]: 该方法应在布局的开始处调用，\n  它触发表明页面开始的 [[yii\\base\\View::EVENT_BEGIN_PAGE|EVENT_BEGIN_PAGE]] 事件。\n- [[yii\\base\\View::endPage()|endPage()]]: 该方法应在布局的结尾处调用，\n  它触发表明页面结尾的 [[yii\\base\\View::EVENT_END_PAGE|EVENT_END_PAGE]] 事件。\n- [[yii\\web\\View::head()|head()]]: 该方法应在HTML页面的`<head>`标签中调用，\n  它生成一个占位符，在页面渲染结束时会被注册的头部HTML代码\n  （如，link标签, meta标签）替换。\n- [[yii\\web\\View::beginBody()|beginBody()]]: 该方法应在`<body>`标签的开始处调用，\n  它触发 [[yii\\web\\View::EVENT_BEGIN_BODY|EVENT_BEGIN_BODY]] 事件并生成一个占位符，\n  会被注册的HTML代码（如JavaScript）在页面主体开始处替换。\n- [[yii\\web\\View::endBody()|endBody()]]: 该方法应在`<body>`标签的结尾处调用，\n  它触发 [[yii\\web\\View::EVENT_END_BODY|EVENT_END_BODY]] 事件并生成一个占位符，\n  会被注册的HTML代码（如JavaScript）在页面主体结尾处替换。\n\n\n### 布局中访问数据 <span id=\"accessing-data-in-layouts\"></span>\n\n在布局中可访问两个预定义变量：`$this` 和 `$content`，\n前者对应和普通视图类似的[[yii\\base\\View|view]] 视图组件\n后者包含调用[[yii\\base\\Controller::render()|render()]]方法渲染内容视图的结果。\n\n如果想在布局中访问其他数据，必须使用[视图中访问数据](#accessing-data-in-views)一节介绍的拉取方式，\n如果想从内容视图中传递数据到布局，\n可使用[视图间共享数据](#sharing-data-among-views)一节中的方法。\n\n\n### 使用布局 <span id=\"using-layouts\"></span>\n\n如[控制器中渲染](#rendering-in-controllers)一节描述，\n当控制器调用[[yii\\base\\Controller::render()|render()]]\n方法渲染视图时，会同时使用布局到渲染结果中，默认会使用`@app/views/layouts/main.php`布局文件。\n\n可配置[[yii\\base\\Application::layout]] 或 [[yii\\base\\Controller::layout]] 使用其他布局文件，\n前者管理所有控制器的布局，后者覆盖前者来控制单个控制器布局。\n例如，如下代码使 `post` 控制器渲染视图时使用 `@app/views/layouts/post.php` 作为布局文件，\n假如 `layout` 属性没改变，\n控制器默认使用 `@app/views/layouts/main.php` 作为布局文件。\n \n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass PostController extends Controller\n{\n    public $layout = 'post';\n    \n    // ...\n}\n```\n\n对于模块中的控制器，可配置模块的 [[yii\\base\\Module::layout|layout]] \n属性指定布局文件应用到模块的所有控制器。\n\n由于`layout` 可在不同层级（控制器、模块，应用）配置，\n在幕后Yii使用两步来决定控制器实际使用的布局。\n\n第一步，它决定布局的值和上下文模块：\n\n- 如果控制器的 [[yii\\base\\Controller::layout]] 属性不为空null，使用它作为布局的值，\n  控制器的 [[yii\\base\\Controller::module|module]]模块 作为上下文模块。\n- 如果 [[yii\\base\\Controller::layout|layout]] 为空，从控制器的祖先模块（包括应用） 开始找\n  第一个[[yii\\base\\Module::layout|layout]] 属性不为空的模块，使用该模块作为上下文模块，\n  并将它的[[yii\\base\\Module::layout|layout]] 的值作为布局的值，\n  如果都没有找到，表示不使用布局。\n  \n第二步，它决定第一步中布局的值和上下文模块对应到实际的布局文件，\n布局的值可为：\n\n- 路径别名 (如 `@app/views/layouts/main`).\n- 绝对路径 (如 `/main`): 布局的值以斜杠开始，\n  在应用的[[yii\\base\\Application::layoutPath|layout path]] 布局路径\n  中查找实际的布局文件，布局路径默认为 `@app/views/layouts`。\n- 相对路径 (如 `main`): 在上下文模块的[[yii\\base\\Module::layoutPath|layout path]]布局路径中查找实际的布局文件，\n  布局路径默认为[[yii\\base\\Module::basePath|module directory]]\n  模块目录下的`views/layouts` 目录。\n- 布尔值 `false`: 不使用布局。\n\n布局的值没有包含文件扩展名，默认使用 `.php`作为扩展名。\n\n\n### 嵌套布局 <span id=\"nested-layouts\"></span>\n\n有时候你想嵌套一个布局到另一个，例如，在Web站点不同地方，想使用不同的布局，\n同时这些布局共享相同的生成全局HTML5页面结构的基本布局，可以在子布局中调用\n[[yii\\base\\View::beginContent()|beginContent()]] 和[[yii\\base\\View::endContent()|endContent()]]\n方法，如下所示：\n\n```php\n<?php $this->beginContent('@app/views/layouts/base.php'); ?>\n\n...child layout content here...\n\n<?php $this->endContent(); ?>\n```\n\n如上所示，子布局内容应在 [[yii\\base\\View::beginContent()|beginContent()]] 和\n[[yii\\base\\View::endContent()|endContent()]] 方法之间，传给 [[yii\\base\\View::beginContent()|beginContent()]]\n的参数指定父布局，父布局可为布局文件或别名。\n\n使用以上方式可多层嵌套布局。\n\n\n### 使用数据块 <span id=\"using-blocks\"></span>\n\n数据块可以在一个地方指定视图内容在另一个地方显示，通常和布局一起使用，\n例如，可在内容视图中定义数据块在布局中显示它。\n\n调用 [[yii\\base\\View::beginBlock()|beginBlock()]] 和 [[yii\\base\\View::endBlock()|endBlock()]] 来定义数据块，\n使用 `$view->blocks[$blockID]` 访问该数据块，\n其中 `$blockID` 为定义数据块时指定的唯一标识ID。\n\n如下实例显示如何在内容视图中使用数据块让布局使用。\n\n首先，在内容视图中定一个或多个数据块：\n\n```php\n...\n\n<?php $this->beginBlock('block1'); ?>\n\n...content of block1...\n\n<?php $this->endBlock(); ?>\n\n...\n\n<?php $this->beginBlock('block3'); ?>\n\n...content of block3...\n\n<?php $this->endBlock(); ?>\n```\n\n然后，在布局视图中，数据块可用的话会渲染数据块，\n如果数据未定义则显示一些默认内容。\n\n```php\n...\n<?php if (isset($this->blocks['block1'])): ?>\n    <?= $this->blocks['block1'] ?>\n<?php else: ?>\n    ... default content for block1 ...\n<?php endif; ?>\n\n...\n\n<?php if (isset($this->blocks['block2'])): ?>\n    <?= $this->blocks['block2'] ?>\n<?php else: ?>\n    ... default content for block2 ...\n<?php endif; ?>\n\n...\n\n<?php if (isset($this->blocks['block3'])): ?>\n    <?= $this->blocks['block3'] ?>\n<?php else: ?>\n    ... default content for block3 ...\n<?php endif; ?>\n...\n```\n\n\n## 使用视图组件 <span id=\"using-view-components\"></span>\n\n[[yii\\base\\View|View components]]视图组件提供许多视图相关特性，\n可创建[[yii\\base\\View]]或它的子类实例来获取视图组件，大多数情况下主要使用 `view` 应用组件，\n可在[应用配置](structure-applications.md#application-configurations)中配置该组件，\n如下所示：\n\n```php\n[\n    // ...\n    'components' => [\n        'view' => [\n            'class' => 'app\\components\\View',\n        ],\n        // ...\n    ],\n]\n```\n\n视图组件提供如下实用的视图相关特性，每项详情会在独立章节中介绍：\n\n- [主题](output-theming.md): 允许为你的Web站点开发和修改主题；\n- [片段缓存](caching-fragment.md): 允许你在Web页面中缓存片段；\n- [客户脚本处理](output-client-scripts.md): 支持CSS 和 JavaScript 注册和渲染；\n- [资源包处理](structure-assets.md): 支持 [资源包](structure-assets.md)的注册和渲染；\n- [模板引擎](tutorial-template-engines.md): 允许你使用其他模板引擎，如\n  [Twig](https://twig.symfony.com/), [Smarty](https://www.smarty.net/)。\n\n开发Web页面时，也可能频繁使用以下实用的小特性。\n\n\n### 设置页面标题 <span id=\"setting-page-titles\"></span>\n\n每个Web页面应有一个标题，正常情况下标题的标签显示在 [布局](#layouts)中，\n但是实际上标题大多由内容视图而不是布局来决定，为解决这个问题， [[yii\\web\\View]] 提供\n[[yii\\web\\View::title|title]] 标题属性可让标题信息从内容视图传递到布局中。\n\n为利用这个特性，在每个内容视图中设置页面标题，如下所示：\n\n```php\n<?php\n$this->title = 'My page title';\n?>\n```\n\n然后在视图中，确保在 `<head>` 段中有如下代码：\n\n```php\n<title><?= Html::encode($this->title) ?></title>\n```\n\n\n### 注册Meta元标签 <span id=\"registering-meta-tags\"></span>\n\nWeb页面通常需要生成各种元标签提供给不同的浏览器，\n如`<head>`中的页面标题，元标签通常在布局中生成。\n\n如果想在内容视图中生成元标签，可在内容视图中调用[[yii\\web\\View::registerMetaTag()]]方法，\n如下所示：\n\n```php\n<?php\n$this->registerMetaTag(['name' => 'keywords', 'content' => 'yii, framework, php']);\n?>\n```\n\n以上代码会在视图组件中注册一个 \"keywords\" 元标签，\n在布局渲染后会渲染该注册的元标签，\n然后，如下HTML代码会插入到布局中调用[[yii\\web\\View::head()]]方法处：\n\n```php\n<meta name=\"keywords\" content=\"yii, framework, php\">\n```\n\n注意如果多次调用 [[yii\\web\\View::registerMetaTag()]] 方法，\n它会注册多个元标签，注册时不会检查是否重复。\n\n为确保每种元标签只有一个，可在调用方法时指定键作为第二个参数，\n例如，如下代码注册两次 \"description\" 元标签，但是只会渲染第二个。\n\n```html\n$this->registerMetaTag(['name' => 'description', 'content' => 'This is my cool website made with Yii!'], 'description');\n$this->registerMetaTag(['name' => 'description', 'content' => 'This website is about funny raccoons.'], 'description');\n```\n\n\n### 注册链接标签 <span id=\"registering-link-tags\"></span>\n\n和 [Meta标签](#adding-meta-tags) 类似，链接标签有时很实用，如自定义网站图标，指定Rss订阅，或授权OpenID到其他服务器。\n可以和元标签相似的方式调用[[yii\\web\\View::registerLinkTag()]]，\n例如，在内容视图中注册链接标签如下所示：\n\n```php\n$this->registerLinkTag([\n    'title' => 'Live News for Yii',\n    'rel' => 'alternate',\n    'type' => 'application/rss+xml',\n    'href' => 'https://www.yiiframework.com/rss.xml/',\n]);\n```\n\n上述代码会转换成\n\n```html\n<link title=\"Live News for Yii\" rel=\"alternate\" type=\"application/rss+xml\" href=\"https://www.yiiframework.com/rss.xml/\">\n```\n\n和 [[yii\\web\\View::registerMetaTag()|registerMetaTags()]] 类似，\n调用[[yii\\web\\View::registerLinkTag()|registerLinkTag()]] 指定键来避免生成重复链接标签。\n\n\n## 视图事件 <span id=\"view-events\"></span>\n\n[[yii\\base\\View|View components]] 视图组件会在视图渲染过程中触发几个事件，\n可以在内容发送给终端用户前，响应这些事件来添加内容到视图中或调整渲染结果。\n\n- [[yii\\base\\View::EVENT_BEFORE_RENDER|EVENT_BEFORE_RENDER]]: 在控制器渲染文件开始时触发，\n  该事件可设置 [[yii\\base\\ViewEvent::isValid]] 为 false 取消视图渲染。\n- [[yii\\base\\View::EVENT_AFTER_RENDER|EVENT_AFTER_RENDER]]: 在布局中调用 [[yii\\base\\View::afterRender()]] 时触发，\n  该事件可获取[[yii\\base\\ViewEvent::output]]的渲染结果，\n  可修改该属性来修改渲染结果。\n- [[yii\\base\\View::EVENT_BEGIN_PAGE|EVENT_BEGIN_PAGE]]: 在布局调用 [[yii\\base\\View::beginPage()]] 时触发；\n- [[yii\\base\\View::EVENT_END_PAGE|EVENT_END_PAGE]]: 在布局调用 [[yii\\base\\View::endPage()]] 是触发；\n- [[yii\\web\\View::EVENT_BEGIN_BODY|EVENT_BEGIN_BODY]]: 在布局调用 [[yii\\web\\View::beginBody()]] 时触发；\n- [[yii\\web\\View::EVENT_END_BODY|EVENT_END_BODY]]: 在布局调用 [[yii\\web\\View::endBody()]] 时触发。\n\n例如，如下代码将当前日期添加到页面结尾处：\n\n```php\n\\Yii::$app->view->on(View::EVENT_END_BODY, function () {\n    echo date('Y-m-d');\n});\n```\n\n\n## 渲染静态页面 <span id=\"rendering-static-pages\"></span>\n\n静态页面指的是大部分内容为静态的不需要控制器传递\n动态数据的Web页面。\n\n可将HTML代码放置在视图中，在控制器中使用以下代码输出静态页面：\n\n```php\npublic function actionAbout()\n{\n    return $this->render('about');\n}\n```\n\n如果Web站点包含很多静态页面，多次重复相似的代码显得很繁琐，\n为解决这个问题，可以使用一个在控制器中称为 [[yii\\web\\ViewAction]] 的[独立动作](structure-controllers.md#standalone-actions)。\n例如：\n\n```php\nnamespace app\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actions()\n    {\n        return [\n            'page' => [\n                'class' => 'yii\\web\\ViewAction',\n            ],\n        ];\n    }\n}\n```\n\n现在如果你在`@app/views/site/pages`目录下创建名为 `about` 的视图，\n可通过如下url显示该视图：\n\n```\nhttp://localhost/index.php?r=site/page&view=about\n```\n\n`GET` 中 `view` 参数告知 [[yii\\web\\ViewAction]] 动作请求哪个视图，然后操作在\n`@app/views/site/pages`目录下寻找该视图，可配置 [[yii\\web\\ViewAction::viewPrefix]]\n修改搜索视图的目录。\n\n\n## 最佳实践 <span id=\"best-practices\"></span>\n\n视图负责将模型的数据展示用户想要的格式，总之，视图\n\n* 应主要包含展示代码，如HTML, 和简单的PHP代码来控制、格式化和渲染数据；\n* 不应包含执行数据查询代码，这种代码放在模型中；\n* 应避免直接访问请求数据，如 `$_GET`, `$_POST`，这种应在控制器中执行，\n  如果需要请求数据，应由控制器推送到视图。\n* 可读取模型属性，但不应修改它们。\n\n为使模型更易于维护，避免创建太复杂或包含太多冗余代码的视图，\n可遵循以下方法达到这个目标：\n\n* 使用 [布局](#layouts) 来展示公共代码（如，页面头部、尾部）；\n* 将复杂的视图分成几个小视图，可使用上面描述的渲染方法将这些\n  小视图渲染并组装成大视图；\n* 创建并使用 [小部件](structure-widgets.md) 作为视图的数据块；\n* 创建并使用助手类在视图中转换和格式化数据。\n\n"
  },
  {
    "path": "docs/guide-zh-CN/structure-widgets.md",
    "content": "小部件\n=======\n\n小部件是在[视图](structure-views.md)中使用的可重用单元，\n使用面向对象方式创建复杂和可配置用户界面单元。\n例如，日期选择器小部件可生成一个精致的允许用户选择日期的日期选择器，\n你只需要在视图中插入如下代码：\n\n```php\n<?php\nuse yii\\jui\\DatePicker;\n?>\n<?= DatePicker::widget(['name' => 'date']) ?>\n```\n\nYii提供许多优秀的小部件，比如 [[yii\\widgets\\ActiveForm|active form]]，[[yii\\widgets\\Menu|menu]]，\n[jQuery UI widgets](widget-jui.md)，\n[Twitter Bootstrap widgets](widget-bootstrap.md)。\n接下来介绍小部件的基本知识，如果你想了解某个小部件请参考对应的类 API 文档。\n\n\n## 使用小部件 <span id=\"using-widgets\"></span>\n\n小部件基本上在 [views](structure-views.md) 中使用，\n在视图中可调用 [[yii\\base\\Widget::widget()]] 方法使用小部件。\n该方法使用 [配置](concept-configurations.md) 数组初始化小部件并返回小部件渲染后的结果。\n例如如下代码插入一个日期选择器小部件，它配置为使用俄罗斯语，\n输入框内容为 `$model` 的 `from_date` 属性值。\n\n```php\n<?php\nuse yii\\jui\\DatePicker;\n?>\n<?= DatePicker::widget([\n    'model' => $model,\n    'attribute' => 'from_date',\n    'language' => 'ru',\n    'dateFormat' => 'php:Y-m-d',\n]) ?>\n```\n\n一些小部件可在 [[yii\\base\\Widget::begin()]] \n和 [[yii\\base\\Widget::end()]] 调用中使用数据内容。\n例如如下代码使用 [[yii\\widgets\\ActiveForm]] 小部件生成一个登录表单，\n小部件会在 `begin()` 和 `end()` 执行处分别生成 `<form>` 的开始标签和结束标签，\n中间的任何代码也会被渲染。\n\n```php\n<?php\nuse yii\\widgets\\ActiveForm;\nuse yii\\helpers\\Html;\n?>\n\n<?php $form = ActiveForm::begin(['id' => 'login-form']); ?>\n\n    <?= $form->field($model, 'username') ?>\n\n    <?= $form->field($model, 'password')->passwordInput() ?>\n\n    <div class=\"form-group\">\n        <?= Html::submitButton('Login') ?>\n    </div>\n\n<?php ActiveForm::end(); ?>\n```\n\n注意和调用 [[yii\\base\\Widget::widget()]] 返回渲染结果不同，\n调用 [[yii\\base\\Widget::begin()]] 方法返回一个可组建小部件内容的小部件实例。\n\n> Note: 当调用 [[yii\\base\\Widget::end()]] 的时候，一些小部件将使用 [输出缓冲](https://www.php.net/manual/zh/book.outcontrol.php)\n> 来调整封闭的内容。因此，当调用 [[yii\\base\\Widget::begin()]] 和\n> [[yii\\base\\Widget::end()]] 时，最好在同一个视图文件里。\n> 不遵循这个规则可能会导致意外的输出。\n\n### 配置全局默认值\n\n小部件的全局默认值可以通过 DI 容器配置：\n\n```php\n\\Yii::$container->set('yii\\widgets\\LinkPager', ['maxButtonCount' => 5]);\n```\n\n有关详细信息，请参阅\n[依赖注入容器“实践中的应用”一节](concept-di-container.md#practical-usage) 。\n\n\n## 创建小部件 <span id=\"creating-widgets\"></span>\n\n可以根据需要以两种不同方式创建小部件。\n\n### 1: 使用 `widget()` 方法\n\n继承 [[yii\\base\\Widget]] 类并覆盖 [[yii\\base\\Widget::init()]] 和/或\n[[yii\\base\\Widget::run()]] 方法可创建小部件。通常 `init()` 方法处理小部件属性，\n`run()` 方法包含小部件生成渲染结果的代码。\n渲染结果可以直接“输出”或通过 `run()` 方法作为字符串返回。\n\n如下代码中 `HelloWidget` 编码并显示赋给 `message` 属性的值，\n如果属性没有被赋值，默认会显示 \"Hello World\"。\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Widget;\nuse yii\\helpers\\Html;\n\nclass HelloWidget extends Widget\n{\n    public $message;\n\n    public function init()\n    {\n        parent::init();\n        if ($this->message === null) {\n            $this->message = 'Hello World';\n        }\n    }\n\n    public function run()\n    {\n        return Html::encode($this->message);\n    }\n}\n```\n\n使用这个小部件只需在视图中简单使用如下代码:\n\n```php\n<?php\nuse app\\components\\HelloWidget;\n?>\n<?= HelloWidget::widget(['message' => 'Good morning']) ?>\n```\n\n\n有时小部件需要渲染很多内容，虽然你可以在 `run()` 方法中嵌入内容，但更好的方法是将内容放入一个[视图](structure-views.md)文件，\n然后调用 [[yii\\base\\Widget::render()]] 方法渲染该视图文件，\n例如：\n\n```php\npublic function run()\n{\n    return $this->render('hello');\n}\n```\n\n### 2: 使用 `begin()` 和 `end()` 方法\n\n这类似于上面的有细微差别。\n以下是另一种可在 `begin()` 和 `end()` 调用中使用的 `HelloWidget`，\nHTML 编码内容然后显示。\n\n```php\nnamespace app\\components;\n\nuse yii\\base\\Widget;\nuse yii\\helpers\\Html;\n\nclass HelloWidget extends Widget\n{\n    public function init()\n    {\n        parent::init();\n        ob_start();\n    }\n\n    public function run()\n    {\n        $content = ob_get_clean();\n        return Html::encode($content);\n    }\n}\n```\n\n如上所示，PHP 输出缓冲在 `init()` 启动，所有在 `init()` \n和 `run()` 方法之间的输出内容都会被获取，并在 `run()` 处理和返回。\n\n> Info: 当你调用 [[yii\\base\\Widget::begin()]] 时会创建一个新的小部件\n  实例并在构造结束时调用 `init()` 方法，\n  在 `end()` 时会调用 `run()` 方法并输出返回结果。\n\n如下代码显示如何使用这种 `HelloWidget`：\n\n```php\n<?php\nuse app\\components\\HelloWidget;\n?>\n<?php HelloWidget::begin(); ?>\n\n    sample content that may contain one or more <strong>HTML</strong> <pre>tags</pre>\n\n    If this content grows too big, use sub views\n\n    For e.g.\n\n    <?php echo $this->render('viewfile'); // Note: here render() method is of class \\yii\\base\\View as this part of code is within view file and not in Widget class file ?>\n\n<?php HelloWidget::end(); ?>\n```\n\n默认情况下，小部件的视图应存储在 `WidgetPath/views` 目录，`WidgetPath` 代表小部件类文件所在的目录。\n假如小部件类文件在 `@app/components` 下，\n上述示例会渲染 `@app/components/views/hello.php` 视图文件。\n您可以覆盖 [[yii\\base\\Widget::getViewPath()]] 方法自定义视图文件所在路径。\n\n\n## 最佳实践 <span id=\"best-practices\"></span>\n\n小部件是面向对象方式来重用视图代码。\n\n创建小部件时仍需要遵循MVC模式，通常逻辑代码在小部件类，\n展示内容在[视图](structure-views.md)中。\n\n小部件设计时应是独立的，也就是说使用一个小部件时候，\n可以直接丢弃它而不需要额外的处理。\n但是当小部件需要外部资源如 CSS，JavaScript，图片等会比较棘手，\n幸运的时候Yii提供 [资源包](structure-asset-bundles.md) 来解决这个问题。\n\n当一个小部件只包含视图代码，它和[视图](structure-views.md)很相似，\n实际上，在这种情况下，唯一的区别是小部件是可以重用类，\n视图只是应用中使用的普通 PHP 脚本。\n\n"
  },
  {
    "path": "docs/guide-zh-CN/test-acceptance.md",
    "content": "验收测试\n================\n\n验收测试从用户角度验证场景。 测试的应用程序可以通过 PhpBrowser 或者\n真正的浏览器。 在这两种情况下，浏览器都通过 HTTP 进行通信，因此应用程序应通过Web服务器提供。\n\n验证测试是在 Codeception 框架的帮助下实现的，该框架有一个很好的文档：\n\n- [Codeception for Yii framework](https://codeception.com/for/yii)\n- [Codeception Acceptance Tests](https://codeception.com/docs/03-AcceptanceTests)\n\n## 运行基本和高级模板测试\n\n如果您已经开始使用高级模板，请参阅 [“测试”指南](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/start-testing.md) \n有关运行测试的更多细节。\n\n如果您已经开始使用基本模板，请查看其 [README“测试”部分](https://github.com/yiisoft/yii2-app-basic/blob/master/README.md#testing)。\n"
  },
  {
    "path": "docs/guide-zh-CN/test-environment-setup.md",
    "content": "测试环境设置\n======================\n\nYii 2 官方兼容 [`Codeception`](https://github.com/Codeception/Codeception) 测试框架，\n你可以创建以下类型的测试：\n\n- [单元测试](test-unit.md) - 验证一个独立的代码单元是否按照期望的方式运行；\n- [功能测试](test-functional.md) - 在浏览器模拟器中以用户视角来验证期望的场景是否发生\n- [验收测试](test-acceptance.md) - 在真实的浏览器中以用户视角验证期望的场景是否发生。\n\nYii 为包括 [`yii2-basic`](https://github.com/yiisoft/yii2-app-basic) 和\n[`yii2-advanced`](https://github.com/yiisoft/yii2-app-advanced) \n在内的应用模板脚手架提供全部三种类型的即用测试套件。\n\nCodeception 预装了基本和高级项目模板。\n如果您没有使用这些模板中的一个，则可以安装 Codeception\n通过输入以下控制台命令：\n\n```\ncomposer require codeception/codeception\ncomposer require codeception/specify\ncomposer require codeception/verify\n```\n"
  },
  {
    "path": "docs/guide-zh-CN/test-fixtures.md",
    "content": "Fixtures\n========\n\nFixtures 是测试中非常重要的一部分。他们的主要目的是建立一个固定/已知的环境状态以确保\n测试可重复并且按照预期方式运行。Yii 提供一个简单可用的 Fixure 框架\n允许你精确的定义你的 Fixtures 。\n\nYii 的 Fixture 框架的核心概念称之为 *fixture object* 。一个 Fixture object 代表\n一个测试环境的某个特定方面，它是 [[yii\\test\\Fixture]] 或者其子类的实例。\n比如，你可以使用 `UserFixture` 来确保用户DB表包含固定的数据。\n你在运行一个测试之前加载一个或者多个 fixture object，并在结束后卸载他们。\n\n一个 Fixture 可能依赖于其他的 Fixtures ，通过它的 [[yii\\test\\Fixture::depends]] 来指定。\n当一个 Fixture 被加载前，它依赖的 Fixture 会被自动的加载；同样，当某个 Fixture 被卸载后，\n它依赖的 Fixtures 也会被自动的卸载。\n\n\n定义一个 Fixture\n------------------\n\n为了定义一个 Fixture，你需要创建一个新的 class 继承自 [[yii\\test\\Fixture]] \n或者 [[yii\\test\\ActiveFixture]] 。前一个类对于一般用途的 Fixture 比较适合，\n而后者则有一些增强功能专用于与数据库和 ActiveRecord 一起协作。\n\n下面的代码定义一个关于 `User` ActiveRecord 和相关的用户表的 Fixture：\n\n```php\n<?php\nnamespace app\\tests\\fixtures;\n\nuse yii\\test\\ActiveFixture;\n\nclass UserFixture extends ActiveFixture\n{\n    public $modelClass = 'app\\models\\User';\n}\n```\n\n> Tip: 每个 `ActiveFixture` 都会准备一个 DB 表用来测试。你可以通过设置 [[yii\\test\\ActiveFixture::tableName]] \n> 或 [[yii\\test\\ActiveFixture::modelClass]] 属性来指定具体的表。如果是后者，\n> 表名会从 `modleClass` 指定的 `ActiveRecord` 中获取。\n\n> Note: [[yii\\test\\ActiveFixture]] 仅限于 SQL 数据库，对于 NoSQL 数据库，\n> Yii 提供以下 `ActiveFixture` 类：\n>\n> - Mongo DB：[[yii\\mongodb\\ActiveFixture]]\n> - Elasticsearch：[[yii\\elasticsearch\\ActiveFixture]]（从版本 2.0.2 开始）\n\n\n提供给 `ActiveFixture` 的 fixture data 通常放在一个路径为 `FixturePath/data/TableName.php` 的文件中，\n其中 `FixturePath` 代表 Fixture 类所在的路径， \n`TableName` 则是和 Fixture 关联的表。在以上的例子中，\n这个文件应该是 `@app/tests/fixtures/data/user.php` 。\ndata 文件返回一个包含要被插入用户表中的数据文件，比如：\n\n```php\n<?php\nreturn [\n    'user1' => [\n        'username' => 'lmayert',\n        'email' => 'strosin.vernice@jerde.com',\n        'auth_key' => 'K3nF70it7tzNsHddEiq0BZ0i-OU8S3xV',\n        'password' => '$2y$13$WSyE5hHsG1rWN2jV8LRHzubilrCLI5Ev/iK0r3jRuwQEs2ldRu.a2',\n    ],\n    'user2' => [\n        'username' => 'napoleon69',\n        'email' => 'aileen.barton@heaneyschumm.com',\n        'auth_key' => 'dZlXsVnIDgIzFgX4EduAqkEPuphhOh9q',\n        'password' => '$2y$13$kkgpvJ8lnjKo8RuoR30ay.RjDf15bMcHIF7Vz1zz/6viYG5xJExU6',\n    ],\n];\n```\n\n你可以给某行指定一个 alias 别名，这样在你以后的测试中，你可以通过别名来确定某行。\n在上面的例子中，这两行指定别名为 `user1` 和 `user2`。\n\n同样，你不需要特别的为自动增长（auto-incremental）的列指定数据，\nYii 将会在 Fixture 被加载时自动的填充正确的列值到这些行中。\n\n> Tip: 你可以通过设置 [[yii\\test\\ActiveFixture::dataFile]] 属性来自定义 data 文件的位置。\n> 同样，你可以重写 [[yii\\test\\ActiveFixture::getData()]] 来提供数据。\n\n如之前所述，一个 Fixture 可以依赖于其他的 Fixture 。比如一个 `UserProfileFixture` 可能需要依赖于 `UserFixture`，\n因为 user profile 表包括一个指向 user 表的外键。那么，\n这个依赖关系可以通过 [[yii\\test\\Fixture::depends]] 属性来指定，比如如下：\n\n```php\nnamespace app\\tests\\fixtures;\n\nuse yii\\test\\ActiveFixture;\n\nclass UserProfileFixture extends ActiveFixture\n{\n    public $modelClass = 'app\\models\\UserProfile';\n    public $depends = ['app\\tests\\fixtures\\UserFixture'];\n}\n```\n\n依赖关系确保所有的 Fixtures 能够以正常的顺序被加载和卸载。在以上的例子中，\n为确保外键存在， `UserFixture` 会在 `UserProfileFixture` 之前加载，\n同样，也会在其卸载后同步卸载。\n\n在上面，我们展示了如何定义一个 DB 表的 Fixture 。为了定义一个与 DB 无关的 Fixture \n（比如一个fixture关于文件和路径的），你可以从一个更通用的基类 [[yii\\test\\Fixture]] 继承，\n并重写 [[yii\\test\\Fixture::load()|load()]] 和 [[yii\\test\\Fixture::unload()|unload()]] 方法。\n\n\n使用 Fixtures\n--------------\n\n如果你使用 [CodeCeption](https://codeception.com/) 作为你的 Yii 代码测试框架，\n你需要考虑使用 `yii2-codeception` 扩展，这个扩展包含内置的机制来支持加载和访问 Fixtures。\n如果你使用其他的测试框架，为了达到加载和访问 Fixture 的目的，\n你需要在你的测试用例中使用 [[yii\\test\\FixtureTrait]]。\n\n在以下示例中，我们会展示如何通过 `yii2-codeception` 写一个 `UserProfile` 单元来测试某个 class。\n\n在一个继承自 [[yii\\codeception\\DbTestCase]] 或者 [[yii\\codeception\\TestCase]] 的单元测试类中，\n你可以在 [[yii\\test\\FixtureTrait::fixtures()|fixtures()]] 方法中声明你希望使用哪个 Fixture。比如：\n\n```php\nnamespace app\\tests\\unit\\models;\n\n\nuse app\\tests\\fixtures\\UserProfileFixture;\n\nclass UserProfileTest extends \\Codeception\\Test\\Unit\n{   \n    public function _fixtures()\n    {\n        return [\n            'profiles' => [\n                'class' => UserProfileFixture::class,\n                // fixture data located in tests/_data/user.php\n                'dataFile' => codecept_data_dir() . 'user.php'\n            ],\n        ];\n    }\n\n    // ...test methods...\n}\n```\n\n在测试用例的每个测试方法运行前 `fixtures()` 方法列表返回的 Fixture 会被自动的加载，\n并在结束后自动的卸载。同样，如前面所述，当一个 Fixture 被加载之前，\n所有它依赖的 Fixture 也会被自动的加载。在上面的例子中，因为 `UserProfileFixture` \n依赖于 `UserFixtrue`，当运行测试类中的任意测试方法时，两个 Fixture，`UserFixture` 和 `UserProfileFixture` 会被依序加载。\n\n当我们通过 `fixtures()` 方法指定需要加载的 Fixture 时，我们既可以使用一个类名，\n也可以使用一个配置数组。\n配置数组可以让你自定义加载的 fixture 的属性名。\n\n你同样可以给一个 Fixture 指定一个别名（alias），在上面的例子中，`UserProfileFixture` 的别名为 `profiles` 。\n在测试方法中，你可以通过别名来访问一个 Fixture 对象。比如，\n\n```php\n$profile = $I->grabFixture('profiles', 'user1');\n```\n\n将会返回 `UserProfileFixture` 对象。\n\n因为 `UserProfileFixture` 从 `ActiveFixture` 处继承，\n在后面，你可以通过如下的语法形式来访问 Fixture 提供的数据：\n\n```php\n// 返回对应于别名为“user1”的数据行的 UserProfile 模型\n$profile = $I->grabFixture('profiles', 'user1');\n// 遍历 fixture 中的数据\nforeach ($I->grabFixture('profiles') as $profile) ...\n```\n\n组织 Fixture 类和相关的数据文件\n-----------------------------------------\n\n默认情况下，Fixture 类会在其所在的目录下面的 `data` 子目录寻找相关的数据文件。\n在一些简单的项目中，你可以遵循此范例。对于一些大项目，\n您可能经常为同一个 Fixture 类的不同测试而切换不同的数据文件。\n在这种情况下，我们推荐你按照一种类似于命名空间的方式有层次地组织你的数据文件，比如：\n\n```\n# under folder tests\\unit\\fixtures\n\ndata\\\n    components\\\n        fixture_data_file1.php\n        fixture_data_file2.php\n        ...\n        fixture_data_fileN.php\n    models\\\n        fixture_data_file1.php\n        fixture_data_file2.php\n        ...\n        fixture_data_fileN.php\n# and so on\n```\n\n这样，你就可以避免在测试用例之间产生冲突，并根据你的需要使用它们。\n\n> Note: 在以上的例子中，Fixture 文件只用于示例目的。在真实的环境下，你需要根据你的 Fixture 类继承的基类来决定它们的命名。\n> 比如，如果你从 [[yii\\test\\ActiveFixture]] 继承了一个 DB Fixture，\n> 你需要用数据库表名字作为 Fixture 的数据文件名；如果你从 [[yii\\mongodb\\ActiveFixture]] 继承了一个 MongoDB Fixture，\n> 你需要使用 collection 名作为文件名。\n\n组织 Fixuture 类名的方式同样可以使用前述的层次组织法，但是，为了避免跟数据文件产生冲突，\n你需要用 `fixtures` 作为根目录而非 `data`。\n\n## 使用 `yii fixture` 来管理 fixtures\n\nYii 通过 `yii fixture` 命令行工具来支持 fixtures 操作. 这个工具支持:\n\n* 将 fixtures 装载到不同的存储设备，例如：RDBMS, NoSQL 等;\n* 以不同方式卸载 fixtures（通常是清理存储）;\n* 自动生成 fixtures 并用随机数据填充。\n\n### Fixtures 数据格式\n\n让我们假设我们有要加载的 fixtures 数据：\n\n```\n#users.php 文件在 fixtures 下的数据路径, 默认为 @tests\\unit\\fixtures\\data\n\nreturn [\n    [\n        'name' => 'Chase',\n        'login' => 'lmayert',\n        'email' => 'strosin.vernice@jerde.com',\n        'auth_key' => 'K3nF70it7tzNsHddEiq0BZ0i-OU8S3xV',\n        'password' => '$2y$13$WSyE5hHsG1rWN2jV8LRHzubilrCLI5Ev/iK0r3jRuwQEs2ldRu.a2',\n    ],\n    [\n        'name' => 'Celestine',\n        'login' => 'napoleon69',\n        'email' => 'aileen.barton@heaneyschumm.com',\n        'auth_key' => 'dZlXsVnIDgIzFgX4EduAqkEPuphhOh9q',\n        'password' => '$2y$13$kkgpvJ8lnjKo8RuoR30ay.RjDf15bMcHIF7Vz1zz/6viYG5xJExU6',\n    ],\n];\n``` \n如果我们使用 fixture 将数据加载到数据库中，那么这些行将作用到 `users` 表。 如果我们使用的是 nosql 类型的 fixtures，例如 `mongodb` fixture然后这个数据将应用于 `users` mongodb 集合。 要了解有关实现各种加载策略的信息，请参阅官方[文档](https://github.com/yiisoft/yii2/blob/master/docs/guide/test-fixtures.md)。\n\n上面的 fixture 示例是由 `yii2-faker` 扩展自动生成的，在这些[章节](#auto-generating-fixtures)中可以了解更多相关内容。\nFixture 类名称不应为复数。\n\n### 加载 Fixtures\n\nFixture 类应该以 `Fixture` 类作为后缀。默认的 Fixtures 能在 `tests\\unit\\fixtures` 命名空间下被搜索到，\n你可以通过配置和命名行选项来更改这个行为，你可以通过加载或者卸载指定它名字前面的 `-` 来排除一些 Fixtures，像 `-User`。\n\n运行如下命令去加载 Fixture：\n\n> Note: 卸载数据事件优先执行，在加载数据之前。通常，这将会清除先前的 fixture 所插入的所有现有数据。\n\n```\nyii fixture/load <fixture_name>\n```\n\n必需参数 `fixture_name` 指定一个将被加载数据的 Fixture 名字。 你可以同时加载多个 Fixtures 。\n以下是这个命令的正确格式：\n\n```\n// 加载 `User` fixture\nyii fixture/load User\n\n// 与上面相同，因为 “fixture” 命令的默认动作是“加载”\nyii fixture User\n\n// 加载几个 fixtures\nyii fixture User UserProfile\n\n// 加载所有 fixtures\nyii fixture/load \"*\"\n\n// 与上面相同\nyii fixture \"*\"\n\n// 加载所有 fixtures 除了这些\nyii fixture \"*\" -DoNotLoadThisOne\n\n// 加载 fixtures, 但在不同的命名空间中搜索它们。默认命名空间是：tests\\unit\\fixtures。\nyii fixture User --namespace='alias\\my\\custom\\namespace'\n\n// 加载全局 fixture `some\\name\\space\\CustomFixture` 在加载其他 fixtures 之前.\n// 默认情况下，此选项设置为 `InitDbFixture` 以禁用/启用完整性检查。您可以用用逗号分离来\n// 指定几个全局 fixtures。\nyii fixture User --globalFixtures='some\\name\\space\\Custom'\n```\n\n### 卸载 Fixtures\n\n运行如下命名去卸载 Fixtures：\n\n```\n// 卸载 User fixture，默认情况下它将清除 fixture 存储（例如“用户”表，或“用户”集合如果这是 mongodb fixture）。\nyii fixture/unload User\n\n// 卸载几个 fixtures\nyii fixture/unload User,UserProfile\n\n// 卸载所有 fixtures\nyii fixture/unload \"*\"\n\n// 卸载所有 fixtures 除了这些\nyii fixture/unload \"*\" -DoNotUnloadThisOne\n\n```\n\n同样的命名选项：`namespace`，`globalFixtures` 也可以应用于该命令。\n\n### 全局命令配置\n\n当命令行选项允许我们配置迁移命令，\n有时我们只想配置一次。例如，\n你可以按照如下配置迁移目录：\n\n```\n'controllerMap' => [\n    'fixture' => [\n        'class' => 'yii\\console\\controllers\\FixtureController',\n        'namespace' => 'myalias\\some\\custom\\namespace',\n        'globalFixtures' => [\n            'some\\name\\space\\Foo',\n            'other\\name\\space\\Bar'\n        ],\n    ],\n]\n```\n\n### 自动生成 fixtures\n\nYii 还可以为你自动生成一些基于一些模板的 Fixtures。 你能够以不同语言格式用不同的数据生成你的 Fixtures。\n这些特征由 [Faker](https://github.com/fzaninotto/Faker) 库和 `yii2-faker` 扩展完成。\n关注 [guide](https://github.com/yiisoft/yii2-faker) 扩展获取更多的文档。\n\n## 总结\n\n在上面，我们描述了如何定义和使用 Fixture，在下面，我们将总结出一个\n标准地运行与 DB 有关的单元测试的规范工作流程：\n\n1. 使用 `yii migrate` 工具来让你的测试数据库更新到最新的版本；\n2. 运行一个测试：\n   - 加载 Fixture：清空所有的相关表结构，并用 Fixture 数据填充\n   - 执行真实的测试用例\n   - 卸载 Fixture\n3. 重复步骤 2 直到所有的测试结束。\n"
  },
  {
    "path": "docs/guide-zh-CN/test-functional.md",
    "content": "功能测试\n================\n\n功能测试从用户的角度验证场景。它类似于[验收测试](test-acceptance.md)。\n但是它不是通过 HTTP 进行通信，而是填充环境，如（填充）POST 和 GET 参数，\n然后直接在代码里执行 Application 实例。\n\n功能测试通常比验收测试快，并且在失败时提供详细的堆栈跟踪。\n根据老司机的经验，功能测试应该是首选的，除非有专门的 Web 服务器设置\n或者由 JavaScript 构建的复杂 UI。\n\n功能测试是借助于具有良好文档的 Codeception 框架来实现的：\n\n- [Codeception for Yii framework](https://codeception.com/for/yii)\n- [Codeception 的功能测试](https://codeception.com/docs/04-FunctionalTests)\n\n## 运行基本模板和高级模板的测试\n\n如果你使用的是高级模板，请参阅[测试指南](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/start-testing.md)\n中关于运行测试的更多细节。  \n\n如果你使用的是基本模板，请检查其 [README \"testing\" 部分](https://github.com/yiisoft/yii2-app-basic/blob/master/README.md#testing)。\n"
  },
  {
    "path": "docs/guide-zh-CN/test-overview.md",
    "content": "测试\n=======\n\n测试是软件开发的一个重要组成部分。不管我们是否意识到，我们一直在不断地进行测试。\n例如，当我们在用 PHP 写一个类的时候，我们可能用 echo 或者 die 语句一步一步简单的调试\n验证我们实现的代码是否按照最初的计划工作。在开发 web 应用的时候，我们在表单中输入\n一些测试数据来确保页面能够如预期那样和我们进行交互。\n\n测试过程可能是自动的，所以每次我们需要验证的时候，我们只需要调用它就可以测试代码了。\n验证代码执行结果是否符合我们的计划叫做测试，\n测试过程的创建以及进一步执行叫做*自动化测试*，\n这是这些测试章节的主要主题。\n\n\n## 带着测试进行开发\n\n测试驱动开发（TDD）和行为驱动开发（BDD）在开始编写实际代码之前，\n首先通过描述一段代码的行为或将其作为一组场景或测试的全部特征，\n然后创建符合这些测试预期验证\n的行为实现。\n\n开发一个功能的过程如下：\n\n- 创建一个描述一个功能被实现测试。\n- 运行这个测试来确保功能失败，因为这是没有实现之前的预期。\n- 编写简单代码确保这个测试通过。\n- 运行所有测试确保所有测试都通过。\n- 优化代码确保测试依然可以通过。\n\n走完上面的过程之后，为其他功能或者扩展重复上面测试过程。如果功能发生变化，测试也需\n要跟着变化。\n\n> Tip: 如果你觉得你做一些很小很简单的迭代是在浪费时间，请尝试覆盖更多的测试\n> 场景，这样你就可以在执行测试之前做更多的尝试。如果你的调试过多，试着做相反的工作。\n\n在做一些具体的实现之前创建测试的原因是，这允许我们后期专注于我们想要的实现，并且\n可以花费更多的精力到实现细节。在涉及功能调整的时候，这会使得抽象更合理、测试维护\n更简单或者使得耦合元件更少。\n\n这种做法的优点如下：\n\n- 在计划和实现发生变更的时候，可以让你在同一时间只专注于一件事情。\n- 更多功能更详细的覆盖测试的结果，如果测试都通过好比再也没有什么问题了。\n\n在很长一段时间内，这通常会给你提供一个有效的时间节省。\n\n## 什么时候测试，怎么测试？\n\n在测试的时候，对于一些相对复杂的项目上面的内容是非常有意义的，但对于一些比较\n简单的项目就做的有些极端了。适用场景如下：\n\n- 项目已经很大且复杂。\n- 项目需求开始变得复杂起来。项目不断发展。\n- 项目历时很长。\n- 失败的代价非常高。\n\n在现有的实现行为中进行覆盖测试是非常适合的。\n\n- 项目是一个逐步更新的遗产。\n- 你有一个还没有经过测试的项目要做。\n\n在一些情况下，任何形式的自动化测试都是过于极端的：\n\n- 项目很简单，也不会变得复杂。\n- 过期不再工作的一次性项目。\n\n假如你有很多的时间，在这种情况下进行自动测试也很好。\n\n## 深度阅读\n\n- 测试驱动开发：示例 / Kent Beck。 ISBN：0321146530。\n"
  },
  {
    "path": "docs/guide-zh-CN/test-unit.md",
    "content": "单元测试\n==========\n\n单元测试验证单个代码单元是否按预期工作。 也就是说，给定不同的输入参数，\n测试验证类方法返回预期结果。\n单元测试通常由编写待测试类的人开发。\n\nYii的单元测试框架 Codeception 基于 PHPUnit，Codeception 建议遵从 PHPUnit 的文档的进行开发：\n\n- [Codeception for Yii framework](https://codeception.com/for/yii)\n- [Codeception Unit Tests](https://codeception.com/docs/05-UnitTests)\n- [PHPUnit docs starting from chapter 2](https://phpunit.readthedocs.io/en/9.5/writing-tests-for-phpunit.html)\n\n## 运行基本和高级模板测试\n\n如果您已经开始使用高级模板，请参阅 [\"testing\" guide](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/start-testing.md)\n有关运行测试的更多细节。\n\n如果您已经开始使用基本模板，请参阅 [README \"testing\" section](https://github.com/yiisoft/yii2-app-basic/blob/master/README.md#testing)。\n\n## 框架单元测试\n\n如果你想运行 Yii 框架的单元测试\n“[Getting started with Yii 2 development](https://github.com/yiisoft/yii2/blob/master/docs/internals/getting-started.md)”。\n"
  },
  {
    "path": "docs/guide-zh-CN/translators.json",
    "content": "[\n    \"https://www.yiichina.com\",\n    \"cuileon\",\n    \"qiansen1386\",\n    \"deepziyu\",\n    \"shi-yang\",\n    \"iyuanc\",\n    \"xiaoliushifu\",\n    \"jhq0113\",\n    \"funson86\",\n    \"riverlet\",\n    \"abrahamgreyson\",\n    \"fengyh\"\n]\n"
  },
  {
    "path": "docs/guide-zh-CN/tutorial-console.md",
    "content": "控制台命令\n==========\n\n除了用于构建 Web 应用程序的丰富功能，Yii 中也有一个拥有丰富功能的控制台，\n它们主要用于创建网站后台处理的任务。\n\n控制台应用程序的结构非常类似于 Yii 的一个 Web 应用程序。\n它由一个或多个 [[yii\\console\\Controller]] 类组成，它们在控制台环境下通常被称为“命令”。\n每个控制器还可以有一个或多个动作，就像 web 控制器。\n\n两个项目模板（基础模版和高级模版）都有自己的控制台应用程序。\n你可以通过运行 `yii` 脚本，在位于仓库的基本目录中运行它。\n当你不带任何参数来运行它时，会给你一些可用的命令列表：\n\n![Running ./yii command for help output](images/tutorial-console-help.png)\n\n正如你在截图中看到，Yii 中已经定义了一组默认情况下可用的命令：\n\n- [[yii\\console\\controllers\\AssetController|AssetController]] - 允许合并和压缩你的 JavaScript 和 CSS 文件。\n  在 [资源 - 使用 asset 命令](structure-assets.md#using-the-asset-command) 一节可获取更多信息。\n- [[yii\\console\\controllers\\CacheController|CacheController]] - 清除应用程序缓存。\n- [[yii\\console\\controllers\\FixtureController|FixtureController]] - 管理用于单元测试 fixture 的加载和卸载。\n  这个命令的更多细节在 [Testing Section about Fixtures](test-fixtures.md#managing-fixtures).\n- [[yii\\console\\controllers\\HelpController|HelpController]] - 提供有关控制台命令的帮助信息，\n  这是默认的命令并会打印上面截图所示的输出。\n- [[yii\\console\\controllers\\MessageController|MessageController]] - 从源文件提取翻译信息。\n  要了解更多关于这个命令的用法，请参阅 [I18N 章节](tutorial-i18n.md#message-command).\n- [[yii\\console\\controllers\\MigrateController|MigrateController]] - 管理应用程序数据库迁移。\n  在 [数据库迁移章节](db-migrations.md) 可获取更多信息。\n- [[yii\\console\\controllers\\ServeController|ServeController]] - Allows you run PHP built-in web server.\n\n\n用法 <span id=\"usage\"></span>\n-----\n\n你可以使用以下语法来执行控制台控制器动作：\n\n```\nyii <route> [--option1=value1 --option2=value2 ... argument1 argument2 ...]\n```\n\n以上，`<route>` 指的是控制器动作的路由。选项将填充类属性，\n参数是动作方法的参数。\n\n例如，将 [[yii\\console\\controllers\\MigrateController::actionUp()|MigrateController::actionUp()]]\n限制 5 个数据库迁移并将 [[yii\\console\\controllers\\MigrateController::$migrationTable|MigrateController::$migrationTable]] \n设置为 `migrations` 应该这样调用：\n\n```\nyii migrate/up 5 --migrationTable=migrations\n```\n\n> Note: 当在控制台使用 `*` 时, 不要忘记像 `\"*\"` 一样用引号来引起来，\n> 为了防止在 shell 中执行命令时被当成当前目录下的所有文件名。\n\n\n入口脚本 <span id=\"entry-script\"></span>\n-------\n\n控制台应用程序的入口脚本相当于用于 Web 应用程序的 `index.php` 入口文件。\n控制台入口脚本通常被称为 `yii`，位于应用程序的根目录。\n它包含了类似下面的代码：\n\n```php\n#!/usr/bin/env php\n<?php\n/**\n * Yii console bootstrap file.\n */\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n\nrequire(__DIR__ . '/vendor/autoload.php');\nrequire(__DIR__ . '/vendor/yiisoft/yii2/Yii.php');\n\n$config = require(__DIR__ . '/config/console.php');\n\n$application = new yii\\console\\Application($config);\n$exitCode = $application->run();\nexit($exitCode);\n```\n\n该脚本将被创建为你应用程序中的一部分；你可以根据你的需求来修改它。\n如果你不需要记录错误信息或者希望提高整体性能，`YII_DEBUG` 常数应定义为 `false`。\n在基本的和高级的两个应用程序模板中，控制台应用程序的入口脚本在默认情况下会启用调试模式，以提供给开发者更好的环境。\n\n\n配置 <span id=\"configuration\"></span>\n-----\n\n在上面的代码中可以看到，控制台应用程序使用它自己的配置文件，名为 `console.php` 。在该文件里你可以给控制台配置各种\n[应用组件](structure-application-components.md) 和属性。\n\n如果你的 web 应用程序和控制台应用程序共享大量的配置参数和值，\n你可以考虑把这些值放在一个单独的文件中，该文件中包括（ web 和控制台）应用程序配置。\n你可以在“高级”项目模板中看到一个例子。\n\n> Tip: 有时，你可能需要使用一个与在入口脚本中指定的应用程序配置不同的控制台命令。\n> 例如，你可能想使用 `yii migrate`\n> 命令来升级你的测试数据库，它被配置在每个测试套件。\n> 要动态地更改配置，只需指定一个自定义应用程序的配置文件，\n> 通过 `appconfig`选项来执行命令：\n> \n> ```\n> yii <route> --appconfig=path/to/config.php ...\n> ```\n\n\n控制台命令完成 <span id=\"console-command-completion\"></span>\n------------\n\n自动完成命令参数在使用 shell 时非常有用。 \n从版本 2.0.11 开始，`./yii` 命令为 Bash 和 ZSH 提供了自动完成功能。\n\n### Bash 完成\n\n确保安装完毕。对于大多数安装，它默认是可用的。\n\n放置完成脚本在 `/etc/bash_completion.d/`：\n\n     curl -L https://raw.githubusercontent.com/yiisoft/yii2/master/contrib/completion/bash/yii -o /etc/bash_completion.d/yii\n\n对于临时使用，您可以将文件放入当前目录，并通过 `source yii` 将其包含在当前会话中。\n如果全局安装，您可能需要重新启动终端或`source ~/.bashrc` 来激活它。\n\n查看 [Bash 手册](https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion.html) \n了解将完成脚本添加到您的环境的其他方法。\n\n### ZSH 完成\n\n将完成脚本放入完成目录中，例如使用 `~/.zsh/completion/`\n\n```\nmkdir -p ~/.zsh/completion\ncurl -L https://raw.githubusercontent.com/yiisoft/yii2/master/contrib/completion/zsh/_yii -o ~/.zsh/completion/_yii\n```\n\n将目录包含在 `$fpath` 中，例如，通过将其添加到 `~/.zshrc` 中\n\n```\nfpath=(~/.zsh/completion $fpath)\n```\n\n确保 `compinit` 被加载或通过在 `~/.zshrc` 中加入完成\n\n```\nautoload -Uz compinit && compinit -i\n```\n\n然后重新加载您的 shell\n\n```\nexec $SHELL -l\n```\n\n创建你自己的控制台命令 <span id=\"create-command\"></span>\n------------------\n\n### 控制台的控制器和行为\n\n一个控制台命令继承自 [[yii\\console\\Controller]] 控制器类。\n在控制器类中，定义一个或多个与控制器的子命令相对应的动作。在每一个动作中，编写你的代码实现特定的子命令的适当的任务。\n\n当你运行一个命令时，你需要指定一个控制器的路由。\n例如，路由 `migrate/create` 调用子命令对应的[[yii\\console\\controllers\\MigrateController::actionCreate()|MigrateController::actionCreate()]] 动作方法。\n如果在执行过程中提供的路由不包含路由 ID ，\n将执行默认动作（如 web 控制器）。\n\n### 选项\n\n通过覆盖在 [[yii\\console\\Controller::options()]] 中的方法，\n你可以指定可用于控制台命令（controller/actionID）选项。这个方法应该返回控制器类的公共属性的列表。\n当运行一个命令，你可以指定使用语法 `--OptionName=OptionValue` 选项的值。\n这将分配 `OptionValue` 到控制器类的 `OptionName` 属性。\n\n如果选项的默认值是数组类型，并且在运行该命令时设置了该选项，\n通过在任何逗号分割输入字符串将选项值转换为数组。\n\n### 选项别名\n\n从版本 2.0.8 起控制台命令提供 [[yii\\console\\Controller::optionAliases()]]\n方法来为选项添加别名。\n\n要定义别名，请在控制器中覆盖 [[yii\\console\\Controller::optionAliases()]]，例如：\n\n```php\nnamespace app\\commands;\n\nuse yii\\console\\Controller;\n\nclass HelloController extends Controller\n{\n    public $message;\n    \n    public function options($actionID)\n    {\n        return ['message'];\n    }\n    \n    public function optionAliases()\n    {\n        return ['m' => 'message'];\n    }\n    \n    public function actionIndex()\n    {\n        echo $this->message . \"\\n\";\n    }\n}\n```\n\n现在，您可以使用以下语法来运行该命令：\n\n```\n./yii hello -m=hello\n```\n\n### 参数\n\n除了选项，命令还可以接收参数。参数将传递给请求的子命令对应的动作方法。\n第一个参数对应第一个参数，第二个参数对应第二个参数，依次类推。\n命令被调用时，如果没有足够的参数，如果有定义默认值的情况下，则相应的参数将采取默认声明的值；\n如果没有设置默认值，并且在运行时没有提供任何值，该命令将以一个错误退出。\n\n你可以使用 `array` 类型提示来指示一个参数应该被视为一个数组。\n该数组通过拆分输入字符串的逗号来生成。\n\n下面的示例演示如何声明参数：\n\n```php\nclass ExampleController extends \\yii\\console\\Controller\n{\n    // 命令 \"yii example/create test\" 会调用 \"actionCreate('test')\"\n    public function actionCreate($name) { ... }\n\n    // 命令 \"yii example/index city\" 会调用 \"actionIndex('city', 'name')\"\n    // 命令 \"yii example/index city id\" 会调用 \"actionIndex('city', 'id')\"\n    public function actionIndex($category, $order = 'name') { ... }\n\n    // 命令 \"yii example/add test\" 会调用 \"actionAdd(['test'])\"\n    // 命令 \"yii example/add test1,test2\" 会调用 \"actionAdd(['test1', 'test2'])\"\n    public function actionAdd(array $name) { ... }\n}\n```\n\n\n### 退出代码\n\n使用退出代码是控制台应用程序开发的最佳做法。通常，执行成功的命令会返回 `0`。\n如果命令返回一个非零数字，会认为出现错误。\n该返回的数字作为出错代码，用以了解错误的详细信息。例如 `1` 可能代表一个未知的错误，\n所有的代码都将保留在特定的情况下：输入错误，丢失的文件等等。\n\n要让控制台命令返回一个退出代码，\n只需在控制器动作方法中返回一个整数：\n\n```php\npublic function actionIndex()\n{\n    if (/* some problem */) {\n        echo \"A problem occured!\\n\";\n        return 1;\n    }\n    // do something\n    return 0;\n}\n```\n\n有一些预定义的常量可以使用。在类 [[yii\\console\\ExitCode]] 中被定义：\n\n```php\npublic function actionIndex()\n{\n    if (/* some problem */) {\n        echo \"A problem occurred!\\n\";\n        return ExitCode::UNSPECIFIED_ERROR;\n    }\n    // do something\n    return ExitCode::OK;\n}\n```\n\n为控制器定义有意义的常量，以防有更多的错误代码类型，这会是一个很好的实践。\n\n### 格式和颜色\n\nYii 支持格式化输出，\n如果终端运行命令不支持的话则会自动退化为非格式化输出。\n\n要输出格式的字符串很简单。以下展示了如何输出一些加粗的文字：\n\n```php\n$this->stdout(\"Hello?\\n\", Console::BOLD);\n```\n\n如果你需要建立字符串动态结合的多种样式，最好使用 `ansiFormat` ：\n\n```php\n$name = $this->ansiFormat('Alex', Console::FG_YELLOW);\necho \"Hello, my name is $name.\";\n```\n\n### 表格\n\n从版本 2.0.13 开始，有一个小部件允许您在控制台中格式化表数据。使用方法如下：\n\n```php\necho Table::widget([\n    'headers' => ['Project', 'Status', 'Participant'],\n    'rows' => [\n        ['Yii', 'OK', '@samdark'],\n        ['Yii', 'OK', '@cebe'],\n    ],\n]);\n```\n\n有关详细信息，请参阅 [[yii\\console\\widgets\\Table|API documentation]].\n"
  },
  {
    "path": "docs/guide-zh-CN/tutorial-core-validators.md",
    "content": "核心验证器（Core Validators）\n===============\n\nYii 提供一系列常用的核心验证器，位于 `yii\\validators` 命名空间之下。\n为了避免使用冗长的类名，你可以直接用**别名**来指定相应的核心验证器。\n比如你可以用 `required` 别名代指 [[yii\\validators\\RequiredValidator]] 类：\n\n```php\npublic function rules()\n{\n    return [\n        [['email', 'password'], 'required'],\n    ];\n}\n```\n\n[[yii\\validators\\Validator::builtInValidators]] 属性声明了所有被支持的验证器别名。\n\n下面，我们将详细介绍每一款验证器的主要用法和属性。\n\n\n## [[yii\\validators\\BooleanValidator|boolean（布尔型）]] <span id=\"boolean\"></span>\n\n```php\n[\n    // 检查 \"selected\" 是否为 0 或 1，无视数据类型\n    ['selected', 'boolean'],\n\n    // 检查 \"deleted\" 是否为布尔类型，即 true 或 false\n    ['deleted', 'boolean', 'trueValue' => true, 'falseValue' => false, 'strict' => true],\n]\n```\n\n该验证器检查输入值是否为一个布尔值。\n\n- `trueValue`： 代表**真**的值。默认为 `'1'`。\n- `falseValue`：代表**假**的值。默认为 `'0'`。\n- `strict`：是否要求待测输入必须严格匹配 `trueValue` 或 `falseValue`。默认为 `false`。\n\n\n> Note: 因为通过 HTML 表单传递的输入数据都是字符串类型，所以一般情况下你都需要保持\n  [[yii\\validators\\BooleanValidator::strict|strict]] 属性为假。\n\n\n## [[yii\\captcha\\CaptchaValidator|captcha（验证码）]] <span id=\"captcha\"></span>\n\n```php\n[\n    ['verificationCode', 'captcha'],\n]\n```\n\n该验证器通常配合 [[yii\\captcha\\CaptchaAction]] 以及 [[yii\\captcha\\Captcha]]\n使用，以确保某一输入与 [[yii\\captcha\\Captcha|CAPTCHA]] 小部件所显示的验证代码（verification code）相同。\n\n- `caseSensitive`：对验证码的比对是否要求大小写敏感。默认为 false。\n- `captchaAction`：指向用于渲染 CAPTCHA 图片的 [[yii\\captcha\\CaptchaAction|CAPTCHA action]] \n  的 [路由](structure-controllers.md#routes)。默认为 `'site/captcha'`。\n- `skipOnEmpty`：当输入为空时，是否跳过验证。\n  默认为 false，也就是输入值为必需项。\n  \n\n## [[yii\\validators\\CompareValidator|compare（比对）]] <span id=\"compare\"></span>\n\n```php\n[\n    // 检查 \"password\" 属性的值是否与 \"password_repeat\" 的值相同\n    ['password', 'compare'],\n\n    // 和上一个相同，只是明确指定了需要对比的属性字段\n    ['password', 'compare', 'compareAttribute' => 'password_repeat'],\n    \n    // 检查年龄是否大于等于 30\n    ['age', 'compare', 'compareValue' => 30, 'operator' => '>='],\n]\n```\n\n该验证器比对两个特定输入值之间的关系\n是否与 `operator` 属性所指定的相同。\n\n- `compareAttribute`：用于与原属性相比对的属性名称。\n  当该验证器被用于验证某目标属性时，\n  该属性会默认为目标属性加后缀 `_repeat`。\n  举例来说，若目标属性为 `password`，则该属性默认为 `password_repeat`。\n- `compareValue`：用于与输入值相比对的常量值。\n  当该属性与 `compareAttribute` 属性同时被指定时，该属性优先被使用。\n- `operator`：比对操作符。默认为 `==`，意味着检查输入值是否与 `compareAttribute` 或 `compareValue` 的值相等。\n  该属性支持如下操作符：\n     * `==`：检查两值是否相等。比对为非严格模式。\n     * `===`：检查两值是否全等。比对为严格模式。\n     * `!=`：检查两值是否不等。比对为非严格模式。\n     * `!==`：检查两值是否不全等。比对为严格模式。\n     * `>`：检查待测目标值是否大于给定被测值。\n     * `>=`：检查待测目标值是否大于等于给定被测值。\n     * `<`：检查待测目标值是否小于给定被测值。\n     * `<=`：检查待测目标值是否小于等于给定被测值。\n- `type`: 默认的比对类型是'[[yii\\validators\\CompareValidator::TYPE_STRING|string]]'，此时将按照字节逐个对比。\n  当需要比对的值是数字时，需要设置类型[[yii\\validators\\CompareValidator::$type|$type]]为\n  '[[yii\\validators\\CompareValidator::TYPE_NUMBER|number]]'，启用数字对比模式。\n\n### 比对日期值\n\n比对验证器只能用来对比字符串和数字。如果你需要比对日期，有两种方式。\n如果需要比对一个固定的日期值，只需要使用\n[[yii\\validators\\DateValidator|date]]验证器并设置对应的属性\n[[yii\\validators\\DateValidator::$min|$min]] 或 [[yii\\validators\\DateValidator::$max|$max]] 。\n如果需要比对如表单提交的两个日期，比如一个`fromDate`和一个`toDate`项，\n你可以结合比对验证器和日期验证器同时使用，如下所示：\n\n```php\n['fromDate', 'date', 'timestampAttribute' => 'fromDate'],\n['toDate', 'date', 'timestampAttribute' => 'toDate'],\n['fromDate', 'compare', 'compareAttribute' => 'toDate', 'operator' => '<', 'enableClientValidation' => false],\n```\n\n因为验证器会按照顺序执行，\n将首先验证 `fromDate` 和 `toDate` 字段是一个有效的日期值，最终将被转换成一个系统可识别的格式。\n此后这两个值将使用比对验证器进行比对。\n因日期验证器只提供服务端使用，当前不提供客户端验证，\n故 [[yii\\validators\\CompareValidator::$enableClientValidation|$enableClientValidation]]\n在比对验证器将同样被设置为 `false` 。\n\n\n## [[yii\\validators\\DateValidator|date（日期）]] <span id=\"date\"></span>\n\n此日期 [[yii\\validators\\DateValidator|date]] 验证器有三种不同的使用方式：\n\n\n```php\n[\n    [['from_date', 'to_date'], 'date'],\n    [['from_datetime', 'to_datetime'], 'datetime'],\n    [['some_time'], 'time'],\n]\n```\n\n该验证器检查输入值是否为适当格式的 date，time，或者 datetime。\n另外，它还可以帮你把输入值转换为一个 UNIX 时间戳并保存到 \n[[yii\\validators\\DateValidator::timestampAttribute|timestampAttribute]] 所指定的属性里。\n\n- `format`：被验证值的日期/时间格式。\n   这里的值可以是 [ICU manual](https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax) 中定义的日期时间格式。\n   另外还可以设置以 `php:` 开头的字符串，用来表示PHP可以识别的日期时间格式。\n   `Datetime` 日期时间类。请参考 <https://www.php.net/manual/zh/datetime.createfromformat.php> 获取更多支持的格式。\n   如果没有设置，默认值将使用 `Yii::$app->formatter->dateFormat` 中的值。\n   请参考 [[yii\\validators\\DateValidator::$format|API 文档]] 以获取更详细的说明。\n\n- `timestampAttribute`：输入的日期时间将被转换为时间戳后设置到的属性的名称。\n  可以设置为和被验证的属性相同。如果相同，\n  原始值将在验证结束后被时间戳覆盖。\n  请参考 [\"Handling date input with the DatePicker\"](https://github.com/yiisoft/yii2-jui/blob/master/docs/guide/topics-date-picker.md) 以获取更多使用事例。\n\n  从版本 2.0.4 开始，支持\n  使用 [[yii\\validators\\DateValidator::$timestampAttributeFormat|$timestampAttributeFormat]] 设置日期时间格式\n  和使用 [[yii\\validators\\DateValidator::$timestampAttributeTimeZone|$timestampAttributeTimeZone]] 设置时区。\n  \n  注意，如果使用 `timestampAttribute`，被验证的值将被转换为UTC标准的时间戳，\n  所以使用 [[yii\\validators\\DateValidator::timeZone|input time zone]] 输入的时区将被转换为UTC时间。\n\n- 自版本 2.0.4 开始支持\n  直接对 [[yii\\validators\\DateValidator::$min|minimum]] 和 [[yii\\validators\\DateValidator::$max|maximum]] 设置时间戳。\n\n如果输入的值是可选的，可以设置一个 [默认值验证器](#default) 来确保空值通过验证器验证后的值是 `null`，\n否则数据库可能会保存类似 `0000-00-00` 的值，\n或表单日期选择器会显示 `1970-01-01` 。\n\n```php\n[\n    [['from_date', 'to_date'], 'default', 'value' => null],\n    [['from_date', 'to_date'], 'date'],\n],\n```\n\n## [[yii\\validators\\DefaultValueValidator|default（默认值）]] <span id=\"default\"></span>\n\n```php\n[\n    // 若 \"age\" 为空，则将其设为 null\n    ['age', 'default', 'value' => null],\n\n    // 若 \"country\" 为空，则将其设为 \"USA\"\n    ['country', 'default', 'value' => 'USA'],\n\n    // 若 \"from\" 和 \"to\" 为空，则分别给他们分配自今天起，3 天后和 6 天后的日期。\n    [['from', 'to'], 'default', 'value' => function ($model, $attribute) {\n        return date('Y-m-d', strtotime($attribute === 'to' ? '+3 days' ：'+6 days'));\n    }],\n]\n```\n\n该验证器并不进行数据验证。\n而是，给为空的待测属性分配默认值。\n\n- `value`：默认值，或一个返回默认值的 PHP Callable 对象（即回调函数）。\n  它们会分配给检测为空的待测属性。PHP 回调方法的样式如下：\n\n```php\nfunction foo($model, $attribute) {\n    // ... 计算 $value ...\n    return $value;\n}\n```\n\n> Info: 如何判断待测值是否为空，\n  被写在另外一个话题的\n  [处理空输入](input-validation.md#handling-empty-inputs)章节。\n\n\n## [[yii\\validators\\NumberValidator|double（双精度浮点型）]] <span id=\"double\"></span>\n\n```php\n[\n    // 检查 \"salary\" 是否为浮点数\n    ['salary', 'double'],\n]\n```\n\n该验证器检查输入值是否为双精度浮点数。等效于 [number](#number) 验证器。\n\n- `max`：上限值（含界点）。若不设置，则验证器不检查上限。\n- `min`：下限值（含界点）。若不设置，则验证器不检查下限。\n\n\n## [[yii\\validators\\EachValidator|each（循环验证）]] <span id=\"each\"></span>\n\n> Info: 此验证器自版本 2.0.4 后可用。\n\n```php\n[\n    // 检查是否每个分类编号都是一个整数\n    ['categoryIDs', 'each', 'rule' => ['integer']],\n]\n```\n\n此验证器只能验证数组格式的属性。此验证器将判断数组中的 *每个* 元素是否都符合验证规则。\n在上面的例子中，`categoryIDs` 属性必须是一个数组\n且每个元素将被使用 `integer` 验证器进行验证。\n\n- `rule`：保存验证规则的数组。数组中第一个元素表示验证器类名或\n  验证器的别名。数组中其余键值对将被用来配置此验证器规则。\n- `allowMessageFromRule`：是否使用规则中指定的验证器返回的错误信息。默认值为 `true`。\n  如果设置为 `false`，将使用 `message` 作为错误信息。\n\n> Note: 如果被验证的值不是一个数组，将被认为验证失败，\n  并且返回 `message` 设定的错误信息。\n  \n  \n## [[yii\\validators\\EmailValidator|email（电子邮件）]] <span id=\"email\"></span>\n\n```php\n[\n    // 检查 \"email\" 是否为有效的邮箱地址\n    ['email', 'email'],\n]\n```\n\n该验证器检查输入值是否为有效的邮箱地址。\n\n- `allowName`：检查是否允许带名称的电子邮件地址 (e.g. `张三 <John.san@example.com>`)。 默认为 false。\n- `checkDNS`：检查邮箱域名是否存在，且有没有对应的 A 或 MX 记录。\n  不过要知道，有的时候该项检查可能会因为临时性 DNS 故障而失败，\n  哪怕它其实是有效的。默认为 false。\n- `enableIDN`：验证过程是否应该考虑 IDN（internationalized domain names，国际化域名，也称多语种域名，比如中文域名）。\n  默认为 false。要注意但是为使用 IDN 验证功能，\n  请先确保安装并开启 `intl` PHP 扩展，不然会导致抛出异常。\n\n\n## [[yii\\validators\\ExistValidator|exist（存在性）]] <span id=\"exist\"></span>\n\n```php\n[\n    // a1 需要在 \"a1\" 属性所代表的字段内存在\n    ['a1', 'exist'],\n\n    // a1 必需存在，但检验的是 a1 的值在字段 a2 中的存在性\n    ['a1', 'exist', 'targetAttribute' => 'a2'],\n\n    // a1 和 a2 的值都需要存在，且它们都能收到错误提示\n    [['a1', 'a2'], 'exist', 'targetAttribute' => ['a1', 'a2']],\n\n    // a1 和 a2 的值都需要存在，只有 a1 能接收到错误信息\n    ['a1', 'exist', 'targetAttribute' => ['a1', 'a2']],\n\n    // 通过同时在 a2 和 a3 字段中检查 a2 和 a1 的值来确定 a1 的存在性\n    ['a1', 'exist', 'targetAttribute' => ['a2', 'a1' => 'a3']],\n\n    // a1 必需存在，若 a1 为数组，则其每个子元素都必须存在。\n    ['a1', 'exist', 'allowArray' => true],\n    \n    // type_id 需要存在于 ProductType 类中定义的表中的“id”列中\n    ['type_id', 'exist', 'targetClass' => ProductType::class, 'targetAttribute' => ['type_id' => 'id']],    \n    \n    // 与前一个相同，但使用已定义的关联“type”\n    ['type_id', 'exist', 'targetRelation' => 'type'],\n]\n```\n\n该验证器检查输入值是否在某表字段中存在。\n它只对[活动记录](db-active-record.md)\n类型的模型类属性起作用，\n能支持对一个或多过字段的验证。\n\n可以使用此验证器验证单个数据列或多个数据列\n（如多个列不同的组合是否存在）。\n\n- `targetClass`：用于查找输入值的目标 [AR](db-active-record.md) 类。\n  若不设置，则会使用正在进行验证的当前模型类。\n- `targetAttribute`：用于检查输入值存在性的 `targetClass` 的模型属性。\n  若不设置，它会直接使用待测属性名（整个参数数组的首元素）。\n  除了指定为字符串以外，你也可以用数组的形式，同时指定多个用于验证的表字段，\n  数组的键和值都是代表字段的属性名，值表示 `targetClass` 的待测数据源字段，而键表示当前模型的待测属性名。\n  若键和值相同，你可以只指定值。（如:`['a2']` 就代表 `['a2'=>'a2']`）\n- `targetRelation`: since version 2.0.14 you can use convenient attribute `targetRelation`, which overrides the `targetClass` and `targetAttribute` attributes using specs from the requested relation.  \n- `filter`：用于检查输入值存在性必然会进行数据库查询，而该属性为用于进一步筛选该查询的过滤条件。\n  可以为代表额外查询条件的字符串或数组（关于查询条件的格式，请参考 [[yii\\db\\Query::where()]]）；\n  或者样式为 `function ($query)` 的匿名函数，\n  `$query` 参数为你希望在该函数内进行修改的 [[yii\\db\\Query|Query]] 对象。\n- `allowArray`：是否允许输入值为数组。默认为 false。\n  若该属性为 true 且输入值为数组，则数组的每个元素都必须在目标字段中存在。\n  值得注意的是，若用吧 `targetAttribute` 设为多元素数组来验证被测值在多字段中的存在性时，该属性不能设置为 true。\n\n\n## [[yii\\validators\\FileValidator|file（文件）]] <span id=\"file\"></span>\n\n```php\n[\n    // 检查 \"primaryImage\" 是否为 PNG, JPG 或 GIF 格式的上传图片。\n    // 文件大小必须小于  1MB\n    ['primaryImage', 'file', 'extensions' => ['png', 'jpg', 'gif'], 'maxSize' => 1024*1024*1024],\n]\n```\n\n该验证器检查输入值是否为一个有效的上传文件。\n\n- `extensions`：可接受上传的文件扩展名列表。它可以是数组，\n  也可以是用空格或逗号分隔各个扩展名的字符串 (如 \"gif, jpg\")。\n  扩展名大小写不敏感。默认为 null，\n  意味着所有扩展名都被接受。\n- `mimeTypes`：可接受上传的 MIME 类型列表。\n  它可以是数组，也可以是用空格或逗号分隔各个 \n  MIME 的字符串 (如 \"image/jpeg, image/png\")。\n  Mime 类型名是大小写不敏感的。默认为 null，\n  意味着所有 MIME 类型都被接受。\n  请参考 [common media types](https://en.wikipedia.org/wiki/Media_type) 获取更多详细内容。\n- `minSize`：上传文件所需最少多少 Byte 的大小。默认为 null，代表没有下限。\n- `maxSize`：上传文件所需最多多少 Byte 的大小。默认为 null，代表没有上限。\n- `maxFiles`：给定属性最多能承载多少个文件。\n  默认为 1，代表只允许单文件上传。\n  若值大于一，那么输入值必须为包含最多 `maxFiles` 个上传文件元素的数组。\n- `checkExtensionByMimeType`：是否通过文件的 MIME 类型来判断其文件扩展。\n  若由 MIME 判定的文件扩展与给定文件的扩展不一样，则文件会被认为无效。\n  默认为 true，代表执行上述检测。\n\n`FileValidator` 通常与 [[yii\\web\\UploadedFile]] 共同使用。\n请参考 [文件上传](input-file-upload.md)章节来了解有关文件上传与上传文件的检验的全部内容。\n\n\n## [[yii\\validators\\FilterValidator|filter（过滤器）]] <span id=\"filter\"></span>\n\n```php\n[\n    // trim 掉 \"username\" 和 \"email\" 输入\n    [['username', 'email'], 'filter', 'filter' => 'trim', 'skipOnArray' => true],\n\n    // 标准化 \"phone\" 输入\n    ['phone', 'filter', 'filter' => function ($value) {\n        // 在此处标准化输入的电话号码\n        return $value;\n    }],\n    \n    // 标准化 \"phone\" 使用方法 \"normalizePhone\"\n    ['phone', 'filter', 'filter' => [$this, 'normalizePhone']],\n    \n    public function normalizePhone($value) {\n        return $value;\n    }\n]\n```\n\n该验证器并不进行数据验证。而是给输入值应用一个过滤器，\n并在验证后把它赋值回原属性变量。\n\n- `filter`：用于定义过滤器的 PHP 回调函数。可以为全局函数名，匿名函数，或其他。\n  该函数的样式必须是 `function ($value) { return $newValue; }`。该属性不能省略，必须设置。\n- `skipOnArray`：是否在输入值为数组时跳过过滤器。默认为 false。\n  请注意如果过滤器不能处理数组输入，你就应该把该属性设为 true。\n  否则可能会导致 PHP Error 的发生。\n\n> Tip: 如果你只是想要用 trim 处理下输入值，你可以直接用 [trim](#trim) 验证器的。\n\n> Tip: 有许多的PHP方法结构和 `filter` 需要的结构一致。\n> 比如使用类型转换方法 ([intval](https://www.php.net/manual/zh/function.intval.php)，\n> [boolval](https://www.php.net/manual/zh/function.boolval.php), ...) 来确保属性为指定的类型，\n> 你可以简单的设置这些方法名而不是重新定义一个匿名函数：\n>\n> ```php\n> ['property', 'filter', 'filter' => 'boolval'],\n> ['property', 'filter', 'filter' => 'intval'],\n> ```\n\n\n## [[yii\\validators\\ImageValidator|image（图片）]] <span id=\"image\"></span>\n\n```php\n[\n    // 检查 \"primaryImage\" 是否为适当尺寸的有效图片\n    ['primaryImage', 'image', 'extensions' => 'png, jpg',\n        'minWidth' => 100, 'maxWidth' => 1000,\n        'minHeight' => 100, 'maxHeight' => 1000,\n    ],\n]\n```\n\n该验证器检查输入值是否为代表有效的图片文件。它继承自 [file](#file) 验证器，\n并因此继承有其全部属性。除此之外，\n它还支持以下为图片检验而设的额外属性：\n\n- `minWidth`：图片的最小宽度。默认为 null，代表无下限。\n- `maxWidth`：图片的最大宽度。默认为 null，代表无上限。\n- `minHeight`：图片的最小高度。 默认为 null，代表无下限。\n- `maxHeight`：图片的最大高度。默认为 null，代表无上限。\n\n## [[yii\\validators\\IpValidator|ip（IP地址）]] <span id=\"ip\"></span>\n```php\n[\n    // 检查 \"ip_address\" 是否为一个有效的 IPv4 或 IPv6 地址\n    ['ip_address', 'ip'],\n\n    // 检查 \"ip_address\" 是否为一个有效的 IPv6 地址或子网地址，\n    // 被检查的值将被展开成为一个完整的 IPv6 表示方法。\n    ['ip_address', 'ip', 'ipv4' => false, 'subnet' => null, 'expandIPv6' => true],\n\n    // 检查 \"ip_address\" 是否为一个有效的 IPv4 或 IPv6 地址，\n    // 允许地址存在一个表示非的字符 `!`\n    ['ip_address', 'ip', 'negation' => true],\n]\n```\n\n此验证器检查属性的值是否是一个有效的 IPv4/IPv6 地址或子网地址。\n如果标准记法或 IPv6 扩展记法被启用，原始值将被改变。\n\n此验证器有以下参数：\n\n- `ipv4`: 是否启用 IPv4 地址检测。默认为 true 。\n- `ipv6`: 是否启用 IPv6 地址检测。默认为 true。\n- `subnet`: 是否启用 CIDR 子网检测，类似 `192.168.10.0/24`\n     * `true` - 子网是必须的，如果不是标准 CIDR 格式将被拒绝\n     * `false` - 地址不能有 CIDR\n     * `null` - CIDR 是可选的\n\n    默认值为 false。\n- `normalize`: 是否在地址没有 CIDR 时添加 CIDR 最小值作为后缀 (IPv4 为 32、IPv6 为 128)\n仅当 `subnet` 不为 `false`时使用。例如：\n    * `10.0.1.5` 将被转换为标准的`10.0.1.5/32`\n    * `2008:db0::1` 将被转换为标准的 `2008:db0::1/128`\n\n    默认为 false。\n- `negation`: 是否允许被检测地址在首位存在代表非的字符 `!` 。默认为 false。\n- `expandIPv6`: 是否将简化的 IPv6 地址扩展为标准记法格式。\n例如， `2008:db0::1` 将被扩展成 `2008:0db0:0000:0000:0000:0000:0000:0001`。默认为 false。\n- `ranges`: 允许或禁止的 IPv4 或 IPv6 范围的数组。\n\n    当此数组为空或此参数没有设置时，所有IP地址都被允许。\n    否则，将逐个对比此规则，当找到第一条适用时停止。\n    如果没有任何规则适用待验证的IP地址，IP地址将被禁止。\n    \n    例如：\n    ```php\n    [\n         'client_ip', 'ip', 'ranges' => [\n             '192.168.10.128'\n             '!192.168.10.0/24',\n             'any' // 允许任何其它IP地址\n         ]\n    ]\n    ```\n在这个例子中，除了 `192.168.10.0/24` 子网之外的所有 IPv4 和 IPv6 地址都被允许。\nIPv4 地址 `192.168.10.128` 同样时允许的，因为这条规则在约束规则之前适用。\n- `networks`: 网络别名数组， 可以用在 `ranges` 中。数组格式：\n    * key - 别名\n    * value - 一组字符串。每个子字符串可以是一个范围，IP地址或另一个别名。子字符串也可以\n    设置一个表示非的字符 `!` (和 `negation` 不相关)。\n\n    下列别名为默认定义：\n    \n    * `*`: `any`\n    * `any`: `0.0.0.0/0, ::/0`\n    * `private`: `10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fd00::/8`\n    * `multicast`: `224.0.0.0/4, ff00::/8`\n    * `linklocal`: `169.254.0.0/16, fe80::/10`\n    * `localhost`: `127.0.0.0/8', ::1`\n    * `documentation`: `192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24, 2001:db8::/32`\n    * `system`: `multicast, linklocal, localhost, documentation`\n\n> Info: 此验证器自版本 2.0.7 后可用。\n\n## [[yii\\validators\\RangeValidator|in（范围）]] <span id=\"in\"></span>\n\n```php\n[\n    // 检查 \"level\" 是否为 1、2 或 3 中的一个\n    ['level', 'in', 'range' => [1, 2, 3]],\n]\n```\n\n该验证器检查输入值是否存在于给定列表的范围之中。\n\n- `range`：用于检查输入值的给定值列表。\n- `strict`：输入值与给定值直接的比较是否为严格模式（也就是类型与值都要相同，即全等）。\n  默认为 false。\n- `not`：是否对验证的结果取反。默认为 false。当该属性被设置为 true，\n  验证器检查输入值是否**不在**给定列表内。\n- `allowArray`：是否接受输入值为数组。当该值为 true 且输入值为数组时，\n  数组内的每一个元素都必须在给定列表内存在，否则返回验证失败。\n\n\n## [[yii\\validators\\NumberValidator|integer（整数）]] <span id=\"integer\"></span>\n\n```php\n[\n    // 检查 \"age\" 是否为整数\n    ['age', 'integer'],\n]\n```\n\n该验证器检查输入值是否为整形。\n\n- `max`：上限值（含界点）。若不设置，则验证器不检查上限。\n- `min`：下限值（含界点）。若不设置，则验证器不检查下限。\n\n\n## [[yii\\validators\\RegularExpressionValidator|match（正则表达式）]] <span id=\"match\"></span>\n\n```php\n[\n    // 检查 \"username\" 是否由字母开头，且只包含单词字符\n    ['username', 'match', 'pattern' => '/^[a-z]\\w*$/i']\n]\n```\n\n该验证器检查输入值是否匹配指定正则表达式。\n\n- `pattern`：用于检测输入值的正则表达式。该属性是必须的，\n  若不设置则会抛出异常。\n- `not`：是否对验证的结果取反。默认为 false，\n  代表输入值匹配正则表达式时验证成功。如果设为 true，\n  则输入值不匹配正则时返回匹配成功。\n\n\n## [[yii\\validators\\NumberValidator|number（数字）]] <span id=\"number\"></span>\n\n```php\n[\n    // 检查 \"salary\" 是否为数字\n    ['salary', 'number'],\n]\n```\n\n该验证器检查输入值是否为数字。他等效于 [double](#double) 验证器。\n\n- `max`：上限值（含界点）。若不设置，则验证器不检查上限。\n- `min`：下限值（含界点）。若不设置，则验证器不检查下限。\n\n\n## [[yii\\validators\\RequiredValidator|required（必填）]] <span id=\"required\"></span>\n\n```php\n[\n    // 检查 \"username\" 与 \"password\" 是否为空\n    [['username', 'password'], 'required'],\n]\n```\n\n该验证器检查输入值是否为空，还是已经提供了。\n\n- `requiredValue`：所期望的输入值。若没设置，意味着输入不能为空。\n- `strict`：检查输入值时是否检查类型。默认为 false。\n  当没有设置 `requiredValue` 属性时，若该属性为 true，\n  验证器会检查输入值是否严格为 null；若该属性设为 false，\n  该验证器会用一个更加宽松的规则检验输入值是否为空。\n  当设置了 `requiredValue` 属性时，若该属性为 true，输入值与 `requiredValue` 的比对会同时检查数据类型。\n\n> Note: 如何判断待测值是否为空，被写在另外一个话题的\n  [处理空输入](input-validation.md#handling-empty-inputs)章节。\n\n\n## [[yii\\validators\\SafeValidator|safe（安全）]] <span id=\"safe\"></span>\n\n```php\n[\n    // 标记 \"description\" 为安全属性\n    ['description', 'safe'],\n]\n```\n\n该验证器并不进行数据验证。而是把一个属性标记为\n[安全属性](structure-models.md#safe-attributes)。\n\n\n## [[yii\\validators\\StringValidator|string（字符串）]] <span id=\"string\"></span>\n\n```php\n[\n    // 检查 \"username\" 是否为长度 4 到 24 之间的字符串\n    ['username', 'string', 'length' => [4, 24]],\n]\n```\n\n该验证器检查输入值是否为特定长度的字符串。并检查属性的值是否为某个特定长度。\n\n- `length`：指定待测输入字符串的长度限制。\n  该属性可以被指定为以下格式之一：\n     * 证书：the exact length that the string should be of;\n     * 单元素数组：代表输入字符串的最小长度 (e.g. `[8]`)。这会重写 `min` 属性。\n     * 包含两个元素的数组：代表输入字符串的最小和最大长度(e.g. `[8, 128]`)。\n     这会同时重写 `min` 和 `max` 属性。\n- `min`：输入字符串的最小长度。若不设置，则代表不设下限。\n- `max`：输入字符串的最大长度。若不设置，则代表不设上限。\n- `encoding`：待测字符串的编码方式。若不设置，则使用应用自身的 [[yii\\base\\Application::charset|charset]] 属性值，\n  该值默认为 `UTF-8`。\n\n\n## [[yii\\validators\\FilterValidator|trim（译为修剪/裁边）]] <span id=\"trim\"></span>\n\n```php\n[\n    // trim 掉 \"username\" 和 \"email\" 两侧的多余空格\n    [['username', 'email'], 'trim'],\n]\n```\n\n该验证器并不进行数据验证。而是，trim 掉输入值两侧的多余空格。\n注意若该输入值为数组，那它会忽略掉该验证器。\n\n\n## [[yii\\validators\\UniqueValidator|unique（唯一性）]] <span id=\"unique\"></span>\n\n```php\n[\n    // a1 需要在 \"a1\" 属性所代表的字段内唯一\n    ['a1', 'unique'],\n\n    // a1 需要唯一，但检验的是 a1 的值在字段 a2 中的唯一性\n    ['a1', 'unique', 'targetAttribute' => 'a2'],\n\n    // a1 和 a2 的组合需要唯一，且它们都能收到错误提示\n    [['a1', 'a2'], 'unique', 'targetAttribute' => ['a1', 'a2']],\n\n    // a1 和 a2 的组合需要唯一，只有 a1 能接收错误提示\n    ['a1', 'unique', 'targetAttribute' => ['a1', 'a2']],\n\n    // 通过同时在 a2 和 a3 字段中检查 a2 和 a3 的值来确定 a1 的唯一性\n    ['a1', 'unique', 'targetAttribute' => ['a2', 'a1' => 'a3']],\n]\n```\n\n该验证器检查输入值是否在某表字段中唯一。\n它只对[活动记录](db-active-record.md)类型的模型类属性起作用，\n能支持对一个或多过字段的验证。\n\n- `targetClass`：用于查找输入值的目标 [AR](db-active-record.md) 类。\n  若不设置，则会使用正在进行验证的当前模型类。\n- `targetAttribute`：用于检查输入值唯一性的 `targetClass` 的模型属性。\n  若不设置，它会直接使用待测属性名（整个参数数组的首元素）。\n  除了指定为字符串以外，你也可以用数组的形式，同时指定多个用于验证的表字段，数组的键和值都是代表字段的属性名，\n  值表示 `targetClass` 的待测数据源字段，而键表示当前模型的待测属性名。\n  若键和值相同，你可以只指定值。（如:`['a2']` 就代表 `['a2'=>'a2']`）\n- `filter`：用于检查输入值唯一性必然会进行数据库查询，\n  而该属性为用于进一步筛选该查询的过滤条件。可以为代表额外查询条件的字符串或数组\n  （关于查询条件的格式，请参考 [[yii\\db\\Query::where()]]）；或者样式为 `function ($query)` 的匿名函数，\n  `$query` 参数为你希望在该函数内进行修改的 [[yii\\db\\Query|Query]] 对象。\n\n\n## [[yii\\validators\\UrlValidator|url（网址）]] <span id=\"url\"></span>\n\n```php\n[\n    // 检查 \"website\" 是否为有效的 URL。若没有 URI 方案，\n    // 则给 \"website\" 属性加 \"http://\" 前缀\n    ['website', 'url', 'defaultScheme' => 'http'],\n]\n```\n\n该验证器检查输入值是否为有效 URL。\n\n- `validSchemes`：用于指定那些 URI 方案会被视为有效的数组。默认为 `['http', 'https']`，\n  代表 `http` 和 `https` URLs 会被认为有效。\n- `defaultScheme`：若输入值没有对应的方案前缀，会使用的默认 URI 方案前缀。\n  默认为 null，代表不修改输入值本身。\n- `enableIDN`：验证过程是否应该考虑 IDN（internationalized domain names，国际化域名，也称多语种域名，比如中文域名）。\n  默认为 false。要注意但是为使用 IDN 验证功能，\n  请先确保安装并开启 `intl` PHP 扩展，不然会导致抛出异常。\n\n> Note: 验证器检查 URL 结构和主机部分是否正确。\n  它不会检查URL的其余部分，也不是为防止XSS或任何其他攻击而设计的。请参阅 [安全最佳实践](security-best-practices.md)\n  有关开发应用程序时威胁防范的更多信息的文章。\n"
  },
  {
    "path": "docs/guide-zh-CN/tutorial-docker.md",
    "content": "Yii 和 Docker\n=============\n\n对于开发和部署 Yii 应用程序可以使用 Docker 容器运行。容器就像一个轻量级的独立虚拟机，它将其服务映射到主机的端口，即在端口 80 上的容器中的Web服务器在您的（本地）主机上的端口 8888 上可用。\n\n容器可以解决许多问题，例如在开发人员计算机和服务器上具有相同的软件版本，在开发时快速部署或模拟多服务器体系结构。\n\n您可以在 [docker.com](https://www.docker.com/why-docker) 上阅读有关Docker容器的更多信息。\n\n## 要求\n\n- `docker`\n- `docker-compose`\n\n访问[下载页面](https://www.docker.com/products/container-runtime)获取 Docker 工具。\n\n## 安装\n\n安装后，你应该可以运行 `docker ps` 并看到类似的输出：\n\n```\nCONTAINER ID   IMAGE   COMMAND   CREATED   STATUS   PORTS\n```\n\n这意味着您的Docker守护进程已启动并正在运行。\n\n另外运行 `docker-compose version`，你的输出应该是这样的\n\n```\ndocker-compose version 1.20.0, build unknown\ndocker-py version: 3.1.3\nCPython version: 3.6.4\nOpenSSL version: OpenSSL 1.1.0g  2 Nov 2017\n```\n\n使用 Compose，您可以配置管理您的应用程序所需的所有服务，例如数据库和缓存。\n\n## 资源\n\n- 基于 PHP 的 Yii 镜像可以在这里找到 [yii2-docker](https://github.com/yiisoft/yii2-docker)\n- Docker 支持的 [yii2-app-basic](https://github.com/yiisoft/yii2-app-basic#install-with-docker)\n- Docker 支持的 [yii2-app-advanced](https://github.com/yiisoft/yii2-app-advanced/pull/347) 正在开发中\n\n## 用法\n\nDocker的基本命令是：\n\n    docker-compose up -d\n    \n在后台启动堆栈中的所有服务\n\n    docker-compose ps\n    \n列出正在运行的服务\n\n    docker-compose logs -f\n    \n持续查看所有服务的日志\n\n    docker-compose stop\n    \n优雅地停止堆栈中的所有服务\n\n    docker-compose kill\n    \n立即停止堆栈中的所有服务\n\n    docker-compose down -v\n    \n停止并删除所有服务，**在不使用 host-volumes 时注意数据丢失**\n\n在容器中运行命令\n\n    docker-compose run --rm php composer install\n    \n在新的容器中运行 composer 安装\n\n    docker-compose exec php bash\n    \n在 *运行中的* `php` 服务中执行 bash\n\n\n## 高级主题\n\n### Yii 框架测试\n\n你可以按照[这里](https://github.com/yiisoft/yii2/blob/master/tests/README.md#dockerized-testing)描述的方式为 Yii 本身运行 dockerized 框架测试。\n\n### 数据库管理工具\n\n以 MySQL（`mysql`）的形式运行MySQL时，可以将 phpMyAdmin 容器添加到您的堆栈中，如下所示：\n\n```\n    phpmyadmin:\n        image: phpmyadmin/phpmyadmin\n        ports:\n            - '8888:80'\n        environment:\n            - PMA_ARBITRARY=1\n            - PMA_HOST=mysql\n        depends_on:\n            - mysql\n```\n"
  },
  {
    "path": "docs/guide-zh-CN/tutorial-i18n.md",
    "content": "国际化（Internationalization）\n===========================\n\n国际化（I18N）是指在设计软件时，使它可以无需做大的改变就能够适应不同的语言和地区的需要。对于 Web 应用程序，\n这有着特别重要的意义，因为潜在的用户可能会在全球范围内。\nYii 提供的国际化功能支持全方位信息翻译，\n视图翻译，日期和数字格式化。\n\n\n## 区域和语言（Locale and Language） <span id=\"locale-language\"></span>\n\n### 区域（Locale）\n\n区域设置是一组参数以定义用户希望能在他们的用户界面所看到用户的语言，\n国家和任何特殊的偏好。\n它通常是由语言 ID 和区域 ID 组成。\n\n例如，ID “en-US” 代表英语和美国的语言环境。为了保持一致性，\n\n在 Yii 应用程序中使用的所有区域 ID 应该规范化为 `ll-CC`，\n其中 `ll` 是根据两个或三个字母的小写字母语言代码\n[ISO-639](https://www.loc.gov/standards/iso639-2/) 和 `CC` 是两个字母的国别代码\n[ISO-3166](https://en.wikipedia.org/wiki/ISO_3166-1#Current_codes)。\n有关区域设置的更多细节可以看\n[ICU 项目文档](https://unicode-org.github.io/icu/userguide/locale/#the-locale-concept)。\n\n### 语言（Language）\n\n在 Yii中，我们经常用 “language” 来代表一个区域。\n\nYii 应用程序使用两种语言：\n* [[yii\\base\\Application::$sourceLanguage|源语言]]：前者指的是写在代码中的语言，后者是向最终用户显示内容的语言。\n* [[yii\\base\\Application::$language|目标语言]]：而信息翻译服务主要是将文本消息从原语言翻译到目标语言。\n\n所谓的消息翻译服务主要将文本消息从源语言转换为目标语言。\n\n### 配置（Configuration）\n可以用类似下面的应用程序配置来配置应用程序语言：\n\n```php\nreturn [\n    // 设置目标语言为俄语\n    'language' => 'ru-RU',\n    \n    // 设置源语言为英语\n    'sourceLanguage' => 'en-US',\n    \n    ......\n];\n```\n\n默认的 [[yii\\base\\Application::$sourceLanguage|源语言]] 值是 `en-US`，即美国英语。\n建议你保留此默认值不变，\n因为通常让人将英语翻译成其它语言要比将其它语言翻译成其它语言容易得多。\n\n你经常需要根据不同的因素来动态地设置 [[yii\\base\\Application::$language|目标语言]] ，\n如最终用户的语言首选项。要在应用程序配置中配置它，\n你可以使用下面的语句来更改目标语言：\n\n```php\n// 改变目标语言为中文\n\\Yii::$app->language = 'zh-CN';\n```\n\n> Tip: 如果您的源语言在代码的不同部分中有所不同，那么您可以覆盖不同消息源的源语言，\n> 这将在下一节中介绍。\n\n## 消息翻译（Message Translation） <span id=\"message-translation\"></span>\n\n### 从源语言到目标语言（From source language to target language）\n消息翻译服务用于将一条文本信息从一种语言（通常是 [[yii\\base\\Application::$sourceLanguage|源语言]] ）\n翻译成另一种语言（通常是 [[yii\\base\\Application::$language|目标语言]]）。\n\n它的翻译原理是通过在语言文件中查找要翻译的信息以及翻译的结果。如果要翻译的信息可以在语言文件中找到，会返回相应的翻译结果；\n否则会返回原始未翻译的信息。\n\n### 如何实现（How to implement）\n为了使用消息翻译服务，需要做如下工作：\n\n1. 调用 [[Yii::t()]] 方法且在其中包含每一条要翻译的消息；\n2. 配置一个或多个消息来源，能在其中找得到要翻译的消息和翻译结果；\n3. 让译者翻译信息并将它们存储在消息来源。\n\n\n#### 1. 包裹一条消息（Wrap a text message）\n这个 [[Yii::t()]] 方法的用法如下，\n\n```php\necho \\Yii::t('app', 'This is a string to translate!');\n```\n\n第一个参数指储存消息来源的类别名称，\n第二个参数指需要被翻译的消息。\n\n#### 2. 配置一个或多个消息源（Configure one or multiple message sources）\n这个 [[Yii::t()]] 方法会调用 `i18n` [应用组件](structure-application-components.md) \n来实现翻译工作。这个组件可以在应用程序中按下面的代码来配置，\n\n```php\n'components' => [\n    // ...\n    'i18n' => [\n        'translations' => [\n            'app*' => [\n                'class' => 'yii\\i18n\\PhpMessageSource',\n                //'basePath' => '@app/messages',\n                //'sourceLanguage' => 'en-US',\n                'fileMap' => [\n                    'app' => 'app.php',\n                    'app/error' => 'error.php',\n                ],\n            ],\n        ],\n    ],\n],\n```\n\n在上面的代码中，正在配置 [[yii\\i18n\\PhpMessageSource]] 支持的消息源。\n\n##### 带 `*` 符号的类别通配符（Category wildcards with `*` symbol）\n\n在上面的代码中，配置了由 [[yii\\i18n\\PhpMessageSource]] 所支持的消息来源。模式 `app*` 表示所有以 `app`\n开头的消息类别名称都使用这个翻译的消息来源。\n\n#### 3. 让译员翻译消息并将它们存储在消息源中（Let the translators translate messages and store them in the message source(s)）\n\n[[yii\\i18n\\PhpMessageSource]] 类使用PHP文件和一个简单的 PHP 数组来存储消息转换。\n这些文件包含 `源语言` 中的消息到 `目标语言` 中的翻译的映射。\n\n> Info: 您可以使用 [`message` 命令](#message-command) 自动生成这些 PHP 文件，\n> 这将在本章后面介绍。\n\n每个PHP文件对应于单个类别的消息。 默认情况下，文件名应与类别名称相同。\n`app/messages/nl-NL/main.php` 的例子：\n\n```php\n<?php\n\n/**\n* Translation map for nl-NL\n*/\nreturn [\n    'welcome' => 'welkom'\n];\n\n```\n\n\n##### 文件映射（File mapping）\n\n[[yii\\i18n\\PhpMessageSource::fileMap|fileMap]] 来映射一个类别到不同名称的 PHP 文件。 \n\n在上面的例子中，类别 `app/error` 被映射到PHP文件 `@app/messages/ru-RU/error.php`（假设 `ru-RU` 为目标语言）。\n如果没有此配置，\n该类别将被映射到 `@app/messages/ru-RU/app/error.php` 。\n\n#####  其他存储类型（Other storage types）\n\n除了在PHP文件中存储消息来源，\n也可以使用下面的消息来源在不同的存储来存储翻译的消息：\n\n- [[yii\\i18n\\GettextMessageSource]] 使用 GNU Gettext 的 MO 或 PO 文件保存翻译的消息。\n- [[yii\\i18n\\DbMessageSource]] 使用一个数据库表来存储翻译的消息。\n\n\n## 消息格式化（Message Formatting） <span id=\"message-formatting\"></span>\n\n在要翻译的消息里，你可以嵌入一些占位符，并让它们通过动态的参数值来代替。\n你甚至可以根据目标语言格式的参数值来使用特殊的占位符。\n在本节中，我们将介绍如何用不同的方式来格式化消息。\n\n### 消息参数（Message Parameters） <span id=\"message-parameters\"></span>\n\n在待翻译的消息，可以嵌入一个或多个占位符，以便它们可以由给定的参数值取代。\n通过给不同的参数值，可以动态地改变翻译内容的消息。\n在下面的例子中，\n占位符 `{username}` 在 `“Hello, {username}！”` 中将分别被 `'Alexander'`和`'Qiang'` 所替换。\n\n```php\n$username = 'Alexander';\n// 输出：“Hello, Alexander”\necho \\Yii::t('app', 'Hello, {username}!', [\n    'username' => $username,\n]);\n\n$username = 'Qiang';\n// 输出：“Hello, Qiang”\necho \\Yii::t('app', 'Hello, {username}!', [\n    'username' => $username,\n]);\n```\n\n当翻译的消息包含占位符时，应该让占位符保留原样。\n这是因为调用 `Yii::t()` 时，占位符将被实际参数值代替。\n\n你可以使用 *名称占位符* 或者 *位置占位符*，但不能两者都用在同一个消息里。\n\n前面的例子说明了如何使用名称占位符。即每个占位符的格式为 `{参数名称}` ，你所提供的参数作为关联数组，\n其中数组的键是参数名称（没有大括号），\n数组的值是对应的参数值。\n\n位置占位符是使用基于零的整数序列，在调用 `Yii::t()` 时会参数值根据它们出现位置的顺序分别进行替换。\n在下面的例子中，位置占位符 `{0}`，`{1}` 和 `{2}` \n将分别被 `$price`，`$count` 和 `$subtotal` 所替换。\n\n```php\n$price = 100;\n$count = 2;\n$subtotal = 200;\necho \\Yii::t('app', 'Price: {0}, Count: {1}, Subtotal: {2}', $price, $count, $subtotal);\n```\n\n在单个位置参数的情况下，它的值可以被指定而不包含在数组中：\n\n```php\necho \\Yii::t('app', 'Price: {0}', $price);\n```\n\n> Tip: 大多数情况下你应该使用名称占位符。\n> 这是因为参数名称可以让翻译者更好的理解要被翻译的消息。\n\n\n### 格式化参数 <span id=\"parameter-formatting\"></span>\n\n你可以在消息的占位符指定附加格式的规则，\n这样的参数值可在替换占位符之前格式化它们。在下面的例子中，\n价格参数值将视为一个数并格式化为货币值：\n\n```php\n$price = 100;\necho \\Yii::t('app', 'Price: {0, number, currency}', $price);\n```\n\n> Note: 参数的格式化需要安装 [intl PHP 扩展](https://www.php.net/manual/zh/intro.intl.php)。\n\n可以使用缩写的形式或完整的形式来格式化占位符：\n\n```\nshort form: {PlaceholderName, ParameterType}\nfull form: {PlaceholderName, ParameterType, ParameterStyle}\n```\n\n> Note: 如果您需要使用特殊字符（如 `{`，`}`，`'`，`#`，请使用 `'`：\n> \n```php\necho Yii::t('app', \"Example of string with ''-escaped characters'': '{' '}' '{test}' {count,plural,other{''count'' value is # '#{}'}}\", ['count' => 3]);\n```\n\n请参阅 [ICU 文档](https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classMessageFormat.html)\n关于如何指定这样的占位符的说明。接下来我们会展示一些常用的使用方法。\n\n\n#### 数字（Number） <span id=\"number\"></span>\n\n参数值应该被格式化为一个数。例如，\n\n```php\n$sum = 42;\necho \\Yii::t('app', 'Balance: {0, number}', $sum);\n```\n\n你可以指定参数的格式为 `integer`（整型），`currency` （货币），或者 `percent` （百分数）：\n\n```php\n$sum = 42;\necho \\Yii::t('app', 'Balance: {0, number, currency}', $sum);\n```\n\n你也可以指定一个自定义模式来格式化数字。 例如，\n\n```php\n$sum = 42;\necho \\Yii::t('app', 'Balance: {0, number, ,000,000000}', $sum);\n```\n\n自定义格式中使用的字符可以在“特殊模式字符”一节的\n[ICU API 参考](https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classDecimalFormat.html)\n中找到。\n\n\n该值始终根据您翻译的区域设置进行格式设置，即无需更改翻译区域设置即可更改小数或千位分隔符，货币符号等。 \n如果你需要定制这些，你可以使用\n[[yii\\i18n\\Formatter::asDecimal()]] 和 [[yii\\i18n\\Formatter::asCurrency()]]。\n\n#### 日期（Date） <span id=\"date\"></span>\n\n该参数值应该被格式化为一个日期。 例如，\n\n```php\necho \\Yii::t('app', 'Today is {0, date}', time());\n```\n\n你可以指定一个可选的参数格式 `short` ，`medium` ，`long` ，或 `full` ：\n\n```php\necho \\Yii::t('app', 'Today is {0, date, short}', time());\n```\n\n你还可以指定一个自定义模式来格式化日期：\n\n```php\necho \\Yii::t('app', 'Today is {0, date, yyyy-MM-dd}', time());\n```\n\n[格式化参考](https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1SimpleDateFormat.html#details)。\n\n\n#### 时间（Time） <span id=\"time\"></span>\n\n参数值应该被格式化为一个时间。 例如，\n\n```php\necho \\Yii::t('app', 'It is {0, time}', time());\n```\n\n你可以指定一个可选的参数格式 `short` ，`medium` ，`long` ，或 `full` ：\n\n```php\necho \\Yii::t('app', 'It is {0, time, short}', time());\n```\n\n你还可以指定一个自定义模式来格式化时间：\n\n```php\necho \\Yii::t('app', 'It is {0, date, HH:mm}', time());\n```\n\n[格式化参考](https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1SimpleDateFormat.html#details)。\n\n\n#### 拼写（Spellout） <span id=\"spellout\"></span>\n\n参数值为一个数并被格式化为它的字母拼写形式。 例如，\n\n```php\n// 输出：\"42 is spelled as forty-two\"\necho \\Yii::t('app', '{n,number} is spelled as {n, spellout}', ['n' => 42]);\n```\n\n默认情况下，该数字拼写为基数。 它可以改变：\n\n```php\n// may produce \"I am forty-seventh agent\"\necho \\Yii::t('app', 'I am {n,spellout,%spellout-ordinal} agent', ['n' => 47]);\n```\n\n请注意，在 `spellout,` 之后和 `%` 之前不应该有空格。\n\n要获取可用于您正在使用的语言环境的选项列表，请查看\n[https://intl.rmcreative.ru/](https://intl.rmcreative.ru/) 上的“编号模式，拼写”。\n\n#### 序数词（Ordinal） <span id=\"ordinal\"></span>\n\n参数值为一个数并被格式化为一个序数词。 例如，\n\n```php\n// 输出：\"You are the 42nd visitor here!\"\necho \\Yii::t('app', 'You are the {n, ordinal} visitor here!', ['n' => 42]);\n```\n\nOrdinal 支持更多格式化西班牙语等语言的方式：\n\n```php\n// may produce 471ª\necho \\Yii::t('app', '{n,ordinal,%digits-ordinal-feminine}', ['n' => 471]);\n```\n\n请注意，在 `ordinal,` 之后和 `%` 之前不应该有空格。\n\n要获取可用于您正在使用的语言环境的选项列表，请查看\n[https://intl.rmcreative.ru/](https://intl.rmcreative.ru/) 上的“编号模式，序号”。\n\n#### 持续时间（Duration） <span id=\"duration\"></span>\n\n参数值为秒数并被格式化为持续的时间段。 例如，\n\n```php\n// 输出：\"You are here for 47 sec. already!\"\necho \\Yii::t('app', 'You are here for {n, duration} already!', ['n' => 47]);\n```\n\n持续时间支持更多格式化方法：\n\n```php\n// may produce 130:53:47\necho \\Yii::t('app', '{n,duration,%in-numerals}', ['n' => 471227]);\n```\n\n请注意，在 `duration,` 之后和 `%` 之前不应该有空格。\n\n要获取您正在使用的区域设置的可用选项列表，请查看\n[https://intl.rmcreative.ru/](https://intl.rmcreative.ru/) 上的“编号模式，持续时间”。\n\n#### 复数（Plural） <span id=\"plural\"></span>\n\n不同的语言有不同的方式来表示复数。 Yii 提供一个便捷的途径，\n即使是非常复杂的规则也使翻译消息时不同的复数形式行之有效。\n取之以直接处理词形变化规则，它是足以面对某些词形变化语言的翻译。 例如，\n\n```php\n// 当 $n = 0 时，输出：\"There are no cats!\"\n// 当 $n = 1 时，输出：\"There is one cat!\"\n// 当 $n = 42 时，输出：\"There are 42 cats!\"\necho \\Yii::t('app', 'There {n, plural, =0{are no cats} =1{is one cat} other{are # cats}}!', ['n' => $n]);\n```\n\n在上面的多个规则的参数中， `=0` 意味着 `n` 的值是 0 ，`=1` 意味着 `n` 的值是 1 ， 而 `other` 则是对于其它值，\n`#` 会被 `n` 中的值给替代。 \n\n复数形式可以是某些非常复杂的语言。下面以俄罗斯为例，`=1` 完全匹配 `n = 1`，\n而 `one` 匹配 `21` 或 `101`：\n\n```\nЗдесь {n, plural, =0{котов нет} =1{есть один кот} one{# кот} few{# кота} many{# котов} other{# кота}}!\n```\n\n注意，上述信息主要是作为一个翻译的信息，\n而不是一个原始消息，除非设置应用程序的\n[[yii\\base\\Application::$sourceLanguage|源语言]] 为 `ru-RU`。\n\n> Note: 除非您将应用程序的 [[yii\\base\\Application::$sourceLanguage|源语言]] \n> 设置为“RU-RU”，并且从以下语言转换而来，上面的示例俄语消息主要用作翻译的消息，而不是原始消息俄语。\n>\n> 当在 `Yii::t()` 调用中指定的原始消息未找到翻译时，\n> [[yii\\base\\Application::$sourceLanguage|源语言]] 复数规则将应用于原始消息。\n\n对于字符串如下所示的情况，有一个 `offset` 参数：\n \n```php\n$likeCount = 2;\necho Yii::t('app', 'You {likeCount,plural,\n    offset: 1\n    =0{did not like this}\n    =1{liked this}\n    one{and one other person liked this}\n    other{and # others liked this}\n}', [\n    'likeCount' => $likeCount\n]);\n\n// You and one other person liked this\n```\n\n#### 序数选择（Ordinal selection） <span id=\"ordinal-selection\"></span>\n\n`selectordinal` 的参数类型旨在为您所翻译的语言环境选择一个基于语序规则的字符串：\n一个基于语序规则的字符串：\n\n```php\n$n = 3;\necho Yii::t('app', 'You are the {n,selectordinal,one{#st} two{#nd} few{#rd} other{#th}} visitor', ['n' => $n]);\n// For English it outputs:\n// You are the 3rd visitor\n\n// Translation\n'You are the {n,selectordinal,one{#st} two{#nd} few{#rd} other{#th}} visitor' => 'Вы {n,selectordinal,other{#-й}} посетитель',\n\n// For Russian translation it outputs:\n// Вы 3-й посетитель\n```\n\n格式与复数使用的格式非常接近。 要了解您应为特定语言环境指定哪些参数，请参阅\n[https://intl.rmcreative.ru/](https://intl.rmcreative.ru/) 上的“复数规则，序数”。\n或者，您可以参考 [unicode.org上的规则参考](https://unicode-org.github.io/cldr-staging/charts/37/supplemental/language_plural_rules.html)。\n\n#### 选择（Selection） <span id=\"selection\"></span>\n\n可以使用 `select` 参数类型来选择基于参数值的短语。例如，\n\n```php\n// 输出：\"Snoopy is a dog and it loves Yii!\"\necho \\Yii::t('app', '{name} is a {gender} and {gender, select, female{she} male{he} other{it}} loves Yii!', [\n    'name' => 'Snoopy',\n    'gender' => 'dog',\n]);\n```\n\n在上面的表达中， `female` 和 `male` 是可能的参数值，\n而 `other` 用于处理不与它们中任何一个相匹配的值。对于每一个可能的参数值，\n应指定一个短语并把它放在在一对大括号中。\n\n\n### 指定默认翻译（Specifying default message source） <span id=\"default-message-source\"></span>\n\n你可以指定使用默认的翻译，该翻译将作为一个类别，用于不匹配任何其他翻译的后备。\n这种翻译应标有 `*` 。\n为了做到这一点以下内容需要添加到应用程序的配置：\n\n```php\n//配置 i18n 组件\n\n'i18n' => [\n    'translations' => [\n        '*' => [\n            'class' => 'yii\\i18n\\PhpMessageSource'\n        ],\n    ],\n],\n```\n\n现在，你可以使用每一个还没有配置的类别，这跟 Yii 1.1 的行为有点类似。该类别的消息将来自在默认翻译 `basePath` 中的一个文件，\n该文件在 `@app/messages` ：\n\n```php\necho Yii::t('not_specified_category', 'message from unspecified category');\n```\n\n该消息将来自 `@app/messages/<LanguageCode>/not_specified_category.php` 。\n\n### 翻译模块消息（Translating module messages） <span id=\"module-translation\"></span>\n\n如果你想翻译一个模块的消息，并避免使用单一翻译文件的所有信息，你可以按照下面的方式来翻译：\n\n```php\n<?php\n\nnamespace app\\modules\\users;\n\nuse Yii;\n\nclass Module extends \\yii\\base\\Module\n{\n    public $controllerNamespace = 'app\\modules\\users\\controllers';\n\n    public function init()\n    {\n        parent::init();\n        $this->registerTranslations();\n    }\n\n    public function registerTranslations()\n    {\n        Yii::$app->i18n->translations['modules/users/*'] = [\n            'class' => 'yii\\i18n\\PhpMessageSource',\n            'sourceLanguage' => 'en-US',\n            'basePath' => '@app/modules/users/messages',\n            'fileMap' => [\n                'modules/users/validation' => 'validation.php',\n                'modules/users/form' => 'form.php',\n                ...\n            ],\n        ];\n    }\n\n    public static function t($category, $message, $params = [], $language = null)\n    {\n        return Yii::t('modules/users/' . $category, $message, $params, $language);\n    }\n\n}\n```\n\n在上面的例子中，我们使用通配符匹配，然后过滤了所需的文件中的每个类别。取之使用 `fileMap` ，你可以简单地使用类映射的同名文件。\n现在你可以直接使用 `Module::t('validation', 'your custom validation message')` \n或 `Module::t('form', 'some form label')`。\n\n### 翻译小部件消息（Translating widgets messages） <span id=\"widget-translation\"></span>\n\n上述模块的翻译规则也同样适用于小部件的翻译规则，例如：\n\n```php\n<?php\n\nnamespace app\\widgets\\menu;\n\nuse yii\\base\\Widget;\nuse Yii;\n\nclass Menu extends Widget\n{\n\n    public function init()\n    {\n        parent::init();\n        $this->registerTranslations();\n    }\n\n    public function registerTranslations()\n    {\n        $i18n = Yii::$app->i18n;\n        $i18n->translations['widgets/menu/*'] = [\n            'class' => 'yii\\i18n\\PhpMessageSource',\n            'sourceLanguage' => 'en-US',\n            'basePath' => '@app/widgets/menu/messages',\n            'fileMap' => [\n                'widgets/menu/messages' => 'messages.php',\n            ],\n        ];\n    }\n\n    public function run()\n    {\n        echo $this->render('index');\n    }\n\n    public static function t($category, $message, $params = [], $language = null)\n    {\n        return Yii::t('widgets/menu/' . $category, $message, $params, $language);\n    }\n\n}\n```\n\n你可以简单地使用类映射的同名文件而不是使用 `fileMap` 。\n现在你直接可以使用 `Menu::t('messages', 'new messages {messages}', ['{messages}' => 10])` 。\n\n> Tip: 对于小部件也可以使用 i18n 视图，并一样以控制器的规则来应用它们。\n\n\n### 翻译框架信息（Translating framework messages） <span id=\"framework-translation\"></span>\n\nYii 自带了一些默认的信息验证错误和其他一些字符串的翻译。\n这些信息都是在 `yii` 类别中。有时候你想纠正应用程序的默认信息翻译。\n为了做到这一点，需配置 `i18n` [应用组件](structure-application-components.md) 如下：\n\n```php\n'i18n' => [\n    'translations' => [\n        'yii' => [\n            'class' => 'yii\\i18n\\PhpMessageSource',\n            'sourceLanguage' => 'en-US',\n            'basePath' => '@app/messages'\n        ],\n    ],\n],\n```\n\n现在可以把你修改过的翻译放在 `@app/messages/<language>/yii.php`。\n\n### 处理缺少的翻译（Handling missing translations） <span id=\"missing-translations\"></span>\n\n如果翻译的消息在消息源文件里找不到，Yii 将直接显示该消息内容。这样一来当你的原始消息是一个有效的冗长的文字时会很方便。\n然而，有时它是不能实现我们的需求。你可能需要执行一些自定义处理的情况，\n这时请求的翻译可能在消息翻译源文件找不到。\n这可通过使用 [[yii\\i18n\\MessageSource::EVENT_MISSING_TRANSLATION|missingTranslation]] - [[yii\\i18n\\MessageSource]] 的事件来完成。\n\n例如，你可能想要将所有缺失的翻译做一个明显的标记，这样它们就可以很容易地在页面中找到。\n为此，你需要先设置一个事件处理程序。这可以在应用程序配置中进行：\n\n```php\n'components' => [\n    // ...\n    'i18n' => [\n        'translations' => [\n            'app*' => [\n                'class' => 'yii\\i18n\\PhpMessageSource',\n                'fileMap' => [\n                    'app' => 'app.php',\n                    'app/error' => 'error.php',\n                ],\n                'on missingTranslation' => ['app\\components\\TranslationEventHandler', 'handleMissingTranslation']\n            ],\n        ],\n    ],\n],\n```\n\n现在，你需要实现自己的事件处理程序：\n\n```php\n<?php\n\nnamespace app\\components;\n\nuse yii\\i18n\\MissingTranslationEvent;\n\nclass TranslationEventHandler\n{\n    public static function handleMissingTranslation(MissingTranslationEvent $event)\n    {\n        $event->translatedMessage = \"@MISSING: {$event->category}.{$event->message} FOR LANGUAGE {$event->language} @\";\n    }\n}\n```\n\n如果 [[yii\\i18n\\MissingTranslationEvent::translatedMessage]] 是由事件处理程序设置，它将显示翻译结果。\n\n> Note: 每个消息源会单独处理它缺少的翻译。如果是使用多个消息源，并希望他们把缺少的翻译以同样的方式来处理，\n> 你应该给它们每一个消息源指定相应的事件处理程序。\n\n\n### 使用 `message` 命令（Using the `message` command） <span id=\"message-command\"></span>\n\n翻译储存在 [[yii\\i18n\\PhpMessageSource|php 文件]]，[[yii\\i18n\\GettextMessageSource|.po 文件] 或者 [[yii\\i18n\\DbMessageSource|数据库]]。具体见类的附加选项。\n\n首先，你需要创建一个配置文件。确定应该保存在哪里，然后执行命令\n\n```bash\n./yii message/config path/to/config.php\n```\n\n打开创建的文件，并按照需求来调整参数。特别注意：\n\n* `languages`: 代表你的应用程序应该被翻译成什么语言的一个数组;\n* `messagePath`: 存储消息文件的路径，这应与配置中 `i18n` 的 `basePath` 参数一致。\n\n您也可以使用 './yii message/config' 命令通过 cli 动态生成带有指定选项的配置文件。\n例如，你可以像下面这样设置 `languages` 和 `messagePath` 参数：\n\n```bash\n./yii message/config --languages=de,ja --messagePath=messages path/to/config.php\n```\n\n要获取可用选项列表，请执行下一个命令：\n\n```bash\n./yii help message/config\n```\n\n一旦你完成了配置文件，你最终可以用命令提取你的消息：\n\n```bash\n./yii message path/to/config.php\n```\n\n另外，您可以使用选项来动态更改提取参数。\n\n然后你会发现你的文件（如果你已经选择基于文件的翻译）在 `messagePath` 目录。\n\n\n## 视图的翻译（View Translation） <span id=\"view-translation\"></span>\n\n有时你可能想要翻译一个完整的视图文件，而不是翻译单条文本消息。为了达到这一目的，\n只需简单的翻译视图并在它子目录下保存一个名称一样的目标语言文件。\n例如，如果你想要翻译的视图文件为 `views/site/index.php` 且目标语言是 `ru-RU`，\n你可以将视图翻译并保存为 `views/site/ru-RU/index.php`。现在\n每当你调用 [[yii\\base\\View::renderFile()]] 或任何其它方法 (如 [[yii\\base\\Controller::render()]]) 来渲染 `views/site/index.php` 视图，\n它最终会使用所翻译的 `views/site/ru-RU/index.php`。\n\n> Note: 如果 [[yii\\base\\Application::$language|目标语言]] 跟 [[yii\\base\\Application::$sourceLanguage|源语言]] 相同，\n在翻译视图的存在下，将呈现原始视图。\n\n\n## 格式化日期和数字值（Formatting Date and Number Values） <span id=\"date-number\"></span>\n\n在 [格式化输出数据](output-formatting.md) 一节可获取详细信息。\n\n\n## 设置 PHP 环境（Setting Up PHP Environment） <span id=\"setup-environment\"></span>\n\nYii 使用 [PHP intl 扩展](https://www.php.net/manual/zh/book.intl.php) 来提供大多数 I18N 的功能，\n如日期和数字格式的 [[yii\\i18n\\Formatter]] 类和消息格式的 [[yii\\i18n\\MessageFormatter]] 类。\n当 `intl` 扩展没有安装时，两者会提供一个回调机制。然而，该回调机制只适用于目标语言是英语的情况下。\n因此，当 I18N 对你来说必不可少时，强烈建议你安装 `intl`。\n\n[PHP intl 扩展](https://www.php.net/manual/zh/book.intl.php) 是基于对于所有不同的语言环境\n提供格式化规则的 [ICU库](https://icu.unicode.org/)。\n不同版本的 ICU 中可能会产生不同日期和数值格式的结果。\n为了确保你的网站在所有环境产生相同的结果，\n建议你安装与 `intl` 扩展相同的版本（和 ICU 同一版本）。\n\n要找出所使用的 PHP 是哪个版本的 ICU ，你可以运行下面的脚本，它会给出你所使用的 PHP 和 ICU 的版本。\n\n```php\n<?php\necho \"PHP: \" . PHP_VERSION . \"\\n\";\necho \"ICU: \" . INTL_ICU_VERSION . \"\\n\";\necho \"ICU Data: \" . INTL_ICU_DATA_VERSION . \"\\n\";\n```\n\n此外，还建议你所使用的 ICU 版本应等于或大于 49 的版本。这确保了可以使用本文档描述的所有功能。例如，\n低于 49 版本的 ICU 不支持使用 `#` 占位符来实现复数规则。\n请参阅 <https://icu.unicode.org/download> 获取可用 ICU 版本的完整列表。\n注意，版本编号在 4.8 之后发生了变化（如 ICU4.8，ICU49，50 ICU 等）。\n\n另外，ICU 库中时区数据库的信息可能过时。要更新时区数据库时详情请参阅\n[ICU 手册](https://unicode-org.github.io/icu/userguide/datetime/timezone/#updating-the-time-zone-data) 。\n而对于 ICU 输出格式使用的时区数据库，PHP 用的时区数据库可能跟它有关。\n你可以通过安装 [pecl package `timezonedb`](https://pecl.php.net/package/timezonedb) 的最新版本来更新它。\n"
  },
  {
    "path": "docs/guide-zh-CN/tutorial-mailing.md",
    "content": "收发邮件\n========\n\n> Note: 本节正在开发中。\n\nYii 支持组成和发送电子邮件。然而，该框架提供的只有内容组成功能和基本接口。\n实际的邮件发送机制可以通过扩展提供，\n因为不同的项目可能需要不同的实现方式，\n它通常取决于外部服务和库。\n\n大多数情况下你可以使用 [yii2-swiftmailer](https://github.com/yiisoft/yii2-swiftmailer) 官方扩展。\n\n\n配置\n-------\n\n邮件组件配置取决于你所使用的扩展。\n一般来说你的应用程序配置应如下：\n\n```php\nreturn [\n    //....\n    'components' => [\n        'mailer' => [\n            'class' => 'yii\\swiftmailer\\Mailer',\n        ],\n    ],\n];\n```\n\n\n基本用法\n---------\n\n一旦 “mailer” 组件被配置，可以使用下面的代码来发送邮件：\n\n```php\nYii::$app->mailer->compose()\n    ->setFrom('from@domain.com')\n    ->setTo('to@domain.com')\n    ->setSubject('Message subject')\n    ->setTextBody('Plain text content')\n    ->setHtmlBody('<b>HTML content</b>')\n    ->send();\n```\n\n在上面的例子中所述的 `compose()` 方法创建了电子邮件消息，这是填充和发送的一个实例。\n如果需要的话在这个过程中你可以用上更复杂的逻辑：\n\n```php\n$message = Yii::$app->mailer->compose();\nif (Yii::$app->user->isGuest) {\n    $message->setFrom('from@domain.com')\n} else {\n    $message->setFrom(Yii::$app->user->identity->email)\n}\n$message->setTo(Yii::$app->params['adminEmail'])\n    ->setSubject('Message subject')\n    ->setTextBody('Plain text content')\n    ->send();\n```\n\n> Note: 每个 “mailer” 的扩展也有两个主要类别：“Mailer” \n  和 “Message”。 “Mailer” 总是知道类名和具体的 “Message”。\n  不要试图直接实例 “Message” 对象 - 而是始终使用 `compose()` 方法。\n\n你也可以一次发送几封邮件：\n\n```php\n$messages = [];\nforeach ($users as $user) {\n    $messages[] = Yii::$app->mailer->compose()\n        // ...\n        ->setTo($user->email);\n}\nYii::$app->mailer->sendMultiple($messages);\n```\n\n一些特定的扩展可能会受益于这种方法，使用单一的网络消息等。\n\n\n撰写邮件内容\n------------\n\nYii 允许通过特殊的视图文件来撰写实际的邮件内容。默认情况下，\n这些文件应该位于 “@app/mail” 路径。\n\n一个邮件视图内容的例子：\n\n```php\n<?php\nuse yii\\helpers\\Html;\nuse yii\\helpers\\Url;\n\n/**\n * @var \\yii\\web\\View $this view component instance\n * @var \\yii\\mail\\BaseMessage $message instance of newly created mail message\n */\n\n?>\n<h2>This message allows you to visit our site home page by one click</h2>\n<?= Html::a('Go to home page', Url::home('http')) ?>\n```\n\n为了通过视图文件撰写正文可传递视图名称到 `compose()` 方法中：\n\n```php\nYii::$app->mailer->compose('home-link') // 渲染一个视图作为邮件内容\n    ->setFrom('from@domain.com')\n    ->setTo('to@domain.com')\n    ->setSubject('Message subject')\n    ->send();\n```\n\n你也可以在 `compose()` 方法中传递一些视图所需参数，这些参数可以在视图文件中使用：\n\n```php\nYii::$app->mailer->compose('greetings', [\n    'user' => Yii::$app->user->identity,\n    'advertisement' => $adContent,\n]);\n```\n\n你可以指定不同的视图文件的 HTML 和纯文本邮件内容：\n\n```php\nYii::$app->mailer->compose([\n    'html' => 'contact-html',\n    'text' => 'contact-text',\n]);\n```\n\n如果指定视图名称为纯字符串，它的渲染结果将被用来作为 HTML Body，\n同时纯文本正文将被删除所有 HTML 实体。\n\n视图渲染结果可以被包裹进布局，可使用 [[yii\\mail\\BaseMailer::htmlLayout]] 和 [[yii\\mail\\BaseMailer::textLayout]] 来设置。\n它的运行方式跟常规应用程序的布局是一样的。\n布局可用于设置邮件 CSS 样式或其他共享内容：\n\n```php\n<?php\nuse yii\\helpers\\Html;\n\n/**\n * @var \\yii\\web\\View $this view component instance\n * @var \\yii\\mail\\MessageInterface $message the message being composed\n * @var string $content main view render result\n */\n?>\n<?php $this->beginPage() ?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n<head>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=<?= Yii::$app->charset ?>\" />\n    <style type=\"text/css\">\n        .heading {...}\n        .list {...}\n        .footer {...}\n    </style>\n    <?php $this->head() ?>\n</head>\n<body>\n    <?php $this->beginBody() ?>\n    <?= $content ?>\n    <div class=\"footer\">With kind regards, <?= Yii::$app->name ?> team</div>\n    <?php $this->endBody() ?>\n</body>\n</html>\n<?php $this->endPage() ?>\n```\n\n\n文件附件\n---------\n\n你可以使用 `attach()` 和 `attachContent()` 方法来添加附件的信息：\n\n```php\n$message = Yii::$app->mailer->compose();\n\n// 附件来自本地文件\n$message->attach('/path/to/source/file.pdf');\n\n// 动态创建一个文件附件\n$message->attachContent('Attachment content', ['fileName' => 'attach.txt', 'contentType' => 'text/plain']);\n```\n\n\n嵌入图片\n---------\n\n你可以使用 `embed()` 方法将图片插入到邮件内容。\n此方法返回会图片 ID ，这将用在“img”标签中。\n当通过视图文件来写信时，这种方法易于使用：\n\n```php\nYii::$app->mailer->compose('embed-email', ['imageFileName' => '/path/to/image.jpg'])\n    // ...\n    ->send();\n```\n\n然后在该视图文件中，你可以使用下面的代码：\n\n```php\n<img src=\"<?= $message->embed($imageFileName); ?>\">\n```\n\n\n测试和调试\n-----------\n\n开发人员常常要检查一下，有什么电子邮件是由应用程序发送的，他们的内容是什么等。\n这可通过 `yii\\mail\\BaseMailer::useFileTransport` 来检查。\n如果开启这个选项，会把邮件信息保存在本地文件而不是发送它们。\n这些文件保存在 `yii\\mail\\BaseMailer::fileTransportPath` 中，默认在 '@runtime/mail' 。\n\n> Tip: 你可以保存这些信息到本地文件或者把它们发送出去，但不能同时两者都做。\n\n邮件信息文件可以在一个普通的文本编辑器中打开，这样你就可以浏览实际的邮件标题，内容等。\n这种机制可以用来调试应用程序或运行单元测试。\n\n> Tip: 该邮件信息文件是会被 `\\yii\\mail\\MessageInterface::toString()` 转成字符串保存的，\n  它依赖于实际在应用程序中使用的邮件扩展。\n\n\n创建自己的邮件解决方案\n------------------------\n\n为了创建你自己的邮件解决方案，你需要创建两个类，一个用于 “Mailer”，另一个用于 “Message”。\n你可以使用 `yii\\mail\\BaseMailer` 和 `yii\\mail\\BaseMessage` 作为基类。\n这些类已经实现了基本的逻辑，这在本指南中有介绍。\n然而，它们的使用不是必须的，\n它实现了 `yii\\mail\\MailerInterface` 和 `yii\\mail\\MessageInterface` 接口。\n然后，你需要实现所有 abstract 方法来构建解决方案。\n"
  },
  {
    "path": "docs/guide-zh-CN/tutorial-performance-tuning.md",
    "content": "性能优化\n========\n\n有许多因素影响你的 Web 应用程序的性能。有些是环境，\n有些是你的代码，而其他一些与 Yii 本身有关。\n在本节中，我们将列举这些因素并解释如何通过调整这些因素来提高应用程序的性能。\n\n\n## 优化你的 PHP 环境 <span id=\"optimizing-php\"></span>\n\n一个好的 PHP 环境是非常重要的。为了得到最大的性能，\n\n- 使用最新稳定版本的 PHP。 PHP 的主要版本可能带来显著的性能提升。\n- 启用字节码缓存 [Opcache](https://www.php.net/manual/zh/book.opcache.php)（PHP 5.5或更高版本）\n  或 [APC](https://www.php.net/manual/zh/book.apcu.php)\n  （PHP 5.4或更早版本）。字节码缓存省去了每次解析和加载 PHP 脚本所带来的开销。\n- [Tune `realpath()` cache](https://github.com/samdark/realpath_cache_tuner).\n\n\n## 禁用调试模式 <span id=\"disable-debug\"></span>\n\n对于运行在生产环境中的应用程序，你应该禁用调试模式。\nYii 中使用名为 `YII_DEBUG` 的常量来定义调试模式是否应被激活。\n若启用了调试模式，Yii 将需要额外的时间来产生和记录调试信息。\n\n你可以将下面的代码行放在 [入口脚本](structure-entry-scripts.md) \n的开头来禁用调试模式：\n\n```php\ndefined('YII_DEBUG') or define('YII_DEBUG', false);\n```\n\n> Tip: `YII_DEBUG` 的默认值是 false 。所以如果你确信你不在你应用程序代码中别的地方更改其默认值，\n  你可以简单地删除上述行来禁用调试模式。\n  \n\n## 使用缓存技术 <span id=\"using-caching\"></span>\n\n你可以使用各种缓存技术来提高应用程序的性能。例如，如果你的应用程序允许用户以 Markdown 格式输入文字，\n你可以考虑缓存解析后的 Markdown 内容，避免每个请求都重复解析相同的 Markdown 文本。\n请参阅 [缓存](caching-overview.md) 一节，\n了解 Yii 提供的缓存支持。\n\n\n## 开启 Schema 缓存 <span id=\"enable-schema-caching\"></span>\n\nSchema 缓存是一个特殊的缓存功能，\n每当你使用[活动记录](db-active-record.md)时应该要开启这个缓存功能。如你所知，\n活动记录能智能检测数据库对象的集合（例如列名、列类型、约束）而不需要手动地描述它们。\n活动记录是通过执行额外的 SQL 查询来获得该信息。\n通过启用 Schema 缓存，检索到的数据库对象的集合将被保存在缓存中并在将来的请求中重用。\n\n要开启 Schema 缓存，需要配置一个 `cache` [应用组件](structure-application-components.md)来储存 Schema 信息，\n并在 [配置](concept-configurations.md) 中设置 [[yii\\db\\Connection::enableSchemaCache]] 为 `true` :\n\n```php\nreturn [\n    // ...\n    'components' => [\n        // ...\n        'cache' => [\n            'class' => 'yii\\caching\\FileCache',\n        ],\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=mydatabase',\n            'username' => 'root',\n            'password' => '',\n            'enableSchemaCache' => true,\n\n            // Duration of schema cache.\n            'schemaCacheDuration' => 3600,\n\n            // Name of the cache component used to store schema information\n            'schemaCache' => 'cache',\n        ],\n    ],\n];\n```\n\n\n## 合并和压缩资源文件 <span id=\"optimizing-assets\"></span>\n\n一个复杂的网页往往包括许多 CSS 和 JavaScript 资源文件。\n为减少 HTTP 请求的数量和这些资源总下载的大小，应考虑将它们合并成一个单一的文件并压缩。\n这可大大提高页面加载时间，且减少了服务器负载。\n想了解更多细节，请参阅[前端资源](structure-assets.md)部分。\n\n\n## 优化会话存储 <span id=\"optimizing-session\"></span>\n\n默认会话数据被存储在文件中。\n这是好的对处于发展项目或小型项目。\n但是，当涉及要处理大量并发请求时，\n最好使用其他的会话存储方式，比如数据库。\nYii 支持各种会话存储。\n你可以通过在[配置](concept-configurations.md)中配置 `session` 组件来使用这些存储，\n如下代码：\n\n```php\nreturn [\n    // ...\n    'components' => [\n        'session' => [\n            'class' => 'yii\\web\\DbSession',\n\n            // Set the following if you want to use DB component other than\n            // default 'db'.\n            // 'db' => 'mydb',\n\n            // To override default session table, set the following\n            // 'sessionTable' => 'my_session',\n        ],\n    ],\n];\n```\n\n以上配置是使用数据库来存储会话数据。默认情况下，\n它会使用 `db` 应用组件连接数据库并将会话数据存储在 `session` 表。\n因此，你必须创建如下的 `session` 表，\n\n```sql\nCREATE TABLE session (\n    id CHAR(40) NOT NULL PRIMARY KEY,\n    expire INTEGER,\n    data BLOB\n)\n```\n\n你也可以通过使用缓存来存储会话数据 [[yii\\web\\CacheSession]] 。\n理论上讲，你可以使用只要支持[数据缓存](caching-data.md#supported-cache-storage)。\n但是请注意，某些缓存的存储当达到存储限制会清除缓存数据。出于这个原因，你应主要在不存在存储限制时才使用这些缓存存储。\n如果你的服务器支持 [Redis](https://redis.io/)，强烈建议你通过使用 [[yii\\redis\\Session]] 来作为会话存储。\n\n如果您的服务器上有 [Redis](https://redis.io/)，\n强烈建议您使用 [[yii\\redis\\Session]] 作为会话存储。\n\n\n## 优化数据库 <span id=\"optimizing-databases\"></span>\n\n执行数据库查询并从数据库中取出数据往往是一个 Web 应用程序主要的性能瓶颈。\n尽管使用[数据缓存](caching-data.md)技术可以缓解性能下降，但它并不完全解决这个问题。\n当数据库包含大量的数据且缓存数据是无效的，\n获取最新的数据可能是最耗性能的假如在没有适当地设计数据库和查询条件。\n\n一般来说，提高数据库查询的性能是创建索引。例如，如果你需要找一个用户表的“用户名”，\n你应该为“用户名”创建一个索引。\n注意，尽管索引可以使选择查询的速度快得多，但它会减慢插入、更新和删除的查询。\n\n对于复杂的数据库查询，建议你创建数据库视图来保存查询分析和准备的时间。\n\n最后，在“SELECT”中使用“LIMIT”查询。\n这可以避免从数据库中取出大量数据。\n\n\n## 使用普通数组 <span id=\"using-arrays\"></span>\n\n尽管[活动记录](db-active-record.md)对象使用起来非常方便，\n但当你需要从数据库中检索大量数据时它的效率不如使用普通的数组。\n在这种情况下，你可以考虑在使用活动记录查询数据时调用 `asArray()`，\n使检索到的数据被表示为数组而不是笨重的活动记录对象。例如，\n\n```php\nclass PostController extends Controller\n{\n    public function actionIndex()\n    {\n        $posts = Post::find()->limit(100)->asArray()->all();\n        \n        return $this->render('index', ['posts' => $posts]);\n    }\n}\n```\n\n在上述代码中，$posts 将被表中的行填充形成数组。每一行是一个普通的数组。要访问\n第 i 行的 `title` 列，你可以使用表达式 `$post[$i]['title']`。\n\n你也可以使用 [DAO](db-dao.md) 以数组的方式来构建查询和检索数据。\n\n\n## 优化 Composer 自动加载 <span id=\"optimizing-autoloader\"></span>\n\n因为 Composer 自动加载用于加载大多数第三方类文件，\n应考虑对其进行优化，通过执行以下命令：\n\n```\ncomposer dumpautoload -o\n```\n\n另外，您可以考虑使用\n[authoritative class maps](https://getcomposer.org/doc/articles/autoloader-optimization.md#optimization-level-2-a-authoritative-class-maps)\n和 [APCu 缓存](https://getcomposer.org/doc/articles/autoloader-optimization.md#optimization-level-2-b-apcu-cache)。\n请注意，这两种选择可能适用于您的特定情况，也可能不适合您。\n\n\n## 处理离线数据 <span id=\"processing-data-offline\"></span>\n\n当一个请求涉及到一些资源密集操作，\n你应该想办法在无需用户等待他们完成脱机模式时来执行这些操作。\n\n有两种方法可以离线数据处理：推和拉。\n\n在拉中，只要有请求涉及到一些复杂的操作，你创建一个任务，并将其保存在永久存储，例如数据库。然后，\n使用一个单独的进程（如 cron 作业）拉任务，并进行处理。\n这种方法很容易实现，但它也有一些缺点。\n例如，该任务过程中需要定期地从任务存储拉。如果拉频率太低，这些任务可以延迟处理；\n但是如果频率过高，将引起的高开销。\n\n在推中，你可以使用消息队列（如 RabbitMQ ，ActiveMQ ， Amazon SQS 等）来管理任务。\n每当一个新的任务放在队列中，它会启动或者通知任务处理过程去触发任务处理。\n\n\n## 性能分析 <span id=\"performance-profiling\"></span>\n\n你应该配置你的代码来找出性能缺陷，并相应地采取适当措施。\n以下分析工具可能是有用的:\n\n- [Yii debug toolbar and debugger](https://github.com/yiisoft/yii2-debug/blob/master/docs/guide/README.md)\n- [Blackfire](https://blackfire.io/)\n- [XHProf](https://www.php.net/manual/zh/book.xhprof.php)\n- [XDebug profiler](https://xdebug.org/docs/profiler)\n\n## 准备扩展应用程序\n\n当没有任何帮助时，您可以尝试使您的应用程序可扩展。[Configuring a Yii2 Application for an Autoscaling Stack](https://github.com/samdark/yii2-cookbook/blob/master/book/scaling.md) 中提供了一个很好的介绍。有关进一步阅读，请参阅 [Web apps performance and scaling](https://thehighload.com/).\n"
  },
  {
    "path": "docs/guide-zh-CN/tutorial-shared-hosting.md",
    "content": "共享托管环境\n==========================\n\n共享的托管环境常常会对目录结构以及配置文件有较多的限制。\n然而，在大多数情况下，你仍可以通过少量的修改以在共享托管环境下运行 Yii 2.0。\n\n部署一个基础应用模板\n---------------------------\n\n由于共享托管环境往往只有一个 webroot，如果可能，请优先使用基础项目模板（ basic project template )构建你的应用程序。\n参考 [安装 Yii 章节](start-installation.md)在本地安装基础项目模板。\n当你让应用程序在本地正常运行后，\n我们将要做少量的修改以让它可以在共享托管服务器运行。\n\n### 重命名 webroot <span id=\"renaming-webroot\"></span>\n\n用 FTP 或者其他的工具连接到你的托管服务器，你可能看到类似如下的目录结构：\n\n```\nconfig\nlogs\nwww\n```\n\n在以上，`www` 是你的 web 服务器的 webroot 目录。不同的托管环境下名称可能各不相同，通常是类似：`www`，`htdocs` 和 `public_html` 之类的名称。\n\n对于我们的基础项目模板而言，其 webroot 名为 `web`。\n在你上传你的应用程序到 web 服务器上去之前，将你的本地 webroot 重命名以匹配服务器。\n即：从 `web` 改为 `www`，`public_html` 或者其他你的托管环境的 webroot 名称。\n\n### FTP 根目录可写\n\n如果你有 FTP 根目录的写权限，\n即，有 `config`，`logs` 和 `www` 的根目录，那么，如本地根目录相同的结构上传 `assets`，`commands` 等目录。\n\n### 增加 web 服务器的额外配置 <span id=\"add-extras-for-webserver\"></span>\n\n如果你的 web 服务器是 Apache，你需要增加一个包含如下内容的 `.htaccess` 文件到你的 `web` 目录\n(或者 `public_html` 根据实际情况而定，是你的 `index.php` 文件所在的目录)。\n\n```\nOptions +FollowSymLinks\nIndexIgnore */*\n\nRewriteEngine on\n\n# if a directory or a file exists, use it directly\nRewriteCond %{REQUEST_FILENAME} !-f\nRewriteCond %{REQUEST_FILENAME} !-d\n\n# otherwise forward it to index.php\nRewriteRule . index.php\n```\n\n对于 nginx 而言，你不需要额外的配置文件。\n\n### 检查环境要求\n\n为了运行 Yii，你的 web 服务器必须匹配它的环境要求。最低的要求必须是 PHP 5.4。\n为了检查环境配置，将 `requirements.php` 从你的根目录拷贝到 webroot 目录，\n并通过浏览器输入 URL `https://example.com/requirements.php` 运行它。最后，检查结束后别忘了删除这个文件哦！\n\n部署一个高级应用程序模板\n---------------------------------\n\n将高级应用程序部署到共享主机比基本应用程序有点棘手但可以实现。\n请按照[高级项目模板文档](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/topic-shared-hosting.md)中的说明进行操作。\n"
  },
  {
    "path": "docs/guide-zh-CN/tutorial-start-from-scratch.md",
    "content": "创建你自己的应用程序结构\n=======================\n\n> 注：本章节正在开发中。\n\n虽然 [basic](https://github.com/yiisoft/yii2-app-basic) 和 [advanced](https://github.com/yiisoft/yii2-app-advanced) \n项目模板能够满足你的大部分需求，但是，\n你仍有可能需要创建你自己的项目模板来开始项目。\n\nYii 的项目模板是一个包含 `composer.json` 文件的仓库，并被注册为一个 Composer package。任何一个仓库都可以被标识为一个 Composer package，\n只要让其可以通过 `create-project` Composer 命令安装。\n\n由于完全从新创建一个你自己的模板工作量有点大，最好的方式是以一个内建模板为基础。\n这里，我们使用基础应用模板。\n\n克隆基础模板\n------------\n\n第一步是从 Git 仓库克隆 Yii 的基础模板：\n\n```bash\ngit clone git@github.com:yiisoft/yii2-app-basic.git\n```\n\n等待仓库下载到你的电脑。因为为调整到你自己的模板所产生的修改不会被 push 回，\n你可以删除下载下来的 `.git` 目录及其内容。\n\n修改文件\n--------\n\n接下来，你需要修改 `composer.json` 以配置你自己的模板。修改 `name`, `description`, `keywords`, `homepage`, `license`, 和 `support` 的值来描述你自己的模板。\n同样，调整 `require`, `require-dev`, `suggest`\n和其他的参数来匹配你模板的环境需求。\n\n> Note: 在 `composer.json` 文件中，使用 `extra` 下的 `writeable` 参数来指定使用模板创建的应用程序后\n> 需要设置文件权限的文件列表。\n\n接下来，真正的修改你的应用程序默认的目录结构和内容。\n最后，更新 README 文件以符合你的模板。\n\n发布一个 Package（Make a Package）\n--------------------------------\n\n模板调整好后，通过其创建一个 Git 仓库并提交你的代码。\n如果你希望将你的应用程序模板开源，[Github](https://github.com) 将是最好的托管服务。\n如果你不喜欢其他的人来跟你一起协作，你可以使用任意的 Git 仓库服务。\n\n接下来，你需要为 Composer 注册你的 package。对于公有的模板，你可以将 package 注册到 [Packagist](https://packagist.org/)。对于私有的模板，\n注册 package 将会麻烦一点。\n参考 [Composer documentation](https://getcomposer.org/doc/05-repositories.md#hosting-your-own) 获取更多的指示。\n\n使用模板\n-------\n\n以上就是为了创建一个新的 Yii 项目模板你需要做的事情。现在，你可以使用你自己的模板创建项目了：\n\n```\ncomposer create-project --prefer-dist --stability=dev mysoft/yii2-app-coolone new-project\n```\n"
  },
  {
    "path": "docs/guide-zh-CN/tutorial-template-engines.md",
    "content": "使用模板引擎（Using template engines）\n====================================\n\n默认情况下，Yii 使用 PHP 作为其默认的模板引擎语言，但是，你可以配置 Yii 以扩展的方式支持其他的渲染引擎，\n比如 [Twig](https://twig.symfony.com/) 或 [Smarty](https://www.smarty.net/)等。\n\n组件 `view` 就是用于渲染视图的。\n你可以重新配置这个组件的行为以增加一个自定义的模板引擎。\n\n```php\n[\n    'components' => [\n        'view' => [\n            'class' => 'yii\\web\\View',\n            'renderers' => [\n                'tpl' => [\n                    'class' => 'yii\\smarty\\ViewRenderer',\n                    //'cachePath' => '@runtime/Smarty/cache',\n                ],\n                'twig' => [\n                    'class' => 'yii\\twig\\ViewRenderer',\n                    'cachePath' => '@runtime/Twig/cache',\n                    // Array of twig options:\n                    'options' => [\n                        'auto_reload' => true,\n                    ],\n                    'globals' => ['html' => '\\yii\\helpers\\Html'],\n                    'uses' => ['yii\\bootstrap'],\n                ],\n                // ...\n            ],\n        ],\n    ],\n]\n```\n\n在上述的代码中， Smarty 和 Twig 都被配置以让视图文件使用。但是，为了让扩展安装到项目中，\n你同样需要修改你的 `composer.json` 文件，如下：\n\n```\n\"yiisoft/yii2-smarty\": \"*\",\n\"yiisoft/yii2-twig\": \"*\",\n```\n上述代码需要增加到 `composer.json` 的 `require` 节中。在做了上述修改，并保存后，你可以运行 `composer update --prefer-dist` 命令来安装扩展。\n\n对于特定模板引擎的使用详细，请参考其文档：\n\n- [Twig guide](https://github.com/yiisoft/yii2-twig/tree/master/docs/guide)\n- [Smarty guide](https://github.com/yiisoft/yii2-smarty/tree/master/docs/guide)\n"
  },
  {
    "path": "docs/guide-zh-CN/tutorial-yii-as-micro-framework.md",
    "content": "# 使用 Yii 作为微框架\n\nYii 可以轻松使用，而不需要基本和高级模板中包含的功能。换句话说，Yii 已经是一个微框架。不需要由模板提供的目录结构与 Yii 一起工作。\n\n当你不需要像 assets 或视图一样的所有预定义模板代码时，这一点特别方便。 其中一种情况是构建 JSON API。 在下面的部分将展示如何做到这一点。\n\n## 安装 Yii\n\n为您的项目创建一个目录并将工作目录更改为该路径。示例中使用的命令是基于 Unix 的，但在 Windows 中也存在类似的命令。\n\n```bash\nmkdir micro-app\ncd micro-app\n```\n\n> Note: 需要一些 Composer 的知识才能继续。如果您还不知道如何使用 composer，请花些时间阅读 [Composer 指南](https://getcomposer.org/doc/00-intro.md)。\n\n使用您最喜爱的编辑器在 `micro-app` 目录下创建 `composer.json` 文件并添加以下内容：\n\n```json\n{\n    \"require\": {\n        \"yiisoft/yii2\": \"~2.0.0\"\n    },\n    \"repositories\": [\n        {\n            \"type\": \"composer\",\n            \"url\": \"https://asset-packagist.org\"\n        }\n    ]\n}\n```\n\n保存文件并运行 `composer install` 命令。这将安装框架及其所有依赖项。\n\n## 创建项目结构\n\n安装框架之后，需要为此应用程序创建一个 [入口点](structure-entry-scripts.md)。入口点是您尝试打开应用程序时将执行的第一个文件。 出于安全原因，建议将入口点文件放在一个单独的目录中，并将其设置为Web根目录。\n\n创建一个 `web` 目录并将 `index.php` 放入其中，内容如下：\n\n```php \n<?php\n\n// comment out the following two lines when deployed to production\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n\nrequire(__DIR__ . '/../vendor/autoload.php');\nrequire(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');\n\n$config = require __DIR__ . '/../config.php';\n(new yii\\web\\Application($config))->run();\n```\n\n还要创建一个名为 `config.php `的文件，它将包含所有的应用程序配置：\n\n```php\n<?php\nreturn [\n    'id' => 'micro-app',\n    // the basePath of the application will be the `micro-app` directory\n    'basePath' => __DIR__,\n    // this is where the application will find all controllers\n    'controllerNamespace' => 'micro\\controllers',\n    // set an alias to enable autoloading of classes from the 'micro' namespace\n    'aliases' => [\n        '@micro' => __DIR__,\n    ],\n];\n```\n\n> Info: 尽管配置可以保存在 `index.php` 文件中，建议单独使用它。\n> 这样它也可以用于控制台应用程序，如下所示。\n\n您的项目现在已经准备进行编码了。尽管由您决定项目目录结构，只要您遵守命名空间即可。\n\n## 创建第一个控制器\n\n创建一个 `controllers` 目录并添加一个文件 `SiteController.php`，这是默认的\n控制器将处理没有路径信息的请求。\n\n```php\n<?php\n\nnamespace micro\\controllers;\n\nuse yii\\web\\Controller;\n\nclass SiteController extends Controller\n{\n    public function actionIndex()\n    {\n        return 'Hello World!';\n    }\n}\n```\n\n如果您想为此控制器使用不同的名称，则可以配置 [[yii\\base\\Application::$defaultRoute]] 进行更改。\n例如，对于 `DefaultController` 将会是 `'defaultRoute' => 'default/index'`。\n\n在这一点上，项目结构应该如下所示：\n\n```\nmicro-app/\n├── composer.json\n├── config.php\n├── web/\n    └── index.php\n└── controllers/\n    └── SiteController.php\n```\n\n如果您尚未设置 Web 服务器，则可能需要查看[Web服务器配置文件示例](start-installation.md#configuring-web-servers)。\n另一种选择是使用 `yii serve` 命令，它将使用 PHP 内置 web 服务器。\n您可以通过以下方式从 `micro-app /` 目录运行它：\n\n    vendor/bin/yii serve --docroot=./web\n\n在浏览器中打开应用程序URL现在应该打印出“Hello World！”，它已经在 `SiteController::actionIndex()` 中返回。\n\n> Info: 在我们的示例中，我们已将默认应用程序名称空间 `app` 更改为 `micro`，\n> 以表明您不受此名称的限制（如果您是这样认为），\n> 然后调整 [[yii\\base\\Application::$controllerNamespace|controllers namespace]] 并设置正确的别名。\n\n\n## 创建一个 REST API\n\n为了演示我们的“微框架”的用法，我们将为帖子创建一个简单的 REST API。\n\n为了这个 API 来操作一些数据，我们首先需要一个数据库。 添加数据库连接配置\n到应用程序配置：\n\n```php\n'components' => [\n    'db' => [\n        'class' => 'yii\\db\\Connection',\n        'dsn' => 'sqlite:@micro/database.sqlite',\n    ],\n],\n```\n\n> Info: 为了简单，我们在这里使用了一个 sqlite 数据库 请参阅[数据库指南](db-dao.md) 以获取更多选项。\n\n接下来我们创建一个[数据库迁移](db-migrations.md)来创建一个帖子表。\n确保你有一个单独的配置文件，如上所述，我们需要它来运行下面的控制台命令。\n运行以下命令将创建数据库迁移文件\n并将迁移应用到数据库：\n\n    vendor/bin/yii migrate/create --appconfig=config.php create_post_table --fields=\"title:string,body:text\"\n    vendor/bin/yii migrate/up --appconfig=config.php\n\n在该目录中创建目录 `models` 和 `Post.php` 文件。以下是模型的代码：\n\n```php\n<?php\n\nnamespace micro\\models;\n\nuse yii\\db\\ActiveRecord;\n\nclass Post extends ActiveRecord\n{ \n    public static function tableName()\n    {\n        return '{{post}}';\n    }\n}\n```\n\n> Info: 这里创建的模型是一个 ActiveRecord 类，它代表 `post` 表中的数据。\n> 有关更多信息，请参阅[活动记录指南](db-active-record.md)。\n\n我们要通过 API 发布帖子，请在 `controllers` 中添加 `PostController`：\n\n```php\n<?php\n\nnamespace micro\\controllers;\n\nuse yii\\rest\\ActiveController;\n\nclass PostController extends ActiveController\n{\n    public $modelClass = 'micro\\models\\Post';\n\n    public function behaviors()\n    {\n        // remove rateLimiter which requires an authenticated user to work\n        $behaviors = parent::behaviors();\n        unset($behaviors['rateLimiter']);\n        return $behaviors;\n    }\n}\n```\n\n此时我们的 API 将提供以下 URL ：\n\n- `/index.php?r=post` - 列出所有帖子\n- `/index.php?r=post/view&id=1` - 显示 ID 为 1 的帖子\n- `/index.php?r=post/create` - 创建一个帖子\n- `/index.php?r=post/update&id=1` - 更新 ID 为 1 的帖子\n- `/index.php?r=post/delete&id=1` - 删除 ID 为 1 的帖子\n\n从这里开始，您可能需要查看以下指南以进一步开发您的应用程序：\n\n- 该 API 目前仅将 urlencoded 表单数据理解为输入，若使其成为真正的 JSON API，\n  您需要配置 [[yii\\web\\JsonParser]]。\n- 为了使 URL 更加友好，您需要配置路由。\n  请参阅 [关于REST路由的指南](rest-routing.md) 了解如何执行此操作。\n- 请参阅 [预览](start-looking-ahead.md) 部分获取更多参考。\n"
  },
  {
    "path": "docs/guide-zh-CN/tutorial-yii-integration.md",
    "content": "引入第三方代码（Working with Third-Party Code）\n=============================================\n\n有时，你可能会需要在 Yii 应用中使用第三方的代码。又或者是你想要在第三方系统中把 Yii 作为类库引用。\n在下面这个板块中，我们向你展示如何实现这些目标。\n\n\n在 Yii 中使用第三方类库（Using Third-Party Libraries in Yii） <span id=\"using-libs-in-yii\"></span>\n--------------------------------------------------------\n\n要想在 Yii 应用中使用第三方类库，\n你主要需要确保这些库中的类文件都可以被正常导入或可以被自动加载。\n\n### 使用 Composer 包（Using Composer Packages） <span id=\"using-composer-packages\"></span>\n\n目前很多第三方的类库都以 [Composer](https://getcomposer.org/) 包的形式发布。\n你只需要以下两个简单的步骤即可安装他们：\n\n1. 修改你应用的 `composer.json` 文件，并注明需要安装哪些 Composer 包。\n2. 运行 `php composer.phar install` 安装这些包。\n\n这些Composer 包内的类库，可以通过 Composer 的自动加载器实现自动加载。\n不过请确保你应用的[入口脚本](structure-entry-scripts.md)\n包含以下几行用于加载 Composer 自动加载器的代码：\n\n```php\n// install Composer autoloader （安装 Composer 自动加载器）\nrequire __DIR__ . '/../vendor/autoload.php';\n\n// include Yii class file （加载 Yii 的类文件）\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n```\n\n### 使用下载的类库（Using Downloaded Libraries） <span id=\"using-downloaded-libs\"></span>\n\n若你的类库并未发布为一个 Composer 包，你可以参考以下安装说明来安装它。\n在大多数情况下，你需要预先下载一个发布文件，并把它解压缩到`BasePath/vendor` 目录，\n这里的 `BasePath` 代指你应用程序自身的 [base path（主目录）](structure-applications.md#basePath)。\n\n若该类库包含他自己的类自动加载器，你可以把它安装到你应用的[入口脚本](structure-entry-scripts.md)里。\n我们推荐你把它的安装代码置于`Yii.php` 的导入之前，\n这样 Yii 的官方自动加载器可以拥有更高的优先级。\n\n若一个类库并没有提供自动加载器，但是他的类库命名方式符合 [PSR-4](https://www.php-fig.org/psr/psr-4/) 标准，\n你可以使用 Yii 官方的自动加载器来自动加载这些类。\n你只需给他们的每个根命名空间声明一下[根路径别名](concept-aliases.md#defining-aliases)。\n比如，假设说你已经在目录 `vendor/foo/bar` 里安装了一个类库，且这些类库的根命名空间为 `xyz`。\n你可以把以下代码放入你的应用配置文件中：\n\n```php\n[\n    'aliases' => [\n        '@xyz' => '@vendor/foo/bar',\n    ],\n]\n```\n\n若以上情形都不符合，最可能是这些类库需要依赖于 PHP 的 include_path 配置，来正确定位并导入类文件。\n只需参考它的安装说明简单地配置一下 PHP 导入路径即可。\n\n最悲催的情形是，该类库需要显式导入每个类文件，\n你可以使用以下方法按需导入相关类文件：\n\n* 找出该库内包含哪些类。\n* 在应用的[入口脚本](structure-entry-scripts.md)里的 `Yii::$classMap` 数组中列出这些类，和他们各自对应的文件路径。\n  举例来说，\n```php\nYii::$classMap['Class1'] = 'path/to/Class1.php';\nYii::$classMap['Class2'] = 'path/to/Class2.php';\n```\n\n\n在第三方系统内使用 Yii（Using Yii in Third-Party Systems） <span id=\"using-yii-in-others\"></span>\n-----------------------------------------------------------\n\n因为 Yii 提供了很多牛逼的功能，有时，你可能会想要使用它们中的一些功能用来支持开发或完善某些第三方的系统，\n比如：WordPress，Joomla，或是用其他 PHP 框架开发的应用程序。\n举两个例子吧，你可能会想念方便的 [[yii\\helpers\\ArrayHelper]] 类，或在第三方系统中使用\n[Active Record](db-active-record.md) 活动记录功能。\n要实现这些目标，你只需两个步骤：安装 Yii，启动 Yii。\n\n若这个第三方系统支持 Composer 管理他的依赖文件，\n你可以直接运行一下命令来安装 Yii：\n\n```bash\ncomposer require yiisoft/yii2\n```\n\n如果您只想使用数据库抽象层或 Yii 的其他非静态资源的相关功能，\n您应该需要一个特殊的 composer 包来阻止 Bower 和 NPM 包的安装。\n有关详细信息，请参见 [cebe/assetfree-yii2](https://github.com/cebe/assetfree-yii2)。\n\n有关 Composer 的更多信息以及安装过程中可能出现的问题的解决方案，另请参见\n[关于安装 Yii 的部分](start-installation.md#installing-via-composer)。\n\n不然的话，你可以[下载](https://www.yiiframework.com/download/) Yii 的发布包，\n并把它解压到对应系统的 `BasePath/vendor` 目录内。\n\n之后，你需要修改该第三方应用的入口脚本，在开头位置添加 Yii 的引入代码：\n\n```php\nrequire __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';\n\n$yiiConfig = require __DIR__ . '/../config/yii/web.php';\nnew yii\\web\\Application($yiiConfig); // 不要在这里调用 run() 方法。\n```\n\n如你所见，这段代码与典型的 Yii 应用的[入口脚本](structure-entry-scripts.md)非常相似。\n唯一的不同之处在于在 Yii 应用创建成功之后，并不会紧接着调用 `run()` 方法。\n因为，`run()` 方法的调用会接管 HTTP 请求的处理流程。\n（译注：换言之，这就不是第三方系统而是 Yii 系统了，URL 规则也会跟着换成 Yii 的规则了）\n\n与 Yii 应用中一样，你可以依据运行该第三方系统的环境，针对性地配置 Yii 应用实例。\n比如，为了使用[活动记录](db-active-record.md)功能，\n你需要先用该第三方系统的 DB 连接信息，配置 Yii 的 `db` 应用组件。\n\n现在，你就可以使用 Yii 提供的绝大多数功能了。\n比如，创建 AR 类，并用它们来操作数据库。\n\n\n配合使用 Yii 2 和 Yii 1（Using Yii 2 with Yii 1） <span id=\"using-both-yii2-yii1\"></span>\n----------------------------------------------\n\n如果你之前使用 Yii 1，大概你也有正在运行的 Yii 1 应用吧。\n不必用 Yii 2 重写整个应用，你也可以通过增添对哪些\nYii 2 独占功能的支持来增强这个系统。下面我们就来详细描述一下具体的实现过程。\n\n> Note: Yii 2 需要 PHP 5.4+ 的版本。你需要确保你的服务器以及现有应用都\n> 可以支持 PHP 5.4。\n\n首先，参考前文板块中给出的方法，在已有的应用中安装 Yii 2。\n\n之后，如下修改 Yii 1 应用的入口脚步：\n\n```php\n// 导入下面会详细说明的定制 Yii 类文件。\nrequire __DIR__ . '/../components/Yii.php';\n\n// Yii 2 应用的配置文件\n$yii2Config = require __DIR__ . '/../config/yii2/web.php';\nnew yii\\web\\Application($yii2Config); // Do NOT call run()\n\n// Yii 1 应用的配置文件\n$yii1Config = require __DIR__ . '/../config/yii1/main.php';\nYii::createWebApplication($yii1Config)->run();\n```\n\n因为，Yii 1 和 Yii 2 都包含有 `Yii` 这个类，你应该创建一个定制版的 Yii 来把他们组合起来。\n上面的代码里包含了的这个定制版的 `Yii` 类，可以用以下代码创建出来：\n\n```php\n$yii2path = '/path/to/yii2';\nrequire $yii2path . '/BaseYii.php'; // Yii 2.x\n\n$yii1path = '/path/to/yii1';\nrequire $yii1path . '/YiiBase.php'; // Yii 1.x\n\nclass Yii extends \\yii\\BaseYii\n{\n    // copy-paste the code from YiiBase (1.x) here\n}\n\nYii::$classMap = include($yii2path . '/classes.php');\n// 通过 Yii 1 注册 Yii 2 的类自动加载器\nYii::registerAutoloader(['Yii', 'autoload']);\n// create the dependency injection container\nYii::$container = new yii\\di\\Container;\n```\n\n大功告成！此时，你可以在你代码的任意位置，调用 `Yii::$app` 以访问 Yii 2 的应用实例，而用\n`Yii::app()` 则会返回 Yii 1 的应用实例：\n\n```php\necho get_class(Yii::app()); // 输出 'CWebApplication'\necho get_class(Yii::$app);  // 输出 'yii\\web\\Application'\n```\n"
  },
  {
    "path": "docs/internals/README.md",
    "content": "Yii Developer Documentation\n===========================\n\nThis directory contains documentation about Yii Framework development and release process.\n\nContributor Guidelines\n----------------------\n\n- [How to Report an Issue](report-an-issue.md)\n- [Getting started](getting-started.md)\n- [Git workflow for Yii 2 contributors](git-workflow.md) - a step by step guide on how to set up your dev environment and start contributing to Yii.\n- [Yii 2 Core Framework Code Style](core-code-style.md)\n- [Yii 2 View Code Style](view-code-style.md)\n\n\nDocumentation\n-------------\n\n- [Translation Status](translation-status.md) - which documents are ready for translation.\n- [Translation teams](translation-teams.md)\n- [Translation workflow](translation-workflow.md)\n\n\nFramework Development\n---------------------\n\n- [Pull request quality assurance](pull-request-qa.md)\n- [Automated Tasks](automation.md), like code style fixes, automatic documentation and file generation.\n- [Design Decisions](design-decisions.md) - a FAQ-like list of statements about commonly debated things.\n\nVersioning and Release\n----------------------\n\n- [Project Organization](project-organization.md)\n- [Yii Versioning](versions.md)\n- [Backwards Compatibility](bc.md)\n- [Releasing a new version](release.md)\n\nMisc\n----\n\n### Exception Hierarchy\n\n![Yii Framework Exception Hierarchy](exception_hierarchy.png)\n\n### Database testing\n\n[Here](https://gist.github.com/sergeymakinen/0696a5952f160ea28d7b64c3adfecf6f) is config for test environments for all supported Yii databases.\n"
  },
  {
    "path": "docs/internals/automation.md",
    "content": "Automation\n==========\n\nThere are some tasks that are done automatically when working on Yii:\n\n- Generation of the classmap `classes.php` located under the framework root directory.\n  Run `./build/build classmap` to generate it.\n\n- Generation of the `@property` annotations in class files that describe properties introduced by getters and setters.\n  Run `./build/build php-doc/property` to update them.\n\n- Fixing of code style and other minor issues in phpdoc comments.\n  Run `./build/build php-doc/fix` to fix them.\n  Check the changes before you commit them as there may be unwanted changes because the command is not perfect.\n  You may use `git add -p` to review the changes.\n\n- Updating the Mime Type Magic file (`framework/helpers/mimeTypes.php`) from the Apache HTTPd repository.\n  Run `./build/build mime-type` to update the file.\n\n- The ordering of entries in the CHANGELOG file can be updated by running `./build/build release/sort-changelog framework`.\n\nAll of the above commands are included in the [release process](). They may be run between releases but it is not necessary.\n"
  },
  {
    "path": "docs/internals/bc.md",
    "content": "# Backwards Compatibility\n\nWe're strictly not breaking backwards compatibility in patch releases such as `2.x.y.Z` and trying to avoid had to fix\nbackwards incompatible changes in minor releases such as `2.x.Y`.\n\nCheck [Yii Versioning](versions.md) to learn about version numbering. \n\n## Usage\n\n### Interfaces\n\nUse case | BC?\n---------|----\nType hint with the interface | Yes\nCall the interface method | Yes\n**Implement the interface and...** |\nImplement method | Yes\nAdd argument to method implemented | Yes\nAdd default value to an argument | Yes\n\n### Classes\n\nUse case | BC?\n---------|----\nType hint with the class | Yes\nCreate a new instance | Yes\nExtend the class | Yes\nAccess a public property | Yes\nCall a public method | Yes\n**Extend the class and...** |\nAccess a protected property\t| Yes\nCall a protected method\t| Yes\nOverride a public property | Yes\nOverride a protected property | Yes\nOverride a public method | Yes\nOverride a protected method | Yes\nAdd a new property | No\nAdd a new method | No\nAdd an argument to an overridden method\t| Yes\nAdd a default value to an argument | Yes\nCall a private method (via Reflection) | No\nAccess a private property (via Reflection) | No\n\n\n## Development\n\n### Changing interfaces\n\nType of change | BC?\n---------------|----\nRemove | No\nChange name or namespace | No\nAdd parent interface | Yes if no new methods are added\nRemove parent interface | No\n**Interface methods** | \nAdd method | No\nRemove method | No\nChange name | No\nMove to parent interface | Yes\nAdd argument without a default value | No\nAdd argument with a default value | No\nRemove argument | Yes (only last ones)\nAdd default value to an argument | No\nRemove default value of an argument | No\nAdd type hint to an argument | No\nRemove type hint of an argument | No\nChange argument type | No\nChange return type | No\n**Constants** |\t \nAdd constant | Yes\nRemove constant | No\nChange value of a constant | Yes except objects that are likely to be serialized. Mandatory to document in UPGRADE.md.\n\n### Classes\n\nType of change | BC?\n---------------|----\nRemove | No\nMake final | No\nMake abstract | No\nChange name or namespace | No\nChange parent class | Yes but original parent class must remain an ancestor of the class.\nAdd interface | Yes\nRemove interface | No\n**Public Properties** | \nAdd public property | Yes\nRemove public property | No\nReduce visibility | No\nMove to parent class | Yes\n**Protected Properties** | \t \nAdd protected property | Yes\nRemove protected property | No\nReduce visibility | No\nMove to parent class | Yes\n**Private Properties** | \nAdd private property | Yes\nRemove private property | Yes\n**Constructors** | \nRemove constructor | No\nReduce visibility of a public constructor | No\nReduce visibility of a protected constructor | No\nMove to parent class | Yes\n**Public Methods** |\nAdd public method | Yes\nRemove public method | No\nChange name | No\nReduce visibility | No\nMove to parent class | Yes\nAdd argument without a default value | No\nAdd argument with a default value | No\nRemove arguments | Yes, only last ones\nAdd default value to an argument | No\nRemove default value of an argument | No\nAdd type hint to an argument | No\nRemove type hint of an argument | No\nChange argument type | No\nChange return type | No\n**Protected Methods** | \t \nAdd protected method | Yes\nRemove protected method | No\nChange name | No\nReduce visibility | No\nMove to parent class | Yes\nAdd argument without a default value | No\nAdd argument with a default value | No\nRemove arguments | Yes, only last ones\nAdd default value to an argument | No\nRemove default value of an argument | No\nAdd type hint to an argument | No\nRemove type hint of an argument | No\nChange argument type | No\nChange return type | No\n**Private Methods** | \t \nAdd private method | Yes\nRemove private method | Yes\nChange name | Yes\nAdd argument without a default value | Yes\nAdd argument with a default value | Yes\nRemove argument | Yes\nAdd default value to an argument | Yes\nRemove default value of an argument | Yes\nAdd type hint to an argument | Yes\nRemove type hint of an argument | Yes\nChange argument type | Yes\nChange return type | Yes\n**Static Methods** | \nTurn non static into static | No\nTurn static into non static | No\n**Constants** | \t \nAdd constant | Yes\nRemove constant | No\nChange value of a constant | Yes except objects that are likely to be serialized. Mandatory to document in UPGRADE.md."
  },
  {
    "path": "docs/internals/core-code-style.md",
    "content": "Yii 2 Core Framework Code Style\n===============================\n\nThe following code style is used for Yii 2.x core and official extensions development. If you want to pull-request code\ninto the core, consider using it. We aren't forcing you to use this code style for your application. Feel free to choose\nwhat suits you better.\n\nYou can get a config for CodeSniffer here: https://github.com/yiisoft/yii2-coding-standards\n\n## 1. Overview\n\nOverall we're using [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)\ncompatible style so everything that applies to\n[PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) is applied to our code\nstyle as well.\n\n- Files MUST use either `<?php` or `<?=` tags.\n- There should be a newline at the end of file.\n- Files MUST use only UTF-8 without BOM for PHP code.\n- Code MUST use 4 spaces for indenting, not tabs.\n- Class names MUST be declared in `StudlyCaps`.\n- Class constants MUST be declared in all upper case with underscore separators.\n- Method names MUST be declared in `camelCase`.\n- Property names MUST be declared in `camelCase`.\n- Property names MUST start with an initial underscore if they are private.\n- Always use `elseif` instead of `else if`.\n\n## 2. Files\n\n### 2.1. PHP Tags\n\n- PHP code MUST use `<?php ?>` or `<?=` tags; it MUST NOT use the other tag variations such as `<?`.\n- In case file contains PHP only it should not have trailing `?>`.\n- Do not add trailing spaces to the end of the lines.\n- Any file that contains PHP code should end with the extension `.php`.\n\n### 2.2. Character Encoding\n\nPHP code MUST use only UTF-8 without BOM.\n\n## 3. Class Names\n\nClass names MUST be declared in `StudlyCaps`. For example, `Controller`, `Model`.\n\n## 4. Classes\n\nThe term \"class\" refers to all classes and interfaces here.\n\n- Classes should be named using `StudlyCase`.\n- The brace should always be written on the line underneath the class name.\n- Every class must have a documentation block that conforms to the PHPDoc.\n- All code in a class must be indented with 4 spaces.\n- There should be only one class in a single PHP file.\n- All classes should be namespaced.\n- Class name should match file name. Class namespace should match directory structure.\n\n```php\n/**\n * Documentation\n */\nclass MyClass extends \\yii\\base\\BaseObject implements MyInterface\n{\n    // code\n}\n```\n\n### 4.1. Constants\n\nClass constants MUST be declared in all upper case with underscore separators.\nFor example:\n\n```php\n<?php\nclass Foo\n{\n    const VERSION = '1.0';\n    const DATE_APPROVED = '2012-06-01';\n}\n```\n### 4.2. Properties\n\n- When declaring public class members specify `public` keyword explicitly.\n- Public and protected variables should be declared at the top of the class before any method declarations.\n  Private variables should also be declared at the top of the class but may be added right before the methods\n  that are dealing with them in cases where they are only related to a small subset of the class methods.\n- The order of property declaration in a class should be ascending based on their visibility: from public over protected to private.\n- There are no strict rules for ordering properties that have the same visibility.\n- For better readability there should be no blank lines between property declarations and two blank lines\n  between property and method declaration sections. One blank line should be added between the different visibility groups.\n- Private variables should be named like `$_varName`.\n- Public class members and standalone variables should be named using `$camelCase`\n  with first letter lowercase.\n- Use descriptive names. Variables such as `$i` and `$j` are better not to be used.\n\nFor example:\n\n```php\n<?php\nclass Foo\n{\n    public $publicProp1;\n    public $publicProp2;\n\n    protected $protectedProp;\n\n    private $_privateProp;\n\n\n    public function someMethod()\n    {\n        // ...\n    }\n}\n```\n\n### 4.3. Methods\n\n- Functions and methods should be named using `camelCase` with first letter lowercase.\n- Name should be descriptive by itself indicating the purpose of the function.\n- Class methods should always declare visibility using `private`, `protected` and\n  `public` modifiers. `var` is not allowed.\n- Opening brace of a function should be on the line after the function declaration.\n\n```php\n/**\n * Documentation\n */\nclass Foo\n{\n    /**\n     * Documentation\n     */\n    public function bar()\n    {\n        // code\n        return $value;\n    }\n}\n```\n\n### 4.4 PHPDoc blocks\n\n - `@param`, `@var`, `@property` and `@return` must declare types as `bool`, `int`, `string`, `array` or `null`.\n   You can use a class names as well such as `Model` or `ActiveRecord`.\n - For a typed arrays use `ClassName[]`.\n - The first line of the PHPDoc must describe the purpose of the method.\n - If method checks something (`isActive`, `hasClass`, etc) the first line should start with `Checks whether`.\n - `@return` should explicitly describe what exactly will be returned.\n\n```php\n/**\n * Checks whether the IP is in subnet range\n *\n * @param string $ip an IPv4 or IPv6 address\n * @param int $cidr the CIDR length\n * @param string $range subnet in CIDR format e.g. `10.0.0.0/8` or `2001:af::/64`\n * @return bool whether the IP is in subnet range\n */\n private function inRange($ip, $cidr, $range)\n {\n   // ...\n }\n```\n\n### 4.5 Constructors\n\n- `__construct` should be used instead of PHP 4 style constructors.\n\n## 5 PHP\n\n### 5.1 Types\n\n- All PHP types and values should be used lowercase. That includes `true`, `false`, `null` and `array`.\n\nChanging type of an existing variable is considered as a bad practice. Try not to write such code unless it is really necessary.\n\n\n```php\npublic function save(Transaction $transaction, $argument2 = 100)\n{\n    $transaction = new Connection; // bad\n    $argument2 = 200; // good\n}\n```\n\n### 5.2 Strings\n\n- If string doesn't contain variables or single quotes, use single quotes.\n\n```php\n$str = 'Like this.';\n```\n\n- If string contains single quotes you can use double quotes to avoid extra escaping.\n\n#### Variable substitution\n\n```php\n$str1 = \"Hello $username!\";\n$str2 = \"Hello {$username}!\";\n```\n\nThe following is not permitted:\n\n```php\n$str3 = \"Hello ${username}!\";\n```\n\n#### Concatenation\n\nAdd spaces around dot when concatenating strings:\n\n```php\n$name = 'Yii' . ' Framework';\n```\n\nWhen string is long format is the following:\n\n```php\n$sql = \"SELECT *\"\n    . \"FROM `post` \"\n    . \"WHERE `id` = 121 \";\n```\n\n### 5.3 arrays\n\nFor arrays we're using PHP 5.4 short array syntax.\n\n#### Numerically indexed\n\n- Do not use negative numbers as array indexes.\n\nUse the following formatting when declaring array:\n\n```php\n$arr = [3, 14, 15, 'Yii', 'Framework'];\n```\n\nIf there are too many elements for a single line:\n\n```php\n$arr = [\n    3, 14, 15,\n    92, 6, $test,\n    'Yii', 'Framework',\n];\n```\n\n#### Associative\n\nUse the following format for associative arrays:\n\n```php\n$config = [\n    'name' => 'Yii',\n    'options' => ['usePHP' => true],\n];\n```\n\n### 5.4 control statements\n\n- Control statement condition must have single space before and after parenthesis.\n- Operators inside of parenthesis should be separated by spaces.\n- Opening brace is on the same line.\n- Closing brace is on a new line.\n- Always use braces for single line statements.\n\n```php\nif ($event === null) {\n    return new Event();\n}\nif ($event instanceof CoolEvent) {\n    return $event->instance();\n}\nreturn null;\n\n\n// the following is NOT allowed:\nif (!$model && null === $event)\n    throw new Exception('test');\n```\n\nPrefer avoiding `else` after `return` where it makes sense.\nUse [guard conditions](https://refactoring.com/catalog/replaceNestedConditionalWithGuardClauses.html).\n\n```php\n$result = $this->getResult();\nif (empty($result)) {\n    return true;\n} else {\n    // process result\n}\n```\n\nis better as\n\n```php\n$result = $this->getResult();\nif (empty($result)) {\n   return true;\n}\n\n// process result\n```\n\n#### switch\n\nUse the following formatting for switch:\n\n```php\nswitch ($this->phpType) {\n    case 'string':\n        $a = (string) $value;\n        break;\n    case 'integer':\n    case 'int':\n        $a = (int) $value;\n        break;\n    case 'boolean':\n        $a = (bool) $value;\n        break;\n    default:\n        $a = null;\n}\n```\n\n### 5.5 function calls\n\n```php\ndoIt(2, 3);\n\ndoIt(['a' => 'b']);\n\ndoIt('a', [\n    'a' => 'b',\n    'c' => 'd',\n]);\n```\n\n### 5.6 Anonymous functions (lambda) declarations\n\nNote space between `function`/`use` tokens and open parenthesis:\n\n```php\n// good\n$n = 100;\n$sum = array_reduce($numbers, function ($r, $x) use ($n) {\n    $this->doMagic();\n    $r += $x * $n;\n    return $r;\n});\n\n// bad\n$n = 100;\n$mul = array_reduce($numbers, function($r, $x) use($n) {\n    $this->doMagic();\n    $r *= $x * $n;\n    return $r;\n});\n```\n\nDocumentation\n-------------\n\n- Refer to [phpDoc](https://phpdoc.org/) for documentation syntax.\n- Code without documentation is not allowed.\n- All class files must contain a \"file-level\" docblock at the top of each file\n  and a \"class-level\" docblock immediately above each class.\n- There is no need to use `@return` if method does return nothing.\n- All virtual properties in classes that extend from `yii\\base\\BaseObject`\n  are documented with an `@property` tag in the class doc block.\n  These annotations are automatically generated from the `@return` or `@param`\n  tag in the corresponding getter or setter by running `./build php-doc` in the build directory.\n  You may add an `@property` tag\n  to the getter or setter to explicitly give a documentation message for the property\n  introduced by these methods when description differs from what is stated\n  in `@return`. Here is an example:\n\n  ```php\n    <?php\n    /**\n     * Returns the errors for all attribute or a single attribute.\n     * @param string $attribute attribute name. Use null to retrieve errors for all attributes.\n     * @property array An array of errors for all attributes. Empty array is returned if no error.\n     * The result is a two-dimensional array. See [[getErrors()]] for detailed description.\n     * @return array errors for all attributes or the specified attribute. Empty array is returned if no error.\n     * Note that when returning errors for all attributes, the result is a two-dimensional array, like the following:\n     * ...\n     */\n    public function getErrors($attribute = null)\n  ```\n\n#### File\n\n```php\n<?php\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n```\n\n#### Class\n\n```php\n/**\n * Component is the base class that provides the *property*, *event* and *behavior* features.\n *\n * @include @yii/docs/base-Component.md\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Component extends \\yii\\base\\BaseObject\n```\n\n\n#### Function / method\n\n```php\n/**\n * Returns the list of attached event handlers for an event.\n * You may manipulate the returned [[Vector]] object by adding or removing handlers.\n * For example,\n *\n * ```\n * $component->getEventHandlers($eventName)->insertAt(0, $eventHandler);\n * ```\n *\n * @param string $name the event name\n * @return Vector list of attached event handlers for the event\n * @throws Exception if the event is not defined\n */\npublic function getEventHandlers($name)\n{\n    if (!isset($this->_e[$name])) {\n        $this->_e[$name] = new Vector;\n    }\n    $this->ensureBehaviors();\n    return $this->_e[$name];\n}\n```\n\n#### Markdown\n\nAs you can see in the examples above we use markdown to format the phpDoc comments.\n\nThere is additional syntax for cross linking between classes, methods and properties in the documentation:\n\n- `[[canSetProperty]]` will create a link to the `canSetProperty` method or property of the same class.\n- `[[Component::canSetProperty]]` will create a link to `canSetProperty` method of the class `Component` in the same namespace.\n- `[[yii\\base\\Component::canSetProperty]]` will create a link to `canSetProperty` method of the class `Component` in namespace `yii\\base`.\n- `[[Component]]` will create a link to the `Component` class in the same namespace. Adding namespace to the class name is also possible here.\n\nTo give one of the above mentioned links another label than the class or method name you can use the syntax shown in the following example:\n\n```\n... as displayed in the [[header|header cell]].\n```\n\nThe part before the | is the method, property or class reference while the part after | is the link label.\n\nIt is also possible to link to the Guide using the following syntax:\n\n```markdown\n[link to guide](guide:file-name.md)\n[link to guide](guide:file-name.md#subsection)\n```\n\n##### Code examples\n\nCode examples should use Markdown syntax, but they should not specify the language.\nSpecifying a language in code examples may break their display in some IDEs. Here is an example:\n\n```php\n/**\n * Correct code example:\n * \n * ```\n * $object->doMagic();\n * ```\n */\nfunction doMagic()\n{\n}\n\n/**\n * Incorrect code example:\n * \n * ```php\n * $object->doMagic();\n * ```\n */\nfunction doMagic()\n{\n}\n```\n\n\n#### Comments\n\n- One-line comments should be started with `//` and not `#`.\n- One-line comment should be on its own line.\n\nAdditional rules\n----------------\n\n### `=== []` vs `empty()`\n\nUse `empty()` where possible.\n\n### multiple return points\n\nReturn early when conditions nesting starts to get cluttered. If the method is short it doesn't matter.\n\n### `self` vs. `static`\n\nAlways use `static` except the following cases:\n\n- accessing constants MUST be done via `self`: `self::MY_CONSTANT`\n- accessing private static properties MUST be done via `self`: `self::$_events`\n- It is allowed to use `self` for method calls where it makes sense such as recursive call to current implementation instead of extending classes implementation.\n\n### value for \"don't do something\"\n\nProperties allowing to configure component not to do something should accept value of `false`. `null`, `''`, or `[]` should not be assumed as such.\n\n### Directory/namespace names\n\n- use lower case\n- use plural form for nouns which represent objects (e.g. validators)\n- use singular form for names representing relevant functionality/features (e.g. web)\n- prefer single word namespaces\n- if single word isn't suitable, use camelCase\n\n"
  },
  {
    "path": "docs/internals/design-decisions.md",
    "content": "Design Decisions\n================\n\nThis document lists the design decisions that we have made after extensive discussions. Unless there are very strong\nreasons, these decisions should be kept for consistency. Any change to these decisions should get agreement among\nthe core developers.\n\n1. **[When to support path aliases](https://github.com/yiisoft/yii2/pull/3079#issuecomment-40312268)**\n   we should support path alias for properties that are configurable because using path aliases in configurations \n   are very convenient. In other cases, we should restrict the support for path aliases.\n2. **When to translate messages**\n   messages should be translated when these are displayed to non-tech end user and make sense to him. HTTP status messages,\n   exceptions about the code etc. should not be translated. Console messages are always in English because of encoding\n   and codepage handling difficulties.\n3. **[Adding new auth client support](https://github.com/yiisoft/yii2/issues/1652)**\n   For better maintainability, we will not add any additional auth clients to the core extension. They should be done \n   in terms of user extensions. \n4. **When using closures** it is recommended to **include all passed parameters** in the signature even if not all of them are\n   used. This way modifying or copying code is easier because all information is directly visible and it is not necessary to\n   look up which params are actually available in the documentation. ([#6584](https://github.com/yiisoft/yii2/pull/6584), [#6875](https://github.com/yiisoft/yii2/issues/6875))\n5. Prefer **int over unsigned int** in database schema. Using int has the benefit that it can be represented in PHP as an integer.\n   If unsigned, for 32 bit system, we would have to use string to represent it.\n   Also although unsigned int doubles the size, if you have a table that needs such big number space,\n   then it's safer to use bigint or mediumint rather than relying on unsigned.\n   <https://github.com/yiisoft/yii/pull/1923#issuecomment-11881967>\n6. [Helpers vs separate non-static classes](https://github.com/yiisoft/yii2/pull/12661#issuecomment-251599463)\n7. **Setters method chaining** should be avoided if there are methods in the classs returning meaningful values. Chaining could be\n   supported if a class is a builder where all setters are modifying internal state: https://github.com/yiisoft/yii2/issues/13026\n8. **Global exception/error handler** is used instead of local try-catch because it is reliable in terms of catching destructors and everything that happens outside the scope of the `run()` method such as bootstrap. See [#14348](https://github.com/yiisoft/yii2/issues/14348).\n"
  },
  {
    "path": "docs/internals/getting-started.md",
    "content": "Getting started with Yii 2 development\n======================================\n\nSee [Git workflow for Yii 2 contributors](git-workflow.md) on how to set up your environment.\n"
  },
  {
    "path": "docs/internals/git-workflow.md",
    "content": "Git workflow for Yii 2 contributors\n===================================\n\nSo you want to contribute to Yii? Great! But to increase the chances of your changes being accepted quickly, please\nfollow the following steps. If you are new to Git\nand GitHub, you might want to first check out [GitHub help](https://help.github.com/), [try Git](https://try.github.com)\nor learn something about [Git internal data model](https://nfarina.com/post/9868516270/git-is-simpler).\n\nPrepare your development environment\n------------------------------------\n\nThe following steps will create a development environment for Yii, which you can use to work\non the core code of Yii framework. These steps only need to be done the first time you contribute.\n\n### 1. [Fork](https://help.github.com/fork-a-repo/) the Yii repository on GitHub and clone your fork to your development environment\n\n```\ngit clone git@github.com:YOUR-GITHUB-USERNAME/yii2.git\n```\n\nIf you have trouble setting up Git with GitHub in Linux, or are getting errors like \"Permission Denied (publickey)\",\nthen you must [setup your Git installation to work with GitHub](https://help.github.com/linux-set-up-git/)\n\n> Tip: if you're not fluent with Git, we recommend reading excellent free [Pro Git book](https://git-scm.com/book/en/v2).\n\n### 2. Add the main Yii repository as an additional git remote called \"upstream\"\n\nChange to the directory where you cloned Yii, normally, \"yii2\". Then enter the following command:\n\n```\ngit remote add upstream https://github.com/yiisoft/yii2.git\n```\n\n### 3. Prepare the testing environment <span id=\"prepare-the-test-environment\"></span>\n\nThe following steps are not necessary if you want to work only on translations or documentation.\n\n- run `composer install` to install dependencies (assuming you have [composer installed globally](https://getcomposer.org/doc/00-intro.md#globally)).\n\nIf you are going to work with JavaScript:\n\n- run `npm install` to install JavaScript testing tools and dependencies (assuming you have [Node.js and NPM installed](https://nodejs.org/en/download/package-manager/)).\n\n> Note: JavaScript tests depend on [jsdom](https://github.com/tmpvar/jsdom) library which requires Node.js 4 or newer.\nUsing of Node.js 6 or 7 is more preferable.\n\n- run `php build/build dev/app basic <fork>` to clone the basic app and install composer dependencies for the basic app.\n  `<fork>` is URL of your repository fork such as `git@github.com:my_nickname/yii2-app-basic.git`. If you are core framework contributor you may skip specifying fork.\n  This command will install foreign composer packages as normal but will link the yii2 repo to\n  the currently checked out repo, so you have one instance of all the code installed.\n  \n  Do the same for the advanced app if needed: `php build/build dev/app advanced <fork>`.\n  \n  This command will also be used to update dependencies, it runs `composer update` internally.\n\n> Note: The default git repository Urls clone from github via SSH, you may add the `--useHttp` flag to the `build` command\n> to use HTTPs instead.\n\n**Now you have a working playground for hacking on Yii 2.**\n\nThe following steps are optional.\n\n### Unit tests\n\nYou can execute unit tests by running `phpunit` in the repo root directory. If you do not have phpunit installed globally\nyou can run `php vendor/bin/phpunit` or `vendor/bin/phpunit.bat` in case of execution from the Windows OS.\n\nSome tests require additional databases to be set up and configured. You can create `tests/data/config.local.php` to override\nsettings that are configured in `tests/data/config.php`.\n\nYou may limit the tests to a group of tests you are working on e.g. to run only tests for the validators and redis\n`phpunit --group=validators,redis`. You get the list of available groups by running `phpunit --list-groups`.\n\nYou can execute JavaScript unit tests by running `npm test` in the repo root directory.\n\n### Static analysis\n\nWe use [PHPStan](https://phpstan.org) for static analysis. It can be launched using the following commands:\n`php vendor/bin/phpstan` or `vendor\\bin\\phpstan.bat` in case of execution from the Windows OS.\n\nBy default, PHPStan will use the configuration from `phpstan.dist.neon`. You can create\na `phpstan.neon` file with your own configuration, and PHPStan will use it.\n\n#### Note\n\nIn PHPDoc annotations, we use PHPStan types. If you need to specify a different type for Psalm\n(which is usually the case with generics), then use Psalm annotations in conjunction with PHPStan\nannotations.\n\nAn example of what it should look like:\n\n```php\n/**\n * @return Action<covariant static>|null\n * @phpstan-return Action<covariant static>|null\n * @psalm-return Action<self>|null\n */\npublic function createAction($id)\n{\n...\n}\n```\n\n### Extensions\n\nTo work on extensions you have to clone the extension repository. We have created a command that can do this for you:\n\n```\nphp build/build dev/ext <extension-name> <fork>\n```\n\nwhere `<extension-name>` is the name of the extension, e.g. `redis` and `<fork>` is URL of your extension fork such as `git@github.com:my_nickname/yii2-redis.git`. If you are core framework contributor you may skip specifying fork.\n\nIf you want to test the extension in one of the application templates, just add it to the `composer.json` of the application as you would\nnormally do e.g. add `\"yiisoft/yii2-redis\": \"~2.0.0\"` to the `require` section of the basic app.\nRunning `php build/build dev/app basic <fork>` will install the extension and its dependencies and create\na symlink to `extensions/redis` so you are not working in the composer vendor dir but in the yii2 repository directly.\n\n> Note: The default git repository Urls clone from github via SSH, you may add the `--useHttp` flag to the `build` command\n> to use HTTPs instead.\n\n\nWorking on bugs and features\n----------------------------\n\nHaving prepared your develop environment as explained above you can now start working on the feature or bugfix.\n\n### 1. Make sure there is an issue created for the thing you are working on if it requires significant effort to fix\n\nAll new features and bug fixes should have an associated issue to provide a single point of reference for discussion\nand documentation. Take a few minutes to look through the existing issue list for one that matches the contribution you\nintend to make. If you find one already on the issue list, then please leave a comment on that issue indicating you\nintend to work on that item. If you do not find an existing issue matching what you intend to work on, please\n[open a new issue](report-an-issue.md) or create a pull request directly if it is straightforward fix. This will allow the team to\nreview your suggestion, and provide appropriate feedback along the way.\n\n> For small changes or documentation issues or straightforward fixes, you don't need to create an issue, a pull request is enough in this case.\n\n### 2. Pull the latest code from the main Yii branch\n\n```\ngit pull upstream master\n```\n\nYou should start at this point for every new contribution to make sure you are working on the latest code.\n\n### 3. Create a new branch for your feature based on the current Yii master branch\n\n> That's very important since you will not be able to submit more than one pull request from your account if you'll\n  use master.\n\nEach separate bug fix or change should go in its own branch. Branch names should be descriptive and start with\nthe number of the issue that your code relates to. If you aren't fixing any particular issue, just skip number.\nFor example:\n\n```\ngit checkout upstream/master\ngit checkout -b 999-name-of-your-branch-goes-here\n```\n\n### 4. Do your magic, write your code\n\nMake sure it works :)\n\nUnit tests are always welcome. Tested and well covered code greatly simplifies the task of checking your contributions.\nFailing unit tests as issue description are also accepted.\n\n### 5. Update the CHANGELOG\n\nEdit the CHANGELOG file to include your change, you should insert this at the top of the file under the\nfirst heading (the version that is currently under development), the line in the change log should look like one of the following:\n\n```\nBug #999: a description of the bug fix (Your Name)\nEnh #999: a description of the enhancement (Your Name)\n```\n\n`#999` is the issue number that the `Bug` or `Enh` is referring to.\nThe changelog should be grouped by type (`Bug`,`Enh`) and ordered by issue number.\n\nFor very small fixes, e.g. typos and documentation changes, there is no need to update the CHANGELOG.\n\n### 6. Commit your changes\n\nadd the files/changes you want to commit to the [staging area](https://git.github.io/git-reference/basic/#add) with\n\n```\ngit add path/to/my/file.php\n```\n\nYou can use the `-p` option to select the changes you want to have in your commit.\n\nCommit your changes with a descriptive commit message. Make sure to mention the ticket number with `#XXX` so GitHub will\nautomatically link your commit with the ticket:\n\n```\ngit commit -m \"A brief description of this change which fixes #999 goes here\"\n```\n\n### 7. Pull the latest Yii code from upstream into your branch\n\n```\ngit pull upstream master\n```\n\nThis ensures you have the latest code in your branch before you open your pull request. If there are any merge conflicts,\nyou should fix them now and commit the changes again. This ensures that it's easy for the Yii team to merge your changes\nwith one click.\n\n### 8. Having resolved any conflicts, push your code to GitHub\n\n```\ngit push -u origin 999-name-of-your-branch-goes-here\n```\n\nThe `-u` parameter ensures that your branch will now automatically push and pull from the GitHub branch. That means\nif you type `git push` the next time it will know where to push to. This is useful if you want to later add more commits\nto the pull request.\n\n### 9. Open a [pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) against upstream.\n\nGo to your repository on GitHub and click \"Pull Request\", choose your branch on the right and enter some more details\nin the comment box. To link the pull request to the issue put anywhere in the pull comment `#999` where 999 is the\nissue number.\n\n> Note that each pull-request should fix a single change. For multiple, unrelated changes, please open multiple pull requests.\n\n### 10. Someone will review your code\n\nSomeone will review your code, and you might be asked to make some changes, if so go to step #6 (you don't need to open\nanother pull request if your current one is still open). If your code is accepted it will be merged into the main branch\nand become part of the next Yii release. If not, don't be disheartened, different people need different features and Yii\ncan't be everything to everyone, your code will still be available on GitHub as a reference for people who need it.\n\n### 11. Cleaning it up\n\nAfter your code was either accepted or declined you can delete branches you've worked with from your local repository\nand `origin`.\n\n```\ngit checkout master\ngit branch -D 999-name-of-your-branch-goes-here\ngit push origin --delete 999-name-of-your-branch-goes-here\n```\n\n### Note:\n\nTo detect regressions early every merge to the Yii codebase on GitHub will be picked up by\n[Travis CI](https://travis-ci.com) for an automated testrun. As core team doesn't wish to overtax this service,\n[`[ci skip]`](https://docs.travis-ci.com/user/customizing-the-build/#Skipping-a-build) will be included to the merge description if\nthe pull request:\n\n* affect javascript, css or image files only,\n* updates the documentation,\n* modify fixed strings only (e.g. translation updates)\n\nDoing so will save travis from commencing testruns on changes that are not covered by tests in the first place.\n\n### Command overview (for advanced contributors)\n\n```\ngit clone git@github.com:YOUR-GITHUB-USERNAME/yii2.git\ngit remote add upstream https://github.com/yiisoft/yii2.git\n```\n\n```\ngit fetch upstream\ngit checkout upstream/master\ngit checkout -b 999-name-of-your-branch-goes-here\n\n/* do your magic, update changelog if needed */\n\ngit add path/to/my/file.php\ngit commit -m \"A brief description of this change which fixes #999 goes here\"\ngit pull upstream master\ngit push -u origin 999-name-of-your-branch-goes-here\n```\n"
  },
  {
    "path": "docs/internals/project-organization.md",
    "content": "Project Organization\n====================\n\nThis document describes the organization of the Yii 2 development repositories.\n \n1. Individual Core extensions and application templates are maintained in\n   separate *independent* GitHub projects under the [yiisoft](https://github.com/yiisoft) Github organization.\n    \n   Extension repository names are prefixed with `yii2-`, e.g. `yii2-gii` for the `gii` extension.\n   The composer package name is equal to the Github repository path, e.g. `yiisoft/yii2-gii`.\n   \n   Application template repository names are prefixed with `yii2-app-`, e.g. `yii2-app-basic` for the `basic` application template.\n   The composer package name is equal to the Github repository path, e.g. `yiisoft/yii2-app-basic`.\n   \n   Each extension/app project will\n \n   * maintain its tutorial doc in its \"docs\" folder. The API doc will be generated on-the-fly when the extension/app is being released.\n   * maintain its own test code in its \"tests\" folder.\n   * maintain its own message translations and all other relevant meta code.\n   * track issues via the corresponding GitHub project.\n      \n   Extension repositories will be released independently as needed, Application templates will be released together with the framework.\n   See [versioning policy](versions.md) for more details.\n\n2. The `yiisoft/yii2` project is the main repository for developing Yii 2 framework.\n   This repository provides the composer package [yiisoft/yii2-dev](https://packagist.org/packages/yiisoft/yii2-dev).\n   It contains the core framework code, framework unit tests, the definitive guide, and a set of build tools for framework development and release.\n   \n   Core framework bugs and feature requests are tracked in the issue tracker of this Github project.\n   \n3. The repository `yiisoft/yii2-framework` is a read-only git subsplit of the `framework` directory of the dev project repository and\n   provides the composer package [yiisoft/yii2](https://packagist.org/packages/yiisoft/yii2) which is the official package to be\n   used when installing the framework.\n\n4. For development the apps and extensions can be included in the dev project structure using the\n   [build dev/app](git-workflow.md#prepare-the-test-environment)-Command."
  },
  {
    "path": "docs/internals/pull-request-qa.md",
    "content": "Pull request quality assurance\n==============================\n\nWhen checking if PR could be merged or not the following criteria should be considered among others:\n\n- There should be either an issue linked to PR or PR should have a good description on what is it fixing or adding.\n- Unit tests. Not mandatory but very appreciated. These should fail without the code that the PR is fixing. \n- CHANGELOG entry should present. It should be put into next release section, ordered by issue type and number.\nNicknames of people responsible should present.\n- [Code style](core-code-style.md) and [views code style](view-code-style.md) should be OK. These could be fixed while\n  merging if the person merging prefers it that way.\n"
  },
  {
    "path": "docs/internals/release.md",
    "content": "Releasing a new version\n=======================\n\nThe list of steps needed to make a release of the framework has grown over time and became\nhard to manage manually, so we have created a command line tool to ensure no step is forgotten.\n\nRelease steps overview\n----------------------\n\n- ...\n\nThe release command\n-------------------\n\nThese steps are automated in the [release console command](../../build/controllers/ReleaseController.php)\nwhich is included in the framework development repository.\n\nThe release command can be invoked using the Yii application contained in the `build` directory of \nthe framework:\n\n    ./build/build help release  # run this in framework repo root\n\n> Info: You can run the command with the `--dryRun` option to see what it would do. Using this option,\n> no changes will be made and no commits or tags will be created or pushed.\n\n### Requirements\n\nThe release command depends on the development environment introduced in\nthe [Git Workflow Document](git-workflow.md#extensions), i.e. the application \ntemplates must be located under `/apps/` and extensions must be located under `/extensions/`.\nThis structure is preferably created using the `dev/app` and `dev/ext` commands.\n\ne.g. install an extension:\n\n    ./build/build dev/ext authclient\n\nor an application:\n\n    ./build/build dev/app basic\n\nThis installation will ensure that the extension will use the same framework code that is in the current\nrepositories state.\n\n### Version overview\n\nTo get an overview over the versions of framework and extensions, you can run\n\n    ./build/build release/info\n\nYou may run it with `--update` to fetch tags for all repos to get the newest information.\n\n### Make a release\n\nMaking a framework release includes the following commands (apps are always released together with the framework):\n\n    ./build/build release framework\n    ./build/build release app-basic\n    ./build/build release app-advanced\n\nMaking an extension release includes only one command (e.g. for redis):\n\n    ./build/build release redis\n\nThe default release command will release a new minor version from the currently checked out branch.\nTo release another version than the default, you have to specify it using the `--version` option, e.g.\n`--version=2.1.0`, or `--version=2.1.0-beta`.\n\n\n#### Release a new major version e.g. 2.1.0\n\nReleasing a new major version includes a branch change as described in the\n[versioning policy](versions.md).\nThe following describes an example of releasing version `2.1.0` which has been\ndeveloped on the `2.1` branch derived from `master`. `master` has contained the `2.0.x` versions\nbefore.\n\n- create a new branch `2.0` from `master`\n- ensure composer.json does not contain a branch alias on this branch anymore.\n- merge necessary changes from `master` to `2.1`\n- point `master` to the latest commit on `2.1`\n- adjust composer.json branch alias for master to `2.1.x-dev`.\n- delete `2.1` branch\n\nNow check out `master` and run the release command with the `--version=2.1.0` option. \n\n"
  },
  {
    "path": "docs/internals/report-an-issue.md",
    "content": "Report an Issue\n===============\n\nPlease follow the guidelines below when creating an issue so that your issue can be more promptly resolved:\n\n* Provide information including: the version of PHP and Yii, the type of operating system and Web server, browser type and version;\n* Provide the **complete** error call stack if available. A screenshot to explain the issue is very welcome.\n* Describe the steps for reproducing the issue. It would be even better if you could provide code to reproduce the issue.\n* If possible you may even create a failing unit test and [send it as a pull request](git-workflow.md).\n\nIf the issue is related to one of the official extensions, please report it in the extension repositories issue tracker.\nIf you are unsure, [report it to the main repository](https://github.com/yiisoft/yii2/issues/new) (<https://github.com/yiisoft/yii2/issues>).\n\n**Do not report an issue if**\n\n* you are asking how to use some Yii feature. You should use [the forum](https://forum.yiiframework.com/index.php/forum/42-general-discussions-for-yii-20/) or [chat room](https://www.yiiframework.com/chat/) for this purpose.\n* your issue is about security. Please [contact us directly](https://www.yiiframework.com/security/) to report security issues.\n\n**Avoid duplicated issues**\n\nBefore you report an issue, please search through [existing issues](https://github.com/yiisoft/yii2/issues) to see if your issue is already reported or fixed to make sure you are not reporting a duplicated issue. Also make sure you have the latest version of Yii and see if the issue still exists.\n"
  },
  {
    "path": "docs/internals/translation-status.md",
    "content": "Documentation status\n====================\n\nEverything is ready to be translated.\n"
  },
  {
    "path": "docs/internals/translation-teams.md",
    "content": "Translation teams\n=================\n\nBrazilian Portuguese\n--------------------\n\n- **Davidson Alencar, [@davidsonalencar](https://github.com/davidsonalencar), davidson.t.i@gmail.com**\n- [@wbraganca](https://github.com/wbraganca)\n- Alan Michel Willms Quinot, [@alanwillms](https://github.com/alanwillms), dyulax@gmail.com\n\nChina\n-----\n\n- **Paris Qian Sen 东方孤思子,[@qiansen1386](https://github.com/qiansen1386),qiansen1386@gmail.com**\n- [@AbrahamGreyson 刘阳](https://github.com/AbrahamGreyson)\n- [@fmalee](https://github.com/fmalee)\n- [@funson86 花生](https://github.com/funson86)\n- [@ivantree 长兴苗木](https://github.com/ivantree)\n- [@netyum 未来](https://github.com/netyum)\n- [@riverlet 小河](https://github.com/riverlet)\n- [@yiichina 巡洋舰](https://github.com/yiichina)\n\nFinnish\n------\n\n- Jani Mikkonen, [@janisto](https://github.com/janisto), janisto@php.net\n\nGerman\n------\n\n- Carsten Brandt, [@cebe](https://github.com/cebe), mail@cebe.cc\n\nItalian\n-------\n\n- Lorenzo Milesi, [@maxxer](https://github.com/maxxer), maxxer@yetopen.it\n\nJapanese\n-------\n\n- Nobuo Kihara 木原伸夫, [@softark](https://github.com/softark), softark@gmail.com\n- Tomoki Morita, [@jamband](https://github.com/jamband), tmsongbooks215@gmail.com\n- Hisateru Tanaka, [@tanakahisateru](https://github.com/tanakahisateru), tanakahisateru@gmail.com\n\nRussian\n-------\n\n- **Alexander Makarov, [@samdark](https://github.com/samdark), sam@rmcreative.ru**\n- [@MUTOgen](https://github.com/MUTOgen)\n- [@prozacUa](https://github.com/prozacUa)\n\nSpanish\n-------\n\n- Luciano Baraglia, [@lucianobaraglia](https://github.com/lucianobaraglia)\n- Marco Da Silva, [@markmarco16](https://github.com/markmarco16), markmarco16@gmail.com\n- Daniel Gómez Pan [@pana1990](https://github.com/pana1990), pana_1990@hotmail.com\n\nUkrainian\n---------\n\n- **Alexandr Bordun [@borales](https://github.com/Borales), admin@yiiframework.com.ua**\n- Roman Bahatyi [@RichWeber](https://github.com/RichWeber), rbagatyi@gmail.com\n- Igor Zozulinskyi [@3y3ik](https://github.com/3y3ik)\n- Vadym Chenin [@vchenin](https://github.com/vchenin), vchenin@meta.ua\n"
  },
  {
    "path": "docs/internals/translation-workflow.md",
    "content": "Translation workflow\n====================\n\nYii is translated in many languages in order to be useful for international applications and developers. Two main areas\nwhere contribution is very welcome are documentation and framework messages.\n\nFramework messages\n------------------\n\nThe framework has two types of messages: exceptions that are intended to developer and are never translated and messages\nthat are actually visible to the end user such as validation errors.\n\nIn order to start with message translation:\n\n1. Check `framework/messages/config.php` and make sure your language is listed in `languages`. If not,\n   add your language there (remember to keep the list in alphabetical order). The format of language code\n   should follow [IETF language tag spec](https://en.wikipedia.org/wiki/IETF_language_tag), for example,\n   `ru`, `zh-CN`.\n2. Go to `framework` and run `./yii message/extract @yii/messages/config.php --languages=<your_language>`.\n3. Translate messages in `framework/messages/your_language/yii.php`. Make sure file is saved using UTF-8 encoding.\n4. [Make a pull request](git-workflow.md).\n\nIn order to keep your translation up to date you may run `./yii message/extract @yii/messages/config.php --languages=<your_language>` again. It will\nautomatically re-extract messages keeping unchanged ones intact.\n\nIn the translation file each array element represents the translation (value) of a message (key). If the value is empty,\nthe message is considered as not translated. Messages that no longer need translation will have their translations\nenclosed between a pair of '@@' marks. Message string can be used with plural forms format. Check [i18n section\nof the guide](../guide/tutorial-i18n.md) for details.\n\nDocumentation\n-------------\n\nPut documentation translations under `docs/<original>-<language>` where `<original>` is the original documentation name\nsuch as `guide` or `internals` and `<language>` is the language code of the language docs are translated to. For the\nRussian guide translation it is `docs/guide-ru`.\n\nAfter initial work is done you can get what's changed since last translation of the file using a special command from\n`build` directory:\n\n```\nphp build translation \"../docs/guide\" \"../docs/guide-ru\" \"Russian guide translation report\" > report_guide_ru.html\n```\n\nIf it will complain about composer, perform `composer install` in the source root dir.\n\nFor information on the documentation syntax and style guide, see [documentation_style_guide.md](../documentation_style_guide.md).\n"
  },
  {
    "path": "docs/internals/versions.md",
    "content": "Yii Versioning\n==============\n\nThis document summarizes the versioning policy of Yii. Our current versioning strategy is\na variant of [Semantic Versioning](https://semver.org/).\n\nWithin the core developer team, we have emphasized several times that it is important to keep 2.0.x releases backwards\ncompatible. But this is an ideal plan. In a real world this is hard to achieve. See [Backwards Compatibility](bc.md)\ndocument for detailed description of what BC is.\n\nIn summary, our versioning policy for Yii 2 is as follows:\n\n## Version numbers\n\nVersion numbers are in the format of `2.x.y.z`, where the `z` can be skipped for releases for which `z` is `0`.\n\nA possible Yii version 3 is not covered here as we'd expect it to be like 2.0 over 1.0. We expect that this only happens every 3 to 5 years,\ndepending on external technology advancement (such as PHP upgraded from 5.0 to 5.4).\n\n### `2.X.0`: major releases\n\nBackwards compatibility breaking releases, which contain major features and changes that may break BC. Upgrading from\nearlier versions may not be trivial, but a complete upgrade guide will be available.\n\n* Mainly contain new features and bug fixes\n* Contain minor features and bug fixes merged from patch releases.\n* May contain BC-breaking changes which are recorded in a `UPGRADE-2.X.md` file.\n* Release cycle is around 12 months or more.\n* Require pre-releases: `2.X.0-alpha`, `2.X.0-beta`, `2.X.0-rc`.\n* Requires major news releases and marketing effort.\n\n\n### `2.x.Y`: minor releases\n\nMinor releases, which are mostly BC-compatible. Ideally, we hope they contain only changes that do not affect backwards\ncompatibility. However, it is not always possible to keep everything 100% BC-compatible, so upgrade notes are recorded\nin `UPGRADE.md`. Practically, since 2.0.x is released more frequently, we are also adding minor features\nto it so that users can enjoy them earlier.\n\n* Mainly contain bug fixes and enhancements\n* Should be mostly backwards compatible to ensure worry-free upgrade. Only a few exceptions are allowed which are documented\n  in `UPGRADE.md`.\n* Release cycle is around 1 to 2 months.\n* No pre-releases (alpha, beta, RC) needed.\n* Should be merged back to master branch constantly (at least once every week manually).\n* With news announcements. Project site will be updated.\n\n\n### `2.x.y.Z`: patch releases\n\nPatch releases, which should be 100% BC-compatible, containing bug fixes only.\nNo news announcement or project site update (unless it contains major/security issue fixes).\nThe release process is mostly automatic.\n\n* Containing bug fixes only, no features included\n* Must be 100% backward compatible to ensure worry-free upgrade. Only exception is security issues that may require breaking BC\n* Release cycle is around 1 to 2 weeks\n* No pre-releases (alpha, beta, RC) needed\n* Should be merged back to master branch on release\n\n\n## Branching policy\n\n* `master` branch is the development branch for the current stable major release, currently `2.0.x` versions.\n* Each new major release will be developed on a branch named after the version number, e.g. `2.1`.\n* Once a new major release `2.n` is ready, create a maintenance branch named `2.(n-1).x` off `master`.\n  E.g. a `2.0` branch is created if version `2.1.0` is released as stable and will now be developed on `master`\n  (cmp. [composer branch naming schema](https://getcomposer.org/doc/02-libraries.md#branches)).\n* Create tags `2.x.y.z` and `2.x.y` branch to mark patch releases. For `2.x.y.0` releases, the `0` will be skipped.\n* Changes on `2.n.x` maintenance branches will be merged back to `master` constantly.\n\nThe following image shows an illustration of the branches on changing commit history over time:\n\n![Branching policy](versions-branches.png)\n\n\n## Releases\n\nFramework and official extension projects are released independently of each other, i.e. version number mismatch between\nframework and extension is expected. The Application Templates are always released together with the framework.\n\nThe release cycles described above only apply to the core framework. Extensions are released on demand.\nIt is likely that an extension has no new releases for a very long time because it does not receive any bug fixes\nor enhancements.\n"
  },
  {
    "path": "docs/internals/view-code-style.md",
    "content": "Yii 2 View Code Style\n=====================\n\nThe following code style is used for Yii 2.x core and official extensions view files. We aren't forcing you to use this code style for your application. Feel free to choose what suits you better.\n\n```php\n<?php\n// Leading PHP tag is a must in every template file. Empty line after leading PHP tag is also required.\n\n// Describe input variables passed by controller here.\n/**\n * @var \\yii\\base\\View $this\n * @var \\yii\\widgets\\ActiveForm $form\n * @var \\app\\models\\Post[] $posts\n * @var \\app\\models\\ContactMessage $contactMessage\n */\n// Empty line below is necessary.\n\n// Namespaced classes declaration.\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n// Empty line below is necessary.\n\n// Set context properties, call its setters, do other things.\n$this->title = 'Posts';\n?>\n<!-- Separate PHP blocks are preferred for foreach, for, if etc. -->\n<?php foreach ($posts as $post): ?>\n    <!-- Note indentation level here. -->\n    <h2><?= Html::encode($post['title']) ?></h2>\n    <p><?= Html::encode($post['shortDescription']) ?></p>\n<!-- `endforeach;`, `endfor;`, `endif;`, etc. should be used instead of `}` in case multiple PHP blocks are used -->\n<?php endforeach; ?>\n\n<!-- Widget declaration may or may not be exploded into multiple LOCs. -->\n<?php $form = ActiveForm::begin([\n    'options' => ['id' => 'contact-message-form'],\n    'fieldConfig' => ['inputOptions' => ['class' => 'common-input']],\n]); ?>\n    <!-- Note indentation level here. -->\n    <?= $form->field($contactMessage, 'name')->textInput() ?>\n    <?= $form->field($contactMessage, 'email')->textInput() ?>\n    <?= $form->field($contactMessage, 'subject')->textInput() ?>\n    <?= $form->field($contactMessage, 'body')->textArea(['rows' => 6]) ?>\n\n    <div class=\"form-actions\">\n        <?= Html::submitButton('Submit', ['class' => 'common-button']) ?>\n    </div>\n<!-- Ending widget call should have individual PHP tag. -->\n<?php ActiveForm::end(); ?>\n<!-- Trailing newline character is mandatory. -->\n\n```\n"
  },
  {
    "path": "docs/internals-es/translation-workflow.md",
    "content": "Flujo de Trabajo de Traducción\n==============================\n\nYii se traduce en muchos idiomas con el fin de ser útil para desarrolladores de aplicaciones e internacionales.\nDos áreas principales donde la contribución es muy bienvenida son la documentación y los mensajes del framework.\n\nMensajes del Framework\n----------------------\n\nEl framework tiene dos tipos de mensajes: excepciones que están destinadas al desarrollador y nunca se traducen, y mensajes\nque en realidad son visibles para el usuario final, tales como errores de validación.\n\nEl orden para comenzar con la traducción de mensajes:\n\n1. Comprobar que en `framework/messages/config.php` su idioma aparece en `languages`. Si no, añade tu idioma allí (recuerda que debes mantener la lista en orden alfabético).\nEl formato de código de idioma debe seguir el [Código de Idiomas IETF](https://es.wikipedia.org/wiki/C%C3%B3digo_de_idioma_IETF), por ejemplo, `es`.\n2. Ir al directorio `framework` y ejecutar el comando `yii message/extract @yii/messages/config.php --languages=<tu-idioma>`.\n3. Traducir los mensajes en `framework/messages/tu-idioma/yii.php`. Asegúrate de guardar el archivo con codificación UTF-8.\n4. [Crear un pull request](https://github.com/yiisoft/yii2/blob/master/docs/internals-es/git-workflow.md).\n\nCon el fin de mantener la traducción al día puedes ejecutar `yii message/extract @yii/messages/config.php --languages=<tu-idioma>` nuevamente.\nSe volverán a extraer automáticamente los mensajes de mantenimiento intactos sin los cambios.\n\nEn el archivo de traducción de cada elemento del `array` representa un mensaje (clave) y su la traducción (valor). Si el valor está vacío, el mensaje se considera como no traducido.\nLos mensajes que ya no necesiten traducción tendrán sus traducciones encerrado entre un par de marcas '@@'. El texto de los mensajes se puede utilizar con el formato de formas plurales.\nChequea la [sección i18n de la guía](../guide-es/tutorial-i18n.md) para más detalles.\n\nDocumentación\n-------------\n\nColoca las traducciones de la documentación bajo `docs/<original>-<language>` donde `<original>` es el nombre de la documentación original como `guide` o `internals`\ny `<language>` es el código del idioma al que se está traduciendo. Para la traducción al español de la guía, es `docs/guide-es`.\n\nDespués de que el trabajo inicial está hecho, puedes obtener los cambios desde la última traducción del archivo usando un comando especial del directorio `build`:\n\n```\nphp build translation \"../docs/guide\" \"../docs/guide-es\" \"Reporte de traducción guia en Español\" > report_guide_es.html\n```\n\nSi recibes un error de composer, ejecuta `composer install` en el directorio raíz.\n\nConvenios para la traducción\n----------------------------\n\nLas palabras en inglés que son propias del framework o de PHP se pueden dejar en el idioma original. Ejemplos: `namespace`, `assets`, `helper`, `widget`, etc.\n\nPara las palabras que están muy ligadas a conceptos extendidos se deben traducir y poner entre paréntesis su equivalente en el idioma original. Ejemplos : `petición` (request), `respuesta` (response), `comportamiento` (behavior), etc.\n\n> Aclaraciones :\n* Sólo mencionar una vez entre paréntesis la palabra original en su primera aparición en el texto o en el fichero README.md,\nevitando redundancias. Ejemplo: vista(view), controlador(controller), etc.\n* Si una palabra se refiere a un concepto o acción se aplicará la traducción, si por el contrario se refiere a un tipo de dato de php o del framework no se debe traducir.\n* El equipo de traductores hemos escogido el Español-latino para elaborar las traducciones de las guías en Español, eviten usar expresiones o palabras autóctonas de su región para un mayor acercamiento al resto de hispano hablantes.\n"
  },
  {
    "path": "docs/internals-fa/core-code-style.md",
    "content": "رعایت اصول و سبک کدنویسی فریمورک Yii2\n===============================\n<p dir='rtl'>\nسبک کدنویسی که در نسخه 2 فریمورک و extension های رسمی استفاده میشه دارای اصول، قواعد و قانون های خودش هست. پس اگر تصمیم دارید چیزی به هسته اضافه کنید باید این قواعد رو در نظر بگیرید حتی در غیر این صورت هم رعایت این موارد خالی از لطف نیست و توصیه می‌کنم این کارو انجام بدین.\n\nالبته که نیاز نیست حتما این موارد رو در برنامه‌های خودتون رعایت کنید و می تونید در این مورد راحت باشید...\n</p>\n<p dir='rtl'>\nمی‌تونید برای دریافت پیکربندی CodeSniffer اینجا رو مطالعه کنید: https://github.com/yiisoft/yii2-coding-standards\n</p>\n\n## 1. نگاه کلی\n<p dir='rtl'>\nبه طور کلی ما از سبک کدنویسی PSR-2 پیروی می‌کنیم:\n\nhttps://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md</p>\n\n<p dir='rtl'> در فایل‌ها باید از برچسب‌های php?> و =?> استفاده شود.</p>\n<p dir='rtl'> در پایان هر فایل باید یک خط جدید (newline) داشته باشید.</p>\n<p dir='rtl'> encoding فایل برای کدهای php باید UTF-8 without BOM باشد.</p>\n<p dir='rtl'>  به جای tab از 4 فضای خالی (space) استفاده کنید.</p>\n<p dir='rtl'> نام کلاس‌ها باید به صورت StudlyCaps تعریف شوند.</p>\n<p dir='rtl'> ثابت‌های داخل کلاس تماما باید با حروف بزرگ و گاهی با جداکننده \"_\" تعریف شوند.</p>\n<p dir='rtl'> نام متدها و پراپرتی‌ها باید به صورت camelCase تعریف شوند.</p>\n<p dir='rtl'> پراپرتی‌های خصوصی (private) باید با \"_\" شروع شوند.</p>\n<p dir='rtl'> همیشه از elseif جای else if استفاده کنید.</p>\n\n## 2. فایل‌ها\n\n<p dir='rtl'> در فایل‌ها باید از برچسب های php?> و =?> استفاده کرد نه از ?> .</p>\n<p dir='rtl'> در انتهای فایل‌های php نباید از تگ ?> استفاده کنید.</p>\n<p dir='rtl'>  در انتهای هر خط نباید space وجود داشته باشد.</p>\n<p dir='rtl'> پسوند فایل‌هایی که شامل کد php هستند باید php. باشد.</p>\n<p dir='rtl'> encoding فایل برای کدهای php باید UTF-8 without BOM باشد.</p>\n\n\n## 3. نام کلاس‌ها\n<p dir='rtl'>\nنام کلاس‌ها باید به صورت StudlyCaps تعریف شوند. به عنوان مثال، `Controller` و `Model`.</p>\n\n## 4. کلاس‌ها\n\n<p dir='rtl'> نام کلاس‌ها باید به صورت CamelCase تعریف شوند.</p>\n<p dir='rtl'> آکولاد باز باید در خط بعدی، زیر نام کلاس نوشته شود.</p>\n<p dir='rtl'> تمام کلاس‌ها باید بلاک مستندات مطابق استاندارد PHPDoc داشته باشند.</p>\n<p dir='rtl'> برای تمام کدهای داخل کلاس باید با 4 space فاصله ایجاد کنید.</p>\n <p dir='rtl'> فقط یک کلاس داخل هر فایل php باید موجود باشد.</p>\n<p dir='rtl'> تمام کلاس‌ها باید namespaced داشته باشند.</p>\n<p dir='rtl'> نام کلاس باید معادل نام فایل و namespace باید مطابق مسیر آن باشد.</p>\n\n```php\n/**\n * Documentation\n */\nclass MyClass extends \\yii\\base\\BaseObject implements MyInterface\n{\n    // code\n}\n```\n\n### 4.1. ثابت‌ها\n<p dir='rtl'>\nثابت‌های داخل کلاس تماما باید با حروف بزرگ و گاهی با جداکننده \"_\" تعریف شوند.<p>\n\n```php\n<?php\nclass Foo\n{\n    const VERSION = '1.0';\n    const DATE_APPROVED = '2012-06-01';\n}\n```\n### 4.2. پراپرتی‌ها\n\n<p dir='rtl'> از کلید واژه های public ،protected و private استفاده کنید.</p>\n<p dir='rtl'> پراپرتی‌های public و protected باید در بالای کلاس و قبل از متدها تعریف شوند. private هم همینطور اما ممکن هست گاهی قبل از متدی که با آن مرتبط هست آورده شود.</p>\n<p dir='rtl'> ترتیب تعریف پراپرتی‌ها باید به صورت اول public، دوم protected و سپس private باشد! کار بسیار ساده‌ایست :)</p>\n<p dir='rtl'> برای خوانایی بهتر می‌تونید از خط خالی بین گروه‌های public، protected و private استفاده کنید.\n<p dir='rtl'> متغیر های private باید مثل varName_$ باشند.</p>\n<p dir='rtl'> اعضای عمومی داخل کلاس باید به صورت camelCase تعریف شوند. (حرف اول کوچک، با CamelCase فرق میکنه)</p>\n<p dir='rtl'> بهتره از نام‌هایی مثل i$ و j$ استفاده نکنید.</p>\n\n```php\n<?php\nclass Foo\n{\n    public $publicProp1;\n    public $publicProp2;\n\n    protected $protectedProp;\n\n    private $_privateProp;\n\n\n    public function someMethod()\n    {\n        // ...\n    }\n}\n```\n\n### 4.3. متدها\n\n<p dir='rtl'> توابع و متدها باید camelCase باشند.</p>\n<p dir='rtl'> نام باید هدف رو نشون بده.</p>\n<p dir='rtl'> از کلید واژه های public، protected و private استفاده کنید.</p>\n<p dir='rtl'> آکولاد باز باید در خط بعدی یعنی زیر نام متد قرار بگیره.</p>\n\n```php\n/**\n * Documentation\n */\nclass Foo\n{\n    /**\n     * Documentation\n     */\n    public function bar()\n    {\n        // code\n        return $value;\n    }\n}\n```\n\n### 4.4 بلوک‌های PHPDoc\n\n<p dir='rtl'> برای متدها باید مستندات بنویسید (PHPDoc).</p>\n<p dir='rtl'> در PHPDoc نوع param@ ،var@ ،property@ و return@ باید مشخص شود (bool, int, string, array یا null).</p>\n<p dir='rtl'> برای تایپ آرایه در PHPDoc از []ClassName استفاده کنید.</p>\n<p dir='rtl'> خط اول PHPDoc باید هدف یک متد رو شرح بده.</p>\n<p dir='rtl'> اگر متدها چیزی رو بررسی میکنن مثل isActive بخش PHPDoc رو باید با عبارت Checks whether شروع کنید.</p>\n<p dir='rtl'> return@ در PHPDoc  یاید دقیقا مشخص کنه چی بازگردانده میشه.</p>\n\n```php\n/**\n * Checks whether the IP is in subnet range\n *\n * @param string $ip an IPv4 or IPv6 address\n * @param int $cidr the CIDR lendth\n * @param string $range subnet in CIDR format e.g. `10.0.0.0/8` or `2001:af::/64`\n * @return bool whether the IP is in subnet range\n */\n private function inRange($ip, $cidr, $range)\n {\n   // ...\n }\n```\n\n### 4.5 Constructors\n\n<p dir='rtl'> `__construct` باید به جای استایل PHP 4 constructors استفاده شود.</p>\n\n## 5 PHP\n\n### 5.1 نوع‌ها\n\n<p dir='rtl'> تمام انواع و مقادیر باید با حروف کوچک نوشته شوند مثل true ،false ،null و array.</p>\n<p dir='rtl'> تغییر نوع یک متغیر خیلی بده، به این مثال توجه کنید:</p>\n\n\n```php\npublic function save(Transaction $transaction, $argument2 = 100)\n{\n    $transaction = new Connection; // bad\n    $argument2 = 200; // good\n}\n```\n\n### 5.2 رشته‌ها\n\n<p dir='rtl'> اگر رشته‌ی شما شامل متغیرهای دیگه‌ای نیست از تک‌کوتیشن جای دابل‌کوتیشن استفاده کنید.</p>\n\n```php\n$str = 'Like this.';\n```\n\n<p dir='rtl'> دو روش زیر مناسب برای جایگزینی هستند:</p>\n\n```php\n$str1 = \"Hello $username!\";\n$str2 = \"Hello {$username}!\";\n```\n<p dir='rtl'>\nحالت زیر مجاز نیست:</p>\n\n```php\n$str3 = \"Hello ${username}!\";\n```\n\n#### الحاق\n\n<p dir='rtl'> برای الحاق قبل و بعد کاراکتر dot فاصله بذارید:</p>\n\n```php\n$name = 'Yii' . ' Framework';\n```\n\n<p dir='rtl'> و اگر رشته ی شما بلند بود می‌تونید اینطور عمل کنید:</p>\n\n```php\n$sql = \"SELECT *\"\n    . \"FROM `post` \"\n    . \"WHERE `id` = 121 \";\n```\n\n### 5.3 آرایه‌ها\n\n<p dir='rtl'> برای تعریف آرایه‌ها از ساختار کوتاه اون یعنی [] استفاده کنید.</p>\n<p dir='rtl'> از ایندکس منفی در آرایه‌ها استفاده نکنید.</p>\n<p dir='rtl'> روش‌های زیر قابل قبول و مناسب هستند:</p>\n\n```php\n$arr = [3, 14, 15, 'Yii', 'Framework'];\n```\n\n```php\n$arr = [\n    3, 14, 15,\n    92, 6, $test,\n    'Yii', 'Framework',\n];\n```\n\n```php\n$config = [\n    'name' => 'Yii',\n    'options' => ['usePHP' => true],\n];\n```\n\n### 5.4 دستورات کنترلی\n\n<p dir='rtl'> در دستورات کنترلی قبل و بعد پرانتز space بذارید.</p>\n<p dir='rtl'> آکولاد باز در همان خط دستور قرار میگیرد.</p>\n<p dir='rtl'> آکولاد بسته در خط جدید.</p>\n<p dir='rtl'> برای دستورات یک خطی همیشه از پرانتز استفاده کنید.</p>\n\n```php\nif ($event === null) {\n    return new Event();\n}\nif ($event instanceof CoolEvent) {\n    return $event->instance();\n}\nreturn null;\n\n\n// the following is NOT allowed:\nif (!$model && null === $event)\n    throw new Exception('test');\n```\n\n<p dir='rtl'>بعد از return از else استفاده نکنید:</p>\n\n```php\n$result = $this->getResult();\nif (empty($result)) {\n    return true;\n} else {\n    // process result\n}\n```\n<p dir='rtl'>اینطوری بهتره:</p>\n\n\n```php\n$result = $this->getResult();\nif (empty($result)) {\n   return true;\n}\n\n// process result\n```\n\n#### switch\n\n<p dir='rtl'> از فرمت زیر برای switch استفاده کنید:</p>\n\n```php\nswitch ($this->phpType) {\n    case 'string':\n        $a = (string) $value;\n        break;\n    case 'integer':\n    case 'int':\n        $a = (int) $value;\n        break;\n    case 'boolean':\n        $a = (bool) $value;\n        break;\n    default:\n        $a = null;\n}\n```\n\n### 5.5 صدا زدن فانکشن‌ها\n\n<p dir='rtl'>روش مناسب صدا زدن فانکشن‌ها همراه با پارامتر هم به این صورته:</p>\n\n```php\ndoIt(2, 3);\n\ndoIt(['a' => 'b']);\n\ndoIt('a', [\n    'a' => 'b',\n    'c' => 'd',\n]);\n```\n\n### 5.6  تعریف Anonymous functions (lambda)\n\n<p dir='rtl'> در توابع بی نام بین function/use فضای خالی (space) بذارید:</p>\n\n```php\n// good\n$n = 100;\n$sum = array_reduce($numbers, function ($r, $x) use ($n) {\n    $this->doMagic();\n    $r += $x * $n;\n    return $r;\n});\n\n// bad\n$n = 100;\n$mul = array_reduce($numbers, function($r, $x) use($n) {\n    $this->doMagic();\n    $r *= $x * $n;\n    return $r;\n});\n```\n\nمستند نویسی\n-------------\n\n<p dir='rtl'> https://phpdoc.org رو بخونید و موارد اون رو رعایت کنید.</p>\n<p dir='rtl'> کد بدون مستندات مجاز نیست.</p>\n<p dir='rtl'> تمام کلاس‌ها باید شامل بلاک مستندات در ابتدای فایل باشند.</p>\n<p dir='rtl'> نیازی به نوشتن return@ ندارید اگر متد شما چیزی برنمی‌گرداند.</p>\n<p dir='rtl'> به مثال‌های زیر توجه کنید:</p>\n\n  ```php\n    <?php\n    /**\n     * Returns the errors for all attribute or a single attribute.\n     * @param string $attribute attribute name. Use null to retrieve errors for all attributes.\n     * @property array An array of errors for all attributes. Empty array is returned if no error.\n     * The result is a two-dimensional array. See [[getErrors()]] for detailed description.\n     * @return array errors for all attributes or the specified attribute. Empty array is returned if no error.\n     * Note that when returning errors for all attributes, the result is a two-dimensional array, like the following:\n     * ...\n     */\n    public function getErrors($attribute = null)\n  ```\n\n#### فایل\n\n```php\n<?php\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n```\n\n#### کلاس\n\n```php\n/**\n * Component is the base class that provides the *property*, *event* and *behavior* features.\n *\n * @include @yii/docs/base-Component.md\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Component extends \\yii\\base\\BaseObject\n```\n\n\n#### توابع / متد\n\n```php\n/**\n * Returns the list of attached event handlers for an event.\n * You may manipulate the returned [[Vector]] object by adding or removing handlers.\n * For example,\n *\n * ```\n* $component->getEventHandlers($eventName)->insertAt(0, $eventHandler);\n* ```\n*\n* @param string $name the event name\n* @return Vector list of attached event handlers for the event\n* @throws Exception if the event is not defined\n  */\n  public function getEventHandlers($name)\n  {\n  if (!isset($this->_e[$name])) {\n  $this->_e[$name] = new Vector;\n  }\n  $this->ensureBehaviors();\n  return $this->_e[$name];\n  }\n```\n\n#### نظرات\n\n<p dir='rtl'> از // برای کامنت گذاری استفاده کنید نه از #.</p>\n<p dir='rtl'> در خطوطی که کامنت گذاشتین نباید کد بنویسید، یعنی اون خط برای اون کامنت باید باشه.</p>\n\nقوانین بیشتر\n----------------\n\n<p dir='rtl'> تا جایی که می‌تونید از تابع empty به جای === استفاده کنید.</p>\n<p dir='rtl'> اگر شرایط تو در تویی در کد شما وجود نداره return زود هنگام یا ساده تر بگم return وسط متد مشکلی نخواهد داشت.</p>\n<p dir='rtl'> همیشه از static جای self به جز موارد زیر استفاده کنید:</p>\n<p dir='rtl'>1) دسترسی به ثابت‌ها باید با self انجام بشه.</p>\n<p dir='rtl'>2) دسترسی به پراپرتی‌های خصوصی باید با self انجام بشه.</p>\n<p dir='rtl'>3) مجاز به استفاده از self برای صدا زدن توابع در مواقعی مثل فراخوانی بازگشتی هستید.</p>\n\n\nنیم‌اسپیس‌ها\n----------------\n<p dir='rtl'> از حرف کوچک استفاده کنید.</p>\n<p dir='rtl'> از فرم جمع اسم‌ها برای نشان دادن یک شی استفاده کنید مثل validators.</p>\n<p dir='rtl'> از فرم مفرد اسم‌ها برای قابلیت‌ها و امکانات استفاده کنید مثل web.</p>\n<p dir='rtl'> بهتره فضای نام تک‌کلمه‌ای باشه در غیر این صورت از camelCase استفاده کنید.</p>\n"
  },
  {
    "path": "docs/internals-ja/README.md",
    "content": "Yii 開発者ドキュメント\n======================\n\nこのディレクトリは、Yii フレームワークの開発とリリース・プロセスに関するドキュメントを含んでいます。\n\n寄稿者のためのガイドライン\n--------------------------\n\n- [課題を報告する仕方](report-an-issue.md)\n- [始めよう](getting-started.md)\n- [Yii 2 寄稿者のための Git ワークフロー](git-workflow.md) - 開発環境をセットアップして Yii に対する寄稿を始めるためのステップ・バイ・ステップのガイド。\n- [Yii 2 コア・フレームワーク・コード・スタイル](core-code-style.md)\n- [Yii 2 ビュー・コード・スタイル](view-code-style.md)\n\n\nドキュメント\n------------\n\n- [翻訳ステータス](translation-status.md) - どのドキュメントが翻訳できる状態か。\n- [翻訳チーム](translation-teams.md)\n- [翻訳ワークフロー](translation-workflow.md)\n\n\nフレームワーク開発\n------------------\n\n- [プル・リクエストの品質保証](pull-request-qa.md)\n- [自動化されるタスク](automation.md) コード・スタイルの修正、ドキュメントやファイルの自動生成など。\n- [設計上の決定](design-decisions.md) - よく議論される事柄についての FAQ 形式の声明リスト。\n\nバージョニングとリリース\n------------------------\n\n- [プロジェクトの編成](project-organization.md)\n- [Yii のバージョニング](versions.md)\n- [後方互換性](bc.md)\n- [新しいバージョンのリリース](release.md)\n\nその他\n------\n\n### 例外の階層\n\n![Yii フレームワークの例外階層](exception_hierarchy.png)\n\n### データベースのテスト\n\n[こちら](https://gist.github.com/sergeymakinen/0696a5952f160ea28d7b64c3adfecf6f) に、Yii がサポートする全てのデータベースのためのテスト環境構成があります。\n"
  },
  {
    "path": "docs/internals-ja/automation.md",
    "content": "自動化\n======\n\nYii の開発に取り組む際に、自動化できるタスクがいくつかあります:\n\n- フレームワークのルート・ディレクトリに配置されるクラス・マップ `classes.php` の生成。\n  `./build/build classmap` を実行して生成してください。\n\n- クラス・ファイルの中の、ゲッターとセッターによって導入されるプロパティを記述する `@property` 注釈の生成。\n  `./build/build php-doc/property` を実行して注釈を更新してください。\n\n- コード・スタイルと phpdoc コメントの細かい問題の修正。\n  `./build/build php-doc/fix` を実行して修正してください。\n  このコマンドは完璧なものではないため、望ましくない変更があるかもしれませんので、コミットする前に変更点をチェックしてください。\n  `git add -p` を使って変更をレビューすることが出来ます。\n\n- Mime タイプ・マジック・ファイル (`framework/helpers/mimeTypes.php`) の Apache HTTPd レポジトリによる更新。\n  `./build/build mime-type` を実行してファイルを更新して下さい。\n\n- CHANGELOG ファイルのエントリの出現順序は、`./build/build release/sort-changelog framework` を実行することで更新することが出来ます。\n\n上記のコマンドの全てが [リリースの工程]() に含まれています。これらをリリースとリリースの間に実行しても構いませんが、必要ではありません。\n"
  },
  {
    "path": "docs/internals-ja/bc.md",
    "content": "# 後方互換性\n\n私たちは `2.x.y.Z` のようなパッチ・リリースにおいては厳密に後方互換性を保持するように努めるとともに、\n`2.x.Y` のようなマイナー・リリースにおいても修正が必要となるような後方互換性の無い変更を避けるように努めています。\n\nバージョン番号については [Yii バージョン規約](versions.md) を参照して下さい。\n\n## 使用\n\n### インタフェイス\n\nユース・ケース | 後方互換?\n-------------|----------\nインタフェイスのタイプ・ヒント | Yes\nインタフェイス・メソッドの呼び出し | Yes\n**インタフェイスの実装における ...** |\nメソッドの実装 | Yes\n実装済みメソッドへの引数の追加 | Yes\n引数のデフォルト値の追加 | Yes\n\n### クラス\n\nユース・ケース | 後方互換?\n-------------|----------\nクラスのタイプ・ヒント | Yes\n新しいインスタンスの作成 | Yes\nクラスの拡張 | Yes\nパブリック・プロパティへのアクセス | Yes\nパブリック・メソッドの呼び出し | Yes\n**クラスの拡張における ...** |\nプロテクト・プロパティへのアクセス | Yes\nプロテクト・メソッドの呼び出し | Yes\nパブリック・プロパティのオーバーライド | Yes\nプロテクト・プロパティのオーバーライド | Yes\nパブリック・メソッドのオーバーライド | Yes\nプロテクト・メソッドのオーバーライド | Yes\n新しいプロパティの追加 | No\n新しいメソッドの追加 | No\nオーバーライドされたメソッドへの引数の追加 | Yes\n引数のデフォルト値の追加 | Yes\nプライベート・メソッドの呼び出し(リフレクション経由) | No\nプライベート・プロパティへのアクセス(リフレクション経由) | No\n\n\n## 開発\n\n### インタフェイスの変更\n\n変更のタイプ | 後方互換?\n-------------|----------\n削除 | No\n名前または名前空間の変更 | No\n親のインタフェイスの追加 | 新しいメソッドが追加されなければ Yes\n親のインタフェイスの削除 | No\n**インタフェイス・メソッド** | \nメソッドの追加 | No\nメソッドの削除 | No\n名前の変更 | No\n親のインタフェイスへの移動 | Yes\nデフォルト値を持たない引数の追加 | No\nデフォルト値を持つ引数の追加 | No\n引数の削除 | Yes (末尾の一つまたは複数の引数のみ)\n引数のデフォルト値の追加 | No\n引数のデフォルト値の削除 | No\n引数のタイプ・ヒントの追加 | No\n引数のタイプ・ヒントの削除 | No\n引数の型の変更 | No\n戻り値の型の変更 | No\n**定数** |\t \n定数の追加 | Yes\n定数の削除 | No\n定数の値の変更 | シリアライズされる可能性のあるオブジェクトを除いて Yes。UPGRADE.md への記載が必須\n\n### クラス\n\n変更のタイプ | 後方互換?\n-------------|----------\n削除 | No\nfinal への変更 | No\nabstract への変更 | No\n名前または名前空間の変更 | No\n親クラスの変更 | Yes ただし元の親クラスは祖先クラス(祖父母クラスなど)として残らなければならない\nインタフェイスの追加 | Yes\nインタフェイスの削除 | No\n**パブリック・プロパティ** | \nパブリック・プロパティの追加 | Yes\nパブリック・プロパティの削除 | No\n可視性の低減 | No\n親クラスへの移動 | Yes\n**プロテクト・プロパティ** | \t \nプロテクト・プロパティの追加 | Yes\nプロテクト・プロパティの削除 | No\n可視性の低減 | No\n親クラスへの移動 | Yes\n**プライベート・プロパティ** | \nプライベート・プロパティの追加 | Yes\nプライベート・プロパティの削除 | Yes\n**コンストラクタ** | \nコンストラクタの削除 | No\nパブリック・コンストラクタの可視性低減 | No\nプロテクト・コンストラクタの可視性低減 | No\n親クラスへの移動 | Yes\n**パブリック・メソッド** |\nパブリック・メソッドの追加 | Yes\nパブリック・メソッドの削除 | No\n名前の変更 | No\n可視性の低減 | No\n親クラスへの移動 | Yes\nデフォルト値を持たない引数の追加 | No\nデフォルト値を持つ引数の追加 | No\n引数の削除 | Yes (末尾の一つまたは複数の引数のみ)\n引数のデフォルト値の追加 | No\n引数のデフォルト値の削除 | No\n引数のタイプ・ヒントの追加 | No\n引数のタイプ・ヒントの削除 | No\n引数の型の変更 | No\n戻り値の型の変更 | No\n**プロテクト・メソッド** | \t \nプロテクト・メソッドの追加 | Yes\nプロテクト・メソッドの削除 | No\n名前の変更 | No\n可視性の低減 | No\n親クラスへの移動 | Yes\nデフォルト値を持たない引数の追加 | No\nデフォルト値を持つ引数の追加 | No\n引数の削除 | Yes (末尾の一つまたは複数の引数のみ)\n引数のデフォルト値の追加 | No\n引数のデフォルト値の削除 | No\n引数のタイプ・ヒントの追加 | No\n引数のタイプ・ヒントの削除 | No\n引数の型の変更 | No\n戻り値の型の変更 | No\n**プライベート・メソッド** | \t \nプライベート・メソッドの追加 | Yes\nプライベート・メソッドの削除 | Yes\n名前の変更 | Yes\nデフォルト値を持たない引数の追加 | Yes\nデフォルト値を持つ引数の追加 | Yes\n引数の削除 | Yes\n引数のデフォルト値の追加 | Yes\n引数のデフォルト値の削除 | Yes\n引数のタイプ・ヒントの追加 | Yes\n引数のタイプ・ヒントの削除 | Yes\n引数の方の変更 | Yes\n戻り値の型の変更 | Yes\n**スタティック・メソッド** | \n非スタティックなメソッドのスタティックへの変更 | No\nスタティックなメソッドの非スタティックへの変更 | No\n**定数** | \t \n定数の追加 | Yes\n定数の削除 | No\n定数の値の変更 | シリアライズされる可能性のあるオブジェクトを除いて Yes。UPGRADE.md への記載が必須\n"
  },
  {
    "path": "docs/internals-ja/core-code-style.md",
    "content": "Yii 2 コア・フレームワーク・コード・スタイル\n=====================================\n\n下記のコード・スタイルが Yii 2.x コアと公式エクステンションの開発に用いられています。\nコアに対してコードをプル・リクエストをしたいときは、これを使用することを考慮してください。\n私たちは、あなたが自分のアプリケーションにこのコード・スタイルを使うことを強制するものではありません。あなたにとってより良いコード・スタイルを自由に選んでください。\n\nなお、CodeSniffer のための設定をここで入手できます: https://github.com/yiisoft/yii2-coding-standards\n\n## 1. 概要\n\n全体として、私たちは [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)\n互換のスタイルを使っていますので、\n[PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) に適用されることは、\nすべて私たちのコード・スタイルにも適用されます。\n\n- ファイルは `<?php` または `<?=` のタグを使わなければならない。\n- ファイルの末尾には一個の改行があるべきである。\n- ファイルは PHP コードに対して BOM 無しの UTF-8 だけを使わなければならない。\n- コードはインデントに、タブではなく、4個の空白を使わなければならない。\n- クラス名は `StudlyCaps` で宣言されなくてはならない。\n- クラス内の定数はアンダースコアで区切られた大文字だけで宣言されなければならない。\n- メソッド名は `camelCase` で宣言されなければならない。\n- プロパティ名は `camelCase` で宣言されなければならない。\n- プロパティ名は private である場合はアンダースコアで始まらなければならない。\n- `else if` ではなく常に `elseif` を使用すること。\n\n## 2. ファイル\n\n### 2.1. PHP タグ\n\n- PHP コードは `<?php ?>` または `<?=` タグを使わなければなりません。他のタグの変種、例えば `<?` を使ってはなりません。\n- ファイルが PHP コードのみを含む場合は、末尾の `?>` を含むべきではありません。\n- 各行の末尾に空白を追加してはいけません。\n- PHP コードを含む全てのファイルの名前は `.php` という拡張子で終るべきです。\n\n### 2.2. 文字エンコーディング\n\nPHP コードは BOM 無しの UTF-8 のみを使わなければなりません。\n\n## 3. クラス名\n\nクラス名は `StudlyCaps` で宣言されなければなりません。例えば、`Controller`、`Model`。\n\n## 4. クラス\n\nここで \"クラス\" という用語はあらゆるクラスとインタフェイスを指すものとします。\n\n- クラスは `CamelCase` で命名されなければなりません。\n- 中括弧は常にクラス名の下の行に書かれるべきです。\n- 全てのクラスは PHPDoc に従ったドキュメント・ブロックを持たなければなりません。\n- クラス内のすべてのコードは4個の空白によってインデントされなければなりません。\n- 一つの PHP ファイルにはクラスが一つだけあるべきです。\n- 全てのクラスは名前空間に属すべきです。\n- クラス名はファイル名と合致すべきです。クラスの名前空間はディレクトリ構造と合致すべきです。\n\n```php\n/**\n * Documentation\n */\nclass MyClass extends \\yii\\base\\BaseObject implements MyInterface\n{\n    // コード\n}\n```\n\n### 4.1. 定数\n\nクラスの定数はアンダースコアで区切られた大文字だけで宣言されなければなりません。\n例えば、\n\n```php\n<?php\nclass Foo\n{\n    const VERSION = '1.0';\n    const DATE_APPROVED = '2012-06-01';\n}\n```\n### 4.2. プロパティ\n\n- Public なクラス・メンバを宣言するときは `public` キーワードを明示的に指定します。\n- Public および protected な変数はクラスの冒頭で、すべてのメソッドの宣言に先立って宣言されるべきです。\n  Private な変数もまたクラスの冒頭で宣言されるべきですが、\n  変数がクラスのメソッドのごく一部分にのみ関係する場合は、変数を扱う一群のメソッドの直前に追加しても構いません。\n- クラスにおけるプロパティの宣言の順序は、その可視性に基づいて、 public から始まり、protected、private と続くべきです。\n- 同じ可視性を持つプロパティの順序については、厳格な規則はありません。\n- より読みやすいように、プロパティの宣言は空行を挟まずに続け、プロパティ宣言とメソッド宣言のブロック間には2行の空行を挟むべきです。\n  また、異なる可視性のグループの間に、1行の空行を追加するべきです。\n- Private 変数は `$_varName` のように名付けるべきです。\n- Public なクラス・メンバとスタンドアロンな変数は、\n  先頭を小文字にした `$camelCase` で名付けるべきです。\n- 説明的な名前を使うこと。`$i` や `$j` のような変数は使わないようにしましょう。\n\n例えば、\n\n```php\n<?php\nclass Foo\n{\n    public $publicProp1;\n    public $publicProp2;\n\n    protected $protectedProp;\n\n    private $_privateProp;\n\n\n    public function someMethod()\n    {\n        // ...\n    }\n}\n```\n\n### 4.3. メソッド\n\n- 関数およびメソッドは、先頭を小文字にした `camelCase` で名付けるべきです。\n- 名前は、関数の目的を示す自己説明的なものであるべきです。\n- クラスのメソッドは常に修飾子 `private`、`protected` または `public` を使って、可視性を宣言すべきです。\n  `var` は許可されません。\n- 関数の開始の中括弧は関数宣言の次の行に置くべきです。\n\n```php\n/**\n * Documentation\n */\nclass Foo\n{\n    /**\n     * Documentation\n     */\n    public function bar()\n    {\n        // コード\n        return $value;\n    }\n}\n```\n\n### 4.4 PHPDoc ブロック\n\n - `@param`、`@var`、`@property` および `@return` は `bool`、`int`、`string`、`array` または `null` として型を宣言しなければなりません。\n   `Model` または `ActiveRecord` のようなクラス名を使うことも出来ます。\n - 型付きの配列に対しては `ClassName[]` を使います。\n - PHPDoc の最初の行には、メソッドの目的を記述しなければなりません。\n - メソッドが何かをチェックする (たとえば、`isActive`, `hasClass` など) ものである場合は、最初の行は `Checks whether` で始まらなければなりません。\n - `@return` は、厳密に何が返されるのかを明示的に記述しなければなりません。\n\n```php\n/**\n * Checks whether the IP is in subnet range\n *\n * @param string $ip an IPv4 or IPv6 address\n * @param int $cidr the CIDR lendth\n * @param string $range subnet in CIDR format e.g. `10.0.0.0/8` or `2001:af::/64`\n * @return bool whether the IP is in subnet range\n */\n private function inRange($ip, $cidr, $range)\n {\n   // ...\n }\n```\n\n### 4.5 コンストラクタ\n\n- PHP 4 スタイルのコンストラクタの代りに、`__construct` が使われるべきです。\n\n## 5 PHP\n\n### 5.1 型\n\n- PHP の全ての型と値には小文字を使うべきです。このことは、`true`、`false`、`null` および `array` にも当てはまります。\n\n既存の変数の型を変えることは悪いプラクティスであると見なされています。本当に必要でない限り、そのようなコードを書かないように努めましょう。\n\n\n```php\npublic function save(Transaction $transaction, $argument2 = 100)\n{\n    $transaction = new Connection; // 駄目\n    $argument2 = 200; // 良い\n}\n```\n\n### 5.2 文字列\n\n- 文字列が変数および一重引用符を含まない場合は、一重引用符を使います。\n\n```php\n$str = 'こんな具合に。';\n```\n\n- 文字列が一重引用符を含む場合は、余計なエスケープを避けるために二重引用符を使ってもかまいません。\n\n#### 変数置換\n\n```php\n$str1 = \"Hello $username!\";\n$str2 = \"Hello {$username}!\";\n```\n\n下記は許可されません。\n\n```php\n$str3 = \"Hello ${username}!\";\n```\n\n#### 連結\n\n文字列を連結するときは、ドットの前後に空白を追加します。\n\n```php\n$name = 'Yii' . ' Framework';\n```\n\n文字列が長い場合、書式は以下のようにします。\n\n```php\n$sql = \"SELECT *\"\n    . \"FROM `post` \"\n    . \"WHERE `id` = 121 \";\n```\n\n### 5.3 配列\n\n配列には、私たちは PHP 5.4 の短縮構文を使用しています。\n\n#### 添え字配列\n\n- 負の数を配列のインデックスに使わないこと。\n\n配列を宣言するときは、下記の書式を使います。\n\n```php\n$arr = [3, 14, 15, 'Yii', 'Framework'];\n```\n\n一つの行には多過ぎるほど要素がたくさんある場合は、\n\n```php\n$arr = [\n    3, 14, 15,\n    92, 6, $test,\n    'Yii', 'Framework',\n];\n```\n\n#### 連想配列\n\n連想配列には下記の書式を使います。\n\n```php\n$config = [\n    'name' => 'Yii',\n    'options' => ['usePHP' => true],\n];\n```\n\n### 5.4 制御文\n\n- 制御文の条件は括弧の前と後に一つの空白を持たなければなりません。\n- 括弧の中の演算子は空白によって区切られるべきです。\n- 開始の中括弧は同じ行に置きます。\n- 終了の中括弧は新しい行に置きます。\n- 単一行の文に対しても、常に中括弧を使用します。\n\n```php\nif ($event === null) {\n    return new Event();\n}\nif ($event instanceof CoolEvent) {\n    return $event->instance();\n}\nreturn null;\n\n\n// 下記は許容されません\nif (!$model && null === $event)\n    throw new Exception('test');\n```\n\nそうしても意味が通じる場合は、`return` の後の `else` は避けてください。\n[ガード条件](https://refactoring.com/catalog/replaceNestedConditionalWithGuardClauses.html) を使用しましょう。\n\n```php\n$result = $this->getResult();\nif (empty($result)) {\n    return true;\n} else {\n    // $result を処理\n}\n```\n\nこれは、次の方が良いです。\n\n```php\n$result = $this->getResult();\nif (empty($result)) {\n    return true;\n}\n\n// $result を処理\n```\n\n#### switch\n\nswitch には下記の書式を使用します。\n\n```php\nswitch ($this->phpType) {\n    case 'string':\n        $a = (string) $value;\n        break;\n    case 'integer':\n    case 'int':\n        $a = (int) $value;\n        break;\n    case 'boolean':\n        $a = (bool) $value;\n        break;\n    default:\n        $a = null;\n}\n```\n\n### 5.5 関数呼び出し\n\n```php\ndoIt(2, 3);\n\ndoIt(['a' => 'b']);\n\ndoIt('a', [\n    'a' => 'b',\n    'c' => 'd',\n]);\n```\n\n### 5.6 無名関数 (lambda) の宣言\n\n`function`/`use` トークンと開始括弧の間の空白に注意してください。\n\n```php\n// 良い\n$n = 100;\n$sum = array_reduce($numbers, function ($r, $x) use ($n) {\n    $this->doMagic();\n    $r += $x * $n;\n    return $r;\n});\n\n// 悪い\n$n = 100;\n$mul = array_reduce($numbers, function($r, $x) use($n) {\n    $this->doMagic();\n    $r *= $x * $n;\n    return $r;\n});\n```\n\nドキュメント\n------------\n\n- ドキュメントの文法については [phpDoc](https://phpdoc.org/) を参照してください。\n- ドキュメントの無いコードは許容されません。\n- 全てのクラス・ファイルは、ファイル・レベルの doc ブロックを各ファイルの先頭に持ち、\n  クラス・レベルの doc ブロックを各クラスの直前に持たなければなりません。\n- メソッドが実際に何も返さないときは `@return` を使う必要はありません。\n- `yii\\base\\BaseObject` から派生するクラスのすべての仮想プロパティは、クラスの doc ブロックで\n  `@property` タグでドキュメントされます。\n  これらの注釈は、`build` ディレクトリで `./build php-doc` コマンドを走らせることにより、\n  対応する getter や setter の `@return` や `@param` タグから自動的に生成されます。\n  getter や setter に `@property` タグを追加することによって、これらのメソッドによって導入されるプロパティに対して\n  ドキュメントのメッセージを明示的に与えることが出来ます。\n  これは `@return` で記述されているのとは違う説明を与えたい場合に有用です。\n  下記が一例です。\n\n  ```php\n    <?php\n    /**\n     * Returns the errors for all attribute or a single attribute.\n     * @param string $attribute attribute name. Use null to retrieve errors for all attributes.\n     * @property array An array of errors for all attributes. Empty array is returned if no error.\n     * The result is a two-dimensional array. See [[getErrors()]] for detailed description.\n     * @return array errors for all attributes or the specified attribute. Empty array is returned if no error.\n     * Note that when returning errors for all attributes, the result is a two-dimensional array, like the following:\n     * ...\n     */\n    public function getErrors($attribute = null)\n  ```\n\n#### ファイル\n\n```php\n<?php\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n```\n\n#### クラス\n\n```php\n/**\n * Component is the base class that provides the *property*, *event* and *behavior* features.\n *\n * @include @yii/docs/base-Component.md\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Component extends \\yii\\base\\BaseObject\n```\n\n\n#### 関数 / メソッド\n\n```php\n/**\n * Returns the list of attached event handlers for an event.\n * You may manipulate the returned [[Vector]] object by adding or removing handlers.\n * For example,\n *\n * ```\n * $component->getEventHandlers($eventName)->insertAt(0, $eventHandler);\n * ```\n *\n * @param string $name the event name\n * @return Vector list of attached event handlers for the event\n * @throws Exception if the event is not defined\n */\npublic function getEventHandlers($name)\n{\n    if (!isset($this->_e[$name])) {\n        $this->_e[$name] = new Vector;\n    }\n    $this->ensureBehaviors();\n    return $this->_e[$name];\n}\n```\n\n#### Markdown\n\n上記の例に見られるように、phpDoc コメントの書式設定には markdown を使います。\n\nドキュメントの中でクラス、メソッド、プロパティをクロス・リンクするために使える追加の文法があります。\n\n- `[[canSetProperty]]` は、同じクラス内の `canSetProperty` メソッドまたはプロパティへのクロス・リンクを生成します。\n- `[[Component::canSetProperty]]` は、同じ名前空間内の `Component` クラスの `canSetProperty` メソッドへのクロス・リンクを生成します。\n- `[[yii\\base\\Component::canSetProperty]]` は、`yii\\base` 名前空間の`Component` クラスの `canSetProperty` メソッドへのクロス・リンクを生成します。\n- `[[Component]]` は、同じ名前空間内の `Component` クラスへのクロス・リンクを生成します。ここでも、クラス名に名前空間を追加することが可能です。\n\n上記のリンクにクラス名やメソッド名以外のラベルを付けるためには、次の例で示されている文法を使うことが出来ます。\n\n```\n... as displayed in the [[header|header cell]].\n```\n\n`|` の前の部分がメソッド、プロパティ、クラスへの参照であり、`|` の後ろの部分がリンクのラベルです。\n\n下記の文法を使ってガイドにリンクすることも可能です。\n\n```markdown\n[link to guide](guide:file-name.md)\n[link to guide](guide:file-name.md#subsection)\n```\n\n\n#### コメント\n\n- 一行コメントは `//` で開始されるべきです。`#` は使いません。\n- 一行コメントはそれ自体で一行を占めるべきです。\n\n追加の規則\n----------\n\n### `=== []` 対 `empty()`\n\n可能な場合は `empty()` を使います。\n\n### 複数の return ポイント\n\n条件の入れ子が込み入ってきた場合は、早期の return を使います。メソッドが短いものである場合は、特に問題にしません。\n\n### `self` 対 `static`\n\n以下の場合を除いて、常に `static` を使います。\n\n- 定数へのアクセスには `self` を使わなければなりません: `self::MY_CONSTANT`\n- private な静的プロパティへのアクセスには `self` を使わなければなりません: `self::$_events`\n- 再帰呼出しにおいて、拡張クラスの実装ではなく、現在のクラスの実装を再び呼び出したいときなど、合理的な理由がある場合には、`self` を使うことが許可されます。\n\n### 「何かをするな」を示す値\n\nコンポーネントに対して「何かをしない」という設定を許可するプロパティは `false` の値を受け入れるべきです。`null`、`''`、または `[]` がそういう値であると見なされるべきではありません。\n\n### ディレクトリ/名前空間の名前\n\n- 小文字を使います。\n- オブジェクトを表すものには複数形の名詞を使います (例えば、validators)。\n- 機能や特徴を表す名前には単数形を使います (例えば、web)。\n- 出来れば単一の語の名前空間にします。\n- 単一の語が適切でない場合は、camelCase を使います。\n\n"
  },
  {
    "path": "docs/internals-ja/design-decisions.md",
    "content": "設計上の決定\n============\n\nこの文書は、私たちが詳細な議論の末に達した設計上の決定を記載するものです。\n非常に強固な理由があるのでない限りは、これらの決定は一貫性のために守られなければなりません。\nこれらの決定に対するいかなる変更もコア開発者間の同意を得なければなりません。\n\n1. **[いかなる時にパス・エイリアスをサポートするか](https://github.com/yiisoft/yii2/pull/3079#issuecomment-40312268)**\n   構成情報にパス・エイリアスを使用することは非常に利便性が高いため、構成可能なプロパティに対してはパス・エイリアスをサポートすべきである。\n   その他の場合には、パス・エイリアスに対するサポートを制限すべきである。\n2. **いかなる時にメッセージを翻訳するか**\n   技術者でないエンド・ユーザに対して表示され、また、そういうユーザに対して意味を持つメッセージは翻訳されるべきである。\n   HTTP ステータス・メッセージ、コードに関する例外などは翻訳されるべきではない。\n   コンソール・メッセージは、エンコーディングとコード・ページの処理に困難が伴うため、常に英語で表示されるものとする。\n3. **[認証クライアントのサポートの追加](https://github.com/yiisoft/yii2/issues/1652)**\n   保守性を高めるために、コア・エクステンションには認証クライアントをこれ以上追加しない。\n   認証クライアントの追加はユーザ・エクステンションの形でなされるべきである。\n4. **クロージャを使うときは**、たとえ使用されないものがある場合でも、**渡されるすべてのパラメータをシグニチャに含める** ことが推奨される。\n   このようにすると、全ての情報が直接に見えるので、コードの修正やコピーがより容易になり、どのパラメータが実際に利用できるかをドキュメントで調べる必要がなくなる。\n   ([#6584](https://github.com/yiisoft/yii2/pull/6584), [#6875](https://github.com/yiisoft/yii2/issues/6875))\n5. データベース・スキーマでは **unsigned int より int** を使う。\n   int を使うと、PHP で整数として表現できるという利点がある。\n   unsigned の場合、32 bit システムでは、文字列を使って表現しなければならなくなる。\n   また、unsigned int はサイズを倍にするとはいうものの、そのような広大な数値空間を必要とするテーブルを持っている場合は、unsigned に頼るより bigint または mediumint を使用する方が安全である。\n   <https://github.com/yiisoft/yii/pull/1923#issuecomment-11881967>\n6. [ヘルパか、独立した非スタティックなクラスか](https://github.com/yiisoft/yii2/pull/12661#issuecomment-251599463)\n7. **セッター・メソッド・チェイニング** は、意味のある値を返すメソッドがそのクラスに存在する場合は、避けるべきである。\n   チェイニングは、クラスがビルダーであり、全てのセッターが内部状態を修正するものである場合にサポートされうる。https://github.com/yiisoft/yii2/issues/13026\n8. ローカルな try-catch の替りに **グローバルな例外/エラーハンドラ** が使われる。なぜなら、その方が、ブートストラップなどのように `run()` メソッドのスコープ外で発生するデストラクタなど全てのものをキャッチする点において信頼性が高いからである。[#14348](https://github.com/yiisoft/yii2/issues/14348) を参照。\n"
  },
  {
    "path": "docs/internals-ja/getting-started.md",
    "content": "Yii 2 の開発を始めよう\n=====================\n\n環境をセットアップする方法については、[Yii 2 寄稿者のための Git ワークフロー](git-workflow.md) を参照してください。\n"
  },
  {
    "path": "docs/internals-ja/git-workflow.md",
    "content": "Yii 2 寄稿者のための Git ワークフロー\n=====================================\n\nで、Yii の開発に貢献したい、と。すばらしい。 \nでも、あなたの修正案が速やかに採用されるチャンスを増やすために、以下のステップを踏むようにしてください。\nあなたが git と github については初めてだという場合は、最初に [github help](https://help.github.com/) や [try git](https://try.github.com) を精査したり、\n[git internal data model](https://nfarina.com/post/9868516270/git-is-simpler) についていくらか学習したりする必要があるかもしれません。\n\nあなたの開発環境を準備する\n--------------------------\n\n以下のステップを踏むと Yii の開発環境を作成し、それを使って Yii フレームワークのコア・コードに取り組むことが出来るようになります。\nこれらのステップは、あなたが最初に寄稿するときにだけ必要になります。\n\n### 1. github で Yii リポジトリを [Fork](https://help.github.com/fork-a-repo/) し、あなたのフォークをあなたの開発環境にクローンする\n\n```\ngit clone git@github.com:YOUR-GITHUB-USERNAME/yii2.git\n```\n\nLinux において、GitHub で GIT を設定するのに問題が生じたり、\"Permission Denied (publickey)\" のようなエラーが発生したりする場合は、\n[setup your GIT installation to work with GitHub](https://help.github.com/linux-set-up-git/) に従ってください。\n\n> Tip: あなたが Git に精通していない場合は、素晴らしい無料の [プロ Git ブック](https://git-scm.com/book/en/v2) を読むことをお勧めします。\n\n### 2. メインの Yii リポジトリを \"upstream\" という名前でリモートとして追加する\n\nYii をクローンしたディレクトリ、通常は \"yii2\" に入って、以下のコマンドを打ち込みます。\n\n```\ngit remote add upstream https://github.com/yiisoft/yii2.git\n```\n\n### 3. テスト環境を準備する <span id=\"prepare-the-test-environment\"></span\n\n以下のステップは、翻訳またはドキュメントだけに取り組みたい場合は、必要ではありません。\n\n- `composer install` を実行して、依存パッケージをインストールします ([composer をグローバルにインストール](https://getcomposer.org/doc/00-intro.md#globally) したものと仮定しています)。\n\nJavaScript を扱おうとしている場合は、\n\n- `npm install` を実行して JavaScript テスト・ツール群とその依存ライブラリをインストールします ([Node.js と NPM のインストール](https://nodejs.org/en/download/package-manager/) は完了しているものとします)。\n\n> Note: JavaScript のテストが依存している [jsdom](https://github.com/tmpvar/jsdom) ライブラリは、Node.js 4 以降を必要とします。\n  Node.js 6 または 7 を使用することをより強く推奨します。\n\n- `php build/build dev/app basic <fork>` を実行し、ベーシック・アプリケーションをクローンし、ベーシック・アプリケーションのための composer 依存パッケージをインストールします\n  ここで `<fork>` は、`git@github.com:my_nickname/yii2-app-basic.git` のような、あなたのレポジトリのフォークの URL です。\n  あなたがコア・フレームワークの貢献者である場合は、フォークの指定を省略しても構いません。このコマンドは外部 composer パッケージは通常どおりインストールしますが、yii2 レポジトリは現在チェックアウトされているものをリンクします。\n  これで、インストールされる全てのコードについて、一つのインスタンスを持つことになります。\n  \n  必要であれば、アドバンスト・アプリケーションについても同様にします: `php build/build dev/app advanced <fork>`。\n  \n  このコマンドは後日、依存パッケージを更新するためにも使用されます。このコマンドは内部的に `composer update` を実行します。\n\n> Note: デフォルトの git レポジトリの Url を使うため、SSH 経由で github からクローンすることになります。\n> `build` コマンドに `--useHttp` フラグを追加すれば、代りに HTTP を使うことが出来ます。\n\n**これであなたは Yii 2 をハックするための作業用の遊び場を手に入れました。**\n\n以下のステップはオプションです。\n\n### 単体テスト\n\nレポジトリのルート・ディレクトリで `phpunit` を実行することによって単体テストを実行することが出来ます。\nphpunit をグローバルにインストールしていない場合は、代りに `php vendor/bin/phpunit` を実行することが出来ます。\n\nテストによっては、データベースの設定と構成が必要なものがあります。\n`tests/data/config.local.php` を作成して、`tests/data/config.php` において構成されている設定を上書きすることが出来ます。\n\n取り組んでいるグループのものだけにテストを限定することが出来ます。例えば、バリデータと redis のためのテストだけを走らせるためには、`phpunit --group=validators,redis` とします。\n利用できるグループのリストを取得するためには、`phpunit --list-groups` を実行してください。\n\nJavaScript の単体テストは、レポジトリのルート・ディレクトリで `npm test` を走らせることによって実行することが出来ます。\n\n### エクステンション\n\nエクステンションに取り組むためには、エクステンションのレポジトリをクローンする必要があります。私たちは、あなたに代ってそれをするコマンドを作っています。\n\n```\nphp build/build dev/ext <extension-name> <fork>\n```\n\nここで `<extension-name>` がエクステンションの名前、例えば `redis` です。そして `<fork>` は、`git@github.com:my_nickname/yii2-redis.git` のような、あなたのエクステンションのフォークの URL です。あなたがコア・フレームワークの貢献者である場合は、フォークの指定を省略しても構いません。\n\nエクステンションをアプリケーション・テンプレートのどちらかでテストしたい場合は、通常そうするように、アプリケーションの `composer.json` にそれを追加するだけです。\n例えば、ベーシック・アプリケーションの `require` セクションに `\"yiisoft/yii2-redis\": \"~2.0.0\"` を追加します。\n`php build/build dev/app basic <fork>` を実行すると、エクステンションとその依存パッケージがインストールされ、`extensions/redis` に対するシンボリック・リンクが作成されます。\nこうすることで、composer の vendor ディレクトリではなく、直接に yii2 のレポジトリで作業をすることが出来るようになります。\n\n> Note: デフォルトの git レポジトリの Url を使うため、SSH 経由で github からクローンすることになります。\n> `build` コマンドに `--useHttp` フラグを追加すれば、代りに HTTP を使うことが出来ます。\n\n\nバグ修正と機能改良に取り組む\n----------------------------\n\n上記で説明したように開発環境の準備を完了すれば、機能改良やバグ修正の取り組みを開始することが出来るようになります。\n\n### 1. あなたが取り組もうとする問題が、修正するために著しい努力を要求するものである場合は、それに対する課題 (issue) が作成されていることを確認する\n\n全ての新機能とバグ修正は、議論とドキュメントのための単一の参照ポイントを提供するために、それに結びつく課題 (issue) を持つべきです。\n数分間時間を取って、既存の課題リストに目を通し、あなたが寄稿しようとしている問題に合致するものが無いかどうか調べてください。\nもし課題リストにすでに挙っているのを見つけた場合は、その課題にコメントを残して、あなたがその項目について取り組もうとしていることを示してください。\nあなたが取り組もうとしている問題に合致する既存の課題が見つからなかった場合は、[新しい課題を立て](report-an-issue.md) てください。\n単純な修正であれば、直接にプル・リクエストをしてください。\nこうすることで、開発チームはあなたの提案をレビューし、将来にわたって適切なフィードバックを提供することが可能になります。\n\n> 小さな変更や、ドキュメントの問題、または単純な修正については、課題を作成する必要はありません。それらについては、プル・リクエストだけで十分です。\n\n### 2. メインの Yii ブランチから最新のコードをプルする\n\n```\ngit pull upstream\n```\n\n最新のコードに対して作業することを保証するために、すべての新しい寄稿においてこのステップから作業を開始すべきです。\n\n### 3. 現在の Yii のマスター・ブランチに基いて、あなたの寄稿のための新しいブランチを作成する\n\n> これは非常に重要です。なぜなら、master ブランチを使うと、\n  あなたのアカウントからは一つ以上のプル・リクエストを送信することが出来なくなるからです。\n\n独立したバグ修正や変更は、各々、それ自身のブランチに入れるべきです。\nブランチの名前は説明的なものにし、あなたのコードが関係する課題の番号で始まるようにしてください。\n特定の課題を修正するものでない場合は、番号を省略してください。例えば、\n\n```\ngit checkout upstream/master\ngit checkout -b 999-name-of-your-branch-goes-here\n```\n\n### 4. あなたの魔法を使って、あなたのコードを書く\n\n動くことを確認してくださいね :)\n\n単体テストは常に歓迎されます。テストされ、十分にカバーされたコードは、あなたの寄稿をチェックするタスクを非常に単純化してくれます。\n単体テストの失敗を中身とする課題を立てることも許容されています。\n\n### 5. CHANGELOG を更新する\n\nCHANGELOG ファイルを編集して、あなたの修正を追加します。新しい行は、ファイル冒頭の最初の見出し (現在開発中のバージョン) の下に挿入してください。\nチェンジログの行は、下記のどちらかのように書いてください。\n\n```\nBug #999: バグ修正の内容説明 (あなたの名前)\nEnh #999: 機能拡張の内容説明 (あなたの名前)\n```\n\n`#999` は `Bug` または `Enh` が参照している課題番号です。\nチェンジ・ログは、タイプ (`Bug`, `Enh`) によってグループ化し、課題番号順に並べます。\n\n非常に小さな修正、すなわち、タイポやドキュメントの修正については、CHANGELOG を更新する必要はありません。\n\n### 6. 修正をコミットする\n\n以下のコマンドを使って、コミットしたいファイルや変更を [staging area](https://git.github.io/git-reference/basic/#add) に追加します。\n\n```\ngit add path/to/my/file.php\n```\n\n`-p` オプションを使って、コミットに含める変更を選択することも出来ます。\n\n説明的なコミット・メッセージを付けて修正をコミットしてください。\ngithub があなたのコミットを自動的にチケットとリンクするように、必ず `#XXX` でチケット番号に言及してください。\n\n```\ngit commit -m \"#999 を解決する変更の短い説明をここに入れる\"\n```\n\n### 7. 最新の Yii コードを upstream からあなたのブランチにプルする\n\n```\ngit pull upstream master\n```\n\nこのステップは、プル・リクエストを出す前にあなたのブランチが最新のコードを持っていることを確実にするためのものです。\nもし何かマージ衝突がある場合は、ここでそれを修正してから再度変更をコミットすべきです。\nこうすると、Yii 開発チームがワン・クリックであなたの変更をマージすることが確実に出来るようになります。\n\n### 8. 衝突をすべて解決したら、あなたのコードを github にプッシュする\n\n```\ngit push -u origin 999-name-of-your-branch-goes-here\n```\n\n`-u` パラメータによって、今後はあなたのそのブランチが github のブランチに対して自動的にプッシュおよびプルされるようになります。\nつまり、次回 `git push` とタイプしたときには、git は既にどこにプッシュすればよいか知っている、ということです。\nこうしておくと、後でプル・リクエストにコミットを追加したくなった場合に便利です。\n\n### 9. upstream に対して [プルリクエスト](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) を発行する\n\ngithub 上のあなたのリポジトリに入って、\"Pull Request\" をクリックし、右側にあるブランチを選び、\nコメント・ボックスにもう少し詳細を記述します。\nプル・リクエストを課題とリンクさせるために、プル・コメントのどこかに `#999` という書式で課題番号を記載します。\n\n> 全てのプル・リクエストはそれぞれ一つの課題を解決すべきものであることに注意してください。\n\n### 10. 誰かがあなたのコードをレビューする\n\n誰かがあなたのコードをレビューします。あなたは修正を求められるかも知れません。その場合は、ステップ #6 に戻ってください\n(現在のプル・リクエストが open である限りは、別の新しいプル・リクエストをする必要はありません)。あなたのコードが受け入れられた場合は、コードはメイン・ブランチにマージされて、次回の Yii のリリースの一部となります。\n受け入れられなくても、落胆しないでください。必要とする機能は人によってさまざまに異なりますし、Yii は全ての人に対して全てを提供することは出来ません。\nまた、却下された場合でも、あなたのコードはそれを必要とする人々から参照されるように github 上で公開され続けます。\n\n### 11. クリーンアップする\n\nあなたのコードが受け入れられるか却下されるかした後、あなたが作業してきたブランチをローカル・レポジトリおよび \n`origin` から削除することが出来ます。\n\n```\ngit checkout master\ngit branch -D 999-name-of-your-branch-goes-here\ngit push origin --delete 999-name-of-your-branch-goes-here\n```\n\n### 注意:\n\n退行 (regression) を早期に発見するために、github 上の Yii コード・ベースへのマージは、すべて [Travis CI](https://travis-ci.com) に取り上げられて、自動化されたテストにかけられます。\nコア・チームとしては、このサービスに過大な負担をかけたくないために、以下の場合にはマージの説明に \n[`[ci skip]`](https://docs.travis-ci.com/user/customizing-the-build/#Skipping-a-build) が含まれるようにしてください。\nすなわち、プル・リクエストが下記のものである場合がそうです。\n\n* javascript、css または画像ファイルだけに影響する場合\n* ドキュメントを更新する場合\n* 固定の文字列だけを修正する (例えば、翻訳をアップデートする) 場合\n\nこのようにすることによって、そもそもテストによってカバーされない変更に対しては、travis がテスト・ランを開始しないようにすることが出来ます。\n\n### コマンド概要 (上級の寄稿者向け)\n\n```\ngit clone git@github.com:YOUR-GITHUB-USERNAME/yii2.git\ngit remote add upstream https://github.com/yiisoft/yii2.git\n```\n\n```\ngit fetch upstream\ngit checkout upstream/master\ngit checkout -b 999-name-of-your-branch-goes-here\n\n/* 魔法を使い、必要なら changelog を更新 */\n\ngit add path/to/my/file.php\ngit commit -m \"A brief description of this change which fixes #999 goes here\"\ngit pull upstream master\ngit push -u origin 999-name-of-your-branch-goes-here\n```\n"
  },
  {
    "path": "docs/internals-ja/project-organization.md",
    "content": "プロジェクトの編成\n==================\n\nこのドキュメントは Yii 2 開発レポジトリの編成を説明するものです。\n \n1. 個々のコア・エクステンションとアプリケーション・テンプレートは、[yiisoft](https://github.com/yiisoft) Github オーガニゼーションの下の\n*独立した* 別の Github プロジェクトとして保守されます。\n    \n   エクステンションのレポジトリ名は、先頭に `yii2-` を付けます。例えば、`gii` エクステンションは `yii2-gii` です。\n   Composer のパッケージ名は Github レポジトリ名と同じで、例えば `yiisoft/yii2-gii` です。\n   \n   アプリケーション・テンプレートのレポジトリ名は、先頭に `yii2-app-` を付けます。例えば、`basic` アプリケーション・テンプレートは `yii2-app-basici` です。\n   Composer のパッケージ名は Github レポジトリ名と同じで、例えば `yiisoft/yii2-app-basic` です。\n   \n   各々のエクステンション/アプリケーションのプロジェクトは、\n \n   * \"docs\" フォルダにおいてそのチュートリアル・ドキュメントを保守します。API ドキュメントは、エクステンション/アプリケーションがリリースされるときにその場で生成されます。\n   * \"tests\" フォルダにおいてそれ自身のテスト・コードを保守します。\n   * それ自身のメッセージ翻訳やその他全ての関係するメタコードを保守します。\n   * 対応する Github プロジェクトによって、課題 (issue) を追跡します。\n      \n   エクステンションのレポジトリは、必要に応じて、個別にリリースされます。アプリケーション・テンプレートはフレームワークとともにリリースされます。\n   詳細は [バージョン・ポリシー](versions.md) を参照して下さい。\n\n2. `yiisoft/yii2` プロジェクトが、Yii 2 フレームワーク開発のためのメイン・レポジトリです。\n   このレポジトリは Composer パッケージ [yiisoft/yii2-dev](https://packagist.org/packages/yiisoft/yii2-dev) を提供します。\n   これは、コア・フレームワーク・コード、フレームワークの単体テスト、決定版ガイド、そして、フレームワーク開発とリリースのための一組のビルド・ツールを含んでいます。\n   \n   コア・フレームワークのバグと機能要望は、この Github プロジェクトのイッシュー・トラッカーによって追跡されます。\n   \n3. `yiisoft/yii2-framework` レポジトリは、開発プロジェクト・レポジトリの `framework` ディレクトリのリード・オンリーな git subsplit です。\n   このレポジトリが、フレームワークのインストールに使用される Composer 公式パッケージである\n   [yiisoft/yii2](https://packagist.org/packages/yiisoft/yii2) を提供します。\n\n4. 開発するときには、[build dev/app](git-workflow.md#prepare-the-test-environment) コマンドを使って、\n   アプリケーションとエクステンションを開発プロジェクトの構成に含めることが出来ます。\n"
  },
  {
    "path": "docs/internals-ja/pull-request-qa.md",
    "content": "プル・リクエストの品質保証\n==========================\n\nPR をマージできるか否かをチェックするときには、特に以下の基準が考慮されるべきです。\n\n- PR にリンクされている課題(イッシュー)が存在するか、または、PR がどのようなことを修正ないし追加しようとしているのかに関する十分な説明があること。\n- 単体テスト。必須ではありませんが、大いに歓迎されます。PR によって修正されるコードが無ければ失敗する、というテストであること。\n- CHANGELOG のエントリがあること。エントリは次のリリースのセクションに、課題のタイプと番号の順に書き入れます。\n  担当した者のニックネームがあること。\n- [コード・スタイル](core-code-style.md) および [ビュー・コード・スタイル](view-code-style.md) が OK であること。\n  これらは、マージされる際に、マージする者の判断に従って修正される場合があります。\n"
  },
  {
    "path": "docs/internals-ja/release.md",
    "content": "新しいバージョンのリリース\n==========================\n\nフレームワークのリリースを作成するのに必要とされる手順のリストは、時とともに長くなり、手作業で管理するのが困難になっています。\nそのため、どの手順も忘れられることが無いように、コマンドライン・ツールを作成しました。\n\nリリースの手順の概要\n--------------------\n\n- ...\n\nリリース・コマンド\n------------------\n\nリリースの手順は、フレームワークの開発レポジトリに含まれている [release コンソール・コマンド](../../build/controllers/ReleaseController.php)\nによって自動化されています。\n\nリリース・コマンドは、フレームワークの `build` ディレクトリに含まれている Yii\nアプリケーションを使って呼び出すことが出来ます。\n\n    ./build/build help release  # このコマンドをフレームワークのレポジトリのルートで実行します\n\n> Info: コマンドを `--dryRun` オプションを付けて実行すると、どのようになるかを見ることが出来ます。\n> このオプションを使うと、変更は何もなされず、どんなコミットやタグも生成されたり、プッシュされたりしません。\n\n### 必要条件\n\nリリース・コマンドは、[Git ワークフローのドキュメント](git-workflow.md#extensions) で紹介されている開発環境に依存しています。\nすなわち、アプリケーション・テンプレートは `/apps/` の下に配置されていなければならず、\nエクステンションは `/extensions/` の下に配置されていなければなりません。\nこの構成は `dev/app` および `dev/ext` のコマンドを使って作成することが推奨されます。\n\n例えば、エクステンションのインストールは:\n\n    ./build/build dev/ext authclient\n\nアプリケーションは:\n\n    ./build/build dev/app basic\n\nこのインストール方法によって、エクステンションが現在のレポジトリの状態と同じフレームワーク・コードを使用する事を\n保証することが出来ます。\n\n### バージョンの概要\n\nフレームワークとエクステンションのバージョンについて概要を把握したいときは、以下を実行することが出来ます。\n\n    ./build/build release/info\n\n全てのレポジトリのタグを取得するために `--update` を指定して実行し、最新の情報を取得することも出来ます。\n\n### リリースを作成する\n\nフレームワークのリリースの作成では、下記のコマンドの実行します (アプリケーションは常にフレームワークと一緒にリリースされます)。\n\n    ./build/build release framework\n    ./build/build release app-basic\n    ./build/build release app-advanced\n\nエクステンションのリリースの作成では、実行するコマンドは一つだけです (例えば、redis なら)\n\n    ./build/build release redis\n\nリリース・コマンドは、デフォルトでは、現在チェックアウトされているブランチを元に新しいマイナー・バージョンをリリースします。\nデフォルトと異なるバージョンをリリースするためには、`--version` オプションを使ってバージョンを指定する必要があります。例えば、\n`--version=2.1.0`, or `--version=2.1.0-beta`.\n\n\n#### 新しいメジャー・バージョン、例えば 2.1.0 をリリースする\n\n新しいメジャー・バージョンのリリースは、[バージョン規約](versions.md) で説明されているように、\nブランチの変更を伴います。\n以下は、`master` から派生した `2.1` ブランチ上で開発されている \n`2.1.0` バージョンをリリースする例を示すものです。\nリリース前においては `master` は `2.0.x` の諸バージョンを含んでいます。\n\n- `master` から新しいブランチ `2.0` を作成する\n- composer.json がこのブランチに対するブランチエイリアスを含まないようにする\n- 必要な変更を `master` から `2.1` にマージする\n- `master` が `2.1` の最新のコミットを指すようにする\n- composer.json のマスターに対するブランチ・エイリアスを `2.1.x-dev` とする\n- `2.1` ブランチを削除する\n\n`master` をチェックアウトし、`--version=2.1.0` オプションを付けて、リリース・コマンドを実行する。\n\n"
  },
  {
    "path": "docs/internals-ja/report-an-issue.md",
    "content": "課題を報告する\n==============\n\nあなたが報告する課題 (issue) がより迅速に解決されるように、課題を作成するときには下記のガイドラインに従って下さい。\n\n* PHP と Yii のバージョン、オペレーティング・システムとウェブ・サーバのタイプ、および、ブラウザのタイプとバージョンについて、情報を提供してください。\n* 出来れば、**完全な**エラーのコール・スタックを提供して下さい。問題を説明するスクリーン・ショットは大いに歓迎されます。\n* 問題を再現する手順を記述して下さい。問題を再現するコードを提供してくださるならば、なお一層良いでしょう。\n* 可能であれば、さらに進んで、失敗する単体テストを作成して [プル・リクエストを発行](git-workflow.md) してくださっても構いません。\n\n課題が公式エクステンションのどれかと関係がある場合は、エクステンションのレポジトリで課題を報告してください。\nエクステンションとの関係がよく分らない場合は、[メインのレポジトリに報告](https://github.com/yiisoft/yii2/issues/new) (<https://github.com/yiisoft/yii2/issues>) してください。\n\n**以下の場合は課題を報告しないでください**\n\n* Yii の何らかの機能の使い方に関する質問。この目的のためには、[フォーラム](https://forum.yiiframework.com/index.php/forum/42-general-discussions-for-yii-20/) または [チャット・ルーム](https://www.yiiframework.com/chat/) を使うべきです。\n* 問題がセキュリティに関するものである場合。セキュリティ問題を報告するときは、[開発者に直接コンタクト](https://www.yiiframework.com/security/) してください。\n\n**課題の重複を避ける**\n\n課題を報告する前に、[既存の課題](https://github.com/yiisoft/yii2/issues) を検索して、あなたが報告しようとしている課題が既に報告されていたり解決されていたりしないかを確かめ、重複した課題を報告しないようにしてください。さらにまた、Yii の最新のバージョンを使ってみて、それでもまだ問題が残っているかどうかを確かめてください。\n"
  },
  {
    "path": "docs/internals-ja/translation-status.md",
    "content": "翻訳ステータス\n==============\n\nすべてのドキュメントが翻訳可能な状態です。\n"
  },
  {
    "path": "docs/internals-ja/translation-teams.md",
    "content": "翻訳チーム\n==========\n\nブラジルのポルトガル語\n----------------------\n\n- **Davidson Alencar, [@davidsonalencar](https://github.com/davidsonalencar), davidson.t.i@gmail.com**\n- [@wbraganca](https://github.com/wbraganca)\n- Alan Michel Willms Quinot, [@alanwillms](https://github.com/alanwillms), dyulax@gmail.com\n\n中国語\n------\n\n- **Paris Qian Sen 东方孤思子,[@qiansen1386](https://github.com/qiansen1386),qiansen1386@gmail.com**\n- [@AbrahamGreyson 刘阳](https://github.com/AbrahamGreyson)\n- [@fmalee](https://github.com/fmalee)\n- [@funson86 花生](https://github.com/funson86)\n- [@ivantree 长兴苗木](https://github.com/ivantree)\n- [@netyum 未来](https://github.com/netyum)\n- [@riverlet 小河](https://github.com/riverlet)\n- [@yiichina 巡洋舰](https://github.com/yiichina)\n\nフィンランド語\n--------------\n\n- Jani Mikkonen, [@janisto](https://github.com/janisto), janisto@php.net\n\nドイツ語\n--------\n\n- Carsten Brandt, [@cebe](https://github.com/cebe), mail@cebe.cc\n\nイタリア語\n----------\n\n- Lorenzo Milesi, [@maxxer](https://github.com/maxxer), maxxer@yetopen.it\n\n日本語\n------\n\n- Nobuo Kihara 木原伸夫, [@softark](https://github.com/softark), softark@gmail.com\n- Tomoki Morita, [@jamband](https://github.com/jamband), tmsongbooks215@gmail.com\n- Hisateru Tanaka, [@tanakahisateru](https://github.com/tanakahisateru), tanakahisateru@gmail.com\n\nロシア語\n--------\n\n- **Alexander Makarov, [@samdark](https://github.com/samdark), sam@rmcreative.ru**\n- [@MUTOgen](https://github.com/MUTOgen)\n- [@prozacUa](https://github.com/prozacUa)\n\nスペイン語\n----------\n\n- Luciano Baraglia, [@lucianobaraglia](https://github.com/lucianobaraglia)\n- Marco Da Silva, [@markmarco16](https://github.com/markmarco16), markmarco16@gmail.com\n- Daniel Gómez Pan [@pana1990](https://github.com/pana1990), pana_1990@hotmail.com\n\nウクライナ語\n------------\n\n- **Alexandr Bordun [@borales](https://github.com/Borales), admin@yiiframework.com.ua**\n- Roman Bahatyi [@RichWeber](https://github.com/RichWeber), rbagatyi@gmail.com\n- Igor Zozulinskyi [@3y3ik](https://github.com/3y3ik)\n- Vadym Chenin [@vchenin](https://github.com/vchenin), vchenin@meta.ua\n"
  },
  {
    "path": "docs/internals-ja/translation-workflow.md",
    "content": "翻訳ワークフロー\n================\n\nYii は国際的なアプリケーションと開発者にとって役に立つように、数多くの言語に翻訳されています。\n貢献が大いに歓迎される主な領域はドキュメントとフレームワーク・メッセージです。\n\nフレームワーク・メッセージ\n--------------------------\n\nフレームワークは二種類のメッセージを持っています。一つは開発者向けの例外メッセージで、これは決して翻訳されません。\nもう一つはエンド・ユーザが実際に目にする検証エラーのようなメッセージです。\n\nメッセージの翻訳を開始するためには:\n\n1. `framework/messages/config.php` をチェックして、あなたの言語が `languages` のリストに載っていることを確認してください。\n   もし無ければ、あなたの言語をそこに追加します (リストをアルファベット順に保つことを忘れないでください)。\n   言語コードの形式は、例えば `ru` や `zh-CN` のように、\n   [IETF言語タグ](https://ja.wikipedia.org/wiki/IETF%E8%A8%80%E8%AA%9E%E3%82%BF%E3%82%B0) に従うべきです。\n2. `framework` に入って、`./yii message/extract @yii/messages/config.php --languages=<your_language>` を走らせます。\n3. `framework/messages/your_language/yii.php` のメッセージを翻訳します。ファイルは必ず UTF-8 エンコーディングを使って保存してください。\n4. [プル・リクエスト](git-workflow.md) をします。\n\nあなたの翻訳を最新状態に保つために、`./yii message/extract @yii/messages/config.php --languages=<your_language>` を再び走らせることが出来ます。\nこのコマンドは、変更のなかった箇所には触れることなく、自動的にメッセージを再抽出してくれます。\n\n翻訳ファイルの中で、配列の各要素は、メッセージ(キー)と翻訳(値)をあらわします。\n値が空文字列の場合は、メッセージは翻訳されないものと見なされます。\n翻訳が不要になったメッセージは、翻訳が一組の '@@' マークで囲まれます。\nメッセージ文字列は複数形書式とともに使うことが出来ます。詳細はガイドの [国際化](../guide-ja/tutorial-i18n.md) の節を参照してください。\n\nドキュメント\n------------\n\nドキュメントの翻訳は `docs/<original>-<language>` の下に置きます。\nここで `<original>` は、`guide` や `internals` などの元の文書の名前であり、`<language>` は文書の翻訳先の言語コードです。\n例えば、ロシア語のガイドの翻訳は `docs/guide-ru` です。\n\n初期の仕事が完了した後は、最新の翻訳以後に変更されたソース文書の箇所を取得するために、\n`build` ディレクトリにある専用のコマンドを使うことが出来ます。\n\n```\nphp build translation \"../docs/guide\" \"../docs/guide-ru\" \"Russian guide translation report\" > report_guide_ru.html\n```\n\nこのコマンドが composer に関して不平を言うようであれば、ソースのルート・ディレクトリで `composer install` を実行してください。\n\nドキュメントの文法の情報およびスタイル・ガイドに関して、[documentation_style_guide.md](../documentation_style_guide.md) を参照して下さい。\n"
  },
  {
    "path": "docs/internals-ja/versions.md",
    "content": "Yii バージョン規約\n==================\n\nこの文書は Yii のバージョン付与ポリシーを要約するものです。\n私たちの現在のバージョン付与戦略は、[Semantic Versioning](https://semver.org/) の一変種です。\n\nコア開発者チームの内部では、2.0.x リリースを後方互換に保つことが重要であることが、何度も強調されました。\nしかし、これは理想としての計画です。現実の世界では達成することは困難です。\n後方互換性とは何であるかについての詳細は、[後方互換性](bc.md) を参照して下さい。\n\n要約すれば、Yii 2 に対する私たちのバージョン付与ポリシーは次のようになります。\n\n## バージョン番号\n\nバージョン番号は `2.x.y.z` のフォーマットとします。ここで `z` が `0` であるリリースについては、`z` は省略可能です。\n\n将来出るであろう Yii バージョン 3 については、1.0 に対する 2.0 のようなものになると予想されるので、ここでは言及しません。\nこのようなことは、外的な技術進歩 (たとえば PHP の 5.0 から 5.4 へのアップグレード) によって、3年から5年に一度しか生じないものと予期しています。\n\n## `2.X.0`: メジャー・リリース\n\n後方互換性を損ないうる大きな機能追加と変更を含む、非後方互換なリリースです。\n以前のバージョンからのアップグレードは簡単ではないかも知れませんが、完全なアップグレードのガイドが提供されます。\n\n* 主として新機能とバグ修正を含む。\n* パッチ・リリースからマージされた小さな機能強化とバグ修正を含む。\n* `UPGRADE-2.X.md` ファイルに記録される非後方互換な変更を含みうる。\n* リリースのサイクルはだいたい12ヶ月またはそれ以上。\n* プレ・リリースが必要: `2.X.0-alpha`, `2.X.0-beta`, `2.X.0-rc`\n* 大きなニュース・リリースとマーケティング努力を必要とする。\n\n\n## `2.x.Y`: マイナー・リリース\n\nほぼ後方互換であるマイナー・リリースです。\n理想的には、後方互換性を損なわない変更だけを含ませたいと私たちは望んでいます。\nしかし、すべてを 100% 後方互換に保つことが常に可能である訳ではありませんので、アップグレードに関するノートが `UPGRADE.md` に記録されます。\n実際の運用では、2.0.x はより頻繁にリリースされるので、小さな機能改良の追加も行って、ユーザが新機能をより早く享受できるようにしています。\n\n* 主としてバグ修正と機能強化を含む。\n* 不安無しのアップグレードを保証するために、ほぼ後方互換でなければならない。\n  ごく少数の例外は許容されるが、その場合は `UPGRADE.md` に記録される。\n* リリースのサイクルは1～2ヶ月程度。\n* プレ・リリース (alpha, beta, RC) は不要。\n* 継続的に (少なくとも週に一回は手作業で) マスター・ブランチにマージ・バックされるべき。\n* ニュースによる広報を伴う。プロジェクト・サイトがアップデートされる。\n\n\n### `2.x.y.Z`: パッチ・リリース\n\nバグ修正のみを含む、100% 後方互換であるべき、パッチ・リリースです。\nニュースによる広報やプロジェクト・サイトのアップデートはしません (ただし、重大な/セキュリティの問題についての修正を含む場合は、別です)。\nこのリリースのプロセスはほぼ自動化されています。\n\n* バグ修正のみを含み、機能追加はしない\n* 不安無しのアップグレードを保証するために、100% 後方互換でなければならない。唯一の例外はセキュリティ問題で、その場合は後方互換性が破られることもある\n* リリースのサイクルは1～2週間程度\n* プレ・リリース (alpha, beta, RC) は不要\n* リリース時にマスター・ブランチにマージ・バックされなければならない\n\n\n## ブランチ規約\n\n* `master` ブランチは、現在の安定版メジャー・リリースの開発ブランチで、現在は `2.0.x` バージョンです。\n* 各メジャー・リリースは、そのバージョン番号の名前を持つブランチ上で開発されます。例えば、`2.1`。\n* メジャー・リリース `2.n` の準備が出来たら、`master` から `2.(n-1).x` と名付けられた保守ブランチを作成します。\n  例えば、バージョン `2.1.0` が安定版としてリリースされ、`master` 上で開発されるようになると、`2.0` ブランチが作成されます。\n  ([composer branch naming schema](https://getcomposer.org/doc/02-libraries.md#branches) を参照).\n* パッチ・リリースをマークするために `2.x.y.z` というタグと `2.x.y` ブランチを作成します。`0` は省略されます。\n* `2.n.x` 保守ブランチ上の変更は、継続的に `master` ブランチにマージ・バックされます。\n\n次の図は、各ブランチ上でのコミット履歴の経時的な変化を説明したものです。\n\n![ブランチ規約](versions-branches.png)\n\n\n## リリース\n\nフレームワークと公式エクステンションのプロジェクトは、お互いに独立にリリースされます。すなわち、フレームワークとエクステンションの間で、バージョン番号が異なることが予想されます。\nアプリケーション・テンプレートは、常に、フレームワークと同時にリリースされます。\n\n上記で説明されたリリース・サイクルは、コア・フレームワークにのみ適用されます。\nエクステンションは必要に応じてリリースされます。\nエクステンションは、何らバグ修正や機能拡張を受けずに、長期間にわたって新しいリリースを持たないことがあり得ます。\n"
  },
  {
    "path": "docs/internals-ja/view-code-style.md",
    "content": "Yii 2 ビュー・コード・スタイル\n==============================\n\n下記のコード・スタイルが Yii 2.x コアと公式エクステンションのビュー・ファイルに用いられています。私たちは、あなたが自分のアプリケーションにこのコード・スタイルを使うことを強制するものではありません。あなたにとってより良いコード・スタイルを自由に選んでください。\n\n```php\n<?php\n// 冒頭の PHP タグは全てのテンプレート・ファイルで不可欠。冒頭のタグに続く空行も同じく必須。\n\n// コントローラから渡される入力変数をここで説明。\n/**\n * @var \\yii\\base\\View $this\n * @var \\yii\\widgets\\ActiveForm $form\n * @var \\app\\models\\Post[] $posts\n * @var \\app\\models\\ContactMessage $contactMessage\n */\n// 下の空行は必要。\n\n// 名前空間に属するクラスの宣言。\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n// 下の空行は必要。\n\n// コンテキストのプロパティを設定したり、コンテキストのセッターを呼んだり、その他のことをする。\n$this->title = 'Posts';\n?>\n<!-- foreach、for, if などには、独立した PHP ブロックを使う方が良い -->\n<?php foreach ($posts as $post): ?>\n    <!-- インデントのレベルに注目 -->\n    <h2><?= Html::encode($post['title']) ?></h2>\n    <p><?= Html::encode($post['shortDescription']) ?></p>\n<!-- 複数の PHP ブロックが使われる場合にそなえて、`}` ではなく、`endforeach;`、`endfor;`、`endif;` などを使う -->\n<?php endforeach; ?>\n\n<!-- ウィジェットの宣言は複数のコード行に分かれても良いし、分かれなくても良い -->\n<?php $form = ActiveForm::begin([\n    'options' => ['id' => 'contact-message-form'],\n    'fieldConfig' => ['inputOptions' => ['class' => 'common-input']],\n]); ?>\n    <!-- インデントのレベルに注目 -->\n    <?= $form->field($contactMessage, 'name')->textInput() ?>\n    <?= $form->field($contactMessage, 'email')->textInput() ?>\n    <?= $form->field($contactMessage, 'subject')->textInput() ?>\n    <?= $form->field($contactMessage, 'body')->textArea(['rows' => 6]) ?>\n\n    <div class=\"form-actions\">\n        <?= Html::submitButton('Submit', ['class' => 'common-button']) ?>\n    </div>\n<!-- ウィジェットの終了の呼び出しは、独立した PHP タグを持つべき -->\n<?php ActiveForm::end(); ?>\n<!-- 末尾の改行文字は必須 -->\n\n```\n"
  },
  {
    "path": "docs/internals-pl/README.md",
    "content": "Dokumentacja dewelopera Yii\n===========================\n\nTen folder zawiera dokumentację dotyczącą procesu rozwoju i powstawania nowych wydań frameworka Yii.\n\nWskazówki dla kontrybutorów\n---------------------------\n\n- [W jaki sposób zgłaszać problemy](report-an-issue.md)\n- [Od czego zacząć](getting-started.md)\n- [Cykl produkcji Git dla kontrybutorów Yii 2](git-workflow.md) - przewodnik krok po kroku jak utworzyć środowisko deweloperskie i współtworzyć Yii.\n- [Styl kodowania bazowych plików frameworka Yii 2](core-code-style.md)\n- [Styl kodowania widoków Yii 2](view-code-style.md)\n\n\nDokumentacja\n------------\n\n- [Status tłumaczeń](translation-status.md) - które z dokumentów są gotowe do przetłumaczenia.\n- [Ekipy tłumaczące](translation-teams.md)\n- [Cykl tworzenia tłumaczenia](translation-workflow.md)\n\n\nRozwój frameworka\n-----------------\n\n- [Zapewnienie jakości kodu w prośbie o połączenie](pull-request-qa.md)\n- [Zadania zautomatyzowane](automation.md), takie jak poprawki stylu kodu, automatyczne tworzenie dokumentacji i generowanie plików.\n- [Decyzje projektowe](design-decisions.md) - lista pytań i odpowiedzi dotyczących często rozważanych zagadnień.\n\nWersjonowanie i wydania\n-----------------------\n\n- [Organizacja projektu](project-organization.md)\n- [Wersjonowanie Yii](versions.md)\n- [Wsteczna kompatybilność](bc.md)\n- [Wydawanie nowej wersji](release.md)\n\nInne zagadnienia\n----------------\n\n### Hierarchia wyjątków\n\n![Hierarchia wyjątków frameworka Yii](exception_hierarchy.png)\n"
  },
  {
    "path": "docs/internals-pl/automation.md",
    "content": "Zadania zautomatyzowane\n=======================\n\nIstnieją zadania wykonywane automatycznie podczas pracy nad Yii:\n\n- Generowanie mapy klas `classes.php` umieszczonej w folderze głównym frameworka.\n  Uruchom `./build/build classmap`, aby ją wygenerować.\n\n- Generowanie notacji `@property` w plikach klas, które opisują właściwości wprowadzane przez gettery i settery.\n  Uruchom `./build/build php-doc/property`, aby je zaktualizować.\n\n- Poprawianie stylu kodu i innych drobnych kwestii w komentarzach phpdoc.\n  Uruchom `./build/build php-doc/fix`, aby je poprawić.\n  Sprawdź efekty poprawek, zanim je zatwierdzisz, ponieważ mogą wystąpić pewne niechciane modyfikacje (proces nie jest doskonały).\n  Możesz użyć `git add -p`, aby przejrzeć zmiany.\n\n- Uaktualnianie magicznego pliku z typami Mime (`framework/helpers/mimeTypes.php`) z repozytorium Apache HTTPd.\n  Uruchom `./build/build mime-type`, aby uaktualnić plik.\n  \n- Korekta porządku wpisów w pliku CHANGELOG może być przeprowadzona poprzez uruchomienie `./build/build release/sort-changelog framework`.\n\nWszystkie powyższe komendy są uruchamiane w [procesie wydawania nowej wersji](release.md). \nMogą być też uruchomione pomiędzy wydaniami, ale nie jest to konieczne.\n"
  },
  {
    "path": "docs/internals-pl/bc.md",
    "content": "# Wsteczna kompatybilność\n\nAbsolutnie nie łamiemy wstecznej kompatybilności w wydaniach - łatach typu `2.x.y.Z` i staramy się unikać koniecznych do wprowadzenia \nzmian niekompatybilnych wstecznie w pomniejszych wydaniach typu `2.x.Y`.\n\nZapoznaj się z sekcją [Wersjonowanie Yii](versions.md), aby dowiedzieć się więcej o numerowaniu wersji. \n\n## Metodyka użytkowania\n\n### Interfejsy\n\nPrzypadek użycia | Łamie kompatybilność wsteczną?\n-----------------|-------------------------------\nInformacja o zwracanym typie w intefejsie | Tak\nWywołanie metody interfejsu | Tak\n**Implementacja interfejsu i ...** |\nImplementacja metody | Tak\nDodanie argumentu do implementowanej metody | Tak\nDodanie domyślnej wartości do argumentu | Tak\n\n### Klasy\n\nPrzypadek użycia | Łamie kompatybilność wsteczną?\n-----------------|-------------------------------\nInformacja o zwracanym typie w klasie | Tak\nTworzenie nowej instancji | Tak\nRozszerzenie klasy | Tak\nOdwołanie do publicznej właściwości | Tak\nWywołanie publicznej metody | Tak\n**Rozszerzenie klasy i ...** |\nOdwołanie do chronionej właściwości\t| Tak\nWywołanie chronionej metody\t| Tak\nPrzeciążenie publicznej właściwości | Tak\nPrzeciążenie chronionej właściwości | Tak\nPrzeciążenie publicznej metody | Tak\nPrzeciążenie chronionej metody | Tak\nDodanie nowej właściwości | Nie\nDodanie nowej metody | Nie\nDodanie argumentu do przeciążonej metody | Tak\nDodanie domyślnej wartości do argumentu | Tak\nWywołanie prywatnej metody (przez Refleksję) | Nie\nOdwołanie do prywatnej właściwości (przez Refleksję) | Nie\n\n\n## Metodyka rozwoju\n\n### Zmiana interfejsów\n\nPrzypadek użycia | Łamie kompatybilność wsteczną?\n-----------------|-------------------------------\nUsunięcie | Nie\nZmiana nazwy lub przestrzeni nazw | Nie\nDodanie interfejsu - rodzica | Tak, jeśli nie ma dodanych nowych metod\nUsunięcie interfejsu - rodzica | Nie\n**Metody interfejsu** | \nDodanie metody | Nie\nUsunięcie metody | Nie\nZmiana nazwy | Nie\nPrzeniesienie do interfejsu - rodzica | Tak\nDodanie argumentu bez domyślnej wartości | Nie\nDodanie argumentu z domyślną wartością | Nie\nUsunięcie argumentów | Tak (tylko ostatnich)\nDodanie domyślnej wartości do argumentu | Nie\nUsunięcie domyślnej wartości z argumentu | Nie\nDodanie informacji o typie argumentu | Nie\nUsunięcie informacji o typie argumentu | Nie\nZmiana typu argumentu | Nie\nZmiana typu zwracanej wartości | Nie\n**Stałe** |\t \nDodanie stałej | Tak\nUsunięcie stałej | Nie\nZmiana wartości stałej | Tak, z wyjątkiem obiektów, które będą serializowane. Obowiązkowa dokumentacja w UPGRADE.md.\n\n### Klasy\n\nPrzypadek użycia | Łamie kompatybilność wsteczną?\n-----------------|-------------------------------\nUsunięcie | Nie\nOkreślenie jako final | Nie\nOkreślenie jako abstract | Nie\nZmiana nazwy lub przestrzeni nazw | Nie\nZmiana klasy - rodzica | Tak, ale oryginalna klasa - rodzic musi pozostać przodkiem klasy.\nDodanie interfejsu | Tak\nUsunięcie interfejsu | Nie\n**Publiczne właściwości** | \nDodanie publicznej właściwości | Tak\nUsunięcie publicznej właściwości | Nie\nOgraniczenie widoczności | Nie\nPrzeniesienie do klasy - rodzica | Tak\n**Chronione właściwości** | \t \nDodanie chronionej właściwości | Tak\nUsunięcie chronionej właściwości | Nie\nOgraniczenie widoczności | Nie\nPrzeniesienie do klasy - rodzica | Tak\n**Prywatne właściwości** | \nDodanie prywatnej właściwości | Tak\nUsunięcie prywatnej właściwości | Tak\n**Konstruktory** | \nUsunięcie konstruktora | Nie\nOgraniczenie widoczności publicznego konstruktora | Nie\nOgraniczenie widoczności chronionego konstruktora | Nie\nPrzeniesienie do klasy - rodzica | Tak\n**Publiczne metody** |\nDodanie publicznej metody | Tak\nUsunięcie publicznej metody | Nie\nZmiana nazwy | Nie\nOgraniczenie widoczności | Nie\nPrzeniesienie do klasy - rodzica | Tak\nDodanie argumentu bez domyślnej wartości | Nie\nDodanie argumentu z domyślną wartością | Nie\nUsunięcie argumentów | Tak, tylko ostatnich\nDodanie domyślnej wartości do argumentu | Nie\nUsunięcie domyślnej wartości z argumentu | Nie\nDodanie informacji o typie argumentu | Nie\nUsunięcie informacji o typie argumentu | Nie\nZmiana typu argumentu | Nie\nZmiana typu zwracanej wartości | Nie\n**Chronione metody** | \t \nDodanie chronionej metody | Tak\nUsunięcie chronionej metody | Nie\nZmiana nazwy | Nie\nOgraniczenie widoczności | Nie\nPrzeniesienie do klasy - rodzica | Tak\nDodanie argumentu bez domyślnej wartości | Nie\nDodanie argumentu z domyślną wartością | Nie\nUsunięcie argumentów | Tak, tylko ostatnich\nDodanie domyślnej wartości do argumentu | Nie\nUsunięcie domyślnej wartości z argumentu | Nie\nDodanie informacji o typie argumentu | Nie\nUsunięcie informacji o typie argumentu | Nie\nZmiana typu argumentu | Nie\nZmiana typu zwracanej wartości | Nie\n**Prywatne metody** | \t \nDodanie prywatnej metody | Tak\nUsunięcie prywatnej metody | Tak\nZmiana nazwy | Tak\nDodanie argumentu bez domyślnej wartości | Tak\nDodanie argumentu z domyślną wartością | Tak\nUsunięcie argumentu | Tak\nDodanie domyślnej wartości do argumentu | Tak\nUsunięcie domyślnej wartości z argumentu | Tak\nDodanie informacji o typie argumentu | Tak\nUsunięcie informacji o typie argumentu | Tak\nZmiana typu argumentu | Tak\nZmiana typu zwracanej wartości | Tak\n**Statyczne metody** | \nZmiana niestatycznej metody w statyczną | Nie\nZmiana statycznej metody w niestatyczną | Nie\n**Stałe** | \t \nDodanie stałej | Tak\nUsunięcie stałej | Nie\nZmiana wartości stałej | z wyjątkiem obiektów, które będą serializowane. Obowiązkowa dokumentacja w UPGRADE.md."
  },
  {
    "path": "docs/internals-pl/core-code-style.md",
    "content": "Styl kodowania bazowych plików frameworka Yii 2\n===============================================\n\nPoniższy styl kodowania jest stosowany w kodzie frameworka Yii 2.x i oficjalnych rozszerzeniach. Jeśli planujesz wysłać prośbę\no dołączenie kodu do bazowego frameworka, powinieneś rozważyć stosowanie takiego samego stylu. Nie zmuszamy jednak nikogo do\nstosowania go we własnych aplikacjach. Wybierz styl, który najbardziej odpowiada Twoim potrzebom.\n\nMożesz pobrać gotową konfigurację dla CodeSniffera pod adresem: https://github.com/yiisoft/yii2-coding-standards\n\n## 1. Omówienie\n\nUżywamy przede wszystkim standardu kodowania\n[PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md), zatem wszystko, co dotyczy\n[PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) dotyczy również naszego stylu\nkodowania.\n\n- Pliki MUSZĄ używać tagów `<?php` albo `<?=`.\n- Na końcu pliku powinna znajdować się pusta linia.\n- Pliki MUSZĄ używać kodowania UTF-8 bez znacznika BOM dla kodu PHP.\n- Kod MUSI używać 4 spacji do wcięć, a nie tabulatorów.\n- Nazwy klas MUSZĄ być zadeklarowane w formacie `StudlyCaps`.\n- Stałe w klasach MUSZĄ być zadeklarowane wyłącznie wielimi literami z łącznikiem w postaci podkreślnika.\n- Nazwy metod klasy MUSZĄ być zadeklarowane w formacie `camelCase`.\n- Nazwy właściwości klasy MUSZĄ być zadeklarowane w formacie `camelCase`.\n- Nazwy właściwości klasy MUSZĄ zaczynać się podkreślnikiem, jeśli są prywatne.\n- Należy używać `elseif` zamiast `else if`.\n\n## 2. Pliki\n\n### 2.1. Tagi PHP\n\n- Kod PHP MUSI używać tagów `<?php ?>` lub `<?=`; NIE MOŻE używać innych tagów takich jak `<?`.\n- W przypadku, gdy plik zawiera tylko kod PHP, nie powinien kończyć się tagiem `?>`.\n- Nie należy dodawać spacji na końcach linii.\n- Nazwa każdego pliku zawierającego kod PHP powinna kończyć się rozszerzeniem `.php`.\n\n### 2.2. Kodowanie znaków\n\nKod PHP MUSI używać wyłącznie UTF-8 bez znacznika BOM.\n\n## 3. Nazwy klas\n\nNazwy klas MUSZĄ być zadeklarowane w formacie `StudlyCaps`. Przykładowo `Controller`, `Model`.\n\n## 4. Klasy\n\nTermin \"klasa\" odnosi się tutaj do wszystkich klas i interfejsów.\n\n- Klasy powinny być nazwane w formacie `CamelCase`.\n- Otwierający nawias klamrowy powinien zawsze pojawić się w linii pod nazwą klasy.\n- Każda klasa musi posiadać blok dokumentacji dostosowany do składni PHPDoc.\n- Kod klasy musi być wcięty za pomocą 4 spacji.\n- W pojedynczym pliku PHP powinna znajdować się tylko jedna klasa.\n- Wszystkie klasy powinny być zadeklarowane w przestrzeni nazw.\n- Nazwa klasy powinna odpowiadać nazwie pliku. Przestrzeń nazw klasy powinna odpowiadać strukturze folderów.\n\n```php\n/**\n * Dokumentacja\n */\nclass MyClass extends \\yii\\base\\BaseObject implements MyInterface\n{\n    // kod\n}\n```\n\n### 4.1. Stałe\n\nStałe klasy MUSZĄ być zadeklarowane wyłącznie wielkimi literami z łącznikiem w postaci podkreślnika.\nDla przykładu:\n\n```php\n<?php\nclass Foo\n{\n    const VERSION = '1.0';\n    const DATE_APPROVED = '2012-06-01';\n}\n```\n### 4.2. Właściwości\n\n- Deklarując publiczne elementy klasy należy używać wprost słowa kluczowego `public`.\n- Publiczne i chronione zmienne powinny być zadeklarowane na początku klasy, przed deklaracjami metod.\n  Prywatne zmienne również powinny być zadeklarowane na początku klasy, ale mogą być również dodane zaraz przed metodami,\n  które ich używają w przypadku, gdy są stosowane tylko w kilku z nich.\n- Kolejność deklaracji właściwości w klasie powinna być rosnąca według ich widoczności: od publicznych, przez chronione, do prywatnych.\n- Nie ma ścisłych zasad dotyczących kolejności właściwości o tej samej widoczności.\n- Dla zapewnienia lepszej czytelności kodu, nie powinno być żadnych pustych linii pomiędzy deklaracjami właściwości, a sekcje\n  deklaracji właściwości i metod klasy powinny być rozdzielona dwoma pustymi liniami. Pojedyncza pusta linia powinna być dodana\n  pomiędzy grupami o różnej widoczności.\n- Prywatne zmienne powinny być nazwane w formacie `$_varName`.\n- Publiczne elementy klasy i niezależne zmienne powinny być nazwane w formacie `$camelCase` z pierwszą literą małą.\n- Należy używać opisowych nazw. Należy unikać używania zmiennych takich jak `$i` i `$j`.\n\nPrzykładowo:\n\n```php\n<?php\nclass Foo\n{\n    public $publicProp1;\n    public $publicProp2;\n\n    protected $protectedProp;\n\n    private $_privateProp;\n\n\n    public function someMethod()\n    {\n        // ...\n    }\n}\n```\n\n### 4.3. Metody\n\n- Funkcje i metody klasy powinny być nazwane w formacie `camelCase` z pierwszą literą małą.\n- Nazwy powinny być opisowe i wskazywać cel istnienia funkcji.\n- Metody klasy powinny zawsze deklarować widoczność używając modyfikatorów `private`, `protected` i `public`. `var` nie jest dozwolony.\n- Otwierający nawias klamrowy funkcji powinien znajdować się w linii pod jej deklaracją.\n\n```php\n/**\n * Dokumentacja\n */\nclass Foo\n{\n    /**\n     * Dokumentacja\n     */\n    public function bar()\n    {\n        // code\n        return $value;\n    }\n}\n```\n\n### 4.4 Bloki PHPDoc\n\n - `@param`, `@var`, `@property` i `@return` muszą deklarować użyte typy `bool`, `int`, `string`, `array` lub `null`.\n   Można również używać nazw klas jak `Model` lub `ActiveRecord`.\n - Dla typowanych tablic należy używać `ClassName[]`.\n - Pierwsza linia bloku PHPDoc musi opisywać cel istnienia metody.\n - Jeśli metoda sprawdza cokolwiek (`isActive`, `hasClass`, itp.) pierwsza linia powinna zaczynać się od `Checks whether`.\n - `@return` powinien wyraźnie opisywać co jest zwracane.\n\n```php\n/**\n * Checks whether the IP is in subnet range\n *\n * @param string $ip an IPv4 or IPv6 address\n * @param int $cidr the CIDR lendth\n * @param string $range subnet in CIDR format e.g. `10.0.0.0/8` or `2001:af::/64`\n * @return bool whether the IP is in subnet range\n */\n private function inRange($ip, $cidr, $range)\n {\n   // ...\n }\n```\n\n### 4.5 Konstruktory\n\n- Należy używać `__construct` zamiast konstruktorów w stylu PHP 4.\n\n## 5 PHP\n\n### 5.1 Typowanie\n\n- Wszystkie typy i wartości PHP powinny być zapisywane małymi literami, łącznie z `true`, `false`, `null` i `array`.\n\nZmiana typu istniejącej zmiennej jest uznawana za złą praktykę. Należy unikać pisania kodu w ten sposób, chyba że jest to naprawdę konieczne.\n\n```php\npublic function save(Transaction $transaction, $argument2 = 100)\n{\n    $transaction = new Connection; // źle\n    $argument2 = 200; // dobrze\n}\n```\n\n### 5.2 Łańcuchy znaków\n\n- Jeśli łańcuch znaków nie zawiera zmiennych lub pojedynczych cudzysłowów, należy używać pojedynczych cudzysłowów.\n\n```php\n$str = 'Like this.';\n```\n\n- Jeśli łańcuch znaków zawiera pojedyncze cudzysłowy, można użyć podwójnych cudzysłowów, aby uniknąć dodatkowego pomijania (znaku ucieczki).\n\n#### Zastępowanie zmiennych\n\n```php\n$str1 = \"Hello $username!\";\n$str2 = \"Hello {$username}!\";\n```\n\nPoniższy zapis jest niedozwolony:\n\n```php\n$str3 = \"Hello ${username}!\";\n```\n\n#### Konkatenacja\n\nNależy dodać spacje przed i po kropce podczas łączenia łańcuchów:\n\n```php\n$name = 'Yii' . ' Framework';\n```\n\nW przypadku długich łańcuchów format jest następujący:\n\n```php\n$sql = \"SELECT *\"\n    . \"FROM `post` \"\n    . \"WHERE `id` = 121 \";\n```\n\n### 5.3 Tablice\n\nW przypadku tablic używamy krótkiej składni PHP 5.4.\n\n#### Indeksowane numerycznie\n\n- Nie należy używać ujemnych liczb jako indeksów tablicy.\n\nNależy używać następującego formatu podczas deklarowania tablicy:\n\n```php\n$arr = [3, 14, 15, 'Yii', 'Framework'];\n```\n\nW przypadku, gdy ilość elementów jest zbyt duża dla pojedynczej linii:\n\n```php\n$arr = [\n    3, 14, 15,\n    92, 6, $test,\n    'Yii', 'Framework',\n];\n```\n\n#### Asocjacyjne\n\nNależy używać następującego formatu podczas deklarowania tablicy asocjacyjnej:\n\n```php\n$config = [\n    'name' => 'Yii',\n    'options' => ['usePHP' => true],\n];\n```\n\n### 5.4 Instrukcje kontrolne\n\n- Warunkowe instrukcje kontrolne muszą mieć pojedynczą spację przed i po nawiasie.\n- Operatory wewnątrz nawiasów powinny być oddzielone spacjami.\n- Otwierający nawias klamrowy powinien znajdować się w tej samej linii.\n- Zamykający nawias klamrowy powinien znajdować się w nowej linii.\n- Należy zawsze używać nawiasów klamrowych, nawet dla pojedynczych instrukcji.\n\n```php\nif ($event === null) {\n    return new Event();\n}\nif ($event instanceof CoolEvent) {\n    return $event->instance();\n}\nreturn null;\n\n\n// poniższy zapis jest NIEDOZWOLONY:\nif (!$model && null === $event)\n    throw new Exception('test');\n```\n\nNależy unikać stosowania `else` po `return`, kiedy ma to sens.\nNależy używać [guard conditions](https://refactoring.com/catalog/replaceNestedConditionalWithGuardClauses.html).\n\n```php\n$result = $this->getResult();\nif (empty($result)) {\n    return true;\n} else {\n    // przetwarzanie wyniku\n}\n```\n\nwygląda lepiej w postaci\n\n```php\n$result = $this->getResult();\nif (empty($result)) {\n   return true;\n}\n\n// przetwarzanie wyniku\n```\n\n#### Instrukcja switch\n\nNależy używać następującego formatu dla instrukcji switch:\n\n```php\nswitch ($this->phpType) {\n    case 'string':\n        $a = (string) $value;\n        break;\n    case 'integer':\n    case 'int':\n        $a = (int) $value;\n        break;\n    case 'boolean':\n        $a = (bool) $value;\n        break;\n    default:\n        $a = null;\n}\n```\n\n### 5.5 Wywołania funkcji\n\n```php\ndoIt(2, 3);\n\ndoIt(['a' => 'b']);\n\ndoIt('a', [\n    'a' => 'b',\n    'c' => 'd',\n]);\n```\n\n### 5.6 Deklaracje funkcji anonimowych (lambdy)\n\nNależy zwrócić uwagę na spację pomiędzy słowem `function`/`use` a otwierającym nawiasem:\n\n```php\n// dobrze\n$n = 100;\n$sum = array_reduce($numbers, function ($r, $x) use ($n) {\n    $this->doMagic();\n    $r += $x * $n;\n    return $r;\n});\n\n// źle\n$n = 100;\n$mul = array_reduce($numbers, function($r, $x) use($n) {\n    $this->doMagic();\n    $r *= $x * $n;\n    return $r;\n});\n```\n\nDokumentacja\n------------\n\n- Należy stosować dokumentację zgodnie ze składnią [phpDoc](https://phpdoc.org/).\n- Kod bez dokumentacji jest niedozwolony.\n- Każdy plik klasy musi zawierać blok dokumentacji \"poziomu pliku\" na początku pliku i blok dokumentacji \"poziomu klasy\"\n  zaraz nad klasą.\n- Nie ma konieczności używania `@return`, jeśli metoda niczego nie zwraca.\n- Wszystkie wirtualne właściwości w klasach, które rozszerzają `yii\\base\\BaseObject` są udokumentowane za pomocą tagu `@property`\n  w bloku dokumentacji klasy.\n  Adnotacje te są automatycznie generowane z tagów `@return` lub `@param` w odpowiednich getterach lub setterach przez\n  uruchomienie `./build php-doc` w folderze build.\n  Można dodać tag `@property` do gettera lub settera, aby wprost określić informację dla dokumentacji właściwości zadeklarowanej\n  w tych metodach, kiedy opis różni się od tego, co znajduje się w `@return`. Poniżej znajduje się przykład:\n\n  ```php\n    <?php\n    /**\n     * Returns the errors for all attribute or a single attribute.\n     * @param string $attribute attribute name. Use `null` to retrieve errors for all attributes.\n     * @property array An array of errors for all attributes. Empty array is returned if no error.\n     * The result is a two-dimensional array. See [[getErrors()]] for detailed description.\n     * @return array errors for all attributes or the specified attribute. Empty array is returned if no error.\n     * Note that when returning errors for all attributes, the result is a two-dimensional array, like the following:\n     * ...\n     */\n    public function getErrors($attribute = null)\n  ```\n\n#### Poziom pliku\n\n```php\n<?php\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n```\n\n#### Poziom klasy\n\n```php\n/**\n * Component is the base class that provides the *property*, *event* and *behavior* features.\n *\n * @include @yii/docs/base-Component.md\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Component extends \\yii\\base\\BaseObject\n```\n\n\n#### Poziom funkcji / metody\n\n```php\n/**\n * Returns the list of attached event handlers for an event.\n * You may manipulate the returned [[Vector]] object by adding or removing handlers.\n * For example,\n *\n * ```\n * $component->getEventHandlers($eventName)->insertAt(0, $eventHandler);\n * ```\n *\n * @param string $name the event name\n * @return Vector list of attached event handlers for the event\n * @throws Exception if the event is not defined\n */\npublic function getEventHandlers($name)\n{\n    if (!isset($this->_e[$name])) {\n        $this->_e[$name] = new Vector;\n    }\n    $this->ensureBehaviors();\n    return $this->_e[$name];\n}\n```\n\n#### Markdown\n\nJak widać w powyższych przykładach, używamy składni markdown do formatowania komentarzy phpDoc.\n\nW dokumentacji stosowana jest dodatkowa składnia do linkowania klas, metod i właściwości:\n\n- `[[canSetProperty]]` utworzy link do metody lub właściwości `canSetProperty` w tej samej klasie.\n- `[[Component::canSetProperty]]` utworzy link do metody `canSetProperty` w klasie `Component` w tej samej przestrzeni nazw.\n- `[[yii\\base\\Component::canSetProperty]]` utworzy link do metody `canSetProperty` w klasie `Component` w przestrzeni nazw `yii\\base`.\n- `[[Component]]` utworzy link do klasy `Component` w tej samej przestrzeni nazw. Można tutaj również dodać przestrzeń nazw.\n\nAby nadać powyższym linkom inną etykietę niż nazwa klasy lub metody, można użyć składni pokazanej w poniższym przykładzie:\n\n```\n... as displayed in the [[header|header cell]].\n```\n\nCzęść przed | jest linkowaną metodą, właściwością lub klasą, a część po | jest etykietą linku.\n\nMożliwe jest też linkowanie do Przewodnika używając następującej składni:\n\n```markdown\n[link to guide](guide:file-name.md)\n[link to guide](guide:file-name.md#subsection)\n```\n\n\n#### Komentarze\n\n- Komentarze jednolinijkowe powinny zaczynać się od `//` a nie od `#`.\n- Komentarze jednolinijkowe powinny znajdować się w osobnej linii.\n\nDodatkowe zasady\n----------------\n\n### `=== []` vs `empty()`\n\nNależy używać `empty()`, kiedy jest to możliwe.\n\n### Wielokrotne punkty powrotu\n\nNależy powracać (return) wcześnie, kiedy tylko instrukcje warunkowe zaczynają się zagnieżdżać. Nie ma to znaczenia w przypadku krótkich metod.\n\n### `self` vs. `static`\n\nNależy zawsze używać `static` z wyjątkiem poniższych przypadków:\n\n- odwołania do stałych MUSZĄ odbywać się za pomocą `self`: `self::MY_CONSTANT`\n- odwołania do prywatnych statycznych właściwości MUSZĄ odbywać się za pomocą `self`: `self::$_events`\n- można używać `self` do wywołania metod, kiedy ma to sens, jak w przypadku wywołań rekurencyjnych aktualnej implementacji zamiast rozszerzania implementacji klas.\n\n### Wartość dla \"nie rób czegoś\"\n\nWłaściwości pozwalające na skonfigurowanie komponentu, tak, aby nie robił czegoś, powinny przyjmować wartość `false`. `null`, `''` lub `[]` nie powinny być traktowane w ten sposób.\n\n### Nazwy folderów/przestrzeni nazw\n\n- należy używać małych liter\n- należy używać liczby mnogiej rzeczowników, które reprezentują obiekty (np. validators)\n- należy używać liczby pojedynczej dla nazw reprezentujących funkcjonalności (np. web)\n- preferowane są przestrzenie nazw będące pojedynczym słowem\n- w przypadku, gdy pojedyncze słowo nie jest wystarczające, należy użyć formatu camelCase\n"
  },
  {
    "path": "docs/internals-pl/design-decisions.md",
    "content": "Decyzje projektowe\n==================\n\nTen dokument zawiera listę decyzji projektowych, które podjęliśmy po długich debatach. O ile nie ma bardzo ważnych powodów ku temu, \naby działać inaczej, postanowienia te powinny być zawsze w mocy, dla zachowania spójności. Każda zmiana poniższych decyzji \npowinna być najpierw zatwierdzona przez głównych deweloperów.\n\n1. **[Kiedy wspierać aliasy ścieżek dostępu](https://github.com/yiisoft/yii2/pull/3079#issuecomment-40312268)**\n   powinniśmy wspierać tworzenie aliasów ścieżej dla właściwości, które są konfigurowalne, ponieważ używanie aliasów ścieżek \n   w konfiguracjach jest bardzo wygodne. W pozostałych przypadkach powinniśmy ograniczyć wsparcie dla aliasów.\n2. **Kiedy tłumaczyć komunikaty**\n   komunikaty powinny być tłumaczone, kiedy są prezentowane użytkownikowi końcowemu nie będącemu deweloperem i są wobec tego dla niego \n   zrozumiałe. Komunikaty statusów HTTP, wyjątki w kodzie itp. nie powinny być tłumaczone. Komunikaty konsoli prezentowane są zawsze \n   w języku angielskim z powodu potencjalnych problemów ze stroną kodową znaków.\n3. **[Dodawanie wsparcia dla nowych klientów uwierzytelniających](https://github.com/yiisoft/yii2/issues/1652)**\n   W celu łatwiejszej utrzymalności kodu, nie będziemy dodawać żadnych dodatkowych klientów uwierzytelniających do bazowego rozszerzenia. \n   Dodatkowe klienty powinny być dodane w rozszerzeniach użytowników. \n4. **Podczas używania domknięć** zalecane jest, aby **podać wszystkie przekazywane parametry** w sygnaturze funkcji, nawet jeśli \n   nie wszystkie z nich są wykorzystane. Dzięki temu modyfikowanie i kopiowanie kodu jest znacznie łatwiejsze, ponieważ wszystkie \n   informacje są bezpośrednio widoczne i nie ma konieczności sprawdzania w dokumentacji, które parametry są w ogóle dostępne. \n   ([#6584](https://github.com/yiisoft/yii2/pull/6584), [#6875](https://github.com/yiisoft/yii2/issues/6875))\n5. Preferowane jest używanie typu **int zamiast unsigned int** w schematach baz danych. Użycie int ma tę zaletę, że może być \n   prezentowany w PHP jako typ integer.\n   W przypadku unsigned i 32 bitowych systemów, musielibyśmy używać typu string do tej prezentacji.\n   Dodatkowo, pomimo tego, że unsigned int podwaja jego zakres, jeśli tabela wymaga tak dużych liczb, bezpieczniej jest używać typu \n   bigint lub mediumint, niż polegać na unsigned.\n   <https://github.com/yiisoft/yii/pull/1923#issuecomment-11881967>\n6. [Klasy pomocnicze vs oddzielne niestatyczne klasy](https://github.com/yiisoft/yii2/pull/12661#issuecomment-251599463)\n7. **Łańcuchowanie metod setterów** powinno być unikane, jeśli w klasie znajdują się metody zwracające ważne wartości. \n   Łańcuchowanie może być wspierane, jeśli klasa jest typu budującego, gdzie wszystkie settery modyfikują jedynie wewnętrzne stany: https://github.com/yiisoft/yii2/issues/13026\n8. **Globalny mechanizm przetwarzania wyjątków/błędów** jest używany zamiast lokalnych bloków try-catch, ponieważ jest on sprawdzony w przypadku łapania destruktorów \n   i wszystkiego, co dzieje się poza zakresem obejmowanym przez metodę `run()` jak np. bootstrap. Zobacz [#14348](https://github.com/yiisoft/yii2/issues/14348).\n"
  },
  {
    "path": "docs/internals-pl/getting-started.md",
    "content": "Jak zacząć proces współtworzenia Yii 2\n======================================\n\nZobacz [cykl produkcyjny Git dla kontrybutorów Yii 2](git-workflow.md), aby zapoznać się z informacjami o konfiguracji środowiska deweloperskiego.\n"
  },
  {
    "path": "docs/internals-pl/git-workflow.md",
    "content": "Cykl produkcyjny Git dla kontrybutorów Yii 2\n============================================\n\nZatem chcesz współtworzyć Yii? Świetnie! Aby przyspieszyć proces akceptacji Twoich modyfikacji, pamiętaj o przestrzeganiu poniższych \nwskazówek. Jeśli pierwszy raz masz do czynienia z repozytorium Git lub GitHubem, zapoznaj się najpierw z \n[dokumentacją pomocy GitHub](https://help.github.com/), witryną [sprawdź Gita](https://try.github.com) lub też poczytaj o \n[wewnętrznym modelu danych Git](https://nfarina.com/post/9868516270/git-is-simpler).\n\nPrzygotuj swoje środowisko deweloperskie\n----------------------------------------\n\nPoniższe instrukcje pomogą Ci w tworzeniu środowiska deweloperskiego dla Yii, którego możesz użyć podczas pracy nad bazowym kodem \nframeworka Yii. Wykonać je należy tylko raz, przed pierwszą kontrybucją.\n\n### 1. [Sforkuj (zduplikuj własną wersję)](https://help.github.com/fork-a-repo/) repozytorium Yii w serwisie GitHub i sklonuj swojego forka do środowiska deweloperskiego\n\n```\ngit clone git@github.com:TWOJA-NAZWA-UZYTKOWNIKA-GITHUB/yii2.git\n```\n\nJeśli napotkasz na problemy związane z Gitem i GitHubem na systemie operacyjnym Linux lub też otrzymujesz błędy typu \n\"Permission Denied (publickey)\" (\"Odmowa dostępu (klucz publiczny)\"), musisz odpowiednio \n[skonfigurować instalację Gita do pracy z GitHubem](https://help.github.com/linux-set-up-git/).\n\n> Tip: jeśli nie jesteś biegły w używaniu Gita, polecamy doskonałą darmową książkę [Pro Git](https://git-scm.com/book/en/v2) (z polskim tłumaczeniem dla [poprzedniej edycji](https://git-scm.com/book/pl/v1).\n\n### 2. Dodaj główne repozytorium Yii jako dodatkowy zdalny git nazwany \"upstream\"\n\nPrzejdź do folderu, do którego sklonowałeś Yii (zwykle \"yii2\") i uruchom następującą komendę:\n\n```\ngit remote add upstream https://github.com/yiisoft/yii2.git\n```\n\n### 3. Przygotuj środowisko testowe <span id=\"prepare-the-test-environment\"></span>\n\nPoniższe kroki nie są wymagane, jeśli chcesz pracować tylko nad tłumaczeniami lub dokumentacją.\n\n- uruchom `composer install`, aby zainstalować wymagane zależności (zakładając, że masz [composera zainstalowanego globalnie](https://getcomposer.org/doc/00-intro.md#globally)).\n\nJeśli zamierzasz pracować z JavaScript:\n\n- uruchom `npm install`, aby zainstalować narzędzia testerskie JavaScript i ich zależności (zakładając, że masz [zainstalowane Node.js i NPM]\n(https://nodejs.org/en/download/package-manager/)).\n\n> Note: testy JavaScript są zależne od biblioteki [jsdom](https://github.com/tmpvar/jsdom), która wymaga Node.js 4 lub nowszego.\nZalecane jest używanie Node.js w wersji 6 lub 7.\n\n- uruchom komendę `php build/build dev/app basic <fork>`, aby sklonować podstawowy szablon projektu aplikacji i zainstaluj dla niego zależności composera.\n  `<fork>` jest URL Twojego forka repozytorium, np. `git@github.com:my_nickname/yii2-app-basic.git`. Jeśli jesteś kontrybutorem głównego kodu frameworka, możesz pominąć wskazywanie forka.\n  Komenda ta zainstaluje normalnie pakiety composera, ale jednocześnie podlinkuje folder yii2 do \n  pobranego wcześniej repozytorium, dzięki czemu otrzymasz instalację jednej instacji całego kodu na raz.\n  \n  Powtórz ten krok dla zaawansowanego szablonu projektu aplikacji, jeśli chcesz: `php build/build dev/app advanced <fork>`.\n  \n  Ta komenda służy również do aktualizacji zależności; uruchamia wewnętrznie `composer update`.\n\n> Note: Domyślnie repozytoria git klonowane są za pośrednictwem SSH - aby użyć zamiast tego połączenia HTTPs dodaj flagę `--useHttp` \n> do komendy `build`.\n\n**Teraz dysponujesz już odpowiednim miejscem, aby rozpocząć hackowanie Yii 2.**\n\nPoniższe kroki są opcjonalne.\n\n### Testy jednostkowe\n\nMożesz uruchomić testy jednostkowe za pomocą komendy `phpunit` w głównym folderze repozytorium. \nJeśli nie posiadasz phpunit zainstalowanego globalnie, użyj zamiast tego komendy `php vendor/bin/phpunit` lub \n`vendor/bin/phpunit.bat` w przypadku korzystania z systemu Windows.\n\nNiektóre testy wymagają przygotowania i skonfigurowania dodatkowych baz danych. Możesz utworzyć plik `tests/data/config.local.php`, \naby nadpisać konfigurację ustawioną w `tests/data/config.php`.\n\nMożesz ograniczyć testy do grupy tych, nad którymi akurat pracujesz, np. aby uruchomić tylko testy walidatorów i redisa użyj \n`phpunit --group=validators,redis`. Możesz zobaczyć listę dostępnych grup po wpisaniu `phpunit --list-groups`. \n\nMożesz rozpocząć testy jednostkowe JavaScript, uruchamiając `npm test` w głównym folderze repozytorium.\n\n### Rozszerzenia\n\nAby pracować nad rozszerzeniami, musisz sklonować ich repozytoria. Stworzyliśmy komendę, która pozwoli Ci to zrobić w prosty sposób:\n\n```\nphp build/build dev/ext <nazwa-rozszerzenia> <fork>\n```\n\ngdzie `<nazwa-rozszerzenia>` jest poprawną nazwą, np. `redis`, a `<fork>` jest URL forka rozszerzenia np. `git@github.com:my_nickname/yii2-redis.git`. \nJeśli jesteś kontrybutorem głównego kodu frameworka, możesz pominąć wskazywanie forka.\n\nJeśli chcesz przetestować rozszerzenie w jednym z szablonów projektów, po prostu dodaj je do pliku `composer.json` aplikacji \nw zwyczajowy sposób, np. dodaj `\"yiisoft/yii2-redis\": \"~2.0.0\"` do sekcji `require` w podstawowym szablonie aplikacji.\nUruchomienie `php build/build dev/app basic <fork>` zainstaluje rozszerzenie i jego zależności i utworzy symlink do folderu \n`extensions/redis`, dzięki czemu możesz pracować bezpośrednio w repozytorium yii2, a nie folderze vendorowym composera.\n\n> Note: Również w tym przypadku pamiętaj o fladze `--useHttp`, jak to opisano powyżej.\n\n\nPraca nad błędami i funkcjonalnościami\n--------------------------------------\n\nMając przygotowane środowisko deweloperskie, jak zostało to opisane powyżej,  możesz rozpocząć prace nad poprawianiem błędów \ni rozwijaniem funkcjonalności.\n\n### 1. Upewnij się, że istnieje zgłoszony problem dotyczący kodu, który zamierzasz modyfikować, jeśli wymaga on wzmożonej pracy\n\nWszystkie nowe funkcjonalności i poprawki błędów powinny być powiązane ze zgłoszeniem, aby zapewnić punkt odniesienia dla dyskusji \ni komentarzy. Poświęć kilka minut, aby przejrzeć istniejące zgłoszenia i odszukać takie, które opisuje Twoją przyszłą kontrybucję. \nJeśli je znajdziesz na liście, dopisz w nim komentarz z informacją, że pracujesz aktualnie nad tym zagadnieniem. Jeśli takiego \nzgłoszenia nie znajdziesz, [stwórz nowe](report-an-issue.md) lub dodaj prośbę o dołączenie kodu bezpośrednio, jeśli to prosta poprawka. \nPozwoli to zespołowi programistów zapoznać się z Twoją sugestią i zapewnić odpowiednią pomoc i komentarz w czasie całego procesu.\n\n> W przypadku drobnych zmian, problemów z dokumentacją czy też szybkich poprawek kodu, nie musisz zgłaszać nowego problemu; prośba o dołączenie kodu jest wystarczająca.\n\n### 2. Pobierz aktualny kod z głównego repozytorium Yii\n\n```\ngit fetch upstream\n```\n\nOd tego kroku powinieneś zawsze zaczynać w przypadku każdej nowej kontrybucji, aby upewnić się, że pracujesz na aktualnej wersji kodu.\n\n### 3. Stwórz nową gałąź repozytorium dla Twojej funkcjonalności bazując na aktualnej gałęzi master Yii\n\n> Jest to bardzo ważne, ponieważ nie będziesz mógł wysłać więcej niż jednej prośby o dołączenie kodu z Twojego konta, jeśli będziesz \n> używać gałęzi master.\n\nKażdy oddzielna poprawka błędu czy też zmiana kodu powinna być utworzona w swojej własnej gałęzi. Nazwy gałęzi powinny być opisowe \ni zaczynać się od numeru zgłoszenia, które jej dotyczą. Jeśli nie poprawiasz któregoś z konkretnych zgłoszeń, po prostu pomiń numer.\nDla przykładu:\n\n```\ngit checkout upstream/master\ngit checkout -b 999-nazwa-twojej-galezi-w-tym-miejscu\n```\n\n### 4. Zademonstruj swoją magię, napisz swój kod\n\nUpewnij się, że działa poprawnie :)\n\nTesty jednostkowe są zawsze mile widziane. Prawidłowo i w całości przetestowany kod znacznie upraszcza proces weryfikacji kontrybucji.\nAkceptowane są również testy jednostkowe kończące się porażką jako opisy zgłoszeń problemów.\n\n### 5. Zaktualizuj CHANGELOG (dziennik zmian)\n\nZedytuj plik CHANGELOG, aby dołączyć informację o Twojej modyfikacji; powinna ona znaleźć się na samym początku pliku zaraz \npod pierwszym nagłówkiem (określającym wersję, nad którą właśnie trwa praca). Linia w dzienniku zmian powinna być zapisana \npo angielsku i wyglądać jak jedna z poniższych:\n\n```\nBug #999: a description of the bug fix (Your Name)\nEnh #999: a description of the enhancement (Your Name)\n```\n\n`#999` jest numerem zgłoszenia, do którego odnosi się `Bug` (błąd) lub `Enh` (ulepszenie).\nDziennik zmian powinien być pogrupowany według typu (`Bug`, `Enh`) i posortowany według numerów zgłoszeń.\n\nW przypadku drobnych zmian, np. literówek i poprawek dokumentacji, nie ma potrzeby aktualizować pliku CHANGELOG.\n\n### 6. Zatwierdź swoje modyfikacje\n\nDodaj swoje pliki/zmiany, które chcesz zatwierdzić do [kolejki oczekujących](https://git.github.io/git-reference/basic/#add) za pomocą\n\n```\ngit add sciezka/do/mojego/pliku.php\n```\n\nMożesz użyć opcji `-p`, aby wybrać modyfikacje, które chcesz, aby pojawiły się w zgłoszeniu.\n\nZatwierdź swoje zmiany wraz z odpowiednio opisującym je komentarzem. Upewnij się, że podałeś w nim numer zgłoszenia w postaci `#XXX`, \naby GitHub mógł automatycznie połączyć modyfikacje ze zgłoszeniem:\n\n```\ngit commit -m \"A brief description of this change which fixes #999 goes here\"\n```\n\n### 7. Pobierz świeży kod Yii z upstream do Twojej gałęzi\n\n```\ngit pull upstream master\n```\n\nDzięki temu możesz być pewny, że posiadasz aktualny kod w Twojej gałęzi przed wysłaniem prośby o dołączenie go. \nJeśli pojawią się jakiekolwiek konflikty scalania, powinieneś je naprawić i zatwierdzić zmiany jeszcze raz. \nDzięki temu ekipa programistów Yii będzie mogła scalić Twoje zmiany z bazowym kodem za pomocą tylko jednego kliknięcia.\n\n### 8. Po rozwiązaniu wszystkich konfliktów, wyślij swój kod do GitHuba\n\n```\ngit push -u origin 999-nazwa-twojej-galezi-w-tym-miejscu\n```\n\nParametr `-u` spowoduje, że Twoja gałąź zostanie automatycznie wysłana i pobrana z gałęzi GitHuba. Oznacza to tyle, że następnym \nrazem, kiedy napiszesz `git push`, będzie wiedział, gdzie ją wysłać. Ułatwia to pracę w przypadku, gdy chcesz zatwierdzić więcej \nmodyfikacji w pojedynczej prośbie o dołączenie kodu.\n\n### 9. Otwórz [prośbę o połączenie kodu](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) z upstream.\n\nPrzejdź do swojego repozytorium na GitHubie i kliknij \"Pull Request\", wybierz swoją gałąź po prawej stronie i podaj dodatkowe \nszczegóły w polu komentarza. Aby połączyć wysyłaną prośbę ze zgłoszeniem umieść gdziekolwiek w komentarzu `#999`, gdzie 999 jest \nnumerem zgłoszenia.\n\n> Zwróć uwagę na to, że każda prośba o dołączenie powinna zawierać kod poprawki dla pojedynczego zgłoszenia. \n> W przypadku wielu niepowiązanych ze sobą modyfikacji otwórz odpowiednie zgłoszenia do każdej z nich.\n\n### 10. Ktoś przejrzy i oceni Twój kod\n\nKtoś zerknie na Twój kod i możesz zostać poproszony o wprowadzenie kilku zmian, a jeśli tak, przejdź do kroku #6 (nie musisz \ntworzyć nowego zgłoszenia, jeśli aktualne jest wciąż otwarte). W momencie, gdy Twój kod będzie zaakceptowany, zostanie scalony \nz kodem głównej gałęzi i stanie się częścią następnego wydania Yii. Jeśli jednak nie uzyska on akceptacji, nie zniechęcaj się - \nróżni ludzie potrzebują różnych funkcjonalności i Yii nie może zapewnić ich wszystkich dla każdego. Twój kod pozostanie dostępny \nna GitHubie dla osób, które będą tego potrzebować.\n\n### 11. Sprzątanie\n\nKiedy Twój kod zostanie zaakceptowany bądź odrzucony, możesz usunąć gałęzie, na których pracowałeś z lokalnego repozytorium \ni z `origin`.\n\n```\ngit checkout master\ngit branch -D 999-nazwa-twojej-galezi-w-tym-miejscu\ngit push origin --delete 999-nazwa-twojej-galezi-w-tym-miejscu\n```\n\n### Note:\n\nW celu wczesnego wykrycia ewentualnych problemów z integracją, każde żądanie scalenia głównego kodu Yii na GitHubie jest \nweryfikowane przez automatyczne testy [Travis CI](https://travis-ci.com). Ponieważ ekipa głównych programistów stara się nie \nnadużywać tej usługi, [`[ci skip]`](https://docs.travis-ci.com/user/customizing-the-build/#Skipping-a-build) jest dodawane przy komentarzu \nscalenia kodu, jeśli żądanie:\n\n* dotyczy jedynie javascript, css lub plików obrazków,\n* aktualizuje dokumentację,\n* modyfikuje jedynie stałe łańcuchy znaków (np. w przypadku aktualizacji tłumaczeń)\n\nDzięki temu pomijane są automatyczne testy travisa dla zmian, które i tak nie są nimi pokryte.\n\n### Przegląd komend (dla zaawansowanych kontrybutorów)\n\n```\ngit clone git@github.com:TWOJA-NAZWA-UZYTKOWNIKA-GITHUB/yii2.git\ngit remote add upstream https://github.com/yiisoft/yii2.git\n```\n\n```\ngit fetch upstream\ngit checkout upstream/master\ngit checkout -b 999-nazwa-twojej-galezi-w-tym-miejscu\n\n/* pokaż swoją magię, zaktualizuj dziennik zmian, jeśli to konieczne */\n\ngit add sciezka/do/mojego/pliku.php\ngit commit -m \"A brief description of this change which fixes #999 goes here\"\ngit pull upstream master\ngit push -u origin 999-nazwa-twojej-galezi-w-tym-miejscu\n```\n"
  },
  {
    "path": "docs/internals-pl/project-organization.md",
    "content": "Organizacja projektu\n====================\n\nW tym dokumencie opisana jest organizacja repozytoriów deweloperskich Yii 2.\n \n1. Poszczególne bazowe rozszerzenia i szablony projektów są utrzymywane w oddzielnych *niezależnych* projektach na GitHubie \n   pod GitHubowym szyldem [yiisoft](https://github.com/yiisoft).\n    \n   Nazwy repozytoriów rozszerzeń są poprzedzone przedrostkiem `yii2-`, np. `yii2-gii` dla rozszenia `gii`.\n   Nazwa pakietu composera jest taka sama jak ścieżka repozytorium w Githubie, np. `yiisoft/yii2-gii`.\n   \n   Nazwy repozytoriów szablonów projektów aplikacji są poprzedzone przedrostkiem `yii2-app-`, np. `yii2-app-basic` dla \n   szablonu aplikacji `basic`.\n   Nazwa pakietu composera jest taka sama jak ścieżka repozytorium w Githubie, np. `yiisoft/yii2-app-basic`.\n   \n   Każde rozszerzenie/projekt aplikacji:\n \n   * utrzymuje swoją dokumentację i instrukcje w folderze \"docs\". Dokumentacja API zostanie wygenerowana w locie, podczas tworzenia nowego wydania.\n   * utrzymuje swój kod testów w folderze \"tests\".\n   * utrzymuje swoje tłumaczenia komunikatów i pozostały istotny meta kod.\n   * śledzi zgłoszenia w odpowiednim projekcie GitHuba.\n      \n   Repozytoria rozszerzeń będą wydawane niezależnie w miarę potrzeb, szablony projektów będą wydawane razem z frameworkiem.\n   Więcej szczegółów znajdziesz w [polityce wersjonowania](versions.md).\n\n2. Projekt `yiisoft/yii2` jest głównym repozytorium deweloperskim frameworka Yii 2.\n   Repozytorium to utrzymuje pakiet composera [yiisoft/yii2-dev](https://packagist.org/packages/yiisoft/yii2-dev).\n   Zawiera bazowy kod frameworka, jego testy jednostkowe, przewodnik i zestaw narzędzi przydatnych w procesie tworzenia i wydawania wersji.\n   \n   Zgłoszenia błędów i nowych funkcjonalności są śledzone w tym projekcie Githuba.\n   \n3. Repozytorium `yiisoft/yii2-framework` jest wydzielonym tylko do odczytu folderem `framework` z repozytorium deweloperskiego \n   i utrzymuje pakiet composera [yiisoft/yii2](https://packagist.org/packages/yiisoft/yii2), który jest oficjalnym pakietem \n   służącym do instalacji frameworka.\n\n4. Dla ułatwienia procesu deweloperskiego szablony projektów i rozszerzenia mogą zostać dołączone do struktur projektu za pomocą \n   komendy [build dev/app](git-workflow.md#prepare-the-test-environment).\n"
  },
  {
    "path": "docs/internals-pl/pull-request-qa.md",
    "content": "Zapewnienie jakości kodu w prośbie o połączenie\n===============================================\n\nPodczas sprawdzania czy PR może być scalony lub nie, następujące kryteria powinny być również wzięte pod uwagę:\n\n- Powinno istnieć zgłoszenie powiązane z PR lub PR powinien zawierać dobry opis tego, co dodaje lub zmienia.\n- Testy jednostkowe. Nieobowiązkowe, ale bardzo cenione. Powinny się nie udać przy braku kodu, który PR wprowadza. \n- Wpis w CHANGELOG powinien być obecny w sekcji następnego wydania, posortowany według typu i numeru zgłoszenia.\n  Pseudonimy autorów powinny też być dodane.\n- [Styl kodu](core-code-style.md) oraz [styl widoków](view-code-style.md) powinien być bez zastrzeżeń. Poprawki tego dotyczące można \n  wprowadzić w trakcie scalania, jeśli bardziej odpowiada to osobie scalającej.\n"
  },
  {
    "path": "docs/internals-pl/release.md",
    "content": "Wydawanie nowej wersji\n======================\n\nLista kroków koniecznych do wykonania podczas wydawania frameworka urosła znacznie w ciągu ostatnich lat i stała się uciążliwa do \nutrzymywania ręcznego, zatem stworzyliśmy narzędzie dostępne z linii komend, aby upewnić się, że żaden z kroków nie zostanie pominięty.\n\nOmówienie kroków wydania\n------------------------\n\n- ...\n\nKomenda wydania\n---------------\n\nPoniższe kroki są zautomatyzowane za pomocą [konsolowej komendy wydania](../../build/controllers/ReleaseController.php), która jest \ndostępna w deweloperskim repozytorium frameworka.\n\nKomenda wydania może być uruchomiona za pomocą aplikacji Yii w folderze `build` frameworka:\n\n```bash\n./build/build help release  # uruchom w fodlerze głównym frameworka\n```\n\n> Info: Możesz uruchomić komendę z opcją `--dryRun`, aby zobaczyć co może zrobić. Używając tej opcji, nie zostanie wykonana \n> żadna zmiana, a modyfikacje plików i tagi nie będą tworzone i wysyłane.\n\n### Wymagania\n\nDziałanie komenda wydania uzależnione jest od środowiska deweloperskiego opisanego w \n[Cyklu produkcyjnym Git](git-workflow.md#extensions), przykładowo szablony aplikacji muszą znajdować się w folderze `/apps/` \na rozszerzenia w `/extensions/`.\nStruktury te najlepiej utworzyć za pomocą komend `dev/app` i `dev/ext`.\n\nPrzykładowa instalacja rozszerzenia:\n\n```bash\n./build/build dev/ext authclient\n```\n\nlub szablonu:\n\n```bash\n./build/build dev/app basic\n```\n\nTaka instalacja zapewni użycie tego samego kodu repozytorium dla rozszerzenia, jaki znajduje się w aktualnej wersji repozytorium.\n\n### Informacje o wersji\n\nAby sprawdzić informacje dotyczące wersji frameworka i rozszerzeń, możesz uruchomić\n\n```bash\n./build/build release/info\n```\n\nMożesz uruchomić powyższą komendę z `--update`, aby pobrać listę tagów dla wszystkich repozytoriów w celu uzyskania najnowszych \ninformacji.\n\n### Tworzenie wydania\n\nTworzenie wydania frameworka zawiera poniższe komendy (szablony aplikacji są zawsze wydawane razem z frameworkiem):\n\n```bash\n./build release framework\n./build release app-basic\n./build release app-advanced\n```\n\nTworzenie wydania rozszerzenia zawiera tylko jedną komendę (np. dla `redis`):\n\n```bash\n./build release redis\n```\n\nDomyślnie komenda wydania wydaje nową pomniejszą wersję w aktualnej gałęzi.\nAby wydać inną wersję niż domyślna, należy określić ją bezpośrednio używając opcji `--version`, np. `--version=2.1.0` \nlub `--version=2.1.0-beta`.\n\n#### Wydanie nowej głównej wersji np. 2.1.0\n\nWydawanie nowej głownej wersji obejmuje zmianę gałęzi, jak to opisano w [polityce wersjonowania](versions.md).\nPoniższy przykład opisuje wydanie wersji `2.1.0`, stworzonej w gałęzi `2.1` pochodzącej z `master`. `master` zawiera już wcześniej \nwersje `2.0.x`.\n\n- stwórz nową gałąź `2.0` z `master`,\n- upewnij się, że composer.json nie zawiera już aliasu dla tej gałęzi,\n- scal potrzebne zmiany z `master` do `2.1`,\n- ustaw `master` na wskazywanie ostatniego wprowadzenia zmian w `2.1`,\n- ustaw alias gałęzi w composer.json dla master na `2.1.x-dev`,\n- skasuj gałąź `2.1`.\n\nTeraz pobierz `master` i uruchom komendę wydania z opcją `--version=2.1.0`. \n"
  },
  {
    "path": "docs/internals-pl/report-an-issue.md",
    "content": "Zgłaszanie problemów\n====================\n\nProsimy o przestrzeganie poniższych wskazówek podczas dodawania nowego zgłoszenia, aby ułatwić i przyspieszyć rozwiązanie problemu:\n\n* Uzupełnij zgłoszenie o dodatkowe informacje takie jak: wersja PHP i Yii, rodzaj i wersja systemu operacyjnego, serwera Web oraz przeglądarki internetowej;\n* Prześlij **kompletny** stos wywołania błędu, jeśli jest to możliwe. Zrzut ekranu wyjaśniający problem jest również mile widziany.\n* Objaśnij kolejne kroki, dzięki którym można odtworzyć problem. Byłoby nawet lepiej, gdybyś przesłał kod pozwalący na odtworzenie.\n* Jeśli jest to możliwe, możesz nawet stworzyć odpowiedni test jednostkowy i [wysłać go z prośbą o dołączenie](git-workflow.md).\n\nJeśli problem dotyczy jednego z oficjalnych rozszerzeń, prosimy o zgłoszenie go w repozytorium błędów odpowiednim dla rozszerzenia.\nJeśli nie jesteś pewien, gdzie powinno trafić zgłoszenie, [wyślij je do głównego repozytorium](https://github.com/yiisoft/yii2/issues/new) (<https://github.com/yiisoft/yii2/issues>).\n\n**Nie zgłaszaj problemu jeśli**\n\n* pytasz o to, w jaki sposób użyć którejś z funkcjonalności Yii. Do tego celu użyj [forum](https://forum.yiiframework.com/index.php/forum/42-general-discussions-for-yii-20/) lub [chatu](https://www.yiiframework.com/chat/).\n* dotyczy on bezpieczeństwa. W takim wypadku prosimy o [bezpośredni kontakt](https://www.yiiframework.com/security/).\n\n**Unikaj duplikowania zgłoszeń**\n\nZanim zgłosisz problem, przeszukaj [uprzednio dodane zgłoszenia](https://github.com/yiisoft/yii2/issues), aby sprawdzić, czy Twój problem nie został już przedstawiony i/lub naprawiony. Upewnij się również, czy używasz najnowszej wersji Yii i czy ten problem dalej w niej występuje.\n"
  },
  {
    "path": "docs/internals-pl/translation-status.md",
    "content": "Status dokumentacji\n===================\n\nCałość jest gotowa do tłumaczenia.\n"
  },
  {
    "path": "docs/internals-pl/translation-teams.md",
    "content": "Ekipy tłumaczące\n================\n\nChiński\n-------\n\n- **Paris Qian Sen 东方孤思子,[@qiansen1386](https://github.com/qiansen1386),qiansen1386@gmail.com**\n- [@AbrahamGreyson 刘阳](https://github.com/AbrahamGreyson)\n- [@fmalee](https://github.com/fmalee)\n- [@funson86 花生](https://github.com/funson86)\n- [@ivantree 长兴苗木](https://github.com/ivantree)\n- [@netyum 未来](https://github.com/netyum)\n- [@riverlet 小河](https://github.com/riverlet)\n- [@yiichina 巡洋舰](https://github.com/yiichina)\n\nFiński\n------\n\n- Jani Mikkonen, [@janisto](https://github.com/janisto), janisto@php.net\n\nHiszpański\n----------\n\n- Luciano Baraglia, [@lucianobaraglia](https://github.com/lucianobaraglia)\n- Marco Da Silva, [@markmarco16](https://github.com/markmarco16), markmarco16@gmail.com\n- Daniel Gómez Pan [@pana1990](https://github.com/pana1990), pana_1990@hotmail.com\n\nJapoński\n--------\n\n- Nobuo Kihara 木原伸夫, [@softark](https://github.com/softark), softark@gmail.com\n- Tomoki Morita, [@jamband](https://github.com/jamband), tmsongbooks215@gmail.com\n- Hisateru Tanaka, [@tanakahisateru](https://github.com/tanakahisateru), tanakahisateru@gmail.com\n\nNiemiecki\n---------\n\n- Carsten Brandt, [@cebe](https://github.com/cebe), mail@cebe.cc\n\nPortugalski brazylijski\n-----------------------\n\n- **Davidson Alencar, [@davidsonalencar](https://github.com/davidsonalencar), davidson.t.i@gmail.com**\n- [@wbraganca](https://github.com/wbraganca)\n- Alan Michel Willms Quinot, [@alanwillms](https://github.com/alanwillms), dyulax@gmail.com\n\nRosyjski\n--------\n\n- **Alexander Makarov, [@samdark](https://github.com/samdark), sam@rmcreative.ru**\n- [@MUTOgen](https://github.com/MUTOgen)\n- [@prozacUa](https://github.com/prozacUa)\n\nUkraiński\n---------\n\n- **Alexandr Bordun [@borales](https://github.com/Borales), admin@yiiframework.com.ua**\n- Roman Bahatyi [@RichWeber](https://github.com/RichWeber), rbagatyi@gmail.com\n- Igor Zozulinskyi [@3y3ik](https://github.com/3y3ik)\n- Vadym Chenin [@vchenin](https://github.com/vchenin), vchenin@meta.ua\n\nWłoski\n------\n\n- Lorenzo Milesi, [@maxxer](https://github.com/maxxer), maxxer@yetopen.it\n"
  },
  {
    "path": "docs/internals-pl/translation-workflow.md",
    "content": "Cykl tworzenia tłumaczenia\n==========================\n\nYii jest przetłumaczony na wiele języków, dzięki czemu może być używany przez międzynarodowe grono deweloperów. Dwa główne obszary, \ngdzie kontrybucje tłumaczeń są mile widziane, to dokumentacja i komunikaty frameworka.\n\nKomunikaty frameworka\n---------------------\n\nFramework posługuje się dwoma typami komunikatów: wyjątkami, przeznaczonymi dla deweloperów, które nie są nigdy tłumaczone, \noraz pozostałymi, zwykle widocznymi dla użytkowników końcowych, takimi jak błędy walidacji.\n\nAby rozpocząć tłumaczenie komunikatów:\n\n1. Sprawdź plik `framework/messages/config.php` i upewnij się, że Twój język jest wymieniony w sekcji `languages`. \n   Jeśli nie, dodaj go tam (pamiętaj, że lista powinna być posortowana alfabetycznie). Format kodu języka powinien być zgodny \n   ze [specyfikacją IETF](https://en.wikipedia.org/wiki/IETF_language_tag) (przykładowo `ru`, `zh-CN`).\n2. Przejdź do folderu `framework` i uruchom `./yii message/extract @yii/messages/config.php --languages=<twoj_jezyk>`.\n3. Przetłumacz komunikaty w pliku `framework/messages/twoj_jezyk/yii.php`. Upewnij się, że zapisujesz plik z kodowaniem UTF-8.\n4. [Wyślij prośbę o dołączenie kodu](git-workflow.md).\n\nAby Twoje tłumaczenia pozostawały aktualne, możesz uruchomić `./yii message/extract @yii/messages/config.php --languages=<twoj_jezyk>` ponownie po pewnym czasie. \nKomunikaty zostaną jeszcze raz wyekstraktowane, pozostawiając w pliku te, które się nie zmieniły.\n\nW pliku tłumaczeń każdy element tablicy reprezentuje przetłumaczony (wartość) komunikat (klucz). Jeśli wartość jest pusta,\nkomunikat jest uznawany za nieprzetłumaczony. Tłumaczenia komunikatów, które nie są już wymagane, ujęte są w pary znaków '@@'. \nŁańcuch znaków komunikatu może być zapisany w formacie liczb mnogich. Więcej szczegółów na ten temat znajdziesz w \n[sekcji i18n przewodnika](../guide/tutorial-i18n.md).\n\nDokumentacja\n------------\n\nUmieść tłumaczenia dokumentacji w folderze `docs/<pierwotny_folder>-<jezyk>`, gdzie `<pierwotny_folder>` jest nazwą folderu \nzawierającego oryginalną dokumentację, jak np. `guide` lub `internals`, a `<jezyk>` jest kodem języka użytego do tłumaczenia. \nDla polskiego tłumaczenia przewodnika folder ten to `docs/guide-pl`.\n\nPo tym jak nowe tłumaczenia zostaną już dodane, możesz sprawdzić co się zmieniło od ostatniego tłumaczenia pliku używając \nspecjalnej komendy w folderze `build`:\n\n```\nphp build translation \"../docs/guide\" \"../docs/guide-pl\" \"Raport tłumaczeń dla polskiego przewodnika\" > report_guide_pl.html\n```\n\nJeśli zobaczysz komunikaty związane z composerem, uruchom `composer install` w źródłowym folderze.\n\nAby zapoznać się z informacjami na temat składni dokumentacji i stylu użytego w przewodniku, przejdź do [documentation_style_guide.md](../documentation_style_guide.md).\n"
  },
  {
    "path": "docs/internals-pl/versions.md",
    "content": "Wersjonowanie Yii\n=================\n\nTen dokument podsumowuje politykę wersjonowania Yii. Naszą aktualną strategię wersjonowania jest wariant [wersjonowania semantycznego](https://semver.org/).\n\nGłówni programiści Yii wiele razy już zgadzali się i podkreślali, że ważnym jest utrzymanie wydań 2.0.x kompatybilnych \nwstecznie. To jednak idealna teoria, którą w praktyce nie zawsze udaje się osiągnąć. Zapoznaj się z sekcją [Wsteczna kompatybilność](bc.md), \nopisującą w szczegółach, czym jest wsteczna kompatybilność.\n\nPodsumowując, nasza polityka wersjonowania Yii 2 przedstawia się następująco:\n\n## Numery wersji\n\nNumery wersji dodawane są w formacie `2.x.y.z`, gdzie `z` można pominąć, w przypadku, gdy wynosi `0`.\n\nEwentualna wersja 3 Yii nie jest tu uwzględniania, ponieważ zakładamy, że skok rozwojowy będzie tutaj podobny jak w przypadku 2.0 \nzastępującego 1.0. Zakładamy, że przejście nastąpi co 3 do 5 lat, w zależności od rozwoju zewnętrznych technologii (takich jak \naktualizacja PHP z 5.0 do 5.4).\n\n### `2.X.0`: główne wydania\n\nWydania nie zachowujące wstecznej kompatybilności, które zawierają główne funkcjonalności i zmiany. Aktualizacja z wcześniejszych \nwersji może nie być trywialna, ale dostępna będzie zawsze dotycząca tego instrukcja.\n\n* Zawiera głównie nowe funkcjonalności i poprawki błędów.\n* Zawiera pomniejsze funkcjonalności i poprawki błędów z wydań łatających.\n* Może zawierać zmiany nie zachowujące wstecznej kompatybilności,  które są wymienione w pliku `UPGRADE-2.X.md`.\n* Cykl wydania to około 12 miesięcy lub więcej.\n* Wymaga wydań poprzedzających: `2.X.0-alpha`, `2.X.0-beta`, `2.X.0-rc`.\n* Wymaga podania informacji o ważnym wydaniu i zabiegów marketingowych.\n\n\n### `2.x.Y`: pomniejsze wydania\n\nWydania łatająces, które powinny być w 100% wstecznie kompatybilne. Zakładając idealny przypadek, mamy nadzieję, że zawierają tylko \nzmiany nie wpływające na wsteczną kompatybilność, jednak ponieważ nie jest to zawsze możliwe, nota aktualizacyjna jest dodawana \nw `UPGRADE.md`. W praktyce, ponieważ 2.0.x są wydawane częściej, dodajemy tam pomniejsze funkcjonalności, aby użytkownicy mogli \nnacieszyć się nimi wcześniej.\n\n* Głównie zawierają poprawki błędów i pomniejsze ulepszenia funkcjonalności.\n* Brak głównych zmian w funkcjonalnościach.\n* Powinny być w 100% wstecznie kompatybilne, aby zapewnić bezproblemową aktualizację. Dozwolone jest tylko kilka wyjątków, które powinny być opisane w `UPGRADE.md`.\n* Cykl wydania to około 1 do 2 miesięcy.\n* Nie wymagają wydań poprzedzających (alpha, beta, RC).\n* Powinny być ciągle scalane z główną gałęzią projektu (ręcznie przynajmniej raz w tygodniu).\n* Z ogłoszeniem o wydaniu. Strona projektu będzie również uaktualniona.\n\n\n### `2.x.y.Z`: wydania łatające\n\nWydania łatające, które powinny być w 100% wstecznie kompatybilne, zawierające tylko poprawki błędów.\nBez ogłoszeń o wydaniu lub aktualizacji strony projektu (chyba, że zawierają ważną poprawkę lub dotyczą bezpieczeństwa).\nProces wydania odbędzie się głównie automatycznie.\n\n* Zawierają tylko poprawki błędów, bez dodawania funkcjonalności.\n* Muszą być w 100% wstecznie kompatybilne, aby zapewnić bezproblemową aktualizację. Dozwolone są tylko wyjątki w kwestii poprawy bezpieczeństwa, które mogą wymagać naruszenia wstecznej kompatybilności.\n* Cykl produkcyjny to 1 do 2 tygodni.\n* Nie wymagają wydań poprzedzających (alpha, beta, RC).\n* Powinny być scalone z główną gałęzią projektu w momencie wydania.\n\n\n## Polityka dotycząca gałęzi projektu\n\n* Gałąź `master` jest gałęzią deweloperską dla aktualnego stabilnego głównego wydania.\n* Praca nad każdym nowym głównym wydaniem będzie odbywać się w gałęzi nazwanej numerem wersji, np. `2.1`.\n* Kiedy główne wydanie `2.n` jest gotowe, należy utworzyć gałąź utrzymującą o nazwie `2.(n-1).x` z `master`.\n  Przykładowo gałąź `2.0` jest tworzona w momencie wydania stabilnej wersji `2.1.0`, która to od tej pory będzie rozwijana w `master`\n  (zobacz [schemat nazw gałęzi dla composera](https://getcomposer.org/doc/02-libraries.md#branches)).\n* Tworzone są tagi `2.x.y.z` i `2.x.y` gałęzi, aby oznaczyć wydania łat. Dla wydań `2.x.y.0` pominięte będzie `0`.\n* Zmiany w gałęzi utrzymującej `2.n.x` będą ciągle scalane do `master`.\n\nPoniższy obrazek ilustruje historię gałęzi zmieniających się w czasie:\n\n![Polityka gałęzi projektu](versions-branches.png)\n\n\n## Wydania\n\nZarówno framework Yii 2, jak i oficjalne projekty rozszerzeń, przestrzegają powyższej polityki wersjonowania i gałęzi projektu.\nFramework i oficjalne rozszerzenia są wydawane niezależnie od siebie, zatem różnica numerów wersji pomiędzy tymi projektami jest czymś normalnym.\nSzablony projektów aplikacji są zawsze wydawane razem z frameworkiem.\n\nCykl wydawniczy opisany powyżej dotyczy tylko głównego frameworka.\nRozszerzenia są wydawane w razie potrzeby.\nRozszerzenie może nie otrzymywać nowych wydań przez bardzo długi czas, kiedy nie ma dla niego żadnych poprawek błędów lub ulepszeń.\n"
  },
  {
    "path": "docs/internals-pl/view-code-style.md",
    "content": "Styl kodowania widoków Yii 2\n============================\n\nPoniższy styl kodowania jest stosowany w kodzie frameworka Yii 2.x i oficjalnych rozszerzeniach. Nie zmuszamy jednak nikogo do stosowania go we własnych aplikacjach. Wybierz styl, który najbardziej odpowiada Twoim potrzebom.\n\n```php\n<?php\n// Rozpoczynający tag PHP jest wymagany w każdym pliku szablonu. Pusta linia za rozpoczynającym tagiem jest również wymagana.\n\n// Opisz zmienne przekazane z kontrolera w tym miejscu.\n/**\n * @var \\yii\\base\\View $this\n * @var \\yii\\widgets\\ActiveForm $form\n * @var \\app\\models\\Post[] $posts\n * @var \\app\\models\\ContactMessage $contactMessage\n */\n// Pusta linia poniżej jest wymagana.\n\n// Deklaracje klas z przestrzeniami nazw.\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n// Pusta linia poniżej jest wymagana.\n\n// Ustaw właściwości kontekstu, wywołaj jego settery, zrób inne rzeczy.\n$this->title = 'Posts';\n?>\n<!-- Preferowane są wydzielone bloki PHP dla `foreach`, `for`, `if` itp. -->\n<?php foreach ($posts as $post): ?>\n    <!-- Zwróć uwagę na wcięcie w tym miejscu. -->\n    <h2><?= Html::encode($post['title']) ?></h2>\n    <p><?= Html::encode($post['shortDescription']) ?></p>\n<!-- `endforeach;`, `endfor;`, `endif;` itd. powinny być użyte tutaj zamiast `}` w przypadku wielu bloków PHP -->\n<?php endforeach; ?>\n\n<!-- Deklaracja widżetu może, ale nie musi, być rozbita na kilka linii kodu. -->\n<?php $form = ActiveForm::begin([\n    'options' => ['id' => 'contact-message-form'],\n    'fieldConfig' => ['inputOptions' => ['class' => 'common-input']],\n]); ?>\n    <!-- Zwróć uwagę na wcięcie w tym miejscu. -->\n    <?= $form->field($contactMessage, 'name')->textInput() ?>\n    <?= $form->field($contactMessage, 'email')->textInput() ?>\n    <?= $form->field($contactMessage, 'subject')->textInput() ?>\n    <?= $form->field($contactMessage, 'body')->textArea(['rows' => 6]) ?>\n\n    <div class=\"form-actions\">\n        <?= Html::submitButton('Submit', ['class' => 'common-button']) ?>\n    </div>\n<!-- Zamykające wywołanie widżetu powinno znajdować się w wydzielonym tagu PHP. -->\n<?php ActiveForm::end(); ?>\n<!-- Kończący znak nowej linii jest wymagany. -->\n\n```\n"
  },
  {
    "path": "docs/internals-pt-BR/translation-workflow.md",
    "content": "Como Colaborar Com a Tradução Para o Português do Brasil\n========================================================\n\nO Yii tem tradução para vários idiomas, incluindo o Português do Brasil. Existem\nduas áreas onde a contribuição para a tradução é muito bem-vindo. A primeira é a\ndocumentação e a segunda são as mensagens do framework.\n\nMensagens do Framework\n----------------------\n\nO Framework tem dois tipos de mensagens: as exceções que são destinadas para o\ndesenvolvedor e nunca são traduzidas, e as mensagens que são realmente visíveis\npara o usuário final, tais como erros de validação.\n\nOs passos para iniciar a tradução de mensagens são:\n\n1. Com o `console` entre na pasta `yii2/framework`  e execute o seguinte comando:\n   `./yii message/extract @yii/messages/config.php --languages=pt-BR`.\n2. As mensagens a serem traduzidas encontram-se no seguinte caminho:\n   `framework/messages/pt-BR/yii.php`. Certifique-se de salvar o arquivo com a\n   codificação UTF-8 (Plain).\n3. Após realizar as devidas traduções o passo seguinte é enviar as suas\n   modificações para o respositório do Yii no Github.\n   [Veja aqui](https://github.com/yiisoft/yii2/blob/master/docs/internals/git-workflow.md)\n   os passos necessários para o envio dos arquivos.\n\nPara manter as traduções sempre atualizadas, certifique-se que seu fork do Yii\nesteja com a última versão. Em seguida, basta executar o comando\n`./yii message/extract @yii/messages/config.php --languages=pt-BR` novamente e o mesmo irá adicionar\nautomaticamente as novas mensagens a serem traduzidas.\n\nNo arquivo de tradução cada elemento do array representa a tradução de uma\nmensagem. Sendo que a \"chave\" representa o texto a ser traduzido e o \"valor\" a\nsua tradução. Se o \"valor\" estiver vazio, a mensagem é considerada como não\ntraduzida. As mensagens que não precisam de tradução terão seus valores cercadas\npor um par de '@@'. Atentar para algumas mensagens que estão no formato de plural,\npara isso verifique a [seção i18n do guia](../guide-pt-BR/tutorial-i18n.md) para\nmais detalhes.\n\nDocumentação\n------------\n\nAs traduções da documentação para o português do Brasil devem ficar dentro do\ndiretório `docs/` seguindo o padrão  `docs/<original>-<pt-BR>` onde `<original>`\ncorresponde ao nome da pasta tal como `guide` ou `internals`.\n\nCom a tradução do documento concluída, você pode obter um diff das mudanças desde\na última tradução, para isso, execute o seguinte comando a partir do diretório\n`build/` do framework:\n\n```\nbuild translation \"../docs/guide\" \"../docs/guide-pt-BR\" > report-guide-pt-BR.html\n```\n\nAntes de iniciar seus trabalhos de tradução certifique-se que o arquivo em qual\nirá trabalhar esteja disponível para ser traduzido. Para isso, acesse a\n[planilha no Google Docs](https://docs.google.com/spreadsheets/d/1pAMe-qsKK0poEsQwGI2HLFmj4afKSkEUd_1qegU5YqQ).\n\n\nRegras e Observações\n--------------------\n\n- Alguns termos não tem uma tradução muito boa para o português, em casos como\n  esse convém escrever a palavra ou expressão em inglês primeiro em seguida sua\n  possível tradução em parênteses.\n- Se você acredita que alguma parte de sua tradução não faz sentido e você não\n  tem certeza de como traduzi-la corretamente. Coloque este bloco de texto em\n  *itálico*, isso ajudará na revisão.\n- Para reduzir erros de digitação você pode utilizar um editor de texto como o\n  MS Word para escrever pequenos blocos textos e em seguida copiar estes blocos\n  para um editor visual de Markdown como o https://dillinger.io/.\n\n### Convenções Para Tradução\n\n- action — ação\n- application system - sistema\n- project template — template de projetos\n- controller — controller (controlador)\n- eager loading — eager loading (carregamento na inicialização)\n- lazy loading — lazy loading (carregamento retardado)\n- model — model (modelo)\n- query builder — query builder (construtor de consulta)\n- view — view (visão)\n- note — observação\n- info — informação\n- tip — dica\n- warning - atenção\n- attribute label - label do atributo\n- inline action — ação inline\n- standalone action — ação standalone\n- advanced project template — template avançado de projetos\n- basic project template — template básico de projetos\n- behaviors — behaviors (comportamentos)\n- pretty URL — URL amigável (pretty URL)\n- class member variable - atributo da classe\n- endpoint - URL (também chamadas *endpoints*)\n\n### Termos Sem Tradução\n\n- active record\n- alias\n- cache\n- CamelCase, camel-case\n- core\n- framework\n- hash\n- helper\n- id\n- runtime\n- widget\n- backend\n- frontend\n- web service\n- template\n- query string\n- case-sensitive\n- case-insensitive\n- callback\n"
  },
  {
    "path": "docs/internals-ru/README.md",
    "content": "Документация для разработчиков Yii\n==================================\n\nВ этой директории собрана документация по разработке Yii Framework и процессу выпуска релизов.\n\nРуководство контрибьютора\n-------------------------\n\n- [Как сообщить о проблеме](report-an-issue.md)\n- [С чего начать](getting-started.md)\n- [Git-процесс для контрибьюторов Yii 2](git-workflow.md) - пошаговое руководство: настройка окружения и начало работы над Yii.\n- [Стиль кода ядра Yii 2](core-code-style.md)\n- [Стиль кода представлений Yii 2](view-code-style.md)\n\n\nДокументация\n------------\n\n- [Статус переводов](translation-status.md) - какие документы готовы к переводу.\n- [Команды переводчиков](translation-teams.md)\n- [Процесс перевода](translation-workflow.md)\n\n\nРазработка фреймворка\n---------------------\n\n- [Контроль качества pull request](pull-request-qa.md)\n- [Автоматизация](automation.md) - автоисправление стиля кода, генерация документации и файлов.\n- [Проектные решения](design-decisions.md) - FAQ по спорным вопросам, которые закрыты решением core team.\n\nВерсионирование и релизы\n------------------------\n\n- [Организация проекта](project-organization.md)\n- [Версионирование Yii](versions.md)\n- [Обратная совместимость](bc.md)\n- [Выпуск новой версии](release.md)\n\nРазное\n------\n\n### Иерархия исключений\n\n![Иерархия исключений Yii Framework](exception_hierarchy.png)\n\n### Тестирование баз данных\n\n[Здесь](https://gist.github.com/sergeymakinen/0696a5952f160ea28d7b64c3adfecf6f) находится конфигурация тестовых окружений для всех поддерживаемых Yii баз данных.\n"
  },
  {
    "path": "docs/internals-ru/automation.md",
    "content": "Автоматизация\n=============\n\nПри работе над Yii, некоторые задачи можно выполнять автоматически:\n\n- Генерация карты классов в файл `classes.php`, который находится в корневой директории фреймворка.\n  Запустите `./build/build classmap` для его генерации.\n\n- Генерация аннотаций `@property` в файлах классов, описывающих свойства, введённые геттерами и сеттерами.\n  Запустите `./build/build php-doc/property` для их обновления.\n\n- Исправление стиля кодирования и других мелких ошибок в комментариях phpdoc.\n  Запустите `./build/build php-doc/fix` для их исправления.\n  Проверьте изменения перед тем, как их закоммитить, так как могут быть нежелательные изменения, потому что команда не\n  является совершенной. Вы можете использовать `git add -p` для обзора изменений.\n\n- Обновление Mime Type Magic файла (`framework/helpers/mimeTypes.php`) из Apache HTTPd репозитория.\n  Запустите `./build/build mime-type` для обновления файла.\n\nВсе вышеуказанные команды включены в [процесс релиза](). Они могут быть запущены между релизами, но это не является обязательным.\n"
  },
  {
    "path": "docs/internals-ru/bc.md",
    "content": "# Обратная совместимость\n\nВ патч-релизах вида `2.x.y.Z` обратная совместимость строго не нарушается. В минорных релизах вида `2.x.Y` мы стараемся избегать несовместимых изменений, но исправлять их приходится.\n\nПодробнее о нумерации версий в [Версионирование Yii](versions.md).\n\n## Использование\n\n### Интерфейсы\n\nСценарий использования | BC?\n-----------------------|----\nType hint с интерфейсом | Да\nВызов метода интерфейса | Да\n**Реализация интерфейса и...** |\nРеализация метода | Да\nДобавление аргумента к реализованному методу | Да\nДобавление значения по умолчанию к аргументу | Да\n\n### Классы\n\nСценарий использования | BC?\n-----------------------|----\nType hint с классом | Да\nСоздание нового экземпляра | Да\nНаследование класса | Да\nДоступ к public свойству | Да\nВызов public метода | Да\n**Наследование класса и...** |\nДоступ к protected свойству | Да\nВызов protected метода | Да\nПереопределение public свойства | Да\nПереопределение protected свойства | Да\nПереопределение public метода | Да\nПереопределение protected метода | Да\nДобавление нового свойства | Нет\nДобавление нового метода | Нет\nДобавление аргумента к переопределённому методу | Да\nДобавление значения по умолчанию к аргументу | Да\nВызов private метода (через Reflection) | Нет\nДоступ к private свойству (через Reflection) | Нет\n\n\n## Разработка\n\n### Изменения интерфейсов\n\nТип изменения | BC?\n--------------|----\nУдаление | Нет\nИзменение имени или namespace | Нет\nДобавление родительского интерфейса | Да, если не добавляются новые методы\nУдаление родительского интерфейса | Нет\n**Методы интерфейса** |\nДобавление метода | Нет\nУдаление метода | Нет\nИзменение имени | Нет\nПеремещение в родительский интерфейс | Да\nДобавление аргумента без значения по умолчанию | Нет\nДобавление аргумента со значением по умолчанию | Нет\nУдаление аргумента | Да (только последних)\nДобавление значения по умолчанию к аргументу | Нет\nУдаление значения по умолчанию у аргумента | Нет\nДобавление type hint к аргументу | Нет\nУдаление type hint у аргумента | Нет\nИзменение типа аргумента | Нет\nИзменение типа возвращаемого значения | Нет\n**Константы** |\nДобавление константы | Да\nУдаление константы | Нет\nИзменение значения константы | Да, кроме объектов, которые могут сериализоваться. Обязательно документировать в UPGRADE.md.\n\n### Классы\n\nТип изменения | BC?\n--------------|----\nУдаление | Нет\nСделать final | Нет\nСделать abstract | Нет\nИзменение имени или namespace | Нет\nИзменение родительского класса | Да, но оригинальный родитель должен остаться предком класса.\nДобавление интерфейса | Да\nУдаление интерфейса | Нет\n**Public свойства** |\nДобавление public свойства | Да\nУдаление public свойства | Нет\nСужение видимости | Нет\nПеремещение в родительский класс | Да\n**Protected свойства** |\nДобавление protected свойства | Да\nУдаление protected свойства | Нет\nСужение видимости | Нет\nПеремещение в родительский класс | Да\n**Private свойства** |\nДобавление private свойства | Да\nУдаление private свойства | Да\n**Конструкторы** |\nУдаление конструктора | Нет\nСужение видимости public конструктора | Нет\nСужение видимости protected конструктора | Нет\nПеремещение в родительский класс | Да\n**Public методы** |\nДобавление public метода | Да\nУдаление public метода | Нет\nИзменение имени | Нет\nСужение видимости | Нет\nПеремещение в родительский класс | Да\nДобавление аргумента без значения по умолчанию | Нет\nДобавление аргумента со значением по умолчанию | Нет\nУдаление аргументов | Да, только последних\nДобавление значения по умолчанию к аргументу | Нет\nУдаление значения по умолчанию у аргумента | Нет\nДобавление type hint к аргументу | Нет\nУдаление type hint у аргумента | Нет\nИзменение типа аргумента | Нет\nИзменение типа возвращаемого значения | Нет\n**Protected методы** |\nДобавление protected метода | Да\nУдаление protected метода | Нет\nИзменение имени | Нет\nСужение видимости | Нет\nПеремещение в родительский класс | Да\nДобавление аргумента без значения по умолчанию | Нет\nДобавление аргумента со значением по умолчанию | Нет\nУдаление аргументов | Да, только последних\nДобавление значения по умолчанию к аргументу | Нет\nУдаление значения по умолчанию у аргумента | Нет\nДобавление type hint к аргументу | Нет\nУдаление type hint у аргумента | Нет\nИзменение типа аргумента | Нет\nИзменение типа возвращаемого значения | Нет\n**Private методы** |\nДобавление private метода | Да\nУдаление private метода | Да\nИзменение имени | Да\nДобавление аргумента без значения по умолчанию | Да\nДобавление аргумента со значением по умолчанию | Да\nУдаление аргумента | Да\nДобавление значения по умолчанию к аргументу | Да\nУдаление значения по умолчанию у аргумента | Да\nДобавление type hint к аргументу | Да\nУдаление type hint у аргумента | Да\nИзменение типа аргумента | Да\nИзменение типа возвращаемого значения | Да\n**Статические методы** |\nСделать нестатический статическим | Нет\nСделать статический нестатическим | Нет\n**Константы** |\nДобавление константы | Да\nУдаление константы | Нет\nИзменение значения константы | Да, кроме объектов, которые могут сериализоваться. Обязательно документировать в UPGRADE.md.\n"
  },
  {
    "path": "docs/internals-ru/blocktypes.json",
    "content": "{\n  \"Warning:\": \"Внимание:\",\n  \"Note:\": \"Примечание:\",\n  \"Info:\": \"Информация:\",\n  \"Tip:\": \"Подсказка:\"\n}\n"
  },
  {
    "path": "docs/internals-ru/core-code-style.md",
    "content": "Стиль кодирования Yii 2 framework\n=================================\n\nОписанный ниже стиль кодирования используется при разработке ядра Yii 2.x и его официальных расширений. Если вы хотите участвовать в разработке фреймворка, постарайтесь придерживаться данного стиля. Мы не принуждаем вас использовать этот стиль при разработке ваших приложений с использованием Yii 2. В данном случае можете использовать тот стиль, который вам больше подходит.\n\nПример конфигурационного файла для CodeSniffer вы можете найти здесь: https://github.com/yiisoft/yii2-coding-standards\n\n## 1. Обзор\n\nВ общем, мы используем совместимый со стандартом [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) стиль, так что все положения [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) вполне применимы к нашему стилю кодирования.\n\n- ДОЛЖНЫ использоваться только открывающие теги `<?php` или `<?=`;\n- В конце файла должна быть пустая строка;\n- Файлы с PHP кодом ДОЛЖНЫ содержать только символы в кодировке UTF-8 без BOM;\n- Для выравнивания кода НУЖНО использовать 4 пробела вместо табуляции;\n- Имена классов ДОЛЖНЫ быть объявлены используя `StudlyCaps`;\n- Константы класса ДОЛЖНЫ быть объявлены в верхнем регистре с подчеркиванием в качестве разделителей;\n- Имена методов ДОЛЖНЫ быть объявлены используя `camelCase`;\n- Имена свойств ДОЛЖНЫ быть объявлены используя `camelCase`;\n- Имена свойств ДОЛЖНЫ начинаться с подчеркивания если они объявлены с использованием модификатора `private`;\n- Всегда используйте `elseif` вместо `else if`.\n\n## 2. Файлы\n\n### 2.1. Теги PHP\n\n- В PHP коде ДОЛЖНЫ использоваться теги `<?php ?>` или `<?=`; НЕ ДОЛЖНЫ использоваться другие теги, такие как `<?`;\n- Если файл содержит только PHP код, тогда закрывающий тег `?>` не нужен;\n- Не добавляйте лишние пробелы в конец строки;\n- Все файлы, содержащие PHP код, должны иметь расширение `.php`.\n\n### 2.2. Кодировка символов\n\nPHP код должен содержать только символы в кодировке UTF-8 без BOM.\n\n## 3. Имена Классов\n\nИмена классов ДОЛЖНЫ быть определены используя `StudlyCaps`. Например, `Controller`, `Model`.\n\n## 4. Классы\n\nВ данном случае, под классом подразумеваются все классы и интерфейсы.\n\n- При именовании классов следует использовать `CamelCase`;\n- Открывающая фигурная скобка всегда должна быть на следующей строке после имени класса;\n- Все классы должны быть документированы в соответствии с PHPDoc;\n- Весь код класса должен быть выровнен с использованием 4 пробелов;\n- В одном PHP файле должен быть только один класс;\n- Все классы должны использовать пространства имен;\n- Имя класса должно совпадать с именем файла. Пространство имен класса должно соответствовать структуре каталогов.\n\n```php\n/**\n * Документация\n */\nclass MyClass extends \\yii\\base\\BaseObject implements MyInterface\n{\n    // код\n}\n```\n\n### 4.1. Константы\n\nКонстанты класса ДОЛЖНЫ быть объявлены в верхнем регистре с подчеркиванием в качестве разделителей.\nПример:\n\n```php\n<?php\nclass Foo\n{\n    const VERSION = '1.0';\n    const DATE_APPROVED = '2012-06-01';\n}\n```\n### 4.2. Свойства\n\n- При объявлении общедоступных (public) членов класса нужно использовать ключевое слово `public`;\n- Общедоступные и защищенные (protected) переменные должны быть объявлены в начале класса, раньше объявления методов;\n  Закрытые (private) переменные, так же, должны быть объявлены в начале класса, но могут быть добавлены и непосредственно перед методами, использующими их, в случае, если эти переменные используются только небольшой частью методов класса;\n- Порядок объявления свойств класса должен соответствовать их видимости: общедоступные, защищенные и закрытые;\n- Ограничений на порядок свойств одной области видимости нет;\n- Для улучшения читаемости, следует не оставлять пустых строк между объявлением свойств и оставлять две пустые строки между объявлениями переменных и методов. Между объявлениями свойств разной области видимости должна быть добавлена одна пустая строка;\n- Закрытые переменные должны иметь имя вида `$_varName`;\n- Общедоступные члены класса и отдельные переменные должны использовать `$camelCase`\n  с первой буквой в нижнем регистре;\n- Используйте понятные имена. Переменные вроде `$i` и `$j` лучше не использовать.\n\nПример:\n\n```php\n<?php\nclass Foo\n{\n    public $publicProp1;\n    public $publicProp2;\n\n    protected $protectedProp;\n\n    private $_privateProp;\n\n\n    public function someMethod()\n    {\n        // ...\n    }\n}\n```\n\n### 4.3. Методы\n\n- При именовании методов и функций следует использовать `camelCase` с первой буквой в нижнем регистре;\n- Имена должны быть понятными и отражать назначение функции;\n- Методы классов должны быть объявлены с использованием одного из модификаторов `private`, `protected` или\n  `public`. Использование `var` недопустимо;\n- Открывающая фигурная кавычка должна быть расположена на строке, следующей за строкой объявления функции.\n\n```php\n/**\n * Документация\n */\nclass Foo\n{\n    /**\n     * Документация\n     */\n    public function bar()\n    {\n        // код\n        return $value;\n    }\n}\n```\n\n### 4.4 Блоки Документации\n\n`@param`, `@var`, `@property` и `@return` должны описывать типы `bool`, `int`, `string`, `array` или `null`. Вы можете использовать и имена классов, например `Model` или `ActiveRecord`. Для типизированных массивов используйте `ClassName[]`.\n\n### 4.5 Конструкторы\n\n- Используйте `__construct` вместо старого стиля, применяемого в PHP 4.\n\n## 5 PHP\n\n### 5.1 Типы Данных\n\n- Все типы данных и значения PHP должны быть в нижнем регистре. Это относится и к `true`, `false`, `null` и `array`.\n\nИзменение типа существующей переменной считается плохой практикой. Постарайтесь не использовать такой подход, за исключением случаев, когда это действительно необходимо.\n\n\n```php\npublic function save(Transaction $transaction, $argument2 = 100)\n{\n    $transaction = new Connection; // плохо\n    $argument2 = 200; // хорошо\n}\n```\n\n### 5.2 Строки\n\n- Если строка не содержит переменных или одинарных кавычек, используйте одинарные кавычки;\n\n```php\n$str = 'Например так.';\n```\n\n- Если строка содержит одинарную кавычку, используйте двойные кавычки во избежание излишнего экранирования.\n\n#### Подстановка переменных\n\n```php\n$str1 = \"Привет, $username!\";\n$str2 = \"Привет, {$username}!\";\n```\n\nСледующий вариант запрещен:\n\n```php\n$str3 = \"Привет, ${username}!\";\n```\n\n#### Конкатенация\n\nПри конкатенации строк выделяйте точку пробелами:\n\n```php\n$name = 'Yii' . ' Framework';\n```\n\nДля длинных строк используйте следующий подход:\n\n```php\n$sql = \"SELECT *\"\n    . \"FROM `post` \"\n    . \"WHERE `id` = 121 \";\n```\n\n### 5.3 Массивы\n\nДля массивов мы используем краткий синтаксис, появившийся в PHP 5.4.\n\n#### Индексированные массивы\n\n- Не используйте отрицательные числа в качестве индексов массива.\n\nИспользуйте следующий стиль:\n\n```php\n$arr = [3, 14, 15, 'Yii', 'Framework'];\n```\n\nПри большом количестве элементов:\n\n```php\n$arr = [\n    3, 14, 15,\n    92, 6, $test,\n    'Yii', 'Framework',\n];\n```\n\n#### Ассоциативные массивы\n\nДля ассоциативных массивов используйте следующий стиль:\n\n```php\n$config = [\n    'name'  => 'Yii',\n    'options' => ['usePHP' => true],\n];\n```\n\n### 5.4 Управляющие конструкции\n\n- Оставляйте один пробел перед открывающей круглой скобкой и после закрывающей круглой скобки в управляющих конструкциях;\n- Операторы внутри круглых скобок должны разделяться пробелами;\n- Открывающая фигурная скобка должна быть на той же строке;\n- Закрывающая фигурная скобка должна быть на новой строке;\n- Всегда используйте фигурные скобки для однострочных выражений.\n\n```php\nif ($event === null) {\n    return new Event();\n}\nif ($event instanceof CoolEvent) {\n    return $event->instance();\n}\nreturn null;\n\n\n// такой код недопустим:\nif (!$model && null === $event)\n    throw new Exception('test');\n```\n\nСтарайтесь избегать использования `else` после `return` там, где это возможно.\nИспользуйте [граничные операторы](https://refactoring.guru/ru/replace-nested-conditional-with-guard-clauses).\n\n```php\n$result = $this->getResult();\nif (empty($result)) {\n    return true;\n} else {\n    // дальнейшие вычисления\n}\n```\n\nлучше переписать так:\n\n```php\n$result = $this->getResult();\nif (empty($result)) {\n    return true;\n}\n\n// дальнейшие вычисления\n```\n\n#### switch\n\nИспользуйте следующий стиль для switch:\n\n```php\nswitch ($this->phpType) {\n    case 'string':\n        $a = (string) $value;\n        break;\n    case 'integer':\n    case 'int':\n        $a = (int) $value;\n        break;\n    case 'boolean':\n        $a = (bool) $value;\n        break;\n    default:\n        $a = null;\n}\n```\n\n### 5.5 Вызовы функций\n\n```php\ndoIt(2, 3);\n\ndoIt(['a' => 'b']);\n\ndoIt('a', [\n    'a' => 'b',\n    'c' => 'd',\n]);\n```\n\n### 5.6 Анонимные (lambda) функции\n\nНе забывайте про пробелы между ключевыми словами `function`/`use` и открывающими круглыми скобками:\n\n```php\n// правильно\n$n = 100;\n$sum = array_reduce($numbers, function ($r, $x) use ($n) {\n    $this->doMagic();\n    $r += $x * $n;\n    return $r;\n});\n\n// неправильно\n$n = 100;\n$mul = array_reduce($numbers, function($r, $x) use($n) {\n    $this->doMagic();\n    $r *= $x * $n;\n    return $r;\n});\n```\n\nДокументация\n-------------\n\n- Для получения информации по синтаксису документации обратитесь к первоисточнику [PHPDoc](https://phpdoc.org/);\n- Код без документации недопустим;\n- Все файлы классов должны содержать блок документации в начале файла и блок документации непосредственно перед каждым классом;\n- Нет необходимости использовать тег `@return` если метод не возвращает значение;\n- Все виртуальные свойства классов, наследованных от `yii\\base\\BaseObject`, документируются тегом `@property` в блоке документации класса;\n  Аннотации геттеров и сеттеров автоматически генерируются из соответствующих тегов `@return` or `@param`\n  посредством выполнения команды `./build php-doc` в соответствующем каталоге;\n  Вы можете добавить дополнительный тег `@property` для геттера или сеттера для пояснения назначения переменной метода, если это необходимо.\n  Например:\n\n```php\n<?php\n/**\n * Returns the errors for all attribute or a single attribute.\n * @param string $attribute attribute name. Use `null` to retrieve errors for all attributes.\n * @property array An array of errors for all attributes. Empty array is returned if no error.\n * The result is a two-dimensional array. See [[getErrors()]] for detailed description.\n * @return array errors for all attributes or the specified attribute. Empty array is returned if no error.\n * Note that when returning errors for all attributes, the result is a two-dimensional array, like the following:\n * ...\n */\npublic function getErrors($attribute = null)\n```\n\n#### Файл\n\n```php\n<?php\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n```\n\n#### Класс\n\n```php\n/**\n * Component is the base class that provides the *property*, *event* and *behavior* features.\n *\n * @include @yii/docs/base-Component.md\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Component extends \\yii\\base\\BaseObject\n```\n\n\n#### Функция / метод\n\n```php\n/**\n * Returns the list of attached event handlers for an event.\n * You may manipulate the returned [[Vector]] object by adding or removing handlers.\n * For example,\n *\n * ```\n * $component->getEventHandlers($eventName)->insertAt(0, $eventHandler);\n * ```\n *\n * @param string $name the event name\n * @return Vector list of attached event handlers for the event\n * @throws Exception if the event is not defined\n */\npublic function getEventHandlers($name)\n{\n    if (!isset($this->_e[$name])) {\n        $this->_e[$name] = new Vector;\n    }\n    $this->ensureBehaviors();\n    return $this->_e[$name];\n}\n```\n\n#### Разметка\n\nКак вы можете видеть в примерах выше, мы используем специальную разметку для форматирования комментариев PHPDoc.\n\nНиже описан дополнительный синтаксис для описания связей между классами, методами и свойствами в документации:\n\n- `[[canSetProperty]]` создаст ссылку на метод или свойство `canSetProperty` этого класса;\n- `[[Component::canSetProperty]]` создаст ссылку на метод `canSetProperty` класса `Component` того же пространства имен;\n- `[[yii\\base\\Component::canSetProperty]]` создаст ссылку на метод `canSetProperty` класса `Component` в пространстве имен `yii\\base`;\n- `[[Component]]` создаст ссылку на класс `Component` в том же пространстве имен. Здесь так же возможно явное указание пространства имен.\n\nДля явного указания текста ссылки возможно использование следующего синтаксиса:\n\n```\n... as displayed in the [[header|header cell]].\n```\n\nЧасть до | это имя свойства, метода или класса для ссылки, а часто поле | это текст ссылки.\n\nТак же, возможно создание ссылок на Руководство:\n\n```markdown\n[Руководство](guide:file-name.md)\n[Раздел руководства](guide:file-name.md#subsection)\n```\n\n##### Примеры кода\n\nВ примерах кода должен использоваться синтаксис Markdown, но не должен указываться язык.\nУказание языка в примерах кода может привести к нарушению их отображения в некоторых IDE. Пример:\n\n```php\n/**\n * Correct code example:\n * \n * ```\n * $object->doMagic();\n * ```\n */\npublic function doMagic()\n{\n}\n\n/**\n * Incorrect code example:\n * \n * ```php\n * $object->doMagic();\n * ```\n */\npublic function doMagic()\n{\n}\n```\n\n\n#### Комментарии\n\n- Однострочные комментарии должны начинаться с `//`, а не с `#`;\n- Однострочные комментарии должны располагаться на отдельной строке.\n\nДополнительные правила\n----------------\n\n### `=== []` или `empty()`\n\nИспользуйте `empty()`, где это возможно.\n\n### Несколько точек возврата\n\nНе допускайте запутанных вложенных условных конструкций, используйте return. Для коротких методов это не актуально.\n\n### `self` или `static`\n\nВсегда используйте `static`, за исключением следующих случаев:\n\n- доступ к константам ДОЛЖЕН осуществляться через `self`: `self::MY_CONSTANT`;\n- доступ к защищенным статическим свойствам ДОЛЖЕН осуществляться через `self`: `self::$_events`;\n- допустимо использовать `self` для рекурсивного обращения к текущему классу, вместо класса наследника.\n\n### Значение \"ничего не делать\"\n\nСвойства указывающее компоненту на отсутствие необходимости что-либо делать, должны принимать значение `false`. `Null`, `''`, или `[]` не должны использоваться в таких случаях.\n\n### Каталоги/пространства имен\n\n- Используйте нижний регистр;\n- используйте множественное число для существительных, представляющих объекты (например валидаторы);\n- используйте единственное число для имен, представляющих соответствующий функционал (например web);\n- предпочтительнее использовать однословные пространства имён;\n- если одно слово использовать не удаётся, используйте camelCase стиль.\n"
  },
  {
    "path": "docs/internals-ru/design-decisions.md",
    "content": "Проектные решения\n=================\n\nВ этом документе перечислены проектные решения, принятые после обсуждений. Без веских причин менять их не стоит.\nЛюбые изменения должны быть согласованы с core-разработчиками.\n\n1. **[Когда поддерживать псевдонимы путей](https://github.com/yiisoft/yii2/pull/3079#issuecomment-40312268)** псевдонимы путей стоит поддерживать для свойств, которые настраиваются через конфигурацию, потому что это удобно. В остальных случаях поддержку псевдонимов стоит ограничивать.\n2. **Когда переводить сообщения** сообщения нужно переводить, когда они показываются конечному пользователю и имеют для него смысл. HTTP-статусы, исключения о коде и т.п. переводить не надо. Консольные сообщения всегда на английском из-за проблем с кодировками и кодовыми страницами.\n3. **[Добавление новых auth-клиентов](https://github.com/yiisoft/yii2/issues/1652)** для упрощения поддержки новые auth-клиенты в core-расширение не добавляются. Их нужно реализовывать как пользовательские расширения.\n4. **При использовании замыканий** рекомендуется **включать все передаваемые параметры** в сигнатуру, даже если не все используются. Так проще модифицировать или копировать код: вся информация видна сразу и не нужно искать доступные параметры в документации. ([#6584](https://github.com/yiisoft/yii2/pull/6584), [#6875](https://github.com/yiisoft/yii2/issues/6875))\n5. Предпочитать **int вместо unsigned int** в схеме базы данных. int можно представить в PHP как integer. Unsigned int на 32-битных системах пришлось бы хранить как строку. Хотя unsigned удваивает диапазон, если таблице нужно такое пространство, безопаснее использовать bigint или mediumint. <https://github.com/yiisoft/yii/pull/1923#issuecomment-11881967>\n6. [Помощники (helpers) vs отдельные нестатические классы](https://github.com/yiisoft/yii2/pull/12661#issuecomment-251599463)\n7. **Цепочки вызовов сеттеров** стоит избегать, если в классе есть методы, возвращающие значимые результаты. Цепочки допустимы, если класс является билдером, где все сеттеры меняют внутреннее состояние: https://github.com/yiisoft/yii2/issues/13026\n8. **Глобальный обработчик исключений/ошибок** используется вместо локального try-catch, потому что он надёжно перехватывает деструкторы и всё, что происходит за пределами метода `run()`, включая bootstrap. См. [#14348](https://github.com/yiisoft/yii2/issues/14348).\n"
  },
  {
    "path": "docs/internals-ru/getting-started.md",
    "content": "Подготовка к разработке Yii 2\n=====================================\n\nСмотрите [Рабочий процесс Git для разработчиков Yii 2](git-workflow.md) о том, как подготовить ваше рабочее окружение.\n"
  },
  {
    "path": "docs/internals-ru/git-workflow.md",
    "content": "Рабочий процесс Git для разработчиков Yii 2\n===========================================\n\nИтак, вы хотите разрабатывать Yii? Хорошо! Но для того, чтобы увеличить шанс принятия ваших изменений,\nпожалуйста, следуйте следующим шагам. Если вы новичок в git и Github, вы можете сначала проверить\n[Github help](https://help.github.com/), [try git](https://try.github.com) или прочитать о\n[внутренней модели данных git](https://nfarina.com/post/9868516270/git-is-simpler).\n\nПодготовка вашего рабочего окружения\n------------------------------------\n\nСледующие шаги создадут рабочее окружение для Yii, которое вы можете использовать для работы над основным кодом\nфреймворка. *Эти шаги будут нужны только в первый раз*.\n\n### 1. [Сделайте форк](https://help.github.com/fork-a-repo/) репозитория Yii на Github и клонируйте этот форк в ваше рабочее окружение.\n\n```\ngit clone git@github.com:YOUR-GITHUB-USERNAME/yii2.git\n```\n\nЕсли у вас есть проблемы с настройкой Git для работы с GitHub в Linux, или вы получаете ошибку похожую на \"Permission Denied\n(publickey)\", тогда вы должны настроить ваш Git по [этой инструкции](https://help.github.com/linux-set-up-git/)\n\n### 2. Добавьте главный репозиторий Yii как дополнительный внешний git репозиторий называемый \"upstream\"\n\nПерейдите в каталог куда вы склонировали Yii, как правило \"yii2\". Затем введите следующую команду:\n\n```\ngit remote add upstream https://github.com/yiisoft/yii2.git\n```\n\n### 3. Настройка тестовой среды <span id=\"prepare-the-test-environment\"></span>\n\nСледующие шаги не обязательны, если вы хотите работать только с переводом или документацией.\n\n- выполните `composer install` для установки зависимостей\n  (если [composer у вас установлен глобально](https://getcomposer.org/doc/00-intro.md#globally)).\n\nЕсли вы планируете работать с Javascript:\n\n- выполните `npm install` для установки зависимостей и программ тестирования Javascript \n  (если [Node.js and NPM установлен](https://nodejs.org/en/download/package-manager/)). \n  \n> Note: тесты JavaScript зависят от библиотеки jsdom, которой требуется Node.js 4 или более новый. \n> Предпочтительнее использовать Node.js 6 or 7. \n\n- выполните `php build/build dev/app basic` для клонирования базового приложения и установки его зависимостей.\n  Эта команда установит сторонние пакеты composer обычным образом, но создаст ссылку с репозитория yii2 на только\n  что загруженный репозиторий. Таким образом у вас будет только один экземпляр кода.\n\n  При необходимости делаем тоже самое для приложения advanced: `php build/build dev/app advanced`.\n\n  Данная команда также может быть использована для обновления зависимостей, внутри она использует `composer update`.\n\n> Note: по умолчанию URL репозиториев git на GitHub работают через SSH. Чтобы использовать HTTPS, добавьте\n> флаг `--useHttp` к команде `build`.\n\n**Теперь у нас есть рабочая площадка для экспериментов с Yii 2.**\n\nСледующие шаги не обязательны.\n\n### Модульные тесты\n\nВы можете выполнить модульные тесты с помощью команды `phpunit` в корневой директории приложения. Если у вас phpunit\nне установлен глобально, вы можете запустить `php vendor/bin/phpunit` или `vendor/bin/phpunit.bat` в случае \nиспользования OS Windows.\n\nНекоторые тесты требуют дополнительно установки и настройки баз данных. Вы можете создать `tests/data/config.local.php`\nдля переопределения настроек, которые определены в `tests/data/config.php`.\n\nВы можете ограничить тестирование группой тестов, с которой вы сейчас работаете. Например, запускать только тесты для\nвалидаторов и redis: `phpunit --group=validators,redis`. Вы можете получить список доступных групп выполнив `phpunit --list-groups`.\n\nВы можете запустить модульные тесты JavaScript с помощью команды `npm test` в корневой директории приложения. \n\n### Статический анализ\n\nМы используем [PHPStan](https://phpstan.org) для статического анализа. Он может быть запущен следующими командами:\n`php vendor/bin/phpstan` или `vendor\\bin\\phpstan.bat` в случае запуска на ОС Windows.\n\nПо умолчанию PHPStan будет использовать конфигурацию из `phpstan.dist.neon`. Вы можете создать файл\n`phpstan.neon` с вашей собственной конфигурацией, и PHPStan будет использовать его.\n\n#### Примечание\n\nВ аннотациях PHPDoc мы используем типы PHPStan. Если вам нужно указать другой тип для Psalm\n(такое обычно происходит в случае с дженериками), используйте аннотации Psalm в сочетании с\nаннотациями PHPStan.\n\nПример того, как это должно выглядеть:\n\n```php\n/**\n * @return Action<covariant static>|null\n * @phpstan-return Action<covariant static>|null\n * @psalm-return Action<self>|null\n */\npublic function createAction($id)\n{\n...\n}\n```\n\n### Расширения <span id=\"extensions\"></span>\n\nДля работы над расширениями вы можете склонировать репозиторий расширения. Мы сделали команду, которая поможет вам\nсделать это:\n\n```\nphp build/build dev/ext <extension-name> <fork>\n```\n\nгде `<extension-name>` это имя расширения, например `redis` и `<fork>` это URL вашего форка расширения, например\n`git@github.com:my_nickname/yii2-redis.git`. Если вы контрибьютор ядра фреймворка, вы можете не указывать `<fork>`.\n\nЕсли вы хотите протестировать расширение в одном из шаблонов приложений, просто добавьте его в `composer.json`\nприложения, например добавив `\"yiisoft/yii2-redis\": \"~2.0.0\"` в секцию `require` базового приложения.\nЗапустите `php build/build dev/app basic` для установки расширения и его зависимостей и создания символической\nссылки на `extensions/redis` так чтоб вы работали не папке вендорных пакетов composer, а напрямую в репозитории yii2.\n\n> Note: по умолчанию URL репозиториев git на GitHub работают через SSH. Чтобы использовать HTTPS, добавьте\n> флаг `--useHttp` к команде `build`.\n\nРабота над багами и новыми функциями\n------------------------------------\n\nПриготовив вашу среду разработки вы можете начать работу над исправлением багов и разработкой новых функций.\n\n### 1. Убедитесь, что issue создана для того, над чем вы работаете, если потребуется много времени для исправления\n\nВсе новые функции и баги должны быть связаны с issue для того, чтоб иметь единое место для обсуждения и документирования.\nПотратьте несколько минут на поиск существующей issue, которая соответствует вашим изменениям. Если вы найдёте её в\nсписке, то, пожалуйста, оставьте комментарий, что вы начали над ней работать. Если вы не нашли нужной issue, пожалуйста,\n[создайте новую issue](report-an-issue.md) или создайте сразу запрос на изменения, если это простые изменения.\nЭто позволит команде проверить ваше предложение и обеспечить соответствующую обратную связь.\n\n> Для небольших изменений или документации, или простых исправлений вам нет необходимости создавать issue,\n  запрос на изменения в этом случае подходит лучше.\n\n### 2. Получите последний код из основного репозитория Yii\n\n```\ngit pull upstream master\n```\n\nВы должны начинать с этого действия работу над каждым новым предложением. Убедитесь, что вы работаете с самой\nпоследней версией кода.\n\n### 3. Сделайте новую ветку для ваших изменений, основанных на текущей ветке master в Yii\n\n> Это очень важно, так как вы не сможете отправлять более одного запроса на изменения из вашего репозитория, если\n  будете использовать ветку master.\n\nКаждое отдельное исправление или изменение должно разрабатываться в своей ветке. Имя ветки должно быть описательным\nи начинаться с номера issue, с которым связан ваш код. Если вы не исправляете какой-то конкретный issue, просто\nпропустите номер.\nНапример:\n\n```\ngit checkout upstream/master\ngit checkout -b 999-name-of-your-branch-goes-here\n```\n\n### 4. Делайте свою магию, пишите свой код\n\nУбедитесь, что он работает :)\n\nМодульные тесты всегда приветствуются. Протестированный и с хорошим покрытием код значительно упрощает задачу проверки\nвашего предложения. Сломанные модульные тесты как описание проблемы тоже приветствуются.\n\n### 5. Обновите CHANGELOG\n\nОтредактируйте файл CHANGELOG, включив ваши изменения. Для этого нужно вставить строки в начало файла под заголовком\n\"Work in progress\", строки с описанием изменений должны выглядеть похожими на следующие:\n\n```\nBug #999: a description of the bug fix (Your Name)\nEnh #999: a description of the enhancement (Your Name)\n```\n\n`#999` это номер issue описывающей `Bug` или `Enh`.\nЛог изменений должен быть сгруппирован по типу (`Bug`,`Enh`) и отсортирован по номеру issue.\n\nДля очень маленьких исправлений, например опечаток и изменений документации, нет необходимости обновлять CHANGELOG.\n\n### 6. Зафиксируйте изменения\n\nДобавляем файлы/изменения которые вы хотите зафиксировать в [staging area](https://git.github.io/git-reference/basic/#add):\n\n```\ngit add path/to/my/file.php\n```\n\nВы можете использовать опцию `-p` для того, чтоб выбрать, какие изменения вы хотите добавить в коммит.\n\nФиксируйте ваши изменения с описательным сообщением. Убедитесь, что в сообщении есть номер `#XXX`, так Github\nавтоматически свяжет ваш коммит с issue:\n\n```\ngit commit -m \"A brief description of this change which fixes #999 goes here\"\n```\n\n### 7. Получите последний код из upstream Yii в вашу ветку\n\n```\ngit pull upstream master\n```\n\nЭто гарантирует, что вы используете самый последний код в вашей ветке перед отправкой запроса на изменения. Если есть\nкакие-либо конфликты слияния, вы должны исправить их и зафиксировать изменения ещё раз. Это гарантирует, что команда Yii\nсможет слить ваши изменения одним кликом.\n\n### 8. Разрешив конфликты, отправьте код на Github\n\n```\ngit push -u origin 999-name-of-your-branch-goes-here\n```\n\nОпция `-u` сохранит указание на ветку в Github, чтобы при следующем выполнении `git push`, git знал, куда отправлять\nизменения. Это полезно, если вы хотите позже отправлять ещё коммиты в этот запрос на слияние.\n\n### 9. Откройте [запрос на слияние](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) в *upstream*.\n\nПерейдите в репозиторий на Github и нажмите \"Pull Request\", выберите ветку справа и введите больше информации\nв поле комментариев. Чтобы связать запрос на изменение с issue, добавьте в комментарий `#999` - где 999 это номер issue.\n\n> Обратите внимание, что каждый запрос на слияние должен исправлять единственное изменение. Для множества независимых\n  изменений, пожалуйста, откройте несколько запросов на слияние.\n\n### 10. Проверка вашего кода кем-то\n\nКто-то будет проверять ваш код, и, возможно, вам будет предложено внести некоторые изменения. Eсли это так, то перейдите\nк шагу #6 (вам не надо открывать новый запрос на слияние, если текущий ещё открыт). Если код будет принят, он будет\nвлит в основную ветку и станет частью следующего релиза Yii. Если нет - не унывайте, разным людям необходимы различные\nфункции, и Yii не может реализовывать всё для всех, ваш код будет ещё доступен на Github как ссылка для людей кому он\nможет пригодится.\n\n### 11. Очистка\n\nПосле того, как код был принят или отклонён, вы можете удалить ветки, над которыми вы работали в локальном репозитории\nи в `origin`.\n\n```\ngit checkout master\ngit branch -D 999-name-of-your-branch-goes-here\ngit push origin --delete 999-name-of-your-branch-goes-here\n```\n\n### Примечание\n\nДля обнаружения регрессии как можно раньше, каждое слияние кодовой базы Yii на Github будет подхвачено\n[Travis CI](https://travis-ci.com) для автоматического запуска тестов. Люди из *core team* не хотят нагружать\nэтот сервис, поэтому добавляют текст [`[ci skip]`](https://docs.travis-ci.com/user/customizing-the-build/#Skipping-a-build) в описание\nзапроса на слияние, в следующих ситуациях:\n\n* затронуты только JavaScript, CSS файлы или файлы изображений,\n* обновление документации,\n* изменения затрагивают только строки (например обновление переводов).\n\nЭто защитит Travis CI от запуска тестов на изменениях, которые не покрыты тестами.\n\n### Обзор команд (для продвинутых участников)\n\n```\ngit clone git@github.com:YOUR-GITHUB-USERNAME/yii2.git\ngit remote add upstream https://github.com/yiisoft/yii2.git\n```\n\n```\ngit fetch upstream\ngit checkout upstream/master\ngit checkout -b 999-name-of-your-branch-goes-here\n\n/* ваша магия, обновление changelog если нужно */\n\ngit add path/to/my/file.php\ngit commit -m \"A brief description of this change which fixes #999 goes here\"\ngit pull upstream master\ngit push -u origin 999-name-of-your-branch-goes-here\n```\n"
  },
  {
    "path": "docs/internals-ru/project-organization.md",
    "content": "Организация проекта\n===================\n\nЭтот документ описывает организацию репозиториев для разработки Yii 2.\n\n1. Отдельные core-расширения и шаблоны приложений хранятся в независимых GitHub-проектах в организации [yiisoft](https://github.com/yiisoft).\n\n   Имена репозиториев расширений начинаются с `yii2-`, например `yii2-gii` для расширения `gii`. Имя пакета composer совпадает с путём на GitHub, например `yiisoft/yii2-gii`.\n\n   Имена репозиториев шаблонов приложений начинаются с `yii2-app-`, например `yii2-app-basic` для шаблона `basic`. Имя пакета composer совпадает с путём на GitHub, например `yiisoft/yii2-app-basic`.\n\n   Каждый проект расширения/приложения:\n\n   * хранит документацию в папке \"docs\". API-документация генерируется автоматически при релизе.\n   * хранит тесты в папке \"tests\".\n   * хранит переводы и другие мета-файлы.\n   * ведёт учёт issues через свой GitHub-проект.\n\n   Расширения релизятся независимо по мере необходимости. Шаблоны приложений релизятся вместе с фреймворком. Подробности в [политике версионирования](versions.md).\n\n2. Проект `yiisoft/yii2` - основной репозиторий для разработки Yii 2. Предоставляет composer-пакет [yiisoft/yii2-dev](https://packagist.org/packages/yiisoft/yii2-dev). Содержит код ядра фреймворка, модульные тесты, полное руководство и инструменты для разработки и релиза.\n\n   Баги и запросы на функции ядра отслеживаются в issue tracker этого проекта.\n\n3. Репозиторий `yiisoft/yii2-framework` - read-only git subsplit директории `framework` из dev-репозитория. Предоставляет composer-пакет [yiisoft/yii2](https://packagist.org/packages/yiisoft/yii2), который и является официальным пакетом для установки фреймворка.\n\n4. Для разработки приложения и расширения можно включить в структуру dev-проекта командой [build dev/app](git-workflow.md#prepare-the-test-environment).\n"
  },
  {
    "path": "docs/internals-ru/pull-request-qa.md",
    "content": "Контроль качества pull request\n==============================\n\nПри проверке PR на возможность слияния нужно учитывать следующие критерии:\n\n- К PR должна быть привязана issue, либо в PR должно быть понятное описание того, что исправляется или добавляется.\n- Модульные тесты. Не обязательно, но приветствуется. Тесты должны падать без кода, который PR исправляет.\n- Запись в CHANGELOG. Добавляется в секцию следующего релиза, сортируется по типу issue и номеру.\n  Никнеймы ответственных должны быть указаны.\n- [Стиль кода](core-code-style.md) и [стиль кода представлений](view-code-style.md) должны соблюдаться. Их можно поправить при слиянии, если тот, кто сливает, предпочитает так делать.\n"
  },
  {
    "path": "docs/internals-ru/release.md",
    "content": "Выпуск новой версии\n===================\n\nСписок шагов для выпуска релиза фреймворка со временем вырос и стал сложным для ручного управления, поэтому мы создали консольный инструмент, чтобы ни один шаг не был пропущен.\n\nОбзор шагов релиза\n-------------------\n\n- ...\n\nКоманда release\n---------------\n\nЭти шаги автоматизированы в [консольной команде release](../../build/controllers/ReleaseController.php), которая включена в репозиторий разработки фреймворка.\n\nКоманду release можно вызвать через Yii-приложение в директории `build`:\n\n    ./build/build help release  # запускать в корне репозитория фреймворка\n\n> Info: Команду можно запустить с опцией `--dryRun`, чтобы посмотреть, что она сделает. С этой опцией никакие изменения не будут внесены, коммиты и теги не будут созданы или отправлены.\n\n### Требования\n\nКоманда release зависит от окружения разработки, описанного в [документе по Git Workflow](git-workflow.md#extensions): шаблоны приложений должны находиться в `/apps/`, а расширения в `/extensions/`. Эта структура создаётся командами `dev/app` и `dev/ext`.\n\nНапример, установка расширения:\n\n    ./build/build dev/ext authclient\n\nили приложения:\n\n    ./build/build dev/app basic\n\nТакая установка гарантирует, что расширение будет использовать тот же код фреймворка, что и в текущем состоянии репозитория.\n\n### Обзор версий\n\nДля обзора версий фреймворка и расширений:\n\n    ./build/build release/info\n\nМожно запустить с `--update`, чтобы подтянуть теги всех репозиториев и получить актуальную информацию.\n\n### Выпуск релиза\n\nРелиз фреймворка включает следующие команды (приложения всегда релизятся вместе с фреймворком):\n\n    ./build/build release framework\n    ./build/build release app-basic\n    ./build/build release app-advanced\n\nРелиз расширения - одна команда (например, для redis):\n\n    ./build/build release redis\n\nПо умолчанию команда release выпускает новый минорный релиз из текущей ветки. Чтобы выпустить другую версию, укажите её через опцию `--version`, например `--version=2.1.0` или `--version=2.1.0-beta`.\n\n\n#### Релиз новой мажорной версии, например 2.1.0\n\nРелиз новой мажорной версии включает смену ветки, как описано в [политике версионирования](versions.md). Ниже пример выпуска версии `2.1.0`, которая разрабатывалась в ветке `2.1`, ответвлённой от `master`. Ветка `master` до этого содержала версии `2.0.x`.\n\n- создать новую ветку `2.0` из `master`\n- убедиться, что composer.json в этой ветке больше не содержит branch alias\n- слить необходимые изменения из `master` в `2.1`\n- указать `master` на последний коммит ветки `2.1`\n- обновить branch alias в composer.json для master на `2.1.x-dev`\n- удалить ветку `2.1`\n\nТеперь переключиться на `master` и запустить команду release с опцией `--version=2.1.0`.\n"
  },
  {
    "path": "docs/internals-ru/report-an-issue.md",
    "content": "Сообщение о проблеме\n====================\n\nПожалуйста, придерживайтесь рекомендаций приведенных ниже при создании вопроса, чтобы ускорить его решение:\n\n* Подавайте информацию включая: версию PHP и Yii, тип операционной системы и веб-сервера, тип и версию браузера.\n* При наличии предоставляйте **полный** вывод об ошибке. Скриншот, объясняющий проблему, очень приветствуется.\n* Опишите шаги, приводящие к ошибке. Было бы еще лучше, предоставить код для воспроизведения проблемы.\n* Если возможно создайте unit-тест и [пришлите его как pull request](git-workflow.md).\n\nЕсли проблема связана с одним из официальных расширений, пожалуйста, сообщайте о ней в репозиторий этого расширения.\nЕсли вы не уверены, [сообщайте в главном репозитории](https://github.com/yiisoft/yii2/issues/new) (<https://github.com/yiisoft/yii2/issues>).\n\n**Не сообщайте о проблеме, если:**\n\n* Вы хотите спросить как использовать ту или иную возможность Yii. Для этого пользуйтесь [форумом](https://forum.yiiframework.com/index.php/forum/42-general-discussions-for-yii-20/) или [чатом](https://www.yiiframework.com/chat/).\n* Проблема касается безопасности. Пожалуйста, [обращайтесь непосредственно к разработчикам](https://www.yiiframework.com/security/) для сообщения о проблемах с безопасностью.\n\n**Избегайте дублирования вопросов**\n\nПеред тем, как сообщать о проблеме, пожалуйста, совершите поиск среди [существующих вопросов](https://github.com/yiisoft/yii2/issues),\nвозможно о проблеме уже сообщили или проблема уже решена. Также убедитесь, что имеете последнюю версию Yii и в случае обновления ещё раз проверьте наличие проблемы.\n"
  },
  {
    "path": "docs/internals-ru/translation-status.md",
    "content": "Статус документации\n===================\n\nВсе документы готовы к переводу.\n"
  },
  {
    "path": "docs/internals-ru/translation-teams.md",
    "content": "Команды переводчиков\n====================\n\nБразильский португальский\n-------------------------\n\n- **Davidson Alencar, [@davidsonalencar](https://github.com/davidsonalencar), davidson.t.i@gmail.com**\n- [@wbraganca](https://github.com/wbraganca)\n- Alan Michel Willms Quinot, [@alanwillms](https://github.com/alanwillms), dyulax@gmail.com\n\nКитайский\n---------\n\n- **Paris Qian Sen 东方孤思子,[@qiansen1386](https://github.com/qiansen1386),qiansen1386@gmail.com**\n- [@AbrahamGreyson 刘阳](https://github.com/AbrahamGreyson)\n- [@fmalee](https://github.com/fmalee)\n- [@funson86 花生](https://github.com/funson86)\n- [@ivantree 长兴苗木](https://github.com/ivantree)\n- [@netyum 未来](https://github.com/netyum)\n- [@riverlet 小河](https://github.com/riverlet)\n- [@yiichina 巡洋舰](https://github.com/yiichina)\n\nФинский\n-------\n\n- Jani Mikkonen, [@janisto](https://github.com/janisto), janisto@php.net\n\nНемецкий\n--------\n\n- Carsten Brandt, [@cebe](https://github.com/cebe), mail@cebe.cc\n\nИтальянский\n-----------\n\n- Lorenzo Milesi, [@maxxer](https://github.com/maxxer), maxxer@yetopen.it\n\nЯпонский\n--------\n\n- Nobuo Kihara 木原伸夫, [@softark](https://github.com/softark), softark@gmail.com\n- Tomoki Morita, [@jamband](https://github.com/jamband), tmsongbooks215@gmail.com\n- Hisateru Tanaka, [@tanakahisateru](https://github.com/tanakahisateru), tanakahisateru@gmail.com\n\nРусский\n-------\n\n- **Alexander Makarov, [@samdark](https://github.com/samdark), sam@rmcreative.ru**\n- [@MUTOgen](https://github.com/MUTOgen)\n- [@prozacUa](https://github.com/prozacUa)\n\nИспанский\n---------\n\n- Luciano Baraglia, [@lucianobaraglia](https://github.com/lucianobaraglia)\n- Marco Da Silva, [@markmarco16](https://github.com/markmarco16), markmarco16@gmail.com\n- Daniel Gómez Pan [@pana1990](https://github.com/pana1990), pana_1990@hotmail.com\n\nУкраинский\n----------\n\n- **Alexandr Bordun [@borales](https://github.com/Borales), admin@yiiframework.com.ua**\n- Roman Bahatyi [@RichWeber](https://github.com/RichWeber), rbagatyi@gmail.com\n- Igor Zozulinskyi [@3y3ik](https://github.com/3y3ik)\n- Vadym Chenin [@vchenin](https://github.com/vchenin), vchenin@meta.ua\n"
  },
  {
    "path": "docs/internals-ru/translation-workflow.md",
    "content": "Как работать над переводом на русский\n=====================================\n\nYii переводится на множество языков, в том числе и на русский. Перевод включает в себя документацию и сообщения.\n\nСообщения фреймворка\n--------------------\n\nЕсть два типа сообщений: исключения, которые нацелены на разработчиков и не переводятся, и сообщения, которые показываются\nпользователям. Например, ошибки валидации.\n\nДля того, чтобы обновить перевод:\n\n1. Открываем в консоли директорию `framework`, запускаем `./yii message/extract @yii/messages/config.php --languages=ru`.\n3. Переводим сообщения в `framework/messages/ru/yii.php`. Важно чтобы файлы были в кодировке UTF-8.\n4. [Делаем pull request](https://github.com/yiisoft/yii2/blob/master/docs/internals-ru/git-workflow.md) с переводом из `ru`,\nостальные языки не трогаем.\n\nВ файле перевода находится массив. Его ключи — исходные строки, значения — перевод. Если значение пусто, сообщение\nсчитается не переведённым. Переводы сообщений, которые больше не встречаются в коде, обрамлены `@@`. Для некоторых сообщений\nнеобходимо использовать [специальный формат для поддержки употребления с числительными](../guide-ru/tutorial-i18n.md).\n\nДокументация\n------------\n\nПеревод документации находится в `docs/<original>-ru`, где `<original>` соответствует оригинальной директории, например\n`guide` или `internals`.\n\nЕсли перевод документа завершён, можно получить diff изменений в оригинале со времени последнего перевода, открыв консоль\nв директории `build` и выполнив:\n\n```\nphp build translation \"../docs/guide\" \"../docs/guide-ru\" \"Russian guide translation report\" > report_guide_ru.html\n```\n\nЕсли ругается на composer, выполните `composer install` в корневой директории.\n\nИнформацию о синтаксисе и стиле документации можно найти в [documentation_style_guide.md](../documentation_style_guide.md).\n\nПеред тем, как начать перевод, убедитесь, что никто им ещё не занимается и запишите себя в\n[список всех переводимых документов](https://docs.google.com/spreadsheets/d/1uxV0LwmR-8XXqlT8C6VqWllZjuoyIj-UkYpAQPWyUzE/edit?usp=sharing).\n\nВсе изменения оформляем в виде [pull request](https://github.com/yiisoft/yii2/blob/master/docs/internals-ru/git-workflow.md).\n\n\n### Общие правила\n\n- Многие термины не имеют однозначного и широко распространенного перевода на русский язык, поэтому, если в тексте\n  встречается такой термин, в скобках возле первого упоминания необходимо указать английский вариант; (список используемых\n  вариантов перевода терминов см. ниже); \n- Если кажется, что при переводе какая-то часть текста теряет смысл и вы не уверены в том, как ее правильно перевести,\n  заключайте эту часть текста в * (внешне шрифт станет наклонным). Это позволит при вычитке/корректуре обратить на эту\n  часть текста особое внимание; \n- При переводе избегайте фактических ошибок! \n- В тексте встречаются ссылки на внешние источники, если ссылка ведет на статью, определение термина и т.п., то при\n  наличии русского варианта на этом же ресурсе или ином авторитетном ресурсе, даем ссылку на русский вариант.\n  Например `https://en.wikipedia.org/wiki/Captcha` → `https://ru.wikipedia.org/wiki/Captcha`. \n- Комментарии в коде переводятся, если не искажают первоначального смысла; временные комментарии в тексте желательно\n  использовать только локально! иначе есть вероятность попадания в релиз; \n- При переводе названий разделов придерживаемся перевода в `README.md`; \n- Добавление собственных комментариев-дополнений возможно, но не приветствуется поскольку во избежание хаоса оригинал\n  должен быть один. В случае такой необходимости в конце комментария нужно добавить \"(прим. пер.)\"; \n- После проведения общей правки документа настоятельно рекомендуется самостоятельно вносить исправления только\n  грамматических, а также фактических ошибок, имеющих отношение только к данному разделу. В остальных случаях необходимо\n  вынести предложение по исправлению, улучшению на обсуждение и в случае необходимости централизованно внести коррективы\n  во все разделы документа.\n   \n\n### Структура документа\n\nПри переводе необходимо правильно именовать структурные единицы документы. Следуем структуре, приведенной ниже:\n\n- Глава 1 \n  - Раздел 1 \n  - Раздел 2 \n    - Подраздел 1 \n  - ... \n  - Раздел N \n- Глава 2 \n- ... \n- Глава N\n \n### Перевод специальных сообщений\n\nСпециальные сообщения `Tip:`, `Note:`, `Info:`, `Warning:` не переводятся.\n\n### Перевод рисунков\n\nРисунки к документации содержатся в подпапке `images`. Все они созданы в [yED](https://www.yworks.com/en/products_yed_about.html).\nПри необходимости перевода исходный файл копируется в директорию `images` перевода, переводится и сохраняется в формате png.\n\nПодписи к рисункам переводятся.\n\n### Грамматика\n\n\nОбращайте внимание на общую стилистику, орфографию и пунктуацию, перед заливкой конечного варианта перевод можно прогнать\nчерез любую программу с встроенной проверкой, например, Microsoft Word;\n\n### Список терминов\n\n- action — действие. \n- active record — без перевода. \n- attach handler — «назначить обработчик». \n- attribute of the model — атрибут модели.\n- cache — кэш.\n- camel case — без перевода. \n- customization — (тонкая) настройка //Ранее встречался перевод \"кастомизация\", желательно этот вариант по возможности не использовать. \n- column — столбец (если речь про БД).\n- configuration — конфигурация.  \n- content — содержимое. \n- controller — контроллер. \n- debug (mode) — отладочный (режим) (см. production mode). \n- eager loading — метод жадной загрузки/жадная загрузка (см. lazy loading).\n- environment — окружение.\n- PHP extension — расширение PHP. \n- field (of the table) — поле (или атрибут) таблицы. \n- framework — фреймворк. \n- front-controller — фронт-контроллер. \n- getter — геттер. \n- (event) handler — обработчик (события). \n- hash — хэш. \n- helper - помощник. \n- id — идентификатор. \n- instance — экземпляр. \n- junction table — промежуточная таблица.\n- lazy loading — отложенная загрузка (загрузим как понадобится и не раньше). \n- method — метод (объекта) //Внимание! У объекта/класса нет функций, есть только методы. \n- model — модель, модель данных. \n- model form — модель формы. \n- parameter — параметр (у метода или функции, никак не у класса). \n- to parse — обрабатывать, если контекст непонятен — парсить. \n- placeholder — маркер. \n- production (mode) — производственный (режим) (см. debug mode). \n- property — свойство (объекта). \n- to render — рендерить, формировать. \n- related, relation — связанный, связь.\n- resolve request — предварительная обработка запроса. \n- route — маршрут. \n- row (of the table) — строка (таблицы). \n- setter — сеттер. \n- tabular input — табличный ввод. \n- to validate — проверять. \n- valid — корректный. \n- validator — валидатор. \n- validator class — класс валидатора. \n- view — представление.\n- query builder — конструктор запросов.\n- time zone — часовой пояс.\n- to trigger — инициализировать\n- event — событие\n- to implement (class implements interface) — реализовывать (класс реализует интерфейс)\n"
  },
  {
    "path": "docs/internals-ru/versions.md",
    "content": "Версионирование Yii\n===================\n\nЭтот документ описывает политику версионирования Yii. Текущая стратегия - вариант [Semantic Versioning](https://semver.org/).\n\nВнутри core team мы неоднократно подчёркивали важность обратной совместимости в релизах 2.0.x. Но это идеальный план. На практике добиться этого сложно. Подробнее в документе [Обратная совместимость](bc.md).\n\nКоротко, политика версионирования Yii 2:\n\n## Номера версий\n\nНомера версий имеют формат `2.x.y.z`, где `z` опускается, если равен `0`.\n\nВозможная версия Yii 3 здесь не рассматривается, т.к. ожидается переход аналогичный 2.0 после 1.0. Такое происходит раз в 3-5 лет, в зависимости от развития внешних технологий (например, переход PHP с 5.0 на 5.4).\n\n### `2.X.0`: мажорные релизы\n\nРелизы с нарушением обратной совместимости. Содержат крупные функции и изменения, которые могут сломать BC. Обновление с предыдущих версий может быть нетривиальным, но полное руководство по обновлению предоставляется.\n\n* Основное содержание - новые функции и исправления багов\n* Включают минорные функции и баг-фиксы из патч-релизов\n* Могут содержать BC-ломающие изменения, записанные в `UPGRADE-2.X.md`\n* Цикл релиза - около 12 месяцев и более\n* Требуют пре-релизы: `2.X.0-alpha`, `2.X.0-beta`, `2.X.0-rc`\n* Требуют новостных публикаций и маркетинговых усилий\n\n\n### `2.x.Y`: минорные релизы\n\nМинорные релизы, в основном совместимые с предыдущими версиями. В идеале содержат только изменения, не затрагивающие обратную совместимость. Но не всегда получается сохранить 100% BC, поэтому заметки об обновлении записываются в `UPGRADE.md`. На практике, поскольку 2.0.x выходят чаще, мы добавляем в них и минорные функции, чтобы пользователи могли использовать их раньше.\n\n* Основное содержание - исправления багов и улучшения\n* Должны быть в основном обратно совместимы для безболезненного обновления. Допускается несколько исключений, задокументированных в `UPGRADE.md`\n* Цикл релиза - 1-2 месяца\n* Пре-релизы (alpha, beta, RC) не требуются\n* Должны постоянно сливаться обратно в master (минимум раз в неделю вручную)\n* Сопровождаются новостными публикациями. Сайт проекта обновляется\n\n\n### `2.x.y.Z`: патч-релизы\n\nПатч-релизы, которые должны быть на 100% обратно совместимы. Содержат только исправления багов. Новостные публикации и обновление сайта не требуются (если нет крупных/security исправлений). Процесс релиза в основном автоматизирован.\n\n* Содержат только исправления багов, без новых функций\n* Должны быть на 100% обратно совместимы. Единственное исключение - security-проблемы, которые могут потребовать нарушения BC\n* Цикл релиза - 1-2 недели\n* Пре-релизы (alpha, beta, RC) не требуются\n* Должны сливаться обратно в master при релизе\n\n\n## Политика ветвления\n\n* Ветка `master` - ветка разработки текущего стабильного мажорного релиза, сейчас версии `2.0.x`.\n* Каждый новый мажорный релиз разрабатывается в ветке с именем версии, например `2.1`.\n* Когда новый мажорный релиз `2.n` готов, из `master` создаётся ветка поддержки `2.(n-1).x`. Т.е. ветка `2.0` создаётся когда версия `2.1.0` выпускается как стабильная и далее разрабатывается в `master` (ср. [схема именования веток composer](https://getcomposer.org/doc/02-libraries.md#branches)).\n* Для патч-релизов создаются теги `2.x.y.z` и ветка `2.x.y`. Для релизов `2.x.y.0` ноль опускается.\n* Изменения из веток поддержки `2.n.x` постоянно сливаются обратно в `master`.\n\nСхема ветвления на примере истории коммитов:\n\n![Политика ветвления](versions-branches.png)\n\n\n## Релизы\n\nФреймворк и официальные расширения релизятся независимо друг от друга, т.е. расхождение номеров версий между фреймворком и расширением - ожидаемая ситуация. Шаблоны приложений всегда релизятся вместе с фреймворком.\n\nЦиклы релиза, описанные выше, относятся только к ядру фреймворка. Расширения релизятся по мере необходимости. Расширение может долго не получать новых релизов, если в нём нет исправлений или улучшений.\n"
  },
  {
    "path": "docs/internals-ru/view-code-style.md",
    "content": "Стиль кодирования представлений Yii 2\n=====================================\n\nДанный стиль кодирования используется для представлений в ядре Yii 2.x и официальных представлениях. Мы не заставляем\nвас использовать данный стиль кодирования для ваших приложений. Не стесняйтесь использовать тот стиль, который вам\nбольше подходит.\n\n```php\n<?php\n// Открывающий PHP тег должен быть в каждом файле шаблона. Пустая строка после открывающего тега также необходима.\n\n// Описывайте входные переменные, переданные сюда контроллером.\n/**\n * @var \\yii\\base\\View $this\n * @var \\yii\\widgets\\ActiveForm $form\n * @var \\app\\models\\Post[] $posts\n * @var \\app\\models\\ContactMessage $contactMessage\n */\n// Пустая строка ниже необходима.\n\n// Описание классов с пространствами имён.\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n// Пустая строка ниже необходима.\n\n// Установка свойств контекста, вызов сеттеров и другие действия.\n$this->title = 'Posts';\n?>\n<!-- Отдельные блоки PHP являются предпочтительными для foreach, for, if и т.д. -->\n<?php foreach ($posts as $post): ?>\n    <!-- Заметьте здесь есть отступ. -->\n    <h2><?= Html::encode($post['title']) ?></h2>\n    <p><?= Html::encode($post['shortDescription']) ?></p>\n<!-- `endforeach;`, `endfor;`, `endif;`, и другие должны использоваться вместо `}` в случае использования множественных PHP блоков -->\n<?php endforeach; ?>\n\n<!-- Описание виджета может быть, а может и не быть, разбито на разных строках. -->\n<?php $form = ActiveForm::begin([\n    'options' => ['id' => 'contact-message-form'],\n    'fieldConfig' => ['inputOptions' => ['class' => 'common-input']],\n]); ?>\n    <!-- Заметьте здесь есть отступ. -->\n    <?= $form->field($contactMessage, 'name')->textInput() ?>\n    <?= $form->field($contactMessage, 'email')->textInput() ?>\n    <?= $form->field($contactMessage, 'subject')->textInput() ?>\n    <?= $form->field($contactMessage, 'body')->textArea(['rows' => 6]) ?>\n\n    <div class=\"form-actions\">\n        <?= Html::submitButton('Submit', ['class' => 'common-button']) ?>\n    </div>\n<!-- Завершающий вызов виджета должен быть в индивидуальном PHP теге. -->\n<?php ActiveForm::end(); ?>\n<!-- Завершающий символ переноса строки обязателен. -->\n\n```\n"
  },
  {
    "path": "docs/internals-sr-Latn/automation.md",
    "content": "Automatizacija\n==============\n\nPostoje taskovi koji se rade automatski kada radite sa Yii frejmvorkom:\n\n- Generisanje mape klasa `classes.php` koji se nalazi u rutu frejmvork direktorijuma.\n  Pokrenite `./build/build classmap` kako bi izgenerisali fajl.\n\n- Generisanje `@property` anotacija u fajlovima sa klasama koje opisuju osobine koje su uveli geteri i seteri.\n  Pokrenite `./build/build php-doc/property` kako bi ih osvežili.\n\n- Ispravljanje stila pisanja koda i ostalih sitnijih problema u phpdoc komentarima.\n  Pokrenite `./build/build php-doc/fix` kako bi ih ispravili.\n  Proverite izmene pre njihovog komitovanja zato što se mogu desiti neželjene promene zato što komanda nije idealna.\n  Možete koristiti `git add -p` kako bi pregledali izmene.\n\n- Ažuriranje Mime Type Magic fajla (`framework/helpers/mimeTypes.php`) iz Apache HTTPd repozitorijuma.\n  Pokrenite `./build/build mime-type` kako bi ažurirali falj.\n"
  },
  {
    "path": "docs/internals-sr-Latn/getting-started.md",
    "content": "Kako započeti sa Yii 2 razvojem\n===============================\n\nPogledajte [Git proces rada za Yii 2 saradnike](git-workflow.md) o tome kako podesiti vaše okruženje.\n"
  },
  {
    "path": "docs/internals-sr-Latn/git-workflow.md",
    "content": "Git proces rada za Yii 2 saradnike\n===================================\n\nŽelite da doprinesete Yii razvoju? Divno! Kako bi povećali šanse da vaše izmene budu prihvaćene što pre, molimo da \nispratite sledeće korake. Ako ste novi sa Git-om\ni GitHub-om, možda bi želeli da prvo pogledate [GitHub pomoć](https://help.github.com/), [probate Git](https://try.github.com)\nili naučite nešto novo o [Git internom modelu podataka](https://nfarina.com/post/9868516270/git-is-simpler).\n\nPripremite vaše razvojno okruženje\n------------------------------------\n\nSledeći koraci će napraviti razvojno okruženje za Yii, koje možete koristiti kako bi radili\nna baznom kodu Yii frejmvorka. Ovi se koraci trebaju uraditi samo jednom.\n\n### 1. [Forkujte](https://help.github.com/fork-a-repo/) Yii repozitorijum na GitHub-u i klonirajte vaš fork na vašem razvojnom okruženju\n\n```\ngit clone git@github.com:VASE-GITHUB-KORISNICKO-IME/yii2.git\n```\n\nAko imate problema sa podešavanjem Git-a sa GitHub-om na Linux-u ili dobijate greške tipa \"Permission Denied (publickey)\",\nonda morate [podesiti vašu Git instalaciju da radi sa GitHub-om](https://help.github.com/linux-set-up-git/)\n\n### 2. Dodajte glavni Yii repozitorijum kao dodatni git repozitorijum sa nazivom \"upstream\"\n\nLocirajte se u direktorijum gde ste klonirali Yii, podrazumevano, \"yii2\" direktorijum. Nakon toga izvršite sledeću komandu:\n\n```\ngit remote add upstream https://github.com/yiisoft/yii2.git\n```\n\n### 3. Pripremite okruženje za testiranje\n\nSledeći koraci nisu neophodni ako želite da radite samo na prevodima i dokumentaciji.\n\n- pokrenite `composer update` kako bi instalirali neophodne pakete (podrazumeva se da imate [composer instaliran globalno](https://getcomposer.org/doc/00-intro.md#globally)).\n- pokrenite `php build/build dev/app basic` kako bi klonirali \"basic\" aplikaciju i instalirali composer neophodne pakete \"basic\" aplikacije.\n  Ova komanda će instalirati spoljne composer pakete i ulinkovati yii2 repozitorujum sa trenutnim preuzetim repozitorijumom, tako da imate samo jednu instancu celog instaliranog koda.\n  \n  Ponovite postupak za \"advanced\" aplikaciju ako je potrebno, pokretanjem: `php build/build dev/app advanced`.\n  \n  Ova komanda će se takođe koristiti da bi se osvežili potrebni paketi, ona pokreće `composer update` interno.\n\n**Sada ste spremni za rad na Yii 2 frejmvorku.**\n\nSledeći koraci su neobavezni.\n\n### Unit testovi\n\nMožete izvršiti unit testove pokretanjem `phpunit` unutar root direktorijuma repozitorijuma. Ako nemate phpunit instaliran globalno možete pokrenuti `php vendor/bin/phpunit` umesto toga.\n\nNeki testovi zahtevaju dodatne baze podataka da budu postavljene i podešene. Možete napraviti `tests/data/config.local.php` fajl kako bi pregazili podešavanja koja su definisana unutar `tests/data/config.php` fajla.\n\nMožete ograničiti testove na grupu testova na kojima radite, na primer, da pokrenete testove za samo validaciju i redis koristite `phpunit --group=validators,redis`. Listu dostupnih grupa možete dobiti pokretanjem `phpunit --list-groups`. \n\n### Ekstenzije\n\nKako bi radili na ekstenzijama morate klonirati repozitorijum ekstenzije. Napravili smo komandu koja može to uraditi umesto vas:\n\n```\nphp build/build dev/ext <extension-name>\n```\n\ngde je `<extension-name>` ime ekstenzije, na primer `redis`.\n\nAko želite da testirate ekstenziju u jednom od aplikacijskih šablona, samo dodajte repozitorijum u `composer.json` aplikacije kao što bi to radili normalno, na primer dodali bi `\"yiisoft/yii2-redis\": \"~2.0.0\"` unutar`require` sekcije za \"basic\" aplikaciju.\nPokretanjem `php build/build dev/app basic` ćete instalirati ekstenziju i njene neophodne pakete i ulinkovaće se `extensions/redis` direktorijum kako ne bi radili u vendor direktorijumu nego u yii2 repozitorijumu direktno.\n\n\nRad na bagovima i poboljšanjima\n-------------------------------\n\nPošto je razvojno okruženje spremno kako je objašnjeno iznad možete započeti rad na nekoj novoj funkcionalnosti ili bagu.\n\n### 1. Postarajte se da je problem prijavljen za bug na kom radite ako zahteva značajniji rad na ispravljanju\n\nSve nove funkcionalnosti i bugovi bi trebali imati povezanu temu koju bi koristili kao jedinstvenu tačku za diskusiju i dokumentaciju. Bacite pogled na postojeću listu koja ima temu koja se poklapa sa onim na čemu bi želeli da radite. Ako pronađete da tema već postoji u listi, onda ostavite komentar na toj temi u kome iskažite da želite da radite na tome. Ako ne pronađete postojeću temu/problem koja se poklapa sa onim na čemu bi želeli da radite, molimo da [postavite temu/prijavite problem](report-an-issue.md) ili napravite pull zahtev direktno ako nije komplikovano rešenje. Na ovaj način, tim će moći da pogleda vaše rešenje i dodatno vas uputi.\n\n\n> Za sitne izmene ili dokumentacijske probleme ili za jednostavnije probleme, nije potrebno praviti posebnu temu, pull zahtev je više nego dovoljan u ovom slučaju.\n\n### 2. Dohvatite poslednji kod sa glavne Yii grane\n\n```\ngit fetch upstream\n```\n\nTrebali bi krenuti uvek od ove tačke kada krećete sa radom kako bi se osigurali da radite sa poslednjim kodom.\n\n### 3. Napravite novu granu za novu funkcionalnost/rešenje baga baziranu na trenutnoj Yii master grani\n\n> Ovo je jako važno zato što nećete moći da pošaljete više od jednog pull zahteva sa vašeg naloga ako koristite master.\n\nSvako posebno rešenje baga ili izmena bi trebala da se nalazi u svojoj posebnoj grani. Imena grana trebaju biti opisna i u imenu sadrže broj teme na koju se odnosi. Ako ne radite na ispravci nekog određenog probelma, prekočite broj teme. Na primer:\n\n```\ngit checkout upstream/master\ngit checkout -b 999-IME-VASE-GRANE\n```\n\n### 4. Bacite se na posao, napišite vaš kod\n\nPotrudite se da funkcioniše :)\n\nUnit testovi su uvek dobrodošli. Testiranje i dobro pokriven kod značajno pojednostavljuje proveru koda.\nNeuspeli unit testovi kao opis teme se takođe prihvataju.\n\n### 5. Izmenite CHANGELOG\n\nIzmenite CHANGELOG fajl kako bi uključili vašu izmenu, trebali bi je uneti na vrhu fajla ispod \"Work in progress\" naslova, linija u CHNAGELOG fajlu bi trebalo da izgleda nešto nalik sledećem:\n\n```\nBug #999: opis vaše ispravke (vaše ime)\nEnh #999: opis vašeg poboljšanja (vaše ime)\n```\n\n`#999` je broj teme na koju se `Bug` ili `Enh` odnosi.\nCHANGELOG bi trebao biti grupisan po tipu (`Bug`,`Enh`) i sortiran po broju teme.\n\nZa veoma male izmene, na primer, greške u tekstu, izmene na dokumentaciji, nije potrebno menjati CHANGELOG.\n\n### 6. Komitujte promene\n\ndodajte fajlove/promene koje želite da [komitujete](https://git.github.io/git-reference/basic/#add) sa\n\n```\ngit add path/to/my/file.php\n```\n\nMožete koristit `-p` opciju kako bi izabrali izmene koje želite da komitujete.\n\nKomitujte vaše izmene sa opisnom porukom komita. Potrudite se da napomente broj teme sa `#XXX` kako bi GitHub automatski ulinkovao vaš komit sa temom:\n\n```\ngit commit -m \"Ovde napišite kratak opis promene koja ispravlja #999\"\n```\n\n### 7. Preuzmite poslednji Yii kod sa upstream-a u vašu granu\n\n```\ngit pull upstream master\n```\n\nOvo nas osigurava da imamo poslednji kod u vašoj lokalnoj grani pre nego napravimo pull zahtev. Ako postoje konfilkti, trebali bi ih ispraviti i komitovati izmene ponovo. Na ovaj način biće lakše Yii timu da poveže izmene sa jednim klikom.\n\n### 8. Nakon razrešenih svih konflikata, postavite vaš kod na GitHub\n\n```\ngit push -u origin 999-IME-VASE-GRANE\n```\n\nParametar `-u` će osigurati da će vaša grana moći da šalje pull i push zahteve sa GitHub grane. To znači da ako pozovete `git push` sledeći put će znati gde treba kod da se pošalje. Ovo je korisno ako budete hteli da kasnije dodate više komitova u jednom pull zahtevu.\n\n### 9. Otvorite [pull zahtev](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) na upstream-u.\n\nPosetite vaš repozitorijum na Github-u i kliknite na \"Pull Request\", izaberite vašu granu na desnoj strani i unesite neki opis u polje za komentar. Kako bi povezali pull zahtev sa temom unesite bilo gde u komentaru `#999` gde 999 je broj teme.\n\n> Imajte na umu da svaki pull zahtev treba ispraviti samo jednu stvar. Za više, nevezanih izmena, molimo koristite više pull zahteva.\n\n### 10. Neko će pregledati vaš kod\n\nNeko će pregledati vaš kod, i možda će se od vas tražiti još neke izmene, ako je tako idite na korak #6 (ne morate da pravite novi pull zahtev ako je vaš trenutni još uvek otvoren). Ako je vaš kod prihvaćen biće spojen u glavnu granu i postaće deo sledećeg Yii izdanja. Ako to nije slučaj, ne budite obeshrabreni, različiti ljudi žele različite funkcionalnosti i Yii ne može biti sve svima, vaš kod će biti dostupan na GitHub-u kao referenca za ljude kojima je to potrebno.\n\n### 11. Čišćenje\n\nNakom što je vaš kod ili prihvaćen ili odbijen možete obrisati vaše grane na kojima ste radili na vašem lokalnom repozitorijumu i `origin`.\n\n```\ngit checkout master\ngit branch -D 999-IME-VASE-GRANE\ngit push origin --delete 999-IME-VASE-GRANE\n```\n\n### Napomena:\n\nKako bi rano otkrili regresije u Yii kodu prilikom svake integracije na GitHub-u pokreće se [Travis CI](https://travis-ci.com) kako bi se radilo testiranje. Pošto Yii tim ne želi da preoptereti ovaj servis,\n[`[ci skip]`](https://docs.travis-ci.com/user/customizing-the-build/#Skipping-a-build) će biti uključen prilikom svake integracije ako pull zahtev:\n\n* utiče samo na javascript, css i slike,\n* osvežava dokumentaciju,\n* menja samo fiksne stringove (npr. izmene u prevodu)\n\nNa ovaj način će Travis započinjati testiranje samo izmena koje nisu prvenstveno pokrivene testovima.\n\n### Pregled komandi (za napredne saradnike)\n\n```\ngit clone git@github.com:VASE-GITHUB-KORISNICKO-IME/yii2.git\ngit remote add upstream https://github.com/yiisoft/yii2.git\n```\n\n```\ngit fetch upstream\ngit checkout upstream/master\ngit checkout -b 999-IME-VASE-GRANE\n\n/* bacite se na posao, izmenite changelog ako je potrebno */\n\ngit add path/to/my/file.php\ngit commit -m \"Ovde napišite kratak opis promene koja ispravlja #999\"\ngit pull upstream master\ngit push -u origin 999-IME-VASE-GRANE\n```\n"
  },
  {
    "path": "docs/internals-sr-Latn/report-an-issue.md",
    "content": "Prijavite problem\n=================\n\nMolimo da ispratite smernice ispod kada prijavljujete problem kako bi vaš problem bio što pre razrešen:\n\n* Priložite informacije uključujući: verziju PHP-a i Yii-a, tip operativnog sistema i web servera, tip brauzera i njegovu verziju;\n* Priložite **kompletan** spisak grešaka ako postoji. Snimak ekrana koji objašnjava problem je vrlo dobrodošao.\n* Opišite korake za reprodukovanje problema. Bilo bi još bolje ako možete da priložite kod koji reprodukuje problem.\n* Ako je moguće možete i da napravite neuspeli unit test i [pošaljete ga kao pull zahtev](git-workflow.md).\n\nAko je problem povezan sa nekom zvaničnom ekstenzijom , molimo da prijavite problem u repozitorijima date ekstenzije.\nUkoliko niste sigurni, [prijavite problem u glavnom repozitorijumu](https://github.com/yiisoft/yii2/issues/new) (<https://github.com/yiisoft/yii2/issues>).\n\n**Ne prijavljujte problem ako**\n\n* tražite pomoć pri korišćenju nekih Yii funkcionalnosti. Koristite [forum](https://forum.yiiframework.com/index.php/forum/42-general-discussions-for-yii-20/) ili [čet sobu](https://www.yiiframework.com/chat/) u te svrhe.\n* je vaš problem sigurnosnog tipa. Molimo da nas [kontaktirate direktno](https://www.yiiframework.com/security/) za prijavu sigurnosnih problema.\n\n**Izbegavajte duple probleme**\n\nPre nego prijavite problem, molimo da pretražite [postojeće probleme](https://github.com/yiisoft/yii2/issues) kako bi videli da li je vaš problem već prijavljen ili rešen kako bi bili sigurni da ne prijavljujete dupli problem. Takođe proverite da li imate poslednju verziju Yii-a i vidite da li problem i dalje postoji.\n"
  },
  {
    "path": "docs/internals-sr-Latn/translation-workflow.md",
    "content": "Proces rada u prevođenju\n========================\n\nYii je preveden na više jezika kako bi bio koristan za internacionalne aplikacije i programere. Dve glavne oblasti u kojima je doprinos veoma poželjan jeste dokumentacija i frejmvork poruke.\n\nFrejmvork poruke\n----------------\n\nFrejmvork ima dva tipa poruka: izuzeci koji su namenjeni programeru i koje se nikada ne prevode i poruke koje su zapravo vidljive krajnjem korisniku kao na primer validacijske greške.\n\nDa bi započeli rad sa prevodom poruka potrebno je da:\n\n1. Otvorite `framework/messages/config.php` i proverite da li je vaš jezik naveden u `languages`. Ako nije,\n   dodajte tu vaš jezik (ne zaboravite da zadržite alfabetički rapored). Format koda jezika treba da prati \n   [IETF jezičku tag specifikaciju](https://en.wikipedia.org/wiki/IETF_language_tag), na primer, `ru`, `zh-CN`.\n2. Uđite u `framework` direktorijum i pokrenite `./yii message/extract @yii/messages/config.php --languages=<your_language>`.\n3. Prevedite poruke unutar `framework/messages/your_language/yii.php` fajla. Pobrinite se da se fajl sačuva sa UTF-8 enkodingom.\n4. [Napravite pull zahtev](https://github.com/yiisoft/yii2/blob/master/docs/internals-sr-Latn/git-workflow.md).\n\nKako bi  vaš prevod bio ažuran možete pokrenuti komandu `./yii message/extract @yii/messages/config.php --languages=<your_language>` još jednom. Ona će automatski ponovo izvući poruke ostavljajući nepromenjene netaknutim.\n\nU fajlu za prevođenje svaki element niza predstavlja prevod (vrednost) poruke (ključ). Ako je vrednost prazna, poruka se smatra neprevedenom. Poruke koje više ne trebaju prevod će imati svoje prevode zatvorene između para '@@' znaka. Poruke se mogu koristiti i u formatu za množinu. Pogledajte [i18n sekciju uputstva](../guide-sr-Latn/tutorial-i18n.md) za više informacija.\n\nDokumentacija\n-------------\n\nStavite prevode dokumentacije unutar `docs/<original>-<language>` gde `<original>` je originalno ime dokumentacije kao što je `guide` ili `internals`, a `<language>` je jezički kod od dokumentacije jezika na koji se prevodi. Za rusko uputstvo prevodi se nalaze u `docs/guide-ru`.\n\nNakon što je inicijalni posao završen možete dobiti šta je promenjeno nakon poslednjeg prevoda fajla korišćenjem specijalne komande unutar `build` direktorijuma:\n\n```\nphp build translation \"../docs/guide\" \"../docs/guide-ru\" \"Russian guide translation report\" > report_guide_ru.html\n```\n\nAko se bude bunio u vezi composer-a, izvršite `composer install` u samom root-u direktorijumu.\n"
  },
  {
    "path": "docs/internals-uk/automation.md",
    "content": "Автоматизація\n=============\n\nЄ кілька задач, які можна автоматизувати, працюючи з Yii:\n\n- Створення мапи класів `classes.php` у кореневій директорії фреймворку.\n  Виконати `./build/build classmap` для її створення.\n\n- Створення анотацій `@property` у файлах класів, що описують властивості представлені геттерами та сеттерами.\n  Виконати `./build/build php-doc/property` для їх оновлення.\n\n- Виправлення стилю кодування та інших невеличких проблем у коментарях PHPDoc.\n  Виконати `./build/build php-doc/fix` для виправлення.\n  Перед тим як створювати комміт, необхідно перевіряти зміни, оскільки команда не є досконалою й можливі не бажані зміни.\n  Можна використовувати `git add -p` для перегляду змін.\n"
  },
  {
    "path": "docs/internals-uk/core-code-style.md",
    "content": "Оформлення основного коду фреймворку Yii 2\n==========================================\n\nНижченаведений стиль кодування використовується у розробці основного коду Yii 2.x та офіційних розширень. Дотримуйтесь його,\nякщо хочете вносити зміни в основний код. Команда розробників не наполягає на використанні цього стилю для вашого додатка.\nВільно обирайте те, що підходить вам більше.\n\nВи можете отримати конфігурацію для CodeSniffer за посиланням: https://github.com/yiisoft/yii2-coding-standards\n\n## 1. Загальні положення\n\nВ основному командою розробників використовується стиль сумісний з [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md),\nтому все, що стосується\n[PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md), застосовується також.\n\n- У файлах НЕОБХІДНО використовувати теги або `<?php` або `<?=`.\n- Пустий рядок у кінці файлу є необхідним.\n- Файли з кодом PHP ПОВИННІ зберігатись лише у кодуванні UTF-8 без BOM.\n- Для кожного рівня відступу НЕОБХІДНО використовувати 4 пробіли, а не табуляцію.\n- Імена класів ПОВИННІ оголошуватись як `StudlyCaps`.\n- Імена констант класу ПОВИННІ оголошуватись повністю у верхньому регістрі, підкреслення використовується як роздільник.\n- Імена методів ПОВИННІ оголошуватись як `camelCase`.\n- Імена властивостей ПОВИННІ оголошуватись як `camelCase`.\n- Імена приватних властивостей ПОВИННІ починатись з підкреслення.\n- Завжди використовуйте `elseif` замість `else if`.\n\n## 2. Файли\n\n### 2.1. Теги PHP\n\n- PHP код ПОВИНЕН використовувати теги `<?php ?>` або `<?=`; він НЕ ПОВИНЕН використовувати інші варіації, такі як `<?`.\n- Якщо файл містить лише PHP він не повинен закінчуватись тегом `?>`.\n- Не додавайте пробіли у кінці рядку.\n- Будь-який файл, який містить код PHP, повинен мати розширення `.php`.\n\n### 2.2. Кодування символів\n\nPHP код ПОВИНЕН використовувати лише UTF-8 без BOM.\n\n## 3. Імена класів\n\nІмена класів ПОВИННІ оголошуватись як `StudlyCaps`. Наприклад: `Controller`, `Model`.\n\n## 4. Класи\n\nТут термін \"клас\" відноситься до всіх класів та інтерфейсів.\n\n- Класи повинні іменуватись як `CamelCase`.\n- Початкова фігурна дужка повинна завжди розміщуватись на наступному рядку після імені класу.\n- Кожний клас повинен мати блок документації, який відповідає стандарту PHPDoc.\n- Код всередині класу повинен бути зміщений на рівень відступу.\n- Один PHP файл може містити лише один клас.\n- Кожний клас повинен бути у просторі імен.\n- Ім’я класу повинне відповідати імені файлу. Простір імен класу повинен відповідати структурі директорій.\n\n```php\n/**\n * Документація\n */\nclass MyClass extends \\yii\\base\\BaseObject implements MyInterface\n{\n    // код\n}\n```\n\n### 4.1. Константи\n\nІмена констант ПОВИННІ оголошуватись повністю у верхньому регістрі, підкреслення використовується як роздільник.\nНаприклад:\n\n```php\n<?php\nclass Foo\n{\n    const VERSION = '1.0';\n    const DATE_APPROVED = '2012-06-01';\n}\n```\n\n### 4.2. Властивості\n\n- При оголошенні публічних членів класу обов’язково вказуйте ключове слово `public`.\n- Публічні та захищені змінні повинні бути оголошенні у початку класу перед будь-яким оголошенням методу.\n  Приватні змінні також повинні бути оголошенні у початку класу, але можуть бути вставленні безпосередньо перед методами,\n  що ними оперують, у випадках коли змінні пов’язані лише з невеликою кількістю методів класу.\n- Порядок оголошення властивостей у класі повинен бути таким: спочатку публічні, потім захищені, а потім приватні.\n- Для кращої читабельності не повинно бути пустих рядків між оголошеннями властивостей та повинно бути два пустих рядки\n  між секціями оголошення властивості та методу.\n- Приватні змінні повинні іменуватись як `$_varName`.\n- Публічні члени класу та автономні змінні повинні іменуватись як `$camelCase`\n  (перша літера у нижньому регістрі).\n- Використовуйте описові імена. Змінні, такі як `$i` та `$j`, краще не використовувати.\n\nНаприклад:\n\n```php\n<?php\nclass Foo\n{\n    public $publicProp;\n    protected $protectedProp;\n    private $_privateProp;\n}\n```\n\n### 4.3. Методи\n\n- Функції та методи повинні іменуватись як `camelCase` (перша літера у нижньому регістрі).\n- Ім’я повинне бути само-описовим та вказувати призначення функції.\n- Методи класу завжди повинні оголошувати видимість, використовуючи модифікатори `private`, `protected` та\n  `public`. Не допускається використання `var`.\n- Початкова фігурна дужка функції повинна розміщуватись на наступному рядку після оголошення функції.\n\n```\n/**\n * Документація\n */\nclass Foo\n{\n    /**\n     * Документація\n     */\n    public function bar()\n    {\n        // код\n        return $value;\n    }\n}\n```\n\n### 4.4 Блоки документації PHPDoc (Doc-блоки)\n\n`@param`, `@var`, `@property` та `@return` повинні оголошувати типи як `bool`, `int`, `string`, `array` чи `null`. Також можна використовувати імена класів, як наприклад: `Model` або `ActiveRecord`. Для типізованих масивів використовуйте `ClassName[]`.\n\n### 4.5 Конструктори\n\n- потрібно використовувати `__construct` замість стилю конструкторів PHP 4.\n\n## 5 PHP\n\n### 5.1 Типи\n\n- Усі типи PHP та значення повинні бути у нижньому регістрі. Це включає `true`, `false`, `null` та `array`.\n\nЗміна типу змінної, що вже існує, вважається поганою практикою. Намагайтесь не писати такий код, поки в цьому дійсно нема потреби.\n\n\n```php\npublic function save(Transaction $transaction, $argument2 = 100)\n{\n    $transaction = new Connection; // погано\n    $argument2 = 200; // добре\n}\n```\n\n### 5.2 Текстові рядки\n\n- Якщо текстовий рядок не містить змінних чи одинарних лапок, використовуйте одинарні лапки.\n\n```php\n$str = 'Текстовий рядок.';\n```\n\n- Якщо текстовий рядок містить одинарні лапки, можна використовувати подвійні лапки, щоб уникнути додаткового екранування символів.\n\n#### Підставлення змінної\n\n```php\n$str1 = \"Привіт, $username!\";\n$str2 = \"Привіт, {$username}!\";\n```\n\nНижченаведений приклад не допускається:\n\n```php\n$str3 = \"Привіт, ${username}!\";\n```\n\n#### Конкатенація\n\nДодавайте пробіли навколо оператора конкатенації (.), коли об’єднуєте текстові рядки:\n\n```php\n$name = 'Фреймворк ' . 'Yii';\n```\n\nДля довгих текстових рядків використовуйте наступний формат:\n\n```php\n$sql = \"SELECT *\"\n    . \"FROM `post` \"\n    . \"WHERE `id` = 121 \";\n```\n\n### 5.3 Масиви\n\nДля масивів використовуйте скорочений синтаксис (PHP 5.4).\n\n#### Індексовані масиви\n\n- Не використовуйте від’ємні числа для індексів масиву.\n\nВикористовуйте нижченаведений формат оголошення масиву:\n\n```php\n$arr = [3, 14, 15, 'Yii', 'фреймворк'];\n```\n\nЯкщо забагато елементів для одного рядка:\n\n```php\n$arr = [\n    3, 14, 15,\n    92, 6, $test,\n    'Yii', 'фреймворк',\n];\n```\n\n#### Асоціативні масиви\n\nВикористовуйте нижченаведений формат для асоціативних масивів:\n\n```php\n$config = [\n    'name'  => 'Yii',\n    'options' => ['usePHP' => true],\n];\n```\n\n### 5.4 Керування порядком виконання\n\n- Перед умовою керувальної інструкції та після неї повинен бути пробіл.\n- Оператори всередині круглих дужок повинні відокремлюватись пробілами.\n- Початкова фігурна дужка розміщена на тому самому рядку.\n- Кінцева фігурна дужка розміщена на новому рядку.\n- Завжди використовуйте фігурні дужки для конструкцій з одного рядка.\n\n```php\nif ($event === null) {\n    return new Event();\n}\nif ($event instanceof CoolEvent) {\n    return $event->instance();\n}\nreturn null;\n\n\n// нижченаведений приклад НЕ допускається:\nif (!$model && null === $event)\n    throw new Exception('test');\n```\n\nКраще уникайте використання `else` після `return`, де це має сенс.\nВикористовуйте [вартові умови](https://refactoring.com/catalog/replaceNestedConditionalWithGuardClauses.html).\n\n```php\n$result = $this->getResult();\nif (empty($result)) {\n  return true;\n} else {\n  // обробка результату\n}\n```\n\nбуде краще так:\n\n```php\n$result = $this->getResult();\nif (empty($result)) {\n  return true;\n}\n\n// обробка результату\n```\n\n#### switch\n\nВикористовуйте наступний формат для switch:\n\n```php\nswitch ($this->phpType) {\n    case 'string':\n        $a = (string) $value;\n        break;\n    case 'integer':\n    case 'int':\n        $a = (int) $value;\n        break;\n    case 'boolean':\n        $a = (bool) $value;\n        break;\n    default:\n        $a = null;\n}\n```\n\n### 5.5 Виклики функції\n\n```php\ndoIt(2, 3);\n\ndoIt(['a' => 'b']);\n\ndoIt('a', [\n    'a' => 'b',\n    'c' => 'd',\n]);\n```\n\n### 5.6 Оголошення анонімних (лямбда) функцій\n\nЗверніть увагу на пробіл між `function`/`use` та початковою круглою дужкою:\n\n```php\n// добре\n$n = 100;\n$sum = array_reduce($numbers, function ($r, $x) use ($n) {\n    $this->doMagic();\n    $r += $x * $n;\n    return $r;\n});\n\n// погано\n$n = 100;\n$mul = array_reduce($numbers, function($r, $x) use($n) {\n    $this->doMagic();\n    $r *= $x * $n;\n    return $r;\n});\n```\n\nДокументація\n------------\n\n- Див. [PHPDoc](https://phpdoc.org/) для довідки про синтаксис документації.\n- Код без документації не допускається.\n- Усі файли класів повинні містити файловий (\"file-level\") doc-блок на початку\n  та класовий (\"class-level\") doc-блок безпосередньо над кожним класом.\n- Нема потреби використовувати `@return`, якщо метод нічого не повертає.\n- Усі віртуальні властивості у класах успадкованих від `yii\\base\\BaseObject`\n  документуються з тегом `@property` у класовому doc-блоці.\n  Ці анотації генеруються автоматично із тегів `@return` чи `@param`\n  відповідних геттерів або сеттерів виконанням команди `./build php-doc` у директорії build.\n  Ви можете додати тег `@property`\n  до геттеру або сеттеру, щоб точно визначити повідомлення для документації властивості,\n  яка представляється цими методами, коли опис відрізняється від того, що встановлено\n  тегом `@return`. Наприклад:\n\n  ```php\n    <?php\n    /**\n     * Returns the errors for all attributes or a single attribute.\n     * @param string $attribute attribute name. Use `null` to retrieve errors for all attributes.\n     * @property array An array of errors for all attributes. Empty array is returned if no error.\n     * The result is a two-dimensional array. See [[getErrors()]] for detailed description.\n     * @return array errors for all attributes or the specified attribute. Empty array is returned if no error.\n     * Note that when returning errors for all attributes, the result is a two-dimensional array, like the following:\n     * ...\n     */\n    public function getErrors($attribute = null)\n  ```\n\n#### Файл\n\n```php\n<?php\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n```\n\n#### Клас\n\n```php\n/**\n * Component is the base class that provides the *property*, *event* and *behavior* features.\n *\n * @include @yii/docs/base-Component.md\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Component extends \\yii\\base\\BaseObject\n```\n\n\n#### Функція / метод\n\n```php\n/**\n * Returns the list of attached event handlers for an event.\n * You may manipulate the returned [[Vector]] object by adding or removing handlers.\n * For example,\n *\n * ```\n * $component->getEventHandlers($eventName)->insertAt(0, $eventHandler);\n * ```\n *\n * @param string $name the event name\n * @return Vector list of attached event handlers for the event\n * @throws Exception if the event is not defined\n */\npublic function getEventHandlers($name)\n{\n    if (!isset($this->_e[$name])) {\n        $this->_e[$name] = new Vector;\n    }\n    $this->ensureBehaviors();\n    return $this->_e[$name];\n}\n```\n\n#### Markdown\n\nЯк ви могли побачити у прикладах вище, для форматування коментарів PHPDoc використовується markdown.\n\nІснує додатковий синтаксис для створення перехресних посилань між класами, методами та властивостями у документації:\n\n- `[[canSetProperty]]` створить посилання на метод або властивість `canSetProperty` того ж самого класу.\n- `[[Component::canSetProperty]]` створить посилання на метод `canSetProperty` класу `Component` в тому ж самому просторі імен.\n- `[[yii\\base\\Component::canSetProperty]]` створить посилання на метод `canSetProperty` класу `Component` з простору імен `yii\\base`.\n- `[[Component]]` створить посилання на клас `Component` в тому ж самому просторі імен. Додавання простору імен до імені класу тут також можливе.\n\nЩоб для одного з вище зазначених посилань вказати мітку відмінну від імені класу чи методу, ви можете використовувати синтаксис, показаний в нижченаведеному прикладі:\n\n```\n... as displayed in the [[header|header cell]].\n```\n\nЧастина перед | є найменуванням методу, властивості або класу, в той час як частина після | є міткою посилання.\n\nТакож можливо посилатись на Посібник, використовуючи наступний синтаксис:\n\n```markdown\n[link to guide](guide:file-name.md)\n[link to guide](guide:file-name.md#subsection)\n```\n\n\n#### Коментарі\n\n- Одно-рядкові коментарі повинні починатись з `//`, а не з `#`.\n- Одно-рядковий коментар повинен бути розміщений на власному рядку.\n\nДодаткові правила\n-----------------\n\n### `=== []` проти `empty()`\n\nВикористовуйте `empty()`, де можливо.\n\n### Кілька інструкцій return\n\nПри великій вкладеності умов, використовуйте інструкцію return раніше. Якщо метод не великий, це неважливо.\n\n### `self` проти `static`\n\nЗавжди використовуйте `static`, за виключенням наведених випадків:\n\n- отримання значень констант ПОВИННО виконуватись через `self`: `self::MY_CONSTANT`\n- отримання значень приватних статичних властивостей ПОВИННО виконуватись через `self`: `self::$_events`\n- Дозволено використовувати `self` для рекурсії, щоб викликати поточне втілення знову замість розширення реалізації класу.\n\n### Значення для \"не робити чогось\"\n\nВластивості, яким дозволено сконфігурувати компонент не робити чогось, повинні приймати значення `false`. Значення `null`, `''` чи `[]` не повинні вважатись такими.\n\n### Імена директорій та просторів імен\n\n- використовуйте нижній регістр\n- використовуйте форму множини для іменників, які представляють об’єкти (наприклад, validators)\n- використовуйте форму однини для імен, які представляють відповідну функціональність/можливості (наприклад, web)\n"
  },
  {
    "path": "docs/internals-uk/design-decisions.md",
    "content": "Проектні рішення\n================\n\nЦей документ перелічує проектні рішення, які були прийняті командою розробників після широких дискусій.\nДопоки немає дуже вагомих причин, ці рішення повинні дотримуватись для узгодженості. Для будь-якої зміни\nу цих рішеннях необхідно отримати згоду команди головних розробників.\n\n1. **[Коли підтримувати псевдоніми шляху?](https://github.com/yiisoft/yii2/pull/3079#issuecomment-40312268)**\n   Псевдоніми шляху повинні підтримуватися для властивостей, які можна сконфігурувати, тому-що використання псевдонімів шляху\n   у конфігураціях дуже зручно. В інших випадках, потрібно обмежувати підтримку псевдонімів шляху.\n2. **Коли перекладати повідомлення?**\n   Повідомлення повинні перекладатись, якщо вони показуються кінцевому користувачу та мають для нього значення. Повідомлення статусу HTTP,\n   виключення і т. п. не повинні перекладатись. Повідомлення консолі завжди англійською мовою, через труднощі обробки кодування\n   та кодової сторінки.\n3. **[Додавання підтримки нового клієнту аутентифікації](https://github.com/yiisoft/yii2/issues/1652)**\n   Для кращого супроводу, ніякі додаткові клієнти аутентифікації не будуть додаватись до базового розширення. Вони\n   повинні бути виконані у вигляді користувацьких розширень.\n4. **При використанні замикань**, рекомендується **вказувати всі параметри, які передаються**, у сигнатурі, навіть, якщо не всі з них\n   використовуються. Таким чином змінювати чи копіювати код легше, тому-що вся інформація перед очима та нема потреби переглядати\n   документацію для пошуку доступних параметрів. ([#6584](https://github.com/yiisoft/yii2/pull/6584), [#6875](https://github.com/yiisoft/yii2/issues/6875))\n5. Надавайте перевагу **int замість unsigned int** у схемі бази даних. Використання int має перевагу, бо може бути представлене в PHP як число.\n   Щоб представити unsigned, для 32-розрядної системи необхідно використовувати текстовий рядок.\n   Але також unsigned int подвоює діапазон позитивних чисел. Якщо у вас є таблиця, яка потребує такий великий числовий простір,\n   то безпечніше використовувати bigint або mediumint, а не покладатися на unsigned.\n   <https://github.com/yiisoft/yii/pull/1923#issuecomment-11881967>\n"
  },
  {
    "path": "docs/internals-uk/getting-started.md",
    "content": "Підготовка до розробки Yii 2\n============================\n\nІнформація про те, як налаштувати середовище для розробки представлена у розділі [Робота з Git для учасників Yii 2](git-workflow.md).\n"
  },
  {
    "path": "docs/internals-uk/git-workflow.md",
    "content": "Робота з Git для учасників Yii 2\n================================\n\nВи бажаєте взяти участь в розробці Yii? Чудово! Але, щоб підвищити шанси швидкого прийняття ваших змін, будь ласка,\nдотримуйтесь наступних кроків. Якщо ви новачок у Git та GitHub, спершу можете ознайомитись із\n[довідкою GitHub](https://help.github.com/), [тренером Git](https://try.github.com), [інтерактивним туром Git How To](https://githowto.com/)\nабо почитати книгу [Магія Git](https://www-cs-students.stanford.edu/~blynn/gitmagic/intl/uk/)\nчи [Розділ Git у Вікіпідручнику](https://uk.wikibooks.org/wiki/Git), щоб краще зрозуміти внутрішню структуру Git.\n\nПідготовка вашого середовища розробки\n-------------------------------------\n\nНаступні кроки створять середовище розробки для Yii, яке ви зможете використовувати для роботи\nнад основним кодом фреймворку Yii. Ці кроки необхідні лише тоді, коли ви вперше долучаєтесь до співпраці.\n\n### 1. [Створіть форк](https://help.github.com/fork-a-repo/) репозиторію Yii на GitHub та клонуйте свій форк у своє середовище розробки\n\n```\ngit clone git@github.com:ВАШЕ-ІМ’Я-НА-GITHUB/yii2.git\n```\n\nЯкщо у вас виникли проблеми із роботою Git з GitHub в Linux, або ви отримали помилку заборони доступу \"Permission Denied (publickey)\",\nто вам необхідно [налаштувати Git для роботи з GitHub](https://help.github.com/articles/set-up-git/#platform-linux).\n\n### 2. Додайте головний репозиторій Yii як додатковий віддалений репозиторій із назвою \"upstream\"\n\nПерейдіть у директорію, в яку ви клонували Yii, зазвичай \"yii2\". Потім виконайте наведену команду:\n\n```\ngit remote add upstream https://github.com/yiisoft/yii2.git\n```\n\n### 3. Підготуйте середовище тестування\n\nНаступні кроки не обов'язкові, якщо ви хочете працювати лише над перекладом або документацією.\n\n- виконайте `composer update` для встановлення залежностей (припускається, що ви маєте [глобально встановлений composer](https://getcomposer.org/doc/00-intro.md#globally)).\n- виконайте `php build/build dev/app basic` для клонування базового додатку та встановлення його залежностей.\n  Ця команда встановить сторонні пакунки composer як завжди, а також створить посилання з репозиторію yii2\n  на поточний репозиторій. Таким чином ви будете мати один екземпляр встановленого коду.\n\nЯкщо необхідно, зробіть те ж саме для розширеного додатку: `php build/build dev/app advanced`.\n\nЦя команда може використовуватись для оновлення залежностей, вона викликає `composer update` в процесі виконання.\n\n**Тепер ви маєте робочий майданчик для експериментів з Yii 2.**\n\nНаступні кроки не обов’язкові.\n\n### Модульні тести\n\nВи можете виконати модульні тести, запустивши `phpunit` у кореневій директорії репозиторію. Якщо у вас phpunit не встановлений глобально,\nви можете запускати `php vendor/bin/phpunit`.\n\nДеякі тести потребують додатково встановлення та налаштування баз даних. Ви можете створити `tests/data/config.local.php` для перевизначення\nналаштувань сконфігурованих у `tests/data/config.php`.\n\nМожливо обмежити тести групою тестів, що покривають область над якою ви працюєте, наприклад, щоб запустити тести для валідаторів\nта redis, виконайте `phpunit --group=validators,redis`. Для отримання списку доступних груп виконайте `phpunit --list-groups`.\n\n### Розширення\n\nДля роботи з розширеннями необхідно клонувати відповідні репозиторії. Наступна команда зробить це для вас:\n\n```\nphp build/build dev/ext <extension-name>\n```\n\nде `<extension-name>` є назвою розширення, наприклад `redis`.\n\nЯкщо бажаєте протестувати розширення в одному із шаблонів додатку, просто додайте його до `composer.json` додатку, як ви будете\nробити зазвичай. Наприклад, додайте `\"yiisoft/yii2-redis\": \"~2.0.0\"` до секції `require` базового додатку.\nКоманда `php build/build dev/app basic` встановить розширення та його залежності й створить\nсимвольне посилання на `extensions/redis`, тому ви можете працювати безпосередньо в директорії репозиторію yii2,\nа не у специфічній для composer директорії vendor.\n\n\nРобота з помилками та функціоналом\n----------------------------------\n\nОтримавши середовище розробки, як було описано вище, ви можете розпочати роботу над функціоналом або виправленням помилок.\n\n### 1. Переконайтесь, що створено питання стосовно речі, над якою ви працюєте, якщо це потребує багатьох зусиль для виконання\n\nУсі нові можливості та виправлення помилок повинні мати пов’язане запитання, яке забезпечує єдину точку посилання для обговорення\nта документації. Витратьте декілька хвилин на перегляд списку створених питань, щоб знайти ті, що стосуються внеску, який ви\nзбираєтесь зробити. Якщо знайдете таке у списку запитань, потім, будь ласка, залиште коментар із зазначенням, що ви\nзбираєтесь працювати над цим. Якщо не знайшли створеного питання, що стосується того, над чим ви збираєтесь працювати, будь ласка\n[створіть нове запитання](report-an-issue.md) або відправте \"pull request\" безпосередньо, якщо це не складне виправлення. Це дозволить команді\nрозробників розглянути вашу пропозицію та надавати відповідний зворотний зв’язок протягом шляху.\n\n> Для невеликих змін, проблем документації або простих виправлень нема потреби створювати питання, достатньо відправити \"pull request\" у цих випадках.\n\n### 2. Отримайте останній код з головної гілки Yii\n\n```\ngit fetch upstream\n```\n\nЗ цього необхідно розпочинати кожний новий внесок, щоб бути впевненим, що ви працюєте з найновішим кодом.\n\n### 3. Створіть нову гілку для вашого внеску на базі поточної основної гілки Yii\n\n> Це дуже важливо, тому що ви не зможете відправляти більше, ніж один \"pull request\" від вашого імені, у разі\n  використання основної (master) гілки.\n\nКожні окремі виправлення помилок або зміни повинні мати власні гілки. Назви гілок повинні бути наочними та починатись з\nномеру питання, яке пов’язане із вашим кодом. Якщо ви працюєте не над специфічним питанням, просто пропустіть номер.\nНаприклад:\n\n```\ngit checkout upstream/master\ngit checkout -b 999-name-of-your-branch-goes-here\n```\n\n### 4. Робіть вашу магію, пишіть ваш код\n\nПереконайтесь, що він працює :)\n\nМодульні тести завжди вітаються. Протестований та добре покритий код надзвичайно полегшує перевірку вашого внеску.\nПровальні модульні тести як опис проблеми також приймаються.\n\n### 5. Оновіть журнал змін (CHANGELOG)\n\nДодайте до файлу CHANGELOG зроблені вами зміни у верхній частині документу під заголовком\n\"Work in progress\", запис у журналі змін повинен виглядати подібно до наведеного прикладу:\n\n```\nBug #999: a description of the bug fix (Your Name)\nEnh #999: a description of the enhancement (Your Name)\n```\n\n`#999` - це номер питання, на яке посилається виправлення помилки (`Bug`) або покращення (`Enh`).\n\nЗаписи журналу змін повинні бути згруповані за типом (`Bug`, `Enh`) та сортовані за номером питання.\n\nДля дуже малих виправлень, наприклад, друкарських помилок та змін у документації, нема потреби оновлювати CHANGELOG.\n\n### 6. Створіть комміт ваших змін\n\nДодайте файли/зміни, призначені для комміту, в [буферну зону](https://git.github.io/git-reference/basic/#add) за допомогою команди:\n\n```\ngit add path/to/my/file.php\n```\n\nВикористовуйте опцію `-p` для відбору змін, які ви бажаєте додати до вашого комміту.\n\nСтворіть комміт з описовим повідомленням. Переконайтесь, що вказали номер питання як `#XXX`, щоб GitHub\nавтоматично пов’язав ваш комміт із питанням:\n\n```\ngit commit -m \"A brief description of this change which fixes #999 goes here\"\n```\n\n### 7. Додайте останній код Yii з upstream до вашої гілки\n\n```\ngit pull upstream master\n```\n\nЦе гарантує, що ви матимете останній код у вашій гілці перед тим, як відправити \"pull request\". Якщо є будь-які конфлікти поєднання,\nтреба виправити їх одразу та знову створити комміт. Це забезпечить команду розробників Yii можливістю легко приєднати ваші зміни\nодним натисканням кнопки.\n\n### 8. Вирішивши будь-які конфлікти, відправте ваш код до GitHub\n\n```\ngit push -u origin 999-name-of-your-branch-goes-here\n```\n\nОпція `-u` забезпечує те, що ваша гілка відтепер оброблятиметься автоматично при запитах push та pull до GitHub гілки.\nЦе означає, якщо ви виконаєте `git push` наступного разу, програма буде знати куди відправляти. Це корисно, якщо ви\nбажаєте пізніше додавати більше коммітів у \"pull request\".\n\n### 9. Відправте [\"pull request\"](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) до upstream.\n\nПерейдіть до вашого репозиторію на GitHub та натисніть \"Pull Request\", оберіть вашу гілку справа та внесіть трохи деталей\nу полі коментарю. Щоб пов’язати \"pull request\" із запитанням, внесіть десь у коментарі `#999`, де 999 - це номер питання.\n\n> Зауважте, що кожний \"pull request\" повинен стосуватись окремої зміни. Для багатьох змін, не пов’язаних між собою,\n  будь ласка, відправляйте \"pull request\" окремо для кожної.\n\n### 10. Дехто перевірить ваш код\n\nДехто перевірить ваш код, й, можливо, вас попросять внести деякі зміни. У цьому випадку перейдіть до кроку #6 (нема потреби\nвідправляти інший \"pull request\", якщо ваш поточний досі відкритий). Якщо ваш код прийнято, то він буде поєднаний з головною гілкою\nта стане частиною наступного релізу Yii. Якщо ж ні, не сумуйте, різні люди потребують різних можливостей, та Yii\nне може бути всім для всіх, ваш код залишатиметься доступним на GitHub для людей, які його потребують.\n\n### 11. Проведіть чистку\n\nПісля того, як ваш код був прийнятий або відхилений, можете видалити гілки, над якими ви працювали, із локального репозиторію\nта з `origin`.\n\n```\ngit checkout master\ngit branch -D 999-name-of-your-branch-goes-here\ngit push origin --delete 999-name-of-your-branch-goes-here\n```\n\n### Примітка:\n\nДля виявлення регресу на ранніх стадіях кожне поєднання з кодовою базою Yii на GitHub опрацьовується у\n[Travis CI](https://travis-ci.com) для автоматичного запуску тестів. Оскільки основна команда розробників не бажає\nперевантажувати сервіс, додавайте [`[ci skip]`](https://docs.travis-ci.com/user/customizing-the-build/#Skipping-a-build) до\nопису поєднання, якщо ваш \"pull request\":\n\n* зачіпає лише файли javascript, css або файли зображень,\n* оновлює документацію,\n* змінює лише фіксовані текстові рядки (наприклад, оновлення перекладу)\n\nЦе захистить travis від запуску тестів на змінах, які не покриті тестами.\n\n### Огляд команд (для досвідчених учасників)\n\n```\ngit clone git@github.com:YOUR-GITHUB-USERNAME/yii2.git\ngit remote add upstream https://github.com/yiisoft/yii2.git\n```\n\n```\ngit fetch upstream\ngit checkout upstream/master\ngit checkout -b 999-name-of-your-branch-goes-here\n\n/* робіть вашу магію; оновіть журнал змін, якщо необхідно */\n\ngit add path/to/my/file.php\ngit commit -m \"A brief description of this change which fixes #999 goes here\"\ngit pull upstream master\ngit push -u origin 999-name-of-your-branch-goes-here\n```\n"
  },
  {
    "path": "docs/internals-uk/report-an-issue.md",
    "content": "Повідомлення про проблему\n=========================\n\nБудь ласка, дотримуйтесь рекомендацій, зазначених нижче, при створенні питання, щоб прискорити його вирішення:\n\n* Подавайте інформацію, включаючи: версію PHP та Yii, тип операційної системи та веб-сервера, тип та версію браузера.\n* Додавайте **повний** вивід про помилку, якщо є. Знімок екрану, що пояснює проблему, дуже вітається.\n* Опишіть кроки, які призводять до помилки. Було б ще краще, надати код для відтворення проблеми.\n* Якщо можливо створіть модульний тест, навіть провальний, та [надішліть його як \"pull request\"](git-workflow.md).\n\nЯкщо проблема повʼязана з одним із офіційних розширень, будь ласка, повідомляйте про неї у репозиторії цього розширення.\nЯкщо ви не впевнені, [повідомляйте у головному репозиторії](https://github.com/yiisoft/yii2/issues/new) (<https://github.com/yiisoft/yii2/issues>).\n\n**Не повідомляйте про проблему, якщо:**\n\n* Ви хочете спитати як використовувати ту чи іншу можливість Yii. Для цього користуйтесь [форумом](https://forum.yiiframework.com/index.php/forum/42-general-discussions-for-yii-20/) або [чатом](https://www.yiiframework.com/chat/).\n* Проблема стосується безпеки. Будь ласка, [звертайтесь безпосередньо до розробників](https://www.yiiframework.com/security/), щодо проблем з безпекою.\n\n**Уникайте дублювання питань**\n\nПеред тим, як повідомити про проблему, будь ласка, здійсніть пошук серед [створених запитань](https://github.com/yiisoft/yii2/issues),\nможливо про проблему вже повідомили або проблема вже вирішена. Також переконайтесь, що маєте останню версію Yii та у разі оновлення перевірте чи проблема при цьому присутня.\n"
  },
  {
    "path": "docs/internals-uk/translation-workflow.md",
    "content": "Процес перекладу\n================\n\nYii перекладається на багато різних мов, щоб бути корисним для міжнародних додатків та розробників. Основними двома областями,\nде вітається співпраця є документація та повідомлення фреймворку.\n\nПовідомлення фреймворку\n-----------------------\n\nФреймворк має два типи повідомлень: виключення, які призначені для розробників й ніколи не перекладаються, та повідомлення\nвидимі кінцевому користувачу, такі як помилки перевірки.\n\nЩоб розпочати переклад повідомлень необхідно:\n\n1. Перейти до директорії `framework` та виконати команду `./yii message/extract @yii/messages/config.php --languages=uk`.\n2. Перекласти повідомлення у файлі `framework/messages/uk/yii.php`. Зберегти файл у кодуванні UTF-8.\n3. [Відправити \"pull request\"](git-workflow.md).\n\nДля підтримання перекладу в актуальному стані можна знову використовувати команду `./yii message/extract @yii/messages/config.php --languages=uk`.\nЦе автоматично здобуде нові повідомлення, зберігаючи при цьому вже перекладені.\n\nУ файлі перекладу кожний ключ елементу масиву представляє повідомлення, а значення елементу масиву представляє переклад.\nЯкщо значення порожнє, повідомлення вважається не перекладеним. Переклади повідомлень, які вже не потребують перекладу,\nзамкнуті між парами знаків '@@'. Текст перекладу може містити формат кількох форм множини.\nОзнайомтесь з розділом посібника [Інтернаціоналізація](../guide-uk/tutorial-i18n.md) для більш детальної інформації.\n\nДокументація\n------------\n\nПереклад документації знаходиться у `docs/<original>-uk`, де `<original>` відповідає оригінальній директорії,\nнаприклад `guide` або `internals`.\n\nПісля завершення розпочатої роботи можна отримати звіт про стан перекладу за допомогою виклику спеціальної команди з\nдиректорії `build`:\n\n```\nphp build translation \"../docs/guide\" \"../docs/guide-uk\" \"Ukrainian guide translation report\" > report_guide_uk.html\n```\n\nУ разі необхідності встановіть [Composer](https://getcomposer.org/) в кореневій директорії вашого локального репозиторію.\n\n### Список документів\n\nПерелік документів, що потребують перекладу, можна знайти за нижченаведеними посиланнями:\n\n- [список документів для guide-uk](https://ethercalc.org/yii2.docs.guide-uk);\n- [список документів для internals-uk](https://ethercalc.org/yii2.docs.internals-uk).\n\nПеред тим, як розпочати переклад, переконайтесь, що їм ніхто не займається, та запишіть себе у списку документів.\n\nВ залежності від прогресу оберіть відповідний статус перекладу:\n- В роботі — переклад готується перекладачем до відправлення \"pull request\";\n- Ревізія — відправлений переклад перевіряється ревізором;\n- Перекладено — переклад прийнято до головної (master) гілки проекту.\n\nЗа додатковою інформацією можете звертатись до учасників української [команди перекладачів](../internals/translation-teams.md).\n\n### Переклад зображень\n\nЗображення до документації знаходяться у вкладеній директорії `images`. Усі вони створенні програмою [yED](https://www.yworks.com/en/products/yfiles/yed/).\nПри необхідності перекладу оригінальний файл копіюється в директорію `images` перекладу, перекладається та зберігається у форматі png.\n\nПерелік зображень, що потребують перекладу, можна знайти за нижченаведеним посиланням:\n\n- [список зображень для guide-uk](https://ethercalc.org/yii2.docs.guide-uk.images).\n\n### Переклад спеціальних повідомлень\n\n- Tip → Підказка\n- Note → Примітка\n- Info → Інформація\n- Warning → Попередження\n\n### Список термінів\n\n[Англійсько-українські словники](https://e2u.org.ua)\n\n- action — дія;\n- Active Record — (не перекладається);\n- Advanced/Basic Project Template — Розширений/Базовий шаблон проекту;\n- alias — псевдонім;\n- alphanumeric — буквено-цифровий;\n- (Web) application — (веб-)додаток;\n- assignment — призначення;\n- attach handler — приєднати обробник;\n- attribute of the model — атрибут моделі;\n- authentication — аутентифікація / установлення справжності;\n- authorization — авторизація/уповноваження;\n- autoloader — автозавантажувач;\n- back-end — (не перекладається);\n- backward compatibility / BC — зворотна сумісність;\n- bootstrap, bootstrapping — початкове завантаження;\n- branch — гілка;\n- browser — браузер;\n- (asset) bundle — колекція (ресурсів);\n- cache, caching — кеш, кешування;\n- camel case — (не перекладається);\n- case-sensitive — регістр-залежний;\n- column — колонка;\n- commit — комміт;\n- concatenation — конкатенація;\n- configuration — конфігурація;\n- content — вміст;\n- content view — вкладене представлення;\n- contributor — учасник;\n- cookies — кукі;\n- customization — (тонке) налаштування;\n- debug mode — режим налагодження (див. production mode);\n- debugger — налагоджувач;\n- (function) declaration — оголошення (функції);\n- definition — визначення;\n- design pattern — шаблон проектування;\n- development mode — режим розробки;\n- (root) directory — (коренева) директорія;\n- eager loading — жадібне завантаження (див. lazy loading);\n- email address — адреса електронної пошти;\n- environment — середовище;\n- exception — виключення;\n- existing — наявний/присутній; // перекладати як \"існуючий\" не вірно\n- (PHP) extension — розширення (PHP);\n- Facebook — Фейсбук;\n- field (of the table) — поле/атрибут (таблиці);\n- fixture — фікстура;\n- folder — папка/каталог;\n- footer — футер;\n- fork — форк;\n- formatter — форматтер;\n- framework — фреймворк;\n- front-controller — фронт-контролер;\n- front-end — (не перекладається);\n- getter — геттер;\n- (event) handler — обробник (події);\n- hash — хеш;\n- header — шапка;\n- help — довідка;\n- helper — хелпер;\n- id — ідентифікатор;\n- image — зображення;\n- initialize — ініціалізувати/встановлювати;\n- to initiate — ініціювати/розпочинати;\n- instance — екземпляр;\n- instantiate — створювати екземпляр;\n- issue — питання/проблема; // в залежності від контексту\n- layout — макет;\n- lazy loading — відкладене завантаження;\n- log, logging — журнал, журналювання;\n- markdown — (не перекладається);\n- method — метод (обʼєкта/класу);\n- merge — поєднання;\n- Model-View-Controller (MVC) — Модель-Представлення-Контролер (MVC);\n- namespace — простір імен;\n- out of the box — \"з коробки\";\n- package — пакунок;\n- pagination — розділення на сторінки;\n- parameter — параметр;\n- to parse — обробляти;\n- (application) performance — швидкодія (додатка);\n- placeholder — заповнювач;\n- plugin — плагін;\n- postprocessing — після-обробка;\n- predefined — попередньо визначений;\n- production mode — робочий режим (див. debug mode);\n- profiling — профілювання;\n- property — властивість (обʼєкта);\n- pull request — (не перекладається);\n- query builder — конструктор запитів;\n- refactoring — рефакторинг;\n- to render, rendering — формувати, формування;\n- related, relation — повʼязаний, звʼязок;\n- release — реліз;\n- repo, repository — репозиторій;\n- resolve request — попередня обробка запиту;\n- route, routing — маршрут, маршрутизація;\n- row (of the table) — рядок (таблиці);\n- screenshot — знімок екрану;\n- Service Locator — Локатор служб;\n- setter — сеттер;\n- shared hosting — віртуальний хостинг;\n- (call) stack — стек (викликів);\n- staging area — буферна зона;\n- standalone — автономний;\n- string — текстовий рядок;\n- sub-directory — під-директорія;\n- substitution — підставлення/заміщення;\n- tabular input — табличний ввід;\n- tag — тег;\n- template engine — шаблонізатор;\n- theming — темізація;\n- third party — сторонній;\n- thumbnail — мініатюра;\n- tracing — трасування;\n- trait — трейт;\n- trigger event — породжувати подію;\n- Twitter — Твіттер;\n- Unicode — (не перекладається);\n- unit tests — модульні тести;\n- to validate — перевіряти;\n- valid — коректний;\n- validator — валідатор;\n- validation — перевірка;\n- validator class — клас валідатора;\n- versioning — версіонування;\n- widget — віджет.\n"
  },
  {
    "path": "docs/internals-uk/versions.md",
    "content": "Версіонування Yii\n=================\n\nЦей документ описує політику призначення версій Yii. Поточна стратегія призначення версій\nбазується на [ferver](https://github.com/jonathanong/ferver), це за думкою розробників є більш практичним\nта розумним рішенням, ніж використання [semver](https://semver.org/) (див. [#7408](https://github.com/yiisoft/yii2/issues/7408) для довідки).\n\nУ колі головних розробників неодноразово підкреслювалась важливість зберігати зворотну сумісність релізів 2.0.x на 100%.\nАле це ідеалістичний план. Стаття про ferver доводить, що досягнути цього на практиці дуже важко,\nне зважаючи на те, використовується semver чи ні.\n\nЗагалом, політика призначення версій наступна:\n\n## Патч-релізи `2.x.Y`\n\nПатч-релізи, які мають бути на 100% зворотно сумісними. В ідеалі, вони містять лише виправлення помилок, що зменшує\nможливість порушення зворотної сумісності. На практиці, релізи починаючи з 2.0.x стали частішими та зазвичай містять невеликі доповнення,\nщо дає можливість користувачам почати використовувати ці зміни раніше.\n\n* Підтримуються у гілці `2.x`\n* Переважно містять виправлення помилок та невеликі покращення\n* Відсутні великі зміни та доповнення\n* 100%-ва зворотна сумісність, що гарантує оновлення без проблем. Виключенням можуть бути лише проблеми безпеки, які потребують порушення зворотної сумісності\n* Цикл релізу близько 1-2 місяців\n* Не має необхідності у пре-релізах (альфа, бета, реліз-кандидат)\n* Регулярно обʼєднуються з головною (master) гілкою (щонайменш раз у тиждень вручну)\n\n\n## Молодші (мінорні) релізи `2.X.0`\n\nЗворотно несумісні релізи, що містять великі доповнення та зміни, які можуть порушувати зворотну сумісність. Оновлення з ранніх версій\nможе бути не простим, але у наявності повна інструкція по оновленню або навіть скрипт.\n\n* Розроблюються у головній (майстер) гілці\n* Переважно містять нові доповнення та виправлення помилок\n* Містять невеликі доповнення та виправлення помилок з патч-релізів\n* Можуть мати зворотно несумісні зміни, які записуються у файл `UPGRADE-2.X.md`\n* Цикл релізу близько 6-8 місяців\n* Необхідні пре-релізи: `2.X.0-alpha`, `2.X.0-beta`, `2.X.0-rc`\n* Потребують маркетингових зусиль та публікування у головних новинах\n\n\n## Основні (мажорні) релізи `X.0.0`\n\nЦе наче 2.0 після 1.0. Такий перехід, вірогідніше, буде не частіше ніж кожні 3-5 років, у звʼязку з просуванням сторонніх технологій\n(наприклад, оновлення PHP з 5.0 до 5.4).\n\n> Примітка: Офіційні розширення використовують таку ж саму політику призначення версій, але можуть публікуватись незалежно від\nфреймворку, тобто номера версій фреймворку та розширення не повинні обовʼязково збігатися.\n"
  },
  {
    "path": "docs/internals-uk/view-code-style.md",
    "content": "Оформлення коду представлень у Yii 2\n====================================\n\nНижченаведений стиль кодування використовується у представленнях основи Yii 2.x та у представленнях офіційних розширень.\nКоманда розробників не наполягає на використанні цього стилю для вашого додатка. Вільно обирайте те, що підходить вам більше.\n\n```php\n<?php\n// Початковий тег PHP, за яким йде пустий рядок, є обовʼязковим для усіх файлів шаблонів.\n\n// Опис вхідних змінних, які передає контролер.\n/**\n * @var \\yii\\base\\View $this\n * @var \\yii\\widgets\\ActiveForm $form\n * @var \\app\\models\\Post[] $posts\n * @var \\app\\models\\ContactMessage $contactMessage\n */\n// Пустий рядок після є необхідним.\n\n// Декларування класів з просторів імен.\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n// Пустий рядок після є необхідним.\n\n// Призначення властивостей контексту, виклики їх сеттерів, інші речі.\n$this->title = 'Posts';\n?>\n<!-- Для foreach, for, if, і т. п. краще використовувати роздільні блоки PHP. -->\n<?php foreach ($posts as $post): ?>\n    <!-- Тут зверніть увагу на відступи. -->\n    <h2><?= Html::encode($post['title']) ?></h2>\n    <p><?= Html::encode($post['shortDescription']) ?></p>\n<!-- `endforeach;`, `endfor;`, `endif;`, і т. п. потрібно використовувати замість `}` у випадку використання багатьох блоків PHP -->\n<?php endforeach; ?>\n\n<!-- При декларуванні віджету код може міститись як на одному так і на багатьох рядках. -->\n<?php $form = ActiveForm::begin([\n    'options' => ['id' => 'contact-message-form'],\n    'fieldConfig' => ['inputOptions' => ['class' => 'common-input']],\n]); ?>\n    <!-- Тут зверніть увагу на відступи. -->\n    <?= $form->field($contactMessage, 'name')->textInput() ?>\n    <?= $form->field($contactMessage, 'email')->textInput() ?>\n    <?= $form->field($contactMessage, 'subject')->textInput() ?>\n    <?= $form->field($contactMessage, 'body')->textArea(['rows' => 6]) ?>\n\n    <div class=\"form-actions\">\n        <?= Html::submitButton('Обробити', ['class' => 'common-button']) ?>\n    </div>\n<!-- Виклик завершення віджету має окремий блок PHP. -->\n<?php ActiveForm::end(); ?>\n<!-- Кінцевий знак нового рядка є обовʼязковим. -->\n\n```\n"
  },
  {
    "path": "docs/internals-uz/translation-workflow.md",
    "content": "O'zbekchaga tarjima qilish bilan qanday ishlash kerak\n=====================================================\n\nYii juda ko'p tillarga tarjima qilinayabdi shu jumladan o'zbekchaga ham. Tarjima qo'llanma va habarlarni o'z ichiga oladi.\n\nFreymvork habari\n----------------\n\nIkki turdagi habarlar bor: istisnolar, qaysiki ishlab chiquvchi nazarda tutgan va ular tarjima qilinmaydi va habarlar, qaysiki foydalanuvchilarga ko'rsatiladigan. Masalan, validatsiyaning xatoliklari.\n\nTarjimani yangilash uchun:\n\n1. Konsolda `framework` direktoriyani ochamiz, `./yii message/extract @yii/messages/config.php --languages=uz` ni ishga tushiramiz.\n3. Habarlarni `framework/messages/uz/yii.php` ga ko`chiramiz. Muhimi fayllar UTF-8 kodlashda bo'lishi kerak.\n4. `uz` dagi tarjimalar bilan [pull request qilamiz](https://github.com/yiisoft/yii2/blob/master/docs/internals/git-workflow.md), qolgan tillarga tegmaymiz.\n\nTarjima fayllarda massiv joylashgan. Uning kalitlari - boshlang'ich kodlar, qiymatlari - tarjima. Agar qiymat bo'sh bo'lsa habar tarjima qilinmagan hisoblanadi. Kodda boshqa uchramaydigan habarlar tarjimasi '@@' ga o'ralgan. Ayrim habarlar uchun [sonlar bilan qo'llanilishini qo'llab-quvvatlash uchun maxsus format](../guide-uz/tutorial-i18n.md) ni ishlatish zarur.\n\nQo'llanma\n---------\n\nQo'llanamani tarjimasi `docs/<original>-uz` da joylashgan, bu yerda <original> - original direktoriyalarga mos keladi, masalan,\n`guide` yoki `internals`.\n\nAgar qo'llanma tarjimasi tugagan bo'lsa, build direktoriyasida konsolni ochib va quyidagini bajarib, originaldagi oxirgi tarjimadan keyingi diff o'zgarishlarni olish mumkin:\n\n```\nphp build translation \"../docs/guide\" \"../docs/guide-uz\" \"Uzbek guide translation report\" > report_guide_uz.html\n```\n\nAgar composer uchun urushsa, bosh direktoriyada `composer install` ni bajaring.\n\nTarjima qilishdan oldin hech kim shug'ullanmayotganligini tekshiring va o'zingizga [barcha tarjima qilinayotgan hujjatlarni ro'yhatini] yozib oling\n//Ushbu manzilni ulardan olgandan keyin o'zgartirib qo'yish kerak bo'ladi.\n(https://docs.google.com/spreadsheets/d/10dS7VB_3jSxUorryRlplB7nhA59e3i2vLYmTwn_1d3I/edit?usp=sharing).\n\nBarcha o'zgarishlarni quyidagi ko'rinishda olib boramiz [pull request](https://github.com/yiisoft/yii2/blob/master/docs/internals/git-workflow.md).\n\n\n### Umumiy qoidalar\n\n- Ko'p terminlar o'zbekchada bitta ma'noga ega emas va o'zbekchada ko'p qo'llanilmaydigandir, shu sababli agar matnda \n  shunday terminlar uchrasa ularni birinchi bor qo'llanilgan joyida qavs ichida ingliz tilidagi varianti \n  ko'rsatilishi kerak; (terminlarning tarjimasini qo'llanilish variantlari quyida keltirilgan); \n- Agar tarjima vaqtida matnning qaysidir qismi mazmuni o'zgarayotgan bo'lsa va siz uni shunday tarjima qilish \n  kerakligiga ishonchingiz komil bo'lmayotganday tuyulsa ushbu qismni * ichiga oling(ichidagi matn qiya holatga keladi). \n  Bu olib tashlash/to'g'rilashlar vaqtida ushbu matnlarga alohida e'tibor qaratishga imkon beradi; \n- Tarjima vaqtida fakt xatoliklarni qilmang! \n- Matnda tashqi manbalarga murojaatlar uchraydi, agar murojaat terminni izohlash maqolasiga olib boradigan bo'lsa, u holda \n  o'zbekchada tarjimasi bo'r bo'lgan vaziyatda o'zbekchasiga murojaat qilinadi.\n  Masalan `https://en.wikipedia.org/wiki/Captcha` → `https://uz.wikipedia.org/wiki/Captcha`.\n- Sharhlar kodda tarjima qilinadi, agarda birinchi holatdagi mazmunini o'zgartirmasa; Matndagi vaqtinchalik sharhlarni \n  imkon qadar faqat *lokal*da ishlatish kerak! Aks holda uni reliz ga tushib qolish ehtimoli bor; \n- Bo'limlarni tarjima qilish vaqtida `README.md` dagi tarjimani olib ketamiz; \n- Shaxsiy qo'shimcha-sharhlarni qo'shish imkoni bor, lekin xaosdan qochish uchun original bitta bo'lishi kerak. Bunga zarurat vaqtida sharh oxiriga \n  \"tar. shar.\" ni qo'shish kerak;\n- Hujjatni umumiy to'g'rilashni o'tkazgandan so'ng mustaqil ravishda faqat ushbu bo'limga tegsihli bo'lgan grammatikadagi, fakt xatoliklardagi o'zgarishlarni kiritish talab darajasida tavsiya etiladi. Boshqa holatlarda gaplarni to'g'rilash, yaxshilash uchun va zarur hollarda o'zgarishlarni markazlashgan tarzda barcha bo'limlar uchun amalga oshirish uchun ularni tahlilga qo'yish zarur.\n   \n\n### Qo'llanma strukturasi\n\nTarjima qilish vaqtida hujjatning struktura birligini to'g'ri nomlash muhim. Quyida keltirilgan strukturaga amal qilamiz :\n\n- Bob 1 \n  - Bo'lim 1 \n  - Bo'lim 2 \n    - Qism bo'lim 1 \n  - ... \n  - Bo'lim N \n- Bob 2 \n- ... \n- Bob N\n \n### Maxsus habarlarni tarjimasi\n\n- Tip → Ko'rsatma \n- Note → Eslatma \n- Info → Ma'lumot \n\n### Rasmlarni tarjima qilish\n\nQo'llanma uchun rasmlar `images` qism katalogida joylashgan. Ularning barchasi [yED](https://www.yworks.com/en/products_yed_about.html) da yaratilgan.\nZarurat tug'ilgan vaqtda fayl tarjima qilinayotgan katalogning `images` katalogiga nusxalanadi va tarjima qilinib png formatida saqlanadi.\n\nRasmlardagi yozuvlar tarjima qilinadi.\n\n### Grammatika\n\n\nOxirgi variantni tashlashdan oldin uni umumiy stilini, orfografiyasini, punktlarini tekshiring. Tarjimani o'zbek tili uchun orfografiyani Wordda tekshirish imkoni bo'lmasada uning yordamida tayyorlashingiz mumkin;\n\n### Terminlar ro'yhati\n\n- action — amal. \n- active record — tarjimasiz. \n- attach handler — «qayta ishlovchini tayinlash».\n- attribute of the model — model atributi. \n- camel case — tarjimasiz. \n- customization — (nozik) sozlash \n- column — ustun (agar MO haqida gap ketayotgan bo'lsa). \n- content — tashkil etuvchilari. \n- controller — kontrollyor. \n- debug (mode) — kod sozlash (tartibi) (production mode ga qarang). \n- eager loading — ziqna yuklash uslubi/ziqna yuklash (lazy loading ga qarang). \n- PHP extension — PHP kengaytmasi. \n- field (of the table) — jadval (yoki atribut) maydoni. \n- framework — freymvork. \n- front-controller — front-kontrollyor. \n- getter — getter. \n- (event) handler — qayta ishlovchi (hodisa). \n- hash — xesh. \n- helper - yordamchi. \n- id — qiymatlovchi(identifikatsiyalovchi). \n- instance — nusxa. \n- lazy loading — chetga olingan yuklash (qanday kerak bo'lsa shunday yuklasymiz va erta emas). \n- method — metod (obektniki) //Diqqat! Obekt/klaslarda funksiyasi yo'q, faqat metodlari bor. \n- model — model, ma`lumotlar modeli. \n- model form — formalar modeli. \n- parameter — parametr (metod yoki funksiyada va lekin klasda emas). \n- to parse — qayta ishlash, agar *kontekst* tushunarsiz bo'lsa *parsing* qilish. \n- placeholder — marker. \n- production (mode) — ishlab chiqarish (rejimi) (см. debug mode). \n- property — xususiyati (obekt). \n- to render — *render* qilish, formallashtirish. \n- related, relation — bog'langan, bog'lanish.\n- resolve request — so'rovni oldindan qayta ishlash. \n- route — marshrut. \n- row (of the table) — satr(jadvallarniki). \n- setter — setter. \n- tabular input — tabli kiritish. \n- to validate — tekshirish. \n- valid — mos. \n- validator — validator. \n- validator class — validator klasi. \n- view — namoyish.\n- query builder — so'rovlar konstruktori.\n"
  },
  {
    "path": "eslint.config.js",
    "content": "module.exports = [\n    {\n        rules: {\n            \"comma-dangle\": [\"error\", \"never\"],\n            \"no-cond-assign\": \"error\",\n            \"no-constant-condition\": \"error\",\n            \"no-control-regex\": \"error\",\n            \"no-debugger\": \"error\",\n            \"no-dupe-args\": \"error\",\n            \"no-dupe-keys\": \"error\",\n            \"no-duplicate-case\": \"error\",\n            \"no-empty\": \"error\",\n            \"no-empty-character-class\": \"error\",\n            \"no-ex-assign\": \"error\",\n            \"no-extra-boolean-cast\": \"error\",\n            \"no-extra-semi\": \"error\",\n            \"no-func-assign\": \"error\",\n            \"no-inner-declarations\": [\"error\", \"functions\"],\n            \"no-invalid-regexp\": \"error\",\n            \"no-irregular-whitespace\": \"error\",\n            \"no-negated-in-lhs\": \"error\",\n            \"no-obj-calls\": \"error\",\n            \"no-regex-spaces\": \"error\",\n            \"no-sparse-arrays\": \"error\",\n            \"no-unexpected-multiline\": \"error\",\n            \"no-unreachable\": \"error\",\n            \"use-isnan\": \"error\",\n            \"valid-typeof\": \"error\",\n            \"accessor-pairs\": \"error\",\n            complexity: [\"warn\", 6],\n            eqeqeq: \"warn\",\n            \"guard-for-in\": \"error\",\n            \"no-alert\": \"warn\",\n            \"no-caller\": \"error\",\n            \"no-case-declarations\": \"error\",\n            \"no-div-regex\": \"error\",\n            \"no-labels\": \"error\",\n            \"no-empty-pattern\": \"error\",\n            \"no-eq-null\": \"warn\",\n            \"no-eval\": \"error\",\n            \"no-extend-native\": \"error\",\n            \"no-extra-bind\": \"error\",\n            \"no-fallthrough\": \"error\",\n            \"no-implied-eval\": \"error\",\n            \"no-iterator\": \"error\",\n            \"no-lone-blocks\": \"error\",\n            \"no-loop-func\": \"warn\",\n            \"no-multi-str\": \"error\",\n            \"no-native-reassign\": \"error\",\n            \"no-new-func\": \"error\",\n            \"no-new-wrappers\": \"warn\",\n            \"no-new\": \"error\",\n            \"no-octal-escape\": \"error\",\n            \"no-octal\": \"error\",\n            \"no-proto\": \"error\",\n            \"no-redeclare\": \"error\",\n            \"no-return-assign\": \"error\",\n            \"no-script-url\": \"error\",\n            \"no-self-compare\": \"error\",\n            \"no-unused-expressions\": \"warn\",\n            \"no-useless-call\": \"error\",\n            \"no-useless-concat\": \"error\",\n            \"no-void\": \"error\",\n            \"no-with\": \"error\",\n            radix: \"error\",\n            \"wrap-iife\": \"warn\",\n            \"no-catch-shadow\": \"error\",\n            \"no-delete-var\": \"error\",\n            \"no-label-var\": \"error\",\n            \"no-shadow-restricted-names\": \"error\",\n            \"no-undef-init\": \"warn\",\n            \"callback-return\": \"error\",\n            \"global-require\": \"warn\",\n            \"handle-callback-err\": \"error\",\n            \"no-path-concat\": \"error\",\n            \"no-process-exit\": \"error\",\n            \"max-statements\": [\"warn\", 30],\n        },\n    },\n];\n"
  },
  {
    "path": "framework/.github/CONTRIBUTING.md",
    "content": "Contributing to Yii 2\n=====================\n\nPlease use [yiisoft/yii2](https://github.com/yiisoft/yii2) to report issues and send pull requests. Thank you!\n"
  },
  {
    "path": "framework/.github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\nopen_collective: yiisoft\ngithub: [yiisoft]\ntidelift: \"packagist/yiisoft/yii2\"\n"
  },
  {
    "path": "framework/.github/PULL_REQUEST_TEMPLATE.md",
    "content": "<!--\nPlease use https://github.com/yiisoft/yii2 to report issues and send pull requests. Thank you!\n-->\n"
  },
  {
    "path": "framework/.github/SECURITY.md",
    "content": "# Security Policy\n\nPlease use the [security issue form](https://www.yiiframework.com/security) to report to us any security issue you find in Yii.\nDO NOT use the issue tracker or discuss it in the public forum as it will cause more damage than help.\n\nPlease note that as a non-commerial OpenSource project we are not able to pay bounties at the moment.\n"
  },
  {
    "path": "framework/.gitignore",
    "content": "phpunit.xml\ncomposer.lock\n\n"
  },
  {
    "path": "framework/.htaccess",
    "content": "deny from all\n"
  },
  {
    "path": "framework/.meta-storm/active-record.meta-storm.xml",
    "content": "<?xml version=\"1.0\"?>\n<meta-storm xmlns=\"meta-storm\">\n    <definitions>\n        <classMethod class=\"\\yii\\db\\BaseActiveRecord\" method=\"canGetProperty\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\BaseActiveRecord\" method=\"canSetProperty\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\BaseActiveRecord\" method=\"__get\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\BaseActiveRecord\" method=\"__set\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\BaseActiveRecord\" method=\"__isset\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\BaseActiveRecord\" method=\"__unset\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n\n        <classMethod class=\"\\yii\\db\\BaseActiveRecord\" method=\"hasAttribute\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\BaseActiveRecord\" method=\"getAttribute\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\BaseActiveRecord\" method=\"setAttribute\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\BaseActiveRecord\" method=\"getOldAttribute\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\BaseActiveRecord\" method=\"setOldAttribute\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\BaseActiveRecord\" method=\"canSetOldAttribute\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\BaseActiveRecord\" method=\"markAttributeDirty\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\BaseActiveRecord\" method=\"isAttributeChanged\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\BaseActiveRecord\" method=\"markAttributeDirty\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\BaseActiveRecord\" method=\"resetDependentRelations\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\BaseActiveRecord\" method=\"setRelationDependencies\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n\n        <classMethod class=\"\\yii\\db\\ActiveRecord\" method=\"hasOne\" argument=\"1\" targetValue=\"false\" targetInArray=\"key\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$argument[0]\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\ActiveRecord\" method=\"hasOne\" argument=\"1\" targetValue=\"false\" targetInArray=\"value\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$containingClass\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\ActiveRecord\" method=\"hasMany\" argument=\"1\" targetValue=\"false\" targetInArray=\"key\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$argument[0]\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\ActiveRecord\" method=\"hasMany\" argument=\"1\" targetValue=\"false\" targetInArray=\"value\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$containingClass\"/>\n        </classMethod>\n    </definitions>\n</meta-storm>\n"
  },
  {
    "path": "framework/.meta-storm/array.meta-storm.xml",
    "content": "<?xml version=\"1.0\"?>\n<meta-storm xmlns=\"meta-storm\">\n    <definitions>\n        <classMethod class=\"\\yii\\helpers\\BaseArrayHelper\" method=\"getValue\" argument=\"1\">\n            <properties protected=\"false\" private=\"false\" relatedArgument=\"0\" relatedTo=\"argument\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\helpers\\BaseArrayHelper\" method=\"index\" argument=\"1\">\n            <properties protected=\"false\" private=\"false\" relatedArgument=\"0\" relatedTo=\"argument\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\helpers\\BaseArrayHelper\" method=\"getColumn\" argument=\"1\">\n            <properties protected=\"false\" private=\"false\" relatedArgument=\"0\" relatedTo=\"argument\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\helpers\\BaseArrayHelper\" method=\"map\" argument=\"1\">\n            <properties protected=\"false\" private=\"false\" relatedArgument=\"0\" relatedTo=\"argument\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\helpers\\BaseArrayHelper\" method=\"map\" argument=\"2\">\n            <properties protected=\"false\" private=\"false\" relatedArgument=\"0\" relatedTo=\"argument\"/>\n        </classMethod>\n    </definitions>\n</meta-storm>\n"
  },
  {
    "path": "framework/.meta-storm/controller.meta-storm.xml",
    "content": "<?xml version=\"1.0\"?>\n<meta-storm xmlns=\"meta-storm\">\n    <definitions>\n        <classMethod class=\"\\yii\\base\\Controller\" method=\"render\" argument=\"0\">\n            <files extension=\"php\" relatedTo=\"file\">\n                <directoryProcessors>\n                    <regexp from=\"Controller\\.php\" to=\"\"/>\n                    <regexp from=\"([a-z])([A-Z])\" to=\"$1-$2\"/>\n                    <case from=\"/[^/]+$\" case=\"lower\"/>\n                    <regexp from=\"/modules/([^\\\\/]+)/controllers/\" to=\"/themes/default/modules/$1/views/\"/>\n                </directoryProcessors>\n            </files>\n            <files extension=\"php\" relatedTo=\"file\">\n                <directoryProcessors>\n                    <regexp from=\"Controller\\.php\" to=\"\"/>\n                    <regexp from=\"([a-z])([A-Z])\" to=\"$1-$2\"/>\n                    <case from=\"/[^/]+$\" case=\"lower\"/>\n                    <regexp from=\"/controllers/\" to=\"/views/\"/>\n                </directoryProcessors>\n            </files>\n        </classMethod>\n        <classMethod class=\"\\yii\\base\\Controller\" method=\"renderPartial\" argument=\"0\">\n            <files extension=\"php\" relatedTo=\"file\">\n                <directoryProcessors>\n                    <regexp from=\"Controller\\.php\" to=\"\"/>\n                    <regexp from=\"([a-z])([A-Z])\" to=\"$1-$2\"/>\n                    <case from=\"/[^/]+$\" case=\"lower\"/>\n                    <regexp from=\"/modules/([^\\\\/]+)/controllers/\" to=\"/themes/default/modules/$1/views/\"/>\n                </directoryProcessors>\n            </files>\n            <files extension=\"php\" relatedTo=\"file\">\n                <directoryProcessors>\n                    <regexp from=\"Controller\\.php\" to=\"\"/>\n                    <regexp from=\"([a-z])([A-Z])\" to=\"$1-$2\"/>\n                    <case from=\"/[^/]+$\" case=\"lower\"/>\n                    <regexp from=\"/controllers/\" to=\"/views/\"/>\n                </directoryProcessors>\n            </files>\n        </classMethod>\n        <classMethod class=\"\\yii\\base\\Controller\" method=\"renderFile\" argument=\"0\">\n            <files extension=\"php\" relatedTo=\"file\">\n                <directoryProcessors>\n                    <regexp from=\"Controller\\.php\" to=\"\"/>\n                    <regexp from=\"([a-z])([A-Z])\" to=\"$1-$2\"/>\n                    <case from=\"/[^/]+$\" case=\"lower\"/>\n                    <regexp from=\"/modules/([^\\\\/]+)/controllers/\" to=\"/themes/default/modules/$1/views/\"/>\n                </directoryProcessors>\n            </files>\n            <files extension=\"php\" relatedTo=\"file\">\n                <directoryProcessors>\n                    <regexp from=\"Controller\\.php\" to=\"\"/>\n                    <regexp from=\"([a-z])([A-Z])\" to=\"$1-$2\"/>\n                    <case from=\"/[^/]+$\" case=\"lower\"/>\n                    <regexp from=\"/controllers/\" to=\"/views/\"/>\n                </directoryProcessors>\n            </files>\n        </classMethod>\n    </definitions>\n    <envs>\n        <env name=\"yiisoft/yii2:view-theme\" value=\"default\"/>\n    </envs>\n</meta-storm>\n"
  },
  {
    "path": "framework/.meta-storm/db.meta-storm.xml",
    "content": "<?xml version=\"1.0\"?>\n<meta-storm xmlns=\"meta-storm\">\n    <definitions>\n        <classMethod class=\"\\yii\\db\\QueryInterface\" method=\"where\" argument=\"0\" targetValue=\"false\" targetInArray=\"key\">\n            <properties xpath=\"$variable\" protected=\"false\" static=\"false\" private=\"false\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\QueryInterface\" method=\"andWhere\" argument=\"0\" targetValue=\"false\" targetInArray=\"key\">\n            <properties xpath=\"$variable\" protected=\"false\" static=\"false\" private=\"false\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\QueryInterface\" method=\"orWhere\" argument=\"0\" targetValue=\"false\" targetInArray=\"key\">\n            <properties xpath=\"$variable\" protected=\"false\" static=\"false\" private=\"false\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\QueryInterface\" method=\"filterWhere\" argument=\"0\" targetValue=\"false\" targetInArray=\"key\">\n            <properties xpath=\"$variable\" protected=\"false\" static=\"false\" private=\"false\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\QueryInterface\" method=\"andFilterWhere\" argument=\"0\" targetValue=\"false\" targetInArray=\"key\">\n            <properties xpath=\"$variable\" protected=\"false\" static=\"false\" private=\"false\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\QueryInterface\" method=\"orFilterWhere\" argument=\"0\" targetValue=\"false\" targetInArray=\"key\">\n            <properties xpath=\"$variable\" protected=\"false\" static=\"false\" private=\"false\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\Query\" method=\"filterHaving\" argument=\"0\" targetValue=\"false\" targetInArray=\"key\">\n            <properties xpath=\"$variable\" protected=\"false\" static=\"false\" private=\"false\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\Query\" method=\"andFilterHaving\" argument=\"0\" targetValue=\"false\" targetInArray=\"key\">\n            <properties xpath=\"$variable\" protected=\"false\" static=\"false\" private=\"false\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\Query\" method=\"orFilterHaving\" argument=\"0\" targetValue=\"false\" targetInArray=\"key\">\n            <properties xpath=\"$variable\" protected=\"false\" static=\"false\" private=\"false\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\Query\" method=\"select\" argument=\"0\" targetInArray=\"value\">\n            <properties xpath=\"$variable\" protected=\"false\" static=\"false\" private=\"false\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\Query\" method=\"addSelect\" argument=\"0\" targetInArray=\"value\">\n            <properties xpath=\"$variable\" protected=\"false\" static=\"false\" private=\"false\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\Query\" method=\"groupBy\" argument=\"0\" targetInArray=\"key\">\n            <properties xpath=\"$variable\" protected=\"false\" static=\"false\" private=\"false\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\Query\" method=\"addGroupBy\" argument=\"0\" targetInArray=\"key\">\n            <properties xpath=\"$variable\" protected=\"false\" static=\"false\" private=\"false\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\QueryInterface\" method=\"orderBy\" argument=\"0\" targetInArray=\"key\">\n            <properties xpath=\"$variable\" protected=\"false\" static=\"false\" private=\"false\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\QueryInterface\" method=\"addOrderBy\" argument=\"0\" targetInArray=\"key\">\n            <properties xpath=\"$variable\" protected=\"false\" static=\"false\" private=\"false\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\db\\QueryInterface\" method=\"indexBy\" argument=\"0\" targetInArray=\"key\">\n            <properties xpath=\"$variable\" protected=\"false\" static=\"false\" private=\"false\"/>\n        </classMethod>\n    </definitions>\n</meta-storm>\n"
  },
  {
    "path": "framework/.meta-storm/html.meta-storm.xml",
    "content": "<?xml version=\"1.0\"?>\n<meta-storm xmlns=\"meta-storm\">\n    <definitions>\n        <classMethod class=\"\\yii\\helpers\\BaseHtml\" method=\"tag\" argument=\"0\">\n            <collection name=\"GLOBAL:html-tags\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\helpers\\BaseHtml\" method=\"beginTag\" argument=\"0\">\n            <collection name=\"GLOBAL:html-tags\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\helpers\\BaseHtml\" method=\"endTag\" argument=\"0\">\n            <collection name=\"GLOBAL:html-tags\"/>\n        </classMethod>\n\n        <classMethod class=\"\\yii\\helpers\\BaseHtml\" method=\"activeLabel\" argument=\"1\">\n            <properties protected=\"false\" private=\"false\" relatedArgument=\"0\" relatedTo=\"argument\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\helpers\\BaseHtml\" method=\"activeHint\" argument=\"1\">\n            <properties protected=\"false\" private=\"false\" relatedArgument=\"0\" relatedTo=\"argument\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\helpers\\BaseHtml\" method=\"error\" argument=\"1\">\n            <properties protected=\"false\" private=\"false\" relatedArgument=\"0\" relatedTo=\"argument\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\helpers\\BaseHtml\" method=\"activeInput\" argument=\"2\">\n            <properties protected=\"false\" private=\"false\" relatedArgument=\"1\" relatedTo=\"argument\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\helpers\\BaseHtml\" method=\"activeTextInput\" argument=\"1\">\n            <properties protected=\"false\" private=\"false\" relatedArgument=\"0\" relatedTo=\"argument\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\helpers\\BaseHtml\" method=\"setActivePlaceholder\" argument=\"1\">\n            <properties protected=\"false\" private=\"false\" relatedArgument=\"0\" relatedTo=\"argument\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\helpers\\BaseHtml\" method=\"activeHiddenInput\" argument=\"1\">\n            <properties protected=\"false\" private=\"false\" relatedArgument=\"0\" relatedTo=\"argument\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\helpers\\BaseHtml\" method=\"activePasswordInput\" argument=\"1\">\n            <properties protected=\"false\" private=\"false\" relatedArgument=\"0\" relatedTo=\"argument\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\helpers\\BaseHtml\" method=\"activeFileInput\" argument=\"1\">\n            <properties protected=\"false\" private=\"false\" relatedArgument=\"0\" relatedTo=\"argument\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\helpers\\BaseHtml\" method=\"activeTextarea\" argument=\"1\">\n            <properties protected=\"false\" private=\"false\" relatedArgument=\"0\" relatedTo=\"argument\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\helpers\\BaseHtml\" method=\"activeCheckbox\" argument=\"1\">\n            <properties protected=\"false\" private=\"false\" relatedArgument=\"0\" relatedTo=\"argument\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\helpers\\BaseHtml\" method=\"activeBooleanInput\" argument=\"2\">\n            <properties protected=\"false\" private=\"false\" relatedArgument=\"1\" relatedTo=\"argument\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\helpers\\BaseHtml\" method=\"activeDropDownList\" argument=\"1\">\n            <properties protected=\"false\" private=\"false\" relatedArgument=\"0\" relatedTo=\"argument\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\helpers\\BaseHtml\" method=\"activeListBox\" argument=\"1\">\n            <properties protected=\"false\" private=\"false\" relatedArgument=\"0\" relatedTo=\"argument\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\helpers\\BaseHtml\" method=\"activeCheckboxList\" argument=\"1\">\n            <properties protected=\"false\" private=\"false\" relatedArgument=\"0\" relatedTo=\"argument\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\helpers\\BaseHtml\" method=\"activeRadioList\" argument=\"1\">\n            <properties protected=\"false\" private=\"false\" relatedArgument=\"0\" relatedTo=\"argument\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\helpers\\BaseHtml\" method=\"getAttributeValue\" argument=\"1\">\n            <properties protected=\"false\" private=\"false\" relatedArgument=\"0\" relatedTo=\"argument\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\helpers\\BaseHtml\" method=\"getInputName\" argument=\"1\">\n            <properties protected=\"false\" private=\"false\" relatedArgument=\"0\" relatedTo=\"argument\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\helpers\\BaseHtml\" method=\"getInputId\" argument=\"1\">\n            <properties protected=\"false\" private=\"false\" relatedArgument=\"0\" relatedTo=\"argument\"/>\n        </classMethod>\n    </definitions>\n</meta-storm>\n"
  },
  {
    "path": "framework/.meta-storm/model.meta-storm.xml",
    "content": "<?xml version=\"1.0\"?>\n<meta-storm xmlns=\"meta-storm\">\n    <definitions>\n        <classConstructor class=\"\\yii\\base\\BaseObject\" argument=\"0\" targetValue=\"false\" targetInArray=\"key\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classConstructor>\n\n        <classMethod class=\"\\yii\\base\\Model\" method=\"validate\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\base\\Model\" method=\"getActiveValidators\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\base\\Model\" method=\"isAttributeRequired\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\base\\Model\" method=\"isAttributeSafe\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\base\\Model\" method=\"isAttributeActive\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\base\\Model\" method=\"getAttributeLabel\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\base\\Model\" method=\"getAttributeHint\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\base\\Model\" method=\"hasErrors\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\base\\Model\" method=\"getErrors\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\base\\Model\" method=\"getFirstError\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\base\\Model\" method=\"addError\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\base\\Model\" method=\"clearErrors\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\base\\Model\" method=\"generateAttributeLabel\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\base\\Model\" method=\"onUnsafeAttribute\" argument=\"0\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n    </definitions>\n</meta-storm>\n"
  },
  {
    "path": "framework/.meta-storm/view.meta-storm.xml",
    "content": "<?xml version=\"1.0\"?>\n<meta-storm xmlns=\"meta-storm\">\n    <definitions>\n        <classMethod class=\"\\yii\\web\\View\" method=\"render\" argument=\"0\">\n            <files extension=\"php\" xpath=\"$directory\"/>\n        </classMethod>\n    </definitions>\n</meta-storm>\n"
  },
  {
    "path": "framework/.meta-storm/widgets.meta-storm.xml",
    "content": "<?xml version=\"1.0\"?>\n<meta-storm xmlns=\"meta-storm\">\n    <definitions>\n        <classMethod class=\"\\yii\\base\\Widget\" method=\"widget\" argument=\"0\" targetValue=\"false\" targetInArray=\"key\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\base\\Widget\" method=\"begin\" argument=\"0\" targetValue=\"false\" targetInArray=\"key\">\n            <properties protected=\"false\" private=\"false\" xpath=\"$variable\"/>\n        </classMethod>\n\n        <classMethod class=\"\\yii\\widgets\\ActiveForm\" method=\"field\" argument=\"1\">\n            <properties protected=\"false\" private=\"false\" relatedArgument=\"0\" relatedTo=\"argument\"/>\n        </classMethod>\n        <classMethod class=\"\\yii\\widgets\\ActiveForm\" method=\"beginField\" argument=\"1\">\n            <properties protected=\"false\" private=\"false\" relatedArgument=\"0\" relatedTo=\"argument\"/>\n        </classMethod>\n    </definitions>\n</meta-storm>\n"
  },
  {
    "path": "framework/.phpstorm.meta.php",
    "content": "<?php\n// .phpstorm.meta.php\n\nnamespace PHPSTORM_META {\n\n    override(\n        \\yii\\di\\Container::get(0),\n        map([\n            '' => '@',\n        ])\n    );\n\n    override(\n        \\yii\\di\\Instance::ensure(0),\n        map([\n            '' => '@',\n        ])\n    );\n\n    override(\n        \\Yii::createObject(0),\n        map([\n            '' => '@',\n        ])\n    );\n}\n"
  },
  {
    "path": "framework/BaseYii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii;\n\nuse yii\\base\\InvalidArgumentException;\nuse yii\\base\\InvalidConfigException;\nuse yii\\base\\UnknownClassException;\nuse yii\\di\\Container;\nuse yii\\log\\Logger;\nuse yii\\web\\IdentityInterface;\n\n/**\n * Gets the application start timestamp.\n */\ndefined('YII_BEGIN_TIME') or define('YII_BEGIN_TIME', microtime(true));\n/**\n * This constant defines the framework installation directory.\n */\ndefined('YII2_PATH') or define('YII2_PATH', __DIR__);\n/**\n * This constant defines whether the application should be in debug mode or not. Defaults to false.\n */\ndefined('YII_DEBUG') or define('YII_DEBUG', false);\n/**\n * This constant defines in which environment the application is running. Defaults to 'prod', meaning production environment.\n * You may define this constant in the bootstrap script. The value could be 'prod' (production), 'dev' (development), 'test', 'staging', etc.\n */\ndefined('YII_ENV') or define('YII_ENV', 'prod');\n/**\n * Whether the application is running in the production environment.\n */\ndefined('YII_ENV_PROD') or define('YII_ENV_PROD', YII_ENV === 'prod');\n/**\n * Whether the application is running in the development environment.\n */\ndefined('YII_ENV_DEV') or define('YII_ENV_DEV', YII_ENV === 'dev');\n/**\n * Whether the application is running in the testing environment.\n */\ndefined('YII_ENV_TEST') or define('YII_ENV_TEST', YII_ENV === 'test');\n\n/**\n * This constant defines whether error handling should be enabled. Defaults to true.\n */\ndefined('YII_ENABLE_ERROR_HANDLER') or define('YII_ENABLE_ERROR_HANDLER', true);\n\n/**\n * BaseYii is the core helper class for the Yii framework.\n *\n * Do not use BaseYii directly. Instead, use its child class [[\\Yii]] which you can replace to\n * customize methods of BaseYii.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template TUserIdentity of IdentityInterface\n */\nclass BaseYii\n{\n    /**\n     * @var array class map used by the Yii autoloading mechanism.\n     * The array keys are the class names (without leading backslashes), and the array values\n     * are the corresponding class file paths (or [path aliases](guide:concept-aliases)). This property mainly affects\n     * how [[autoload()]] works.\n     * @see autoload()\n     */\n    public static $classMap = [];\n    /**\n     * @var \\yii\\console\\Application|\\yii\\web\\Application<TUserIdentity>|null the application instance\n     */\n    public static $app;\n    /**\n     * @var array registered path aliases\n     * @see getAlias()\n     * @see setAlias()\n     */\n    public static $aliases = ['@yii' => __DIR__];\n    /**\n     * @var Container the dependency injection (DI) container used by [[createObject()]].\n     * You may use [[Container::set()]] to set up the needed dependencies of classes and\n     * their initial property values.\n     * @see createObject()\n     * @see Container\n     */\n    public static $container;\n\n\n    /**\n     * Returns a string representing the current version of the Yii framework.\n     * @return string the version of Yii framework\n     */\n    public static function getVersion()\n    {\n        return '2.0.55-dev';\n    }\n\n    /**\n     * Translates a path alias into an actual path.\n     *\n     * The translation is done according to the following procedure:\n     *\n     * 1. If the given alias does not start with '@', it is returned back without change;\n     * 2. Otherwise, look for the longest registered alias that matches the beginning part\n     *    of the given alias. If it exists, replace the matching part of the given alias with\n     *    the corresponding registered path.\n     * 3. Throw an exception or return false, depending on the `$throwException` parameter.\n     *\n     * For example, by default '@yii' is registered as the alias to the Yii framework directory,\n     * say '/path/to/yii'. The alias '@yii/web' would then be translated into '/path/to/yii/web'.\n     *\n     * If you have registered two aliases '@foo' and '@foo/bar'. Then translating '@foo/bar/config'\n     * would replace the part '@foo/bar' (instead of '@foo') with the corresponding registered path.\n     * This is because the longest alias takes precedence.\n     *\n     * However, if the alias to be translated is '@foo/barbar/config', then '@foo' will be replaced\n     * instead of '@foo/bar', because '/' serves as the boundary character.\n     *\n     * Note, this method does not check if the returned path exists or not.\n     *\n     * See the [guide article on aliases](guide:concept-aliases) for more information.\n     *\n     * @param string $alias the alias to be translated.\n     * @param bool $throwException whether to throw an exception if the given alias is invalid.\n     * If this is false and an invalid alias is given, false will be returned by this method.\n     * @return ($throwException is true ? string : string|false) the path corresponding to the alias, false if the root alias is not previously registered.\n     * @throws InvalidArgumentException if the alias is invalid while $throwException is true.\n     * @see setAlias()\n     */\n    public static function getAlias($alias, $throwException = true)\n    {\n        if (strncmp((string)$alias, '@', 1) !== 0) {\n            // not an alias\n            return $alias;\n        }\n\n        $pos = strpos($alias, '/');\n        $root = $pos === false ? $alias : substr($alias, 0, $pos);\n\n        if (isset(static::$aliases[$root])) {\n            if (is_string(static::$aliases[$root])) {\n                return $pos === false ? static::$aliases[$root] : static::$aliases[$root] . substr($alias, $pos);\n            }\n\n            foreach (static::$aliases[$root] as $name => $path) {\n                if (strpos($alias . '/', $name . '/') === 0) {\n                    return $path . substr($alias, strlen($name));\n                }\n            }\n        }\n\n        if ($throwException) {\n            throw new InvalidArgumentException(\"Invalid path alias: $alias\");\n        }\n\n        return false;\n    }\n\n    /**\n     * Returns the root alias part of a given alias.\n     * A root alias is an alias that has been registered via [[setAlias()]] previously.\n     * If a given alias matches multiple root aliases, the longest one will be returned.\n     * @param string $alias the alias\n     * @return string|false the root alias, or false if no root alias is found\n     */\n    public static function getRootAlias($alias)\n    {\n        $pos = strpos($alias, '/');\n        $root = $pos === false ? $alias : substr($alias, 0, $pos);\n\n        if (isset(static::$aliases[$root])) {\n            if (is_string(static::$aliases[$root])) {\n                return $root;\n            }\n\n            foreach (static::$aliases[$root] as $name => $path) {\n                if (strpos($alias . '/', $name . '/') === 0) {\n                    return $name;\n                }\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * Registers a path alias.\n     *\n     * A path alias is a short name representing a long path (a file path, a URL, etc.)\n     * For example, we use '@yii' as the alias of the path to the Yii framework directory.\n     *\n     * A path alias must start with the character '@' so that it can be easily differentiated\n     * from non-alias paths.\n     *\n     * Note that this method does not check if the given path exists or not. All it does is\n     * to associate the alias with the path.\n     *\n     * Any trailing '/' and '\\' characters in the given path will be trimmed.\n     *\n     * See the [guide article on aliases](guide:concept-aliases) for more information.\n     *\n     * @param string $alias the alias name (e.g. \"@yii\"). It must start with a '@' character.\n     * It may contain the forward-slash '/' which serves as a boundary character when performing\n     * alias translation by [[getAlias()]].\n     * @param string|null $path the path corresponding to the alias. If this is null, the alias will\n     * be removed. Trailing '/' and '\\' characters will be trimmed. This can be\n     *\n     * - a directory or a file path (e.g. `/tmp`, `/tmp/main.txt`)\n     * - a URL (e.g. `https://www.yiiframework.com`)\n     * - a path alias (e.g. `@yii/base`). In this case, the path alias will be converted into the\n     *   actual path first by calling [[getAlias()]].\n     *\n     * @throws InvalidArgumentException if $path is an invalid alias.\n     * @see getAlias()\n     */\n    public static function setAlias($alias, $path)\n    {\n        if (strncmp($alias, '@', 1)) {\n            $alias = '@' . $alias;\n        }\n        $pos = strpos($alias, '/');\n        $root = $pos === false ? $alias : substr($alias, 0, $pos);\n        if ($path !== null) {\n            $path = strncmp($path, '@', 1) ? rtrim($path, '\\\\/') : static::getAlias($path);\n            if (!isset(static::$aliases[$root])) {\n                if ($pos === false) {\n                    static::$aliases[$root] = $path;\n                } else {\n                    static::$aliases[$root] = [$alias => $path];\n                }\n            } elseif (is_string(static::$aliases[$root])) {\n                if ($pos === false) {\n                    static::$aliases[$root] = $path;\n                } else {\n                    static::$aliases[$root] = [\n                        $alias => $path,\n                        $root => static::$aliases[$root],\n                    ];\n                }\n            } else {\n                static::$aliases[$root][$alias] = $path;\n                krsort(static::$aliases[$root]);\n            }\n        } elseif (isset(static::$aliases[$root])) {\n            if (is_array(static::$aliases[$root])) {\n                unset(static::$aliases[$root][$alias]);\n            } elseif ($pos === false) {\n                unset(static::$aliases[$root]);\n            }\n        }\n    }\n\n    /**\n     * Class autoload loader.\n     *\n     * This method is invoked automatically when PHP sees an unknown class.\n     * The method will attempt to include the class file according to the following procedure:\n     *\n     * 1. Search in [[classMap]];\n     * 2. If the class is namespaced (e.g. `yii\\base\\Component`), it will attempt\n     *    to include the file associated with the corresponding path alias\n     *    (e.g. `@yii/base/Component.php`);\n     *\n     * This autoloader allows loading classes that follow the [PSR-4 standard](https://www.php-fig.org/psr/psr-4/)\n     * and have its top-level namespace or sub-namespaces defined as path aliases.\n     *\n     * Example: When aliases `@yii` and `@yii/bootstrap` are defined, classes in the `yii\\bootstrap` namespace\n     * will be loaded using the `@yii/bootstrap` alias which points to the directory where the bootstrap extension\n     * files are installed and all classes from other `yii` namespaces will be loaded from the yii framework directory.\n     *\n     * Also the [guide section on autoloading](guide:concept-autoloading).\n     *\n     * @param string $className the fully qualified class name without a leading backslash \"\\\"\n     * @throws UnknownClassException if the class does not exist in the class file\n     */\n    public static function autoload($className)\n    {\n        if (isset(static::$classMap[$className])) {\n            $classFile = static::$classMap[$className];\n            if (strncmp($classFile, '@', 1) === 0) {\n                $classFile = static::getAlias($classFile);\n            }\n        } elseif (strpos($className, '\\\\') !== false) {\n            $classFile = static::getAlias('@' . str_replace('\\\\', '/', $className) . '.php', false);\n            if ($classFile === false || !is_file($classFile)) {\n                return;\n            }\n        } else {\n            return;\n        }\n\n        include $classFile;\n\n        if (YII_DEBUG && !class_exists($className, false) && !interface_exists($className, false) && !trait_exists($className, false)) {\n            throw new UnknownClassException(\"Unable to find '$className' in file: $classFile. Namespace missing?\");\n        }\n    }\n\n    /**\n     * Creates a new object using the given configuration.\n     *\n     * You may view this method as an enhanced version of the `new` operator.\n     * The method supports creating an object based on a class name, a configuration array or\n     * an anonymous function.\n     *\n     * Below are some usage examples:\n     *\n     * ```\n     * // create an object using a class name\n     * $object = Yii::createObject('yii\\db\\Connection');\n     *\n     * // create an object using a configuration array\n     * $object = Yii::createObject([\n     *     'class' => 'yii\\db\\Connection',\n     *     'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n     *     'username' => 'root',\n     *     'password' => '',\n     *     'charset' => 'utf8',\n     * ]);\n     *\n     * // create an object with two constructor parameters\n     * $object = \\Yii::createObject('MyClass', [$param1, $param2]);\n     * ```\n     *\n     * Using [[\\yii\\di\\Container|dependency injection container]], this method can also identify\n     * dependent objects, instantiate them and inject them into the newly created object.\n     *\n     * @template T\n     *\n     * @param class-string<T>|array{class?: class-string<T>, __class?: class-string<T>, ...}|callable(): T $type the object type. This can be specified in one of the following forms:\n     *\n     * - a string: representing the class name of the object to be created\n     * - a configuration array: the array must contain a `class` element which is treated as the object class,\n     *   and the rest of the name-value pairs will be used to initialize the corresponding object properties\n     * - a PHP callable: either an anonymous function or an array representing a class method (`[$class or $object, $method]`).\n     *   The callable should return a new instance of the object being created.\n     *\n     * @param array $params the constructor parameters\n     * @return T the created object\n     * @throws InvalidConfigException if the configuration is invalid.\n     * @see \\yii\\di\\Container\n     */\n    public static function createObject($type, array $params = [])\n    {\n        if (is_string($type)) {\n            return static::$container->get($type, $params);\n        }\n\n        if (is_callable($type, true)) {\n            return static::$container->invoke($type, $params);\n        }\n\n        if (!is_array($type)) {\n            throw new InvalidConfigException('Unsupported configuration type: ' . gettype($type));\n        }\n\n        if (isset($type['__class'])) {\n            $class = $type['__class'];\n            unset($type['__class'], $type['class']);\n            return static::$container->get($class, $params, $type);\n        }\n\n        if (isset($type['class'])) {\n            $class = $type['class'];\n            unset($type['class']);\n            return static::$container->get($class, $params, $type);\n        }\n\n        throw new InvalidConfigException('Object configuration must be an array containing a \"class\" or \"__class\" element.');\n    }\n\n    private static $_logger;\n\n    /**\n     * @return Logger message logger\n     */\n    public static function getLogger()\n    {\n        if (self::$_logger !== null) {\n            return self::$_logger;\n        }\n\n        return self::$_logger = static::createObject('yii\\log\\Logger');\n    }\n\n    /**\n     * Sets the logger object.\n     * @param Logger|null $logger the logger object.\n     */\n    public static function setLogger($logger)\n    {\n        self::$_logger = $logger;\n    }\n\n    /**\n     * Logs a debug message.\n     * Trace messages are logged mainly for development purposes to see\n     * the execution workflow of some code. This method will only log\n     * a message when the application is in debug mode.\n     * @param string|array $message the message to be logged. This can be a simple string or a more\n     * complex data structure, such as an array.\n     * @param string $category the category of the message.\n     * @since 2.0.14\n     */\n    public static function debug($message, $category = 'application')\n    {\n        if (YII_DEBUG) {\n            static::getLogger()->log($message, Logger::LEVEL_TRACE, $category);\n        }\n    }\n\n    /**\n     * Alias of [[debug()]].\n     * @param string|array $message the message to be logged. This can be a simple string or a more\n     * complex data structure, such as an array.\n     * @param string $category the category of the message.\n     * @deprecated since 2.0.14. Use [[debug()]] instead.\n     */\n    public static function trace($message, $category = 'application')\n    {\n        static::debug($message, $category);\n    }\n\n    /**\n     * Logs an error message.\n     * An error message is typically logged when an unrecoverable error occurs\n     * during the execution of an application.\n     * @param string|array|\\Throwable $message the message to be logged. This can be a simple string or a more\n     * complex data structure, such as an array.\n     * @param string $category the category of the message.\n     */\n    public static function error($message, $category = 'application')\n    {\n        static::getLogger()->log($message, Logger::LEVEL_ERROR, $category);\n    }\n\n    /**\n     * Logs a warning message.\n     * A warning message is typically logged when an error occurs while the execution\n     * can still continue.\n     * @param string|array|\\Throwable $message the message to be logged. This can be a simple string or a more\n     * complex data structure, such as an array.\n     * @param string $category the category of the message.\n     */\n    public static function warning($message, $category = 'application')\n    {\n        static::getLogger()->log($message, Logger::LEVEL_WARNING, $category);\n    }\n\n    /**\n     * Logs an informative message.\n     * An informative message is typically logged by an application to keep record of\n     * something important (e.g. an administrator logs in).\n     * @param string|array|\\Throwable $message the message to be logged. This can be a simple string or a more\n     * complex data structure, such as an array.\n     * @param string $category the category of the message.\n     */\n    public static function info($message, $category = 'application')\n    {\n        static::getLogger()->log($message, Logger::LEVEL_INFO, $category);\n    }\n\n    /**\n     * Marks the beginning of a code block for profiling.\n     *\n     * This has to be matched with a call to [[endProfile]] with the same category name.\n     * The begin- and end- calls must also be properly nested. For example,\n     *\n     * ```\n     * \\Yii::beginProfile('block1');\n     * // some code to be profiled\n     *     \\Yii::beginProfile('block2');\n     *     // some other code to be profiled\n     *     \\Yii::endProfile('block2');\n     * \\Yii::endProfile('block1');\n     * ```\n     * @param string $token token for the code block\n     * @param string $category the category of this log message\n     * @see endProfile()\n     */\n    public static function beginProfile($token, $category = 'application')\n    {\n        static::getLogger()->log($token, Logger::LEVEL_PROFILE_BEGIN, $category);\n    }\n\n    /**\n     * Marks the end of a code block for profiling.\n     * This has to be matched with a previous call to [[beginProfile]] with the same category name.\n     * @param string $token token for the code block\n     * @param string $category the category of this log message\n     * @see beginProfile()\n     */\n    public static function endProfile($token, $category = 'application')\n    {\n        static::getLogger()->log($token, Logger::LEVEL_PROFILE_END, $category);\n    }\n\n    /**\n     * Returns an HTML hyperlink that can be displayed on your Web page showing \"Powered by Yii Framework\" information.\n     * @return string an HTML hyperlink that can be displayed on your Web page showing \"Powered by Yii Framework\" information\n     * @deprecated since 2.0.14, this method will be removed in 2.1.0.\n     */\n    public static function powered()\n    {\n        return \\Yii::t('yii', 'Powered by {yii}', [\n            'yii' => '<a href=\"https://www.yiiframework.com/\" rel=\"external\">' . \\Yii::t('yii', 'Yii Framework') . '</a>',\n        ]);\n    }\n\n    /**\n     * Translates a message to the specified language.\n     *\n     * This is a shortcut method of [[\\yii\\i18n\\I18N::translate()]].\n     *\n     * The translation will be conducted according to the message category and the target language will be used.\n     *\n     * You can add parameters to a translation message that will be substituted with the corresponding value after\n     * translation. The format for this is to use curly brackets around the parameter name as you can see in the following example:\n     *\n     * ```\n     * $username = 'Alexander';\n     * echo \\Yii::t('app', 'Hello, {username}!', ['username' => $username]);\n     * ```\n     *\n     * Further formatting of message parameters is supported using the [PHP intl extensions](https://www.php.net/manual/en/intro.intl.php)\n     * message formatter. See [[\\yii\\i18n\\I18N::translate()]] for more details.\n     *\n     * @param string $category the message category.\n     * @param string $message the message to be translated.\n     * @param array $params the parameters that will be used to replace the corresponding placeholders in the message.\n     * @param string|null $language the language code (e.g. `en-US`, `en`). If this is null, the current\n     * [[\\yii\\base\\Application::language|application language]] will be used.\n     * @return string the translated message.\n     */\n    public static function t($category, $message, $params = [], $language = null)\n    {\n        if (static::$app !== null) {\n            return static::$app->getI18n()->translate($category, $message, $params, $language ?: static::$app->language);\n        }\n\n        $placeholders = [];\n        foreach ((array) $params as $name => $value) {\n            $placeholders['{' . $name . '}'] = $value;\n        }\n\n        return ($placeholders === []) ? $message : strtr($message, $placeholders);\n    }\n\n    /**\n     * Configures an object with the initial property values.\n     * @param object $object the object to be configured\n     * @param array $properties the property initial values given in terms of name-value pairs.\n     * @return object the object itself\n     */\n    public static function configure($object, $properties)\n    {\n        foreach ($properties as $name => $value) {\n            $object->$name = $value;\n        }\n\n        return $object;\n    }\n\n    /**\n     * Returns the public member variables of an object.\n     * This method is provided such that we can get the public member variables of an object.\n     * It is different from \"get_object_vars()\" because the latter will return private\n     * and protected variables if it is called within the object itself.\n     * @param object $object the object to be handled\n     * @return array the public member variables of the object\n     */\n    public static function getObjectVars($object)\n    {\n        return get_object_vars($object);\n    }\n}\n"
  },
  {
    "path": "framework/CHANGELOG.md",
    "content": "Yii Framework 2 Change Log\n==========================\n\n2.0.55 under development\n------------------------\n\n- Bug #20705: Replace `$this` with `self` in generics in Psalm annotations (mspirkov)\n- Bug #20715: Adjust `JSON` helper error message assertions for `PHP 8.6` compatibility in `JsonTest` class (terabytesoftw)\n- Enh #20714: Allow overriding the `yii\\grid\\GridView`'s default `filterSelector`, allow using `Closure`s for `filterSelector` (chriscpty)\n- Enh #20717: Use PHPStan/Psalm types in PHPDoc annotations (mspirkov)\n- Enh #20718: When set_time_limit() is not available, throw a warning only for big files (@marc-farre)\n- Enh #20729: Add default types in `@template` annotations (mspirkov)\n- Enh #20730: Make the configuration type for `Application` wider (mspirkov)\n- Bug #20733: Replace `$this` with `static` and `covariant static` in generics in PHPStan and PHPDoc annotations (mspirkov)\n- Bug #20735: Fix `@param` annotations for `$name` in `Request::get()` and `Request::post()` (mspirkov)\n- Enh #20735: Add conditional types in `@return` annotations in `Request::get()` and `Request::post()` (mspirkov)\n- Bug #20738: Fix `@var` annotations for `Application::$requestedParams`, `AssetBundle::$basePath`, `AssetBundle::$baseUrl`, `MultiFieldSession::$writeCallback` (mspirkov)\n- Bug #20738: Fix `@return` annotations for `User::getAuthManager()` and `User::getAccessChecker()` (mspirkov)\n- Bug #20738: Fix `@param` annotation for `$value` parameter in `AssetManager::setConverter()` (mspirkov)\n- Bug #20739: Fix `@var` annotation for `BaseYii::$app` (mspirkov)\n- Enh #20743: Remove dead code for PHP < 5.6 in `UrlValidator` and `EmailValidator` (WarLikeLaux)\n- Bug #20746: Fix `@var` annotations for `DbDependency::$db` and `DbDependency::$sql` (mspirkov)\n- Bug #20750: Fix `@return` annotation for `yii\\console\\Controller::runAction()` (mspirkov)\n- Bug #20750: Add the missing `@property-write` annotation to `yii\\console\\Controller` (mspirkov)\n- Bug #20751: Fix `@param` annotation for `$param` parameter in `Sort::parseSortParam()` (mspirkov)\n- Enh #20756: Remove dead code for PHP < 5.6 in `SchemaBuilderTrait::json()` (WarLikeLaux)\n- Bug #20764: Fix `@return` annotation for `Model::rules()` (mspirkov)\n- Bug #20697: `loadTableIndexes()` includes LOB indexes with `NULL` column names, causing `strpos()` deprecation on PHP `8.1+` (terabytesoftw)\n- Chg #20757: Remove dead code for PHP < 7.4 in `Security` (WarLikeLaux)\n- Bug #17254: Fix `MessageController` crash on dynamic input in `Yii::t()` call (CeBe)\n\n\n2.0.54 January 09, 2026\n-----------------------\n\n- Bug #19506: Fix `@property` annotations in `yii\\console\\widgets\\Table`, `yii\\di\\Container` and `yii\\web\\Session` (mspirkov)\n- Bug #19655: Fix `LinkPager::getPageRange` when `maxButtons` is 2 (mspirkov)\n- Bug #20432: Fix PHPStan/Psalm annotations for `ActiveQuery::asArray` (mspirkov)\n- Bug #20437: Fix PHPStan/Psalm annotations for `BaseArrayHelper::merge` (mspirkov)\n- Bug #20447: Fix behavior for `yii\\web\\Controller::bindActionParams` around `mixed` type (chriscpty)\n- Bug #20453: Fix PHPStan/Psalm types in `yii\\web\\View` (mspirkov)\n- Bug #20459: Fix return type in `RequestParserInterface::parse` (mspirkov)\n- Bug #20475: Fix `Formatter` class `asScientific()` method for PHP `8.5` `sprintf` precision change (`6` to `0`) (terabytesoftw)\n- Bug #20479: Fix issue with MSSQL related to char and nchar (craiglondon)\n- Bug #20482: Fix deprecation of `ReflectionMethod::setAccessible()` in PHP `8.5` (terabytesoftw)\n- Bug #20483: Fix `CompositeAuth` making bad assumptions on `AuthInterface` implementations (sammousa)\n- Bug #20485: Fix error `Cannot unset string offsets` in `yii\\di\\Instance:ensure(['__class' => ...], 'some\\class\\name')` (mspirkov)\n- Bug #20489: Replace deprecated `strftime` with `date` in `YiiRequirementChecker` (mspirkov)\n- Bug #20492: Fix deprecation of `finfo_close()` in PHP `8.5` by conditionally closing the resource (terabytesoftw)\n- Bug #20494: Fix `PHPdoc`, add `PHPStan/Psalm` annotations for `authMethods` property in `CompositeAuth` class (terabytesoftw)\n- Bug #20495: Fix behavior when resetting sequence in `QueryBuilder` for `MSSQL` (achretien)\n- Bug #20508: Fix PHPDoc, add PHPStan/Psalm annotations for `yii\\web\\CookieCollection::getIterator`. Add missing `@property` annotation in `yii\\base\\Model` (mspirkov)\n- Bug #20513: Fix code examples in PHPDoc (mspirkov)\n- Bug #20515: Fix `@param` annotations in `BetweenColumnsCondition`, `InCondition` and `LikeCondition` (mspirkov)\n- Bug #20516: Fix `@template` annotations in `ActiveRecord` (mspirkov)\n- Bug #20524: Fix PHPStan/Psalm annotations in `Yii::createObject` (mspirkov)\n- Bug #20530: Fix notice \"Object of class DateTimeImmutable could not be converted to int\" in `CookieCollection::has` (mspirkov)\n- Bug #20532: Add missing `return` statements in `CacheController::actionFlushSchema`, `FixtureController::actionUnload`, `HelpController::actionIndex` and `ServeController::actionIndex` (mspirkov)\n- Bug #20541: Remove deprecated caching components: `XCache` and `ZendDataCache`, and update related tests and documentation (terabytesoftw)\n- Bug #20542: Fix `Formatter` working with more complex ICU unit data structure (OndrejVasicek)\n- Bug #20548: Fix PHP `8.5` `null` array offset deprecation warnings (terabytesoftw)\n- Bug #20569: Fix `@param` annotation for `$default` in `HeaderCollection::get` (mspirkov)\n- Bug #20570: Fix `@var` annotation for `UrlManager::$cache` (mspirkov)\n- Bug #20571: Fix `@var` annotation for `yii\\web\\Response::$stream` (mspirkov)\n- Bug #20576: Fix `@var` annotation for `StringValidator::$length` (mspirkov)\n- Bug #20583: Fix return value in `Request::getServerPort` (mspirkov)\n- Bug #20585: Fix `@return` annotation for `CompositeUrlRule::createRules()` (mspirkov)\n- Bug #20587: Fix `@var` annotation for `yii\\rbac\\Item::$ruleName` (mspirkov)\n- Bug #20589: Fix `@var` annotations for `yii\\rbac\\DbManager` properties (mspirkov)\n- Bug #20594: Fix `@return` annotation for `Instance::get()` (mspirkov)\n- Bug #20595: Fix `@return` annotation for `BaseHtml::getAttributeValue()` (mspirkov)\n- Bug #20599: Fix `@return` annotation for `ConditionInterface::fromArrayDefinition()` (mspirkov)\n- Bug #20600: Fix `@var` annotation for `yii\\test\\FileFixtureTrait::$dataFile` (mspirkov)\n- Bug #20604: Fix `@var` annotation for `yii\\db\\Command::$pdoStatement` (mspirkov)\n- Bug #20605: Fix return value in `SerialColumn::renderDataCellContent()` (mspirkov)\n- Bug #20608: Fix `@return` annotations for `yii\\rest\\Serializer` methods (mspirkov)\n- Bug #20610: Fix `@var` annotation for `ActiveQueryTrait::$with` (mspirkov)\n- Bug #20611: Fix `@return` annotations for `yii\\i18n\\GettextMoFile` methods (mspirkov)\n- Bug #20617: Fix `@return` annotation for `DataColumn::getDataCellValue()` (mspirkov)\n- Bug #20618: Fix `@var` annotation for `yii\\web\\Response::$acceptMimeType` (mspirkov)\n- Bug #20619: Fix `@return` annotation for `yii\\db\\Query::prepare()` (mspirkov)\n- Bug #20620: Fix `@var` annotation for `RateLimiter::$user` (mspirkov)\n- Bug #20628: Fix `@return` annotations for `lastInsertId` methods in `yii\\db\\mssql` namespace (mspirkov)\n- Bug #20630: Fix `@var` annotations for `yii\\web\\CompositeUrlRule::$rules` and `yii\\web\\GroupUrlRule::$rules` (mspirkov)\n- Bug #20634: Fix PHPDoc annotations in `yii\\db` namespace. Add PHPStan/Psalm annotations for `yii\\db\\SqlTokenizer::startsWithAnyLongest()` (mspirkov)\n- Bug #20636: Fix `@param` annotations for `$message` in logging methods (mspirkov)\n- Bug #20637: Fix PHPDoc annotations in `ActiveRelationTrait`. Add PHPStan/Psalm annotations for `$modelClass` in `ActiveQuery`, `ActiveQueryTrait` and `ActiveRelationTrait` (mspirkov)\n- Bug #20639: Add missing generics in `yii\\web` namespace (mspirkov)\n- Bug #20640: Fix `@param` annotation for `$block` in `yii\\console\\Markdown::renderParagraph()` (mspirkov)\n- Bug #20645: Add missing generics in `yii\\helpers` and `yii\\test` namespaces. Fix PHPDoc annotations in `ArrayAccessTrait` (mspirkov)\n- Bug #20651: Add missing generics in `yii\\filters` namespace (mspirkov)\n- Bug #20654: Add missing generics in `yii\\db` namespace. Fix PHPDoc annotations in `yii\\db\\ArrayExpression` (mspirkov)\n- Bug #20658: Add missing generics in `yii\\console`, `yii\\captcha`, `yii\\caching` and `yii\\behaviors` namespaces (mspirkov)\n- Bug #20659: Fix PHP `8.5` `null` array offset deprecation warnings in `MariaDB` driver (terabytesoftw)\n- Bug #20665: Fix PHP `8.5` `null` array offset deprecation warnings in `yii\\build\\controllers\\ReleaseController` class (terabytesoftw)\n- Bug #20666: Add missing generics in `yii\\base`, `yii\\console`, `yii\\filters` and `yii\\web` namespaces (mspirkov)\n- Bug #20671: Fix PHPDoc annotations in `yii\\base`, `yii\\console`, `yii\\web` and `yii\\widgets` namespaces (mspirkov)\n- Bug #20673: Sanitize `null` bytes before `quoteValue()` on PHP 8.5+ in SQLite (terabytesoftw)\n- Bug #20675: Add generics for all controllers (mspirkov)\n- Bug #20689: Fix PHP `8.5` `imagedestroy` deprecation warning (ElisDN)\n- Enh #20047: Throw exception when fixture not found rather than ignoring (borgou)\n- Enh #20433: Add PHPStan/Psalm annotations for some controllers methods: `beforeAction`, `afterAction` and `bindActionParams` (mspirkov)\n- Enh #20434: Add PHPStan/Psalm annotations for `hasMany` and `hasOne` methods (mspirkov)\n- Enh #20442: Add PHPStan/Psalm annotations for `yii\\base\\Controller` methods: `runAction`, `run`, `render`, `renderPartial` and `renderFile` (mspirkov)\n- Enh #20461: Add PHPStan/Psalm annotations for `yii\\filters\\auth\\AuthInterface` (mspirkov)\n- Enh #20480: Add PHPStan/Psalm annotations for `ServiceLocator::get` (mspirkov)\n- Enh #20505: `ArrayDataProvider` key handling with flexible path support (fetus-hina)\n- Enh #20514: Add `@property` annotations for `yii\\console\\Controller` (mspirkov)\n- Enh #20525, #20529, #20629: Add `@template` annotations for all actions (mspirkov)\n- Enh #20539: Update minimum PHP version requirement from `7.3` to `7.4` (terabytesoftw)\n- Enh #20579: Add PHPStan/Psalm annotations for `HeaderCollection::get` (mspirkov)\n- Enh #20591: Add PHPStan/Psalm annotations for `yii\\rbac\\BaseManager::getItems()` (mspirkov)\n- Enh #20607: Add PHPStan/Psalm annotations for DB schemas (mspirkov)\n- Enh #20642: Pass `$model` and `$attribute` in Compare Validator's compareValue property closure (samuelrajan747)\n- Enh #20650: Add PHPStan/Psalm annotations for `yii\\di\\Container` (mspirkov)\n- New #19526: Add the `convertIniSizeToBytes` method to `BaseStringHelper` (mspirkov)\n\n\n2.0.53 June 27, 2025\n--------------------\n\n- Bug #8298: Loading fixtures does not update table sequence for PostgreSQL database (mtangoo)\n- Bug #20329: pgsql: ColumnSchema doesn't recognize PG type cast (arkhamvm)\n- Bug #20347: Fix compatibility with PHP 8.4: remove usage of `session.use_trans_sid` and `session.use_only_cookies` (tehmaestro)\n- Bug #20348: `ErrorHandler::convertExceptionToError()` Passing `E_USER_ERROR` to `trigger_error()` is deprecated since PHP `8.4` (terabytesoftw)\n- Bug #20351: Fix behavior for `yii\\web\\Controller::bindActionParams` around union types (chriscpty)\n- Bug #20355: Fix SQL syntax for resetting sequence in `QueryBuilder` for `MSSQL` (terabytesoftw)\n- Bug #20371: Fix \"Typed property must not be accessed before initialization\" when calling `toArray()` on a model with typed properties without default value (uaoleg)\n- Bug #20373: Fixed the type of the first parameter `yii\\base\\Controller::bindInjectedParams()` (mspirkov)\n- Bug #20387: Fixed the generated command to use the router.php file in the PHP built-in server (eseperio)\n- Bug #20423: `strcmp()` Passing `null` to parameter `2` ($string2) of type string is deprecated (terabytesoftw)\n- Enh #20309: Add custom attributes support to style tags (nzwz)\n- Enh #20354: Add PHPStan/Psalm annotations for `Container` and `Instance` (mspirkov)\n- Enh #20361: Add PHPStan/Psalm annotations for `ActiveQuery` (mspirkov)\n- Enh #20363: Add PHPStan/Psalm annotations for `ActiveRecord` and `ActiveQuery` (mspirkov)\n- Enh #20372: Add PHPStan/Psalm annotations for `AssetBundle`, `AssetManager` and `View` (mspirkov)\n- Enh #20374: Add PHPStan/Psalm annotations for `BaseYii`, `BaseObject`, `Component`, `Model`, `Module` and `yii\\base\\Controller` (mspirkov)\n- Enh #20385: Fixed the memory leak issue when using `Query::exists()` with MySQL (snewer)\n- Enh #20394: Add PHPStan/Psalm annotations for `Yii::getAlias` (mspirkov)\n- Enh #20395: Add PHPStan/Psalm annotations for `ActiveForm::validate` (samuelrajan747)\n- Enh #20397: Add PHPStan/Psalm annotations for `ArrayHelper::merge` (samuelrajan747)\n- Enh #20400: Add PHPStan/Psalm annotations for `yii\\web\\User` (samuelrajan747)\n- Enh #20413: Add PHPStan/Psalm annotations for `Action`, `ActionEvent`, `Application`, `DynamicModel` and `InlineAction` (mspirkov)\n- Enh #20416: Add `PHPStan`/`Psalm` annotation for `owner` property in `Behavior` class (terabytesoftw)\n- Enh #20426: Add `PHPStan`/`Psalm` annotation for `controller` property in `Action` class (terabytesoftw)\n- Enh #20430: Update PHPStan/Psalm annotations for `ArrayHelper::merge` to handle array-key (samuelrajan747)\n\n\n2.0.52 February 13, 2025\n------------------------\n\n- Bug #17365: Fix \"Trying to access array offset on null\" warning (xcopy)\n- Bug #20140: Fix compatibility with PHP 8.4: calling `session_set_save_handler()` (Izumi-kun)\n- Bug #20231: Fix regression introduced in #20167 in `yii\\validators\\FileValidator` (bizley)\n- Bug #20232: Fix regression introduced in `GHSA-cjcc-p67m-7qxm` while attaching behavior defined by `__class` array key (erickskrauch)\n- Bug #20256: Add support for dropping views in MSSQL server when running migrate/fresh (ambrozt)\n- Bug #20282: Fix compatibility with PHP 8.4: deprecated constant `E_STRICT` (Izumi-kun)\n- Bug #20284: Revert punycode to 1.4.x which supports pre ES6 format (mtangoo)\n- Bug #20292: Fix `\\yii\\web\\Session` should not set cookie params, when `session.use_cookies` is `false` (cebe)\n- Bug #20296: Fix broken enum test (briedis)\n- Bug #20300: Clear stat cache in `FileCache::setValue()` (rob006)\n- Bug #20308: Allow CompositeAuth auth methods to use their own user if defined (mtangoo)\n- Bug #20313: Allow CompositeAuth auth methods to use their own request and response if defined (mtangoo)\n- Enh #20247: Support for variadic console controller action methods (brandonkelly)\n- Enh #20248: Add support for attaching behaviors in configurations with Closure (timkelty)\n- Enh #20267: Fixed called class check in `Widget::end()` when widget configured using callable (rob006, jrajamaki)\n- Enh #20268: Minor optimisation in `\\yii\\helpers\\BaseArrayHelper::map` (chriscpty)\n- Enh #20273: Remove unnecessary `paragonie/random_compat` dependency (timwolla)\n- Enh #20279: Add to the `\\yii\\web\\Request` `csrfHeader` property to configure a custom HTTP header for CSRF validation (olegbaturin)\n- Enh #20279: Add to the `\\yii\\web\\Request` `csrfTokenSafeMethods` property to configure a custom safe HTTP methods list (olegbaturin)\n- Enh #20295: Add an ability to have wildcards in `yii\\log\\Target::$maskVars` array (xcopy)\n- Enh #20306: Add new `yii\\helpers\\ArrayHelper::flatten()` method (xcopy)\n- Chg #20276: Removed autogenerated migration PHPDoc (userator)\n- New #20185: Add `BackedEnum` support to `AttributeTypecastBehavior` (briedis)\n- New #20279: Add to the `\\yii\\web\\Request` CSRF validation by custom HTTP header (olegbaturin)\n- New #20332: Added support for the '__class' key in `\\yii\\di\\Instance::ensure(['__class' => ...])` (LAV45)\n\n\n2.0.51 July 18, 2024\n--------------------\n\n- Bug #16116: Codeception: oci does not support enabling/disabling integrity check (@terabytesoftw)\n- Bug #20147: Fix error handler compatibility with PHP 8.3 (samdark)\n- Bug #20191: Fix `ActiveRecord::getDirtyAttributes()` for JSON columns with multi-dimensional array values (brandonkelly)\n- Bug #20195: Do not set non-abstract values into `ColumnSchema->type` on MSSQL version less than 2017 (axeltomasson)\n- Bug #20211: Add acceptable parameters to `MaskedInput::init()` method (alxlnk)\n- Bug #20226: Revert all PR for \"Data providers perform unnecessary COUNT queries that negatively affect performance\" (@terabytesoftw)\n- Bug #20230: Fix getting ID in `\\yii\\filters\\Cors::actions()` when attached to a module (timkelty)\n\n\n2.0.50 May 30, 2024\n-------------------\n\n- Bug #13920: Fixed erroneous validation for specific cases (tim-fischer-maschinensucher)\n- Bug #17181: Improved `BaseUrl::isRelative($url)` performance (sammousa, bizley, rob006)\n- Bug #17191: Fixed `BaseUrl::isRelative($url)` method in `yii\\helpers\\BaseUrl` (ggh2e3)\n- Bug #18469: Fixed `Link::serialize(array $links)` method in `yii\\web\\Link` (ggh2e3)\n- Bug #19060: Fix `yii\\widgets\\Menu` bug when using Closure for active item and adding additional tests in `tests\\framework\\widgets\\MenuTest` (atrandafir)\n- Bug #19691: Allow using custom class to style error summary (skepticspriggan)\n- Bug #19817: Add MySQL Query `addCheck()` and `dropCheck()` (@bobonov)\n- Bug #19855: Fixed `yii\\validators\\FileValidator` to not limit some of its rules only to array attribute (bizley)\n- Bug #19927: Fixed `console\\controllers\\MessageController` when saving translations to database: fixed FK error when adding new string and language at the same time, checking/regenerating all missing messages and dropping messages for unused languages (atrandafir)\n- Bug #20002: Fixed superfluous query on HEAD request in serializer (xicond)\n- Bug #20005: Fix `yii\\console\\controllers\\ServeController` to specify the router script (terabytesoftw)\n- Bug #20040: Fix type `boolean` in `MSSQL` (terabytesoftw)\n- Bug #20055: Fix Response header X-Pagination-Total-Count is always 0 (lav45, xicond)\n- Bug #20083: Fix deprecated warning implicit conversion from float (skepticspriggan)\n- Bug #20122: Fixed parsing of boolean keywords (e.g. used in SQLite) in `\\yii\\db\\ColumnSchema::typecast()` (rhertogh)\n- Bug #20141: Update `ezyang/htmlpurifier` dependency to version `4.17` (@terabytesoftw)\n- Bug #20165: Adjust pretty name of closures for PHP 8.4 compatibility (@staabm)\n- Bug: CVE-2024-32877, Fix Reflected XSS in Debug mode (Antiphishing)\n- Bug: CVE-2024-4990, Fix Unsafe Reflection in base Component class (@mtangoo)\n- Enh #12743: Added new methods `BaseActiveRecord::loadRelations()` and `BaseActiveRecord::loadRelationsFor()` to eager load related models for existing primary model instances (PowerGamer1)\n- Enh #20030: Improve performance of handling `ErrorHandler::$memoryReserveSize` (antonshevelev, rob006)\n- Enh #20032: Added `yii\\helpers\\BaseStringHelper::mask()` method for string masking with multibyte support (salehhashemi1992)\n- Enh #20034: Added `yii\\helpers\\BaseStringHelper::findBetween()` to retrieve a substring that lies between two strings (salehhashemi1992)\n- Enh #20042: Add empty array check to `ActiveQueryTrait::findWith()` (renkas)\n- Enh #20087: Add custom attributes to script tags (skepticspriggan)\n- Enh #20121: Added `yiisoft/yii2-coding-standards` to composer `require-dev` and lint code to comply with PSR12 (razvanphp)\n- Enh #20134: Raise minimum PHP version to `7.3` (@terabytesoftw)\n- Enh #20171: Support JSON columns for MariaDB 10.4 or higher (@terabytesoftw)\n- New #20137: Added `yii\\caching\\CallbackDependency` to allow using a callback to determine if a cache dependency is still valid (laxity7)\n\n2.0.49.2 October 12, 2023\n-------------------------\n\n- Bug #19925: Improved PHP version check when handling MIME types (schmunk42)\n\n\n2.0.49.1 October 05, 2023\n-------------------------\n\n- Bug #19940: File Log writer without newline (terabytesoftw)\n- Bug #19950: Fix `Query::groupBy(null)` causes error for PHP 8.1: `trim(): Passing null to parameter #1 ($string) of type string is deprecated` (uaoleg)\n- Bug #19951: Removed unneeded MIME file tests (schmunk42)\n- Bug #19984: Do not duplicate log messages in memory (lubosdz)\n- Enh #19780: added pcntl to requirements check (schmunk42)\n\n\n2.0.49 August 29, 2023\n----------------------\n\n- Bug #9899: Fix caching a MSSQL query with BLOB data type (terabytesoftw)\n- Bug #16208: Fix `yii\\log\\FileTarget` to not export empty messages (terabytesoftw)\n- Bug #18859: Fix `yii\\web\\Controller::bindInjectedParams()` to not throw error when argument of `ReflectionUnionType` type is passed (bizley)\n- Bug #19857: Fix AttributeTypecastBehavior::resetOldAttributes() causes \"class has no attribute named\" InvalidArgumentException (uaoleg)\n- Bug #19868: Added whitespace sanitation for tests, due to updates in ICU 72 (schmunk42)\n- Bug #19872: Fixed the definition of dirty attributes in AR properties for a non-associative array in case of changing the order of elements (eegusakov)\n- Bug #19899: Fixed `GridView` in some cases calling `Model::generateAttributeLabel()` to generate label values that are never used (PowerGamer1)\n- Bug #19906: Fixed multiline strings in the `\\yii\\console\\widgets\\Table` widget (rhertogh)\n- Bug #19908: Fix associative array cell content rendering in Table widget (rhertogh)\n- Bug #19911: Resolved inconsistency in `ActiveRecord::getAttributeLabel()` with regard of overriding in primary model labels for attributes of related model in favor of allowing such overriding for all levels of relation nesting (PowerGamer1)\n- Bug #19914: Fixed `ArrayHelper::keyExists()` and  `::remove()` functions when the key is a float and the value is `null` (rhertogh)\n- Bug #19924: Fix `yii\\i18n\\Formatter` to not throw error `Unknown named parameter` under PHP 8 (arollmann)\n- Enh #19841: Allow jQuery 3.7 to be installed (wouter90)\n- Enh #19853: Added support for default value for `\\yii\\helpers\\Console::select()` (rhertogh)\n- Enh #19884: Added support Enums in Query Builder (sk1t0n)\n- Enh #19920: Broadened the accepted type of `Cookie::$expire` from `int` to `int|string|\\DateTimeInterface|null` (rhertogh)\n\n\n2.0.48.1 May 24, 2023\n---------------------\n\n- Bug #19847: Fix regression introduced in #15376 that caused `DbManager::getRolesByUser()` to return stale data (michaelarnauts)\n\n\n2.0.48 May 22, 2023\n-------------------\n\n- Bug #15376: Added $userId for RBAC roles cache (manchenkoff)\n- Bug #17194: Fix unnecessary SQL updates in the database on attributes typecast via `yii\\behaviors\\AttributeTypecastBehavior` (aivchen)\n- Bug #18867: Fixed multiple issues with `yii\\grid\\CheckboxColumn`: \"check all\" checkbox not being checked on page load when all data row checkboxes are initially checked; clicking checkboxes triggered \"change\" event for other checkboxes that do not change their state; \"check all\" checkbox not being checked when disabled checkboxes are present and clicking last non-checked data row checkbox (PowerGamer1)\n- Bug #19635: PHP 8.2 compatibility fix for `yii\\validators\\DateValidator` (PowerGamer1)\n- Bug #19683: Updated `framework\\mimeType.php` to the actual value. Fix typo in `build/controllers/MimeTypeController.php` (DeryabinSergey)\n- Bug #19693: Fix db/Command not caching `NULL` result with scalar fetchMode (Arkeins)\n- Bug #19705: Add binary and other data types to `$typeMap` list for MySQL (sohelahmed7)\n- Bug #19712: Cast shell_exec() output to string for jsCompressor (impayru)\n- Bug #19720: Fix \"zh-HK\" locale causing [error][yii\\i18n\\PhpMessageSource::loadFallbackMessages] The message file for category 'yii' doesn't exist (uaoleg)\n- Bug #19731: Fix `yii\\data\\Sort` to generate a proper link when multisort is on and attribute has a default sort order set (bizley)\n- Bug #19734: PHP 8.1 compatibility fix for `$query->orderBy(null)` (uaoleg)\n- Bug #19735: Fix `yii\\validators\\NumberValidator` to use a programmable message for the value validation (bizley)\n- Bug #19736: Fix `StringHelper::truncate(null, 10)` causes error `Deprecated: mb_strlen(): Passing null to parameter #1 ($string) of type string is deprecated` (uaoleg)\n- Bug #19743: Non-associative array values in AR weren't considered dirty when reordered (samdark)\n- Bug #19749: Add PHP 8.2 support (samdark, schmunk42, aldok10, DanaLuther)\n- Bug #19770: Fix `yii\\mutex\\MysqlMutex` `keyPrefix` expression param binding (kamarton)\n- Bug #19795: Fix `yii\\web\\Response::redirect()` to prevent setting headers with URL containing new line character (bizley)\n- Bug #19807: Fix REST serializer not using `serializeModel()` when working with an array of models (zucha)\n- Bug #19813: Fix `yii\\base\\DynamicModel` validation with validators that reference missing attributes (michaelarnauts)\n- Bug #19828: Fix \"strtr(): Passing null to parameter #1 ($string) of type string is deprecated\" (uaoleg)\n- Bug #19837: Fixed processing of numeric file extensions in `yii\\build\\controllers\\MimeTypeController::generateMimeTypesFile()` (rhertogh)\n- Enh #9740: Usage of DI instead of new keyword in Schemas (manchenkoff)\n- Enh #15376: Added cache usage for `yii\\rbac\\DbManager::getRolesByUser()` (manchenkoff)\n- Enh #19689: Remove empty elements from the `class` array in `yii\\helpers\\BaseHtml::renderTagAttributes()` to prevent unwanted spaces (MoritzLost)\n- Enh #19741: Added option to use a closure for `$variations` definition in `yii\\filters\\PageCache` (nadar)\n- Enh #19766: Add support for PHP generators to JSON helper (vladis84)\n- Enh #19794: Add caching in `yii\\web\\Request` for `getUserIP()` and `getSecureForwardedHeaderTrustedParts()` (rhertogh)\n- Enh #19804: Remove the unnecessary call to `$this->oldAttributes` in `BaseActiveRecord::getDirtyAttributes()` (thiagotalma)\n- Enh #19816: Explicitly pass `$fallbackToMaster` as `true` to `getSlavePdo()` to ensure it isn't affected by child class with changed defaults (developedsoftware)\n- Enh #19838: Added `yii\\helpers\\BaseFileHelper::getExtensionByMimeType()` to get the most common extension for a given MIME type (rhertogh)\n- Chg #19696: Change visibility of `yii\\web\\View::isPageEnded` to `protected` (lubosdz, samdark)\n\n\n2.0.47 November 18, 2022\n------------------------\n\n- Bug #12636: Fix CompareValidator successful state when compareAttribute has an error (manchenkoff)\n- Bug #14388: Fixed fixture loading order in output message (manchenkoff)\n- Bug #15500: Fix saving empty records to MySQL (manchenkoff)\n- Bug #15557: Fix empty fields exclusion in safe attributes of `yii\\base\\Model` (manchenkoff)\n- Bug #16658: Fix file readability check on publishing assets by `yii\\web\\AssetManager` (manchenkoff)\n- Bug #19316: Fix MysqlMutex with same connection but difference database (kamarton)\n- Bug #19507: Fix eager loading of nested one-to-many relations (spo0okie)\n- Bug #19508: Fix wrong selection for boolean attributes in GridView (alnidok)\n- Bug #19517: Fix regression in `CompositeAuth::authenticate()` introduced in #19418 (WinterSilence)\n- Bug #19520: Fix for TIMESTAMP & ROWVERSION columns in MSSQL insert query (darkdef)\n- Bug #19530: Fix specifying the field id for `yii\\widgets\\ActiveField` (kv4nt)\n- Bug #19534: Fix `yii\\helpers\\BaseHtml::renderSelectOptions()` to properly render boolean selection (bizley)\n- Bug #19537: Fix default expression detection for MariaDB `date` and `time` columns (bizley)\n- Bug #19546: Reverted #19309 (bizley)\n- Bug #19581: Fix regression in `CompositeAuth` introduced in #19418 (SamMousa, WinterSilence, samdark)\n- Bug #19589: Fix Error reporting in to the `BaseArrayHelper::getValue()` (lav45)\n- Bug #19670: Fix Error null check PHP 8.1 `yii\\rbac\\DbManager` (samuelexyz)\n- Chg #17811: Do not reset `retryHandler` when `yii\\db\\Command::reset()` called (erickskrauch)\n- Chg #19354: Reuse `Validator::getAttributeNames()` in  `AttributeTypecastBehavior::detectAttributeTypes()` (WinterSilence)\n\n\n2.0.46 August 18, 2022\n----------------------\n\n- Bug #19235: Fix return type compatibility of `yii\\web\\SessionIterator` class methods for PHP 8.1 (virtual-designer)\n- Bug #19237: Fix OCI PHP 8.1 passing `null` to trim() (longthanhtran)\n- Bug #19243: Handle `finfo_open` for tar.xz as `application/octet-stream` on PHP 8.1 (longthanhtran)\n- Bug #19256: Pass missed `$view` to user's callback in `yii\\validators\\InlineValidator::clientValidateAttribute()` (WinterSilence)\n- Bug #19259: Fix `yii\\log\\FileTarget` locking and always rotate files only by copy (bizley, mikehaertl)\n- Bug #19272: Fix bug in dirty attributes check on multidimensional array (speedplli)\n- Bug #19291: Reset errors and validators in `yii\\base\\Model::__clone()` (WinterSilence)\n- Bug #19303: Fix serialization in `yii\\caching\\Dependency::generateReusableHash()` (WinterSilence)\n- Bug #19312: Fix PHP 8.1 error when passing null to `yii\\helpers\\BaseInflector` (WinterSilence)\n- Bug #19322: Revert force setting value to empty string in case it's `null` in `yii\\validators\\FilterValidator::validateAttribute()` (bizley)\n- Bug #19324: Fix `yii\\helpers\\BaseHtml::renderSelectOptions()` giving wrong selection for boolean attributes (adnandautovic)\n- Bug #19328: Passing null to parameter #1 ($string) of type string is deprecated in `yii\\db\\oci\\Schema` (Arkeins)\n- Bug #19329: Fix `yii\\web\\GroupUrlRule` to properly normalize prefix (bizley)\n- Bug #19349: Fix PHP 8.1 error when attribute and label of `yii\\grid\\DataColumn` are empty (githubjeka)\n- Bug #19368: Fix PHP 8.1 error when `$fileMimeType` is `null` in `yii\\validators\\FileValidator::validateMimeType()` (bizley)\n- Bug #19380: Fix PHP 8.1 passing non string to trim() in `yii\\db\\Query` (wa1kb0y)\n- Bug #19386: Fix recursive calling `yii\\helpers\\BaseArrayHelper::htmlDecode()` (WinterSilence)\n- Bug #19400: Fix passing null in `yii\\web\\Response::redirect()` (bizley)\n- Bug #19402: Add shutdown event and fix working directory in `yii\\base\\ErrorHandler` (WinterSilence)\n- Bug #19403: Fix types in `yii\\web\\SessionIterator` (WinterSilence)\n- Bug #19407: Fix `yii\\validators\\UniqueValidator` and `yii\\validators\\ExistValidator` to respect `skipOnError` option for target attributes (bizley)\n- Bug #19418: Fix `yii\\filters\\auth\\CompositeAuth` ignoring `only` and `except` options (lesha724)\n- Bug #19445: Fix caching in `yii\\i18n\\Formatter::getUnitMessage()` (WinterSilence)\n- Bug #19454: Fix PDO exception code not properly passed to `yii\\db\\Exception` (Roguyt)\n- Bug #19462: Fix validator client options to encode HTML tags (bizley)\n- Bug #19467: Revert changes in `Inflector::camel2words()` introduced in #19204 (samdark)\n- Bug #19469: Fix a virtual relation not working because of new isset checks in `\\yii\\db\\ActiveRelationTrait` (wvanheumen)\n- Bug #19471: Enable console commands on hosting with disabled `exec()` function (WinterSilence, lubosdz)\n- Bug #19477: Cast shell_exec() output to string (schmunk42)\n- Bug #19481: Exception is always empty in ErrorHandler when handling fatal error (Renkas)\n- Enh #19254: Support specifying custom characters for `yii.validation.trim()` and replace deprecated `jQuery.trim()` (WinterSilence)\n- Enh #19270: Replace deprecated `scss` converter in `yii\\web\\AssetConverter::$commands` (WinterSilence)\n- Enh #19295: Add alias `text/rtf` for mime-type `application/rtf` (lesha724)\n- Enh #19304: Add filtering validator `yii\\validators\\TrimValidator` (WinterSilence)\n- Enh #19308: Add `yii\\web\\UploadedFile::$fullPath` represents 'full_path' key added in PHP 8.1 (WinterSilence)\n- Enh #19309: Optimize `yii\\base\\Model::attributes()` (WinterSilence)\n- Enh #19318: Add support for typecasting PHP 8.1 enums (EtienneBruines)\n- Enh #19384: Normalize `setBodyParams()` and `getBodyParam()` in `yii\\web\\Request` (WinterSilence, albertborsos)\n- Enh #19401: Delay `exit(1)` in `yii\\base\\ErrorHandler::handleFatalError` (arrilot)\n- Enh #19416: Update and improve configurations for `yii\\console\\controllers\\MessageController` (WinterSilence)\n- Enh #19420: Update list of JS callbacks in `yii\\widgets\\MaskedInput` (WinterSilence)\n- Enh #19437: Add support to specify request port by trusted proxies in `\\yii\\web\\Request::getServerPort()` (rhertogh)\n\n\n2.0.45 February 11, 2022\n------------------------\n\n- Bug #19004: Container::resolveCallableDependencies() unable to handle union and intersection types (sartor)\n- Bug #19041: Fix PHP 8.1 issues (longthanhtran, samdark, pamparam83, sartor, githubjeka)\n- Bug #19047: Fix deprecated preg_match() passing null parameters #2 in db\\mysql\\Schema.php (longthanhtran)\n- Bug #19130: Fix DbSession breaks in some case (longthanhtran)\n- Bug #19138: Allow digits in language code (ntesic)\n- Bug #19148: Fix undefined array key errors in `yii\\db\\ActiveRelationTrait` (stevekr)\n- Bug #19182: RBAC Migration failed when use oracle with oci8 (Murolike)\n- Bug #19187: Fix `yii\\filters\\PageCache` to store original headers names instead of normalized ones (bizley)\n- Bug #19191: Change `\\Exception` to `\\Throwable` in `BadRequestHttpException` and `HttpException` (Dmitrijlin)\n- Bug #19204: Support numbers in Inflector::camel2words (longthanhtran)\n- Enh #18821: Allow `yii\\db\\ExpressionInterface` as column in `yii\\db\\conditions\\InBuilder` (ntesic)\n- Enh #19171: Added `$pagination` and `$sort` to `\\yii\\rest\\IndexAction` for easy configuration (rhertogh)\n\n\n2.0.44 December 30, 2021\n------------------------\n\n- Bug #17119: Fix `yii\\caching\\Cache::multiSet()` to use `yii\\caching\\Cache::$defaultDuration` when no duration is passed (OscarBarrett)\n- Bug #18646: Remove stale identity data from session if `IdentityInterface::findIdentity()` returns `null` (mikehaertl)\n- Bug #18660: Fix database migration template does not work with namespaced migrations when migration name does not include namespace (iridance)\n- Bug #18798: Fix `StringHelper::dirname()` when passing string with a trailing slash (perlexed)\n- Bug #18832: Fix `Inflector::camel2words()` adding extra spaces (brandonkelly)\n- Bug #18842: Fix `yii\\base\\Controller::bindInjectedParams()` to not throw error when argument of `ReflectionUnionType` type is passed (bizley)\n- Bug #18845: Fix duplicating `id` in `MigrateController::addDefaultPrimaryKey()` (WinterSilence, samdark)\n- Bug #18880: Fix `yii\\helpers\\ArrayHelper::toArray()` for `DateTime` objects in PHP >= 7.4 (rhertogh)\n- Bug #18883: Fix `yii\\web\\HeaderCollection::fromArray()` now ensures lower case keys (rhertogh)\n- Bug #18898: Fix `yii\\helpers\\Inflector::camel2words()` to work with words ending with 0 (michaelarnauts)\n- Bug #18909: Fix bug with binding default action parameters for controllers (bizley)\n- Bug #18913: Add filename validation for `MessageSource::getMessageFilePath()` (uaoleg)\n- Bug #18955: Check `yiisoft/yii2-swiftmailer` before using as default mailer in `yii\\base\\Application` (WinterSilence)\n- Bug #18988: Fix default value of `yii\\console\\controllers\\MessageController::$translator` (WinterSilence)\n- Bug #18993: Load defaults by `attributes()` in `yii\\db\\ActiveRecord::loadDefaultValues()` (WinterSilence)\n- Bug #19021: Fix return type in PhpDoc `yii\\db\\Migration` functions `up()`, `down()`, `safeUp()` and `safeDown()` (WinterSilence, rhertogh)\n- Bug #19030: Add DI container usage to `yii\\base\\Widget::end()` (papppeter)\n- Bug #19031: Fix displaying console help for parameters with declared types (WinterSilence)\n- Bug #19096: Fix `Request::getIsConsoleRequest()` may return erroneously when testing a Web application in Codeception (WinterSilence)\n- Enh #13105: Add yiiActiveForm `validate_only` property for skipping form auto-submission (ptolomaues)\n- Enh #18328: Raise warning when trying to register a file after `View::endPage()` has been called (perlexed)\n- Enh #18762: Add `yii\\helpers\\Json::$keepObjectType` and `yii\\web\\JsonResponseFormatter::$keepObjectType` in order to avoid changing zero-indexed objects to array in `yii\\helpers\\Json::encode()` (zebraf1)\n- Enh #18783: Add support for URI namespaced tags in `XmlResponseFormatter` (WinterSilence, samdark)\n- Enh #18783: Add `XmlResponseFormatter::$objectTagToLowercase` option to lowercase object tags (WinterSilence, samdark)\n- Enh #18812: Add error messages and optimize \"error\" methods in `yii\\helpers\\BaseJson` (WinterSilence, samdark)\n- Enh #18826: Add ability to turn the sorting off for a clicked column in GridView with multisort (ditibal)\n- Enh #18858: Reduce memory usage in `yii\\base\\View::afterRender` method (LeoOnTheEarth)\n- Enh #18899: Replace usages of `strpos` with `strncmp` and remove redundant usage of `array_merge` and `array_values` (AlexGx)\n- Enh #18904: Improve Captcha client-side validation (hexkir)\n- Enh #18967: Use proper attribute names for tabular data in `yii\\widgets\\ActiveField::addAriaAttributes()` (AnkIF)\n- Enh #19005: Add `yii\\base\\Module::setControllerPath()` (WinterSilence)\n- Enh #19098: Add `yii\\helper\\BaseHtml::$normalizeClassAttribute` to fix duplicate classes (WinterSilence)\n- Enh #19108: Optimize `Component::hasEventHandlers()` and `Component::trigger()` (WinterSilence)\n- Chg #18823: Rollback changes from #18806 in `yii\\validators\\ExistValidator::checkTargetRelationExistence()` (WinterSilence)\n\n\n2.0.43 August 09, 2021\n----------------------\n\n- Bug #14663: Do not convert int to string if database type of column is numeric (egorrishe)\n- Bug #18274: Fix `yii\\log\\Logger` to calculate profile timings no matter the value of the flush interval (bizley)\n- Bug #18648: Fix `yii\\web\\Request` to properly handle HTTP Basic Auth headers (olegbaturin)\n- Bug #18650: Refactor `framework/assets/yii.activeForm.js` arrow function into traditional function for IE11 compatibility (marcovtwout)\n- Bug #18678: Fix `yii\\caching\\DbCache` to use configured cache table name instead of the default one in case of MSSQL varbinary column type detection (aidanbek)\n- Bug #18749: Fix `yii\\web\\ErrorHandler::encodeHtml()` to support strings with invalid UTF symbols (vjik)\n- Bug #18756: Fix `\\yii\\validators\\ExistValidator::queryValueExists` to validate against an array of unique values (DrDeath72)\n- Bug #18807: Fix replacing source whitespaces and optimize code of `yii\\helpers\\BaseStringHelper::mb_ucwords()` (WinterSilence)\n- Enh #18274: Add `profilingAware` option to `yii\\log\\Logger` to prevent breaking the profiling block messages pair when flushing them (bizley)\n- Enh #18628: Add strings \"software\", and \"hardware\" to `$specials` array in `yii\\helpers\\BaseInflector` (kjusupov)\n- Enh #18653: Add method `yii\\helpers\\BaseHtml::getInputIdByName()` (WinterSilence)\n- Enh #18656: Add ability for `yii serve`'s `--router` param to take an alias (markhuot)\n- Enh #18669: Change visibility of `yii\\web\\User::checkRedirectAcceptable()` to `public` (rhertogh)\n- Enh #18674: Add more user-friendly exception messages for `yii\\i18n\\Formatter` (bizley)\n- Enh #18676: Add method `yii\\helpers\\BaseFileHelper::changeOwnership()` and `newFileMode`/`newFileOwnership` properties to `yii\\console\\controllers\\BaseMigrateController` (rhertogh)\n- Enh #18695: Add `yii\\web\\Cookie::SAME_SITE_NONE` constant (rhertogh)\n- Enh #18707: Change the base error handler to not expose `$_SERVER` details unless `YII_DEBUG` is enabled (coolgoose)\n- Enh #18712: Add `scheme` option for `$options` argument for `yii\\i18n\\Formatter::asUrl()` (bizley)\n- Enh #18724: Allow jQuery 3.6 to be installed (marcovtwout)\n- Enh #18726: Add `yii\\helpers\\Json::$prettyPrint` (rhertogh)\n- Enh #18734: Add `yii\\validators\\EmailValidator::$enableLocalIDN` (brandonkelly)\n- Enh #18789: Add JSONP support in `yii\\web\\JsonParser::parse()` (WinterSilence)\n- Enh #18817: Use `paragonie/random_compat` for random bytes and int generation (samdark)\n\n\n2.0.42.1 May 06, 2021\n---------------------\n\n- Bug #18634: Fix `yii\\db\\BaseActiveRecord::unlink()` and `unlinkAll()` to omit condition for `on` property when it doesn't exist (bizley)\n\n\n2.0.42 May 05, 2021\n-------------------\n\n- Bug #14343: Fix `yii\\test\\ActiveFixture` to use model's DB connection instead of the default one (margori, bizley)\n- Bug #17174: Fix `yii\\db\\BaseActiveRecord::unlink()` to not ignore `on` conditions in `via` relations (bizley)\n- Bug #17203: Fix `yii\\db\\Connection` to persist customized `queryBuilder` configuration after the `close()` → `open()` cycle (silverfire)\n- Bug #17479: Fix `yii\\grid\\ActionColumn` to render icons when no glyphicons are available (simialbi)\n- Bug #17631: Fix `yii\\widgets\\BaseListView` to properly render custom summary (sjaakp, bizley)\n- Bug #18323: Fix client validation of RadioList when there are disabled items (toir427)\n- Bug #18325: Fix `yii\\db\\pgsql\\Schema` to respect non-default PgSQL schema name for data types (theonedemon, silverfire)\n- Bug #18526: Fix `yii\\caching\\DbCache` to work with MSSQL, add `normalizeTableRowData()` to `yii\\db\\mssql\\QueryBuilder::upsert()` (darkdef)\n- Bug #18544: Fix `yii\\validators\\NumberValidator` to disallow values with whitespaces (bizley)\n- Bug #18552: Fix `yii\\data\\SqlDataProvider` to properly handle SQL with `ORDER BY` clause (bizley)\n- Bug #18557: Fix `yii\\data\\ActiveDataProvider` to handle DB connection configuration of different type than just `yii\\db\\Connection` (bizley)\n- Bug #18574: Fix `yii\\web\\DbSession` to use the correct db if strict mode is used (Mignar)\n- Bug #18585: Fix `yii\\validators\\EmailValidator` to handle an edge case where `IDN` is enabled, but fails ascii conversion for valid email addresses (ihitbuttons)\n- Bug #18590: Fix `yii\\web\\UrlManager` to instantiate cache only when it's actually needed (bizley)\n- Bug #18592: Fix `yii\\db\\Command::getRawSql()` to not replace query params in invalid places (sartor)\n- Bug #18593: Fix setting the `maxlength` attribute for `Html::activeInput()` and `Html::activeTextArea()` based on `length` parameter of validator (BSCheshir)\n- Bug #18604: Function alterColumn for MSSQL build incorrect query with default values `NULL` and other expressions (darkdef)\n- Bug #18613: Do not call static methods non-statically in `BaseActiveRecord` (samdark)\n- Bug #18619: Do not modify `yii\\web\\Cookie::$path` on `yii\\web\\Response::sendCookies()` (mikk150)\n- Bug #18624: Fix `yii\\di\\Container` to properly resolve dependencies in case of PHP 8 union types (bizley)\n- Enh #18534: Add `prepareSearchQuery` property in `yii\\rest\\IndexAction` (programmis)\n- Enh #18566: Throw the original exception when `yii\\web\\Controller::bindInjectedParams()` catches HttpException (pigochu)\n- Enh #18569: Add `NumberValidator::$allowArray` (raidkon)\n\n\n2.0.41.1 March 04, 2021\n-----------------------\n\n- Bug #18545: Reversed changes made to the `yii\\db\\Query::all()` and `indexBy` handling (bizley)\n- Bug #18548: Fix bug with REST rules with prefixes containing tokens not being parsed properly (bizley)\n\n\n2.0.41 March 03, 2021\n---------------------\n\n- Bug #8750: Fix MySQL support when running in `ANSI`/`ANSI_QUOTES` modes (brandonkelly)\n- Bug #9718: Fix user staying authorized despite authKey change (kidol, Charlie Jack, Kunal Mhaske, samdark)\n- Bug #18448: Fix issues in queries and tests for older MSSQL versions (darkdef)\n- Bug #18450: Allow empty string to be passed as a nullable typed argument to a controller's action (dicrtarasov, bizley)\n- Bug #18464: Fix bug with processing fallback messages when translation language is set to `null` (bizley)\n- Bug #18472: Fix initializing `db` component configuration in `yii\\data\\ActiveDataProvider` (bizley)\n- Bug #18477: Fix detecting availability of Xdebug's stack trace in `yii\\base\\ErrorException` (bizley)\n- Bug #18479: Fix invalid argument type for `preg_split()` in `\\yii\\console\\Controller` (gazooz)\n- Bug #18480: Transactions are not committed using the dblib driver (bbrunekreeft)\n- Bug #18505: Fix `yii\\helpers\\ArrayHelper::getValue()` for ArrayAccess objects with explicitly defined properties (samdark)\n- Bug #18508: Fix Postgres SQL query for load table indexes with correct column order (insolita)\n- Bug #18529: Fix asset files path with `appendTimestamp` option for non-root-relative base URLs (bizley)\n- Bug #18535: Set Cookie SameSite to Lax by default (samdark)\n- Bug #18539: Fix \"driver does not support quoting\" when using the driver pdo_odbc (xpohoc69)\n- Enh #18447: Do not use `getLastInsertID()` to get PK from insert query to lower collision probability for concurrent inserts (darkdef)\n- Enh #18455: Add ability to use separate attributes for data model and filter model of `yii\\grid\\GridView` in `yii\\grid\\DataColumn` (PowerGamer1)\n- Enh #18457: Add `EVENT_RESET` and `EVENT_FINISH` events to `yii\\db\\BatchQueryResult` (brandonkelly)\n- Enh #18460: `compareValue` in `CompareValidator` can now take a closure returning a value (mmonem)\n- Enh #18483: Add `yii\\log\\Logger::$dbEventNames` that allows specifying event names used to get statistical results (profiling) of DB queries (atiline)\n- Enh #18487: Allow creating URLs for non-GET-verb rules (bizley)\n- Enh #18493: Faster request parsing for REST UrlRule with prefix handling (bizley)\n- Enh #18499: When using `yii\\db\\Query::all()` and `yii\\db\\Query::$indexBy`, the `yii\\db\\Query::$indexBy` is auto inserted into `yii\\db\\Query::$select` - the same as in `yii\\db\\Query::column()` (OndrejVasicek, samdark, bizley)\n- Enh #18518: Add support for ngrok’s `X-Original-Host` header (brandonkelly)\n\n\n2.0.40 December 23, 2020\n------------------------\n\n- Bug #16492: Fix eager loading Active Record relations when relation key is a subject to a type-casting behavior (bizley)\n- Bug #18199: Fix content body response on 304 HTTP status code, according to RFC 7232 (rad8329)\n- Bug #18287: Fix the OUTPUT got SQL syntax error if the column name is MSSQL keyword e.g. key (darkdef)\n- Bug #18339: Fix migrate controller actions to return exit codes (haohetao, bizley)\n- Bug #18365: Move quoting of table names to upper level to function `getSchemaMetadata()` in MSSQL driver to get clean names from the schema (darkdef)\n- Bug #18383: RBAC's generated file made PSR-12 compliant (perlexed)\n- Bug #18386: Fix `assets/yii.activeForm.js` incorrect target selector for `validatingCssClass` (brussens)\n- Bug #18393: Fix `ActiveRecord::refresh()` to load data from the database even if cache is enabled (hooman-mirghasemi)\n- Bug #18395: Fix regression in `yii\\helpers\\BaseArrayHelper::filter()` (allowing filtering arrays with numeric keys) (bizley)\n- Bug #18400: Set parent module of the newly attached child module by `Module::setModule()` and `Module::setModules()` (sup-ham)\n- Bug #18406: Fix PDO exception when committing or rolling back an autocommitted transaction in PHP 8 (brandonkelly)\n- Bug #18414: Fix `AssetManager::appendTimestamp()` not appending timestamp for website root in sub-directory (Isitar)\n- Bug #18426: Fix check for route's leading slash in `yii\\widgets\\Menu` (stevekr)\n- Bug #18435: Fix ensuring Active Record relation links' keys to be strings (bizley)\n- Bug #18437: Change the check order whether an object is an implementation of `Arrayable` or `JsonSerializable` in `\\yii\\base\\ArrayableTrait::toArray()` and `\\yii\\rest\\Serializer::serialize()` (spell6inder)\n- Bug #18442: Fix calls with array access to string (bizley)\n- Enh #18381: The `yii\\web\\AssetManager` `$basePath` readable and writeable check has been moved to the `checkBasePathPermission()`. This check will run once before `publishFile()` and `publishDirectory()` (nadar)\n- Enh #18394: Add support for setting `yii\\web\\Response::$stream` to a callable (brandonkelly)\n\n\n2.0.39.3 November 23, 2020\n--------------------------\n\n- Bug #18396: Fix not throw `InvalidConfigException` when failed to instantiate class via DI container in some cases (vjik)\n- Enh #18200: Add `maxlength` attribute by default to the input text when it is an active field within a `yii\\grid\\DataColumn` (rad8329)\n\n\n2.0.39.2 November 13, 2020\n--------------------------\n\n- Bug #18378: Fix not taking default value when unable to resolve abstract class via DI container (vjik)\n\n\n2.0.39.1 November 10, 2020\n--------------------------\n\n- Bug #18373: Fix not taking default value when unable to resolve non-existing class via DI container (vjik)\n- Enh #18370: Add option to provide a string replacement for `null` value in `yii\\data\\DataFilter` (bizley)\n\n\n2.0.39 November 10, 2020\n------------------------\n\n- Bug #16418: Fix `yii\\data\\Pagination::getLinks()` to return links to the first and the last pages regardless of the current page (ptz-nerf, bizley)\n- Bug #16831: Fix console Table widget does not render correctly in combination with ANSI formatting (issidorov, cebe)\n- Bug #18160, #18192: Fix `registerFile` with set argument `depends` does not take `position` and `appendTimestamp` into account (baleeny)\n- Bug #18263: Fix writing `\\yii\\caching\\FileCache` files to the same directory when `keyPrefix` is set (githubjeka)\n- Bug #18287: Fix for `OUTPUT INSERTED` and computed columns. Add flag to mark computed values in table schema (darkdef)\n- Bug #18290: Fix response with non-seekable streams (schmunk42)\n- Bug #18297: Replace usage of deprecated `ReflectionParameter::isArray()` method in PHP8 (baletskyi)\n- Bug #18303: Fix creating migration for column methods used after `defaultValues` (wsaid)\n- Bug #18308: Fix `\\yii\\base\\Model::getErrorSummary()` reverse order (DrDeath72)\n- Bug #18313: Fix multipart form data parsing with double quotes (wsaid)\n- Bug #18317: Additional PHP 8 compatibility fixes (samdark, bizley)\n- Enh #18247: Add support for the 'session.use_strict_mode' ini directive in `yii\\web\\Session` (rhertogh)\n- Enh #18285: Enhanced DI container to allow passing parameters by name in a constructor (vjik)\n- Enh #18304: Add support of constructor parameters with default values to DI container (vjik)\n- Enh #18351: Add option to change default timezone for parsing formats without time part in `yii\\validators\\DateValidator` (bizley)\n\n\n2.0.38 September 14, 2020\n-------------------------\n\n- Bug #13973: Correct alterColumn for MSSQL & drop constraints before dropping a column (darkdef)\n- Bug #15265: PostgreSQL > 10.0 is not pass tests with default value of timestamp CURRENT_TIMESTAMP (terabytesoftw)\n- Bug #16892: Validation error class was not applied to checkbox and radio when validationStateOn = self::VALIDATION_STATE_ON_INPUT (dan-szabo, samdark)\n- Bug #18040: Display width specification for integer data types was deprecated in MySQL 8.0.19 (terabytesoftw)\n- Bug #18066: Fix `yii\\db\\Query::create()` wasn't using all info from `withQuery()` (maximkou)\n- Bug #18229: Add a flag to specify SyBase database when used with pdo_dblib (darkdef)\n- Bug #18232: Fail tests pgsql v-10.14, v-11.9, v-12-latest (terabytesoftw)\n- Bug #18233: Add PHP 8 support (samdark)\n- Bug #18239: Fix support of no-extension files for `FileValidator::validateExtension()` (darkdef)\n- Bug #18245: Make resolving DI references inside of arrays in dependencies optional (SamMousa, samdark, hiqsol)\n- Bug #18248: Render only one stack trace on a console for chained exceptions (mikehaertl)\n- Bug #18269: Fix integer safe attribute to work properly in `yii\\base\\Model` (Ladone)\n- Bug: (CVE-2020-15148): Disable unserialization of `yii\\db\\BatchQueryResult` to prevent remote code execution in case application calls unserialize() on user input containing specially crafted string (samdark, russtone)\n- Enh #18196: `yii\\rbac\\DbManager::$checkAccessAssignments` is now `protected` (alex-code)\n- Enh #18213: Do not load fixtures with circular dependencies twice instead of throwing an exception (JesseHines0)\n- Enh #18236: Allow `yii\\filters\\RateLimiter` to accept a closure function for the `$user` property in order to assign values on runtime (nadar)\n\n\n2.0.37 August 07, 2020\n----------------------\n\n- Bug #17147: Fix form attribute validations for empty select inputs (kartik-v)\n- Bug #18156: Fix `yii\\db\\Schema::quoteSimpleTableName()` was checking incorrect quote character (M4tho, samdark)\n- Bug #18170: Fix 2.0.36 regression in passing extra console command arguments to the action (darkdef)\n- Bug #18171: Change case of column names in SQL query for `findConstraints` to fix MySQL 8 compatibility (darkdef)\n- Bug #18182: `yii\\db\\Expression` was not supported as condition in `ActiveRecord::findOne()` and `ActiveRecord::findAll()` (rhertogh)\n- Bug #18189: Fix \"Invalid parameter number\" in `yii\\rbac\\DbManager::removeItem()` (samdark)\n- Bug #18198: Fix saving tables with trigger by outputting inserted data from insert query with usage of temporary table (darkdef)\n- Bug #18203: PDO exception code was not properly passed to `yii\\db\\Exception` (samdark)\n- Bug #18204: Fix 2.0.36 regression in inline validator and JS validation (samdark)\n- Enh #18205: Add `.phpstorm.meta.php` file for better auto-completion in PhpStorm (vjik)\n- Enh #18210: Allow strict comparison for multi-select inputs (alex-code)\n- Enh #18217: Make `yii\\console\\ErrorHandler` render chained exceptions in debug mode (mikehaertl)\n\n\n2.0.36 July 07, 2020\n--------------------\n\n- Bug #13828: Fix retrieving inserted data for a primary key of type uniqueidentifier for SQL Server 2005 or later (darkdef)\n- Bug #17474: Fix retrieving inserted data for a primary key of type trigger for SQL Server 2005 or later (darkdef)\n- Bug #17985: Convert migrationNamespaces to array if needed (darkdef)\n- Bug #18001: Fix getting table metadata for tables `(` in their name (floor12)\n- Bug #18026: Fix `ArrayHelper::getValue()` did not work with `ArrayAccess` objects (mikk150)\n- Bug #18028: Fix division by zero exception in console `Table::calculateRowHeight()` (fourhundredfour)\n- Bug #18031: `HttpBasicAuth` with auth callback now triggers login events same was as other authentication methods (samdark)\n- Bug #18041: Fix RBAC migration for MSSQL (darkdef)\n- Bug #18047: Fix colorization markers output in console `Table` (cheeseq)\n- Bug #18051: Fix missing support for custom validation method in EachValidator (bizley)\n- Bug #18051: Fix using `EachValidator` with custom validation function (bizley)\n- Bug #18081: Fix for PDO_DBLIB/MSSQL. Set flag `ANSI_NULL_DFLT_ON` to ON for current DB connection (darkdef)\n- Bug #18086: Fix accessing public properties of `ArrayAccess` via `ArrayHelper::getValue()` (samdark)\n- Bug #18094: Add support for composite file extension validation (darkdef)\n- Bug #18096: Fix `InlineValidator` with anonymous inline function not working well from `EachValidator` (trombipeti)\n- Bug #18101: Fix behavior of `OUTPUT INSERTED.*` for SQL Server query: \"insert default values\"; correct MSSQL unit tests; turn off profiling echo message in migration test (darkdef)\n- Bug #18105: Fix for old trigger in RBAC migration with/without `prefixTable` (darkdef)\n- Bug #18110: Add quotes to return value of viewName in MSSQL schema. It is `[someView]` now (darkdef)\n- Bug #18127: Resolve DI references inside of arrays in dependencies (hiqsol)\n- Bug #18134: `Expression` as `columnName` should not be quoted in `likeCondition` (darkdef)\n- Bug #18147: Fix parameters binding for MySQL when prepare emulation is off (rskrzypczak)\n- Enh #15202: Add optional param `--silent-exit-on-exception` in `yii\\console\\Controller` (egorrishe)\n- Enh #17722: Add action injection support (SamMousa, samdark, erickskrauch)\n- Enh #18019: Allow jQuery 3.5.0 to be installed (wouter90)\n- Enh #18048: Use `Instance::ensure()` to set `User::$accessChecker` (lav45)\n- Enh #18083: Add `Controller::$request` and `$response` (brandonkelly)\n- Enh #18120: Include the path to the log file into error message if `FileTarget::export` fails (uaoleg)\n- Enh #18151: Add `Mutex::isAcquired()` to check if lock is currently acquired (rhertogh)\n\n\n2.0.35 May 02, 2020\n-------------------\n\n- Bug #16481: Fix RBAC MSSQL trigger (achretien)\n- Bug #17653: Fix `TypeError: pair[1] is undefined` when query param doesn't have `=` sign (baso10)\n- Bug #17810: Fix `EachValidator` crashing with uninitialized typed properties (ricardomm85)\n- Bug #17942: Fix for `DbCache` loop in MySQL `QueryBuilder` (alex-code)\n- Bug #17948: Ignore errors caused by `set_time_limit(0)` (brandonkelly)\n- Bug #17960: Fix unsigned primary key type mapping for SQLite (bizley)\n- Bug #17961: Fix pagination `pageSizeLimit` ignored if set as array with more then 2 elements (tsvetiligo)\n- Bug #17974: Fix `ActiveRelationTrait` compatibility with PHP 7.4 (Ximich)\n- Bug #17975: Fix deleting unused messages with console command if message tables were created manually (auerswald, cebe)\n- Bug #17991: Improve `yii\\db\\Connection` master and slave failover, no connection attempt was made when all servers are marked as unavailable  (cebe)\n- Bug #18000: PK value of Oracle ActiveRecord is missing after save (mankwok)\n- Bug #18010: Allow upper or lower case operators in `InCondition` and `LikeCondition` (alex-code)\n- Bug #18011: Add attribute labels support for `DynamicModel`, fixed `EachValidator` to pass the attribute label to the underlying `DynamicModel` (storch)\n- Enh #17758: `Query::withQuery()` can now be used for CTE (sartor)\n- Enh #17993: Add `yii\\i18n\\Formatter::$currencyDecimalSeparator` to allow setting custom symbols for currency decimal in `IntlNumberFormatter` (XPOHOC269)\n- Enh #18006: Allow `SameSite` cookie pre PHP 7.3 (scottix)\n\n\n2.0.34 March 26, 2020\n---------------------\n\n- Bug #17932: Fix regression in detection of AJAX requests (samdark)\n- Bug #17933: Log warning instead of erroring when URLManager is unable to initialize cache (samdark)\n- Bug #17934: Fix regression in Oracle when binding several string parameters (fen1xpv, samdark)\n- Bug #17935: Reset DB quoted table/column name caches when the connection is closed (brandonkelly)\n\n\n2.0.33 March 24, 2020\n---------------------\n\n- Bug #11945: Fix Schema Builder MySQL column definition order (simialbi)\n- Bug #13749: Fix Yii opens db connection even when hits query cache (shushenghong)\n- Bug #16092: Fix duplicate joins in usage of `joinWith` (germanow)\n- Bug #16145: Fix `Html` helper `checkboxList()`, `radioList()`, `renderSelectOptions()`, `dropDownList()`, `listBox()` methods to work properly with traversable selection (samdark)\n- Bug #16334: Add `\\JsonSerializable` support to `ArrayableTrait` (germanow)\n- Bug #17667: Fix `CREATE INDEX` failure on SQLite when specifying schema (santilin, samdark)\n- Bug #17679: Fix Oracle exception \"ORA-01461: can bind a LONG value only for insert into a LONG column\" when inserting 4k+ string (vinpel, 243083df)\n- Bug #17797: Fix for `activeListInput` options (alex-code)\n- Bug #17798: Avoid creating directory for stream log targets in `FileTarget` (wapmorgan)\n- Bug #17828: Fix `yii\\web\\UploadedFile::saveAs()` failing when error value in `$_FILES` entry is a string (haveyaseen)\n- Bug #17829: `yii\\helpers\\ArrayHelper::filter` now correctly filters data when passing a filter with more than 2 levels (rhertogh)\n- Bug #17843: Fix `yii\\web\\Session::setCookieParamsInternal` checked \"samesite\" parameter incorrectly (schevgeny)\n- Bug #17850: Update to `ReplaceArrayValue` config exception message (alex-code)\n- Bug #17859: Fix loading fixtures under Windows (samdark)\n- Bug #17863: `\\yii\\helpers\\BaseInflector::slug()` doesn't work with an empty string as a replacement argument (haruatari)\n- Bug #17875: Use `move_uploaded_file()` function instead of `copy()` and `unlink()` for saving uploaded files in case of POST request (sup-ham)\n- Bug #17878: Detect CORS AJAX requests without `X-Requested-With` in `Request::getIsAjax()` (dicrtarasov, samdark)\n- Bug #17881: `yii\\db\\Query::queryScalar()` wasn’t reverting the `select`, `orderBy`, `limit`, and `offset` params if an exception occurred (brandonkelly)\n- Bug #17884: Fix 0 values in console Table rendered as empty string (mikehaertl)\n- Bug #17886: Fix `yii\\rest\\Serializer` to serialize arrays (patacca)\n- Bug #17909: Reset DB schema, transaction, and driver name when the connection is closed (brandonkelly)\n- Bug #17920: Fix quoting for `Command::getRawSql` having `Expression` in params (alex-code)\n- Enh #7622: Allow `yii\\data\\ArrayDataProvider` to control the sort flags for `sortModels` through `yii\\data\\Sort::sortFlags` property (askobara)\n- Enh #16721: Use `Instance::ensure()` to initialize `UrlManager::$cache` (rob006)\n- Enh #17827: Add `StringValidator::$strict` that can be turned off to allow any scalars (adhayward, samdark)\n- Enh #17929: Actions can now have bool typed params bound (alex-code)\n\n\n2.0.32 January 21, 2020\n-----------------------\n\n- Bug #12539: `yii\\filters\\ContentNegotiator` now generates 406 'Not Acceptable' instead of 415 'Unsupported Media Type' on content-type negotiation fail (PowerGamer1)\n- Bug #17037: Fix uploaded file saving method when data came from `MultipartFormDataParser` (sup-ham)\n- Bug #17300: Fix class-level event handling with wildcards (Toma91)\n- Bug #17635: Fix varbinary data handling for MSSQL (toatall)\n- Bug #17744: Fix a bug with setting incorrect `defaultValue` to AR column with `CURRENT_TIMESTAMP(x)` as default expression (MySQL >= 5.6.4) (bizley)\n- Bug #17749: Fix logger dispatcher behavior when target crashes in PHP 7.0+ (kamarton)\n- Bug #17755: Fix a bug for web request with `trustedHosts` set to format `['10.0.0.1' => ['X-Forwarded-For']]` (shushenghong)\n- Bug #17760: Fix `JSON::encode()` for `\\DateTimeInterface` under PHP 7.4 (samdark)\n- Bug #17762: PHP 7.4: Remove special condition for converting PHP errors to exceptions if they occurred inside of `__toString()` call (rob006)\n- Bug #17766: Remove previous PJAX event binding before registering new one (samdark)\n- Bug #17767: Make `Formatter::formatNumber` method protected (TheCodeholic)\n- Bug #17771: migrate/fresh was not returning exit code (samdark)\n- Bug #17793: Fix inconsistent handling of null `data` attribute values in `yii\\helpers\\BaseHtml::renderTagAttributes()` (brandonkelly)\n- Bug #17803: Fix `ErrorHandler` unregister and register to only change global state when applicable (SamMousa)\n- Enh #17729: Path alias support was added to `yii\\web\\UploadedFile::saveAs()` (sup-ham)\n- Enh #17792: Add support for `aria` attributes to `yii\\helpers\\BaseHtml::renderTagAttributes()` (brandonkelly)\n\n2.0.31 December 18, 2019\n------------------------\n\n- Bug #17661: Fix query builder incorrect IN/NOT IN condition handling for null values (strychu)\n- Bug #17685: Fix invalid db component in `m180523_151638_rbac_updates_indexes_without_prefix` (rvkulikov)\n- Bug #17687: `Query::indexBy` can now include a table alias (brandonkelly)\n- Bug #17694: Fixed Error Handler to clear registered view tags, scripts, and files when rendering error view through action view (bizley)\n- Bug #17701: Throw `BadRequestHttpException` when request params can’t be bound to `int` and `float` controller action arguments (brandonkelly)\n- Bug #17710: Fix MemCache duration normalization to avoid memcached/system timestamp mismatch (samdark)\n- Bug #17723: Fix `Model::activeAttributes()` to access array offset on value of non-string (samdark)\n- Bug #17723: Fix incorrect decoding of default binary value for PostgreSQL (samdark)\n- Bug #17723: Fix incorrect type-casting of reflection type to string (samdark)\n- Bug #17725: Ensure we do not use external polyfills for pbkdf2() as these may be implemented incorrectly (samdark)\n- Bug #17740: `yii\\helpers\\BaseInflector::slug()` doesn't replace multiple replacement string occurrences to single one (batyrmastyr)\n- Bug #17745: Fix PostgreSQL query builder drops default value when it is empty (xepozz)\n- Enh #17665: Implement RFC 7239 `Forwarded` header parsing in Request (mikk150, kamarton)\n- Enh #17720: DI 3 support for application core components and default object configurations (sup-ham)\n\n\n2.0.30 November 19, 2019\n------------------------\n\n- Bug #17434: IE Ajax redirect fix for non 11.0 versions (kamarton)\n- Bug #17632: Unicode file name was not correctly parsed in multipart forms (AlexRas007, samdark)\n- Bug #17648: Handle empty column arrays in console `Table` widget (alex-code)\n- Bug #17657: Fix migration errors from missing `$schema` in RBAC init file when using MSSQL (PoohOka)\n- Bug #17670: Fix overriding core component class using `__class` (sup-ham, samdark)\n\n\n2.0.29 October 22, 2019\n-----------------------\n\n- Bug #8225: Fixed AJAX validation with checkboxList was only triggered on first select (execut)\n- Bug #17597: PostgreSQL 12 and partitioned tables support (batyrmastyr)\n- Bug #17602: `EmailValidator` with `checkDNS=true` throws `ErrorException` on bad domains on Alpine (batyrmastyr)\n- Bug #17606: Fix error in `AssetBundle` when a disabled bundle with custom init() was still published (onmotion)\n- Bug #17625: Fix boolean `data` attributes from subkeys rendering in `Html::renderTagAttributes()` (brandonkelly)\n- Enh #17607: Added Yii version 3 DI config compatibility (hiqsol)\n\n\n2.0.28 October 08, 2019\n-----------------------\n\n- Bug #17573: `Request::getUserIP()` security fix for the case when `Request::$trustedHost` and `Request::$ipHeaders` are used (kamarton)\n- Bug #17585: Fix `yii\\i18n\\Formatter` including the `@calendar` locale param in `Yii::t()` calls (brandonkelly)\n- Bug #17853: Fix errors in ActiveField to be properly caught when PHP 7 is used (My6UoT9)\n\n\n2.0.27 September 18, 2019\n-------------------------\n\n- Bug #16610: ErrorException trace was cut when using XDebug (Izumi-kun)\n- Bug #16671: Logging in `Connection::open()` was not respecting `Connection::$enableLogging` (samdark)\n- Bug #16855: Ignore console commands that have no actions (alexeevdv)\n- Bug #17434: Fix regular expression illegal character; Repeated fix for Internet Explorer 11 AJAX redirect bug in case of 301 and 302 response codes (`XMLHttpRequest: Network Error 0x800c0008`) (kamarton)\n- Bug #17539: Fixed error when using `batch()` with `indexBy()` with MSSQL (alexkart)\n- Bug #17549: Fix `yii\\db\\ExpressionInterface` not supported in `yii\\db\\conditions\\SimpleConditionBuilder` (razvanphp)\n- Enh #15526: Show valid aliases and options on invalid input in console application (samdark)\n- Enh #16826: `appendTimestamp` support was added to `View` methods `registerCssFile()` and `registerJsFile()` (onmotion)\n\n\n2.0.26 September 03, 2019\n-------------------------\n\n- Bug #16305: Fix `FileValidator` mime-type validation failure because of case sensitivity (kamarton)\n- Bug #16531: Fix error in `Response::sendContent()` when `set_time_limit()` is disabled (brandonkelly)\n- Bug #17355: Fix incorrect sequence of `EVENT_AFTER_REQUEST` when using Pjax (kamarton)\n- Bug #17434: Fix Internet Explorer 11 AJAX redirect bug in case of 301 and 302 response codes (`XMLHttpRequest: Network Error 0x800c0008`) (kamarton)\n- Bug #17449: Ensure `CHECK` statement goes after `COMMENT` in MySQL `QueryBuilder::addCommentOnColumn()` (Manu311)\n- Bug #17504: Fix upsert when `$updateColumns` is `true` but there are no columns to update in the table (alexkart)\n- Bug #17507: Fix regular expression escaping and simplify condition in `Controller::createAction()` (kamarton)\n- Bug #17511: Fix IPv6 subnets matching in `IpHelper::inRange()` (kamarton)\n- Bug #17522: `DbManager::isEmptyUserId()` is now protected (samdark)\n\n\n2.0.25 August 13, 2019\n----------------------\n\n- Bug #15779: If directory path is passed to `FileHelper::unlink()` and directory has files it will not delete files in this directory on Windows now (alexkart)\n- Bug #17223: Fixed detaching a behavior event when it is a Closure instance (GHopperMSK, rob006)\n- Bug #17473: Fixed `SimpleConditionBuilder::build()` when column is not a string (alexkart)\n- Bug #17485: Reverted #17477 (samdark)\n- Bug #17486: Fixed error when using `batch()` without `$db` parameter with MSSQL (alexkart)\n\n\n2.0.24 July 30, 2019\n--------------------\n\n- Bug #10020: Fixed quoting of column names with dots in MSSQL (alexkart)\n- Bug #16796: Fixed addition and removal of table and column comments in MSSQL (sdlins)\n- Bug #17219: Fixed quoting of table names with spaces in MSSQL (alexkart)\n- Bug #17424: Subdomain support for `User::loginRequired` (alex-code)\n- Bug #17437: Fixed generating namespaced migrations (bizley)\n- Bug #17449: Fixed order of SQL column build syntax for MySQL migration (choken)\n- Bug #17457: Fixed `phpTypecast()` for MSSQL (alexkart)\n- Bug #17469: Fixed updating `Yii` logger instance when setting new logger via configuration (samdark)\n\n\n2.0.23 July 16, 2019\n--------------------\n\n- Bug #10023: Fixed MSSQL \"There are no more rows in the active result set\" exception when using `each()` and `batch()` (alexkart)\n- Bug #17395: Fixed issues with actions that contain underscores in their names (alexkart)\n- Bug #17413, #17418, #17426, #17431: Fixed MSSQL tests (alexkart)\n- Bug #17420: Fixed loading of column default values for MSSQL (alexkart)\n- Bug #17435: Fixed `i18n_init` migration for MSSQL (alexkart)\n\n\n2.0.22 July 02, 2019\n--------------------\n\n- Bug #16394: Fixed issues in `migrate/create` when specifying default values with colons and adding multiple columns (alexkart)\n- Bug #17057: Fixed issues with table names that contain special characters or keywords in MSSQL (alexkart)\n- Bug #17325: Fixed \"Cannot drop view\" for MySQL while `migrate/fresh` (alexkart)\n- Bug #17341: Re-added fix for error from yii.activeForm.js in strict mode (mikehaertl)\n- Bug #17384: Fixed SQL error when passing `DISTINCT ON` queries (brandonkelly)\n- Bug #17389: Fixed `UniqueValidator` to work with Active Record having `joinWith()` in its `find()` (garthpmurray)\n- Enh #17382: Added `\\yii\\validators\\DateValidator::$strictDateFormat` to enable strict validation (alexkart)\n- Enh #17396: Added 'invoked by controller' to the debug log message when `\\yii\\base\\Action` is used (alexkart)\n\n\n2.0.21 June 18, 2019\n--------------------\n\n- Bug #16565: Added missing parts of the context message in `\\yii\\log\\Target::collect` (alexkart)\n- Bug #17070: Striped invalid character from fallback file name in `Content-Disposition` header when using `\\yii\\web\\Response::sendFile` (alexkart)\n- Bug #17332: Trigger 'change' for checkboxes in GridView (andrii-borysov-me)\n- Bug #17341: Fixed error from yii.activeForm.js in strict mode (mikehaertl)\n- Bug #17341: Allowed callable objects to be set to `\\yii\\filters\\AccessRule::$roleParams` (alexkart)\n- Bug #17356: MSSQL Schema was not detecting string field size (ricarnevale, sdlins)\n- Enh #17344: Improved performance of `yii\\db\\Connection::addSelect()` (brandonkelly)\n- Enh #17345: Improved performance of `yii\\db\\Connection::quoteColumnName()` (brandonkelly)\n- Enh #17348: Improved performance of `yii\\db\\Connection::quoteTableName()` (brandonkelly)\n- Enh #17353: Added `sameSite` support for `yii\\web\\Cookie` and `yii\\web\\Session::cookieParams` (rhertogh)\n\n\n2.0.20 June 04, 2019\n--------------------\n\n- Bug #16509: Fixed console command help text wordwrap for multi-byte strings (alexkart)\n- Bug #17299: Fixed adding of input error class in `\\yii\\widgets\\ActiveField::widget` (alexkart)\n- Bug #17328: Added mime aliases for BMP and SVG files (cmoeke)\n- Bug #17336: Fixed wildcard matching in Event::hasHandlers() (samdark)\n- Bug #12080: Fixed afterValidate triggering when any validation occurs (czzplnm)\n\n\n2.0.19 May 21, 2019\n-------------------\n\n- Bug #12077, #12135, #17263: Fixed PostgreSQL version of `alterColumn()` to accept properly `ColumnSchemaBuilder` definition of column (bizley)\n- Bug #16918: Console Table widget variables visibility was changed to protected to allow extending it (samdark)\n- Bug #17233: Fixed bug with integer model attribute names in Validator class (nadar)\n- Bug #17306: Added \".mjs\" extensions to mimetypes meta (samdark)\n- Bug #17313: Support jQuery 3.4 (samdark)\n\n\n2.0.18 April 23, 2019\n---------------------\n\n- Bug #16589: Fixed not using `defaultValue` in `BlameableBehavior` for console app (evil1)\n- Bug #16820: `yii\\filters\\Cors::prepareHeaders()` now accepts Access-Control-Allow-Headers in preflight response (georgezim85)\n- Bug #17220: Fixed error when using non-InputWidget in active form field (s1lver)\n- Bug #17235: `yii\\helpers\\FileHelper::normalizePath()` now accepts stream wrappers (razvanphp)\n- Bug #17268: Fixed Formatter didn't take power into account (samdark)\n\n\n2.0.17 March 22, 2019\n---------------------\n\n- Bug #9438, #13740, #15037: Handle DB session callback custom fields before session closed (lubosdz)\n- Bug #16158: Fix multiple select validation was trigged on other fields blur event (GHopperMSK)\n- Bug #16335: Fixed in `yii\\filters\\AccessRule::matchIP()` user IP validation with netmask in rule (omentes)\n- Bug #16681: `ActiveField::inputOptions` were not used during some widgets rendering (GHopperMSK)\n- Bug #17083: Fixed `yii\\validators\\EmailValidator::$checkDNS` tells that every domain is correct on alpine linux (mikk150)\n- Bug #17124: Fixed ErrorException when run `./yii fixture/unload` without arguments (ricpelo)\n- Bug #17127: `yii\\db\\ActiveRecord::findOne()` now accepts table aliases (albertborsos)\n- Bug #17133: Fixed aliases rendering during help generation for a console command (GHopperMSK)\n- Bug #17152: Fixed error page when using traceline option (asamats)\n- Bug #17156: Fixes PHP 7.2 warning when a data provider has no data as a parameter for a GridView (evilito)\n- Bug #17180: Do not populate `yii\\web\\Response::$response` when response code is 204 (mikk150)\n- Bug #17185: Fixed `AssetManager` timestamp appending when a file is published manually (GHopperMSK)\n- Bug #17215: Improved security for servers running PHP 7.0.0+ (brandonkelly)\n\n\n2.0.16.1 February 28, 2019\n--------------------------\n\n- Bug #17089: Fixed caching of related records when `via()` using with callable (rugabarbo)\n- Bug #17094: Fixed response on 204 status. Now it is empty (GHopperMSK)\n- Bug #17098: Fixed message/extract when using message params returned from method calls (rugabarbo)\n- Bug #17150: Fixed `yii\\helpers\\BaseInflector::camel2words()` splitting `ALLCAPS` words on each letter (brandonkelly)\n- Bug #17093: Fixed regression in `DataProvider::totalCount` (samdark)\n\n\n2.0.16 January 30, 2019\n-----------------------\n\n- Bug #5341: HasMany via two relations (shirase, cebe)\n- Bug #10843: Additional hidden input rendered by `yii\\helpers\\BaseHtml` methods inherits `disabled` HTML option if provided and set to `true` (bizley)\n- Bug #11960: Fixed `checked` option ignore in `yii\\helpers\\BaseHtml::checkbox()` (misantron)\n- Bug #13932: Fix number validator attributes comparison (uaoleg, s1lver)\n- Bug #13977: Skip validation if file input does not exist (RobinKamps, s1lver)\n- Bug #14039, #16636: Fixed validation for disabled inputs (s1lver, omzy83)\n- Bug #14230: Fixed `itemsOptions` ignored in `checkBoxList` and `radioList` (s1lver)\n- Bug #14230: Fixed `itemsOptions` ignored in `checkBoxList` (s1lver)\n- Bug #14368: Added `role` attribute for active radio list (s1lver)\n- Bug #14636: Views can now use relative paths even when using themed views (sammousa)\n- Bug #14660: Fixed `yii\\caching\\DbCache` concurrency issue when set values with the same key (rugabarbo)\n- Bug #14759: Fixed `yii\\web\\JsonResponseFormatter` output for `null` data (misantron)\n- Bug #14901: Fixed trim validation for radio/checkbox button (s1lver)\n- Bug #14950: Fixed `yii\\i18n\\Formatter` methods `asInteger`, `asDecimal`, `asPercent`, and `asCurrency` outputs for very big numbers (bizley)\n- Bug #15117: Fixed `yii\\db\\Schema::getTableMetadata` cache refreshing (boboldehampsink)\n- Bug #15167: Fixed loading of default value `current_timestamp()` for MariaDB >= 10.2.3 (rugabarbo, bloodrain777, Skinka)\n- Bug #15204: `yii\\helpers\\BaseInflector::slug()` is not removing substrings matching provided replacement from given string anymore (bizley)\n- Bug #15286: Fixed incorrect formatting of time with timezone information (rugabarbo)\n- Bug #15482: AR::find()->with() missing data when using string identifiers for relations (rugabarbo)\n- Bug #15528: Fix timestamp formatting to always use decimal notation in `yii\\log\\Target::getTime()` (rob006)\n- Bug #15548: Fixed index names collision in RBAC (gonimar)\n- Bug #15683: Fixed file as array uploading in MultipartFormDataParser (Groonya)\n- Bug #15791: Added a warning when the form names conflict (s1lver, rustamwin)\n- Bug #15798: Fixed render `yii\\grid\\RadioButtonColumn::$content` and `yii\\grid\\CheckboxColumn::$content` (lesha724)\n- Bug #15802: Fixed exception class in yii\\di\\Container (vuchastyi, developeruz)\n- Bug #15826: Fixed JavaScript compareValidator in `yii.validation.js` for attributes not in rules (mgrechanik)\n- Bug #15850: check basePath is writable on publish in AssetManager (Groonya)\n- Bug #15875: afterSave for new models flushes unsaved data (shirase)\n- Bug #15876: `yii\\db\\ActiveQuery::viaTable()` now throws `InvalidConfigException`, if query is not prepared correctly (silverfire)\n- Bug #15889: Fixed override `yii\\helpers\\Html::setActivePlaceholder` (lesha724)\n- Bug #15931: `yii\\db\\ActiveRecord::findOne()` now accepts quoted table and column names using curly and square braces respectively (silverfire)\n- Bug #15988: Fixed bash completion (alekciy)\n- Bug #16006: Handle case when `X-Forwarded-Host` header have multiple hosts separated with a comma (pgaultier)\n- Bug #16010: Fixed `yii\\filters\\ContentNegotiator` behavior when GET parameters contain an array (rugabarbo)\n- Bug #16022: Fix UniqueValidator for PostgreSQL. Checks the uniqueness of keys in `jsonb` field (lav45)\n- Bug #16028: Fix serialization of complex cache keys that contain non-UTF sequences (rugabarbo)\n- Bug #16039: Fixed implicit conversion from `char` to `varbinnary` in MSSQL (vsivsivsi)\n- Bug #16068: Fixed `yii\\web\\CookieCollection::has` when an expiration param is set to 'until the browser is closed' (OndrejVasicek)\n- Bug #16073: Fixed regression in Oracle `IN` condition builder for more than 1000 items (cebe)\n- Bug #16081: Fixed composite IN using just one column (rugabarbo)\n- Bug #16091: Make `yii\\test\\InitDbFixture` work with non-SQL DBMS (cebe)\n- Bug #16101: Fixed Error Handler to clear registered meta tags, link tags, css/js scripts and files in error view (bizley)\n- Bug #16104: Fixed `yii\\db\\pgsql\\QueryBuilder::dropIndex()` to prepend index name with schema name (wapmorgan)\n- Bug #16120: FileCache: rebuild cache file before touch when different file owner (Slamdunk)\n- Bug #16183: Fixed when `yii\\helpers\\BaseFileHelper` sometimes returned wrong value (samdark, SilverFire, OndrejVasicek)\n- Bug #16184: Fixed `yii\\base\\Widget` to access `stack` property with `self` instead of `static` (yanggs07)\n- Bug #16193: Fixed `yii\\filters\\Cors` to not reflect origin header value when configured to wildcard origins (Jianjun Chen)\n- Bug #16217: Fixed `yii\\console\\controllers\\HelpController` to work well in Windows environment (samdark)\n- Bug #16245: Fixed `__isset()` in `BaseActiveRecord` not catching errors (sammousa)\n- Bug #16252: Fixed `yii\\base\\DynamicModel` for checking exist property (vuongxuongminh)\n- Bug #16253: Fixed empty checkboxlist validation (GHopperMSK)\n- Bug #16266: Fixed `yii\\helpers\\BaseStringHelper` where explode would not allow 0 as trim string (Thoulah)\n- Bug #16277: Fixed `yii\\db\\Query::from()` to respect `yii\\db\\ExpressionInterface` (noname007)\n- Bug #16278: Fixed drop existing views when console `migrate/fresh` command runs (developeruz)\n- Bug #16280: Fixed `yii\\base\\Model::getActiveValidators()` to return correct validators for attribute on scenario (paweljankowiak06)\n- Bug #16292: Fixed misconfigured CORS filter exception throwing. Now it throws `InvalidConfigException` in Debug mode (khvalov)\n- Bug #16301: Fixed `yii\\web\\User::setIdentity()` to clear access check cache while setting identity object to `null` (Izumi-kun)\n- Bug #16322: Fixed strings were not were not compared using timing attack resistant approach while CSRF token validation (samdark, Felix Wiedemann)\n- Bug #16331: Fixed console table without headers (rhertogh)\n- Bug #16377: Fixed `yii\\base\\Event:off()` undefined index error when event handler does not match (razvanphp)\n- Bug #16424: `yii\\db\\Transaction::begin()` throws now `NotSupportedException` for nested transaction and DBMS not supporting savepoints (bizley)\n- Bug #16425: Check for additional values for disabled confirm dialog (Alex-Code, s1lver)\n- Bug #16469: Allow cache to be specified as interface and to be configured in DI container (alexeevdv)\n- Bug #16490: Fix schema on rbac init (marcelodeandrade)\n- Bug #16514: Fixed `yii\\di\\Container::resolveCallableDependencies` to support callable object (wi1dcard)\n- Bug #16527: Fixed return content for `\\yii\\widgets\\ActiveForm::run()` (carono)\n- Bug #16552: Added check in `yii\\db\\ActiveQuery::prepare()` to prevent populating already populated relation when another relation is requested with `via` (drlibra)\n- Bug #16558: Added cloning `yii\\data\\ActiveDataProvider::query` property when ActiveDataProvider object is cloned (mgrechanik)\n- Bug #16580: Delete unused php message files in MessageController if `$removeUnused` option is on (Groonya)\n- Bug #16648: Html::strtolower() was corrupting UTF-8 strings (Kolyunya)\n- Bug #16657: Ensure widgets after run event result contains the result of the rendered widget (AdeAttwood)\n- Bug #16666: Fixed `yii\\helpers\\ArrayHelper::merge` (rustamwin)\n- Bug #16680: Fixed ActiveField 'text' input with maxlength (s1lver)\n- Bug #16687: Add missing translations for `nl-NL` durations used in `yii\\i18n\\Formatter::asDuration()` (alexeevdv)\n- Bug #16716: The ability to filter by pressing the Enter key when the option `$filterOnFocusOut` off (s1lver)\n- Bug #16748: Fixed params when normalize data (marcelodeandrade)\n- Bug #16752: Fix rotating files under Windows (samdark, nadirvishun)\n- Bug #16766: `yii\\filters\\ContentNegotiator` was not setting `Vary` header to inform cache recipients (koteq, cebe, samdark)\n- Bug #16822: Create config dir recursively in message/config (Groonya)\n- Bug #16828: `yii\\console\\controllers\\MessageController::translator` recognized object' methods and functions calls as identical sets of tokens (erickskrauch)\n- Bug #16836: Fix `yii\\mutex\\MysqlMutex` to handle locks with names longer than 64 characters (rob006)\n- Bug #16838: `yii\\mutex\\Mutex::acquire()` no longer returns `true` if lock is already acquired by the same component in the same process (rob006)\n- Bug #16858: Allow `\\yii\\console\\widgets\\Table` to render empty table when headers provided but no columns (damiandziaduch)\n- Bug #16891: Fixed Pagination::totalCount initialized incorrectly (taobig)\n- Bug #16897: Fixed `yii\\db\\sqlite\\Schema` missing primary key constraint detection in case of `INTEGER PRIMARY KEY` (bizley)\n- Bug #16903: Fixed 'yii\\validators\\NumberValidator' method 'isNotNumber' returns false for true/false value (annechko)\n- Bug #16910: Fix messages sorting on extract (Groonya)\n- Bug #16945: Fixed RBAC DbManager ruleName fetching on the case of PDO::ATTR_ORACLE_NULLS => PDO::NULL_TO_STRING (razonyang)\n- Bug #16959: Fixed typo in if condition inside `yii\\web\\DbSession::typecastFields()` that caused problems with session overwriting (silverfire)\n- Bug #16966: Fix ArrayExpression support in related tables (GHopperMSK)\n- Bug #16969: Fix `yii\\filters\\PageCache` incorrectly storing empty data in some cases (sammousa)\n- Bug #16974: Regular Expression Validator to include support for 'u' (UTF-8) modifier (Dzhuneyt)\n- Bug #16991: Removed usage of `utf8_encode()` from `Request::resolvePathInfo()` (GHopperMSK)\n- Bug #17021: Fix to do not remove existing message category files in a subfolder (albertborsos)\n- Bug: Fixed bad instanceof check in `yii\\db\\Schema::getTableMetadata()` (samdark)\n- Bug: (CVE-2018-14578): Fixed CSRF token check bypassing in `\\yii\\web\\Request::getMethod()` (silverfire)\n- Bug: (CVE-2018-19454): Fixed excess logging of sensitive information in `\\yii\\log\\Target` (silverfire)\n- Enh #9133: Added `yii\\behaviors\\OptimisticLockBehavior` (tunecino)\n- Enh #14289: Added `yii\\db\\Command::executeResetSequence()` to work with Oracle (CedricYii)\n- Enh #14367: In `yii\\db\\mysql\\QueryBuilder` added support fractional seconds for time types for MySQL >= 5.6.4 (konstantin-vl)\n- Enh #16151: `ActiveQuery::getTableNameAndAlias()` is now protected (s1lver)\n- Enh #16151: Change of scope for method `getTableNameAndAlias()` (s1lver)\n- Enh #16191: Enhanced `yii\\helpers\\Inflector` to work correctly with UTF-8 (silverfire)\n- Enh #16365: Added $filterOnFocusOut option for GridView (s1lver)\n- Enh #16522: Allow jQuery 3.3 (Slamdunk)\n- Enh #16603: Added `yii\\mutex\\FileMutex::$isWindows` for Windows file shares on Unix guest machines (brandonkelly)\n- Enh #16839: Increase frequency of lock tries for `yii\\mutex\\FileMutex::acquireLock()` when $timeout is provided (rob006)\n- Enh #16839: Add support for `$timeout` in  `yii\\mutex\\PgsqlMutex::acquire()` (rob006)\n- Enh: `yii\\helpers\\UnsetArrayValue`, `yii\\helpers\\ReplaceArrayValue` object now can be restored after serialization using `var_export()` function (silvefire)\n- Chg #16192: `yii\\db\\Command::logQuery()` is now protected, extracted `getCacheKey()` from `queryInternal()` (drlibra)\n- Chg #16941: Set `yii\\console\\controllers\\MigrateController::useTablePrefix` to true as default value (GHopperMSK)\n\n\n2.0.15.1 March 21, 2018\n-----------------------\n\n- Bug #15931: `yii\\db\\ActiveRecord::findOne()` now accepts column names prefixed with table name (cebe)\n\n\n2.0.15 March 20, 2018\n---------------------\n\n- Bug #15688: (CVE-2018-7269): Fixed possible SQL injection through `yii\\db\\ActiveRecord::findOne()`, `::findAll()` (analitic1983, silverfire, cebe)\n- Bug #15878: Fixed migration with a comment containing an apostrophe (MarcoMoreno)\n\n\n2.0.14.2 March 13, 2018\n-----------------------\n\n- Bug #15776: Fixed slow MySQL constraints retrieving (MartijnHols, berosoboy, sergeymakinen)\n- Bug #15783: Regenerate CSRF token only when logging in directly (samdark)\n- Bug #15792: Added missing `yii\\db\\QueryBuilder::conditionClasses` setter (silverfire)\n- Bug #15801: Fixed `has-error` CSS class assignment in `yii\\widgets\\ActiveField` when attribute name is prefixed with tabular index (FabrizioCaldarelli)\n- Bug #15804: Fixed `null` values handling for PostgresSQL arrays (silverfire)\n- Bug #15817: Fixed support of deprecated array format type casting in `yii\\db\\Command::bindValues()` (silverfire)\n- Bug #15822: Fixed `yii\\base\\Component::off()` not to throw an exception when handler does not exist (silverfire)\n- Bug #15829: Fixed JSONB support in PostgreSQL 9.4 (silverfire)\n- Bug #15836: Fixed nesting of `yii\\db\\ArrayExpression`, `yii\\db\\JsonExpression` (silverfire)\n- Bug #15839: Fixed `yii\\db\\mysql\\JsonExpressionBuilder` to cast JSON explicitly (silverfire)\n- Bug #15840: Fixed regression on load fixture data file (leandrogehlen)\n- Bug #15858: Fixed `Undefined offset` error calling `yii\\helpers\\Html::errorSummary()` with the same error messages for different model attributes (FabrizioCaldarelli, silverfire)\n- Bug #15863: Fixed saving of `null` attribute value for JSON and Array columns in MySQL and PostgreSQL (silverfire)\n- Bug: Fixed encoding of empty `yii\\db\\ArrayExpression` for PostgreSQL (silverfire)\n- Bug: Fixed table schema retrieving for PostgreSQL when the table name was wrapped in quotes (silverfire)\n\n\n2.0.14.1 February 24, 2018\n--------------------------\n\n- Bug #15318: Fixed `session_name(): Cannot change session name when session is active` errors (bscheshirwork, samdark)\n- Bug #15678: Fixed `resetForm()` method in `yii.activeForm.js` which used an undefined variable (Izumi-kun)\n- Bug #15692: Fix `yii\\validators\\ExistValidator` to respect filter when `targetRelation` is used (developeruz)\n- Bug #15693: Fixed `yii\\filters\\auth\\HttpHeaderAuth` to work correctly when pattern is set but was not matched (bboure)\n- Bug #15696: Fix magic getter for `yii\\db\\ActiveRecord` (developeruz)\n- Bug #15707: Fixed JSON retrieving from MySQL (silverfire)\n- Bug #15708: Fixed `yii\\db\\Command::upsert()` for Cubrid/MSSQL/Oracle (sergeymakinen)\n- Bug #15724: Changed shortcut in `yii\\console\\controllers\\BaseMigrateController` for `comment` option from `-c` to `-C` due to conflict (Izumi-kun)\n- Bug #15726: Fix ExistValidator is broken for NOSQL (developeruz)\n- Bug #15728, #15731: Fixed BC break in `Query::select()` method (silverfire)\n- Bug #15742: Updated `yii\\helpers\\BaseHtml::setActivePlaceholder()` to be consistent with `activeLabel()` (edwards-sj)\n- Enh #15716: Added `disableJsonSupport` to MySQL and PgSQL `ColumnSchema`, `disableArraySupport` and `deserializeArrayColumnToArrayExpression` to PgSQL `ColumnSchema` (silverfire)\n- Enh #15716: Implemented `\\Traversable` in `yii\\db\\ArrayExpression` (silverfire)\n- Enh #15760: Added `ArrayAccess` support as validated value in `yii\\validators\\EachValidator` (silverfire)\n\n\n2.0.14 February 18, 2018\n------------------------\n\n- Bug #8983: Only truncate the original log file for rotation (matthewyang, developeruz)\n- Bug #9342: Fixed `yii\\db\\ActiveQueryTrait` to apply `indexBy` after relations population in order to prevent excess queries (sammousa, silverfire)\n- Bug #11401: Fixed `yii\\web\\DbSession` concurrency issues when writing and regenerating IDs (samdark, andreasanta, cebe)\n- Bug #13034: Fixed `normalizePath` for windows network shares that start with two backslashes (developeruz)\n- Bug #14135: Fixed `yii\\web\\Request::getBodyParam()` crashes on object type body params (klimov-paul)\n- Bug #14157: Add support for loading default value `CURRENT_TIMESTAMP` of MySQL `datetime` field (rossoneri)\n- Bug #14276: Fixed I18N format with dotted parameters (developeruz)\n- Bug #14296: Fixed log targets to throw exception in case log can not be properly exported (bizley)\n- Bug #14484: Fixed `yii\\validators\\UniqueValidator` for target classes with a default scope (laszlovl, developeruz)\n- Bug #14604: Fixed `yii\\validators\\CompareValidator` `compareAttribute` does not work if `compareAttribute` form ID has been changed (mikk150)\n- Bug #14711: (CVE-2018-6010): Fixed `yii\\web\\ErrorHandler` displaying exception message in non-debug mode (samdark)\n- Bug #14811: Fixed `yii\\filters\\HttpCache` to work with PHP 7.2 (samdark)\n- Bug #14859: Fixed OCI DB `defaultSchema` failure when `masterConfig` is used (lovezhl456)\n- Bug #14903: Fixed route with extra dashes is executed controller while it should not (developeruz)\n- Bug #14916: Fixed `yii\\db\\Query::each()` iterator key starts from 1 instead of 0 (Vovan-VE)\n- Bug #14980: Fix looping in `yii\\i18n\\MessageFormatter` tokenize pattern if pattern is invalid (uaoleg, developeruz)\n- Bug #15031: Fixed incorrect string type length detection for OCI DB schema (Murolike)\n- Bug #15046: Throw an `yii\\web\\HeadersAlreadySentException` if headers were sent before web response (dmirogin)\n- Bug #15122: Fixed `yii\\db\\Command::getRawSql()` to properly replace expressions (hiscaler, samdark)\n- Bug #15142: Fixed array params replacing in `yii\\helpers\\BaseUrl::current()` (IceJOKER)\n- Bug #15169: Fixed translating a string when NULL parameter is passed (developeruz)\n- Bug #15194: Fixed `yii\\db\\QueryBuilder::insert()` to preserve passed params when building a `INSERT INTO ... SELECT` query for MSSQL, PostgreSQL and SQLite (sergeymakinen)\n- Bug #15229: Fixed `yii\\console\\widgets\\Table` default value for `getScreenWidth()`, when `Console::getScreenSize()` can't determine screen size (webleaf)\n- Bug #15234: Fixed `\\yii\\widgets\\LinkPager` removed `tag` from `disabledListItemSubTagOptions` (SDKiller)\n- Bug #15249: Controllers in subdirectories were not visible in commands list (IceJOKER)\n- Bug #15270: Resolved potential race conditions when writing generated php-files (kalessil)\n- Bug #15300: Fixed \"Cannot read property 'style' of undefined\" error at the error screen (vitorarantes)\n- Bug #15301: Fixed `ArrayHelper::filter()` to work properly with `0` in values (hhniao)\n- Bug #15302: Fixed `yii\\caching\\DbCache` so that `getValues` now behaves the same as `getValue` with regards to streams (edwards-sj)\n- Bug #15317: Regenerate CSRF token if an empty value is given (sammousa)\n- Bug #15320: Fixed special role checks in `yii\\filters\\AccessRule::matchRole()` (Izumi-kun)\n- Bug #15322: Fixed PHP 7.2 compatibility of `FileHelper::getExtensionsByMimeType()` (samdark)\n- Bug #15353: Remove side effect of ActiveQuery::getTablesUsedInFrom() introduced in 2.0.13 (terales)\n- Bug #15355: Fixed `yii\\db\\Query::from()` does not work with `yii\\db\\Expression` (vladis84, silverfire, samdark)\n- Bug #15356: Fixed multiple bugs in `yii\\db\\Query::getTablesUsedInFrom()` (vladis84, samdark)\n- Bug #15380: `FormatConverter::convertDateIcuToPhp()` now converts `a` ICU symbols to `A` (brandonkelly)\n- Bug #15407: Fixed rendering rows with associative arrays in `yii\\console\\widgets\\Table` (dmrogin)\n- Bug #15432: Fixed wrong value being set in `yii\\filters\\RateLimiter::checkRateLimit()` resulting in wrong `X-Rate-Limit-Reset` header value (bizley)\n- Bug #15440: Fixed `yii\\behaviors\\AttributeTypecastBehavior::$attributeTypes` auto-detection fails for rule, which specify attribute with '!' prefix (klimov-paul)\n- Bug #15462: Fixed `accessChecker` configuration error (developeruz)\n- Bug #15494: Fixed missing `WWW-Authenticate` header (developeruz)\n- Bug #15522: Fixed `yii\\db\\ActiveRecord::refresh()` method does not use an alias in the condition (vladis84)\n- Bug #15523: `yii\\web\\Session` settings could now be configured after session is started (StalkAlex, rob006, daniel1302, samdark)\n- Bug #15536: Fixed `yii\\widgets\\ActiveForm::init()` for call `parent::init()` (panchenkodv)\n- Bug #15540: Fixed `yii\\db\\ActiveRecord::with()` unable to use relation defined via attached behavior in case `asArray` is enabled (klimov-paul)\n- Bug #15553: Fixed `yii\\validators\\NumberValidator` incorrectly validate resource (developeruz)\n- Bug #15621: Fixed `yii\\web\\User::getIdentity()` returning `null` if an exception had been thrown when it was called previously (brandonkelly)\n- Bug #15628: Fixed `yii\\validators\\DateValidator` to respect time when the `format` property is set to UNIX Epoch format (silverfire, gayHacker)\n- Bug #15644: Avoid wrong default selection on a dropdown, checkbox list, and radio list, when a option has a key equals to zero (berosoboy)\n- Bug #15658: Fixed `yii\\filters\\auth\\HttpBasicAuth` not to switch identity, when user is already authenticated and identity does not get changed (silverfire)\n- Bug #15662: Fixed `yii\\log\\FileTarget` not to create log directory during init process (alexeevdv)\n- Enh #3087: Added `yii\\helpers\\BaseHtml::error()` \"errorSource\" option to be able to customize errors display (yanggs07, developeruz, silverfire)\n- Enh #3250: Added support for events partial wildcard matching (klimov-paul)\n- Enh #5515: Added default value for `yii\\behaviors\\BlameableBehavior` for cases when the user is guest (dmirogin)\n- Enh #6844: `yii\\base\\ArrayableTrait::toArray()` now allows recursive `$fields` and `$expand` (bboure)\n- Enh #7640: Implemented custom data types support. Added JSON support for MySQL and PostgreSQL, array support for PostgreSQL (silverfire, cebe)\n- Enh #7988: Added `\\yii\\helpers\\Console::errorSummary()` and `\\yii\\helpers\\Json::errorSummary()` (developeruz)\n- Enh #7996: Short syntax for verb in GroupUrlRule (schojniak, developeruz)\n- Enh #8092: ExistValidator for relations (developeruz)\n- Enh #8527: Added `yii\\i18n\\Locale` component having `getCurrencySymbol()` method (amarox, samdark)\n- Enh #8752: Allow specify `$attributeNames` as a string for `yii\\base\\Model` `validate()` method (developeruz)\n- Enh #9137: Added `Access-Control-Allow-Method` header for the OPTIONS request (developeruz)\n- Enh #9253: Allow `variations` to be a string for `yii\\filters\\PageCache` and `yii\\widgets\\FragmentCache` (schojniak, developeruz)\n- Enh #9771: Assign hidden input with its own set of HTML options via `$hiddenOptions` in activeFileInput `$options` (HanafiAhmat)\n- Enh #10186: Use native `hash_equals` in `yii\\base\\Security::compareString()` if available, throw exception if non-strings are compared (aotd1, samdark)\n- Enh #11611: Added `BetweenColumnsCondition` to build SQL condition like `value BETWEEN col1 and col2` (silverfire)\n- Enh #12623: Added `yii\\helpers\\StringHelper::matchWildcard()` replacing usage of `fnmatch()`, which may be unreliable (klimov-paul)\n- Enh #13019: Support JSON in SchemaBuilderTrait (zhukovra, undefinedor)\n- Enh #13425: Added caching of dynamically added URL rules with `yii\\web\\UrlManager::addRules()` (scriptcube, silverfire)\n- Enh #13465: Added `yii\\helpers\\FileHelper::findDirectories()` method (ArsSirek, developeruz)\n- Enh #13618: Active Record now resets related models after corresponding attributes updates (Kolyunya, rob006)\n- Enh #13679: Added `yii\\behaviors\\CacheableWidgetBehavior` (Kolyunya)\n- Enh #13814: MySQL unique index names can now contain spaces (df2)\n- Enh #13879: Added upsert support for `yii\\db\\QueryBuilder`, `yii\\db\\Command`, and `yii\\db\\Migration` (sergeymakinen)\n- Enh #13919: Added option to add comment for created table to migration console command (mixartemev, developeruz)\n- Enh #13996: Added `yii\\web\\View::registerJsVar()` method that allows registering JavaScript variables (Eseperio, samdark)\n- Enh #14043: Added `yii\\helpers\\IpHelper` (silverfire, cebe)\n- Enh #14254: add an option to specify whether validator is forced to always use master DB for `yii\\validators\\UniqueValidator` and `yii\\validators\\ExistValidator` (rossoneri, samdark)\n- Enh #14355: Added ability to pass an empty array as a parameter in console command (developeruz)\n- Enh #14488: Added support for X-Forwarded-Host to `yii\\web\\Request`, fixed `getServerPort()` usage (si294r, samdark)\n- Enh #14538: Added `yii\\behaviors\\AttributeTypecastBehavior::typecastAfterSave` property (littlefuntik, silverfire)\n- Enh #14546: Added `dataDirectory` property into `BaseActiveFixture` (leandrogehlen)\n- Enh #14568: Refactored migration templates to use `safeUp()` and `safeDown()` methods (Kolyunya)\n- Enh #14638: Added `yii\\db\\SchemaBuilderTrait::tinyInteger()` (rob006)\n- Enh #14643: Added `yii\\web\\ErrorAction::$layout` property to conveniently set layout from error action config (swods, cebe, samdark)\n- Enh #14662: Added support for custom `Content-Type` specification to `yii\\web\\JsonResponseFormatter` (Kolyunya)\n- Enh #14732, #11218, #14810, #10855: It is now possible to pass `yii\\db\\Query` anywhere, where `yii\\db\\Expression` was supported (silverfire)\n- Enh #14806: Added $placeFooterAfterBody option for GridView (terehru)\n- Enh #15024: `yii\\web\\Pjax` widget does not prevent CSS files from sending anymore because they are handled by client-side plugin correctly (onmotion)\n- Enh #15047: `yii\\db\\Query::select()` and `yii\\db\\Query::addSelect()` now check for duplicate column names (wapmorgan)\n- Enh #15076: Improve `yii\\db\\QueryBuilder::buildColumns()` to throw exception on invalid input (hiscaler)\n- Enh #15120: Refactored dynamic caching introducing `DynamicContentAwareInterface` and `DynamicContentAwareTrait` (sergeymakinen)\n- Enh #15135: Automatic completion for help in bash and zsh (Valkeru)\n- Enh #15216: Added `yii\\web\\ErrorHandler::$traceLine` to allow opening file at line clicked in IDE (vladis84)\n- Enh #15219: Added `yii\\filters\\auth\\HttpHeaderAuth` (bboure)\n- Enh #15221: Added support for specifying `--camelCase` console options in `--kebab-case` (brandonkelly)\n- Enh #15221: Added support for the `--<option> <value>` console option syntax (brandonkelly)\n- Enh #15221: Improved the `help/list-action-options` console command output for command options without a description (brandonkelly)\n- Enh #15226: Auto generate placeholder from fields (vladis84)\n- Enh #15272: Removed type attribute from script tag (aleksbelic)\n- Enh #15332: Always check for availability of `openssl_pseudo_random_bytes`, even if LibreSSL is available (sammousa)\n- Enh #15335: Added `FileHelper::unlink()` that works well under all OSes (samdark)\n- Enh #15340: Test CHANGELOG.md for valid format (sammousa)\n- Enh #15347: Add `Instance` support for object property in DI container (kojit2009)\n- Enh #15357: Added multi statement support for `yii\\db\\sqlite\\Command` (sergeymakinen)\n- Enh #15360: Refactored `BaseConsole::updateProgress()` (developeruz)\n- Enh #15398: Added `yii\\db\\Query::cache()` (hubeiwei, silverfire)\n- Enh #15415: Added transaction/retry support for `yii\\db\\Command` (sergeymakinen)\n- Enh #15417: Added `yii\\validators\\FileValidator::$minFiles` (vladis84)\n- Enh #15422: Added default roles dynamic definition support via closure for `yii\\rbac\\BaseManager` (deltacube)\n- Enh #15426: Added abilitiy to create and drop database views (igravity, vladis84)\n- Enh #15476: Added `\\yii\\widgets\\ActiveForm::$validationStateOn` to be able to specify where to add class for invalid fields (samdark)\n- Enh #15496: (CVE-2018-6009): CSRF token is now regenerated on changing identity (samdark, rhertogh)\n- Enh #15595: `yii\\data\\DataFilter` can now handle `lt`,`gt`,`lte` and `gte` on `yii\\validators\\DateValidator` (mikk150)\n- Enh #15661: Added `yii\\db\\ExpressionInterface` support to `yii\\db\\Command::batchInsert()` (silverfire)\n- Enh: Added check to `yii\\base\\Model::formName()` to prevent source path disclosure when form is represented by an anonymous class (silverfire)\n- Chg #15420: Handle OPTIONS request in `yii\\filter\\Cors` so the preflight check isn't passed trough authentication filters (michaelarnauts, leandrogehlen)\n- Chg #15625: `yii\\grid\\DataColumn` boolean filter dropdown list values are now in reversed order (bizley)\n- Chg #15633: Deprecated `yii\\base\\BaseObject::className()` in favor of native PHP syntax `::class`, which does not trigger autoloading (brandonkelly)\n- Chg #15633: Deprecated XCache and Zend data cache support as caching backends (brandonkelly)\n- Chg #15633: Deprecated `yii\\BaseYii::powered()` method (brandonkelly)\n- Chg #15633: Added `yii\\base\\InvalidArgumentException` and deprecated `yii\\base\\InvalidParamException` (brandonkelly)\n- Chg #15633: Added `yii\\BaseYii::debug()` and deprecated `yii\\BaseYii::trace()` (brandonkelly)\n\n\n2.0.13.1 November 14, 2017\n--------------------------\n\n- Bug #15081: Fixed \"Undefined offset: 1\" in log Target (ischenko)\n- Bug #15086: Fixed jQuery onLoad event handling (alexantr)\n- Bug #15108: Fixed `yii\\db\\Schema::getSchemaNames()` for MSSQL and added tests for all DBMSes (sergeymakinen)\n- Bug #15117: Fixed DB schema cache did not honor table prefixes (sergeymakinen)\n\n\n2.0.13 November 03, 2017\n------------------------\n\n- Bug #6226: Fix fatal symlink error during assets publishing in multi threaded environment (dynasource)\n- Bug #6526: Fixed `yii\\db\\Command::batchInsert()` casting of double values correctly independent of the locale (cebe, leammas)\n- Bug #6588: Fixed changing array keys after validation of multiple files in `yii\\validators\\FileValidator` (developeruz)\n- Bug #7890: Allow `migrate/mark` to mark history at the point of the base migration (cebe)\n- Bug #11242: Fixed excess escaping in `yii\\db\\Command::batchInsert()` (silverfire)\n- Bug #11825: User can login by cookie only once when `autoRenewCookie` is set to false (shirase, silverfire)\n- Bug #12860: Fixed possible race conditions in `yii\\mutex\\FileMutex` (kidol)\n- Bug #13258: Fixed `yii\\mutex\\FileMutex::$autoRelease` having no effect due to missing base class initialization (kidol)\n- Bug #13436: Fixed migration for MSSQL DbSession (silverfire)\n- Bug #13564: Fixed `yii\\web\\Request::getAuthUser()`, `getAuthPassword()` to respect `HTTP_AUTHORIZATION` request header (silverfire)\n- Bug #13720: Improve `yii\\helpers\\FormatConverter::convertDatePhpToIcu()` to handle escaped chars correctly (rob006)\n- Bug #13757: Fixed ambiguous column error in `BaseActiveRecord::refresh()` when the query adds a JOIN by default (cebe, ivankff)\n- Bug #13779: Fixed `yii\\db\\ActiveRecord::joinWith()` unable to use relation defined via attached behavior (ElisDN, klimov-paul)\n- Bug #13859: Fixed ambiguous column error in `Query::column()` when `$indexBy` is used with a JOIN (cebe)\n- Bug #13969: Fixed a bug in a `yii\\console\\controllers\\CacheController` when caches defined via a closure were not detected (Kolyunya)\n- Bug #14016: Fixed empty messages marked as unused in PHP and PO sources when extracted with message command when `markUnused` is `false` (samdark)\n- Bug #14129: Fixed console help to properly work with tricky camelcased controller names (samdark, silverfire)\n- Bug #14134: Fixed multiple `validateAttribute()` calls when `scenarios()` returns duplicate attributes (krukru)\n- Bug #14165: Set `_slave` of `Connection` to `false` instead of `null` in `close` method (rossoneri)\n- Bug #14186: Forced validation in `yiiActiveForm` do not trigger `afterValidate` event (arogachev)\n- Bug #14192: Fixed wrong default null value for TIMESTAMP when using PostgreSQL (Tigrov)\n- Bug #14202: Fixed current time in (UTC) `\\Yii::$app->formatter` if time not set (bscheshirwork)\n- Bug #14206: `MySqlMutex`, `PgsqlMutex` and `OracleMutex` now use `useMaster()` to ensure lock is aquired on the same DB server (cebe, ryusoft)\n- Bug #14248: `yii\\console\\controllers\\MessageController` no longer outputs colorized filenames when console does not support text colorization (PowerGamer1)\n- Bug #14264: Fixed a bug where `yii\\log\\Logger::calculateTimings()` was not accepting messages with array tokens (bizley)\n- Bug #14269: Fixed broken error page when calling an undefined method (cebe)\n- Bug #14304: Fixed `yii\\validators\\UniqueValidator` and `yii\\validators\\ExistValidator` to skip prefixes in case expressions are used (samdark)\n- Bug #14307: Fixed PHP warning when `yii\\console\\UnknownCommandException` is thrown for empty command (rob006)\n- Bug #14318: Trigger `yiiActiveForm.events.afterValidateAttribute` after updating attribute  (dmirogin)\n- Bug #14334: Fixed `\\yii\\db\\QueryBuilder::buildNotCondition` loses params when operand is `\\yii\\db\\Expression` (Ni-san)\n- Bug #14341: Fixed regression in error handling introduced by fixing #14264 (samdark)\n- Bug #14370: Fixed creating built-in validator in model with same function name (dmirogin)\n- Bug #14406: Fixed caching rules in `yii\\web\\UrlManager` with different ruleConfig configuration (dmirogin)\n- Bug #14423: Fixed `ArrayHelper::merge` behavior with null values for integer-keyed elements (dmirogin)\n- Bug #14449: Fix PHP 7.2 compatibility bugs and add explicit closure support in `yii\\base\\Application` (dynasource)\n- Bug #14471: `ContentNegotiator` will always set one of the configured server response formats even if the client does not accept any of them (PowerGamer1)\n- Bug #14492: Fixed error handler not escaping error info in debug mode, see CVE-2017-11516 (samdark)\n- Bug #14493: Fixed getting permissions in `yii\\rbac\\Dbmanger::getPermissionsByUser` by user with id equals 0 (dmirogin)\n- Bug #14510: The state of a form is always \"not validated\" when using forced validation in `yiiActiveForm` (arogachev)\n- Bug #14523: Added `yii\\web\\MultipartFormDataParser::$force` option allowing to enforce parsing even on 'POST' request (klimov-paul)\n- Bug #14525: Fixed 2.0.12 regression of loading of global fixtures trough `yii fixture/load` (michaelarnauts)\n- Bug #14533: Fixed `yii\\validators\\ExistValidator` and `yii\\validators\\UniqueValidator` throw exception in case they are set for `yii\\db\\ActiveRecord` with `$targetClass` pointing to NOSQL ActiveRecord (klimov-paul)\n- Bug #14542: Ensured only ASCII characters are in CSRF cookie value since binary data causes issues with ModSecurity and some browsers (samdark)\n- Bug #14543: Throw exception when trying to create migration longer than 180 symbols (dmirogin, cebe)\n- Bug #14596: Fix event call on init in `yii\\widgets\\BaseListView` (panchenkodv)\n- Bug #14697: Fixed `console\\widgets\\Table` rendering when there's no data supplied (bscheshirwork)\n- Bug #14723: Fixed serialization of `yii\\db\\Connection` instance closes database connection (klimov-paul)\n- Bug #14773: Fixed `yii\\widgets\\ActiveField::$options` does not support 'class' option in array format (klimov-paul)\n- Bug #14902: Fixed PHP notice in `yii\\web\\MultipartFormDataParser` (olimsaidov)\n- Bug #14921: Fixed bug with replacing numeric keys in `yii\\helpers\\Url::current()` (rob006)\n- Enh #4479: Implemented REST filters (klimov-paul)\n- Enh #4495:  Added closure support in `yii\\i18n\\Formatter` (developeruz)\n- Enh #5786: Allowed to use custom constructors in ActiveRecord-based classes (ElisDN, klimov-paul)\n- Enh #6644: Added `yii\\helpers\\ArrayHelper::setValue()` (LAV45)\n- Enh #7823: Added `yii\\filters\\AjaxFilter` filter (dmirogin)\n- Enh #9438: `yii\\web\\DbSession` now relies on error handler to display errors (samdark)\n- Enh #9703, #9709: Added `yii\\i18n\\Formatter::asWeight()` and `::asLength()` formatters (nineinchnick, silverfire)\n- Enh #11415: Added `yii\\console\\widgets\\Table` to draw tables in console apps (pana1990, rob006, samdark, tonykor)\n- Enh #13254: Made `yii\\helpers\\StringHelper` and `yii\\validators\\StringValidator` independent of `Yii::$app` instance (cebe)\n- Enh #13378: Added `yii\\behaviors\\SluggableBehaviour::skipOnEmpty` option (andrewnester)\n- Enh #13403: Added 'permissions' additionally to 'roles' in `yii\\filters\\AccessRule` in order to be able to specify these separately (thyseus)\n- Enh #13486: Use DI container to instantiate cookies in order to be able to set defaults (samdark)\n- Enh #13586: Added `$preserveNonEmptyValues` property to the `yii\\behaviors\\AttributeBehavior` (Kolyunya)\n- Enh #13780: Added support for trusted proxies in `yii\\web\\Request` (sammousa, cebe, silverfire)\n- Enh #13787: Added `yii\\db\\Migration::$maxSqlOutputLength` that allows limiting number of characters for outputting SQL (thiagotalma)\n- Enh #13824: Support extracting concatenated strings in `yii message` (developeruz)\n- Enh #13835: Added `yii\\web\\Request::getOrigin()` method that returns `HTTP_ORIGIN` of current CORS request (yyxx9988)\n- Enh #13853: Added `yii\\db\\Migration::$compact` as well as `yii\\console\\controllers\\BaseMigrateController::$compact` to allow making the migration console output more compact (francislavoie)\n- Enh #14022: `yii\\web\\UrlManager::setBaseUrl()` now supports aliases (dmirogin)\n- Enh #14061: Added request scope assignments cache to `yii\\rbac\\DbManager::checkAccess()` to avoid duplicate queries for user assignments (leandrogehlen, cebe, nineinchnick, ryusoft)\n- Enh #14081: Added `yii\\caching\\CacheInterface` to make custom cache extensions adoption easier (silverfire)\n- Enh #14087: Added `yii\\web\\View::registerCsrfMetaTags()` method that registers CSRF tags dynamically ensuring that caching doesn't interfere (RobinKamps)\n- Enh #14089: Added tests for `yii\\base\\Theme` (vladis84)\n- Enh #14105: Implemented a solution for retrieving DBMS constraints in `yii\\db\\Schema` (sergeymakinen)\n- Enh #14126: Added variadic parameters support to DI container (SamMousa)\n- Enh #14151: Added `yii\\behaviors\\AttributesBehavior` that assigns values specified to one or multiple attributes of an AR object when certain events happen (bscheshirwork)\n- Enh #14184: Module service locator now falls back to its parent module service locator in case component isn't found (SamMousa)\n- Enh #14188: Add constants and function for sysexits(3) to `ConsoleHelper` (tom--, samdark, cebe)\n- Enh #14273: `yii\\log\\Target::$enabled` now supports callable value (dmirogin)\n- Enh #14294: Added `InputWidget::renderInput()` to move behavior described in `InputWidget` class docs to the class itself (cebe)\n- Enh #14298: The default response formatter configs defined by `yii\\web\\Response::defaultFormatters()` now use the array syntax (brandonkelly)\n- Enh #14363: Added `yii\\widgets\\LinkPager::$linkContainerOptions` and possibility to override tag in `yii\\widgets\\LinkPager::$options` (dmirogin)\n- Enh #14389: Optimize `Validator::validateAttributes()` by calling `attributeNames()` only once (nicdnep)\n- Enh #14417: Added configuration for headers in PHP files generated by `message/extract` command (rob006)\n- Enh #14431: Moved `ActiveQuery::getTablesUsedInFrom()` to `Query` to make the functionality available on the lower layer (cebe)\n- Enh #14620: Updated `yii.activeForm.js` and `yii\\web\\View` to jQuery 3.0 compatible API (silverfire)\n- Enh #14633: Add miliseconds to log time in `\\yii\\log\\Target` (Ni-san)\n- Enh #14664: Add migrate/fresh command to truncate database and apply migrations again (thyseus)\n- Enh #14765: RBAC: add index on `user_id` column in `auth_assignment` table for performance reasons (bicf)\n- Enh #14864: Ability to use dependencies in constructor of migrations (vtvz)\n- Enh #14877: Disabled profiling on connection opening when profiling is disabled (njasm)\n- Enh #14913: Assset hashing now takes asset linking into account to improve cache busting (schmunk42)\n- Enh #14929: Ensure trailing `;` on combining files with `asset` command to fix compiler failures (tanakahisateru)\n- Enh #14958: Added options to copy stacktrace and search for error message to the exception page (cebe)\n- Enh #14967: Added Armenian Translations (gevorgmansuryan)\n- Enh #15015: Added `StringHelper::floatToString()` to safely cast float values independent of the locale, also fixes some places in the framework that use it now (cebe)\n- Chg #7936: Deprecate `yii\\base\\Object` in favor of `yii\\base\\BaseObject` for compatibility with PHP 7.2 (rob006, cebe, klimov-paul)\n- Chg #14201: `yii\\console\\controllers\\MessageController::extractMessagesFromTokens()` is now protected (faenir)\n- Chg #14286: Used primary inputmask package name instead of an alias (samdark)\n- Chg #14321: `yii\\widgets\\MaskedInput` is now registering its JavaScript `clientOptions` initialization code in head section (DaveFerger)\n- Chg #14487: Changed i18n message error to warning (dmirogin)\n\n\n2.0.12 June 05, 2017\n--------------------\n\n- Bug #4408: Add support for unicode word characters and `+` character in attribute names (sammousa, kmindi)\n- Bug #5442: Fixed problem on load fixture dependencies with database related tests (leandrogehlen)\n- Bug #7946: Fixed a bug when the `form` attribute was not propagated to the hidden input of the checkbox (Kolyunya)\n- Bug #8120: Fixes LIKE special characters escaping for Cubrid/MSSQL/Oracle/SQLite in `yii\\db\\QueryBuilder` (sergeymakinen)\n- Bug #9669: AssetManager and `FileHelper::copyDirectory()` were copying empty directories when using `only` or `except` options. Added an option to disable this (cebe)\n- Bug #10305: Oracle SQL queries with `IN` condition and more than 1000 parameters are working now (silverfire)\n- Bug #10346: Fixed \"DOMException: Invalid Character Error\" in `yii\\web\\XmlResponseFormatter::buildXml()` (sasha-ch)\n- Bug #10372: Fixed console controller including complex typed arguments in help (sammousa)\n- Bug #11230: Include `defaultRoles` in `yii\\rbac\\DbManager->getRolesByUser()` results (developeruz)\n- Bug #11404: `yii\\base\\Model::loadMultiple()` returns true even if `yii\\base\\Model::load()` returns false (zvook)\n- Bug #11719: Fixed `yii\\db\\Connection::$enableQueryCache` caused infinite loop when the same connection was used for `yii\\caching\\DbCache` (michaelarnauts)\n- Bug #12715: Exception `SAVEPOINT LEVEL1 does not exist` instead of deadlock exception (Vovan-VE)\n- Bug #13058: Fixed caught exception thrown during view file rendering produces wrong output (klimov-paul)\n- Bug #13086, #13656: Fixed bug with optional parameters at the beginning of pattern in `yii\\web\\UrlRule` (rob006)\n- Bug #13087: Fixed getting active validators for safe attribute (developeruz, klimov-paul)\n- Bug #13306: Wildcard in `reloadableScripts` in `yii.js` allows 0 characters (arogachev)\n- Bug #13340: Fixed `yii\\db\\Connection::useMaster()` - exception within callback completely disables slaves (Vovan-VE)\n- Bug #13343: Fixed `yii\\i18n\\Formatter::asTime()` to process time-only values without time zone conversion (bizley)\n- Bug #13350: Fixed bug with incorrect caching of `yii\\web\\UrlRule::createUrl()` results in `yii\\web\\UrlManager` (rob006)\n- Bug #13362: Fixed return value of `yii\\caching\\MemCache::setValues()`  (masterklavi)\n- Bug #13379: Fixed `applyFilter()` function in `yii.gridView.js` to work correctly when params in `filterUrl` are indexed (SilverFire, arogachev)\n- Bug #13418: Fixed `QueryBuilder::batchInsert()` if `$rows` is `\\Generator` (lav45)\n- Bug #13494: Fixed `yii\\console\\controllers\\MessageConstroller::saveMessagesToDb()` to work on different DBMS correctly (silverfire)\n- Bug #13513: Fixed RBAC migration to work correctly on Oracle DBMS (silverfire)\n- Bug #13537: Fixed `yii\\web\\CacheSession::destroySession()` to work correctly when session is not written yet (silverfire, papalapa)\n- Bug #13538: Fixed `yii\\db\\BaseActiveRecord::deleteAll()` changes method signature declared by `yii\\db\\ActiveRecordInterface::deleteAll()` (klimov-paul)\n- Bug #13551: Fixed `FixtureController` to load fixtures from subdirectories (d1rtyf1ng3rs, silverfire)\n- Bug #13571: Fix `yii\\db\\mssql\\QueryBuilder::checkIntegrity` for all tables (boboldehampsink)\n- Bug #13577: `yii\\db\\QueryBuilder::truncateTable` should work consistent over all databases (boboldehampsink)\n- Bug #13582: PK column in `yii\\db\\pgsql\\QueryBuilder::resetSequence()` was not quoted properly (boboldehampsink)\n- Bug #13592: Fixes `yii\\db\\oci\\Schema::setTransactionIsolationLevel()` in Oracle (sergeymakinen)\n- Bug #13594: Fixes insufficient quoting in `yii\\db\\QueryBuilder::prepareInsertSelectSubQuery()` (sergeymakinen)\n- Bug #13649: Fixes issue where `['uncheck' => false]` and `['label' => false]` options for `ActiveRadio` and `ActiveCheckbox` were ignored (Alex-Code)\n- Bug #13657: Fixed `yii\\helpers\\StringHelper::truncateHtml()` skip extra tags at the end (sam002)\n- Bug #13670: Fixed alias option from console when it includes `-` or `_` in option name (pana1990)\n- Bug #13671: Fixed error handler trace to work correctly with XDebug (samdark)\n- Bug #13689: Fixed handling of errors in closures (mikehaertl)\n- Bug #13694: `yii\\widgets\\Pjax` now sends `X-Pjax-Url` header with response to fix redirect (wleona3, Faryshta)\n- Bug #13704: Fixed `yii\\validators\\UniqueValidator` to prefix attribute name with model's database table name (vladis84)\n- Bug #13707: Fixed `yii\\web\\ErrorHandler` and `yii\\web\\ErrorAction` not setting correct response code to response object before rendering error view (samdark)\n- Bug #13728: Fixed the bug when `yii\\behaviors\\SluggableBehavior` wasn't preserving immutable slug values (Kolyunya)\n- Bug #13738: Fixed `getQueryParams()` method in `yii.js` to correctly parse URL with question mark and no query parameters (vladdnepr)\n- Bug #13776: Fixed setting precision and scale for decimal columns in MSSQL (arturf)\n- Bug #13790: Fixed error in `\\yii\\widgets\\MaskedInput` JavaScript by raising version required (samdark)\n- Bug #13807: Fixed `yii\\db\\QueryBuilder` to inherit subquery params when building a `INSERT INTO ... SELECT` query (sergeymakinen)\n- Bug #13842: Fixed ambiguous table SQL error while using `yii\\validators\\ExistValidator` and `yii\\validators\\UniqueValidator` (vladis84, samdark)\n- Bug #13846: Fixed `Query::count()` issue with `orderBy` (Alex-Code)\n- Bug #13848: `yii\\di\\Instance::ensure()` wasn't throwing an exception when `$type` is specified and `$reference` object isn't instance of `$type` (c-jonua)\n- Bug #13890: `yii\\log\\DbTarget` log messages where not written when a database transaction was rolled back, added support for cloning a `yii\\db\\Connection` (shirase, cebe)\n- Bug #13901: Fixed passing unused parameter to `formatMessage()` call in `\\yii\\validators\\IpValidator` (Kolyunya)\n- Bug #13961: Fixed `unserialize()` error during RBAC rule retrieving from PostgreSQL DBMS (vsguts, nanodesu88, cebe)\n- Bug #14012: `yii\\db\\pgsql\\Schema::findViewNames()` was skipping materialized views (insolita)\n- Bug #14033: Fixed `yii\\filters\\AccessRule::matchIp()` erroring in case IP is not defined under HHVM (Kolyunya)\n- Bug #14042: Fixed ambiguous column name in SELECT in UniqueValidator (cebe)\n- Bug #14052: Fixed processing parse errors on PHP 7 since these are instances of `\\ParseError` (samdark)\n- Bug #14072: Fixed a bug where `\\yii\\db\\Command::createTable()`, `addForeignKey()`, `dropForeignKey()`, `addCommentOnColumn()`, and `dropCommentFromColumn()` weren't refreshing the table cache on `yii\\db\\Schema` (brandonkelly)\n- Bug #14074: Fixed default value of `yii\\console\\controllers\\FixtureController::$globalFixtures` to contain valid class name (lynicidn)\n- Bug #14094: Fixed bug when single `yii\\web\\UrlManager::createUrl()` call my result multiple calls of `yii\\web\\UrlRule::createUrl()` for the same rule (rossoneri)\n- Bug #14133: Fixed bug when calculating timings with mixed nested profile begin and end in `yii\\log\\Logger::calculateTimings()` (bizley)\n- Enh #4793: `yii\\filters\\AccessControl` now can be used without `user` component (bizley)\n- Enh #4999: Added support for wildcards at `yii\\filters\\AccessRule::$controllers` (klimov-paul)\n- Enh #5108: `yii\\validators\\DateValidator` now resets `$timestampAttribute` value on empty validated attribute value (klimov-paul)\n- Enh #8426: `yii\\filters\\AccessRule` now allows passing parameters to the role checking function (fsateler, cebe, Faryshta)\n- Enh #8641: Enhanced `yii\\console\\Request::resolve()` to prevent passing parameters, that begin from digits (silverfire)\n- Enh #11288: Added support for caching of `yii\\web\\UrlRule::createUrl()` results in `yii\\web\\UrlManager` for rules with defaults (rob006)\n- Enh #12528: Added option to disable query logging and profiling in DB command (cebe)\n- Enh #13144: Refactored `yii\\db\\Query::queryScalar()` (Alex-Code)\n- Enh #13179: Added `yii\\data\\Sort::parseSortParam` allowing to customize sort param in descendant class (leandrogehlen)\n- Enh #13221: Make `\\yii\\db\\QueryTrait::limit()` and `\\yii\\db\\QueryTrait::offset()` methods work with `\\yii\\db\\Expression` (Ni-san)\n- Enh #13226: `yii cache` command now warns about the fact that it's not able to flush APC cache from console (samdark)\n- Enh #13240: Client scripts registration in `yii\\widgets\\ActiverForm` was moved to the separate `registerClientScript()` method (uaoleg, silverfire)\n- Enh #13243: Added support for unicode attribute names in `yii\\widgets\\DetailView` (arogachev)\n- Enh #13254: Core validators no longer require `Yii::$app` to be set (sammousa)\n- Enh #13260: Added support for sorting by expression to `\\yii\\data\\Sort` (LAV45, klimov-paul)\n- Enh #13278: `yii\\caching\\DbQueryDependency` created allowing specification of the cache dependency via `yii\\db\\QueryInterface` (klimov-paul)\n- Enh #13352: Added option to not render empty row in `yii\\grid\\GridView` when data is empty and `emptyText` set to `false` (arogachev)\n- Enh #13356: Support multiple paths in `MigrateController::$migrationPath` to load non-namespaced migrations for BC with existing applications and extensions (schmunk42, cebe)\n- Enh #13360: Added Dockerized test setup for the framework tests (schmunk42)\n- Enh #13369: Added ability to render current `yii\\widgets\\LinkPager` page disabled (aquy)\n- Enh #13376: Data provider now automatically sets an ID so there is no need to set it manually in case multiple data providers are used with pagination (SamMousa)\n- Enh #13407: Added URL-safe base64 encode/decode methods to `StringHelper` (andrewnester)\n- Enh #13467: `yii\\data\\ActiveDataProvider` no longer queries models if models count is zero (kLkA, Kolyunya)\n- Enh #13523: Fixed pluralization and singularization for words `pasta`, `currency` (developeruz, silverfire)\n- Enh #13550: Refactored `unset()` call order in `yii\\di\\ServiceLocator::set()` (Lanrik)\n- Enh #13560: Refactored `\\yii\\widgets\\FragmentCache::getCachedContent()`, added tests (Kolyunya)\n- Enh #13576: Added support of `srcset` to `yii\\helpers\\Html::img()` (Kolyunya)\n- Enh #13577: Implemented `yii\\db\\mssql\\QueryBuilder::resetSequence()` (boboldehampsink)\n- Enh #13582: Added tests for all `yii\\db\\QueryBuilder::resetSequence()` implementations, fixed SQLite implementation (boboldehampsink)\n- Enh #13642: Allow overriding the function for creating related queries in ActiveRecord by adding `createRelationQuery()` (leandrogehlen)\n- Enh #13650: Improved `yii\\base\\Security::hkdf()` to take advantage of native `hash_hkdf()` implementation in PHP >= 7.1.2  (charlesportwoodii)\n- Enh #13695: `yii\\web\\Response::setStatusCode()` method now returns the Response object itself (kyle-mccarthy)\n- Enh #13698: `yii\\grid\\DataColumn` filter is automatically generated as dropdown list in case of `format` set to `boolean` (bizley)\n- Enh #13770: Added support for `yii\\widgets\\Menu` item classes definition in the form of an array (Kolyunya)\n- Enh #13820: Add new HTTP status code 451 (yyxx9988)\n- Enh #13823: Refactored migrations template (Kolyunya)\n- Enh #13837: Refactored masking of CSRF tokens (sammousa)\n- Enh #13845: `mt_rand()` is used instead of `rand()` in `yii\\captcha\\CaptchaAction` (kalessil)\n- Enh #13883: `yii\\data\\SqlDataProvider` now provides automatic fallback for the case when `totalCount` is not specified (SamMousa)\n- Enh #13911: Significantly enhanced MSSQL schema reading performance (paulzi, WebdevMerlion)\n- Enh #13945: Removed Courier New from error page fonts list since it looks bad on Linux (samdark)\n- Enh #13963: Added tests for `yii\\behaviors\\TimestampBehavior` (vladis84)\n- Enh #13976: Disabled IPv6 check on `\\yii\\validators\\IpValidator` as it turns out it is not needed for `inet_*` methods to work (mikk150)\n- Enh #13981: `yii\\caching\\Cache::getOrSet()` now supports both `Closure` and `callable` (silverfire)\n- Enh #13994: Refactored `yii\\filters\\RateLimiter`. Added tests (vladis84)\n- Enh #14059: Removed unused AR instantiating for calling of static methods (ElisDN)\n- Enh #14067: `yii\\web\\View::clear()` sets populated arrays to empty arrays instead of null, also changed default values to empty array (craiglondon)\n- Enh #14098: `yii\\helpers\\BaseFileHelper::normalizeOptions()` is now protected (brandonkelly)\n- Enh: Added `yii\\di\\Instance::__set_state()` method to restore object after serialization using `var_export()` function (silvefire)\n\n\n2.0.11.2 February 08, 2017\n--------------------------\n\n- Bug #13501: Fixed `yii\\rbac\\DbManager::getRule()` and `yii\\rbac\\DbManager::getRules()` to properly handle resource data came from Rule table when using PostgreSQL (StalkAlex)\n- Bug #13508: Fixed duplicate attachment of behavior BC break (cebe)\n- Bug #13522: Issue with UrlRule, which created duplicate slashes when a default value was used (cebe)\n- Bug #13533: Fixed BC break in `yii\\validators\\ExistValidator::$targetAttribute` (developeruz)\n\n\n2.0.11.1 February 02, 2017\n------------------------\n\n- Bug #11502: Fixed `yii\\console\\controllers\\MessageController` to properly populate missing languages in case of extraction with \"db\" format (bizley)\n- Bug #13489: Fixed button names in ActionColumn to contain proper `Yii::t()` tags and restored missing translations for `el`, `fa`, `ja`, `ru`, and `sk` (cebe, softark)\n\n\n2.0.11 February 01, 2017\n------------------------\n\n- Bug #4113: Error page stacktrace was generating links to private methods which are not part of the API docs (samdark)\n- Bug #7727: Fixed `yii\\helpers\\StringHelper::truncateHtml()` leaving extra tags (developeruz)\n- Bug #9305: Fixed MSSQL `Schema::TYPE_TIMESTAMP` to be 'datetime' instead of 'timestamp', which is just an incremental number (nkovacs)\n- Bug #9616: Fixed mysql\\Schema::loadColumnSchema to set enumValues attribute correctly if enum definition contains commas (fphammerle)\n- Bug #9796: Initialization of not existing `yii\\grid\\ActionColumn` default buttons (arogachev)\n- Bug #10488: Fixed incorrect behavior of `yii\\validation\\NumberValidator` when used with locales where decimal separator is comma (quantum13, samdark, rob006)\n- Bug #11122: Fixed can not use `orderBy` with aggregate functions like `count` (Ni-san)\n- Bug #11771: Fixed semantics of `yii\\di\\ServiceLocator::__isset()` to match the behavior of `__get()` which fixes inconsistent behavior on newer PHP versions (cebe)\n- Bug #12133: Fixed `getDbTargets()` function in `yii\\log\\migrations\\m141106_185632_log_init` that would create a log table correctly (bumstik)\n- Bug #12213: Fixed `yii\\db\\ActiveRecord::unlinkAll()` to respect `onCondition()` of the relational query (silverfire)\n- Bug #12345: Fixed `Formatter::asCurrency()` for proper decimal formatting (Oxyaction)\n- Bug #12599: Fixed MSSQL fail to work with `nvarbinary`. Enhanced SQL scripts compatibility with older versions (samdark)\n- Bug #12681: Changed `data` column type from `text` to `blob` to handle null-byte (`\\0`) in serialized RBAC rule properly (silverfire)\n- Bug #12703: Fixed `StringHelper::truncateHtml()` non functional when dom PHP extension is disabled (samdark)\n- Bug #12713: Fixed `yii\\caching\\FileDependency` to clear stat cache before reading filemtime (SG5)\n- Bug #12714: Fixed `yii\\validation\\EmailValidator` to prevent false-positives checks when property `checkDns` is set to `true` (silverfire)\n- Bug #12735: Fixed `yii\\console\\controllers\\MigrateController` creating multiple primary keys for field `bigPrimaryKey:unsigned` (SG5)\n- Bug #12791: Fixed `yii\\behaviors\\AttributeTypecastBehavior` unable to automatically detect `attributeTypes`, triggering PHP Fatal Error (klimov-paul)\n- Bug #12795: Fixed inconsistency, `Yii::$app->controller` is available after handling the request since 2.0.10, this is now also the case for `Yii::$app->controller->action` (cebe)\n- Bug #12803, #12921: Fixed BC break in `yii.activeForm.js` introduced in #11999. Reverted commit 3ba72da (silverfire)\n- Bug #12810: Fixed `yii\\rbac\\DbManager::getChildRoles()` and `yii\\rbac\\PhpManager::getChildRoles()` throws an exception when role has no child roles (mysterydragon)\n- Bug #12822: Fixed `yii\\i18n\\Formatter::asTimestamp()` to process timestamp with miliseconds correctly (h311ion)\n- Bug #12824: Enabled usage of `yii\\mutex\\FileMutex` on Windows systems (davidsonalencar)\n- Bug #12828: Fixed handling of nested arrays, objects in `\\yii\\grid\\GridView::guessColumns` (githubjeka)\n- Bug #12836: Fixed `yii\\widgets\\GridView::filterUrl` to not ignore `#` part of filter URL (cebe, arogachev)\n- Bug #12856: Fixed `yii\\web\\XmlResponseFormatter` to use `true` and `false` to represent booleans (samdark)\n- Bug #12879: Console progress bar was not working properly in Windows terminals (samdark, kids-return)\n- Bug #12880: Fixed `yii\\behaviors\\AttributeTypecastBehavior` marks attributes with `null` value as 'dirty' (klimov-paul)\n- Bug #12904: Fixed lowercase table name in migrations (zlakomanoff)\n- Bug #12939: Hard coded table names for MSSQL in RBAC migration (arogachev)\n- Bug #12969: Improved unique ID generation for `yii\\widgets\\Pjax` widgets (dynasource, samdark, rob006)\n- Bug #12974: Fixed incorrect order of migrations history in case `yii\\console\\controllers\\MigrateController::$migrationNamespaces` is in use (evgen-d, klimov-paul)\n- Bug #13071: Help option for commands was not working in modules (arogachev, haimanman)\n- Bug #13089: Fixed `yii\\console\\controllers\\AssetController::adjustCssUrl()` breaks URL reference specification (`url(#id)`) (vitalyzhakov)\n- Bug #13105: Fixed `validate()` method in `yii.activeForm.js` to prevent unexpected form submit when `forceValidate` set to `true` (silverfire)\n- Bug #13108: Fix execute command with negative integer parameter (pana1990, uaoleg)\n- Bug #13118: Fixed `handleAction()` function in `yii.js` to handle attribute `data-pjax=0` as disabled PJAX (silverfire, arisk)\n- Bug #13128: Fixed incorrect position of {pos} string in ColumnSchemaBuilder `__toString` (df2)\n- Bug #13159: Fixed `destroy` method in `yii.captcha.js` which did not work as expected (arogachev)\n- Bug #13198: Fixed order of checks in `yii\\validators\\IpValidator` that sometimes caused wrong error message (silverfire)\n- Bug #13200: Creating URLs for routes specified in `yii\\rest\\UrlRule::$extraPatterns` did not work if no HTTP verb was specified (cebe)\n- Bug #13212: Fixed `DbSession::regenerateID()` failure when `session_regenerate_id()` fails (andrewnester)\n- Bug #13229: Fix fetching schema information for `pgsql` when `PDO::ATTR_CASE` is set (klimov-paul)\n- Bug #13231: Fixed `destroy` method in `yii.gridView.js` which did not work as expected (arogachev)\n- Bug #13232: Event handlers were not detached with changed selector in `yii.gridView.js` (arogachev)\n- Bug #13277: Fixed invalid parsing of `--` (\"End of Options\" special argument) in CLI (rugabarbo)\n- Bug #13287: Fixed translating \"and\" separator in `UniqueValidator` error message (jetexe)\n- Bug #13300: Allow pjax with \"data-pjax\" with no value in `yii.js` (arogachev)\n- Bug #13307: Preventing of race conditions in script filter in `yii.js` works incorrectly (arogachev)\n- Bug #13309: Fixes incorrect console width/height detecting with using Stty on Mac (nowm)\n- Bug #13310: Handle relative and absolute URLs coincidence in CSS filter in `yii.js` (arogachev)\n- Bug #13312: `skipOuterContainers` option was incorrectly passed to pjax in `handleAction` in `yii.js` (arogachev)\n- Bug #13326: Fixed wrong background color generation in `BaseConsole::renderColoredString()` (nowm, silverfire)\n- Bug #13401: Fixed lack of escaping of request dump at exception screens (samdark)\n- Bug #13416: Fixed `yii\\web\\MultipartFormDataParser` adds an extra newline to every value (klimov-paul)\n- Enh #475: Added Bash and Zsh completion support for the `./yii` command (cebe, silverfire)\n- Enh #6242: Access to validator in inline validation (arogachev)\n- Enh #6373: Introduce `yii\\db\\Query::emulateExecution()` to force returning an empty result for a query (klimov-paul)\n- Enh #6809: Added `yii\\caching\\Cache::$defaultDuration` property, allowing to set custom default cache duration (sdkiller)\n- Enh #7333: Improved error message for `yii\\di\\Instance::ensure()` when a component does not exist (cebe)\n- Enh #7420: Attributes for prompt generated with `renderSelectOptions` of `\\yii\\helpers\\Html` helper (arogachev)\n- Enh #7435: Added `EVENT_BEFORE_RUN`, `EVENT_AFTER_RUN` and corresponding methods to `yii\\base\\Widget` (petrabarus)\n- Enh #7820: Add `or` relation for `targetAttribute` in `yii\\validators\\UniqueValidator` (developeruz)\n- Enh #8293: `yii\\db\\Query` can be passed to `insert` method in `yii\\db\\QueryBuilder` (voroks)\n- Enh #9053: Added`yii\\grid\\RadioButtonColumn` (darwinisgod)\n- Enh #9162: Added support of closures in `value` for attributes in `yii\\widgets\\DetailView` (arogachev)\n- Enh #10970: Allow omit specifying empty default params on URL creation (rob006)\n- Enh #11037: `yii.js` and `yii.validation.js` use `Regexp.test()` instead of `String.match()` (arogachev, nkovacs)\n- Enh #11163: Added separate method for client-side validation options `yii\\validators\\Validator::getClientOptions()` (arogachev)\n- Enh #11464: Populate foreign key names from schema (joaoppereira)\n- Enh #11697: Added `filterHaving()`, `andFilterHaving()` and `orFilterHaving()` to `yii\\db\\Query` (nicdnepr, samdark)\n- Enh #11756: Added type mapping for `varbinary` data type in MySQL DBMS (silverfire)\n- Enh #11758: Implemented Dependency Injection Container configuration using Application configuration array (silverfire)\n- Enh #11929: Changed `type` column type from `int` to `smallInt` in RBAC migrations (silverfire)\n- Enh #11959: Added `yii\\caching\\Cache::getOrSet()` method (silverfire)\n- Enh #12000: Added EVENT_INIT to widget (user57376)\n- Enh #12015: Changed visibility `yii\\db\\ActiveQueryTrait::createModels()` from private to protected (ArekX, dynasource)\n- Enh #12145: Added `beforeCacheResponse` and `afterRestoreResponse` to `yii\\filters\\PageCache` to be more easily extendable (sergeymakinen)\n- Enh #12390: Avoid creating queries with false where condition (`0=1`) when fetching relational data (klimov-paul)\n- Enh #12399: Added `ActiveField::addAriaAttributes` property for `aria-required` and `aria-invalid` attributes rendering (Oxyaction, samdark)\n- Enh #12419: Added ability to remove root tag and object tags for `yii\\web\\XmlResponseFormatter` (mhthnz, samdark)\n- Enh #12612: Query conditions added with `yii\\db\\Query::andWhere()` now get appended to the existing conditions if they were already being joined with the `and` operator (brandonkelly)\n- Enh #12619: Added catch `Throwable` in `yii\\base\\ErrorHandler::handleException()`, transactions and simlar places where consistency must be kept after exception (rob006, cebe)\n- Enh #12659: Suggest alternatives when console command was not found (mdmunir, cebe)\n- Enh #12691: Added support for protocol-relative URLs in `yii\\web\\UrlRule::$pattern` (erickskrauch)\n- Enh #12710: Added `beforeItem` and `afterItem` to `yii\\widgets\\ListView` (mdmunir, silverfire)\n- Enh #12725: Enhanced `yii\\widgets\\Menu` to allow item option `active` be a Closure (voskobovich, silverfire)\n- Enh #12726: `yii\\base\\Application::$version` converted to `yii\\base\\Module::$version` virtual property, allowing to specify version as a PHP callback (klimov-paul)\n- Enh #12732: Added `is_dir()` validation to `yii\\helpers\\BaseFileHelper::findFiles()` method (zalatov, silverfire)\n- Enh #12738: Added support for creating protocol-relative URLs in `UrlManager::createAbsoluteUrl()` and `Url` helper methods (rob006)\n- Enh #12748: Migration generator now tries to fetch reference column name for foreignKey from schema if it's not set explicitly (MKiselev)\n- Enh #12750: `yii\\widgets\\ListView::itemOptions` can be a closure now (webdevsega, silverfire)\n- Enh #12758: Added the ability to use instances of `\\yii\\db\\Query` class as values in the `\\yii\\db\\QueryBuilder::insert()` method (PowerGamer1)\n- Enh #12771: Skip \\yii\\rbac\\PhpManager::checkAccessRecursive and \\yii\\rbac\\DbManager::checkAccessRecursive if role assignments are empty (Ni-san)\n- Enh #12790: Added `scrollToErrorOffset` option for `yii\\widgets\\ActiveForm` which adds ability to specify offset in pixels when scrolling to error (mg-code)\n- Enh #12798: Changed `yii\\cache\\Dependency::getHasChanged()` (deprecated, to be removed in 2.1) to `yii\\cache\\Dependency::isChanged()` (dynasource)\n- Enh #12807: Added console controller checks for `yii\\console\\controllers\\HelpController` (schmunk42)\n- Enh #12816: Added `columnSchemaClass` option for `yii\\db\\Schema` which adds ability to specify custom `yii\\db\\ColumnSchema` class (nanodesu88)\n- Enh #12854: Added `RangeNotSatisfiableHttpException` to cover HTTP error 416 file request exceptions (zalatov)\n- Enh #12881: Added `removeValue` method to `yii\\helpers\\BaseArrayHelper` (nilsburg)\n- Enh #12901: Added `getDefaultHelpHeader` method to the `yii\\console\\controllers\\HelpController` class to be able to override default help header in a class heir (diezztsk)\n- Enh #12988: Changed `textarea` method within the `yii\\helpers\\BaseHtml` class to allow users to control whether HTML entities found within `$value` will be double-encoded or not (cyphix333)\n- Enh #13020: Added `disabledListItemSubTagOptions` attribute for `yii\\widgets\\LinkPager` in order to customize the disabled list item sub tag element (nadar)\n- Enh #13035: Use ArrayHelper::getValue() in SluggableBehavior::getValue() (thyseus)\n- Enh #13036: Added shortcut methods `asJson()` and `asXml()` for returning JSON and XML data in web controller actions (cebe)\n- Enh #13050: Added `yii\\filters\\HostControl` allowing protection against 'host header' attacks (klimov-paul, rob006)\n- Enh #13074: Improved `yii\\log\\SyslogTarget` with `$options` to be able to change the default `openlog` options (timbeks)\n- Enh #13122: Optimized query for information about foreign keys in `yii\\db\\oci` (zlakomanoff)\n- Enh #13134: Added logging URL rules (bashkarev)\n- Enh #13202: Refactor validateAttribute method in UniqueValidator (developeruz)\n- Enh #13219: Enhancements for `yii\\db\\Connection` (Vovan-VE)\n  - Added `shuffleMasters` option which adds ability to disable shuffling of masters connections.\n  - Added `getMaster()` getter and `master` property for getting currently active master connection.\n  - Extracted `openFromPoolSequentially()` protected method from `openFromPool()` protected method.\n- Enh #13264: Added `yii\\widgets\\InputWidget::$field` field, allowing access to the related `yii\\widget\\ActiveField` instance (klimov-paul)\n- Enh #13266: Added `yii\\validators\\EachValidator::$stopOnFirstError` allowing addition of more than one error (klimov-paul)\n- Enh #13268: Added logging of memory usage (bashkarev)\n- Enh #13417: Allow customizing `yii\\data\\ActiveDataProvider` in `yii\\rest\\IndexAction` (leandrogehlen)\n- Enh #13453: Select only primary key when counting records in UniqueValidator (developeruz)\n- Enh: Added constants for specifying `yii\\validators\\CompareValidator::$type` (cebe)\n- Enh: Refactored `yii\\web\\ErrorAction` to make it reusable (silverfire)\n- Enh: Added support for field `yii\\console\\controllers\\BaseMigrateController::$migrationNamespaces` setup from CLI (schmunk42)\n- Chg #11906: Updated `yii\\widgets\\MaskedInput` inputmask dependency to `~3.3.3` (samdark)\n\n2.0.10 October 20, 2016\n-----------------------\n\n- Bug #7670: Added `yii\\web\\UrlNormalizer` for normalizing requests with and without trailing slashes (rob006, cronfy, klimov-paul)\n- Bug #7670: Added `UrlNormalizer` for normalizing requests with and without trailing slashes (rob006, cronfy, klimov-paul)\n- Bug #9027: Fixed descendant class of `yii\\web\\UploadedFile` returns parent instances in case invoked after it (andrewnester)\n- Bug #9277: Fixed `yii\\console\\controllers\\AssetController` looses custom options of 'target' bundles (petrabarus, klimov-paul)\n- Bug #9561: Fixed `canGetProperty()` and `canSetProperty()` returns `false` for `yii\\db\\BaseActiveRecord` attributes (klimov-paul, Ni-san)\n- Bug #10358: Fixed race condition in `yii.js` AJAX prefilter (silverfire)\n- Bug #10563: Fixed forming `Content-Disposition` header for file downloads (samdark)\n- Bug #10567: Fixed `yii\\console\\controllers\\AssetController` looses bundle override configuration, which makes it external one (klimov-paul)\n- Bug #10587: Latest used controller instance was not available in `Response::EVENT_AFTER_SEND` handler (samdark, andrewnester)\n- Bug #10681: Reverted fix of beforeValidate event calling in `yii.activeForm.js` (silverfire)\n- Bug #11347: Fixed `yii\\widgets\\Pjax::registerClientScript()` to pass custom `container` to the PJAX JS plugin (silverfire)\n- Bug #11352: Fixed `updateInputs()` method in `yii.activeForm.js` to prevent reading property of undefined (silverfire)\n- Bug #11461: Fixed migration tool error when create migrate with comma in `defaultValue` (pana1990, s-o-f)\n- Bug #11541: Fixed default MySQL integer display width for unsigned primary key (h311ion, rob006, cebe)\n- Bug #11715: Fixed JS validation when the same model's attribute file input is listed more than once on the same page (uaoleg)\n- Bug #11726: `yii\\web\\DbSession` was echoing database errors in production mode (samdark, pastuhov, deadkrolik)\n- Bug #11907: Fixed `yii\\helpers\\Console::getScreenSize()` on Windows was giving out width and height swapped (Spell6inder, samdark, cebe)\n- Bug #11912: Fixed PostgreSQL Schema to support negative default values for integer/float/decimal columns (nsknewbie)\n- Bug #11921: Fixed URL decoding in `yii.getQueryParams()` to handle `+` (plus) character properly (silverfire)\n- Bug #11922: Fixed `yii\\log\\FileTarget` does not apply `fileMode` for rotated via copy files (klimov-paul)\n- Bug #11947: Fixed `gridData` initialization in `yii.gridView.js` (pavlm)\n- Bug #11949: Fixed `yii\\widgets\\ActiveField::end()` generates close tag when it's `option['tag']` is `null` (egorio)\n- Bug #11977: Fixed `yii\\rest\\Serializer::serialize()` serializes DataProvider incorrectly, if models keys do not compose integer sequence (dcb9, klimov-paul)\n- Bug #11990: Fixed `yii\\db\\BaseActiveRecord::refresh()` may set incorrect `oldAttributes` values at some cases (only-victor)\n- Bug #12009: Do not render `for` field label attribute for active form `RadioList` and `CheckboxList` (shevchik87, samdark)\n- Bug #12030: Fixed `yii\\base\\Model::offsetExists()` throws an exception on un-existing field (klimov-paul)\n- Bug #12037: Fixed 2.0.7 regression in memcahe/memcached cache backend (samdark)\n- Bug #12043: Fixed `yii\\helpers\\Json::encode()` encodes empty array returned by `JsonSerializable::jsonSerialize()` as object (klimov-paul)\n- Bug #12053: `./yii migrate/create` was generating wrong code when using `bigPrimaryKey` (VojtechH, samdark)\n- Bug #12068: Added missing `LEVEL_PROFILE` for the syslog target (Mak-Di)\n- Bug #12100: Fixed `yii\\filters\\HttpCache` was sending an empty Pragma header (sergeymakinen)\n- Bug #12107: Fixed REST Serializer to validate input for 'expand' and 'fields' parameter, which crashed on array input (njspok, cebe)\n- Bug #12143: Fixed `yii\\db\\BaseActiveRecord::updateAttributes()` change `isNewRecord` state for the new model (klimov-paul)\n- Bug #12152: Fixed BC break for `yii\\validators\\UniqueValidator` custom message when validating multiple attributes (rob006)\n- Bug #12293: Fixed MSSQL `yii\\db\\mssql\\Schema::resolveTableNames()` when using linked database tables (hAppywAy)\n- Bug #12331: Fixed bug with incorrect currency formatter output, when `$thousandSeparator` was explicitly set (cebe)\n- Bug #12423: Fixed migration tool problem of creating fields with brackets in comment (pana1990)\n- Bug #12428: Fixed `yii\\db\\mysql\\QueryBuilder` causes warning when insert default rows into a table without primary key (DrmagicE)\n- Bug #12431: Fix bug when lock file was not removed after `yii\\mutex\\FileMutex::release()` (rob006)\n- Bug #12446: Disable slaves when execute migrations to resolve master-slave replication no-sync (lichunqiang)\n- Bug #12463: Fixed `yii\\web\\Request::getBodyParams()` does not pass full 'content-type' value to `yii\\web\\RequestParserInterface::parse()` (klimov-paul)\n- Bug #12537: Fixes issues with spaces in `StringHelper:truncateHtml` (Alex-Code)\n- Bug #12554: Fixed `yii\\validators\\UniqueValidator` error of getting first model indexed by field (DrDeath72)\n- Bug #12599: Fixed casting of `binary()` type for MSSQL (silverfire)\n- Bug #12605: Make 'safe' validator work on write-only properties (arthibald, CeBe)\n- Bug #12629: Fixed `yii\\widgets\\ActiveField::widget()` to call `adjustLabelFor()` for `InputWidget` descendants (coderlex)\n- Bug #12649: Fixed consistency of `indexBy` handling for `yii\\db\\Query::column()` (silverfire)\n- Enh #384: Added ability to run migration from several locations via `yii\\console\\controllers\\BaseMigrateController::$migrationNamespaces` (klimov-paul)\n- Enh #6996: Added `yii\\web\\MultipartFormDataParser`, which allows proper processing of 'multipart/form-data' encoded non POST requests (klimov-paul)\n- Enh #8719: Add support for HTML5 attributes on submitbutton (formaction/formmethod...) for ActiveForm (VirtualRJ)\n- Enh #9469: Added support for namespaced migrations via `yii\\console\\controllers\\BaseMigrateController::migrationNamespaces` (klimov-paul)\n- Enh #9708: Added `yii\\console\\controllers\\AssetController::deleteSource` option allowing deletion of the source asset files after compression (pana1990, klimov-paul)\n- Enh #9989: ActiveForm now respects formtarget, formmethod and formenctype attributes of submit button (AnatolyRugalev)\n- Enh #10243: Added `yii\\data\\Sort::setAttributeOrders()` method allowing manual setup of current sort (klimov-paul)\n- Enh #10583: Do not silence session errors in debug mode (samdark)\n- Enh #11096: Added support for PSR-2 style migration naming for namespaced migrations (klimov-paul)\n- Enh #11245: Added `yii\\rbac\\ManagerInterface::getChildRoles()` method, allowing finding child roles for the given one (githubjeka)\n- Enh #11275: Added possibility of unset or force replace former value in `yii\\helpers\\ArrayHelper::merge()` (mdmunir, rob006)\n- Enh #11309: Added `yii\\web\\Request::getHostName()` method that returns hostname of current request (rob006)\n- Enh #11494: `yii.reloadableScripts` now support wildcards with `*` character (silverfire)\n- Enh #11658: Added argument to `yii\\grid\\ActionColumn::urlCreator` callback, which holds reference to the column instance (klimov-paul)\n- Enh #11804: Added `yii\\behaviors\\AttributeTypecastBehavior` for maintaining of strict ActiveRecord attribute types (klimov-paul)\n- Enh #11835: Fixed `\\yii\\db\\mssql\\QueryBuilder::$typeMap[TYPE_TEXT]` - `NTEXT` data type was deprecated in MSSQL (githubjeka)\n- Enh #11844: Added the ability to customize HTML attributes of label and value tags in `\\yii\\widgets\\DetailView` (githubjeka)\n- Enh #11950: Improve `yii\\helpers\\BaseArrayHelper::keyExists()` speed (egorio)\n- Enh #11979: Added `yii\\mutex\\OracleMutex` which implements mutex \"lock\" mechanism via Oracle locks (zlakomanoff)\n- Enh #12028: Add -h|--help option to console command to display help information (pana1990)\n- Enh #12038: Introduced `yii\\base\\ViewNotFoundException` which is thrown when views file doesn't exists, used it in `ViewAction` (samdark)\n- Enh #12048: Improved message extraction command performance (samdark)\n- Enh #12073: Added the ability to suppress the generation of input hint when it is specified through `yii\\base\\Model::attributeHints()` (PowerGamer1)\n- Enh #12082: Used `jQuery.on(` instead of event method to ensure forwards compatibility (newerton)\n- Enh #12099: `yii\\filters\\HttpCache` no longer returns 304 HTTP code when callbacks return null (sergeymakinen)\n- Enh #12193: Added the ability to suppress the generation of duplicate error messages in `yii\\helpers\\Html::errorSummary()`. Added the ability to display error messages beyond the first error for each model attribute (PowerGamer1)\n- Enh #12198: Added `time` and `datetime` validator short names (nkovacs)\n- Enh #12230: Allows BaseHtml::activeListInput to override the field value (RangelReale)\n- Enh #12296: Added value validation to `yii\\log\\Target::setLevels()` (Mak-Di)\n- Enh #12376: Added parameter to `yii.activeForm.js` `validate()` method to be able to force validation (DrDeath72)\n- Enh #12382: Changed `yii\\widgets\\MaskedInput` to use `jQuery` instead of `$` to prevent conflicts (samdark)\n- Enh #12440: Added `yii\\base\\Event::offAll()` method allowing clear all registered class-level event handlers (klimov-paul)\n- Enh #12499: When AJAX validation in enabled, `yii.activeForm.js` will run it forcefully on form submit to display all possible errors (silverfire)\n- Enh #12580: Make `yii.js` comply with strict and non-strict javascript mode to allow concatenation with external code (mikehaertl)\n- Enh #12664: Added support for wildcards for `optional` at `yii\\filters\\auth\\AuthMethod` (mg-code)\n- Enh #12744: Added `afterInit` event to `yii.activeForm.js` (werew01f)\n- Enh: Method `yii\\console\\controllers\\AssetController::getAssetManager()` automatically enables `yii\\web\\AssetManager::forceCopy` in case it is not explicitly specified (pana1990, klimov-paul)\n\n\n2.0.9 July 11, 2016\n-------------------\n\n- Bug #6347: `inverseOf()` not working for dynamic relational queries (laszlovl)\n- Bug #8644: Fixed trying to ENABLE/DISABLE TRIGGER ALL on a view in PostgreSQL (ricpelo)\n- Bug #9950: Updated `yii\\grid\\DataColumn::getHeaderCellLabel()` to extract attribute label from the `filterModel` of Grid (silverfire)\n- Bug #10613: Fixed PostgreSQL Schema to return correct column names for unique indexes that have mixed case column names (cebe)\n- Bug #10681: Fixed active form `beforeValidate` wasn't triggered in some cases (lynicidn)\n- Bug #11252: Use strict comparison on `ActiveRecord::hasAttribute()` to avoid cases where it returns true when a number is passed to it (Faryshta)\n- Bug #11322: Fixed incorrect error message in `yii\\validators\\UniqueValidator` for composite `targetAttribute` (PowerGamer1, silverfire, cebe)\n- Bug #11429: Fixed `yii\\i18n\\PhpMessageSource::loadFallbackMessages()` not to log error when source and language is same, but locales are different (silverfire)\n- Bug #11459: Fixed flash messages not destroyed when `session.auto_start = 1` set in php.ini (cartmanchen)\n- Bug #11498: Fixed inability to save serialized object into PostgreSQL binary column (klimov-paul)\n- Bug #11507: Fixed `yii\\validators\\EachValidator::validateAttribute()` does not respect `skipOnEmpty` rule parameter (webdevsega)\n- Bug #11523: Fixed `yii\\web\\User::checkRedirectAcceptable()` to treat acceptable content type `*/*` as `*` (silverfire)\n- Bug #11527: Fixed `bigPrimaryKey()` for SQLite (dynasource)\n- Bug #11528: Fixed `yii\\i18n\\MessageFormatter` construction exception caught for PHP 7 compatibility (subdee, cebe)\n- Bug #11532: Fixed casting of empty char value to `null` resulting in integrity constraint violation for not null columns (samdark)\n- Bug #11536: Fixed regression introduced in 2.0.8, where scalar value was not allowed in `QueryBuilder` `IN` condition anymore (cebe)\n- Bug #11549: Fixed `yii\\helpers\\ArrayHelper::getValue()` to work properly with float keys (zsounder, AnikanovD)\n- Bug #11561: Fixed DI container throwing exceptions for optional dependencies (SamMousa)\n- Bug #11571: Fixed `yii\\db\\ColumnSchemaBuilder` to work with custom column types (andrey-mokhov, silverfire)\n- Bug #11662: Fixed `schema-oci.sql` for RBAC (jonny7)\n- Bug #11672: Fixed `yii\\validators\\NumberValidator` erroring when value is an object without `__toString()` method (SamMousa)\n- Bug #11686: `yii\\helpers\\ArrayHelper::isIn()` comparison did not work in strict mode (taobig)\n- Bug #11693: Handle `yii\\db\\QueryBuilder::batchInsert()` calls with no data to insert (rob006)\n- Bug #11723: Fixed PHP 7 + XDebug error handling displaying \"Expected array for frame 0\" (tanakahisateru)\n- Bug #11735: Fixed `yii\\web\\UploadedFile` to return `null` when there's no file uploaded (brummm)\n- Bug #11739: Fixed `yii\\helpers\\ArrayHelper::index()` losing precision for float keys (AnikanovD)\n- Bug #11774: Fixed incorrect recusuive symlinks check in `yii\\helpers\\FileHelper` (AnikanovD)\n- Bug #11822: Fixed exception on non-string value provided as CSRF token (cebe)\n- Bug #11847: Fixed `yii\\widgets\\Pjax` to properly respond with partials when custom selector is used for container (pigochu, samdark)\n- Bug #11863: Fixed usage of `mb_substr()` with PHP < 5.4.8 where length of NULL was treated the same as 0 (samdark)\n- Bug #11865: Fixed setting `selected` for dropdown list using options (samdark)\n- Bug #11878: Fixed i18n gettext fallback language message loading (stevekr)\n- Bug #11896: Fix for DROP TRIGGER in migrations from RBAC. (barradas-alberto)\n- Enh #8795: Refactored `yii\\web\\User::loginByCookie()` in order to make it easier to override (maine-mike, silverfire)\n- Enh #9574: Implicit run `yii\\db\\ColumnSchemaBuilder::null()` when default value is set to `null`. (rob006)\n- Enh #9948: `yii\\rbac\\PhpManager` now invalidates script file cache performed by 'OPCache' or 'APC' on file saving (klimov-paul)\n- Enh #10422: Added `null` method on `yii\\db\\ColumnSchemaBuilder` to explicitly set column nullability (nevermnd)\n- Enh #11168: `yii\\helpers\\BaseHtml` now uses abstracted `booleanInput()` and `activeBooleanInput()` methods to render `radio()`, `checkbox()`, `activeRadio()` and `activeCheckbox()` (cesarnicola)\n- Enh #11195: Added ability to append custom string to schema builder column definition (df2, samdark)\n- Enh #11212: Added headers to PO file in `yii\\i18n\\GettextPoFile::save()` (stevekr)\n- Enh #11414: Files specified as `null` in `yii\\web\\AssetBundle` won't be registered (Razzwan)\n- Enh #11428: Speedup SQL query in `yii\\db\\oci\\Schema::findColumns()` (SSiwek)\n- Enh #11432: Added HTTP status 421 \"Misdirected Request\" to list of statuses in `yii\\web\\Response` (dasmfm)\n- Enh #11438: Configurable `yii\\helpers\\Markdown` default flavor (mdmunir)\n- Enh #11462: Added support of filtering rules to `yii\\log\\Target::logVar` (HaruAtari)\n- Enh #11462: Added `yii\\helpers\\BaseArrayHelper::filter()` method (HaruAtari)\n- Enh #11484: Speed up `yii\\db\\oci\\Schema::loadTableSchema()` for Oracle DBMS (SSiwek)\n- Enh #11490: Added `yii\\data\\ArrayDataProvider::$modelClass` property to specify a model used to provide column labels even when data array is empty (PowerGamer1)\n- Enh #11591: Added support for wildcards for `only` and `except` at `yii\\base\\ActionFilter` (klimov-paul)\n- Enh #11679: Extracted `yii\\rbac\\CheckAccessInterface` from `yii\\rbac\\ManagerInterface` (SamMousa, samdark, mdomba)\n- Enh #11725: Added indexes on message tables (OndrejVasicek)\n- Enh #11729: Added `yii\\grid\\CheckboxColumn::$cssClass` property to specify a class added to checkbox input (thiagotalma)\n- Enh #11808: `_table` and `_column` suffixes are now required when generating migration (rob006)\n- Enh #11850: Introduced `yii\\widgets\\Pjax::$submitEvent` to be able to customize event triggering PJAX form submit (Bvanleeuwen)\n- Enh #11857: `yii\\filters\\AccessRule::$verbs` can now be configured in upper and lowercase (DrDeath72, samdark)\n- Chg #11364: Updated jQuery dependency to include versions `1.12.*` (cebe)\n- Chg #11683: Fixed fixture command to work with short syntax. `yii fixture \"*, -User\"` should be used instead of `yii fixture \"*\" -User` (Faryshta, samdark)\n\n\n2.0.8 April 28, 2016\n--------------------\n\n- Bug #7627: Fixed `yii\\widgets\\ActiveField` to handle inputs AJAX validation with changed ID properly (dizeee)\n- Bug #7717: Fixed the bug that `$properties` parameter in `ArrayHelper::toArray()` was not passed to recursive calls (quantum13)\n- Bug #9074: Fixed JS call `$('#grid').yiiGridView('getSelectedRows')` when `GridView::$showHeader` is set to false (NekitoSP, silverfire)\n- Bug #9851: Fixed partial commit / rollback in nested transactions (sammousa)\n- Bug #9935: Fixed `yii\\validators\\EachValidator` does not invoke `validateAttribute()` method of the embedded validator (klimov-paul)\n- Bug #10201: Fixed bug in ActiveRecord, where relational data could not be fetched in case with composite key and join with IN condition (PaulVanSchayck, airmoi, joe-meyer, cebe)\n- Bug #10235: Fixed `yii\\console\\Application::runAction()` to not to corrupt response object (hiqsol)\n- Bug #10480: Fixed removing old identity cookie when loggin in as another user without logging out first (maine-mike)\n- Bug #10617: Fixed `yii\\web\\Request::getBodyParams()` returned `null` instead of empty array if request body is empty and content type is application/json (samdark)\n- Bug #10784: Fixed `yii\\grid\\CheckboxColumn` to set correct value when `yii\\grid\\CheckboxColumn::$checkboxOptions` closure is used (nukkumatti)\n- Bug #10850: Fixed unable to use 'definitions' and 'aliases' at `yii\\widgets\\MaskedInput` (rahimov, klimov-paul)\n- Bug #10884: Fixed `yii\\i18n\\MessageFormatter` for formatting messages when not all parameters are given (laxity7, cebe)\n- Bug #10935: Fixed cache key collision in `yii\\web\\UrlManager::createUrl()` (sammousa)\n- Bug #10946: Fixed parameters binding to the SQL query in `yii\\db\\mysqlSchema::findConstraints()` (silverfire)\n- Bug #10969: Fixed generator migration tool with decimal params in column (pana1990)\n- Bug #10974: `yii.js` - fixed error in ajaxPrefilter event handler, caused by blocked frame (maximal)\n- Bug #11012: Fixed `yii\\web\\UploadedFile::getBaseName()` to work with UTF-8 file names (hiscaler, silverfire)\n- Bug #11026: Fixed `yii\\helpers\\StringHelper::truncateWords()` to count words properly for non-English text (samdark, tol17)\n- Bug #11038: Fixed handling of intervals of 0 seconds in `yii\\i18n\\Formatter::asDuration()` (VirtualRJ)\n- Bug #11040: Check parameter 'recursive' and disable recursive copying with option 'recursive' => false in method BaseFileHelper::copyDirectory (Ni-san)\n- Bug #11052: Fixed `HtmlPurifier` configuration sequence (samdark)\n- Bug #11066: `yii.js` - fixed `getQueryParams()` function to handle URLs with anchors correctly (DrDeath72)\n- Bug #11088: Fixed bug with column name not being quoted correctly, when a quoted table name or a table name in prefix syntax was used (cebe, edgardmessias, Ni-san)\n- Bug #11093: Fixed `yii\\db\\QueryBuilder::buildAndCondition()` to add query params passed directly by `yii\\db\\Expression` (CedricYii, silverfire)\n- Bug #11125: Fixed `JSON_ERROR_SYNTAX` for `json_decode(null)` in PHP 7 (fps01)\n- Bug #11132: Fixed `yii\\widgets\\FragmentCache` not handling empty content correctly in all cases (kidol)\n- Bug #11188: Fixed wrong index usage in `CaptchaAction` when calling `imagefilledrectangle` (alsopub)\n- Bug #11196: Fixed VarDumper throws PHP Fatal when dumping `__PHP_Incomplete_Class` (DamianZ)\n- Bug #11220: NumberValidator now handles objects properly (samdark)\n- Bug #11221: Boolean validator generates incorrect error message (azaikin, githubjeka)\n- Bug #11223: Fixed returning an empty array when DbManager::getRolesByUser() was called on a user with user id 0 (VirtualRJ)\n- Bug #11228: `yii.activeForm.js` - AJAX validation will not be triggered if client side validation failed (silverfire)\n- Bug #11262: Enabled use of yii2 inside of PHAR packaged console applications (hiqsol)\n- Bug #11270: Fixed `yii\\db\\BaseActiveRecord::link()` method in order to support closure in `indexBy` for relations declaration (iushev)\n- Bug #11280: Descendants of `yii\\console\\controllers\\BaseMigrateController`, like the one for MongoDB, unable to create new migration (klimov-paul)\n- Bug #11333: Avoid serializing PHP 7 errors (zuozp8)\n- Bug #11425: Fixed namespace conflict with Markdown helper and Console helper (cebe, mdmunir)\n- Bug: SQlite querybuilder did not create primary key with bigint for `TYPE_BIGPK` (cebe)\n- Enh #5469: Add mimetype validation by mask in FileValidator (kirsenn, samdark, silverfire)\n- Enh #7177, #10165: Added support for validating datetime and time values using intl short format to `DateValidator` (VirtualRJ, cebe)\n- Enh #8145, #8139, #10234 #11153: `yii\\validators\\Validator::$attributes` property now supports `!attribute` notation to validate attribute, but do not mark it as safe (mdmunir)\n- Enh #8148: Implemented ability to add comment on table and column in migration (vaseninm, silverfire)\n- Enh #8505: `yii\\db\\Query` now contains a andFilterCompare() method that allows filtering using operators in the query value (lennartvdd)\n- Enh #8602: `yii\\validators\\DateValidator` skip validation for `timestampAttribute`, if it is already in correct format (klimov-paul)\n- Enh #8639: Improve ActiveRecord to not create new instances of classes when objects are available (cebe)\n- Enh #8779: Automatically set `enctype` form option when using file input field (pana1990, arogachev)\n- Enh #9340: Adds `after()` and `first()` column schema builder modifiers (df2)\n- Enh #9425: `yii\\db\\Query::exists()` now uses SQL standard `EXISTS()` query via new `yii\\db\\QueryBuilder::selectExists()` method to improving performance in some cases (PowerGamer1)\n- Enh #9562: Adds `char` datatype to framework (df2)\n- Enh #9604: `yii\\db\\BaseActiveRecord` now triggers event `EVENT_AFTER_REFRESH` after a record is refreshed (raoul2000)\n- Enh #9893: `yii.js` handleAction enhanced to support for data-form attribute, so links can trigger specific forms (SamMousa)\n- Enh #10309: Extracted `yii\\web\\UrlManager` rule cache key into `$cacheKey` protected property (lordthorzonus)\n- Enh #10322: ActiveForm now respects `formtarget` attribute of submit button (AnatolyRugalev)\n- Enh #10451: Check of existence of `$_SERVER` in `yii\\web\\Request` before using it (quantum13)\n- Enh #10475: Extracted `getUrlFromCache()` and `setRuleToCache()` protected methods from `yii\\web\\UrlManager::createUrl()` (dmdark)\n- Enh #10487: `yii\\helpers\\BaseArrayHelper::index()` got a third parameter `$groupBy` to group the input array by the key in one or more dimensions (quantum13, silverfire, samdark)\n- Enh #10610: Added `BaseUrl::$urlManager` to be able to set URL manager used for creating URLs (samdark)\n- Enh #10631: Split getting label and rendering cell in `yii\\grid\\DataColumn::renderHeaderCellContent()` to make code simpler (t-kanstantsin, samdark)\n- Enh #10710: `yii\\helpers\\FileHelper::copyDirectory()` is now throwing exception when trying to copy a directory to itself or a subdirectory (wallysalami, cebe, samdark)\n- Enh #10764: `yii\\helpers\\Html::tag()` and `::beginTag()` return content without any HTML when the `$tag` attribute is `false` or `null` (pana1990)\n- Enh #10840: Added `yii\\console\\Controller::optionAliases()` method to support aliases for commands (pana1990)\n- Enh #10889: Allows unsigned primary key column definitions (df2)\n- Enh #10908: Added Dependency Injection for Closure configuration (SamMousa)\n- Enh #10910: Fixed Captcha client side validation after image refresh, when controller is under module (silverfire)\n- Enh #10921: `__toString()` of column schema builder now adapts to column types (df2)\n- Enh #10931: Removed hard dependency of `yii\\di\\Container` on `Yii::$app` (SamMousa)\n- Enh #10937: `yii\\web\\User` will now confirm the request accepts an HTML response before redirecting to the login page. Added optional `$checkAcceptHeader` to `yii\\web\\User::loginRequired()` (sammousa)\n- Enh #10941: Added `yii\\helpers\\ArrayHelper::isTraversable()`, added support for traversable selections for dropdownList, radioList and checkboxList in `yii\\helpers\\Html` (sammousa)\n- Enh #10954: `yii\\db\\QueryBuilder` now accepts `Traversable` objects for `in` condition (SamMousa, silverfire)\n- Enh #10967: Simplified Javascript on the exception debug page (SamMousa)\n- Enh #10976: `yii\\helpers\\Inflector::transliterate()` now uses `strtr` instead of `str_replace` (DrDeath72)\n- Enh #11002: `yii\\behaviors\\AttributeBehavior::$skipUpdateOnClean` which determines whether to skip a behavior when the behavior owner has not been modified (Faryshta)\n- Enh #11056: Allow setting a custom logger configuration for `yii\\log\\Dispatcher` in configuration (bionoren, cebe)\n- Enh #11058: `yii\\web\\User::loginRequired()` now does not set return URL when request method is not GET (dawei101, silverfire)\n- Enh #11110: Added migrations for DB session (mdmunir)\n- Enh #11137: Added weak ETag support to `yii\\filters\\HttpCache`. It could be turned on via setting `$weakEtag` to `true` (particleflux)\n- Enh #11139: `yii\\validators\\EachValidator` injects specific attribute value in error message parameters (silverfire)\n- Enh #11166: migrate command new option `useTablePrefix` (Faryshta)\n- Enh #11187: migrate command now generates PHPDoc for table migrations (Faryshta)\n- Enh #11207: migrate command can create foreign keys. (Faryshta)\n- Enh #11254: Added ability to attach RBAC rule using class name (mdmunir)\n- Enh #11285: `yii\\base\\Security` enhancements (tom--, samdark)\n  - Avoid reading more bytes than needed from `/dev/urandom` and `/dev/random`.\n  - Pefer `/dev/random` to `/dev/urandom` when running on FreeBSD.\n  - Better RNG performance.\n- Enh #11336: Allow resettting `$hostInfo`, `$scriptUrl`, and `$pathInfo` in `yii\\web\\Request` and `$baseUrl`, and `$hostInfo` in `yii\\web\\UrlManager` to `null`, to make Yii determine the value again (cebe)\n- Enh: Added `yii\\helpers\\StringHelper::countWords()` that given a string returns number of words in it (samdark)\n- Chg #9854: Added `ActiveRecordInterface::populateRelation()` to respect the methods called by the implementation (SamMousa)\n- Chg #10726: Added `yii\\rbac\\ManagerInterface::canAddChild()` (dkhlystov, samdark)\n- Chg #10921: Inverts responsibility of database specific column schema builder classes (df2)\n- Chg #11071: `yii\\helpers\\BaseArrayHelper::isIn()` and `isTraversable()` since now throw `yii\\base\\InvalidParamException` instead of `InvalidArgumentException` (nukkumatti)\n- Chg #11283: `yii\\db\\ActiveRecord::unlink()` is not setting FK to `null` before deleting itself anymore (samdark)\n- Chg: HTMLPurifier dependency updated to `~4.6` (samdark)\n- New #8920: Added `yii\\mutex\\PgsqlMutex` which implements mutex \"lock\" mechanism via PgSQL locks (nineinchnick, CSharpRU)\n\n\n2.0.7 February 14, 2016\n-----------------------\n\n- Bug #6351: Find MySQL FK constraints from `information_schema` tables instead of `SHOW CREATE TABLE` to improve reliability (nineinchnick)\n- Bug #6363, #8301, #8582, #9566: Fixed data methods and PJAX issues when used together (derekisbusy)\n- Bug #6876: Fixed RBAC migration MSSQL cascade problem (thejahweh)\n- Bug #7627: Fixed `yii\\widgets\\ActiveField` to handle inputs validation with changed ID properly (dynasource, cebe)\n- Bug #7806: Fixed `yii\\grid\\CheckboxColumn` fixed `_all` checkbox column name generation (cebe, silverfire)\n- Bug #7964: Fixed i18n message sources to load fallback messages in a smarter way (silverfire)\n- Bug #8348: Fixed `yii\\helpers\\BaseArrayHelper` fixed PHP Fatal Error: Nesting level too deep - recursive dependency? (andrewnester)\n- Bug #8466: Fixed `yii\\validators\\FileValidator` to display error for `tooBig` and `tooSmall` with formatted unit (silverfire)\n- Bug #8573: Fixed incorrect data type mapping for NUMBER to INTEGER or DECIMAL in Oracle (vbelogai)\n- Bug #8723: Fixed `yii\\helpers\\VarDumper::export()` unable to export circle referenced objects with `Closure` (klimov-paul)\n- Bug #9061: Fixed `yii.activeForm.js`: input onBlur event forces field validation (githubjeka)\n- Bug #9108: Negative number resulted in no formatting when using `yii\\i18n\\Formatter::asSize()` or `yii\\i18n\\Formatter::asShortSize()` (nxnx, cebe)\n- Bug #9288: Fixed `yii\\helpers\\FileHelper::createDirectory()` directory creation to be concurrency friendly (dynasource)\n- Bug #9314: Fixed `yii\\rbac\\DbManager::getPermissionsByUser()` not returning permissions directly assigned to a user (hesna)\n- Bug #9323: Fixed `yii\\console\\controllers\\MessageController` not using database connection specified in config (raccoon69, samdark)\n- Bug #9333: Fixed `yii\\web\\AssetManager` to publish bundles using symlink with nested directories in `hash` (silverfire)\n- Bug #9415: Fixed regression in 2.0.6 where on Oracle DB `PDO::ATTR_CASE = PDO::CASE_LOWER` did not work anymore (cebe)\n- Bug #9442: Fixed `yii\\db\\Migration::renameTable()` caused fatal error when using SQLite driver (fetus-hina)\n- Bug #9452: Fixed `yii\\db\\Query::where()` does not add params from directly passed `yii\\db\\Expression` (klimov-paul)\n- Bug #9454: Fixed MSSQL MARS wasn't working with transactions (daliran)\n- Bug #9583: Server response on invalid JSON request included a wrong message about \"Internal Server Error\" with status 500 (cebe)\n- Bug #9591: Fixed `yii.validation.js` code so it is compressable by YUICompressor (samdark, hofrob)\n- Bug #9596: Fixed `yii\\web\\UrlManager::createAbsoluteUrl(['site/index', '#' => 'testHash'])` losing hash (alchimik, samdark)\n- Bug #9670: Fixed PJAX redirect in IE. `yii\\web\\Response::redirect()` - added check for `X-Ie-Redirect-Compatibility` header (silverfire)\n- Bug #9678: `yii\\i18n\\I18N::format()` wasn't able to handle named placeholder in \"selectordinal\" (samdark)\n- Bug #9681: `yii\\helpers\\Json::encode()` was erroring under CYGWIN (samdark)\n- Bug #9689: Hidden input on `Html::activeFileInput()` had the wrong name if a name was explicitly given (graphcon, cebe)\n- Bug #9707: Fixed Memcache duration which exceeds 30 days (vernik91)\n- Bug #9714: Fixed `yii\\rbac\\PhpManager::updateItem()` unable to save users assignments (rezident1307)\n- Bug #9754: Fixed `yii\\web\\Request` error when path info is empty (dynasource)\n- Bug #9790: Fixed `yii\\db\\sqlite\\QueryBuilder` to generate proper SQL for UNION (romeOz, samdark)\n- Bug #9791: Fixed endless loop on file creation for non-existing device letters on windows (lukos, cebe)\n- Bug #9874: Fixed outputting exception stacktrace in non-debug mode when `Response::FORMAT_RAW` is used (nainoon)\n- Bug #9883: Passing a single `yii\\db\\Expression` to `Query::select()` or `::addSelect()` was not handled correctly in all cases (cebe)\n- Bug #9911: Fixed `yii\\helpers\\BaseStringHelper::explode()` code so it doesn't remove items equal to 0 when `skip_empty` is true (silverfire, kidol)\n- Bug #9924: Fixed `yii.js` handleAction corrupted parameter values containing quote (\") character (silverfire)\n- Bug #9984: Fixed wrong captcha color in case Imagick is used (DrDeath72)\n- Bug #9999: Fixed `yii\\web\\UrlRule` to allow route parameter names with `-`, `_`, `.`characters (silverfire)\n- Bug #10029: Fixed `yii\\widgets\\MaskedInput` not working with PJAX (martrix78, samdark)\n- Bug #10052: Fixed `yii\\i18n\\Formatter` to work with huge numbers on 32-bit arch (necrox87, silverfire)\n- Bug #10101: Fixed assignments saving on role removing in `yii\\rbac\\PhpManager` (rezident1307)\n- Bug #10142: Fixed `yii\\validators\\EmailValidator` to check the length of email properly (silverfire)\n- Bug #10218: Fixed Flash messages not showing after logging out a user (andrewnester)\n- Bug #10263: Fixed `yii\\validators\\UniqueValidator` to work properly when model is not instance of `targetClass` (bupy7, githubjeka, silverfire)\n- Bug #10278: Fixed `yii\\helpers\\BaseJson` support \\SimpleXMLElement data (SilverFire, LAV45)\n- Bug #10302: Fixed JS function `yii.getQueryParams`, which parsed array variables incorrectly (servocoder, silverfire)\n- Bug #10363: Fixed fallback float and integer formatting (AnatolyRugalev, silverfire)\n- Bug #10385: Fixed `yii\\validators\\CaptchaValidator` passed incorrect hashKey to JS validator when `captchaAction` begins with `/` (silverfire)\n- Bug #10467: Fixed `yii\\di\\Instance::ensure()` to work with minimum settings (LAV45)\n- Bug #10541: Fixed division by zero issue in `Console` helper progress bar (youmad)\n- Bug #10580: Fixed `yii\\grid\\GridView::guessColumns()` to work with numeric column names (silverfire)\n- Bug #10625: Fixed `activeForm.js` - when submit doesn't reload page, submit button value simulation with hidden input did not work (andrewnester)\n- Bug #10629: Fixed `yii\\helpers\\BaseStringHelper` - BaseStringHelper::truncateHtml adds suffix regardless of the string length (andrewnester)\n- Bug #10692: Fixed default value extraction in PostgreSQL Schema for null values (wallysalami, gabrielhomsi)\n- Bug #10739: Fixed `yii\\web\\UrlManager::parseRequest()` to treat request URL with more than one slash at the end as invalid (andrewnester)\n- Bug #10751: Fixed `yii\\validators\\UrlValidator` pattern to improve matching (silverfire)\n- Bug #10760: `yii\\widgets\\DetailView::normalizeAttributes()` fixed for arrayable models (boehsermoe)\n- Bug #10825: Fixed `yii\\validators\\EachValidator` does not respect `skipOnEmpty` rule parameter (klimov-paul)\n- Bug: Fixed generation of canonical URLs for `ViewAction` pages (samdark)\n- Bug: Fixed `mb_*` functions calls to use `UTF-8` or `Yii::$app->charset` (silverfire)\n- Enh #2377: Allow specifying a table alias when joining relations via `joinWith()` (cebe, nainoon)\n- Enh #3506: Added `yii\\validators\\IpValidator` to perform validation of IP addresses and subnets (SilverFire, samdark)\n- Enh #4972: Added `yii\\db\\ActiveQuery::alias()` to allow specifying a table alias for the model table without having to know the name (cebe, stepanselyuk)\n- Enh #5146: Added `yii\\i18n\\Formatter::asDuration()` method (nineinchnick, SilverFire)\n- Enh #5902: `yii\\widgets\\Pjax::options` now support special option `tag` to specify tag of container (Alex-Code)\n- Enh #6162, #9198: Added parameter visibleButtons for `yii\\grid\\ActionColumn` (fornit1917, silverfire)\n- Enh #7341: Client validation now skips disabled inputs (SamMousa)\n- Enh #7405: Added `yii\\filters\\auth\\AuthMethod::optional` for optional authentification in all child classes (SilverFire)\n- Enh #7566: Improved `yii\\validators\\CompareValidator` default messages (slinstj)\n- Enh #7581: Added ability to specify range using anonymous function in `RangeValidator` (RomeroMsk)\n- Enh #7674: Added `yii\\db\\Connection::commandClass` to configure a command class that will be used by the connection (sammousa, silverfire, cebe)\n- Enh #8284: Added `yii\\captcha\\CaptchaAction::$imageLibrary` property allowing to set image rendering library (AnatolyRugalev)\n- Enh #8329: Added support of options for `message` console command (vchenin)\n- Enh #8613: `yii\\widgets\\FragmentCache` will not store empty content anymore which fixes some problems related to `yii\\filters\\PageCache` (kidol)\n- Enh #8649: Added total applied migrations to final report (vernik91)\n- Enh #8687: Added support for non-gregorian calendars, e.g. persian, taiwan, islamic to `yii\\i18n\\Formatter` (cebe, z-avanes, hooman-mirghasemi)\n- Enh #8824: Allow passing a `yii\\db\\Expression` to `Query::groupBy()` (cebe)\n- Enh #8995: `yii\\validators\\FileValidator::maxFiles` can be set to `0` to allow unlimited count of files (PowerGamer1, silverfire)\n- Enh #9282: Improved JSON error handling to support PHP 5.5 error codes (freezy-sk)\n- Enh #9337: Added `yii\\db\\ColumnSchemaBuilder::defaultExpression()` to support DB Expression as default value (kotchuprik)\n- Enh #9412: `yii\\web\\Response::sendHeaders()` does now set the status header last which negates certain magic PHP behavior regarding the `header()` function (nd4c, kidol)\n- Enh #9443: Added `unsigned()` to `ColumnSchemaBuilder` (samdark)\n- Enh #9465: ./yii migrate/create now generates code based on migration name and --fields (pana1990)\n- Enh #9573: Added `yii\\rbac\\ManagerInterface::getUserIdsByRole()` and implementations (samdark)\n- Enh #9635: Added default CSS class for `yii\\grid\\ActionColumn` header (arogachev, dynasource)\n- Enh #9643: Added migrations for DB cache (mdmunir)\n- Enh #9711: Added `yii\\widgets\\LinkPager::$pageCssClass` that allows to set default page class (ShNURoK42)\n- Enh #9733: Added Unprocessable Entity HTTP Exception (janfrs)\n- Enh #9762: Added `yii\\web\\JsonResponseFormatter::$encodeOptions` and `::$prettyPrint` for better JSON output formatting (cebe)\n- Enh #9783: jQuery inputmask dependency updated to `~3.2.2` (samdark)\n- Enh #9785: Added ability to invoke callback with dependency resolution to DI container (mdmunir, SamMousa)\n- Enh #9869: Allow path alias for SQLite database files in DSN config (ASlatius)\n- Enh #9901: Default `Cache.SerializerPermissions` configuration option for `HTMLPurifier` is set to `0775` (klimov-paul)\n- Enh #10056: Allowed any callable to be passed to `yii\\grid\\ActionColumn::$urlCreator` (freezy-sk)\n- Enh #10061: `yii\\helpers\\BaseInflector::transliterate()` is now public. Introduced different levels of transliteration strictness (silverfire)\n- Enh #10078: Added `csrf` option to `Html::beginForm()` to allow disabling the hidden csrf field generation (machour)\n- Enh #10086: `yii\\base\\Controller::viewPath` is now configurable (Sibilino)\n- Enh #10098: Changed `yii.confirm` context to the event's target DOM element which is triggered by clickable or changeable elements (lichunqiang)\n- Enh #10108: Added support for events in interfaces (omnilight)\n- Enh #10118: Allow easy extension of slug generation in `yii\\behaviors\\SluggableBehavior` (cebe, hesna, silverfire)\n- Enh #10149: Made `yii\\db\\Connection` serializable (Sam Mousa)\n- Enh #10154: Implemented support of traversable objects in `RangeValidator::ranges`, added `ArrayHelper::isIn()` and `ArrayHelper::isSubset()` (Sam Mousa)\n- Enh #10158: Added the possibility to specify CSS and Javascript options per file in `yii\\web\\AssetBundle` (machour)\n- Enh #10170: `Yii::powered()` now uses `Yii::t()` (SamMousa)\n- Enh #10217: Support for selecting multiple filter values with the same name was added to `yii.gridView.js` (omnilight, silverfire)\n- Enh #10255: Added Yii warning to session initialization if session was already started (AnatolyRugalev)\n- Enh #10264: Generation of HTML tags in Yii's JavaScript now uses jQuery style (silverfire)\n- Enh #10267: `yii.js` - added original event passing to `pjaxOptions` for links with `data-method` and `data-pjax` (servocoder, silverfire)\n- Enh #10319: `yii\\helpers\\VarDumper::dump()` now respects PHP magic method `__debugInfo()` (klimov-paul)\n- Enh #10359: Support wildcard category name in `yii/console/controllers/MessageController` (rmrevin)\n- Enh #10390: Added ability to disable outer tag for `yii\\helpers\\BaseHtml::radiolist()`, `::checkboxList()` (TianJinRong, githubjeka, silverfire)\n- Enh #10535: Allow passing a `yii\\db\\Expression` to `yii\\db\\Query::orderBy()` and `yii\\db\\Query::groupBy()` (andrewnester, cebe)\n- Enh #10545: `yii\\web\\XMLResponseFormatter` changed to format models in a proper way (andrewnester)\n- Enh #10783: Added migration and unit-tests for `yii\\i18n\\DbMessageSource` (silverfire)\n- Enh #10797: Cleaned up requirements checker CSS (muhammadcahya)\n- Enh: Added last resort measure for `yii\\helpers\\FileHelper::removeDirectory()` fail to unlink symlinks under Windows (samdark)\n- Enh: `yii\\behaviors\\AttributeBehavior::getValue()` now respects the callable in array format (silverfire)\n- Chg #6419: Added `yii\\web\\ErrorHandler::displayVars` make list of displayed vars customizable. `$_ENV` and `$_SERVER` are not displayed by default anymore (silverfire)\n- Chg #9369: `Yii::$app->user->can()` now returns `false` instead of erroring in case `authManager` component is not configured (creocoder)\n- Chg #9411: `DetailView` now automatically sets container tag ID in case it's not specified (samdark)\n- Chg #9528: `Traversable` objects are now formatted as arrays in `yii\\web\\XmlResponseFormatter` to support SPL objects and Generators (MaXL-ru)\n- Chg #9878, #9879, #9880: Make `yii\\base\\Security` use `random_bytes()`, LibreSSL, mcrypt, limit OpenSSL to Windows, and to prefer `password_hash()` over `crypt()` (tom--)\n- Chg #9953: `yii\\behaviors\\TimestampBehavior::getValue()` changed to make value processing consistent with `AttributeBehavior::getValue()` (silverfire)\n- Chg #10296: Methods mset, mget and madd of `yii\\caching\\Cache` have been marked as deprecated (trejder, githubjeka)\n- Chg: ApcCache is now able to handle PHP 7 APCu (samdark)\n- Chg: `yii\\behaviors\\BlameableBehavior::getValue()` changed to make value processing consistent with `yii\\behaviors\\AttributeBehavior::getValue()` (silverfire)\n- New #10083: Added wrapper for PHP webserver (samdark)\n- New: Added new requirement: ICU Data version >= 49.1 (SilverFire)\n\n\n2.0.6 August 05, 2015\n---------------------\n\n- Bug #4763: Fixed display issue with overlapping call stack item on exception display page (cebe)\n- Bug #7305: Logging of Exception objects resulted in failure of the logger i.e. no logs being written (cebe)\n- Bug #7374: Use proper INSERT syntax with default values when no values are specified (nineinchnick)\n- Bug #7707: Client-side `trim` validator now passes the trimmed value to subsequent validators (nkovacs)\n- Bug #7764: `yii\\helpers\\ArrayHelper::toArray()` wasn't passing `$recursive` to `yii\\base\\Arrayable::toArray()` (brandonkelly)\n- Bug #8161: Fixed active form `data-method` submit bug when client validation is used (vbelogai)\n- Bug #8322: `yii\\behaviors\\TimestampBehavior::touch()` now throws an exception if owner is new record (klimov-paul)\n- Bug #8451: `yii\\i18n\\Formatter` did not allow negative unix timestamps as input for date formatting (cebe)\n- Bug #8483: Sequence name in `Schema::getLastInsertId()` was not properly quoted (nineinchnick)\n- Bug #8506: Cleaning of output buffer in `Widget::run()` conflicts with `Pjax` widget which did the cleanup itself (cebe, joester89)\n- Bug #8544: Fixed `yii\\db\\ActiveRecord` does not update attribute specified at `optimisticLock()` after save (klimov-paul)\n- Bug #8549: Fixed `yii\\caching\\FileCache` doesn't lock cache files when reading (iworker)\n- Bug #8551: `yii\\pgsql\\QueryBuilder::batchInsert()` may cause \"undefined index\" error (arkhamvm)\n- Bug #8585: Fixed `yii\\helpers\\Html::activeTextarea()` does not allow value overriding via options (klimov-paul)\n- Bug #8592: Fixed `yii\\db\\Command::getRawSql()` unable to parse params specified without colon (':') (klimov-paul)\n- Bug #8593: Fixed `yii\\db\\ActiveQuery` produces incorrect SQL for aggregations, when `sql` field is set (klimov-paul)\n- Bug #8595: Fixed `yii\\rbac\\DbManager::checkAccessFromCache()` to check against auth items loaded in cache recursively (achretien, qiangxue)\n- Bug #8606: Fixed `yii\\web\\Response::xSendFile()` does not reset format (vyants)\n- Bug #8627: Fixed `yii\\db\\Migration` produces incorrect results due to table schema caching (klimov-paul)\n- Bug #8661: Fixed `yii.activeForm.js` scrolling to top (nkovacs)\n- Bug #8684: Formatter ignored explicit decimal number settings when a default value is configured (leandrogehlen, cebe)\n- Bug #8772: `yii\\db\\ActiveQuery` failed removing duplicate records after join when the resultset did not contain the pk values e.g. after grouping (cebe)\n- Bug #8844: Added a workaround for an oracle bug when fetching information about table constraints and filtering by `CONSTRAINT_TYPE` (nidgetgod)\n- Bug #8900: Fixed determining active menu item with URL-alias in route `yii\\widgets\\Menu::isItemActive()` (demi)\n- Bug #9006: Fixed bit column always returning true using certain version of PDO MySQL (stratoss, RusAlex, mj4444ru, samdark)\n- Bug #9046: Fixed problem with endless error loop when an error occurred after sending a stream or file download response to the user (cebe)\n- Bug #9059: Fixed PHP Notice in error handler view (dynasource, andrewnester, samdark)\n- Bug #9063: Workaround for MySQL losing table case when adding index (sebathi)\n- Bug #9076: Fixed `yii\\filters\\PageCache` not using the configured duration and dependency when caching the response data (kidol)\n- Bug #9091: `yii\\web\\UrlManager::createUrl()` did not create correct URL when defaults were used, internal cache is now skipped in certain situations (cebe)\n- Bug #9127, #9128: Fixed MSSQL `yii\\helpers\\mssql\\QueryBuilder::renameColumn()` and `yii\\helpers\\mssql\\QueryBuilder::renameTable()` escaping (sitawit)\n- Bug #9161: Fixed `yii\\web\\Request` ignore `queryParams` when resolve request (zetamen)\n- Bug: Fixed string comparison in `yii\\db\\BaseActiveRecord::unlink()` which may result in wrong comparison result for hash valued primary keys starting with `0e` (cebe)\n- Bug: Pass correct action name to `yii\\console\\Controller::options()` when default action was requested (cebe)\n- Bug: Automatic garbage collection in `yii\\caching\\FileCache` was not triggered (kidol)\n- Bug: Fixed missing stacktrace items on PHP syntax errors of \"unexpected end of file\" type (samdark)\n- Enh #3335: Implemented `yii\\db\\ColumnSchemaBuilder` (pana1990, vaseninm, samdark, cebe)\n- Enh #5991: Added `updateMessages()` to `yii.activeForm.js` to support manually updating ActiveForm messages (nkovacs)\n- Enh #6043: Specification for 'class' and 'style' in array format added to `yii\\helpers\\Html` (klimov-paul)\n- Enh #6853: Console application will now register PHP constants for `STDIN`, `STDOUT`, and `STDERR` itself if they are not defined (cebe)\n- Enh #7169: `yii\\widgets\\ActiveField` now uses corresponding methods for default parts rendering (klimov-paul)\n- Enh #7259: Added `errorAttributes` parameter to ActiveForm `afterValidate` event. Made scrolling to first error optional (nkovacs)\n- Enh #8070: `yii\\console\\controllers\\MessageController` now sorts created messages, even if there is no new one, while saving to PHP file (klimov-paul)\n- Enh #8286: `yii\\console\\controllers\\MessageController` improved allowing extraction of nested translator calls (klimov-paul)\n- Enh #8373: Check also `post_max_size` parameter in `yii\\validators\\FileValidator::getSizeLimit()` (maxxer)\n- Enh #8415: `yii\\helpers\\Html` allows correct rendering of conditional comments containing `!IE` (salaros, klimov-paul)\n- Enh #8444: Added `yii\\widgets\\LinkPager::$linkOptions` to allow configuring HTML attributes of the `a` tags (zinzinday)\n- Enh #8486: Added support to automatically set the `maxlength` attribute for `Html::activeTextArea()` and `Html::activePassword()` (klimov-paul)\n- Enh #8566: Added support for 'only' and 'except' options for `yii\\web\\AssetManager::publish()` (klimov-paul)\n- Enh #8574: Added `yii\\console\\controllers\\MessageController` support .pot file creation (pgaultier)\n- Enh #8625: Added `markUnused` option to `yii\\console\\controllers\\MessageController` (marius7383)\n- Enh #8670: Added support for saving extra fields in session table for `yii\\web\\DbSession` (klimov-paul)\n- Enh #8671: Extracted `yii\\helpers\\Html::escapeJsRegularExpression()` method from `yii\\validators\\RegularExpressionValidator` (silverfire, klimov-paul, samdark, qiangxue)\n- Enh #8903: PostgreSQL `yii\\db\\pgsql\\QueryBuilder::createIndex()` can now specify the index method to use (LAV45)\n- Enh #8933: Yii is now able to properly handle HHVM fatal errors (dieend, samdark)\n- Enh #9011: Allow `yii\\widgets\\MaskedInput` to produce an input tag of a custom type (TriAnMan)\n- Enh #9038: Write warning to log in case `FileCache` fails to write into file (foccy)\n- Enh #9072: `yii\\web\\ErrorAction` displays 404 error instead of blank page on direct access (klimov-paul)\n- Enh #9149: Print directory migrationPath in a `yii migrate` command error. (RusAlex)\n- Enh #9177: Added password hash cost setting to Security component (freezy-sk)\n- Enh #9239: Better handling of `Json` errors (grzegorzkurtyka, samdark)\n- Enh #9246: Added `yii\\web\\UrlRule::getParamRules()` (df2)\n- Enh #9249: Added `hashCallback` in `yii\\web\\AssetManager` to allow custom hash generation for asset directory (petrabarus)\n- Enh #9263: Avoid extra DB query in RBAC DbManager in case auth item name is empty (samdark)\n- Enh #9268: Improved display of boolean parameters in logged SQL queries (arkhamvm, samdark)\n- Enh: Improved Console helper progress bar ETA time estimation, updated only once per second to avoid flapping (cebe)\n- Chg #6354: `yii\\base\\ErrorHandler::logException()` will now log the whole exception object instead of only its string representation (cebe)\n- Chg #8556: Extracted `yii\\web\\User::getAuthManager()` method (samdark)\n- Chg #9181: `yii\\helpers\\BaseStringHelper::truncateHtml()` is now using `runtime` directory for `HTMLPurifier` cache (webdevsega)\n\n\n2.0.5 July 11, 2015\n-------------------\n\n- Bug #9070 (CVE-2015-5467): Fixed `ViewAction::resolveViewName()` not to accept `/../` and `/./` (thejahweh, samdark)\n\n\n2.0.4 May 10, 2015\n------------------\n\n- Bug #5042: Use `RETURNING` for inserts for pgsql and oci to support PKs with a custom default value expression (nineinchnick, klimov-paul)\n- Bug #6234: Fixed wrong table schema information for MSSQL when using multiple schemas (nineinchnick)\n- Bug #6642: Fixed the bug that using confirmation dialog via `data-confirm` in an `yii\\widgets\\ActiveForm` may cause the dialog to appear twice (pana1990, qiangxue)\n- Bug #6871: Fixed the bug that using defaults and hostnames in URL rules may cause an out-of-range index issue (qiangxue)\n- Bug #7036: Fixed `yii\\helpers\\Html::dropDownList()` overrides label specified at 'groups' option (aktec, klimov-paul)\n- Bug #7473: Fixed `yii\\console\\controllers\\AssetController` does not create missing folders for the target bundles (schmunk42, klimov-paul)\n- Bug #7894: Fixed incorrect URL config processing at `yii\\web\\Application::handleRequest()` and `yii\\widgets\\Menu::items` if route element is not a first one (nkovacs, klimov-paul)\n- Bug #7529: Fixed `yii\\web\\Response::sendContentAsFile()` that was broken in 2.0.3 (samdark)\n- Bug #7603: Fixed escape characters in `FormatConverter` to work with unicode characters (maddoger, cebe)\n- Bug #7656: Fixed `yii\\rbac\\DbManager::getRolesByUser()` and `yii\\rbac\\PhpManager::getRolesByUser()` to return roles only (samdark)\n- Bug #7757: Fix fetching tables schema for oci and mysql when `PDO::ATTR_CASE` is set (nineinchnick)\n- Bug #7761: Fixed formatting one digit month in date formatter using ICU format `L` (nkovacs)\n- Bug #7775: Added more strict check on controller IDs when they are being used to create controller instances on Windows (Bhoft, qiangxue)\n- Bug #7831: Add order when fetching database table names and constraints (nineinchnick)\n- Bug #7846: Fixed `yii\\base\\Model` does not recognize scenario declared by rules using 'except' (klimov-paul)\n- Bug #7847: `yii\\db\\ColumnSchema` was typecasting numerics to strings with incorrect decimal separator for some locales (nineinchnick)\n- Bug #7861: Fixed `yii\\helpers\\VarDumper::export()` fails to export object containing `Closure` (klimov-paul)\n- Bug #7867: Fixed `findUniqueIndexes()` not to perform any processing on unique index on function for pgsql (nineinchnick)\n- Bug #7868: Fixed fetching columns definition and composite foreign keys for oci (nineinchnick)\n- Bug #7868: Removed column's autoIncrement detection from oci (nineinchnick)\n- Bug #7868: Fixed creating raw SQL (for logging) by skipping object and resource params (nineinchnick)\n- Bug #7868: Fixed `yii\\db\\Schema::getLastInsertID()`` by quoting sequence name (nineinchnick)\n- Bug #7957: Removed extra `parseFloat()` call for the `compare` js validator (CthulhuDen)\n- Bug #8012: Fixed fetching multiple relations between two tables for pgsql (nineinchnick)\n- Bug #8014: Fixed setting incorrect form \"action\" property after submitting a form using a link with \"data-method\" and containing \"action\" among \"data-params\" (samdark)\n- Bug #8032: `yii\\rbac\\PhpManager::updateItem()` was unable to rename item updated (ChristopheBrun, samdark)\n- Bug #8036: Fixed `yii\\log\\Logger` unable to export session id (klimov-paul)\n- Bug #8068: Fixed `yii\\db\\Query::count()` fails for query containing 'having' without 'group by' (klimov-paul)\n- Bug #8073: Fixed `yii\\data\\ArrayDataProvider::getKeys()` return wrong when `yii\\data\\ArrayDataProvider::$allModels` contain integer key (mdmunir, klimov-paul)\n- Bug #8082: Fixed `yii\\db\\BaseActiveRecord::getAttributeLabel()` return wrong label for related attribute, if several relations in chain share same name (klimov-paul)\n- Bug #8149: Fixed `yii\\db\\BaseActiveRecord::updateCounters()` fails for new record saved with counter attribute not set (klimov-paul)\n- Bug #8158: Avoid exception when detecting console screen size fails on windows (EliasZ)\n- Bug #8165: Fixed `yii\\db\\ActiveRelationTrait::populateRelation()` fails when `link` refers to string convertable object attribute, like `MongoId` (klimov-paul)\n- Bug #8231: Configuration of GridView and DetailView where not preserved when used multiple times (cebe, idMolotov)\n- Bug #8273: Fixed `yii\\widgets\\FragmentCache` when `enabled` is false (nkovacs)\n- Bug #8291: Fixed numeric keys in `$_GET` transformed to 0-based, if 'pretty URL' enabled (quantum13, klimov-paul)\n- Bug #5053: DateValidator is now more robust against different timezone settings (cebe)\n- Bug (CVE-2015-3397): Added `yii\\helpers\\Json::htmlEncode()` to support safer JSON data encoding in HTML code (samdark, Wojciech Janusz, Tomasz Tokarski)\n- Enh #1468: Added ability to specify hints for model attributes via `attributeHints()` method (klimov-paul)\n- Enh #3376: Added `yii\\validators\\EachValidator`, which allows validation of the array attributes (klimov-paul)\n- Enh #5053: Added possibility to specify a format and time zone for the `timestampAttribute` of date validator making it fully usable for validating complete timestamps (cebe)\n- Enh #6442: Improved error message on `yii\\helpers\\FileHelper::createDirectory()` to include the path name of the directory (cebe)\n- Enh #6895: Added `ignoreCategories` config option for message command to ignore categories specified (samdark)\n- Enh #6975: Pressing arrows while focused in inputs of Active Form with `validateOnType` enabled no longer triggers validation (slinstj)\n- Enh #7409: Allow `yii\\filters\\auth\\CompositeAuth::authMethods` to take authentication objects (fernandezekiel, qiangxue)\n- Enh #7443: Allow specification of the `$key` as an array at `yii\\helpers\\ArrayHelper::getValue()` (Alex-Code)\n- Enh #7488: Added `yii\\helpers\\StringHelper::explode()` to perform explode with trimming and skipping of empty elements (SilverFire, nineinchnick, creocoder, samdark)\n- Enh #7514: Added min/max validation to `yii\\validators\\DateValidator` (nkovacs)\n- Enh #7515: Added support to use `indexBy()` together with `column()` in query builder (qiangxue)\n- Enh #7530: Improved default values for `yii\\data\\Sort` link labels in a `ListView` when used with an `ActiveDataProvider` (cebe)\n- Enh #7539: `yii\\console\\controllers\\AssetController` provides dependency trace in case bundle circular dependency detected (klimov-paul)\n- Enh #7562: `yii help` now lists all sub-commands by default (callmez)\n- Enh #7571: HTTP status 500 and \"An internal server error occurred.\" are now returned in case there was an exception in layout and `YII_DEBUG` is false (samdark)\n- Enh #7636: `yii\\web\\Session::getHasSessionId()` uses a more lenient way to check if session ID is provided in URL (robsch)\n- Enh #7637: Allow `yii\\web\\Request::validateCsrfToken()` to validate a manually provided token (miraage, qiangxue)\n- Enh #7808: Adjusted Masked input to correctly generate the hash variable to store the plugin options (wbraganca)\n- Enh #7833: Support (materialized) views and foreign tables along normal tables when fetching table schema (nineinchnick)\n- Enh #7850: Added `yii\\filters\\PageCache::cacheCookies` and `cacheHeaders` to allow selectively caching cookies and HTTP headers (qiangxue)\n- Enh #7867: Implemented findUniqueIndexes for oci and mssql (nineinchnick)\n- Enh #7912: Added `aria-label` to ActionColumn buttons (LAV45, samdark)\n- Enh #7915: Added `yii\\i18n\\Formatter::$numberFormatterSymbols` to allow setting custom symbols for the internally used IntlNumberFormatter, e.g. currency signs (cebe)\n- Enh #7918: `yii\\widgets\\Pjax` got ability to avoid registering link/form handler via setting `false` to `$linkSelector`/`$formSelector` (usualdesigner, Alex-Code, samdark)\n- Enh #7973: Added `yii\\db\\Schema::getSchemaNames()` method (nineinchnick)\n- Enh #8027: Added support for using sub queries in simple Query WHERE conditions (cebe)\n- Enh #8055: `yii\\rest\\UrlRule::extraPatterns` should take precedence over `patterns` (Agrumas)\n- Enh #8064: Added ability to remove containing menu tag by setting `yii\\widgets\\Menu::$options['tag']` to `false` (kirsenn, samdark)\n- Enh #8078: 'links' and 'meta' envelope names are now configurable at `yii\\rest\\Serializer` (arturf)\n- Enh #8171: Allow the user to enforce the fileSize to allow sending files which are not seekable. Needed when using S3 Stream Wrapper (pgaultier)\n- Enh #8179: Added parameter to define whether the comparison of `yii\\db\\BaseActiveRecord::isAttributeChanged()` method will be made as identical (thiagotalma)\n- Enh #8194: Caching of the matched rules added to `yii\\web\\UrlManager::createUrl()` (laszlovl, klimov-paul)\n- Enh #8268: Allow `yii\\db\\QueryBuilder` to recognize more variations of `limit` and `offset` values (tino415, qiangxue)\n- Enh: `yii\\i18n\\Formatter` now shows more information about errors which occured when formatting values (cebe)\n- Enh: Added `yii\\helper\\Console::wrapText()` method to wrap indented text by console window width and used it in `yii help` command (cebe)\n- Enh: Implement batchInsert for oci (nineinchnick)\n- Enh: Detecting `yii\\db\\IntegrityException` for oci (nineinchnick)\n- Enh: `yii\\widgets\\LinkPager::$firstPageLabel` and `yii\\widgets\\LinkPager::$lastPageLabel` now could be set to true in order to use page number as label (samdark)\n- Chg #7924: Migrations in history are now ordered by time applied allowing to roll back in reverse order no matter how these were applied (samdark)\n- Chg: Updated dependency to `cebe/markdown` to version `1.1.x` (cebe)\n\n\n2.0.3 March 01, 2015\n--------------------\n\n- Bug #5457: `yii\\web\\Cors` should handle `Access-Control-Request-Headers` in a case-insensitive manner (qiangxue)\n- Bug #6553: `yii\\validators\\DateValidator` returned valid for date \"2012-12-12foo\" with intl extension enabled (gajahlemu)\n- Bug #6919: Fixed wrong namespaces under advanced application's `TestCase` classes (ivokund)\n- Bug #6940: `yii\\web\\Response::sendContentAsFile()` may not send correct `content-length` header (sadgnome)\n- Bug #6969: `yii\\helpers\\ArrayHelper::htmlEncode()` and `htmlDecode()` should not remove non-string data (qiangxue)\n- Bug #7037: `yii\\console\\controllers\\AssetController` now correctly handles relative image URLs if source and target CSS are under same directory (klimov-paul)\n- Bug #7055: composite IN condition was not generated correctly for certain DBMS (nineinchnick)\n- Bug #7074: `yii\\data\\ArrayDataProvider` did not correctly handle the case `Pagination::pageSize = 0` (kirsenn, qiangxue)\n- Bug #7172: Fixed problem with EmailValidator which did not extract domain correctly for DNS check (nbogol)\n- Bug #7209: `yii\\helpers\\Html::getInputId()`` now also replaces `.` with `-` to ensure a valid ID is generated (omnilight)\n- Bug #7211: Query caching should properly deal with the case when query result is false (qiangxue)\n- Bug #7218: `yii\\captcha\\CaptchaAction` should send response in JSON format (InteLigent, qiangxue)\n- Bug #7226: `yii\\web\\Request::getEtag()` should strip off `-gzip` which may be added by Apache (mcd-php)\n- Bug #7227: Query builder should respect column alias setting when `yii\\db\\Expression` is being selected (mdmunir, qiangxue)\n- Bug #7271: `yii\\db\\ActiveRecord::populateRecord()` should be called in late binding approach (jlorente)\n- Bug #7258: Response was sending HTML content type when formatter was set to JSON or XML, nulls were handled wrong (slavcodev, samdark)\n- Bug #7358: Fix trimming PHPDoc prefix with TAB indent in `yii\\console\\Controller::parseDocCommentSummary()` (gugglegum)\n- Bug #7384: Fix precision loss in log timestamps when using `yii\\log\\DBTarget` (samdark)\n- Bug #7425: `yii\\widgets\\ActiveField::radio()` should not generate the label twice (justinvoelker)\n- Enh #3168: Improved the performance of `yii\\rbac\\DbManager::checkAccess()` by caching mechanism (qiangxue)\n- Enh #3723: `yii\\filters\\PageCache` now supports caching response headers as well as non-HTML response content (qiangxue)\n- Enh #4710: Added `yii\\web\\AssetManager::appendTimestamp` to support cache busting for assets  (qiangxue)\n- Enh #5663: Added support for using `data-params` to specify additional form data to be submitted via the `data-method` approach (usualdesigner, qiangxue)\n- Enh #5681: Allow customization of Menu::submenuTemplate in menu items (RobertBoes, otsec)\n- Enh #6106: Added ability to specify `encode` for each item of `yii\\widgets\\Breadcrumbs` (samdark, aleksanderd)\n- Enh #6361: Added `validateAttribute()` to `yii.activeForm.js` to support manually triggering data validation of an input (Alex-Code, qiang)\n- Enh #6493: Added support for the `Access-Control-Expose-Headers` header by `yii\\filters\\Cors` (usualdesigner)\n- Enh #6697: Added `yii\\helpers\\Url::current()` method that allows adding or removing parameters from current URL (samdark, callmez)\n- Enh #6852: Added `yii\\helpers\\BaseHtmlPurifier::helpers()` in order to be able to configure `HtmlPurifier` helper globally via subclassing (Alex-Code)\n- Enh #6882: Added `yii\\web\\ErrorHandler::getTypeUrl()` in order to allow providing custom types/classes/methods URLs for subclasses (brandonkelly)\n- Enh #6883: `yii\\base\\ErrorHandler::logException()` is now public (samdark)\n- Enh #6896: Added `yii\\log\\FileTarget::$enableRotation` to allow disabling log rotation when external tools are configured for this (cebe)\n- Enh #7008: Removed extra white space in GridView filter cell (uran1980)\n- Enh #7051: Added support for preventing swapping values between different cookies (pavimus, qiangxue)\n- Enh #7150: FormatConverter for date formats now supports single quote escaping (brandonkelly)\n- Enh #7255: Added support to allow widgets that use text input to specify input types (qiangxue)\n- Enh #7269: `yii\\console\\controllers\\BaseMigrateController` now throws exception if directory specified doesn't exist and action isn't `create` (lynicidn, samdark)\n- Enh #7301: Added checking for ICU version in requirements warning about plural problems when it is lower than 49 (sidtj)\n- Enh #7332: Added ability to remove `yii\\widgets\\Menu` container tag by setting `options['tag']` to `false` (dynasource, samdark)\n- Enh #7350: Added `yii\\helpers\\Html::$dataAttributes` to support customizing data attributes (Faryshta, qiangxue)\n- Enh #7357: Refactored `yii\\db\\ColumnSchema` by adding `typecast()` method to decouple `phpTypecast()` from `dbTypecast()` (mcd-php, qiangxue)\n- Enh #7361: The `trim` validator now works on the client side too (qiangxue)\n- Enh #7440: Added support to automatically set the `maxlength` attribute for `Html::activeTextInput()` (llfm)\n- Enh #7446: Added `Schema::TYPE_DOUBLE` to represent ANSI SQL Double Precision type (samdark)\n- Enh #7449: Added `encode` option to allow not encoding select options for `Html::dropDownList()` and `Html::listBox()` (yapi68, qiangxue)\n- Enh: Added support to `yii\\di\\Container` to instantiate and configure an object that implements `yii\\base\\Configurable` (qiangxue)\n- Chg #5690: adjusted paths in message config generated by `yii message/config` to reflect directory structure better (mikehaertl, samdark)\n- Chg #6661: Hyperlinks that are enclosed within an exist form will use the same form for submission if they specify both of the `href` and `data-method` attributes (qiangxue)\n- Chg #7094: Console confirmation must be answered correctly. To return `true`: `y` or `yes`. To return `false`: `n` or `no`. Any other input the question will be asked again (thiagotalma)\n- Chg #7130: Changed the signature of `ActiveRecord::findByCondition()` to simplify the implementation and usage (Faryshta)\n- Chg #7215: Uses OpenSSL crypto lib instead of Mcrypt. Added testing of encrypted data compatibility, both backward and forward (tom--)\n\n\n### Apidoc Extension (yii2-apidoc)\n\n- no changes in this release.\n\n\n### Authclient Extension (yii2-authclient)\n\n- Enh #6892: Default value of `yii\\authclient\\clients\\Twitter::$authUrl` changed to 'authenticate', allowing usage of previous logged user without request an access (kotchuprik)\n\n\n### Bootstrap Extension (yii2-bootstrap)\n\n- no changes in this release.\n\n\n### Codeception Extension (yii2-codeception)\n\n- Bug #6978: DI Container is not reset when destroying application in functional tests (ivokund)\n\n\n### Composer Extension (yii2-composer)\n\n- no changes in this release.\n\n\n### Debug Extension (yii2-debug)\n\n- Bug #6903: Fixed display issue with phpinfo() table (kalayda, cebe)\n- Bug #7222: Debug toolbar wasn't displayed properly in rtl pages (mohammadhosain, johonunu, samdark)\n- Enh #6890: Added ability to filter by query type (pana1990)\n\n\n### Elasticsearch Extension (yii2-elasticsearch)\n\n- no changes in this release.\n\n\n### Faker Extension (yii2-faker)\n\n- no changes in this release.\n\n\n### Gii Extension (yii2-gii)\n\n- Chg #7328: Changed the way CRUD generator translates \"Create X\". Now it's a whole string because of translation difficulties (samdark)\n\n\n### Imagine Extension (yii2-imagine)\n\n- no changes in this release.\n\n\n### Jui Extension (yii2-jui)\n\n- Enh #7127: `name` or `model` and `attribute` are no longer required properties of `yii\\jui\\InputWidget` (nirvana-msu, cebe)\n\n\n### Mongodb Extension (yii2-mongodb)\n\n- Bug #7010: Fixed `yii\\mongodb\\Query::select` now allows excluding fields (Sammaye, klimov-paul)\n\n\n### Redis Extension (yii2-redis)\n\n- no changes in this release.\n\n\n### Smarty Extension (yii2-smarty)\n\n- Bug #6845: Fixed incorrect implementation of `{registerCssFile` and `{registerJsFile` (TomassunGitHub, samdark)\n- Bug #6991: Fixed exception when using `{use class='yii\\bootstrap\\Nav' type='function'}` (ivanlemeshev)\n\n\n### Sphinx Extension (yii2-sphinx)\n\n- Bug #7198: `yii\\sphinx\\Query` no longer attempts to call snippets for the empty query result set (Hrumpa)\n\n\n### Swiftmailer Extension (yii2-swiftmailer)\n\n- no changes in this release.\n\n\n### Twig Extension (yii2-twig)\n\n- no changes in this release.\n\n\n2.0.2 January 11, 2015\n----------------------\n\n- Bug #5577: formatting date and time values for years >=2038 or <=1901 on 32bit systems will not use intl extension but fall back to the PHP implementation (cebe)\n- Bug #6080: Oracle DB schema did not load column types correctly (wenbin1989)\n- Bug #6404: advanced project template `Alert` widget was generating duplicate IDs in case of multiple flashes (SDKiller)\n- Bug #6557: Link URLs generated by `yii\\widgets\\Menu` are not encoded (qiangxue)\n- Bug #6632: `yii\\di\\Container::get()` did not handle config parameter correctly when it is passed as a constructor parameter (qiangxue)\n- Bug #6648: Added explicit type casting to avoid dblib issues on SQL Server 2014 (o-rey)\n- Bug #6691: Fixed console help description parsing with UTF8 characters (cebe)\n- Bug #6717: Fixed issue with UrlManager not matching a route on URL creation when it was prefixed with `/` and pattern was empty (cebe)\n- Bug #6736: Removed `Content-Transfer-Encoding` from the list of default download headers (DaSourcerer)\n- Enh #4502: Added alias support to URL route when calling `yii\\helpers\\Url::toRoute()` and `yii\\helpers\\Url::to()` (qiangxue, lynicidn)\n- Enh #5194: `yii\\console\\controllers\\AssetController` now handles bundle files from external resources properly (klimov-paul)\n- Enh #6247: Logger and error handler are now using slightly less memory (stepanselyuk, samdark)\n- Enh #6398: Added support for specifying dependent component in terms of a configuration array for classes such as `DbCache` (qiangxue)\n- Enh #6434: Added `yii\\behaviors\\SluggableBehavior::immutable` to support keeping the generated slug unchanged (trntv)\n- Enh #6467: `yii\\widgets\\ActiveForm` will scroll to the nearest visible element when the first error input is hidden (newartix)\n- Enh #6488: Support changing `yii\\base\\Theme::basePath` during runtime (qiangxue)\n- Enh #6618: Added Model::addErrors() (slavcodev, pana1990)\n- Enh #6739: Log `Target` now works also when there is no `Yii::$app` instance available, no message prefix will be added in this case (schmunk42)\n- Enh #6748: Improved HTML to Text converter in BaseMailer to generate more readable and correct text version of emails (cebe)\n- Chg #6427: In case of invalid route web application now throws exception with \"Page not found\" instead of \"Invalid Route\" (cebe, samdark)\n- Chg #6641: removed zero padding from ETag strings (DaSourcerer)\n- Chg #6678: `yii\\behaviors\\SluggableBehavior` will generate a new slug only when the slug attribute is empty or the source attribute is changed (qiangxue)\n\n\n### Authclient Extension (yii2-authclient)\n\n- Bug #6502: Fixed `yii\\authclient\\OAuth2::refreshAccessToken()` does not save fetched token (sebathi)\n- Bug #6510: Fixed infinite redirect loop using default `yii\\authclient\\AuthAction::cancelUrl` (klimov-paul)\n\n\n### Bootstrap Extension (yii2-bootstrap)\n\n- Bug #6672: `yii\\bootstrap\\Dropdown` should register client event handlers (qiangxue)\n\n\n### Debug Extension (yii2-debug)\n\n- Bug #4820: Fixed reading incomplete debug index data in case of high request concurrency (martingeorg, samdark)\n- Chg #6572: Allow panels to stay even if they do not receive any debug data (qiangxue)\n\n\n### Elasticsearch Extension (yii2-elasticsearch)\n\n- Enh: Added `ActiveFixture` class for testing fixture support for elasticsearch (cebe, viilveer)\n\n\n### Gii Extension (yii2-gii)\n\n- Bug #6463: The Gii controller generator generates incorrect controller namespace (pana1990)\n- Enh #3665: Better default behavior for ModelSearch generated by the crud generator (qiangxue, mdmunir)\n\n\n### Jui Extension (yii2-jui)\n\n- Enh #6570: Datepicker now uses fallback to find language files, e.g. application language is `de-DE` and the translation files does not exists, it will use `de` instead (cebe)\n- Enh #6471: Datepicker will now show an empty field when value is an empty string (cebe)\n\n\n### Mongodb Extension (yii2-mongodb)\n\n- Bug #6376: Fixed lazy load of relations to `yii\\mongodb\\file\\ActiveRecord` (klimov-paul)\n\n\n### Redis Extension (yii2-redis)\n\n- Bug #6547: Fixed redis connection to deal with large data in combination with `mget()` (pyurin)\n\n\n### Sphinx Extension (yii2-sphinx)\n\n- Bug #6621: Creating sub query at `yii\\sphinx\\Query::queryScalar()` fixed (klimov-paul)\n\n\n### Twig Extension (yii2-twig)\n\n- Bug #6464: `path` and `url` weren't resolving aliases (samdark, lynicidn)\n\n\n2.0.1 December 07, 2014\n-----------------------\n\n- Bug #4471: `yii\\caching\\ApcCache::getValues()` now returns array in case of APC is installed but not enabled in CLI mode (samdark, cebe)\n- Bug #4823: `yii message` accuracy and error handling were improved (samdark)\n- Bug #4889: Application was getting into redirect loop when user wasn't allowed accessing login page. Now shows 403 (samdark)\n- Bug #5070: Gii controller generator should use controller class name instead of controller ID to specify new controller (qiangxue)\n- Bug #5402: Debugger was not loading when there were closures in asset classes (samdark)\n- Bug #5448: Date formatter was doing timezone conversion on date only values resulting in different date displayed than provided (cebe)\n- Bug #5452: Errors occurring after the response is sent are not displayed (qiangxue)\n- Bug #5521: Fixed `yii\\console\\controllers\\AssetController` breaks CSS URLs, which start from '/' (klimov-paul)\n- Bug #5570: `yii\\bootstrap\\Tabs` would throw an exception if `content` is not set for one of its `items` (RomeroMsk)\n- Bug #5584: `yii\\rbac\\DbRbacManager` should not delete items when deleting a rule on a database not supporting cascade update (mdmunir)\n- Bug #5601: Simple conditions in Query::where() and ActiveQuery::where() did not allow `yii\\db\\Expression` to be used as the value (cebe, stevekr)\n- Bug #5619: `yii\\log\\Target` should not attempt to start session when there is none (klimov-paul, qiangxue)\n- Bug #5657: `yii\\caching\\ApcCache::mset()` and `madd()` may cause warning in some APC setup (LAV45)\n- Bug #5665: The `currentPage` meta data in the RESTful result should be 1-based, similar to that in HTTP headers (qiangxue)\n- Bug #5682: The `asset` command would incorrectly combine CSS files when `yii\\web\\AssetManager::linkAssets` is true (dmvslv)\n- Bug #5702: Parenthesis should be automatically added to `yii\\validators\\Validator::whenClient` to avoid js error (mdmunir, qiangxue)\n- Bug #5745: Gii and debug modules may cause 404 exception when the route contains dashes (qiangxue)\n- Bug #5748: Smarty `{path` was generating absolute URLs instead of relative ones (samdark, motzel)\n- Bug #5768: When setting `data-confirm` attribute to a submit button, clicking on the button would not trigger form submission (qiangxue)\n- Bug #5780: `QueryBuilder::batchInsert()` may cause \"undefined index\" error (qiangxue)\n- Bug #5833: The `message` command fails with a FK constraint error when trying to update messages (qiangxue)\n- Bug #5863: Selecting all individual `yii\\grid\\CheckboxColumn` checkboxes in grid view wasn't resulting in \"all\" checkbox selected (samdark)\n- Bug #5893: `yii\\helpers\\ArrayHelper::toArray()` now applies `$properties` parameter for converting descending objects in recursive calls (otsec)\n- Bug #5925: `yii\\helpers\\ArrayHelper::htmlEncode()` does not work properly when the value being encoded is a nested array (tebazil)\n- Bug #5962: DateValidator was throwing a warning on invalid dates using the ICU format on Windows, Yii now works around this [PHP Bug](https://bugs.php.net/bug.php?id=68528) (cebe)\n- Bug #5997: The same message may be exported twice to log targets (klimov-paul)\n- Bug #6018: When setting the `encode` option via `yii\\widgets\\ActiveRecord::errorOptions`, it works the other way around (stanishevsky, qiangxue)\n- Bug #6049: `yii\\db\\Connection::getSchema()` for Oracle should return false when the table does not exist. Oracle does not support `ON UPDATE` clause. (wenbin1989)\n- Bug #6081: `yii\\rbac\\DbManager::getChildren()` was not quoting column name properly (wenbin1989)\n- Bug #6107: `yii message` was emptying existing translations in .po in case of multiple categories (samdark)\n- Bug #6112: `yii message` was incorrectly writing not yet translated strings in .po in case of multiple categories (samdark)\n- Bug #6172: `yii\\rbac\\DbManager` should properly quote table and column names (qiangxue)\n- Bug #6164: Added missing support for `yii\\db\\Expression` to `yii\\db\\QueryBuilder` `BETWEEN` and `LIKE` conditions (cebe)\n- Bug #6236: No JS scripts should be registered when `yii\\widgets\\ActiveForm::enableClientScript` is false (qiangxue)\n- Bug #6150: `yii\\bootstrap\\Tabs` dropdown IDs were generated incorrectly (samdark)\n- Bug #6266: Clicking on reset button does not hide error summary when using ` (InteLigent, qiangxue)\n- Bug #6271: Query caching returns the same data when running the same SQL with `yii\\widgets\\ActiveForm` different fetch modes (grachov)\n- Bug #6279: `yii\\db\\Schema::getLastInsertID()` was passing wrong default schema name to PDO (samdark)\n- Bug #6305: `yii\\i18n\\Formatter::asParagraphs()` was not unicode-aware (samdark)\n- Bug #6311: Optimistic lock for ActiveRecord does not work as expected (qiangxue)\n- Bug #6367: Added `yii\\gii\\generators\\crud\\Generator` to support customizing view path for the generated CRUD controller (qiangxue)\n- Bug #6381: Client-side file validation should be disabled if the browser does not support it (Skysplit)\n- Bug: Gii console command help information does not contain global options (qiangxue)\n- Bug: `yii\\web\\UrlRule` was unable to create URLs for rules containing unicode characters (samdark)\n- Bug: `yii\\web\\AssetManager` should not publish disabled asset bundles (qiangxue)\n- Enh #608: Added `yii\\web\\AssetConverter::$forceConvert` (klimov-paul)\n- Enh #4146: Added `yii\\bootstrap\\ButtonDropdown::$containerOptions` (samdark)\n- Enh #4181: Added `yii\\bootstrap\\Modal::$headerOptions` and `yii\\bootstrap\\Modal::$footerOptions` (tuxoff, samdark)\n- Enh #4263: Added migration and SQL schema files for `yii\\log\\DbTarget` (samdark)\n- Enh #4395: Added `$checkAjax` parameter to `yii\\web\\Response::redirect()` to support default redirection behavior for AJAX/PJAX requests (qiangxue)\n- Enh #4450: Added `yii\\bootstrap\\Nav::renderDropdown()` (qiangxue)\n- Enh #4457: Added support for using noscript for css files registered through asset bundles and Html helper (samdark)\n- Enh #4492: Support PostgreSQL-specific syntax for `yii\\db\\QueryBuilder::alterColumn()` (qiangxue)\n- Enh #4643: Extra options specified in `yii\\widgets\\Breadcrumbs::links` will be treated as HTML attributes for the generated hyperlinks (qiangxue)\n- Enh #4739: Better display of exceptions when the response format is set as \"raw\" format (qiangxue)\n- Enh #4791: Added console output support and more colors for console commands (6pblcb, samdark, klimov-paul, Ragazzo)\n- Enh #5005: Added support to suppress loading the same CSS files in AJAX responses (tof06, qiangxue)\n- Enh #5223: Query builder now supports selecting sub-queries as columns (qiangxue)\n- Enh #5367: Added `yii\\grid\\DataColumn::encodeLabel` (SDKiller)\n- Enh #5480: Added defensive code to `yii\\web\\User::getIdentity()` to avoid potential infinite recursion (qiangxue)\n- Enh #5494: Added support for specifying a menu header as a configuration array in `yii\\bootstrap\\Dropdown` (hiltonjanfield, qiangxue)\n- Enh #5503: Added support for `DateTimeImmutable` to `yii\\i18n\\Formatter` (olegtsvetkov, cebe)\n- Enh #5587: `json_encode` is now used with `JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE` where it makes sense, also\n  it is now default for `yii\\helpers\\Json::encode()` (samdark)\n- Enh #5600: Allow configuring debug panels in `yii\\debug\\Module::panels` as panel class name strings (qiangxue)\n- Enh #5613: Added `--overwrite` option to Gii console command to support overwriting all files (motin, qiangxue)\n- Enh #5627: Added `yii cache/flush-schema` console command to flush DB schema cache of a given database connection (6pblcb, samdark)\n- Enh #5646: Call `yii\\base\\ErrorHandler::unregister()` instead of `restore_*_handlers` directly (aivus)\n- Enh #5683: Added `yii\\i18n\\Formatter::defaultTimeZone` for specifying the default time zone to use for datetime values stored in the database (cebe)\n- Enh #5688: Added optional `$formName` to `Model::loadMultiple()` to support customizing form name directly (qiangxue)\n- Enh #5735: Added `yii\\bootstrap\\Tabs::renderTabContent` to support manually rendering tab contents (RomeroMsk)\n- Enh #5770: Added more PHP error names for `ErrorException` (mongosoft)\n- Enh #5799: `yii\\bootstrap\\ButtonGroup::buttons` can take all options that are supported by `yii\\bootstrap\\Button` (aleksanderd)\n- Enh #5806: Allow `Html::encode()` to be used when the application is not started (qiangxue)\n- Enh #5954: `yii message` command now shows user friendly error if it's not able to parse source file (samdark)\n- Enh #5983: Added `yii\\helpers\\Inflector::sentence()` (pana1990, qiangxue)\n- Enh #6113: Improved debugger configuration and request UI (schmunk42)\n- Enh #6207: Added support for truncating HTML strings using `StringHelper::truncate()` and `StringHelper::truncateWords()` (Alex-Code)\n- Enh #6318: Made widgets more error-tolerant and user-friendly when certain option values are null (qiangxue)\n- Enh: `Console::confirm()` now uses `Console::stdout()` instead of `echo` to be consistent with all other functions (cebe)\n- Enh: `yii\\rbac\\DbManager` migration now uses database component specified in component settings instead of always using default `db` (samdark)\n- Enh: Added `yii\\base\\Controller::renderContent()` (qiangxue)\n- Enh: `yii fixture` command now offers help if no arguments are provided instead of crashing (samdark)\n- Chg #3630: `yii\\db\\Command::queryInternal()` is now protected (samdark)\n- Chg #4277: `yii\\grid\\GridView` is no longer throwing an exception when results are empty and `columns` aren't defined (samdark)\n- Chg #5508: Dropped the support for the `--append` option for the `fixture` command (qiangxue)\n- Chg #5874: Upgraded Twitter Bootstrap to 3.3.x (samdark)\n\n\n### Apidoc Extension (yii2-apidoc)\n\n- Bug #5623: Fixed crash when a class contains a setter that has no arguments e.g. `setXyz()` (cebe)\n- Bug #5899: Incorrect class listed as `definedBy` reference for properties (cebe)\n- Bug: Guide and API renderer now work with relative paths/URLs (cebe)\n- Enh: Guide generator now skips `images` directory if it does not exist instead of throwing an error (cebe)\n- Enh: Made `--guidePrefix` option available as a command line option (cebe)\n\n\n### Authclient Extension (yii2-authclient)\n\n- Bug #6000: Fixed CCS for `yii\\authclient\\widgets\\AuthChoice` does not loaded if `popupMode` disabled (klimov-paul)\n\n\n### Bootstrap Extension (yii2-bootstrap)\n\n- Bug #5570: `yii\\bootstrap\\Tabs` would throw an exception if `content` is not set for one of its `items` (RomeroMsk)\n- Bug #6150: `yii\\bootstrap\\Tabs` dropdown IDs were generated incorrectly (samdark)\n- Enh #4146: Added `yii\\bootstrap\\ButtonDropdown::$containerOptions` (samdark)\n- Enh #4181: Added `yii\\bootstrap\\Modal::$headerOptions` and `yii\\bootstrap\\Modal::$footerOptions` (tuxoff, samdark)\n- Enh #4450: Added `yii\\bootstrap\\Nav::renderDropdown()` (qiangxue)\n- Enh #5494: Added support for specifying a menu header as a configuration array in `yii\\bootstrap\\Dropdown` (hiltonjanfield, qiangxue)\n- Enh #5735: Added `yii\\bootstrap\\Tabs::renderTabContent` to support manually rendering tab contents (RomeroMsk)\n- Enh #5799: `yii\\bootstrap\\ButtonGroup::buttons` can take all options that are supported by `yii\\bootstrap\\Button` (aleksanderd)\n- Chg #5874: Upgraded Twitter Bootstrap to 3.3.x (samdark)\n\n\n### Debug Extension (yii2-debug)\n\n- Bug #5402: Debugger was not loading when there were closures in asset classes (samdark)\n- Bug #5745: Gii and debug modules may cause 404 exception when the route contains dashes (qiangxue)\n- Enh #5600: Allow configuring debug panels in `yii\\debug\\Module::panels` as panel class name strings (qiangxue)\n- Enh #6113: Improved configuration and request UI (schmunk42)\n- Enh: Made `DefaultController::getManifest()` more robust against corrupt files (cebe)\n\n\n### Elasticsearch Extension (yii2-elasticsearch)\n\n- Bug #5662: Elasticsearch AR updateCounters() now uses explicitly `groovy` script for updating making it compatible with ES >1.3.0 (cebe)\n- Bug #6065: `ActiveRecord::unlink()` was failing in some situations when working with relations via array valued attributes (cebe)\n- Enh #5758: Allow passing custom options to `ActiveRecord::update()` and `::delete()` including support for routing needed for updating records with parent relation (cebe)\n- Enh: Add support for optimistic locking (cebe)\n\n\n### Gii Extension (yii2-gii)\n\n- Bug #5070: Gii controller generator should use controller class name instead of controller ID to specify new controller (qiangxue)\n- Bug #5745: Gii and debug modules may cause 404 exception when the route contains dashes (qiangxue)\n- Bug #6367: Added `yii\\gii\\generators\\crud\\Generator` to support customizing view path for the generated CRUD controller (qiangxue)\n- Bug: Gii console command help information does not contain global options (qiangxue)\n- Enh #5613: Added `--overwrite` option to Gii console command to support overwriting all files (motin, qiangxue)\n\n\n### Mongodb Extension (yii2-mongodb)\n\n- Bug #6026: Fixed `yii\\mongodb\\ActiveRecord` saves `null` as `_id`, if attributes are empty (klimov-paul)\n- Enh #3855: Added debug toolbar panel for MongoDB (klimov-paul)\n- Enh #5592: Added support for 'findAndModify' operation at `yii\\mongodb\\Query` and `yii\\mongodb\\ActiveQuery` (klimov-paul)\n\n\n### Redis Extension (yii2-redis)\n\n- Bug #4745: value of simple string returns was ignored by redis client and `true` is returned instead, now only `OK` will result in a `true` while all other values are returned as is (cebe)\n- Enh #3714: Added support for connecting to redis server using a unix socket (savvot, robregonm)\n\n\n### Smarty Extension (yii2-smarty)\n\n- Bug #5748: `{path` was generating absolute URLs instead of relative ones (samdark, motzel)\n\n\n### Sphinx Extension (yii2-sphinx)\n\n- Bug #5601: Simple conditions in `yii\\sphinx\\Query::where()`` and `yii\\sphinx\\ActiveQuery::where()` did not allow `yii\\db\\Expression` to be used as the value (cebe, stevekr)\n- Bug #5634: Fixed `yii\\sphinx\\QueryBuilder` does not support comparison operators (>,<,>= etc) in where specification (klimov-paul)\n- Bug #6164: Added missing support for `yii\\db\\Exression` to QueryBuilder `LIKE` conditions (cebe)\n- Enh #5223: Query builder now supports selecting sub-queries as columns (qiangxue)\n\n\n2.0.0 October 12, 2014\n----------------------\n\n- Bug #4881: Fixed `yii\\console\\controllers\\AssetController` breaks CSS URLs on Windows (klimov-paul)\n- Bug #5171: Fixed the bug that ActiveForm + Pjax submit event is only triggered once (qiangxue)\n- Bug #5242: Fixed `yii\\console\\controllers\\AssetController` breaks CSS URLs in case target file localed at `yii\\web\\AssetManager::basePath` root (klimov-paul)\n- Bug #5252: Null values are not properly handled by `RangeValidator` (githubjeka, qiangxue)\n- Bug #5260: `yii\\i18n\\Formatter::decimalSeparator` and `yii\\i18n\\Formatter::thousandSeparator` where not configurable when intl is not installed (execut, cebe)\n- Bug #5314: Fixed typo in the implementation of `yii\\web\\Session::getHasSessionId()` (qiangxue)\n- Bug #5323: Nested dropdown does not work for `yii\\bootstrap\\DropDown` (aryraditya)\n- Bug #5336: `yii\\bootstrap\\DropDown` should register bootstrap plugin asset (zelenin)\n- Bug #5379: `yii\\base\\Module::afterAction()` was called even when `beforeAction()` returned false (cebe)\n- Bug #5408: Gii console command incorrectly reports errors when there is actually no error (qiangxue)\n- Bug #5423: `yii\\behaviors\\Cors` causes \"undefined index\" error when its `cors` is configured (qiangxue)\n- Bug #5424: `yii\\helpers\\Html::addCssStyle()` wasn't correctly setting style passed in array (kartik-v, samdark)\n- Bug #5435: Added extra checks to `yii\\rbac\\DbManager` to prevent database exceptions when `$userId` is empty (samdark)\n- Bug #5484: Fixed potential string suffix detection failure on 5.5.11 (qiangxue)\n- Bug: Date and time formatting now assumes UTC as the timezone for input dates unless a timezone is explicitly given (cebe)\n- Enh #4040: Added `$viewFile` and `$params` to the `EVENT_BEFORE_RENDER` and `EVENT_AFTER_RENDER` events for `View` (qiangxue)\n- Enh #4275: Added `removeChildren()` to `yii\\rbac\\ManagerInterface` and implementations (samdark)\n- Enh: Added `yii\\base\\Application::loadedModules` (qiangxue)\n- Enh #5316: Added `startsWith()` and `endsWith()` to `yii\\helpers\\StringHelper`. Methods are binary-safe, multibyte-safe and optionally case-insensitive (armab)\n- Enh #5467: Added ability to pass HTML tag options to `asEmail()`, `asImage()` and `asUrl()` methods of `yii\\i18n\\Formatter` (alxkolm, samdark)\n- Chg #2037: Dropped the support for using `yii\\base\\Module` as concrete module classes (qiangxue)\n- Chg: Updated cebe/markdown to 1.0.0 which includes breaking changes in its internal API (cebe)\n- Chg: If you are using CUBRID DBMS, make sure to use at least version 9.3.0 because quoting is broken in prior versions and Yii has no reliable way to work around this issue (cebe)\n\n\n2.0.0-rc September 27, 2014\n---------------------------\n\n- Bug #1263: Fixed the issue that Gii and Debug modules might be affected by incompatible asset manager configuration (qiangxue)\n- Bug #2314: Gii model generator does not generate correct relation type in some special case (qiangxue)\n- Bug #2563: Theming is not working if the path map of the theme contains \"..\" or \".\" in the paths (qiangxue)\n- Bug #2801: Fixed the issue that GridView gets footer content before data cells content (ElisDN)\n- Bug #2821: Console help command incorrectly lists non-console controllers as available commands (qiangxue)\n- Bug #2853: ActiveRecord did not handle resource-typed columns well (chris68, qiangxue)\n- Bug #3042: `yii\\widgets\\Pjax` should end application right after it finishes responding to a pjax request (qiangxue)\n- Bug #3066: `yii\\db\\mssql\\Schema::getTableSchema()` should return null when the table does not exist (qiangxue)\n- Bug #3091: Fixed inconsistent treatment of `Widget::run()` when a widget is used as a container and as a self-contained object (qiangxue)\n- Bug #3118: Ensure client validation has the same behavior as server side validation for number validator (cebe)\n- Bug #3121: `yii\\base\\Application::bootstrap` may fail to load some components if they are specified as class names (qiangxue)\n- Bug #3125: `yii\\console\\controllers\\AssetController` now respects data URL resources (klimov-paul)\n- Bug #3128: Fixed the bug that `defaultRoles` set in RBAC manager was not working as specified (qiangxue)\n- Bug #3153: Fixed the bug that using \"between\" operator to build a SQL query will cause a PHP notice (gonimar)\n- Bug #3184: Fixed the bug that client validation for string length comparison did not set error message correctly (Sergeygithub)\n- Bug #3194: Date formatter works only for timestamps in the year range 1970 to 2038 (kartik-v)\n- Bug #3197: Using `yii\\db\\ActiveQuery::indexBy()` may cause relational AR queries to generate incorrect relational results (qiangxue)\n- Bug #3204: `yii\\di\\Container` did not handle the `$config` parameter well in case when it does not have a default value (qiangxue)\n- Bug #3216: Fixed the bug that `yii.activeForm.destroy()` did not remove `submit` event handlers (qiangxue)\n- Bug #3233: Ensure consistent behavior in `yii\\db\\ActiveRecord::afterSave()`` (cebe, qiangxue)\n- Bug #3236: Return value for `DateTime->format('U')`` casted to double to allow correct date formatting (pgaultier)\n- Bug #3268: Fixed the bug that the schema name in a table name was not respected by `yii\\db\\mysql\\Schema` (terazoid, qiangxue)\n- Bug #3311: Fixed the bug that `yii\\di\\Container::has()` did not return correct value (mgrechanik, qiangxue)\n- Bug #3327: Fixed \"Unable to find debug data\" when logging objects with circular references (jarekkozak, samdark)\n- Bug #3368: Fix for comparing numeric attributes in JavaScript (technixp)\n- Bug #3393: Fix `yii\\helpers\\FileHelper::copyDirectory()` pattern not working (klimov-paul)\n- Bug #3431: Allow using extended `yii\\base\\ErrorHandler` class from the app namespace (cebe)\n- Bug #3436: Fixed the issue that `yii\\di\\ServiceLocator` still returns the old component after calling `set()` with a new definition (qiangxue)\n- Bug #3458: Fixed the bug that the image rendered by `CaptchaAction` was using a wrong content type (MDMunir, qiangxue)\n- Bug #3473: Allow postgreSQL to specify timestamp precision via abstract types in `yii\\db\\QueryBuilder` (cebe)\n- Bug #3478: Fixed `yii\\console\\Controller::select()` accept empty input as '0' value (lynicidn)\n- Bug #3522: Fixed `yii\\helpers\\FileHelper::normalizePath()` to allow a (.) for the current path. (skotos)\n- Bug #3548: Fixed the bug that X-Rate-Limit-Remaining header is always zero when using RateLimiter (qiangxue)\n- Bug #3559: Use native support for batchInsert in SQLite versions >= 3.7.11 and avoid limitations of the fallback (cebe)\n- Bug #3564: Fixed the bug that primary key columns should not take default values from schema (qiangxue)\n- Bug #3567: Fixed the bug that smallint was treated as string for PostgreSQL (qiangxue)\n- Bug #3568: When the primary query sets `asArray`, it is not respected by the `via` relational query (qiangxue)\n- Bug #3578: Fixed postgreSQL column type detection, added missing types (MDMunir, cebe)\n- Bug #3583: Added typecast to auto value of primary key on insert of SQL Active Record (cebe)\n- Bug #3591: Fixed incomplete obsolete filling in i18n `MessageController::saveMessagesToDb()` (advsm)\n- Bug #3601: Fixed the bug that the refresh URL was not generated correctly by `Captcha` (qiangxue, klevron)\n- Bug #3638: `yii\\filters\\HttpCache` does not work as expected when session is started before the filter (qiangxue)\n- Bug #3681: Fixed problem with AR::findOne() when a default scope joins another table so that PK name becomes ambigous (cebe)\n- Bug #3715: Fixed the bug that using a custom pager/sorter with `GridView` may generate two different pagers/sorters if the layout configures two pagers/sorters (qiangxue)\n- Bug #3716: `yii\\base\\DynamicModel::validateData()` does not call `validate()` if the `$rules` parameter is empty (qiangxue)\n- Bug #3725: Fixed the bug that the filtering condition used in relation definition was ignored when calling `ActiveRecord::unlinkAll()`. (qiangxue, cebe)\n- Bug #3738: `yii\\widgets\\ActiveField` custom error selector not functioning (qiangxue)\n- Bug #3751: Fixed postgreSQL schema data for enum values, do not add values if there are none (makroxyz)\n- Bug #3752: `QueryBuilder::batchInsert()` does not typecast input values (qiangxue)\n- Bug #3756: Fix number formatting error for `yii\\base\\Formatter` by converting strings to float (kartik-v)\n- Bug #3772: Behaviors adding validation rules do not work as expected (qiangxue)\n- Bug #3817: `yii\\rbac\\PhpManager::getChildren()` returns null instead of expected empty array (qiangxue)\n- Bug #3843: Fixed Menu bug when using `template` with `encodeLabel` => false (creocoder, umneeq)\n- Bug #3863: Fixed incorrect js selector for `yii\\widgets\\ActiveForm::errorSummaryCssClass` when it contains multiple classes (creocoder, umneeq)\n- Bug #3893: Headers did not overwrite default setting by webserver (cebe)\n- Bug #3909: `yii\\helpers\\Html::to()` should not prefix base URL to URLs that already contain scheme (qiangxue)\n- Bug #3920: Fixed issue with loading default values of PostgreSQL boolean columns (cebe)\n- Bug #3934: `yii.handleAction()` in `yii.js` does not correctly detect if a hyperlink contains useful URL or not (joni-jones, qiangxue)\n- Bug #3968: Messages logged in shutdown functions are not handled (qiangxue)\n- Bug #3989: Fixed `yii\\log\\FileTarget::rotateByCopy` to avoid any rename (cebe)\n- Bug #3996: Traversing `Yii::$app->session` may cause a PHP error (qiangxue)\n- Bug #4020: OCI column detection did not work so gii and other things failed (Sanya1991)\n- Bug #4105: `yii\\helpers\\Html::dropDownlist()` options encodeSpaces was not applied to subgroups (MDMunir)\n- Bug #4123: Trace level in logger had no effect in Targets, traces where not logged (cebe)\n- Bug #4127: `yii\\captcha\\CaptchaValidator` clientside error message wasn't formed properly (samdark)\n- Bug #4162: Fixed bug where schema name was not used in `SHOW CREATE TABLE` query in `yii\\db\\mysql\\Schema` (stevekr)\n- Bug #4241: `yii\\widgets\\Pjax` was incorrectly setting container id (mitalcoi)\n- Bug #4254: `SqlDataProvider` does not work with Oracle and SQL Server (qiangxue, miramir)\n- Bug #4276: Added check for UPLOAD_ERR_NO_FILE in `yii\\web\\UploadedFile` and return null if no file was uploaded (OmgDef)\n- Bug #4342: mssql (dblib) driver does not support getting attributes (tof06)\n- Bug #4371: Active form client validation wasn't working in case of two models having same named fields (abrahamy)\n- Bug #4409: Upper case letters in subdirectory prefixes of controller IDs were not properly handled (qiangxue)\n- Bug #4412: Formatter used SI Prefixes for base 1024, now uses binary prefixes (kmindi)\n- Bug #4427: Formatter could do one division too much (kmindi)\n- Bug #4453: `yii message/extract` wasn't properly writing to po files in case of multiple categories (samdark)\n- Bug #4469: Make `yii\\base\\Security::compareString()` timing depend only on length of `$actual` input and add unit test. (tom--)\n- Bug #4470: Avoid endless loop when exporting logs with low values of flushInterval and eportInterval (cebe)\n- Bug #4497: Fixed `yii\\helpers\\StringHelper::byteSubstr()`` returning empty string on null $length param (mbman)\n- Bug #4514: Fixed Request class crashing when empty CSRF token value is sent in cookie (cebe)\n- Bug #4519: `yii\\base\\Model::isAttributeRequired()` should check if the `when` option of the validator is set (thiagotalma)\n- Bug #4592: Fixed `yii help` command was listing incorrect action names for methods like `actionSayNO` (samdark)\n- Bug #4654: Fixed issue with PostgreSQL and inserting boolean values with batch insert (cebe)\n- Bug #4672: Fixed issue with PostgreSQL handling of boolean values in queries, dropped support for using boolean value for integer columns (cebe)\n- Bug #4727: Fixed wrong Stylus definition in `yii\\web\\AssetConverter` (samdark)\n- Bug #4755: `yii\\test\\BaseActiveFixture::unload()` does not clean up the internal cached data (qiangxue)\n- Bug #4813: Fixed MSSQL schema that was getting incorrect info about constraints (samdark, SerjRamone, o-rey)\n- Bug #4880: Return value of yii\\web\\Request::getPrefferedLanguage() was a normalized value instead of a valid language value from the input array (cebe)\n- Bug #4905: ActiveForm::$validationDelay doesn't delay after keyrelease when $validateOnType=true (qiangxue)\n- Bug #4920: `yii\\filters\\auth\\CompositeAuth` should not trigger error as long as one of the methods succeeds (qiangxue)\n- Bug #4926: Fixed `yii\\console\\controllers\\MessageController` handles category name containing dot incorrectly (klimov-paul)\n- Bug #4938: When `yii\\db\\ActiveQuery` is used to build sub-queries, its WHERE clause is not correctly generated (qiangxue)\n- Bug #4954: MSSQL column comments are not retrieved correctly (SerjRamone)\n- Bug #4970: `joinWith()` called by a relation was ignored by `yii\\db\\ActiveQuery` (stepanselyuk)\n- Bug #5001: `yii\\rest\\CreateAction`, `yii\\rest\\UpdateAction` and `yii\\rest\\DeleteAction` should throw 500 error if the model operation returns false without validation errors (qiangxue)\n- Bug #5039: `UniqueValidator` and `ExistValidator` did not respect query conditions added by default scope (qiangxue)\n- Bug #5049: `yii\\widgets\\ActiveForm::validationDelay` should be applied to user types only (qiangxue)\n- Bug #5055: Fixed `yii\\console\\controllers\\CacheController` does not check if cache component instance of 'yii\\caching\\Cache' (klimov-paul)\n- Bug #5126: Fixed text body and charset not being set for multipart mail (nkovacs)\n- Bug: Fixed inconsistent return of `yii\\console\\Application::runAction()` (samdark)\n- Bug: URL encoding for the route parameter added to `yii\\web\\UrlManager` (klimov-paul)\n- Bug: Fixed the bug that requesting protected or private action methods would cause 500 error instead of 404 (qiangxue)\n- Bug: Fixed Object of class Imagick could not be converted to string in `yii\\captcha\\CaptchaAction` (eXprojects, cebe)\n- Bug: Fixed wrong behavior of `yii\\helpers\\StringHelper::byteSubstr()` in some edge cases (cebe)\n- Enh #87: Helper `yii\\helpers\\Security` converted into application component, cryptographic strength improved (klimov-paul)\n- Enh #422: Added Support for BIT(M) data type default values in Schema (cebe)\n- Enh #1160: Added $strict parameter to `yii\\helpers\\Inflector::camel2id()` to handle consecutive uppercase chars (schmunk)\n- Enh #1249: Added support for Active Record relation via array attributes (klimov-paul, cebe)\n- Enh #1388: Added mapping from physical types to abstract types for OCI DB driver (qiangxue)\n- Enh #1452: Added `yii\\base\\Module::getInstance()` to allow accessing the module instance from anywhere within the module (qiangxue)\n- Enh #2264: `yii\\web\\CookieCollection::has()` will return false for expired or removed cookies (qiangxue)\n- Enh #2315: Any operator now could be used with `yii\\db\\Query::where()` operand format (samdark)\n- Ehn #2380: Added `yii\\widgets\\ActiveForm::enableClientScript` to support turning on and off client side script generation (qiangxue)\n- Enh #2435: `yii\\db\\IntegrityException` is now thrown on database integrity errors instead of general `yii\\db\\Exception` (samdark)\n- Enh #2558: Enhanced support for memcached by adding `yii\\caching\\MemCache::persistentId` and `yii\\caching\\MemCache::options` (qiangxue)\n- Enh #2837: Error page now shows arguments in stack trace method calls (samdark)\n- Enh #2906: Added support for using conditional comments for js and css files registered through asset bundles and `yii\\helpers\\Html` helper (exromany, qiangxue)\n- Enh #2942: Added truncate and truncateWord methods (Alex-Code, samdark)\n- Enh #3008: Added `yii\\helpers\\Html::errorSummary()` (qiangxue)\n- Enh #3088: The debug and gii modules will manage their own URL rules now (hiltonjanfield, qiangxue)\n- Enh #3101: Improved handling of log target failures. It will now skip target and log reason instead of going into infinite cycle (samdark)\n- Enh #3103: debugger panel is now not displayed when printing a page (githubjeka)\n- Enh #3108: Added `yii\\debug\\Module::enableDebugLogs` to disable logging debug logs by default (qiangxue)\n- Enh #3132: `yii\\rbac\\PhpManager` now supports more compact data file format (qiangxue)\n- Enh #3154: Added validation error display for `GridView` filters (ivan-kolmychek)\n- Enh #3177: `yii\\filters\\auth\\CompositeAuth` will send out challenges from all auth methods (qiangxue)\n- Enh #3196: Masked input upgraded to use jquery.inputmask plugin with more features. (kartik-v)\n- Enh #3220: Added support for setting transaction isolation levels (cebe)\n- Enh #3221: Added events for DB transaction commit/rollback (drcypher, qiangxue)\n- Enh #3222: Added `useTablePrefix` option to the model generator for Gii (horizons2)\n- Enh #3230: Added `yii\\filters\\AccessControl::user` to support access control with different actors (qiangxue)\n- Enh #3232: Added `export()` and `exportAsString()` methods to `yii\\helpers\\BaseVarDumper` (klimov-paul)\n- Enh #3240: Added support for assigning an anonymous function to `yii\\widgets\\ActiveForm::fieldConfig` (qiangxue)\n- Enh #3244: Allow logging complex data such as arrays and object via the log system (cebe)\n- Enh #3252: Added support for case insensitive matching using ILIKE to PostgreSQL QueryBuilder (cebe)\n- Enh #3280: Support dynamically attaching anonymous behaviors (qiangxue)\n- Enh #3283: Added `$checkAjax` to `yii\\web\\User::loginRequired()` (qiangxue)\n- Enh #3284: Added support for checking multiple ETags by `yii\\filters\\HttpCache` (qiangxue)\n- Enh #3298: Supported configuring `View::theme` using a class name (netyum, qiangxue)\n- Enh #3328: `yii\\mail\\BaseMailer` generates better text body from html body (armab)\n- Enh #3380: Allow `value` in `defaultValueValidator` to be a closure (Alex-Code)\n- Enh #3384: Added callback-style transactions (leandrogehlen, Ragazzo, samdark)\n- Enh #3399, #3241: Added support for MS SQL Server older than 2012 (fourteenmeister, samdark)\n- Enh #3410: `yii.activeForm.js` now supports adding/removing fields dynamically (qiangxue)\n- Enh #3459: Added logging of errors, which may occur at `yii\\caching\\FileCache::gc()` (klimov-paul)\n- Enh #3472: Added configurable option to encode spaces in dropDownLists and listBoxes (kartik-v)\n- Enh #3518: `yii\\helpers\\Html::encode()` now replaces invalid code sequences with \"�\" (DaSourcerer)\n- Enh #3520: Added `unlinkAll()` method to Active Record to remove all records of a model relation (NmDimas, samdark, cebe)\n- Enh #3521: Added `yii\\filters\\HttpCache::sessionCacheLimiter` (qiangxue)\n- Enh #3542: Removed requirement to specify `extensions` in application config (samdark)\n- Enh #3562: Adding rotateByCopy to `yii\\log\\FileTarget` (pawzar)\n- Enh #3574: Add integrity check support for SQLite (zeeke)\n- Enh #3581: Added `yii\\validators\\CompareValidator::type` to support type conversion before comparing values (qiangxue)\n- Enh #3597: Nested array support for HTML5 custom `data-*` attributes (armab)\n- Enh #3607: Added support for limit in migrations actions: history, new, redo (Ragazzo)\n- Enh #3631: Added property `currencyCode` to `yii\\i18n\\Formatter` (leandrogehlen)\n- Enh #3636: Hide menu container tag with empty items in `yii\\widgets\\Menu` (arturf)\n- Enh #3643: Improved Mime-Type detection by using the `mime.types` file from apache http project to dected mime types by file extension (cebe, pavel-voronin, trejder)\n- Enh #3765: Added `yii\\web\\User::enableSession` to support authentication without using session (qiangxue)\n- Enh #3708: Added database replication and automatic read-write splitting support for `yii\\db\\Connection` (qiangxue)\n- Enh #3773: Added `yii\\validators\\FileValidator::mimeTypes` to support validating MIME types of files (Ragazzo)\n- Enh #3774: Added `yii\\validators\\FileValidator::checkExtensionByMimeType` to support validating file types against file mime-types (Ragazzo)\n- Enh #3801: Base migration controller `yii\\console\\controllers\\BaseMigrateController` extracted (klimov-paul)\n- Enh #3811: Now Gii model generator makes autocomplete for model class field (mitalcoi)\n- Enh #3926: `yii\\widgets\\Breadcrumbs::$links`. Allows individual link to have its own `template` (creocoder, umneeq)\n- Enh #3939: `yii\\helpers\\Inflector::slug()` improvements (samdark)\n    - Added protected `yii\\helpers\\Inflector::transliterate()` that could be replaced with custom translit implementation.\n    - Added proper tests for both intl-based slug and PHP fallback.\n    - Removed character maps for non-latin languages.\n    - Improved overall slug results.\n    - Added note about the fact that intl is required for non-latin languages to requirements checker.\n- Enh #3957: Added more straightforward configurable properties to `yii\\behaviors\\BlameableBehavior`, `yii\\behaviors\\SluggableBehavior` and `yii\\behaviors\\TimestampBehavior` (creocoder)\n- Enh #3992: In mail layouts you can now access the message object via `$message` variable (qiangxue)\n- Enh #4028: Added ability to `yii\\widgets\\Menu` to encode each item's label separately (creocoder, umneeq)\n- Enh #4048: Added `init` event to `yii\\bd\\ActiveQuery` classes (qiangxue)\n- Enh #4072: `yii\\rbac\\PhpManager` adjustments (samdark)\n    - Data is now stored in three separate files for items, assignments and rules. File format is simpler.\n    - Removed `authFile`. Added `itemFile`, `assignmentFile` and `ruleFile`.\n    - `createdAt` and `updatedAt` are now properly filled with corresponding file modification time.\n    - `save()` and `load()` are now protected instead of public.\n    - Added unit test for saving and loading data.\n- Enh #4080: Added proper handling and support of the symlinked directories in `yii\\helpers\\FileHelper`, added $options parameter in `yii\\helpers\\FileHelper::removeDirectory()` (resurtm)\n- Enh #4086: `changedAttributes` of `afterSave` Event now contain old values (dizews)\n- Enh #4114: Added `yii\\helpers\\Security::generateRandomBytes()`, improved tests (samdark)\n- Enh #4122: `yii\\helpers\\Html::error()` and `yii\\helpers\\Html::errorSummary()` are now accepting `encode` option. If set to false it prevents encoding of error messages (samdark)\n- Enh #4131: Security adjustments (tom--)\n     - Added HKDF to `yii\\base\\Security`.\n     - Reverted auto fallback to PHP PBKDF2.\n     - Fixed PBKDF2 key truncation.\n     - Adjusted API.\n- Enh #4209: Added `beforeCopy`, `afterCopy`, `forceCopy` properties to `yii\\web\\AssetManager` (cebe)\n- Enh #4225: Added `yii\\widgets\\ActiveForm::validateOnBlur` and `yii\\widgets\\ActiveField::validateOnBlur` (qiangxue)\n- Enh #4297: Added check for DOM extension to requirements (samdark)\n- Enh #4317: Added `absoluteAuthTimeout` to `yii\\web\\User` (ivokund, nkovacs)\n- Enh #4360: Added client validation support for file validator (Skysplit)\n- Enh #4372: `yii\\filters\\HttpCache` failed to comply to RFC 7232 (DaSourcerer)\n- Enh #4424: Added `inline` parameter to `yii\\web\\Response::xSendFile()` (klimov-paul)\n- Enh #4436: Added callback functions to AJAX-based form validation (thiagotalma)\n- Enh #4485: Added support for deferred validation in `yii\\widgets\\ActiveForm` (Alex-Code)\n- Enh #4520: Added sasl support to `yii\\caching\\MemCache` (xjflyttp)\n- Enh #4566: Added client validation support for image validator (Skysplit, qiangxue)\n- Enh #4581: Added ability to disable URL encoding in `yii\\web\\UrlRule` (tadaszelvys)\n- Enh #4602: Added $key param in `yii\\grid\\ActionColumn` buttons Closure call (disem)\n- Enh #4607: AR model will throw an exception if it does not have a primary key to avoid updating/deleting data massively (qiangxue)\n- Enh #4630: Added automatic generating of unique slug value to `yii\\behaviors\\Sluggable` (klimov-paul)\n- Enh #4636: Added `yii\\web\\Response::setDownloadHeaders()` (pawzar)\n- Enh #4644: Added `yii\\db\\Schema::createColumnSchema()` to be able to customize column schema used (mcd-php)\n- Enh #4656: HtmlPurifier helper config can now be a closure to change the purifier config object after it was created (Alex-Code)\n- Enh #4062: Added 'caseSensitive' option to `yii\\helpers\\BaseFileHelper::findFiles()` (klimov-paul)\n- Enh #4691: Encoding on `yii\\widgets\\ActiveForm` and `yii\\widgets\\ActiveField` validation errors is now configurable (Alex-Code)\n- Enh #4740: Added `yii\\web\\Session::addFlash()` (restyler)\n- Enh #4897: Added `yii\\helpers\\FileHelper::mimeMagicFile` (qiangxue)\n- Enh #5058: Added `$pageSize` parameter to `yii\\data\\Pagination::createUrl()` to allow creating URLs with arbitrary page sizes (cdcchen, qiangxue)\n- Enh #5089: Added asset debugger panel (arturf, qiangxue)\n- Enh #5117: Added `beforeFilter` and `afterFilter` JS events to `GridView` (kartik-v)\n- Enh #5124: Added support to prevent duplicated form submission when using `yii\\widgets\\ActiveForm` (qiangxue)\n- Enh #5131: Added `$autoRenew` parameter to `yii\\web\\User::getIdentity()` (qiangxue)\n- Enh #5164: Added `yii\\helpers\\Inlfector::$transliterator` that can be used to customize intl transliteration (zinzinday)\n- Enh: Added support for using sub-queries when building a DB query with `IN` condition (qiangxue)\n- Enh: Supported adding a new response formatter without the need to reconfigure existing formatters (qiangxue)\n- Enh: Added `yii\\web\\UrlManager::addRules()` to simplify adding new URL rules (qiangxue)\n- Enh: Added support to insert an event handler at the beginning of class-level event handler queue (qiangxue)\n- Enh: Added `yii\\console\\Controller::EXIT_CODE_NORMAL` and `yii\\console\\Controller::EXIT_CODE_ERROR` constants (samdark)\n- Enh: `yii\\console\\MigrateController` now returns `yii\\console\\Controller::EXIT_CODE_ERROR` in case of failed migration (samdark)\n- Enh: Added method `yii\\base\\ErrorHandler::unregister()`` for unregistering the ErrorHandler (cebe)\n- Enh: Added `all` option to `yii\\console\\controllers\\MigrateController::actionDown()` action (creocoder, umneeq)\n- Enh: Added support for array attributes in `exist` validator (creocoder)\n- Enh: Added support for using path alias with `FileDependency::fileName` (qiangxue)\n- Enh: Added param `hideOnSinglePage` to `yii\\widgets\\LinkPager` (arturf)\n- Enh: Added support for array attributes in `in` validator (creocoder)\n- Enh: Improved `yii\\helpers\\Inflector::slug()` to support more cases for Russian, Hebrew and special characters (samdark)\n- Enh: ListView now uses the widget ID in the base tag, consistent to gridview (cebe)\n- Enh: Added `yii\\web\\Response::enableCsrfCookie` to support storing CSRF tokens in session (qiangxue)\n- Chg #2287: Split `yii\\db\\ColumnSchema::typecast()` into two methods `phpTypecast()` and `dbTypecast()` to allow specifying PDO type explicitly (cebe)\n- Chg #2359: Refactored formatter class. One class with or without intl extension and PHP format pattern as standard (Erik_r, cebe)\n   - `yii\\base\\Formatter` functionality has been merged into `yii\\i18n\\Formatter`\n   - removed the `yii\\base\\Formatter` class\n- Chg #1551: Refactored `yii\\validators\\DateValidator `to support ICU date format and use the format defined in `yii\\i18n\\Formatter` by default (cebe)\n- Chg #2898: `yii\\console\\controllers\\AssetController` is now using hashes instead of timestamps (samdark)\n- Chg #2913: RBAC `yii\\rbac\\DbManager` is now initialized via migration (samdark)\n- Chg #2914: `yii\\widgets\\ActiveForm::fieldConfig` will be merged recursively with the `$options` parameter in `yii\\widgets\\ActiveForm::field()` (qiangxue)\n- Chg #3036: Upgraded Twitter Bootstrap to 3.1.x (qiangxue)\n- Chg #3175: `yii\\base\\InvalidCallException`, `yii\\base\\InvalidParamException`, `yii\\base\\UnknownMethodException` are now extended from SPL `BadMethodCallException` (samdark)\n- Chg #3358: Removed automatic CSRF meta tag generation by `View`. Added `yii\\helpers\\Html::csrfMetaTags()` and its call to main layout files (qiangxue)\n- Chg #3383: Added `$type` parameter to `IdentityInterface::findIdentityByAccessToken()` (qiangxue)\n- Chg #3511: Dropped `yii.allowAction()` and modified `yii.confirm()` in `yii.js` to support callbacks (tanakahisateru)\n- Chg #3531: `yii\\grid\\GridView` now allows any character (except \":\") in the attribute part of the shorthand syntax for columns (rawtaz)\n- Chg #3544: Added `$key` as a parameter to the callable specified via `yii\\grid\\DataColumn::value` (mdmunir)\n- Chg #3611: Query caching is refactored. (qiangxue)\n    - `yii\\db\\Connection::beginCache()` and `endCache()` are removed.\n    - Added `yii\\db\\Connection::cache()` and `noCache()`.\n    - Added `Command::cache()` and `noCache()`.\n    - `yii\\db\\Connection::queryCacheDuration` is now used as a default cache duration parameter.\n- Chg #3640: All cookies are now httpOnly by default in order to increase overall security (samdark)\n- Chg #3687: Default `sourceLanguage` and `language` are now `en-US` in order for i18n formatter to work correctly (samdark)\n- Chg #3804: Added `fileinfo` PHP extension to the basic requirement of Yii (Ragazzo)\n- Chg #3866: The `yii\\validators\\FileValidator::types` property is renamed to `yii\\validators\\FileValidator::extensions` (Ragazzo)\n- Chg #3897: Raised visibility of `yii\\web\\View::registerAssetFiles()` to protected (samdark)\n- Chg #3899: Moved `MailEvent` class to `yii\\mail` namespace (cebe)\n- Chg #3910: Removed the `container` option from `yii\\helpers\\Html::checkbox()` and `yii\\helpers\\Html::radio()` (creocoder)\n- Chg #3956: Flash messages set via `Yii::$app->session->setFlash()` will be removed only if they are accessed (qiangxue)\n- Chg #3989: The default value for `yii\\log\\FileTarget::$rotateByCopy` now defaults to true to work on windows by default (cebe)\n- Chg #4051: Renamed `yii\\caching\\GroupDependency` to `TagDependency` and added support for associating multiple tags to a single cached data item (qiangxue)\n- Chg #4071: `mail` component renamed to `mailer`, `yii\\log\\EmailTarget::$mail` renamed to `yii\\log\\EmailTarget::$mailer` (samdark)\n- Chg #4147: `yii\\mail\\BaseMailer::compose()` will not overwrite the `message` parameter if it is explicitly provided (qiangxue)\n- Chg #4188: API exceptions are now exposing less data when YII_DEBUG is false (samdark)\n- Chg #4201: change default value of `SyslogTarget::facility` from `LOG_SYSLOG` to `LOG_USER` (dizews)\n- Chg #4211: `yii\\db\\BaseActiveRecord::populateRecord()` now silently hide selected columns that are not defined in AR instead of failing with an error (miramir)\n- Chg #4227: `yii\\widgets\\LinkPager::hideOnSinglePage` is now `true` by default (samdark)\n- Chg #4310: Removed `$data` from signature of `yii\\rbac\\ManagerInterface` (samdark)\n- Chg #4318: `yii\\helpers\\Html::ul()` and `ol()` will return an empty list tag if an empty item array is given (qiangxue)\n- Chg #4331: `yii\\helpers\\Url` now uses `UrlManager` to determine base URL when generating URLs (qiangxue)\n- Chg #4424: Added `inline` and `mimeType` options to all file downloading methods provided in `yii\\web\\Response` (qiangxue)\n- Chg #4454: Improved asset bundle managed and used composer-asset-plugin to manage the dependencies on 3rd-party JS libraries (qiangxue)\n- Chg #4501: Renamed the constant `YII_PATH` to `YII2_PATH` (qiangxue)\n- Chg #4586: Signed bigint and unsigned int will be converted into integers when they are loaded from DB by AR (qiangxue)\n- Chg #4591: `yii\\helpers\\Url::to()` will no longer prefix relative URLs with the base URL (qiangxue)\n- Chg #4595: `yii\\widgets\\LinkPager`'s `nextPageLabel`, `prevPageLabel`, `firstPageLabel`, `lastPageLabel` are now taking `false` instead of `null` for \"no label\" (samdark)\n- Chg #4911: Changed callback signature used in `yii\\base\\ArrayableTrait::fields()` from `function ($field, $model) {` to `function ($model, $field) {` (samdark)\n- Chg #4955: Replaced callbacks with events for `yii\\widgets\\ActiveForm` (qiangxue)\n    - Removed `beforeValidate()`, `beforeValidateAll()`, `afterValidate()`, `afterValidateAll()`, `ajaxBeforeSend()` and `ajaxComplete()` from `yii\\widgets\\ActiveForm`.\n    - Added `beforeValidate`, `afterValidate`, `beforeValidateAttribute`, `afterValidateAttribute`, `beforeSubmit`, `ajaxBeforeSend` and `ajaxComplete` events to `yii.activeForm` jQuery plugin.\n- Chg #5176: `ActiveFixture` will reset table in its `load()` method instead of `unload()` (qiangxue)\n- Chg: Replaced `clearAll()` and `clearAllAssignments()` in `yii\\rbac\\ManagerInterface` with `removeAll()`, `removeAllRoles()`, `removeAllPermissions()`, `removeAllRules()` and `removeAllAssignments()` (qiangxue)\n- Chg: Added `$user` as the first parameter of `yii\\rbac\\Rule::execute()` (qiangxue)\n- Chg: `yii\\grid\\DataColumn::getDataCellValue()` visibility is now `public` to allow accessing the value from a GridView directly (cebe)\n- Chg: `yii\\data\\ActiveDataProvider::$query` will not be modified directly with pagination and sorting anymore so it will be reuseable (cebe)\n- Chg: Removed `yii\\rest\\ActiveController::$transactional` property and connected functionality (samdark)\n- Chg: Changed the default value of the `keyPrefix` property of cache components to be null (qiangxue)\n- Chg: Added `prefix` column to `yii\\log\\DbTarget` to have the same amount of information logged as in files and emails (cebe)\n- Chg: Use `limit(null)` instead of `limit(-1)` in migration controller to be compatible to more backends (cebe)\n- Chg: `yii\\web\\Request::cookieValidationKey` must be explicitly specified for each application that wants to use cookie validation (qiangxue)\n- Chg: Added `yii\\composer\\Installer::postCreateProject()` and modified the syntax of calling installer methods in composer.json (qiangxue)\n- Chg: When an ID is found to be in both `Application::controllerMap` and `Application::modules`, the former will take precedence (qiangxue)\n- Chg: `yii\\helpers\\Html::activeCheckbox()` and `activeRadio()` will generate labels by default using the corresponding attribute labels (qiangxue)\n- New #1280: Gii can now be run from command line (schmunk42, cebe, qiangxue)\n- New #3911: Added `yii\\behaviors\\SluggableBehavior` that fills the specified model attribute with the transliterated and adjusted version to use in URLs (creocoder)\n- New #4193: Added `yii\\filters\\Cors` CORS filter to allow Cross Origin Resource Sharing (pgaultier)\n- New #4945: Added `yii\\test\\ArrayFixture` (Ragazzo)\n- New: Added `yii\\base\\InvalidValueException` (qiangxue)\n- New: Added `yii\\caching\\ArrayCache` (cebe)\n\n\n2.0.0-beta April 13, 2014\n-------------------------\n\n- Bug #1265: `yii\\console\\controllers\\AssetController` does not override 'js' and 'css' for compressed bundles (klimov-paul)\n- Bug #1326: The `visible` setting for `yii\\widgets\\DetailView` doesn't work as expected (qiangxue)\n- Bug #1412: `yii\\validators\\FileValidator` and `yii\\validators\\ImageValidator` still trigger `uploadRequired` error in some case when `skipOnEmpty` is true and no upload is provided (qiangxue)\n- Bug #1446: Logging while logs are processed causes infinite loop (qiangxue)\n- Bug #1497: Localized view files are not correctly returned (mintao)\n- Bug #1500: Log messages exported to files are not separated by newlines (omnilight, qiangxue)\n- Bug #1504: Debug toolbar isn't loaded successfully in some environments when xdebug is enabled (qiangxue)\n- Bug #1509: The SQL for creating Postgres RBAC tables is incorrect (qiangxue)\n- Bug #1545: It was not possible to execute DB Query twice, params where missing (cebe)\n- Bug #1550: fixed the issue that JUI input widgets did not property input IDs.\n- Bug #1654: Fixed the issue that a new message source object is generated for every new message being translated (qiangxue)\n- Bug #1582: Error messages shown via client-side validation should not be double encoded (qiangxue)\n- Bug #1591: `yii\\validators\\StringValidator` is accessing undefined property (qiangxue)\n- Bug #1597: Added `enableAutoLogin` to basic and advanced project templates so \"remember me\" now works properly (samdark)\n- Bug #1631: Charset is now explicitly set to UTF-8 when serving JSON (samdark)\n- Bug #1635: `yii\\jui\\SliderInput` wasn't properly initialized (samdark)\n- Bug #1659: MSSQL doesn't support limit (Ana1oliy)\n- Bug #1686: `yii\\widgets\\ActiveForm` is creating duplicated messages in error summary (qiangxue)\n- Bug #1704: Incorrect regexp is used in `yii\\helpers\\Inflector::camelize()` (qiangxue)\n- Bug #1710: OpenId auth client does not request required attributes correctly (klimov-paul)\n- Bug #1798: Fixed label attributes for array fields (zhuravljov)\n- Bug #1800: Better check for `$_SERVER['HTTPS']` in `yii\\web\\Request::getIsSecureConnection()` (ginus, samdark)\n- Bug #1812: Hide potential warning message due to race condition occurring to `Session::regenerateID()` call (qiangxue)\n- Bug #1827: Debugger toolbar is loaded twice if an action is calling `run()` to execute another action (qiangxue)\n- Bug #1868: Added ability to exclude tables from `yii\\console\\controllers\\FixtureController` apply/clear actions. (Ragazzo)\n- Bug #1869: Fixed tables clearing. `TRUNCATE` changed to `DELETE` to avoid PostgreSQL tables checks (and truncating all tables) (Ragazzo)\n- Bug #1870: Validation errors weren't properly translated when using clientside validation (samdark)\n- Bug #1930: Fixed domain based URL matching for website root (samdark)\n- Bug #1937: Fixed wrong behavior or advanced app's `init --env` when called without parameter actually specified (samdark)\n- Bug #1959: `yii\\helpers\\Html::activeCheckbox()` wasn't respecting custom values for checked/unchecked state (klevron, samdark)\n- Bug #1965: `yii\\base\\Controller::findLayoutFile()` returns incorrect file path when layout name starts with a slash (qiangxue)\n- Bug #1992: In module scenario that use 'site/captcha' will get wrong refreshUrl (callmez)\n- Bug #1993: afterFind event in AR is now called after relations have been populated (cebe, creocoder)\n- Bug #1998: Unchecked required checkbox never pass client validation (klevron)\n- Bug #2084: AssetController adjusting CSS URLs declared at same line fixed (klimov-paul)\n- Bug #2091: `yii\\db\\QueryBuilder::buildInCondition()` fails to handle array not starting with index 0 (qiangxue)\n- Bug #2160: SphinxQL does not support OFFSET (qiangxue, romeo7)\n- Bug #2209: When I18N message translation is missing source language is now used for formatting (samdark)\n- Bug #2212: `yii\\gridview\\DataColumn` generates incorrect labels when used with nosql DB and there is no data (qiangxue)\n- Bug #2298: Fixed the bug that Gii controller generator did not allow digit in the controller ID (qiangxue)\n- Bug #2303: Fixed the bug that `yii\\base\\Theme::pathMap` did not support dynamic update with path aliases (qiangxue)\n- Bug #2324: Fixed QueryBuilder bug when building a query with \"query\" option (mintao)\n- Bug #2399: Fixed the bug that AssetBundle did not handle relative URLs correctly (qiangxue)\n- Bug #2502: Unclear error message when `$_SERVER['DOCUMENT_ROOT']` is empty (samdark)\n- Bug #2519: MessageSource removed translation messages when event handler was bound to `missingTranslation`-event (cebe)\n- Bug #2527: Source language for `app` message category was always `en` no matter which application `sourceLanguage` was used (samdark)\n- Bug #2559: Going back on browser history breaks `yii\\widgets\\GridView` filtering with `Pjax` (tonydspaniard)\n- Bug #2571: Fixed the bug that batchInsert will fail for SQLite if the values contain null or boolean false (qiangxue)\n- Bug #2607: `yii message` tool wasn't updating `message` table (mitalcoi)\n- Bug #2624: `yii\\helpers\\Html::textArea()`` should respect `name` option. (qiangxue)\n- Bug #2653: Fixed the bug that unsetting an unpopulated AR relation would trigger exception (qiangxue)\n- Bug #2681: Fixed the bug of php build-in server `https://bugs.php.net/bug.php?id=66606` (dizews)\n- Bug #2683: Fixed the bug that batchInsert will fail for MySQL if the values contain boolean false (qiangxue)\n- Bug #2695: Fixed the issue that `yii\\validators\\FileValidator::isEmpty()` always returns true for validate multiple files (ZhandosKz)\n- Bug #2739: Fixed the issue that `yii\\rest\\CreateAction::run()` was using obsolete `yii\\web\\Controller::createAbsoluteUrl()` method (tonydspaniard)\n- Bug #2740: Fixed the issue that `yii\\captcha\\CaptchaAction::run()` was using obsolete `yii\\web\\Controller::createUrl()` method (tonydspaniard)\n- Bug #2760: Fixed `yii\\grid\\GridView` `filterUrl` parameters (qiangxue, AlexGx)\n- Bug #2834: When overriding i18n translation sources from config using `app*` or `yii*` default `app` and `yii` sources were not removed (samdark)\n- Bug #2848: Individual queries should be enclosed within parenthesis in a UNION query (qiangxue)\n- Bug #2862: Using `yii\\cache\\DbCache` while enabling schema caching may cause infinite loops (qiangxue)\n- Bug #3052: Fixed the issue that cache dependency data is not reused when `reusable` is set true (qiangxue)\n- Bug #3443: Fixed `yii\\bootstrap\\Nav` and `yii\\bootstrap\\Dropdown` were generating wrong ids for submenus (arturf)\n- Bug #3691: Fixed the issue that `yii\\web\\CookieCollection::has()` always returns false for cookies from browser (sonicgd)\n- Bug #4212: MSSQL query builder should not generate the `ORDER BY` clause when it is not needed (qiangxue)\n- Bug #4232: `yii\\db\\TableSchema::sequenceName` for PostgreSQL should remove the enclosing quotes (katzz0, qiangxue)\n- Bug #4697: MSSQL query builder does not work for newer MSSQL versions when LIMIT is used without ORDER BY (qiangxue)\n- Bug: Fixed `Call to a member function `registerAssetFiles()` on a non-object` in case of wrong `sourcePath` for an asset bundle (samdark)\n- Bug: Fixed incorrect event name for `yii\\jui\\Spinner` (samdark)\n- Bug: `yii\\helpers\\Json::encode()`` did not handle objects that implement `JsonSerializable` interface correctly (cebe)\n- Bug: Fixed issue with tabular input on `yii\\widgets\\ActiveField::radio()` and `yii\\widgets\\ActiveField::checkbox()` (jom)\n- Bug: Fixed the issue that query cache returns the same data for the same SQL but different query methods (qiangxue)\n- Bug: Fixed URL parsing so it's now properly giving 404 for URLs like `https://example.com//////site/about` (samdark)\n- Bug: Fixed `yii\\console\\controllers\\HelpController::getModuleCommands()` issue where it attempts to scan a module's controller directory when it doesn't exist (jom)\n- Bug: Fixed an issue with FileHelper and not accessible directories which resulted in endless loop (cebe)\n- Bug: Fixed `yii\\base\\Model::load()` returned `true` if `$data` and `formName` were empty (samdark)\n- Bug: Fixed issue with `yii\\db\\ActiveRelationTrait` preventing `yii\\db\\ActiveQuery` from clearing events and behaviors on clone (jom)\n- Bug: `yii\\db\\Query::queryScalar()` wasn't making `SELECT DISTINCT` queries subqueries (jom)\n- Bug: Fixed use `$files` instead of `self::$_files[$key]` to allow inheritance (pgaultier)\n- Enh #46: Added Image extension based on [Imagine library](https://imagine.readthedocs.io/) (tonydspaniard)\n- Enh #364: Improve `yii\\helpers\\Inflector::slug()` with `intl` transliteration. Improved transliteration char map. (tonydspaniard)\n- Enh #497: Removed `yii\\log\\Target::logUser` and added `yii\\log\\Target::prefix` to support customizing message prefix (qiangxue)\n- Enh #499: Decoupled `Rule` from RBAC `Item` (samdark, qiangxue)\n- Enh #797: Added support for validating multiple columns by `yii\\validators\\UniqueValidator` and `yii\\validators\\ExistValidator` (qiangxue)\n- Enh #802: Added support for retrieving sub-array element or child object property through `ArrayHelper::getValue()` (qiangxue, cebe)\n- Enh #938: Added `yii\\web\\View::renderAjax()` and `yii\\web\\Controller::renderAjax()` (qiangxue)\n- Enh #1293: Replaced `yii\\helpers\\Console::showProgress()` with a better approach. See `yii\\helpers\\Console::startProgress()` for details (cebe)\n- Enh #1406: DB Schema support for Oracle Database (p0larbeer, qiangxue)\n- Enh #1437: Added `yii\\widgets\\ListView::viewParams` (qiangxue)\n- Enh #1467: Added support for organizing controllers in subdirectories (qiangxue)\n- Enh #1469: yii\\db\\ActiveRecord::find()` now works with default conditions (default scope) applied by createQuery (cebe)\n- Enh #1476: Add `yii\\web\\Session::handler` property (nineinchnick)\n- Enh #1499: Added `yii\\grid\\ActionColumn::controller` property to support customizing the controller for handling GridView actions (qiangxue)\n- Enh #1523: Query conditions now allow to use the NOT operator (cebe)\n- Enh #1535: Improved `yii\\web\\User` to start session only when needed. Also prepared it for use without session. (qiangxue)\n- Enh #1562: Added `yii\\bootstrap\\Tabs::linkOptions` (kartik-v)\n- Enh #1572: Added `yii\\web\\Controller::createAbsoluteUrl()` (samdark)\n- Enh #1579: throw exception when the given AR relation name does not match in a case sensitive manner (qiangxue)\n- Enh #1581: Added `yii\\db\\ActiveQuery::joinWith()` and `yii\\db\\ActiveQuery::innerJoinWith()` to support joining with relations (qiangxue)\n- Enh #1585: added schema parameter to `createAbsoluteUrl()` to force 'http' or 'https' (cebe)\n- Enh #1601: Added support for tagName and encodeLabel parameters in ButtonDropdown (omnilight)\n- Enh #1611: Added `yii\\db\\BaseActiveRecord::markAttributeDirty()` (qiangxue)\n- Enh #1633: Advanced project template now works with MongoDB by default (samdark)\n- Enh #1634: Use masked CSRF tokens to prevent BREACH exploits (qiangxue)\n- Enh #1641: Added `BaseActiveRecord::updateAttributes()` (qiangxue)\n- Enh #1646: Added PostgreSQL `yii\\db\\QueryBuilder::checkIntegrity()` and `yii\\db\\QueryBuilder::resetSequence()` (Ragazzo)\n- Enh #1645: Added `yii\\db\\Connection::$pdoClass` property (Ragazzo)\n- Enh #1645: Added support for nested DB transactions (qiangxue)\n- Enh #1681: Added support for automatically adjusting the \"for\" attribute of label generated by `yii\\widgets\\ActiveField::label()` (qiangxue)\n- Enh #1706: Added support for registering a single JS/CSS file with dependency (qiangxue)\n- Enh #1773: keyPrefix property of Cache is not restricted to alnum characters anymore, however it is still recommended (cebe)\n- Enh #1809: Added support for building \"EXISTS\" and \"NOT EXISTS\" query conditions (abdrasulov)\n- Enh #1839: Added support for getting file extension and basename from uploaded file (anfrantic)\n- Enh #1852: `yii\\db\\ActiveRecord::tableName()` now returns table name using `yii\\db\\Connection::tablePrefix` (creocoder)\n- Enh #1881: Improved `yii\\bootstrap\\NavBar` with `containerOptions`, `innerContainerOptions` and `renderInnerContainer` (creocoder)\n- Enh #1894: The path aliases `@webroot` and `@web` are now available right after the application is initialized (qiangxue)\n- Enh #1921: Grid view `yii\\grid\\ActionColumn` now allow to name buttons like `{controller/action}` (creocoder)\n- Enh #1973: `yii message/extract` is now able to generate `.po` files (SergeiKutanov, samdark)\n- Enh #1984: `yii\\base\\ActionFilter` will now mark event as handled when action run is aborted (cebe)\n- Enh #2002: Added `filterWhere()` method to yii\\db\\Query to allow easy addition of search filter conditions by ignoring empty search fields (samdark, cebe)\n- Enh #2003: Added `filter` property to `yii\\validators\\ExistValidator` and `yii\\validators\\UniqueValidator` to support adding additional filtering conditions (qiangxue)\n- Enh #2008: `yii message/extract` is now able to save translation strings to database (kate-kate, samdark)\n- Enh #2043: Added support for custom request body parsers (danschmidt5189, cebe)\n- Enh #2051: Do not save null data into database when using RBAC (qiangxue)\n- Enh #2054: Added support for using custom application configuration with the console command runner (qiangxue)\n- Enh #2079:\n    - i18n now falls back to `en` from `en-US` if message translation isn't found (samdark)\n    - View now falls back to `en` from `en-US` if file not found (samdark)\n    - Default `sourceLanguage` and `language` are now `en` (samdark)\n- Enh #2101: Gii is now using model labels when generating search (thiagotalma)\n- Enh #2102: `yii\\widgets\\DetailView` now allow use `category.name` as attribute name (creocoder)\n- Enh #2102: `yii\\widgets\\DetailView` now allow use custom label in string format like `name:format:label` (creocoder)\n- Enh #2103: Renamed `yii\\web\\AccessDeniedHttpException` to `yii\\web\\ForbiddenHttpException`, added new commonly used HTTP exception classes (danschmidt5189)\n- Enh #2124: Added support for UNION ALL queries (Ivan Pomortsev, iworker)\n- Enh #2132: Allow URL of CSS and JS files registered in yii\\web\\View to be URL alias (cebe)\n- Enh #2144: `Html` helper now supports rendering \"data\" attributes (qiangxue)\n- Enh #2156: `yii migrate` now automatically creates `migrations` directory if it does not exist (samdark)\n- Enh #2211: Added typecast database types into php types (dizews)\n- Enh #2240: Improved `yii\\web\\AssetManager::publish()`, `yii\\web\\AssetManager::getPublishedPath()` and `yii\\web\\AssetManager::getPublishedUrl()` to support aliases (vova07)\n- Enh #2325: Adding support for the `X-HTTP-Method-Override` header in `yii\\web\\Request::getMethod()` (pawzar)\n- Enh #2364: Take into account current error reporting level in error handler (gureedo)\n- Enh #2387: Added support for fetching data from database in batches (nineinchnick, qiangxue)\n- Enh #2392: Added `addCssStyle()`, `removeCssStyle()`, `cssStyleFromArray()` and `cssStyleToArray()` to `yii\\helpers\\Html` (qiangxue, kartik-v, Alex-Code)\n- Enh #2406: Added support for conditional validation (drenty, cebe, qiangxue)\n- Enh #2411: Added Gii extension generator (schmunk42)\n- Enh #2415: Added support for inverse relations (qiangxue)\n- Enh #2417: Added possibility to set `dataType` for `$.ajax` call in yii.activeForm.js (Borales)\n- Enh #2436: Label of the attribute, which looks like `relatedModel.attribute`, will be received from the related model if it available (djagya)\n- Enh #2490: `yii\\db\\Query::count()` and other query scalar methods now properly handle queries with GROUP BY clause (qiangxue)\n- Enh #2491: Added support for using the same base class name of search model and data model in Gii (qiangxue)\n- Enh #2499: Added ability to downgrade migrations by their absolute apply time (resurtm, gorcer)\n- Enh #2525: Added support for formatting file sizes with `yii\\base\\Formatter` (VinceG)\n- Enh #2526: Allow for null values in batchInsert (skotos)\n- Enh #2646: Added support for specifying hostinfo in the pattern of a URL rule (qiangxue)\n- Enh #2661: Added boolean column type support for SQLite (qiangxue)\n- Enh #2670: Changed `yii\\console\\Controller::globalOptions()` to `options($actionID)` to (make it possible to) differentiate options per action (hqx)\n- Enh #2714: Added support for formatting time intervals relative to the current time with `yii\\base\\Formatter` (drenty)\n- Enh #2726: Added `yii\\db\\ActiveRecord::loadDefaultValues()` that fills default values from DB schema (samdark)\n- Enh #2729: Added `yii\\validators\\FilterValidator::skipOnArray` so that filters like `trim` will not fail for array inputs (qiangxue)\n- Enh #2735: Added support for `DateTimeInterface` in `yii\\i18n\\Formatter` (ivokund)\n- Enh #2756: Added support for injecting custom `isEmpty` check for all validators (qiangxue)\n- Enh #2775: Added `yii\\base\\Application::bootstrap` and `yii\\base\\BootstrapInterface` to support running bootstrap classes when starting an application (qiangxue)\n- Enh #2892: ActiveRecord dirty attributes are now reset after call to `afterSave()` so information about changed attributes is available in `afterSave`-event (cebe)\n- Enh #2910: Added `yii\\base\\Application::end()` (qiangxue)\n- Enh: Added support for using arrays as option values for console commands (qiangxue)\n- Enh: Added `favicon.ico` and `robots.txt` to default project templates (samdark)\n- Enh: Added `yii\\base\\Widget::autoIdPrefix` to support prefixing automatically generated widget IDs (qiangxue)\n- Enh: Support for file aliases in console command 'message' (omnilight)\n- Enh: Sort and Pagination can now create absolute URLs (cebe)\n- Enh: Added support for using array-typed arguments for console commands (qiangxue)\n- Enh: Added support for installing packages conforming to PSR-4 standard (qiangxue)\n- Enh: Better exception message when class cannot be loaded (samdark)\n- Enh: `init` of advanced application now allows to specify answer for overwriting files via `init --overwrite=n` (samdark)\n- Enh: Added `yii\\db\\TableSchema::fullName` property (qiangxue)\n- Enh: `yii\\codeception\\TestCase` now supports loading and using fixtures via Yii fixture framework (qiangxue)\n- Enh: Added ability to get incoming headers (dizews)\n- Enh: Added `beforeRun()` and `afterRun()` to `yii\\base\\Action` (qiangxue)\n- Enh: Added support for using timeZone with `yii\\base\\Formatter` (dizews)\n- Enh: Added `yii\\web\\View::POS_LOAD` (qiangxue)\n- Enh: Added `yii\\web\\Response::clearOutputBuffers()` (qiangxue)\n- Enh: Improved `yii\\db\\QueryBuilder::buildLimit()` to support big numbers (qiangxue)\n- Enh: Added support for building SQLs with sub-queries (qiangxue)\n- Enh: Added `yii\\data\\Pagination::getLinks()` (qiangxue)\n- Enh: Added support for reading page size from query parameters by `yii\\data\\Pagination` (qiangxue)\n- Enh: LinkPager can now register relational link tags in the html header for prev, next, first and last page (cebe)\n- Enh: Added `yii\\web\\UrlRuleInterface` and `yii\\web\\CompositeUrlRule` (qiangxue)\n- Enh: Added `yii\\web\\Request::getAuthUser()` and `getAuthPassword()` (qiangxue)\n- Enh: Added summaryOptions and emptyTextOptions to `yii\\widgets\\BaseListView` (johonunu)\n- Enh: Implemented Oracle column comment reading from another schema (gureedo, samdark)\n- Enh: Added support to allow an event handler to be inserted at the beginning of the existing event handler list (qiangxue)\n- Enh: Improved action filter and action execution flow by supporting installing action filters at controller, module and application levels (qiangxue)\n- Enh: Added `isAssociative()` and `isIndexed()` to `yii\\helpers\\ArrayHelper` (qiangxue)\n- Enh: Added `addSelect` to `yii\\db\\Query` (Alex-Code)\n- Enh: Added ODBC support in `yii\\db\\Connection` (nineinchnick, resurtm)\n- Chg #47: Changed Markdown library to cebe/markdown and adjusted Markdown helper API (cebe)\n- Chg #735: Added back `yii\\widgets\\ActiveField::hiddenInput()` (qiangxue)\n- Chg #1186: Changed `Sort` to use comma to separate multiple sort fields and use negative sign to indicate descending sort (qiangxue)\n- Chg #1519: `yii\\web\\User::loginRequired()` now returns the `Response` object instead of exiting the application (qiangxue)\n- Chg #1564: Removed `yii\\web\\Session::autoStart` and added `hasSessionId`. Session will be automatically started when accessing session data (qiangxue)\n- Chg #1586: `yii\\db\\QueryBuilder::buildLikeCondition()` will now escape special characters and use percentage characters by default (qiangxue)\n- Chg #1610: `yii\\helpers\\Html::activeCheckboxList()` and `Html::activeRadioList()` will submit an empty string if no checkbox/radio is selected (qiangxue)\n- Chg #1643: Added default value for `yii\\captcha\\Captcha::options` (qiangxue)\n- Chg #1796: Removed `yii\\base\\Controller::getActionParams()` (samdark)\n- Chg #1835: `yii\\grid\\CheckboxColumn` now renders checkboxes whose values are the corresponding data key values (qiangxue)\n- Chg #1821: Changed default values for yii\\db\\Connection username and password to null (cebe)\n- Chg #1844: `yii\\web\\Response::sendFile()` and other file sending methods will not send the response (qiangxue)\n- Chg #1852: `yii\\db\\Connection::tablePrefix` default value now `tbl_` (creocoder)\n- Chg #1958: `beforeSubmit` in `yii.activeform` is now executed after validation and before form submission (6pblcb)\n- Chg #2025: Removed ability to declare scopes in ActiveRecord (samdark)\n- Chg #2043:\n    - Renamed `yii\\web\\Request::acceptedLanguages` to `acceptableLanguages` (qiangxue)\n    - Removed `yii\\web\\Request::getPost()`, `getPut()`, `getDelete()`, `getPatch()` in favor of `getBodyParam()` (cebe)\n    - Renamed `yii\\web\\Request::get()` to `getQueryParams()` and `getRestParams()` to `getBodyParams()` (cebe)\n    - Added `yii\\web\\Request::get($name = null, $defaultValue = null)` and `yii\\web\\Request::post($name = null, $defaultValue = null)` (samdark)\n- Chg #2059: Implemented git-flavored file excluding/filtering for `yii\\helpers\\FileHelper` (nineinchnick)\n- Chg #2063: Removed `yii\\web\\Request::acceptTypes` and renamed `yii\\web\\Request::acceptedContentTypes` to `acceptableContentTypes` (qiangxue)\n- Chg #2103: Renamed `yii\\web\\AccessDeniedHttpException` to `yii\\web\\ForbiddenHttpException` (danschmidt5189)\n- Chg #2146: Removed `yii\\db\\ActiveRelation` class and `yii\\db\\ActiveRelationInterface`, moved the functionality to `yii\\db\\ActiveQuery`.\n             All relational queries are now directly served by `ActiveQuery` allowing to use custom scopes in relations\n             and also to declare arbitrary queries as relations.\n             Also removed `yii\\db\\ActiveRecordInterface::createActiveRelation()` (cebe)\n- Chg #2157: The `*` category pattern will match all categories that do not match any other patterns listed in `yii\\i18n\\I18N::translations` (qiangxue, Ragazzo)\n- Chg #2161: Added ability to use `return` in `Widget::run` (samdark)\n- Chg #2173: Removed `yii\\helpers\\StringHelper::diff()`, Moved `phpspec/php-diff` dependency from `yiisoft/yii2` to `yiisoft/yii2-gii` (samdark)\n- Chg #2175: `yii\\db\\QueryBuilder` will now append UNION statements at the end of the primary SQL (qiangxue)\n- Chg #2210: MySQL driver will now treat `tinyint(1)` as integer instead of boolean (qiangxue)\n- Chg #2248: Renamed `yii\\base\\Model::DEFAULT_SCENARIO` to `yii\\base\\Model::SCENARIO_DEFAULT` (samdark)\n- Chg #2281: Renamed `yii\\db\\ActiveRecord::create()` to `populateRecord()` and changed signature. This method will not call instantiate() anymore (cebe)\n- Chg #2405: The CSS class of `yii\\widgets\\MaskedInput` now defaults to `form-control` (qiangxue)\n- Chg #2426: Changed URL creation method signatures to be consistent (samdark)\n- Chg #2516: Moved error handling from application to ErrorHandler class and fixed problems with HTTP Exception response code (cebe)\n    - `Yii::$app->exception` has now moved to `Yii::$app->errorHandler->exception`\n    - `yii\\base\\ErrorHandler` was split into `yii\\web\\ErrorHandler` and `yii\\console\\ErrorHandler`\n- Chg #2544: Changed `yii\\widgets\\DetailView`'s `name:format:label` to `attribute:format:label` to match `GridView` (samdark)\n- Chg #2603: `yii\\base\\ErrorException` now extends `\\ErrorException` (samdark)\n- Chg #2629: `yii\\base\\Module::controllerPath` is now read only, and all controller classes must be namespaced under `Module::controllerNamespace`. (qiangxue)\n- Chg #2630: API changes for URLs generation (samdark, qiangxue, cebe)\n    - Added `yii\\helpers\\Url`.\n    - Removed `yii\\helpers\\Html::url()`, use `yii\\helpers\\Url::to()` instead.\n    - Removed `yii\\web\\Controller::createUrl()` and `yii\\web\\Controller::createAbsoluteUrl()`, use `yii\\helpers\\Url::toRoute()` instead.\n    - Removed `yii\\web\\Controller::getCanonicalUrl()`, use `yii\\helpers\\Url::canonical()` instead.\n- Chg #2691: Null parameters will not be included in the generated URLs by `UrlManager` (gonimar, qiangxue)\n- Chg #2734: `FileCache::keyPrefix` defaults to empty string now (qiangxue)\n- Chg #2796: Removed `Application::preload` in favor of `Application::bootstrap` (qiangxue)\n- Chg #2816: Changed default date and time format of `yii\\base\\Formatter` to `Y-m-d` and `H:i:s` (qiangxue)\n- Chg #2911: Removed `tbl_` default for table prefix (samdark)\n- Chg #2912: Relative view files will be looked for under the directory containing the view currently being rendered (qiangxue)\n- Chg #2955: Changed the signature of ActiveQuery constructors and replaced `yii\\db\\ActiveRecord::createQuery()` with `find()` to simplify customizing `yii\\db\\ActiveQuery` classes (qiangxue)\n- Chg #2999: Added `findOne()` and `findAll()` to replace the usage of `yii\\db\\ActiveRecord::find($condition)`. (samdark, qiangxue)\n- Chg #4204: `yii\\web\\Request::getUserIP()` will return null if it cannot detect user IP address (qiangxue)\n- Chg #4622: Simplified the way of creating a Faker fixture template file (qiangxue)\n- Chg: Renamed `yii\\jui\\Widget::clientEventsMap` to `clientEventMap` (qiangxue)\n- Chg: Renamed `ActiveRecord::getPopulatedRelations()` to `getRelatedRecords()` (qiangxue)\n- Chg: Renamed `attributeName` and `className` to `targetAttribute` and `targetClass` for `UniqueValidator` and `ExistValidator` (qiangxue)\n- Chg: Added `yii\\widgets\\InputWidget::options` (qiangxue)\n- Chg: Changed the signature of `urlCreator` and button creators for `yii\\gridview\\ActionColumn` (qiangxue)\n- Chg: Updated HTMLPurified dependency to `4.6.*`.\n- Chg: Changed Yii autoloader to support loading PSR-4 classes only (i.e. PEAR-styled classes not supported anymore) (qiangxue)\n- Chg: Changed the directory structure according to PSR-4. You have to update your application `index.php`,\n       `index-test.php` and `yii` files to point to the new location of `Yii.php` (qiangxue, cebe)\n- Chg: Advanced app template: moved database connection DSN, login and password to `-local` config not to expose it to VCS (samdark)\n- Chg: Renamed `yii\\web\\Request::acceptedLanguages` to `acceptableLanguages` (qiangxue)\n- Chg: Removed implementation of `yii\\base\\Arrayable` from `yii\\base\\Object` (qiangxue)\n- Chg: The scripts in asset bundles are now registered in `View` at the end of `endBody()`. It was done in `endPage()` previously (qiangxue)\n- Chg: Renamed `csrf-var` to `csrf-param` for CSRF header name (Dilip)\n- Chg: The directory holding email templates is renamed from `mails` to `mail` (qiangxue)\n- Chg: Renamed properties `fooVar` to `fooParam` for various classes (qiangxue)\n    - Renamed `yii\\widgets\\ActiveForm::ajaxVar` to `ajaxParam`\n    - Renamed `yii\\data\\Pagination::pageVar` to `pageParam`\n    - Renamed `yii\\data\\Sort::sortVar` to `sortParam`\n    - Renamed `yii\\web\\Request::csrfVar` to `csrfParam`\n    - Renamed `yii\\web\\Request::methodVar` to `methodParam`\n    - Renamed `yii\\web\\UrlManager::routeVar` to `routeParam`\n    - Renamed `yii\\web\\Session::flashVar` to `flashParam`\n    - Renamed `yii\\web\\User::idVar` to `idParam`\n    - Renamed `yii\\web\\User::authTimeoutVar` to `authTimeoutParam`\n    - Renamed `yii\\web\\User::returnUrlVar` to `returnUrlParam`\n- Chg: Added `yii\\base\\View::viewFile` and removed `yii\\base\\ViewEvent::viewFile` (qiangxue)\n- Chg: Changed `yii\\base\\Controller::afterAction()`, `yii\\base\\Module::afterAction()` and `yii\\base\\ActionFilter::afterAction()` to pass `$result` by value instead of reference (qiangxue)\n- Chg: `yii\\base\\Extension::init()` is renamed to `bootstrap()` (qiangxue)\n- Chg: `getComponent()` and `setComponent()` in `yii\\base\\Application` and `yii\\base\\Module` are renamed to `get()` and `set()` respectively. (qiangxue)\n- Chg: The signature of `Yii::createObject()` is changed. Constructor parameters must be passed as the second parameter. (qiangxue)\n- Chg: `Yii::$objectConfig` is removed. You should use `Yii::$container->set()` to configure default settings of classes. (qiangxue)\n- Chg: Removed `yii\\grid\\Column::getDataCellContent()` and renamed `yii\\grid\\DataColumn::getDataCellContent()` to `yii\\grid\\DataColumn::getDataCellValue()` (cebe)\n- Chg: `yii\\log\\Logger` is split into `yii\\log\\Logger` and `yii\\log\\Dispatcher`. (qiangxue)\n- Chg: Moved all filter classes to namespace `yii\\filters` (qiangxue)\n- Chg: Re-implemented RBAC by following more closely to the original NIST RBAC model. Dropped `yii\\rbac\\PhpManager`. (qiangxue)\n- Chg: Renamed `yii\\web\\User::checkAccess()` to `yii\\web\\User::can()` (qiangxue)\n- New #66: [Auth client library](https://github.com/yiisoft/yii2-authclient) OpenId, OAuth1, OAuth2 clients (klimov-paul)\n- New #303: Added built-in support for REST API (qiangxue)\n- New #503: Added `yii\\di\\Container` and `yii\\di\\ServiceLocator` (qiangxue)\n- New #706: Added `yii\\widgets\\Pjax` and enhanced `yii\\grid\\GridView` to work with `Pjax` to support AJAX-update (qiangxue)\n- New #1393: [Codeception testing framework integration](https://github.com/yiisoft/yii2-codeception) (Ragazzo)\n- New #1438: [MongoDB integration](https://github.com/yiisoft/yii2-mongodb) ActiveRecord and Query (klimov-paul)\n- New #1956: Implemented test fixture framework (qiangxue)\n- New #2034: Added `yii\\filters\\ContentNegotiator` to support response format and language negotiation (qiangxue)\n- New #2149: Added `yii\\base\\DynamicModel` to support ad-hoc data validation (qiangxue)\n- New #2360: Added `AttributeBehavior` and `BlameableBehavior`, and renamed `AutoTimestamp` to `TimestampBehavior` (lucianobaraglia, qiangxue)\n- New #2932: Added `yii\\web\\ViewAction` that allow you to render views based on GET parameter (samdark)\n- New #2998: Added `yii\\log\\SyslogTarget` that is able to write log to syslog (miramir, samdark)\n- New #3029: Added `yii\\bootstrap\\ActiveForm` and `yii\\bootstrap\\ActiveField` (mikehaertl)\n- New #4640: Added `yii\\widgets\\ActiveForm::beginField()` and `endField()` (qiangxue)\n- New: Yii framework now comes with core messages translated into 26 languages, many thanks to all our translators!\n- New: Added `yii\\codeception\\DbTestCase` (qiangxue)\n- New: Added `yii\\web\\GroupUrlRule` (qiangxue)\n- New: Added `yii\\filters\\RateLimiter` (qiangxue)\n- New: Added various authentication methods, including `HttpBasicAuth`, `HttpBearerAuth`, `QueryParamAuth`, and `CompositeAuth` (qiangxue)\n- New: Added `yii\\web\\HtmlResponseFormatter` and `yii\\web\\JsonResponseFormatter` (qiangxue)\n\n\n2.0.0-alpha December 1, 2013\n-----------------------------\n\n- Initial release.\n- Official extensions released in this version:\n  - [Twitter bootstrap 3.0](https://github.com/yiisoft/yii2-bootstrap)\n  - [Jquery UI](https://github.com/yiisoft/yii2-jui)\n\n  - [Debug Toolbar](https://github.com/yiisoft/yii2-debug)\n  - [Gii code generator](https://github.com/yiisoft/yii2-gii)\n\n  - [Elasticsearch integration](https://github.com/yiisoft/yii2-elasticsearch): ActiveRecord and Query\n  - [Redis integration](https://github.com/yiisoft/yii2-redis): ActiveRecord, Cache and Session\n  - [Sphinx integration](https://github.com/yiisoft/yii2-sphinx): ActiveRecord and Query\n\n  - [Swiftmailer](https://github.com/yiisoft/yii2-swiftmailer)\n\n  - [Smarty View Renderer](https://github.com/yiisoft/yii2-smarty)\n  - [Twig View Renderer](https://github.com/yiisoft/yii2-twig)\n"
  },
  {
    "path": "framework/LICENSE.md",
    "content": "Copyright © 2008 by Yii Software LLC (http://www.yiisoft.com)\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions\nare met:\n\n * Redistributions of source code must retain the above copyright\n   notice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above copyright\n   notice, this list of conditions and the following disclaimer in\n   the documentation and/or other materials provided with the\n   distribution.\n * Neither the name of Yii Software LLC nor the names of its\n   contributors may be used to endorse or promote products derived\n   from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\nFOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\nCOPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\nINCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\nBUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\nLIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\nANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "framework/README.md",
    "content": "Yii PHP Framework Version 2\n===========================\n\nThis is the core framework code of [Yii 2](https://github.com/yiisoft/yii2#readme).\n\nThis repository is a read-only git subsplit of <https://github.com/yiisoft/yii2>.\nPlease submit issue reports and pull requests to the main repository.\nFor license information check the [LICENSE](LICENSE.md)-file.\n\nInstallation\n------------\n\nThe preferred way to install the Yii framework is through [composer](https://getcomposer.org/download/).\n\nRefer to [Bower and NPM Assets installation](https://www.yiiframework.com/doc/guide/2.0/en/structure-assets#bower-npm-assets) to select an asset installation method.\n\nEither run\n\n```\ncomposer require yiisoft/yii2\n```\n\nor add\n\n```json\n\"yiisoft/yii2\": \"~2.0.0\",\n```\n\nto the require section of your composer.json.\n"
  },
  {
    "path": "framework/UPGRADE.md",
    "content": "Upgrading Instructions for Yii Framework 2.0\n============================================\n\nThis file contains the upgrade notes for Yii 2.0. These notes highlight changes that\ncould break your application when you upgrade Yii from one version to another.\nEven though we try to ensure backwards compatibility (BC) as much as possible, sometimes\nit is not possible or very complicated to avoid it and still create a good solution to\na problem. You may also want to check out the [versioning policy](https://github.com/yiisoft/yii2/blob/master/docs/internals/versions.md)\nfor further details.\n\nUpgrading in general is as simple as updating your dependency in your composer.json and\nrunning `composer update`. In a big application however there may be more things to consider,\nwhich are explained in the following.\n\n> Note: This document assumes you have composer [installed globally](https://www.yiiframework.com/doc-2.0/guide-start-installation.html#installing-composer)\nso that you can run the `composer` command. If you have a `composer.phar` file inside of your project you need to\nreplace `composer` with `php composer.phar` in the following.\n\n> Tip: Upgrading dependencies of a complex software project always comes at the risk of breaking something, so make sure\nyou have a backup (you should be doing this anyway ;) ).\n\nIn case you use [composer asset plugin](https://github.com/fxpio/composer-asset-plugin) instead of the currently recommended\n[asset-packagist.org](https://asset-packagist.org) to install Bower and NPM assets, make sure it is upgraded to the latest version as well. To ensure best stability you should also upgrade composer in this step:\n\n    composer self-update\n    composer global require \"fxp/composer-asset-plugin:^1.4.1\" --no-plugins\n\nThe simple way to upgrade Yii, for example to version 2.0.10 (replace this with the version you want) will be running `composer require`:\n\n    composer require \"yiisoft/yii2:~2.0.10\" --update-with-dependencies\n\nThis command will only upgrade Yii and its direct dependencies, if necessary. Without `--update-with-dependencies` the\nupgrade might fail when the Yii version you chose has slightly different dependencies than the version you had before.\n`composer require` will by default not update any other packages as a safety feature.\n\nAnother way to upgrade is to change the `composer.json` file to require the new Yii version and then\nrun `composer update` by specifying all packages that are allowed to be updated.\n\n    composer update yiisoft/yii2 yiisoft/yii2-composer bower-asset/inputmask\n\nThe above command will only update the specified packages and leave the versions of all other dependencies intact.\nThis helps to update packages step by step without causing a lot of package version changes that might break in some way.\nIf you feel lucky you can of course update everything to the latest version by running `composer update` without\nany restrictions.\n\nAfter upgrading you should check whether your application still works as expected and no tests are broken.\nSee the following notes on which changes to consider when upgrading from one version to another.\n\n> Note: The following upgrading instructions are cumulative. That is,\nif you want to upgrade from version A to version C and there is\nversion B between A and C, you need to follow the instructions\nfor both A and B.\n\nUpgrade from Yii 2.0.53\n-----------------------\n\n* Raised minimum supported PHP version to `7.4`.\n* Deprecated caching components: `XCache` and `ZendDataCache` have been removed. If you were using these components, you\nwill need to replace them with alternative caching solutions.\n\n* Extensive static analysis template annotations (generics) have been added throughout the framework to improve type inference \n  with PHPStan and Psalm. These include:\n  \n  - **Actions**: `Action`, `InlineAction`, and all action classes now have generic type parameters for their controller type\n  - **Controllers**: `Controller` classes now have generic type parameters for their module type\n  - **Filters**: All filter classes (including `ActionFilter` and authentication filters) now have generic type parameters\n  - **Database**: `ActiveQuery` has generic type parameter for the model type, `ActiveRecord` has template methods for fluent interfaces, `Schema` classes have generic type parameters for column schema types\n  - **Dependency Injection**: `Container` and `Instance` classes have generic type parameters for object types\n  - **Applications**: `Application` and `BaseYii` have generic type parameters for user identity types\n  - **Behaviors**: Comprehensive generic annotations for all behavior classes (see the 2.0.52 upgrade notes for details on `Behavior` usage)\n  \n  If you are using static analysis tools like PHPStan or Psalm in your application and extend any of these framework classes,\n  you may need to add appropriate template annotations to maintain proper type inference. In most cases, the framework will\n  infer the types automatically, but for complex inheritance hierarchies you may need to explicitly declare template parameters.\n  \n  For example, when creating a custom action:\n  \n  ```php\n  use yii\\base\\Action;\n  use app\\controllers\\SiteController;\n  \n  /**\n   * @extends Action<SiteController>\n   */\n  class CustomAction extends Action\n  {\n      public function run()\n      {\n          // $this->controller is now properly typed as SiteController\n      }\n  }\n  ```\n\nUpgrade from Yii 2.0.52\n-----------------------\n\n* Static analysis template annotations have been added to `yii\\base\\Behavior` and related behavior classes.\n  The `Behavior` class now uses PHPStan and Psalm template annotations to provide better type inference for the `$owner` property.\n  \n  If you are using static analysis tools like PHPStan or Psalm in your application and have custom behavior classes,\n  you may need to add template annotations to avoid `MissingTemplateParam` errors.\n  \n  **Example for a simple behavior:**\n  \n  ```php\n  use yii\\base\\Behavior;\n  use yii\\db\\ActiveRecord;\n  \n  /**\n   * @extends Behavior<ActiveRecord>\n   */\n  class TestBehavior extends Behavior\n  {\n      public function events(): array\n      {\n          return [\n              ActiveRecord::EVENT_BEFORE_INSERT => 'beforeInsert',\n          ];\n      }\n      \n      public function beforeInsert(\\yii\\base\\ModelEvent $event): void\n      {\n          // $this->owner is now properly typed as ActiveRecord\n      }\n  }\n  ```\n  \n  **Example for a behavior with its own template parameter:**\n  \n  If you're creating a reusable behavior that should work with different component types,\n  you can define your own template parameter:\n  \n  ```php\n  use yii\\base\\Behavior;\n  use yii\\db\\ActiveRecord;\n  \n  /**\n   * @template T of ActiveRecord\n   * @extends Behavior<T>\n   */\n  class ReusableBehavior extends Behavior\n  {\n      // The $owner property inherited from Behavior will be typed as T\n      // This allows your behavior to work with any ActiveRecord subclass\n  }\n  ```\n  \n  This allows static analysis to infer the type of `$owner` without having to add type annotations throughout your code.\n  The framework's built-in behaviors (like `TimestampBehavior`, `BlameableBehavior`, etc.) have already been updated with\n  these annotations.\n\n* `ErrorHandler::convertExceptionToError()` has been deprecated and will be removed in version 2.2.0.\n\n  This method was deprecated due to `PHP 8.4` deprecating the use of `E_USER_ERROR` with `trigger_error()`.\n  The framework now handles exceptions in `__toString()` methods more appropriately based on the PHP version.\n\n  **Before (deprecated):**\n  ```php\n  public function __toString() {\n      try {\n          return $this->render();\n      } catch (\\Throwable $e) {\n          ErrorHandler::convertExceptionToError($e);\n          return '';\n      }\n  }\n  ```\n\n  **After (recommended):**\n  ```php\n  public function __toString() {\n      try {\n          return $this->render();\n      } catch (\\Throwable $e) {\n          if (PHP_VERSION_ID < 70400) {\n              trigger_error(ErrorHandler::convertExceptionToString($e), E_USER_ERROR);\n              return '';\n          }\n          throw $e;\n      }\n  }\n  ```\n\n* There was a bug when loading fixtures into PostgreSQL database, the table sequences were not reset. If you used a work-around or if you depended on this behavior, you are advised to review your code.\n\nUpgrade from Yii 2.0.51\n-----------------------\n\n* The function signature for `yii\\web\\Session::readSession()` and `yii\\web\\Session::gcSession()` have been changed.\n  They now have the same return types as `\\SessionHandlerInterface::read()` and `\\SessionHandlerInterface::gc()` respectively.\n  In case those methods have overwritten you will need to update your child classes accordingly.\n\nUpgrade from Yii 2.0.50\n-----------------------\n\n* Correcting the behavior for `JSON` column type in `MariaDb`.\n\n  Columns that are created as `JSON` now automatically add a Check constraint for `json_valid` to the according column.\n  If Yii2 detects that a column has this `json_valid` constraint, data passed into or fetched from it is automatically serialized/deserialized.\n\n  While this does affect migrations created before the update when running them afterwards, it doesn't retroactively change tables created through migrations before the update!\n  This means that running a migration script that invokes `yii\\db\\Migration::json()` for a MariaDb database will create a different database schema after the update than it did before.\n\n  To preserve the old behavior, migrations need to be changed to instead create a `LONGTEXT` column without a constraint:\n\n```php\n<?php\nclass m251103_091000_example_migration extends Migration {\n    public function up() {\n        // before\n        $this->addColumn('MyTable', 'json_data', $this->json()->null()->after('other_column'));\n        // after\n        $this->addColumn('MyTable', 'json_data', \"LONGTEXT NULL DEFAULT NULL COLLATE 'utf8mb4_bin' AFTER other_column\");\n    }\n}\n```\n \n  To make use of the new behavior, add the json_valid constraint to according columns on pre-existing databases\n  and remove any JSON serialization/deserialization logic that was in place before this change (as (de)serialization is now done by Yii2).\n\n  Example usage of `JSON` column type in `db`:\n  \n  ```php\n  <?php\n  \n  use yii\\db\\Schema;\n  \n  $db = Yii::$app->db;\n  $command = $db->createCommand();\n  \n  // Create a table with a JSON column\n  $command->createTable(\n      'products',\n      [\n          'id' => Schema::TYPE_PK,\n          'details' => Schema::TYPE_JSON,\n      ],\n  )->execute();\n  \n  // Insert a new product\n  $command->insert(\n      'products',\n      [\n          'details' => [\n              'name' => 'apple',\n              'price' => 100,\n              'color' => 'blue',\n              'size' => 'small',\n          ],\n      ],\n  )->execute();\n  \n  // Read all products\n  $records = $db->createCommand('SELECT * FROM products')->queryAll();\n  ```\n  \n  Example usage of `JSON` column type in `ActiveRecord`:\n  \n  ```php\n  <?php\n  \n  namespace app\\model;\n  \n  use yii\\db\\ActiveRecord;\n  \n  class ProductModel extends ActiveRecord\n  {\n      public static function tableName()\n      {\n          return 'products';\n      }\n  \n      public function rules()\n      {\n          return [\n              [['details'], 'safe'],\n          ];\n      }\n  }\n  ```\n  \n  ```php\n  <?php\n  \n  use app\\model\\ProductModel;\n  \n  // Create a new product\n  $product = new ProductModel();\n  \n  // Set the product details\n  $product->details = [\n      'name' => 'windows',\n      'color' => 'red',\n      'price' => 200,\n      'size' => 'large',\n  ];\n  \n  // Save the product\n  $product->save();\n  \n  // Read the first product\n  $product = ProductModel::findOne(1);\n  \n  // Get the product details\n  $details = $product->details;\n  \n  echo 'Name: ' . $details['name'];\n  echo 'Color: ' . $details['color'];\n  echo 'Size: ' . $details['size'];\n  \n  // Read all products with color red\n  $products = ProductModel::find()\n      ->where(new \\yii\\db\\Expression('JSON_EXTRACT(details, \"$.color\") = :color', [':color' => 'red']))\n      ->all();\n  \n  // Loop through all products\n  foreach ($products as $product) {\n      $details = $product->details;\n      echo 'Name: ' . $details['name'];\n      echo 'Color: ' . $details['color'];\n      echo 'Size: ' . $details['size'];\n  }\n  ```\n\nUpgrade from Yii 2.0.48\n-----------------------\n\n* Since Yii 2.0.49 the `yii\\console\\Controller::select()` function supports a default value and respects\n  the `yii\\console\\Controller::$interactive` setting. Before the user was always prompted to select an option\n  regardless of the `$interactive` setting. Now the `$default` value is automatically returned when `$interactive` is \n  `false`.\n* The function signature for `yii\\console\\Controller::select()` and `yii\\helpers\\BaseConsole::select()` have changed.\n  They now have an additional `$default = null` parameter. In case those methods are overwritten you will need to\n  update your child classes accordingly.\n\nUpgrade from Yii 2.0.46\n-----------------------\n\n* The default \"scope\" of the `yii\\mutex\\MysqlMutex` has changed, the name of the mutex now includes the name of the\n  database by default. Before this change the \"scope\" of the `MysqlMutex` was \"server wide\".\n  No matter which database was used, the mutex lock was acquired for the entire server, since version 2.0.47 \n  the \"scope\" of the mutex will be limited to the database being used. \n  This might impact your application if …\n    * The database name of the database connection specified in the `MysqlMutex::$db` property is set dynamically\n      (or changes in any other way during your application execution):  \n      Depending on your application you might want to set the `MysqlMutex::$keyPrefix` property (see below).\n    * The database connection specified in the `MysqlMutex::$db` does not include a database name:  \n      You must specify the `MysqlMutex::$keyPrefix` property (see below).\n  \n  If you need to specify/lock the \"scope\" of the `MysqlMutex` you can specify the `$keyPrefix` property.  \n  For example in your application config:   \n    ```php\n    'mutex' => [\n        'class' => 'yii\\mutex\\MysqlMutex',\n        'db' => 'db',\n        'keyPrefix' => 'myPrefix' // Your custom prefix which determines the \"scope\" of the mutex.\n    ],\n    ```\n  > Warning: Even if you're not impacted by the aforementioned conditions and even if you specify the `$keyPrefix`,\n    if you rely on a locked mutex during and/or across your application deployment\n    (e.g. switching over in a live environment from an old version to a new version of your application)\n    you will have to make sure any running process that acquired a lock finishes before switching over to the new \n    version of your application. A lock acquired before the deployment will *not* be mutually exclusive with a \n    lock acquired after the deployment (even if they have the same name). \n\nUpgrade from Yii 2.0.45\n-----------------------\n\n* Changes in `Inflector::camel2words()` introduced in 2.0.45 were reverted, so it works as in pre-2.0.45. If you need\n  2.0.45 behavior, [introduce your own method](https://github.com/yiisoft/yii2/pull/19495/files).\n* `yii\\log\\FileTarget::$rotateByCopy` is now deprecated and setting it to `false` has no effect since rotating of \n  the files is done only by copy.\n* `yii\\validators\\UniqueValidator` and `yii\\validators\\ExistValidator`, when used on multiple attributes, now only\n  generate an error on a single attribute. Previously, they would report a separate error on each attribute.\n  Old behavior can be achieved by setting `'skipOnError' => false`, but this might have undesired side effects with\n  additional validators on one of the target attributes.\n  See [issue #19407](https://github.com/yiisoft/yii2/issues/19407)\n\nUpgrade from Yii 2.0.44\n-----------------------\n\n* `yii\\filters\\PageCache::$cacheHeaders` now takes a case-sensitive list of header names since PageCache is no longer \n  storing the normalized (lowercase) versions of them so make sure this list is properly updated and your page cache \n  is recreated.\n\nUpgrade from Yii 2.0.43\n-----------------------\n\n* `Json::encode()` can now handle zero-indexed objects in same way as `json_encode()` and keep them as objects. In order \n  to avoid breaking backwards compatibility this behavior could be enabled by a new option flag but is disabled by default.\n  * Set `yii/helpers/Json::$keepObjectType = true` anywhere in your application code\n  * Or configure json response formatter to enable it for all JSON responses:\n      ```php\n      'response' => [\n        'formatters' => [\n          \\yii\\web\\Response::FORMAT_JSON => [\n            'class' => 'yii\\web\\JsonResponseFormatter',\n            'prettyPrint' => YII_DEBUG, // use \"pretty\" output in debug mode\n            'keepObjectType' => true, // keep object type for zero-indexed objects\n          ],\n        ],\n      ],\n      ```\n* `yii\\caching\\Cache::multiSet()` now uses the default cache duration (`yii\\caching\\Cache::$defaultDuration`) when no \n  duration is provided. A duration of 0 should be explicitly passed if items should not expire.\n* Default value of `yii\\console\\controllers\\MessageController::$translator` is updated to `['Yii::t', '\\Yii::t']`, since \n  old value of `'Yii::t'` didn't match `\\Yii::t` calls on PHP 8. If configuration file for \"extract\" command overrides \n  default value, update config file accordingly. See [issue #18941](https://github.com/yiisoft/yii2/issues/18941)\n\nUpgrade from Yii 2.0.42\n-----------------------\n\n* `yii\\base\\ErrorHandler` does not expose the `$_SERVER` information implicitly anymore.\n* The methods `phpTypecast()` and `dbTypecast()` of `yii\\db\\ColumnSchema` will no longer convert `$value` from `int` to \n  `string`, if database column type is `INTEGER UNSIGNED` or `BIGINT UNSIGNED`.\n  * I.e. it affects update and insert queries. For example:\n  ```php\n  \\Yii::$app->db->createCommand()->insert('{{some_table}}', ['int_unsigned_col' => 22])->execute();\n  ```\n  will execute next SQL:\n  ```sql\n  INSERT INTO `some_table` (`int_unsigned_col`) VALUES (22)\n  ```\n* Property `yii\\db\\ColumnSchemaBuilder::$categoryMap` has been removed in favor of getter/setter methods `getCategoryMap()` \n  and `setCategoryMap()`.\n\nUpgrade from Yii 2.0.41\n-----------------------\n\n* `NumberValidator` (`number`, `double`, `integer`) does not allow values with leading or terminating (non-trimmed) \n  white spaces anymore. If your application expects non-trimmed values provided to this validator make sure to trim \n  them first (i.e. by using `trim` / `filter` \"validators\").\n\nUpgrade from Yii 2.0.40\n-----------------------\n\n* The methods `getAuthKey()` and `validateAuthKey()` of `yii\\web\\IdentityInterface` are now also used to validate active\n  sessions (previously these methods were only used for cookie-based login). If your identity class does not properly\n  implement these methods yet, you should update it accordingly (an example can be found in the guide under\n  `Security` -> `Authentication`). Alternatively, you can simply return `null` in the `getAuthKey()` method to keep\n  the old behavior (that is, no validation of active sessions). Applications that change the underlying `authKey` of\n  an authenticated identity, should now call `yii\\web\\User::switchIdentity()`, `yii\\web\\User::login()`\n  or `yii\\web\\User::logout()` to recreate the active session with the new `authKey`.\n\nUpgrade from Yii 2.0.39.3\n-------------------------\n\n* Priority of processing `yii\\base\\Arrayable`, and `JsonSerializable` data has been reversed (`Arrayable` data is checked\n  first now) in `yii\\base\\Model`, and `yii\\rest\\Serializer`. If your application relies on the previous priority you need \n  to fix it manually based on the complexity of desired (de)serialization result.\n\nUpgrade from Yii 2.0.38\n-----------------------\n\n* The storage structure of the file cache has been changed when you use `\\yii\\caching\\FileCache::$keyPrefix`.\nIt is worth warming up the cache again if there is a logical dependency when working with the file cache.\n\n* `yii\\web\\Session` now respects the 'session.use_strict_mode' ini directive.\n  In case you use a custom `Session` class and have overwritten the `Session::openSession()` and/or \n  `Session::writeSession()` functions changes might be required:\n  * When in strict mode the `openSession()` function should check if the requested session id exists\n    (and mark it for forced regeneration if not).\n    For example, the `DbSession` does this at the beginning of the function as follows:\n    ```php\n    if ($this->getUseStrictMode()) {\n        $id = $this->getId();\n        if (!$this->getReadQuery($id)->exists()) {\n            //This session id does not exist, mark it for forced regeneration\n            $this->_forceRegenerateId = $id;\n        }\n    }\n    // ... normal function continues ...\n    ``` \n  * When in strict mode the `writeSession()` function should ignore writing the session under the old id.\n    For example, the `DbSession` does this at the beginning of the function as follows:\n    ```php\n    if ($this->getUseStrictMode() && $id === $this->_forceRegenerateId) {\n        //Ignore write when forceRegenerate is active for this id\n        return true;\n    }\n    // ... normal function continues ...\n    ```\n  > Note: The sample code above is specific for the `yii\\web\\DbSession` class.\n    Make sure you use the correct implementation based on your parent class,\n    e.g. `yii\\web\\CacheSession`, `yii\\redis\\Session`, `yii\\mongodb\\Session`, etc.\n  \n  > Note: In case your custom functions call their `parent` functions, there are probably no changes needed to your \n    code if those parents implement the `useStrictMode` checks.\n\n  > Warning: in case `openSession()` and/or `writeSession()` functions do not implement the `useStrictMode` code\n    the session could be stored under a malicious id without warning even if `useStrictMode` is enabled.\n\nUpgrade from Yii 2.0.37\n-----------------------\n\n* Resolving DI references inside of arrays in dependencies was made optional and turned off by default. In order\n  to turn it on, set `resolveArrays` of container instance to `true`.\n\nUpgrade from Yii 2.0.36\n-----------------------\n\n* `yii\\db\\Exception::getCode()` now returns full PDO code that is SQLSTATE string. If you have relied on comparing code\n  with an integer value, adjust your code.\n\nUpgrade from Yii 2.0.35\n-----------------------\n\n* Inline validator signature has been updated with 4th parameter `current`:\n\n  ```php\n  /**\n   * @param mixed $current the currently validated value of attribute\n   */\n  function ($attribute, $params, $validator, $current)\n  ```\n\n* Behavior of inline validator used as a rule of `EachValidator` has been changed - `$attribute` now refers to original\n  model's attribute and not its temporary counterpart:\n  \n  ```php\n  public $array_attribute = ['first', 'second'];\n\n  public function rules()\n  {\n      return [\n          ['array_attribute', 'each', 'rule' => ['customValidatingMethod']],\n      ];\n  }\n  \n  public function customValidatingMethod($attribute, $params, $validator, $current)\n  {\n      // $attribute === 'array_attribute' (as before)\n  \n      // now: $this->$attribute === ['first', 'second'] (on every iteration)\n      // previously:\n      // $this->$attribute === 'first' (on first iteration)\n      // $this->$attribute === 'second' (on second iteration)\n  \n      // use now $current instead\n      // $current === 'first' (on first iteration)\n      // $current === 'second' (on second iteration)\n  }\n  ```\n  \n* `$this` in an inline validator defined as closure now refers to model instance. If you need to access the object registering\n  the validator, pass its instance through use statement:\n  \n  ```php\n  $registrar = $this;\n  $validator = function($attribute, $params, $validator, $current) use ($registrar) {\n      // ...\n  }\n  ```\n  \n* Validator closure callbacks should not be declared as static.\n\n* If you have any controllers that override the `init()` method, make sure they are calling `parent::init()` at\n  the beginning, as demonstrated in the [component guide](https://www.yiiframework.com/doc/guide/2.0/en/concept-components).\n\nUpgrade from Yii 2.0.34\n-----------------------\n\n* `ExistValidator` used as a rule of `EachValidator` now requires providing `targetClass` explicitely and it's not possible to use it with `targetRelation` in\n  that configuration.\n  \n  ```php\n  public function rules()\n  {\n      return [\n          ['attribute', 'each', 'rule' => ['exist', 'targetClass' => static::className(), 'targetAttribute' => 'id']],\n      ];\n  }\n  ```\n\nUpgrade from Yii 2.0.32\n-----------------------\n\n* `yii\\helpers\\ArrayHelper::filter` now correctly filters data when passing a filter with more than 2 \"levels\",\n  e.g. `ArrayHelper::filter($myArray, ['A.B.C']`. Until Yii 2.0.32 all data after the 2nd level was returned,\n  please see the following example:\n  \n  ```php\n  $myArray = [\n      'A' => 1,\n      'B' => [\n          'C' => 1,\n          'D' => [\n              'E' => 1,\n              'F' => 2,\n          ]\n      ],\n  ];\n  ArrayHelper::filter($myArray, ['B.D.E']);\n  ```\n  \n  Before Yii 2.0.33 this would return\n  \n  ```php\n  [\n      'B' => [\n          'D' => [\n              'E' => 1,\n              'F' => 2, //Please note the unexpected inclusion of other elements\n          ],\n      ],\n  ]\n  ```\n\n  Since Yii 2.0.33 this returns\n\n  ```php\n  [\n      'B' => [\n          'D' => [\n              'E' => 1,\n          ],\n      ],\n  ]\n  ```\n  \n  Note: If you are only using up to 2 \"levels\" (e.g. `ArrayHelper::filter($myArray, ['A.B']`), this change has no impact.\n  \n* `UploadedFile` class `deleteTempFile()` and `isUploadedFile()` methods introduced in 2.0.32 were removed.\n\n* Exception will be thrown if `UrlManager::$cache` configuration is incorrect (previously misconfiguration was silently \n  ignored and `UrlManager` continue to work without cache). Make sure that `UrlManager::$cache` is correctly configured \n  or set it to `null` to explicitly disable cache.\n\nUpgrade from Yii 2.0.31\n-----------------------\n\n* `yii\\filters\\ContentNegotiator` now generates 406 'Not Acceptable' instead of 415 'Unsupported Media Type' on\n  content-type negotiation fail.\n\nUpgrade from Yii 2.0.30\n-----------------------\n* `yii\\helpers\\BaseInflector::slug()` now ensures there is no repeating $replacement string occurrences.\n  In case you rely on Yii 2.0.16 - 2.0.30 behavior, consider replacing `Inflector` with your own implementation.\n  \n  \nUpgrade from Yii 2.0.28\n-----------------------\n\n* `yii\\helpers\\Html::tag()` now generates boolean attributes\n  [according to HTML specification](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#boolean-attribute).\n  For `true` value attribute is present, for `false` value it is absent.  \n\nUpgrade from Yii 2.0.20\n-----------------------\n\n* `yii\\db\\Query::select()` and `addSelect()` now normalize the format that columns are stored in when saving them \n  to `$this->select`, so code that works directly with that property may need to be modified.\n  \n  For the following code:\n  \n  ```php\n  $a = $query->select('*');\n  $b = $query->select('id, name');\n  ```\n  \n  The value was stored as is i.e.\n  \n  ```php\n  // a\n  ['*']\n  \n  // b\n  ['id', 'name']\n  ``` \n  \n  Now it is stored as\n  \n  ```php\n  // a\n  ['*' => '*']\n  \n  // b\n  ['id' => 'id', 'name' => 'name']\n  ```\n\nUpgrade from Yii 2.0.16\n-----------------------\n\n* In case you have extended the `yii\\web\\DbSession` class you should check if your \n  custom implementation is compatible with the new `yii\\web\\DbSession::$fields` attribute.\n  Especially when overriding the `yii\\web\\DbSession::writeSession($id, $data)` function.\n\nUpgrade from Yii 2.0.15\n-----------------------\n\n* Updated dependency to `cebe/markdown` to version `1.2.x`.\n  If you need stick with 1.1.x, you can specify that in your `composer.json` by\n  adding the following line in the `require` section:\n\n  ```json\n  \"cebe/markdown\": \"~1.1.0\",\n  ```\n  \n* `yii\\mutex\\Mutex::acquire()` no longer returns `true` if lock is already acquired by the same component in the same process.\n  Make sure that you're not trying to acquire the same lock multiple times in a way that may create infinite loops, for example:\n    \n  ```php\n  if (Yii::$app->mutex->acquire('test')) {\n       while (!Yii::$app->mutex->acquire('test')) {\n           // `Yii::$app->mutex->acquire('test')` will always return `false` here, since lock is already acquired\n      }\n  }\n  ```\n  \n* Formatter methods `asInteger`, `asDecimal`, `asPercent`, and `asCurrency` are using now inner fallback methods to handle \n  very big number values to counter inner PHP casting and floating point number presentation issues. Make sure to provide \n  such values as string numbers.\n  \n* Active Record relations are now being reset when corresponding key fields are changed. If you have relied on the fact\n  that relations are never reloaded you have to adjust your code.\n\n\nUpgrade from Yii 2.0.14\n-----------------------\n\n* When hash format condition (array) is used in `yii\\db\\ActiveRecord::findOne()` and `findAll()`, the array keys (column names)\n  are now limited to the table column names. This is to prevent SQL injection if input was not filtered properly.\n  You should check all usages of `findOne()` and `findAll()` to ensure that input is filtered correctly.\n  If you need to find models using different keys than the table columns, use `find()->where(...)` instead.\n\n  It's not an issue in the default generated code though as ID is filtered by\n  controller code:\n\n  The following code examples are **not** affected by this issue (examples shown for `findOne()` are valid also for `findAll()`):\n\n  ```php\n  // yii\\web\\Controller ensures that $id is scalar\n  public function actionView($id)\n  {\n      $model = Post::findOne($id);\n      // ...\n  }\n  ```\n\n  ```php\n  // casting to (int) or (string) ensures no array can be injected (an exception will be thrown so this is not a good practise)\n  $model = Post::findOne((int) Yii::$app->request->get('id'));\n  ```\n\n  ```php\n  // explicitly specifying the colum to search, passing a scalar or array here will always result in finding a single record\n  $model = Post::findOne(['id' => Yii::$app->request->get('id')]);\n  ```\n\n  The following code however **is vulnerable**, an attacker could inject an array with an arbitrary condition and even exploit SQL injection:\n\n  ```php\n  $model = Post::findOne(Yii::$app->request->get('id'));\n  ```\n\n  For the above example, the SQL injection part is fixed with the patches provided in this release, but an attacker may still be able to search\n  records by different condition than a primary key search and violate your application business logic. So passing user input directly like this can cause problems and should be avoided.\n\n\nUpgrade from Yii 2.0.13\n-----------------------\n\n* Constants `IPV6_ADDRESS_LENGTH`, `IPV4_ADDRESS_LENGTH` were moved from `yii\\validators\\IpValidator` to `yii\\helpers\\IpHelper`.\n  If your application relies on these constants, make sure to update your code to follow the changes.\n\n* `yii\\base\\Security::compareString()` is now throwing `yii\\base\\InvalidArgumentException` in case non-strings are compared.\n\n* `yii\\db\\ExpressionInterface` has been introduced to represent a wider range of SQL expressions. In case you check for\n  `instanceof yii\\db\\Expression` in your code, you might consider changing that to checking for the interface and use the newly\n  introduced methods to retrieve the expression content.\n\n* Added JSON support for PostgreSQL and MySQL as well as Arrays support for PostgreSQL in ActiveRecord layer.\n  In case you already implemented such support yourself, please switch to Yii implementation.\n  * For MySQL JSON and PgSQL JSON & JSONB columns Active Record will return decoded JSON (that can be either array or scalar) after data population\n  and expects arrays or scalars to be assigned for further saving them into a database.\n  * For PgSQL Array columns Active Record will return `yii\\db\\ArrayExpression` object that acts as an array\n  (it implements `ArrayAccess`, `Traversable` and `Countable` interfaces) and expects array or `yii\\db\\ArrayExpression` to be\n  assigned for further saving it into the database.\n\n  In case this change makes the upgrade process to Yii 2.0.14 too hard in your project, you can [switch off the described behavior](https://github.com/yiisoft/yii2/issues/15716#issuecomment-368143206)\n  Then you can take your time to change your code and then re-enable arrays or JSON support.\n\n* `yii\\db\\PdoValue` class has been introduced to replace a special syntax that was used to declare PDO parameter type \n  when binding parameters to an SQL command, for example: `['value', \\PDO::PARAM_STR]`.\n  You should use `new PdoValue('value', \\PDO::PARAM_STR)` instead. Old syntax will be removed in Yii 2.1.\n\n* `yii\\db\\QueryBuilder::conditionBuilders` property and method-based condition builders are no longer used. \n  Class-based conditions and builders are introduced instead to provide more flexibility, extensibility and\n  space to customization. In case you rely on that property or override any of default condition builders, follow the \n  special [guide article](https://www.yiiframework.com/doc-2.0/guide-db-query-builder.html#adding-custom-conditions-and-expressions)\n  to update your code.\n\n* Protected method `yii\\db\\ActiveQueryTrait::createModels()` does not apply indexes as defined in `indexBy` property anymore.  \n  In case you override default ActiveQuery implementation and relied on that behavior, call `yii\\db\\Query::populate()`\n  method instead to index query results according to the `indexBy` parameter.\n\n* Log targets (like `yii\\log\\EmailTarget`) are now throwing `yii\\log\\LogRuntimeException` in case log can not be properly exported.\n\n* You can start preparing your application for Yii 2.1 by doing the following:\n\n  - Replace `::className()` calls with `::class` (if you’re running PHP 5.5+).\n  - Replace usages of `yii\\base\\InvalidParamException` with `yii\\base\\InvalidArgumentException`.\n  - Replace calls to `Yii::trace()` with `Yii::debug()`.\n  - Remove calls to `yii\\BaseYii::powered()`.\n  - If you are using XCache or Zend data cache, those are going away in 2.1 so you might want to start looking for an alternative.\n\n* In case you aren't using CSRF cookies (REST APIs etc.) you should turn them off explicitly by setting\n  `\\yii\\web\\Request::$enableCsrfCookie` to `false` in your config file.\n  \n* Previously headers sent after content output was started were silently ignored. This behavior was changed to throwing\n  `\\yii\\web\\HeadersAlreadySentException`.\n\nUpgrade from Yii 2.0.12\n-----------------------\n\n* The `yii\\web\\Request` class allowed to determine the value of `getIsSecureConnection()` form the\n  `X-Forwarded-Proto` header if the connection was made via a normal HTTP request. This behavior\n  was insecure as the header could have been set by a malicious client on a non-HTTPS connection.\n  With 2.0.13 Yii adds support for configuring trusted proxies. If your application runs behind a reverse proxy and relies on\n  `getIsSecureConnection()` to return the value form the `X-Forwarded-Proto` header you need to explicitly allow\n  this in the Request configuration. See the [guide](https://www.yiiframework.com/doc-2.0/guide-runtime-requests.html#trusted-proxies) for more information.\n\n  This setting also affects you when Yii is running on IIS webserver, which sets the `X-Rewrite-Url` header.\n  This header is now filtered by default and must be listed in trusted hosts to be detected by Yii:\n\n  ```php\n  [   // accept X-Rewrite-Url from all hosts, as it will be set by IIS\n      '/.*/' => ['X-Rewrite-Url'],\n  ]\n  ```\n\n* For compatibiliy with [PHP 7.2 which does not allow classes to be named `Object` anymore](https://wiki.php.net/rfc/object-typehint),\n  we needed to rename `yii\\base\\Object` to `yii\\base\\BaseObject`.\n  \n  `yii\\base\\Object` still exists for backwards compatibility and will be loaded if needed in projects that are\n  running on PHP <7.2. The compatibility class `yii\\base\\Object` extends from `yii\\base\\BaseObject` so if you\n  have classes that extend from `yii\\base\\Object` these would still work.\n  \n  What does not work however will be code that relies on `instanceof` checks or `is_subclass_of()` calls\n  for `yii\\base\\Object` on framework classes as these do not extend `yii\\base\\Object` anymore but only\n  extend from `yii\\base\\BaseObject`. In general such a check is not needed as there is a `yii\\base\\Configurable`\n  interface you should check against instead.\n  \n  Here is a visualisation of the change (`a < b` means \"b extends a\"):\n  \n  ```\n  Before:\n  \n  yii\\base\\Object < Framework Classes\n  yii\\base\\Object < Application Classes\n  \n  After Upgrade:\n  \n  yii\\base\\BaseObject < Framework Classes\n  yii\\base\\BaseObject < yii\\base\\Object < Application Classes\n\n  ```\n  \n  If you want to upgrade PHP to version 7.2 in your project you need to remove all cases that extend `yii\\base\\Object`\n  and extend from `yii\\base\\BaseObject` instead:\n  \n  ```\n  yii\\base\\BaseObject < Framework Classes\n  yii\\base\\BaseObject < Application Classes\n  ```\n  \n  For extensions that have classes extending from `yii\\base\\Object`, to be compatible with PHP 7.2, you need to\n  require `\"yiisoft/yii2\": \"~2.0.13\"` in composer.json and change affected classes to extend from `yii\\base\\BaseObject`\n  instead. It is not possible to allow Yii versions `<2.0.13` and be compatible with PHP 7.2 or higher.\n\n* A new method `public static function instance($refresh = false);` has been added to the `yii\\db\\ActiveRecordInterface` via a new\n  `yii\\base\\StaticInstanceInterface`. This change may affect your application in the following ways:\n\n  - If you have an `instance()` method defined in an `ActiveRecord` or `Model` class, you need to check whether the behavior is\n    compatible with the method added by Yii.\n  - Otherwise this method is implemented in the `yii\\base\\Model`, so the change only affects your code if you implement `ActiveRecordInterface`\n    in a class that does not extend `Model`. You may use `yii\\base\\StaticInstanceTrait` to implement it.\n    \n* Fixed built-in validator creating when model has a method with the same name. \n\n  It is documented, that for the validation rules declared in model by `yii\\base\\Model::rules()`, validator can be either \n  a built-in validator name, a method name of the model class, an anonymous function, or a validator class name. \n  Before this change behavior was inconsistent with the documentation: method in the model had higher priority, than\n  a built-in validator. In case you have relied on this behavior, make sure to fix it.\n\n* Behavior was changed for methods `yii\\base\\Module::get()` and `yii\\base\\Module::has()` so in case when the requested\n  component was not found in the current module, the parent ones will be checked for this component hierarchically.\n  Considering that the root parent module is usually an application, this change can reduce calls to global `Yii::$app->get()`,\n  and replace them with module-scope calls to `get()`, making code more reliable and easier to test.\n  However, this change may affect your application if you have code that uses method `yii\\base\\Module::has()` in order\n  to check existence of the component exactly in this specific module. In this case make sure the logic is not corrupted.\n\n* If you are using \"asset\" command to compress assets and your web application `assetManager` has `linkAssets` turned on,\n  make sure that \"asset\" command config has `linkAssets` turned on as well.\n\n\nUpgrade from Yii 2.0.11\n-----------------------\n\n* `yii\\i18n\\Formatter::normalizeDatetimeValue()` returns now array with additional third boolean element\n  indicating whether the timestamp has date information or it is just time value.\n\n* `yii\\grid\\DataColumn` filter is now automatically generated as dropdown list with localized `Yes` and `No` strings\n  in case of `format` being set to `boolean`.\n \n* The signature of `yii\\db\\QueryBuilder::prepareInsertSelectSubQuery()` was changed. The method has got an extra optional parameter\n  `$params`.\n\n* The signature of `yii\\cache\\Cache::getOrSet()` has been adjusted to also accept a callable and not only `Closure`.\n  If you extend this method, make sure to adjust your code.\n  \n* `yii\\web\\UrlManager` now checks if rules implement `getCreateUrlStatus()` method in order to decide whether to use\n  internal cache for `createUrl()` calls. Ensure that all your custom rules implement this method in order to fully \n  benefit from the acceleration provided by this cache.\n\n* `yii\\filters\\AccessControl` now can be used without `user` component. This has two consequences:\n\n  1. If used without user component, `yii\\filters\\AccessControl::denyAccess()` throws `yii\\web\\ForbiddenHttpException` instead of redirecting to login page.\n  2. If used without user component, using `AccessRule` matching a role throws `yii\\base\\InvalidConfigException`.\n  \n* Inputmask package name was changed from `jquery.inputmask` to `inputmask`. If you've configured path to\n  assets manually, please adjust it. \n\nUpgrade from Yii 2.0.10\n-----------------------\n\n* A new method `public function emulateExecution($value = true);` has been added to the `yii\\db\\QueryInterace`.\n  This method is implemented in the `yii\\db\\QueryTrait`, so this only affects your code if you implement QueryInterface\n  in a class that does not use the trait.\n\n* `yii\\validators\\FileValidator::getClientOptions()` and `yii\\validators\\ImageValidator::getClientOptions()` are now public.\n  If you extend from these classes and override these methods, you must make them public as well.\n\n* `yii\\widgets\\MaskedInput` inputmask dependency was updated to `~3.3.3`.\n  [See its changelog for details](https://github.com/RobinHerbots/Inputmask/blob/3.x/CHANGELOG.md).\n\n* PJAX: Auto generated IDs of the Pjax widget have been changed to use their own prefix to avoid conflicts.\n  Auto generated IDs are now prefixed with `p` instead of `w`. This is defined by the `$autoIdPrefix`\n  property of `yii\\widgets\\Pjax`. If you have any PHP or Javascript code that depends on autogenerated IDs\n  you should update these to match this new value. It is not a good idea to rely on auto generated values anyway, so\n  you better fix these cases by specifying an explicit ID.\n\n\nUpgrade from Yii 2.0.9\n----------------------\n\n* RBAC: `getChildRoles()` method was added to `\\yii\\rbac\\ManagerInterface`. If you've implemented your own RBAC manager\n  you need to implement new method.\n\n* Microsoft SQL `NTEXT` data type [was marked as deprecated](https://msdn.microsoft.com/en-us/library/ms187993.aspx) in MSSQL so\n  `\\yii\\db\\mssql\\Schema::TYPE_TEXT` was changed from `'ntext'` to `'nvarchar(max)'\n\n* Method `yii\\web\\Request::getBodyParams()` has been changed to pass full value of 'content-type' header to the second\n  argument of `yii\\web\\RequestParserInterface::parse()`. If you create your own custom parser, which relies on `$contentType`\n  argument, ensure to process it correctly as it may content additional data.\n\n* `yii\\rest\\Serializer` has been changed to return a JSON array for collection data in all cases to be consistent among pages\n  for data that is not indexed starting by 0. If your API relies on the Serializer to return data as JSON objects indexed by\n  PHP array keys, you should set `yii\\rest\\Serializer::$preserveKeys` to `true`.\n\n\nUpgrade from Yii 2.0.8\n----------------------\n\n* Part of code from `yii\\web\\User::loginByCookie()` method was moved to new `getIdentityAndDurationFromCookie()`\n  and `removeIdentityCookie()` methods. If you override `loginByCookie()` method, update it in order use new methods.\n\n* Fixture console command syntax was changed from `yii fixture \"*\" -User` to `yii fixture \"*, -User\"`. Upgrade your\n  scripts if necessary.\n\nUpgrade from Yii 2.0.7\n----------------------\n\n* The signature of `yii\\helpers\\BaseArrayHelper::index()` was changed. The method has got an extra optional parameter\n  `$groups`.\n\n* `yii\\helpers\\BaseArrayHelper` methods `isIn()` and `isSubset()` throw `\\yii\\base\\InvalidParamException`\n  instead of `\\InvalidArgumentException`. If you wrap calls of these methods in try/catch block, change expected\n  exception class.\n\n* `yii\\rbac\\ManagerInterface::canAddChild()` method was added. If you have custom backend for RBAC you need to implement\n  it.\n\n* The signature of `yii\\web\\User::loginRequired()` was changed. The method has got an extra optional parameter\n  `$checkAcceptHeader`.\n\n* The signature of `yii\\db\\ColumnSchemaBuilder::__construct()` was changed. The method has got an extra optional\n  parameter `$db`. In case you are instantiating this class yourself and using the `$config` parameter, you will need to\n  move it to the right by one.\n\n* String types in the MSSQL column schema map were upgraded to Unicode storage types. This will have no effect on\n  existing columns, but any new columns you generate via the migrations engine will now store data as Unicode.\n\n* Output buffering was introduced in the pair of `yii\\widgets\\ActiveForm::init()` and `::run()`. If you override any of\n  these methods, make sure that output buffer handling is not corrupted. If you call the parent implementation, when\n  overriding, everything should work fine. You should be doing that anyway.\n\nUpgrade from Yii 2.0.6\n----------------------\n\n* Added new requirement: ICU Data version >= 49.1. Please, ensure that your environment has ICU data installed and\n  up to date to prevent unexpected behavior or crashes. This may not be the case on older systems e.g. running Debian Wheezy.\n\n  > Tip: Use Yii 2 Requirements checker for easy and fast check. Look for `requirements.php` in root of Basic and Advanced\n  templates (howto-comment is in head of the script).\n\n* The signature of `yii\\helpers\\BaseInflector::transliterate()` was changed. The method is now public and has an\n  extra optional parameter `$transliterator`.\n\n* In `yii\\web\\UrlRule` the `pattern` matching group names are being replaced with the placeholders on class\n  initialization to support wider range of allowed characters. Because of this change:\n\n  - You are required to flush your application cache to remove outdated `UrlRule` serialized objects.\n    See the [Cache Flushing Guide](https://www.yiiframework.com/doc-2.0/guide-caching-data.html#cache-flushing)\n  - If you implement `parseRequest()` or `createUrl()` and rely on parameter names, call `substitutePlaceholderNames()`\n    in order to replace temporary IDs with parameter names after doing matching.\n\n* The context of `yii.confirm` JavaScript function was changed from `yii` object to the DOM element which triggered\n  the event.\n\n  - If you overrode the `yii.confirm` function and accessed the `yii` object through `this`, you must access it\n    with global variable `yii` instead.\n\n* Traversable objects are now formatted as arrays in XML response to support SPL objects and Generators. Previous\n  behavior could be turned on by setting `XmlResponseFormatter::$useTraversableAsArray` to `false`.\n\n* If you've implemented `yii\\rbac\\ManagerInterface` you need to implement additional method `getUserIdsByRole($roleName)`.\n\n* If you're using ApcCache with APCu, set `useApcu` to `true` in the component config.\n\n* The `yii\\behaviors\\SluggableBehavior` class has been refactored to make it more reusable.\n  Added new `protected` methods:\n\n  - `isSlugNeeded()`\n  - `makeUnique()`\n\n  The visibility of the following Methods has changed from `private` to `protected`:\n\n  - `validateSlug()`\n  - `generateUniqueSlug()`\n\n* The `yii\\console\\controllers\\MessageController` class has been refactored to be better configurable and now also allows\n  setting a lot of configuration options via command line. If you extend from this class, make sure it works as expected after\n  these changes.\n\nUpgrade from Yii 2.0.5\n----------------------\n\n* The signature of the following methods in `yii\\console\\controllers\\MessageController` has changed. They have an extra parameter `$markUnused`.\n  - `saveMessagesToDb($messages, $db, $sourceMessageTable, $messageTable, $removeUnused, $languages, $markUnused)`\n  - `saveMessagesToPHP($messages, $dirName, $overwrite, $removeUnused, $sort, $markUnused)`\n  - `saveMessagesCategoryToPHP($messages, $fileName, $overwrite, $removeUnused, $sort, $category, $markUnused)`\n  - `saveMessagesToPO($messages, $dirName, $overwrite, $removeUnused, $sort, $catalog, $markUnused)`\n\nUpgrade from Yii 2.0.4\n----------------------\n\nUpgrading from 2.0.4 to 2.0.5 does not require any changes.\n\nUpgrade from Yii 2.0.3\n----------------------\n\n* Updated dependency to `cebe/markdown` to version `1.1.x`.\n  If you need stick with 1.0.x, you can specify that in your `composer.json` by\n  adding the following line in the `require` section:\n\n  ```json\n  \"cebe/markdown\": \"~1.0.0\",\n  ```\n\nUpgrade from Yii 2.0.2\n----------------------\n\nStarting from version 2.0.3 Yii `Security` component relies on OpenSSL crypto lib instead of Mcrypt. The reason is that\nMcrypt is abandoned and isn't maintained for years. Therefore your PHP should be compiled with OpenSSL support. Most\nprobably there's nothing to worry because it is quite typical.\n\nIf you've extended `yii\\base\\Security` to override any of the config constants you have to update your code:\n\n    - `MCRYPT_CIPHER` — now encoded in `$cipher` (and hence `$allowedCiphers`).\n    - `MCRYPT_MODE` — now encoded in `$cipher` (and hence `$allowedCiphers`).\n    - `KEY_SIZE` — now encoded in `$cipher` (and hence `$allowedCiphers`).\n    - `KDF_HASH` — now `$kdfHash`.\n    - `MAC_HASH` — now `$macHash`.\n    - `AUTH_KEY_INFO` — now `$authKeyInfo`.\n\nUpgrade from Yii 2.0.0\n----------------------\n\n* Upgraded Twitter Bootstrap to [version 3.3.x](https://blog.getbootstrap.com/2014/10/29/bootstrap-3-3-0-released/).\n  If you need to use an older version (i.e. stick with 3.2.x) you can specify that in your `composer.json` by\n  adding the following line in the `require` section:\n\n  ```json\n  \"bower-asset/bootstrap\": \"3.2.*\",\n  ```\n\nUpgrade from Yii 2.0 RC\n-----------------------\n\n* If you've implemented `yii\\rbac\\ManagerInterface` you need to add implementation for new method `removeChildren()`.\n\n* The input dates for datetime formatting are now assumed to be in UTC unless a timezone is explicitly given.\n  Before, the timezone assumed for input dates was the default timezone set by PHP which is the same as `Yii::$app->timeZone`.\n  This causes trouble because the formatter uses `Yii::$app->timeZone` as the default values for output so no timezone conversion\n  was possible. If your timestamps are stored in the database without a timezone identifier you have to ensure they are in UTC or\n  add a timezone identifier explicitly.\n\n* `yii\\bootstrap\\Collapse` is now encoding labels by default. `encode` item option and global `encodeLabels` property were\n introduced to disable it. Keys are no longer used as labels. You need to remove keys and use `label` item option instead.\n\n* The `yii\\base\\View::beforeRender()` and `yii\\base\\View::afterRender()` methods have two extra parameters `$viewFile`\n  and `$params`. If you are overriding these methods, you should adjust the method signature accordingly.\n\n* If you've used `asImage` formatter i.e. `Yii::$app->formatter->asImage($value, $alt);` you should change it\n  to `Yii::$app->formatter->asImage($value, ['alt' => $alt]);`.\n\n* Yii now requires `cebe/markdown` 1.0.0 or higher, which includes breaking changes in its internal API. If you extend the markdown class\n  you need to update your implementation. See <https://github.com/cebe/markdown/releases/tag/1.0.0-rc> for details.\n  If you just used the markdown helper class there is no need to change anything.\n\n* If you are using CUBRID DBMS, make sure to use at least version 9.3.0 as the server and also as the PDO extension.\n  Quoting of values is broken in prior versions and Yii has no reliable way to work around this issue.\n  A workaround that may have worked before has been removed in this release because it was not reliable.\n\nUpgrade from Yii 2.0 Beta\n-------------------------\n\n* If you are using Composer to upgrade Yii, you should run the following command first (once for all) to install\n  the composer-asset-plugin, *before* you update your project:\n\n  ```\n  php composer.phar global require \"fxp/composer-asset-plugin:~1.3.1\"\n  ```\n\n  You also need to add the following code to your project's `composer.json` file:\n\n  ```json\n  \"extra\": {\n      \"asset-installer-paths\": {\n          \"npm-asset-library\": \"vendor/npm\",\n          \"bower-asset-library\": \"vendor/bower\"\n      }\n  }\n  ```\n\n  It is also a good idea to upgrade composer itself to the latest version if you see any problems:\n\n  ```\n  composer self-update\n  ```\n\n* If you used `clearAll()` or `clearAllAssignments()` of `yii\\rbac\\DbManager`, you should replace\n  them with `removeAll()` and `removeAllAssignments()` respectively.\n\n* If you created RBAC rule classes, you should modify their `execute()` method by adding `$user`\n  as the first parameter: `execute($user, $item, $params)`. The `$user` parameter represents\n  the ID of the user currently being access checked. Previously, this is passed via `$params['user']`.\n\n* If you override `yii\\grid\\DataColumn::getDataCellValue()` with visibility `protected` you have\n  to change visibility to `public` as visibility of the base method has changed.\n\n* If you have classes implementing `yii\\web\\IdentityInterface` (very common), you should modify\n  the signature of `findIdentityByAccessToken()` as\n  `public static function findIdentityByAccessToken($token, $type = null)`. The new `$type` parameter\n  will contain the type information about the access token. For example, if you use\n  `yii\\filters\\auth\\HttpBearerAuth` authentication method, the value of this parameter will be\n  `yii\\filters\\auth\\HttpBearerAuth`. This allows you to differentiate access tokens taken by\n  different authentication methods.\n\n* If you are sharing the same cache across different applications, you should configure\n  the `keyPrefix` property of the cache component to use some unique string.\n  Previously, this property was automatically assigned with a unique string.\n\n* If you are using `dropDownList()`, `listBox()`, `activeDropDownList()`, or `activeListBox()`\n  of `yii\\helpers\\Html`, and your list options use multiple blank spaces to format and align\n  option label texts, you need to specify the option `encodeSpaces` to be true.\n\n* If you are using `yii\\grid\\GridView` and have configured a data column to use a PHP callable\n  to return cell values (via `yii\\grid\\DataColumn::value`), you may need to adjust the signature\n  of the callable to be `function ($model, $key, $index, $widget)`. The `$key` parameter was newly added\n  in this release.\n\n* `yii\\console\\controllers\\AssetController` is now using hashes instead of timestamps. Replace all `{ts}` with `{hash}`.\n\n* The database table of the `yii\\log\\DbTarget` now needs a `prefix` column to store context information.\n  You can add it with `ALTER TABLE log ADD COLUMN prefix TEXT AFTER log_time;`.\n\n* The `fileinfo` PHP extension is now required by Yii. If you use  `yii\\helpers\\FileHelper::getMimeType()`, make sure\n  you have enabled this extension. This extension is [builtin](https://www.php.net/manual/en/fileinfo.installation.php) in php above `5.3`.\n\n* Please update your main layout file by adding this line in the `<head>` section: `<?= Html::csrfMetaTags() ?>`.\n  This change is needed because `yii\\web\\View` no longer automatically generates CSRF meta tags due to issue #3358.\n\n* If your model code is using the `file` validation rule, you should rename its `types` option to `extensions`.\n\n* `MailEvent` class has been moved to the `yii\\mail` namespace. You have to adjust all references that may exist in your code.\n\n* The behavior and signature of `ActiveRecord::afterSave()` has changed. `ActiveRecord::$isNewRecord` will now always be\n  false in afterSave and also dirty attributes are not available. This change has been made to have a more consistent and\n  expected behavior. The changed attributes are now available in the new parameter of afterSave() `$changedAttributes`.\n  `$changedAttributes` contains the old values of attributes that had changed and were saved.\n\n* `ActiveRecord::updateAttributes()` has been changed to not trigger events and not respect optimistic locking anymore to\n  differentiate it more from calling `update(false)` and to ensure it can be used in `afterSave()` without triggering infinite\n  loops.\n\n* If you are developing RESTful APIs and using an authentication method such as `yii\\filters\\auth\\HttpBasicAuth`,\n  you should explicitly configure `yii\\web\\User::enableSession` in the application configuration to be false to avoid\n  starting a session when authentication is performed. Previously this was done automatically by authentication method.\n\n* `mail` component was renamed to `mailer`, `yii\\log\\EmailTarget::$mail` was renamed to `yii\\log\\EmailTarget::$mailer`.\n  Please update all references in the code and config files.\n\n* `yii\\caching\\GroupDependency` was renamed to `TagDependency`. You should create such a dependency using the code\n  `new \\yii\\caching\\TagDependency(['tags' => 'TagName'])`, where `TagName` is similar to the group name that you\n  previously used.\n\n* If you are using the constant `YII_PATH` in your code, you should rename it to `YII2_PATH` now.\n\n* You must explicitly configure `yii\\web\\Request::cookieValidationKey` with a secret key. Previously this is done automatically.\n  To do so, modify your application configuration like the following:\n\n  ```php\n  return [\n      // ...\n      'components' => [\n          'request' => [\n              'cookieValidationKey' => 'your secret key here',\n          ],\n      ],\n  ];\n  ```\n\n  > Note: If you are using the `Advanced Project Template` you should not add this configuration to `common/config`\n  or `console/config` because the console application doesn't have to deal with CSRF and uses its own request that\n  doesn't have `cookieValidationKey` property.\n\n* `yii\\rbac\\PhpManager` now stores data in three separate files instead of one. In order to convert old file to\nnew ones save the following code as `convert.php` that should be placed in the same directory your `rbac.php` is in:\n\n  ```php\n  <?php\n  $oldFile = 'rbac.php';\n  $itemsFile = 'items.php';\n  $assignmentsFile = 'assignments.php';\n  $rulesFile = 'rules.php';\n\n  $oldData = include $oldFile;\n\n  function saveToFile($data, $fileName) {\n      $out = var_export($data, true);\n      $out = \"<?php\\nreturn \" . $out . ';';\n      $out = str_replace(['array (', ')'], ['[', ']'], $out);\n      file_put_contents($fileName, $out, LOCK_EX);\n  }\n\n  $items = [];\n  $assignments = [];\n  if (isset($oldData['items'])) {\n      foreach ($oldData['items'] as $name => $data) {\n          if (isset($data['assignments'])) {\n              foreach ($data['assignments'] as $userId => $assignmentData) {\n                  $assignments[$userId][] = $assignmentData['roleName'];\n              }\n              unset($data['assignments']);\n          }\n          $items[$name] = $data;\n      }\n  }\n\n  $rules = [];\n  if (isset($oldData['rules'])) {\n      $rules = $oldData['rules'];\n  }\n\n  saveToFile($items, $itemsFile);\n  saveToFile($assignments, $assignmentsFile);\n  saveToFile($rules, $rulesFile);\n\n  echo \"Done!\\n\";\n  ```\n\n  Run it once, delete `rbac.php`. If you've configured `authFile` property, remove the line from config and instead\n  configure `itemFile`, `assignmentFile` and `ruleFile`.\n\n* Static helper `yii\\helpers\\Security` has been converted into an application component. You should change all usage of\n  its methods to a new syntax, for example: instead of `yii\\helpers\\Security::hashData()` use `Yii::$app->getSecurity()->hashData()`.\n  The `generateRandomKey()` method now produces not an ASCII compatible output. Use `generateRandomString()` instead.\n  Default encryption and hash parameters has been upgraded. If you need to decrypt/validate data that was encrypted/hashed\n  before, use the following configuration of the 'security' component:\n\n  ```php\n  return [\n      'components' => [\n          'security' => [\n              'derivationIterations' => 1000,\n          ],\n          // ...\n      ],\n      // ...\n  ];\n  ```\n\n* If you are using query caching, you should modify your relevant code as follows, as `beginCache()` and `endCache()` are\n  replaced by `cache()`:\n\n  ```php\n  $db->cache(function ($db) {\n\n     // ... SQL queries that need to use query caching\n\n  }, $duration, $dependency);\n  ```\n\n* Due to significant changes to security you need to upgrade your code to use `\\yii\\base\\Security` component instead of\n  helper. If you have any data encrypted it should be re-encrypted. In order to do so you can use old security helper\n  [as explained by @docsolver at github](https://github.com/yiisoft/yii2/issues/4461#issuecomment-50237807).\n\n* [[yii\\helpers\\Url::to()]] will no longer prefix base URL to relative URLs. For example, `Url::to('images/logo.png')`\n  will return `images/logo.png` directly. If you want a relative URL to be prefix with base URL, you should make use\n  of the alias `@web`. For example, `Url::to('@web/images/logo.png')` will return `/BaseUrl/images/logo.png`.\n\n* The following properties are now taking `false` instead of `null` for \"don't use\" case:\n  - `yii\\bootstrap\\NavBar::$brandLabel`.\n  - `yii\\bootstrap\\NavBar::$brandUrl`.\n  - `yii\\bootstrap\\Modal::$closeButton`.\n  - `yii\\bootstrap\\Modal::$toggleButton`.\n  - `yii\\bootstrap\\Alert::$closeButton`.\n  - `yii\\widgets\\LinkPager::$nextPageLabel`.\n  - `yii\\widgets\\LinkPager::$prevPageLabel`.\n  - `yii\\widgets\\LinkPager::$firstPageLabel`.\n  - `yii\\widgets\\LinkPager::$lastPageLabel`.\n\n* The format of the Faker fixture template is changed. For an example, please refer to the file\n  `apps/advanced/common/tests/templates/fixtures/user.php`.\n\n* The signature of all file downloading methods in `yii\\web\\Response` is changed, as summarized below:\n  - `sendFile($filePath, $attachmentName = null, $options = [])`\n  - `sendContentAsFile($content, $attachmentName, $options = [])`\n  - `sendStreamAsFile($handle, $attachmentName, $options = [])`\n  - `xSendFile($filePath, $attachmentName = null, $options = [])`\n\n* The signature of callbacks used in `yii\\base\\ArrayableTrait::fields()` is changed from `function ($field, $model) {`\n  to `function ($model, $field) {`.\n\n* `Html::radio()`, `Html::checkbox()`, `Html::radioList()`, `Html::checkboxList()` no longer generate the container\n  tag around each radio/checkbox when you specify labels for them. You should manually render such container tags,\n  or set the `item` option for `Html::radioList()`, `Html::checkboxList()` to generate the container tags.\n\n* The formatter class has been refactored to have only one class regardless whether PHP intl extension is installed or not.\n  Functionality of `yii\\base\\Formatter` has been merged into `yii\\i18n\\Formatter` and `yii\\base\\Formatter` has been\n  removed so you have to replace all usage of `yii\\base\\Formatter` with `yii\\i18n\\Formatter` in your code.\n  Also the API of the Formatter class has changed in many ways.\n  The signature of the following Methods has changed:\n\n  - `asDate`\n  - `asTime`\n  - `asDatetime`\n  - `asSize` has been split up into `asSize` and `asShortSize`\n  - `asCurrency`\n  - `asDecimal`\n  - `asPercent`\n  - `asScientific`\n\n  The following methods have been removed, this also means that the corresponding format which may be used by a\n  GridView or DetailView is not available anymore:\n\n  - `asNumber`\n  - `asDouble`\n\n  Also due to these changes some formatting defaults have changes so you have to check all your GridView and DetailView\n  configuration and make sure the formatting is displayed correctly.\n\n  The configuration for `asSize()` has changed. It now uses the configuration for the number formatting from intl\n  and only the base is configured using `$sizeFormatBase`.\n\n  The specification of the date and time formats is now using the ICU pattern format even if PHP intl extension is not installed.\n  You can prefix a date format with `php:` to use the old format of the PHP `date()`-function.\n\n* The DateValidator has been refactored to use the same format as the Formatter class now (see previous change).\n  When you use the DateValidator and did not specify a format it will now be what is configured in the formatter class instead of 'Y-m-d'.\n  To get the old behavior of the DateValidator you have to set the format explicitly in your validation rule:\n\n  ```php\n  ['attributeName', 'date', 'format' => 'php:Y-m-d'],\n  ```\n\n* `beforeValidate()`, `beforeValidateAll()`, `afterValidate()`, `afterValidateAll()`, `ajaxBeforeSend()` and `ajaxComplete()`\n  are removed from `ActiveForm`. The same functionality is now achieved via JavaScript event mechanism like the following:\n\n  ```js\n  $('#myform').on('beforeValidate', function (event, messages, deferreds) {\n      // called when the validation is triggered by submitting the form\n      // return false if you want to cancel the validation for the whole form\n  }).on('beforeValidateAttribute', function (event, attribute, messages, deferreds) {\n      // before validating an attribute\n      // return false if you want to cancel the validation for the attribute\n  }).on('afterValidateAttribute', function (event, attribute, messages) {\n      // ...\n  }).on('afterValidate', function (event, messages) {\n      // ...\n  }).on('beforeSubmit', function () {\n      // after all validations have passed\n      // you can do ajax form submission here\n      // return false if you want to stop form submission\n  });\n  ```\n\n* The signature of `View::registerJsFile()` and `View::registerCssFile()` has changed. The `$depends` and `$position`\n  paramaters have been merged into `$options`. The new signatures are as follows:\n\n  - `registerJsFile($url, $options = [], $key = null)`\n  - `registerCssFile($url, $options = [], $key = null)`\n"
  },
  {
    "path": "framework/Yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nrequire __DIR__ . '/BaseYii.php';\n\n/**\n * Yii is a helper class serving common framework functionalities.\n *\n * It extends from [[\\yii\\BaseYii]] which provides the actual implementation.\n * By writing your own Yii class, you can customize some functionalities of [[\\yii\\BaseYii]].\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n * @phpcs:disable PSR1.Files.SideEffects.FoundWithSymbols\n * @phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace\n *\n * @template TUserIdentity of \\yii\\web\\IdentityInterface\n * @extends \\yii\\BaseYii<TUserIdentity>\n */\nclass Yii extends \\yii\\BaseYii\n{\n}\n\nspl_autoload_register(['Yii', 'autoload'], true, true);\nYii::$classMap = require __DIR__ . '/classes.php';\nYii::$container = new yii\\di\\Container();\n"
  },
  {
    "path": "framework/assets/yii.activeForm.js",
    "content": "/**\n * Yii form widget.\n *\n * This is the JavaScript widget used by the yii\\widgets\\ActiveForm widget.\n *\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\n(function ($) {\n\n    $.fn.yiiActiveForm = function (method) {\n        if (methods[method]) {\n            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));\n        } else {\n            if (typeof method === 'object' || !method) {\n                return methods.init.apply(this, arguments);\n            } else {\n                $.error('Method ' + method + ' does not exist on jQuery.yiiActiveForm');\n                return false;\n            }\n        }\n    };\n\n    var events = {\n        /**\n         * beforeValidate event is triggered before validating the whole form.\n         * The signature of the event handler should be:\n         *     function (event, messages, deferreds)\n         * where\n         *  - event: an Event object.\n         *  - messages: an associative array with keys being attribute IDs and values being error message arrays\n         *    for the corresponding attributes.\n         *  - deferreds: an array of Deferred objects. You can use deferreds.add(callback) to add a new deferred validation.\n         *\n         * If the handler returns a boolean false, it will stop further form validation after this event. And as\n         * a result, afterValidate event will not be triggered.\n         */\n        beforeValidate: 'beforeValidate',\n        /**\n         * afterValidate event is triggered after validating the whole form.\n         * The signature of the event handler should be:\n         *     function (event, messages, errorAttributes)\n         * where\n         *  - event: an Event object.\n         *  - messages: an associative array with keys being attribute IDs and values being error message arrays\n         *    for the corresponding attributes.\n         *  - errorAttributes: an array of attributes that have validation errors. Please refer to attributeDefaults for the structure of this parameter.\n         */\n        afterValidate: 'afterValidate',\n        /**\n         * beforeValidateAttribute event is triggered before validating an attribute.\n         * The signature of the event handler should be:\n         *     function (event, attribute, messages, deferreds)\n         * where\n         *  - event: an Event object.\n         *  - attribute: the attribute to be validated. Please refer to attributeDefaults for the structure of this parameter.\n         *  - messages: an array to which you can add validation error messages for the specified attribute.\n         *  - deferreds: an array of Deferred objects. You can use deferreds.add(callback) to add a new deferred validation.\n         *\n         * If the handler returns a boolean false, it will stop further validation of the specified attribute.\n         * And as a result, afterValidateAttribute event will not be triggered.\n         */\n        beforeValidateAttribute: 'beforeValidateAttribute',\n        /**\n         * afterValidateAttribute event is triggered after validating the whole form and each attribute.\n         * The signature of the event handler should be:\n         *     function (event, attribute, messages)\n         * where\n         *  - event: an Event object.\n         *  - attribute: the attribute being validated. Please refer to attributeDefaults for the structure of this parameter.\n         *  - messages: an array to which you can add additional validation error messages for the specified attribute.\n         */\n        afterValidateAttribute: 'afterValidateAttribute',\n        /**\n         * beforeSubmit event is triggered before submitting the form after all validations have passed.\n         * The signature of the event handler should be:\n         *     function (event)\n         * where event is an Event object.\n         *\n         * If the handler returns a boolean false, it will stop form submission.\n         */\n        beforeSubmit: 'beforeSubmit',\n        /**\n         * ajaxBeforeSend event is triggered before sending an AJAX request for AJAX-based validation.\n         * The signature of the event handler should be:\n         *     function (event, jqXHR, settings)\n         * where\n         *  - event: an Event object.\n         *  - jqXHR: a jqXHR object\n         *  - settings: the settings for the AJAX request\n         */\n        ajaxBeforeSend: 'ajaxBeforeSend',\n        /**\n         * ajaxComplete event is triggered after completing an AJAX request for AJAX-based validation.\n         * The signature of the event handler should be:\n         *     function (event, jqXHR, textStatus)\n         * where\n         *  - event: an Event object.\n         *  - jqXHR: a jqXHR object\n         *  - textStatus: the status of the request (\"success\", \"notmodified\", \"error\", \"timeout\", \"abort\", or \"parsererror\").\n         */\n        ajaxComplete: 'ajaxComplete',\n        /**\n         * afterInit event is triggered after yii activeForm init.\n         * The signature of the event handler should be:\n         *     function (event)\n         * where\n         *  - event: an Event object.\n         */\n        afterInit: 'afterInit'\n    };\n\n    // NOTE: If you change any of these defaults, make sure you update yii\\widgets\\ActiveForm::getClientOptions() as well\n    var defaults = {\n        // whether to encode the error summary\n        encodeErrorSummary: true,\n        // the jQuery selector for the error summary\n        errorSummary: '.error-summary',\n        // whether to perform validation before submitting the form.\n        validateOnSubmit: true,\n        // the container CSS class representing the corresponding attribute has validation error\n        errorCssClass: 'has-error',\n        // the container CSS class representing the corresponding attribute passes validation\n        successCssClass: 'has-success',\n        // the container CSS class representing the corresponding attribute is being validated\n        validatingCssClass: 'validating',\n        // the GET parameter name indicating an AJAX-based validation\n        ajaxParam: 'ajax',\n        // the type of data that you're expecting back from the server\n        ajaxDataType: 'json',\n        // the URL for performing AJAX-based validation. If not set, it will use the the form's action\n        validationUrl: undefined,\n        // whether to scroll to first visible error after validation.\n        scrollToError: true,\n        // offset in pixels that should be added when scrolling to the first error.\n        scrollToErrorOffset: 0,\n        // where to add validation class: container or input\n        validationStateOn: 'container'\n    };\n\n    // NOTE: If you change any of these defaults, make sure you update yii\\widgets\\ActiveField::getClientOptions() as well\n    var attributeDefaults = {\n        // a unique ID identifying an attribute (e.g. \"loginform-username\") in a form\n        id: undefined,\n        // attribute name or expression (e.g. \"[0]content\" for tabular input)\n        name: undefined,\n        // the jQuery selector of the container of the input field\n        container: undefined,\n        // the jQuery selector of the input field under the context of the form\n        input: undefined,\n        // the jQuery selector of the error tag under the context of the container\n        error: '.help-block',\n        // whether to encode the error\n        encodeError: true,\n        // whether to perform validation when a change is detected on the input\n        validateOnChange: true,\n        // whether to perform validation when the input loses focus\n        validateOnBlur: true,\n        // whether to perform validation when the user is typing.\n        validateOnType: false,\n        // number of milliseconds that the validation should be delayed when a user is typing in the input field.\n        validationDelay: 500,\n        // whether to enable AJAX-based validation.\n        enableAjaxValidation: false,\n        // function (attribute, value, messages, deferred, $form), the client-side validation function.\n        validate: undefined,\n        // status of the input field, 0: empty, not entered before, 1: validated, 2: pending validation, 3: validating\n        status: 0,\n        // whether the validation is cancelled by beforeValidateAttribute event handler\n        cancelled: false,\n        // the value of the input\n        value: undefined,\n        // whether to update aria-invalid attribute after validation\n        updateAriaInvalid: true\n    };\n\n\n    var submitDefer;\n\n    var setSubmitFinalizeDefer = function ($form) {\n        submitDefer = $.Deferred();\n        $form.data('yiiSubmitFinalizePromise', submitDefer.promise());\n    };\n\n    // finalize yii.js $form.submit\n    var submitFinalize = function ($form) {\n        if (submitDefer) {\n            submitDefer.resolve();\n            submitDefer = undefined;\n            $form.removeData('yiiSubmitFinalizePromise');\n        }\n    };\n\n\n    var methods = {\n        init: function (attributes, options) {\n            return this.each(function () {\n                var $form = $(this);\n                if ($form.data('yiiActiveForm')) {\n                    return;\n                }\n\n                var settings = $.extend({}, defaults, options || {});\n                if (settings.validationUrl === undefined) {\n                    settings.validationUrl = $form.attr('action');\n                }\n\n                $.each(attributes, function (i) {\n                    attributes[i] = $.extend({value: getValue($form, this)}, attributeDefaults, this);\n                    watchAttribute($form, attributes[i]);\n                });\n\n                $form.data('yiiActiveForm', {\n                    settings: settings,\n                    attributes: attributes,\n                    submitting: false,\n                    validated: false,\n                    validate_only: false, // validate without auto submitting\n                    options: getFormOptions($form)\n                });\n\n                /**\n                 * Clean up error status when the form is reset.\n                 * Note that $form.on('reset', ...) does work because the \"reset\" event does not bubble on IE.\n                 */\n                $form.on('reset.yiiActiveForm', methods.resetForm);\n\n                if (settings.validateOnSubmit) {\n                    $form.on('mouseup.yiiActiveForm keyup.yiiActiveForm', ':submit', function () {\n                        $form.data('yiiActiveForm').submitObject = $(this);\n                    });\n                    $form.on('submit.yiiActiveForm', methods.submitForm);\n                }\n                var event = $.Event(events.afterInit);\n                $form.trigger(event);\n            });\n        },\n\n        // add a new attribute to the form dynamically.\n        // please refer to attributeDefaults for the structure of attribute\n        add: function (attribute) {\n            var $form = $(this);\n            attribute = $.extend({value: getValue($form, attribute)}, attributeDefaults, attribute);\n            $form.data('yiiActiveForm').attributes.push(attribute);\n            watchAttribute($form, attribute);\n        },\n\n        // remove the attribute with the specified ID from the form\n        remove: function (id) {\n            var $form = $(this),\n                attributes = $form.data('yiiActiveForm').attributes,\n                index = -1,\n                attribute = undefined;\n            $.each(attributes, function (i) {\n                if (attributes[i]['id'] == id) {\n                    index = i;\n                    attribute = attributes[i];\n                    return false;\n                }\n            });\n            if (index >= 0) {\n                attributes.splice(index, 1);\n                unwatchAttribute($form, attribute);\n            }\n\n            return attribute;\n        },\n\n        // manually trigger the validation of the attribute with the specified ID\n        validateAttribute: function (id) {\n            var attribute = methods.find.call(this, id);\n            if (attribute != undefined) {\n                validateAttribute($(this), attribute, true);\n            }\n        },\n\n        // find an attribute config based on the specified attribute ID\n        find: function (id) {\n            var attributes = $(this).data('yiiActiveForm').attributes,\n                result = undefined;\n            $.each(attributes, function (i) {\n                if (attributes[i]['id'] == id) {\n                    result = attributes[i];\n                    return false;\n                }\n            });\n            return result;\n        },\n\n        destroy: function () {\n            return this.each(function () {\n                $(this).off('.yiiActiveForm');\n                $(this).removeData('yiiActiveForm');\n            });\n        },\n\n        data: function () {\n            return this.data('yiiActiveForm');\n        },\n\n        // validate all applicable inputs in the form\n        validate: function (forceValidate) {\n            if (forceValidate) {\n                $(this).data('yiiActiveForm').submitting = true;\n            }\n\n            var $form = $(this),\n                data = $form.data('yiiActiveForm'),\n                needAjaxValidation = false,\n                messages = {},\n                deferreds = deferredArray(),\n                submitting = data.submitting;\n\n            if (submitting) {\n                var event = $.Event(events.beforeValidate);\n                $form.trigger(event, [messages, deferreds]);\n\n                if (event.result === false) {\n                    data.submitting = false;\n                    submitFinalize($form);\n                    return;\n                }\n            }\n\n            // client-side validation\n            $.each(data.attributes, function () {\n                this.$form = $form;\n                var $input = findInput($form, this);\n\n                var disabled = $input.toArray().reduce(function (result, next) {\n                    return result && $(next).is(':disabled');\n                }, true);\n                if (disabled) {\n                    return true;\n                }\n                // validate markup for select input\n                if ($input.length && $input[0].tagName.toLowerCase() === 'select') {\n                    var opts = $input[0].options, isEmpty = !opts || !opts.length, isRequired = $input.attr('required'),\n                        isMultiple = $input.attr('multiple'), size = $input.attr('size') || 1;\n                    // check if valid HTML markup for select input, else return validation as `true`\n                    // https://w3c.github.io/html-reference/select.html\n                    if (isRequired && !isMultiple && parseInt(size, 10) === 1) { // invalid select markup condition\n                        if (isEmpty) { // empty option elements for the select\n                            return true;\n                        }\n                        if (opts[0] && (opts[0].value !== '' && opts[0].text !== '')) { // first option is not empty\n                            return true;\n                        }\n                    }\n                }\n                this.cancelled = false;\n                // perform validation only if the form is being submitted or if an attribute is pending validation\n                if (data.submitting || this.status === 2 || this.status === 3) {\n                    var msg = messages[this.id];\n                    if (msg === undefined) {\n                        msg = [];\n                        messages[this.id] = msg;\n                    }\n\n                    var event = $.Event(events.beforeValidateAttribute);\n                    $form.trigger(event, [this, msg, deferreds]);\n                    if (event.result !== false) {\n                        if (this.validate) {\n                            this.validate(this, getValue($form, this), msg, deferreds, $form);\n                        }\n                        if (this.enableAjaxValidation) {\n                            needAjaxValidation = true;\n                        }\n                    } else {\n                        this.cancelled = true;\n                    }\n                }\n            });\n\n            // ajax validation\n            $.when.apply(this, deferreds).always(function () {\n                // Remove empty message arrays\n                for (var i in messages) {\n                    if (0 === messages[i].length) {\n                        delete messages[i];\n                    }\n                }\n                if (needAjaxValidation && ($.isEmptyObject(messages) || data.submitting)) {\n                    var $button = data.submitObject,\n                        extData = '&' + data.settings.ajaxParam + '=' + $form.attr('id');\n                    if ($button && $button.length && $button.attr('name')) {\n                        extData += '&' + $button.attr('name') + '=' + $button.attr('value');\n                    }\n                    $.ajax({\n                        url: data.settings.validationUrl,\n                        type: $form.attr('method'),\n                        data: $form.serialize() + extData,\n                        dataType: data.settings.ajaxDataType,\n                        complete: function (jqXHR, textStatus) {\n                            currentAjaxRequest = null;\n                            $form.trigger(events.ajaxComplete, [jqXHR, textStatus]);\n                        },\n                        beforeSend: function (jqXHR, settings) {\n                            currentAjaxRequest = jqXHR;\n                            $form.trigger(events.ajaxBeforeSend, [jqXHR, settings]);\n                        },\n                        success: function (msgs) {\n                            if (msgs !== null && typeof msgs === 'object') {\n                                $.each(data.attributes, function () {\n                                    if (!this.enableAjaxValidation || this.cancelled) {\n                                        delete msgs[this.id];\n                                    }\n                                });\n                                updateInputs($form, $.extend(messages, msgs), submitting);\n                            } else {\n                                updateInputs($form, messages, submitting);\n                            }\n                        },\n                        error: function () {\n                            data.submitting = false;\n                            submitFinalize($form);\n                        }\n                    });\n                } else {\n                    if (data.submitting) {\n                        // delay callback so that the form can be submitted without problem\n                        window.setTimeout(function () {\n                            updateInputs($form, messages, submitting);\n                        }, 200);\n                    } else {\n                        updateInputs($form, messages, submitting);\n                    }\n                }\n            });\n        },\n\n        submitForm: function () {\n            var $form = $(this),\n                data = $form.data('yiiActiveForm');\n            if (data.validated) {\n                // Second submit's call (from validate/updateInputs)\n                data.submitting = false;\n                var event = $.Event(events.beforeSubmit);\n                $form.trigger(event);\n                if (event.result === false) {\n                    data.validated = false;\n                    submitFinalize($form);\n                    return false;\n                }\n                updateHiddenButton($form);\n                return true;   // continue submitting the form since validation passes\n            } else {\n                // First submit's call (from yii.js/handleAction) - execute validating\n                setSubmitFinalizeDefer($form);\n\n                if (data.settings.timer !== undefined) {\n                    clearTimeout(data.settings.timer);\n                }\n                data.submitting = true;\n                methods.validate.call($form);\n                return false;\n            }\n        },\n\n        resetForm: function () {\n            var $form = $(this);\n            var data = $form.data('yiiActiveForm');\n            // Because we bind directly to a form reset event instead of a reset button (that may not exist),\n            // when this function is executed form input values have not been reset yet.\n            // Therefore we do the actual reset work through setTimeout.\n            window.setTimeout(function () {\n                $.each(data.attributes, function () {\n                    // Without setTimeout() we would get the input values that are not reset yet.\n                    this.value = getValue($form, this);\n                    this.status = 0;\n                    var $container = $form.find(this.container),\n                        $input = findInput($form, this),\n                        $errorElement = data.settings.validationStateOn === 'input' ? $input : $container;\n\n                    $errorElement.removeClass(\n                        data.settings.validatingCssClass + ' ' +\n                        data.settings.errorCssClass + ' ' +\n                        data.settings.successCssClass\n                    );\n                    $container.find(this.error).html('');\n                });\n                $form.find(data.settings.errorSummary).hide().find('ul').html('');\n            }, 1);\n        },\n\n        /**\n         * Updates error messages, input containers, and optionally summary as well.\n         * If an attribute is missing from messages, it is considered valid.\n         * @param messages array the validation error messages, indexed by attribute IDs\n         * @param summary whether to update summary as well.\n         */\n        updateMessages: function (messages, summary) {\n            var $form = $(this);\n            var data = $form.data('yiiActiveForm');\n            $.each(data.attributes, function () {\n                updateInput($form, this, messages);\n            });\n            if (summary) {\n                updateSummary($form, messages);\n            }\n        },\n\n        /**\n         * Updates error messages and input container of a single attribute.\n         * If messages is empty, the attribute is considered valid.\n         * @param id attribute ID\n         * @param messages array with error messages\n         */\n        updateAttribute: function (id, messages) {\n            var attribute = methods.find.call(this, id);\n            if (attribute != undefined) {\n                var msg = {};\n                msg[id] = messages;\n                updateInput($(this), attribute, msg);\n            }\n        }\n    };\n\n    var watchAttribute = function ($form, attribute) {\n        var $input = findInput($form, attribute);\n        if (attribute.validateOnChange) {\n            $input.on('change.yiiActiveForm', function () {\n                validateAttribute($form, attribute, false);\n            });\n        }\n        if (attribute.validateOnBlur) {\n            $input.on('blur.yiiActiveForm', function () {\n                if (attribute.status == 0 || attribute.status == 1) {\n                    validateAttribute($form, attribute, true);\n                }\n            });\n        }\n        if (attribute.validateOnType) {\n            $input.on('keyup.yiiActiveForm', function (e) {\n                if ($.inArray(e.which, [16, 17, 18, 37, 38, 39, 40]) !== -1) {\n                    return;\n                }\n                if (attribute.value !== getValue($form, attribute)) {\n                    validateAttribute($form, attribute, false, attribute.validationDelay);\n                }\n            });\n        }\n    };\n\n    var unwatchAttribute = function ($form, attribute) {\n        findInput($form, attribute).off('.yiiActiveForm');\n    };\n\n    var validateAttribute = function ($form, attribute, forceValidate, validationDelay) {\n        var data = $form.data('yiiActiveForm');\n\n        if (forceValidate) {\n            attribute.status = 2;\n        }\n        $.each(data.attributes, function () {\n            if (!isEqual(this.value, getValue($form, this))) {\n                this.status = 2;\n                forceValidate = true;\n            }\n        });\n        if (!forceValidate) {\n            return;\n        }\n\n        if (currentAjaxRequest !== null) {\n            currentAjaxRequest.abort();\n        }\n        if (data.settings.timer !== undefined) {\n            clearTimeout(data.settings.timer);\n        }\n        data.settings.timer = window.setTimeout(function () {\n            if (data.submitting || $form.is(':hidden')) {\n                return;\n            }\n            $.each(data.attributes, function () {\n                if (this.status === 2) {\n                    this.status = 3;\n\n                    var $container = $form.find(this.container),\n                        $input = findInput($form, this);\n\n                    var $errorElement = data.settings.validationStateOn === 'input' ? $input : $container;\n\n                    $errorElement.addClass(data.settings.validatingCssClass);\n                }\n            });\n            methods.validate.call($form);\n        }, validationDelay ? validationDelay : 200);\n    };\n\n    /**\n     * Compares two value whatever it objects, arrays or simple types\n     * @param val1\n     * @param val2\n     * @returns boolean\n     */\n    var isEqual = function (val1, val2) {\n        // objects\n        if (val1 instanceof Object) {\n            return isObjectsEqual(val1, val2)\n        }\n\n        // arrays\n        if (Array.isArray(val1)) {\n            return isArraysEqual(val1, val2);\n        }\n\n        // simple types\n        return val1 === val2;\n    };\n\n    /**\n     * Compares two objects\n     * @param obj1\n     * @param obj2\n     * @returns boolean\n     */\n    var isObjectsEqual = function (obj1, obj2) {\n        if (!(obj1 instanceof Object) || !(obj2 instanceof Object)) {\n            return false;\n        }\n\n        var keys1 = Object.keys(obj1);\n        var keys2 = Object.keys(obj2);\n        if (keys1.length !== keys2.length) {\n            return false;\n        }\n\n        for (var i = 0; i < keys1.length; i += 1) {\n            if (!obj2.hasOwnProperty(keys1[i])) {\n                return false;\n            }\n            if (obj1[keys1[i]] !== obj2[keys1[i]]) {\n                return false;\n            }\n        }\n\n        return true;\n    };\n\n    /**\n     * Compares two arrays\n     * @param arr1\n     * @param arr2\n     * @returns boolean\n     */\n    var isArraysEqual = function (arr1, arr2) {\n        if (!Array.isArray(arr1) || !Array.isArray(arr2)) {\n            return false;\n        }\n\n        if (arr1.length !== arr2.length) {\n            return false;\n        }\n        for (var i = 0; i < arr1.length; i += 1) {\n            if (arr1[i] !== arr2[i]) {\n                return false;\n            }\n        }\n        return true;\n    };\n\n    /**\n     * Returns an array prototype with a shortcut method for adding a new deferred.\n     * The context of the callback will be the deferred object so it can be resolved like ```this.resolve()```\n     * @returns Array\n     */\n    var deferredArray = function () {\n        var array = [];\n        array.add = function (callback) {\n            this.push(new $.Deferred(callback));\n        };\n        return array;\n    };\n\n    var buttonOptions = ['action', 'target', 'method', 'enctype'];\n\n    /**\n     * Returns current form options\n     * @param $form\n     * @returns object Object with button of form options\n     */\n    var getFormOptions = function ($form) {\n        var attributes = {};\n        for (var i = 0; i < buttonOptions.length; i++) {\n            attributes[buttonOptions[i]] = $form.attr(buttonOptions[i]);\n        }\n\n        return attributes;\n    };\n\n    /**\n     * Applies temporary form options related to submit button\n     * @param $form the form jQuery object\n     * @param $button the button jQuery object\n     */\n    var applyButtonOptions = function ($form, $button) {\n        for (var i = 0; i < buttonOptions.length; i++) {\n            var value = $button.attr('form' + buttonOptions[i]);\n            if (value) {\n                $form.attr(buttonOptions[i], value);\n            }\n        }\n    };\n\n    /**\n     * Restores original form options\n     * @param $form the form jQuery object\n     */\n    var restoreButtonOptions = function ($form) {\n        var data = $form.data('yiiActiveForm');\n\n        for (var i = 0; i < buttonOptions.length; i++) {\n            $form.attr(buttonOptions[i], data.options[buttonOptions[i]] || null);\n        }\n    };\n\n    /**\n     * Updates the error messages and the input containers for all applicable attributes\n     * @param $form the form jQuery object\n     * @param messages array the validation error messages\n     * @param submitting whether this method is called after validation triggered by form submission\n     */\n    var updateInputs = function ($form, messages, submitting) {\n        var data = $form.data('yiiActiveForm');\n\n        if (data === undefined) {\n            return false;\n        }\n\n        var errorAttributes = [], $input;\n        $.each(data.attributes, function () {\n            var hasError = (submitting && updateInput($form, this, messages)) || (!submitting && attrHasError($form, this, messages));\n            $input = findInput($form, this);\n\n            if (!$input.is(':disabled') && !this.cancelled && hasError) {\n                errorAttributes.push(this);\n            }\n        });\n\n        $form.trigger(events.afterValidate, [messages, errorAttributes]);\n\n        if (submitting) {\n            updateSummary($form, messages);\n            if (errorAttributes.length) {\n                if (data.settings.scrollToError) {\n                    var h = $(document).height(), top = $form.find($.map(errorAttributes, function (attribute) {\n                        return attribute.input;\n                    }).join(',')).first().closest(':visible').offset().top - data.settings.scrollToErrorOffset;\n                    top = top < 0 ? 0 : (top > h ? h : top);\n                    var wtop = $(window).scrollTop();\n                    if (top < wtop || top > wtop + $(window).height()) {\n                        $(window).scrollTop(top);\n                    }\n                }\n                data.submitting = false;\n            } else {\n                data.validated = true;\n                if (!data.validate_only) {\n                    if (data.submitObject) {\n                        applyButtonOptions($form, data.submitObject);\n                    }\n                    $form.submit();\n                    if (data.submitObject) {\n                        restoreButtonOptions($form);\n                    }\n                }\n            }\n        } else {\n            $.each(data.attributes, function () {\n                if (!this.cancelled && (this.status === 2 || this.status === 3)) {\n                    updateInput($form, this, messages);\n                }\n            });\n        }\n        submitFinalize($form);\n    };\n\n    /**\n     * Updates hidden field that represents clicked submit button.\n     * @param $form the form jQuery object.\n     */\n    var updateHiddenButton = function ($form) {\n        var data = $form.data('yiiActiveForm');\n        var $button = data.submitObject || $form.find(':submit:first');\n        // TODO: if the submission is caused by \"change\" event, it will not work\n        if ($button.length && $button.attr('type') == 'submit' && $button.attr('name')) {\n            // simulate button input value\n            var $hiddenButton = $('input[type=\"hidden\"][name=\"' + $button.attr('name') + '\"]', $form);\n            if (!$hiddenButton.length) {\n                $('<input>').attr({\n                    type: 'hidden',\n                    name: $button.attr('name'),\n                    value: $button.attr('value')\n                }).appendTo($form);\n            } else {\n                $hiddenButton.attr('value', $button.attr('value'));\n            }\n        }\n    };\n\n    /**\n     * Updates the error message and the input container for a particular attribute.\n     * @param $form the form jQuery object\n     * @param attribute object the configuration for a particular attribute.\n     * @param messages array the validation error messages\n     * @return boolean whether there is a validation error for the specified attribute\n     */\n    var updateInput = function ($form, attribute, messages) {\n        var data = $form.data('yiiActiveForm'),\n            $input = findInput($form, attribute),\n            hasError = attrHasError($form, attribute, messages);\n\n        if (!$.isArray(messages[attribute.id])) {\n            messages[attribute.id] = [];\n        }\n\n        attribute.status = 1;\n        if ($input.length) {\n            var $container = $form.find(attribute.container);\n            var $error = $container.find(attribute.error);\n            updateAriaInvalid($form, attribute, hasError);\n\n            var $errorElement = data.settings.validationStateOn === 'input' ? $input : $container;\n\n            if (hasError) {\n                if (attribute.encodeError) {\n                    $error.text(messages[attribute.id][0]);\n                } else {\n                    $error.html(messages[attribute.id][0]);\n                }\n                $errorElement.removeClass(data.settings.validatingCssClass + ' ' + data.settings.successCssClass)\n                    .addClass(data.settings.errorCssClass);\n            } else {\n                $error.empty();\n                $errorElement.removeClass(data.settings.validatingCssClass + ' ' + data.settings.errorCssClass + ' ')\n                    .addClass(data.settings.successCssClass);\n            }\n            attribute.value = getValue($form, attribute);\n        }\n\n        $form.trigger(events.afterValidateAttribute, [attribute, messages[attribute.id]]);\n\n        return hasError;\n    };\n\n    /**\n     * Checks if a particular attribute has an error\n     * @param $form the form jQuery object\n     * @param attribute object the configuration for a particular attribute.\n     * @param messages array the validation error messages\n     * @return boolean whether there is a validation error for the specified attribute\n     */\n    var attrHasError = function ($form, attribute, messages) {\n        var $input = findInput($form, attribute),\n            hasError = false;\n\n        if (!$.isArray(messages[attribute.id])) {\n            messages[attribute.id] = [];\n        }\n\n        if ($input.length) {\n            hasError = messages[attribute.id].length > 0;\n        }\n\n        return hasError;\n    };\n\n    /**\n     * Updates the error summary.\n     * @param $form the form jQuery object\n     * @param messages array the validation error messages\n     */\n    var updateSummary = function ($form, messages) {\n        var data = $form.data('yiiActiveForm'),\n            $summary = $form.find(data.settings.errorSummary),\n            $ul = $summary.find('ul').empty();\n\n        if ($summary.length && messages) {\n            $.each(data.attributes, function () {\n                if ($.isArray(messages[this.id]) && messages[this.id].length) {\n                    var error = $('<li/>');\n                    if (data.settings.encodeErrorSummary) {\n                        error.text(messages[this.id][0]);\n                    } else {\n                        error.html(messages[this.id][0]);\n                    }\n                    $ul.append(error);\n                }\n            });\n            $summary.toggle($ul.find('li').length > 0);\n        }\n    };\n\n    var getValue = function ($form, attribute) {\n        var $input = findInput($form, attribute);\n        var type = $input.attr('type');\n        if (type === 'checkbox' || type === 'radio') {\n            var $realInput = $input.filter(':checked');\n            if ($realInput.length > 1) {\n                var values = [];\n                $realInput.each(function (index) {\n                    values.push($($realInput.get(index)).val());\n                });\n                return values;\n            }\n\n            if (!$realInput.length) {\n                $realInput = $form.find('input[type=hidden][name=\"' + $input.attr('name') + '\"]');\n            }\n\n            return $realInput.val();\n        } else {\n            return $input.val();\n        }\n    };\n\n    var findInput = function ($form, attribute) {\n        var $input = $form.find(attribute.input);\n        if ($input.length && $input[0].tagName.toLowerCase() === 'div') {\n            // checkbox list or radio list\n            return $input.find('input');\n        } else {\n            return $input;\n        }\n    };\n\n    var updateAriaInvalid = function ($form, attribute, hasError) {\n        if (attribute.updateAriaInvalid) {\n            $form.find(attribute.input).attr('aria-invalid', hasError ? 'true' : 'false');\n        }\n    }\n\n    var currentAjaxRequest = null;\n\n})(window.jQuery);\n"
  },
  {
    "path": "framework/assets/yii.captcha.js",
    "content": "/**\n * Yii Captcha widget.\n *\n * This is the JavaScript widget used by the yii\\captcha\\Captcha widget.\n *\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\n(function ($) {\n    $.fn.yiiCaptcha = function (method) {\n        if (methods[method]) {\n            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));\n        } else if (typeof method === 'object' || !method) {\n            return methods.init.apply(this, arguments);\n        } else {\n            $.error('Method ' + method + ' does not exist in jQuery.yiiCaptcha');\n            return false;\n        }\n    };\n\n    var defaults = {\n        refreshUrl: undefined,\n        hashKey: undefined\n    };\n\n    var methods = {\n        init: function (options) {\n            return this.each(function () {\n                var $e = $(this);\n                var settings = $.extend({}, defaults, options || {});\n                $e.data('yiiCaptcha', {\n                    settings: settings\n                });\n\n                $e.on('click.yiiCaptcha', function () {\n                    methods.refresh.apply($e);\n                    return false;\n                });\n            });\n        },\n\n        refresh: function () {\n            var $e = this,\n                settings = this.data('yiiCaptcha').settings;\n            $.ajax({\n                url: $e.data('yiiCaptcha').settings.refreshUrl,\n                dataType: 'json',\n                cache: false,\n                success: function (data) {\n                    $e.attr('src', data.url);\n                    $('body').data(settings.hashKey, [data.hash1, data.hash2]);\n                }\n            });\n        },\n\n        destroy: function () {\n            this.off('.yiiCaptcha');\n            this.removeData('yiiCaptcha');\n            return this;\n        },\n\n        data: function () {\n            return this.data('yiiCaptcha');\n        }\n    };\n})(window.jQuery);\n"
  },
  {
    "path": "framework/assets/yii.gridView.js",
    "content": "/**\n * Yii GridView widget.\n *\n * This is the JavaScript widget used by the yii\\grid\\GridView widget.\n *\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\n(function ($) {\n    $.fn.yiiGridView = function (method) {\n        if (methods[method]) {\n            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));\n        } else if (typeof method === 'object' || !method) {\n                return methods.init.apply(this, arguments);\n        } else {\n            $.error('Method ' + method + ' does not exist in jQuery.yiiGridView');\n            return false;\n        }\n    };\n\n    var defaults = {\n        filterUrl: undefined,\n        filterSelector: undefined,\n        filterOnFocusOut: true\n    };\n\n    var gridData = {};\n\n    var gridEvents = {\n        /**\n         * beforeFilter event is triggered before filtering the grid.\n         * The signature of the event handler should be:\n         *     function (event)\n         * where\n         *  - event: an Event object.\n         *\n         * If the handler returns a boolean false, it will stop filter form submission after this event. As\n         * a result, afterFilter event will not be triggered.\n         */\n        beforeFilter: 'beforeFilter',\n        /**\n         * afterFilter event is triggered after filtering the grid and filtered results are fetched.\n         * The signature of the event handler should be:\n         *     function (event)\n         * where\n         *  - event: an Event object.\n         */\n        afterFilter: 'afterFilter'\n    };\n\n    /**\n     * Used for storing active event handlers and removing them later.\n     * The structure of single event handler is:\n     *\n     * {\n     *     gridViewId: {\n     *         type: {\n     *             event: '...',\n     *             selector: '...'\n     *         }\n     *     }\n     * }\n     *\n     * Used types:\n     *\n     * - filter, used for filtering grid with elements found by filterSelector\n     * - checkRow, used for checking single row\n     * - checkAllRows, used for checking all rows with according \"Check all\" checkbox\n     *\n     * event is the name of event, for example: 'change.yiiGridView'\n     * selector is a jQuery selector for finding elements\n     *\n     * @type {{}}\n     */\n    var gridEventHandlers = {};\n\n    var methods = {\n        init: function (options) {\n            return this.each(function () {\n                var $e = $(this);\n                var settings = $.extend({}, defaults, options || {});\n                var id = $e.attr('id');\n                if (gridData[id] === undefined) {\n                    gridData[id] = {};\n                }\n\n                gridData[id] = $.extend(gridData[id], {settings: settings});\n\n                var filterEvents = 'change.yiiGridView keydown.yiiGridView';\n                var enterPressed = false;\n                initEventHandler($e, 'filter', filterEvents, settings.filterSelector, function (event) {\n                    if (event.type === 'keydown') {\n                        if (event.keyCode !== 13) {\n                            return; // only react to enter key\n                        } else {\n                            enterPressed = true;\n                        }\n                    } else {\n                        // prevent processing for both keydown and change events\n                        if (enterPressed) {\n                            enterPressed = false;\n                            return;\n                        }\n                    }\n                    if (!settings.filterOnFocusOut && event.type !== 'keydown') {\n                        return false;\n                    }\n\n                    methods.applyFilter.apply($e);\n\n                    return false;\n                });\n            });\n        },\n\n        applyFilter: function () {\n            var $grid = $(this);\n            var settings = gridData[$grid.attr('id')].settings;\n            var data = {};\n            $.each($(settings.filterSelector).serializeArray(), function () {\n                if (!(this.name in data)) {\n                    data[this.name] = [];\n                }\n                data[this.name].push(this.value);\n            });\n\n            var namesInFilter = Object.keys(data);\n\n            $.each(yii.getQueryParams(settings.filterUrl), function (name, value) {\n                if (namesInFilter.indexOf(name) === -1 && namesInFilter.indexOf(name.replace(/\\[\\d*\\]$/, '')) === -1) {\n                    if (!$.isArray(value)) {\n                        value = [value];\n                    }\n                    if (!(name in data)) {\n                        data[name] = value;\n                    } else {\n                        $.each(value, function (i, val) {\n                            if ($.inArray(val, data[name])) {\n                                data[name].push(val);\n                            }\n                        });\n                    }\n                }\n            });\n\n            var pos = settings.filterUrl.indexOf('?');\n            var url = pos < 0 ? settings.filterUrl : settings.filterUrl.substring(0, pos);\n            var hashPos = settings.filterUrl.indexOf('#');\n            if (pos >= 0 && hashPos >= 0) {\n                url += settings.filterUrl.substring(hashPos);\n            }\n\n            $grid.find('form.gridview-filter-form').remove();\n            var $form = $('<form/>', {\n                action: url,\n                method: 'get',\n                'class': 'gridview-filter-form',\n                style: 'display:none',\n                'data-pjax': ''\n            }).appendTo($grid);\n            $.each(data, function (name, values) {\n                $.each(values, function (index, value) {\n                    $form.append($('<input/>').attr({type: 'hidden', name: name, value: value}));\n                });\n            });\n\n            var event = $.Event(gridEvents.beforeFilter);\n            $grid.trigger(event);\n            if (event.result === false) {\n                return;\n            }\n\n            $form.submit();\n\n            $grid.trigger(gridEvents.afterFilter);\n        },\n\n        setSelectionColumn: function (options) {\n            var $grid = $(this);\n            var id = $(this).attr('id');\n            if (gridData[id] === undefined) {\n                gridData[id] = {};\n            }\n            gridData[id].selectionColumn = options.name;\n            if (!options.multiple || !options.checkAll) {\n                return;\n            }\n            var checkAllInput = \"input[name='\" + options.checkAll + \"']\";\n            var inputs = (options['class'] ? \"input.\" + options['class'] : \"input[name='\" + options.name + \"']\") + \":enabled\";\n            initEventHandler($grid, 'checkAllRows', 'click.yiiGridView', \"#\" + id + \" \" + checkAllInput, function () {\n                $grid.find(inputs + (this.checked ? \":not(:checked)\" : \":checked\")).prop('checked', this.checked).change();\n            });\n            var handler = function () {\n                var all = $grid.find(inputs).length == $grid.find(inputs + \":checked\").length;\n                $grid.find(checkAllInput + (all ? \":not(:checked)\" : \":checked\")).prop('checked', all).change();\n            };\n            initEventHandler($grid, 'checkRow', 'click.yiiGridView', \"#\" + id + \" \" + inputs, handler);\n            if ($grid.find(inputs).length) {\n                handler(); // Ensure \"check all\" checkbox is checked on page load if all data row checkboxes are initially checked.\n            }\n        },\n\n        getSelectedRows: function () {\n            var $grid = $(this);\n            var data = gridData[$grid.attr('id')];\n            var keys = [];\n            if (data.selectionColumn) {\n                $grid.find(\"input[name='\" + data.selectionColumn + \"']:checked\").each(function () {\n                    keys.push($(this).parent().closest('tr').data('key'));\n                });\n            }\n\n            return keys;\n        },\n\n        destroy: function () {\n            var events = ['.yiiGridView', gridEvents.beforeFilter, gridEvents.afterFilter].join(' ');\n            this.off(events);\n\n            var id = $(this).attr('id');\n            $.each(gridEventHandlers[id], function (type, data) {\n                $(document).off(data.event, data.selector);\n            });\n\n            delete gridData[id];\n\n            return this;\n        },\n\n        data: function () {\n            var id = $(this).attr('id');\n            return gridData[id];\n        }\n    };\n\n    /**\n     * Used for attaching event handler and prevent of duplicating them. With each call previously attached handler of\n     * the same type is removed even selector was changed.\n     * @param {jQuery} $gridView According jQuery grid view element\n     * @param {string} type Type of the event which acts like a key\n     * @param {string} event Event name, for example 'change.yiiGridView'\n     * @param {string} selector jQuery selector\n     * @param {function} callback The actual function to be executed with this event\n     */\n    function initEventHandler($gridView, type, event, selector, callback)\n    {\n        var id = $gridView.attr('id');\n        var prevHandler = gridEventHandlers[id];\n        if (prevHandler !== undefined && prevHandler[type] !== undefined) {\n            var data = prevHandler[type];\n            $(document).off(data.event, data.selector);\n        }\n        if (prevHandler === undefined) {\n            gridEventHandlers[id] = {};\n        }\n        $(document).on(event, selector, callback);\n        gridEventHandlers[id][type] = {event: event, selector: selector};\n    }\n})(window.jQuery);\n"
  },
  {
    "path": "framework/assets/yii.js",
    "content": "/**\n * Yii JavaScript module.\n *\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\n\n/**\n * yii is the root module for all Yii JavaScript modules.\n * It implements a mechanism of organizing JavaScript code in modules through the function \"yii.initModule()\".\n *\n * Each module should be named as \"x.y.z\", where \"x\" stands for the root module (for the Yii core code, this is \"yii\").\n *\n * A module may be structured as follows:\n *\n * ```javascript\n * window.yii.sample = (function($) {\n *     var pub = {\n *         // whether this module is currently active. If false, init() will not be called for this module\n *         // it will also not be called for all its child modules. If this property is undefined, it means true.\n *         isActive: true,\n *         init: function() {\n *             // ... module initialization code goes here ...\n *         },\n *\n *         // ... other public functions and properties go here ...\n *     };\n *\n *     // ... private functions and properties go here ...\n *\n *     return pub;\n * })(window.jQuery);\n * ```\n *\n * Using this structure, you can define public and private functions/properties for a module.\n * Private functions/properties are only visible within the module, while public functions/properties\n * may be accessed outside of the module. For example, you can access \"yii.sample.isActive\".\n *\n * You must call \"yii.initModule()\" once for the root module of all your modules.\n */\nwindow.yii = (function ($) {\n    var pub = {\n        /**\n         * List of JS or CSS URLs that can be loaded multiple times via AJAX requests.\n         * Each item may be represented as either an absolute URL or a relative one.\n         * Each item may contain a wildcard matching character `*`, that means one or more\n         * any characters on the position. For example:\n         *  - `/css/*.css` will match any file ending with `.css` in the `css` directory of the current web site\n         *  - `http*://cdn.example.com/*` will match any files on domain `cdn.example.com`, loaded with HTTP or HTTPS\n         *  - `/js/myCustomScript.js?realm=*` will match file `/js/myCustomScript.js` with defined `realm` parameter\n         */\n        reloadableScripts: [],\n        /**\n         * The selector for clickable elements that need to support confirmation and form submission.\n         */\n        clickableSelector: 'a, button, input[type=\"submit\"], input[type=\"button\"], input[type=\"reset\"], ' +\n            'input[type=\"image\"]',\n        /**\n         * The selector for changeable elements that need to support confirmation and form submission.\n         */\n        changeableSelector: 'select, input, textarea',\n\n        /**\n         * @return string|undefined the CSRF parameter name. Undefined is returned if CSRF validation is not enabled.\n         */\n        getCsrfParam: function () {\n            return $('meta[name=csrf-param]').attr('content');\n        },\n\n        /**\n         * @return string|undefined the CSRF token. Undefined is returned if CSRF validation is not enabled.\n         */\n        getCsrfToken: function () {\n            return $('meta[name=csrf-token]').attr('content');\n        },\n\n        /**\n         * Sets the CSRF token in the meta elements.\n         * This method is provided so that you can update the CSRF token with the latest one you obtain from the server.\n         * @param name the CSRF token name\n         * @param value the CSRF token value\n         */\n        setCsrfToken: function (name, value) {\n            $('meta[name=csrf-param]').attr('content', name);\n            $('meta[name=csrf-token]').attr('content', value);\n        },\n\n        /**\n         * Updates all form CSRF input fields with the latest CSRF token.\n         * This method is provided to avoid cached forms containing outdated CSRF tokens.\n         */\n        refreshCsrfToken: function () {\n            var token = pub.getCsrfToken();\n            if (token) {\n                $('form input[name=\"' + pub.getCsrfParam() + '\"]').val(token);\n            }\n        },\n\n        /**\n         * Displays a confirmation dialog.\n         * The default implementation simply displays a js confirmation dialog.\n         * You may override this by setting `yii.confirm`.\n         * @param message the confirmation message.\n         * @param ok a callback to be called when the user confirms the message\n         * @param cancel a callback to be called when the user cancels the confirmation\n         */\n        confirm: function (message, ok, cancel) {\n            if (window.confirm(message)) {\n                !ok || ok();\n            } else {\n                !cancel || cancel();\n            }\n        },\n\n        /**\n         * Handles the action triggered by user.\n         * This method recognizes the `data-method` attribute of the element. If the attribute exists,\n         * the method will submit the form containing this element. If there is no containing form, a form\n         * will be created and submitted using the method given by this attribute value (e.g. \"post\", \"put\").\n         * For hyperlinks, the form action will take the value of the \"href\" attribute of the link.\n         * For other elements, either the containing form action or the current page URL will be used\n         * as the form action URL.\n         *\n         * If the `data-method` attribute is not defined, the `href` attribute (if any) of the element\n         * will be assigned to `window.location`.\n         *\n         * Starting from version 2.0.3, the `data-params` attribute is also recognized when you specify\n         * `data-method`. The value of `data-params` should be a JSON representation of the data (name-value pairs)\n         * that should be submitted as hidden inputs. For example, you may use the following code to generate\n         * such a link:\n         *\n         * ```php\n         * use yii\\helpers\\Html;\n         * use yii\\helpers\\Json;\n         *\n         * echo Html::a('submit', ['site/foobar'], [\n         *     'data' => [\n         *         'method' => 'post',\n         *         'params' => [\n         *             'name1' => 'value1',\n         *             'name2' => 'value2',\n         *         ],\n         *     ],\n         * ]);\n         * ```\n         *\n         * @param $e the jQuery representation of the element\n         * @param event Related event\n         */\n        handleAction: function ($e, event) {\n            var $form = $e.attr('data-form') ? $('#' + $e.attr('data-form')) : $e.closest('form'),\n                method = !$e.data('method') && $form ? $form.attr('method') : $e.data('method'),\n                action = $e.attr('href'),\n                isValidAction = action && action !== '#',\n                params = $e.data('params'),\n                areValidParams = params && $.isPlainObject(params),\n                pjax = $e.data('pjax'),\n                usePjax = pjax !== undefined && pjax !== 0 && $.support.pjax,\n                pjaxContainer,\n                pjaxOptions = {},\n                conflictParams = ['submit', 'reset', 'elements', 'length', 'name', 'acceptCharset',\n                    'action', 'enctype', 'method', 'target'];\n\n            // Forms and their child elements should not use input names or ids that conflict with properties of a form,\n            // such as submit, length, or method.\n            $.each(conflictParams, function (index, param) {\n                if (areValidParams && params.hasOwnProperty(param)) {\n                    console.error(\"Parameter name '\" + param + \"' conflicts with a same named form property. \" +\n                        \"Please use another name.\");\n                }\n            });\n\n            if (usePjax) {\n                pjaxContainer = $e.data('pjax-container');\n                if (pjaxContainer === undefined || !pjaxContainer.length) {\n                    pjaxContainer = $e.closest('[data-pjax-container]').attr('id')\n                        ? ('#' + $e.closest('[data-pjax-container]').attr('id'))\n                        : '';\n                }\n                if (!pjaxContainer.length) {\n                    pjaxContainer = 'body';\n                }\n                pjaxOptions = {\n                    container: pjaxContainer,\n                    push: !!$e.data('pjax-push-state'),\n                    replace: !!$e.data('pjax-replace-state'),\n                    scrollTo: $e.data('pjax-scrollto'),\n                    pushRedirect: $e.data('pjax-push-redirect'),\n                    replaceRedirect: $e.data('pjax-replace-redirect'),\n                    skipOuterContainers: $e.data('pjax-skip-outer-containers'),\n                    timeout: $e.data('pjax-timeout'),\n                    originalEvent: event,\n                    originalTarget: $e\n                };\n            }\n\n            if (method === undefined) {\n                if (isValidAction) {\n                    usePjax ? $.pjax.click(event, pjaxOptions) : window.location.assign(action);\n                } else if ($e.is(':submit') && $form.length) {\n                    if (usePjax) {\n                        $form.on('submit', function (e) {\n                            $.pjax.submit(e, pjaxOptions);\n                        });\n                    }\n                    $form.trigger('submit');\n                }\n                return;\n            }\n\n            var oldMethod,\n                oldAction,\n                newForm = !$form.length;\n            if (!newForm) {\n                oldMethod = $form.attr('method');\n                $form.attr('method', method);\n                if (isValidAction) {\n                    oldAction = $form.attr('action');\n                    $form.attr('action', action);\n                }\n            } else {\n                if (!isValidAction) {\n                    action = pub.getCurrentUrl();\n                }\n                $form = $('<form/>', {method: method, action: action});\n                var target = $e.attr('target');\n                if (target) {\n                    $form.attr('target', target);\n                }\n                if (!/(get|post)/i.test(method)) {\n                    $form.append($('<input/>', {name: '_method', value: method, type: 'hidden'}));\n                    method = 'post';\n                    $form.attr('method', method);\n                }\n                if (/post/i.test(method)) {\n                    var csrfParam = pub.getCsrfParam();\n                    if (csrfParam) {\n                        $form.append($('<input/>', {name: csrfParam, value: pub.getCsrfToken(), type: 'hidden'}));\n                    }\n                }\n                $form.hide().appendTo('body');\n            }\n\n            var activeFormData = $form.data('yiiActiveForm');\n            if (activeFormData) {\n                // Remember the element triggered the form submission. This is used by yii.activeForm.js.\n                activeFormData.submitObject = $e;\n            }\n\n            if (areValidParams) {\n                $.each(params, function (name, value) {\n                    $form.append($('<input/>').attr({name: name, value: value, type: 'hidden'}));\n                });\n            }\n\n            if (usePjax) {\n                $form.on('submit', function (e) {\n                    $.pjax.submit(e, pjaxOptions);\n                });\n            }\n\n            $form.trigger('submit');\n\n            $.when($form.data('yiiSubmitFinalizePromise')).done(function () {\n                if (newForm) {\n                    $form.remove();\n                    return;\n                }\n\n                if (oldAction !== undefined) {\n                    $form.attr('action', oldAction);\n                }\n                $form.attr('method', oldMethod);\n\n                if (areValidParams) {\n                    $.each(params, function (name) {\n                        $('input[name=\"' + name + '\"]', $form).remove();\n                    });\n                }\n            });\n        },\n\n        getQueryParams: function (url) {\n            var pos = url.indexOf('?');\n            if (pos < 0) {\n                return {};\n            }\n\n            var pairs = $.grep(url.substring(pos + 1).split('#')[0].split('&'), function (value) {\n                return value !== '';\n            });\n            var params = {};\n\n            for (var i = 0, len = pairs.length; i < len; i++) {\n                var pair = pairs[i].split('=');\n                var name = decodeURIComponent(pair[0].replace(/\\+/g, '%20'));\n                var value = pair.length > 1 ? decodeURIComponent(pair[1].replace(/\\+/g, '%20')) : '';\n                if (!name.length) {\n                    continue;\n                }\n                if (params[name] === undefined) {\n                    params[name] = value || '';\n                } else {\n                    if (!$.isArray(params[name])) {\n                        params[name] = [params[name]];\n                    }\n                    params[name].push(value || '');\n                }\n            }\n\n            return params;\n        },\n\n        initModule: function (module) {\n            if (module.isActive !== undefined && !module.isActive) {\n                return;\n            }\n            if ($.isFunction(module.init)) {\n                module.init();\n            }\n            $.each(module, function () {\n                if ($.isPlainObject(this)) {\n                    pub.initModule(this);\n                }\n            });\n        },\n\n        init: function () {\n            initCsrfHandler();\n            initRedirectHandler();\n            initAssetFilters();\n            initDataMethods();\n        },\n\n        /**\n         * Returns the URL of the current page without params and trailing slash. Separated and made public for testing.\n         * @returns {string}\n         */\n        getBaseCurrentUrl: function () {\n            return window.location.protocol + '//' + window.location.host;\n        },\n\n        /**\n         * Returns the URL of the current page. Used for testing, you can always call `window.location.href` manually\n         * instead.\n         * @returns {string}\n         */\n        getCurrentUrl: function () {\n            return window.location.href;\n        }\n    };\n\n    function initCsrfHandler()\n    {\n        // automatically send CSRF token for all AJAX requests\n        $.ajaxPrefilter(function (options, originalOptions, xhr) {\n            if (!options.crossDomain && pub.getCsrfParam()) {\n                xhr.setRequestHeader('X-CSRF-Token', pub.getCsrfToken());\n            }\n        });\n        pub.refreshCsrfToken();\n    }\n\n    function initRedirectHandler()\n    {\n        // handle AJAX redirection\n        $(document).ajaxComplete(function (event, xhr) {\n            var url = xhr && xhr.getResponseHeader('X-Redirect');\n            if (url) {\n                window.location.assign(url);\n            }\n        });\n    }\n\n    function initAssetFilters()\n    {\n        /**\n         * Used for storing loaded scripts and information about loading each script if it's in the process of loading.\n         * A single script can have one of the following values:\n         *\n         * - `undefined` - script was not loaded at all before or was loaded with error last time.\n         * - `true` (boolean) -  script was successfully loaded.\n         * - object - script is currently loading.\n         *\n         * In case of a value being an object the properties are:\n         * - `xhrList` - represents a queue of XHR requests sent to the same URL (related with this script) in the same\n         * small period of time.\n         * - `xhrDone` - boolean, acts like a locking mechanism. When one of the XHR requests in the queue is\n         * successfully completed, it will abort the rest of concurrent requests to the same URL until cleanup is done\n         * to prevent possible errors and race conditions.\n         * @type {{}}\n         */\n        var loadedScripts = {};\n\n        $('script[src]').each(function () {\n            var url = getAbsoluteUrl(this.src);\n            loadedScripts[url] = true;\n        });\n\n        $.ajaxPrefilter('script', function (options, originalOptions, xhr) {\n            if (options.dataType == 'jsonp') {\n                return;\n            }\n\n            var url = getAbsoluteUrl(options.url),\n                forbiddenRepeatedLoad = loadedScripts[url] === true && !isReloadableAsset(url),\n                cleanupRunning = loadedScripts[url] !== undefined && loadedScripts[url]['xhrDone'] === true;\n\n            if (forbiddenRepeatedLoad || cleanupRunning) {\n                xhr.abort();\n                return;\n            }\n\n            if (loadedScripts[url] === undefined || loadedScripts[url] === true) {\n                loadedScripts[url] = {\n                    xhrList: [],\n                    xhrDone: false\n                };\n            }\n\n            xhr.done(function (data, textStatus, jqXHR) {\n                // If multiple requests were successfully loaded, perform cleanup only once\n                if (loadedScripts[jqXHR.yiiUrl]['xhrDone'] === true) {\n                    return;\n                }\n\n                loadedScripts[jqXHR.yiiUrl]['xhrDone'] = true;\n\n                for (var i = 0, len = loadedScripts[jqXHR.yiiUrl]['xhrList'].length; i < len; i++) {\n                    var singleXhr = loadedScripts[jqXHR.yiiUrl]['xhrList'][i];\n                    if (singleXhr && singleXhr.readyState !== XMLHttpRequest.DONE) {\n                        singleXhr.abort();\n                    }\n                }\n\n                loadedScripts[jqXHR.yiiUrl] = true;\n            }).fail(function (jqXHR, textStatus) {\n                if (textStatus === 'abort') {\n                    return;\n                }\n\n                delete loadedScripts[jqXHR.yiiUrl]['xhrList'][jqXHR.yiiIndex];\n\n                var allFailed = true;\n                for (var i = 0, len = loadedScripts[jqXHR.yiiUrl]['xhrList'].length; i < len; i++) {\n                    if (loadedScripts[jqXHR.yiiUrl]['xhrList'][i]) {\n                        allFailed = false;\n                    }\n                }\n\n                if (allFailed) {\n                    delete loadedScripts[jqXHR.yiiUrl];\n                }\n            });\n            // Use prefix for custom XHR properties to avoid possible conflicts with existing properties\n            xhr.yiiIndex = loadedScripts[url]['xhrList'].length;\n            xhr.yiiUrl = url;\n\n            loadedScripts[url]['xhrList'][xhr.yiiIndex] = xhr;\n        });\n\n        $(document).ajaxComplete(function () {\n            var styleSheets = [];\n            $('link[rel=stylesheet]').each(function () {\n                var url = getAbsoluteUrl(this.href);\n                if (isReloadableAsset(url)) {\n                    return;\n                }\n\n                $.inArray(url, styleSheets) === -1 ? styleSheets.push(url) : $(this).remove();\n            });\n        });\n    }\n\n    function initDataMethods()\n    {\n        var handler = function (event) {\n            var $this = $(this),\n                method = $this.data('method'),\n                message = $this.data('confirm'),\n                form = $this.data('form');\n\n            if (method === undefined && message === undefined && form === undefined) {\n                return true;\n            }\n\n            if (message !== undefined && message !== false && message !== '') {\n                $.proxy(pub.confirm, this)(message, function () {\n                    pub.handleAction($this, event);\n                });\n            } else {\n                pub.handleAction($this, event);\n            }\n            event.stopImmediatePropagation();\n            return false;\n        };\n\n        // handle data-confirm and data-method for clickable and changeable elements\n        $(document).on('click.yii', pub.clickableSelector, handler)\n            .on('change.yii', pub.changeableSelector, handler);\n    }\n\n    function isReloadableAsset(url)\n    {\n        for (var i = 0; i < pub.reloadableScripts.length; i++) {\n            var rule = getAbsoluteUrl(pub.reloadableScripts[i]);\n            var match = new RegExp(\"^\" + escapeRegExp(rule).split('\\\\*').join('.+') + \"$\").test(url);\n            if (match === true) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    // https://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex/6969486#6969486\n    function escapeRegExp(str)\n    {\n        return str.replace(/[\\-\\[\\]\\/\\{\\}\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|]/g, \"\\\\$&\");\n    }\n\n    /**\n     * Returns absolute URL based on the given URL\n     * @param {string} url Initial URL\n     * @returns {string}\n     */\n    function getAbsoluteUrl(url)\n    {\n        return url.charAt(0) === '/' ? pub.getBaseCurrentUrl() + url : url;\n    }\n\n    return pub;\n})(window.jQuery);\n\nwindow.jQuery(function () {\n    window.yii.initModule(window.yii);\n});\n"
  },
  {
    "path": "framework/assets/yii.validation.js",
    "content": "/**\n * Yii validation module.\n *\n * This JavaScript module provides the validation methods for the built-in validators.\n *\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\n\nyii.validation = (function ($) {\n    var pub = {\n        isEmpty: function (value) {\n            return value === null || value === undefined || ($.isArray(value) && value.length === 0) || value === '';\n        },\n\n        addMessage: function (messages, message, value) {\n            messages.push(message.replace(/\\{value\\}/g, value));\n        },\n\n        required: function (value, messages, options) {\n            var valid = false;\n            if (options.requiredValue === undefined) {\n                var isString = typeof value == 'string' || value instanceof String;\n                if (options.strict && value !== undefined || !options.strict && !pub.isEmpty(isString ? trimString(value) : value)) {\n                    valid = true;\n                }\n            } else if (!options.strict && value == options.requiredValue || options.strict && value === options.requiredValue) {\n                valid = true;\n            }\n\n            if (!valid) {\n                pub.addMessage(messages, options.message, value);\n            }\n        },\n\n        // \"boolean\" is a reserved keyword in older versions of ES so it's quoted for IE < 9 support\n        'boolean': function (value, messages, options) {\n            if (options.skipOnEmpty && pub.isEmpty(value)) {\n                return;\n            }\n            var valid = !options.strict && (value == options.trueValue || value == options.falseValue)\n                || options.strict && (value === options.trueValue || value === options.falseValue);\n\n            if (!valid) {\n                pub.addMessage(messages, options.message, value);\n            }\n        },\n\n        string: function (value, messages, options) {\n            if (options.skipOnEmpty && pub.isEmpty(value)) {\n                return;\n            }\n\n            if (typeof value !== 'string') {\n                pub.addMessage(messages, options.message, value);\n                return;\n            }\n\n            if (options.is !== undefined && value.length != options.is) {\n                pub.addMessage(messages, options.notEqual, value);\n                return;\n            }\n            if (options.min !== undefined && value.length < options.min) {\n                pub.addMessage(messages, options.tooShort, value);\n            }\n            if (options.max !== undefined && value.length > options.max) {\n                pub.addMessage(messages, options.tooLong, value);\n            }\n        },\n\n        file: function (attribute, messages, options) {\n            var files = getUploadedFiles(attribute, messages, options);\n            $.each(files, function (i, file) {\n                validateFile(file, messages, options);\n            });\n        },\n\n        image: function (attribute, messages, options, deferredList) {\n            var files = getUploadedFiles(attribute, messages, options);\n            $.each(files, function (i, file) {\n                validateFile(file, messages, options);\n\n                // Skip image validation if FileReader API is not available\n                if (typeof FileReader === \"undefined\") {\n                    return;\n                }\n\n                var deferred = $.Deferred();\n                pub.validateImage(file, messages, options, deferred, new FileReader(), new Image());\n                deferredList.push(deferred);\n            });\n        },\n\n        validateImage: function (file, messages, options, deferred, fileReader, image) {\n            image.onload = function () {\n                validateImageSize(file, image, messages, options);\n                deferred.resolve();\n            };\n\n            image.onerror = function () {\n                messages.push(options.notImage.replace(/\\{file\\}/g, file.name));\n                deferred.resolve();\n            };\n\n            fileReader.onload = function () {\n                image.src = this.result;\n            };\n\n            // Resolve deferred if there was error while reading data\n            fileReader.onerror = function () {\n                deferred.resolve();\n            };\n\n            fileReader.readAsDataURL(file);\n        },\n\n        number: function (value, messages, options) {\n            if (options.skipOnEmpty && pub.isEmpty(value)) {\n                return;\n            }\n\n            if (typeof value === 'string' && !options.pattern.test(value)) {\n                pub.addMessage(messages, options.message, value);\n                return;\n            }\n\n            if (options.min !== undefined && value < options.min) {\n                pub.addMessage(messages, options.tooSmall, value);\n            }\n            if (options.max !== undefined && value > options.max) {\n                pub.addMessage(messages, options.tooBig, value);\n            }\n        },\n\n        range: function (value, messages, options) {\n            if (options.skipOnEmpty && pub.isEmpty(value)) {\n                return;\n            }\n\n            if (!options.allowArray && $.isArray(value)) {\n                pub.addMessage(messages, options.message, value);\n                return;\n            }\n\n            var inArray = true;\n\n            $.each($.isArray(value) ? value : [value], function (i, v) {\n                if ($.inArray(v, options.range) == -1) {\n                    inArray = false;\n                    return false;\n                } else {\n                    return true;\n                }\n            });\n\n            if (options.not === undefined) {\n                options.not = false;\n            }\n\n            if (options.not === inArray) {\n                pub.addMessage(messages, options.message, value);\n            }\n        },\n\n        regularExpression: function (value, messages, options) {\n            if (options.skipOnEmpty && pub.isEmpty(value)) {\n                return;\n            }\n\n            if (!options.not && !options.pattern.test(value) || options.not && options.pattern.test(value)) {\n                pub.addMessage(messages, options.message, value);\n            }\n        },\n\n        email: function (value, messages, options) {\n            if (options.skipOnEmpty && pub.isEmpty(value)) {\n                return;\n            }\n\n            var valid = true,\n                regexp = /^((?:\"?([^\"]*)\"?\\s)?)(?:\\s+)?(?:(<?)((.+)@([^>]+))(>?))$/,\n                matches = regexp.exec(value);\n\n            if (matches === null) {\n                valid = false;\n            } else {\n                var localPart = matches[5],\n                    domain = matches[6];\n\n                if (options.enableIDN) {\n                    localPart = punycode.toASCII(localPart);\n                    domain = punycode.toASCII(domain);\n\n                    value = matches[1] + matches[3] + localPart + '@' + domain + matches[7];\n                }\n\n                if (localPart.length > 64) {\n                    valid = false;\n                } else if ((localPart + '@' + domain).length > 254) {\n                    valid = false;\n                } else {\n                    valid = options.pattern.test(value) || (options.allowName && options.fullPattern.test(value));\n                }\n            }\n\n            if (!valid) {\n                pub.addMessage(messages, options.message, value);\n            }\n        },\n\n        url: function (value, messages, options) {\n            if (options.skipOnEmpty && pub.isEmpty(value)) {\n                return;\n            }\n\n            if (options.defaultScheme && !/:\\/\\//.test(value)) {\n                value = options.defaultScheme + '://' + value;\n            }\n\n            var valid = true;\n\n            if (options.enableIDN) {\n                var matches = /^([^:]+):\\/\\/([^\\/]+)(.*)$/.exec(value);\n                if (matches === null) {\n                    valid = false;\n                } else {\n                    value = matches[1] + '://' + punycode.toASCII(matches[2]) + matches[3];\n                }\n            }\n\n            if (!valid || !options.pattern.test(value)) {\n                pub.addMessage(messages, options.message, value);\n            }\n        },\n\n        trim: function ($form, attribute, options, value) {\n            var $input = $form.find(attribute.input);\n            if ($input.is(':checkbox, :radio')) {\n                return value;\n            }\n\n            value = $input.val();\n            if (\n                (!options.skipOnEmpty || !pub.isEmpty(value))\n                && (!options.skipOnArray || !Array.isArray(value))\n            ) {\n                if (Array.isArray(value)) {\n                    for (var i = 0; i < value.length; i++) {\n                        value[i] = trimString(value[i], options);\n                    }\n                } else {\n                    value = trimString(value, options);\n                }\n                $input.val(value);\n            }\n\n            return value;\n        },\n\n        captcha: function (value, messages, options) {\n            if (options.skipOnEmpty && pub.isEmpty(value)) {\n                return;\n            }\n\n            // CAPTCHA may be updated via AJAX and the updated hash is stored in body data\n            var hash = $('body').data(options.hashKey);\n            hash = hash == null ? options.hash : hash[options.caseSensitive ? 0 : 1];\n            var v = options.caseSensitive ? value : value.toLowerCase();\n            for (var i = v.length - 1, h = 0; i >= 0; --i) {\n                h += v.charCodeAt(i) << i;\n            }\n            if (h != hash) {\n                pub.addMessage(messages, options.message, value);\n            }\n        },\n\n        compare: function (value, messages, options, $form) {\n            if (options.skipOnEmpty && pub.isEmpty(value)) {\n                return;\n            }\n\n            var compareValue,\n                valid = true;\n            if (options.compareAttribute === undefined) {\n                compareValue = options.compareValue;\n            } else {\n                var $target = $('#' + options.compareAttribute);\n                if (!$target.length) {\n                    $target = $form.find('[name=\"' + options.compareAttributeName + '\"]');\n                }\n                compareValue = $target.val();\n            }\n\n            if (options.type === 'number') {\n                value = value ? parseFloat(value) : 0;\n                compareValue = compareValue ? parseFloat(compareValue) : 0;\n            }\n            switch (options.operator) {\n                case '==':\n                    valid = value == compareValue;\n                    break;\n                case '===':\n                    valid = value === compareValue;\n                    break;\n                case '!=':\n                    valid = value != compareValue;\n                    break;\n                case '!==':\n                    valid = value !== compareValue;\n                    break;\n                case '>':\n                    valid = value > compareValue;\n                    break;\n                case '>=':\n                    valid = value >= compareValue;\n                    break;\n                case '<':\n                    valid = value < compareValue;\n                    break;\n                case '<=':\n                    valid = value <= compareValue;\n                    break;\n                default:\n                    valid = false;\n                    break;\n            }\n\n            if (!valid) {\n                pub.addMessage(messages, options.message, value);\n            }\n        },\n\n        ip: function (value, messages, options) {\n            if (options.skipOnEmpty && pub.isEmpty(value)) {\n                return;\n            }\n\n            var negation = null,\n                cidr = null,\n                matches = new RegExp(options.ipParsePattern).exec(value);\n            if (matches) {\n                negation = matches[1] || null;\n                value = matches[2];\n                cidr = matches[4] || null;\n            }\n\n            if (options.subnet === true && cidr === null) {\n                pub.addMessage(messages, options.messages.noSubnet, value);\n                return;\n            }\n            if (options.subnet === false && cidr !== null) {\n                pub.addMessage(messages, options.messages.hasSubnet, value);\n                return;\n            }\n            if (options.negation === false && negation !== null) {\n                pub.addMessage(messages, options.messages.message, value);\n                return;\n            }\n\n            var ipVersion = value.indexOf(':') === -1 ? 4 : 6;\n            if (ipVersion == 6) {\n                if (!(new RegExp(options.ipv6Pattern)).test(value)) {\n                    pub.addMessage(messages, options.messages.message, value);\n                }\n                if (!options.ipv6) {\n                    pub.addMessage(messages, options.messages.ipv6NotAllowed, value);\n                }\n            } else {\n                if (!(new RegExp(options.ipv4Pattern)).test(value)) {\n                    pub.addMessage(messages, options.messages.message, value);\n                }\n                if (!options.ipv4) {\n                    pub.addMessage(messages, options.messages.ipv4NotAllowed, value);\n                }\n            }\n        }\n    };\n\n    function getUploadedFiles(attribute, messages, options)\n    {\n        // Skip validation if File API is not available\n        if (typeof File === \"undefined\") {\n            return [];\n        }\n\n        var fileInput = $(attribute.input, attribute.$form).get(0);\n\n        // Skip validation if file input does not exist\n        // (in case file inputs are added dynamically and no file input has been added to the form)\n        if (typeof fileInput === \"undefined\") {\n            return [];\n        }\n\n        var files = fileInput.files;\n        if (!files) {\n            messages.push(options.message);\n            return [];\n        }\n\n        if (files.length === 0) {\n            if (!options.skipOnEmpty) {\n                messages.push(options.uploadRequired);\n            }\n\n            return [];\n        }\n\n        if (options.maxFiles && options.maxFiles < files.length) {\n            messages.push(options.tooMany);\n            return [];\n        }\n\n        return files;\n    }\n\n    function validateFile(file, messages, options)\n    {\n        if (options.extensions && options.extensions.length > 0) {\n            var found = false;\n            var filename = file.name.toLowerCase();\n\n            for (var index = 0; index < options.extensions.length; index++) {\n                var ext = options.extensions[index].toLowerCase();\n                if ((ext === '' && filename.indexOf('.') === -1) || (filename.substr(filename.length - options.extensions[index].length - 1) === ('.' + ext))) {\n                    found = true;\n                    break;\n                }\n            }\n\n            if (!found) {\n                messages.push(options.wrongExtension.replace(/\\{file\\}/g, file.name));\n            }\n        }\n\n        if (options.mimeTypes && options.mimeTypes.length > 0) {\n            if (!validateMimeType(options.mimeTypes, file.type)) {\n                messages.push(options.wrongMimeType.replace(/\\{file\\}/g, file.name));\n            }\n        }\n\n        if (options.maxSize && options.maxSize < file.size) {\n            messages.push(options.tooBig.replace(/\\{file\\}/g, file.name));\n        }\n\n        if (options.minSize && options.minSize > file.size) {\n            messages.push(options.tooSmall.replace(/\\{file\\}/g, file.name));\n        }\n    }\n\n    function validateMimeType(mimeTypes, fileType)\n    {\n        for (var i = 0, len = mimeTypes.length; i < len; i++) {\n            if (new RegExp(mimeTypes[i]).test(fileType)) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    function validateImageSize(file, image, messages, options)\n    {\n        if (options.minWidth && image.width < options.minWidth) {\n            messages.push(options.underWidth.replace(/\\{file\\}/g, file.name));\n        }\n\n        if (options.maxWidth && image.width > options.maxWidth) {\n            messages.push(options.overWidth.replace(/\\{file\\}/g, file.name));\n        }\n\n        if (options.minHeight && image.height < options.minHeight) {\n            messages.push(options.underHeight.replace(/\\{file\\}/g, file.name));\n        }\n\n        if (options.maxHeight && image.height > options.maxHeight) {\n            messages.push(options.overHeight.replace(/\\{file\\}/g, file.name));\n        }\n    }\n\n    /**\n     * PHP: `trim($path, ' /')`, JS: `yii.helpers.trim(path, {chars: ' /'})`\n     */\n    function trimString(value, options = {skipOnEmpty: true, chars: null})\n    {\n        if (options.skipOnEmpty !== false && pub.isEmpty(value)) {\n            return value;\n        }\n\n        value = new String(value);\n        if (options.chars || !String.prototype.trim) {\n            var chars = !options.chars\n                ? ' \\\\s\\xA0'\n                : options.chars.replace(/([\\[\\]\\(\\)\\.\\?\\/\\*\\{\\}\\+\\$\\^\\:])/g, '\\$1');\n\n            return value.replace(new RegExp('^[' + chars + ']+|[' + chars + ']+$', 'g'), '');\n        }\n\n        return value.trim();\n    }\n\n    return pub;\n})(jQuery);\n"
  },
  {
    "path": "framework/base/Action.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\nuse Yii;\n\n/**\n * Action is the base class for all controller action classes.\n *\n * Action provides a way to reuse action method code. An action method in an Action\n * class can be used in multiple controllers or in different projects.\n *\n * Derived classes must implement a method named `run()`. This method\n * will be invoked by the controller when the action is requested.\n * The `run()` method can have parameters which will be filled up\n * with user input values automatically according to their names.\n * For example, if the `run()` method is declared as follows:\n *\n * ```\n * public function run($id, $type = 'book') { ... }\n * ```\n *\n * And the parameters provided for the action are: `['id' => 1]`.\n * Then the `run()` method will be invoked as `run(1)` automatically.\n *\n * For more details and usage information on Action, see the [guide article on actions](guide:structure-controllers).\n *\n * @property-read string $uniqueId The unique ID of this action among the whole application.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Controller = Controller\n */\nclass Action extends Component\n{\n    /**\n     * @var string ID of the action\n     */\n    public $id;\n    /**\n     * @var T the controller that owns this action\n     */\n    public $controller;\n\n\n    /**\n     * Constructor.\n     *\n     * @param string $id the ID of this action\n     * @param T $controller the controller that owns this action\n     * @param array<string, mixed> $config name-value pairs that will be used to initialize the object properties\n     */\n    public function __construct($id, $controller, $config = [])\n    {\n        $this->id = $id;\n        $this->controller = $controller;\n        parent::__construct($config);\n    }\n\n    /**\n     * Returns the unique ID of this action among the whole application.\n     *\n     * @return string the unique ID of this action among the whole application.\n     */\n    public function getUniqueId()\n    {\n        return $this->controller->getUniqueId() . '/' . $this->id;\n    }\n\n    /**\n     * Runs this action with the specified parameters.\n     * This method is mainly invoked by the controller.\n     *\n     * @param array $params the parameters to be bound to the action's run() method.\n     * @return mixed the result of the action\n     * @throws InvalidConfigException if the action class does not have a run() method\n     */\n    public function runWithParams($params)\n    {\n        if (!method_exists($this, 'run')) {\n            throw new InvalidConfigException(get_class($this) . ' must define a \"run()\" method.');\n        }\n        $args = $this->controller->bindActionParams($this, $params);\n        Yii::debug('Running action: ' . get_class($this) . '::run(), invoked by ' . get_class($this->controller), __METHOD__);\n        if (Yii::$app->requestedParams === null) {\n            Yii::$app->requestedParams = $args;\n        }\n        if ($this->beforeRun()) {\n            $result = call_user_func_array([$this, 'run'], $args);\n            $this->afterRun();\n\n            return $result;\n        }\n\n        return null;\n    }\n\n    /**\n     * This method is called right before `run()` is executed.\n     * You may override this method to do preparation work for the action run.\n     * If the method returns false, it will cancel the action.\n     *\n     * @return bool whether to run the action.\n     */\n    protected function beforeRun()\n    {\n        return true;\n    }\n\n    /**\n     * This method is called right after `run()` is executed.\n     * You may override this method to do post-processing work for the action run.\n     */\n    protected function afterRun()\n    {\n    }\n}\n"
  },
  {
    "path": "framework/base/ActionEvent.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * ActionEvent represents the event parameter used for an action event.\n *\n * By setting the [[isValid]] property, one may control whether to continue running the action.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Action = Action\n */\nclass ActionEvent extends Event\n{\n    /**\n     * @var T the action currently being executed\n     */\n    public $action;\n    /**\n     * @var mixed the action result. Event handlers may modify this property to change the action result.\n     */\n    public $result;\n    /**\n     * @var bool whether to continue running the action. Event handlers of\n     * [[Controller::EVENT_BEFORE_ACTION]] may set this property to decide whether\n     * to continue running the current action.\n     */\n    public $isValid = true;\n\n\n    /**\n     * Constructor.\n     * @param T $action the action associated with this action event.\n     * @param array<string, mixed> $config name-value pairs that will be used to initialize the object properties\n     */\n    public function __construct($action, $config = [])\n    {\n        $this->action = $action;\n        parent::__construct($config);\n    }\n}\n"
  },
  {
    "path": "framework/base/ActionFilter.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\nuse yii\\helpers\\StringHelper;\n\n/**\n * ActionFilter is the base class for action filters.\n *\n * An action filter will participate in the action execution workflow by responding to\n * the `beforeAction` and `afterAction` events triggered by modules and controllers.\n *\n * Check implementation of [[\\yii\\filters\\AccessControl]], [[\\yii\\filters\\PageCache]] and [[\\yii\\filters\\HttpCache]] as examples on how to use it.\n *\n * For more details and usage information on ActionFilter, see the [guide article on filters](guide:structure-filters).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Component = Component\n * @extends Behavior<T>\n */\nclass ActionFilter extends Behavior\n{\n    /**\n     * @var array list of action IDs that this filter should apply to. If this property is not set,\n     * then the filter applies to all actions, unless they are listed in [[except]].\n     * If an action ID appears in both [[only]] and [[except]], this filter will NOT apply to it.\n     *\n     * Note that if the filter is attached to a module, the action IDs should also include child module IDs (if any)\n     * and controller IDs.\n     *\n     * Since version 2.0.9 action IDs can be specified as wildcards, e.g. `site/*`.\n     *\n     * @see except\n     */\n    public $only = [];\n    /**\n     * @var array list of action IDs that this filter should not apply to.\n     * @see only\n     */\n    public $except = [];\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function attach($owner)\n    {\n        $this->owner = $owner;\n        $owner->on(Controller::EVENT_BEFORE_ACTION, [$this, 'beforeFilter']);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function detach()\n    {\n        if ($this->owner) {\n            $this->owner->off(Controller::EVENT_BEFORE_ACTION, [$this, 'beforeFilter']);\n            $this->owner->off(Controller::EVENT_AFTER_ACTION, [$this, 'afterFilter']);\n            $this->owner = null;\n        }\n    }\n\n    /**\n     * @param ActionEvent $event\n     */\n    public function beforeFilter($event)\n    {\n        if (!$this->isActive($event->action)) {\n            return;\n        }\n\n        $event->isValid = $this->beforeAction($event->action);\n        if ($event->isValid) {\n            // call afterFilter only if beforeFilter succeeds\n            // beforeFilter and afterFilter should be properly nested\n            $this->owner->on(Controller::EVENT_AFTER_ACTION, [$this, 'afterFilter'], null, false);\n        } else {\n            $event->handled = true;\n        }\n    }\n\n    /**\n     * @param ActionEvent $event\n     */\n    public function afterFilter($event)\n    {\n        $event->result = $this->afterAction($event->action, $event->result);\n        $this->owner->off(Controller::EVENT_AFTER_ACTION, [$this, 'afterFilter']);\n    }\n\n    /**\n     * This method is invoked right before an action is to be executed (after all possible filters.)\n     * You may override this method to do last-minute preparation for the action.\n     * @param Action $action the action to be executed.\n     * @return bool whether the action should continue to be executed.\n     */\n    public function beforeAction($action)\n    {\n        return true;\n    }\n\n    /**\n     * This method is invoked right after an action is executed.\n     * You may override this method to do some postprocessing for the action.\n     * @param Action $action the action just executed.\n     * @param mixed $result the action execution result\n     * @return mixed the processed action result.\n     */\n    public function afterAction($action, $result)\n    {\n        return $result;\n    }\n\n    /**\n     * Returns an action ID by converting [[Action::$uniqueId]] into an ID relative to the module.\n     * @param Action $action\n     * @return string\n     * @since 2.0.7\n     */\n    protected function getActionId($action)\n    {\n        if ($this->owner instanceof Module) {\n            $mid = $this->owner->getUniqueId();\n            $id = $action->getUniqueId();\n            if ($mid !== '' && strpos($id, $mid) === 0) {\n                $id = substr($id, strlen($mid) + 1);\n            }\n        } else {\n            $id = $action->id;\n        }\n\n        return $id;\n    }\n\n    /**\n     * Returns a value indicating whether the filter is active for the given action.\n     * @param Action $action the action being filtered\n     * @return bool whether the filter is active for the given action.\n     */\n    protected function isActive($action)\n    {\n        $id = $this->getActionId($action);\n\n        if (empty($this->only)) {\n            $onlyMatch = true;\n        } else {\n            $onlyMatch = false;\n            foreach ($this->only as $pattern) {\n                if (StringHelper::matchWildcard($pattern, $id)) {\n                    $onlyMatch = true;\n                    break;\n                }\n            }\n        }\n\n        $exceptMatch = false;\n        foreach ($this->except as $pattern) {\n            if (StringHelper::matchWildcard($pattern, $id)) {\n                $exceptMatch = true;\n                break;\n            }\n        }\n\n        return !$exceptMatch && $onlyMatch;\n    }\n}\n"
  },
  {
    "path": "framework/base/Application.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\nuse Yii;\n\n/**\n * Application is the base class for all application classes.\n *\n * For more details and usage information on Application, see the [guide article on applications](guide:structure-applications).\n *\n * @property-read \\yii\\web\\AssetManager $assetManager The asset manager application component.\n * @property-read \\yii\\rbac\\ManagerInterface|null $authManager The auth manager application component or null\n * if it's not configured.\n * @property string $basePath The root directory of the application.\n * @property-read \\yii\\caching\\CacheInterface|null $cache The cache application component. Null if the\n * component is not enabled.\n * @property-write array $container Values given in terms of name-value pairs.\n * @property-read \\yii\\db\\Connection $db The database connection.\n * @property-read \\yii\\web\\ErrorHandler|\\yii\\console\\ErrorHandler $errorHandler The error handler application\n * component.\n * @property-read \\yii\\i18n\\Formatter $formatter The formatter application component.\n * @property-read \\yii\\i18n\\I18N $i18n The internationalization application component.\n * @property-read \\yii\\log\\Dispatcher $log The log dispatcher application component.\n * @property-read \\yii\\mail\\MailerInterface $mailer The mailer application component.\n * @property-read \\yii\\web\\Request|\\yii\\console\\Request $request The request component.\n * @property-read \\yii\\web\\Response|\\yii\\console\\Response $response The response component.\n * @property string $runtimePath The directory that stores runtime files. Defaults to the \"runtime\"\n * subdirectory under [[basePath]].\n * @property-read \\yii\\base\\Security $security The security application component.\n * @property string $timeZone The time zone used by this application.\n * @property-read string $uniqueId The unique ID of the module.\n * @property-read \\yii\\web\\UrlManager $urlManager The URL manager for this application.\n * @property string $vendorPath The directory that stores vendor files. Defaults to \"vendor\" directory under\n * [[basePath]].\n * @property-read View|\\yii\\web\\View $view The view application component that is used to render various view\n * files.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nabstract class Application extends Module\n{\n    /**\n     * @event Event an event raised before the application starts to handle a request.\n     */\n    public const EVENT_BEFORE_REQUEST = 'beforeRequest';\n    /**\n     * @event Event an event raised after the application successfully handles a request (before the response is sent out).\n     */\n    public const EVENT_AFTER_REQUEST = 'afterRequest';\n    /**\n     * Application state used by [[state]]: application just started.\n     */\n    public const STATE_BEGIN = 0;\n    /**\n     * Application state used by [[state]]: application is initializing.\n     */\n    public const STATE_INIT = 1;\n    /**\n     * Application state used by [[state]]: application is triggering [[EVENT_BEFORE_REQUEST]].\n     */\n    public const STATE_BEFORE_REQUEST = 2;\n    /**\n     * Application state used by [[state]]: application is handling the request.\n     */\n    public const STATE_HANDLING_REQUEST = 3;\n    /**\n     * Application state used by [[state]]: application is triggering [[EVENT_AFTER_REQUEST]]..\n     */\n    public const STATE_AFTER_REQUEST = 4;\n    /**\n     * Application state used by [[state]]: application is about to send response.\n     */\n    public const STATE_SENDING_RESPONSE = 5;\n    /**\n     * Application state used by [[state]]: application has ended.\n     */\n    public const STATE_END = 6;\n    /**\n     * @var string the namespace that controller classes are located in.\n     * This namespace will be used to load controller classes by prepending it to the controller class name.\n     * The default namespace is `app\\controllers`.\n     *\n     * Please refer to the [guide about class autoloading](guide:concept-autoloading.md) for more details.\n     */\n    public $controllerNamespace = 'app\\\\controllers';\n    /**\n     * @var string the application name.\n     */\n    public $name = 'My Application';\n    /**\n     * @var string the charset currently used for the application.\n     */\n    public $charset = 'UTF-8';\n    /**\n     * @var string the language that is meant to be used for end users. It is recommended that you\n     * use [IETF language tags](https://en.wikipedia.org/wiki/IETF_language_tag). For example, `en` stands\n     * for English, while `en-US` stands for English (United States).\n     * @see sourceLanguage\n     */\n    public $language = 'en-US';\n    /**\n     * @var string the language that the application is written in. This mainly refers to\n     * the language that the messages and view files are written in.\n     * @see language\n     */\n    public $sourceLanguage = 'en-US';\n    /**\n     * @var Controller|null the currently active controller instance\n     */\n    public $controller;\n    /**\n     * @var string|bool the layout that should be applied for views in this application. Defaults to 'main'.\n     * If this is false, layout will be disabled.\n     */\n    public $layout = 'main';\n    /**\n     * @var string the requested route\n     */\n    public $requestedRoute;\n    /**\n     * @var Action<covariant Controller>|null the requested Action. If null, it means the request cannot be resolved into an action.\n     */\n    public $requestedAction;\n    /**\n     * @var array|null the parameters supplied to the requested action.\n     */\n    public $requestedParams;\n    /**\n     * @var array|null list of installed Yii extensions. Each array element represents a single extension\n     * with the following structure:\n     *\n     * ```\n     * [\n     *     'name' => 'extension name',\n     *     'version' => 'version number',\n     *     'bootstrap' => 'BootstrapClassName',  // optional, may also be a configuration array\n     *     'alias' => [\n     *         '@alias1' => 'to/path1',\n     *         '@alias2' => 'to/path2',\n     *     ],\n     * ]\n     * ```\n     *\n     * The \"bootstrap\" class listed above will be instantiated during the application\n     * [[bootstrap()|bootstrapping process]]. If the class implements [[BootstrapInterface]],\n     * its [[BootstrapInterface::bootstrap()|bootstrap()]] method will be also be called.\n     *\n     * If not set explicitly in the application config, this property will be populated with the contents of\n     * `@vendor/yiisoft/extensions.php`.\n     */\n    public $extensions;\n    /**\n     * @var array list of components that should be run during the application [[bootstrap()|bootstrapping process]].\n     *\n     * Each component may be specified in one of the following formats:\n     *\n     * - an application component ID as specified via [[components]].\n     * - a module ID as specified via [[modules]].\n     * - a class name.\n     * - a configuration array.\n     * - a Closure\n     *\n     * During the bootstrapping process, each component will be instantiated. If the component class\n     * implements [[BootstrapInterface]], its [[BootstrapInterface::bootstrap()|bootstrap()]] method\n     * will be also be called.\n     */\n    public $bootstrap = [];\n    /**\n     * @var int the current application state during a request handling life cycle.\n     * This property is managed by the application. Do not modify this property.\n     */\n    public $state;\n    /**\n     * @var array list of loaded modules indexed by their class names.\n     */\n    public $loadedModules = [];\n\n\n    /**\n     * Constructor.\n     * @param array<array-key, mixed> $config name-value pairs that will be used to initialize the object properties.\n     * Note that the configuration must contain both [[id]] and [[basePath]].\n     * @throws InvalidConfigException if either [[id]] or [[basePath]] configuration is missing.\n     */\n    public function __construct($config = [])\n    {\n        Yii::$app = $this;\n        static::setInstance($this);\n\n        $this->state = self::STATE_BEGIN;\n\n        $this->preInit($config);\n\n        $this->registerErrorHandler($config);\n\n        Component::__construct($config);\n    }\n\n    /**\n     * Pre-initializes the application.\n     * This method is called at the beginning of the application constructor.\n     * It initializes several important application properties.\n     * If you override this method, please make sure you call the parent implementation.\n     * @param array $config the application configuration\n     * @throws InvalidConfigException if either [[id]] or [[basePath]] configuration is missing.\n     */\n    public function preInit(&$config)\n    {\n        if (!isset($config['id'])) {\n            throw new InvalidConfigException('The \"id\" configuration for the Application is required.');\n        }\n        if (isset($config['basePath'])) {\n            $this->setBasePath($config['basePath']);\n            unset($config['basePath']);\n        } else {\n            throw new InvalidConfigException('The \"basePath\" configuration for the Application is required.');\n        }\n\n        if (isset($config['vendorPath'])) {\n            $this->setVendorPath($config['vendorPath']);\n            unset($config['vendorPath']);\n        } else {\n            // set \"@vendor\"\n            $this->getVendorPath();\n        }\n        if (isset($config['runtimePath'])) {\n            $this->setRuntimePath($config['runtimePath']);\n            unset($config['runtimePath']);\n        } else {\n            // set \"@runtime\"\n            $this->getRuntimePath();\n        }\n\n        if (isset($config['timeZone'])) {\n            $this->setTimeZone($config['timeZone']);\n            unset($config['timeZone']);\n        } elseif (!ini_get('date.timezone')) {\n            $this->setTimeZone('UTC');\n        }\n\n        if (isset($config['container'])) {\n            $this->setContainer($config['container']);\n\n            unset($config['container']);\n        }\n\n        // merge core components with custom components\n        foreach ($this->coreComponents() as $id => $component) {\n            if (!isset($config['components'][$id])) {\n                $config['components'][$id] = $component;\n            } elseif (is_array($config['components'][$id]) && !isset($config['components'][$id]['class'])) {\n                $config['components'][$id]['class'] = $component['class'];\n            }\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        $this->state = self::STATE_INIT;\n        $this->bootstrap();\n    }\n\n    /**\n     * Initializes extensions and executes bootstrap components.\n     * This method is called by [[init()]] after the application has been fully configured.\n     * If you override this method, make sure you also call the parent implementation.\n     */\n    protected function bootstrap()\n    {\n        if ($this->extensions === null) {\n            $file = Yii::getAlias('@vendor/yiisoft/extensions.php');\n            $this->extensions = is_file($file) ? include $file : [];\n        }\n        foreach ($this->extensions as $extension) {\n            if (!empty($extension['alias'])) {\n                foreach ($extension['alias'] as $name => $path) {\n                    Yii::setAlias($name, $path);\n                }\n            }\n            if (isset($extension['bootstrap'])) {\n                $component = Yii::createObject($extension['bootstrap']);\n                if ($component instanceof BootstrapInterface) {\n                    Yii::debug('Bootstrap with ' . get_class($component) . '::bootstrap()', __METHOD__);\n                    $component->bootstrap($this);\n                } else {\n                    Yii::debug('Bootstrap with ' . get_class($component), __METHOD__);\n                }\n            }\n        }\n\n        foreach ($this->bootstrap as $mixed) {\n            $component = null;\n            if ($mixed instanceof \\Closure) {\n                Yii::debug('Bootstrap with Closure', __METHOD__);\n                if (!$component = call_user_func($mixed, $this)) {\n                    continue;\n                }\n            } elseif (is_string($mixed)) {\n                if ($this->has($mixed)) {\n                    $component = $this->get($mixed);\n                } elseif ($this->hasModule($mixed)) {\n                    $component = $this->getModule($mixed);\n                } elseif (strpos($mixed, '\\\\') === false) {\n                    throw new InvalidConfigException(\"Unknown bootstrapping component ID: $mixed\");\n                }\n            }\n\n            if (!isset($component)) {\n                $component = Yii::createObject($mixed);\n            }\n\n            if ($component instanceof BootstrapInterface) {\n                Yii::debug('Bootstrap with ' . get_class($component) . '::bootstrap()', __METHOD__);\n                $component->bootstrap($this);\n            } else {\n                Yii::debug('Bootstrap with ' . get_class($component), __METHOD__);\n            }\n        }\n    }\n\n    /**\n     * Registers the errorHandler component as a PHP error handler.\n     * @param array $config application config\n     */\n    protected function registerErrorHandler(&$config)\n    {\n        if (YII_ENABLE_ERROR_HANDLER) {\n            if (!isset($config['components']['errorHandler']['class'])) {\n                echo \"Error: no errorHandler component is configured.\\n\";\n                exit(1);\n            }\n            $this->set('errorHandler', $config['components']['errorHandler']);\n            unset($config['components']['errorHandler']);\n            $this->getErrorHandler()->register();\n        }\n    }\n\n    /**\n     * Returns an ID that uniquely identifies this module among all modules within the current application.\n     * Since this is an application instance, it will always return an empty string.\n     * @return string the unique ID of the module.\n     */\n    public function getUniqueId()\n    {\n        return '';\n    }\n\n    /**\n     * Sets the root directory of the application and the @app alias.\n     * This method can only be invoked at the beginning of the constructor.\n     * @param string $path the root directory of the application.\n     * @throws InvalidArgumentException if the directory does not exist.\n     */\n    public function setBasePath($path)\n    {\n        parent::setBasePath($path);\n        Yii::setAlias('@app', $this->getBasePath());\n    }\n\n    /**\n     * Runs the application.\n     * This is the main entrance of an application.\n     * @return int the exit status (0 means normal, non-zero values mean abnormal)\n     */\n    public function run()\n    {\n        try {\n            $this->state = self::STATE_BEFORE_REQUEST;\n            $this->trigger(self::EVENT_BEFORE_REQUEST);\n\n            $this->state = self::STATE_HANDLING_REQUEST;\n            $response = $this->handleRequest($this->getRequest());\n\n            $this->state = self::STATE_AFTER_REQUEST;\n            $this->trigger(self::EVENT_AFTER_REQUEST);\n\n            $this->state = self::STATE_SENDING_RESPONSE;\n            $response->send();\n\n            $this->state = self::STATE_END;\n\n            return $response->exitStatus;\n        } catch (ExitException $e) {\n            $this->end($e->statusCode, isset($response) ? $response : null);\n            return $e->statusCode;\n        }\n    }\n\n    /**\n     * Handles the specified request.\n     *\n     * This method should return an instance of [[Response]] or its child class\n     * which represents the handling result of the request.\n     *\n     * @param Request $request the request to be handled\n     * @return Response the resulting response\n     */\n    abstract public function handleRequest($request);\n\n    private $_runtimePath;\n\n    /**\n     * Returns the directory that stores runtime files.\n     * @return string the directory that stores runtime files.\n     * Defaults to the \"runtime\" subdirectory under [[basePath]].\n     */\n    public function getRuntimePath()\n    {\n        if ($this->_runtimePath === null) {\n            $this->setRuntimePath($this->getBasePath() . DIRECTORY_SEPARATOR . 'runtime');\n        }\n\n        return $this->_runtimePath;\n    }\n\n    /**\n     * Sets the directory that stores runtime files.\n     * @param string $path the directory that stores runtime files.\n     */\n    public function setRuntimePath($path)\n    {\n        $this->_runtimePath = Yii::getAlias($path);\n        Yii::setAlias('@runtime', $this->_runtimePath);\n    }\n\n    private $_vendorPath;\n\n    /**\n     * Returns the directory that stores vendor files.\n     * @return string the directory that stores vendor files.\n     * Defaults to \"vendor\" directory under [[basePath]].\n     */\n    public function getVendorPath()\n    {\n        if ($this->_vendorPath === null) {\n            $this->setVendorPath($this->getBasePath() . DIRECTORY_SEPARATOR . 'vendor');\n        }\n\n        return $this->_vendorPath;\n    }\n\n    /**\n     * Sets the directory that stores vendor files.\n     * @param string $path the directory that stores vendor files.\n     */\n    public function setVendorPath($path)\n    {\n        $this->_vendorPath = Yii::getAlias($path);\n        Yii::setAlias('@vendor', $this->_vendorPath);\n        Yii::setAlias('@bower', $this->_vendorPath . DIRECTORY_SEPARATOR . 'bower');\n        Yii::setAlias('@npm', $this->_vendorPath . DIRECTORY_SEPARATOR . 'npm');\n    }\n\n    /**\n     * Returns the time zone used by this application.\n     * This is a simple wrapper of PHP function date_default_timezone_get().\n     * If time zone is not configured in php.ini or application config,\n     * it will be set to UTC by default.\n     * @return string the time zone used by this application.\n     * @see https://www.php.net/manual/en/function.date-default-timezone-get.php\n     */\n    public function getTimeZone()\n    {\n        return date_default_timezone_get();\n    }\n\n    /**\n     * Sets the time zone used by this application.\n     * This is a simple wrapper of PHP function date_default_timezone_set().\n     * Refer to the [php manual](https://www.php.net/manual/en/timezones.php) for available timezones.\n     * @param string $value the time zone used by this application.\n     * @see https://www.php.net/manual/en/function.date-default-timezone-set.php\n     */\n    public function setTimeZone($value)\n    {\n        date_default_timezone_set($value);\n    }\n\n    /**\n     * Returns the database connection component.\n     * @return \\yii\\db\\Connection the database connection.\n     */\n    public function getDb()\n    {\n        return $this->get('db');\n    }\n\n    /**\n     * Returns the log dispatcher component.\n     * @return \\yii\\log\\Dispatcher the log dispatcher application component.\n     */\n    public function getLog()\n    {\n        return $this->get('log');\n    }\n\n    /**\n     * Returns the error handler component.\n     * @return \\yii\\web\\ErrorHandler|\\yii\\console\\ErrorHandler the error handler application component.\n     */\n    public function getErrorHandler()\n    {\n        return $this->get('errorHandler');\n    }\n\n    /**\n     * Returns the cache component.\n     * @return \\yii\\caching\\CacheInterface|null the cache application component. Null if the component is not enabled.\n     */\n    public function getCache()\n    {\n        return $this->get('cache', false);\n    }\n\n    /**\n     * Returns the formatter component.\n     * @return \\yii\\i18n\\Formatter the formatter application component.\n     */\n    public function getFormatter()\n    {\n        return $this->get('formatter');\n    }\n\n    /**\n     * Returns the request component.\n     * @return \\yii\\web\\Request|\\yii\\console\\Request the request component.\n     */\n    public function getRequest()\n    {\n        return $this->get('request');\n    }\n\n    /**\n     * Returns the response component.\n     * @return \\yii\\web\\Response|\\yii\\console\\Response the response component.\n     */\n    public function getResponse()\n    {\n        return $this->get('response');\n    }\n\n    /**\n     * Returns the view object.\n     * @return View|\\yii\\web\\View the view application component that is used to render various view files.\n     */\n    public function getView()\n    {\n        return $this->get('view');\n    }\n\n    /**\n     * Returns the URL manager for this application.\n     * @return \\yii\\web\\UrlManager the URL manager for this application.\n     */\n    public function getUrlManager()\n    {\n        return $this->get('urlManager');\n    }\n\n    /**\n     * Returns the internationalization (i18n) component.\n     * @return \\yii\\i18n\\I18N the internationalization application component.\n     */\n    public function getI18n()\n    {\n        return $this->get('i18n');\n    }\n\n    /**\n     * Returns the mailer component.\n     * @return \\yii\\mail\\MailerInterface the mailer application component.\n     * @throws InvalidConfigException If this component is not configured.\n     */\n    public function getMailer()\n    {\n        return $this->get('mailer');\n    }\n\n    /**\n     * Returns the auth manager for this application.\n     * @return \\yii\\rbac\\ManagerInterface|null the auth manager application component or null if it's not configured.\n     */\n    public function getAuthManager()\n    {\n        return $this->get('authManager', false);\n    }\n\n    /**\n     * Returns the asset manager.\n     * @return \\yii\\web\\AssetManager the asset manager application component.\n     */\n    public function getAssetManager()\n    {\n        return $this->get('assetManager');\n    }\n\n    /**\n     * Returns the security component.\n     * @return \\yii\\base\\Security the security application component.\n     */\n    public function getSecurity()\n    {\n        return $this->get('security');\n    }\n\n    /**\n     * Returns the configuration of core application components.\n     * @return array\n     * @see set()\n     */\n    public function coreComponents()\n    {\n        $components = [\n            'log' => ['class' => 'yii\\log\\Dispatcher'],\n            'view' => ['class' => 'yii\\web\\View'],\n            'formatter' => ['class' => 'yii\\i18n\\Formatter'],\n            'i18n' => ['class' => 'yii\\i18n\\I18N'],\n            'urlManager' => ['class' => 'yii\\web\\UrlManager'],\n            'assetManager' => ['class' => 'yii\\web\\AssetManager'],\n            'security' => ['class' => 'yii\\base\\Security'],\n        ];\n        if (class_exists('yii\\swiftmailer\\Mailer')) {\n            $components['mailer'] = ['class' => 'yii\\swiftmailer\\Mailer'];\n        }\n\n        return $components;\n    }\n\n    /**\n     * Terminates the application.\n     * This method replaces the `exit()` function by ensuring the application life cycle is completed\n     * before terminating the application.\n     * @param int $status the exit status (value 0 means normal exit while other values mean abnormal exit).\n     * @param Response|null $response the response to be sent. If not set, the default application [[response]] component will be used.\n     * @throws ExitException if the application is in testing mode\n     */\n    public function end($status = 0, $response = null)\n    {\n        if ($this->state === self::STATE_BEFORE_REQUEST || $this->state === self::STATE_HANDLING_REQUEST) {\n            $this->state = self::STATE_AFTER_REQUEST;\n            $this->trigger(self::EVENT_AFTER_REQUEST);\n        }\n\n        if ($this->state !== self::STATE_SENDING_RESPONSE && $this->state !== self::STATE_END) {\n            $this->state = self::STATE_END;\n            $response = $response ?: $this->getResponse();\n            $response->send();\n        }\n\n        if (YII_ENV_TEST) {\n            throw new ExitException($status);\n        }\n\n        exit($status);\n    }\n\n    /**\n     * Configures [[Yii::$container]] with the $config.\n     *\n     * @param array $config values given in terms of name-value pairs\n     * @since 2.0.11\n     */\n    public function setContainer($config)\n    {\n        Yii::configure(Yii::$container, $config);\n    }\n}\n"
  },
  {
    "path": "framework/base/ArrayAccessTrait.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * ArrayAccessTrait provides the implementation for [[\\IteratorAggregate]], [[\\ArrayAccess]] and [[\\Countable]].\n *\n * Note that ArrayAccessTrait requires the class using it contain a property named `data` which should be an array.\n * The data will be exposed by ArrayAccessTrait to support accessing the class object like an array.\n *\n * @property array<array-key, mixed> $data\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\ntrait ArrayAccessTrait\n{\n    /**\n     * Returns an iterator for traversing the data.\n     * This method is required by the SPL interface [[\\IteratorAggregate]].\n     * It will be implicitly called when you use `foreach` to traverse the collection.\n     * @return \\ArrayIterator<array-key, mixed> an iterator for traversing the cookies in the collection.\n     */\n    #[\\ReturnTypeWillChange]\n    public function getIterator()\n    {\n        return new \\ArrayIterator($this->data);\n    }\n\n    /**\n     * Returns the number of data items.\n     * This method is required by Countable interface.\n     * @return int number of data elements.\n     */\n    #[\\ReturnTypeWillChange]\n    public function count()\n    {\n        return count($this->data);\n    }\n\n    /**\n     * This method is required by the interface [[\\ArrayAccess]].\n     * @param int|string $offset the offset to check on\n     * @return bool\n     */\n    #[\\ReturnTypeWillChange]\n    public function offsetExists($offset)\n    {\n        return isset($this->data[$offset]);\n    }\n\n    /**\n     * This method is required by the interface [[\\ArrayAccess]].\n     * @param int|string $offset the offset to retrieve element.\n     * @return mixed the element at the offset, null if no element is found at the offset\n     */\n    #[\\ReturnTypeWillChange]\n    public function offsetGet($offset)\n    {\n        return isset($this->data[$offset]) ? $this->data[$offset] : null;\n    }\n\n    /**\n     * This method is required by the interface [[\\ArrayAccess]].\n     * @param int|string $offset the offset to set element\n     * @param mixed $item the element value\n     */\n    #[\\ReturnTypeWillChange]\n    public function offsetSet($offset, $item)\n    {\n        $this->data[$offset] = $item;\n    }\n\n    /**\n     * This method is required by the interface [[\\ArrayAccess]].\n     * @param int|string $offset the offset to unset element\n     */\n    #[\\ReturnTypeWillChange]\n    public function offsetUnset($offset)\n    {\n        unset($this->data[$offset]);\n    }\n}\n"
  },
  {
    "path": "framework/base/Arrayable.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * Arrayable is the interface that should be implemented by classes who want to support customizable representation of their instances.\n *\n * For example, if a class implements Arrayable, by calling [[toArray()]], an instance of this class\n * can be turned into an array (including all its embedded objects) which can then be further transformed easily\n * into other formats, such as JSON, XML.\n *\n * The methods [[fields()]] and [[extraFields()]] allow the implementing classes to customize how and which of their data\n * should be formatted and put into the result of [[toArray()]].\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\ninterface Arrayable\n{\n    /**\n     * Returns the list of fields that should be returned by default by [[toArray()]] when no specific fields are specified.\n     *\n     * A field is a named element in the returned array by [[toArray()]].\n     *\n     * This method should return an array of field names or field definitions.\n     * If the former, the field name will be treated as an object property name whose value will be used\n     * as the field value. If the latter, the array key should be the field name while the array value should be\n     * the corresponding field definition which can be either an object property name or a PHP callable\n     * returning the corresponding field value. The signature of the callable should be:\n     *\n     * ```\n     * function ($model, $field) {\n     *     // return field value\n     * }\n     * ```\n     *\n     * For example, the following code declares four fields:\n     *\n     * - `email`: the field name is the same as the property name `email`;\n     * - `firstName` and `lastName`: the field names are `firstName` and `lastName`, and their\n     *   values are obtained from the `first_name` and `last_name` properties;\n     * - `fullName`: the field name is `fullName`. Its value is obtained by concatenating `first_name`\n     *   and `last_name`.\n     *\n     * ```\n     * return [\n     *     'email',\n     *     'firstName' => 'first_name',\n     *     'lastName' => 'last_name',\n     *     'fullName' => function ($model) {\n     *         return $model->first_name . ' ' . $model->last_name;\n     *     },\n     * ];\n     * ```\n     *\n     * @return array the list of field names or field definitions.\n     * @see toArray()\n     */\n    public function fields();\n\n    /**\n     * Returns the list of additional fields that can be returned by [[toArray()]] in addition to those listed in [[fields()]].\n     *\n     * This method is similar to [[fields()]] except that the list of fields declared\n     * by this method are not returned by default by [[toArray()]]. Only when a field in the list\n     * is explicitly requested, will it be included in the result of [[toArray()]].\n     *\n     * @return array the list of expandable field names or field definitions. Please refer\n     * to [[fields()]] on the format of the return value.\n     * @see toArray()\n     * @see fields()\n     */\n    public function extraFields();\n\n    /**\n     * Converts the object into an array.\n     *\n     * @param array $fields the fields that the output array should contain. Fields not specified\n     * in [[fields()]] will be ignored. If this parameter is empty, all fields as specified in [[fields()]] will be returned.\n     * @param array $expand the additional fields that the output array should contain.\n     * Fields not specified in [[extraFields()]] will be ignored. If this parameter is empty, no extra fields\n     * will be returned.\n     * @param bool $recursive whether to recursively return array representation of embedded objects.\n     * @return array the array representation of the object\n     */\n    public function toArray(array $fields = [], array $expand = [], $recursive = true);\n}\n"
  },
  {
    "path": "framework/base/ArrayableTrait.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\nuse Yii;\nuse yii\\helpers\\ArrayHelper;\nuse yii\\web\\Link;\nuse yii\\web\\Linkable;\n\n/**\n * ArrayableTrait provides a common implementation of the [[Arrayable]] interface.\n *\n * ArrayableTrait implements [[toArray()]] by respecting the field definitions as declared\n * in [[fields()]] and [[extraFields()]].\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\ntrait ArrayableTrait\n{\n    /**\n     * Returns the list of fields that should be returned by default by [[toArray()]] when no specific fields are specified.\n     *\n     * A field is a named element in the returned array by [[toArray()]].\n     *\n     * This method should return an array of field names or field definitions.\n     * If the former, the field name will be treated as an object property name whose value will be used\n     * as the field value. If the latter, the array key should be the field name while the array value should be\n     * the corresponding field definition which can be either an object property name or a PHP callable\n     * returning the corresponding field value. The signature of the callable should be:\n     *\n     * ```\n     * function ($model, $field) {\n     *     // return field value\n     * }\n     * ```\n     *\n     * For example, the following code declares four fields:\n     *\n     * - `email`: the field name is the same as the property name `email`;\n     * - `firstName` and `lastName`: the field names are `firstName` and `lastName`, and their\n     *   values are obtained from the `first_name` and `last_name` properties;\n     * - `fullName`: the field name is `fullName`. Its value is obtained by concatenating `first_name`\n     *   and `last_name`.\n     *\n     * ```\n     * return [\n     *     'email',\n     *     'firstName' => 'first_name',\n     *     'lastName' => 'last_name',\n     *     'fullName' => function () {\n     *         return $this->first_name . ' ' . $this->last_name;\n     *     },\n     * ];\n     * ```\n     *\n     * In this method, you may also want to return different lists of fields based on some context\n     * information. For example, depending on the privilege of the current application user,\n     * you may return different sets of visible fields or filter out some fields.\n     *\n     * The default implementation of this method returns the public object member variables indexed by themselves.\n     *\n     * @return array the list of field names or field definitions.\n     * @see toArray()\n     */\n    public function fields()\n    {\n        $fields = array_keys(Yii::getObjectVars($this));\n        return array_combine($fields, $fields);\n    }\n\n    /**\n     * Returns the list of fields that can be expanded further and returned by [[toArray()]].\n     *\n     * This method is similar to [[fields()]] except that the list of fields returned\n     * by this method are not returned by default by [[toArray()]]. Only when field names\n     * to be expanded are explicitly specified when calling [[toArray()]], will their values\n     * be exported.\n     *\n     * The default implementation returns an empty array.\n     *\n     * You may override this method to return a list of expandable fields based on some context information\n     * (e.g. the current application user).\n     *\n     * @return array the list of expandable field names or field definitions. Please refer\n     * to [[fields()]] on the format of the return value.\n     * @see toArray()\n     * @see fields()\n     */\n    public function extraFields()\n    {\n        return [];\n    }\n\n    /**\n     * Converts the model into an array.\n     *\n     * This method will first identify which fields to be included in the resulting array by calling [[resolveFields()]].\n     * It will then turn the model into an array with these fields. If `$recursive` is true,\n     * any embedded objects will also be converted into arrays.\n     * When embedded objects are [[Arrayable]], their respective nested fields will be extracted and passed to [[toArray()]].\n     *\n     * If the model implements the [[Linkable]] interface, the resulting array will also have a `_link` element\n     * which refers to a list of links as specified by the interface.\n     *\n     * @param array $fields the fields being requested.\n     * If empty or if it contains '*', all fields as specified by [[fields()]] will be returned.\n     * Fields can be nested, separated with dots (.). e.g.: item.field.sub-field\n     * `$recursive` must be true for nested fields to be extracted. If `$recursive` is false, only the root fields will be extracted.\n     * @param array $expand the additional fields being requested for exporting. Only fields declared in [[extraFields()]]\n     * will be considered.\n     * Expand can also be nested, separated with dots (.). e.g.: item.expand1.expand2\n     * `$recursive` must be true for nested expands to be extracted. If `$recursive` is false, only the root expands will be extracted.\n     * @param bool $recursive whether to recursively return array representation of embedded objects.\n     * @return array the array representation of the object\n     */\n    public function toArray(array $fields = [], array $expand = [], $recursive = true)\n    {\n        $data = [];\n        foreach ($this->resolveFields($fields, $expand) as $field => $definition) {\n            $attribute = is_string($definition) ? ($this->$definition ?? null) : $definition($this, $field);\n\n            if ($recursive) {\n                $nestedFields = $this->extractFieldsFor($fields, $field);\n                $nestedExpand = $this->extractFieldsFor($expand, $field);\n                if ($attribute instanceof Arrayable) {\n                    $attribute = $attribute->toArray($nestedFields, $nestedExpand);\n                } elseif ($attribute instanceof \\JsonSerializable) {\n                    $attribute = $attribute->jsonSerialize();\n                } elseif (is_array($attribute)) {\n                    $attribute = array_map(\n                        function ($item) use ($nestedFields, $nestedExpand) {\n                            if ($item instanceof Arrayable) {\n                                return $item->toArray($nestedFields, $nestedExpand);\n                            } elseif ($item instanceof \\JsonSerializable) {\n                                return $item->jsonSerialize();\n                            }\n                            return $item;\n                        },\n                        $attribute\n                    );\n                }\n            }\n            $data[$field] = $attribute;\n        }\n\n        if ($this instanceof Linkable) {\n            $data['_links'] = Link::serialize($this->getLinks());\n        }\n\n        return $recursive ? ArrayHelper::toArray($data) : $data;\n    }\n\n    /**\n     * Extracts the root field names from nested fields.\n     * Nested fields are separated with dots (.). e.g: \"item.id\"\n     * The previous example would extract \"item\".\n     *\n     * @param array $fields The fields requested for extraction\n     * @return array root fields extracted from the given nested fields\n     * @since 2.0.14\n     */\n    protected function extractRootFields(array $fields)\n    {\n        $result = [];\n\n        foreach ($fields as $field) {\n            $result[] = current(explode('.', $field, 2));\n        }\n\n        if (in_array('*', $result, true)) {\n            $result = [];\n        }\n\n        return array_unique($result);\n    }\n\n    /**\n     * Extract nested fields from a fields collection for a given root field\n     * Nested fields are separated with dots (.). e.g: \"item.id\"\n     * The previous example would extract \"id\".\n     *\n     * @param array $fields The fields requested for extraction\n     * @param string $rootField The root field for which we want to extract the nested fields\n     * @return array nested fields extracted for the given field\n     * @since 2.0.14\n     */\n    protected function extractFieldsFor(array $fields, $rootField)\n    {\n        $result = [];\n\n        foreach ($fields as $field) {\n            if (0 === strpos($field, \"{$rootField}.\")) {\n                $result[] = preg_replace('/^' . preg_quote($rootField, '/') . '\\./i', '', $field);\n            }\n        }\n\n        return array_unique($result);\n    }\n\n    /**\n     * Determines which fields can be returned by [[toArray()]].\n     * This method will first extract the root fields from the given fields.\n     * Then it will check the requested root fields against those declared in [[fields()]] and [[extraFields()]]\n     * to determine which fields can be returned.\n     * @param array $fields the fields being requested for exporting\n     * @param array $expand the additional fields being requested for exporting\n     * @return array the list of fields to be exported. The array keys are the field names, and the array values\n     * are the corresponding object property names or PHP callables returning the field values.\n     */\n    protected function resolveFields(array $fields, array $expand)\n    {\n        $fields = $this->extractRootFields($fields);\n        $expand = $this->extractRootFields($expand);\n        $result = [];\n\n        foreach ($this->fields() as $field => $definition) {\n            if (is_int($field)) {\n                $field = $definition;\n            }\n            if (empty($fields) || in_array($field, $fields, true)) {\n                $result[$field] = $definition;\n            }\n        }\n\n        if (empty($expand)) {\n            return $result;\n        }\n\n        foreach ($this->extraFields() as $field => $definition) {\n            if (is_int($field)) {\n                $field = $definition;\n            }\n            if (in_array($field, $expand, true)) {\n                $result[$field] = $definition;\n            }\n        }\n\n        return $result;\n    }\n}\n"
  },
  {
    "path": "framework/base/BaseObject.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\nuse Yii;\n\n/**\n * BaseObject is the base class that implements the *property* feature.\n *\n * A property is defined by a getter method (e.g. `getLabel`), and/or a setter method (e.g. `setLabel`). For example,\n * the following getter and setter methods define a property named `label`:\n *\n * ```\n * private $_label;\n *\n * public function getLabel()\n * {\n *     return $this->_label;\n * }\n *\n * public function setLabel($value)\n * {\n *     $this->_label = $value;\n * }\n * ```\n *\n * Property names are *case-insensitive*.\n *\n * A property can be accessed like a member variable of an object. Reading or writing a property will cause the invocation\n * of the corresponding getter or setter method. For example,\n *\n * ```\n * // equivalent to $label = $object->getLabel();\n * $label = $object->label;\n * // equivalent to $object->setLabel('abc');\n * $object->label = 'abc';\n * ```\n *\n * If a property has only a getter method and has no setter method, it is considered as *read-only*. In this case, trying\n * to modify the property value will cause an exception.\n *\n * One can call [[hasProperty()]], [[canGetProperty()]] and/or [[canSetProperty()]] to check the existence of a property.\n *\n * Besides the property feature, BaseObject also introduces an important object initialization life cycle. In particular,\n * creating an new instance of BaseObject or its derived class will involve the following life cycles sequentially:\n *\n * 1. the class constructor is invoked;\n * 2. object properties are initialized according to the given configuration;\n * 3. the `init()` method is invoked.\n *\n * In the above, both Step 2 and 3 occur at the end of the class constructor. It is recommended that\n * you perform object initialization in the `init()` method because at that stage, the object configuration\n * is already applied.\n *\n * In order to ensure the above life cycles, if a child class of BaseObject needs to override the constructor,\n * it should be done like the following:\n *\n * ```\n * public function __construct($param1, $param2, ..., $config = [])\n * {\n *     ...\n *     parent::__construct($config);\n * }\n * ```\n *\n * That is, a `$config` parameter (defaults to `[]`) should be declared as the last parameter\n * of the constructor, and the parent implementation should be called at the end of the constructor.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0.13\n */\nclass BaseObject implements Configurable\n{\n    /**\n     * Returns the fully qualified name of this class.\n     * @return string the fully qualified name of this class.\n     * @deprecated since 2.0.14. On PHP >=5.5, use `::class` instead.\n     */\n    public static function className()\n    {\n        return get_called_class();\n    }\n\n    /**\n     * Constructor.\n     *\n     * The default implementation does two things:\n     *\n     * - Initializes the object with the given configuration `$config`.\n     * - Call [[init()]].\n     *\n     * If this method is overridden in a child class, it is recommended that\n     *\n     * - the last parameter of the constructor is a configuration array, like `$config` here.\n     * - call the parent implementation at the end of the constructor.\n     *\n     * @param array<string, mixed> $config name-value pairs that will be used to initialize the object properties\n     */\n    public function __construct($config = [])\n    {\n        if (!empty($config)) {\n            Yii::configure($this, $config);\n        }\n        $this->init();\n    }\n\n    /**\n     * Initializes the object.\n     * This method is invoked at the end of the constructor after the object is initialized with the\n     * given configuration.\n     */\n    public function init()\n    {\n    }\n\n    /**\n     * Returns the value of an object property.\n     *\n     * Do not call this method directly as it is a PHP magic method that\n     * will be implicitly called when executing `$value = $object->property;`.\n     * @param string $name the property name\n     * @return mixed the property value\n     * @throws UnknownPropertyException if the property is not defined\n     * @throws InvalidCallException if the property is write-only\n     * @see __set()\n     */\n    public function __get($name)\n    {\n        $getter = 'get' . $name;\n        if (method_exists($this, $getter)) {\n            return $this->$getter();\n        } elseif (method_exists($this, 'set' . $name)) {\n            throw new InvalidCallException('Getting write-only property: ' . get_class($this) . '::' . $name);\n        }\n\n        throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '::' . $name);\n    }\n\n    /**\n     * Sets value of an object property.\n     *\n     * Do not call this method directly as it is a PHP magic method that\n     * will be implicitly called when executing `$object->property = $value;`.\n     * @param string $name the property name or the event name\n     * @param mixed $value the property value\n     * @throws UnknownPropertyException if the property is not defined\n     * @throws InvalidCallException if the property is read-only\n     * @see __get()\n     */\n    public function __set($name, $value)\n    {\n        $setter = 'set' . $name;\n        if (method_exists($this, $setter)) {\n            $this->$setter($value);\n        } elseif (method_exists($this, 'get' . $name)) {\n            throw new InvalidCallException('Setting read-only property: ' . get_class($this) . '::' . $name);\n        } else {\n            throw new UnknownPropertyException('Setting unknown property: ' . get_class($this) . '::' . $name);\n        }\n    }\n\n    /**\n     * Checks if a property is set, i.e. defined and not null.\n     *\n     * Do not call this method directly as it is a PHP magic method that\n     * will be implicitly called when executing `isset($object->property)`.\n     *\n     * Note that if the property is not defined, false will be returned.\n     * @param string $name the property name or the event name\n     * @return bool whether the named property is set (not null).\n     * @see https://www.php.net/manual/en/function.isset.php\n     */\n    public function __isset($name)\n    {\n        $getter = 'get' . $name;\n        if (method_exists($this, $getter)) {\n            return $this->$getter() !== null;\n        }\n\n        return false;\n    }\n\n    /**\n     * Sets an object property to null.\n     *\n     * Do not call this method directly as it is a PHP magic method that\n     * will be implicitly called when executing `unset($object->property)`.\n     *\n     * Note that if the property is not defined, this method will do nothing.\n     * If the property is read-only, it will throw an exception.\n     * @param string $name the property name\n     * @throws InvalidCallException if the property is read only.\n     * @see https://www.php.net/manual/en/function.unset.php\n     */\n    public function __unset($name)\n    {\n        $setter = 'set' . $name;\n        if (method_exists($this, $setter)) {\n            $this->$setter(null);\n        } elseif (method_exists($this, 'get' . $name)) {\n            throw new InvalidCallException('Unsetting read-only property: ' . get_class($this) . '::' . $name);\n        }\n    }\n\n    /**\n     * Calls the named method which is not a class method.\n     *\n     * Do not call this method directly as it is a PHP magic method that\n     * will be implicitly called when an unknown method is being invoked.\n     * @param string $name the method name\n     * @param array $params method parameters\n     * @throws UnknownMethodException when calling unknown method\n     * @return mixed the method return value\n     */\n    public function __call($name, $params)\n    {\n        throw new UnknownMethodException('Calling unknown method: ' . get_class($this) . \"::$name()\");\n    }\n\n    /**\n     * Returns a value indicating whether a property is defined.\n     *\n     * A property is defined if:\n     *\n     * - the class has a getter or setter method associated with the specified name\n     *   (in this case, property name is case-insensitive);\n     * - the class has a member variable with the specified name (when `$checkVars` is true);\n     *\n     * @param string $name the property name\n     * @param bool $checkVars whether to treat member variables as properties\n     * @return bool whether the property is defined\n     * @see canGetProperty()\n     * @see canSetProperty()\n     */\n    public function hasProperty($name, $checkVars = true)\n    {\n        return $this->canGetProperty($name, $checkVars) || $this->canSetProperty($name, false);\n    }\n\n    /**\n     * Returns a value indicating whether a property can be read.\n     *\n     * A property is readable if:\n     *\n     * - the class has a getter method associated with the specified name\n     *   (in this case, property name is case-insensitive);\n     * - the class has a member variable with the specified name (when `$checkVars` is true);\n     *\n     * @param string $name the property name\n     * @param bool $checkVars whether to treat member variables as properties\n     * @return bool whether the property can be read\n     * @see canSetProperty()\n     */\n    public function canGetProperty($name, $checkVars = true)\n    {\n        return method_exists($this, 'get' . $name) || $checkVars && property_exists($this, $name);\n    }\n\n    /**\n     * Returns a value indicating whether a property can be set.\n     *\n     * A property is writable if:\n     *\n     * - the class has a setter method associated with the specified name\n     *   (in this case, property name is case-insensitive);\n     * - the class has a member variable with the specified name (when `$checkVars` is true);\n     *\n     * @param string $name the property name\n     * @param bool $checkVars whether to treat member variables as properties\n     * @return bool whether the property can be written\n     * @see canGetProperty()\n     */\n    public function canSetProperty($name, $checkVars = true)\n    {\n        return method_exists($this, 'set' . $name) || $checkVars && property_exists($this, $name);\n    }\n\n    /**\n     * Returns a value indicating whether a method is defined.\n     *\n     * The default implementation is a call to php function `method_exists()`.\n     * You may override this method when you implemented the php magic method `__call()`.\n     * @param string $name the method name\n     * @return bool whether the method is defined\n     */\n    public function hasMethod($name)\n    {\n        return method_exists($this, $name);\n    }\n}\n"
  },
  {
    "path": "framework/base/Behavior.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * Behavior is the base class for all behavior classes.\n *\n * A behavior can be used to enhance the functionality of an existing component without modifying its code.\n * In particular, it can \"inject\" its own methods and properties into the component\n * and make them directly accessible via the component. It can also respond to the events triggered in the component\n * and thus intercept the normal code execution.\n *\n * For more details and usage information on Behavior, see the [guide article on behaviors](guide:concept-behaviors).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Component = Component\n */\nclass Behavior extends BaseObject\n{\n    /**\n     * @var T|null the owner of this behavior\n     */\n    public $owner;\n\n    /**\n     * @var array Attached events handlers\n     */\n    private $_attachedEvents = [];\n\n\n    /**\n     * Declares event handlers for the [[owner]]'s events.\n     *\n     * Child classes may override this method to declare what PHP callbacks should\n     * be attached to the events of the [[owner]] component.\n     *\n     * The callbacks will be attached to the [[owner]]'s events when the behavior is\n     * attached to the owner; and they will be detached from the events when\n     * the behavior is detached from the component.\n     *\n     * The callbacks can be any of the following:\n     *\n     * - method in this behavior: `'handleClick'`, equivalent to `[$this, 'handleClick']`\n     * - object method: `[$object, 'handleClick']`\n     * - static method: `['Page', 'handleClick']`\n     * - anonymous function: `function ($event) { ... }`\n     *\n     * The following is an example:\n     *\n     * ```\n     * [\n     *     Model::EVENT_BEFORE_VALIDATE => 'myBeforeValidate',\n     *     Model::EVENT_AFTER_VALIDATE => 'myAfterValidate',\n     * ]\n     * ```\n     *\n     * @return array events (array keys) and the corresponding event handler methods (array values).\n     */\n    public function events()\n    {\n        return [];\n    }\n\n    /**\n     * Attaches the behavior object to the component.\n     * The default implementation will set the [[owner]] property\n     * and attach event handlers as declared in [[events]].\n     * Make sure you call the parent implementation if you override this method.\n     * @param T $owner the component that this behavior is to be attached to.\n     */\n    public function attach($owner)\n    {\n        $this->owner = $owner;\n        foreach ($this->events() as $event => $handler) {\n            $this->_attachedEvents[$event] = $handler;\n            $owner->on($event, is_string($handler) ? [$this, $handler] : $handler);\n        }\n    }\n\n    /**\n     * Detaches the behavior object from the component.\n     * The default implementation will unset the [[owner]] property\n     * and detach event handlers declared in [[events]].\n     * Make sure you call the parent implementation if you override this method.\n     */\n    public function detach()\n    {\n        if ($this->owner) {\n            foreach ($this->_attachedEvents as $event => $handler) {\n                $this->owner->off($event, is_string($handler) ? [$this, $handler] : $handler);\n            }\n            $this->_attachedEvents = [];\n            $this->owner = null;\n        }\n    }\n}\n"
  },
  {
    "path": "framework/base/BootstrapInterface.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * BootstrapInterface is the interface that should be implemented by classes who want to participate in the application bootstrap process.\n *\n * The main method [[bootstrap()]] will be invoked by an application at the beginning of its `init()` method.\n *\n * Bootstrapping classes can be registered in two approaches.\n *\n * The first approach is mainly used by extensions and is managed by the Composer installation process.\n * You mainly need to list the bootstrapping class of your extension in the `composer.json` file like following,\n *\n * ```\n * {\n *     // ...\n *     \"extra\": {\n *         \"bootstrap\": \"path\\\\to\\\\MyBootstrapClass\"\n *     }\n * }\n * ```\n *\n * If the extension is installed, the bootstrap information will be saved in [[Application::extensions]].\n *\n * The second approach is used by application code which needs to register some code to be run during\n * the bootstrap process. This is done by configuring the [[Application::bootstrap]] property:\n *\n * ```\n * return [\n *     // ...\n *     'bootstrap' => [\n *         \"path\\\\to\\\\MyBootstrapClass1\",\n *         [\n *             'class' => \"path\\\\to\\\\MyBootstrapClass2\",\n *             'prop1' => 'value1',\n *             'prop2' => 'value2',\n *         ],\n *     ],\n * ];\n * ```\n *\n * As you can see, you can register a bootstrapping class in terms of either a class name or a configuration class.\n *\n * For more details and usage information on BootstrapInterface, see the [guide article on bootstrapping applications](guide:structure-applications).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\ninterface BootstrapInterface\n{\n    /**\n     * Bootstrap method to be called during application bootstrap stage.\n     * @param Application $app the application currently running\n     */\n    public function bootstrap($app);\n}\n"
  },
  {
    "path": "framework/base/Component.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\nuse Yii;\nuse yii\\helpers\\StringHelper;\n\n/**\n * Component is the base class that implements the *property*, *event* and *behavior* features.\n *\n * Component provides the *event* and *behavior* features, in addition to the *property* feature which is implemented in\n * its parent class [[\\yii\\base\\BaseObject|BaseObject]].\n *\n * Event is a way to \"inject\" custom code into existing code at certain places. For example, a comment object can trigger\n * an \"add\" event when the user adds a comment. We can write custom code and attach it to this event so that when the event\n * is triggered (i.e. comment will be added), our custom code will be executed.\n *\n * An event is identified by a name that should be unique within the class it is defined at. Event names are *case-sensitive*.\n *\n * One or multiple PHP callbacks, called *event handlers*, can be attached to an event. You can call [[trigger()]] to\n * raise an event. When an event is raised, the event handlers will be invoked automatically in the order they were\n * attached.\n *\n * To attach an event handler to an event, call [[on()]]:\n *\n * ```\n * $post->on('update', function ($event) {\n *     // send email notification\n * });\n * ```\n *\n * In the above, an anonymous function is attached to the \"update\" event of the post. You may attach\n * the following types of event handlers:\n *\n * - anonymous function: `function ($event) { ... }`\n * - object method: `[$object, 'handleAdd']`\n * - static class method: `['Page', 'handleAdd']`\n * - global function: `'handleAdd'`\n *\n * The signature of an event handler should be like the following:\n *\n * ```\n * function foo($event)\n * ```\n *\n * where `$event` is an [[Event]] object which includes parameters associated with the event.\n *\n * You can also attach a handler to an event when configuring a component with a configuration array.\n * The syntax is like the following:\n *\n * ```\n * [\n *     'on add' => function ($event) { ... }\n * ]\n * ```\n *\n * where `on add` stands for attaching an event to the `add` event.\n *\n * Sometimes, you may want to associate extra data with an event handler when you attach it to an event\n * and then access it when the handler is invoked. You may do so by\n *\n * ```\n * $post->on('update', function ($event) {\n *     // the data can be accessed via $event->data\n * }, $data);\n * ```\n *\n * A behavior is an instance of [[Behavior]] or its child class. A component can be attached with one or multiple\n * behaviors. When a behavior is attached to a component, its public properties and methods can be accessed via the\n * component directly, as if the component owns those properties and methods.\n *\n * To attach a behavior to a component, declare it in [[behaviors()]], or explicitly call [[attachBehavior]]. Behaviors\n * declared in [[behaviors()]] are automatically attached to the corresponding component.\n *\n * One can also attach a behavior to a component when configuring it with a configuration array. The syntax is like the\n * following:\n *\n * ```\n * [\n *     'as tree' => [\n *         'class' => 'Tree',\n *     ],\n * ]\n * ```\n *\n * where `as tree` stands for attaching a behavior named `tree`, and the array will be passed to [[\\Yii::createObject()]]\n * to create the behavior object.\n *\n * For more details and usage information on Component, see the [guide article on components](guide:concept-components).\n *\n * @property-read Behavior<static>[] $behaviors List of behaviors attached to this component.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @phpstan-property-read Behavior<static>[] $behaviors\n * @psalm-property-read Behavior<self>[] $behaviors\n */\nclass Component extends BaseObject\n{\n    /**\n     * @var array the attached event handlers (event name => handlers)\n     */\n    private $_events = [];\n    /**\n     * @var array the event handlers attached for wildcard patterns (event name wildcard => handlers)\n     * @since 2.0.14\n     */\n    private $_eventWildcards = [];\n    /**\n     * @var Behavior<static>[]|null the attached behaviors (behavior name => behavior). This is `null` when not initialized.\n     */\n    private $_behaviors;\n\n\n    /**\n     * Returns the value of a component property.\n     *\n     * This method will check in the following order and act accordingly:\n     *\n     *  - a property defined by a getter: return the getter result\n     *  - a property of a behavior: return the behavior property value\n     *\n     * Do not call this method directly as it is a PHP magic method that\n     * will be implicitly called when executing `$value = $component->property;`.\n     * @param string $name the property name\n     * @return mixed the property value or the value of a behavior's property\n     * @throws UnknownPropertyException if the property is not defined\n     * @throws InvalidCallException if the property is write-only.\n     * @see __set()\n     */\n    public function __get($name)\n    {\n        $getter = 'get' . $name;\n        if (method_exists($this, $getter)) {\n            // read property, e.g. getName()\n            return $this->$getter();\n        }\n\n        // behavior property\n        $this->ensureBehaviors();\n        foreach ($this->_behaviors as $behavior) {\n            if ($behavior->canGetProperty($name)) {\n                return $behavior->$name;\n            }\n        }\n\n        if (method_exists($this, 'set' . $name)) {\n            throw new InvalidCallException('Getting write-only property: ' . get_class($this) . '::' . $name);\n        }\n\n        throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '::' . $name);\n    }\n\n    /**\n     * Sets the value of a component property.\n     *\n     * This method will check in the following order and act accordingly:\n     *\n     *  - a property defined by a setter: set the property value\n     *  - an event in the format of \"on xyz\": attach the handler to the event \"xyz\"\n     *  - a behavior in the format of \"as xyz\": attach the behavior named as \"xyz\"\n     *  - a property of a behavior: set the behavior property value\n     *\n     * Do not call this method directly as it is a PHP magic method that\n     * will be implicitly called when executing `$component->property = $value;`.\n     * @param string $name the property name or the event name\n     * @param mixed $value the property value\n     * @throws UnknownPropertyException if the property is not defined\n     * @throws InvalidCallException if the property is read-only.\n     * @see __get()\n     */\n    public function __set($name, $value)\n    {\n        $setter = 'set' . $name;\n        if (method_exists($this, $setter)) {\n            // set property\n            $this->$setter($value);\n\n            return;\n        } elseif (strncmp($name, 'on ', 3) === 0) {\n            // on event: attach event handler\n            $this->on(trim(substr($name, 3)), $value);\n\n            return;\n        } elseif (strncmp($name, 'as ', 3) === 0) {\n            // as behavior: attach behavior\n            $name = trim(substr($name, 3));\n            if ($value instanceof Behavior) {\n                $this->attachBehavior($name, $value);\n            } elseif ($value instanceof \\Closure) {\n                $this->attachBehavior($name, call_user_func($value));\n            } elseif (isset($value['__class']) && is_subclass_of($value['__class'], Behavior::class)) {\n                $this->attachBehavior($name, Yii::createObject($value));\n            } elseif (!isset($value['__class']) && isset($value['class']) && is_subclass_of($value['class'], Behavior::class)) {\n                $this->attachBehavior($name, Yii::createObject($value));\n            } elseif (is_string($value) && is_subclass_of($value, Behavior::class, true)) {\n                $this->attachBehavior($name, Yii::createObject($value));\n            } else {\n                throw new InvalidConfigException('Class is not of type ' . Behavior::class . ' or its subclasses');\n            }\n\n            return;\n        }\n\n        // behavior property\n        $this->ensureBehaviors();\n        foreach ($this->_behaviors as $behavior) {\n            if ($behavior->canSetProperty($name)) {\n                $behavior->$name = $value;\n                return;\n            }\n        }\n\n        if (method_exists($this, 'get' . $name)) {\n            throw new InvalidCallException('Setting read-only property: ' . get_class($this) . '::' . $name);\n        }\n\n        throw new UnknownPropertyException('Setting unknown property: ' . get_class($this) . '::' . $name);\n    }\n\n    /**\n     * Checks if a property is set, i.e. defined and not null.\n     *\n     * This method will check in the following order and act accordingly:\n     *\n     *  - a property defined by a setter: return whether the property is set\n     *  - a property of a behavior: return whether the property is set\n     *  - return `false` for non existing properties\n     *\n     * Do not call this method directly as it is a PHP magic method that\n     * will be implicitly called when executing `isset($component->property)`.\n     * @param string $name the property name or the event name\n     * @return bool whether the named property is set\n     * @see https://www.php.net/manual/en/function.isset.php\n     */\n    public function __isset($name)\n    {\n        $getter = 'get' . $name;\n        if (method_exists($this, $getter)) {\n            return $this->$getter() !== null;\n        }\n\n        // behavior property\n        $this->ensureBehaviors();\n        foreach ($this->_behaviors as $behavior) {\n            if ($behavior->canGetProperty($name)) {\n                return $behavior->$name !== null;\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * Sets a component property to be null.\n     *\n     * This method will check in the following order and act accordingly:\n     *\n     *  - a property defined by a setter: set the property value to be null\n     *  - a property of a behavior: set the property value to be null\n     *\n     * Do not call this method directly as it is a PHP magic method that\n     * will be implicitly called when executing `unset($component->property)`.\n     * @param string $name the property name\n     * @throws InvalidCallException if the property is read only.\n     * @see https://www.php.net/manual/en/function.unset.php\n     */\n    public function __unset($name)\n    {\n        $setter = 'set' . $name;\n        if (method_exists($this, $setter)) {\n            $this->$setter(null);\n            return;\n        }\n\n        // behavior property\n        $this->ensureBehaviors();\n        foreach ($this->_behaviors as $behavior) {\n            if ($behavior->canSetProperty($name)) {\n                $behavior->$name = null;\n                return;\n            }\n        }\n\n        throw new InvalidCallException('Unsetting an unknown or read-only property: ' . get_class($this) . '::' . $name);\n    }\n\n    /**\n     * Calls the named method which is not a class method.\n     *\n     * This method will check if any attached behavior has\n     * the named method and will execute it if available.\n     *\n     * Do not call this method directly as it is a PHP magic method that\n     * will be implicitly called when an unknown method is being invoked.\n     * @param string $name the method name\n     * @param array $params method parameters\n     * @return mixed the method return value\n     * @throws UnknownMethodException when calling unknown method\n     */\n    public function __call($name, $params)\n    {\n        $this->ensureBehaviors();\n        foreach ($this->_behaviors as $object) {\n            if ($object->hasMethod($name)) {\n                return call_user_func_array([$object, $name], $params);\n            }\n        }\n        throw new UnknownMethodException('Calling unknown method: ' . get_class($this) . \"::$name()\");\n    }\n\n    /**\n     * This method is called after the object is created by cloning an existing one.\n     * It removes all behaviors because they are attached to the old object.\n     */\n    public function __clone()\n    {\n        $this->_events = [];\n        $this->_eventWildcards = [];\n        $this->_behaviors = null;\n    }\n\n    /**\n     * Returns a value indicating whether a property is defined for this component.\n     *\n     * A property is defined if:\n     *\n     * - the class has a getter or setter method associated with the specified name\n     *   (in this case, property name is case-insensitive);\n     * - the class has a member variable with the specified name (when `$checkVars` is true);\n     * - an attached behavior has a property of the given name (when `$checkBehaviors` is true).\n     *\n     * @param string $name the property name\n     * @param bool $checkVars whether to treat member variables as properties\n     * @param bool $checkBehaviors whether to treat behaviors' properties as properties of this component\n     * @return bool whether the property is defined\n     * @see canGetProperty()\n     * @see canSetProperty()\n     */\n    public function hasProperty($name, $checkVars = true, $checkBehaviors = true)\n    {\n        return $this->canGetProperty($name, $checkVars, $checkBehaviors) || $this->canSetProperty($name, false, $checkBehaviors);\n    }\n\n    /**\n     * Returns a value indicating whether a property can be read.\n     *\n     * A property can be read if:\n     *\n     * - the class has a getter method associated with the specified name\n     *   (in this case, property name is case-insensitive);\n     * - the class has a member variable with the specified name (when `$checkVars` is true);\n     * - an attached behavior has a readable property of the given name (when `$checkBehaviors` is true).\n     *\n     * @param string $name the property name\n     * @param bool $checkVars whether to treat member variables as properties\n     * @param bool $checkBehaviors whether to treat behaviors' properties as properties of this component\n     * @return bool whether the property can be read\n     * @see canSetProperty()\n     */\n    public function canGetProperty($name, $checkVars = true, $checkBehaviors = true)\n    {\n        if (method_exists($this, 'get' . $name) || $checkVars && property_exists($this, $name)) {\n            return true;\n        } elseif ($checkBehaviors) {\n            $this->ensureBehaviors();\n            foreach ($this->_behaviors as $behavior) {\n                if ($behavior->canGetProperty($name, $checkVars)) {\n                    return true;\n                }\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * Returns a value indicating whether a property can be set.\n     *\n     * A property can be written if:\n     *\n     * - the class has a setter method associated with the specified name\n     *   (in this case, property name is case-insensitive);\n     * - the class has a member variable with the specified name (when `$checkVars` is true);\n     * - an attached behavior has a writable property of the given name (when `$checkBehaviors` is true).\n     *\n     * @param string $name the property name\n     * @param bool $checkVars whether to treat member variables as properties\n     * @param bool $checkBehaviors whether to treat behaviors' properties as properties of this component\n     * @return bool whether the property can be written\n     * @see canGetProperty()\n     */\n    public function canSetProperty($name, $checkVars = true, $checkBehaviors = true)\n    {\n        if (method_exists($this, 'set' . $name) || $checkVars && property_exists($this, $name)) {\n            return true;\n        } elseif ($checkBehaviors) {\n            $this->ensureBehaviors();\n            foreach ($this->_behaviors as $behavior) {\n                if ($behavior->canSetProperty($name, $checkVars)) {\n                    return true;\n                }\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * Returns a value indicating whether a method is defined.\n     *\n     * A method is defined if:\n     *\n     * - the class has a method with the specified name\n     * - an attached behavior has a method with the given name (when `$checkBehaviors` is true).\n     *\n     * @param string $name the property name\n     * @param bool $checkBehaviors whether to treat behaviors' methods as methods of this component\n     * @return bool whether the method is defined\n     */\n    public function hasMethod($name, $checkBehaviors = true)\n    {\n        if (method_exists($this, $name)) {\n            return true;\n        } elseif ($checkBehaviors) {\n            $this->ensureBehaviors();\n            foreach ($this->_behaviors as $behavior) {\n                if ($behavior->hasMethod($name)) {\n                    return true;\n                }\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * Returns a list of behaviors that this component should behave as.\n     *\n     * Child classes may override this method to specify the behaviors they want to behave as.\n     *\n     * The return value of this method should be an array of behavior objects or configurations\n     * indexed by behavior names. A behavior configuration can be either a string specifying\n     * the behavior class or an array of the following structure:\n     *\n     * ```\n     * 'behaviorName' => [\n     *     'class' => 'BehaviorClass',\n     *     'property1' => 'value1',\n     *     'property2' => 'value2',\n     * ]\n     * ```\n     *\n     * Note that a behavior class must extend from [[Behavior]]. Behaviors can be attached using a name or anonymously.\n     * When a name is used as the array key, using this name, the behavior can later be retrieved using [[getBehavior()]]\n     * or be detached using [[detachBehavior()]]. Anonymous behaviors can not be retrieved or detached.\n     *\n     * Behaviors declared in this method will be attached to the component automatically (on demand).\n     *\n     * @return array<array-key, class-string|array{class: class-string, ...}> the behavior configurations.\n     */\n    public function behaviors()\n    {\n        return [];\n    }\n\n    /**\n     * Returns a value indicating whether there is any handler attached to the named event.\n     * @param string $name the event name\n     * @return bool whether there is any handler attached to the event.\n     */\n    public function hasEventHandlers($name)\n    {\n        $this->ensureBehaviors();\n\n        if (!empty($this->_events[$name])) {\n            return true;\n        }\n\n        foreach ($this->_eventWildcards as $wildcard => $handlers) {\n            if (!empty($handlers) && StringHelper::matchWildcard($wildcard, $name)) {\n                return true;\n            }\n        }\n\n        return Event::hasHandlers($this, $name);\n    }\n\n    /**\n     * Attaches an event handler to an event.\n     *\n     * The event handler must be a valid PHP callback. The following are\n     * some examples:\n     *\n     * ```\n     * function ($event) { ... }         // anonymous function\n     * [$object, 'handleClick']          // $object->handleClick()\n     * ['Page', 'handleClick']           // Page::handleClick()\n     * 'handleClick'                     // global function handleClick()\n     * ```\n     *\n     * The event handler must be defined with the following signature,\n     *\n     * ```\n     * function ($event)\n     * ```\n     *\n     * where `$event` is an [[Event]] object which includes parameters associated with the event.\n     *\n     * Since 2.0.14 you can specify event name as a wildcard pattern:\n     *\n     * ```\n     * $component->on('event.group.*', function ($event) {\n     *     Yii::trace($event->name . ' is triggered.');\n     * });\n     * ```\n     *\n     * @param string $name the event name\n     * @param callable $handler the event handler\n     * @param mixed $data the data to be passed to the event handler when the event is triggered.\n     * When the event handler is invoked, this data can be accessed via [[Event::data]].\n     * @param bool $append whether to append new event handler to the end of the existing\n     * handler list. If false, the new handler will be inserted at the beginning of the existing\n     * handler list.\n     * @see off()\n     */\n    public function on($name, $handler, $data = null, $append = true)\n    {\n        $this->ensureBehaviors();\n\n        if (strpos($name, '*') !== false) {\n            if ($append || empty($this->_eventWildcards[$name])) {\n                $this->_eventWildcards[$name][] = [$handler, $data];\n            } else {\n                array_unshift($this->_eventWildcards[$name], [$handler, $data]);\n            }\n            return;\n        }\n\n        if ($append || empty($this->_events[$name])) {\n            $this->_events[$name][] = [$handler, $data];\n        } else {\n            array_unshift($this->_events[$name], [$handler, $data]);\n        }\n    }\n\n    /**\n     * Detaches an existing event handler from this component.\n     *\n     * This method is the opposite of [[on()]].\n     *\n     * Note: in case wildcard pattern is passed for event name, only the handlers registered with this\n     * wildcard will be removed, while handlers registered with plain names matching this wildcard will remain.\n     *\n     * @param string $name event name\n     * @param callable|null $handler the event handler to be removed.\n     * If it is null, all handlers attached to the named event will be removed.\n     * @return bool if a handler is found and detached\n     * @see on()\n     */\n    public function off($name, $handler = null)\n    {\n        $this->ensureBehaviors();\n        if (empty($this->_events[$name]) && empty($this->_eventWildcards[$name])) {\n            return false;\n        }\n        if ($handler === null) {\n            unset($this->_events[$name], $this->_eventWildcards[$name]);\n            return true;\n        }\n\n        $removed = false;\n        // plain event names\n        if (isset($this->_events[$name])) {\n            foreach ($this->_events[$name] as $i => $event) {\n                if ($event[0] === $handler) {\n                    unset($this->_events[$name][$i]);\n                    $removed = true;\n                }\n            }\n            if ($removed) {\n                $this->_events[$name] = array_values($this->_events[$name]);\n                return true;\n            }\n        }\n\n        // wildcard event names\n        if (isset($this->_eventWildcards[$name])) {\n            foreach ($this->_eventWildcards[$name] as $i => $event) {\n                if ($event[0] === $handler) {\n                    unset($this->_eventWildcards[$name][$i]);\n                    $removed = true;\n                }\n            }\n            if ($removed) {\n                $this->_eventWildcards[$name] = array_values($this->_eventWildcards[$name]);\n                // remove empty wildcards to save future redundant regex checks:\n                if (empty($this->_eventWildcards[$name])) {\n                    unset($this->_eventWildcards[$name]);\n                }\n            }\n        }\n\n        return $removed;\n    }\n\n    /**\n     * Triggers an event.\n     *\n     * This method represents the happening of an event. It invokes all attached handlers for the event\n     * including class-level handlers.\n     *\n     * @param string $name the event name\n     * @param Event|null $event the event instance. If not set, a default [[Event]] object will be created.\n     */\n    public function trigger($name, ?Event $event = null)\n    {\n        $this->ensureBehaviors();\n\n        $eventHandlers = [];\n        foreach ($this->_eventWildcards as $wildcard => $handlers) {\n            if (StringHelper::matchWildcard($wildcard, $name)) {\n                $eventHandlers[] = $handlers;\n            }\n        }\n        if (!empty($this->_events[$name])) {\n            $eventHandlers[] = $this->_events[$name];\n        }\n\n        if (!empty($eventHandlers)) {\n            $eventHandlers = call_user_func_array('array_merge', $eventHandlers);\n            if ($event === null) {\n                $event = new Event();\n            }\n            if ($event->sender === null) {\n                $event->sender = $this;\n            }\n            $event->handled = false;\n            $event->name = $name;\n            foreach ($eventHandlers as $handler) {\n                $event->data = $handler[1];\n                call_user_func($handler[0], $event);\n                // stop further handling if the event is handled\n                if ($event->handled) {\n                    return;\n                }\n            }\n        }\n\n        // invoke class-level attached handlers\n        Event::trigger($this, $name, $event);\n    }\n\n    /**\n     * Returns the named behavior object.\n     * @param string $name the behavior name\n     * @return Behavior<static>|null the behavior object, or null if the behavior does not exist\n     *\n     * @phpstan-return Behavior<static>|null\n     * @psalm-return Behavior<self>|null\n     */\n    public function getBehavior($name)\n    {\n        $this->ensureBehaviors();\n        return isset($this->_behaviors[$name]) ? $this->_behaviors[$name] : null;\n    }\n\n    /**\n     * Returns all behaviors attached to this component.\n     * @return Behavior<static>[] list of behaviors attached to this component\n     *\n     * @phpstan-return Behavior<static>[]\n     * @psalm-return Behavior<self>[]\n     */\n    public function getBehaviors()\n    {\n        $this->ensureBehaviors();\n        return $this->_behaviors;\n    }\n\n    /**\n     * Attaches a behavior to this component.\n     * This method will create the behavior object based on the given\n     * configuration. After that, the behavior object will be attached to\n     * this component by calling the [[Behavior::attach()]] method.\n     * @param string $name the name of the behavior.\n     * @param string|array|Behavior<static> $behavior the behavior configuration. This can be one of the following:\n     *\n     *  - a [[Behavior]] object\n     *  - a string specifying the behavior class\n     *  - an object configuration array that will be passed to [[Yii::createObject()]] to create the behavior object.\n     *\n     * @return Behavior<static> the behavior object\n     * @see detachBehavior()\n     *\n     * @phpstan-param string|array|Behavior<static> $behavior\n     * @psalm-param string|array|Behavior<self> $behavior\n     *\n     * @phpstan-return Behavior<static>\n     * @psalm-return Behavior<self>\n     */\n    public function attachBehavior($name, $behavior)\n    {\n        $this->ensureBehaviors();\n        return $this->attachBehaviorInternal($name, $behavior);\n    }\n\n    /**\n     * Attaches a list of behaviors to the component.\n     * Each behavior is indexed by its name and should be a [[Behavior]] object,\n     * a string specifying the behavior class, or an configuration array for creating the behavior.\n     * @param array $behaviors list of behaviors to be attached to the component\n     * @see attachBehavior()\n     */\n    public function attachBehaviors($behaviors)\n    {\n        $this->ensureBehaviors();\n        foreach ($behaviors as $name => $behavior) {\n            $this->attachBehaviorInternal($name, $behavior);\n        }\n    }\n\n    /**\n     * Detaches a behavior from the component.\n     * The behavior's [[Behavior::detach()]] method will be invoked.\n     * @param string $name the behavior's name.\n     * @return Behavior<static>|null the detached behavior. Null if the behavior does not exist.\n     *\n     * @phpstan-return Behavior<static>|null\n     * @psalm-return Behavior<self>|null\n     */\n    public function detachBehavior($name)\n    {\n        $this->ensureBehaviors();\n        if (isset($this->_behaviors[$name])) {\n            $behavior = $this->_behaviors[$name];\n            unset($this->_behaviors[$name]);\n            $behavior->detach();\n            return $behavior;\n        }\n\n        return null;\n    }\n\n    /**\n     * Detaches all behaviors from the component.\n     */\n    public function detachBehaviors()\n    {\n        $this->ensureBehaviors();\n        foreach ($this->_behaviors as $name => $behavior) {\n            $this->detachBehavior($name);\n        }\n    }\n\n    /**\n     * Makes sure that the behaviors declared in [[behaviors()]] are attached to this component.\n     */\n    public function ensureBehaviors()\n    {\n        if ($this->_behaviors === null) {\n            $this->_behaviors = [];\n            foreach ($this->behaviors() as $name => $behavior) {\n                $this->attachBehaviorInternal($name, $behavior);\n            }\n        }\n    }\n\n    /**\n     * Attaches a behavior to this component.\n     * @param string|int $name the name of the behavior. If this is an integer, it means the behavior\n     * is an anonymous one. Otherwise, the behavior is a named one and any existing behavior with the same name\n     * will be detached first.\n     * @param string|array|Behavior<static> $behavior the behavior to be attached\n     * @return Behavior<static> the attached behavior.\n     */\n    private function attachBehaviorInternal($name, $behavior)\n    {\n        if (!($behavior instanceof Behavior)) {\n            $behavior = Yii::createObject($behavior);\n        }\n        if (is_int($name)) {\n            $behavior->attach($this);\n            $this->_behaviors[] = $behavior;\n        } else {\n            if (isset($this->_behaviors[$name])) {\n                $this->_behaviors[$name]->detach();\n            }\n            $behavior->attach($this);\n            $this->_behaviors[$name] = $behavior;\n        }\n\n        return $behavior;\n    }\n}\n"
  },
  {
    "path": "framework/base/Configurable.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * Configurable is the interface that should be implemented by classes who support configuring\n * its properties through the last parameter to its constructor.\n *\n * The interface does not declare any method. Classes implementing this interface must declare their constructors\n * like the following:\n *\n * ```\n * public function __construct($param1, $param2, ..., $config = [])\n * ```\n *\n * That is, the last parameter of the constructor must accept a configuration array.\n *\n * This interface is mainly used by [[\\yii\\di\\Container]] so that it can pass object configuration as the\n * last parameter to the implementing class' constructor.\n *\n * For more details and usage information on Configurable, see the [guide article on configurations](guide:concept-configurations).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0.3\n */\ninterface Configurable\n{\n}\n"
  },
  {
    "path": "framework/base/Controller.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\nuse Yii;\nuse yii\\di\\Instance;\nuse yii\\di\\NotInstantiableException;\n\n/**\n * Controller is the base class for classes containing controller logic.\n *\n * For more details and usage information on Controller, see the [guide article on controllers](guide:structure-controllers).\n *\n * @property-read Module[] $modules All ancestor modules that this controller is located within.\n * @property-read string $route The route (module ID, controller ID and action ID) of the current request.\n * @property-read string $uniqueId The controller ID that is prefixed with the module ID (if any).\n * @property View|\\yii\\web\\View $view The view object that can be used to render views or view files.\n * @property string $viewPath The directory containing the view files for this controller.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Module = Module\n */\nclass Controller extends Component implements ViewContextInterface\n{\n    /**\n     * @event ActionEvent an event raised right before executing a controller action.\n     * You may set [[ActionEvent::isValid]] to be false to cancel the action execution.\n     */\n    public const EVENT_BEFORE_ACTION = 'beforeAction';\n    /**\n     * @event ActionEvent an event raised right after executing a controller action.\n     */\n    public const EVENT_AFTER_ACTION = 'afterAction';\n    /**\n     * @var string the ID of this controller.\n     */\n    public $id;\n    /**\n     * @var T the module that this controller belongs to.\n     */\n    public $module;\n    /**\n     * @var string the ID of the action that is used when the action ID is not specified\n     * in the request. Defaults to 'index'.\n     */\n    public $defaultAction = 'index';\n    /**\n     * @var string|null|false the name of the layout to be applied to this controller's views.\n     * This property mainly affects the behavior of [[render()]].\n     * Defaults to null, meaning the actual layout value should inherit that from [[module]]'s layout value.\n     * If false, no layout will be applied.\n     */\n    public $layout;\n    /**\n     * @var Action<covariant static>|null the action that is currently being executed. This property will be set\n     * by [[run()]] when it is called by [[Application]] to run an action.\n     *\n     * @phpstan-var Action<covariant static>|null\n     * @psalm-var Action<self>|null\n     */\n    public $action;\n    /**\n     * @var Request|array|string The request.\n     * @since 2.0.36\n     */\n    public $request = 'request';\n    /**\n     * @var Response|array|string The response.\n     * @since 2.0.36\n     */\n    public $response = 'response';\n\n    /**\n     * @var View|null the view object that can be used to render views or view files.\n     */\n    private $_view;\n    /**\n     * @var string|null the root directory that contains view files for this controller.\n     */\n    private $_viewPath;\n\n\n    /**\n     * @param string $id the ID of this controller.\n     * @param T $module the module that this controller belongs to.\n     * @param array<string, mixed> $config name-value pairs that will be used to initialize the object properties.\n     */\n    public function __construct($id, $module, $config = [])\n    {\n        $this->id = $id;\n        $this->module = $module;\n        parent::__construct($config);\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.36\n     */\n    public function init()\n    {\n        parent::init();\n        $this->request = Instance::ensure($this->request, Request::className());\n        $this->response = Instance::ensure($this->response, Response::className());\n    }\n\n    /**\n     * Declares external actions for the controller.\n     *\n     * This method is meant to be overwritten to declare external actions for the controller.\n     * It should return an array, with array keys being action IDs, and array values the corresponding\n     * action class names or action configuration arrays. For example,\n     *\n     * ```\n     * return [\n     *     'action1' => 'app\\components\\Action1',\n     *     'action2' => [\n     *         'class' => 'app\\components\\Action2',\n     *         'property1' => 'value1',\n     *         'property2' => 'value2',\n     *     ],\n     * ];\n     * ```\n     *\n     * [[\\Yii::createObject()]] will be used later to create the requested action\n     * using the configuration provided here.\n     * @return array<array-key, class-string|array{class: class-string, ...}>\n     */\n    public function actions()\n    {\n        return [];\n    }\n\n    /**\n     * Runs an action within this controller with the specified action ID and parameters.\n     * If the action ID is empty, the method will use [[defaultAction]].\n     * @param string $id the ID of the action to be executed.\n     * @param array<array-key, mixed> $params the parameters (name-value pairs) to be passed to the action.\n     * @return mixed the result of the action.\n     * @throws InvalidRouteException if the requested action ID cannot be resolved into an action successfully.\n     * @see createAction()\n     */\n    public function runAction($id, $params = [])\n    {\n        $action = $this->createAction($id);\n        if ($action === null) {\n            throw new InvalidRouteException('Unable to resolve the request: ' . $this->getUniqueId() . '/' . $id);\n        }\n\n        Yii::debug('Route to run: ' . $action->getUniqueId(), __METHOD__);\n\n        if (Yii::$app->requestedAction === null) {\n            Yii::$app->requestedAction = $action;\n        }\n\n        $oldAction = $this->action;\n        $this->action = $action;\n\n        $modules = [];\n        $runAction = true;\n\n        // call beforeAction on modules\n        foreach ($this->getModules() as $module) {\n            if ($module->beforeAction($action)) {\n                array_unshift($modules, $module);\n            } else {\n                $runAction = false;\n                break;\n            }\n        }\n\n        $result = null;\n\n        if ($runAction && $this->beforeAction($action)) {\n            // run the action\n            $result = $action->runWithParams($params);\n\n            $result = $this->afterAction($action, $result);\n\n            // call afterAction on modules\n            foreach ($modules as $module) {\n                /** @var Module $module */\n                $result = $module->afterAction($action, $result);\n            }\n        }\n\n        if ($oldAction !== null) {\n            $this->action = $oldAction;\n        }\n\n        return $result;\n    }\n\n    /**\n     * Runs a request specified in terms of a route.\n     * The route can be either an ID of an action within this controller or a complete route consisting\n     * of module IDs, controller ID and action ID. If the route starts with a slash '/', the parsing of\n     * the route will start from the application; otherwise, it will start from the parent module of this controller.\n     * @param string $route the route to be handled, e.g., 'view', 'comment/view', '/admin/comment/view'.\n     * @param array<array-key, mixed> $params the parameters to be passed to the action.\n     * @return mixed the result of the action.\n     * @see runAction()\n     */\n    public function run($route, $params = [])\n    {\n        $pos = strpos($route, '/');\n        if ($pos === false) {\n            return $this->runAction($route, $params);\n        } elseif ($pos > 0) {\n            return $this->module->runAction($route, $params);\n        }\n\n        return Yii::$app->runAction(ltrim($route, '/'), $params);\n    }\n\n    /**\n     * Binds the parameters to the action.\n     * This method is invoked by [[Action]] when it begins to run with the given parameters.\n     * @param Action<static> $action the action to be bound with parameters.\n     * @param array<array-key, mixed> $params the parameters to be bound to the action.\n     * @return mixed[] the valid parameters that the action can run with.\n     *\n     * @phpstan-param Action<static> $action\n     * @psalm-param Action<self> $action\n     */\n    public function bindActionParams($action, $params)\n    {\n        return [];\n    }\n\n    /**\n     * Creates an action based on the given action ID.\n     * The method first checks if the action ID has been declared in [[actions()]]. If so,\n     * it will use the configuration declared there to create the action object.\n     * If not, it will look for a controller method whose name is in the format of `actionXyz`\n     * where `xyz` is the action ID. If found, an [[InlineAction]] representing that\n     * method will be created and returned.\n     * @param string $id the action ID.\n     * @return Action<covariant static>|null the newly created action instance. Null if the ID doesn't resolve into any action.\n     *\n     * @phpstan-return Action<covariant static>|null\n     * @psalm-return Action<self>|null\n     */\n    public function createAction($id)\n    {\n        if ($id === '') {\n            $id = $this->defaultAction;\n        }\n\n        $actionMap = $this->actions();\n        if (isset($actionMap[$id])) {\n            return Yii::createObject($actionMap[$id], [$id, $this]);\n        }\n\n        if (preg_match('/^(?:[a-z0-9_]+-)*[a-z0-9_]+$/', $id)) {\n            $methodName = 'action' . str_replace(' ', '', ucwords(str_replace('-', ' ', $id)));\n            if (method_exists($this, $methodName)) {\n                $method = new \\ReflectionMethod($this, $methodName);\n                if ($method->isPublic() && $method->getName() === $methodName) {\n                    return new InlineAction($id, $this, $methodName);\n                }\n            }\n        }\n\n        return null;\n    }\n\n    /**\n     * This method is invoked right before an action is executed.\n     *\n     * The method will trigger the [[EVENT_BEFORE_ACTION]] event. The return value of the method\n     * will determine whether the action should continue to run.\n     *\n     * In case the action should not run, the request should be handled inside of the `beforeAction` code\n     * by either providing the necessary output or redirecting the request. Otherwise the response will be empty.\n     *\n     * If you override this method, your code should look like the following:\n     *\n     * ```\n     * public function beforeAction($action)\n     * {\n     *     // your custom code here, if you want the code to run before action filters,\n     *     // which are triggered on the [[EVENT_BEFORE_ACTION]] event, e.g. PageCache or AccessControl\n     *\n     *     if (!parent::beforeAction($action)) {\n     *         return false;\n     *     }\n     *\n     *     // other custom code here\n     *\n     *     return true; // or false to not run the action\n     * }\n     * ```\n     *\n     * @param Action<static> $action the action to be executed.\n     * @return bool whether the action should continue to run.\n     *\n     * @phpstan-param Action<static> $action\n     * @psalm-param Action<self> $action\n     */\n    public function beforeAction($action)\n    {\n        $event = new ActionEvent($action);\n        $this->trigger(self::EVENT_BEFORE_ACTION, $event);\n        return $event->isValid;\n    }\n\n    /**\n     * This method is invoked right after an action is executed.\n     *\n     * The method will trigger the [[EVENT_AFTER_ACTION]] event. The return value of the method\n     * will be used as the action return value.\n     *\n     * If you override this method, your code should look like the following:\n     *\n     * ```\n     * public function afterAction($action, $result)\n     * {\n     *     $result = parent::afterAction($action, $result);\n     *     // your custom code here\n     *     return $result;\n     * }\n     * ```\n     *\n     * @param Action<static> $action the action just executed.\n     * @param mixed $result the action return result.\n     * @return mixed the processed action result.\n     *\n     * @phpstan-param Action<static> $action\n     * @psalm-param Action<self> $action\n     */\n    public function afterAction($action, $result)\n    {\n        $event = new ActionEvent($action);\n        $event->result = $result;\n        $this->trigger(self::EVENT_AFTER_ACTION, $event);\n        return $event->result;\n    }\n\n    /**\n     * Returns all ancestor modules of this controller.\n     * The first module in the array is the outermost one (i.e., the application instance),\n     * while the last is the innermost one.\n     * @return Module[] all ancestor modules that this controller is located within.\n     */\n    public function getModules()\n    {\n        $modules = [$this->module];\n        $module = $this->module;\n        while ($module->module !== null) {\n            array_unshift($modules, $module->module);\n            $module = $module->module;\n        }\n\n        return $modules;\n    }\n\n    /**\n     * Returns the unique ID of the controller.\n     * @return string the controller ID that is prefixed with the module ID (if any).\n     */\n    public function getUniqueId()\n    {\n        return $this->module instanceof Application ? $this->id : $this->module->getUniqueId() . '/' . $this->id;\n    }\n\n    /**\n     * Returns the route of the current request.\n     * @return string the route (module ID, controller ID and action ID) of the current request.\n     */\n    public function getRoute()\n    {\n        return $this->action !== null ? $this->action->getUniqueId() : $this->getUniqueId();\n    }\n\n    /**\n     * Renders a view and applies layout if available.\n     *\n     * The view to be rendered can be specified in one of the following formats:\n     *\n     * - [path alias](guide:concept-aliases) (e.g. \"@app/views/site/index\");\n     * - absolute path within application (e.g. \"//site/index\"): the view name starts with double slashes.\n     *   The actual view file will be looked for under the [[Application::viewPath|view path]] of the application.\n     * - absolute path within module (e.g. \"/site/index\"): the view name starts with a single slash.\n     *   The actual view file will be looked for under the [[Module::viewPath|view path]] of [[module]].\n     * - relative path (e.g. \"index\"): the actual view file will be looked for under [[viewPath]].\n     *\n     * To determine which layout should be applied, the following two steps are conducted:\n     *\n     * 1. In the first step, it determines the layout name and the context module:\n     *\n     * - If [[layout]] is specified as a string, use it as the layout name and [[module]] as the context module;\n     * - If [[layout]] is null, search through all ancestor modules of this controller and find the first\n     *   module whose [[Module::layout|layout]] is not null. The layout and the corresponding module\n     *   are used as the layout name and the context module, respectively. If such a module is not found\n     *   or the corresponding layout is not a string, it will return false, meaning no applicable layout.\n     *\n     * 2. In the second step, it determines the actual layout file according to the previously found layout name\n     *    and context module. The layout name can be:\n     *\n     * - a [path alias](guide:concept-aliases) (e.g. \"@app/views/layouts/main\");\n     * - an absolute path (e.g. \"/main\"): the layout name starts with a slash. The actual layout file will be\n     *   looked for under the [[Application::layoutPath|layout path]] of the application;\n     * - a relative path (e.g. \"main\"): the actual layout file will be looked for under the\n     *   [[Module::layoutPath|layout path]] of the context module.\n     *\n     * If the layout name does not contain a file extension, it will use the default one `.php`.\n     *\n     * @param string $view the view name.\n     * @param array<string, mixed> $params the parameters (name-value pairs) that should be made available in the view.\n     * These parameters will not be available in the layout.\n     * @return string the rendering result.\n     * @throws InvalidArgumentException if the view file or the layout file does not exist.\n     */\n    public function render($view, $params = [])\n    {\n        $content = $this->getView()->render($view, $params, $this);\n        return $this->renderContent($content);\n    }\n\n    /**\n     * Renders a static string by applying a layout.\n     * @param string $content the static string being rendered\n     * @return string the rendering result of the layout with the given static string as the `$content` variable.\n     * If the layout is disabled, the string will be returned back.\n     * @since 2.0.1\n     */\n    public function renderContent($content)\n    {\n        $layoutFile = $this->findLayoutFile($this->getView());\n        if ($layoutFile !== false) {\n            return $this->getView()->renderFile($layoutFile, ['content' => $content], $this);\n        }\n\n        return $content;\n    }\n\n    /**\n     * Renders a view without applying layout.\n     * This method differs from [[render()]] in that it does not apply any layout.\n     * @param string $view the view name. Please refer to [[render()]] on how to specify a view name.\n     * @param array<string, mixed> $params the parameters (name-value pairs) that should be made available in the view.\n     * @return string the rendering result.\n     * @throws InvalidArgumentException if the view file does not exist.\n     */\n    public function renderPartial($view, $params = [])\n    {\n        return $this->getView()->render($view, $params, $this);\n    }\n\n    /**\n     * Renders a view file.\n     * @param string $file the view file to be rendered. This can be either a file path or a [path alias](guide:concept-aliases).\n     * @param array<string, mixed> $params the parameters (name-value pairs) that should be made available in the view.\n     * @return string the rendering result.\n     * @throws InvalidArgumentException if the view file does not exist.\n     */\n    public function renderFile($file, $params = [])\n    {\n        return $this->getView()->renderFile($file, $params, $this);\n    }\n\n    /**\n     * Returns the view object that can be used to render views or view files.\n     * The [[render()]], [[renderPartial()]] and [[renderFile()]] methods will use\n     * this view object to implement the actual view rendering.\n     * If not set, it will default to the \"view\" application component.\n     * @return View|\\yii\\web\\View the view object that can be used to render views or view files.\n     */\n    public function getView()\n    {\n        if ($this->_view === null) {\n            $this->_view = Yii::$app->getView();\n        }\n\n        return $this->_view;\n    }\n\n    /**\n     * Sets the view object to be used by this controller.\n     * @param View|\\yii\\web\\View $view the view object that can be used to render views or view files.\n     */\n    public function setView($view)\n    {\n        $this->_view = $view;\n    }\n\n    /**\n     * Returns the directory containing view files for this controller.\n     * The default implementation returns the directory named as controller [[id]] under the [[module]]'s\n     * [[viewPath]] directory.\n     * @return string the directory containing the view files for this controller.\n     */\n    public function getViewPath()\n    {\n        if ($this->_viewPath === null) {\n            $this->_viewPath = $this->module->getViewPath() . DIRECTORY_SEPARATOR . $this->id;\n        }\n\n        return $this->_viewPath;\n    }\n\n    /**\n     * Sets the directory that contains the view files.\n     * @param string $path the root directory of view files.\n     * @throws InvalidArgumentException if the directory is invalid\n     * @since 2.0.7\n     */\n    public function setViewPath($path)\n    {\n        $this->_viewPath = Yii::getAlias($path);\n    }\n\n    /**\n     * Finds the applicable layout file.\n     * @param View $view the view object to render the layout file.\n     * @return string|bool the layout file path, or false if layout is not needed.\n     * Please refer to [[render()]] on how to specify this parameter.\n     * @throws InvalidArgumentException if an invalid path alias is used to specify the layout.\n     */\n    public function findLayoutFile($view)\n    {\n        $module = $this->module;\n        $layout = null;\n        if (is_string($this->layout)) {\n            $layout = $this->layout;\n        } elseif ($this->layout === null) {\n            while ($module !== null && $module->layout === null) {\n                $module = $module->module;\n            }\n            if ($module !== null && is_string($module->layout)) {\n                $layout = $module->layout;\n            }\n        }\n\n        if ($layout === null) {\n            return false;\n        }\n\n        if (strncmp($layout, '@', 1) === 0) {\n            $file = Yii::getAlias($layout);\n        } elseif (strncmp($layout, '/', 1) === 0) {\n            $file = Yii::$app->getLayoutPath() . DIRECTORY_SEPARATOR . substr($layout, 1);\n        } else {\n            $file = $module->getLayoutPath() . DIRECTORY_SEPARATOR . $layout;\n        }\n\n        if (pathinfo($file, PATHINFO_EXTENSION) !== '') {\n            return $file;\n        }\n        $path = $file . '.' . $view->defaultExtension;\n        if ($view->defaultExtension !== 'php' && !is_file($path)) {\n            $path = $file . '.php';\n        }\n\n        return $path;\n    }\n\n    /**\n     * Fills parameters based on types and names in action method signature.\n     * @param \\ReflectionNamedType $type The reflected type of the action parameter.\n     * @param string $name The name of the parameter.\n     * @param array &$args The array of arguments for the action, this function may append items to it.\n     * @param array &$requestedParams The array with requested params, this function may write specific keys to it.\n     * @throws ErrorException when we cannot load a required service.\n     * @throws InvalidConfigException Thrown when there is an error in the DI configuration.\n     * @throws NotInstantiableException Thrown when a definition cannot be resolved to a concrete class\n     * (for example an interface type hint) without a proper definition in the container.\n     * @since 2.0.36\n     */\n    final protected function bindInjectedParams(\\ReflectionNamedType $type, $name, &$args, &$requestedParams)\n    {\n        // Since it is not a builtin type it must be DI injection.\n        $typeName = $type->getName();\n        if (($component = $this->module->get($name, false)) instanceof $typeName) {\n            $args[] = $component;\n            $requestedParams[$name] = 'Component: ' . get_class($component) . \" \\$$name\";\n        } elseif ($this->module->has($typeName) && ($service = $this->module->get($typeName)) instanceof $typeName) {\n            $args[] = $service;\n            $requestedParams[$name] = 'Module ' . get_class($this->module) . \" DI: $typeName \\$$name\";\n        } elseif (\\Yii::$container->has($typeName) && ($service = \\Yii::$container->get($typeName)) instanceof $typeName) {\n            $args[] = $service;\n            $requestedParams[$name] = \"Container DI: $typeName \\$$name\";\n        } elseif ($type->allowsNull()) {\n            $args[] = null;\n            $requestedParams[$name] = \"Unavailable service: $name\";\n        } else {\n            throw new Exception('Could not load required service: ' . $name);\n        }\n    }\n}\n"
  },
  {
    "path": "framework/base/DynamicContentAwareInterface.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * DynamicContentAwareInterface is the interface that should be implemented by classes\n * which support a [[View]] dynamic content feature.\n *\n * @author Sergey Makinen <sergey@makinen.ru>\n * @since 2.0.14\n */\ninterface DynamicContentAwareInterface\n{\n    /**\n     * Returns a list of placeholders for dynamic content. This method\n     * is used internally to implement the content caching feature.\n     * @return array a list of placeholders.\n     */\n    public function getDynamicPlaceholders();\n\n    /**\n     * Sets a list of placeholders for dynamic content. This method\n     * is used internally to implement the content caching feature.\n     * @param array $placeholders a list of placeholders.\n     */\n    public function setDynamicPlaceholders($placeholders);\n\n    /**\n     * Adds a placeholder for dynamic content.\n     * This method is used internally to implement the content caching feature.\n     * @param string $name the placeholder name.\n     * @param string $statements the PHP statements for generating the dynamic content.\n     */\n    public function addDynamicPlaceholder($name, $statements);\n}\n"
  },
  {
    "path": "framework/base/DynamicContentAwareTrait.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * DynamicContentAwareTrait implements common methods for classes\n * which support a [[View]] dynamic content feature.\n *\n * @author Sergey Makinen <sergey@makinen.ru>\n * @since 2.0.14\n */\ntrait DynamicContentAwareTrait\n{\n    /**\n     * @var string[] a list of placeholders for dynamic content\n     */\n    private $_dynamicPlaceholders;\n\n    /**\n     * Returns the view object that can be used to render views or view files using dynamic contents.\n     * @return View the view object that can be used to render views or view files.\n     */\n    abstract protected function getView();\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getDynamicPlaceholders()\n    {\n        return $this->_dynamicPlaceholders;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function setDynamicPlaceholders($placeholders)\n    {\n        $this->_dynamicPlaceholders = $placeholders;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function addDynamicPlaceholder($name, $statements)\n    {\n        $this->_dynamicPlaceholders[$name] = $statements;\n    }\n\n    /**\n     * Replaces placeholders in $content with results of evaluated dynamic statements.\n     * @param string $content content to be parsed.\n     * @param string[] $placeholders placeholders and their values.\n     * @param bool $isRestoredFromCache whether content is going to be restored from cache.\n     * @return string final content.\n     */\n    protected function updateDynamicContent($content, $placeholders, $isRestoredFromCache = false)\n    {\n        if (empty($placeholders) || !is_array($placeholders)) {\n            return $content;\n        }\n\n        if (count($this->getView()->getDynamicContents()) === 0) {\n            // outermost cache: replace placeholder with dynamic content\n            foreach ($placeholders as $name => $statements) {\n                $placeholders[$name] = $this->getView()->evaluateDynamicContent($statements);\n            }\n            $content = strtr($content, $placeholders);\n        }\n        if ($isRestoredFromCache) {\n            $view = $this->getView();\n            foreach ($placeholders as $name => $statements) {\n                $view->addDynamicPlaceholder($name, $statements);\n            }\n        }\n\n        return $content;\n    }\n}\n"
  },
  {
    "path": "framework/base/DynamicModel.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\nuse yii\\validators\\Validator;\n\n/**\n * DynamicModel is a model class that supports defining attributes at run-time (the so-called\n * \"dynamic attributes\") using its constructor or [[defineAttribute()]]. DynamicModel can be used\n * to support ad hoc data validation.\n *\n * The typical usage of DynamicModel is as follows,\n *\n * ```\n * public function actionSearch($name, $email)\n * {\n *     $model = DynamicModel::validateData(compact('name', 'email'), [\n *         [['name', 'email'], 'string', 'max' => 128],\n *         ['email', 'email'],\n *     ]);\n *     if ($model->hasErrors()) {\n *         // validation fails\n *     } else {\n *         // validation succeeds\n *     }\n * }\n * ```\n *\n * The above example shows how to validate `$name` and `$email` with the help of DynamicModel.\n * The [[validateData()]] method creates an instance of DynamicModel, defines the attributes\n * using the given data (`name` and `email` in this example), and then calls [[Model::validate()]].\n *\n * You can check the validation result using [[hasErrors()]], like you do with a normal model.\n * You may also access the dynamic attributes defined through the model instance, e.g.,\n * `$model->name` and `$model->email`.\n *\n * Alternatively, you may use the following more \"classic\" syntax to perform ad-hoc data validation:\n *\n * ```\n * $model = new DynamicModel(compact('name', 'email'));\n * $model->addRule(['name', 'email'], 'string', ['max' => 128])\n *     ->addRule('email', 'email')\n *     ->validate();\n * ```\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass DynamicModel extends Model\n{\n    /**\n     * @var mixed[] dynamic attribute values (name => value).\n     */\n    private $_attributes = [];\n    /**\n     * @var string[] dynamic attribute labels (name => label).\n     * Used as form field labels and in validation error messages.\n     * @since 2.0.35\n     */\n    private $_attributeLabels = [];\n\n\n    /**\n     * Constructor.\n     * @param array<string, mixed>|string[] $attributes the attributes (name-value pairs, or names) being defined.\n     * @param array<string, mixed> $config the configuration array to be applied to this object.\n     */\n    public function __construct(array $attributes = [], $config = [])\n    {\n        foreach ($attributes as $name => $value) {\n            if (is_int($name)) {\n                $this->_attributes[$value] = null;\n            } else {\n                $this->_attributes[$name] = $value;\n            }\n        }\n        parent::__construct($config);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function __get($name)\n    {\n        if ($this->hasAttribute($name)) {\n            return $this->_attributes[$name];\n        }\n\n        return parent::__get($name);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function __set($name, $value)\n    {\n        if ($this->hasAttribute($name)) {\n            $this->_attributes[$name] = $value;\n        } else {\n            parent::__set($name, $value);\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function __isset($name)\n    {\n        if ($this->hasAttribute($name)) {\n            return isset($this->_attributes[$name]);\n        }\n\n        return parent::__isset($name);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function __unset($name)\n    {\n        if ($this->hasAttribute($name)) {\n            unset($this->_attributes[$name]);\n        } else {\n            parent::__unset($name);\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function canGetProperty($name, $checkVars = true, $checkBehaviors = true)\n    {\n        return parent::canGetProperty($name, $checkVars, $checkBehaviors) || $this->hasAttribute($name);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function canSetProperty($name, $checkVars = true, $checkBehaviors = true)\n    {\n        return parent::canSetProperty($name, $checkVars, $checkBehaviors) || $this->hasAttribute($name);\n    }\n\n    /**\n     * Returns a value indicating whether the model has an attribute with the specified name.\n     * @param string $name the name of the attribute.\n     * @return bool whether the model has an attribute with the specified name.\n     * @since 2.0.16\n     */\n    public function hasAttribute($name)\n    {\n        return array_key_exists($name, $this->_attributes);\n    }\n\n    /**\n     * Defines an attribute.\n     * @param string $name the attribute name.\n     * @param mixed $value the attribute value.\n     */\n    public function defineAttribute($name, $value = null)\n    {\n        $this->_attributes[$name] = $value;\n    }\n\n    /**\n     * Undefines an attribute.\n     * @param string $name the attribute name.\n     */\n    public function undefineAttribute($name)\n    {\n        unset($this->_attributes[$name]);\n    }\n\n    /**\n     * Adds a validation rule to this model.\n     * You can also directly manipulate [[validators]] to add or remove validation rules.\n     * This method provides a shortcut.\n     * @param string|array $attributes the attribute(s) to be validated by the rule.\n     * @param string|Validator|\\Closure $validator the validator. This can be either:\n     *  * a built-in validator name listed in [[builtInValidators]];\n     *  * a method name of the model class;\n     *  * an anonymous function;\n     *  * a validator class name.\n     *  * a Validator.\n     * @param array $options the options (name-value pairs) to be applied to the validator.\n     * @return $this\n     */\n    public function addRule($attributes, $validator, $options = [])\n    {\n        $validators = $this->getValidators();\n\n        if ($validator instanceof Validator) {\n            $validator->attributes = (array)$attributes;\n        } else {\n            $validator = Validator::createValidator($validator, $this, (array)$attributes, $options);\n        }\n\n        $validators->append($validator);\n        $this->defineAttributesByValidator($validator);\n\n        return $this;\n    }\n\n    /**\n     * Validates the given data with the specified validation rules.\n     * This method will create a DynamicModel instance, populate it with the data to be validated,\n     * create the specified validation rules, and then validate the data using these rules.\n     * @param array $data the data (name-value pairs) to be validated.\n     * @param array $rules the validation rules. Please refer to [[Model::rules()]] on the format of this parameter.\n     * @return static the model instance that contains the data being validated.\n     * @throws InvalidConfigException if a validation rule is not specified correctly.\n     */\n    public static function validateData(array $data, $rules = [])\n    {\n        /** @var static $model */\n        $model = new static($data);\n        if (!empty($rules)) {\n            $validators = $model->getValidators();\n            foreach ($rules as $rule) {\n                if ($rule instanceof Validator) {\n                    $validators->append($rule);\n                    $model->defineAttributesByValidator($rule);\n                } elseif (is_array($rule) && isset($rule[0], $rule[1])) { // attributes, validator type\n                    $validator = Validator::createValidator($rule[1], $model, (array)$rule[0], array_slice($rule, 2));\n                    $validators->append($validator);\n                    $model->defineAttributesByValidator($validator);\n                } else {\n                    throw new InvalidConfigException('Invalid validation rule: a rule must specify both attribute names and validator type.');\n                }\n            }\n        }\n\n        $model->validate();\n\n        return $model;\n    }\n\n    /**\n     * Define the attributes that applies to the specified Validator.\n     * @param Validator $validator the validator whose attributes are to be defined.\n     */\n    private function defineAttributesByValidator($validator)\n    {\n        foreach ($validator->getAttributeNames() as $attribute) {\n            if (!$this->hasAttribute($attribute)) {\n                $this->defineAttribute($attribute);\n            }\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function attributes()\n    {\n        return array_keys($this->_attributes);\n    }\n\n    /**\n     * Sets the labels for all attributes.\n     * @param string[] $labels attribute labels.\n     * @return $this\n     * @since 2.0.35\n     */\n    public function setAttributeLabels(array $labels = [])\n    {\n        $this->_attributeLabels = $labels;\n\n        return $this;\n    }\n\n    /**\n     * Sets a label for a single attribute.\n     * @param string $attribute attribute name.\n     * @param string $label attribute label value.\n     * @return $this\n     * @since 2.0.35\n     */\n    public function setAttributeLabel($attribute, $label)\n    {\n        $this->_attributeLabels[$attribute] = $label;\n\n        return $this;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function attributeLabels()\n    {\n        return $this->_attributeLabels;\n    }\n}\n"
  },
  {
    "path": "framework/base/ErrorException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * ErrorException represents a PHP error.\n *\n * For more details and usage information on ErrorException, see the [guide article on handling errors](guide:runtime-handling-errors).\n *\n * @author Alexander Makarov <sam@rmcreative.ru>\n * @since 2.0\n */\nclass ErrorException extends \\ErrorException\n{\n    /**\n     * This constant represents a fatal error in the HHVM engine.\n     *\n     * PHP Zend runtime won't call the error handler on fatals, HHVM will, with an error code of 16777217\n     * We will handle fatal error a bit different on HHVM.\n     * @see https://github.com/facebook/hhvm/blob/master/hphp/runtime/base/runtime-error.h#L62\n     * @since 2.0.6\n     */\n    public const E_HHVM_FATAL_ERROR = 16777217; // E_ERROR | (1 << 24)\n    /**\n     * Constructs the exception.\n     * @link https://www.php.net/manual/en/errorexception.construct.php\n     * @param string $message [optional]\n     * @param int $code [optional]\n     * @param int $severity [optional]\n     * @param string $filename [optional]\n     * @param int $lineno [optional]\n     * @param \\Throwable|null $previous [optional]\n     */\n    public function __construct($message = '', $code = 0, $severity = 1, $filename = __FILE__, $lineno = __LINE__, $previous = null)\n    {\n        parent::__construct($message, $code, $severity, $filename, $lineno, $previous);\n\n        if ($this->isXdebugStackAvailable()) {\n            // Xdebug trace can't be modified and used directly with PHP 7\n            // @see https://github.com/yiisoft/yii2/pull/11723\n            $xdebugTrace = array_slice(array_reverse(xdebug_get_function_stack()), 1, -1);\n            $trace = [];\n            foreach ($xdebugTrace as $frame) {\n                if (!isset($frame['function'])) {\n                    $frame['function'] = 'unknown';\n                }\n\n                // Xdebug < 2.1.1: https://bugs.xdebug.org/view.php?id=695\n                if (!isset($frame['type']) || $frame['type'] === 'static') {\n                    $frame['type'] = '::';\n                } elseif ($frame['type'] === 'dynamic') {\n                    $frame['type'] = '->';\n                }\n\n                // Xdebug has a different key name\n                if (isset($frame['params']) && !isset($frame['args'])) {\n                    $frame['args'] = $frame['params'];\n                }\n                $trace[] = $frame;\n            }\n\n            $ref = new \\ReflectionProperty('Exception', 'trace');\n\n            // @link https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_reflectionsetaccessible\n            // @link https://wiki.php.net/rfc/make-reflection-setaccessible-no-op\n            if (PHP_VERSION_ID < 80100) {\n                $ref->setAccessible(true);\n            }\n\n            $ref->setValue($this, $trace);\n        }\n    }\n\n    /**\n     * Ensures that Xdebug stack trace is available based on Xdebug version.\n     * Idea taken from developer bishopb at https://github.com/rollbar/rollbar-php\n     * @return bool\n     */\n    private function isXdebugStackAvailable()\n    {\n        if (!function_exists('xdebug_get_function_stack')) {\n            return false;\n        }\n\n        // check for Xdebug being installed to ensure origin of xdebug_get_function_stack()\n        $version = phpversion('xdebug');\n        if ($version === false) {\n            return false;\n        }\n\n        // Xdebug 2 and prior\n        if (version_compare($version, '3.0.0', '<')) {\n            return true;\n        }\n\n        // Xdebug 3 and later, proper mode is required\n        return false !== strpos(ini_get('xdebug.mode'), 'develop');\n    }\n\n    /**\n     * Returns if error is one of fatal type.\n     *\n     * @param array $error error got from error_get_last()\n     * @return bool if error is one of fatal type\n     */\n    public static function isFatalError($error)\n    {\n        return isset($error['type']) && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING, self::E_HHVM_FATAL_ERROR]);\n    }\n\n    /**\n     * @return string the user-friendly name of this exception\n     */\n    public function getName()\n    {\n        static $names = [\n            E_COMPILE_ERROR => 'PHP Compile Error',\n            E_COMPILE_WARNING => 'PHP Compile Warning',\n            E_CORE_ERROR => 'PHP Core Error',\n            E_CORE_WARNING => 'PHP Core Warning',\n            E_DEPRECATED => 'PHP Deprecated Warning',\n            E_ERROR => 'PHP Fatal Error',\n            E_NOTICE => 'PHP Notice',\n            E_PARSE => 'PHP Parse Error',\n            E_RECOVERABLE_ERROR => 'PHP Recoverable Error',\n            E_USER_DEPRECATED => 'PHP User Deprecated Warning',\n            E_USER_ERROR => 'PHP User Error',\n            E_USER_NOTICE => 'PHP User Notice',\n            E_USER_WARNING => 'PHP User Warning',\n            E_WARNING => 'PHP Warning',\n            self::E_HHVM_FATAL_ERROR => 'HHVM Fatal Error',\n        ] + (PHP_VERSION_ID < 80400 ? [E_STRICT => 'PHP Strict Warning'] : []);\n\n        return $names[$this->getCode()] ?? 'Error';\n    }\n}\n"
  },
  {
    "path": "framework/base/ErrorHandler.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\nuse Yii;\nuse yii\\helpers\\VarDumper;\nuse yii\\web\\HttpException;\n\n/**\n * ErrorHandler handles uncaught PHP errors and exceptions.\n *\n * ErrorHandler is configured as an application component in [[\\yii\\base\\Application]] by default.\n * You can access that instance via `Yii::$app->errorHandler`.\n *\n * For more details and usage information on ErrorHandler, see the [guide article on handling errors](guide:runtime-handling-errors).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Alexander Makarov <sam@rmcreative.ru>\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0\n */\nabstract class ErrorHandler extends Component\n{\n    /**\n     * @event Event an event that is triggered when the handler is called by shutdown function via [[handleFatalError()]].\n     * @since 2.0.46\n     */\n    public const EVENT_SHUTDOWN = 'shutdown';\n    /**\n     * @var bool whether to discard any existing page output before error display. Defaults to true.\n     */\n    public $discardExistingOutput = true;\n    /**\n     * @var int the size of the reserved memory. A portion of memory is pre-allocated so that\n     * when an out-of-memory issue occurs, the error handler is able to handle the error with\n     * the help of this reserved memory. If you set this value to be 0, no memory will be reserved.\n     * Defaults to 256KB.\n     */\n    public $memoryReserveSize = 262144;\n    /**\n     * @var \\Throwable|null the exception that is being handled currently.\n     */\n    public $exception;\n    /**\n     * @var bool if true - `handleException()` will finish script with `ExitCode::OK`.\n     * false - `ExitCode::UNSPECIFIED_ERROR`.\n     * @since 2.0.36\n     */\n    public $silentExitOnException;\n\n    /**\n     * @var string|null Used to reserve memory for fatal error handler.\n     */\n    private $_memoryReserve;\n    /**\n     * @var \\Throwable from HHVM error that stores backtrace\n     */\n    private $_hhvmException;\n    /**\n     * @var bool whether this instance has been registered using `register()`\n     */\n    private $_registered = false;\n    /**\n     * @var string|null the current working directory\n     */\n    private $_workingDirectory;\n\n\n    public function init()\n    {\n        $this->silentExitOnException = $this->silentExitOnException !== null ? $this->silentExitOnException : YII_ENV_TEST;\n        parent::init();\n    }\n\n    /**\n     * Register this error handler.\n     *\n     * @since 2.0.32 this will not do anything if the error handler was already registered\n     */\n    public function register()\n    {\n        if (!$this->_registered) {\n            ini_set('display_errors', false);\n            set_exception_handler([$this, 'handleException']);\n            if (defined('HHVM_VERSION')) {\n                set_error_handler([$this, 'handleHhvmError']);\n            } else {\n                set_error_handler([$this, 'handleError']);\n            }\n            if ($this->memoryReserveSize > 0) {\n                $this->_memoryReserve = str_repeat('x', $this->memoryReserveSize);\n            }\n            // to restore working directory in shutdown handler\n            if (PHP_SAPI !== 'cli') {\n                $this->_workingDirectory = getcwd();\n            }\n            register_shutdown_function([$this, 'handleFatalError']);\n            $this->_registered = true;\n        }\n    }\n\n    /**\n     * Unregisters this error handler by restoring the PHP error and exception handlers.\n     * @since 2.0.32 this will not do anything if the error handler was not registered\n     */\n    public function unregister()\n    {\n        if ($this->_registered) {\n            $this->_memoryReserve = null;\n            $this->_workingDirectory = null;\n            restore_error_handler();\n            restore_exception_handler();\n            $this->_registered = false;\n        }\n    }\n\n    /**\n     * Handles uncaught PHP exceptions.\n     *\n     * This method is implemented as a PHP exception handler.\n     *\n     * @param \\Throwable $exception the exception that is not caught\n     */\n    public function handleException($exception)\n    {\n        if ($exception instanceof ExitException) {\n            return;\n        }\n\n        $this->exception = $exception;\n\n        // disable error capturing to avoid recursive errors while handling exceptions\n        $this->unregister();\n\n        // set preventive HTTP status code to 500 in case error handling somehow fails and headers are sent\n        // HTTP exceptions will override this value in renderException()\n        if (PHP_SAPI !== 'cli') {\n            http_response_code(500);\n        }\n\n        try {\n            $this->logException($exception);\n            if ($this->discardExistingOutput) {\n                $this->clearOutput();\n            }\n            $this->renderException($exception);\n            if (!$this->silentExitOnException) {\n                \\Yii::getLogger()->flush(true);\n                if (defined('HHVM_VERSION')) {\n                    flush();\n                }\n                exit(1);\n            }\n        } catch (\\Exception $e) {\n            // an other exception could be thrown while displaying the exception\n            $this->handleFallbackExceptionMessage($e, $exception);\n        } catch (\\Throwable $e) {\n            // additional check for \\Throwable introduced in PHP 7\n            $this->handleFallbackExceptionMessage($e, $exception);\n        }\n\n        $this->exception = null;\n    }\n\n    /**\n     * Handles exception thrown during exception processing in [[handleException()]].\n     * @param \\Throwable $exception Exception that was thrown during main exception processing.\n     * @param \\Throwable $previousException Main exception processed in [[handleException()]].\n     * @since 2.0.11\n     */\n    protected function handleFallbackExceptionMessage($exception, $previousException)\n    {\n        $msg = \"An Error occurred while handling another error:\\n\";\n        $msg .= (string) $exception;\n        $msg .= \"\\nPrevious exception:\\n\";\n        $msg .= (string) $previousException;\n        if (YII_DEBUG) {\n            if (PHP_SAPI === 'cli') {\n                echo $msg . \"\\n\";\n            } else {\n                echo '<pre>' . htmlspecialchars($msg, ENT_QUOTES, Yii::$app->charset) . '</pre>';\n            }\n            $msg .= \"\\n\\$_SERVER = \" . VarDumper::export($_SERVER);\n        } else {\n            echo 'An internal server error occurred.';\n        }\n        error_log($msg);\n        if (defined('HHVM_VERSION')) {\n            flush();\n        }\n        exit(1);\n    }\n\n    /**\n     * Handles HHVM execution errors such as warnings and notices.\n     *\n     * This method is used as a HHVM error handler. It will store exception that will\n     * be used in fatal error handler\n     *\n     * @param int $code the level of the error raised.\n     * @param string $message the error message.\n     * @param string $file the filename that the error was raised in.\n     * @param int $line the line number the error was raised at.\n     * @param mixed $context\n     * @param mixed $backtrace trace of error\n     * @return bool whether the normal error handler continues.\n     *\n     * @throws ErrorException\n     * @since 2.0.6\n     */\n    public function handleHhvmError($code, $message, $file, $line, $context, $backtrace)\n    {\n        if ($this->handleError($code, $message, $file, $line)) {\n            return true;\n        }\n        if (E_ERROR & $code) {\n            $exception = new ErrorException($message, $code, $code, $file, $line);\n            $ref = new \\ReflectionProperty('\\Exception', 'trace');\n\n            // @link https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_reflectionsetaccessible\n            // @link https://wiki.php.net/rfc/make-reflection-setaccessible-no-op\n            if (PHP_VERSION_ID < 80100) {\n                $ref->setAccessible(true);\n            }\n\n            $ref->setValue($exception, $backtrace);\n            $this->_hhvmException = $exception;\n        }\n\n        return false;\n    }\n\n    /**\n     * Handles PHP execution errors such as warnings and notices.\n     *\n     * This method is used as a PHP error handler. It will simply raise an [[ErrorException]].\n     *\n     * @param int $code the level of the error raised.\n     * @param string $message the error message.\n     * @param string $file the filename that the error was raised in.\n     * @param int $line the line number the error was raised at.\n     * @return bool whether the normal error handler continues.\n     *\n     * @throws ErrorException\n     */\n    public function handleError($code, $message, $file, $line)\n    {\n        if (error_reporting() & $code) {\n            // load ErrorException manually here because autoloading them will not work\n            // when error occurs while autoloading a class\n            if (!class_exists('yii\\\\base\\\\ErrorException', false)) {\n                require_once __DIR__ . '/ErrorException.php';\n            }\n            $exception = new ErrorException($message, $code, $code, $file, $line);\n\n            if (PHP_VERSION_ID < 70400) {\n                // prior to PHP 7.4 we can't throw exceptions inside of __toString() - it will result a fatal error\n                $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);\n                array_shift($trace);\n                foreach ($trace as $frame) {\n                    if ($frame['function'] === '__toString') {\n                        $this->handleException($exception);\n                        if (defined('HHVM_VERSION')) {\n                            flush();\n                        }\n                        exit(1);\n                    }\n                }\n            }\n\n            throw $exception;\n        }\n\n        return false;\n    }\n\n    /**\n     * Handles fatal PHP errors.\n     */\n    public function handleFatalError()\n    {\n        $this->_memoryReserve = null;\n\n        if (!empty($this->_workingDirectory)) {\n            // fix working directory for some Web servers e.g. Apache\n            chdir($this->_workingDirectory);\n            // flush memory\n            $this->_workingDirectory = null;\n        }\n\n        $error = error_get_last();\n        if ($error === null) {\n            return;\n        }\n\n        // load ErrorException manually here because autoloading them will not work\n        // when error occurs while autoloading a class\n        if (!class_exists('yii\\\\base\\\\ErrorException', false)) {\n            require_once __DIR__ . '/ErrorException.php';\n        }\n        if (!ErrorException::isFatalError($error)) {\n            return;\n        }\n\n        if (!empty($this->_hhvmException)) {\n            $this->exception = $this->_hhvmException;\n        } else {\n            $this->exception = new ErrorException(\n                $error['message'],\n                $error['type'],\n                $error['type'],\n                $error['file'],\n                $error['line']\n            );\n        }\n        unset($error);\n\n        $this->logException($this->exception);\n\n        if ($this->discardExistingOutput) {\n            $this->clearOutput();\n        }\n        $this->renderException($this->exception);\n\n        // need to explicitly flush logs because exit() next will terminate the app immediately\n        Yii::getLogger()->flush(true);\n        if (defined('HHVM_VERSION')) {\n            flush();\n        }\n\n        $this->trigger(static::EVENT_SHUTDOWN);\n\n        // ensure it is called after user-defined shutdown functions\n        register_shutdown_function(function () {\n            exit(1);\n        });\n    }\n\n    /**\n     * Renders the exception.\n     * @param \\Throwable $exception the exception to be rendered.\n     */\n    abstract protected function renderException($exception);\n\n    /**\n     * Logs the given exception.\n     * @param \\Throwable $exception the exception to be logged\n     * @since 2.0.3 this method is now public.\n     */\n    public function logException($exception)\n    {\n        $category = get_class($exception);\n        if ($exception instanceof HttpException) {\n            $category = 'yii\\\\web\\\\HttpException:' . $exception->statusCode;\n        } elseif ($exception instanceof \\ErrorException) {\n            $category .= ':' . $exception->getSeverity();\n        }\n        Yii::error($exception, $category);\n    }\n\n    /**\n     * Removes all output echoed before calling this method.\n     */\n    public function clearOutput()\n    {\n        // the following manual level counting is to deal with zlib.output_compression set to On\n        for ($level = ob_get_level(); $level > 0; --$level) {\n            if (!@ob_end_clean()) {\n                ob_clean();\n            }\n        }\n    }\n\n    /**\n     * Converts an exception into a PHP error.\n     *\n     * This method can be used to convert exceptions inside of methods like `__toString()`\n     * to PHP errors because exceptions cannot be thrown inside of them.\n     * @param \\Throwable $exception the exception to convert to a PHP error.\n     * @return never\n     *\n     * @deprecated since 2.0.53. Use conditional exception throwing in `__toString()` methods instead.\n     * For PHP < 7.4: use `trigger_error()` directly with `convertExceptionToString()` method.\n     * For PHP >= 7.4: throw the exception directly as `__toString()` supports exceptions.\n     * This method will be removed in 2.2.0.\n     */\n    public static function convertExceptionToError($exception)\n    {\n        trigger_error(static::convertExceptionToString($exception), E_USER_ERROR);\n    }\n\n    /**\n     * Converts an exception into a simple string.\n     * @param \\Throwable $exception the exception being converted\n     * @return string the string representation of the exception.\n     */\n    public static function convertExceptionToString($exception)\n    {\n        if ($exception instanceof UserException) {\n            return \"{$exception->getName()}: {$exception->getMessage()}\";\n        }\n\n        if (YII_DEBUG) {\n            return static::convertExceptionToVerboseString($exception);\n        }\n\n        return 'An internal server error occurred.';\n    }\n\n    /**\n     * Converts an exception into a string that has verbose information about the exception and its trace.\n     * @param \\Throwable $exception the exception being converted\n     * @return string the string representation of the exception.\n     *\n     * @since 2.0.14\n     */\n    public static function convertExceptionToVerboseString($exception)\n    {\n        if ($exception instanceof Exception) {\n            $message = \"Exception ({$exception->getName()})\";\n        } elseif ($exception instanceof ErrorException) {\n            $message = (string)$exception->getName();\n        } else {\n            $message = 'Exception';\n        }\n        $message .= \" '\" . get_class($exception) . \"' with message '{$exception->getMessage()}' \\n\\nin \"\n            . $exception->getFile() . ':' . $exception->getLine() . \"\\n\\n\"\n            . \"Stack trace:\\n\" . $exception->getTraceAsString();\n\n        return $message;\n    }\n}\n"
  },
  {
    "path": "framework/base/Event.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\nuse yii\\helpers\\StringHelper;\n\n/**\n * Event is the base class for all event classes.\n *\n * It encapsulates the parameters associated with an event.\n * The [[sender]] property describes who raises the event.\n * And the [[handled]] property indicates if the event is handled.\n * If an event handler sets [[handled]] to be `true`, the rest of the\n * uninvoked handlers will no longer be called to handle the event.\n *\n * Additionally, when attaching an event handler, extra data may be passed\n * and be available via the [[data]] property when the event handler is invoked.\n *\n * For more details and usage information on Event, see the [guide article on events](guide:concept-events).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Event extends BaseObject\n{\n    /**\n     * @var string the event name. This property is set by [[Component::trigger()]] and [[trigger()]].\n     * Event handlers may use this property to check what event it is handling.\n     */\n    public $name;\n    /**\n     * @var object|null the sender of this event. If not set, this property will be\n     * set as the object whose `trigger()` method is called.\n     * This property may also be a `null` when this event is a\n     * class-level event which is triggered in a static context.\n     */\n    public $sender;\n    /**\n     * @var bool whether the event is handled. Defaults to `false`.\n     * When a handler sets this to be `true`, the event processing will stop and\n     * ignore the rest of the uninvoked event handlers.\n     */\n    public $handled = false;\n    /**\n     * @var mixed the data that is passed to [[Component::on()]] when attaching an event handler.\n     * Note that this varies according to which event handler is currently executing.\n     */\n    public $data;\n\n    /**\n     * @var array contains all globally registered event handlers.\n     */\n    private static $_events = [];\n    /**\n     * @var array the globally registered event handlers attached for wildcard patterns (event name wildcard => handlers)\n     * @since 2.0.14\n     */\n    private static $_eventWildcards = [];\n\n\n    /**\n     * Attaches an event handler to a class-level event.\n     *\n     * When a class-level event is triggered, event handlers attached\n     * to that class and all parent classes will be invoked.\n     *\n     * For example, the following code attaches an event handler to `ActiveRecord`'s\n     * `afterInsert` event:\n     *\n     * ```\n     * Event::on(ActiveRecord::class, ActiveRecord::EVENT_AFTER_INSERT, function ($event) {\n     *     Yii::trace(get_class($event->sender) . ' is inserted.');\n     * });\n     * ```\n     *\n     * The handler will be invoked for EVERY successful ActiveRecord insertion.\n     *\n     * Since 2.0.14 you can specify either class name or event name as a wildcard pattern:\n     *\n     * ```\n     * Event::on('app\\models\\db\\*', '*Insert', function ($event) {\n     *     Yii::trace(get_class($event->sender) . ' is inserted.');\n     * });\n     * ```\n     *\n     * For more details about how to declare an event handler, please refer to [[Component::on()]].\n     *\n     * @param string $class the fully qualified class name to which the event handler needs to attach.\n     * @param string $name the event name.\n     * @param callable $handler the event handler.\n     * @param mixed $data the data to be passed to the event handler when the event is triggered.\n     * When the event handler is invoked, this data can be accessed via [[Event::data]].\n     * @param bool $append whether to append new event handler to the end of the existing\n     * handler list. If `false`, the new handler will be inserted at the beginning of the existing\n     * handler list.\n     * @see off()\n     */\n    public static function on($class, $name, $handler, $data = null, $append = true)\n    {\n        $class = ltrim($class, '\\\\');\n\n        if (strpos($class, '*') !== false || strpos($name, '*') !== false) {\n            if ($append || empty(self::$_eventWildcards[$name][$class])) {\n                self::$_eventWildcards[$name][$class][] = [$handler, $data];\n            } else {\n                array_unshift(self::$_eventWildcards[$name][$class], [$handler, $data]);\n            }\n            return;\n        }\n\n        if ($append || empty(self::$_events[$name][$class])) {\n            self::$_events[$name][$class][] = [$handler, $data];\n        } else {\n            array_unshift(self::$_events[$name][$class], [$handler, $data]);\n        }\n    }\n\n    /**\n     * Detaches an event handler from a class-level event.\n     *\n     * This method is the opposite of [[on()]].\n     *\n     * Note: in case wildcard pattern is passed for class name or event name, only the handlers registered with this\n     * wildcard will be removed, while handlers registered with plain names matching this wildcard will remain.\n     *\n     * @param string $class the fully qualified class name from which the event handler needs to be detached.\n     * @param string $name the event name.\n     * @param callable|null $handler the event handler to be removed.\n     * If it is `null`, all handlers attached to the named event will be removed.\n     * @return bool whether a handler is found and detached.\n     * @see on()\n     */\n    public static function off($class, $name, $handler = null)\n    {\n        $class = ltrim($class, '\\\\');\n        if (empty(self::$_events[$name][$class]) && empty(self::$_eventWildcards[$name][$class])) {\n            return false;\n        }\n        if ($handler === null) {\n            unset(self::$_events[$name][$class]);\n            unset(self::$_eventWildcards[$name][$class]);\n            return true;\n        }\n\n        // plain event names\n        if (isset(self::$_events[$name][$class])) {\n            $removed = false;\n            foreach (self::$_events[$name][$class] as $i => $event) {\n                if ($event[0] === $handler) {\n                    unset(self::$_events[$name][$class][$i]);\n                    $removed = true;\n                }\n            }\n            if ($removed) {\n                self::$_events[$name][$class] = array_values(self::$_events[$name][$class]);\n                return true;\n            }\n        }\n\n        // wildcard event names\n        $removed = false;\n        if (isset(self::$_eventWildcards[$name][$class])) {\n            foreach (self::$_eventWildcards[$name][$class] as $i => $event) {\n                if ($event[0] === $handler) {\n                    unset(self::$_eventWildcards[$name][$class][$i]);\n                    $removed = true;\n                }\n            }\n            if ($removed) {\n                self::$_eventWildcards[$name][$class] = array_values(self::$_eventWildcards[$name][$class]);\n                // remove empty wildcards to save future redundant regex checks :\n                if (empty(self::$_eventWildcards[$name][$class])) {\n                    unset(self::$_eventWildcards[$name][$class]);\n                    if (empty(self::$_eventWildcards[$name])) {\n                        unset(self::$_eventWildcards[$name]);\n                    }\n                }\n            }\n        }\n\n        return $removed;\n    }\n\n    /**\n     * Detaches all registered class-level event handlers.\n     * @see on()\n     * @see off()\n     * @since 2.0.10\n     */\n    public static function offAll()\n    {\n        self::$_events = [];\n        self::$_eventWildcards = [];\n    }\n\n    /**\n     * Returns a value indicating whether there is any handler attached to the specified class-level event.\n     * Note that this method will also check all parent classes to see if there is any handler attached\n     * to the named event.\n     * @param string|object $class the object or the fully qualified class name specifying the class-level event.\n     * @param string $name the event name.\n     * @return bool whether there is any handler attached to the event.\n     */\n    public static function hasHandlers($class, $name)\n    {\n        if (empty(self::$_eventWildcards) && empty(self::$_events[$name])) {\n            return false;\n        }\n\n        if (is_object($class)) {\n            $class = get_class($class);\n        } else {\n            $class = ltrim($class, '\\\\');\n        }\n\n        $classes = array_merge(\n            [$class],\n            class_parents($class, true),\n            class_implements($class, true)\n        );\n\n        // regular events\n        foreach ($classes as $className) {\n            if (!empty(self::$_events[$name][$className])) {\n                return true;\n            }\n        }\n\n        // wildcard events\n        foreach (self::$_eventWildcards as $nameWildcard => $classHandlers) {\n            if (!StringHelper::matchWildcard($nameWildcard, $name, ['escape' => false])) {\n                continue;\n            }\n            foreach ($classHandlers as $classWildcard => $handlers) {\n                if (empty($handlers)) {\n                    continue;\n                }\n                foreach ($classes as $className) {\n                    if (StringHelper::matchWildcard($classWildcard, $className, ['escape' => false])) {\n                        return true;\n                    }\n                }\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * Triggers a class-level event.\n     * This method will cause invocation of event handlers that are attached to the named event\n     * for the specified class and all its parent classes.\n     * @param string|object $class the object or the fully qualified class name specifying the class-level event.\n     * @param string $name the event name.\n     * @param Event|null $event the event parameter. If not set, a default [[Event]] object will be created.\n     */\n    public static function trigger($class, $name, $event = null)\n    {\n        $wildcardEventHandlers = [];\n        foreach (self::$_eventWildcards as $nameWildcard => $classHandlers) {\n            if (!StringHelper::matchWildcard($nameWildcard, $name)) {\n                continue;\n            }\n            $wildcardEventHandlers = array_merge($wildcardEventHandlers, $classHandlers);\n        }\n\n        if (empty(self::$_events[$name]) && empty($wildcardEventHandlers)) {\n            return;\n        }\n\n        if ($event === null) {\n            $event = new static();\n        }\n        $event->handled = false;\n        $event->name = $name;\n\n        if (is_object($class)) {\n            if ($event->sender === null) {\n                $event->sender = $class;\n            }\n            $class = get_class($class);\n        } else {\n            $class = ltrim($class, '\\\\');\n        }\n\n        $classes = array_merge(\n            [$class],\n            class_parents($class, true),\n            class_implements($class, true)\n        );\n\n        foreach ($classes as $class) {\n            $eventHandlers = [];\n            foreach ($wildcardEventHandlers as $classWildcard => $handlers) {\n                if (StringHelper::matchWildcard($classWildcard, $class, ['escape' => false])) {\n                    $eventHandlers = array_merge($eventHandlers, $handlers);\n                    unset($wildcardEventHandlers[$classWildcard]);\n                }\n            }\n\n            if (!empty(self::$_events[$name][$class])) {\n                $eventHandlers = array_merge($eventHandlers, self::$_events[$name][$class]);\n            }\n\n            foreach ($eventHandlers as $handler) {\n                $event->data = $handler[1];\n                call_user_func($handler[0], $event);\n                if ($event->handled) {\n                    return;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "framework/base/Exception.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * Exception represents a generic exception for all purposes.\n *\n * For more details and usage information on Exception, see the [guide article on handling errors](guide:runtime-handling-errors).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Exception extends \\Exception\n{\n    /**\n     * @return string the user-friendly name of this exception\n     */\n    public function getName()\n    {\n        return 'Exception';\n    }\n}\n"
  },
  {
    "path": "framework/base/ExitException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * ExitException represents a normal termination of an application.\n *\n * Do not catch ExitException. Yii will handle this exception to terminate the application gracefully.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass ExitException extends \\Exception\n{\n    /**\n     * @var int the exit status code\n     */\n    public $statusCode;\n\n\n    /**\n     * Constructor.\n     * @param int $status the exit status code\n     * @param string $message error message\n     * @param int $code error code\n     * @param \\Throwable|null $previous The previous exception used for the exception chaining.\n     */\n    public function __construct($status = 0, $message = null, $code = 0, $previous = null)\n    {\n        $this->statusCode = $status;\n        if ($previous === null) {\n            parent::__construct((string)$message, $code);\n        } else {\n            parent::__construct((string)$message, $code, $previous);\n        }\n    }\n}\n"
  },
  {
    "path": "framework/base/InlineAction.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\nuse Yii;\n\n/**\n * InlineAction represents an action that is defined as a controller method.\n *\n * The name of the controller method is available via [[actionMethod]] which\n * is set by the [[controller]] who creates this action.\n *\n * For more details and usage information on InlineAction, see the [guide article on actions](guide:structure-controllers).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Controller = Controller\n * @extends Action<T>\n */\nclass InlineAction extends Action\n{\n    /**\n     * @var string the controller method that this inline action is associated with\n     */\n    public $actionMethod;\n\n\n    /**\n     * @param string $id the ID of this action\n     * @param T $controller the controller that owns this action\n     * @param string $actionMethod the controller method that this inline action is associated with\n     * @param array<string, mixed> $config name-value pairs that will be used to initialize the object properties\n     */\n    public function __construct($id, $controller, $actionMethod, $config = [])\n    {\n        $this->actionMethod = $actionMethod;\n        parent::__construct($id, $controller, $config);\n    }\n\n    /**\n     * Runs this action with the specified parameters.\n     * This method is mainly invoked by the controller.\n     * @param array $params action parameters\n     * @return mixed the result of the action\n     */\n    public function runWithParams($params)\n    {\n        $args = $this->controller->bindActionParams($this, $params);\n        Yii::debug('Running action: ' . get_class($this->controller) . '::' . $this->actionMethod . '()', __METHOD__);\n        if (Yii::$app->requestedParams === null) {\n            Yii::$app->requestedParams = $args;\n        }\n\n        return call_user_func_array([$this->controller, $this->actionMethod], $args);\n    }\n}\n"
  },
  {
    "path": "framework/base/InvalidArgumentException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * InvalidArgumentException represents an exception caused by invalid arguments passed to a method.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0.14\n */\nclass InvalidArgumentException extends InvalidParamException\n{\n    /**\n     * @return string the user-friendly name of this exception\n     */\n    public function getName()\n    {\n        return 'Invalid Argument';\n    }\n}\n"
  },
  {
    "path": "framework/base/InvalidCallException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * InvalidCallException represents an exception caused by calling a method in a wrong way.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass InvalidCallException extends \\BadMethodCallException\n{\n    /**\n     * @return string the user-friendly name of this exception\n     */\n    public function getName()\n    {\n        return 'Invalid Call';\n    }\n}\n"
  },
  {
    "path": "framework/base/InvalidConfigException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * InvalidConfigException represents an exception caused by incorrect object configuration.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass InvalidConfigException extends Exception\n{\n    /**\n     * @return string the user-friendly name of this exception\n     */\n    public function getName()\n    {\n        return 'Invalid Configuration';\n    }\n}\n"
  },
  {
    "path": "framework/base/InvalidParamException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * InvalidParamException represents an exception caused by invalid parameters passed to a method.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n * @deprecated since 2.0.14. Use [[InvalidArgumentException]] instead.\n */\nclass InvalidParamException extends \\BadMethodCallException\n{\n    /**\n     * @return string the user-friendly name of this exception\n     */\n    public function getName()\n    {\n        return 'Invalid Parameter';\n    }\n}\n"
  },
  {
    "path": "framework/base/InvalidRouteException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * InvalidRouteException represents an exception caused by an invalid route.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass InvalidRouteException extends UserException\n{\n    /**\n     * @return string the user-friendly name of this exception\n     */\n    public function getName()\n    {\n        return 'Invalid Route';\n    }\n}\n"
  },
  {
    "path": "framework/base/InvalidValueException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * InvalidValueException represents an exception caused by a function returning a value of unexpected type.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass InvalidValueException extends \\UnexpectedValueException\n{\n    /**\n     * @return string the user-friendly name of this exception\n     */\n    public function getName()\n    {\n        return 'Invalid Return Value';\n    }\n}\n"
  },
  {
    "path": "framework/base/Model.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\nuse ArrayAccess;\nuse ArrayIterator;\nuse ArrayObject;\nuse IteratorAggregate;\nuse ReflectionClass;\nuse Yii;\nuse yii\\helpers\\Inflector;\nuse yii\\validators\\RequiredValidator;\nuse yii\\validators\\Validator;\n\n/**\n * Model is the base class for data models.\n *\n * Model implements the following commonly used features:\n *\n * - attribute declaration: by default, every public class member is considered as\n *   a model attribute\n * - attribute labels: each attribute may be associated with a label for display purpose\n * - massive attribute assignment\n * - scenario-based validation\n *\n * Model also raises the following events when performing data validation:\n *\n * - [[EVENT_BEFORE_VALIDATE]]: an event raised at the beginning of [[validate()]]\n * - [[EVENT_AFTER_VALIDATE]]: an event raised at the end of [[validate()]]\n *\n * You may directly use Model to store model data, or extend it with customization.\n *\n * For more details and usage information on Model, see the [guide article on models](guide:structure-models).\n *\n * @property-read array $errors Errors for all attributes. This is a two-dimensional\n * array of errors for all attributes, similar to the following:\n *\n * ```\n * [\n *     'username' => [\n *         'Username is required.',\n *         'Username must contain only word characters.',\n *     ],\n *     'email' => [\n *         'Email address is invalid.',\n *     ]\n * ]\n * ```\n *\n * Empty array if no errors.\n * @property-read Validator[] $activeValidators The validators applicable to the current [[scenario]].\n * @property array<string, mixed> $attributes Attribute values (name => value). Note that the type of this\n * property differs in getter and setter. See [[getAttributes()]] and [[setAttributes()]] for details.\n * @property-read array<string, string> $firstErrors The first errors. The array keys are the attribute names,\n * and the array values are the corresponding error messages. An empty array will be returned if there is no\n * error.\n * @property-read ArrayIterator<string, mixed> $iterator An iterator for traversing the items in the list.\n * @property string $scenario The scenario that this model is in. Defaults to [[SCENARIO_DEFAULT]].\n * @property-read ArrayObject<int, Validator>|Validator[] $validators All the validators declared in the\n * model.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @implements IteratorAggregate<string, mixed>\n * @implements ArrayAccess<string, mixed>\n */\nclass Model extends Component implements StaticInstanceInterface, IteratorAggregate, ArrayAccess, Arrayable\n{\n    use ArrayableTrait;\n    use StaticInstanceTrait;\n\n    /**\n     * The name of the default scenario.\n     */\n    public const SCENARIO_DEFAULT = 'default';\n    /**\n     * @event ModelEvent an event raised at the beginning of [[validate()]]. You may set\n     * [[ModelEvent::isValid]] to be false to stop the validation.\n     */\n    public const EVENT_BEFORE_VALIDATE = 'beforeValidate';\n    /**\n     * @event Event an event raised at the end of [[validate()]]\n     */\n    public const EVENT_AFTER_VALIDATE = 'afterValidate';\n    /**\n     * @var array|null validation errors (attribute name => array of errors)\n     */\n    private $_errors;\n    /**\n     * @var ArrayObject<int, Validator>|null list of validators\n     */\n    private $_validators;\n    /**\n     * @var string current scenario\n     */\n    private $_scenario = self::SCENARIO_DEFAULT;\n\n\n    /**\n     * Returns the validation rules for attributes.\n     *\n     * Validation rules are used by [[validate()]] to check if attribute values are valid.\n     * Child classes may override this method to declare different validation rules.\n     *\n     * Each rule is an array with the following structure:\n     *\n     * ```\n     * [\n     *     ['attribute1', 'attribute2'],\n     *     'validator type',\n     *     'on' => ['scenario1', 'scenario2'],\n     *     //...other parameters...\n     * ]\n     * ```\n     *\n     * where\n     *\n     *  - attribute list: required, specifies the attributes array to be validated, for single attribute you can pass a string;\n     *  - validator type: required, specifies the validator to be used. It can be a built-in validator name,\n     *    a method name of the model class, an anonymous function, or a validator class name.\n     *  - on: optional, specifies the [[scenario|scenarios]] array in which the validation\n     *    rule can be applied. If this option is not set, the rule will apply to all scenarios.\n     *  - additional name-value pairs can be specified to initialize the corresponding validator properties.\n     *    Please refer to individual validator class API for possible properties.\n     *\n     * A validator can be either an object of a class extending [[Validator]], or a model class method\n     * (called *inline validator*) that has the following signature:\n     *\n     * ```\n     * // $params refers to validation parameters given in the rule\n     * function validatorName($attribute, $params)\n     * ```\n     *\n     * In the above `$attribute` refers to the attribute currently being validated while `$params` contains an array of\n     * validator configuration options such as `max` in case of `string` validator. The value of the attribute currently being validated\n     * can be accessed as `$this->$attribute`. Note the `$` before `attribute`; this is taking the value of the variable\n     * `$attribute` and using it as the name of the property to access.\n     *\n     * Yii also provides a set of [[Validator::builtInValidators|built-in validators]].\n     * Each one has an alias name which can be used when specifying a validation rule.\n     *\n     * Below are some examples:\n     *\n     * ```\n     * [\n     *     // built-in \"required\" validator\n     *     [['username', 'password'], 'required'],\n     *     // built-in \"string\" validator customized with \"min\" and \"max\" properties\n     *     ['username', 'string', 'min' => 3, 'max' => 12],\n     *     // built-in \"compare\" validator that is used in \"register\" scenario only\n     *     ['password', 'compare', 'compareAttribute' => 'password2', 'on' => 'register'],\n     *     // an inline validator defined via the \"authenticate()\" method in the model class\n     *     ['password', 'authenticate', 'on' => 'login'],\n     *     // a validator of class \"DateRangeValidator\"\n     *     ['dateRange', 'DateRangeValidator'],\n     * ];\n     * ```\n     *\n     * Note, in order to inherit rules defined in the parent class, a child class needs to\n     * merge the parent rules with child rules using functions such as `array_merge()`.\n     *\n     * @return array<int, array<array-key, mixed>|Validator> validation rules\n     * @see scenarios()\n     */\n    public function rules()\n    {\n        return [];\n    }\n\n    /**\n     * Returns a list of scenarios and the corresponding active attributes.\n     *\n     * An active attribute is one that is subject to validation in the current scenario.\n     * The returned array should be in the following format:\n     *\n     * ```\n     * [\n     *     'scenario1' => ['attribute11', 'attribute12', ...],\n     *     'scenario2' => ['attribute21', 'attribute22', ...],\n     *     ...\n     * ]\n     * ```\n     *\n     * By default, an active attribute is considered safe and can be massively assigned.\n     * If an attribute should NOT be massively assigned (thus considered unsafe),\n     * please prefix the attribute with an exclamation character (e.g. `'!rank'`).\n     *\n     * The default implementation of this method will return all scenarios found in the [[rules()]]\n     * declaration. A special scenario named [[SCENARIO_DEFAULT]] will contain all attributes\n     * found in the [[rules()]]. Each scenario will be associated with the attributes that\n     * are being validated by the validation rules that apply to the scenario.\n     *\n     * @return array<string, string[]> a list of scenarios and the corresponding active attributes.\n     */\n    public function scenarios()\n    {\n        $scenarios = [self::SCENARIO_DEFAULT => []];\n        foreach ($this->getValidators() as $validator) {\n            foreach ($validator->on as $scenario) {\n                $scenarios[$scenario] = [];\n            }\n            foreach ($validator->except as $scenario) {\n                $scenarios[$scenario] = [];\n            }\n        }\n        $names = array_keys($scenarios);\n\n        foreach ($this->getValidators() as $validator) {\n            if (empty($validator->on) && empty($validator->except)) {\n                foreach ($names as $name) {\n                    foreach ($validator->attributes as $attribute) {\n                        $scenarios[$name][$attribute] = true;\n                    }\n                }\n            } elseif (empty($validator->on)) {\n                foreach ($names as $name) {\n                    if (!in_array($name, $validator->except, true)) {\n                        foreach ($validator->attributes as $attribute) {\n                            $scenarios[$name][$attribute] = true;\n                        }\n                    }\n                }\n            } else {\n                foreach ($validator->on as $name) {\n                    foreach ($validator->attributes as $attribute) {\n                        $scenarios[$name][$attribute] = true;\n                    }\n                }\n            }\n        }\n\n        foreach ($scenarios as $scenario => $attributes) {\n            if (!empty($attributes)) {\n                $scenarios[$scenario] = array_keys($attributes);\n            }\n        }\n\n        return $scenarios;\n    }\n\n    /**\n     * Returns the form name that this model class should use.\n     *\n     * The form name is mainly used by [[\\yii\\widgets\\ActiveForm]] to determine how to name\n     * the input fields for the attributes in a model. If the form name is \"A\" and an attribute\n     * name is \"b\", then the corresponding input name would be \"A[b]\". If the form name is\n     * an empty string, then the input name would be \"b\".\n     *\n     * The purpose of the above naming schema is that for forms which contain multiple different models,\n     * the attributes of each model are grouped in sub-arrays of the POST-data and it is easier to\n     * differentiate between them.\n     *\n     * By default, this method returns the model class name (without the namespace part)\n     * as the form name. You may override it when the model is used in different forms.\n     *\n     * @return string the form name of this model class.\n     * @see load()\n     * @throws InvalidConfigException when form is defined with anonymous class and `formName()` method is\n     * not overridden.\n     */\n    public function formName()\n    {\n        $reflector = new ReflectionClass($this);\n        if (PHP_VERSION_ID >= 70000 && $reflector->isAnonymous()) {\n            throw new InvalidConfigException('The \"formName()\" method should be explicitly defined for anonymous models');\n        }\n        return $reflector->getShortName();\n    }\n\n    /**\n     * Returns the list of attribute names.\n     *\n     * By default, this method returns all public non-static properties of the class.\n     * You may override this method to change the default behavior.\n     *\n     * @return string[] list of attribute names.\n     */\n    public function attributes()\n    {\n        $class = new ReflectionClass($this);\n        $names = [];\n        foreach ($class->getProperties(\\ReflectionProperty::IS_PUBLIC) as $property) {\n            if (!$property->isStatic()) {\n                $names[] = $property->getName();\n            }\n        }\n\n        return $names;\n    }\n\n    /**\n     * Returns the attribute labels.\n     *\n     * Attribute labels are mainly used for display purpose. For example, given an attribute\n     * `firstName`, we can declare a label `First Name` which is more user-friendly and can\n     * be displayed to end users.\n     *\n     * By default an attribute label is generated using [[generateAttributeLabel()]].\n     * This method allows you to explicitly specify attribute labels.\n     *\n     * Note, in order to inherit labels defined in the parent class, a child class needs to\n     * merge the parent labels with child labels using functions such as `array_merge()`.\n     *\n     * @return array<string, string> attribute labels (name => label)\n     * @see generateAttributeLabel()\n     */\n    public function attributeLabels()\n    {\n        return [];\n    }\n\n    /**\n     * Returns the attribute hints.\n     *\n     * Attribute hints are mainly used for display purpose. For example, given an attribute\n     * `isPublic`, we can declare a hint `Whether the post should be visible for not logged in users`,\n     * which provides user-friendly description of the attribute meaning and can be displayed to end users.\n     *\n     * Unlike label hint will not be generated, if its explicit declaration is omitted.\n     *\n     * Note, in order to inherit hints defined in the parent class, a child class needs to\n     * merge the parent hints with child hints using functions such as `array_merge()`.\n     *\n     * @return array<string, string> attribute hints (name => hint)\n     * @since 2.0.4\n     */\n    public function attributeHints()\n    {\n        return [];\n    }\n\n    /**\n     * Performs the data validation.\n     *\n     * This method executes the validation rules applicable to the current [[scenario]].\n     * The following criteria are used to determine whether a rule is currently applicable:\n     *\n     * - the rule must be associated with the attributes relevant to the current scenario;\n     * - the rules must be effective for the current scenario.\n     *\n     * This method will call [[beforeValidate()]] and [[afterValidate()]] before and\n     * after the actual validation, respectively. If [[beforeValidate()]] returns false,\n     * the validation will be cancelled and [[afterValidate()]] will not be called.\n     *\n     * Errors found during the validation can be retrieved via [[getErrors()]],\n     * [[getFirstErrors()]] and [[getFirstError()]].\n     *\n     * @param string[]|string|null $attributeNames attribute name or list of attribute names\n     * that should be validated. If this parameter is empty, it means any attribute listed in\n     * the applicable validation rules should be validated.\n     * @param bool $clearErrors whether to call [[clearErrors()]] before performing validation\n     * @return bool whether the validation is successful without any error.\n     * @throws InvalidArgumentException if the current scenario is unknown.\n     */\n    public function validate($attributeNames = null, $clearErrors = true)\n    {\n        if ($clearErrors) {\n            $this->clearErrors();\n        }\n\n        if (!$this->beforeValidate()) {\n            return false;\n        }\n\n        $scenarios = $this->scenarios();\n        $scenario = $this->getScenario();\n        if (!isset($scenarios[$scenario])) {\n            throw new InvalidArgumentException(\"Unknown scenario: $scenario\");\n        }\n\n        if ($attributeNames === null) {\n            $attributeNames = $this->activeAttributes();\n        }\n\n        $attributeNames = (array)$attributeNames;\n\n        foreach ($this->getActiveValidators() as $validator) {\n            $validator->validateAttributes($this, $attributeNames);\n        }\n        $this->afterValidate();\n\n        return !$this->hasErrors();\n    }\n\n    /**\n     * This method is invoked before validation starts.\n     * The default implementation raises a `beforeValidate` event.\n     * You may override this method to do preliminary checks before validation.\n     * Make sure the parent implementation is invoked so that the event can be raised.\n     * @return bool whether the validation should be executed. Defaults to true.\n     * If false is returned, the validation will stop and the model is considered invalid.\n     */\n    public function beforeValidate()\n    {\n        $event = new ModelEvent();\n        $this->trigger(self::EVENT_BEFORE_VALIDATE, $event);\n\n        return $event->isValid;\n    }\n\n    /**\n     * This method is invoked after validation ends.\n     * The default implementation raises an `afterValidate` event.\n     * You may override this method to do postprocessing after validation.\n     * Make sure the parent implementation is invoked so that the event can be raised.\n     */\n    public function afterValidate()\n    {\n        $this->trigger(self::EVENT_AFTER_VALIDATE);\n    }\n\n    /**\n     * Returns all the validators declared in [[rules()]].\n     *\n     * This method differs from [[getActiveValidators()]] in that the latter\n     * only returns the validators applicable to the current [[scenario]].\n     *\n     * Because this method returns an ArrayObject object, you may\n     * manipulate it by inserting or removing validators (useful in model behaviors).\n     * For example,\n     *\n     * ```\n     * $model->validators[] = $newValidator;\n     * ```\n     *\n     * @return ArrayObject<int, Validator>|Validator[] all the validators declared in the model.\n     */\n    public function getValidators()\n    {\n        if ($this->_validators === null) {\n            $this->_validators = $this->createValidators();\n        }\n\n        return $this->_validators;\n    }\n\n    /**\n     * Returns the validators applicable to the current [[scenario]].\n     * @param string|null $attribute the name of the attribute whose applicable validators should be returned.\n     * If this is null, the validators for ALL attributes in the model will be returned.\n     * @return Validator[] the validators applicable to the current [[scenario]].\n     */\n    public function getActiveValidators($attribute = null)\n    {\n        $activeAttributes = $this->activeAttributes();\n        if ($attribute !== null && !in_array($attribute, $activeAttributes, true)) {\n            return [];\n        }\n        $scenario = $this->getScenario();\n        $validators = [];\n        foreach ($this->getValidators() as $validator) {\n            if ($attribute === null) {\n                $validatorAttributes = $validator->getValidationAttributes($activeAttributes);\n                $attributeValid = !empty($validatorAttributes);\n            } else {\n                $attributeValid = in_array($attribute, $validator->getValidationAttributes($attribute), true);\n            }\n            if ($attributeValid && $validator->isActive($scenario)) {\n                $validators[] = $validator;\n            }\n        }\n\n        return $validators;\n    }\n\n    /**\n     * Creates validator objects based on the validation rules specified in [[rules()]].\n     * Unlike [[getValidators()]], each time this method is called, a new list of validators will be returned.\n     * @return ArrayObject<int, Validator> validators\n     * @throws InvalidConfigException if any validation rule configuration is invalid\n     */\n    public function createValidators()\n    {\n        $validators = new ArrayObject();\n        foreach ($this->rules() as $rule) {\n            if ($rule instanceof Validator) {\n                $validators->append($rule);\n            } elseif (is_array($rule) && isset($rule[0], $rule[1])) { // attributes, validator type\n                $validator = Validator::createValidator($rule[1], $this, (array) $rule[0], array_slice($rule, 2));\n                $validators->append($validator);\n            } else {\n                throw new InvalidConfigException('Invalid validation rule: a rule must specify both attribute names and validator type.');\n            }\n        }\n\n        return $validators;\n    }\n\n    /**\n     * Returns a value indicating whether the attribute is required.\n     * This is determined by checking if the attribute is associated with a\n     * [[\\yii\\validators\\RequiredValidator|required]] validation rule in the\n     * current [[scenario]].\n     *\n     * Note that when the validator has a conditional validation applied using\n     * [[\\yii\\validators\\RequiredValidator::$when|$when]] this method will return\n     * `false` regardless of the `when` condition because it may be called be\n     * before the model is loaded with data.\n     *\n     * @param string $attribute attribute name\n     * @return bool whether the attribute is required\n     */\n    public function isAttributeRequired($attribute)\n    {\n        foreach ($this->getActiveValidators($attribute) as $validator) {\n            if ($validator instanceof RequiredValidator && $validator->when === null) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * Returns a value indicating whether the attribute is safe for massive assignments.\n     * @param string $attribute attribute name\n     * @return bool whether the attribute is safe for massive assignments\n     * @see safeAttributes()\n     */\n    public function isAttributeSafe($attribute)\n    {\n        return in_array($attribute, $this->safeAttributes(), true);\n    }\n\n    /**\n     * Returns a value indicating whether the attribute is active in the current scenario.\n     * @param string $attribute attribute name\n     * @return bool whether the attribute is active in the current scenario\n     * @see activeAttributes()\n     */\n    public function isAttributeActive($attribute)\n    {\n        return in_array($attribute, $this->activeAttributes(), true);\n    }\n\n    /**\n     * Returns the text label for the specified attribute.\n     * @param string $attribute the attribute name\n     * @return string the attribute label\n     * @see generateAttributeLabel()\n     * @see attributeLabels()\n     */\n    public function getAttributeLabel($attribute)\n    {\n        $labels = $this->attributeLabels();\n        return isset($labels[$attribute]) ? $labels[$attribute] : $this->generateAttributeLabel($attribute);\n    }\n\n    /**\n     * Returns the text hint for the specified attribute.\n     * @param string $attribute the attribute name\n     * @return string the attribute hint\n     * @see attributeHints()\n     * @since 2.0.4\n     */\n    public function getAttributeHint($attribute)\n    {\n        $hints = $this->attributeHints();\n        return isset($hints[$attribute]) ? $hints[$attribute] : '';\n    }\n\n    /**\n     * Returns a value indicating whether there is any validation error.\n     * @param string|null $attribute attribute name. Use null to check all attributes.\n     * @return bool whether there is any error.\n     */\n    public function hasErrors($attribute = null)\n    {\n        return $attribute === null ? !empty($this->_errors) : isset($this->_errors[$attribute]);\n    }\n\n    /**\n     * Returns the errors for all attributes or a single attribute.\n     * @param string|null $attribute attribute name. Use null to retrieve errors for all attributes.\n     * @return array<string, string[]> errors for all attributes or the specified attribute. Empty array is returned if no error.\n     * Note that when returning errors for all attributes, the result is a two-dimensional array, like the following:\n     *\n     * ```\n     * [\n     *     'username' => [\n     *         'Username is required.',\n     *         'Username must contain only word characters.',\n     *     ],\n     *     'email' => [\n     *         'Email address is invalid.',\n     *     ]\n     * ]\n     * ```\n     *\n     * @see getFirstErrors()\n     * @see getFirstError()\n     */\n    public function getErrors($attribute = null)\n    {\n        if ($attribute === null) {\n            return $this->_errors === null ? [] : $this->_errors;\n        }\n\n        return isset($this->_errors[$attribute]) ? $this->_errors[$attribute] : [];\n    }\n\n    /**\n     * Returns the first error of every attribute in the model.\n     * @return array<string, string> the first errors. The array keys are the attribute names, and the array\n     * values are the corresponding error messages. An empty array will be returned if there is no error.\n     * @see getErrors()\n     * @see getFirstError()\n     */\n    public function getFirstErrors()\n    {\n        if (empty($this->_errors)) {\n            return [];\n        }\n\n        $errors = [];\n        foreach ($this->_errors as $name => $es) {\n            if (!empty($es)) {\n                $errors[$name] = reset($es);\n            }\n        }\n\n        return $errors;\n    }\n\n    /**\n     * Returns the first error of the specified attribute.\n     * @param string $attribute attribute name.\n     * @return string|null the error message. Null is returned if no error.\n     * @see getErrors()\n     * @see getFirstErrors()\n     */\n    public function getFirstError($attribute)\n    {\n        return isset($this->_errors[$attribute]) ? reset($this->_errors[$attribute]) : null;\n    }\n\n    /**\n     * Returns the errors for all attributes as a one-dimensional array.\n     * @param bool $showAllErrors boolean, if set to true every error message for each attribute will be shown otherwise\n     * only the first error message for each attribute will be shown.\n     * @return string[] errors for all attributes as a one-dimensional array. Empty array is returned if no error.\n     * @see getErrors()\n     * @see getFirstErrors()\n     * @since 2.0.14\n     */\n    public function getErrorSummary($showAllErrors)\n    {\n        $lines = [];\n        $errors = $showAllErrors ? $this->getErrors() : $this->getFirstErrors();\n        foreach ($errors as $es) {\n            $lines = array_merge($lines, (array)$es);\n        }\n        return $lines;\n    }\n\n    /**\n     * Adds a new error to the specified attribute.\n     * @param string $attribute attribute name\n     * @param string $error new error message\n     */\n    public function addError($attribute, $error = '')\n    {\n        $this->_errors[$attribute][] = $error;\n    }\n\n    /**\n     * Adds a list of errors.\n     * @param array $items a list of errors. The array keys must be attribute names.\n     * The array values should be error messages. If an attribute has multiple errors,\n     * these errors must be given in terms of an array.\n     * You may use the result of [[getErrors()]] as the value for this parameter.\n     * @since 2.0.2\n     */\n    public function addErrors(array $items)\n    {\n        foreach ($items as $attribute => $errors) {\n            if (is_array($errors)) {\n                foreach ($errors as $error) {\n                    $this->addError($attribute, $error);\n                }\n            } else {\n                $this->addError($attribute, $errors);\n            }\n        }\n    }\n\n    /**\n     * Removes errors for all attributes or a single attribute.\n     * @param string|null $attribute attribute name. Use null to remove errors for all attributes.\n     */\n    public function clearErrors($attribute = null)\n    {\n        if ($attribute === null) {\n            $this->_errors = [];\n        } else {\n            unset($this->_errors[$attribute]);\n        }\n    }\n\n    /**\n     * Generates a user friendly attribute label based on the give attribute name.\n     * This is done by replacing underscores, dashes and dots with blanks and\n     * changing the first letter of each word to upper case.\n     * For example, 'department_name' or 'DepartmentName' will generate 'Department Name'.\n     * @param string $name the column name\n     * @return string the attribute label\n     */\n    public function generateAttributeLabel($name)\n    {\n        return Inflector::camel2words($name, true);\n    }\n\n    /**\n     * Returns attribute values.\n     * @param array|null $names list of attributes whose value needs to be returned.\n     * Defaults to null, meaning all attributes listed in [[attributes()]] will be returned.\n     * If it is an array, only the attributes in the array will be returned.\n     * @param array $except list of attributes whose value should NOT be returned.\n     * @return array<string, mixed> attribute values (name => value).\n     */\n    public function getAttributes($names = null, $except = [])\n    {\n        $values = [];\n        if ($names === null) {\n            $names = $this->attributes();\n        }\n        foreach ($names as $name) {\n            $values[$name] = $this->$name;\n        }\n        foreach ($except as $name) {\n            unset($values[$name]);\n        }\n\n        return $values;\n    }\n\n    /**\n     * Sets the attribute values in a massive way.\n     * @param array $values attribute values (name => value) to be assigned to the model.\n     * @param bool $safeOnly whether the assignments should only be done to the safe attributes.\n     * A safe attribute is one that is associated with a validation rule in the current [[scenario]].\n     * @see safeAttributes()\n     * @see attributes()\n     */\n    public function setAttributes($values, $safeOnly = true)\n    {\n        if (is_array($values)) {\n            $attributes = array_flip($safeOnly ? $this->safeAttributes() : $this->attributes());\n            foreach ($values as $name => $value) {\n                if (isset($attributes[$name])) {\n                    $this->$name = $value;\n                } elseif ($safeOnly) {\n                    $this->onUnsafeAttribute($name, $value);\n                }\n            }\n        }\n    }\n\n    /**\n     * This method is invoked when an unsafe attribute is being massively assigned.\n     * The default implementation will log a warning message if YII_DEBUG is on.\n     * It does nothing otherwise.\n     * @param string $name the unsafe attribute name\n     * @param mixed $value the attribute value\n     */\n    public function onUnsafeAttribute($name, $value)\n    {\n        if (YII_DEBUG) {\n            Yii::debug(\"Failed to set unsafe attribute '$name' in '\" . get_class($this) . \"'.\", __METHOD__);\n        }\n    }\n\n    /**\n     * Returns the scenario that this model is used in.\n     *\n     * Scenario affects how validation is performed and which attributes can\n     * be massively assigned.\n     *\n     * @return string the scenario that this model is in. Defaults to [[SCENARIO_DEFAULT]].\n     */\n    public function getScenario()\n    {\n        return $this->_scenario;\n    }\n\n    /**\n     * Sets the scenario for the model.\n     * Note that this method does not check if the scenario exists or not.\n     * The method [[validate()]] will perform this check.\n     * @param string $value the scenario that this model is in.\n     */\n    public function setScenario($value)\n    {\n        $this->_scenario = $value;\n    }\n\n    /**\n     * Returns the attribute names that are safe to be massively assigned in the current scenario.\n     *\n     * @return string[] safe attribute names\n     */\n    public function safeAttributes()\n    {\n        $scenario = $this->getScenario();\n        $scenarios = $this->scenarios();\n        if (!isset($scenarios[$scenario])) {\n            return [];\n        }\n        $attributes = [];\n        foreach ($scenarios[$scenario] as $attribute) {\n            if (\n                $attribute !== ''\n                && strncmp($attribute, '!', 1) !== 0\n                && !in_array('!' . $attribute, $scenarios[$scenario])\n            ) {\n                $attributes[] = $attribute;\n            }\n        }\n\n        return $attributes;\n    }\n\n    /**\n     * Returns the attribute names that are subject to validation in the current scenario.\n     * @return string[] safe attribute names\n     */\n    public function activeAttributes()\n    {\n        $scenario = $this->getScenario();\n        $scenarios = $this->scenarios();\n        if (!isset($scenarios[$scenario])) {\n            return [];\n        }\n        $attributes = array_keys(array_flip($scenarios[$scenario]));\n        foreach ($attributes as $i => $attribute) {\n            if (strncmp($attribute, '!', 1) === 0) {\n                $attributes[$i] = substr($attribute, 1);\n            }\n        }\n\n        return $attributes;\n    }\n\n    /**\n     * Populates the model with input data.\n     *\n     * This method provides a convenient shortcut for:\n     *\n     * ```\n     * if (isset($_POST['FormName'])) {\n     *     $model->attributes = $_POST['FormName'];\n     *     if ($model->save()) {\n     *         // handle success\n     *     }\n     * }\n     * ```\n     *\n     * which, with `load()` can be written as:\n     *\n     * ```\n     * if ($model->load($_POST) && $model->save()) {\n     *     // handle success\n     * }\n     * ```\n     *\n     * `load()` gets the `'FormName'` from the model's [[formName()]] method (which you may override), unless the\n     * `$formName` parameter is given. If the form name is empty, `load()` populates the model with the whole of `$data`,\n     * instead of `$data['FormName']`.\n     *\n     * Note, that the data being populated is subject to the safety check by [[setAttributes()]].\n     *\n     * @param array $data the data array to load, typically `$_POST` or `$_GET`.\n     * @param string|null $formName the form name to use to load the data into the model, empty string when form not use.\n     * If not set, [[formName()]] is used.\n     * @return bool whether `load()` found the expected form in `$data`.\n     */\n    public function load($data, $formName = null)\n    {\n        $scope = $formName === null ? $this->formName() : $formName;\n        if ($scope === '' && !empty($data)) {\n            $this->setAttributes($data);\n\n            return true;\n        } elseif (isset($data[$scope])) {\n            $this->setAttributes($data[$scope]);\n\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * Populates a set of models with the data from end user.\n     * This method is mainly used to collect tabular data input.\n     * The data to be loaded for each model is `$data[formName][index]`, where `formName`\n     * refers to the value of [[formName()]], and `index` the index of the model in the `$models` array.\n     * If [[formName()]] is empty, `$data[index]` will be used to populate each model.\n     * The data being populated to each model is subject to the safety check by [[setAttributes()]].\n     * @param array $models the models to be populated. Note that all models should have the same class.\n     * @param array $data the data array. This is usually `$_POST` or `$_GET`, but can also be any valid array\n     * supplied by end user.\n     * @param string|null $formName the form name to be used for loading the data into the models.\n     * If not set, it will use the [[formName()]] value of the first model in `$models`.\n     * This parameter is available since version 2.0.1.\n     * @return bool whether at least one of the models is successfully populated.\n     */\n    public static function loadMultiple($models, $data, $formName = null)\n    {\n        if ($formName === null) {\n            /** @var self|false $first */\n            $first = reset($models);\n            if ($first === false) {\n                return false;\n            }\n            $formName = $first->formName();\n        }\n\n        $success = false;\n        foreach ($models as $i => $model) {\n            /** @var self $model */\n            if ($formName == '') {\n                if (!empty($data[$i]) && $model->load($data[$i], '')) {\n                    $success = true;\n                }\n            } elseif (!empty($data[$formName][$i]) && $model->load($data[$formName][$i], '')) {\n                $success = true;\n            }\n        }\n\n        return $success;\n    }\n\n    /**\n     * Validates multiple models.\n     * This method will validate every model. The models being validated may\n     * be of the same or different types.\n     * @param array $models the models to be validated\n     * @param array|null $attributeNames list of attribute names that should be validated.\n     * If this parameter is empty, it means any attribute listed in the applicable\n     * validation rules should be validated.\n     * @return bool whether all models are valid. False will be returned if one\n     * or multiple models have validation error.\n     */\n    public static function validateMultiple($models, $attributeNames = null)\n    {\n        $valid = true;\n        /** @var self $model */\n        foreach ($models as $model) {\n            $valid = $model->validate($attributeNames) && $valid;\n        }\n\n        return $valid;\n    }\n\n    /**\n     * Returns the list of fields that should be returned by default by [[toArray()]] when no specific fields are specified.\n     *\n     * A field is a named element in the returned array by [[toArray()]].\n     *\n     * This method should return an array of field names or field definitions.\n     * If the former, the field name will be treated as an object property name whose value will be used\n     * as the field value. If the latter, the array key should be the field name while the array value should be\n     * the corresponding field definition which can be either an object property name or a PHP callable\n     * returning the corresponding field value. The signature of the callable should be:\n     *\n     * ```\n     * function ($model, $field) {\n     *     // return field value\n     * }\n     * ```\n     *\n     * For example, the following code declares four fields:\n     *\n     * - `email`: the field name is the same as the property name `email`;\n     * - `firstName` and `lastName`: the field names are `firstName` and `lastName`, and their\n     *   values are obtained from the `first_name` and `last_name` properties;\n     * - `fullName`: the field name is `fullName`. Its value is obtained by concatenating `first_name`\n     *   and `last_name`.\n     *\n     * ```\n     * return [\n     *     'email',\n     *     'firstName' => 'first_name',\n     *     'lastName' => 'last_name',\n     *     'fullName' => function ($model) {\n     *         return $model->first_name . ' ' . $model->last_name;\n     *     },\n     * ];\n     * ```\n     *\n     * In this method, you may also want to return different lists of fields based on some context\n     * information. For example, depending on [[scenario]] or the privilege of the current application user,\n     * you may return different sets of visible fields or filter out some fields.\n     *\n     * The default implementation of this method returns [[attributes()]] indexed by the same attribute names.\n     *\n     * @return array the list of field names or field definitions.\n     * @see toArray()\n     */\n    public function fields()\n    {\n        $fields = $this->attributes();\n\n        return array_combine($fields, $fields);\n    }\n\n    /**\n     * Returns an iterator for traversing the attributes in the model.\n     * This method is required by the interface [[\\IteratorAggregate]].\n     * @return ArrayIterator<string, mixed> an iterator for traversing the items in the list.\n     */\n    #[\\ReturnTypeWillChange]\n    public function getIterator()\n    {\n        $attributes = $this->getAttributes();\n        return new ArrayIterator($attributes);\n    }\n\n    /**\n     * Returns whether there is an element at the specified offset.\n     * This method is required by the SPL interface [[\\ArrayAccess]].\n     * It is implicitly called when you use something like `isset($model[$offset])`.\n     * @param string $offset the offset to check on.\n     * @return bool whether or not an offset exists.\n     */\n    #[\\ReturnTypeWillChange]\n    public function offsetExists($offset)\n    {\n        return isset($this->$offset);\n    }\n\n    /**\n     * Returns the element at the specified offset.\n     * This method is required by the SPL interface [[\\ArrayAccess]].\n     * It is implicitly called when you use something like `$value = $model[$offset];`.\n     * @param string $offset the offset to retrieve element.\n     * @return mixed the element at the offset, null if no element is found at the offset\n     */\n    #[\\ReturnTypeWillChange]\n    public function offsetGet($offset)\n    {\n        return $this->$offset;\n    }\n\n    /**\n     * Sets the element at the specified offset.\n     * This method is required by the SPL interface [[\\ArrayAccess]].\n     * It is implicitly called when you use something like `$model[$offset] = $value;`.\n     * @param string $offset the offset to set element\n     * @param mixed $value the element value\n     */\n    #[\\ReturnTypeWillChange]\n    public function offsetSet($offset, $value)\n    {\n        $this->$offset = $value;\n    }\n\n    /**\n     * Sets the element value at the specified offset to null.\n     * This method is required by the SPL interface [[\\ArrayAccess]].\n     * It is implicitly called when you use something like `unset($model[$offset])`.\n     * @param string $offset the offset to unset element\n     */\n    #[\\ReturnTypeWillChange]\n    public function offsetUnset($offset)\n    {\n        $this->$offset = null;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function __clone()\n    {\n        parent::__clone();\n\n        $this->_errors = null;\n        $this->_validators = null;\n    }\n}\n"
  },
  {
    "path": "framework/base/ModelEvent.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * ModelEvent represents the parameter needed by [[Model]] events.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass ModelEvent extends Event\n{\n    /**\n     * @var bool whether the model is in valid status. Defaults to true.\n     * A model is in valid status if it passes validations or certain checks.\n     */\n    public $isValid = true;\n}\n"
  },
  {
    "path": "framework/base/Module.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\nuse Yii;\nuse yii\\di\\ServiceLocator;\n\n/**\n * Module is the base class for module and application classes.\n *\n * A module represents a sub-application which contains MVC elements by itself, such as\n * models, views, controllers, etc.\n *\n * A module may consist of [[modules|sub-modules]].\n *\n * [[components|Components]] may be registered with the module so that they are globally\n * accessible within the module.\n *\n * For more details and usage information on Module, see the [guide article on modules](guide:structure-modules).\n *\n * @property-write array $aliases List of path aliases to be defined. The array keys are alias names (must\n * start with `@`) and the array values are the corresponding paths or aliases. See [[setAliases()]] for an\n * example.\n * @property string $basePath The root directory of the module.\n * @property string $controllerPath The directory that contains the controller classes.\n * @property string $layoutPath The root directory of layout files. Defaults to \"[[viewPath]]/layouts\".\n * @property array $modules The modules (indexed by their IDs).\n * @property-read string $uniqueId The unique ID of the module.\n * @property string $version The version of this module. Note that the type of this property differs in getter\n * and setter. See [[getVersion()]] and [[setVersion()]] for details.\n * @property string $viewPath The root directory of view files. Defaults to \"[[basePath]]/views\".\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Module extends ServiceLocator\n{\n    /**\n     * @event ActionEvent an event raised before executing a controller action.\n     * You may set [[ActionEvent::isValid]] to be `false` to cancel the action execution.\n     */\n    public const EVENT_BEFORE_ACTION = 'beforeAction';\n    /**\n     * @event ActionEvent an event raised after executing a controller action.\n     */\n    public const EVENT_AFTER_ACTION = 'afterAction';\n    /**\n     * @var array custom module parameters (name => value).\n     */\n    public $params = [];\n    /**\n     * @var string an ID that uniquely identifies this module among other modules which have the same [[module|parent]].\n     */\n    public $id;\n    /**\n     * @var Module|null the parent module of this module. `null` if this module does not have a parent.\n     */\n    public $module;\n    /**\n     * @var string|bool|null the layout that should be applied for views within this module. This refers to a view name\n     * relative to [[layoutPath]]. If this is not set, it means the layout value of the [[module|parent module]]\n     * will be taken. If this is `false`, layout will be disabled within this module.\n     */\n    public $layout;\n    /**\n     * @var array mapping from controller ID to controller configurations.\n     * Each name-value pair specifies the configuration of a single controller.\n     * A controller configuration can be either a string or an array.\n     * If the former, the string should be the fully qualified class name of the controller.\n     * If the latter, the array must contain a `class` element which specifies\n     * the controller's fully qualified class name, and the rest of the name-value pairs\n     * in the array are used to initialize the corresponding controller properties. For example,\n     *\n     * ```\n     * [\n     *   'account' => 'app\\controllers\\UserController',\n     *   'article' => [\n     *      'class' => 'app\\controllers\\PostController',\n     *      'pageTitle' => 'something new',\n     *   ],\n     * ]\n     * ```\n     */\n    public $controllerMap = [];\n    /**\n     * @var string|null the namespace that controller classes are in.\n     * This namespace will be used to load controller classes by prepending it to the controller\n     * class name.\n     *\n     * If not set, it will use the `controllers` sub-namespace under the namespace of this module.\n     * For example, if the namespace of this module is `foo\\bar`, then the default\n     * controller namespace would be `foo\\bar\\controllers`.\n     *\n     * See also the [guide section on autoloading](guide:concept-autoloading) to learn more about\n     * defining namespaces and how classes are loaded.\n     */\n    public $controllerNamespace;\n    /**\n     * @var string the default route of this module. Defaults to `default`.\n     * The route may consist of child module ID, controller ID, and/or action ID.\n     * For example, `help`, `post/create`, `admin/post/create`.\n     * If action ID is not given, it will take the default value as specified in\n     * [[Controller::defaultAction]].\n     */\n    public $defaultRoute = 'default';\n\n    /**\n     * @var string the root directory of the module.\n     */\n    private $_basePath;\n    /**\n     * @var string The root directory that contains the controller classes for this module.\n     */\n    private $_controllerPath;\n    /**\n     * @var string the root directory that contains view files for this module\n     */\n    private $_viewPath;\n    /**\n     * @var string the root directory that contains layout view files for this module.\n     */\n    private $_layoutPath;\n    /**\n     * @var array child modules of this module\n     */\n    private $_modules = [];\n    /**\n     * @var string|callable|null the version of this module.\n     * Version can be specified as a PHP callback, which can accept module instance as an argument and should\n     * return the actual version. For example:\n     *\n     * ```\n     * function (Module $module) {\n     *     //return string|int\n     * }\n     * ```\n     *\n     * If not set, [[defaultVersion()]] will be used to determine actual value.\n     *\n     * @since 2.0.11\n     */\n    private $_version;\n\n\n    /**\n     * Constructor.\n     * @param string $id the ID of this module.\n     * @param Module|null $parent the parent module (if any).\n     * @param array<string, mixed> $config name-value pairs that will be used to initialize the object properties.\n     */\n    public function __construct($id, $parent = null, $config = [])\n    {\n        $this->id = $id;\n        $this->module = $parent;\n        parent::__construct($config);\n    }\n\n    /**\n     * Returns the currently requested instance of this module class.\n     * If the module class is not currently requested, `null` will be returned.\n     * This method is provided so that you access the module instance from anywhere within the module.\n     * @return static|null the currently requested instance of this module class, or `null` if the module class is not requested.\n     */\n    public static function getInstance()\n    {\n        $class = get_called_class();\n        return isset(Yii::$app->loadedModules[$class]) ? Yii::$app->loadedModules[$class] : null;\n    }\n\n    /**\n     * Sets the currently requested instance of this module class.\n     * @param Module|null $instance the currently requested instance of this module class.\n     * If it is `null`, the instance of the calling class will be removed, if any.\n     */\n    public static function setInstance($instance)\n    {\n        if ($instance === null) {\n            unset(Yii::$app->loadedModules[get_called_class()]);\n        } else {\n            Yii::$app->loadedModules[get_class($instance)] = $instance;\n        }\n    }\n\n    /**\n     * Initializes the module.\n     *\n     * This method is called after the module is created and initialized with property values\n     * given in configuration. The default implementation will initialize [[controllerNamespace]]\n     * if it is not set.\n     *\n     * If you override this method, please make sure you call the parent implementation.\n     */\n    public function init()\n    {\n        if ($this->controllerNamespace === null) {\n            $class = get_class($this);\n            if (($pos = strrpos($class, '\\\\')) !== false) {\n                $this->controllerNamespace = substr($class, 0, $pos) . '\\\\controllers';\n            }\n        }\n    }\n\n    /**\n     * Returns an ID that uniquely identifies this module among all modules within the current application.\n     * Note that if the module is an application, an empty string will be returned.\n     * @return string the unique ID of the module.\n     */\n    public function getUniqueId()\n    {\n        return $this->module ? ltrim($this->module->getUniqueId() . '/' . $this->id, '/') : $this->id;\n    }\n\n    /**\n     * Returns the root directory of the module.\n     * It defaults to the directory containing the module class file.\n     * @return string the root directory of the module.\n     */\n    public function getBasePath()\n    {\n        if ($this->_basePath === null) {\n            $class = new \\ReflectionClass($this);\n            $this->_basePath = dirname($class->getFileName());\n        }\n\n        return $this->_basePath;\n    }\n\n    /**\n     * Sets the root directory of the module.\n     * This method can only be invoked at the beginning of the constructor.\n     * @param string $path the root directory of the module. This can be either a directory name or a [path alias](guide:concept-aliases).\n     * @throws InvalidArgumentException if the directory does not exist.\n     */\n    public function setBasePath($path)\n    {\n        $path = Yii::getAlias($path);\n        $p = strncmp($path, 'phar://', 7) === 0 ? $path : realpath($path);\n        if (is_string($p) && is_dir($p)) {\n            $this->_basePath = $p;\n        } else {\n            throw new InvalidArgumentException(\"The directory does not exist: $path\");\n        }\n    }\n\n    /**\n     * Returns the directory that contains the controller classes according to [[controllerNamespace]].\n     * Note that in order for this method to return a value, you must define\n     * an alias for the root namespace of [[controllerNamespace]].\n     * @return string the directory that contains the controller classes.\n     * @throws InvalidArgumentException if there is no alias defined for the root namespace of [[controllerNamespace]].\n     */\n    public function getControllerPath()\n    {\n        if ($this->_controllerPath === null) {\n            $this->_controllerPath = Yii::getAlias('@' . str_replace('\\\\', '/', $this->controllerNamespace));\n        }\n\n        return $this->_controllerPath;\n    }\n\n    /**\n     * Sets the directory that contains the controller classes.\n     * @param string $path the root directory that contains the controller classes.\n     * @throws InvalidArgumentException if the directory is invalid.\n     * @since 2.0.44\n     */\n    public function setControllerPath($path)\n    {\n        $this->_controllerPath = Yii::getAlias($path);\n    }\n\n    /**\n     * Returns the directory that contains the view files for this module.\n     * @return string the root directory of view files. Defaults to \"[[basePath]]/views\".\n     */\n    public function getViewPath()\n    {\n        if ($this->_viewPath === null) {\n            $this->_viewPath = $this->getBasePath() . DIRECTORY_SEPARATOR . 'views';\n        }\n\n        return $this->_viewPath;\n    }\n\n    /**\n     * Sets the directory that contains the view files.\n     * @param string $path the root directory of view files.\n     * @throws InvalidArgumentException if the directory is invalid.\n     */\n    public function setViewPath($path)\n    {\n        $this->_viewPath = Yii::getAlias($path);\n    }\n\n    /**\n     * Returns the directory that contains layout view files for this module.\n     * @return string the root directory of layout files. Defaults to \"[[viewPath]]/layouts\".\n     */\n    public function getLayoutPath()\n    {\n        if ($this->_layoutPath === null) {\n            $this->_layoutPath = $this->getViewPath() . DIRECTORY_SEPARATOR . 'layouts';\n        }\n\n        return $this->_layoutPath;\n    }\n\n    /**\n     * Sets the directory that contains the layout files.\n     * @param string $path the root directory or [path alias](guide:concept-aliases) of layout files.\n     * @throws InvalidArgumentException if the directory is invalid\n     */\n    public function setLayoutPath($path)\n    {\n        $this->_layoutPath = Yii::getAlias($path);\n    }\n\n    /**\n     * Returns current module version.\n     * If version is not explicitly set, [[defaultVersion()]] method will be used to determine its value.\n     * @return string the version of this module.\n     * @since 2.0.11\n     */\n    public function getVersion()\n    {\n        if ($this->_version === null) {\n            $this->_version = $this->defaultVersion();\n        } else {\n            if (!is_scalar($this->_version)) {\n                $this->_version = call_user_func($this->_version, $this);\n            }\n        }\n\n        return $this->_version;\n    }\n\n    /**\n     * Sets current module version.\n     * @param string|callable|null $version the version of this module.\n     * Version can be specified as a PHP callback, which can accept module instance as an argument and should\n     * return the actual version. For example:\n     *\n     * ```\n     * function (Module $module) {\n     *     //return string\n     * }\n     * ```\n     *\n     * @since 2.0.11\n     */\n    public function setVersion($version)\n    {\n        $this->_version = $version;\n    }\n\n    /**\n     * Returns default module version.\n     * Child class may override this method to provide more specific version detection.\n     * @return string the version of this module.\n     * @since 2.0.11\n     */\n    protected function defaultVersion()\n    {\n        if ($this->module === null) {\n            return '1.0';\n        }\n\n        return $this->module->getVersion();\n    }\n\n    /**\n     * Defines path aliases.\n     * This method calls [[Yii::setAlias()]] to register the path aliases.\n     * This method is provided so that you can define path aliases when configuring a module.\n     * @param array $aliases list of path aliases to be defined. The array keys are alias names\n     * (must start with `@`) and the array values are the corresponding paths or aliases.\n     * For example,\n     *\n     * ```\n     * [\n     *     '@models' => '@app/models', // an existing alias\n     *     '@backend' => __DIR__ . '/../backend',  // a directory\n     * ]\n     * ```\n     */\n    public function setAliases($aliases)\n    {\n        foreach ($aliases as $name => $alias) {\n            Yii::setAlias($name, $alias);\n        }\n    }\n\n    /**\n     * Checks whether the child module of the specified ID exists.\n     * This method supports checking the existence of both child and grand child modules.\n     * @param string $id module ID. For grand child modules, use ID path relative to this module (e.g. `admin/content`).\n     * @return bool whether the named module exists. Both loaded and unloaded modules\n     * are considered.\n     */\n    public function hasModule($id)\n    {\n        if (($pos = strpos($id, '/')) !== false) {\n            // sub-module\n            $module = $this->getModule(substr($id, 0, $pos));\n\n            return $module === null ? false : $module->hasModule(substr($id, $pos + 1));\n        }\n\n        return isset($this->_modules[$id]);\n    }\n\n    /**\n     * Retrieves the child module of the specified ID.\n     * This method supports retrieving both child modules and grand child modules.\n     * @param string $id module ID (case-sensitive). To retrieve grand child modules,\n     * use ID path relative to this module (e.g. `admin/content`).\n     * @param bool $load whether to load the module if it is not yet loaded.\n     * @return Module|null the module instance, `null` if the module does not exist.\n     * @see hasModule()\n     */\n    public function getModule($id, $load = true)\n    {\n        if (($pos = strpos($id, '/')) !== false) {\n            // sub-module\n            $module = $this->getModule(substr($id, 0, $pos));\n\n            return $module === null ? null : $module->getModule(substr($id, $pos + 1), $load);\n        }\n\n        if (isset($this->_modules[$id])) {\n            if ($this->_modules[$id] instanceof self) {\n                return $this->_modules[$id];\n            } elseif ($load) {\n                Yii::debug(\"Loading module: $id\", __METHOD__);\n                /** @var self $module */\n                $module = Yii::createObject($this->_modules[$id], [$id, $this]);\n                $module::setInstance($module);\n                return $this->_modules[$id] = $module;\n            }\n        }\n\n        return null;\n    }\n\n    /**\n     * Adds a sub-module to this module.\n     * @param string $id module ID.\n     * @param Module|array|null $module the sub-module to be added to this module. This can\n     * be one of the following:\n     *\n     * - a [[Module]] object\n     * - a configuration array: when [[getModule()]] is called initially, the array\n     *   will be used to instantiate the sub-module\n     * - `null`: the named sub-module will be removed from this module\n     */\n    public function setModule($id, $module)\n    {\n        if ($module === null) {\n            unset($this->_modules[$id]);\n        } else {\n            $this->_modules[$id] = $module;\n            if ($module instanceof self) {\n                $module->module = $this;\n            }\n        }\n    }\n\n    /**\n     * Returns the sub-modules in this module.\n     * @param bool $loadedOnly whether to return the loaded sub-modules only. If this is set `false`,\n     * then all sub-modules registered in this module will be returned, whether they are loaded or not.\n     * Loaded modules will be returned as objects, while unloaded modules as configuration arrays.\n     * @return array the modules (indexed by their IDs).\n     */\n    public function getModules($loadedOnly = false)\n    {\n        if ($loadedOnly) {\n            $modules = [];\n            foreach ($this->_modules as $module) {\n                if ($module instanceof self) {\n                    $modules[] = $module;\n                }\n            }\n\n            return $modules;\n        }\n\n        return $this->_modules;\n    }\n\n    /**\n     * Registers sub-modules in the current module.\n     *\n     * Each sub-module should be specified as a name-value pair, where\n     * name refers to the ID of the module and value the module or a configuration\n     * array that can be used to create the module. In the latter case, [[Yii::createObject()]]\n     * will be used to create the module.\n     *\n     * If a new sub-module has the same ID as an existing one, the existing one will be overwritten silently.\n     *\n     * The following is an example for registering two sub-modules:\n     *\n     * ```\n     * [\n     *     'comment' => [\n     *         'class' => 'app\\modules\\comment\\CommentModule',\n     *         'db' => 'db',\n     *     ],\n     *     'booking' => ['class' => 'app\\modules\\booking\\BookingModule'],\n     * ]\n     * ```\n     *\n     * @param array $modules modules (id => module configuration or instances).\n     */\n    public function setModules($modules)\n    {\n        foreach ($modules as $id => $module) {\n            $this->_modules[$id] = $module;\n            if ($module instanceof self) {\n                $module->module = $this;\n            }\n        }\n    }\n\n    /**\n     * Runs a controller action specified by a route.\n     * This method parses the specified route and creates the corresponding child module(s), controller and action\n     * instances. It then calls [[Controller::runAction()]] to run the action with the given parameters.\n     * If the route is empty, the method will use [[defaultRoute]].\n     * @param string $route the route that specifies the action.\n     * @param array $params the parameters to be passed to the action\n     * @return mixed the result of the action.\n     * @throws InvalidRouteException if the requested route cannot be resolved into an action successfully.\n     */\n    public function runAction($route, $params = [])\n    {\n        $parts = $this->createController($route);\n        if (is_array($parts)) {\n            list($controller, $actionID) = $parts;\n            $oldController = Yii::$app->controller;\n            Yii::$app->controller = $controller;\n            $result = $controller->runAction($actionID, $params);\n            if ($oldController !== null) {\n                Yii::$app->controller = $oldController;\n            }\n\n            return $result;\n        }\n\n        $id = $this->getUniqueId();\n        throw new InvalidRouteException('Unable to resolve the request \"' . ($id === '' ? $route : $id . '/' . $route) . '\".');\n    }\n\n    /**\n     * Creates a controller instance based on the given route.\n     *\n     * The route should be relative to this module. The method implements the following algorithm\n     * to resolve the given route:\n     *\n     * 1. If the route is empty, use [[defaultRoute]];\n     * 2. If the first segment of the route is found in [[controllerMap]], create a controller\n     *    based on the corresponding configuration found in [[controllerMap]];\n     * 3. If the first segment of the route is a valid module ID as declared in [[modules]],\n     *    call the module's `createController()` with the rest part of the route;\n     * 4. The given route is in the format of `abc/def/xyz`. Try either `abc\\DefController`\n     *    or `abc\\def\\XyzController` class within the [[controllerNamespace|controller namespace]].\n     *\n     * If any of the above steps resolves into a controller, it is returned together with the rest\n     * part of the route which will be treated as the action ID. Otherwise, `false` will be returned.\n     *\n     * @param string $route the route consisting of module, controller and action IDs.\n     * @return array{Controller<static>, string}|false If the controller is created successfully, it will be returned together\n     * with the requested action ID. Otherwise `false` will be returned.\n     * @throws InvalidConfigException if the controller class and its file do not match.\n     *\n     * @phpstan-return array{Controller<static>, string}|false\n     * @psalm-return array{Controller<self>, string}|false\n     */\n    public function createController($route)\n    {\n        if ($route === '') {\n            $route = $this->defaultRoute;\n        }\n\n        // double slashes or leading/ending slashes may cause substr problem\n        $route = trim($route, '/');\n        if (strpos($route, '//') !== false) {\n            return false;\n        }\n\n        if (strpos($route, '/') !== false) {\n            list($id, $route) = explode('/', $route, 2);\n        } else {\n            $id = $route;\n            $route = '';\n        }\n\n        // module and controller map take precedence\n        if (isset($this->controllerMap[$id])) {\n            $controller = Yii::createObject($this->controllerMap[$id], [$id, $this]);\n            return [$controller, $route];\n        }\n        $module = $this->getModule($id);\n        if ($module !== null) {\n            return $module->createController($route);\n        }\n\n        if (($pos = strrpos($route, '/')) !== false) {\n            $id .= '/' . substr($route, 0, $pos);\n            $route = substr($route, $pos + 1);\n        }\n\n        $controller = $this->createControllerByID($id);\n        if ($controller === null && $route !== '') {\n            $controller = $this->createControllerByID($id . '/' . $route);\n            $route = '';\n        }\n\n        return $controller === null ? false : [$controller, $route];\n    }\n\n    /**\n     * Creates a controller based on the given controller ID.\n     *\n     * The controller ID is relative to this module. The controller class\n     * should be namespaced under [[controllerNamespace]].\n     *\n     * Note that this method does not check [[modules]] or [[controllerMap]].\n     *\n     * @param string $id the controller ID.\n     * @return Controller<static>|null the newly created controller instance, or `null` if the controller ID is invalid.\n     * @throws InvalidConfigException if the controller class and its file name do not match.\n     * This exception is only thrown when in debug mode.\n     *\n     * @phpstan-return Controller<static>|null\n     * @psalm-return Controller<self>|null\n     */\n    public function createControllerByID($id)\n    {\n        $pos = strrpos($id, '/');\n        if ($pos === false) {\n            $prefix = '';\n            $className = $id;\n        } else {\n            $prefix = substr($id, 0, $pos + 1);\n            $className = substr($id, $pos + 1);\n        }\n\n        if ($this->isIncorrectClassNameOrPrefix($className, $prefix)) {\n            return null;\n        }\n\n        $className = preg_replace_callback('%-([a-z0-9_])%i', function ($matches) {\n                return ucfirst($matches[1]);\n        }, ucfirst($className)) . 'Controller';\n        $className = ltrim($this->controllerNamespace . '\\\\' . str_replace('/', '\\\\', $prefix) . $className, '\\\\');\n        if (strpos($className, '-') !== false || !class_exists($className)) {\n            return null;\n        }\n\n        if (is_subclass_of($className, 'yii\\base\\Controller')) {\n            $controller = Yii::createObject($className, [$id, $this]);\n            return get_class($controller) === $className ? $controller : null;\n        } elseif (YII_DEBUG) {\n            throw new InvalidConfigException('Controller class must extend from \\\\yii\\\\base\\\\Controller.');\n        }\n\n        return null;\n    }\n\n    /**\n     * Checks if class name or prefix is incorrect\n     *\n     * @param string $className\n     * @param string $prefix\n     * @return bool\n     */\n    private function isIncorrectClassNameOrPrefix($className, $prefix)\n    {\n        if (!preg_match('%^[a-z][a-z0-9\\\\-_]*$%', $className)) {\n            return true;\n        }\n        if ($prefix !== '' && !preg_match('%^[a-z0-9_/]+$%i', $prefix)) {\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * This method is invoked right before an action within this module is executed.\n     *\n     * The method will trigger the [[EVENT_BEFORE_ACTION]] event. The return value of the method\n     * will determine whether the action should continue to run.\n     *\n     * In case the action should not run, the request should be handled inside of the `beforeAction` code\n     * by either providing the necessary output or redirecting the request. Otherwise the response will be empty.\n     *\n     * If you override this method, your code should look like the following:\n     *\n     * ```\n     * public function beforeAction($action)\n     * {\n     *     if (!parent::beforeAction($action)) {\n     *         return false;\n     *     }\n     *\n     *     // your custom code here\n     *\n     *     return true; // or false to not run the action\n     * }\n     * ```\n     *\n     * @param Action<Controller<static>> $action the action to be executed.\n     * @return bool whether the action should continue to be executed.\n     *\n     * @phpstan-param Action<Controller<static>> $action\n     * @psalm-param Action<Controller<self>> $action\n     */\n    public function beforeAction($action)\n    {\n        $event = new ActionEvent($action);\n        $this->trigger(self::EVENT_BEFORE_ACTION, $event);\n        return $event->isValid;\n    }\n\n    /**\n     * This method is invoked right after an action within this module is executed.\n     *\n     * The method will trigger the [[EVENT_AFTER_ACTION]] event. The return value of the method\n     * will be used as the action return value.\n     *\n     * If you override this method, your code should look like the following:\n     *\n     * ```\n     * public function afterAction($action, $result)\n     * {\n     *     $result = parent::afterAction($action, $result);\n     *     // your custom code here\n     *     return $result;\n     * }\n     * ```\n     *\n     * @param Action<Controller<static>> $action the action just executed.\n     * @param mixed $result the action return result.\n     * @return mixed the processed action result.\n     *\n     * @phpstan-param Action<Controller<static>> $action\n     * @psalm-param Action<Controller<self>> $action\n     */\n    public function afterAction($action, $result)\n    {\n        $event = new ActionEvent($action);\n        $event->result = $result;\n        $this->trigger(self::EVENT_AFTER_ACTION, $event);\n        return $event->result;\n    }\n\n    /**\n     * {@inheritdoc}\n     *\n     * Since version 2.0.13, if a component isn't defined in the module, it will be looked up in the parent module.\n     * The parent module may be the application.\n     */\n    public function get($id, $throwException = true)\n    {\n        if (!isset($this->module)) {\n            return parent::get($id, $throwException);\n        }\n\n        $component = parent::get($id, false);\n        if ($component === null) {\n            $component = $this->module->get($id, $throwException);\n        }\n        return $component;\n    }\n\n    /**\n     * {@inheritdoc}\n     *\n     * Since version 2.0.13, if a component isn't defined in the module, it will be looked up in the parent module.\n     * The parent module may be the application.\n     */\n    public function has($id, $checkInstance = false)\n    {\n        return parent::has($id, $checkInstance) || (isset($this->module) && $this->module->has($id, $checkInstance));\n    }\n}\n"
  },
  {
    "path": "framework/base/NotSupportedException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * NotSupportedException represents an exception caused by accessing features that are not supported.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass NotSupportedException extends Exception\n{\n    /**\n     * @return string the user-friendly name of this exception\n     */\n    public function getName()\n    {\n        return 'Not Supported';\n    }\n}\n"
  },
  {
    "path": "framework/base/Request.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\nuse Yii;\n\n/**\n * Request represents a request that is handled by an [[Application]].\n *\n * For more details and usage information on Request, see the [guide article on requests](guide:runtime-requests).\n *\n * @property bool $isConsoleRequest The value indicating whether the current request is made via console.\n * @property string $scriptFile Entry script file path (processed w/ realpath()).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nabstract class Request extends Component\n{\n    private $_scriptFile;\n    private $_isConsoleRequest;\n\n\n    /**\n     * Resolves the current request into a route and the associated parameters.\n     * @return array the first element is the route, and the second is the associated parameters.\n     */\n    abstract public function resolve();\n\n    /**\n     * Returns a value indicating whether the current request is made via command line.\n     * @return bool the value indicating whether the current request is made via console\n     */\n    public function getIsConsoleRequest()\n    {\n        return $this->_isConsoleRequest !== null ? $this->_isConsoleRequest : PHP_SAPI === 'cli';\n    }\n\n    /**\n     * Sets the value indicating whether the current request is made via command line.\n     * @param bool $value the value indicating whether the current request is made via command line\n     */\n    public function setIsConsoleRequest($value)\n    {\n        $this->_isConsoleRequest = $value;\n    }\n\n    /**\n     * Returns entry script file path.\n     * @return string entry script file path (processed w/ realpath())\n     * @throws InvalidConfigException if the entry script file path cannot be determined automatically.\n     */\n    public function getScriptFile()\n    {\n        if ($this->_scriptFile === null) {\n            if (isset($_SERVER['SCRIPT_FILENAME'])) {\n                $this->setScriptFile($_SERVER['SCRIPT_FILENAME']);\n            } else {\n                throw new InvalidConfigException('Unable to determine the entry script file path.');\n            }\n        }\n\n        return $this->_scriptFile;\n    }\n\n    /**\n     * Sets the entry script file path.\n     * The entry script file path can normally be determined based on the `SCRIPT_FILENAME` SERVER variable.\n     * However, for some server configurations, this may not be correct or feasible.\n     * This setter is provided so that the entry script file path can be manually specified.\n     * @param string $value the entry script file path. This can be either a file path or a [path alias](guide:concept-aliases).\n     * @throws InvalidConfigException if the provided entry script file path is invalid.\n     */\n    public function setScriptFile($value)\n    {\n        $scriptFile = realpath(Yii::getAlias($value));\n        if ($scriptFile !== false && is_file($scriptFile)) {\n            $this->_scriptFile = $scriptFile;\n        } else {\n            throw new InvalidConfigException('Unable to determine the entry script file path.');\n        }\n    }\n}\n"
  },
  {
    "path": "framework/base/Response.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * Response represents the response of an [[Application]] to a [[Request]].\n *\n * For more details and usage information on Response, see the [guide article on responses](guide:runtime-responses).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Response extends Component\n{\n    /**\n     * @var int the exit status. Exit statuses should be in the range 0 to 254.\n     * The status 0 means the program terminates successfully.\n     */\n    public $exitStatus = 0;\n\n\n    /**\n     * Sends the response to client.\n     */\n    public function send()\n    {\n    }\n\n    /**\n     * Removes all existing output buffers.\n     */\n    public function clearOutputBuffers()\n    {\n        // the following manual level counting is to deal with zlib.output_compression set to On\n        for ($level = ob_get_level(); $level > 0; --$level) {\n            if (!@ob_end_clean()) {\n                ob_clean();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "framework/base/Security.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\nuse yii\\helpers\\StringHelper;\n\n/**\n * Security provides a set of methods to handle common security-related tasks.\n *\n * In particular, Security supports the following features:\n *\n * - Encryption/decryption: [[encryptByKey()]], [[decryptByKey()]], [[encryptByPassword()]] and [[decryptByPassword()]]\n * - Key derivation using standard algorithms: [[pbkdf2()]] and [[hkdf()]]\n * - Data tampering prevention: [[hashData()]] and [[validateData()]]\n * - Password validation: [[generatePasswordHash()]] and [[validatePassword()]]\n *\n * > Note: this class requires 'OpenSSL' PHP extension for random key/string generation on Windows and\n * for encryption/decryption on all platforms. For the highest security level PHP version >= 5.5.0 is recommended.\n *\n * For more details and usage information on Security, see the [guide article on security](guide:security-overview).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Tom Worster <fsb@thefsb.org>\n * @author Klimov Paul <klimov.paul@gmail.com>\n * @since 2.0\n */\nclass Security extends Component\n{\n    /**\n     * @var string The cipher to use for encryption and decryption.\n     */\n    public $cipher = 'AES-128-CBC';\n    /**\n     * @var array[] Look-up table of block sizes and key sizes for each supported OpenSSL cipher.\n     *\n     * In each element, the key is one of the ciphers supported by OpenSSL (@see openssl_get_cipher_methods()).\n     * The value is an array of two integers, the first is the cipher's block size in bytes and the second is\n     * the key size in bytes.\n     *\n     * > Warning: All OpenSSL ciphers that we recommend are in the default value, i.e. AES in CBC mode.\n     *\n     * > Note: Yii's encryption protocol uses the same size for cipher key, HMAC signature key and key\n     * derivation salt.\n     */\n    public $allowedCiphers = [\n        'AES-128-CBC' => [16, 16],\n        'AES-192-CBC' => [16, 24],\n        'AES-256-CBC' => [16, 32],\n    ];\n    /**\n     * @var string Hash algorithm for key derivation. Recommend sha256, sha384 or sha512.\n     * @see [hash_algos()](https://www.php.net/manual/en/function.hash-algos.php)\n     */\n    public $kdfHash = 'sha256';\n    /**\n     * @var string Hash algorithm for message authentication. Recommend sha256, sha384 or sha512.\n     * @see [hash_algos()](https://www.php.net/manual/en/function.hash-algos.php)\n     */\n    public $macHash = 'sha256';\n    /**\n     * @var string HKDF info value for derivation of message authentication key.\n     * @see hkdf()\n     */\n    public $authKeyInfo = 'AuthorizationKey';\n    /**\n     * @var int derivation iterations count.\n     * Set as high as possible to hinder dictionary password attacks.\n     */\n    public $derivationIterations = 100000;\n    /**\n     * @var string strategy, which should be used to generate password hash.\n     * Available strategies:\n     * - 'password_hash' - use of PHP `password_hash()` function with PASSWORD_DEFAULT algorithm.\n     *   This option is recommended, but it requires PHP version >= 5.5.0\n     * - 'crypt' - use PHP `crypt()` function.\n     * @deprecated since version 2.0.7, [[generatePasswordHash()]] ignores [[passwordHashStrategy]] and\n     * uses `password_hash()` when available or `crypt()` when not.\n     */\n    public $passwordHashStrategy;\n    /**\n     * @var int Default cost used for password hashing.\n     * Allowed value is between 4 and 31.\n     * @see generatePasswordHash()\n     * @since 2.0.6\n     */\n    public $passwordHashCost = 13;\n\n    /**\n     * @var boolean if LibreSSL should be used.\n     * The recent (> 2.1.5) LibreSSL RNGs are faster and likely better than /dev/urandom.\n     */\n    private $_useLibreSSL;\n\n\n    /**\n     * @return bool if LibreSSL should be used\n     * Use version is 2.1.5 or higher.\n     * @since 2.0.36\n     * @deprecated since 2.0.55. This method is not used internally. Will be removed in 2.2.\n     */\n    protected function shouldUseLibreSSL()\n    {\n        if ($this->_useLibreSSL === null) {\n            // Parse OPENSSL_VERSION_TEXT because OPENSSL_VERSION_NUMBER is no use for LibreSSL.\n            // https://bugs.php.net/bug.php?id=71143\n            $this->_useLibreSSL = defined('OPENSSL_VERSION_TEXT')\n                && preg_match('{^LibreSSL (\\d\\d?)\\.(\\d\\d?)\\.(\\d\\d?)$}', OPENSSL_VERSION_TEXT, $matches)\n                && (10000 * $matches[1]) + (100 * $matches[2]) + $matches[3] >= 20105;\n        }\n\n        return $this->_useLibreSSL;\n    }\n\n    /**\n     * Encrypts data using a password.\n     * Derives keys for encryption and authentication from the password using PBKDF2 and a random salt,\n     * which is deliberately slow to protect against dictionary attacks. Use [[encryptByKey()]] to\n     * encrypt fast using a cryptographic key rather than a password. Key derivation time is\n     * determined by [[$derivationIterations]], which should be set as high as possible.\n     * The encrypted data includes a keyed message authentication code (MAC) so there is no need\n     * to hash input or output data.\n     * > Note: Avoid encrypting with passwords wherever possible. Nothing can protect against\n     * poor-quality or compromised passwords.\n     * @param string $data the data to encrypt\n     * @param string $password the password to use for encryption\n     * @return string the encrypted data as byte string\n     * @see decryptByPassword()\n     * @see encryptByKey()\n     */\n    public function encryptByPassword($data, $password)\n    {\n        return $this->encrypt($data, true, $password, null);\n    }\n\n    /**\n     * Encrypts data using a cryptographic key.\n     * Derives keys for encryption and authentication from the input key using HKDF and a random salt,\n     * which is very fast relative to [[encryptByPassword()]]. The input key must be properly\n     * random -- use [[generateRandomKey()]] to generate keys.\n     * The encrypted data includes a keyed message authentication code (MAC) so there is no need\n     * to hash input or output data.\n     * @param string $data the data to encrypt\n     * @param string $inputKey the input to use for encryption and authentication\n     * @param string|null $info optional context and application specific information, see [[hkdf()]]\n     * @return string the encrypted data as byte string\n     * @see decryptByKey()\n     * @see encryptByPassword()\n     */\n    public function encryptByKey($data, $inputKey, $info = null)\n    {\n        return $this->encrypt($data, false, $inputKey, $info);\n    }\n\n    /**\n     * Verifies and decrypts data encrypted with [[encryptByPassword()]].\n     * @param string $data the encrypted data to decrypt\n     * @param string $password the password to use for decryption\n     * @return bool|string the decrypted data or false on authentication failure\n     * @see encryptByPassword()\n     */\n    public function decryptByPassword($data, $password)\n    {\n        return $this->decrypt($data, true, $password, null);\n    }\n\n    /**\n     * Verifies and decrypts data encrypted with [[encryptByKey()]].\n     * @param string $data the encrypted data to decrypt\n     * @param string $inputKey the input to use for encryption and authentication\n     * @param string|null $info optional context and application specific information, see [[hkdf()]]\n     * @return bool|string the decrypted data or false on authentication failure\n     * @see encryptByKey()\n     */\n    public function decryptByKey($data, $inputKey, $info = null)\n    {\n        return $this->decrypt($data, false, $inputKey, $info);\n    }\n\n    /**\n     * Encrypts data.\n     *\n     * @param string $data data to be encrypted\n     * @param bool $passwordBased set true to use password-based key derivation\n     * @param string $secret the encryption password or key\n     * @param string|null $info context/application specific information, e.g. a user ID\n     * See [RFC 5869 Section 3.2](https://tools.ietf.org/html/rfc5869#section-3.2) for more details.\n     *\n     * @return string the encrypted data as byte string\n     * @throws InvalidConfigException on OpenSSL not loaded\n     * @throws Exception on OpenSSL error\n     * @see decrypt()\n     */\n    protected function encrypt($data, $passwordBased, $secret, $info)\n    {\n        if (!extension_loaded('openssl')) {\n            throw new InvalidConfigException('Encryption requires the OpenSSL PHP extension');\n        }\n        if (!isset($this->allowedCiphers[$this->cipher][0], $this->allowedCiphers[$this->cipher][1])) {\n            throw new InvalidConfigException($this->cipher . ' is not an allowed cipher');\n        }\n\n        list($blockSize, $keySize) = $this->allowedCiphers[$this->cipher];\n\n        $keySalt = $this->generateRandomKey($keySize);\n        if ($passwordBased) {\n            $key = $this->pbkdf2($this->kdfHash, $secret, $keySalt, $this->derivationIterations, $keySize);\n        } else {\n            $key = $this->hkdf($this->kdfHash, $secret, $keySalt, $info, $keySize);\n        }\n\n        $iv = $this->generateRandomKey($blockSize);\n\n        $encrypted = openssl_encrypt($data, $this->cipher, $key, OPENSSL_RAW_DATA, $iv);\n        if ($encrypted === false) {\n            throw new \\yii\\base\\Exception('OpenSSL failure on encryption: ' . openssl_error_string());\n        }\n\n        $authKey = $this->hkdf($this->kdfHash, $key, null, $this->authKeyInfo, $keySize);\n        $hashed = $this->hashData($iv . $encrypted, $authKey);\n\n        /*\n         * Output: [keySalt][MAC][IV][ciphertext]\n         * - keySalt is KEY_SIZE bytes long\n         * - MAC: message authentication code, length same as the output of MAC_HASH\n         * - IV: initialization vector, length $blockSize\n         */\n        return $keySalt . $hashed;\n    }\n\n    /**\n     * Decrypts data.\n     *\n     * @param string $data encrypted data to be decrypted.\n     * @param bool $passwordBased set true to use password-based key derivation\n     * @param string $secret the decryption password or key\n     * @param string|null $info context/application specific information, @see encrypt()\n     *\n     * @return bool|string the decrypted data or false on authentication failure\n     * @throws InvalidConfigException on OpenSSL not loaded\n     * @throws Exception on OpenSSL error\n     * @see encrypt()\n     */\n    protected function decrypt($data, $passwordBased, $secret, $info)\n    {\n        if (!extension_loaded('openssl')) {\n            throw new InvalidConfigException('Encryption requires the OpenSSL PHP extension');\n        }\n        if (!isset($this->allowedCiphers[$this->cipher][0], $this->allowedCiphers[$this->cipher][1])) {\n            throw new InvalidConfigException($this->cipher . ' is not an allowed cipher');\n        }\n\n        list($blockSize, $keySize) = $this->allowedCiphers[$this->cipher];\n\n        $keySalt = StringHelper::byteSubstr($data, 0, $keySize);\n        if ($passwordBased) {\n            $key = $this->pbkdf2($this->kdfHash, $secret, $keySalt, $this->derivationIterations, $keySize);\n        } else {\n            $key = $this->hkdf($this->kdfHash, $secret, $keySalt, $info, $keySize);\n        }\n\n        $authKey = $this->hkdf($this->kdfHash, $key, null, $this->authKeyInfo, $keySize);\n        $data = $this->validateData(StringHelper::byteSubstr($data, $keySize, null), $authKey);\n        if ($data === false) {\n            return false;\n        }\n\n        $iv = StringHelper::byteSubstr($data, 0, $blockSize);\n        $encrypted = StringHelper::byteSubstr($data, $blockSize, null);\n\n        $decrypted = openssl_decrypt($encrypted, $this->cipher, $key, OPENSSL_RAW_DATA, $iv);\n        if ($decrypted === false) {\n            throw new \\yii\\base\\Exception('OpenSSL failure on decryption: ' . openssl_error_string());\n        }\n\n        return $decrypted;\n    }\n\n    /**\n     * Derives a key from the given input key using the standard HKDF algorithm.\n     * Implements HKDF specified in [RFC 5869](https://tools.ietf.org/html/rfc5869).\n     * Recommend use one of the SHA-2 hash algorithms: sha224, sha256, sha384 or sha512.\n     * @param string $algo a hash algorithm supported by `hash_hmac()`, e.g. 'SHA-256'\n     * @param string $inputKey the source key\n     * @param string|null $salt the random salt\n     * @param string|null $info optional info to bind the derived key material to application-\n     * and context-specific information, e.g. a user ID or API version, see\n     * [RFC 5869](https://tools.ietf.org/html/rfc5869)\n     * @param int $length length of the output key in bytes. If 0, the output key is\n     * the length of the hash algorithm output.\n     * @throws InvalidArgumentException when HMAC generation fails.\n     * @return string the derived key\n     */\n    public function hkdf($algo, $inputKey, $salt = null, $info = null, $length = 0)\n    {\n        $outputKey = hash_hkdf((string)$algo, (string)$inputKey, $length, (string)$info, (string)$salt);\n        if ($outputKey === false) {\n            throw new InvalidArgumentException('Invalid parameters to hash_hkdf()');\n        }\n\n        return $outputKey;\n    }\n\n    /**\n     * Derives a key from the given password using the standard PBKDF2 algorithm.\n     * Implements HKDF2 specified in [RFC 2898](https://datatracker.ietf.org/doc/html/rfc2898#section-5.2)\n     * Recommend use one of the SHA-2 hash algorithms: sha224, sha256, sha384 or sha512.\n     * @param string $algo a hash algorithm supported by `hash_hmac()`, e.g. 'SHA-256'\n     * @param string $password the source password\n     * @param string $salt the random salt\n     * @param int $iterations the number of iterations of the hash algorithm. Set as high as\n     * possible to hinder dictionary password attacks.\n     * @param int $length length of the output key in bytes. If 0, the output key is\n     * the length of the hash algorithm output.\n     * @return string the derived key\n     * @throws InvalidArgumentException when hash generation fails due to invalid params given.\n     */\n    public function pbkdf2($algo, $password, $salt, $iterations, $length = 0)\n    {\n        $outputKey = hash_pbkdf2($algo, $password, $salt, $iterations, $length, true);\n        if ($outputKey === false) {\n            throw new InvalidArgumentException('Invalid parameters to hash_pbkdf2()');\n        }\n\n        return $outputKey;\n    }\n\n    /**\n     * Prefixes data with a keyed hash value so that it can later be detected if it is tampered.\n     * There is no need to hash inputs or outputs of [[encryptByKey()]] or [[encryptByPassword()]]\n     * as those methods perform the task.\n     * @param string $data the data to be protected\n     * @param string $key the secret key to be used for generating hash. Should be a secure\n     * cryptographic key.\n     * @param bool $rawHash whether the generated hash value is in raw binary format. If false, lowercase\n     * hex digits will be generated.\n     * @return string the data prefixed with the keyed hash\n     * @throws InvalidConfigException when HMAC generation fails.\n     * @see validateData()\n     * @see generateRandomKey()\n     * @see hkdf()\n     * @see pbkdf2()\n     */\n    public function hashData($data, $key, $rawHash = false)\n    {\n        $hash = hash_hmac($this->macHash, $data, $key, $rawHash);\n        if (!$hash) {\n            throw new InvalidConfigException('Failed to generate HMAC with hash algorithm: ' . $this->macHash);\n        }\n\n        return $hash . $data;\n    }\n\n    /**\n     * Validates if the given data is tampered.\n     * @param string $data the data to be validated. The data must be previously\n     * generated by [[hashData()]].\n     * @param string $key the secret key that was previously used to generate the hash for the data in [[hashData()]].\n     * function to see the supported hashing algorithms on your system. This must be the same\n     * as the value passed to [[hashData()]] when generating the hash for the data.\n     * @param bool $rawHash this should take the same value as when you generate the data using [[hashData()]].\n     * It indicates whether the hash value in the data is in binary format. If false, it means the hash value consists\n     * of lowercase hex digits only.\n     * hex digits will be generated.\n     * @return string|false the real data with the hash stripped off. False if the data is tampered.\n     * @throws InvalidConfigException when HMAC generation fails.\n     * @see hashData()\n     */\n    public function validateData($data, $key, $rawHash = false)\n    {\n        $test = @hash_hmac($this->macHash, '', '', $rawHash);\n        if (!$test) {\n            throw new InvalidConfigException('Failed to generate HMAC with hash algorithm: ' . $this->macHash);\n        }\n        $hashLength = StringHelper::byteLength($test);\n        if (StringHelper::byteLength($data) >= $hashLength) {\n            $hash = StringHelper::byteSubstr($data, 0, $hashLength);\n            $pureData = StringHelper::byteSubstr($data, $hashLength, null);\n\n            $calculatedHash = hash_hmac($this->macHash, $pureData, $key, $rawHash);\n\n            if ($this->compareString($hash, $calculatedHash)) {\n                return $pureData;\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * Generates specified number of random bytes.\n     * Note that output may not be ASCII.\n     * @see generateRandomString() if you need a string.\n     *\n     * @param int $length the number of bytes to generate\n     * @return string the generated random bytes\n     * @throws InvalidArgumentException if wrong length is specified\n     * @throws Exception on failure.\n     */\n    public function generateRandomKey($length = 32)\n    {\n        if (!is_int($length)) {\n            throw new InvalidArgumentException('First parameter ($length) must be an integer');\n        }\n\n        if ($length < 1) {\n            throw new InvalidArgumentException('First parameter ($length) must be greater than 0');\n        }\n\n        return random_bytes($length);\n    }\n\n    /**\n     * Generates a random string of specified length.\n     * The string generated matches [A-Za-z0-9_-]+ and is transparent to URL-encoding.\n     *\n     * @param int $length the length of the key in characters\n     * @return string the generated random key\n     * @throws Exception on failure.\n     */\n    public function generateRandomString($length = 32)\n    {\n        if (!is_int($length)) {\n            throw new InvalidArgumentException('First parameter ($length) must be an integer');\n        }\n\n        if ($length < 1) {\n            throw new InvalidArgumentException('First parameter ($length) must be greater than 0');\n        }\n\n        $bytes = $this->generateRandomKey($length);\n        return substr(StringHelper::base64UrlEncode($bytes), 0, $length);\n    }\n\n    /**\n     * Generates a secure hash from a password and a random salt.\n     *\n     * The generated hash can be stored in database.\n     * Later when a password needs to be validated, the hash can be fetched and passed\n     * to [[validatePassword()]]. For example,\n     *\n     * ```\n     * // generates the hash (usually done during user registration or when the password is changed)\n     * $hash = Yii::$app->getSecurity()->generatePasswordHash($password);\n     * // ...save $hash in database...\n     *\n     * // during login, validate if the password entered is correct using $hash fetched from database\n     * if (Yii::$app->getSecurity()->validatePassword($password, $hash)) {\n     *     // password is good\n     * } else {\n     *     // password is bad\n     * }\n     * ```\n     *\n     * @param string $password The password to be hashed.\n     * @param int|null $cost Cost parameter used by the Blowfish hash algorithm.\n     * The higher the value of cost,\n     * the longer it takes to generate the hash and to verify a password against it. Higher cost\n     * therefore slows down a brute-force attack. For best protection against brute-force attacks,\n     * set it to the highest value that is tolerable on production servers. The time taken to\n     * compute the hash doubles for every increment by one of $cost.\n     * @return string The password hash string. The output length\n     * might increase in future versions of PHP (https://www.php.net/manual/en/function.password-hash.php)\n     * @throws Exception on bad password parameter or cost parameter.\n     * @see validatePassword()\n     */\n    public function generatePasswordHash($password, $cost = null)\n    {\n        if ($cost === null) {\n            $cost = $this->passwordHashCost;\n        }\n\n        return password_hash($password, PASSWORD_DEFAULT, ['cost' => $cost]);\n    }\n\n    /**\n     * Verifies a password against a hash.\n     * @param string $password The password to verify.\n     * @param string $hash The hash to verify the password against.\n     * @return bool whether the password is correct.\n     * @throws InvalidArgumentException on bad password/hash parameters.\n     * @see generatePasswordHash()\n     */\n    public function validatePassword($password, $hash)\n    {\n        if (!is_string($password) || $password === '') {\n            throw new InvalidArgumentException('Password must be a string and cannot be empty.');\n        }\n\n        if (\n            !preg_match('/^\\$2[axy]\\$(\\d\\d)\\$[\\.\\/0-9A-Za-z]{22}/', $hash, $matches)\n            || $matches[1] < 4\n            || $matches[1] > 30\n        ) {\n            throw new InvalidArgumentException('Hash is invalid.');\n        }\n\n        return password_verify($password, $hash);\n    }\n\n    /**\n     * Generates a salt that can be used to generate a password hash.\n     *\n     * The PHP [crypt()](https://www.php.net/manual/en/function.crypt.php) built-in function\n     * requires, for the Blowfish hash algorithm, a salt string in a specific format:\n     * \"$2a$\", \"$2x$\" or \"$2y$\", a two digit cost parameter, \"$\", and 22 characters\n     * from the alphabet \"./0-9A-Za-z\".\n     *\n     * @param int $cost the cost parameter\n     * @return string the random salt value.\n     * @throws InvalidArgumentException if the cost parameter is out of the range of 4 to 31.\n     * @deprecated since 2.0.55. This method is no longer used internally\n     * as [[generatePasswordHash()]] now relies on `password_hash()`. Will be removed in 2.2.\n     */\n    protected function generateSalt($cost = 13)\n    {\n        $cost = (int) $cost;\n        if ($cost < 4 || $cost > 31) {\n            throw new InvalidArgumentException('Cost must be between 4 and 31.');\n        }\n\n        // Get a 20-byte random string\n        $rand = $this->generateRandomKey(20);\n        // Form the prefix that specifies Blowfish (bcrypt) algorithm and cost parameter.\n        $salt = sprintf('$2y$%02d$', $cost);\n        // Append the random salt data in the required base64 format.\n        $salt .= str_replace('+', '.', substr(base64_encode($rand), 0, 22));\n\n        return $salt;\n    }\n\n    /**\n     * Performs string comparison using timing attack resistant approach.\n     * @see https://codereview.stackexchange.com/q/13512\n     * @param string $expected string to compare.\n     * @param string $actual user-supplied string.\n     * @return bool whether strings are equal.\n     */\n    public function compareString($expected, $actual)\n    {\n        if (!is_string($expected)) {\n            throw new InvalidArgumentException('Expected expected value to be a string, ' . gettype($expected) . ' given.');\n        }\n\n        if (!is_string($actual)) {\n            throw new InvalidArgumentException('Expected actual value to be a string, ' . gettype($actual) . ' given.');\n        }\n\n        return hash_equals($expected, $actual);\n    }\n\n    /**\n     * Masks a token to make it uncompressible.\n     * Applies a random mask to the token and prepends the mask used to the result making the string always unique.\n     * Used to mitigate BREACH attack by randomizing how token is outputted on each request.\n     * @param string $token An unmasked token.\n     * @return string A masked token.\n     * @since 2.0.12\n     */\n    public function maskToken($token)\n    {\n        // The number of bytes in a mask is always equal to the number of bytes in a token.\n        $mask = $this->generateRandomKey(StringHelper::byteLength($token));\n        return StringHelper::base64UrlEncode($mask . ($mask ^ $token));\n    }\n\n    /**\n     * Unmasks a token previously masked by `maskToken`.\n     * @param string $maskedToken A masked token.\n     * @return string An unmasked token, or an empty string in case of token format is invalid.\n     * @since 2.0.12\n     */\n    public function unmaskToken($maskedToken)\n    {\n        $decoded = StringHelper::base64UrlDecode($maskedToken);\n        $length = StringHelper::byteLength($decoded) / 2;\n        // Check if the masked token has an even length.\n        if (!is_int($length)) {\n            return '';\n        }\n\n        return StringHelper::byteSubstr($decoded, $length, $length) ^ StringHelper::byteSubstr($decoded, 0, $length);\n    }\n}\n"
  },
  {
    "path": "framework/base/StaticInstanceInterface.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * StaticInstanceInterface is the interface for providing static instances to classes,\n * which can be used to obtain class meta information that can not be expressed in static methods.\n * For example: adjustments made by DI or behaviors reveal only at object level, but might be needed\n * at class (static) level as well.\n *\n * To implement the [[instance()]] method you may use [[StaticInstanceTrait]].\n *\n * @author Paul Klimov <klimov.paul@gmail.com>\n * @since 2.0.13\n * @see StaticInstanceTrait\n */\ninterface StaticInstanceInterface\n{\n    /**\n     * Returns static class instance, which can be used to obtain meta information.\n     * @param bool $refresh whether to re-create static instance even, if it is already cached.\n     * @return static class instance.\n     */\n    public static function instance($refresh = false);\n}\n"
  },
  {
    "path": "framework/base/StaticInstanceTrait.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\nuse Yii;\n\n/**\n * StaticInstanceTrait provides methods to satisfy [[StaticInstanceInterface]] interface.\n *\n * @see StaticInstanceInterface\n *\n * @author Paul Klimov <klimov.paul@gmail.com>\n * @since 2.0.13\n */\ntrait StaticInstanceTrait\n{\n    /**\n     * @var static[] static instances in format: `[className => object]`\n     */\n    private static $_instances = [];\n\n\n    /**\n     * Returns static class instance, which can be used to obtain meta information.\n     * @param bool $refresh whether to re-create static instance even, if it is already cached.\n     * @return static class instance.\n     */\n    public static function instance($refresh = false)\n    {\n        $className = get_called_class();\n        if ($refresh || !isset(self::$_instances[$className])) {\n            self::$_instances[$className] = Yii::createObject($className);\n        }\n        return self::$_instances[$className];\n    }\n}\n"
  },
  {
    "path": "framework/base/Theme.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\nuse Yii;\nuse yii\\helpers\\FileHelper;\n\n/**\n * Theme represents an application theme.\n *\n * When [[View]] renders a view file, it will check the [[View::theme|active theme]]\n * to see if there is a themed version of the view file exists. If so, the themed version will be rendered instead.\n *\n * A theme is a directory consisting of view files which are meant to replace their non-themed counterparts.\n *\n * Theme uses [[pathMap]] to achieve the view file replacement:\n *\n * 1. It first looks for a key in [[pathMap]] that is a substring of the given view file path;\n * 2. If such a key exists, the corresponding value will be used to replace the corresponding part\n *    in the view file path;\n * 3. It will then check if the updated view file exists or not. If so, that file will be used\n *    to replace the original view file.\n * 4. If Step 2 or 3 fails, the original view file will be used.\n *\n * For example, if [[pathMap]] is `['@app/views' => '@app/themes/basic']`,\n * then the themed version for a view file `@app/views/site/index.php` will be\n * `@app/themes/basic/site/index.php`.\n *\n * It is possible to map a single path to multiple paths. For example,\n *\n * ```\n * 'pathMap' => [\n *     '@app/views' => [\n *         '@app/themes/christmas',\n *         '@app/themes/basic',\n *     ],\n * ]\n * ```\n *\n * In this case, the themed version could be either `@app/themes/christmas/site/index.php` or\n * `@app/themes/basic/site/index.php`. The former has precedence over the latter if both files exist.\n *\n * To use a theme, you should configure the [[View::theme|theme]] property of the \"view\" application\n * component like the following:\n *\n * ```\n * 'view' => [\n *     'theme' => [\n *         'basePath' => '@app/themes/basic',\n *         'baseUrl' => '@web/themes/basic',\n *     ],\n * ],\n * ```\n *\n * The above configuration specifies a theme located under the \"themes/basic\" directory of the Web folder\n * that contains the entry script of the application. If your theme is designed to handle modules,\n * you may configure the [[pathMap]] property like described above.\n *\n * For more details and usage information on Theme, see the [guide article on theming](guide:output-theming).\n *\n * @property string $basePath The root path of this theme. All resources of this theme are located under this\n * directory.\n * @property string $baseUrl The base URL (without ending slash) for this theme. All resources of this theme\n * are considered to be under this base URL.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Theme extends Component\n{\n    /**\n     * @var array|null the mapping between view directories and their corresponding themed versions.\n     * This property is used by [[applyTo()]] when a view is trying to apply the theme.\n     * [Path aliases](guide:concept-aliases) can be used when specifying directories.\n     * If this property is empty or not set, a mapping [[Application::basePath]] to [[basePath]] will be used.\n     */\n    public $pathMap;\n\n    private $_baseUrl;\n\n\n    /**\n     * @return string the base URL (without ending slash) for this theme. All resources of this theme are considered\n     * to be under this base URL.\n     */\n    public function getBaseUrl()\n    {\n        return $this->_baseUrl;\n    }\n\n    /**\n     * @param string $url the base URL or [path alias](guide:concept-aliases) for this theme. All resources of this theme are considered\n     * to be under this base URL.\n     */\n    public function setBaseUrl($url)\n    {\n        $this->_baseUrl = $url === null ? null : rtrim(Yii::getAlias($url), '/');\n    }\n\n    private $_basePath;\n\n    /**\n     * @return string the root path of this theme. All resources of this theme are located under this directory.\n     * @see pathMap\n     */\n    public function getBasePath()\n    {\n        return $this->_basePath;\n    }\n\n    /**\n     * @param string $path the root path or [path alias](guide:concept-aliases) of this theme. All resources of this theme are located\n     * under this directory.\n     * @see pathMap\n     */\n    public function setBasePath($path)\n    {\n        $this->_basePath = Yii::getAlias($path);\n    }\n\n    /**\n     * Converts a file to a themed file if possible.\n     * If there is no corresponding themed file, the original file will be returned.\n     * @param string $path the file to be themed\n     * @return string the themed file, or the original file if the themed version is not available.\n     * @throws InvalidConfigException if [[basePath]] is not set\n     */\n    public function applyTo($path)\n    {\n        $pathMap = $this->pathMap;\n        if (empty($pathMap)) {\n            if (($basePath = $this->getBasePath()) === null) {\n                throw new InvalidConfigException('The \"basePath\" property must be set.');\n            }\n            $pathMap = [Yii::$app->getBasePath() => [$basePath]];\n        }\n        $path = FileHelper::normalizePath($path);\n        foreach ($pathMap as $from => $tos) {\n            $from = FileHelper::normalizePath(Yii::getAlias($from)) . DIRECTORY_SEPARATOR;\n            if (strpos($path, $from) === 0) {\n                $n = strlen($from);\n                foreach ((array) $tos as $to) {\n                    $to = FileHelper::normalizePath(Yii::getAlias($to)) . DIRECTORY_SEPARATOR;\n                    $file = $to . substr($path, $n);\n                    if (is_file($file)) {\n                        return $file;\n                    }\n                }\n            }\n        }\n\n        return $path;\n    }\n\n    /**\n     * Converts a relative URL into an absolute URL using [[baseUrl]].\n     * @param string $url the relative URL to be converted.\n     * @return string the absolute URL\n     * @throws InvalidConfigException if [[baseUrl]] is not set\n     */\n    public function getUrl($url)\n    {\n        if (($baseUrl = $this->getBaseUrl()) !== null) {\n            return $baseUrl . '/' . ltrim($url, '/');\n        }\n\n        throw new InvalidConfigException('The \"baseUrl\" property must be set.');\n    }\n\n    /**\n     * Converts a relative file path into an absolute one using [[basePath]].\n     * @param string $path the relative file path to be converted.\n     * @return string the absolute file path\n     * @throws InvalidConfigException if [[basePath]] is not set\n     */\n    public function getPath($path)\n    {\n        if (($basePath = $this->getBasePath()) !== null) {\n            return $basePath . DIRECTORY_SEPARATOR . ltrim($path, '/\\\\');\n        }\n\n        throw new InvalidConfigException('The \"basePath\" property must be set.');\n    }\n}\n"
  },
  {
    "path": "framework/base/UnknownClassException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * UnknownClassException represents an exception caused by using an unknown class.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass UnknownClassException extends Exception\n{\n    /**\n     * @return string the user-friendly name of this exception\n     */\n    public function getName()\n    {\n        return 'Unknown Class';\n    }\n}\n"
  },
  {
    "path": "framework/base/UnknownMethodException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * UnknownMethodException represents an exception caused by accessing an unknown object method.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass UnknownMethodException extends \\BadMethodCallException\n{\n    /**\n     * @return string the user-friendly name of this exception\n     */\n    public function getName()\n    {\n        return 'Unknown Method';\n    }\n}\n"
  },
  {
    "path": "framework/base/UnknownPropertyException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * UnknownPropertyException represents an exception caused by accessing unknown object properties.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass UnknownPropertyException extends Exception\n{\n    /**\n     * @return string the user-friendly name of this exception\n     */\n    public function getName()\n    {\n        return 'Unknown Property';\n    }\n}\n"
  },
  {
    "path": "framework/base/UserException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * UserException is the base class for exceptions that are meant to be shown to end users.\n * Such exceptions are often caused by mistakes of end users.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass UserException extends Exception\n{\n}\n"
  },
  {
    "path": "framework/base/View.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\nuse Yii;\nuse yii\\helpers\\FileHelper;\nuse yii\\widgets\\Block;\nuse yii\\widgets\\ContentDecorator;\nuse yii\\widgets\\FragmentCache;\n\n/**\n * View represents a view object in the MVC pattern.\n *\n * View provides a set of methods (e.g. [[render()]]) for rendering purpose.\n *\n * For more details and usage information on View, see the [guide article on views](guide:structure-views).\n *\n * @property-read DynamicContentAwareInterface[] $dynamicContents Class instances supporting dynamic contents.\n * @property-read string|bool $viewFile The view file currently being rendered. False if no view file is being\n * rendered.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass View extends Component implements DynamicContentAwareInterface\n{\n    /**\n     * @event Event an event that is triggered by [[beginPage()]].\n     */\n    public const EVENT_BEGIN_PAGE = 'beginPage';\n    /**\n     * @event Event an event that is triggered by [[endPage()]].\n     */\n    public const EVENT_END_PAGE = 'endPage';\n    /**\n     * @event ViewEvent an event that is triggered by [[renderFile()]] right before it renders a view file.\n     */\n    public const EVENT_BEFORE_RENDER = 'beforeRender';\n    /**\n     * @event ViewEvent an event that is triggered by [[renderFile()]] right after it renders a view file.\n     */\n    public const EVENT_AFTER_RENDER = 'afterRender';\n    /**\n     * @var ViewContextInterface the context under which the [[renderFile()]] method is being invoked.\n     */\n    public $context;\n    /**\n     * @var array custom parameters that are shared among view templates.\n     */\n    public $params = [];\n    /**\n     * @var array|null a list of available renderers indexed by their corresponding supported file extensions.\n     * Each renderer may be a view renderer object or the configuration for creating the renderer object.\n     * For example, the following configuration enables both Smarty and Twig view renderers:\n     *\n     * ```\n     * [\n     *     'tpl' => ['class' => 'yii\\smarty\\ViewRenderer'],\n     *     'twig' => ['class' => 'yii\\twig\\ViewRenderer'],\n     * ]\n     * ```\n     *\n     * If no renderer is available for the given view file, the view file will be treated as a normal PHP\n     * and rendered via [[renderPhpFile()]].\n     */\n    public $renderers;\n    /**\n     * @var string the default view file extension. This will be appended to view file names if they don't have file extensions.\n     */\n    public $defaultExtension = 'php';\n    /**\n     * @var Theme|array|string|null the theme object or the configuration for creating the theme object.\n     * If not set, it means theming is not enabled.\n     */\n    public $theme;\n    /**\n     * @var array a list of named output blocks. The keys are the block names and the values\n     * are the corresponding block content. You can call [[beginBlock()]] and [[endBlock()]]\n     * to capture small fragments of a view. They can be later accessed somewhere else\n     * through this property.\n     */\n    public $blocks;\n    /**\n     * @var array|DynamicContentAwareInterface[] a list of currently active dynamic content class instances.\n     * This property is used internally to implement the dynamic content caching feature. Do not modify it directly.\n     * @internal\n     * @deprecated Since 2.0.14. Do not use this property directly. Use methods [[getDynamicContents()]],\n     * [[pushDynamicContent()]], [[popDynamicContent()]] instead.\n     */\n    public $cacheStack = [];\n    /**\n     * @var array a list of placeholders for embedding dynamic contents. This property\n     * is used internally to implement the content caching feature. Do not modify it directly.\n     * @internal\n     * @deprecated Since 2.0.14. Do not use this property directly. Use methods [[getDynamicPlaceholders()]],\n     * [[setDynamicPlaceholders()]], [[addDynamicPlaceholder()]] instead.\n     */\n    public $dynamicPlaceholders = [];\n\n    /**\n     * @var array the view files currently being rendered. There may be multiple view files being\n     * rendered at a moment because one view may be rendered within another.\n     */\n    private $_viewFiles = [];\n\n\n    /**\n     * Initializes the view component.\n     */\n    public function init()\n    {\n        parent::init();\n        if (is_array($this->theme)) {\n            if (!isset($this->theme['class'])) {\n                $this->theme['class'] = 'yii\\base\\Theme';\n            }\n            $this->theme = Yii::createObject($this->theme);\n        } elseif (is_string($this->theme)) {\n            $this->theme = Yii::createObject($this->theme);\n        }\n    }\n\n    /**\n     * Renders a view.\n     *\n     * The view to be rendered can be specified in one of the following formats:\n     *\n     * - [path alias](guide:concept-aliases) (e.g. \"@app/views/site/index\");\n     * - absolute path within application (e.g. \"//site/index\"): the view name starts with double slashes.\n     *   The actual view file will be looked for under the [[Application::viewPath|view path]] of the application.\n     * - absolute path within current module (e.g. \"/site/index\"): the view name starts with a single slash.\n     *   The actual view file will be looked for under the [[Module::viewPath|view path]] of the [[Controller::module|current module]].\n     * - relative view (e.g. \"index\"): the view name does not start with `@` or `/`. The corresponding view file will be\n     *   looked for under the [[ViewContextInterface::getViewPath()|view path]] of the view `$context`.\n     *   If `$context` is not given, it will be looked for under the directory containing the view currently\n     *   being rendered (i.e., this happens when rendering a view within another view).\n     *\n     * @param string $view the view name.\n     * @param array $params the parameters (name-value pairs) that will be extracted and made available in the view file.\n     * @param object|null $context the context to be assigned to the view and can later be accessed via [[context]]\n     * in the view. If the context implements [[ViewContextInterface]], it may also be used to locate\n     * the view file corresponding to a relative view name.\n     * @return string the rendering result\n     * @throws ViewNotFoundException if the view file does not exist.\n     * @throws InvalidCallException if the view cannot be resolved.\n     * @see renderFile()\n     */\n    public function render($view, $params = [], $context = null)\n    {\n        $viewFile = $this->findViewFile($view, $context);\n        return $this->renderFile($viewFile, $params, $context);\n    }\n\n    /**\n     * Finds the view file based on the given view name.\n     * @param string $view the view name or the [path alias](guide:concept-aliases) of the view file. Please refer to [[render()]]\n     * on how to specify this parameter.\n     * @param object|null $context the context to be assigned to the view and can later be accessed via [[context]]\n     * in the view. If the context implements [[ViewContextInterface]], it may also be used to locate\n     * the view file corresponding to a relative view name.\n     * @return string the view file path. Note that the file may not exist.\n     * @throws InvalidCallException if a relative view name is given while there is no active context to\n     * determine the corresponding view file.\n     */\n    protected function findViewFile($view, $context = null)\n    {\n        if (strncmp($view, '@', 1) === 0) {\n            // e.g. \"@app/views/main\"\n            $file = Yii::getAlias($view);\n        } elseif (strncmp($view, '//', 2) === 0) {\n            // e.g. \"//layouts/main\"\n            $file = Yii::$app->getViewPath() . DIRECTORY_SEPARATOR . ltrim($view, '/');\n        } elseif (strncmp($view, '/', 1) === 0) {\n            // e.g. \"/site/index\"\n            if (Yii::$app->controller !== null) {\n                $file = Yii::$app->controller->module->getViewPath() . DIRECTORY_SEPARATOR . ltrim($view, '/');\n            } else {\n                throw new InvalidCallException(\"Unable to locate view file for view '$view': no active controller.\");\n            }\n        } elseif ($context instanceof ViewContextInterface) {\n            $file = $context->getViewPath() . DIRECTORY_SEPARATOR . $view;\n        } elseif (($currentViewFile = $this->getRequestedViewFile()) !== false) {\n            $file = dirname($currentViewFile) . DIRECTORY_SEPARATOR . $view;\n        } else {\n            throw new InvalidCallException(\"Unable to resolve view file for view '$view': no active view context.\");\n        }\n\n        if (pathinfo($file, PATHINFO_EXTENSION) !== '') {\n            return $file;\n        }\n        $path = $file . '.' . $this->defaultExtension;\n        if ($this->defaultExtension !== 'php' && !is_file($path)) {\n            $path = $file . '.php';\n        }\n\n        return $path;\n    }\n\n    /**\n     * Renders a view file.\n     *\n     * If [[theme]] is enabled (not null), it will try to render the themed version of the view file as long\n     * as it is available.\n     *\n     * The method will call [[FileHelper::localize()]] to localize the view file.\n     *\n     * If [[renderers|renderer]] is enabled (not null), the method will use it to render the view file.\n     * Otherwise, it will simply include the view file as a normal PHP file, capture its output and\n     * return it as a string.\n     *\n     * @param string $viewFile the view file. This can be either an absolute file path or an alias of it.\n     * @param array $params the parameters (name-value pairs) that will be extracted and made available in the view file.\n     * @param object|null $context the context that the view should use for rendering the view. If null,\n     * existing [[context]] will be used.\n     * @return string the rendering result\n     * @throws ViewNotFoundException if the view file does not exist\n     */\n    public function renderFile($viewFile, $params = [], $context = null)\n    {\n        $viewFile = $requestedFile = Yii::getAlias($viewFile);\n\n        if ($this->theme !== null) {\n            $viewFile = $this->theme->applyTo($viewFile);\n        }\n        if (is_file($viewFile)) {\n            $viewFile = FileHelper::localize($viewFile);\n        } else {\n            throw new ViewNotFoundException(\"The view file does not exist: $viewFile\");\n        }\n\n        $oldContext = $this->context;\n        if ($context !== null) {\n            $this->context = $context;\n        }\n        $output = '';\n        $this->_viewFiles[] = [\n            'resolved' => $viewFile,\n            'requested' => $requestedFile\n        ];\n\n        if ($this->beforeRender($viewFile, $params)) {\n            Yii::debug(\"Rendering view file: $viewFile\", __METHOD__);\n            $ext = pathinfo($viewFile, PATHINFO_EXTENSION);\n            if (isset($this->renderers[$ext])) {\n                if (is_array($this->renderers[$ext]) || is_string($this->renderers[$ext])) {\n                    $this->renderers[$ext] = Yii::createObject($this->renderers[$ext]);\n                }\n                /** @var ViewRenderer $renderer */\n                $renderer = $this->renderers[$ext];\n                $output = $renderer->render($this, $viewFile, $params);\n            } else {\n                $output = $this->renderPhpFile($viewFile, $params);\n            }\n            $this->afterRender($viewFile, $params, $output);\n        }\n\n        array_pop($this->_viewFiles);\n        $this->context = $oldContext;\n\n        return $output;\n    }\n\n    /**\n     * @return string|bool the view file currently being rendered. False if no view file is being rendered.\n     */\n    public function getViewFile()\n    {\n        return empty($this->_viewFiles) ? false : end($this->_viewFiles)['resolved'];\n    }\n\n    /**\n     * @return string|bool the requested view currently being rendered. False if no view file is being rendered.\n     * @since 2.0.16\n     */\n    protected function getRequestedViewFile()\n    {\n        return empty($this->_viewFiles) ? false : end($this->_viewFiles)['requested'];\n    }\n\n    /**\n     * This method is invoked right before [[renderFile()]] renders a view file.\n     * The default implementation will trigger the [[EVENT_BEFORE_RENDER]] event.\n     * If you override this method, make sure you call the parent implementation first.\n     * @param string $viewFile the view file to be rendered.\n     * @param array $params the parameter array passed to the [[render()]] method.\n     * @return bool whether to continue rendering the view file.\n     */\n    public function beforeRender($viewFile, $params)\n    {\n        $event = new ViewEvent([\n            'viewFile' => $viewFile,\n            'params' => $params,\n        ]);\n        $this->trigger(self::EVENT_BEFORE_RENDER, $event);\n\n        return $event->isValid;\n    }\n\n    /**\n     * This method is invoked right after [[renderFile()]] renders a view file.\n     * The default implementation will trigger the [[EVENT_AFTER_RENDER]] event.\n     * If you override this method, make sure you call the parent implementation first.\n     * @param string $viewFile the view file being rendered.\n     * @param array $params the parameter array passed to the [[render()]] method.\n     * @param string $output the rendering result of the view file. Updates to this parameter\n     * will be passed back and returned by [[renderFile()]].\n     */\n    public function afterRender($viewFile, $params, &$output)\n    {\n        if ($this->hasEventHandlers(self::EVENT_AFTER_RENDER)) {\n            $event = new ViewEvent([\n                'viewFile' => $viewFile,\n                'params' => $params,\n            ]);\n            $event->output =& $output;\n\n            $this->trigger(self::EVENT_AFTER_RENDER, $event);\n        }\n    }\n\n    /**\n     * Renders a view file as a PHP script.\n     *\n     * This method treats the view file as a PHP script and includes the file.\n     * It extracts the given parameters and makes them available in the view file.\n     * The method captures the output of the included view file and returns it as a string.\n     *\n     * This method should mainly be called by view renderer or [[renderFile()]].\n     *\n     * @param string $_file_ the view file.\n     * @param array $_params_ the parameters (name-value pairs) that will be extracted and made available in the view file.\n     * @return string the rendering result\n     * @throws \\Throwable\n     */\n    public function renderPhpFile($_file_, $_params_ = [])\n    {\n        $_obInitialLevel_ = ob_get_level();\n        ob_start();\n        ob_implicit_flush(false);\n        extract($_params_, EXTR_OVERWRITE);\n        try {\n            require $_file_;\n            return ob_get_clean();\n        } catch (\\Exception $e) {\n            while (ob_get_level() > $_obInitialLevel_) {\n                if (!@ob_end_clean()) {\n                    ob_clean();\n                }\n            }\n            throw $e;\n        } catch (\\Throwable $e) {\n            while (ob_get_level() > $_obInitialLevel_) {\n                if (!@ob_end_clean()) {\n                    ob_clean();\n                }\n            }\n            throw $e;\n        }\n    }\n\n    /**\n     * Renders dynamic content returned by the given PHP statements.\n     * This method is mainly used together with content caching (fragment caching and page caching)\n     * when some portions of the content (called *dynamic content*) should not be cached.\n     * The dynamic content must be returned by some PHP statements.\n     * @param string $statements the PHP statements for generating the dynamic content.\n     * @return string the placeholder of the dynamic content, or the dynamic content if there is no\n     * active content cache currently.\n     *\n     * Note that most methods that indirectly modify layout such as registerJS() or registerJSFile() do not\n     * work with dynamic rendering.\n     *\n     * @see https://github.com/yiisoft/yii2/issues/17673\n     */\n    public function renderDynamic($statements)\n    {\n        if (!empty($this->cacheStack)) {\n            $n = count($this->dynamicPlaceholders);\n            $placeholder = \"<![CDATA[YII-DYNAMIC-$n]]>\";\n            $this->addDynamicPlaceholder($placeholder, $statements);\n\n            return $placeholder;\n        }\n\n        return $this->evaluateDynamicContent($statements);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getDynamicPlaceholders()\n    {\n        return $this->dynamicPlaceholders;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function setDynamicPlaceholders($placeholders)\n    {\n        $this->dynamicPlaceholders = $placeholders;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function addDynamicPlaceholder($placeholder, $statements)\n    {\n        foreach ($this->cacheStack as $cache) {\n            if ($cache instanceof DynamicContentAwareInterface) {\n                $cache->addDynamicPlaceholder($placeholder, $statements);\n            } else {\n                // TODO: Remove in 2.1\n                $cache->dynamicPlaceholders[$placeholder] = $statements;\n            }\n        }\n        $this->dynamicPlaceholders[$placeholder] = $statements;\n    }\n\n    /**\n     * Evaluates the given PHP statements.\n     * This method is mainly used internally to implement dynamic content feature.\n     * @param string $statements the PHP statements to be evaluated.\n     * @return mixed the return value of the PHP statements.\n     */\n    public function evaluateDynamicContent($statements)\n    {\n        return eval($statements);\n    }\n\n    /**\n     * Returns a list of currently active dynamic content class instances.\n     * @return DynamicContentAwareInterface[] class instances supporting dynamic contents.\n     * @since 2.0.14\n     */\n    public function getDynamicContents()\n    {\n        return $this->cacheStack;\n    }\n\n    /**\n     * Adds a class instance supporting dynamic contents to the end of a list of currently active\n     * dynamic content class instances.\n     * @param DynamicContentAwareInterface $instance class instance supporting dynamic contents.\n     * @since 2.0.14\n     */\n    public function pushDynamicContent(DynamicContentAwareInterface $instance)\n    {\n        $this->cacheStack[] = $instance;\n    }\n\n    /**\n     * Removes a last class instance supporting dynamic contents from a list of currently active\n     * dynamic content class instances.\n     * @since 2.0.14\n     */\n    public function popDynamicContent()\n    {\n        array_pop($this->cacheStack);\n    }\n\n    /**\n     * Begins recording a block.\n     *\n     * This method is a shortcut to beginning [[Block]].\n     * @param string $id the block ID.\n     * @param bool $renderInPlace whether to render the block content in place.\n     * Defaults to false, meaning the captured block will not be displayed.\n     * @return Block the Block widget instance\n     */\n    public function beginBlock($id, $renderInPlace = false)\n    {\n        return Block::begin([\n            'id' => $id,\n            'renderInPlace' => $renderInPlace,\n            'view' => $this,\n        ]);\n    }\n\n    /**\n     * Ends recording a block.\n     */\n    public function endBlock()\n    {\n        Block::end();\n    }\n\n    /**\n     * Begins the rendering of content that is to be decorated by the specified view.\n     *\n     * This method can be used to implement nested layout. For example, a layout can be embedded\n     * in another layout file specified as '@app/views/layouts/base.php' like the following:\n     *\n     * ```\n     * <?php $this->beginContent('@app/views/layouts/base.php'); ?>\n     * //...layout content here...\n     * <?php $this->endContent(); ?>\n     * ```\n     *\n     * @param string $viewFile the view file that will be used to decorate the content enclosed by this widget.\n     * This can be specified as either the view file path or [path alias](guide:concept-aliases).\n     * @param array $params the variables (name => value) to be extracted and made available in the decorative view.\n     * @return ContentDecorator the ContentDecorator widget instance\n     * @see ContentDecorator\n     */\n    public function beginContent($viewFile, $params = [])\n    {\n        return ContentDecorator::begin([\n            'viewFile' => $viewFile,\n            'params' => $params,\n            'view' => $this,\n        ]);\n    }\n\n    /**\n     * Ends the rendering of content.\n     */\n    public function endContent()\n    {\n        ContentDecorator::end();\n    }\n\n    /**\n     * Begins fragment caching.\n     *\n     * This method will display cached content if it is available.\n     * If not, it will start caching and would expect an [[endCache()]]\n     * call to end the cache and save the content into cache.\n     * A typical usage of fragment caching is as follows,\n     *\n     * ```\n     * if ($this->beginCache($id)) {\n     *     // ...generate content here\n     *     $this->endCache();\n     * }\n     * ```\n     *\n     * @param string $id a unique ID identifying the fragment to be cached.\n     * @param array $properties initial property values for [[FragmentCache]]\n     * @return bool whether you should generate the content for caching.\n     * False if the cached version is available.\n     */\n    public function beginCache($id, $properties = [])\n    {\n        $properties['id'] = $id;\n        $properties['view'] = $this;\n        /** @var FragmentCache $cache */\n        $cache = FragmentCache::begin($properties);\n        if ($cache->getCachedContent() !== false) {\n            $this->endCache();\n\n            return false;\n        }\n\n        return true;\n    }\n\n    /**\n     * Ends fragment caching.\n     */\n    public function endCache()\n    {\n        FragmentCache::end();\n    }\n\n    /**\n     * Marks the beginning of a page.\n     */\n    public function beginPage()\n    {\n        ob_start();\n        ob_implicit_flush(false);\n\n        $this->trigger(self::EVENT_BEGIN_PAGE);\n    }\n\n    /**\n     * Marks the ending of a page.\n     */\n    public function endPage()\n    {\n        $this->trigger(self::EVENT_END_PAGE);\n        ob_end_flush();\n    }\n}\n"
  },
  {
    "path": "framework/base/ViewContextInterface.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * ViewContextInterface is the interface that should implemented by classes who want to support relative view names.\n *\n * The method [[getViewPath()]] should be implemented to return the view path that may be prefixed to a relative view name.\n *\n * @author Paul Klimov <klimov.paul@gmail.com>\n * @since 2.0\n */\ninterface ViewContextInterface\n{\n    /**\n     * @return string the view path that may be prefixed to a relative view name.\n     */\n    public function getViewPath();\n}\n"
  },
  {
    "path": "framework/base/ViewEvent.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * ViewEvent represents events triggered by the [[View]] component.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass ViewEvent extends Event\n{\n    /**\n     * @var string the view file being rendered.\n     */\n    public $viewFile;\n    /**\n     * @var array the parameter array passed to the [[View::render()]] method.\n     */\n    public $params;\n    /**\n     * @var string the rendering result of [[View::renderFile()]].\n     * Event handlers may modify this property and the modified output will be\n     * returned by [[View::renderFile()]]. This property is only used\n     * by [[View::EVENT_AFTER_RENDER]] event.\n     */\n    public $output;\n    /**\n     * @var bool whether to continue rendering the view file. Event handlers of\n     * [[View::EVENT_BEFORE_RENDER]] may set this property to decide whether\n     * to continue rendering the current view file.\n     */\n    public $isValid = true;\n}\n"
  },
  {
    "path": "framework/base/ViewNotFoundException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * ViewNotFoundException represents an exception caused by view file not found.\n *\n * @author Alexander Makarov\n * @since 2.0.10\n */\nclass ViewNotFoundException extends InvalidArgumentException\n{\n    /**\n     * @return string the user-friendly name of this exception\n     */\n    public function getName()\n    {\n        return 'View not Found';\n    }\n}\n"
  },
  {
    "path": "framework/base/ViewRenderer.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * ViewRenderer is the base class for view renderer classes.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nabstract class ViewRenderer extends Component\n{\n    /**\n     * Renders a view file.\n     *\n     * This method is invoked by [[View]] whenever it tries to render a view.\n     * Child classes must implement this method to render the given view file.\n     *\n     * @param View $view the view object used for rendering the file.\n     * @param string $file the view file.\n     * @param array $params the parameters to be passed to the view file.\n     * @return string the rendering result\n     */\n    abstract public function render($view, $file, $params);\n}\n"
  },
  {
    "path": "framework/base/Widget.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\nuse ReflectionClass;\nuse Yii;\n\n/**\n * Widget is the base class for widgets.\n *\n * For more details and usage information on Widget, see the [guide article on widgets](guide:structure-widgets).\n *\n * @property string|null $id ID of the widget. Note that the type of this property differs in getter and\n * setter. See [[getId()]] and [[setId()]] for details.\n * @property \\yii\\web\\View $view The view object that can be used to render views or view files. Note that the\n * type of this property differs in getter and setter. See [[getView()]] and [[setView()]] for details.\n * @property-read string $viewPath The directory containing the view files for this widget.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Widget extends Component implements ViewContextInterface\n{\n    /**\n     * @event Event an event that is triggered when the widget is initialized via [[init()]].\n     * @since 2.0.11\n     */\n    public const EVENT_INIT = 'init';\n    /**\n     * @event WidgetEvent an event raised right before executing a widget.\n     * You may set [[WidgetEvent::isValid]] to be false to cancel the widget execution.\n     * @since 2.0.11\n     */\n    public const EVENT_BEFORE_RUN = 'beforeRun';\n    /**\n     * @event WidgetEvent an event raised right after executing a widget.\n     * @since 2.0.11\n     */\n    public const EVENT_AFTER_RUN = 'afterRun';\n    /**\n     * @var int a counter used to generate [[id]] for widgets.\n     * @internal\n     */\n    public static $counter = 0;\n    /**\n     * @var string the prefix to the automatically generated widget IDs.\n     * @see getId()\n     */\n    public static $autoIdPrefix = 'w';\n    /**\n     * @var Widget[] the widgets that are currently being rendered (not ended). This property\n     * is maintained by [[begin()]] and [[end()]] methods.\n     * @internal\n     */\n    public static $stack = [];\n\n    /**\n     * @var string[] used widget classes that have been resolved to their actual class name.\n     */\n    private static $_resolvedClasses = [];\n\n\n    /**\n     * Initializes the object.\n     * This method is called at the end of the constructor.\n     * The default implementation will trigger an [[EVENT_INIT]] event.\n     */\n    public function init()\n    {\n        parent::init();\n        $this->trigger(self::EVENT_INIT);\n    }\n\n    /**\n     * Begins a widget.\n     * This method creates an instance of the calling class. It will apply the configuration\n     * to the created instance. A matching [[end()]] call should be called later.\n     * As some widgets may use output buffering, the [[end()]] call should be made in the same view\n     * to avoid breaking the nesting of output buffers.\n     * @param array $config name-value pairs that will be used to initialize the object properties\n     * @return static the newly created widget instance\n     * @see end()\n     */\n    public static function begin($config = [])\n    {\n        $config['class'] = get_called_class();\n        /** @var static $widget */\n        $widget = Yii::createObject($config);\n        self::$stack[] = $widget;\n        self::$_resolvedClasses[get_called_class()] = get_class($widget);\n\n        return $widget;\n    }\n\n    /**\n     * Ends a widget.\n     * Note that the rendering result of the widget is directly echoed out.\n     * @return static the widget instance that is ended.\n     * @throws InvalidCallException if [[begin()]] and [[end()]] calls are not properly nested\n     * @see begin()\n     */\n    public static function end()\n    {\n        if (!empty(self::$stack)) {\n            $widget = array_pop(self::$stack);\n\n            $calledClass = self::$_resolvedClasses[get_called_class()] ?? get_called_class();\n\n            if (get_class($widget) === $calledClass) {\n                /** @var static $widget */\n                if ($widget->beforeRun()) {\n                    $result = $widget->run();\n                    $result = $widget->afterRun($result);\n                    echo $result;\n                }\n\n                return $widget;\n            }\n\n            throw new InvalidCallException('Expecting end() of ' . get_class($widget) . ', found ' . get_called_class());\n        }\n\n        throw new InvalidCallException('Unexpected ' . get_called_class() . '::end() call. A matching begin() is not found.');\n    }\n\n    /**\n     * Creates a widget instance and runs it.\n     * The widget rendering result is returned by this method.\n     * @param array $config name-value pairs that will be used to initialize the object properties\n     * @return string the rendering result of the widget.\n     * @throws \\Throwable\n     */\n    public static function widget($config = [])\n    {\n        ob_start();\n        ob_implicit_flush(false);\n        try {\n            $config['class'] = get_called_class();\n            /** @var self $widget */\n            $widget = Yii::createObject($config);\n            $out = '';\n            if ($widget->beforeRun()) {\n                $result = $widget->run();\n                $out = $widget->afterRun($result);\n            }\n        } catch (\\Exception $e) {\n            // close the output buffer opened above if it has not been closed already\n            if (ob_get_level() > 0) {\n                ob_end_clean();\n            }\n            throw $e;\n        } catch (\\Throwable $e) {\n            // close the output buffer opened above if it has not been closed already\n            if (ob_get_level() > 0) {\n                ob_end_clean();\n            }\n            throw $e;\n        }\n\n        return ob_get_clean() . $out;\n    }\n\n    private $_id;\n\n    /**\n     * Returns the ID of the widget.\n     * @param bool $autoGenerate whether to generate an ID if it is not set previously\n     * @return string|null ID of the widget.\n     */\n    public function getId($autoGenerate = true)\n    {\n        if ($autoGenerate && $this->_id === null) {\n            $this->_id = static::$autoIdPrefix . static::$counter++;\n        }\n\n        return $this->_id;\n    }\n\n    /**\n     * Sets the ID of the widget.\n     * @param string $value id of the widget.\n     */\n    public function setId($value)\n    {\n        $this->_id = $value;\n    }\n\n    private $_view;\n\n    /**\n     * Returns the view object that can be used to render views or view files.\n     * The [[render()]] and [[renderFile()]] methods will use\n     * this view object to implement the actual view rendering.\n     * If not set, it will default to the \"view\" application component.\n     * @return \\yii\\web\\View the view object that can be used to render views or view files.\n     */\n    public function getView()\n    {\n        if ($this->_view === null) {\n            $this->_view = Yii::$app->getView();\n        }\n\n        return $this->_view;\n    }\n\n    /**\n     * Sets the view object to be used by this widget.\n     * @param View $view the view object that can be used to render views or view files.\n     */\n    public function setView($view)\n    {\n        $this->_view = $view;\n    }\n\n    /**\n     * Executes the widget.\n     *\n     * @return string|void the rendering result may be directly \"echoed\" or returned as a string\n     */\n    public function run()\n    {\n    }\n\n    /**\n     * Renders a view.\n     *\n     * The view to be rendered can be specified in one of the following formats:\n     *\n     * - [path alias](guide:concept-aliases) (e.g. \"@app/views/site/index\");\n     * - absolute path within application (e.g. \"//site/index\"): the view name starts with double slashes.\n     *   The actual view file will be looked for under the [[Application::viewPath|view path]] of the application.\n     * - absolute path within module (e.g. \"/site/index\"): the view name starts with a single slash.\n     *   The actual view file will be looked for under the [[Module::viewPath|view path]] of the currently\n     *   active module.\n     * - relative path (e.g. \"index\"): the actual view file will be looked for under [[viewPath]].\n     *\n     * If the view name does not contain a file extension, it will use the default one `.php`.\n     *\n     * @param string $view the view name.\n     * @param array $params the parameters (name-value pairs) that should be made available in the view.\n     * @return string the rendering result.\n     * @throws InvalidArgumentException if the view file does not exist.\n     */\n    public function render($view, $params = [])\n    {\n        return $this->getView()->render($view, $params, $this);\n    }\n\n    /**\n     * Renders a view file.\n     * @param string $file the view file to be rendered. This can be either a file path or a [path alias](guide:concept-aliases).\n     * @param array $params the parameters (name-value pairs) that should be made available in the view.\n     * @return string the rendering result.\n     * @throws InvalidArgumentException if the view file does not exist.\n     */\n    public function renderFile($file, $params = [])\n    {\n        return $this->getView()->renderFile($file, $params, $this);\n    }\n\n    /**\n     * Returns the directory containing the view files for this widget.\n     * The default implementation returns the 'views' subdirectory under the directory containing the widget class file.\n     * @return string the directory containing the view files for this widget.\n     */\n    public function getViewPath()\n    {\n        $class = new ReflectionClass($this);\n\n        return dirname($class->getFileName()) . DIRECTORY_SEPARATOR . 'views';\n    }\n\n    /**\n     * This method is invoked right before the widget is executed.\n     *\n     * The method will trigger the [[EVENT_BEFORE_RUN]] event. The return value of the method\n     * will determine whether the widget should continue to run.\n     *\n     * When overriding this method, make sure you call the parent implementation like the following:\n     *\n     * ```\n     * public function beforeRun()\n     * {\n     *     if (!parent::beforeRun()) {\n     *         return false;\n     *     }\n     *\n     *     // your custom code here\n     *\n     *     return true; // or false to not run the widget\n     * }\n     * ```\n     *\n     * @return bool whether the widget should continue to be executed.\n     * @since 2.0.11\n     */\n    public function beforeRun()\n    {\n        $event = new WidgetEvent();\n        $this->trigger(self::EVENT_BEFORE_RUN, $event);\n        return $event->isValid;\n    }\n\n    /**\n     * This method is invoked right after a widget is executed.\n     *\n     * The method will trigger the [[EVENT_AFTER_RUN]] event. The return value of the method\n     * will be used as the widget return value.\n     *\n     * If you override this method, your code should look like the following:\n     *\n     * ```\n     * public function afterRun($result)\n     * {\n     *     $result = parent::afterRun($result);\n     *     // your custom code here\n     *     return $result;\n     * }\n     * ```\n     *\n     * @param mixed $result the widget return result.\n     * @return mixed the processed widget result.\n     * @since 2.0.11\n     */\n    public function afterRun($result)\n    {\n        $event = new WidgetEvent();\n        $event->result = $result;\n        $this->trigger(self::EVENT_AFTER_RUN, $event);\n        return $event->result;\n    }\n}\n"
  },
  {
    "path": "framework/base/WidgetEvent.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\base;\n\n/**\n * WidgetEvent represents the event parameter used for a widget event.\n *\n * By setting the [[isValid]] property, one may control whether to continue running the widget.\n *\n * @author Petra Barus <petra.barus@gmail.com>\n * @since 2.0.11\n */\nclass WidgetEvent extends Event\n{\n    /**\n     * @var mixed the widget result. Event handlers may modify this property to change the widget result.\n     */\n    public $result;\n    /**\n     * @var bool whether to continue running the widget. Event handlers of\n     * [[Widget::EVENT_BEFORE_RUN]] may set this property to decide whether\n     * to continue running the current widget.\n     */\n    public $isValid = true;\n}\n"
  },
  {
    "path": "framework/base/package.json",
    "content": "{\n    \"name\": \"base\",\n    \"version\": \"1.0.0\",\n    \"dependencies\": {\n    }\n}\n"
  },
  {
    "path": "framework/behaviors/AttributeBehavior.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\behaviors;\n\nuse Closure;\nuse yii\\base\\Behavior;\nuse yii\\base\\Event;\nuse yii\\db\\ActiveRecord;\nuse yii\\db\\BaseActiveRecord;\n\n/**\n * AttributeBehavior automatically assigns a specified value to one or multiple attributes of an ActiveRecord\n * object when certain events happen.\n *\n * To use AttributeBehavior, configure the [[attributes]] property which should specify the list of attributes\n * that need to be updated and the corresponding events that should trigger the update. Then configure the\n * [[value]] property with a PHP callable whose return value will be used to assign to the current attribute(s).\n * For example,\n *\n * ```\n * use yii\\behaviors\\AttributeBehavior;\n *\n * public function behaviors()\n * {\n *     return [\n *         [\n *             'class' => AttributeBehavior::class,\n *             'attributes' => [\n *                 ActiveRecord::EVENT_BEFORE_INSERT => 'attribute1',\n *                 ActiveRecord::EVENT_BEFORE_UPDATE => 'attribute2',\n *             ],\n *             'value' => function ($event) {\n *                 return 'some value';\n *             },\n *         ],\n *     ];\n * }\n * ```\n *\n * Because attribute values will be set automatically by this behavior, they are usually not user input and should therefore\n * not be validated, i.e. they should not appear in the [[\\yii\\base\\Model::rules()|rules()]] method of the model.\n *\n * @author Luciano Baraglia <luciano.baraglia@gmail.com>\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of BaseActiveRecord = BaseActiveRecord\n * @extends Behavior<T>\n */\nclass AttributeBehavior extends Behavior\n{\n    /**\n     * @var array list of attributes that are to be automatically filled with the value specified via [[value]].\n     * The array keys are the ActiveRecord events upon which the attributes are to be updated,\n     * and the array values are the corresponding attribute(s) to be updated. You can use a string to represent\n     * a single attribute, or an array to represent a list of attributes. For example,\n     *\n     * ```\n     * [\n     *     ActiveRecord::EVENT_BEFORE_INSERT => ['attribute1', 'attribute2'],\n     *     ActiveRecord::EVENT_BEFORE_UPDATE => 'attribute2',\n     * ]\n     * ```\n     */\n    public $attributes = [];\n    /**\n     * @var mixed the value that will be assigned to the current attributes. This can be an anonymous function,\n     * callable in array format (e.g. `[$this, 'methodName']`), an [[\\yii\\db\\Expression|Expression]] object representing a DB expression\n     * (e.g. `new Expression('NOW()')`), scalar, string or an arbitrary value. If the former, the return value of the\n     * function will be assigned to the attributes.\n     * The signature of the function should be as follows,\n     *\n     * ```\n     * function ($event)\n     * {\n     *     // return value will be assigned to the attribute\n     * }\n     * ```\n     */\n    public $value;\n    /**\n     * @var bool whether to skip this behavior when the `$owner` has not been\n     * modified\n     * @since 2.0.8\n     */\n    public $skipUpdateOnClean = true;\n    /**\n     * @var bool whether to preserve non-empty attribute values.\n     * @since 2.0.13\n     */\n    public $preserveNonEmptyValues = false;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function events()\n    {\n        return array_fill_keys(\n            array_keys($this->attributes),\n            'evaluateAttributes'\n        );\n    }\n\n    /**\n     * Evaluates the attribute value and assigns it to the current attributes.\n     * @param Event $event\n     */\n    public function evaluateAttributes($event)\n    {\n        if (\n            $this->skipUpdateOnClean\n            && $event->name == ActiveRecord::EVENT_BEFORE_UPDATE\n            && empty($this->owner->dirtyAttributes)\n        ) {\n            return;\n        }\n\n        if (!empty($this->attributes[$event->name])) {\n            $attributes = (array) $this->attributes[$event->name];\n            $value = $this->getValue($event);\n            foreach ($attributes as $attribute) {\n                // ignore attribute names which are not string (e.g. when set by TimestampBehavior::updatedAtAttribute)\n                if (is_string($attribute)) {\n                    if ($this->preserveNonEmptyValues && !empty($this->owner->$attribute)) {\n                        continue;\n                    }\n                    $this->owner->$attribute = $value;\n                }\n            }\n        }\n    }\n\n    /**\n     * Returns the value for the current attributes.\n     * This method is called by [[evaluateAttributes()]]. Its return value will be assigned\n     * to the attributes corresponding to the triggering event.\n     * @param Event $event the event that triggers the current attribute updating.\n     * @return mixed the attribute value\n     */\n    protected function getValue($event)\n    {\n        if ($this->value instanceof Closure || (is_array($this->value) && is_callable($this->value))) {\n            return call_user_func($this->value, $event);\n        }\n\n        return $this->value;\n    }\n}\n"
  },
  {
    "path": "framework/behaviors/AttributeTypecastBehavior.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\behaviors;\n\nuse yii\\base\\Behavior;\nuse yii\\base\\InvalidArgumentException;\nuse yii\\base\\Model;\nuse yii\\db\\BaseActiveRecord;\nuse yii\\helpers\\StringHelper;\nuse yii\\validators\\BooleanValidator;\nuse yii\\validators\\NumberValidator;\nuse yii\\validators\\StringValidator;\n\n/**\n * AttributeTypecastBehavior provides an ability of automatic model attribute typecasting.\n * This behavior is very useful in case of usage of ActiveRecord for the schema-less databases like MongoDB or Redis.\n * It may also come in handy for regular [[\\yii\\db\\ActiveRecord]] or even [[\\yii\\base\\Model]], allowing to maintain\n * strict attribute types after model validation.\n *\n * This behavior should be attached to [[\\yii\\base\\Model]] or [[\\yii\\db\\BaseActiveRecord]] descendant.\n *\n * You should specify exact attribute types via [[attributeTypes]].\n *\n * For example:\n *\n * ```\n * use yii\\behaviors\\AttributeTypecastBehavior;\n *\n * class Item extends \\yii\\db\\ActiveRecord\n * {\n *     public function behaviors()\n *     {\n *         return [\n *             'typecast' => [\n *                 'class' => AttributeTypecastBehavior::class,\n *                 'attributeTypes' => [\n *                     'amount' => AttributeTypecastBehavior::TYPE_INTEGER,\n *                     'price' => AttributeTypecastBehavior::TYPE_FLOAT,\n *                     'is_active' => AttributeTypecastBehavior::TYPE_BOOLEAN,\n *                 ],\n *                 'typecastAfterValidate' => true,\n *                 'typecastBeforeSave' => false,\n *                 'typecastAfterFind' => false,\n *             ],\n *         ];\n *     }\n *\n *     // ...\n * }\n * ```\n *\n * Tip: you may left [[attributeTypes]] blank - in this case its value will be detected\n * automatically based on owner validation rules.\n * Following example will automatically create same [[attributeTypes]] value as it was configured at the above one:\n *\n * ```\n * use yii\\behaviors\\AttributeTypecastBehavior;\n *\n * class Item extends \\yii\\db\\ActiveRecord\n * {\n *\n *     public function rules()\n *     {\n *         return [\n *             ['amount', 'integer'],\n *             ['price', 'number'],\n *             ['is_active', 'boolean'],\n *         ];\n *     }\n *\n *     public function behaviors()\n *     {\n *         return [\n *             'typecast' => [\n *                 'class' => AttributeTypecastBehavior::class,\n *                 // 'attributeTypes' will be composed automatically according to `rules()`\n *             ],\n *         ];\n *     }\n *\n *     // ...\n * }\n * ```\n *\n * This behavior allows automatic attribute typecasting at following cases:\n *\n * - after successful model validation\n * - before model save (insert or update)\n * - after model find (found by query or refreshed)\n *\n * You may control automatic typecasting for particular case using fields [[typecastAfterValidate]],\n * [[typecastBeforeSave]] and [[typecastAfterFind]].\n * By default typecasting will be performed only after model validation.\n *\n * Note: you can manually trigger attribute typecasting anytime invoking [[typecastAttributes()]] method:\n *\n * ```\n * $model = new Item();\n * $model->price = '38.5';\n * $model->is_active = 1;\n * $model->typecastAttributes();\n * ```\n *\n * @author Paul Klimov <klimov.paul@gmail.com>\n * @since 2.0.10\n *\n * @template T of Model|BaseActiveRecord = Model|BaseActiveRecord\n * @extends Behavior<T>\n */\nclass AttributeTypecastBehavior extends Behavior\n{\n    public const TYPE_INTEGER = 'integer';\n    public const TYPE_FLOAT = 'float';\n    public const TYPE_BOOLEAN = 'boolean';\n    public const TYPE_STRING = 'string';\n    /**\n     * @var T|null the owner of this behavior.\n     */\n    public $owner;\n    /**\n     * @var array|null attribute typecast map in format: attributeName => type.\n     * Type can be set via PHP callable, which accept raw value as an argument and should return\n     * typecast result.\n     * For example:\n     *\n     * ```\n     * [\n     *     'amount' => 'integer',\n     *     'price' => 'float',\n     *     'is_active' => 'boolean',\n     *     'date' => function ($value) {\n     *         return ($value instanceof \\DateTime) ? $value->getTimestamp(): (int) $value;\n     *     },\n     * ]\n     * ```\n     *\n     * If not set, attribute type map will be composed automatically from the owner validation rules.\n     */\n    public $attributeTypes;\n    /**\n     * @var bool whether to skip typecasting of `null` values.\n     * If enabled attribute value which equals to `null` will not be type-casted (e.g. `null` remains `null`),\n     * otherwise it will be converted according to the type configured at [[attributeTypes]].\n     */\n    public $skipOnNull = true;\n    /**\n     * @var bool whether to perform typecasting after owner model validation.\n     * Note that typecasting will be performed only if validation was successful, e.g.\n     * owner model has no errors.\n     * Note that changing this option value will have no effect after this behavior has been attached to the model.\n     */\n    public $typecastAfterValidate = true;\n    /**\n     * @var bool whether to perform typecasting before saving owner model (insert or update).\n     * This option may be disabled in order to achieve better performance.\n     * For example, in case of [[\\yii\\db\\ActiveRecord]] usage, typecasting before save\n     * will grant no benefit an thus can be disabled.\n     * Note that changing this option value will have no effect after this behavior has been attached to the model.\n     */\n    public $typecastBeforeSave = false;\n    /**\n     * @var bool whether to perform typecasting after saving owner model (insert or update).\n     * This option may be disabled in order to achieve better performance.\n     * For example, in case of [[\\yii\\db\\ActiveRecord]] usage, typecasting after save\n     * will grant no benefit an thus can be disabled.\n     * Note that changing this option value will have no effect after this behavior has been attached to the model.\n     * @since 2.0.14\n     */\n    public $typecastAfterSave = false;\n    /**\n     * @var bool whether to perform typecasting after retrieving owner model data from\n     * the database (after find or refresh).\n     * This option may be disabled in order to achieve better performance.\n     * For example, in case of [[\\yii\\db\\ActiveRecord]] usage, typecasting after find\n     * will grant no benefit in most cases an thus can be disabled.\n     * Note that changing this option value will have no effect after this behavior has been attached to the model.\n     */\n    public $typecastAfterFind = false;\n\n    /**\n     * @var array internal static cache for auto detected [[attributeTypes]] values\n     * in format: ownerClassName => attributeTypes\n     */\n    private static $_autoDetectedAttributeTypes = [];\n\n\n    /**\n     * Clears internal static cache of auto detected [[attributeTypes]] values\n     * over all affected owner classes.\n     */\n    public static function clearAutoDetectedAttributeTypes()\n    {\n        self::$_autoDetectedAttributeTypes = [];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function attach($owner)\n    {\n        parent::attach($owner);\n\n        if ($this->attributeTypes === null) {\n            $ownerClass = get_class($this->owner);\n            if (!isset(self::$_autoDetectedAttributeTypes[$ownerClass])) {\n                self::$_autoDetectedAttributeTypes[$ownerClass] = $this->detectAttributeTypes();\n            }\n            $this->attributeTypes = self::$_autoDetectedAttributeTypes[$ownerClass];\n        }\n    }\n\n    /**\n     * Typecast owner attributes according to [[attributeTypes]].\n     * @param array|null $attributeNames list of attribute names that should be type-casted.\n     * If this parameter is empty, it means any attribute listed in the [[attributeTypes]]\n     * should be type-casted.\n     */\n    public function typecastAttributes($attributeNames = null)\n    {\n        $attributeTypes = [];\n\n        if ($attributeNames === null) {\n            $attributeTypes = $this->attributeTypes;\n        } else {\n            foreach ($attributeNames as $attribute) {\n                if (!isset($this->attributeTypes[$attribute])) {\n                    throw new InvalidArgumentException(\"There is no type mapping for '{$attribute}'.\");\n                }\n                $attributeTypes[$attribute] = $this->attributeTypes[$attribute];\n            }\n        }\n\n        foreach ($attributeTypes as $attribute => $type) {\n            $value = $this->owner->{$attribute};\n            if ($this->skipOnNull && $value === null) {\n                continue;\n            }\n            $this->owner->{$attribute} = $this->typecastValue($value, $type);\n        }\n    }\n\n    /**\n     * Casts the given value to the specified type.\n     * @param mixed $value value to be type-casted.\n     * @param string|callable $type type name or typecast callable.\n     * @return mixed typecast result.\n     */\n    protected function typecastValue($value, $type)\n    {\n        if (is_scalar($type)) {\n            if (is_object($value) && method_exists($value, '__toString')) {\n                $value = $value->__toString();\n            }\n\n            switch ($type) {\n                case self::TYPE_INTEGER:\n                    return (int) $value;\n                case self::TYPE_FLOAT:\n                    return (float) $value;\n                case self::TYPE_BOOLEAN:\n                    return (bool) $value;\n                case self::TYPE_STRING:\n                    if (is_float($value)) {\n                        return StringHelper::floatToString($value);\n                    }\n                    return (string) $value;\n            }\n\n            if (PHP_VERSION_ID >= 80100 && is_subclass_of($type, \\BackedEnum::class)) {\n                if ($value instanceof $type) {\n                    return $value;\n                }\n                return $type::from($value);\n            }\n\n            throw new InvalidArgumentException(\"Unsupported type '{$type}'\");\n        }\n\n        return call_user_func($type, $value);\n    }\n\n    /**\n     * Composes default value for [[attributeTypes]] from the owner validation rules.\n     * @return array attribute type map.\n     */\n    protected function detectAttributeTypes()\n    {\n        $attributeTypes = [];\n        foreach ($this->owner->getValidators() as $validator) {\n            $type = null;\n            if ($validator instanceof BooleanValidator) {\n                $type = self::TYPE_BOOLEAN;\n            } elseif ($validator instanceof NumberValidator) {\n                $type = $validator->integerOnly ? self::TYPE_INTEGER : self::TYPE_FLOAT;\n            } elseif ($validator instanceof StringValidator) {\n                $type = self::TYPE_STRING;\n            }\n\n            if ($type !== null) {\n                $attributeTypes += array_fill_keys($validator->getAttributeNames(), $type);\n            }\n        }\n\n        return $attributeTypes;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function events()\n    {\n        $events = [];\n\n        if ($this->typecastAfterValidate) {\n            $events[Model::EVENT_AFTER_VALIDATE] = 'afterValidate';\n        }\n        if ($this->typecastBeforeSave) {\n            $events[BaseActiveRecord::EVENT_BEFORE_INSERT] = 'beforeSave';\n            $events[BaseActiveRecord::EVENT_BEFORE_UPDATE] = 'beforeSave';\n        }\n        if ($this->typecastAfterSave) {\n            $events[BaseActiveRecord::EVENT_AFTER_INSERT] = 'afterSave';\n            $events[BaseActiveRecord::EVENT_AFTER_UPDATE] = 'afterSave';\n        }\n        if ($this->typecastAfterFind) {\n            $events[BaseActiveRecord::EVENT_AFTER_FIND] = 'afterFind';\n        }\n\n        return $events;\n    }\n\n    /**\n     * Handles owner 'afterValidate' event, ensuring attribute typecasting.\n     * @param \\yii\\base\\Event $event event instance.\n     */\n    public function afterValidate($event)\n    {\n        if (!$this->owner->hasErrors()) {\n            $this->typecastAttributes();\n        }\n    }\n\n    /**\n     * Handles owner 'beforeInsert' and 'beforeUpdate' events, ensuring attribute typecasting.\n     * @param \\yii\\base\\Event $event event instance.\n     */\n    public function beforeSave($event)\n    {\n        $this->typecastAttributes();\n    }\n\n    /**\n     * Handles owner 'afterInsert' and 'afterUpdate' events, ensuring attribute typecasting.\n     * @param \\yii\\base\\Event $event event instance.\n     * @since 2.0.14\n     */\n    public function afterSave($event)\n    {\n        $this->typecastAttributes();\n    }\n\n    /**\n     * Handles owner 'afterFind' event, ensuring attribute typecasting.\n     * @param \\yii\\base\\Event $event event instance.\n     */\n    public function afterFind($event)\n    {\n        $this->typecastAttributes();\n\n        $this->resetOldAttributes();\n    }\n\n    /**\n     * Resets the old values of the named attributes.\n     */\n    protected function resetOldAttributes()\n    {\n        if ($this->attributeTypes === null) {\n            return;\n        }\n\n        $attributes = array_keys($this->attributeTypes);\n\n        foreach ($attributes as $attribute) {\n            if ($this->owner->canSetOldAttribute($attribute)) {\n                $this->owner->setOldAttribute($attribute, $this->owner->{$attribute});\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "framework/behaviors/AttributesBehavior.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\behaviors;\n\nuse Closure;\nuse yii\\base\\Behavior;\nuse yii\\base\\Event;\nuse yii\\db\\ActiveRecord;\nuse yii\\db\\BaseActiveRecord;\n\n/**\n * AttributesBehavior automatically assigns values specified to one or multiple attributes of an ActiveRecord\n * object when certain events happen.\n *\n * To use AttributesBehavior, configure the [[attributes]] property which should specify the list of attributes\n * that need to be updated and the corresponding events that should trigger the update. Then configure the\n * value of enclosed arrays with a PHP callable whose return value will be used to assign to the current attribute.\n * For example,\n *\n * ```\n * use yii\\behaviors\\AttributesBehavior;\n *\n * public function behaviors()\n * {\n *     return [\n *         [\n *             'class' => AttributesBehavior::class,\n *             'attributes' => [\n *                 'attribute1' => [\n *                     ActiveRecord::EVENT_BEFORE_INSERT => new Expression('NOW()'),\n *                     ActiveRecord::EVENT_BEFORE_UPDATE => \\Yii::$app->formatter->asDatetime('2017-07-13'),\n *                 ],\n *                 'attribute2' => [\n *                     ActiveRecord::EVENT_BEFORE_VALIDATE => [$this, 'storeAttributes'],\n *                     ActiveRecord::EVENT_AFTER_VALIDATE => [$this, 'restoreAttributes'],\n *                 ],\n *                 'attribute3' => [\n *                     ActiveRecord::EVENT_BEFORE_VALIDATE => $fn2 = [$this, 'getAttribute2'],\n *                     ActiveRecord::EVENT_AFTER_VALIDATE => $fn2,\n *                 ],\n *                 'attribute4' => [\n *                     ActiveRecord::EVENT_BEFORE_DELETE => function ($event, $attribute) {\n *                         static::disabled() || $event->isValid = false;\n *                     },\n *                 ],\n *             ],\n *         ],\n *     ];\n * }\n * ```\n *\n * Because attribute values will be set automatically by this behavior, they are usually not user input and should therefore\n * not be validated, i.e. they should not appear in the [[\\yii\\base\\Model::rules()|rules()]] method of the model.\n *\n * @author Luciano Baraglia <luciano.baraglia@gmail.com>\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Bogdan Stepanenko <bscheshirwork@gmail.com>\n * @since 2.0.13\n *\n * @template T of BaseActiveRecord = BaseActiveRecord\n * @extends Behavior<T>\n */\nclass AttributesBehavior extends Behavior\n{\n    /**\n     * @var array list of attributes that are to be automatically filled with the values specified via enclosed arrays.\n     * The array keys are the ActiveRecord attributes upon which the events are to be updated,\n     * and the array values are the array of corresponding events(s). For this enclosed array:\n     * the array keys are the ActiveRecord events upon which the attributes are to be updated,\n     * and the array values are the value that will be assigned to the current attributes. This can be an anonymous function,\n     * callable in array format (e.g. `[$this, 'methodName']`), an [[\\yii\\db\\Expression|Expression]] object representing a DB expression\n     * (e.g. `new Expression('NOW()')`), scalar, string or an arbitrary value. If the former, the return value of the\n     * function will be assigned to the attributes.\n     *\n     * ```\n     * [\n     *   'attribute1' => [\n     *       ActiveRecord::EVENT_BEFORE_INSERT => new Expression('NOW()'),\n     *       ActiveRecord::EVENT_BEFORE_UPDATE => \\Yii::$app->formatter->asDatetime('2017-07-13'),\n     *   ],\n     *   'attribute2' => [\n     *       ActiveRecord::EVENT_BEFORE_VALIDATE => [$this, 'storeAttributes'],\n     *       ActiveRecord::EVENT_AFTER_VALIDATE => [$this, 'restoreAttributes'],\n     *   ],\n     *   'attribute3' => [\n     *       ActiveRecord::EVENT_BEFORE_VALIDATE => $fn2 = [$this, 'getAttribute2'],\n     *       ActiveRecord::EVENT_AFTER_VALIDATE => $fn2,\n     *   ],\n     *   'attribute4' => [\n     *       ActiveRecord::EVENT_BEFORE_DELETE => function ($event, $attribute) {\n     *           static::disabled() || $event->isValid = false;\n     *       },\n     *   ],\n     * ]\n     * ```\n     */\n    public $attributes = [];\n    /**\n     * @var array list of order of attributes that are to be automatically filled with the event.\n     * The array keys are the ActiveRecord events upon which the attributes are to be updated,\n     * and the array values are represent the order corresponding attributes.\n     * The rest of the attributes are processed at the end.\n     * If the [[attributes]] for this attribute do not specify this event, it is ignored\n     *\n     * ```\n     * [\n     *     ActiveRecord::EVENT_BEFORE_VALIDATE => ['attribute1', 'attribute2'],\n     *     ActiveRecord::EVENT_AFTER_VALIDATE => ['attribute2', 'attribute1'],\n     * ]\n     * ```\n     */\n    public $order = [];\n    /**\n     * @var bool whether to skip this behavior when the `$owner` has not been modified\n     */\n    public $skipUpdateOnClean = true;\n    /**\n     * @var bool whether to preserve non-empty attribute values.\n     */\n    public $preserveNonEmptyValues = false;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function events()\n    {\n        return array_fill_keys(\n            array_reduce($this->attributes, function ($carry, $item) {\n                return array_merge($carry, array_keys($item));\n            }, []),\n            'evaluateAttributes'\n        );\n    }\n\n    /**\n     * Evaluates the attributes values and assigns it to the current attributes.\n     * @param Event $event\n     */\n    public function evaluateAttributes($event)\n    {\n        if (\n            $this->skipUpdateOnClean\n            && $event->name === ActiveRecord::EVENT_BEFORE_UPDATE\n            && empty($this->owner->dirtyAttributes)\n        ) {\n            return;\n        }\n        $attributes = array_keys(array_filter($this->attributes, function ($carry) use ($event) {\n            return array_key_exists($event->name, $carry);\n        }));\n        if (!empty($this->order[$event->name])) {\n            $attributes = array_merge(\n                array_intersect((array) $this->order[$event->name], $attributes),\n                array_diff($attributes, (array) $this->order[$event->name])\n            );\n        }\n        foreach ($attributes as $attribute) {\n            if ($this->preserveNonEmptyValues && !empty($this->owner->$attribute)) {\n                continue;\n            }\n            $this->owner->$attribute = $this->getValue($attribute, $event);\n        }\n    }\n\n    /**\n     * Returns the value for the current attributes.\n     * This method is called by [[evaluateAttributes()]]. Its return value will be assigned\n     * to the target attribute corresponding to the triggering event.\n     * @param string $attribute target attribute name\n     * @param Event $event the event that triggers the current attribute updating.\n     * @return mixed the attribute value\n     */\n    protected function getValue($attribute, $event)\n    {\n        if (!isset($this->attributes[$attribute][$event->name])) {\n            return null;\n        }\n        $value = $this->attributes[$attribute][$event->name];\n        if ($value instanceof Closure || (is_array($value) && is_callable($value))) {\n            return $value($event, $attribute);\n        }\n\n        return $value;\n    }\n}\n"
  },
  {
    "path": "framework/behaviors/BlameableBehavior.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\behaviors;\n\nuse Yii;\nuse yii\\db\\BaseActiveRecord;\n\n/**\n * BlameableBehavior automatically fills the specified attributes with the current user ID.\n *\n * To use BlameableBehavior, insert the following code to your ActiveRecord class:\n *\n * ```\n * use yii\\behaviors\\BlameableBehavior;\n *\n * public function behaviors()\n * {\n *     return [\n *         BlameableBehavior::class,\n *     ];\n * }\n * ```\n *\n * By default, BlameableBehavior will fill the `created_by` and `updated_by` attributes with the current user ID\n * when the associated AR object is being inserted; it will fill the `updated_by` attribute\n * with the current user ID when the AR object is being updated.\n *\n * Because attribute values will be set automatically by this behavior, they are usually not user input and should therefore\n * not be validated, i.e. `created_by` and `updated_by` should not appear in the [[\\yii\\base\\Model::rules()|rules()]] method of the model.\n *\n * If your attribute names are different, you may configure the [[createdByAttribute]] and [[updatedByAttribute]]\n * properties like the following:\n *\n * ```\n * public function behaviors()\n * {\n *     return [\n *         [\n *             'class' => BlameableBehavior::class,\n *             'createdByAttribute' => 'author_id',\n *             'updatedByAttribute' => 'updater_id',\n *         ],\n *     ];\n * }\n * ```\n *\n * @author Luciano Baraglia <luciano.baraglia@gmail.com>\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Alexander Kochetov <creocoder@gmail.com>\n * @since 2.0\n *\n * @template T of BaseActiveRecord = BaseActiveRecord\n * @extends AttributeBehavior<T>\n */\nclass BlameableBehavior extends AttributeBehavior\n{\n    /**\n     * @var string the attribute that will receive current user ID value\n     * Set this property to false if you do not want to record the creator ID.\n     */\n    public $createdByAttribute = 'created_by';\n    /**\n     * @var string the attribute that will receive current user ID value\n     * Set this property to false if you do not want to record the updater ID.\n     */\n    public $updatedByAttribute = 'updated_by';\n    /**\n     * {@inheritdoc}\n     *\n     * In case, when the property is `null`, the value of `Yii::$app->user->id` will be used as the value.\n     */\n    public $value;\n    /**\n     * @var mixed Default value for cases when the user is guest\n     * @since 2.0.14\n     */\n    public $defaultValue;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n\n        if (empty($this->attributes)) {\n            $this->attributes = [\n                BaseActiveRecord::EVENT_BEFORE_INSERT => [$this->createdByAttribute, $this->updatedByAttribute],\n                BaseActiveRecord::EVENT_BEFORE_UPDATE => $this->updatedByAttribute,\n            ];\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     *\n     * In case, when the [[value]] property is `null`, the value of [[defaultValue]] will be used as the value.\n     */\n    protected function getValue($event)\n    {\n        if ($this->value === null && Yii::$app->has('user')) {\n            $userId = Yii::$app->get('user')->id;\n            if ($userId === null) {\n                return $this->getDefaultValue($event);\n            }\n\n            return $userId;\n        } elseif ($this->value === null) {\n            return $this->getDefaultValue($event);\n        }\n\n        return parent::getValue($event);\n    }\n\n    /**\n     * Get default value\n     * @param \\yii\\base\\Event $event\n     * @return array|mixed\n     * @since 2.0.14\n     */\n    protected function getDefaultValue($event)\n    {\n        if ($this->defaultValue instanceof \\Closure || (is_array($this->defaultValue) && is_callable($this->defaultValue))) {\n            return call_user_func($this->defaultValue, $event);\n        }\n\n        return $this->defaultValue;\n    }\n}\n"
  },
  {
    "path": "framework/behaviors/CacheableWidgetBehavior.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\behaviors;\n\nuse yii\\base\\Behavior;\nuse yii\\base\\InvalidConfigException;\nuse yii\\base\\Widget;\nuse yii\\base\\WidgetEvent;\nuse yii\\caching\\CacheInterface;\nuse yii\\caching\\Dependency;\nuse yii\\di\\Instance;\n\n/**\n * Cacheable widget behavior automatically caches widget contents according to duration and dependencies specified.\n *\n * The behavior may be used without any configuration if an application has `cache` component configured.\n * By default the widget will be cached for one minute.\n *\n * The following example will cache the posts widget for an indefinite duration until any post is modified.\n *\n * ```\n * use yii\\behaviors\\CacheableWidgetBehavior;\n *\n * public function behaviors()\n * {\n *     return [\n *         [\n *             'class' => CacheableWidgetBehavior::class,\n *             'cacheDuration' => 0,\n *             'cacheDependency' => [\n *                 'class' => 'yii\\caching\\DbDependency',\n *                 'sql' => 'SELECT MAX(updated_at) FROM posts',\n *             ],\n *         ],\n *     ];\n * }\n * ```\n *\n * @author Nikolay Oleynikov <oleynikovny@mail.ru>\n * @since 2.0.14\n *\n * @template T of Widget = Widget\n * @extends Behavior<Widget>\n */\nclass CacheableWidgetBehavior extends Behavior\n{\n    /**\n     * @var CacheInterface|string|array a cache object or a cache component ID\n     * or a configuration array for creating a cache object.\n     * Defaults to the `cache` application component.\n     */\n    public $cache = 'cache';\n    /**\n     * @var int cache duration in seconds.\n     * Set to `0` to indicate that the cached data will never expire.\n     * Defaults to 60 seconds or 1 minute.\n     */\n    public $cacheDuration = 60;\n    /**\n     * @var Dependency|array|null a cache dependency or a configuration array\n     * for creating a cache dependency or `null` meaning no cache dependency.\n     *\n     * For example,\n     *\n     * ```\n     * [\n     *     'class' => 'yii\\caching\\DbDependency',\n     *     'sql' => 'SELECT MAX(updated_at) FROM posts',\n     * ]\n     * ```\n     *\n     * would make the widget cache depend on the last modified time of all posts.\n     * If any post has its modification time changed, the cached content would be invalidated.\n     */\n    public $cacheDependency;\n    /**\n     * @var string[]|string an array of strings or a single string which would cause\n     * the variation of the content being cached (e.g. an application language, a GET parameter).\n     *\n     * The following variation setting will cause the content to be cached in different versions\n     * according to the current application language:\n     *\n     * ```\n     * [\n     *     Yii::$app->language,\n     * ]\n     * ```\n     */\n    public $cacheKeyVariations = [];\n    /**\n     * @var bool whether to enable caching or not. Allows to turn the widget caching\n     * on and off according to specific conditions.\n     * The following configuration will disable caching when a special GET parameter is passed:\n     *\n     * ```\n     * empty(Yii::$app->request->get('disable-caching'))\n     * ```\n     */\n    public $cacheEnabled = true;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function attach($owner)\n    {\n        parent::attach($owner);\n\n        $this->initializeEventHandlers();\n    }\n\n    /**\n     * Begins fragment caching. Prevents owner widget from execution\n     * if its contents can be retrieved from the cache.\n     *\n     * @param WidgetEvent $event `Widget::EVENT_BEFORE_RUN` event.\n     */\n    public function beforeRun($event)\n    {\n        $cacheKey = $this->getCacheKey();\n        $fragmentCacheConfiguration = $this->getFragmentCacheConfiguration();\n\n        if (!$this->owner->view->beginCache($cacheKey, $fragmentCacheConfiguration)) {\n            $event->isValid = false;\n        }\n    }\n\n    /**\n     * Outputs widget contents and ends fragment caching.\n     *\n     * @param WidgetEvent $event `Widget::EVENT_AFTER_RUN` event.\n     */\n    public function afterRun($event)\n    {\n        echo $event->result;\n        $event->result = null;\n\n        $this->owner->view->endCache();\n    }\n\n    /**\n     * Initializes widget event handlers.\n     */\n    private function initializeEventHandlers()\n    {\n        $this->owner->on(Widget::EVENT_BEFORE_RUN, [$this, 'beforeRun']);\n        $this->owner->on(Widget::EVENT_AFTER_RUN, [$this, 'afterRun']);\n    }\n\n    /**\n     * Returns the cache instance.\n     *\n     * @return CacheInterface cache instance.\n     * @throws InvalidConfigException if cache instance instantiation fails.\n     */\n    private function getCacheInstance()\n    {\n        $cacheInterface = 'yii\\caching\\CacheInterface';\n        return Instance::ensure($this->cache, $cacheInterface);\n    }\n\n    /**\n     * Returns the widget cache key.\n     *\n     * @return string[] an array of strings representing the cache key.\n     */\n    private function getCacheKey()\n    {\n        // `$cacheKeyVariations` may be a `string` and needs to be cast to an `array`.\n        $cacheKey = array_merge(\n            (array)get_class($this->owner),\n            (array)$this->cacheKeyVariations\n        );\n\n        return $cacheKey;\n    }\n\n    /**\n     * Returns a fragment cache widget configuration array.\n     *\n     * @return array a fragment cache widget configuration array.\n     */\n    private function getFragmentCacheConfiguration()\n    {\n        $cache = $this->getCacheInstance();\n        $fragmentCacheConfiguration = [\n            'cache' => $cache,\n            'duration' => $this->cacheDuration,\n            'dependency' => $this->cacheDependency,\n            'enabled' => $this->cacheEnabled,\n        ];\n\n        return $fragmentCacheConfiguration;\n    }\n}\n"
  },
  {
    "path": "framework/behaviors/OptimisticLockBehavior.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\behaviors;\n\nuse Yii;\nuse yii\\db\\BaseActiveRecord;\nuse yii\\base\\InvalidCallException;\nuse yii\\validators\\NumberValidator;\nuse yii\\helpers\\ArrayHelper;\n\n/**\n * OptimisticLockBehavior automatically upgrades a model's lock version using the column name\n * returned by [[\\yii\\db\\BaseActiveRecord::optimisticLock()|optimisticLock()]].\n *\n * Optimistic locking allows multiple users to access the same record for edits and avoids\n * potential conflicts. In case when a user attempts to save the record upon some staled data\n * (because another user has modified the data), a [[StaleObjectException]] exception will be thrown,\n * and the update or deletion is skipped.\n *\n * To use this behavior, first enable optimistic lock by following the steps listed in\n * [[\\yii\\db\\BaseActiveRecord::optimisticLock()|optimisticLock()]], remove the column name\n * holding the lock version from the [[\\yii\\base\\Model::rules()|rules()]] method of your\n * ActiveRecord class, then add the following code to it:\n *\n * ```\n * use yii\\behaviors\\OptimisticLockBehavior;\n *\n * public function behaviors()\n * {\n *     return [\n *         OptimisticLockBehavior::class,\n *     ];\n * }\n * ```\n *\n * By default, OptimisticLockBehavior will use [[\\yii\\web\\Request::getBodyParam()|getBodyParam()]] to parse\n * the submitted value or set it to 0 on any fail. That means a request not holding the version attribute\n * may achieve a first successful update to entity, but starting from there any further try should fail\n * unless the request is holding the expected version number.\n *\n * Once attached, internal use of the model class should also fail to save the record if the version number\n * isn't held by [[\\yii\\web\\Request::getBodyParam()|getBodyParam()]]. It may be useful to extend your model class,\n * enable optimistic lock in parent class by overriding [[\\yii\\db\\BaseActiveRecord::optimisticLock()|optimisticLock()]],\n * then attach the behavior to the child class so you can tie the parent model to internal use while linking the child model\n * holding this behavior to the controllers responsible of receiving end user inputs.\n * Alternatively, you can also configure the [[value]] property with a PHP callable to implement a different logic.\n *\n * OptimisticLockBehavior also provides a method named [[upgrade()]] that increases a model's\n * version by one, that may be useful when you need to mark an entity as stale among connected clients\n * and avoid any change to it until they load it again:\n *\n * ```\n * $model->upgrade();\n * ```\n *\n * @author Salem Ouerdani <tunecino@gmail.com>\n * @since 2.0.16\n * @see \\yii\\db\\BaseActiveRecord::optimisticLock() for details on how to enable optimistic lock.\n *\n * @template T of BaseActiveRecord = BaseActiveRecord\n * @extends AttributeBehavior<T>\n */\nclass OptimisticLockBehavior extends AttributeBehavior\n{\n    /**\n     * {@inheritdoc}\n     *\n     * In case of `null` value it will be directly parsed from [[\\yii\\web\\Request::getBodyParam()|getBodyParam()]] or set to 0.\n     */\n    public $value;\n    /**\n     * {@inheritdoc}\n     */\n    public $skipUpdateOnClean = false;\n\n    /**\n     * @var string the attribute name holding the version value.\n     */\n    private $_lockAttribute;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function attach($owner)\n    {\n        parent::attach($owner);\n\n        if (empty($this->attributes)) {\n            $lock = $this->getLockAttribute();\n            $this->attributes = array_fill_keys(array_keys($this->events()), $lock);\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function events()\n    {\n        return Yii::$app->request instanceof \\yii\\web\\Request ? [\n            BaseActiveRecord::EVENT_BEFORE_INSERT => 'evaluateAttributes',\n            BaseActiveRecord::EVENT_BEFORE_UPDATE => 'evaluateAttributes',\n            BaseActiveRecord::EVENT_BEFORE_DELETE => 'evaluateAttributes',\n        ] : [];\n    }\n\n    /**\n     * Returns the column name to hold the version value as defined in [[\\yii\\db\\BaseActiveRecord::optimisticLock()|optimisticLock()]].\n     * @return string the property name.\n     * @throws InvalidCallException if [[\\yii\\db\\BaseActiveRecord::optimisticLock()|optimisticLock()]] is not properly configured.\n     * @since 2.0.16\n     */\n    protected function getLockAttribute()\n    {\n        if ($this->_lockAttribute) {\n            return $this->_lockAttribute;\n        }\n\n        /** @var BaseActiveRecord $owner */\n        $owner = $this->owner;\n        $lock = $owner->optimisticLock();\n        if ($lock === null || $owner->hasAttribute($lock) === false) {\n            throw new InvalidCallException(\"Unable to get the optimistic lock attribute. Probably 'optimisticLock()' method is misconfigured.\");\n        }\n        $this->_lockAttribute = $lock;\n        return $lock;\n    }\n\n    /**\n     * {@inheritdoc}\n     *\n     * In case of `null`, value will be parsed from [[\\yii\\web\\Request::getBodyParam()|getBodyParam()]] or set to 0.\n     */\n    protected function getValue($event)\n    {\n        if ($this->value === null) {\n            $request = Yii::$app->getRequest();\n            $lock = $this->getLockAttribute();\n            $formName = $this->owner->formName();\n            $formValue = $formName ? ArrayHelper::getValue($request->getBodyParams(), $formName . '.' . $lock) : null;\n            $input = $formValue ?: $request->getBodyParam($lock);\n            $isValid = $input && (new NumberValidator())->validate($input);\n            return $isValid ? $input : 0;\n        }\n\n        return parent::getValue($event);\n    }\n\n    /**\n     * Upgrades the version value by one and stores it to database.\n     *\n     * ```\n     * $model->upgrade();\n     * ```\n     * @throws InvalidCallException if owner is a new record.\n     * @since 2.0.16\n     */\n    public function upgrade()\n    {\n        /** @var BaseActiveRecord $owner */\n        $owner = $this->owner;\n        if ($owner->getIsNewRecord()) {\n            throw new InvalidCallException('Upgrading the model version is not possible on a new record.');\n        }\n        $lock = $this->getLockAttribute();\n        $version = $owner->$lock ?: 0;\n        $owner->updateAttributes([$lock => $version + 1]);\n    }\n}\n"
  },
  {
    "path": "framework/behaviors/SluggableBehavior.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\behaviors;\n\nuse Yii;\nuse yii\\base\\InvalidConfigException;\nuse yii\\db\\BaseActiveRecord;\nuse yii\\helpers\\ArrayHelper;\nuse yii\\helpers\\Inflector;\nuse yii\\validators\\UniqueValidator;\n\n/**\n * SluggableBehavior automatically fills the specified attribute with a value that can be used a slug in a URL.\n *\n * Note: This behavior relies on php-intl extension for transliteration. If it is not installed it\n * falls back to replacements defined in [[\\yii\\helpers\\Inflector::$transliteration]].\n *\n * To use SluggableBehavior, insert the following code to your ActiveRecord class:\n *\n * ```\n * use yii\\behaviors\\SluggableBehavior;\n *\n * public function behaviors()\n * {\n *     return [\n *         [\n *             'class' => SluggableBehavior::class,\n *             'attribute' => 'title',\n *             // 'slugAttribute' => 'slug',\n *         ],\n *     ];\n * }\n * ```\n *\n * By default, SluggableBehavior will fill the `slug` attribute with a value that can be used a slug in a URL\n * when the associated AR object is being validated.\n *\n * Because attribute values will be set automatically by this behavior, they are usually not user input and should therefore\n * not be validated, i.e. the `slug` attribute should not appear in the [[\\yii\\base\\Model::rules()|rules()]] method of the model.\n *\n * If your attribute name is different, you may configure the [[slugAttribute]] property like the following:\n *\n * ```\n * public function behaviors()\n * {\n *     return [\n *         [\n *             'class' => SluggableBehavior::class,\n *             'slugAttribute' => 'alias',\n *         ],\n *     ];\n * }\n * ```\n *\n * @author Alexander Kochetov <creocoder@gmail.com>\n * @author Paul Klimov <klimov.paul@gmail.com>\n * @since 2.0\n *\n * @template T of BaseActiveRecord = BaseActiveRecord\n * @extends AttributeBehavior<T>\n */\nclass SluggableBehavior extends AttributeBehavior\n{\n    /**\n     * @var string the attribute that will receive the slug value\n     */\n    public $slugAttribute = 'slug';\n    /**\n     * @var string|array|null the attribute or list of attributes whose value will be converted into a slug\n     * or `null` meaning that the `$value` property will be used to generate a slug.\n     */\n    public $attribute;\n    /**\n     * @var callable|string|null the value that will be used as a slug. This can be an anonymous function\n     * or an arbitrary value or null. If the former, the return value of the function will be used as a slug.\n     * If `null` then the `$attribute` property will be used to generate a slug.\n     * The signature of the function should be as follows,\n     *\n     * ```\n     * function ($event)\n     * {\n     *     // return slug\n     * }\n     * ```\n     */\n    public $value;\n    /**\n     * @var bool whether to generate a new slug if it has already been generated before.\n     * If true, the behavior will not generate a new slug even if [[attribute]] is changed.\n     * @since 2.0.2\n     */\n    public $immutable = false;\n    /**\n     * @var bool whether to ensure generated slug value to be unique among owner class records.\n     * If enabled behavior will validate slug uniqueness automatically. If validation fails it will attempt\n     * generating unique slug value from based one until success.\n     */\n    public $ensureUnique = false;\n    /**\n     * @var bool whether to skip slug generation if [[attribute]] is null or an empty string.\n     * If true, the behaviour will not generate a new slug if [[attribute]] is null or an empty string.\n     * @since 2.0.13\n     */\n    public $skipOnEmpty = false;\n    /**\n     * @var array configuration for slug uniqueness validator. Parameter 'class' may be omitted - by default\n     * [[UniqueValidator]] will be used.\n     * @see UniqueValidator\n     */\n    public $uniqueValidator = [];\n    /**\n     * @var callable|null slug unique value generator. It is used in case [[ensureUnique]] enabled and generated\n     * slug is not unique. This should be a PHP callable with following signature:\n     *\n     * ```\n     * function ($baseSlug, $iteration, $model)\n     * {\n     *     // return uniqueSlug\n     * }\n     * ```\n     *\n     * If not set unique slug will be generated adding incrementing suffix to the base slug.\n     */\n    public $uniqueSlugGenerator;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n\n        if (empty($this->attributes)) {\n            $this->attributes = [BaseActiveRecord::EVENT_BEFORE_VALIDATE => $this->slugAttribute];\n        }\n\n        if ($this->attribute === null && $this->value === null) {\n            throw new InvalidConfigException('Either \"attribute\" or \"value\" property must be specified.');\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function getValue($event)\n    {\n        if (!$this->isNewSlugNeeded()) {\n            return $this->owner->{$this->slugAttribute};\n        }\n\n        if ($this->attribute !== null) {\n            $slugParts = [];\n            foreach ((array) $this->attribute as $attribute) {\n                $part = ArrayHelper::getValue($this->owner, $attribute);\n                if ($this->skipOnEmpty && $this->isEmpty($part)) {\n                    return $this->owner->{$this->slugAttribute};\n                }\n                $slugParts[] = $part;\n            }\n            $slug = $this->generateSlug($slugParts);\n        } else {\n            $slug = parent::getValue($event);\n        }\n\n        return $this->ensureUnique ? $this->makeUnique($slug) : $slug;\n    }\n\n    /**\n     * Checks whether the new slug generation is needed\n     * This method is called by [[getValue]] to check whether the new slug generation is needed.\n     * You may override it to customize checking.\n     * @return bool\n     * @since 2.0.7\n     */\n    protected function isNewSlugNeeded()\n    {\n        if (empty($this->owner->{$this->slugAttribute})) {\n            return true;\n        }\n\n        if ($this->immutable) {\n            return false;\n        }\n\n        if ($this->attribute === null) {\n            return true;\n        }\n\n        foreach ((array) $this->attribute as $attribute) {\n            if ($this->owner->isAttributeChanged($attribute)) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * This method is called by [[getValue]] to generate the slug.\n     * You may override it to customize slug generation.\n     * The default implementation calls [[\\yii\\helpers\\Inflector::slug()]] on the input strings\n     * concatenated by dashes (`-`).\n     * @param array $slugParts an array of strings that should be concatenated and converted to generate the slug value.\n     * @return string the conversion result.\n     */\n    protected function generateSlug($slugParts)\n    {\n        return Inflector::slug(implode('-', $slugParts));\n    }\n\n    /**\n     * This method is called by [[getValue]] when [[ensureUnique]] is true to generate the unique slug.\n     * Calls [[generateUniqueSlug]] until generated slug is unique and returns it.\n     * @param string $slug basic slug value\n     * @return string unique slug\n     * @see getValue\n     * @see generateUniqueSlug\n     * @since 2.0.7\n     */\n    protected function makeUnique($slug)\n    {\n        $uniqueSlug = $slug;\n        $iteration = 0;\n        while (!$this->validateSlug($uniqueSlug)) {\n            $iteration++;\n            $uniqueSlug = $this->generateUniqueSlug($slug, $iteration);\n        }\n\n        return $uniqueSlug;\n    }\n\n    /**\n     * Checks if given slug value is unique.\n     * @param string $slug slug value\n     * @return bool whether slug is unique.\n     */\n    protected function validateSlug($slug)\n    {\n        /** @var UniqueValidator $validator */\n        $validator = Yii::createObject(array_merge(\n            [\n                'class' => UniqueValidator::className(),\n            ],\n            $this->uniqueValidator\n        ));\n\n        /** @var BaseActiveRecord $model */\n        $model = clone $this->owner;\n        $model->clearErrors();\n        $model->{$this->slugAttribute} = $slug;\n\n        $validator->validateAttribute($model, $this->slugAttribute);\n        return !$model->hasErrors();\n    }\n\n    /**\n     * Generates slug using configured callback or increment of iteration.\n     * @param string $baseSlug base slug value\n     * @param int $iteration iteration number\n     * @return string new slug value\n     * @throws \\yii\\base\\InvalidConfigException\n     */\n    protected function generateUniqueSlug($baseSlug, $iteration)\n    {\n        if (is_callable($this->uniqueSlugGenerator)) {\n            return call_user_func($this->uniqueSlugGenerator, $baseSlug, $iteration, $this->owner);\n        }\n\n        return $baseSlug . '-' . ($iteration + 1);\n    }\n\n    /**\n     * Checks if $slugPart is empty string or null.\n     *\n     * @param string $slugPart One of attributes that is used for slug generation.\n     * @return bool whether $slugPart empty or not.\n     * @since 2.0.13\n     */\n    protected function isEmpty($slugPart)\n    {\n        return $slugPart === null || $slugPart === '';\n    }\n}\n"
  },
  {
    "path": "framework/behaviors/TimestampBehavior.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\behaviors;\n\nuse yii\\base\\InvalidCallException;\nuse yii\\db\\BaseActiveRecord;\n\n/**\n * TimestampBehavior automatically fills the specified attributes with the current timestamp.\n *\n * To use TimestampBehavior, insert the following code to your ActiveRecord class:\n *\n * ```\n * use yii\\behaviors\\TimestampBehavior;\n *\n * public function behaviors()\n * {\n *     return [\n *         TimestampBehavior::class,\n *     ];\n * }\n * ```\n *\n * By default, TimestampBehavior will fill the `created_at` and `updated_at` attributes with the current timestamp\n * when the associated AR object is being inserted; it will fill the `updated_at` attribute\n * with the timestamp when the AR object is being updated. The timestamp value is obtained by `time()`.\n *\n * Because attribute values will be set automatically by this behavior, they are usually not user input and should therefore\n * not be validated, i.e. `created_at` and `updated_at` should not appear in the [[\\yii\\base\\Model::rules()|rules()]] method of the model.\n *\n * For the above implementation to work with MySQL database, please declare the columns(`created_at`, `updated_at`) as int(11) for being UNIX timestamp.\n *\n * If your attribute names are different or you want to use a different way of calculating the timestamp,\n * you may configure the [[createdAtAttribute]], [[updatedAtAttribute]] and [[value]] properties like the following:\n *\n * ```\n * use yii\\db\\Expression;\n *\n * public function behaviors()\n * {\n *     return [\n *         [\n *             'class' => TimestampBehavior::class,\n *             'createdAtAttribute' => 'create_time',\n *             'updatedAtAttribute' => 'update_time',\n *             'value' => new Expression('NOW()'),\n *         ],\n *     ];\n * }\n * ```\n *\n * In case you use an [[\\yii\\db\\Expression]] object as in the example above, the attribute will not hold the timestamp value, but\n * the Expression object itself after the record has been saved. If you need the value from DB afterwards you should call\n * the [[\\yii\\db\\ActiveRecord::refresh()|refresh()]] method of the record.\n *\n * TimestampBehavior also provides a method named [[touch()]] that allows you to assign the current\n * timestamp to the specified attribute(s) and save them to the database. For example,\n *\n * ```\n * $model->touch('creation_time');\n * ```\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Alexander Kochetov <creocoder@gmail.com>\n * @since 2.0\n *\n * @template T of BaseActiveRecord = BaseActiveRecord\n * @extends AttributeBehavior<T>\n */\nclass TimestampBehavior extends AttributeBehavior\n{\n    /**\n     * @var string the attribute that will receive timestamp value\n     * Set this property to false if you do not want to record the creation time.\n     */\n    public $createdAtAttribute = 'created_at';\n    /**\n     * @var string the attribute that will receive timestamp value.\n     * Set this property to false if you do not want to record the update time.\n     */\n    public $updatedAtAttribute = 'updated_at';\n    /**\n     * {@inheritdoc}\n     *\n     * In case, when the value is `null`, the result of the PHP function [time()](https://www.php.net/manual/en/function.time.php)\n     * will be used as value.\n     */\n    public $value;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n\n        if (empty($this->attributes)) {\n            $this->attributes = [\n                BaseActiveRecord::EVENT_BEFORE_INSERT => [$this->createdAtAttribute, $this->updatedAtAttribute],\n                BaseActiveRecord::EVENT_BEFORE_UPDATE => $this->updatedAtAttribute,\n            ];\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     *\n     * In case, when the [[value]] is `null`, the result of the PHP function [time()](https://www.php.net/manual/en/function.time.php)\n     * will be used as value.\n     */\n    protected function getValue($event)\n    {\n        if ($this->value === null) {\n            return time();\n        }\n\n        return parent::getValue($event);\n    }\n\n    /**\n     * Updates a timestamp attribute to the current timestamp.\n     *\n     * ```\n     * $model->touch('lastVisit');\n     * ```\n     * @param string $attribute the name of the attribute to update.\n     * @throws InvalidCallException if owner is a new record (since version 2.0.6).\n     */\n    public function touch($attribute)\n    {\n        /** @var BaseActiveRecord $owner */\n        $owner = $this->owner;\n        if ($owner->getIsNewRecord()) {\n            throw new InvalidCallException('Updating the timestamp is not possible on a new record.');\n        }\n        $owner->updateAttributes(array_fill_keys((array) $attribute, $this->getValue(null)));\n    }\n}\n"
  },
  {
    "path": "framework/caching/ApcCache.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\caching;\n\nuse yii\\base\\InvalidConfigException;\n\n/**\n * ApcCache provides APC caching in terms of an application component.\n *\n * To use this application component, the [APC PHP extension](https://www.php.net/apc) must be loaded.\n * Alternatively [APCu PHP extension](https://www.php.net/apcu) could be used via setting `useApcu` to `true`.\n * In order to enable APC or APCu for CLI you should add \"apc.enable_cli = 1\" to your php.ini.\n *\n * See [[Cache]] for common cache operations that ApcCache supports.\n *\n * For more details and usage information on Cache, see the [guide article on caching](guide:caching-overview).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass ApcCache extends Cache\n{\n    /**\n     * @var bool whether to use apcu or apc as the underlying caching extension.\n     * If true, [apcu](https://pecl.php.net/package/apcu) will be used.\n     * If false, [apc](https://pecl.php.net/package/apc) will be used.\n     * Defaults to false.\n     * @since 2.0.7\n     */\n    public $useApcu = false;\n\n\n    /**\n     * Initializes this application component.\n     * It checks if extension required is loaded.\n     */\n    public function init()\n    {\n        parent::init();\n        $extension = $this->useApcu ? 'apcu' : 'apc';\n        if (!extension_loaded($extension)) {\n            throw new InvalidConfigException(\"ApcCache requires PHP $extension extension to be loaded.\");\n        }\n    }\n\n    /**\n     * Checks whether a specified key exists in the cache.\n     * This can be faster than getting the value from the cache if the data is big.\n     * Note that this method does not check whether the dependency associated\n     * with the cached data, if there is any, has changed. So a call to [[get]]\n     * may return false while exists returns true.\n     * @param mixed $key a key identifying the cached value. This can be a simple string or\n     * a complex data structure consisting of factors representing the key.\n     * @return bool true if a value exists in cache, false if the value is not in the cache or expired.\n     */\n    public function exists($key)\n    {\n        $key = $this->buildKey($key);\n\n        return $this->useApcu ? apcu_exists($key) : apc_exists($key);\n    }\n\n    /**\n     * Retrieves a value from cache with a specified key.\n     * This is the implementation of the method declared in the parent class.\n     * @param string $key a unique key identifying the cached value\n     * @return mixed|false the value stored in cache, false if the value is not in the cache or expired.\n     */\n    protected function getValue($key)\n    {\n        return $this->useApcu ? apcu_fetch($key) : apc_fetch($key);\n    }\n\n    /**\n     * Retrieves multiple values from cache with the specified keys.\n     * @param array $keys a list of keys identifying the cached values\n     * @return array a list of cached values indexed by the keys\n     */\n    protected function getValues($keys)\n    {\n        $values = $this->useApcu ? apcu_fetch($keys) : apc_fetch($keys);\n        return is_array($values) ? $values : [];\n    }\n\n    /**\n     * Stores a value identified by a key in cache.\n     * This is the implementation of the method declared in the parent class.\n     *\n     * @param string $key the key identifying the value to be cached\n     * @param mixed $value the value to be cached. Most often it's a string. If you have disabled [[serializer]],\n     * it could be something else.\n     * @param int $duration the number of seconds in which the cached value will expire. 0 means never expire.\n     * @return bool true if the value is successfully stored into cache, false otherwise.\n     */\n    protected function setValue($key, $value, $duration)\n    {\n        return $this->useApcu ? apcu_store($key, $value, $duration) : apc_store($key, $value, $duration);\n    }\n\n    /**\n     * Stores multiple key-value pairs in cache.\n     * @param array $data array where key corresponds to cache key while value\n     * @param int $duration the number of seconds in which the cached values will expire. 0 means never expire.\n     * @return array array of failed keys\n     */\n    protected function setValues($data, $duration)\n    {\n        $result = $this->useApcu ? apcu_store($data, null, $duration) : apc_store($data, null, $duration);\n        return is_array($result) ? array_keys($result) : [];\n    }\n\n    /**\n     * Stores a value identified by a key into cache if the cache does not contain this key.\n     * This is the implementation of the method declared in the parent class.\n     * @param string $key the key identifying the value to be cached\n     * @param mixed $value the value to be cached. Most often it's a string. If you have disabled [[serializer]],\n     * it could be something else.\n     * @param int $duration the number of seconds in which the cached value will expire. 0 means never expire.\n     * @return bool true if the value is successfully stored into cache, false otherwise\n     */\n    protected function addValue($key, $value, $duration)\n    {\n        return $this->useApcu ? apcu_add($key, $value, $duration) : apc_add($key, $value, $duration);\n    }\n\n    /**\n     * Adds multiple key-value pairs to cache.\n     * @param array $data array where key corresponds to cache key while value is the value stored\n     * @param int $duration the number of seconds in which the cached values will expire. 0 means never expire.\n     * @return array array of failed keys\n     */\n    protected function addValues($data, $duration)\n    {\n        $result = $this->useApcu ? apcu_add($data, null, $duration) : apc_add($data, null, $duration);\n        return is_array($result) ? array_keys($result) : [];\n    }\n\n    /**\n     * Deletes a value with the specified key from cache\n     * This is the implementation of the method declared in the parent class.\n     * @param string $key the key of the value to be deleted\n     * @return bool if no error happens during deletion\n     */\n    protected function deleteValue($key)\n    {\n        return $this->useApcu ? apcu_delete($key) : apc_delete($key);\n    }\n\n    /**\n     * Deletes all values from cache.\n     * This is the implementation of the method declared in the parent class.\n     * @return bool whether the flush operation was successful.\n     */\n    protected function flushValues()\n    {\n        return $this->useApcu ? apcu_clear_cache() : apc_clear_cache('user');\n    }\n}\n"
  },
  {
    "path": "framework/caching/ArrayCache.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\caching;\n\n/**\n * ArrayCache provides caching for the current request only by storing the values in an array.\n *\n * See [[Cache]] for common cache operations that ArrayCache supports.\n *\n * Unlike the [[Cache]], ArrayCache allows the expire parameter of [[set]], [[add]], [[multiSet]] and [[multiAdd]] to\n * be a floating point number, so you may specify the time in milliseconds (e.g. 0.1 will be 100 milliseconds).\n *\n * For enhanced performance of ArrayCache, you can disable serialization of the stored data by setting [[$serializer]] to `false`.\n *\n * For more details and usage information on Cache, see the [guide article on caching](guide:caching-overview).\n *\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0\n */\nclass ArrayCache extends Cache\n{\n    private $_cache = [];\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function exists($key)\n    {\n        $key = $this->buildKey($key);\n        return isset($this->_cache[$key]) && ($this->_cache[$key][1] === 0 || $this->_cache[$key][1] > microtime(true));\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function getValue($key)\n    {\n        if (isset($this->_cache[$key]) && ($this->_cache[$key][1] === 0 || $this->_cache[$key][1] > microtime(true))) {\n            return $this->_cache[$key][0];\n        }\n\n        return false;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function setValue($key, $value, $duration)\n    {\n        $this->_cache[$key] = [$value, $duration === 0 ? 0 : microtime(true) + $duration];\n        return true;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function addValue($key, $value, $duration)\n    {\n        if (isset($this->_cache[$key]) && ($this->_cache[$key][1] === 0 || $this->_cache[$key][1] > microtime(true))) {\n            return false;\n        }\n        $this->_cache[$key] = [$value, $duration === 0 ? 0 : microtime(true) + $duration];\n        return true;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function deleteValue($key)\n    {\n        unset($this->_cache[$key]);\n        return true;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function flushValues()\n    {\n        $this->_cache = [];\n        return true;\n    }\n}\n"
  },
  {
    "path": "framework/caching/Cache.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\caching;\n\nuse Yii;\nuse yii\\base\\Component;\nuse yii\\helpers\\StringHelper;\n\n/**\n * Cache is the base class for cache classes supporting different cache storage implementations.\n *\n * A data item can be stored in the cache by calling [[set()]] and be retrieved back\n * later (in the same or different request) by [[get()]]. In both operations,\n * a key identifying the data item is required. An expiration time and/or a [[Dependency|dependency]]\n * can also be specified when calling [[set()]]. If the data item expires or the dependency\n * changes at the time of calling [[get()]], the cache will return no data.\n *\n * A typical usage pattern of cache is like the following:\n *\n * ```\n * $key = 'demo';\n * $data = $cache->get($key);\n * if ($data === false) {\n *     // ...generate $data here...\n *     $cache->set($key, $data, $duration, $dependency);\n * }\n * ```\n *\n * Because Cache implements the [[\\ArrayAccess]] interface, it can be used like an array. For example,\n *\n * ```\n * $cache['foo'] = 'some data';\n * echo $cache['foo'];\n * ```\n *\n * Derived classes should implement the following methods which do the actual cache storage operations:\n *\n * - [[getValue()]]: retrieve the value with a key (if any) from cache\n * - [[setValue()]]: store the value with a key into cache\n * - [[addValue()]]: store the value only if the cache does not have this key before\n * - [[deleteValue()]]: delete the value with the specified key from cache\n * - [[flushValues()]]: delete all values from cache\n *\n * For more details and usage information on Cache, see the [guide article on caching](guide:caching-overview).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nabstract class Cache extends Component implements CacheInterface\n{\n    /**\n     * @var string a string prefixed to every cache key so that it is unique globally in the whole cache storage.\n     * It is recommended that you set a unique cache key prefix for each application if the same cache\n     * storage is being used by different applications.\n     *\n     * To ensure interoperability, only alphanumeric characters should be used.\n     */\n    public $keyPrefix;\n    /**\n     * @var array|null|false the functions used to serialize and unserialize cached data. Defaults to null, meaning\n     * using the default PHP `serialize()` and `unserialize()` functions. If you want to use some more efficient\n     * serializer (e.g. [igbinary](https://pecl.php.net/package/igbinary)), you may configure this property with\n     * a two-element array. The first element specifies the serialization function, and the second the deserialization\n     * function. If this property is set false, data will be directly sent to and retrieved from the underlying\n     * cache component without any serialization or deserialization. You should not turn off serialization if\n     * you are using [[Dependency|cache dependency]], because it relies on data serialization. Also, some\n     * implementations of the cache can not correctly save and retrieve data different from a string type.\n     */\n    public $serializer;\n    /**\n     * @var int default duration in seconds before a cache entry will expire. Default value is 0, meaning infinity.\n     * This value is used by [[set()]] if the duration is not explicitly given.\n     * @since 2.0.11\n     */\n    public $defaultDuration = 0;\n\n    /**\n     * @var bool whether [igbinary serialization](https://pecl.php.net/package/igbinary) is available or not.\n     */\n    private $_igbinaryAvailable = false;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        $this->_igbinaryAvailable = \\extension_loaded('igbinary');\n    }\n\n    /**\n     * Builds a normalized cache key from a given key.\n     *\n     * If the given key is a string containing alphanumeric characters only and no more than 32 characters,\n     * then the key will be returned back prefixed with [[keyPrefix]]. Otherwise, a normalized key\n     * is generated by serializing the given key, applying MD5 hashing, and prefixing with [[keyPrefix]].\n     *\n     * @param mixed $key the key to be normalized\n     * @return string the generated cache key\n     */\n    public function buildKey($key)\n    {\n        if (is_string($key)) {\n            $key = ctype_alnum($key) && StringHelper::byteLength($key) <= 32 ? $key : md5($key);\n        } else {\n            if ($this->_igbinaryAvailable) {\n                $serializedKey = igbinary_serialize($key);\n            } else {\n                $serializedKey = serialize($key);\n            }\n\n            $key = md5($serializedKey);\n        }\n\n        return $this->keyPrefix . $key;\n    }\n\n    /**\n     * Retrieves a value from cache with a specified key.\n     * @param mixed $key a key identifying the cached value. This can be a simple string or\n     * a complex data structure consisting of factors representing the key.\n     * @return mixed the value stored in cache, false if the value is not in the cache, expired,\n     * or the dependency associated with the cached data has changed.\n     */\n    public function get($key)\n    {\n        $key = $this->buildKey($key);\n        $value = $this->getValue($key);\n        if ($value === false || $this->serializer === false) {\n            return $value;\n        } elseif ($this->serializer === null) {\n            $value = unserialize((string)$value);\n        } else {\n            $value = call_user_func($this->serializer[1], $value);\n        }\n        if (is_array($value) && !($value[1] instanceof Dependency && $value[1]->isChanged($this))) {\n            return $value[0];\n        }\n\n        return false;\n    }\n\n    /**\n     * Checks whether a specified key exists in the cache.\n     * This can be faster than getting the value from the cache if the data is big.\n     * In case a cache does not support this feature natively, this method will try to simulate it\n     * but has no performance improvement over getting it.\n     * Note that this method does not check whether the dependency associated\n     * with the cached data, if there is any, has changed. So a call to [[get]]\n     * may return false while exists returns true.\n     * @param mixed $key a key identifying the cached value. This can be a simple string or\n     * a complex data structure consisting of factors representing the key.\n     * @return bool true if a value exists in cache, false if the value is not in the cache or expired.\n     */\n    public function exists($key)\n    {\n        $key = $this->buildKey($key);\n        $value = $this->getValue($key);\n\n        return $value !== false;\n    }\n\n    /**\n     * Retrieves multiple values from cache with the specified keys.\n     * Some caches (such as memcache, apc) allow retrieving multiple cached values at the same time,\n     * which may improve the performance. In case a cache does not support this feature natively,\n     * this method will try to simulate it.\n     *\n     * @param string[] $keys list of string keys identifying the cached values\n     * @return array list of cached values corresponding to the specified keys. The array\n     * is returned in terms of (key, value) pairs.\n     * If a value is not cached or expired, the corresponding array value will be false.\n     * @deprecated This method is an alias for [[multiGet()]] and will be removed in 2.1.0.\n     */\n    public function mget($keys)\n    {\n        return $this->multiGet($keys);\n    }\n\n    /**\n     * Retrieves multiple values from cache with the specified keys.\n     * Some caches (such as memcache, apc) allow retrieving multiple cached values at the same time,\n     * which may improve the performance. In case a cache does not support this feature natively,\n     * this method will try to simulate it.\n     * @param string[] $keys list of string keys identifying the cached values\n     * @return array list of cached values corresponding to the specified keys. The array\n     * is returned in terms of (key, value) pairs.\n     * If a value is not cached or expired, the corresponding array value will be false.\n     * @since 2.0.7\n     */\n    public function multiGet($keys)\n    {\n        $keyMap = [];\n        foreach ($keys as $key) {\n            $keyMap[$key] = $this->buildKey($key);\n        }\n        $values = $this->getValues(array_values($keyMap));\n        $results = [];\n        foreach ($keyMap as $key => $newKey) {\n            $results[$key] = false;\n            if (isset($values[$newKey])) {\n                if ($this->serializer === false) {\n                    $results[$key] = $values[$newKey];\n                } else {\n                    $value = $this->serializer === null ? unserialize($values[$newKey])\n                        : call_user_func($this->serializer[1], $values[$newKey]);\n\n                    if (is_array($value) && !($value[1] instanceof Dependency && $value[1]->isChanged($this))) {\n                        $results[$key] = $value[0];\n                    }\n                }\n            }\n        }\n\n        return $results;\n    }\n\n    /**\n     * Stores a value identified by a key into cache.\n     * If the cache already contains such a key, the existing value and\n     * expiration time will be replaced with the new ones, respectively.\n     *\n     * @param mixed $key a key identifying the value to be cached. This can be a simple string or\n     * a complex data structure consisting of factors representing the key.\n     * @param mixed $value the value to be cached\n     * @param int|null $duration default duration in seconds before the cache will expire. If not set,\n     * default [[defaultDuration]] value is used.\n     * @param Dependency|null $dependency dependency of the cached item. If the dependency changes,\n     * the corresponding value in the cache will be invalidated when it is fetched via [[get()]].\n     * This parameter is ignored if [[serializer]] is false.\n     * @return bool whether the value is successfully stored into cache\n     */\n    public function set($key, $value, $duration = null, $dependency = null)\n    {\n        if ($duration === null) {\n            $duration = $this->defaultDuration;\n        }\n\n        if ($dependency !== null && $this->serializer !== false) {\n            $dependency->evaluateDependency($this);\n        }\n        if ($this->serializer === null) {\n            $value = serialize([$value, $dependency]);\n        } elseif ($this->serializer !== false) {\n            $value = call_user_func($this->serializer[0], [$value, $dependency]);\n        }\n        $key = $this->buildKey($key);\n\n        return $this->setValue($key, $value, $duration);\n    }\n\n    /**\n     * Stores multiple items in cache. Each item contains a value identified by a key.\n     * If the cache already contains such a key, the existing value and\n     * expiration time will be replaced with the new ones, respectively.\n     *\n     * @param array $items the items to be cached, as key-value pairs.\n     * @param int|null $duration default duration in seconds before the cache will expire. If not set,\n     * default [[defaultDuration]] value is used.\n     * @param Dependency|null $dependency dependency of the cached items. If the dependency changes,\n     * the corresponding values in the cache will be invalidated when it is fetched via [[get()]].\n     * This parameter is ignored if [[serializer]] is false.\n     * @return array array of failed keys\n     * @deprecated This method is an alias for [[multiSet()]] and will be removed in 2.1.0.\n     */\n    public function mset($items, $duration = null, $dependency = null)\n    {\n        return $this->multiSet($items, $duration, $dependency);\n    }\n\n    /**\n     * Stores multiple items in cache. Each item contains a value identified by a key.\n     * If the cache already contains such a key, the existing value and\n     * expiration time will be replaced with the new ones, respectively.\n     *\n     * @param array $items the items to be cached, as key-value pairs.\n     * @param int|null $duration default duration in seconds before the cache will expire. If not set,\n     * default [[defaultDuration]] value is used.\n     * @param Dependency|null $dependency dependency of the cached items. If the dependency changes,\n     * the corresponding values in the cache will be invalidated when it is fetched via [[get()]].\n     * This parameter is ignored if [[serializer]] is false.\n     * @return array array of failed keys\n     * @since 2.0.7\n     */\n    public function multiSet($items, $duration = null, $dependency = null)\n    {\n        if ($duration === null) {\n            $duration = $this->defaultDuration;\n        }\n\n        $data = $this->prepareCacheData($items, $dependency);\n\n        return $this->setValues($data, $duration);\n    }\n\n    /**\n     * Stores multiple items in cache. Each item contains a value identified by a key.\n     * If the cache already contains such a key, the existing value and expiration time will be preserved.\n     *\n     * @param array $items the items to be cached, as key-value pairs.\n     * @param int $duration default number of seconds in which the cached values will expire. 0 means never expire.\n     * @param Dependency|null $dependency dependency of the cached items. If the dependency changes,\n     * the corresponding values in the cache will be invalidated when it is fetched via [[get()]].\n     * This parameter is ignored if [[serializer]] is false.\n     * @return array array of failed keys\n     * @deprecated This method is an alias for [[multiAdd()]] and will be removed in 2.1.0.\n     */\n    public function madd($items, $duration = 0, $dependency = null)\n    {\n        return $this->multiAdd($items, $duration, $dependency);\n    }\n\n    /**\n     * Stores multiple items in cache. Each item contains a value identified by a key.\n     * If the cache already contains such a key, the existing value and expiration time will be preserved.\n     *\n     * @param array $items the items to be cached, as key-value pairs.\n     * @param int $duration default number of seconds in which the cached values will expire. 0 means never expire.\n     * @param Dependency|null $dependency dependency of the cached items. If the dependency changes,\n     * the corresponding values in the cache will be invalidated when it is fetched via [[get()]].\n     * This parameter is ignored if [[serializer]] is false.\n     * @return array array of failed keys\n     * @since 2.0.7\n     */\n    public function multiAdd($items, $duration = 0, $dependency = null)\n    {\n        $data = $this->prepareCacheData($items, $dependency);\n\n        return $this->addValues($data, $duration);\n    }\n\n    /**\n     * Prepares data for caching by serializing values and evaluating dependencies.\n     *\n     * @param array $items The items to be cached.\n     * @param mixed $dependency The dependency to be evaluated.\n     *\n     * @return array The prepared data for caching.\n     */\n    private function prepareCacheData($items, $dependency)\n    {\n        if ($dependency !== null && $this->serializer !== false) {\n            $dependency->evaluateDependency($this);\n        }\n\n        $data = [];\n        foreach ($items as $key => $value) {\n            if ($this->serializer === null) {\n                $value = serialize([$value, $dependency]);\n            } elseif ($this->serializer !== false) {\n                $value = call_user_func($this->serializer[0], [$value, $dependency]);\n            }\n\n            $key = $this->buildKey($key);\n            $data[$key] = $value;\n        }\n\n        return $data;\n    }\n\n    /**\n     * Stores a value identified by a key into cache if the cache does not contain this key.\n     * Nothing will be done if the cache already contains the key.\n     * @param mixed $key a key identifying the value to be cached. This can be a simple string or\n     * a complex data structure consisting of factors representing the key.\n     * @param mixed $value the value to be cached\n     * @param int $duration the number of seconds in which the cached value will expire. 0 means never expire.\n     * @param Dependency|null $dependency dependency of the cached item. If the dependency changes,\n     * the corresponding value in the cache will be invalidated when it is fetched via [[get()]].\n     * This parameter is ignored if [[serializer]] is false.\n     * @return bool whether the value is successfully stored into cache\n     */\n    public function add($key, $value, $duration = 0, $dependency = null)\n    {\n        if ($dependency !== null && $this->serializer !== false) {\n            $dependency->evaluateDependency($this);\n        }\n        if ($this->serializer === null) {\n            $value = serialize([$value, $dependency]);\n        } elseif ($this->serializer !== false) {\n            $value = call_user_func($this->serializer[0], [$value, $dependency]);\n        }\n        $key = $this->buildKey($key);\n\n        return $this->addValue($key, $value, $duration);\n    }\n\n    /**\n     * Deletes a value with the specified key from cache.\n     * @param mixed $key a key identifying the value to be deleted from cache. This can be a simple string or\n     * a complex data structure consisting of factors representing the key.\n     * @return bool if no error happens during deletion\n     */\n    public function delete($key)\n    {\n        $key = $this->buildKey($key);\n\n        return $this->deleteValue($key);\n    }\n\n    /**\n     * Deletes all values from cache.\n     * Be careful of performing this operation if the cache is shared among multiple applications.\n     * @return bool whether the flush operation was successful.\n     */\n    public function flush()\n    {\n        return $this->flushValues();\n    }\n\n    /**\n     * Retrieves a value from cache with a specified key.\n     * This method should be implemented by child classes to retrieve the data\n     * from specific cache storage.\n     * @param string $key a unique key identifying the cached value\n     * @return mixed|false the value stored in cache, false if the value is not in the cache or expired. Most often\n     * value is a string. If you have disabled [[serializer]], it could be something else.\n     */\n    abstract protected function getValue($key);\n\n    /**\n     * Stores a value identified by a key in cache.\n     * This method should be implemented by child classes to store the data\n     * in specific cache storage.\n     * @param string $key the key identifying the value to be cached\n     * @param mixed $value the value to be cached. Most often it's a string. If you have disabled [[serializer]],\n     * it could be something else.\n     * @param int $duration the number of seconds in which the cached value will expire. 0 means never expire.\n     * @return bool true if the value is successfully stored into cache, false otherwise\n     */\n    abstract protected function setValue($key, $value, $duration);\n\n    /**\n     * Stores a value identified by a key into cache if the cache does not contain this key.\n     * This method should be implemented by child classes to store the data\n     * in specific cache storage.\n     * @param string $key the key identifying the value to be cached\n     * @param mixed $value the value to be cached. Most often it's a string. If you have disabled [[serializer]],\n     * it could be something else.\n     * @param int $duration the number of seconds in which the cached value will expire. 0 means never expire.\n     * @return bool true if the value is successfully stored into cache, false otherwise\n     */\n    abstract protected function addValue($key, $value, $duration);\n\n    /**\n     * Deletes a value with the specified key from cache\n     * This method should be implemented by child classes to delete the data from actual cache storage.\n     * @param string $key the key of the value to be deleted\n     * @return bool if no error happens during deletion\n     */\n    abstract protected function deleteValue($key);\n\n    /**\n     * Deletes all values from cache.\n     * Child classes may implement this method to realize the flush operation.\n     * @return bool whether the flush operation was successful.\n     */\n    abstract protected function flushValues();\n\n    /**\n     * Retrieves multiple values from cache with the specified keys.\n     * The default implementation calls [[getValue()]] multiple times to retrieve\n     * the cached values one by one. If the underlying cache storage supports multiget,\n     * this method should be overridden to exploit that feature.\n     * @param array $keys a list of keys identifying the cached values\n     * @return array a list of cached values indexed by the keys\n     */\n    protected function getValues($keys)\n    {\n        $results = [];\n        foreach ($keys as $key) {\n            $results[$key] = $this->getValue($key);\n        }\n\n        return $results;\n    }\n\n    /**\n     * Stores multiple key-value pairs in cache.\n     * The default implementation calls [[setValue()]] multiple times store values one by one. If the underlying cache\n     * storage supports multi-set, this method should be overridden to exploit that feature.\n     * @param array $data array where key corresponds to cache key while value is the value stored\n     * @param int $duration the number of seconds in which the cached values will expire. 0 means never expire.\n     * @return array array of failed keys\n     */\n    protected function setValues($data, $duration)\n    {\n        $failedKeys = [];\n        foreach ($data as $key => $value) {\n            if ($this->setValue($key, $value, $duration) === false) {\n                $failedKeys[] = $key;\n            }\n        }\n\n        return $failedKeys;\n    }\n\n    /**\n     * Adds multiple key-value pairs to cache.\n     * The default implementation calls [[addValue()]] multiple times add values one by one. If the underlying cache\n     * storage supports multi-add, this method should be overridden to exploit that feature.\n     * @param array $data array where key corresponds to cache key while value is the value stored.\n     * @param int $duration the number of seconds in which the cached values will expire. 0 means never expire.\n     * @return array array of failed keys\n     */\n    protected function addValues($data, $duration)\n    {\n        $failedKeys = [];\n        foreach ($data as $key => $value) {\n            if ($this->addValue($key, $value, $duration) === false) {\n                $failedKeys[] = $key;\n            }\n        }\n\n        return $failedKeys;\n    }\n\n    /**\n     * Returns whether there is a cache entry with a specified key.\n     * This method is required by the interface [[\\ArrayAccess]].\n     * @param string $key a key identifying the cached value\n     * @return bool\n     */\n    #[\\ReturnTypeWillChange]\n    public function offsetExists($key)\n    {\n        return $this->get($key) !== false;\n    }\n\n    /**\n     * Retrieves the value from cache with a specified key.\n     * This method is required by the interface [[\\ArrayAccess]].\n     * @param string $key a key identifying the cached value\n     * @return mixed the value stored in cache, false if the value is not in the cache or expired.\n     */\n    #[\\ReturnTypeWillChange]\n    public function offsetGet($key)\n    {\n        return $this->get($key);\n    }\n\n    /**\n     * Stores the value identified by a key into cache.\n     * If the cache already contains such a key, the existing value will be\n     * replaced with the new ones. To add expiration and dependencies, use the [[set()]] method.\n     * This method is required by the interface [[\\ArrayAccess]].\n     * @param string $key the key identifying the value to be cached\n     * @param mixed $value the value to be cached\n     */\n    #[\\ReturnTypeWillChange]\n    public function offsetSet($key, $value)\n    {\n        $this->set($key, $value);\n    }\n\n    /**\n     * Deletes the value with the specified key from cache\n     * This method is required by the interface [[\\ArrayAccess]].\n     * @param string $key the key of the value to be deleted\n     */\n    #[\\ReturnTypeWillChange]\n    public function offsetUnset($key)\n    {\n        $this->delete($key);\n    }\n\n    /**\n     * Method combines both [[set()]] and [[get()]] methods to retrieve value identified by a $key,\n     * or to store the result of $callable execution if there is no cache available for the $key.\n     *\n     * Usage example:\n     *\n     * ```\n     * public function getTopProducts($count = 10) {\n     *     $cache = $this->cache; // Could be Yii::$app->cache\n     *     return $cache->getOrSet(['top-n-products', 'n' => $count], function () use ($count) {\n     *         return Products::find()->mostPopular()->limit($count)->all();\n     *     }, 1000);\n     * }\n     * ```\n     *\n     * @param mixed $key a key identifying the value to be cached. This can be a simple string or\n     * a complex data structure consisting of factors representing the key.\n     * @param callable|\\Closure $callable the callable or closure that will be used to generate a value to be cached.\n     * If you use $callable that can return `false`, then keep in mind that [[getOrSet()]] may work inefficiently\n     * because the [[yii\\caching\\Cache::get()]] method uses `false` return value to indicate the data item is not found\n     * in the cache. Thus, caching of `false` value will lead to unnecessary internal calls.\n     * @param int|null $duration default duration in seconds before the cache will expire. If not set,\n     * [[defaultDuration]] value will be used.\n     * @param Dependency|null $dependency dependency of the cached item. If the dependency changes,\n     * the corresponding value in the cache will be invalidated when it is fetched via [[get()]].\n     * This parameter is ignored if [[serializer]] is `false`.\n     * @return mixed result of $callable execution\n     * @since 2.0.11\n     */\n    public function getOrSet($key, $callable, $duration = null, $dependency = null)\n    {\n        if (($value = $this->get($key)) !== false) {\n            return $value;\n        }\n\n        $value = call_user_func($callable, $this);\n        if (!$this->set($key, $value, $duration, $dependency)) {\n            Yii::warning('Failed to set cache value for key ' . json_encode($key), __METHOD__);\n        }\n\n        return $value;\n    }\n}\n"
  },
  {
    "path": "framework/caching/CacheInterface.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\caching;\n\n/**\n * CacheInterface is the base interface for cache.\n *\n * A data item can be stored in the cache by calling [[set()]] and be retrieved back\n * later (in the same or different request) by [[get()]]. In both operations,\n * a key identifying the data item is required. An expiration time and/or a [[Dependency|dependency]]\n * can also be specified when calling [[set()]]. If the data item expires or the dependency\n * changes at the time of calling [[get()]], the cache will return no data.\n *\n * A typical usage pattern of cache is like the following:\n *\n * ```\n * $key = 'demo';\n * $data = $cache->get($key);\n * if ($data === false) {\n *     // ...generate $data here...\n *     $cache->set($key, $data, $duration, $dependency);\n * }\n * ```\n *\n * Because CacheInterface extends the [[\\ArrayAccess]] interface, it can be used like an array. For example,\n *\n * ```\n * $cache['foo'] = 'some data';\n * echo $cache['foo'];\n * ```\n *\n * For more details and usage information on Cache, see the [guide article on caching](guide:caching-overview).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Dmitry Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.13. Previous framework versions used abstract class [[yii\\caching\\Cache]] as interface.\n *\n * @extends \\ArrayAccess<string, mixed>\n */\ninterface CacheInterface extends \\ArrayAccess\n{\n    /**\n     * Builds a normalized cache key from a given key.\n     *\n     * If the given key is a string containing alphanumeric characters only and no more than 32 characters,\n     * then the key will be returned back prefixed with [[keyPrefix]]. Otherwise, a normalized key\n     * is generated by serializing the given key, applying MD5 hashing, and prefixing with [[keyPrefix]].\n     *\n     * @param mixed $key the key to be normalized\n     * @return string the generated cache key\n     */\n    public function buildKey($key);\n\n    /**\n     * Retrieves a value from cache with a specified key.\n     * @param mixed $key a key identifying the cached value. This can be a simple string or\n     * a complex data structure consisting of factors representing the key.\n     * @return mixed the value stored in cache, false if the value is not in the cache, expired,\n     * or the dependency associated with the cached data has changed.\n     */\n    public function get($key);\n\n    /**\n     * Checks whether a specified key exists in the cache.\n     * This can be faster than getting the value from the cache if the data is big.\n     * In case a cache does not support this feature natively, this method will try to simulate it\n     * but has no performance improvement over getting it.\n     * Note that this method does not check whether the dependency associated\n     * with the cached data, if there is any, has changed. So a call to [[get]]\n     * may return false while exists returns true.\n     * @param mixed $key a key identifying the cached value. This can be a simple string or\n     * a complex data structure consisting of factors representing the key.\n     * @return bool true if a value exists in cache, false if the value is not in the cache or expired.\n     */\n    public function exists($key);\n\n    /**\n     * Retrieves multiple values from cache with the specified keys.\n     * Some caches (such as memcache, apc) allow retrieving multiple cached values at the same time,\n     * which may improve the performance. In case a cache does not support this feature natively,\n     * this method will try to simulate it.\n     * @param string[] $keys list of string keys identifying the cached values\n     * @return array list of cached values corresponding to the specified keys. The array\n     * is returned in terms of (key, value) pairs.\n     * If a value is not cached or expired, the corresponding array value will be false.\n     */\n    public function multiGet($keys);\n\n    /**\n     * Stores a value identified by a key into cache.\n     * If the cache already contains such a key, the existing value and\n     * expiration time will be replaced with the new ones, respectively.\n     *\n     * @param mixed $key a key identifying the value to be cached. This can be a simple string or\n     * a complex data structure consisting of factors representing the key.\n     * @param mixed $value the value to be cached\n     * @param int|null $duration default duration in seconds before the cache will expire. If not set,\n     * default [[defaultDuration]] value is used.\n     * @param Dependency|null $dependency dependency of the cached item. If the dependency changes,\n     * the corresponding value in the cache will be invalidated when it is fetched via [[get()]].\n     * This parameter is ignored if [[serializer]] is false.\n     * @return bool whether the value is successfully stored into cache\n     */\n    public function set($key, $value, $duration = null, $dependency = null);\n\n    /**\n     * Stores multiple items in cache. Each item contains a value identified by a key.\n     * If the cache already contains such a key, the existing value and\n     * expiration time will be replaced with the new ones, respectively.\n     *\n     * @param array $items the items to be cached, as key-value pairs.\n     * @param int|null $duration default duration in seconds before the cache will expire. If not set,\n     * default [[defaultDuration]] value is used.\n     * @param Dependency|null $dependency dependency of the cached items. If the dependency changes,\n     * the corresponding values in the cache will be invalidated when it is fetched via [[get()]].\n     * This parameter is ignored if [[serializer]] is false.\n     * @return array array of failed keys\n     */\n    public function multiSet($items, $duration = null, $dependency = null);\n\n    /**\n     * Stores a value identified by a key into cache if the cache does not contain this key.\n     * Nothing will be done if the cache already contains the key.\n     * @param mixed $key a key identifying the value to be cached. This can be a simple string or\n     * a complex data structure consisting of factors representing the key.\n     * @param mixed $value the value to be cached\n     * @param int $duration the number of seconds in which the cached value will expire. 0 means never expire.\n     * @param Dependency|null $dependency dependency of the cached item. If the dependency changes,\n     * the corresponding value in the cache will be invalidated when it is fetched via [[get()]].\n     * This parameter is ignored if [[serializer]] is false.\n     * @return bool whether the value is successfully stored into cache\n     */\n    public function add($key, $value, $duration = 0, $dependency = null);\n\n    /**\n     * Stores multiple items in cache. Each item contains a value identified by a key.\n     * If the cache already contains such a key, the existing value and expiration time will be preserved.\n     *\n     * @param array $items the items to be cached, as key-value pairs.\n     * @param int $duration default number of seconds in which the cached values will expire. 0 means never expire.\n     * @param Dependency|null $dependency dependency of the cached items. If the dependency changes,\n     * the corresponding values in the cache will be invalidated when it is fetched via [[get()]].\n     * This parameter is ignored if [[serializer]] is false.\n     * @return array array of failed keys\n     */\n    public function multiAdd($items, $duration = 0, $dependency = null);\n\n    /**\n     * Deletes a value with the specified key from cache.\n     * @param mixed $key a key identifying the value to be deleted from cache. This can be a simple string or\n     * a complex data structure consisting of factors representing the key.\n     * @return bool if no error happens during deletion\n     */\n    public function delete($key);\n\n    /**\n     * Deletes all values from cache.\n     * Be careful of performing this operation if the cache is shared among multiple applications.\n     * @return bool whether the flush operation was successful.\n     */\n    public function flush();\n\n    /**\n     * Method combines both [[set()]] and [[get()]] methods to retrieve value identified by a $key,\n     * or to store the result of $callable execution if there is no cache available for the $key.\n     *\n     * Usage example:\n     *\n     * ```\n     * public function getTopProducts($count = 10) {\n     *     $cache = $this->cache; // Could be Yii::$app->cache\n     *     return $cache->getOrSet(['top-n-products', 'n' => $count], function ($cache) use ($count) {\n     *         return Products::find()->mostPopular()->limit($count)->all();\n     *     }, 1000);\n     * }\n     * ```\n     * @template TResult of mixed\n     *\n     * @param mixed $key a key identifying the value to be cached. This can be a simple string or\n     * a complex data structure consisting of factors representing the key.\n     * @param callable(): TResult|\\Closure(): TResult $callable the callable or closure that will be used to generate a value to be cached.\n     * In case $callable returns `false`, the value will not be cached.\n     * @param int|null $duration default duration in seconds before the cache will expire. If not set,\n     * [[defaultDuration]] value will be used.\n     * @param Dependency|null $dependency dependency of the cached item. If the dependency changes,\n     * the corresponding value in the cache will be invalidated when it is fetched via [[get()]].\n     * This parameter is ignored if [[serializer]] is `false`.\n     * @return TResult result of $callable execution\n     */\n    public function getOrSet($key, $callable, $duration = null, $dependency = null);\n}\n"
  },
  {
    "path": "framework/caching/CallbackDependency.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\caching;\n\n/**\n * CallbackDependency represents a dependency based on the result of a callback function.\n *\n * Callback function should return a value that serves as the dependency data.\n *\n * For more details and usage information on Cache, see the [guide article on caching](guide:caching-overview).\n *\n * @author Vlad Varlamov <vlad@varlamov.dev>\n * @since 2.0.50\n */\nclass CallbackDependency extends Dependency\n{\n    /**\n     * @var callable the PHP callback that will be called to determine if the dependency has been changed.\n     */\n    public $callback;\n\n\n    /**\n     * Generates the data needed to determine if dependency has been changed.\n     * This method returns the result of the callback function.\n     * @param CacheInterface $cache the cache component that is currently evaluating this dependency\n     * @return mixed the data needed to determine if dependency has been changed.\n     */\n    protected function generateDependencyData($cache)\n    {\n        return ($this->callback)();\n    }\n}\n"
  },
  {
    "path": "framework/caching/ChainedDependency.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\caching;\n\n/**\n * ChainedDependency represents a dependency which is composed of a list of other dependencies.\n *\n * When [[dependOnAll]] is true, if any of the dependencies has changed, this dependency is\n * considered changed; When [[dependOnAll]] is false, if one of the dependencies has NOT changed,\n * this dependency is considered NOT changed.\n *\n * For more details and usage information on Cache, see the [guide article on caching](guide:caching-overview).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass ChainedDependency extends Dependency\n{\n    /**\n     * @var Dependency[] list of dependencies that this dependency is composed of.\n     * Each array element must be a dependency object.\n     */\n    public $dependencies = [];\n    /**\n     * @var bool whether this dependency is depending on every dependency in [[dependencies]].\n     * Defaults to true, meaning if any of the dependencies has changed, this dependency is considered changed.\n     * When it is set false, it means if one of the dependencies has NOT changed, this dependency\n     * is considered NOT changed.\n     */\n    public $dependOnAll = true;\n\n\n    /**\n     * Evaluates the dependency by generating and saving the data related with dependency.\n     * @param CacheInterface $cache the cache component that is currently evaluating this dependency\n     */\n    public function evaluateDependency($cache)\n    {\n        foreach ($this->dependencies as $dependency) {\n            $dependency->evaluateDependency($cache);\n        }\n    }\n\n    /**\n     * Generates the data needed to determine if dependency has been changed.\n     * This method does nothing in this class.\n     * @param CacheInterface $cache the cache component that is currently evaluating this dependency\n     * @return mixed the data needed to determine if dependency has been changed.\n     */\n    protected function generateDependencyData($cache)\n    {\n        return null;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function isChanged($cache)\n    {\n        foreach ($this->dependencies as $dependency) {\n            if ($this->dependOnAll && $dependency->isChanged($cache)) {\n                return true;\n            } elseif (!$this->dependOnAll && !$dependency->isChanged($cache)) {\n                return false;\n            }\n        }\n\n        return !$this->dependOnAll;\n    }\n}\n"
  },
  {
    "path": "framework/caching/DbCache.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\caching;\n\nuse Yii;\nuse yii\\base\\InvalidConfigException;\nuse yii\\db\\Connection;\nuse yii\\db\\PdoValue;\nuse yii\\db\\Query;\nuse yii\\di\\Instance;\n\n/**\n * DbCache implements a cache application component by storing cached data in a database.\n *\n * By default, DbCache stores session data in a DB table named 'cache'. This table\n * must be pre-created. The table name can be changed by setting [[cacheTable]].\n *\n * Please refer to [[Cache]] for common cache operations that are supported by DbCache.\n *\n * The following example shows how you can configure the application to use DbCache:\n *\n * ```\n * 'cache' => [\n *     'class' => 'yii\\caching\\DbCache',\n *     // 'db' => 'mydb',\n *     // 'cacheTable' => 'my_cache',\n * ]\n * ```\n *\n * For more details and usage information on Cache, see the [guide article on caching](guide:caching-overview).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass DbCache extends Cache\n{\n    /**\n     * @var Connection|array|string the DB connection object or the application component ID of the DB connection.\n     * After the DbCache object is created, if you want to change this property, you should only assign it\n     * with a DB connection object.\n     * Starting from version 2.0.2, this can also be a configuration array for creating the object.\n     */\n    public $db = 'db';\n    /**\n     * @var string name of the DB table to store cache content.\n     * The table should be pre-created as follows:\n     *\n     * ```\n     * CREATE TABLE cache (\n     *     id char(128) NOT NULL PRIMARY KEY,\n     *     expire int(11),\n     *     data BLOB\n     * );\n     * ```\n     *\n     * For MSSQL:\n     * ```\n     * CREATE TABLE cache (\n     *     id VARCHAR(128) NOT NULL PRIMARY KEY,\n     *     expire INT(11),\n     *     data VARBINARY(MAX)\n     * );\n     * ```\n     *\n     * where 'BLOB' refers to the BLOB-type of your preferred DBMS. Below are the BLOB type\n     * that can be used for some popular DBMS:\n     *\n     * - MySQL: LONGBLOB\n     * - PostgreSQL: BYTEA\n     *\n     * When using DbCache in a production server, we recommend you create a DB index for the 'expire'\n     * column in the cache table to improve the performance.\n     */\n    public $cacheTable = '{{%cache}}';\n    /**\n     * @var int the probability (parts per million) that garbage collection (GC) should be performed\n     * when storing a piece of data in the cache. Defaults to 100, meaning 0.01% chance.\n     * This number should be between 0 and 1000000. A value 0 meaning no GC will be performed at all.\n     */\n    public $gcProbability = 100;\n\n    protected $isVarbinaryDataField;\n\n\n    /**\n     * Initializes the DbCache component.\n     * This method will initialize the [[db]] property to make sure it refers to a valid DB connection.\n     * @throws InvalidConfigException if [[db]] is invalid.\n     */\n    public function init()\n    {\n        parent::init();\n        $this->db = Instance::ensure($this->db, Connection::className());\n    }\n\n    /**\n     * Checks whether a specified key exists in the cache.\n     * This can be faster than getting the value from the cache if the data is big.\n     * Note that this method does not check whether the dependency associated\n     * with the cached data, if there is any, has changed. So a call to [[get]]\n     * may return false while exists returns true.\n     * @param mixed $key a key identifying the cached value. This can be a simple string or\n     * a complex data structure consisting of factors representing the key.\n     * @return bool true if a value exists in cache, false if the value is not in the cache or expired.\n     */\n    public function exists($key)\n    {\n        $key = $this->buildKey($key);\n\n        $query = new Query();\n        $query->select(['COUNT(*)'])\n            ->from($this->cacheTable)\n            ->where('[[id]] = :id AND ([[expire]] = 0 OR [[expire]] >' . time() . ')', [':id' => $key]);\n        if ($this->db->enableQueryCache) {\n            // temporarily disable and re-enable query caching\n            $this->db->enableQueryCache = false;\n            $result = $query->createCommand($this->db)->queryScalar();\n            $this->db->enableQueryCache = true;\n        } else {\n            $result = $query->createCommand($this->db)->queryScalar();\n        }\n\n        return $result > 0;\n    }\n\n    /**\n     * Retrieves a value from cache with a specified key.\n     * This is the implementation of the method declared in the parent class.\n     * @param string $key a unique key identifying the cached value\n     * @return string|false the value stored in cache, false if the value is not in the cache or expired.\n     */\n    protected function getValue($key)\n    {\n        $query = new Query();\n        $query->select([$this->getDataFieldName()])\n            ->from($this->cacheTable)\n            ->where('[[id]] = :id AND ([[expire]] = 0 OR [[expire]] >' . time() . ')', [':id' => $key]);\n        if ($this->db->enableQueryCache) {\n            // temporarily disable and re-enable query caching\n            $this->db->enableQueryCache = false;\n            $result = $query->createCommand($this->db)->queryScalar();\n            $this->db->enableQueryCache = true;\n\n            return $result;\n        }\n\n        return $query->createCommand($this->db)->queryScalar();\n    }\n\n    /**\n     * Retrieves multiple values from cache with the specified keys.\n     * @param array $keys a list of keys identifying the cached values\n     * @return array a list of cached values indexed by the keys\n     */\n    protected function getValues($keys)\n    {\n        if (empty($keys)) {\n            return [];\n        }\n        $query = new Query();\n        $query->select(['id', $this->getDataFieldName()])\n            ->from($this->cacheTable)\n            ->where(['id' => $keys])\n            ->andWhere('([[expire]] = 0 OR [[expire]] > ' . time() . ')');\n\n        if ($this->db->enableQueryCache) {\n            $this->db->enableQueryCache = false;\n            $rows = $query->createCommand($this->db)->queryAll();\n            $this->db->enableQueryCache = true;\n        } else {\n            $rows = $query->createCommand($this->db)->queryAll();\n        }\n\n        $results = [];\n        foreach ($keys as $key) {\n            $results[$key] = false;\n        }\n        foreach ($rows as $row) {\n            if (is_resource($row['data']) && get_resource_type($row['data']) === 'stream') {\n                $results[$row['id']] = stream_get_contents($row['data']);\n            } else {\n                $results[$row['id']] = $row['data'];\n            }\n        }\n\n        return $results;\n    }\n\n    /**\n     * Stores a value identified by a key in cache.\n     * This is the implementation of the method declared in the parent class.\n     *\n     * @param string $key the key identifying the value to be cached\n     * @param string $value the value to be cached. Other types (if you have disabled [[serializer]]) cannot be saved.\n     * @param int $duration the number of seconds in which the cached value will expire. 0 means never expire.\n     * @return bool true if the value is successfully stored into cache, false otherwise\n     */\n    protected function setValue($key, $value, $duration)\n    {\n        try {\n            $this->db->noCache(function (Connection $db) use ($key, $value, $duration) {\n                $db->createCommand()->upsert($this->cacheTable, [\n                    'id' => $key,\n                    'expire' => $duration > 0 ? $duration + time() : 0,\n                    'data' => $this->getDataFieldValue($value),\n                ])->execute();\n            });\n\n            $this->gc();\n\n            return true;\n        } catch (\\Exception $e) {\n            Yii::warning(\"Unable to update or insert cache data: {$e->getMessage()}\", __METHOD__);\n\n            return false;\n        }\n    }\n\n    /**\n     * Stores a value identified by a key into cache if the cache does not contain this key.\n     * This is the implementation of the method declared in the parent class.\n     *\n     * @param string $key the key identifying the value to be cached\n     * @param string $value the value to be cached. Other types (if you have disabled [[serializer]]) cannot be saved.\n     * @param int $duration the number of seconds in which the cached value will expire. 0 means never expire.\n     * @return bool true if the value is successfully stored into cache, false otherwise\n     */\n    protected function addValue($key, $value, $duration)\n    {\n        $this->gc();\n\n        try {\n            $this->db->noCache(function (Connection $db) use ($key, $value, $duration) {\n                $db->createCommand()\n                    ->insert($this->cacheTable, [\n                        'id' => $key,\n                        'expire' => $duration > 0 ? $duration + time() : 0,\n                        'data' => $this->getDataFieldValue($value),\n                    ])->execute();\n            });\n\n            return true;\n        } catch (\\Exception $e) {\n            Yii::warning(\"Unable to insert cache data: {$e->getMessage()}\", __METHOD__);\n\n            return false;\n        }\n    }\n\n    /**\n     * Deletes a value with the specified key from cache\n     * This is the implementation of the method declared in the parent class.\n     * @param string $key the key of the value to be deleted\n     * @return bool if no error happens during deletion\n     */\n    protected function deleteValue($key)\n    {\n        $this->db->noCache(function (Connection $db) use ($key) {\n            $db->createCommand()\n                ->delete($this->cacheTable, ['id' => $key])\n                ->execute();\n        });\n\n        return true;\n    }\n\n    /**\n     * Removes the expired data values.\n     * @param bool $force whether to enforce the garbage collection regardless of [[gcProbability]].\n     * Defaults to false, meaning the actual deletion happens with the probability as specified by [[gcProbability]].\n     */\n    public function gc($force = false)\n    {\n\n        if ($force || random_int(0, 1000000) < $this->gcProbability) {\n            $this->db->createCommand()\n                ->delete($this->cacheTable, '[[expire]] > 0 AND [[expire]] < ' . time())\n                ->execute();\n        }\n    }\n\n    /**\n     * Deletes all values from cache.\n     * This is the implementation of the method declared in the parent class.\n     * @return bool whether the flush operation was successful.\n     */\n    protected function flushValues()\n    {\n        $this->db->createCommand()\n            ->delete($this->cacheTable)\n            ->execute();\n\n        return true;\n    }\n\n    /**\n     * @return bool whether field is MSSQL varbinary\n     * @since 2.0.42\n     */\n    protected function isVarbinaryDataField()\n    {\n        if ($this->isVarbinaryDataField === null) {\n            $this->isVarbinaryDataField = in_array($this->db->getDriverName(), ['sqlsrv', 'dblib']) &&\n                $this->db->getTableSchema($this->cacheTable)->columns['data']->dbType === 'varbinary';\n        }\n        return $this->isVarbinaryDataField;\n    }\n\n    /**\n     * @return string `data` field name converted for usage in MSSQL (if needed)\n     * @since 2.0.42\n     */\n    protected function getDataFieldName()\n    {\n        return $this->isVarbinaryDataField() ? 'CONVERT(VARCHAR(MAX), [[data]]) data' : 'data';\n    }\n\n    /**\n     * @return PdoValue PdoValue or direct $value for usage in MSSQL\n     * @since 2.0.42\n     */\n    protected function getDataFieldValue($value)\n    {\n        return $this->isVarbinaryDataField() ? $value : new PdoValue($value, \\PDO::PARAM_LOB);\n    }\n}\n"
  },
  {
    "path": "framework/caching/DbDependency.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\caching;\n\nuse yii\\base\\InvalidConfigException;\nuse yii\\db\\Connection;\nuse yii\\di\\Instance;\n\n/**\n * DbDependency represents a dependency based on the query result of a SQL statement.\n *\n * If the query result changes, the dependency is considered as changed.\n * The query is specified via the [[sql]] property.\n *\n * For more details and usage information on Cache, see the [guide article on caching](guide:caching-overview).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass DbDependency extends Dependency\n{\n    /**\n     * @var Connection|string the DB connection object or the application component ID of the DB connection.\n     */\n    public $db = 'db';\n    /**\n     * @var string|null the SQL query whose result is used to determine if the dependency has been changed.\n     * Only the first row of the query result will be used.\n     */\n    public $sql;\n    /**\n     * @var array the parameters (name => value) to be bound to the SQL statement specified by [[sql]].\n     */\n    public $params = [];\n\n\n    /**\n     * Generates the data needed to determine if dependency has been changed.\n     * This method returns the value of the global state.\n     * @param CacheInterface $cache the cache component that is currently evaluating this dependency\n     * @return mixed the data needed to determine if dependency has been changed.\n     * @throws InvalidConfigException if [[db]] is not a valid application component ID\n     */\n    protected function generateDependencyData($cache)\n    {\n        /** @var Connection $db */\n        $db = Instance::ensure($this->db, Connection::className());\n        if ($this->sql === null) {\n            throw new InvalidConfigException('DbDependency::sql must be set.');\n        }\n\n        if ($db->enableQueryCache) {\n            // temporarily disable and re-enable query caching\n            $db->enableQueryCache = false;\n            $result = $db->createCommand($this->sql, $this->params)->queryOne();\n            $db->enableQueryCache = true;\n        } else {\n            $result = $db->createCommand($this->sql, $this->params)->queryOne();\n        }\n\n        return $result;\n    }\n}\n"
  },
  {
    "path": "framework/caching/DbQueryDependency.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\caching;\n\nuse yii\\base\\InvalidConfigException;\nuse yii\\db\\QueryInterface;\nuse yii\\di\\Instance;\n\n/**\n * DbQueryDependency represents a dependency based on the query result of an [[QueryInterface]] instance.\n *\n * If the query result changes, the dependency is considered as changed.\n * The query is specified via the [[query]] property.\n *\n * Object of any class which matches [[QueryInterface]] can be used, so this dependency can be used not only\n * with regular relational databases but with MongoDB, Redis and so on as well.\n *\n * For more details and usage information on Cache, see the [guide article on caching](guide:caching-overview).\n *\n * @see QueryInterface\n *\n * @author Paul Klimov <klimov.paul@gmail.com>\n * @since 2.0.12\n */\nclass DbQueryDependency extends Dependency\n{\n    /**\n     * @var string|array|object the application component ID of the database connection, connection object or\n     * its array configuration.\n     * This field can be left blank, allowing query to determine connection automatically.\n     */\n    public $db;\n    /**\n     * @var QueryInterface the query which result is used to determine if the dependency has been changed.\n     * Actual query method to be invoked is determined by [[method]].\n     */\n    public $query;\n    /**\n     * @var string|callable|null method which should be invoked in over the [[query]] object.\n     *\n     * If specified as a string an own query method with such name will be invoked, passing [[db]] value as its\n     * first argument. For example: `exists`, `all`.\n     *\n     * This field can be specified as a PHP callback of following signature:\n     *\n     * ```\n     * function (QueryInterface $query, mixed $db) {\n     *     //return mixed;\n     * }\n     * ```\n     *\n     * If not set - [[QueryInterface::one()]] will be used.\n     */\n    public $method;\n\n\n    /**\n     * Generates the data needed to determine if dependency is changed.\n     *\n     * This method returns the query result.\n     * @param CacheInterface $cache the cache component that is currently evaluating this dependency\n     * @return mixed the data needed to determine if dependency has been changed.\n     * @throws InvalidConfigException on invalid configuration.\n     */\n    protected function generateDependencyData($cache)\n    {\n        $db = $this->db;\n        if ($db !== null) {\n            $db = Instance::ensure($db);\n        }\n\n        if (!$this->query instanceof QueryInterface) {\n            throw new InvalidConfigException('\"' . get_class($this) . '::$query\" should be an instance of \"yii\\db\\QueryInterface\".');\n        }\n\n        if (!empty($db->enableQueryCache)) {\n            // temporarily disable and re-enable query caching\n            $originEnableQueryCache = $db->enableQueryCache;\n            $db->enableQueryCache = false;\n            $result = $this->executeQuery($this->query, $db);\n            $db->enableQueryCache = $originEnableQueryCache;\n        } else {\n            $result = $this->executeQuery($this->query, $db);\n        }\n\n        return $result;\n    }\n\n    /**\n     * Executes the query according to [[method]] specification.\n     * @param QueryInterface $query query to be executed.\n     * @param mixed $db connection.\n     * @return mixed query result.\n     */\n    private function executeQuery($query, $db)\n    {\n        if ($this->method === null) {\n            return $query->one($db);\n        }\n        if (is_string($this->method)) {\n            return call_user_func([$query, $this->method], $db);\n        }\n\n        return call_user_func($this->method, $query, $db);\n    }\n}\n"
  },
  {
    "path": "framework/caching/Dependency.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\caching;\n\n/**\n * Dependency is the base class for cache dependency classes.\n *\n * Child classes should override its [[generateDependencyData()]] for generating\n * the actual dependency data.\n *\n * For more details and usage information on Cache, see the [guide article on caching](guide:caching-overview).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nabstract class Dependency extends \\yii\\base\\BaseObject\n{\n    /**\n     * @var mixed the dependency data that is saved in cache and later is compared with the\n     * latest dependency data.\n     */\n    public $data;\n    /**\n     * @var bool whether this dependency is reusable or not. True value means that dependent\n     * data for this cache dependency will be generated only once per request. This allows you\n     * to use the same cache dependency for multiple separate cache calls while generating the same\n     * page without an overhead of re-evaluating dependency data each time. Defaults to false.\n     */\n    public $reusable = false;\n\n    /**\n     * @var array static storage of cached data for reusable dependencies.\n     */\n    private static $_reusableData = [];\n\n\n    /**\n     * Evaluates the dependency by generating and saving the data related with dependency.\n     * This method is invoked by cache before writing data into it.\n     * @param CacheInterface $cache the cache component that is currently evaluating this dependency\n     */\n    public function evaluateDependency($cache)\n    {\n        if ($this->reusable) {\n            $hash = $this->generateReusableHash();\n            if (!array_key_exists($hash, self::$_reusableData)) {\n                self::$_reusableData[$hash] = $this->generateDependencyData($cache);\n            }\n            $this->data = self::$_reusableData[$hash];\n        } else {\n            $this->data = $this->generateDependencyData($cache);\n        }\n    }\n\n    /**\n     * Returns a value indicating whether the dependency has changed.\n     * @deprecated since version 2.0.11. Will be removed in version 2.1. Use [[isChanged()]] instead.\n     * @param CacheInterface $cache the cache component that is currently evaluating this dependency\n     * @return bool whether the dependency has changed.\n     */\n    public function getHasChanged($cache)\n    {\n        return $this->isChanged($cache);\n    }\n\n    /**\n     * Checks whether the dependency is changed.\n     * @param CacheInterface $cache the cache component that is currently evaluating this dependency\n     * @return bool whether the dependency has changed.\n     * @since 2.0.11\n     */\n    public function isChanged($cache)\n    {\n        if ($this->reusable) {\n            $hash = $this->generateReusableHash();\n            if (!array_key_exists($hash, self::$_reusableData)) {\n                self::$_reusableData[$hash] = $this->generateDependencyData($cache);\n            }\n            $data = self::$_reusableData[$hash];\n        } else {\n            $data = $this->generateDependencyData($cache);\n        }\n\n        return $data !== $this->data;\n    }\n\n    /**\n     * Resets all cached data for reusable dependencies.\n     */\n    public static function resetReusableData()\n    {\n        self::$_reusableData = [];\n    }\n\n    /**\n     * Generates a unique hash that can be used for retrieving reusable dependency data.\n     *\n     * @return string a unique hash value for this cache dependency.\n     * @see reusable\n     */\n    protected function generateReusableHash()\n    {\n        $clone = clone $this;\n        $clone->data = null; // https://github.com/yiisoft/yii2/issues/3052\n\n        try {\n            $serialized = serialize($clone);\n        } catch (\\Exception $e) {\n            // unserializable properties are nulled\n            foreach ($clone as $name => $value) {\n                if (is_object($value) && $value instanceof \\Closure) {\n                    $clone->{$name} = null;\n                }\n            }\n            $serialized = serialize($clone);\n        }\n\n        return sha1($serialized);\n    }\n\n    /**\n     * Generates the data needed to determine if dependency is changed.\n     * Derived classes should override this method to generate the actual dependency data.\n     * @param CacheInterface $cache the cache component that is currently evaluating this dependency\n     * @return mixed the data needed to determine if dependency has been changed.\n     */\n    abstract protected function generateDependencyData($cache);\n}\n"
  },
  {
    "path": "framework/caching/DummyCache.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\caching;\n\n/**\n * DummyCache is a placeholder cache component.\n *\n * DummyCache does not cache anything. It is provided so that one can always configure\n * a 'cache' application component and save the check of existence of `\\Yii::$app->cache`.\n * By replacing DummyCache with some other cache component, one can quickly switch from\n * non-caching mode to caching mode.\n *\n * For more details and usage information on Cache, see the [guide article on caching](guide:caching-overview).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass DummyCache extends Cache\n{\n    /**\n     * Retrieves a value from cache with a specified key.\n     * This is the implementation of the method declared in the parent class.\n     * @param string $key a unique key identifying the cached value\n     * @return mixed|false the value stored in cache, false if the value is not in the cache or expired.\n     */\n    protected function getValue($key)\n    {\n        return false;\n    }\n\n    /**\n     * Stores a value identified by a key in cache.\n     * This is the implementation of the method declared in the parent class.\n     *\n     * @param string $key the key identifying the value to be cached\n     * @param mixed $value the value to be cached\n     * @param int $duration the number of seconds in which the cached value will expire. 0 means never expire.\n     * @return bool true if the value is successfully stored into cache, false otherwise\n     */\n    protected function setValue($key, $value, $duration)\n    {\n        return true;\n    }\n\n    /**\n     * Stores a value identified by a key into cache if the cache does not contain this key.\n     * This is the implementation of the method declared in the parent class.\n     * @param string $key the key identifying the value to be cached\n     * @param mixed $value the value to be cached\n     * @param int $duration the number of seconds in which the cached value will expire. 0 means never expire.\n     * @return bool true if the value is successfully stored into cache, false otherwise\n     */\n    protected function addValue($key, $value, $duration)\n    {\n        return true;\n    }\n\n    /**\n     * Deletes a value with the specified key from cache\n     * This is the implementation of the method declared in the parent class.\n     * @param string $key the key of the value to be deleted\n     * @return bool if no error happens during deletion\n     */\n    protected function deleteValue($key)\n    {\n        return true;\n    }\n\n    /**\n     * Deletes all values from cache.\n     * This is the implementation of the method declared in the parent class.\n     * @return bool whether the flush operation was successful.\n     */\n    protected function flushValues()\n    {\n        return true;\n    }\n}\n"
  },
  {
    "path": "framework/caching/ExpressionDependency.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\caching;\n\n/**\n * ExpressionDependency represents a dependency based on the result of a PHP expression.\n *\n * ExpressionDependency will use `eval()` to evaluate the PHP expression.\n * The dependency is reported as unchanged if and only if the result of the expression is\n * the same as the one evaluated when storing the data to cache.\n *\n * A PHP expression can be any PHP code that has a value. To learn more about what an expression is,\n * please refer to the [php manual](https://www.php.net/manual/en/language.expressions.php).\n *\n * For more details and usage information on Cache, see the [guide article on caching](guide:caching-overview).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass ExpressionDependency extends Dependency\n{\n    /**\n     * @var string the string representation of a PHP expression whose result is used to determine the dependency.\n     * A PHP expression can be any PHP code that evaluates to a value. To learn more about what an expression is,\n     * please refer to the [php manual](https://www.php.net/manual/en/language.expressions.php).\n     */\n    public $expression = 'true';\n    /**\n     * @var mixed custom parameters associated with this dependency. You may get the value\n     * of this property in [[expression]] using `$this->params`.\n     */\n    public $params;\n\n\n    /**\n     * Generates the data needed to determine if dependency has been changed.\n     * This method returns the result of the PHP expression.\n     * @param CacheInterface $cache the cache component that is currently evaluating this dependency\n     * @return mixed the data needed to determine if dependency has been changed.\n     */\n    protected function generateDependencyData($cache)\n    {\n        return eval(\"return {$this->expression};\");\n    }\n}\n"
  },
  {
    "path": "framework/caching/FileCache.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\caching;\n\nuse Yii;\nuse yii\\helpers\\FileHelper;\n\n/**\n * FileCache implements a cache component using files.\n *\n * For each data value being cached, FileCache will store it in a separate file.\n * The cache files are placed under [[cachePath]]. FileCache will perform garbage collection\n * automatically to remove expired cache files.\n *\n * Please refer to [[Cache]] for common cache operations that are supported by FileCache.\n *\n * For more details and usage information on Cache, see the [guide article on caching](guide:caching-overview).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass FileCache extends Cache\n{\n    /**\n     * @var string a string prefixed to every cache key. This is needed when you store\n     * cache data under the same [[cachePath]] for different applications to avoid\n     * conflict.\n     *\n     * To ensure interoperability, only alphanumeric characters should be used.\n     */\n    public $keyPrefix = '';\n    /**\n     * @var string the directory to store cache files. You may use [path alias](guide:concept-aliases) here.\n     * If not set, it will use the \"cache\" subdirectory under the application runtime path.\n     */\n    public $cachePath = '@runtime/cache';\n    /**\n     * @var string cache file suffix. Defaults to '.bin'.\n     */\n    public $cacheFileSuffix = '.bin';\n    /**\n     * @var int the level of sub-directories to store cache files. Defaults to 1.\n     * If the system has huge number of cache files (e.g. one million), you may use a bigger value\n     * (usually no bigger than 3). Using sub-directories is mainly to ensure the file system\n     * is not over burdened with a single directory having too many files.\n     */\n    public $directoryLevel = 1;\n    /**\n     * @var int the probability (parts per million) that garbage collection (GC) should be performed\n     * when storing a piece of data in the cache. Defaults to 10, meaning 0.001% chance.\n     * This number should be between 0 and 1000000. A value 0 means no GC will be performed at all.\n     */\n    public $gcProbability = 10;\n    /**\n     * @var int|null the permission to be set for newly created cache files.\n     * This value will be used by PHP chmod() function. No umask will be applied.\n     * If not set, the permission will be determined by the current environment.\n     */\n    public $fileMode;\n    /**\n     * @var int the permission to be set for newly created directories.\n     * This value will be used by PHP chmod() function. No umask will be applied.\n     * Defaults to 0775, meaning the directory is read-writable by owner and group,\n     * but read-only for other users.\n     */\n    public $dirMode = 0775;\n\n\n    /**\n     * Initializes this component by ensuring the existence of the cache path.\n     */\n    public function init()\n    {\n        parent::init();\n        $this->cachePath = Yii::getAlias($this->cachePath);\n        if (!is_dir($this->cachePath)) {\n            FileHelper::createDirectory($this->cachePath, $this->dirMode, true);\n        }\n    }\n\n    /**\n     * Checks whether a specified key exists in the cache.\n     * This can be faster than getting the value from the cache if the data is big.\n     * Note that this method does not check whether the dependency associated\n     * with the cached data, if there is any, has changed. So a call to [[get]]\n     * may return false while exists returns true.\n     * @param mixed $key a key identifying the cached value. This can be a simple string or\n     * a complex data structure consisting of factors representing the key.\n     * @return bool true if a value exists in cache, false if the value is not in the cache or expired.\n     */\n    public function exists($key)\n    {\n        $cacheFile = $this->getCacheFile($this->buildKey($key));\n\n        return @filemtime($cacheFile) > time();\n    }\n\n    /**\n     * Retrieves a value from cache with a specified key.\n     * This is the implementation of the method declared in the parent class.\n     * @param string $key a unique key identifying the cached value\n     * @return string|false the value stored in cache, false if the value is not in the cache or expired.\n     */\n    protected function getValue($key)\n    {\n        $cacheFile = $this->getCacheFile($key);\n\n        if (@filemtime($cacheFile) > time()) {\n            $fp = @fopen($cacheFile, 'r');\n            if ($fp !== false) {\n                @flock($fp, LOCK_SH);\n                $cacheValue = @stream_get_contents($fp);\n                @flock($fp, LOCK_UN);\n                @fclose($fp);\n                return $cacheValue;\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * Stores a value identified by a key in cache.\n     * This is the implementation of the method declared in the parent class.\n     *\n     * @param string $key the key identifying the value to be cached\n     * @param string $value the value to be cached. Other types (If you have disabled [[serializer]]) unable to get is\n     * correct in [[getValue()]].\n     * @param int $duration the number of seconds in which the cached value will expire. Fewer than or equal to 0 means 1 year expiration time.\n     * @return bool true if the value is successfully stored into cache, false otherwise\n     */\n    protected function setValue($key, $value, $duration)\n    {\n        $this->gc();\n        $cacheFile = $this->getCacheFile($key);\n        if ($this->directoryLevel > 0) {\n            @FileHelper::createDirectory(dirname($cacheFile), $this->dirMode, true);\n        }\n        // If ownership differs the touch call will fail, so we try to\n        // rebuild the file from scratch by deleting it first\n        // https://github.com/yiisoft/yii2/pull/16120\n        if (is_file($cacheFile) && function_exists('posix_geteuid') && fileowner($cacheFile) !== posix_geteuid()) {\n            @unlink($cacheFile);\n        }\n        if (@file_put_contents($cacheFile, $value, LOCK_EX) !== false) {\n            if ($this->fileMode !== null) {\n                @chmod($cacheFile, $this->fileMode);\n            }\n            if ($duration <= 0) {\n                $duration = 31536000; // 1 year\n            }\n\n            if (@touch($cacheFile, $duration + time())) {\n                clearstatcache();\n                return true;\n            }\n\n            return false;\n        }\n\n        $message = \"Unable to write cache file '{$cacheFile}'\";\n\n        if ($error = error_get_last()) {\n            $message .= \": {$error['message']}\";\n        }\n\n        Yii::warning($message, __METHOD__);\n\n        return false;\n    }\n\n    /**\n     * Stores a value identified by a key into cache if the cache does not contain this key.\n     * This is the implementation of the method declared in the parent class.\n     *\n     * @param string $key the key identifying the value to be cached\n     * @param string $value the value to be cached. Other types (if you have disabled [[serializer]]) unable to get is\n     * correct in [[getValue()]].\n     * @param int $duration the number of seconds in which the cached value will expire. 0 means never expire.\n     * @return bool true if the value is successfully stored into cache, false otherwise\n     */\n    protected function addValue($key, $value, $duration)\n    {\n        $cacheFile = $this->getCacheFile($key);\n        if (@filemtime($cacheFile) > time()) {\n            return false;\n        }\n\n        return $this->setValue($key, $value, $duration);\n    }\n\n    /**\n     * Deletes a value with the specified key from cache\n     * This is the implementation of the method declared in the parent class.\n     * @param string $key the key of the value to be deleted\n     * @return bool if no error happens during deletion\n     */\n    protected function deleteValue($key)\n    {\n        $cacheFile = $this->getCacheFile($key);\n\n        return @unlink($cacheFile);\n    }\n\n    /**\n     * Returns the cache file path given the normalized cache key.\n     * @param string $normalizedKey normalized cache key by [[buildKey]] method\n     * @return string the cache file path\n     */\n    protected function getCacheFile($normalizedKey)\n    {\n        $cacheKey = $normalizedKey;\n\n        if ($this->keyPrefix !== '') {\n            // Remove key prefix to avoid generating constant directory levels\n            $lenKeyPrefix = strlen($this->keyPrefix);\n            $cacheKey = substr_replace($normalizedKey, '', 0, $lenKeyPrefix);\n        }\n\n        $cachePath = $this->cachePath;\n\n        if ($this->directoryLevel > 0) {\n            for ($i = 0; $i < $this->directoryLevel; ++$i) {\n                if (($subDirectory = substr($cacheKey, $i + $i, 2)) !== false) {\n                    $cachePath .= DIRECTORY_SEPARATOR . $subDirectory;\n                }\n            }\n        }\n\n        return $cachePath . DIRECTORY_SEPARATOR . $normalizedKey . $this->cacheFileSuffix;\n    }\n\n    /**\n     * Deletes all values from cache.\n     * This is the implementation of the method declared in the parent class.\n     * @return bool whether the flush operation was successful.\n     */\n    protected function flushValues()\n    {\n        $this->gc(true, false);\n\n        return true;\n    }\n\n    /**\n     * Removes expired cache files.\n     * @param bool $force whether to enforce the garbage collection regardless of [[gcProbability]].\n     * Defaults to false, meaning the actual deletion happens with the probability as specified by [[gcProbability]].\n     * @param bool $expiredOnly whether to removed expired cache files only.\n     * If false, all cache files under [[cachePath]] will be removed.\n     */\n    public function gc($force = false, $expiredOnly = true)\n    {\n        if ($force || random_int(0, 1000000) < $this->gcProbability) {\n            $this->gcRecursive($this->cachePath, $expiredOnly);\n        }\n    }\n\n    /**\n     * Recursively removing expired cache files under a directory.\n     * This method is mainly used by [[gc()]].\n     * @param string $path the directory under which expired cache files are removed.\n     * @param bool $expiredOnly whether to only remove expired cache files. If false, all files\n     * under `$path` will be removed.\n     */\n    protected function gcRecursive($path, $expiredOnly)\n    {\n        if (($handle = opendir($path)) !== false) {\n            while (($file = readdir($handle)) !== false) {\n                if (strncmp($file, '.', 1) === 0) {\n                    continue;\n                }\n                $fullPath = $path . DIRECTORY_SEPARATOR . $file;\n                $message = null;\n                if (is_dir($fullPath)) {\n                    $this->gcRecursive($fullPath, $expiredOnly);\n                    if (!$expiredOnly) {\n                        if (!@rmdir($fullPath)) {\n                            $message = \"Unable to remove directory '$fullPath'\";\n                            if ($error = error_get_last()) {\n                                $message .= \": {$error['message']}\";\n                            }\n                        }\n                    }\n                } elseif (!$expiredOnly || $expiredOnly && @filemtime($fullPath) < time()) {\n                    if (!@unlink($fullPath)) {\n                        $message = \"Unable to remove file '$fullPath'\";\n                        if ($error = error_get_last()) {\n                            $message .= \": {$error['message']}\";\n                        }\n                    }\n                }\n                $message and Yii::warning($message, __METHOD__);\n            }\n            closedir($handle);\n        }\n    }\n}\n"
  },
  {
    "path": "framework/caching/FileDependency.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\caching;\n\nuse Yii;\nuse yii\\base\\InvalidConfigException;\n\n/**\n * FileDependency represents a dependency based on a file's last modification time.\n *\n * If the last modification time of the file specified via [[fileName]] is changed,\n * the dependency is considered as changed.\n *\n * For more details and usage information on Cache, see the [guide article on caching](guide:caching-overview).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass FileDependency extends Dependency\n{\n    /**\n     * @var string the file path or [path alias](guide:concept-aliases) whose last modification time is used to\n     * check if the dependency has been changed.\n     */\n    public $fileName;\n\n\n    /**\n     * Generates the data needed to determine if dependency has been changed.\n     * This method returns the file's last modification time.\n     * @param CacheInterface $cache the cache component that is currently evaluating this dependency\n     * @return mixed the data needed to determine if dependency has been changed.\n     * @throws InvalidConfigException if [[fileName]] is not set\n     */\n    protected function generateDependencyData($cache)\n    {\n        if ($this->fileName === null) {\n            throw new InvalidConfigException('FileDependency::fileName must be set');\n        }\n\n        $fileName = Yii::getAlias($this->fileName);\n\n        clearstatcache(false, $fileName);\n        return @filemtime($fileName);\n    }\n}\n"
  },
  {
    "path": "framework/caching/MemCache.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\caching;\n\nuse Yii;\nuse yii\\base\\InvalidConfigException;\n\n/**\n * MemCache implements a cache application component based on [memcache](https://pecl.php.net/package/memcache)\n * and [memcached](https://pecl.php.net/package/memcached).\n *\n * MemCache supports both [memcache](https://pecl.php.net/package/memcache) and\n * [memcached](https://pecl.php.net/package/memcached). By setting [[useMemcached]] to be true or false,\n * one can let MemCache to use either memcached or memcache, respectively.\n *\n * MemCache can be configured with a list of memcache servers by settings its [[servers]] property.\n * By default, MemCache assumes there is a memcache server running on localhost at port 11211.\n *\n * See [[Cache]] for common cache operations that MemCache supports.\n *\n * Note, there is no security measure to protected data in memcache.\n * All data in memcache can be accessed by any process running in the system.\n *\n * To use MemCache as the cache application component, configure the application as follows,\n *\n * ```\n * [\n *     'components' => [\n *         'cache' => [\n *             'class' => 'yii\\caching\\MemCache',\n *             'servers' => [\n *                 [\n *                     'host' => 'server1',\n *                     'port' => 11211,\n *                     'weight' => 60,\n *                 ],\n *                 [\n *                     'host' => 'server2',\n *                     'port' => 11211,\n *                     'weight' => 40,\n *                 ],\n *             ],\n *         ],\n *     ],\n * ]\n * ```\n *\n * In the above, two memcache servers are used: server1 and server2. You can configure more properties of\n * each server, such as `persistent`, `weight`, `timeout`. Please see [[MemCacheServer]] for available options.\n *\n * For more details and usage information on Cache, see the [guide article on caching](guide:caching-overview).\n *\n * @property-read \\Memcache|\\Memcached $memcache The memcache (or memcached) object used by this cache\n * component.\n * @property MemCacheServer[] $servers List of memcache server configurations. Note that the type of this\n * property differs in getter and setter. See [[getServers()]] and [[setServers()]] for details.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass MemCache extends Cache\n{\n    /**\n     * @var bool whether to use memcached or memcache as the underlying caching extension.\n     * If true, [memcached](https://pecl.php.net/package/memcached) will be used.\n     * If false, [memcache](https://pecl.php.net/package/memcache) will be used.\n     * Defaults to false.\n     */\n    public $useMemcached = false;\n    /**\n     * @var string an ID that identifies a Memcached instance. This property is used only when [[useMemcached]] is true.\n     * By default the Memcached instances are destroyed at the end of the request. To create an instance that\n     * persists between requests, you may specify a unique ID for the instance. All instances created with the\n     * same ID will share the same connection.\n     * @see https://www.php.net/manual/en/memcached.construct.php\n     */\n    public $persistentId;\n    /**\n     * @var array options for Memcached. This property is used only when [[useMemcached]] is true.\n     * @see https://www.php.net/manual/en/memcached.setoptions.php\n     */\n    public $options;\n    /**\n     * @var string memcached sasl username. This property is used only when [[useMemcached]] is true.\n     * @see https://www.php.net/manual/en/memcached.setsaslauthdata.php\n     */\n    public $username;\n    /**\n     * @var string memcached sasl password. This property is used only when [[useMemcached]] is true.\n     * @see https://www.php.net/manual/en/memcached.setsaslauthdata.php\n     */\n    public $password;\n\n    /**\n     * @var \\Memcache|\\Memcached the Memcache instance\n     */\n    private $_cache;\n    /**\n     * @var array list of memcache server configurations\n     */\n    private $_servers = [];\n\n\n    /**\n     * Initializes this application component.\n     * It creates the memcache instance and adds memcache servers.\n     */\n    public function init()\n    {\n        parent::init();\n        $this->addServers($this->getMemcache(), $this->getServers());\n    }\n\n    /**\n     * Add servers to the server pool of the cache specified.\n     *\n     * @param \\Memcache|\\Memcached $cache\n     * @param MemCacheServer[] $servers\n     * @throws InvalidConfigException\n     */\n    protected function addServers($cache, $servers)\n    {\n        if (empty($servers)) {\n            $servers = [new MemCacheServer([\n                'host' => '127.0.0.1',\n                'port' => 11211,\n            ])];\n        } else {\n            foreach ($servers as $server) {\n                if ($server->host === null) {\n                    throw new InvalidConfigException(\"The 'host' property must be specified for every memcache server.\");\n                }\n            }\n        }\n        if ($this->useMemcached) {\n            $this->addMemcachedServers($cache, $servers);\n        } else {\n            $this->addMemcacheServers($cache, $servers);\n        }\n    }\n\n    /**\n     * Add servers to the server pool of the cache specified\n     * Used for memcached PECL extension.\n     *\n     * @param \\Memcached $cache\n     * @param MemCacheServer[] $servers\n     */\n    protected function addMemcachedServers($cache, $servers)\n    {\n        $existingServers = [];\n        if ($this->persistentId !== null) {\n            foreach ($cache->getServerList() as $s) {\n                $existingServers[$s['host'] . ':' . $s['port']] = true;\n            }\n        }\n        foreach ($servers as $server) {\n            if (empty($existingServers) || !isset($existingServers[$server->host . ':' . $server->port])) {\n                $cache->addServer($server->host, $server->port, $server->weight);\n            }\n        }\n    }\n\n    /**\n     * Add servers to the server pool of the cache specified\n     * Used for memcache PECL extension.\n     *\n     * @param \\Memcache $cache\n     * @param MemCacheServer[] $servers\n     */\n    protected function addMemcacheServers($cache, $servers)\n    {\n        $class = new \\ReflectionClass($cache);\n        $paramCount = $class->getMethod('addServer')->getNumberOfParameters();\n        foreach ($servers as $server) {\n            // $timeout is used for memcache versions that do not have $timeoutms parameter\n            $timeout = (int) ($server->timeout / 1000) + (($server->timeout % 1000 > 0) ? 1 : 0);\n            if ($paramCount === 9) {\n                $cache->addserver(\n                    $server->host,\n                    $server->port,\n                    $server->persistent,\n                    $server->weight,\n                    $timeout,\n                    $server->retryInterval,\n                    $server->status,\n                    $server->failureCallback,\n                    $server->timeout\n                );\n            } else {\n                $cache->addserver(\n                    $server->host,\n                    $server->port,\n                    $server->persistent,\n                    $server->weight,\n                    $timeout,\n                    $server->retryInterval,\n                    $server->status,\n                    $server->failureCallback\n                );\n            }\n        }\n    }\n\n    /**\n     * Returns the underlying memcache (or memcached) object.\n     * @return \\Memcache|\\Memcached the memcache (or memcached) object used by this cache component.\n     * @throws InvalidConfigException if memcache or memcached extension is not loaded\n     */\n    public function getMemcache()\n    {\n        if ($this->_cache === null) {\n            $extension = $this->useMemcached ? 'memcached' : 'memcache';\n            if (!extension_loaded($extension)) {\n                throw new InvalidConfigException(\"MemCache requires PHP $extension extension to be loaded.\");\n            }\n\n            if ($this->useMemcached) {\n                $this->_cache = $this->persistentId !== null ? new \\Memcached($this->persistentId) : new \\Memcached();\n                if ($this->username !== null || $this->password !== null) {\n                    $this->_cache->setOption(\\Memcached::OPT_BINARY_PROTOCOL, true);\n                    $this->_cache->setSaslAuthData($this->username, $this->password);\n                }\n                if (!empty($this->options)) {\n                    $this->_cache->setOptions($this->options);\n                }\n            } else {\n                $this->_cache = new \\Memcache();\n            }\n        }\n\n        return $this->_cache;\n    }\n\n    /**\n     * Returns the memcache or memcached server configurations.\n     * @return MemCacheServer[] list of memcache server configurations.\n     */\n    public function getServers()\n    {\n        return $this->_servers;\n    }\n\n    /**\n     * @param array $config list of memcache or memcached server configurations. Each element must be an array\n     * with the following keys: host, port, persistent, weight, timeout, retryInterval, status.\n     * @see https://www.php.net/manual/en/memcache.addserver.php\n     * @see https://www.php.net/manual/en/memcached.addserver.php\n     */\n    public function setServers($config)\n    {\n        foreach ($config as $c) {\n            $this->_servers[] = new MemCacheServer($c);\n        }\n    }\n\n    /**\n     * Retrieves a value from cache with a specified key.\n     * This is the implementation of the method declared in the parent class.\n     * @param string $key a unique key identifying the cached value\n     * @return mixed|false the value stored in cache, false if the value is not in the cache or expired.\n     */\n    protected function getValue($key)\n    {\n        return $this->_cache->get($key);\n    }\n\n    /**\n     * Retrieves multiple values from cache with the specified keys.\n     * @param array $keys a list of keys identifying the cached values\n     * @return array a list of cached values indexed by the keys\n     */\n    protected function getValues($keys)\n    {\n        return $this->useMemcached ? $this->_cache->getMulti($keys) : $this->_cache->get($keys);\n    }\n\n    /**\n     * Stores a value identified by a key in cache.\n     * This is the implementation of the method declared in the parent class.\n     *\n     * @param string $key the key identifying the value to be cached\n     * @param mixed $value the value to be cached.\n     * @see [Memcache::set()](https://www.php.net/manual/en/memcache.set.php)\n     * @param int $duration the number of seconds in which the cached value will expire. 0 means never expire.\n     * @return bool true if the value is successfully stored into cache, false otherwise\n     */\n    protected function setValue($key, $value, $duration)\n    {\n        $expire = $this->normalizeDuration($duration);\n        return $this->useMemcached ? $this->_cache->set($key, $value, $expire) : $this->_cache->set($key, $value, 0, $expire);\n    }\n\n    /**\n     * Stores multiple key-value pairs in cache.\n     * @param array $data array where key corresponds to cache key while value is the value stored\n     * @param int $duration the number of seconds in which the cached values will expire. 0 means never expire.\n     * @return array array of failed keys.\n     */\n    protected function setValues($data, $duration)\n    {\n        if ($this->useMemcached) {\n            $expire = $this->normalizeDuration($duration);\n\n            // Memcached::setMulti() returns boolean\n            // @see https://www.php.net/manual/en/memcached.setmulti.php\n            return $this->_cache->setMulti($data, $expire) ? [] : array_keys($data);\n        }\n\n        return parent::setValues($data, $duration);\n    }\n\n    /**\n     * Stores a value identified by a key into cache if the cache does not contain this key.\n     * This is the implementation of the method declared in the parent class.\n     *\n     * @param string $key the key identifying the value to be cached\n     * @param mixed $value the value to be cached\n     * @see [Memcache::set()](https://www.php.net/manual/en/memcache.set.php)\n     * @param int $duration the number of seconds in which the cached value will expire. 0 means never expire.\n     * @return bool true if the value is successfully stored into cache, false otherwise\n     */\n    protected function addValue($key, $value, $duration)\n    {\n        $expire = $this->normalizeDuration($duration);\n        return $this->useMemcached ? $this->_cache->add($key, $value, $expire) : $this->_cache->add($key, $value, 0, $expire);\n    }\n\n    /**\n     * Deletes a value with the specified key from cache\n     * This is the implementation of the method declared in the parent class.\n     * @param string $key the key of the value to be deleted\n     * @return bool if no error happens during deletion\n     */\n    protected function deleteValue($key)\n    {\n        return $this->_cache->delete($key, 0);\n    }\n\n    /**\n     * Deletes all values from cache.\n     * This is the implementation of the method declared in the parent class.\n     * @return bool whether the flush operation was successful.\n     */\n    protected function flushValues()\n    {\n        return $this->_cache->flush();\n    }\n\n    /**\n     * Normalizes duration value\n     *\n     * @see https://github.com/yiisoft/yii2/issues/17710\n     * @see https://www.php.net/manual/en/memcache.set.php\n     * @see https://www.php.net/manual/en/memcached.expiration.php\n     *\n     * @since 2.0.31\n     * @param int $duration\n     * @return int\n     */\n    protected function normalizeDuration($duration)\n    {\n        if ($duration < 0) {\n            return 0;\n        }\n\n        if ($duration < 2592001) {\n            return $duration;\n        }\n\n        return $duration + time();\n    }\n}\n"
  },
  {
    "path": "framework/caching/MemCacheServer.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\caching;\n\n/**\n * MemCacheServer represents the configuration data for a single memcache or memcached server.\n *\n * See [PHP manual](https://www.php.net/manual/en/memcache.addserver.php) for detailed explanation\n * of each configuration property.\n *\n * For more details and usage information on Cache, see the [guide article on caching](guide:caching-overview).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass MemCacheServer extends \\yii\\base\\BaseObject\n{\n    /**\n     * @var string memcache server hostname or IP address\n     */\n    public $host;\n    /**\n     * @var int memcache server port\n     */\n    public $port = 11211;\n    /**\n     * @var int probability of using this server among all servers.\n     */\n    public $weight = 1;\n    /**\n     * @var bool whether to use a persistent connection. This is used by memcache only.\n     */\n    public $persistent = true;\n    /**\n     * @var int timeout in milliseconds which will be used for connecting to the server.\n     * This is used by memcache only. For old versions of memcache that only support specifying\n     * timeout in seconds this will be rounded up to full seconds.\n     */\n    public $timeout = 1000;\n    /**\n     * @var int how often a failed server will be retried (in seconds). This is used by memcache only.\n     */\n    public $retryInterval = 15;\n    /**\n     * @var bool if the server should be flagged as online upon a failure. This is used by memcache only.\n     */\n    public $status = true;\n    /**\n     * @var \\Closure this callback function will run upon encountering an error.\n     * The callback is run before fail over is attempted. The function takes two parameters,\n     * the [[host]] and the [[port]] of the failed server.\n     * This is used by memcache only.\n     */\n    public $failureCallback;\n}\n"
  },
  {
    "path": "framework/caching/TagDependency.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\caching;\n\n/**\n * TagDependency associates a cached data item with one or multiple [[tags]].\n *\n * By calling [[invalidate()]], you can invalidate all cached data items that are associated with the specified tag name(s).\n *\n * ```\n * // setting multiple cache keys to store data forever and tagging them with \"user-123\"\n * Yii::$app->cache->set('user_42_profile', '', 0, new TagDependency(['tags' => 'user-123']));\n * Yii::$app->cache->set('user_42_stats', '', 0, new TagDependency(['tags' => 'user-123']));\n *\n * // invalidating all keys tagged with \"user-123\"\n * TagDependency::invalidate(Yii::$app->cache, 'user-123');\n * ```\n *\n * For more details and usage information on Cache, see the [guide article on caching](guide:caching-overview).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass TagDependency extends Dependency\n{\n    /**\n     * @var string|array a list of tag names for this dependency. For a single tag, you may specify it as a string.\n     */\n    public $tags = [];\n\n\n    /**\n     * Generates the data needed to determine if dependency has been changed.\n     * This method does nothing in this class.\n     * @param CacheInterface $cache the cache component that is currently evaluating this dependency\n     * @return mixed the data needed to determine if dependency has been changed.\n     */\n    protected function generateDependencyData($cache)\n    {\n        $timestamps = $this->getTimestamps($cache, (array) $this->tags);\n\n        $newKeys = [];\n        foreach ($timestamps as $key => $timestamp) {\n            if ($timestamp === false) {\n                $newKeys[] = $key;\n            }\n        }\n        if (!empty($newKeys)) {\n            $timestamps = array_merge($timestamps, static::touchKeys($cache, $newKeys));\n        }\n\n        return $timestamps;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function isChanged($cache)\n    {\n        $timestamps = $this->getTimestamps($cache, (array) $this->tags);\n        return $timestamps !== $this->data;\n    }\n\n    /**\n     * Invalidates all of the cached data items that are associated with any of the specified [[tags]].\n     * @param CacheInterface $cache the cache component that caches the data items\n     * @param string|array $tags\n     */\n    public static function invalidate($cache, $tags)\n    {\n        $keys = [];\n        foreach ((array) $tags as $tag) {\n            $keys[] = $cache->buildKey([__CLASS__, $tag]);\n        }\n        static::touchKeys($cache, $keys);\n    }\n\n    /**\n     * Generates the timestamp for the specified cache keys.\n     * @param CacheInterface $cache\n     * @param string[] $keys\n     * @return array the timestamp indexed by cache keys\n     */\n    protected static function touchKeys($cache, $keys)\n    {\n        $items = [];\n        $time = microtime();\n        foreach ($keys as $key) {\n            $items[$key] = $time;\n        }\n        $cache->multiSet($items);\n        return $items;\n    }\n\n    /**\n     * Returns the timestamps for the specified tags.\n     * @param CacheInterface $cache\n     * @param string[] $tags\n     * @return array the timestamps indexed by the specified tags.\n     */\n    protected function getTimestamps($cache, $tags)\n    {\n        if (empty($tags)) {\n            return [];\n        }\n\n        $keys = [];\n        foreach ($tags as $tag) {\n            $keys[] = $cache->buildKey([__CLASS__, $tag]);\n        }\n\n        return $cache->multiGet($keys);\n    }\n}\n"
  },
  {
    "path": "framework/caching/WinCache.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\caching;\n\n/**\n * WinCache provides Windows Cache caching in terms of an application component.\n *\n * To use this application component, the [WinCache PHP extension](https://www.iis.net/downloads/microsoft/wincache-extension)\n * must be loaded. Also note that \"wincache.ucenabled\" should be set to \"On\" in your php.ini file.\n *\n * See [[Cache]] manual for common cache operations that are supported by WinCache.\n *\n * For more details and usage information on Cache, see the [guide article on caching](guide:caching-overview).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass WinCache extends Cache\n{\n    /**\n     * Checks whether a specified key exists in the cache.\n     * This can be faster than getting the value from the cache if the data is big.\n     * Note that this method does not check whether the dependency associated\n     * with the cached data, if there is any, has changed. So a call to [[get]]\n     * may return false while exists returns true.\n     * @param mixed $key a key identifying the cached value. This can be a simple string or\n     * a complex data structure consisting of factors representing the key.\n     * @return bool true if a value exists in cache, false if the value is not in the cache or expired.\n     */\n    public function exists($key)\n    {\n        $key = $this->buildKey($key);\n\n        return wincache_ucache_exists($key);\n    }\n\n    /**\n     * Retrieves a value from cache with a specified key.\n     * This is the implementation of the method declared in the parent class.\n     * @param string $key a unique key identifying the cached value\n     * @return string|bool the value stored in cache, false if the value is not in the cache or expired.\n     */\n    protected function getValue($key)\n    {\n        return wincache_ucache_get($key);\n    }\n\n    /**\n     * Retrieves multiple values from cache with the specified keys.\n     * @param array $keys a list of keys identifying the cached values\n     * @return array a list of cached values indexed by the keys\n     */\n    protected function getValues($keys)\n    {\n        return wincache_ucache_get($keys);\n    }\n\n    /**\n     * Stores a value identified by a key in cache.\n     * This is the implementation of the method declared in the parent class.\n     *\n     * @param string $key the key identifying the value to be cached\n     * @param mixed $value the value to be cached. Most often it's a string. If you have disabled [[serializer]],\n     * it could be something else.\n     * @param int $duration the number of seconds in which the cached value will expire. 0 means never expire.\n     * @return bool true if the value is successfully stored into cache, false otherwise\n     */\n    protected function setValue($key, $value, $duration)\n    {\n        return wincache_ucache_set($key, $value, $duration);\n    }\n\n    /**\n     * Stores multiple key-value pairs in cache.\n     * @param array $data array where key corresponds to cache key while value is the value stored\n     * @param int $duration the number of seconds in which the cached values will expire. 0 means never expire.\n     * @return array array of failed keys\n     */\n    protected function setValues($data, $duration)\n    {\n        return wincache_ucache_set($data, null, $duration);\n    }\n\n    /**\n     * Stores a value identified by a key into cache if the cache does not contain this key.\n     * This is the implementation of the method declared in the parent class.\n     *\n     * @param string $key the key identifying the value to be cached\n     * @param mixed $value the value to be cached. Most often it's a string. If you have disabled [[serializer]],\n     * it could be something else.\n     * @param int $duration the number of seconds in which the cached value will expire. 0 means never expire.\n     * @return bool true if the value is successfully stored into cache, false otherwise\n     */\n    protected function addValue($key, $value, $duration)\n    {\n        return wincache_ucache_add($key, $value, $duration);\n    }\n\n    /**\n     * Adds multiple key-value pairs to cache.\n     * The default implementation calls [[addValue()]] multiple times add values one by one. If the underlying cache\n     * storage supports multiadd, this method should be overridden to exploit that feature.\n     * @param array $data array where key corresponds to cache key while value is the value stored\n     * @param int $duration the number of seconds in which the cached values will expire. 0 means never expire.\n     * @return array array of failed keys\n     */\n    protected function addValues($data, $duration)\n    {\n        return wincache_ucache_add($data, null, $duration);\n    }\n\n    /**\n     * Deletes a value with the specified key from cache\n     * This is the implementation of the method declared in the parent class.\n     * @param string $key the key of the value to be deleted\n     * @return bool if no error happens during deletion\n     */\n    protected function deleteValue($key)\n    {\n        return wincache_ucache_delete($key);\n    }\n\n    /**\n     * Deletes all values from cache.\n     * This is the implementation of the method declared in the parent class.\n     * @return bool whether the flush operation was successful.\n     */\n    protected function flushValues()\n    {\n        return wincache_ucache_clear();\n    }\n}\n"
  },
  {
    "path": "framework/caching/migrations/m150909_153426_cache_init.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nuse yii\\base\\InvalidConfigException;\nuse yii\\caching\\DbCache;\nuse yii\\db\\Migration;\n\n/**\n * Initializes Cache tables.\n *\n * @author Misbahul D Munir <misbahuldmunir@gmail.com>\n * @since 2.0.7\n */\nclass m150909_153426_cache_init extends Migration\n{\n    /**\n     * @throws yii\\base\\InvalidConfigException\n     * @return DbCache\n     */\n    protected function getCache()\n    {\n        $cache = Yii::$app->getCache();\n        if (!$cache instanceof DbCache) {\n            throw new InvalidConfigException('You should configure \"cache\" component to use database before executing this migration.');\n        }\n\n        return $cache;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $cache = $this->getCache();\n        $this->db = $cache->db;\n\n        $tableOptions = null;\n        if ($this->db->driverName === 'mysql') {\n            // https://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci\n            $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';\n        }\n\n        $this->createTable($cache->cacheTable, [\n            'id' => $this->string(128)->notNull(),\n            'expire' => $this->integer(),\n            'data' => $this->binary(),\n            'PRIMARY KEY ([[id]])',\n            ], $tableOptions);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        $cache = $this->getCache();\n        $this->db = $cache->db;\n\n        $this->dropTable($cache->cacheTable);\n    }\n}\n"
  },
  {
    "path": "framework/caching/migrations/schema-mssql.sql",
    "content": "/**\n * Database schema required by \\yii\\caching\\DbCache.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Misbahul D Munir <misbahuldmunir@gmail.com>\n * @link https://www.yiiframework.com/\n * @copyright 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n * @since 2.0.7\n */\nif object_id('[cache]', 'U') is not null\n    drop table [cache];\n\ndrop table if exists [cache];\n\ncreate table [cache]\n(\n    [id]  varchar(128) not null,\n    [expire] integer,\n    [data]   BLOB,\n    primary key ([id])\n);\n"
  },
  {
    "path": "framework/caching/migrations/schema-mysql.sql",
    "content": "/**\n * Database schema required by \\yii\\caching\\DbCache.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Misbahul D Munir <misbahuldmunir@gmail.com>\n * @link https://www.yiiframework.com/\n * @copyright 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n * @since 2.0.7\n */\n\ndrop table if exists `cache`;\n\ncreate table `cache`\n(\n    `id`  varchar(128) not null,\n    `expire` integer,\n    `data`   LONGBLOB,\n    primary key (`id`)\n) engine InnoDB;\n"
  },
  {
    "path": "framework/caching/migrations/schema-oci.sql",
    "content": "/**\n * Database schema required by \\yii\\caching\\DbCache.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Misbahul D Munir <misbahuldmunir@gmail.com>\n * @link https://www.yiiframework.com/\n * @copyright 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n * @since 2.0.7\n */\n\ndrop table if exists \"cache\";\n\ncreate table \"cache\"\n(\n    \"id\"  varchar(128) not null,\n    \"expire\" integer,\n    \"data\"   BYTEA,\n    primary key (\"id\")\n);\n"
  },
  {
    "path": "framework/caching/migrations/schema-pgsql.sql",
    "content": "/**\n * Database schema required by \\yii\\caching\\DbCache.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Misbahul D Munir <misbahuldmunir@gmail.com>\n * @link https://www.yiiframework.com/\n * @copyright 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n * @since 2.0.7\n */\n\ndrop table if exists \"cache\";\n\ncreate table \"cache\"\n(\n    \"id\"  varchar(128) not null,\n    \"expire\" integer,\n    \"data\"   bytea,\n    primary key (\"id\")\n);\n"
  },
  {
    "path": "framework/caching/migrations/schema-sqlite.sql",
    "content": "/**\n * Database schema required by \\yii\\caching\\DbCache.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Misbahul D Munir <misbahuldmunir@gmail.com>\n * @link https://www.yiiframework.com/\n * @copyright 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n * @since 2.0.7\n */\n\ndrop table if exists \"cache\";\n\ncreate table \"cache\"\n(\n    \"id\"  varchar(128) not null,\n    \"expire\" integer,\n    \"data\"   BLOB,\n    primary key (\"id\")\n);\n"
  },
  {
    "path": "framework/captcha/Captcha.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\captcha;\n\nuse yii\\base\\InvalidConfigException;\nuse yii\\helpers\\Html;\nuse yii\\helpers\\Json;\nuse yii\\helpers\\Url;\nuse yii\\widgets\\InputWidget;\n\n/**\n * Captcha renders a CAPTCHA image and an input field that takes user-entered verification code.\n *\n * Captcha is used together with [[CaptchaAction]] to provide [CAPTCHA](https://en.wikipedia.org/wiki/CAPTCHA) - a way\n * of preventing website spamming.\n *\n * The image element rendered by Captcha will display a CAPTCHA image generated by\n * an action whose route is specified by [[captchaAction]]. This action must be an instance of [[CaptchaAction]].\n *\n * When the user clicks on the CAPTCHA image, it will cause the CAPTCHA image\n * to be refreshed with a new CAPTCHA.\n *\n * You may use [[\\yii\\captcha\\CaptchaValidator]] to validate the user input matches\n * the current CAPTCHA verification code.\n *\n * The following example shows how to use this widget with a model attribute:\n *\n * ```\n * echo Captcha::widget([\n *     'model' => $model,\n *     'attribute' => 'captcha',\n * ]);\n * ```\n *\n * The following example will use the name property instead:\n *\n * ```\n * echo Captcha::widget([\n *     'name' => 'captcha',\n * ]);\n * ```\n *\n * You can also use this widget in an [[\\yii\\widgets\\ActiveForm|ActiveForm]] using the [[\\yii\\widgets\\ActiveField::widget()|widget()]]\n * method, for example like this:\n *\n * ```\n * <?= $form->field($model, 'captcha')->widget(\\yii\\captcha\\Captcha::classname(), [\n *     // configure additional widget properties here\n * ]) ?>\n * ```\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Captcha extends InputWidget\n{\n    /**\n     * @var string|array the route of the action that generates the CAPTCHA images.\n     * The action represented by this route must be an action of [[CaptchaAction]].\n     * Please refer to [[\\yii\\helpers\\Url::toRoute()]] for acceptable formats.\n     */\n    public $captchaAction = 'site/captcha';\n    /**\n     * @var array HTML attributes to be applied to the CAPTCHA image tag.\n     * @see \\yii\\helpers\\Html::renderTagAttributes() for details on how attributes are being rendered.\n     */\n    public $imageOptions = [];\n    /**\n     * @var string the template for arranging the CAPTCHA image tag and the text input tag.\n     * In this template, the token `{image}` will be replaced with the actual image tag,\n     * while `{input}` will be replaced with the text input tag.\n     */\n    public $template = '{image} {input}';\n    /**\n     * @var array the HTML attributes for the input tag.\n     * @see \\yii\\helpers\\Html::renderTagAttributes() for details on how attributes are being rendered.\n     */\n    public $options = ['class' => 'form-control'];\n\n\n    /**\n     * Initializes the widget.\n     */\n    public function init()\n    {\n        parent::init();\n\n        static::checkRequirements();\n\n        if (!isset($this->imageOptions['id'])) {\n            $this->imageOptions['id'] = $this->options['id'] . '-image';\n        }\n    }\n\n    /**\n     * Renders the widget.\n     */\n    public function run()\n    {\n        $this->registerClientScript();\n        $input = $this->renderInputHtml('text');\n        $route = $this->captchaAction;\n        if (is_array($route)) {\n            $route['v'] = uniqid('', true);\n        } else {\n            $route = [$route, 'v' => uniqid('', true)];\n        }\n        $image = Html::img($route, $this->imageOptions);\n        echo strtr($this->template, [\n            '{input}' => $input,\n            '{image}' => $image,\n        ]);\n    }\n\n    /**\n     * Registers the needed JavaScript.\n     */\n    public function registerClientScript()\n    {\n        $options = $this->getClientOptions();\n        $options = empty($options) ? '' : Json::htmlEncode($options);\n        $id = $this->imageOptions['id'];\n        $view = $this->getView();\n        CaptchaAsset::register($view);\n        $view->registerJs(\"jQuery('#$id').yiiCaptcha($options);\");\n    }\n\n    /**\n     * Returns the options for the captcha JS widget.\n     * @return array the options\n     */\n    protected function getClientOptions()\n    {\n        $route = $this->captchaAction;\n        if (is_array($route)) {\n            $route[CaptchaAction::REFRESH_GET_VAR] = 1;\n        } else {\n            $route = [$route, CaptchaAction::REFRESH_GET_VAR => 1];\n        }\n\n        $options = [\n            'refreshUrl' => Url::toRoute($route),\n            'hashKey' => 'yiiCaptcha/' . trim($route[0], '/'),\n        ];\n\n        return $options;\n    }\n\n    /**\n     * Checks if there is graphic extension available to generate CAPTCHA images.\n     * This method will check the existence of ImageMagick and GD extensions.\n     * @return string the name of the graphic extension, either \"imagick\" or \"gd\".\n     * @throws InvalidConfigException if neither ImageMagick nor GD is installed.\n     */\n    public static function checkRequirements()\n    {\n        if (extension_loaded('imagick')) {\n            $imagickFormats = (new \\Imagick())->queryFormats('PNG');\n            if (in_array('PNG', $imagickFormats, true)) {\n                return 'imagick';\n            }\n        }\n        if (extension_loaded('gd')) {\n            $gdInfo = gd_info();\n            if (!empty($gdInfo['FreeType Support'])) {\n                return 'gd';\n            }\n        }\n        throw new InvalidConfigException('Either GD PHP extension with FreeType support or ImageMagick PHP extension with PNG support is required.');\n    }\n}\n"
  },
  {
    "path": "framework/captcha/CaptchaAction.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\captcha;\n\nuse Yii;\nuse yii\\base\\Action;\nuse yii\\base\\InvalidConfigException;\nuse yii\\helpers\\Url;\nuse yii\\web\\Controller;\nuse yii\\web\\Response;\n\n/**\n * CaptchaAction renders a CAPTCHA image.\n *\n * CaptchaAction is used together with [[Captcha]] and [[\\yii\\captcha\\CaptchaValidator]]\n * to provide the [CAPTCHA](https://en.wikipedia.org/wiki/CAPTCHA) feature.\n *\n * By configuring the properties of CaptchaAction, you may customize the appearance of\n * the generated CAPTCHA images, such as the font color, the background color, etc.\n *\n * Note that CaptchaAction requires either GD2 extension or ImageMagick PHP extension.\n *\n * Using CAPTCHA involves the following steps:\n *\n * 1. Override [[\\yii\\web\\Controller::actions()]] and register an action of class CaptchaAction with ID 'captcha'\n * 2. In the form model, declare an attribute to store user-entered verification code, and declare the attribute\n *    to be validated by the 'captcha' validator.\n * 3. In the controller view, insert a [[Captcha]] widget in the form.\n *\n * @property-read string $verifyCode The verification code.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Controller = Controller\n * @extends Action<T>\n */\nclass CaptchaAction extends Action\n{\n    /**\n     * The name of the GET parameter indicating whether the CAPTCHA image should be regenerated.\n     */\n    public const REFRESH_GET_VAR = 'refresh';\n    /**\n     * @var int how many times should the same CAPTCHA be displayed. Defaults to 3.\n     * A value less than or equal to 0 means the test is unlimited (available since version 1.1.2).\n     */\n    public $testLimit = 3;\n    /**\n     * @var int the width of the generated CAPTCHA image. Defaults to 120.\n     */\n    public $width = 120;\n    /**\n     * @var int the height of the generated CAPTCHA image. Defaults to 50.\n     */\n    public $height = 50;\n    /**\n     * @var int padding around the text. Defaults to 2.\n     */\n    public $padding = 2;\n    /**\n     * @var int the background color. For example, 0x55FF00.\n     * Defaults to 0xFFFFFF, meaning white color.\n     */\n    public $backColor = 0xFFFFFF;\n    /**\n     * @var int the font color. For example, 0x55FF00. Defaults to 0x2040A0 (blue color).\n     */\n    public $foreColor = 0x2040A0;\n    /**\n     * @var bool whether to use transparent background. Defaults to false.\n     */\n    public $transparent = false;\n    /**\n     * @var int the minimum length for randomly generated word. Defaults to 6.\n     */\n    public $minLength = 6;\n    /**\n     * @var int the maximum length for randomly generated word. Defaults to 7.\n     */\n    public $maxLength = 7;\n    /**\n     * @var int the offset between characters. Defaults to -2. You can adjust this property\n     * in order to decrease or increase the readability of the captcha.\n     */\n    public $offset = -2;\n    /**\n     * @var string the TrueType font file. This can be either a file path or [path alias](guide:concept-aliases).\n     */\n    public $fontFile = '@yii/captcha/SpicyRice.ttf';\n    /**\n     * @var string|null the fixed verification code. When this property is set,\n     * [[getVerifyCode()]] will always return the value of this property.\n     * This is mainly used in automated tests where we want to be able to reproduce\n     * the same verification code each time we run the tests.\n     * If not set, it means the verification code will be randomly generated.\n     */\n    public $fixedVerifyCode;\n    /**\n     * @var string|null the rendering library to use. Currently supported only 'gd' and 'imagick'.\n     * If not set, library will be determined automatically.\n     * @since 2.0.7\n     */\n    public $imageLibrary;\n\n\n    /**\n     * Initializes the action.\n     * @throws InvalidConfigException if the font file does not exist.\n     */\n    public function init()\n    {\n        $this->fontFile = Yii::getAlias($this->fontFile);\n        if (!is_file($this->fontFile)) {\n            throw new InvalidConfigException(\"The font file does not exist: {$this->fontFile}\");\n        }\n    }\n\n    /**\n     * Runs the action.\n     */\n    public function run()\n    {\n        if (Yii::$app->request->getQueryParam(self::REFRESH_GET_VAR) !== null) {\n            // AJAX request for regenerating code\n            $code = $this->getVerifyCode(true);\n            Yii::$app->response->format = Response::FORMAT_JSON;\n            return [\n                'hash1' => $this->generateValidationHash($code),\n                'hash2' => $this->generateValidationHash(strtolower($code)),\n                // we add a random 'v' parameter so that FireFox can refresh the image\n                // when src attribute of image tag is changed\n                'url' => Url::to([$this->id, 'v' => uniqid('', true)]),\n            ];\n        }\n\n        $this->setHttpHeaders();\n        Yii::$app->response->format = Response::FORMAT_RAW;\n\n        return $this->renderImage($this->getVerifyCode());\n    }\n\n    /**\n     * Generates a hash code that can be used for client-side validation.\n     * @param string $code the CAPTCHA code\n     * @return string a hash code generated from the CAPTCHA code\n     */\n    public function generateValidationHash($code)\n    {\n        for ($h = 0, $i = strlen($code) - 1; $i >= 0; --$i) {\n            $h += ord($code[$i]) << $i;\n        }\n\n        return $h;\n    }\n\n    /**\n     * Gets the verification code.\n     * @param bool $regenerate whether the verification code should be regenerated.\n     * @return string the verification code.\n     */\n    public function getVerifyCode($regenerate = false)\n    {\n        if ($this->fixedVerifyCode !== null) {\n            return $this->fixedVerifyCode;\n        }\n\n        $session = Yii::$app->getSession();\n        $session->open();\n        $name = $this->getSessionKey();\n        if ($session[$name] === null || $regenerate) {\n            $session[$name] = $this->generateVerifyCode();\n            $session[$name . 'count'] = 1;\n        }\n\n        return $session[$name];\n    }\n\n    /**\n     * Validates the input to see if it matches the generated code.\n     * @param string $input user input\n     * @param bool $caseSensitive whether the comparison should be case-sensitive\n     * @return bool whether the input is valid\n     */\n    public function validate($input, $caseSensitive)\n    {\n        $code = $this->getVerifyCode();\n        $valid = $caseSensitive ? ($input === $code) : strcasecmp($input, $code) === 0;\n        $session = Yii::$app->getSession();\n        $session->open();\n        $name = $this->getSessionKey() . 'count';\n        $session[$name] += 1;\n        if ($valid || $session[$name] > $this->testLimit && $this->testLimit > 0) {\n            $this->getVerifyCode(true);\n        }\n\n        return $valid;\n    }\n\n    /**\n     * Generates a new verification code.\n     * @return string the generated verification code\n     */\n    protected function generateVerifyCode()\n    {\n        if ($this->minLength > $this->maxLength) {\n            $this->maxLength = $this->minLength;\n        }\n        if ($this->minLength < 3) {\n            $this->minLength = 3;\n        }\n        if ($this->maxLength > 20) {\n            $this->maxLength = 20;\n        }\n\n        $length = random_int($this->minLength, $this->maxLength);\n\n        $letters = 'bcdfghjklmnpqrstvwxyz';\n        $vowels = 'aeiou';\n        $code = '';\n        for ($i = 0; $i < $length; ++$i) {\n            if ($i % 2 && random_int(0, 10) > 2 || !($i % 2) && random_int(0, 10) > 9) {\n                $code .= $vowels[random_int(0, 4)];\n            } else {\n                $code .= $letters[random_int(0, 20)];\n            }\n        }\n\n        return $code;\n    }\n\n    /**\n     * Returns the session variable name used to store verification code.\n     * @return string the session variable name\n     */\n    protected function getSessionKey()\n    {\n        return '__captcha/' . $this->getUniqueId();\n    }\n\n    /**\n     * Renders the CAPTCHA image.\n     * @param string $code the verification code\n     * @return string image contents\n     * @throws InvalidConfigException if imageLibrary is not supported\n     */\n    protected function renderImage($code)\n    {\n        if (isset($this->imageLibrary)) {\n            $imageLibrary = $this->imageLibrary;\n        } else {\n            $imageLibrary = Captcha::checkRequirements();\n        }\n        if ($imageLibrary === 'gd') {\n            return $this->renderImageByGD($code);\n        } elseif ($imageLibrary === 'imagick') {\n            return $this->renderImageByImagick($code);\n        }\n\n        throw new InvalidConfigException(\"Defined library '{$imageLibrary}' is not supported\");\n    }\n\n    /**\n     * Renders the CAPTCHA image based on the code using GD library.\n     * @param string $code the verification code\n     * @return string image contents in PNG format.\n     */\n    protected function renderImageByGD($code)\n    {\n        $image = imagecreatetruecolor($this->width, $this->height);\n\n        $backColor = imagecolorallocate(\n            $image,\n            (int) ($this->backColor % 0x1000000 / 0x10000),\n            (int) ($this->backColor % 0x10000 / 0x100),\n            $this->backColor % 0x100\n        );\n        imagefilledrectangle($image, 0, 0, $this->width - 1, $this->height - 1, $backColor);\n        imagecolordeallocate($image, $backColor);\n\n        if ($this->transparent) {\n            imagecolortransparent($image, $backColor);\n        }\n\n        $foreColor = imagecolorallocate(\n            $image,\n            (int) ($this->foreColor % 0x1000000 / 0x10000),\n            (int) ($this->foreColor % 0x10000 / 0x100),\n            $this->foreColor % 0x100\n        );\n\n        $length = strlen($code);\n        $box = imagettfbbox(30, 0, $this->fontFile, $code);\n        $w = $box[4] - $box[0] + $this->offset * ($length - 1);\n        $h = $box[1] - $box[5];\n        $scale = min(($this->width - $this->padding * 2) / $w, ($this->height - $this->padding * 2) / $h);\n        $x = 10;\n        $y = round($this->height * 27 / 40);\n        for ($i = 0; $i < $length; ++$i) {\n            $fontSize = (int) (random_int(26, 32) * $scale * 0.8);\n            $angle = random_int(-10, 10);\n            $letter = $code[$i];\n            $box = imagettftext($image, $fontSize, $angle, $x, $y, $foreColor, $this->fontFile, $letter);\n            $x = $box[2] + $this->offset;\n        }\n\n        imagecolordeallocate($image, $foreColor);\n\n        ob_start();\n        imagepng($image);\n\n        // Function `imagedestroy` is deprecated since PHP `8.5`, as it has no effect since PHP `8.0`\n        if (PHP_VERSION_ID < 80000) {\n            imagedestroy($image);\n        }\n\n        return ob_get_clean();\n    }\n\n    /**\n     * Renders the CAPTCHA image based on the code using ImageMagick library.\n     * @param string $code the verification code\n     * @return string image contents in PNG format.\n     */\n    protected function renderImageByImagick($code)\n    {\n        $backColor = $this->transparent ? new \\ImagickPixel('transparent') : new \\ImagickPixel('#' . str_pad(dechex($this->backColor), 6, 0, STR_PAD_LEFT));\n        $foreColor = new \\ImagickPixel('#' . str_pad(dechex($this->foreColor), 6, 0, STR_PAD_LEFT));\n\n        $image = new \\Imagick();\n        $image->newImage($this->width, $this->height, $backColor);\n\n        $draw = new \\ImagickDraw();\n        $draw->setFont($this->fontFile);\n        $draw->setFontSize(30);\n        $fontMetrics = $image->queryFontMetrics($draw, $code);\n\n        $length = strlen($code);\n        $w = (int) $fontMetrics['textWidth'] - 8 + $this->offset * ($length - 1);\n        $h = (int) $fontMetrics['textHeight'] - 8;\n        $scale = min(($this->width - $this->padding * 2) / $w, ($this->height - $this->padding * 2) / $h);\n        $x = 10;\n        $y = round($this->height * 27 / 40);\n        for ($i = 0; $i < $length; ++$i) {\n            $draw = new \\ImagickDraw();\n            $draw->setFont($this->fontFile);\n            $draw->setFontSize((int) (random_int(26, 32) * $scale * 0.8));\n            $draw->setFillColor($foreColor);\n            $image->annotateImage($draw, $x, $y, random_int(-10, 10), $code[$i]);\n            $fontMetrics = $image->queryFontMetrics($draw, $code[$i]);\n            $x += (int) $fontMetrics['textWidth'] + $this->offset;\n        }\n\n        $image->setImageFormat('png');\n        return $image->getImageBlob();\n    }\n\n    /**\n     * Sets the HTTP headers needed by image response.\n     */\n    protected function setHttpHeaders()\n    {\n        Yii::$app->getResponse()->getHeaders()\n            ->set('Pragma', 'public')\n            ->set('Expires', '0')\n            ->set('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')\n            ->set('Content-Transfer-Encoding', 'binary')\n            ->set('Content-type', 'image/png');\n    }\n}\n"
  },
  {
    "path": "framework/captcha/CaptchaAsset.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\captcha;\n\nuse yii\\web\\AssetBundle;\n\n/**\n * This asset bundle provides the javascript files needed for the [[Captcha]] widget.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass CaptchaAsset extends AssetBundle\n{\n    public $sourcePath = '@yii/assets';\n    public $js = [\n        'yii.captcha.js',\n    ];\n    public $depends = [\n        'yii\\web\\YiiAsset',\n    ];\n}\n"
  },
  {
    "path": "framework/captcha/CaptchaValidator.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\captcha;\n\nuse Yii;\nuse yii\\base\\InvalidConfigException;\nuse yii\\helpers\\Json;\nuse yii\\validators\\ValidationAsset;\nuse yii\\validators\\Validator;\nuse yii\\web\\Controller;\n\n/**\n * CaptchaValidator validates that the attribute value is the same as the verification code displayed in the CAPTCHA.\n *\n * CaptchaValidator should be used together with [[CaptchaAction]].\n *\n * Note that once CAPTCHA validation succeeds, a new CAPTCHA will be generated automatically. As a result,\n * CAPTCHA validation should not be used in AJAX validation mode because it may fail the validation\n * even if a user enters the same code as shown in the CAPTCHA image which is actually different from the latest CAPTCHA code.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass CaptchaValidator extends Validator\n{\n    /**\n     * @var bool whether to skip this validator if the input is empty.\n     */\n    public $skipOnEmpty = false;\n    /**\n     * @var bool whether the comparison is case sensitive. Defaults to false.\n     */\n    public $caseSensitive = false;\n    /**\n     * @var string the route of the controller action that renders the CAPTCHA image.\n     */\n    public $captchaAction = 'site/captcha';\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        if ($this->message === null) {\n            $this->message = Yii::t('yii', 'The verification code is incorrect.');\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function validateValue($value)\n    {\n        $captcha = $this->createCaptchaAction();\n        $valid = !is_array($value) && $captcha->validate($value, $this->caseSensitive);\n\n        return $valid ? null : [$this->message, []];\n    }\n\n    /**\n     * Creates the CAPTCHA action object from the route specified by [[captchaAction]].\n     * @return CaptchaAction the action object\n     * @throws InvalidConfigException\n     */\n    public function createCaptchaAction()\n    {\n        $ca = Yii::$app->createController($this->captchaAction);\n        if ($ca !== false) {\n            /** @var Controller $controller */\n            list($controller, $actionID) = $ca;\n            /** @var CaptchaAction|null $action */\n            $action = $controller->createAction($actionID);\n            if ($action !== null) {\n                return $action;\n            }\n        }\n        throw new InvalidConfigException('Invalid CAPTCHA action ID: ' . $this->captchaAction);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function clientValidateAttribute($model, $attribute, $view)\n    {\n        ValidationAsset::register($view);\n        $options = $this->getClientOptions($model, $attribute);\n\n        return 'yii.validation.captcha(value, messages, ' . Json::htmlEncode($options) . ');';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getClientOptions($model, $attribute)\n    {\n        $captcha = $this->createCaptchaAction();\n        $code = $captcha->getVerifyCode(false);\n        $hash = $captcha->generateValidationHash($this->caseSensitive ? $code : strtolower($code));\n        $options = [\n            'hash' => $hash,\n            'hashKey' => 'yiiCaptcha/' . $captcha->getUniqueId(),\n            'caseSensitive' => $this->caseSensitive,\n            'message' => Yii::$app->getI18n()->format($this->message, [\n                'attribute' => $model->getAttributeLabel($attribute),\n            ], Yii::$app->language),\n        ];\n        if ($this->skipOnEmpty) {\n            $options['skipOnEmpty'] = 1;\n        }\n\n        return $options;\n    }\n}\n"
  },
  {
    "path": "framework/captcha/SpicyRice.md",
    "content": "## Spicy Rice font\n\n* **Author:** Brian J. Bonislawsky, Astigmatic (AOETI, Astigmatic One Eye Typographic Institute)\n* **License:** SIL Open Font License (OFL), version 1.1, [notes and FAQ](https://scripts.sil.org/OFL)\n\n## Links\n\n* [Astigmatic](http://www.astigmatic.com/)\n* [Google WebFonts](https://fonts.google.com/specimen/Spicy+Rice)\n* [fontsquirrel.com](https://www.fontsquirrel.com/fonts/spicy-rice)\n* [fontspace.com](https://www.fontspace.com/astigmatic-one-eye-typographic-institute/spicy-rice)\n"
  },
  {
    "path": "framework/classes.php",
    "content": "<?php\n\n/**\n * Yii core class map.\n *\n * This file is automatically generated by the \"build classmap\" command under the \"build\" folder.\n * Do not modify it directly.\n *\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nreturn [\n  'yii\\base\\Action' => YII2_PATH . '/base/Action.php',\n  'yii\\base\\ActionEvent' => YII2_PATH . '/base/ActionEvent.php',\n  'yii\\base\\ActionFilter' => YII2_PATH . '/base/ActionFilter.php',\n  'yii\\base\\Application' => YII2_PATH . '/base/Application.php',\n  'yii\\base\\ArrayAccessTrait' => YII2_PATH . '/base/ArrayAccessTrait.php',\n  'yii\\base\\Arrayable' => YII2_PATH . '/base/Arrayable.php',\n  'yii\\base\\ArrayableTrait' => YII2_PATH . '/base/ArrayableTrait.php',\n  'yii\\base\\BaseObject' => YII2_PATH . '/base/BaseObject.php',\n  'yii\\base\\Behavior' => YII2_PATH . '/base/Behavior.php',\n  'yii\\base\\BootstrapInterface' => YII2_PATH . '/base/BootstrapInterface.php',\n  'yii\\base\\Component' => YII2_PATH . '/base/Component.php',\n  'yii\\base\\Configurable' => YII2_PATH . '/base/Configurable.php',\n  'yii\\base\\Controller' => YII2_PATH . '/base/Controller.php',\n  'yii\\base\\DynamicContentAwareInterface' => YII2_PATH . '/base/DynamicContentAwareInterface.php',\n  'yii\\base\\DynamicContentAwareTrait' => YII2_PATH . '/base/DynamicContentAwareTrait.php',\n  'yii\\base\\DynamicModel' => YII2_PATH . '/base/DynamicModel.php',\n  'yii\\base\\ErrorException' => YII2_PATH . '/base/ErrorException.php',\n  'yii\\base\\ErrorHandler' => YII2_PATH . '/base/ErrorHandler.php',\n  'yii\\base\\Event' => YII2_PATH . '/base/Event.php',\n  'yii\\base\\Exception' => YII2_PATH . '/base/Exception.php',\n  'yii\\base\\ExitException' => YII2_PATH . '/base/ExitException.php',\n  'yii\\base\\InlineAction' => YII2_PATH . '/base/InlineAction.php',\n  'yii\\base\\InvalidArgumentException' => YII2_PATH . '/base/InvalidArgumentException.php',\n  'yii\\base\\InvalidCallException' => YII2_PATH . '/base/InvalidCallException.php',\n  'yii\\base\\InvalidConfigException' => YII2_PATH . '/base/InvalidConfigException.php',\n  'yii\\base\\InvalidParamException' => YII2_PATH . '/base/InvalidParamException.php',\n  'yii\\base\\InvalidRouteException' => YII2_PATH . '/base/InvalidRouteException.php',\n  'yii\\base\\InvalidValueException' => YII2_PATH . '/base/InvalidValueException.php',\n  'yii\\base\\Model' => YII2_PATH . '/base/Model.php',\n  'yii\\base\\ModelEvent' => YII2_PATH . '/base/ModelEvent.php',\n  'yii\\base\\Module' => YII2_PATH . '/base/Module.php',\n  'yii\\base\\NotSupportedException' => YII2_PATH . '/base/NotSupportedException.php',\n  'yii\\base\\Request' => YII2_PATH . '/base/Request.php',\n  'yii\\base\\Response' => YII2_PATH . '/base/Response.php',\n  'yii\\base\\Security' => YII2_PATH . '/base/Security.php',\n  'yii\\base\\StaticInstanceInterface' => YII2_PATH . '/base/StaticInstanceInterface.php',\n  'yii\\base\\StaticInstanceTrait' => YII2_PATH . '/base/StaticInstanceTrait.php',\n  'yii\\base\\Theme' => YII2_PATH . '/base/Theme.php',\n  'yii\\base\\UnknownClassException' => YII2_PATH . '/base/UnknownClassException.php',\n  'yii\\base\\UnknownMethodException' => YII2_PATH . '/base/UnknownMethodException.php',\n  'yii\\base\\UnknownPropertyException' => YII2_PATH . '/base/UnknownPropertyException.php',\n  'yii\\base\\UserException' => YII2_PATH . '/base/UserException.php',\n  'yii\\base\\View' => YII2_PATH . '/base/View.php',\n  'yii\\base\\ViewContextInterface' => YII2_PATH . '/base/ViewContextInterface.php',\n  'yii\\base\\ViewEvent' => YII2_PATH . '/base/ViewEvent.php',\n  'yii\\base\\ViewNotFoundException' => YII2_PATH . '/base/ViewNotFoundException.php',\n  'yii\\base\\ViewRenderer' => YII2_PATH . '/base/ViewRenderer.php',\n  'yii\\base\\Widget' => YII2_PATH . '/base/Widget.php',\n  'yii\\base\\WidgetEvent' => YII2_PATH . '/base/WidgetEvent.php',\n  'yii\\behaviors\\AttributeBehavior' => YII2_PATH . '/behaviors/AttributeBehavior.php',\n  'yii\\behaviors\\AttributeTypecastBehavior' => YII2_PATH . '/behaviors/AttributeTypecastBehavior.php',\n  'yii\\behaviors\\AttributesBehavior' => YII2_PATH . '/behaviors/AttributesBehavior.php',\n  'yii\\behaviors\\BlameableBehavior' => YII2_PATH . '/behaviors/BlameableBehavior.php',\n  'yii\\behaviors\\CacheableWidgetBehavior' => YII2_PATH . '/behaviors/CacheableWidgetBehavior.php',\n  'yii\\behaviors\\OptimisticLockBehavior' => YII2_PATH . '/behaviors/OptimisticLockBehavior.php',\n  'yii\\behaviors\\SluggableBehavior' => YII2_PATH . '/behaviors/SluggableBehavior.php',\n  'yii\\behaviors\\TimestampBehavior' => YII2_PATH . '/behaviors/TimestampBehavior.php',\n  'yii\\caching\\ApcCache' => YII2_PATH . '/caching/ApcCache.php',\n  'yii\\caching\\ArrayCache' => YII2_PATH . '/caching/ArrayCache.php',\n  'yii\\caching\\Cache' => YII2_PATH . '/caching/Cache.php',\n  'yii\\caching\\CacheInterface' => YII2_PATH . '/caching/CacheInterface.php',\n  'yii\\caching\\CallbackDependency' => YII2_PATH . '/caching/CallbackDependency.php',\n  'yii\\caching\\ChainedDependency' => YII2_PATH . '/caching/ChainedDependency.php',\n  'yii\\caching\\DbCache' => YII2_PATH . '/caching/DbCache.php',\n  'yii\\caching\\DbDependency' => YII2_PATH . '/caching/DbDependency.php',\n  'yii\\caching\\DbQueryDependency' => YII2_PATH . '/caching/DbQueryDependency.php',\n  'yii\\caching\\Dependency' => YII2_PATH . '/caching/Dependency.php',\n  'yii\\caching\\DummyCache' => YII2_PATH . '/caching/DummyCache.php',\n  'yii\\caching\\ExpressionDependency' => YII2_PATH . '/caching/ExpressionDependency.php',\n  'yii\\caching\\FileCache' => YII2_PATH . '/caching/FileCache.php',\n  'yii\\caching\\FileDependency' => YII2_PATH . '/caching/FileDependency.php',\n  'yii\\caching\\MemCache' => YII2_PATH . '/caching/MemCache.php',\n  'yii\\caching\\MemCacheServer' => YII2_PATH . '/caching/MemCacheServer.php',\n  'yii\\caching\\TagDependency' => YII2_PATH . '/caching/TagDependency.php',\n  'yii\\caching\\WinCache' => YII2_PATH . '/caching/WinCache.php',\n  'yii\\captcha\\Captcha' => YII2_PATH . '/captcha/Captcha.php',\n  'yii\\captcha\\CaptchaAction' => YII2_PATH . '/captcha/CaptchaAction.php',\n  'yii\\captcha\\CaptchaAsset' => YII2_PATH . '/captcha/CaptchaAsset.php',\n  'yii\\captcha\\CaptchaValidator' => YII2_PATH . '/captcha/CaptchaValidator.php',\n  'yii\\data\\ActiveDataFilter' => YII2_PATH . '/data/ActiveDataFilter.php',\n  'yii\\data\\ActiveDataProvider' => YII2_PATH . '/data/ActiveDataProvider.php',\n  'yii\\data\\ArrayDataProvider' => YII2_PATH . '/data/ArrayDataProvider.php',\n  'yii\\data\\BaseDataProvider' => YII2_PATH . '/data/BaseDataProvider.php',\n  'yii\\data\\DataFilter' => YII2_PATH . '/data/DataFilter.php',\n  'yii\\data\\DataProviderInterface' => YII2_PATH . '/data/DataProviderInterface.php',\n  'yii\\data\\Pagination' => YII2_PATH . '/data/Pagination.php',\n  'yii\\data\\Sort' => YII2_PATH . '/data/Sort.php',\n  'yii\\data\\SqlDataProvider' => YII2_PATH . '/data/SqlDataProvider.php',\n  'yii\\db\\ActiveQuery' => YII2_PATH . '/db/ActiveQuery.php',\n  'yii\\db\\ActiveQueryInterface' => YII2_PATH . '/db/ActiveQueryInterface.php',\n  'yii\\db\\ActiveQueryTrait' => YII2_PATH . '/db/ActiveQueryTrait.php',\n  'yii\\db\\ActiveRecord' => YII2_PATH . '/db/ActiveRecord.php',\n  'yii\\db\\ActiveRecordInterface' => YII2_PATH . '/db/ActiveRecordInterface.php',\n  'yii\\db\\ActiveRelationTrait' => YII2_PATH . '/db/ActiveRelationTrait.php',\n  'yii\\db\\AfterSaveEvent' => YII2_PATH . '/db/AfterSaveEvent.php',\n  'yii\\db\\ArrayExpression' => YII2_PATH . '/db/ArrayExpression.php',\n  'yii\\db\\BaseActiveRecord' => YII2_PATH . '/db/BaseActiveRecord.php',\n  'yii\\db\\BatchQueryResult' => YII2_PATH . '/db/BatchQueryResult.php',\n  'yii\\db\\CheckConstraint' => YII2_PATH . '/db/CheckConstraint.php',\n  'yii\\db\\ColumnSchema' => YII2_PATH . '/db/ColumnSchema.php',\n  'yii\\db\\ColumnSchemaBuilder' => YII2_PATH . '/db/ColumnSchemaBuilder.php',\n  'yii\\db\\Command' => YII2_PATH . '/db/Command.php',\n  'yii\\db\\Connection' => YII2_PATH . '/db/Connection.php',\n  'yii\\db\\Constraint' => YII2_PATH . '/db/Constraint.php',\n  'yii\\db\\ConstraintFinderInterface' => YII2_PATH . '/db/ConstraintFinderInterface.php',\n  'yii\\db\\ConstraintFinderTrait' => YII2_PATH . '/db/ConstraintFinderTrait.php',\n  'yii\\db\\DataReader' => YII2_PATH . '/db/DataReader.php',\n  'yii\\db\\DefaultValueConstraint' => YII2_PATH . '/db/DefaultValueConstraint.php',\n  'yii\\db\\Exception' => YII2_PATH . '/db/Exception.php',\n  'yii\\db\\Expression' => YII2_PATH . '/db/Expression.php',\n  'yii\\db\\ExpressionBuilder' => YII2_PATH . '/db/ExpressionBuilder.php',\n  'yii\\db\\ExpressionBuilderInterface' => YII2_PATH . '/db/ExpressionBuilderInterface.php',\n  'yii\\db\\ExpressionBuilderTrait' => YII2_PATH . '/db/ExpressionBuilderTrait.php',\n  'yii\\db\\ExpressionInterface' => YII2_PATH . '/db/ExpressionInterface.php',\n  'yii\\db\\ForeignKeyConstraint' => YII2_PATH . '/db/ForeignKeyConstraint.php',\n  'yii\\db\\IndexConstraint' => YII2_PATH . '/db/IndexConstraint.php',\n  'yii\\db\\IntegrityException' => YII2_PATH . '/db/IntegrityException.php',\n  'yii\\db\\JsonExpression' => YII2_PATH . '/db/JsonExpression.php',\n  'yii\\db\\Migration' => YII2_PATH . '/db/Migration.php',\n  'yii\\db\\MigrationInterface' => YII2_PATH . '/db/MigrationInterface.php',\n  'yii\\db\\PdoValue' => YII2_PATH . '/db/PdoValue.php',\n  'yii\\db\\PdoValueBuilder' => YII2_PATH . '/db/PdoValueBuilder.php',\n  'yii\\db\\Query' => YII2_PATH . '/db/Query.php',\n  'yii\\db\\QueryBuilder' => YII2_PATH . '/db/QueryBuilder.php',\n  'yii\\db\\QueryExpressionBuilder' => YII2_PATH . '/db/QueryExpressionBuilder.php',\n  'yii\\db\\QueryInterface' => YII2_PATH . '/db/QueryInterface.php',\n  'yii\\db\\QueryTrait' => YII2_PATH . '/db/QueryTrait.php',\n  'yii\\db\\Schema' => YII2_PATH . '/db/Schema.php',\n  'yii\\db\\SchemaBuilderTrait' => YII2_PATH . '/db/SchemaBuilderTrait.php',\n  'yii\\db\\SqlToken' => YII2_PATH . '/db/SqlToken.php',\n  'yii\\db\\SqlTokenizer' => YII2_PATH . '/db/SqlTokenizer.php',\n  'yii\\db\\StaleObjectException' => YII2_PATH . '/db/StaleObjectException.php',\n  'yii\\db\\TableSchema' => YII2_PATH . '/db/TableSchema.php',\n  'yii\\db\\Transaction' => YII2_PATH . '/db/Transaction.php',\n  'yii\\db\\ViewFinderTrait' => YII2_PATH . '/db/ViewFinderTrait.php',\n  'yii\\db\\conditions\\AndCondition' => YII2_PATH . '/db/conditions/AndCondition.php',\n  'yii\\db\\conditions\\BetweenColumnsCondition' => YII2_PATH . '/db/conditions/BetweenColumnsCondition.php',\n  'yii\\db\\conditions\\BetweenColumnsConditionBuilder' => YII2_PATH . '/db/conditions/BetweenColumnsConditionBuilder.php',\n  'yii\\db\\conditions\\BetweenCondition' => YII2_PATH . '/db/conditions/BetweenCondition.php',\n  'yii\\db\\conditions\\BetweenConditionBuilder' => YII2_PATH . '/db/conditions/BetweenConditionBuilder.php',\n  'yii\\db\\conditions\\ConditionInterface' => YII2_PATH . '/db/conditions/ConditionInterface.php',\n  'yii\\db\\conditions\\ConjunctionCondition' => YII2_PATH . '/db/conditions/ConjunctionCondition.php',\n  'yii\\db\\conditions\\ConjunctionConditionBuilder' => YII2_PATH . '/db/conditions/ConjunctionConditionBuilder.php',\n  'yii\\db\\conditions\\ExistsCondition' => YII2_PATH . '/db/conditions/ExistsCondition.php',\n  'yii\\db\\conditions\\ExistsConditionBuilder' => YII2_PATH . '/db/conditions/ExistsConditionBuilder.php',\n  'yii\\db\\conditions\\HashCondition' => YII2_PATH . '/db/conditions/HashCondition.php',\n  'yii\\db\\conditions\\HashConditionBuilder' => YII2_PATH . '/db/conditions/HashConditionBuilder.php',\n  'yii\\db\\conditions\\InCondition' => YII2_PATH . '/db/conditions/InCondition.php',\n  'yii\\db\\conditions\\InConditionBuilder' => YII2_PATH . '/db/conditions/InConditionBuilder.php',\n  'yii\\db\\conditions\\LikeCondition' => YII2_PATH . '/db/conditions/LikeCondition.php',\n  'yii\\db\\conditions\\LikeConditionBuilder' => YII2_PATH . '/db/conditions/LikeConditionBuilder.php',\n  'yii\\db\\conditions\\NotCondition' => YII2_PATH . '/db/conditions/NotCondition.php',\n  'yii\\db\\conditions\\NotConditionBuilder' => YII2_PATH . '/db/conditions/NotConditionBuilder.php',\n  'yii\\db\\conditions\\OrCondition' => YII2_PATH . '/db/conditions/OrCondition.php',\n  'yii\\db\\conditions\\SimpleCondition' => YII2_PATH . '/db/conditions/SimpleCondition.php',\n  'yii\\db\\conditions\\SimpleConditionBuilder' => YII2_PATH . '/db/conditions/SimpleConditionBuilder.php',\n  'yii\\db\\cubrid\\ColumnSchemaBuilder' => YII2_PATH . '/db/cubrid/ColumnSchemaBuilder.php',\n  'yii\\db\\cubrid\\QueryBuilder' => YII2_PATH . '/db/cubrid/QueryBuilder.php',\n  'yii\\db\\cubrid\\Schema' => YII2_PATH . '/db/cubrid/Schema.php',\n  'yii\\db\\cubrid\\conditions\\LikeConditionBuilder' => YII2_PATH . '/db/cubrid/conditions/LikeConditionBuilder.php',\n  'yii\\db\\mssql\\ColumnSchema' => YII2_PATH . '/db/mssql/ColumnSchema.php',\n  'yii\\db\\mssql\\ColumnSchemaBuilder' => YII2_PATH . '/db/mssql/ColumnSchemaBuilder.php',\n  'yii\\db\\mssql\\DBLibPDO' => YII2_PATH . '/db/mssql/DBLibPDO.php',\n  'yii\\db\\mssql\\PDO' => YII2_PATH . '/db/mssql/PDO.php',\n  'yii\\db\\mssql\\QueryBuilder' => YII2_PATH . '/db/mssql/QueryBuilder.php',\n  'yii\\db\\mssql\\Schema' => YII2_PATH . '/db/mssql/Schema.php',\n  'yii\\db\\mssql\\SqlsrvPDO' => YII2_PATH . '/db/mssql/SqlsrvPDO.php',\n  'yii\\db\\mssql\\TableSchema' => YII2_PATH . '/db/mssql/TableSchema.php',\n  'yii\\db\\mssql\\conditions\\InConditionBuilder' => YII2_PATH . '/db/mssql/conditions/InConditionBuilder.php',\n  'yii\\db\\mssql\\conditions\\LikeConditionBuilder' => YII2_PATH . '/db/mssql/conditions/LikeConditionBuilder.php',\n  'yii\\db\\mysql\\ColumnSchema' => YII2_PATH . '/db/mysql/ColumnSchema.php',\n  'yii\\db\\mysql\\ColumnSchemaBuilder' => YII2_PATH . '/db/mysql/ColumnSchemaBuilder.php',\n  'yii\\db\\mysql\\JsonExpressionBuilder' => YII2_PATH . '/db/mysql/JsonExpressionBuilder.php',\n  'yii\\db\\mysql\\QueryBuilder' => YII2_PATH . '/db/mysql/QueryBuilder.php',\n  'yii\\db\\mysql\\Schema' => YII2_PATH . '/db/mysql/Schema.php',\n  'yii\\db\\oci\\ColumnSchemaBuilder' => YII2_PATH . '/db/oci/ColumnSchemaBuilder.php',\n  'yii\\db\\oci\\Command' => YII2_PATH . '/db/oci/Command.php',\n  'yii\\db\\oci\\QueryBuilder' => YII2_PATH . '/db/oci/QueryBuilder.php',\n  'yii\\db\\oci\\Schema' => YII2_PATH . '/db/oci/Schema.php',\n  'yii\\db\\oci\\conditions\\InConditionBuilder' => YII2_PATH . '/db/oci/conditions/InConditionBuilder.php',\n  'yii\\db\\oci\\conditions\\LikeConditionBuilder' => YII2_PATH . '/db/oci/conditions/LikeConditionBuilder.php',\n  'yii\\db\\pgsql\\ArrayExpressionBuilder' => YII2_PATH . '/db/pgsql/ArrayExpressionBuilder.php',\n  'yii\\db\\pgsql\\ArrayParser' => YII2_PATH . '/db/pgsql/ArrayParser.php',\n  'yii\\db\\pgsql\\ColumnSchema' => YII2_PATH . '/db/pgsql/ColumnSchema.php',\n  'yii\\db\\pgsql\\JsonExpressionBuilder' => YII2_PATH . '/db/pgsql/JsonExpressionBuilder.php',\n  'yii\\db\\pgsql\\QueryBuilder' => YII2_PATH . '/db/pgsql/QueryBuilder.php',\n  'yii\\db\\pgsql\\Schema' => YII2_PATH . '/db/pgsql/Schema.php',\n  'yii\\db\\sqlite\\ColumnSchemaBuilder' => YII2_PATH . '/db/sqlite/ColumnSchemaBuilder.php',\n  'yii\\db\\sqlite\\Command' => YII2_PATH . '/db/sqlite/Command.php',\n  'yii\\db\\sqlite\\QueryBuilder' => YII2_PATH . '/db/sqlite/QueryBuilder.php',\n  'yii\\db\\sqlite\\Schema' => YII2_PATH . '/db/sqlite/Schema.php',\n  'yii\\db\\sqlite\\SqlTokenizer' => YII2_PATH . '/db/sqlite/SqlTokenizer.php',\n  'yii\\db\\sqlite\\conditions\\InConditionBuilder' => YII2_PATH . '/db/sqlite/conditions/InConditionBuilder.php',\n  'yii\\db\\sqlite\\conditions\\LikeConditionBuilder' => YII2_PATH . '/db/sqlite/conditions/LikeConditionBuilder.php',\n  'yii\\di\\Container' => YII2_PATH . '/di/Container.php',\n  'yii\\di\\Instance' => YII2_PATH . '/di/Instance.php',\n  'yii\\di\\NotInstantiableException' => YII2_PATH . '/di/NotInstantiableException.php',\n  'yii\\di\\ServiceLocator' => YII2_PATH . '/di/ServiceLocator.php',\n  'yii\\filters\\AccessControl' => YII2_PATH . '/filters/AccessControl.php',\n  'yii\\filters\\AccessRule' => YII2_PATH . '/filters/AccessRule.php',\n  'yii\\filters\\AjaxFilter' => YII2_PATH . '/filters/AjaxFilter.php',\n  'yii\\filters\\ContentNegotiator' => YII2_PATH . '/filters/ContentNegotiator.php',\n  'yii\\filters\\Cors' => YII2_PATH . '/filters/Cors.php',\n  'yii\\filters\\HostControl' => YII2_PATH . '/filters/HostControl.php',\n  'yii\\filters\\HttpCache' => YII2_PATH . '/filters/HttpCache.php',\n  'yii\\filters\\PageCache' => YII2_PATH . '/filters/PageCache.php',\n  'yii\\filters\\RateLimitInterface' => YII2_PATH . '/filters/RateLimitInterface.php',\n  'yii\\filters\\RateLimiter' => YII2_PATH . '/filters/RateLimiter.php',\n  'yii\\filters\\VerbFilter' => YII2_PATH . '/filters/VerbFilter.php',\n  'yii\\filters\\auth\\AuthInterface' => YII2_PATH . '/filters/auth/AuthInterface.php',\n  'yii\\filters\\auth\\AuthMethod' => YII2_PATH . '/filters/auth/AuthMethod.php',\n  'yii\\filters\\auth\\CompositeAuth' => YII2_PATH . '/filters/auth/CompositeAuth.php',\n  'yii\\filters\\auth\\HttpBasicAuth' => YII2_PATH . '/filters/auth/HttpBasicAuth.php',\n  'yii\\filters\\auth\\HttpBearerAuth' => YII2_PATH . '/filters/auth/HttpBearerAuth.php',\n  'yii\\filters\\auth\\HttpHeaderAuth' => YII2_PATH . '/filters/auth/HttpHeaderAuth.php',\n  'yii\\filters\\auth\\QueryParamAuth' => YII2_PATH . '/filters/auth/QueryParamAuth.php',\n  'yii\\grid\\ActionColumn' => YII2_PATH . '/grid/ActionColumn.php',\n  'yii\\grid\\CheckboxColumn' => YII2_PATH . '/grid/CheckboxColumn.php',\n  'yii\\grid\\Column' => YII2_PATH . '/grid/Column.php',\n  'yii\\grid\\DataColumn' => YII2_PATH . '/grid/DataColumn.php',\n  'yii\\grid\\GridView' => YII2_PATH . '/grid/GridView.php',\n  'yii\\grid\\GridViewAsset' => YII2_PATH . '/grid/GridViewAsset.php',\n  'yii\\grid\\RadioButtonColumn' => YII2_PATH . '/grid/RadioButtonColumn.php',\n  'yii\\grid\\SerialColumn' => YII2_PATH . '/grid/SerialColumn.php',\n  'yii\\helpers\\ArrayHelper' => YII2_PATH . '/helpers/ArrayHelper.php',\n  'yii\\helpers\\BaseArrayHelper' => YII2_PATH . '/helpers/BaseArrayHelper.php',\n  'yii\\helpers\\BaseConsole' => YII2_PATH . '/helpers/BaseConsole.php',\n  'yii\\helpers\\BaseFileHelper' => YII2_PATH . '/helpers/BaseFileHelper.php',\n  'yii\\helpers\\BaseFormatConverter' => YII2_PATH . '/helpers/BaseFormatConverter.php',\n  'yii\\helpers\\BaseHtml' => YII2_PATH . '/helpers/BaseHtml.php',\n  'yii\\helpers\\BaseHtmlPurifier' => YII2_PATH . '/helpers/BaseHtmlPurifier.php',\n  'yii\\helpers\\BaseInflector' => YII2_PATH . '/helpers/BaseInflector.php',\n  'yii\\helpers\\BaseIpHelper' => YII2_PATH . '/helpers/BaseIpHelper.php',\n  'yii\\helpers\\BaseJson' => YII2_PATH . '/helpers/BaseJson.php',\n  'yii\\helpers\\BaseMarkdown' => YII2_PATH . '/helpers/BaseMarkdown.php',\n  'yii\\helpers\\BaseStringHelper' => YII2_PATH . '/helpers/BaseStringHelper.php',\n  'yii\\helpers\\BaseUrl' => YII2_PATH . '/helpers/BaseUrl.php',\n  'yii\\helpers\\BaseVarDumper' => YII2_PATH . '/helpers/BaseVarDumper.php',\n  'yii\\helpers\\Console' => YII2_PATH . '/helpers/Console.php',\n  'yii\\helpers\\FileHelper' => YII2_PATH . '/helpers/FileHelper.php',\n  'yii\\helpers\\FormatConverter' => YII2_PATH . '/helpers/FormatConverter.php',\n  'yii\\helpers\\Html' => YII2_PATH . '/helpers/Html.php',\n  'yii\\helpers\\HtmlPurifier' => YII2_PATH . '/helpers/HtmlPurifier.php',\n  'yii\\helpers\\Inflector' => YII2_PATH . '/helpers/Inflector.php',\n  'yii\\helpers\\IpHelper' => YII2_PATH . '/helpers/IpHelper.php',\n  'yii\\helpers\\Json' => YII2_PATH . '/helpers/Json.php',\n  'yii\\helpers\\Markdown' => YII2_PATH . '/helpers/Markdown.php',\n  'yii\\helpers\\ReplaceArrayValue' => YII2_PATH . '/helpers/ReplaceArrayValue.php',\n  'yii\\helpers\\StringHelper' => YII2_PATH . '/helpers/StringHelper.php',\n  'yii\\helpers\\UnsetArrayValue' => YII2_PATH . '/helpers/UnsetArrayValue.php',\n  'yii\\helpers\\Url' => YII2_PATH . '/helpers/Url.php',\n  'yii\\helpers\\VarDumper' => YII2_PATH . '/helpers/VarDumper.php',\n  'yii\\i18n\\DbMessageSource' => YII2_PATH . '/i18n/DbMessageSource.php',\n  'yii\\i18n\\Formatter' => YII2_PATH . '/i18n/Formatter.php',\n  'yii\\i18n\\GettextFile' => YII2_PATH . '/i18n/GettextFile.php',\n  'yii\\i18n\\GettextMessageSource' => YII2_PATH . '/i18n/GettextMessageSource.php',\n  'yii\\i18n\\GettextMoFile' => YII2_PATH . '/i18n/GettextMoFile.php',\n  'yii\\i18n\\GettextPoFile' => YII2_PATH . '/i18n/GettextPoFile.php',\n  'yii\\i18n\\I18N' => YII2_PATH . '/i18n/I18N.php',\n  'yii\\i18n\\Locale' => YII2_PATH . '/i18n/Locale.php',\n  'yii\\i18n\\MessageFormatter' => YII2_PATH . '/i18n/MessageFormatter.php',\n  'yii\\i18n\\MessageSource' => YII2_PATH . '/i18n/MessageSource.php',\n  'yii\\i18n\\MissingTranslationEvent' => YII2_PATH . '/i18n/MissingTranslationEvent.php',\n  'yii\\i18n\\PhpMessageSource' => YII2_PATH . '/i18n/PhpMessageSource.php',\n  'yii\\log\\DbTarget' => YII2_PATH . '/log/DbTarget.php',\n  'yii\\log\\Dispatcher' => YII2_PATH . '/log/Dispatcher.php',\n  'yii\\log\\EmailTarget' => YII2_PATH . '/log/EmailTarget.php',\n  'yii\\log\\FileTarget' => YII2_PATH . '/log/FileTarget.php',\n  'yii\\log\\LogRuntimeException' => YII2_PATH . '/log/LogRuntimeException.php',\n  'yii\\log\\Logger' => YII2_PATH . '/log/Logger.php',\n  'yii\\log\\SyslogTarget' => YII2_PATH . '/log/SyslogTarget.php',\n  'yii\\log\\Target' => YII2_PATH . '/log/Target.php',\n  'yii\\mail\\BaseMailer' => YII2_PATH . '/mail/BaseMailer.php',\n  'yii\\mail\\BaseMessage' => YII2_PATH . '/mail/BaseMessage.php',\n  'yii\\mail\\MailEvent' => YII2_PATH . '/mail/MailEvent.php',\n  'yii\\mail\\MailerInterface' => YII2_PATH . '/mail/MailerInterface.php',\n  'yii\\mail\\MessageInterface' => YII2_PATH . '/mail/MessageInterface.php',\n  'yii\\mutex\\DbMutex' => YII2_PATH . '/mutex/DbMutex.php',\n  'yii\\mutex\\FileMutex' => YII2_PATH . '/mutex/FileMutex.php',\n  'yii\\mutex\\Mutex' => YII2_PATH . '/mutex/Mutex.php',\n  'yii\\mutex\\MysqlMutex' => YII2_PATH . '/mutex/MysqlMutex.php',\n  'yii\\mutex\\OracleMutex' => YII2_PATH . '/mutex/OracleMutex.php',\n  'yii\\mutex\\PgsqlMutex' => YII2_PATH . '/mutex/PgsqlMutex.php',\n  'yii\\mutex\\RetryAcquireTrait' => YII2_PATH . '/mutex/RetryAcquireTrait.php',\n  'yii\\rbac\\Assignment' => YII2_PATH . '/rbac/Assignment.php',\n  'yii\\rbac\\BaseManager' => YII2_PATH . '/rbac/BaseManager.php',\n  'yii\\rbac\\CheckAccessInterface' => YII2_PATH . '/rbac/CheckAccessInterface.php',\n  'yii\\rbac\\DbManager' => YII2_PATH . '/rbac/DbManager.php',\n  'yii\\rbac\\Item' => YII2_PATH . '/rbac/Item.php',\n  'yii\\rbac\\ManagerInterface' => YII2_PATH . '/rbac/ManagerInterface.php',\n  'yii\\rbac\\Permission' => YII2_PATH . '/rbac/Permission.php',\n  'yii\\rbac\\PhpManager' => YII2_PATH . '/rbac/PhpManager.php',\n  'yii\\rbac\\Role' => YII2_PATH . '/rbac/Role.php',\n  'yii\\rbac\\Rule' => YII2_PATH . '/rbac/Rule.php',\n  'yii\\rest\\Action' => YII2_PATH . '/rest/Action.php',\n  'yii\\rest\\ActiveController' => YII2_PATH . '/rest/ActiveController.php',\n  'yii\\rest\\Controller' => YII2_PATH . '/rest/Controller.php',\n  'yii\\rest\\CreateAction' => YII2_PATH . '/rest/CreateAction.php',\n  'yii\\rest\\DeleteAction' => YII2_PATH . '/rest/DeleteAction.php',\n  'yii\\rest\\IndexAction' => YII2_PATH . '/rest/IndexAction.php',\n  'yii\\rest\\OptionsAction' => YII2_PATH . '/rest/OptionsAction.php',\n  'yii\\rest\\Serializer' => YII2_PATH . '/rest/Serializer.php',\n  'yii\\rest\\UpdateAction' => YII2_PATH . '/rest/UpdateAction.php',\n  'yii\\rest\\UrlRule' => YII2_PATH . '/rest/UrlRule.php',\n  'yii\\rest\\ViewAction' => YII2_PATH . '/rest/ViewAction.php',\n  'yii\\test\\ActiveFixture' => YII2_PATH . '/test/ActiveFixture.php',\n  'yii\\test\\ArrayFixture' => YII2_PATH . '/test/ArrayFixture.php',\n  'yii\\test\\BaseActiveFixture' => YII2_PATH . '/test/BaseActiveFixture.php',\n  'yii\\test\\DbFixture' => YII2_PATH . '/test/DbFixture.php',\n  'yii\\test\\FileFixtureTrait' => YII2_PATH . '/test/FileFixtureTrait.php',\n  'yii\\test\\Fixture' => YII2_PATH . '/test/Fixture.php',\n  'yii\\test\\FixtureTrait' => YII2_PATH . '/test/FixtureTrait.php',\n  'yii\\test\\InitDbFixture' => YII2_PATH . '/test/InitDbFixture.php',\n  'yii\\validators\\BooleanValidator' => YII2_PATH . '/validators/BooleanValidator.php',\n  'yii\\validators\\CompareValidator' => YII2_PATH . '/validators/CompareValidator.php',\n  'yii\\validators\\DateValidator' => YII2_PATH . '/validators/DateValidator.php',\n  'yii\\validators\\DefaultValueValidator' => YII2_PATH . '/validators/DefaultValueValidator.php',\n  'yii\\validators\\EachValidator' => YII2_PATH . '/validators/EachValidator.php',\n  'yii\\validators\\EmailValidator' => YII2_PATH . '/validators/EmailValidator.php',\n  'yii\\validators\\ExistValidator' => YII2_PATH . '/validators/ExistValidator.php',\n  'yii\\validators\\FileValidator' => YII2_PATH . '/validators/FileValidator.php',\n  'yii\\validators\\FilterValidator' => YII2_PATH . '/validators/FilterValidator.php',\n  'yii\\validators\\ImageValidator' => YII2_PATH . '/validators/ImageValidator.php',\n  'yii\\validators\\InlineValidator' => YII2_PATH . '/validators/InlineValidator.php',\n  'yii\\validators\\IpValidator' => YII2_PATH . '/validators/IpValidator.php',\n  'yii\\validators\\NumberValidator' => YII2_PATH . '/validators/NumberValidator.php',\n  'yii\\validators\\PunycodeAsset' => YII2_PATH . '/validators/PunycodeAsset.php',\n  'yii\\validators\\RangeValidator' => YII2_PATH . '/validators/RangeValidator.php',\n  'yii\\validators\\RegularExpressionValidator' => YII2_PATH . '/validators/RegularExpressionValidator.php',\n  'yii\\validators\\RequiredValidator' => YII2_PATH . '/validators/RequiredValidator.php',\n  'yii\\validators\\SafeValidator' => YII2_PATH . '/validators/SafeValidator.php',\n  'yii\\validators\\StringValidator' => YII2_PATH . '/validators/StringValidator.php',\n  'yii\\validators\\TrimValidator' => YII2_PATH . '/validators/TrimValidator.php',\n  'yii\\validators\\UniqueValidator' => YII2_PATH . '/validators/UniqueValidator.php',\n  'yii\\validators\\UrlValidator' => YII2_PATH . '/validators/UrlValidator.php',\n  'yii\\validators\\ValidationAsset' => YII2_PATH . '/validators/ValidationAsset.php',\n  'yii\\validators\\Validator' => YII2_PATH . '/validators/Validator.php',\n  'yii\\web\\Application' => YII2_PATH . '/web/Application.php',\n  'yii\\web\\AssetBundle' => YII2_PATH . '/web/AssetBundle.php',\n  'yii\\web\\AssetConverter' => YII2_PATH . '/web/AssetConverter.php',\n  'yii\\web\\AssetConverterInterface' => YII2_PATH . '/web/AssetConverterInterface.php',\n  'yii\\web\\AssetManager' => YII2_PATH . '/web/AssetManager.php',\n  'yii\\web\\BadRequestHttpException' => YII2_PATH . '/web/BadRequestHttpException.php',\n  'yii\\web\\CacheSession' => YII2_PATH . '/web/CacheSession.php',\n  'yii\\web\\CompositeUrlRule' => YII2_PATH . '/web/CompositeUrlRule.php',\n  'yii\\web\\ConflictHttpException' => YII2_PATH . '/web/ConflictHttpException.php',\n  'yii\\web\\Controller' => YII2_PATH . '/web/Controller.php',\n  'yii\\web\\Cookie' => YII2_PATH . '/web/Cookie.php',\n  'yii\\web\\CookieCollection' => YII2_PATH . '/web/CookieCollection.php',\n  'yii\\web\\DbSession' => YII2_PATH . '/web/DbSession.php',\n  'yii\\web\\ErrorAction' => YII2_PATH . '/web/ErrorAction.php',\n  'yii\\web\\ErrorHandler' => YII2_PATH . '/web/ErrorHandler.php',\n  'yii\\web\\ForbiddenHttpException' => YII2_PATH . '/web/ForbiddenHttpException.php',\n  'yii\\web\\GoneHttpException' => YII2_PATH . '/web/GoneHttpException.php',\n  'yii\\web\\GroupUrlRule' => YII2_PATH . '/web/GroupUrlRule.php',\n  'yii\\web\\HeaderCollection' => YII2_PATH . '/web/HeaderCollection.php',\n  'yii\\web\\HeadersAlreadySentException' => YII2_PATH . '/web/HeadersAlreadySentException.php',\n  'yii\\web\\HtmlResponseFormatter' => YII2_PATH . '/web/HtmlResponseFormatter.php',\n  'yii\\web\\HttpException' => YII2_PATH . '/web/HttpException.php',\n  'yii\\web\\IdentityInterface' => YII2_PATH . '/web/IdentityInterface.php',\n  'yii\\web\\JqueryAsset' => YII2_PATH . '/web/JqueryAsset.php',\n  'yii\\web\\JsExpression' => YII2_PATH . '/web/JsExpression.php',\n  'yii\\web\\JsonParser' => YII2_PATH . '/web/JsonParser.php',\n  'yii\\web\\JsonResponseFormatter' => YII2_PATH . '/web/JsonResponseFormatter.php',\n  'yii\\web\\Link' => YII2_PATH . '/web/Link.php',\n  'yii\\web\\Linkable' => YII2_PATH . '/web/Linkable.php',\n  'yii\\web\\MethodNotAllowedHttpException' => YII2_PATH . '/web/MethodNotAllowedHttpException.php',\n  'yii\\web\\MultiFieldSession' => YII2_PATH . '/web/MultiFieldSession.php',\n  'yii\\web\\MultipartFormDataParser' => YII2_PATH . '/web/MultipartFormDataParser.php',\n  'yii\\web\\NotAcceptableHttpException' => YII2_PATH . '/web/NotAcceptableHttpException.php',\n  'yii\\web\\NotFoundHttpException' => YII2_PATH . '/web/NotFoundHttpException.php',\n  'yii\\web\\RangeNotSatisfiableHttpException' => YII2_PATH . '/web/RangeNotSatisfiableHttpException.php',\n  'yii\\web\\Request' => YII2_PATH . '/web/Request.php',\n  'yii\\web\\RequestParserInterface' => YII2_PATH . '/web/RequestParserInterface.php',\n  'yii\\web\\Response' => YII2_PATH . '/web/Response.php',\n  'yii\\web\\ResponseFormatterInterface' => YII2_PATH . '/web/ResponseFormatterInterface.php',\n  'yii\\web\\ServerErrorHttpException' => YII2_PATH . '/web/ServerErrorHttpException.php',\n  'yii\\web\\Session' => YII2_PATH . '/web/Session.php',\n  'yii\\web\\SessionHandler' => YII2_PATH . '/web/SessionHandler.php',\n  'yii\\web\\SessionIterator' => YII2_PATH . '/web/SessionIterator.php',\n  'yii\\web\\TooManyRequestsHttpException' => YII2_PATH . '/web/TooManyRequestsHttpException.php',\n  'yii\\web\\UnauthorizedHttpException' => YII2_PATH . '/web/UnauthorizedHttpException.php',\n  'yii\\web\\UnprocessableEntityHttpException' => YII2_PATH . '/web/UnprocessableEntityHttpException.php',\n  'yii\\web\\UnsupportedMediaTypeHttpException' => YII2_PATH . '/web/UnsupportedMediaTypeHttpException.php',\n  'yii\\web\\UploadedFile' => YII2_PATH . '/web/UploadedFile.php',\n  'yii\\web\\UrlManager' => YII2_PATH . '/web/UrlManager.php',\n  'yii\\web\\UrlNormalizer' => YII2_PATH . '/web/UrlNormalizer.php',\n  'yii\\web\\UrlNormalizerRedirectException' => YII2_PATH . '/web/UrlNormalizerRedirectException.php',\n  'yii\\web\\UrlRule' => YII2_PATH . '/web/UrlRule.php',\n  'yii\\web\\UrlRuleInterface' => YII2_PATH . '/web/UrlRuleInterface.php',\n  'yii\\web\\User' => YII2_PATH . '/web/User.php',\n  'yii\\web\\UserEvent' => YII2_PATH . '/web/UserEvent.php',\n  'yii\\web\\View' => YII2_PATH . '/web/View.php',\n  'yii\\web\\ViewAction' => YII2_PATH . '/web/ViewAction.php',\n  'yii\\web\\XmlResponseFormatter' => YII2_PATH . '/web/XmlResponseFormatter.php',\n  'yii\\web\\YiiAsset' => YII2_PATH . '/web/YiiAsset.php',\n  'yii\\widgets\\ActiveField' => YII2_PATH . '/widgets/ActiveField.php',\n  'yii\\widgets\\ActiveForm' => YII2_PATH . '/widgets/ActiveForm.php',\n  'yii\\widgets\\ActiveFormAsset' => YII2_PATH . '/widgets/ActiveFormAsset.php',\n  'yii\\widgets\\BaseListView' => YII2_PATH . '/widgets/BaseListView.php',\n  'yii\\widgets\\Block' => YII2_PATH . '/widgets/Block.php',\n  'yii\\widgets\\Breadcrumbs' => YII2_PATH . '/widgets/Breadcrumbs.php',\n  'yii\\widgets\\ContentDecorator' => YII2_PATH . '/widgets/ContentDecorator.php',\n  'yii\\widgets\\DetailView' => YII2_PATH . '/widgets/DetailView.php',\n  'yii\\widgets\\FragmentCache' => YII2_PATH . '/widgets/FragmentCache.php',\n  'yii\\widgets\\InputWidget' => YII2_PATH . '/widgets/InputWidget.php',\n  'yii\\widgets\\LinkPager' => YII2_PATH . '/widgets/LinkPager.php',\n  'yii\\widgets\\LinkSorter' => YII2_PATH . '/widgets/LinkSorter.php',\n  'yii\\widgets\\ListView' => YII2_PATH . '/widgets/ListView.php',\n  'yii\\widgets\\MaskedInput' => YII2_PATH . '/widgets/MaskedInput.php',\n  'yii\\widgets\\MaskedInputAsset' => YII2_PATH . '/widgets/MaskedInputAsset.php',\n  'yii\\widgets\\Menu' => YII2_PATH . '/widgets/Menu.php',\n  'yii\\widgets\\Pjax' => YII2_PATH . '/widgets/Pjax.php',\n  'yii\\widgets\\PjaxAsset' => YII2_PATH . '/widgets/PjaxAsset.php',\n  'yii\\widgets\\Spaceless' => YII2_PATH . '/widgets/Spaceless.php',\n];\n"
  },
  {
    "path": "framework/composer.json",
    "content": "{\n    \"name\": \"yiisoft/yii2\",\n    \"description\": \"Yii PHP Framework Version 2\",\n    \"keywords\": [\n        \"yii2\",\n        \"framework\"\n    ],\n    \"homepage\": \"https://www.yiiframework.com/\",\n    \"type\": \"library\",\n    \"license\": \"BSD-3-Clause\",\n    \"authors\": [\n        {\n            \"name\": \"Qiang Xue\",\n            \"email\": \"qiang.xue@gmail.com\",\n            \"homepage\": \"https://www.yiiframework.com/\",\n            \"role\": \"Founder and project lead\"\n        },\n        {\n            \"name\": \"Alexander Makarov\",\n            \"email\": \"sam@rmcreative.ru\",\n            \"homepage\": \"https://rmcreative.ru/\",\n            \"role\": \"Core framework development\"\n        },\n        {\n            \"name\": \"Maurizio Domba\",\n            \"homepage\": \"http://mdomba.info/\",\n            \"role\": \"Core framework development\"\n        },\n        {\n            \"name\": \"Carsten Brandt\",\n            \"email\": \"mail@cebe.cc\",\n            \"homepage\": \"https://www.cebe.cc/\",\n            \"role\": \"Core framework development\"\n        },\n        {\n            \"name\": \"Timur Ruziev\",\n            \"email\": \"resurtm@gmail.com\",\n            \"homepage\": \"http://resurtm.com/\",\n            \"role\": \"Core framework development\"\n        },\n        {\n            \"name\": \"Paul Klimov\",\n            \"email\": \"klimov.paul@gmail.com\",\n            \"role\": \"Core framework development\"\n        },\n        {\n            \"name\": \"Dmitry Naumenko\",\n            \"email\": \"d.naumenko.a@gmail.com\",\n            \"role\": \"Core framework development\"\n        },\n        {\n            \"name\": \"Boudewijn Vahrmeijer\",\n            \"email\": \"info@dynasource.eu\",\n            \"homepage\": \"http://dynasource.eu\",\n            \"role\": \"Core framework development\"\n        }\n    ],\n    \"support\": {\n        \"issues\": \"https://github.com/yiisoft/yii2/issues?state=open\",\n        \"forum\": \"https://forum.yiiframework.com/\",\n        \"wiki\": \"https://www.yiiframework.com/wiki\",\n        \"irc\": \"ircs://irc.libera.chat:6697/yii\",\n        \"source\": \"https://github.com/yiisoft/yii2\"\n    },\n    \"require\": {\n        \"php\": \">=7.4.0\",\n        \"ext-mbstring\": \"*\",\n        \"ext-ctype\": \"*\",\n        \"lib-pcre\": \"*\",\n        \"yiisoft/yii2-composer\": \"~2.0.4\",\n        \"ezyang/htmlpurifier\": \"^4.17\",\n        \"cebe/markdown\": \"~1.0.0 | ~1.1.0 | ~1.2.0\",\n        \"bower-asset/jquery\": \"3.7.*@stable | 3.6.*@stable | 3.5.*@stable | 3.4.*@stable | 3.3.*@stable | 3.2.*@stable | 3.1.*@stable | 2.2.*@stable | 2.1.*@stable | 1.11.*@stable | 1.12.*@stable\",\n        \"bower-asset/inputmask\": \"^5.0.8 \",\n        \"bower-asset/punycode\": \"^2.2\",\n        \"bower-asset/yii2-pjax\": \"~2.0.1\"\n    },\n    \"autoload\": {\n        \"psr-4\": {\"yii\\\\\": \"\"}\n    },\n    \"bin\": [\n        \"yii\"\n    ],\n    \"extra\": {\n        \"branch-alias\": {\n            \"dev-master\": \"2.0.x-dev\"\n        }\n    }\n}\n"
  },
  {
    "path": "framework/console/Application.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\console;\n\nuse Yii;\nuse yii\\base\\InvalidRouteException;\nuse yii\\base\\Module;\n\n// define STDIN, STDOUT and STDERR if the PHP SAPI did not define them (e.g. creating console application in web env)\n// https://www.php.net/manual/en/features.commandline.io-streams.php\ndefined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));\ndefined('STDOUT') or define('STDOUT', fopen('php://stdout', 'w'));\ndefined('STDERR') or define('STDERR', fopen('php://stderr', 'w'));\n\n/**\n * Application represents a console application.\n *\n * Application extends from [[\\yii\\base\\Application]] by providing functionalities that are\n * specific to console requests. In particular, it deals with console requests\n * through a command-based approach:\n *\n * - A console application consists of one or several possible user commands;\n * - Each user command is implemented as a class extending [[\\yii\\console\\Controller]];\n * - User specifies which command to run on the command line;\n * - The command processes the user request with the specified parameters.\n *\n * The command classes should be under the namespace specified by [[controllerNamespace]].\n * Their naming should follow the same naming convention as controllers. For example, the `help` command\n * is implemented using the `HelpController` class.\n *\n * To run the console application, enter the following on the command line:\n *\n * ```\n * yii <route> [--param1=value1 --param2 ...]\n * ```\n *\n * where `<route>` refers to a controller route in the form of `ModuleID/ControllerID/ActionID`\n * (e.g. `sitemap/create`), and `param1`, `param2` refers to a set of named parameters that\n * will be used to initialize the controller action (e.g. `--since=0` specifies a `since` parameter\n * whose value is 0 and a corresponding `$since` parameter is passed to the action method).\n *\n * A `help` command is provided by default, which lists available commands and shows their usage.\n * To use this command, simply type:\n *\n * ```\n * yii help\n * ```\n *\n * @property-read ErrorHandler $errorHandler The error handler application component.\n * @property-read Request $request The request component.\n * @property-read Response $response The response component.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Application extends \\yii\\base\\Application\n{\n    /**\n     * The option name for specifying the application configuration file path.\n     */\n    public const OPTION_APPCONFIG = 'appconfig';\n    /**\n     * @var string the default route of this application. Defaults to 'help',\n     * meaning the `help` command.\n     */\n    public $defaultRoute = 'help';\n    /**\n     * @var bool whether to enable the commands provided by the core framework.\n     * Defaults to true.\n     */\n    public $enableCoreCommands = true;\n    /**\n     * @var Controller|null the currently active controller instance\n     */\n    public $controller;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function __construct($config = [])\n    {\n        $config = $this->loadConfig($config);\n        parent::__construct($config);\n    }\n\n    /**\n     * Loads the configuration.\n     * This method will check if the command line option [[OPTION_APPCONFIG]] is specified.\n     * If so, the corresponding file will be loaded as the application configuration.\n     * Otherwise, the configuration provided as the parameter will be returned back.\n     * @param array $config the configuration provided in the constructor.\n     * @return array the actual configuration to be used by the application.\n     */\n    protected function loadConfig($config)\n    {\n        if (!empty($_SERVER['argv'])) {\n            $option = '--' . self::OPTION_APPCONFIG . '=';\n            foreach ($_SERVER['argv'] as $param) {\n                if (strpos($param, $option) !== false) {\n                    $path = substr($param, strlen($option));\n                    if (!empty($path) && is_file($file = Yii::getAlias($path))) {\n                        return require $file;\n                    }\n\n                    exit(\"The configuration file does not exist: $path\\n\");\n                }\n            }\n        }\n\n        return $config;\n    }\n\n    /**\n     * Initialize the application.\n     */\n    public function init()\n    {\n        parent::init();\n        if ($this->enableCoreCommands) {\n            foreach ($this->coreCommands() as $id => $command) {\n                if (!isset($this->controllerMap[$id])) {\n                    $this->controllerMap[$id] = $command;\n                }\n            }\n        }\n        // ensure we have the 'help' command so that we can list the available commands\n        if (!isset($this->controllerMap['help'])) {\n            $this->controllerMap['help'] = 'yii\\console\\controllers\\HelpController';\n        }\n    }\n\n    /**\n     * Handles the specified request.\n     * @param Request $request the request to be handled\n     * @return Response the resulting response\n     */\n    public function handleRequest($request)\n    {\n        list($route, $params) = $request->resolve();\n        $this->requestedRoute = $route;\n        $result = $this->runAction($route, $params);\n        if ($result instanceof Response) {\n            return $result;\n        }\n\n        $response = $this->getResponse();\n        $response->exitStatus = $result;\n\n        return $response;\n    }\n\n    /**\n     * Runs a controller action specified by a route.\n     * This method parses the specified route and creates the corresponding child module(s), controller and action\n     * instances. It then calls [[Controller::runAction()]] to run the action with the given parameters.\n     * If the route is empty, the method will use [[defaultRoute]].\n     *\n     * For example, to run `public function actionTest($a, $b)` assuming that the controller has options the following\n     * code should be used:\n     *\n     * ```\n     * \\Yii::$app->runAction('controller/test', ['option' => 'value', $a, $b]);\n     * ```\n     *\n     * @param string $route the route that specifies the action.\n     * @param array $params the parameters to be passed to the action\n     * @return int|Response|null the result of the action. This can be either an exit code or Response object.\n     * Exit code 0 means normal, and other values mean abnormal. Exit code of `null` is treated as `0` as well.\n     * @throws Exception if the route is invalid\n     */\n    public function runAction($route, $params = [])\n    {\n        try {\n            $res = parent::runAction($route, $params);\n            return is_object($res) ? $res : (int) $res;\n        } catch (InvalidRouteException $e) {\n            throw new UnknownCommandException($route, $this, 0, $e);\n        }\n    }\n\n    /**\n     * Returns the configuration of the built-in commands.\n     * @return array the configuration of the built-in commands.\n     */\n    public function coreCommands()\n    {\n        return [\n            'asset' => 'yii\\console\\controllers\\AssetController',\n            'cache' => 'yii\\console\\controllers\\CacheController',\n            'fixture' => 'yii\\console\\controllers\\FixtureController',\n            'help' => 'yii\\console\\controllers\\HelpController',\n            'message' => 'yii\\console\\controllers\\MessageController',\n            'migrate' => 'yii\\console\\controllers\\MigrateController',\n            'serve' => 'yii\\console\\controllers\\ServeController',\n        ];\n    }\n\n    /**\n     * Returns the error handler component.\n     * @return ErrorHandler the error handler application component.\n     */\n    public function getErrorHandler()\n    {\n        return $this->get('errorHandler');\n    }\n\n    /**\n     * Returns the request component.\n     * @return Request the request component.\n     */\n    public function getRequest()\n    {\n        return $this->get('request');\n    }\n\n    /**\n     * Returns the response component.\n     * @return Response the response component.\n     */\n    public function getResponse()\n    {\n        return $this->get('response');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function coreComponents()\n    {\n        return array_merge(parent::coreComponents(), [\n            'request' => ['class' => 'yii\\console\\Request'],\n            'response' => ['class' => 'yii\\console\\Response'],\n            'errorHandler' => ['class' => 'yii\\console\\ErrorHandler'],\n        ]);\n    }\n}\n"
  },
  {
    "path": "framework/console/Controller.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\console;\n\nuse Yii;\nuse yii\\base\\Action;\nuse yii\\base\\InlineAction;\nuse yii\\base\\InvalidRouteException;\nuse yii\\helpers\\Console;\nuse yii\\helpers\\Inflector;\nuse yii\\base\\Controller as BaseController;\nuse yii\\base\\Module;\n\n/**\n * Controller is the base class of console command classes.\n *\n * A console controller consists of one or several actions known as sub-commands.\n * Users call a console command by specifying the corresponding route which identifies a controller action.\n * The `yii` program is used when calling a console command, like the following:\n *\n * ```\n * yii <route> [--param1=value1 --param2 ...]\n * ```\n *\n * where `<route>` is a route to a controller action and the params will be populated as properties of a command.\n * See [[options()]] for details.\n *\n * @property Request $request The request object.\n * @property Response $response The response object.\n * @property-read string $help The help information for this controller.\n * @property-write bool $help Whether to display help information about current command.\n * @property-read string $helpSummary The one-line short summary describing this controller.\n * @property-read array $passedOptionValues The properties corresponding to the passed options.\n * @property-read array $passedOptions The names of the options passed during execution.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Module = Module\n * @extends BaseController<T>\n */\nclass Controller extends BaseController\n{\n    /**\n     * @deprecated since 2.0.13. Use [[ExitCode::OK]] instead.\n     */\n    public const EXIT_CODE_NORMAL = 0;\n    /**\n     * @deprecated since 2.0.13. Use [[ExitCode::UNSPECIFIED_ERROR]] instead.\n     */\n    public const EXIT_CODE_ERROR = 1;\n    /**\n     * @var bool whether to run the command interactively.\n     */\n    public $interactive = true;\n    /**\n     * @var bool|null whether to enable ANSI color in the output.\n     * If not set, ANSI color will only be enabled for terminals that support it.\n     */\n    public $color;\n    /**\n     * @var bool whether to display help information about current command.\n     * @since 2.0.10\n     */\n    public $help = false;\n    /**\n     * @var bool|null if true - script finish with `ExitCode::OK` in case of exception.\n     * false - `ExitCode::UNSPECIFIED_ERROR`.\n     * Default: `YII_ENV_TEST`\n     * @since 2.0.36\n     */\n    public $silentExitOnException;\n\n    /**\n     * @var array the options passed during execution.\n     */\n    private $_passedOptions = [];\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function beforeAction($action)\n    {\n        $silentExit = $this->silentExitOnException !== null ? $this->silentExitOnException : YII_ENV_TEST;\n        Yii::$app->errorHandler->silentExitOnException = $silentExit;\n\n        return parent::beforeAction($action);\n    }\n\n    /**\n     * Returns a value indicating whether ANSI color is enabled.\n     *\n     * ANSI color is enabled only if [[color]] is set true or is not set\n     * and the terminal supports ANSI color.\n     *\n     * @param resource $stream the stream to check.\n     * @return bool Whether to enable ANSI style in output.\n     */\n    public function isColorEnabled($stream = \\STDOUT)\n    {\n        return $this->color === null ? Console::streamSupportsAnsiColors($stream) : $this->color;\n    }\n\n    /**\n     * Runs an action with the specified action ID and parameters.\n     * If the action ID is empty, the method will use [[defaultAction]].\n     * @param string $id the ID of the action to be executed.\n     * @param array $params the parameters (name-value pairs) to be passed to the action.\n     * @return mixed the result of the action.\n     * @throws InvalidRouteException if the requested action ID cannot be resolved into an action successfully.\n     * @throws Exception if there are unknown options or missing arguments\n     * @see createAction\n     */\n    public function runAction($id, $params = [])\n    {\n        if (!empty($params)) {\n            // populate options here so that they are available in beforeAction().\n            $options = $this->options($id === '' ? $this->defaultAction : $id);\n            if (isset($params['_aliases'])) {\n                $optionAliases = $this->optionAliases();\n                foreach ($params['_aliases'] as $name => $value) {\n                    if (array_key_exists($name, $optionAliases)) {\n                        $params[$optionAliases[$name]] = $value;\n                    } else {\n                        $message = Yii::t('yii', 'Unknown alias: -{name}', ['name' => $name]);\n                        if (!empty($optionAliases)) {\n                            $aliasesAvailable = [];\n                            foreach ($optionAliases as $alias => $option) {\n                                $aliasesAvailable[] = '-' . $alias . ' (--' . $option . ')';\n                            }\n\n                            $message .= '. ' . Yii::t('yii', 'Aliases available: {aliases}', [\n                                'aliases' => implode(', ', $aliasesAvailable)\n                            ]);\n                        }\n                        throw new Exception($message);\n                    }\n                }\n                unset($params['_aliases']);\n            }\n            foreach ($params as $name => $value) {\n                // Allow camelCase options to be entered in kebab-case\n                if (!in_array($name, $options, true) && strpos($name, '-') !== false) {\n                    $kebabName = $name;\n                    $altName = lcfirst(Inflector::id2camel($kebabName));\n                    if (in_array($altName, $options, true)) {\n                        $name = $altName;\n                    }\n                }\n\n                if (in_array($name, $options, true)) {\n                    $default = $this->$name;\n                    if (is_array($default) && is_string($value)) {\n                        $this->$name = preg_split('/\\s*,\\s*(?![^()]*\\))/', $value);\n                    } elseif ($default !== null) {\n                        settype($value, gettype($default));\n                        $this->$name = $value;\n                    } else {\n                        $this->$name = $value;\n                    }\n                    $this->_passedOptions[] = $name;\n                    unset($params[$name]);\n                    if (isset($kebabName)) {\n                        unset($params[$kebabName]);\n                    }\n                } elseif (!is_int($name)) {\n                    $message = Yii::t('yii', 'Unknown option: --{name}', ['name' => $name]);\n                    if (!empty($options)) {\n                        $message .= '. ' . Yii::t('yii', 'Options available: {options}', ['options' => '--' . implode(', --', $options)]);\n                    }\n\n                    throw new Exception($message);\n                }\n            }\n        }\n        if ($this->help) {\n            $route = $this->getUniqueId() . '/' . $id;\n            return Yii::$app->runAction('help', [$route]);\n        }\n\n        return parent::runAction($id, $params);\n    }\n\n    /**\n     * Binds the parameters to the action.\n     * This method is invoked by [[Action]] when it begins to run with the given parameters.\n     * This method will first bind the parameters with the [[options()|options]]\n     * available to the action. It then validates the given arguments.\n     * @param Action<static> $action the action to be bound with parameters\n     * @param array<array-key, mixed> $params the parameters to be bound to the action\n     * @return mixed[] the valid parameters that the action can run with.\n     * @throws Exception if there are unknown options or missing arguments\n     *\n     * @phpstan-param Action<static> $action\n     * @psalm-param Action<self> $action\n     */\n    public function bindActionParams($action, $params)\n    {\n        if ($action instanceof InlineAction) {\n            $method = new \\ReflectionMethod($this, $action->actionMethod);\n        } else {\n            $method = new \\ReflectionMethod($action, 'run');\n        }\n\n        $paramKeys = array_keys($params);\n        $args = [];\n        $missing = [];\n        $actionParams = [];\n        $requestedParams = [];\n        foreach ($method->getParameters() as $i => $param) {\n            $name = $param->getName();\n            $key = null;\n            if (array_key_exists($i, $params)) {\n                $key = $i;\n            } elseif (array_key_exists($name, $params)) {\n                $key = $name;\n            }\n\n            if ($key !== null) {\n                if ($param->isVariadic()) {\n                    for ($j = array_search($key, $paramKeys); $j < count($paramKeys); $j++) {\n                        $jKey = $paramKeys[$j];\n                        if ($jKey !== $key && !is_int($jKey)) {\n                            break;\n                        }\n                        $args[] = $actionParams[$key][] = $params[$jKey];\n                        unset($params[$jKey]);\n                    }\n                } else {\n                    if (PHP_VERSION_ID >= 80000) {\n                        $isArray = ($type = $param->getType()) instanceof \\ReflectionNamedType && $type->getName() === 'array';\n                    } else {\n                        $isArray = $param->isArray();\n                    }\n                    if ($isArray) {\n                        $params[$key] = $params[$key] === '' ? [] : preg_split('/\\s*,\\s*/', $params[$key]);\n                    }\n                    $args[] = $actionParams[$key] = $params[$key];\n                    unset($params[$key]);\n                }\n            } elseif (\n                PHP_VERSION_ID >= 70100\n                && ($type = $param->getType()) !== null\n                && $type instanceof \\ReflectionNamedType\n                && !$type->isBuiltin()\n            ) {\n                try {\n                    $this->bindInjectedParams($type, $name, $args, $requestedParams);\n                } catch (\\yii\\base\\Exception $e) {\n                    throw new Exception($e->getMessage());\n                }\n            } elseif ($param->isDefaultValueAvailable()) {\n                $args[] = $actionParams[$i] = $param->getDefaultValue();\n            } else {\n                $missing[] = $name;\n            }\n        }\n\n        if (!empty($missing)) {\n            throw new Exception(Yii::t('yii', 'Missing required arguments: {params}', ['params' => implode(', ', $missing)]));\n        }\n\n        // We use a different array here, specifically one that doesn't contain service instances but descriptions instead.\n        if (\\Yii::$app->requestedParams === null) {\n            \\Yii::$app->requestedParams = array_merge($actionParams, $requestedParams);\n        }\n\n        return array_merge($args, $params);\n    }\n\n    /**\n     * Formats a string with ANSI codes.\n     *\n     * You may pass additional parameters using the constants defined in [[\\yii\\helpers\\Console]].\n     *\n     * Example:\n     *\n     * ```\n     * echo $this->ansiFormat('This will be red and underlined.', Console::FG_RED, Console::UNDERLINE);\n     * ```\n     *\n     * @param string $string the string to be formatted\n     * @return string\n     */\n    public function ansiFormat($string)\n    {\n        if ($this->isColorEnabled()) {\n            $args = func_get_args();\n            array_shift($args);\n            $string = Console::ansiFormat($string, $args);\n        }\n\n        return $string;\n    }\n\n    /**\n     * Prints a string to STDOUT.\n     *\n     * You may optionally format the string with ANSI codes by\n     * passing additional parameters using the constants defined in [[\\yii\\helpers\\Console]].\n     *\n     * Example:\n     *\n     * ```\n     * $this->stdout('This will be red and underlined.', Console::FG_RED, Console::UNDERLINE);\n     * ```\n     *\n     * @param string $string the string to print\n     * @param int ...$args additional parameters to decorate the output\n     * @return int|bool Number of bytes printed or false on error\n     */\n    public function stdout($string)\n    {\n        if ($this->isColorEnabled()) {\n            $args = func_get_args();\n            array_shift($args);\n            $string = Console::ansiFormat($string, $args);\n        }\n\n        return Console::stdout($string);\n    }\n\n    /**\n     * Prints a string to STDERR.\n     *\n     * You may optionally format the string with ANSI codes by\n     * passing additional parameters using the constants defined in [[\\yii\\helpers\\Console]].\n     *\n     * Example:\n     *\n     * ```\n     * $this->stderr('This will be red and underlined.', Console::FG_RED, Console::UNDERLINE);\n     * ```\n     *\n     * @param string $string the string to print\n     * @param int ...$args additional parameters to decorate the output\n     * @return int|bool Number of bytes printed or false on error\n     */\n    public function stderr($string)\n    {\n        if ($this->isColorEnabled(\\STDERR)) {\n            $args = func_get_args();\n            array_shift($args);\n            $string = Console::ansiFormat($string, $args);\n        }\n\n        return fwrite(\\STDERR, $string);\n    }\n\n    /**\n     * Prompts the user for input and validates it.\n     *\n     * @param string $text prompt string\n     * @param array $options the options to validate the input:\n     *\n     *  - required: whether it is required or not\n     *  - default: default value if no input is inserted by the user\n     *  - pattern: regular expression pattern to validate user input\n     *  - validator: a callable function to validate input. The function must accept two parameters:\n     *      - $input: the user input to validate\n     *      - $error: the error value passed by reference if validation failed.\n     *\n     * An example of how to use the prompt method with a validator function.\n     *\n     * ```\n     * $code = $this->prompt('Enter 4-Chars-Pin', ['required' => true, 'validator' => function($input, &$error) {\n     *     if (strlen($input) !== 4) {\n     *         $error = 'The Pin must be exactly 4 chars!';\n     *         return false;\n     *     }\n     *     return true;\n     * }]);\n     * ```\n     *\n     * @return string the user input\n     */\n    public function prompt($text, $options = [])\n    {\n        if ($this->interactive) {\n            return Console::prompt($text, $options);\n        }\n\n        return isset($options['default']) ? $options['default'] : '';\n    }\n\n    /**\n     * Asks user to confirm by typing y or n.\n     *\n     * A typical usage looks like the following:\n     *\n     * ```\n     * if ($this->confirm(\"Are you sure?\")) {\n     *     echo \"user typed yes\\n\";\n     * } else {\n     *     echo \"user typed no\\n\";\n     * }\n     * ```\n     *\n     * @param string $message to echo out before waiting for user input\n     * @param bool $default this value is returned if no selection is made.\n     * @return bool whether user confirmed.\n     * Will return true if [[interactive]] is false.\n     */\n    public function confirm($message, $default = false)\n    {\n        if ($this->interactive) {\n            return Console::confirm($message, $default);\n        }\n\n        return true;\n    }\n\n    /**\n     * Gives the user an option to choose from. Giving '?' as an input will show\n     * a list of options to choose from and their explanations.\n     *\n     * @param string $prompt the prompt message\n     * @param array $options Key-value array of options to choose from\n     * @param string|null $default value to use when the user doesn't provide an option.\n     * If the default is `null`, the user is required to select an option.\n     *\n     * @return string An option character the user chose\n     * @since 2.0.49 Added the $default argument\n     */\n    public function select($prompt, $options = [], $default = null)\n    {\n        if ($this->interactive) {\n            return Console::select($prompt, $options, $default);\n        }\n\n        return $default;\n    }\n\n    /**\n     * Returns the names of valid options for the action (id)\n     * An option requires the existence of a public member variable whose\n     * name is the option name.\n     * Child classes may override this method to specify possible options.\n     *\n     * Note that the values setting via options are not available\n     * until [[beforeAction()]] is being called.\n     *\n     * @param string $actionID the action id of the current request\n     * @return string[] the names of the options valid for the action\n     */\n    public function options($actionID)\n    {\n        // $actionId might be used in subclasses to provide options specific to action id\n        return ['color', 'interactive', 'help', 'silentExitOnException'];\n    }\n\n    /**\n     * Returns option alias names.\n     * Child classes may override this method to specify alias options.\n     *\n     * @return array the options alias names valid for the action\n     * where the keys is alias name for option and value is option name.\n     *\n     * @since 2.0.8\n     * @see options()\n     */\n    public function optionAliases()\n    {\n        return [\n            'h' => 'help',\n        ];\n    }\n\n    /**\n     * Returns properties corresponding to the options for the action id\n     * Child classes may override this method to specify possible properties.\n     *\n     * @param string $actionID the action id of the current request\n     * @return array properties corresponding to the options for the action\n     */\n    public function getOptionValues($actionID)\n    {\n        // $actionId might be used in subclasses to provide properties specific to action id\n        $properties = [];\n        foreach ($this->options($this->action->id) as $property) {\n            $properties[$property] = $this->$property;\n        }\n\n        return $properties;\n    }\n\n    /**\n     * Returns the names of valid options passed during execution.\n     *\n     * @return array the names of the options passed during execution\n     */\n    public function getPassedOptions()\n    {\n        return $this->_passedOptions;\n    }\n\n    /**\n     * Returns the properties corresponding to the passed options.\n     *\n     * @return array the properties corresponding to the passed options\n     */\n    public function getPassedOptionValues()\n    {\n        $properties = [];\n        foreach ($this->_passedOptions as $property) {\n            $properties[$property] = $this->$property;\n        }\n\n        return $properties;\n    }\n\n    /**\n     * Returns one-line short summary describing this controller.\n     *\n     * You may override this method to return customized summary.\n     * The default implementation returns first line from the PHPDoc comment.\n     *\n     * @return string the one-line short summary describing this controller.\n     */\n    public function getHelpSummary()\n    {\n        return $this->parseDocCommentSummary(new \\ReflectionClass($this));\n    }\n\n    /**\n     * Returns help information for this controller.\n     *\n     * You may override this method to return customized help.\n     * The default implementation returns help information retrieved from the PHPDoc comment.\n     * @return string the help information for this controller.\n     */\n    public function getHelp()\n    {\n        return $this->parseDocCommentDetail(new \\ReflectionClass($this));\n    }\n\n    /**\n     * Returns a one-line short summary describing the specified action.\n     * @param Action<static> $action action to get summary for\n     * @return string a one-line short summary describing the specified action.\n     *\n     * @phpstan-param Action<static> $action\n     * @psalm-param Action<self> $action\n     */\n    public function getActionHelpSummary($action)\n    {\n        if ($action === null) {\n            return $this->ansiFormat(Yii::t('yii', 'Action not found.'), Console::FG_RED);\n        }\n\n        return $this->parseDocCommentSummary($this->getActionMethodReflection($action));\n    }\n\n    /**\n     * Returns the detailed help information for the specified action.\n     * @param Action<static> $action action to get help for\n     * @return string the detailed help information for the specified action.\n     *\n     * @phpstan-param Action<static> $action\n     * @psalm-param Action<self> $action\n     */\n    public function getActionHelp($action)\n    {\n        return $this->parseDocCommentDetail($this->getActionMethodReflection($action));\n    }\n\n    /**\n     * Returns the help information for the anonymous arguments for the action.\n     *\n     * The returned value should be an array. The keys are the argument names, and the values are\n     * the corresponding help information. Each value must be an array of the following structure:\n     *\n     * - required: bool, whether this argument is required\n     * - type: string|null, the PHP type(s) of this argument\n     * - default: mixed, the default value of this argument\n     * - comment: string, the description of this argument\n     *\n     * The default implementation will return the help information extracted from the Reflection or\n     * DocBlock of the parameters corresponding to the action method.\n     *\n     * @param Action<static> $action the action instance\n     * @return array the help information of the action arguments\n     *\n     * @phpstan-param Action<static> $action\n     * @psalm-param Action<self> $action\n     */\n    public function getActionArgsHelp($action)\n    {\n        $method = $this->getActionMethodReflection($action);\n\n        $tags = $this->parseDocCommentTags($method);\n        $tags['param'] = isset($tags['param']) ? (array) $tags['param'] : [];\n        $phpDocParams = [];\n        foreach ($tags['param'] as $i => $tag) {\n            if (preg_match('/^(?<type>\\S+)(\\s+\\$(?<name>\\w+))?(?<comment>.*)/us', $tag, $matches) === 1) {\n                $key = empty($matches['name']) ? $i : $matches['name'];\n                $phpDocParams[$key] = ['type' => $matches['type'], 'comment' => $matches['comment']];\n            }\n        }\n        unset($tags);\n\n        $args = [];\n\n        /** @var \\ReflectionParameter $parameter */\n        foreach ($method->getParameters() as $i => $parameter) {\n            $type = null;\n            $comment = '';\n            if (PHP_MAJOR_VERSION > 5 && $parameter->hasType()) {\n                $reflectionType = $parameter->getType();\n                if (PHP_VERSION_ID >= 70100) {\n                    $types = method_exists($reflectionType, 'getTypes') ? $reflectionType->getTypes() : [$reflectionType];\n                    foreach ($types as $key => $reflectionType) {\n                        $types[$key] = $reflectionType->getName();\n                    }\n                    $type = implode('|', $types);\n                } else {\n                    $type = (string) $reflectionType;\n                }\n            }\n            // find PhpDoc tag by property name or position\n            $key = isset($phpDocParams[$parameter->name]) ? $parameter->name : (isset($phpDocParams[$i]) ? $i : null);\n            if ($key !== null) {\n                $comment = $phpDocParams[$key]['comment'];\n                if ($type === null && !empty($phpDocParams[$key]['type'])) {\n                    $type = $phpDocParams[$key]['type'];\n                }\n            }\n            // if type still not detected, then using type of default value\n            if ($type === null && $parameter->isDefaultValueAvailable() && $parameter->getDefaultValue() !== null) {\n                $type = gettype($parameter->getDefaultValue());\n            }\n\n            $args[$parameter->name] = [\n                'required' => !$parameter->isOptional(),\n                'type' => $type,\n                'default' => $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : null,\n                'comment' => $comment,\n            ];\n        }\n\n        return $args;\n    }\n\n    /**\n     * Returns the help information for the options for the action.\n     *\n     * The returned value should be an array. The keys are the option names, and the values are\n     * the corresponding help information. Each value must be an array of the following structure:\n     *\n     * - type: string, the PHP type of this argument.\n     * - default: string, the default value of this argument\n     * - comment: string, the comment of this argument\n     *\n     * The default implementation will return the help information extracted from the doc-comment of\n     * the properties corresponding to the action options.\n     *\n     * @param Action<static> $action\n     * @return array the help information of the action options\n     *\n     * @phpstan-param Action<static> $action\n     * @psalm-param Action<self> $action\n     */\n    public function getActionOptionsHelp($action)\n    {\n        $optionNames = $this->options($action->id);\n        if (empty($optionNames)) {\n            return [];\n        }\n\n        $class = new \\ReflectionClass($this);\n        $options = [];\n        foreach ($class->getProperties() as $property) {\n            $name = $property->getName();\n            if (!in_array($name, $optionNames, true)) {\n                continue;\n            }\n            $defaultValue = $property->getValue($this);\n            $tags = $this->parseDocCommentTags($property);\n\n            // Display camelCase options in kebab-case\n            $name = Inflector::camel2id($name, '-', true);\n\n            if (isset($tags['var']) || isset($tags['property'])) {\n                $doc = isset($tags['var']) ? $tags['var'] : $tags['property'];\n                if (is_array($doc)) {\n                    $doc = reset($doc);\n                }\n                if (preg_match('/^(\\S+)(.*)/s', $doc, $matches)) {\n                    $type = $matches[1];\n                    $comment = $matches[2];\n                } else {\n                    $type = null;\n                    $comment = $doc;\n                }\n                $options[$name] = [\n                    'type' => $type,\n                    'default' => $defaultValue,\n                    'comment' => $comment,\n                ];\n            } else {\n                $options[$name] = [\n                    'type' => null,\n                    'default' => $defaultValue,\n                    'comment' => '',\n                ];\n            }\n        }\n\n        return $options;\n    }\n\n    private $_reflections = [];\n\n    /**\n     * @param Action<static> $action\n     * @return \\ReflectionFunctionAbstract\n     *\n     * @phpstan-param Action<static> $action\n     * @psalm-param Action<self> $action\n     */\n    protected function getActionMethodReflection($action)\n    {\n        if (!isset($this->_reflections[$action->id])) {\n            if ($action instanceof InlineAction) {\n                $this->_reflections[$action->id] = new \\ReflectionMethod($this, $action->actionMethod);\n            } else {\n                $this->_reflections[$action->id] = new \\ReflectionMethod($action, 'run');\n            }\n        }\n\n        return $this->_reflections[$action->id];\n    }\n\n    /**\n     * Parses the comment block into tags.\n     * @param \\ReflectionClass<object>|\\ReflectionProperty|\\ReflectionFunctionAbstract $reflection the comment block\n     * @return array the parsed tags\n     */\n    protected function parseDocCommentTags($reflection)\n    {\n        $comment = $reflection->getDocComment();\n        $comment = \"@description \\n\" . strtr(trim(preg_replace('/^\\s*\\**([ \\t])?/m', '', trim($comment, '/'))), \"\\r\", '');\n        $parts = preg_split('/^\\s*@/m', $comment, -1, PREG_SPLIT_NO_EMPTY);\n        $tags = [];\n        foreach ($parts as $part) {\n            if (preg_match('/^(\\w+)(.*)/ms', trim($part), $matches)) {\n                $name = $matches[1];\n                if (!isset($tags[$name])) {\n                    $tags[$name] = trim($matches[2]);\n                } elseif (is_array($tags[$name])) {\n                    $tags[$name][] = trim($matches[2]);\n                } else {\n                    $tags[$name] = [$tags[$name], trim($matches[2])];\n                }\n            }\n        }\n\n        return $tags;\n    }\n\n    /**\n     * Returns the first line of docblock.\n     *\n     * @param \\ReflectionClass<static>|\\ReflectionProperty|\\ReflectionFunctionAbstract $reflection\n     * @return string\n     *\n     * @phpstan-param \\ReflectionClass<static>|\\ReflectionProperty|\\ReflectionFunctionAbstract $reflection\n     * @psalm-param \\ReflectionClass<self>|\\ReflectionProperty|\\ReflectionFunctionAbstract $reflection\n     */\n    protected function parseDocCommentSummary($reflection)\n    {\n        $docLines = preg_split('~\\R~u', $reflection->getDocComment());\n        if (isset($docLines[1])) {\n            return trim($docLines[1], \"\\t *\");\n        }\n\n        return '';\n    }\n\n    /**\n     * Returns full description from the docblock.\n     *\n     * @param \\ReflectionClass<static>|\\ReflectionProperty|\\ReflectionFunctionAbstract $reflection\n     * @return string\n     *\n     * @phpstan-param \\ReflectionClass<static>|\\ReflectionProperty|\\ReflectionFunctionAbstract $reflection\n     * @psalm-param \\ReflectionClass<self>|\\ReflectionProperty|\\ReflectionFunctionAbstract $reflection\n     */\n    protected function parseDocCommentDetail($reflection)\n    {\n        $comment = strtr(trim(preg_replace('/^\\s*\\**([ \\t])?/m', '', trim($reflection->getDocComment(), '/'))), \"\\r\", '');\n        if (preg_match('/^\\s*@\\w+/m', $comment, $matches, PREG_OFFSET_CAPTURE)) {\n            $comment = trim(substr($comment, 0, $matches[0][1]));\n        }\n        if ($comment !== '') {\n            return rtrim(Console::renderColoredString(Console::markdownToAnsi($comment)));\n        }\n\n        return '';\n    }\n}\n"
  },
  {
    "path": "framework/console/ErrorHandler.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\console;\n\nuse Yii;\nuse yii\\base\\ErrorException;\nuse yii\\base\\UserException;\nuse yii\\helpers\\Console;\n\n/**\n * ErrorHandler handles uncaught PHP errors and exceptions.\n *\n * ErrorHandler is configured as an application component in [[\\yii\\base\\Application]] by default.\n * You can access that instance via `Yii::$app->errorHandler`.\n *\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0\n */\nclass ErrorHandler extends \\yii\\base\\ErrorHandler\n{\n    /**\n     * Renders an exception using ansi format for console output.\n     * @param \\Throwable $exception the exception to be rendered.\n     */\n    protected function renderException($exception)\n    {\n        $previous = $exception->getPrevious();\n        if ($exception instanceof UnknownCommandException) {\n            // display message and suggest alternatives in case of unknown command\n            $message = $this->formatMessage($exception->getName() . ': ') . $exception->command;\n            $alternatives = $exception->getSuggestedAlternatives();\n            if (count($alternatives) === 1) {\n                $message .= \"\\n\\nDid you mean \\\"\" . reset($alternatives) . '\"?';\n            } elseif (count($alternatives) > 1) {\n                $message .= \"\\n\\nDid you mean one of these?\\n    - \" . implode(\"\\n    - \", $alternatives);\n            }\n        } elseif ($exception instanceof UserException && ($exception instanceof Exception || !YII_DEBUG)) {\n            $message = $this->formatMessage($exception->getName() . ': ') . $exception->getMessage();\n        } elseif (YII_DEBUG) {\n            if ($exception instanceof Exception) {\n                $message = $this->formatMessage(\"Exception ({$exception->getName()})\");\n            } elseif ($exception instanceof ErrorException) {\n                $message = $this->formatMessage($exception->getName());\n            } else {\n                $message = $this->formatMessage('Exception');\n            }\n            $message .= $this->formatMessage(\" '\" . get_class($exception) . \"'\", [Console::BOLD, Console::FG_BLUE])\n                . ' with message ' . $this->formatMessage(\"'{$exception->getMessage()}'\", [Console::BOLD]) //. \"\\n\"\n                . \"\\n\\nin \" . dirname($exception->getFile()) . DIRECTORY_SEPARATOR . $this->formatMessage(basename($exception->getFile()), [Console::BOLD])\n                . ':' . $this->formatMessage($exception->getLine(), [Console::BOLD, Console::FG_YELLOW]) . \"\\n\";\n            if ($exception instanceof \\yii\\db\\Exception && !empty($exception->errorInfo)) {\n                $message .= \"\\n\" . $this->formatMessage(\"Error Info:\\n\", [Console::BOLD]) . print_r($exception->errorInfo, true);\n            }\n            if ($previous === null) {\n                $message .= \"\\n\" . $this->formatMessage(\"Stack trace:\\n\", [Console::BOLD]) . $exception->getTraceAsString();\n            }\n        } else {\n            $message = $this->formatMessage('Error: ') . $exception->getMessage();\n        }\n\n        if (PHP_SAPI === 'cli') {\n            Console::stderr($message . \"\\n\");\n        } else {\n            echo $message . \"\\n\";\n        }\n        if (YII_DEBUG && $previous !== null) {\n            $causedBy = $this->formatMessage('Caused by: ', [Console::BOLD]);\n            if (PHP_SAPI === 'cli') {\n                Console::stderr($causedBy);\n            } else {\n                echo $causedBy;\n            }\n            $this->renderException($previous);\n        }\n    }\n\n    /**\n     * Colorizes a message for console output.\n     * @param string $message the message to colorize.\n     * @param array $format the message format.\n     * @return string the colorized message.\n     * @see Console::ansiFormat() for details on how to specify the message format.\n     */\n    protected function formatMessage($message, $format = [Console::FG_RED, Console::BOLD])\n    {\n        $stream = (PHP_SAPI === 'cli') ? \\STDERR : \\STDOUT;\n        // try controller first to allow check for --color switch\n        if (\n            Yii::$app->controller instanceof \\yii\\console\\Controller && Yii::$app->controller->isColorEnabled($stream)\n            || Yii::$app instanceof \\yii\\console\\Application && Console::streamSupportsAnsiColors($stream)\n        ) {\n            $message = Console::ansiFormat($message, $format);\n        }\n\n        return $message;\n    }\n}\n"
  },
  {
    "path": "framework/console/Exception.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\console;\n\nuse yii\\base\\UserException;\n\n/**\n * Exception represents an exception caused by incorrect usage of a console command.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Exception extends UserException\n{\n    /**\n     * @return string the user-friendly name of this exception\n     */\n    public function getName()\n    {\n        return 'Error';\n    }\n}\n"
  },
  {
    "path": "framework/console/ExitCode.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\console;\n\n/**\n * This class provides constants for defining console command exit codes.\n *\n * The exit codes follow the codes defined in the [FreeBSD sysexits(3)](https://man.openbsd.org/sysexits) manual page.\n *\n * These constants can be used in console controllers for example like this:\n *\n * ```\n * public function actionIndex()\n * {\n *     if (!$this->isAllowedToPerformAction()) {\n *          $this->stderr('Error: ' . ExitCode::getReason(ExitCode::NOPERM));\n *          return ExitCode::NOPERM;\n *     }\n *\n *     // do something\n *\n *     return ExitCode::OK;\n * }\n * ```\n *\n * @author Tom Worster <fsb@thefsb.org>\n * @author Alexander Makarov <sam@rmcreative.ru>\n * @see https://man.openbsd.org/sysexits\n * @since 2.0.13\n */\nclass ExitCode\n{\n    /**\n     * The command completed successfully.\n     */\n    public const OK = 0;\n    /**\n     * The command exited with an error code that says nothing about the error.\n     */\n    public const UNSPECIFIED_ERROR = 1;\n    /**\n     * The command was used incorrectly, e.g., with the wrong number of\n     * arguments, a bad flag, a bad syntax in a parameter, or whatever.\n     */\n    public const USAGE = 64;\n    /**\n     * The input data was incorrect in some way.  This should only be used for\n     * user's data and not system files.\n     */\n    public const DATAERR = 65;\n    /**\n     * An input file (not a system file) did not exist or was not readable.\n     * This could also include errors like ``No message'' to a mailer (if it\n     * cared to catch it).\n     */\n    public const NOINPUT = 66;\n    /**\n     * The user specified did not exist.  This might be used for mail addresses\n     * or remote logins.\n     */\n    public const NOUSER = 67;\n    /**\n     * The host specified did not exist.  This is used in mail addresses or\n     * network requests.\n     */\n    public const NOHOST = 68;\n    /**\n     * A service is unavailable.  This can occur if a support program or file\n     * does not exist.  This can also be used as a catchall message when\n     * something you wanted to do does not work, but you do not know why.\n     */\n    public const UNAVAILABLE = 69;\n    /**\n     * An internal software error has been detected.  This should be limited to\n     * non-operating system related errors as possible.\n     */\n    public const SOFTWARE = 70;\n    /**\n     * An operating system error has been detected.  This is intended to be\n     * used for such things as ``cannot fork'', ``cannot create pipe'', or the\n     * like.  It includes things like getuid returning a user that does not\n     * exist in the passwd file.\n     */\n    public const OSERR = 71;\n    /**\n     * Some system file (e.g., /etc/passwd, /var/run/utx.active, etc.) does not\n     * exist, cannot be opened, or has some sort of error (e.g., syntax error).\n     */\n    public const OSFILE = 72;\n    /**\n     * A (user specified) output file cannot be created.\n     */\n    public const CANTCREAT = 73;\n    /**\n     * An error occurred while doing I/O on some file.\n     */\n    public const IOERR = 74;\n    /**\n     * Temporary failure, indicating something that is not really an error. In\n     * sendmail, this means that a mailer (e.g.) could not create a connection,\n     * and the request should be reattempted later.\n     */\n    public const TEMPFAIL = 75;\n    /**\n     * The remote system returned something that was ``not possible'' during a\n     * protocol exchange.\n     */\n    public const PROTOCOL = 76;\n    /**\n     * You did not have sufficient permission to perform the operation.  This\n     * is not intended for file system problems, which should use NOINPUT or\n     * CANTCREAT, but rather for higher level permissions.\n     */\n    public const NOPERM = 77;\n    /**\n     * Something was found in an unconfigured or misconfigured state.\n     */\n    public const CONFIG = 78;\n    /**\n     * @var array a map of reason descriptions for exit codes.\n     */\n    public static $reasons = [\n        self::OK => 'Success',\n        self::UNSPECIFIED_ERROR => 'Unspecified error',\n        self::USAGE => 'Incorrect usage, argument or option error',\n        self::DATAERR => 'Error in input data',\n        self::NOINPUT => 'Input file not found or unreadable',\n        self::NOUSER => 'User not found',\n        self::NOHOST => 'Host not found',\n        self::UNAVAILABLE => 'A required service is unavailable',\n        self::SOFTWARE => 'Internal error',\n        self::OSERR => 'Error making system call or using OS service',\n        self::OSFILE => 'Error accessing system file',\n        self::CANTCREAT => 'Cannot create output file',\n        self::IOERR => 'I/O error',\n        self::TEMPFAIL => 'Temporary failure',\n        self::PROTOCOL => 'Unexpected remote service behavior',\n        self::NOPERM => 'Insufficient permissions',\n        self::CONFIG => 'Configuration error',\n    ];\n\n\n    /**\n     * Returns a short reason text for the given exit code.\n     *\n     * This method uses [[$reasons]] to determine the reason for an exit code.\n     * @param int $exitCode one of the constants defined in this class.\n     * @return string the reason text, or `\"Unknown exit code\"` if the code is not listed in [[$reasons]].\n     */\n    public static function getReason($exitCode)\n    {\n        return isset(static::$reasons[$exitCode]) ? static::$reasons[$exitCode] : 'Unknown exit code';\n    }\n}\n"
  },
  {
    "path": "framework/console/Markdown.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\console;\n\nuse cebe\\markdown\\block\\FencedCodeTrait;\nuse cebe\\markdown\\inline\\CodeTrait;\nuse cebe\\markdown\\inline\\EmphStrongTrait;\nuse cebe\\markdown\\inline\\StrikeoutTrait;\nuse yii\\helpers\\Console;\n\n/**\n * A Markdown parser that enhances markdown for reading in console environments.\n *\n * Based on [cebe/markdown](https://github.com/cebe/markdown).\n *\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0\n */\nclass Markdown extends \\cebe\\markdown\\Parser\n{\n    use FencedCodeTrait;\n    use CodeTrait;\n    use EmphStrongTrait;\n    use StrikeoutTrait;\n\n    /**\n     * @var array these are \"escapeable\" characters. When using one of these prefixed with a\n     * backslash, the character will be outputted without the backslash and is not interpreted\n     * as markdown.\n     */\n    protected $escapeCharacters = [\n        '\\\\', // backslash\n        '`', // backtick\n        '*', // asterisk\n        '_', // underscore\n        '~', // tilde\n    ];\n\n\n    /**\n     * Renders a code block.\n     *\n     * @param array $block\n     * @return string\n     */\n    protected function renderCode($block)\n    {\n        return Console::ansiFormat($block['content'], [Console::NEGATIVE]) . \"\\n\\n\";\n    }\n\n    /**\n     * Render a paragraph block.\n     *\n     * @param array $block\n     * @return string\n     */\n    protected function renderParagraph($block)\n    {\n        return rtrim($this->renderAbsy($block['content'])) . \"\\n\\n\";\n    }\n\n    /**\n     * Renders an inline code span `` ` ``.\n     * @param array $element\n     * @return string\n     */\n    protected function renderInlineCode($element)\n    {\n        return Console::ansiFormat($element[1], [Console::UNDERLINE]);\n    }\n\n    /**\n     * Renders empathized elements.\n     * @param array $element\n     * @return string\n     */\n    protected function renderEmph($element)\n    {\n        return Console::ansiFormat($this->renderAbsy($element[1]), [Console::ITALIC]);\n    }\n\n    /**\n     * Renders strong elements.\n     * @param array $element\n     * @return string\n     */\n    protected function renderStrong($element)\n    {\n        return Console::ansiFormat($this->renderAbsy($element[1]), [Console::BOLD]);\n    }\n\n    /**\n     * Renders the strike through feature.\n     * @param array $element\n     * @return string\n     */\n    protected function renderStrike($element)\n    {\n        return Console::ansiFormat($this->parseInline($this->renderAbsy($element[1])), [Console::CROSSED_OUT]);\n    }\n}\n"
  },
  {
    "path": "framework/console/Request.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\console;\n\n/**\n * The console Request represents the environment information for a console application.\n *\n * It is a wrapper for the PHP `$_SERVER` variable which holds information about the\n * currently running PHP script and the command line arguments given to it.\n *\n * @property array $params The command line arguments. It does not include the entry script name.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Request extends \\yii\\base\\Request\n{\n    private $_params;\n\n\n    /**\n     * Returns the command line arguments.\n     * @return array the command line arguments. It does not include the entry script name.\n     */\n    public function getParams()\n    {\n        if ($this->_params === null) {\n            if (isset($_SERVER['argv'])) {\n                $this->_params = $_SERVER['argv'];\n                array_shift($this->_params);\n            } else {\n                $this->_params = [];\n            }\n        }\n\n        return $this->_params;\n    }\n\n    /**\n     * Sets the command line arguments.\n     * @param array $params the command line arguments\n     */\n    public function setParams($params)\n    {\n        $this->_params = $params;\n    }\n\n    /**\n     * Resolves the current request into a route and the associated parameters.\n     * @return array the first element is the route, and the second is the associated parameters.\n     * @throws Exception when parameter is wrong and can not be resolved\n     */\n    public function resolve()\n    {\n        $rawParams = $this->getParams();\n        $endOfOptionsFound = false;\n        if (isset($rawParams[0])) {\n            $route = array_shift($rawParams);\n\n            if ($route === '--') {\n                $endOfOptionsFound = true;\n                $route = array_shift($rawParams);\n            }\n        } else {\n            $route = '';\n        }\n\n        $params = [];\n        $prevOption = null;\n        foreach ($rawParams as $param) {\n            if ($endOfOptionsFound) {\n                $params[] = $param;\n            } elseif ($param === '--') {\n                $endOfOptionsFound = true;\n            } elseif (preg_match('/^--([\\w-]+)(?:=(.*))?$/', $param, $matches)) {\n                $name = $matches[1];\n                if (is_numeric(substr($name, 0, 1))) {\n                    throw new Exception('Parameter \"' . $name . '\" is not valid');\n                }\n\n                if ($name !== Application::OPTION_APPCONFIG) {\n                    $params[$name] = isset($matches[2]) ? $matches[2] : true;\n                    $prevOption = &$params[$name];\n                }\n            } elseif (preg_match('/^-([\\w-]+)(?:=(.*))?$/', $param, $matches)) {\n                $name = $matches[1];\n                if (is_numeric($name)) {\n                    $params[] = $param;\n                } else {\n                    $params['_aliases'][$name] = isset($matches[2]) ? $matches[2] : true;\n                    $prevOption = &$params['_aliases'][$name];\n                }\n            } elseif ($prevOption === true) {\n                // `--option value` syntax\n                $prevOption = $param;\n            } else {\n                $params[] = $param;\n            }\n        }\n\n        return [$route, $params];\n    }\n}\n"
  },
  {
    "path": "framework/console/Response.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\console;\n\n/**\n * The console Response represents the result of a console application.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Response extends \\yii\\base\\Response\n{\n}\n"
  },
  {
    "path": "framework/console/UnknownCommandException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\console;\n\nuse yii\\console\\controllers\\HelpController;\n\n/**\n * UnknownCommandException represents an exception caused by incorrect usage of a console command.\n *\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0.11\n */\nclass UnknownCommandException extends Exception\n{\n    /**\n     * @var string the name of the command that could not be recognized.\n     */\n    public $command;\n\n    /**\n     * @var Application\n     */\n    protected $application;\n\n\n    /**\n     * Construct the exception.\n     *\n     * @param string $route the route of the command that could not be found.\n     * @param Application $application the console application instance involved.\n     * @param int $code the Exception code.\n     * @param \\Throwable|null $previous the previous exception used for the exception chaining.\n     */\n    public function __construct($route, $application, $code = 0, $previous = null)\n    {\n        $this->command = $route;\n        $this->application = $application;\n        parent::__construct(\"Unknown command \\\"$route\\\".\", $code, $previous);\n    }\n\n    /**\n     * @return string the user-friendly name of this exception\n     */\n    public function getName()\n    {\n        return 'Unknown command';\n    }\n\n    /**\n     * Suggest alternative commands for [[$command]] based on string similarity.\n     *\n     * Alternatives are searched using the following steps:\n     *\n     * - suggest alternatives that begin with `$command`\n     * - find typos by calculating the Levenshtein distance between the unknown command and all\n     *   available commands. The Levenshtein distance is defined as the minimal number of\n     *   characters you have to replace, insert or delete to transform str1 into str2.\n     *\n     * @see https://www.php.net/manual/en/function.levenshtein.php\n     * @return array a list of suggested alternatives sorted by similarity.\n     */\n    public function getSuggestedAlternatives()\n    {\n        $help = $this->application->createController('help');\n        if ($help === false || $this->command === '') {\n            return [];\n        }\n        /** @var HelpController<Application> $helpController */\n        list($helpController, $actionID) = $help;\n\n        $availableActions = [];\n        foreach ($helpController->getCommands() as $command) {\n            $result = $this->application->createController($command);\n            /** @var Controller<Application> $controller */\n            list($controller, $actionID) = $result;\n            if ($controller->createAction($controller->defaultAction) !== null) {\n                // add the command itself (default action)\n                $availableActions[] = $command;\n            }\n\n            // add all actions of this controller\n            $actions = $helpController->getActions($controller);\n            $prefix = $controller->getUniqueId();\n            foreach ($actions as $action) {\n                $availableActions[] = $prefix . '/' . $action;\n            }\n        }\n\n        return $this->filterBySimilarity($availableActions, $this->command);\n    }\n\n    /**\n     * Find suggest alternative commands based on string similarity.\n     *\n     * Alternatives are searched using the following steps:\n     *\n     * - suggest alternatives that begin with `$command`\n     * - find typos by calculating the Levenshtein distance between the unknown command and all\n     *   available commands. The Levenshtein distance is defined as the minimal number of\n     *   characters you have to replace, insert or delete to transform str1 into str2.\n     *\n     * @see https://www.php.net/manual/en/function.levenshtein.php\n     * @param array $actions available command names.\n     * @param string $command the command to compare to.\n     * @return array a list of suggested alternatives sorted by similarity.\n     */\n    private function filterBySimilarity($actions, $command)\n    {\n        $alternatives = [];\n\n        // suggest alternatives that begin with $command first\n        foreach ($actions as $action) {\n            if (strpos($action, $command) === 0) {\n                $alternatives[] = $action;\n            }\n        }\n\n        // calculate the Levenshtein distance between the unknown command and all available commands.\n        $distances = array_map(function ($action) use ($command) {\n            $action = strlen($action) > 255 ? substr($action, 0, 255) : $action;\n            $command = strlen($command) > 255 ? substr($command, 0, 255) : $command;\n            return levenshtein($action, $command);\n        }, array_combine($actions, $actions));\n\n        // we assume a typo if the levensthein distance is no more than 3, i.e. 3 replacements needed\n        $relevantTypos = array_filter($distances, function ($distance) {\n            return $distance <= 3;\n        });\n        asort($relevantTypos);\n        $alternatives = array_merge($alternatives, array_flip($relevantTypos));\n\n        return array_unique($alternatives);\n    }\n}\n"
  },
  {
    "path": "framework/console/controllers/AssetController.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\console\\controllers;\n\nuse Yii;\nuse yii\\console\\Application;\nuse yii\\console\\Controller;\nuse yii\\console\\Exception;\nuse yii\\console\\ExitCode;\nuse yii\\helpers\\Console;\nuse yii\\helpers\\FileHelper;\nuse yii\\helpers\\VarDumper;\nuse yii\\web\\AssetBundle;\n\n/**\n * Allows you to combine and compress your JavaScript and CSS files.\n *\n * Usage:\n *\n * 1. Create a configuration file using the `template` action:\n *\n *    yii asset/template /path/to/myapp/config.php\n *\n * 2. Edit the created config file, adjusting it for your web application needs.\n * 3. Run the 'compress' action, using created config:\n *\n *    yii asset /path/to/myapp/config.php /path/to/myapp/config/assets_compressed.php\n *\n * 4. Adjust your web application config to use compressed assets.\n *\n * Note: in the console environment some [path aliases](guide:concept-aliases) like `@webroot` and `@web` may not exist,\n * so corresponding paths inside the configuration should be specified directly.\n *\n * Note: by default this command relies on an external tools to perform actual files compression,\n * check [[jsCompressor]] and [[cssCompressor]] for more details.\n *\n * @property \\yii\\web\\AssetManager $assetManager Asset manager instance. Note that the type of this property\n * differs in getter and setter. See [[getAssetManager()]] and [[setAssetManager()]] for details.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Paul Klimov <klimov.paul@gmail.com>\n * @since 2.0\n *\n * @template T of Application = Application\n * @extends Controller<T>\n */\nclass AssetController extends Controller\n{\n    /**\n     * @var string controller default action ID.\n     */\n    public $defaultAction = 'compress';\n    /**\n     * @var array list of asset bundles to be compressed.\n     */\n    public $bundles = [];\n    /**\n     * @var array list of asset bundles, which represents output compressed files.\n     * You can specify the name of the output compressed file using 'css' and 'js' keys:\n     * For example:\n     *\n     * ```\n     * 'app\\config\\AllAsset' => [\n     *     'js' => 'js/all-{hash}.js',\n     *     'css' => 'css/all-{hash}.css',\n     *     'depends' => [ ... ],\n     * ]\n     * ```\n     *\n     * File names can contain placeholder \"{hash}\", which will be filled by the hash of the resulting file.\n     *\n     * You may specify several target bundles in order to compress different groups of assets.\n     * In this case you should use 'depends' key to specify, which bundles should be covered with particular\n     * target bundle. You may leave 'depends' to be empty for single bundle, which will compress all remaining\n     * bundles in this case.\n     * For example:\n     *\n     * ```\n     * 'allShared' => [\n     *     'js' => 'js/all-shared-{hash}.js',\n     *     'css' => 'css/all-shared-{hash}.css',\n     *     'depends' => [\n     *         // Include all assets shared between 'backend' and 'frontend'\n     *         'yii\\web\\YiiAsset',\n     *         'app\\assets\\SharedAsset',\n     *     ],\n     * ],\n     * 'allBackEnd' => [\n     *     'js' => 'js/all-{hash}.js',\n     *     'css' => 'css/all-{hash}.css',\n     *     'depends' => [\n     *         // Include only 'backend' assets:\n     *         'app\\assets\\AdminAsset'\n     *     ],\n     * ],\n     * 'allFrontEnd' => [\n     *     'js' => 'js/all-{hash}.js',\n     *     'css' => 'css/all-{hash}.css',\n     *     'depends' => [], // Include all remaining assets\n     * ],\n     * ```\n     */\n    public $targets = [];\n    /**\n     * @var string|callable JavaScript file compressor.\n     * If a string, it is treated as shell command template, which should contain\n     * placeholders {from} - source file name - and {to} - output file name.\n     * Otherwise, it is treated as PHP callback, which should perform the compression.\n     *\n     * Default value relies on usage of \"Closure Compiler\"\n     * @see https://developers.google.com/closure/compiler/\n     */\n    public $jsCompressor = 'java -jar compiler.jar --js {from} --js_output_file {to}';\n    /**\n     * @var string|callable CSS file compressor.\n     * If a string, it is treated as shell command template, which should contain\n     * placeholders {from} - source file name - and {to} - output file name.\n     * Otherwise, it is treated as PHP callback, which should perform the compression.\n     *\n     * Default value relies on usage of \"YUI Compressor\"\n     * @see https://github.com/yui/yuicompressor/\n     */\n    public $cssCompressor = 'java -jar yuicompressor.jar --type css {from} -o {to}';\n    /**\n     * @var bool whether to delete asset source files after compression.\n     * This option affects only those bundles, which have [[\\yii\\web\\AssetBundle::sourcePath]] is set.\n     * @since 2.0.10\n     */\n    public $deleteSource = false;\n\n    /**\n     * @var array|\\yii\\web\\AssetManager [[\\yii\\web\\AssetManager]] instance or its array configuration, which will be used\n     * for assets processing.\n     */\n    private $_assetManager = [];\n\n\n    /**\n     * Returns the asset manager instance.\n     * @throws \\yii\\console\\Exception on invalid configuration.\n     * @return \\yii\\web\\AssetManager asset manager instance.\n     */\n    public function getAssetManager()\n    {\n        if (!is_object($this->_assetManager)) {\n            $options = $this->_assetManager;\n            if (!isset($options['class'])) {\n                $options['class'] = 'yii\\\\web\\\\AssetManager';\n            }\n            if (!isset($options['basePath'])) {\n                throw new Exception(\"Please specify 'basePath' for the 'assetManager' option.\");\n            }\n            if (!isset($options['baseUrl'])) {\n                throw new Exception(\"Please specify 'baseUrl' for the 'assetManager' option.\");\n            }\n\n            if (!isset($options['forceCopy'])) {\n                $options['forceCopy'] = true;\n            }\n\n            $this->_assetManager = Yii::createObject($options);\n        }\n\n        return $this->_assetManager;\n    }\n\n    /**\n     * Sets asset manager instance or configuration.\n     * @param \\yii\\web\\AssetManager|array $assetManager asset manager instance or its array configuration.\n     * @throws \\yii\\console\\Exception on invalid argument type.\n     */\n    public function setAssetManager($assetManager)\n    {\n        if (is_scalar($assetManager)) {\n            throw new Exception('\"' . get_class($this) . '::assetManager\" should be either object or array - \"' . gettype($assetManager) . '\" given.');\n        }\n        $this->_assetManager = $assetManager;\n    }\n\n    /**\n     * Combines and compresses the asset files according to the given configuration.\n     * During the process new asset bundle configuration file will be created.\n     * You should replace your original asset bundle configuration with this file in order to use compressed files.\n     * @param string $configFile configuration file name.\n     * @param string $bundleFile output asset bundles configuration file name.\n     */\n    public function actionCompress($configFile, $bundleFile)\n    {\n        $this->loadConfiguration($configFile);\n        $bundles = $this->loadBundles($this->bundles);\n        $targets = $this->loadTargets($this->targets, $bundles);\n        foreach ($targets as $name => $target) {\n            $this->stdout(\"Creating output bundle '{$name}':\\n\");\n            if (!empty($target->js)) {\n                $this->buildTarget($target, 'js', $bundles);\n            }\n            if (!empty($target->css)) {\n                $this->buildTarget($target, 'css', $bundles);\n            }\n            $this->stdout(\"\\n\");\n        }\n\n        $targets = $this->adjustDependency($targets, $bundles);\n        $this->saveTargets($targets, $bundleFile);\n\n        if ($this->deleteSource) {\n            $this->deletePublishedAssets($bundles);\n        }\n    }\n\n    /**\n     * Applies configuration from the given file to self instance.\n     * @param string $configFile configuration file name.\n     * @throws \\yii\\console\\Exception on failure.\n     */\n    protected function loadConfiguration($configFile)\n    {\n        $this->stdout(\"Loading configuration from '{$configFile}'...\\n\");\n        $config = require $configFile;\n        foreach ($config as $name => $value) {\n            if (property_exists($this, $name) || $this->canSetProperty($name)) {\n                $this->$name = $value;\n            } else {\n                throw new Exception(\"Unknown configuration option: $name\");\n            }\n        }\n\n        $this->getAssetManager(); // check if asset manager configuration is correct\n    }\n\n    /**\n     * Creates full list of source asset bundles.\n     * @param string[] $bundles list of asset bundle names\n     * @return \\yii\\web\\AssetBundle[] list of source asset bundles.\n     */\n    protected function loadBundles($bundles)\n    {\n        $this->stdout(\"Collecting source bundles information...\\n\");\n\n        $am = $this->getAssetManager();\n        $result = [];\n        foreach ($bundles as $name) {\n            $result[$name] = $am->getBundle($name);\n        }\n        foreach ($result as $bundle) {\n            $this->loadDependency($bundle, $result);\n        }\n\n        return $result;\n    }\n\n    /**\n     * Loads asset bundle dependencies recursively.\n     * @param \\yii\\web\\AssetBundle $bundle bundle instance\n     * @param array $result already loaded bundles list.\n     * @throws Exception on failure.\n     */\n    protected function loadDependency($bundle, &$result)\n    {\n        $am = $this->getAssetManager();\n        foreach ($bundle->depends as $name) {\n            if (!isset($result[$name])) {\n                $dependencyBundle = $am->getBundle($name);\n                $result[$name] = false;\n                $this->loadDependency($dependencyBundle, $result);\n                $result[$name] = $dependencyBundle;\n            } elseif ($result[$name] === false) {\n                throw new Exception(\"A circular dependency is detected for bundle '{$name}': \" . $this->composeCircularDependencyTrace($name, $result) . '.');\n            }\n        }\n    }\n\n    /**\n     * Creates full list of output asset bundles.\n     * @param array $targets output asset bundles configuration.\n     * @param \\yii\\web\\AssetBundle[] $bundles list of source asset bundles.\n     * @return \\yii\\web\\AssetBundle[] list of output asset bundles.\n     * @throws Exception on failure.\n     */\n    protected function loadTargets($targets, $bundles)\n    {\n        // build the dependency order of bundles\n        $registered = [];\n        foreach ($bundles as $name => $bundle) {\n            $this->registerBundle($bundles, $name, $registered);\n        }\n        $bundleOrders = array_combine(array_keys($registered), range(0, count($bundles) - 1));\n\n        // fill up the target which has empty 'depends'.\n        $referenced = [];\n        foreach ($targets as $name => $target) {\n            if (empty($target['depends'])) {\n                if (!isset($all)) {\n                    $all = $name;\n                } else {\n                    throw new Exception(\"Only one target can have empty 'depends' option. Found two now: $all, $name\");\n                }\n            } else {\n                foreach ($target['depends'] as $bundle) {\n                    if (!isset($referenced[$bundle])) {\n                        $referenced[$bundle] = $name;\n                    } else {\n                        throw new Exception(\"Target '{$referenced[$bundle]}' and '$name' cannot contain the bundle '$bundle' at the same time.\");\n                    }\n                }\n            }\n        }\n        if (isset($all)) {\n            $targets[$all]['depends'] = array_diff(array_keys($registered), array_keys($referenced));\n        }\n\n        // adjust the 'depends' order for each target according to the dependency order of bundles\n        // create an AssetBundle object for each target\n        foreach ($targets as $name => $target) {\n            if (!isset($target['basePath'])) {\n                throw new Exception(\"Please specify 'basePath' for the '$name' target.\");\n            }\n            if (!isset($target['baseUrl'])) {\n                throw new Exception(\"Please specify 'baseUrl' for the '$name' target.\");\n            }\n            usort($target['depends'], function ($a, $b) use ($bundleOrders) {\n                if ($bundleOrders[$a] == $bundleOrders[$b]) {\n                    return 0;\n                }\n\n                return $bundleOrders[$a] > $bundleOrders[$b] ? 1 : -1;\n            });\n            if (!isset($target['class'])) {\n                $target['class'] = $name;\n            }\n            $targets[$name] = Yii::createObject($target);\n        }\n\n        return $targets;\n    }\n\n    /**\n     * Builds output asset bundle.\n     * @param \\yii\\web\\AssetBundle $target output asset bundle\n     * @param string $type either 'js' or 'css'.\n     * @param \\yii\\web\\AssetBundle[] $bundles source asset bundles.\n     * @throws Exception on failure.\n     */\n    protected function buildTarget($target, $type, $bundles)\n    {\n        $inputFiles = [];\n        foreach ($target->depends as $name) {\n            if (isset($bundles[$name])) {\n                if (!$this->isBundleExternal($bundles[$name])) {\n                    foreach ($bundles[$name]->$type as $file) {\n                        if (is_array($file)) {\n                            $inputFiles[] = $bundles[$name]->basePath . '/' . $file[0];\n                        } else {\n                            $inputFiles[] = $bundles[$name]->basePath . '/' . $file;\n                        }\n                    }\n                }\n            } else {\n                throw new Exception(\"Unknown bundle: '{$name}'\");\n            }\n        }\n\n        if (empty($inputFiles)) {\n            $target->$type = [];\n        } else {\n            FileHelper::createDirectory($target->basePath, $this->getAssetManager()->dirMode);\n            $tempFile = $target->basePath . '/' . strtr($target->$type, ['{hash}' => 'temp']);\n\n            if ($type === 'js') {\n                $this->compressJsFiles($inputFiles, $tempFile);\n            } else {\n                $this->compressCssFiles($inputFiles, $tempFile);\n            }\n\n            $targetFile = strtr($target->$type, ['{hash}' => md5_file($tempFile)]);\n            $outputFile = $target->basePath . '/' . $targetFile;\n            rename($tempFile, $outputFile);\n            $target->$type = [$targetFile];\n        }\n    }\n\n    /**\n     * Adjust dependencies between asset bundles in the way source bundles begin to depend on output ones.\n     * @param \\yii\\web\\AssetBundle[] $targets output asset bundles.\n     * @param \\yii\\web\\AssetBundle[] $bundles source asset bundles.\n     * @return \\yii\\web\\AssetBundle[] output asset bundles.\n     */\n    protected function adjustDependency($targets, $bundles)\n    {\n        $this->stdout(\"Creating new bundle configuration...\\n\");\n\n        $map = [];\n        foreach ($targets as $name => $target) {\n            foreach ($target->depends as $bundle) {\n                $map[$bundle] = $name;\n            }\n        }\n\n        foreach ($targets as $name => $target) {\n            $depends = [];\n            foreach ($target->depends as $bn) {\n                foreach ($bundles[$bn]->depends as $bundle) {\n                    $depends[$map[$bundle]] = true;\n                }\n            }\n            unset($depends[$name]);\n            $target->depends = array_keys($depends);\n        }\n\n        // detect possible circular dependencies\n        foreach ($targets as $name => $target) {\n            $registered = [];\n            $this->registerBundle($targets, $name, $registered);\n        }\n\n        foreach ($map as $bundle => $target) {\n            $sourceBundle = $bundles[$bundle];\n            $depends = $sourceBundle->depends;\n            if (!$this->isBundleExternal($sourceBundle)) {\n                $depends[] = $target;\n            }\n            $targetBundle = clone $sourceBundle;\n            $targetBundle->depends = $depends;\n            $targets[$bundle] = $targetBundle;\n        }\n\n        return $targets;\n    }\n\n    /**\n     * Registers asset bundles including their dependencies.\n     * @param \\yii\\web\\AssetBundle[] $bundles asset bundles list.\n     * @param string $name bundle name.\n     * @param array $registered stores already registered names.\n     * @throws Exception if circular dependency is detected.\n     */\n    protected function registerBundle($bundles, $name, &$registered)\n    {\n        if (!isset($registered[$name])) {\n            $registered[$name] = false;\n            $bundle = $bundles[$name];\n            foreach ($bundle->depends as $depend) {\n                $this->registerBundle($bundles, $depend, $registered);\n            }\n            unset($registered[$name]);\n            $registered[$name] = $bundle;\n        } elseif ($registered[$name] === false) {\n            throw new Exception(\"A circular dependency is detected for target '{$name}': \" . $this->composeCircularDependencyTrace($name, $registered) . '.');\n        }\n    }\n\n    /**\n     * Saves new asset bundles configuration.\n     * @param \\yii\\web\\AssetBundle[] $targets list of asset bundles to be saved.\n     * @param string $bundleFile output file name.\n     * @throws \\yii\\console\\Exception on failure.\n     */\n    protected function saveTargets($targets, $bundleFile)\n    {\n        $array = [];\n        foreach ($targets as $name => $target) {\n            if (isset($this->targets[$name])) {\n                $array[$name] = array_merge($this->targets[$name], [\n                    'class' => get_class($target),\n                    'sourcePath' => null,\n                    'basePath' => $this->targets[$name]['basePath'],\n                    'baseUrl' => $this->targets[$name]['baseUrl'],\n                    'js' => $target->js,\n                    'css' => $target->css,\n                    'depends' => [],\n                ]);\n            } else {\n                if ($this->isBundleExternal($target)) {\n                    $array[$name] = $this->composeBundleConfig($target);\n                } else {\n                    $array[$name] = [\n                        'sourcePath' => null,\n                        'js' => [],\n                        'css' => [],\n                        'depends' => $target->depends,\n                    ];\n                }\n            }\n        }\n        $array = VarDumper::export($array);\n        $version = date('Y-m-d H:i:s');\n        $bundleFileContent = <<<EOD\n<?php\n/**\n * This file is generated by the \"yii {$this->id}\" command.\n * DO NOT MODIFY THIS FILE DIRECTLY.\n * @version {$version}\n */\nreturn {$array};\nEOD;\n        if (!file_put_contents($bundleFile, $bundleFileContent, LOCK_EX)) {\n            throw new Exception(\"Unable to write output bundle configuration at '{$bundleFile}'.\");\n        }\n        $this->stdout(\"Output bundle configuration created at '{$bundleFile}'.\\n\", Console::FG_GREEN);\n    }\n\n    /**\n     * Compresses given JavaScript files and combines them into the single one.\n     * @param array $inputFiles list of source file names.\n     * @param string $outputFile output file name.\n     * @throws \\yii\\console\\Exception on failure\n     */\n    protected function compressJsFiles($inputFiles, $outputFile)\n    {\n        if (empty($inputFiles)) {\n            return;\n        }\n        $this->stdout(\"  Compressing JavaScript files...\\n\");\n        if (is_string($this->jsCompressor)) {\n            $tmpFile = $outputFile . '.tmp';\n            $this->combineJsFiles($inputFiles, $tmpFile);\n            $this->stdout((string)shell_exec(strtr($this->jsCompressor, [\n                '{from}' => escapeshellarg($tmpFile),\n                '{to}' => escapeshellarg($outputFile),\n            ])));\n            @unlink($tmpFile);\n        } else {\n            call_user_func($this->jsCompressor, $this, $inputFiles, $outputFile);\n        }\n        if (!file_exists($outputFile)) {\n            throw new Exception(\"Unable to compress JavaScript files into '{$outputFile}'.\");\n        }\n        $this->stdout(\"  JavaScript files compressed into '{$outputFile}'.\\n\");\n    }\n\n    /**\n     * Compresses given CSS files and combines them into the single one.\n     * @param array $inputFiles list of source file names.\n     * @param string $outputFile output file name.\n     * @throws \\yii\\console\\Exception on failure\n     */\n    protected function compressCssFiles($inputFiles, $outputFile)\n    {\n        if (empty($inputFiles)) {\n            return;\n        }\n        $this->stdout(\"  Compressing CSS files...\\n\");\n        if (is_string($this->cssCompressor)) {\n            $tmpFile = $outputFile . '.tmp';\n            $this->combineCssFiles($inputFiles, $tmpFile);\n            $this->stdout((string)shell_exec(strtr($this->cssCompressor, [\n                '{from}' => escapeshellarg($tmpFile),\n                '{to}' => escapeshellarg($outputFile),\n            ])));\n            @unlink($tmpFile);\n        } else {\n            call_user_func($this->cssCompressor, $this, $inputFiles, $outputFile);\n        }\n        if (!file_exists($outputFile)) {\n            throw new Exception(\"Unable to compress CSS files into '{$outputFile}'.\");\n        }\n        $this->stdout(\"  CSS files compressed into '{$outputFile}'.\\n\");\n    }\n\n    /**\n     * Combines JavaScript files into a single one.\n     * @param array $inputFiles source file names.\n     * @param string $outputFile output file name.\n     * @throws \\yii\\console\\Exception on failure.\n     */\n    public function combineJsFiles($inputFiles, $outputFile)\n    {\n        $content = '';\n        foreach ($inputFiles as $file) {\n            // Add a semicolon to source code if trailing semicolon missing.\n            // Notice: It needs a new line before `;` to avoid affection of line comment. (// ...;)\n            $fileContent = rtrim(file_get_contents($file));\n            if (substr($fileContent, -1) !== ';') {\n                $fileContent .= \"\\n;\";\n            }\n            $content .= \"/*** BEGIN FILE: $file ***/\\n\"\n                . $fileContent . \"\\n\"\n                . \"/*** END FILE: $file ***/\\n\";\n        }\n        if (!file_put_contents($outputFile, $content)) {\n            throw new Exception(\"Unable to write output JavaScript file '{$outputFile}'.\");\n        }\n    }\n\n    /**\n     * Combines CSS files into a single one.\n     * @param array $inputFiles source file names.\n     * @param string $outputFile output file name.\n     * @throws \\yii\\console\\Exception on failure.\n     */\n    public function combineCssFiles($inputFiles, $outputFile)\n    {\n        $content = '';\n        $outputFilePath = dirname($this->findRealPath($outputFile));\n        foreach ($inputFiles as $file) {\n            $content .= \"/*** BEGIN FILE: $file ***/\\n\"\n                . $this->adjustCssUrl(file_get_contents($file), dirname($this->findRealPath($file)), $outputFilePath)\n                . \"/*** END FILE: $file ***/\\n\";\n        }\n        if (!file_put_contents($outputFile, $content)) {\n            throw new Exception(\"Unable to write output CSS file '{$outputFile}'.\");\n        }\n    }\n\n    /**\n     * Adjusts CSS content allowing URL references pointing to the original resources.\n     * @param string $cssContent source CSS content.\n     * @param string $inputFilePath input CSS file name.\n     * @param string $outputFilePath output CSS file name.\n     * @return string adjusted CSS content.\n     */\n    protected function adjustCssUrl($cssContent, $inputFilePath, $outputFilePath)\n    {\n        $inputFilePath = str_replace('\\\\', '/', $inputFilePath);\n        $outputFilePath = str_replace('\\\\', '/', $outputFilePath);\n\n        $sharedPathParts = [];\n        $inputFilePathParts = explode('/', $inputFilePath);\n        $inputFilePathPartsCount = count($inputFilePathParts);\n        $outputFilePathParts = explode('/', $outputFilePath);\n        $outputFilePathPartsCount = count($outputFilePathParts);\n        for ($i = 0; $i < $inputFilePathPartsCount && $i < $outputFilePathPartsCount; $i++) {\n            if ($inputFilePathParts[$i] == $outputFilePathParts[$i]) {\n                $sharedPathParts[] = $inputFilePathParts[$i];\n            } else {\n                break;\n            }\n        }\n        $sharedPath = implode('/', $sharedPathParts);\n\n        $inputFileRelativePath = trim(str_replace($sharedPath, '', $inputFilePath), '/');\n        $outputFileRelativePath = trim(str_replace($sharedPath, '', $outputFilePath), '/');\n        if (empty($inputFileRelativePath)) {\n            $inputFileRelativePathParts = [];\n        } else {\n            $inputFileRelativePathParts = explode('/', $inputFileRelativePath);\n        }\n        if (empty($outputFileRelativePath)) {\n            $outputFileRelativePathParts = [];\n        } else {\n            $outputFileRelativePathParts = explode('/', $outputFileRelativePath);\n        }\n\n        $callback = function ($matches) use ($inputFileRelativePathParts, $outputFileRelativePathParts) {\n            $fullMatch = $matches[0];\n            $inputUrl = $matches[1];\n\n            if (strncmp($inputUrl, '/', 1) === 0 || strncmp($inputUrl, '#', 1) === 0 || preg_match('/^https?:\\/\\//i', $inputUrl) || preg_match('/^data:/i', $inputUrl)) {\n                return $fullMatch;\n            }\n            if ($inputFileRelativePathParts === $outputFileRelativePathParts) {\n                return $fullMatch;\n            }\n\n            if (empty($outputFileRelativePathParts)) {\n                $outputUrlParts = [];\n            } else {\n                $outputUrlParts = array_fill(0, count($outputFileRelativePathParts), '..');\n            }\n            $outputUrlParts = array_merge($outputUrlParts, $inputFileRelativePathParts);\n\n            if (strpos($inputUrl, '/') !== false) {\n                $inputUrlParts = explode('/', $inputUrl);\n                foreach ($inputUrlParts as $key => $inputUrlPart) {\n                    if ($inputUrlPart === '..') {\n                        array_pop($outputUrlParts);\n                        unset($inputUrlParts[$key]);\n                    }\n                }\n                $outputUrlParts[] = implode('/', $inputUrlParts);\n            } else {\n                $outputUrlParts[] = $inputUrl;\n            }\n            $outputUrl = implode('/', $outputUrlParts);\n\n            return str_replace($inputUrl, $outputUrl, $fullMatch);\n        };\n\n        $cssContent = preg_replace_callback('/url\\([\"\\']?([^)^\"\\']*)[\"\\']?\\)/i', $callback, $cssContent);\n\n        return $cssContent;\n    }\n\n    /**\n     * Creates template of configuration file for [[actionCompress]].\n     * @param string $configFile output file name.\n     * @return int CLI exit code\n     * @throws \\yii\\console\\Exception on failure.\n     */\n    public function actionTemplate($configFile)\n    {\n        $jsCompressor = VarDumper::export($this->jsCompressor);\n        $cssCompressor = VarDumper::export($this->cssCompressor);\n\n        $template = <<<EOD\n<?php\n/**\n * Configuration file for the \"yii asset\" console command.\n */\n\n// In the console environment, some path aliases may not exist. Please define these:\n// Yii::setAlias('@webroot', __DIR__ . '/../web');\n// Yii::setAlias('@web', '/');\n\nreturn [\n    // Adjust command/callback for JavaScript files compressing:\n    'jsCompressor' => {$jsCompressor},\n    // Adjust command/callback for CSS files compressing:\n    'cssCompressor' => {$cssCompressor},\n    // Whether to delete asset source after compression:\n    'deleteSource' => false,\n    // The list of asset bundles to compress:\n    'bundles' => [\n        // 'app\\assets\\AppAsset',\n        // 'yii\\web\\YiiAsset',\n        // 'yii\\web\\JqueryAsset',\n    ],\n    // Asset bundle for compression output:\n    'targets' => [\n        'all' => [\n            'class' => 'yii\\web\\AssetBundle',\n            'basePath' => '@webroot/assets',\n            'baseUrl' => '@web/assets',\n            'js' => 'js/all-{hash}.js',\n            'css' => 'css/all-{hash}.css',\n        ],\n    ],\n    // Asset manager configuration:\n    'assetManager' => [\n        //'basePath' => '@webroot/assets',\n        //'baseUrl' => '@web/assets',\n    ],\n];\nEOD;\n        if (file_exists($configFile)) {\n            if (!$this->confirm(\"File '{$configFile}' already exists. Do you wish to overwrite it?\")) {\n                return ExitCode::OK;\n            }\n        }\n        if (!file_put_contents($configFile, $template, LOCK_EX)) {\n            throw new Exception(\"Unable to write template file '{$configFile}'.\");\n        }\n\n        $this->stdout(\"Configuration file template created at '{$configFile}'.\\n\\n\", Console::FG_GREEN);\n        return ExitCode::OK;\n    }\n\n    /**\n     * Returns canonicalized absolute pathname.\n     * Unlike regular `realpath()` this method does not expand symlinks and does not check path existence.\n     * @param string $path raw path\n     * @return string canonicalized absolute pathname\n     */\n    private function findRealPath($path)\n    {\n        $path = str_replace(['/', '\\\\'], DIRECTORY_SEPARATOR, $path);\n        $pathParts = explode(DIRECTORY_SEPARATOR, $path);\n\n        $realPathParts = [];\n        foreach ($pathParts as $pathPart) {\n            if ($pathPart === '..') {\n                array_pop($realPathParts);\n            } else {\n                $realPathParts[] = $pathPart;\n            }\n        }\n\n        return implode(DIRECTORY_SEPARATOR, $realPathParts);\n    }\n\n    /**\n     * @param AssetBundle $bundle\n     * @return bool whether asset bundle external or not.\n     */\n    private function isBundleExternal($bundle)\n    {\n        return empty($bundle->sourcePath) && empty($bundle->basePath);\n    }\n\n    /**\n     * @param AssetBundle $bundle asset bundle instance.\n     * @return array bundle configuration.\n     */\n    private function composeBundleConfig($bundle)\n    {\n        $config = Yii::getObjectVars($bundle);\n        $config['class'] = get_class($bundle);\n        return $config;\n    }\n\n    /**\n     * Composes trace info for bundle circular dependency.\n     * @param string $circularDependencyName name of the bundle, which have circular dependency\n     * @param array $registered list of bundles registered while detecting circular dependency.\n     * @return string bundle circular dependency trace string.\n     */\n    private function composeCircularDependencyTrace($circularDependencyName, array $registered)\n    {\n        $dependencyTrace = [];\n        $startFound = false;\n        foreach ($registered as $name => $value) {\n            if ($name === $circularDependencyName) {\n                $startFound = true;\n            }\n            if ($startFound && $value === false) {\n                $dependencyTrace[] = $name;\n            }\n        }\n        $dependencyTrace[] = $circularDependencyName;\n        return implode(' -> ', $dependencyTrace);\n    }\n\n    /**\n     * Deletes bundle asset files, which have been published from `sourcePath`.\n     * @param \\yii\\web\\AssetBundle[] $bundles asset bundles to be processed.\n     * @since 2.0.10\n     */\n    private function deletePublishedAssets($bundles)\n    {\n        $this->stdout(\"Deleting source files...\\n\");\n\n        if ($this->getAssetManager()->linkAssets) {\n            $this->stdout(\"`AssetManager::linkAssets` option is enabled. Deleting of source files canceled.\\n\", Console::FG_YELLOW);\n            return;\n        }\n\n        foreach ($bundles as $bundle) {\n            if ($bundle->sourcePath !== null) {\n                foreach ($bundle->js as $jsFile) {\n                    @unlink($bundle->basePath . DIRECTORY_SEPARATOR . $jsFile);\n                }\n                foreach ($bundle->css as $cssFile) {\n                    @unlink($bundle->basePath . DIRECTORY_SEPARATOR . $cssFile);\n                }\n            }\n        }\n\n        $this->stdout(\"Source files deleted.\\n\", Console::FG_GREEN);\n    }\n}\n"
  },
  {
    "path": "framework/console/controllers/BaseMigrateController.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\console\\controllers;\n\nuse Yii;\nuse yii\\base\\Action;\nuse yii\\base\\BaseObject;\nuse yii\\base\\InvalidConfigException;\nuse yii\\base\\NotSupportedException;\nuse yii\\console\\Application;\nuse yii\\console\\Controller;\nuse yii\\console\\Exception;\nuse yii\\console\\ExitCode;\nuse yii\\db\\MigrationInterface;\nuse yii\\helpers\\Console;\nuse yii\\helpers\\FileHelper;\nuse yii\\helpers\\Inflector;\n\n/**\n * BaseMigrateController is the base class for migrate controllers.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Application = Application\n * @extends Controller<T>\n */\nabstract class BaseMigrateController extends Controller\n{\n    /**\n     * The name of the dummy migration that marks the beginning of the whole migration history.\n     */\n    public const BASE_MIGRATION = 'm000000_000000_base';\n    /**\n     * @var string the default command action.\n     */\n    public $defaultAction = 'up';\n    /**\n     * @var string|array|null the directory containing the migration classes. This can be either\n     * a [path alias](guide:concept-aliases) or a directory path.\n     *\n     * Migration classes located at this path should be declared without a namespace.\n     * Use [[migrationNamespaces]] property in case you are using namespaced migrations.\n     *\n     * If you have set up [[migrationNamespaces]], you may set this field to `null` in order\n     * to disable usage of migrations that are not namespaced.\n     *\n     * Since version 2.0.12 you may also specify an array of migration paths that should be searched for\n     * migrations to load. This is mainly useful to support old extensions that provide migrations\n     * without namespace and to adopt the new feature of namespaced migrations while keeping existing migrations.\n     *\n     * In general, to load migrations from different locations, [[migrationNamespaces]] is the preferable solution\n     * as the migration name contains the origin of the migration in the history, which is not the case when\n     * using multiple migration paths.\n     *\n     * @see migrationNamespaces\n     */\n    public $migrationPath = ['@app/migrations'];\n    /**\n     * @var array list of namespaces containing the migration classes.\n     *\n     * Migration namespaces should be resolvable as a [path alias](guide:concept-aliases) if prefixed with `@`, e.g. if you specify\n     * the namespace `app\\migrations`, the code `Yii::getAlias('@app/migrations')` should be able to return\n     * the file path to the directory this namespace refers to.\n     * This corresponds with the [autoloading conventions](guide:concept-autoloading) of Yii.\n     *\n     * For example:\n     *\n     * ```\n     * [\n     *     'app\\migrations',\n     *     'some\\extension\\migrations',\n     * ]\n     * ```\n     *\n     * @since 2.0.10\n     * @see migrationPath\n     */\n    public $migrationNamespaces = [];\n    /**\n     * @var string the template file for generating new migrations.\n     * This can be either a [path alias](guide:concept-aliases) (e.g. \"@app/migrations/template.php\")\n     * or a file path.\n     */\n    public $templateFile;\n    /**\n     * @var int|null the permission to be set for newly generated migration files.\n     * This value will be used by PHP chmod() function. No umask will be applied.\n     * If not set, the permission will be determined by the current environment.\n     * @since 2.0.43\n     */\n    public $newFileMode;\n    /**\n     * @var string|int|null the user and/or group ownership to be set for newly generated migration files.\n     * If not set, the ownership will be determined by the current environment.\n     * @since 2.0.43\n     * @see FileHelper::changeOwnership()\n     */\n    public $newFileOwnership;\n    /**\n     * @var bool indicates whether the console output should be compacted.\n     * If this is set to true, the individual commands ran within the migration will not be output to the console.\n     * Default is false, in other words the output is fully verbose by default.\n     * @since 2.0.13\n     */\n    public $compact = false;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function options($actionID)\n    {\n        return array_merge(\n            parent::options($actionID),\n            ['migrationPath', 'migrationNamespaces', 'compact'], // global for all actions\n            $actionID === 'create' ? ['templateFile'] : [] // action create\n        );\n    }\n\n    /**\n     * This method is invoked right before an action is to be executed (after all possible filters.)\n     * It checks the existence of the [[migrationPath]].\n     * @param Action<static> $action the action to be executed.\n     * @throws InvalidConfigException if directory specified in migrationPath doesn't exist and action isn't \"create\".\n     * @return bool whether the action should continue to be executed.\n     *\n     * @phpstan-param Action<static> $action\n     * @psalm-param Action<self> $action\n     */\n    public function beforeAction($action)\n    {\n        if (parent::beforeAction($action)) {\n            if (empty($this->migrationNamespaces) && empty($this->migrationPath)) {\n                throw new InvalidConfigException('At least one of `migrationPath` or `migrationNamespaces` should be specified.');\n            }\n\n            $this->migrationNamespaces = (array) $this->migrationNamespaces;\n\n            foreach ($this->migrationNamespaces as $key => $value) {\n                $this->migrationNamespaces[$key] = trim($value, '\\\\');\n            }\n\n            if (is_array($this->migrationPath)) {\n                foreach ($this->migrationPath as $i => $path) {\n                    $this->migrationPath[$i] = Yii::getAlias($path);\n                }\n            } elseif ($this->migrationPath !== null) {\n                $path = Yii::getAlias($this->migrationPath);\n                if (!is_dir($path)) {\n                    if ($action->id !== 'create') {\n                        throw new InvalidConfigException(\"Migration failed. Directory specified in migrationPath doesn't exist: {$this->migrationPath}\");\n                    }\n                    FileHelper::createDirectory($path);\n                }\n                $this->migrationPath = $path;\n            }\n\n            $version = Yii::getVersion();\n            $this->stdout(\"Yii Migration Tool (based on Yii v{$version})\\n\\n\");\n\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * Upgrades the application by applying new migrations.\n     *\n     * For example,\n     *\n     * ```\n     * yii migrate     # apply all new migrations\n     * yii migrate 3   # apply the first 3 new migrations\n     * ```\n     *\n     * @param int $limit the number of new migrations to be applied. If 0, it means\n     * applying all available new migrations.\n     *\n     * @return int the status of the action execution. 0 means normal, other values mean abnormal.\n     */\n    public function actionUp($limit = 0)\n    {\n        $migrations = $this->getNewMigrations();\n        if (empty($migrations)) {\n            $this->stdout(\"No new migrations found. Your system is up-to-date.\\n\", Console::FG_GREEN);\n\n            return ExitCode::OK;\n        }\n\n        $total = count($migrations);\n        $limit = (int) $limit;\n        if ($limit > 0) {\n            $migrations = array_slice($migrations, 0, $limit);\n        }\n\n        $n = count($migrations);\n        if ($n === $total) {\n            $this->stdout(\"Total $n new \" . ($n === 1 ? 'migration' : 'migrations') . \" to be applied:\\n\", Console::FG_YELLOW);\n        } else {\n            $this->stdout(\"Total $n out of $total new \" . ($total === 1 ? 'migration' : 'migrations') . \" to be applied:\\n\", Console::FG_YELLOW);\n        }\n\n        foreach ($migrations as $migration) {\n            $nameLimit = $this->getMigrationNameLimit();\n            if ($nameLimit !== null && strlen($migration) > $nameLimit) {\n                $this->stdout(\"\\nThe migration name '$migration' is too long. Its not possible to apply this migration.\\n\", Console::FG_RED);\n                return ExitCode::UNSPECIFIED_ERROR;\n            }\n            $this->stdout(\"\\t$migration\\n\");\n        }\n        $this->stdout(\"\\n\");\n\n        $applied = 0;\n        if ($this->confirm('Apply the above ' . ($n === 1 ? 'migration' : 'migrations') . '?')) {\n            foreach ($migrations as $migration) {\n                if (!$this->migrateUp($migration)) {\n                    $this->stdout(\"\\n$applied from $n \" . ($applied === 1 ? 'migration was' : 'migrations were') . \" applied.\\n\", Console::FG_RED);\n                    $this->stdout(\"\\nMigration failed. The rest of the migrations are canceled.\\n\", Console::FG_RED);\n\n                    return ExitCode::UNSPECIFIED_ERROR;\n                }\n                $applied++;\n            }\n\n            $this->stdout(\"\\n$n \" . ($n === 1 ? 'migration was' : 'migrations were') . \" applied.\\n\", Console::FG_GREEN);\n            $this->stdout(\"\\nMigrated up successfully.\\n\", Console::FG_GREEN);\n        }\n\n        return ExitCode::OK;\n    }\n\n    /**\n     * Downgrades the application by reverting old migrations.\n     *\n     * For example,\n     *\n     * ```\n     * yii migrate/down     # revert the last migration\n     * yii migrate/down 3   # revert the last 3 migrations\n     * yii migrate/down all # revert all migrations\n     * ```\n     *\n     * @param int|string $limit the number of migrations to be reverted. Defaults to 1,\n     * meaning the last applied migration will be reverted. When value is \"all\", all migrations will be reverted.\n     * @throws Exception if the number of the steps specified is less than 1.\n     *\n     * @return int the status of the action execution. 0 means normal, other values mean abnormal.\n     */\n    public function actionDown($limit = 1)\n    {\n        if ($limit === 'all') {\n            $limit = null;\n        } else {\n            $limit = (int) $limit;\n            if ($limit < 1) {\n                throw new Exception('The step argument must be greater than 0.');\n            }\n        }\n\n        $migrations = $this->getMigrationHistory($limit);\n\n        if (empty($migrations)) {\n            $this->stdout(\"No migration has been done before.\\n\", Console::FG_YELLOW);\n\n            return ExitCode::OK;\n        }\n\n        $migrations = array_keys($migrations);\n\n        $n = count($migrations);\n        $this->stdout(\"Total $n \" . ($n === 1 ? 'migration' : 'migrations') . \" to be reverted:\\n\", Console::FG_YELLOW);\n        foreach ($migrations as $migration) {\n            $this->stdout(\"\\t$migration\\n\");\n        }\n        $this->stdout(\"\\n\");\n\n        $reverted = 0;\n        if ($this->confirm('Revert the above ' . ($n === 1 ? 'migration' : 'migrations') . '?')) {\n            foreach ($migrations as $migration) {\n                if (!$this->migrateDown($migration)) {\n                    $this->stdout(\"\\n$reverted from $n \" . ($reverted === 1 ? 'migration was' : 'migrations were') . \" reverted.\\n\", Console::FG_RED);\n                    $this->stdout(\"\\nMigration failed. The rest of the migrations are canceled.\\n\", Console::FG_RED);\n\n                    return ExitCode::UNSPECIFIED_ERROR;\n                }\n                $reverted++;\n            }\n            $this->stdout(\"\\n$n \" . ($n === 1 ? 'migration was' : 'migrations were') . \" reverted.\\n\", Console::FG_GREEN);\n            $this->stdout(\"\\nMigrated down successfully.\\n\", Console::FG_GREEN);\n        }\n\n        return ExitCode::OK;\n    }\n\n    /**\n     * Redoes the last few migrations.\n     *\n     * This command will first revert the specified migrations, and then apply\n     * them again. For example,\n     *\n     * ```\n     * yii migrate/redo     # redo the last applied migration\n     * yii migrate/redo 3   # redo the last 3 applied migrations\n     * yii migrate/redo all # redo all migrations\n     * ```\n     *\n     * @param int|string $limit the number of migrations to be redone. Defaults to 1,\n     * meaning the last applied migration will be redone. When equals \"all\", all migrations will be redone.\n     * @throws Exception if the number of the steps specified is less than 1.\n     *\n     * @return int the status of the action execution. 0 means normal, other values mean abnormal.\n     */\n    public function actionRedo($limit = 1)\n    {\n        if ($limit === 'all') {\n            $limit = null;\n        } else {\n            $limit = (int) $limit;\n            if ($limit < 1) {\n                throw new Exception('The step argument must be greater than 0.');\n            }\n        }\n\n        $migrations = $this->getMigrationHistory($limit);\n\n        if (empty($migrations)) {\n            $this->stdout(\"No migration has been done before.\\n\", Console::FG_YELLOW);\n\n            return ExitCode::OK;\n        }\n\n        $migrations = array_keys($migrations);\n\n        $n = count($migrations);\n        $this->stdout(\"Total $n \" . ($n === 1 ? 'migration' : 'migrations') . \" to be redone:\\n\", Console::FG_YELLOW);\n        foreach ($migrations as $migration) {\n            $this->stdout(\"\\t$migration\\n\");\n        }\n        $this->stdout(\"\\n\");\n\n        if ($this->confirm('Redo the above ' . ($n === 1 ? 'migration' : 'migrations') . '?')) {\n            foreach ($migrations as $migration) {\n                if (!$this->migrateDown($migration)) {\n                    $this->stdout(\"\\nMigration failed. The rest of the migrations are canceled.\\n\", Console::FG_RED);\n\n                    return ExitCode::UNSPECIFIED_ERROR;\n                }\n            }\n            foreach (array_reverse($migrations) as $migration) {\n                if (!$this->migrateUp($migration)) {\n                    $this->stdout(\"\\nMigration failed. The rest of the migrations are canceled.\\n\", Console::FG_RED);\n\n                    return ExitCode::UNSPECIFIED_ERROR;\n                }\n            }\n            $this->stdout(\"\\n$n \" . ($n === 1 ? 'migration was' : 'migrations were') . \" redone.\\n\", Console::FG_GREEN);\n            $this->stdout(\"\\nMigration redone successfully.\\n\", Console::FG_GREEN);\n        }\n\n        return ExitCode::OK;\n    }\n\n    /**\n     * Upgrades or downgrades till the specified version.\n     *\n     * Can also downgrade versions to the certain apply time in the past by providing\n     * a UNIX timestamp or a string parseable by the strtotime() function. This means\n     * that all the versions applied after the specified certain time would be reverted.\n     *\n     * This command will first revert the specified migrations, and then apply\n     * them again. For example,\n     *\n     * ```\n     * yii migrate/to 101129_185401                          # using timestamp\n     * yii migrate/to m101129_185401_create_user_table       # using full name\n     * yii migrate/to 1392853618                             # using UNIX timestamp\n     * yii migrate/to \"2014-02-15 13:00:50\"                  # using strtotime() parseable string\n     * yii migrate/to app\\migrations\\M101129185401CreateUser # using full namespace name\n     * ```\n     *\n     * @param string $version either the version name or the certain time value in the past\n     * that the application should be migrated to. This can be either the timestamp,\n     * the full name of the migration, the UNIX timestamp, or the parseable datetime\n     * string.\n     * @throws Exception if the version argument is invalid.\n     */\n    public function actionTo($version)\n    {\n        if (($namespaceVersion = $this->extractNamespaceMigrationVersion($version)) !== false) {\n            return $this->migrateToVersion($namespaceVersion);\n        } elseif (($migrationName = $this->extractMigrationVersion($version)) !== false) {\n            return $this->migrateToVersion($migrationName);\n        } elseif ((string) (int) $version == $version) {\n            return $this->migrateToTime($version);\n        } elseif (($time = strtotime($version)) !== false) {\n            return $this->migrateToTime($time);\n        } else {\n            throw new Exception(\"The version argument must be either a timestamp (e.g. 101129_185401),\\n the full name of a migration (e.g. m101129_185401_create_user_table),\\n the full namespaced name of a migration (e.g. app\\\\migrations\\\\M101129185401CreateUserTable),\\n a UNIX timestamp (e.g. 1392853000), or a datetime string parseable\\nby the strtotime() function (e.g. 2014-02-15 13:00:50).\");\n        }\n    }\n\n    /**\n     * Modifies the migration history to the specified version.\n     *\n     * No actual migration will be performed.\n     *\n     * ```\n     * yii migrate/mark 101129_185401                        # using timestamp\n     * yii migrate/mark m101129_185401_create_user_table     # using full name\n     * yii migrate/mark app\\migrations\\M101129185401CreateUser # using full namespace name\n     * yii migrate/mark m000000_000000_base # reset the complete migration history\n     * ```\n     *\n     * @param string $version the version at which the migration history should be marked.\n     * This can be either the timestamp or the full name of the migration.\n     * You may specify the name `m000000_000000_base` to set the migration history to a\n     * state where no migration has been applied.\n     * @return int CLI exit code\n     * @throws Exception if the version argument is invalid or the version cannot be found.\n     */\n    public function actionMark($version)\n    {\n        $originalVersion = $version;\n        if (($namespaceVersion = $this->extractNamespaceMigrationVersion($version)) !== false) {\n            $version = $namespaceVersion;\n        } elseif (($migrationName = $this->extractMigrationVersion($version)) !== false) {\n            $version = $migrationName;\n        } elseif ($version !== static::BASE_MIGRATION) {\n            throw new Exception(\"The version argument must be either a timestamp (e.g. 101129_185401)\\nor the full name of a migration (e.g. m101129_185401_create_user_table)\\nor the full name of a namespaced migration (e.g. app\\\\migrations\\\\M101129185401CreateUserTable).\");\n        }\n\n        // try mark up\n        $migrations = $this->getNewMigrations();\n        foreach ($migrations as $i => $migration) {\n            if (strpos($migration, $version) === 0) {\n                if ($this->confirm(\"Set migration history at $originalVersion?\")) {\n                    for ($j = 0; $j <= $i; ++$j) {\n                        $this->addMigrationHistory($migrations[$j]);\n                    }\n                    $this->stdout(\"The migration history is set at $originalVersion.\\nNo actual migration was performed.\\n\", Console::FG_GREEN);\n                }\n\n                return ExitCode::OK;\n            }\n        }\n\n        // try mark down\n        $migrations = array_keys($this->getMigrationHistory(null));\n        $migrations[] = static::BASE_MIGRATION;\n        foreach ($migrations as $i => $migration) {\n            if (strpos($migration, $version) === 0) {\n                if ($i === 0) {\n                    $this->stdout(\"Already at '$originalVersion'. Nothing needs to be done.\\n\", Console::FG_YELLOW);\n                } elseif ($this->confirm(\"Set migration history at $originalVersion?\")) {\n                    for ($j = 0; $j < $i; ++$j) {\n                        $this->removeMigrationHistory($migrations[$j]);\n                    }\n                    $this->stdout(\"The migration history is set at $originalVersion.\\nNo actual migration was performed.\\n\", Console::FG_GREEN);\n                }\n\n                return ExitCode::OK;\n            }\n        }\n\n        throw new Exception(\"Unable to find the version '$originalVersion'.\");\n    }\n\n    /**\n     * Drops all tables and related constraints. Starts the migration from the beginning.\n     *\n     * ```\n     * yii migrate/fresh\n     * ```\n     *\n     * @since 2.0.13\n     */\n    public function actionFresh()\n    {\n        if (YII_ENV_PROD) {\n            $this->stdout(\"YII_ENV is set to 'prod'.\\nRefreshing migrations is not possible on production systems.\\n\");\n\n            return ExitCode::OK;\n        }\n\n        if ($this->confirm(\"Are you sure you want to drop all tables and related constraints and start the migration from the beginning?\\nAll data will be lost irreversibly!\")) {\n            $this->truncateDatabase();\n\n            return $this->actionUp();\n        }\n\n        $this->stdout('Action was cancelled by user. Nothing has been performed.');\n\n        return ExitCode::OK;\n    }\n\n    /**\n     * Checks if given migration version specification matches namespaced migration name.\n     * @param string $rawVersion raw version specification received from user input.\n     * @return string|false actual migration version, `false` - if not match.\n     * @since 2.0.10\n     */\n    private function extractNamespaceMigrationVersion($rawVersion)\n    {\n        if (preg_match('/^\\\\\\\\?([\\w_]+\\\\\\\\)+m(\\d{6}_?\\d{6})(\\D.*)?$/is', $rawVersion, $matches)) {\n            return trim($rawVersion, '\\\\');\n        }\n\n        return false;\n    }\n\n    /**\n     * Checks if given migration version specification matches migration base name.\n     * @param string $rawVersion raw version specification received from user input.\n     * @return string|false actual migration version, `false` - if not match.\n     * @since 2.0.10\n     */\n    private function extractMigrationVersion($rawVersion)\n    {\n        if (preg_match('/^m?(\\d{6}_?\\d{6})(\\D.*)?$/is', $rawVersion, $matches)) {\n            return 'm' . $matches[1];\n        }\n\n        return false;\n    }\n\n    /**\n     * Displays the migration history.\n     *\n     * This command will show the list of migrations that have been applied\n     * so far. For example,\n     *\n     * ```\n     * yii migrate/history     # showing the last 10 migrations\n     * yii migrate/history 5   # showing the last 5 migrations\n     * yii migrate/history all # showing the whole history\n     * ```\n     *\n     * @param int|string $limit the maximum number of migrations to be displayed.\n     * If it is \"all\", the whole migration history will be displayed.\n     * @throws \\yii\\console\\Exception if invalid limit value passed\n     */\n    public function actionHistory($limit = 10)\n    {\n        if ($limit === 'all') {\n            $limit = null;\n        } else {\n            $limit = (int) $limit;\n            if ($limit < 1) {\n                throw new Exception('The limit must be greater than 0.');\n            }\n        }\n\n        $migrations = $this->getMigrationHistory($limit);\n\n        if (empty($migrations)) {\n            $this->stdout(\"No migration has been done before.\\n\", Console::FG_YELLOW);\n        } else {\n            $n = count($migrations);\n            if ($limit > 0) {\n                $this->stdout(\"Showing the last $n applied \" . ($n === 1 ? 'migration' : 'migrations') . \":\\n\", Console::FG_YELLOW);\n            } else {\n                $this->stdout(\"Total $n \" . ($n === 1 ? 'migration has' : 'migrations have') . \" been applied before:\\n\", Console::FG_YELLOW);\n            }\n            foreach ($migrations as $version => $time) {\n                $this->stdout(\"\\t(\" . date('Y-m-d H:i:s', $time) . ') ' . $version . \"\\n\");\n            }\n        }\n\n        return ExitCode::OK;\n    }\n\n    /**\n     * Displays the un-applied new migrations.\n     *\n     * This command will show the new migrations that have not been applied.\n     * For example,\n     *\n     * ```\n     * yii migrate/new     # showing the first 10 new migrations\n     * yii migrate/new 5   # showing the first 5 new migrations\n     * yii migrate/new all # showing all new migrations\n     * ```\n     *\n     * @param int|string $limit the maximum number of new migrations to be displayed.\n     * If it is `all`, all available new migrations will be displayed.\n     * @throws \\yii\\console\\Exception if invalid limit value passed\n     */\n    public function actionNew($limit = 10)\n    {\n        if ($limit !== 'all') {\n            $limit = (int) $limit;\n            if ($limit < 1) {\n                throw new Exception('The limit must be greater than 0.');\n            }\n        }\n\n        $migrations = $this->getNewMigrations();\n\n        if (empty($migrations)) {\n            $this->stdout(\"No new migrations found. Your system is up-to-date.\\n\", Console::FG_GREEN);\n        } else {\n            $n = count($migrations);\n            if ($limit !== 'all' && $n > $limit) {\n                $migrations = array_slice($migrations, 0, $limit);\n                $this->stdout(\"Showing $limit out of $n new \" . ($n === 1 ? 'migration' : 'migrations') . \":\\n\", Console::FG_YELLOW);\n            } else {\n                $this->stdout(\"Found $n new \" . ($n === 1 ? 'migration' : 'migrations') . \":\\n\", Console::FG_YELLOW);\n            }\n\n            foreach ($migrations as $migration) {\n                $this->stdout(\"\\t\" . $migration . \"\\n\");\n            }\n        }\n\n        return ExitCode::OK;\n    }\n\n    /**\n     * Creates a new migration.\n     *\n     * This command creates a new migration using the available migration template.\n     * After using this command, developers should modify the created migration\n     * skeleton by filling up the actual migration logic.\n     *\n     * ```\n     * yii migrate/create create_user_table\n     * ```\n     *\n     * In order to generate a namespaced migration, you should specify a namespace before the migration's name.\n     * Note that backslash (`\\`) is usually considered a special character in the shell, so you need to escape it\n     * properly to avoid shell errors or incorrect behavior.\n     * For example:\n     *\n     * ```\n     * yii migrate/create app\\\\migrations\\\\createUserTable\n     * ```\n     *\n     * In case [[migrationPath]] is not set and no namespace is provided, the first entry of [[migrationNamespaces]] will be used.\n     *\n     * @param string $name the name of the new migration. This should only contain\n     * letters, digits, underscores and/or backslashes.\n     *\n     * Note: If the migration name is of a special form, for example create_xxx or\n     * drop_xxx, then the generated migration file will contain extra code,\n     * in this case for creating/dropping tables.\n     *\n     * @throws Exception if the name argument is invalid.\n     */\n    public function actionCreate($name)\n    {\n        if (!preg_match('/^[\\w\\\\\\\\]+$/', $name)) {\n            throw new Exception('The migration name should contain letters, digits, underscore and/or backslash characters only.');\n        }\n\n        list($namespace, $className) = $this->generateClassName($name);\n        // Abort if name is too long\n        $nameLimit = $this->getMigrationNameLimit();\n        if ($nameLimit !== null && strlen($className) > $nameLimit) {\n            throw new Exception('The migration name is too long.');\n        }\n\n        $migrationPath = $this->findMigrationPath($namespace);\n\n        $file = $migrationPath . DIRECTORY_SEPARATOR . $className . '.php';\n        if ($this->confirm(\"Create new migration '$file'?\")) {\n            $content = $this->generateMigrationSourceCode([\n                'name' => $name,\n                'className' => $className,\n                'namespace' => $namespace,\n            ]);\n            FileHelper::createDirectory($migrationPath);\n            if (file_put_contents($file, $content, LOCK_EX) === false) {\n                $this->stdout(\"Failed to create new migration.\\n\", Console::FG_RED);\n\n                return ExitCode::IOERR;\n            }\n\n            FileHelper::changeOwnership($file, $this->newFileOwnership, $this->newFileMode);\n\n            $this->stdout(\"New migration created successfully.\\n\", Console::FG_GREEN);\n        }\n\n        return ExitCode::OK;\n    }\n\n    /**\n     * Generates class base name and namespace from migration name from user input.\n     * @param string $name migration name from user input.\n     * @return array list of 2 elements: 'namespace' and 'class base name'\n     * @since 2.0.10\n     */\n    private function generateClassName($name)\n    {\n        $namespace = null;\n        $name = trim($name, '\\\\');\n        if (strpos($name, '\\\\') !== false) {\n            $namespace = substr($name, 0, strrpos($name, '\\\\'));\n            $name = substr($name, strrpos($name, '\\\\') + 1);\n        } elseif ($this->migrationPath === null) {\n            $migrationNamespaces = $this->migrationNamespaces;\n            $namespace = array_shift($migrationNamespaces);\n        }\n\n        if ($namespace === null) {\n            $class = 'm' . gmdate('ymd_His') . '_' . $name;\n        } else {\n            $class = 'M' . gmdate('ymdHis') . Inflector::camelize($name);\n        }\n\n        return [$namespace, $class];\n    }\n\n    /**\n     * Finds the file path for the specified migration namespace.\n     * @param string|null $namespace migration namespace.\n     * @return string migration file path.\n     * @throws Exception on failure.\n     * @since 2.0.10\n     */\n    private function findMigrationPath($namespace)\n    {\n        if (empty($namespace)) {\n            return is_array($this->migrationPath) ? reset($this->migrationPath) : $this->migrationPath;\n        }\n\n        if (!in_array($namespace, $this->migrationNamespaces, true)) {\n            throw new Exception(\"Namespace '{$namespace}' not found in `migrationNamespaces`\");\n        }\n\n        return $this->getNamespacePath($namespace);\n    }\n\n    /**\n     * Returns the file path matching the give namespace.\n     * @param string $namespace namespace.\n     * @return string file path.\n     * @since 2.0.10\n     */\n    private function getNamespacePath($namespace)\n    {\n        return str_replace('/', DIRECTORY_SEPARATOR, Yii::getAlias('@' . str_replace('\\\\', '/', $namespace)));\n    }\n\n    /**\n     * Upgrades with the specified migration class.\n     * @param string $class the migration class name\n     * @return bool whether the migration is successful\n     */\n    protected function migrateUp($class)\n    {\n        if ($class === self::BASE_MIGRATION) {\n            return true;\n        }\n\n        $this->stdout(\"*** applying $class\\n\", Console::FG_YELLOW);\n        $start = microtime(true);\n        $migration = $this->createMigration($class);\n        if ($migration->up() !== false) {\n            $this->addMigrationHistory($class);\n            $time = microtime(true) - $start;\n            $this->stdout(\"*** applied $class (time: \" . sprintf('%.3f', $time) . \"s)\\n\\n\", Console::FG_GREEN);\n\n            return true;\n        }\n\n        $time = microtime(true) - $start;\n        $this->stdout(\"*** failed to apply $class (time: \" . sprintf('%.3f', $time) . \"s)\\n\\n\", Console::FG_RED);\n\n        return false;\n    }\n\n    /**\n     * Downgrades with the specified migration class.\n     * @param string $class the migration class name\n     * @return bool whether the migration is successful\n     */\n    protected function migrateDown($class)\n    {\n        if ($class === self::BASE_MIGRATION) {\n            return true;\n        }\n\n        $this->stdout(\"*** reverting $class\\n\", Console::FG_YELLOW);\n        $start = microtime(true);\n        $migration = $this->createMigration($class);\n        if ($migration->down() !== false) {\n            $this->removeMigrationHistory($class);\n            $time = microtime(true) - $start;\n            $this->stdout(\"*** reverted $class (time: \" . sprintf('%.3f', $time) . \"s)\\n\\n\", Console::FG_GREEN);\n\n            return true;\n        }\n\n        $time = microtime(true) - $start;\n        $this->stdout(\"*** failed to revert $class (time: \" . sprintf('%.3f', $time) . \"s)\\n\\n\", Console::FG_RED);\n\n        return false;\n    }\n\n    /**\n     * Creates a new migration instance.\n     * @param string $class the migration class name\n     * @return \\yii\\db\\MigrationInterface the migration instance\n     */\n    protected function createMigration($class)\n    {\n        $this->includeMigrationFile($class);\n\n        /** @var MigrationInterface $migration */\n        $migration = Yii::createObject($class);\n        if ($migration instanceof BaseObject && $migration->canSetProperty('compact')) {\n            $migration->compact = $this->compact;\n        }\n\n        return $migration;\n    }\n\n    /**\n     * Includes the migration file for a given migration class name.\n     *\n     * This function will do nothing on namespaced migrations, which are loaded by\n     * autoloading automatically. It will include the migration file, by searching\n     * [[migrationPath]] for classes without namespace.\n     * @param string $class the migration class name.\n     * @since 2.0.12\n     */\n    protected function includeMigrationFile($class)\n    {\n        $class = trim($class, '\\\\');\n        if (strpos($class, '\\\\') === false) {\n            if (is_array($this->migrationPath)) {\n                foreach ($this->migrationPath as $path) {\n                    $file = $path . DIRECTORY_SEPARATOR . $class . '.php';\n                    if (is_file($file)) {\n                        require_once $file;\n                        break;\n                    }\n                }\n            } else {\n                $file = $this->migrationPath . DIRECTORY_SEPARATOR . $class . '.php';\n                require_once $file;\n            }\n        }\n    }\n\n    /**\n     * Migrates to the specified apply time in the past.\n     * @param int $time UNIX timestamp value.\n     */\n    protected function migrateToTime($time)\n    {\n        $count = 0;\n        $migrations = array_values($this->getMigrationHistory(null));\n        while ($count < count($migrations) && $migrations[$count] > $time) {\n            ++$count;\n        }\n        if ($count === 0) {\n            $this->stdout(\"Nothing needs to be done.\\n\", Console::FG_GREEN);\n        } else {\n            return $this->actionDown($count);\n        }\n\n        return ExitCode::OK;\n    }\n\n    /**\n     * Migrates to the certain version.\n     * @param string $version name in the full format.\n     * @return int CLI exit code\n     * @throws Exception if the provided version cannot be found.\n     */\n    protected function migrateToVersion($version)\n    {\n        $originalVersion = $version;\n\n        // try migrate up\n        $migrations = $this->getNewMigrations();\n        foreach ($migrations as $i => $migration) {\n            if (strpos($migration, $version) === 0) {\n                return $this->actionUp($i + 1);\n            }\n        }\n\n        // try migrate down\n        $migrations = array_keys($this->getMigrationHistory(null));\n        foreach ($migrations as $i => $migration) {\n            if (strpos($migration, $version) === 0) {\n                if ($i === 0) {\n                    $this->stdout(\"Already at '$originalVersion'. Nothing needs to be done.\\n\", Console::FG_YELLOW);\n                } else {\n                    return $this->actionDown($i);\n                }\n\n                return ExitCode::OK;\n            }\n        }\n\n        throw new Exception(\"Unable to find the version '$originalVersion'.\");\n    }\n\n    /**\n     * Returns the migrations that are not applied.\n     * @return array list of new migrations\n     */\n    protected function getNewMigrations()\n    {\n        $applied = [];\n        foreach ($this->getMigrationHistory(null) as $class => $time) {\n            $applied[trim($class, '\\\\')] = true;\n        }\n\n        $migrationPaths = [];\n        if (is_array($this->migrationPath)) {\n            foreach ($this->migrationPath as $path) {\n                $migrationPaths[] = [$path, ''];\n            }\n        } elseif (!empty($this->migrationPath)) {\n            $migrationPaths[] = [$this->migrationPath, ''];\n        }\n        foreach ($this->migrationNamespaces as $namespace) {\n            $migrationPaths[] = [$this->getNamespacePath($namespace), $namespace];\n        }\n\n        $migrations = [];\n        foreach ($migrationPaths as $item) {\n            list($migrationPath, $namespace) = $item;\n            if (!file_exists($migrationPath)) {\n                continue;\n            }\n            $handle = opendir($migrationPath);\n            while (($file = readdir($handle)) !== false) {\n                if ($file === '.' || $file === '..') {\n                    continue;\n                }\n                $path = $migrationPath . DIRECTORY_SEPARATOR . $file;\n                if (preg_match('/^(m(\\d{6}_?\\d{6})\\D.*?)\\.php$/is', $file, $matches) && is_file($path)) {\n                    $class = $matches[1];\n                    if (!empty($namespace)) {\n                        $class = $namespace . '\\\\' . $class;\n                    }\n                    $time = str_replace('_', '', $matches[2]);\n                    if (!isset($applied[$class])) {\n                        $migrations[$time . '\\\\' . $class] = $class;\n                    }\n                }\n            }\n            closedir($handle);\n        }\n        ksort($migrations);\n\n        return array_values($migrations);\n    }\n\n    /**\n     * Generates new migration source PHP code.\n     * Child class may override this method, adding extra logic or variation to the process.\n     * @param array $params generation parameters, usually following parameters are present:\n     *\n     *  - name: string migration base name\n     *  - className: string migration class name\n     *\n     * @return string generated PHP code.\n     * @since 2.0.8\n     */\n    protected function generateMigrationSourceCode($params)\n    {\n        return $this->renderFile(Yii::getAlias($this->templateFile), $params);\n    }\n\n    /**\n     * Truncates the database.\n     * This method should be overwritten in subclasses to implement the task of clearing the database.\n     * @throws NotSupportedException if not overridden\n     * @since 2.0.13\n     */\n    protected function truncateDatabase()\n    {\n        throw new NotSupportedException('This command is not implemented in ' . get_class($this));\n    }\n\n    /**\n     * Return the maximum name length for a migration.\n     *\n     * Subclasses may override this method to define a limit.\n     * @return int|null the maximum name length for a migration or `null` if no limit applies.\n     * @since 2.0.13\n     */\n    protected function getMigrationNameLimit()\n    {\n        return null;\n    }\n\n    /**\n     * Returns the migration history.\n     * @param int|null $limit the maximum number of records in the history to be returned. `null` for \"no limit\".\n     * @return array the migration history\n     */\n    abstract protected function getMigrationHistory($limit);\n\n    /**\n     * Adds new migration entry to the history.\n     * @param string $version migration version name.\n     */\n    abstract protected function addMigrationHistory($version);\n\n    /**\n     * Removes existing migration from the history.\n     * @param string $version migration version name.\n     */\n    abstract protected function removeMigrationHistory($version);\n}\n"
  },
  {
    "path": "framework/console/controllers/CacheController.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\console\\controllers;\n\nuse Yii;\nuse yii\\caching\\ApcCache;\nuse yii\\caching\\CacheInterface;\nuse yii\\console\\Application;\nuse yii\\console\\Controller;\nuse yii\\console\\Exception;\nuse yii\\console\\ExitCode;\nuse yii\\helpers\\Console;\n\n/**\n * Allows you to flush cache.\n *\n * see list of available components to flush:\n *\n *     yii cache\n *\n * flush particular components specified by their names:\n *\n *     yii cache/flush first second third\n *\n * flush all cache components that can be found in the system\n *\n *     yii cache/flush-all\n *\n * Note that the command uses cache components defined in your console application configuration file. If components\n * configured are different from web application, web application cache won't be cleared. In order to fix it please\n * duplicate web application cache components in console config. You can use any component names.\n *\n * APC is not shared between PHP processes so flushing cache from command line has no effect on web.\n * Flushing web cache could be either done by:\n *\n * - Putting a php file under web root and calling it via HTTP\n * - Using [Cachetool](https://gordalina.github.io/cachetool/)\n *\n * @author Alexander Makarov <sam@rmcreative.ru>\n * @author Mark Jebri <mark.github@yandex.ru>\n * @since 2.0\n *\n * @template T of Application = Application\n * @extends Controller<T>\n */\nclass CacheController extends Controller\n{\n    /**\n     * Lists the caches that can be flushed.\n     */\n    public function actionIndex()\n    {\n        $caches = $this->findCaches();\n\n        if (!empty($caches)) {\n            $this->notifyCachesCanBeFlushed($caches);\n        } else {\n            $this->notifyNoCachesFound();\n        }\n    }\n\n    /**\n     * Flushes given cache components.\n     *\n     * For example,\n     *\n     * ```\n     * # flushes caches specified by their id: \"first\", \"second\", \"third\"\n     * yii cache/flush first second third\n     * ```\n     */\n    public function actionFlush()\n    {\n        $cachesInput = func_get_args();\n\n        if (empty($cachesInput)) {\n            throw new Exception('You should specify cache components names');\n        }\n\n        $caches = $this->findCaches($cachesInput);\n        $cachesInfo = [];\n\n        $foundCaches = array_keys($caches);\n        $notFoundCaches = array_diff($cachesInput, array_keys($caches));\n\n        if ($notFoundCaches !== []) {\n            $this->notifyNotFoundCaches($notFoundCaches);\n        }\n\n        if ($foundCaches === []) {\n            $this->notifyNoCachesFound();\n            return ExitCode::OK;\n        }\n\n        if (!$this->confirmFlush($foundCaches)) {\n            return ExitCode::OK;\n        }\n\n        foreach ($caches as $name => $class) {\n            $cachesInfo[] = [\n                'name' => $name,\n                'class' => $class,\n                'is_flushed' => $this->canBeFlushed($class) ? Yii::$app->get($name)->flush() : false,\n            ];\n        }\n\n        $this->notifyFlushed($cachesInfo);\n    }\n\n    /**\n     * Flushes all caches registered in the system.\n     */\n    public function actionFlushAll()\n    {\n        $caches = $this->findCaches();\n        $cachesInfo = [];\n\n        if (empty($caches)) {\n            $this->notifyNoCachesFound();\n            return ExitCode::OK;\n        }\n\n        foreach ($caches as $name => $class) {\n            $cachesInfo[] = [\n                'name' => $name,\n                'class' => $class,\n                'is_flushed' => $this->canBeFlushed($class) ? Yii::$app->get($name)->flush() : false,\n            ];\n        }\n\n        $this->notifyFlushed($cachesInfo);\n    }\n\n    /**\n     * Clears DB schema cache for a given connection component.\n     *\n     * ```\n     * # clears cache schema specified by component id: \"db\"\n     * yii cache/flush-schema db\n     * ```\n     *\n     * @param string $db id connection component\n     * @return int exit code\n     * @throws Exception\n     * @throws \\yii\\base\\InvalidConfigException\n     *\n     * @since 2.0.1\n     */\n    public function actionFlushSchema($db = 'db')\n    {\n        $connection = Yii::$app->get($db, false);\n        if ($connection === null) {\n            $this->stdout(\"Unknown component \\\"$db\\\".\\n\", Console::FG_RED);\n            return ExitCode::UNSPECIFIED_ERROR;\n        }\n\n        if (!$connection instanceof \\yii\\db\\Connection) {\n            $this->stdout(\"\\\"$db\\\" component doesn't inherit \\\\yii\\\\db\\\\Connection.\\n\", Console::FG_RED);\n            return ExitCode::UNSPECIFIED_ERROR;\n        } elseif (!$this->confirm(\"Flush cache schema for \\\"$db\\\" connection?\")) {\n            return ExitCode::OK;\n        }\n\n        try {\n            $schema = $connection->getSchema();\n            $schema->refresh();\n            $this->stdout(\"Schema cache for component \\\"$db\\\", was flushed.\\n\\n\", Console::FG_GREEN);\n        } catch (\\Exception $e) {\n            $this->stdout($e->getMessage() . \"\\n\\n\", Console::FG_RED);\n        }\n\n        return ExitCode::OK;\n    }\n\n    /**\n     * Notifies user that given caches are found and can be flushed.\n     * @param array $caches array of cache component classes\n     */\n    private function notifyCachesCanBeFlushed($caches)\n    {\n        $this->stdout(\"The following caches were found in the system:\\n\\n\", Console::FG_YELLOW);\n\n        foreach ($caches as $name => $class) {\n            if ($this->canBeFlushed($class)) {\n                $this->stdout(\"\\t* $name ($class)\\n\", Console::FG_GREEN);\n            } else {\n                $this->stdout(\"\\t* $name ($class) - can not be flushed via console\\n\", Console::FG_YELLOW);\n            }\n        }\n\n        $this->stdout(\"\\n\");\n    }\n\n    /**\n     * Notifies user that there was not found any cache in the system.\n     */\n    private function notifyNoCachesFound()\n    {\n        $this->stdout(\"No cache components were found in the system.\\n\", Console::FG_RED);\n    }\n\n    /**\n     * Notifies user that given cache components were not found in the system.\n     * @param array $cachesNames\n     */\n    private function notifyNotFoundCaches($cachesNames)\n    {\n        $this->stdout(\"The following cache components were NOT found:\\n\\n\", Console::FG_RED);\n\n        foreach ($cachesNames as $name) {\n            $this->stdout(\"\\t* $name \\n\", Console::FG_GREEN);\n        }\n\n        $this->stdout(\"\\n\");\n    }\n\n    /**\n     * @param array $caches\n     */\n    private function notifyFlushed($caches)\n    {\n        $this->stdout(\"The following cache components were processed:\\n\\n\", Console::FG_YELLOW);\n\n        foreach ($caches as $cache) {\n            $this->stdout(\"\\t* \" . $cache['name'] . ' (' . $cache['class'] . ')', Console::FG_GREEN);\n\n            if (!$cache['is_flushed']) {\n                $this->stdout(\" - not flushed\\n\", Console::FG_RED);\n            } else {\n                $this->stdout(\"\\n\");\n            }\n        }\n\n        $this->stdout(\"\\n\");\n    }\n\n    /**\n     * Prompts user with confirmation if caches should be flushed.\n     * @param array $cachesNames\n     * @return bool\n     */\n    private function confirmFlush($cachesNames)\n    {\n        $this->stdout(\"The following cache components will be flushed:\\n\\n\", Console::FG_YELLOW);\n\n        foreach ($cachesNames as $name) {\n            $this->stdout(\"\\t* $name \\n\", Console::FG_GREEN);\n        }\n\n        return $this->confirm(\"\\nFlush above cache components?\");\n    }\n\n    /**\n     * Returns array of caches in the system, keys are cache components names, values are class names.\n     * @param array $cachesNames caches to be found\n     * @return array\n     */\n    private function findCaches(array $cachesNames = [])\n    {\n        $caches = [];\n        $components = Yii::$app->getComponents();\n        $findAll = ($cachesNames === []);\n\n        foreach ($components as $name => $component) {\n            if (!$findAll && !in_array($name, $cachesNames, true)) {\n                continue;\n            }\n\n            if ($component instanceof CacheInterface) {\n                $caches[$name] = get_class($component);\n            } elseif (is_array($component) && isset($component['class']) && $this->isCacheClass($component['class'])) {\n                $caches[$name] = $component['class'];\n            } elseif (is_string($component) && $this->isCacheClass($component)) {\n                $caches[$name] = $component;\n            } elseif ($component instanceof \\Closure) {\n                $cache = Yii::$app->get($name);\n                if ($this->isCacheClass($cache)) {\n                    $cacheClass = get_class($cache);\n                    $caches[$name] = $cacheClass;\n                }\n            }\n        }\n\n        return $caches;\n    }\n\n    /**\n     * Checks if given class is a Cache class.\n     * @param string $className class name.\n     * @return bool\n     */\n    private function isCacheClass($className)\n    {\n        return is_subclass_of($className, 'yii\\caching\\CacheInterface') || $className === 'yii\\caching\\CacheInterface';\n    }\n\n    /**\n     * Checks if cache of a certain class can be flushed.\n     * @param string $className class name.\n     * @return bool\n     */\n    private function canBeFlushed($className)\n    {\n        return !is_a($className, ApcCache::className(), true) || PHP_SAPI !== 'cli';\n    }\n}\n"
  },
  {
    "path": "framework/console/controllers/FixtureController.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\console\\controllers;\n\nuse Yii;\nuse yii\\base\\InvalidConfigException;\nuse yii\\base\\InvalidParamException;\nuse yii\\console\\Application;\nuse yii\\console\\Controller;\nuse yii\\console\\Exception;\nuse yii\\console\\ExitCode;\nuse yii\\helpers\\Console;\nuse yii\\helpers\\FileHelper;\nuse yii\\test\\Fixture;\nuse yii\\test\\FixtureTrait;\n\n/**\n * Manages fixture data loading and unloading.\n *\n * ```\n * #load fixtures from UsersFixture class with default namespace \"tests\\unit\\fixtures\"\n * yii fixture/load User\n *\n * #also a short version of this command (generate action is default)\n * yii fixture User\n *\n * #load all fixtures\n * yii fixture \"*\"\n *\n * #load all fixtures except User\n * yii fixture \"*, -User\"\n *\n * #load fixtures with different namespace.\n * yii fixture/load User --namespace=alias\\my\\custom\\namespace\\goes\\here\n * ```\n *\n * The `unload` sub-command can be used similarly to unload fixtures.\n *\n * @author Mark Jebri <mark.github@yandex.ru>\n * @since 2.0\n *\n * @template T of Application = Application\n * @extends Controller<T>\n */\nclass FixtureController extends Controller\n{\n    use FixtureTrait;\n\n    /**\n     * @var string controller default action ID.\n     */\n    public $defaultAction = 'load';\n    /**\n     * @var string default namespace to search fixtures in\n     */\n    public $namespace = 'tests\\unit\\fixtures';\n    /**\n     * @var array global fixtures that should be applied when loading and unloading. By default it is set to `InitDbFixture`\n     * that disables and enables integrity check, so your data can be safely loaded.\n     */\n    public $globalFixtures = [\n        'yii\\test\\InitDbFixture',\n    ];\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function options($actionID)\n    {\n        return array_merge(parent::options($actionID), [\n            'namespace', 'globalFixtures',\n        ]);\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.8\n     */\n    public function optionAliases()\n    {\n        return array_merge(parent::optionAliases(), [\n            'g' => 'globalFixtures',\n            'n' => 'namespace',\n        ]);\n    }\n\n    /**\n     * Loads the specified fixture data.\n     *\n     * For example,\n     *\n     * ```\n     * # load the fixture data specified by User and UserProfile.\n     * # any existing fixture data will be removed first\n     * yii fixture/load \"User, UserProfile\"\n     *\n     * # load all available fixtures found under 'tests\\unit\\fixtures'\n     * yii fixture/load \"*\"\n     *\n     * # load all fixtures except User and UserProfile\n     * yii fixture/load \"*, -User, -UserProfile\"\n     * ```\n     *\n     * @param array $fixturesInput\n     * @return int return code\n     * @throws Exception if the specified fixture does not exist.\n     */\n    public function actionLoad(array $fixturesInput = [])\n    {\n        if ($fixturesInput === []) {\n            $this->printHelpMessage();\n            return ExitCode::OK;\n        }\n\n        $filtered = $this->filterFixtures($fixturesInput);\n        $except = $filtered['except'];\n\n        if (!$this->needToApplyAll($fixturesInput[0])) {\n            $fixtures = $filtered['apply'];\n\n            $foundFixtures = $this->findFixtures($fixtures);\n            $notFoundFixtures = array_diff($fixtures, $foundFixtures);\n\n            if ($notFoundFixtures !== []) {\n                $this->notifyNotFound($notFoundFixtures);\n            }\n        } else {\n            $foundFixtures = $this->findFixtures();\n        }\n\n        $fixturesToLoad = array_diff($foundFixtures, $except);\n\n        if (!$foundFixtures) {\n            throw new Exception(\n                'No files were found for: \"' . implode(', ', $fixturesInput) . \"\\\".\\n\" .\n                \"Check that files exist under fixtures path: \\n\\\"\" . $this->getFixturePath() . '\".'\n            );\n        }\n\n        if ($fixturesToLoad === []) {\n            $this->notifyNothingToLoad($foundFixtures, $except);\n            return ExitCode::OK;\n        }\n\n        if (!$this->confirmLoad($fixturesToLoad, $except)) {\n            return ExitCode::OK;\n        }\n\n        $fixtures = $this->getFixturesConfig(array_merge($this->globalFixtures, $fixturesToLoad));\n\n        if (!$fixtures) {\n            throw new Exception('No fixtures were found in namespace: \"' . $this->namespace . '\"' . '');\n        }\n\n        $fixturesObjects = $this->createFixtures($fixtures);\n\n        $this->unloadFixtures($fixturesObjects);\n        $this->loadFixtures($fixturesObjects);\n        $this->notifyLoaded($fixturesObjects);\n\n        return ExitCode::OK;\n    }\n\n    /**\n     * Unloads the specified fixtures.\n     *\n     * For example,\n     *\n     * ```\n     * # unload the fixture data specified by User and UserProfile.\n     * yii fixture/unload \"User, UserProfile\"\n     *\n     * # unload all fixtures found under 'tests\\unit\\fixtures'\n     * yii fixture/unload \"*\"\n     *\n     * # unload all fixtures except User and UserProfile\n     * yii fixture/unload \"*, -User, -UserProfile\"\n     * ```\n     *\n     * @param array $fixturesInput\n     * @return int return code\n     * @throws Exception if the specified fixture does not exist.\n     */\n    public function actionUnload(array $fixturesInput = [])\n    {\n        if ($fixturesInput === []) {\n            $this->printHelpMessage();\n            return ExitCode::OK;\n        }\n\n        $filtered = $this->filterFixtures($fixturesInput);\n        $except = $filtered['except'];\n\n        if (!$this->needToApplyAll($fixturesInput[0])) {\n            $fixtures = $filtered['apply'];\n\n            $foundFixtures = $this->findFixtures($fixtures);\n            $notFoundFixtures = array_diff($fixtures, $foundFixtures);\n\n            if ($notFoundFixtures !== []) {\n                $this->notifyNotFound($notFoundFixtures);\n            }\n        } else {\n            $foundFixtures = $this->findFixtures();\n        }\n\n        if ($foundFixtures === []) {\n            throw new Exception(\n                'No files were found for: \"' . implode(', ', $fixturesInput) . \"\\\".\\n\" .\n                \"Check that files exist under fixtures path: \\n\\\"\" . $this->getFixturePath() . '\".'\n            );\n        }\n\n        $fixturesToUnload = array_diff($foundFixtures, $except);\n\n        if ($fixturesToUnload === []) {\n            $this->notifyNothingToUnload($foundFixtures, $except);\n            return ExitCode::OK;\n        }\n\n        if (!$this->confirmUnload($fixturesToUnload, $except)) {\n            return ExitCode::OK;\n        }\n\n        $fixtures = $this->getFixturesConfig(array_merge($this->globalFixtures, $fixturesToUnload));\n\n        if ($fixtures === []) {\n            throw new Exception('No fixtures were found in namespace: ' . $this->namespace . '\".');\n        }\n\n        $this->unloadFixtures($this->createFixtures($fixtures));\n        $this->notifyUnloaded($fixtures);\n\n        return ExitCode::OK;\n    }\n\n    /**\n     * Show help message.\n     */\n    private function printHelpMessage()\n    {\n        $this->stdout($this->getHelpSummary() . \"\\n\");\n\n        $helpCommand = Console::ansiFormat('yii help fixture', [Console::FG_CYAN]);\n        $this->stdout(\"Use $helpCommand to get usage info.\\n\");\n    }\n\n    /**\n     * Notifies user that fixtures were successfully loaded.\n     * @param Fixture[] $fixtures array of loaded fixtures\n     */\n    private function notifyLoaded($fixtures)\n    {\n        $this->stdout(\"Fixtures were successfully loaded from namespace:\\n\", Console::FG_YELLOW);\n        $this->stdout(\"\\t\\\"\" . Yii::getAlias($this->namespace) . \"\\\"\\n\\n\", Console::FG_GREEN);\n\n        $fixtureClassNames = [];\n\n        foreach ($fixtures as $fixture) {\n            $fixtureClassNames[] = $fixture::className();\n        }\n\n        $this->outputList($fixtureClassNames);\n    }\n\n    /**\n     * Notifies user that there are no fixtures to load according input conditions.\n     * @param array $foundFixtures array of found fixtures\n     * @param array $except array of names of fixtures that should not be loaded\n     */\n    public function notifyNothingToLoad($foundFixtures, $except)\n    {\n        $this->stdout(\"Fixtures to load could not be found according given conditions:\\n\\n\", Console::FG_RED);\n        $this->stdout(\"Fixtures namespace is: \\n\", Console::FG_YELLOW);\n        $this->stdout(\"\\t\" . $this->namespace . \"\\n\", Console::FG_GREEN);\n\n        if (count($foundFixtures)) {\n            $this->stdout(\"\\nFixtures founded under the namespace:\\n\\n\", Console::FG_YELLOW);\n            $this->outputList($foundFixtures);\n        }\n\n        if (count($except)) {\n            $this->stdout(\"\\nFixtures that will NOT be loaded: \\n\\n\", Console::FG_YELLOW);\n            $this->outputList($except);\n        }\n    }\n\n    /**\n     * Notifies user that there are no fixtures to unload according input conditions.\n     * @param array $foundFixtures array of found fixtures\n     * @param array $except array of names of fixtures that should not be loaded\n     */\n    public function notifyNothingToUnload($foundFixtures, $except)\n    {\n        $this->stdout(\"Fixtures to unload could not be found according to given conditions:\\n\\n\", Console::FG_RED);\n        $this->stdout(\"Fixtures namespace is: \\n\", Console::FG_YELLOW);\n        $this->stdout(\"\\t\" . $this->namespace . \"\\n\", Console::FG_GREEN);\n\n        if (count($foundFixtures)) {\n            $this->stdout(\"\\nFixtures found under the namespace:\\n\\n\", Console::FG_YELLOW);\n            $this->outputList($foundFixtures);\n        }\n\n        if (count($except)) {\n            $this->stdout(\"\\nFixtures that will NOT be unloaded: \\n\\n\", Console::FG_YELLOW);\n            $this->outputList($except);\n        }\n    }\n\n    /**\n     * Notifies user that fixtures were successfully unloaded.\n     * @param array $fixtures\n     */\n    private function notifyUnloaded($fixtures)\n    {\n        $this->stdout(\"\\nFixtures were successfully unloaded from namespace: \", Console::FG_YELLOW);\n        $this->stdout(Yii::getAlias($this->namespace) . \"\\\"\\n\\n\", Console::FG_GREEN);\n        $this->outputList($fixtures);\n    }\n\n    /**\n     * Notifies user that fixtures were not found under fixtures path.\n     * @param array $fixtures\n     */\n    private function notifyNotFound($fixtures)\n    {\n        $this->stdout(\"Some fixtures were not found under path:\\n\", Console::BG_RED);\n        $this->stdout(\"\\t\" . $this->getFixturePath() . \"\\n\\n\", Console::FG_GREEN);\n        $this->stdout(\"Check that they have correct namespace \\\"{$this->namespace}\\\" \\n\", Console::BG_RED);\n        $this->outputList($fixtures);\n        $this->stdout(\"\\n\");\n    }\n\n    /**\n     * Prompts user with confirmation if fixtures should be loaded.\n     * @param array $fixtures\n     * @param array $except\n     * @return bool\n     */\n    private function confirmLoad($fixtures, $except)\n    {\n        $this->stdout(\"Fixtures namespace is: \\n\", Console::FG_YELLOW);\n        $this->stdout(\"\\t\" . $this->namespace . \"\\n\\n\", Console::FG_GREEN);\n\n        if (count($this->globalFixtures)) {\n            $this->stdout(\"Global fixtures will be used:\\n\\n\", Console::FG_YELLOW);\n            $this->outputList($this->globalFixtures);\n        }\n\n        if (count($fixtures)) {\n            $this->stdout(\"\\nFixtures below will be loaded:\\n\\n\", Console::FG_YELLOW);\n            $this->outputList($fixtures);\n        }\n\n        if (count($except)) {\n            $this->stdout(\"\\nFixtures that will NOT be loaded: \\n\\n\", Console::FG_YELLOW);\n            $this->outputList($except);\n        }\n\n        $this->stdout(\"\\nBe aware that:\\n\", Console::BOLD);\n        $this->stdout(\"Applying leads to purging of certain data in the database!\\n\", Console::FG_RED);\n\n        return $this->confirm(\"\\nLoad above fixtures?\");\n    }\n\n    /**\n     * Prompts user with confirmation for fixtures that should be unloaded.\n     * @param array $fixtures\n     * @param array $except\n     * @return bool\n     */\n    private function confirmUnload($fixtures, $except)\n    {\n        $this->stdout(\"Fixtures namespace is: \\n\", Console::FG_YELLOW);\n        $this->stdout(\"\\t\" . $this->namespace . \"\\n\\n\", Console::FG_GREEN);\n\n        if (count($this->globalFixtures)) {\n            $this->stdout(\"Global fixtures will be used:\\n\\n\", Console::FG_YELLOW);\n            $this->outputList($this->globalFixtures);\n        }\n\n        if (count($fixtures)) {\n            $this->stdout(\"\\nFixtures below will be unloaded:\\n\\n\", Console::FG_YELLOW);\n            $this->outputList($fixtures);\n        }\n\n        if (count($except)) {\n            $this->stdout(\"\\nFixtures that will NOT be unloaded:\\n\\n\", Console::FG_YELLOW);\n            $this->outputList($except);\n        }\n\n        return $this->confirm(\"\\nUnload fixtures?\");\n    }\n\n    /**\n     * Outputs data to the console as a list.\n     * @param array $data\n     */\n    private function outputList($data)\n    {\n        foreach ($data as $index => $item) {\n            $this->stdout(\"\\t\" . ($index + 1) . \". {$item}\\n\", Console::FG_GREEN);\n        }\n    }\n\n    /**\n     * Checks if needed to apply all fixtures.\n     * @param string $fixture\n     * @return bool\n     */\n    public function needToApplyAll($fixture)\n    {\n        return $fixture === '*';\n    }\n\n    /**\n     * Finds fixtures to be loaded, for example \"User\", if no fixtures were specified then all of them\n     * will be searching by suffix \"Fixture.php\".\n     * @param array $fixtures fixtures to be loaded\n     * @return array Array of found fixtures. These may differ from input parameter as not all fixtures may exists.\n     */\n    private function findFixtures(array $fixtures = [])\n    {\n        $fixturesPath = $this->getFixturePath();\n\n        $filesToSearch = ['*Fixture.php'];\n        $findAll = ($fixtures === []);\n\n        if (!$findAll) {\n            $filesToSearch = [];\n\n            foreach ($fixtures as $fileName) {\n                $filesToSearch[] = $fileName . 'Fixture.php';\n            }\n        }\n\n        $files = FileHelper::findFiles($fixturesPath, ['only' => $filesToSearch]);\n        $foundFixtures = [];\n\n        foreach ($files as $fixture) {\n            $foundFixtures[] = $this->getFixtureRelativeName($fixture);\n        }\n\n        return $foundFixtures;\n    }\n\n    /**\n     * Calculates fixture's name\n     * Basically, strips [[getFixturePath()]] and `Fixture.php' suffix from fixture's full path.\n     * @see getFixturePath()\n     * @param string $fullFixturePath Full fixture path\n     * @return string Relative fixture name\n     */\n    private function getFixtureRelativeName($fullFixturePath)\n    {\n        $fixturesPath = FileHelper::normalizePath($this->getFixturePath());\n        $fullFixturePath = FileHelper::normalizePath($fullFixturePath);\n\n        $relativeName = substr($fullFixturePath, strlen($fixturesPath) + 1);\n        $relativeDir = dirname($relativeName) === '.' ? '' : dirname($relativeName) . '/';\n\n        return $relativeDir . basename($fullFixturePath, 'Fixture.php');\n    }\n\n    /**\n     * Returns valid fixtures config that can be used to load them.\n     * @param array $fixtures fixtures to configure\n     * @return array\n     */\n    private function getFixturesConfig($fixtures)\n    {\n        $config = [];\n\n        foreach ($fixtures as $fixture) {\n            $isNamespaced = (strpos($fixture, '\\\\') !== false);\n            // replace linux' path slashes to namespace backslashes, in case if $fixture is non-namespaced relative path\n            $fixture = str_replace('/', '\\\\', $fixture);\n            $fullClassName = $isNamespaced ? $fixture : $this->namespace . '\\\\' . $fixture;\n\n            if (class_exists($fullClassName)) {\n                $config[] = $fullClassName;\n            } elseif (class_exists($fullClassName . 'Fixture')) {\n                $config[] = $fullClassName . 'Fixture';\n            } else {\n                throw new Exception('Neither fixture \"' . $fullClassName . '\" nor \"' . $fullClassName . 'Fixture\" was found.');\n            }\n        }\n\n        return $config;\n    }\n\n    /**\n     * Filters fixtures by splitting them in two categories: one that should be applied and not.\n     *\n     * If fixture is prefixed with \"-\", for example \"-User\", that means that fixture should not be loaded,\n     * if it is not prefixed it is considered as one to be loaded. Returns array:\n     *\n     * ```\n     * [\n     *     'apply' => [\n     *         'User',\n     *         ...\n     *     ],\n     *     'except' => [\n     *         'Custom',\n     *         ...\n     *     ],\n     * ]\n     * ```\n     * @param array $fixtures\n     * @return array fixtures array with 'apply' and 'except' elements.\n     */\n    private function filterFixtures($fixtures)\n    {\n        $filtered = [\n            'apply' => [],\n            'except' => [],\n        ];\n\n        foreach ($fixtures as $fixture) {\n            if (mb_strpos($fixture, '-') !== false) {\n                $filtered['except'][] = str_replace('-', '', $fixture);\n            } else {\n                $filtered['apply'][] = $fixture;\n            }\n        }\n\n        return $filtered;\n    }\n\n    /**\n     * Returns fixture path that determined on fixtures namespace.\n     * @throws InvalidConfigException if fixture namespace is invalid\n     * @return string fixture path\n     */\n    private function getFixturePath()\n    {\n        try {\n            return Yii::getAlias('@' . str_replace('\\\\', '/', $this->namespace));\n        } catch (InvalidParamException $e) {\n            throw new InvalidConfigException('Invalid fixture namespace: \"' . $this->namespace . '\". Please, check your FixtureController::namespace parameter');\n        }\n    }\n}\n"
  },
  {
    "path": "framework/console/controllers/HelpController.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\console\\controllers;\n\nuse Yii;\nuse yii\\base\\Application;\nuse yii\\base\\Module;\nuse yii\\console\\Controller;\nuse yii\\console\\Exception;\nuse yii\\console\\ExitCode;\nuse yii\\helpers\\Console;\nuse yii\\helpers\\Inflector;\nuse yii\\console\\Application as ConsoleApplication;\n\n/**\n * Provides help information about console commands.\n *\n * This command displays the available command list in\n * the application or the detailed instructions about using\n * a specific command.\n *\n * This command can be used as follows on command line:\n *\n * ```\n * yii help [command name]\n * ```\n *\n * In the above, if the command name is not provided, all\n * available commands will be displayed.\n *\n * @property-read array $commands All available command names.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of ConsoleApplication = ConsoleApplication\n * @extends Controller<T>\n */\nclass HelpController extends Controller\n{\n    /**\n     * Displays available commands or the detailed information\n     * about a particular command.\n     *\n     * @param string|null $command The name of the command to show help about.\n     * If not provided, all available commands will be displayed.\n     * @return int the exit status\n     * @throws Exception if the command for help is unknown\n     */\n    public function actionIndex($command = null)\n    {\n        if ($command !== null) {\n            $result = Yii::$app->createController($command);\n            if ($result === false) {\n                $name = $this->ansiFormat($command, Console::FG_YELLOW);\n                throw new Exception(\"No help for unknown command \\\"$name\\\".\");\n            }\n\n            list($controller, $actionID) = $result;\n\n            $actions = $this->getActions($controller);\n            if ($actionID !== '' || count($actions) === 1 && $actions[0] === $controller->defaultAction) {\n                $this->getSubCommandHelp($controller, $actionID);\n            } else {\n                $this->getCommandHelp($controller);\n            }\n        } else {\n            $this->getDefaultHelp();\n        }\n\n        return ExitCode::OK;\n    }\n\n    /**\n     * List all available controllers and actions in machine readable format.\n     * This is used for shell completion.\n     * @since 2.0.11\n     */\n    public function actionList()\n    {\n        foreach ($this->getCommandDescriptions() as $command => $description) {\n            $result = Yii::$app->createController($command);\n            /** @var Controller<Application> $controller */\n            list($controller, $actionID) = $result;\n            $actions = $this->getActions($controller);\n            $prefix = $controller->getUniqueId();\n            if ($controller->createAction($controller->defaultAction) !== null) {\n                $this->stdout(\"$prefix\\n\");\n            }\n            foreach ($actions as $action) {\n                $this->stdout(\"$prefix/$action\\n\");\n            }\n        }\n    }\n\n    /**\n     * List all available options for the $action in machine readable format.\n     * This is used for shell completion.\n     *\n     * @param string $action route to action\n     * @since 2.0.11\n     */\n    public function actionListActionOptions($action)\n    {\n        $result = Yii::$app->createController($action);\n\n        if ($result === false || !($result[0] instanceof Controller)) {\n            return;\n        }\n\n        /** @var Controller<Application> $controller */\n        list($controller, $actionID) = $result;\n        $action = $controller->createAction($actionID);\n        if ($action === null) {\n            return;\n        }\n\n        foreach ($controller->getActionArgsHelp($action) as $argument => $help) {\n            $description = preg_replace('~\\R~', '', addcslashes($help['comment'], ':')) ?: $argument;\n            $this->stdout($argument . ':' . $description . \"\\n\");\n        }\n\n        $this->stdout(\"\\n\");\n        foreach ($controller->getActionOptionsHelp($action) as $argument => $help) {\n            $description = preg_replace('~\\R~', '', addcslashes($help['comment'], ':'));\n            $this->stdout('--' . $argument . ($description ? ':' . $description : '') . \"\\n\");\n        }\n    }\n\n    /**\n     * Displays usage information for $action.\n     *\n     * @param string $action route to action\n     * @since 2.0.11\n     */\n    public function actionUsage($action)\n    {\n        $result = Yii::$app->createController($action);\n\n        if ($result === false || !($result[0] instanceof Controller)) {\n            return;\n        }\n\n        /** @var Controller<Application> $controller */\n        list($controller, $actionID) = $result;\n        $action = $controller->createAction($actionID);\n        if ($action === null) {\n            return;\n        }\n\n        $scriptName = $this->getScriptName();\n        if ($action->id === $controller->defaultAction) {\n            $this->stdout($scriptName . ' ' . $this->ansiFormat($controller->getUniqueId(), Console::FG_YELLOW));\n        } else {\n            $this->stdout($scriptName . ' ' . $this->ansiFormat($action->getUniqueId(), Console::FG_YELLOW));\n        }\n\n        foreach ($controller->getActionArgsHelp($action) as $name => $arg) {\n            if ($arg['required']) {\n                $this->stdout(' <' . $name . '>', Console::FG_CYAN);\n            } else {\n                $this->stdout(' [' . $name . ']', Console::FG_CYAN);\n            }\n        }\n\n        $this->stdout(\"\\n\");\n    }\n\n    /**\n     * Returns all available command names.\n     * @return array all available command names\n     */\n    public function getCommands()\n    {\n        $commands = $this->getModuleCommands(Yii::$app);\n        sort($commands);\n        return array_filter(array_unique($commands), function ($command) {\n            $result = Yii::$app->createController($command);\n            if ($result === false || !$result[0] instanceof Controller) {\n                return false;\n            }\n            /** @var Controller<Application> $controller */\n            list($controller, $actionID) = $result;\n            $actions = $this->getActions($controller);\n            return $actions !== [];\n        });\n    }\n\n    /**\n     * Returns an array of commands an their descriptions.\n     * @return array all available commands as keys and their description as values.\n     */\n    protected function getCommandDescriptions()\n    {\n        $descriptions = [];\n        foreach ($this->getCommands() as $command) {\n            $result = Yii::$app->createController($command);\n            /** @var Controller<Application> $controller */\n            list($controller, $actionID) = $result;\n            $descriptions[$command] = $controller->getHelpSummary();\n        }\n\n        return $descriptions;\n    }\n\n    /**\n     * Returns all available actions of the specified controller.\n     * @param Controller $controller the controller instance\n     * @return array all available action IDs.\n     */\n    public function getActions($controller)\n    {\n        $actions = array_keys($controller->actions());\n        $class = new \\ReflectionClass($controller);\n        foreach ($class->getMethods() as $method) {\n            $name = $method->getName();\n            if ($name !== 'actions' && $method->isPublic() && !$method->isStatic() && strncmp($name, 'action', 6) === 0) {\n                $actions[] = $this->camel2id(substr($name, 6));\n            }\n        }\n        sort($actions);\n\n        return array_unique($actions);\n    }\n\n    /**\n     * Returns available commands of a specified module.\n     * @param Module $module the module instance\n     * @return array the available command names\n     */\n    protected function getModuleCommands($module)\n    {\n        $prefix = $module instanceof Application ? '' : $module->getUniqueId() . '/';\n\n        $commands = [];\n        foreach (array_keys($module->controllerMap) as $id) {\n            $commands[] = $prefix . $id;\n        }\n\n        foreach ($module->getModules() as $id => $child) {\n            if (($child = $module->getModule($id)) === null) {\n                continue;\n            }\n            foreach ($this->getModuleCommands($child) as $command) {\n                $commands[] = $command;\n            }\n        }\n\n        $controllerPath = $module->getControllerPath();\n        if (is_dir($controllerPath)) {\n            $iterator = new \\RecursiveIteratorIterator(new \\RecursiveDirectoryIterator($controllerPath, \\RecursiveDirectoryIterator::KEY_AS_PATHNAME));\n            $iterator = new \\RegexIterator($iterator, '/.*Controller\\.php$/', \\RecursiveRegexIterator::GET_MATCH);\n            foreach ($iterator as $matches) {\n                $file = $matches[0];\n                $relativePath = str_replace($controllerPath, '', $file);\n                $class = strtr($relativePath, [\n                    '/' => '\\\\',\n                    '.php' => '',\n                ]);\n                $controllerClass = $module->controllerNamespace . $class;\n                if ($this->validateControllerClass($controllerClass)) {\n                    $dir = ltrim(pathinfo($relativePath, PATHINFO_DIRNAME), '\\\\/');\n\n                    $command = Inflector::camel2id(substr(basename($file), 0, -14), '-', true);\n                    if (!empty($dir)) {\n                        $command = $dir . '/' . $command;\n                    }\n                    $commands[] = $prefix . $command;\n                }\n            }\n        }\n\n        return $commands;\n    }\n\n    /**\n     * Validates if the given class is a valid console controller class.\n     * @param string $controllerClass\n     * @return bool\n     */\n    protected function validateControllerClass($controllerClass)\n    {\n        if (class_exists($controllerClass)) {\n            $class = new \\ReflectionClass($controllerClass);\n            return !$class->isAbstract() && $class->isSubclassOf('yii\\console\\Controller');\n        }\n\n        return false;\n    }\n\n    /**\n     * Displays all available commands.\n     */\n    protected function getDefaultHelp()\n    {\n        $commands = $this->getCommandDescriptions();\n        $this->stdout($this->getDefaultHelpHeader());\n        if (empty($commands)) {\n            $this->stdout(\"\\nNo commands are found.\\n\\n\", Console::BOLD);\n            return;\n        }\n\n        $this->stdout(\"\\nThe following commands are available:\\n\\n\", Console::BOLD);\n        $maxLength = 0;\n        foreach ($commands as $command => $description) {\n            $result = Yii::$app->createController($command);\n            /** @var Controller<Application> $controller */\n            list($controller, $actionID) = $result;\n            $actions = $this->getActions($controller);\n            $prefix = $controller->getUniqueId();\n            foreach ($actions as $action) {\n                $string = $prefix . '/' . $action;\n                if ($action === $controller->defaultAction) {\n                    $string .= ' (default)';\n                }\n                $maxLength = max($maxLength, strlen($string));\n            }\n        }\n        foreach ($commands as $command => $description) {\n            $result = Yii::$app->createController($command);\n            /** @var Controller<Application> $controller */\n            list($controller, $actionID) = $result;\n            $actions = $this->getActions($controller);\n            $this->stdout('- ' . $this->ansiFormat($command, Console::FG_YELLOW));\n            $this->stdout(str_repeat(' ', $maxLength + 4 - strlen($command)));\n            $this->stdout(Console::wrapText($description, $maxLength + 4 + 2), Console::BOLD);\n            $this->stdout(\"\\n\");\n            $prefix = $controller->getUniqueId();\n            foreach ($actions as $action) {\n                $string = '  ' . $prefix . '/' . $action;\n                $this->stdout('  ' . $this->ansiFormat($string, Console::FG_GREEN));\n                if ($action === $controller->defaultAction) {\n                    $string .= ' (default)';\n                    $this->stdout(' (default)', Console::FG_YELLOW);\n                }\n                $summary = $controller->getActionHelpSummary($controller->createAction($action));\n                if ($summary !== '') {\n                    $this->stdout(str_repeat(' ', $maxLength + 4 - strlen($string)));\n                    $this->stdout(Console::wrapText($summary, $maxLength + 4 + 2));\n                }\n                $this->stdout(\"\\n\");\n            }\n            $this->stdout(\"\\n\");\n        }\n        $scriptName = $this->getScriptName();\n        $this->stdout(\"\\nTo see the help of each command, enter:\\n\", Console::BOLD);\n        $this->stdout(\"\\n  $scriptName \" . $this->ansiFormat('help', Console::FG_YELLOW) . ' '\n            . $this->ansiFormat('<command-name>', Console::FG_CYAN) . \"\\n\\n\");\n    }\n\n    /**\n     * Displays the overall information of the command.\n     * @param Controller $controller the controller instance\n     */\n    protected function getCommandHelp($controller)\n    {\n        $controller->color = $this->color;\n\n        $this->stdout(\"\\nDESCRIPTION\\n\", Console::BOLD);\n        $comment = $controller->getHelp();\n        if ($comment !== '') {\n            $this->stdout(\"\\n$comment\\n\\n\");\n        }\n\n        $actions = $this->getActions($controller);\n        if (!empty($actions)) {\n            $this->stdout(\"\\nSUB-COMMANDS\\n\\n\", Console::BOLD);\n            $prefix = $controller->getUniqueId();\n\n            $maxlen = 5;\n            foreach ($actions as $action) {\n                $len = strlen($prefix . '/' . $action) + 2 + ($action === $controller->defaultAction ? 10 : 0);\n                $maxlen = max($maxlen, $len);\n            }\n            foreach ($actions as $action) {\n                $this->stdout('- ' . $this->ansiFormat($prefix . '/' . $action, Console::FG_YELLOW));\n                $len = strlen($prefix . '/' . $action) + 2;\n                if ($action === $controller->defaultAction) {\n                    $this->stdout(' (default)', Console::FG_GREEN);\n                    $len += 10;\n                }\n                $summary = $controller->getActionHelpSummary($controller->createAction($action));\n                if ($summary !== '') {\n                    $this->stdout(str_repeat(' ', $maxlen - $len + 2) . Console::wrapText($summary, $maxlen + 2));\n                }\n                $this->stdout(\"\\n\");\n            }\n            $scriptName = $this->getScriptName();\n            $this->stdout(\"\\nTo see the detailed information about individual sub-commands, enter:\\n\");\n            $this->stdout(\"\\n  $scriptName \" . $this->ansiFormat('help', Console::FG_YELLOW) . ' '\n                . $this->ansiFormat('<sub-command>', Console::FG_CYAN) . \"\\n\\n\");\n        }\n    }\n\n    /**\n     * Displays the detailed information of a command action.\n     * @param Controller $controller the controller instance\n     * @param string $actionID action ID\n     * @throws Exception if the action does not exist\n     */\n    protected function getSubCommandHelp($controller, $actionID)\n    {\n        $action = $controller->createAction($actionID);\n        if ($action === null) {\n            $name = $this->ansiFormat(rtrim($controller->getUniqueId() . '/' . $actionID, '/'), Console::FG_YELLOW);\n            throw new Exception(\"No help for unknown sub-command \\\"$name\\\".\");\n        }\n\n        $description = $controller->getActionHelp($action);\n        if ($description !== '') {\n            $this->stdout(\"\\nDESCRIPTION\\n\", Console::BOLD);\n            $this->stdout(\"\\n$description\\n\\n\");\n        }\n\n        $this->stdout(\"\\nUSAGE\\n\\n\", Console::BOLD);\n        $scriptName = $this->getScriptName();\n        if ($action->id === $controller->defaultAction) {\n            $this->stdout($scriptName . ' ' . $this->ansiFormat($controller->getUniqueId(), Console::FG_YELLOW));\n        } else {\n            $this->stdout($scriptName . ' ' . $this->ansiFormat($action->getUniqueId(), Console::FG_YELLOW));\n        }\n\n        $args = $controller->getActionArgsHelp($action);\n        foreach ($args as $name => $arg) {\n            if ($arg['required']) {\n                $this->stdout(' <' . $name . '>', Console::FG_CYAN);\n            } else {\n                $this->stdout(' [' . $name . ']', Console::FG_CYAN);\n            }\n        }\n\n        $options = $controller->getActionOptionsHelp($action);\n        $options[\\yii\\console\\Application::OPTION_APPCONFIG] = [\n            'type' => 'string',\n            'default' => null,\n            'comment' => \"custom application configuration file path.\\nIf not set, default application configuration is used.\",\n        ];\n        ksort($options);\n\n        $this->stdout(' [...options...]', Console::FG_RED);\n        $this->stdout(\"\\n\\n\");\n\n        if (!empty($args)) {\n            foreach ($args as $name => $arg) {\n                $this->stdout($this->formatOptionHelp(\n                    '- ' . $this->ansiFormat($name, Console::FG_CYAN),\n                    $arg['required'],\n                    $arg['type'],\n                    $arg['default'],\n                    $arg['comment']\n                ) . \"\\n\\n\");\n            }\n        }\n\n        $this->stdout(\"\\nOPTIONS\\n\\n\", Console::BOLD);\n        foreach ($options as $name => $option) {\n            $this->stdout($this->formatOptionHelp(\n                $this->ansiFormat(\n                    '--' . $name . $this->formatOptionAliases($controller, $name),\n                    Console::FG_RED,\n                    empty($option['required']) ? Console::FG_RED : Console::BOLD\n                ),\n                !empty($option['required']),\n                $option['type'],\n                $option['default'],\n                $option['comment']\n            ) . \"\\n\\n\");\n        }\n    }\n\n    /**\n     * Generates a well-formed string for an argument or option.\n     * @param string $name the name of the argument or option\n     * @param bool $required whether the argument is required\n     * @param string $type the type of the option or argument\n     * @param mixed $defaultValue the default value of the option or argument\n     * @param string $comment comment about the option or argument\n     * @return string the formatted string for the argument or option\n     */\n    protected function formatOptionHelp($name, $required, $type, $defaultValue, $comment)\n    {\n        $comment = trim((string)$comment);\n        $type = trim((string)$type);\n        if (strncmp($type, 'bool', 4) === 0) {\n            $type = 'boolean, 0 or 1';\n        }\n\n        if ($defaultValue !== null && !is_array($defaultValue)) {\n            if ($type === null) {\n                $type = gettype($defaultValue);\n            }\n            if (is_bool($defaultValue)) {\n                // show as integer to avoid confusion\n                $defaultValue = (int) $defaultValue;\n            }\n            if (is_string($defaultValue)) {\n                $defaultValue = \"'\" . $defaultValue . \"'\";\n            } else {\n                $defaultValue = var_export($defaultValue, true);\n            }\n            $doc = \"$type (defaults to $defaultValue)\";\n        } else {\n            $doc = $type;\n        }\n\n        if ($doc === '') {\n            $doc = $comment;\n        } elseif ($comment !== '') {\n            $doc .= \"\\n\" . preg_replace('/^/m', '  ', $comment);\n        }\n\n        $name = $required ? \"$name (required)\" : $name;\n\n        return $doc === '' ? $name : \"$name: $doc\";\n    }\n\n    /**\n     * @param Controller $controller the controller instance\n     * @param string $option the option name\n     * @return string the formatted string for the alias argument or option\n     * @since 2.0.8\n     */\n    protected function formatOptionAliases($controller, $option)\n    {\n        foreach ($controller->optionAliases() as $name => $value) {\n            if (Inflector::camel2id($value, '-', true) === $option) {\n                return ', -' . $name;\n            }\n        }\n\n        return '';\n    }\n\n    /**\n     * @return string the name of the cli script currently running.\n     */\n    protected function getScriptName()\n    {\n        return basename(Yii::$app->request->scriptFile);\n    }\n\n    /**\n     * Return a default help header.\n     * @return string default help header.\n     * @since 2.0.11\n     */\n    protected function getDefaultHelpHeader()\n    {\n        return \"\\nThis is Yii version \" . \\Yii::getVersion() . \".\\n\";\n    }\n\n    /**\n     * Converts a CamelCase action name into an ID in lowercase.\n     * Words in the ID are concatenated using the specified character '-'.\n     * For example, 'CreateUser' will be converted to 'create-user'.\n     * @param string $name the string to be converted\n     * @return string the resulting ID\n     */\n    private function camel2id($name)\n    {\n        return mb_strtolower(trim(preg_replace('/\\p{Lu}/u', '-\\0', $name), '-'), 'UTF-8');\n    }\n}\n"
  },
  {
    "path": "framework/console/controllers/MessageController.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\console\\controllers;\n\nuse Yii;\nuse yii\\console\\Application;\nuse yii\\console\\Controller;\nuse yii\\console\\Exception;\nuse yii\\console\\ExitCode;\nuse yii\\db\\Connection;\nuse yii\\db\\Query;\nuse yii\\di\\Instance;\nuse yii\\helpers\\Console;\nuse yii\\helpers\\FileHelper;\nuse yii\\helpers\\VarDumper;\nuse yii\\i18n\\GettextPoFile;\n\n/**\n * Extracts messages to be translated from source files.\n *\n * The extracted messages can be saved the following depending on `format`\n * setting in config file:\n *\n * - PHP message source files.\n * - \".po\" files.\n * - Database.\n *\n * Usage:\n * 1. Create a configuration file using the 'message/config' command:\n *    yii message/config /path/to/myapp/messages/config.php\n * 2. Edit the created config file, adjusting it for your web application needs.\n * 3. Run the 'message/extract' command, using created config:\n *    yii message /path/to/myapp/messages/config.php\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Application = Application\n * @extends Controller<T>\n */\nclass MessageController extends Controller\n{\n    /**\n     * @var string controller default action ID.\n     */\n    public $defaultAction = 'extract';\n    /**\n     * @var string required, root directory of all source files.\n     */\n    public $sourcePath = '@yii';\n    /**\n     * @var string required, root directory containing message translations.\n     */\n    public $messagePath = '@yii/messages';\n    /**\n     * @var array required, list of language codes that the extracted messages\n     * should be translated to. For example, ['zh-CN', 'de'].\n     */\n    public $languages = [];\n    /**\n     * @var string|string[] the name of the function for translating messages.\n     * This is used as a mark to find the messages to be translated.\n     * You may use a string for single function name or an array for multiple function names.\n     */\n    public $translator = ['Yii::t', '\\Yii::t'];\n    /**\n     * @var bool whether to sort messages by keys when merging new messages\n     * with the existing ones. Defaults to false, which means the new (untranslated)\n     * messages will be separated from the old (translated) ones.\n     */\n    public $sort = false;\n    /**\n     * @var bool whether the message file should be overwritten with the merged messages\n     */\n    public $overwrite = true;\n    /**\n     * @var bool whether to remove messages that no longer appear in the source code.\n     * Defaults to false, which means these messages will NOT be removed.\n     */\n    public $removeUnused = false;\n    /**\n     * @var bool whether to mark messages that no longer appear in the source code.\n     * Defaults to true, which means each of these messages will be enclosed with a pair of '@@' marks.\n     */\n    public $markUnused = true;\n    /**\n     * @var array|null list of patterns that specify which files/directories should NOT be processed.\n     * If empty or not set, all files/directories will be processed.\n     * See helpers/FileHelper::findFiles() description for pattern matching rules.\n     * If a file/directory matches both a pattern in \"only\" and \"except\", it will NOT be processed.\n     */\n    public $except = [\n        '.*',\n        '/.*',\n        '/messages',\n        '/tests',\n        '/runtime',\n        '/vendor',\n        '/BaseYii.php', // contains examples about Yii::t()\n    ];\n    /**\n     * @var array|null list of patterns that specify which files (not directories) should be processed.\n     * If empty or not set, all files will be processed.\n     * See helpers/FileHelper::findFiles() description for pattern matching rules.\n     * If a file/directory matches both a pattern in \"only\" and \"except\", it will NOT be processed.\n     */\n    public $only = ['*.php'];\n    /**\n     * @var string generated file format. Can be \"php\", \"db\", \"po\" or \"pot\".\n     */\n    public $format = 'php';\n    /**\n     * @var string connection component ID for \"db\" format.\n     */\n    public $db = 'db';\n    /**\n     * @var string custom name for source message table for \"db\" format.\n     */\n    public $sourceMessageTable = '{{%source_message}}';\n    /**\n     * @var string custom name for translation message table for \"db\" format.\n     */\n    public $messageTable = '{{%message}}';\n    /**\n     * @var string name of the file that will be used for translations for \"po\" format.\n     */\n    public $catalog = 'messages';\n    /**\n     * @var array message categories to ignore. For example, 'yii', 'app*', 'widgets/menu', etc.\n     * @see isCategoryIgnored\n     */\n    public $ignoreCategories = [];\n    /**\n     * @var string File header in generated PHP file with messages. This property is used only if [[$format]] is \"php\".\n     * @since 2.0.13\n     */\n    public $phpFileHeader = '';\n    /**\n     * @var string|null DocBlock used for messages array in generated PHP file. If `null`, default DocBlock will be used.\n     * This property is used only if [[$format]] is \"php\".\n     * @since 2.0.13\n     */\n    public $phpDocBlock;\n\n    /**\n     * @var array Config for messages extraction.\n     * @see actionExtract()\n     * @see initConfig()\n     * @since 2.0.13\n     */\n    protected $config;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function options($actionID)\n    {\n        return array_merge(parent::options($actionID), [\n            'sourcePath',\n            'messagePath',\n            'languages',\n            'translator',\n            'sort',\n            'overwrite',\n            'removeUnused',\n            'markUnused',\n            'except',\n            'only',\n            'format',\n            'db',\n            'sourceMessageTable',\n            'messageTable',\n            'catalog',\n            'ignoreCategories',\n            'phpFileHeader',\n            'phpDocBlock',\n        ]);\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.8\n     */\n    public function optionAliases()\n    {\n        return array_merge(parent::optionAliases(), [\n            'c' => 'catalog',\n            'e' => 'except',\n            'f' => 'format',\n            'i' => 'ignoreCategories',\n            'l' => 'languages',\n            'u' => 'markUnused',\n            'p' => 'messagePath',\n            'o' => 'only',\n            'w' => 'overwrite',\n            'S' => 'sort',\n            't' => 'translator',\n            'm' => 'sourceMessageTable',\n            's' => 'sourcePath',\n            'r' => 'removeUnused',\n        ]);\n    }\n\n    /**\n     * Creates a configuration file for the \"extract\" command using command line options specified.\n     *\n     * The generated configuration file contains parameters required\n     * for source code messages extraction.\n     * You may use this configuration file with the \"extract\" command.\n     *\n     * @param string $filePath output file name or alias.\n     * @return int CLI exit code\n     * @throws Exception on failure.\n     */\n    public function actionConfig($filePath)\n    {\n        $filePath = Yii::getAlias($filePath);\n        $dir = dirname($filePath);\n\n        if (file_exists($filePath)) {\n            if (!$this->confirm(\"File '{$filePath}' already exists. Do you wish to overwrite it?\")) {\n                return ExitCode::OK;\n            }\n        }\n\n        $array = VarDumper::export($this->getOptionValues($this->action->id));\n        $content = <<<EOD\n<?php\n/**\n * Configuration file for 'yii {$this->id}/{$this->defaultAction}' command.\n *\n * This file is automatically generated by 'yii {$this->id}/{$this->action->id}' command.\n * It contains parameters for source code messages extraction.\n * You may modify this file to suit your needs.\n *\n * You can use 'yii {$this->id}/{$this->action->id}-template' command to create\n * template configuration file with detailed description for each parameter.\n */\nreturn $array;\n\nEOD;\n\n        if (FileHelper::createDirectory($dir) === false || file_put_contents($filePath, $content, LOCK_EX) === false) {\n            $this->stdout(\"Configuration file was NOT created: '{$filePath}'.\\n\\n\", Console::FG_RED);\n            return ExitCode::UNSPECIFIED_ERROR;\n        }\n\n        $this->stdout(\"Configuration file created: '{$filePath}'.\\n\\n\", Console::FG_GREEN);\n        return ExitCode::OK;\n    }\n\n    /**\n     * Creates a configuration file template for the \"extract\" command.\n     *\n     * The created configuration file contains detailed instructions on\n     * how to customize it to fit for your needs. After customization,\n     * you may use this configuration file with the \"extract\" command.\n     *\n     * @param string $filePath output file name or alias.\n     * @return int CLI exit code\n     * @throws Exception on failure.\n     */\n    public function actionConfigTemplate($filePath)\n    {\n        $filePath = Yii::getAlias($filePath);\n\n        if (file_exists($filePath)) {\n            if (!$this->confirm(\"File '{$filePath}' already exists. Do you wish to overwrite it?\")) {\n                return ExitCode::OK;\n            }\n        }\n\n        if (!copy(Yii::getAlias('@yii/views/messageConfig.php'), $filePath)) {\n            $this->stdout(\"Configuration file template was NOT created at '{$filePath}'.\\n\\n\", Console::FG_RED);\n            return ExitCode::UNSPECIFIED_ERROR;\n        }\n\n        $this->stdout(\"Configuration file template created at '{$filePath}'.\\n\\n\", Console::FG_GREEN);\n        return ExitCode::OK;\n    }\n\n    /**\n     * Extracts messages to be translated from source code.\n     *\n     * This command will search through source code files and extract\n     * messages that need to be translated in different languages.\n     *\n     * @param string|null $configFile the path or alias of the configuration file.\n     * You may use the \"yii message/config\" command to generate\n     * this file and then customize it for your needs.\n     * @throws Exception on failure.\n     */\n    public function actionExtract($configFile = null)\n    {\n        $this->initConfig($configFile);\n\n        $files = FileHelper::findFiles(realpath($this->config['sourcePath']), $this->config);\n\n        $messages = [];\n        foreach ($files as $file) {\n            $messages = array_merge_recursive($messages, $this->extractMessages($file, $this->config['translator'], $this->config['ignoreCategories']));\n        }\n\n        $catalog = isset($this->config['catalog']) ? $this->config['catalog'] : 'messages';\n\n        if (in_array($this->config['format'], ['php', 'po'])) {\n            foreach ($this->config['languages'] as $language) {\n                $dir = $this->config['messagePath'] . DIRECTORY_SEPARATOR . $language;\n                if (!is_dir($dir) && !@mkdir($dir)) {\n                    throw new Exception(\"Directory '{$dir}' can not be created.\");\n                }\n                if ($this->config['format'] === 'po') {\n                    $this->saveMessagesToPO($messages, $dir, $this->config['overwrite'], $this->config['removeUnused'], $this->config['sort'], $catalog, $this->config['markUnused']);\n                } else {\n                    $this->saveMessagesToPHP($messages, $dir, $this->config['overwrite'], $this->config['removeUnused'], $this->config['sort'], $this->config['markUnused']);\n                }\n            }\n        } elseif ($this->config['format'] === 'db') {\n            /** @var Connection $db */\n            $db = Instance::ensure($this->config['db'], Connection::className());\n            $sourceMessageTable = isset($this->config['sourceMessageTable']) ? $this->config['sourceMessageTable'] : '{{%source_message}}';\n            $messageTable = isset($this->config['messageTable']) ? $this->config['messageTable'] : '{{%message}}';\n            $this->saveMessagesToDb(\n                $messages,\n                $db,\n                $sourceMessageTable,\n                $messageTable,\n                $this->config['removeUnused'],\n                $this->config['languages'],\n                $this->config['markUnused']\n            );\n        } elseif ($this->config['format'] === 'pot') {\n            $this->saveMessagesToPOT($messages, $this->config['messagePath'], $catalog);\n        }\n    }\n\n    /**\n     * Saves messages to database.\n     *\n     * @param array $messages\n     * @param Connection $db\n     * @param string $sourceMessageTable\n     * @param string $messageTable\n     * @param bool $removeUnused\n     * @param array $languages\n     * @param bool $markUnused\n     */\n    protected function saveMessagesToDb($messages, $db, $sourceMessageTable, $messageTable, $removeUnused, $languages, $markUnused)\n    {\n        $currentMessages = [];\n        $rows = (new Query())->select(['id', 'category', 'message'])->from($sourceMessageTable)->all($db);\n        foreach ($rows as $row) {\n            $currentMessages[$row['category']][$row['id']] = $row['message'];\n        }\n\n        $new = [];\n        $obsolete = [];\n\n        foreach ($messages as $category => $msgs) {\n            $msgs = array_unique($msgs);\n\n            if (isset($currentMessages[$category])) {\n                $new[$category] = array_diff($msgs, $currentMessages[$category]);\n                // obsolete messages per category\n                $obsolete += array_diff($currentMessages[$category], $msgs);\n            } else {\n                $new[$category] = $msgs;\n            }\n        }\n\n        // obsolete categories\n        foreach (array_diff(array_keys($currentMessages), array_keys($messages)) as $category) {\n            $obsolete += $currentMessages[$category];\n        }\n\n        if (!$removeUnused) {\n            foreach ($obsolete as $pk => $msg) {\n                // skip already marked unused\n                if (strncmp($msg, '@@', 2) === 0 && substr($msg, -2) === '@@') {\n                    unset($obsolete[$pk]);\n                }\n            }\n        }\n\n        $this->stdout('Inserting new messages...');\n        $insertCount = 0;\n\n        foreach ($new as $category => $msgs) {\n            foreach ($msgs as $msg) {\n                $insertCount++;\n                $db->schema->insert($sourceMessageTable, ['category' => $category, 'message' => $msg]);\n            }\n        }\n\n        $this->stdout($insertCount ? \"{$insertCount} saved.\\n\" : \"Nothing to save.\\n\");\n\n        $this->stdout($removeUnused ? 'Deleting obsoleted messages...' : 'Updating obsoleted messages...');\n\n        if (empty($obsolete)) {\n            $this->stdout(\"Nothing obsoleted...skipped.\\n\");\n        }\n\n        if ($obsolete) {\n            if ($removeUnused) {\n                $affected = $db->createCommand()\n                   ->delete($sourceMessageTable, ['in', 'id', array_keys($obsolete)])\n                   ->execute();\n                $this->stdout(\"{$affected} deleted.\\n\");\n            } elseif ($markUnused) {\n                $marked = 0;\n                $rows = (new Query())\n                    ->select(['id', 'message'])\n                    ->from($sourceMessageTable)\n                    ->where(['in', 'id', array_keys($obsolete)])\n                    ->all($db);\n\n                foreach ($rows as $row) {\n                    $marked++;\n                    $db->createCommand()->update(\n                        $sourceMessageTable,\n                        ['message' => '@@' . $row['message'] . '@@'],\n                        ['id' => $row['id']]\n                    )->execute();\n                }\n                $this->stdout(\"{$marked} updated.\\n\");\n            } else {\n                $this->stdout(\"kept untouched.\\n\");\n            }\n        }\n\n        // get fresh message id list\n        $freshMessagesIds = [];\n        $rows = (new Query())->select(['id'])->from($sourceMessageTable)->all($db);\n        foreach ($rows as $row) {\n            $freshMessagesIds[] = $row['id'];\n        }\n\n        $this->stdout('Generating missing rows...');\n        $generatedMissingRows = [];\n\n        foreach ($languages as $language) {\n            $count = 0;\n\n            // get list of ids of translations for this language\n            $msgRowsIds = [];\n            $msgRows = (new Query())->select(['id'])->from($messageTable)->where([\n                'language' => $language,\n            ])->all($db);\n            foreach ($msgRows as $row) {\n                $msgRowsIds[] = $row['id'];\n            }\n\n            // insert missing\n            foreach ($freshMessagesIds as $id) {\n                if (!in_array($id, $msgRowsIds)) {\n                    $db->createCommand()\n                       ->insert($messageTable, ['id' => $id, 'language' => $language])\n                       ->execute();\n                    $count++;\n                }\n            }\n            if ($count) {\n                $generatedMissingRows[] = \"{$count} for {$language}\";\n            }\n        }\n\n        $this->stdout($generatedMissingRows ? implode(', ', $generatedMissingRows) . \".\\n\" : \"Nothing to do.\\n\");\n\n        $this->stdout('Dropping unused languages...');\n        $droppedLanguages = [];\n\n        $currentLanguages = [];\n        $rows = (new Query())->select(['language'])->from($messageTable)->groupBy('language')->all($db);\n        foreach ($rows as $row) {\n            $currentLanguages[] = $row['language'];\n        }\n\n        foreach ($currentLanguages as $currentLanguage) {\n            if (!in_array($currentLanguage, $languages)) {\n                $deleted = $db->createCommand()->delete($messageTable, 'language=:language', [\n                    'language' => $currentLanguage,\n                ])->execute();\n                $droppedLanguages[] = \"removed {$deleted} rows for $currentLanguage\";\n            }\n        }\n\n        $this->stdout($droppedLanguages ? implode(', ', $droppedLanguages) . \".\\n\" : \"Nothing to do.\\n\");\n    }\n\n    /**\n     * Extracts messages from a file.\n     *\n     * @param string $fileName name of the file to extract messages from\n     * @param string $translator name of the function used to translate messages\n     * @param array $ignoreCategories message categories to ignore.\n     * This parameter is available since version 2.0.4.\n     * @return array\n     */\n    protected function extractMessages($fileName, $translator, $ignoreCategories = [])\n    {\n        $this->stdout('Extracting messages from ');\n        $this->stdout($fileName, Console::FG_CYAN);\n        $this->stdout(\"...\\n\");\n\n        $subject = file_get_contents($fileName);\n        $messages = [];\n        $tokens = token_get_all($subject);\n        foreach ((array) $translator as $currentTranslator) {\n            $translatorTokens = token_get_all('<?php ' . $currentTranslator);\n            array_shift($translatorTokens);\n            $messages = array_merge_recursive($messages, $this->extractMessagesFromTokens($tokens, $translatorTokens, $ignoreCategories));\n        }\n\n        $this->stdout(\"\\n\");\n\n        return $messages;\n    }\n\n    /**\n     * Extracts messages from a parsed PHP tokens list.\n     * @param array $tokens tokens to be processed.\n     * @param array $translatorTokens translator tokens.\n     * @param array $ignoreCategories message categories to ignore.\n     * @return array messages.\n     */\n    protected function extractMessagesFromTokens(array $tokens, array $translatorTokens, array $ignoreCategories)\n    {\n        $messages = [];\n        $translatorTokensCount = count($translatorTokens);\n        $matchedTokensCount = 0;\n        $buffer = [];\n        $pendingParenthesisCount = 0;\n\n        foreach ($tokens as $tokenIndex => $token) {\n            // finding out translator call\n            if ($matchedTokensCount < $translatorTokensCount) {\n                if ($this->tokensEqual($token, $translatorTokens[$matchedTokensCount])) {\n                    $matchedTokensCount++;\n                } else {\n                    $matchedTokensCount = 0;\n                }\n            } elseif ($matchedTokensCount === $translatorTokensCount) {\n                // translator found\n\n                // end of function call\n                if ($this->tokensEqual(')', $token)) {\n                    $pendingParenthesisCount--;\n\n                    if ($pendingParenthesisCount === 0) {\n                        // end of translator call or end of something that we can't extract\n                        if (isset($buffer[0][0], $buffer[1], $buffer[2][0]) && $buffer[0][0] === T_CONSTANT_ENCAPSED_STRING && $buffer[1] === ',' && $buffer[2][0] === T_CONSTANT_ENCAPSED_STRING) {\n                            // is valid call we can extract\n                            $category = stripcslashes($buffer[0][1]);\n                            $category = mb_substr($category, 1, -1);\n\n                            if (!$this->isCategoryIgnored($category, $ignoreCategories)) {\n                                $fullMessage = mb_substr($buffer[2][1], 1, -1);\n                                $i = 3;\n                                while ($i < count($buffer) - 1 && !is_array($buffer[$i]) && $buffer[$i] === '.') {\n                                    if (!is_array($buffer[$i + 1]) || $buffer[$i + 1][0] !== T_CONSTANT_ENCAPSED_STRING) {\n                                        // invalid call or dynamic call we can't extract\n                                        $line = Console::ansiFormat($this->getLine($buffer), [Console::FG_CYAN]);\n                                        $skipping = Console::ansiFormat('Skipping line', [Console::FG_YELLOW]);\n                                        $this->stdout(\"$skipping $line. Make sure both category and message are static strings.\\n\");\n\n                                        $fullMessage = null;\n                                        break;\n                                    }\n                                    $fullMessage .= mb_substr($buffer[$i + 1][1], 1, -1);\n                                    $i += 2;\n                                }\n\n                                if ($fullMessage !== null) {\n                                    $message = stripcslashes($fullMessage);\n                                    $messages[$category][] = $message;\n                                }\n                            }\n\n                            $nestedTokens = array_slice($buffer, 3);\n                            if (count($nestedTokens) > $translatorTokensCount) {\n                                // search for possible nested translator calls\n                                $messages = array_merge_recursive($messages, $this->extractMessagesFromTokens($nestedTokens, $translatorTokens, $ignoreCategories));\n                            }\n                        } else {\n                            // invalid call or dynamic call we can't extract\n                            $line = Console::ansiFormat($this->getLine($buffer), [Console::FG_CYAN]);\n                            $skipping = Console::ansiFormat('Skipping line', [Console::FG_YELLOW]);\n                            $this->stdout(\"$skipping $line. Make sure both category and message are static strings.\\n\");\n                        }\n\n                        // prepare for the next match\n                        $matchedTokensCount = 0;\n                        $pendingParenthesisCount = 0;\n                        $buffer = [];\n                    } else {\n                        $buffer[] = $token;\n                    }\n                } elseif ($this->tokensEqual('(', $token)) {\n                    // count beginning of function call, skipping translator beginning\n\n                    // If we are not yet inside the translator, make sure that it's beginning of the real translator.\n                    // See https://github.com/yiisoft/yii2/issues/16828\n                    if ($pendingParenthesisCount === 0) {\n                        $previousTokenIndex = $tokenIndex - $matchedTokensCount - 1;\n                        if (is_array($tokens[$previousTokenIndex])) {\n                            $previousToken = $tokens[$previousTokenIndex][0];\n                            if (in_array($previousToken, [T_OBJECT_OPERATOR, T_PAAMAYIM_NEKUDOTAYIM], true)) {\n                                $matchedTokensCount = 0;\n                                continue;\n                            }\n                        }\n                    }\n\n                    if ($pendingParenthesisCount > 0) {\n                        $buffer[] = $token;\n                    }\n                    $pendingParenthesisCount++;\n                } elseif (isset($token[0]) && !in_array($token[0], [T_WHITESPACE, T_COMMENT])) {\n                    // ignore comments and whitespaces\n                    $buffer[] = $token;\n                }\n            }\n        }\n\n        return $messages;\n    }\n\n    /**\n     * The method checks, whether the $category is ignored according to $ignoreCategories array.\n     *\n     * Examples:\n     *\n     * - `myapp` - will be ignored only `myapp` category;\n     * - `myapp*` - will be ignored by all categories beginning with `myapp` (`myapp`, `myapplication`, `myapprove`, `myapp/widgets`, `myapp.widgets`, etc).\n     *\n     * @param string $category category that is checked\n     * @param array $ignoreCategories message categories to ignore.\n     * @return bool\n     * @since 2.0.7\n     */\n    protected function isCategoryIgnored($category, array $ignoreCategories)\n    {\n        if (!empty($ignoreCategories)) {\n            if (in_array($category, $ignoreCategories, true)) {\n                return true;\n            }\n            foreach ($ignoreCategories as $pattern) {\n                if (strpos($pattern, '*') > 0 && strpos($category, rtrim($pattern, '*')) === 0) {\n                    return true;\n                }\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * Finds out if two PHP tokens are equal.\n     *\n     * @param array|string $a\n     * @param array|string $b\n     * @return bool\n     * @since 2.0.1\n     */\n    protected function tokensEqual($a, $b)\n    {\n        if (is_string($a) && is_string($b)) {\n            return $a === $b;\n        }\n        if (isset($a[0], $a[1], $b[0], $b[1])) {\n            return $a[0] === $b[0] && $a[1] == $b[1];\n        }\n\n        return false;\n    }\n\n    /**\n     * Finds out a line of the first non-char PHP token found.\n     *\n     * @param array $tokens\n     * @return int|string\n     * @since 2.0.1\n     */\n    protected function getLine($tokens)\n    {\n        foreach ($tokens as $token) {\n            if (isset($token[2])) {\n                return $token[2];\n            }\n        }\n\n        return 'unknown';\n    }\n\n    /**\n     * Writes messages into PHP files.\n     *\n     * @param array $messages\n     * @param string $dirName name of the directory to write to\n     * @param bool $overwrite if existing file should be overwritten without backup\n     * @param bool $removeUnused if obsolete translations should be removed\n     * @param bool $sort if translations should be sorted\n     * @param bool $markUnused if obsolete translations should be marked\n     */\n    protected function saveMessagesToPHP($messages, $dirName, $overwrite, $removeUnused, $sort, $markUnused)\n    {\n        foreach ($messages as $category => $msgs) {\n            $file = str_replace('\\\\', '/', \"$dirName/$category.php\");\n            $path = dirname($file);\n            FileHelper::createDirectory($path);\n            $msgs = array_values(array_unique($msgs));\n            $coloredFileName = Console::ansiFormat($file, [Console::FG_CYAN]);\n            $this->stdout(\"Saving messages to $coloredFileName...\\n\");\n            $this->saveMessagesCategoryToPHP($msgs, $file, $overwrite, $removeUnused, $sort, $category, $markUnused);\n        }\n\n        if ($removeUnused) {\n            $this->deleteUnusedPhpMessageFiles(array_keys($messages), $dirName);\n        }\n    }\n\n    /**\n     * Writes category messages into PHP file.\n     *\n     * @param array $messages\n     * @param string $fileName name of the file to write to\n     * @param bool $overwrite if existing file should be overwritten without backup\n     * @param bool $removeUnused if obsolete translations should be removed\n     * @param bool $sort if translations should be sorted\n     * @param string $category message category\n     * @param bool $markUnused if obsolete translations should be marked\n     * @return int exit code\n     */\n    protected function saveMessagesCategoryToPHP($messages, $fileName, $overwrite, $removeUnused, $sort, $category, $markUnused)\n    {\n        if (is_file($fileName)) {\n            $rawExistingMessages = require $fileName;\n            $existingMessages = $rawExistingMessages;\n            sort($messages);\n            ksort($existingMessages);\n            if (array_keys($existingMessages) === $messages && (!$sort || array_keys($rawExistingMessages) === $messages)) {\n                $this->stdout(\"Nothing new in \\\"$category\\\" category... Nothing to save.\\n\\n\", Console::FG_GREEN);\n                return ExitCode::OK;\n            }\n            unset($rawExistingMessages);\n            $merged = [];\n            $untranslated = [];\n            foreach ($messages as $message) {\n                if (array_key_exists($message, $existingMessages) && $existingMessages[$message] !== '') {\n                    $merged[$message] = $existingMessages[$message];\n                } else {\n                    $untranslated[] = $message;\n                }\n            }\n            ksort($merged);\n            sort($untranslated);\n            $todo = [];\n            foreach ($untranslated as $message) {\n                $todo[$message] = '';\n            }\n            ksort($existingMessages);\n            foreach ($existingMessages as $message => $translation) {\n                if (!$removeUnused && !isset($merged[$message]) && !isset($todo[$message])) {\n                    if (!$markUnused || (!empty($translation) && (strncmp($translation, '@@', 2) === 0 && substr_compare($translation, '@@', -2, 2) === 0))) {\n                        $todo[$message] = $translation;\n                    } else {\n                        $todo[$message] = '@@' . $translation . '@@';\n                    }\n                }\n            }\n            $merged = array_merge($merged, $todo);\n            if ($sort) {\n                ksort($merged);\n            }\n            if (false === $overwrite) {\n                $fileName .= '.merged';\n            }\n            $this->stdout(\"Translation merged.\\n\");\n        } else {\n            $merged = [];\n            foreach ($messages as $message) {\n                $merged[$message] = '';\n            }\n            ksort($merged);\n        }\n\n        $array = VarDumper::export($merged);\n        $content = <<<EOD\n<?php\n{$this->config['phpFileHeader']}{$this->config['phpDocBlock']}\nreturn $array;\n\nEOD;\n\n        if (file_put_contents($fileName, $content, LOCK_EX) === false) {\n            $this->stdout(\"Translation was NOT saved.\\n\\n\", Console::FG_RED);\n            return ExitCode::UNSPECIFIED_ERROR;\n        }\n\n        $this->stdout(\"Translation saved.\\n\\n\", Console::FG_GREEN);\n        return ExitCode::OK;\n    }\n\n    /**\n     * Writes messages into PO file.\n     *\n     * @param array $messages\n     * @param string $dirName name of the directory to write to\n     * @param bool $overwrite if existing file should be overwritten without backup\n     * @param bool $removeUnused if obsolete translations should be removed\n     * @param bool $sort if translations should be sorted\n     * @param string $catalog message catalog\n     * @param bool $markUnused if obsolete translations should be marked\n     */\n    protected function saveMessagesToPO($messages, $dirName, $overwrite, $removeUnused, $sort, $catalog, $markUnused)\n    {\n        $file = str_replace('\\\\', '/', \"$dirName/$catalog.po\");\n        FileHelper::createDirectory(dirname($file));\n        $this->stdout(\"Saving messages to $file...\\n\");\n\n        $poFile = new GettextPoFile();\n\n        $merged = [];\n        $todos = [];\n\n        $hasSomethingToWrite = false;\n        foreach ($messages as $category => $msgs) {\n            $notTranslatedYet = [];\n            $msgs = array_values(array_unique($msgs));\n\n            if (is_file($file)) {\n                $existingMessages = $poFile->load($file, $category);\n\n                sort($msgs);\n                ksort($existingMessages);\n                if (array_keys($existingMessages) == $msgs) {\n                    $this->stdout(\"Nothing new in \\\"$category\\\" category...\\n\");\n\n                    sort($msgs);\n                    foreach ($msgs as $message) {\n                        $merged[$category . chr(4) . $message] = $existingMessages[$message];\n                    }\n                    ksort($merged);\n                    continue;\n                }\n\n                // merge existing message translations with new message translations\n                foreach ($msgs as $message) {\n                    if (array_key_exists($message, $existingMessages) && $existingMessages[$message] !== '') {\n                        $merged[$category . chr(4) . $message] = $existingMessages[$message];\n                    } else {\n                        $notTranslatedYet[] = $message;\n                    }\n                }\n                ksort($merged);\n                sort($notTranslatedYet);\n\n                // collect not yet translated messages\n                foreach ($notTranslatedYet as $message) {\n                    $todos[$category . chr(4) . $message] = '';\n                }\n\n                // add obsolete unused messages\n                foreach ($existingMessages as $message => $translation) {\n                    if (!$removeUnused && !isset($merged[$category . chr(4) . $message]) && !isset($todos[$category . chr(4) . $message])) {\n                        if (!$markUnused || (!empty($translation) && (substr($translation, 0, 2) === '@@' && substr($translation, -2) === '@@'))) {\n                            $todos[$category . chr(4) . $message] = $translation;\n                        } else {\n                            $todos[$category . chr(4) . $message] = '@@' . $translation . '@@';\n                        }\n                    }\n                }\n\n                $merged = array_merge($merged, $todos);\n                if ($sort) {\n                    ksort($merged);\n                }\n\n                if ($overwrite === false) {\n                    $file .= '.merged';\n                }\n            } else {\n                sort($msgs);\n                foreach ($msgs as $message) {\n                    $merged[$category . chr(4) . $message] = '';\n                }\n                ksort($merged);\n            }\n            $this->stdout(\"Category \\\"$category\\\" merged.\\n\");\n            $hasSomethingToWrite = true;\n        }\n        if ($hasSomethingToWrite) {\n            $poFile->save($file, $merged);\n            $this->stdout(\"Translation saved.\\n\", Console::FG_GREEN);\n        } else {\n            $this->stdout(\"Nothing to save.\\n\", Console::FG_GREEN);\n        }\n    }\n\n    /**\n     * Writes messages into POT file.\n     *\n     * @param array $messages\n     * @param string $dirName name of the directory to write to\n     * @param string $catalog message catalog\n     * @since 2.0.6\n     */\n    protected function saveMessagesToPOT($messages, $dirName, $catalog)\n    {\n        $file = str_replace('\\\\', '/', \"$dirName/$catalog.pot\");\n        FileHelper::createDirectory(dirname($file));\n        $this->stdout(\"Saving messages to $file...\\n\");\n\n        $poFile = new GettextPoFile();\n\n        $merged = [];\n\n        $hasSomethingToWrite = false;\n        foreach ($messages as $category => $msgs) {\n            $msgs = array_values(array_unique($msgs));\n\n            sort($msgs);\n            foreach ($msgs as $message) {\n                $merged[$category . chr(4) . $message] = '';\n            }\n            $this->stdout(\"Category \\\"$category\\\" merged.\\n\");\n            $hasSomethingToWrite = true;\n        }\n        if ($hasSomethingToWrite) {\n            ksort($merged);\n            $poFile->save($file, $merged);\n            $this->stdout(\"Translation saved.\\n\", Console::FG_GREEN);\n        } else {\n            $this->stdout(\"Nothing to save.\\n\", Console::FG_GREEN);\n        }\n    }\n\n    private function deleteUnusedPhpMessageFiles($existingCategories, $dirName)\n    {\n        $messageFiles = FileHelper::findFiles($dirName);\n        foreach ($messageFiles as $messageFile) {\n            $categoryFileName = str_replace($dirName, '', $messageFile);\n            $categoryFileName = ltrim($categoryFileName, DIRECTORY_SEPARATOR);\n            $category = preg_replace('#\\.php$#', '', $categoryFileName);\n            $category = str_replace(DIRECTORY_SEPARATOR, '/', $category);\n\n            if (!in_array($category, $existingCategories, true)) {\n                unlink($messageFile);\n            }\n        }\n    }\n\n    /**\n     * @param string $configFile\n     * @throws Exception If configuration file does not exists.\n     * @since 2.0.13\n     */\n    protected function initConfig($configFile)\n    {\n        $configFileContent = [];\n        if ($configFile !== null) {\n            $configFile = Yii::getAlias($configFile);\n            if (!is_file($configFile)) {\n                throw new Exception(\"The configuration file does not exist: $configFile\");\n            }\n            $configFileContent = require $configFile;\n        }\n\n        $this->config = array_merge(\n            $this->getOptionValues($this->action->id),\n            $configFileContent,\n            $this->getPassedOptionValues()\n        );\n        $this->config['sourcePath'] = Yii::getAlias($this->config['sourcePath']);\n        $this->config['messagePath'] = Yii::getAlias($this->config['messagePath']);\n\n        if (!isset($this->config['sourcePath'], $this->config['languages'])) {\n            throw new Exception('The configuration file must specify \"sourcePath\" and \"languages\".');\n        }\n        if (!is_dir($this->config['sourcePath'])) {\n            throw new Exception(\"The source path {$this->config['sourcePath']} is not a valid directory.\");\n        }\n        if (empty($this->config['format']) || !in_array($this->config['format'], ['php', 'po', 'pot', 'db'])) {\n            throw new Exception('Format should be either \"php\", \"po\", \"pot\" or \"db\".');\n        }\n        if (in_array($this->config['format'], ['php', 'po', 'pot'])) {\n            if (!isset($this->config['messagePath'])) {\n                throw new Exception('The configuration file must specify \"messagePath\".');\n            }\n            if (!is_dir($this->config['messagePath'])) {\n                throw new Exception(\"The message path {$this->config['messagePath']} is not a valid directory.\");\n            }\n        }\n        if (empty($this->config['languages'])) {\n            throw new Exception('Languages cannot be empty.');\n        }\n\n        if ($this->config['format'] === 'php' && $this->config['phpDocBlock'] === null) {\n            $this->config['phpDocBlock'] = <<<DOCBLOCK\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii {$this->id}/{$this->action->id}' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nDOCBLOCK;\n        }\n    }\n}\n"
  },
  {
    "path": "framework/console/controllers/MigrateController.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\console\\controllers;\n\nuse Yii;\nuse yii\\base\\Action;\nuse yii\\console\\Application;\nuse yii\\db\\Connection;\nuse yii\\db\\Query;\nuse yii\\di\\Instance;\nuse yii\\helpers\\ArrayHelper;\nuse yii\\helpers\\Console;\nuse yii\\helpers\\Inflector;\n\n/**\n * Manages application migrations.\n *\n * A migration means a set of persistent changes to the application environment\n * that is shared among different developers. For example, in an application\n * backed by a database, a migration may refer to a set of changes to\n * the database, such as creating a new table, adding a new table column.\n *\n * This command provides support for tracking the migration history, upgrading\n * or downloading with migrations, and creating new migration skeletons.\n *\n * The migration history is stored in a database table named\n * as [[migrationTable]]. The table will be automatically created the first time\n * this command is executed, if it does not exist. You may also manually\n * create it as follows:\n *\n * ```\n * CREATE TABLE migration (\n *     version varchar(180) PRIMARY KEY,\n *     apply_time integer\n * )\n * ```\n *\n * Below are some common usages of this command:\n *\n * ```\n * # creates a new migration named 'create_user_table'\n * yii migrate/create create_user_table\n *\n * # applies ALL new migrations\n * yii migrate\n *\n * # reverts the last applied migration\n * yii migrate/down\n * ```\n *\n * Since 2.0.10 you can use namespaced migrations. In order to enable this feature you should configure [[migrationNamespaces]]\n * property for the controller at application configuration:\n *\n * ```\n * return [\n *     'controllerMap' => [\n *         'migrate' => [\n *             'class' => 'yii\\console\\controllers\\MigrateController',\n *             'migrationNamespaces' => [\n *                 'app\\migrations',\n *                 'some\\extension\\migrations',\n *             ],\n *             //'migrationPath' => null, // allows to disable not namespaced migration completely\n *         ],\n *     ],\n * ];\n * ```\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Application = Application\n * @extends BaseMigrateController<T>\n */\nclass MigrateController extends BaseMigrateController\n{\n    /**\n     * Maximum length of a migration name.\n     * @since 2.0.13\n     */\n    public const MAX_NAME_LENGTH = 180;\n    /**\n     * @var string the name of the table for keeping applied migration information.\n     */\n    public $migrationTable = '{{%migration}}';\n    /**\n     * {@inheritdoc}\n     */\n    public $templateFile = '@yii/views/migration.php';\n    /**\n     * @var array a set of template paths for generating migration code automatically.\n     *\n     * The key is the template type, the value is a path or the alias. Supported types are:\n     * - `create_table`: table creating template\n     * - `drop_table`: table dropping template\n     * - `add_column`: adding new column template\n     * - `drop_column`: dropping column template\n     * - `create_junction`: create junction template\n     *\n     * @since 2.0.7\n     */\n    public $generatorTemplateFiles = [\n        'create_table' => '@yii/views/createTableMigration.php',\n        'drop_table' => '@yii/views/dropTableMigration.php',\n        'add_column' => '@yii/views/addColumnMigration.php',\n        'drop_column' => '@yii/views/dropColumnMigration.php',\n        'create_junction' => '@yii/views/createTableMigration.php',\n    ];\n    /**\n     * @var bool indicates whether the table names generated should consider\n     * the `tablePrefix` setting of the DB connection. For example, if the table\n     * name is `post` the generator wil return `{{%post}}`.\n     * @since 2.0.8\n     */\n    public $useTablePrefix = true;\n    /**\n     * @var array column definition strings used for creating migration code.\n     *\n     * The format of each definition is `COLUMN_NAME:COLUMN_TYPE:COLUMN_DECORATOR`. Delimiter is `,`.\n     * For example, `--fields=\"name:string(12):notNull:unique\"`\n     * produces a string column of size 12 which is not null and unique values.\n     *\n     * Note: primary key is added automatically and is named id by default.\n     * If you want to use another name you may specify it explicitly like\n     * `--fields=\"id_key:primaryKey,name:string(12):notNull:unique\"`\n     * @since 2.0.7\n     */\n    public $fields = [];\n    /**\n     * @var Connection|array|string the DB connection object or the application component ID of the DB connection to use\n     * when applying migrations. Starting from version 2.0.3, this can also be a configuration array\n     * for creating the object.\n     */\n    public $db = 'db';\n    /**\n     * @var string the comment for the table being created.\n     * @since 2.0.14\n     */\n    public $comment = '';\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function options($actionID)\n    {\n        return array_merge(\n            parent::options($actionID),\n            ['migrationTable', 'db'], // global for all actions\n            $actionID === 'create'\n                ? ['templateFile', 'fields', 'useTablePrefix', 'comment']\n                : []\n        );\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.8\n     */\n    public function optionAliases()\n    {\n        return array_merge(parent::optionAliases(), [\n            'C' => 'comment',\n            'f' => 'fields',\n            'p' => 'migrationPath',\n            't' => 'migrationTable',\n            'F' => 'templateFile',\n            'P' => 'useTablePrefix',\n            'c' => 'compact',\n        ]);\n    }\n\n    /**\n     * This method is invoked right before an action is to be executed (after all possible filters.)\n     * It checks the existence of the [[migrationPath]].\n     * @param Action<static> $action the action to be executed.\n     * @return bool whether the action should continue to be executed.\n     *\n     * @phpstan-param Action<static> $action\n     * @psalm-param Action<self> $action\n     */\n    public function beforeAction($action)\n    {\n        if (parent::beforeAction($action)) {\n            $this->db = Instance::ensure($this->db, Connection::className());\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * Creates a new migration instance.\n     * @param string $class the migration class name\n     * @return \\yii\\db\\Migration the migration instance\n     */\n    protected function createMigration($class)\n    {\n        $this->includeMigrationFile($class);\n\n        return Yii::createObject([\n            'class' => $class,\n            'db' => $this->db,\n            'compact' => $this->compact,\n        ]);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function getMigrationHistory($limit)\n    {\n        if ($this->db->schema->getTableSchema($this->migrationTable, true) === null) {\n            $this->createMigrationHistoryTable();\n        }\n        $query = (new Query())\n            ->select(['version', 'apply_time'])\n            ->from($this->migrationTable)\n            ->orderBy(['apply_time' => SORT_DESC, 'version' => SORT_DESC]);\n\n        if (empty($this->migrationNamespaces)) {\n            $query->limit($limit);\n            $rows = $query->all($this->db);\n            $history = ArrayHelper::map($rows, 'version', 'apply_time');\n            unset($history[self::BASE_MIGRATION]);\n            return $history;\n        }\n\n        $rows = $query->all($this->db);\n\n        $history = [];\n        foreach ($rows as $key => $row) {\n            if ($row['version'] === self::BASE_MIGRATION) {\n                continue;\n            }\n            if (preg_match('/m?(\\d{6}_?\\d{6})(\\D.*)?$/is', $row['version'], $matches)) {\n                $time = str_replace('_', '', $matches[1]);\n                $row['canonicalVersion'] = $time;\n            } else {\n                $row['canonicalVersion'] = $row['version'];\n            }\n            $row['apply_time'] = (int) $row['apply_time'];\n            $history[] = $row;\n        }\n\n        usort($history, function ($a, $b) {\n            if ($a['apply_time'] === $b['apply_time']) {\n                if (($compareResult = strcasecmp($b['canonicalVersion'], $a['canonicalVersion'])) !== 0) {\n                    return $compareResult;\n                }\n\n                return strcasecmp($b['version'], $a['version']);\n            }\n\n            return ($a['apply_time'] > $b['apply_time']) ? -1 : +1;\n        });\n\n        $history = array_slice($history, 0, $limit);\n\n        $history = ArrayHelper::map($history, 'version', 'apply_time');\n\n        return $history;\n    }\n\n    /**\n     * Creates the migration history table.\n     */\n    protected function createMigrationHistoryTable()\n    {\n        $tableName = $this->db->schema->getRawTableName($this->migrationTable);\n        $this->stdout(\"Creating migration history table \\\"$tableName\\\"...\", Console::FG_YELLOW);\n        $this->db->createCommand()->createTable($this->migrationTable, [\n            'version' => 'varchar(' . static::MAX_NAME_LENGTH . ') NOT NULL PRIMARY KEY',\n            'apply_time' => 'integer',\n        ])->execute();\n        $this->db->createCommand()->insert($this->migrationTable, [\n            'version' => self::BASE_MIGRATION,\n            'apply_time' => time(),\n        ])->execute();\n        $this->stdout(\"Done.\\n\", Console::FG_GREEN);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function addMigrationHistory($version)\n    {\n        $command = $this->db->createCommand();\n        $command->insert($this->migrationTable, [\n            'version' => $version,\n            'apply_time' => time(),\n        ])->execute();\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.13\n     */\n    protected function truncateDatabase()\n    {\n        $db = $this->db;\n        $schemas = $db->schema->getTableSchemas();\n\n        // First drop all foreign keys,\n        foreach ($schemas as $schema) {\n            foreach ($schema->foreignKeys as $name => $foreignKey) {\n                $db->createCommand()->dropForeignKey($name, $schema->name)->execute();\n                $this->stdout(\"Foreign key $name dropped.\\n\");\n            }\n        }\n\n        // Then drop the tables:\n        foreach ($schemas as $schema) {\n            try {\n                $db->createCommand()->dropTable($schema->name)->execute();\n                $this->stdout(\"Table {$schema->name} dropped.\\n\");\n            } catch (\\Exception $e) {\n                if ($this->isViewRelated($e->getMessage())) {\n                    $db->createCommand()->dropView($schema->name)->execute();\n                    $this->stdout(\"View {$schema->name} dropped.\\n\");\n                } else {\n                    $this->stdout(\"Cannot drop {$schema->name} Table .\\n\");\n                }\n            }\n        }\n    }\n\n    /**\n     * Determines whether the error message is related to deleting a view or not\n     * @param string $errorMessage\n     * @return bool\n     */\n    private function isViewRelated($errorMessage)\n    {\n        $dropViewErrors = [\n            'DROP VIEW to delete view', // SQLite\n            'SQLSTATE[42S02]', // MySQL\n            'is a view. Use DROP VIEW', // Microsoft SQL Server\n        ];\n\n        foreach ($dropViewErrors as $dropViewError) {\n            if (strpos($errorMessage, $dropViewError) !== false) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function removeMigrationHistory($version)\n    {\n        $command = $this->db->createCommand();\n        $command->delete($this->migrationTable, [\n            'version' => $version,\n        ])->execute();\n    }\n\n    private $_migrationNameLimit;\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.13\n     */\n    protected function getMigrationNameLimit()\n    {\n        if ($this->_migrationNameLimit !== null) {\n            return $this->_migrationNameLimit;\n        }\n        $tableSchema = $this->db->schema ? $this->db->schema->getTableSchema($this->migrationTable, true) : null;\n        if ($tableSchema !== null) {\n            return $this->_migrationNameLimit = $tableSchema->columns['version']->size;\n        }\n\n        return static::MAX_NAME_LENGTH;\n    }\n\n    /**\n     * Normalizes table name for generator.\n     * When name is preceded with underscore name case is kept - otherwise it's converted from camelcase to underscored.\n     * Last underscore is always trimmed so if there should be underscore at the end of name use two of them.\n     * @param string $name\n     * @return string\n     */\n    private function normalizeTableName($name)\n    {\n        if (substr($name, -1) === '_') {\n            $name = substr($name, 0, -1);\n        }\n\n        if (strncmp($name, '_', 1) === 0) {\n            return substr($name, 1);\n        }\n\n        return Inflector::underscore($name);\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.8\n     */\n    protected function generateMigrationSourceCode($params)\n    {\n        $parsedFields = $this->parseFields();\n        $fields = $parsedFields['fields'];\n        $foreignKeys = $parsedFields['foreignKeys'];\n\n        $name = $params['name'];\n        if ($params['namespace']) {\n            $name = substr($name, (strrpos($name, '\\\\') ?: -1) + 1);\n        }\n\n        $templateFile = $this->templateFile;\n        $table = null;\n        if (preg_match('/^create_?junction_?(?:table)?_?(?:for)?(.+)_?and(.+)_?tables?$/i', $name, $matches)) {\n            $templateFile = $this->generatorTemplateFiles['create_junction'];\n            $firstTable = $this->normalizeTableName($matches[1]);\n            $secondTable = $this->normalizeTableName($matches[2]);\n\n            $fields = array_merge(\n                [\n                    [\n                        'property' => $firstTable . '_id',\n                        'decorators' => 'integer()',\n                    ],\n                    [\n                        'property' => $secondTable . '_id',\n                        'decorators' => 'integer()',\n                    ],\n                ],\n                $fields,\n                [\n                    [\n                        'property' => 'PRIMARY KEY(' .\n                            $firstTable . '_id, ' .\n                            $secondTable . '_id)',\n                    ],\n                ]\n            );\n\n            $foreignKeys[$firstTable . '_id']['table'] = $firstTable;\n            $foreignKeys[$secondTable . '_id']['table'] = $secondTable;\n            $foreignKeys[$firstTable . '_id']['column'] = null;\n            $foreignKeys[$secondTable . '_id']['column'] = null;\n            $table = $firstTable . '_' . $secondTable;\n        } elseif (preg_match('/^add(.+)columns?_?to(.+)table$/i', $name, $matches)) {\n            $templateFile = $this->generatorTemplateFiles['add_column'];\n            $table = $this->normalizeTableName($matches[2]);\n        } elseif (preg_match('/^drop(.+)columns?_?from(.+)table$/i', $name, $matches)) {\n            $templateFile = $this->generatorTemplateFiles['drop_column'];\n            $table = $this->normalizeTableName($matches[2]);\n        } elseif (preg_match('/^create(.+)table$/i', $name, $matches)) {\n            $this->addDefaultPrimaryKey($fields);\n            $templateFile = $this->generatorTemplateFiles['create_table'];\n            $table = $this->normalizeTableName($matches[1]);\n        } elseif (preg_match('/^drop(.+)table$/i', $name, $matches)) {\n            $this->addDefaultPrimaryKey($fields);\n            $templateFile = $this->generatorTemplateFiles['drop_table'];\n            $table = $this->normalizeTableName($matches[1]);\n        }\n\n        foreach ($foreignKeys as $column => $foreignKey) {\n            $relatedColumn = $foreignKey['column'];\n            $relatedTable = $foreignKey['table'];\n            // Since 2.0.11 if related column name is not specified,\n            // we're trying to get it from table schema\n            // @see https://github.com/yiisoft/yii2/issues/12748\n            if ($relatedColumn === null) {\n                $relatedColumn = 'id';\n                try {\n                    $this->db = Instance::ensure($this->db, Connection::className());\n                    $relatedTableSchema = $this->db->getTableSchema($relatedTable);\n                    if ($relatedTableSchema !== null) {\n                        $primaryKeyCount = count($relatedTableSchema->primaryKey);\n                        if ($primaryKeyCount === 1) {\n                            $relatedColumn = $relatedTableSchema->primaryKey[0];\n                        } elseif ($primaryKeyCount > 1) {\n                            $this->stdout(\"Related table for field \\\"{$column}\\\" exists, but primary key is composite. Default name \\\"id\\\" will be used for related field\\n\", Console::FG_YELLOW);\n                        } elseif ($primaryKeyCount === 0) {\n                            $this->stdout(\"Related table for field \\\"{$column}\\\" exists, but does not have a primary key. Default name \\\"id\\\" will be used for related field.\\n\", Console::FG_YELLOW);\n                        }\n                    }\n                } catch (\\ReflectionException $e) {\n                    $this->stdout(\"Cannot initialize database component to try reading referenced table schema for field \\\"{$column}\\\". Default name \\\"id\\\" will be used for related field.\\n\", Console::FG_YELLOW);\n                }\n            }\n            $foreignKeys[$column] = [\n                'idx' => $this->generateTableName(\"idx-$table-$column\"),\n                'fk' => $this->generateTableName(\"fk-$table-$column\"),\n                'relatedTable' => $this->generateTableName($relatedTable),\n                'relatedColumn' => $relatedColumn,\n            ];\n        }\n\n        return $this->renderFile(Yii::getAlias($templateFile), array_merge($params, [\n            'table' => $this->generateTableName($table),\n            'fields' => $fields,\n            'foreignKeys' => $foreignKeys,\n            'tableComment' => $this->comment,\n        ]));\n    }\n\n    /**\n     * If `useTablePrefix` equals true, then the table name will contain the\n     * prefix format.\n     *\n     * @param string $tableName the table name to generate.\n     * @return string\n     * @since 2.0.8\n     */\n    protected function generateTableName($tableName)\n    {\n        if (!$this->useTablePrefix) {\n            return $tableName;\n        }\n\n        return '{{%' . $tableName . '}}';\n    }\n\n    /**\n     * Parse the command line migration fields.\n     * @return array parse result with following fields:\n     *\n     * - fields: array, parsed fields\n     * - foreignKeys: array, detected foreign keys\n     *\n     * @since 2.0.7\n     */\n    protected function parseFields()\n    {\n        $fields = [];\n        $foreignKeys = [];\n\n        foreach ($this->fields as $index => $field) {\n            $chunks = $this->splitFieldIntoChunks($field);\n            $property = array_shift($chunks);\n\n            foreach ($chunks as $i => &$chunk) {\n                if (strncmp($chunk, 'foreignKey', 10) === 0) {\n                    preg_match('/foreignKey\\((\\w*)\\s?(\\w*)\\)/', $chunk, $matches);\n                    $foreignKeys[$property] = [\n                        'table' => isset($matches[1])\n                            ? $matches[1]\n                            : preg_replace('/_id$/', '', $property),\n                        'column' => !empty($matches[2])\n                            ? $matches[2]\n                            : null,\n                    ];\n\n                    unset($chunks[$i]);\n                    continue;\n                }\n\n                if (!preg_match('/^(.+?)\\(([^(]+)\\)$/', $chunk)) {\n                    $chunk .= '()';\n                }\n            }\n            $fields[] = [\n                'property' => $property,\n                'decorators' => implode('->', $chunks),\n            ];\n        }\n\n        return [\n            'fields' => $fields,\n            'foreignKeys' => $foreignKeys,\n        ];\n    }\n\n    /**\n     * Splits field into chunks\n     *\n     * @param string $field\n     * @return string[]|false\n     */\n    protected function splitFieldIntoChunks($field)\n    {\n        $originalDefaultValue = null;\n        $defaultValue = null;\n        preg_match_all('/defaultValue\\([\"\\'].*?:?.*?[\"\\']\\)/', $field, $matches, PREG_SET_ORDER, 0);\n        if (isset($matches[0][0])) {\n            $originalDefaultValue = $matches[0][0];\n            $defaultValue = str_replace(':', '{{colon}}', $originalDefaultValue);\n            $field = str_replace($originalDefaultValue, $defaultValue, $field);\n        }\n\n        $chunks = preg_split('/\\s?:\\s?/', $field);\n\n        if (is_array($chunks) && $defaultValue !== null && $originalDefaultValue !== null) {\n            foreach ($chunks as $key => $chunk) {\n                $chunks[$key] = str_replace($defaultValue, $originalDefaultValue, $chunk);\n            }\n        }\n\n        return $chunks;\n    }\n\n    /**\n     * Adds default primary key to fields list if there's no primary key specified.\n     * @param array $fields parsed fields\n     * @since 2.0.7\n     */\n    protected function addDefaultPrimaryKey(&$fields)\n    {\n        foreach ($fields as $field) {\n            if ($field['property'] === 'id' || false !== strripos($field['decorators'], 'primarykey()')) {\n                return;\n            }\n        }\n        array_unshift($fields, ['property' => 'id', 'decorators' => 'primaryKey()']);\n    }\n}\n"
  },
  {
    "path": "framework/console/controllers/ServeController.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\console\\controllers;\n\nuse Yii;\nuse yii\\console\\Application;\nuse yii\\console\\Controller;\nuse yii\\console\\ExitCode;\nuse yii\\helpers\\Console;\n\n/**\n * Runs PHP built-in web server.\n *\n * In order to access server from remote machines use 0.0.0.0:8000. That is especially useful when running server in\n * a virtual machine.\n *\n * @author Alexander Makarov <sam@rmcreative.ru>\n * @since 2.0.7\n *\n * @template T of Application = Application\n * @extends Controller<T>\n */\nclass ServeController extends Controller\n{\n    public const EXIT_CODE_NO_DOCUMENT_ROOT = 2;\n    public const EXIT_CODE_NO_ROUTING_FILE = 3;\n    public const EXIT_CODE_ADDRESS_TAKEN_BY_ANOTHER_SERVER = 4;\n    public const EXIT_CODE_ADDRESS_TAKEN_BY_ANOTHER_PROCESS = 5;\n    /**\n     * @var int port to serve on.\n     */\n    public $port = 8080;\n    /**\n     * @var string path or [path alias](guide:concept-aliases) to directory to serve\n     */\n    public $docroot = '@app/web';\n    /**\n     * @var string path or [path alias](guide:concept-aliases) to router script.\n     * See https://www.php.net/manual/en/features.commandline.webserver.php\n     */\n    public $router;\n\n\n    /**\n     * Runs PHP built-in web server.\n     *\n     * @param string $address address to serve on. Either \"host\" or \"host:port\".\n     *\n     * @return int\n     */\n    public function actionIndex($address = 'localhost')\n    {\n        $documentRoot = Yii::getAlias($this->docroot);\n        $router = $this->router !== null ? Yii::getAlias($this->router) : null;\n\n        if (strpos($address, ':') === false) {\n            $address = $address . ':' . $this->port;\n        }\n\n        if (!is_dir($documentRoot)) {\n            $this->stdout(\"Document root \\\"$documentRoot\\\" does not exist.\\n\", Console::FG_RED);\n            return self::EXIT_CODE_NO_DOCUMENT_ROOT;\n        }\n\n        if ($this->isAddressTaken($address)) {\n            $this->stdout(\"http://$address is taken by another process.\\n\", Console::FG_RED);\n            return self::EXIT_CODE_ADDRESS_TAKEN_BY_ANOTHER_PROCESS;\n        }\n\n        if ($this->router !== null && !file_exists($router)) {\n            $this->stdout(\"Routing file \\\"$router\\\" does not exist.\\n\", Console::FG_RED);\n            return self::EXIT_CODE_NO_ROUTING_FILE;\n        }\n\n        $this->stdout(\"Server started on http://{$address}/\\n\");\n        $this->stdout(\"Document root is \\\"{$documentRoot}\\\"\\n\");\n        if ($this->router) {\n            $this->stdout(\"Routing file is \\\"$router\\\"\\n\");\n        }\n        $this->stdout(\"Quit the server with CTRL-C or COMMAND-C.\\n\");\n\n        $command = '\"' . PHP_BINARY . '\"' . \" -S {$address} -t \\\"{$documentRoot}\\\"\";\n\n        if ($this->router !== null && $router !== '') {\n            $command .= \" \\\"{$router}\\\"\";\n        }\n\n        $this->runCommand($command);\n\n        return ExitCode::OK;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function options($actionID)\n    {\n        return array_merge(parent::options($actionID), [\n            'docroot',\n            'router',\n            'port',\n        ]);\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.8\n     */\n    public function optionAliases()\n    {\n        return array_merge(parent::optionAliases(), [\n            't' => 'docroot',\n            'p' => 'port',\n            'r' => 'router',\n        ]);\n    }\n\n    /**\n     * @param string $address server address\n     * @return bool if address is already in use\n     */\n    protected function isAddressTaken($address)\n    {\n        list($hostname, $port) = explode(':', $address);\n        $fp = @fsockopen($hostname, $port, $errno, $errstr, 3);\n        if ($fp === false) {\n            return false;\n        }\n        fclose($fp);\n        return true;\n    }\n\n    protected function runCommand($command)\n    {\n        passthru($command);\n    }\n}\n"
  },
  {
    "path": "framework/console/runtime/.gitignore",
    "content": "*\n!.gitignore\n"
  },
  {
    "path": "framework/console/widgets/Table.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\console\\widgets;\n\nuse Yii;\nuse yii\\base\\Widget;\nuse yii\\helpers\\ArrayHelper;\nuse yii\\helpers\\Console;\n\n/**\n * Table class displays a table in console.\n *\n * For example,\n *\n * ```\n * $table = new Table();\n *\n * echo $table\n *     ->setHeaders(['test1', 'test2', 'test3'])\n *     ->setRows([\n *         ['col1', 'col2', 'col3'],\n *         ['col1', 'col2', ['col3-0', 'col3-1', 'col3-2']],\n *     ])\n *     ->run();\n * ```\n *\n * or\n *\n * ```\n * echo Table::widget([\n *     'headers' => ['test1', 'test2', 'test3'],\n *     'rows' => [\n *         ['col1', 'col2', 'col3'],\n *         ['col1', 'col2', ['col3-0', 'col3-1', 'col3-2']],\n *     ],\n * ]);\n *\n * @property-write array $chars Table chars.\n * @property-write array $headers Table headers.\n * @property-write string $listPrefix List prefix.\n * @property-write array $rows Table rows.\n * @property-write int $screenWidth Screen width.\n *\n * @author Daniel Gomez Pan <pana_1990@hotmail.com>\n * @since 2.0.13\n */\nclass Table extends Widget\n{\n    public const DEFAULT_CONSOLE_SCREEN_WIDTH = 120;\n    public const CONSOLE_SCROLLBAR_OFFSET = 3;\n    public const CHAR_TOP = 'top';\n    public const CHAR_TOP_MID = 'top-mid';\n    public const CHAR_TOP_LEFT = 'top-left';\n    public const CHAR_TOP_RIGHT = 'top-right';\n    public const CHAR_BOTTOM = 'bottom';\n    public const CHAR_BOTTOM_MID = 'bottom-mid';\n    public const CHAR_BOTTOM_LEFT = 'bottom-left';\n    public const CHAR_BOTTOM_RIGHT = 'bottom-right';\n    public const CHAR_LEFT = 'left';\n    public const CHAR_LEFT_MID = 'left-mid';\n    public const CHAR_MID = 'mid';\n    public const CHAR_MID_MID = 'mid-mid';\n    public const CHAR_RIGHT = 'right';\n    public const CHAR_RIGHT_MID = 'right-mid';\n    public const CHAR_MIDDLE = 'middle';\n    /**\n     * @var array table headers\n     * @since 2.0.19\n     */\n    protected $headers = [];\n    /**\n     * @var array table rows\n     * @since 2.0.19\n     */\n    protected $rows = [];\n    /**\n     * @var array table chars\n     * @since 2.0.19\n     */\n    protected $chars = [\n        self::CHAR_TOP => '═',\n        self::CHAR_TOP_MID => '╤',\n        self::CHAR_TOP_LEFT => '╔',\n        self::CHAR_TOP_RIGHT => '╗',\n        self::CHAR_BOTTOM => '═',\n        self::CHAR_BOTTOM_MID => '╧',\n        self::CHAR_BOTTOM_LEFT => '╚',\n        self::CHAR_BOTTOM_RIGHT => '╝',\n        self::CHAR_LEFT => '║',\n        self::CHAR_LEFT_MID => '╟',\n        self::CHAR_MID => '─',\n        self::CHAR_MID_MID => '┼',\n        self::CHAR_RIGHT => '║',\n        self::CHAR_RIGHT_MID => '╢',\n        self::CHAR_MIDDLE => '│',\n    ];\n    /**\n     * @var array table column widths\n     * @since 2.0.19\n     */\n    protected $columnWidths = [];\n    /**\n     * @var int screen width\n     * @since 2.0.19\n     */\n    protected $screenWidth;\n    /**\n     * @var string list prefix\n     * @since 2.0.19\n     */\n    protected $listPrefix = '• ';\n\n\n    /**\n     * Set table headers.\n     *\n     * @param array $headers table headers\n     * @return $this\n     */\n    public function setHeaders(array $headers)\n    {\n        $this->headers = array_values($headers);\n        return $this;\n    }\n\n    /**\n     * Set table rows.\n     *\n     * @param array $rows table rows\n     * @return $this\n     */\n    public function setRows(array $rows)\n    {\n        $this->rows = array_map(function ($row) {\n            return array_map(function ($value) {\n                return empty($value) && !is_numeric($value)\n                    ? ' '\n                    :  (is_array($value)\n                        ? array_values($value)\n                        : $value);\n            }, array_values($row));\n        }, $rows);\n        return $this;\n    }\n\n    /**\n     * Set table chars.\n     *\n     * @param array $chars table chars\n     * @return $this\n     */\n    public function setChars(array $chars)\n    {\n        $this->chars = $chars;\n        return $this;\n    }\n\n    /**\n     * Set screen width.\n     *\n     * @param int $width screen width\n     * @return $this\n     */\n    public function setScreenWidth($width)\n    {\n        $this->screenWidth = $width;\n        return $this;\n    }\n\n    /**\n     * Set list prefix.\n     *\n     * @param string $listPrefix list prefix\n     * @return $this\n     */\n    public function setListPrefix($listPrefix)\n    {\n        $this->listPrefix = $listPrefix;\n        return $this;\n    }\n\n    /**\n     * @return string the rendered table\n     */\n    public function run()\n    {\n        $this->calculateRowsSize();\n        $headerCount = count($this->headers);\n\n        $buffer = $this->renderSeparator(\n            $this->chars[self::CHAR_TOP_LEFT],\n            $this->chars[self::CHAR_TOP_MID],\n            $this->chars[self::CHAR_TOP],\n            $this->chars[self::CHAR_TOP_RIGHT]\n        );\n        // Header\n        if ($headerCount > 0) {\n            $buffer .= $this->renderRow(\n                $this->headers,\n                $this->chars[self::CHAR_LEFT],\n                $this->chars[self::CHAR_MIDDLE],\n                $this->chars[self::CHAR_RIGHT]\n            );\n        }\n\n        // Content\n        foreach ($this->rows as $i => $row) {\n            if ($i > 0 || $headerCount > 0) {\n                $buffer .= $this->renderSeparator(\n                    $this->chars[self::CHAR_LEFT_MID],\n                    $this->chars[self::CHAR_MID_MID],\n                    $this->chars[self::CHAR_MID],\n                    $this->chars[self::CHAR_RIGHT_MID]\n                );\n            }\n            $buffer .= $this->renderRow(\n                $row,\n                $this->chars[self::CHAR_LEFT],\n                $this->chars[self::CHAR_MIDDLE],\n                $this->chars[self::CHAR_RIGHT]\n            );\n        }\n\n        $buffer .= $this->renderSeparator(\n            $this->chars[self::CHAR_BOTTOM_LEFT],\n            $this->chars[self::CHAR_BOTTOM_MID],\n            $this->chars[self::CHAR_BOTTOM],\n            $this->chars[self::CHAR_BOTTOM_RIGHT]\n        );\n\n        return $buffer;\n    }\n\n    /**\n     * Renders a row of data into a string.\n     *\n     * @param array $row row of data\n     * @param string $spanLeft character for left border\n     * @param string $spanMiddle character for middle border\n     * @param string $spanRight character for right border\n     * @return string\n     * @see \\yii\\console\\widgets\\Table::render()\n     */\n    protected function renderRow(array $row, $spanLeft, $spanMiddle, $spanRight)\n    {\n        $size = $this->columnWidths;\n\n        $buffer = '';\n        $arrayPointer = [];\n        $renderedChunkTexts = [];\n        for ($i = 0, ($max = $this->calculateRowHeight($row)) ?: $max = 1; $i < $max; $i++) {\n            $buffer .= $spanLeft . ' ';\n            foreach ($size as $index => $cellSize) {\n                $cell = isset($row[$index]) ? $row[$index] : null;\n                $prefix = '';\n                if ($index !== 0) {\n                    $buffer .= $spanMiddle . ' ';\n                }\n\n                $arrayFromMultilineString = false;\n                if (is_string($cell)) {\n                    $cellLines = explode(PHP_EOL, $cell);\n                    if (count($cellLines) > 1) {\n                        $cell = $cellLines;\n                        $arrayFromMultilineString = true;\n                    }\n                }\n\n                if (is_array($cell)) {\n                    if (empty($renderedChunkTexts[$index])) {\n                        $renderedChunkTexts[$index] = '';\n                        $start = 0;\n                        $prefix = $arrayFromMultilineString ? '' : $this->listPrefix;\n                        if (!isset($arrayPointer[$index])) {\n                            $arrayPointer[$index] = 0;\n                        }\n                    } else {\n                        $start = mb_strwidth($renderedChunkTexts[$index], Yii::$app->charset);\n                    }\n                    $chunk = Console::ansiColorizedSubstr(\n                        $cell[$arrayPointer[$index]],\n                        $start,\n                        $cellSize - 2 - Console::ansiStrwidth($prefix)\n                    );\n                    $renderedChunkTexts[$index] .= Console::stripAnsiFormat($chunk);\n                    $fullChunkText = Console::stripAnsiFormat($cell[$arrayPointer[$index]]);\n                    if (isset($cell[$arrayPointer[$index] + 1]) && $renderedChunkTexts[$index] === $fullChunkText) {\n                        $arrayPointer[$index]++;\n                        $renderedChunkTexts[$index] = '';\n                    }\n                } else {\n                    $chunk = Console::ansiColorizedSubstr($cell, ($cellSize * $i) - ($i * 2), $cellSize - 2);\n                }\n                $chunk = $prefix . $chunk;\n                $repeat = $cellSize - Console::ansiStrwidth($chunk) - 1;\n                $buffer .= $chunk;\n                if ($repeat >= 0) {\n                    $buffer .= str_repeat(' ', $repeat);\n                }\n            }\n            $buffer .= \"$spanRight\\n\";\n        }\n\n        return $buffer;\n    }\n\n    /**\n     * Renders separator.\n     *\n     * @param string $spanLeft character for left border\n     * @param string $spanMid character for middle border\n     * @param string $spanMidMid character for middle-middle border\n     * @param string $spanRight character for right border\n     * @return string the generated separator row\n     * @see \\yii\\console\\widgets\\Table::render()\n     */\n    protected function renderSeparator($spanLeft, $spanMid, $spanMidMid, $spanRight)\n    {\n        $separator = $spanLeft;\n        foreach ($this->columnWidths as $index => $rowSize) {\n            if ($index !== 0) {\n                $separator .= $spanMid;\n            }\n            $separator .= str_repeat($spanMidMid, $rowSize);\n        }\n        $separator .= $spanRight . \"\\n\";\n        return $separator;\n    }\n\n    /**\n     * Calculate the size of rows to draw anchor of columns in console.\n     *\n     * @see \\yii\\console\\widgets\\Table::render()\n     */\n    protected function calculateRowsSize()\n    {\n        $this->columnWidths = $columns = [];\n        $totalWidth = 0;\n        $screenWidth = $this->getScreenWidth() - self::CONSOLE_SCROLLBAR_OFFSET;\n\n        $headerCount = count($this->headers);\n        if (empty($this->rows)) {\n            $rowColCount = 0;\n        } else {\n            $rowColCount = max(array_map('count', $this->rows));\n        }\n        $count = max($headerCount, $rowColCount);\n        for ($i = 0; $i < $count; $i++) {\n            $columns[] = ArrayHelper::getColumn($this->rows, $i);\n            if ($i < $headerCount) {\n                $columns[$i][] = $this->headers[$i];\n            }\n        }\n\n        foreach ($columns as $column) {\n            $columnWidth = max(array_map(function ($val) {\n                if (is_array($val)) {\n                    return max(array_map('yii\\helpers\\Console::ansiStrwidth', $val)) + Console::ansiStrwidth($this->listPrefix);\n                }\n                if (is_string($val)) {\n                    return max(array_map('yii\\helpers\\Console::ansiStrwidth', explode(PHP_EOL, $val)));\n                }\n                return Console::ansiStrwidth($val);\n            }, $column)) + 2;\n            $this->columnWidths[] = $columnWidth;\n            $totalWidth += $columnWidth;\n        }\n\n        if ($totalWidth > $screenWidth) {\n            $minWidth = 3;\n            $fixWidths = [];\n            $relativeWidth = $screenWidth / $totalWidth;\n            foreach ($this->columnWidths as $j => $width) {\n                $scaledWidth = (int) ($width * $relativeWidth);\n                if ($scaledWidth < $minWidth) {\n                    $fixWidths[$j] = 3;\n                }\n            }\n\n            $totalFixWidth = array_sum($fixWidths);\n            $relativeWidth = ($screenWidth - $totalFixWidth) / ($totalWidth - $totalFixWidth);\n            foreach ($this->columnWidths as $j => $width) {\n                if (!array_key_exists($j, $fixWidths)) {\n                    $this->columnWidths[$j] = (int) ($width * $relativeWidth);\n                }\n            }\n        }\n    }\n\n    /**\n     * Calculate the height of a row.\n     *\n     * @param array $row\n     * @return int maximum row per cell\n     * @see \\yii\\console\\widgets\\Table::render()\n     */\n    protected function calculateRowHeight($row)\n    {\n        $rowsPerCell = array_map(function ($size, $columnWidth) {\n            if (is_array($columnWidth)) {\n                $rows = 0;\n                foreach ($columnWidth as $width) {\n                    $rows +=  $size == 2 ? 0 : ceil($width / ($size - 2));\n                }\n                return $rows;\n            }\n            return $size == 2 || $columnWidth == 0 ? 0 : ceil($columnWidth / ($size - 2));\n        }, $this->columnWidths, array_map(function ($val) {\n            if (is_array($val)) {\n                return array_map('yii\\helpers\\Console::ansiStrwidth', $val);\n            }\n            if (is_string($val)) {\n                return array_map('yii\\helpers\\Console::ansiStrwidth', explode(PHP_EOL, $val));\n            }\n            return Console::ansiStrwidth($val);\n        }, $row));\n        return max($rowsPerCell);\n    }\n\n    /**\n     * Getting screen width.\n     * If it is not able to determine screen width, default value `123` will be set.\n     *\n     * @return int screen width\n     */\n    protected function getScreenWidth()\n    {\n        if (!$this->screenWidth) {\n            $size = Console::getScreenSize();\n            $this->screenWidth = isset($size[0])\n                ? $size[0]\n                : self::DEFAULT_CONSOLE_SCREEN_WIDTH + self::CONSOLE_SCROLLBAR_OFFSET;\n        }\n        return $this->screenWidth;\n    }\n}\n"
  },
  {
    "path": "framework/data/ActiveDataFilter.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\data;\n\n/**\n * ActiveDataFilter allows composing a filtering condition in a format suitable for [[\\yii\\db\\QueryInterface::where()]].\n *\n * @see DataFilter\n *\n * @author Paul Klimov <klimov.paul@gmail.com>\n * @since 2.0.13\n */\nclass ActiveDataFilter extends DataFilter\n{\n    /**\n     * @var array maps filtering condition keywords to build methods.\n     * These methods are used by [[buildCondition()]] to build the actual filtering conditions.\n     * Particular condition builder can be specified using a PHP callback. For example:\n     *\n     * ```\n     * [\n     *     'XOR' => function (string $operator, mixed $condition) {\n     *         //return array;\n     *     },\n     *     'LIKE' => function (string $operator, mixed $condition, string $attribute) {\n     *         //return array;\n     *     },\n     * ]\n     * ```\n     */\n    public $conditionBuilders = [\n        'AND' => 'buildConjunctionCondition',\n        'OR' => 'buildConjunctionCondition',\n        'NOT' => 'buildBlockCondition',\n        '<' => 'buildOperatorCondition',\n        '>' => 'buildOperatorCondition',\n        '<=' => 'buildOperatorCondition',\n        '>=' => 'buildOperatorCondition',\n        '=' => 'buildOperatorCondition',\n        '!=' => 'buildOperatorCondition',\n        'IN' => 'buildOperatorCondition',\n        'NOT IN' => 'buildOperatorCondition',\n        'LIKE' => 'buildOperatorCondition',\n    ];\n    /**\n     * @var array map filtering operators to operators used in [[\\yii\\db\\QueryInterface::where()]].\n     * The format is: `[filterOperator => queryOperator]`.\n     * If particular operator keyword does not appear in the map, it will be used as is.\n     *\n     * Usually the map can be left empty as filter operator names are consistent with the ones\n     * used in [[\\yii\\db\\QueryInterface::where()]]. However, you may want to adjust it in some special cases.\n     * For example, when using PostgreSQL you may want to setup the following map:\n     *\n     * ```\n     * [\n     *     'LIKE' => 'ILIKE'\n     * ]\n     * ```\n     */\n    public $queryOperatorMap = [];\n\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function buildInternal()\n    {\n        $filter = $this->normalize(false);\n        if (empty($filter)) {\n            return [];\n        }\n\n        return $this->buildCondition($filter);\n    }\n\n    /**\n     * @param array $condition\n     * @return array built condition.\n     */\n    protected function buildCondition($condition)\n    {\n        $parts = [];\n        foreach ($condition as $key => $value) {\n            if (isset($this->conditionBuilders[$key])) {\n                $method = $this->conditionBuilders[$key];\n                if (is_string($method)) {\n                    $callback = [$this, $method];\n                } else {\n                    $callback = $method;\n                }\n            } else {\n                $callback = [$this, 'buildAttributeCondition'];\n            }\n            $parts[] = $callback($key, $value);\n        }\n\n        if (!empty($parts)) {\n            if (count($parts) > 1) {\n                array_unshift($parts, 'AND');\n            } else {\n                $parts = array_shift($parts);\n            }\n        }\n\n        return $parts;\n    }\n\n    /**\n     * Builds conjunction condition, which consists of multiple independent ones.\n     * It covers such operators as `and` and `or`.\n     * @param string $operator operator keyword.\n     * @param mixed $condition raw condition.\n     * @return array actual condition.\n     */\n    protected function buildConjunctionCondition($operator, $condition)\n    {\n        if (isset($this->queryOperatorMap[$operator])) {\n            $operator = $this->queryOperatorMap[$operator];\n        }\n        $result = [$operator];\n\n        foreach ($condition as $part) {\n            $result[] = $this->buildCondition($part);\n        }\n\n        return $result;\n    }\n\n    /**\n     * Builds block condition, which consists of a single condition.\n     * It covers such operators as `not`.\n     * @param string $operator operator keyword.\n     * @param mixed $condition raw condition.\n     * @return array actual condition.\n     */\n    protected function buildBlockCondition($operator, $condition)\n    {\n        if (isset($this->queryOperatorMap[$operator])) {\n            $operator = $this->queryOperatorMap[$operator];\n        }\n        return [\n            $operator,\n            $this->buildCondition($condition),\n        ];\n    }\n\n    /**\n     * Builds search condition for a particular attribute.\n     * @param string $attribute search attribute name.\n     * @param mixed $condition search condition.\n     * @return array actual condition.\n     */\n    protected function buildAttributeCondition($attribute, $condition)\n    {\n        if (is_array($condition)) {\n            $parts = [];\n            foreach ($condition as $operator => $value) {\n                if (isset($this->operatorTypes[$operator])) {\n                    if (isset($this->conditionBuilders[$operator])) {\n                        $method = $this->conditionBuilders[$operator];\n                        if (is_string($method)) {\n                            $callback = [$this, $method];\n                        } else {\n                            $callback = $method;\n                        }\n                        $parts[] = $callback($operator, $value, $attribute);\n                    } else {\n                        $parts[] = $this->buildOperatorCondition($operator, $value, $attribute);\n                    }\n                }\n            }\n\n            if (!empty($parts)) {\n                if (count($parts) > 1) {\n                    return array_merge(['AND'], $parts);\n                }\n                return array_shift($parts);\n            }\n        }\n\n        return [$attribute => $this->filterAttributeValue($attribute, $condition)];\n    }\n\n    /**\n     * Builds an operator condition.\n     * @param string $operator operator keyword.\n     * @param mixed $condition attribute condition.\n     * @param string $attribute attribute name.\n     * @return array actual condition.\n     */\n    protected function buildOperatorCondition($operator, $condition, $attribute)\n    {\n        if (isset($this->queryOperatorMap[$operator])) {\n            $operator = $this->queryOperatorMap[$operator];\n        }\n        return [$operator, $attribute, $this->filterAttributeValue($attribute, $condition)];\n    }\n}\n"
  },
  {
    "path": "framework/data/ActiveDataProvider.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\data;\n\nuse yii\\base\\InvalidConfigException;\nuse yii\\base\\Model;\nuse yii\\db\\ActiveQueryInterface;\nuse yii\\db\\Connection;\nuse yii\\db\\QueryInterface;\nuse yii\\di\\Instance;\n\n/**\n * ActiveDataProvider implements a data provider based on [[\\yii\\db\\Query]] and [[\\yii\\db\\ActiveQuery]].\n *\n * ActiveDataProvider provides data by performing DB queries using [[query]].\n *\n * The following is an example of using ActiveDataProvider to provide ActiveRecord instances:\n *\n * ```\n * $provider = new ActiveDataProvider([\n *     'query' => Post::find(),\n *     'pagination' => [\n *         'pageSize' => 20,\n *     ],\n * ]);\n *\n * // get the posts in the current page\n * $posts = $provider->getModels();\n * ```\n *\n * And the following example shows how to use ActiveDataProvider without ActiveRecord:\n *\n * ```\n * $query = new Query();\n * $provider = new ActiveDataProvider([\n *     'query' => $query->from('post'),\n *     'pagination' => [\n *         'pageSize' => 20,\n *     ],\n * ]);\n *\n * // get the posts in the current page\n * $posts = $provider->getModels();\n * ```\n *\n * For more details and usage information on ActiveDataProvider, see the [guide article on data providers](guide:output-data-providers).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass ActiveDataProvider extends BaseDataProvider\n{\n    /**\n     * @var QueryInterface|null the query that is used to fetch data models and [[totalCount]] if it is not explicitly set.\n     */\n    public $query;\n    /**\n     * @var string|callable|null the column that is used as the key of the data models.\n     * This can be either a column name, or a callable that returns the key value of a given data model.\n     *\n     * If this is not set, the following rules will be used to determine the keys of the data models:\n     *\n     * - If [[query]] is an [[\\yii\\db\\ActiveQuery]] instance, the primary keys of [[\\yii\\db\\ActiveQuery::modelClass]] will be used.\n     * - Otherwise, the keys of the [[models]] array will be used.\n     *\n     * @see getKeys()\n     */\n    public $key;\n    /**\n     * @var Connection|array|string|null the DB connection object or the application component ID of the DB connection.\n     * If set it overrides [[query]] default DB connection.\n     * Starting from version 2.0.2, this can also be a configuration array for creating the object.\n     */\n    public $db;\n\n\n    /**\n     * Initializes the DB connection component.\n     * This method will initialize the [[db]] property (when set) to make sure it refers to a valid DB connection.\n     * @throws InvalidConfigException if [[db]] is invalid.\n     */\n    public function init()\n    {\n        parent::init();\n        if ($this->db !== null) {\n            $this->db = Instance::ensure($this->db);\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function prepareModels()\n    {\n        if (!$this->query instanceof QueryInterface) {\n            throw new InvalidConfigException('The \"query\" property must be an instance of a class that implements the QueryInterface e.g. yii\\db\\Query or its subclasses.');\n        }\n        $query = clone $this->query;\n        if (($pagination = $this->getPagination()) !== false) {\n            $pagination->totalCount = $this->getTotalCount();\n            if ($pagination->totalCount === 0) {\n                return [];\n            }\n            $query->limit($pagination->getLimit())->offset($pagination->getOffset());\n        }\n        if (($sort = $this->getSort()) !== false) {\n            $query->addOrderBy($sort->getOrders());\n        }\n\n        return $query->all($this->db);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function prepareKeys($models)\n    {\n        $keys = [];\n        if ($this->key !== null) {\n            foreach ($models as $model) {\n                if (is_string($this->key)) {\n                    $keys[] = $model[$this->key];\n                } else {\n                    $keys[] = call_user_func($this->key, $model);\n                }\n            }\n\n            return $keys;\n        } elseif ($this->query instanceof ActiveQueryInterface) {\n            /** @var \\yii\\db\\ActiveRecordInterface $class */\n            $class = $this->query->modelClass;\n            $pks = $class::primaryKey();\n            if (count($pks) === 1) {\n                $pk = $pks[0];\n                foreach ($models as $model) {\n                    $keys[] = $model[$pk];\n                }\n            } else {\n                foreach ($models as $model) {\n                    $kk = [];\n                    foreach ($pks as $pk) {\n                        $kk[$pk] = $model[$pk];\n                    }\n                    $keys[] = $kk;\n                }\n            }\n\n            return $keys;\n        }\n\n        return array_keys($models);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function prepareTotalCount()\n    {\n        if (!$this->query instanceof QueryInterface) {\n            throw new InvalidConfigException('The \"query\" property must be an instance of a class that implements the QueryInterface e.g. yii\\db\\Query or its subclasses.');\n        }\n        $query = clone $this->query;\n        return (int) $query->limit(-1)->offset(-1)->orderBy([])->count('*', $this->db);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function setSort($value)\n    {\n        parent::setSort($value);\n        if ($this->query instanceof ActiveQueryInterface && ($sort = $this->getSort()) !== false) {\n            /** @var Model $modelClass */\n            $modelClass = $this->query->modelClass;\n            $model = $modelClass::instance();\n            if (empty($sort->attributes)) {\n                foreach ($model->attributes() as $attribute) {\n                    $sort->attributes[$attribute] = [\n                        'asc' => [$attribute => SORT_ASC],\n                        'desc' => [$attribute => SORT_DESC],\n                    ];\n                }\n            }\n            if ($sort->modelClass === null) {\n                $sort->modelClass = $modelClass;\n            }\n        }\n    }\n\n    public function __clone()\n    {\n        if (is_object($this->query)) {\n            $this->query = clone $this->query;\n        }\n\n        parent::__clone();\n    }\n}\n"
  },
  {
    "path": "framework/data/ArrayDataProvider.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\data;\n\nuse yii\\helpers\\ArrayHelper;\n\n/**\n * ArrayDataProvider implements a data provider based on a data array.\n *\n * The [[allModels]] property contains all data models that may be sorted and/or paginated.\n * ArrayDataProvider will provide the data after sorting and/or pagination.\n * You may configure the [[sort]] and [[pagination]] properties to\n * customize the sorting and pagination behaviors.\n *\n * Elements in the [[allModels]] array may be either objects (e.g. model objects)\n * or associative arrays (e.g. query results of DAO).\n * Make sure to set the [[key]] property to the name of the field that uniquely\n * identifies a data record or false if you do not have such a field.\n *\n * Compared to [[ActiveDataProvider]], ArrayDataProvider could be less efficient\n * because it needs to have [[allModels]] ready.\n *\n * ArrayDataProvider may be used in the following way:\n *\n * ```\n * $query = new Query;\n * $provider = new ArrayDataProvider([\n *     'allModels' => $query->from('post')->all(),\n *     'sort' => [\n *         'attributes' => ['id', 'username', 'email'],\n *     ],\n *     'pagination' => [\n *         'pageSize' => 10,\n *     ],\n * ]);\n * // get the posts in the current page\n * $posts = $provider->getModels();\n * ```\n *\n * Note: if you want to use the sorting feature, you must configure the [[sort]] property\n * so that the provider knows which columns can be sorted.\n *\n * For more details and usage information on ArrayDataProvider, see the [guide article on data providers](guide:output-data-providers).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass ArrayDataProvider extends BaseDataProvider\n{\n    /**\n     * @var string|array|callable|null the column that is used as the key of the data models.\n     * This can be either a column name, a dot-separated path, an array of keys, or a callable\n     * that returns the key value of a given data model.\n     * If this is not set, the index of the [[models]] array will be used.\n     * @see getKeys()\n     */\n    public $key;\n    /**\n     * @var array the data that is not paginated or sorted. When pagination is enabled,\n     * this property usually contains more elements than [[models]].\n     * The array elements must use zero-based integer keys.\n     */\n    public $allModels;\n    /**\n     * @var string the name of the [[\\yii\\base\\Model|Model]] class that will be represented.\n     * This property is used to get columns' names.\n     * @since 2.0.9\n     */\n    public $modelClass;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function prepareModels()\n    {\n        if (($models = $this->allModels) === null) {\n            return [];\n        }\n\n        if (($sort = $this->getSort()) !== false) {\n            $models = $this->sortModels($models, $sort);\n        }\n\n        if (($pagination = $this->getPagination()) !== false) {\n            $pagination->totalCount = $this->getTotalCount();\n\n            if ($pagination->getPageSize() > 0) {\n                $models = array_slice($models, $pagination->getOffset(), $pagination->getLimit(), true);\n            }\n        }\n\n        return $models;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function prepareKeys($models)\n    {\n        if ($this->key !== null) {\n            return ArrayHelper::getColumn($models, $this->key, false);\n        }\n\n        return array_keys($models);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function prepareTotalCount()\n    {\n        return is_array($this->allModels) ? count($this->allModels) : 0;\n    }\n\n    /**\n     * Sorts the data models according to the given sort definition.\n     * @param array $models the models to be sorted\n     * @param Sort $sort the sort definition\n     * @return array the sorted data models\n     */\n    protected function sortModels($models, $sort)\n    {\n        $orders = $sort->getOrders();\n        if (!empty($orders)) {\n            ArrayHelper::multisort($models, array_keys($orders), array_values($orders), $sort->sortFlags);\n        }\n\n        return $models;\n    }\n}\n"
  },
  {
    "path": "framework/data/BaseDataProvider.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\data;\n\nuse Yii;\nuse yii\\base\\Component;\nuse yii\\base\\InvalidArgumentException;\n\n/**\n * BaseDataProvider provides a base class that implements the [[DataProviderInterface]].\n *\n * For more details and usage information on BaseDataProvider, see the [guide article on data providers](guide:output-data-providers).\n *\n * @property-read int $count The number of data models in the current page.\n * @property array $keys The list of key values corresponding to [[models]]. Each data model in [[models]] is\n * uniquely identified by the corresponding key value in this array.\n * @property array $models The list of data models in the current page.\n * @property Pagination|false $pagination The pagination object. If this is false, it means the pagination is\n * disabled. Note that the type of this property differs in getter and setter. See [[getPagination()]] and\n * [[setPagination()]] for details.\n * @property Sort|bool $sort The sorting object. If this is false, it means the sorting is disabled. Note that\n * the type of this property differs in getter and setter. See [[getSort()]] and [[setSort()]] for details.\n * @property int $totalCount Total number of possible data models.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n * @phpcs:disable Squiz.NamingConventions.ValidVariableName.PrivateNoUnderscore\n */\nabstract class BaseDataProvider extends Component implements DataProviderInterface\n{\n    /**\n     * @var int Number of data providers on the current page. Used to generate unique IDs.\n     */\n    private static $counter = 0;\n    /**\n     * @var string|null an ID that uniquely identifies the data provider among all data providers.\n     * Generated automatically the following way in case it is not set:\n     *\n     * - First data provider ID is empty.\n     * - Second and all subsequent data provider IDs are: \"dp-1\", \"dp-2\", etc.\n     */\n    public $id;\n\n    private $_sort;\n    private $_pagination;\n    private $_keys;\n    private $_models;\n    private $_totalCount;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        if ($this->id === null) {\n            if (self::$counter > 0) {\n                $this->id = 'dp-' . self::$counter;\n            }\n            self::$counter++;\n        }\n    }\n\n    /**\n     * Prepares the data models that will be made available in the current page.\n     * @return array the available data models\n     */\n    abstract protected function prepareModels();\n\n    /**\n     * Prepares the keys associated with the currently available data models.\n     * @param array $models the available data models\n     * @return array the keys\n     */\n    abstract protected function prepareKeys($models);\n\n    /**\n     * Returns a value indicating the total number of data models in this data provider.\n     * @return int total number of data models in this data provider.\n     */\n    abstract protected function prepareTotalCount();\n\n    /**\n     * Prepares the data models and keys.\n     *\n     * This method will prepare the data models and keys that can be retrieved via\n     * [[getModels()]] and [[getKeys()]].\n     *\n     * This method will be implicitly called by [[getModels()]] and [[getKeys()]] if it has not been called before.\n     *\n     * @param bool $forcePrepare whether to force data preparation even if it has been done before.\n     */\n    public function prepare($forcePrepare = false)\n    {\n        if ($forcePrepare || $this->_models === null) {\n            $this->_models = $this->prepareModels();\n        }\n        if ($forcePrepare || $this->_keys === null) {\n            $this->_keys = $this->prepareKeys($this->_models);\n        }\n    }\n\n    /**\n     * Returns the data models in the current page.\n     * @return array the list of data models in the current page.\n     */\n    public function getModels()\n    {\n        $this->prepare();\n\n        return $this->_models;\n    }\n\n    /**\n     * Sets the data models in the current page.\n     * @param array $models the models in the current page\n     */\n    public function setModels($models)\n    {\n        $this->_models = $models;\n    }\n\n    /**\n     * Returns the key values associated with the data models.\n     * @return array the list of key values corresponding to [[models]]. Each data model in [[models]]\n     * is uniquely identified by the corresponding key value in this array.\n     */\n    public function getKeys()\n    {\n        $this->prepare();\n\n        return $this->_keys;\n    }\n\n    /**\n     * Sets the key values associated with the data models.\n     * @param array $keys the list of key values corresponding to [[models]].\n     */\n    public function setKeys($keys)\n    {\n        $this->_keys = $keys;\n    }\n\n    /**\n     * Returns the number of data models in the current page.\n     * @return int the number of data models in the current page.\n     */\n    public function getCount()\n    {\n        return count($this->getModels());\n    }\n\n    /**\n     * Returns the total number of data models.\n     * When [[pagination]] is false, this returns the same value as [[count]].\n     * Otherwise, it will call [[prepareTotalCount()]] to get the count.\n     * @return int total number of possible data models.\n     */\n    public function getTotalCount()\n    {\n        if ($this->getPagination() === false) {\n            return $this->getCount();\n        } elseif ($this->_totalCount === null) {\n            $this->_totalCount = $this->prepareTotalCount();\n        }\n        return $this->_totalCount;\n    }\n\n    /**\n     * Sets the total number of data models.\n     * @param int $value the total number of data models.\n     */\n    public function setTotalCount($value)\n    {\n        $this->_totalCount = $value;\n    }\n\n    /**\n     * Returns the pagination object used by this data provider.\n     * Note that you should call [[prepare()]] or [[getModels()]] first to get correct values\n     * of [[Pagination::totalCount]] and [[Pagination::pageCount]].\n     * @return Pagination|false the pagination object. If this is false, it means the pagination is disabled.\n     */\n    public function getPagination()\n    {\n        if ($this->_pagination === null) {\n            $this->setPagination([]);\n        }\n\n        return $this->_pagination;\n    }\n\n    /**\n     * Sets the pagination for this data provider.\n     * @param array|Pagination|bool $value the pagination to be used by this data provider.\n     * This can be one of the following:\n     *\n     * - a configuration array for creating the pagination object. The \"class\" element defaults\n     *   to 'yii\\data\\Pagination'\n     * - an instance of [[Pagination]] or its subclass\n     * - false, if pagination needs to be disabled.\n     *\n     * @throws InvalidArgumentException\n     */\n    public function setPagination($value)\n    {\n        if (is_array($value)) {\n            $config = ['class' => Pagination::className()];\n            if ($this->id !== null) {\n                $config['pageParam'] = $this->id . '-page';\n                $config['pageSizeParam'] = $this->id . '-per-page';\n            }\n            $this->_pagination = Yii::createObject(array_merge($config, $value));\n        } elseif ($value instanceof Pagination || $value === false) {\n            $this->_pagination = $value;\n        } else {\n            throw new InvalidArgumentException('Only Pagination instance, configuration array or false is allowed.');\n        }\n    }\n\n    /**\n     * Returns the sorting object used by this data provider.\n     * @return Sort|bool the sorting object. If this is false, it means the sorting is disabled.\n     */\n    public function getSort()\n    {\n        if ($this->_sort === null) {\n            $this->setSort([]);\n        }\n\n        return $this->_sort;\n    }\n\n    /**\n     * Sets the sort definition for this data provider.\n     * @param array|Sort|bool $value the sort definition to be used by this data provider.\n     * This can be one of the following:\n     *\n     * - a configuration array for creating the sort definition object. The \"class\" element defaults\n     *   to 'yii\\data\\Sort'\n     * - an instance of [[Sort]] or its subclass\n     * - false, if sorting needs to be disabled.\n     *\n     * @throws InvalidArgumentException\n     */\n    public function setSort($value)\n    {\n        if (is_array($value)) {\n            $config = ['class' => Sort::className()];\n            if ($this->id !== null) {\n                $config['sortParam'] = $this->id . '-sort';\n            }\n            $this->_sort = Yii::createObject(array_merge($config, $value));\n        } elseif ($value instanceof Sort || $value === false) {\n            $this->_sort = $value;\n        } else {\n            throw new InvalidArgumentException('Only Sort instance, configuration array or false is allowed.');\n        }\n    }\n\n    /**\n     * Refreshes the data provider.\n     * After calling this method, if [[getModels()]], [[getKeys()]] or [[getTotalCount()]] is called again,\n     * they will re-execute the query and return the latest data available.\n     */\n    public function refresh()\n    {\n        $this->_totalCount = null;\n        $this->_models = null;\n        $this->_keys = null;\n    }\n}\n"
  },
  {
    "path": "framework/data/DataFilter.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\data;\n\nuse Yii;\nuse yii\\base\\InvalidConfigException;\nuse yii\\base\\Model;\nuse yii\\helpers\\ArrayHelper;\nuse yii\\validators\\BooleanValidator;\nuse yii\\validators\\EachValidator;\nuse yii\\validators\\NumberValidator;\nuse yii\\validators\\StringValidator;\nuse yii\\validators\\DateValidator;\nuse yii\\validators\\Validator;\n\n/**\n * DataFilter is a special [[Model]] for processing query filtering specification.\n * It allows validating and building a filter condition passed via request.\n *\n * Filter example:\n *\n * ```\n * {\n *     \"or\": [\n *         {\n *             \"and\": [\n *                 {\n *                     \"name\": \"some name\",\n *                 },\n *                 {\n *                     \"price\": \"25\",\n *                 }\n *             ]\n *         },\n *         {\n *             \"id\": {\"in\": [2, 5, 9]},\n *             \"price\": {\n *                 \"gt\": 10,\n *                 \"lt\": 50\n *             }\n *         }\n *     ]\n * }\n * ```\n *\n * In the request the filter should be specified using a key name equal to [[filterAttributeName]]. Thus actual HTTP request body\n * will look like following:\n *\n * ```\n * {\n *     \"filter\": {\"or\": {...}},\n *     \"page\": 2,\n *     ...\n * }\n * ```\n *\n * Raw filter value should be assigned to [[filter]] property of the model.\n * You may populate it from request data via [[load()]] method:\n *\n * ```\n * use yii\\data\\DataFilter;\n *\n * $dataFilter = new DataFilter();\n * $dataFilter->load(Yii::$app->request->getBodyParams());\n * ```\n *\n * In order to function this class requires a search model specified via [[searchModel]]. This search model should declare\n * all available search attributes and their validation rules. For example:\n *\n * ```\n * class SearchModel extends \\yii\\base\\Model\n * {\n *     public $id;\n *     public $name;\n *\n *     public function rules()\n *     {\n *         return [\n *             [['id', 'name'], 'trim'],\n *             ['id', 'integer'],\n *             ['name', 'string'],\n *         ];\n *     }\n * }\n * ```\n *\n * In order to reduce amount of classes, you may use [[\\yii\\base\\DynamicModel]] instance as a [[searchModel]].\n * In this case you should specify [[searchModel]] using a PHP callable:\n *\n * ```\n * function () {\n *     return (new \\yii\\base\\DynamicModel(['id' => null, 'name' => null]))\n *         ->addRule(['id', 'name'], 'trim')\n *         ->addRule('id', 'integer')\n *         ->addRule('name', 'string');\n * }\n * ```\n *\n * You can use [[validate()]] method to check if filter value is valid. If validation fails you can use\n * [[getErrors()]] to get actual error messages.\n *\n * In order to acquire filter condition suitable for fetching data use [[build()]] method.\n *\n * > Note: This is a base class. Its implementation of [[build()]] simply returns normalized [[filter]] value.\n * In order to convert filter to particular format you should use descendant of this class that implements\n * [[buildInternal()]] method accordingly.\n *\n * @see ActiveDataFilter\n *\n * @property array $errorMessages Error messages in format `[errorKey => message]`. Note that the type of this\n * property differs in getter and setter. See [[getErrorMessages()]] and [[setErrorMessages()]] for details.\n * @property mixed $filter Raw filter value.\n * @property array $searchAttributeTypes Search attribute type map. Note that the type of this property\n * differs in getter and setter. See [[getSearchAttributeTypes()]] and [[setSearchAttributeTypes()]] for details.\n * @property Model $searchModel Model instance. Note that the type of this property differs in getter and\n * setter. See [[getSearchModel()]] and [[setSearchModel()]] for details.\n *\n * @author Paul Klimov <klimov.paul@gmail.com>\n * @since 2.0.13\n */\nclass DataFilter extends Model\n{\n    public const TYPE_INTEGER = 'integer';\n    public const TYPE_FLOAT = 'float';\n    public const TYPE_BOOLEAN = 'boolean';\n    public const TYPE_STRING = 'string';\n    public const TYPE_ARRAY = 'array';\n    public const TYPE_DATETIME = 'datetime';\n    public const TYPE_DATE = 'date';\n    public const TYPE_TIME = 'time';\n    /**\n     * @var string name of the attribute that handles filter value.\n     * The name is used to load data via [[load()]] method.\n     */\n    public $filterAttributeName = 'filter';\n    /**\n     * @var string label for the filter attribute specified via [[filterAttributeName]].\n     * It will be used during error messages composition.\n     */\n    public $filterAttributeLabel;\n    /**\n     * @var array keywords or expressions that could be used in a filter.\n     * Array keys are the expressions used in raw filter value obtained from user request.\n     * Array values are internal build keys used in this class methods.\n     *\n     * Any unspecified keyword will not be recognized as a filter control and will be treated as\n     * an attribute name. Thus you should avoid conflicts between control keywords and attribute names.\n     * For example: in case you have control keyword 'like' and an attribute named 'like', specifying condition\n     * for such attribute will be impossible.\n     *\n     * You may specify several keywords for the same filter build key, creating multiple aliases. For example:\n     *\n     * ```\n     * [\n     *     'eq' => '=',\n     *     '=' => '=',\n     *     '==' => '=',\n     *     '===' => '=',\n     *     // ...\n     * ]\n     * ```\n     *\n     * > Note: while specifying filter controls take actual data exchange format, which your API uses, in mind.\n     * > Make sure each specified control keyword is valid for the format. For example, in XML tag name can start\n     * > only with a letter character, thus controls like `>`, '=' or `$gt` will break the XML schema.\n     */\n    public $filterControls = [\n        'and' => 'AND',\n        'or' => 'OR',\n        'not' => 'NOT',\n        'lt' => '<',\n        'gt' => '>',\n        'lte' => '<=',\n        'gte' => '>=',\n        'eq' => '=',\n        'neq' => '!=',\n        'in' => 'IN',\n        'nin' => 'NOT IN',\n        'like' => 'LIKE',\n    ];\n    /**\n     * @var array maps filter condition keywords to validation methods.\n     * These methods are used by [[validateCondition()]] to validate raw filter conditions.\n     */\n    public $conditionValidators = [\n        'AND' => 'validateConjunctionCondition',\n        'OR' => 'validateConjunctionCondition',\n        'NOT' => 'validateBlockCondition',\n        '<' => 'validateOperatorCondition',\n        '>' => 'validateOperatorCondition',\n        '<=' => 'validateOperatorCondition',\n        '>=' => 'validateOperatorCondition',\n        '=' => 'validateOperatorCondition',\n        '!=' => 'validateOperatorCondition',\n        'IN' => 'validateOperatorCondition',\n        'NOT IN' => 'validateOperatorCondition',\n        'LIKE' => 'validateOperatorCondition',\n    ];\n    /**\n     * @var array specifies the list of supported search attribute types per each operator.\n     * This field should be in format: 'operatorKeyword' => ['type1', 'type2' ...].\n     * Supported types list can be specified as `*`, which indicates that operator supports all types available.\n     * Any unspecified keyword will not be considered as a valid operator.\n     */\n    public $operatorTypes = [\n        '<' => [self::TYPE_INTEGER, self::TYPE_FLOAT, self::TYPE_DATETIME, self::TYPE_DATE, self::TYPE_TIME],\n        '>' => [self::TYPE_INTEGER, self::TYPE_FLOAT, self::TYPE_DATETIME, self::TYPE_DATE, self::TYPE_TIME],\n        '<=' => [self::TYPE_INTEGER, self::TYPE_FLOAT, self::TYPE_DATETIME, self::TYPE_DATE, self::TYPE_TIME],\n        '>=' => [self::TYPE_INTEGER, self::TYPE_FLOAT, self::TYPE_DATETIME, self::TYPE_DATE, self::TYPE_TIME],\n        '=' => '*',\n        '!=' => '*',\n        'IN' => '*',\n        'NOT IN' => '*',\n        'LIKE' => [self::TYPE_STRING],\n    ];\n    /**\n     * @var array list of operators keywords, which should accept multiple values.\n     */\n    public $multiValueOperators = [\n        'IN',\n        'NOT IN',\n    ];\n    /**\n     * @var array actual attribute names to be used in searched condition, in format: [filterAttribute => actualAttribute].\n     * For example, in case of using table joins in the search query, attribute map may look like the following:\n     *\n     * ```\n     * [\n     *     'authorName' => '{{author}}.[[name]]'\n     * ]\n     * ```\n     *\n     * Attribute map will be applied to filter condition in [[normalize()]] method.\n     */\n    public $attributeMap = [];\n    /**\n     * @var string representation of `null` instead of literal `null` in case the latter cannot be used.\n     * @since 2.0.40\n     */\n    public $nullValue = 'NULL';\n\n    /**\n     * @var array|\\Closure list of error messages responding to invalid filter structure, in format: `[errorKey => message]`.\n     */\n    private $_errorMessages;\n    /**\n     * @var mixed raw filter specification.\n     */\n    private $_filter;\n    /**\n     * @var Model|array|string|callable model to be used for filter attributes validation.\n     */\n    private $_searchModel;\n    /**\n     * @var array list of search attribute types in format: attributeName => type\n     */\n    private $_searchAttributeTypes;\n\n\n    /**\n     * @return mixed raw filter value.\n     */\n    public function getFilter()\n    {\n        return $this->_filter;\n    }\n\n    /**\n     * @param mixed $filter raw filter value.\n     */\n    public function setFilter($filter)\n    {\n        $this->_filter = $filter;\n    }\n\n    /**\n     * @return Model model instance.\n     * @throws InvalidConfigException on invalid configuration.\n     */\n    public function getSearchModel()\n    {\n        if (!is_object($this->_searchModel) || $this->_searchModel instanceof \\Closure) {\n            $model = Yii::createObject($this->_searchModel);\n            if (!$model instanceof Model) {\n                throw new InvalidConfigException('`' . get_class($this) . '::$searchModel` should be an instance of `' . Model::className() . '` or its DI compatible configuration.');\n            }\n            $this->_searchModel = $model;\n        }\n        return $this->_searchModel;\n    }\n\n    /**\n     * @param Model|array|string|callable $model model instance or its DI compatible configuration.\n     * @throws InvalidConfigException on invalid configuration.\n     */\n    public function setSearchModel($model)\n    {\n        if (is_object($model) && !$model instanceof Model && !$model instanceof \\Closure) {\n            throw new InvalidConfigException('`' . get_class($this) . '::$searchModel` should be an instance of `' . Model::className() . '` or its DI compatible configuration.');\n        }\n        $this->_searchModel = $model;\n    }\n\n    /**\n     * @return array search attribute type map.\n     */\n    public function getSearchAttributeTypes()\n    {\n        if ($this->_searchAttributeTypes === null) {\n            $this->_searchAttributeTypes = $this->detectSearchAttributeTypes();\n        }\n        return $this->_searchAttributeTypes;\n    }\n\n    /**\n     * @param array|null $searchAttributeTypes search attribute type map.\n     */\n    public function setSearchAttributeTypes($searchAttributeTypes)\n    {\n        $this->_searchAttributeTypes = $searchAttributeTypes;\n    }\n\n    /**\n     * Composes default value for [[searchAttributeTypes]] from the [[searchModel]] validation rules.\n     * @return array attribute type map.\n     */\n    protected function detectSearchAttributeTypes()\n    {\n        $model = $this->getSearchModel();\n\n        $attributeTypes = [];\n        foreach ($model->activeAttributes() as $attribute) {\n            $attributeTypes[$attribute] = self::TYPE_STRING;\n        }\n\n        foreach ($model->getValidators() as $validator) {\n            $type = $this->detectSearchAttributeType($validator);\n\n            if ($type !== null) {\n                foreach ((array) $validator->attributes as $attribute) {\n                    $attributeTypes[$attribute] = $type;\n                }\n            }\n        }\n\n        return $attributeTypes;\n    }\n\n    /**\n     * Detect attribute type from given validator.\n     *\n     * @param Validator $validator validator from which to detect attribute type.\n     * @return string|null detected attribute type.\n     * @since 2.0.14\n     */\n    protected function detectSearchAttributeType(Validator $validator)\n    {\n        if ($validator instanceof BooleanValidator) {\n            return self::TYPE_BOOLEAN;\n        }\n\n        if ($validator instanceof NumberValidator) {\n            return $validator->integerOnly ? self::TYPE_INTEGER : self::TYPE_FLOAT;\n        }\n\n        if ($validator instanceof StringValidator) {\n            return self::TYPE_STRING;\n        }\n\n        if ($validator instanceof EachValidator) {\n            return self::TYPE_ARRAY;\n        }\n\n        if ($validator instanceof DateValidator) {\n            if ($validator->type == DateValidator::TYPE_DATETIME) {\n                return self::TYPE_DATETIME;\n            }\n\n            if ($validator->type == DateValidator::TYPE_TIME) {\n                return self::TYPE_TIME;\n            }\n            return self::TYPE_DATE;\n        }\n\n        return null;\n    }\n\n    /**\n     * @return array error messages in format `[errorKey => message]`.\n     */\n    public function getErrorMessages()\n    {\n        if (!is_array($this->_errorMessages)) {\n            if ($this->_errorMessages === null) {\n                $this->_errorMessages = $this->defaultErrorMessages();\n            } else {\n                $this->_errorMessages = array_merge(\n                    $this->defaultErrorMessages(),\n                    call_user_func($this->_errorMessages)\n                );\n            }\n        }\n        return $this->_errorMessages;\n    }\n\n    /**\n     * Sets the list of error messages responding to invalid filter structure, in format: `[errorKey => message]`.\n     * Message may contain placeholders that will be populated depending on the message context.\n     * For each message a `{filter}` placeholder is available referring to the label for [[filterAttributeName]] attribute.\n     * @param array|\\Closure $errorMessages error messages in `[errorKey => message]` format, or a PHP callback returning them.\n     */\n    public function setErrorMessages($errorMessages)\n    {\n        if (is_array($errorMessages)) {\n            $errorMessages = array_merge($this->defaultErrorMessages(), $errorMessages);\n        }\n        $this->_errorMessages = $errorMessages;\n    }\n\n    /**\n     * Returns default values for [[errorMessages]].\n     * @return array default error messages in `[errorKey => message]` format.\n     */\n    protected function defaultErrorMessages()\n    {\n        return [\n            'invalidFilter' => Yii::t('yii', 'The format of {filter} is invalid.'),\n            'operatorRequireMultipleOperands' => Yii::t('yii', 'Operator \"{operator}\" requires multiple operands.'),\n            'unknownAttribute' => Yii::t('yii', 'Unknown filter attribute \"{attribute}\"'),\n            'invalidAttributeValueFormat' => Yii::t('yii', 'Condition for \"{attribute}\" should be either a value or valid operator specification.'),\n            'operatorRequireAttribute' => Yii::t('yii', 'Operator \"{operator}\" must be used with a search attribute.'),\n            'unsupportedOperatorType' => Yii::t('yii', '\"{attribute}\" does not support operator \"{operator}\".'),\n        ];\n    }\n\n    /**\n     * Parses content of the message from [[errorMessages]], specified by message key.\n     * @param string $messageKey message key.\n     * @param array $params params to be parsed into the message.\n     * @return string composed message string.\n     */\n    protected function parseErrorMessage($messageKey, $params = [])\n    {\n        $messages = $this->getErrorMessages();\n        if (isset($messages[$messageKey])) {\n            $message = $messages[$messageKey];\n        } else {\n            $message = Yii::t('yii', 'The format of {filter} is invalid.');\n        }\n\n        $params = array_merge(\n            [\n                'filter' => $this->getAttributeLabel($this->filterAttributeName),\n            ],\n            $params\n        );\n\n        return Yii::$app->getI18n()->format($message, $params, Yii::$app->language);\n    }\n\n    // Model specific:\n\n    /**\n     * {@inheritdoc}\n     */\n    public function attributes()\n    {\n        return [\n            $this->filterAttributeName,\n        ];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function formName()\n    {\n        return '';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function rules()\n    {\n        return [\n            [$this->filterAttributeName, 'validateFilter', 'skipOnEmpty' => false],\n        ];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function attributeLabels()\n    {\n        return [\n            $this->filterAttributeName => $this->filterAttributeLabel,\n        ];\n    }\n\n    // Validation:\n\n    /**\n     * Validates filter attribute value to match filer condition specification.\n     */\n    public function validateFilter()\n    {\n        $value = $this->getFilter();\n        if ($value !== null) {\n            $this->validateCondition($value);\n        }\n    }\n\n    /**\n     * Validates filter condition.\n     * @param mixed $condition raw filter condition.\n     */\n    protected function validateCondition($condition)\n    {\n        if (!is_array($condition)) {\n            $this->addError($this->filterAttributeName, $this->parseErrorMessage('invalidFilter'));\n            return;\n        }\n\n        if (empty($condition)) {\n            return;\n        }\n\n        foreach ($condition as $key => $value) {\n            $method = 'validateAttributeCondition';\n            if (isset($this->filterControls[$key])) {\n                $controlKey = $this->filterControls[$key];\n                if (isset($this->conditionValidators[$controlKey])) {\n                    $method = $this->conditionValidators[$controlKey];\n                }\n            }\n            $this->$method($key, $value);\n        }\n    }\n\n    /**\n     * Validates conjunction condition that consists of multiple independent ones.\n     * This covers such operators as `and` and `or`.\n     * @param string $operator raw operator control keyword.\n     * @param mixed $condition raw condition.\n     */\n    protected function validateConjunctionCondition($operator, $condition)\n    {\n        if (!is_array($condition) || !ArrayHelper::isIndexed($condition)) {\n            $this->addError($this->filterAttributeName, $this->parseErrorMessage('operatorRequireMultipleOperands', ['operator' => $operator]));\n            return;\n        }\n\n        foreach ($condition as $part) {\n            $this->validateCondition($part);\n        }\n    }\n\n    /**\n     * Validates block condition that consists of a single condition.\n     * This covers such operators as `not`.\n     * @param string $operator raw operator control keyword.\n     * @param mixed $condition raw condition.\n     */\n    protected function validateBlockCondition($operator, $condition)\n    {\n        $this->validateCondition($condition);\n    }\n\n    /**\n     * Validates search condition for a particular attribute.\n     * @param string $attribute search attribute name.\n     * @param mixed $condition search condition.\n     */\n    protected function validateAttributeCondition($attribute, $condition)\n    {\n        $attributeTypes = $this->getSearchAttributeTypes();\n        if (!isset($attributeTypes[$attribute])) {\n            $this->addError($this->filterAttributeName, $this->parseErrorMessage('unknownAttribute', ['attribute' => $attribute]));\n            return;\n        }\n\n        if (is_array($condition)) {\n            $operatorCount = 0;\n            foreach ($condition as $rawOperator => $value) {\n                if (isset($this->filterControls[$rawOperator])) {\n                    $operator = $this->filterControls[$rawOperator];\n                    if (isset($this->operatorTypes[$operator])) {\n                        $operatorCount++;\n                        $this->validateOperatorCondition($rawOperator, $value, $attribute);\n                    }\n                }\n            }\n\n            if ($operatorCount > 0) {\n                if ($operatorCount < count($condition)) {\n                    $this->addError($this->filterAttributeName, $this->parseErrorMessage('invalidAttributeValueFormat', ['attribute' => $attribute]));\n                }\n            } else {\n                // attribute may allow array value:\n                $this->validateAttributeValue($attribute, $condition);\n            }\n        } else {\n            $this->validateAttributeValue($attribute, $condition);\n        }\n    }\n\n    /**\n     * Validates operator condition.\n     * @param string $operator raw operator control keyword.\n     * @param mixed $condition attribute condition.\n     * @param string|null $attribute attribute name.\n     */\n    protected function validateOperatorCondition($operator, $condition, $attribute = null)\n    {\n        if ($attribute === null) {\n            // absence of an attribute indicates that operator has been placed in a wrong position\n            $this->addError($this->filterAttributeName, $this->parseErrorMessage('operatorRequireAttribute', ['operator' => $operator]));\n            return;\n        }\n\n        $internalOperator = $this->filterControls[$operator];\n\n        // check operator type :\n        $operatorTypes = $this->operatorTypes[$internalOperator];\n        if ($operatorTypes !== '*') {\n            $attributeTypes = $this->getSearchAttributeTypes();\n            $attributeType = $attributeTypes[$attribute];\n            if (!in_array($attributeType, $operatorTypes, true)) {\n                $this->addError($this->filterAttributeName, $this->parseErrorMessage('unsupportedOperatorType', ['attribute' => $attribute, 'operator' => $operator]));\n                return;\n            }\n        }\n\n        if (in_array($internalOperator, $this->multiValueOperators, true)) {\n            // multi-value operator:\n            if (!is_array($condition)) {\n                $this->addError($this->filterAttributeName, $this->parseErrorMessage('operatorRequireMultipleOperands', ['operator' => $operator]));\n            } else {\n                foreach ($condition as $v) {\n                    $this->validateAttributeValue($attribute, $v);\n                }\n            }\n        } else {\n            // single-value operator :\n            $this->validateAttributeValue($attribute, $condition);\n        }\n    }\n\n    /**\n     * Validates attribute value in the scope of [[model]].\n     * @param string $attribute attribute name.\n     * @param mixed $value attribute value.\n     */\n    protected function validateAttributeValue($attribute, $value)\n    {\n        $model = $this->getSearchModel();\n        if (!$model->isAttributeSafe($attribute)) {\n            $this->addError($this->filterAttributeName, $this->parseErrorMessage('unknownAttribute', ['attribute' => $attribute]));\n            return;\n        }\n\n        $model->{$attribute} = $value === $this->nullValue ? null : $value;\n        if (!$model->validate([$attribute])) {\n            $this->addError($this->filterAttributeName, $model->getFirstError($attribute));\n            return;\n        }\n    }\n\n    /**\n     * Validates attribute value in the scope of [[searchModel]], applying attribute value filters if any.\n     * @param string $attribute attribute name.\n     * @param mixed $value attribute value.\n     * @return mixed filtered attribute value.\n     */\n    protected function filterAttributeValue($attribute, $value)\n    {\n        $model = $this->getSearchModel();\n        if (!$model->isAttributeSafe($attribute)) {\n            $this->addError($this->filterAttributeName, $this->parseErrorMessage('unknownAttribute', ['attribute' => $attribute]));\n            return $value;\n        }\n        $model->{$attribute} = $value;\n        if (!$model->validate([$attribute])) {\n            $this->addError($this->filterAttributeName, $model->getFirstError($attribute));\n            return $value;\n        }\n\n        return $model->{$attribute};\n    }\n\n    // Build:\n\n    /**\n     * Builds actual filter specification form [[filter]] value.\n     * @param bool $runValidation whether to perform validation (calling [[validate()]])\n     * before building the filter. Defaults to `true`. If the validation fails, no filter will\n     * be built and this method will return `false`.\n     * @return mixed|false built actual filter value, or `false` if validation fails.\n     */\n    public function build($runValidation = true)\n    {\n        if ($runValidation && !$this->validate()) {\n            return false;\n        }\n        return $this->buildInternal();\n    }\n\n    /**\n     * Performs actual filter build.\n     * By default this method returns result of [[normalize()]].\n     * The child class may override this method providing more specific implementation.\n     * @return mixed built actual filter value.\n     */\n    protected function buildInternal()\n    {\n        return $this->normalize(false);\n    }\n\n    /**\n     * Normalizes filter value, replacing raw keys according to [[filterControls]] and [[attributeMap]].\n     * @param bool $runValidation whether to perform validation (calling [[validate()]])\n     * before normalizing the filter. Defaults to `true`. If the validation fails, no filter will\n     * be processed and this method will return `false`.\n     * @return array|bool normalized filter value, or `false` if validation fails.\n     */\n    public function normalize($runValidation = true)\n    {\n        if ($runValidation && !$this->validate()) {\n            return false;\n        }\n\n        $filter = $this->getFilter();\n        if (!is_array($filter) || empty($filter)) {\n            return [];\n        }\n\n        return $this->normalizeComplexFilter($filter);\n    }\n\n    /**\n     * Normalizes complex filter recursively.\n     * @param array $filter raw filter.\n     * @return array normalized filter.\n     */\n    private function normalizeComplexFilter(array $filter)\n    {\n        $result = [];\n        foreach ($filter as $key => $value) {\n            if (isset($this->filterControls[$key])) {\n                $key = $this->filterControls[$key];\n            } elseif (isset($this->attributeMap[$key])) {\n                $key = $this->attributeMap[$key];\n            }\n            if (is_array($value)) {\n                $result[$key] = $this->normalizeComplexFilter($value);\n            } elseif ($value === $this->nullValue) {\n                $result[$key] = null;\n            } else {\n                $result[$key] = $value;\n            }\n        }\n        return $result;\n    }\n\n    // Property access:\n\n    /**\n     * {@inheritdoc}\n     */\n    public function canGetProperty($name, $checkVars = true, $checkBehaviors = true)\n    {\n        if ($name === $this->filterAttributeName) {\n            return true;\n        }\n        return parent::canGetProperty($name, $checkVars, $checkBehaviors);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function canSetProperty($name, $checkVars = true, $checkBehaviors = true)\n    {\n        if ($name === $this->filterAttributeName) {\n            return true;\n        }\n        return parent::canSetProperty($name, $checkVars, $checkBehaviors);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function __get($name)\n    {\n        if ($name === $this->filterAttributeName) {\n            return $this->getFilter();\n        }\n\n        return parent::__get($name);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function __set($name, $value)\n    {\n        if ($name === $this->filterAttributeName) {\n            $this->setFilter($value);\n        } else {\n            parent::__set($name, $value);\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function __isset($name)\n    {\n        if ($name === $this->filterAttributeName) {\n            return $this->getFilter() !== null;\n        }\n\n        return parent::__isset($name);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function __unset($name)\n    {\n        if ($name === $this->filterAttributeName) {\n            $this->setFilter(null);\n        } else {\n            parent::__unset($name);\n        }\n    }\n}\n"
  },
  {
    "path": "framework/data/DataProviderInterface.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\data;\n\n/**\n * DataProviderInterface is the interface that must be implemented by data provider classes.\n *\n * Data providers are components that sort and paginate data, and provide them to widgets\n * such as [[\\yii\\grid\\GridView]], [[\\yii\\widgets\\ListView]].\n *\n * For more details and usage information on DataProviderInterface, see the [guide article on data providers](guide:output-data-providers).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\ninterface DataProviderInterface\n{\n    /**\n     * Prepares the data models and keys.\n     *\n     * This method will prepare the data models and keys that can be retrieved via\n     * [[getModels()]] and [[getKeys()]].\n     *\n     * This method will be implicitly called by [[getModels()]] and [[getKeys()]] if it has not been called before.\n     *\n     * @param bool $forcePrepare whether to force data preparation even if it has been done before.\n     */\n    public function prepare($forcePrepare = false);\n\n    /**\n     * Returns the number of data models in the current page.\n     * This is equivalent to `count($provider->getModels())`.\n     * When [[getPagination|pagination]] is false, this is the same as [[getTotalCount|totalCount]].\n     * @return int the number of data models in the current page.\n     */\n    public function getCount();\n\n    /**\n     * Returns the total number of data models.\n     * When [[getPagination|pagination]] is false, this is the same as [[getCount|count]].\n     * @return int total number of possible data models.\n     */\n    public function getTotalCount();\n\n    /**\n     * Returns the data models in the current page.\n     * @return array the list of data models in the current page.\n     */\n    public function getModels();\n\n    /**\n     * Returns the key values associated with the data models.\n     * @return array the list of key values corresponding to [[getModels|models]]. Each data model in [[getModels|models]]\n     * is uniquely identified by the corresponding key value in this array.\n     */\n    public function getKeys();\n\n    /**\n     * @return Sort|false the sorting object. If this is false, it means the sorting is disabled.\n     */\n    public function getSort();\n\n    /**\n     * @return Pagination|false the pagination object. If this is false, it means the pagination is disabled.\n     */\n    public function getPagination();\n}\n"
  },
  {
    "path": "framework/data/Pagination.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\data;\n\nuse Yii;\nuse yii\\base\\BaseObject;\nuse yii\\web\\Link;\nuse yii\\web\\Linkable;\nuse yii\\web\\Request;\n\n/**\n * Pagination represents information relevant to pagination of data items.\n *\n * When data needs to be rendered in multiple pages, Pagination can be used to\n * represent information such as [[totalCount|total item count]], [[pageSize|page size]],\n * [[page|current page]], etc. These information can be passed to [[\\yii\\widgets\\LinkPager|pagers]]\n * to render pagination buttons or links.\n *\n * The following example shows how to create a pagination object and feed it\n * to a pager.\n *\n * Controller action:\n *\n * ```\n * public function actionIndex()\n * {\n *     $query = Article::find()->where(['status' => 1]);\n *     $countQuery = clone $query;\n *     $pages = new Pagination(['totalCount' => $countQuery->count()]);\n *     $models = $query->offset($pages->offset)\n *         ->limit($pages->limit)\n *         ->all();\n *\n *     return $this->render('index', [\n *          'models' => $models,\n *          'pages' => $pages,\n *     ]);\n * }\n * ```\n *\n * View:\n *\n * ```\n * foreach ($models as $model) {\n *     // display $model here\n * }\n *\n * // display pagination\n * echo LinkPager::widget([\n *     'pagination' => $pages,\n * ]);\n * ```\n *\n * For more details and usage information on Pagination, see the [guide article on pagination](guide:output-pagination).\n *\n * @property-read int $limit The limit of the data. This may be used to set the LIMIT value for a SQL\n * statement for fetching the current page of data. Note that if the page size is infinite, a value -1 will be\n * returned.\n * @property-read array $links The links for navigational purpose. The array keys specify the purpose of the\n * links (e.g. [[LINK_FIRST]]), and the array values are the corresponding URLs.\n * @property-read int $offset The offset of the data. This may be used to set the OFFSET value for a SQL\n * statement for fetching the current page of data.\n * @property int $page The zero-based current page number.\n * @property-read int $pageCount Number of pages.\n * @property int $pageSize The number of items per page. If it is less than 1, it means the page size is\n * infinite, and thus a single page contains all items.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Pagination extends BaseObject implements Linkable\n{\n    public const LINK_NEXT = 'next';\n    public const LINK_PREV = 'prev';\n    public const LINK_FIRST = 'first';\n    public const LINK_LAST = 'last';\n    /**\n     * @var string name of the parameter storing the current page index.\n     * @see params\n     */\n    public $pageParam = 'page';\n    /**\n     * @var string name of the parameter storing the page size.\n     * @see params\n     */\n    public $pageSizeParam = 'per-page';\n    /**\n     * @var bool whether to always have the page parameter in the URL created by [[createUrl()]].\n     * If false and [[page]] is 0, the page parameter will not be put in the URL.\n     */\n    public $forcePageParam = true;\n    /**\n     * @var string|null the route of the controller action for displaying the paged contents.\n     * If not set, it means using the currently requested route.\n     */\n    public $route;\n    /**\n     * @var array|null parameters (name => value) that should be used to obtain the current page number\n     * and to create new pagination URLs. If not set, all parameters from $_GET will be used instead.\n     *\n     * In order to add hash to all links use `array_merge($_GET, ['#' => 'my-hash'])`.\n     *\n     * The array element indexed by [[pageParam]] is considered to be the current page number (defaults to 0);\n     * while the element indexed by [[pageSizeParam]] is treated as the page size (defaults to [[defaultPageSize]]).\n     */\n    public $params;\n    /**\n     * @var \\yii\\web\\UrlManager|null the URL manager used for creating pagination URLs. If not set,\n     * the \"urlManager\" application component will be used.\n     */\n    public $urlManager;\n    /**\n     * @var bool whether to check if [[page]] is within valid range.\n     * When this property is true, the value of [[page]] will always be between 0 and ([[pageCount]]-1).\n     * Because [[pageCount]] relies on the correct value of [[totalCount]] which may not be available\n     * in some cases (e.g. MongoDB), you may want to set this property to be false to disable the page\n     * number validation. By doing so, [[page]] will return the value indexed by [[pageParam]] in [[params]].\n     */\n    public $validatePage = true;\n    /**\n     * @var int total number of items.\n     */\n    public $totalCount = 0;\n    /**\n     * @var int the default page size. This property will be returned by [[pageSize]] when page size\n     * cannot be determined by [[pageSizeParam]] from [[params]].\n     */\n    public $defaultPageSize = 20;\n    /**\n     * @var array|false the page size limits. The first array element defines the minimum page size, and the second\n     * the maximum page size. If this is false, it means [[pageSize]] should always return the value of [[defaultPageSize]].\n     */\n    public $pageSizeLimit = [1, 50];\n\n    /**\n     * @var int|null number of items on each page.\n     * If it is less than 1, it means the page size is infinite, and thus a single page contains all items.\n     */\n    private $_pageSize;\n\n\n    /**\n     * @return int number of pages\n     */\n    public function getPageCount()\n    {\n        $pageSize = $this->getPageSize();\n        if ($pageSize < 1) {\n            return $this->totalCount > 0 ? 1 : 0;\n        }\n\n        $totalCount = $this->totalCount < 0 ? 0 : (int) $this->totalCount;\n\n        return (int) (($totalCount + $pageSize - 1) / $pageSize);\n    }\n\n    private $_page;\n\n    /**\n     * Returns the zero-based current page number.\n     * @param bool $recalculate whether to recalculate the current page based on the page size and item count.\n     * @return int the zero-based current page number.\n     */\n    public function getPage($recalculate = false)\n    {\n        if ($this->_page === null || $recalculate) {\n            $page = (int) $this->getQueryParam($this->pageParam, 1) - 1;\n            $this->setPage($page, true);\n        }\n\n        return $this->_page;\n    }\n\n    /**\n     * Sets the current page number.\n     * @param int $value the zero-based index of the current page.\n     * @param bool $validatePage whether to validate the page number. Note that in order\n     * to validate the page number, both [[validatePage]] and this parameter must be true.\n     */\n    public function setPage($value, $validatePage = false)\n    {\n        if ($value === null) {\n            $this->_page = null;\n        } else {\n            $value = (int) $value;\n            if ($validatePage && $this->validatePage) {\n                $pageCount = $this->getPageCount();\n                if ($value >= $pageCount) {\n                    $value = $pageCount - 1;\n                }\n            }\n            if ($value < 0) {\n                $value = 0;\n            }\n            $this->_page = $value;\n        }\n    }\n\n    /**\n     * Returns the number of items per page.\n     * By default, this method will try to determine the page size by [[pageSizeParam]] in [[params]].\n     * If the page size cannot be determined this way, [[defaultPageSize]] will be returned.\n     * @return int the number of items per page. If it is less than 1, it means the page size is infinite,\n     * and thus a single page contains all items.\n     * @see pageSizeLimit\n     */\n    public function getPageSize()\n    {\n        if ($this->_pageSize === null) {\n            if (empty($this->pageSizeLimit) || !isset($this->pageSizeLimit[0], $this->pageSizeLimit[1])) {\n                $pageSize = $this->defaultPageSize;\n                $this->setPageSize($pageSize);\n            } else {\n                $pageSize = (int) $this->getQueryParam($this->pageSizeParam, $this->defaultPageSize);\n                $this->setPageSize($pageSize, true);\n            }\n        }\n\n        return $this->_pageSize;\n    }\n\n    /**\n     * @param int $value the number of items per page.\n     * @param bool $validatePageSize whether to validate page size.\n     */\n    public function setPageSize($value, $validatePageSize = false)\n    {\n        if ($value === null) {\n            $this->_pageSize = null;\n        } else {\n            $value = (int) $value;\n            if ($validatePageSize && isset($this->pageSizeLimit[0], $this->pageSizeLimit[1])) {\n                if ($value < $this->pageSizeLimit[0]) {\n                    $value = $this->pageSizeLimit[0];\n                } elseif ($value > $this->pageSizeLimit[1]) {\n                    $value = $this->pageSizeLimit[1];\n                }\n            }\n            $this->_pageSize = $value;\n        }\n    }\n\n    /**\n     * Creates the URL suitable for pagination with the specified page number.\n     * This method is mainly called by pagers when creating URLs used to perform pagination.\n     * @param int $page the zero-based page number that the URL should point to.\n     * @param int|null $pageSize the number of items on each page. If not set, the value of [[pageSize]] will be used.\n     * @param bool $absolute whether to create an absolute URL. Defaults to `false`.\n     * @return string the created URL\n     * @see params\n     * @see forcePageParam\n     */\n    public function createUrl($page, $pageSize = null, $absolute = false)\n    {\n        $page = (int) $page;\n        $pageSize = (int) $pageSize;\n        if (($params = $this->params) === null) {\n            $request = Yii::$app->getRequest();\n            $params = $request instanceof Request ? $request->getQueryParams() : [];\n        }\n        if ($page > 0 || $page == 0 && $this->forcePageParam) {\n            $params[$this->pageParam] = $page + 1;\n        } else {\n            unset($params[$this->pageParam]);\n        }\n        if ($pageSize <= 0) {\n            $pageSize = $this->getPageSize();\n        }\n        if ($pageSize != $this->defaultPageSize) {\n            $params[$this->pageSizeParam] = $pageSize;\n        } else {\n            unset($params[$this->pageSizeParam]);\n        }\n        $params[0] = $this->route === null ? Yii::$app->controller->getRoute() : $this->route;\n        $urlManager = $this->urlManager === null ? Yii::$app->getUrlManager() : $this->urlManager;\n        if ($absolute) {\n            return $urlManager->createAbsoluteUrl($params);\n        }\n\n        return $urlManager->createUrl($params);\n    }\n\n    /**\n     * @return int the offset of the data. This may be used to set the\n     * OFFSET value for a SQL statement for fetching the current page of data.\n     */\n    public function getOffset()\n    {\n        $pageSize = $this->getPageSize();\n\n        return $pageSize < 1 ? 0 : $this->getPage() * $pageSize;\n    }\n\n    /**\n     * @return int the limit of the data. This may be used to set the\n     * LIMIT value for a SQL statement for fetching the current page of data.\n     * Note that if the page size is infinite, a value -1 will be returned.\n     */\n    public function getLimit()\n    {\n        $pageSize = $this->getPageSize();\n\n        return $pageSize < 1 ? -1 : $pageSize;\n    }\n\n    /**\n     * Returns a whole set of links for navigating to the first, last, next and previous pages.\n     * @param bool $absolute whether the generated URLs should be absolute.\n     * @return array the links for navigational purpose. The array keys specify the purpose of the links (e.g. [[LINK_FIRST]]),\n     * and the array values are the corresponding URLs.\n     */\n    public function getLinks($absolute = false)\n    {\n        $currentPage = $this->getPage();\n        $pageCount = $this->getPageCount();\n\n        $links = [Link::REL_SELF => $this->createUrl($currentPage, null, $absolute)];\n        if ($pageCount > 0) {\n            $links[self::LINK_FIRST] = $this->createUrl(0, null, $absolute);\n            $links[self::LINK_LAST] = $this->createUrl($pageCount - 1, null, $absolute);\n            if ($currentPage > 0) {\n                $links[self::LINK_PREV] = $this->createUrl($currentPage - 1, null, $absolute);\n            }\n            if ($currentPage < $pageCount - 1) {\n                $links[self::LINK_NEXT] = $this->createUrl($currentPage + 1, null, $absolute);\n            }\n        }\n\n        return $links;\n    }\n\n    /**\n     * Returns the value of the specified query parameter.\n     * This method returns the named parameter value from [[params]]. Null is returned if the value does not exist.\n     * @param string $name the parameter name\n     * @param string|null $defaultValue the value to be returned when the specified parameter does not exist in [[params]].\n     * @return string|null the parameter value\n     */\n    protected function getQueryParam($name, $defaultValue = null)\n    {\n        if (($params = $this->params) === null) {\n            $request = Yii::$app->getRequest();\n            $params = $request instanceof Request ? $request->getQueryParams() : [];\n        }\n\n        return isset($params[$name]) && is_scalar($params[$name]) ? $params[$name] : $defaultValue;\n    }\n}\n"
  },
  {
    "path": "framework/data/Sort.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\data;\n\nuse Yii;\nuse yii\\base\\BaseObject;\nuse yii\\base\\InvalidConfigException;\nuse yii\\helpers\\Html;\nuse yii\\helpers\\Inflector;\nuse yii\\web\\Request;\n\n/**\n * Sort represents information relevant to sorting.\n *\n * When data needs to be sorted according to one or several attributes,\n * we can use Sort to represent the sorting information and generate\n * appropriate hyperlinks that can lead to sort actions.\n *\n * A typical usage example is as follows,\n *\n * ```\n * public function actionIndex()\n * {\n *     $sort = new Sort([\n *         'attributes' => [\n *             'age',\n *             'name' => [\n *                 'asc' => ['first_name' => SORT_ASC, 'last_name' => SORT_ASC],\n *                 'desc' => ['first_name' => SORT_DESC, 'last_name' => SORT_DESC],\n *                 'default' => SORT_DESC,\n *                 'label' => 'Name',\n *             ],\n *         ],\n *     ]);\n *\n *     $models = Article::find()\n *         ->where(['status' => 1])\n *         ->orderBy($sort->orders)\n *         ->all();\n *\n *     return $this->render('index', [\n *          'models' => $models,\n *          'sort' => $sort,\n *     ]);\n * }\n * ```\n *\n * View:\n *\n * ```\n * // display links leading to sort actions\n * echo $sort->link('name') . ' | ' . $sort->link('age');\n *\n * foreach ($models as $model) {\n *     // display $model here\n * }\n * ```\n *\n * In the above, we declare two [[attributes]] that support sorting: `name` and `age`.\n * We pass the sort information to the Article query so that the query results are\n * sorted by the orders specified by the Sort object. In the view, we show two hyperlinks\n * that can lead to pages with the data sorted by the corresponding attributes.\n *\n * For more details and usage information on Sort, see the [guide article on sorting](guide:output-sorting).\n *\n * @property array $attributeOrders Sort directions indexed by attribute names. Sort direction can be either\n * `SORT_ASC` for ascending order or `SORT_DESC` for descending order. Note that the type of this property\n * differs in getter and setter. See [[getAttributeOrders()]] and [[setAttributeOrders()]] for details.\n * @property-read array $orders The columns (keys) and their corresponding sort directions (values). This can\n * be passed to [[\\yii\\db\\Query::orderBy()]] to construct a DB query.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Sort extends BaseObject\n{\n    /**\n     * @var bool whether the sorting can be applied to multiple attributes simultaneously.\n     * Defaults to `false`, which means each time the data can only be sorted by one attribute.\n     */\n    public $enableMultiSort = false;\n    /**\n     * @var array list of attributes that are allowed to be sorted. Its syntax can be\n     * described using the following example:\n     *\n     * ```\n     * [\n     *     'age',\n     *     'name' => [\n     *         'asc' => ['first_name' => SORT_ASC, 'last_name' => SORT_ASC],\n     *         'desc' => ['first_name' => SORT_DESC, 'last_name' => SORT_DESC],\n     *         'default' => SORT_DESC,\n     *         'label' => 'Name',\n     *     ],\n     * ]\n     * ```\n     *\n     * In the above, two attributes are declared: `age` and `name`. The `age` attribute is\n     * a simple attribute which is equivalent to the following:\n     *\n     * ```\n     * 'age' => [\n     *     'asc' => ['age' => SORT_ASC],\n     *     'desc' => ['age' => SORT_DESC],\n     *     'default' => SORT_ASC,\n     *     'label' => Inflector::camel2words('age'),\n     * ]\n     * ```\n     *\n     * Since 2.0.12 particular sort direction can be also specified as direct sort expression, like following:\n     *\n     * ```\n     * 'name' => [\n     *     'asc' => '[[last_name]] ASC NULLS FIRST', // PostgreSQL specific feature\n     *     'desc' => '[[last_name]] DESC NULLS LAST',\n     * ]\n     * ```\n     *\n     * The `name` attribute is a composite attribute:\n     *\n     * - The `name` key represents the attribute name which will appear in the URLs leading\n     *   to sort actions.\n     * - The `asc` and `desc` elements specify how to sort by the attribute in ascending\n     *   and descending orders, respectively. Their values represent the actual columns and\n     *   the directions by which the data should be sorted by.\n     * - The `default` element specifies by which direction the attribute should be sorted\n     *   if it is not currently sorted (the default value is ascending order).\n     * - The `label` element specifies what label should be used when calling [[link()]] to create\n     *   a sort link. If not set, [[Inflector::camel2words()]] will be called to get a label.\n     *   Note that it will not be HTML-encoded.\n     *\n     * Note that if the Sort object is already created, you can only use the full format\n     * to configure every attribute. Each attribute must include these elements: `asc` and `desc`.\n     */\n    public $attributes = [];\n    /**\n     * @var string the name of the parameter that specifies which attributes to be sorted\n     * in which direction. Defaults to `sort`.\n     * @see params\n     */\n    public $sortParam = 'sort';\n    /**\n     * @var array|null the order that should be used when the current request does not specify any order.\n     * The array keys are attribute names and the array values are the corresponding sort directions. For example,\n     *\n     * ```\n     * [\n     *     'name' => SORT_ASC,\n     *     'created_at' => SORT_DESC,\n     * ]\n     * ```\n     *\n     * @see attributeOrders\n     */\n    public $defaultOrder;\n    /**\n     * @var string|null the route of the controller action for displaying the sorted contents.\n     * If not set, it means using the currently requested route.\n     */\n    public $route;\n    /**\n     * @var string the character used to separate different attributes that need to be sorted by.\n     */\n    public $separator = ',';\n    /**\n     * @var array|null parameters (name => value) that should be used to obtain the current sort directions\n     * and to create new sort URLs. If not set, `$_GET` will be used instead.\n     *\n     * In order to add hash to all links use `array_merge($_GET, ['#' => 'my-hash'])`.\n     *\n     * The array element indexed by [[sortParam]] is considered to be the current sort directions.\n     * If the element does not exist, the [[defaultOrder|default order]] will be used.\n     *\n     * @see sortParam\n     * @see defaultOrder\n     */\n    public $params;\n    /**\n     * @var \\yii\\web\\UrlManager|null the URL manager used for creating sort URLs. If not set,\n     * the `urlManager` application component will be used.\n     */\n    public $urlManager;\n    /**\n     * @var int Allow to control a value of the fourth parameter which will be\n     * passed to [[ArrayHelper::multisort()]]\n     * @since 2.0.33\n     */\n    public $sortFlags = SORT_REGULAR;\n    /**\n     * @var string|null the name of the [[\\yii\\base\\Model]]-based class used by the [[link()]] method to retrieve\n     * attributes' labels. See [[link]] method for details.\n     * @since 2.0.49\n     */\n    public $modelClass;\n\n\n    /**\n     * Normalizes the [[attributes]] property.\n     */\n    public function init()\n    {\n        $attributes = [];\n        foreach ($this->attributes as $name => $attribute) {\n            if (!is_array($attribute)) {\n                $attributes[$attribute] = [\n                    'asc' => [$attribute => SORT_ASC],\n                    'desc' => [$attribute => SORT_DESC],\n                ];\n            } elseif (!isset($attribute['asc'], $attribute['desc'])) {\n                $attributes[$name] = array_merge([\n                    'asc' => [$name => SORT_ASC],\n                    'desc' => [$name => SORT_DESC],\n                ], $attribute);\n            } else {\n                $attributes[$name] = $attribute;\n            }\n        }\n        $this->attributes = $attributes;\n    }\n\n    /**\n     * Returns the columns and their corresponding sort directions.\n     * @param bool $recalculate whether to recalculate the sort directions\n     * @return array the columns (keys) and their corresponding sort directions (values).\n     * This can be passed to [[\\yii\\db\\Query::orderBy()]] to construct a DB query.\n     */\n    public function getOrders($recalculate = false)\n    {\n        $attributeOrders = $this->getAttributeOrders($recalculate);\n        $orders = [];\n        foreach ($attributeOrders as $attribute => $direction) {\n            $definition = $this->attributes[$attribute];\n            $columns = $definition[$direction === SORT_ASC ? 'asc' : 'desc'];\n            if (is_array($columns) || $columns instanceof \\Traversable) {\n                foreach ($columns as $name => $dir) {\n                    $orders[$name] = $dir;\n                }\n            } else {\n                $orders[] = $columns;\n            }\n        }\n\n        return $orders;\n    }\n\n    /**\n     * @var array the currently requested sort order as computed by [[getAttributeOrders]].\n     */\n    private $_attributeOrders;\n\n    /**\n     * Returns the currently requested sort information.\n     * @param bool $recalculate whether to recalculate the sort directions\n     * @return array sort directions indexed by attribute names.\n     * Sort direction can be either `SORT_ASC` for ascending order or\n     * `SORT_DESC` for descending order.\n     */\n    public function getAttributeOrders($recalculate = false)\n    {\n        if ($this->_attributeOrders === null || $recalculate) {\n            $this->_attributeOrders = [];\n            if (($params = $this->params) === null) {\n                $request = Yii::$app->getRequest();\n                $params = $request instanceof Request ? $request->getQueryParams() : [];\n            }\n            if (isset($params[$this->sortParam])) {\n                foreach ($this->parseSortParam($params[$this->sortParam]) as $attribute) {\n                    $descending = false;\n                    if (strncmp($attribute, '-', 1) === 0) {\n                        $descending = true;\n                        $attribute = substr($attribute, 1);\n                    }\n\n                    if (isset($this->attributes[$attribute])) {\n                        $this->_attributeOrders[$attribute] = $descending ? SORT_DESC : SORT_ASC;\n                        if (!$this->enableMultiSort) {\n                            return $this->_attributeOrders;\n                        }\n                    }\n                }\n\n                return $this->_attributeOrders;\n            }\n            if (empty($this->_attributeOrders) && is_array($this->defaultOrder)) {\n                $this->_attributeOrders = $this->defaultOrder;\n            }\n        }\n\n        return $this->_attributeOrders;\n    }\n\n    /**\n     * Parses the value of [[sortParam]] into an array of sort attributes.\n     *\n     * The format must be the attribute name only for ascending\n     * or the attribute name prefixed with `-` for descending.\n     *\n     * For example the following return value will result in ascending sort by\n     * `category` and descending sort by `created_at`:\n     *\n     * ```\n     * [\n     *     'category',\n     *     '-created_at'\n     * ]\n     * ```\n     *\n     * @param mixed $param the value of the [[sortParam]].\n     * @return array the valid sort attributes.\n     * @since 2.0.12\n     * @see separator for the attribute name separator.\n     * @see sortParam\n     */\n    protected function parseSortParam($param)\n    {\n        return is_scalar($param) ? explode($this->separator, $param) : [];\n    }\n\n    /**\n     * Sets up the currently sort information.\n     * @param array|null $attributeOrders sort directions indexed by attribute names.\n     * Sort direction can be either `SORT_ASC` for ascending order or\n     * `SORT_DESC` for descending order.\n     * @param bool $validate whether to validate given attribute orders against [[attributes]] and [[enableMultiSort]].\n     * If validation is enabled incorrect entries will be removed.\n     * @since 2.0.10\n     */\n    public function setAttributeOrders($attributeOrders, $validate = true)\n    {\n        if ($attributeOrders === null || !$validate) {\n            $this->_attributeOrders = $attributeOrders;\n        } else {\n            $this->_attributeOrders = [];\n            foreach ($attributeOrders as $attribute => $order) {\n                if (isset($this->attributes[$attribute])) {\n                    $this->_attributeOrders[$attribute] = $order;\n                    if (!$this->enableMultiSort) {\n                        break;\n                    }\n                }\n            }\n        }\n    }\n\n    /**\n     * Returns the sort direction of the specified attribute in the current request.\n     * @param string $attribute the attribute name\n     * @return int|null Sort direction of the attribute. Can be either `SORT_ASC`\n     * for ascending order or `SORT_DESC` for descending order. Null is returned\n     * if the attribute is invalid or does not need to be sorted.\n     */\n    public function getAttributeOrder($attribute)\n    {\n        $orders = $this->getAttributeOrders();\n\n        return isset($orders[$attribute]) ? $orders[$attribute] : null;\n    }\n\n    /**\n     * Generates a hyperlink that links to the sort action to sort by the specified attribute.\n     * Based on the sort direction, the CSS class of the generated hyperlink will be appended\n     * with \"asc\" or \"desc\".\n     * @param string $attribute the attribute name by which the data should be sorted by.\n     * @param array $options additional HTML attributes for the hyperlink tag.\n     * There is one special attribute `label` which will be used as the label of the hyperlink.\n     * If this is not set, the label defined in [[attributes]] will be used.\n     * If no label is defined, it will be retrieved from the instance of [[modelClass]] (if [[modelClass]] is not null)\n     * or generated from attribute name using [[\\yii\\helpers\\Inflector::camel2words()]].\n     * Note that it will not be HTML-encoded.\n     * @return string the generated hyperlink\n     * @throws InvalidConfigException if the attribute is unknown\n     */\n    public function link($attribute, $options = [])\n    {\n        if (($direction = $this->getAttributeOrder($attribute)) !== null) {\n            $class = $direction === SORT_DESC ? 'desc' : 'asc';\n            if (isset($options['class'])) {\n                $options['class'] .= ' ' . $class;\n            } else {\n                $options['class'] = $class;\n            }\n        }\n\n        $url = $this->createUrl($attribute);\n        $options['data-sort'] = $this->createSortParam($attribute);\n\n        if (isset($options['label'])) {\n            $label = $options['label'];\n            unset($options['label']);\n        } else {\n            if (isset($this->attributes[$attribute]['label'])) {\n                $label = $this->attributes[$attribute]['label'];\n            } elseif ($this->modelClass !== null) {\n                $modelClass = $this->modelClass;\n                /** @var \\yii\\base\\Model $model */\n                $model = $modelClass::instance();\n                $label = $model->getAttributeLabel($attribute);\n            } else {\n                $label = Inflector::camel2words($attribute);\n            }\n        }\n\n        return Html::a($label, $url, $options);\n    }\n\n    /**\n     * Creates a URL for sorting the data by the specified attribute.\n     * This method will consider the current sorting status given by [[attributeOrders]].\n     * For example, if the current page already sorts the data by the specified attribute in ascending order,\n     * then the URL created will lead to a page that sorts the data by the specified attribute in descending order.\n     * @param string $attribute the attribute name\n     * @param bool $absolute whether to create an absolute URL. Defaults to `false`.\n     * @return string the URL for sorting. False if the attribute is invalid.\n     * @throws InvalidConfigException if the attribute is unknown\n     * @see attributeOrders\n     * @see params\n     */\n    public function createUrl($attribute, $absolute = false)\n    {\n        if (($params = $this->params) === null) {\n            $request = Yii::$app->getRequest();\n            $params = $request instanceof Request ? $request->getQueryParams() : [];\n        }\n        $params[$this->sortParam] = $this->createSortParam($attribute);\n        $params[0] = $this->route === null ? Yii::$app->controller->getRoute() : $this->route;\n        $urlManager = $this->urlManager === null ? Yii::$app->getUrlManager() : $this->urlManager;\n        if ($absolute) {\n            return $urlManager->createAbsoluteUrl($params);\n        }\n\n        return $urlManager->createUrl($params);\n    }\n\n    /**\n     * Creates the sort variable for the specified attribute.\n     * The newly created sort variable can be used to create a URL that will lead to\n     * sorting by the specified attribute.\n     * @param string $attribute the attribute name\n     * @return string the value of the sort variable\n     * @throws InvalidConfigException if the specified attribute is not defined in [[attributes]]\n     */\n    public function createSortParam($attribute)\n    {\n        if (!isset($this->attributes[$attribute])) {\n            throw new InvalidConfigException(\"Unknown attribute: $attribute\");\n        }\n        $definition = $this->attributes[$attribute];\n        $directions = $this->getAttributeOrders();\n        if (isset($directions[$attribute])) {\n            if ($this->enableMultiSort) {\n                if ($directions[$attribute] === SORT_ASC) {\n                    $direction = SORT_DESC;\n                } else {\n                    $direction = null;\n                }\n            } else {\n                $direction = $directions[$attribute] === SORT_DESC ? SORT_ASC : SORT_DESC;\n            }\n\n            unset($directions[$attribute]);\n        } else {\n            $direction = isset($definition['default']) ? $definition['default'] : SORT_ASC;\n        }\n\n        if ($this->enableMultiSort) {\n            if ($direction !== null) {\n                $directions = array_merge([$attribute => $direction], $directions);\n            }\n        } else {\n            $directions = [$attribute => $direction];\n        }\n\n        $sorts = [];\n        foreach ($directions as $attribute => $direction) {\n            $sorts[] = $direction === SORT_DESC ? '-' . $attribute : $attribute;\n        }\n\n        return implode($this->separator, $sorts);\n    }\n\n    /**\n     * Returns a value indicating whether the sort definition supports sorting by the named attribute.\n     * @param string $name the attribute name\n     * @return bool whether the sort definition supports sorting by the named attribute.\n     */\n    public function hasAttribute($name)\n    {\n        return isset($this->attributes[$name]);\n    }\n}\n"
  },
  {
    "path": "framework/data/SqlDataProvider.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\data;\n\nuse yii\\base\\InvalidConfigException;\nuse yii\\db\\Connection;\nuse yii\\db\\Expression;\nuse yii\\db\\Query;\nuse yii\\di\\Instance;\n\n/**\n * SqlDataProvider implements a data provider based on a plain SQL statement.\n *\n * SqlDataProvider provides data in terms of arrays, each representing a row of query result.\n *\n * Like other data providers, SqlDataProvider also supports sorting and pagination.\n * It does so by modifying the given [[sql]] statement with \"ORDER BY\" and \"LIMIT\"\n * clauses. You may configure the [[sort]] and [[pagination]] properties to\n * customize sorting and pagination behaviors.\n *\n * SqlDataProvider may be used in the following way:\n *\n * ```\n * $count = Yii::$app->db->createCommand('\n *     SELECT COUNT(*) FROM user WHERE status=:status\n * ', [':status' => 1])->queryScalar();\n *\n * $dataProvider = new SqlDataProvider([\n *     'sql' => 'SELECT * FROM user WHERE status=:status',\n *     'params' => [':status' => 1],\n *     'totalCount' => $count,\n *     'sort' => [\n *         'attributes' => [\n *             'age',\n *             'name' => [\n *                 'asc' => ['first_name' => SORT_ASC, 'last_name' => SORT_ASC],\n *                 'desc' => ['first_name' => SORT_DESC, 'last_name' => SORT_DESC],\n *                 'default' => SORT_DESC,\n *                 'label' => 'Name',\n *             ],\n *         ],\n *     ],\n *     'pagination' => [\n *         'pageSize' => 20,\n *     ],\n * ]);\n *\n * // get the user records in the current page\n * $models = $dataProvider->getModels();\n * ```\n *\n * Note: if you want to use the pagination feature, you must configure the [[totalCount]] property\n * to be the total number of rows (without pagination). And if you want to use the sorting feature,\n * you must configure the [[sort]] property so that the provider knows which columns can be sorted.\n *\n * For more details and usage information on SqlDataProvider, see the [guide article on data providers](guide:output-data-providers).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass SqlDataProvider extends BaseDataProvider\n{\n    /**\n     * @var Connection|array|string the DB connection object or the application component ID of the DB connection.\n     * Starting from version 2.0.2, this can also be a configuration array for creating the object.\n     */\n    public $db = 'db';\n    /**\n     * @var string the SQL statement to be used for fetching data rows.\n     */\n    public $sql;\n    /**\n     * @var array parameters (name=>value) to be bound to the SQL statement.\n     */\n    public $params = [];\n    /**\n     * @var string|callable|null the column that is used as the key of the data models.\n     * This can be either a column name, or a callable that returns the key value of a given data model.\n     *\n     * If this is not set, the keys of the [[models]] array will be used.\n     */\n    public $key;\n\n\n    /**\n     * Initializes the DB connection component.\n     * This method will initialize the [[db]] property to make sure it refers to a valid DB connection.\n     * @throws InvalidConfigException if [[db]] is invalid.\n     */\n    public function init()\n    {\n        parent::init();\n        $this->db = Instance::ensure($this->db, Connection::className());\n        if ($this->sql === null) {\n            throw new InvalidConfigException('The \"sql\" property must be set.');\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function prepareModels()\n    {\n        $sort = $this->getSort();\n        $pagination = $this->getPagination();\n        if ($pagination === false && $sort === false) {\n            return $this->db->createCommand($this->sql, $this->params)->queryAll();\n        }\n\n        $sql = $this->sql;\n        $orders = [];\n        $limit = $offset = null;\n\n        if ($sort !== false) {\n            $orders = $sort->getOrders();\n            $pattern = '/\\s+order\\s+by\\s+([\\w\\s,\\.\"`\\[\\]]+)$/i';\n            if (preg_match($pattern, $sql, $matches)) {\n                array_unshift($orders, new Expression($matches[1]));\n                $sql = preg_replace($pattern, '', $sql);\n            }\n        }\n\n        if ($pagination !== false) {\n            $pagination->totalCount = $this->getTotalCount();\n            $limit = $pagination->getLimit();\n            $offset = $pagination->getOffset();\n        }\n\n        $sql = $this->db->getQueryBuilder()->buildOrderByAndLimit($sql, $orders, $limit, $offset);\n\n        return $this->db->createCommand($sql, $this->params)->queryAll();\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function prepareKeys($models)\n    {\n        $keys = [];\n        if ($this->key !== null) {\n            foreach ($models as $model) {\n                if (is_string($this->key)) {\n                    $keys[] = $model[$this->key];\n                } else {\n                    $keys[] = call_user_func($this->key, $model);\n                }\n            }\n\n            return $keys;\n        }\n\n        return array_keys($models);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function prepareTotalCount()\n    {\n        return (new Query([\n            'from' => ['sub' => \"({$this->sql})\"],\n            'params' => $this->params,\n        ]))->count('*', $this->db);\n    }\n}\n"
  },
  {
    "path": "framework/db/ActiveQuery.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\nuse yii\\base\\InvalidConfigException;\n\n/**\n * ActiveQuery represents a DB query associated with an Active Record class.\n *\n * An ActiveQuery can be a normal query or be used in a relational context.\n *\n * ActiveQuery instances are usually created by [[ActiveRecord::find()]] and [[ActiveRecord::findBySql()]].\n * Relational queries are created by [[ActiveRecord::hasOne()]] and [[ActiveRecord::hasMany()]].\n *\n * Normal Query\n * ------------\n *\n * ActiveQuery mainly provides the following methods to retrieve the query results:\n *\n * - [[one()]]: returns a single record populated with the first row of data.\n * - [[all()]]: returns all records based on the query results.\n * - [[count()]]: returns the number of records.\n * - [[sum()]]: returns the sum over the specified column.\n * - [[average()]]: returns the average over the specified column.\n * - [[min()]]: returns the min over the specified column.\n * - [[max()]]: returns the max over the specified column.\n * - [[scalar()]]: returns the value of the first column in the first row of the query result.\n * - [[column()]]: returns the value of the first column in the query result.\n * - [[exists()]]: returns a value indicating whether the query result has data or not.\n *\n * Because ActiveQuery extends from [[Query]], one can use query methods, such as [[where()]],\n * [[orderBy()]] to customize the query options.\n *\n * ActiveQuery also provides the following additional query options:\n *\n * - [[with()]]: list of relations that this query should be performed with.\n * - [[joinWith()]]: reuse a relation query definition to add a join to a query.\n * - [[indexBy()]]: the name of the column by which the query result should be indexed.\n * - [[asArray()]]: whether to return each record as an array.\n *\n * These options can be configured using methods of the same name. For example:\n *\n * ```\n * $customers = Customer::find()->with('orders')->asArray()->all();\n * ```\n *\n * Relational query\n * ----------------\n *\n * In relational context ActiveQuery represents a relation between two Active Record classes.\n *\n * Relational ActiveQuery instances are usually created by calling [[ActiveRecord::hasOne()]] and\n * [[ActiveRecord::hasMany()]]. An Active Record class declares a relation by defining\n * a getter method which calls one of the above methods and returns the created ActiveQuery object.\n *\n * A relation is specified by [[link]] which represents the association between columns\n * of different tables; and the multiplicity of the relation is indicated by [[multiple]].\n *\n * If a relation involves a junction table, it may be specified by [[via()]] or [[viaTable()]] method.\n * These methods may only be called in a relational context. Same is true for [[inverseOf()]], which\n * marks a relation as inverse of another relation and [[onCondition()]] which adds a condition that\n * is to be added to relational query join condition.\n *\n * @template T of ActiveRecord|array = ActiveRecord|array<array-key, mixed>\n *\n * @method T|null one($db = null) See [[ActiveQueryInterface::one()]] for more info.\n * @method T[] all($db = null) See [[ActiveQueryInterface::all()]] for more info.\n * @method ($value is true ? (T is array ? static<T> : static<array<string, mixed>>) : static<T>) asArray($value = true) Sets the [[asArray]] property.\n * @method BatchQueryResult<int, T[]> batch($batchSize = 100, $db = null) the batch query result. It implements the [[\\Iterator]] interface\n * and can be traversed to retrieve the data in batches.\n * @method BatchQueryResult<int, T> each($batchSize = 100, $db = null) the batch query result. It implements the [[\\Iterator]] interface\n * and can be traversed to retrieve the data in batches.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0\n */\nclass ActiveQuery extends Query implements ActiveQueryInterface\n{\n    use ActiveQueryTrait;\n    use ActiveRelationTrait;\n\n    /**\n     * @event Event an event that is triggered when the query is initialized via [[init()]].\n     */\n    public const EVENT_INIT = 'init';\n    /**\n     * @var string|null the SQL statement to be executed for retrieving AR records.\n     * This is set by [[ActiveRecord::findBySql()]].\n     */\n    public $sql;\n    /**\n     * @var string|array|null the join condition to be used when this query is used in a relational context.\n     * The condition will be used in the ON part when [[ActiveQuery::joinWith()]] is called.\n     * Otherwise, the condition will be used in the WHERE part of a query.\n     * Please refer to [[Query::where()]] on how to specify this parameter.\n     * @see onCondition()\n     */\n    public $on;\n    /**\n     * @var array|null a list of relations that this query should be joined with\n     */\n    public $joinWith;\n\n\n    /**\n     * Constructor.\n     * @param class-string<ActiveRecordInterface> $modelClass the model class associated with this query\n     * @param array $config configurations to be applied to the newly created query object\n     */\n    public function __construct($modelClass, $config = [])\n    {\n        $this->modelClass = $modelClass;\n        parent::__construct($config);\n    }\n\n    /**\n     * Initializes the object.\n     * This method is called at the end of the constructor. The default implementation will trigger\n     * an [[EVENT_INIT]] event. If you override this method, make sure you call the parent implementation at the end\n     * to ensure triggering of the event.\n     */\n    public function init()\n    {\n        parent::init();\n        $this->trigger(self::EVENT_INIT);\n    }\n\n    /**\n     * Executes query and returns all results as an array.\n     * @param Connection|null $db the DB connection used to create the DB command.\n     * If null, the DB connection returned by [[modelClass]] will be used.\n     * @return T[] the query results. If the query results in nothing, an empty array will be returned.\n     */\n    public function all($db = null)\n    {\n        return parent::all($db);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function prepare($builder)\n    {\n        // NOTE: because the same ActiveQuery may be used to build different SQL statements\n        // (e.g. by ActiveDataProvider, one for count query, the other for row data query,\n        // it is important to make sure the same ActiveQuery can be used to build SQL statements\n        // multiple times.\n        if (!empty($this->joinWith)) {\n            $this->buildJoinWith();\n            $this->joinWith = null;    // clean it up to avoid issue https://github.com/yiisoft/yii2/issues/2687\n        }\n\n        if (empty($this->from)) {\n            $this->from = [$this->getPrimaryTableName()];\n        }\n\n        if (empty($this->select) && !empty($this->join)) {\n            list(, $alias) = $this->getTableNameAndAlias();\n            $this->select = [\"$alias.*\"];\n        }\n\n        if ($this->primaryModel === null) {\n            // eager loading\n            $query = Query::create($this);\n        } else {\n            // lazy loading of a relation\n            $where = $this->where;\n\n            if ($this->via instanceof self) {\n                // via junction table\n                $viaModels = $this->via->findJunctionRows([$this->primaryModel]);\n                $this->filterByModels($viaModels);\n            } elseif (is_array($this->via)) {\n                // via relation\n                /** @var self<ActiveRecord|array<string, mixed>> $viaQuery */\n                list($viaName, $viaQuery, $viaCallableUsed) = $this->via;\n                if ($viaQuery->multiple) {\n                    if ($viaCallableUsed) {\n                        $viaModels = $viaQuery->all();\n                    } elseif ($this->primaryModel->isRelationPopulated($viaName)) {\n                        $viaModels = $this->primaryModel->$viaName;\n                    } else {\n                        $viaModels = $viaQuery->all();\n                        $this->primaryModel->populateRelation($viaName, $viaModels);\n                    }\n                } else {\n                    if ($viaCallableUsed) {\n                        $model = $viaQuery->one();\n                    } elseif ($this->primaryModel->isRelationPopulated($viaName)) {\n                        $model = $this->primaryModel->$viaName;\n                    } else {\n                        $model = $viaQuery->one();\n                        $this->primaryModel->populateRelation($viaName, $model);\n                    }\n                    $viaModels = $model === null ? [] : [$model];\n                }\n                $this->filterByModels($viaModels);\n            } else {\n                $this->filterByModels([$this->primaryModel]);\n            }\n\n            $query = Query::create($this);\n            $this->where = $where;\n        }\n\n        if (!empty($this->on)) {\n            $query->andWhere($this->on);\n        }\n\n        return $query;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function populate($rows)\n    {\n        if (empty($rows)) {\n            return [];\n        }\n\n        $models = $this->createModels($rows);\n        if (!empty($this->join) && $this->indexBy === null) {\n            $models = $this->removeDuplicatedModels($models);\n        }\n        if (!empty($this->with)) {\n            $this->findWith($this->with, $models);\n        }\n\n        if ($this->inverseOf !== null) {\n            $this->addInverseRelations($models);\n        }\n\n        if (!$this->asArray) {\n            foreach ($models as $model) {\n                $model->afterFind();\n            }\n        }\n\n        return parent::populate($models);\n    }\n\n    /**\n     * Removes duplicated models by checking their primary key values.\n     * This method is mainly called when a join query is performed, which may cause duplicated rows being returned.\n     * @param array $models the models to be checked\n     * @throws InvalidConfigException if model primary key is empty\n     * @return array the distinctive models\n     */\n    private function removeDuplicatedModels($models)\n    {\n        $hash = [];\n        /** @var class-string<ActiveRecord> $class */\n        $class = $this->modelClass;\n        $pks = $class::primaryKey();\n\n        if (count($pks) > 1) {\n            // composite primary key\n            foreach ($models as $i => $model) {\n                $key = [];\n                foreach ($pks as $pk) {\n                    if (!isset($model[$pk])) {\n                        // do not continue if the primary key is not part of the result set\n                        break 2;\n                    }\n                    $key[] = $model[$pk];\n                }\n                $key = serialize($key);\n                if (isset($hash[$key])) {\n                    unset($models[$i]);\n                } else {\n                    $hash[$key] = true;\n                }\n            }\n        } elseif (empty($pks)) {\n            throw new InvalidConfigException(\"Primary key of '{$class}' can not be empty.\");\n        } else {\n            // single column primary key\n            $pk = reset($pks);\n            foreach ($models as $i => $model) {\n                if (!isset($model[$pk])) {\n                    // do not continue if the primary key is not part of the result set\n                    break;\n                }\n                $key = $model[$pk];\n                if (isset($hash[$key])) {\n                    unset($models[$i]);\n                } elseif ($key !== null) {\n                    $hash[$key] = true;\n                }\n            }\n        }\n\n        return array_values($models);\n    }\n\n    /**\n     * Executes query and returns a single row of result.\n     * @param Connection|null $db the DB connection used to create the DB command.\n     * If `null`, the DB connection returned by [[modelClass]] will be used.\n     * @return T|null a single row of query result. Depending on the setting of [[asArray]],\n     * the query result may be either an array or an ActiveRecord object. `null` will be returned\n     * if the query results in nothing.\n     */\n    public function one($db = null)\n    {\n        $row = parent::one($db);\n        if ($row !== false) {\n            $models = $this->populate([$row]);\n            return reset($models) ?: null;\n        }\n\n        return null;\n    }\n\n    /**\n     * Creates a DB command that can be used to execute this query.\n     * @param Connection|null $db the DB connection used to create the DB command.\n     * If `null`, the DB connection returned by [[modelClass]] will be used.\n     * @return Command the created DB command instance.\n     */\n    public function createCommand($db = null)\n    {\n        /** @var ActiveRecord $modelClass */\n        $modelClass = $this->modelClass;\n        if ($db === null) {\n            $db = $modelClass::getDb();\n        }\n\n        if ($this->sql === null) {\n            list($sql, $params) = $db->getQueryBuilder()->build($this);\n        } else {\n            $sql = $this->sql;\n            $params = $this->params;\n        }\n\n        $command = $db->createCommand($sql, $params);\n        $this->setCommandCache($command);\n\n        return $command;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function queryScalar($selectExpression, $db)\n    {\n        /** @var ActiveRecord $modelClass */\n        $modelClass = $this->modelClass;\n        if ($db === null) {\n            $db = $modelClass::getDb();\n        }\n\n        if ($this->sql === null) {\n            return parent::queryScalar($selectExpression, $db);\n        }\n\n        $command = (new Query())->select([$selectExpression])\n            ->from(['c' => \"({$this->sql})\"])\n            ->params($this->params)\n            ->createCommand($db);\n        $this->setCommandCache($command);\n\n        return $command->queryScalar();\n    }\n\n    /**\n     * Joins with the specified relations.\n     *\n     * This method allows you to reuse existing relation definitions to perform JOIN queries.\n     * Based on the definition of the specified relation(s), the method will append one or multiple\n     * JOIN statements to the current query.\n     *\n     * If the `$eagerLoading` parameter is true, the method will also perform eager loading for the specified relations,\n     * which is equivalent to calling [[with()]] using the specified relations.\n     *\n     * Note that because a JOIN query will be performed, you are responsible to disambiguate column names.\n     *\n     * This method differs from [[with()]] in that it will build up and execute a JOIN SQL statement\n     * for the primary table. And when `$eagerLoading` is true, it will call [[with()]] in addition with the specified relations.\n     *\n     * @param string|array $with the relations to be joined. This can either be a string, representing a relation name or\n     * an array with the following semantics:\n     *\n     * - Each array element represents a single relation.\n     * - You may specify the relation name as the array key and provide an anonymous functions that\n     *   can be used to modify the relation queries on-the-fly as the array value.\n     * - If a relation query does not need modification, you may use the relation name as the array value.\n     *\n     * The relation name may optionally contain an alias for the relation table (e.g. `books b`).\n     *\n     * Sub-relations can also be specified, see [[with()]] for the syntax.\n     *\n     * In the following you find some examples:\n     *\n     * ```\n     * // find all orders that contain books, and eager loading \"books\"\n     * Order::find()->joinWith('books', true, 'INNER JOIN')->all();\n     * // find all orders, eager loading \"books\", and sort the orders and books by the book names.\n     * Order::find()->joinWith([\n     *     'books' => function (\\yii\\db\\ActiveQuery $query) {\n     *         $query->orderBy('item.name');\n     *     }\n     * ])->all();\n     * // find all orders that contain books of the category 'Science fiction', using the alias \"b\" for the books table\n     * Order::find()->joinWith(['books b'], true, 'INNER JOIN')->where(['b.category' => 'Science fiction'])->all();\n     * ```\n     *\n     * The alias syntax is available since version 2.0.7.\n     *\n     * @param bool|array $eagerLoading whether to eager load the relations\n     * specified in `$with`.  When this is a boolean, it applies to all\n     * relations specified in `$with`. Use an array to explicitly list which\n     * relations in `$with` need to be eagerly loaded.  Note, that this does\n     * not mean, that the relations are populated from the query result. An\n     * extra query will still be performed to bring in the related data.\n     * Defaults to `true`.\n     * @param string|array $joinType the join type of the relations specified in `$with`.\n     * When this is a string, it applies to all relations specified in `$with`. Use an array\n     * in the format of `relationName => joinType` to specify different join types for different relations.\n     * @return $this the query object itself\n     */\n    public function joinWith($with, $eagerLoading = true, $joinType = 'LEFT JOIN')\n    {\n        $relations = [];\n        foreach ((array) $with as $name => $callback) {\n            if (is_int($name)) {\n                $name = $callback;\n                $callback = null;\n            }\n\n            if (preg_match('/^(.*?)(?:\\s+AS\\s+|\\s+)(\\w+)$/i', $name, $matches)) {\n                // relation is defined with an alias, adjust callback to apply alias\n                list(, $relation, $alias) = $matches;\n                $name = $relation;\n                $callback = function ($query) use ($callback, $alias) {\n                    /** @var self<ActiveRecord|array<string, mixed>> $query */\n                    $query->alias($alias);\n                    if ($callback !== null) {\n                        call_user_func($callback, $query);\n                    }\n                };\n            }\n\n            if ($callback === null) {\n                $relations[] = $name;\n            } else {\n                $relations[$name] = $callback;\n            }\n        }\n        $this->joinWith[] = [$relations, $eagerLoading, $joinType];\n        return $this;\n    }\n\n    private function buildJoinWith()\n    {\n        $join = $this->join;\n        $this->join = [];\n\n        /** @var ActiveRecordInterface $modelClass */\n        $modelClass = $this->modelClass;\n        $model = $modelClass::instance();\n        foreach ($this->joinWith as $config) {\n            list($with, $eagerLoading, $joinType) = $config;\n            $this->joinWithRelations($model, $with, $joinType);\n\n            if (is_array($eagerLoading)) {\n                foreach ($with as $name => $callback) {\n                    if (is_int($name)) {\n                        if (!in_array($callback, $eagerLoading, true)) {\n                            unset($with[$name]);\n                        }\n                    } elseif (!in_array($name, $eagerLoading, true)) {\n                        unset($with[$name]);\n                    }\n                }\n            } elseif (!$eagerLoading) {\n                $with = [];\n            }\n\n            $this->with($with);\n        }\n\n        // remove duplicated joins added by joinWithRelations that may be added\n        // e.g. when joining a relation and a via relation at the same time\n        $uniqueJoins = [];\n        foreach ($this->join as $j) {\n            $uniqueJoins[serialize($j)] = $j;\n        }\n        $this->join = array_values($uniqueJoins);\n\n        // https://github.com/yiisoft/yii2/issues/16092\n        $uniqueJoinsByTableName = [];\n        foreach ($this->join as $config) {\n            $tableName = serialize($config[1]);\n            if (!array_key_exists($tableName, $uniqueJoinsByTableName)) {\n                $uniqueJoinsByTableName[$tableName] = $config;\n            }\n        }\n        $this->join = array_values($uniqueJoinsByTableName);\n\n        if (!empty($join)) {\n            // append explicit join to joinWith()\n            // https://github.com/yiisoft/yii2/issues/2880\n            $this->join = empty($this->join) ? $join : array_merge($this->join, $join);\n        }\n    }\n\n    /**\n     * Inner joins with the specified relations.\n     * This is a shortcut method to [[joinWith()]] with the join type set as \"INNER JOIN\".\n     * Please refer to [[joinWith()]] for detailed usage of this method.\n     * @param string|array $with the relations to be joined with.\n     * @param bool|array $eagerLoading whether to eager load the relations.\n     * Note, that this does not mean, that the relations are populated from the\n     * query result. An extra query will still be performed to bring in the\n     * related data.\n     * @return $this the query object itself\n     * @see joinWith()\n     */\n    public function innerJoinWith($with, $eagerLoading = true)\n    {\n        return $this->joinWith($with, $eagerLoading, 'INNER JOIN');\n    }\n\n    /**\n     * Modifies the current query by adding join fragments based on the given relations.\n     * @param ActiveRecord $model the primary model\n     * @param array $with the relations to be joined\n     * @param string|array $joinType the join type\n     */\n    private function joinWithRelations($model, $with, $joinType)\n    {\n        $relations = [];\n\n        foreach ($with as $name => $callback) {\n            if (is_int($name)) {\n                $name = $callback;\n                $callback = null;\n            }\n\n            $primaryModel = $model;\n            $parent = $this;\n            $prefix = '';\n            while (($pos = strpos($name, '.')) !== false) {\n                $childName = substr($name, $pos + 1);\n                $name = substr($name, 0, $pos);\n                $fullName = $prefix === '' ? $name : \"$prefix.$name\";\n                if (!isset($relations[$fullName])) {\n                    $relations[$fullName] = $relation = $primaryModel->getRelation($name);\n                    $this->joinWithRelation($parent, $relation, $this->getJoinType($joinType, $fullName));\n                } else {\n                    $relation = $relations[$fullName];\n                }\n                /** @var ActiveRecordInterface $relationModelClass */\n                $relationModelClass = $relation->modelClass;\n                $primaryModel = $relationModelClass::instance();\n                $parent = $relation;\n                $prefix = $fullName;\n                $name = $childName;\n            }\n\n            $fullName = $prefix === '' ? $name : \"$prefix.$name\";\n            if (!isset($relations[$fullName])) {\n                $relations[$fullName] = $relation = $primaryModel->getRelation($name);\n                if ($callback !== null) {\n                    call_user_func($callback, $relation);\n                }\n                if (!empty($relation->joinWith)) {\n                    $relation->buildJoinWith();\n                }\n                $this->joinWithRelation($parent, $relation, $this->getJoinType($joinType, $fullName));\n            }\n        }\n    }\n\n    /**\n     * Returns the join type based on the given join type parameter and the relation name.\n     * @param string|array $joinType the given join type(s)\n     * @param string $name relation name\n     * @return string the real join type\n     */\n    private function getJoinType($joinType, $name)\n    {\n        if (is_array($joinType) && isset($joinType[$name])) {\n            return $joinType[$name];\n        }\n\n        return is_string($joinType) ? $joinType : 'INNER JOIN';\n    }\n\n    /**\n     * Returns the table name and the table alias for [[modelClass]].\n     * @return array the table name and the table alias.\n     * @since 2.0.16\n     */\n    protected function getTableNameAndAlias()\n    {\n        if (empty($this->from)) {\n            $tableName = $this->getPrimaryTableName();\n        } else {\n            $tableName = '';\n            // if the first entry in \"from\" is an alias-tablename-pair return it directly\n            foreach ($this->from as $alias => $tableName) {\n                if (is_string($alias)) {\n                    return [$tableName, $alias];\n                }\n                break;\n            }\n        }\n\n        if (preg_match('/^(.*?)\\s+({{\\w+}}|\\w+)$/', $tableName, $matches)) {\n            $alias = $matches[2];\n        } else {\n            $alias = $tableName;\n        }\n\n        return [$tableName, $alias];\n    }\n\n    /**\n     * Joins a parent query with a child query.\n     * The current query object will be modified accordingly.\n     * @param ActiveQuery $parent\n     * @param ActiveQuery $child\n     * @param string $joinType\n     */\n    private function joinWithRelation($parent, $child, $joinType)\n    {\n        $via = $child->via;\n        $child->via = null;\n        if ($via instanceof self) {\n            // via table\n            $this->joinWithRelation($parent, $via, $joinType);\n            $this->joinWithRelation($via, $child, $joinType);\n            return;\n        } elseif (is_array($via)) {\n            // via relation\n            $this->joinWithRelation($parent, $via[1], $joinType);\n            $this->joinWithRelation($via[1], $child, $joinType);\n            return;\n        }\n\n        list($parentTable, $parentAlias) = $parent->getTableNameAndAlias();\n        list($childTable, $childAlias) = $child->getTableNameAndAlias();\n\n        if (!empty($child->link)) {\n            if (strpos($parentAlias, '{{') === false) {\n                $parentAlias = '{{' . $parentAlias . '}}';\n            }\n            if (strpos($childAlias, '{{') === false) {\n                $childAlias = '{{' . $childAlias . '}}';\n            }\n\n            $on = [];\n            foreach ($child->link as $childColumn => $parentColumn) {\n                $on[] = \"$parentAlias.[[$parentColumn]] = $childAlias.[[$childColumn]]\";\n            }\n            $on = implode(' AND ', $on);\n            if (!empty($child->on)) {\n                $on = ['and', $on, $child->on];\n            }\n        } else {\n            $on = $child->on;\n        }\n        $this->join($joinType, empty($child->from) ? $childTable : $child->from, $on);\n\n        if (!empty($child->where)) {\n            $this->andWhere($child->where);\n        }\n        if (!empty($child->having)) {\n            $this->andHaving($child->having);\n        }\n        if (!empty($child->orderBy)) {\n            $this->addOrderBy($child->orderBy);\n        }\n        if (!empty($child->groupBy)) {\n            $this->addGroupBy($child->groupBy);\n        }\n        if (!empty($child->params)) {\n            $this->addParams($child->params);\n        }\n        if (!empty($child->join)) {\n            foreach ($child->join as $join) {\n                $this->join[] = $join;\n            }\n        }\n        if (!empty($child->union)) {\n            foreach ($child->union as $union) {\n                $this->union[] = $union;\n            }\n        }\n    }\n\n    /**\n     * Sets the ON condition for a relational query.\n     * The condition will be used in the ON part when [[ActiveQuery::joinWith()]] is called.\n     * Otherwise, the condition will be used in the WHERE part of a query.\n     *\n     * Use this method to specify additional conditions when declaring a relation in the [[ActiveRecord]] class:\n     *\n     * ```\n     * public function getActiveUsers()\n     * {\n     *     return $this->hasMany(User::class, ['id' => 'user_id'])\n     *                 ->onCondition(['active' => true]);\n     * }\n     * ```\n     *\n     * Note that this condition is applied in case of a join as well as when fetching the related records.\n     * Thus only fields of the related table can be used in the condition. Trying to access fields of the primary\n     * record will cause an error in a non-join-query.\n     *\n     * @param string|array $condition the ON condition. Please refer to [[Query::where()]] on how to specify this parameter.\n     * @param array $params the parameters (name => value) to be bound to the query.\n     * @return $this the query object itself\n     */\n    public function onCondition($condition, $params = [])\n    {\n        $this->on = $condition;\n        $this->addParams($params);\n        return $this;\n    }\n\n    /**\n     * Adds an additional ON condition to the existing one.\n     * The new condition and the existing one will be joined using the 'AND' operator.\n     * @param string|array $condition the new ON condition. Please refer to [[where()]]\n     * on how to specify this parameter.\n     * @param array $params the parameters (name => value) to be bound to the query.\n     * @return $this the query object itself\n     * @see onCondition()\n     * @see orOnCondition()\n     */\n    public function andOnCondition($condition, $params = [])\n    {\n        if ($this->on === null) {\n            $this->on = $condition;\n        } else {\n            $this->on = ['and', $this->on, $condition];\n        }\n        $this->addParams($params);\n        return $this;\n    }\n\n    /**\n     * Adds an additional ON condition to the existing one.\n     * The new condition and the existing one will be joined using the 'OR' operator.\n     * @param string|array $condition the new ON condition. Please refer to [[where()]]\n     * on how to specify this parameter.\n     * @param array $params the parameters (name => value) to be bound to the query.\n     * @return $this the query object itself\n     * @see onCondition()\n     * @see andOnCondition()\n     */\n    public function orOnCondition($condition, $params = [])\n    {\n        if ($this->on === null) {\n            $this->on = $condition;\n        } else {\n            $this->on = ['or', $this->on, $condition];\n        }\n        $this->addParams($params);\n        return $this;\n    }\n\n    /**\n     * Specifies the junction table for a relational query.\n     *\n     * Use this method to specify a junction table when declaring a relation in the [[ActiveRecord]] class:\n     *\n     * ```\n     * public function getItems()\n     * {\n     *     return $this->hasMany(Item::class, ['id' => 'item_id'])\n     *                 ->viaTable('order_item', ['order_id' => 'id']);\n     * }\n     * ```\n     *\n     * @param string $tableName the name of the junction table.\n     * @param array $link the link between the junction table and the table associated with [[primaryModel]].\n     * The keys of the array represent the columns in the junction table, and the values represent the columns\n     * in the [[primaryModel]] table.\n     * @param callable|null $callable a PHP callback for customizing the relation associated with the junction table.\n     * Its signature should be `function($query)`, where `$query` is the query to be customized.\n     * @return $this the query object itself\n     * @throws InvalidConfigException when query is not initialized properly\n     * @see via()\n     */\n    public function viaTable($tableName, $link, ?callable $callable = null)\n    {\n        $modelClass = $this->primaryModel ? get_class($this->primaryModel) : $this->modelClass;\n        $relation = new self($modelClass, [\n            'from' => [$tableName],\n            'link' => $link,\n            'multiple' => true,\n            'asArray' => true,\n        ]);\n        $this->via = $relation;\n        if ($callable !== null) {\n            call_user_func($callable, $relation);\n        }\n\n        return $this;\n    }\n\n    /**\n     * Define an alias for the table defined in [[modelClass]].\n     *\n     * This method will adjust [[from]] so that an already defined alias will be overwritten.\n     * If none was defined, [[from]] will be populated with the given alias.\n     *\n     * @param string $alias the table alias.\n     * @return $this the query object itself\n     * @since 2.0.7\n     */\n    public function alias($alias)\n    {\n        if (empty($this->from) || count($this->from) < 2) {\n            list($tableName) = $this->getTableNameAndAlias();\n            $this->from = [$alias => $tableName];\n        } else {\n            $tableName = $this->getPrimaryTableName();\n\n            foreach ($this->from as $key => $table) {\n                if ($table === $tableName) {\n                    unset($this->from[$key]);\n                    $this->from[$alias] = $tableName;\n                }\n            }\n        }\n\n        return $this;\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.12\n     */\n    public function getTablesUsedInFrom()\n    {\n        if (empty($this->from)) {\n            return $this->cleanUpTableNames([$this->getPrimaryTableName()]);\n        }\n\n        return parent::getTablesUsedInFrom();\n    }\n\n    /**\n     * @return string primary table name\n     * @since 2.0.12\n     */\n    protected function getPrimaryTableName()\n    {\n        /** @var ActiveRecord $modelClass */\n        $modelClass = $this->modelClass;\n        return $modelClass::tableName();\n    }\n}\n"
  },
  {
    "path": "framework/db/ActiveQueryInterface.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\n/**\n * ActiveQueryInterface defines the common interface to be implemented by active record query classes.\n *\n * That are methods for either normal queries that return active records but also relational queries\n * in which the query represents a relation between two active record classes and will return related\n * records only.\n *\n * A class implementing this interface should also use [[ActiveQueryTrait]] and [[ActiveRelationTrait]].\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0\n */\ninterface ActiveQueryInterface extends QueryInterface\n{\n    /**\n     * Sets the [[asArray]] property.\n     * @param bool $value whether to return the query results in terms of arrays instead of Active Records.\n     * @return $this the query object itself\n     */\n    public function asArray($value = true);\n\n    /**\n     * Executes query and returns a single row of result.\n     * @param Connection|null $db the DB connection used to create the DB command.\n     * If `null`, the DB connection returned by [[ActiveQueryTrait::$modelClass|modelClass]] will be used.\n     * @return ActiveRecordInterface|array|null a single row of query result. Depending on the setting of [[asArray]],\n     * the query result may be either an array or an ActiveRecord object. `null` will be returned\n     * if the query results in nothing.\n     */\n    public function one($db = null);\n\n    /**\n     * Sets the [[indexBy]] property.\n     * @param string|callable $column the name of the column by which the query results should be indexed by.\n     * This can also be a callable (e.g. anonymous function) that returns the index value based on the given\n     * row or model data. The signature of the callable should be:\n     *\n     * ```\n     * // $model is an AR instance when `asArray` is false,\n     * // or an array of column values when `asArray` is true.\n     * function ($model)\n     * {\n     *     // return the index value corresponding to $model\n     * }\n     * ```\n     *\n     * @return $this the query object itself\n     */\n    public function indexBy($column);\n\n    /**\n     * Specifies the relations with which this query should be performed.\n     *\n     * The parameters to this method can be either one or multiple strings, or a single array\n     * of relation names and the optional callbacks to customize the relations.\n     *\n     * A relation name can refer to a relation defined in [[ActiveQueryTrait::modelClass|modelClass]]\n     * or a sub-relation that stands for a relation of a related record.\n     * For example, `orders.address` means the `address` relation defined\n     * in the model class corresponding to the `orders` relation.\n     *\n     * The following are some usage examples:\n     *\n     * ```\n     * // find customers together with their orders and country\n     * Customer::find()->with('orders', 'country')->all();\n     * // find customers together with their orders and the orders' shipping address\n     * Customer::find()->with('orders.address')->all();\n     * // find customers together with their country and orders of status 1\n     * Customer::find()->with([\n     *     'orders' => function (\\yii\\db\\ActiveQuery $query) {\n     *         $query->andWhere('status = 1');\n     *     },\n     *     'country',\n     * ])->all();\n     * ```\n     *\n     * @return $this the query object itself\n     */\n    public function with();\n\n    /**\n     * Specifies the relation associated with the junction table for use in relational query.\n     * @param string $relationName the relation name. This refers to a relation declared in the [[ActiveRelationTrait::primaryModel|primaryModel]] of the relation.\n     * @param callable|null $callable a PHP callback for customizing the relation associated with the junction table.\n     * Its signature should be `function($query)`, where `$query` is the query to be customized.\n     * @return $this the relation object itself.\n     */\n    public function via($relationName, ?callable $callable = null);\n\n    /**\n     * Finds the related records for the specified primary record.\n     * This method is invoked when a relation of an ActiveRecord is being accessed in a lazy fashion.\n     * @param string $name the relation name\n     * @param ActiveRecordInterface $model the primary model\n     * @return mixed the related record(s)\n     */\n    public function findFor($name, $model);\n}\n"
  },
  {
    "path": "framework/db/ActiveQueryTrait.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\n/**\n * ActiveQueryTrait implements the common methods and properties for active record query classes.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0\n */\ntrait ActiveQueryTrait\n{\n    /**\n     * @var class-string<ActiveRecordInterface> the name of the ActiveRecord class.\n     */\n    public $modelClass;\n    /**\n     * @var array|null a list of relations that this query should be performed with\n     */\n    public $with;\n    /**\n     * @var bool whether to return each record as an array. If false (default), an object\n     * of [[modelClass]] will be created to represent each record.\n     */\n    public $asArray;\n\n\n    /**\n     * Sets the [[asArray]] property.\n     * @param bool $value whether to return the query results in terms of arrays instead of Active Records.\n     * @return $this the query object itself\n     */\n    public function asArray($value = true)\n    {\n        $this->asArray = $value;\n        return $this;\n    }\n\n    /**\n     * Specifies the relations with which this query should be performed.\n     *\n     * The parameters to this method can be either one or multiple strings, or a single array\n     * of relation names and the optional callbacks to customize the relations.\n     *\n     * A relation name can refer to a relation defined in [[modelClass]]\n     * or a sub-relation that stands for a relation of a related record.\n     * For example, `orders.address` means the `address` relation defined\n     * in the model class corresponding to the `orders` relation.\n     *\n     * The following are some usage examples:\n     *\n     * ```\n     * // find customers together with their orders and country\n     * Customer::find()->with('orders', 'country')->all();\n     * // find customers together with their orders and the orders' shipping address\n     * Customer::find()->with('orders.address')->all();\n     * // find customers together with their country and orders of status 1\n     * Customer::find()->with([\n     *     'orders' => function (\\yii\\db\\ActiveQuery $query) {\n     *         $query->andWhere('status = 1');\n     *     },\n     *     'country',\n     * ])->all();\n     * ```\n     *\n     * You can call `with()` multiple times. Each call will add relations to the existing ones.\n     * For example, the following two statements are equivalent:\n     *\n     * ```\n     * Customer::find()->with('orders', 'country')->all();\n     * Customer::find()->with('orders')->with('country')->all();\n     * ```\n     *\n     * @return $this the query object itself\n     */\n    public function with()\n    {\n        $with = func_get_args();\n        if (isset($with[0]) && is_array($with[0])) {\n            // the parameter is given as an array\n            $with = $with[0];\n        }\n\n        if (empty($this->with)) {\n            $this->with = $with;\n        } elseif (!empty($with)) {\n            foreach ($with as $name => $value) {\n                if (is_int($name)) {\n                    // repeating relation is fine as normalizeRelations() handle it well\n                    $this->with[] = $value;\n                } else {\n                    $this->with[$name] = $value;\n                }\n            }\n        }\n\n        return $this;\n    }\n\n    /**\n     * Converts found rows into model instances.\n     * @param array $rows\n     * @return array|ActiveRecord[]\n     * @since 2.0.11\n     */\n    protected function createModels($rows)\n    {\n        if ($this->asArray) {\n            return $rows;\n        } else {\n            $models = [];\n            /** @var ActiveRecord $class */\n            $class = $this->modelClass;\n            foreach ($rows as $row) {\n                $model = $class::instantiate($row);\n                $modelClass = get_class($model);\n                $modelClass::populateRecord($model, $row);\n                $models[] = $model;\n            }\n            return $models;\n        }\n    }\n\n    /**\n     * Finds records corresponding to one or multiple relations and populates them into the primary models.\n     * @param array $with a list of relations that this query should be performed with. Please\n     * refer to [[with()]] for details about specifying this parameter.\n     * @param array|ActiveRecord[] $models the primary models (can be either AR instances or arrays)\n     */\n    public function findWith($with, &$models)\n    {\n        if (empty($models)) {\n            return;\n        }\n\n        $primaryModel = reset($models);\n        if (!$primaryModel instanceof ActiveRecordInterface) {\n            /** @var ActiveRecordInterface $modelClass */\n            $modelClass = $this->modelClass;\n            $primaryModel = $modelClass::instance();\n        }\n        $relations = $this->normalizeRelations($primaryModel, $with);\n        /** @var ActiveQuery $relation */\n        foreach ($relations as $name => $relation) {\n            if ($relation->asArray === null) {\n                // inherit asArray from primary query\n                $relation->asArray($this->asArray);\n            }\n            $relation->populateRelation($name, $models);\n        }\n    }\n\n    /**\n     * @param ActiveRecord $model\n     * @param array $with\n     * @return ActiveQueryInterface[]\n     */\n    private function normalizeRelations($model, $with)\n    {\n        $relations = [];\n        foreach ($with as $name => $callback) {\n            if (is_int($name)) {\n                $name = $callback;\n                $callback = null;\n            }\n            if (($pos = strpos($name, '.')) !== false) {\n                // with sub-relations\n                $childName = substr($name, $pos + 1);\n                $name = substr($name, 0, $pos);\n            } else {\n                $childName = null;\n            }\n\n            if (!isset($relations[$name])) {\n                $relation = $model->getRelation($name);\n                $relation->primaryModel = null;\n                $relations[$name] = $relation;\n            } else {\n                $relation = $relations[$name];\n            }\n\n            if (isset($childName)) {\n                $relation->with[$childName] = $callback;\n            } elseif ($callback !== null) {\n                call_user_func($callback, $relation);\n            }\n        }\n\n        return $relations;\n    }\n}\n"
  },
  {
    "path": "framework/db/ActiveRecord.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\nuse Yii;\nuse yii\\base\\InvalidArgumentException;\nuse yii\\base\\InvalidConfigException;\nuse yii\\helpers\\ArrayHelper;\nuse yii\\helpers\\Inflector;\nuse yii\\helpers\\StringHelper;\n\n/**\n * ActiveRecord is the base class for classes representing relational data in terms of objects.\n *\n * Active Record implements the [Active Record design pattern](https://en.wikipedia.org/wiki/Active_record_pattern).\n * The premise behind Active Record is that an individual [[ActiveRecord]] object is associated with a specific\n * row in a database table. The object's attributes are mapped to the columns of the corresponding table.\n * Referencing an Active Record attribute is equivalent to accessing the corresponding table column for that record.\n *\n * As an example, say that the `Customer` ActiveRecord class is associated with the `customer` table.\n * This would mean that the class's `name` attribute is automatically mapped to the `name` column in `customer` table.\n * Thanks to Active Record, assuming the variable `$customer` is an object of type `Customer`, to get the value of\n * the `name` column for the table row, you can use the expression `$customer->name`.\n * In this example, Active Record is providing an object-oriented interface for accessing data stored in the database.\n * But Active Record provides much more functionality than this.\n *\n * To declare an ActiveRecord class you need to extend [[\\yii\\db\\ActiveRecord]] and\n * implement the `tableName` method:\n *\n * ```\n * <?php\n *\n * class Customer extends \\yii\\db\\ActiveRecord\n * {\n *     public static function tableName()\n *     {\n *         return 'customer';\n *     }\n * }\n * ```\n *\n * The `tableName` method only has to return the name of the database table associated with the class.\n *\n * > Tip: You may also use the [Gii code generator](guide:start-gii) to generate ActiveRecord classes from your\n * > database tables.\n *\n * Class instances are obtained in one of two ways:\n *\n * * Using the `new` operator to create a new, empty object\n * * Using a method to fetch an existing record (or records) from the database\n *\n * Below is an example showing some typical usage of ActiveRecord:\n *\n * ```\n * $user = new User();\n * $user->name = 'Qiang';\n * $user->save();  // a new row is inserted into user table\n *\n * // the following will retrieve the user 'CeBe' from the database\n * $user = User::find()->where(['name' => 'CeBe'])->one();\n *\n * // this will get related records from orders table when relation is defined\n * $orders = $user->orders;\n * ```\n *\n * For more details and usage information on ActiveRecord, see the [guide article on ActiveRecord](guide:db-active-record).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0\n */\nclass ActiveRecord extends BaseActiveRecord\n{\n    /**\n     * The insert operation. This is mainly used when overriding [[transactions()]] to specify which operations are transactional.\n     */\n    public const OP_INSERT = 0x01;\n    /**\n     * The update operation. This is mainly used when overriding [[transactions()]] to specify which operations are transactional.\n     */\n    public const OP_UPDATE = 0x02;\n    /**\n     * The delete operation. This is mainly used when overriding [[transactions()]] to specify which operations are transactional.\n     */\n    public const OP_DELETE = 0x04;\n    /**\n     * All three operations: insert, update, delete.\n     * This is a shortcut of the expression: OP_INSERT | OP_UPDATE | OP_DELETE.\n     */\n    public const OP_ALL = 0x07;\n    /**\n     * Loads default values from database table schema.\n     *\n     * You may call this method to load default values after creating a new instance:\n     *\n     * ```\n     * // class Customer extends \\yii\\db\\ActiveRecord\n     * $customer = new Customer();\n     * $customer->loadDefaultValues();\n     * ```\n     *\n     * @param bool $skipIfSet whether existing value should be preserved.\n     * This will only set defaults for attributes that are `null`.\n     * @return $this the model instance itself.\n     */\n    public function loadDefaultValues($skipIfSet = true)\n    {\n        $columns = static::getTableSchema()->columns;\n        foreach ($this->attributes() as $name) {\n            if (isset($columns[$name])) {\n                $defaultValue = $columns[$name]->defaultValue;\n                if ($defaultValue !== null && (!$skipIfSet || $this->getAttribute($name) === null)) {\n                    $this->setAttribute($name, $defaultValue);\n                }\n            }\n        }\n\n        return $this;\n    }\n\n    /**\n     * Returns the database connection used by this AR class.\n     * By default, the \"db\" application component is used as the database connection.\n     * You may override this method if you want to use a different database connection.\n     * @return Connection the database connection used by this AR class.\n     */\n    public static function getDb()\n    {\n        return Yii::$app->getDb();\n    }\n\n    /**\n     * Creates an [[ActiveQuery]] instance with a given SQL statement.\n     *\n     * Note that because the SQL statement is already specified, calling additional\n     * query modification methods (such as `where()`, `order()`) on the created [[ActiveQuery]]\n     * instance will have no effect. However, calling `with()`, `asArray()` or `indexBy()` is\n     * still fine.\n     *\n     * Below is an example:\n     *\n     * ```\n     * $customers = Customer::findBySql('SELECT * FROM customer')->all();\n     * ```\n     *\n     * @param string $sql the SQL statement to be executed\n     * @param array $params parameters to be bound to the SQL statement during execution.\n     * @return ActiveQuery<static> the newly created [[ActiveQuery]] instance\n     */\n    public static function findBySql($sql, $params = [])\n    {\n        $query = static::find();\n        $query->sql = $sql;\n\n        return $query->params($params);\n    }\n\n    /**\n     * Finds ActiveRecord instance(s) by the given condition.\n     * This method is internally called by [[findOne()]] and [[findAll()]].\n     * @param mixed $condition please refer to [[findOne()]] for the explanation of this parameter\n     * @return ActiveQueryInterface the newly created [[ActiveQueryInterface|ActiveQuery]] instance.\n     * @throws InvalidConfigException if there is no primary key defined.\n     * @internal\n     */\n    protected static function findByCondition($condition)\n    {\n        $query = static::find();\n\n        if (!ArrayHelper::isAssociative($condition) && !$condition instanceof ExpressionInterface) {\n            // query by primary key\n            $primaryKey = static::primaryKey();\n            if (isset($primaryKey[0])) {\n                $pk = $primaryKey[0];\n                if (!empty($query->join) || !empty($query->joinWith)) {\n                    $pk = static::tableName() . '.' . $pk;\n                }\n                // if condition is scalar, search for a single primary key, if it is array, search for multiple primary key values\n                $condition = [$pk => is_array($condition) ? array_values($condition) : $condition];\n            } else {\n                throw new InvalidConfigException('\"' . get_called_class() . '\" must have a primary key.');\n            }\n        } elseif (is_array($condition)) {\n            $aliases = static::filterValidAliases($query);\n            $condition = static::filterCondition($condition, $aliases);\n        }\n\n        return $query->andWhere($condition);\n    }\n\n    /**\n     * Returns table aliases which are not the same as the name of the tables.\n     *\n     * @param Query $query\n     * @return array\n     * @throws InvalidConfigException\n     * @since 2.0.17\n     * @internal\n     */\n    protected static function filterValidAliases(Query $query)\n    {\n        $tables = $query->getTablesUsedInFrom();\n\n        $aliases = array_diff(array_keys($tables), $tables);\n\n        return array_map(function ($alias) {\n            return preg_replace('/{{(\\w+)}}/', '$1', $alias);\n        }, array_values($aliases));\n    }\n\n    /**\n     * Filters array condition before it is assiged to a Query filter.\n     *\n     * This method will ensure that an array condition only filters on existing table columns.\n     *\n     * @param array $condition condition to filter.\n     * @param array $aliases\n     * @return array filtered condition.\n     * @throws InvalidArgumentException in case array contains unsafe values.\n     * @throws InvalidConfigException\n     * @since 2.0.15\n     * @internal\n     */\n    protected static function filterCondition(array $condition, array $aliases = [])\n    {\n        $result = [];\n        $db = static::getDb();\n        $columnNames = static::filterValidColumnNames($db, $aliases);\n\n        foreach ($condition as $key => $value) {\n            if (is_string($key) && !in_array($db->quoteSql($key), $columnNames, true)) {\n                throw new InvalidArgumentException('Key \"' . $key . '\" is not a column name and can not be used as a filter');\n            }\n            $result[$key] = is_array($value) ? array_values($value) : $value;\n        }\n\n        return $result;\n    }\n\n    /**\n     * Valid column names are table column names or column names prefixed with table name or table alias\n     *\n     * @param Connection $db\n     * @param array $aliases\n     * @return array\n     * @throws InvalidConfigException\n     * @since 2.0.17\n     * @internal\n     */\n    protected static function filterValidColumnNames($db, array $aliases)\n    {\n        $columnNames = [];\n        $tableName = static::tableName();\n        $quotedTableName = $db->quoteTableName($tableName);\n\n        foreach (static::getTableSchema()->getColumnNames() as $columnName) {\n            $columnNames[] = $columnName;\n            $columnNames[] = $db->quoteColumnName($columnName);\n            $columnNames[] = \"$tableName.$columnName\";\n            $columnNames[] = $db->quoteSql(\"$quotedTableName.[[$columnName]]\");\n            foreach ($aliases as $tableAlias) {\n                $columnNames[] = \"$tableAlias.$columnName\";\n                $quotedTableAlias = $db->quoteTableName($tableAlias);\n                $columnNames[] = $db->quoteSql(\"$quotedTableAlias.[[$columnName]]\");\n            }\n        }\n\n        return $columnNames;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function refresh()\n    {\n        $query = static::find();\n        $tableName = key($query->getTablesUsedInFrom());\n        $pk = [];\n        // disambiguate column names in case ActiveQuery adds a JOIN\n        foreach ($this->getPrimaryKey(true) as $key => $value) {\n            $pk[$tableName . '.' . $key] = $value;\n        }\n        $query->where($pk);\n\n        /** @var BaseActiveRecord $record */\n        $record = $query->noCache()->one();\n        return $this->refreshInternal($record);\n    }\n\n    /**\n     * Updates the whole table using the provided attribute values and conditions.\n     *\n     * For example, to change the status to be 1 for all customers whose status is 2:\n     *\n     * ```\n     * Customer::updateAll(['status' => 1], 'status = 2');\n     * ```\n     *\n     * > Warning: If you do not specify any condition, this method will update **all** rows in the table.\n     *\n     * Note that this method will not trigger any events. If you need [[EVENT_BEFORE_UPDATE]] or\n     * [[EVENT_AFTER_UPDATE]] to be triggered, you need to [[find()|find]] the models first and then\n     * call [[update()]] on each of them. For example an equivalent of the example above would be:\n     *\n     * ```\n     * $models = Customer::find()->where('status = 2')->all();\n     * foreach ($models as $model) {\n     *     $model->status = 1;\n     *     $model->update(false); // skipping validation as no user input is involved\n     * }\n     * ```\n     *\n     * For a large set of models you might consider using [[ActiveQuery::each()]] to keep memory usage within limits.\n     *\n     * @param array $attributes attribute values (name-value pairs) to be saved into the table\n     * @param string|array $condition the conditions that will be put in the WHERE part of the UPDATE SQL.\n     * Please refer to [[Query::where()]] on how to specify this parameter.\n     * @param array $params the parameters (name => value) to be bound to the query.\n     * @return int the number of rows updated\n     */\n    public static function updateAll($attributes, $condition = '', $params = [])\n    {\n        $command = static::getDb()->createCommand();\n        $command->update(static::tableName(), $attributes, $condition, $params);\n\n        return $command->execute();\n    }\n\n    /**\n     * Updates the whole table using the provided counter changes and conditions.\n     *\n     * For example, to increment all customers' age by 1,\n     *\n     * ```\n     * Customer::updateAllCounters(['age' => 1]);\n     * ```\n     *\n     * Note that this method will not trigger any events.\n     *\n     * @param array $counters the counters to be updated (attribute name => increment value).\n     * Use negative values if you want to decrement the counters.\n     * @param string|array $condition the conditions that will be put in the WHERE part of the UPDATE SQL.\n     * Please refer to [[Query::where()]] on how to specify this parameter.\n     * @param array $params the parameters (name => value) to be bound to the query.\n     * Do not name the parameters as `:bp0`, `:bp1`, etc., because they are used internally by this method.\n     * @return int the number of rows updated\n     */\n    public static function updateAllCounters($counters, $condition = '', $params = [])\n    {\n        $n = 0;\n        foreach ($counters as $name => $value) {\n            $counters[$name] = new Expression(\"[[$name]]+:bp{$n}\", [\":bp{$n}\" => $value]);\n            $n++;\n        }\n        $command = static::getDb()->createCommand();\n        $command->update(static::tableName(), $counters, $condition, $params);\n\n        return $command->execute();\n    }\n\n    /**\n     * Deletes rows in the table using the provided conditions.\n     *\n     * For example, to delete all customers whose status is 3:\n     *\n     * ```\n     * Customer::deleteAll('status = 3');\n     * ```\n     *\n     * > Warning: If you do not specify any condition, this method will delete **all** rows in the table.\n     *\n     * Note that this method will not trigger any events. If you need [[EVENT_BEFORE_DELETE]] or\n     * [[EVENT_AFTER_DELETE]] to be triggered, you need to [[find()|find]] the models first and then\n     * call [[delete()]] on each of them. For example an equivalent of the example above would be:\n     *\n     * ```\n     * $models = Customer::find()->where('status = 3')->all();\n     * foreach ($models as $model) {\n     *     $model->delete();\n     * }\n     * ```\n     *\n     * For a large set of models you might consider using [[ActiveQuery::each()]] to keep memory usage within limits.\n     *\n     * @param string|array|null $condition the conditions that will be put in the WHERE part of the DELETE SQL.\n     * Please refer to [[Query::where()]] on how to specify this parameter.\n     * @param array $params the parameters (name => value) to be bound to the query.\n     * @return int the number of rows deleted\n     */\n    public static function deleteAll($condition = null, $params = [])\n    {\n        $command = static::getDb()->createCommand();\n        $command->delete(static::tableName(), $condition, $params);\n\n        return $command->execute();\n    }\n\n    /**\n     * {@inheritdoc}\n     * @return ActiveQuery<static> the newly created [[ActiveQuery]] instance.\n     */\n    public static function find()\n    {\n        return Yii::createObject(ActiveQuery::className(), [get_called_class()]);\n    }\n\n    /**\n     * Declares the name of the database table associated with this AR class.\n     * By default this method returns the class name as the table name by calling [[Inflector::camel2id()]]\n     * with prefix [[Connection::tablePrefix]]. For example if [[Connection::tablePrefix]] is `tbl_`,\n     * `Customer` becomes `tbl_customer`, and `OrderItem` becomes `tbl_order_item`. You may override this method\n     * if the table is not named after this convention.\n     * @return string the table name\n     */\n    public static function tableName()\n    {\n        return '{{%' . Inflector::camel2id(StringHelper::basename(get_called_class()), '_') . '}}';\n    }\n\n    /**\n     * Returns the schema information of the DB table associated with this AR class.\n     * @return TableSchema the schema information of the DB table associated with this AR class.\n     * @throws InvalidConfigException if the table for the AR class does not exist.\n     */\n    public static function getTableSchema()\n    {\n        $tableSchema = static::getDb()\n            ->getSchema()\n            ->getTableSchema(static::tableName());\n\n        if ($tableSchema === null) {\n            throw new InvalidConfigException('The table does not exist: ' . static::tableName());\n        }\n\n        return $tableSchema;\n    }\n\n    /**\n     * Returns the primary key name(s) for this AR class.\n     * The default implementation will return the primary key(s) as declared\n     * in the DB table that is associated with this AR class.\n     *\n     * If the DB table does not declare any primary key, you should override\n     * this method to return the attributes that you want to use as primary keys\n     * for this AR class.\n     *\n     * Note that an array should be returned even for a table with single primary key.\n     *\n     * @return string[] the primary keys of the associated database table.\n     */\n    public static function primaryKey()\n    {\n        return static::getTableSchema()->primaryKey;\n    }\n\n    /**\n     * Returns the list of all attribute names of the model.\n     * The default implementation will return all column names of the table associated with this AR class.\n     * @return array list of attribute names.\n     */\n    public function attributes()\n    {\n        return static::getTableSchema()->getColumnNames();\n    }\n\n    /**\n     * Declares which DB operations should be performed within a transaction in different scenarios.\n     * The supported DB operations are: [[OP_INSERT]], [[OP_UPDATE]] and [[OP_DELETE]],\n     * which correspond to the [[insert()]], [[update()]] and [[delete()]] methods, respectively.\n     * By default, these methods are NOT enclosed in a DB transaction.\n     *\n     * In some scenarios, to ensure data consistency, you may want to enclose some or all of them\n     * in transactions. You can do so by overriding this method and returning the operations\n     * that need to be transactional. For example,\n     *\n     * ```\n     * return [\n     *     'admin' => self::OP_INSERT,\n     *     'api' => self::OP_INSERT | self::OP_UPDATE | self::OP_DELETE,\n     *     // the above is equivalent to the following:\n     *     // 'api' => self::OP_ALL,\n     *\n     * ];\n     * ```\n     *\n     * The above declaration specifies that in the \"admin\" scenario, the insert operation ([[insert()]])\n     * should be done in a transaction; and in the \"api\" scenario, all the operations should be done\n     * in a transaction.\n     *\n     * @return array the declarations of transactional operations. The array keys are scenarios names,\n     * and the array values are the corresponding transaction operations.\n     */\n    public function transactions()\n    {\n        return [];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public static function populateRecord($record, $row)\n    {\n        $columns = static::getTableSchema()->columns;\n        foreach ($row as $name => $value) {\n            if (isset($columns[$name])) {\n                $row[$name] = $columns[$name]->phpTypecast($value);\n            }\n        }\n        parent::populateRecord($record, $row);\n    }\n\n    /**\n     * Inserts a row into the associated database table using the attribute values of this record.\n     *\n     * This method performs the following steps in order:\n     *\n     * 1. call [[beforeValidate()]] when `$runValidation` is `true`. If [[beforeValidate()]]\n     *    returns `false`, the rest of the steps will be skipped;\n     * 2. call [[afterValidate()]] when `$runValidation` is `true`. If validation\n     *    failed, the rest of the steps will be skipped;\n     * 3. call [[beforeSave()]]. If [[beforeSave()]] returns `false`,\n     *    the rest of the steps will be skipped;\n     * 4. insert the record into database. If this fails, it will skip the rest of the steps;\n     * 5. call [[afterSave()]];\n     *\n     * In the above step 1, 2, 3 and 5, events [[EVENT_BEFORE_VALIDATE]],\n     * [[EVENT_AFTER_VALIDATE]], [[EVENT_BEFORE_INSERT]], and [[EVENT_AFTER_INSERT]]\n     * will be raised by the corresponding methods.\n     *\n     * Only the [[dirtyAttributes|changed attribute values]] will be inserted into database.\n     *\n     * If the table's primary key is auto-incremental and is `null` during insertion,\n     * it will be populated with the actual value after insertion.\n     *\n     * For example, to insert a customer record:\n     *\n     * ```\n     * $customer = new Customer;\n     * $customer->name = $name;\n     * $customer->email = $email;\n     * $customer->insert();\n     * ```\n     *\n     * @param bool $runValidation whether to perform validation (calling [[validate()]])\n     * before saving the record. Defaults to `true`. If the validation fails, the record\n     * will not be saved to the database and this method will return `false`.\n     * @param array|null $attributes list of attributes that need to be saved. Defaults to `null`,\n     * meaning all attributes that are loaded from DB will be saved.\n     * @return bool whether the attributes are valid and the record is inserted successfully.\n     * @throws \\Throwable in case insert failed.\n     */\n    public function insert($runValidation = true, $attributes = null)\n    {\n        if ($runValidation && !$this->validate($attributes)) {\n            Yii::info('Model not inserted due to validation error.', __METHOD__);\n            return false;\n        }\n\n        if (!$this->isTransactional(self::OP_INSERT)) {\n            return $this->insertInternal($attributes);\n        }\n\n        $transaction = static::getDb()->beginTransaction();\n        try {\n            $result = $this->insertInternal($attributes);\n            if ($result === false) {\n                $transaction->rollBack();\n            } else {\n                $transaction->commit();\n            }\n\n            return $result;\n        } catch (\\Exception $e) {\n            $transaction->rollBack();\n            throw $e;\n        } catch (\\Throwable $e) {\n            $transaction->rollBack();\n            throw $e;\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     *\n     * @template T of self\n     *\n     * @param class-string<T> $class the class name of the related record.\n     * @return ActiveQuery<T> the relational query object.\n     */\n    public function hasMany($class, $link)\n    {\n        return parent::hasMany($class, $link);\n    }\n\n    /**\n     * {@inheritdoc}\n     *\n     * @return ActiveQuery\n     *\n     * @template T of self\n     *\n     * @param class-string<T> $class the class name of the related record.\n     * @return ActiveQuery<T> the relational query object.\n     */\n    public function hasOne($class, $link)\n    {\n        return parent::hasOne($class, $link);\n    }\n\n    /**\n     * Inserts an ActiveRecord into DB without considering transaction.\n     * @param array|null $attributes list of attributes that need to be saved. Defaults to `null`,\n     * meaning all attributes that are loaded from DB will be saved.\n     * @return bool whether the record is inserted successfully.\n     */\n    protected function insertInternal($attributes = null)\n    {\n        if (!$this->beforeSave(true)) {\n            return false;\n        }\n        $values = $this->getDirtyAttributes($attributes);\n        if (($primaryKeys = static::getDb()->schema->insert(static::tableName(), $values)) === false) {\n            return false;\n        }\n        foreach ($primaryKeys as $name => $value) {\n            $id = static::getTableSchema()->columns[$name]->phpTypecast($value);\n            $this->setAttribute($name, $id);\n            $values[$name] = $id;\n        }\n\n        $changedAttributes = array_fill_keys(array_keys($values), null);\n        $this->setOldAttributes($values);\n        $this->afterSave(true, $changedAttributes);\n\n        return true;\n    }\n\n    /**\n     * Saves the changes to this active record into the associated database table.\n     *\n     * This method performs the following steps in order:\n     *\n     * 1. call [[beforeValidate()]] when `$runValidation` is `true`. If [[beforeValidate()]]\n     *    returns `false`, the rest of the steps will be skipped;\n     * 2. call [[afterValidate()]] when `$runValidation` is `true`. If validation\n     *    failed, the rest of the steps will be skipped;\n     * 3. call [[beforeSave()]]. If [[beforeSave()]] returns `false`,\n     *    the rest of the steps will be skipped;\n     * 4. save the record into database. If this fails, it will skip the rest of the steps;\n     * 5. call [[afterSave()]];\n     *\n     * In the above step 1, 2, 3 and 5, events [[EVENT_BEFORE_VALIDATE]],\n     * [[EVENT_AFTER_VALIDATE]], [[EVENT_BEFORE_UPDATE]], and [[EVENT_AFTER_UPDATE]]\n     * will be raised by the corresponding methods.\n     *\n     * Only the [[dirtyAttributes|changed attribute values]] will be saved into database.\n     *\n     * For example, to update a customer record:\n     *\n     * ```\n     * $customer = Customer::findOne($id);\n     * $customer->name = $name;\n     * $customer->email = $email;\n     * $customer->update();\n     * ```\n     *\n     * Note that it is possible the update does not affect any row in the table.\n     * In this case, this method will return 0. For this reason, you should use the following\n     * code to check if update() is successful or not:\n     *\n     * ```\n     * if ($customer->update() !== false) {\n     *     // update successful\n     * } else {\n     *     // update failed\n     * }\n     * ```\n     *\n     * @param bool $runValidation whether to perform validation (calling [[validate()]])\n     * before saving the record. Defaults to `true`. If the validation fails, the record\n     * will not be saved to the database and this method will return `false`.\n     * @param array|null $attributeNames list of attributes that need to be saved. Defaults to `null`,\n     * meaning all attributes that are loaded from DB will be saved.\n     * @return int|false the number of rows affected, or false if validation fails\n     * or [[beforeSave()]] stops the updating process.\n     * @throws StaleObjectException if [[optimisticLock|optimistic locking]] is enabled and the data\n     * being updated is outdated.\n     * @throws \\Throwable in case update failed.\n     */\n    public function update($runValidation = true, $attributeNames = null)\n    {\n        if ($runValidation && !$this->validate($attributeNames)) {\n            Yii::info('Model not updated due to validation error.', __METHOD__);\n            return false;\n        }\n\n        if (!$this->isTransactional(self::OP_UPDATE)) {\n            return $this->updateInternal($attributeNames);\n        }\n\n        $transaction = static::getDb()->beginTransaction();\n        try {\n            $result = $this->updateInternal($attributeNames);\n            if ($result === false) {\n                $transaction->rollBack();\n            } else {\n                $transaction->commit();\n            }\n\n            return $result;\n        } catch (\\Exception $e) {\n            $transaction->rollBack();\n            throw $e;\n        } catch (\\Throwable $e) {\n            $transaction->rollBack();\n            throw $e;\n        }\n    }\n\n    /**\n     * Deletes the table row corresponding to this active record.\n     *\n     * This method performs the following steps in order:\n     *\n     * 1. call [[beforeDelete()]]. If the method returns `false`, it will skip the\n     *    rest of the steps;\n     * 2. delete the record from the database;\n     * 3. call [[afterDelete()]].\n     *\n     * In the above step 1 and 3, events named [[EVENT_BEFORE_DELETE]] and [[EVENT_AFTER_DELETE]]\n     * will be raised by the corresponding methods.\n     *\n     * @return int|false the number of rows deleted, or `false` if the deletion is unsuccessful for some reason.\n     * Note that it is possible the number of rows deleted is 0, even though the deletion execution is successful.\n     * @throws StaleObjectException if [[optimisticLock|optimistic locking]] is enabled and the data\n     * being deleted is outdated.\n     * @throws \\Throwable in case delete failed.\n     */\n    public function delete()\n    {\n        if (!$this->isTransactional(self::OP_DELETE)) {\n            return $this->deleteInternal();\n        }\n\n        $transaction = static::getDb()->beginTransaction();\n        try {\n            $result = $this->deleteInternal();\n            if ($result === false) {\n                $transaction->rollBack();\n            } else {\n                $transaction->commit();\n            }\n\n            return $result;\n        } catch (\\Exception $e) {\n            $transaction->rollBack();\n            throw $e;\n        } catch (\\Throwable $e) {\n            $transaction->rollBack();\n            throw $e;\n        }\n    }\n\n    /**\n     * Deletes an ActiveRecord without considering transaction.\n     * @return int|false the number of rows deleted, or `false` if the deletion is unsuccessful for some reason.\n     * Note that it is possible the number of rows deleted is 0, even though the deletion execution is successful.\n     * @throws StaleObjectException\n     */\n    protected function deleteInternal()\n    {\n        if (!$this->beforeDelete()) {\n            return false;\n        }\n\n        // we do not check the return value of deleteAll() because it's possible\n        // the record is already deleted in the database and thus the method will return 0\n        $condition = $this->getOldPrimaryKey(true);\n        $lock = $this->optimisticLock();\n        if ($lock !== null) {\n            $condition[$lock] = $this->$lock;\n        }\n        $result = static::deleteAll($condition);\n        if ($lock !== null && !$result) {\n            throw new StaleObjectException('The object being deleted is outdated.');\n        }\n        $this->setOldAttributes(null);\n        $this->afterDelete();\n\n        return $result;\n    }\n\n    /**\n     * Returns a value indicating whether the given active record is the same as the current one.\n     * The comparison is made by comparing the table names and the primary key values of the two active records.\n     * If one of the records [[isNewRecord|is new]] they are also considered not equal.\n     * @param ActiveRecord $record record to compare to\n     * @return bool whether the two active records refer to the same row in the same database table.\n     */\n    public function equals($record)\n    {\n        if ($this->isNewRecord || $record->isNewRecord) {\n            return false;\n        }\n\n        return static::tableName() === $record->tableName() && $this->getPrimaryKey() === $record->getPrimaryKey();\n    }\n\n    /**\n     * Returns a value indicating whether the specified operation is transactional in the current [[$scenario]].\n     * @param int $operation the operation to check. Possible values are [[OP_INSERT]], [[OP_UPDATE]] and [[OP_DELETE]].\n     * @return bool whether the specified operation is transactional in the current [[scenario]].\n     */\n    public function isTransactional($operation)\n    {\n        $scenario = $this->getScenario();\n        $transactions = $this->transactions();\n\n        return isset($transactions[$scenario]) && ($transactions[$scenario] & $operation);\n    }\n}\n"
  },
  {
    "path": "framework/db/ActiveRecordInterface.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\nuse yii\\base\\StaticInstanceInterface;\n\n/**\n * ActiveRecordInterface.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0\n */\ninterface ActiveRecordInterface extends StaticInstanceInterface\n{\n    /**\n     * Returns the primary key **name(s)** for this AR class.\n     *\n     * Note that an array should be returned even when the record only has a single primary key.\n     *\n     * For the primary key **value** see [[getPrimaryKey()]] instead.\n     *\n     * @return string[] the primary key name(s) for this AR class.\n     */\n    public static function primaryKey();\n\n    /**\n     * Returns the list of all attribute names of the record.\n     * @return array list of attribute names.\n     */\n    public function attributes();\n\n    /**\n     * Returns the named attribute value.\n     * If this record is the result of a query and the attribute is not loaded,\n     * `null` will be returned.\n     * @param string $name the attribute name\n     * @return mixed the attribute value. `null` if the attribute is not set or does not exist.\n     * @see hasAttribute()\n     */\n    public function getAttribute($name);\n\n    /**\n     * Sets the named attribute value.\n     * @param string $name the attribute name.\n     * @param mixed $value the attribute value.\n     * @see hasAttribute()\n     */\n    public function setAttribute($name, $value);\n\n    /**\n     * Returns a value indicating whether the record has an attribute with the specified name.\n     * @param string $name the name of the attribute\n     * @return bool whether the record has an attribute with the specified name.\n     */\n    public function hasAttribute($name);\n\n    /**\n     * Returns the primary key value(s).\n     * @param bool $asArray whether to return the primary key value as an array. If true,\n     * the return value will be an array with attribute names as keys and attribute values as values.\n     * Note that for composite primary keys, an array will always be returned regardless of this parameter value.\n     * @return mixed the primary key value. An array (attribute name => attribute value) is returned if the primary key\n     * is composite or `$asArray` is true. A string is returned otherwise (`null` will be returned if\n     * the key value is `null`).\n     */\n    public function getPrimaryKey($asArray = false);\n\n    /**\n     * Returns the old primary key value(s).\n     * This refers to the primary key value that is populated into the record\n     * after executing a find method (e.g. find(), findOne()).\n     * The value remains unchanged even if the primary key attribute is manually assigned with a different value.\n     * @param bool $asArray whether to return the primary key value as an array. If true,\n     * the return value will be an array with column name as key and column value as value.\n     * If this is `false` (default), a scalar value will be returned for non-composite primary key.\n     * @return mixed the old primary key value. An array (column name => column value) is returned if the primary key\n     * is composite or `$asArray` is true. A string is returned otherwise (`null` will be returned if\n     * the key value is `null`).\n     */\n    public function getOldPrimaryKey($asArray = false);\n\n    /**\n     * Returns a value indicating whether the given set of attributes represents the primary key for this model.\n     * @param array $keys the set of attributes to check\n     * @return bool whether the given set of attributes represents the primary key for this model\n     */\n    public static function isPrimaryKey($keys);\n\n    /**\n     * Creates an [[ActiveQueryInterface]] instance for query purpose.\n     *\n     * The returned [[ActiveQueryInterface]] instance can be further customized by calling\n     * methods defined in [[ActiveQueryInterface]] before `one()` or `all()` is called to return\n     * populated ActiveRecord instances. For example,\n     *\n     * ```\n     * // find the customer whose ID is 1\n     * $customer = Customer::find()->where(['id' => 1])->one();\n     *\n     * // find all active customers and order them by their age:\n     * $customers = Customer::find()\n     *     ->where(['status' => 1])\n     *     ->orderBy('age')\n     *     ->all();\n     * ```\n     *\n     * This method is also called by [[BaseActiveRecord::hasOne()]] and [[BaseActiveRecord::hasMany()]] to\n     * create a relational query.\n     *\n     * You may override this method to return a customized query. For example,\n     *\n     * ```\n     * class Customer extends ActiveRecord\n     * {\n     *     public static function find()\n     *     {\n     *         // use CustomerQuery instead of the default ActiveQuery\n     *         return new CustomerQuery(get_called_class());\n     *     }\n     * }\n     * ```\n     *\n     * The following code shows how to apply a default condition for all queries:\n     *\n     * ```\n     * class Customer extends ActiveRecord\n     * {\n     *     public static function find()\n     *     {\n     *         return parent::find()->where(['deleted' => false]);\n     *     }\n     * }\n     *\n     * // Use andWhere()/orWhere() to apply the default condition\n     * // SELECT FROM customer WHERE `deleted`=:deleted AND age>30\n     * $customers = Customer::find()->andWhere('age>30')->all();\n     *\n     * // Use where() to ignore the default condition\n     * // SELECT FROM customer WHERE age>30\n     * $customers = Customer::find()->where('age>30')->all();\n     * ```\n     *\n     * @return ActiveQueryInterface the newly created [[ActiveQueryInterface]] instance.\n     */\n    public static function find();\n\n    /**\n     * Returns a single active record model instance by a primary key or an array of column values.\n     *\n     * The method accepts:\n     *\n     *  - a scalar value (integer or string): query by a single primary key value and return the\n     *    corresponding record (or `null` if not found).\n     *  - a non-associative array: query by a list of primary key values and return the\n     *    first record (or `null` if not found).\n     *  - an associative array of name-value pairs: query by a set of attribute values and return a single record\n     *    matching all of them (or `null` if not found). Note that `['id' => 1, 2]` is treated as a non-associative array.\n     *    Column names are limited to current records table columns for SQL DBMS, or filtered otherwise to be limited to simple filter conditions.\n     *  - a yii\\db\\Expression: The expression will be used directly. (@since 2.0.37)\n     *\n     * That this method will automatically call the `one()` method and return an [[ActiveRecordInterface|ActiveRecord]]\n     * instance.\n     *\n     * > Note: As this is a short-hand method only, using more complex conditions, like ['!=', 'id', 1] will not work.\n     * > If you need to specify more complex conditions, use [[find()]] in combination with [[ActiveQuery::where()|where()]] instead.\n     *\n     * See the following code for usage examples:\n     *\n     * ```\n     * // find a single customer whose primary key value is 10\n     * $customer = Customer::findOne(10);\n     *\n     * // the above code is equivalent to:\n     * $customer = Customer::find()->where(['id' => 10])->one();\n     *\n     * // find the customers whose primary key value is 10, 11 or 12.\n     * $customers = Customer::findOne([10, 11, 12]);\n     *\n     * // the above code is equivalent to:\n     * $customers = Customer::find()->where(['id' => [10, 11, 12]])->one();\n     *\n     * // find the first customer whose age is 30 and whose status is 1\n     * $customer = Customer::findOne(['age' => 30, 'status' => 1]);\n     *\n     * // the above code is equivalent to:\n     * $customer = Customer::find()->where(['age' => 30, 'status' => 1])->one();\n     * ```\n     *\n     * If you need to pass user input to this method, make sure the input value is scalar or in case of\n     * array condition, make sure the array structure can not be changed from the outside:\n     *\n     * ```\n     * // yii\\web\\Controller ensures that $id is scalar\n     * public function actionView($id)\n     * {\n     *     $model = Post::findOne($id);\n     *     // ...\n     * }\n     *\n     * // explicitly specifying the colum to search, passing a scalar or array here will always result in finding a single record\n     * $model = Post::findOne(['id' => Yii::$app->request->get('id')]);\n     *\n     * // do NOT use the following code! it is possible to inject an array condition to filter by arbitrary column values!\n     * $model = Post::findOne(Yii::$app->request->get('id'));\n     * ```\n     *\n     * @param mixed $condition primary key value or a set of column values\n     * @return static|null ActiveRecord instance matching the condition, or `null` if nothing matches.\n     */\n    public static function findOne($condition);\n\n    /**\n     * Returns a list of active record models that match the specified primary key value(s) or a set of column values.\n     *\n     * The method accepts:\n     *\n     *  - a scalar value (integer or string): query by a single primary key value and return an array containing the\n     *    corresponding record (or an empty array if not found).\n     *  - a non-associative array: query by a list of primary key values and return the\n     *    corresponding records (or an empty array if none was found).\n     *    Note that an empty condition will result in an empty result as it will be interpreted as a search for\n     *    primary keys and not an empty `WHERE` condition.\n     *  - an associative array of name-value pairs: query by a set of attribute values and return an array of records\n     *    matching all of them (or an empty array if none was found). Note that `['id' => 1, 2]` is treated as\n     *    a non-associative array.\n     *    Column names are limited to current records table columns for SQL DBMS, or filtered otherwise to be limted to simple filter conditions.\n     *  - a yii\\db\\Expression: The expression will be used directly. (@since 2.0.37)\n     *\n     * This method will automatically call the `all()` method and return an array of [[ActiveRecordInterface|ActiveRecord]]\n     * instances.\n     *\n     * > Note: As this is a short-hand method only, using more complex conditions, like ['!=', 'id', 1] will not work.\n     * > If you need to specify more complex conditions, use [[find()]] in combination with [[ActiveQuery::where()|where()]] instead.\n     *\n     * See the following code for usage examples:\n     *\n     * ```\n     * // find the customers whose primary key value is 10\n     * $customers = Customer::findAll(10);\n     *\n     * // the above code is equivalent to:\n     * $customers = Customer::find()->where(['id' => 10])->all();\n     *\n     * // find the customers whose primary key value is 10, 11 or 12.\n     * $customers = Customer::findAll([10, 11, 12]);\n     *\n     * // the above code is equivalent to:\n     * $customers = Customer::find()->where(['id' => [10, 11, 12]])->all();\n     *\n     * // find customers whose age is 30 and whose status is 1\n     * $customers = Customer::findAll(['age' => 30, 'status' => 1]);\n     *\n     * // the above code is equivalent to:\n     * $customers = Customer::find()->where(['age' => 30, 'status' => 1])->all();\n     * ```\n     *\n     * If you need to pass user input to this method, make sure the input value is scalar or in case of\n     * array condition, make sure the array structure can not be changed from the outside:\n     *\n     * ```\n     * // yii\\web\\Controller ensures that $id is scalar\n     * public function actionView($id)\n     * {\n     *     $model = Post::findOne($id);\n     *     // ...\n     * }\n     *\n     * // explicitly specifying the colum to search, passing a scalar or array here will always result in finding a single record\n     * $model = Post::findOne(['id' => Yii::$app->request->get('id')]);\n     *\n     * // do NOT use the following code! it is possible to inject an array condition to filter by arbitrary column values!\n     * $model = Post::findOne(Yii::$app->request->get('id'));\n     * ```\n     *\n     * @param mixed $condition primary key value or a set of column values\n     * @return array an array of ActiveRecord instance, or an empty array if nothing matches.\n     */\n    public static function findAll($condition);\n\n    /**\n     * Updates records using the provided attribute values and conditions.\n     *\n     * For example, to change the status to be 1 for all customers whose status is 2:\n     *\n     * ```\n     * Customer::updateAll(['status' => 1], ['status' => '2']);\n     * ```\n     *\n     * @param array $attributes attribute values (name-value pairs) to be saved for the record.\n     * Unlike [[update()]] these are not going to be validated.\n     * @param mixed $condition the condition that matches the records that should get updated.\n     * Please refer to [[QueryInterface::where()]] on how to specify this parameter.\n     * An empty condition will match all records.\n     * @return int the number of rows updated\n     */\n    public static function updateAll($attributes, $condition = null);\n\n    /**\n     * Deletes records using the provided conditions.\n     * WARNING: If you do not specify any condition, this method will delete ALL rows in the table.\n     *\n     * For example, to delete all customers whose status is 3:\n     *\n     * ```\n     * Customer::deleteAll([status = 3]);\n     * ```\n     *\n     * @param array|null $condition the condition that matches the records that should get deleted.\n     * Please refer to [[QueryInterface::where()]] on how to specify this parameter.\n     * An empty condition will match all records.\n     * @return int the number of rows deleted\n     */\n    public static function deleteAll($condition = null);\n\n    /**\n     * Saves the current record.\n     *\n     * This method will call [[insert()]] when [[getIsNewRecord()|isNewRecord]] is true, or [[update()]]\n     * when [[getIsNewRecord()|isNewRecord]] is false.\n     *\n     * For example, to save a customer record:\n     *\n     * ```\n     * $customer = new Customer; // or $customer = Customer::findOne($id);\n     * $customer->name = $name;\n     * $customer->email = $email;\n     * $customer->save();\n     * ```\n     *\n     * @param bool $runValidation whether to perform validation (calling [[\\yii\\base\\Model::validate()|validate()]])\n     * before saving the record. Defaults to `true`. If the validation fails, the record\n     * will not be saved to the database and this method will return `false`.\n     * @param array|null $attributeNames list of attribute names that need to be saved. Defaults to `null`,\n     * meaning all attributes that are loaded from DB will be saved.\n     * @return bool whether the saving succeeded (i.e. no validation errors occurred).\n     */\n    public function save($runValidation = true, $attributeNames = null);\n\n    /**\n     * Inserts the record into the database using the attribute values of this record.\n     *\n     * Usage example:\n     *\n     * ```\n     * $customer = new Customer;\n     * $customer->name = $name;\n     * $customer->email = $email;\n     * $customer->insert();\n     * ```\n     *\n     * @param bool $runValidation whether to perform validation (calling [[\\yii\\base\\Model::validate()|validate()]])\n     * before saving the record. Defaults to `true`. If the validation fails, the record\n     * will not be saved to the database and this method will return `false`.\n     * @param array|null $attributes list of attributes that need to be saved. Defaults to `null`,\n     * meaning all attributes that are loaded from DB will be saved.\n     * @return bool whether the attributes are valid and the record is inserted successfully.\n     */\n    public function insert($runValidation = true, $attributes = null);\n\n    /**\n     * Saves the changes to this active record into the database.\n     *\n     * Usage example:\n     *\n     * ```\n     * $customer = Customer::findOne($id);\n     * $customer->name = $name;\n     * $customer->email = $email;\n     * $customer->update();\n     * ```\n     *\n     * @param bool $runValidation whether to perform validation (calling [[\\yii\\base\\Model::validate()|validate()]])\n     * before saving the record. Defaults to `true`. If the validation fails, the record\n     * will not be saved to the database and this method will return `false`.\n     * @param array|null $attributeNames list of attributes that need to be saved. Defaults to `null`,\n     * meaning all attributes that are loaded from DB will be saved.\n     * @return int|bool the number of rows affected, or `false` if validation fails\n     * or updating process is stopped for other reasons.\n     * Note that it is possible that the number of rows affected is 0, even though the\n     * update execution is successful.\n     */\n    public function update($runValidation = true, $attributeNames = null);\n\n    /**\n     * Deletes the record from the database.\n     *\n     * @return int|bool the number of rows deleted, or `false` if the deletion is unsuccessful for some reason.\n     * Note that it is possible that the number of rows deleted is 0, even though the deletion execution is successful.\n     */\n    public function delete();\n\n    /**\n     * Returns a value indicating whether the current record is new (not saved in the database).\n     * @return bool whether the record is new and should be inserted when calling [[save()]].\n     */\n    public function getIsNewRecord();\n\n    /**\n     * Returns a value indicating whether the given active record is the same as the current one.\n     * Two [[getIsNewRecord()|new]] records are considered to be not equal.\n     * @param static $record record to compare to\n     * @return bool whether the two active records refer to the same row in the same database table.\n     */\n    public function equals($record);\n\n    /**\n     * Returns the relation object with the specified name.\n     * A relation is defined by a getter method which returns an object implementing the [[ActiveQueryInterface]]\n     * (normally this would be a relational [[ActiveQuery]] object).\n     * It can be declared in either the ActiveRecord class itself or one of its behaviors.\n     * @param string $name the relation name, e.g. `orders` for a relation defined via `getOrders()` method (case-sensitive).\n     * @param bool $throwException whether to throw exception if the relation does not exist.\n     * @return ActiveQueryInterface the relational query object\n     */\n    public function getRelation($name, $throwException = true);\n\n    /**\n     * Populates the named relation with the related records.\n     * Note that this method does not check if the relation exists or not.\n     * @param string $name the relation name, e.g. `orders` for a relation defined via `getOrders()` method (case-sensitive).\n     * @param ActiveRecordInterface|array|null $records the related records to be populated into the relation.\n     * @since 2.0.8\n     */\n    public function populateRelation($name, $records);\n\n    /**\n     * Establishes the relationship between two records.\n     *\n     * The relationship is established by setting the foreign key value(s) in one record\n     * to be the corresponding primary key value(s) in the other record.\n     * The record with the foreign key will be saved into database without performing validation.\n     *\n     * If the relationship involves a junction table, a new row will be inserted into the\n     * junction table which contains the primary key values from both records.\n     *\n     * This method requires that the primary key value is not `null`.\n     *\n     * @param string $name the case sensitive name of the relationship, e.g. `orders` for a relation defined via `getOrders()` method.\n     * @param static $model the record to be linked with the current one.\n     * @param array $extraColumns additional column values to be saved into the junction table.\n     * This parameter is only meaningful for a relationship involving a junction table\n     * (i.e., a relation set with [[ActiveQueryInterface::via()]]).\n     */\n    public function link($name, $model, $extraColumns = []);\n\n    /**\n     * Destroys the relationship between two records.\n     *\n     * The record with the foreign key of the relationship will be deleted if `$delete` is true.\n     * Otherwise, the foreign key will be set `null` and the record will be saved without validation.\n     *\n     * @param string $name the case sensitive name of the relationship, e.g. `orders` for a relation defined via `getOrders()` method.\n     * @param static $model the model to be unlinked from the current one.\n     * @param bool $delete whether to delete the model that contains the foreign key.\n     * If false, the model's foreign key will be set `null` and saved.\n     * If true, the model containing the foreign key will be deleted.\n     */\n    public function unlink($name, $model, $delete = false);\n\n    /**\n     * Returns the connection used by this AR class.\n     * @return mixed the database connection used by this AR class.\n     */\n    public static function getDb();\n}\n"
  },
  {
    "path": "framework/db/ActiveRelationTrait.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\nuse yii\\base\\InvalidArgumentException;\nuse yii\\base\\InvalidConfigException;\n\n/**\n * ActiveRelationTrait implements the common methods and properties for active record relational queries.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0\n * @phpcs:disable Squiz.NamingConventions.ValidVariableName.PrivateNoUnderscore\n *\n * @method ActiveRecordInterface|array|null one($db = null) See [[ActiveQueryInterface::one()]] for more info.\n * @method ActiveRecordInterface[] all($db = null) See [[ActiveQueryInterface::all()]] for more info.\n * @property class-string<ActiveRecordInterface> $modelClass\n */\ntrait ActiveRelationTrait\n{\n    /**\n     * @var bool whether this query represents a relation to more than one record.\n     * This property is only used in relational context. If true, this relation will\n     * populate all query results into AR instances using [[Query::all()|all()]].\n     * If false, only the first row of the results will be retrieved using [[Query::one()|one()]].\n     */\n    public $multiple;\n    /**\n     * @var ActiveRecord the primary model of a relational query.\n     * This is used only in lazy loading with dynamic query options.\n     */\n    public $primaryModel;\n    /**\n     * @var array the columns of the primary and foreign tables that establish a relation.\n     * The array keys must be columns of the table for this relation, and the array values\n     * must be the corresponding columns from the primary table.\n     * Do not prefix or quote the column names as this will be done automatically by Yii.\n     * This property is only used in relational context.\n     */\n    public $link;\n    /**\n     * @var array|object|null the query associated with the junction table. Please call [[via()]]\n     * to set this property instead of directly setting it.\n     * This property is only used in relational context.\n     * @see via()\n     */\n    public $via;\n    /**\n     * @var string the name of the relation that is the inverse of this relation.\n     * For example, an order has a customer, which means the inverse of the \"customer\" relation\n     * is the \"orders\", and the inverse of the \"orders\" relation is the \"customer\".\n     * If this property is set, the primary record(s) will be referenced through the specified relation.\n     * For example, `$customer->orders[0]->customer` and `$customer` will be the same object,\n     * and accessing the customer of an order will not trigger new DB query.\n     * This property is only used in relational context.\n     * @see inverseOf()\n     */\n    public $inverseOf;\n\n    private $viaMap;\n\n    /**\n     * Clones internal objects.\n     */\n    public function __clone()\n    {\n        parent::__clone();\n        // make a clone of \"via\" object so that the same query object can be reused multiple times\n        if (is_object($this->via)) {\n            $this->via = clone $this->via;\n        } elseif (is_array($this->via)) {\n            $this->via = [$this->via[0], clone $this->via[1], $this->via[2]];\n        }\n    }\n\n    /**\n     * Specifies the relation associated with the junction table.\n     *\n     * Use this method to specify a pivot record/table when declaring a relation in the [[ActiveRecord]] class:\n     *\n     * ```\n     * class Order extends ActiveRecord\n     * {\n     *    public function getOrderItems() {\n     *        return $this->hasMany(OrderItem::class, ['order_id' => 'id']);\n     *    }\n     *\n     *    public function getItems() {\n     *        return $this->hasMany(Item::class, ['id' => 'item_id'])\n     *                    ->via('orderItems');\n     *    }\n     * }\n     * ```\n     *\n     * @param string $relationName the relation name. This refers to a relation declared in [[primaryModel]].\n     * @param callable|null $callable a PHP callback for customizing the relation associated with the junction table.\n     * Its signature should be `function($query)`, where `$query` is the query to be customized.\n     * @return $this the relation object itself.\n     */\n    public function via($relationName, ?callable $callable = null)\n    {\n        $relation = $this->primaryModel->getRelation($relationName);\n        $callableUsed = $callable !== null;\n        $this->via = [$relationName, $relation, $callableUsed];\n        if ($callable !== null) {\n            call_user_func($callable, $relation);\n        }\n\n        return $this;\n    }\n\n    /**\n     * Sets the name of the relation that is the inverse of this relation.\n     * For example, a customer has orders, which means the inverse of the \"orders\" relation is the \"customer\".\n     * If this property is set, the primary record(s) will be referenced through the specified relation.\n     * For example, `$customer->orders[0]->customer` and `$customer` will be the same object,\n     * and accessing the customer of an order will not trigger a new DB query.\n     *\n     * Use this method when declaring a relation in the [[ActiveRecord]] class, e.g. in Customer model:\n     *\n     * ```\n     * public function getOrders()\n     * {\n     *     return $this->hasMany(Order::class, ['customer_id' => 'id'])->inverseOf('customer');\n     * }\n     * ```\n     *\n     * This also may be used for Order model, but with caution:\n     *\n     * ```\n     * public function getCustomer()\n     * {\n     *     return $this->hasOne(Customer::class, ['id' => 'customer_id'])->inverseOf('orders');\n     * }\n     * ```\n     *\n     * in this case result will depend on how order(s) was loaded.\n     * Let's suppose customer has several orders. If only one order was loaded:\n     *\n     * ```\n     * $orders = Order::find()->where(['id' => 1])->all();\n     * $customerOrders = $orders[0]->customer->orders;\n     * ```\n     *\n     * variable `$customerOrders` will contain only one order. If orders was loaded like this:\n     *\n     * ```\n     * $orders = Order::find()->with('customer')->where(['customer_id' => 1])->all();\n     * $customerOrders = $orders[0]->customer->orders;\n     * ```\n     *\n     * variable `$customerOrders` will contain all orders of the customer.\n     *\n     * @param string $relationName the name of the relation that is the inverse of this relation.\n     * @return $this the relation object itself.\n     */\n    public function inverseOf($relationName)\n    {\n        $this->inverseOf = $relationName;\n        return $this;\n    }\n\n    /**\n     * Finds the related records for the specified primary record.\n     * This method is invoked when a relation of an ActiveRecord is being accessed lazily.\n     * @param string $name the relation name\n     * @param ActiveRecordInterface|BaseActiveRecord $model the primary model\n     * @return mixed the related record(s)\n     * @throws InvalidArgumentException if the relation is invalid\n     */\n    public function findFor($name, $model)\n    {\n        if (method_exists($model, 'get' . $name)) {\n            $method = new \\ReflectionMethod($model, 'get' . $name);\n            $realName = lcfirst(substr($method->getName(), 3));\n            if ($realName !== $name) {\n                throw new InvalidArgumentException('Relation names are case sensitive. ' . get_class($model) . \" has a relation named \\\"$realName\\\" instead of \\\"$name\\\".\");\n            }\n        }\n\n        return $this->multiple ? $this->all() : $this->one();\n    }\n\n    /**\n     * If applicable, populate the query's primary model into the related records' inverse relationship.\n     * @param array $result the array of related records as generated by [[populate()]]\n     * @since 2.0.9\n     */\n    private function addInverseRelations(&$result)\n    {\n        if ($this->inverseOf === null) {\n            return;\n        }\n\n        foreach ($result as $i => $relatedModel) {\n            if ($relatedModel instanceof ActiveRecordInterface) {\n                if (!isset($inverseRelation)) {\n                    $inverseRelation = $relatedModel->getRelation($this->inverseOf);\n                }\n                $relatedModel->populateRelation($this->inverseOf, $inverseRelation->multiple ? [$this->primaryModel] : $this->primaryModel);\n            } else {\n                if (!isset($inverseRelation)) {\n                    /** @var ActiveRecordInterface $modelClass */\n                    $modelClass = $this->modelClass;\n                    $inverseRelation = $modelClass::instance()->getRelation($this->inverseOf);\n                }\n                $result[$i][$this->inverseOf] = $inverseRelation->multiple ? [$this->primaryModel] : $this->primaryModel;\n            }\n        }\n    }\n\n    /**\n     * Finds the related records and populates them into the primary models.\n     * @param string $name the relation name\n     * @param array $primaryModels primary models\n     * @return array the related models\n     * @throws InvalidConfigException if [[link]] is invalid\n     */\n    public function populateRelation($name, &$primaryModels)\n    {\n        if (!is_array($this->link)) {\n            throw new InvalidConfigException('Invalid link: it must be an array of key-value pairs.');\n        }\n\n        if ($this->via instanceof self) {\n            // via junction table\n            /** @var self<ActiveRecord|array<string, mixed>> $viaQuery */\n            $viaQuery = $this->via;\n            $viaModels = $viaQuery->findJunctionRows($primaryModels);\n            $this->filterByModels($viaModels);\n        } elseif (is_array($this->via)) {\n            // via relation\n            /** @var self<ActiveRecord|array<string, mixed>>|ActiveQueryTrait $viaQuery */\n            list($viaName, $viaQuery) = $this->via;\n            if ($viaQuery->asArray === null) {\n                // inherit asArray from primary query\n                $viaQuery->asArray($this->asArray);\n            }\n            $viaQuery->primaryModel = null;\n            $viaModels = array_filter($viaQuery->populateRelation($viaName, $primaryModels));\n            $this->filterByModels($viaModels);\n        } else {\n            $this->filterByModels($primaryModels);\n        }\n\n        if (!$this->multiple && count($primaryModels) === 1) {\n            $model = $this->one();\n            $primaryModel = reset($primaryModels);\n            if ($primaryModel instanceof ActiveRecordInterface) {\n                $primaryModel->populateRelation($name, $model);\n            } else {\n                $primaryModels[key($primaryModels)][$name] = $model;\n            }\n            if ($this->inverseOf !== null) {\n                $this->populateInverseRelation($primaryModels, [$model], $name, $this->inverseOf);\n            }\n\n            return [$model];\n        }\n\n        // https://github.com/yiisoft/yii2/issues/3197\n        // delay indexing related models after buckets are built\n        $indexBy = $this->indexBy;\n        $this->indexBy = null;\n        $models = $this->all();\n\n        if (isset($viaModels, $viaQuery)) {\n            $buckets = $this->buildBuckets($models, $this->link, $viaModels, $viaQuery);\n        } else {\n            $buckets = $this->buildBuckets($models, $this->link);\n        }\n\n        $this->indexBy = $indexBy;\n        if ($this->indexBy !== null && $this->multiple) {\n            $buckets = $this->indexBuckets($buckets, $this->indexBy);\n        }\n\n        $link = array_values($this->link);\n        if (isset($viaQuery)) {\n            $deepViaQuery = $viaQuery;\n            while ($deepViaQuery->via) {\n                $deepViaQuery = is_array($deepViaQuery->via) ? $deepViaQuery->via[1] : $deepViaQuery->via;\n            };\n            $link = array_values($deepViaQuery->link);\n        }\n        foreach ($primaryModels as $i => $primaryModel) {\n            $keys = null;\n            if ($this->multiple && count($link) === 1) {\n                $primaryModelKey = reset($link);\n                $keys = isset($primaryModel[$primaryModelKey]) ? $primaryModel[$primaryModelKey] : null;\n            }\n            if (is_array($keys)) {\n                $value = [];\n                foreach ($keys as $key) {\n                    $key = $this->normalizeModelKey($key);\n                    if (isset($buckets[$key])) {\n                        if ($this->indexBy !== null) {\n                            // if indexBy is set, array_merge will cause renumbering of numeric array\n                            foreach ($buckets[$key] as $bucketKey => $bucketValue) {\n                                $value[$bucketKey] = $bucketValue;\n                            }\n                        } else {\n                            $value = array_merge($value, $buckets[$key]);\n                        }\n                    }\n                }\n            } else {\n                $key = $this->getModelKey($primaryModel, $link);\n                $value = isset($buckets[$key]) ? $buckets[$key] : ($this->multiple ? [] : null);\n            }\n            if ($primaryModel instanceof ActiveRecordInterface) {\n                $primaryModel->populateRelation($name, $value);\n            } else {\n                $primaryModels[$i][$name] = $value;\n            }\n        }\n        if ($this->inverseOf !== null) {\n            $this->populateInverseRelation($primaryModels, $models, $name, $this->inverseOf);\n        }\n\n        return $models;\n    }\n\n    /**\n     * @param ActiveRecordInterface[]|array<array-key, array<string, mixed>> $primaryModels primary models\n     * @param ActiveRecordInterface[] $models models\n     * @param string $primaryName the primary relation name\n     * @param string $name the relation name\n     */\n    private function populateInverseRelation(&$primaryModels, $models, $primaryName, $name)\n    {\n        if (empty($models) || empty($primaryModels)) {\n            return;\n        }\n        $model = reset($models);\n        if ($model instanceof ActiveRecordInterface) {\n            $relation = $model->getRelation($name);\n        } else {\n            /** @var ActiveRecordInterface $modelClass */\n            $modelClass = $this->modelClass;\n            $relation = $modelClass::instance()->getRelation($name);\n        }\n\n        /** @var ActiveQueryInterface|ActiveQuery $relation */\n        if ($relation->multiple) {\n            $buckets = $this->buildBuckets($primaryModels, $relation->link, null, null, false);\n            if ($model instanceof ActiveRecordInterface) {\n                foreach ($models as $model) {\n                    $key = $this->getModelKey($model, $relation->link);\n                    $model->populateRelation($name, isset($buckets[$key]) ? $buckets[$key] : []);\n                }\n            } else {\n                foreach ($primaryModels as $i => $primaryModel) {\n                    if ($this->multiple) {\n                        foreach ($primaryModel as $j => $m) {\n                            $key = $this->getModelKey($m, $relation->link);\n                            $primaryModels[$i][$j][$name] = isset($buckets[$key]) ? $buckets[$key] : [];\n                        }\n                    } elseif (!empty($primaryModel[$primaryName])) {\n                        $key = $this->getModelKey($primaryModel[$primaryName], $relation->link);\n                        $primaryModels[$i][$primaryName][$name] = isset($buckets[$key]) ? $buckets[$key] : [];\n                    }\n                }\n            }\n        } elseif ($this->multiple) {\n            foreach ($primaryModels as $i => $primaryModel) {\n                foreach ($primaryModel[$primaryName] as $j => $m) {\n                    if ($m instanceof ActiveRecordInterface) {\n                        $m->populateRelation($name, $primaryModel);\n                    } else {\n                        $primaryModels[$i][$primaryName][$j][$name] = $primaryModel;\n                    }\n                }\n            }\n        } else {\n            foreach ($primaryModels as $i => $primaryModel) {\n                if ($primaryModels[$i][$primaryName] instanceof ActiveRecordInterface) {\n                    $primaryModels[$i][$primaryName]->populateRelation($name, $primaryModel);\n                } elseif (!empty($primaryModels[$i][$primaryName])) {\n                    $primaryModels[$i][$primaryName][$name] = $primaryModel;\n                }\n            }\n        }\n    }\n\n    /**\n     * @param array $models\n     * @param array $link\n     * @param array|null $viaModels\n     * @param self<ActiveRecord|array<string, mixed>>|null $viaQuery\n     * @param bool $checkMultiple\n     * @return array\n     */\n    private function buildBuckets($models, $link, $viaModels = null, $viaQuery = null, $checkMultiple = true)\n    {\n        if ($viaModels !== null) {\n            $map = [];\n            $viaLink = $viaQuery->link;\n            $viaLinkKeys = array_keys($viaLink);\n            $linkValues = array_values($link);\n            foreach ($viaModels as $viaModel) {\n                $key1 = $this->getModelKey($viaModel, $viaLinkKeys);\n                $key2 = $this->getModelKey($viaModel, $linkValues);\n                $map[$key2][$key1] = true;\n            }\n\n            $viaQuery->viaMap = $map;\n\n            $viaVia = $viaQuery->via;\n            while ($viaVia) {\n                $viaViaQuery = is_array($viaVia) ? $viaVia[1] : $viaVia;\n                $map = $this->mapVia($map, $viaViaQuery->viaMap);\n\n                $viaVia = $viaViaQuery->via;\n            };\n        }\n\n        $buckets = [];\n        $linkKeys = array_keys($link);\n\n        if (isset($map)) {\n            foreach ($models as $model) {\n                $key = $this->getModelKey($model, $linkKeys);\n                if (isset($map[$key])) {\n                    foreach (array_keys($map[$key]) as $key2) {\n                        $buckets[$key2][] = $model;\n                    }\n                }\n            }\n        } else {\n            foreach ($models as $model) {\n                $key = $this->getModelKey($model, $linkKeys);\n                $buckets[$key][] = $model;\n            }\n        }\n\n        if ($checkMultiple && !$this->multiple) {\n            foreach ($buckets as $i => $bucket) {\n                $buckets[$i] = reset($bucket);\n            }\n        }\n\n        return $buckets;\n    }\n\n    /**\n     * @param array $map\n     * @param array $viaMap\n     * @return array\n     */\n    private function mapVia($map, $viaMap)\n    {\n        $resultMap = [];\n        foreach ($map as $key => $linkKeys) {\n            $resultMap[$key] = [];\n            foreach (array_keys($linkKeys) as $linkKey) {\n                $resultMap[$key] += $viaMap[$linkKey];\n            }\n        }\n        return $resultMap;\n    }\n\n    /**\n     * Indexes buckets by column name.\n     *\n     * @param array $buckets\n     * @param string|callable $indexBy the name of the column by which the query results should be indexed by.\n     * This can also be a callable (e.g. anonymous function) that returns the index value based on the given row data.\n     * @return array\n     */\n    private function indexBuckets($buckets, $indexBy)\n    {\n        $result = [];\n        foreach ($buckets as $key => $models) {\n            $result[$key] = [];\n            foreach ($models as $model) {\n                $index = is_string($indexBy) ? $model[$indexBy] : call_user_func($indexBy, $model);\n                $result[$key][$index] = $model;\n            }\n        }\n\n        return $result;\n    }\n\n    /**\n     * @param array $attributes the attributes to prefix\n     * @return array\n     */\n    private function prefixKeyColumns($attributes)\n    {\n        if ($this instanceof ActiveQuery && (!empty($this->join) || !empty($this->joinWith))) {\n            if (empty($this->from)) {\n                /** @var ActiveRecord $modelClass */\n                $modelClass = $this->modelClass;\n                $alias = $modelClass::tableName();\n            } else {\n                foreach ($this->from as $alias => $table) {\n                    if (!is_string($alias)) {\n                        $alias = $table;\n                    }\n                    break;\n                }\n            }\n            if (isset($alias)) {\n                foreach ($attributes as $i => $attribute) {\n                    $attributes[$i] = \"$alias.$attribute\";\n                }\n            }\n        }\n\n        return $attributes;\n    }\n\n    /**\n     * @param array $models\n     */\n    private function filterByModels($models)\n    {\n        $attributes = array_keys($this->link);\n\n        $attributes = $this->prefixKeyColumns($attributes);\n\n        $values = [];\n        if (count($attributes) === 1) {\n            // single key\n            $attribute = reset($this->link);\n            foreach ($models as $model) {\n                $value = isset($model[$attribute]) || (is_object($model) && property_exists($model, $attribute)) ? $model[$attribute] : null;\n                if ($value !== null) {\n                    if (is_array($value)) {\n                        $values = array_merge($values, $value);\n                    } elseif ($value instanceof ArrayExpression && $value->getDimension() === 1) {\n                        $values = array_merge($values, $value->getValue());\n                    } else {\n                        $values[] = $value;\n                    }\n                }\n            }\n            if (empty($values)) {\n                $this->emulateExecution();\n            }\n        } else {\n            // composite keys\n\n            // ensure keys of $this->link are prefixed the same way as $attributes\n            $prefixedLink = array_combine($attributes, $this->link);\n            foreach ($models as $model) {\n                $v = [];\n                foreach ($prefixedLink as $attribute => $link) {\n                    $v[$attribute] = $model[$link];\n                }\n                $values[] = $v;\n                if (empty($v)) {\n                    $this->emulateExecution();\n                }\n            }\n        }\n\n        if (!empty($values)) {\n            $scalarValues = [];\n            $nonScalarValues = [];\n            foreach ($values as $value) {\n                if (is_scalar($value)) {\n                    $scalarValues[] = $value;\n                } else {\n                    $nonScalarValues[] = $value;\n                }\n            }\n\n            $scalarValues = array_unique($scalarValues);\n            $values = array_merge($scalarValues, $nonScalarValues);\n        }\n\n        $this->andWhere(['in', $attributes, $values]);\n    }\n\n    /**\n     * @param ActiveRecordInterface|array $model\n     * @param array $attributes\n     * @return string|false\n     */\n    private function getModelKey($model, $attributes)\n    {\n        $key = [];\n        foreach ($attributes as $attribute) {\n            if (isset($model[$attribute]) || (is_object($model) && property_exists($model, $attribute))) {\n                $key[] = $this->normalizeModelKey($model[$attribute]);\n            }\n        }\n        if (count($key) > 1) {\n            return serialize($key);\n        }\n        return reset($key);\n    }\n\n    /**\n     * @param mixed $value raw key value. Since 2.0.40 non-string values must be convertible to string (like special\n     * objects for cross-DBMS relations, for example: `|MongoId`).\n     * @return string normalized key value.\n     */\n    private function normalizeModelKey($value)\n    {\n        try {\n            return (string)$value;\n        } catch (\\Exception $e) {\n            throw new InvalidConfigException('Value must be convertable to string.');\n        } catch (\\Throwable $e) {\n            throw new InvalidConfigException('Value must be convertable to string.');\n        }\n    }\n\n    /**\n     * @param array $primaryModels either array of AR instances or arrays\n     * @return array\n     */\n    private function findJunctionRows($primaryModels)\n    {\n        if (empty($primaryModels)) {\n            return [];\n        }\n        $this->filterByModels($primaryModels);\n        /** @var ActiveRecord $primaryModel */\n        $primaryModel = reset($primaryModels);\n        if (!$primaryModel instanceof ActiveRecordInterface) {\n            // when primaryModels are array of arrays (asArray case)\n            $primaryModel = $this->modelClass;\n        }\n\n        return $this->asArray()->all($primaryModel::getDb());\n    }\n}\n"
  },
  {
    "path": "framework/db/AfterSaveEvent.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\nuse yii\\base\\Event;\n\n/**\n * AfterSaveEvent represents the information available in [[ActiveRecord::EVENT_AFTER_INSERT]] and [[ActiveRecord::EVENT_AFTER_UPDATE]].\n *\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0\n */\nclass AfterSaveEvent extends Event\n{\n    /**\n     * @var array The attribute values that had changed and were saved.\n     */\n    public $changedAttributes;\n}\n"
  },
  {
    "path": "framework/db/ArrayExpression.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\nuse Traversable;\nuse yii\\base\\InvalidConfigException;\n\n/**\n * Class ArrayExpression represents an array SQL expression.\n *\n * Expressions of this type can be used in conditions as well:\n *\n * ```\n * $query->andWhere(['@>', 'items', new ArrayExpression([1, 2, 3], 'integer')])\n * ```\n *\n * which, depending on DBMS, will result in a well-prepared condition. For example, in\n * PostgreSQL it will be compiled to `WHERE \"items\" @> ARRAY[1, 2, 3]::integer[]`.\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n * @phpcs:disable Squiz.NamingConventions.ValidVariableName.PrivateNoUnderscore\n *\n * @implements \\ArrayAccess<array-key, mixed>\n * @implements \\IteratorAggregate<array-key, mixed>\n */\nclass ArrayExpression implements ExpressionInterface, \\ArrayAccess, \\Countable, \\IteratorAggregate\n{\n    /**\n     * @var string|null the type of the array elements. Defaults to `null` which means the type is\n     * not explicitly specified.\n     *\n     * Note that in case when type is not specified explicitly and DBMS can not guess it from the context,\n     * SQL error will be raised.\n     */\n    private $type;\n    /**\n     * @var array|QueryInterface the array's content.\n     * In can be represented as an array of values or a [[Query]] that returns these values.\n     */\n    private $value;\n    /**\n     * @var int the number of indices needed to select an element\n     */\n    private $dimension;\n\n\n    /**\n     * ArrayExpression constructor.\n     *\n     * @param array|QueryInterface|mixed $value the array content. Either represented as an array of values or a Query that\n     * returns these values. A single value will be considered as an array containing one element.\n     * @param string|null $type the type of the array elements. Defaults to `null` which means the type is\n     * not explicitly specified. In case when type is not specified explicitly and DBMS can not guess it from the context,\n     * SQL error will be raised.\n     * @param int $dimension the number of indices needed to select an element\n     */\n    public function __construct($value, $type = null, $dimension = 1)\n    {\n        if ($value instanceof self) {\n            $value = $value->getValue();\n        }\n\n        $this->value = $value;\n        $this->type = $type;\n        $this->dimension = $dimension;\n    }\n\n    /**\n     * @return string|null\n     */\n    public function getType()\n    {\n        return $this->type;\n    }\n\n    /**\n     * @return array|mixed|QueryInterface\n     */\n    public function getValue()\n    {\n        return $this->value;\n    }\n\n    /**\n     * @return int the number of indices needed to select an element\n     */\n    public function getDimension()\n    {\n        return $this->dimension;\n    }\n\n    /**\n     * Whether a offset exists\n     *\n     * @link https://www.php.net/manual/en/arrayaccess.offsetexists.php\n     * @param int|string $offset <p>\n     * An offset to check for.\n     * </p>\n     * @return bool true on success or false on failure.\n     * </p>\n     * <p>\n     * The return value will be casted to boolean if non-boolean was returned.\n     * @since 2.0.14\n     */\n    #[\\ReturnTypeWillChange]\n    public function offsetExists($offset)\n    {\n        return isset($this->value[$offset]);\n    }\n\n    /**\n     * Offset to retrieve\n     *\n     * @link https://www.php.net/manual/en/arrayaccess.offsetget.php\n     * @param int|string $offset <p>\n     * The offset to retrieve.\n     * </p>\n     * @return mixed Can return all value types.\n     * @since 2.0.14\n     */\n    #[\\ReturnTypeWillChange]\n    public function offsetGet($offset)\n    {\n        return $this->value[$offset];\n    }\n\n    /**\n     * Offset to set\n     *\n     * @link https://www.php.net/manual/en/arrayaccess.offsetset.php\n     * @param int|string $offset <p>\n     * The offset to assign the value to.\n     * </p>\n     * @param mixed $value <p>\n     * The value to set.\n     * </p>\n     * @return void\n     * @since 2.0.14\n     */\n    #[\\ReturnTypeWillChange]\n    public function offsetSet($offset, $value)\n    {\n        $this->value[$offset] = $value;\n    }\n\n    /**\n     * Offset to unset\n     *\n     * @link https://www.php.net/manual/en/arrayaccess.offsetunset.php\n     * @param int|string $offset <p>\n     * The offset to unset.\n     * </p>\n     * @return void\n     * @since 2.0.14\n     */\n    #[\\ReturnTypeWillChange]\n    public function offsetUnset($offset)\n    {\n        unset($this->value[$offset]);\n    }\n\n    /**\n     * Count elements of an object\n     *\n     * @link https://www.php.net/manual/en/countable.count.php\n     * @return int The custom count as an integer.\n     * </p>\n     * <p>\n     * The return value is cast to an integer.\n     * @since 2.0.14\n     */\n    #[\\ReturnTypeWillChange]\n    public function count()\n    {\n        return count($this->value);\n    }\n\n    /**\n     * Retrieve an external iterator\n     *\n     * @link https://www.php.net/manual/en/iteratoraggregate.getiterator.php\n     * @return Traversable An instance of an object implementing <b>Iterator</b> or\n     * <b>Traversable</b>\n     * @since 2.0.14.1\n     * @throws InvalidConfigException when ArrayExpression contains QueryInterface object\n     */\n    #[\\ReturnTypeWillChange]\n    public function getIterator()\n    {\n        $value = $this->getValue();\n        if ($value instanceof QueryInterface) {\n            throw new InvalidConfigException('The ArrayExpression class can not be iterated when the value is a QueryInterface object');\n        }\n        if ($value === null) {\n            $value = [];\n        }\n\n        return new \\ArrayIterator($value);\n    }\n}\n"
  },
  {
    "path": "framework/db/BaseActiveRecord.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\nuse Yii;\nuse yii\\base\\InvalidArgumentException;\nuse yii\\base\\InvalidCallException;\nuse yii\\base\\InvalidConfigException;\nuse yii\\base\\InvalidParamException;\nuse yii\\base\\Model;\nuse yii\\base\\ModelEvent;\nuse yii\\base\\NotSupportedException;\nuse yii\\base\\UnknownMethodException;\nuse yii\\helpers\\ArrayHelper;\n\n/**\n * ActiveRecord is the base class for classes representing relational data in terms of objects.\n *\n * See [[\\yii\\db\\ActiveRecord]] for a concrete implementation.\n *\n * @property-read array $dirtyAttributes The changed attribute values (name-value pairs).\n * @property bool $isNewRecord Whether the record is new and should be inserted when calling [[save()]].\n * @property array $oldAttributes The old attribute values (name-value pairs). Note that the type of this\n * property differs in getter and setter. See [[getOldAttributes()]] and [[setOldAttributes()]] for details.\n * @property-read mixed $oldPrimaryKey The old primary key value. An array (column name => column value) is\n * returned if the primary key is composite or `$asArray` is `true`. A string is returned otherwise (null will be\n * returned if the key value is null).\n * @property-read mixed $primaryKey The primary key value. An array (column name => column value) is returned\n * if the primary key is composite or `$asArray` is `true`. A string is returned otherwise (null will be returned\n * if the key value is null).\n * @property-read array $relatedRecords An array of related records indexed by relation names.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0\n */\nabstract class BaseActiveRecord extends Model implements ActiveRecordInterface\n{\n    /**\n     * @event Event an event that is triggered when the record is initialized via [[init()]].\n     */\n    public const EVENT_INIT = 'init';\n    /**\n     * @event Event an event that is triggered after the record is created and populated with query result.\n     */\n    public const EVENT_AFTER_FIND = 'afterFind';\n    /**\n     * @event ModelEvent an event that is triggered before inserting a record.\n     * You may set [[ModelEvent::isValid]] to be `false` to stop the insertion.\n     */\n    public const EVENT_BEFORE_INSERT = 'beforeInsert';\n    /**\n     * @event AfterSaveEvent an event that is triggered after a record is inserted.\n     */\n    public const EVENT_AFTER_INSERT = 'afterInsert';\n    /**\n     * @event ModelEvent an event that is triggered before updating a record.\n     * You may set [[ModelEvent::isValid]] to be `false` to stop the update.\n     */\n    public const EVENT_BEFORE_UPDATE = 'beforeUpdate';\n    /**\n     * @event AfterSaveEvent an event that is triggered after a record is updated.\n     */\n    public const EVENT_AFTER_UPDATE = 'afterUpdate';\n    /**\n     * @event ModelEvent an event that is triggered before deleting a record.\n     * You may set [[ModelEvent::isValid]] to be `false` to stop the deletion.\n     */\n    public const EVENT_BEFORE_DELETE = 'beforeDelete';\n    /**\n     * @event Event an event that is triggered after a record is deleted.\n     */\n    public const EVENT_AFTER_DELETE = 'afterDelete';\n    /**\n     * @event Event an event that is triggered after a record is refreshed.\n     * @since 2.0.8\n     */\n    public const EVENT_AFTER_REFRESH = 'afterRefresh';\n    /**\n     * @var array attribute values indexed by attribute names\n     */\n    private $_attributes = [];\n    /**\n     * @var array|null old attribute values indexed by attribute names.\n     * This is `null` if the record [[isNewRecord|is new]].\n     */\n    private $_oldAttributes;\n    /**\n     * @var array related models indexed by the relation names\n     */\n    private $_related = [];\n    /**\n     * @var array relation names indexed by their link attributes\n     */\n    private $_relationsDependencies = [];\n\n\n    /**\n     * {@inheritdoc}\n     * @return static|null ActiveRecord instance matching the condition, or `null` if nothing matches.\n     */\n    public static function findOne($condition)\n    {\n        return static::findByCondition($condition)->one();\n    }\n\n    /**\n     * {@inheritdoc}\n     * @return static[] an array of ActiveRecord instances, or an empty array if nothing matches.\n     */\n    public static function findAll($condition)\n    {\n        return static::findByCondition($condition)->all();\n    }\n\n    /**\n     * Finds ActiveRecord instance(s) by the given condition.\n     * This method is internally called by [[findOne()]] and [[findAll()]].\n     * @param mixed $condition please refer to [[findOne()]] for the explanation of this parameter\n     * @return ActiveQueryInterface the newly created [[ActiveQueryInterface|ActiveQuery]] instance.\n     * @throws InvalidConfigException if there is no primary key defined\n     * @internal\n     */\n    protected static function findByCondition($condition)\n    {\n        $query = static::find();\n\n        if (!ArrayHelper::isAssociative($condition) && !$condition instanceof ExpressionInterface) {\n            // query by primary key\n            $primaryKey = static::primaryKey();\n            if (isset($primaryKey[0])) {\n                // if condition is scalar, search for a single primary key, if it is array, search for multiple primary key values\n                $condition = [$primaryKey[0] => is_array($condition) ? array_values($condition) : $condition];\n            } else {\n                throw new InvalidConfigException('\"' . get_called_class() . '\" must have a primary key.');\n            }\n        }\n\n        return $query->andWhere($condition);\n    }\n\n    /**\n     * Updates the whole table using the provided attribute values and conditions.\n     *\n     * For example, to change the status to be 1 for all customers whose status is 2:\n     *\n     * ```\n     * Customer::updateAll(['status' => 1], 'status = 2');\n     * ```\n     *\n     * @param array $attributes attribute values (name-value pairs) to be saved into the table\n     * @param string|array $condition the conditions that will be put in the WHERE part of the UPDATE SQL.\n     * Please refer to [[Query::where()]] on how to specify this parameter.\n     * @return int the number of rows updated\n     * @throws NotSupportedException if not overridden\n     */\n    public static function updateAll($attributes, $condition = '')\n    {\n        throw new NotSupportedException(__METHOD__ . ' is not supported.');\n    }\n\n    /**\n     * Updates the whole table using the provided counter changes and conditions.\n     *\n     * For example, to increment all customers' age by 1,\n     *\n     * ```\n     * Customer::updateAllCounters(['age' => 1]);\n     * ```\n     *\n     * @param array $counters the counters to be updated (attribute name => increment value).\n     * Use negative values if you want to decrement the counters.\n     * @param string|array $condition the conditions that will be put in the WHERE part of the UPDATE SQL.\n     * Please refer to [[Query::where()]] on how to specify this parameter.\n     * @return int the number of rows updated\n     * @throws NotSupportedException if not overrided\n     */\n    public static function updateAllCounters($counters, $condition = '')\n    {\n        throw new NotSupportedException(__METHOD__ . ' is not supported.');\n    }\n\n    /**\n     * Deletes rows in the table using the provided conditions.\n     * WARNING: If you do not specify any condition, this method will delete ALL rows in the table.\n     *\n     * For example, to delete all customers whose status is 3:\n     *\n     * ```\n     * Customer::deleteAll('status = 3');\n     * ```\n     *\n     * @param string|array|null $condition the conditions that will be put in the WHERE part of the DELETE SQL.\n     * Please refer to [[Query::where()]] on how to specify this parameter.\n     * @return int the number of rows deleted\n     * @throws NotSupportedException if not overridden.\n     */\n    public static function deleteAll($condition = null)\n    {\n        throw new NotSupportedException(__METHOD__ . ' is not supported.');\n    }\n\n    /**\n     * Returns the name of the column that stores the lock version for implementing optimistic locking.\n     *\n     * Optimistic locking allows multiple users to access the same record for edits and avoids\n     * potential conflicts. In case when a user attempts to save the record upon some staled data\n     * (because another user has modified the data), a [[StaleObjectException]] exception will be thrown,\n     * and the update or deletion is skipped.\n     *\n     * Optimistic locking is only supported by [[update()]] and [[delete()]].\n     *\n     * To use Optimistic locking:\n     *\n     * 1. Create a column to store the version number of each row. The column type should be `BIGINT DEFAULT 0`.\n     *    Override this method to return the name of this column.\n     * 2. Ensure the version value is submitted and loaded to your model before any update or delete.\n     *    Or add [[\\yii\\behaviors\\OptimisticLockBehavior|OptimisticLockBehavior]] to your model\n     *    class in order to automate the process.\n     * 3. In the Web form that collects the user input, add a hidden field that stores\n     *    the lock version of the record being updated.\n     * 4. In the controller action that does the data updating, try to catch the [[StaleObjectException]]\n     *    and implement necessary business logic (e.g. merging the changes, prompting stated data)\n     *    to resolve the conflict.\n     *\n     * @return string|null the column name that stores the lock version of a table row.\n     * If `null` is returned (default implemented), optimistic locking will not be supported.\n     */\n    public function optimisticLock()\n    {\n        return null;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function canGetProperty($name, $checkVars = true, $checkBehaviors = true)\n    {\n        if (parent::canGetProperty($name, $checkVars, $checkBehaviors)) {\n            return true;\n        }\n\n        try {\n            return $this->hasAttribute($name);\n        } catch (\\Exception $e) {\n            // `hasAttribute()` may fail on base/abstract classes in case automatic attribute list fetching used\n            return false;\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function canSetProperty($name, $checkVars = true, $checkBehaviors = true)\n    {\n        if (parent::canSetProperty($name, $checkVars, $checkBehaviors)) {\n            return true;\n        }\n\n        try {\n            return $this->hasAttribute($name);\n        } catch (\\Exception $e) {\n            // `hasAttribute()` may fail on base/abstract classes in case automatic attribute list fetching used\n            return false;\n        }\n    }\n\n    /**\n     * PHP getter magic method.\n     * This method is overridden so that attributes and related objects can be accessed like properties.\n     *\n     * @param string $name property name\n     * @throws InvalidArgumentException if relation name is wrong\n     * @return mixed property value\n     * @see getAttribute()\n     */\n    public function __get($name)\n    {\n        if (array_key_exists($name, $this->_attributes)) {\n            return $this->_attributes[$name];\n        }\n\n        if ($this->hasAttribute($name)) {\n            return null;\n        }\n\n        if (array_key_exists($name, $this->_related)) {\n            return $this->_related[$name];\n        }\n        $value = parent::__get($name);\n        if ($value instanceof ActiveQueryInterface) {\n            $this->setRelationDependencies($name, $value);\n            return $this->_related[$name] = $value->findFor($name, $this);\n        }\n\n        return $value;\n    }\n\n    /**\n     * PHP setter magic method.\n     * This method is overridden so that AR attributes can be accessed like properties.\n     * @param string $name property name\n     * @param mixed $value property value\n     */\n    public function __set($name, $value)\n    {\n        if ($this->hasAttribute($name)) {\n            if (\n                !empty($this->_relationsDependencies[$name])\n                && (!array_key_exists($name, $this->_attributes) || $this->_attributes[$name] !== $value)\n            ) {\n                $this->resetDependentRelations($name);\n            }\n            $this->_attributes[$name] = $value;\n        } else {\n            parent::__set($name, $value);\n        }\n    }\n\n    /**\n     * Checks if a property value is null.\n     * This method overrides the parent implementation by checking if the named attribute is `null` or not.\n     * @param string $name the property name or the event name\n     * @return bool whether the property value is null\n     */\n    public function __isset($name)\n    {\n        try {\n            return $this->__get($name) !== null;\n        } catch (\\Exception $t) {\n            return false;\n        } catch (\\Throwable $e) {\n            return false;\n        }\n    }\n\n    /**\n     * Sets a component property to be null.\n     * This method overrides the parent implementation by clearing\n     * the specified attribute value.\n     * @param string $name the property name or the event name\n     */\n    public function __unset($name)\n    {\n        if ($this->hasAttribute($name)) {\n            unset($this->_attributes[$name]);\n            if (!empty($this->_relationsDependencies[$name])) {\n                $this->resetDependentRelations($name);\n            }\n        } elseif (array_key_exists($name, $this->_related)) {\n            unset($this->_related[$name]);\n        } elseif ($this->getRelation($name, false) === null) {\n            parent::__unset($name);\n        }\n    }\n\n    /**\n     * Declares a `has-one` relation.\n     * The declaration is returned in terms of a relational [[ActiveQuery]] instance\n     * through which the related record can be queried and retrieved back.\n     *\n     * A `has-one` relation means that there is at most one related record matching\n     * the criteria set by this relation, e.g., a customer has one country.\n     *\n     * For example, to declare the `country` relation for `Customer` class, we can write\n     * the following code in the `Customer` class:\n     *\n     * ```\n     * public function getCountry()\n     * {\n     *     return $this->hasOne(Country::class, ['id' => 'country_id']);\n     * }\n     * ```\n     *\n     * Note that in the above, the 'id' key in the `$link` parameter refers to an attribute name\n     * in the related class `Country`, while the 'country_id' value refers to an attribute name\n     * in the current AR class.\n     *\n     * Call methods declared in [[ActiveQuery]] to further customize the relation.\n     *\n     * @param class-string $class the class name of the related record\n     * @param array<string, string> $link the primary-foreign key constraint. The keys of the array refer to\n     * the attributes of the record associated with the `$class` model, while the values of the\n     * array refer to the corresponding attributes in **this** AR class.\n     * @return ActiveQueryInterface the relational query object.\n     */\n    public function hasOne($class, $link)\n    {\n        return $this->createRelationQuery($class, $link, false);\n    }\n\n    /**\n     * Declares a `has-many` relation.\n     * The declaration is returned in terms of a relational [[ActiveQuery]] instance\n     * through which the related record can be queried and retrieved back.\n     *\n     * A `has-many` relation means that there are multiple related records matching\n     * the criteria set by this relation, e.g., a customer has many orders.\n     *\n     * For example, to declare the `orders` relation for `Customer` class, we can write\n     * the following code in the `Customer` class:\n     *\n     * ```\n     * public function getOrders()\n     * {\n     *     return $this->hasMany(Order::class, ['customer_id' => 'id']);\n     * }\n     * ```\n     *\n     * Note that in the above, the 'customer_id' key in the `$link` parameter refers to\n     * an attribute name in the related class `Order`, while the 'id' value refers to\n     * an attribute name in the current AR class.\n     *\n     * Call methods declared in [[ActiveQuery]] to further customize the relation.\n     *\n     * @param class-string $class the class name of the related record\n     * @param array<string, string> $link the primary-foreign key constraint. The keys of the array refer to\n     * the attributes of the record associated with the `$class` model, while the values of the\n     * array refer to the corresponding attributes in **this** AR class.\n     * @return ActiveQueryInterface the relational query object.\n     */\n    public function hasMany($class, $link)\n    {\n        return $this->createRelationQuery($class, $link, true);\n    }\n\n    /**\n     * Creates a query instance for `has-one` or `has-many` relation.\n     * @param string $class the class name of the related record.\n     * @param array $link the primary-foreign key constraint.\n     * @param bool $multiple whether this query represents a relation to more than one record.\n     * @return ActiveQueryInterface the relational query object.\n     * @since 2.0.12\n     * @see hasOne()\n     * @see hasMany()\n     */\n    protected function createRelationQuery($class, $link, $multiple)\n    {\n        /**\n         * @var ActiveRecordInterface $class\n         * @var ActiveQuery<ActiveRecord> $query\n         */\n\n        $query = $class::find();\n        $query->primaryModel = $this;\n        $query->link = $link;\n        $query->multiple = $multiple;\n        return $query;\n    }\n\n    /**\n     * Populates the named relation with the related records.\n     * Note that this method does not check if the relation exists or not.\n     * @param string $name the relation name, e.g. `orders` for a relation defined via `getOrders()` method (case-sensitive).\n     * @param ActiveRecordInterface|array|null $records the related records to be populated into the relation.\n     * @see getRelation()\n     */\n    public function populateRelation($name, $records)\n    {\n        foreach ($this->_relationsDependencies as &$relationNames) {\n            unset($relationNames[$name]);\n        }\n\n        $this->_related[$name] = $records;\n    }\n\n    /**\n     * Check whether the named relation has been populated with records.\n     * @param string $name the relation name, e.g. `orders` for a relation defined via `getOrders()` method (case-sensitive).\n     * @return bool whether relation has been populated with records.\n     * @see getRelation()\n     */\n    public function isRelationPopulated($name)\n    {\n        return array_key_exists($name, $this->_related);\n    }\n\n    /**\n     * Returns all populated related records.\n     * @return array an array of related records indexed by relation names.\n     * @see getRelation()\n     */\n    public function getRelatedRecords()\n    {\n        return $this->_related;\n    }\n\n    /**\n     * Returns a value indicating whether the model has an attribute with the specified name.\n     * @param string $name the name of the attribute\n     * @return bool whether the model has an attribute with the specified name.\n     */\n    public function hasAttribute($name)\n    {\n        // using null as an array offset is deprecated in PHP 8.5\n        if ($name === null || $name === '') {\n            return false;\n        }\n\n        return isset($this->_attributes[$name]) || in_array($name, $this->attributes(), true);\n    }\n\n    /**\n     * Returns the named attribute value.\n     * If this record is the result of a query and the attribute is not loaded,\n     * `null` will be returned.\n     * @param string $name the attribute name\n     * @return mixed the attribute value. `null` if the attribute is not set or does not exist.\n     * @see hasAttribute()\n     */\n    public function getAttribute($name)\n    {\n        return isset($this->_attributes[$name]) ? $this->_attributes[$name] : null;\n    }\n\n    /**\n     * Sets the named attribute value.\n     * @param string $name the attribute name\n     * @param mixed $value the attribute value.\n     * @throws InvalidArgumentException if the named attribute does not exist.\n     * @see hasAttribute()\n     */\n    public function setAttribute($name, $value)\n    {\n        if ($this->hasAttribute($name)) {\n            if (\n                !empty($this->_relationsDependencies[$name])\n                && (!array_key_exists($name, $this->_attributes) || $this->_attributes[$name] !== $value)\n            ) {\n                $this->resetDependentRelations($name);\n            }\n            $this->_attributes[$name] = $value;\n        } else {\n            throw new InvalidArgumentException(get_class($this) . ' has no attribute named \"' . $name . '\".');\n        }\n    }\n\n    /**\n     * Returns the old attribute values.\n     * @return array the old attribute values (name-value pairs)\n     */\n    public function getOldAttributes()\n    {\n        return $this->_oldAttributes === null ? [] : $this->_oldAttributes;\n    }\n\n    /**\n     * Sets the old attribute values.\n     * All existing old attribute values will be discarded.\n     * @param array|null $values old attribute values to be set.\n     * If set to `null` this record is considered to be [[isNewRecord|new]].\n     */\n    public function setOldAttributes($values)\n    {\n        $this->_oldAttributes = $values;\n    }\n\n    /**\n     * Returns the old value of the named attribute.\n     * If this record is the result of a query and the attribute is not loaded,\n     * `null` will be returned.\n     * @param string $name the attribute name\n     * @return mixed the old attribute value. `null` if the attribute is not loaded before\n     * or does not exist.\n     * @see hasAttribute()\n     */\n    public function getOldAttribute($name)\n    {\n        return isset($this->_oldAttributes[$name]) ? $this->_oldAttributes[$name] : null;\n    }\n\n    /**\n     * Sets the old value of the named attribute.\n     * @param string $name the attribute name\n     * @param mixed $value the old attribute value.\n     * @throws InvalidArgumentException if the named attribute does not exist.\n     * @see hasAttribute()\n     */\n    public function setOldAttribute($name, $value)\n    {\n        if ($this->canSetOldAttribute($name)) {\n            $this->_oldAttributes[$name] = $value;\n        } else {\n            throw new InvalidArgumentException(get_class($this) . ' has no attribute named \"' . $name . '\".');\n        }\n    }\n\n    /**\n     * Returns if the old named attribute can be set.\n     * @param string $name the attribute name\n     * @return bool whether the old attribute can be set\n     * @see setOldAttribute()\n     */\n    public function canSetOldAttribute($name)\n    {\n        return (isset($this->_oldAttributes[$name]) || $this->hasAttribute($name));\n    }\n\n    /**\n     * Marks an attribute dirty.\n     * This method may be called to force updating a record when calling [[update()]],\n     * even if there is no change being made to the record.\n     * @param string $name the attribute name\n     */\n    public function markAttributeDirty($name)\n    {\n        unset($this->_oldAttributes[$name]);\n    }\n\n    /**\n     * Returns a value indicating whether the named attribute has been changed.\n     * @param string $name the name of the attribute.\n     * @param bool $identical whether the comparison of new and old value is made for\n     * identical values using `===`, defaults to `true`. Otherwise `==` is used for comparison.\n     * This parameter is available since version 2.0.4.\n     * @return bool whether the attribute has been changed\n     */\n    public function isAttributeChanged($name, $identical = true)\n    {\n        if (isset($this->_attributes[$name], $this->_oldAttributes[$name])) {\n            if ($identical) {\n                return $this->_attributes[$name] !== $this->_oldAttributes[$name];\n            }\n\n            return $this->_attributes[$name] != $this->_oldAttributes[$name];\n        }\n\n        return isset($this->_attributes[$name]) || isset($this->_oldAttributes[$name]);\n    }\n\n    /**\n     * Returns the attribute values that have been modified since they are loaded or saved most recently.\n     *\n     * The comparison of new and old values is made for identical values using `===`.\n     *\n     * @param string[]|null $names the names of the attributes whose values may be returned if they are\n     * changed recently. If null, [[attributes()]] will be used.\n     * @return array the changed attribute values (name-value pairs)\n     */\n    public function getDirtyAttributes($names = null)\n    {\n        if ($names === null) {\n            $names = $this->attributes();\n        }\n        $names = array_flip($names);\n        $attributes = [];\n        if ($this->_oldAttributes === null) {\n            foreach ($this->_attributes as $name => $value) {\n                if (isset($names[$name])) {\n                    $attributes[$name] = $value;\n                }\n            }\n        } else {\n            foreach ($this->_attributes as $name => $value) {\n                if (isset($names[$name]) && (!array_key_exists($name, $this->_oldAttributes) || $this->isValueDifferent($value, $this->_oldAttributes[$name]))) {\n                    $attributes[$name] = $value;\n                }\n            }\n        }\n\n        return $attributes;\n    }\n\n    /**\n     * Saves the current record.\n     *\n     * This method will call [[insert()]] when [[isNewRecord]] is `true`, or [[update()]]\n     * when [[isNewRecord]] is `false`.\n     *\n     * For example, to save a customer record:\n     *\n     * ```\n     * $customer = new Customer; // or $customer = Customer::findOne($id);\n     * $customer->name = $name;\n     * $customer->email = $email;\n     * $customer->save();\n     * ```\n     *\n     * @param bool $runValidation whether to perform validation (calling [[validate()]])\n     * before saving the record. Defaults to `true`. If the validation fails, the record\n     * will not be saved to the database and this method will return `false`.\n     * @param array|null $attributeNames list of attribute names that need to be saved. Defaults to null,\n     * meaning all attributes that are loaded from DB will be saved.\n     * @return bool whether the saving succeeded (i.e. no validation errors occurred).\n     * @throws Exception in case update or insert failed.\n     */\n    public function save($runValidation = true, $attributeNames = null)\n    {\n        if ($this->getIsNewRecord()) {\n            return $this->insert($runValidation, $attributeNames);\n        }\n\n        return $this->update($runValidation, $attributeNames) !== false;\n    }\n\n    /**\n     * Saves the changes to this active record into the associated database table.\n     *\n     * This method performs the following steps in order:\n     *\n     * 1. call [[beforeValidate()]] when `$runValidation` is `true`. If [[beforeValidate()]]\n     *    returns `false`, the rest of the steps will be skipped;\n     * 2. call [[afterValidate()]] when `$runValidation` is `true`. If validation\n     *    failed, the rest of the steps will be skipped;\n     * 3. call [[beforeSave()]]. If [[beforeSave()]] returns `false`,\n     *    the rest of the steps will be skipped;\n     * 4. save the record into database. If this fails, it will skip the rest of the steps;\n     * 5. call [[afterSave()]];\n     *\n     * In the above step 1, 2, 3 and 5, events [[EVENT_BEFORE_VALIDATE]],\n     * [[EVENT_AFTER_VALIDATE]], [[EVENT_BEFORE_UPDATE]], and [[EVENT_AFTER_UPDATE]]\n     * will be raised by the corresponding methods.\n     *\n     * Only the [[dirtyAttributes|changed attribute values]] will be saved into database.\n     *\n     * For example, to update a customer record:\n     *\n     * ```\n     * $customer = Customer::findOne($id);\n     * $customer->name = $name;\n     * $customer->email = $email;\n     * $customer->update();\n     * ```\n     *\n     * Note that it is possible the update does not affect any row in the table.\n     * In this case, this method will return 0. For this reason, you should use the following\n     * code to check if update() is successful or not:\n     *\n     * ```\n     * if ($customer->update() !== false) {\n     *     // update successful\n     * } else {\n     *     // update failed\n     * }\n     * ```\n     *\n     * @param bool $runValidation whether to perform validation (calling [[validate()]])\n     * before saving the record. Defaults to `true`. If the validation fails, the record\n     * will not be saved to the database and this method will return `false`.\n     * @param array|null $attributeNames list of attribute names that need to be saved. Defaults to null,\n     * meaning all attributes that are loaded from DB will be saved.\n     * @return int|false the number of rows affected, or `false` if validation fails\n     * or [[beforeSave()]] stops the updating process.\n     * @throws StaleObjectException if [[optimisticLock|optimistic locking]] is enabled and the data\n     * being updated is outdated.\n     * @throws Exception in case update failed.\n     */\n    public function update($runValidation = true, $attributeNames = null)\n    {\n        if ($runValidation && !$this->validate($attributeNames)) {\n            return false;\n        }\n\n        return $this->updateInternal($attributeNames);\n    }\n\n    /**\n     * Updates the specified attributes.\n     *\n     * This method is a shortcut to [[update()]] when data validation is not needed\n     * and only a small set attributes need to be updated.\n     *\n     * You may specify the attributes to be updated as name list or name-value pairs.\n     * If the latter, the corresponding attribute values will be modified accordingly.\n     * The method will then save the specified attributes into database.\n     *\n     * Note that this method will **not** perform data validation and will **not** trigger events.\n     *\n     * @param array $attributes the attributes (names or name-value pairs) to be updated\n     * @return int the number of rows affected.\n     */\n    public function updateAttributes($attributes)\n    {\n        $attrs = [];\n        foreach ($attributes as $name => $value) {\n            if (is_int($name)) {\n                $attrs[] = $value;\n            } else {\n                $this->$name = $value;\n                $attrs[] = $name;\n            }\n        }\n\n        $values = $this->getDirtyAttributes($attrs);\n        if (empty($values) || $this->getIsNewRecord()) {\n            return 0;\n        }\n\n        $rows = static::updateAll($values, $this->getOldPrimaryKey(true));\n\n        foreach ($values as $name => $value) {\n            $this->_oldAttributes[$name] = $this->_attributes[$name];\n        }\n\n        return $rows;\n    }\n\n    /**\n     * @see update()\n     * @param array|null $attributes attributes to update\n     * @return int|false the number of rows affected, or false if [[beforeSave()]] stops the updating process.\n     * @throws StaleObjectException\n     */\n    protected function updateInternal($attributes = null)\n    {\n        if (!$this->beforeSave(false)) {\n            return false;\n        }\n        $values = $this->getDirtyAttributes($attributes);\n        if (empty($values)) {\n            $this->afterSave(false, $values);\n            return 0;\n        }\n        $condition = $this->getOldPrimaryKey(true);\n        $lock = $this->optimisticLock();\n        if ($lock !== null) {\n            $values[$lock] = $this->$lock + 1;\n            $condition[$lock] = $this->$lock;\n        }\n        // We do not check the return value of updateAll() because it's possible\n        // that the UPDATE statement doesn't change anything and thus returns 0.\n        $rows = static::updateAll($values, $condition);\n\n        if ($lock !== null && !$rows) {\n            throw new StaleObjectException('The object being updated is outdated.');\n        }\n\n        // using null as an array offset is deprecated in PHP `8.5`\n        if ($lock !== null && isset($values[$lock])) {\n            $this->$lock = $values[$lock];\n        }\n\n        $changedAttributes = [];\n        foreach ($values as $name => $value) {\n            $changedAttributes[$name] = isset($this->_oldAttributes[$name]) ? $this->_oldAttributes[$name] : null;\n            $this->_oldAttributes[$name] = $value;\n        }\n        $this->afterSave(false, $changedAttributes);\n\n        return $rows;\n    }\n\n    /**\n     * Updates one or several counter columns for the current AR object.\n     * Note that this method differs from [[updateAllCounters()]] in that it only\n     * saves counters for the current AR object.\n     *\n     * An example usage is as follows:\n     *\n     * ```\n     * $post = Post::findOne($id);\n     * $post->updateCounters(['view_count' => 1]);\n     * ```\n     *\n     * @param array $counters the counters to be updated (attribute name => increment value)\n     * Use negative values if you want to decrement the counters.\n     * @return bool whether the saving is successful\n     * @see updateAllCounters()\n     */\n    public function updateCounters($counters)\n    {\n        if (static::updateAllCounters($counters, $this->getOldPrimaryKey(true)) > 0) {\n            foreach ($counters as $name => $value) {\n                if (!isset($this->_attributes[$name])) {\n                    $this->_attributes[$name] = $value;\n                } else {\n                    $this->_attributes[$name] += $value;\n                }\n                $this->_oldAttributes[$name] = $this->_attributes[$name];\n            }\n\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * Deletes the table row corresponding to this active record.\n     *\n     * This method performs the following steps in order:\n     *\n     * 1. call [[beforeDelete()]]. If the method returns `false`, it will skip the\n     *    rest of the steps;\n     * 2. delete the record from the database;\n     * 3. call [[afterDelete()]].\n     *\n     * In the above step 1 and 3, events named [[EVENT_BEFORE_DELETE]] and [[EVENT_AFTER_DELETE]]\n     * will be raised by the corresponding methods.\n     *\n     * @return int|false the number of rows deleted, or `false` if the deletion is unsuccessful for some reason.\n     * Note that it is possible the number of rows deleted is 0, even though the deletion execution is successful.\n     * @throws StaleObjectException if [[optimisticLock|optimistic locking]] is enabled and the data\n     * being deleted is outdated.\n     * @throws Exception in case delete failed.\n     */\n    public function delete()\n    {\n        $result = false;\n        if ($this->beforeDelete()) {\n            // we do not check the return value of deleteAll() because it's possible\n            // the record is already deleted in the database and thus the method will return 0\n            $condition = $this->getOldPrimaryKey(true);\n            $lock = $this->optimisticLock();\n            if ($lock !== null) {\n                $condition[$lock] = $this->$lock;\n            }\n            $result = static::deleteAll($condition);\n            if ($lock !== null && !$result) {\n                throw new StaleObjectException('The object being deleted is outdated.');\n            }\n            $this->_oldAttributes = null;\n            $this->afterDelete();\n        }\n\n        return $result;\n    }\n\n    /**\n     * Returns a value indicating whether the current record is new.\n     * @return bool whether the record is new and should be inserted when calling [[save()]].\n     */\n    public function getIsNewRecord()\n    {\n        return $this->_oldAttributes === null;\n    }\n\n    /**\n     * Sets the value indicating whether the record is new.\n     * @param bool $value whether the record is new and should be inserted when calling [[save()]].\n     * @see getIsNewRecord()\n     */\n    public function setIsNewRecord($value)\n    {\n        $this->_oldAttributes = $value ? null : $this->_attributes;\n    }\n\n    /**\n     * Initializes the object.\n     * This method is called at the end of the constructor.\n     * The default implementation will trigger an [[EVENT_INIT]] event.\n     */\n    public function init()\n    {\n        parent::init();\n        $this->trigger(self::EVENT_INIT);\n    }\n\n    /**\n     * This method is called when the AR object is created and populated with the query result.\n     * The default implementation will trigger an [[EVENT_AFTER_FIND]] event.\n     * When overriding this method, make sure you call the parent implementation to ensure the\n     * event is triggered.\n     */\n    public function afterFind()\n    {\n        $this->trigger(self::EVENT_AFTER_FIND);\n    }\n\n    /**\n     * This method is called at the beginning of inserting or updating a record.\n     *\n     * The default implementation will trigger an [[EVENT_BEFORE_INSERT]] event when `$insert` is `true`,\n     * or an [[EVENT_BEFORE_UPDATE]] event if `$insert` is `false`.\n     * When overriding this method, make sure you call the parent implementation like the following:\n     *\n     * ```\n     * public function beforeSave($insert)\n     * {\n     *     if (!parent::beforeSave($insert)) {\n     *         return false;\n     *     }\n     *\n     *     // ...custom code here...\n     *     return true;\n     * }\n     * ```\n     *\n     * @param bool $insert whether this method called while inserting a record.\n     * If `false`, it means the method is called while updating a record.\n     * @return bool whether the insertion or updating should continue.\n     * If `false`, the insertion or updating will be cancelled.\n     */\n    public function beforeSave($insert)\n    {\n        $event = new ModelEvent();\n        $this->trigger($insert ? self::EVENT_BEFORE_INSERT : self::EVENT_BEFORE_UPDATE, $event);\n\n        return $event->isValid;\n    }\n\n    /**\n     * This method is called at the end of inserting or updating a record.\n     * The default implementation will trigger an [[EVENT_AFTER_INSERT]] event when `$insert` is `true`,\n     * or an [[EVENT_AFTER_UPDATE]] event if `$insert` is `false`. The event class used is [[AfterSaveEvent]].\n     * When overriding this method, make sure you call the parent implementation so that\n     * the event is triggered.\n     * @param bool $insert whether this method called while inserting a record.\n     * If `false`, it means the method is called while updating a record.\n     * @param array $changedAttributes The old values of attributes that had changed and were saved.\n     * You can use this parameter to take action based on the changes made for example send an email\n     * when the password had changed or implement audit trail that tracks all the changes.\n     * `$changedAttributes` gives you the old attribute values while the active record (`$this`) has\n     * already the new, updated values.\n     *\n     * Note that no automatic type conversion performed by default. You may use\n     * [[\\yii\\behaviors\\AttributeTypecastBehavior]] to facilitate attribute typecasting.\n     * See https://www.yiiframework.com/doc-2.0/guide-db-active-record.html#attributes-typecasting.\n     */\n    public function afterSave($insert, $changedAttributes)\n    {\n        $this->trigger($insert ? self::EVENT_AFTER_INSERT : self::EVENT_AFTER_UPDATE, new AfterSaveEvent([\n            'changedAttributes' => $changedAttributes,\n        ]));\n    }\n\n    /**\n     * This method is invoked before deleting a record.\n     *\n     * The default implementation raises the [[EVENT_BEFORE_DELETE]] event.\n     * When overriding this method, make sure you call the parent implementation like the following:\n     *\n     * ```\n     * public function beforeDelete()\n     * {\n     *     if (!parent::beforeDelete()) {\n     *         return false;\n     *     }\n     *\n     *     // ...custom code here...\n     *     return true;\n     * }\n     * ```\n     *\n     * @return bool whether the record should be deleted. Defaults to `true`.\n     */\n    public function beforeDelete()\n    {\n        $event = new ModelEvent();\n        $this->trigger(self::EVENT_BEFORE_DELETE, $event);\n\n        return $event->isValid;\n    }\n\n    /**\n     * This method is invoked after deleting a record.\n     * The default implementation raises the [[EVENT_AFTER_DELETE]] event.\n     * You may override this method to do postprocessing after the record is deleted.\n     * Make sure you call the parent implementation so that the event is raised properly.\n     */\n    public function afterDelete()\n    {\n        $this->trigger(self::EVENT_AFTER_DELETE);\n    }\n\n    /**\n     * Repopulates this active record with the latest data.\n     *\n     * If the refresh is successful, an [[EVENT_AFTER_REFRESH]] event will be triggered.\n     * This event is available since version 2.0.8.\n     *\n     * @return bool whether the row still exists in the database. If `true`, the latest data\n     * will be populated to this active record. Otherwise, this record will remain unchanged.\n     */\n    public function refresh()\n    {\n        /** @var self $record */\n        $record = static::findOne($this->getPrimaryKey(true));\n        return $this->refreshInternal($record);\n    }\n\n    /**\n     * Repopulates this active record with the latest data from a newly fetched instance.\n     * @param BaseActiveRecord $record the record to take attributes from.\n     * @return bool whether refresh was successful.\n     * @see refresh()\n     * @since 2.0.13\n     */\n    protected function refreshInternal($record)\n    {\n        if ($record === null) {\n            return false;\n        }\n        foreach ($this->attributes() as $name) {\n            $this->_attributes[$name] = isset($record->_attributes[$name]) ? $record->_attributes[$name] : null;\n        }\n        $this->_oldAttributes = $record->_oldAttributes;\n        $this->_related = [];\n        $this->_relationsDependencies = [];\n        $this->afterRefresh();\n\n        return true;\n    }\n\n    /**\n     * This method is called when the AR object is refreshed.\n     * The default implementation will trigger an [[EVENT_AFTER_REFRESH]] event.\n     * When overriding this method, make sure you call the parent implementation to ensure the\n     * event is triggered.\n     * @since 2.0.8\n     */\n    public function afterRefresh()\n    {\n        $this->trigger(self::EVENT_AFTER_REFRESH);\n    }\n\n    /**\n     * Returns a value indicating whether the given active record is the same as the current one.\n     * The comparison is made by comparing the table names and the primary key values of the two active records.\n     * If one of the records [[isNewRecord|is new]] they are also considered not equal.\n     * @param ActiveRecordInterface $record record to compare to\n     * @return bool whether the two active records refer to the same row in the same database table.\n     */\n    public function equals($record)\n    {\n        if ($this->getIsNewRecord() || $record->getIsNewRecord()) {\n            return false;\n        }\n\n        return get_class($this) === get_class($record) && $this->getPrimaryKey() === $record->getPrimaryKey();\n    }\n\n    /**\n     * Returns the primary key value(s).\n     * @param bool $asArray whether to return the primary key value as an array. If `true`,\n     * the return value will be an array with column names as keys and column values as values.\n     * Note that for composite primary keys, an array will always be returned regardless of this parameter value.\n     * @return mixed the primary key value. An array (column name => column value) is returned if the primary key\n     * is composite or `$asArray` is `true`. A string is returned otherwise (null will be returned if\n     * the key value is null).\n     */\n    public function getPrimaryKey($asArray = false)\n    {\n        $keys = static::primaryKey();\n        if (!$asArray && count($keys) === 1) {\n            return isset($this->_attributes[$keys[0]]) ? $this->_attributes[$keys[0]] : null;\n        }\n\n        $values = [];\n        foreach ($keys as $name) {\n            $values[$name] = isset($this->_attributes[$name]) ? $this->_attributes[$name] : null;\n        }\n\n        return $values;\n    }\n\n    /**\n     * Returns the old primary key value(s).\n     * This refers to the primary key value that is populated into the record\n     * after executing a find method (e.g. find(), findOne()).\n     * The value remains unchanged even if the primary key attribute is manually assigned with a different value.\n     * @param bool $asArray whether to return the primary key value as an array. If `true`,\n     * the return value will be an array with column name as key and column value as value.\n     * If this is `false` (default), a scalar value will be returned for non-composite primary key.\n     * @return mixed the old primary key value. An array (column name => column value) is returned if the primary key\n     * is composite or `$asArray` is `true`. A string is returned otherwise (null will be returned if\n     * the key value is null).\n     * @throws Exception if the AR model does not have a primary key\n     */\n    public function getOldPrimaryKey($asArray = false)\n    {\n        $keys = static::primaryKey();\n        if (empty($keys)) {\n            throw new Exception(get_class($this) . ' does not have a primary key. You should either define a primary key for the corresponding table or override the primaryKey() method.');\n        }\n        if (!$asArray && count($keys) === 1) {\n            return isset($this->_oldAttributes[$keys[0]]) ? $this->_oldAttributes[$keys[0]] : null;\n        }\n\n        $values = [];\n        foreach ($keys as $name) {\n            $values[$name] = isset($this->_oldAttributes[$name]) ? $this->_oldAttributes[$name] : null;\n        }\n\n        return $values;\n    }\n\n    /**\n     * Populates an active record object using a row of data from the database/storage.\n     *\n     * This is an internal method meant to be called to create active record objects after\n     * fetching data from the database. It is mainly used by [[ActiveQuery]] to populate\n     * the query results into active records.\n     *\n     * When calling this method manually you should call [[afterFind()]] on the created\n     * record to trigger the [[EVENT_AFTER_FIND|afterFind Event]].\n     *\n     * @param BaseActiveRecord $record the record to be populated. In most cases this will be an instance\n     * created by [[instantiate()]] beforehand.\n     * @param array $row attribute values (name => value)\n     */\n    public static function populateRecord($record, $row)\n    {\n        $columns = array_flip($record->attributes());\n        foreach ($row as $name => $value) {\n            if (isset($columns[$name])) {\n                $record->_attributes[$name] = $value;\n            } elseif ($record->canSetProperty($name)) {\n                $record->$name = $value;\n            }\n        }\n        $record->_oldAttributes = $record->_attributes;\n        $record->_related = [];\n        $record->_relationsDependencies = [];\n    }\n\n    /**\n     * Creates an active record instance.\n     *\n     * This method is called together with [[populateRecord()]] by [[ActiveQuery]].\n     * It is not meant to be used for creating new records directly.\n     *\n     * You may override this method if the instance being created\n     * depends on the row data to be populated into the record.\n     * For example, by creating a record based on the value of a column,\n     * you may implement the so-called single-table inheritance mapping.\n     * @param array $row row data to be populated into the record.\n     * @return static the newly created active record\n     */\n    public static function instantiate($row)\n    {\n        return new static();\n    }\n\n    /**\n     * Returns whether there is an element at the specified offset.\n     * This method is required by the interface [[\\ArrayAccess]].\n     * @param mixed $offset the offset to check on\n     * @return bool whether there is an element at the specified offset.\n     */\n    #[\\ReturnTypeWillChange]\n    public function offsetExists($offset)\n    {\n        return $this->__isset($offset);\n    }\n\n    /**\n     * Returns the relation object with the specified name.\n     * A relation is defined by a getter method which returns an [[ActiveQueryInterface]] object.\n     * It can be declared in either the Active Record class itself or one of its behaviors.\n     * @param string $name the relation name, e.g. `orders` for a relation defined via `getOrders()` method (case-sensitive).\n     * @param bool $throwException whether to throw exception if the relation does not exist.\n     * @return ActiveQueryInterface|ActiveQuery|null the relational query object. If the relation does not exist\n     * and `$throwException` is `false`, `null` will be returned.\n     * @throws InvalidArgumentException if the named relation does not exist.\n     */\n    public function getRelation($name, $throwException = true)\n    {\n        $getter = 'get' . $name;\n        try {\n            // the relation could be defined in a behavior\n            $relation = $this->$getter();\n        } catch (UnknownMethodException $e) {\n            if ($throwException) {\n                throw new InvalidArgumentException(get_class($this) . ' has no relation named \"' . $name . '\".', 0, $e);\n            }\n\n            return null;\n        }\n        if (!$relation instanceof ActiveQueryInterface) {\n            if ($throwException) {\n                throw new InvalidArgumentException(get_class($this) . ' has no relation named \"' . $name . '\".');\n            }\n\n            return null;\n        }\n\n        if (method_exists($this, $getter)) {\n            // relation name is case sensitive, trying to validate it when the relation is defined within this class\n            $method = new \\ReflectionMethod($this, $getter);\n            $realName = lcfirst(substr($method->getName(), 3));\n            if ($realName !== $name) {\n                if ($throwException) {\n                    throw new InvalidArgumentException('Relation names are case sensitive. ' . get_class($this) . \" has a relation named \\\"$realName\\\" instead of \\\"$name\\\".\");\n                }\n\n                return null;\n            }\n        }\n\n        return $relation;\n    }\n\n    /**\n     * Establishes the relationship between two models.\n     *\n     * The relationship is established by setting the foreign key value(s) in one model\n     * to be the corresponding primary key value(s) in the other model.\n     * The model with the foreign key will be saved into database **without** performing validation\n     * and **without** events/behaviors.\n     *\n     * If the relationship involves a junction table, a new row will be inserted into the\n     * junction table which contains the primary key values from both models.\n     *\n     * Note that this method requires that the primary key value is not null.\n     *\n     * @param string $name the case sensitive name of the relationship, e.g. `orders` for a relation defined via `getOrders()` method.\n     * @param ActiveRecordInterface $model the model to be linked with the current one.\n     * @param array $extraColumns additional column values to be saved into the junction table.\n     * This parameter is only meaningful for a relationship involving a junction table\n     * (i.e., a relation set with [[ActiveRelationTrait::via()]] or [[ActiveQuery::viaTable()]].)\n     * @throws InvalidCallException if the method is unable to link two models.\n     */\n    public function link($name, $model, $extraColumns = [])\n    {\n        /** @var ActiveQueryInterface|ActiveQuery $relation */\n        $relation = $this->getRelation($name);\n\n        if ($relation->via !== null) {\n            if ($this->getIsNewRecord() || $model->getIsNewRecord()) {\n                throw new InvalidCallException('Unable to link models: the models being linked cannot be newly created.');\n            }\n            if (is_array($relation->via)) {\n                /** @var ActiveQuery $viaRelation */\n                list($viaName, $viaRelation) = $relation->via;\n                $viaClass = $viaRelation->modelClass;\n                // unset $viaName so that it can be reloaded to reflect the change\n                unset($this->_related[$viaName]);\n            } else {\n                $viaRelation = $relation->via;\n                $viaTable = reset($relation->via->from);\n            }\n            $columns = [];\n            foreach ($viaRelation->link as $a => $b) {\n                $columns[$a] = $this->$b;\n            }\n            foreach ($relation->link as $a => $b) {\n                $columns[$b] = $model->$a;\n            }\n            foreach ($extraColumns as $k => $v) {\n                $columns[$k] = $v;\n            }\n            if (is_array($relation->via)) {\n                /** @var ActiveRecordInterface $viaClass */\n                /** @var ActiveRecordInterface $record */\n                $record = Yii::createObject($viaClass);\n                foreach ($columns as $column => $value) {\n                    $record->$column = $value;\n                }\n                $record->insert(false);\n            } else {\n                /** @var string $viaTable */\n                static::getDb()->createCommand()->insert($viaTable, $columns)->execute();\n            }\n        } else {\n            $p1 = $model->isPrimaryKey(array_keys($relation->link));\n            $p2 = static::isPrimaryKey(array_values($relation->link));\n            if ($p1 && $p2) {\n                if ($this->getIsNewRecord()) {\n                    if ($model->getIsNewRecord()) {\n                        throw new InvalidCallException('Unable to link models: at most one model can be newly created.');\n                    }\n                    $this->bindModels(array_flip($relation->link), $this, $model);\n                } else {\n                    $this->bindModels($relation->link, $model, $this);\n                }\n            } elseif ($p1) {\n                $this->bindModels(array_flip($relation->link), $this, $model);\n            } elseif ($p2) {\n                $this->bindModels($relation->link, $model, $this);\n            } else {\n                throw new InvalidCallException('Unable to link models: the link defining the relation does not involve any primary key.');\n            }\n        }\n\n        // update lazily loaded related objects\n        if (!$relation->multiple) {\n            $this->_related[$name] = $model;\n        } elseif (isset($this->_related[$name])) {\n            if ($relation->indexBy !== null) {\n                if ($relation->indexBy instanceof \\Closure) {\n                    $index = call_user_func($relation->indexBy, $model);\n                } else {\n                    $index = $model->{$relation->indexBy};\n                }\n                $this->_related[$name][$index] = $model;\n            } else {\n                $this->_related[$name][] = $model;\n            }\n        }\n    }\n\n    /**\n     * Destroys the relationship between two models.\n     *\n     * The model with the foreign key of the relationship will be deleted if `$delete` is `true`.\n     * Otherwise, the foreign key will be set `null` and the model will be saved without validation.\n     *\n     * @param string $name the case sensitive name of the relationship, e.g. `orders` for a relation defined via `getOrders()` method.\n     * @param ActiveRecordInterface $model the model to be unlinked from the current one.\n     * You have to make sure that the model is really related with the current model as this method\n     * does not check this.\n     * @param bool $delete whether to delete the model that contains the foreign key.\n     * If `false`, the model's foreign key will be set `null` and saved.\n     * If `true`, the model containing the foreign key will be deleted.\n     * @throws InvalidCallException if the models cannot be unlinked\n     * @throws Exception\n     * @throws StaleObjectException\n     */\n    public function unlink($name, $model, $delete = false)\n    {\n        /** @var ActiveQueryInterface|ActiveQuery $relation */\n        $relation = $this->getRelation($name);\n\n        if ($relation->via !== null) {\n            if (is_array($relation->via)) {\n                /** @var ActiveQuery<ActiveRecord> $viaRelation */\n                list($viaName, $viaRelation) = $relation->via;\n                $viaClass = $viaRelation->modelClass;\n                unset($this->_related[$viaName]);\n            } else {\n                $viaRelation = $relation->via;\n                $viaTable = reset($relation->via->from);\n            }\n            $columns = [];\n            foreach ($viaRelation->link as $a => $b) {\n                $columns[$a] = $this->$b;\n            }\n            foreach ($relation->link as $a => $b) {\n                $columns[$b] = $model->$a;\n            }\n            $nulls = [];\n            foreach (array_keys($columns) as $a) {\n                $nulls[$a] = null;\n            }\n            if (property_exists($viaRelation, 'on') && $viaRelation->on !== null) {\n                $columns = ['and', $columns, $viaRelation->on];\n            }\n            if (is_array($relation->via)) {\n                /** @var ActiveRecordInterface $viaClass */\n                if ($delete) {\n                    $viaClass::deleteAll($columns);\n                } else {\n                    $viaClass::updateAll($nulls, $columns);\n                }\n            } else {\n                /** @var string $viaTable */\n                /** @var Command $command */\n                $command = static::getDb()->createCommand();\n                if ($delete) {\n                    $command->delete($viaTable, $columns)->execute();\n                } else {\n                    $command->update($viaTable, $nulls, $columns)->execute();\n                }\n            }\n        } else {\n            $p1 = $model->isPrimaryKey(array_keys($relation->link));\n            $p2 = static::isPrimaryKey(array_values($relation->link));\n            if ($p2) {\n                if ($delete) {\n                    $model->delete();\n                } else {\n                    foreach ($relation->link as $a => $b) {\n                        $model->$a = null;\n                    }\n                    $model->save(false);\n                }\n            } elseif ($p1) {\n                foreach ($relation->link as $a => $b) {\n                    if (is_array($this->$b)) { // relation via array valued attribute\n                        if (($key = array_search($model->$a, $this->$b, false)) !== false) {\n                            $values = $this->$b;\n                            unset($values[$key]);\n                            $this->$b = array_values($values);\n                        }\n                    } else {\n                        $this->$b = null;\n                    }\n                }\n                $delete ? $this->delete() : $this->save(false);\n            } else {\n                throw new InvalidCallException('Unable to unlink models: the link does not involve any primary key.');\n            }\n        }\n\n        if (!$relation->multiple) {\n            unset($this->_related[$name]);\n        } elseif (isset($this->_related[$name])) {\n            /** @var ActiveRecordInterface $b */\n            foreach ($this->_related[$name] as $a => $b) {\n                if ($model->getPrimaryKey() === $b->getPrimaryKey()) {\n                    unset($this->_related[$name][$a]);\n                }\n            }\n        }\n    }\n\n    /**\n     * Destroys the relationship in current model.\n     *\n     * The model with the foreign key of the relationship will be deleted if `$delete` is `true`.\n     * Otherwise, the foreign key will be set `null` and the model will be saved without validation.\n     *\n     * Note that to destroy the relationship without removing records make sure your keys can be set to null\n     *\n     * @param string $name the case sensitive name of the relationship, e.g. `orders` for a relation defined via `getOrders()` method.\n     * @param bool $delete whether to delete the model that contains the foreign key.\n     *\n     * Note that the deletion will be performed using [[deleteAll()]], which will not trigger any events on the related models.\n     * If you need [[EVENT_BEFORE_DELETE]] or [[EVENT_AFTER_DELETE]] to be triggered, you need to [[find()|find]] the models first\n     * and then call [[delete()]] on each of them.\n     */\n    public function unlinkAll($name, $delete = false)\n    {\n        /** @var ActiveQueryInterface|ActiveQuery $relation */\n        $relation = $this->getRelation($name);\n\n        if ($relation->via !== null) {\n            if (is_array($relation->via)) {\n                /** @var ActiveQuery $viaRelation */\n                list($viaName, $viaRelation) = $relation->via;\n                $viaClass = $viaRelation->modelClass;\n                unset($this->_related[$viaName]);\n            } else {\n                $viaRelation = $relation->via;\n                $viaTable = reset($relation->via->from);\n            }\n            $condition = [];\n            $nulls = [];\n            foreach ($viaRelation->link as $a => $b) {\n                $nulls[$a] = null;\n                $condition[$a] = $this->$b;\n            }\n            if (!empty($viaRelation->where)) {\n                $condition = ['and', $condition, $viaRelation->where];\n            }\n            if (property_exists($viaRelation, 'on') && !empty($viaRelation->on)) {\n                $condition = ['and', $condition, $viaRelation->on];\n            }\n            if (is_array($relation->via)) {\n                /** @var ActiveRecordInterface $viaClass */\n                if ($delete) {\n                    $viaClass::deleteAll($condition);\n                } else {\n                    $viaClass::updateAll($nulls, $condition);\n                }\n            } else {\n                /** @var string $viaTable */\n                /** @var Command $command */\n                $command = static::getDb()->createCommand();\n                if ($delete) {\n                    $command->delete($viaTable, $condition)->execute();\n                } else {\n                    $command->update($viaTable, $nulls, $condition)->execute();\n                }\n            }\n        } else {\n            /** @var ActiveRecordInterface $relatedModel */\n            $relatedModel = $relation->modelClass;\n            if (!$delete && count($relation->link) === 1 && is_array($this->{$b = reset($relation->link)})) {\n                // relation via array valued attribute\n                $this->$b = [];\n                $this->save(false);\n            } else {\n                $nulls = [];\n                $condition = [];\n                foreach ($relation->link as $a => $b) {\n                    $nulls[$a] = null;\n                    $condition[$a] = $this->$b;\n                }\n                if (!empty($relation->where)) {\n                    $condition = ['and', $condition, $relation->where];\n                }\n                if (property_exists($relation, 'on') && !empty($relation->on)) {\n                    $condition = ['and', $condition, $relation->on];\n                }\n                if ($delete) {\n                    $relatedModel::deleteAll($condition);\n                } else {\n                    $relatedModel::updateAll($nulls, $condition);\n                }\n            }\n        }\n\n        unset($this->_related[$name]);\n    }\n\n    /**\n     * @param array $link\n     * @param ActiveRecordInterface $foreignModel\n     * @param ActiveRecordInterface $primaryModel\n     * @throws InvalidCallException\n     */\n    private function bindModels($link, $foreignModel, $primaryModel)\n    {\n        foreach ($link as $fk => $pk) {\n            $value = $primaryModel->$pk;\n            if ($value === null) {\n                throw new InvalidCallException('Unable to link models: the primary key of ' . get_class($primaryModel) . ' is null.');\n            }\n            if (is_array($foreignModel->$fk)) { // relation via array valued attribute\n                $foreignModel->{$fk}[] = $value;\n            } else {\n                $foreignModel->{$fk} = $value;\n            }\n        }\n        $foreignModel->save(false);\n    }\n\n    /**\n     * Returns a value indicating whether the given set of attributes represents the primary key for this model.\n     * @param array $keys the set of attributes to check\n     * @return bool whether the given set of attributes represents the primary key for this model\n     */\n    public static function isPrimaryKey($keys)\n    {\n        $pks = static::primaryKey();\n        if (count($keys) === count($pks)) {\n            return count(array_intersect($keys, $pks)) === count($pks);\n        }\n\n        return false;\n    }\n\n    /**\n     * Returns the text label for the specified attribute.\n     * The attribute may be specified in a dot format to retrieve the label from related model or allow this model to override the label defined in related model.\n     * For example, if the attribute is specified as 'relatedModel1.relatedModel2.attr' the function will return the first label definition it can find\n     * in the following order:\n     * - the label for 'relatedModel1.relatedModel2.attr' defined in [[attributeLabels()]] of this model;\n     * - the label for 'relatedModel2.attr' defined in related model represented by relation 'relatedModel1' of this model;\n     * - the label for 'attr' defined in related model represented by relation 'relatedModel2' of relation 'relatedModel1'.\n     *   If no label definition was found then the value of $this->generateAttributeLabel('relatedModel1.relatedModel2.attr') will be returned.\n     * @param string $attribute the attribute name\n     * @return string the attribute label\n     * @see attributeLabels()\n     * @see generateAttributeLabel()\n     */\n    public function getAttributeLabel($attribute)\n    {\n        $model = $this;\n        $modelAttribute = $attribute;\n        for (;;) {\n            $labels = $model->attributeLabels();\n            if (isset($labels[$modelAttribute])) {\n                return $labels[$modelAttribute];\n            }\n\n            $parts = explode('.', $modelAttribute, 2);\n            if (count($parts) < 2) {\n                break;\n            }\n\n            list ($relationName, $modelAttribute) = $parts;\n\n            if ($model->isRelationPopulated($relationName) && $model->$relationName instanceof self) {\n                $model = $model->$relationName;\n            } else {\n                try {\n                    $relation = $model->getRelation($relationName);\n                } catch (InvalidArgumentException $e) {\n                    break;\n                }\n                /** @var ActiveRecordInterface $modelClass */\n                $modelClass = $relation->modelClass;\n                $model = $modelClass::instance();\n            }\n        }\n\n        return $this->generateAttributeLabel($attribute);\n    }\n\n    /**\n     * Returns the text hint for the specified attribute.\n     * If the attribute looks like `relatedModel.attribute`, then the attribute will be received from the related model.\n     * @param string $attribute the attribute name\n     * @return string the attribute hint\n     * @see attributeHints()\n     * @since 2.0.4\n     */\n    public function getAttributeHint($attribute)\n    {\n        $hints = $this->attributeHints();\n        if (isset($hints[$attribute])) {\n            return $hints[$attribute];\n        } elseif (strpos($attribute, '.')) {\n            $attributeParts = explode('.', $attribute);\n            $neededAttribute = array_pop($attributeParts);\n\n            $relatedModel = $this;\n            foreach ($attributeParts as $relationName) {\n                if ($relatedModel->isRelationPopulated($relationName) && $relatedModel->$relationName instanceof self) {\n                    $relatedModel = $relatedModel->$relationName;\n                } else {\n                    try {\n                        $relation = $relatedModel->getRelation($relationName);\n                    } catch (InvalidParamException $e) {\n                        return '';\n                    }\n                    /** @var ActiveRecordInterface $modelClass */\n                    $modelClass = $relation->modelClass;\n                    $relatedModel = $modelClass::instance();\n                }\n            }\n\n            $hints = $relatedModel->attributeHints();\n            if (isset($hints[$neededAttribute])) {\n                return $hints[$neededAttribute];\n            }\n        }\n\n        return '';\n    }\n\n    /**\n     * {@inheritdoc}\n     *\n     * The default implementation returns the names of the columns whose values have been populated into this record.\n     */\n    public function fields()\n    {\n        $fields = array_keys($this->_attributes);\n\n        return array_combine($fields, $fields);\n    }\n\n    /**\n     * {@inheritdoc}\n     *\n     * The default implementation returns the names of the relations that have been populated into this record.\n     */\n    public function extraFields()\n    {\n        $fields = array_keys($this->getRelatedRecords());\n\n        return array_combine($fields, $fields);\n    }\n\n    /**\n     * Sets the element value at the specified offset to null.\n     * This method is required by the SPL interface [[\\ArrayAccess]].\n     * It is implicitly called when you use something like `unset($model[$offset])`.\n     * @param mixed $offset the offset to unset element\n     */\n    public function offsetUnset($offset)\n    {\n        if (property_exists($this, $offset)) {\n            $this->$offset = null;\n        } else {\n            unset($this->$offset);\n        }\n    }\n\n    /**\n     * Resets dependent related models checking if their links contain specific attribute.\n     * @param string $attribute The changed attribute name.\n     */\n    private function resetDependentRelations($attribute)\n    {\n        foreach ($this->_relationsDependencies[$attribute] as $relation) {\n            unset($this->_related[$relation]);\n        }\n        unset($this->_relationsDependencies[$attribute]);\n    }\n\n    /**\n     * Sets relation dependencies for a property\n     * @param string $name property name\n     * @param ActiveQueryInterface $relation relation instance\n     * @param string|null $viaRelationName intermediate relation\n     */\n    private function setRelationDependencies($name, $relation, $viaRelationName = null)\n    {\n        if (empty($relation->via) && $relation->link) {\n            foreach ($relation->link as $attribute) {\n                $this->_relationsDependencies[$attribute][$name] = $name;\n                if ($viaRelationName !== null) {\n                    $this->_relationsDependencies[$attribute][] = $viaRelationName;\n                }\n            }\n        } elseif ($relation->via instanceof ActiveQueryInterface) {\n            $this->setRelationDependencies($name, $relation->via);\n        } elseif (is_array($relation->via)) {\n            list($viaRelationName, $viaQuery) = $relation->via;\n            $this->setRelationDependencies($name, $viaQuery, $viaRelationName);\n        }\n    }\n\n    /**\n     * @param mixed $newValue\n     * @param mixed $oldValue\n     * @return bool\n     * @since 2.0.48\n     */\n    private function isValueDifferent($newValue, $oldValue)\n    {\n        if (is_array($newValue) && is_array($oldValue)) {\n            // Only sort associative arrays\n            $sorter = function (&$array) {\n                if (ArrayHelper::isAssociative($array)) {\n                    ksort($array);\n                }\n            };\n            $newValue = ArrayHelper::recursiveSort($newValue, $sorter);\n            $oldValue = ArrayHelper::recursiveSort($oldValue, $sorter);\n        }\n\n        return $newValue !== $oldValue;\n    }\n\n    /**\n     * Eager loads related models for the already loaded primary models.\n     *\n     * Helps to reduce the number of queries performed against database if some related models are only used\n     * when a specific condition is met. For example:\n     *\n     * ```\n     * $customers = Customer::find()->where(['country_id' => 123])->all();\n     * if (Yii:app()->getUser()->getIdentity()->canAccessOrders()) {\n     *     Customer::loadRelationsFor($customers, 'orders.items');\n     * }\n     * ```\n     *\n     * @param array|ActiveRecordInterface[] $models array of primary models. Each model should have the same type and can be:\n     * - an active record instance;\n     * - active record instance represented by array (i.e. active record was loaded using [[ActiveQuery::asArray()]]).\n     * @param string|array $relationNames the names of the relations of primary models to be loaded from database. See [[ActiveQueryInterface::with()]] on how to specify this argument.\n     * @param bool $asArray whether to load each related model as an array or an object (if the relation itself does not specify that).\n     * @since 2.0.50\n     */\n    public static function loadRelationsFor(&$models, $relationNames, $asArray = false)\n    {\n        // ActiveQueryTrait::findWith() called below assumes $models array is non-empty.\n        if (empty($models)) {\n            return;\n        }\n\n        static::find()->asArray($asArray)->findWith((array)$relationNames, $models);\n    }\n\n    /**\n     * Eager loads related models for the already loaded primary model.\n     *\n     * Helps to reduce the number of queries performed against database if some related models are only used\n     * when a specific condition is met. For example:\n     *\n     * ```\n     * $customer = Customer::find()->where(['id' => 123])->one();\n     * if (Yii:app()->getUser()->getIdentity()->canAccessOrders()) {\n     *     $customer->loadRelations('orders.items');\n     * }\n     * ```\n     *\n     * @param string|array $relationNames the names of the relations of this model to be loaded from database. See [[ActiveQueryInterface::with()]] on how to specify this argument.\n     * @param bool $asArray whether to load each relation as an array or an object (if the relation itself does not specify that).\n     * @since 2.0.50\n     */\n    public function loadRelations($relationNames, $asArray = false)\n    {\n        $models = [$this];\n        static::loadRelationsFor($models, $relationNames, $asArray);\n    }\n}\n"
  },
  {
    "path": "framework/db/BatchQueryResult.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\nuse yii\\base\\Component;\n\n/**\n * BatchQueryResult represents a batch query from which you can retrieve data in batches.\n *\n * You usually do not instantiate BatchQueryResult directly. Instead, you obtain it by\n * calling [[Query::batch()]] or [[Query::each()]]. Because BatchQueryResult implements the [[\\Iterator]] interface,\n * you can iterate it to obtain a batch of data in each iteration. For example,\n *\n * ```\n * $query = (new Query)->from('user');\n * foreach ($query->batch() as $i => $users) {\n *     // $users represents the rows in the $i-th batch\n * }\n * foreach ($query->each() as $user) {\n * }\n * ```\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @implements \\Iterator<int, mixed>\n */\nclass BatchQueryResult extends Component implements \\Iterator\n{\n    /**\n     * @event Event an event that is triggered when the batch query is reset.\n     * @see reset()\n     * @since 2.0.41\n     */\n    public const EVENT_RESET = 'reset';\n    /**\n     * @event Event an event that is triggered when the last batch has been fetched.\n     * @since 2.0.41\n     */\n    public const EVENT_FINISH = 'finish';\n    /**\n     * MSSQL error code for exception that is thrown when last batch is size less than specified batch size\n     * @see https://github.com/yiisoft/yii2/issues/10023\n     */\n    public const MSSQL_NO_MORE_ROWS_ERROR_CODE = -13;\n    /**\n     * @var Connection|null the DB connection to be used when performing batch query.\n     * If null, the \"db\" application component will be used.\n     */\n    public $db;\n    /**\n     * @var Query the query object associated with this batch query.\n     * Do not modify this property directly unless after [[reset()]] is called explicitly.\n     */\n    public $query;\n    /**\n     * @var int the number of rows to be returned in each batch.\n     */\n    public $batchSize = 100;\n    /**\n     * @var bool whether to return a single row during each iteration.\n     * If false, a whole batch of rows will be returned in each iteration.\n     */\n    public $each = false;\n\n    /**\n     * @var DataReader|null the data reader associated with this batch query.\n     */\n    private $_dataReader;\n    /**\n     * @var array|null the data retrieved in the current batch\n     */\n    private $_batch;\n    /**\n     * @var mixed the value for the current iteration\n     */\n    private $_value;\n    /**\n     * @var string|int|null the key for the current iteration\n     */\n    private $_key;\n\n\n    /**\n     * Destructor.\n     */\n    public function __destruct()\n    {\n        // make sure cursor is closed\n        $this->reset();\n    }\n\n    /**\n     * Resets the batch query.\n     * This method will clean up the existing batch query so that a new batch query can be performed.\n     */\n    public function reset()\n    {\n        if ($this->_dataReader !== null) {\n            $this->_dataReader->close();\n        }\n        $this->_dataReader = null;\n        $this->_batch = null;\n        $this->_value = null;\n        $this->_key = null;\n        $this->trigger(self::EVENT_RESET);\n    }\n\n    /**\n     * Resets the iterator to the initial state.\n     * This method is required by the interface [[\\Iterator]].\n     */\n    #[\\ReturnTypeWillChange]\n    public function rewind()\n    {\n        $this->reset();\n        $this->next();\n    }\n\n    /**\n     * Moves the internal pointer to the next dataset.\n     * This method is required by the interface [[\\Iterator]].\n     */\n    #[\\ReturnTypeWillChange]\n    public function next()\n    {\n        if ($this->_batch === null || !$this->each || $this->each && next($this->_batch) === false) {\n            $this->_batch = $this->fetchData();\n            reset($this->_batch);\n        }\n\n        if ($this->each) {\n            $this->_value = current($this->_batch);\n            if ($this->query->indexBy !== null) {\n                $this->_key = key($this->_batch);\n            } elseif (key($this->_batch) !== null) {\n                $this->_key = $this->_key === null ? 0 : $this->_key + 1;\n            } else {\n                $this->_key = null;\n            }\n        } else {\n            $this->_value = $this->_batch;\n            $this->_key = $this->_key === null ? 0 : $this->_key + 1;\n        }\n    }\n\n    /**\n     * Fetches the next batch of data.\n     * @return array the data fetched\n     * @throws Exception\n     */\n    protected function fetchData()\n    {\n        if ($this->_dataReader === null) {\n            $this->_dataReader = $this->query->createCommand($this->db)->query();\n        }\n\n        $rows = $this->getRows();\n\n        return $this->query->populate($rows);\n    }\n\n    /**\n     * Reads and collects rows for batch\n     * @return array\n     * @since 2.0.23\n     */\n    protected function getRows()\n    {\n        $rows = [];\n        $count = 0;\n\n        try {\n            while ($count++ < $this->batchSize) {\n                if ($row = $this->_dataReader->read()) {\n                    $rows[] = $row;\n                } else {\n                    // we've reached the end\n                    $this->trigger(self::EVENT_FINISH);\n                    break;\n                }\n            }\n        } catch (\\PDOException $e) {\n            $errorCode = isset($e->errorInfo[1]) ? $e->errorInfo[1] : null;\n            if ($this->getDbDriverName() !== 'sqlsrv' || $errorCode !== self::MSSQL_NO_MORE_ROWS_ERROR_CODE) {\n                throw $e;\n            }\n        }\n\n        return $rows;\n    }\n\n    /**\n     * Returns the index of the current dataset.\n     * This method is required by the interface [[\\Iterator]].\n     * @return int the index of the current row.\n     */\n    #[\\ReturnTypeWillChange]\n    public function key()\n    {\n        return $this->_key;\n    }\n\n    /**\n     * Returns the current dataset.\n     * This method is required by the interface [[\\Iterator]].\n     * @return mixed the current dataset.\n     */\n    #[\\ReturnTypeWillChange]\n    public function current()\n    {\n        return $this->_value;\n    }\n\n    /**\n     * Returns whether there is a valid dataset at the current position.\n     * This method is required by the interface [[\\Iterator]].\n     * @return bool whether there is a valid dataset at the current position.\n     */\n    #[\\ReturnTypeWillChange]\n    public function valid()\n    {\n        return !empty($this->_batch);\n    }\n\n    /**\n     * Gets db driver name from the db connection that is passed to the `batch()`, if it is not passed it uses\n     * connection from the active record model\n     * @return string|null\n     */\n    private function getDbDriverName()\n    {\n        if (isset($this->db->driverName)) {\n            return $this->db->driverName;\n        }\n\n        if (!empty($this->_batch)) {\n            $key = array_keys($this->_batch)[0];\n            if (isset($this->_batch[$key]->db->driverName)) {\n                return $this->_batch[$key]->db->driverName;\n            }\n        }\n\n        return null;\n    }\n\n    /**\n     * Unserialization is disabled to prevent remote code execution in case application\n     * calls unserialize() on user input containing specially crafted string.\n     * @see https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-15148\n     * @since 2.0.38\n     */\n    public function __wakeup()\n    {\n        throw new \\BadMethodCallException('Cannot unserialize ' . __CLASS__);\n    }\n}\n"
  },
  {
    "path": "framework/db/CheckConstraint.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\n/**\n * CheckConstraint represents the metadata of a table `CHECK` constraint.\n *\n * @author Sergey Makinen <sergey@makinen.ru>\n * @since 2.0.13\n */\nclass CheckConstraint extends Constraint\n{\n    /**\n     * @var string the SQL of the `CHECK` constraint.\n     */\n    public $expression;\n}\n"
  },
  {
    "path": "framework/db/ColumnSchema.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\nuse yii\\base\\BaseObject;\nuse yii\\helpers\\StringHelper;\n\n/**\n * ColumnSchema class describes the metadata of a column in a database table.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass ColumnSchema extends BaseObject\n{\n    /**\n     * @var string name of this column (without quotes).\n     */\n    public $name;\n    /**\n     * @var bool whether this column can be null.\n     */\n    public $allowNull;\n    /**\n     * @var string abstract type of this column. Possible abstract types include:\n     * char, string, text, boolean, smallint, integer, bigint, float, decimal, datetime,\n     * timestamp, time, date, binary, and money.\n     */\n    public $type;\n    /**\n     * @var string the PHP type of this column. Possible PHP types include:\n     * `string`, `boolean`, `integer`, `double`, `array`.\n     */\n    public $phpType;\n    /**\n     * @var string the DB type of this column. Possible DB types vary according to the type of DBMS.\n     */\n    public $dbType;\n    /**\n     * @var mixed default value of this column\n     */\n    public $defaultValue;\n    /**\n     * @var array enumerable values. This is set only if the column is declared to be an enumerable type.\n     */\n    public $enumValues;\n    /**\n     * @var int display size of the column.\n     */\n    public $size;\n    /**\n     * @var int precision of the column data, if it is numeric.\n     */\n    public $precision;\n    /**\n     * @var int scale of the column data, if it is numeric.\n     */\n    public $scale;\n    /**\n     * @var bool|null whether this column is a primary key\n     */\n    public $isPrimaryKey;\n    /**\n     * @var bool whether this column is auto-incremental\n     */\n    public $autoIncrement = false;\n    /**\n     * @var bool whether this column is unsigned. This is only meaningful\n     * when [[type]] is `smallint`, `integer` or `bigint`.\n     */\n    public $unsigned;\n    /**\n     * @var string comment of this column. Not all DBMS support this.\n     */\n    public $comment;\n\n\n    /**\n     * Converts the input value according to [[phpType]] after retrieval from the database.\n     * If the value is null or an [[Expression]], it will not be converted.\n     * @param mixed $value input value\n     * @return mixed converted value\n     */\n    public function phpTypecast($value)\n    {\n        return $this->typecast($value);\n    }\n\n    /**\n     * Converts the input value according to [[type]] and [[dbType]] for use in a db query.\n     * If the value is null or an [[Expression]], it will not be converted.\n     * @param mixed $value input value\n     * @return mixed converted value. This may also be an array containing the value as the first element\n     * and the PDO type as the second element.\n     */\n    public function dbTypecast($value)\n    {\n        // the default implementation does the same as casting for PHP, but it should be possible\n        // to override this with annotation of explicit PDO type.\n        return $this->typecast($value);\n    }\n\n    /**\n     * Converts the input value according to [[phpType]] after retrieval from the database.\n     * If the value is null or an [[Expression]], it will not be converted.\n     * @param mixed $value input value\n     * @return mixed converted value\n     * @since 2.0.3\n     */\n    protected function typecast($value)\n    {\n        if (\n            $value === ''\n            && !in_array(\n                $this->type,\n                [\n                    Schema::TYPE_TEXT,\n                    Schema::TYPE_STRING,\n                    Schema::TYPE_BINARY,\n                    Schema::TYPE_CHAR\n                ],\n                true\n            )\n        ) {\n            return null;\n        }\n\n        if (\n            $value === null\n            || gettype($value) === $this->phpType\n            || $value instanceof ExpressionInterface\n            || $value instanceof Query\n        ) {\n            return $value;\n        }\n\n        if (\n            is_array($value)\n            && count($value) === 2\n            && isset($value[1])\n            && in_array($value[1], $this->getPdoParamTypes(), true)\n        ) {\n            return new PdoValue($value[0], $value[1]);\n        }\n\n        switch ($this->phpType) {\n            case 'resource':\n            case 'string':\n                if (is_resource($value)) {\n                    return $value;\n                }\n                if (is_float($value)) {\n                    // ensure type cast always has . as decimal separator in all locales\n                    return StringHelper::floatToString($value);\n                }\n                if (\n                    is_numeric($value)\n                    && ColumnSchemaBuilder::CATEGORY_NUMERIC === ColumnSchemaBuilder::$typeCategoryMap[$this->type]\n                ) {\n                    // https://github.com/yiisoft/yii2/issues/14663\n                    return $value;\n                }\n\n                if (PHP_VERSION_ID >= 80100 && is_object($value) && $value instanceof \\BackedEnum) {\n                    return (string) $value->value;\n                }\n\n                return (string) $value;\n            case 'integer':\n                if (PHP_VERSION_ID >= 80100 && is_object($value) && $value instanceof \\BackedEnum) {\n                    return (int) $value->value;\n                }\n                return (int) $value;\n            case 'boolean':\n                // treating a 0 bit value as false too\n                // https://github.com/yiisoft/yii2/issues/9006\n                return (bool) $value && $value !== \"\\0\" && strtolower($value) !== 'false';\n            case 'double':\n                return (float) $value;\n        }\n\n        return $value;\n    }\n\n    /**\n     * @return int[] array of numbers that represent possible PDO parameter types\n     */\n    private function getPdoParamTypes()\n    {\n        return [\\PDO::PARAM_BOOL, \\PDO::PARAM_INT, \\PDO::PARAM_STR, \\PDO::PARAM_LOB, \\PDO::PARAM_NULL, \\PDO::PARAM_STMT];\n    }\n}\n"
  },
  {
    "path": "framework/db/ColumnSchemaBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\nuse yii\\base\\BaseObject;\nuse yii\\helpers\\StringHelper;\n\n/**\n * ColumnSchemaBuilder helps to define database schema types using a PHP interface.\n *\n * See [[SchemaBuilderTrait]] for more detailed description and usage examples.\n *\n * @property array $categoryMap Mapping of abstract column types (keys) to type categories (values).\n *\n * @author Vasenin Matvey <vaseninm@gmail.com>\n * @since 2.0.6\n */\nclass ColumnSchemaBuilder extends BaseObject\n{\n    // Internally used constants representing categories that abstract column types fall under.\n    // See [[$categoryMap]] for mappings of abstract column types to category.\n    // @since 2.0.8\n    public const CATEGORY_PK = 'pk';\n    public const CATEGORY_STRING = 'string';\n    public const CATEGORY_NUMERIC = 'numeric';\n    public const CATEGORY_TIME = 'time';\n    public const CATEGORY_OTHER = 'other';\n    /**\n     * @var string the column type definition such as INTEGER, VARCHAR, DATETIME, etc.\n     */\n    protected $type;\n    /**\n     * @var int|string|array column size or precision definition. This is what goes into the parenthesis after\n     * the column type. This can be either a string, an integer or an array. If it is an array, the array values will\n     * be joined into a string separated by comma.\n     */\n    protected $length;\n    /**\n     * @var bool|null whether the column is or not nullable. If this is `true`, a `NOT NULL` constraint will be added.\n     * If this is `false`, a `NULL` constraint will be added.\n     */\n    protected $isNotNull;\n    /**\n     * @var bool whether the column values should be unique. If this is `true`, a `UNIQUE` constraint will be added.\n     */\n    protected $isUnique = false;\n    /**\n     * @var string the `CHECK` constraint for the column.\n     */\n    protected $check;\n    /**\n     * @var mixed default value of the column.\n     */\n    protected $default;\n    /**\n     * @var mixed SQL string to be appended to column schema definition.\n     * @since 2.0.9\n     */\n    protected $append;\n    /**\n     * @var bool whether the column values should be unsigned. If this is `true`, an `UNSIGNED` keyword will be added.\n     * @since 2.0.7\n     */\n    protected $isUnsigned = false;\n    /**\n     * @var string the column after which this column will be added.\n     * @since 2.0.8\n     */\n    protected $after;\n    /**\n     * @var bool whether this column is to be inserted at the beginning of the table.\n     * @since 2.0.8\n     */\n    protected $isFirst;\n\n\n    /**\n     * @var array mapping of abstract column types (keys) to type categories (values).\n     * @since 2.0.43\n     */\n    public static $typeCategoryMap = [\n        Schema::TYPE_PK => self::CATEGORY_PK,\n        Schema::TYPE_UPK => self::CATEGORY_PK,\n        Schema::TYPE_BIGPK => self::CATEGORY_PK,\n        Schema::TYPE_UBIGPK => self::CATEGORY_PK,\n        Schema::TYPE_CHAR => self::CATEGORY_STRING,\n        Schema::TYPE_STRING => self::CATEGORY_STRING,\n        Schema::TYPE_TEXT => self::CATEGORY_STRING,\n        Schema::TYPE_TINYINT => self::CATEGORY_NUMERIC,\n        Schema::TYPE_SMALLINT => self::CATEGORY_NUMERIC,\n        Schema::TYPE_INTEGER => self::CATEGORY_NUMERIC,\n        Schema::TYPE_BIGINT => self::CATEGORY_NUMERIC,\n        Schema::TYPE_FLOAT => self::CATEGORY_NUMERIC,\n        Schema::TYPE_DOUBLE => self::CATEGORY_NUMERIC,\n        Schema::TYPE_DECIMAL => self::CATEGORY_NUMERIC,\n        Schema::TYPE_DATETIME => self::CATEGORY_TIME,\n        Schema::TYPE_TIMESTAMP => self::CATEGORY_TIME,\n        Schema::TYPE_TIME => self::CATEGORY_TIME,\n        Schema::TYPE_DATE => self::CATEGORY_TIME,\n        Schema::TYPE_BINARY => self::CATEGORY_OTHER,\n        Schema::TYPE_BOOLEAN => self::CATEGORY_NUMERIC,\n        Schema::TYPE_MONEY => self::CATEGORY_NUMERIC,\n    ];\n    /**\n     * @var \\yii\\db\\Connection the current database connection. It is used mainly to escape strings\n     * safely when building the final column schema string.\n     * @since 2.0.8\n     */\n    public $db;\n    /**\n     * @var string comment value of the column.\n     * @since 2.0.8\n     */\n    public $comment;\n\n    /**\n     * Create a column schema builder instance giving the type and value precision.\n     *\n     * @param string $type type of the column. See [[$type]].\n     * @param int|string|array|null $length length or precision of the column. See [[$length]].\n     * @param \\yii\\db\\Connection|null $db the current database connection. See [[$db]].\n     * @param array $config name-value pairs that will be used to initialize the object properties\n     */\n    public function __construct($type, $length = null, $db = null, $config = [])\n    {\n        $this->type = $type;\n        $this->length = $length;\n        $this->db = $db;\n        parent::__construct($config);\n    }\n\n    /**\n     * Adds a `NOT NULL` constraint to the column.\n     * @return $this\n     */\n    public function notNull()\n    {\n        $this->isNotNull = true;\n        return $this;\n    }\n\n    /**\n     * Adds a `NULL` constraint to the column.\n     * @return $this\n     * @since 2.0.9\n     */\n    public function null()\n    {\n        $this->isNotNull = false;\n        return $this;\n    }\n\n    /**\n     * Adds a `UNIQUE` constraint to the column.\n     * @return $this\n     */\n    public function unique()\n    {\n        $this->isUnique = true;\n        return $this;\n    }\n\n    /**\n     * Sets a `CHECK` constraint for the column.\n     * @param string $check the SQL of the `CHECK` constraint to be added.\n     * @return $this\n     */\n    public function check($check)\n    {\n        $this->check = $check;\n        return $this;\n    }\n\n    /**\n     * Specify the default value for the column.\n     * @param mixed $default the default value.\n     * @return $this\n     */\n    public function defaultValue($default)\n    {\n        if ($default === null) {\n            $this->null();\n        }\n\n        $this->default = $default;\n        return $this;\n    }\n\n    /**\n     * Specifies the comment for column.\n     * @param string $comment the comment\n     * @return $this\n     * @since 2.0.8\n     */\n    public function comment($comment)\n    {\n        $this->comment = $comment;\n        return $this;\n    }\n\n    /**\n     * Marks column as unsigned.\n     * @return $this\n     * @since 2.0.7\n     */\n    public function unsigned()\n    {\n        switch ($this->type) {\n            case Schema::TYPE_PK:\n                $this->type = Schema::TYPE_UPK;\n                break;\n            case Schema::TYPE_BIGPK:\n                $this->type = Schema::TYPE_UBIGPK;\n                break;\n        }\n        $this->isUnsigned = true;\n        return $this;\n    }\n\n    /**\n     * Adds an `AFTER` constraint to the column.\n     * Note: MySQL, Oracle and Cubrid support only.\n     * @param string $after the column after which $this column will be added.\n     * @return $this\n     * @since 2.0.8\n     */\n    public function after($after)\n    {\n        $this->after = $after;\n        return $this;\n    }\n\n    /**\n     * Adds an `FIRST` constraint to the column.\n     * Note: MySQL, Oracle and Cubrid support only.\n     * @return $this\n     * @since 2.0.8\n     */\n    public function first()\n    {\n        $this->isFirst = true;\n        return $this;\n    }\n\n    /**\n     * Specify the default SQL expression for the column.\n     * @param string $default the default value expression.\n     * @return $this\n     * @since 2.0.7\n     */\n    public function defaultExpression($default)\n    {\n        $this->default = new Expression($default);\n        return $this;\n    }\n\n    /**\n     * Specify additional SQL to be appended to column definition.\n     * Position modifiers will be appended after column definition in databases that support them.\n     * @param string $sql the SQL string to be appended.\n     * @return $this\n     * @since 2.0.9\n     */\n    public function append($sql)\n    {\n        $this->append = $sql;\n        return $this;\n    }\n\n    /**\n     * Builds the full string for the column's schema.\n     * @return string\n     */\n    public function __toString()\n    {\n        switch ($this->getTypeCategory()) {\n            case self::CATEGORY_PK:\n                $format = '{type}{check}{comment}{append}';\n                break;\n            default:\n                $format = '{type}{length}{notnull}{unique}{default}{check}{comment}{append}';\n        }\n\n        return $this->buildCompleteString($format);\n    }\n\n    /**\n     * @return array mapping of abstract column types (keys) to type categories (values).\n     * @since 2.0.43\n     */\n    public function getCategoryMap()\n    {\n        return static::$typeCategoryMap;\n    }\n\n    /**\n     * @param array $categoryMap mapping of abstract column types (keys) to type categories (values).\n     * @since 2.0.43\n     */\n    public function setCategoryMap($categoryMap)\n    {\n        static::$typeCategoryMap = $categoryMap;\n    }\n\n    /**\n     * Builds the length/precision part of the column.\n     * @return string\n     */\n    protected function buildLengthString()\n    {\n        if ($this->length === null || $this->length === []) {\n            return '';\n        }\n        if (is_array($this->length)) {\n            $this->length = implode(',', $this->length);\n        }\n\n        return \"({$this->length})\";\n    }\n\n    /**\n     * Builds the not null constraint for the column.\n     * @return string returns 'NOT NULL' if [[isNotNull]] is true,\n     * 'NULL' if [[isNotNull]] is false or an empty string otherwise.\n     */\n    protected function buildNotNullString()\n    {\n        if ($this->isNotNull === true) {\n            return ' NOT NULL';\n        } elseif ($this->isNotNull === false) {\n            return ' NULL';\n        }\n\n        return '';\n    }\n\n    /**\n     * Builds the unique constraint for the column.\n     * @return string returns string 'UNIQUE' if [[isUnique]] is true, otherwise it returns an empty string.\n     */\n    protected function buildUniqueString()\n    {\n        return $this->isUnique ? ' UNIQUE' : '';\n    }\n\n    /**\n     * Return the default value for the column.\n     * @return string|null string with default value of column.\n     */\n    protected function buildDefaultValue()\n    {\n        if ($this->default === null) {\n            return $this->isNotNull === false ? 'NULL' : null;\n        }\n\n        switch (gettype($this->default)) {\n            case 'double':\n                // ensure type cast always has . as decimal separator in all locales\n                $defaultValue = StringHelper::floatToString($this->default);\n                break;\n            case 'boolean':\n                $defaultValue = $this->default ? 'TRUE' : 'FALSE';\n                break;\n            case 'integer':\n            case 'object':\n                $defaultValue = (string) $this->default;\n                break;\n            default:\n                $defaultValue = \"'{$this->default}'\";\n        }\n\n        return $defaultValue;\n    }\n\n    /**\n     * Builds the default value specification for the column.\n     * @return string string with default value of column.\n     */\n    protected function buildDefaultString()\n    {\n        $defaultValue = $this->buildDefaultValue();\n        if ($defaultValue === null) {\n            return '';\n        }\n\n        return ' DEFAULT ' . $defaultValue;\n    }\n\n    /**\n     * Builds the check constraint for the column.\n     * @return string a string containing the CHECK constraint.\n     */\n    protected function buildCheckString()\n    {\n        return $this->check !== null ? \" CHECK ({$this->check})\" : '';\n    }\n\n    /**\n     * Builds the unsigned string for column. Defaults to unsupported.\n     * @return string a string containing UNSIGNED keyword.\n     * @since 2.0.7\n     */\n    protected function buildUnsignedString()\n    {\n        return '';\n    }\n\n    /**\n     * Builds the after constraint for the column. Defaults to unsupported.\n     * @return string a string containing the AFTER constraint.\n     * @since 2.0.8\n     */\n    protected function buildAfterString()\n    {\n        return '';\n    }\n\n    /**\n     * Builds the first constraint for the column. Defaults to unsupported.\n     * @return string a string containing the FIRST constraint.\n     * @since 2.0.8\n     */\n    protected function buildFirstString()\n    {\n        return '';\n    }\n\n    /**\n     * Builds the custom string that's appended to column definition.\n     * @return string custom string to append.\n     * @since 2.0.9\n     */\n    protected function buildAppendString()\n    {\n        return $this->append !== null ? ' ' . $this->append : '';\n    }\n\n    /**\n     * Returns the category of the column type.\n     * @return string a string containing the column type category name.\n     * @since 2.0.8\n     */\n    protected function getTypeCategory()\n    {\n        return isset($this->categoryMap[$this->type]) ? $this->categoryMap[$this->type] : null;\n    }\n\n    /**\n     * Builds the comment specification for the column.\n     * @return string a string containing the COMMENT keyword and the comment itself\n     * @since 2.0.8\n     */\n    protected function buildCommentString()\n    {\n        return '';\n    }\n\n    /**\n     * Returns the complete column definition from input format.\n     * @param string $format the format of the definition.\n     * @return string a string containing the complete column definition.\n     * @since 2.0.8\n     */\n    protected function buildCompleteString($format)\n    {\n        $placeholderValues = [\n            '{type}' => $this->type,\n            '{length}' => $this->buildLengthString(),\n            '{unsigned}' => $this->buildUnsignedString(),\n            '{notnull}' => $this->buildNotNullString(),\n            '{unique}' => $this->buildUniqueString(),\n            '{default}' => $this->buildDefaultString(),\n            '{check}' => $this->buildCheckString(),\n            '{comment}' => $this->buildCommentString(),\n            '{pos}' => $this->isFirst ? $this->buildFirstString() : $this->buildAfterString(),\n            '{append}' => $this->buildAppendString(),\n        ];\n        return strtr($format, $placeholderValues);\n    }\n}\n"
  },
  {
    "path": "framework/db/Command.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\nuse Yii;\nuse yii\\base\\Component;\nuse yii\\base\\NotSupportedException;\n\n/**\n * Command represents a SQL statement to be executed against a database.\n *\n * A command object is usually created by calling [[Connection::createCommand()]].\n * The SQL statement it represents can be set via the [[sql]] property.\n *\n * To execute a non-query SQL (such as INSERT, DELETE, UPDATE), call [[execute()]].\n * To execute a SQL statement that returns a result data set (such as SELECT),\n * use [[queryAll()]], [[queryOne()]], [[queryColumn()]], [[queryScalar()]], or [[query()]].\n *\n * For example,\n *\n * ```\n * $users = $connection->createCommand('SELECT * FROM user')->queryAll();\n * ```\n *\n * Command supports SQL statement preparation and parameter binding.\n * Call [[bindValue()]] to bind a value to a SQL parameter;\n * Call [[bindParam()]] to bind a PHP variable to a SQL parameter.\n * When binding a parameter, the SQL statement is automatically prepared.\n * You may also call [[prepare()]] explicitly to prepare a SQL statement.\n *\n * Command also supports building SQL statements by providing methods such as [[insert()]],\n * [[update()]], etc. For example, the following code will create and execute an INSERT SQL statement:\n *\n * ```\n * $connection->createCommand()->insert('user', [\n *     'name' => 'Sam',\n *     'age' => 30,\n * ])->execute();\n * ```\n *\n * To build SELECT SQL statements, please use [[Query]] instead.\n *\n * For more details and usage information on Command, see the [guide article on Database Access Objects](guide:db-dao).\n *\n * @property string $rawSql The raw SQL with parameter values inserted into the corresponding placeholders in\n * [[sql]].\n * @property string $sql The SQL statement to be executed.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Command extends Component\n{\n    /**\n     * @var Connection the DB connection that this command is associated with\n     */\n    public $db;\n    /**\n     * @var \\PDOStatement|null the PDOStatement object that this command is associated with\n     */\n    public $pdoStatement;\n    /**\n     * @var int the default fetch mode for this command.\n     * @see https://www.php.net/manual/en/pdostatement.setfetchmode.php\n     */\n    public $fetchMode = \\PDO::FETCH_ASSOC;\n    /**\n     * @var array the parameters (name => value) that are bound to the current PDO statement.\n     * This property is maintained by methods such as [[bindValue()]]. It is mainly provided for logging purpose\n     * and is used to generate [[rawSql]]. Do not modify it directly.\n     */\n    public $params = [];\n    /**\n     * @var int the default number of seconds that query results can remain valid in cache.\n     * Use 0 to indicate that the cached data will never expire. And use a negative number to indicate\n     * query cache should not be used.\n     * @see cache()\n     */\n    public $queryCacheDuration;\n    /**\n     * @var \\yii\\caching\\Dependency the dependency to be associated with the cached query result for this command\n     * @see cache()\n     */\n    public $queryCacheDependency;\n\n    /**\n     * @var array pending parameters to be bound to the current PDO statement.\n     * @since 2.0.33\n     */\n    protected $pendingParams = [];\n\n    /**\n     * @var string|null the SQL statement that this command represents\n     */\n    private $_sql;\n    /**\n     * @var string|null name of the table, which schema, should be refreshed after command execution.\n     */\n    private $_refreshTableName;\n    /**\n     * @var string|null|false the isolation level to use for this transaction.\n     * See [[Transaction::begin()]] for details.\n     */\n    private $_isolationLevel = false;\n    /**\n     * @var callable a callable (e.g. anonymous function) that is called when [[\\yii\\db\\Exception]] is thrown\n     * when executing the command.\n     */\n    private $_retryHandler;\n\n\n    /**\n     * Enables query cache for this command.\n     * @param int|null $duration the number of seconds that query result of this command can remain valid in the cache.\n     * If this is not set, the value of [[Connection::queryCacheDuration]] will be used instead.\n     * Use 0 to indicate that the cached data will never expire.\n     * @param \\yii\\caching\\Dependency|null $dependency the cache dependency associated with the cached query result.\n     * @return $this the command object itself\n     */\n    public function cache($duration = null, $dependency = null)\n    {\n        $this->queryCacheDuration = $duration === null ? $this->db->queryCacheDuration : $duration;\n        $this->queryCacheDependency = $dependency;\n        return $this;\n    }\n\n    /**\n     * Disables query cache for this command.\n     * @return $this the command object itself\n     */\n    public function noCache()\n    {\n        $this->queryCacheDuration = -1;\n        return $this;\n    }\n\n    /**\n     * Returns the SQL statement for this command.\n     * @return string the SQL statement to be executed\n     */\n    public function getSql()\n    {\n        return $this->_sql;\n    }\n\n    /**\n     * Specifies the SQL statement to be executed. The SQL statement will be quoted using [[Connection::quoteSql()]].\n     * The previous SQL (if any) will be discarded, and [[params]] will be cleared as well. See [[reset()]]\n     * for details.\n     *\n     * @param string $sql the SQL statement to be set.\n     * @return $this this command instance\n     * @see reset()\n     * @see cancel()\n     */\n    public function setSql($sql)\n    {\n        if ($sql !== $this->_sql) {\n            $this->cancel();\n            $this->reset();\n            $this->_sql = $this->db->quoteSql($sql);\n        }\n\n        return $this;\n    }\n\n    /**\n     * Specifies the SQL statement to be executed. The SQL statement will not be modified in any way.\n     * The previous SQL (if any) will be discarded, and [[params]] will be cleared as well. See [[reset()]]\n     * for details.\n     *\n     * @param string $sql the SQL statement to be set.\n     * @return $this this command instance\n     * @since 2.0.13\n     * @see reset()\n     * @see cancel()\n     */\n    public function setRawSql($sql)\n    {\n        if ($sql !== $this->_sql) {\n            $this->cancel();\n            $this->reset();\n            $this->_sql = $sql;\n        }\n\n        return $this;\n    }\n\n    /**\n     * Returns the raw SQL by inserting parameter values into the corresponding placeholders in [[sql]].\n     * Note that the return value of this method should mainly be used for logging purpose.\n     * It is likely that this method returns an invalid SQL due to improper replacement of parameter placeholders.\n     * @return string the raw SQL with parameter values inserted into the corresponding placeholders in [[sql]].\n     */\n    public function getRawSql()\n    {\n        if (empty($this->params)) {\n            return $this->_sql;\n        }\n        $params = [];\n        foreach ($this->params as $name => $value) {\n            if (is_string($name) && strncmp(':', $name, 1)) {\n                $name = ':' . $name;\n            }\n            if (is_string($value) || $value instanceof Expression) {\n                $params[$name] = $this->db->quoteValue((string)$value);\n            } elseif (is_bool($value)) {\n                $params[$name] = ($value ? 'TRUE' : 'FALSE');\n            } elseif ($value === null) {\n                $params[$name] = 'NULL';\n            } elseif (!is_object($value) && !is_resource($value)) {\n                $params[$name] = $value;\n            }\n        }\n        if (!isset($params[1])) {\n            return preg_replace_callback('#(:\\w+)#', function ($matches) use ($params) {\n                $m = $matches[1];\n                return isset($params[$m]) ? $params[$m] : $m;\n            }, $this->_sql);\n        }\n        $sql = '';\n        foreach (explode('?', $this->_sql) as $i => $part) {\n            $sql .= (isset($params[$i]) ? $params[$i] : '') . $part;\n        }\n\n        return $sql;\n    }\n\n    /**\n     * Prepares the SQL statement to be executed.\n     * For complex SQL statement that is to be executed multiple times,\n     * this may improve performance.\n     * For SQL statement with binding parameters, this method is invoked\n     * automatically.\n     * @param bool|null $forRead whether this method is called for a read query. If null, it means\n     * the SQL statement should be used to determine whether it is for read or write.\n     * @throws Exception if there is any DB error\n     */\n    public function prepare($forRead = null)\n    {\n        if ($this->pdoStatement) {\n            $this->bindPendingParams();\n            return;\n        }\n\n        $sql = $this->getSql();\n        if ($sql === '') {\n            return;\n        }\n\n        if ($this->db->getTransaction()) {\n            // master is in a transaction. use the same connection.\n            $forRead = false;\n        }\n        if ($forRead || $forRead === null && $this->db->getSchema()->isReadQuery($sql)) {\n            $pdo = $this->db->getSlavePdo(true);\n        } else {\n            $pdo = $this->db->getMasterPdo();\n        }\n\n        try {\n            $this->pdoStatement = $pdo->prepare($sql);\n            $this->bindPendingParams();\n        } catch (\\Exception $e) {\n            $message = $e->getMessage() . \"\\nFailed to prepare SQL: $sql\";\n            $errorInfo = $e instanceof \\PDOException ? $e->errorInfo : null;\n            throw new Exception($message, $errorInfo, $e->getCode(), $e);\n        } catch (\\Throwable $e) {\n            $message = $e->getMessage() . \"\\nFailed to prepare SQL: $sql\";\n            throw new Exception($message, null, $e->getCode(), $e);\n        }\n    }\n\n    /**\n     * Cancels the execution of the SQL statement.\n     * This method mainly sets [[pdoStatement]] to be null.\n     */\n    public function cancel()\n    {\n        $this->pdoStatement = null;\n    }\n\n    /**\n     * Binds a parameter to the SQL statement to be executed.\n     * @param string|int $name parameter identifier. For a prepared statement\n     * using named placeholders, this will be a parameter name of\n     * the form `:name`. For a prepared statement using question mark\n     * placeholders, this will be the 1-indexed position of the parameter.\n     * @param mixed $value the PHP variable to bind to the SQL statement parameter (passed by reference)\n     * @param int|null $dataType SQL data type of the parameter. If null, the type is determined by the PHP type of the value.\n     * @param int|null $length length of the data type\n     * @param mixed $driverOptions the driver-specific options\n     * @return $this the current command being executed\n     * @see https://www.php.net/manual/en/function.PDOStatement-bindParam.php\n     */\n    public function bindParam($name, &$value, $dataType = null, $length = null, $driverOptions = null)\n    {\n        $this->prepare();\n\n        if ($dataType === null) {\n            $dataType = $this->db->getSchema()->getPdoType($value);\n        }\n        if ($length === null) {\n            $this->pdoStatement->bindParam($name, $value, $dataType);\n        } elseif ($driverOptions === null) {\n            $this->pdoStatement->bindParam($name, $value, $dataType, $length);\n        } else {\n            $this->pdoStatement->bindParam($name, $value, $dataType, $length, $driverOptions);\n        }\n        $this->params[$name] = &$value;\n\n        return $this;\n    }\n\n    /**\n     * Binds pending parameters that were registered via [[bindValue()]] and [[bindValues()]].\n     * Note that this method requires an active [[pdoStatement]].\n     */\n    protected function bindPendingParams()\n    {\n        foreach ($this->pendingParams as $name => $value) {\n            $this->pdoStatement->bindValue($name, $value[0], $value[1]);\n        }\n        $this->pendingParams = [];\n    }\n\n    /**\n     * Binds a value to a parameter.\n     * @param string|int $name Parameter identifier. For a prepared statement\n     * using named placeholders, this will be a parameter name of\n     * the form `:name`. For a prepared statement using question mark\n     * placeholders, this will be the 1-indexed position of the parameter.\n     * @param mixed $value The value to bind to the parameter\n     * @param int|null $dataType SQL data type of the parameter. If null, the type is determined by the PHP type of the value.\n     * @return $this the current command being executed\n     * @see https://www.php.net/manual/en/function.PDOStatement-bindValue.php\n     */\n    public function bindValue($name, $value, $dataType = null)\n    {\n        if ($dataType === null) {\n            $dataType = $this->db->getSchema()->getPdoType($value);\n        }\n        $this->pendingParams[$name] = [$value, $dataType];\n        $this->params[$name] = $value;\n\n        return $this;\n    }\n\n    /**\n     * Binds a list of values to the corresponding parameters.\n     * This is similar to [[bindValue()]] except that it binds multiple values at a time.\n     * Note that the SQL data type of each value is determined by its PHP type.\n     * @param array $values the values to be bound. This must be given in terms of an associative\n     * array with array keys being the parameter names, and array values the corresponding parameter values,\n     * e.g. `[':name' => 'John', ':age' => 25]`. By default, the PDO type of each value is determined\n     * by its PHP type. You may explicitly specify the PDO type by using a [[yii\\db\\PdoValue]] class: `new PdoValue(value, type)`,\n     * e.g. `[':name' => 'John', ':profile' => new PdoValue($profile, \\PDO::PARAM_LOB)]`.\n     * @return $this the current command being executed\n     */\n    public function bindValues($values)\n    {\n        if (empty($values)) {\n            return $this;\n        }\n\n        $schema = $this->db->getSchema();\n        foreach ($values as $name => $value) {\n            if (is_array($value)) { // TODO: Drop in Yii 2.1\n                $this->pendingParams[$name] = $value;\n                $this->params[$name] = $value[0];\n            } elseif ($value instanceof PdoValue) {\n                $this->pendingParams[$name] = [$value->getValue(), $value->getType()];\n                $this->params[$name] = $value->getValue();\n            } else {\n                if (version_compare(PHP_VERSION, '8.1.0') >= 0) {\n                    if ($value instanceof \\BackedEnum) {\n                        $value = $value->value;\n                    } elseif ($value instanceof \\UnitEnum) {\n                        $value = $value->name;\n                    }\n                }\n                $type = $schema->getPdoType($value);\n                $this->pendingParams[$name] = [$value, $type];\n                $this->params[$name] = $value;\n            }\n        }\n\n        return $this;\n    }\n\n    /**\n     * Executes the SQL statement and returns query result.\n     * This method is for executing a SQL query that returns result set, such as `SELECT`.\n     * @return DataReader the reader object for fetching the query result\n     * @throws Exception execution failed\n     */\n    public function query()\n    {\n        return $this->queryInternal('');\n    }\n\n    /**\n     * Executes the SQL statement and returns ALL rows at once.\n     * @param int|null $fetchMode the result fetch mode. Please refer to [PHP manual](https://www.php.net/manual/en/function.PDOStatement-setFetchMode.php)\n     * for valid fetch modes. If this parameter is null, the value set in [[fetchMode]] will be used.\n     * @return array all rows of the query result. Each array element is an array representing a row of data.\n     * An empty array is returned if the query results in nothing.\n     * @throws Exception execution failed\n     */\n    public function queryAll($fetchMode = null)\n    {\n        return $this->queryInternal('fetchAll', $fetchMode);\n    }\n\n    /**\n     * Executes the SQL statement and returns the first row of the result.\n     * This method is best used when only the first row of result is needed for a query.\n     * @param int|null $fetchMode the result fetch mode. Please refer to [PHP manual](https://www.php.net/manual/en/pdostatement.setfetchmode.php)\n     * for valid fetch modes. If this parameter is null, the value set in [[fetchMode]] will be used.\n     * @return array|false the first row (in terms of an array) of the query result. False is returned if the query\n     * results in nothing.\n     * @throws Exception execution failed\n     */\n    public function queryOne($fetchMode = null)\n    {\n        return $this->queryInternal('fetch', $fetchMode);\n    }\n\n    /**\n     * Executes the SQL statement and returns the value of the first column in the first row of data.\n     * This method is best used when only a single value is needed for a query.\n     * @return string|int|null|false the value of the first column in the first row of the query result.\n     * False is returned if there is no value.\n     * @throws Exception execution failed\n     */\n    public function queryScalar()\n    {\n        $result = $this->queryInternal('fetchColumn', 0);\n        if (is_resource($result) && get_resource_type($result) === 'stream') {\n            return stream_get_contents($result);\n        }\n\n        return $result;\n    }\n\n    /**\n     * Executes the SQL statement and returns the first column of the result.\n     * This method is best used when only the first column of result (i.e. the first element in each row)\n     * is needed for a query.\n     * @return array the first column of the query result. Empty array is returned if the query results in nothing.\n     * @throws Exception execution failed\n     */\n    public function queryColumn()\n    {\n        return $this->queryInternal('fetchAll', \\PDO::FETCH_COLUMN);\n    }\n\n    /**\n     * Creates an INSERT command.\n     *\n     * For example,\n     *\n     * ```\n     * $connection->createCommand()->insert('user', [\n     *     'name' => 'Sam',\n     *     'age' => 30,\n     * ])->execute();\n     * ```\n     *\n     * The method will properly escape the column names, and bind the values to be inserted.\n     *\n     * Note that the created command is not executed until [[execute()]] is called.\n     *\n     * @param string $table the table that new rows will be inserted into.\n     * @param array|\\yii\\db\\Query $columns the column data (name => value) to be inserted into the table or instance\n     * of [[yii\\db\\Query|Query]] to perform INSERT INTO ... SELECT SQL statement.\n     * Passing of [[yii\\db\\Query|Query]] is available since version 2.0.11.\n     * @return $this the command object itself\n     */\n    public function insert($table, $columns)\n    {\n        $params = [];\n        $sql = $this->db->getQueryBuilder()->insert($table, $columns, $params);\n\n        return $this->setSql($sql)->bindValues($params);\n    }\n\n    /**\n     * Creates a batch INSERT command.\n     *\n     * For example,\n     *\n     * ```\n     * $connection->createCommand()->batchInsert('user', ['name', 'age'], [\n     *     ['Tom', 30],\n     *     ['Jane', 20],\n     *     ['Linda', 25],\n     * ])->execute();\n     * ```\n     *\n     * The method will properly escape the column names, and quote the values to be inserted.\n     *\n     * Note that the values in each row must match the corresponding column names.\n     *\n     * Also note that the created command is not executed until [[execute()]] is called.\n     *\n     * @param string $table the table that new rows will be inserted into.\n     * @param array $columns the column names\n     * @param array|\\Generator $rows the rows to be batch inserted into the table\n     * @return $this the command object itself\n     */\n    public function batchInsert($table, $columns, $rows)\n    {\n        $table = $this->db->quoteSql($table);\n        $columns = array_map(function ($column) {\n            return $this->db->quoteSql($column);\n        }, $columns);\n\n        $params = [];\n        $sql = $this->db->getQueryBuilder()->batchInsert($table, $columns, $rows, $params);\n\n        $this->setRawSql($sql);\n        $this->bindValues($params);\n\n        return $this;\n    }\n\n    /**\n     * Creates a command to insert rows into a database table if\n     * they do not already exist (matching unique constraints),\n     * or update them if they do.\n     *\n     * For example,\n     *\n     * ```\n     * $sql = $queryBuilder->upsert('pages', [\n     *     'name' => 'Front page',\n     *     'url' => 'https://example.com/', // url is unique\n     *     'visits' => 0,\n     * ], [\n     *     'visits' => new \\yii\\db\\Expression('visits + 1'),\n     * ], $params);\n     * ```\n     *\n     * The method will properly escape the table and column names.\n     *\n     * @param string $table the table that new rows will be inserted into/updated in.\n     * @param array|Query $insertColumns the column data (name => value) to be inserted into the table or instance\n     * of [[Query]] to perform `INSERT INTO ... SELECT` SQL statement.\n     * @param array|bool $updateColumns the column data (name => value) to be updated if they already exist.\n     * If `true` is passed, the column data will be updated to match the insert column data.\n     * If `false` is passed, no update will be performed if the column data already exists.\n     * @param array $params the parameters to be bound to the command.\n     * @return $this the command object itself.\n     * @since 2.0.14\n     */\n    public function upsert($table, $insertColumns, $updateColumns = true, $params = [])\n    {\n        $sql = $this->db->getQueryBuilder()->upsert($table, $insertColumns, $updateColumns, $params);\n\n        return $this->setSql($sql)->bindValues($params);\n    }\n\n    /**\n     * Creates an UPDATE command.\n     *\n     * For example,\n     *\n     * ```\n     * $connection->createCommand()->update('user', ['status' => 1], 'age > 30')->execute();\n     * ```\n     *\n     * or with using parameter binding for the condition:\n     *\n     * ```\n     * $minAge = 30;\n     * $connection->createCommand()->update('user', ['status' => 1], 'age > :minAge', [':minAge' => $minAge])->execute();\n     * ```\n     *\n     * The method will properly escape the column names and bind the values to be updated.\n     *\n     * Note that the created command is not executed until [[execute()]] is called.\n     *\n     * @param string $table the table to be updated.\n     * @param array $columns the column data (name => value) to be updated.\n     * @param string|array $condition the condition that will be put in the WHERE part. Please\n     * refer to [[Query::where()]] on how to specify condition.\n     * @param array $params the parameters to be bound to the command\n     * @return $this the command object itself\n     */\n    public function update($table, $columns, $condition = '', $params = [])\n    {\n        $sql = $this->db->getQueryBuilder()->update($table, $columns, $condition, $params);\n\n        return $this->setSql($sql)->bindValues($params);\n    }\n\n    /**\n     * Creates a DELETE command.\n     *\n     * For example,\n     *\n     * ```\n     * $connection->createCommand()->delete('user', 'status = 0')->execute();\n     * ```\n     *\n     * or with using parameter binding for the condition:\n     *\n     * ```\n     * $status = 0;\n     * $connection->createCommand()->delete('user', 'status = :status', [':status' => $status])->execute();\n     * ```\n     *\n     * The method will properly escape the table and column names.\n     *\n     * Note that the created command is not executed until [[execute()]] is called.\n     *\n     * @param string $table the table where the data will be deleted from.\n     * @param string|array $condition the condition that will be put in the WHERE part. Please\n     * refer to [[Query::where()]] on how to specify condition.\n     * @param array $params the parameters to be bound to the command\n     * @return $this the command object itself\n     */\n    public function delete($table, $condition = '', $params = [])\n    {\n        $sql = $this->db->getQueryBuilder()->delete($table, $condition, $params);\n\n        return $this->setSql($sql)->bindValues($params);\n    }\n\n    /**\n     * Creates a SQL command for creating a new DB table.\n     *\n     * The columns in the new table should be specified as name-definition pairs (e.g. 'name' => 'string'),\n     * where name stands for a column name which will be properly quoted by the method, and definition\n     * stands for the column type which must contain an abstract DB type.\n     *\n     * The method [[QueryBuilder::getColumnType()]] will be called\n     * to convert the abstract column types to physical ones. For example, `string` will be converted\n     * as `varchar(255)`, and `string not null` becomes `varchar(255) not null`.\n     *\n     * If a column is specified with definition only (e.g. 'PRIMARY KEY (name, type)'), it will be directly\n     * inserted into the generated SQL.\n     *\n     * Example usage:\n     * ```\n     * Yii::$app->db->createCommand()->createTable('post', [\n     *     'id' => 'pk',\n     *     'title' => 'string',\n     *     'text' => 'text',\n     *     'column_name double precision null default null',\n     * ]);\n     * ```\n     *\n     * @param string $table the name of the table to be created. The name will be properly quoted by the method.\n     * @param array $columns the columns (name => definition) in the new table.\n     * @param string|null $options additional SQL fragment that will be appended to the generated SQL.\n     * @return $this the command object itself\n     */\n    public function createTable($table, $columns, $options = null)\n    {\n        $sql = $this->db->getQueryBuilder()->createTable($table, $columns, $options);\n\n        return $this->setSql($sql)->requireTableSchemaRefresh($table);\n    }\n\n    /**\n     * Creates a SQL command for renaming a DB table.\n     * @param string $table the table to be renamed. The name will be properly quoted by the method.\n     * @param string $newName the new table name. The name will be properly quoted by the method.\n     * @return $this the command object itself\n     */\n    public function renameTable($table, $newName)\n    {\n        $sql = $this->db->getQueryBuilder()->renameTable($table, $newName);\n\n        return $this->setSql($sql)->requireTableSchemaRefresh($table);\n    }\n\n    /**\n     * Creates a SQL command for dropping a DB table.\n     * @param string $table the table to be dropped. The name will be properly quoted by the method.\n     * @return $this the command object itself\n     */\n    public function dropTable($table)\n    {\n        $sql = $this->db->getQueryBuilder()->dropTable($table);\n\n        return $this->setSql($sql)->requireTableSchemaRefresh($table);\n    }\n\n    /**\n     * Creates a SQL command for truncating a DB table.\n     * @param string $table the table to be truncated. The name will be properly quoted by the method.\n     * @return $this the command object itself\n     */\n    public function truncateTable($table)\n    {\n        $sql = $this->db->getQueryBuilder()->truncateTable($table);\n\n        return $this->setSql($sql);\n    }\n\n    /**\n     * Creates a SQL command for adding a new DB column.\n     * @param string $table the table that the new column will be added to. The table name will be properly quoted by the method.\n     * @param string $column the name of the new column. The name will be properly quoted by the method.\n     * @param string $type the column type. [[\\yii\\db\\QueryBuilder::getColumnType()]] will be called\n     * to convert the given column type to the physical one. For example, `string` will be converted\n     * as `varchar(255)`, and `string not null` becomes `varchar(255) not null`.\n     * @return $this the command object itself\n     */\n    public function addColumn($table, $column, $type)\n    {\n        $sql = $this->db->getQueryBuilder()->addColumn($table, $column, $type);\n\n        return $this->setSql($sql)->requireTableSchemaRefresh($table);\n    }\n\n    /**\n     * Creates a SQL command for dropping a DB column.\n     * @param string $table the table whose column is to be dropped. The name will be properly quoted by the method.\n     * @param string $column the name of the column to be dropped. The name will be properly quoted by the method.\n     * @return $this the command object itself\n     */\n    public function dropColumn($table, $column)\n    {\n        $sql = $this->db->getQueryBuilder()->dropColumn($table, $column);\n\n        return $this->setSql($sql)->requireTableSchemaRefresh($table);\n    }\n\n    /**\n     * Creates a SQL command for renaming a column.\n     * @param string $table the table whose column is to be renamed. The name will be properly quoted by the method.\n     * @param string $oldName the old name of the column. The name will be properly quoted by the method.\n     * @param string $newName the new name of the column. The name will be properly quoted by the method.\n     * @return $this the command object itself\n     */\n    public function renameColumn($table, $oldName, $newName)\n    {\n        $sql = $this->db->getQueryBuilder()->renameColumn($table, $oldName, $newName);\n\n        return $this->setSql($sql)->requireTableSchemaRefresh($table);\n    }\n\n    /**\n     * Creates a SQL command for changing the definition of a column.\n     * @param string $table the table whose column is to be changed. The table name will be properly quoted by the method.\n     * @param string $column the name of the column to be changed. The name will be properly quoted by the method.\n     * @param string $type the column type. [[\\yii\\db\\QueryBuilder::getColumnType()]] will be called\n     * to convert the give column type to the physical one. For example, `string` will be converted\n     * as `varchar(255)`, and `string not null` becomes `varchar(255) not null`.\n     * @return $this the command object itself\n     */\n    public function alterColumn($table, $column, $type)\n    {\n        $sql = $this->db->getQueryBuilder()->alterColumn($table, $column, $type);\n\n        return $this->setSql($sql)->requireTableSchemaRefresh($table);\n    }\n\n    /**\n     * Creates a SQL command for adding a primary key constraint to an existing table.\n     * The method will properly quote the table and column names.\n     * @param string $name the name of the primary key constraint.\n     * @param string $table the table that the primary key constraint will be added to.\n     * @param string|array $columns comma separated string or array of columns that the primary key will consist of.\n     * @return $this the command object itself.\n     */\n    public function addPrimaryKey($name, $table, $columns)\n    {\n        $sql = $this->db->getQueryBuilder()->addPrimaryKey($name, $table, $columns);\n\n        return $this->setSql($sql)->requireTableSchemaRefresh($table);\n    }\n\n    /**\n     * Creates a SQL command for removing a primary key constraint to an existing table.\n     * @param string $name the name of the primary key constraint to be removed.\n     * @param string $table the table that the primary key constraint will be removed from.\n     * @return $this the command object itself\n     */\n    public function dropPrimaryKey($name, $table)\n    {\n        $sql = $this->db->getQueryBuilder()->dropPrimaryKey($name, $table);\n\n        return $this->setSql($sql)->requireTableSchemaRefresh($table);\n    }\n\n    /**\n     * Creates a SQL command for adding a foreign key constraint to an existing table.\n     * The method will properly quote the table and column names.\n     * @param string $name the name of the foreign key constraint.\n     * @param string $table the table that the foreign key constraint will be added to.\n     * @param string|array $columns the name of the column to that the constraint will be added on. If there are multiple columns, separate them with commas.\n     * @param string $refTable the table that the foreign key references to.\n     * @param string|array $refColumns the name of the column that the foreign key references to. If there are multiple columns, separate them with commas.\n     * @param string|null $delete the ON DELETE option. Most DBMS support these options: RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL\n     * @param string|null $update the ON UPDATE option. Most DBMS support these options: RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL\n     * @return $this the command object itself\n     */\n    public function addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete = null, $update = null)\n    {\n        $sql = $this->db->getQueryBuilder()->addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete, $update);\n\n        return $this->setSql($sql)->requireTableSchemaRefresh($table);\n    }\n\n    /**\n     * Creates a SQL command for dropping a foreign key constraint.\n     * @param string $name the name of the foreign key constraint to be dropped. The name will be properly quoted by the method.\n     * @param string $table the table whose foreign is to be dropped. The name will be properly quoted by the method.\n     * @return $this the command object itself\n     */\n    public function dropForeignKey($name, $table)\n    {\n        $sql = $this->db->getQueryBuilder()->dropForeignKey($name, $table);\n\n        return $this->setSql($sql)->requireTableSchemaRefresh($table);\n    }\n\n    /**\n     * Creates a SQL command for creating a new index.\n     * @param string $name the name of the index. The name will be properly quoted by the method.\n     * @param string $table the table that the new index will be created for. The table name will be properly quoted by the method.\n     * @param string|array $columns the column(s) that should be included in the index. If there are multiple columns, please separate them\n     * by commas. The column names will be properly quoted by the method.\n     * @param bool $unique whether to add UNIQUE constraint on the created index.\n     * @return $this the command object itself\n     */\n    public function createIndex($name, $table, $columns, $unique = false)\n    {\n        $sql = $this->db->getQueryBuilder()->createIndex($name, $table, $columns, $unique);\n\n        return $this->setSql($sql)->requireTableSchemaRefresh($table);\n    }\n\n    /**\n     * Creates a SQL command for dropping an index.\n     * @param string $name the name of the index to be dropped. The name will be properly quoted by the method.\n     * @param string $table the table whose index is to be dropped. The name will be properly quoted by the method.\n     * @return $this the command object itself\n     */\n    public function dropIndex($name, $table)\n    {\n        $sql = $this->db->getQueryBuilder()->dropIndex($name, $table);\n\n        return $this->setSql($sql)->requireTableSchemaRefresh($table);\n    }\n\n    /**\n     * Creates a SQL command for adding an unique constraint to an existing table.\n     * @param string $name the name of the unique constraint.\n     * The name will be properly quoted by the method.\n     * @param string $table the table that the unique constraint will be added to.\n     * The name will be properly quoted by the method.\n     * @param string|array $columns the name of the column to that the constraint will be added on.\n     * If there are multiple columns, separate them with commas.\n     * The name will be properly quoted by the method.\n     * @return $this the command object itself.\n     * @since 2.0.13\n     */\n    public function addUnique($name, $table, $columns)\n    {\n        $sql = $this->db->getQueryBuilder()->addUnique($name, $table, $columns);\n\n        return $this->setSql($sql)->requireTableSchemaRefresh($table);\n    }\n\n    /**\n     * Creates a SQL command for dropping an unique constraint.\n     * @param string $name the name of the unique constraint to be dropped.\n     * The name will be properly quoted by the method.\n     * @param string $table the table whose unique constraint is to be dropped.\n     * The name will be properly quoted by the method.\n     * @return $this the command object itself.\n     * @since 2.0.13\n     */\n    public function dropUnique($name, $table)\n    {\n        $sql = $this->db->getQueryBuilder()->dropUnique($name, $table);\n\n        return $this->setSql($sql)->requireTableSchemaRefresh($table);\n    }\n\n    /**\n     * Creates a SQL command for adding a check constraint to an existing table.\n     * @param string $name the name of the check constraint.\n     * The name will be properly quoted by the method.\n     * @param string $table the table that the check constraint will be added to.\n     * The name will be properly quoted by the method.\n     * @param string $expression the SQL of the `CHECK` constraint.\n     * @return $this the command object itself.\n     * @since 2.0.13\n     */\n    public function addCheck($name, $table, $expression)\n    {\n        $sql = $this->db->getQueryBuilder()->addCheck($name, $table, $expression);\n\n        return $this->setSql($sql)->requireTableSchemaRefresh($table);\n    }\n\n    /**\n     * Creates a SQL command for dropping a check constraint.\n     * @param string $name the name of the check constraint to be dropped.\n     * The name will be properly quoted by the method.\n     * @param string $table the table whose check constraint is to be dropped.\n     * The name will be properly quoted by the method.\n     * @return $this the command object itself.\n     * @since 2.0.13\n     */\n    public function dropCheck($name, $table)\n    {\n        $sql = $this->db->getQueryBuilder()->dropCheck($name, $table);\n\n        return $this->setSql($sql)->requireTableSchemaRefresh($table);\n    }\n\n    /**\n     * Creates a SQL command for adding a default value constraint to an existing table.\n     * @param string $name the name of the default value constraint.\n     * The name will be properly quoted by the method.\n     * @param string $table the table that the default value constraint will be added to.\n     * The name will be properly quoted by the method.\n     * @param string $column the name of the column to that the constraint will be added on.\n     * The name will be properly quoted by the method.\n     * @param mixed $value default value.\n     * @return $this the command object itself.\n     * @since 2.0.13\n     */\n    public function addDefaultValue($name, $table, $column, $value)\n    {\n        $sql = $this->db->getQueryBuilder()->addDefaultValue($name, $table, $column, $value);\n\n        return $this->setSql($sql)->requireTableSchemaRefresh($table);\n    }\n\n    /**\n     * Creates a SQL command for dropping a default value constraint.\n     * @param string $name the name of the default value constraint to be dropped.\n     * The name will be properly quoted by the method.\n     * @param string $table the table whose default value constraint is to be dropped.\n     * The name will be properly quoted by the method.\n     * @return $this the command object itself.\n     * @since 2.0.13\n     */\n    public function dropDefaultValue($name, $table)\n    {\n        $sql = $this->db->getQueryBuilder()->dropDefaultValue($name, $table);\n\n        return $this->setSql($sql)->requireTableSchemaRefresh($table);\n    }\n\n    /**\n     * Creates a SQL command for resetting the sequence value of a table's primary key.\n     * The sequence will be reset such that the primary key of the next new row inserted\n     * will have the specified value or the maximum existing value +1.\n     * @param string $table the name of the table whose primary key sequence will be reset\n     * @param mixed $value the value for the primary key of the next new row inserted. If this is not set,\n     * the next new row's primary key will have the maximum existing value +1.\n     * @return $this the command object itself\n     * @throws NotSupportedException if this is not supported by the underlying DBMS\n     */\n    public function resetSequence($table, $value = null)\n    {\n        $sql = $this->db->getQueryBuilder()->resetSequence($table, $value);\n\n        return $this->setSql($sql);\n    }\n\n    /**\n     * Executes a db command resetting the sequence value of a table's primary key.\n     * Reason for execute is that some databases (Oracle) need several queries to do so.\n     * The sequence is reset such that the primary key of the next new row inserted\n     * will have the specified value or the maximum existing value +1.\n     * @param string $table the name of the table whose primary key sequence is reset\n     * @param mixed $value the value for the primary key of the next new row inserted. If this is not set,\n     * the next new row's primary key will have the maximum existing value +1.\n     * @throws NotSupportedException if this is not supported by the underlying DBMS\n     * @since 2.0.16\n     */\n    public function executeResetSequence($table, $value = null)\n    {\n        return $this->db->getQueryBuilder()->executeResetSequence($table, $value);\n    }\n\n    /**\n     * Builds a SQL command for enabling or disabling integrity check.\n     * @param bool $check whether to turn on or off the integrity check.\n     * @param string $schema the schema name of the tables. Defaults to empty string, meaning the current\n     * or default schema.\n     * @param string $table the table name.\n     * @return $this the command object itself\n     * @throws NotSupportedException if this is not supported by the underlying DBMS\n     */\n    public function checkIntegrity($check = true, $schema = '', $table = '')\n    {\n        $sql = $this->db->getQueryBuilder()->checkIntegrity($check, $schema, $table);\n\n        return $this->setSql($sql);\n    }\n\n    /**\n     * Builds a SQL command for adding comment to column.\n     *\n     * @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.\n     * @param string $column the name of the column to be commented. The column name will be properly quoted by the method.\n     * @param string $comment the text of the comment to be added. The comment will be properly quoted by the method.\n     * @return $this the command object itself\n     * @since 2.0.8\n     */\n    public function addCommentOnColumn($table, $column, $comment)\n    {\n        $sql = $this->db->getQueryBuilder()->addCommentOnColumn($table, $column, $comment);\n\n        return $this->setSql($sql)->requireTableSchemaRefresh($table);\n    }\n\n    /**\n     * Builds a SQL command for adding comment to table.\n     *\n     * @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.\n     * @param string $comment the text of the comment to be added. The comment will be properly quoted by the method.\n     * @return $this the command object itself\n     * @since 2.0.8\n     */\n    public function addCommentOnTable($table, $comment)\n    {\n        $sql = $this->db->getQueryBuilder()->addCommentOnTable($table, $comment);\n\n        return $this->setSql($sql);\n    }\n\n    /**\n     * Builds a SQL command for dropping comment from column.\n     *\n     * @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.\n     * @param string $column the name of the column to be commented. The column name will be properly quoted by the method.\n     * @return $this the command object itself\n     * @since 2.0.8\n     */\n    public function dropCommentFromColumn($table, $column)\n    {\n        $sql = $this->db->getQueryBuilder()->dropCommentFromColumn($table, $column);\n\n        return $this->setSql($sql)->requireTableSchemaRefresh($table);\n    }\n\n    /**\n     * Builds a SQL command for dropping comment from table.\n     *\n     * @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.\n     * @return $this the command object itself\n     * @since 2.0.8\n     */\n    public function dropCommentFromTable($table)\n    {\n        $sql = $this->db->getQueryBuilder()->dropCommentFromTable($table);\n\n        return $this->setSql($sql);\n    }\n\n    /**\n     * Creates a SQL View.\n     *\n     * @param string $viewName the name of the view to be created.\n     * @param string|Query $subquery the select statement which defines the view.\n     * This can be either a string or a [[Query]] object.\n     * @return $this the command object itself.\n     * @since 2.0.14\n     */\n    public function createView($viewName, $subquery)\n    {\n        $sql = $this->db->getQueryBuilder()->createView($viewName, $subquery);\n\n        return $this->setSql($sql)->requireTableSchemaRefresh($viewName);\n    }\n\n    /**\n     * Drops a SQL View.\n     *\n     * @param string $viewName the name of the view to be dropped.\n     * @return $this the command object itself.\n     * @since 2.0.14\n     */\n    public function dropView($viewName)\n    {\n        $sql = $this->db->getQueryBuilder()->dropView($viewName);\n\n        return $this->setSql($sql)->requireTableSchemaRefresh($viewName);\n    }\n\n    /**\n     * Executes the SQL statement.\n     * This method should only be used for executing non-query SQL statement, such as `INSERT`, `DELETE`, `UPDATE` SQLs.\n     * No result set will be returned.\n     * @return int number of rows affected by the execution.\n     * @throws Exception execution failed\n     */\n    public function execute()\n    {\n        $sql = $this->getSql();\n        list($profile, $rawSql) = $this->logQuery(__METHOD__);\n\n        if ($sql == '') {\n            return 0;\n        }\n\n        $this->prepare(false);\n\n        try {\n            $profile and Yii::beginProfile($rawSql, __METHOD__);\n\n            $this->internalExecute($rawSql);\n            $n = $this->pdoStatement->rowCount();\n\n            $profile and Yii::endProfile($rawSql, __METHOD__);\n\n            $this->refreshTableSchema();\n\n            return $n;\n        } catch (Exception $e) {\n            $profile and Yii::endProfile($rawSql, __METHOD__);\n            throw $e;\n        }\n    }\n\n    /**\n     * Logs the current database query if query logging is enabled and returns\n     * the profiling token if profiling is enabled.\n     * @param string $category the log category.\n     * @return array array of two elements, the first is boolean of whether profiling is enabled or not.\n     * The second is the rawSql if it has been created.\n     */\n    protected function logQuery($category)\n    {\n        if ($this->db->enableLogging) {\n            $rawSql = $this->getRawSql();\n            Yii::info($rawSql, $category);\n        }\n        if (!$this->db->enableProfiling) {\n            return [false, isset($rawSql) ? $rawSql : null];\n        }\n\n        return [true, isset($rawSql) ? $rawSql : $this->getRawSql()];\n    }\n\n    /**\n     * Performs the actual DB query of a SQL statement.\n     * @param string $method method of PDOStatement to be called\n     * @param int|null $fetchMode the result fetch mode. Please refer to [PHP manual](https://www.php.net/manual/en/function.PDOStatement-setFetchMode.php)\n     * for valid fetch modes. If this parameter is null, the value set in [[fetchMode]] will be used.\n     * @return mixed the method execution result\n     * @throws Exception if the query causes any problem\n     * @since 2.0.1 this method is protected (was private before).\n     */\n    protected function queryInternal($method, $fetchMode = null)\n    {\n        list($profile, $rawSql) = $this->logQuery('yii\\db\\Command::query');\n\n        if ($method !== '') {\n            $info = $this->db->getQueryCacheInfo($this->queryCacheDuration, $this->queryCacheDependency);\n            if (is_array($info)) {\n                /** @var \\yii\\caching\\CacheInterface $cache */\n                $cache = $info[0];\n                $cacheKey = $this->getCacheKey($method, $fetchMode, '');\n                $result = $cache->get($cacheKey);\n                if (is_array($result) && array_key_exists(0, $result)) {\n                    Yii::debug('Query result served from cache', 'yii\\db\\Command::query');\n                    return $result[0];\n                }\n            }\n        }\n\n        $this->prepare(true);\n\n        try {\n            $profile and Yii::beginProfile($rawSql, 'yii\\db\\Command::query');\n\n            $this->internalExecute($rawSql);\n\n            if ($method === '') {\n                $result = new DataReader($this);\n            } else {\n                if ($fetchMode === null) {\n                    $fetchMode = $this->fetchMode;\n                }\n                $result = call_user_func_array([$this->pdoStatement, $method], (array) $fetchMode);\n                $this->pdoStatement->closeCursor();\n            }\n\n            $profile and Yii::endProfile($rawSql, 'yii\\db\\Command::query');\n        } catch (Exception $e) {\n            $profile and Yii::endProfile($rawSql, 'yii\\db\\Command::query');\n            throw $e;\n        }\n\n        if (isset($cache, $cacheKey, $info)) {\n            $cache->set($cacheKey, [$result], $info[1], $info[2]);\n            Yii::debug('Saved query result in cache', 'yii\\db\\Command::query');\n        }\n\n        return $result;\n    }\n\n    /**\n     * Returns the cache key for the query.\n     *\n     * @param string $method method of PDOStatement to be called\n     * @param int $fetchMode the result fetch mode. Please refer to [PHP manual](https://www.php.net/manual/en/function.PDOStatement-setFetchMode.php)\n     * for valid fetch modes.\n     * @return array the cache key\n     * @since 2.0.16\n     */\n    protected function getCacheKey($method, $fetchMode, $rawSql)\n    {\n        $params = $this->params;\n        ksort($params);\n        return [\n            __CLASS__,\n            $method,\n            $fetchMode,\n            $this->db->dsn,\n            $this->db->username,\n            $this->getSql(),\n            json_encode($params),\n        ];\n    }\n\n    /**\n     * Marks a specified table schema to be refreshed after command execution.\n     * @param string $name name of the table, which schema should be refreshed.\n     * @return $this this command instance\n     * @since 2.0.6\n     */\n    protected function requireTableSchemaRefresh($name)\n    {\n        $this->_refreshTableName = $name;\n        return $this;\n    }\n\n    /**\n     * Refreshes table schema, which was marked by [[requireTableSchemaRefresh()]].\n     * @since 2.0.6\n     */\n    protected function refreshTableSchema()\n    {\n        if ($this->_refreshTableName !== null) {\n            $this->db->getSchema()->refreshTableSchema($this->_refreshTableName);\n        }\n    }\n\n    /**\n     * Marks the command to be executed in transaction.\n     * @param string|null $isolationLevel The isolation level to use for this transaction.\n     * See [[Transaction::begin()]] for details.\n     * @return $this this command instance.\n     * @since 2.0.14\n     */\n    protected function requireTransaction($isolationLevel = null)\n    {\n        $this->_isolationLevel = $isolationLevel;\n        return $this;\n    }\n\n    /**\n     * Sets a callable (e.g. anonymous function) that is called when [[Exception]] is thrown\n     * when executing the command. The signature of the callable should be:\n     *\n     * ```\n     * function (\\yii\\db\\Exception $e, $attempt)\n     * {\n     *     // return true or false (whether to retry the command or rethrow $e)\n     * }\n     * ```\n     *\n     * The callable will recieve a database exception thrown and a current attempt\n     * (to execute the command) number starting from 1.\n     *\n     * @param callable $handler a PHP callback to handle database exceptions.\n     * @return $this this command instance.\n     * @since 2.0.14\n     */\n    protected function setRetryHandler(callable $handler)\n    {\n        $this->_retryHandler = $handler;\n        return $this;\n    }\n\n    /**\n     * Executes a prepared statement.\n     *\n     * It's a wrapper around [[\\PDOStatement::execute()]] to support transactions\n     * and retry handlers.\n     *\n     * @param string|null $rawSql the rawSql if it has been created.\n     * @throws Exception if execution failed.\n     * @since 2.0.14\n     */\n    protected function internalExecute($rawSql)\n    {\n        $attempt = 0;\n        while (true) {\n            try {\n                if (\n                    ++$attempt === 1\n                    && $this->_isolationLevel !== false\n                    && $this->db->getTransaction() === null\n                ) {\n                    $this->db->transaction(function () use ($rawSql) {\n                        $this->internalExecute($rawSql);\n                    }, $this->_isolationLevel);\n                } else {\n                    $this->pdoStatement->execute();\n                }\n                break;\n            } catch (\\Exception $e) {\n                $rawSql = $rawSql ?: $this->getRawSql();\n                $e = $this->db->getSchema()->convertException($e, $rawSql);\n                if ($this->_retryHandler === null || !call_user_func($this->_retryHandler, $e, $attempt)) {\n                    throw $e;\n                }\n            }\n        }\n    }\n\n    /**\n     * Resets command properties to their initial state.\n     *\n     * @since 2.0.13\n     */\n    protected function reset()\n    {\n        $this->_sql = null;\n        $this->pendingParams = [];\n        $this->params = [];\n        $this->_refreshTableName = null;\n        $this->_isolationLevel = false;\n    }\n}\n"
  },
  {
    "path": "framework/db/Connection.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\nuse PDO;\nuse Yii;\nuse yii\\base\\Component;\nuse yii\\base\\InvalidConfigException;\nuse yii\\base\\NotSupportedException;\nuse yii\\caching\\CacheInterface;\n\n/**\n * Connection represents a connection to a database via [PDO](https://www.php.net/manual/en/book.pdo.php).\n *\n * Connection works together with [[Command]], [[DataReader]] and [[Transaction]]\n * to provide data access to various DBMS in a common set of APIs. They are a thin wrapper\n * of the [PDO PHP extension](https://www.php.net/manual/en/book.pdo.php).\n *\n * Connection supports database replication and read-write splitting. In particular, a Connection component\n * can be configured with multiple [[masters]] and [[slaves]]. It will do load balancing and failover by choosing\n * appropriate servers. It will also automatically direct read operations to the slaves and write operations to\n * the masters.\n *\n * To establish a DB connection, set [[dsn]], [[username]] and [[password]], and then\n * call [[open()]] to connect to the database server. The current state of the connection can be checked using [[$isActive]].\n *\n * The following example shows how to create a Connection instance and establish\n * the DB connection:\n *\n * ```\n * $connection = new \\yii\\db\\Connection([\n *     'dsn' => $dsn,\n *     'username' => $username,\n *     'password' => $password,\n * ]);\n * $connection->open();\n * ```\n *\n * After the DB connection is established, one can execute SQL statements like the following:\n *\n * ```\n * $command = $connection->createCommand('SELECT * FROM post');\n * $posts = $command->queryAll();\n * $command = $connection->createCommand('UPDATE post SET status=1');\n * $command->execute();\n * ```\n *\n * One can also do prepared SQL execution and bind parameters to the prepared SQL.\n * When the parameters are coming from user input, you should use this approach\n * to prevent SQL injection attacks. The following is an example:\n *\n * ```\n * $command = $connection->createCommand('SELECT * FROM post WHERE id=:id');\n * $command->bindValue(':id', $_GET['id']);\n * $post = $command->query();\n * ```\n *\n * For more information about how to perform various DB queries, please refer to [[Command]].\n *\n * If the underlying DBMS supports transactions, you can perform transactional SQL queries\n * like the following:\n *\n * ```\n * $transaction = $connection->beginTransaction();\n * try {\n *     $connection->createCommand($sql1)->execute();\n *     $connection->createCommand($sql2)->execute();\n *     // ... executing other SQL statements ...\n *     $transaction->commit();\n * } catch (Exception $e) {\n *     $transaction->rollBack();\n * }\n * ```\n *\n * You also can use shortcut for the above like the following:\n *\n * ```\n * $connection->transaction(function () {\n *     $order = new Order($customer);\n *     $order->save();\n *     $order->addItems($items);\n * });\n * ```\n *\n * If needed you can pass transaction isolation level as a second parameter:\n *\n * ```\n * $connection->transaction(function (Connection $db) {\n *     //return $db->...\n * }, Transaction::READ_UNCOMMITTED);\n * ```\n *\n * Connection is often used as an application component and configured in the application\n * configuration like the following:\n *\n * ```\n * 'components' => [\n *     'db' => [\n *         'class' => '\\yii\\db\\Connection',\n *         'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n *         'username' => 'root',\n *         'password' => '',\n *         'charset' => 'utf8',\n *     ],\n * ],\n * ```\n *\n * @property string|null $driverName Name of the DB driver. Note that the type of this property differs in\n * getter and setter. See [[getDriverName()]] and [[setDriverName()]] for details.\n * @property-read bool $isActive Whether the DB connection is established.\n * @property-read string $lastInsertID The row ID of the last row inserted, or the last value retrieved from\n * the sequence object.\n * @property-read Connection|null $master The currently active master connection. `null` is returned if there\n * is no master available.\n * @property-read PDO $masterPdo The PDO instance for the currently active master connection.\n * @property QueryBuilder $queryBuilder The query builder for the current DB connection. Note that the type of\n * this property differs in getter and setter. See [[getQueryBuilder()]] and [[setQueryBuilder()]] for details.\n * @property-read Schema $schema The schema information for the database opened by this\n * connection.\n * @property-read string $serverVersion Server version as a string.\n * @property-read Connection|null $slave The currently active slave connection. `null` is returned if there is\n * no slave available and `$fallbackToMaster` is false.\n * @property-read PDO|null $slavePdo The PDO instance for the currently active slave connection. `null` is\n * returned if no slave connection is available and `$fallbackToMaster` is false.\n * @property-read Transaction|null $transaction The currently active transaction. Null if no active\n * transaction.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Connection extends Component\n{\n    /**\n     * @event \\yii\\base\\Event an event that is triggered after a DB connection is established\n     */\n    public const EVENT_AFTER_OPEN = 'afterOpen';\n    /**\n     * @event \\yii\\base\\Event an event that is triggered right before a top-level transaction is started\n     */\n    public const EVENT_BEGIN_TRANSACTION = 'beginTransaction';\n    /**\n     * @event \\yii\\base\\Event an event that is triggered right after a top-level transaction is committed\n     */\n    public const EVENT_COMMIT_TRANSACTION = 'commitTransaction';\n    /**\n     * @event \\yii\\base\\Event an event that is triggered right after a top-level transaction is rolled back\n     */\n    public const EVENT_ROLLBACK_TRANSACTION = 'rollbackTransaction';\n    /**\n     * @var string the Data Source Name, or DSN, contains the information required to connect to the database.\n     * Please refer to the [PHP manual](https://www.php.net/manual/en/pdo.construct.php) on\n     * the format of the DSN string.\n     *\n     * For [SQLite](https://www.php.net/manual/en/ref.pdo-sqlite.connection.php) you may use a [path alias](guide:concept-aliases)\n     * for specifying the database path, e.g. `sqlite:@app/data/db.sql`.\n     *\n     * @see charset\n     */\n    public $dsn;\n    /**\n     * @var string|null the username for establishing DB connection. Defaults to `null` meaning no username to use.\n     */\n    public $username;\n    /**\n     * @var string|null the password for establishing DB connection. Defaults to `null` meaning no password to use.\n     */\n    public $password;\n    /**\n     * @var array PDO attributes (name => value) that should be set when calling [[open()]]\n     * to establish a DB connection. Please refer to the\n     * [PHP manual](https://www.php.net/manual/en/pdo.setattribute.php) for\n     * details about available attributes.\n     */\n    public $attributes;\n    /**\n     * @var PDO|null the PHP PDO instance associated with this DB connection.\n     * This property is mainly managed by [[open()]] and [[close()]] methods.\n     * When a DB connection is active, this property will represent a PDO instance;\n     * otherwise, it will be null.\n     * @see pdoClass\n     */\n    public $pdo;\n    /**\n     * @var bool whether to enable schema caching.\n     * Note that in order to enable truly schema caching, a valid cache component as specified\n     * by [[schemaCache]] must be enabled and [[enableSchemaCache]] must be set true.\n     * @see schemaCacheDuration\n     * @see schemaCacheExclude\n     * @see schemaCache\n     */\n    public $enableSchemaCache = false;\n    /**\n     * @var int number of seconds that table metadata can remain valid in cache.\n     * Use 0 to indicate that the cached data will never expire.\n     * @see enableSchemaCache\n     */\n    public $schemaCacheDuration = 3600;\n    /**\n     * @var array list of tables whose metadata should NOT be cached. Defaults to empty array.\n     * The table names may contain schema prefix, if any. Do not quote the table names.\n     * @see enableSchemaCache\n     */\n    public $schemaCacheExclude = [];\n    /**\n     * @var CacheInterface|string the cache object or the ID of the cache application component that\n     * is used to cache the table metadata.\n     * @see enableSchemaCache\n     */\n    public $schemaCache = 'cache';\n    /**\n     * @var bool whether to enable query caching.\n     * Note that in order to enable query caching, a valid cache component as specified\n     * by [[queryCache]] must be enabled and [[enableQueryCache]] must be set true.\n     * Also, only the results of the queries enclosed within [[cache()]] will be cached.\n     * @see queryCache\n     * @see cache()\n     * @see noCache()\n     */\n    public $enableQueryCache = true;\n    /**\n     * @var int the default number of seconds that query results can remain valid in cache.\n     * Defaults to 3600, meaning 3600 seconds, or one hour. Use 0 to indicate that the cached data will never expire.\n     * The value of this property will be used when [[cache()]] is called without a cache duration.\n     * @see enableQueryCache\n     * @see cache()\n     */\n    public $queryCacheDuration = 3600;\n    /**\n     * @var CacheInterface|string the cache object or the ID of the cache application component\n     * that is used for query caching.\n     * @see enableQueryCache\n     */\n    public $queryCache = 'cache';\n    /**\n     * @var string|null the charset used for database connection. The property is only used\n     * for MySQL, PostgreSQL and CUBRID databases. Defaults to null, meaning using default charset\n     * as configured by the database.\n     *\n     * For Oracle Database, the charset must be specified in the [[dsn]], for example for UTF-8 by appending `;charset=UTF-8`\n     * to the DSN string.\n     *\n     * The same applies for if you're using GBK or BIG5 charset with MySQL, then it's highly recommended to\n     * specify charset via [[dsn]] like `'mysql:dbname=mydatabase;host=127.0.0.1;charset=GBK;'`.\n     */\n    public $charset;\n    /**\n     * @var bool|null whether to turn on prepare emulation. Defaults to false, meaning PDO\n     * will use the native prepare support if available. For some databases (such as MySQL),\n     * this may need to be set true so that PDO can emulate the prepare support to bypass\n     * the buggy native prepare support.\n     * The default value is null, which means the PDO ATTR_EMULATE_PREPARES value will not be changed.\n     */\n    public $emulatePrepare;\n    /**\n     * @var string the common prefix or suffix for table names. If a table name is given\n     * as `{{%TableName}}`, then the percentage character `%` will be replaced with this\n     * property value. For example, `{{%post}}` becomes `{{tbl_post}}`.\n     */\n    public $tablePrefix = '';\n    /**\n     * @var array mapping between PDO driver names and [[Schema]] classes.\n     * The keys of the array are PDO driver names while the values are either the corresponding\n     * schema class names or configurations. Please refer to [[Yii::createObject()]] for\n     * details on how to specify a configuration.\n     *\n     * This property is mainly used by [[getSchema()]] when fetching the database schema information.\n     * You normally do not need to set this property unless you want to use your own\n     * [[Schema]] class to support DBMS that is not supported by Yii.\n     */\n    public $schemaMap = [\n        'pgsql' => 'yii\\db\\pgsql\\Schema', // PostgreSQL\n        'mysqli' => 'yii\\db\\mysql\\Schema', // MySQL\n        'mysql' => 'yii\\db\\mysql\\Schema', // MySQL\n        'sqlite' => 'yii\\db\\sqlite\\Schema', // sqlite 3\n        'sqlite2' => 'yii\\db\\sqlite\\Schema', // sqlite 2\n        'sqlsrv' => 'yii\\db\\mssql\\Schema', // newer MSSQL driver on MS Windows hosts\n        'oci' => 'yii\\db\\oci\\Schema', // Oracle driver\n        'mssql' => 'yii\\db\\mssql\\Schema', // older MSSQL driver on MS Windows hosts\n        'dblib' => 'yii\\db\\mssql\\Schema', // dblib drivers on GNU/Linux (and maybe other OSes) hosts\n        'cubrid' => 'yii\\db\\cubrid\\Schema', // CUBRID\n    ];\n    /**\n     * @var string|null Custom PDO wrapper class. If not set, it will use [[PDO]] or [[\\yii\\db\\mssql\\PDO]] when MSSQL is used.\n     * @see pdo\n     */\n    public $pdoClass;\n    /**\n     * @var string the class used to create new database [[Command]] objects. If you want to extend the [[Command]] class,\n     * you may configure this property to use your extended version of the class.\n     * Since version 2.0.14 [[$commandMap]] is used if this property is set to its default value.\n     * @see createCommand\n     * @since 2.0.7\n     * @deprecated since 2.0.14. Use [[$commandMap]] for precise configuration.\n     */\n    public $commandClass = 'yii\\db\\Command';\n    /**\n     * @var array mapping between PDO driver names and [[Command]] classes.\n     * The keys of the array are PDO driver names while the values are either the corresponding\n     * command class names or configurations. Please refer to [[Yii::createObject()]] for\n     * details on how to specify a configuration.\n     *\n     * This property is mainly used by [[createCommand()]] to create new database [[Command]] objects.\n     * You normally do not need to set this property unless you want to use your own\n     * [[Command]] class or support DBMS that is not supported by Yii.\n     * @since 2.0.14\n     */\n    public $commandMap = [\n        'pgsql' => 'yii\\db\\Command', // PostgreSQL\n        'mysqli' => 'yii\\db\\Command', // MySQL\n        'mysql' => 'yii\\db\\Command', // MySQL\n        'sqlite' => 'yii\\db\\sqlite\\Command', // sqlite 3\n        'sqlite2' => 'yii\\db\\sqlite\\Command', // sqlite 2\n        'sqlsrv' => 'yii\\db\\Command', // newer MSSQL driver on MS Windows hosts\n        'oci' => 'yii\\db\\oci\\Command', // Oracle driver\n        'mssql' => 'yii\\db\\Command', // older MSSQL driver on MS Windows hosts\n        'dblib' => 'yii\\db\\Command', // dblib drivers on GNU/Linux (and maybe other OSes) hosts\n        'cubrid' => 'yii\\db\\Command', // CUBRID\n    ];\n    /**\n     * @var bool whether to enable [savepoint](https://en.wikipedia.org/wiki/Savepoint).\n     * Note that if the underlying DBMS does not support savepoint, setting this property to be true will have no effect.\n     */\n    public $enableSavepoint = true;\n    /**\n     * @var CacheInterface|string|false the cache object or the ID of the cache application component that is used to store\n     * the health status of the DB servers specified in [[masters]] and [[slaves]].\n     * This is used only when read/write splitting is enabled or [[masters]] is not empty.\n     * Set boolean `false` to disabled server status caching.\n     * @see openFromPoolSequentially() for details about the failover behavior.\n     * @see serverRetryInterval\n     */\n    public $serverStatusCache = 'cache';\n    /**\n     * @var int the retry interval in seconds for dead servers listed in [[masters]] and [[slaves]].\n     * This is used together with [[serverStatusCache]].\n     */\n    public $serverRetryInterval = 600;\n    /**\n     * @var bool whether to enable read/write splitting by using [[slaves]] to read data.\n     * Note that if [[slaves]] is empty, read/write splitting will NOT be enabled no matter what value this property takes.\n     */\n    public $enableSlaves = true;\n    /**\n     * @var array list of slave connection configurations. Each configuration is used to create a slave DB connection.\n     * When [[enableSlaves]] is true, one of these configurations will be chosen and used to create a DB connection\n     * for performing read queries only.\n     * @see enableSlaves\n     * @see slaveConfig\n     */\n    public $slaves = [];\n    /**\n     * @var array the configuration that should be merged with every slave configuration listed in [[slaves]].\n     * For example,\n     *\n     * ```\n     * [\n     *     'username' => 'slave',\n     *     'password' => 'slave',\n     *     'attributes' => [\n     *         // use a smaller connection timeout\n     *         PDO::ATTR_TIMEOUT => 10,\n     *     ],\n     * ]\n     * ```\n     */\n    public $slaveConfig = [];\n    /**\n     * @var array list of master connection configurations. Each configuration is used to create a master DB connection.\n     * When [[open()]] is called, one of these configurations will be chosen and used to create a DB connection\n     * which will be used by this object.\n     * Note that when this property is not empty, the connection setting (e.g. \"dsn\", \"username\") of this object will\n     * be ignored.\n     * @see masterConfig\n     * @see shuffleMasters\n     */\n    public $masters = [];\n    /**\n     * @var array the configuration that should be merged with every master configuration listed in [[masters]].\n     * For example,\n     *\n     * ```\n     * [\n     *     'username' => 'master',\n     *     'password' => 'master',\n     *     'attributes' => [\n     *         // use a smaller connection timeout\n     *         PDO::ATTR_TIMEOUT => 10,\n     *     ],\n     * ]\n     * ```\n     */\n    public $masterConfig = [];\n    /**\n     * @var bool whether to shuffle [[masters]] before getting one.\n     * @since 2.0.11\n     * @see masters\n     */\n    public $shuffleMasters = true;\n    /**\n     * @var bool whether to enable logging of database queries. Defaults to true.\n     * You may want to disable this option in a production environment to gain performance\n     * if you do not need the information being logged.\n     * @since 2.0.12\n     * @see enableProfiling\n     */\n    public $enableLogging = true;\n    /**\n     * @var bool whether to enable profiling of opening database connection and database queries. Defaults to true.\n     * You may want to disable this option in a production environment to gain performance\n     * if you do not need the information being logged.\n     * @since 2.0.12\n     * @see enableLogging\n     */\n    public $enableProfiling = true;\n    /**\n     * @var bool If the database connected via pdo_dblib is SyBase.\n     * @since 2.0.38\n     */\n    public $isSybase = false;\n\n    /**\n     * @var array An array of [[setQueryBuilder()]] calls, holding the passed arguments.\n     * Is used to restore a QueryBuilder configuration after the connection close/open cycle.\n     *\n     * @see restoreQueryBuilderConfiguration()\n     */\n    private $_queryBuilderConfigurations = [];\n    /**\n     * @var Transaction|null the currently active transaction\n     */\n    private $_transaction;\n    /**\n     * @var Schema|null the database schema\n     */\n    private $_schema;\n    /**\n     * @var string|null driver name\n     */\n    private $_driverName;\n    /**\n     * @var Connection|false the currently active master connection\n     */\n    private $_master = false;\n    /**\n     * @var Connection|false the currently active slave connection\n     */\n    private $_slave = false;\n    /**\n     * @var array query cache parameters for the [[cache()]] calls\n     */\n    private $_queryCacheInfo = [];\n    /**\n     * @var string[]|null quoted table name cache for [[quoteTableName()]] calls\n     */\n    private $_quotedTableNames;\n    /**\n     * @var string[]|null quoted column name cache for [[quoteColumnName()]] calls\n     */\n    private $_quotedColumnNames;\n\n\n    /**\n     * Returns a value indicating whether the DB connection is established.\n     * @return bool whether the DB connection is established\n     */\n    public function getIsActive()\n    {\n        return $this->pdo !== null;\n    }\n\n    /**\n     * Uses query cache for the queries performed with the callable.\n     *\n     * When query caching is enabled ([[enableQueryCache]] is true and [[queryCache]] refers to a valid cache),\n     * queries performed within the callable will be cached and their results will be fetched from cache if available.\n     * For example,\n     *\n     * ```\n     * // The customer will be fetched from cache if available.\n     * // If not, the query will be made against DB and cached for use next time.\n     * $customer = $db->cache(function (Connection $db) {\n     *     return $db->createCommand('SELECT * FROM customer WHERE id=1')->queryOne();\n     * });\n     * ```\n     *\n     * Note that query cache is only meaningful for queries that return results. For queries performed with\n     * [[Command::execute()]], query cache will not be used.\n     *\n     * @param callable $callable a PHP callable that contains DB queries which will make use of query cache.\n     * The signature of the callable is `function (Connection $db)`.\n     * @param int|null $duration the number of seconds that query results can remain valid in the cache. If this is\n     * not set, the value of [[queryCacheDuration]] will be used instead.\n     * Use 0 to indicate that the cached data will never expire.\n     * @param \\yii\\caching\\Dependency|null $dependency the cache dependency associated with the cached query results.\n     * @return mixed the return result of the callable\n     * @throws \\Throwable if there is any exception during query\n     * @see enableQueryCache\n     * @see queryCache\n     * @see noCache()\n     */\n    public function cache(callable $callable, $duration = null, $dependency = null)\n    {\n        $this->_queryCacheInfo[] = [$duration === null ? $this->queryCacheDuration : $duration, $dependency];\n        try {\n            $result = call_user_func($callable, $this);\n            array_pop($this->_queryCacheInfo);\n            return $result;\n        } catch (\\Exception $e) {\n            array_pop($this->_queryCacheInfo);\n            throw $e;\n        } catch (\\Throwable $e) {\n            array_pop($this->_queryCacheInfo);\n            throw $e;\n        }\n    }\n\n    /**\n     * Disables query cache temporarily.\n     *\n     * Queries performed within the callable will not use query cache at all. For example,\n     *\n     * ```\n     * $db->cache(function (Connection $db) {\n     *\n     *     // ... queries that use query cache ...\n     *\n     *     return $db->noCache(function (Connection $db) {\n     *         // this query will not use query cache\n     *         return $db->createCommand('SELECT * FROM customer WHERE id=1')->queryOne();\n     *     });\n     * });\n     * ```\n     *\n     * @param callable $callable a PHP callable that contains DB queries which should not use query cache.\n     * The signature of the callable is `function (Connection $db)`.\n     * @return mixed the return result of the callable\n     * @throws \\Throwable if there is any exception during query\n     * @see enableQueryCache\n     * @see queryCache\n     * @see cache()\n     */\n    public function noCache(callable $callable)\n    {\n        $this->_queryCacheInfo[] = false;\n        try {\n            $result = call_user_func($callable, $this);\n            array_pop($this->_queryCacheInfo);\n            return $result;\n        } catch (\\Exception $e) {\n            array_pop($this->_queryCacheInfo);\n            throw $e;\n        } catch (\\Throwable $e) {\n            array_pop($this->_queryCacheInfo);\n            throw $e;\n        }\n    }\n\n    /**\n     * Returns the current query cache information.\n     * This method is used internally by [[Command]].\n     * @param int|null $duration the preferred caching duration. If null, it will be ignored.\n     * @param \\yii\\caching\\Dependency|null $dependency the preferred caching dependency. If null, it will be ignored.\n     * @return array|null the current query cache information, or null if query cache is not enabled.\n     * @internal\n     */\n    public function getQueryCacheInfo($duration, $dependency)\n    {\n        if (!$this->enableQueryCache) {\n            return null;\n        }\n\n        $info = end($this->_queryCacheInfo);\n        if (is_array($info)) {\n            if ($duration === null) {\n                $duration = $info[0];\n            }\n            if ($dependency === null) {\n                $dependency = $info[1];\n            }\n        }\n\n        if ($duration === 0 || $duration > 0) {\n            if (is_string($this->queryCache) && Yii::$app) {\n                $cache = Yii::$app->get($this->queryCache, false);\n            } else {\n                $cache = $this->queryCache;\n            }\n            if ($cache instanceof CacheInterface) {\n                return [$cache, $duration, $dependency];\n            }\n        }\n\n        return null;\n    }\n\n    /**\n     * Establishes a DB connection.\n     * It does nothing if a DB connection has already been established.\n     * @throws Exception if connection fails\n     */\n    public function open()\n    {\n        if ($this->pdo !== null) {\n            return;\n        }\n\n        if (!empty($this->masters)) {\n            $db = $this->getMaster();\n            if ($db !== null) {\n                $this->pdo = $db->pdo;\n                return;\n            }\n\n            throw new InvalidConfigException('None of the master DB servers is available.');\n        }\n\n        if (empty($this->dsn)) {\n            throw new InvalidConfigException('Connection::dsn cannot be empty.');\n        }\n\n        $token = 'Opening DB connection: ' . $this->dsn;\n        $enableProfiling = $this->enableProfiling;\n        try {\n            if ($this->enableLogging) {\n                Yii::info($token, __METHOD__);\n            }\n\n            if ($enableProfiling) {\n                Yii::beginProfile($token, __METHOD__);\n            }\n\n            $this->pdo = $this->createPdoInstance();\n            $this->initConnection();\n\n            if ($enableProfiling) {\n                Yii::endProfile($token, __METHOD__);\n            }\n        } catch (\\PDOException $e) {\n            if ($enableProfiling) {\n                Yii::endProfile($token, __METHOD__);\n            }\n\n            throw new Exception($e->getMessage(), $e->errorInfo, $e->getCode(), $e);\n        }\n    }\n\n    /**\n     * Closes the currently active DB connection.\n     * It does nothing if the connection is already closed.\n     */\n    public function close()\n    {\n        if ($this->_master) {\n            if ($this->pdo === $this->_master->pdo) {\n                $this->pdo = null;\n            }\n\n            $this->_master->close();\n            $this->_master = false;\n        }\n\n        if ($this->pdo !== null) {\n            Yii::debug('Closing DB connection: ' . $this->dsn, __METHOD__);\n            $this->pdo = null;\n        }\n\n        if ($this->_slave) {\n            $this->_slave->close();\n            $this->_slave = false;\n        }\n\n        $this->_schema = null;\n        $this->_transaction = null;\n        $this->_driverName = null;\n        $this->_queryCacheInfo = [];\n        $this->_quotedTableNames = null;\n        $this->_quotedColumnNames = null;\n    }\n\n    /**\n     * Creates the PDO instance.\n     * This method is called by [[open]] to establish a DB connection.\n     * The default implementation will create a PHP PDO instance.\n     * You may override this method if the default PDO needs to be adapted for certain DBMS.\n     * @return PDO the pdo instance\n     */\n    protected function createPdoInstance()\n    {\n        $pdoClass = $this->pdoClass;\n        if ($pdoClass === null) {\n            $driver = null;\n            if ($this->_driverName !== null) {\n                $driver = $this->_driverName;\n            } elseif (($pos = strpos($this->dsn, ':')) !== false) {\n                $driver = strtolower(substr($this->dsn, 0, $pos));\n            }\n            switch ($driver) {\n                case 'mssql':\n                    $pdoClass = 'yii\\db\\mssql\\PDO';\n                    break;\n                case 'dblib':\n                    $pdoClass = 'yii\\db\\mssql\\DBLibPDO';\n                    break;\n                case 'sqlsrv':\n                    $pdoClass = 'yii\\db\\mssql\\SqlsrvPDO';\n                    break;\n                default:\n                    $pdoClass = 'PDO';\n            }\n        }\n\n        $dsn = $this->dsn;\n        if (strncmp('sqlite:@', $dsn, 8) === 0) {\n            $dsn = 'sqlite:' . Yii::getAlias(substr($dsn, 7));\n        }\n\n        return new $pdoClass($dsn, $this->username, $this->password, $this->attributes);\n    }\n\n    /**\n     * Initializes the DB connection.\n     * This method is invoked right after the DB connection is established.\n     * The default implementation turns on `PDO::ATTR_EMULATE_PREPARES`\n     * if [[emulatePrepare]] is true, and sets the database [[charset]] if it is not empty.\n     * It then triggers an [[EVENT_AFTER_OPEN]] event.\n     */\n    protected function initConnection()\n    {\n        $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);\n        if ($this->emulatePrepare !== null && constant('PDO::ATTR_EMULATE_PREPARES')) {\n            if ($this->driverName !== 'sqlsrv') {\n                $this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, $this->emulatePrepare);\n            }\n        }\n\n        if (PHP_VERSION_ID >= 80100 && $this->getDriverName() === 'sqlite') {\n            $this->pdo->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true);\n        }\n\n        if (!$this->isSybase && in_array($this->getDriverName(), ['mssql', 'dblib'], true)) {\n            $this->pdo->exec('SET ANSI_NULL_DFLT_ON ON');\n        }\n        if ($this->charset !== null && in_array($this->getDriverName(), ['pgsql', 'mysql', 'mysqli', 'cubrid'], true)) {\n            $this->pdo->exec('SET NAMES ' . $this->pdo->quote($this->charset));\n        }\n        $this->trigger(self::EVENT_AFTER_OPEN);\n    }\n\n    /**\n     * Creates a command for execution.\n     * @param string|null $sql the SQL statement to be executed\n     * @param array $params the parameters to be bound to the SQL statement\n     * @return Command the DB command\n     */\n    public function createCommand($sql = null, $params = [])\n    {\n        $driver = $this->getDriverName();\n        $config = ['class' => 'yii\\db\\Command'];\n        if ($this->commandClass !== $config['class']) {\n            $config['class'] = $this->commandClass;\n        } elseif (isset($this->commandMap[$driver])) {\n            $config = !is_array($this->commandMap[$driver]) ? ['class' => $this->commandMap[$driver]] : $this->commandMap[$driver];\n        }\n        $config['db'] = $this;\n        $config['sql'] = $sql;\n        /** @var Command $command */\n        $command = Yii::createObject($config);\n        return $command->bindValues($params);\n    }\n\n    /**\n     * Returns the currently active transaction.\n     * @return Transaction|null the currently active transaction. Null if no active transaction.\n     */\n    public function getTransaction()\n    {\n        return $this->_transaction && $this->_transaction->getIsActive() ? $this->_transaction : null;\n    }\n\n    /**\n     * Starts a transaction.\n     * @param string|null $isolationLevel The isolation level to use for this transaction.\n     * See [[Transaction::begin()]] for details.\n     * @return Transaction the transaction initiated\n     */\n    public function beginTransaction($isolationLevel = null)\n    {\n        $this->open();\n\n        if (($transaction = $this->getTransaction()) === null) {\n            $transaction = $this->_transaction = new Transaction(['db' => $this]);\n        }\n        $transaction->begin($isolationLevel);\n\n        return $transaction;\n    }\n\n    /**\n     * Executes callback provided in a transaction.\n     *\n     * @param callable $callback a valid PHP callback that performs the job. Accepts connection instance as parameter.\n     * @param string|null $isolationLevel The isolation level to use for this transaction.\n     * See [[Transaction::begin()]] for details.\n     * @throws \\Throwable if there is any exception during query. In this case the transaction will be rolled back.\n     * @return mixed result of callback function\n     */\n    public function transaction(callable $callback, $isolationLevel = null)\n    {\n        $transaction = $this->beginTransaction($isolationLevel);\n        $level = $transaction->level;\n\n        try {\n            $result = call_user_func($callback, $this);\n            if ($transaction->isActive && $transaction->level === $level) {\n                $transaction->commit();\n            }\n        } catch (\\Exception $e) {\n            $this->rollbackTransactionOnLevel($transaction, $level);\n            throw $e;\n        } catch (\\Throwable $e) {\n            $this->rollbackTransactionOnLevel($transaction, $level);\n            throw $e;\n        }\n\n        return $result;\n    }\n\n    /**\n     * Rolls back given [[Transaction]] object if it's still active and level match.\n     * In some cases rollback can fail, so this method is fail safe. Exception thrown\n     * from rollback will be caught and just logged with [[\\Yii::error()]].\n     * @param Transaction $transaction Transaction object given from [[beginTransaction()]].\n     * @param int $level Transaction level just after [[beginTransaction()]] call.\n     */\n    private function rollbackTransactionOnLevel($transaction, $level)\n    {\n        if ($transaction->isActive && $transaction->level === $level) {\n            // https://github.com/yiisoft/yii2/pull/13347\n            try {\n                $transaction->rollBack();\n            } catch (\\Exception $e) {\n                \\Yii::error($e, __METHOD__);\n                // hide this exception to be able to continue throwing original exception outside\n            }\n        }\n    }\n\n    /**\n     * Returns the schema information for the database opened by this connection.\n     * @return Schema the schema information for the database opened by this connection.\n     * @throws NotSupportedException if there is no support for the current driver type\n     */\n    public function getSchema()\n    {\n        if ($this->_schema !== null) {\n            return $this->_schema;\n        }\n\n        $driver = $this->getDriverName();\n        if (isset($this->schemaMap[$driver])) {\n            $config = !is_array($this->schemaMap[$driver]) ? ['class' => $this->schemaMap[$driver]] : $this->schemaMap[$driver];\n            $config['db'] = $this;\n\n            $this->_schema = Yii::createObject($config);\n            $this->restoreQueryBuilderConfiguration();\n\n            return $this->_schema;\n        }\n\n        throw new NotSupportedException(\"Connection does not support reading schema information for '$driver' DBMS.\");\n    }\n\n    /**\n     * Returns the query builder for the current DB connection.\n     * @return QueryBuilder the query builder for the current DB connection.\n     */\n    public function getQueryBuilder()\n    {\n        return $this->getSchema()->getQueryBuilder();\n    }\n\n    /**\n     * Can be used to set [[QueryBuilder]] configuration via Connection configuration array.\n     *\n     * @param array $value the [[QueryBuilder]] properties to be configured.\n     * @since 2.0.14\n     */\n    public function setQueryBuilder($value)\n    {\n        Yii::configure($this->getQueryBuilder(), $value);\n        $this->_queryBuilderConfigurations[] = $value;\n    }\n\n    /**\n     * Restores custom QueryBuilder configuration after the connection close/open cycle\n     */\n    private function restoreQueryBuilderConfiguration()\n    {\n        if ($this->_queryBuilderConfigurations === []) {\n            return;\n        }\n\n        $queryBuilderConfigurations = $this->_queryBuilderConfigurations;\n        $this->_queryBuilderConfigurations = [];\n        foreach ($queryBuilderConfigurations as $queryBuilderConfiguration) {\n            $this->setQueryBuilder($queryBuilderConfiguration);\n        }\n    }\n\n    /**\n     * Obtains the schema information for the named table.\n     * @param string $name table name.\n     * @param bool $refresh whether to reload the table schema even if it is found in the cache.\n     * @return TableSchema|null table schema information. Null if the named table does not exist.\n     */\n    public function getTableSchema($name, $refresh = false)\n    {\n        return $this->getSchema()->getTableSchema($name, $refresh);\n    }\n\n    /**\n     * Returns the ID of the last inserted row or sequence value.\n     * @param string $sequenceName name of the sequence object (required by some DBMS)\n     * @return string the row ID of the last row inserted, or the last value retrieved from the sequence object\n     * @see https://www.php.net/manual/en/pdo.lastinsertid.php\n     */\n    public function getLastInsertID($sequenceName = '')\n    {\n        return $this->getSchema()->getLastInsertID($sequenceName);\n    }\n\n    /**\n     * Quotes a string value for use in a query.\n     * Note that if the parameter is not a string, it will be returned without change.\n     * @param string $value string to be quoted\n     * @return string the properly quoted string\n     * @see https://www.php.net/manual/en/pdo.quote.php\n     */\n    public function quoteValue($value)\n    {\n        return $this->getSchema()->quoteValue($value);\n    }\n\n    /**\n     * Quotes a table name for use in a query.\n     * If the table name contains schema prefix, the prefix will also be properly quoted.\n     * If the table name is already quoted or contains special characters including '(', '[[' and '{{',\n     * then this method will do nothing.\n     * @param string $name table name\n     * @return string the properly quoted table name\n     */\n    public function quoteTableName($name)\n    {\n        if (isset($this->_quotedTableNames[$name])) {\n            return $this->_quotedTableNames[$name];\n        }\n        return $this->_quotedTableNames[$name] = $this->getSchema()->quoteTableName($name);\n    }\n\n    /**\n     * Quotes a column name for use in a query.\n     * If the column name contains prefix, the prefix will also be properly quoted.\n     * If the column name is already quoted or contains special characters including '(', '[[' and '{{',\n     * then this method will do nothing.\n     * @param string $name column name\n     * @return string the properly quoted column name\n     */\n    public function quoteColumnName($name)\n    {\n        if (isset($this->_quotedColumnNames[$name])) {\n            return $this->_quotedColumnNames[$name];\n        }\n        return $this->_quotedColumnNames[$name] = $this->getSchema()->quoteColumnName($name);\n    }\n\n    /**\n     * Processes a SQL statement by quoting table and column names that are enclosed within double brackets.\n     * Tokens enclosed within double curly brackets are treated as table names, while\n     * tokens enclosed within double square brackets are column names. They will be quoted accordingly.\n     * Also, the percentage character \"%\" at the beginning or ending of a table name will be replaced\n     * with [[tablePrefix]].\n     * @param string $sql the SQL to be quoted\n     * @return string the quoted SQL\n     */\n    public function quoteSql($sql)\n    {\n        return preg_replace_callback(\n            '/(\\\\{\\\\{(%?[\\w\\-\\. ]+%?)\\\\}\\\\}|\\\\[\\\\[([\\w\\-\\. ]+)\\\\]\\\\])/',\n            function ($matches) {\n                if (isset($matches[3])) {\n                    return $this->quoteColumnName($matches[3]);\n                }\n\n                return str_replace('%', $this->tablePrefix, $this->quoteTableName($matches[2]));\n            },\n            $sql\n        );\n    }\n\n    /**\n     * Returns the name of the DB driver. Based on the the current [[dsn]], in case it was not set explicitly\n     * by an end user.\n     * @return string|null name of the DB driver\n     */\n    public function getDriverName()\n    {\n        if ($this->_driverName === null) {\n            if (($pos = strpos((string)$this->dsn, ':')) !== false) {\n                $this->_driverName = strtolower(substr($this->dsn, 0, $pos));\n            } else {\n                $this->_driverName = strtolower($this->getSlavePdo(true)->getAttribute(PDO::ATTR_DRIVER_NAME));\n            }\n        }\n\n        return $this->_driverName;\n    }\n\n    /**\n     * Changes the current driver name.\n     * @param string $driverName name of the DB driver\n     */\n    public function setDriverName($driverName)\n    {\n        $this->_driverName = strtolower($driverName);\n    }\n\n    /**\n     * Returns a server version as a string comparable by [[\\version_compare()]].\n     * @return string server version as a string.\n     * @since 2.0.14\n     */\n    public function getServerVersion()\n    {\n        return $this->getSchema()->getServerVersion();\n    }\n\n    /**\n     * Returns the PDO instance for the currently active slave connection.\n     * When [[enableSlaves]] is true, one of the slaves will be used for read queries, and its PDO instance\n     * will be returned by this method.\n     * @param bool $fallbackToMaster whether to return a master PDO in case none of the slave connections is available.\n     * @return PDO|null the PDO instance for the currently active slave connection. `null` is returned if no slave connection\n     * is available and `$fallbackToMaster` is false.\n     */\n    public function getSlavePdo($fallbackToMaster = true)\n    {\n        $db = $this->getSlave(false);\n        if ($db === null) {\n            return $fallbackToMaster ? $this->getMasterPdo() : null;\n        }\n\n        return $db->pdo;\n    }\n\n    /**\n     * Returns the PDO instance for the currently active master connection.\n     * This method will open the master DB connection and then return [[pdo]].\n     * @return PDO the PDO instance for the currently active master connection.\n     */\n    public function getMasterPdo()\n    {\n        $this->open();\n        return $this->pdo;\n    }\n\n    /**\n     * Returns the currently active slave connection.\n     * If this method is called for the first time, it will try to open a slave connection when [[enableSlaves]] is true.\n     * @param bool $fallbackToMaster whether to return a master connection in case there is no slave connection available.\n     * @return Connection|null the currently active slave connection. `null` is returned if there is no slave available and\n     * `$fallbackToMaster` is false.\n     */\n    public function getSlave($fallbackToMaster = true)\n    {\n        if (!$this->enableSlaves) {\n            return $fallbackToMaster ? $this : null;\n        }\n\n        if ($this->_slave === false) {\n            $this->_slave = $this->openFromPool($this->slaves, $this->slaveConfig);\n        }\n\n        return $this->_slave === null && $fallbackToMaster ? $this : $this->_slave;\n    }\n\n    /**\n     * Returns the currently active master connection.\n     * If this method is called for the first time, it will try to open a master connection.\n     * @return Connection|null the currently active master connection. `null` is returned if there is no master available.\n     * @since 2.0.11\n     */\n    public function getMaster()\n    {\n        if ($this->_master === false) {\n            $this->_master = $this->shuffleMasters\n                ? $this->openFromPool($this->masters, $this->masterConfig)\n                : $this->openFromPoolSequentially($this->masters, $this->masterConfig);\n        }\n\n        return $this->_master;\n    }\n\n    /**\n     * Executes the provided callback by using the master connection.\n     *\n     * This method is provided so that you can temporarily force using the master connection to perform\n     * DB operations even if they are read queries. For example,\n     *\n     * ```\n     * $result = $db->useMaster(function ($db) {\n     *     return $db->createCommand('SELECT * FROM user LIMIT 1')->queryOne();\n     * });\n     * ```\n     *\n     * @param callable $callback a PHP callable to be executed by this method. Its signature is\n     * `function (Connection $db)`. Its return value will be returned by this method.\n     * @return mixed the return value of the callback\n     * @throws \\Throwable if there is any exception thrown from the callback\n     */\n    public function useMaster(callable $callback)\n    {\n        if ($this->enableSlaves) {\n            $this->enableSlaves = false;\n            try {\n                $result = call_user_func($callback, $this);\n            } catch (\\Exception $e) {\n                $this->enableSlaves = true;\n                throw $e;\n            } catch (\\Throwable $e) {\n                $this->enableSlaves = true;\n                throw $e;\n            }\n            // TODO: use \"finally\" keyword when miminum required PHP version is >= 5.5\n            $this->enableSlaves = true;\n        } else {\n            $result = call_user_func($callback, $this);\n        }\n\n        return $result;\n    }\n\n    /**\n     * Opens the connection to a server in the pool.\n     *\n     * This method implements load balancing and failover among the given list of the servers.\n     * Connections will be tried in random order.\n     * For details about the failover behavior, see [[openFromPoolSequentially]].\n     *\n     * @param array $pool the list of connection configurations in the server pool\n     * @param array $sharedConfig the configuration common to those given in `$pool`.\n     * @return Connection|null the opened DB connection, or `null` if no server is available\n     * @throws InvalidConfigException if a configuration does not specify \"dsn\"\n     * @see openFromPoolSequentially\n     */\n    protected function openFromPool(array $pool, array $sharedConfig)\n    {\n        shuffle($pool);\n        return $this->openFromPoolSequentially($pool, $sharedConfig);\n    }\n\n    /**\n     * Opens the connection to a server in the pool.\n     *\n     * This method implements failover among the given list of servers.\n     * Connections will be tried in sequential order. The first successful connection will return.\n     *\n     * If [[serverStatusCache]] is configured, this method will cache information about\n     * unreachable servers and does not try to connect to these for the time configured in [[serverRetryInterval]].\n     * This helps to keep the application stable when some servers are unavailable. Avoiding\n     * connection attempts to unavailable servers saves time when the connection attempts fail due to timeout.\n     *\n     * If none of the servers are available the status cache is ignored and connection attempts are made to all\n     * servers (Since version 2.0.35). This is to avoid downtime when all servers are unavailable for a short time.\n     * After a successful connection attempt the server is marked as available again.\n     *\n     * @param array $pool the list of connection configurations in the server pool\n     * @param array $sharedConfig the configuration common to those given in `$pool`.\n     * @return Connection|null the opened DB connection, or `null` if no server is available\n     * @throws InvalidConfigException if a configuration does not specify \"dsn\"\n     * @since 2.0.11\n     * @see openFromPool\n     * @see serverStatusCache\n     */\n    protected function openFromPoolSequentially(array $pool, array $sharedConfig)\n    {\n        if (empty($pool)) {\n            return null;\n        }\n\n        if (!isset($sharedConfig['class'])) {\n            $sharedConfig['class'] = get_class($this);\n        }\n\n        $cache = is_string($this->serverStatusCache) ? Yii::$app->get($this->serverStatusCache, false) : $this->serverStatusCache;\n\n        foreach ($pool as $i => $config) {\n            $pool[$i] = $config = array_merge($sharedConfig, $config);\n            if (empty($config['dsn'])) {\n                throw new InvalidConfigException('The \"dsn\" option must be specified.');\n            }\n\n            $key = [__METHOD__, $config['dsn']];\n            if ($cache instanceof CacheInterface && $cache->get($key)) {\n                // should not try this dead server now\n                continue;\n            }\n\n            /** @var self $db */\n            $db = Yii::createObject($config);\n\n            try {\n                $db->open();\n                return $db;\n            } catch (\\Exception $e) {\n                Yii::warning(\"Connection ({$config['dsn']}) failed: \" . $e->getMessage(), __METHOD__);\n                if ($cache instanceof CacheInterface) {\n                    // mark this server as dead and only retry it after the specified interval\n                    $cache->set($key, 1, $this->serverRetryInterval);\n                }\n                // exclude server from retry below\n                unset($pool[$i]);\n            }\n        }\n\n        if ($cache instanceof CacheInterface) {\n            // if server status cache is enabled and no server is available\n            // ignore the cache and try to connect anyway\n            // $pool now only contains servers we did not already try in the loop above\n            foreach ($pool as $config) {\n                /** @var self $db */\n                $db = Yii::createObject($config);\n                try {\n                    $db->open();\n                } catch (\\Exception $e) {\n                    Yii::warning(\"Connection ({$config['dsn']}) failed: \" . $e->getMessage(), __METHOD__);\n                    continue;\n                }\n\n                // mark this server as available again after successful connection\n                $cache->delete([__METHOD__, $config['dsn']]);\n\n                return $db;\n            }\n        }\n\n        return null;\n    }\n\n    /**\n     * Close the connection before serializing.\n     * @return array\n     */\n    public function __sleep()\n    {\n        $fields = (array) $this;\n\n        unset($fields['pdo']);\n        unset($fields[\"\\000\" . __CLASS__ . \"\\000\" . '_master']);\n        unset($fields[\"\\000\" . __CLASS__ . \"\\000\" . '_slave']);\n        unset($fields[\"\\000\" . __CLASS__ . \"\\000\" . '_transaction']);\n        unset($fields[\"\\000\" . __CLASS__ . \"\\000\" . '_schema']);\n\n        return array_keys($fields);\n    }\n\n    /**\n     * Reset the connection after cloning.\n     */\n    public function __clone()\n    {\n        parent::__clone();\n\n        $this->_master = false;\n        $this->_slave = false;\n        $this->_schema = null;\n        $this->_transaction = null;\n        if (strncmp($this->dsn, 'sqlite::memory:', 15) !== 0) {\n            // reset PDO connection, unless its sqlite in-memory, which can only have one connection\n            $this->pdo = null;\n        }\n    }\n}\n"
  },
  {
    "path": "framework/db/Constraint.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\nuse yii\\base\\BaseObject;\n\n/**\n * Constraint represents the metadata of a table constraint.\n *\n * @author Sergey Makinen <sergey@makinen.ru>\n * @since 2.0.13\n */\nclass Constraint extends BaseObject\n{\n    /**\n     * @var string[]|null list of column names the constraint belongs to.\n     */\n    public $columnNames;\n    /**\n     * @var string|null the constraint name.\n     */\n    public $name;\n}\n"
  },
  {
    "path": "framework/db/ConstraintFinderInterface.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\n/**\n * ConstraintFinderInterface defines methods for getting a table constraint information.\n *\n * @author Sergey Makinen <sergey@makinen.ru>\n * @since 2.0.14\n */\ninterface ConstraintFinderInterface\n{\n    /**\n     * Obtains the primary key for the named table.\n     * @param string $name table name. The table name may contain schema name if any. Do not quote the table name.\n     * @param bool $refresh whether to reload the information even if it is found in the cache.\n     * @return Constraint|null table primary key, `null` if the table has no primary key.\n     */\n    public function getTablePrimaryKey($name, $refresh = false);\n\n    /**\n     * Returns primary keys for all tables in the database.\n     * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.\n     * @param bool $refresh whether to fetch the latest available table schemas. If this is `false`,\n     * cached data may be returned if available.\n     * @return Constraint[] primary keys for all tables in the database.\n     * Each array element is an instance of [[Constraint]] or its child class.\n     */\n    public function getSchemaPrimaryKeys($schema = '', $refresh = false);\n\n    /**\n     * Obtains the foreign keys information for the named table.\n     * @param string $name table name. The table name may contain schema name if any. Do not quote the table name.\n     * @param bool $refresh whether to reload the information even if it is found in the cache.\n     * @return ForeignKeyConstraint[] table foreign keys.\n     */\n    public function getTableForeignKeys($name, $refresh = false);\n\n    /**\n     * Returns foreign keys for all tables in the database.\n     * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.\n     * @param bool $refresh whether to fetch the latest available table schemas. If this is false,\n     * cached data may be returned if available.\n     * @return ForeignKeyConstraint[][] foreign keys for all tables in the database.\n     * Each array element is an array of [[ForeignKeyConstraint]] or its child classes.\n     */\n    public function getSchemaForeignKeys($schema = '', $refresh = false);\n\n    /**\n     * Obtains the indexes information for the named table.\n     * @param string $name table name. The table name may contain schema name if any. Do not quote the table name.\n     * @param bool $refresh whether to reload the information even if it is found in the cache.\n     * @return IndexConstraint[] table indexes.\n     */\n    public function getTableIndexes($name, $refresh = false);\n\n    /**\n     * Returns indexes for all tables in the database.\n     * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.\n     * @param bool $refresh whether to fetch the latest available table schemas. If this is false,\n     * cached data may be returned if available.\n     * @return IndexConstraint[][] indexes for all tables in the database.\n     * Each array element is an array of [[IndexConstraint]] or its child classes.\n     */\n    public function getSchemaIndexes($schema = '', $refresh = false);\n\n    /**\n     * Obtains the unique constraints information for the named table.\n     * @param string $name table name. The table name may contain schema name if any. Do not quote the table name.\n     * @param bool $refresh whether to reload the information even if it is found in the cache.\n     * @return Constraint[] table unique constraints.\n     */\n    public function getTableUniques($name, $refresh = false);\n\n    /**\n     * Returns unique constraints for all tables in the database.\n     * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.\n     * @param bool $refresh whether to fetch the latest available table schemas. If this is false,\n     * cached data may be returned if available.\n     * @return Constraint[][] unique constraints for all tables in the database.\n     * Each array element is an array of [[Constraint]] or its child classes.\n     */\n    public function getSchemaUniques($schema = '', $refresh = false);\n\n    /**\n     * Obtains the check constraints information for the named table.\n     * @param string $name table name. The table name may contain schema name if any. Do not quote the table name.\n     * @param bool $refresh whether to reload the information even if it is found in the cache.\n     * @return CheckConstraint[] table check constraints.\n     */\n    public function getTableChecks($name, $refresh = false);\n\n    /**\n     * Returns check constraints for all tables in the database.\n     * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.\n     * @param bool $refresh whether to fetch the latest available table schemas. If this is false,\n     * cached data may be returned if available.\n     * @return CheckConstraint[][] check constraints for all tables in the database.\n     * Each array element is an array of [[CheckConstraint]] or its child classes.\n     */\n    public function getSchemaChecks($schema = '', $refresh = false);\n\n    /**\n     * Obtains the default value constraints information for the named table.\n     * @param string $name table name. The table name may contain schema name if any. Do not quote the table name.\n     * @param bool $refresh whether to reload the information even if it is found in the cache.\n     * @return DefaultValueConstraint[] table default value constraints.\n     */\n    public function getTableDefaultValues($name, $refresh = false);\n\n    /**\n     * Returns default value constraints for all tables in the database.\n     * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.\n     * @param bool $refresh whether to fetch the latest available table schemas. If this is false,\n     * cached data may be returned if available.\n     * @return DefaultValueConstraint[] default value constraints for all tables in the database.\n     * Each array element is an array of [[DefaultValueConstraint]] or its child classes.\n     */\n    public function getSchemaDefaultValues($schema = '', $refresh = false);\n}\n"
  },
  {
    "path": "framework/db/ConstraintFinderTrait.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\n/**\n * ConstraintFinderTrait provides methods for getting a table constraint information.\n *\n * @property CheckConstraint[][] $schemaChecks Check constraints for all tables in the database.\n * Each array element is an array of [[CheckConstraint]] or its child classes. This property is read-only.\n * @property DefaultValueConstraint[] $schemaDefaultValues Default value constraints for all tables in the database.\n * Each array element is an array of [[DefaultValueConstraint]] or its child classes. This property is read-only.\n * @property ForeignKeyConstraint[][] $schemaForeignKeys Foreign keys for all tables in the database. Each\n * array element is an array of [[ForeignKeyConstraint]] or its child classes. This property is read-only.\n * @property IndexConstraint[][] $schemaIndexes Indexes for all tables in the database. Each array element is\n * an array of [[IndexConstraint]] or its child classes. This property is read-only.\n * @property Constraint[] $schemaPrimaryKeys Primary keys for all tables in the database. Each array element\n * is an instance of [[Constraint]] or its child class. This property is read-only.\n * @property IndexConstraint[][] $schemaUniques Unique constraints for all tables in the database.\n * Each array element is an array of [[IndexConstraint]] or its child classes. This property is read-only.\n *\n * @author Sergey Makinen <sergey@makinen.ru>\n * @since 2.0.13\n */\ntrait ConstraintFinderTrait\n{\n    /**\n     * Returns the metadata of the given type for the given table.\n     * @param string $name table name. The table name may contain schema name if any. Do not quote the table name.\n     * @param string $type metadata type.\n     * @param bool $refresh whether to reload the table metadata even if it is found in the cache.\n     * @return mixed metadata.\n     */\n    abstract protected function getTableMetadata($name, $type, $refresh);\n\n    /**\n     * Returns the metadata of the given type for all tables in the given schema.\n     * @param string $schema the schema of the metadata. Defaults to empty string, meaning the current or default schema name.\n     * @param string $type metadata type.\n     * @param bool $refresh whether to fetch the latest available table metadata. If this is `false`,\n     * cached data may be returned if available.\n     * @return array array of metadata.\n     */\n    abstract protected function getSchemaMetadata($schema, $type, $refresh);\n\n    /**\n     * Loads a primary key for the given table.\n     * @param string $tableName table name.\n     * @return Constraint|null primary key for the given table, `null` if the table has no primary key.\n     */\n    abstract protected function loadTablePrimaryKey($tableName);\n\n    /**\n     * Loads all foreign keys for the given table.\n     * @param string $tableName table name.\n     * @return ForeignKeyConstraint[] foreign keys for the given table.\n     */\n    abstract protected function loadTableForeignKeys($tableName);\n\n    /**\n     * Loads all indexes for the given table.\n     * @param string $tableName table name.\n     * @return IndexConstraint[] indexes for the given table.\n     */\n    abstract protected function loadTableIndexes($tableName);\n\n    /**\n     * Loads all unique constraints for the given table.\n     * @param string $tableName table name.\n     * @return Constraint[] unique constraints for the given table.\n     */\n    abstract protected function loadTableUniques($tableName);\n\n    /**\n     * Loads all check constraints for the given table.\n     * @param string $tableName table name.\n     * @return CheckConstraint[] check constraints for the given table.\n     */\n    abstract protected function loadTableChecks($tableName);\n\n    /**\n     * Loads all default value constraints for the given table.\n     *\n     * @param string $tableName table name.\n     * @return DefaultValueConstraint[] default value constraints for the given table.\n     */\n    abstract protected function loadTableDefaultValues($tableName);\n\n    /**\n     * Obtains the primary key for the named table.\n     * @param string $name table name. The table name may contain schema name if any. Do not quote the table name.\n     * @param bool $refresh whether to reload the information even if it is found in the cache.\n     * @return Constraint|null table primary key, `null` if the table has no primary key.\n     */\n    public function getTablePrimaryKey($name, $refresh = false)\n    {\n        return $this->getTableMetadata($name, 'primaryKey', $refresh);\n    }\n\n    /**\n     * Returns primary keys for all tables in the database.\n     * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.\n     * @param bool $refresh whether to fetch the latest available table schemas. If this is `false`,\n     * cached data may be returned if available.\n     * @return Constraint[] primary keys for all tables in the database.\n     * Each array element is an instance of [[Constraint]] or its child class.\n     */\n    public function getSchemaPrimaryKeys($schema = '', $refresh = false)\n    {\n        return $this->getSchemaMetadata($schema, 'primaryKey', $refresh);\n    }\n\n    /**\n     * Obtains the foreign keys information for the named table.\n     * @param string $name table name. The table name may contain schema name if any. Do not quote the table name.\n     * @param bool $refresh whether to reload the information even if it is found in the cache.\n     * @return ForeignKeyConstraint[] table foreign keys.\n     */\n    public function getTableForeignKeys($name, $refresh = false)\n    {\n        return $this->getTableMetadata($name, 'foreignKeys', $refresh);\n    }\n\n    /**\n     * Returns foreign keys for all tables in the database.\n     * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.\n     * @param bool $refresh whether to fetch the latest available table schemas. If this is false,\n     * cached data may be returned if available.\n     * @return ForeignKeyConstraint[][] foreign keys for all tables in the database.\n     * Each array element is an array of [[ForeignKeyConstraint]] or its child classes.\n     */\n    public function getSchemaForeignKeys($schema = '', $refresh = false)\n    {\n        return $this->getSchemaMetadata($schema, 'foreignKeys', $refresh);\n    }\n\n    /**\n     * Obtains the indexes information for the named table.\n     * @param string $name table name. The table name may contain schema name if any. Do not quote the table name.\n     * @param bool $refresh whether to reload the information even if it is found in the cache.\n     * @return IndexConstraint[] table indexes.\n     */\n    public function getTableIndexes($name, $refresh = false)\n    {\n        return $this->getTableMetadata($name, 'indexes', $refresh);\n    }\n\n    /**\n     * Returns indexes for all tables in the database.\n     * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.\n     * @param bool $refresh whether to fetch the latest available table schemas. If this is false,\n     * cached data may be returned if available.\n     * @return IndexConstraint[][] indexes for all tables in the database.\n     * Each array element is an array of [[IndexConstraint]] or its child classes.\n     */\n    public function getSchemaIndexes($schema = '', $refresh = false)\n    {\n        return $this->getSchemaMetadata($schema, 'indexes', $refresh);\n    }\n\n    /**\n     * Obtains the unique constraints information for the named table.\n     * @param string $name table name. The table name may contain schema name if any. Do not quote the table name.\n     * @param bool $refresh whether to reload the information even if it is found in the cache.\n     * @return Constraint[] table unique constraints.\n     */\n    public function getTableUniques($name, $refresh = false)\n    {\n        return $this->getTableMetadata($name, 'uniques', $refresh);\n    }\n\n    /**\n     * Returns unique constraints for all tables in the database.\n     * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.\n     * @param bool $refresh whether to fetch the latest available table schemas. If this is false,\n     * cached data may be returned if available.\n     * @return Constraint[][] unique constraints for all tables in the database.\n     * Each array element is an array of [[Constraint]] or its child classes.\n     */\n    public function getSchemaUniques($schema = '', $refresh = false)\n    {\n        return $this->getSchemaMetadata($schema, 'uniques', $refresh);\n    }\n\n    /**\n     * Obtains the check constraints information for the named table.\n     * @param string $name table name. The table name may contain schema name if any. Do not quote the table name.\n     * @param bool $refresh whether to reload the information even if it is found in the cache.\n     * @return CheckConstraint[] table check constraints.\n     */\n    public function getTableChecks($name, $refresh = false)\n    {\n        return $this->getTableMetadata($name, 'checks', $refresh);\n    }\n\n    /**\n     * Returns check constraints for all tables in the database.\n     * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.\n     * @param bool $refresh whether to fetch the latest available table schemas. If this is false,\n     * cached data may be returned if available.\n     * @return CheckConstraint[][] check constraints for all tables in the database.\n     * Each array element is an array of [[CheckConstraint]] or its child classes.\n     */\n    public function getSchemaChecks($schema = '', $refresh = false)\n    {\n        return $this->getSchemaMetadata($schema, 'checks', $refresh);\n    }\n\n    /**\n     * Obtains the default value constraints information for the named table.\n     * @param string $name table name. The table name may contain schema name if any. Do not quote the table name.\n     * @param bool $refresh whether to reload the information even if it is found in the cache.\n     * @return DefaultValueConstraint[] table default value constraints.\n     */\n    public function getTableDefaultValues($name, $refresh = false)\n    {\n        return $this->getTableMetadata($name, 'defaultValues', $refresh);\n    }\n\n    /**\n     * Returns default value constraints for all tables in the database.\n     * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.\n     * @param bool $refresh whether to fetch the latest available table schemas. If this is false,\n     * cached data may be returned if available.\n     * @return DefaultValueConstraint[] default value constraints for all tables in the database.\n     * Each array element is an array of [[DefaultValueConstraint]] or its child classes.\n     */\n    public function getSchemaDefaultValues($schema = '', $refresh = false)\n    {\n        return $this->getSchemaMetadata($schema, 'defaultValues', $refresh);\n    }\n}\n"
  },
  {
    "path": "framework/db/DataReader.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\nuse yii\\base\\InvalidCallException;\n\n/**\n * DataReader represents a forward-only stream of rows from a query result set.\n *\n * To read the current row of data, call [[read()]]. The method [[readAll()]]\n * returns all the rows in a single array. Rows of data can also be read by\n * iterating through the reader. For example,\n *\n * ```\n * $command = $connection->createCommand('SELECT * FROM post');\n * $reader = $command->query();\n *\n * while ($row = $reader->read()) {\n *     $rows[] = $row;\n * }\n *\n * // equivalent to:\n * foreach ($reader as $row) {\n *     $rows[] = $row;\n * }\n *\n * // equivalent to:\n * $rows = $reader->readAll();\n * ```\n *\n * Note that since DataReader is a forward-only stream, you can only traverse it once.\n * Doing it the second time will throw an exception.\n *\n * It is possible to use a specific mode of data fetching by setting\n * [[fetchMode]]. See the [PHP manual](https://www.php.net/manual/en/function.PDOStatement-setFetchMode.php)\n * for more details about possible fetch mode.\n *\n * @property-read int $columnCount The number of columns in the result set.\n * @property-write int $fetchMode Fetch mode.\n * @property-read bool $isClosed Whether the reader is closed or not.\n * @property-read int $rowCount Number of rows contained in the result.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @implements \\Iterator<int, mixed>\n */\nclass DataReader extends \\yii\\base\\BaseObject implements \\Iterator, \\Countable\n{\n    /**\n     * @var \\PDOStatement the PDOStatement associated with the command\n     */\n    private $_statement;\n    private $_closed = false;\n    private $_row;\n    private $_index = -1;\n\n\n    /**\n     * Constructor.\n     * @param Command $command the command generating the query result\n     * @param array $config name-value pairs that will be used to initialize the object properties\n     */\n    public function __construct(Command $command, $config = [])\n    {\n        $this->_statement = $command->pdoStatement;\n        $this->_statement->setFetchMode(\\PDO::FETCH_ASSOC);\n        parent::__construct($config);\n    }\n\n    /**\n     * Binds a column to a PHP variable.\n     * When rows of data are being fetched, the corresponding column value\n     * will be set in the variable. Note, the fetch mode must include PDO::FETCH_BOUND.\n     * @param int|string $column Number of the column (1-indexed) or name of the column\n     * in the result set. If using the column name, be aware that the name\n     * should match the case of the column, as returned by the driver.\n     * @param mixed $value Name of the PHP variable to which the column will be bound.\n     * @param int|null $dataType Data type of the parameter\n     * @see https://www.php.net/manual/en/function.PDOStatement-bindColumn.php\n     */\n    public function bindColumn($column, &$value, $dataType = null)\n    {\n        if ($dataType === null) {\n            $this->_statement->bindColumn($column, $value);\n        } else {\n            $this->_statement->bindColumn($column, $value, $dataType);\n        }\n    }\n\n    /**\n     * Set the default fetch mode for this statement.\n     *\n     * @param int $mode fetch mode\n     * @see https://www.php.net/manual/en/function.PDOStatement-setFetchMode.php\n     */\n    public function setFetchMode($mode)\n    {\n        $params = func_get_args();\n        call_user_func_array([$this->_statement, 'setFetchMode'], $params);\n    }\n\n    /**\n     * Advances the reader to the next row in a result set.\n     * @return array|false the current row, false if no more row available\n     */\n    public function read()\n    {\n        return $this->_statement->fetch();\n    }\n\n    /**\n     * Returns a single column from the next row of a result set.\n     * @param int $columnIndex zero-based column index\n     * @return mixed the column of the current row, false if no more rows available\n     */\n    public function readColumn($columnIndex)\n    {\n        return $this->_statement->fetchColumn($columnIndex);\n    }\n\n    /**\n     * Returns an object populated with the next row of data.\n     * @param string $className class name of the object to be created and populated\n     * @param array $fields Elements of this array are passed to the constructor\n     * @return mixed the populated object, false if no more row of data available\n     */\n    public function readObject($className, $fields)\n    {\n        return $this->_statement->fetchObject($className, $fields);\n    }\n\n    /**\n     * Reads the whole result set into an array.\n     * @return array the result set (each array element represents a row of data).\n     * An empty array will be returned if the result contains no row.\n     */\n    public function readAll()\n    {\n        return $this->_statement->fetchAll();\n    }\n\n    /**\n     * Advances the reader to the next result when reading the results of a batch of statements.\n     * This method is only useful when there are multiple result sets\n     * returned by the query. Not all DBMS support this feature.\n     * @return bool Returns true on success or false on failure.\n     */\n    public function nextResult()\n    {\n        if (($result = $this->_statement->nextRowset()) !== false) {\n            $this->_index = -1;\n        }\n\n        return $result;\n    }\n\n    /**\n     * Closes the reader.\n     * This frees up the resources allocated for executing this SQL statement.\n     * Read attempts after this method call are unpredictable.\n     */\n    public function close()\n    {\n        $this->_statement->closeCursor();\n        $this->_closed = true;\n    }\n\n    /**\n     * whether the reader is closed or not.\n     * @return bool whether the reader is closed or not.\n     */\n    public function getIsClosed()\n    {\n        return $this->_closed;\n    }\n\n    /**\n     * Returns the number of rows in the result set.\n     * Note, most DBMS may not give a meaningful count.\n     * In this case, use \"SELECT COUNT(*) FROM tableName\" to obtain the number of rows.\n     * @return int number of rows contained in the result.\n     */\n    public function getRowCount()\n    {\n        return $this->_statement->rowCount();\n    }\n\n    /**\n     * Returns the number of rows in the result set.\n     * This method is required by the Countable interface.\n     * Note, most DBMS may not give a meaningful count.\n     * In this case, use \"SELECT COUNT(*) FROM tableName\" to obtain the number of rows.\n     * @return int number of rows contained in the result.\n     */\n    #[\\ReturnTypeWillChange]\n    public function count()\n    {\n        return $this->getRowCount();\n    }\n\n    /**\n     * Returns the number of columns in the result set.\n     * Note, even there's no row in the reader, this still gives correct column number.\n     * @return int the number of columns in the result set.\n     */\n    public function getColumnCount()\n    {\n        return $this->_statement->columnCount();\n    }\n\n    /**\n     * Resets the iterator to the initial state.\n     * This method is required by the interface [[\\Iterator]].\n     * @throws InvalidCallException if this method is invoked twice\n     */\n    #[\\ReturnTypeWillChange]\n    public function rewind()\n    {\n        if ($this->_index < 0) {\n            $this->_row = $this->_statement->fetch();\n            $this->_index = 0;\n        } else {\n            throw new InvalidCallException('DataReader cannot rewind. It is a forward-only reader.');\n        }\n    }\n\n    /**\n     * Returns the index of the current row.\n     * This method is required by the interface [[\\Iterator]].\n     * @return int the index of the current row.\n     */\n    #[\\ReturnTypeWillChange]\n    public function key()\n    {\n        return $this->_index;\n    }\n\n    /**\n     * Returns the current row.\n     * This method is required by the interface [[\\Iterator]].\n     * @return mixed the current row.\n     */\n    #[\\ReturnTypeWillChange]\n    public function current()\n    {\n        return $this->_row;\n    }\n\n    /**\n     * Moves the internal pointer to the next row.\n     * This method is required by the interface [[\\Iterator]].\n     */\n    #[\\ReturnTypeWillChange]\n    public function next()\n    {\n        $this->_row = $this->_statement->fetch();\n        $this->_index++;\n    }\n\n    /**\n     * Returns whether there is a row of data at current position.\n     * This method is required by the interface [[\\Iterator]].\n     * @return bool whether there is a row of data at current position.\n     */\n    #[\\ReturnTypeWillChange]\n    public function valid()\n    {\n        return $this->_row !== false;\n    }\n}\n"
  },
  {
    "path": "framework/db/DefaultValueConstraint.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\n/**\n * DefaultValueConstraint represents the metadata of a table `DEFAULT` constraint.\n *\n * @author Sergey Makinen <sergey@makinen.ru>\n * @since 2.0.13\n */\nclass DefaultValueConstraint extends Constraint\n{\n    /**\n     * @var mixed default value as returned by the DBMS.\n     */\n    public $value;\n}\n"
  },
  {
    "path": "framework/db/Exception.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\n/**\n * Exception represents an exception that is caused by some DB-related operations.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Exception extends \\yii\\base\\Exception\n{\n    /**\n     * @var array the error info provided by a PDO exception. This is the same as returned\n     * by [PDO::errorInfo](https://www.php.net/manual/en/pdo.errorinfo.php).\n     */\n    public $errorInfo = [];\n\n\n    /**\n     * Constructor.\n     * @param string $message PDO error message\n     * @param array $errorInfo PDO error info\n     * @param string $code PDO error code\n     * @param \\Throwable|null $previous The previous exception used for the exception chaining.\n     */\n    public function __construct($message, $errorInfo = [], $code = '', $previous = null)\n    {\n        parent::__construct($message, 0, $previous);\n        $this->errorInfo = $errorInfo;\n        $this->code = $code;\n    }\n\n    /**\n     * @return string the user-friendly name of this exception\n     */\n    public function getName()\n    {\n        return 'Database Exception';\n    }\n\n    /**\n     * @return string readable representation of exception\n     */\n    public function __toString()\n    {\n        return parent::__toString() . PHP_EOL\n        . 'Additional Information:' . PHP_EOL . print_r($this->errorInfo, true);\n    }\n}\n"
  },
  {
    "path": "framework/db/Expression.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\n/**\n * Expression represents a DB expression that does not need escaping or quoting.\n *\n * When an Expression object is embedded within a SQL statement or fragment,\n * it will be replaced with the [[expression]] property value without any\n * DB escaping or quoting. For example,\n *\n * ```\n * $expression = new Expression('NOW()');\n * $now = (new \\yii\\db\\Query)->select($expression)->scalar();  // SELECT NOW();\n * echo $now; // prints the current date\n * ```\n *\n * Expression objects are mainly created for passing raw SQL expressions to methods of\n * [[Query]], [[ActiveQuery]], and related classes.\n *\n * An expression can also be bound with parameters specified via [[params]].\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Expression extends \\yii\\base\\BaseObject implements ExpressionInterface\n{\n    /**\n     * @var string the DB expression\n     */\n    public $expression;\n    /**\n     * @var array list of parameters that should be bound for this expression.\n     * The keys are placeholders appearing in [[expression]] and the values\n     * are the corresponding parameter values.\n     */\n    public $params = [];\n\n\n    /**\n     * Constructor.\n     * @param string $expression the DB expression\n     * @param array $params parameters\n     * @param array $config name-value pairs that will be used to initialize the object properties\n     */\n    public function __construct($expression, $params = [], $config = [])\n    {\n        $this->expression = $expression;\n        $this->params = $params;\n        parent::__construct($config);\n    }\n\n    /**\n     * String magic method.\n     * @return string the DB expression.\n     */\n    public function __toString()\n    {\n        return $this->expression;\n    }\n}\n"
  },
  {
    "path": "framework/db/ExpressionBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\n/**\n * Class ExpressionBuilder builds objects of [[yii\\db\\Expression]] class.\n *\n * @author Dmitry Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n */\nclass ExpressionBuilder implements ExpressionBuilderInterface\n{\n    use ExpressionBuilderTrait;\n\n\n    /**\n     * {@inheritdoc}\n     * @param Expression|ExpressionInterface $expression the expression to be built\n     */\n    public function build(ExpressionInterface $expression, array &$params = [])\n    {\n        $params = array_merge($params, $expression->params);\n        return $expression->__toString();\n    }\n}\n"
  },
  {
    "path": "framework/db/ExpressionBuilderInterface.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\n/**\n * Interface ExpressionBuilderInterface is designed to build raw SQL from specific expression\n * objects that implement [[ExpressionInterface]].\n *\n * @author Dmitry Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n */\ninterface ExpressionBuilderInterface\n{\n    /**\n     * Method builds the raw SQL from the $expression that will not be additionally\n     * escaped or quoted.\n     *\n     * @param ExpressionInterface $expression the expression to be built.\n     * @param array $params the binding parameters.\n     * @return string the raw SQL that will not be additionally escaped or quoted.\n     */\n    public function build(ExpressionInterface $expression, array &$params = []);\n}\n"
  },
  {
    "path": "framework/db/ExpressionBuilderTrait.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\n/**\n * Trait ExpressionBuilderTrait provides common constructor for classes that\n * should implement [[ExpressionBuilderInterface]]\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n */\ntrait ExpressionBuilderTrait\n{\n    /**\n     * @var QueryBuilder\n     */\n    protected $queryBuilder;\n\n    /**\n     * ExpressionBuilderTrait constructor.\n     *\n     * @param QueryBuilder $queryBuilder\n     */\n    public function __construct(QueryBuilder $queryBuilder)\n    {\n        $this->queryBuilder = $queryBuilder;\n    }\n}\n"
  },
  {
    "path": "framework/db/ExpressionInterface.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\n/**\n * Interface ExpressionInterface should be used to mark classes, that should be built\n * in a special way.\n *\n * The database abstraction layer of Yii framework supports objects that implement this\n * interface and will use [[ExpressionBuilderInterface]] to build them.\n *\n * The default implementation is a class [[Expression]].\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n */\ninterface ExpressionInterface\n{\n}\n"
  },
  {
    "path": "framework/db/ForeignKeyConstraint.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\n/**\n * ForeignKeyConstraint represents the metadata of a table `FOREIGN KEY` constraint.\n *\n * @author Sergey Makinen <sergey@makinen.ru>\n * @since 2.0.13\n */\nclass ForeignKeyConstraint extends Constraint\n{\n    /**\n     * @var string|null referenced table schema name.\n     */\n    public $foreignSchemaName;\n    /**\n     * @var string referenced table name.\n     */\n    public $foreignTableName;\n    /**\n     * @var string[] list of referenced table column names.\n     */\n    public $foreignColumnNames;\n    /**\n     * @var string|null referential action if rows in a referenced table are to be updated.\n     */\n    public $onUpdate;\n    /**\n     * @var string|null referential action if rows in a referenced table are to be deleted.\n     */\n    public $onDelete;\n}\n"
  },
  {
    "path": "framework/db/IndexConstraint.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\n/**\n * IndexConstraint represents the metadata of a table `INDEX` constraint.\n *\n * @author Sergey Makinen <sergey@makinen.ru>\n * @since 2.0.13\n */\nclass IndexConstraint extends Constraint\n{\n    /**\n     * @var bool whether the index is unique.\n     */\n    public $isUnique;\n    /**\n     * @var bool whether the index was created for a primary key.\n     */\n    public $isPrimary;\n}\n"
  },
  {
    "path": "framework/db/IntegrityException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\n/**\n * Exception represents an exception that is caused by violation of DB constraints.\n *\n * @author Alexander Makarov <sam@rmcreative.ru>\n * @since 2.0\n */\nclass IntegrityException extends Exception\n{\n    /**\n     * @return string the user-friendly name of this exception\n     */\n    public function getName()\n    {\n        return 'Integrity constraint violation';\n    }\n}\n"
  },
  {
    "path": "framework/db/JsonExpression.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\nuse yii\\base\\InvalidConfigException;\n\n/**\n * Class JsonExpression represents data that should be encoded to JSON.\n *\n * For example:\n *\n * ```\n * new JsonExpression(['a' => 1, 'b' => 2]); // will be encoded to '{\"a\": 1, \"b\": 2}'\n * ```\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n */\nclass JsonExpression implements ExpressionInterface, \\JsonSerializable\n{\n    public const TYPE_JSON = 'json';\n    public const TYPE_JSONB = 'jsonb';\n    /**\n     * @var mixed the value to be encoded to JSON.\n     * The value must be compatible with [\\yii\\helpers\\Json::encode()|Json::encode()]] input requirements.\n     */\n    protected $value;\n    /**\n     * @var string|null Type of JSON, expression should be casted to. Defaults to `null`, meaning\n     * no explicit casting will be performed.\n     * This property will be encountered only for DBMSs that support different types of JSON.\n     * For example, PostgreSQL has `json` and `jsonb` types.\n     */\n    protected $type;\n\n\n    /**\n     * JsonExpression constructor.\n     *\n     * @param mixed $value the value to be encoded to JSON.\n     * The value must be compatible with [\\yii\\helpers\\Json::encode()|Json::encode()]] requirements.\n     * @param string|null $type the type of the JSON. See [[JsonExpression::type]]\n     *\n     * @see type\n     */\n    public function __construct($value, $type = null)\n    {\n        if ($value instanceof self) {\n            $value = $value->getValue();\n        }\n\n        $this->value = $value;\n        $this->type = $type;\n    }\n\n    /**\n     * @return mixed\n     * @see value\n     */\n    public function getValue()\n    {\n        return $this->value;\n    }\n\n    /**\n     * @return string|null the type of JSON\n     * @see type\n     */\n    public function getType()\n    {\n        return $this->type;\n    }\n\n    /**\n     * Specify data which should be serialized to JSON\n     *\n     * @link https://www.php.net/manual/en/jsonserializable.jsonserialize.php\n     * @return mixed data which can be serialized by <b>json_encode</b>,\n     * which is a value of any type other than a resource.\n     * @since 2.0.14.2\n     * @throws InvalidConfigException when JsonExpression contains QueryInterface object\n     */\n    #[\\ReturnTypeWillChange]\n    public function jsonSerialize()\n    {\n        $value = $this->getValue();\n        if ($value instanceof QueryInterface) {\n            throw new InvalidConfigException('The JsonExpression class can not be serialized to JSON when the value is a QueryInterface object');\n        }\n\n        return $value;\n    }\n}\n"
  },
  {
    "path": "framework/db/Migration.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\nuse yii\\base\\Component;\nuse yii\\di\\Instance;\nuse yii\\helpers\\StringHelper;\n\n/**\n * Migration is the base class for representing a database migration.\n *\n * Migration is designed to be used together with the \"yii migrate\" command.\n *\n * Each child class of Migration represents an individual database migration which\n * is identified by the child class name.\n *\n * Within each migration, the [[up()]] method should be overridden to contain the logic\n * for \"upgrading\" the database; while the [[down()]] method for the \"downgrading\"\n * logic. The \"yii migrate\" command manages all available migrations in an application.\n *\n * If the database supports transactions, you may also override [[safeUp()]] and\n * [[safeDown()]] so that if anything wrong happens during the upgrading or downgrading,\n * the whole migration can be reverted in a whole.\n *\n * Note that some DB queries in some DBMS cannot be put into a transaction. For some examples,\n * please refer to [implicit commit](https://dev.mysql.com/doc/refman/5.7/en/implicit-commit.html). If this is the case,\n * you should still implement `up()` and `down()`, instead.\n *\n * Migration provides a set of convenient methods for manipulating database data and schema.\n * For example, the [[insert()]] method can be used to easily insert a row of data into\n * a database table; the [[createTable()]] method can be used to create a database table.\n * Compared with the same methods in [[Command]], these methods will display extra\n * information showing the method parameters and execution time, which may be useful when\n * applying migrations.\n *\n * For more details and usage information on Migration, see the [guide article on Migration](guide:db-migrations).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Migration extends Component implements MigrationInterface\n{\n    use SchemaBuilderTrait;\n\n    /**\n     * @var Connection|array|string the DB connection object or the application component ID of the DB connection\n     * that this migration should work with. Starting from version 2.0.2, this can also be a configuration array\n     * for creating the object.\n     *\n     * Note that when a Migration object is created by the `migrate` command, this property will be overwritten\n     * by the command. If you do not want to use the DB connection provided by the command, you may override\n     * the [[init()]] method like the following:\n     *\n     * ```\n     * public function init()\n     * {\n     *     $this->db = 'db2';\n     *     parent::init();\n     * }\n     * ```\n     */\n    public $db = 'db';\n    /**\n     * @var int max number of characters of the SQL outputted. Useful for reduction of long statements and making\n     * console output more compact.\n     * @since 2.0.13\n     */\n    public $maxSqlOutputLength;\n    /**\n     * @var bool indicates whether the console output should be compacted.\n     * If this is set to true, the individual commands ran within the migration will not be output to the console.\n     * Default is false, in other words the output is fully verbose by default.\n     * @since 2.0.13\n     */\n    public $compact = false;\n\n\n    /**\n     * Initializes the migration.\n     * This method will set [[db]] to be the 'db' application component, if it is `null`.\n     */\n    public function init()\n    {\n        parent::init();\n        $this->db = Instance::ensure($this->db, Connection::className());\n        $this->db->getSchema()->refresh();\n        $this->db->enableSlaves = false;\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.6\n     */\n    protected function getDb()\n    {\n        return $this->db;\n    }\n\n    /**\n     * This method contains the logic to be executed when applying this migration.\n     * Child classes may override this method to provide actual migration logic.\n     * @return false|void|mixed return a false value to indicate the migration fails\n     * and should not proceed further. All other return values mean the migration succeeds.\n     */\n    public function up()\n    {\n        $transaction = $this->db->beginTransaction();\n        try {\n            if ($this->safeUp() === false) {\n                $transaction->rollBack();\n                return false;\n            }\n            $transaction->commit();\n        } catch (\\Exception $e) {\n            $this->printException($e);\n            $transaction->rollBack();\n            return false;\n        } catch (\\Throwable $e) {\n            $this->printException($e);\n            $transaction->rollBack();\n            return false;\n        }\n\n        return null;\n    }\n\n    /**\n     * This method contains the logic to be executed when removing this migration.\n     * The default implementation throws an exception indicating the migration cannot be removed.\n     * Child classes may override this method if the corresponding migrations can be removed.\n     * @return false|void|mixed return a false value to indicate the migration fails\n     * and should not proceed further. All other return values mean the migration succeeds.\n     */\n    public function down()\n    {\n        $transaction = $this->db->beginTransaction();\n        try {\n            if ($this->safeDown() === false) {\n                $transaction->rollBack();\n                return false;\n            }\n            $transaction->commit();\n        } catch (\\Exception $e) {\n            $this->printException($e);\n            $transaction->rollBack();\n            return false;\n        } catch (\\Throwable $e) {\n            $this->printException($e);\n            $transaction->rollBack();\n            return false;\n        }\n\n        return null;\n    }\n\n    /**\n     * @param \\Throwable $e\n     */\n    private function printException($e)\n    {\n        echo 'Exception: ' . $e->getMessage() . ' (' . $e->getFile() . ':' . $e->getLine() . \")\\n\";\n        echo $e->getTraceAsString() . \"\\n\";\n    }\n\n    /**\n     * This method contains the logic to be executed when applying this migration.\n     * This method differs from [[up()]] in that the DB logic implemented here will\n     * be enclosed within a DB transaction.\n     * Child classes may implement this method instead of [[up()]] if the DB logic\n     * needs to be within a transaction.\n     *\n     * Note: Not all DBMS support transactions. And some DB queries cannot be put into a transaction. For some examples,\n     * please refer to [implicit commit](https://dev.mysql.com/doc/refman/5.7/en/implicit-commit.html).\n     *\n     * @return false|void|mixed return a false value to indicate the migration fails\n     * and should not proceed further. All other return values mean the migration succeeds.\n     */\n    public function safeUp()\n    {\n    }\n\n    /**\n     * This method contains the logic to be executed when removing this migration.\n     * This method differs from [[down()]] in that the DB logic implemented here will\n     * be enclosed within a DB transaction.\n     * Child classes may implement this method instead of [[down()]] if the DB logic\n     * needs to be within a transaction.\n     *\n     * Note: Not all DBMS support transactions. And some DB queries cannot be put into a transaction. For some examples,\n     * please refer to [implicit commit](https://dev.mysql.com/doc/refman/5.7/en/implicit-commit.html).\n     *\n     * @return false|void|mixed return a false value to indicate the migration fails\n     * and should not proceed further. All other return values mean the migration succeeds.\n     */\n    public function safeDown()\n    {\n    }\n\n    /**\n     * Executes a SQL statement.\n     * This method executes the specified SQL statement using [[db]].\n     * @param string $sql the SQL statement to be executed\n     * @param array $params input parameters (name => value) for the SQL execution.\n     * See [[Command::execute()]] for more details.\n     */\n    public function execute($sql, $params = [])\n    {\n        $sqlOutput = $sql;\n        if ($this->maxSqlOutputLength !== null) {\n            $sqlOutput = StringHelper::truncate($sql, $this->maxSqlOutputLength, '[... hidden]');\n        }\n\n        $time = $this->beginCommand(\"execute SQL: $sqlOutput\");\n        $this->db->createCommand($sql)->bindValues($params)->execute();\n        $this->endCommand($time);\n    }\n\n    /**\n     * Creates and executes an INSERT SQL statement.\n     * The method will properly escape the column names, and bind the values to be inserted.\n     * @param string $table the table that new rows will be inserted into.\n     * @param array $columns the column data (name => value) to be inserted into the table.\n     */\n    public function insert($table, $columns)\n    {\n        $time = $this->beginCommand(\"insert into $table\");\n        $this->db->createCommand()->insert($table, $columns)->execute();\n        $this->endCommand($time);\n    }\n\n    /**\n     * Creates and executes a batch INSERT SQL statement.\n     * The method will properly escape the column names, and bind the values to be inserted.\n     * @param string $table the table that new rows will be inserted into.\n     * @param array $columns the column names.\n     * @param array $rows the rows to be batch inserted into the table\n     */\n    public function batchInsert($table, $columns, $rows)\n    {\n        $time = $this->beginCommand(\"insert into $table\");\n        $this->db->createCommand()->batchInsert($table, $columns, $rows)->execute();\n        $this->endCommand($time);\n    }\n\n    /**\n     * Creates and executes a command to insert rows into a database table if\n     * they do not already exist (matching unique constraints),\n     * or update them if they do.\n     *\n     * The method will properly escape the column names, and bind the values to be inserted.\n     *\n     * @param string $table the table that new rows will be inserted into/updated in.\n     * @param array|Query $insertColumns the column data (name => value) to be inserted into the table or instance\n     * of [[Query]] to perform `INSERT INTO ... SELECT` SQL statement.\n     * @param array|bool $updateColumns the column data (name => value) to be updated if they already exist.\n     * If `true` is passed, the column data will be updated to match the insert column data.\n     * If `false` is passed, no update will be performed if the column data already exists.\n     * @param array $params the parameters to be bound to the command.\n     * @since 2.0.14\n     */\n    public function upsert($table, $insertColumns, $updateColumns = true, $params = [])\n    {\n        $time = $this->beginCommand(\"upsert into $table\");\n        $this->db->createCommand()->upsert($table, $insertColumns, $updateColumns, $params)->execute();\n        $this->endCommand($time);\n    }\n\n    /**\n     * Creates and executes an UPDATE SQL statement.\n     * The method will properly escape the column names and bind the values to be updated.\n     * @param string $table the table to be updated.\n     * @param array $columns the column data (name => value) to be updated.\n     * @param array|string $condition the conditions that will be put in the WHERE part. Please\n     * refer to [[Query::where()]] on how to specify conditions.\n     * @param array $params the parameters to be bound to the query.\n     */\n    public function update($table, $columns, $condition = '', $params = [])\n    {\n        $time = $this->beginCommand(\"update $table\");\n        $this->db->createCommand()->update($table, $columns, $condition, $params)->execute();\n        $this->endCommand($time);\n    }\n\n    /**\n     * Creates and executes a DELETE SQL statement.\n     * @param string $table the table where the data will be deleted from.\n     * @param array|string $condition the conditions that will be put in the WHERE part. Please\n     * refer to [[Query::where()]] on how to specify conditions.\n     * @param array $params the parameters to be bound to the query.\n     */\n    public function delete($table, $condition = '', $params = [])\n    {\n        $time = $this->beginCommand(\"delete from $table\");\n        $this->db->createCommand()->delete($table, $condition, $params)->execute();\n        $this->endCommand($time);\n    }\n\n    /**\n     * Builds and executes a SQL statement for creating a new DB table.\n     *\n     * The columns in the new  table should be specified as name-definition pairs (e.g. 'name' => 'string'),\n     * where name stands for a column name which will be properly quoted by the method, and definition\n     * stands for the column type which must contain an abstract DB type.\n     *\n     * The [[QueryBuilder::getColumnType()]] method will be invoked to convert any abstract type into a physical one.\n     *\n     * If a column is specified with definition only (e.g. 'PRIMARY KEY (name, type)'), it will be directly\n     * put into the generated SQL.\n     *\n     * Example usage:\n     * ```\n     * class m200000_000000_create_table_fruits extends \\yii\\db\\Migration\n     * {\n     *     public function safeUp()\n     *     {\n     *          $this->createTable('{{%fruits}}', [\n     *              // ...\n     *              'column_name double precision null default null',\n     * ```\n     *\n     *\n     * @param string $table the name of the table to be created. The name will be properly quoted by the method.\n     * @param array $columns the columns (name => definition) in the new table.\n     * @param string|null $options additional SQL fragment that will be appended to the generated SQL.\n     */\n    public function createTable($table, $columns, $options = null)\n    {\n        $time = $this->beginCommand(\"create table $table\");\n        $this->db->createCommand()->createTable($table, $columns, $options)->execute();\n        foreach ($columns as $column => $type) {\n            if ($type instanceof ColumnSchemaBuilder && $type->comment !== null) {\n                $this->db->createCommand()->addCommentOnColumn($table, $column, $type->comment)->execute();\n            }\n        }\n        $this->endCommand($time);\n    }\n\n    /**\n     * Builds and executes a SQL statement for renaming a DB table.\n     * @param string $table the table to be renamed. The name will be properly quoted by the method.\n     * @param string $newName the new table name. The name will be properly quoted by the method.\n     */\n    public function renameTable($table, $newName)\n    {\n        $time = $this->beginCommand(\"rename table $table to $newName\");\n        $this->db->createCommand()->renameTable($table, $newName)->execute();\n        $this->endCommand($time);\n    }\n\n    /**\n     * Builds and executes a SQL statement for dropping a DB table.\n     * @param string $table the table to be dropped. The name will be properly quoted by the method.\n     */\n    public function dropTable($table)\n    {\n        $time = $this->beginCommand(\"drop table $table\");\n        $this->db->createCommand()->dropTable($table)->execute();\n        $this->endCommand($time);\n    }\n\n    /**\n     * Builds and executes a SQL statement for truncating a DB table.\n     * @param string $table the table to be truncated. The name will be properly quoted by the method.\n     */\n    public function truncateTable($table)\n    {\n        $time = $this->beginCommand(\"truncate table $table\");\n        $this->db->createCommand()->truncateTable($table)->execute();\n        $this->endCommand($time);\n    }\n\n    /**\n     * Builds and executes a SQL statement for adding a new DB column.\n     * @param string $table the table that the new column will be added to. The table name will be properly quoted by the method.\n     * @param string $column the name of the new column. The name will be properly quoted by the method.\n     * @param string $type the column type. The [[QueryBuilder::getColumnType()]] method will be invoked to convert abstract column type (if any)\n     * into the physical one. Anything that is not recognized as abstract type will be kept in the generated SQL.\n     * For example, 'string' will be turned into 'varchar(255)', while 'string not null' will become 'varchar(255) not null'.\n     */\n    public function addColumn($table, $column, $type)\n    {\n        $time = $this->beginCommand(\"add column $column $type to table $table\");\n        $this->db->createCommand()->addColumn($table, $column, $type)->execute();\n        if ($type instanceof ColumnSchemaBuilder && $type->comment !== null) {\n            $this->db->createCommand()->addCommentOnColumn($table, $column, $type->comment)->execute();\n        }\n        $this->endCommand($time);\n    }\n\n    /**\n     * Builds and executes a SQL statement for dropping a DB column.\n     * @param string $table the table whose column is to be dropped. The name will be properly quoted by the method.\n     * @param string $column the name of the column to be dropped. The name will be properly quoted by the method.\n     */\n    public function dropColumn($table, $column)\n    {\n        $time = $this->beginCommand(\"drop column $column from table $table\");\n        $this->db->createCommand()->dropColumn($table, $column)->execute();\n        $this->endCommand($time);\n    }\n\n    /**\n     * Builds and executes a SQL statement for renaming a column.\n     * @param string $table the table whose column is to be renamed. The name will be properly quoted by the method.\n     * @param string $name the old name of the column. The name will be properly quoted by the method.\n     * @param string $newName the new name of the column. The name will be properly quoted by the method.\n     */\n    public function renameColumn($table, $name, $newName)\n    {\n        $time = $this->beginCommand(\"rename column $name in table $table to $newName\");\n        $this->db->createCommand()->renameColumn($table, $name, $newName)->execute();\n        $this->endCommand($time);\n    }\n\n    /**\n     * Builds and executes a SQL statement for changing the definition of a column.\n     * @param string $table the table whose column is to be changed. The table name will be properly quoted by the method.\n     * @param string $column the name of the column to be changed. The name will be properly quoted by the method.\n     * @param string $type the new column type. The [[QueryBuilder::getColumnType()]] method will be invoked to convert abstract column type (if any)\n     * into the physical one. Anything that is not recognized as abstract type will be kept in the generated SQL.\n     * For example, 'string' will be turned into 'varchar(255)', while 'string not null' will become 'varchar(255) not null'.\n     */\n    public function alterColumn($table, $column, $type)\n    {\n        $time = $this->beginCommand(\"alter column $column in table $table to $type\");\n        $this->db->createCommand()->alterColumn($table, $column, $type)->execute();\n        if ($type instanceof ColumnSchemaBuilder && $type->comment !== null) {\n            $this->db->createCommand()->addCommentOnColumn($table, $column, $type->comment)->execute();\n        }\n        $this->endCommand($time);\n    }\n\n    /**\n     * Builds and executes a SQL statement for creating a primary key.\n     * The method will properly quote the table and column names.\n     * @param string $name the name of the primary key constraint.\n     * @param string $table the table that the primary key constraint will be added to.\n     * @param string|array $columns comma separated string or array of columns that the primary key will consist of.\n     */\n    public function addPrimaryKey($name, $table, $columns)\n    {\n        $time = $this->beginCommand(\"add primary key $name on $table (\" . (is_array($columns) ? implode(',', $columns) : $columns) . ')');\n        $this->db->createCommand()->addPrimaryKey($name, $table, $columns)->execute();\n        $this->endCommand($time);\n    }\n\n    /**\n     * Builds and executes a SQL statement for dropping a primary key.\n     * @param string $name the name of the primary key constraint to be removed.\n     * @param string $table the table that the primary key constraint will be removed from.\n     */\n    public function dropPrimaryKey($name, $table)\n    {\n        $time = $this->beginCommand(\"drop primary key $name\");\n        $this->db->createCommand()->dropPrimaryKey($name, $table)->execute();\n        $this->endCommand($time);\n    }\n\n    /**\n     * Builds a SQL statement for adding a foreign key constraint to an existing table.\n     * The method will properly quote the table and column names.\n     * @param string $name the name of the foreign key constraint.\n     * @param string $table the table that the foreign key constraint will be added to.\n     * @param string|array $columns the name of the column to that the constraint will be added on. If there are multiple columns, separate them with commas or use an array.\n     * @param string $refTable the table that the foreign key references to.\n     * @param string|array $refColumns the name of the column that the foreign key references to. If there are multiple columns, separate them with commas or use an array.\n     * @param string|null $delete the ON DELETE option. Most DBMS support these options: RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL\n     * @param string|null $update the ON UPDATE option. Most DBMS support these options: RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL\n     */\n    public function addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete = null, $update = null)\n    {\n        $time = $this->beginCommand(\"add foreign key $name: $table (\" . implode(',', (array) $columns) . \") references $refTable (\" . implode(',', (array) $refColumns) . ')');\n        $this->db->createCommand()->addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete, $update)->execute();\n        $this->endCommand($time);\n    }\n\n    /**\n     * Builds a SQL statement for dropping a foreign key constraint.\n     * @param string $name the name of the foreign key constraint to be dropped. The name will be properly quoted by the method.\n     * @param string $table the table whose foreign is to be dropped. The name will be properly quoted by the method.\n     */\n    public function dropForeignKey($name, $table)\n    {\n        $time = $this->beginCommand(\"drop foreign key $name from table $table\");\n        $this->db->createCommand()->dropForeignKey($name, $table)->execute();\n        $this->endCommand($time);\n    }\n\n    /**\n     * Builds and executes a SQL statement for creating a new index.\n     * @param string $name the name of the index. The name will be properly quoted by the method.\n     * @param string $table the table that the new index will be created for. The table name will be properly quoted by the method.\n     * @param string|array $columns the column(s) that should be included in the index. If there are multiple columns, please separate them\n     * by commas or use an array. Each column name will be properly quoted by the method. Quoting will be skipped for column names that\n     * include a left parenthesis \"(\".\n     * @param bool $unique whether to add UNIQUE constraint on the created index.\n     */\n    public function createIndex($name, $table, $columns, $unique = false)\n    {\n        $time = $this->beginCommand('create' . ($unique ? ' unique' : '') . \" index $name on $table (\" . implode(',', (array) $columns) . ')');\n        $this->db->createCommand()->createIndex($name, $table, $columns, $unique)->execute();\n        $this->endCommand($time);\n    }\n\n    /**\n     * Builds and executes a SQL statement for dropping an index.\n     * @param string $name the name of the index to be dropped. The name will be properly quoted by the method.\n     * @param string $table the table whose index is to be dropped. The name will be properly quoted by the method.\n     */\n    public function dropIndex($name, $table)\n    {\n        $time = $this->beginCommand(\"drop index $name on $table\");\n        $this->db->createCommand()->dropIndex($name, $table)->execute();\n        $this->endCommand($time);\n    }\n\n    /**\n     * Creates a SQL command for adding a check constraint to an existing table.\n     * @param string $name the name of the check constraint.\n     * The name will be properly quoted by the method.\n     * @param string $table the table that the check constraint will be added to.\n     * The name will be properly quoted by the method.\n     * @param string $expression the SQL of the `CHECK` constraint.\n     */\n    public function addCheck($name, $table, $expression)\n    {\n        $time = $this->beginCommand(\"add check $name in table $table\");\n        $this->db->createCommand()->addCheck($name, $table, $expression)->execute();\n        $this->endCommand($time);\n    }\n\n    /**\n     * Creates a SQL command for dropping a check constraint.\n     * @param string $name the name of the check constraint to be dropped.\n     * The name will be properly quoted by the method.\n     * @param string $table the table whose check constraint is to be dropped.\n     * The name will be properly quoted by the method.\n     */\n    public function dropCheck($name, $table)\n    {\n        $time = $this->beginCommand(\"drop check $name in table $table\");\n        $this->db->createCommand()->dropCheck($name, $table)->execute();\n        $this->endCommand($time);\n    }\n\n    /**\n     * Builds and execute a SQL statement for adding comment to column.\n     *\n     * @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.\n     * @param string $column the name of the column to be commented. The column name will be properly quoted by the method.\n     * @param string $comment the text of the comment to be added. The comment will be properly quoted by the method.\n     * @since 2.0.8\n     */\n    public function addCommentOnColumn($table, $column, $comment)\n    {\n        $time = $this->beginCommand(\"add comment on column $column\");\n        $this->db->createCommand()->addCommentOnColumn($table, $column, $comment)->execute();\n        $this->endCommand($time);\n    }\n\n    /**\n     * Builds a SQL statement for adding comment to table.\n     *\n     * @param string $table the table to be commented. The table name will be properly quoted by the method.\n     * @param string $comment the text of the comment to be added. The comment will be properly quoted by the method.\n     * @since 2.0.8\n     */\n    public function addCommentOnTable($table, $comment)\n    {\n        $time = $this->beginCommand(\"add comment on table $table\");\n        $this->db->createCommand()->addCommentOnTable($table, $comment)->execute();\n        $this->endCommand($time);\n    }\n\n    /**\n     * Builds and execute a SQL statement for dropping comment from column.\n     *\n     * @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.\n     * @param string $column the name of the column to be commented. The column name will be properly quoted by the method.\n     * @since 2.0.8\n     */\n    public function dropCommentFromColumn($table, $column)\n    {\n        $time = $this->beginCommand(\"drop comment from column $column\");\n        $this->db->createCommand()->dropCommentFromColumn($table, $column)->execute();\n        $this->endCommand($time);\n    }\n\n    /**\n     * Builds a SQL statement for dropping comment from table.\n     *\n     * @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.\n     * @since 2.0.8\n     */\n    public function dropCommentFromTable($table)\n    {\n        $time = $this->beginCommand(\"drop comment from table $table\");\n        $this->db->createCommand()->dropCommentFromTable($table)->execute();\n        $this->endCommand($time);\n    }\n\n    /**\n     * Prepares for a command to be executed, and outputs to the console.\n     *\n     * @param string $description the description for the command, to be output to the console.\n     * @return float the time before the command is executed, for the time elapsed to be calculated.\n     * @since 2.0.13\n     */\n    protected function beginCommand($description)\n    {\n        if (!$this->compact) {\n            echo \"    > $description ...\";\n        }\n        return microtime(true);\n    }\n\n    /**\n     * Finalizes after the command has been executed, and outputs to the console the time elapsed.\n     *\n     * @param float $time the time before the command was executed.\n     * @since 2.0.13\n     */\n    protected function endCommand($time)\n    {\n        if (!$this->compact) {\n            echo ' done (time: ' . sprintf('%.3f', microtime(true) - $time) . \"s)\\n\";\n        }\n    }\n}\n"
  },
  {
    "path": "framework/db/MigrationInterface.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\n/**\n * The MigrationInterface defines the minimum set of methods to be implemented by a database migration.\n *\n * Each migration class should provide the [[up()]] method containing the logic for \"upgrading\" the database\n * and the [[down()]] method for the \"downgrading\" logic.\n *\n * @author Klimov Paul <klimov@zfort.com>\n * @since 2.0\n */\ninterface MigrationInterface\n{\n    /**\n     * This method contains the logic to be executed when applying this migration.\n     * @return bool return a false value to indicate the migration fails\n     * and should not proceed further. All other return values mean the migration succeeds.\n     */\n    public function up();\n\n    /**\n     * This method contains the logic to be executed when removing this migration.\n     * The default implementation throws an exception indicating the migration cannot be removed.\n     * @return bool return a false value to indicate the migration fails\n     * and should not proceed further. All other return values mean the migration succeeds.\n     */\n    public function down();\n}\n"
  },
  {
    "path": "framework/db/PdoValue.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\n/**\n * Class PdoValue represents a $value that should be bound to PDO with exact $type.\n *\n * For example, it will be useful when you need to bind binary data to BLOB column in DBMS:\n *\n * ```\n * [':name' => 'John', ':profile' => new PdoValue($profile, \\PDO::PARAM_LOB)]`.\n * ```\n *\n * To see possible types, check [PDO::PARAM_* constants](https://www.php.net/manual/en/pdo.constants.php).\n *\n * @see https://www.php.net/manual/en/pdostatement.bindparam.php\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n * @phpcs:disable Squiz.NamingConventions.ValidVariableName.PrivateNoUnderscore\n */\nfinal class PdoValue implements ExpressionInterface\n{\n    /**\n     * @var mixed\n     */\n    private $value;\n    /**\n     * @var int One of PDO_PARAM_* constants\n     * @see https://www.php.net/manual/en/pdo.constants.php\n     */\n    private $type;\n\n\n    /**\n     * PdoValue constructor.\n     *\n     * @param $value\n     * @param $type\n     */\n    public function __construct($value, $type)\n    {\n        $this->value = $value;\n        $this->type = $type;\n    }\n\n    /**\n     * @return mixed\n     */\n    public function getValue()\n    {\n        return $this->value;\n    }\n\n    /**\n     * @return int\n     */\n    public function getType()\n    {\n        return $this->type;\n    }\n}\n"
  },
  {
    "path": "framework/db/PdoValueBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\n/**\n * Class PdoValueBuilder builds object of the [[PdoValue]] expression class.\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n */\nclass PdoValueBuilder implements ExpressionBuilderInterface\n{\n    public const PARAM_PREFIX = ':pv';\n    /**\n     * {@inheritdoc}\n     */\n    public function build(ExpressionInterface $expression, array &$params = [])\n    {\n        $placeholder = static::PARAM_PREFIX . count($params);\n        $params[$placeholder] = $expression;\n\n        return $placeholder;\n    }\n}\n"
  },
  {
    "path": "framework/db/Query.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\nuse Yii;\nuse yii\\base\\Component;\nuse yii\\base\\InvalidArgumentException;\nuse yii\\helpers\\ArrayHelper;\nuse yii\\base\\InvalidConfigException;\n\n/**\n * Query represents a SELECT SQL statement in a way that is independent of DBMS.\n *\n * Query provides a set of methods to facilitate the specification of different clauses\n * in a SELECT statement. These methods can be chained together.\n *\n * By calling [[createCommand()]], we can get a [[Command]] instance which can be further\n * used to perform/execute the DB query against a database.\n *\n * For example,\n *\n * ```\n * $query = new Query;\n * // compose the query\n * $query->select('id, name')\n *     ->from('user')\n *     ->limit(10);\n * // build and execute the query\n * $rows = $query->all();\n * // alternatively, you can create DB command and execute it\n * $command = $query->createCommand();\n * // $command->sql returns the actual SQL\n * $rows = $command->queryAll();\n * ```\n *\n * Query internally uses the [[QueryBuilder]] class to generate the SQL statement.\n *\n * A more detailed usage guide on how to work with Query can be found in the [guide article on Query Builder](guide:db-query-builder).\n *\n * @property-read string[] $tablesUsedInFrom Table names indexed by aliases.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0\n */\nclass Query extends Component implements QueryInterface, ExpressionInterface\n{\n    use QueryTrait;\n\n    /**\n     * @var array|null the columns being selected. For example, `['id', 'name']`.\n     * This is used to construct the SELECT clause in a SQL statement. If not set, it means selecting all columns.\n     * @see select()\n     */\n    public $select;\n    /**\n     * @var string|null additional option that should be appended to the 'SELECT' keyword. For example,\n     * in MySQL, the option 'SQL_CALC_FOUND_ROWS' can be used.\n     */\n    public $selectOption;\n    /**\n     * @var bool whether to select distinct rows of data only. If this is set true,\n     * the SELECT clause would be changed to SELECT DISTINCT.\n     */\n    public $distinct = false;\n    /**\n     * @var array|null the table(s) to be selected from. For example, `['user', 'post']`.\n     * This is used to construct the FROM clause in a SQL statement.\n     * @see from()\n     */\n    public $from;\n    /**\n     * @var array|null how to group the query results. For example, `['company', 'department']`.\n     * This is used to construct the GROUP BY clause in a SQL statement.\n     */\n    public $groupBy;\n    /**\n     * @var array|null how to join with other tables. Each array element represents the specification\n     * of one join which has the following structure:\n     *\n     * ```\n     * [$joinType, $tableName, $joinCondition]\n     * ```\n     *\n     * For example,\n     *\n     * ```\n     * [\n     *     ['INNER JOIN', 'user', 'user.id = author_id'],\n     *     ['LEFT JOIN', 'team', 'team.id = team_id'],\n     * ]\n     * ```\n     */\n    public $join;\n    /**\n     * @var string|array|ExpressionInterface|null the condition to be applied in the GROUP BY clause.\n     * It can be either a string or an array. Please refer to [[where()]] on how to specify the condition.\n     */\n    public $having;\n    /**\n     * @var array|null this is used to construct the UNION clause(s) in a SQL statement.\n     * Each array element is an array of the following structure:\n     *\n     * - `query`: either a string or a [[Query]] object representing a query\n     * - `all`: boolean, whether it should be `UNION ALL` or `UNION`\n     */\n    public $union;\n    /**\n     * @var array|null this is used to construct the WITH section in a SQL query.\n     * Each array element is an array of the following structure:\n     *\n     * - `query`: either a string or a [[Query]] object representing a query\n     * - `alias`: string, alias of query for further usage\n     * - `recursive`: boolean, whether it should be `WITH RECURSIVE` or `WITH`\n     * @see withQuery()\n     * @since 2.0.35\n     */\n    public $withQueries;\n    /**\n     * @var array|null list of query parameter values indexed by parameter placeholders.\n     * For example, `[':name' => 'Dan', ':age' => 31]`.\n     */\n    public $params = [];\n    /**\n     * @var int|bool|null the default number of seconds that query results can remain valid in cache.\n     * Use 0 to indicate that the cached data will never expire.\n     * Use a negative number to indicate that query cache should not be used.\n     * Use boolean `true` to indicate that [[Connection::queryCacheDuration]] should be used.\n     * @see cache()\n     * @since 2.0.14\n     */\n    public $queryCacheDuration;\n    /**\n     * @var \\yii\\caching\\Dependency|null the dependency to be associated with the cached query result for this query\n     * @see cache()\n     * @since 2.0.14\n     */\n    public $queryCacheDependency;\n\n\n    /**\n     * Creates a DB command that can be used to execute this query.\n     * @param Connection|null $db the database connection used to generate the SQL statement.\n     * If this parameter is not given, the `db` application component will be used.\n     * @return Command the created DB command instance.\n     */\n    public function createCommand($db = null)\n    {\n        if ($db === null) {\n            $db = Yii::$app->getDb();\n        }\n        list($sql, $params) = $db->getQueryBuilder()->build($this);\n\n        $command = $db->createCommand($sql, $params);\n        $this->setCommandCache($command);\n\n        return $command;\n    }\n\n    /**\n     * Prepares for building SQL.\n     * This method is called by [[QueryBuilder]] when it starts to build SQL from a query object.\n     * You may override this method to do some final preparation work when converting a query into a SQL statement.\n     * @param QueryBuilder $builder\n     * @return self a prepared query instance which will be used by [[QueryBuilder]] to build the SQL\n     */\n    public function prepare($builder)\n    {\n        return $this;\n    }\n\n    /**\n     * Starts a batch query.\n     *\n     * A batch query supports fetching data in batches, which can keep the memory usage under a limit.\n     * This method will return a [[BatchQueryResult]] object which implements the [[\\Iterator]] interface\n     * and can be traversed to retrieve the data in batches.\n     *\n     * For example,\n     *\n     * ```\n     * $query = (new Query)->from('user');\n     * foreach ($query->batch() as $rows) {\n     *     // $rows is an array of 100 or fewer rows from user table\n     * }\n     * ```\n     *\n     * @param int $batchSize the number of records to be fetched in each batch.\n     * @param Connection|null $db the database connection. If not set, the \"db\" application component will be used.\n     * @return BatchQueryResult the batch query result. It implements the [[\\Iterator]] interface\n     * and can be traversed to retrieve the data in batches.\n     */\n    public function batch($batchSize = 100, $db = null)\n    {\n        return Yii::createObject([\n            'class' => BatchQueryResult::className(),\n            'query' => $this,\n            'batchSize' => $batchSize,\n            'db' => $db,\n            'each' => false,\n        ]);\n    }\n\n    /**\n     * Starts a batch query and retrieves data row by row.\n     *\n     * This method is similar to [[batch()]] except that in each iteration of the result,\n     * only one row of data is returned. For example,\n     *\n     * ```\n     * $query = (new Query)->from('user');\n     * foreach ($query->each() as $row) {\n     * }\n     * ```\n     *\n     * @param int $batchSize the number of records to be fetched in each batch.\n     * @param Connection|null $db the database connection. If not set, the \"db\" application component will be used.\n     * @return BatchQueryResult the batch query result. It implements the [[\\Iterator]] interface\n     * and can be traversed to retrieve the data in batches.\n     */\n    public function each($batchSize = 100, $db = null)\n    {\n        return Yii::createObject([\n            'class' => BatchQueryResult::className(),\n            'query' => $this,\n            'batchSize' => $batchSize,\n            'db' => $db,\n            'each' => true,\n        ]);\n    }\n\n    /**\n     * Executes the query and returns all results as an array.\n     * @param Connection|null $db the database connection used to generate the SQL statement.\n     * If this parameter is not given, the `db` application component will be used.\n     * @return array the query results. If the query results in nothing, an empty array will be returned.\n     */\n    public function all($db = null)\n    {\n        if ($this->emulateExecution) {\n            return [];\n        }\n\n        $rows = $this->createCommand($db)->queryAll();\n\n        return $this->populate($rows);\n    }\n\n    /**\n     * Converts the raw query results into the format as specified by this query.\n     * This method is internally used to convert the data fetched from database\n     * into the format as required by this query.\n     * @param array $rows the raw query result from database\n     * @return array the converted query result\n     */\n    public function populate($rows)\n    {\n        if ($this->indexBy === null) {\n            return $rows;\n        }\n        $result = [];\n        foreach ($rows as $row) {\n            $result[ArrayHelper::getValue($row, $this->indexBy)] = $row;\n        }\n\n        return $result;\n    }\n\n    /**\n     * Executes the query and returns a single row of result.\n     * @param Connection|null $db the database connection used to generate the SQL statement.\n     * If this parameter is not given, the `db` application component will be used.\n     * @return array|bool the first row (in terms of an array) of the query result. False is returned if the query\n     * results in nothing.\n     */\n    public function one($db = null)\n    {\n        if ($this->emulateExecution) {\n            return false;\n        }\n\n        return $this->createCommand($db)->queryOne();\n    }\n\n    /**\n     * Returns the query result as a scalar value.\n     * The value returned will be the first column in the first row of the query results.\n     * @param Connection|null $db the database connection used to generate the SQL statement.\n     * If this parameter is not given, the `db` application component will be used.\n     * @return string|int|null|false the value of the first column in the first row of the query result.\n     * False is returned if the query result is empty.\n     */\n    public function scalar($db = null)\n    {\n        if ($this->emulateExecution) {\n            return null;\n        }\n\n        return $this->createCommand($db)->queryScalar();\n    }\n\n    /**\n     * Executes the query and returns the first column of the result.\n     * @param Connection|null $db the database connection used to generate the SQL statement.\n     * If this parameter is not given, the `db` application component will be used.\n     * @return array the first column of the query result. An empty array is returned if the query results in nothing.\n     */\n    public function column($db = null)\n    {\n        if ($this->emulateExecution) {\n            return [];\n        }\n\n        if ($this->indexBy === null) {\n            return $this->createCommand($db)->queryColumn();\n        }\n\n        if (is_string($this->indexBy) && is_array($this->select) && count($this->select) === 1) {\n            if (strpos($this->indexBy, '.') === false && count($tables = $this->getTablesUsedInFrom()) > 0) {\n                $this->select[] = key($tables) . '.' . $this->indexBy;\n            } else {\n                $this->select[] = $this->indexBy;\n            }\n        }\n        $rows = $this->createCommand($db)->queryAll();\n        $results = [];\n        $column = null;\n        if (is_string($this->indexBy)) {\n            if (($dotPos = strpos($this->indexBy, '.')) === false) {\n                $column = $this->indexBy;\n            } else {\n                $column = substr($this->indexBy, $dotPos + 1);\n            }\n        }\n        foreach ($rows as $row) {\n            $value = reset($row);\n\n            if ($this->indexBy instanceof \\Closure) {\n                $results[call_user_func($this->indexBy, $row)] = $value;\n            } else {\n                $results[$row[$column]] = $value;\n            }\n        }\n\n        return $results;\n    }\n\n    /**\n     * Returns the number of records.\n     * @param string $q the COUNT expression. Defaults to '*'.\n     * Make sure you properly [quote](guide:db-dao#quoting-table-and-column-names) column names in the expression.\n     * @param Connection|null $db the database connection used to generate the SQL statement.\n     * If this parameter is not given (or null), the `db` application component will be used.\n     * @return int|string|null number of records. The result may be a string depending on the\n     * underlying database engine and to support integer values higher than a 32bit PHP integer can handle.\n     */\n    public function count($q = '*', $db = null)\n    {\n        if ($this->emulateExecution) {\n            return 0;\n        }\n\n        return $this->queryScalar(\"COUNT($q)\", $db);\n    }\n\n    /**\n     * Returns the sum of the specified column values.\n     * @param string $q the column name or expression.\n     * Make sure you properly [quote](guide:db-dao#quoting-table-and-column-names) column names in the expression.\n     * @param Connection|null $db the database connection used to generate the SQL statement.\n     * If this parameter is not given, the `db` application component will be used.\n     * @return mixed the sum of the specified column values.\n     */\n    public function sum($q, $db = null)\n    {\n        if ($this->emulateExecution) {\n            return 0;\n        }\n\n        return $this->queryScalar(\"SUM($q)\", $db);\n    }\n\n    /**\n     * Returns the average of the specified column values.\n     * @param string $q the column name or expression.\n     * Make sure you properly [quote](guide:db-dao#quoting-table-and-column-names) column names in the expression.\n     * @param Connection|null $db the database connection used to generate the SQL statement.\n     * If this parameter is not given, the `db` application component will be used.\n     * @return mixed the average of the specified column values.\n     */\n    public function average($q, $db = null)\n    {\n        if ($this->emulateExecution) {\n            return 0;\n        }\n\n        return $this->queryScalar(\"AVG($q)\", $db);\n    }\n\n    /**\n     * Returns the minimum of the specified column values.\n     * @param string $q the column name or expression.\n     * Make sure you properly [quote](guide:db-dao#quoting-table-and-column-names) column names in the expression.\n     * @param Connection|null $db the database connection used to generate the SQL statement.\n     * If this parameter is not given, the `db` application component will be used.\n     * @return mixed the minimum of the specified column values.\n     */\n    public function min($q, $db = null)\n    {\n        return $this->queryScalar(\"MIN($q)\", $db);\n    }\n\n    /**\n     * Returns the maximum of the specified column values.\n     * @param string $q the column name or expression.\n     * Make sure you properly [quote](guide:db-dao#quoting-table-and-column-names) column names in the expression.\n     * @param Connection|null $db the database connection used to generate the SQL statement.\n     * If this parameter is not given, the `db` application component will be used.\n     * @return mixed the maximum of the specified column values.\n     */\n    public function max($q, $db = null)\n    {\n        return $this->queryScalar(\"MAX($q)\", $db);\n    }\n\n    /**\n     * Returns a value indicating whether the query result contains any row of data.\n     * @param Connection|null $db the database connection used to generate the SQL statement.\n     * If this parameter is not given, the `db` application component will be used.\n     * @return bool whether the query result contains any row of data.\n     */\n    public function exists($db = null)\n    {\n        if ($this->emulateExecution) {\n            return false;\n        }\n        $command = $this->createCommand($db);\n        $params = $command->params;\n        $command->setSql($command->db->getQueryBuilder()->selectExists($command->getSql()));\n        $command->bindValues($params);\n        return (bool) $command->queryScalar();\n    }\n\n    /**\n     * Queries a scalar value by setting [[select]] first.\n     * Restores the value of select to make this query reusable.\n     * @param string|ExpressionInterface $selectExpression\n     * @param Connection|null $db the database connection used to execute the query.\n     * @return bool|string|null\n     * @throws \\Throwable if can't create command\n     */\n    protected function queryScalar($selectExpression, $db)\n    {\n        if ($this->emulateExecution) {\n            return null;\n        }\n\n        if (\n            !$this->distinct\n            && empty($this->groupBy)\n            && empty($this->having)\n            && empty($this->union)\n        ) {\n            $select = $this->select;\n            $order = $this->orderBy;\n            $limit = $this->limit;\n            $offset = $this->offset;\n\n            $this->select = [$selectExpression];\n            $this->orderBy = null;\n            $this->limit = null;\n            $this->offset = null;\n\n            $e = null;\n            try {\n                $command = $this->createCommand($db);\n            } catch (\\Exception $e) {\n                // throw it later (for PHP < 7.0)\n            } catch (\\Throwable $e) {\n                // throw it later\n            }\n\n            $this->select = $select;\n            $this->orderBy = $order;\n            $this->limit = $limit;\n            $this->offset = $offset;\n\n            if ($e !== null) {\n                throw $e;\n            }\n\n            return $command->queryScalar();\n        }\n\n        $command = (new self())\n            ->select([$selectExpression])\n            ->from(['c' => $this])\n            ->createCommand($db);\n        $this->setCommandCache($command);\n\n        return $command->queryScalar();\n    }\n\n    /**\n     * Returns table names used in [[from]] indexed by aliases.\n     * Both aliases and names are enclosed into {{ and }}.\n     * @return string[] table names indexed by aliases\n     * @throws \\yii\\base\\InvalidConfigException\n     * @since 2.0.12\n     */\n    public function getTablesUsedInFrom()\n    {\n        if (empty($this->from)) {\n            return [];\n        }\n\n        if (is_array($this->from)) {\n            $tableNames = $this->from;\n        } elseif (is_string($this->from)) {\n            $tableNames = preg_split('/\\s*,\\s*/', trim($this->from), -1, PREG_SPLIT_NO_EMPTY);\n        } elseif ($this->from instanceof Expression) {\n            $tableNames = [$this->from];\n        } else {\n            throw new InvalidConfigException(gettype($this->from) . ' in $from is not supported.');\n        }\n\n        return $this->cleanUpTableNames($tableNames);\n    }\n\n    /**\n     * Clean up table names and aliases\n     * Both aliases and names are enclosed into {{ and }}.\n     * @param array $tableNames non-empty array\n     * @return string[] table names indexed by aliases\n     * @since 2.0.14\n     */\n    protected function cleanUpTableNames($tableNames)\n    {\n        $cleanedUpTableNames = [];\n        foreach ($tableNames as $alias => $tableName) {\n            if (is_string($tableName) && !is_string($alias)) {\n                $pattern = <<<PATTERN\n~\n^\n\\s*\n(\n(?:['\"`\\[]|{{)\n.*?\n(?:['\"`\\]]|}})\n|\n\\(.*?\\)\n|\n.*?\n)\n(?:\n(?:\n    \\s+\n    (?:as)?\n    \\s*\n)\n(\n   (?:['\"`\\[]|{{)\n    .*?\n    (?:['\"`\\]]|}})\n    |\n    .*?\n)\n)?\n\\s*\n$\n~iux\nPATTERN;\n                if (preg_match($pattern, $tableName, $matches)) {\n                    if (isset($matches[2])) {\n                        list(, $tableName, $alias) = $matches;\n                    } else {\n                        $tableName = $alias = $matches[1];\n                    }\n                }\n            }\n\n\n            if ($tableName instanceof Expression) {\n                if (!is_string($alias)) {\n                    throw new InvalidArgumentException('To use Expression in from() method, pass it in array format with alias.');\n                }\n                $cleanedUpTableNames[$this->ensureNameQuoted($alias)] = $tableName;\n            } elseif ($tableName instanceof self) {\n                $cleanedUpTableNames[$this->ensureNameQuoted($alias)] = $tableName;\n            } else {\n                $cleanedUpTableNames[$this->ensureNameQuoted($alias)] = $this->ensureNameQuoted($tableName);\n            }\n        }\n\n        return $cleanedUpTableNames;\n    }\n\n    /**\n     * Ensures name is wrapped with {{ and }}\n     * @param string $name\n     * @return string\n     */\n    private function ensureNameQuoted($name)\n    {\n        $name = str_replace([\"'\", '\"', '`', '[', ']'], '', $name);\n        if ($name && !preg_match('/^{{.*}}$/', $name)) {\n            return '{{' . $name . '}}';\n        }\n\n        return $name;\n    }\n\n    /**\n     * Sets the SELECT part of the query.\n     * @param string|array|ExpressionInterface $columns the columns to be selected.\n     * Columns can be specified in either a string (e.g. \"id, name\") or an array (e.g. ['id', 'name']).\n     * Columns can be prefixed with table names (e.g. \"user.id\") and/or contain column aliases (e.g. \"user.id AS user_id\").\n     * The method will automatically quote the column names unless a column contains some parenthesis\n     * (which means the column contains a DB expression). A DB expression may also be passed in form of\n     * an [[ExpressionInterface]] object.\n     *\n     * Note that if you are selecting an expression like `CONCAT(first_name, ' ', last_name)`, you should\n     * use an array to specify the columns. Otherwise, the expression may be incorrectly split into several parts.\n     *\n     * When the columns are specified as an array, you may also use array keys as the column aliases (if a column\n     * does not need alias, do not use a string key).\n     *\n     * Starting from version 2.0.1, you may also select sub-queries as columns by specifying each such column\n     * as a `Query` instance representing the sub-query.\n     *\n     * @param string|null $option additional option that should be appended to the 'SELECT' keyword. For example,\n     * in MySQL, the option 'SQL_CALC_FOUND_ROWS' can be used.\n     * @return $this the query object itself\n     */\n    public function select($columns, $option = null)\n    {\n        $this->select = $this->normalizeSelect($columns);\n        $this->selectOption = $option;\n        return $this;\n    }\n\n    /**\n     * Add more columns to the SELECT part of the query.\n     *\n     * Note, that if [[select]] has not been specified before, you should include `*` explicitly\n     * if you want to select all remaining columns too:\n     *\n     * ```\n     * $query->addSelect([\"*\", \"CONCAT(first_name, ' ', last_name) AS full_name\"])->one();\n     * ```\n     *\n     * @param string|array|ExpressionInterface $columns the columns to add to the select. See [[select()]] for more\n     * details about the format of this parameter.\n     * @return $this the query object itself\n     * @see select()\n     */\n    public function addSelect($columns)\n    {\n        if ($this->select === null) {\n            return $this->select($columns);\n        }\n        if (!is_array($this->select)) {\n            $this->select = $this->normalizeSelect($this->select);\n        }\n        $this->select = array_merge($this->select, $this->normalizeSelect($columns));\n\n        return $this;\n    }\n\n    /**\n     * Normalizes the SELECT columns passed to [[select()]] or [[addSelect()]].\n     *\n     * @param string|array|ExpressionInterface $columns\n     * @return array\n     * @since 2.0.21\n     */\n    protected function normalizeSelect($columns)\n    {\n        if ($columns instanceof ExpressionInterface) {\n            $columns = [$columns];\n        } elseif (!is_array($columns)) {\n            $columns = preg_split('/\\s*,\\s*/', trim((string)$columns), -1, PREG_SPLIT_NO_EMPTY);\n        }\n        $select = [];\n        foreach ($columns as $columnAlias => $columnDefinition) {\n            if (is_string($columnAlias)) {\n                // Already in the normalized format, good for them\n                $select[$columnAlias] = $columnDefinition;\n                continue;\n            }\n            if (is_string($columnDefinition)) {\n                if (\n                    preg_match('/^(.*?)(?i:\\s+as\\s+|\\s+)([\\w\\-_\\.]+)$/', $columnDefinition, $matches) &&\n                    !preg_match('/^\\d+$/', $matches[2]) &&\n                    strpos($matches[2], '.') === false\n                ) {\n                    // Using \"columnName as alias\" or \"columnName alias\" syntax\n                    $select[$matches[2]] = $matches[1];\n                    continue;\n                }\n                if (strpos($columnDefinition, '(') === false) {\n                    // Normal column name, just alias it to itself to ensure it's not selected twice\n                    $select[$columnDefinition] = $columnDefinition;\n                    continue;\n                }\n            }\n            // Either a string calling a function, DB expression, or sub-query\n            $select[] = $columnDefinition;\n        }\n        return $select;\n    }\n\n    /**\n     * Returns unique column names excluding duplicates.\n     * Columns to be removed:\n     * - if column definition already present in SELECT part with same alias\n     * - if column definition without alias already present in SELECT part without alias too\n     * @param array $columns the columns to be merged to the select.\n     * @since 2.0.14\n     * @deprecated in 2.0.21\n     */\n    protected function getUniqueColumns($columns)\n    {\n        $unaliasedColumns = $this->getUnaliasedColumnsFromSelect();\n\n        $result = [];\n        foreach ($columns as $columnAlias => $columnDefinition) {\n            if (!$columnDefinition instanceof Query) {\n                if (is_string($columnAlias)) {\n                    $existsInSelect = isset($this->select[$columnAlias]) && $this->select[$columnAlias] === $columnDefinition;\n                    if ($existsInSelect) {\n                        continue;\n                    }\n                } elseif (is_int($columnAlias)) {\n                    $existsInSelect = in_array($columnDefinition, $unaliasedColumns, true);\n                    $existsInResultSet = in_array($columnDefinition, $result, true);\n                    if ($existsInSelect || $existsInResultSet) {\n                        continue;\n                    }\n                }\n            }\n\n            $result[$columnAlias] = $columnDefinition;\n        }\n        return $result;\n    }\n\n    /**\n     * @return array List of columns without aliases from SELECT statement.\n     * @since 2.0.14\n     * @deprecated in 2.0.21\n     */\n    protected function getUnaliasedColumnsFromSelect()\n    {\n        $result = [];\n        if (is_array($this->select)) {\n            foreach ($this->select as $name => $value) {\n                if (is_int($name)) {\n                    $result[] = $value;\n                }\n            }\n        }\n        return array_unique($result);\n    }\n\n    /**\n     * Sets the value indicating whether to SELECT DISTINCT or not.\n     * @param bool $value whether to SELECT DISTINCT or not.\n     * @return $this the query object itself\n     */\n    public function distinct($value = true)\n    {\n        $this->distinct = $value;\n        return $this;\n    }\n\n    /**\n     * Sets the FROM part of the query.\n     * @param string|array|ExpressionInterface $tables the table(s) to be selected from. This can be either a string (e.g. `'user'`)\n     * or an array (e.g. `['user', 'profile']`) specifying one or several table names.\n     * Table names can contain schema prefixes (e.g. `'public.user'`) and/or table aliases (e.g. `'user u'`).\n     * The method will automatically quote the table names unless it contains some parenthesis\n     * (which means the table is given as a sub-query or DB expression).\n     *\n     * When the tables are specified as an array, you may also use the array keys as the table aliases\n     * (if a table does not need alias, do not use a string key).\n     *\n     * Use a Query object to represent a sub-query. In this case, the corresponding array key will be used\n     * as the alias for the sub-query.\n     *\n     * To specify the `FROM` part in plain SQL, you may pass an instance of [[ExpressionInterface]].\n     *\n     * Here are some examples:\n     *\n     * ```\n     * // SELECT * FROM  `user` `u`, `profile`;\n     * $query = (new \\yii\\db\\Query)->from(['u' => 'user', 'profile']);\n     *\n     * // SELECT * FROM (SELECT * FROM `user` WHERE `active` = 1) `activeusers`;\n     * $subquery = (new \\yii\\db\\Query)->from('user')->where(['active' => true])\n     * $query = (new \\yii\\db\\Query)->from(['activeusers' => $subquery]);\n     *\n     * // subquery can also be a string with plain SQL wrapped in parenthesis\n     * // SELECT * FROM (SELECT * FROM `user` WHERE `active` = 1) `activeusers`;\n     * $subquery = \"(SELECT * FROM `user` WHERE `active` = 1)\";\n     * $query = (new \\yii\\db\\Query)->from(['activeusers' => $subquery]);\n     * ```\n     *\n     * @return $this the query object itself\n     */\n    public function from($tables)\n    {\n        if ($tables instanceof ExpressionInterface) {\n            $tables = [$tables];\n        }\n        if (is_string($tables)) {\n            $tables = preg_split('/\\s*,\\s*/', trim($tables), -1, PREG_SPLIT_NO_EMPTY);\n        }\n        $this->from = $tables;\n        return $this;\n    }\n\n    /**\n     * Sets the WHERE part of the query.\n     *\n     * The method requires a `$condition` parameter, and optionally a `$params` parameter\n     * specifying the values to be bound to the query.\n     *\n     * The `$condition` parameter should be either a string (e.g. `'id=1'`) or an array.\n     *\n     * {@inheritdoc}\n     *\n     * @param string|array|ExpressionInterface $condition the conditions that should be put in the WHERE part.\n     * @param array $params the parameters (name => value) to be bound to the query.\n     * @return $this the query object itself\n     * @see andWhere()\n     * @see orWhere()\n     * @see QueryInterface::where()\n     */\n    public function where($condition, $params = [])\n    {\n        $this->where = $condition;\n        $this->addParams($params);\n        return $this;\n    }\n\n    /**\n     * Adds an additional WHERE condition to the existing one.\n     * The new condition and the existing one will be joined using the `AND` operator.\n     * @param string|array|ExpressionInterface $condition the new WHERE condition. Please refer to [[where()]]\n     * on how to specify this parameter.\n     * @param array $params the parameters (name => value) to be bound to the query.\n     * @return $this the query object itself\n     * @see where()\n     * @see orWhere()\n     */\n    public function andWhere($condition, $params = [])\n    {\n        if ($this->where === null) {\n            $this->where = $condition;\n        } elseif (is_array($this->where) && isset($this->where[0]) && strcasecmp($this->where[0], 'and') === 0) {\n            $this->where[] = $condition;\n        } else {\n            $this->where = ['and', $this->where, $condition];\n        }\n        $this->addParams($params);\n        return $this;\n    }\n\n    /**\n     * Adds an additional WHERE condition to the existing one.\n     * The new condition and the existing one will be joined using the `OR` operator.\n     * @param string|array|ExpressionInterface $condition the new WHERE condition. Please refer to [[where()]]\n     * on how to specify this parameter.\n     * @param array $params the parameters (name => value) to be bound to the query.\n     * @return $this the query object itself\n     * @see where()\n     * @see andWhere()\n     */\n    public function orWhere($condition, $params = [])\n    {\n        if ($this->where === null) {\n            $this->where = $condition;\n        } else {\n            $this->where = ['or', $this->where, $condition];\n        }\n        $this->addParams($params);\n        return $this;\n    }\n\n    /**\n     * Adds a filtering condition for a specific column and allow the user to choose a filter operator.\n     *\n     * It adds an additional WHERE condition for the given field and determines the comparison operator\n     * based on the first few characters of the given value.\n     * The condition is added in the same way as in [[andFilterWhere]] so [[isEmpty()|empty values]] are ignored.\n     * The new condition and the existing one will be joined using the `AND` operator.\n     *\n     * The comparison operator is intelligently determined based on the first few characters in the given value.\n     * In particular, it recognizes the following operators if they appear as the leading characters in the given value:\n     *\n     * - `<`: the column must be less than the given value.\n     * - `>`: the column must be greater than the given value.\n     * - `<=`: the column must be less than or equal to the given value.\n     * - `>=`: the column must be greater than or equal to the given value.\n     * - `<>`: the column must not be the same as the given value.\n     * - `=`: the column must be equal to the given value.\n     * - If none of the above operators is detected, the `$defaultOperator` will be used.\n     *\n     * @param string $name the column name.\n     * @param string $value the column value optionally prepended with the comparison operator.\n     * @param string $defaultOperator The operator to use, when no operator is given in `$value`.\n     * Defaults to `=`, performing an exact match.\n     * @return $this The query object itself\n     * @since 2.0.8\n     */\n    public function andFilterCompare($name, $value, $defaultOperator = '=')\n    {\n        if (preg_match('/^(<>|>=|>|<=|<|=)/', (string)$value, $matches)) {\n            $operator = $matches[1];\n            $value = substr($value, strlen($operator));\n        } else {\n            $operator = $defaultOperator;\n        }\n\n        return $this->andFilterWhere([$operator, $name, $value]);\n    }\n\n    /**\n     * Appends a JOIN part to the query.\n     * The first parameter specifies what type of join it is.\n     * @param string $type the type of join, such as INNER JOIN, LEFT JOIN.\n     * @param string|array $table the table or sub-query to be joined.\n     *\n     * Use a string to represent the name of the table to be joined.\n     * The table name can contain a schema prefix (e.g. 'public.user') and/or table alias (e.g. 'user u').\n     * The method will automatically quote the table name unless it contains some parenthesis\n     * (which means the table is given as a sub-query or DB expression).\n     *\n     * You may also specify the table as an array with one element, using the array key as the table alias\n     * (e.g. ['u' => 'user']).\n     *\n     * To join a sub-query, use an array with one element, with the value set to a [[Query]] object\n     * representing the sub-query, and the corresponding key representing the alias.\n     *\n     * @param string|array $on the join condition that should appear in the ON part.\n     * Please refer to [[where()]] on how to specify this parameter.\n     *\n     * Note that the array format of [[where()]] is designed to match columns to values instead of columns to columns, so\n     * the following would **not** work as expected: `['post.author_id' => 'user.id']`, it would\n     * match the `post.author_id` column value against the string `'user.id'`.\n     * It is recommended to use the string syntax here which is more suited for a join:\n     *\n     * ```\n     * 'post.author_id = user.id'\n     * ```\n     *\n     * @param array $params the parameters (name => value) to be bound to the query.\n     * @return $this the query object itself\n     */\n    public function join($type, $table, $on = '', $params = [])\n    {\n        $this->join[] = [$type, $table, $on];\n        return $this->addParams($params);\n    }\n\n    /**\n     * Appends an INNER JOIN part to the query.\n     * @param string|array $table the table or sub-query to be joined.\n     *\n     * Use a string to represent the name of the table to be joined.\n     * The table name can contain a schema prefix (e.g. 'public.user') and/or table alias (e.g. 'user u').\n     * The method will automatically quote the table name unless it contains some parenthesis\n     * (which means the table is given as a sub-query or DB expression).\n     *\n     * You may also specify the table as an array with one element, using the array key as the table alias\n     * (e.g. ['u' => 'user']).\n     *\n     * To join a sub-query, use an array with one element, with the value set to a [[Query]] object\n     * representing the sub-query, and the corresponding key representing the alias.\n     *\n     * @param string|array $on the join condition that should appear in the ON part.\n     * Please refer to [[join()]] on how to specify this parameter.\n     * @param array $params the parameters (name => value) to be bound to the query.\n     * @return $this the query object itself\n     */\n    public function innerJoin($table, $on = '', $params = [])\n    {\n        $this->join[] = ['INNER JOIN', $table, $on];\n        return $this->addParams($params);\n    }\n\n    /**\n     * Appends a LEFT OUTER JOIN part to the query.\n     * @param string|array $table the table or sub-query to be joined.\n     *\n     * Use a string to represent the name of the table to be joined.\n     * The table name can contain a schema prefix (e.g. 'public.user') and/or table alias (e.g. 'user u').\n     * The method will automatically quote the table name unless it contains some parenthesis\n     * (which means the table is given as a sub-query or DB expression).\n     *\n     * You may also specify the table as an array with one element, using the array key as the table alias\n     * (e.g. ['u' => 'user']).\n     *\n     * To join a sub-query, use an array with one element, with the value set to a [[Query]] object\n     * representing the sub-query, and the corresponding key representing the alias.\n     *\n     * @param string|array $on the join condition that should appear in the ON part.\n     * Please refer to [[join()]] on how to specify this parameter.\n     * @param array $params the parameters (name => value) to be bound to the query\n     * @return $this the query object itself\n     */\n    public function leftJoin($table, $on = '', $params = [])\n    {\n        $this->join[] = ['LEFT JOIN', $table, $on];\n        return $this->addParams($params);\n    }\n\n    /**\n     * Appends a RIGHT OUTER JOIN part to the query.\n     * @param string|array $table the table or sub-query to be joined.\n     *\n     * Use a string to represent the name of the table to be joined.\n     * The table name can contain a schema prefix (e.g. 'public.user') and/or table alias (e.g. 'user u').\n     * The method will automatically quote the table name unless it contains some parenthesis\n     * (which means the table is given as a sub-query or DB expression).\n     *\n     * You may also specify the table as an array with one element, using the array key as the table alias\n     * (e.g. ['u' => 'user']).\n     *\n     * To join a sub-query, use an array with one element, with the value set to a [[Query]] object\n     * representing the sub-query, and the corresponding key representing the alias.\n     *\n     * @param string|array $on the join condition that should appear in the ON part.\n     * Please refer to [[join()]] on how to specify this parameter.\n     * @param array $params the parameters (name => value) to be bound to the query\n     * @return $this the query object itself\n     */\n    public function rightJoin($table, $on = '', $params = [])\n    {\n        $this->join[] = ['RIGHT JOIN', $table, $on];\n        return $this->addParams($params);\n    }\n\n    /**\n     * Sets the GROUP BY part of the query.\n     * @param string|array|ExpressionInterface|null $columns the columns to be grouped by.\n     * Columns can be specified in either a string (e.g. \"id, name\") or an array (e.g. ['id', 'name']).\n     * The method will automatically quote the column names unless a column contains some parenthesis\n     * (which means the column contains a DB expression).\n     *\n     * Note that if your group-by is an expression containing commas, you should always use an array\n     * to represent the group-by information. Otherwise, the method will not be able to correctly determine\n     * the group-by columns.\n     *\n     * Since version 2.0.7, an [[ExpressionInterface]] object can be passed to specify the GROUP BY part explicitly in plain SQL.\n     * Since version 2.0.14, an [[ExpressionInterface]] object can be passed as well.\n     * @return $this the query object itself\n     * @see addGroupBy()\n     */\n    public function groupBy($columns)\n    {\n        if ($columns instanceof ExpressionInterface) {\n            $columns = [$columns];\n        } elseif (!is_array($columns) && !is_null($columns)) {\n            $columns = preg_split('/\\s*,\\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);\n        }\n        $this->groupBy = $columns;\n        return $this;\n    }\n\n    /**\n     * Adds additional group-by columns to the existing ones.\n     * @param string|array|ExpressionInterface $columns additional columns to be grouped by.\n     * Columns can be specified in either a string (e.g. \"id, name\") or an array (e.g. ['id', 'name']).\n     * The method will automatically quote the column names unless a column contains some parenthesis\n     * (which means the column contains a DB expression).\n     *\n     * Note that if your group-by is an expression containing commas, you should always use an array\n     * to represent the group-by information. Otherwise, the method will not be able to correctly determine\n     * the group-by columns.\n     *\n     * Since version 2.0.7, an [[Expression]] object can be passed to specify the GROUP BY part explicitly in plain SQL.\n     * Since version 2.0.14, an [[ExpressionInterface]] object can be passed as well.\n     * @return $this the query object itself\n     * @see groupBy()\n     */\n    public function addGroupBy($columns)\n    {\n        if ($columns instanceof ExpressionInterface) {\n            $columns = [$columns];\n        } elseif (!is_array($columns)) {\n            $columns = preg_split('/\\s*,\\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);\n        }\n        if ($this->groupBy === null) {\n            $this->groupBy = $columns;\n        } else {\n            $this->groupBy = array_merge($this->groupBy, $columns);\n        }\n\n        return $this;\n    }\n\n    /**\n     * Sets the HAVING part of the query.\n     * @param string|array|ExpressionInterface $condition the conditions to be put after HAVING.\n     * Please refer to [[where()]] on how to specify this parameter.\n     * @param array $params the parameters (name => value) to be bound to the query.\n     * @return $this the query object itself\n     * @see andHaving()\n     * @see orHaving()\n     */\n    public function having($condition, $params = [])\n    {\n        $this->having = $condition;\n        $this->addParams($params);\n        return $this;\n    }\n\n    /**\n     * Adds an additional HAVING condition to the existing one.\n     * The new condition and the existing one will be joined using the `AND` operator.\n     * @param string|array|ExpressionInterface $condition the new HAVING condition. Please refer to [[where()]]\n     * on how to specify this parameter.\n     * @param array $params the parameters (name => value) to be bound to the query.\n     * @return $this the query object itself\n     * @see having()\n     * @see orHaving()\n     */\n    public function andHaving($condition, $params = [])\n    {\n        if ($this->having === null) {\n            $this->having = $condition;\n        } else {\n            $this->having = ['and', $this->having, $condition];\n        }\n        $this->addParams($params);\n        return $this;\n    }\n\n    /**\n     * Adds an additional HAVING condition to the existing one.\n     * The new condition and the existing one will be joined using the `OR` operator.\n     * @param string|array|ExpressionInterface $condition the new HAVING condition. Please refer to [[where()]]\n     * on how to specify this parameter.\n     * @param array $params the parameters (name => value) to be bound to the query.\n     * @return $this the query object itself\n     * @see having()\n     * @see andHaving()\n     */\n    public function orHaving($condition, $params = [])\n    {\n        if ($this->having === null) {\n            $this->having = $condition;\n        } else {\n            $this->having = ['or', $this->having, $condition];\n        }\n        $this->addParams($params);\n        return $this;\n    }\n\n    /**\n     * Sets the HAVING part of the query but ignores [[isEmpty()|empty operands]].\n     *\n     * This method is similar to [[having()]]. The main difference is that this method will\n     * remove [[isEmpty()|empty query operands]]. As a result, this method is best suited\n     * for building query conditions based on filter values entered by users.\n     *\n     * The following code shows the difference between this method and [[having()]]:\n     *\n     * ```\n     * // HAVING `age`=:age\n     * $query->filterHaving(['name' => null, 'age' => 20]);\n     * // HAVING `age`=:age\n     * $query->having(['age' => 20]);\n     * // HAVING `name` IS NULL AND `age`=:age\n     * $query->having(['name' => null, 'age' => 20]);\n     * ```\n     *\n     * Note that unlike [[having()]], you cannot pass binding parameters to this method.\n     *\n     * @param array $condition the conditions that should be put in the HAVING part.\n     * See [[having()]] on how to specify this parameter.\n     * @return $this the query object itself\n     * @see having()\n     * @see andFilterHaving()\n     * @see orFilterHaving()\n     * @since 2.0.11\n     */\n    public function filterHaving(array $condition)\n    {\n        $condition = $this->filterCondition($condition);\n        if ($condition !== []) {\n            $this->having($condition);\n        }\n\n        return $this;\n    }\n\n    /**\n     * Adds an additional HAVING condition to the existing one but ignores [[isEmpty()|empty operands]].\n     * The new condition and the existing one will be joined using the `AND` operator.\n     *\n     * This method is similar to [[andHaving()]]. The main difference is that this method will\n     * remove [[isEmpty()|empty query operands]]. As a result, this method is best suited\n     * for building query conditions based on filter values entered by users.\n     *\n     * @param array $condition the new HAVING condition. Please refer to [[having()]]\n     * on how to specify this parameter.\n     * @return $this the query object itself\n     * @see filterHaving()\n     * @see orFilterHaving()\n     * @since 2.0.11\n     */\n    public function andFilterHaving(array $condition)\n    {\n        $condition = $this->filterCondition($condition);\n        if ($condition !== []) {\n            $this->andHaving($condition);\n        }\n\n        return $this;\n    }\n\n    /**\n     * Adds an additional HAVING condition to the existing one but ignores [[isEmpty()|empty operands]].\n     * The new condition and the existing one will be joined using the `OR` operator.\n     *\n     * This method is similar to [[orHaving()]]. The main difference is that this method will\n     * remove [[isEmpty()|empty query operands]]. As a result, this method is best suited\n     * for building query conditions based on filter values entered by users.\n     *\n     * @param array $condition the new HAVING condition. Please refer to [[having()]]\n     * on how to specify this parameter.\n     * @return $this the query object itself\n     * @see filterHaving()\n     * @see andFilterHaving()\n     * @since 2.0.11\n     */\n    public function orFilterHaving(array $condition)\n    {\n        $condition = $this->filterCondition($condition);\n        if ($condition !== []) {\n            $this->orHaving($condition);\n        }\n\n        return $this;\n    }\n\n    /**\n     * Appends a SQL statement using UNION operator.\n     * @param string|Query $sql the SQL statement to be appended using UNION\n     * @param bool $all TRUE if using UNION ALL and FALSE if using UNION\n     * @return $this the query object itself\n     */\n    public function union($sql, $all = false)\n    {\n        $this->union[] = ['query' => $sql, 'all' => $all];\n        return $this;\n    }\n\n    /**\n     * Prepends a SQL statement using WITH syntax.\n     * @param string|Query $query the SQL statement to be prepended using WITH\n     * @param string $alias query alias in WITH construction\n     * @param bool $recursive TRUE if using WITH RECURSIVE and FALSE if using WITH\n     * @return $this the query object itself\n     * @since 2.0.35\n     */\n    public function withQuery($query, $alias, $recursive = false)\n    {\n        $this->withQueries[] = ['query' => $query, 'alias' => $alias, 'recursive' => $recursive];\n        return $this;\n    }\n\n    /**\n     * Sets the parameters to be bound to the query.\n     * @param array $params list of query parameter values indexed by parameter placeholders.\n     * For example, `[':name' => 'Dan', ':age' => 31]`.\n     * @return $this the query object itself\n     * @see addParams()\n     */\n    public function params($params)\n    {\n        $this->params = $params;\n        return $this;\n    }\n\n    /**\n     * Adds additional parameters to be bound to the query.\n     * @param array $params list of query parameter values indexed by parameter placeholders.\n     * For example, `[':name' => 'Dan', ':age' => 31]`.\n     * @return $this the query object itself\n     * @see params()\n     */\n    public function addParams($params)\n    {\n        if (!empty($params)) {\n            if (empty($this->params)) {\n                $this->params = $params;\n            } else {\n                foreach ($params as $name => $value) {\n                    if (is_int($name)) {\n                        $this->params[] = $value;\n                    } else {\n                        $this->params[$name] = $value;\n                    }\n                }\n            }\n        }\n\n        return $this;\n    }\n\n    /**\n     * Enables query cache for this Query.\n     * @param int|true $duration the number of seconds that query results can remain valid in cache.\n     * Use 0 to indicate that the cached data will never expire.\n     * Use a negative number to indicate that query cache should not be used.\n     * Use boolean `true` to indicate that [[Connection::queryCacheDuration]] should be used.\n     * Defaults to `true`.\n     * @param \\yii\\caching\\Dependency|null $dependency the cache dependency associated with the cached result.\n     * @return $this the Query object itself\n     * @since 2.0.14\n     */\n    public function cache($duration = true, $dependency = null)\n    {\n        $this->queryCacheDuration = $duration;\n        $this->queryCacheDependency = $dependency;\n        return $this;\n    }\n\n    /**\n     * Disables query cache for this Query.\n     * @return $this the Query object itself\n     * @since 2.0.14\n     */\n    public function noCache()\n    {\n        $this->queryCacheDuration = -1;\n        return $this;\n    }\n\n    /**\n     * Sets $command cache, if this query has enabled caching.\n     *\n     * @param Command $command\n     * @return Command\n     * @since 2.0.14\n     */\n    protected function setCommandCache($command)\n    {\n        if ($this->queryCacheDuration !== null || $this->queryCacheDependency !== null) {\n            $duration = $this->queryCacheDuration === true ? null : $this->queryCacheDuration;\n            $command->cache($duration, $this->queryCacheDependency);\n        }\n\n        return $command;\n    }\n\n    /**\n     * Creates a new Query object and copies its property values from an existing one.\n     * The properties being copies are the ones to be used by query builders.\n     * @param Query $from the source query object\n     * @return Query the new Query object\n     */\n    public static function create($from)\n    {\n        return new self([\n            'where' => $from->where,\n            'limit' => $from->limit,\n            'offset' => $from->offset,\n            'orderBy' => $from->orderBy,\n            'indexBy' => $from->indexBy,\n            'select' => $from->select,\n            'selectOption' => $from->selectOption,\n            'distinct' => $from->distinct,\n            'from' => $from->from,\n            'groupBy' => $from->groupBy,\n            'join' => $from->join,\n            'having' => $from->having,\n            'union' => $from->union,\n            'params' => $from->params,\n            'withQueries' => $from->withQueries,\n        ]);\n    }\n\n    /**\n     * Returns the SQL representation of Query\n     * @return string\n     */\n    public function __toString()\n    {\n        return serialize($this);\n    }\n}\n"
  },
  {
    "path": "framework/db/QueryBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\nuse yii\\base\\InvalidArgumentException;\nuse yii\\base\\NotSupportedException;\nuse yii\\db\\conditions\\ConditionInterface;\nuse yii\\db\\conditions\\HashCondition;\nuse yii\\helpers\\StringHelper;\n\n/**\n * QueryBuilder builds a SELECT SQL statement based on the specification given as a [[Query]] object.\n *\n * SQL statements are created from [[Query]] objects using the [[build()]]-method.\n *\n * QueryBuilder is also used by [[Command]] to build SQL statements such as INSERT, UPDATE, DELETE, CREATE TABLE.\n *\n * For more details and usage information on QueryBuilder, see the [guide article on query builders](guide:db-query-builder).\n *\n * @property-write string[] $conditionClasses Map of condition aliases to condition classes. For example:\n *\n * ```\n * ['LIKE' => yii\\db\\condition\\LikeCondition::class]\n * ```\n * @property-write string[] $expressionBuilders Array of builders that should be merged with the pre-defined\n * ones in [[expressionBuilders]] property.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass QueryBuilder extends \\yii\\base\\BaseObject\n{\n    /**\n     * The prefix for automatically generated query binding parameters.\n     */\n    public const PARAM_PREFIX = ':qp';\n    /**\n     * @var Connection the database connection.\n     */\n    public $db;\n    /**\n     * @var string the separator between different fragments of a SQL statement.\n     * Defaults to an empty space. This is mainly used by [[build()]] when generating a SQL statement.\n     */\n    public $separator = ' ';\n    /**\n     * @var array the abstract column types mapped to physical column types.\n     * This is mainly used to support creating/modifying tables using DB-independent data type specifications.\n     * Child classes should override this property to declare supported type mappings.\n     */\n    public $typeMap = [];\n\n    /**\n     * @var array map of query condition to builder methods.\n     * These methods are used by [[buildCondition]] to build SQL conditions from array syntax.\n     * @deprecated since 2.0.14. Is not used, will be dropped in 2.1.0.\n     */\n    protected $conditionBuilders = [];\n    /**\n     * @var array map of condition aliases to condition classes. For example:\n     *\n     * ```\n     * return [\n     *     'LIKE' => yii\\db\\condition\\LikeCondition::class,\n     * ];\n     * ```\n     *\n     * This property is used by [[createConditionFromArray]] method.\n     * See default condition classes list in [[defaultConditionClasses()]] method.\n     *\n     * In case you want to add custom conditions support, use the [[setConditionClasses()]] method.\n     *\n     * @see setConditionClasses()\n     * @see defaultConditionClasses()\n     * @since 2.0.14\n     */\n    protected $conditionClasses = [];\n    /**\n     * @var string[]|ExpressionBuilderInterface[] maps expression class to expression builder class.\n     * For example:\n     *\n     * ```\n     * [\n     *    yii\\db\\Expression::class => yii\\db\\ExpressionBuilder::class\n     * ]\n     * ```\n     * This property is mainly used by [[buildExpression()]] to build SQL expressions form expression objects.\n     * See default values in [[defaultExpressionBuilders()]] method.\n     *\n     *\n     * To override existing builders or add custom, use [[setExpressionBuilder()]] method. New items will be added\n     * to the end of this array.\n     *\n     * To find a builder, [[buildExpression()]] will check the expression class for its exact presence in this map.\n     * In case it is NOT present, the array will be iterated in reverse direction, checking whether the expression\n     * extends the class, defined in this map.\n     *\n     * @see setExpressionBuilders()\n     * @see defaultExpressionBuilders()\n     * @since 2.0.14\n     */\n    protected $expressionBuilders = [];\n\n\n    /**\n     * Constructor.\n     * @param Connection $connection the database connection.\n     * @param array $config name-value pairs that will be used to initialize the object properties\n     */\n    public function __construct($connection, $config = [])\n    {\n        $this->db = $connection;\n        parent::__construct($config);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n\n        $this->expressionBuilders = array_merge($this->defaultExpressionBuilders(), $this->expressionBuilders);\n        $this->conditionClasses = array_merge($this->defaultConditionClasses(), $this->conditionClasses);\n    }\n\n    /**\n     * Contains array of default condition classes. Extend this method, if you want to change\n     * default condition classes for the query builder. See [[conditionClasses]] docs for details.\n     *\n     * @return array\n     * @see conditionClasses\n     * @since 2.0.14\n     */\n    protected function defaultConditionClasses()\n    {\n        return [\n            'NOT' => 'yii\\db\\conditions\\NotCondition',\n            'AND' => 'yii\\db\\conditions\\AndCondition',\n            'OR' => 'yii\\db\\conditions\\OrCondition',\n            'BETWEEN' => 'yii\\db\\conditions\\BetweenCondition',\n            'NOT BETWEEN' => 'yii\\db\\conditions\\BetweenCondition',\n            'IN' => 'yii\\db\\conditions\\InCondition',\n            'NOT IN' => 'yii\\db\\conditions\\InCondition',\n            'LIKE' => 'yii\\db\\conditions\\LikeCondition',\n            'NOT LIKE' => 'yii\\db\\conditions\\LikeCondition',\n            'OR LIKE' => 'yii\\db\\conditions\\LikeCondition',\n            'OR NOT LIKE' => 'yii\\db\\conditions\\LikeCondition',\n            'EXISTS' => 'yii\\db\\conditions\\ExistsCondition',\n            'NOT EXISTS' => 'yii\\db\\conditions\\ExistsCondition',\n        ];\n    }\n\n    /**\n     * Contains array of default expression builders. Extend this method and override it, if you want to change\n     * default expression builders for this query builder. See [[expressionBuilders]] docs for details.\n     *\n     * @return array\n     * @see expressionBuilders\n     * @since 2.0.14\n     */\n    protected function defaultExpressionBuilders()\n    {\n        return [\n            'yii\\db\\Query' => 'yii\\db\\QueryExpressionBuilder',\n            'yii\\db\\PdoValue' => 'yii\\db\\PdoValueBuilder',\n            'yii\\db\\Expression' => 'yii\\db\\ExpressionBuilder',\n            'yii\\db\\conditions\\ConjunctionCondition' => 'yii\\db\\conditions\\ConjunctionConditionBuilder',\n            'yii\\db\\conditions\\NotCondition' => 'yii\\db\\conditions\\NotConditionBuilder',\n            'yii\\db\\conditions\\AndCondition' => 'yii\\db\\conditions\\ConjunctionConditionBuilder',\n            'yii\\db\\conditions\\OrCondition' => 'yii\\db\\conditions\\ConjunctionConditionBuilder',\n            'yii\\db\\conditions\\BetweenCondition' => 'yii\\db\\conditions\\BetweenConditionBuilder',\n            'yii\\db\\conditions\\InCondition' => 'yii\\db\\conditions\\InConditionBuilder',\n            'yii\\db\\conditions\\LikeCondition' => 'yii\\db\\conditions\\LikeConditionBuilder',\n            'yii\\db\\conditions\\ExistsCondition' => 'yii\\db\\conditions\\ExistsConditionBuilder',\n            'yii\\db\\conditions\\SimpleCondition' => 'yii\\db\\conditions\\SimpleConditionBuilder',\n            'yii\\db\\conditions\\HashCondition' => 'yii\\db\\conditions\\HashConditionBuilder',\n            'yii\\db\\conditions\\BetweenColumnsCondition' => 'yii\\db\\conditions\\BetweenColumnsConditionBuilder',\n        ];\n    }\n\n    /**\n     * Setter for [[expressionBuilders]] property.\n     *\n     * @param string[] $builders array of builders that should be merged with the pre-defined ones\n     * in [[expressionBuilders]] property.\n     * @since 2.0.14\n     * @see expressionBuilders\n     */\n    public function setExpressionBuilders($builders)\n    {\n        $this->expressionBuilders = array_merge($this->expressionBuilders, $builders);\n    }\n\n    /**\n     * Setter for [[conditionClasses]] property.\n     *\n     * @param string[] $classes map of condition aliases to condition classes. For example:\n     *\n     * ```\n     * ['LIKE' => yii\\db\\condition\\LikeCondition::class]\n     * ```\n     *\n     * @since 2.0.14.2\n     * @see conditionClasses\n     */\n    public function setConditionClasses($classes)\n    {\n        $this->conditionClasses = array_merge($this->conditionClasses, $classes);\n    }\n\n    /**\n     * Generates a SELECT SQL statement from a [[Query]] object.\n     *\n     * @param Query $query the [[Query]] object from which the SQL statement will be generated.\n     * @param array $params the parameters to be bound to the generated SQL statement. These parameters will\n     * be included in the result with the additional parameters generated during the query building process.\n     * @return array the generated SQL statement (the first array element) and the corresponding\n     * parameters to be bound to the SQL statement (the second array element). The parameters returned\n     * include those provided in `$params`.\n     */\n    public function build($query, $params = [])\n    {\n        $query = $query->prepare($this);\n\n        $params = empty($params) ? $query->params : array_merge($params, $query->params);\n\n        $clauses = [\n            $this->buildSelect($query->select, $params, $query->distinct, $query->selectOption),\n            $this->buildFrom($query->from, $params),\n            $this->buildJoin($query->join, $params),\n            $this->buildWhere($query->where, $params),\n            $this->buildGroupBy($query->groupBy),\n            $this->buildHaving($query->having, $params),\n        ];\n\n        $sql = implode($this->separator, array_filter($clauses));\n        $sql = $this->buildOrderByAndLimit($sql, $query->orderBy, $query->limit, $query->offset);\n\n        if (!empty($query->orderBy)) {\n            foreach ($query->orderBy as $expression) {\n                if ($expression instanceof ExpressionInterface) {\n                    $this->buildExpression($expression, $params);\n                }\n            }\n        }\n        if (!empty($query->groupBy)) {\n            foreach ($query->groupBy as $expression) {\n                if ($expression instanceof ExpressionInterface) {\n                    $this->buildExpression($expression, $params);\n                }\n            }\n        }\n\n        $union = $this->buildUnion($query->union, $params);\n        if ($union !== '') {\n            $sql = \"($sql){$this->separator}$union\";\n        }\n\n        $with = $this->buildWithQueries($query->withQueries, $params);\n        if ($with !== '') {\n            $sql = \"$with{$this->separator}$sql\";\n        }\n\n        return [$sql, $params];\n    }\n\n    /**\n     * Builds given $expression\n     *\n     * @param ExpressionInterface $expression the expression to be built\n     * @param array $params the parameters to be bound to the generated SQL statement. These parameters will\n     * be included in the result with the additional parameters generated during the expression building process.\n     * @return string the SQL statement that will not be neither quoted nor encoded before passing to DBMS\n     * @throws InvalidArgumentException when $expression building is not supported by this QueryBuilder.\n     * @see ExpressionBuilderInterface\n     * @see expressionBuilders\n     * @since 2.0.14\n     * @see ExpressionInterface\n     */\n    public function buildExpression(ExpressionInterface $expression, &$params = [])\n    {\n        $builder = $this->getExpressionBuilder($expression);\n\n        return $builder->build($expression, $params);\n    }\n\n    /**\n     * Gets object of [[ExpressionBuilderInterface]] that is suitable for $expression.\n     * Uses [[expressionBuilders]] array to find a suitable builder class.\n     *\n     * @param ExpressionInterface $expression\n     * @return ExpressionBuilderInterface\n     * @throws InvalidArgumentException when $expression building is not supported by this QueryBuilder.\n     * @since 2.0.14\n     * @see expressionBuilders\n     */\n    public function getExpressionBuilder(ExpressionInterface $expression)\n    {\n        $className = get_class($expression);\n\n        if (!isset($this->expressionBuilders[$className])) {\n            foreach (array_reverse($this->expressionBuilders) as $expressionClass => $builderClass) {\n                if (is_subclass_of($expression, $expressionClass)) {\n                    $this->expressionBuilders[$className] = $builderClass;\n                    break;\n                }\n            }\n\n            if (!isset($this->expressionBuilders[$className])) {\n                throw new InvalidArgumentException('Expression of class ' . $className . ' can not be built in ' . get_class($this));\n            }\n        }\n\n        if ($this->expressionBuilders[$className] === __CLASS__) {\n            /** @var $this&ExpressionBuilderInterface $result */\n            $result = $this;\n            return $result;\n        }\n\n        if (!is_object($this->expressionBuilders[$className])) {\n            $this->expressionBuilders[$className] = new $this->expressionBuilders[$className]($this);\n        }\n\n        return $this->expressionBuilders[$className];\n    }\n\n    /**\n     * Creates an INSERT SQL statement.\n     * For example,\n     * ```\n     * $sql = $queryBuilder->insert('user', [\n     *     'name' => 'Sam',\n     *     'age' => 30,\n     * ], $params);\n     * ```\n     * The method will properly escape the table and column names.\n     *\n     * @param string $table the table that new rows will be inserted into.\n     * @param array|Query $columns the column data (name => value) to be inserted into the table or instance\n     * of [[yii\\db\\Query|Query]] to perform INSERT INTO ... SELECT SQL statement.\n     * Passing of [[yii\\db\\Query|Query]] is available since version 2.0.11.\n     * @param array $params the binding parameters that will be generated by this method.\n     * They should be bound to the DB command later.\n     * @return string the INSERT SQL\n     */\n    public function insert($table, $columns, &$params)\n    {\n        list($names, $placeholders, $values, $params) = $this->prepareInsertValues($table, $columns, $params);\n        return 'INSERT INTO ' . $this->db->quoteTableName($table)\n            . (!empty($names) ? ' (' . implode(', ', $names) . ')' : '')\n            . (!empty($placeholders) ? ' VALUES (' . implode(', ', $placeholders) . ')' : $values);\n    }\n\n    /**\n     * Prepares a `VALUES` part for an `INSERT` SQL statement.\n     *\n     * @param string $table the table that new rows will be inserted into.\n     * @param array|Query $columns the column data (name => value) to be inserted into the table or instance\n     * of [[yii\\db\\Query|Query]] to perform INSERT INTO ... SELECT SQL statement.\n     * @param array $params the binding parameters that will be generated by this method.\n     * They should be bound to the DB command later.\n     * @return array array of column names, placeholders, values and params.\n     * @since 2.0.14\n     */\n    protected function prepareInsertValues($table, $columns, $params = [])\n    {\n        $schema = $this->db->getSchema();\n        $tableSchema = $schema->getTableSchema($table);\n        $columnSchemas = $tableSchema !== null ? $tableSchema->columns : [];\n        $names = [];\n        $placeholders = [];\n        $values = ' DEFAULT VALUES';\n        if ($columns instanceof Query) {\n            list($names, $values, $params) = $this->prepareInsertSelectSubQuery($columns, $schema, $params);\n        } else {\n            foreach ($columns as $name => $value) {\n                $names[] = $schema->quoteColumnName($name);\n                $value = isset($columnSchemas[$name]) ? $columnSchemas[$name]->dbTypecast($value) : $value;\n\n                if ($value instanceof ExpressionInterface) {\n                    $placeholders[] = $this->buildExpression($value, $params);\n                } elseif ($value instanceof \\yii\\db\\Query) {\n                    list($sql, $params) = $this->build($value, $params);\n                    $placeholders[] = \"($sql)\";\n                } else {\n                    $placeholders[] = $this->bindParam($value, $params);\n                }\n            }\n        }\n        return [$names, $placeholders, $values, $params];\n    }\n\n    /**\n     * Prepare select-subquery and field names for INSERT INTO ... SELECT SQL statement.\n     *\n     * @param Query $columns Object, which represents select query.\n     * @param Schema $schema Schema object to quote column name.\n     * @param array $params the parameters to be bound to the generated SQL statement. These parameters will\n     * be included in the result with the additional parameters generated during the query building process.\n     * @return array array of column names, values and params.\n     * @throws InvalidArgumentException if query's select does not contain named parameters only.\n     * @since 2.0.11\n     */\n    protected function prepareInsertSelectSubQuery($columns, $schema, $params = [])\n    {\n        if (!is_array($columns->select) || empty($columns->select) || in_array('*', $columns->select)) {\n            throw new InvalidArgumentException('Expected select query object with enumerated (named) parameters');\n        }\n\n        list($values, $params) = $this->build($columns, $params);\n        $names = [];\n        $values = ' ' . $values;\n        foreach ($columns->select as $title => $field) {\n            if (is_string($title)) {\n                $names[] = $schema->quoteColumnName($title);\n            } elseif (preg_match('/^(.*?)(?i:\\s+as\\s+|\\s+)([\\w\\-_\\.]+)$/', $field, $matches)) {\n                $names[] = $schema->quoteColumnName($matches[2]);\n            } else {\n                $names[] = $schema->quoteColumnName($field);\n            }\n        }\n\n        return [$names, $values, $params];\n    }\n\n    /**\n     * Generates a batch INSERT SQL statement.\n     *\n     * For example,\n     *\n     * ```\n     * $sql = $queryBuilder->batchInsert('user', ['name', 'age'], [\n     *     ['Tom', 30],\n     *     ['Jane', 20],\n     *     ['Linda', 25],\n     * ]);\n     * ```\n     *\n     * Note that the values in each row must match the corresponding column names.\n     *\n     * The method will properly escape the column names, and quote the values to be inserted.\n     *\n     * @param string $table the table that new rows will be inserted into.\n     * @param array $columns the column names\n     * @param array|\\Generator $rows the rows to be batch inserted into the table\n     * @param array $params the binding parameters. This parameter exists since 2.0.14\n     * @return string the batch INSERT SQL statement\n     */\n    public function batchInsert($table, $columns, $rows, &$params = [])\n    {\n        if (empty($rows)) {\n            return '';\n        }\n\n        $schema = $this->db->getSchema();\n        if (($tableSchema = $schema->getTableSchema($table)) !== null) {\n            $columnSchemas = $tableSchema->columns;\n        } else {\n            $columnSchemas = [];\n        }\n\n        $values = [];\n        foreach ($rows as $row) {\n            $vs = [];\n            foreach ($row as $i => $value) {\n                if (isset($columns[$i], $columnSchemas[$columns[$i]])) {\n                    $value = $columnSchemas[$columns[$i]]->dbTypecast($value);\n                }\n                if (is_string($value)) {\n                    $value = $schema->quoteValue($value);\n                } elseif (is_float($value)) {\n                    // ensure type cast always has . as decimal separator in all locales\n                    $value = StringHelper::floatToString($value);\n                } elseif ($value === false) {\n                    $value = 0;\n                } elseif ($value === null) {\n                    $value = 'NULL';\n                } elseif ($value instanceof ExpressionInterface) {\n                    $value = $this->buildExpression($value, $params);\n                }\n                $vs[] = $value;\n            }\n            $values[] = '(' . implode(', ', $vs) . ')';\n        }\n        if (empty($values)) {\n            return '';\n        }\n\n        foreach ($columns as $i => $name) {\n            $columns[$i] = $schema->quoteColumnName($name);\n        }\n\n        return 'INSERT INTO ' . $schema->quoteTableName($table)\n            . ' (' . implode(', ', $columns) . ') VALUES ' . implode(', ', $values);\n    }\n\n    /**\n     * Creates an SQL statement to insert rows into a database table if\n     * they do not already exist (matching unique constraints),\n     * or update them if they do.\n     *\n     * For example,\n     *\n     * ```\n     * $sql = $queryBuilder->upsert('pages', [\n     *     'name' => 'Front page',\n     *     'url' => 'https://example.com/', // url is unique\n     *     'visits' => 0,\n     * ], [\n     *     'visits' => new \\yii\\db\\Expression('visits + 1'),\n     * ], $params);\n     * ```\n     *\n     * The method will properly escape the table and column names.\n     *\n     * @param string $table the table that new rows will be inserted into/updated in.\n     * @param array|Query $insertColumns the column data (name => value) to be inserted into the table or instance\n     * of [[Query]] to perform `INSERT INTO ... SELECT` SQL statement.\n     * @param array|bool $updateColumns the column data (name => value) to be updated if they already exist.\n     * If `true` is passed, the column data will be updated to match the insert column data.\n     * If `false` is passed, no update will be performed if the column data already exists.\n     * @param array $params the binding parameters that will be generated by this method.\n     * They should be bound to the DB command later.\n     * @return string the resulting SQL.\n     * @throws NotSupportedException if this is not supported by the underlying DBMS.\n     * @since 2.0.14\n     */\n    public function upsert($table, $insertColumns, $updateColumns, &$params)\n    {\n        throw new NotSupportedException($this->db->getDriverName() . ' does not support upsert statements.');\n    }\n\n    /**\n     * @param string $table\n     * @param array|Query $insertColumns\n     * @param array|bool $updateColumns\n     * @param Constraint[] $constraints this parameter recieves a matched constraint list.\n     * The constraints will be unique by their column names.\n     * @return array\n     * @since 2.0.14\n     */\n    protected function prepareUpsertColumns($table, $insertColumns, $updateColumns, &$constraints = [])\n    {\n        if ($insertColumns instanceof Query) {\n            list($insertNames) = $this->prepareInsertSelectSubQuery($insertColumns, $this->db->getSchema());\n        } else {\n            $insertNames = array_map([$this->db, 'quoteColumnName'], array_keys($insertColumns));\n        }\n        $uniqueNames = $this->getTableUniqueColumnNames($table, $insertNames, $constraints);\n        $uniqueNames = array_map([$this->db, 'quoteColumnName'], $uniqueNames);\n        if ($updateColumns !== true) {\n            return [$uniqueNames, $insertNames, null];\n        }\n\n        return [$uniqueNames, $insertNames, array_diff($insertNames, $uniqueNames)];\n    }\n\n    /**\n     * Returns all column names belonging to constraints enforcing uniqueness (`PRIMARY KEY`, `UNIQUE INDEX`, etc.)\n     * for the named table removing constraints which did not cover the specified column list.\n     * The column list will be unique by column names.\n     *\n     * @param string $name table name. The table name may contain schema name if any. Do not quote the table name.\n     * @param string[] $columns source column list.\n     * @param Constraint[] $constraints this parameter optionally recieves a matched constraint list.\n     * The constraints will be unique by their column names.\n     * @return string[] column list.\n     */\n    private function getTableUniqueColumnNames($name, $columns, &$constraints = [])\n    {\n        $schema = $this->db->getSchema();\n        if (!$schema instanceof ConstraintFinderInterface) {\n            return [];\n        }\n\n        $constraints = [];\n        $primaryKey = $schema->getTablePrimaryKey($name);\n        if ($primaryKey !== null) {\n            $constraints[] = $primaryKey;\n        }\n        foreach ($schema->getTableIndexes($name) as $constraint) {\n            if ($constraint->isUnique) {\n                $constraints[] = $constraint;\n            }\n        }\n        $constraints = array_merge($constraints, $schema->getTableUniques($name));\n        // Remove duplicates\n        $constraints = array_combine(array_map(function (Constraint $constraint) {\n            $columns = $constraint->columnNames;\n            sort($columns, SORT_STRING);\n            return json_encode($columns);\n        }, $constraints), $constraints);\n        $columnNames = [];\n        // Remove all constraints which do not cover the specified column list\n        $constraints = array_values(array_filter($constraints, function (Constraint $constraint) use ($schema, $columns, &$columnNames) {\n            $constraintColumnNames = array_map([$schema, 'quoteColumnName'], $constraint->columnNames);\n            $result = !array_diff($constraintColumnNames, $columns);\n            if ($result) {\n                $columnNames = array_merge($columnNames, $constraintColumnNames);\n            }\n            return $result;\n        }));\n        return array_unique($columnNames);\n    }\n\n    /**\n     * Creates an UPDATE SQL statement.\n     *\n     * For example,\n     *\n     * ```\n     * $params = [];\n     * $sql = $queryBuilder->update('user', ['status' => 1], 'age > 30', $params);\n     * ```\n     *\n     * The method will properly escape the table and column names.\n     *\n     * @param string $table the table to be updated.\n     * @param array $columns the column data (name => value) to be updated.\n     * @param array|string $condition the condition that will be put in the WHERE part. Please\n     * refer to [[Query::where()]] on how to specify condition.\n     * @param array $params the binding parameters that will be modified by this method\n     * so that they can be bound to the DB command later.\n     * @return string the UPDATE SQL\n     */\n    public function update($table, $columns, $condition, &$params)\n    {\n        list($lines, $params) = $this->prepareUpdateSets($table, $columns, $params);\n        $sql = 'UPDATE ' . $this->db->quoteTableName($table) . ' SET ' . implode(', ', $lines);\n        $where = $this->buildWhere($condition, $params);\n        return $where === '' ? $sql : $sql . ' ' . $where;\n    }\n\n    /**\n     * Prepares a `SET` parts for an `UPDATE` SQL statement.\n     * @param string $table the table to be updated.\n     * @param array $columns the column data (name => value) to be updated.\n     * @param array $params the binding parameters that will be modified by this method\n     * so that they can be bound to the DB command later.\n     * @return array an array `SET` parts for an `UPDATE` SQL statement (the first array element) and params (the second array element).\n     * @since 2.0.14\n     */\n    protected function prepareUpdateSets($table, $columns, $params = [])\n    {\n        $tableSchema = $this->db->getTableSchema($table);\n        $columnSchemas = $tableSchema !== null ? $tableSchema->columns : [];\n        $sets = [];\n        foreach ($columns as $name => $value) {\n            $value = isset($columnSchemas[$name]) ? $columnSchemas[$name]->dbTypecast($value) : $value;\n            if ($value instanceof ExpressionInterface) {\n                $placeholder = $this->buildExpression($value, $params);\n            } else {\n                $placeholder = $this->bindParam($value, $params);\n            }\n\n            $sets[] = $this->db->quoteColumnName($name) . '=' . $placeholder;\n        }\n        return [$sets, $params];\n    }\n\n    /**\n     * Creates a DELETE SQL statement.\n     *\n     * For example,\n     *\n     * ```\n     * $sql = $queryBuilder->delete('user', 'status = 0');\n     * ```\n     *\n     * The method will properly escape the table and column names.\n     *\n     * @param string $table the table where the data will be deleted from.\n     * @param array|string $condition the condition that will be put in the WHERE part. Please\n     * refer to [[Query::where()]] on how to specify condition.\n     * @param array $params the binding parameters that will be modified by this method\n     * so that they can be bound to the DB command later.\n     * @return string the DELETE SQL\n     */\n    public function delete($table, $condition, &$params)\n    {\n        $sql = 'DELETE FROM ' . $this->db->quoteTableName($table);\n        $where = $this->buildWhere($condition, $params);\n\n        return $where === '' ? $sql : $sql . ' ' . $where;\n    }\n\n    /**\n     * Builds a SQL statement for creating a new DB table.\n     *\n     * The columns in the new table should be specified as name-definition pairs (e.g. 'name' => 'string'),\n     * where name stands for a column name which will be properly quoted by the method, and definition\n     * stands for the column type which must contain an abstract DB type.\n     * The [[getColumnType()]] method will be invoked to convert any abstract type into a physical one.\n     *\n     * If a column is specified with definition only (e.g. 'PRIMARY KEY (name, type)'), it will be directly\n     * inserted into the generated SQL.\n     *\n     * For example,\n     *\n     * ```\n     * $sql = $queryBuilder->createTable('user', [\n     *  'id' => 'pk',\n     *  'name' => 'string',\n     *  'age' => 'integer',\n     *  'column_name double precision null default null', # definition only example\n     * ]);\n     * ```\n     *\n     * @param string $table the name of the table to be created. The name will be properly quoted by the method.\n     * @param array $columns the columns (name => definition) in the new table.\n     * @param string|null $options additional SQL fragment that will be appended to the generated SQL.\n     * @return string the SQL statement for creating a new DB table.\n     */\n    public function createTable($table, $columns, $options = null)\n    {\n        $cols = [];\n        foreach ($columns as $name => $type) {\n            if (is_string($name)) {\n                $cols[] = \"\\t\" . $this->db->quoteColumnName($name) . ' ' . $this->getColumnType($type);\n            } else {\n                $cols[] = \"\\t\" . $type;\n            }\n        }\n        $sql = 'CREATE TABLE ' . $this->db->quoteTableName($table) . \" (\\n\" . implode(\",\\n\", $cols) . \"\\n)\";\n\n        return $options === null ? $sql : $sql . ' ' . $options;\n    }\n\n    /**\n     * Builds a SQL statement for renaming a DB table.\n     * @param string $oldName the table to be renamed. The name will be properly quoted by the method.\n     * @param string $newName the new table name. The name will be properly quoted by the method.\n     * @return string the SQL statement for renaming a DB table.\n     */\n    public function renameTable($oldName, $newName)\n    {\n        return 'RENAME TABLE ' . $this->db->quoteTableName($oldName) . ' TO ' . $this->db->quoteTableName($newName);\n    }\n\n    /**\n     * Builds a SQL statement for dropping a DB table.\n     * @param string $table the table to be dropped. The name will be properly quoted by the method.\n     * @return string the SQL statement for dropping a DB table.\n     */\n    public function dropTable($table)\n    {\n        return 'DROP TABLE ' . $this->db->quoteTableName($table);\n    }\n\n    /**\n     * Builds a SQL statement for adding a primary key constraint to an existing table.\n     * @param string $name the name of the primary key constraint.\n     * @param string $table the table that the primary key constraint will be added to.\n     * @param string|array $columns comma separated string or array of columns that the primary key will consist of.\n     * @return string the SQL statement for adding a primary key constraint to an existing table.\n     */\n    public function addPrimaryKey($name, $table, $columns)\n    {\n        if (is_string($columns)) {\n            $columns = preg_split('/\\s*,\\s*/', $columns, -1, PREG_SPLIT_NO_EMPTY);\n        }\n\n        foreach ($columns as $i => $col) {\n            $columns[$i] = $this->db->quoteColumnName($col);\n        }\n\n        return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' ADD CONSTRAINT '\n            . $this->db->quoteColumnName($name) . ' PRIMARY KEY ('\n            . implode(', ', $columns) . ')';\n    }\n\n    /**\n     * Builds a SQL statement for removing a primary key constraint to an existing table.\n     * @param string $name the name of the primary key constraint to be removed.\n     * @param string $table the table that the primary key constraint will be removed from.\n     * @return string the SQL statement for removing a primary key constraint from an existing table.\n     */\n    public function dropPrimaryKey($name, $table)\n    {\n        return 'ALTER TABLE ' . $this->db->quoteTableName($table)\n            . ' DROP CONSTRAINT ' . $this->db->quoteColumnName($name);\n    }\n\n    /**\n     * Builds a SQL statement for truncating a DB table.\n     * @param string $table the table to be truncated. The name will be properly quoted by the method.\n     * @return string the SQL statement for truncating a DB table.\n     */\n    public function truncateTable($table)\n    {\n        return 'TRUNCATE TABLE ' . $this->db->quoteTableName($table);\n    }\n\n    /**\n     * Builds a SQL statement for adding a new DB column.\n     * @param string $table the table that the new column will be added to. The table name will be properly quoted by the method.\n     * @param string $column the name of the new column. The name will be properly quoted by the method.\n     * @param string $type the column type. The [[getColumnType()]] method will be invoked to convert abstract column type (if any)\n     * into the physical one. Anything that is not recognized as abstract type will be kept in the generated SQL.\n     * For example, 'string' will be turned into 'varchar(255)', while 'string not null' will become 'varchar(255) not null'.\n     * @return string the SQL statement for adding a new column.\n     */\n    public function addColumn($table, $column, $type)\n    {\n        return 'ALTER TABLE ' . $this->db->quoteTableName($table)\n            . ' ADD ' . $this->db->quoteColumnName($column) . ' '\n            . $this->getColumnType($type);\n    }\n\n    /**\n     * Builds a SQL statement for dropping a DB column.\n     * @param string $table the table whose column is to be dropped. The name will be properly quoted by the method.\n     * @param string $column the name of the column to be dropped. The name will be properly quoted by the method.\n     * @return string the SQL statement for dropping a DB column.\n     */\n    public function dropColumn($table, $column)\n    {\n        return 'ALTER TABLE ' . $this->db->quoteTableName($table)\n            . ' DROP COLUMN ' . $this->db->quoteColumnName($column);\n    }\n\n    /**\n     * Builds a SQL statement for renaming a column.\n     * @param string $table the table whose column is to be renamed. The name will be properly quoted by the method.\n     * @param string $oldName the old name of the column. The name will be properly quoted by the method.\n     * @param string $newName the new name of the column. The name will be properly quoted by the method.\n     * @return string the SQL statement for renaming a DB column.\n     */\n    public function renameColumn($table, $oldName, $newName)\n    {\n        return 'ALTER TABLE ' . $this->db->quoteTableName($table)\n            . ' RENAME COLUMN ' . $this->db->quoteColumnName($oldName)\n            . ' TO ' . $this->db->quoteColumnName($newName);\n    }\n\n    /**\n     * Builds a SQL statement for changing the definition of a column.\n     * @param string $table the table whose column is to be changed. The table name will be properly quoted by the method.\n     * @param string $column the name of the column to be changed. The name will be properly quoted by the method.\n     * @param string $type the new column type. The [[getColumnType()]] method will be invoked to convert abstract\n     * column type (if any) into the physical one. Anything that is not recognized as abstract type will be kept\n     * in the generated SQL. For example, 'string' will be turned into 'varchar(255)', while 'string not null'\n     * will become 'varchar(255) not null'.\n     * @return string the SQL statement for changing the definition of a column.\n     */\n    public function alterColumn($table, $column, $type)\n    {\n        return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' CHANGE '\n            . $this->db->quoteColumnName($column) . ' '\n            . $this->db->quoteColumnName($column) . ' '\n            . $this->getColumnType($type);\n    }\n\n    /**\n     * Builds a SQL statement for adding a foreign key constraint to an existing table.\n     * The method will properly quote the table and column names.\n     * @param string $name the name of the foreign key constraint.\n     * @param string $table the table that the foreign key constraint will be added to.\n     * @param string|array $columns the name of the column to that the constraint will be added on.\n     * If there are multiple columns, separate them with commas or use an array to represent them.\n     * @param string $refTable the table that the foreign key references to.\n     * @param string|array $refColumns the name of the column that the foreign key references to.\n     * If there are multiple columns, separate them with commas or use an array to represent them.\n     * @param string|null $delete the ON DELETE option. Most DBMS support these options: RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL\n     * @param string|null $update the ON UPDATE option. Most DBMS support these options: RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL\n     * @return string the SQL statement for adding a foreign key constraint to an existing table.\n     */\n    public function addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete = null, $update = null)\n    {\n        $sql = 'ALTER TABLE ' . $this->db->quoteTableName($table)\n            . ' ADD CONSTRAINT ' . $this->db->quoteColumnName($name)\n            . ' FOREIGN KEY (' . $this->buildColumns($columns) . ')'\n            . ' REFERENCES ' . $this->db->quoteTableName($refTable)\n            . ' (' . $this->buildColumns($refColumns) . ')';\n        if ($delete !== null) {\n            $sql .= ' ON DELETE ' . $delete;\n        }\n        if ($update !== null) {\n            $sql .= ' ON UPDATE ' . $update;\n        }\n\n        return $sql;\n    }\n\n    /**\n     * Builds a SQL statement for dropping a foreign key constraint.\n     * @param string $name the name of the foreign key constraint to be dropped. The name will be properly quoted by the method.\n     * @param string $table the table whose foreign is to be dropped. The name will be properly quoted by the method.\n     * @return string the SQL statement for dropping a foreign key constraint.\n     */\n    public function dropForeignKey($name, $table)\n    {\n        return 'ALTER TABLE ' . $this->db->quoteTableName($table)\n            . ' DROP CONSTRAINT ' . $this->db->quoteColumnName($name);\n    }\n\n    /**\n     * Builds a SQL statement for creating a new index.\n     * @param string $name the name of the index. The name will be properly quoted by the method.\n     * @param string $table the table that the new index will be created for. The table name will be properly quoted by the method.\n     * @param string|array $columns the column(s) that should be included in the index. If there are multiple columns,\n     * separate them with commas or use an array to represent them. Each column name will be properly quoted\n     * by the method, unless a parenthesis is found in the name.\n     * @param bool $unique whether to add UNIQUE constraint on the created index.\n     * @return string the SQL statement for creating a new index.\n     */\n    public function createIndex($name, $table, $columns, $unique = false)\n    {\n        return ($unique ? 'CREATE UNIQUE INDEX ' : 'CREATE INDEX ')\n            . $this->db->quoteTableName($name) . ' ON '\n            . $this->db->quoteTableName($table)\n            . ' (' . $this->buildColumns($columns) . ')';\n    }\n\n    /**\n     * Builds a SQL statement for dropping an index.\n     * @param string $name the name of the index to be dropped. The name will be properly quoted by the method.\n     * @param string $table the table whose index is to be dropped. The name will be properly quoted by the method.\n     * @return string the SQL statement for dropping an index.\n     */\n    public function dropIndex($name, $table)\n    {\n        return 'DROP INDEX ' . $this->db->quoteTableName($name) . ' ON ' . $this->db->quoteTableName($table);\n    }\n\n    /**\n     * Creates a SQL command for adding an unique constraint to an existing table.\n     * @param string $name the name of the unique constraint.\n     * The name will be properly quoted by the method.\n     * @param string $table the table that the unique constraint will be added to.\n     * The name will be properly quoted by the method.\n     * @param string|array $columns the name of the column to that the constraint will be added on.\n     * If there are multiple columns, separate them with commas.\n     * The name will be properly quoted by the method.\n     * @return string the SQL statement for adding an unique constraint to an existing table.\n     * @since 2.0.13\n     */\n    public function addUnique($name, $table, $columns)\n    {\n        if (is_string($columns)) {\n            $columns = preg_split('/\\s*,\\s*/', $columns, -1, PREG_SPLIT_NO_EMPTY);\n        }\n        foreach ($columns as $i => $col) {\n            $columns[$i] = $this->db->quoteColumnName($col);\n        }\n\n        return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' ADD CONSTRAINT '\n            . $this->db->quoteColumnName($name) . ' UNIQUE ('\n            . implode(', ', $columns) . ')';\n    }\n\n    /**\n     * Creates a SQL command for dropping an unique constraint.\n     * @param string $name the name of the unique constraint to be dropped.\n     * The name will be properly quoted by the method.\n     * @param string $table the table whose unique constraint is to be dropped.\n     * The name will be properly quoted by the method.\n     * @return string the SQL statement for dropping an unique constraint.\n     * @since 2.0.13\n     */\n    public function dropUnique($name, $table)\n    {\n        return 'ALTER TABLE ' . $this->db->quoteTableName($table)\n            . ' DROP CONSTRAINT ' . $this->db->quoteColumnName($name);\n    }\n\n    /**\n     * Creates a SQL command for adding a check constraint to an existing table.\n     * @param string $name the name of the check constraint.\n     * The name will be properly quoted by the method.\n     * @param string $table the table that the check constraint will be added to.\n     * The name will be properly quoted by the method.\n     * @param string $expression the SQL of the `CHECK` constraint.\n     * @return string the SQL statement for adding a check constraint to an existing table.\n     * @since 2.0.13\n     */\n    public function addCheck($name, $table, $expression)\n    {\n        return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' ADD CONSTRAINT '\n            . $this->db->quoteColumnName($name) . ' CHECK (' . $this->db->quoteSql($expression) . ')';\n    }\n\n    /**\n     * Creates a SQL command for dropping a check constraint.\n     * @param string $name the name of the check constraint to be dropped.\n     * The name will be properly quoted by the method.\n     * @param string $table the table whose check constraint is to be dropped.\n     * The name will be properly quoted by the method.\n     * @return string the SQL statement for dropping a check constraint.\n     * @since 2.0.13\n     */\n    public function dropCheck($name, $table)\n    {\n        return 'ALTER TABLE ' . $this->db->quoteTableName($table)\n            . ' DROP CONSTRAINT ' . $this->db->quoteColumnName($name);\n    }\n\n    /**\n     * Creates a SQL command for adding a default value constraint to an existing table.\n     * @param string $name the name of the default value constraint.\n     * The name will be properly quoted by the method.\n     * @param string $table the table that the default value constraint will be added to.\n     * The name will be properly quoted by the method.\n     * @param string $column the name of the column to that the constraint will be added on.\n     * The name will be properly quoted by the method.\n     * @param mixed $value default value.\n     * @return string the SQL statement for adding a default value constraint to an existing table.\n     * @throws NotSupportedException if this is not supported by the underlying DBMS.\n     * @since 2.0.13\n     */\n    public function addDefaultValue($name, $table, $column, $value)\n    {\n        throw new NotSupportedException($this->db->getDriverName() . ' does not support adding default value constraints.');\n    }\n\n    /**\n     * Creates a SQL command for dropping a default value constraint.\n     * @param string $name the name of the default value constraint to be dropped.\n     * The name will be properly quoted by the method.\n     * @param string $table the table whose default value constraint is to be dropped.\n     * The name will be properly quoted by the method.\n     * @return string the SQL statement for dropping a default value constraint.\n     * @throws NotSupportedException if this is not supported by the underlying DBMS.\n     * @since 2.0.13\n     */\n    public function dropDefaultValue($name, $table)\n    {\n        throw new NotSupportedException($this->db->getDriverName() . ' does not support dropping default value constraints.');\n    }\n\n    /**\n     * Creates a SQL statement for resetting the sequence value of a table's primary key.\n     * The sequence will be reset such that the primary key of the next new row inserted\n     * will have the specified value or the maximum existing value +1.\n     * @param string $tableName the name of the table whose primary key sequence will be reset\n     * @param array|string|null $value the value for the primary key of the next new row inserted. If this is not set,\n     * the next new row's primary key will have the maximum existing value +1.\n     * @return string the SQL statement for resetting sequence\n     * @throws NotSupportedException if this is not supported by the underlying DBMS\n     */\n    public function resetSequence($tableName, $value = null)\n    {\n        throw new NotSupportedException($this->db->getDriverName() . ' does not support resetting sequence.');\n    }\n\n    /**\n     * Execute a SQL statement for resetting the sequence value of a table's primary key.\n     * Reason for execute is that some databases (Oracle) need several queries to do so.\n     * The sequence is reset such that the primary key of the next new row inserted\n     * will have the specified value or the maximum existing value +1.\n     * @param string $table the name of the table whose primary key sequence is reset\n     * @param array|string|null $value the value for the primary key of the next new row inserted. If this is not set,\n     * the next new row's primary key will have the maximum existing value +1.\n     * @throws NotSupportedException if this is not supported by the underlying DBMS\n     * @since 2.0.16\n     */\n    public function executeResetSequence($table, $value = null)\n    {\n        $this->db->createCommand()->resetSequence($table, $value)->execute();\n    }\n\n    /**\n     * Builds a SQL statement for enabling or disabling integrity check.\n     * @param bool $check whether to turn on or off the integrity check.\n     * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.\n     * @param string $table the table name. Defaults to empty string, meaning that no table will be changed.\n     * @return string the SQL statement for checking integrity\n     * @throws NotSupportedException if this is not supported by the underlying DBMS\n     */\n    public function checkIntegrity($check = true, $schema = '', $table = '')\n    {\n        throw new NotSupportedException($this->db->getDriverName() . ' does not support enabling/disabling integrity check.');\n    }\n\n    /**\n     * Builds a SQL command for adding comment to column.\n     *\n     * @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.\n     * @param string $column the name of the column to be commented. The column name will be properly quoted by the method.\n     * @param string $comment the text of the comment to be added. The comment will be properly quoted by the method.\n     * @return string the SQL statement for adding comment on column\n     * @since 2.0.8\n     */\n    public function addCommentOnColumn($table, $column, $comment)\n    {\n        return 'COMMENT ON COLUMN ' . $this->db->quoteTableName($table) . '.' . $this->db->quoteColumnName($column) . ' IS ' . $this->db->quoteValue($comment);\n    }\n\n    /**\n     * Builds a SQL command for adding comment to table.\n     *\n     * @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.\n     * @param string $comment the text of the comment to be added. The comment will be properly quoted by the method.\n     * @return string the SQL statement for adding comment on table\n     * @since 2.0.8\n     */\n    public function addCommentOnTable($table, $comment)\n    {\n        return 'COMMENT ON TABLE ' . $this->db->quoteTableName($table) . ' IS ' . $this->db->quoteValue($comment);\n    }\n\n    /**\n     * Builds a SQL command for adding comment to column.\n     *\n     * @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.\n     * @param string $column the name of the column to be commented. The column name will be properly quoted by the method.\n     * @return string the SQL statement for adding comment on column\n     * @since 2.0.8\n     */\n    public function dropCommentFromColumn($table, $column)\n    {\n        return 'COMMENT ON COLUMN ' . $this->db->quoteTableName($table) . '.' . $this->db->quoteColumnName($column) . ' IS NULL';\n    }\n\n    /**\n     * Builds a SQL command for adding comment to table.\n     *\n     * @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.\n     * @return string the SQL statement for adding comment on column\n     * @since 2.0.8\n     */\n    public function dropCommentFromTable($table)\n    {\n        return 'COMMENT ON TABLE ' . $this->db->quoteTableName($table) . ' IS NULL';\n    }\n\n    /**\n     * Creates a SQL View.\n     *\n     * @param string $viewName the name of the view to be created.\n     * @param string|Query $subQuery the select statement which defines the view.\n     * This can be either a string or a [[Query]] object.\n     * @return string the `CREATE VIEW` SQL statement.\n     * @since 2.0.14\n     */\n    public function createView($viewName, $subQuery)\n    {\n        if ($subQuery instanceof Query) {\n            list($rawQuery, $params) = $this->build($subQuery);\n            array_walk(\n                $params,\n                function (&$param) {\n                    $param = $this->db->quoteValue($param);\n                }\n            );\n            $subQuery = strtr($rawQuery, $params);\n        }\n\n        return 'CREATE VIEW ' . $this->db->quoteTableName($viewName) . ' AS ' . $subQuery;\n    }\n\n    /**\n     * Drops a SQL View.\n     *\n     * @param string $viewName the name of the view to be dropped.\n     * @return string the `DROP VIEW` SQL statement.\n     * @since 2.0.14\n     */\n    public function dropView($viewName)\n    {\n        return 'DROP VIEW ' . $this->db->quoteTableName($viewName);\n    }\n\n    /**\n     * Converts an abstract column type into a physical column type.\n     *\n     * The conversion is done using the type map specified in [[typeMap]].\n     * The following abstract column types are supported (using MySQL as an example to explain the corresponding\n     * physical types):\n     *\n     * - `pk`: an auto-incremental primary key type, will be converted into \"int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY\"\n     * - `bigpk`: an auto-incremental primary key type, will be converted into \"bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY\"\n     * - `upk`: an unsigned auto-incremental primary key type, will be converted into \"int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY\"\n     * - `char`: char type, will be converted into \"char(1)\"\n     * - `string`: string type, will be converted into \"varchar(255)\"\n     * - `text`: a long string type, will be converted into \"text\"\n     * - `smallint`: a small integer type, will be converted into \"smallint(6)\"\n     * - `integer`: integer type, will be converted into \"int(11)\"\n     * - `bigint`: a big integer type, will be converted into \"bigint(20)\"\n     * - `boolean`: boolean type, will be converted into \"tinyint(1)\"\n     * - `float``: float number type, will be converted into \"float\"\n     * - `decimal`: decimal number type, will be converted into \"decimal\"\n     * - `datetime`: datetime type, will be converted into \"datetime\"\n     * - `timestamp`: timestamp type, will be converted into \"timestamp\"\n     * - `time`: time type, will be converted into \"time\"\n     * - `date`: date type, will be converted into \"date\"\n     * - `money`: money type, will be converted into \"decimal(19,4)\"\n     * - `binary`: binary data type, will be converted into \"blob\"\n     *\n     * If the abstract type contains two or more parts separated by spaces (e.g. \"string NOT NULL\"), then only\n     * the first part will be converted, and the rest of the parts will be appended to the converted result.\n     * For example, 'string NOT NULL' is converted to 'varchar(255) NOT NULL'.\n     *\n     * For some of the abstract types you can also specify a length or precision constraint\n     * by appending it in round brackets directly to the type.\n     * For example `string(32)` will be converted into \"varchar(32)\" on a MySQL database.\n     * If the underlying DBMS does not support these kind of constraints for a type it will\n     * be ignored.\n     *\n     * If a type cannot be found in [[typeMap]], it will be returned without any change.\n     * @param string|ColumnSchemaBuilder $type abstract column type\n     * @return string physical column type.\n     */\n    public function getColumnType($type)\n    {\n        if ($type instanceof ColumnSchemaBuilder) {\n            $type = $type->__toString();\n        }\n\n        if (isset($this->typeMap[$type])) {\n            return $this->typeMap[$type];\n        } elseif (preg_match('/^(\\w+)\\((.+?)\\)(.*)$/', $type, $matches)) {\n            if (isset($this->typeMap[$matches[1]])) {\n                return preg_replace('/\\(.+\\)/', '(' . $matches[2] . ')', $this->typeMap[$matches[1]]) . $matches[3];\n            }\n        } elseif (preg_match('/^(\\w+)\\s+/', $type, $matches)) {\n            if (isset($this->typeMap[$matches[1]])) {\n                return preg_replace('/^\\w+/', $this->typeMap[$matches[1]], $type);\n            }\n        }\n\n        return $type;\n    }\n\n    /**\n     * @param array $columns\n     * @param array $params the binding parameters to be populated\n     * @param bool $distinct\n     * @param string|null $selectOption\n     * @return string the SELECT clause built from [[Query::$select]].\n     */\n    public function buildSelect($columns, &$params, $distinct = false, $selectOption = null)\n    {\n        $select = $distinct ? 'SELECT DISTINCT' : 'SELECT';\n        if ($selectOption !== null) {\n            $select .= ' ' . $selectOption;\n        }\n\n        if (empty($columns)) {\n            return $select . ' *';\n        }\n\n        foreach ($columns as $i => $column) {\n            if ($column instanceof ExpressionInterface) {\n                if (is_int($i)) {\n                    $columns[$i] = $this->buildExpression($column, $params);\n                } else {\n                    $columns[$i] = $this->buildExpression($column, $params) . ' AS ' . $this->db->quoteColumnName($i);\n                }\n            } elseif ($column instanceof Query) {\n                list($sql, $params) = $this->build($column, $params);\n                $columns[$i] = \"($sql) AS \" . $this->db->quoteColumnName($i);\n            } elseif (is_string($i) && $i !== $column) {\n                if (strpos($column, '(') === false) {\n                    $column = $this->db->quoteColumnName($column);\n                }\n                $columns[$i] = \"$column AS \" . $this->db->quoteColumnName($i);\n            } elseif (strpos($column, '(') === false) {\n                if (preg_match('/^(.*?)(?i:\\s+as\\s+|\\s+)([\\w\\-_\\.]+)$/', $column, $matches)) {\n                    $columns[$i] = $this->db->quoteColumnName($matches[1]) . ' AS ' . $this->db->quoteColumnName($matches[2]);\n                } else {\n                    $columns[$i] = $this->db->quoteColumnName($column);\n                }\n            }\n        }\n\n        return $select . ' ' . implode(', ', $columns);\n    }\n\n    /**\n     * @param array $tables\n     * @param array $params the binding parameters to be populated\n     * @return string the FROM clause built from [[Query::$from]].\n     */\n    public function buildFrom($tables, &$params)\n    {\n        if (empty($tables)) {\n            return '';\n        }\n\n        $tables = $this->quoteTableNames($tables, $params);\n\n        return 'FROM ' . implode(', ', $tables);\n    }\n\n    /**\n     * @param array $joins\n     * @param array $params the binding parameters to be populated\n     * @return string the JOIN clause built from [[Query::$join]].\n     * @throws Exception if the $joins parameter is not in proper format\n     */\n    public function buildJoin($joins, &$params)\n    {\n        if (empty($joins)) {\n            return '';\n        }\n\n        foreach ($joins as $i => $join) {\n            if (!is_array($join) || !isset($join[0], $join[1])) {\n                throw new Exception('A join clause must be specified as an array of join type, join table, and optionally join condition.');\n            }\n            // 0:join type, 1:join table, 2:on-condition (optional)\n            list($joinType, $table) = $join;\n            $tables = $this->quoteTableNames((array)$table, $params);\n            $table = reset($tables);\n            $joins[$i] = \"$joinType $table\";\n            if (isset($join[2])) {\n                $condition = $this->buildCondition($join[2], $params);\n                if ($condition !== '') {\n                    $joins[$i] .= ' ON ' . $condition;\n                }\n            }\n        }\n\n        return implode($this->separator, $joins);\n    }\n\n    /**\n     * Quotes table names passed.\n     *\n     * @param array $tables\n     * @param array $params\n     * @return array\n     */\n    private function quoteTableNames($tables, &$params)\n    {\n        foreach ($tables as $i => $table) {\n            if ($table instanceof Query) {\n                list($sql, $params) = $this->build($table, $params);\n                $tables[$i] = \"($sql) \" . $this->db->quoteTableName($i);\n            } elseif (is_string($i)) {\n                if (strpos($table, '(') === false) {\n                    $table = $this->db->quoteTableName($table);\n                }\n                $tables[$i] = \"$table \" . $this->db->quoteTableName($i);\n            } elseif (strpos($table, '(') === false) {\n                if ($tableWithAlias = $this->extractAlias($table)) { // with alias\n                    $tables[$i] = $this->db->quoteTableName($tableWithAlias[1]) . ' ' . $this->db->quoteTableName($tableWithAlias[2]);\n                } else {\n                    $tables[$i] = $this->db->quoteTableName($table);\n                }\n            }\n        }\n\n        return $tables;\n    }\n\n    /**\n     * @param string|array $condition\n     * @param array $params the binding parameters to be populated\n     * @return string the WHERE clause built from [[Query::$where]].\n     */\n    public function buildWhere($condition, &$params)\n    {\n        $where = $this->buildCondition($condition, $params);\n\n        return $where === '' ? '' : 'WHERE ' . $where;\n    }\n\n    /**\n     * @param array $columns\n     * @return string the GROUP BY clause\n     */\n    public function buildGroupBy($columns)\n    {\n        if (empty($columns)) {\n            return '';\n        }\n        foreach ($columns as $i => $column) {\n            if ($column instanceof ExpressionInterface) {\n                $columns[$i] = $this->buildExpression($column);\n            } elseif (strpos($column, '(') === false) {\n                $columns[$i] = $this->db->quoteColumnName($column);\n            }\n        }\n\n        return 'GROUP BY ' . implode(', ', $columns);\n    }\n\n    /**\n     * @param string|array $condition\n     * @param array $params the binding parameters to be populated\n     * @return string the HAVING clause built from [[Query::$having]].\n     */\n    public function buildHaving($condition, &$params)\n    {\n        $having = $this->buildCondition($condition, $params);\n\n        return $having === '' ? '' : 'HAVING ' . $having;\n    }\n\n    /**\n     * Builds the ORDER BY and LIMIT/OFFSET clauses and appends them to the given SQL.\n     * @param string $sql the existing SQL (without ORDER BY/LIMIT/OFFSET)\n     * @param array $orderBy the order by columns. See [[Query::orderBy]] for more details on how to specify this parameter.\n     * @param int $limit the limit number. See [[Query::limit]] for more details.\n     * @param int $offset the offset number. See [[Query::offset]] for more details.\n     * @return string the SQL completed with ORDER BY/LIMIT/OFFSET (if any)\n     */\n    public function buildOrderByAndLimit($sql, $orderBy, $limit, $offset)\n    {\n        $orderBy = $this->buildOrderBy($orderBy);\n        if ($orderBy !== '') {\n            $sql .= $this->separator . $orderBy;\n        }\n        $limit = $this->buildLimit($limit, $offset);\n        if ($limit !== '') {\n            $sql .= $this->separator . $limit;\n        }\n\n        return $sql;\n    }\n\n    /**\n     * @param array $columns\n     * @return string the ORDER BY clause built from [[Query::$orderBy]].\n     */\n    public function buildOrderBy($columns)\n    {\n        if (empty($columns)) {\n            return '';\n        }\n        $orders = [];\n        foreach ($columns as $name => $direction) {\n            if ($direction instanceof ExpressionInterface) {\n                $orders[] = $this->buildExpression($direction);\n            } else {\n                $orders[] = $this->db->quoteColumnName($name) . ($direction === SORT_DESC ? ' DESC' : '');\n            }\n        }\n\n        return 'ORDER BY ' . implode(', ', $orders);\n    }\n\n    /**\n     * @param int $limit\n     * @param int $offset\n     * @return string the LIMIT and OFFSET clauses\n     */\n    public function buildLimit($limit, $offset)\n    {\n        $sql = '';\n        if ($this->hasLimit($limit)) {\n            $sql = 'LIMIT ' . $limit;\n        }\n        if ($this->hasOffset($offset)) {\n            $sql .= ' OFFSET ' . $offset;\n        }\n\n        return ltrim($sql);\n    }\n\n    /**\n     * Checks to see if the given limit is effective.\n     * @param mixed $limit the given limit\n     * @return bool whether the limit is effective\n     */\n    protected function hasLimit($limit)\n    {\n        return ($limit instanceof ExpressionInterface) || ctype_digit((string)$limit);\n    }\n\n    /**\n     * Checks to see if the given offset is effective.\n     * @param mixed $offset the given offset\n     * @return bool whether the offset is effective\n     */\n    protected function hasOffset($offset)\n    {\n        return ($offset instanceof ExpressionInterface) || ctype_digit((string)$offset) && (string)$offset !== '0';\n    }\n\n    /**\n     * @param array $unions\n     * @param array $params the binding parameters to be populated\n     * @return string the UNION clause built from [[Query::$union]].\n     */\n    public function buildUnion($unions, &$params)\n    {\n        if (empty($unions)) {\n            return '';\n        }\n\n        $result = '';\n\n        foreach ($unions as $i => $union) {\n            $query = $union['query'];\n            if ($query instanceof Query) {\n                list($unions[$i]['query'], $params) = $this->build($query, $params);\n            }\n\n            $result .= 'UNION ' . ($union['all'] ? 'ALL ' : '') . '( ' . $unions[$i]['query'] . ' ) ';\n        }\n\n        return trim($result);\n    }\n\n    /**\n     * @param array $withs of configurations for each WITH query\n     * @param array $params the binding parameters to be populated\n     * @return string compiled WITH prefix of query including nested queries\n     * @see Query::withQuery()\n     * @since 2.0.35\n     */\n    public function buildWithQueries($withs, &$params)\n    {\n        if (empty($withs)) {\n            return '';\n        }\n\n        $recursive = false;\n        $result = [];\n\n        foreach ($withs as $i => $with) {\n            if ($with['recursive']) {\n                $recursive = true;\n            }\n\n            $query = $with['query'];\n            if ($query instanceof Query) {\n                list($with['query'], $params) = $this->build($query, $params);\n            }\n\n            $result[] = $with['alias'] . ' AS (' . $with['query'] . ')';\n        }\n\n        return 'WITH ' . ($recursive ? 'RECURSIVE ' : '') . implode(', ', $result);\n    }\n\n    /**\n     * Processes columns and properly quotes them if necessary.\n     * It will join all columns into a string with comma as separators.\n     * @param string|array $columns the columns to be processed\n     * @return string the processing result\n     */\n    public function buildColumns($columns)\n    {\n        if (!is_array($columns)) {\n            if (strpos($columns, '(') !== false) {\n                return $columns;\n            }\n\n            $rawColumns = $columns;\n            $columns = preg_split('/\\s*,\\s*/', $columns, -1, PREG_SPLIT_NO_EMPTY);\n            if ($columns === false) {\n                throw new InvalidArgumentException(\"$rawColumns is not valid columns.\");\n            }\n        }\n        foreach ($columns as $i => $column) {\n            if ($column instanceof ExpressionInterface) {\n                $columns[$i] = $this->buildExpression($column);\n            } elseif (strpos($column, '(') === false) {\n                $columns[$i] = $this->db->quoteColumnName($column);\n            }\n        }\n\n        return implode(', ', $columns);\n    }\n\n    /**\n     * Parses the condition specification and generates the corresponding SQL expression.\n     * @param string|array|ExpressionInterface $condition the condition specification. Please refer to [[Query::where()]]\n     * on how to specify a condition.\n     * @param array $params the binding parameters to be populated\n     * @return string the generated SQL expression\n     */\n    public function buildCondition($condition, &$params)\n    {\n        if (is_array($condition)) {\n            if (empty($condition)) {\n                return '';\n            }\n\n            $condition = $this->createConditionFromArray($condition);\n        }\n\n        if ($condition instanceof ExpressionInterface) {\n            return $this->buildExpression($condition, $params);\n        }\n\n        return (string)$condition;\n    }\n\n    /**\n     * Transforms $condition defined in array format (as described in [[Query::where()]]\n     * to instance of [[yii\\db\\condition\\ConditionInterface|ConditionInterface]] according to\n     * [[conditionClasses]] map.\n     *\n     * @param string|array $condition\n     * @return ConditionInterface\n     * @see conditionClasses\n     * @since 2.0.14\n     */\n    public function createConditionFromArray($condition)\n    {\n        if (isset($condition[0])) { // operator format: operator, operand 1, operand 2, ...\n            $operator = strtoupper(array_shift($condition));\n            if (isset($this->conditionClasses[$operator])) {\n                $className = $this->conditionClasses[$operator];\n            } else {\n                $className = 'yii\\db\\conditions\\SimpleCondition';\n            }\n            /** @var ConditionInterface $className */\n            return $className::fromArrayDefinition($operator, $condition);\n        }\n\n        // hash format: 'column1' => 'value1', 'column2' => 'value2', ...\n        return new HashCondition($condition);\n    }\n\n    /**\n     * Creates a condition based on column-value pairs.\n     * @param array $condition the condition specification.\n     * @param array $params the binding parameters to be populated\n     * @return string the generated SQL expression\n     * @deprecated since 2.0.14. Use `buildCondition()` instead.\n     */\n    public function buildHashCondition($condition, &$params)\n    {\n        return $this->buildCondition(new HashCondition($condition), $params);\n    }\n\n    /**\n     * Connects two or more SQL expressions with the `AND` or `OR` operator.\n     * @param string $operator the operator to use for connecting the given operands\n     * @param array $operands the SQL expressions to connect.\n     * @param array $params the binding parameters to be populated\n     * @return string the generated SQL expression\n     * @deprecated since 2.0.14. Use `buildCondition()` instead.\n     */\n    public function buildAndCondition($operator, $operands, &$params)\n    {\n        array_unshift($operands, $operator);\n        return $this->buildCondition($operands, $params);\n    }\n\n    /**\n     * Inverts an SQL expressions with `NOT` operator.\n     * @param string $operator the operator to use for connecting the given operands\n     * @param array $operands the SQL expressions to connect.\n     * @param array $params the binding parameters to be populated\n     * @return string the generated SQL expression\n     * @throws InvalidArgumentException if wrong number of operands have been given.\n     * @deprecated since 2.0.14. Use `buildCondition()` instead.\n     */\n    public function buildNotCondition($operator, $operands, &$params)\n    {\n        array_unshift($operands, $operator);\n        return $this->buildCondition($operands, $params);\n    }\n\n    /**\n     * Creates an SQL expressions with the `BETWEEN` operator.\n     * @param string $operator the operator to use (e.g. `BETWEEN` or `NOT BETWEEN`)\n     * @param array $operands the first operand is the column name. The second and third operands\n     * describe the interval that column value should be in.\n     * @param array $params the binding parameters to be populated\n     * @return string the generated SQL expression\n     * @throws InvalidArgumentException if wrong number of operands have been given.\n     * @deprecated since 2.0.14. Use `buildCondition()` instead.\n     */\n    public function buildBetweenCondition($operator, $operands, &$params)\n    {\n        array_unshift($operands, $operator);\n        return $this->buildCondition($operands, $params);\n    }\n\n    /**\n     * Creates an SQL expressions with the `IN` operator.\n     * @param string $operator the operator to use (e.g. `IN` or `NOT IN`)\n     * @param array $operands the first operand is the column name. If it is an array\n     * a composite IN condition will be generated.\n     * The second operand is an array of values that column value should be among.\n     * If it is an empty array the generated expression will be a `false` value if\n     * operator is `IN` and empty if operator is `NOT IN`.\n     * @param array $params the binding parameters to be populated\n     * @return string the generated SQL expression\n     * @throws Exception if wrong number of operands have been given.\n     * @deprecated since 2.0.14. Use `buildCondition()` instead.\n     */\n    public function buildInCondition($operator, $operands, &$params)\n    {\n        array_unshift($operands, $operator);\n        return $this->buildCondition($operands, $params);\n    }\n\n    /**\n     * Creates an SQL expressions with the `LIKE` operator.\n     * @param string $operator the operator to use (e.g. `LIKE`, `NOT LIKE`, `OR LIKE` or `OR NOT LIKE`)\n     * @param array $operands an array of two or three operands\n     *\n     * - The first operand is the column name.\n     * - The second operand is a single value or an array of values that column value\n     *   should be compared with. If it is an empty array the generated expression will\n     *   be a `false` value if operator is `LIKE` or `OR LIKE`, and empty if operator\n     *   is `NOT LIKE` or `OR NOT LIKE`.\n     * - An optional third operand can also be provided to specify how to escape special characters\n     *   in the value(s). The operand should be an array of mappings from the special characters to their\n     *   escaped counterparts. If this operand is not provided, a default escape mapping will be used.\n     *   You may use `false` or an empty array to indicate the values are already escaped and no escape\n     *   should be applied. Note that when using an escape mapping (or the third operand is not provided),\n     *   the values will be automatically enclosed within a pair of percentage characters.\n     * @param array $params the binding parameters to be populated\n     * @return string the generated SQL expression\n     * @throws InvalidArgumentException if wrong number of operands have been given.\n     * @deprecated since 2.0.14. Use `buildCondition()` instead.\n     */\n    public function buildLikeCondition($operator, $operands, &$params)\n    {\n        array_unshift($operands, $operator);\n        return $this->buildCondition($operands, $params);\n    }\n\n    /**\n     * Creates an SQL expressions with the `EXISTS` operator.\n     * @param string $operator the operator to use (e.g. `EXISTS` or `NOT EXISTS`)\n     * @param array $operands contains only one element which is a [[Query]] object representing the sub-query.\n     * @param array $params the binding parameters to be populated\n     * @return string the generated SQL expression\n     * @throws InvalidArgumentException if the operand is not a [[Query]] object.\n     * @deprecated since 2.0.14. Use `buildCondition()` instead.\n     */\n    public function buildExistsCondition($operator, $operands, &$params)\n    {\n        array_unshift($operands, $operator);\n        return $this->buildCondition($operands, $params);\n    }\n\n    /**\n     * Creates an SQL expressions like `\"column\" operator value`.\n     * @param string $operator the operator to use. Anything could be used e.g. `>`, `<=`, etc.\n     * @param array $operands contains two column names.\n     * @param array $params the binding parameters to be populated\n     * @return string the generated SQL expression\n     * @throws InvalidArgumentException if wrong number of operands have been given.\n     * @deprecated since 2.0.14. Use `buildCondition()` instead.\n     */\n    public function buildSimpleCondition($operator, $operands, &$params)\n    {\n        array_unshift($operands, $operator);\n        return $this->buildCondition($operands, $params);\n    }\n\n    /**\n     * Creates a SELECT EXISTS() SQL statement.\n     * @param string $rawSql the subquery in a raw form to select from.\n     * @return string the SELECT EXISTS() SQL statement.\n     * @since 2.0.8\n     */\n    public function selectExists($rawSql)\n    {\n        return 'SELECT EXISTS(' . $rawSql . ')';\n    }\n\n    /**\n     * Helper method to add $value to $params array using [[PARAM_PREFIX]].\n     *\n     * @param string|null $value\n     * @param array $params passed by reference\n     * @return string the placeholder name in $params array\n     *\n     * @since 2.0.14\n     */\n    public function bindParam($value, &$params)\n    {\n        $phName = self::PARAM_PREFIX . count($params);\n        $params[$phName] = $value;\n\n        return $phName;\n    }\n\n    /**\n     * Extracts table alias if there is one or returns false\n     * @param $table\n     * @return bool|array\n     * @since 2.0.24\n     */\n    protected function extractAlias($table)\n    {\n        if (preg_match('/^(.*?)(?i:\\s+as|)\\s+([^ ]+)$/', $table, $matches)) {\n            return $matches;\n        }\n\n        return false;\n    }\n}\n"
  },
  {
    "path": "framework/db/QueryExpressionBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\n/**\n * Class QueryExpressionBuilder is used internally to build [[Query]] object\n * using unified [[QueryBuilder]] expression building interface.\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n */\nclass QueryExpressionBuilder implements ExpressionBuilderInterface\n{\n    use ExpressionBuilderTrait;\n\n\n    /**\n     * Method builds the raw SQL from the $expression that will not be additionally\n     * escaped or quoted.\n     *\n     * @param ExpressionInterface|Query $expression the expression to be built.\n     * @param array $params the binding parameters.\n     * @return string the raw SQL that will not be additionally escaped or quoted.\n     */\n    public function build(ExpressionInterface $expression, array &$params = [])\n    {\n        list($sql, $params) = $this->queryBuilder->build($expression, $params);\n\n        return \"($sql)\";\n    }\n}\n"
  },
  {
    "path": "framework/db/QueryInterface.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\n/**\n * The QueryInterface defines the minimum set of methods to be implemented by a database query.\n *\n * The default implementation of this interface is provided by [[QueryTrait]].\n *\n * It has support for getting [[one]] instance or [[all]].\n * Allows pagination via [[limit]] and [[offset]].\n * Sorting is supported via [[orderBy]] and items can be limited to match some conditions using [[where]].\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0\n */\ninterface QueryInterface\n{\n    /**\n     * Executes the query and returns all results as an array.\n     * @param Connection|null $db the database connection used to execute the query.\n     * If this parameter is not given, the `db` application component will be used.\n     * @return array the query results. If the query results in nothing, an empty array will be returned.\n     */\n    public function all($db = null);\n\n    /**\n     * Executes the query and returns a single row of result.\n     * @param Connection|null $db the database connection used to execute the query.\n     * If this parameter is not given, the `db` application component will be used.\n     * @return array|bool the first row (in terms of an array) of the query result. False is returned if the query\n     * results in nothing.\n     */\n    public function one($db = null);\n\n    /**\n     * Returns the number of records.\n     * @param string $q the COUNT expression. Defaults to '*'.\n     * @param Connection|null $db the database connection used to execute the query.\n     * If this parameter is not given, the `db` application component will be used.\n     * @return int|string|null number of records.\n     */\n    public function count($q = '*', $db = null);\n\n    /**\n     * Returns a value indicating whether the query result contains any row of data.\n     * @param Connection|null $db the database connection used to execute the query.\n     * If this parameter is not given, the `db` application component will be used.\n     * @return bool whether the query result contains any row of data.\n     */\n    public function exists($db = null);\n\n    /**\n     * Sets the [[indexBy]] property.\n     * @param string|callable $column the name of the column by which the query results should be indexed by.\n     * This can also be a callable (e.g. anonymous function) that returns the index value based on the given\n     * row data. The signature of the callable should be:\n     *\n     * ```\n     * function ($row)\n     * {\n     *     // return the index value corresponding to $row\n     * }\n     * ```\n     *\n     * @return $this the query object itself\n     */\n    public function indexBy($column);\n\n    /**\n     * Sets the WHERE part of the query.\n     *\n     * The `$condition` specified as an array can be in one of the following two formats:\n     *\n     * - hash format: `['column1' => value1, 'column2' => value2, ...]`\n     * - operator format: `[operator, operand1, operand2, ...]`\n     *\n     * A condition in hash format represents the following SQL expression in general:\n     * `column1=value1 AND column2=value2 AND ...`. In case when a value is an array or sub-query,\n     * an `IN` expression will be generated. And if a value is `null`, `IS NULL` will be used\n     * in the generated expression. Below are some examples:\n     *\n     * - `['type' => 1, 'status' => 2]` generates `(type = 1) AND (status = 2)`.\n     * - `['id' => [1, 2, 3], 'status' => 2]` generates `(id IN (1, 2, 3)) AND (status = 2)`.\n     * - `['status' => null]` generates `status IS NULL`.\n     *\n     * A condition in operator format generates the SQL expression according to the specified operator, which\n     * can be one of the following:\n     *\n     * - **and**: the operands should be concatenated together using `AND`. For example,\n     *   `['and', 'id=1', 'id=2']` will generate `id=1 AND id=2`. If an operand is an array,\n     *   it will be converted into a string using the rules described here. For example,\n     *   `['and', 'type=1', ['or', 'id=1', 'id=2']]` will generate `type=1 AND (id=1 OR id=2)`.\n     *   The method will *not* do any quoting or escaping.\n     *\n     * - **or**: similar to the `and` operator except that the operands are concatenated using `OR`. For example,\n     *   `['or', ['type' => [7, 8, 9]], ['id' => [1, 2, 3]]]` will generate `(type IN (7, 8, 9) OR (id IN (1, 2, 3)))`.\n     *\n     * - **not**: this will take only one operand and build the negation of it by prefixing the query string with `NOT`.\n     *   For example `['not', ['attribute' => null]]` will result in the condition `NOT (attribute IS NULL)`.\n     *\n     * - **between**: operand 1 should be the column name, and operand 2 and 3 should be the\n     *   starting and ending values of the range that the column is in.\n     *   For example, `['between', 'id', 1, 10]` will generate `id BETWEEN 1 AND 10`.\n     *\n     * - **not between**: similar to `between` except the `BETWEEN` is replaced with `NOT BETWEEN`\n     *   in the generated condition.\n     *\n     * - **in**: operand 1 should be a column or DB expression, and operand 2 be an array representing\n     *   the range of the values that the column or DB expression should be in. For example,\n     *   `['in', 'id', [1, 2, 3]]` will generate `id IN (1, 2, 3)`.\n     *   The method will properly quote the column name and escape values in the range.\n     *\n     *   To create a composite `IN` condition you can use and array for the column name and value, where the values are indexed by the column name:\n     *   `['in', ['id', 'name'], [['id' => 1, 'name' => 'foo'], ['id' => 2, 'name' => 'bar']] ]`.\n     *\n     *   You may also specify a sub-query that is used to get the values for the `IN`-condition:\n     *   `['in', 'user_id', (new Query())->select('id')->from('users')->where(['active' => 1])]`\n     *\n     * - **not in**: similar to the `in` operator except that `IN` is replaced with `NOT IN` in the generated condition.\n     *\n     * - **like**: operand 1 should be a column or DB expression, and operand 2 be a string or an array representing\n     *   the values that the column or DB expression should be like.\n     *   For example, `['like', 'name', 'tester']` will generate `name LIKE '%tester%'`.\n     *   When the value range is given as an array, multiple `LIKE` predicates will be generated and concatenated\n     *   using `AND`. For example, `['like', 'name', ['test', 'sample']]` will generate\n     *   `name LIKE '%test%' AND name LIKE '%sample%'`.\n     *   The method will properly quote the column name and escape special characters in the values.\n     *   Sometimes, you may want to add the percentage characters to the matching value by yourself, you may supply\n     *   a third operand `false` to do so. For example, `['like', 'name', '%tester', false]` will generate `name LIKE '%tester'`.\n     *\n     * - **or like**: similar to the `like` operator except that `OR` is used to concatenate the `LIKE`\n     *   predicates when operand 2 is an array.\n     *\n     * - **not like**: similar to the `like` operator except that `LIKE` is replaced with `NOT LIKE`\n     *   in the generated condition.\n     *\n     * - **or not like**: similar to the `not like` operator except that `OR` is used to concatenate\n     *   the `NOT LIKE` predicates.\n     *\n     * - **exists**: operand 1 is a query object that used to build an `EXISTS` condition. For example\n     *   `['exists', (new Query())->select('id')->from('users')->where(['active' => 1])]` will result in the following SQL expression:\n     *   `EXISTS (SELECT \"id\" FROM \"users\" WHERE \"active\"=1)`.\n     *\n     * - **not exists**: similar to the `exists` operator except that `EXISTS` is replaced with `NOT EXISTS` in the generated condition.\n     *\n     * - Additionally you can specify arbitrary operators as follows: A condition of `['>=', 'id', 10]` will result in the\n     *   following SQL expression: `id >= 10`.\n     *\n     * **Note that this method will override any existing WHERE condition. You might want to use [[andWhere()]] or [[orWhere()]] instead.**\n     *\n     * @param array $condition the conditions that should be put in the WHERE part.\n     * @return $this the query object itself\n     * @see andWhere()\n     * @see orWhere()\n     */\n    public function where($condition);\n\n    /**\n     * Adds an additional WHERE condition to the existing one.\n     * The new condition and the existing one will be joined using the 'AND' operator.\n     * @param array $condition the new WHERE condition. Please refer to [[where()]]\n     * on how to specify this parameter.\n     * @return $this the query object itself\n     * @see where()\n     * @see orWhere()\n     */\n    public function andWhere($condition);\n\n    /**\n     * Adds an additional WHERE condition to the existing one.\n     * The new condition and the existing one will be joined using the 'OR' operator.\n     * @param array $condition the new WHERE condition. Please refer to [[where()]]\n     * on how to specify this parameter.\n     * @return $this the query object itself\n     * @see where()\n     * @see andWhere()\n     */\n    public function orWhere($condition);\n\n    /**\n     * Sets the WHERE part of the query ignoring empty parameters.\n     *\n     * @param array $condition the conditions that should be put in the WHERE part. Please refer to [[where()]]\n     * on how to specify this parameter.\n     * @return $this the query object itself\n     * @see andFilterWhere()\n     * @see orFilterWhere()\n     */\n    public function filterWhere(array $condition);\n\n    /**\n     * Adds an additional WHERE condition to the existing one ignoring empty parameters.\n     * The new condition and the existing one will be joined using the 'AND' operator.\n     * @param array $condition the new WHERE condition. Please refer to [[where()]]\n     * on how to specify this parameter.\n     * @return $this the query object itself\n     * @see filterWhere()\n     * @see orFilterWhere()\n     */\n    public function andFilterWhere(array $condition);\n\n    /**\n     * Adds an additional WHERE condition to the existing one ignoring empty parameters.\n     * The new condition and the existing one will be joined using the 'OR' operator.\n     * @param array $condition the new WHERE condition. Please refer to [[where()]]\n     * on how to specify this parameter.\n     * @return $this the query object itself\n     * @see filterWhere()\n     * @see andFilterWhere()\n     */\n    public function orFilterWhere(array $condition);\n\n    /**\n     * Sets the ORDER BY part of the query.\n     * @param string|array $columns the columns (and the directions) to be ordered by.\n     * Columns can be specified in either a string (e.g. \"id ASC, name DESC\") or an array\n     * (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`).\n     * The method will automatically quote the column names unless a column contains some parenthesis\n     * (which means the column contains a DB expression).\n     * @return $this the query object itself\n     * @see addOrderBy()\n     */\n    public function orderBy($columns);\n\n    /**\n     * Adds additional ORDER BY columns to the query.\n     * @param string|array $columns the columns (and the directions) to be ordered by.\n     * Columns can be specified in either a string (e.g. \"id ASC, name DESC\") or an array\n     * (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`).\n     * The method will automatically quote the column names unless a column contains some parenthesis\n     * (which means the column contains a DB expression).\n     * @return $this the query object itself\n     * @see orderBy()\n     */\n    public function addOrderBy($columns);\n\n    /**\n     * Sets the LIMIT part of the query.\n     * @param int|null $limit the limit. Use null or negative value to disable limit.\n     * @return $this the query object itself\n     */\n    public function limit($limit);\n\n    /**\n     * Sets the OFFSET part of the query.\n     * @param int|null $offset the offset. Use null or negative value to disable offset.\n     * @return $this the query object itself\n     */\n    public function offset($offset);\n\n    /**\n     * Sets whether to emulate query execution, preventing any interaction with data storage.\n     * After this mode is enabled, methods, returning query results like [[one()]], [[all()]], [[exists()]]\n     * and so on, will return empty or false values.\n     * You should use this method in case your program logic indicates query should not return any results, like\n     * in case you set false where condition like `0=1`.\n     * @param bool $value whether to prevent query execution.\n     * @return $this the query object itself.\n     * @since 2.0.11\n     */\n    public function emulateExecution($value = true);\n}\n"
  },
  {
    "path": "framework/db/QueryTrait.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\nuse yii\\base\\NotSupportedException;\n\n/**\n * The BaseQuery trait represents the minimum method set of a database Query.\n *\n * It is supposed to be used in a class that implements the [[QueryInterface]].\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0\n */\ntrait QueryTrait\n{\n    /**\n     * @var string|array|ExpressionInterface|null query condition. This refers to the WHERE clause in a SQL statement.\n     * For example, `['age' => 31, 'team' => 1]`.\n     * @see where() for valid syntax on specifying this value.\n     */\n    public $where;\n    /**\n     * @var int|ExpressionInterface|null maximum number of records to be returned. May be an instance of [[ExpressionInterface]].\n     * If not set or less than 0, it means no limit.\n     */\n    public $limit;\n    /**\n     * @var int|ExpressionInterface|null zero-based offset from where the records are to be returned.\n     * May be an instance of [[ExpressionInterface]]. If not set or less than 0, it means starting from the beginning.\n     */\n    public $offset;\n    /**\n     * @var array|null how to sort the query results. This is used to construct the ORDER BY clause in a SQL statement.\n     * The array keys are the columns to be sorted by, and the array values are the corresponding sort directions which\n     * can be either [SORT_ASC](https://www.php.net/manual/en/array.constants.php#constant.sort-asc)\n     * or [SORT_DESC](https://www.php.net/manual/en/array.constants.php#constant.sort-desc).\n     * The array may also contain [[ExpressionInterface]] objects. If that is the case, the expressions\n     * will be converted into strings without any change.\n     */\n    public $orderBy;\n    /**\n     * @var string|callable|null the name of the column by which the query results should be indexed by.\n     * This can also be a callable (e.g. anonymous function) that returns the index value based on the given\n     * row data. For more details, see [[indexBy()]]. This property is only used by [[QueryInterface::all()|all()]].\n     */\n    public $indexBy;\n    /**\n     * @var bool whether to emulate the actual query execution, returning empty or false results.\n     * @see emulateExecution()\n     * @since 2.0.11\n     */\n    public $emulateExecution = false;\n\n\n    /**\n     * Sets the [[indexBy]] property.\n     * @param string|callable $column the name of the column by which the query results should be indexed by.\n     * This can also be a callable (e.g. anonymous function) that returns the index value based on the given\n     * row data. The signature of the callable should be:\n     *\n     * ```\n     * function ($row)\n     * {\n     *     // return the index value corresponding to $row\n     * }\n     * ```\n     *\n     * @return $this the query object itself\n     */\n    public function indexBy($column)\n    {\n        $this->indexBy = $column;\n        return $this;\n    }\n\n    /**\n     * Sets the WHERE part of the query.\n     *\n     * See [[QueryInterface::where()]] for detailed documentation.\n     *\n     * @param string|array|ExpressionInterface $condition the conditions that should be put in the WHERE part.\n     * @return $this the query object itself\n     * @see andWhere()\n     * @see orWhere()\n     */\n    public function where($condition)\n    {\n        $this->where = $condition;\n        return $this;\n    }\n\n    /**\n     * Adds an additional WHERE condition to the existing one.\n     * The new condition and the existing one will be joined using the 'AND' operator.\n     * @param string|array|ExpressionInterface $condition the new WHERE condition. Please refer to [[where()]]\n     * on how to specify this parameter.\n     * @return $this the query object itself\n     * @see where()\n     * @see orWhere()\n     */\n    public function andWhere($condition)\n    {\n        if ($this->where === null) {\n            $this->where = $condition;\n        } else {\n            $this->where = ['and', $this->where, $condition];\n        }\n\n        return $this;\n    }\n\n    /**\n     * Adds an additional WHERE condition to the existing one.\n     * The new condition and the existing one will be joined using the 'OR' operator.\n     * @param string|array|ExpressionInterface $condition the new WHERE condition. Please refer to [[where()]]\n     * on how to specify this parameter.\n     * @return $this the query object itself\n     * @see where()\n     * @see andWhere()\n     */\n    public function orWhere($condition)\n    {\n        if ($this->where === null) {\n            $this->where = $condition;\n        } else {\n            $this->where = ['or', $this->where, $condition];\n        }\n\n        return $this;\n    }\n\n    /**\n     * Sets the WHERE part of the query but ignores [[isEmpty()|empty operands]].\n     *\n     * This method is similar to [[where()]]. The main difference is that this method will\n     * remove [[isEmpty()|empty query operands]]. As a result, this method is best suited\n     * for building query conditions based on filter values entered by users.\n     *\n     * The following code shows the difference between this method and [[where()]]:\n     *\n     * ```\n     * // WHERE `age`=:age\n     * $query->filterWhere(['name' => null, 'age' => 20]);\n     * // WHERE `age`=:age\n     * $query->where(['age' => 20]);\n     * // WHERE `name` IS NULL AND `age`=:age\n     * $query->where(['name' => null, 'age' => 20]);\n     * ```\n     *\n     * Note that unlike [[where()]], you cannot pass binding parameters to this method.\n     *\n     * @param array $condition the conditions that should be put in the WHERE part.\n     * See [[where()]] on how to specify this parameter.\n     * @return $this the query object itself\n     * @see where()\n     * @see andFilterWhere()\n     * @see orFilterWhere()\n     */\n    public function filterWhere(array $condition)\n    {\n        $condition = $this->filterCondition($condition);\n        if ($condition !== []) {\n            $this->where($condition);\n        }\n\n        return $this;\n    }\n\n    /**\n     * Adds an additional WHERE condition to the existing one but ignores [[isEmpty()|empty operands]].\n     * The new condition and the existing one will be joined using the 'AND' operator.\n     *\n     * This method is similar to [[andWhere()]]. The main difference is that this method will\n     * remove [[isEmpty()|empty query operands]]. As a result, this method is best suited\n     * for building query conditions based on filter values entered by users.\n     *\n     * @param array $condition the new WHERE condition. Please refer to [[where()]]\n     * on how to specify this parameter.\n     * @return $this the query object itself\n     * @see filterWhere()\n     * @see orFilterWhere()\n     */\n    public function andFilterWhere(array $condition)\n    {\n        $condition = $this->filterCondition($condition);\n        if ($condition !== []) {\n            $this->andWhere($condition);\n        }\n\n        return $this;\n    }\n\n    /**\n     * Adds an additional WHERE condition to the existing one but ignores [[isEmpty()|empty operands]].\n     * The new condition and the existing one will be joined using the 'OR' operator.\n     *\n     * This method is similar to [[orWhere()]]. The main difference is that this method will\n     * remove [[isEmpty()|empty query operands]]. As a result, this method is best suited\n     * for building query conditions based on filter values entered by users.\n     *\n     * @param array $condition the new WHERE condition. Please refer to [[where()]]\n     * on how to specify this parameter.\n     * @return $this the query object itself\n     * @see filterWhere()\n     * @see andFilterWhere()\n     */\n    public function orFilterWhere(array $condition)\n    {\n        $condition = $this->filterCondition($condition);\n        if ($condition !== []) {\n            $this->orWhere($condition);\n        }\n\n        return $this;\n    }\n\n    /**\n     * Removes [[isEmpty()|empty operands]] from the given query condition.\n     *\n     * @param array $condition the original condition\n     * @return array the condition with [[isEmpty()|empty operands]] removed.\n     * @throws NotSupportedException if the condition operator is not supported\n     */\n    protected function filterCondition($condition)\n    {\n        if (!is_array($condition)) {\n            return $condition;\n        }\n\n        if (!isset($condition[0])) {\n            // hash format: 'column1' => 'value1', 'column2' => 'value2', ...\n            foreach ($condition as $name => $value) {\n                if ($this->isEmpty($value)) {\n                    unset($condition[$name]);\n                }\n            }\n\n            return $condition;\n        }\n\n        // operator format: operator, operand 1, operand 2, ...\n\n        $operator = array_shift($condition);\n\n        switch (strtoupper($operator)) {\n            case 'NOT':\n            case 'AND':\n            case 'OR':\n                foreach ($condition as $i => $operand) {\n                    $subCondition = $this->filterCondition($operand);\n                    if ($this->isEmpty($subCondition)) {\n                        unset($condition[$i]);\n                    } else {\n                        $condition[$i] = $subCondition;\n                    }\n                }\n\n                if (empty($condition)) {\n                    return [];\n                }\n                break;\n            case 'BETWEEN':\n            case 'NOT BETWEEN':\n                if (array_key_exists(1, $condition) && array_key_exists(2, $condition)) {\n                    if ($this->isEmpty($condition[1]) || $this->isEmpty($condition[2])) {\n                        return [];\n                    }\n                }\n                break;\n            default:\n                if (array_key_exists(1, $condition) && $this->isEmpty($condition[1])) {\n                    return [];\n                }\n        }\n\n        array_unshift($condition, $operator);\n\n        return $condition;\n    }\n\n    /**\n     * Returns a value indicating whether the give value is \"empty\".\n     *\n     * The value is considered \"empty\", if one of the following conditions is satisfied:\n     *\n     * - it is `null`,\n     * - an empty string (`''`),\n     * - a string containing only whitespace characters,\n     * - or an empty array.\n     *\n     * @param mixed $value\n     * @return bool if the value is empty\n     */\n    protected function isEmpty($value)\n    {\n        return $value === '' || $value === [] || $value === null || is_string($value) && trim($value) === '';\n    }\n\n    /**\n     * Sets the ORDER BY part of the query.\n     * @param string|array|ExpressionInterface|null $columns the columns (and the directions) to be ordered by.\n     * Columns can be specified in either a string (e.g. `\"id ASC, name DESC\"`) or an array\n     * (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`).\n     *\n     * The method will automatically quote the column names unless a column contains some parenthesis\n     * (which means the column contains a DB expression).\n     *\n     * Note that if your order-by is an expression containing commas, you should always use an array\n     * to represent the order-by information. Otherwise, the method will not be able to correctly determine\n     * the order-by columns.\n     *\n     * Since version 2.0.7, an [[ExpressionInterface]] object can be passed to specify the ORDER BY part explicitly in plain SQL.\n     * @return $this the query object itself\n     * @see addOrderBy()\n     */\n    public function orderBy($columns)\n    {\n        $this->orderBy = $this->normalizeOrderBy($columns);\n        return $this;\n    }\n\n    /**\n     * Adds additional ORDER BY columns to the query.\n     * @param string|array|ExpressionInterface $columns the columns (and the directions) to be ordered by.\n     * Columns can be specified in either a string (e.g. \"id ASC, name DESC\") or an array\n     * (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`).\n     *\n     * The method will automatically quote the column names unless a column contains some parenthesis\n     * (which means the column contains a DB expression).\n     *\n     * Note that if your order-by is an expression containing commas, you should always use an array\n     * to represent the order-by information. Otherwise, the method will not be able to correctly determine\n     * the order-by columns.\n     *\n     * Since version 2.0.7, an [[ExpressionInterface]] object can be passed to specify the ORDER BY part explicitly in plain SQL.\n     * @return $this the query object itself\n     * @see orderBy()\n     */\n    public function addOrderBy($columns)\n    {\n        $columns = $this->normalizeOrderBy($columns);\n        if ($this->orderBy === null) {\n            $this->orderBy = $columns;\n        } else {\n            $this->orderBy = array_merge($this->orderBy, $columns);\n        }\n\n        return $this;\n    }\n\n    /**\n     * Normalizes format of ORDER BY data.\n     *\n     * @param array|string|ExpressionInterface|null $columns the columns value to normalize. See [[orderBy]] and [[addOrderBy]].\n     * @return array\n     */\n    protected function normalizeOrderBy($columns)\n    {\n        if (empty($columns)) {\n            return [];\n        } elseif ($columns instanceof ExpressionInterface) {\n            return [$columns];\n        } elseif (is_array($columns)) {\n            return $columns;\n        }\n\n        $columns = preg_split('/\\s*,\\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);\n        $result = [];\n        foreach ($columns as $column) {\n            if (preg_match('/^(.*?)\\s+(asc|desc)$/i', $column, $matches)) {\n                $result[$matches[1]] = strcasecmp($matches[2], 'desc') ? SORT_ASC : SORT_DESC;\n            } else {\n                $result[$column] = SORT_ASC;\n            }\n        }\n\n        return $result;\n    }\n\n    /**\n     * Sets the LIMIT part of the query.\n     * @param int|ExpressionInterface|null $limit the limit. Use null or negative value to disable limit.\n     * @return $this the query object itself\n     */\n    public function limit($limit)\n    {\n        $this->limit = $limit;\n        return $this;\n    }\n\n    /**\n     * Sets the OFFSET part of the query.\n     * @param int|ExpressionInterface|null $offset the offset. Use null or negative value to disable offset.\n     * @return $this the query object itself\n     */\n    public function offset($offset)\n    {\n        $this->offset = $offset;\n        return $this;\n    }\n\n    /**\n     * Sets whether to emulate query execution, preventing any interaction with data storage.\n     * After this mode is enabled, methods, returning query results like [[QueryInterface::one()]],\n     * [[QueryInterface::all()]], [[QueryInterface::exists()]] and so on, will return empty or false values.\n     * You should use this method in case your program logic indicates query should not return any results, like\n     * in case you set false where condition like `0=1`.\n     * @param bool $value whether to prevent query execution.\n     * @return $this the query object itself.\n     * @since 2.0.11\n     */\n    public function emulateExecution($value = true)\n    {\n        $this->emulateExecution = $value;\n        return $this;\n    }\n}\n"
  },
  {
    "path": "framework/db/Schema.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\nuse Yii;\nuse yii\\base\\BaseObject;\nuse yii\\base\\InvalidCallException;\nuse yii\\base\\InvalidConfigException;\nuse yii\\base\\NotSupportedException;\nuse yii\\caching\\Cache;\nuse yii\\caching\\CacheInterface;\nuse yii\\caching\\TagDependency;\n\n/**\n * Schema is the base class for concrete DBMS-specific schema classes.\n *\n * Schema represents the database schema information that is DBMS specific.\n *\n * @property-read string $lastInsertID The row ID of the last row inserted, or the last value retrieved from\n * the sequence object.\n * @property-read QueryBuilder $queryBuilder The query builder for this connection.\n * @property-read string[] $schemaNames All schema names in the database, except system schemas.\n * @property-read string $serverVersion Server version as a string.\n * @property-read string[] $tableNames All table names in the database.\n * @property-read TableSchema[] $tableSchemas The metadata for all tables in the database. Each array element\n * is an instance of [[TableSchema]] or its child class.\n * @property-write string $transactionIsolationLevel The transaction isolation level to use for this\n * transaction. This can be one of [[Transaction::READ_UNCOMMITTED]], [[Transaction::READ_COMMITTED]],\n * [[Transaction::REPEATABLE_READ]] and [[Transaction::SERIALIZABLE]] but also a string containing DBMS specific\n * syntax to be used after `SET TRANSACTION ISOLATION LEVEL`.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Sergey Makinen <sergey@makinen.ru>\n * @since 2.0\n *\n * @template T of ColumnSchema = ColumnSchema\n */\nabstract class Schema extends BaseObject\n{\n    // The following are the supported abstract column data types.\n    public const TYPE_PK = 'pk';\n    public const TYPE_UPK = 'upk';\n    public const TYPE_BIGPK = 'bigpk';\n    public const TYPE_UBIGPK = 'ubigpk';\n    public const TYPE_CHAR = 'char';\n    public const TYPE_STRING = 'string';\n    public const TYPE_TEXT = 'text';\n    public const TYPE_TINYINT = 'tinyint';\n    public const TYPE_SMALLINT = 'smallint';\n    public const TYPE_INTEGER = 'integer';\n    public const TYPE_BIGINT = 'bigint';\n    public const TYPE_FLOAT = 'float';\n    public const TYPE_DOUBLE = 'double';\n    public const TYPE_DECIMAL = 'decimal';\n    public const TYPE_DATETIME = 'datetime';\n    public const TYPE_TIMESTAMP = 'timestamp';\n    public const TYPE_TIME = 'time';\n    public const TYPE_DATE = 'date';\n    public const TYPE_BINARY = 'binary';\n    public const TYPE_BOOLEAN = 'boolean';\n    public const TYPE_MONEY = 'money';\n    public const TYPE_JSON = 'json';\n    /**\n     * Schema cache version, to detect incompatibilities in cached values when the\n     * data format of the cache changes.\n     */\n    public const SCHEMA_CACHE_VERSION = 1;\n    /**\n     * @var Connection the database connection\n     */\n    public $db;\n    /**\n     * @var string the default schema name used for the current session.\n     */\n    public $defaultSchema;\n    /**\n     * @var array map of DB errors and corresponding exceptions\n     * If left part is found in DB error message exception class from the right part is used.\n     */\n    public $exceptionMap = [\n        'SQLSTATE[23' => 'yii\\db\\IntegrityException',\n    ];\n    /**\n     * @var class-string<T>|array{class?: class-string<T>, __class?: class-string<T>, ...} column schema class or class config\n     * @since 2.0.11\n     */\n    public $columnSchemaClass = 'yii\\db\\ColumnSchema';\n\n    /**\n     * @var string|string[] character used to quote schema, table, etc. names.\n     * An array of 2 characters can be used in case starting and ending characters are different.\n     * @since 2.0.14\n     */\n    protected $tableQuoteCharacter = \"'\";\n    /**\n     * @var string|string[] character used to quote column names.\n     * An array of 2 characters can be used in case starting and ending characters are different.\n     * @since 2.0.14\n     */\n    protected $columnQuoteCharacter = '\"';\n\n    /**\n     * @var array list of ALL schema names in the database, except system schemas\n     */\n    private $_schemaNames;\n    /**\n     * @var array list of ALL table names in the database\n     */\n    private $_tableNames = [];\n    /**\n     * @var array list of loaded table metadata (table name => metadata type => metadata).\n     */\n    private $_tableMetadata = [];\n    /**\n     * @var QueryBuilder the query builder for this database\n     */\n    private $_builder;\n    /**\n     * @var string server version as a string.\n     */\n    private $_serverVersion;\n\n\n    /**\n     * Resolves the table name and schema name (if any).\n     * @param string $name the table name\n     * @return TableSchema [[TableSchema]] with resolved table, schema, etc. names.\n     * @throws NotSupportedException if this method is not supported by the DBMS.\n     * @since 2.0.13\n     */\n    protected function resolveTableName($name)\n    {\n        throw new NotSupportedException(get_class($this) . ' does not support resolving table names.');\n    }\n\n    /**\n     * Returns all schema names in the database, including the default one but not system schemas.\n     * This method should be overridden by child classes in order to support this feature\n     * because the default implementation simply throws an exception.\n     * @return array all schema names in the database, except system schemas.\n     * @throws NotSupportedException if this method is not supported by the DBMS.\n     * @since 2.0.4\n     */\n    protected function findSchemaNames()\n    {\n        throw new NotSupportedException(get_class($this) . ' does not support fetching all schema names.');\n    }\n\n    /**\n     * Returns all table names in the database.\n     * This method should be overridden by child classes in order to support this feature\n     * because the default implementation simply throws an exception.\n     * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.\n     * @return array all table names in the database. The names have NO schema name prefix.\n     * @throws NotSupportedException if this method is not supported by the DBMS.\n     */\n    protected function findTableNames($schema = '')\n    {\n        throw new NotSupportedException(get_class($this) . ' does not support fetching all table names.');\n    }\n\n    /**\n     * Loads the metadata for the specified table.\n     * @param string $name table name\n     * @return TableSchema|null DBMS-dependent table metadata, `null` if the table does not exist.\n     */\n    abstract protected function loadTableSchema($name);\n\n    /**\n     * Creates a column schema for the database.\n     * This method may be overridden by child classes to create a DBMS-specific column schema.\n     * @return T column schema instance.\n     * @throws InvalidConfigException if a column schema class cannot be created.\n     */\n    protected function createColumnSchema()\n    {\n        return Yii::createObject($this->columnSchemaClass);\n    }\n\n    /**\n     * Obtains the metadata for the named table.\n     * @param string $name table name. The table name may contain schema name if any. Do not quote the table name.\n     * @param bool $refresh whether to reload the table schema even if it is found in the cache.\n     * @return TableSchema|null table metadata. `null` if the named table does not exist.\n     */\n    public function getTableSchema($name, $refresh = false)\n    {\n        return $this->getTableMetadata($name, 'schema', $refresh);\n    }\n\n    /**\n     * Returns the metadata for all tables in the database.\n     * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.\n     * @param bool $refresh whether to fetch the latest available table schemas. If this is `false`,\n     * cached data may be returned if available.\n     * @return TableSchema[] the metadata for all tables in the database.\n     * Each array element is an instance of [[TableSchema]] or its child class.\n     */\n    public function getTableSchemas($schema = '', $refresh = false)\n    {\n        return $this->getSchemaMetadata($schema, 'schema', $refresh);\n    }\n\n    /**\n     * Returns all schema names in the database, except system schemas.\n     * @param bool $refresh whether to fetch the latest available schema names. If this is false,\n     * schema names fetched previously (if available) will be returned.\n     * @return string[] all schema names in the database, except system schemas.\n     * @since 2.0.4\n     */\n    public function getSchemaNames($refresh = false)\n    {\n        if ($this->_schemaNames === null || $refresh) {\n            $this->_schemaNames = $this->findSchemaNames();\n        }\n\n        return $this->_schemaNames;\n    }\n\n    /**\n     * Returns all table names in the database.\n     * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema name.\n     * If not empty, the returned table names will be prefixed with the schema name.\n     * @param bool $refresh whether to fetch the latest available table names. If this is false,\n     * table names fetched previously (if available) will be returned.\n     * @return string[] all table names in the database.\n     */\n    public function getTableNames($schema = '', $refresh = false)\n    {\n        if (!isset($this->_tableNames[$schema]) || $refresh) {\n            $this->_tableNames[$schema] = $this->findTableNames($schema);\n        }\n\n        return $this->_tableNames[$schema];\n    }\n\n    /**\n     * @return QueryBuilder the query builder for this connection.\n     */\n    public function getQueryBuilder()\n    {\n        if ($this->_builder === null) {\n            $this->_builder = $this->createQueryBuilder();\n        }\n\n        return $this->_builder;\n    }\n\n    /**\n     * Determines the PDO type for the given PHP data value.\n     * @param mixed $data the data whose PDO type is to be determined\n     * @return int the PDO type\n     * @see https://www.php.net/manual/en/pdo.constants.php\n     */\n    public function getPdoType($data)\n    {\n        static $typeMap = [\n            // php type => PDO type\n            'boolean' => \\PDO::PARAM_BOOL,\n            'integer' => \\PDO::PARAM_INT,\n            'string' => \\PDO::PARAM_STR,\n            'resource' => \\PDO::PARAM_LOB,\n            'NULL' => \\PDO::PARAM_NULL,\n        ];\n        $type = gettype($data);\n\n        return isset($typeMap[$type]) ? $typeMap[$type] : \\PDO::PARAM_STR;\n    }\n\n    /**\n     * Refreshes the schema.\n     * This method cleans up all cached table schemas so that they can be re-created later\n     * to reflect the database schema change.\n     */\n    public function refresh()\n    {\n        /** @var CacheInterface $cache */\n        $cache = is_string($this->db->schemaCache) ? Yii::$app->get($this->db->schemaCache, false) : $this->db->schemaCache;\n        if ($this->db->enableSchemaCache && $cache instanceof CacheInterface) {\n            TagDependency::invalidate($cache, $this->getCacheTag());\n        }\n        $this->_tableNames = [];\n        $this->_tableMetadata = [];\n    }\n\n    /**\n     * Refreshes the particular table schema.\n     * This method cleans up cached table schema so that it can be re-created later\n     * to reflect the database schema change.\n     * @param string $name table name.\n     * @since 2.0.6\n     */\n    public function refreshTableSchema($name)\n    {\n        $rawName = $this->getRawTableName($name);\n        unset($this->_tableMetadata[$rawName]);\n        $this->_tableNames = [];\n        /** @var CacheInterface $cache */\n        $cache = is_string($this->db->schemaCache) ? Yii::$app->get($this->db->schemaCache, false) : $this->db->schemaCache;\n        if ($this->db->enableSchemaCache && $cache instanceof CacheInterface) {\n            $cache->delete($this->getCacheKey($rawName));\n        }\n    }\n\n    /**\n     * Creates a query builder for the database.\n     * This method may be overridden by child classes to create a DBMS-specific query builder.\n     * @return QueryBuilder query builder instance\n     */\n    public function createQueryBuilder()\n    {\n        return Yii::createObject(QueryBuilder::className(), [$this->db]);\n    }\n\n    /**\n     * Create a column schema builder instance giving the type and value precision.\n     *\n     * This method may be overridden by child classes to create a DBMS-specific column schema builder.\n     *\n     * @param string $type type of the column. See [[ColumnSchemaBuilder::$type]].\n     * @param int|string|array|null $length length or precision of the column. See [[ColumnSchemaBuilder::$length]].\n     * @return ColumnSchemaBuilder column schema builder instance\n     * @since 2.0.6\n     */\n    public function createColumnSchemaBuilder($type, $length = null)\n    {\n        return Yii::createObject(ColumnSchemaBuilder::className(), [$type, $length]);\n    }\n\n    /**\n     * Returns all unique indexes for the given table.\n     *\n     * Each array element is of the following structure:\n     *\n     * ```\n     * [\n     *  'IndexName1' => ['col1' [, ...]],\n     *  'IndexName2' => ['col2' [, ...]],\n     * ]\n     * ```\n     *\n     * This method should be overridden by child classes in order to support this feature\n     * because the default implementation simply throws an exception\n     * @param TableSchema $table the table metadata\n     * @return array all unique indexes for the given table.\n     * @throws NotSupportedException if this method is called\n     */\n    public function findUniqueIndexes($table)\n    {\n        throw new NotSupportedException(get_class($this) . ' does not support getting unique indexes information.');\n    }\n\n    /**\n     * Returns the ID of the last inserted row or sequence value.\n     * @param string $sequenceName name of the sequence object (required by some DBMS)\n     * @return string the row ID of the last row inserted, or the last value retrieved from the sequence object\n     * @throws InvalidCallException if the DB connection is not active\n     * @see https://www.php.net/manual/en/function.PDO-lastInsertId.php\n     */\n    public function getLastInsertID($sequenceName = '')\n    {\n        if ($this->db->isActive) {\n            return $this->db->pdo->lastInsertId($sequenceName === '' ? null : $this->quoteTableName($sequenceName));\n        }\n\n        throw new InvalidCallException('DB Connection is not active.');\n    }\n\n    /**\n     * @return bool whether this DBMS supports [savepoint](https://en.wikipedia.org/wiki/Savepoint).\n     */\n    public function supportsSavepoint()\n    {\n        return $this->db->enableSavepoint;\n    }\n\n    /**\n     * Creates a new savepoint.\n     * @param string $name the savepoint name\n     */\n    public function createSavepoint($name)\n    {\n        $this->db->createCommand(\"SAVEPOINT $name\")->execute();\n    }\n\n    /**\n     * Releases an existing savepoint.\n     * @param string $name the savepoint name\n     */\n    public function releaseSavepoint($name)\n    {\n        $this->db->createCommand(\"RELEASE SAVEPOINT $name\")->execute();\n    }\n\n    /**\n     * Rolls back to a previously created savepoint.\n     * @param string $name the savepoint name\n     */\n    public function rollBackSavepoint($name)\n    {\n        $this->db->createCommand(\"ROLLBACK TO SAVEPOINT $name\")->execute();\n    }\n\n    /**\n     * Sets the isolation level of the current transaction.\n     * @param string $level The transaction isolation level to use for this transaction.\n     * This can be one of [[Transaction::READ_UNCOMMITTED]], [[Transaction::READ_COMMITTED]], [[Transaction::REPEATABLE_READ]]\n     * and [[Transaction::SERIALIZABLE]] but also a string containing DBMS specific syntax to be used\n     * after `SET TRANSACTION ISOLATION LEVEL`.\n     * @see https://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels\n     */\n    public function setTransactionIsolationLevel($level)\n    {\n        $this->db->createCommand(\"SET TRANSACTION ISOLATION LEVEL $level\")->execute();\n    }\n\n    /**\n     * Executes the INSERT command, returning primary key values.\n     * @param string $table the table that new rows will be inserted into.\n     * @param array $columns the column data (name => value) to be inserted into the table.\n     * @return array|false primary key values or false if the command fails\n     * @since 2.0.4\n     */\n    public function insert($table, $columns)\n    {\n        $command = $this->db->createCommand()->insert($table, $columns);\n        if (!$command->execute()) {\n            return false;\n        }\n        $tableSchema = $this->getTableSchema($table);\n        $result = [];\n        foreach ($tableSchema->primaryKey as $name) {\n            if ($tableSchema->columns[$name]->autoIncrement) {\n                $result[$name] = $this->getLastInsertID($tableSchema->sequenceName);\n                break;\n            }\n\n            $result[$name] = isset($columns[$name]) ? $columns[$name] : $tableSchema->columns[$name]->defaultValue;\n        }\n\n        return $result;\n    }\n\n    /**\n     * Quotes a string value for use in a query.\n     * Note that if the parameter is not a string, it will be returned without change.\n     * @param string $str string to be quoted\n     * @return string the properly quoted string\n     * @see https://www.php.net/manual/en/function.PDO-quote.php\n     */\n    public function quoteValue($str)\n    {\n        if (!is_string($str)) {\n            return $str;\n        }\n\n        if (mb_stripos((string)$this->db->dsn, 'odbc:') === false && ($value = $this->db->getSlavePdo(true)->quote($str)) !== false) {\n            return $value;\n        }\n\n        // the driver doesn't support quote (e.g. oci)\n        return \"'\" . addcslashes(str_replace(\"'\", \"''\", $str), \"\\000\\n\\r\\\\\\032\") . \"'\";\n    }\n\n    /**\n     * Quotes a table name for use in a query.\n     * If the table name contains schema prefix, the prefix will also be properly quoted.\n     * If the table name is already quoted or contains '(' or '{{',\n     * then this method will do nothing.\n     * @param string $name table name\n     * @return string the properly quoted table name\n     * @see quoteSimpleTableName()\n     */\n    public function quoteTableName($name)\n    {\n\n        if (strncmp($name, '(', 1) === 0 && strpos($name, ')') === strlen($name) - 1) {\n            return $name;\n        }\n        if (strpos($name, '{{') !== false) {\n            return $name;\n        }\n        if (strpos($name, '.') === false) {\n            return $this->quoteSimpleTableName($name);\n        }\n        $parts = $this->getTableNameParts($name);\n        foreach ($parts as $i => $part) {\n            $parts[$i] = $this->quoteSimpleTableName($part);\n        }\n        return implode('.', $parts);\n    }\n\n    /**\n     * Splits full table name into parts\n     * @param string $name\n     * @return array\n     * @since 2.0.22\n     */\n    protected function getTableNameParts($name)\n    {\n        return explode('.', $name);\n    }\n\n    /**\n     * Quotes a column name for use in a query.\n     * If the column name contains prefix, the prefix will also be properly quoted.\n     * If the column name is already quoted or contains '(', '[[' or '{{',\n     * then this method will do nothing.\n     * @param string $name column name\n     * @return string the properly quoted column name\n     * @see quoteSimpleColumnName()\n     */\n    public function quoteColumnName($name)\n    {\n        if (strpos($name, '(') !== false || strpos($name, '[[') !== false) {\n            return $name;\n        }\n        if (($pos = strrpos($name, '.')) !== false) {\n            $prefix = $this->quoteTableName(substr($name, 0, $pos)) . '.';\n            $name = substr($name, $pos + 1);\n        } else {\n            $prefix = '';\n        }\n        if (strpos($name, '{{') !== false) {\n            return $name;\n        }\n\n        return $prefix . $this->quoteSimpleColumnName($name);\n    }\n\n    /**\n     * Quotes a simple table name for use in a query.\n     * A simple table name should contain the table name only without any schema prefix.\n     * If the table name is already quoted, this method will do nothing.\n     * @param string $name table name\n     * @return string the properly quoted table name\n     */\n    public function quoteSimpleTableName($name)\n    {\n        if (is_string($this->tableQuoteCharacter)) {\n            $startingCharacter = $endingCharacter = $this->tableQuoteCharacter;\n        } else {\n            list($startingCharacter, $endingCharacter) = $this->tableQuoteCharacter;\n        }\n        return strpos($name, $startingCharacter) !== false ? $name : $startingCharacter . $name . $endingCharacter;\n    }\n\n    /**\n     * Quotes a simple column name for use in a query.\n     * A simple column name should contain the column name only without any prefix.\n     * If the column name is already quoted or is the asterisk character '*', this method will do nothing.\n     * @param string $name column name\n     * @return string the properly quoted column name\n     */\n    public function quoteSimpleColumnName($name)\n    {\n        if (is_string($this->columnQuoteCharacter)) {\n            $startingCharacter = $endingCharacter = $this->columnQuoteCharacter;\n        } else {\n            list($startingCharacter, $endingCharacter) = $this->columnQuoteCharacter;\n        }\n        return $name === '*' || strpos($name, $startingCharacter) !== false ? $name : $startingCharacter . $name . $endingCharacter;\n    }\n\n    /**\n     * Unquotes a simple table name.\n     * A simple table name should contain the table name only without any schema prefix.\n     * If the table name is not quoted, this method will do nothing.\n     * @param string $name table name.\n     * @return string unquoted table name.\n     * @since 2.0.14\n     */\n    public function unquoteSimpleTableName($name)\n    {\n        if (is_string($this->tableQuoteCharacter)) {\n            $startingCharacter = $this->tableQuoteCharacter;\n        } else {\n            $startingCharacter = $this->tableQuoteCharacter[0];\n        }\n        return strpos($name, $startingCharacter) === false ? $name : substr($name, 1, -1);\n    }\n\n    /**\n     * Unquotes a simple column name.\n     * A simple column name should contain the column name only without any prefix.\n     * If the column name is not quoted or is the asterisk character '*', this method will do nothing.\n     * @param string $name column name.\n     * @return string unquoted column name.\n     * @since 2.0.14\n     */\n    public function unquoteSimpleColumnName($name)\n    {\n        if (is_string($this->columnQuoteCharacter)) {\n            $startingCharacter = $this->columnQuoteCharacter;\n        } else {\n            $startingCharacter = $this->columnQuoteCharacter[0];\n        }\n        return strpos($name, $startingCharacter) === false ? $name : substr($name, 1, -1);\n    }\n\n    /**\n     * Returns the actual name of a given table name.\n     * This method will strip off curly brackets from the given table name\n     * and replace the percentage character '%' with [[Connection::tablePrefix]].\n     * @param string $name the table name to be converted\n     * @return string the real name of the given table name\n     */\n    public function getRawTableName($name)\n    {\n        if (strpos($name, '{{') !== false) {\n            $name = preg_replace('/\\\\{\\\\{(.*?)\\\\}\\\\}/', '\\1', $name);\n\n            return str_replace('%', $this->db->tablePrefix, $name);\n        }\n\n        return $name;\n    }\n\n    /**\n     * Extracts the PHP type from abstract DB type.\n     * @param ColumnSchema $column the column schema information\n     * @return string PHP type name\n     */\n    protected function getColumnPhpType($column)\n    {\n        static $typeMap = [\n            // abstract type => php type\n            self::TYPE_TINYINT => 'integer',\n            self::TYPE_SMALLINT => 'integer',\n            self::TYPE_INTEGER => 'integer',\n            self::TYPE_BIGINT => 'integer',\n            self::TYPE_BOOLEAN => 'boolean',\n            self::TYPE_FLOAT => 'double',\n            self::TYPE_DOUBLE => 'double',\n            self::TYPE_BINARY => 'resource',\n            self::TYPE_JSON => 'array',\n        ];\n        if (isset($typeMap[$column->type])) {\n            if ($column->type === 'bigint') {\n                return PHP_INT_SIZE === 8 && !$column->unsigned ? 'integer' : 'string';\n            } elseif ($column->type === 'integer') {\n                return PHP_INT_SIZE === 4 && $column->unsigned ? 'string' : 'integer';\n            }\n\n            return $typeMap[$column->type];\n        }\n\n        return 'string';\n    }\n\n    /**\n     * Converts a DB exception to a more concrete one if possible.\n     *\n     * @param \\Exception $e\n     * @param string $rawSql SQL that produced exception\n     * @return Exception\n     */\n    public function convertException(\\Exception $e, $rawSql)\n    {\n        if ($e instanceof Exception) {\n            return $e;\n        }\n\n        $exceptionClass = '\\yii\\db\\Exception';\n        foreach ($this->exceptionMap as $error => $class) {\n            if (strpos($e->getMessage(), $error) !== false) {\n                $exceptionClass = $class;\n            }\n        }\n        $message = $e->getMessage() . \"\\nThe SQL being executed was: $rawSql\";\n        $errorInfo = $e instanceof \\PDOException ? $e->errorInfo : null;\n        return new $exceptionClass($message, $errorInfo, $e->getCode(), $e);\n    }\n\n    /**\n     * Returns a value indicating whether a SQL statement is for read purpose.\n     * @param string $sql the SQL statement\n     * @return bool whether a SQL statement is for read purpose.\n     */\n    public function isReadQuery($sql)\n    {\n        $pattern = '/^\\s*(SELECT|SHOW|DESCRIBE)\\b/i';\n        return preg_match($pattern, $sql) > 0;\n    }\n\n    /**\n     * Returns a server version as a string comparable by [[\\version_compare()]].\n     * @return string server version as a string.\n     * @since 2.0.14\n     */\n    public function getServerVersion()\n    {\n        if ($this->_serverVersion === null) {\n            $this->_serverVersion = $this->db->getSlavePdo(true)->getAttribute(\\PDO::ATTR_SERVER_VERSION);\n        }\n        return $this->_serverVersion;\n    }\n\n    /**\n     * Returns the cache key for the specified table name.\n     * @param string $name the table name.\n     * @return mixed the cache key.\n     */\n    protected function getCacheKey($name)\n    {\n        return [\n            __CLASS__,\n            $this->db->dsn,\n            $this->db->username,\n            $this->getRawTableName($name),\n        ];\n    }\n\n    /**\n     * Returns the cache tag name.\n     * This allows [[refresh()]] to invalidate all cached table schemas.\n     * @return string the cache tag name\n     */\n    protected function getCacheTag()\n    {\n        return md5(serialize([\n            __CLASS__,\n            $this->db->dsn,\n            $this->db->username,\n        ]));\n    }\n\n    /**\n     * Returns the metadata of the given type for the given table.\n     * If there's no metadata in the cache, this method will call\n     * a `'loadTable' . ucfirst($type)` named method with the table name to obtain the metadata.\n     * @param string $name table name. The table name may contain schema name if any. Do not quote the table name.\n     * @param string $type metadata type.\n     * @param bool $refresh whether to reload the table metadata even if it is found in the cache.\n     * @return mixed metadata.\n     * @since 2.0.13\n     */\n    protected function getTableMetadata($name, $type, $refresh)\n    {\n        $cache = null;\n        if ($this->db->enableSchemaCache && !in_array($name, $this->db->schemaCacheExclude, true)) {\n            $schemaCache = is_string($this->db->schemaCache) ? Yii::$app->get($this->db->schemaCache, false) : $this->db->schemaCache;\n            if ($schemaCache instanceof CacheInterface) {\n                $cache = $schemaCache;\n            }\n        }\n        $rawName = $this->getRawTableName($name);\n        if (!isset($this->_tableMetadata[$rawName])) {\n            $this->loadTableMetadataFromCache($cache, $rawName);\n        }\n        if ($refresh || !array_key_exists($type, $this->_tableMetadata[$rawName])) {\n            $this->_tableMetadata[$rawName][$type] = $this->{'loadTable' . ucfirst($type)}($rawName);\n            $this->saveTableMetadataToCache($cache, $rawName);\n        }\n\n        return $this->_tableMetadata[$rawName][$type];\n    }\n\n    /**\n     * Returns the metadata of the given type for all tables in the given schema.\n     * This method will call a `'getTable' . ucfirst($type)` named method with the table name\n     * and the refresh flag to obtain the metadata.\n     * @param string $schema the schema of the metadata. Defaults to empty string, meaning the current or default schema name.\n     * @param string $type metadata type.\n     * @param bool $refresh whether to fetch the latest available table metadata. If this is `false`,\n     * cached data may be returned if available.\n     * @return array array of metadata.\n     * @since 2.0.13\n     */\n    protected function getSchemaMetadata($schema, $type, $refresh)\n    {\n        $metadata = [];\n        $methodName = 'getTable' . ucfirst($type);\n        foreach ($this->getTableNames($schema, $refresh) as $name) {\n            if ($schema !== '') {\n                $name = $schema . '.' . $name;\n            }\n            $tableMetadata = $this->$methodName($name, $refresh);\n            if ($tableMetadata !== null) {\n                $metadata[] = $tableMetadata;\n            }\n        }\n\n        return $metadata;\n    }\n\n    /**\n     * Sets the metadata of the given type for the given table.\n     * @param string $name table name.\n     * @param string $type metadata type.\n     * @param mixed $data metadata.\n     * @since 2.0.13\n     */\n    protected function setTableMetadata($name, $type, $data)\n    {\n        $this->_tableMetadata[$this->getRawTableName($name)][$type] = $data;\n    }\n\n    /**\n     * Changes row's array key case to lower if PDO's one is set to uppercase.\n     * @param array $row row's array or an array of row's arrays.\n     * @param bool $multiple whether multiple rows or a single row passed.\n     * @return array normalized row or rows.\n     * @since 2.0.13\n     */\n    protected function normalizePdoRowKeyCase(array $row, $multiple)\n    {\n        if ($this->db->getSlavePdo(true)->getAttribute(\\PDO::ATTR_CASE) !== \\PDO::CASE_UPPER) {\n            return $row;\n        }\n\n        if ($multiple) {\n            return array_map(function (array $row) {\n                return array_change_key_case($row, CASE_LOWER);\n            }, $row);\n        }\n\n        return array_change_key_case($row, CASE_LOWER);\n    }\n\n    /**\n     * Tries to load and populate table metadata from cache.\n     * @param Cache|null $cache\n     * @param string $name\n     */\n    private function loadTableMetadataFromCache($cache, $name)\n    {\n        if ($cache === null) {\n            $this->_tableMetadata[$name] = [];\n            return;\n        }\n\n        $metadata = $cache->get($this->getCacheKey($name));\n        if (!is_array($metadata) || !isset($metadata['cacheVersion']) || $metadata['cacheVersion'] !== static::SCHEMA_CACHE_VERSION) {\n            $this->_tableMetadata[$name] = [];\n            return;\n        }\n\n        unset($metadata['cacheVersion']);\n        $this->_tableMetadata[$name] = $metadata;\n    }\n\n    /**\n     * Saves table metadata to cache.\n     * @param Cache|null $cache\n     * @param string $name\n     */\n    private function saveTableMetadataToCache($cache, $name)\n    {\n        if ($cache === null) {\n            return;\n        }\n\n        $metadata = $this->_tableMetadata[$name];\n        $metadata['cacheVersion'] = static::SCHEMA_CACHE_VERSION;\n        $cache->set(\n            $this->getCacheKey($name),\n            $metadata,\n            $this->db->schemaCacheDuration,\n            new TagDependency(['tags' => $this->getCacheTag()])\n        );\n    }\n}\n"
  },
  {
    "path": "framework/db/SchemaBuilderTrait.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\n/**\n * SchemaBuilderTrait contains shortcut methods to create instances of [[ColumnSchemaBuilder]].\n *\n * These can be used in database migrations to define database schema types using a PHP interface.\n * This is useful to define a schema in a DBMS independent way so that the application may run on\n * different DBMS the same way.\n *\n * For example you may use the following code inside your migration files:\n *\n * ```\n * $this->createTable('example_table', [\n *   'id' => $this->primaryKey(),\n *   'name' => $this->string(64)->notNull(),\n *   'type' => $this->integer()->notNull()->defaultValue(10),\n *   'description' => $this->text(),\n *   'rule_name' => $this->string(64),\n *   'data' => $this->text(),\n *   'created_at' => $this->datetime()->notNull(),\n *   'updated_at' => $this->datetime(),\n * ]);\n * ```\n *\n * @author Vasenin Matvey <vaseninm@gmail.com>\n * @since 2.0.6\n */\ntrait SchemaBuilderTrait\n{\n    /**\n     * @return Connection the database connection to be used for schema building.\n     */\n    abstract protected function getDb();\n\n    /**\n     * Creates a primary key column.\n     * @param int|null $length column size or precision definition.\n     * This parameter will be ignored if not supported by the DBMS.\n     * @return ColumnSchemaBuilder the column instance which can be further customized.\n     * @since 2.0.6\n     */\n    public function primaryKey($length = null)\n    {\n        return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_PK, $length);\n    }\n\n    /**\n     * Creates a big primary key column.\n     * @param int|null $length column size or precision definition.\n     * This parameter will be ignored if not supported by the DBMS.\n     * @return ColumnSchemaBuilder the column instance which can be further customized.\n     * @since 2.0.6\n     */\n    public function bigPrimaryKey($length = null)\n    {\n        return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_BIGPK, $length);\n    }\n\n    /**\n     * Creates a char column.\n     * @param int|null $length column size definition i.e. the maximum string length.\n     * This parameter will be ignored if not supported by the DBMS.\n     * @return ColumnSchemaBuilder the column instance which can be further customized.\n     * @since 2.0.8\n     */\n    public function char($length = null)\n    {\n        return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_CHAR, $length);\n    }\n\n    /**\n     * Creates a string column.\n     * @param int|null $length column size definition i.e. the maximum string length.\n     * This parameter will be ignored if not supported by the DBMS.\n     * @return ColumnSchemaBuilder the column instance which can be further customized.\n     * @since 2.0.6\n     */\n    public function string($length = null)\n    {\n        return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_STRING, $length);\n    }\n\n    /**\n     * Creates a text column.\n     * @return ColumnSchemaBuilder the column instance which can be further customized.\n     * @since 2.0.6\n     */\n    public function text()\n    {\n        return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_TEXT);\n    }\n\n    /**\n     * Creates a tinyint column. If tinyint is not supported by the DBMS, smallint will be used.\n     * @param int|null $length column size or precision definition.\n     * This parameter will be ignored if not supported by the DBMS.\n     * @return ColumnSchemaBuilder the column instance which can be further customized.\n     * @since 2.0.14\n     */\n    public function tinyInteger($length = null)\n    {\n        return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_TINYINT, $length);\n    }\n\n    /**\n     * Creates a smallint column.\n     * @param int|null $length column size or precision definition.\n     * This parameter will be ignored if not supported by the DBMS.\n     * @return ColumnSchemaBuilder the column instance which can be further customized.\n     * @since 2.0.6\n     */\n    public function smallInteger($length = null)\n    {\n        return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_SMALLINT, $length);\n    }\n\n    /**\n     * Creates an integer column.\n     * @param int|null $length column size or precision definition.\n     * This parameter will be ignored if not supported by the DBMS.\n     * @return ColumnSchemaBuilder the column instance which can be further customized.\n     * @since 2.0.6\n     */\n    public function integer($length = null)\n    {\n        return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_INTEGER, $length);\n    }\n\n    /**\n     * Creates a bigint column.\n     * @param int|null $length column size or precision definition.\n     * This parameter will be ignored if not supported by the DBMS.\n     * @return ColumnSchemaBuilder the column instance which can be further customized.\n     * @since 2.0.6\n     */\n    public function bigInteger($length = null)\n    {\n        return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_BIGINT, $length);\n    }\n\n    /**\n     * Creates a float column.\n     * @param int|null $precision column value precision. First parameter passed to the column type, e.g. FLOAT(precision).\n     * This parameter will be ignored if not supported by the DBMS.\n     * @return ColumnSchemaBuilder the column instance which can be further customized.\n     * @since 2.0.6\n     */\n    public function float($precision = null)\n    {\n        return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_FLOAT, $precision);\n    }\n\n    /**\n     * Creates a double column.\n     * @param int|null $precision column value precision. First parameter passed to the column type, e.g. DOUBLE(precision).\n     * This parameter will be ignored if not supported by the DBMS.\n     * @return ColumnSchemaBuilder the column instance which can be further customized.\n     * @since 2.0.6\n     */\n    public function double($precision = null)\n    {\n        return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_DOUBLE, $precision);\n    }\n\n    /**\n     * Creates a decimal column.\n     * @param int|null $precision column value precision, which is usually the total number of digits.\n     * First parameter passed to the column type, e.g. DECIMAL(precision, scale).\n     * This parameter will be ignored if not supported by the DBMS.\n     * @param int|null $scale column value scale, which is usually the number of digits after the decimal point.\n     * Second parameter passed to the column type, e.g. DECIMAL(precision, scale).\n     * This parameter will be ignored if not supported by the DBMS.\n     * @return ColumnSchemaBuilder the column instance which can be further customized.\n     * @since 2.0.6\n     */\n    public function decimal($precision = null, $scale = null)\n    {\n        $length = [];\n        if ($precision !== null) {\n            $length[] = $precision;\n        }\n        if ($scale !== null) {\n            $length[] = $scale;\n        }\n\n        return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_DECIMAL, $length);\n    }\n\n    /**\n     * Creates a datetime column.\n     * @param int|null $precision column value precision. First parameter passed to the column type, e.g. DATETIME(precision).\n     * This parameter will be ignored if not supported by the DBMS.\n     * @return ColumnSchemaBuilder the column instance which can be further customized.\n     * @since 2.0.6\n     */\n    public function dateTime($precision = null)\n    {\n        return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_DATETIME, $precision);\n    }\n\n    /**\n     * Creates a timestamp column.\n     * @param int|null $precision column value precision. First parameter passed to the column type, e.g. TIMESTAMP(precision).\n     * This parameter will be ignored if not supported by the DBMS.\n     * @return ColumnSchemaBuilder the column instance which can be further customized.\n     * @since 2.0.6\n     */\n    public function timestamp($precision = null)\n    {\n        return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_TIMESTAMP, $precision);\n    }\n\n    /**\n     * Creates a time column.\n     * @param int|null $precision column value precision. First parameter passed to the column type, e.g. TIME(precision).\n     * This parameter will be ignored if not supported by the DBMS.\n     * @return ColumnSchemaBuilder the column instance which can be further customized.\n     * @since 2.0.6\n     */\n    public function time($precision = null)\n    {\n        return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_TIME, $precision);\n    }\n\n    /**\n     * Creates a date column.\n     * @return ColumnSchemaBuilder the column instance which can be further customized.\n     * @since 2.0.6\n     */\n    public function date()\n    {\n        return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_DATE);\n    }\n\n    /**\n     * Creates a binary column.\n     * @param int|null $length column size or precision definition.\n     * This parameter will be ignored if not supported by the DBMS.\n     * @return ColumnSchemaBuilder the column instance which can be further customized.\n     * @since 2.0.6\n     */\n    public function binary($length = null)\n    {\n        return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_BINARY, $length);\n    }\n\n    /**\n     * Creates a boolean column.\n     * @return ColumnSchemaBuilder the column instance which can be further customized.\n     * @since 2.0.6\n     */\n    public function boolean()\n    {\n        return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_BOOLEAN);\n    }\n\n    /**\n     * Creates a money column.\n     * @param int|null $precision column value precision, which is usually the total number of digits.\n     * First parameter passed to the column type, e.g. DECIMAL(precision, scale).\n     * This parameter will be ignored if not supported by the DBMS.\n     * @param int|null $scale column value scale, which is usually the number of digits after the decimal point.\n     * Second parameter passed to the column type, e.g. DECIMAL(precision, scale).\n     * This parameter will be ignored if not supported by the DBMS.\n     * @return ColumnSchemaBuilder the column instance which can be further customized.\n     * @since 2.0.6\n     */\n    public function money($precision = null, $scale = null)\n    {\n        $length = [];\n        if ($precision !== null) {\n            $length[] = $precision;\n        }\n        if ($scale !== null) {\n            $length[] = $scale;\n        }\n\n        return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_MONEY, $length);\n    }\n\n    /**\n     * Creates a JSON column.\n     * @return ColumnSchemaBuilder the column instance which can be further customized.\n     * @since 2.0.14\n     */\n    public function json()\n    {\n        return $this->getDb()->getSchema()->createColumnSchemaBuilder(Schema::TYPE_JSON);\n    }\n}\n"
  },
  {
    "path": "framework/db/SqlToken.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\nuse yii\\base\\BaseObject;\n\n/**\n * SqlToken represents SQL tokens produced by [[SqlTokenizer]] or its child classes.\n *\n * @property SqlToken[] $children Child tokens.\n * @property-read bool $hasChildren Whether the token has children.\n * @property-read bool $isCollection Whether the token represents a collection of tokens.\n * @property-read string $sql SQL code.\n *\n * @author Sergey Makinen <sergey@makinen.ru>\n * @since 2.0.13\n *\n * @implements \\ArrayAccess<int, SqlToken>\n */\nclass SqlToken extends BaseObject implements \\ArrayAccess\n{\n    public const TYPE_CODE = 0;\n    public const TYPE_STATEMENT = 1;\n    public const TYPE_TOKEN = 2;\n    public const TYPE_PARENTHESIS = 3;\n    public const TYPE_KEYWORD = 4;\n    public const TYPE_OPERATOR = 5;\n    public const TYPE_IDENTIFIER = 6;\n    public const TYPE_STRING_LITERAL = 7;\n    /**\n     * @var int token type. It has to be one of the following constants:\n     *\n     * - [[TYPE_CODE]]\n     * - [[TYPE_STATEMENT]]\n     * - [[TYPE_TOKEN]]\n     * - [[TYPE_PARENTHESIS]]\n     * - [[TYPE_KEYWORD]]\n     * - [[TYPE_OPERATOR]]\n     * - [[TYPE_IDENTIFIER]]\n     * - [[TYPE_STRING_LITERAL]]\n     */\n    public $type = self::TYPE_TOKEN;\n    /**\n     * @var string|null token content.\n     */\n    public $content;\n    /**\n     * @var int original SQL token start position.\n     */\n    public $startOffset;\n    /**\n     * @var int original SQL token end position.\n     */\n    public $endOffset;\n    /**\n     * @var SqlToken parent token.\n     */\n    public $parent;\n\n    /**\n     * @var SqlToken[] token children.\n     */\n    private $_children = [];\n\n\n    /**\n     * Returns the SQL code representing the token.\n     * @return string SQL code.\n     */\n    public function __toString()\n    {\n        return $this->getSql();\n    }\n\n    /**\n     * Returns whether there is a child token at the specified offset.\n     * This method is required by the SPL [[\\ArrayAccess]] interface.\n     * It is implicitly called when you use something like `isset($token[$offset])`.\n     * @param int $offset child token offset.\n     * @return bool whether the token exists.\n     */\n    #[\\ReturnTypeWillChange]\n    public function offsetExists($offset)\n    {\n        return isset($this->_children[$this->calculateOffset($offset)]);\n    }\n\n    /**\n     * Returns a child token at the specified offset.\n     * This method is required by the SPL [[\\ArrayAccess]] interface.\n     * It is implicitly called when you use something like `$child = $token[$offset];`.\n     * @param int $offset child token offset.\n     * @return SqlToken|null the child token at the specified offset, `null` if there's no token.\n     */\n    #[\\ReturnTypeWillChange]\n    public function offsetGet($offset)\n    {\n        $offset = $this->calculateOffset($offset);\n        return isset($this->_children[$offset]) ? $this->_children[$offset] : null;\n    }\n\n    /**\n     * Adds a child token to the token.\n     * This method is required by the SPL [[\\ArrayAccess]] interface.\n     * It is implicitly called when you use something like `$token[$offset] = $child;`.\n     * @param int|null $offset child token offset.\n     * @param SqlToken $token token to be added.\n     */\n    #[\\ReturnTypeWillChange]\n    public function offsetSet($offset, $token)\n    {\n        $token->parent = $this;\n        if ($offset === null) {\n            $this->_children[] = $token;\n        } else {\n            $this->_children[$this->calculateOffset($offset)] = $token;\n        }\n        $this->updateCollectionOffsets();\n    }\n\n    /**\n     * Removes a child token at the specified offset.\n     * This method is required by the SPL [[\\ArrayAccess]] interface.\n     * It is implicitly called when you use something like `unset($token[$offset])`.\n     * @param int $offset child token offset.\n     */\n    #[\\ReturnTypeWillChange]\n    public function offsetUnset($offset)\n    {\n        $offset = $this->calculateOffset($offset);\n        if (isset($this->_children[$offset])) {\n            array_splice($this->_children, $offset, 1);\n        }\n        $this->updateCollectionOffsets();\n    }\n\n    /**\n     * Returns child tokens.\n     * @return SqlToken[] child tokens.\n     */\n    public function getChildren()\n    {\n        return $this->_children;\n    }\n\n    /**\n     * Sets a list of child tokens.\n     * @param SqlToken[] $children child tokens.\n     */\n    public function setChildren($children)\n    {\n        $this->_children = [];\n        foreach ($children as $child) {\n            $child->parent = $this;\n            $this->_children[] = $child;\n        }\n        $this->updateCollectionOffsets();\n    }\n\n    /**\n     * Returns whether the token represents a collection of tokens.\n     * @return bool whether the token represents a collection of tokens.\n     */\n    public function getIsCollection()\n    {\n        return in_array($this->type, [\n            self::TYPE_CODE,\n            self::TYPE_STATEMENT,\n            self::TYPE_PARENTHESIS,\n        ], true);\n    }\n\n    /**\n     * Returns whether the token represents a collection of tokens and has non-zero number of children.\n     * @return bool whether the token has children.\n     */\n    public function getHasChildren()\n    {\n        return $this->getIsCollection() && !empty($this->_children);\n    }\n\n    /**\n     * Returns the SQL code representing the token.\n     * @return string SQL code.\n     */\n    public function getSql()\n    {\n        $code = $this;\n        while ($code->parent !== null) {\n            $code = $code->parent;\n        }\n\n        return mb_substr($code->content, $this->startOffset, $this->endOffset - $this->startOffset, 'UTF-8');\n    }\n\n    /**\n     * Returns whether this token (including its children) matches the specified \"pattern\" SQL code.\n     *\n     * Usage Example:\n     *\n     * ```\n     * $patternToken = (new \\yii\\db\\sqlite\\SqlTokenizer('SELECT any FROM any'))->tokenize();\n     * if ($sqlToken->matches($patternToken, 0, $firstMatchIndex, $lastMatchIndex)) {\n     *     // ...\n     * }\n     * ```\n     *\n     * @param SqlToken $patternToken tokenized SQL code to match against. In addition to normal SQL, the\n     * `any` keyword is supported which will match any number of keywords, identifiers, whitespaces.\n     * @param int $offset token children offset to start lookup with.\n     * @param int|null $firstMatchIndex token children offset where a successful match begins.\n     * @param int|null $lastMatchIndex token children offset where a successful match ends.\n     * @return bool whether this token matches the pattern SQL code.\n     */\n    public function matches(SqlToken $patternToken, $offset = 0, &$firstMatchIndex = null, &$lastMatchIndex = null)\n    {\n        if (!$patternToken->getHasChildren()) {\n            return false;\n        }\n\n        $patternToken = $patternToken[0];\n        return $this->tokensMatch($patternToken, $this, $offset, $firstMatchIndex, $lastMatchIndex);\n    }\n\n    /**\n     * Tests the given token to match the specified pattern token.\n     * @param SqlToken $patternToken\n     * @param SqlToken $token\n     * @param int $offset\n     * @param int|null $firstMatchIndex\n     * @param int|null $lastMatchIndex\n     * @return bool\n     */\n    private function tokensMatch(SqlToken $patternToken, SqlToken $token, $offset = 0, &$firstMatchIndex = null, &$lastMatchIndex = null)\n    {\n        if (\n            $patternToken->getIsCollection() !== $token->getIsCollection()\n            || (!$patternToken->getIsCollection() && $patternToken->content !== $token->content)\n        ) {\n            return false;\n        }\n\n        if ($patternToken->children === $token->children) {\n            $firstMatchIndex = $lastMatchIndex = $offset;\n            return true;\n        }\n\n        $firstMatchIndex = $lastMatchIndex = null;\n        $wildcard = false;\n        for ($index = 0, $count = count($patternToken->children); $index < $count; $index++) {\n            // Here we iterate token by token with an exception of \"any\" that toggles\n            // an iteration until we matched with a next pattern token or EOF.\n            if ($patternToken[$index]->content === 'any') {\n                $wildcard = true;\n                continue;\n            }\n\n            for ($limit = $wildcard ? count($token->children) : $offset + 1; $offset < $limit; $offset++) {\n                if (!$wildcard && !isset($token[$offset])) {\n                    break;\n                }\n\n                if (!$this->tokensMatch($patternToken[$index], $token[$offset])) {\n                    continue;\n                }\n\n                if ($firstMatchIndex === null) {\n                    $firstMatchIndex = $offset;\n                }\n                $lastMatchIndex = $offset;\n                $wildcard = false;\n                $offset++;\n                continue 2;\n            }\n\n            return false;\n        }\n\n        return true;\n    }\n\n    /**\n     * Returns an absolute offset in the children array.\n     * @param int $offset\n     * @return int\n     */\n    private function calculateOffset($offset)\n    {\n        if ($offset >= 0) {\n            return $offset;\n        }\n\n        return count($this->_children) + $offset;\n    }\n\n    /**\n     * Updates token SQL code start and end offsets based on its children.\n     */\n    private function updateCollectionOffsets()\n    {\n        if (!empty($this->_children)) {\n            $this->startOffset = reset($this->_children)->startOffset;\n            $this->endOffset = end($this->_children)->endOffset;\n        }\n        if ($this->parent !== null) {\n            $this->parent->updateCollectionOffsets();\n        }\n    }\n}\n"
  },
  {
    "path": "framework/db/SqlTokenizer.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\nuse yii\\base\\Component;\nuse yii\\base\\InvalidArgumentException;\n\n/**\n * SqlTokenizer splits an SQL query into individual SQL tokens.\n *\n * It can be used to obtain an addition information from an SQL code.\n *\n * Usage example:\n *\n * ```\n * $tokenizer = new SqlTokenizer(\"SELECT * FROM user WHERE id = 1\");\n * $root = $tokeinzer->tokenize();\n * $sqlTokens = $root->getChildren();\n * ```\n *\n * Tokens are instances of [[SqlToken]].\n *\n * @author Sergey Makinen <sergey@makinen.ru>\n * @since 2.0.13\n */\nabstract class SqlTokenizer extends Component\n{\n    /**\n     * @var string SQL code.\n     */\n    public $sql;\n\n    /**\n     * @var int SQL code string length.\n     */\n    protected $length;\n    /**\n     * @var int SQL code string current offset.\n     */\n    protected $offset;\n\n    /**\n     * @var \\SplStack<SqlToken> stack of active tokens.\n     */\n    private $_tokenStack;\n    /**\n     * @var SqlToken active token. It's usually a top of the token stack.\n     */\n    private $_currentToken;\n    /**\n     * @var string[] cached substrings.\n     */\n    private $_substrings;\n    /**\n     * @var string current buffer value.\n     */\n    private $_buffer = '';\n    /**\n     * @var SqlToken resulting token of a last [[tokenize()]] call.\n     */\n    private $_token;\n\n\n    /**\n     * Constructor.\n     * @param string $sql SQL code to be tokenized.\n     * @param array $config name-value pairs that will be used to initialize the object properties\n     */\n    public function __construct($sql, $config = [])\n    {\n        $this->sql = $sql;\n        parent::__construct($config);\n    }\n\n    /**\n     * Tokenizes and returns a code type token.\n     * @return SqlToken code type token.\n     */\n    public function tokenize()\n    {\n        $this->length = mb_strlen($this->sql, 'UTF-8');\n        $this->offset = 0;\n        $this->_substrings = [];\n        $this->_buffer = '';\n        $this->_token = new SqlToken([\n            'type' => SqlToken::TYPE_CODE,\n            'content' => $this->sql,\n        ]);\n        $this->_tokenStack = new \\SplStack();\n        $this->_tokenStack->push($this->_token);\n        $this->_token[] = new SqlToken(['type' => SqlToken::TYPE_STATEMENT]);\n        $this->_tokenStack->push($this->_token[0]);\n        $this->_currentToken = $this->_tokenStack->top();\n        while (!$this->isEof()) {\n            if ($this->isWhitespace($length) || $this->isComment($length)) {\n                $this->addTokenFromBuffer();\n                $this->advance($length);\n                continue;\n            }\n\n            if ($this->tokenizeOperator($length) || $this->tokenizeDelimitedString($length)) {\n                $this->advance($length);\n                continue;\n            }\n\n            $this->_buffer .= $this->substring(1);\n            $this->advance(1);\n        }\n        $this->addTokenFromBuffer();\n        if ($this->_token->getHasChildren() && !$this->_token[-1]->getHasChildren()) {\n            unset($this->_token[-1]);\n        }\n\n        return $this->_token;\n    }\n\n    /**\n     * Returns whether there's a whitespace at the current offset.\n     * If this methos returns `true`, it has to set the `$length` parameter to the length of the matched string.\n     * @param int $length length of the matched string.\n     * @return bool whether there's a whitespace at the current offset.\n     */\n    abstract protected function isWhitespace(&$length);\n\n    /**\n     * Returns whether there's a commentary at the current offset.\n     * If this methos returns `true`, it has to set the `$length` parameter to the length of the matched string.\n     * @param int $length length of the matched string.\n     * @return bool whether there's a commentary at the current offset.\n     */\n    abstract protected function isComment(&$length);\n\n    /**\n     * Returns whether there's an operator at the current offset.\n     * If this methos returns `true`, it has to set the `$length` parameter to the length of the matched string.\n     * It may also set `$content` to a string that will be used as a token content.\n     * @param int $length length of the matched string.\n     * @param string $content optional content instead of the matched string.\n     * @return bool whether there's an operator at the current offset.\n     */\n    abstract protected function isOperator(&$length, &$content);\n\n    /**\n     * Returns whether there's an identifier at the current offset.\n     * If this methos returns `true`, it has to set the `$length` parameter to the length of the matched string.\n     * It may also set `$content` to a string that will be used as a token content.\n     * @param int $length length of the matched string.\n     * @param string $content optional content instead of the matched string.\n     * @return bool whether there's an identifier at the current offset.\n     */\n    abstract protected function isIdentifier(&$length, &$content);\n\n    /**\n     * Returns whether there's a string literal at the current offset.\n     * If this methos returns `true`, it has to set the `$length` parameter to the length of the matched string.\n     * It may also set `$content` to a string that will be used as a token content.\n     * @param int $length length of the matched string.\n     * @param string $content optional content instead of the matched string.\n     * @return bool whether there's a string literal at the current offset.\n     */\n    abstract protected function isStringLiteral(&$length, &$content);\n\n    /**\n     * Returns whether the given string is a keyword.\n     * The method may set `$content` to a string that will be used as a token content.\n     * @param string $string string to be matched.\n     * @param string $content optional content instead of the matched string.\n     * @return bool whether the given string is a keyword.\n     */\n    abstract protected function isKeyword($string, &$content);\n\n    /**\n     * Returns whether the longest common prefix equals to the SQL code of the same length at the current offset.\n     * @param array<int, string>|array<int, array<string, mixed>> $with strings to be tested.\n     * The method **will** modify this parameter to speed up lookups.\n     * @param bool $caseSensitive whether to perform a case sensitive comparison.\n     * @param int|null $length length of the matched string.\n     * @param string|null $content matched string.\n     * @return bool whether a match is found.\n     */\n    protected function startsWithAnyLongest(array &$with, $caseSensitive, &$length = null, &$content = null)\n    {\n        if (empty($with)) {\n            return false;\n        }\n\n        if (!is_array(reset($with))) {\n            usort($with, function ($string1, $string2) {\n                return mb_strlen($string2, 'UTF-8') - mb_strlen($string1, 'UTF-8');\n            });\n            $map = [];\n            /** @var string $string */\n            foreach ($with as $string) {\n                $map[mb_strlen($string, 'UTF-8')][$caseSensitive ? $string : mb_strtoupper($string, 'UTF-8')] = true;\n            }\n            $with = $map;\n        }\n        foreach ($with as $testLength => $testValues) {\n            $content = $this->substring($testLength, $caseSensitive);\n            if (isset($testValues[$content])) {\n                $length = $testLength;\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * Returns a string of the given length starting with the specified offset.\n     * @param int $length string length to be returned.\n     * @param bool $caseSensitive if it's `false`, the string will be uppercased.\n     * @param int|null $offset SQL code offset, defaults to current if `null` is passed.\n     * @return string result string, it may be empty if there's nothing to return.\n     */\n    protected function substring($length, $caseSensitive = true, $offset = null)\n    {\n        if ($offset === null) {\n            $offset = $this->offset;\n        }\n        if ($offset + $length > $this->length) {\n            return '';\n        }\n\n        $cacheKey = $offset . ',' . $length;\n        if (!isset($this->_substrings[$cacheKey . ',1'])) {\n            $this->_substrings[$cacheKey . ',1'] = mb_substr($this->sql, $offset, $length, 'UTF-8');\n        }\n        if (!$caseSensitive && !isset($this->_substrings[$cacheKey . ',0'])) {\n            $this->_substrings[$cacheKey . ',0'] = mb_strtoupper($this->_substrings[$cacheKey . ',1'], 'UTF-8');\n        }\n\n        return $this->_substrings[$cacheKey . ',' . (int) $caseSensitive];\n    }\n\n    /**\n     * Returns an index after the given string in the SQL code starting with the specified offset.\n     * @param string $string string to be found.\n     * @param int|null $offset SQL code offset, defaults to current if `null` is passed.\n     * @return int index after the given string or end of string index.\n     */\n    protected function indexAfter($string, $offset = null)\n    {\n        if ($offset === null) {\n            $offset = $this->offset;\n        }\n        if ($offset + mb_strlen($string, 'UTF-8') > $this->length) {\n            return $this->length;\n        }\n\n        $afterIndexOf = mb_strpos($this->sql, $string, $offset, 'UTF-8');\n        if ($afterIndexOf === false) {\n            $afterIndexOf = $this->length;\n        } else {\n            $afterIndexOf += mb_strlen($string, 'UTF-8');\n        }\n\n        return $afterIndexOf;\n    }\n\n    /**\n     * Determines whether there is a delimited string at the current offset and adds it to the token children.\n     * @param int $length\n     * @return bool\n     */\n    private function tokenizeDelimitedString(&$length)\n    {\n        $isIdentifier = $this->isIdentifier($length, $content);\n        $isStringLiteral = !$isIdentifier && $this->isStringLiteral($length, $content);\n        if (!$isIdentifier && !$isStringLiteral) {\n            return false;\n        }\n\n        $this->addTokenFromBuffer();\n        $this->_currentToken[] = new SqlToken([\n            'type' => $isIdentifier ? SqlToken::TYPE_IDENTIFIER : SqlToken::TYPE_STRING_LITERAL,\n            'content' => is_string($content) ? $content : $this->substring($length),\n            'startOffset' => $this->offset,\n            'endOffset' => $this->offset + $length,\n        ]);\n        return true;\n    }\n\n    /**\n     * Determines whether there is an operator at the current offset and adds it to the token children.\n     * @param int $length\n     * @return bool\n     */\n    private function tokenizeOperator(&$length)\n    {\n        if (!$this->isOperator($length, $content)) {\n            return false;\n        }\n\n        $this->addTokenFromBuffer();\n        switch ($this->substring($length)) {\n            case '(':\n                $this->_currentToken[] = new SqlToken([\n                    'type' => SqlToken::TYPE_OPERATOR,\n                    'content' => is_string($content) ? $content : $this->substring($length),\n                    'startOffset' => $this->offset,\n                    'endOffset' => $this->offset + $length,\n                ]);\n                $this->_currentToken[] = new SqlToken(['type' => SqlToken::TYPE_PARENTHESIS]);\n                $this->_tokenStack->push($this->_currentToken[-1]);\n                $this->_currentToken = $this->_tokenStack->top();\n                break;\n            case ')':\n                $this->_tokenStack->pop();\n                $this->_currentToken = $this->_tokenStack->top();\n                $this->_currentToken[] = new SqlToken([\n                    'type' => SqlToken::TYPE_OPERATOR,\n                    'content' => ')',\n                    'startOffset' => $this->offset,\n                    'endOffset' => $this->offset + $length,\n                ]);\n                break;\n            case ';':\n                if (!$this->_currentToken->getHasChildren()) {\n                    break;\n                }\n\n                $this->_currentToken[] = new SqlToken([\n                    'type' => SqlToken::TYPE_OPERATOR,\n                    'content' => is_string($content) ? $content : $this->substring($length),\n                    'startOffset' => $this->offset,\n                    'endOffset' => $this->offset + $length,\n                ]);\n                $this->_tokenStack->pop();\n                $this->_currentToken = $this->_tokenStack->top();\n                $this->_currentToken[] = new SqlToken(['type' => SqlToken::TYPE_STATEMENT]);\n                $this->_tokenStack->push($this->_currentToken[-1]);\n                $this->_currentToken = $this->_tokenStack->top();\n                break;\n            default:\n                $this->_currentToken[] = new SqlToken([\n                    'type' => SqlToken::TYPE_OPERATOR,\n                    'content' => is_string($content) ? $content : $this->substring($length),\n                    'startOffset' => $this->offset,\n                    'endOffset' => $this->offset + $length,\n                ]);\n                break;\n        }\n\n        return true;\n    }\n\n    /**\n     * Determines a type of text in the buffer, tokenizes it and adds it to the token children.\n     */\n    private function addTokenFromBuffer()\n    {\n        if ($this->_buffer === '') {\n            return;\n        }\n\n        $isKeyword = $this->isKeyword($this->_buffer, $content);\n        $this->_currentToken[] = new SqlToken([\n            'type' => $isKeyword ? SqlToken::TYPE_KEYWORD : SqlToken::TYPE_TOKEN,\n            'content' => is_string($content) ? $content : $this->_buffer,\n            'startOffset' => $this->offset - mb_strlen($this->_buffer, 'UTF-8'),\n            'endOffset' => $this->offset,\n        ]);\n        $this->_buffer = '';\n    }\n\n    /**\n     * Adds the specified length to the current offset.\n     * @param int $length\n     * @throws InvalidArgumentException\n     */\n    private function advance($length)\n    {\n        if ($length <= 0) {\n            throw new InvalidArgumentException('Length must be greater than 0.');\n        }\n\n        $this->offset += $length;\n        $this->_substrings = [];\n    }\n\n    /**\n     * Returns whether the SQL code is completely traversed.\n     * @return bool\n     */\n    private function isEof()\n    {\n        return $this->offset >= $this->length;\n    }\n}\n"
  },
  {
    "path": "framework/db/StaleObjectException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\n/**\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass StaleObjectException extends Exception\n{\n    /**\n     * @return string the user-friendly name of this exception\n     */\n    public function getName()\n    {\n        return 'Stale Object Exception';\n    }\n}\n"
  },
  {
    "path": "framework/db/TableSchema.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\nuse yii\\base\\BaseObject;\nuse yii\\base\\InvalidArgumentException;\n\n/**\n * TableSchema represents the metadata of a database table.\n *\n * @property-read array $columnNames List of column names.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass TableSchema extends BaseObject\n{\n    /**\n     * @var string the name of the schema that this table belongs to.\n     */\n    public $schemaName;\n    /**\n     * @var string the name of this table. The schema name is not included. Use [[fullName]] to get the name with schema name prefix.\n     */\n    public $name;\n    /**\n     * @var string the full name of this table, which includes the schema name prefix, if any.\n     * Note that if the schema name is the same as the [[Schema::defaultSchema|default schema name]],\n     * the schema name will not be included.\n     */\n    public $fullName;\n    /**\n     * @var string[] primary keys of this table.\n     */\n    public $primaryKey = [];\n    /**\n     * @var string|null sequence name for the primary key. Null if no sequence.\n     */\n    public $sequenceName;\n    /**\n     * @var array foreign keys of this table. Each array element is of the following structure:\n     *\n     * ```\n     * [\n     *  'ForeignTableName',\n     *  'fk1' => 'pk1',  // pk1 is in foreign table\n     *  'fk2' => 'pk2',  // if composite foreign key\n     * ]\n     * ```\n     */\n    public $foreignKeys = [];\n    /**\n     * @var ColumnSchema[] column metadata of this table. Each array element is a [[ColumnSchema]] object, indexed by column names.\n     */\n    public $columns = [];\n\n\n    /**\n     * Gets the named column metadata.\n     * This is a convenient method for retrieving a named column even if it does not exist.\n     * @param string $name column name\n     * @return ColumnSchema|null metadata of the named column. Null if the named column does not exist.\n     */\n    public function getColumn($name)\n    {\n        return isset($this->columns[$name]) ? $this->columns[$name] : null;\n    }\n\n    /**\n     * Returns the names of all columns in this table.\n     * @return array list of column names\n     */\n    public function getColumnNames()\n    {\n        return array_keys($this->columns);\n    }\n\n    /**\n     * Manually specifies the primary key for this table.\n     * @param string|array $keys the primary key (can be composite)\n     * @throws InvalidArgumentException if the specified key cannot be found in the table.\n     */\n    public function fixPrimaryKey($keys)\n    {\n        $keys = (array) $keys;\n        $this->primaryKey = $keys;\n        foreach ($this->columns as $column) {\n            $column->isPrimaryKey = false;\n        }\n        foreach ($keys as $key) {\n            if (isset($this->columns[$key])) {\n                $this->columns[$key]->isPrimaryKey = true;\n            } else {\n                throw new InvalidArgumentException(\"Primary key '$key' cannot be found in table '{$this->name}'.\");\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "framework/db/Transaction.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\nuse Yii;\nuse yii\\base\\InvalidConfigException;\nuse yii\\base\\NotSupportedException;\n\n/**\n * Transaction represents a DB transaction.\n *\n * It is usually created by calling [[Connection::beginTransaction()]].\n *\n * The following code is a typical example of using transactions (note that some\n * DBMS may not support transactions):\n *\n * ```\n * $transaction = $connection->beginTransaction();\n * try {\n *     $connection->createCommand($sql1)->execute();\n *     $connection->createCommand($sql2)->execute();\n *     //.... other SQL executions\n *     $transaction->commit();\n * } catch (\\Exception $e) {\n *     $transaction->rollBack();\n *     throw $e;\n * } catch (\\Throwable $e) {\n *     $transaction->rollBack();\n *     throw $e;\n * }\n * ```\n *\n * > Note: in the above code we have two catch-blocks for compatibility\n * > with PHP 5.x and PHP 7.x. `\\Exception` implements the [`\\Throwable` interface](https://www.php.net/manual/en/class.throwable.php)\n * > since PHP 7.0, so you can skip the part with `\\Exception` if your app uses only PHP 7.0 and higher.\n *\n * @property-read bool $isActive Whether this transaction is active. Only an active transaction can\n * [[commit()]] or [[rollBack()]].\n * @property-write string $isolationLevel The transaction isolation level to use for this transaction. This\n * can be one of [[READ_UNCOMMITTED]], [[READ_COMMITTED]], [[REPEATABLE_READ]] and [[SERIALIZABLE]] but also a\n * string containing DBMS specific syntax to be used after `SET TRANSACTION ISOLATION LEVEL`.\n * @property-read int $level The current nesting level of the transaction.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Transaction extends \\yii\\base\\BaseObject\n{\n    /**\n     * A constant representing the transaction isolation level `READ UNCOMMITTED`.\n     * @see https://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels\n     */\n    public const READ_UNCOMMITTED = 'READ UNCOMMITTED';\n    /**\n     * A constant representing the transaction isolation level `READ COMMITTED`.\n     * @see https://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels\n     */\n    public const READ_COMMITTED = 'READ COMMITTED';\n    /**\n     * A constant representing the transaction isolation level `REPEATABLE READ`.\n     * @see https://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels\n     */\n    public const REPEATABLE_READ = 'REPEATABLE READ';\n    /**\n     * A constant representing the transaction isolation level `SERIALIZABLE`.\n     * @see https://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels\n     */\n    public const SERIALIZABLE = 'SERIALIZABLE';\n    /**\n     * @var Connection the database connection that this transaction is associated with.\n     */\n    public $db;\n\n    /**\n     * @var int the nesting level of the transaction. 0 means the outermost level.\n     */\n    private $_level = 0;\n\n\n    /**\n     * Returns a value indicating whether this transaction is active.\n     * @return bool whether this transaction is active. Only an active transaction\n     * can [[commit()]] or [[rollBack()]].\n     */\n    public function getIsActive()\n    {\n        return $this->_level > 0 && $this->db && $this->db->isActive;\n    }\n\n    /**\n     * Begins a transaction.\n     * @param string|null $isolationLevel The [isolation level][] to use for this transaction.\n     * This can be one of [[READ_UNCOMMITTED]], [[READ_COMMITTED]], [[REPEATABLE_READ]] and [[SERIALIZABLE]] but\n     * also a string containing DBMS specific syntax to be used after `SET TRANSACTION ISOLATION LEVEL`.\n     * If not specified (`null`) the isolation level will not be set explicitly and the DBMS default will be used.\n     *\n     * > Note: This setting does not work for PostgreSQL, where setting the isolation level before the transaction\n     * has no effect. You have to call [[setIsolationLevel()]] in this case after the transaction has started.\n     *\n     * > Note: Some DBMS allow setting of the isolation level only for the whole connection so subsequent transactions\n     * may get the same isolation level even if you did not specify any. When using this feature\n     * you may need to set the isolation level for all transactions explicitly to avoid conflicting settings.\n     * At the time of this writing affected DBMS are MSSQL and SQLite.\n     *\n     * [isolation level]: https://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels\n     *\n     * Starting from version 2.0.16, this method throws exception when beginning nested transaction and underlying DBMS\n     * does not support savepoints.\n     * @throws InvalidConfigException if [[db]] is `null`\n     * @throws NotSupportedException if the DBMS does not support nested transactions\n     * @throws Exception if DB connection fails\n     */\n    public function begin($isolationLevel = null)\n    {\n        if ($this->db === null) {\n            throw new InvalidConfigException('Transaction::db must be set.');\n        }\n        $this->db->open();\n\n        if ($this->_level === 0) {\n            if ($isolationLevel !== null) {\n                $this->db->getSchema()->setTransactionIsolationLevel($isolationLevel);\n            }\n            Yii::debug('Begin transaction' . ($isolationLevel ? ' with isolation level ' . $isolationLevel : ''), __METHOD__);\n\n            $this->db->trigger(Connection::EVENT_BEGIN_TRANSACTION);\n            $this->db->pdo->beginTransaction();\n            $this->_level = 1;\n\n            return;\n        }\n\n        $schema = $this->db->getSchema();\n        if ($schema->supportsSavepoint()) {\n            Yii::debug('Set savepoint ' . $this->_level, __METHOD__);\n            // make sure the transaction wasn't autocommitted\n            if ($this->db->pdo->inTransaction()) {\n                $schema->createSavepoint('LEVEL' . $this->_level);\n            }\n        } else {\n            Yii::info('Transaction not started: nested transaction not supported', __METHOD__);\n            throw new NotSupportedException('Transaction not started: nested transaction not supported.');\n        }\n        $this->_level++;\n    }\n\n    /**\n     * Commits a transaction.\n     * @throws Exception if the transaction is not active\n     */\n    public function commit()\n    {\n        if (!$this->getIsActive()) {\n            throw new Exception('Failed to commit transaction: transaction was inactive.');\n        }\n\n        $this->_level--;\n        if ($this->_level === 0) {\n            Yii::debug('Commit transaction', __METHOD__);\n            // make sure the transaction wasn't autocommitted\n            if ($this->db->pdo->inTransaction()) {\n                $this->db->pdo->commit();\n            }\n            $this->db->trigger(Connection::EVENT_COMMIT_TRANSACTION);\n            return;\n        }\n\n        $schema = $this->db->getSchema();\n        if ($schema->supportsSavepoint()) {\n            Yii::debug('Release savepoint ' . $this->_level, __METHOD__);\n            // make sure the transaction wasn't autocommitted\n            if ($this->db->pdo->inTransaction()) {\n                $schema->releaseSavepoint('LEVEL' . $this->_level);\n            }\n        } else {\n            Yii::info('Transaction not committed: nested transaction not supported', __METHOD__);\n        }\n    }\n\n    /**\n     * Rolls back a transaction.\n     */\n    public function rollBack()\n    {\n        if (!$this->getIsActive()) {\n            // do nothing if transaction is not active: this could be the transaction is committed\n            // but the event handler to \"commitTransaction\" throw an exception\n            return;\n        }\n\n        $this->_level--;\n        if ($this->_level === 0) {\n            Yii::debug('Roll back transaction', __METHOD__);\n            // make sure the transaction wasn't autocommitted\n            if ($this->db->pdo->inTransaction()) {\n                $this->db->pdo->rollBack();\n            }\n            $this->db->trigger(Connection::EVENT_ROLLBACK_TRANSACTION);\n            return;\n        }\n\n        $schema = $this->db->getSchema();\n        if ($schema->supportsSavepoint()) {\n            Yii::debug('Roll back to savepoint ' . $this->_level, __METHOD__);\n            // make sure the transaction wasn't autocommitted\n            if ($this->db->pdo->inTransaction()) {\n                $schema->rollBackSavepoint('LEVEL' . $this->_level);\n            }\n        } else {\n            Yii::info('Transaction not rolled back: nested transaction not supported', __METHOD__);\n        }\n    }\n\n    /**\n     * Sets the transaction isolation level for this transaction.\n     *\n     * This method can be used to set the isolation level while the transaction is already active.\n     * However this is not supported by all DBMS so you might rather specify the isolation level directly\n     * when calling [[begin()]].\n     * @param string $level The transaction isolation level to use for this transaction.\n     * This can be one of [[READ_UNCOMMITTED]], [[READ_COMMITTED]], [[REPEATABLE_READ]] and [[SERIALIZABLE]] but\n     * also a string containing DBMS specific syntax to be used after `SET TRANSACTION ISOLATION LEVEL`.\n     * @throws Exception if the transaction is not active\n     * @see https://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels\n     */\n    public function setIsolationLevel($level)\n    {\n        if (!$this->getIsActive()) {\n            throw new Exception('Failed to set isolation level: transaction was inactive.');\n        }\n        Yii::debug('Setting transaction isolation level to ' . $level, __METHOD__);\n        $this->db->getSchema()->setTransactionIsolationLevel($level);\n    }\n\n    /**\n     * @return int The current nesting level of the transaction.\n     * @since 2.0.8\n     */\n    public function getLevel()\n    {\n        return $this->_level;\n    }\n}\n"
  },
  {
    "path": "framework/db/ViewFinderTrait.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db;\n\n/**\n * ViewFinderTrait implements the method getViewNames for finding views in a database.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Bob Olde Hampsink <b.oldehampsink@nerds.company>\n * @since 2.0.12\n */\ntrait ViewFinderTrait\n{\n    /**\n     * @var array list of ALL view names in the database\n     */\n    private $_viewNames = [];\n\n    /**\n     * Returns all views names in the database.\n     * @param string $schema the schema of the views. Defaults to empty string, meaning the current or default schema.\n     * @return array all views names in the database. The names have NO schema name prefix.\n     */\n    abstract protected function findViewNames($schema = '');\n\n    /**\n     * Returns all view names in the database.\n     * @param string $schema the schema of the views. Defaults to empty string, meaning the current or default schema name.\n     * If not empty, the returned view names will be prefixed with the schema name.\n     * @param bool $refresh whether to fetch the latest available view names. If this is false,\n     * view names fetched previously (if available) will be returned.\n     * @return string[] all view names in the database.\n     */\n    public function getViewNames($schema = '', $refresh = false)\n    {\n        if (!isset($this->_viewNames[$schema]) || $refresh) {\n            $this->_viewNames[$schema] = $this->findViewNames($schema);\n        }\n\n        return $this->_viewNames[$schema];\n    }\n}\n"
  },
  {
    "path": "framework/db/conditions/AndCondition.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\conditions;\n\n/**\n * Condition that connects two or more SQL expressions with the `AND` operator.\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n */\nclass AndCondition extends ConjunctionCondition\n{\n    /**\n     * Returns the operator that is represented by this condition class, e.g. `AND`, `OR`.\n     *\n     * @return string\n     */\n    public function getOperator()\n    {\n        return 'AND';\n    }\n}\n"
  },
  {
    "path": "framework/db/conditions/BetweenColumnsCondition.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\conditions;\n\nuse yii\\base\\InvalidArgumentException;\nuse yii\\db\\ExpressionInterface;\nuse yii\\db\\Query;\n\n/**\n * Class BetweenColumnCondition represents a `BETWEEN` condition where\n * values is between two columns. For example:\n *\n * ```\n * new BetweenColumnsCondition(42, 'BETWEEN', 'min_value', 'max_value')\n * // Will be build to:\n * // 42 BETWEEN min_value AND max_value\n * ```\n *\n * And a more complex example:\n *\n * ```\n * new BetweenColumnsCondition(\n *    new Expression('NOW()'),\n *    'NOT BETWEEN',\n *    (new Query)->select('time')->from('log')->orderBy('id ASC')->limit(1),\n *    'update_time'\n * );\n *\n * // Will be built to:\n * // NOW() NOT BETWEEN (SELECT time FROM log ORDER BY id ASC LIMIT 1) AND update_time\n * ```\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n * @phpcs:disable Squiz.NamingConventions.ValidVariableName.PrivateNoUnderscore\n */\nclass BetweenColumnsCondition implements ConditionInterface\n{\n    /**\n     * @var string $operator the operator to use (e.g. `BETWEEN` or `NOT BETWEEN`)\n     */\n    private $operator;\n    /**\n     * @var mixed the value to compare against\n     */\n    private $value;\n    /**\n     * @var string|ExpressionInterface|Query the column name or expression that is a beginning of the interval\n     */\n    private $intervalStartColumn;\n    /**\n     * @var string|ExpressionInterface|Query the column name or expression that is an end of the interval\n     */\n    private $intervalEndColumn;\n\n\n    /**\n     * Creates a condition with the `BETWEEN` operator.\n     *\n     * @param mixed $value the value to compare against\n     * @param string $operator the operator to use (e.g. `BETWEEN` or `NOT BETWEEN`)\n     * @param string|ExpressionInterface $intervalStartColumn the column name or expression that is a beginning of the interval\n     * @param string|ExpressionInterface $intervalEndColumn the column name or expression that is an end of the interval\n     */\n    public function __construct($value, $operator, $intervalStartColumn, $intervalEndColumn)\n    {\n        $this->value = $value;\n        $this->operator = $operator;\n        $this->intervalStartColumn = $intervalStartColumn;\n        $this->intervalEndColumn = $intervalEndColumn;\n    }\n\n    /**\n     * @return string\n     */\n    public function getOperator()\n    {\n        return $this->operator;\n    }\n\n    /**\n     * @return mixed\n     */\n    public function getValue()\n    {\n        return $this->value;\n    }\n\n    /**\n     * @return string|ExpressionInterface|Query\n     */\n    public function getIntervalStartColumn()\n    {\n        return $this->intervalStartColumn;\n    }\n\n    /**\n     * @return string|ExpressionInterface|Query\n     */\n    public function getIntervalEndColumn()\n    {\n        return $this->intervalEndColumn;\n    }\n\n    /**\n     * {@inheritdoc}\n     * @throws InvalidArgumentException if wrong number of operands have been given.\n     */\n    public static function fromArrayDefinition($operator, $operands)\n    {\n        if (!isset($operands[0], $operands[1], $operands[2])) {\n            throw new InvalidArgumentException(\"Operator '$operator' requires three operands.\");\n        }\n\n        return new static($operands[0], $operator, $operands[1], $operands[2]);\n    }\n}\n"
  },
  {
    "path": "framework/db/conditions/BetweenColumnsConditionBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\conditions;\n\nuse yii\\db\\ExpressionBuilderInterface;\nuse yii\\db\\ExpressionBuilderTrait;\nuse yii\\db\\ExpressionInterface;\nuse yii\\db\\Query;\n\n/**\n * Class BetweenColumnsConditionBuilder builds objects of [[BetweenColumnsCondition]]\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n */\nclass BetweenColumnsConditionBuilder implements ExpressionBuilderInterface\n{\n    use ExpressionBuilderTrait;\n\n\n    /**\n     * Method builds the raw SQL from the $expression that will not be additionally\n     * escaped or quoted.\n     *\n     * @param ExpressionInterface|BetweenColumnsCondition $expression the expression to be built.\n     * @param array $params the binding parameters.\n     * @return string the raw SQL that will not be additionally escaped or quoted.\n     */\n    public function build(ExpressionInterface $expression, array &$params = [])\n    {\n        $operator = $expression->getOperator();\n\n        $startColumn = $this->escapeColumnName($expression->getIntervalStartColumn(), $params);\n        $endColumn = $this->escapeColumnName($expression->getIntervalEndColumn(), $params);\n        $value = $this->createPlaceholder($expression->getValue(), $params);\n\n        return \"$value $operator $startColumn AND $endColumn\";\n    }\n\n    /**\n     * Prepares column name to be used in SQL statement.\n     *\n     * @param Query|ExpressionInterface|string $columnName\n     * @param array $params the binding parameters.\n     * @return string\n     */\n    protected function escapeColumnName($columnName, &$params = [])\n    {\n        if ($columnName instanceof Query) {\n            list($sql, $params) = $this->queryBuilder->build($columnName, $params);\n            return \"($sql)\";\n        } elseif ($columnName instanceof ExpressionInterface) {\n            return $this->queryBuilder->buildExpression($columnName, $params);\n        } elseif (strpos($columnName, '(') === false) {\n            return $this->queryBuilder->db->quoteColumnName($columnName);\n        }\n\n        return $columnName;\n    }\n\n    /**\n     * Attaches $value to $params array and returns placeholder.\n     *\n     * @param mixed $value\n     * @param array $params passed by reference\n     * @return string\n     */\n    protected function createPlaceholder($value, &$params)\n    {\n        if ($value instanceof ExpressionInterface) {\n            return $this->queryBuilder->buildExpression($value, $params);\n        }\n\n        return $this->queryBuilder->bindParam($value, $params);\n    }\n}\n"
  },
  {
    "path": "framework/db/conditions/BetweenCondition.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\conditions;\n\nuse yii\\base\\InvalidArgumentException;\n\n/**\n * Class BetweenCondition represents a `BETWEEN` condition.\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n * @phpcs:disable Squiz.NamingConventions.ValidVariableName.PrivateNoUnderscore\n */\nclass BetweenCondition implements ConditionInterface\n{\n    /**\n     * @var string $operator the operator to use (e.g. `BETWEEN` or `NOT BETWEEN`)\n     */\n    private $operator;\n    /**\n     * @var mixed the column name to the left of [[operator]]\n     */\n    private $column;\n    /**\n     * @var mixed beginning of the interval\n     */\n    private $intervalStart;\n    /**\n     * @var mixed end of the interval\n     */\n    private $intervalEnd;\n\n\n    /**\n     * Creates a condition with the `BETWEEN` operator.\n     *\n     * @param mixed $column the literal to the left of $operator\n     * @param string $operator the operator to use (e.g. `BETWEEN` or `NOT BETWEEN`)\n     * @param mixed $intervalStart beginning of the interval\n     * @param mixed $intervalEnd end of the interval\n     */\n    public function __construct($column, $operator, $intervalStart, $intervalEnd)\n    {\n        $this->column = $column;\n        $this->operator = $operator;\n        $this->intervalStart = $intervalStart;\n        $this->intervalEnd = $intervalEnd;\n    }\n\n    /**\n     * @return string\n     */\n    public function getOperator()\n    {\n        return $this->operator;\n    }\n\n    /**\n     * @return mixed\n     */\n    public function getColumn()\n    {\n        return $this->column;\n    }\n\n    /**\n     * @return mixed\n     */\n    public function getIntervalStart()\n    {\n        return $this->intervalStart;\n    }\n\n    /**\n     * @return mixed\n     */\n    public function getIntervalEnd()\n    {\n        return $this->intervalEnd;\n    }\n\n    /**\n     * {@inheritdoc}\n     * @throws InvalidArgumentException if wrong number of operands have been given.\n     */\n    public static function fromArrayDefinition($operator, $operands)\n    {\n        if (!isset($operands[0], $operands[1], $operands[2])) {\n            throw new InvalidArgumentException(\"Operator '$operator' requires three operands.\");\n        }\n\n        return new static($operands[0], $operator, $operands[1], $operands[2]);\n    }\n}\n"
  },
  {
    "path": "framework/db/conditions/BetweenConditionBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\conditions;\n\nuse yii\\db\\ExpressionBuilderInterface;\nuse yii\\db\\ExpressionBuilderTrait;\nuse yii\\db\\ExpressionInterface;\n\n/**\n * Class BetweenConditionBuilder builds objects of [[BetweenCondition]]\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n */\nclass BetweenConditionBuilder implements ExpressionBuilderInterface\n{\n    use ExpressionBuilderTrait;\n\n\n    /**\n     * Method builds the raw SQL from the $expression that will not be additionally\n     * escaped or quoted.\n     *\n     * @param ExpressionInterface|BetweenCondition $expression the expression to be built.\n     * @param array $params the binding parameters.\n     * @return string the raw SQL that will not be additionally escaped or quoted.\n     */\n    public function build(ExpressionInterface $expression, array &$params = [])\n    {\n        $operator = $expression->getOperator();\n        $column = $expression->getColumn();\n\n        if (strpos($column, '(') === false) {\n            $column = $this->queryBuilder->db->quoteColumnName($column);\n        }\n\n        $phName1 = $this->createPlaceholder($expression->getIntervalStart(), $params);\n        $phName2 = $this->createPlaceholder($expression->getIntervalEnd(), $params);\n\n        return \"$column $operator $phName1 AND $phName2\";\n    }\n\n    /**\n     * Attaches $value to $params array and returns placeholder.\n     *\n     * @param mixed $value\n     * @param array $params passed by reference\n     * @return string\n     */\n    protected function createPlaceholder($value, &$params)\n    {\n        if ($value instanceof ExpressionInterface) {\n            return $this->queryBuilder->buildExpression($value, $params);\n        }\n\n        return $this->queryBuilder->bindParam($value, $params);\n    }\n}\n"
  },
  {
    "path": "framework/db/conditions/ConditionInterface.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\conditions;\n\nuse yii\\base\\InvalidParamException;\nuse yii\\db\\ExpressionInterface;\n\n/**\n * Interface ConditionInterface should be implemented by classes that represent a condition\n * in DBAL of framework.\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n */\ninterface ConditionInterface extends ExpressionInterface\n{\n    /**\n     * Creates object by array-definition as described in\n     * [Query Builder – Operator format](guide:db-query-builder#operator-format) guide article.\n     *\n     * @param string $operator operator in uppercase.\n     * @param array $operands array of corresponding operands\n     *\n     * @return static\n     * @throws InvalidParamException if input parameters are not suitable for this condition\n     */\n    public static function fromArrayDefinition($operator, $operands);\n}\n"
  },
  {
    "path": "framework/db/conditions/ConjunctionCondition.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\conditions;\n\n/**\n * Class ConjunctionCondition\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n */\nabstract class ConjunctionCondition implements ConditionInterface\n{\n    /**\n     * @var mixed[]\n     */\n    protected $expressions;\n\n\n    /**\n     * @param mixed $expressions\n     */\n    public function __construct($expressions) // TODO: use variadic params when PHP>5.6\n    {\n        $this->expressions = $expressions;\n    }\n\n    /**\n     * @return mixed[]\n     */\n    public function getExpressions()\n    {\n        return $this->expressions;\n    }\n\n    /**\n     * Returns the operator that is represented by this condition class, e.g. `AND`, `OR`.\n     * @return string\n     */\n    abstract public function getOperator();\n\n    /**\n     * {@inheritdoc}\n     */\n    public static function fromArrayDefinition($operator, $operands)\n    {\n        return new static($operands);\n    }\n}\n"
  },
  {
    "path": "framework/db/conditions/ConjunctionConditionBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\conditions;\n\nuse yii\\db\\ExpressionBuilderInterface;\nuse yii\\db\\ExpressionBuilderTrait;\nuse yii\\db\\ExpressionInterface;\n\n/**\n * Class ConjunctionConditionBuilder builds objects of abstract class [[ConjunctionCondition]]\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n */\nclass ConjunctionConditionBuilder implements ExpressionBuilderInterface\n{\n    use ExpressionBuilderTrait;\n\n\n    /**\n     * Method builds the raw SQL from the $expression that will not be additionally\n     * escaped or quoted.\n     *\n     * @param ExpressionInterface|ConjunctionCondition $condition the expression to be built.\n     * @param array $params the binding parameters.\n     * @return string the raw SQL that will not be additionally escaped or quoted.\n     */\n    public function build(ExpressionInterface $condition, array &$params = [])\n    {\n        $parts = $this->buildExpressionsFrom($condition, $params);\n\n        if (empty($parts)) {\n            return '';\n        }\n\n        if (count($parts) === 1) {\n            return reset($parts);\n        }\n\n        return '(' . implode(\") {$condition->getOperator()} (\", $parts) . ')';\n    }\n\n    /**\n     * Builds expressions, that are stored in $condition\n     *\n     * @param ExpressionInterface|ConjunctionCondition $condition the expression to be built.\n     * @param array $params the binding parameters.\n     * @return string[]\n     */\n    private function buildExpressionsFrom(ExpressionInterface $condition, &$params = [])\n    {\n        $parts = [];\n        foreach ($condition->getExpressions() as $condition) {\n            if (is_array($condition)) {\n                $condition = $this->queryBuilder->buildCondition($condition, $params);\n            }\n            if ($condition instanceof ExpressionInterface) {\n                $condition = $this->queryBuilder->buildExpression($condition, $params);\n            }\n            if ($condition !== '') {\n                $parts[] = $condition;\n            }\n        }\n\n        return $parts;\n    }\n}\n"
  },
  {
    "path": "framework/db/conditions/ExistsCondition.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\conditions;\n\nuse yii\\base\\InvalidArgumentException;\nuse yii\\db\\Query;\n\n/**\n * Condition that represents `EXISTS` operator.\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n * @phpcs:disable Squiz.NamingConventions.ValidVariableName.PrivateNoUnderscore\n */\nclass ExistsCondition implements ConditionInterface\n{\n    /**\n     * @var string $operator the operator to use (e.g. `EXISTS` or `NOT EXISTS`)\n     */\n    private $operator;\n    /**\n     * @var Query the [[Query]] object representing the sub-query.\n     */\n    private $query;\n\n\n    /**\n     * ExistsCondition constructor.\n     *\n     * @param string $operator the operator to use (e.g. `EXISTS` or `NOT EXISTS`)\n     * @param Query $query the [[Query]] object representing the sub-query.\n     */\n    public function __construct($operator, $query)\n    {\n        $this->operator = $operator;\n        $this->query = $query;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public static function fromArrayDefinition($operator, $operands)\n    {\n        if (!isset($operands[0]) || !$operands[0] instanceof Query) {\n            throw new InvalidArgumentException('Subquery for EXISTS operator must be a Query object.');\n        }\n\n        return new static($operator, $operands[0]);\n    }\n\n    /**\n     * @return string\n     */\n    public function getOperator()\n    {\n        return $this->operator;\n    }\n\n    /**\n     * @return Query\n     */\n    public function getQuery()\n    {\n        return $this->query;\n    }\n}\n"
  },
  {
    "path": "framework/db/conditions/ExistsConditionBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\conditions;\n\nuse yii\\db\\ExpressionBuilderInterface;\nuse yii\\db\\ExpressionBuilderTrait;\nuse yii\\db\\ExpressionInterface;\n\n/**\n * Class ExistsConditionBuilder builds objects of [[ExistsCondition]]\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n */\nclass ExistsConditionBuilder implements ExpressionBuilderInterface\n{\n    use ExpressionBuilderTrait;\n\n\n    /**\n     * Method builds the raw SQL from the $expression that will not be additionally\n     * escaped or quoted.\n     *\n     * @param ExpressionInterface|ExistsCondition $expression the expression to be built.\n     * @param array $params the binding parameters.\n     * @return string the raw SQL that will not be additionally escaped or quoted.\n     */\n    public function build(ExpressionInterface $expression, array &$params = [])\n    {\n        $operator = $expression->getOperator();\n        $query = $expression->getQuery();\n\n        $sql = $this->queryBuilder->buildExpression($query, $params);\n\n        return \"$operator $sql\";\n    }\n}\n"
  },
  {
    "path": "framework/db/conditions/HashCondition.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\conditions;\n\n/**\n * Condition based on column-value pairs.\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n * @phpcs:disable Squiz.NamingConventions.ValidVariableName.PrivateNoUnderscore\n */\nclass HashCondition implements ConditionInterface\n{\n    /**\n     * @var array|null the condition specification.\n     */\n    private $hash;\n\n\n    /**\n     * HashCondition constructor.\n     *\n     * @param array|null $hash\n     */\n    public function __construct($hash)\n    {\n        $this->hash = $hash;\n    }\n\n    /**\n     * @return array|null\n     */\n    public function getHash()\n    {\n        return $this->hash;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public static function fromArrayDefinition($operator, $operands)\n    {\n        return new static($operands);\n    }\n}\n"
  },
  {
    "path": "framework/db/conditions/HashConditionBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\conditions;\n\nuse yii\\db\\ExpressionBuilderInterface;\nuse yii\\db\\ExpressionBuilderTrait;\nuse yii\\db\\ExpressionInterface;\nuse yii\\db\\Query;\nuse yii\\helpers\\ArrayHelper;\n\n/**\n * Class HashConditionBuilder builds objects of [[HashCondition]]\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n */\nclass HashConditionBuilder implements ExpressionBuilderInterface\n{\n    use ExpressionBuilderTrait;\n\n\n    /**\n     * Method builds the raw SQL from the $expression that will not be additionally\n     * escaped or quoted.\n     *\n     * @param ExpressionInterface|HashCondition $expression the expression to be built.\n     * @param array $params the binding parameters.\n     * @return string the raw SQL that will not be additionally escaped or quoted.\n     */\n    public function build(ExpressionInterface $expression, array &$params = [])\n    {\n        $hash = $expression->getHash();\n        $parts = [];\n        foreach ($hash as $column => $value) {\n            if (ArrayHelper::isTraversable($value) || $value instanceof Query) {\n                // IN condition\n                $parts[] = $this->queryBuilder->buildCondition(new InCondition($column, 'IN', $value), $params);\n            } else {\n                if (strpos($column, '(') === false) {\n                    $column = $this->queryBuilder->db->quoteColumnName($column);\n                }\n                if ($value === null) {\n                    $parts[] = \"$column IS NULL\";\n                } elseif ($value instanceof ExpressionInterface) {\n                    $parts[] = \"$column=\" . $this->queryBuilder->buildExpression($value, $params);\n                } else {\n                    $phName = $this->queryBuilder->bindParam($value, $params);\n                    $parts[] = \"$column=$phName\";\n                }\n            }\n        }\n\n        return count($parts) === 1 ? $parts[0] : '(' . implode(') AND (', $parts) . ')';\n    }\n}\n"
  },
  {
    "path": "framework/db/conditions/InCondition.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\conditions;\n\nuse yii\\base\\InvalidArgumentException;\nuse yii\\db\\ExpressionInterface;\n\n/**\n * Class InCondition represents `IN` condition.\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n * @phpcs:disable Squiz.NamingConventions.ValidVariableName.PrivateNoUnderscore\n */\nclass InCondition implements ConditionInterface\n{\n    /**\n     * @var string $operator the operator to use (e.g. `IN` or `NOT IN`)\n     */\n    private $operator;\n    /**\n     * @var string|string[] the column name. If it is an array, a composite `IN` condition\n     * will be generated.\n     */\n    private $column;\n    /**\n     * @var ExpressionInterface[]|string[]|int[] an array of values that [[column]] value should be among.\n     * If it is an empty array the generated expression will be a `false` value if\n     * [[operator]] is `IN` and empty if operator is `NOT IN`.\n     */\n    private $values;\n\n\n    /**\n     * SimpleCondition constructor\n     *\n     * @param string|string[] $column the column name. If it is an array, a composite `IN` condition\n     * will be generated.\n     * @param string $operator the operator to use (e.g. `IN` or `NOT IN`)\n     * @param array $values an array of values that [[column]] value should be among. If it is an empty array the generated\n     * expression will be a `false` value if [[operator]] is `IN` and empty if operator is `NOT IN`.\n     */\n    public function __construct($column, $operator, $values)\n    {\n        $this->column = $column;\n        $this->operator = $operator;\n        $this->values = $values;\n    }\n\n    /**\n     * @return string\n     */\n    public function getOperator()\n    {\n        return $this->operator;\n    }\n\n    /**\n     * @return mixed\n     */\n    public function getColumn()\n    {\n        return $this->column;\n    }\n\n    /**\n     * @return ExpressionInterface[]|string[]|int[]\n     */\n    public function getValues()\n    {\n        return $this->values;\n    }\n    /**\n     * {@inheritdoc}\n     * @throws InvalidArgumentException if wrong number of operands have been given.\n     */\n    public static function fromArrayDefinition($operator, $operands)\n    {\n        if (!isset($operands[0], $operands[1])) {\n            throw new InvalidArgumentException(\"Operator '$operator' requires two operands.\");\n        }\n\n        return new static($operands[0], $operator, $operands[1]);\n    }\n}\n"
  },
  {
    "path": "framework/db/conditions/InConditionBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\conditions;\n\nuse yii\\db\\Expression;\nuse yii\\db\\ExpressionBuilderInterface;\nuse yii\\db\\ExpressionBuilderTrait;\nuse yii\\db\\ExpressionInterface;\nuse yii\\db\\Query;\n\n/**\n * Class InConditionBuilder builds objects of [[InCondition]]\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n */\nclass InConditionBuilder implements ExpressionBuilderInterface\n{\n    use ExpressionBuilderTrait;\n\n\n    /**\n     * Method builds the raw SQL from the $expression that will not be additionally\n     * escaped or quoted.\n     *\n     * @param ExpressionInterface|InCondition $expression the expression to be built.\n     * @param array $params the binding parameters.\n     * @return string the raw SQL that will not be additionally escaped or quoted.\n     */\n    public function build(ExpressionInterface $expression, array &$params = [])\n    {\n        $operator = strtoupper($expression->getOperator());\n        $column = $expression->getColumn();\n        $values = $expression->getValues();\n\n        if ($column === []) {\n            // no columns to test against\n            return $operator === 'IN' ? '0=1' : '';\n        }\n\n        if ($values instanceof Query) {\n            return $this->buildSubqueryInCondition($operator, $column, $values, $params);\n        }\n\n        if (!is_array($values) && !$values instanceof \\Traversable) {\n            // ensure values is an array\n            $values = (array) $values;\n        }\n\n        if (is_array($column)) {\n            if (count($column) > 1) {\n                return $this->buildCompositeInCondition($operator, $column, $values, $params);\n            }\n            $column = reset($column);\n        }\n\n        if ($column instanceof \\Traversable) {\n            if (iterator_count($column) > 1) {\n                return $this->buildCompositeInCondition($operator, $column, $values, $params);\n            }\n            $column->rewind();\n            $column = $column->current();\n        }\n\n        if ($column instanceof Expression) {\n            $column = $column->expression;\n        }\n\n        if (is_array($values)) {\n            $rawValues = $values;\n        } elseif ($values instanceof \\Traversable) {\n            $rawValues = $this->getRawValuesFromTraversableObject($values);\n        }\n\n        $nullCondition = null;\n        $nullConditionOperator = null;\n        if (isset($rawValues) && in_array(null, $rawValues, true)) {\n            $nullCondition = $this->getNullCondition($operator, $column);\n            $nullConditionOperator = $operator === 'IN' ? 'OR' : 'AND';\n        }\n\n        $sqlValues = $this->buildValues($expression, $values, $params);\n        if (empty($sqlValues)) {\n            if ($nullCondition === null) {\n                return $operator === 'IN' ? '0=1' : '';\n            }\n            return $nullCondition;\n        }\n\n        if (strpos($column, '(') === false) {\n            $column = $this->queryBuilder->db->quoteColumnName($column);\n        }\n        if (count($sqlValues) > 1) {\n            $sql = \"$column $operator (\" . implode(', ', $sqlValues) . ')';\n        } else {\n            $operator = $operator === 'IN' ? '=' : '<>';\n            $sql = $column . $operator . reset($sqlValues);\n        }\n\n        return $nullCondition !== null && $nullConditionOperator !== null\n            ? sprintf('%s %s %s', $sql, $nullConditionOperator, $nullCondition)\n            : $sql;\n    }\n\n    /**\n     * Builds $values to be used in [[InCondition]]\n     *\n     * @param ConditionInterface|InCondition $condition\n     * @param array $values\n     * @param array $params the binding parameters\n     * @return array of prepared for SQL placeholders\n     */\n    protected function buildValues(ConditionInterface $condition, $values, &$params)\n    {\n        $sqlValues = [];\n        $column = $condition->getColumn();\n\n        if (is_array($column)) {\n            $column = reset($column);\n        }\n\n        if ($column instanceof \\Traversable) {\n            $column->rewind();\n            $column = $column->current();\n        }\n\n        if ($column instanceof Expression) {\n            $column = $column->expression;\n        }\n\n        foreach ($values as $i => $value) {\n            if (is_array($value) || $value instanceof \\ArrayAccess) {\n                $value = isset($value[$column]) ? $value[$column] : null;\n            }\n            if ($value === null) {\n                continue;\n            } elseif ($value instanceof ExpressionInterface) {\n                $sqlValues[$i] = $this->queryBuilder->buildExpression($value, $params);\n            } else {\n                $sqlValues[$i] = $this->queryBuilder->bindParam($value, $params);\n            }\n        }\n\n        return $sqlValues;\n    }\n\n    /**\n     * Builds SQL for IN condition.\n     *\n     * @param string $operator\n     * @param array|string $columns\n     * @param Query $values\n     * @param array $params\n     * @return string SQL\n     */\n    protected function buildSubqueryInCondition($operator, $columns, $values, &$params)\n    {\n        $sql = $this->queryBuilder->buildExpression($values, $params);\n\n        if (is_array($columns)) {\n            foreach ($columns as $i => $col) {\n                if ($col instanceof Expression) {\n                    $col = $col->expression;\n                }\n                if (strpos($col, '(') === false) {\n                    $columns[$i] = $this->queryBuilder->db->quoteColumnName($col);\n                }\n            }\n\n            return '(' . implode(', ', $columns) . \") $operator $sql\";\n        }\n\n        if ($columns instanceof Expression) {\n            $columns = $columns->expression;\n        }\n        if (strpos($columns, '(') === false) {\n            $columns = $this->queryBuilder->db->quoteColumnName($columns);\n        }\n\n        return \"$columns $operator $sql\";\n    }\n\n    /**\n     * Builds SQL for IN condition.\n     *\n     * @param string $operator\n     * @param array|\\Traversable $columns\n     * @param array $values\n     * @param array $params\n     * @return string SQL\n     */\n    protected function buildCompositeInCondition($operator, $columns, $values, &$params)\n    {\n        $vss = [];\n        foreach ($values as $value) {\n            $vs = [];\n            foreach ($columns as $column) {\n                if ($column instanceof Expression) {\n                    $column = $column->expression;\n                }\n                if (isset($value[$column])) {\n                    $vs[] = $this->queryBuilder->bindParam($value[$column], $params);\n                } else {\n                    $vs[] = 'NULL';\n                }\n            }\n            $vss[] = '(' . implode(', ', $vs) . ')';\n        }\n\n        if (empty($vss)) {\n            return $operator === 'IN' ? '0=1' : '';\n        }\n\n        $sqlColumns = [];\n        foreach ($columns as $i => $column) {\n            if ($column instanceof Expression) {\n                $column = $column->expression;\n            }\n            $sqlColumns[] = strpos($column, '(') === false ? $this->queryBuilder->db->quoteColumnName($column) : $column;\n        }\n\n        return '(' . implode(', ', $sqlColumns) . \") $operator (\" . implode(', ', $vss) . ')';\n    }\n\n    /**\n     * Builds is null/is not null condition for column based on operator\n     *\n     * @param string $operator\n     * @param string $column\n     * @return string is null or is not null condition\n     * @since 2.0.31\n     */\n    protected function getNullCondition($operator, $column)\n    {\n        $column = $this->queryBuilder->db->quoteColumnName($column);\n        if ($operator === 'IN') {\n            return sprintf('%s IS NULL', $column);\n        }\n        return sprintf('%s IS NOT NULL', $column);\n    }\n\n    /**\n     * @param \\Traversable $traversableObject\n     * @return array raw values\n     * @since 2.0.31\n     */\n    protected function getRawValuesFromTraversableObject(\\Traversable $traversableObject)\n    {\n        $rawValues = [];\n        foreach ($traversableObject as $value) {\n            if (is_array($value)) {\n                $values = array_values($value);\n                $rawValues = array_merge($rawValues, $values);\n            } else {\n                $rawValues[] = $value;\n            }\n        }\n        return $rawValues;\n    }\n}\n"
  },
  {
    "path": "framework/db/conditions/LikeCondition.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\conditions;\n\nuse yii\\base\\InvalidArgumentException;\n\n/**\n * Class LikeCondition represents a `LIKE` condition.\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n */\nclass LikeCondition extends SimpleCondition\n{\n    /**\n     * @var array|null|false map of chars to their replacements, `false` if characters should not be escaped\n     * or either `null` or empty array if escaping is condition builder responsibility.\n     * By default it's set to `null`.\n     */\n    protected $escapingReplacements;\n\n\n    /**\n     * @param string $column the column name.\n     * @param string $operator the operator to use (e.g. `LIKE`, `NOT LIKE`, `OR LIKE` or `OR NOT LIKE`)\n     * @param string[]|string $value single value or an array of values that $column should be compared with.\n     * If it is an empty array the generated expression will  be a `false` value if operator is `LIKE` or `OR LIKE`\n     * and empty if operator is `NOT LIKE` or `OR NOT LIKE`.\n     */\n    public function __construct($column, $operator, $value)\n    {\n        parent::__construct($column, $operator, $value);\n    }\n\n    /**\n     * This method allows to specify how to escape special characters in the value(s).\n     *\n     * @param array|null|false $escapingReplacements an array of mappings from the special characters to their escaped counterparts.\n     * You may use `false` to indicate the values are already escaped and no escape should be applied,\n     * or either `null` or empty array if escaping is condition builder responsibility.\n     * Note that when using an escape mapping (or the third operand is not provided),\n     * the values will be automatically enclosed within a pair of percentage characters.\n     */\n    public function setEscapingReplacements($escapingReplacements)\n    {\n        $this->escapingReplacements = $escapingReplacements;\n    }\n\n    /**\n     * @return array|null|false\n     */\n    public function getEscapingReplacements()\n    {\n        return $this->escapingReplacements;\n    }\n\n    /**\n     * {@inheritdoc}\n     * @throws InvalidArgumentException if wrong number of operands have been given.\n     */\n    public static function fromArrayDefinition($operator, $operands)\n    {\n        if (!isset($operands[0], $operands[1])) {\n            throw new InvalidArgumentException(\"Operator '$operator' requires two operands.\");\n        }\n\n        $condition = new static($operands[0], $operator, $operands[1]);\n        if (isset($operands[2])) {\n            $condition->escapingReplacements = $operands[2];\n        }\n\n        return $condition;\n    }\n}\n"
  },
  {
    "path": "framework/db/conditions/LikeConditionBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\conditions;\n\nuse yii\\base\\InvalidArgumentException;\nuse yii\\db\\ExpressionBuilderInterface;\nuse yii\\db\\ExpressionBuilderTrait;\nuse yii\\db\\ExpressionInterface;\n\n/**\n * Class LikeConditionBuilder builds objects of [[LikeCondition]]\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n */\nclass LikeConditionBuilder implements ExpressionBuilderInterface\n{\n    use ExpressionBuilderTrait;\n\n    /**\n     * @var array map of chars to their replacements in LIKE conditions.\n     * By default it's configured to escape `%`, `_` and `\\` with `\\`.\n     */\n    protected $escapingReplacements = [\n        '%' => '\\%',\n        '_' => '\\_',\n        '\\\\' => '\\\\\\\\',\n    ];\n    /**\n     * @var string|null character used to escape special characters in LIKE conditions.\n     * By default it's assumed to be `\\`.\n     */\n    protected $escapeCharacter;\n\n\n    /**\n     * Method builds the raw SQL from the $expression that will not be additionally\n     * escaped or quoted.\n     *\n     * @param ExpressionInterface|LikeCondition $expression the expression to be built.\n     * @param array $params the binding parameters.\n     * @return string the raw SQL that will not be additionally escaped or quoted.\n     */\n    public function build(ExpressionInterface $expression, array &$params = [])\n    {\n        $operator = strtoupper($expression->getOperator());\n        $column = $expression->getColumn();\n        $values = $expression->getValue();\n        $escape = $expression->getEscapingReplacements();\n        if ($escape === null || $escape === []) {\n            $escape = $this->escapingReplacements;\n        }\n\n        list($andor, $not, $operator) = $this->parseOperator($operator);\n\n        if (!is_array($values)) {\n            $values = [$values];\n        }\n\n        if (empty($values)) {\n            return $not ? '' : '0=1';\n        }\n\n        if ($column instanceof ExpressionInterface) {\n            $column = $this->queryBuilder->buildExpression($column, $params);\n        } elseif (is_string($column) && strpos($column, '(') === false) {\n            $column = $this->queryBuilder->db->quoteColumnName($column);\n        }\n\n        $escapeSql = $this->getEscapeSql();\n        $parts = [];\n        foreach ($values as $value) {\n            if ($value instanceof ExpressionInterface) {\n                $phName = $this->queryBuilder->buildExpression($value, $params);\n            } else {\n                $phName = $this->queryBuilder->bindParam(empty($escape) ? $value : ('%' . strtr((string)$value, $escape) . '%'), $params);\n            }\n            $parts[] = \"{$column} {$operator} {$phName}{$escapeSql}\";\n        }\n\n        return implode($andor, $parts);\n    }\n\n    /**\n     * @return string\n     */\n    private function getEscapeSql()\n    {\n        if ($this->escapeCharacter !== null) {\n            return \" ESCAPE '{$this->escapeCharacter}'\";\n        }\n\n        return '';\n    }\n\n    /**\n     * @param string $operator\n     * @return array\n     */\n    protected function parseOperator($operator)\n    {\n        if (!preg_match('/^(AND |OR |)(((NOT |))I?LIKE)/', $operator, $matches)) {\n            throw new InvalidArgumentException(\"Invalid operator '$operator'.\");\n        }\n        $andor = ' ' . (!empty($matches[1]) ? $matches[1] : 'AND ');\n        $not = !empty($matches[3]);\n        $operator = $matches[2];\n\n        return [$andor, $not, $operator];\n    }\n}\n"
  },
  {
    "path": "framework/db/conditions/NotCondition.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\conditions;\n\nuse yii\\base\\InvalidArgumentException;\n\n/**\n * Condition that inverts passed [[condition]].\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n * @phpcs:disable Squiz.NamingConventions.ValidVariableName.PrivateNoUnderscore\n */\nclass NotCondition implements ConditionInterface\n{\n    /**\n     * @var mixed the condition to be negated\n     */\n    private $condition;\n\n\n    /**\n     * NotCondition constructor.\n     *\n     * @param mixed $condition the condition to be negated\n     */\n    public function __construct($condition)\n    {\n        $this->condition = $condition;\n    }\n\n    /**\n     * @return mixed\n     */\n    public function getCondition()\n    {\n        return $this->condition;\n    }\n\n    /**\n     * {@inheritdoc}\n     * @throws InvalidArgumentException if wrong number of operands have been given.\n     */\n    public static function fromArrayDefinition($operator, $operands)\n    {\n        if (count($operands) !== 1) {\n            throw new InvalidArgumentException(\"Operator '$operator' requires exactly one operand.\");\n        }\n\n        return new static(array_shift($operands));\n    }\n}\n"
  },
  {
    "path": "framework/db/conditions/NotConditionBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\conditions;\n\nuse yii\\db\\ExpressionBuilderInterface;\nuse yii\\db\\ExpressionBuilderTrait;\nuse yii\\db\\ExpressionInterface;\n\n/**\n * Class NotConditionBuilder builds objects of [[NotCondition]]\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n */\nclass NotConditionBuilder implements ExpressionBuilderInterface\n{\n    use ExpressionBuilderTrait;\n\n\n    /**\n     * Method builds the raw SQL from the $expression that will not be additionally\n     * escaped or quoted.\n     *\n     * @param ExpressionInterface|NotCondition $expression the expression to be built.\n     * @param array $params the binding parameters.\n     * @return string the raw SQL that will not be additionally escaped or quoted.\n     */\n    public function build(ExpressionInterface $expression, array &$params = [])\n    {\n        $operand = $expression->getCondition();\n        if ($operand === '') {\n            return '';\n        }\n\n        $expession = $this->queryBuilder->buildCondition($operand, $params);\n        return \"{$this->getNegationOperator()} ($expession)\";\n    }\n\n    /**\n     * @return string\n     */\n    protected function getNegationOperator()\n    {\n        return 'NOT';\n    }\n}\n"
  },
  {
    "path": "framework/db/conditions/OrCondition.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\conditions;\n\n/**\n * Condition that connects two or more SQL expressions with the `AND` operator.\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n */\nclass OrCondition extends ConjunctionCondition\n{\n    /**\n     * Returns the operator that is represented by this condition class, e.g. `AND`, `OR`.\n     *\n     * @return string\n     */\n    public function getOperator()\n    {\n        return 'OR';\n    }\n}\n"
  },
  {
    "path": "framework/db/conditions/SimpleCondition.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\conditions;\n\nuse yii\\base\\InvalidArgumentException;\n\n/**\n * Class SimpleCondition represents a simple condition like `\"column\" operator value`.\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n * @phpcs:disable Squiz.NamingConventions.ValidVariableName.PrivateNoUnderscore\n */\nclass SimpleCondition implements ConditionInterface\n{\n    /**\n     * @var string $operator the operator to use. Anything could be used e.g. `>`, `<=`, etc.\n     */\n    private $operator;\n    /**\n     * @var mixed the column name to the left of [[operator]]\n     */\n    private $column;\n    /**\n     * @var mixed the value to the right of the [[operator]]\n     */\n    private $value;\n\n\n    /**\n     * SimpleCondition constructor\n     *\n     * @param mixed $column the literal to the left of $operator\n     * @param string $operator the operator to use. Anything could be used e.g. `>`, `<=`, etc.\n     * @param mixed $value the literal to the right of $operator\n     */\n    public function __construct($column, $operator, $value)\n    {\n        $this->column = $column;\n        $this->operator = $operator;\n        $this->value = $value;\n    }\n\n    /**\n     * @return string\n     */\n    public function getOperator()\n    {\n        return $this->operator;\n    }\n\n    /**\n     * @return mixed\n     */\n    public function getColumn()\n    {\n        return $this->column;\n    }\n\n    /**\n     * @return mixed\n     */\n    public function getValue()\n    {\n        return $this->value;\n    }\n\n    /**\n     * {@inheritdoc}\n     * @throws InvalidArgumentException if wrong number of operands have been given.\n     */\n    public static function fromArrayDefinition($operator, $operands)\n    {\n        if (count($operands) !== 2) {\n            throw new InvalidArgumentException(\"Operator '$operator' requires two operands.\");\n        }\n\n        return new static($operands[0], $operator, $operands[1]);\n    }\n}\n"
  },
  {
    "path": "framework/db/conditions/SimpleConditionBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\conditions;\n\nuse yii\\db\\ExpressionBuilderInterface;\nuse yii\\db\\ExpressionBuilderTrait;\nuse yii\\db\\ExpressionInterface;\n\n/**\n * Class NotConditionBuilder builds objects of [[SimpleCondition]]\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n */\nclass SimpleConditionBuilder implements ExpressionBuilderInterface\n{\n    use ExpressionBuilderTrait;\n\n\n    /**\n     * Method builds the raw SQL from the $expression that will not be additionally\n     * escaped or quoted.\n     *\n     * @param ExpressionInterface|SimpleCondition $expression the expression to be built.\n     * @param array $params the binding parameters.\n     * @return string the raw SQL that will not be additionally escaped or quoted.\n     */\n    public function build(ExpressionInterface $expression, array &$params = [])\n    {\n        $operator = $expression->getOperator();\n        $column = $expression->getColumn();\n        $value = $expression->getValue();\n\n        if ($column instanceof ExpressionInterface) {\n            $column = $this->queryBuilder->buildExpression($column, $params);\n        } elseif (is_string($column) && strpos($column, '(') === false) {\n            $column = $this->queryBuilder->db->quoteColumnName($column);\n        }\n\n        if ($value === null) {\n            return \"$column $operator NULL\";\n        }\n        if ($value instanceof ExpressionInterface) {\n            return \"$column $operator {$this->queryBuilder->buildExpression($value, $params)}\";\n        }\n\n        $phName = $this->queryBuilder->bindParam($value, $params);\n        return \"$column $operator $phName\";\n    }\n}\n"
  },
  {
    "path": "framework/db/cubrid/ColumnSchemaBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\cubrid;\n\nuse yii\\db\\ColumnSchemaBuilder as AbstractColumnSchemaBuilder;\n\n/**\n * ColumnSchemaBuilder is the schema builder for Cubrid databases.\n *\n * @author Chris Harris <chris@buckshotsoftware.com>\n * @since 2.0.8\n */\nclass ColumnSchemaBuilder extends AbstractColumnSchemaBuilder\n{\n    /**\n     * {@inheritdoc}\n     */\n    protected function buildUnsignedString()\n    {\n        return $this->isUnsigned ? ' UNSIGNED' : '';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function buildAfterString()\n    {\n        return $this->after !== null ?\n            ' AFTER ' . $this->db->quoteColumnName($this->after) :\n            '';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function buildFirstString()\n    {\n        return $this->isFirst ? ' FIRST' : '';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function buildCommentString()\n    {\n        return $this->comment !== null ? ' COMMENT ' . $this->db->quoteValue($this->comment) : '';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function __toString()\n    {\n        switch ($this->getTypeCategory()) {\n            case self::CATEGORY_PK:\n                $format = '{type}{check}{comment}{append}{pos}';\n                break;\n            case self::CATEGORY_NUMERIC:\n                $format = '{type}{length}{unsigned}{notnull}{unique}{default}{check}{comment}{append}{pos}';\n                break;\n            default:\n                $format = '{type}{length}{notnull}{unique}{default}{check}{comment}{append}{pos}';\n        }\n\n        return $this->buildCompleteString($format);\n    }\n}\n"
  },
  {
    "path": "framework/db/cubrid/QueryBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\cubrid;\n\nuse yii\\base\\InvalidArgumentException;\nuse yii\\base\\NotSupportedException;\nuse yii\\db\\Exception;\nuse yii\\db\\Expression;\n\n/**\n * QueryBuilder is the query builder for CUBRID databases (version 9.3.x and higher).\n *\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0\n */\nclass QueryBuilder extends \\yii\\db\\QueryBuilder\n{\n    /**\n     * @var array mapping from abstract column types (keys) to physical column types (values).\n     */\n    public $typeMap = [\n        Schema::TYPE_PK => 'int NOT NULL AUTO_INCREMENT PRIMARY KEY',\n        Schema::TYPE_UPK => 'int UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY',\n        Schema::TYPE_BIGPK => 'bigint NOT NULL AUTO_INCREMENT PRIMARY KEY',\n        Schema::TYPE_UBIGPK => 'bigint UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY',\n        Schema::TYPE_CHAR => 'char(1)',\n        Schema::TYPE_STRING => 'varchar(255)',\n        Schema::TYPE_TEXT => 'varchar',\n        Schema::TYPE_TINYINT => 'smallint',\n        Schema::TYPE_SMALLINT => 'smallint',\n        Schema::TYPE_INTEGER => 'int',\n        Schema::TYPE_BIGINT => 'bigint',\n        Schema::TYPE_FLOAT => 'float(7)',\n        Schema::TYPE_DOUBLE => 'double(15)',\n        Schema::TYPE_DECIMAL => 'decimal(10,0)',\n        Schema::TYPE_DATETIME => 'datetime',\n        Schema::TYPE_TIMESTAMP => 'timestamp',\n        Schema::TYPE_TIME => 'time',\n        Schema::TYPE_DATE => 'date',\n        Schema::TYPE_BINARY => 'blob',\n        Schema::TYPE_BOOLEAN => 'smallint',\n        Schema::TYPE_MONEY => 'decimal(19,4)',\n    ];\n\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function defaultExpressionBuilders()\n    {\n        return array_merge(parent::defaultExpressionBuilders(), [\n            'yii\\db\\conditions\\LikeCondition' => 'yii\\db\\cubrid\\conditions\\LikeConditionBuilder',\n        ]);\n    }\n\n    /**\n     * {@inheritdoc}\n     * @see https://www.cubrid.org/manual/en/9.3.0/sql/query/merge.html\n     */\n    public function upsert($table, $insertColumns, $updateColumns, &$params)\n    {\n        list($uniqueNames, $insertNames, $updateNames) = $this->prepareUpsertColumns($table, $insertColumns, $updateColumns, $constraints);\n        if (empty($uniqueNames)) {\n            return $this->insert($table, $insertColumns, $params);\n        }\n        if ($updateNames === []) {\n            // there are no columns to update\n            $updateColumns = false;\n        }\n\n        $onCondition = ['or'];\n        $quotedTableName = $this->db->quoteTableName($table);\n        foreach ($constraints as $constraint) {\n            $constraintCondition = ['and'];\n            foreach ($constraint->columnNames as $name) {\n                $quotedName = $this->db->quoteColumnName($name);\n                $constraintCondition[] = \"$quotedTableName.$quotedName=\\\"EXCLUDED\\\".$quotedName\";\n            }\n            $onCondition[] = $constraintCondition;\n        }\n        $on = $this->buildCondition($onCondition, $params);\n        list(, $placeholders, $values, $params) = $this->prepareInsertValues($table, $insertColumns, $params);\n        $mergeSql = 'MERGE INTO ' . $this->db->quoteTableName($table) . ' '\n            . 'USING (' . (!empty($placeholders) ? 'VALUES (' . implode(', ', $placeholders) . ')' : ltrim($values, ' ')) . ') AS \"EXCLUDED\" (' . implode(', ', $insertNames) . ') '\n            . \"ON ($on)\";\n        $insertValues = [];\n        foreach ($insertNames as $name) {\n            $quotedName = $this->db->quoteColumnName($name);\n            if (strrpos($quotedName, '.') === false) {\n                $quotedName = '\"EXCLUDED\".' . $quotedName;\n            }\n            $insertValues[] = $quotedName;\n        }\n        $insertSql = 'INSERT (' . implode(', ', $insertNames) . ')'\n            . ' VALUES (' . implode(', ', $insertValues) . ')';\n        if ($updateColumns === false) {\n            return \"$mergeSql WHEN NOT MATCHED THEN $insertSql\";\n        }\n\n        if ($updateColumns === true) {\n            $updateColumns = [];\n            foreach ($updateNames as $name) {\n                $quotedName = $this->db->quoteColumnName($name);\n                if (strrpos($quotedName, '.') === false) {\n                    $quotedName = '\"EXCLUDED\".' . $quotedName;\n                }\n                $updateColumns[$name] = new Expression($quotedName);\n            }\n        }\n        list($updates, $params) = $this->prepareUpdateSets($table, $updateColumns, $params);\n        $updateSql = 'UPDATE SET ' . implode(', ', $updates);\n        return \"$mergeSql WHEN MATCHED THEN $updateSql WHEN NOT MATCHED THEN $insertSql\";\n    }\n\n    /**\n     * Creates a SQL statement for resetting the sequence value of a table's primary key.\n     * The sequence will be reset such that the primary key of the next new row inserted\n     * will have the specified value or 1.\n     * @param string $tableName the name of the table whose primary key sequence will be reset\n     * @param mixed $value the value for the primary key of the next new row inserted. If this is not set,\n     * the next new row's primary key will have a value 1.\n     * @return string the SQL statement for resetting sequence\n     * @throws InvalidArgumentException if the table does not exist or there is no sequence associated with the table.\n     */\n    public function resetSequence($tableName, $value = null)\n    {\n        $table = $this->db->getTableSchema($tableName);\n        if ($table !== null && $table->sequenceName !== null) {\n            $tableName = $this->db->quoteTableName($tableName);\n            if ($value === null) {\n                $key = reset($table->primaryKey);\n                $value = (int) $this->db->createCommand(\"SELECT MAX(`$key`) FROM \" . $this->db->schema->quoteTableName($tableName))->queryScalar() + 1;\n            } else {\n                $value = (int) $value;\n            }\n\n            return 'ALTER TABLE ' . $this->db->schema->quoteTableName($tableName) . \" AUTO_INCREMENT=$value;\";\n        } elseif ($table === null) {\n            throw new InvalidArgumentException(\"Table not found: $tableName\");\n        }\n\n        throw new InvalidArgumentException(\"There is not sequence associated with table '$tableName'.\");\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function buildLimit($limit, $offset)\n    {\n        $sql = '';\n        // limit is not optional in CUBRID\n        // https://www.cubrid.org/manual/en/9.3.0/sql/query/select.html#limit-clause\n        // \"You can specify a very big integer for row_count to display to the last row, starting from a specific row.\"\n        if ($this->hasLimit($limit)) {\n            $sql = 'LIMIT ' . $limit;\n            if ($this->hasOffset($offset)) {\n                $sql .= ' OFFSET ' . $offset;\n            }\n        } elseif ($this->hasOffset($offset)) {\n            $sql = \"LIMIT 9223372036854775807 OFFSET $offset\"; // 2^63-1\n        }\n\n        return $sql;\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.8\n     */\n    public function selectExists($rawSql)\n    {\n        return 'SELECT CASE WHEN EXISTS(' . $rawSql . ') THEN 1 ELSE 0 END';\n    }\n\n    /**\n     * {@inheritdoc}\n     * @see https://www.cubrid.org/manual/en/9.3.0/sql/schema/table.html#drop-index-clause\n     */\n    public function dropIndex($name, $table)\n    {\n        /** @var Schema $schema */\n        $schema = $this->db->getSchema();\n        foreach ($schema->getTableUniques($table) as $unique) {\n            if ($unique->name === $name) {\n                return $this->dropUnique($name, $table);\n            }\n        }\n\n        return 'DROP INDEX ' . $this->db->quoteTableName($name) . ' ON ' . $this->db->quoteTableName($table);\n    }\n\n    /**\n     * {@inheritdoc}\n     * @throws NotSupportedException this is not supported by CUBRID.\n     */\n    public function addCheck($name, $table, $expression)\n    {\n        throw new NotSupportedException(__METHOD__ . ' is not supported by CUBRID.');\n    }\n\n    /**\n     * {@inheritdoc}\n     * @throws NotSupportedException this is not supported by CUBRID.\n     */\n    public function dropCheck($name, $table)\n    {\n        throw new NotSupportedException(__METHOD__ . ' is not supported by CUBRID.');\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.8\n     */\n    public function addCommentOnColumn($table, $column, $comment)\n    {\n        $definition = $this->getColumnDefinition($table, $column);\n        $definition = trim(preg_replace(\"/COMMENT '(.*?)'/i\", '', $definition));\n\n        return 'ALTER TABLE ' . $this->db->quoteTableName($table)\n        . ' CHANGE ' . $this->db->quoteColumnName($column)\n        . ' ' . $this->db->quoteColumnName($column)\n        . (empty($definition) ? '' : ' ' . $definition)\n        . ' COMMENT ' . $this->db->quoteValue($comment);\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.8\n     */\n    public function addCommentOnTable($table, $comment)\n    {\n        return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' COMMENT ' . $this->db->quoteValue($comment);\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.8\n     */\n    public function dropCommentFromColumn($table, $column)\n    {\n        return $this->addCommentOnColumn($table, $column, '');\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.8\n     */\n    public function dropCommentFromTable($table)\n    {\n        return $this->addCommentOnTable($table, '');\n    }\n\n\n    /**\n     * Gets column definition.\n     *\n     * @param string $table table name\n     * @param string $column column name\n     * @return string|null the column definition\n     * @throws Exception in case when table does not contain column\n     * @since 2.0.8\n     */\n    private function getColumnDefinition($table, $column)\n    {\n        $row = $this->db->createCommand('SHOW CREATE TABLE ' . $this->db->quoteTableName($table))->queryOne();\n        if ($row === false) {\n            throw new Exception(\"Unable to find column '$column' in table '$table'.\");\n        }\n        if (isset($row['Create Table'])) {\n            $sql = $row['Create Table'];\n        } else {\n            $row = array_values($row);\n            $sql = $row[1];\n        }\n        $sql = preg_replace('/^[^(]+\\((.*)\\).*$/', '\\1', $sql);\n        $sql = str_replace(', [', \",\\n[\", $sql);\n        if (preg_match_all('/^\\s*\\[(.*?)\\]\\s+(.*?),?$/m', $sql, $matches)) {\n            foreach ($matches[1] as $i => $c) {\n                if ($c === $column) {\n                    return $matches[2][$i];\n                }\n            }\n        }\n\n        return null;\n    }\n}\n"
  },
  {
    "path": "framework/db/cubrid/Schema.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\cubrid;\n\nuse Yii;\nuse yii\\base\\NotSupportedException;\nuse yii\\db\\ColumnSchema;\nuse yii\\db\\Constraint;\nuse yii\\db\\ConstraintFinderInterface;\nuse yii\\db\\ConstraintFinderTrait;\nuse yii\\db\\Expression;\nuse yii\\db\\ForeignKeyConstraint;\nuse yii\\db\\IndexConstraint;\nuse yii\\db\\TableSchema;\nuse yii\\db\\Transaction;\nuse yii\\helpers\\ArrayHelper;\nuse yii\\db\\Schema as BaseSchema;\n\n/**\n * Schema is the class for retrieving metadata from a CUBRID database (version 9.3.x and higher).\n *\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0\n *\n * @template T of ColumnSchema = ColumnSchema\n * @extends BaseSchema<T>\n */\nclass Schema extends BaseSchema implements ConstraintFinderInterface\n{\n    use ConstraintFinderTrait;\n\n    /**\n     * @var array mapping from physical column types (keys) to abstract column types (values)\n     * Please refer to [CUBRID manual](https://www.cubrid.org/manual/en/9.3.0/sql/datatype.html) for\n     * details on data types.\n     */\n    public $typeMap = [\n        // Numeric data types\n        'short' => self::TYPE_SMALLINT,\n        'smallint' => self::TYPE_SMALLINT,\n        'int' => self::TYPE_INTEGER,\n        'integer' => self::TYPE_INTEGER,\n        'bigint' => self::TYPE_BIGINT,\n        'numeric' => self::TYPE_DECIMAL,\n        'decimal' => self::TYPE_DECIMAL,\n        'float' => self::TYPE_FLOAT,\n        'real' => self::TYPE_FLOAT,\n        'double' => self::TYPE_DOUBLE,\n        'double precision' => self::TYPE_DOUBLE,\n        'monetary' => self::TYPE_MONEY,\n        // Date/Time data types\n        'date' => self::TYPE_DATE,\n        'time' => self::TYPE_TIME,\n        'timestamp' => self::TYPE_TIMESTAMP,\n        'datetime' => self::TYPE_DATETIME,\n        // String data types\n        'char' => self::TYPE_CHAR,\n        'varchar' => self::TYPE_STRING,\n        'char varying' => self::TYPE_STRING,\n        'nchar' => self::TYPE_CHAR,\n        'nchar varying' => self::TYPE_STRING,\n        'string' => self::TYPE_STRING,\n        // BLOB/CLOB data types\n        'blob' => self::TYPE_BINARY,\n        'clob' => self::TYPE_BINARY,\n        // Bit string data types\n        'bit' => self::TYPE_INTEGER,\n        'bit varying' => self::TYPE_INTEGER,\n        // Collection data types (considered strings for now)\n        'set' => self::TYPE_STRING,\n        'multiset' => self::TYPE_STRING,\n        'list' => self::TYPE_STRING,\n        'sequence' => self::TYPE_STRING,\n        'enum' => self::TYPE_STRING,\n    ];\n    /**\n     * @var array map of DB errors and corresponding exceptions\n     * If left part is found in DB error message exception class from the right part is used.\n     */\n    public $exceptionMap = [\n        'Operation would have caused one or more unique constraint violations' => 'yii\\db\\IntegrityException',\n    ];\n\n    /**\n     * {@inheritdoc}\n     */\n    protected $tableQuoteCharacter = '\"';\n\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function findTableNames($schema = '')\n    {\n        $pdo = $this->db->getSlavePdo(true);\n        $tables = $pdo->cubrid_schema(\\PDO::CUBRID_SCH_TABLE);\n        $tableNames = [];\n        foreach ($tables as $table) {\n            // do not list system tables\n            if ($table['TYPE'] != 0) {\n                $tableNames[] = $table['NAME'];\n            }\n        }\n\n        return $tableNames;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableSchema($name)\n    {\n        $pdo = $this->db->getSlavePdo(true);\n\n        $tableInfo = $pdo->cubrid_schema(\\PDO::CUBRID_SCH_TABLE, $name);\n\n        if (!isset($tableInfo[0]['NAME'])) {\n            return null;\n        }\n\n        $table = new TableSchema();\n        $table->fullName = $table->name = $tableInfo[0]['NAME'];\n\n        $sql = 'SHOW FULL COLUMNS FROM ' . $this->quoteSimpleTableName($table->name);\n        $columns = $this->db->createCommand($sql)->queryAll();\n\n        foreach ($columns as $info) {\n            $column = $this->loadColumnSchema($info);\n            $table->columns[$column->name] = $column;\n        }\n\n        $primaryKeys = $pdo->cubrid_schema(\\PDO::CUBRID_SCH_PRIMARY_KEY, $table->name);\n        foreach ($primaryKeys as $key) {\n            $column = $table->columns[$key['ATTR_NAME']];\n            $column->isPrimaryKey = true;\n            $table->primaryKey[] = $column->name;\n            if ($column->autoIncrement) {\n                $table->sequenceName = '';\n            }\n        }\n\n        $foreignKeys = $pdo->cubrid_schema(\\PDO::CUBRID_SCH_IMPORTED_KEYS, $table->name);\n        foreach ($foreignKeys as $key) {\n            if (isset($table->foreignKeys[$key['FK_NAME']])) {\n                $table->foreignKeys[$key['FK_NAME']][$key['FKCOLUMN_NAME']] = $key['PKCOLUMN_NAME'];\n            } else {\n                $table->foreignKeys[$key['FK_NAME']] = [\n                    $key['PKTABLE_NAME'],\n                    $key['FKCOLUMN_NAME'] => $key['PKCOLUMN_NAME'],\n                ];\n            }\n        }\n\n        return $table;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTablePrimaryKey($tableName)\n    {\n        $primaryKey = $this->db->getSlavePdo(true)->cubrid_schema(\\PDO::CUBRID_SCH_PRIMARY_KEY, $tableName);\n        if (empty($primaryKey)) {\n            return null;\n        }\n\n        ArrayHelper::multisort($primaryKey, 'KEY_SEQ', SORT_ASC, SORT_NUMERIC);\n        return new Constraint([\n            'name' => $primaryKey[0]['KEY_NAME'],\n            'columnNames' => ArrayHelper::getColumn($primaryKey, 'ATTR_NAME'),\n        ]);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableForeignKeys($tableName)\n    {\n        static $actionTypes = [\n            0 => 'CASCADE',\n            1 => 'RESTRICT',\n            2 => 'NO ACTION',\n            3 => 'SET NULL',\n        ];\n\n        $foreignKeys = $this->db->getSlavePdo(true)->cubrid_schema(\\PDO::CUBRID_SCH_IMPORTED_KEYS, $tableName);\n        $foreignKeys = ArrayHelper::index($foreignKeys, null, 'FK_NAME');\n        ArrayHelper::multisort($foreignKeys, 'KEY_SEQ', SORT_ASC, SORT_NUMERIC);\n        $result = [];\n        foreach ($foreignKeys as $name => $foreignKey) {\n            $result[] = new ForeignKeyConstraint([\n                'name' => $name,\n                'columnNames' => ArrayHelper::getColumn($foreignKey, 'FKCOLUMN_NAME'),\n                'foreignTableName' => $foreignKey[0]['PKTABLE_NAME'],\n                'foreignColumnNames' => ArrayHelper::getColumn($foreignKey, 'PKCOLUMN_NAME'),\n                'onDelete' => isset($actionTypes[$foreignKey[0]['DELETE_RULE']]) ? $actionTypes[$foreignKey[0]['DELETE_RULE']] : null,\n                'onUpdate' => isset($actionTypes[$foreignKey[0]['UPDATE_RULE']]) ? $actionTypes[$foreignKey[0]['UPDATE_RULE']] : null,\n            ]);\n        }\n\n        return $result;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableIndexes($tableName)\n    {\n        return $this->loadTableConstraints($tableName, 'indexes');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableUniques($tableName)\n    {\n        return $this->loadTableConstraints($tableName, 'uniques');\n    }\n\n    /**\n     * {@inheritdoc}\n     * @throws NotSupportedException if this method is called.\n     */\n    protected function loadTableChecks($tableName)\n    {\n        throw new NotSupportedException('CUBRID does not support check constraints.');\n    }\n\n    /**\n     * {@inheritdoc}\n     * @throws NotSupportedException if this method is called.\n     */\n    protected function loadTableDefaultValues($tableName)\n    {\n        throw new NotSupportedException('CUBRID does not support default value constraints.');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function releaseSavepoint($name)\n    {\n        // does nothing as cubrid does not support this\n    }\n\n    /**\n     * Creates a query builder for the CUBRID database.\n     * @return QueryBuilder query builder instance\n     */\n    public function createQueryBuilder()\n    {\n        return Yii::createObject(QueryBuilder::className(), [$this->db]);\n    }\n\n    /**\n     * Loads the column information into a [[ColumnSchema]] object.\n     * @param array $info column information\n     * @return T the column schema object\n     */\n    protected function loadColumnSchema($info)\n    {\n        $column = $this->createColumnSchema();\n\n        $column->name = $info['Field'];\n        $column->allowNull = $info['Null'] === 'YES';\n        $column->isPrimaryKey = false; // primary key will be set by loadTableSchema() later\n        $column->autoIncrement = stripos($info['Extra'], 'auto_increment') !== false;\n\n        $column->dbType = $info['Type'];\n        $column->unsigned = strpos($column->dbType, 'unsigned') !== false;\n\n        $column->type = self::TYPE_STRING;\n        if (preg_match('/^([\\w ]+)(?:\\(([^\\)]+)\\))?$/', $column->dbType, $matches)) {\n            $type = strtolower($matches[1]);\n            $column->dbType = $type . (isset($matches[2]) ? \"({$matches[2]})\" : '');\n            if (isset($this->typeMap[$type])) {\n                $column->type = $this->typeMap[$type];\n            }\n            if (!empty($matches[2])) {\n                if ($type === 'enum') {\n                    $values = preg_split('/\\s*,\\s*/', $matches[2]);\n                    foreach ($values as $i => $value) {\n                        $values[$i] = trim($value, \"'\");\n                    }\n                    $column->enumValues = $values;\n                } else {\n                    $values = explode(',', $matches[2]);\n                    $column->size = $column->precision = (int) $values[0];\n                    if (isset($values[1])) {\n                        $column->scale = (int) $values[1];\n                    }\n                    if ($column->size === 1 && $type === 'bit') {\n                        $column->type = 'boolean';\n                    } elseif ($type === 'bit') {\n                        if ($column->size > 32) {\n                            $column->type = 'bigint';\n                        } elseif ($column->size === 32) {\n                            $column->type = 'integer';\n                        }\n                    }\n                }\n            }\n        }\n\n        $column->phpType = $this->getColumnPhpType($column);\n\n        if ($column->isPrimaryKey) {\n            return $column;\n        }\n\n        if (\n            $column->type === 'timestamp' && $info['Default'] === 'SYS_TIMESTAMP' ||\n            $column->type === 'datetime' && $info['Default'] === 'SYS_DATETIME' ||\n            $column->type === 'date' && $info['Default'] === 'SYS_DATE' ||\n            $column->type === 'time' && $info['Default'] === 'SYS_TIME'\n        ) {\n            $column->defaultValue = new Expression($info['Default']);\n        } elseif (isset($type) && $type === 'bit') {\n            $column->defaultValue = hexdec(trim($info['Default'], 'X\\''));\n        } else {\n            $column->defaultValue = $column->phpTypecast($info['Default']);\n        }\n\n        return $column;\n    }\n\n    /**\n     * Determines the PDO type for the given PHP data value.\n     * @param mixed $data the data whose PDO type is to be determined\n     * @return int the PDO type\n     * @see https://www.php.net/manual/en/pdo.constants.php\n     */\n    public function getPdoType($data)\n    {\n        static $typeMap = [\n            // php type => PDO type\n            'boolean' => \\PDO::PARAM_INT, // PARAM_BOOL is not supported by CUBRID PDO\n            'integer' => \\PDO::PARAM_INT,\n            'string' => \\PDO::PARAM_STR,\n            'resource' => \\PDO::PARAM_LOB,\n            'NULL' => \\PDO::PARAM_NULL,\n        ];\n        $type = gettype($data);\n\n        return isset($typeMap[$type]) ? $typeMap[$type] : \\PDO::PARAM_STR;\n    }\n\n    /**\n     * {@inheritdoc}\n     * @see https://www.cubrid.org/manual/en/9.3.0/sql/transaction.html#database-concurrency\n     */\n    public function setTransactionIsolationLevel($level)\n    {\n        // translate SQL92 levels to CUBRID levels:\n        switch ($level) {\n            case Transaction::SERIALIZABLE:\n                $level = '6'; // SERIALIZABLE\n                break;\n            case Transaction::REPEATABLE_READ:\n                $level = '5'; // REPEATABLE READ CLASS with REPEATABLE READ INSTANCES\n                break;\n            case Transaction::READ_COMMITTED:\n                $level = '4'; // REPEATABLE READ CLASS with READ COMMITTED INSTANCES\n                break;\n            case Transaction::READ_UNCOMMITTED:\n                $level = '3'; // REPEATABLE READ CLASS with READ UNCOMMITTED INSTANCES\n                break;\n        }\n        parent::setTransactionIsolationLevel($level);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function createColumnSchemaBuilder($type, $length = null)\n    {\n        return new ColumnSchemaBuilder($type, $length, $this->db);\n    }\n\n    /**\n     * Loads multiple types of constraints and returns the specified ones.\n     * @param string $tableName table name.\n     * @param string $returnType return type:\n     * - indexes\n     * - uniques\n     * @return mixed constraints.\n     */\n    private function loadTableConstraints($tableName, $returnType)\n    {\n        $constraints = $this->db->getSlavePdo(true)->cubrid_schema(\\PDO::CUBRID_SCH_CONSTRAINT, $tableName);\n        $constraints = ArrayHelper::index($constraints, null, ['TYPE', 'NAME']);\n        ArrayHelper::multisort($constraints, 'KEY_ORDER', SORT_ASC, SORT_NUMERIC);\n        $result = [\n            'indexes' => [],\n            'uniques' => [],\n        ];\n        foreach ($constraints as $type => $names) {\n            foreach ($names as $name => $constraint) {\n                $isUnique = in_array((int) $type, [0, 2], true);\n                $result['indexes'][] = new IndexConstraint([\n                    'isPrimary' => (bool) $constraint[0]['PRIMARY_KEY'],\n                    'isUnique' => $isUnique,\n                    'name' => $name,\n                    'columnNames' => ArrayHelper::getColumn($constraint, 'ATTR_NAME'),\n                ]);\n                if ($isUnique) {\n                    $result['uniques'][] = new Constraint([\n                        'name' => $name,\n                        'columnNames' => ArrayHelper::getColumn($constraint, 'ATTR_NAME'),\n                    ]);\n                }\n            }\n        }\n        foreach ($result as $type => $data) {\n            $this->setTableMetadata($tableName, $type, $data);\n        }\n\n        return $result[$returnType];\n    }\n}\n"
  },
  {
    "path": "framework/db/cubrid/conditions/LikeConditionBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\cubrid\\conditions;\n\n/**\n * {@inheritdoc}\n */\nclass LikeConditionBuilder extends \\yii\\db\\conditions\\LikeConditionBuilder\n{\n    /**\n     * {@inheritdoc}\n     */\n    protected $escapeCharacter = '!';\n    /**\n     * `\\` is initialized in [[buildLikeCondition()]] method since\n     * we need to choose replacement value based on [[\\yii\\db\\Schema::quoteValue()]].\n     * {@inheritdoc}\n     */\n    protected $escapingReplacements = [\n        '%' => '!%',\n        '_' => '!_',\n        '!' => '!!',\n    ];\n}\n"
  },
  {
    "path": "framework/db/mssql/ColumnSchema.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\mssql;\n\n/**\n * Class ColumnSchema for MSSQL database\n *\n * @since 2.0.23\n */\nclass ColumnSchema extends \\yii\\db\\ColumnSchema\n{\n    /**\n     * @var bool whether this column is a computed column\n     * @since 2.0.39\n     */\n    public $isComputed;\n\n\n    /**\n     * Prepares default value and converts it according to [[phpType]]\n     * @param mixed $value default value\n     * @return mixed converted value\n     * @since 2.0.24\n     */\n    public function defaultPhpTypecast($value)\n    {\n        if ($value !== null) {\n            // convert from MSSQL column_default format, e.g. ('1') -> 1, ('string') -> string\n            $value = substr(substr($value, 2), 0, -2);\n        }\n\n        return parent::phpTypecast($value);\n    }\n}\n"
  },
  {
    "path": "framework/db/mssql/ColumnSchemaBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\mssql;\n\nuse yii\\db\\ColumnSchemaBuilder as AbstractColumnSchemaBuilder;\nuse yii\\db\\Expression;\n\n/**\n * ColumnSchemaBuilder is the schema builder for MSSQL databases.\n *\n * @property-read string|null $checkValue The `CHECK` constraint for the column.\n * @property-read string|Expression|null $defaultValue Default value of the column.\n *\n * @author Valerii Gorbachev <darkdef@gmail.com>\n * @since 2.0.42\n */\nclass ColumnSchemaBuilder extends AbstractColumnSchemaBuilder\n{\n    protected $format = '{type}{length}{notnull}{unique}{default}{check}{append}';\n\n\n    /**\n     * Builds the full string for the column's schema.\n     * @return string\n     */\n    public function __toString()\n    {\n        if ($this->getTypeCategory() === self::CATEGORY_PK) {\n            $format = '{type}{check}{comment}{append}';\n        } else {\n            $format = $this->format;\n        }\n\n        return $this->buildCompleteString($format);\n    }\n\n    /**\n     * Changes default format string to MSSQL ALTER COMMAND.\n     */\n    public function setAlterColumnFormat()\n    {\n        $this->format = '{type}{length}{notnull}{append}';\n    }\n\n    /**\n     * Getting the `Default` value for constraint\n     * @return string|Expression|null default value of the column.\n     */\n    public function getDefaultValue()\n    {\n        if ($this->default instanceof Expression) {\n            return $this->default;\n        }\n\n        return $this->buildDefaultValue();\n    }\n\n    /**\n     * Get the `Check` value for constraint\n     * @return string|null the `CHECK` constraint for the column.\n     */\n    public function getCheckValue()\n    {\n        return $this->check !== null ? (string) $this->check : null;\n    }\n\n    /**\n     * @return bool whether the column values should be unique. If this is `true`, a `UNIQUE` constraint will be added.\n     */\n    public function isUnique()\n    {\n        return $this->isUnique;\n    }\n}\n"
  },
  {
    "path": "framework/db/mssql/DBLibPDO.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\mssql;\n\n/**\n * This is an extension of the default PDO class of DBLIB drivers.\n * It provides workarounds for improperly implemented functionalities of the DBLIB drivers.\n *\n * @author Bert Brunekreeft <bbrunekreeft@gmail.com>\n * @since 2.0.41\n */\nclass DBLibPDO extends \\PDO\n{\n    /**\n     * Returns value of the last inserted ID.\n     * @param string|null $name the sequence name. Defaults to null.\n     * @return string|false last inserted ID value.\n     */\n    #[\\ReturnTypeWillChange]\n    public function lastInsertId($name = null)\n    {\n        return $this->query('SELECT CAST(COALESCE(SCOPE_IDENTITY(), @@IDENTITY) AS bigint)')->fetchColumn();\n    }\n\n    /**\n     * Retrieve a database connection attribute.\n     *\n     * It is necessary to override PDO's method as some MSSQL PDO driver (e.g. dblib) does not\n     * support getting attributes.\n     * @param int $attribute One of the PDO::ATTR_* constants.\n     * @return mixed A successful call returns the value of the requested PDO attribute.\n     * An unsuccessful call returns null.\n     */\n    #[\\ReturnTypeWillChange]\n    public function getAttribute($attribute)\n    {\n        try {\n            return parent::getAttribute($attribute);\n        } catch (\\PDOException $e) {\n            switch ($attribute) {\n                case self::ATTR_SERVER_VERSION:\n                    return $this->query(\"SELECT CAST(SERVERPROPERTY('productversion') AS VARCHAR)\")->fetchColumn();\n                default:\n                    throw $e;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "framework/db/mssql/PDO.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\mssql;\n\n/**\n * This is an extension of the default PDO class of MSSQL and DBLIB drivers.\n * It provides workarounds for improperly implemented functionalities of the MSSQL and DBLIB drivers.\n *\n * @author Timur Ruziev <resurtm@gmail.com>\n * @since 2.0\n */\nclass PDO extends \\PDO\n{\n    /**\n     * Returns value of the last inserted ID.\n     * @param string|null $sequence the sequence name. Defaults to null.\n     * @return string|false last inserted ID value.\n     */\n    #[\\ReturnTypeWillChange]\n    public function lastInsertId($sequence = null)\n    {\n        return $this->query('SELECT CAST(COALESCE(SCOPE_IDENTITY(), @@IDENTITY) AS bigint)')->fetchColumn();\n    }\n\n    /**\n     * Starts a transaction. It is necessary to override PDO's method as MSSQL PDO driver does not\n     * natively support transactions.\n     * @return bool the result of a transaction start.\n     */\n    #[\\ReturnTypeWillChange]\n    public function beginTransaction()\n    {\n        $this->exec('BEGIN TRANSACTION');\n\n        return true;\n    }\n\n    /**\n     * Commits a transaction. It is necessary to override PDO's method as MSSQL PDO driver does not\n     * natively support transactions.\n     * @return bool the result of a transaction commit.\n     */\n    #[\\ReturnTypeWillChange]\n    public function commit()\n    {\n        $this->exec('COMMIT TRANSACTION');\n\n        return true;\n    }\n\n    /**\n     * Rollbacks a transaction. It is necessary to override PDO's method as MSSQL PDO driver does not\n     * natively support transactions.\n     * @return bool the result of a transaction roll back.\n     */\n    #[\\ReturnTypeWillChange]\n    public function rollBack()\n    {\n        $this->exec('ROLLBACK TRANSACTION');\n\n        return true;\n    }\n\n    /**\n     * Retrieve a database connection attribute.\n     *\n     * It is necessary to override PDO's method as some MSSQL PDO driver (e.g. dblib) does not\n     * support getting attributes.\n     * @param int $attribute One of the PDO::ATTR_* constants.\n     * @return mixed A successful call returns the value of the requested PDO attribute.\n     * An unsuccessful call returns null.\n     */\n    #[\\ReturnTypeWillChange]\n    public function getAttribute($attribute)\n    {\n        try {\n            return parent::getAttribute($attribute);\n        } catch (\\PDOException $e) {\n            switch ($attribute) {\n                case self::ATTR_SERVER_VERSION:\n                    return $this->query(\"SELECT CAST(SERVERPROPERTY('productversion') AS VARCHAR)\")->fetchColumn();\n                default:\n                    throw $e;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "framework/db/mssql/QueryBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\mssql;\n\nuse yii\\base\\InvalidArgumentException;\nuse yii\\base\\NotSupportedException;\nuse yii\\db\\Expression;\nuse yii\\db\\Query;\nuse yii\\db\\TableSchema;\n\n/**\n * QueryBuilder is the query builder for MS SQL Server databases (version 2008 and above).\n *\n * @author Timur Ruziev <resurtm@gmail.com>\n * @since 2.0\n */\nclass QueryBuilder extends \\yii\\db\\QueryBuilder\n{\n    /**\n     * @var array mapping from abstract column types (keys) to physical column types (values).\n     */\n    public $typeMap = [\n        Schema::TYPE_PK => 'int IDENTITY PRIMARY KEY',\n        Schema::TYPE_UPK => 'int IDENTITY PRIMARY KEY',\n        Schema::TYPE_BIGPK => 'bigint IDENTITY PRIMARY KEY',\n        Schema::TYPE_UBIGPK => 'bigint IDENTITY PRIMARY KEY',\n        Schema::TYPE_CHAR => 'nchar(1)',\n        Schema::TYPE_STRING => 'nvarchar(255)',\n        Schema::TYPE_TEXT => 'nvarchar(max)',\n        Schema::TYPE_TINYINT => 'tinyint',\n        Schema::TYPE_SMALLINT => 'smallint',\n        Schema::TYPE_INTEGER => 'int',\n        Schema::TYPE_BIGINT => 'bigint',\n        Schema::TYPE_FLOAT => 'float',\n        Schema::TYPE_DOUBLE => 'float',\n        Schema::TYPE_DECIMAL => 'decimal(18,0)',\n        Schema::TYPE_DATETIME => 'datetime',\n        Schema::TYPE_TIMESTAMP => 'datetime',\n        Schema::TYPE_TIME => 'time',\n        Schema::TYPE_DATE => 'date',\n        Schema::TYPE_BINARY => 'varbinary(max)',\n        Schema::TYPE_BOOLEAN => 'bit',\n        Schema::TYPE_MONEY => 'decimal(19,4)',\n    ];\n\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function defaultExpressionBuilders()\n    {\n        return array_merge(parent::defaultExpressionBuilders(), [\n            'yii\\db\\conditions\\InCondition' => 'yii\\db\\mssql\\conditions\\InConditionBuilder',\n            'yii\\db\\conditions\\LikeCondition' => 'yii\\db\\mssql\\conditions\\LikeConditionBuilder',\n        ]);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function buildOrderByAndLimit($sql, $orderBy, $limit, $offset)\n    {\n        if (!$this->hasOffset($offset) && !$this->hasLimit($limit)) {\n            $orderBy = $this->buildOrderBy($orderBy);\n            return $orderBy === '' ? $sql : $sql . $this->separator . $orderBy;\n        }\n\n        if (version_compare($this->db->getSchema()->getServerVersion(), '11', '<')) {\n            return $this->oldBuildOrderByAndLimit($sql, $orderBy, $limit, $offset);\n        }\n\n        return $this->newBuildOrderByAndLimit($sql, $orderBy, $limit, $offset);\n    }\n\n    /**\n     * Builds the ORDER BY/LIMIT/OFFSET clauses for SQL SERVER 2012 or newer.\n     * @param string $sql the existing SQL (without ORDER BY/LIMIT/OFFSET)\n     * @param array $orderBy the order by columns. See [[\\yii\\db\\Query::orderBy]] for more details on how to specify this parameter.\n     * @param int $limit the limit number. See [[\\yii\\db\\Query::limit]] for more details.\n     * @param int $offset the offset number. See [[\\yii\\db\\Query::offset]] for more details.\n     * @return string the SQL completed with ORDER BY/LIMIT/OFFSET (if any)\n     */\n    protected function newBuildOrderByAndLimit($sql, $orderBy, $limit, $offset)\n    {\n        $orderBy = $this->buildOrderBy($orderBy);\n        if ($orderBy === '') {\n            // ORDER BY clause is required when FETCH and OFFSET are in the SQL\n            $orderBy = 'ORDER BY (SELECT NULL)';\n        }\n        $sql .= $this->separator . $orderBy;\n\n        // https://technet.microsoft.com/en-us/library/gg699618.aspx\n        $offset = $this->hasOffset($offset) ? $offset : '0';\n        $sql .= $this->separator . \"OFFSET $offset ROWS\";\n        if ($this->hasLimit($limit)) {\n            $sql .= $this->separator . \"FETCH NEXT $limit ROWS ONLY\";\n        }\n\n        return $sql;\n    }\n\n    /**\n     * Builds the ORDER BY/LIMIT/OFFSET clauses for SQL SERVER 2005 to 2008.\n     * @param string $sql the existing SQL (without ORDER BY/LIMIT/OFFSET)\n     * @param array $orderBy the order by columns. See [[\\yii\\db\\Query::orderBy]] for more details on how to specify this parameter.\n     * @param int|Expression $limit the limit number. See [[\\yii\\db\\Query::limit]] for more details.\n     * @param int $offset the offset number. See [[\\yii\\db\\Query::offset]] for more details.\n     * @return string the SQL completed with ORDER BY/LIMIT/OFFSET (if any)\n     */\n    protected function oldBuildOrderByAndLimit($sql, $orderBy, $limit, $offset)\n    {\n        $orderBy = $this->buildOrderBy($orderBy);\n        if ($orderBy === '') {\n            // ROW_NUMBER() requires an ORDER BY clause\n            $orderBy = 'ORDER BY (SELECT NULL)';\n        }\n\n        $sql = preg_replace('/^([\\s(])*SELECT(\\s+DISTINCT)?(?!\\s*TOP\\s*\\()/i', \"\\\\1SELECT\\\\2 rowNum = ROW_NUMBER() over ($orderBy),\", $sql);\n\n        if ($this->hasLimit($limit)) {\n            if ($limit instanceof Expression) {\n                $limit = '(' . (string)$limit . ')';\n            }\n            $sql = \"SELECT TOP $limit * FROM ($sql) sub\";\n        } else {\n            $sql = \"SELECT * FROM ($sql) sub\";\n        }\n        if ($this->hasOffset($offset)) {\n            $sql .= $this->separator . \"WHERE rowNum > $offset\";\n        }\n\n        return $sql;\n    }\n\n    /**\n     * Builds a SQL statement for renaming a DB table.\n     * @param string $oldName the table to be renamed. The name will be properly quoted by the method.\n     * @param string $newName the new table name. The name will be properly quoted by the method.\n     * @return string the SQL statement for renaming a DB table.\n     */\n    public function renameTable($oldName, $newName)\n    {\n        return 'sp_rename ' . $this->db->quoteTableName($oldName) . ', ' . $this->db->quoteTableName($newName);\n    }\n\n    /**\n     * Builds a SQL statement for renaming a column.\n     * @param string $table the table whose column is to be renamed. The name will be properly quoted by the method.\n     * @param string $oldName the old name of the column. The name will be properly quoted by the method.\n     * @param string $newName the new name of the column. The name will be properly quoted by the method.\n     * @return string the SQL statement for renaming a DB column.\n     */\n    public function renameColumn($table, $oldName, $newName)\n    {\n        $table = $this->db->quoteTableName($table);\n        $oldName = $this->db->quoteColumnName($oldName);\n        $newName = $this->db->quoteColumnName($newName);\n        return \"sp_rename '{$table}.{$oldName}', {$newName}, 'COLUMN'\";\n    }\n\n    /**\n     * Builds a SQL statement for changing the definition of a column.\n     * @param string $table the table whose column is to be changed. The table name will be properly quoted by the method.\n     * @param string $column the name of the column to be changed. The name will be properly quoted by the method.\n     * @param string $type the new column type. The [[getColumnType]] method will be invoked to convert abstract column type (if any)\n     * into the physical one. Anything that is not recognized as abstract type will be kept in the generated SQL.\n     * For example, 'string' will be turned into 'varchar(255)', while 'string not null' will become 'varchar(255) not null'.\n     * @return string the SQL statement for changing the definition of a column.\n     * @throws NotSupportedException if this is not supported by the underlying DBMS.\n     */\n    public function alterColumn($table, $column, $type)\n    {\n        $sqlAfter = [$this->dropConstraintsForColumn($table, $column, 'D')];\n\n        $columnName = $this->db->quoteColumnName($column);\n        $tableName = $this->db->quoteTableName($table);\n        $constraintBase = preg_replace('/[^a-z0-9_]/i', '', $table . '_' . $column);\n\n        if ($type instanceof \\yii\\db\\mssql\\ColumnSchemaBuilder) {\n            $type->setAlterColumnFormat();\n\n\n            $defaultValue = $type->getDefaultValue();\n            if ($defaultValue !== null) {\n                $sqlAfter[] = $this->addDefaultValue(\n                    \"DF_{$constraintBase}\",\n                    $table,\n                    $column,\n                    $defaultValue instanceof Expression ? $defaultValue : new Expression($defaultValue)\n                );\n            }\n\n            $checkValue = $type->getCheckValue();\n            if ($checkValue !== null) {\n                $sqlAfter[] = \"ALTER TABLE {$tableName} ADD CONSTRAINT \" .\n                    $this->db->quoteColumnName(\"CK_{$constraintBase}\") .\n                    ' CHECK (' . ($defaultValue instanceof Expression ?  $checkValue : new Expression($checkValue)) . ')';\n            }\n\n            if ($type->isUnique()) {\n                $sqlAfter[] = \"ALTER TABLE {$tableName} ADD CONSTRAINT \" . $this->db->quoteColumnName(\"UQ_{$constraintBase}\") . \" UNIQUE ({$columnName})\";\n            }\n        }\n\n        return 'ALTER TABLE ' . $tableName . ' ALTER COLUMN '\n            . $columnName . ' '\n            . $this->getColumnType($type) . \"\\n\"\n            . implode(\"\\n\", $sqlAfter);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function addDefaultValue($name, $table, $column, $value)\n    {\n        return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' ADD CONSTRAINT '\n            . $this->db->quoteColumnName($name) . ' DEFAULT ' . $this->db->quoteValue($value) . ' FOR '\n            . $this->db->quoteColumnName($column);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function dropDefaultValue($name, $table)\n    {\n        return 'ALTER TABLE ' . $this->db->quoteTableName($table)\n            . ' DROP CONSTRAINT ' . $this->db->quoteColumnName($name);\n    }\n\n    /**\n     * Creates a SQL statement for resetting the sequence value of a table's primary key.\n     * The sequence will be reset such that the primary key of the next new row inserted\n     * will have the specified value or 1.\n     * @param string $tableName the name of the table whose primary key sequence will be reset\n     * @param mixed $value the value for the primary key of the next new row inserted. If this is not set,\n     * the next new row's primary key will have a value 1.\n     * @return string the SQL statement for resetting sequence\n     * @throws InvalidArgumentException if the table does not exist or there is no sequence associated with the table.\n     */\n    public function resetSequence($tableName, $value = null)\n    {\n        $table = $this->db->getTableSchema($tableName);\n        if ($table !== null && $table->sequenceName !== null) {\n            $tableName = $this->db->quoteTableName($tableName);\n\n            if ($value === null || $value === 1) {\n                $key = $this->db->quoteColumnName(reset($table->primaryKey));\n                $subSql = (new Query())\n                    ->select('last_value')\n                    ->from('sys.identity_columns')\n                    ->where(['object_id' => new Expression(\"OBJECT_ID('{$tableName}')\")])\n                    ->andWhere(['IS NOT', 'last_value', null])\n                    ->createCommand($this->db)\n                    ->getRawSql();\n                $sql = \"SELECT COALESCE(MAX({$key}), CASE WHEN EXISTS({$subSql}) THEN 0 ELSE 1 END) FROM {$tableName}\";\n                $value = $this->db->createCommand($sql)->queryScalar();\n            } else {\n                $value = (int) $value;\n            }\n\n            return \"DBCC CHECKIDENT ('{$tableName}', RESEED, {$value})\";\n        } elseif ($table === null) {\n            throw new InvalidArgumentException(\"Table not found: $tableName\");\n        }\n\n        throw new InvalidArgumentException(\"There is not sequence associated with table '$tableName'.\");\n    }\n\n    /**\n     * Builds a SQL statement for enabling or disabling integrity check.\n     * @param bool $check whether to turn on or off the integrity check.\n     * @param string $schema the schema of the tables.\n     * @param string $table the table name.\n     * @return string the SQL statement for checking integrity\n     */\n    public function checkIntegrity($check = true, $schema = '', $table = '')\n    {\n        /** @var Schema $dbSchema */\n        $dbSchema = $this->db->getSchema();\n        $enable = $check ? 'CHECK' : 'NOCHECK';\n        $schema = $schema ?: $dbSchema->defaultSchema;\n        $tableNames = $this->db->getTableSchema($table) ? [$table] : $dbSchema->getTableNames($schema);\n        $viewNames = $dbSchema->getViewNames($schema);\n        $tableNames = array_diff($tableNames, $viewNames);\n        $command = '';\n\n        foreach ($tableNames as $tableName) {\n            $tableName = $this->db->quoteTableName(\"{$schema}.{$tableName}\");\n            $command .= \"ALTER TABLE $tableName $enable CONSTRAINT ALL; \";\n        }\n\n        return $command;\n    }\n\n     /**\n      * Builds a SQL command for adding or updating a comment to a table or a column. The command built will check if a comment\n      * already exists. If so, it will be updated, otherwise, it will be added.\n      *\n      * @param string $comment the text of the comment to be added. The comment will be properly quoted by the method.\n      * @param string $table the table to be commented or whose column is to be commented. The table name will be\n      * properly quoted by the method.\n      * @param string|null $column optional. The name of the column to be commented. If empty, the command will add the\n      * comment to the table instead. The column name will be properly quoted by the method.\n      * @return string the SQL statement for adding a comment.\n      * @throws InvalidArgumentException if the table does not exist.\n      * @since 2.0.24\n      */\n    protected function buildAddCommentSql($comment, $table, $column = null)\n    {\n        $tableSchema = $this->db->schema->getTableSchema($table);\n\n        if ($tableSchema === null) {\n            throw new InvalidArgumentException(\"Table not found: $table\");\n        }\n\n        $schemaName = $tableSchema->schemaName ? \"N'\" . $tableSchema->schemaName . \"'\" : 'SCHEMA_NAME()';\n        $tableName = 'N' . $this->db->quoteValue($tableSchema->name);\n        $columnName = $column ? 'N' . $this->db->quoteValue($column) : null;\n        $comment = 'N' . $this->db->quoteValue($comment);\n\n        $functionParams = \"\n            @name = N'MS_description',\n            @value = $comment,\n            @level0type = N'SCHEMA', @level0name = $schemaName,\n            @level1type = N'TABLE', @level1name = $tableName\"\n            . ($column ? \", @level2type = N'COLUMN', @level2name = $columnName\" : '') . ';';\n\n        return \"\n            IF NOT EXISTS (\n                    SELECT 1\n                    FROM fn_listextendedproperty (\n                        N'MS_description',\n                        'SCHEMA', $schemaName,\n                        'TABLE', $tableName,\n                        \" . ($column ? \"'COLUMN', $columnName \" : ' DEFAULT, DEFAULT ') . \"\n                    )\n            )\n                EXEC sys.sp_addextendedproperty $functionParams\n            ELSE\n                EXEC sys.sp_updateextendedproperty $functionParams\n        \";\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.8\n     */\n    public function addCommentOnColumn($table, $column, $comment)\n    {\n        return $this->buildAddCommentSql($comment, $table, $column);\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.8\n     */\n    public function addCommentOnTable($table, $comment)\n    {\n        return $this->buildAddCommentSql($comment, $table);\n    }\n\n    /**\n     * Builds a SQL command for removing a comment from a table or a column. The command built will check if a comment\n     * already exists before trying to perform the removal.\n     *\n     * @param string $table the table that will have the comment removed or whose column will have the comment removed.\n     * The table name will be properly quoted by the method.\n     * @param string|null $column optional. The name of the column whose comment will be removed. If empty, the command\n     * will remove the comment from the table instead. The column name will be properly quoted by the method.\n     * @return string the SQL statement for removing the comment.\n     * @throws InvalidArgumentException if the table does not exist.\n     * @since 2.0.24\n     */\n    protected function buildRemoveCommentSql($table, $column = null)\n    {\n        $tableSchema = $this->db->schema->getTableSchema($table);\n\n        if ($tableSchema === null) {\n            throw new InvalidArgumentException(\"Table not found: $table\");\n        }\n\n        $schemaName = $tableSchema->schemaName ? \"N'\" . $tableSchema->schemaName . \"'\" : 'SCHEMA_NAME()';\n        $tableName = 'N' . $this->db->quoteValue($tableSchema->name);\n        $columnName = $column ? 'N' . $this->db->quoteValue($column) : null;\n\n        return \"\n            IF EXISTS (\n                    SELECT 1\n                    FROM fn_listextendedproperty (\n                        N'MS_description',\n                        'SCHEMA', $schemaName,\n                        'TABLE', $tableName,\n                        \" . ($column ? \"'COLUMN', $columnName \" : ' DEFAULT, DEFAULT ') . \"\n                    )\n            )\n                EXEC sys.sp_dropextendedproperty\n                    @name = N'MS_description',\n                    @level0type = N'SCHEMA', @level0name = $schemaName,\n                    @level1type = N'TABLE', @level1name = $tableName\"\n                    . ($column ? \", @level2type = N'COLUMN', @level2name = $columnName\" : '') . ';';\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.8\n     */\n    public function dropCommentFromColumn($table, $column)\n    {\n        return $this->buildRemoveCommentSql($table, $column);\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.8\n     */\n    public function dropCommentFromTable($table)\n    {\n        return $this->buildRemoveCommentSql($table);\n    }\n\n    /**\n     * Returns an array of column names given model name.\n     *\n     * @param string|null $modelClass name of the model class\n     * @return array|null array of column names\n     */\n    protected function getAllColumnNames($modelClass = null)\n    {\n        if (!$modelClass) {\n            return null;\n        }\n        /** @var \\yii\\db\\ActiveRecord $modelClass */\n        $schema = $modelClass::getTableSchema();\n        return array_keys($schema->columns);\n    }\n\n    /**\n     * @return bool whether the version of the MSSQL being used is older than 2012.\n     * @throws \\yii\\base\\InvalidConfigException\n     * @throws \\yii\\db\\Exception\n     * @deprecated 2.0.14 Use [[Schema::getServerVersion]] with [[\\version_compare()]].\n     */\n    protected function isOldMssql()\n    {\n        return version_compare($this->db->getSchema()->getServerVersion(), '11', '<');\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.8\n     */\n    public function selectExists($rawSql)\n    {\n        return 'SELECT CASE WHEN EXISTS(' . $rawSql . ') THEN 1 ELSE 0 END';\n    }\n\n    /**\n     * Normalizes data to be saved into the table, performing extra preparations and type converting, if necessary.\n     * @param string $table the table that data will be saved into.\n     * @param array $columns the column data (name => value) to be saved into the table.\n     * @return array normalized columns\n     */\n    private function normalizeTableRowData($table, $columns, &$params)\n    {\n        if (($tableSchema = $this->db->getSchema()->getTableSchema($table)) !== null) {\n            $columnSchemas = $tableSchema->columns;\n            foreach ($columns as $name => $value) {\n                // @see https://github.com/yiisoft/yii2/issues/12599\n                if (isset($columnSchemas[$name]) && $columnSchemas[$name]->type === Schema::TYPE_BINARY && $columnSchemas[$name]->dbType === 'varbinary' && (is_string($value))) {\n                    // @see https://github.com/yiisoft/yii2/issues/12599\n                    $columns[$name] = new Expression('CONVERT(VARBINARY(MAX), ' . ('0x' . bin2hex($value)) . ')');\n                }\n            }\n        }\n\n        return $columns;\n    }\n\n    /**\n     * {@inheritdoc}\n     * Added OUTPUT construction for getting inserted data (for SQL Server 2005 or later)\n     * OUTPUT clause - The OUTPUT clause is new to SQL Server 2005 and has the ability to access\n     * the INSERTED and DELETED tables as is the case with a trigger.\n     */\n    public function insert($table, $columns, &$params)\n    {\n        $columns = $this->normalizeTableRowData($table, $columns, $params);\n\n        $version2005orLater = version_compare($this->db->getSchema()->getServerVersion(), '9', '>=');\n\n        list($names, $placeholders, $values, $params) = $this->prepareInsertValues($table, $columns, $params);\n        $cols = [];\n        $outputColumns = [];\n        if ($version2005orLater) {\n            /** @var TableSchema $schema */\n            $schema = $this->db->getTableSchema($table);\n            foreach ($schema->columns as $column) {\n                if ($column->isComputed) {\n                    continue;\n                }\n\n                $dbType = $column->dbType;\n                if (in_array($dbType, ['varchar', 'nvarchar', 'binary', 'varbinary'])) {\n                    $dbType .= '(MAX)';\n                } elseif (in_array($dbType, ['char',  'nchar'])) {\n                    $dbType .= \"($column->size)\";\n                }\n\n                if ($column->dbType === Schema::TYPE_TIMESTAMP) {\n                    $dbType = $column->allowNull ? 'varbinary(8)' : 'binary(8)';\n                }\n\n                $quoteColumnName = $this->db->quoteColumnName($column->name);\n                $cols[] = $quoteColumnName . ' ' . $dbType . ' ' . ($column->allowNull ? 'NULL' : '');\n                $outputColumns[] = 'INSERTED.' . $quoteColumnName;\n            }\n        }\n\n        $countColumns = count($outputColumns);\n\n        $sql = 'INSERT INTO ' . $this->db->quoteTableName($table)\n            . (!empty($names) ? ' (' . implode(', ', $names) . ')' : '')\n            . (($version2005orLater && $countColumns) ? ' OUTPUT ' . implode(',', $outputColumns) . ' INTO @temporary_inserted' : '')\n            . (!empty($placeholders) ? ' VALUES (' . implode(', ', $placeholders) . ')' : $values);\n\n        if ($version2005orLater && $countColumns) {\n            $sql = 'SET NOCOUNT ON;DECLARE @temporary_inserted TABLE (' . implode(', ', $cols) . ');' . $sql .\n                ';SELECT * FROM @temporary_inserted';\n        }\n\n        return $sql;\n    }\n\n    /**\n     * {@inheritdoc}\n     * @see https://docs.microsoft.com/en-us/sql/t-sql/statements/merge-transact-sql\n     * @see https://weblogs.sqlteam.com/dang/2009/01/31/upsert-race-condition-with-merge/\n     */\n    public function upsert($table, $insertColumns, $updateColumns, &$params)\n    {\n        $insertColumns = $this->normalizeTableRowData($table, $insertColumns, $params);\n\n        list($uniqueNames, $insertNames, $updateNames) = $this->prepareUpsertColumns($table, $insertColumns, $updateColumns, $constraints);\n        if (empty($uniqueNames)) {\n            return $this->insert($table, $insertColumns, $params);\n        }\n        if ($updateNames === []) {\n            // there are no columns to update\n            $updateColumns = false;\n        }\n\n        $onCondition = ['or'];\n        $quotedTableName = $this->db->quoteTableName($table);\n        foreach ($constraints as $constraint) {\n            $constraintCondition = ['and'];\n            foreach ($constraint->columnNames as $name) {\n                $quotedName = $this->db->quoteColumnName($name);\n                $constraintCondition[] = \"$quotedTableName.$quotedName=[EXCLUDED].$quotedName\";\n            }\n            $onCondition[] = $constraintCondition;\n        }\n        $on = $this->buildCondition($onCondition, $params);\n        list(, $placeholders, $values, $params) = $this->prepareInsertValues($table, $insertColumns, $params);\n\n        /**\n         * Fix number of select query params for old MSSQL version that does not support offset correctly.\n         * @see QueryBuilder::oldBuildOrderByAndLimit\n         */\n        $insertNamesUsing = $insertNames;\n        if (strstr($values, 'rowNum = ROW_NUMBER()') !== false) {\n            $insertNamesUsing = array_merge(['[rowNum]'], $insertNames);\n        }\n\n        $mergeSql = 'MERGE ' . $this->db->quoteTableName($table) . ' WITH (HOLDLOCK) '\n            . 'USING (' . (!empty($placeholders) ? 'VALUES (' . implode(', ', $placeholders) . ')' : ltrim($values, ' ')) . ') AS [EXCLUDED] (' . implode(', ', $insertNamesUsing) . ') '\n            . \"ON ($on)\";\n        $insertValues = [];\n        foreach ($insertNames as $name) {\n            $quotedName = $this->db->quoteColumnName($name);\n            if (strrpos($quotedName, '.') === false) {\n                $quotedName = '[EXCLUDED].' . $quotedName;\n            }\n            $insertValues[] = $quotedName;\n        }\n        $insertSql = 'INSERT (' . implode(', ', $insertNames) . ')'\n            . ' VALUES (' . implode(', ', $insertValues) . ')';\n        if ($updateColumns === false) {\n            return \"$mergeSql WHEN NOT MATCHED THEN $insertSql;\";\n        }\n\n        if ($updateColumns === true) {\n            $updateColumns = [];\n            foreach ($updateNames as $name) {\n                $quotedName = $this->db->quoteColumnName($name);\n                if (strrpos($quotedName, '.') === false) {\n                    $quotedName = '[EXCLUDED].' . $quotedName;\n                }\n                $updateColumns[$name] = new Expression($quotedName);\n            }\n        }\n        $updateColumns = $this->normalizeTableRowData($table, $updateColumns, $params);\n\n        list($updates, $params) = $this->prepareUpdateSets($table, $updateColumns, $params);\n        $updateSql = 'UPDATE SET ' . implode(', ', $updates);\n        return \"$mergeSql WHEN MATCHED THEN $updateSql WHEN NOT MATCHED THEN $insertSql;\";\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function update($table, $columns, $condition, &$params)\n    {\n        return parent::update($table, $this->normalizeTableRowData($table, $columns, $params), $condition, $params);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getColumnType($type)\n    {\n        $columnType = parent::getColumnType($type);\n        // remove unsupported keywords\n        $columnType = preg_replace(\"/\\s*comment '.*'/i\", '', $columnType);\n        $columnType = preg_replace('/ first$/i', '', $columnType);\n\n        return $columnType;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function extractAlias($table)\n    {\n        if (preg_match('/^\\[.*\\]$/', $table)) {\n            return false;\n        }\n\n        return parent::extractAlias($table);\n    }\n\n    /**\n     * Builds a SQL statement for dropping constraints for column of table.\n     *\n     * @param string $table the table whose constraint is to be dropped. The name will be properly quoted by the method.\n     * @param string $column the column whose constraint is to be dropped. The name will be properly quoted by the method.\n     * @param string $type type of constraint, leave empty for all type of constraints(for example: D - default, 'UQ' - unique, 'C' - check)\n     * @see https://docs.microsoft.com/sql/relational-databases/system-catalog-views/sys-objects-transact-sql\n     * @return string the DROP CONSTRAINTS SQL\n     */\n    private function dropConstraintsForColumn($table, $column, $type = '')\n    {\n        return \"DECLARE @tableName VARCHAR(MAX) = '\" . $this->db->quoteTableName($table) . \"'\nDECLARE @columnName VARCHAR(MAX) = '{$column}'\n\nWHILE 1=1 BEGIN\n    DECLARE @constraintName NVARCHAR(128)\n    SET @constraintName = (SELECT TOP 1 OBJECT_NAME(cons.[object_id])\n        FROM (\n            SELECT sc.[constid] object_id\n            FROM [sys].[sysconstraints] sc\n            JOIN [sys].[columns] c ON c.[object_id]=sc.[id] AND c.[column_id]=sc.[colid] AND c.[name]=@columnName\n            WHERE sc.[id] = OBJECT_ID(@tableName)\n            UNION\n            SELECT object_id(i.[name]) FROM [sys].[indexes] i\n            JOIN [sys].[columns] c ON c.[object_id]=i.[object_id] AND c.[name]=@columnName\n            JOIN [sys].[index_columns] ic ON ic.[object_id]=i.[object_id] AND i.[index_id]=ic.[index_id] AND c.[column_id]=ic.[column_id]\n            WHERE i.[is_unique_constraint]=1 and i.[object_id]=OBJECT_ID(@tableName)\n        ) cons\n        JOIN [sys].[objects] so ON so.[object_id]=cons.[object_id]\n        \" . (!empty($type) ? \" WHERE so.[type]='{$type}'\" : '') . \")\n    IF @constraintName IS NULL BREAK\n    EXEC (N'ALTER TABLE ' + @tableName + ' DROP CONSTRAINT [' + @constraintName + ']')\nEND\";\n    }\n\n    /**\n     * Drop all constraints before column delete\n     * {@inheritdoc}\n     */\n    public function dropColumn($table, $column)\n    {\n        return $this->dropConstraintsForColumn($table, $column) . \"\\nALTER TABLE \" . $this->db->quoteTableName($table)\n            . ' DROP COLUMN ' . $this->db->quoteColumnName($column);\n    }\n}\n"
  },
  {
    "path": "framework/db/mssql/Schema.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\mssql;\n\nuse Yii;\nuse yii\\db\\CheckConstraint;\nuse yii\\db\\Constraint;\nuse yii\\db\\ConstraintFinderInterface;\nuse yii\\db\\ConstraintFinderTrait;\nuse yii\\db\\DefaultValueConstraint;\nuse yii\\db\\ForeignKeyConstraint;\nuse yii\\db\\IndexConstraint;\nuse yii\\db\\ViewFinderTrait;\nuse yii\\helpers\\ArrayHelper;\nuse yii\\db\\Schema as BaseSchema;\n\n/**\n * Schema is the class for retrieving metadata from MS SQL Server databases (version 2008 and above).\n *\n * @author Timur Ruziev <resurtm@gmail.com>\n * @since 2.0\n *\n * @template T of ColumnSchema = ColumnSchema\n * @extends BaseSchema<T>\n */\nclass Schema extends BaseSchema implements ConstraintFinderInterface\n{\n    use ViewFinderTrait;\n    use ConstraintFinderTrait;\n\n    /**\n     * {@inheritdoc}\n     */\n    public $columnSchemaClass = 'yii\\db\\mssql\\ColumnSchema';\n    /**\n     * @var string the default schema used for the current session.\n     */\n    public $defaultSchema = 'dbo';\n    /**\n     * @var array mapping from physical column types (keys) to abstract column types (values)\n     */\n    public $typeMap = [\n        // exact numbers\n        'bigint' => self::TYPE_BIGINT,\n        'numeric' => self::TYPE_DECIMAL,\n        'bit' => self::TYPE_SMALLINT,\n        'smallint' => self::TYPE_SMALLINT,\n        'decimal' => self::TYPE_DECIMAL,\n        'smallmoney' => self::TYPE_MONEY,\n        'int' => self::TYPE_INTEGER,\n        'tinyint' => self::TYPE_TINYINT,\n        'money' => self::TYPE_MONEY,\n        // approximate numbers\n        'float' => self::TYPE_FLOAT,\n        'double' => self::TYPE_DOUBLE,\n        'real' => self::TYPE_FLOAT,\n        // date and time\n        'date' => self::TYPE_DATE,\n        'datetimeoffset' => self::TYPE_DATETIME,\n        'datetime2' => self::TYPE_DATETIME,\n        'smalldatetime' => self::TYPE_DATETIME,\n        'datetime' => self::TYPE_DATETIME,\n        'time' => self::TYPE_TIME,\n        // character strings\n        'char' => self::TYPE_CHAR,\n        'varchar' => self::TYPE_STRING,\n        'text' => self::TYPE_TEXT,\n        // unicode character strings\n        'nchar' => self::TYPE_CHAR,\n        'nvarchar' => self::TYPE_STRING,\n        'ntext' => self::TYPE_TEXT,\n        // binary strings\n        'binary' => self::TYPE_BINARY,\n        'varbinary' => self::TYPE_BINARY,\n        'image' => self::TYPE_BINARY,\n        // other data types\n        // 'cursor' type cannot be used with tables\n        'timestamp' => self::TYPE_TIMESTAMP,\n        'hierarchyid' => self::TYPE_STRING,\n        'uniqueidentifier' => self::TYPE_STRING,\n        'sql_variant' => self::TYPE_STRING,\n        'xml' => self::TYPE_STRING,\n        'table' => self::TYPE_STRING,\n    ];\n\n    /**\n     * {@inheritdoc}\n     */\n    protected $tableQuoteCharacter = ['[', ']'];\n    /**\n     * {@inheritdoc}\n     */\n    protected $columnQuoteCharacter = ['[', ']'];\n\n\n    /**\n     * Resolves the table name and schema name (if any).\n     * @param string $name the table name\n     * @return TableSchema resolved table, schema, etc. names.\n     */\n    protected function resolveTableName($name)\n    {\n        $resolvedName = new TableSchema();\n        $parts = $this->getTableNameParts($name);\n        $partCount = count($parts);\n        if ($partCount === 4) {\n            // server name, catalog name, schema name and table name passed\n            $resolvedName->catalogName = $parts[1];\n            $resolvedName->schemaName = $parts[2];\n            $resolvedName->name = $parts[3];\n            $resolvedName->fullName = $resolvedName->catalogName . '.' . $resolvedName->schemaName . '.' . $resolvedName->name;\n        } elseif ($partCount === 3) {\n            // catalog name, schema name and table name passed\n            $resolvedName->catalogName = $parts[0];\n            $resolvedName->schemaName = $parts[1];\n            $resolvedName->name = $parts[2];\n            $resolvedName->fullName = $resolvedName->catalogName . '.' . $resolvedName->schemaName . '.' . $resolvedName->name;\n        } elseif ($partCount === 2) {\n            // only schema name and table name passed\n            $resolvedName->schemaName = $parts[0];\n            $resolvedName->name = $parts[1];\n            $resolvedName->fullName = ($resolvedName->schemaName !== $this->defaultSchema ? $resolvedName->schemaName . '.' : '') . $resolvedName->name;\n        } else {\n            // only table name passed\n            $resolvedName->schemaName = $this->defaultSchema;\n            $resolvedName->fullName = $resolvedName->name = $parts[0];\n        }\n\n        return $resolvedName;\n    }\n\n    /**\n     * {@inheritDoc}\n     * @param string $name\n     * @return array\n     * @since 2.0.22\n     */\n    protected function getTableNameParts($name)\n    {\n        $parts = [$name];\n        preg_match_all('/([^.\\[\\]]+)|\\[([^\\[\\]]+)\\]/', $name, $matches);\n        if (isset($matches[0]) && is_array($matches[0]) && !empty($matches[0])) {\n            $parts = $matches[0];\n        }\n\n        $parts = str_replace(['[', ']'], '', $parts);\n\n        return $parts;\n    }\n\n    /**\n     * {@inheritdoc}\n     * @see https://docs.microsoft.com/en-us/sql/relational-databases/system-catalog-views/sys-database-principals-transact-sql\n     */\n    protected function findSchemaNames()\n    {\n        static $sql = <<<'SQL'\nSELECT [s].[name]\nFROM [sys].[schemas] AS [s]\nINNER JOIN [sys].[database_principals] AS [p] ON [p].[principal_id] = [s].[principal_id]\nWHERE [p].[is_fixed_role] = 0 AND [p].[sid] IS NOT NULL\nORDER BY [s].[name] ASC\nSQL;\n\n        return $this->db->createCommand($sql)->queryColumn();\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function findTableNames($schema = '')\n    {\n        if ($schema === '') {\n            $schema = $this->defaultSchema;\n        }\n\n        $sql = <<<'SQL'\nSELECT [t].[table_name]\nFROM [INFORMATION_SCHEMA].[TABLES] AS [t]\nWHERE [t].[table_schema] = :schema AND [t].[table_type] IN ('BASE TABLE', 'VIEW')\nORDER BY [t].[table_name]\nSQL;\n        return $this->db->createCommand($sql, [':schema' => $schema])->queryColumn();\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableSchema($name)\n    {\n        $table = new TableSchema();\n        $this->resolveTableNames($table, $name);\n        $this->findPrimaryKeys($table);\n        if ($this->findColumns($table)) {\n            $this->findForeignKeys($table);\n            return $table;\n        }\n\n        return null;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function getSchemaMetadata($schema, $type, $refresh)\n    {\n        $metadata = [];\n        $methodName = 'getTable' . ucfirst($type);\n        $tableNames = array_map(function ($table) {\n            return $this->quoteSimpleTableName($table);\n        }, $this->getTableNames($schema, $refresh));\n        foreach ($tableNames as $name) {\n            if ($schema !== '') {\n                $name = $schema . '.' . $name;\n            }\n            $tableMetadata = $this->$methodName($name, $refresh);\n            if ($tableMetadata !== null) {\n                $metadata[] = $tableMetadata;\n            }\n        }\n\n        return $metadata;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTablePrimaryKey($tableName)\n    {\n        return $this->loadTableConstraints($tableName, 'primaryKey');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableForeignKeys($tableName)\n    {\n        return $this->loadTableConstraints($tableName, 'foreignKeys');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableIndexes($tableName)\n    {\n        static $sql = <<<'SQL'\nSELECT\n    [i].[name] AS [name],\n    [iccol].[name] AS [column_name],\n    [i].[is_unique] AS [index_is_unique],\n    [i].[is_primary_key] AS [index_is_primary]\nFROM [sys].[indexes] AS [i]\nINNER JOIN [sys].[index_columns] AS [ic]\n    ON [ic].[object_id] = [i].[object_id] AND [ic].[index_id] = [i].[index_id]\nINNER JOIN [sys].[columns] AS [iccol]\n    ON [iccol].[object_id] = [ic].[object_id] AND [iccol].[column_id] = [ic].[column_id]\nWHERE [i].[object_id] = OBJECT_ID(:fullName)\nORDER BY [ic].[key_ordinal] ASC\nSQL;\n\n        $resolvedName = $this->resolveTableName($tableName);\n        $indexes = $this->db->createCommand($sql, [\n            ':fullName' => $resolvedName->fullName,\n        ])->queryAll();\n        $indexes = $this->normalizePdoRowKeyCase($indexes, true);\n        $indexes = ArrayHelper::index($indexes, null, 'name');\n        $result = [];\n        foreach ($indexes as $name => $index) {\n            $result[] = new IndexConstraint([\n                'isPrimary' => (bool)$index[0]['index_is_primary'],\n                'isUnique' => (bool)$index[0]['index_is_unique'],\n                'name' => $name,\n                'columnNames' => ArrayHelper::getColumn($index, 'column_name'),\n            ]);\n        }\n\n        return $result;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableUniques($tableName)\n    {\n        return $this->loadTableConstraints($tableName, 'uniques');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableChecks($tableName)\n    {\n        return $this->loadTableConstraints($tableName, 'checks');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableDefaultValues($tableName)\n    {\n        return $this->loadTableConstraints($tableName, 'defaults');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function createSavepoint($name)\n    {\n        $this->db->createCommand(\"SAVE TRANSACTION $name\")->execute();\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function releaseSavepoint($name)\n    {\n        // does nothing as MSSQL does not support this\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function rollBackSavepoint($name)\n    {\n        $this->db->createCommand(\"ROLLBACK TRANSACTION $name\")->execute();\n    }\n\n    /**\n     * Creates a query builder for the MSSQL database.\n     * @return QueryBuilder query builder interface.\n     */\n    public function createQueryBuilder()\n    {\n        return Yii::createObject(QueryBuilder::className(), [$this->db]);\n    }\n\n    /**\n     * Resolves the table name and schema name (if any).\n     * @param TableSchema $table the table metadata object\n     * @param string $name the table name\n     */\n    protected function resolveTableNames($table, $name)\n    {\n        $parts = $this->getTableNameParts($name);\n        $partCount = count($parts);\n        if ($partCount === 4) {\n            // server name, catalog name, schema name and table name passed\n            $table->catalogName = $parts[1];\n            $table->schemaName = $parts[2];\n            $table->name = $parts[3];\n            $table->fullName = $table->catalogName . '.' . $table->schemaName . '.' . $table->name;\n        } elseif ($partCount === 3) {\n            // catalog name, schema name and table name passed\n            $table->catalogName = $parts[0];\n            $table->schemaName = $parts[1];\n            $table->name = $parts[2];\n            $table->fullName = $table->catalogName . '.' . $table->schemaName . '.' . $table->name;\n        } elseif ($partCount === 2) {\n            // only schema name and table name passed\n            $table->schemaName = $parts[0];\n            $table->name = $parts[1];\n            $table->fullName = $table->schemaName !== $this->defaultSchema ? $table->schemaName . '.' . $table->name : $table->name;\n        } else {\n            // only table name passed\n            $table->schemaName = $this->defaultSchema;\n            $table->fullName = $table->name = $parts[0];\n        }\n    }\n\n    /**\n     * Loads the column information into a [[ColumnSchema]] object.\n     * @param array $info column information\n     * @return T the column schema object\n     */\n    protected function loadColumnSchema($info)\n    {\n        $isVersion2017orLater = version_compare($this->db->getSchema()->getServerVersion(), '14', '>=');\n        $column = $this->createColumnSchema();\n\n        $column->name = $info['column_name'];\n        $column->allowNull = $info['is_nullable'] === 'YES';\n        $column->dbType = $info['data_type'];\n        $column->enumValues = []; // mssql has only vague equivalents to enum\n        $column->isPrimaryKey = null; // primary key will be determined in findColumns() method\n        $column->autoIncrement = $info['is_identity'] == 1;\n        $column->isComputed = (bool)$info['is_computed'];\n        $column->unsigned = stripos($column->dbType, 'unsigned') !== false;\n        $column->comment = $info['comment'] === null ? '' : $info['comment'];\n\n        $column->type = self::TYPE_STRING;\n        if (preg_match('/^(\\w+)(?:\\(([^\\)]+)\\))?/', $column->dbType, $matches)) {\n            $type = $matches[1];\n            if (isset($this->typeMap[$type])) {\n                $column->type = $this->typeMap[$type];\n            }\n\n            if ($isVersion2017orLater && $type === 'bit') {\n                $column->type = 'boolean';\n            }\n\n            if (!empty($matches[2])) {\n                $values = explode(',', $matches[2]);\n                $column->size = $column->precision = (int) $values[0];\n\n                if (isset($values[1])) {\n                    $column->scale = (int) $values[1];\n                }\n\n                if ($isVersion2017orLater === false) {\n                    if ($column->size === 1 && ($type === 'tinyint' || $type === 'bit')) {\n                        $column->type = 'boolean';\n                    } elseif ($type === 'bit') {\n                        if ($column->size > 32) {\n                            $column->type = 'bigint';\n                        } elseif ($column->size === 32) {\n                            $column->type = 'integer';\n                        }\n                    }\n                }\n            }\n        }\n\n        $column->phpType = $this->getColumnPhpType($column);\n\n        if ($info['column_default'] === '(NULL)') {\n            $info['column_default'] = null;\n        }\n        if (!$column->isPrimaryKey && ($column->type !== 'timestamp' || $info['column_default'] !== 'CURRENT_TIMESTAMP')) {\n            $column->defaultValue = $column->defaultPhpTypecast($info['column_default']);\n        }\n\n        return $column;\n    }\n\n    /**\n     * Collects the metadata of table columns.\n     * @param TableSchema $table the table metadata\n     * @return bool whether the table exists in the database\n     */\n    protected function findColumns($table)\n    {\n        $columnsTableName = 'INFORMATION_SCHEMA.COLUMNS';\n        $whereSql = '[t1].[table_name] = ' . $this->db->quoteValue($table->name);\n        if ($table->catalogName !== null) {\n            $columnsTableName = \"{$table->catalogName}.{$columnsTableName}\";\n            $whereSql .= \" AND [t1].[table_catalog] = '{$table->catalogName}'\";\n        }\n        if ($table->schemaName !== null) {\n            $whereSql .= \" AND [t1].[table_schema] = '{$table->schemaName}'\";\n        }\n        $columnsTableName = $this->quoteTableName($columnsTableName);\n\n        $sql = <<<SQL\nSELECT\n [t1].[column_name],\n [t1].[is_nullable],\n CASE WHEN [t1].[data_type] IN ('char','varchar','nchar','nvarchar','binary','varbinary') THEN\n    CASE WHEN [t1].[character_maximum_length] = NULL OR [t1].[character_maximum_length] = -1 THEN\n        [t1].[data_type]\n    ELSE\n        [t1].[data_type] + '(' + LTRIM(RTRIM(CONVERT(CHAR,[t1].[character_maximum_length]))) + ')'\n    END\n ELSE\n    [t1].[data_type]\n END AS 'data_type',\n [t1].[column_default],\n COLUMNPROPERTY(OBJECT_ID([t1].[table_schema] + '.' + [t1].[table_name]), [t1].[column_name], 'IsIdentity') AS is_identity,\n COLUMNPROPERTY(OBJECT_ID([t1].[table_schema] + '.' + [t1].[table_name]), [t1].[column_name], 'IsComputed') AS is_computed,\n (\n    SELECT CONVERT(VARCHAR, [t2].[value])\n\t\tFROM [sys].[extended_properties] AS [t2]\n\t\tWHERE\n\t\t\t[t2].[class] = 1 AND\n\t\t\t[t2].[class_desc] = 'OBJECT_OR_COLUMN' AND\n\t\t\t[t2].[name] = 'MS_Description' AND\n\t\t\t[t2].[major_id] = OBJECT_ID([t1].[TABLE_SCHEMA] + '.' + [t1].[table_name]) AND\n\t\t\t[t2].[minor_id] = COLUMNPROPERTY(OBJECT_ID([t1].[TABLE_SCHEMA] + '.' + [t1].[TABLE_NAME]), [t1].[COLUMN_NAME], 'ColumnID')\n ) as comment\nFROM {$columnsTableName} AS [t1]\nWHERE {$whereSql}\nSQL;\n\n        try {\n            $columns = $this->db->createCommand($sql)->queryAll();\n            if (empty($columns)) {\n                return false;\n            }\n        } catch (\\Exception $e) {\n            return false;\n        }\n        foreach ($columns as $column) {\n            $column = $this->loadColumnSchema($column);\n            foreach ($table->primaryKey as $primaryKey) {\n                if (strcasecmp($column->name, $primaryKey) === 0) {\n                    $column->isPrimaryKey = true;\n                    break;\n                }\n            }\n            if ($column->isPrimaryKey && $column->autoIncrement) {\n                $table->sequenceName = '';\n            }\n            $table->columns[$column->name] = $column;\n        }\n\n        return true;\n    }\n\n    /**\n     * Collects the constraint details for the given table and constraint type.\n     * @param TableSchema $table\n     * @param string $type either PRIMARY KEY or UNIQUE\n     * @return array each entry contains index_name and field_name\n     * @since 2.0.4\n     */\n    protected function findTableConstraints($table, $type)\n    {\n        $keyColumnUsageTableName = 'INFORMATION_SCHEMA.KEY_COLUMN_USAGE';\n        $tableConstraintsTableName = 'INFORMATION_SCHEMA.TABLE_CONSTRAINTS';\n        if ($table->catalogName !== null) {\n            $keyColumnUsageTableName = $table->catalogName . '.' . $keyColumnUsageTableName;\n            $tableConstraintsTableName = $table->catalogName . '.' . $tableConstraintsTableName;\n        }\n        $keyColumnUsageTableName = $this->quoteTableName($keyColumnUsageTableName);\n        $tableConstraintsTableName = $this->quoteTableName($tableConstraintsTableName);\n\n        $sql = <<<SQL\nSELECT\n    [kcu].[constraint_name] AS [index_name],\n    [kcu].[column_name] AS [field_name]\nFROM {$keyColumnUsageTableName} AS [kcu]\nLEFT JOIN {$tableConstraintsTableName} AS [tc] ON\n    [kcu].[table_schema] = [tc].[table_schema] AND\n    [kcu].[table_name] = [tc].[table_name] AND\n    [kcu].[constraint_name] = [tc].[constraint_name]\nWHERE\n    [tc].[constraint_type] = :type AND\n    [kcu].[table_name] = :tableName AND\n    [kcu].[table_schema] = :schemaName\nSQL;\n\n        return $this->db\n            ->createCommand($sql, [\n                ':tableName' => $table->name,\n                ':schemaName' => $table->schemaName,\n                ':type' => $type,\n            ])\n            ->queryAll();\n    }\n\n    /**\n     * Collects the primary key column details for the given table.\n     * @param TableSchema $table the table metadata\n     */\n    protected function findPrimaryKeys($table)\n    {\n        $result = [];\n        foreach ($this->findTableConstraints($table, 'PRIMARY KEY') as $row) {\n            $result[] = $row['field_name'];\n        }\n        $table->primaryKey = $result;\n    }\n\n    /**\n     * Collects the foreign key column details for the given table.\n     * @param TableSchema $table the table metadata\n     */\n    protected function findForeignKeys($table)\n    {\n        $object = $table->name;\n        if ($table->schemaName !== null) {\n            $object = $table->schemaName . '.' . $object;\n        }\n        if ($table->catalogName !== null) {\n            $object = $table->catalogName . '.' . $object;\n        }\n\n        // please refer to the following page for more details:\n        // http://msdn2.microsoft.com/en-us/library/aa175805(SQL.80).aspx\n        $sql = <<<'SQL'\nSELECT\n\t[fk].[name] AS [fk_name],\n\t[cp].[name] AS [fk_column_name],\n\tOBJECT_NAME([fk].[referenced_object_id]) AS [uq_table_name],\n\t[cr].[name] AS [uq_column_name]\nFROM\n\t[sys].[foreign_keys] AS [fk]\n\tINNER JOIN [sys].[foreign_key_columns] AS [fkc] ON\n\t\t[fk].[object_id] = [fkc].[constraint_object_id]\n\tINNER JOIN [sys].[columns] AS [cp] ON\n\t\t[fk].[parent_object_id] = [cp].[object_id] AND\n\t\t[fkc].[parent_column_id] = [cp].[column_id]\n\tINNER JOIN [sys].[columns] AS [cr] ON\n\t\t[fk].[referenced_object_id] = [cr].[object_id] AND\n\t\t[fkc].[referenced_column_id] = [cr].[column_id]\nWHERE\n\t[fk].[parent_object_id] = OBJECT_ID(:object)\nSQL;\n\n        $rows = $this->db->createCommand($sql, [\n            ':object' => $object,\n        ])->queryAll();\n\n        $table->foreignKeys = [];\n        foreach ($rows as $row) {\n            if (!isset($table->foreignKeys[$row['fk_name']])) {\n                $table->foreignKeys[$row['fk_name']][] = $row['uq_table_name'];\n            }\n            $table->foreignKeys[$row['fk_name']][$row['fk_column_name']] = $row['uq_column_name'];\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function findViewNames($schema = '')\n    {\n        if ($schema === '') {\n            $schema = $this->defaultSchema;\n        }\n\n        $sql = <<<'SQL'\nSELECT [t].[table_name]\nFROM [INFORMATION_SCHEMA].[TABLES] AS [t]\nWHERE [t].[table_schema] = :schema AND [t].[table_type] = 'VIEW'\nORDER BY [t].[table_name]\nSQL;\n\n        return $this->db->createCommand($sql, [':schema' => $schema])->queryColumn();\n    }\n\n    /**\n     * Returns all unique indexes for the given table.\n     *\n     * Each array element is of the following structure:\n     *\n     * ```\n     * [\n     *     'IndexName1' => ['col1' [, ...]],\n     *     'IndexName2' => ['col2' [, ...]],\n     * ]\n     * ```\n     *\n     * @param TableSchema $table the table metadata\n     * @return array all unique indexes for the given table.\n     * @since 2.0.4\n     */\n    public function findUniqueIndexes($table)\n    {\n        $result = [];\n        foreach ($this->findTableConstraints($table, 'UNIQUE') as $row) {\n            $result[$row['index_name']][] = $row['field_name'];\n        }\n\n        return $result;\n    }\n\n    /**\n     * Loads multiple types of constraints and returns the specified ones.\n     * @param string $tableName table name.\n     * @param string $returnType return type:\n     * - primaryKey\n     * - foreignKeys\n     * - uniques\n     * - checks\n     * - defaults\n     * @return mixed constraints.\n     */\n    private function loadTableConstraints($tableName, $returnType)\n    {\n        static $sql = <<<'SQL'\nSELECT\n    [o].[name] AS [name],\n    COALESCE([ccol].[name], [dcol].[name], [fccol].[name], [kiccol].[name]) AS [column_name],\n    RTRIM([o].[type]) AS [type],\n    OBJECT_SCHEMA_NAME([f].[referenced_object_id]) AS [foreign_table_schema],\n    OBJECT_NAME([f].[referenced_object_id]) AS [foreign_table_name],\n    [ffccol].[name] AS [foreign_column_name],\n    [f].[update_referential_action_desc] AS [on_update],\n    [f].[delete_referential_action_desc] AS [on_delete],\n    [c].[definition] AS [check_expr],\n    [d].[definition] AS [default_expr]\nFROM (SELECT OBJECT_ID(:fullName) AS [object_id]) AS [t]\nINNER JOIN [sys].[objects] AS [o]\n    ON [o].[parent_object_id] = [t].[object_id] AND [o].[type] IN ('PK', 'UQ', 'C', 'D', 'F')\nLEFT JOIN [sys].[check_constraints] AS [c]\n    ON [c].[object_id] = [o].[object_id]\nLEFT JOIN [sys].[columns] AS [ccol]\n    ON [ccol].[object_id] = [c].[parent_object_id] AND [ccol].[column_id] = [c].[parent_column_id]\nLEFT JOIN [sys].[default_constraints] AS [d]\n    ON [d].[object_id] = [o].[object_id]\nLEFT JOIN [sys].[columns] AS [dcol]\n    ON [dcol].[object_id] = [d].[parent_object_id] AND [dcol].[column_id] = [d].[parent_column_id]\nLEFT JOIN [sys].[key_constraints] AS [k]\n    ON [k].[object_id] = [o].[object_id]\nLEFT JOIN [sys].[index_columns] AS [kic]\n    ON [kic].[object_id] = [k].[parent_object_id] AND [kic].[index_id] = [k].[unique_index_id]\nLEFT JOIN [sys].[columns] AS [kiccol]\n    ON [kiccol].[object_id] = [kic].[object_id] AND [kiccol].[column_id] = [kic].[column_id]\nLEFT JOIN [sys].[foreign_keys] AS [f]\n    ON [f].[object_id] = [o].[object_id]\nLEFT JOIN [sys].[foreign_key_columns] AS [fc]\n    ON [fc].[constraint_object_id] = [o].[object_id]\nLEFT JOIN [sys].[columns] AS [fccol]\n    ON [fccol].[object_id] = [fc].[parent_object_id] AND [fccol].[column_id] = [fc].[parent_column_id]\nLEFT JOIN [sys].[columns] AS [ffccol]\n    ON [ffccol].[object_id] = [fc].[referenced_object_id] AND [ffccol].[column_id] = [fc].[referenced_column_id]\nORDER BY [kic].[key_ordinal] ASC, [fc].[constraint_column_id] ASC\nSQL;\n\n        $resolvedName = $this->resolveTableName($tableName);\n        $constraints = $this->db->createCommand($sql, [\n            ':fullName' => $resolvedName->fullName,\n        ])->queryAll();\n        $constraints = $this->normalizePdoRowKeyCase($constraints, true);\n        $constraints = ArrayHelper::index($constraints, null, ['type', 'name']);\n        $result = [\n            'primaryKey' => null,\n            'foreignKeys' => [],\n            'uniques' => [],\n            'checks' => [],\n            'defaults' => [],\n        ];\n        foreach ($constraints as $type => $names) {\n            foreach ($names as $name => $constraint) {\n                switch ($type) {\n                    case 'PK':\n                        $result['primaryKey'] = new Constraint([\n                            'name' => $name,\n                            'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),\n                        ]);\n                        break;\n                    case 'F':\n                        $result['foreignKeys'][] = new ForeignKeyConstraint([\n                            'name' => $name,\n                            'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),\n                            'foreignSchemaName' => $constraint[0]['foreign_table_schema'],\n                            'foreignTableName' => $constraint[0]['foreign_table_name'],\n                            'foreignColumnNames' => ArrayHelper::getColumn($constraint, 'foreign_column_name'),\n                            'onDelete' => str_replace('_', '', $constraint[0]['on_delete']),\n                            'onUpdate' => str_replace('_', '', $constraint[0]['on_update']),\n                        ]);\n                        break;\n                    case 'UQ':\n                        $result['uniques'][] = new Constraint([\n                            'name' => $name,\n                            'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),\n                        ]);\n                        break;\n                    case 'C':\n                        $result['checks'][] = new CheckConstraint([\n                            'name' => $name,\n                            'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),\n                            'expression' => $constraint[0]['check_expr'],\n                        ]);\n                        break;\n                    case 'D':\n                        $result['defaults'][] = new DefaultValueConstraint([\n                            'name' => $name,\n                            'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),\n                            'value' => $constraint[0]['default_expr'],\n                        ]);\n                        break;\n                }\n            }\n        }\n        foreach ($result as $type => $data) {\n            $this->setTableMetadata($tableName, $type, $data);\n        }\n\n        return $result[$returnType];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function quoteColumnName($name)\n    {\n        if (preg_match('/^\\[.*\\]$/', $name)) {\n            return $name;\n        }\n\n        return parent::quoteColumnName($name);\n    }\n\n    /**\n     * Retrieving inserted data from a primary key request of type uniqueidentifier (for SQL Server 2005 or later)\n     * {@inheritdoc}\n     */\n    public function insert($table, $columns)\n    {\n        $command = $this->db->createCommand()->insert($table, $columns);\n        if (!$command->execute()) {\n            return false;\n        }\n\n        $isVersion2005orLater = version_compare($this->db->getSchema()->getServerVersion(), '9', '>=');\n        $inserted = $isVersion2005orLater ? $command->pdoStatement->fetch() : [];\n\n        $tableSchema = $this->getTableSchema($table);\n        $result = [];\n        foreach ($tableSchema->primaryKey as $name) {\n            // @see https://github.com/yiisoft/yii2/issues/13828 & https://github.com/yiisoft/yii2/issues/17474\n            if (isset($inserted[$name])) {\n                $result[$name] = $inserted[$name];\n            } elseif ($tableSchema->columns[$name]->autoIncrement) {\n                // for a version earlier than 2005\n                $result[$name] = $this->getLastInsertID($tableSchema->sequenceName);\n            } elseif (isset($columns[$name])) {\n                $result[$name] = $columns[$name];\n            } else {\n                $result[$name] = $tableSchema->columns[$name]->defaultValue;\n            }\n        }\n\n        return $result;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function createColumnSchemaBuilder($type, $length = null)\n    {\n        return Yii::createObject(ColumnSchemaBuilder::className(), [$type, $length, $this->db]);\n    }\n}\n"
  },
  {
    "path": "framework/db/mssql/SqlsrvPDO.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\mssql;\n\n/**\n * This is an extension of the default PDO class of SQLSRV driver.\n * It provides workarounds for improperly implemented functionalities of the SQLSRV driver.\n *\n * @author Timur Ruziev <resurtm@gmail.com>\n * @since 2.0\n */\nclass SqlsrvPDO extends \\PDO\n{\n    /**\n     * Returns value of the last inserted ID.\n     *\n     * SQLSRV driver implements [[PDO::lastInsertId()]] method but with a single peculiarity:\n     * when `$sequence` value is a null or an empty string it returns an empty string.\n     * But when parameter is not specified it works as expected and returns actual\n     * last inserted ID (like the other PDO drivers).\n     * @param string|null $sequence the sequence name. Defaults to null.\n     * @return string|false last inserted ID value.\n     */\n    #[\\ReturnTypeWillChange]\n    public function lastInsertId($sequence = null)\n    {\n        return !$sequence ? parent::lastInsertId() : parent::lastInsertId($sequence);\n    }\n}\n"
  },
  {
    "path": "framework/db/mssql/TableSchema.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\mssql;\n\n/**\n * TableSchema represents the metadata of a database table.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass TableSchema extends \\yii\\db\\TableSchema\n{\n    /**\n     * @var string|null name of the catalog (database) that this table belongs to.\n     * Defaults to null, meaning no catalog (or the current database).\n     */\n    public $catalogName;\n}\n"
  },
  {
    "path": "framework/db/mssql/conditions/InConditionBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\mssql\\conditions;\n\nuse yii\\base\\NotSupportedException;\nuse yii\\db\\Expression;\n\n/**\n * {@inheritdoc}\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n */\nclass InConditionBuilder extends \\yii\\db\\conditions\\InConditionBuilder\n{\n    /**\n     * {@inheritdoc}\n     * @throws NotSupportedException if `$columns` is an array\n     */\n    protected function buildSubqueryInCondition($operator, $columns, $values, &$params)\n    {\n        if (is_array($columns)) {\n            throw new NotSupportedException(__METHOD__ . ' is not supported by MSSQL.');\n        }\n\n        return parent::buildSubqueryInCondition($operator, $columns, $values, $params);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function buildCompositeInCondition($operator, $columns, $values, &$params)\n    {\n        $quotedColumns = [];\n        foreach ($columns as $i => $column) {\n            if ($column instanceof Expression) {\n                $column = $column->expression;\n            }\n            $quotedColumns[$i] = strpos($column, '(') === false ? $this->queryBuilder->db->quoteColumnName($column) : $column;\n        }\n        $vss = [];\n        foreach ($values as $value) {\n            $vs = [];\n            foreach ($columns as $i => $column) {\n                if ($column instanceof Expression) {\n                    $column = $column->expression;\n                }\n                if (isset($value[$column])) {\n                    $phName = $this->queryBuilder->bindParam($value[$column], $params);\n                    $vs[] = $quotedColumns[$i] . ($operator === 'IN' ? ' = ' : ' != ') . $phName;\n                } else {\n                    $vs[] = $quotedColumns[$i] . ($operator === 'IN' ? ' IS' : ' IS NOT') . ' NULL';\n                }\n            }\n            $vss[] = '(' . implode($operator === 'IN' ? ' AND ' : ' OR ', $vs) . ')';\n        }\n\n        return '(' . implode($operator === 'IN' ? ' OR ' : ' AND ', $vss) . ')';\n    }\n}\n"
  },
  {
    "path": "framework/db/mssql/conditions/LikeConditionBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\mssql\\conditions;\n\n/**\n * {@inheritdoc}\n */\nclass LikeConditionBuilder extends \\yii\\db\\conditions\\LikeConditionBuilder\n{\n    /**\n     * {@inheritdoc}\n     */\n    protected $escapingReplacements = [\n        '%' => '[%]',\n        '_' => '[_]',\n        '[' => '[[]',\n        ']' => '[]]',\n        '\\\\' => '[\\\\]',\n    ];\n}\n"
  },
  {
    "path": "framework/db/mysql/ColumnSchema.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\mysql;\n\nuse yii\\db\\ExpressionInterface;\nuse yii\\db\\JsonExpression;\n\n/**\n * Class ColumnSchema for MySQL database\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14.1\n */\nclass ColumnSchema extends \\yii\\db\\ColumnSchema\n{\n    /**\n     * @var bool whether the column schema should OMIT using JSON support feature.\n     * You can use this property to make upgrade to Yii 2.0.14 easier.\n     * Default to `false`, meaning JSON support is enabled.\n     *\n     * @since 2.0.14.1\n     * @deprecated Since 2.0.14.1 and will be removed in 2.1.\n     */\n    public $disableJsonSupport = false;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function dbTypecast($value)\n    {\n        if ($value === null) {\n            return $value;\n        }\n\n        if ($value instanceof ExpressionInterface) {\n            return $value;\n        }\n\n        if (!$this->disableJsonSupport && $this->dbType === Schema::TYPE_JSON) {\n            return new JsonExpression($value, $this->type);\n        }\n\n        return $this->typecast($value);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function phpTypecast($value)\n    {\n        if ($value === null) {\n            return null;\n        }\n\n        if (!$this->disableJsonSupport && $this->type === Schema::TYPE_JSON) {\n            return json_decode($value, true);\n        }\n\n        return parent::phpTypecast($value);\n    }\n}\n"
  },
  {
    "path": "framework/db/mysql/ColumnSchemaBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\mysql;\n\nuse yii\\db\\ColumnSchemaBuilder as AbstractColumnSchemaBuilder;\n\n/**\n * ColumnSchemaBuilder is the schema builder for MySQL databases.\n *\n * @author Chris Harris <chris@buckshotsoftware.com>\n * @since 2.0.8\n */\nclass ColumnSchemaBuilder extends AbstractColumnSchemaBuilder\n{\n    /**\n     * {@inheritdoc}\n     */\n    protected function buildUnsignedString()\n    {\n        return $this->isUnsigned ? ' UNSIGNED' : '';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function buildAfterString()\n    {\n        return $this->after !== null ?\n            ' AFTER ' . $this->db->quoteColumnName($this->after) :\n            '';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function buildFirstString()\n    {\n        return $this->isFirst ? ' FIRST' : '';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function buildCommentString()\n    {\n        return $this->comment !== null ? ' COMMENT ' . $this->db->quoteValue($this->comment) : '';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function __toString()\n    {\n        switch ($this->getTypeCategory()) {\n            case self::CATEGORY_PK:\n                $format = '{type}{length}{comment}{check}{append}{pos}';\n                break;\n            case self::CATEGORY_NUMERIC:\n                $format = '{type}{length}{unsigned}{notnull}{default}{unique}{comment}{append}{pos}{check}';\n                break;\n            default:\n                $format = '{type}{length}{notnull}{default}{unique}{comment}{append}{pos}{check}';\n        }\n\n        return $this->buildCompleteString($format);\n    }\n}\n"
  },
  {
    "path": "framework/db/mysql/JsonExpressionBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\mysql;\n\nuse yii\\db\\ExpressionBuilderInterface;\nuse yii\\db\\ExpressionBuilderTrait;\nuse yii\\db\\ExpressionInterface;\nuse yii\\db\\JsonExpression;\nuse yii\\db\\Query;\nuse yii\\helpers\\Json;\n\n/**\n * Class JsonExpressionBuilder builds [[JsonExpression]] for MySQL DBMS.\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n */\nclass JsonExpressionBuilder implements ExpressionBuilderInterface\n{\n    use ExpressionBuilderTrait;\n\n\n    public const PARAM_PREFIX = ':qp';\n    /**\n     * {@inheritdoc}\n     * @param JsonExpression|ExpressionInterface $expression the expression to be built\n     */\n    public function build(ExpressionInterface $expression, array &$params = [])\n    {\n        $value = $expression->getValue();\n\n        if ($value instanceof Query) {\n            list ($sql, $params) = $this->queryBuilder->build($value, $params);\n            return \"($sql)\";\n        }\n\n        $placeholder = static::PARAM_PREFIX . count($params);\n        $params[$placeholder] = Json::encode($value);\n\n        return $placeholder;\n    }\n}\n"
  },
  {
    "path": "framework/db/mysql/QueryBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\mysql;\n\nuse yii\\base\\InvalidArgumentException;\nuse yii\\caching\\CacheInterface;\nuse yii\\caching\\DbCache;\nuse yii\\db\\Exception;\nuse yii\\db\\Expression;\nuse yii\\db\\Query;\n\n/**\n * QueryBuilder is the query builder for MySQL databases.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass QueryBuilder extends \\yii\\db\\QueryBuilder\n{\n    /**\n     * @var array mapping from abstract column types (keys) to physical column types (values).\n     */\n    public $typeMap = [\n        Schema::TYPE_PK => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY',\n        Schema::TYPE_UPK => 'int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY',\n        Schema::TYPE_BIGPK => 'bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY',\n        Schema::TYPE_UBIGPK => 'bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY',\n        Schema::TYPE_CHAR => 'char(1)',\n        Schema::TYPE_STRING => 'varchar(255)',\n        Schema::TYPE_TEXT => 'text',\n        Schema::TYPE_TINYINT => 'tinyint(3)',\n        Schema::TYPE_SMALLINT => 'smallint(6)',\n        Schema::TYPE_INTEGER => 'int(11)',\n        Schema::TYPE_BIGINT => 'bigint(20)',\n        Schema::TYPE_FLOAT => 'float',\n        Schema::TYPE_DOUBLE => 'double',\n        Schema::TYPE_DECIMAL => 'decimal(10,0)',\n        Schema::TYPE_DATE => 'date',\n        Schema::TYPE_BINARY => 'blob',\n        Schema::TYPE_BOOLEAN => 'tinyint(1)',\n        Schema::TYPE_MONEY => 'decimal(19,4)',\n        Schema::TYPE_JSON => 'json'\n    ];\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n\n        $this->typeMap = array_merge($this->typeMap, $this->defaultTimeTypeMap());\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function defaultExpressionBuilders()\n    {\n        return array_merge(parent::defaultExpressionBuilders(), [\n            'yii\\db\\JsonExpression' => 'yii\\db\\mysql\\JsonExpressionBuilder',\n        ]);\n    }\n\n    /**\n     * Builds a SQL statement for renaming a column.\n     * @param string $table the table whose column is to be renamed. The name will be properly quoted by the method.\n     * @param string $oldName the old name of the column. The name will be properly quoted by the method.\n     * @param string $newName the new name of the column. The name will be properly quoted by the method.\n     * @return string the SQL statement for renaming a DB column.\n     * @throws Exception\n     */\n    public function renameColumn($table, $oldName, $newName)\n    {\n        $quotedTable = $this->db->quoteTableName($table);\n        $row = $this->db->createCommand('SHOW CREATE TABLE ' . $quotedTable)->queryOne();\n        if ($row === false) {\n            throw new Exception(\"Unable to find column '$oldName' in table '$table'.\");\n        }\n        if (isset($row['Create Table'])) {\n            $sql = $row['Create Table'];\n        } else {\n            $row = array_values($row);\n            $sql = $row[1];\n        }\n        if (preg_match_all('/^\\s*[`\"](.*?)[`\"]\\s+(.*?),?$/m', $sql, $matches)) {\n            foreach ($matches[1] as $i => $c) {\n                if ($c === $oldName) {\n                    return \"ALTER TABLE $quotedTable CHANGE \"\n                        . $this->db->quoteColumnName($oldName) . ' '\n                        . $this->db->quoteColumnName($newName) . ' '\n                        . $matches[2][$i];\n                }\n            }\n        }\n        // try to give back a SQL anyway\n        return \"ALTER TABLE $quotedTable CHANGE \"\n            . $this->db->quoteColumnName($oldName) . ' '\n            . $this->db->quoteColumnName($newName);\n    }\n\n    /**\n     * {@inheritdoc}\n     * @see https://bugs.mysql.com/bug.php?id=48875\n     */\n    public function createIndex($name, $table, $columns, $unique = false)\n    {\n        return 'ALTER TABLE '\n        . $this->db->quoteTableName($table)\n        . ($unique ? ' ADD UNIQUE INDEX ' : ' ADD INDEX ')\n        . $this->db->quoteTableName($name)\n        . ' (' . $this->buildColumns($columns) . ')';\n    }\n\n    /**\n     * Builds a SQL statement for dropping a foreign key constraint.\n     * @param string $name the name of the foreign key constraint to be dropped. The name will be properly quoted by the method.\n     * @param string $table the table whose foreign is to be dropped. The name will be properly quoted by the method.\n     * @return string the SQL statement for dropping a foreign key constraint.\n     */\n    public function dropForeignKey($name, $table)\n    {\n        return 'ALTER TABLE ' . $this->db->quoteTableName($table)\n            . ' DROP FOREIGN KEY ' . $this->db->quoteColumnName($name);\n    }\n\n    /**\n     * Builds a SQL statement for removing a primary key constraint to an existing table.\n     * @param string $name the name of the primary key constraint to be removed.\n     * @param string $table the table that the primary key constraint will be removed from.\n     * @return string the SQL statement for removing a primary key constraint from an existing table.\n     */\n    public function dropPrimaryKey($name, $table)\n    {\n        return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' DROP PRIMARY KEY';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function dropUnique($name, $table)\n    {\n        return $this->dropIndex($name, $table);\n    }\n\n    /**\n     * Creates a SQL statement for resetting the sequence value of a table's primary key.\n     * The sequence will be reset such that the primary key of the next new row inserted\n     * will have the specified value or 1.\n     * @param string $tableName the name of the table whose primary key sequence will be reset\n     * @param mixed $value the value for the primary key of the next new row inserted. If this is not set,\n     * the next new row's primary key will have a value 1.\n     * @return string the SQL statement for resetting sequence\n     * @throws InvalidArgumentException if the table does not exist or there is no sequence associated with the table.\n     */\n    public function resetSequence($tableName, $value = null)\n    {\n        $table = $this->db->getTableSchema($tableName);\n        if ($table !== null && $table->sequenceName !== null) {\n            $tableName = $this->db->quoteTableName($tableName);\n            if ($value === null) {\n                $key = reset($table->primaryKey);\n                $value = $this->db->createCommand(\"SELECT MAX(`$key`) FROM $tableName\")->queryScalar() + 1;\n            } else {\n                $value = (int) $value;\n            }\n\n            return \"ALTER TABLE $tableName AUTO_INCREMENT=$value\";\n        } elseif ($table === null) {\n            throw new InvalidArgumentException(\"Table not found: $tableName\");\n        }\n\n        throw new InvalidArgumentException(\"There is no sequence associated with table '$tableName'.\");\n    }\n\n    /**\n     * Builds a SQL statement for enabling or disabling integrity check.\n     * @param bool $check whether to turn on or off the integrity check.\n     * @param string $schema the schema of the tables. Meaningless for MySQL.\n     * @param string $table the table name. Meaningless for MySQL.\n     * @return string the SQL statement for checking integrity\n     */\n    public function checkIntegrity($check = true, $schema = '', $table = '')\n    {\n        return 'SET FOREIGN_KEY_CHECKS = ' . ($check ? 1 : 0);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function buildLimit($limit, $offset)\n    {\n        $sql = '';\n        if ($this->hasLimit($limit)) {\n            $sql = 'LIMIT ' . $limit;\n            if ($this->hasOffset($offset)) {\n                $sql .= ' OFFSET ' . $offset;\n            }\n        } elseif ($this->hasOffset($offset)) {\n            // limit is not optional in MySQL\n            // https://stackoverflow.com/questions/255517/mysql-offset-infinite-rows/271650#271650\n            // https://dev.mysql.com/doc/refman/5.7/en/select.html#idm46193796386608\n            $sql = \"LIMIT $offset, 18446744073709551615\"; // 2^64-1\n        }\n\n        return $sql;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function hasLimit($limit)\n    {\n        // In MySQL limit argument must be nonnegative integer constant\n        return ctype_digit((string) $limit);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function hasOffset($offset)\n    {\n        // In MySQL offset argument must be nonnegative integer constant\n        $offset = (string) $offset;\n        return ctype_digit($offset) && $offset !== '0';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function prepareInsertValues($table, $columns, $params = [])\n    {\n        list($names, $placeholders, $values, $params) = parent::prepareInsertValues($table, $columns, $params);\n        if (!$columns instanceof Query && empty($names)) {\n            $tableSchema = $this->db->getSchema()->getTableSchema($table);\n            if ($tableSchema !== null) {\n                if (!empty($tableSchema->primaryKey)) {\n                    $columns = $tableSchema->primaryKey;\n                    $defaultValue = 'NULL';\n                } else {\n                    $columns = [reset($tableSchema->columns)->name];\n                    $defaultValue = 'DEFAULT';\n                }\n\n                foreach ($columns as $name) {\n                    $names[] = $this->db->quoteColumnName($name);\n                    $placeholders[] = $defaultValue;\n                }\n            }\n        }\n        return [$names, $placeholders, $values, $params];\n    }\n\n    /**\n     * {@inheritdoc}\n     * @see https://downloads.mysql.com/docs/refman-5.1-en.pdf\n     */\n    public function upsert($table, $insertColumns, $updateColumns, &$params)\n    {\n        $insertSql = $this->insert($table, $insertColumns, $params);\n        list($uniqueNames, , $updateNames) = $this->prepareUpsertColumns($table, $insertColumns, $updateColumns);\n        if (empty($uniqueNames)) {\n            return $insertSql;\n        }\n        if ($updateNames === []) {\n            // there are no columns to update\n            $updateColumns = false;\n        }\n\n        if ($updateColumns === true) {\n            $updateColumns = [];\n            foreach ($updateNames as $name) {\n                $updateColumns[$name] = new Expression('VALUES(' . $this->db->quoteColumnName($name) . ')');\n            }\n        } elseif ($updateColumns === false) {\n            $name = $this->db->quoteColumnName(reset($uniqueNames));\n            $updateColumns = [$name => new Expression($this->db->quoteTableName($table) . '.' . $name)];\n        }\n        list($updates, $params) = $this->prepareUpdateSets($table, $updateColumns, $params);\n        return $insertSql . ' ON DUPLICATE KEY UPDATE ' . implode(', ', $updates);\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.8\n     */\n    public function addCommentOnColumn($table, $column, $comment)\n    {\n        // Strip existing comment which may include escaped quotes\n        $definition = trim(preg_replace(\"/COMMENT '(?:''|[^'])*'/i\", '', $this->getColumnDefinition($table, $column)));\n\n        $checkRegex = '/CHECK *(\\(([^()]|(?-2))*\\))/';\n        $check = preg_match($checkRegex, $definition, $checkMatches);\n        if ($check === 1) {\n            $definition = preg_replace($checkRegex, '', $definition);\n        }\n        $alterSql = 'ALTER TABLE ' . $this->db->quoteTableName($table)\n            . ' CHANGE ' . $this->db->quoteColumnName($column)\n            . ' ' . $this->db->quoteColumnName($column)\n            . (empty($definition) ? '' : ' ' . $definition)\n            . ' COMMENT ' . $this->db->quoteValue($comment);\n        if ($check === 1) {\n            $alterSql .= ' ' . $checkMatches[0];\n        }\n        return $alterSql;\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.8\n     */\n    public function addCommentOnTable($table, $comment)\n    {\n        return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' COMMENT ' . $this->db->quoteValue($comment);\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.8\n     */\n    public function dropCommentFromColumn($table, $column)\n    {\n        return $this->addCommentOnColumn($table, $column, '');\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.8\n     */\n    public function dropCommentFromTable($table)\n    {\n        return $this->addCommentOnTable($table, '');\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.8\n     */\n    public function selectExists($rawSql)\n    {\n        return 'SELECT EXISTS(' . $rawSql . ') AS ' . $this->db->quoteColumnName('result');\n    }\n\n    /**\n     * Gets column definition.\n     *\n     * @param string $table table name\n     * @param string $column column name\n     * @return string|null the column definition\n     * @throws Exception in case when table does not contain column\n     */\n    private function getColumnDefinition($table, $column)\n    {\n        $quotedTable = $this->db->quoteTableName($table);\n        $row = $this->db->createCommand('SHOW CREATE TABLE ' . $quotedTable)->queryOne();\n        if ($row === false) {\n            throw new Exception(\"Unable to find column '$column' in table '$table'.\");\n        }\n        if (isset($row['Create Table'])) {\n            $sql = $row['Create Table'];\n        } else {\n            $row = array_values($row);\n            $sql = $row[1];\n        }\n        if (preg_match_all('/^\\s*[`\"](.*?)[`\"]\\s+(.*?),?$/m', $sql, $matches)) {\n            foreach ($matches[1] as $i => $c) {\n                if ($c === $column) {\n                    return $matches[2][$i];\n                }\n            }\n        }\n\n        return null;\n    }\n\n    /**\n     * Checks the ability to use fractional seconds.\n     *\n     * @return bool\n     * @see https://dev.mysql.com/doc/refman/5.6/en/fractional-seconds.html\n     */\n    private function supportsFractionalSeconds()\n    {\n        // use cache to prevent opening MySQL connection\n        // https://github.com/yiisoft/yii2/issues/13749#issuecomment-481657224\n        $key = [__METHOD__, $this->db->dsn];\n        $cache = null;\n        $schemaCache = (\\Yii::$app && is_string($this->db->schemaCache)) ? \\Yii::$app->get($this->db->schemaCache, false) : $this->db->schemaCache;\n        // If the `$schemaCache` is an instance of `DbCache` we don't use it to avoid a loop\n        if ($this->db->enableSchemaCache && $schemaCache instanceof CacheInterface && !($schemaCache instanceof DbCache)) {\n            $cache = $schemaCache;\n        }\n        $version = $cache ? $cache->get($key) : null;\n        if (!$version) {\n            $version = $this->db->getSlavePdo(true)->getAttribute(\\PDO::ATTR_SERVER_VERSION);\n            if ($cache) {\n                $cache->set($key, $version, $this->db->schemaCacheDuration);\n            }\n        }\n\n        return version_compare($version, '5.6.4', '>=');\n    }\n\n    /**\n     * Returns the map for default time type.\n     * If the version of MySQL is lower than 5.6.4, then the types will be without fractional seconds,\n     * otherwise with fractional seconds.\n     *\n     * @return array\n     */\n    private function defaultTimeTypeMap()\n    {\n        $map = [\n            Schema::TYPE_DATETIME => 'datetime',\n            Schema::TYPE_TIMESTAMP => 'timestamp',\n            Schema::TYPE_TIME => 'time',\n        ];\n\n        if ($this->supportsFractionalSeconds()) {\n            $map = [\n                Schema::TYPE_DATETIME => 'datetime(0)',\n                Schema::TYPE_TIMESTAMP => 'timestamp(0)',\n                Schema::TYPE_TIME => 'time(0)',\n            ];\n        }\n\n        return $map;\n    }\n}\n"
  },
  {
    "path": "framework/db/mysql/Schema.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\mysql;\n\nuse Yii;\nuse yii\\base\\InvalidConfigException;\nuse yii\\base\\NotSupportedException;\nuse yii\\db\\CheckConstraint;\nuse yii\\db\\Constraint;\nuse yii\\db\\ConstraintFinderInterface;\nuse yii\\db\\ConstraintFinderTrait;\nuse yii\\db\\Exception;\nuse yii\\db\\Expression;\nuse yii\\db\\ForeignKeyConstraint;\nuse yii\\db\\IndexConstraint;\nuse yii\\db\\TableSchema;\nuse yii\\helpers\\ArrayHelper;\nuse yii\\db\\Schema as BaseSchema;\n\n/**\n * Schema is the class for retrieving metadata from a MySQL database (version 4.1.x and 5.x).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of ColumnSchema = ColumnSchema\n * @extends BaseSchema<T>\n */\nclass Schema extends BaseSchema implements ConstraintFinderInterface\n{\n    use ConstraintFinderTrait;\n\n    /**\n     * {@inheritdoc}\n     */\n    public $columnSchemaClass = 'yii\\db\\mysql\\ColumnSchema';\n    /**\n     * @var bool whether MySQL used is older than 5.1.\n     */\n    private $_oldMysql;\n\n\n    /**\n     * @var array mapping from physical column types (keys) to abstract column types (values)\n     */\n    public $typeMap = [\n        'tinyint' => self::TYPE_TINYINT,\n        'bool' => self::TYPE_TINYINT,\n        'boolean' => self::TYPE_TINYINT,\n        'bit' => self::TYPE_INTEGER,\n        'smallint' => self::TYPE_SMALLINT,\n        'mediumint' => self::TYPE_INTEGER,\n        'int' => self::TYPE_INTEGER,\n        'integer' => self::TYPE_INTEGER,\n        'bigint' => self::TYPE_BIGINT,\n        'float' => self::TYPE_FLOAT,\n        'double' => self::TYPE_DOUBLE,\n        'double precision' => self::TYPE_DOUBLE,\n        'real' => self::TYPE_FLOAT,\n        'decimal' => self::TYPE_DECIMAL,\n        'numeric' => self::TYPE_DECIMAL,\n        'dec' => self::TYPE_DECIMAL,\n        'fixed' => self::TYPE_DECIMAL,\n        'tinytext' => self::TYPE_TEXT,\n        'mediumtext' => self::TYPE_TEXT,\n        'longtext' => self::TYPE_TEXT,\n        'longblob' => self::TYPE_BINARY,\n        'blob' => self::TYPE_BINARY,\n        'text' => self::TYPE_TEXT,\n        'varchar' => self::TYPE_STRING,\n        'string' => self::TYPE_STRING,\n        'char' => self::TYPE_CHAR,\n        'datetime' => self::TYPE_DATETIME,\n        'year' => self::TYPE_DATE,\n        'date' => self::TYPE_DATE,\n        'time' => self::TYPE_TIME,\n        'timestamp' => self::TYPE_TIMESTAMP,\n        'enum' => self::TYPE_STRING,\n        'set' => self::TYPE_STRING,\n        'binary' => self::TYPE_BINARY,\n        'varbinary' => self::TYPE_BINARY,\n        'json' => self::TYPE_JSON,\n    ];\n\n    /**\n     * {@inheritdoc}\n     */\n    protected $tableQuoteCharacter = '`';\n    /**\n     * {@inheritdoc}\n     */\n    protected $columnQuoteCharacter = '`';\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function resolveTableName($name)\n    {\n        $resolvedName = new TableSchema();\n        $parts = explode('.', str_replace('`', '', $name));\n        if (isset($parts[1])) {\n            $resolvedName->schemaName = $parts[0];\n            $resolvedName->name = $parts[1];\n        } else {\n            $resolvedName->schemaName = $this->defaultSchema;\n            $resolvedName->name = $name;\n        }\n        $resolvedName->fullName = ($resolvedName->schemaName !== $this->defaultSchema ? $resolvedName->schemaName . '.' : '') . $resolvedName->name;\n        return $resolvedName;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function findTableNames($schema = '')\n    {\n        $sql = 'SHOW TABLES';\n        if ($schema !== '') {\n            $sql .= ' FROM ' . $this->quoteSimpleTableName($schema);\n        }\n\n        return $this->db->createCommand($sql)->queryColumn();\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableSchema($name)\n    {\n        $table = new TableSchema();\n        $this->resolveTableNames($table, $name);\n\n        if ($this->findColumns($table)) {\n            $this->findConstraints($table);\n            return $table;\n        }\n\n        return null;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTablePrimaryKey($tableName)\n    {\n        return $this->loadTableConstraints($tableName, 'primaryKey');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableForeignKeys($tableName)\n    {\n        return $this->loadTableConstraints($tableName, 'foreignKeys');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableIndexes($tableName)\n    {\n        static $sql = <<<'SQL'\nSELECT\n    `s`.`INDEX_NAME` AS `name`,\n    `s`.`COLUMN_NAME` AS `column_name`,\n    `s`.`NON_UNIQUE` ^ 1 AS `index_is_unique`,\n    `s`.`INDEX_NAME` = 'PRIMARY' AS `index_is_primary`\nFROM `information_schema`.`STATISTICS` AS `s`\nWHERE `s`.`TABLE_SCHEMA` = COALESCE(:schemaName, DATABASE()) AND `s`.`INDEX_SCHEMA` = `s`.`TABLE_SCHEMA` AND `s`.`TABLE_NAME` = :tableName\nORDER BY `s`.`SEQ_IN_INDEX` ASC\nSQL;\n\n        $resolvedName = $this->resolveTableName($tableName);\n        $indexes = $this->db->createCommand($sql, [\n            ':schemaName' => $resolvedName->schemaName,\n            ':tableName' => $resolvedName->name,\n        ])->queryAll();\n        $indexes = $this->normalizePdoRowKeyCase($indexes, true);\n        $indexes = ArrayHelper::index($indexes, null, 'name');\n        $result = [];\n        foreach ($indexes as $name => $index) {\n            $result[] = new IndexConstraint([\n                'isPrimary' => (bool) $index[0]['index_is_primary'],\n                'isUnique' => (bool) $index[0]['index_is_unique'],\n                'name' => $name !== 'PRIMARY' ? $name : null,\n                'columnNames' => ArrayHelper::getColumn($index, 'column_name'),\n            ]);\n        }\n\n        return $result;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableUniques($tableName)\n    {\n        return $this->loadTableConstraints($tableName, 'uniques');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableChecks($tableName)\n    {\n        $version = $this->db->getServerVersion();\n\n        // check version MySQL >= 8.0.16\n        if (\\stripos($version, 'MariaDb') === false && \\version_compare($version, '8.0.16', '<')) {\n            throw new NotSupportedException('MySQL < 8.0.16 does not support check constraints.');\n        }\n\n        $checks = [];\n\n        $sql = <<<SQL\n        SELECT cc.CONSTRAINT_NAME as constraint_name, cc.CHECK_CLAUSE as check_clause\n        FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc\n        JOIN INFORMATION_SCHEMA.CHECK_CONSTRAINTS cc\n        ON tc.CONSTRAINT_NAME = cc.CONSTRAINT_NAME\n        WHERE tc.TABLE_NAME = :tableName AND tc.CONSTRAINT_TYPE = 'CHECK';\n        SQL;\n\n        $resolvedName = $this->resolveTableName($tableName);\n        $tableRows = $this->db->createCommand($sql, [':tableName' => $resolvedName->name])->queryAll();\n\n        if ($tableRows === []) {\n            return $checks;\n        }\n\n        $tableRows = $this->normalizePdoRowKeyCase($tableRows, true);\n\n        foreach ($tableRows as $tableRow) {\n            $check = new CheckConstraint(\n                [\n                    'name' => $tableRow['constraint_name'],\n                    'expression' => $tableRow['check_clause'],\n                ]\n            );\n            $checks[] = $check;\n        }\n\n        return $checks;\n    }\n\n    /**\n     * {@inheritdoc}\n     * @throws NotSupportedException if this method is called.\n     */\n    protected function loadTableDefaultValues($tableName)\n    {\n        throw new NotSupportedException('MySQL does not support default value constraints.');\n    }\n\n    /**\n     * Creates a query builder for the MySQL database.\n     * @return QueryBuilder query builder instance\n     */\n    public function createQueryBuilder()\n    {\n        return Yii::createObject(QueryBuilder::className(), [$this->db]);\n    }\n\n    /**\n     * Resolves the table name and schema name (if any).\n     * @param TableSchema $table the table metadata object\n     * @param string $name the table name\n     */\n    protected function resolveTableNames($table, $name)\n    {\n        $parts = explode('.', str_replace('`', '', $name));\n        if (isset($parts[1])) {\n            $table->schemaName = $parts[0];\n            $table->name = $parts[1];\n            $table->fullName = $table->schemaName . '.' . $table->name;\n        } else {\n            $table->fullName = $table->name = $parts[0];\n        }\n    }\n\n    /**\n     * Loads the column information into a [[ColumnSchema]] object.\n     * @param array $info column information\n     * @return T the column schema object\n     */\n    protected function loadColumnSchema($info)\n    {\n        $column = $this->createColumnSchema();\n\n        $column->name = $info['field'];\n        $column->allowNull = $info['null'] === 'YES';\n        $column->isPrimaryKey = strpos($info['key'], 'PRI') !== false;\n        $column->autoIncrement = stripos($info['extra'], 'auto_increment') !== false;\n        $column->comment = $info['comment'];\n\n        $column->dbType = $info['type'];\n        $column->unsigned = stripos($column->dbType, 'unsigned') !== false;\n\n        $column->type = self::TYPE_STRING;\n        if (preg_match('/^(\\w+)(?:\\(([^\\)]+)\\))?/', $column->dbType, $matches)) {\n            $type = strtolower($matches[1]);\n            if (isset($this->typeMap[$type])) {\n                $column->type = $this->typeMap[$type];\n            }\n            if (!empty($matches[2])) {\n                if ($type === 'enum') {\n                    preg_match_all(\"/'[^']*'/\", $matches[2], $values);\n                    foreach ($values[0] as $i => $value) {\n                        $values[$i] = trim($value, \"'\");\n                    }\n                    $column->enumValues = $values;\n                } else {\n                    $values = explode(',', $matches[2]);\n                    $column->size = $column->precision = (int) $values[0];\n                    if (isset($values[1])) {\n                        $column->scale = (int) $values[1];\n                    }\n                    if ($column->size === 1 && $type === 'bit') {\n                        $column->type = 'boolean';\n                    } elseif ($type === 'bit') {\n                        if ($column->size > 32) {\n                            $column->type = 'bigint';\n                        } elseif ($column->size === 32) {\n                            $column->type = 'integer';\n                        }\n                    }\n                }\n            }\n        }\n\n        $column->phpType = $this->getColumnPhpType($column);\n\n        if (!$column->isPrimaryKey) {\n            /**\n             * When displayed in the INFORMATION_SCHEMA.COLUMNS table, a default CURRENT TIMESTAMP is displayed\n             * as CURRENT_TIMESTAMP up until MariaDB 10.2.2, and as current_timestamp() from MariaDB 10.2.3.\n             *\n             * See details here: https://mariadb.com/kb/en/library/now/#description\n             */\n            if (\n                in_array($column->type, ['timestamp', 'datetime', 'date', 'time'])\n                && isset($info['default'])\n                && preg_match('/^current_timestamp(?:\\(([0-9]*)\\))?$/i', $info['default'], $matches)\n            ) {\n                $column->defaultValue = new Expression('CURRENT_TIMESTAMP' . (!empty($matches[1]) ? '(' . $matches[1] . ')' : ''));\n            } elseif (isset($type) && $type === 'bit') {\n                $column->defaultValue = bindec(trim(isset($info['default']) ? $info['default'] : '', 'b\\''));\n            } else {\n                $column->defaultValue = $column->phpTypecast($info['default']);\n            }\n        }\n\n        return $column;\n    }\n\n    /**\n     * Collects the metadata of table columns.\n     * @param TableSchema $table the table metadata\n     * @return bool whether the table exists in the database\n     * @throws \\Exception if DB query fails\n     */\n    protected function findColumns($table)\n    {\n        $sql = 'SHOW FULL COLUMNS FROM ' . $this->quoteTableName($table->fullName);\n        try {\n            $columns = $this->db->createCommand($sql)->queryAll();\n        } catch (\\Exception $e) {\n            $previous = $e->getPrevious();\n            if ($previous instanceof \\PDOException && strpos($previous->getMessage(), 'SQLSTATE[42S02') !== false) {\n                // table does not exist\n                // https://dev.mysql.com/doc/refman/5.5/en/error-messages-server.html#error_er_bad_table_error\n                return false;\n            }\n            throw $e;\n        }\n\n\n        $jsonColumns = $this->getJsonColumns($table);\n\n        foreach ($columns as $info) {\n            if ($this->db->slavePdo->getAttribute(\\PDO::ATTR_CASE) !== \\PDO::CASE_LOWER) {\n                $info = array_change_key_case($info, CASE_LOWER);\n            }\n\n            if (\\in_array($info['field'], $jsonColumns, true)) {\n                $info['type'] = static::TYPE_JSON;\n            }\n\n            $column = $this->loadColumnSchema($info);\n            $table->columns[$column->name] = $column;\n            if ($column->isPrimaryKey) {\n                $table->primaryKey[] = $column->name;\n                if ($column->autoIncrement) {\n                    $table->sequenceName = '';\n                }\n            }\n        }\n\n        return true;\n    }\n\n    /**\n     * Gets the CREATE TABLE sql string.\n     * @param TableSchema $table the table metadata\n     * @return string $sql the result of 'SHOW CREATE TABLE'\n     */\n    protected function getCreateTableSql($table)\n    {\n        $row = $this->db->createCommand('SHOW CREATE TABLE ' . $this->quoteTableName($table->fullName))->queryOne();\n        if (isset($row['Create Table'])) {\n            $sql = $row['Create Table'];\n        } else {\n            $row = array_values($row);\n            $sql = $row[1];\n        }\n\n        return $sql;\n    }\n\n    /**\n     * Collects the foreign key column details for the given table.\n     * @param TableSchema $table the table metadata\n     * @throws \\Exception\n     */\n    protected function findConstraints($table)\n    {\n        $sql = <<<'SQL'\nSELECT\n    `kcu`.`CONSTRAINT_NAME` AS `constraint_name`,\n    `kcu`.`COLUMN_NAME` AS `column_name`,\n    `kcu`.`REFERENCED_TABLE_NAME` AS `referenced_table_name`,\n    `kcu`.`REFERENCED_COLUMN_NAME` AS `referenced_column_name`\nFROM `information_schema`.`REFERENTIAL_CONSTRAINTS` AS `rc`\nJOIN `information_schema`.`KEY_COLUMN_USAGE` AS `kcu` ON\n    (\n        `kcu`.`CONSTRAINT_CATALOG` = `rc`.`CONSTRAINT_CATALOG` OR\n        (`kcu`.`CONSTRAINT_CATALOG` IS NULL AND `rc`.`CONSTRAINT_CATALOG` IS NULL)\n    ) AND\n    `kcu`.`CONSTRAINT_SCHEMA` = `rc`.`CONSTRAINT_SCHEMA` AND\n    `kcu`.`CONSTRAINT_NAME` = `rc`.`CONSTRAINT_NAME`\nWHERE `rc`.`CONSTRAINT_SCHEMA` = database() AND `kcu`.`TABLE_SCHEMA` = database()\nAND `rc`.`TABLE_NAME` = :tableName AND `kcu`.`TABLE_NAME` = :tableName1\nSQL;\n\n        try {\n            $rows = $this->db->createCommand($sql, [':tableName' => $table->name, ':tableName1' => $table->name])->queryAll();\n            $constraints = [];\n\n            foreach ($rows as $row) {\n                $constraints[$row['constraint_name']]['referenced_table_name'] = $row['referenced_table_name'];\n                $constraints[$row['constraint_name']]['columns'][$row['column_name']] = $row['referenced_column_name'];\n            }\n\n            $table->foreignKeys = [];\n            foreach ($constraints as $name => $constraint) {\n                $table->foreignKeys[$name] = array_merge(\n                    [$constraint['referenced_table_name']],\n                    $constraint['columns']\n                );\n            }\n        } catch (\\Exception $e) {\n            $previous = $e->getPrevious();\n            if (!$previous instanceof \\PDOException || strpos($previous->getMessage(), 'SQLSTATE[42S02') === false) {\n                throw $e;\n            }\n\n            // table does not exist, try to determine the foreign keys using the table creation sql\n            $sql = $this->getCreateTableSql($table);\n            $regexp = '/FOREIGN KEY\\s+\\(([^\\)]+)\\)\\s+REFERENCES\\s+([^\\(^\\s]+)\\s*\\(([^\\)]+)\\)/mi';\n            if (preg_match_all($regexp, $sql, $matches, PREG_SET_ORDER)) {\n                foreach ($matches as $match) {\n                    $fks = array_map('trim', explode(',', str_replace(['`', '\"'], '', $match[1])));\n                    $pks = array_map('trim', explode(',', str_replace(['`', '\"'], '', $match[3])));\n                    $constraint = [str_replace(['`', '\"'], '', $match[2])];\n                    foreach ($fks as $k => $name) {\n                        $constraint[$name] = $pks[$k];\n                    }\n                    $table->foreignKeys[md5(serialize($constraint))] = $constraint;\n                }\n                $table->foreignKeys = array_values($table->foreignKeys);\n            }\n        }\n    }\n\n    /**\n     * Returns all unique indexes for the given table.\n     *\n     * Each array element is of the following structure:\n     *\n     * ```\n     * [\n     *     'IndexName1' => ['col1' [, ...]],\n     *     'IndexName2' => ['col2' [, ...]],\n     * ]\n     * ```\n     *\n     * @param TableSchema $table the table metadata\n     * @return array all unique indexes for the given table.\n     */\n    public function findUniqueIndexes($table)\n    {\n        $sql = $this->getCreateTableSql($table);\n        $uniqueIndexes = [];\n\n        $regexp = '/UNIQUE KEY\\s+[`\"](.+)[`\"]\\s*\\(([`\"].+[`\"])+\\)/mi';\n        if (preg_match_all($regexp, $sql, $matches, PREG_SET_ORDER)) {\n            foreach ($matches as $match) {\n                $indexName = $match[1];\n                $indexColumns = array_map('trim', preg_split('/[`\"],[`\"]/', trim($match[2], '`\"')));\n                $uniqueIndexes[$indexName] = $indexColumns;\n            }\n        }\n\n        return $uniqueIndexes;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function createColumnSchemaBuilder($type, $length = null)\n    {\n        return Yii::createObject(ColumnSchemaBuilder::className(), [$type, $length, $this->db]);\n    }\n\n    /**\n     * @return bool whether the version of the MySQL being used is older than 5.1.\n     * @throws InvalidConfigException\n     * @throws Exception\n     * @since 2.0.13\n     */\n    protected function isOldMysql()\n    {\n        if ($this->_oldMysql === null) {\n            $version = $this->db->getSlavePdo(true)->getAttribute(\\PDO::ATTR_SERVER_VERSION);\n            $this->_oldMysql = version_compare($version, '5.1', '<=');\n        }\n\n        return $this->_oldMysql;\n    }\n\n    /**\n     * Loads multiple types of constraints and returns the specified ones.\n     * @param string $tableName table name.\n     * @param string $returnType return type:\n     * - primaryKey\n     * - foreignKeys\n     * - uniques\n     * @return mixed constraints.\n     */\n    private function loadTableConstraints($tableName, $returnType)\n    {\n        static $sql = <<<'SQL'\nSELECT\n    `kcu`.`CONSTRAINT_NAME` AS `name`,\n    `kcu`.`COLUMN_NAME` AS `column_name`,\n    `tc`.`CONSTRAINT_TYPE` AS `type`,\n    CASE\n        WHEN :schemaName IS NULL AND `kcu`.`REFERENCED_TABLE_SCHEMA` = DATABASE() THEN NULL\n        ELSE `kcu`.`REFERENCED_TABLE_SCHEMA`\n    END AS `foreign_table_schema`,\n    `kcu`.`REFERENCED_TABLE_NAME` AS `foreign_table_name`,\n    `kcu`.`REFERENCED_COLUMN_NAME` AS `foreign_column_name`,\n    `rc`.`UPDATE_RULE` AS `on_update`,\n    `rc`.`DELETE_RULE` AS `on_delete`,\n    `kcu`.`ORDINAL_POSITION` AS `position`\nFROM\n    `information_schema`.`KEY_COLUMN_USAGE` AS `kcu`,\n    `information_schema`.`REFERENTIAL_CONSTRAINTS` AS `rc`,\n    `information_schema`.`TABLE_CONSTRAINTS` AS `tc`\nWHERE\n    `kcu`.`TABLE_SCHEMA` = COALESCE(:schemaName1, DATABASE()) AND `kcu`.`CONSTRAINT_SCHEMA` = `kcu`.`TABLE_SCHEMA` AND `kcu`.`TABLE_NAME` = :tableName\n    AND `rc`.`CONSTRAINT_SCHEMA` = `kcu`.`TABLE_SCHEMA` AND `rc`.`TABLE_NAME` = :tableName1 AND `rc`.`CONSTRAINT_NAME` = `kcu`.`CONSTRAINT_NAME`\n    AND `tc`.`TABLE_SCHEMA` = `kcu`.`TABLE_SCHEMA` AND `tc`.`TABLE_NAME` = :tableName2 AND `tc`.`CONSTRAINT_NAME` = `kcu`.`CONSTRAINT_NAME` AND `tc`.`CONSTRAINT_TYPE` = 'FOREIGN KEY'\nUNION\nSELECT\n    `kcu`.`CONSTRAINT_NAME` AS `name`,\n    `kcu`.`COLUMN_NAME` AS `column_name`,\n    `tc`.`CONSTRAINT_TYPE` AS `type`,\n    NULL AS `foreign_table_schema`,\n    NULL AS `foreign_table_name`,\n    NULL AS `foreign_column_name`,\n    NULL AS `on_update`,\n    NULL AS `on_delete`,\n    `kcu`.`ORDINAL_POSITION` AS `position`\nFROM\n    `information_schema`.`KEY_COLUMN_USAGE` AS `kcu`,\n    `information_schema`.`TABLE_CONSTRAINTS` AS `tc`\nWHERE\n    `kcu`.`TABLE_SCHEMA` = COALESCE(:schemaName2, DATABASE()) AND `kcu`.`TABLE_NAME` = :tableName3\n    AND `tc`.`TABLE_SCHEMA` = `kcu`.`TABLE_SCHEMA` AND `tc`.`TABLE_NAME` = :tableName4 AND `tc`.`CONSTRAINT_NAME` = `kcu`.`CONSTRAINT_NAME` AND `tc`.`CONSTRAINT_TYPE` IN ('PRIMARY KEY', 'UNIQUE')\nORDER BY `position` ASC\nSQL;\n\n        $resolvedName = $this->resolveTableName($tableName);\n        $constraints = $this->db->createCommand($sql, [\n            ':schemaName' => $resolvedName->schemaName,\n            ':schemaName1' => $resolvedName->schemaName,\n            ':schemaName2' => $resolvedName->schemaName,\n            ':tableName' => $resolvedName->name,\n            ':tableName1' => $resolvedName->name,\n            ':tableName2' => $resolvedName->name,\n            ':tableName3' => $resolvedName->name,\n            ':tableName4' => $resolvedName->name\n        ])->queryAll();\n        $constraints = $this->normalizePdoRowKeyCase($constraints, true);\n        $constraints = ArrayHelper::index($constraints, null, ['type', 'name']);\n        $result = [\n            'primaryKey' => null,\n            'foreignKeys' => [],\n            'uniques' => [],\n        ];\n        foreach ($constraints as $type => $names) {\n            foreach ($names as $name => $constraint) {\n                switch ($type) {\n                    case 'PRIMARY KEY':\n                        $result['primaryKey'] = new Constraint([\n                            'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),\n                        ]);\n                        break;\n                    case 'FOREIGN KEY':\n                        $result['foreignKeys'][] = new ForeignKeyConstraint([\n                            'name' => $name,\n                            'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),\n                            'foreignSchemaName' => $constraint[0]['foreign_table_schema'],\n                            'foreignTableName' => $constraint[0]['foreign_table_name'],\n                            'foreignColumnNames' => ArrayHelper::getColumn($constraint, 'foreign_column_name'),\n                            'onDelete' => $constraint[0]['on_delete'],\n                            'onUpdate' => $constraint[0]['on_update'],\n                        ]);\n                        break;\n                    case 'UNIQUE':\n                        $result['uniques'][] = new Constraint([\n                            'name' => $name,\n                            'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),\n                        ]);\n                        break;\n                }\n            }\n        }\n        foreach ($result as $type => $data) {\n            $this->setTableMetadata($tableName, $type, $data);\n        }\n\n        return $result[$returnType];\n    }\n\n    private function getJsonColumns(TableSchema $table): array\n    {\n        $sql = $this->getCreateTableSql($table);\n        $result = [];\n\n        $regexp = '/json_valid\\([\\`\"](.+)[\\`\"]\\s*\\)/mi';\n\n        if (\\preg_match_all($regexp, $sql, $matches, PREG_SET_ORDER)) {\n            foreach ($matches as $match) {\n                $result[] = $match[1];\n            }\n        }\n\n        return $result;\n    }\n}\n"
  },
  {
    "path": "framework/db/oci/ColumnSchemaBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\oci;\n\nuse yii\\db\\ColumnSchemaBuilder as AbstractColumnSchemaBuilder;\n\n/**\n * ColumnSchemaBuilder is the schema builder for Oracle databases.\n *\n * @author Vasenin Matvey <vaseninm@gmail.com>\n * @author Chris Harris <chris@buckshotsoftware.com>\n * @since 2.0.6\n */\nclass ColumnSchemaBuilder extends AbstractColumnSchemaBuilder\n{\n    /**\n     * {@inheritdoc}\n     */\n    protected function buildUnsignedString()\n    {\n        return $this->isUnsigned ? ' UNSIGNED' : '';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function __toString()\n    {\n        switch ($this->getTypeCategory()) {\n            case self::CATEGORY_PK:\n                $format = '{type}{length}{check}{append}';\n                break;\n            case self::CATEGORY_NUMERIC:\n                $format = '{type}{length}{unsigned}{default}{notnull}{check}{append}';\n                break;\n            default:\n                $format = '{type}{length}{default}{notnull}{check}{append}';\n        }\n\n        return $this->buildCompleteString($format);\n    }\n}\n"
  },
  {
    "path": "framework/db/oci/Command.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\oci;\n\n/**\n * Command represents an Oracle SQL statement to be executed against a database.\n *\n * {@inheritdoc}\n *\n * @since 2.0.33\n */\nclass Command extends \\yii\\db\\Command\n{\n    /**\n     * {@inheritdoc}\n     */\n    protected function bindPendingParams()\n    {\n        $paramsPassedByReference = [];\n        foreach ($this->pendingParams as $name => $value) {\n            if (\\PDO::PARAM_STR === $value[1]) {\n                $paramsPassedByReference[$name] = $value[0];\n                $this->pdoStatement->bindParam($name, $paramsPassedByReference[$name], $value[1], strlen($value[0]));\n            } else {\n                $this->pdoStatement->bindValue($name, $value[0], $value[1]);\n            }\n        }\n        $this->pendingParams = [];\n    }\n}\n"
  },
  {
    "path": "framework/db/oci/QueryBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\oci;\n\nuse yii\\base\\InvalidArgumentException;\nuse yii\\db\\Connection;\nuse yii\\db\\Exception;\nuse yii\\db\\Expression;\nuse yii\\db\\Query;\nuse yii\\helpers\\StringHelper;\nuse yii\\db\\ExpressionInterface;\n\n/**\n * QueryBuilder is the query builder for Oracle databases.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass QueryBuilder extends \\yii\\db\\QueryBuilder\n{\n    /**\n     * @var array mapping from abstract column types (keys) to physical column types (values).\n     */\n    public $typeMap = [\n        Schema::TYPE_PK => 'NUMBER(10) NOT NULL PRIMARY KEY',\n        Schema::TYPE_UPK => 'NUMBER(10) UNSIGNED NOT NULL PRIMARY KEY',\n        Schema::TYPE_BIGPK => 'NUMBER(20) NOT NULL PRIMARY KEY',\n        Schema::TYPE_UBIGPK => 'NUMBER(20) UNSIGNED NOT NULL PRIMARY KEY',\n        Schema::TYPE_CHAR => 'CHAR(1)',\n        Schema::TYPE_STRING => 'VARCHAR2(255)',\n        Schema::TYPE_TEXT => 'CLOB',\n        Schema::TYPE_TINYINT => 'NUMBER(3)',\n        Schema::TYPE_SMALLINT => 'NUMBER(5)',\n        Schema::TYPE_INTEGER => 'NUMBER(10)',\n        Schema::TYPE_BIGINT => 'NUMBER(20)',\n        Schema::TYPE_FLOAT => 'NUMBER',\n        Schema::TYPE_DOUBLE => 'NUMBER',\n        Schema::TYPE_DECIMAL => 'NUMBER',\n        Schema::TYPE_DATETIME => 'TIMESTAMP',\n        Schema::TYPE_TIMESTAMP => 'TIMESTAMP',\n        Schema::TYPE_TIME => 'TIMESTAMP',\n        Schema::TYPE_DATE => 'DATE',\n        Schema::TYPE_BINARY => 'BLOB',\n        Schema::TYPE_BOOLEAN => 'NUMBER(1)',\n        Schema::TYPE_MONEY => 'NUMBER(19,4)',\n    ];\n\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function defaultExpressionBuilders()\n    {\n        return array_merge(parent::defaultExpressionBuilders(), [\n            'yii\\db\\conditions\\InCondition' => 'yii\\db\\oci\\conditions\\InConditionBuilder',\n            'yii\\db\\conditions\\LikeCondition' => 'yii\\db\\oci\\conditions\\LikeConditionBuilder',\n        ]);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function buildOrderByAndLimit($sql, $orderBy, $limit, $offset)\n    {\n        $orderBy = $this->buildOrderBy($orderBy);\n        if ($orderBy !== '') {\n            $sql .= $this->separator . $orderBy;\n        }\n\n        $filters = [];\n        if ($this->hasOffset($offset)) {\n            $filters[] = 'rowNumId > ' . $offset;\n        }\n        if ($this->hasLimit($limit)) {\n            $filters[] = 'rownum <= ' . $limit;\n        }\n        if (empty($filters)) {\n            return $sql;\n        }\n\n        $filter = implode(' AND ', $filters);\n        return <<<EOD\nWITH USER_SQL AS ($sql),\n    PAGINATION AS (SELECT USER_SQL.*, rownum as rowNumId FROM USER_SQL)\nSELECT *\nFROM PAGINATION\nWHERE $filter\nEOD;\n    }\n\n    /**\n     * Builds a SQL statement for renaming a DB table.\n     *\n     * @param string $table the table to be renamed. The name will be properly quoted by the method.\n     * @param string $newName the new table name. The name will be properly quoted by the method.\n     * @return string the SQL statement for renaming a DB table.\n     */\n    public function renameTable($table, $newName)\n    {\n        return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' RENAME TO ' . $this->db->quoteTableName($newName);\n    }\n\n    /**\n     * Builds a SQL statement for changing the definition of a column.\n     *\n     * @param string $table the table whose column is to be changed. The table name will be properly quoted by the method.\n     * @param string $column the name of the column to be changed. The name will be properly quoted by the method.\n     * @param string $type the new column type. The [[getColumnType]] method will be invoked to convert abstract column type (if any)\n     * into the physical one. Anything that is not recognized as abstract type will be kept in the generated SQL.\n     * For example, 'string' will be turned into 'varchar(255)', while 'string not null' will become 'varchar(255) not null'.\n     * @return string the SQL statement for changing the definition of a column.\n     */\n    public function alterColumn($table, $column, $type)\n    {\n        $type = $this->getColumnType($type);\n\n        return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' MODIFY ' . $this->db->quoteColumnName($column) . ' ' . $this->getColumnType($type);\n    }\n\n    /**\n     * Builds a SQL statement for dropping an index.\n     *\n     * @param string $name the name of the index to be dropped. The name will be properly quoted by the method.\n     * @param string $table the table whose index is to be dropped. The name will be properly quoted by the method.\n     * @return string the SQL statement for dropping an index.\n     */\n    public function dropIndex($name, $table)\n    {\n        return 'DROP INDEX ' . $this->db->quoteTableName($name);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function executeResetSequence($table, $value = null)\n    {\n        $tableSchema = $this->db->getTableSchema($table);\n        if ($tableSchema === null) {\n            throw new InvalidArgumentException(\"Unknown table: $table\");\n        }\n        if ($tableSchema->sequenceName === null) {\n            throw new InvalidArgumentException(\"There is no sequence associated with table: $table\");\n        }\n\n        if ($value !== null) {\n            $value = (int) $value;\n        } else {\n            if (count($tableSchema->primaryKey) > 1) {\n                throw new InvalidArgumentException(\"Can't reset sequence for composite primary key in table: $table\");\n            }\n            // use master connection to get the biggest PK value\n            $value = $this->db->useMaster(function (Connection $db) use ($tableSchema) {\n                return $db->createCommand(\n                    'SELECT MAX(\"' . $tableSchema->primaryKey[0] . '\") FROM \"' . $tableSchema->name . '\"'\n                )->queryScalar();\n            }) + 1;\n        }\n\n        //Oracle needs at least two queries to reset sequence (see adding transactions and/or use alter method to avoid grants' issue?)\n        $this->db->createCommand('DROP SEQUENCE \"' . $tableSchema->sequenceName . '\"')->execute();\n        $this->db->createCommand('CREATE SEQUENCE \"' . $tableSchema->sequenceName . '\" START WITH ' . $value\n            . ' INCREMENT BY 1 NOMAXVALUE NOCACHE')->execute();\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete = null, $update = null)\n    {\n        $sql = 'ALTER TABLE ' . $this->db->quoteTableName($table)\n            . ' ADD CONSTRAINT ' . $this->db->quoteColumnName($name)\n            . ' FOREIGN KEY (' . $this->buildColumns($columns) . ')'\n            . ' REFERENCES ' . $this->db->quoteTableName($refTable)\n            . ' (' . $this->buildColumns($refColumns) . ')';\n        if ($delete !== null) {\n            $sql .= ' ON DELETE ' . $delete;\n        }\n        if ($update !== null) {\n            throw new Exception('Oracle does not support ON UPDATE clause.');\n        }\n\n        return $sql;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function prepareInsertValues($table, $columns, $params = [])\n    {\n        list($names, $placeholders, $values, $params) = parent::prepareInsertValues($table, $columns, $params);\n        if (!$columns instanceof Query && empty($names)) {\n            $tableSchema = $this->db->getSchema()->getTableSchema($table);\n            if ($tableSchema !== null) {\n                $columns = !empty($tableSchema->primaryKey) ? $tableSchema->primaryKey : [reset($tableSchema->columns)->name];\n                foreach ($columns as $name) {\n                    $names[] = $this->db->quoteColumnName($name);\n                    $placeholders[] = 'DEFAULT';\n                }\n            }\n        }\n        return [$names, $placeholders, $values, $params];\n    }\n\n    /**\n     * {@inheritdoc}\n     * @see https://docs.oracle.com/cd/B28359_01/server.111/b28286/statements_9016.htm#SQLRF01606\n     */\n    public function upsert($table, $insertColumns, $updateColumns, &$params)\n    {\n        list($uniqueNames, $insertNames, $updateNames) = $this->prepareUpsertColumns($table, $insertColumns, $updateColumns, $constraints);\n        if (empty($uniqueNames)) {\n            return $this->insert($table, $insertColumns, $params);\n        }\n        if ($updateNames === []) {\n            // there are no columns to update\n            $updateColumns = false;\n        }\n\n        $onCondition = ['or'];\n        $quotedTableName = $this->db->quoteTableName($table);\n        foreach ($constraints as $constraint) {\n            $constraintCondition = ['and'];\n            foreach ($constraint->columnNames as $name) {\n                $quotedName = $this->db->quoteColumnName($name);\n                $constraintCondition[] = \"$quotedTableName.$quotedName=\\\"EXCLUDED\\\".$quotedName\";\n            }\n            $onCondition[] = $constraintCondition;\n        }\n        $on = $this->buildCondition($onCondition, $params);\n        list(, $placeholders, $values, $params) = $this->prepareInsertValues($table, $insertColumns, $params);\n        if (!empty($placeholders)) {\n            $usingSelectValues = [];\n            foreach ($insertNames as $index => $name) {\n                $usingSelectValues[$name] = new Expression($placeholders[$index]);\n            }\n            $usingSubQuery = (new Query())\n                ->select($usingSelectValues)\n                ->from('DUAL');\n            list($usingValues, $params) = $this->build($usingSubQuery, $params);\n        }\n        $mergeSql = 'MERGE INTO ' . $this->db->quoteTableName($table) . ' '\n            . 'USING (' . (isset($usingValues) ? $usingValues : ltrim($values, ' ')) . ') \"EXCLUDED\" '\n            . \"ON ($on)\";\n        $insertValues = [];\n        foreach ($insertNames as $name) {\n            $quotedName = $this->db->quoteColumnName($name);\n            if (strrpos($quotedName, '.') === false) {\n                $quotedName = '\"EXCLUDED\".' . $quotedName;\n            }\n            $insertValues[] = $quotedName;\n        }\n        $insertSql = 'INSERT (' . implode(', ', $insertNames) . ')'\n            . ' VALUES (' . implode(', ', $insertValues) . ')';\n        if ($updateColumns === false) {\n            return \"$mergeSql WHEN NOT MATCHED THEN $insertSql\";\n        }\n\n        if ($updateColumns === true) {\n            $updateColumns = [];\n            foreach ($updateNames as $name) {\n                $quotedName = $this->db->quoteColumnName($name);\n                if (strrpos($quotedName, '.') === false) {\n                    $quotedName = '\"EXCLUDED\".' . $quotedName;\n                }\n                $updateColumns[$name] = new Expression($quotedName);\n            }\n        }\n        list($updates, $params) = $this->prepareUpdateSets($table, $updateColumns, $params);\n        $updateSql = 'UPDATE SET ' . implode(', ', $updates);\n        return \"$mergeSql WHEN MATCHED THEN $updateSql WHEN NOT MATCHED THEN $insertSql\";\n    }\n\n    /**\n     * Generates a batch INSERT SQL statement.\n     *\n     * For example,\n     *\n     * ```\n     * $sql = $queryBuilder->batchInsert('user', ['name', 'age'], [\n     *     ['Tom', 30],\n     *     ['Jane', 20],\n     *     ['Linda', 25],\n     * ]);\n     * ```\n     *\n     * Note that the values in each row must match the corresponding column names.\n     *\n     * @param string $table the table that new rows will be inserted into.\n     * @param array $columns the column names\n     * @param array|\\Generator $rows the rows to be batch inserted into the table\n     * @return string the batch INSERT SQL statement\n     */\n    public function batchInsert($table, $columns, $rows, &$params = [])\n    {\n        if (empty($rows)) {\n            return '';\n        }\n\n        $schema = $this->db->getSchema();\n        if (($tableSchema = $schema->getTableSchema($table)) !== null) {\n            $columnSchemas = $tableSchema->columns;\n        } else {\n            $columnSchemas = [];\n        }\n\n        $values = [];\n        foreach ($rows as $row) {\n            $vs = [];\n            foreach ($row as $i => $value) {\n                if (isset($columns[$i], $columnSchemas[$columns[$i]])) {\n                    $value = $columnSchemas[$columns[$i]]->dbTypecast($value);\n                }\n                if (is_string($value)) {\n                    $value = $schema->quoteValue($value);\n                } elseif (is_float($value)) {\n                    // ensure type cast always has . as decimal separator in all locales\n                    $value = StringHelper::floatToString($value);\n                } elseif ($value === false) {\n                    $value = 0;\n                } elseif ($value === null) {\n                    $value = 'NULL';\n                } elseif ($value instanceof ExpressionInterface) {\n                    $value = $this->buildExpression($value, $params);\n                }\n                $vs[] = $value;\n            }\n            $values[] = '(' . implode(', ', $vs) . ')';\n        }\n        if (empty($values)) {\n            return '';\n        }\n\n        foreach ($columns as $i => $name) {\n            $columns[$i] = $schema->quoteColumnName($name);\n        }\n\n        $tableAndColumns = ' INTO ' . $schema->quoteTableName($table)\n        . ' (' . implode(', ', $columns) . ') VALUES ';\n\n        return 'INSERT ALL ' . $tableAndColumns . implode($tableAndColumns, $values) . ' SELECT 1 FROM SYS.DUAL';\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.8\n     */\n    public function selectExists($rawSql)\n    {\n        return 'SELECT CASE WHEN EXISTS(' . $rawSql . ') THEN 1 ELSE 0 END FROM DUAL';\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.8\n     */\n    public function dropCommentFromColumn($table, $column)\n    {\n        return 'COMMENT ON COLUMN ' . $this->db->quoteTableName($table) . '.' . $this->db->quoteColumnName($column) . \" IS ''\";\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.8\n     */\n    public function dropCommentFromTable($table)\n    {\n        return 'COMMENT ON TABLE ' . $this->db->quoteTableName($table) . \" IS ''\";\n    }\n}\n"
  },
  {
    "path": "framework/db/oci/Schema.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\oci;\n\nuse Yii;\nuse yii\\base\\InvalidCallException;\nuse yii\\base\\NotSupportedException;\nuse yii\\db\\CheckConstraint;\nuse yii\\db\\ColumnSchema;\nuse yii\\db\\Connection;\nuse yii\\db\\Constraint;\nuse yii\\db\\ConstraintFinderInterface;\nuse yii\\db\\ConstraintFinderTrait;\nuse yii\\db\\Expression;\nuse yii\\db\\ForeignKeyConstraint;\nuse yii\\db\\IndexConstraint;\nuse yii\\db\\TableSchema;\nuse yii\\helpers\\ArrayHelper;\nuse yii\\db\\Schema as BaseSchema;\n\n/**\n * Schema is the class for retrieving metadata from an Oracle database.\n *\n * @property-read string $lastInsertID The row ID of the last row inserted, or the last value retrieved from\n * the sequence object.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of ColumnSchema = ColumnSchema\n * @extends BaseSchema<T>\n */\nclass Schema extends BaseSchema implements ConstraintFinderInterface\n{\n    use ConstraintFinderTrait;\n\n    /**\n     * @var array map of DB errors and corresponding exceptions\n     * If left part is found in DB error message exception class from the right part is used.\n     */\n    public $exceptionMap = [\n        'ORA-00001: unique constraint' => 'yii\\db\\IntegrityException',\n    ];\n\n    /**\n     * {@inheritdoc}\n     */\n    protected $tableQuoteCharacter = '\"';\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        if ($this->defaultSchema === null) {\n            $username = $this->db->username;\n            if (empty($username)) {\n                $username = isset($this->db->masters[0]['username']) ? $this->db->masters[0]['username'] : '';\n            }\n            $this->defaultSchema = strtoupper($username);\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function resolveTableName($name)\n    {\n        $resolvedName = new TableSchema();\n        $parts = explode('.', str_replace('\"', '', $name));\n        if (isset($parts[1])) {\n            $resolvedName->schemaName = $parts[0];\n            $resolvedName->name = $parts[1];\n        } else {\n            $resolvedName->schemaName = $this->defaultSchema;\n            $resolvedName->name = $name;\n        }\n        $resolvedName->fullName = ($resolvedName->schemaName !== $this->defaultSchema ? $resolvedName->schemaName . '.' : '') . $resolvedName->name;\n        return $resolvedName;\n    }\n\n    /**\n     * {@inheritdoc}\n     * @see https://docs.oracle.com/cd/B28359_01/server.111/b28337/tdpsg_user_accounts.htm\n     */\n    protected function findSchemaNames()\n    {\n        static $sql = <<<'SQL'\nSELECT \"u\".\"USERNAME\"\nFROM \"DBA_USERS\" \"u\"\nWHERE \"u\".\"DEFAULT_TABLESPACE\" NOT IN ('SYSTEM', 'SYSAUX')\nORDER BY \"u\".\"USERNAME\" ASC\nSQL;\n\n        return $this->db->createCommand($sql)->queryColumn();\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function findTableNames($schema = '')\n    {\n        if ($schema === '') {\n            $sql = <<<'SQL'\nSELECT\n    TABLE_NAME\nFROM USER_TABLES\nUNION ALL\nSELECT\n    VIEW_NAME AS TABLE_NAME\nFROM USER_VIEWS\nUNION ALL\nSELECT\n    MVIEW_NAME AS TABLE_NAME\nFROM USER_MVIEWS\nORDER BY TABLE_NAME\nSQL;\n            $command = $this->db->createCommand($sql);\n        } else {\n            $sql = <<<'SQL'\nSELECT\n    OBJECT_NAME AS TABLE_NAME\nFROM ALL_OBJECTS\nWHERE\n    OBJECT_TYPE IN ('TABLE', 'VIEW', 'MATERIALIZED VIEW')\n    AND OWNER = :schema\nORDER BY OBJECT_NAME\nSQL;\n            $command = $this->db->createCommand($sql, [':schema' => $schema]);\n        }\n\n        $rows = $command->queryAll();\n        $names = [];\n        foreach ($rows as $row) {\n            if ($this->db->slavePdo->getAttribute(\\PDO::ATTR_CASE) === \\PDO::CASE_LOWER) {\n                $row = array_change_key_case($row, CASE_UPPER);\n            }\n            $names[] = $row['TABLE_NAME'];\n        }\n\n        return $names;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableSchema($name)\n    {\n        $table = new TableSchema();\n        $this->resolveTableNames($table, $name);\n        if ($this->findColumns($table)) {\n            $this->findConstraints($table);\n            return $table;\n        }\n\n        return null;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTablePrimaryKey($tableName)\n    {\n        return $this->loadTableConstraints($tableName, 'primaryKey');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableForeignKeys($tableName)\n    {\n        return $this->loadTableConstraints($tableName, 'foreignKeys');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableIndexes($tableName)\n    {\n        static $sql = <<<'SQL'\nSELECT\n    /*+ PUSH_PRED(\"ui\") PUSH_PRED(\"uicol\") PUSH_PRED(\"uc\") */\n    \"ui\".\"INDEX_NAME\" AS \"name\",\n    \"uicol\".\"COLUMN_NAME\" AS \"column_name\",\n    CASE \"ui\".\"UNIQUENESS\" WHEN 'UNIQUE' THEN 1 ELSE 0 END AS \"index_is_unique\",\n    CASE WHEN \"uc\".\"CONSTRAINT_NAME\" IS NOT NULL THEN 1 ELSE 0 END AS \"index_is_primary\"\nFROM \"SYS\".\"USER_INDEXES\" \"ui\"\nLEFT JOIN \"SYS\".\"USER_IND_COLUMNS\" \"uicol\"\n    ON \"uicol\".\"INDEX_NAME\" = \"ui\".\"INDEX_NAME\"\nLEFT JOIN \"SYS\".\"USER_CONSTRAINTS\" \"uc\"\n    ON \"uc\".\"OWNER\" = \"ui\".\"TABLE_OWNER\" AND \"uc\".\"CONSTRAINT_NAME\" = \"ui\".\"INDEX_NAME\" AND \"uc\".\"CONSTRAINT_TYPE\" = 'P'\nWHERE \"ui\".\"TABLE_OWNER\" = :schemaName AND \"ui\".\"TABLE_NAME\" = :tableName\n    AND \"ui\".\"INDEX_TYPE\" != 'LOB'\nORDER BY \"uicol\".\"COLUMN_POSITION\" ASC\nSQL;\n\n        $resolvedName = $this->resolveTableName($tableName);\n        $indexes = $this->db->createCommand($sql, [\n            ':schemaName' => $resolvedName->schemaName,\n            ':tableName' => $resolvedName->name,\n        ])->queryAll();\n        $indexes = $this->normalizePdoRowKeyCase($indexes, true);\n        $indexes = ArrayHelper::index($indexes, null, 'name');\n        $result = [];\n        foreach ($indexes as $name => $index) {\n            $result[] = new IndexConstraint([\n                'isPrimary' => (bool) $index[0]['index_is_primary'],\n                'isUnique' => (bool) $index[0]['index_is_unique'],\n                'name' => $name,\n                'columnNames' => ArrayHelper::getColumn($index, 'column_name'),\n            ]);\n        }\n\n        return $result;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableUniques($tableName)\n    {\n        return $this->loadTableConstraints($tableName, 'uniques');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableChecks($tableName)\n    {\n        return $this->loadTableConstraints($tableName, 'checks');\n    }\n\n    /**\n     * {@inheritdoc}\n     * @throws NotSupportedException if this method is called.\n     */\n    protected function loadTableDefaultValues($tableName)\n    {\n        throw new NotSupportedException('Oracle does not support default value constraints.');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function releaseSavepoint($name)\n    {\n        // does nothing as Oracle does not support this\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function quoteSimpleTableName($name)\n    {\n        return strpos($name, '\"') !== false ? $name : '\"' . $name . '\"';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function createQueryBuilder()\n    {\n        return Yii::createObject(QueryBuilder::className(), [$this->db]);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function createColumnSchemaBuilder($type, $length = null)\n    {\n        return Yii::createObject(ColumnSchemaBuilder::className(), [$type, $length]);\n    }\n\n    /**\n     * Resolves the table name and schema name (if any).\n     *\n     * @param TableSchema $table the table metadata object\n     * @param string $name the table name\n     */\n    protected function resolveTableNames($table, $name)\n    {\n        $parts = explode('.', str_replace('\"', '', $name));\n        if (isset($parts[1])) {\n            $table->schemaName = $parts[0];\n            $table->name = $parts[1];\n        } else {\n            $table->schemaName = $this->defaultSchema;\n            $table->name = $name;\n        }\n\n        $table->fullName = $table->schemaName !== $this->defaultSchema ? $table->schemaName . '.' . $table->name : $table->name;\n    }\n\n    /**\n     * Collects the table column metadata.\n     * @param TableSchema $table the table schema\n     * @return bool whether the table exists\n     */\n    protected function findColumns($table)\n    {\n        $sql = <<<'SQL'\nSELECT\n    A.COLUMN_NAME,\n    A.DATA_TYPE,\n    A.DATA_PRECISION,\n    A.DATA_SCALE,\n    (\n      CASE A.CHAR_USED WHEN 'C' THEN A.CHAR_LENGTH\n        ELSE A.DATA_LENGTH\n      END\n    ) AS DATA_LENGTH,\n    A.NULLABLE,\n    A.DATA_DEFAULT,\n    COM.COMMENTS AS COLUMN_COMMENT\nFROM ALL_TAB_COLUMNS A\n    INNER JOIN ALL_OBJECTS B ON B.OWNER = A.OWNER AND LTRIM(B.OBJECT_NAME) = LTRIM(A.TABLE_NAME)\n    LEFT JOIN ALL_COL_COMMENTS COM ON (A.OWNER = COM.OWNER AND A.TABLE_NAME = COM.TABLE_NAME AND A.COLUMN_NAME = COM.COLUMN_NAME)\nWHERE\n    A.OWNER = :schemaName\n    AND B.OBJECT_TYPE IN ('TABLE', 'VIEW', 'MATERIALIZED VIEW')\n    AND B.OBJECT_NAME = :tableName\nORDER BY A.COLUMN_ID\nSQL;\n\n        try {\n            $columns = $this->db->createCommand($sql, [\n                ':tableName' => $table->name,\n                ':schemaName' => $table->schemaName,\n            ])->queryAll();\n        } catch (\\Exception $e) {\n            return false;\n        }\n\n        if (empty($columns)) {\n            return false;\n        }\n\n        foreach ($columns as $column) {\n            if ($this->db->slavePdo->getAttribute(\\PDO::ATTR_CASE) === \\PDO::CASE_LOWER) {\n                $column = array_change_key_case($column, CASE_UPPER);\n            }\n            $c = $this->createColumn($column);\n            $table->columns[$c->name] = $c;\n        }\n\n        return true;\n    }\n\n    /**\n     * Sequence name of table.\n     *\n     * @param string $tableName\n     * @internal param \\yii\\db\\TableSchema $table->name the table schema\n     * @return string|null whether the sequence exists\n     */\n    protected function getTableSequenceName($tableName)\n    {\n        $sequenceNameSql = <<<'SQL'\nSELECT\n    UD.REFERENCED_NAME AS SEQUENCE_NAME\nFROM USER_DEPENDENCIES UD\n    JOIN USER_TRIGGERS UT ON (UT.TRIGGER_NAME = UD.NAME)\nWHERE\n    UT.TABLE_NAME = :tableName\n    AND UD.TYPE = 'TRIGGER'\n    AND UD.REFERENCED_TYPE = 'SEQUENCE'\nSQL;\n        $sequenceName = $this->db->createCommand($sequenceNameSql, [':tableName' => $tableName])->queryScalar();\n        return $sequenceName === false ? null : $sequenceName;\n    }\n\n    /**\n     * @Overrides method in class 'Schema'\n     * @see https://www.php.net/manual/en/function.PDO-lastInsertId.php -> Oracle does not support this\n     *\n     * Returns the ID of the last inserted row or sequence value.\n     * @param string $sequenceName name of the sequence object (required by some DBMS)\n     * @return string the row ID of the last row inserted, or the last value retrieved from the sequence object\n     * @throws InvalidCallException if the DB connection is not active\n     */\n    public function getLastInsertID($sequenceName = '')\n    {\n        if ($this->db->isActive) {\n            // get the last insert id from the master connection\n            $sequenceName = $this->quoteSimpleTableName($sequenceName);\n            return $this->db->useMaster(function (Connection $db) use ($sequenceName) {\n                return $db->createCommand(\"SELECT {$sequenceName}.CURRVAL FROM DUAL\")->queryScalar();\n            });\n        } else {\n            throw new InvalidCallException('DB Connection is not active.');\n        }\n    }\n\n    /**\n     * Creates ColumnSchema instance.\n     *\n     * @param array $column\n     * @return T\n     */\n    protected function createColumn($column)\n    {\n        $c = $this->createColumnSchema();\n        $c->name = $column['COLUMN_NAME'];\n        $c->allowNull = $column['NULLABLE'] === 'Y';\n        $c->comment = $column['COLUMN_COMMENT'] === null ? '' : $column['COLUMN_COMMENT'];\n        $c->isPrimaryKey = false;\n        $this->extractColumnType($c, $column['DATA_TYPE'], $column['DATA_PRECISION'], $column['DATA_SCALE'], $column['DATA_LENGTH']);\n        $this->extractColumnSize($c, $column['DATA_TYPE'], $column['DATA_PRECISION'], $column['DATA_SCALE'], $column['DATA_LENGTH']);\n\n        $c->phpType = $this->getColumnPhpType($c);\n\n        if (!$c->isPrimaryKey) {\n            if (stripos((string) $column['DATA_DEFAULT'], 'timestamp') !== false) {\n                $c->defaultValue = null;\n            } else {\n                $defaultValue = (string) $column['DATA_DEFAULT'];\n                if ($c->type === 'timestamp' && $defaultValue === 'CURRENT_TIMESTAMP') {\n                    $c->defaultValue = new Expression('CURRENT_TIMESTAMP');\n                } else {\n                    if ($defaultValue !== null) {\n                        if (\n                            strlen($defaultValue) > 2\n                            && strncmp($defaultValue, \"'\", 1) === 0\n                            && substr($defaultValue, -1) === \"'\"\n                        ) {\n                            $defaultValue = substr($defaultValue, 1, -1);\n                        } else {\n                            $defaultValue = trim($defaultValue);\n                        }\n                    }\n                    $c->defaultValue = $c->phpTypecast($defaultValue);\n                }\n            }\n        }\n\n        return $c;\n    }\n\n    /**\n     * Finds constraints and fills them into TableSchema object passed.\n     * @param TableSchema $table\n     */\n    protected function findConstraints($table)\n    {\n        $sql = <<<'SQL'\nSELECT\n    /*+ PUSH_PRED(C) PUSH_PRED(D) PUSH_PRED(E) */\n    D.CONSTRAINT_NAME,\n    D.CONSTRAINT_TYPE,\n    C.COLUMN_NAME,\n    C.POSITION,\n    D.R_CONSTRAINT_NAME,\n    E.TABLE_NAME AS TABLE_REF,\n    F.COLUMN_NAME AS COLUMN_REF,\n    C.TABLE_NAME\nFROM ALL_CONS_COLUMNS C\n    INNER JOIN ALL_CONSTRAINTS D ON D.OWNER = C.OWNER AND D.CONSTRAINT_NAME = C.CONSTRAINT_NAME\n    LEFT JOIN ALL_CONSTRAINTS E ON E.OWNER = D.R_OWNER AND E.CONSTRAINT_NAME = D.R_CONSTRAINT_NAME\n    LEFT JOIN ALL_CONS_COLUMNS F ON F.OWNER = E.OWNER AND F.CONSTRAINT_NAME = E.CONSTRAINT_NAME AND F.POSITION = C.POSITION\nWHERE\n    C.OWNER = :schemaName\n    AND C.TABLE_NAME = :tableName\nORDER BY D.CONSTRAINT_NAME, C.POSITION\nSQL;\n        $command = $this->db->createCommand($sql, [\n            ':tableName' => $table->name,\n            ':schemaName' => $table->schemaName,\n        ]);\n        $constraints = [];\n        foreach ($command->queryAll() as $row) {\n            if ($this->db->slavePdo->getAttribute(\\PDO::ATTR_CASE) === \\PDO::CASE_LOWER) {\n                $row = array_change_key_case($row, CASE_UPPER);\n            }\n\n            if ($row['CONSTRAINT_TYPE'] === 'P') {\n                $table->columns[$row['COLUMN_NAME']]->isPrimaryKey = true;\n                $table->primaryKey[] = $row['COLUMN_NAME'];\n                if (empty($table->sequenceName)) {\n                    $table->sequenceName = $this->getTableSequenceName($table->name);\n                }\n            }\n\n            if ($row['CONSTRAINT_TYPE'] !== 'R') {\n                // this condition is not checked in SQL WHERE because of an Oracle Bug:\n                // see https://github.com/yiisoft/yii2/pull/8844\n                continue;\n            }\n\n            $name = $row['CONSTRAINT_NAME'];\n            if (!isset($constraints[$name])) {\n                $constraints[$name] = [\n                    'tableName' => $row['TABLE_REF'],\n                    'columns' => [],\n                ];\n            }\n            $constraints[$name]['columns'][$row['COLUMN_NAME']] = $row['COLUMN_REF'];\n        }\n\n        foreach ($constraints as $constraint) {\n            $name = current(array_keys($constraint));\n\n            $table->foreignKeys[$name] = array_merge([$constraint['tableName']], $constraint['columns']);\n        }\n    }\n\n    /**\n     * Returns all unique indexes for the given table.\n     * Each array element is of the following structure:.\n     *\n     * ```\n     * [\n     *     'IndexName1' => ['col1' [, ...]],\n     *     'IndexName2' => ['col2' [, ...]],\n     * ]\n     * ```\n     *\n     * @param TableSchema $table the table metadata\n     * @return array all unique indexes for the given table.\n     * @since 2.0.4\n     */\n    public function findUniqueIndexes($table)\n    {\n        $query = <<<'SQL'\nSELECT\n    DIC.INDEX_NAME,\n    DIC.COLUMN_NAME\nFROM ALL_INDEXES DI\n    INNER JOIN ALL_IND_COLUMNS DIC ON DI.TABLE_NAME = DIC.TABLE_NAME AND DI.INDEX_NAME = DIC.INDEX_NAME\nWHERE\n    DI.UNIQUENESS = 'UNIQUE'\n    AND DIC.TABLE_OWNER = :schemaName\n    AND DIC.TABLE_NAME = :tableName\nORDER BY DIC.TABLE_NAME, DIC.INDEX_NAME, DIC.COLUMN_POSITION\nSQL;\n        $result = [];\n        $command = $this->db->createCommand($query, [\n            ':tableName' => $table->name,\n            ':schemaName' => $table->schemaName,\n        ]);\n        foreach ($command->queryAll() as $row) {\n            $result[$row['INDEX_NAME']][] = $row['COLUMN_NAME'];\n        }\n\n        return $result;\n    }\n\n    /**\n     * Extracts the data types for the given column.\n     * @param ColumnSchema $column\n     * @param string $dbType DB type\n     * @param string $precision total number of digits.\n     * This parameter is available since version 2.0.4.\n     * @param string $scale number of digits on the right of the decimal separator.\n     * This parameter is available since version 2.0.4.\n     * @param string $length length for character types.\n     * This parameter is available since version 2.0.4.\n     */\n    protected function extractColumnType($column, $dbType, $precision, $scale, $length)\n    {\n        $column->dbType = $dbType;\n\n        if (strpos($dbType, 'FLOAT') !== false || strpos($dbType, 'DOUBLE') !== false) {\n            $column->type = 'double';\n        } elseif (strpos($dbType, 'NUMBER') !== false) {\n            if ($scale === null || $scale > 0) {\n                $column->type = 'decimal';\n            } else {\n                $column->type = 'integer';\n            }\n        } elseif (strpos($dbType, 'INTEGER') !== false) {\n            $column->type = 'integer';\n        } elseif (strpos($dbType, 'BLOB') !== false) {\n            $column->type = 'binary';\n        } elseif (strpos($dbType, 'CLOB') !== false) {\n            $column->type = 'text';\n        } elseif (strpos($dbType, 'TIMESTAMP') !== false) {\n            $column->type = 'timestamp';\n        } else {\n            $column->type = 'string';\n        }\n    }\n\n    /**\n     * Extracts size, precision and scale information from column's DB type.\n     * @param ColumnSchema $column\n     * @param string $dbType the column's DB type\n     * @param string $precision total number of digits.\n     * This parameter is available since version 2.0.4.\n     * @param string $scale number of digits on the right of the decimal separator.\n     * This parameter is available since version 2.0.4.\n     * @param string $length length for character types.\n     * This parameter is available since version 2.0.4.\n     */\n    protected function extractColumnSize($column, $dbType, $precision, $scale, $length)\n    {\n        $column->size = trim((string) $length) === '' ? null : (int) $length;\n        $column->precision = trim((string) $precision) === '' ? null : (int) $precision;\n        $column->scale = trim((string) $scale) === '' ? null : (int) $scale;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function insert($table, $columns)\n    {\n        $params = [];\n        $returnParams = [];\n        $sql = $this->db->getQueryBuilder()->insert($table, $columns, $params);\n        $tableSchema = $this->getTableSchema($table);\n        $returnColumns = $tableSchema->primaryKey;\n        if (!empty($returnColumns)) {\n            $columnSchemas = $tableSchema->columns;\n            $returning = [];\n            foreach ((array) $returnColumns as $name) {\n                $phName = QueryBuilder::PARAM_PREFIX . (count($params) + count($returnParams));\n                $returnParams[$phName] = [\n                    'column' => $name,\n                    'value' => '',\n                ];\n                if (!isset($columnSchemas[$name]) || $columnSchemas[$name]->phpType !== 'integer') {\n                    $returnParams[$phName]['dataType'] = \\PDO::PARAM_STR;\n                } else {\n                    $returnParams[$phName]['dataType'] = \\PDO::PARAM_INT;\n                }\n                $returnParams[$phName]['size'] = isset($columnSchemas[$name]->size) ? $columnSchemas[$name]->size : -1;\n                $returning[] = $this->quoteColumnName($name);\n            }\n            $sql .= ' RETURNING ' . implode(', ', $returning) . ' INTO ' . implode(', ', array_keys($returnParams));\n        }\n\n        $command = $this->db->createCommand($sql, $params);\n        $command->prepare(false);\n\n        foreach ($returnParams as $name => &$value) {\n            $command->pdoStatement->bindParam($name, $value['value'], $value['dataType'], $value['size']);\n        }\n\n        if (!$command->execute()) {\n            return false;\n        }\n\n        $result = [];\n        foreach ($returnParams as $value) {\n            $result[$value['column']] = $value['value'];\n        }\n\n        return $result;\n    }\n\n    /**\n     * Loads multiple types of constraints and returns the specified ones.\n     * @param string $tableName table name.\n     * @param string $returnType return type:\n     * - primaryKey\n     * - foreignKeys\n     * - uniques\n     * - checks\n     * @return mixed constraints.\n     */\n    private function loadTableConstraints($tableName, $returnType)\n    {\n        static $sql = <<<'SQL'\nSELECT\n    /*+ PUSH_PRED(\"uc\") PUSH_PRED(\"uccol\") PUSH_PRED(\"fuc\") */\n    \"uc\".\"CONSTRAINT_NAME\" AS \"name\",\n    \"uccol\".\"COLUMN_NAME\" AS \"column_name\",\n    \"uc\".\"CONSTRAINT_TYPE\" AS \"type\",\n    \"fuc\".\"OWNER\" AS \"foreign_table_schema\",\n    \"fuc\".\"TABLE_NAME\" AS \"foreign_table_name\",\n    \"fuccol\".\"COLUMN_NAME\" AS \"foreign_column_name\",\n    \"uc\".\"DELETE_RULE\" AS \"on_delete\",\n    \"uc\".\"SEARCH_CONDITION\" AS \"check_expr\"\nFROM \"USER_CONSTRAINTS\" \"uc\"\nINNER JOIN \"USER_CONS_COLUMNS\" \"uccol\"\n    ON \"uccol\".\"OWNER\" = \"uc\".\"OWNER\" AND \"uccol\".\"CONSTRAINT_NAME\" = \"uc\".\"CONSTRAINT_NAME\"\nLEFT JOIN \"USER_CONSTRAINTS\" \"fuc\"\n    ON \"fuc\".\"OWNER\" = \"uc\".\"R_OWNER\" AND \"fuc\".\"CONSTRAINT_NAME\" = \"uc\".\"R_CONSTRAINT_NAME\"\nLEFT JOIN \"USER_CONS_COLUMNS\" \"fuccol\"\n    ON \"fuccol\".\"OWNER\" = \"fuc\".\"OWNER\" AND \"fuccol\".\"CONSTRAINT_NAME\" = \"fuc\".\"CONSTRAINT_NAME\" AND \"fuccol\".\"POSITION\" = \"uccol\".\"POSITION\"\nWHERE \"uc\".\"OWNER\" = :schemaName AND \"uc\".\"TABLE_NAME\" = :tableName\nORDER BY \"uccol\".\"POSITION\" ASC\nSQL;\n\n        $resolvedName = $this->resolveTableName($tableName);\n        $constraints = $this->db->createCommand($sql, [\n            ':schemaName' => $resolvedName->schemaName,\n            ':tableName' => $resolvedName->name,\n        ])->queryAll();\n        $constraints = $this->normalizePdoRowKeyCase($constraints, true);\n        $constraints = ArrayHelper::index($constraints, null, ['type', 'name']);\n        $result = [\n            'primaryKey' => null,\n            'foreignKeys' => [],\n            'uniques' => [],\n            'checks' => [],\n        ];\n        foreach ($constraints as $type => $names) {\n            foreach ($names as $name => $constraint) {\n                switch ($type) {\n                    case 'P':\n                        $result['primaryKey'] = new Constraint([\n                            'name' => $name,\n                            'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),\n                        ]);\n                        break;\n                    case 'R':\n                        $result['foreignKeys'][] = new ForeignKeyConstraint([\n                            'name' => $name,\n                            'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),\n                            'foreignSchemaName' => $constraint[0]['foreign_table_schema'],\n                            'foreignTableName' => $constraint[0]['foreign_table_name'],\n                            'foreignColumnNames' => ArrayHelper::getColumn($constraint, 'foreign_column_name'),\n                            'onDelete' => $constraint[0]['on_delete'],\n                            'onUpdate' => null,\n                        ]);\n                        break;\n                    case 'U':\n                        $result['uniques'][] = new Constraint([\n                            'name' => $name,\n                            'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),\n                        ]);\n                        break;\n                    case 'C':\n                        $result['checks'][] = new CheckConstraint([\n                            'name' => $name,\n                            'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),\n                            'expression' => $constraint[0]['check_expr'],\n                        ]);\n                        break;\n                }\n            }\n        }\n        foreach ($result as $type => $data) {\n            $this->setTableMetadata($tableName, $type, $data);\n        }\n\n        return $result[$returnType];\n    }\n}\n"
  },
  {
    "path": "framework/db/oci/conditions/InConditionBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\oci\\conditions;\n\nuse yii\\db\\conditions\\InCondition;\nuse yii\\db\\ExpressionInterface;\n\n/**\n * {@inheritdoc}\n */\nclass InConditionBuilder extends \\yii\\db\\conditions\\InConditionBuilder\n{\n    /**\n     * Method builds the raw SQL from the $expression that will not be additionally\n     * escaped or quoted.\n     *\n     * @param ExpressionInterface|InCondition $expression the expression to be built.\n     * @param array $params the binding parameters.\n     * @return string the raw SQL that will not be additionally escaped or quoted.\n     */\n    public function build(ExpressionInterface $expression, array &$params = [])\n    {\n        $splitCondition = $this->splitCondition($expression, $params);\n        if ($splitCondition !== null) {\n            return $splitCondition;\n        }\n\n        return parent::build($expression, $params);\n    }\n\n    /**\n     * Oracle DBMS does not support more than 1000 parameters in `IN` condition.\n     * This method splits long `IN` condition into series of smaller ones.\n     *\n     * @param ExpressionInterface|InCondition $condition the expression to be built.\n     * @param array $params the binding parameters.\n     * @return string|null null when split is not required. Otherwise - built SQL condition.\n     */\n    protected function splitCondition(InCondition $condition, &$params)\n    {\n        $operator = $condition->getOperator();\n        $values = $condition->getValues();\n        $column = $condition->getColumn();\n\n        if ($values instanceof \\Traversable) {\n            $values = iterator_to_array($values);\n        }\n\n        if (!is_array($values)) {\n            return null;\n        }\n\n        $maxParameters = 1000;\n        $count = count($values);\n        if ($count <= $maxParameters) {\n            return null;\n        }\n\n        $slices = [];\n        for ($i = 0; $i < $count; $i += $maxParameters) {\n            $slices[] = $this->queryBuilder->createConditionFromArray([$operator, $column, array_slice($values, $i, $maxParameters)]);\n        }\n        array_unshift($slices, ($operator === 'IN') ? 'OR' : 'AND');\n\n        return $this->queryBuilder->buildCondition($slices, $params);\n    }\n}\n"
  },
  {
    "path": "framework/db/oci/conditions/LikeConditionBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\oci\\conditions;\n\nuse yii\\db\\ExpressionInterface;\n\n/**\n * {@inheritdoc}\n */\nclass LikeConditionBuilder extends \\yii\\db\\conditions\\LikeConditionBuilder\n{\n    /**\n     * {@inheritdoc}\n     */\n    protected $escapeCharacter = '!';\n    /**\n     * `\\` is initialized in [[buildLikeCondition()]] method since\n     * we need to choose replacement value based on [[\\yii\\db\\Schema::quoteValue()]].\n     * {@inheritdoc}\n     */\n    protected $escapingReplacements = [\n        '%' => '!%',\n        '_' => '!_',\n        '!' => '!!',\n    ];\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function build(ExpressionInterface $expression, array &$params = [])\n    {\n        if (!isset($this->escapingReplacements['\\\\'])) {\n            /*\n             * Different pdo_oci8 versions may or may not implement PDO::quote(), so\n             * yii\\db\\Schema::quoteValue() may or may not quote \\.\n             */\n            $this->escapingReplacements['\\\\'] = substr($this->queryBuilder->db->quoteValue('\\\\'), 1, -1);\n        }\n\n        return parent::build($expression, $params);\n    }\n}\n"
  },
  {
    "path": "framework/db/pgsql/ArrayExpressionBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\pgsql;\n\nuse yii\\db\\ArrayExpression;\nuse yii\\db\\ExpressionBuilderInterface;\nuse yii\\db\\ExpressionBuilderTrait;\nuse yii\\db\\ExpressionInterface;\nuse yii\\db\\JsonExpression;\nuse yii\\db\\Query;\n\n/**\n * Class ArrayExpressionBuilder builds [[ArrayExpression]] for PostgreSQL DBMS.\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n */\nclass ArrayExpressionBuilder implements ExpressionBuilderInterface\n{\n    use ExpressionBuilderTrait;\n\n\n    /**\n     * {@inheritdoc}\n     * @param ArrayExpression|ExpressionInterface $expression the expression to be built\n     */\n    public function build(ExpressionInterface $expression, array &$params = [])\n    {\n        $value = $expression->getValue();\n        if ($value === null) {\n            return 'NULL';\n        }\n\n        if ($value instanceof Query) {\n            list ($sql, $params) = $this->queryBuilder->build($value, $params);\n            return $this->buildSubqueryArray($sql, $expression);\n        }\n\n        $placeholders = $this->buildPlaceholders($expression, $params);\n\n        return 'ARRAY[' . implode(', ', $placeholders) . ']' . $this->getTypehint($expression);\n    }\n\n    /**\n     * Builds placeholders array out of $expression values\n     * @param ExpressionInterface|ArrayExpression $expression\n     * @param array $params the binding parameters.\n     * @return array\n     */\n    protected function buildPlaceholders(ExpressionInterface $expression, &$params)\n    {\n        $value = $expression->getValue();\n\n        $placeholders = [];\n        if ($value === null || !is_array($value) && !$value instanceof \\Traversable) {\n            return $placeholders;\n        }\n\n        if ($expression->getDimension() > 1) {\n            foreach ($value as $item) {\n                $placeholders[] = $this->build($this->unnestArrayExpression($expression, $item), $params);\n            }\n            return $placeholders;\n        }\n\n        foreach ($value as $item) {\n            if ($item instanceof Query) {\n                list ($sql, $params) = $this->queryBuilder->build($item, $params);\n                $placeholders[] = $this->buildSubqueryArray($sql, $expression);\n                continue;\n            }\n\n            $item = $this->typecastValue($expression, $item);\n            if ($item instanceof ExpressionInterface) {\n                $placeholders[] = $this->queryBuilder->buildExpression($item, $params);\n                continue;\n            }\n\n            $placeholders[] = $this->queryBuilder->bindParam($item, $params);\n        }\n\n        return $placeholders;\n    }\n\n    /**\n     * @param ArrayExpression $expression\n     * @param mixed $value\n     * @return ArrayExpression\n     */\n    private function unnestArrayExpression(ArrayExpression $expression, $value)\n    {\n        $expressionClass = get_class($expression);\n\n        return new $expressionClass($value, $expression->getType(), $expression->getDimension() - 1);\n    }\n\n    /**\n     * @param ArrayExpression $expression\n     * @return string the typecast expression based on [[type]].\n     */\n    protected function getTypehint(ArrayExpression $expression)\n    {\n        if ($expression->getType() === null) {\n            return '';\n        }\n\n        $result = '::' . $expression->getType();\n        $result .= str_repeat('[]', $expression->getDimension());\n\n        return $result;\n    }\n\n    /**\n     * Build an array expression from a subquery SQL.\n     *\n     * @param string $sql the subquery SQL.\n     * @param ArrayExpression $expression\n     * @return string the subquery array expression.\n     */\n    protected function buildSubqueryArray($sql, ArrayExpression $expression)\n    {\n        return 'ARRAY(' . $sql . ')' . $this->getTypehint($expression);\n    }\n\n    /**\n     * Casts $value to use in $expression\n     *\n     * @param ArrayExpression $expression\n     * @param mixed $value\n     * @return mixed\n     */\n    protected function typecastValue(ArrayExpression $expression, $value)\n    {\n        if ($value instanceof ExpressionInterface) {\n            return $value;\n        }\n\n        if (in_array($expression->getType(), [Schema::TYPE_JSON, Schema::TYPE_JSONB], true)) {\n            return new JsonExpression($value);\n        }\n\n        return $value;\n    }\n}\n"
  },
  {
    "path": "framework/db/pgsql/ArrayParser.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\pgsql;\n\n/**\n * The class converts PostgreSQL array representation to PHP array\n *\n * @author Sergei Tigrov <rrr-r@ya.ru>\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n * @phpcs:disable Squiz.NamingConventions.ValidVariableName.PrivateNoUnderscore\n */\nclass ArrayParser\n{\n    /**\n     * @var string Character used in array\n     */\n    private $delimiter = ',';\n\n\n    /**\n     * Convert array from PostgreSQL to PHP\n     *\n     * @param string $value string to be converted\n     * @return array|null\n     */\n    public function parse($value)\n    {\n        if ($value === null) {\n            return null;\n        }\n\n        if ($value === '{}') {\n            return [];\n        }\n\n        return $this->parseArray($value);\n    }\n\n    /**\n     * Pares PgSQL array encoded in string\n     *\n     * @param string $value\n     * @param int $i parse starting position\n     * @return array\n     */\n    private function parseArray($value, &$i = 0)\n    {\n        $result = [];\n        $len = strlen($value);\n        for (++$i; $i < $len; ++$i) {\n            switch ($value[$i]) {\n                case '{':\n                    $result[] = $this->parseArray($value, $i);\n                    break;\n                case '}':\n                    break 2;\n                case $this->delimiter:\n                    if (empty($result)) { // `{}` case\n                        $result[] = null;\n                    }\n                    if (in_array($value[$i + 1], [$this->delimiter, '}'], true)) { // `{,}` case\n                        $result[] = null;\n                    }\n                    break;\n                default:\n                    $result[] = $this->parseString($value, $i);\n            }\n        }\n\n        return $result;\n    }\n\n    /**\n     * Parses PgSQL encoded string\n     *\n     * @param string $value\n     * @param int $i parse starting position\n     * @return string|null\n     */\n    private function parseString($value, &$i)\n    {\n        $isQuoted = $value[$i] === '\"';\n        $stringEndChars = $isQuoted ? ['\"'] : [$this->delimiter, '}'];\n        $result = '';\n        $len = strlen($value);\n        for ($i += $isQuoted ? 1 : 0; $i < $len; ++$i) {\n            if (in_array($value[$i], ['\\\\', '\"'], true) && in_array($value[$i + 1], [$value[$i], '\"'], true)) {\n                ++$i;\n            } elseif (in_array($value[$i], $stringEndChars, true)) {\n                break;\n            }\n\n            $result .= $value[$i];\n        }\n\n        $i -= $isQuoted ? 0 : 1;\n\n        if (!$isQuoted && $result === 'NULL') {\n            $result = null;\n        }\n\n        return $result;\n    }\n}\n"
  },
  {
    "path": "framework/db/pgsql/ColumnSchema.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\pgsql;\n\nuse yii\\db\\ArrayExpression;\nuse yii\\db\\ExpressionInterface;\nuse yii\\db\\JsonExpression;\n\n/**\n * Class ColumnSchema for PostgreSQL database.\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n */\nclass ColumnSchema extends \\yii\\db\\ColumnSchema\n{\n    /**\n     * @var int the dimension of array. Defaults to 0, means this column is not an array.\n     */\n    public $dimension = 0;\n    /**\n     * @var bool whether the column schema should OMIT using JSON support feature.\n     * You can use this property to make upgrade to Yii 2.0.14 easier.\n     * Default to `false`, meaning JSON support is enabled.\n     *\n     * @since 2.0.14.1\n     * @deprecated Since 2.0.14.1 and will be removed in 2.1.\n     */\n    public $disableJsonSupport = false;\n    /**\n     * @var bool whether the column schema should OMIT using PgSQL Arrays support feature.\n     * You can use this property to make upgrade to Yii 2.0.14 easier.\n     * Default to `false`, meaning Arrays support is enabled.\n     *\n     * @since 2.0.14.1\n     * @deprecated Since 2.0.14.1 and will be removed in 2.1.\n     */\n    public $disableArraySupport = false;\n    /**\n     * @var bool whether the Array column value should be unserialized to an [[ArrayExpression]] object.\n     * You can use this property to make upgrade to Yii 2.0.14 easier.\n     * Default to `true`, meaning arrays are unserialized to [[ArrayExpression]] objects.\n     *\n     * @since 2.0.14.1\n     * @deprecated Since 2.0.14.1 and will be removed in 2.1.\n     */\n    public $deserializeArrayColumnToArrayExpression = true;\n    /**\n     * @var string name of associated sequence if column is auto-incremental\n     * @since 2.0.29\n     */\n    public $sequenceName;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function dbTypecast($value)\n    {\n        if ($value === null) {\n            return $value;\n        }\n\n        if ($value instanceof ExpressionInterface) {\n            return $value;\n        }\n\n        if ($this->dimension > 0) {\n            return $this->disableArraySupport\n                ? (string) $value\n                : new ArrayExpression($value, $this->dbType, $this->dimension);\n        }\n        if (!$this->disableJsonSupport && in_array($this->dbType, [Schema::TYPE_JSON, Schema::TYPE_JSONB], true)) {\n            return new JsonExpression($value, $this->dbType);\n        }\n\n        return $this->typecast($value);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function phpTypecast($value)\n    {\n        if ($this->dimension > 0) {\n            if ($this->disableArraySupport) {\n                return $value;\n            }\n            if (!is_array($value)) {\n                $value = $this->getArrayParser()->parse($value);\n            }\n            if (is_array($value)) {\n                array_walk_recursive($value, function (&$val, $key) {\n                    $val = $this->phpTypecastValue($val);\n                });\n            } elseif ($value === null) {\n                return null;\n            }\n\n            return $this->deserializeArrayColumnToArrayExpression\n                ? new ArrayExpression($value, $this->dbType, $this->dimension)\n                : $value;\n        }\n\n        return $this->phpTypecastValue($value);\n    }\n\n    /**\n     * Casts $value after retrieving from the DBMS to PHP representation.\n     *\n     * @param string|null $value\n     * @return bool|mixed|null\n     */\n    protected function phpTypecastValue($value)\n    {\n        if ($value === null) {\n            return null;\n        }\n\n        switch ($this->type) {\n            case Schema::TYPE_BOOLEAN:\n                switch (strtolower($value)) {\n                    case 't':\n                    case 'true':\n                        return true;\n                    case 'f':\n                    case 'false':\n                        return false;\n                }\n                return (bool) $value;\n            case Schema::TYPE_JSON:\n                return $this->disableJsonSupport ? $value : json_decode($value, true);\n        }\n\n        return parent::phpTypecast($value);\n    }\n\n    /**\n     * Creates instance of ArrayParser\n     *\n     * @return ArrayParser\n     */\n    protected function getArrayParser()\n    {\n        static $parser = null;\n\n        if ($parser === null) {\n            $parser = new ArrayParser();\n        }\n\n        return $parser;\n    }\n}\n"
  },
  {
    "path": "framework/db/pgsql/JsonExpressionBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\pgsql;\n\nuse yii\\db\\ArrayExpression;\nuse yii\\db\\ExpressionBuilderInterface;\nuse yii\\db\\ExpressionBuilderTrait;\nuse yii\\db\\ExpressionInterface;\nuse yii\\db\\JsonExpression;\nuse yii\\db\\Query;\nuse yii\\helpers\\Json;\n\n/**\n * Class JsonExpressionBuilder builds [[JsonExpression]] for PostgreSQL DBMS.\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n */\nclass JsonExpressionBuilder implements ExpressionBuilderInterface\n{\n    use ExpressionBuilderTrait;\n\n\n    /**\n     * {@inheritdoc}\n     * @param JsonExpression|ExpressionInterface $expression the expression to be built\n     */\n    public function build(ExpressionInterface $expression, array &$params = [])\n    {\n        $value = $expression->getValue();\n\n        if ($value instanceof Query) {\n            list ($sql, $params) = $this->queryBuilder->build($value, $params);\n            return \"($sql)\" . $this->getTypecast($expression);\n        }\n        if ($value instanceof ArrayExpression) {\n            $placeholder = 'array_to_json(' . $this->queryBuilder->buildExpression($value, $params) . ')';\n        } else {\n            $placeholder = $this->queryBuilder->bindParam(Json::encode($value), $params);\n        }\n\n        return $placeholder . $this->getTypecast($expression);\n    }\n\n    /**\n     * @param JsonExpression $expression\n     * @return string the typecast expression based on [[type]].\n     */\n    protected function getTypecast(JsonExpression $expression)\n    {\n        if ($expression->getType() === null) {\n            return '';\n        }\n\n        return '::' . $expression->getType();\n    }\n}\n"
  },
  {
    "path": "framework/db/pgsql/QueryBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\pgsql;\n\nuse yii\\base\\InvalidArgumentException;\nuse yii\\db\\Expression;\nuse yii\\db\\ExpressionInterface;\nuse yii\\db\\Query;\nuse yii\\db\\PdoValue;\nuse yii\\helpers\\StringHelper;\n\n/**\n * QueryBuilder is the query builder for PostgreSQL databases.\n *\n * @author Gevik Babakhani <gevikb@gmail.com>\n * @since 2.0\n */\nclass QueryBuilder extends \\yii\\db\\QueryBuilder\n{\n    /**\n     * Defines a UNIQUE index for [[createIndex()]].\n     * @since 2.0.6\n     */\n    public const INDEX_UNIQUE = 'unique';\n    /**\n     * Defines a B-tree index for [[createIndex()]].\n     * @since 2.0.6\n     */\n    public const INDEX_B_TREE = 'btree';\n    /**\n     * Defines a hash index for [[createIndex()]].\n     * @since 2.0.6\n     */\n    public const INDEX_HASH = 'hash';\n    /**\n     * Defines a GiST index for [[createIndex()]].\n     * @since 2.0.6\n     */\n    public const INDEX_GIST = 'gist';\n    /**\n     * Defines a GIN index for [[createIndex()]].\n     * @since 2.0.6\n     */\n    public const INDEX_GIN = 'gin';\n    /**\n     * @var array mapping from abstract column types (keys) to physical column types (values).\n     */\n    public $typeMap = [\n        Schema::TYPE_PK => 'serial NOT NULL PRIMARY KEY',\n        Schema::TYPE_UPK => 'serial NOT NULL PRIMARY KEY',\n        Schema::TYPE_BIGPK => 'bigserial NOT NULL PRIMARY KEY',\n        Schema::TYPE_UBIGPK => 'bigserial NOT NULL PRIMARY KEY',\n        Schema::TYPE_CHAR => 'char(1)',\n        Schema::TYPE_STRING => 'varchar(255)',\n        Schema::TYPE_TEXT => 'text',\n        Schema::TYPE_TINYINT => 'smallint',\n        Schema::TYPE_SMALLINT => 'smallint',\n        Schema::TYPE_INTEGER => 'integer',\n        Schema::TYPE_BIGINT => 'bigint',\n        Schema::TYPE_FLOAT => 'double precision',\n        Schema::TYPE_DOUBLE => 'double precision',\n        Schema::TYPE_DECIMAL => 'numeric(10,0)',\n        Schema::TYPE_DATETIME => 'timestamp(0)',\n        Schema::TYPE_TIMESTAMP => 'timestamp(0)',\n        Schema::TYPE_TIME => 'time(0)',\n        Schema::TYPE_DATE => 'date',\n        Schema::TYPE_BINARY => 'bytea',\n        Schema::TYPE_BOOLEAN => 'boolean',\n        Schema::TYPE_MONEY => 'numeric(19,4)',\n        Schema::TYPE_JSON => 'jsonb',\n    ];\n\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function defaultConditionClasses()\n    {\n        return array_merge(parent::defaultConditionClasses(), [\n            'ILIKE' => 'yii\\db\\conditions\\LikeCondition',\n            'NOT ILIKE' => 'yii\\db\\conditions\\LikeCondition',\n            'OR ILIKE' => 'yii\\db\\conditions\\LikeCondition',\n            'OR NOT ILIKE' => 'yii\\db\\conditions\\LikeCondition',\n        ]);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function defaultExpressionBuilders()\n    {\n        return array_merge(parent::defaultExpressionBuilders(), [\n            'yii\\db\\ArrayExpression' => 'yii\\db\\pgsql\\ArrayExpressionBuilder',\n            'yii\\db\\JsonExpression' => 'yii\\db\\pgsql\\JsonExpressionBuilder',\n        ]);\n    }\n\n    /**\n     * Builds a SQL statement for creating a new index.\n     * @param string $name the name of the index. The name will be properly quoted by the method.\n     * @param string $table the table that the new index will be created for. The table name will be properly quoted by the method.\n     * @param string|array $columns the column(s) that should be included in the index. If there are multiple columns,\n     * separate them with commas or use an array to represent them. Each column name will be properly quoted\n     * by the method, unless a parenthesis is found in the name.\n     * @param bool|string $unique whether to make this a UNIQUE index constraint. You can pass `true` or [[INDEX_UNIQUE]] to create\n     * a unique index, `false` to make a non-unique index using the default index type, or one of the following constants to specify\n     * the index method to use: [[INDEX_B_TREE]], [[INDEX_HASH]], [[INDEX_GIST]], [[INDEX_GIN]].\n     * @return string the SQL statement for creating a new index.\n     * @see https://www.postgresql.org/docs/8.2/sql-createindex.html\n     */\n    public function createIndex($name, $table, $columns, $unique = false)\n    {\n        if ($unique === self::INDEX_UNIQUE || $unique === true) {\n            $index = false;\n            $unique = true;\n        } else {\n            $index = $unique;\n            $unique = false;\n        }\n\n        return ($unique ? 'CREATE UNIQUE INDEX ' : 'CREATE INDEX ') .\n        $this->db->quoteTableName($name) . ' ON ' .\n        $this->db->quoteTableName($table) .\n        ($index !== false ? \" USING $index\" : '') .\n        ' (' . $this->buildColumns($columns) . ')';\n    }\n\n    /**\n     * Builds a SQL statement for dropping an index.\n     * @param string $name the name of the index to be dropped. The name will be properly quoted by the method.\n     * @param string $table the table whose index is to be dropped. The name will be properly quoted by the method.\n     * @return string the SQL statement for dropping an index.\n     */\n    public function dropIndex($name, $table)\n    {\n        if (strpos($table, '.') !== false && strpos($name, '.') === false) {\n            if (strpos($table, '{{') !== false) {\n                $table = preg_replace('/\\\\{\\\\{(.*?)\\\\}\\\\}/', '\\1', $table);\n                list($schema, $table) = explode('.', $table);\n                if (strpos($schema, '%') === false) {\n                    $name = $schema . '.' . $name;\n                } else {\n                    $name = '{{' . $schema . '.' . $name . '}}';\n                }\n            } else {\n                list($schema) = explode('.', $table);\n                $name = $schema . '.' . $name;\n            }\n        }\n        return 'DROP INDEX ' . $this->db->quoteTableName($name);\n    }\n\n    /**\n     * Builds a SQL statement for renaming a DB table.\n     * @param string $oldName the table to be renamed. The name will be properly quoted by the method.\n     * @param string $newName the new table name. The name will be properly quoted by the method.\n     * @return string the SQL statement for renaming a DB table.\n     */\n    public function renameTable($oldName, $newName)\n    {\n        return 'ALTER TABLE ' . $this->db->quoteTableName($oldName) . ' RENAME TO ' . $this->db->quoteTableName($newName);\n    }\n\n    /**\n     * Creates a SQL statement for resetting the sequence value of a table's primary key.\n     * The sequence will be reset such that the primary key of the next new row inserted\n     * will have the specified value or 1.\n     * @param string $tableName the name of the table whose primary key sequence will be reset\n     * @param mixed $value the value for the primary key of the next new row inserted. If this is not set,\n     * the next new row's primary key will have a value 1.\n     * @return string the SQL statement for resetting sequence\n     * @throws InvalidArgumentException if the table does not exist or there is no sequence associated with the table.\n     */\n    public function resetSequence($tableName, $value = null)\n    {\n        $table = $this->db->getTableSchema($tableName);\n        if ($table !== null && $table->sequenceName !== null) {\n            // c.f. https://www.postgresql.org/docs/8.1/functions-sequence.html\n            $sequence = $this->db->quoteTableName($table->sequenceName);\n            $tableName = $this->db->quoteTableName($tableName);\n            if ($value === null) {\n                $key = $this->db->quoteColumnName(reset($table->primaryKey));\n                $value = \"(SELECT COALESCE(MAX({$key}),0) FROM {$tableName})+1\";\n            } else {\n                $value = (int) $value;\n            }\n\n            return \"SELECT SETVAL('$sequence',$value,false)\";\n        } elseif ($table === null) {\n            throw new InvalidArgumentException(\"Table not found: $tableName\");\n        }\n\n        throw new InvalidArgumentException(\"There is not sequence associated with table '$tableName'.\");\n    }\n\n    /**\n     * Builds a SQL statement for enabling or disabling integrity check.\n     * @param bool $check whether to turn on or off the integrity check.\n     * @param string $schema the schema of the tables.\n     * @param string $table the table name.\n     * @return string the SQL statement for checking integrity\n     */\n    public function checkIntegrity($check = true, $schema = '', $table = '')\n    {\n        /** @var Schema $dbSchema */\n        $dbSchema = $this->db->getSchema();\n        $enable = $check ? 'ENABLE' : 'DISABLE';\n        $schema = $schema ?: $dbSchema->defaultSchema;\n        $tableNames = $table ? [$table] : $dbSchema->getTableNames($schema);\n        $viewNames = $dbSchema->getViewNames($schema);\n        $tableNames = array_diff($tableNames, $viewNames);\n        $command = '';\n\n        foreach ($tableNames as $tableName) {\n            $tableName = $this->db->quoteTableName(\"{$schema}.{$tableName}\");\n            $command .= \"ALTER TABLE $tableName $enable TRIGGER ALL; \";\n        }\n\n        // enable to have ability to alter several tables\n        $this->db->getMasterPdo()->setAttribute(\\PDO::ATTR_EMULATE_PREPARES, true);\n\n        return $command;\n    }\n\n    /**\n     * Builds a SQL statement for truncating a DB table.\n     * Explicitly restarts identity for PGSQL to be consistent with other databases which all do this by default.\n     * @param string $table the table to be truncated. The name will be properly quoted by the method.\n     * @return string the SQL statement for truncating a DB table.\n     */\n    public function truncateTable($table)\n    {\n        return 'TRUNCATE TABLE ' . $this->db->quoteTableName($table) . ' RESTART IDENTITY';\n    }\n\n    /**\n     * Builds a SQL statement for changing the definition of a column.\n     * @param string $table the table whose column is to be changed. The table name will be properly quoted by the method.\n     * @param string $column the name of the column to be changed. The name will be properly quoted by the method.\n     * @param string $type the new column type. The [[getColumnType()]] method will be invoked to convert abstract\n     * column type (if any) into the physical one. Anything that is not recognized as abstract type will be kept\n     * in the generated SQL. For example, 'string' will be turned into 'varchar(255)', while 'string not null'\n     * will become 'varchar(255) not null'. You can also use PostgreSQL-specific syntax such as `SET NOT NULL`.\n     * @return string the SQL statement for changing the definition of a column.\n     */\n    public function alterColumn($table, $column, $type)\n    {\n        $columnName = $this->db->quoteColumnName($column);\n        $tableName = $this->db->quoteTableName($table);\n\n        // https://github.com/yiisoft/yii2/issues/4492\n        // https://www.postgresql.org/docs/9.1/sql-altertable.html\n        if (preg_match('/^(DROP|SET|RESET)\\s+/i', $type)) {\n            return \"ALTER TABLE {$tableName} ALTER COLUMN {$columnName} {$type}\";\n        }\n\n        $type = 'TYPE ' . $this->getColumnType($type);\n\n        $multiAlterStatement = [];\n        $constraintPrefix = preg_replace('/[^a-z0-9_]/i', '', $table . '_' . $column);\n\n        if (preg_match('/\\s+DEFAULT\\s+([\"\\']?\\w*[\"\\']?)/i', $type, $matches)) {\n            $type = preg_replace('/\\s+DEFAULT\\s+([\"\\']?\\w*[\"\\']?)/i', '', $type);\n            $multiAlterStatement[] = \"ALTER COLUMN {$columnName} SET DEFAULT {$matches[1]}\";\n        } else {\n            // safe to drop default even if there was none in the first place\n            $multiAlterStatement[] = \"ALTER COLUMN {$columnName} DROP DEFAULT\";\n        }\n\n        $type = preg_replace('/\\s+NOT\\s+NULL/i', '', $type, -1, $count);\n        if ($count) {\n            $multiAlterStatement[] = \"ALTER COLUMN {$columnName} SET NOT NULL\";\n        } else {\n            // remove additional null if any\n            $type = preg_replace('/\\s+NULL/i', '', $type);\n            // safe to drop not null even if there was none in the first place\n            $multiAlterStatement[] = \"ALTER COLUMN {$columnName} DROP NOT NULL\";\n        }\n\n        if (preg_match('/\\s+CHECK\\s+\\((.+)\\)/i', $type, $matches)) {\n            $type = preg_replace('/\\s+CHECK\\s+\\((.+)\\)/i', '', $type);\n            $multiAlterStatement[] = \"ADD CONSTRAINT {$constraintPrefix}_check CHECK ({$matches[1]})\";\n        }\n\n        $type = preg_replace('/\\s+UNIQUE/i', '', $type, -1, $count);\n        if ($count) {\n            $multiAlterStatement[] = \"ADD UNIQUE ({$columnName})\";\n        }\n\n        // add what's left at the beginning\n        array_unshift($multiAlterStatement, \"ALTER COLUMN {$columnName} {$type}\");\n\n        return 'ALTER TABLE ' . $tableName . ' ' . implode(', ', $multiAlterStatement);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function insert($table, $columns, &$params)\n    {\n        return parent::insert($table, $this->normalizeTableRowData($table, $columns), $params);\n    }\n\n    /**\n     * {@inheritdoc}\n     * @see https://www.postgresql.org/docs/9.5/static/sql-insert.html#SQL-ON-CONFLICT\n     * @see https://stackoverflow.com/questions/1109061/insert-on-duplicate-update-in-postgresql/8702291#8702291\n     */\n    public function upsert($table, $insertColumns, $updateColumns, &$params)\n    {\n        $insertColumns = $this->normalizeTableRowData($table, $insertColumns);\n        if (!is_bool($updateColumns)) {\n            $updateColumns = $this->normalizeTableRowData($table, $updateColumns);\n        }\n        if (version_compare($this->db->getServerVersion(), '9.5', '<')) {\n            return $this->oldUpsert($table, $insertColumns, $updateColumns, $params);\n        }\n\n        return $this->newUpsert($table, $insertColumns, $updateColumns, $params);\n    }\n\n    /**\n     * [[upsert()]] implementation for PostgreSQL 9.5 or higher.\n     * @param string $table\n     * @param array|Query $insertColumns\n     * @param array|bool $updateColumns\n     * @param array $params\n     * @return string\n     */\n    private function newUpsert($table, $insertColumns, $updateColumns, &$params)\n    {\n        $insertSql = $this->insert($table, $insertColumns, $params);\n        list($uniqueNames, , $updateNames) = $this->prepareUpsertColumns($table, $insertColumns, $updateColumns);\n        if (empty($uniqueNames)) {\n            return $insertSql;\n        }\n        if ($updateNames === []) {\n            // there are no columns to update\n            $updateColumns = false;\n        }\n\n        if ($updateColumns === false) {\n            return \"$insertSql ON CONFLICT DO NOTHING\";\n        }\n\n        if ($updateColumns === true) {\n            $updateColumns = [];\n            foreach ($updateNames as $name) {\n                $updateColumns[$name] = new Expression('EXCLUDED.' . $this->db->quoteColumnName($name));\n            }\n        }\n        list($updates, $params) = $this->prepareUpdateSets($table, $updateColumns, $params);\n        return $insertSql . ' ON CONFLICT (' . implode(', ', $uniqueNames) . ') DO UPDATE SET ' . implode(', ', $updates);\n    }\n\n    /**\n     * [[upsert()]] implementation for PostgreSQL older than 9.5.\n     * @param string $table\n     * @param array|Query $insertColumns\n     * @param array|bool $updateColumns\n     * @param array $params\n     * @return string\n     */\n    private function oldUpsert($table, $insertColumns, $updateColumns, &$params)\n    {\n        list($uniqueNames, $insertNames, $updateNames) = $this->prepareUpsertColumns($table, $insertColumns, $updateColumns, $constraints);\n        if (empty($uniqueNames)) {\n            return $this->insert($table, $insertColumns, $params);\n        }\n        if ($updateNames === []) {\n            // there are no columns to update\n            $updateColumns = false;\n        }\n\n        /** @var Schema $schema */\n        $schema = $this->db->getSchema();\n        if (!$insertColumns instanceof Query) {\n            $tableSchema = $schema->getTableSchema($table);\n            $columnSchemas = $tableSchema !== null ? $tableSchema->columns : [];\n            foreach ($insertColumns as $name => $value) {\n                // NULLs and numeric values must be type hinted in order to be used in SET assigments\n                // NVM, let's cast them all\n                if (isset($columnSchemas[$name])) {\n                    $phName = self::PARAM_PREFIX . count($params);\n                    $params[$phName] = $value;\n                    $insertColumns[$name] = new Expression(\"CAST($phName AS {$columnSchemas[$name]->dbType})\");\n                }\n            }\n        }\n        list(, $placeholders, $values, $params) = $this->prepareInsertValues($table, $insertColumns, $params);\n        $updateCondition = ['or'];\n        $insertCondition = ['or'];\n        $quotedTableName = $schema->quoteTableName($table);\n        foreach ($constraints as $constraint) {\n            $constraintUpdateCondition = ['and'];\n            $constraintInsertCondition = ['and'];\n            foreach ($constraint->columnNames as $name) {\n                $quotedName = $schema->quoteColumnName($name);\n                $constraintUpdateCondition[] = \"$quotedTableName.$quotedName=\\\"EXCLUDED\\\".$quotedName\";\n                $constraintInsertCondition[] = \"\\\"upsert\\\".$quotedName=\\\"EXCLUDED\\\".$quotedName\";\n            }\n            $updateCondition[] = $constraintUpdateCondition;\n            $insertCondition[] = $constraintInsertCondition;\n        }\n        $withSql = 'WITH \"EXCLUDED\" (' . implode(', ', $insertNames)\n            . ') AS (' . (!empty($placeholders) ? 'VALUES (' . implode(', ', $placeholders) . ')' : ltrim($values, ' ')) . ')';\n        if ($updateColumns === false) {\n            $selectSubQuery = (new Query())\n                ->select(new Expression('1'))\n                ->from($table)\n                ->where($updateCondition);\n            $insertSelectSubQuery = (new Query())\n                ->select($insertNames)\n                ->from('EXCLUDED')\n                ->where(['not exists', $selectSubQuery]);\n            $insertSql = $this->insert($table, $insertSelectSubQuery, $params);\n            return \"$withSql $insertSql\";\n        }\n\n        if ($updateColumns === true) {\n            $updateColumns = [];\n            foreach ($updateNames as $name) {\n                $quotedName = $this->db->quoteColumnName($name);\n                if (strrpos($quotedName, '.') === false) {\n                    $quotedName = '\"EXCLUDED\".' . $quotedName;\n                }\n                $updateColumns[$name] = new Expression($quotedName);\n            }\n        }\n        list($updates, $params) = $this->prepareUpdateSets($table, $updateColumns, $params);\n        $updateSql = 'UPDATE ' . $this->db->quoteTableName($table) . ' SET ' . implode(', ', $updates)\n            . ' FROM \"EXCLUDED\" ' . $this->buildWhere($updateCondition, $params)\n            . ' RETURNING ' . $this->db->quoteTableName($table) . '.*';\n        $selectUpsertSubQuery = (new Query())\n            ->select(new Expression('1'))\n            ->from('upsert')\n            ->where($insertCondition);\n        $insertSelectSubQuery = (new Query())\n            ->select($insertNames)\n            ->from('EXCLUDED')\n            ->where(['not exists', $selectUpsertSubQuery]);\n        $insertSql = $this->insert($table, $insertSelectSubQuery, $params);\n        return \"$withSql, \\\"upsert\\\" AS ($updateSql) $insertSql\";\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function update($table, $columns, $condition, &$params)\n    {\n        return parent::update($table, $this->normalizeTableRowData($table, $columns), $condition, $params);\n    }\n\n    /**\n     * Normalizes data to be saved into the table, performing extra preparations and type converting, if necessary.\n     *\n     * @param string $table the table that data will be saved into.\n     * @param array|Query $columns the column data (name => value) to be saved into the table or instance\n     * of [[yii\\db\\Query|Query]] to perform INSERT INTO ... SELECT SQL statement.\n     * Passing of [[yii\\db\\Query|Query]] is available since version 2.0.11.\n     * @return array|Query normalized columns\n     * @since 2.0.9\n     */\n    private function normalizeTableRowData($table, $columns)\n    {\n        if ($columns instanceof Query) {\n            return $columns;\n        }\n\n        if (($tableSchema = $this->db->getSchema()->getTableSchema($table)) !== null) {\n            $columnSchemas = $tableSchema->columns;\n            foreach ($columns as $name => $value) {\n                if (isset($columnSchemas[$name]) && $columnSchemas[$name]->type === Schema::TYPE_BINARY && is_string($value)) {\n                    $columns[$name] = new PdoValue($value, \\PDO::PARAM_LOB); // explicitly setup PDO param type for binary column\n                }\n            }\n        }\n\n        return $columns;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function batchInsert($table, $columns, $rows, &$params = [])\n    {\n        if (empty($rows)) {\n            return '';\n        }\n\n        $schema = $this->db->getSchema();\n        if (($tableSchema = $schema->getTableSchema($table)) !== null) {\n            $columnSchemas = $tableSchema->columns;\n        } else {\n            $columnSchemas = [];\n        }\n\n        $values = [];\n        foreach ($rows as $row) {\n            $vs = [];\n            foreach ($row as $i => $value) {\n                if (isset($columns[$i], $columnSchemas[$columns[$i]])) {\n                    $value = $columnSchemas[$columns[$i]]->dbTypecast($value);\n                }\n                if (is_string($value)) {\n                    $value = $schema->quoteValue($value);\n                } elseif (is_float($value)) {\n                    // ensure type cast always has . as decimal separator in all locales\n                    $value = StringHelper::floatToString($value);\n                } elseif ($value === true) {\n                    $value = 'TRUE';\n                } elseif ($value === false) {\n                    $value = 'FALSE';\n                } elseif ($value === null) {\n                    $value = 'NULL';\n                } elseif ($value instanceof ExpressionInterface) {\n                    $value = $this->buildExpression($value, $params);\n                }\n                $vs[] = $value;\n            }\n            $values[] = '(' . implode(', ', $vs) . ')';\n        }\n        if (empty($values)) {\n            return '';\n        }\n\n        foreach ($columns as $i => $name) {\n            $columns[$i] = $schema->quoteColumnName($name);\n        }\n\n        return 'INSERT INTO ' . $schema->quoteTableName($table)\n        . ' (' . implode(', ', $columns) . ') VALUES ' . implode(', ', $values);\n    }\n}\n"
  },
  {
    "path": "framework/db/pgsql/Schema.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\pgsql;\n\nuse Yii;\nuse yii\\base\\NotSupportedException;\nuse yii\\db\\CheckConstraint;\nuse yii\\db\\Constraint;\nuse yii\\db\\ConstraintFinderInterface;\nuse yii\\db\\ConstraintFinderTrait;\nuse yii\\db\\Expression;\nuse yii\\db\\ForeignKeyConstraint;\nuse yii\\db\\IndexConstraint;\nuse yii\\db\\TableSchema;\nuse yii\\db\\ViewFinderTrait;\nuse yii\\helpers\\ArrayHelper;\nuse yii\\db\\Schema as BaseSchema;\n\n/**\n * Schema is the class for retrieving metadata from a PostgreSQL database\n * (version 9.x and above).\n *\n * @author Gevik Babakhani <gevikb@gmail.com>\n * @since 2.0\n *\n * @template T of ColumnSchema = ColumnSchema\n * @extends BaseSchema<T>\n */\nclass Schema extends BaseSchema implements ConstraintFinderInterface\n{\n    use ViewFinderTrait;\n    use ConstraintFinderTrait;\n\n    public const TYPE_JSONB = 'jsonb';\n    /**\n     * @var string the default schema used for the current session.\n     */\n    public $defaultSchema = 'public';\n    /**\n     * {@inheritdoc}\n     */\n    public $columnSchemaClass = 'yii\\db\\pgsql\\ColumnSchema';\n    /**\n     * @var array mapping from physical column types (keys) to abstract\n     * column types (values)\n     * @see https://www.postgresql.org/docs/current/datatype.html#DATATYPE-TABLE\n     */\n    public $typeMap = [\n        'bit' => self::TYPE_INTEGER,\n        'bit varying' => self::TYPE_INTEGER,\n        'varbit' => self::TYPE_INTEGER,\n\n        'bool' => self::TYPE_BOOLEAN,\n        'boolean' => self::TYPE_BOOLEAN,\n\n        'box' => self::TYPE_STRING,\n        'circle' => self::TYPE_STRING,\n        'point' => self::TYPE_STRING,\n        'line' => self::TYPE_STRING,\n        'lseg' => self::TYPE_STRING,\n        'polygon' => self::TYPE_STRING,\n        'path' => self::TYPE_STRING,\n\n        'character' => self::TYPE_CHAR,\n        'char' => self::TYPE_CHAR,\n        'bpchar' => self::TYPE_CHAR,\n        'character varying' => self::TYPE_STRING,\n        'varchar' => self::TYPE_STRING,\n        'text' => self::TYPE_TEXT,\n\n        'bytea' => self::TYPE_BINARY,\n\n        'cidr' => self::TYPE_STRING,\n        'inet' => self::TYPE_STRING,\n        'macaddr' => self::TYPE_STRING,\n\n        'real' => self::TYPE_FLOAT,\n        'float4' => self::TYPE_FLOAT,\n        'double precision' => self::TYPE_DOUBLE,\n        'float8' => self::TYPE_DOUBLE,\n        'decimal' => self::TYPE_DECIMAL,\n        'numeric' => self::TYPE_DECIMAL,\n\n        'money' => self::TYPE_MONEY,\n\n        'smallint' => self::TYPE_SMALLINT,\n        'int2' => self::TYPE_SMALLINT,\n        'int4' => self::TYPE_INTEGER,\n        'int' => self::TYPE_INTEGER,\n        'integer' => self::TYPE_INTEGER,\n        'bigint' => self::TYPE_BIGINT,\n        'int8' => self::TYPE_BIGINT,\n        'oid' => self::TYPE_BIGINT, // should not be used. it's pg internal!\n\n        'smallserial' => self::TYPE_SMALLINT,\n        'serial2' => self::TYPE_SMALLINT,\n        'serial4' => self::TYPE_INTEGER,\n        'serial' => self::TYPE_INTEGER,\n        'bigserial' => self::TYPE_BIGINT,\n        'serial8' => self::TYPE_BIGINT,\n        'pg_lsn' => self::TYPE_BIGINT,\n\n        'date' => self::TYPE_DATE,\n        'interval' => self::TYPE_STRING,\n        'time without time zone' => self::TYPE_TIME,\n        'time' => self::TYPE_TIME,\n        'time with time zone' => self::TYPE_TIME,\n        'timetz' => self::TYPE_TIME,\n        'timestamp without time zone' => self::TYPE_TIMESTAMP,\n        'timestamp' => self::TYPE_TIMESTAMP,\n        'timestamp with time zone' => self::TYPE_TIMESTAMP,\n        'timestamptz' => self::TYPE_TIMESTAMP,\n        'abstime' => self::TYPE_TIMESTAMP,\n\n        'tsquery' => self::TYPE_STRING,\n        'tsvector' => self::TYPE_STRING,\n        'txid_snapshot' => self::TYPE_STRING,\n\n        'unknown' => self::TYPE_STRING,\n\n        'uuid' => self::TYPE_STRING,\n        'json' => self::TYPE_JSON,\n        'jsonb' => self::TYPE_JSON,\n        'xml' => self::TYPE_STRING,\n    ];\n\n    /**\n     * {@inheritdoc}\n     */\n    protected $tableQuoteCharacter = '\"';\n\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function resolveTableName($name)\n    {\n        $resolvedName = new TableSchema();\n        $parts = explode('.', str_replace('\"', '', $name));\n        if (isset($parts[1])) {\n            $resolvedName->schemaName = $parts[0];\n            $resolvedName->name = $parts[1];\n        } else {\n            $resolvedName->schemaName = $this->defaultSchema;\n            $resolvedName->name = $name;\n        }\n        $resolvedName->fullName = ($resolvedName->schemaName !== $this->defaultSchema ? $resolvedName->schemaName . '.' : '') . $resolvedName->name;\n        return $resolvedName;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function findSchemaNames()\n    {\n        static $sql = <<<'SQL'\nSELECT \"ns\".\"nspname\"\nFROM \"pg_namespace\" AS \"ns\"\nWHERE \"ns\".\"nspname\" != 'information_schema' AND \"ns\".\"nspname\" NOT LIKE 'pg_%'\nORDER BY \"ns\".\"nspname\" ASC\nSQL;\n\n        return $this->db->createCommand($sql)->queryColumn();\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function findTableNames($schema = '')\n    {\n        if ($schema === '') {\n            $schema = $this->defaultSchema;\n        }\n        $sql = <<<'SQL'\nSELECT c.relname AS table_name\nFROM pg_class c\nINNER JOIN pg_namespace ns ON ns.oid = c.relnamespace\nWHERE ns.nspname = :schemaName AND c.relkind IN ('r','v','m','f', 'p')\nORDER BY c.relname\nSQL;\n        return $this->db->createCommand($sql, [':schemaName' => $schema])->queryColumn();\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableSchema($name)\n    {\n        $table = new TableSchema();\n        $this->resolveTableNames($table, $name);\n        if ($this->findColumns($table)) {\n            $this->findConstraints($table);\n            return $table;\n        }\n\n        return null;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTablePrimaryKey($tableName)\n    {\n        return $this->loadTableConstraints($tableName, 'primaryKey');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableForeignKeys($tableName)\n    {\n        return $this->loadTableConstraints($tableName, 'foreignKeys');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableIndexes($tableName)\n    {\n        static $sql = <<<'SQL'\nSELECT\n    \"ic\".\"relname\" AS \"name\",\n    \"ia\".\"attname\" AS \"column_name\",\n    \"i\".\"indisunique\" AS \"index_is_unique\",\n    \"i\".\"indisprimary\" AS \"index_is_primary\"\nFROM \"pg_class\" AS \"tc\"\nINNER JOIN \"pg_namespace\" AS \"tcns\"\n    ON \"tcns\".\"oid\" = \"tc\".\"relnamespace\"\nINNER JOIN \"pg_index\" AS \"i\"\n    ON \"i\".\"indrelid\" = \"tc\".\"oid\"\nINNER JOIN \"pg_class\" AS \"ic\"\n    ON \"ic\".\"oid\" = \"i\".\"indexrelid\"\nINNER JOIN \"pg_attribute\" AS \"ia\"\n    ON \"ia\".\"attrelid\" = \"i\".\"indexrelid\"\nWHERE \"tcns\".\"nspname\" = :schemaName AND \"tc\".\"relname\" = :tableName\nORDER BY \"ia\".\"attnum\" ASC\nSQL;\n\n        $resolvedName = $this->resolveTableName($tableName);\n        $indexes = $this->db->createCommand($sql, [\n            ':schemaName' => $resolvedName->schemaName,\n            ':tableName' => $resolvedName->name,\n        ])->queryAll();\n        $indexes = $this->normalizePdoRowKeyCase($indexes, true);\n        $indexes = ArrayHelper::index($indexes, null, 'name');\n        $result = [];\n        foreach ($indexes as $name => $index) {\n            $result[] = new IndexConstraint([\n                'isPrimary' => (bool) $index[0]['index_is_primary'],\n                'isUnique' => (bool) $index[0]['index_is_unique'],\n                'name' => $name,\n                'columnNames' => ArrayHelper::getColumn($index, 'column_name'),\n            ]);\n        }\n\n        return $result;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableUniques($tableName)\n    {\n        return $this->loadTableConstraints($tableName, 'uniques');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableChecks($tableName)\n    {\n        return $this->loadTableConstraints($tableName, 'checks');\n    }\n\n    /**\n     * {@inheritdoc}\n     * @throws NotSupportedException if this method is called.\n     */\n    protected function loadTableDefaultValues($tableName)\n    {\n        throw new NotSupportedException('PostgreSQL does not support default value constraints.');\n    }\n\n    /**\n     * Creates a query builder for the PostgreSQL database.\n     * @return QueryBuilder query builder instance\n     */\n    public function createQueryBuilder()\n    {\n        return Yii::createObject(QueryBuilder::className(), [$this->db]);\n    }\n\n    /**\n     * Resolves the table name and schema name (if any).\n     * @param TableSchema $table the table metadata object\n     * @param string $name the table name\n     */\n    protected function resolveTableNames($table, $name)\n    {\n        $parts = explode('.', str_replace('\"', '', $name));\n\n        if (isset($parts[1])) {\n            $table->schemaName = $parts[0];\n            $table->name = $parts[1];\n        } else {\n            $table->schemaName = $this->defaultSchema;\n            $table->name = $parts[0];\n        }\n\n        $table->fullName = $table->schemaName !== $this->defaultSchema ? $table->schemaName . '.' . $table->name : $table->name;\n    }\n\n    /**\n     * {@inheritdoc]\n     */\n    protected function findViewNames($schema = '')\n    {\n        if ($schema === '') {\n            $schema = $this->defaultSchema;\n        }\n        $sql = <<<'SQL'\nSELECT c.relname AS table_name\nFROM pg_class c\nINNER JOIN pg_namespace ns ON ns.oid = c.relnamespace\nWHERE ns.nspname = :schemaName AND (c.relkind = 'v' OR c.relkind = 'm')\nORDER BY c.relname\nSQL;\n        return $this->db->createCommand($sql, [':schemaName' => $schema])->queryColumn();\n    }\n\n    /**\n     * Collects the foreign key column details for the given table.\n     * @param TableSchema $table the table metadata\n     */\n    protected function findConstraints($table)\n    {\n        $tableName = $this->quoteValue($table->name);\n        $tableSchema = $this->quoteValue($table->schemaName);\n\n        //We need to extract the constraints de hard way since:\n        //https://www.postgresql.org/message-id/26677.1086673982@sss.pgh.pa.us\n\n        $sql = <<<SQL\nselect\n    ct.conname as constraint_name,\n    a.attname as column_name,\n    fc.relname as foreign_table_name,\n    fns.nspname as foreign_table_schema,\n    fa.attname as foreign_column_name\nfrom\n    (SELECT ct.conname, ct.conrelid, ct.confrelid, ct.conkey, ct.contype, ct.confkey, generate_subscripts(ct.conkey, 1) AS s\n       FROM pg_constraint ct\n    ) AS ct\n    inner join pg_class c on c.oid=ct.conrelid\n    inner join pg_namespace ns on c.relnamespace=ns.oid\n    inner join pg_attribute a on a.attrelid=ct.conrelid and a.attnum = ct.conkey[ct.s]\n    left join pg_class fc on fc.oid=ct.confrelid\n    left join pg_namespace fns on fc.relnamespace=fns.oid\n    left join pg_attribute fa on fa.attrelid=ct.confrelid and fa.attnum = ct.confkey[ct.s]\nwhere\n    ct.contype='f'\n    and c.relname={$tableName}\n    and ns.nspname={$tableSchema}\norder by\n    fns.nspname, fc.relname, a.attnum\nSQL;\n\n        $constraints = [];\n        foreach ($this->db->createCommand($sql)->queryAll() as $constraint) {\n            if ($this->db->slavePdo->getAttribute(\\PDO::ATTR_CASE) === \\PDO::CASE_UPPER) {\n                $constraint = array_change_key_case($constraint, CASE_LOWER);\n            }\n            if ($constraint['foreign_table_schema'] !== $this->defaultSchema) {\n                $foreignTable = $constraint['foreign_table_schema'] . '.' . $constraint['foreign_table_name'];\n            } else {\n                $foreignTable = $constraint['foreign_table_name'];\n            }\n            $name = $constraint['constraint_name'];\n            if (!isset($constraints[$name])) {\n                $constraints[$name] = [\n                    'tableName' => $foreignTable,\n                    'columns' => [],\n                ];\n            }\n            $constraints[$name]['columns'][$constraint['column_name']] = $constraint['foreign_column_name'];\n        }\n        foreach ($constraints as $name => $constraint) {\n            $table->foreignKeys[$name] = array_merge([$constraint['tableName']], $constraint['columns']);\n        }\n    }\n\n    /**\n     * Gets information about given table unique indexes.\n     * @param TableSchema $table the table metadata\n     * @return array with index and column names\n     */\n    protected function getUniqueIndexInformation($table)\n    {\n        $sql = <<<'SQL'\nSELECT\n    i.relname as indexname,\n    pg_get_indexdef(idx.indexrelid, k + 1, TRUE) AS columnname\nFROM (\n  SELECT *, generate_subscripts(indkey, 1) AS k\n  FROM pg_index\n) idx\nINNER JOIN pg_class i ON i.oid = idx.indexrelid\nINNER JOIN pg_class c ON c.oid = idx.indrelid\nINNER JOIN pg_namespace ns ON c.relnamespace = ns.oid\nWHERE idx.indisprimary = FALSE AND idx.indisunique = TRUE\nAND c.relname = :tableName AND ns.nspname = :schemaName\nORDER BY i.relname, k\nSQL;\n\n        return $this->db->createCommand($sql, [\n            ':schemaName' => $table->schemaName,\n            ':tableName' => $table->name,\n        ])->queryAll();\n    }\n\n    /**\n     * Returns all unique indexes for the given table.\n     *\n     * Each array element is of the following structure:\n     *\n     * ```\n     * [\n     *     'IndexName1' => ['col1' [, ...]],\n     *     'IndexName2' => ['col2' [, ...]],\n     * ]\n     * ```\n     *\n     * @param TableSchema $table the table metadata\n     * @return array all unique indexes for the given table.\n     */\n    public function findUniqueIndexes($table)\n    {\n        $uniqueIndexes = [];\n\n        foreach ($this->getUniqueIndexInformation($table) as $row) {\n            if ($this->db->slavePdo->getAttribute(\\PDO::ATTR_CASE) === \\PDO::CASE_UPPER) {\n                $row = array_change_key_case($row, CASE_LOWER);\n            }\n            $column = $row['columnname'];\n            if (strncmp($column, '\"', 1) === 0) {\n                // postgres will quote names that are not lowercase-only\n                // https://github.com/yiisoft/yii2/issues/10613\n                $column = substr($column, 1, -1);\n            }\n            $uniqueIndexes[$row['indexname']][] = $column;\n        }\n\n        return $uniqueIndexes;\n    }\n\n    /**\n     * Collects the metadata of table columns.\n     * @param TableSchema $table the table metadata\n     * @return bool whether the table exists in the database\n     */\n    protected function findColumns($table)\n    {\n        $tableName = $this->db->quoteValue($table->name);\n        $schemaName = $this->db->quoteValue($table->schemaName);\n\n        $orIdentity = '';\n        if (version_compare($this->db->serverVersion, '12.0', '>=')) {\n            $orIdentity = 'OR attidentity != \\'\\'';\n        }\n\n        $sql = <<<SQL\nSELECT\n    d.nspname AS table_schema,\n    c.relname AS table_name,\n    a.attname AS column_name,\n    COALESCE(td.typname, tb.typname, t.typname) AS data_type,\n    COALESCE(td.typtype, tb.typtype, t.typtype) AS type_type,\n    (SELECT nspname FROM pg_namespace WHERE oid = COALESCE(td.typnamespace, tb.typnamespace, t.typnamespace)) AS type_scheme,\n    a.attlen AS character_maximum_length,\n    pg_catalog.col_description(c.oid, a.attnum) AS column_comment,\n    a.atttypmod AS modifier,\n    a.attnotnull = false AS is_nullable,\n    CAST(pg_get_expr(ad.adbin, ad.adrelid) AS varchar) AS column_default,\n    coalesce(pg_get_expr(ad.adbin, ad.adrelid) ~ 'nextval',false) {$orIdentity} AS is_autoinc,\n    pg_get_serial_sequence(quote_ident(d.nspname) || '.' || quote_ident(c.relname), a.attname) AS sequence_name,\n    CASE WHEN COALESCE(td.typtype, tb.typtype, t.typtype) = 'e'::char\n        THEN array_to_string((SELECT array_agg(enumlabel) FROM pg_enum WHERE enumtypid = COALESCE(td.oid, tb.oid, a.atttypid))::varchar[], ',')\n        ELSE NULL\n    END AS enum_values,\n    CASE atttypid\n         WHEN 21 /*int2*/ THEN 16\n         WHEN 23 /*int4*/ THEN 32\n         WHEN 20 /*int8*/ THEN 64\n         WHEN 1700 /*numeric*/ THEN\n              CASE WHEN atttypmod = -1\n               THEN null\n               ELSE ((atttypmod - 4) >> 16) & 65535\n               END\n         WHEN 700 /*float4*/ THEN 24 /*FLT_MANT_DIG*/\n         WHEN 701 /*float8*/ THEN 53 /*DBL_MANT_DIG*/\n         ELSE null\n      END   AS numeric_precision,\n      CASE\n        WHEN atttypid IN (21, 23, 20) THEN 0\n        WHEN atttypid IN (1700) THEN\n        CASE\n            WHEN atttypmod = -1 THEN null\n            ELSE (atttypmod - 4) & 65535\n        END\n           ELSE null\n      END AS numeric_scale,\n    CAST(\n             information_schema._pg_char_max_length(information_schema._pg_truetypid(a, t), information_schema._pg_truetypmod(a, t))\n             AS numeric\n    ) AS size,\n    a.attnum = any (ct.conkey) as is_pkey,\n    COALESCE(NULLIF(a.attndims, 0), NULLIF(t.typndims, 0), (t.typcategory='A')::int) AS dimension\nFROM\n    pg_class c\n    LEFT JOIN pg_attribute a ON a.attrelid = c.oid\n    LEFT JOIN pg_attrdef ad ON a.attrelid = ad.adrelid AND a.attnum = ad.adnum\n    LEFT JOIN pg_type t ON a.atttypid = t.oid\n    LEFT JOIN pg_type tb ON (a.attndims > 0 OR t.typcategory='A') AND t.typelem > 0 AND t.typelem = tb.oid OR t.typbasetype > 0 AND t.typbasetype = tb.oid\n    LEFT JOIN pg_type td ON t.typndims > 0 AND t.typbasetype > 0 AND tb.typelem = td.oid\n    LEFT JOIN pg_namespace d ON d.oid = c.relnamespace\n    LEFT JOIN pg_constraint ct ON ct.conrelid = c.oid AND ct.contype = 'p'\nWHERE\n    a.attnum > 0 AND t.typname != '' AND NOT a.attisdropped\n    AND c.relname = {$tableName}\n    AND d.nspname = {$schemaName}\nORDER BY\n    a.attnum;\nSQL;\n        $columns = $this->db->createCommand($sql)->queryAll();\n        if (empty($columns)) {\n            return false;\n        }\n        foreach ($columns as $column) {\n            if ($this->db->slavePdo->getAttribute(\\PDO::ATTR_CASE) === \\PDO::CASE_UPPER) {\n                $column = array_change_key_case($column, CASE_LOWER);\n            }\n            $column = $this->loadColumnSchema($column);\n            $table->columns[$column->name] = $column;\n            if ($column->isPrimaryKey) {\n                $table->primaryKey[] = $column->name;\n                if ($table->sequenceName === null) {\n                    $table->sequenceName = $column->sequenceName;\n                }\n                $column->defaultValue = null;\n            } elseif ($column->defaultValue) {\n                if (\n                    in_array($column->type, [self::TYPE_TIMESTAMP, self::TYPE_DATE, self::TYPE_TIME], true) &&\n                    (\n                        in_array(\n                            strtoupper($column->defaultValue),\n                            ['NOW()', 'CURRENT_TIMESTAMP', 'CURRENT_DATE', 'CURRENT_TIME'],\n                            true\n                        ) ||\n                        (false !== strpos($column->defaultValue, '('))\n                    )\n                ) {\n                    $column->defaultValue = new Expression($column->defaultValue);\n                } elseif ($column->type === 'boolean') {\n                    $column->defaultValue = ($column->defaultValue === 'true');\n                } elseif (preg_match(\"/^B'(.*?)'::/\", $column->defaultValue, $matches)) {\n                    $column->defaultValue = bindec($matches[1]);\n                } elseif (preg_match(\"/^'(\\d+)'::\\\"bit\\\"$/\", $column->defaultValue, $matches)) {\n                    $column->defaultValue = bindec($matches[1]);\n                } elseif (preg_match(\"/^'(.*?)'::/\", $column->defaultValue, $matches)) {\n                    $column->defaultValue = $column->phpTypecast($matches[1]);\n                } elseif (preg_match('/^(\\()?(.*?)(?(1)\\))(?:::.+)?$/', $column->defaultValue, $matches)) {\n                    if ($matches[2] === 'NULL') {\n                        $column->defaultValue = null;\n                    } else {\n                        $column->defaultValue = $column->phpTypecast($matches[2]);\n                    }\n                } else {\n                    $column->defaultValue = $column->phpTypecast($column->defaultValue);\n                }\n            }\n        }\n\n        return true;\n    }\n\n    /**\n     * Loads the column information into a [[ColumnSchema]] object.\n     * @param array $info column information\n     * @return T the column schema object\n     */\n    protected function loadColumnSchema($info)\n    {\n        /** @var ColumnSchema $column */\n        $column = $this->createColumnSchema();\n        $column->allowNull = $info['is_nullable'];\n        $column->autoIncrement = $info['is_autoinc'];\n        $column->comment = $info['column_comment'];\n        if ($info['type_scheme'] !== null && !in_array($info['type_scheme'], [$this->defaultSchema, 'pg_catalog'], true)) {\n            $column->dbType = $info['type_scheme'] . '.' . $info['data_type'];\n        } else {\n            $column->dbType = $info['data_type'];\n        }\n        $column->defaultValue = $info['column_default'];\n        $column->enumValues = ($info['enum_values'] !== null) ? explode(',', str_replace([\"''\"], [\"'\"], $info['enum_values'])) : null;\n        $column->unsigned = false; // has no meaning in PG\n        $column->isPrimaryKey = $info['is_pkey'];\n        $column->name = $info['column_name'];\n        $column->precision = $info['numeric_precision'];\n        $column->scale = $info['numeric_scale'];\n        $column->size = $info['size'] === null ? null : (int) $info['size'];\n        $column->dimension = (int) $info['dimension'];\n        // pg_get_serial_sequence() doesn't track DEFAULT value change. GENERATED BY IDENTITY columns always have null default value\n        if (isset($column->defaultValue) && preg_match(\"/nextval\\\\('\\\"?\\\\w+\\\"?\\.?\\\"?\\\\w+\\\"?'(::regclass)?\\\\)/\", $column->defaultValue) === 1) {\n            $column->sequenceName = preg_replace(['/nextval/', '/::/', '/regclass/', '/\\'\\)/', '/\\(\\'/'], '', $column->defaultValue);\n        } elseif (isset($info['sequence_name'])) {\n            $column->sequenceName = $this->resolveTableName($info['sequence_name'])->fullName;\n        }\n\n        if (isset($this->typeMap[$column->dbType])) {\n            $column->type = $this->typeMap[$column->dbType];\n        } else {\n            $column->type = self::TYPE_STRING;\n        }\n        $column->phpType = $this->getColumnPhpType($column);\n\n        return $column;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function insert($table, $columns)\n    {\n        $params = [];\n        $sql = $this->db->getQueryBuilder()->insert($table, $columns, $params);\n        $returnColumns = $this->getTableSchema($table)->primaryKey;\n        if (!empty($returnColumns)) {\n            $returning = [];\n            foreach ((array) $returnColumns as $name) {\n                $returning[] = $this->quoteColumnName($name);\n            }\n            $sql .= ' RETURNING ' . implode(', ', $returning);\n        }\n\n        $command = $this->db->createCommand($sql, $params);\n        $command->prepare(false);\n        $result = $command->queryOne();\n\n        return !$command->pdoStatement->rowCount() ? false : $result;\n    }\n\n    /**\n     * Loads multiple types of constraints and returns the specified ones.\n     * @param string $tableName table name.\n     * @param string $returnType return type:\n     * - primaryKey\n     * - foreignKeys\n     * - uniques\n     * - checks\n     * @return mixed constraints.\n     */\n    private function loadTableConstraints($tableName, $returnType)\n    {\n        static $sql = <<<'SQL'\nSELECT\n    \"c\".\"conname\" AS \"name\",\n    \"a\".\"attname\" AS \"column_name\",\n    \"c\".\"contype\" AS \"type\",\n    \"ftcns\".\"nspname\" AS \"foreign_table_schema\",\n    \"ftc\".\"relname\" AS \"foreign_table_name\",\n    \"fa\".\"attname\" AS \"foreign_column_name\",\n    \"c\".\"confupdtype\" AS \"on_update\",\n    \"c\".\"confdeltype\" AS \"on_delete\",\n    pg_get_constraintdef(\"c\".\"oid\") AS \"check_expr\"\nFROM \"pg_class\" AS \"tc\"\nINNER JOIN \"pg_namespace\" AS \"tcns\"\n    ON \"tcns\".\"oid\" = \"tc\".\"relnamespace\"\nINNER JOIN \"pg_constraint\" AS \"c\"\n    ON \"c\".\"conrelid\" = \"tc\".\"oid\"\nINNER JOIN \"pg_attribute\" AS \"a\"\n    ON \"a\".\"attrelid\" = \"c\".\"conrelid\" AND \"a\".\"attnum\" = ANY (\"c\".\"conkey\")\nLEFT JOIN \"pg_class\" AS \"ftc\"\n    ON \"ftc\".\"oid\" = \"c\".\"confrelid\"\nLEFT JOIN \"pg_namespace\" AS \"ftcns\"\n    ON \"ftcns\".\"oid\" = \"ftc\".\"relnamespace\"\nLEFT JOIN \"pg_attribute\" \"fa\"\n    ON \"fa\".\"attrelid\" = \"c\".\"confrelid\" AND \"fa\".\"attnum\" = ANY (\"c\".\"confkey\")\nWHERE \"tcns\".\"nspname\" = :schemaName AND \"tc\".\"relname\" = :tableName\nORDER BY \"a\".\"attnum\" ASC, \"fa\".\"attnum\" ASC\nSQL;\n        static $actionTypes = [\n            'a' => 'NO ACTION',\n            'r' => 'RESTRICT',\n            'c' => 'CASCADE',\n            'n' => 'SET NULL',\n            'd' => 'SET DEFAULT',\n        ];\n\n        $resolvedName = $this->resolveTableName($tableName);\n        $constraints = $this->db->createCommand($sql, [\n            ':schemaName' => $resolvedName->schemaName,\n            ':tableName' => $resolvedName->name,\n        ])->queryAll();\n        $constraints = $this->normalizePdoRowKeyCase($constraints, true);\n        $constraints = ArrayHelper::index($constraints, null, ['type', 'name']);\n        $result = [\n            'primaryKey' => null,\n            'foreignKeys' => [],\n            'uniques' => [],\n            'checks' => [],\n        ];\n        foreach ($constraints as $type => $names) {\n            foreach ($names as $name => $constraint) {\n                switch ($type) {\n                    case 'p':\n                        $result['primaryKey'] = new Constraint([\n                            'name' => $name,\n                            'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),\n                        ]);\n                        break;\n                    case 'f':\n                        $result['foreignKeys'][] = new ForeignKeyConstraint([\n                            'name' => $name,\n                            'columnNames' => array_keys(array_count_values(ArrayHelper::getColumn($constraint, 'column_name'))),\n                            'foreignSchemaName' => $constraint[0]['foreign_table_schema'],\n                            'foreignTableName' => $constraint[0]['foreign_table_name'],\n                            'foreignColumnNames' => array_keys(array_count_values(ArrayHelper::getColumn($constraint, 'foreign_column_name'))),\n                            'onDelete' => isset($actionTypes[$constraint[0]['on_delete']]) ? $actionTypes[$constraint[0]['on_delete']] : null,\n                            'onUpdate' => isset($actionTypes[$constraint[0]['on_update']]) ? $actionTypes[$constraint[0]['on_update']] : null,\n                        ]);\n                        break;\n                    case 'u':\n                        $result['uniques'][] = new Constraint([\n                            'name' => $name,\n                            'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),\n                        ]);\n                        break;\n                    case 'c':\n                        $result['checks'][] = new CheckConstraint([\n                            'name' => $name,\n                            'columnNames' => ArrayHelper::getColumn($constraint, 'column_name'),\n                            'expression' => $constraint[0]['check_expr'],\n                        ]);\n                        break;\n                }\n            }\n        }\n        foreach ($result as $type => $data) {\n            $this->setTableMetadata($tableName, $type, $data);\n        }\n\n        return $result[$returnType];\n    }\n}\n"
  },
  {
    "path": "framework/db/sqlite/ColumnSchemaBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\sqlite;\n\nuse yii\\db\\ColumnSchemaBuilder as AbstractColumnSchemaBuilder;\n\n/**\n * ColumnSchemaBuilder is the schema builder for Sqlite databases.\n *\n * @author Chris Harris <chris@buckshotsoftware.com>\n * @since 2.0.8\n */\nclass ColumnSchemaBuilder extends AbstractColumnSchemaBuilder\n{\n    /**\n     * {@inheritdoc}\n     */\n    protected function buildUnsignedString()\n    {\n        return $this->isUnsigned ? ' UNSIGNED' : '';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function __toString()\n    {\n        switch ($this->getTypeCategory()) {\n            case self::CATEGORY_PK:\n                $format = '{type}{check}{append}';\n                break;\n            case self::CATEGORY_NUMERIC:\n                $format = '{type}{length}{unsigned}{notnull}{unique}{check}{default}{append}';\n                break;\n            default:\n                $format = '{type}{length}{notnull}{unique}{check}{default}{append}';\n        }\n\n        return $this->buildCompleteString($format);\n    }\n}\n"
  },
  {
    "path": "framework/db/sqlite/Command.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\sqlite;\n\nuse yii\\db\\SqlToken;\nuse yii\\helpers\\StringHelper;\n\n/**\n * Command represents an SQLite's SQL statement to be executed against a database.\n *\n * {@inheritdoc}\n *\n * @author Sergey Makinen <sergey@makinen.ru>\n * @since 2.0.14\n */\nclass Command extends \\yii\\db\\Command\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function execute()\n    {\n        $sql = $this->getSql();\n        $params = $this->params;\n        $statements = $this->splitStatements($sql, $params);\n        if ($statements === false) {\n            return parent::execute();\n        }\n\n        $result = null;\n        foreach ($statements as $statement) {\n            list($statementSql, $statementParams) = $statement;\n            $this->setSql($statementSql)->bindValues($statementParams);\n            $result = parent::execute();\n        }\n        $this->setSql($sql)->bindValues($params);\n        return $result;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function queryInternal($method, $fetchMode = null)\n    {\n        $sql = $this->getSql();\n        $params = $this->params;\n        $statements = $this->splitStatements($sql, $params);\n        if ($statements === false) {\n            return parent::queryInternal($method, $fetchMode);\n        }\n\n        list($lastStatementSql, $lastStatementParams) = array_pop($statements);\n        foreach ($statements as $statement) {\n            list($statementSql, $statementParams) = $statement;\n            $this->setSql($statementSql)->bindValues($statementParams);\n            parent::execute();\n        }\n        $this->setSql($lastStatementSql)->bindValues($lastStatementParams);\n        $result = parent::queryInternal($method, $fetchMode);\n        $this->setSql($sql)->bindValues($params);\n        return $result;\n    }\n\n    /**\n     * Splits the specified SQL code into individual SQL statements and returns them\n     * or `false` if there's a single statement.\n     * @param string $sql\n     * @param array $params\n     * @return list<array{string, array}>|false\n     */\n    private function splitStatements($sql, $params)\n    {\n        $semicolonIndex = strpos($sql, ';');\n        if ($semicolonIndex === false || $semicolonIndex === StringHelper::byteLength($sql) - 1) {\n            return false;\n        }\n\n        $tokenizer = new SqlTokenizer($sql);\n        $codeToken = $tokenizer->tokenize();\n        if (count($codeToken->getChildren()) === 1) {\n            return false;\n        }\n\n        $statements = [];\n        foreach ($codeToken->getChildren() as $statement) {\n            $statements[] = [$statement->getSql(), $this->extractUsedParams($statement, $params)];\n        }\n        return $statements;\n    }\n\n    /**\n     * Returns named bindings used in the specified statement token.\n     * @param SqlToken $statement\n     * @param array $params\n     * @return array\n     */\n    private function extractUsedParams(SqlToken $statement, $params)\n    {\n        preg_match_all('/(?P<placeholder>:\\w+)/', $statement->getSql(), $matches, PREG_SET_ORDER);\n        $result = [];\n        foreach ($matches as $match) {\n            $phName = ltrim($match['placeholder'], ':');\n            if (isset($params[$phName])) {\n                $result[$phName] = $params[$phName];\n            } elseif (isset($params[':' . $phName])) {\n                $result[':' . $phName] = $params[':' . $phName];\n            }\n        }\n        return $result;\n    }\n}\n"
  },
  {
    "path": "framework/db/sqlite/QueryBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\sqlite;\n\nuse yii\\base\\InvalidArgumentException;\nuse yii\\base\\NotSupportedException;\nuse yii\\db\\Connection;\nuse yii\\db\\Expression;\nuse yii\\db\\ExpressionInterface;\nuse yii\\db\\Query;\nuse yii\\helpers\\StringHelper;\n\n/**\n * QueryBuilder is the query builder for SQLite databases.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass QueryBuilder extends \\yii\\db\\QueryBuilder\n{\n    /**\n     * @var array mapping from abstract column types (keys) to physical column types (values).\n     */\n    public $typeMap = [\n        Schema::TYPE_PK => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL',\n        Schema::TYPE_UPK => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL',\n        Schema::TYPE_BIGPK => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL',\n        Schema::TYPE_UBIGPK => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL',\n        Schema::TYPE_CHAR => 'char(1)',\n        Schema::TYPE_STRING => 'varchar(255)',\n        Schema::TYPE_TEXT => 'text',\n        Schema::TYPE_TINYINT => 'tinyint',\n        Schema::TYPE_SMALLINT => 'smallint',\n        Schema::TYPE_INTEGER => 'integer',\n        Schema::TYPE_BIGINT => 'bigint',\n        Schema::TYPE_FLOAT => 'float',\n        Schema::TYPE_DOUBLE => 'double',\n        Schema::TYPE_DECIMAL => 'decimal(10,0)',\n        Schema::TYPE_DATETIME => 'datetime',\n        Schema::TYPE_TIMESTAMP => 'timestamp',\n        Schema::TYPE_TIME => 'time',\n        Schema::TYPE_DATE => 'date',\n        Schema::TYPE_BINARY => 'blob',\n        Schema::TYPE_BOOLEAN => 'boolean',\n        Schema::TYPE_MONEY => 'decimal(19,4)',\n    ];\n\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function defaultExpressionBuilders()\n    {\n        return array_merge(parent::defaultExpressionBuilders(), [\n            'yii\\db\\conditions\\LikeCondition' => 'yii\\db\\sqlite\\conditions\\LikeConditionBuilder',\n            'yii\\db\\conditions\\InCondition' => 'yii\\db\\sqlite\\conditions\\InConditionBuilder',\n        ]);\n    }\n\n    /**\n     * {@inheritdoc}\n     * @see https://stackoverflow.com/questions/15277373/sqlite-upsert-update-or-insert/15277374#15277374\n     */\n    public function upsert($table, $insertColumns, $updateColumns, &$params)\n    {\n        list($uniqueNames, $insertNames, $updateNames) = $this->prepareUpsertColumns($table, $insertColumns, $updateColumns, $constraints);\n        if (empty($uniqueNames)) {\n            return $this->insert($table, $insertColumns, $params);\n        }\n        if ($updateNames === []) {\n            // there are no columns to update\n            $updateColumns = false;\n        }\n\n        list(, $placeholders, $values, $params) = $this->prepareInsertValues($table, $insertColumns, $params);\n        $insertSql = 'INSERT OR IGNORE INTO ' . $this->db->quoteTableName($table)\n            . (!empty($insertNames) ? ' (' . implode(', ', $insertNames) . ')' : '')\n            . (!empty($placeholders) ? ' VALUES (' . implode(', ', $placeholders) . ')' : $values);\n        if ($updateColumns === false) {\n            return $insertSql;\n        }\n\n        $updateCondition = ['or'];\n        $quotedTableName = $this->db->quoteTableName($table);\n        foreach ($constraints as $constraint) {\n            $constraintCondition = ['and'];\n            foreach ($constraint->columnNames as $name) {\n                $quotedName = $this->db->quoteColumnName($name);\n                $constraintCondition[] = \"$quotedTableName.$quotedName=(SELECT $quotedName FROM `EXCLUDED`)\";\n            }\n            $updateCondition[] = $constraintCondition;\n        }\n        if ($updateColumns === true) {\n            $updateColumns = [];\n            foreach ($updateNames as $name) {\n                $quotedName = $this->db->quoteColumnName($name);\n                if (strrpos($quotedName, '.') === false) {\n                    $quotedName = \"(SELECT $quotedName FROM `EXCLUDED`)\";\n                }\n                $updateColumns[$name] = new Expression($quotedName);\n            }\n        }\n        $updateSql = 'WITH \"EXCLUDED\" (' . implode(', ', $insertNames)\n            . ') AS (' . (!empty($placeholders) ? 'VALUES (' . implode(', ', $placeholders) . ')' : ltrim($values, ' ')) . ') '\n            . $this->update($table, $updateColumns, $updateCondition, $params);\n        return \"$updateSql; $insertSql;\";\n    }\n\n    /**\n     * Generates a batch INSERT SQL statement.\n     *\n     * For example,\n     *\n     * ```\n     * $connection->createCommand()->batchInsert('user', ['name', 'age'], [\n     *     ['Tom', 30],\n     *     ['Jane', 20],\n     *     ['Linda', 25],\n     * ])->execute();\n     * ```\n     *\n     * Note that the values in each row must match the corresponding column names.\n     *\n     * @param string $table the table that new rows will be inserted into.\n     * @param array $columns the column names\n     * @param array|\\Generator $rows the rows to be batch inserted into the table\n     * @return string the batch INSERT SQL statement\n     */\n    public function batchInsert($table, $columns, $rows, &$params = [])\n    {\n        if (empty($rows)) {\n            return '';\n        }\n\n        // SQLite supports batch insert natively since 3.7.11\n        // https://www.sqlite.org/releaselog/3_7_11.html\n        $this->db->open(); // ensure pdo is not null\n        if (version_compare($this->db->getServerVersion(), '3.7.11', '>=')) {\n            return parent::batchInsert($table, $columns, $rows, $params);\n        }\n\n        $schema = $this->db->getSchema();\n        if (($tableSchema = $schema->getTableSchema($table)) !== null) {\n            $columnSchemas = $tableSchema->columns;\n        } else {\n            $columnSchemas = [];\n        }\n\n        $values = [];\n        foreach ($rows as $row) {\n            $vs = [];\n            foreach ($row as $i => $value) {\n                if (isset($columnSchemas[$columns[$i]])) {\n                    $value = $columnSchemas[$columns[$i]]->dbTypecast($value);\n                }\n                if (is_string($value)) {\n                    $value = $schema->quoteValue($value);\n                } elseif (is_float($value)) {\n                    // ensure type cast always has . as decimal separator in all locales\n                    $value = StringHelper::floatToString($value);\n                } elseif ($value === false) {\n                    $value = 0;\n                } elseif ($value === null) {\n                    $value = 'NULL';\n                } elseif ($value instanceof ExpressionInterface) {\n                    $value = $this->buildExpression($value, $params);\n                }\n                $vs[] = $value;\n            }\n            $values[] = implode(', ', $vs);\n        }\n        if (empty($values)) {\n            return '';\n        }\n\n        foreach ($columns as $i => $name) {\n            $columns[$i] = $schema->quoteColumnName($name);\n        }\n\n        return 'INSERT INTO ' . $schema->quoteTableName($table)\n        . ' (' . implode(', ', $columns) . ') SELECT ' . implode(' UNION SELECT ', $values);\n    }\n\n    /**\n     * Creates a SQL statement for resetting the sequence value of a table's primary key.\n     * The sequence will be reset such that the primary key of the next new row inserted\n     * will have the specified value or 1.\n     * @param string $tableName the name of the table whose primary key sequence will be reset\n     * @param mixed $value the value for the primary key of the next new row inserted. If this is not set,\n     * the next new row's primary key will have a value 1.\n     * @return string the SQL statement for resetting sequence\n     * @throws InvalidArgumentException if the table does not exist or there is no sequence associated with the table.\n     */\n    public function resetSequence($tableName, $value = null)\n    {\n        $db = $this->db;\n        $table = $db->getTableSchema($tableName);\n        if ($table !== null && $table->sequenceName !== null) {\n            $tableName = $db->quoteTableName($tableName);\n            if ($value === null) {\n                $key = $this->db->quoteColumnName(reset($table->primaryKey));\n                $value = $this->db->useMaster(function (Connection $db) use ($key, $tableName) {\n                    return $db->createCommand(\"SELECT MAX($key) FROM $tableName\")->queryScalar();\n                });\n            } else {\n                $value = (int) $value - 1;\n            }\n\n            return \"UPDATE sqlite_sequence SET seq='$value' WHERE name='{$table->name}'\";\n        } elseif ($table === null) {\n            throw new InvalidArgumentException(\"Table not found: $tableName\");\n        }\n\n        throw new InvalidArgumentException(\"There is not sequence associated with table '$tableName'.'\");\n    }\n\n    /**\n     * Enables or disables integrity check.\n     * @param bool $check whether to turn on or off the integrity check.\n     * @param string $schema the schema of the tables. Meaningless for SQLite.\n     * @param string $table the table name. Meaningless for SQLite.\n     * @return string the SQL statement for checking integrity\n     * @throws NotSupportedException this is not supported by SQLite\n     */\n    public function checkIntegrity($check = true, $schema = '', $table = '')\n    {\n        return 'PRAGMA foreign_keys=' . (int) $check;\n    }\n\n    /**\n     * Builds a SQL statement for truncating a DB table.\n     * @param string $table the table to be truncated. The name will be properly quoted by the method.\n     * @return string the SQL statement for truncating a DB table.\n     */\n    public function truncateTable($table)\n    {\n        return 'DELETE FROM ' . $this->db->quoteTableName($table);\n    }\n\n    /**\n     * Builds a SQL statement for dropping an index.\n     * @param string $name the name of the index to be dropped. The name will be properly quoted by the method.\n     * @param string $table the table whose index is to be dropped. The name will be properly quoted by the method.\n     * @return string the SQL statement for dropping an index.\n     */\n    public function dropIndex($name, $table)\n    {\n        return 'DROP INDEX ' . $this->db->quoteTableName($name);\n    }\n\n    /**\n     * Builds a SQL statement for dropping a DB column.\n     * @param string $table the table whose column is to be dropped. The name will be properly quoted by the method.\n     * @param string $column the name of the column to be dropped. The name will be properly quoted by the method.\n     * @return string the SQL statement for dropping a DB column.\n     * @throws NotSupportedException this is not supported by SQLite\n     */\n    public function dropColumn($table, $column)\n    {\n        throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');\n    }\n\n    /**\n     * Builds a SQL statement for renaming a column.\n     * @param string $table the table whose column is to be renamed. The name will be properly quoted by the method.\n     * @param string $oldName the old name of the column. The name will be properly quoted by the method.\n     * @param string $newName the new name of the column. The name will be properly quoted by the method.\n     * @return string the SQL statement for renaming a DB column.\n     * @throws NotSupportedException this is not supported by SQLite\n     */\n    public function renameColumn($table, $oldName, $newName)\n    {\n        throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');\n    }\n\n    /**\n     * Builds a SQL statement for adding a foreign key constraint to an existing table.\n     * The method will properly quote the table and column names.\n     * @param string $name the name of the foreign key constraint.\n     * @param string $table the table that the foreign key constraint will be added to.\n     * @param string|array $columns the name of the column to that the constraint will be added on.\n     * If there are multiple columns, separate them with commas or use an array to represent them.\n     * @param string $refTable the table that the foreign key references to.\n     * @param string|array $refColumns the name of the column that the foreign key references to.\n     * If there are multiple columns, separate them with commas or use an array to represent them.\n     * @param string|null $delete the ON DELETE option. Most DBMS support these options: RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL\n     * @param string|null $update the ON UPDATE option. Most DBMS support these options: RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL\n     * @return string the SQL statement for adding a foreign key constraint to an existing table.\n     * @throws NotSupportedException this is not supported by SQLite\n     */\n    public function addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete = null, $update = null)\n    {\n        throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');\n    }\n\n    /**\n     * Builds a SQL statement for dropping a foreign key constraint.\n     * @param string $name the name of the foreign key constraint to be dropped. The name will be properly quoted by the method.\n     * @param string $table the table whose foreign is to be dropped. The name will be properly quoted by the method.\n     * @return string the SQL statement for dropping a foreign key constraint.\n     * @throws NotSupportedException this is not supported by SQLite\n     */\n    public function dropForeignKey($name, $table)\n    {\n        throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');\n    }\n\n    /**\n     * Builds a SQL statement for renaming a DB table.\n     *\n     * @param string $table the table to be renamed. The name will be properly quoted by the method.\n     * @param string $newName the new table name. The name will be properly quoted by the method.\n     * @return string the SQL statement for renaming a DB table.\n     */\n    public function renameTable($table, $newName)\n    {\n        return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' RENAME TO ' . $this->db->quoteTableName($newName);\n    }\n\n    /**\n     * Builds a SQL statement for changing the definition of a column.\n     * @param string $table the table whose column is to be changed. The table name will be properly quoted by the method.\n     * @param string $column the name of the column to be changed. The name will be properly quoted by the method.\n     * @param string $type the new column type. The [[getColumnType()]] method will be invoked to convert abstract\n     * column type (if any) into the physical one. Anything that is not recognized as abstract type will be kept\n     * in the generated SQL. For example, 'string' will be turned into 'varchar(255)', while 'string not null'\n     * will become 'varchar(255) not null'.\n     * @return string the SQL statement for changing the definition of a column.\n     * @throws NotSupportedException this is not supported by SQLite\n     */\n    public function alterColumn($table, $column, $type)\n    {\n        throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');\n    }\n\n    /**\n     * Builds a SQL statement for adding a primary key constraint to an existing table.\n     * @param string $name the name of the primary key constraint.\n     * @param string $table the table that the primary key constraint will be added to.\n     * @param string|array $columns comma separated string or array of columns that the primary key will consist of.\n     * @return string the SQL statement for adding a primary key constraint to an existing table.\n     * @throws NotSupportedException this is not supported by SQLite\n     */\n    public function addPrimaryKey($name, $table, $columns)\n    {\n        throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');\n    }\n\n    /**\n     * Builds a SQL statement for removing a primary key constraint to an existing table.\n     * @param string $name the name of the primary key constraint to be removed.\n     * @param string $table the table that the primary key constraint will be removed from.\n     * @return string the SQL statement for removing a primary key constraint from an existing table.\n     * @throws NotSupportedException this is not supported by SQLite\n     */\n    public function dropPrimaryKey($name, $table)\n    {\n        throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');\n    }\n\n    /**\n     * {@inheritdoc}\n     * @throws NotSupportedException this is not supported by SQLite.\n     */\n    public function addUnique($name, $table, $columns)\n    {\n        throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');\n    }\n\n    /**\n     * {@inheritdoc}\n     * @throws NotSupportedException this is not supported by SQLite.\n     */\n    public function dropUnique($name, $table)\n    {\n        throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');\n    }\n\n    /**\n     * {@inheritdoc}\n     * @throws NotSupportedException this is not supported by SQLite.\n     */\n    public function addCheck($name, $table, $expression)\n    {\n        throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');\n    }\n\n    /**\n     * {@inheritdoc}\n     * @throws NotSupportedException this is not supported by SQLite.\n     */\n    public function dropCheck($name, $table)\n    {\n        throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');\n    }\n\n    /**\n     * {@inheritdoc}\n     * @throws NotSupportedException this is not supported by SQLite.\n     */\n    public function addDefaultValue($name, $table, $column, $value)\n    {\n        throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');\n    }\n\n    /**\n     * {@inheritdoc}\n     * @throws NotSupportedException this is not supported by SQLite.\n     */\n    public function dropDefaultValue($name, $table)\n    {\n        throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');\n    }\n\n    /**\n     * {@inheritdoc}\n     * @throws NotSupportedException\n     * @since 2.0.8\n     */\n    public function addCommentOnColumn($table, $column, $comment)\n    {\n        throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');\n    }\n\n    /**\n     * {@inheritdoc}\n     * @throws NotSupportedException\n     * @since 2.0.8\n     */\n    public function addCommentOnTable($table, $comment)\n    {\n        throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');\n    }\n\n    /**\n     * {@inheritdoc}\n     * @throws NotSupportedException\n     * @since 2.0.8\n     */\n    public function dropCommentFromColumn($table, $column)\n    {\n        throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');\n    }\n\n    /**\n     * {@inheritdoc}\n     * @throws NotSupportedException\n     * @since 2.0.8\n     */\n    public function dropCommentFromTable($table)\n    {\n        throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function buildLimit($limit, $offset)\n    {\n        $sql = '';\n        if ($this->hasLimit($limit)) {\n            $sql = 'LIMIT ' . $limit;\n            if ($this->hasOffset($offset)) {\n                $sql .= ' OFFSET ' . $offset;\n            }\n        } elseif ($this->hasOffset($offset)) {\n            // limit is not optional in SQLite\n            // https://www.sqlite.org/syntaxdiagrams.html#select-stmt\n            $sql = \"LIMIT 9223372036854775807 OFFSET $offset\"; // 2^63-1\n        }\n\n        return $sql;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function build($query, $params = [])\n    {\n        $query = $query->prepare($this);\n\n        $params = empty($params) ? $query->params : array_merge($params, $query->params);\n\n        $clauses = [\n            $this->buildSelect($query->select, $params, $query->distinct, $query->selectOption),\n            $this->buildFrom($query->from, $params),\n            $this->buildJoin($query->join, $params),\n            $this->buildWhere($query->where, $params),\n            $this->buildGroupBy($query->groupBy),\n            $this->buildHaving($query->having, $params),\n        ];\n\n        $sql = implode($this->separator, array_filter($clauses));\n        $sql = $this->buildOrderByAndLimit($sql, $query->orderBy, $query->limit, $query->offset);\n\n        if (!empty($query->orderBy)) {\n            foreach ($query->orderBy as $expression) {\n                if ($expression instanceof ExpressionInterface) {\n                    $this->buildExpression($expression, $params);\n                }\n            }\n        }\n        if (!empty($query->groupBy)) {\n            foreach ($query->groupBy as $expression) {\n                if ($expression instanceof ExpressionInterface) {\n                    $this->buildExpression($expression, $params);\n                }\n            }\n        }\n\n        $union = $this->buildUnion($query->union, $params);\n        if ($union !== '') {\n            $sql = \"$sql{$this->separator}$union\";\n        }\n\n        $with = $this->buildWithQueries($query->withQueries, $params);\n        if ($with !== '') {\n            $sql = \"$with{$this->separator}$sql\";\n        }\n\n        return [$sql, $params];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function buildUnion($unions, &$params)\n    {\n        if (empty($unions)) {\n            return '';\n        }\n\n        $result = '';\n\n        foreach ($unions as $i => $union) {\n            $query = $union['query'];\n            if ($query instanceof Query) {\n                list($unions[$i]['query'], $params) = $this->build($query, $params);\n            }\n\n            $result .= ' UNION ' . ($union['all'] ? 'ALL ' : '') . ' ' . $unions[$i]['query'];\n        }\n\n        return trim($result);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function createIndex($name, $table, $columns, $unique = false)\n    {\n        $tableParts = explode('.', $table);\n\n        $schema = null;\n        if (count($tableParts) === 2) {\n            list ($schema, $table) = $tableParts;\n        }\n\n        return ($unique ? 'CREATE UNIQUE INDEX ' : 'CREATE INDEX ')\n            . $this->db->quoteTableName(($schema ? $schema . '.' : '') . $name) . ' ON '\n            . $this->db->quoteTableName($table)\n            . ' (' . $this->buildColumns($columns) . ')';\n    }\n}\n"
  },
  {
    "path": "framework/db/sqlite/Schema.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\sqlite;\n\nuse Yii;\nuse yii\\base\\NotSupportedException;\nuse yii\\db\\CheckConstraint;\nuse yii\\db\\ColumnSchema;\nuse yii\\db\\Constraint;\nuse yii\\db\\ConstraintFinderInterface;\nuse yii\\db\\ConstraintFinderTrait;\nuse yii\\db\\Expression;\nuse yii\\db\\ForeignKeyConstraint;\nuse yii\\db\\IndexConstraint;\nuse yii\\db\\SqlToken;\nuse yii\\db\\TableSchema;\nuse yii\\db\\Transaction;\nuse yii\\helpers\\ArrayHelper;\nuse yii\\db\\Schema as BaseSchema;\n\n/**\n * Schema is the class for retrieving metadata from a SQLite (2/3) database.\n *\n * @property-write string $transactionIsolationLevel The transaction isolation level to use for this\n * transaction. This can be either [[Transaction::READ_UNCOMMITTED]] or [[Transaction::SERIALIZABLE]].\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of ColumnSchema = ColumnSchema\n * @extends BaseSchema<T>\n */\nclass Schema extends BaseSchema implements ConstraintFinderInterface\n{\n    use ConstraintFinderTrait;\n\n    /**\n     * @var array mapping from physical column types (keys) to abstract column types (values)\n     */\n    public $typeMap = [\n        'tinyint' => self::TYPE_TINYINT,\n        'bit' => self::TYPE_SMALLINT,\n        'boolean' => self::TYPE_BOOLEAN,\n        'bool' => self::TYPE_BOOLEAN,\n        'smallint' => self::TYPE_SMALLINT,\n        'mediumint' => self::TYPE_INTEGER,\n        'int' => self::TYPE_INTEGER,\n        'integer' => self::TYPE_INTEGER,\n        'bigint' => self::TYPE_BIGINT,\n        'float' => self::TYPE_FLOAT,\n        'double' => self::TYPE_DOUBLE,\n        'real' => self::TYPE_FLOAT,\n        'decimal' => self::TYPE_DECIMAL,\n        'numeric' => self::TYPE_DECIMAL,\n        'tinytext' => self::TYPE_TEXT,\n        'mediumtext' => self::TYPE_TEXT,\n        'longtext' => self::TYPE_TEXT,\n        'text' => self::TYPE_TEXT,\n        'varchar' => self::TYPE_STRING,\n        'string' => self::TYPE_STRING,\n        'char' => self::TYPE_CHAR,\n        'blob' => self::TYPE_BINARY,\n        'datetime' => self::TYPE_DATETIME,\n        'year' => self::TYPE_DATE,\n        'date' => self::TYPE_DATE,\n        'time' => self::TYPE_TIME,\n        'timestamp' => self::TYPE_TIMESTAMP,\n        'enum' => self::TYPE_STRING,\n    ];\n\n    /**\n     * {@inheritdoc}\n     */\n    protected $tableQuoteCharacter = '`';\n    /**\n     * {@inheritdoc}\n     */\n    protected $columnQuoteCharacter = '`';\n\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function findTableNames($schema = '')\n    {\n        $sql = \"SELECT DISTINCT tbl_name FROM sqlite_master WHERE tbl_name<>'sqlite_sequence' ORDER BY tbl_name\";\n        return $this->db->createCommand($sql)->queryColumn();\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableSchema($name)\n    {\n        $table = new TableSchema();\n        $table->name = $name;\n        $table->fullName = $name;\n\n        if ($this->findColumns($table)) {\n            $this->findConstraints($table);\n            return $table;\n        }\n\n        return null;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTablePrimaryKey($tableName)\n    {\n        return $this->loadTableConstraints($tableName, 'primaryKey');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableForeignKeys($tableName)\n    {\n        $foreignKeys = $this->db->createCommand('PRAGMA FOREIGN_KEY_LIST (' . $this->quoteValue($tableName) . ')')->queryAll();\n        $foreignKeys = $this->normalizePdoRowKeyCase($foreignKeys, true);\n        $foreignKeys = ArrayHelper::index($foreignKeys, null, 'table');\n        ArrayHelper::multisort($foreignKeys, 'seq', SORT_ASC, SORT_NUMERIC);\n        $result = [];\n        foreach ($foreignKeys as $table => $foreignKey) {\n            $result[] = new ForeignKeyConstraint([\n                'columnNames' => ArrayHelper::getColumn($foreignKey, 'from'),\n                'foreignTableName' => $table,\n                'foreignColumnNames' => ArrayHelper::getColumn($foreignKey, 'to'),\n                'onDelete' => isset($foreignKey[0]['on_delete']) ? $foreignKey[0]['on_delete'] : null,\n                'onUpdate' => isset($foreignKey[0]['on_update']) ? $foreignKey[0]['on_update'] : null,\n            ]);\n        }\n\n        return $result;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableIndexes($tableName)\n    {\n        return $this->loadTableConstraints($tableName, 'indexes');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableUniques($tableName)\n    {\n        return $this->loadTableConstraints($tableName, 'uniques');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function loadTableChecks($tableName)\n    {\n        $sql = $this->db->createCommand('SELECT `sql` FROM `sqlite_master` WHERE name = :tableName', [\n            ':tableName' => $tableName,\n        ])->queryScalar();\n        /** @var SqlToken[]|SqlToken[][]|SqlToken[][][] $code */\n        $code = (new SqlTokenizer($sql))->tokenize();\n        $pattern = (new SqlTokenizer('any CREATE any TABLE any()'))->tokenize();\n        if (!$code[0]->matches($pattern, 0, $firstMatchIndex, $lastMatchIndex)) {\n            return [];\n        }\n\n        $createTableToken = $code[0][$lastMatchIndex - 1];\n        $result = [];\n        $offset = 0;\n        while (true) {\n            $pattern = (new SqlTokenizer('any CHECK()'))->tokenize();\n            if (!$createTableToken->matches($pattern, $offset, $firstMatchIndex, $offset)) {\n                break;\n            }\n\n            $checkSql = $createTableToken[$offset - 1]->getSql();\n            $name = null;\n            $pattern = (new SqlTokenizer('CONSTRAINT any'))->tokenize();\n            if (isset($createTableToken[$firstMatchIndex - 2]) && $createTableToken->matches($pattern, $firstMatchIndex - 2)) {\n                $name = $createTableToken[$firstMatchIndex - 1]->content;\n            }\n            $result[] = new CheckConstraint([\n                'name' => $name,\n                'expression' => $checkSql,\n            ]);\n        }\n\n        return $result;\n    }\n\n    /**\n     * {@inheritdoc}\n     * @throws NotSupportedException if this method is called.\n     */\n    protected function loadTableDefaultValues($tableName)\n    {\n        throw new NotSupportedException('SQLite does not support default value constraints.');\n    }\n\n    /**\n     * Creates a query builder for the MySQL database.\n     * This method may be overridden by child classes to create a DBMS-specific query builder.\n     * @return QueryBuilder query builder instance\n     */\n    public function createQueryBuilder()\n    {\n        return Yii::createObject(QueryBuilder::className(), [$this->db]);\n    }\n\n    /**\n     * {@inheritdoc}\n     * @return ColumnSchemaBuilder column schema builder instance\n     */\n    public function createColumnSchemaBuilder($type, $length = null)\n    {\n        return Yii::createObject(ColumnSchemaBuilder::className(), [$type, $length]);\n    }\n\n    /**\n     * Collects the table column metadata.\n     * @param TableSchema $table the table metadata\n     * @return bool whether the table exists in the database\n     */\n    protected function findColumns($table)\n    {\n        $sql = 'PRAGMA table_info(' . $this->quoteSimpleTableName($table->name) . ')';\n        $columns = $this->db->createCommand($sql)->queryAll();\n        if (empty($columns)) {\n            return false;\n        }\n\n        foreach ($columns as $info) {\n            $column = $this->loadColumnSchema($info);\n            $table->columns[$column->name] = $column;\n            if ($column->isPrimaryKey) {\n                $table->primaryKey[] = $column->name;\n            }\n        }\n        if (count($table->primaryKey) === 1 && !strncasecmp($table->columns[$table->primaryKey[0]]->dbType, 'int', 3)) {\n            $table->sequenceName = '';\n            $table->columns[$table->primaryKey[0]]->autoIncrement = true;\n        }\n\n        return true;\n    }\n\n    /**\n     * Collects the foreign key column details for the given table.\n     * @param TableSchema $table the table metadata\n     */\n    protected function findConstraints($table)\n    {\n        $sql = 'PRAGMA foreign_key_list(' . $this->quoteSimpleTableName($table->name) . ')';\n        $keys = $this->db->createCommand($sql)->queryAll();\n        foreach ($keys as $key) {\n            $id = (int) $key['id'];\n            if (!isset($table->foreignKeys[$id])) {\n                $table->foreignKeys[$id] = [$key['table'], $key['from'] => $key['to']];\n            } else {\n                // composite FK\n                $table->foreignKeys[$id][$key['from']] = $key['to'];\n            }\n        }\n    }\n\n    /**\n     * Returns all unique indexes for the given table.\n     *\n     * Each array element is of the following structure:\n     *\n     * ```\n     * [\n     *     'IndexName1' => ['col1' [, ...]],\n     *     'IndexName2' => ['col2' [, ...]],\n     * ]\n     * ```\n     *\n     * @param TableSchema $table the table metadata\n     * @return array all unique indexes for the given table.\n     */\n    public function findUniqueIndexes($table)\n    {\n        $sql = 'PRAGMA index_list(' . $this->quoteSimpleTableName($table->name) . ')';\n        $indexes = $this->db->createCommand($sql)->queryAll();\n        $uniqueIndexes = [];\n\n        foreach ($indexes as $index) {\n            $indexName = $index['name'];\n            $indexInfo = $this->db->createCommand('PRAGMA index_info(' . $this->quoteValue($index['name']) . ')')->queryAll();\n\n            if ($index['unique']) {\n                $uniqueIndexes[$indexName] = [];\n                foreach ($indexInfo as $row) {\n                    $uniqueIndexes[$indexName][] = $row['name'];\n                }\n            }\n        }\n\n        return $uniqueIndexes;\n    }\n\n    /**\n     * Loads the column information into a [[ColumnSchema]] object.\n     * @param array $info column information\n     * @return T the column schema object\n     */\n    protected function loadColumnSchema($info)\n    {\n        $column = $this->createColumnSchema();\n        $column->name = $info['name'];\n        $column->allowNull = !$info['notnull'];\n        $column->isPrimaryKey = $info['pk'] != 0;\n\n        $column->dbType = strtolower($info['type']);\n        $column->unsigned = strpos($column->dbType, 'unsigned') !== false;\n\n        $column->type = self::TYPE_STRING;\n        if (preg_match('/^(\\w+)(?:\\(([^\\)]+)\\))?/', $column->dbType, $matches)) {\n            $type = strtolower($matches[1]);\n            if (isset($this->typeMap[$type])) {\n                $column->type = $this->typeMap[$type];\n            }\n\n            if (!empty($matches[2])) {\n                $values = explode(',', $matches[2]);\n                $column->size = $column->precision = (int) $values[0];\n                if (isset($values[1])) {\n                    $column->scale = (int) $values[1];\n                }\n                if ($column->size === 1 && ($type === 'tinyint' || $type === 'bit')) {\n                    $column->type = 'boolean';\n                } elseif ($type === 'bit') {\n                    if ($column->size > 32) {\n                        $column->type = 'bigint';\n                    } elseif ($column->size === 32) {\n                        $column->type = 'integer';\n                    }\n                }\n            }\n        }\n        $column->phpType = $this->getColumnPhpType($column);\n\n        if (!$column->isPrimaryKey) {\n            if ($info['dflt_value'] === 'null' || $info['dflt_value'] === '' || $info['dflt_value'] === null) {\n                $column->defaultValue = null;\n            } elseif ($column->type === 'timestamp' && $info['dflt_value'] === 'CURRENT_TIMESTAMP') {\n                $column->defaultValue = new Expression('CURRENT_TIMESTAMP');\n            } else {\n                $value = trim($info['dflt_value'], \"'\\\"\");\n                $column->defaultValue = $column->phpTypecast($value);\n            }\n        }\n\n        return $column;\n    }\n\n    /**\n     * Sets the isolation level of the current transaction.\n     * @param string $level The transaction isolation level to use for this transaction.\n     * This can be either [[Transaction::READ_UNCOMMITTED]] or [[Transaction::SERIALIZABLE]].\n     * @throws NotSupportedException when unsupported isolation levels are used.\n     * SQLite only supports SERIALIZABLE and READ UNCOMMITTED.\n     * @see https://www.sqlite.org/pragma.html#pragma_read_uncommitted\n     */\n    public function setTransactionIsolationLevel($level)\n    {\n        switch ($level) {\n            case Transaction::SERIALIZABLE:\n                $this->db->createCommand('PRAGMA read_uncommitted = False;')->execute();\n                break;\n            case Transaction::READ_UNCOMMITTED:\n                $this->db->createCommand('PRAGMA read_uncommitted = True;')->execute();\n                break;\n            default:\n                throw new NotSupportedException(get_class($this) . ' only supports transaction isolation levels READ UNCOMMITTED and SERIALIZABLE.');\n        }\n    }\n\n    /**\n     * Returns table columns info.\n     * @param string $tableName table name\n     * @return array\n     */\n    private function loadTableColumnsInfo($tableName)\n    {\n        $tableColumns = $this->db->createCommand('PRAGMA TABLE_INFO (' . $this->quoteValue($tableName) . ')')->queryAll();\n        $tableColumns = $this->normalizePdoRowKeyCase($tableColumns, true);\n\n        return ArrayHelper::index($tableColumns, 'cid');\n    }\n\n    /**\n     * Loads multiple types of constraints and returns the specified ones.\n     * @param string $tableName table name.\n     * @param string $returnType return type:\n     * - primaryKey\n     * - indexes\n     * - uniques\n     * @return mixed constraints.\n     */\n    private function loadTableConstraints($tableName, $returnType)\n    {\n        $indexes = $this->db->createCommand('PRAGMA INDEX_LIST (' . $this->quoteValue($tableName) . ')')->queryAll();\n        $indexes = $this->normalizePdoRowKeyCase($indexes, true);\n        $tableColumns = null;\n        if (!empty($indexes) && !isset($indexes[0]['origin'])) {\n            /*\n             * SQLite may not have an \"origin\" column in INDEX_LIST\n             * See https://www.sqlite.org/src/info/2743846cdba572f6\n             */\n            $tableColumns = $this->loadTableColumnsInfo($tableName);\n        }\n        $result = [\n            'primaryKey' => null,\n            'indexes' => [],\n            'uniques' => [],\n        ];\n        foreach ($indexes as $index) {\n            $columns = $this->db->createCommand('PRAGMA INDEX_INFO (' . $this->quoteValue($index['name']) . ')')->queryAll();\n            $columns = $this->normalizePdoRowKeyCase($columns, true);\n            ArrayHelper::multisort($columns, 'seqno', SORT_ASC, SORT_NUMERIC);\n            if ($tableColumns !== null) {\n                // SQLite may not have an \"origin\" column in INDEX_LIST\n                $index['origin'] = 'c';\n                if (!empty($columns) && $tableColumns[$columns[0]['cid']]['pk'] > 0) {\n                    $index['origin'] = 'pk';\n                } elseif ($index['unique'] && $this->isSystemIdentifier($index['name'])) {\n                    $index['origin'] = 'u';\n                }\n            }\n            $result['indexes'][] = new IndexConstraint([\n                'isPrimary' => $index['origin'] === 'pk',\n                'isUnique' => (bool) $index['unique'],\n                'name' => $index['name'],\n                'columnNames' => ArrayHelper::getColumn($columns, 'name'),\n            ]);\n            if ($index['origin'] === 'u') {\n                $result['uniques'][] = new Constraint([\n                    'name' => $index['name'],\n                    'columnNames' => ArrayHelper::getColumn($columns, 'name'),\n                ]);\n            } elseif ($index['origin'] === 'pk') {\n                $result['primaryKey'] = new Constraint([\n                    'columnNames' => ArrayHelper::getColumn($columns, 'name'),\n                ]);\n            }\n        }\n\n        if ($result['primaryKey'] === null) {\n            /*\n             * Additional check for PK in case of INTEGER PRIMARY KEY with ROWID\n             * See https://www.sqlite.org/lang_createtable.html#primkeyconst\n             */\n            if ($tableColumns === null) {\n                $tableColumns = $this->loadTableColumnsInfo($tableName);\n            }\n            foreach ($tableColumns as $tableColumn) {\n                if ($tableColumn['pk'] > 0) {\n                    $result['primaryKey'] = new Constraint([\n                        'columnNames' => [$tableColumn['name']],\n                    ]);\n                    break;\n                }\n            }\n        }\n\n        foreach ($result as $type => $data) {\n            $this->setTableMetadata($tableName, $type, $data);\n        }\n\n        return $result[$returnType];\n    }\n\n    /**\n     * Return whether the specified identifier is a SQLite system identifier.\n     * @param string $identifier\n     * @return bool\n     * @see https://www.sqlite.org/src/artifact/74108007d286232f\n     */\n    private function isSystemIdentifier($identifier)\n    {\n        return strncmp($identifier, 'sqlite_', 7) === 0;\n    }\n\n    /**\n     * @inheritdoc\n     *\n     * Since PHP 8.5, `PDO::quote()` throws a ValueError when the string contains null bytes (\"\\0\").\n     *\n     * This method sanitizes such bytes before calling the parent implementation to avoid exceptions while maintaining\n     * backward compatibility.\n     *\n     * @link https://github.com/php/php-src/commit/0a10f6db26875e0f1d0f867307cee591d29a43c7\n     */\n    public function quoteValue($value)\n    {\n        if (PHP_VERSION_ID >= 80500 && is_string($value) && str_contains($value, \"\\0\")) {\n            // Sanitize null bytes to prevent PDO ValueError on PHP 8.5+\n            $value = str_replace(\"\\0\", '', $value);\n        }\n\n        return parent::quoteValue($value);\n    }\n}\n"
  },
  {
    "path": "framework/db/sqlite/SqlTokenizer.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\sqlite;\n\n/**\n * SqlTokenizer splits SQLite query into individual SQL tokens.\n * It's used to obtain a `CHECK` constraint information from a `CREATE TABLE` SQL code.\n *\n * @see https://www.sqlite.org/draft/tokenreq.html\n * @see https://sqlite.org/lang.html\n * @author Sergey Makinen <sergey@makinen.ru>\n * @since 2.0.13\n */\nclass SqlTokenizer extends \\yii\\db\\SqlTokenizer\n{\n    /**\n     * {@inheritdoc}\n     */\n    protected function isWhitespace(&$length)\n    {\n        static $whitespaces = [\n            \"\\f\" => true,\n            \"\\n\" => true,\n            \"\\r\" => true,\n            \"\\t\" => true,\n            ' ' => true,\n        ];\n\n        $length = 1;\n        return isset($whitespaces[$this->substring($length)]);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function isComment(&$length)\n    {\n        static $comments = [\n            '--' => true,\n            '/*' => true,\n        ];\n\n        $length = 2;\n        if (!isset($comments[$this->substring($length)])) {\n            return false;\n        }\n\n        if ($this->substring($length) === '--') {\n            $length = $this->indexAfter(\"\\n\") - $this->offset;\n        } else {\n            $length = $this->indexAfter('*/') - $this->offset;\n        }\n\n        return true;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function isOperator(&$length, &$content)\n    {\n        static $operators = [\n            '!=',\n            '%',\n            '&',\n            '(',\n            ')',\n            '*',\n            '+',\n            ',',\n            '-',\n            '.',\n            '/',\n            ';',\n            '<',\n            '<<',\n            '<=',\n            '<>',\n            '=',\n            '==',\n            '>',\n            '>=',\n            '>>',\n            '|',\n            '||',\n            '~',\n        ];\n\n        return $this->startsWithAnyLongest($operators, true, $length);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function isIdentifier(&$length, &$content)\n    {\n        static $identifierDelimiters = [\n            '\"' => '\"',\n            '[' => ']',\n            '`' => '`',\n        ];\n\n        if (!isset($identifierDelimiters[$this->substring(1)])) {\n            return false;\n        }\n\n        $delimiter = $identifierDelimiters[$this->substring(1)];\n        $offset = $this->offset;\n        while (true) {\n            $offset = $this->indexAfter($delimiter, $offset + 1);\n            if ($delimiter === ']' || $this->substring(1, true, $offset) !== $delimiter) {\n                break;\n            }\n        }\n        $length = $offset - $this->offset;\n        $content = $this->substring($length - 2, true, $this->offset + 1);\n        if ($delimiter !== ']') {\n            $content = strtr($content, [\"$delimiter$delimiter\" => $delimiter]);\n        }\n\n        return true;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function isStringLiteral(&$length, &$content)\n    {\n        if ($this->substring(1) !== \"'\") {\n            return false;\n        }\n\n        $offset = $this->offset;\n        while (true) {\n            $offset = $this->indexAfter(\"'\", $offset + 1);\n            if ($this->substring(1, true, $offset) !== \"'\") {\n                break;\n            }\n        }\n        $length = $offset - $this->offset;\n        $content = strtr($this->substring($length - 2, true, $this->offset + 1), [\"''\" => \"'\"]);\n        return true;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function isKeyword($string, &$content)\n    {\n        static $keywords = [\n            'ABORT' => true,\n            'ACTION' => true,\n            'ADD' => true,\n            'AFTER' => true,\n            'ALL' => true,\n            'ALTER' => true,\n            'ANALYZE' => true,\n            'AND' => true,\n            'AS' => true,\n            'ASC' => true,\n            'ATTACH' => true,\n            'AUTOINCREMENT' => true,\n            'BEFORE' => true,\n            'BEGIN' => true,\n            'BETWEEN' => true,\n            'BY' => true,\n            'CASCADE' => true,\n            'CASE' => true,\n            'CAST' => true,\n            'CHECK' => true,\n            'COLLATE' => true,\n            'COLUMN' => true,\n            'COMMIT' => true,\n            'CONFLICT' => true,\n            'CONSTRAINT' => true,\n            'CREATE' => true,\n            'CROSS' => true,\n            'CURRENT_DATE' => true,\n            'CURRENT_TIME' => true,\n            'CURRENT_TIMESTAMP' => true,\n            'DATABASE' => true,\n            'DEFAULT' => true,\n            'DEFERRABLE' => true,\n            'DEFERRED' => true,\n            'DELETE' => true,\n            'DESC' => true,\n            'DETACH' => true,\n            'DISTINCT' => true,\n            'DROP' => true,\n            'EACH' => true,\n            'ELSE' => true,\n            'END' => true,\n            'ESCAPE' => true,\n            'EXCEPT' => true,\n            'EXCLUSIVE' => true,\n            'EXISTS' => true,\n            'EXPLAIN' => true,\n            'FAIL' => true,\n            'FOR' => true,\n            'FOREIGN' => true,\n            'FROM' => true,\n            'FULL' => true,\n            'GLOB' => true,\n            'GROUP' => true,\n            'HAVING' => true,\n            'IF' => true,\n            'IGNORE' => true,\n            'IMMEDIATE' => true,\n            'IN' => true,\n            'INDEX' => true,\n            'INDEXED' => true,\n            'INITIALLY' => true,\n            'INNER' => true,\n            'INSERT' => true,\n            'INSTEAD' => true,\n            'INTERSECT' => true,\n            'INTO' => true,\n            'IS' => true,\n            'ISNULL' => true,\n            'JOIN' => true,\n            'KEY' => true,\n            'LEFT' => true,\n            'LIKE' => true,\n            'LIMIT' => true,\n            'MATCH' => true,\n            'NATURAL' => true,\n            'NO' => true,\n            'NOT' => true,\n            'NOTNULL' => true,\n            'NULL' => true,\n            'OF' => true,\n            'OFFSET' => true,\n            'ON' => true,\n            'OR' => true,\n            'ORDER' => true,\n            'OUTER' => true,\n            'PLAN' => true,\n            'PRAGMA' => true,\n            'PRIMARY' => true,\n            'QUERY' => true,\n            'RAISE' => true,\n            'RECURSIVE' => true,\n            'REFERENCES' => true,\n            'REGEXP' => true,\n            'REINDEX' => true,\n            'RELEASE' => true,\n            'RENAME' => true,\n            'REPLACE' => true,\n            'RESTRICT' => true,\n            'RIGHT' => true,\n            'ROLLBACK' => true,\n            'ROW' => true,\n            'SAVEPOINT' => true,\n            'SELECT' => true,\n            'SET' => true,\n            'TABLE' => true,\n            'TEMP' => true,\n            'TEMPORARY' => true,\n            'THEN' => true,\n            'TO' => true,\n            'TRANSACTION' => true,\n            'TRIGGER' => true,\n            'UNION' => true,\n            'UNIQUE' => true,\n            'UPDATE' => true,\n            'USING' => true,\n            'VACUUM' => true,\n            'VALUES' => true,\n            'VIEW' => true,\n            'VIRTUAL' => true,\n            'WHEN' => true,\n            'WHERE' => true,\n            'WITH' => true,\n            'WITHOUT' => true,\n        ];\n\n        $string = mb_strtoupper($string, 'UTF-8');\n        if (!isset($keywords[$string])) {\n            return false;\n        }\n\n        $content = $string;\n        return true;\n    }\n}\n"
  },
  {
    "path": "framework/db/sqlite/conditions/InConditionBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\sqlite\\conditions;\n\nuse yii\\base\\NotSupportedException;\nuse yii\\db\\Expression;\n\n/**\n * {@inheritdoc}\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n */\nclass InConditionBuilder extends \\yii\\db\\conditions\\InConditionBuilder\n{\n    /**\n     * {@inheritdoc}\n     * @throws NotSupportedException if `$columns` is an array\n     */\n    protected function buildSubqueryInCondition($operator, $columns, $values, &$params)\n    {\n        if (is_array($columns)) {\n            throw new NotSupportedException(__METHOD__ . ' is not supported by SQLite.');\n        }\n\n        return parent::buildSubqueryInCondition($operator, $columns, $values, $params);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function buildCompositeInCondition($operator, $columns, $values, &$params)\n    {\n        $quotedColumns = [];\n        foreach ($columns as $i => $column) {\n            if ($column instanceof Expression) {\n                $column = $column->expression;\n            }\n            $quotedColumns[$i] = strpos($column, '(') === false ? $this->queryBuilder->db->quoteColumnName($column) : $column;\n        }\n        $vss = [];\n        foreach ($values as $value) {\n            $vs = [];\n            foreach ($columns as $i => $column) {\n                if ($column instanceof Expression) {\n                    $column = $column->expression;\n                }\n                if (isset($value[$column])) {\n                    $phName = $this->queryBuilder->bindParam($value[$column], $params);\n                    $vs[] = $quotedColumns[$i] . ($operator === 'IN' ? ' = ' : ' != ') . $phName;\n                } else {\n                    $vs[] = $quotedColumns[$i] . ($operator === 'IN' ? ' IS' : ' IS NOT') . ' NULL';\n                }\n            }\n            $vss[] = '(' . implode($operator === 'IN' ? ' AND ' : ' OR ', $vs) . ')';\n        }\n\n        return '(' . implode($operator === 'IN' ? ' OR ' : ' AND ', $vss) . ')';\n    }\n}\n"
  },
  {
    "path": "framework/db/sqlite/conditions/LikeConditionBuilder.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\db\\sqlite\\conditions;\n\n/**\n * {@inheritdoc}\n */\nclass LikeConditionBuilder extends \\yii\\db\\conditions\\LikeConditionBuilder\n{\n    /**\n     * {@inheritdoc}\n     */\n    protected $escapeCharacter = '\\\\';\n}\n"
  },
  {
    "path": "framework/di/Container.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\di;\n\nuse ReflectionClass;\nuse ReflectionException;\nuse ReflectionNamedType;\nuse ReflectionParameter;\nuse Yii;\nuse yii\\base\\Component;\nuse yii\\base\\InvalidConfigException;\nuse yii\\helpers\\ArrayHelper;\n\n/**\n * Container implements a [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) container.\n *\n * A dependency injection (DI) container is an object that knows how to instantiate and configure objects and\n * all their dependent objects. For more information about DI, please refer to\n * [Martin Fowler's article](https://martinfowler.com/articles/injection.html).\n *\n * Container supports constructor injection as well as property injection.\n *\n * To use Container, you first need to set up the class dependencies by calling [[set()]].\n * You then call [[get()]] to create a new class object. The Container will automatically instantiate\n * dependent objects, inject them into the object being created, configure, and finally return the newly created object.\n *\n * By default, [[\\Yii::$container]] refers to a Container instance which is used by [[\\Yii::createObject()]]\n * to create new object instances. You may use this method to replace the `new` operator\n * when creating a new object, which gives you the benefit of automatic dependency resolution and default\n * property configuration.\n *\n * Below is an example of using Container:\n *\n * ```\n * namespace app\\models;\n *\n * use yii\\base\\BaseObject;\n * use yii\\db\\Connection;\n * use yii\\di\\Container;\n *\n * interface UserFinderInterface\n * {\n *     function findUser();\n * }\n *\n * class UserFinder extends BaseObject implements UserFinderInterface\n * {\n *     public $db;\n *\n *     public function __construct(Connection $db, $config = [])\n *     {\n *         $this->db = $db;\n *         parent::__construct($config);\n *     }\n *\n *     public function findUser()\n *     {\n *     }\n * }\n *\n * class UserLister extends BaseObject\n * {\n *     public $finder;\n *\n *     public function __construct(UserFinderInterface $finder, $config = [])\n *     {\n *         $this->finder = $finder;\n *         parent::__construct($config);\n *     }\n * }\n *\n * $container = new Container;\n * $container->set('yii\\db\\Connection', [\n *     'dsn' => '...',\n * ]);\n * $container->set('app\\models\\UserFinderInterface', [\n *     'class' => 'app\\models\\UserFinder',\n * ]);\n * $container->set('userLister', 'app\\models\\UserLister');\n *\n * $lister = $container->get('userLister');\n *\n * // which is equivalent to:\n *\n * $db = new \\yii\\db\\Connection(['dsn' => '...']);\n * $finder = new UserFinder($db);\n * $lister = new UserLister($finder);\n * ```\n *\n * For more details and usage information on Container, see the [guide article on di-containers](guide:concept-di-container).\n *\n * @property array $definitions The list of the object definitions or the loaded shared objects (type or ID =>\n * definition or instance).\n * @property-write bool $resolveArrays Whether to attempt to resolve elements in array dependencies.\n * @property-write array $singletons Array of singleton definitions. See [[setDefinitions()]] for allowed\n * formats of array.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Container extends Component\n{\n    /**\n     * @var array singleton objects indexed by their types\n     */\n    private $_singletons = [];\n    /**\n     * @var array object definitions indexed by their types\n     */\n    private $_definitions = [];\n    /**\n     * @var array constructor parameters indexed by object types\n     */\n    private $_params = [];\n    /**\n     * @var array cached ReflectionClass objects indexed by class/interface names\n     */\n    private $_reflections = [];\n    /**\n     * @var array<class-string, array<string, mixed>> cached dependencies indexed by class/interface names. Each class name\n     * is associated with a list of constructor parameter types or default values.\n     */\n    private $_dependencies = [];\n    /**\n     * @var bool whether to attempt to resolve elements in array dependencies\n     */\n    private $_resolveArrays = false;\n\n\n    /**\n     * Returns an instance of the requested class.\n     *\n     * You may provide constructor parameters (`$params`) and object configurations (`$config`)\n     * that will be used during the creation of the instance.\n     *\n     * If the class implements [[\\yii\\base\\Configurable]], the `$config` parameter will be passed as the last\n     * parameter to the class constructor; Otherwise, the configuration will be applied *after* the object is\n     * instantiated.\n     *\n     * Note that if the class is declared to be singleton by calling [[setSingleton()]],\n     * the same instance of the class will be returned each time this method is called.\n     * In this case, the constructor parameters and object configurations will be used\n     * only if the class is instantiated the first time.\n     *\n     * @template T of object\n     *\n     * @param string|class-string<T>|Instance $class the class Instance, name, or an alias name (e.g. `foo`) that was previously\n     * registered via [[set()]] or [[setSingleton()]].\n     * @param array $params a list of constructor parameter values. Use one of two definitions:\n     *  - Parameters as name-value pairs, for example: `['posts' => PostRepository::class]`.\n     *  - Parameters in the order they appear in the constructor declaration. If you want to skip some parameters,\n     *    you should index the remaining ones with the integers that represent their positions in the constructor\n     *    parameter list.\n     *    Dependencies indexed by name and by position in the same array are not allowed.\n     * @param array $config a list of name-value pairs that will be used to initialize the object properties.\n     * @return ($class is class-string<T> ? T : object) an instance of the requested class.\n     * @throws InvalidConfigException if the class cannot be recognized or correspond to an invalid definition\n     * @throws NotInstantiableException If resolved to an abstract class or an interface (since 2.0.9)\n     */\n    public function get($class, $params = [], $config = [])\n    {\n        if ($class instanceof Instance) {\n            $class = $class->id;\n        }\n        if (isset($this->_singletons[$class])) {\n            // singleton\n            return $this->_singletons[$class];\n        } elseif (!isset($this->_definitions[$class])) {\n            return $this->build($class, $params, $config);\n        }\n\n        $definition = $this->_definitions[$class];\n\n        if (is_callable($definition, true)) {\n            $params = $this->resolveDependencies($this->mergeParams($class, $params));\n            $object = call_user_func($definition, $this, $params, $config);\n        } elseif (is_array($definition)) {\n            $concrete = $definition['class'];\n            unset($definition['class']);\n\n            $config = array_merge($definition, $config);\n            $params = $this->mergeParams($class, $params);\n\n            if ($concrete === $class) {\n                $object = $this->build($class, $params, $config);\n            } else {\n                $object = $this->get($concrete, $params, $config);\n            }\n        } elseif (is_object($definition)) {\n            return $this->_singletons[$class] = $definition;\n        } else {\n            throw new InvalidConfigException('Unexpected object definition type: ' . gettype($definition));\n        }\n\n        if (array_key_exists($class, $this->_singletons)) {\n            // singleton\n            $this->_singletons[$class] = $object;\n        }\n\n        return $object;\n    }\n\n    /**\n     * Registers a class definition with this container.\n     *\n     * For example,\n     *\n     * ```\n     * // register a class name as is. This can be skipped.\n     * $container->set('yii\\db\\Connection');\n     *\n     * // register an interface\n     * // When a class depends on the interface, the corresponding class\n     * // will be instantiated as the dependent object\n     * $container->set('yii\\mail\\MailInterface', 'yii\\swiftmailer\\Mailer');\n     *\n     * // register an alias name. You can use $container->get('foo')\n     * // to create an instance of Connection\n     * $container->set('foo', 'yii\\db\\Connection');\n     *\n     * // register a class with configuration. The configuration\n     * // will be applied when the class is instantiated by get()\n     * $container->set('yii\\db\\Connection', [\n     *     'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n     *     'username' => 'root',\n     *     'password' => '',\n     *     'charset' => 'utf8',\n     * ]);\n     *\n     * // register an alias name with class configuration\n     * // In this case, a \"class\" element is required to specify the class\n     * $container->set('db', [\n     *     'class' => 'yii\\db\\Connection',\n     *     'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n     *     'username' => 'root',\n     *     'password' => '',\n     *     'charset' => 'utf8',\n     * ]);\n     *\n     * // register a PHP callable\n     * // The callable will be executed when $container->get('db') is called\n     * $container->set('db', function ($container, $params, $config) {\n     *     return new \\yii\\db\\Connection($config);\n     * });\n     * ```\n     *\n     * If a class definition with the same name already exists, it will be overwritten with the new one.\n     * You may use [[has()]] to check if a class definition already exists.\n     *\n     * @param string $class class name, interface name or alias name\n     * @param mixed $definition the definition associated with `$class`. It can be one of the following:\n     *\n     * - a PHP callable: The callable will be executed when [[get()]] is invoked. The signature of the callable\n     *   should be `function ($container, $params, $config)`, where `$params` stands for the list of constructor\n     *   parameters, `$config` the object configuration, and `$container` the container object. The return value\n     *   of the callable will be returned by [[get()]] as the object instance requested.\n     * - a configuration array: the array contains name-value pairs that will be used to initialize the property\n     *   values of the newly created object when [[get()]] is called. The `class` element stands for\n     *   the class of the object to be created. If `class` is not specified, `$class` will be used as the class name.\n     * - a string: a class name, an interface name or an alias name.\n     * @param array $params the list of constructor parameters. The parameters will be passed to the class\n     * constructor when [[get()]] is called.\n     * @return $this the container itself\n     */\n    public function set($class, $definition = [], array $params = [])\n    {\n        $this->_definitions[$class] = $this->normalizeDefinition($class, $definition);\n        $this->_params[$class] = $params;\n        unset($this->_singletons[$class]);\n        return $this;\n    }\n\n    /**\n     * Registers a class definition with this container and marks the class as a singleton class.\n     *\n     * This method is similar to [[set()]] except that classes registered via this method will only have one\n     * instance. Each time [[get()]] is called, the same instance of the specified class will be returned.\n     *\n     * @param string $class class name, interface name or alias name\n     * @param mixed $definition the definition associated with `$class`. See [[set()]] for more details.\n     * @param array $params the list of constructor parameters. The parameters will be passed to the class\n     * constructor when [[get()]] is called.\n     * @return $this the container itself\n     * @see set()\n     */\n    public function setSingleton($class, $definition = [], array $params = [])\n    {\n        $this->_definitions[$class] = $this->normalizeDefinition($class, $definition);\n        $this->_params[$class] = $params;\n        $this->_singletons[$class] = null;\n        return $this;\n    }\n\n    /**\n     * Returns a value indicating whether the container has the definition of the specified name.\n     * @param string $class class name, interface name or alias name\n     * @return bool Whether the container has the definition of the specified name.\n     * @see set()\n     */\n    public function has($class)\n    {\n        return isset($this->_definitions[$class]);\n    }\n\n    /**\n     * Returns a value indicating whether the given name corresponds to a registered singleton.\n     * @param string $class class name, interface name or alias name\n     * @param bool $checkInstance whether to check if the singleton has been instantiated.\n     * @return bool whether the given name corresponds to a registered singleton. If `$checkInstance` is true,\n     * the method should return a value indicating whether the singleton has been instantiated.\n     */\n    public function hasSingleton($class, $checkInstance = false)\n    {\n        return $checkInstance ? isset($this->_singletons[$class]) : array_key_exists($class, $this->_singletons);\n    }\n\n    /**\n     * Removes the definition for the specified name.\n     * @param string $class class name, interface name or alias name\n     */\n    public function clear($class)\n    {\n        unset($this->_definitions[$class], $this->_singletons[$class]);\n    }\n\n    /**\n     * Normalizes the class definition.\n     * @param string $class class name\n     * @param string|array|callable $definition the class definition\n     * @return array the normalized class definition\n     * @throws InvalidConfigException if the definition is invalid.\n     */\n    protected function normalizeDefinition($class, $definition)\n    {\n        if (empty($definition)) {\n            return ['class' => $class];\n        } elseif (is_string($definition)) {\n            return ['class' => $definition];\n        } elseif ($definition instanceof Instance) {\n            return ['class' => $definition->id];\n        } elseif (is_callable($definition, true) || is_object($definition)) {\n            return $definition;\n        } elseif (is_array($definition)) {\n            if (!isset($definition['class']) && isset($definition['__class'])) {\n                $definition['class'] = $definition['__class'];\n                unset($definition['__class']);\n            }\n            if (!isset($definition['class'])) {\n                if (strpos($class, '\\\\') !== false) {\n                    $definition['class'] = $class;\n                } else {\n                    throw new InvalidConfigException('A class definition requires a \"class\" member.');\n                }\n            }\n\n            return $definition;\n        }\n\n        throw new InvalidConfigException(\"Unsupported definition type for \\\"$class\\\": \" . gettype($definition));\n    }\n\n    /**\n     * Returns the list of the object definitions or the loaded shared objects.\n     * @return array the list of the object definitions or the loaded shared objects (type or ID => definition or instance).\n     */\n    public function getDefinitions()\n    {\n        return $this->_definitions;\n    }\n\n    /**\n     * Creates an instance of the specified class.\n     * This method will resolve dependencies of the specified class, instantiate them, and inject\n     * them into the new instance of the specified class.\n     *\n     * @template T of object\n     *\n     * @param class-string<T> $class the class name\n     * @param array $params constructor parameters\n     * @param array $config configurations to be applied to the new instance\n     * @return T the newly created instance of the specified class\n     * @throws NotInstantiableException If resolved to an abstract class or an interface (since 2.0.9)\n     */\n    protected function build($class, $params, $config)\n    {\n        /** @var ReflectionClass<T> $reflection */\n        list($reflection, $dependencies) = $this->getDependencies($class);\n\n        $addDependencies = [];\n        if (isset($config['__construct()'])) {\n            $addDependencies = $config['__construct()'];\n            unset($config['__construct()']);\n        }\n        foreach ($params as $index => $param) {\n            $addDependencies[$index] = $param;\n        }\n\n        $this->validateDependencies($addDependencies);\n\n        if ($addDependencies && is_int(key($addDependencies))) {\n            $dependencies = array_values($dependencies);\n            $dependencies = $this->mergeDependencies($dependencies, $addDependencies);\n        } else {\n            $dependencies = $this->mergeDependencies($dependencies, $addDependencies);\n            $dependencies = array_values($dependencies);\n        }\n\n        $dependencies = $this->resolveDependencies($dependencies, $reflection);\n        if (!$reflection->isInstantiable()) {\n            throw new NotInstantiableException($reflection->name);\n        }\n        if (empty($config)) {\n            return $reflection->newInstanceArgs($dependencies);\n        }\n\n        $config = $this->resolveDependencies($config);\n\n        if (!empty($dependencies) && $reflection->implementsInterface('yii\\base\\Configurable')) {\n            // set $config as the last parameter (existing one will be overwritten)\n            $dependencies[count($dependencies) - 1] = $config;\n            return $reflection->newInstanceArgs($dependencies);\n        }\n\n        $object = $reflection->newInstanceArgs($dependencies);\n        foreach ($config as $name => $value) {\n            $object->$name = $value;\n        }\n\n        return $object;\n    }\n\n    /**\n     * @param array $a\n     * @param array $b\n     * @return array\n     */\n    private function mergeDependencies($a, $b)\n    {\n        foreach ($b as $index => $dependency) {\n            $a[$index] = $dependency;\n        }\n        return $a;\n    }\n\n    /**\n     * @param array $parameters\n     * @throws InvalidConfigException\n     */\n    private function validateDependencies($parameters)\n    {\n        $hasStringParameter = false;\n        $hasIntParameter = false;\n        foreach ($parameters as $index => $parameter) {\n            if (is_string($index)) {\n                $hasStringParameter = true;\n                if ($hasIntParameter) {\n                    break;\n                }\n            } else {\n                $hasIntParameter = true;\n                if ($hasStringParameter) {\n                    break;\n                }\n            }\n        }\n        if ($hasIntParameter && $hasStringParameter) {\n            throw new InvalidConfigException(\n                'Dependencies indexed by name and by position in the same array are not allowed.'\n            );\n        }\n    }\n\n    /**\n     * Merges the user-specified constructor parameters with the ones registered via [[set()]].\n     * @param string $class class name, interface name or alias name\n     * @param array $params the constructor parameters\n     * @return array the merged parameters\n     */\n    protected function mergeParams($class, $params)\n    {\n        if (empty($this->_params[$class])) {\n            return $params;\n        } elseif (empty($params)) {\n            return $this->_params[$class];\n        }\n\n        $ps = $this->_params[$class];\n        foreach ($params as $index => $value) {\n            $ps[$index] = $value;\n        }\n\n        return $ps;\n    }\n\n    /**\n     * Returns the dependencies of the specified class.\n     *\n     * @template T of object\n     *\n     * @param class-string<T> $class class name, interface name or alias name\n     * @return array{ReflectionClass<T>, array<string, mixed>} the dependencies of the specified class.\n     * @throws NotInstantiableException if a dependency cannot be resolved or if a dependency cannot be fulfilled.\n     */\n    protected function getDependencies($class)\n    {\n        if (isset($this->_reflections[$class])) {\n            return [$this->_reflections[$class], $this->_dependencies[$class]];\n        }\n\n        $dependencies = [];\n        try {\n            $reflection = new ReflectionClass($class);\n        } catch (\\ReflectionException $e) {\n            throw new NotInstantiableException(\n                $class,\n                'Failed to instantiate component or class \"' . $class . '\".',\n                0,\n                $e\n            );\n        }\n\n        $constructor = $reflection->getConstructor();\n        if ($constructor !== null) {\n            foreach ($constructor->getParameters() as $param) {\n                if (PHP_VERSION_ID >= 50600 && $param->isVariadic()) {\n                    break;\n                }\n\n                if (PHP_VERSION_ID >= 80000) {\n                    $c = $param->getType();\n                    $isClass = false;\n                    if ($c instanceof ReflectionNamedType) {\n                        $isClass = !$c->isBuiltin();\n                    }\n                } else {\n                    try {\n                        $c = $param->getClass();\n                    } catch (ReflectionException $e) {\n                        if (!$this->isNulledParam($param)) {\n                            $notInstantiableClass = null;\n                            if (PHP_VERSION_ID >= 70000) {\n                                $type = $param->getType();\n                                if ($type instanceof ReflectionNamedType) {\n                                    $notInstantiableClass = $type->getName();\n                                }\n                            }\n                            throw new NotInstantiableException(\n                                $notInstantiableClass,\n                                $notInstantiableClass === null ? 'Can not instantiate unknown class.' : null\n                            );\n                        } else {\n                            $c = null;\n                        }\n                    }\n                    $isClass = $c !== null;\n                }\n                $className = $isClass ? $c->getName() : null;\n\n                if ($className !== null) {\n                    $dependencies[$param->getName()] = Instance::of($className, $this->isNulledParam($param));\n                } else {\n                    $dependencies[$param->getName()] = $param->isDefaultValueAvailable()\n                        ? $param->getDefaultValue()\n                        : null;\n                }\n            }\n        }\n\n        $this->_reflections[$class] = $reflection;\n        $this->_dependencies[$class] = $dependencies;\n\n        return [$reflection, $dependencies];\n    }\n\n    /**\n     * @param ReflectionParameter $param\n     * @return bool\n     */\n    private function isNulledParam($param)\n    {\n        return $param->isOptional() || (PHP_VERSION_ID >= 70100 && $param->getType()->allowsNull());\n    }\n\n    /**\n     * Resolves dependencies by replacing them with the actual object instances.\n     * @param array $dependencies the dependencies\n     * @param ReflectionClass<object>|null $reflection the class reflection associated with the dependencies\n     * @return array the resolved dependencies\n     * @throws InvalidConfigException if a dependency cannot be resolved or if a dependency cannot be fulfilled.\n     */\n    protected function resolveDependencies($dependencies, $reflection = null)\n    {\n        foreach ($dependencies as $index => $dependency) {\n            if ($dependency instanceof Instance) {\n                if ($dependency->id !== null) {\n                    $dependencies[$index] = $dependency->get($this);\n                } elseif ($reflection !== null) {\n                    $name = $reflection->getConstructor()->getParameters()[$index]->getName();\n                    $class = $reflection->getName();\n                    throw new InvalidConfigException(\"Missing required parameter \\\"$name\\\" when instantiating \\\"$class\\\".\");\n                }\n            } elseif ($this->_resolveArrays && is_array($dependency)) {\n                $dependencies[$index] = $this->resolveDependencies($dependency, $reflection);\n            }\n        }\n\n        return $dependencies;\n    }\n\n    /**\n     * Invoke a callback with resolving dependencies in parameters.\n     *\n     * This method allows invoking a callback and let type hinted parameter names to be\n     * resolved as objects of the Container. It additionally allows calling function using named parameters.\n     *\n     * For example, the following callback may be invoked using the Container to resolve the formatter dependency:\n     *\n     * ```\n     * $formatString = function($string, \\yii\\i18n\\Formatter $formatter) {\n     *    // ...\n     * }\n     * Yii::$container->invoke($formatString, ['string' => 'Hello World!']);\n     * ```\n     *\n     * This will pass the string `'Hello World!'` as the first param, and a formatter instance created\n     * by the DI container as the second param to the callable.\n     *\n     * @param callable $callback callable to be invoked.\n     * @param array $params The array of parameters for the function.\n     * This can be either a list of parameters, or an associative array representing named function parameters.\n     * @return mixed the callback return value.\n     * @throws InvalidConfigException if a dependency cannot be resolved or if a dependency cannot be fulfilled.\n     * @throws NotInstantiableException If resolved to an abstract class or an interface (since 2.0.9)\n     * @since 2.0.7\n     */\n    public function invoke(callable $callback, $params = [])\n    {\n        return call_user_func_array($callback, $this->resolveCallableDependencies($callback, $params));\n    }\n\n    /**\n     * Resolve dependencies for a function.\n     *\n     * This method can be used to implement similar functionality as provided by [[invoke()]] in other\n     * components.\n     *\n     * @param callable $callback callable to be invoked.\n     * @param array $params The array of parameters for the function, can be either numeric or associative.\n     * @return array The resolved dependencies.\n     * @throws InvalidConfigException if a dependency cannot be resolved or if a dependency cannot be fulfilled.\n     * @throws NotInstantiableException If resolved to an abstract class or an interface (since 2.0.9)\n     * @since 2.0.7\n     */\n    public function resolveCallableDependencies(callable $callback, $params = [])\n    {\n        if (is_array($callback)) {\n            $reflection = new \\ReflectionMethod($callback[0], $callback[1]);\n        } elseif (is_object($callback) && !$callback instanceof \\Closure) {\n            $reflection = new \\ReflectionMethod($callback, '__invoke');\n        } else {\n            $reflection = new \\ReflectionFunction($callback);\n        }\n\n        $args = [];\n\n        $associative = ArrayHelper::isAssociative($params);\n\n        foreach ($reflection->getParameters() as $param) {\n            $name = $param->getName();\n\n            if (PHP_VERSION_ID >= 80000) {\n                $class = $param->getType();\n                if ($class instanceof \\ReflectionUnionType || (PHP_VERSION_ID >= 80100 && $class instanceof \\ReflectionIntersectionType)) {\n                    $isClass = false;\n                    /** @var ReflectionNamedType $type */\n                    foreach ($class->getTypes() as $type) {\n                        if (!$type->isBuiltin()) {\n                            $class = $type;\n                            $isClass = true;\n                            break;\n                        }\n                    }\n                } else {\n                    /** @var ReflectionNamedType|null $class */\n                    $isClass = $class !== null && !$class->isBuiltin();\n                }\n            } else {\n                $class = $param->getClass();\n                $isClass = $class !== null;\n            }\n\n            if ($isClass) {\n                $className = $class->getName();\n                if (PHP_VERSION_ID >= 50600 && $param->isVariadic()) {\n                    $args = array_merge($args, array_values($params));\n                    break;\n                }\n\n                if ($associative && isset($params[$name]) && $params[$name] instanceof $className) {\n                    $args[] = $params[$name];\n                    unset($params[$name]);\n                } elseif (!$associative && isset($params[0]) && $params[0] instanceof $className) {\n                    $args[] = array_shift($params);\n                } elseif (isset(Yii::$app) && Yii::$app->has($name) && ($obj = Yii::$app->get($name)) instanceof $className) {\n                    $args[] = $obj;\n                } else {\n                    // If the argument is optional we catch not instantiable exceptions\n                    try {\n                        $args[] = $this->get($className);\n                    } catch (NotInstantiableException $e) {\n                        if ($param->isDefaultValueAvailable()) {\n                            $args[] = $param->getDefaultValue();\n                        } else {\n                            throw $e;\n                        }\n                    }\n                }\n            } elseif ($associative && isset($params[$name])) {\n                $args[] = $params[$name];\n                unset($params[$name]);\n            } elseif (!$associative && count($params)) {\n                $args[] = array_shift($params);\n            } elseif ($param->isDefaultValueAvailable()) {\n                $args[] = $param->getDefaultValue();\n            } elseif (!$param->isOptional()) {\n                $funcName = $reflection->getName();\n                throw new InvalidConfigException(\"Missing required parameter \\\"$name\\\" when calling \\\"$funcName\\\".\");\n            }\n        }\n\n        foreach ($params as $value) {\n            $args[] = $value;\n        }\n\n        return $args;\n    }\n\n    /**\n     * Registers class definitions within this container.\n     *\n     * @param array $definitions array of definitions. There are two allowed formats of array.\n     * The first format:\n     *  - key: class name, interface name or alias name. The key will be passed to the [[set()]] method\n     *    as a first argument `$class`.\n     *  - value: the definition associated with `$class`. Possible values are described in\n     *    [[set()]] documentation for the `$definition` parameter. Will be passed to the [[set()]] method\n     *    as the second argument `$definition`.\n     *\n     * Example:\n     * ```\n     * $container->setDefinitions([\n     *     'yii\\web\\Request' => 'app\\components\\Request',\n     *     'yii\\web\\Response' => [\n     *         'class' => 'app\\components\\Response',\n     *         'format' => 'json'\n     *     ],\n     *     'foo\\Bar' => function () {\n     *         $qux = new Qux;\n     *         $foo = new Foo($qux);\n     *         return new Bar($foo);\n     *     }\n     * ]);\n     * ```\n     *\n     * The second format:\n     *  - key: class name, interface name or alias name. The key will be passed to the [[set()]] method\n     *    as a first argument `$class`.\n     *  - value: array of two elements. The first element will be passed the [[set()]] method as the\n     *    second argument `$definition`, the second one — as `$params`.\n     *\n     * Example:\n     * ```\n     * $container->setDefinitions([\n     *     'foo\\Bar' => [\n     *          ['class' => 'app\\Bar'],\n     *          [Instance::of('baz')]\n     *      ]\n     * ]);\n     * ```\n     *\n     * @see set() to know more about possible values of definitions\n     * @since 2.0.11\n     */\n    public function setDefinitions(array $definitions)\n    {\n        foreach ($definitions as $class => $definition) {\n            if (is_array($definition) && count($definition) === 2 && array_values($definition) === $definition && is_array($definition[1])) {\n                $this->set($class, $definition[0], $definition[1]);\n                continue;\n            }\n\n            $this->set($class, $definition);\n        }\n    }\n\n    /**\n     * Registers class definitions as singletons within this container by calling [[setSingleton()]].\n     *\n     * @param array $singletons array of singleton definitions. See [[setDefinitions()]]\n     * for allowed formats of array.\n     *\n     * @see setDefinitions() for allowed formats of $singletons parameter\n     * @see setSingleton() to know more about possible values of definitions\n     * @since 2.0.11\n     */\n    public function setSingletons(array $singletons)\n    {\n        foreach ($singletons as $class => $definition) {\n            if (is_array($definition) && count($definition) === 2 && array_values($definition) === $definition) {\n                $this->setSingleton($class, $definition[0], $definition[1]);\n                continue;\n            }\n\n            $this->setSingleton($class, $definition);\n        }\n    }\n\n    /**\n     * @param bool $value whether to attempt to resolve elements in array dependencies\n     * @since 2.0.37\n     */\n    public function setResolveArrays($value)\n    {\n        $this->_resolveArrays = (bool) $value;\n    }\n}\n"
  },
  {
    "path": "framework/di/Instance.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\di;\n\nuse Yii;\nuse yii\\base\\InvalidConfigException;\n\n/**\n * Instance represents a reference to a named object in a dependency injection (DI) container or a service locator.\n *\n * You may use [[get()]] to obtain the actual object referenced by [[id]].\n *\n * Instance is mainly used in two places:\n *\n * - When configuring a dependency injection container, you use Instance to reference a class name, interface name\n *   or alias name. The reference can later be resolved into the actual object by the container.\n * - In classes which use service locator to obtain dependent objects.\n *\n * The following example shows how to configure a DI container with Instance:\n *\n * ```\n * $container = new \\yii\\di\\Container;\n * $container->set('cache', [\n *     'class' => 'yii\\caching\\DbCache',\n *     'db' => Instance::of('db')\n * ]);\n * $container->set('db', [\n *     'class' => 'yii\\db\\Connection',\n *     'dsn' => 'sqlite:path/to/file.db',\n * ]);\n * ```\n *\n * And the following example shows how a class retrieves a component from a service locator:\n *\n * ```\n * class DbCache extends Cache\n * {\n *     public $db = 'db';\n *\n *     public function init()\n *     {\n *         parent::init();\n *         $this->db = Instance::ensure($this->db, 'yii\\db\\Connection');\n *     }\n * }\n * ```\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Instance\n{\n    /**\n     * @var string the component ID, class name, interface name or alias name\n     */\n    public $id;\n    /**\n     * @var bool if null should be returned instead of throwing an exception\n     */\n    public $optional;\n\n\n    /**\n     * Constructor.\n     * @param string $id the component ID\n     * @param bool $optional if null should be returned instead of throwing an exception\n     */\n    protected function __construct($id, $optional = false)\n    {\n        $this->id = $id;\n        $this->optional = $optional;\n    }\n\n    /**\n     * Creates a new Instance object.\n     * @param string $id the component ID\n     * @param bool $optional if null should be returned instead of throwing an exception\n     * @return Instance the new Instance object.\n     */\n    public static function of($id, $optional = false)\n    {\n        return new static($id, $optional);\n    }\n\n    /**\n     * Resolves the specified reference into the actual object and makes sure it is of the specified type.\n     *\n     * The reference may be specified as a string or an Instance object. If the former,\n     * it will be treated as a component ID, a class/interface name or an alias, depending on the container type.\n     *\n     * If you do not specify a container, the method will first try `Yii::$app` followed by `Yii::$container`.\n     *\n     * For example,\n     *\n     * ```\n     * use yii\\db\\Connection;\n     *\n     * // returns Yii::$app->db\n     * $db = Instance::ensure('db', Connection::class);\n     * // returns an instance of Connection using the given configuration\n     * $db = Instance::ensure(['dsn' => 'sqlite:path/to/my.db'], Connection::class);\n     * ```\n     *\n     * @template T of object\n     *\n     * @param object|string|array|static $reference an object or a reference to the desired object.\n     * You may specify a reference in terms of a component ID or an Instance object.\n     * Starting from version 2.0.2, you may also pass in a configuration array for creating the object.\n     * If the \"class\" value is not specified in the configuration array, it will use the value of `$type`.\n     * @param class-string<T>|null $type the class/interface name to be checked. If null, type check will not be performed.\n     * @param ServiceLocator|Container|null $container the container. This will be passed to [[get()]].\n     * @return ($type is null ? object : T) the object referenced by the Instance, or `$reference` itself if it is an object.\n     * @throws InvalidConfigException if the reference is invalid\n     */\n    public static function ensure($reference, $type = null, $container = null)\n    {\n        if (is_array($reference)) {\n            if (!$container instanceof Container) {\n                $container = Yii::$container;\n            }\n            if (isset($reference['__class'])) {\n                $class = $reference['__class'];\n                unset($reference['__class'], $reference['class']);\n            } elseif (isset($reference['class'])) {\n                $class = $reference['class'];\n                unset($reference['class']);\n            } else {\n                $class = $type;\n            }\n            $component = $container->get($class, [], $reference);\n            if ($type === null || $component instanceof $type) {\n                return $component;\n            }\n\n            throw new InvalidConfigException('Invalid data type: ' . $class . '. ' . $type . ' is expected.');\n        } elseif (empty($reference)) {\n            throw new InvalidConfigException('The required component is not specified.');\n        }\n\n        if (is_string($reference)) {\n            $reference = new static($reference);\n        } elseif ($type === null || $reference instanceof $type) {\n            return $reference;\n        }\n\n        if ($reference instanceof self) {\n            try {\n                $component = $reference->get($container);\n            } catch (\\ReflectionException $e) {\n                throw new InvalidConfigException('Failed to instantiate component or class \"' . $reference->id . '\".', 0, $e);\n            }\n            if ($type === null || $component instanceof $type) {\n                return $component;\n            }\n\n            throw new InvalidConfigException('\"' . $reference->id . '\" refers to a ' . get_class($component) . \" component. $type is expected.\");\n        }\n\n        $valueType = is_object($reference) ? get_class($reference) : gettype($reference);\n        throw new InvalidConfigException(\"Invalid data type: $valueType. $type is expected.\");\n    }\n\n    /**\n     * Returns the actual object referenced by this Instance object.\n     * @param ServiceLocator|Container|null $container the container used to locate the referenced object.\n     * If null, the method will first try `Yii::$app` then `Yii::$container`.\n     * @return object|null the actual object referenced by this Instance object.\n     */\n    public function get($container = null)\n    {\n        try {\n            if ($container) {\n                return $container->get($this->id);\n            }\n            if (Yii::$app && Yii::$app->has($this->id)) {\n                return Yii::$app->get($this->id);\n            }\n\n            return Yii::$container->get($this->id);\n        } catch (\\Exception $e) {\n            if ($this->optional) {\n                return null;\n            }\n            throw $e;\n        } catch (\\Throwable $e) {\n            if ($this->optional) {\n                return null;\n            }\n            throw $e;\n        }\n    }\n\n    /**\n     * Restores class state after using `var_export()`.\n     *\n     * @param array $state\n     * @return Instance\n     * @throws InvalidConfigException when $state property does not contain `id` parameter\n     * @see https://www.php.net/manual/en/function.var-export.php\n     * @since 2.0.12\n     */\n    public static function __set_state($state)\n    {\n        if (!isset($state['id'])) {\n            throw new InvalidConfigException('Failed to instantiate class \"Instance\". Required parameter \"id\" is missing');\n        }\n\n        return new self($state['id']);\n    }\n}\n"
  },
  {
    "path": "framework/di/NotInstantiableException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\di;\n\nuse yii\\base\\InvalidConfigException;\n\n/**\n * NotInstantiableException represents an exception caused by incorrect dependency injection container\n * configuration or usage.\n *\n * @author Sam Mousa <sam@mousa.nl>\n * @since 2.0.9\n */\nclass NotInstantiableException extends InvalidConfigException\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function __construct($class, $message = null, $code = 0, $previous = null)\n    {\n        if ($message === null) {\n            $message = \"Can not instantiate $class.\";\n        }\n        parent::__construct($message, $code, $previous);\n    }\n\n    /**\n     * @return string the user-friendly name of this exception\n     */\n    public function getName()\n    {\n        return 'Not instantiable';\n    }\n}\n"
  },
  {
    "path": "framework/di/ServiceLocator.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\di;\n\nuse Closure;\nuse Yii;\nuse yii\\base\\Component;\nuse yii\\base\\InvalidConfigException;\n\n/**\n * ServiceLocator implements a [service locator](https://en.wikipedia.org/wiki/Service_locator_pattern).\n *\n * To use ServiceLocator, you first need to register component IDs with the corresponding component\n * definitions with the locator by calling [[set()]] or [[setComponents()]].\n * You can then call [[get()]] to retrieve a component with the specified ID. The locator will automatically\n * instantiate and configure the component according to the definition.\n *\n * For example,\n *\n * ```\n * $locator = new \\yii\\di\\ServiceLocator;\n * $locator->setComponents([\n *     'db' => [\n *         'class' => 'yii\\db\\Connection',\n *         'dsn' => 'sqlite:path/to/file.db',\n *     ],\n *     'cache' => [\n *         'class' => 'yii\\caching\\DbCache',\n *         'db' => 'db',\n *     ],\n * ]);\n *\n * $db = $locator->get('db');  // or $locator->db\n * $cache = $locator->get('cache');  // or $locator->cache\n * ```\n *\n * Because [[\\yii\\base\\Module]] extends from ServiceLocator, modules and the application are all service locators.\n * Modules add [tree traversal](guide:concept-service-locator#tree-traversal) for service resolution.\n *\n * For more details and usage information on ServiceLocator, see the [guide article on service locators](guide:concept-service-locator).\n *\n * @property array $components The list of the component definitions or the loaded component instances (ID =>\n * definition or instance).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass ServiceLocator extends Component\n{\n    /**\n     * @var array shared component instances indexed by their IDs\n     */\n    private $_components = [];\n    /**\n     * @var array component definitions indexed by their IDs\n     */\n    private $_definitions = [];\n\n\n    /**\n     * Getter magic method.\n     * This method is overridden to support accessing components like reading properties.\n     * @param string $name component or property name\n     * @return mixed the named property value\n     */\n    public function __get($name)\n    {\n        if ($this->has($name)) {\n            return $this->get($name);\n        }\n\n        return parent::__get($name);\n    }\n\n    /**\n     * Checks if a property value is null.\n     * This method overrides the parent implementation by checking if the named component is loaded.\n     * @param string $name the property name or the event name\n     * @return bool whether the property value is null\n     */\n    public function __isset($name)\n    {\n        if ($this->has($name)) {\n            return true;\n        }\n\n        return parent::__isset($name);\n    }\n\n    /**\n     * Returns a value indicating whether the locator has the specified component definition or has instantiated the component.\n     * This method may return different results depending on the value of `$checkInstance`.\n     *\n     * - If `$checkInstance` is false (default), the method will return a value indicating whether the locator has the specified\n     *   component definition.\n     * - If `$checkInstance` is true, the method will return a value indicating whether the locator has\n     *   instantiated the specified component.\n     *\n     * @param string $id component ID (e.g. `db`).\n     * @param bool $checkInstance whether the method should check if the component is shared and instantiated.\n     * @return bool whether the locator has the specified component definition or has instantiated the component.\n     * @see set()\n     */\n    public function has($id, $checkInstance = false)\n    {\n        return $checkInstance ? isset($this->_components[$id]) : isset($this->_definitions[$id]);\n    }\n\n    /**\n     * Returns the component instance with the specified ID.\n     *\n     * @param string $id component ID (e.g. `db`).\n     * @param bool $throwException whether to throw an exception if `$id` is not registered with the locator before.\n     * @return ($throwException is true ? object : object|null) the component of the specified ID. If `$throwException` is false and `$id`\n     * is not registered before, null will be returned.\n     * @throws InvalidConfigException if `$id` refers to a nonexistent component ID\n     * @see has()\n     * @see set()\n     */\n    public function get($id, $throwException = true)\n    {\n        if (isset($this->_components[$id])) {\n            return $this->_components[$id];\n        }\n\n        if (isset($this->_definitions[$id])) {\n            $definition = $this->_definitions[$id];\n            if (is_object($definition) && !$definition instanceof Closure) {\n                return $this->_components[$id] = $definition;\n            }\n\n            return $this->_components[$id] = Yii::createObject($definition);\n        } elseif ($throwException) {\n            throw new InvalidConfigException(\"Unknown component ID: $id\");\n        }\n\n        return null;\n    }\n\n    /**\n     * Registers a component definition with this locator.\n     *\n     * For example,\n     *\n     * ```\n     * // a class name\n     * $locator->set('cache', 'yii\\caching\\FileCache');\n     *\n     * // a configuration array\n     * $locator->set('db', [\n     *     'class' => 'yii\\db\\Connection',\n     *     'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n     *     'username' => 'root',\n     *     'password' => '',\n     *     'charset' => 'utf8',\n     * ]);\n     *\n     * // an anonymous function\n     * $locator->set('cache', function ($params) {\n     *     return new \\yii\\caching\\FileCache;\n     * });\n     *\n     * // an instance\n     * $locator->set('cache', new \\yii\\caching\\FileCache);\n     * ```\n     *\n     * If a component definition with the same ID already exists, it will be overwritten.\n     *\n     * @param string $id component ID (e.g. `db`).\n     * @param mixed $definition the component definition to be registered with this locator.\n     * It can be one of the following:\n     *\n     * - a class name\n     * - a configuration array: the array contains name-value pairs that will be used to\n     *   initialize the property values of the newly created object when [[get()]] is called.\n     *   The `class` element is required and stands for the the class of the object to be created.\n     * - a PHP callable: either an anonymous function or an array representing a class method (e.g. `['Foo', 'bar']`).\n     *   The callable will be called by [[get()]] to return an object associated with the specified component ID.\n     * - an object: When [[get()]] is called, this object will be returned.\n     *\n     * @throws InvalidConfigException if the definition is an invalid configuration array\n     */\n    public function set($id, $definition)\n    {\n        unset($this->_components[$id]);\n\n        if ($definition === null) {\n            unset($this->_definitions[$id]);\n            return;\n        }\n\n        if (is_object($definition) || is_callable($definition, true)) {\n            // an object, a class name, or a PHP callable\n            $this->_definitions[$id] = $definition;\n        } elseif (is_array($definition)) {\n            // a configuration array\n            if (isset($definition['__class'])) {\n                $this->_definitions[$id] = $definition;\n                $this->_definitions[$id]['class'] = $definition['__class'];\n                unset($this->_definitions[$id]['__class']);\n            } elseif (isset($definition['class'])) {\n                $this->_definitions[$id] = $definition;\n            } else {\n                throw new InvalidConfigException(\"The configuration for the \\\"$id\\\" component must contain a \\\"class\\\" element.\");\n            }\n        } else {\n            throw new InvalidConfigException(\"Unexpected configuration type for the \\\"$id\\\" component: \" . gettype($definition));\n        }\n    }\n\n    /**\n     * Removes the component from the locator.\n     * @param string $id the component ID\n     */\n    public function clear($id)\n    {\n        unset($this->_definitions[$id], $this->_components[$id]);\n    }\n\n    /**\n     * Returns the list of the component definitions or the loaded component instances.\n     * @param bool $returnDefinitions whether to return component definitions instead of the loaded component instances.\n     * @return array the list of the component definitions or the loaded component instances (ID => definition or instance).\n     */\n    public function getComponents($returnDefinitions = true)\n    {\n        return $returnDefinitions ? $this->_definitions : $this->_components;\n    }\n\n    /**\n     * Registers a set of component definitions in this locator.\n     *\n     * This is the bulk version of [[set()]]. The parameter should be an array\n     * whose keys are component IDs and values the corresponding component definitions.\n     *\n     * For more details on how to specify component IDs and definitions, please refer to [[set()]].\n     *\n     * If a component definition with the same ID already exists, it will be overwritten.\n     *\n     * The following is an example for registering two component definitions:\n     *\n     * ```\n     * [\n     *     'db' => [\n     *         'class' => 'yii\\db\\Connection',\n     *         'dsn' => 'sqlite:path/to/file.db',\n     *     ],\n     *     'cache' => [\n     *         'class' => 'yii\\caching\\DbCache',\n     *         'db' => 'db',\n     *     ],\n     * ]\n     * ```\n     *\n     * @param array $components component definitions or instances\n     */\n    public function setComponents($components)\n    {\n        foreach ($components as $id => $component) {\n            $this->set($id, $component);\n        }\n    }\n}\n"
  },
  {
    "path": "framework/filters/AccessControl.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\filters;\n\nuse Yii;\nuse yii\\base\\ActionFilter;\nuse yii\\base\\Component;\nuse yii\\di\\Instance;\nuse yii\\web\\ForbiddenHttpException;\nuse yii\\web\\User;\n\n/**\n * AccessControl provides simple access control based on a set of rules.\n *\n * AccessControl is an action filter. It will check its [[rules]] to find\n * the first rule that matches the current context variables (such as user IP address, user role).\n * The matching rule will dictate whether to allow or deny the access to the requested controller\n * action. If no rule matches, the access will be denied.\n *\n * To use AccessControl, declare it in the `behaviors()` method of your controller class.\n * For example, the following declarations will allow authenticated users to access the \"create\"\n * and \"update\" actions and deny all other users from accessing these two actions.\n *\n * ```\n * public function behaviors()\n * {\n *     return [\n *         'access' => [\n *             'class' => \\yii\\filters\\AccessControl::class,\n *             'only' => ['create', 'update'],\n *             'rules' => [\n *                 // deny all POST requests\n *                 [\n *                     'allow' => false,\n *                     'verbs' => ['POST']\n *                 ],\n *                 // allow authenticated users\n *                 [\n *                     'allow' => true,\n *                     'roles' => ['@'],\n *                 ],\n *                 // everything else is denied\n *             ],\n *         ],\n *     ];\n * }\n * ```\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Component = Component\n * @extends ActionFilter<T>\n */\nclass AccessControl extends ActionFilter\n{\n    /**\n     * @var User|array|string|false the user object representing the authentication status or the ID of the user application component.\n     * Starting from version 2.0.2, this can also be a configuration array for creating the object.\n     * Starting from version 2.0.12, you can set it to `false` to explicitly switch this component support off for the filter.\n     */\n    public $user = 'user';\n    /**\n     * @var callable|null a callback that will be called if the access should be denied\n     * to the current user. This is the case when either no rule matches, or a rule with\n     * [[AccessRule::$allow|$allow]] set to `false` matches.\n     * If not set, [[denyAccess()]] will be called.\n     *\n     * The signature of the callback should be as follows:\n     *\n     * ```\n     * function ($rule, $action)\n     * ```\n     *\n     * where `$rule` is the rule that denies the user, and `$action` is the current [[Action|action]] object.\n     * `$rule` can be `null` if access is denied because none of the rules matched.\n     */\n    public $denyCallback;\n    /**\n     * @var array the default configuration of access rules. Individual rule configurations\n     * specified via [[rules]] will take precedence when the same property of the rule is configured.\n     */\n    public $ruleConfig = ['class' => 'yii\\filters\\AccessRule'];\n    /**\n     * @var array a list of access rule objects or configuration arrays for creating the rule objects.\n     * If a rule is specified via a configuration array, it will be merged with [[ruleConfig]] first\n     * before it is used for creating the rule object.\n     * @see ruleConfig\n     */\n    public $rules = [];\n\n\n    /**\n     * Initializes the [[rules]] array by instantiating rule objects from configurations.\n     */\n    public function init()\n    {\n        parent::init();\n        if ($this->user !== false) {\n            $this->user = Instance::ensure($this->user, User::className());\n        }\n        foreach ($this->rules as $i => $rule) {\n            if (is_array($rule)) {\n                $this->rules[$i] = Yii::createObject(array_merge($this->ruleConfig, $rule));\n            }\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function beforeAction($action)\n    {\n        $user = $this->user;\n        $request = Yii::$app->getRequest();\n        /** @var AccessRule $rule */\n        foreach ($this->rules as $rule) {\n            if ($allow = $rule->allows($action, $user, $request)) {\n                return true;\n            } elseif ($allow === false) {\n                if (isset($rule->denyCallback)) {\n                    call_user_func($rule->denyCallback, $rule, $action);\n                } elseif ($this->denyCallback !== null) {\n                    call_user_func($this->denyCallback, $rule, $action);\n                } else {\n                    $this->denyAccess($user);\n                }\n\n                return false;\n            }\n        }\n        if ($this->denyCallback !== null) {\n            call_user_func($this->denyCallback, null, $action);\n        } else {\n            $this->denyAccess($user);\n        }\n\n        return false;\n    }\n\n    /**\n     * Denies the access of the user.\n     * The default implementation will redirect the user to the login page if he is a guest;\n     * if the user is already logged, a 403 HTTP exception will be thrown.\n     * @param User|false $user the current user or boolean `false` in case of detached User component\n     * @throws ForbiddenHttpException if the user is already logged in or in case of detached User component.\n     */\n    protected function denyAccess($user)\n    {\n        if ($user !== false && $user->getIsGuest()) {\n            $user->loginRequired();\n        } else {\n            throw new ForbiddenHttpException(Yii::t('yii', 'You are not allowed to perform this action.'));\n        }\n    }\n}\n"
  },
  {
    "path": "framework/filters/AccessRule.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\filters;\n\nuse Closure;\nuse yii\\base\\Action;\nuse yii\\base\\Component;\nuse yii\\base\\Controller;\nuse yii\\base\\InvalidConfigException;\nuse yii\\base\\Module;\nuse yii\\helpers\\IpHelper;\nuse yii\\helpers\\StringHelper;\nuse yii\\web\\Request;\nuse yii\\web\\User;\n\n/**\n * This class represents an access rule defined by the [[AccessControl]] action filter.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass AccessRule extends Component\n{\n    /**\n     * @var bool whether this is an 'allow' rule or 'deny' rule.\n     */\n    public $allow = false;\n    /**\n     * @var array|null list of action IDs that this rule applies to. The comparison is case-sensitive.\n     * If not set or empty, it means this rule applies to all actions.\n     */\n    public $actions;\n    /**\n     * @var array|null list of the controller IDs that this rule applies to.\n     *\n     * The comparison uses [[\\yii\\base\\Controller::uniqueId]], so each controller ID is prefixed\n     * with the module ID (if any). For a `product` controller in the application, you would specify\n     * this property like `['product']` and if that controller is located in a `shop` module, this\n     * would be `['shop/product']`.\n     *\n     * The comparison is case-sensitive.\n     *\n     * If not set or empty, it means this rule applies to all controllers.\n     *\n     * Since version 2.0.12 controller IDs can be specified as wildcards, e.g. `module/*`.\n     */\n    public $controllers;\n    /**\n     * @var array|null list of roles that this rule applies to (requires properly configured User component).\n     * Two special roles are recognized, and they are checked via [[User::isGuest]]:\n     *\n     * - `?`: matches a guest user (not authenticated yet)\n     * - `@`: matches an authenticated user\n     *\n     * If you are using RBAC (Role-Based Access Control), you may also specify role names.\n     * In this case, [[User::can()]] will be called to check access.\n     *\n     * Note that it is preferred to check for permissions instead.\n     *\n     * If this property is not set or empty, it means this rule applies regardless of roles.\n     * @see permissions\n     * @see roleParams\n     */\n    public $roles;\n    /**\n     * @var array|null list of RBAC (Role-Based Access Control) permissions that this rules applies to.\n     * [[User::can()]] will be called to check access.\n     *\n     * If this property is not set or empty, it means this rule applies regardless of permissions.\n     * @since 2.0.12\n     * @see roles\n     * @see roleParams\n     */\n    public $permissions;\n    /**\n     * @var array|Closure parameters to pass to the [[User::can()]] function for evaluating\n     * user permissions in [[$roles]].\n     *\n     * If this is an array, it will be passed directly to [[User::can()]]. For example for passing an\n     * ID from the current request, you may use the following:\n     *\n     * ```\n     * ['postId' => Yii::$app->request->get('id')]\n     * ```\n     *\n     * You may also specify a closure that returns an array. This can be used to\n     * evaluate the array values only if they are needed, for example when a model needs to be\n     * loaded like in the following code:\n     *\n     * ```\n     * 'rules' => [\n     *     [\n     *         'allow' => true,\n     *         'actions' => ['update'],\n     *         'roles' => ['updatePost'],\n     *         'roleParams' => function($rule) {\n     *             return ['post' => Post::findOne(Yii::$app->request->get('id'))];\n     *         },\n     *     ],\n     * ],\n     * ```\n     *\n     * A reference to the [[AccessRule]] instance will be passed to the closure as the first parameter.\n     *\n     * @see roles\n     * @since 2.0.12\n     */\n    public $roleParams = [];\n    /**\n     * @var array|null list of user IP addresses that this rule applies to. An IP address\n     * can contain the wildcard `*` at the end so that it matches IP addresses with the same prefix.\n     * For example, '192.168.*' matches all IP addresses in the segment '192.168.'.\n     * It may also contain a pattern/mask like '172.16.0.0/12' which would match all IPs from the\n     * 20-bit private network block in RFC1918.\n     * If not set or empty, it means this rule applies to all IP addresses.\n     * @see Request::userIP\n     * @see IpHelper::inRange\n     */\n    public $ips;\n    /**\n     * @var array|null list of request methods (e.g. `GET`, `POST`) that this rule applies to.\n     * If not set or empty, it means this rule applies to all request methods.\n     * @see \\yii\\web\\Request::method\n     */\n    public $verbs;\n    /**\n     * @var callable a callback that will be called to determine if the rule should be applied.\n     * The signature of the callback should be as follows:\n     *\n     * ```\n     * function ($rule, $action)\n     * ```\n     *\n     * where `$rule` is this rule, and `$action` is the current [[Action|action]] object.\n     * The callback should return a boolean value indicating whether this rule should be applied.\n     */\n    public $matchCallback;\n    /**\n     * @var callable|null a callback that will be called if this rule determines the access to\n     * the current action should be denied. This is the case when this rule matches\n     * and [[$allow]] is set to `false`.\n     *\n     * If not set, the behavior will be determined by [[AccessControl]],\n     * either using [[AccessControl::denyAccess()]]\n     * or [[AccessControl::$denyCallback]], if configured.\n     *\n     * The signature of the callback should be as follows:\n     *\n     * ```\n     * function ($rule, $action)\n     * ```\n     *\n     * where `$rule` is this rule, and `$action` is the current [[Action|action]] object.\n     * @see AccessControl::$denyCallback\n     */\n    public $denyCallback;\n\n\n    /**\n     * Checks whether the Web user is allowed to perform the specified action.\n     * @param Action $action the action to be performed\n     * @param User|false $user the user object or `false` in case of detached User component\n     * @param Request $request\n     * @return bool|null `true` if the user is allowed, `false` if the user is denied, `null` if the rule does not apply to the user\n     */\n    public function allows($action, $user, $request)\n    {\n        if (\n            $this->matchAction($action)\n            && $this->matchRole($user)\n            && $this->matchIP($request->getUserIP())\n            && $this->matchVerb($request->getMethod())\n            && $this->matchController($action->controller)\n            && $this->matchCustom($action)\n        ) {\n            return $this->allow ? true : false;\n        }\n\n        return null;\n    }\n\n    /**\n     * @param Action $action the action\n     * @return bool whether the rule applies to the action\n     */\n    protected function matchAction($action)\n    {\n        return empty($this->actions) || in_array($action->id, $this->actions, true);\n    }\n\n    /**\n     * @param Controller $controller the controller\n     * @return bool whether the rule applies to the controller\n     */\n    protected function matchController($controller)\n    {\n        if (empty($this->controllers)) {\n            return true;\n        }\n\n        $id = $controller->getUniqueId();\n        foreach ($this->controllers as $pattern) {\n            if (StringHelper::matchWildcard($pattern, $id)) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * @param User $user the user object\n     * @return bool whether the rule applies to the role\n     * @throws InvalidConfigException if User component is detached\n     */\n    protected function matchRole($user)\n    {\n        $items = empty($this->roles) ? [] : $this->roles;\n\n        if (!empty($this->permissions)) {\n            $items = array_merge($items, $this->permissions);\n        }\n\n        if (empty($items)) {\n            return true;\n        }\n\n        if ($user === false) {\n            throw new InvalidConfigException('The user application component must be available to specify roles in AccessRule.');\n        }\n\n        foreach ($items as $item) {\n            if ($item === '?') {\n                if ($user->getIsGuest()) {\n                    return true;\n                }\n            } elseif ($item === '@') {\n                if (!$user->getIsGuest()) {\n                    return true;\n                }\n            } else {\n                if (!isset($roleParams)) {\n                    $roleParams = !is_array($this->roleParams) && is_callable($this->roleParams) ? call_user_func($this->roleParams, $this) : $this->roleParams;\n                }\n                if ($user->can($item, $roleParams)) {\n                    return true;\n                }\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * @param string|null $ip the IP address\n     * @return bool whether the rule applies to the IP address\n     */\n    protected function matchIP($ip)\n    {\n        if (empty($this->ips)) {\n            return true;\n        }\n        foreach ($this->ips as $rule) {\n            if (\n                $rule === '*'\n                || $rule === $ip\n                || (\n                    $ip !== null\n                    && ($pos = strpos($rule, '*')) !== false\n                    && strncmp($ip, $rule, $pos) === 0\n                )\n                || (\n                    strpos($rule, '/') !== false\n                    && IpHelper::inRange($ip, $rule) === true\n                )\n            ) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * @param string $verb the request method.\n     * @return bool whether the rule applies to the request\n     */\n    protected function matchVerb($verb)\n    {\n        return empty($this->verbs) || in_array(strtoupper($verb), array_map('strtoupper', $this->verbs), true);\n    }\n\n    /**\n     * @param Action $action the action to be performed\n     * @return bool whether the rule should be applied\n     */\n    protected function matchCustom($action)\n    {\n        return empty($this->matchCallback) || call_user_func($this->matchCallback, $this, $action);\n    }\n}\n"
  },
  {
    "path": "framework/filters/AjaxFilter.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\filters;\n\nuse Yii;\nuse yii\\base\\ActionFilter;\nuse yii\\base\\Component;\nuse yii\\web\\BadRequestHttpException;\nuse yii\\web\\Request;\n\n/**\n * AjaxFilter allow to limit access only for ajax requests.\n *\n * ```\n * public function behaviors()\n * {\n *     return [\n *         [\n *             'class' => 'yii\\filters\\AjaxFilter',\n *             'only' => ['index']\n *         ],\n *     ];\n * }\n * ```\n *\n * @author Dmitry Dorogin <dmirogin@ya.ru>\n * @since 2.0.13\n *\n * @template T of Component = Component\n * @extends ActionFilter<T>\n */\nclass AjaxFilter extends ActionFilter\n{\n    /**\n     * @var string the message to be displayed when request isn't ajax\n     */\n    public $errorMessage = 'Request must be XMLHttpRequest.';\n    /**\n     * @var Request|null the current request. If not set, the `request` application component will be used.\n     */\n    public $request;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        if ($this->request === null) {\n            $this->request = Yii::$app->getRequest();\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function beforeAction($action)\n    {\n        if ($this->request->getIsAjax()) {\n            return true;\n        }\n\n        throw new BadRequestHttpException($this->errorMessage);\n    }\n}\n"
  },
  {
    "path": "framework/filters/ContentNegotiator.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\filters;\n\nuse Yii;\nuse yii\\base\\ActionFilter;\nuse yii\\base\\BootstrapInterface;\nuse yii\\base\\Component;\nuse yii\\web\\BadRequestHttpException;\nuse yii\\web\\NotAcceptableHttpException;\nuse yii\\web\\Request;\nuse yii\\web\\Response;\n\n/**\n * ContentNegotiator supports response format negotiation and application language negotiation.\n *\n * When the [[formats|supported formats]] property is specified, ContentNegotiator will support response format\n * negotiation based on the value of the GET parameter [[formatParam]] and the `Accept` HTTP header.\n * If a match is found, the [[Response::format]] property will be set as the chosen format.\n * The [[Response::acceptMimeType]] as well as [[Response::acceptParams]] will also be updated accordingly.\n *\n * When the [[languages|supported languages]] is specified, ContentNegotiator will support application\n * language negotiation based on the value of the GET parameter [[languageParam]] and the `Accept-Language` HTTP header.\n * If a match is found, the [[\\yii\\base\\Application::language]] property will be set as the chosen language.\n *\n * You may use ContentNegotiator as a bootstrapping component as well as an action filter.\n *\n * The following code shows how you can use ContentNegotiator as a bootstrapping component. Note that in this case,\n * the content negotiation applies to the whole application.\n *\n * ```\n * // in application configuration\n * use yii\\web\\Response;\n *\n * return [\n *     'bootstrap' => [\n *         [\n *             'class' => 'yii\\filters\\ContentNegotiator',\n *             'formats' => [\n *                 'application/json' => Response::FORMAT_JSON,\n *                 'application/xml' => Response::FORMAT_XML,\n *             ],\n *             'languages' => [\n *                 'en',\n *                 'de',\n *             ],\n *         ],\n *     ],\n * ];\n * ```\n *\n * The following code shows how you can use ContentNegotiator as an action filter in either a controller or a module.\n * In this case, the content negotiation result only applies to the corresponding controller or module, or even\n * specific actions if you configure the `only` or `except` property of the filter.\n *\n * ```\n * use yii\\web\\Response;\n *\n * public function behaviors()\n * {\n *     return [\n *         [\n *             'class' => 'yii\\filters\\ContentNegotiator',\n *             'only' => ['view', 'index'],  // in a controller\n *             // if in a module, use the following IDs for user actions\n *             // 'only' => ['user/view', 'user/index']\n *             'formats' => [\n *                 'application/json' => Response::FORMAT_JSON,\n *             ],\n *             'languages' => [\n *                 'en',\n *                 'de',\n *             ],\n *         ],\n *     ];\n * }\n * ```\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Component = Component\n * @extends ActionFilter<T>\n */\nclass ContentNegotiator extends ActionFilter implements BootstrapInterface\n{\n    /**\n     * @var string the name of the GET parameter that specifies the response format.\n     * Note that if the specified format does not exist in [[formats]], a [[NotAcceptableHttpException]]\n     * exception will be thrown.  If the parameter value is empty or if this property is null,\n     * the response format will be determined based on the `Accept` HTTP header only.\n     * @see formats\n     */\n    public $formatParam = '_format';\n    /**\n     * @var string the name of the GET parameter that specifies the [[\\yii\\base\\Application::$language|application language]].\n     * Note that if the specified language does not match any of [[languages]], the first language in [[languages]]\n     * will be used. If the parameter value is empty or if this property is null,\n     * the application language will be determined based on the `Accept-Language` HTTP header only.\n     * @see languages\n     */\n    public $languageParam = '_lang';\n    /**\n     * @var array|null list of supported response formats. The keys are MIME types (e.g. `application/json`)\n     * while the values are the corresponding formats (e.g. `html`, `json`) which must be supported\n     * as declared in [[\\yii\\web\\Response::$formatters]].\n     *\n     * If this property is empty or not set, response format negotiation will be skipped.\n     */\n    public $formats;\n    /**\n     * @var array|null a list of supported languages. The array keys are the supported language variants (e.g. `en-GB`, `en-US`),\n     * while the array values are the corresponding language codes (e.g. `en`, `de`) recognized by the application.\n     *\n     * Array keys are not always required. When an array value does not have a key, the matching of the requested language\n     * will be based on a language fallback mechanism. For example, a value of `en` will match `en`, `en_US`, `en-US`, `en-GB`, etc.\n     *\n     * If this property is empty or not set, language negotiation will be skipped.\n     */\n    public $languages;\n    /**\n     * @var Request the current request. If not set, the `request` application component will be used.\n     */\n    public $request;\n    /**\n     * @var Response|null the response to be sent. If not set, the `response` application component will be used.\n     */\n    public $response;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function bootstrap($app)\n    {\n        $this->negotiate();\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function beforeAction($action)\n    {\n        $this->negotiate();\n        return true;\n    }\n\n    /**\n     * Negotiates the response format and application language.\n     */\n    public function negotiate()\n    {\n        $request = $this->request ?: Yii::$app->getRequest();\n        $response = $this->response ?: Yii::$app->getResponse();\n        if (!empty($this->formats)) {\n            if (\\count($this->formats) > 1) {\n                $response->getHeaders()->add('Vary', 'Accept');\n            }\n            $this->negotiateContentType($request, $response);\n        }\n        if (!empty($this->languages)) {\n            if (\\count($this->languages) > 1) {\n                $response->getHeaders()->add('Vary', 'Accept-Language');\n            }\n            Yii::$app->language = $this->negotiateLanguage($request);\n        }\n    }\n\n    /**\n     * Negotiates the response format.\n     * @param Request $request\n     * @param Response $response\n     * @throws BadRequestHttpException if an array received for GET parameter [[formatParam]].\n     * @throws NotAcceptableHttpException if none of the requested content types is accepted.\n     */\n    protected function negotiateContentType($request, $response)\n    {\n        if (!empty($this->formatParam) && ($format = $request->get($this->formatParam)) !== null) {\n            if (is_array($format)) {\n                throw new BadRequestHttpException(\"Invalid data received for GET parameter '{$this->formatParam}'.\");\n            }\n\n            if (in_array($format, $this->formats)) {\n                $response->format = $format;\n                $response->acceptMimeType = null;\n                $response->acceptParams = [];\n                return;\n            }\n\n            throw new NotAcceptableHttpException('The requested response format is not supported: ' . $format);\n        }\n\n        $types = $request->getAcceptableContentTypes();\n        if (empty($types)) {\n            $types['*/*'] = [];\n        }\n\n        foreach ($types as $type => $params) {\n            if (isset($this->formats[$type])) {\n                $response->format = $this->formats[$type];\n                $response->acceptMimeType = $type;\n                $response->acceptParams = $params;\n                return;\n            }\n        }\n\n        foreach ($this->formats as $type => $format) {\n            $response->format = $format;\n            $response->acceptMimeType = $type;\n            $response->acceptParams = [];\n            break;\n        }\n\n        if (isset($types['*/*'])) {\n            return;\n        }\n\n        throw new NotAcceptableHttpException('None of your requested content types is supported.');\n    }\n\n    /**\n     * Negotiates the application language.\n     * @param Request $request\n     * @return string the chosen language\n     */\n    protected function negotiateLanguage($request)\n    {\n        if (!empty($this->languageParam) && ($language = $request->get($this->languageParam)) !== null) {\n            if (is_array($language)) {\n                // If an array received, then skip it and use the first of supported languages\n                return reset($this->languages);\n            }\n            if (isset($this->languages[$language])) {\n                return $this->languages[$language];\n            }\n            foreach ($this->languages as $key => $supported) {\n                if (is_int($key) && $this->isLanguageSupported($language, $supported)) {\n                    return $supported;\n                }\n            }\n\n            return reset($this->languages);\n        }\n\n        foreach ($request->getAcceptableLanguages() as $language) {\n            if (isset($this->languages[$language])) {\n                return $this->languages[$language];\n            }\n            foreach ($this->languages as $key => $supported) {\n                if (is_int($key) && $this->isLanguageSupported($language, $supported)) {\n                    return $supported;\n                }\n            }\n        }\n\n        return reset($this->languages);\n    }\n\n    /**\n     * Returns a value indicating whether the requested language matches the supported language.\n     * @param string $requested the requested language code\n     * @param string $supported the supported language code\n     * @return bool whether the requested language is supported\n     */\n    protected function isLanguageSupported($requested, $supported)\n    {\n        $supported = str_replace('_', '-', strtolower($supported));\n        $requested = str_replace('_', '-', strtolower($requested));\n        return strpos($requested . '-', $supported . '-') === 0;\n    }\n}\n"
  },
  {
    "path": "framework/filters/Cors.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\filters;\n\nuse Yii;\nuse yii\\base\\Action;\nuse yii\\base\\ActionFilter;\nuse yii\\base\\Component;\nuse yii\\base\\Controller;\nuse yii\\base\\InvalidConfigException;\nuse yii\\base\\Module;\nuse yii\\web\\Request;\nuse yii\\web\\Response;\n\n/**\n * Cors filter implements [Cross Origin Resource Sharing](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing).\n *\n * Make sure to read carefully what CORS does and does not. CORS do not secure your API,\n * but allow the developer to grant access to third party code (ajax calls from external domain).\n *\n * You may use CORS filter by attaching it as a behavior to a controller or module, like the following,\n *\n * ```\n * public function behaviors()\n * {\n *     return [\n *         'corsFilter' => [\n *             'class' => \\yii\\filters\\Cors::class,\n *         ],\n *     ];\n * }\n * ```\n *\n * The CORS filter can be specialized to restrict parameters, like this,\n * [MDN CORS Information](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS)\n *\n * ```\n * public function behaviors()\n * {\n *     return [\n *         'corsFilter' => [\n *             'class' => \\yii\\filters\\Cors::class,\n *             'cors' => [\n *                 // restrict access to\n *                 'Origin' => ['http://www.myserver.com', 'https://www.myserver.com'],\n *                 // Allow only POST and PUT methods\n *                 'Access-Control-Request-Method' => ['POST', 'PUT'],\n *                 // Allow only headers 'X-Wsse'\n *                 'Access-Control-Request-Headers' => ['X-Wsse'],\n *                 // Allow credentials (cookies, authorization headers, etc.) to be exposed to the browser\n *                 'Access-Control-Allow-Credentials' => true,\n *                 // Allow OPTIONS caching\n *                 'Access-Control-Max-Age' => 3600,\n *                 // Allow the X-Pagination-Current-Page header to be exposed to the browser.\n *                 'Access-Control-Expose-Headers' => ['X-Pagination-Current-Page'],\n *             ],\n *\n *         ],\n *     ];\n * }\n * ```\n *\n * For more information on how to add the CORS filter to a controller, see\n * the [Guide on REST controllers](guide:rest-controllers#cors).\n *\n * @author Philippe Gaultier <pgaultier@gmail.com>\n * @since 2.0\n *\n * @template T of Component = Component\n * @extends ActionFilter<T>\n */\nclass Cors extends ActionFilter\n{\n    /**\n     * @var Request|null the current request. If not set, the `request` application component will be used.\n     */\n    public $request;\n    /**\n     * @var Response|null the response to be sent. If not set, the `response` application component will be used.\n     */\n    public $response;\n    /**\n     * @var array define specific CORS rules for specific actions\n     */\n    public $actions = [];\n    /**\n     * @var array Basic headers handled for the CORS requests.\n     */\n    public $cors = [\n        'Origin' => ['*'],\n        'Access-Control-Request-Method' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'],\n        'Access-Control-Request-Headers' => ['*'],\n        'Access-Control-Allow-Credentials' => null,\n        'Access-Control-Max-Age' => 86400,\n        'Access-Control-Expose-Headers' => [],\n    ];\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function beforeAction($action)\n    {\n        $this->request = $this->request ?: Yii::$app->getRequest();\n        $this->response = $this->response ?: Yii::$app->getResponse();\n\n        $this->overrideDefaultSettings($action);\n\n        $requestCorsHeaders = $this->extractHeaders();\n        $responseCorsHeaders = $this->prepareHeaders($requestCorsHeaders);\n        $this->addCorsHeaders($this->response, $responseCorsHeaders);\n\n        if ($this->request->isOptions && $this->request->headers->has('Access-Control-Request-Method')) {\n            // it is CORS preflight request, respond with 200 OK without further processing\n            $this->response->setStatusCode(200);\n            return false;\n        }\n\n        return true;\n    }\n\n    /**\n     * Override settings for specific action.\n     * @param Action $action the action settings to override\n     */\n    public function overrideDefaultSettings($action)\n    {\n        $actionId = $this->getActionId($action);\n\n        if (isset($this->actions[$actionId])) {\n            $actionParams = $this->actions[$actionId];\n            $actionParamsKeys = array_keys($actionParams);\n            foreach ($this->cors as $headerField => $headerValue) {\n                if (in_array($headerField, $actionParamsKeys)) {\n                    $this->cors[$headerField] = $actionParams[$headerField];\n                }\n            }\n        }\n    }\n\n    /**\n     * Extract CORS headers from the request.\n     * @return array CORS headers to handle\n     */\n    public function extractHeaders()\n    {\n        $headers = [];\n        foreach (array_keys($this->cors) as $headerField) {\n            $serverField = $this->headerizeToPhp($headerField);\n            $headerData = isset($_SERVER[$serverField]) ? $_SERVER[$serverField] : null;\n            if ($headerData !== null) {\n                $headers[$headerField] = $headerData;\n            }\n        }\n\n        return $headers;\n    }\n\n    /**\n     * For each CORS headers create the specific response.\n     * @param array $requestHeaders CORS headers we have detected\n     * @return array CORS headers ready to be sent\n     */\n    public function prepareHeaders($requestHeaders)\n    {\n        $responseHeaders = [];\n        // handle Origin\n        if (isset($requestHeaders['Origin'], $this->cors['Origin'])) {\n            if (in_array($requestHeaders['Origin'], $this->cors['Origin'], true)) {\n                $responseHeaders['Access-Control-Allow-Origin'] = $requestHeaders['Origin'];\n            }\n\n            if (in_array('*', $this->cors['Origin'], true)) {\n                // Per CORS standard (https://fetch.spec.whatwg.org), wildcard origins shouldn't be used together with credentials\n                if (isset($this->cors['Access-Control-Allow-Credentials']) && $this->cors['Access-Control-Allow-Credentials']) {\n                    if (YII_DEBUG) {\n                        throw new InvalidConfigException(\"Allowing credentials for wildcard origins is insecure. Please specify more restrictive origins or set 'credentials' to false in your CORS configuration.\");\n                    } else {\n                        Yii::error(\"Allowing credentials for wildcard origins is insecure. Please specify more restrictive origins or set 'credentials' to false in your CORS configuration.\", __METHOD__);\n                    }\n                } else {\n                    $responseHeaders['Access-Control-Allow-Origin'] = '*';\n                }\n            }\n        }\n\n        $this->prepareAllowHeaders('Headers', $requestHeaders, $responseHeaders);\n\n        if (isset($requestHeaders['Access-Control-Request-Method'])) {\n            $responseHeaders['Access-Control-Allow-Methods'] = implode(', ', $this->cors['Access-Control-Request-Method']);\n        }\n\n        if (isset($this->cors['Access-Control-Allow-Credentials'])) {\n            $responseHeaders['Access-Control-Allow-Credentials'] = $this->cors['Access-Control-Allow-Credentials'] ? 'true' : 'false';\n        }\n\n        if (isset($this->cors['Access-Control-Max-Age']) && $this->request->getIsOptions()) {\n            $responseHeaders['Access-Control-Max-Age'] = $this->cors['Access-Control-Max-Age'];\n        }\n\n        if (isset($this->cors['Access-Control-Expose-Headers'])) {\n            $responseHeaders['Access-Control-Expose-Headers'] = implode(', ', $this->cors['Access-Control-Expose-Headers']);\n        }\n\n        if (isset($this->cors['Access-Control-Allow-Headers'])) {\n            $responseHeaders['Access-Control-Allow-Headers'] = implode(', ', $this->cors['Access-Control-Allow-Headers']);\n        }\n\n        return $responseHeaders;\n    }\n\n    /**\n     * Handle classic CORS request to avoid duplicate code.\n     * @param string $type the kind of headers we would handle\n     * @param array $requestHeaders CORS headers request by client\n     * @param array $responseHeaders CORS response headers sent to the client\n     */\n    protected function prepareAllowHeaders($type, $requestHeaders, &$responseHeaders)\n    {\n        $requestHeaderField = 'Access-Control-Request-' . $type;\n        $responseHeaderField = 'Access-Control-Allow-' . $type;\n        if (!isset($requestHeaders[$requestHeaderField], $this->cors[$requestHeaderField])) {\n            return;\n        }\n        if (in_array('*', $this->cors[$requestHeaderField])) {\n            $responseHeaders[$responseHeaderField] = $this->headerize($requestHeaders[$requestHeaderField]);\n        } else {\n            $requestedData = preg_split('/[\\\\s,]+/', $requestHeaders[$requestHeaderField], -1, PREG_SPLIT_NO_EMPTY);\n            $acceptedData = array_uintersect($requestedData, $this->cors[$requestHeaderField], 'strcasecmp');\n            if (!empty($acceptedData)) {\n                $responseHeaders[$responseHeaderField] = implode(', ', $acceptedData);\n            }\n        }\n    }\n\n    /**\n     * Adds the CORS headers to the response.\n     * @param Response $response\n     * @param array $headers CORS headers which have been computed\n     */\n    public function addCorsHeaders($response, $headers)\n    {\n        if (empty($headers) === false) {\n            $responseHeaders = $response->getHeaders();\n            foreach ($headers as $field => $value) {\n                $responseHeaders->set($field, $value);\n            }\n        }\n    }\n\n    /**\n     * Convert any string (including php headers with HTTP prefix) to header format.\n     *\n     * Example:\n     *  - X-PINGOTHER -> X-Pingother\n     *  - X_PINGOTHER -> X-Pingother\n     * @param string $string string to convert\n     * @return string the result in \"header\" format\n     */\n    protected function headerize($string)\n    {\n        $headers = preg_split('/[\\\\s,]+/', $string, -1, PREG_SPLIT_NO_EMPTY);\n        $headers = array_map(function ($element) {\n            return str_replace(' ', '-', ucwords(strtolower(str_replace(['_', '-'], [' ', ' '], $element))));\n        }, $headers);\n        return implode(', ', $headers);\n    }\n\n    /**\n     * Convert any string (including php headers with HTTP prefix) to header format.\n     *\n     * Example:\n     *  - X-Pingother -> HTTP_X_PINGOTHER\n     *  - X PINGOTHER -> HTTP_X_PINGOTHER\n     * @param string $string string to convert\n     * @return string the result in \"php $_SERVER header\" format\n     */\n    protected function headerizeToPhp($string)\n    {\n        return 'HTTP_' . strtoupper(str_replace([' ', '-'], ['_', '_'], $string));\n    }\n}\n"
  },
  {
    "path": "framework/filters/HostControl.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\filters;\n\nuse Yii;\nuse yii\\base\\Action;\nuse yii\\base\\ActionFilter;\nuse yii\\base\\Component;\nuse yii\\base\\Controller;\nuse yii\\helpers\\StringHelper;\nuse yii\\web\\NotFoundHttpException;\n\n/**\n * HostControl provides simple control over requested host name.\n *\n * This filter provides protection against ['host header' attacks](https://www.acunetix.com/vulnerabilities/web/host-header-attack),\n * allowing action execution only for specified host names.\n *\n * Application configuration example:\n *\n * ```\n * return [\n *     'as hostControl' => [\n *         'class' => 'yii\\filters\\HostControl',\n *         'allowedHosts' => [\n *             'example.com',\n *             '*.example.com',\n *         ],\n *     ],\n *     // ...\n * ];\n * ```\n *\n * Controller configuration example:\n *\n * ```\n * use yii\\web\\Controller;\n * use yii\\filters\\HostControl;\n *\n * class SiteController extends Controller\n * {\n *     public function behaviors()\n *     {\n *         return [\n *             'hostControl' => [\n *                 'class' => HostControl::class,\n *                 'allowedHosts' => [\n *                     'example.com',\n *                     '*.example.com',\n *                 ],\n *             ],\n *         ];\n *     }\n *\n *     // ...\n * }\n * ```\n *\n * > Note: the best way to restrict allowed host names is usage of the web server 'virtual hosts' configuration.\n * This filter should be used only if this configuration is not available or compromised.\n *\n * @author Paul Klimov <klimov.paul@gmail.com>\n * @since 2.0.11\n *\n * @template T of Component = Component\n * @extends ActionFilter<T>\n */\nclass HostControl extends ActionFilter\n{\n    /**\n     * @var array|\\Closure|null list of host names, which are allowed.\n     * Each host can be specified as a wildcard pattern. For example:\n     *\n     * ```\n     * [\n     *     'example.com',\n     *     '*.example.com',\n     * ]\n     * ```\n     *\n     * This field can be specified as a PHP callback of following signature:\n     *\n     * ```\n     * function (\\yii\\base\\Action $action) {\n     *     //return array of strings\n     * }\n     * ```\n     *\n     * where `$action` is the current [[\\yii\\base\\Action|action]] object.\n     *\n     * If this field is not set - no host name check will be performed.\n     */\n    public $allowedHosts;\n    /**\n     * @var callable|null a callback that will be called if the current host does not match [[allowedHosts]].\n     * If not set, [[denyAccess()]] will be called.\n     *\n     * The signature of the callback should be as follows:\n     *\n     * ```\n     * function (\\yii\\base\\Action $action)\n     * ```\n     *\n     * where `$action` is the current [[\\yii\\base\\Action|action]] object.\n     *\n     * > Note: while implementing your own host deny processing, make sure you avoid usage of the current requested\n     * host name, creation of absolute URL links, caching page parts and so on.\n     */\n    public $denyCallback;\n    /**\n     * @var string|null fallback host info (e.g. `https://www.yiiframework.com`) used when [[\\yii\\web\\Request::$hostInfo|Request::$hostInfo]] is invalid.\n     * This value will replace [[\\yii\\web\\Request::$hostInfo|Request::$hostInfo]] before [[$denyCallback]] is called to make sure that\n     * an invalid host will not be used for further processing. You can set it to `null` to leave [[\\yii\\web\\Request::$hostInfo|Request::$hostInfo]] untouched.\n     * Default value is empty string (this will result creating relative URLs instead of absolute).\n     * @see \\yii\\web\\Request::getHostInfo()\n     */\n    public $fallbackHostInfo = '';\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function beforeAction($action)\n    {\n        $allowedHosts = $this->allowedHosts;\n        if ($allowedHosts instanceof \\Closure) {\n            $allowedHosts = call_user_func($allowedHosts, $action);\n        }\n        if ($allowedHosts === null) {\n            return true;\n        }\n\n        if (!is_array($allowedHosts) && !$allowedHosts instanceof \\Traversable) {\n            $allowedHosts = (array) $allowedHosts;\n        }\n\n        $currentHost = Yii::$app->getRequest()->getHostName();\n\n        foreach ($allowedHosts as $allowedHost) {\n            if (StringHelper::matchWildcard($allowedHost, $currentHost)) {\n                return true;\n            }\n        }\n\n        // replace invalid host info to prevent using it in further processing\n        if ($this->fallbackHostInfo !== null) {\n            Yii::$app->getRequest()->setHostInfo($this->fallbackHostInfo);\n        }\n\n        if ($this->denyCallback !== null) {\n            call_user_func($this->denyCallback, $action);\n        } else {\n            $this->denyAccess($action);\n        }\n\n        return false;\n    }\n\n    /**\n     * Denies the access.\n     * The default implementation will display 404 page right away, terminating the program execution.\n     * You may override this method, creating your own deny access handler. While doing so, make sure you\n     * avoid usage of the current requested host name, creation of absolute URL links, caching page parts and so on.\n     * @param Action $action the action to be executed.\n     * @throws NotFoundHttpException\n     */\n    protected function denyAccess($action)\n    {\n        $exception = new NotFoundHttpException(Yii::t('yii', 'Page not found.'));\n\n        // use regular error handling if $this->fallbackHostInfo was set\n        if (!empty(Yii::$app->getRequest()->hostName)) {\n            throw $exception;\n        }\n\n        $response = Yii::$app->getResponse();\n        $errorHandler = Yii::$app->getErrorHandler();\n\n        $response->setStatusCode($exception->statusCode, $exception->getMessage());\n        $response->data = $errorHandler->renderFile($errorHandler->errorView, ['exception' => $exception]);\n        $response->send();\n\n        Yii::$app->end();\n    }\n}\n"
  },
  {
    "path": "framework/filters/HttpCache.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\filters;\n\nuse Yii;\nuse yii\\base\\ActionFilter;\nuse yii\\base\\Component;\n\n/**\n * HttpCache implements client-side caching by utilizing the `Last-Modified` and `ETag` HTTP headers.\n *\n * It is an action filter that can be added to a controller and handles the `beforeAction` event.\n *\n * To use HttpCache, declare it in the `behaviors()` method of your controller class.\n * In the following example the filter will be applied to the `index` action and\n * the Last-Modified header will contain the date of the last update to the user table in the database.\n *\n * ```\n * public function behaviors()\n * {\n *     return [\n *         [\n *             'class' => 'yii\\filters\\HttpCache',\n *             'only' => ['index'],\n *             'lastModified' => function ($action, $params) {\n *                 $q = new \\yii\\db\\Query();\n *                 return $q->from('user')->max('updated_at');\n *             },\n * //            'etagSeed' => function ($action, $params) {\n * //                return // generate ETag seed here\n * //            }\n *         ],\n *     ];\n * }\n * ```\n *\n * @author Da:Sourcerer <webmaster@dasourcerer.net>\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Component = Component\n * @extends ActionFilter<T>\n */\nclass HttpCache extends ActionFilter\n{\n    /**\n     * @var callable a PHP callback that returns the UNIX timestamp of the last modification time.\n     * The callback's signature should be:\n     *\n     * ```\n     * function ($action, $params)\n     * ```\n     *\n     * where `$action` is the [[Action]] object that this filter is currently handling;\n     * `$params` takes the value of [[params]]. The callback should return a UNIX timestamp.\n     *\n     * @see https://datatracker.ietf.org/doc/html/rfc7232#section-2.2\n     */\n    public $lastModified;\n    /**\n     * @var callable a PHP callback that generates the ETag seed string.\n     * The callback's signature should be:\n     *\n     * ```\n     * function ($action, $params)\n     * ```\n     *\n     * where `$action` is the [[Action]] object that this filter is currently handling;\n     * `$params` takes the value of [[params]]. The callback should return a string serving\n     * as the seed for generating an ETag.\n     */\n    public $etagSeed;\n    /**\n     * @var bool whether to generate weak ETags.\n     *\n     * Weak ETags should be used if the content should be considered semantically equivalent, but not byte-equal.\n     *\n     * @since 2.0.8\n     * @see https://datatracker.ietf.org/doc/html/rfc7232#section-2.3\n     */\n    public $weakEtag = false;\n    /**\n     * @var mixed additional parameters that should be passed to the [[lastModified]] and [[etagSeed]] callbacks.\n     */\n    public $params;\n    /**\n     * @var string|null the value of the `Cache-Control` HTTP header. If null, the header will not be sent.\n     * @see https://datatracker.ietf.org/doc/html/rfc2616#section-14.9\n     */\n    public $cacheControlHeader = 'public, max-age=3600';\n    /**\n     * @var string|null the name of the cache limiter to be set when [session_cache_limiter()](https://www.php.net/manual/en/function.session-cache-limiter.php)\n     * is called. The default value is an empty string, meaning turning off automatic sending of cache headers entirely.\n     * You may set this property to be `public`, `private`, `private_no_expire`, and `nocache`.\n     * Please refer to [session_cache_limiter()](https://www.php.net/manual/en/function.session-cache-limiter.php)\n     * for detailed explanation of these values.\n     *\n     * If this property is `null`, then `session_cache_limiter()` will not be called. As a result,\n     * PHP will send headers according to the `session.cache_limiter` PHP ini setting.\n     */\n    public $sessionCacheLimiter = '';\n    /**\n     * @var bool a value indicating whether this filter should be enabled.\n     */\n    public $enabled = true;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function beforeAction($action)\n    {\n        if (!$this->enabled) {\n            return true;\n        }\n\n        $verb = Yii::$app->getRequest()->getMethod();\n        if ($verb !== 'GET' && $verb !== 'HEAD' || $this->lastModified === null && $this->etagSeed === null) {\n            return true;\n        }\n\n        $lastModified = $etag = null;\n        if ($this->lastModified !== null) {\n            $lastModified = call_user_func($this->lastModified, $action, $this->params);\n        }\n        if ($this->etagSeed !== null) {\n            $seed = call_user_func($this->etagSeed, $action, $this->params);\n            if ($seed !== null) {\n                $etag = $this->generateEtag($seed);\n            }\n        }\n\n        $this->sendCacheControlHeader();\n\n        $response = Yii::$app->getResponse();\n        if ($etag !== null) {\n            $response->getHeaders()->set('Etag', $etag);\n        }\n\n        $cacheValid = $this->validateCache($lastModified, $etag);\n        // https://tools.ietf.org/html/rfc7232#section-4.1\n        if ($lastModified !== null && (!$cacheValid || ($cacheValid && $etag === null))) {\n            $response->getHeaders()->set('Last-Modified', gmdate('D, d M Y H:i:s', $lastModified) . ' GMT');\n        }\n        if ($cacheValid) {\n            $response->setStatusCode(304);\n            return false;\n        }\n\n        return true;\n    }\n\n    /**\n     * Validates if the HTTP cache contains valid content.\n     * If both Last-Modified and ETag are null, returns false.\n     * @param int|null $lastModified the calculated Last-Modified value in terms of a UNIX timestamp.\n     * If null, the Last-Modified header will not be validated.\n     * @param string|null $etag the calculated ETag value. If null, the ETag header will not be validated.\n     * @return bool whether the HTTP cache is still valid.\n     */\n    protected function validateCache($lastModified, $etag)\n    {\n        if (Yii::$app->request->headers->has('If-None-Match')) {\n            // HTTP_IF_NONE_MATCH takes precedence over HTTP_IF_MODIFIED_SINCE\n            // https://datatracker.ietf.org/doc/html/rfc7232#section-3.3\n            return $etag !== null && in_array($etag, Yii::$app->request->getETags(), true);\n        } elseif (Yii::$app->request->headers->has('If-Modified-Since')) {\n            return $lastModified !== null && @strtotime(Yii::$app->request->headers->get('If-Modified-Since')) >= $lastModified;\n        }\n\n        return false;\n    }\n\n    /**\n     * Sends the cache control header to the client.\n     * @see cacheControlHeader\n     */\n    protected function sendCacheControlHeader()\n    {\n        if ($this->sessionCacheLimiter !== null) {\n            if ($this->sessionCacheLimiter === '' && !headers_sent() && Yii::$app->getSession()->getIsActive()) {\n                header_remove('Expires');\n                header_remove('Cache-Control');\n                header_remove('Last-Modified');\n                header_remove('Pragma');\n            }\n\n            Yii::$app->getSession()->setCacheLimiter($this->sessionCacheLimiter);\n        }\n\n        $headers = Yii::$app->getResponse()->getHeaders();\n\n        if ($this->cacheControlHeader !== null) {\n            $headers->set('Cache-Control', $this->cacheControlHeader);\n        }\n    }\n\n    /**\n     * Generates an ETag from the given seed string.\n     * @param string $seed Seed for the ETag\n     * @return string the generated ETag\n     */\n    protected function generateEtag($seed)\n    {\n        $etag = '\"' . rtrim(base64_encode(sha1($seed, true)), '=') . '\"';\n        return $this->weakEtag ? 'W/' . $etag : $etag;\n    }\n}\n"
  },
  {
    "path": "framework/filters/PageCache.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\filters;\n\nuse Closure;\nuse Yii;\nuse yii\\base\\ActionFilter;\nuse yii\\base\\Component;\nuse yii\\base\\DynamicContentAwareInterface;\nuse yii\\base\\DynamicContentAwareTrait;\nuse yii\\caching\\CacheInterface;\nuse yii\\caching\\Dependency;\nuse yii\\di\\Instance;\nuse yii\\web\\Response;\n\n/**\n * PageCache implements server-side caching of whole pages.\n *\n * It is an action filter that can be added to a controller and handles the `beforeAction` event.\n *\n * To use PageCache, declare it in the `behaviors()` method of your controller class.\n * In the following example the filter will be applied to the `index` action and\n * cache the whole page for maximum 60 seconds or until the count of entries in the post table changes.\n * It also stores different versions of the page depending on the application language.\n *\n * ```\n * public function behaviors()\n * {\n *     return [\n *         'pageCache' => [\n *             'class' => 'yii\\filters\\PageCache',\n *             'only' => ['index'],\n *             'duration' => 60,\n *             'dependency' => [\n *                 'class' => 'yii\\caching\\DbDependency',\n *                 'sql' => 'SELECT COUNT(*) FROM post',\n *             ],\n *             'variations' => [\n *                 \\Yii::$app->language,\n *             ]\n *         ],\n *     ];\n * }\n * ```\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Sergey Makinen <sergey@makinen.ru>\n * @since 2.0\n *\n * @template T of Component = Component\n * @extends ActionFilter<T>\n */\nclass PageCache extends ActionFilter implements DynamicContentAwareInterface\n{\n    use DynamicContentAwareTrait;\n\n    /**\n     * Page cache version, to detect incompatibilities in cached values when the\n     * data format of the cache changes.\n     */\n    public const PAGE_CACHE_VERSION = 1;\n    /**\n     * @var bool whether the content being cached should be differentiated according to the route.\n     * A route consists of the requested controller ID and action ID. Defaults to `true`.\n     */\n    public $varyByRoute = true;\n    /**\n     * @var CacheInterface|array|string the cache object or the application component ID of the cache object.\n     * After the PageCache object is created, if you want to change this property,\n     * you should only assign it with a cache object.\n     * Starting from version 2.0.2, this can also be a configuration array for creating the object.\n     */\n    public $cache = 'cache';\n    /**\n     * @var int number of seconds that the data can remain valid in cache.\n     * Use `0` to indicate that the cached data will never expire.\n     */\n    public $duration = 60;\n    /**\n     * @var array|Dependency the dependency that the cached content depends on.\n     * This can be either a [[Dependency]] object or a configuration array for creating the dependency object.\n     * For example,\n     *\n     * ```\n     * [\n     *     'class' => 'yii\\caching\\DbDependency',\n     *     'sql' => 'SELECT MAX(updated_at) FROM post',\n     * ]\n     * ```\n     *\n     * would make the output cache depend on the last modified time of all posts.\n     * If any post has its modification time changed, the cached content would be invalidated.\n     *\n     * If [[cacheCookies]] or [[cacheHeaders]] is enabled, then [[\\yii\\caching\\Dependency::reusable]] should be enabled as well to save performance.\n     * This is because the cookies and headers are currently stored separately from the actual page content, causing the dependency to be evaluated twice.\n     */\n    public $dependency;\n    /**\n     * @var string[]|string|callable list of factors that would cause the variation of the content being cached.\n     * Each factor is a string representing a variation (e.g. the language, a GET parameter).\n     * The following variation setting will cause the content to be cached in different versions\n     * according to the current application language:\n     *\n     * ```\n     * [\n     *     Yii::$app->language,\n     * ]\n     * ```\n     *\n     * Since version 2.0.48 you can provide an anonymous function to generate variations. This is especially helpful\n     * when you need to access the User component, which is resolved before the PageCache behavior:\n     *\n     * ```\n     * 'variations' => function() {\n     *     return [\n     *         Yii::$app->language,\n     *         Yii::$app->user->id\n     *     ];\n     * }\n     * ```\n     *\n     * The callable should return an array.\n     */\n    public $variations;\n    /**\n     * @var bool whether to enable the page cache. You may use this property to turn on and off\n     * the page cache according to specific setting (e.g. enable page cache only for GET requests).\n     */\n    public $enabled = true;\n    /**\n     * @var \\yii\\base\\View|null the view component to use for caching. If not set, the default application view component\n     * [[\\yii\\web\\Application::view]] will be used.\n     */\n    public $view;\n    /**\n     * @var bool|array a boolean value indicating whether to cache all cookies, or an array of\n     * cookie names indicating which cookies can be cached. Be very careful with caching cookies, because\n     * it may leak sensitive or private data stored in cookies to unwanted users.\n     * @since 2.0.4\n     */\n    public $cacheCookies = false;\n    /**\n     * @var bool|array a boolean value indicating whether to cache all HTTP headers, or an array of\n     * HTTP header names (case-sensitive) indicating which HTTP headers can be cached.\n     * Note if your HTTP headers contain sensitive information, you should white-list which headers can be cached.\n     * @since 2.0.4\n     */\n    public $cacheHeaders = true;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        if ($this->view === null) {\n            $this->view = Yii::$app->getView();\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function beforeAction($action)\n    {\n        if (!$this->enabled) {\n            return true;\n        }\n\n        $this->cache = Instance::ensure($this->cache, 'yii\\caching\\CacheInterface');\n\n        if (is_array($this->dependency)) {\n            $this->dependency = Yii::createObject($this->dependency);\n        }\n\n        $response = Yii::$app->getResponse();\n        $data = $this->cache->get($this->calculateCacheKey());\n        if (!is_array($data) || !isset($data['cacheVersion']) || $data['cacheVersion'] !== static::PAGE_CACHE_VERSION) {\n            $this->view->pushDynamicContent($this);\n            ob_start();\n            ob_implicit_flush(false);\n            $response->on(Response::EVENT_AFTER_SEND, [$this, 'cacheResponse']);\n            Yii::debug('Valid page content is not found in the cache.', __METHOD__);\n            return true;\n        }\n\n        $this->restoreResponse($response, $data);\n        Yii::debug('Valid page content is found in the cache.', __METHOD__);\n        return false;\n    }\n\n    /**\n     * This method is invoked right before the response caching is to be started.\n     * You may override this method to cancel caching by returning `false` or store an additional data\n     * in a cache entry by returning an array instead of `true`.\n     * @return bool|array whether to cache or not, return an array instead of `true` to store an additional data.\n     * @since 2.0.11\n     */\n    public function beforeCacheResponse()\n    {\n        return true;\n    }\n\n    /**\n     * This method is invoked right after the response restoring is finished (but before the response is sent).\n     * You may override this method to do last-minute preparation before the response is sent.\n     * @param array|null $data an array of an additional data stored in a cache entry or `null`.\n     * @since 2.0.11\n     */\n    public function afterRestoreResponse($data)\n    {\n    }\n\n    /**\n     * Restores response properties from the given data.\n     * @param Response $response the response to be restored.\n     * @param array $data the response property data.\n     * @since 2.0.3\n     */\n    protected function restoreResponse($response, $data)\n    {\n        foreach (['format', 'version', 'statusCode', 'statusText', 'content'] as $name) {\n            $response->{$name} = $data[$name];\n        }\n        foreach (['headers', 'cookies'] as $name) {\n            if (isset($data[$name]) && is_array($data[$name])) {\n                $response->{$name}->fromArray(array_merge($data[$name], $response->{$name}->toArray()));\n            }\n        }\n        if (!empty($data['dynamicPlaceholders']) && is_array($data['dynamicPlaceholders'])) {\n            $response->content = $this->updateDynamicContent($response->content, $data['dynamicPlaceholders'], true);\n        }\n        $this->afterRestoreResponse(isset($data['cacheData']) ? $data['cacheData'] : null);\n    }\n\n    /**\n     * Caches response properties.\n     * @since 2.0.3\n     */\n    public function cacheResponse()\n    {\n        $this->view->popDynamicContent();\n        $beforeCacheResponseResult = $this->beforeCacheResponse();\n        if ($beforeCacheResponseResult === false) {\n            echo $this->updateDynamicContent(ob_get_clean(), $this->getDynamicPlaceholders());\n            return;\n        }\n\n        $response = Yii::$app->getResponse();\n        $response->off(Response::EVENT_AFTER_SEND, [$this, 'cacheResponse']);\n        $data = [\n            'cacheVersion' => static::PAGE_CACHE_VERSION,\n            'cacheData' => is_array($beforeCacheResponseResult) ? $beforeCacheResponseResult : null,\n            'content' => ob_get_clean(),\n        ];\n        if ($data['content'] === false || $data['content'] === '') {\n            return;\n        }\n\n        $data['dynamicPlaceholders'] = $this->getDynamicPlaceholders();\n        foreach (['format', 'version', 'statusCode', 'statusText'] as $name) {\n            $data[$name] = $response->{$name};\n        }\n        $this->insertResponseHeaderCollectionIntoData($response, $data);\n        $this->insertResponseCookieCollectionIntoData($response, $data);\n        $this->cache->set($this->calculateCacheKey(), $data, $this->duration, $this->dependency);\n        $data['content'] = $this->updateDynamicContent($data['content'], $this->getDynamicPlaceholders());\n        echo $data['content'];\n    }\n\n    /**\n     * Inserts (or filters/ignores according to config) response cookies into a cache data array.\n     * @param Response $response the response.\n     * @param array $data the cache data.\n     */\n    private function insertResponseCookieCollectionIntoData(Response $response, array &$data)\n    {\n        if ($this->cacheCookies === false) {\n            return;\n        }\n\n        $all = $response->cookies->toArray();\n        if (is_array($this->cacheCookies)) {\n            $filtered = [];\n            foreach ($this->cacheCookies as $name) {\n                if (isset($all[$name])) {\n                    $filtered[$name] = $all[$name];\n                }\n            }\n            $all = $filtered;\n        }\n        $data['cookies'] = $all;\n    }\n\n    /**\n     * Inserts (or filters/ignores according to config) response headers into a cache data array.\n     * @param Response $response the response.\n     * @param array $data the cache data.\n     */\n    private function insertResponseHeaderCollectionIntoData(Response $response, array &$data)\n    {\n        if ($this->cacheHeaders === false) {\n            return;\n        }\n\n        $all = $response->headers->toOriginalArray();\n        if (is_array($this->cacheHeaders)) {\n            $filtered = [];\n            foreach ($this->cacheHeaders as $name) {\n                if (isset($all[$name])) {\n                    $filtered[$name] = $all[$name];\n                }\n            }\n            $all = $filtered;\n        }\n        $data['headers'] = $all;\n    }\n\n    /**\n     * @return array the key used to cache response properties.\n     * @since 2.0.3\n     */\n    protected function calculateCacheKey()\n    {\n        $key = [__CLASS__];\n        if ($this->varyByRoute) {\n            $key[] = Yii::$app->requestedRoute;\n        }\n\n        if ($this->variations instanceof Closure) {\n            $variations = call_user_func($this->variations, $this);\n        } else {\n            $variations = $this->variations;\n        }\n        return array_merge($key, (array) $variations);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getView()\n    {\n        return $this->view;\n    }\n}\n"
  },
  {
    "path": "framework/filters/RateLimitInterface.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\filters;\n\nuse yii\\base\\Action;\n\n/**\n * RateLimitInterface is the interface that may be implemented by an identity object to enforce rate limiting.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\ninterface RateLimitInterface\n{\n    /**\n     * Returns the maximum number of allowed requests and the window size.\n     * @param \\yii\\web\\Request $request the current request\n     * @param Action $action the action to be executed\n     * @return array an array of two elements. The first element is the maximum number of allowed requests,\n     * and the second element is the size of the window in seconds.\n     */\n    public function getRateLimit($request, $action);\n\n    /**\n     * Loads the number of allowed requests and the corresponding timestamp from a persistent storage.\n     * @param \\yii\\web\\Request $request the current request\n     * @param Action $action the action to be executed\n     * @return array an array of two elements. The first element is the number of allowed requests,\n     * and the second element is the corresponding UNIX timestamp.\n     */\n    public function loadAllowance($request, $action);\n\n    /**\n     * Saves the number of allowed requests and the corresponding timestamp to a persistent storage.\n     * @param \\yii\\web\\Request $request the current request\n     * @param Action $action the action to be executed\n     * @param int $allowance the number of allowed requests remaining.\n     * @param int $timestamp the current timestamp.\n     */\n    public function saveAllowance($request, $action, $allowance, $timestamp);\n}\n"
  },
  {
    "path": "framework/filters/RateLimiter.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\filters;\n\nuse Closure;\nuse Yii;\nuse yii\\base\\Action;\nuse yii\\base\\ActionFilter;\nuse yii\\base\\Component;\nuse yii\\base\\Controller;\nuse yii\\base\\Module;\nuse yii\\web\\IdentityInterface;\nuse yii\\web\\Request;\nuse yii\\web\\Response;\nuse yii\\web\\TooManyRequestsHttpException;\n\n/**\n * RateLimiter implements a rate limiting algorithm based on the [leaky bucket algorithm](https://en.wikipedia.org/wiki/Leaky_bucket).\n *\n * You may use RateLimiter by attaching it as a behavior to a controller or module, like the following,\n *\n * ```\n * public function behaviors()\n * {\n *     return [\n *         'rateLimiter' => [\n *             'class' => \\yii\\filters\\RateLimiter::class,\n *         ],\n *     ];\n * }\n * ```\n *\n * When the user has exceeded his rate limit, RateLimiter will throw a [[TooManyRequestsHttpException]] exception.\n *\n * Note that RateLimiter requires [[user]] to implement the [[RateLimitInterface]]. RateLimiter will\n * do nothing if [[user]] is not set or does not implement [[RateLimitInterface]].\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Component = Component\n * @extends ActionFilter<T>\n */\nclass RateLimiter extends ActionFilter\n{\n    /**\n     * @var bool whether to include rate limit headers in the response\n     */\n    public $enableRateLimitHeaders = true;\n    /**\n     * @var string the message to be displayed when rate limit exceeds\n     */\n    public $errorMessage = 'Rate limit exceeded.';\n    /**\n     * @var RateLimitInterface|IdentityInterface|Closure|null the user object that implements the RateLimitInterface. If not set, it will take the value of `Yii::$app->user->getIdentity(false)`.\n     * {@since 2.0.38} It's possible to provide a closure function in order to assign the user identity on runtime. Using a closure to assign the user identity is recommend\n     * when you are **not** using the standard `Yii::$app->user` component. See the example below:\n     * ```\n     * 'user' => function() {\n     *     return Yii::$app->apiUser->identity;\n     * }\n     * ```\n     */\n    public $user;\n    /**\n     * @var Request|null the current request. If not set, the `request` application component will be used.\n     */\n    public $request;\n    /**\n     * @var Response|null the response to be sent. If not set, the `response` application component will be used.\n     */\n    public $response;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        if ($this->request === null) {\n            $this->request = Yii::$app->getRequest();\n        }\n        if ($this->response === null) {\n            $this->response = Yii::$app->getResponse();\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function beforeAction($action)\n    {\n        if ($this->user === null && Yii::$app->getUser()) {\n            $this->user = Yii::$app->getUser()->getIdentity(false);\n        }\n\n        if ($this->user instanceof Closure) {\n            $this->user = call_user_func($this->user, $action);\n        }\n\n        if ($this->user instanceof RateLimitInterface) {\n            Yii::debug('Check rate limit', __METHOD__);\n            $this->checkRateLimit($this->user, $this->request, $this->response, $action);\n        } elseif ($this->user) {\n            Yii::info('Rate limit skipped: \"user\" does not implement RateLimitInterface.', __METHOD__);\n        } else {\n            Yii::info('Rate limit skipped: user not logged in.', __METHOD__);\n        }\n\n        return true;\n    }\n\n    /**\n     * Checks whether the rate limit exceeds.\n     * @param RateLimitInterface $user the current user\n     * @param Request $request\n     * @param Response $response\n     * @param Action $action the action to be executed\n     * @throws TooManyRequestsHttpException if rate limit exceeds\n     */\n    public function checkRateLimit($user, $request, $response, $action)\n    {\n        list($limit, $window) = $user->getRateLimit($request, $action);\n        list($allowance, $timestamp) = $user->loadAllowance($request, $action);\n\n        $current = time();\n\n        $allowance += (int) (($current - $timestamp) * $limit / $window);\n        if ($allowance > $limit) {\n            $allowance = $limit;\n        }\n\n        if ($allowance < 1) {\n            $user->saveAllowance($request, $action, 0, $current);\n            $this->addRateLimitHeaders($response, $limit, 0, $window);\n            throw new TooManyRequestsHttpException($this->errorMessage);\n        }\n\n        $user->saveAllowance($request, $action, $allowance - 1, $current);\n        $this->addRateLimitHeaders($response, $limit, $allowance - 1, (int) (($limit - $allowance + 1) * $window / $limit));\n    }\n\n    /**\n     * Adds the rate limit headers to the response.\n     * @param Response $response\n     * @param int $limit the maximum number of allowed requests during a period\n     * @param int $remaining the remaining number of allowed requests within the current period\n     * @param int $reset the number of seconds to wait before having maximum number of allowed requests again\n     */\n    public function addRateLimitHeaders($response, $limit, $remaining, $reset)\n    {\n        if ($this->enableRateLimitHeaders) {\n            $response->getHeaders()\n                ->set('X-Rate-Limit-Limit', $limit)\n                ->set('X-Rate-Limit-Remaining', $remaining)\n                ->set('X-Rate-Limit-Reset', $reset);\n        }\n    }\n}\n"
  },
  {
    "path": "framework/filters/VerbFilter.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\filters;\n\nuse Yii;\nuse yii\\base\\Action;\nuse yii\\base\\ActionEvent;\nuse yii\\base\\Behavior;\nuse yii\\web\\Controller;\nuse yii\\web\\MethodNotAllowedHttpException;\n\n/**\n * VerbFilter is an action filter that filters by HTTP request methods.\n *\n * It allows to define allowed HTTP request methods for each action and will throw\n * an HTTP 405 error when the method is not allowed.\n *\n * To use VerbFilter, declare it in the `behaviors()` method of your controller class.\n * For example, the following declarations will define a typical set of allowed\n * request methods for REST CRUD actions.\n *\n * ```\n * public function behaviors()\n * {\n *     return [\n *         'verbs' => [\n *             'class' => \\yii\\filters\\VerbFilter::class,\n *             'actions' => [\n *                 'index'  => ['GET'],\n *                 'view'   => ['GET'],\n *                 'create' => ['GET', 'POST'],\n *                 'update' => ['GET', 'PUT', 'POST'],\n *                 'delete' => ['POST', 'DELETE'],\n *             ],\n *         ],\n *     ];\n * }\n * ```\n *\n * @see https://tools.ietf.org/html/rfc2616#section-14.7\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0\n *\n * @extends Behavior<Controller>\n */\nclass VerbFilter extends Behavior\n{\n    /**\n     * @var array this property defines the allowed request methods for each action.\n     * For each action that should only support limited set of request methods\n     * you add an entry with the action id as array key and an array of\n     * allowed methods (e.g. GET, HEAD, PUT) as the value.\n     * If an action is not listed all request methods are considered allowed.\n     *\n     * You can use `'*'` to stand for all actions. When an action is explicitly\n     * specified, it takes precedence over the specification given by `'*'`.\n     *\n     * @see https://www.yiiframework.com/doc/guide/2.0/en/structure-controllers#action-ids\n     *\n     * For example,\n     *\n     * ```\n     * [\n     *   'create' => ['GET', 'POST'],\n     *   'update' => ['GET', 'PUT', 'POST'],\n     *   'delete' => ['POST', 'DELETE'],\n     *   'author-comment' => ['POST', 'DELETE'],\n     *   '*' => ['GET'],\n     * ]\n     * ```\n     */\n    public $actions = [];\n\n\n    /**\n     * Declares event handlers for the [[owner]]'s events.\n     * @return array events (array keys) and the corresponding event handler methods (array values).\n     */\n    public function events()\n    {\n        return [Controller::EVENT_BEFORE_ACTION => 'beforeAction'];\n    }\n\n    /**\n     * @param ActionEvent $event\n     * @return bool\n     * @throws MethodNotAllowedHttpException when the request method is not allowed.\n     */\n    public function beforeAction($event)\n    {\n        $action = $event->action->id;\n        if (isset($this->actions[$action])) {\n            $verbs = $this->actions[$action];\n        } elseif (isset($this->actions['*'])) {\n            $verbs = $this->actions['*'];\n        } else {\n            return $event->isValid;\n        }\n\n        $verb = Yii::$app->getRequest()->getMethod();\n        $allowed = array_map('strtoupper', $verbs);\n        if (!in_array($verb, $allowed)) {\n            $event->isValid = false;\n            // https://tools.ietf.org/html/rfc2616#section-14.7\n            Yii::$app->getResponse()->getHeaders()->set('Allow', implode(', ', $allowed));\n            throw new MethodNotAllowedHttpException('Method Not Allowed. This URL can only handle the following request methods: ' . implode(', ', $allowed) . '.');\n        }\n\n        return $event->isValid;\n    }\n}\n"
  },
  {
    "path": "framework/filters/auth/AuthInterface.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\filters\\auth;\n\nuse yii\\web\\IdentityInterface;\nuse yii\\web\\Request;\nuse yii\\web\\Response;\nuse yii\\web\\UnauthorizedHttpException;\nuse yii\\web\\User;\n\n/**\n * AuthInterface is the interface that should be implemented by auth method classes.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\ninterface AuthInterface\n{\n    /**\n     * Authenticates the current user.\n     * @param User $user\n     * @param Request $request\n     * @param Response $response\n     * @return IdentityInterface|null the authenticated user identity. If authentication information is not provided, null will be returned.\n     * @throws UnauthorizedHttpException if authentication information is provided but is invalid.\n     */\n    public function authenticate($user, $request, $response);\n\n    /**\n     * Generates challenges upon authentication failure.\n     * For example, some appropriate HTTP headers may be generated.\n     * @param Response $response\n     */\n    public function challenge($response);\n\n    /**\n     * Handles authentication failure.\n     * The implementation should normally throw UnauthorizedHttpException to indicate authentication failure.\n     * @param Response $response\n     * @throws UnauthorizedHttpException\n     */\n    public function handleFailure($response);\n}\n"
  },
  {
    "path": "framework/filters/auth/AuthMethod.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\filters\\auth;\n\nuse Yii;\nuse yii\\base\\Action;\nuse yii\\base\\ActionFilter;\nuse yii\\base\\Component;\nuse yii\\helpers\\StringHelper;\nuse yii\\web\\Request;\nuse yii\\web\\Response;\nuse yii\\web\\UnauthorizedHttpException;\nuse yii\\web\\User;\n\n/**\n * AuthMethod is a base class implementing the [[AuthInterface]] interface.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Component = Component\n * @extends ActionFilter<T>\n */\nabstract class AuthMethod extends ActionFilter implements AuthInterface\n{\n    /**\n     * @var User|null the user object representing the user authentication status. If not set, the `user` application component will be used.\n     */\n    public $user;\n    /**\n     * @var Request|null the current request. If not set, the `request` application component will be used.\n     */\n    public $request;\n    /**\n     * @var Response|null the response to be sent. If not set, the `response` application component will be used.\n     */\n    public $response;\n    /**\n     * @var array list of action IDs that this filter will be applied to, but auth failure will not lead to error.\n     * It may be used for actions, that are allowed for public, but return some additional data for authenticated users.\n     * Defaults to empty, meaning authentication is not optional for any action.\n     * Since version 2.0.10 action IDs can be specified as wildcards, e.g. `site/*`.\n     * @see isOptional()\n     * @since 2.0.7\n     */\n    public $optional = [];\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function beforeAction($action)\n    {\n        $response = $this->response ?: Yii::$app->getResponse();\n\n        try {\n            $identity = $this->authenticate(\n                $this->user ?: Yii::$app->getUser(),\n                $this->request ?: Yii::$app->getRequest(),\n                $response\n            );\n        } catch (UnauthorizedHttpException $e) {\n            if ($this->isOptional($action)) {\n                return true;\n            }\n\n            throw $e;\n        }\n\n        if ($identity !== null || $this->isOptional($action)) {\n            return true;\n        }\n\n        $this->challenge($response);\n        $this->handleFailure($response);\n\n        return false;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function challenge($response)\n    {\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function handleFailure($response)\n    {\n        throw new UnauthorizedHttpException('Your request was made with invalid credentials.');\n    }\n\n    /**\n     * Checks, whether authentication is optional for the given action.\n     *\n     * @param Action $action action to be checked.\n     * @return bool whether authentication is optional or not.\n     * @see optional\n     * @since 2.0.7\n     */\n    protected function isOptional($action)\n    {\n        $id = $this->getActionId($action);\n        foreach ($this->optional as $pattern) {\n            if (StringHelper::matchWildcard($pattern, $id)) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n}\n"
  },
  {
    "path": "framework/filters/auth/CompositeAuth.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\filters\\auth;\n\nuse Yii;\nuse yii\\base\\ActionFilter;\nuse yii\\base\\Component;\nuse yii\\base\\Controller;\nuse yii\\base\\InvalidConfigException;\n\n/**\n * CompositeAuth is an action filter that supports multiple authentication methods at the same time.\n *\n * The authentication methods contained by CompositeAuth are configured via [[authMethods]],\n * which is a list of supported authentication class configurations.\n *\n * The following example shows how to support three authentication methods:\n *\n * ```\n * public function behaviors()\n * {\n *     return [\n *         'compositeAuth' => [\n *             'class' => \\yii\\filters\\auth\\CompositeAuth::class,\n *             'authMethods' => [\n *                 \\yii\\filters\\auth\\HttpBasicAuth::class,\n *                 \\yii\\filters\\auth\\QueryParamAuth::class,\n *             ],\n *         ],\n *     ];\n * }\n * ```\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Component = Component\n * @extends AuthMethod<T>\n */\nclass CompositeAuth extends AuthMethod\n{\n    /**\n     * @var list<(class-string<AuthInterface>|array{class: class-string<AuthInterface>})> the supported authentication methods. This property should take a list of supported\n     * authentication methods, each represented by an authentication class or configuration.\n     *\n     * If this property is empty, no authentication will be performed.\n     *\n     * Note that an auth method class must implement the [[\\yii\\filters\\auth\\AuthInterface]] interface.\n     */\n    public $authMethods = [];\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function beforeAction($action)\n    {\n        return empty($this->authMethods) ? true : parent::beforeAction($action);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function authenticate($user, $request, $response)\n    {\n        foreach ($this->authMethods as $i => $auth) {\n            if (!$auth instanceof AuthInterface) {\n                $this->authMethods[$i] = $auth = Yii::createObject($auth);\n                if (!$auth instanceof AuthInterface) {\n                    throw new InvalidConfigException(get_class($auth) . ' must implement yii\\filters\\auth\\AuthInterface');\n                }\n            }\n\n            if (\n                $this->owner instanceof Controller\n                && (\n                    !isset($this->owner->action)\n                    || (\n                        $auth instanceof ActionFilter\n                        && !$auth->isActive($this->owner->action)\n                    )\n                )\n            ) {\n                continue;\n            }\n\n            if ($auth instanceof AuthMethod) {\n                $authUser = $auth->user;\n                if ($authUser != null && !$authUser instanceof \\yii\\web\\User) {\n                    throw new InvalidConfigException(get_class($authUser) . ' must implement yii\\web\\User');\n                } elseif ($authUser != null) {\n                    $user = $authUser;\n                }\n\n                $authRequest = $auth->request ?? null;\n                if ($authRequest != null && !$authRequest instanceof \\yii\\web\\Request) {\n                    throw new InvalidConfigException(get_class($authRequest) . ' must implement yii\\web\\Request');\n                } elseif ($authRequest != null) {\n                    $request = $authRequest;\n                }\n\n                $authResponse = $auth->response;\n                if ($authResponse != null && !$authResponse instanceof \\yii\\web\\Response) {\n                    throw new InvalidConfigException(get_class($authResponse) . ' must implement yii\\web\\Response');\n                } elseif ($authResponse != null) {\n                    $response = $authResponse;\n                }\n            }\n\n            $identity = $auth->authenticate($user, $request, $response);\n            if ($identity !== null) {\n                return $identity;\n            }\n        }\n\n        return null;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function challenge($response)\n    {\n        foreach ($this->authMethods as $method) {\n            /** @var AuthInterface $method */\n            $method->challenge($response);\n        }\n    }\n}\n"
  },
  {
    "path": "framework/filters/auth/HttpBasicAuth.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\filters\\auth;\n\nuse yii\\base\\Component;\n\n/**\n * HttpBasicAuth is an action filter that supports the HTTP Basic authentication method.\n *\n * You may use HttpBasicAuth by attaching it as a behavior to a controller or module, like the following:\n *\n * ```\n * public function behaviors()\n * {\n *     return [\n *         'basicAuth' => [\n *             'class' => \\yii\\filters\\auth\\HttpBasicAuth::class,\n *         ],\n *     ];\n * }\n * ```\n *\n * The default implementation of HttpBasicAuth uses the [[\\yii\\web\\User::loginByAccessToken()|loginByAccessToken()]]\n * method of the `user` application component and only passes the user name. This implementation is used\n * for authenticating API clients.\n *\n * If you want to authenticate users using username and password, you should provide the [[auth]] function for example like the following:\n *\n * ```\n * public function behaviors()\n * {\n *     return [\n *         'basicAuth' => [\n *             'class' => \\yii\\filters\\auth\\HttpBasicAuth::class,\n *             'auth' => function ($username, $password) {\n *                 $user = User::find()->where(['username' => $username])->one();\n *                 if ($user && $user->validatePassword($password)) {\n *                     return $user;\n *                 }\n *                 return null;\n *             },\n *         ],\n *     ];\n * }\n * ```\n *\n * > Tip: In case authentication does not work like expected, make sure your web server passes\n * username and password to `$_SERVER['PHP_AUTH_USER']` and `$_SERVER['PHP_AUTH_PW']` variables.\n * If you are using Apache with PHP-CGI, you might need to add this line to your `.htaccess` file:\n * ```\n * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]\n * ```\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Component = Component\n * @extends AuthMethod<T>\n */\nclass HttpBasicAuth extends AuthMethod\n{\n    /**\n     * @var string the HTTP authentication realm\n     */\n    public $realm = 'api';\n    /**\n     * @var callable|null a PHP callable that will authenticate the user with the HTTP basic auth information.\n     * The callable receives a username and a password as its parameters. It should return an identity object\n     * that matches the username and password. Null should be returned if there is no such identity.\n     * The callable will be called only if current user is not authenticated.\n     *\n     * The following code is a typical implementation of this callable:\n     *\n     * ```\n     * function ($username, $password) {\n     *     return \\app\\models\\User::findOne([\n     *         'username' => $username,\n     *         'password' => $password,\n     *     ]);\n     * }\n     * ```\n     *\n     * If this property is not set, the username information will be considered as an access token\n     * while the password information will be ignored. The [[\\yii\\web\\User::loginByAccessToken()]]\n     * method will be called to authenticate and login the user.\n     */\n    public $auth;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function authenticate($user, $request, $response)\n    {\n        list($username, $password) = $request->getAuthCredentials();\n\n        if ($this->auth) {\n            if ($username !== null || $password !== null) {\n                $identity = $user->getIdentity() ?: call_user_func($this->auth, $username, $password);\n\n                if ($identity === null) {\n                    $this->handleFailure($response);\n                } elseif ($user->getIdentity(false) !== $identity) {\n                    $user->login($identity);\n                }\n\n                return $identity;\n            }\n        } elseif ($username !== null) {\n            $identity = $user->loginByAccessToken($username, get_class($this));\n            if ($identity === null) {\n                $this->handleFailure($response);\n            }\n\n            return $identity;\n        }\n\n        return null;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function challenge($response)\n    {\n        $response->getHeaders()->set('WWW-Authenticate', \"Basic realm=\\\"{$this->realm}\\\"\");\n    }\n}\n"
  },
  {
    "path": "framework/filters/auth/HttpBearerAuth.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\filters\\auth;\n\nuse yii\\base\\Component;\n\n/**\n * HttpBearerAuth is an action filter that supports the authentication method based on HTTP Bearer token.\n *\n * You may use HttpBearerAuth by attaching it as a behavior to a controller or module, like the following:\n *\n * ```\n * public function behaviors()\n * {\n *     return [\n *         'bearerAuth' => [\n *             'class' => \\yii\\filters\\auth\\HttpBearerAuth::class,\n *         ],\n *     ];\n * }\n * ```\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Component = Component\n * @extends HttpHeaderAuth<T>\n */\nclass HttpBearerAuth extends HttpHeaderAuth\n{\n    /**\n     * {@inheritdoc}\n     */\n    public $header = 'Authorization';\n    /**\n     * {@inheritdoc}\n     */\n    public $pattern = '/^Bearer\\s+(.*?)$/';\n    /**\n     * @var string the HTTP authentication realm\n     */\n    public $realm = 'api';\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function challenge($response)\n    {\n        $response->getHeaders()->set('WWW-Authenticate', \"Bearer realm=\\\"{$this->realm}\\\"\");\n    }\n}\n"
  },
  {
    "path": "framework/filters/auth/HttpHeaderAuth.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\filters\\auth;\n\nuse yii\\base\\Component;\n\n/**\n * HttpHeaderAuth is an action filter that supports HTTP authentication through HTTP Headers.\n *\n * You may use HttpHeaderAuth by attaching it as a behavior to a controller or module, like the following:\n *\n * ```\n * public function behaviors()\n * {\n *     return [\n *         'basicAuth' => [\n *             'class' => \\yii\\filters\\auth\\HttpHeaderAuth::class,\n *         ],\n *     ];\n * }\n * ```\n *\n * The default implementation of HttpHeaderAuth uses the [[\\yii\\web\\User::loginByAccessToken()|loginByAccessToken()]]\n * method of the `user` application component and passes the value of the `X-Api-Key` header. This implementation is used\n * for authenticating API clients.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Benoît Boure <benoit.boure@gmail.com>\n * @since 2.0.14\n *\n * @template T of Component = Component\n * @extends AuthMethod<T>\n */\nclass HttpHeaderAuth extends AuthMethod\n{\n    /**\n     * @var string the HTTP header name\n     */\n    public $header = 'X-Api-Key';\n    /**\n     * @var string a pattern to use to extract the HTTP authentication value\n     */\n    public $pattern;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function authenticate($user, $request, $response)\n    {\n        $authHeader = $request->getHeaders()->get($this->header);\n\n        if ($authHeader !== null) {\n            if ($this->pattern !== null) {\n                if (preg_match($this->pattern, $authHeader, $matches)) {\n                    $authHeader = $matches[1];\n                } else {\n                    return null;\n                }\n            }\n\n            $identity = $user->loginByAccessToken($authHeader, get_class($this));\n            if ($identity === null) {\n                $this->challenge($response);\n                $this->handleFailure($response);\n            }\n\n            return $identity;\n        }\n\n        return null;\n    }\n}\n"
  },
  {
    "path": "framework/filters/auth/QueryParamAuth.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\filters\\auth;\n\nuse yii\\base\\Component;\n\n/**\n * QueryParamAuth is an action filter that supports the authentication based on the access token passed through a query parameter.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Component = Component\n * @extends AuthMethod<T>\n */\nclass QueryParamAuth extends AuthMethod\n{\n    /**\n     * @var string the parameter name for passing the access token\n     */\n    public $tokenParam = 'access-token';\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function authenticate($user, $request, $response)\n    {\n        $accessToken = $request->get($this->tokenParam);\n        if (is_string($accessToken)) {\n            $identity = $user->loginByAccessToken($accessToken, get_class($this));\n            if ($identity !== null) {\n                return $identity;\n            }\n        }\n        if ($accessToken !== null) {\n            $this->handleFailure($response);\n        }\n\n        return null;\n    }\n}\n"
  },
  {
    "path": "framework/grid/ActionColumn.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\grid;\n\nuse Yii;\nuse yii\\helpers\\Html;\nuse yii\\helpers\\Url;\n\n/**\n * ActionColumn is a column for the [[GridView]] widget that displays buttons for viewing and manipulating the items.\n *\n * To add an ActionColumn to the gridview, add it to the [[GridView::columns|columns]] configuration as follows:\n *\n * ```\n * 'columns' => [\n *     // ...\n *     [\n *         'class' => ActionColumn::class,\n *         // you may configure additional properties here\n *     ],\n * ]\n * ```\n *\n * For more details and usage information on ActionColumn, see the [guide article on data widgets](guide:output-data-widgets).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass ActionColumn extends Column\n{\n    /**\n     * {@inheritdoc}\n     */\n    public $headerOptions = ['class' => 'action-column'];\n    /**\n     * @var string|null the ID of the controller that should handle the actions specified here.\n     * If not set, it will use the currently active controller. This property is mainly used by\n     * [[urlCreator]] to create URLs for different actions. The value of this property will be prefixed\n     * to each action name to form the route of the action.\n     */\n    public $controller;\n    /**\n     * @var string the template used for composing each cell in the action column.\n     * Tokens enclosed within curly brackets are treated as controller action IDs (also called *button names*\n     * in the context of action column). They will be replaced by the corresponding button rendering callbacks\n     * specified in [[buttons]]. For example, the token `{view}` will be replaced by the result of\n     * the callback `buttons['view']`. If a callback cannot be found, the token will be replaced with an empty string.\n     *\n     * As an example, to only have the view, and update button you can add the ActionColumn to your GridView columns as follows:\n     *\n     * ```\n     * ['class' => 'yii\\grid\\ActionColumn', 'template' => '{view} {update}'],\n     * ```\n     *\n     * @see buttons\n     */\n    public $template = '{view} {update} {delete}';\n    /**\n     * @var array button rendering callbacks. The array keys are the button names (without curly brackets),\n     * and the values are the corresponding button rendering callbacks. The callbacks should use the following\n     * signature:\n     *\n     * ```\n     * function ($url, $model, $key) {\n     *     // return the button HTML code\n     * }\n     * ```\n     *\n     * where `$url` is the URL that the column creates for the button, `$model` is the model object\n     * being rendered for the current row, and `$key` is the key of the model in the data provider array.\n     *\n     * You can add further conditions to the button, for example only display it, when the model is\n     * editable (here assuming you have a status field that indicates that):\n     *\n     * ```\n     * [\n     *     'update' => function ($url, $model, $key) {\n     *         return $model->status === 'editable' ? Html::a('Update', $url) : '';\n     *     },\n     * ],\n     * ```\n     */\n    public $buttons = [];\n    /**\n     * @var array button icons. The array keys are the icon names and the values the corresponding html:\n     * ```\n     * [\n     *     'eye-open' => '<svg ...></svg>',\n     *     'pencil' => Html::tag('span', '', ['class' => 'glyphicon glyphicon-pencil'])\n     * ]\n     * ```\n     * Defaults to FontAwesome 5 free svg icons.\n     * @since 2.0.42\n     * @see https://fontawesome.com\n     */\n    public $icons = [\n        'eye-open' => '<svg aria-hidden=\"true\" style=\"display:inline-block;font-size:inherit;height:1em;overflow:visible;vertical-align:-.125em;width:1.125em\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 576 512\"><path fill=\"currentColor\" d=\"M573 241C518 136 411 64 288 64S58 136 3 241a32 32 0 000 30c55 105 162 177 285 177s230-72 285-177a32 32 0 000-30zM288 400a144 144 0 11144-144 144 144 0 01-144 144zm0-240a95 95 0 00-25 4 48 48 0 01-67 67 96 96 0 1092-71z\"/></svg>',\n        'pencil' => '<svg aria-hidden=\"true\" style=\"display:inline-block;font-size:inherit;height:1em;overflow:visible;vertical-align:-.125em;width:1em\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path fill=\"currentColor\" d=\"M498 142l-46 46c-5 5-13 5-17 0L324 77c-5-5-5-12 0-17l46-46c19-19 49-19 68 0l60 60c19 19 19 49 0 68zm-214-42L22 362 0 484c-3 16 12 30 28 28l122-22 262-262c5-5 5-13 0-17L301 100c-4-5-12-5-17 0zM124 340c-5-6-5-14 0-20l154-154c6-5 14-5 20 0s5 14 0 20L144 340c-6 5-14 5-20 0zm-36 84h48v36l-64 12-32-31 12-65h36v48z\"/></svg>',\n        'trash' => '<svg aria-hidden=\"true\" style=\"display:inline-block;font-size:inherit;height:1em;overflow:visible;vertical-align:-.125em;width:.875em\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"><path fill=\"currentColor\" d=\"M32 464a48 48 0 0048 48h288a48 48 0 0048-48V128H32zm272-256a16 16 0 0132 0v224a16 16 0 01-32 0zm-96 0a16 16 0 0132 0v224a16 16 0 01-32 0zm-96 0a16 16 0 0132 0v224a16 16 0 01-32 0zM432 32H312l-9-19a24 24 0 00-22-13H167a24 24 0 00-22 13l-9 19H16A16 16 0 000 48v32a16 16 0 0016 16h416a16 16 0 0016-16V48a16 16 0 00-16-16z\"/></svg>'\n    ];\n    /** @var array visibility conditions for each button. The array keys are the button names (without curly brackets),\n     * and the values are the boolean true/false or the anonymous function. When the button name is not specified in\n     * this array it will be shown by default.\n     * The callbacks must use the following signature:\n     *\n     * ```\n     * function ($model, $key, $index) {\n     *     return $model->status === 'editable';\n     * }\n     * ```\n     *\n     * Or you can pass a boolean value:\n     *\n     * ```\n     * [\n     *     'update' => \\Yii::$app->user->can('update'),\n     * ],\n     * ```\n     * @since 2.0.7\n     */\n    public $visibleButtons = [];\n    /**\n     * @var callable|null a callback that creates a button URL using the specified model information.\n     * The signature of the callback should be the same as that of [[createUrl()]]\n     * Since 2.0.10 it can accept additional parameter, which refers to the column instance itself:\n     *\n     * ```\n     * function (string $action, mixed $model, mixed $key, integer $index, ActionColumn $this) {\n     *     //return string;\n     * }\n     * ```\n     *\n     * If this property is not set, button URLs will be created using [[createUrl()]].\n     */\n    public $urlCreator;\n    /**\n     * @var array html options to be applied to the [[initDefaultButton()|default button]].\n     * @since 2.0.4\n     */\n    public $buttonOptions = [];\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        $this->initDefaultButtons();\n    }\n\n    /**\n     * Initializes the default button rendering callbacks.\n     */\n    protected function initDefaultButtons()\n    {\n        $this->initDefaultButton('view', 'eye-open');\n        $this->initDefaultButton('update', 'pencil');\n        $this->initDefaultButton('delete', 'trash', [\n            'data-confirm' => Yii::t('yii', 'Are you sure you want to delete this item?'),\n            'data-method' => 'post',\n        ]);\n    }\n\n    /**\n     * Initializes the default button rendering callback for single button.\n     * @param string $name Button name as it's written in template\n     * @param string $iconName The part of Bootstrap glyphicon class that makes it unique\n     * @param array $additionalOptions Array of additional options\n     * @since 2.0.11\n     */\n    protected function initDefaultButton($name, $iconName, $additionalOptions = [])\n    {\n        if (!isset($this->buttons[$name]) && strpos($this->template, '{' . $name . '}') !== false) {\n            $this->buttons[$name] = function ($url, $model, $key) use ($name, $iconName, $additionalOptions) {\n                switch ($name) {\n                    case 'view':\n                        $title = Yii::t('yii', 'View');\n                        break;\n                    case 'update':\n                        $title = Yii::t('yii', 'Update');\n                        break;\n                    case 'delete':\n                        $title = Yii::t('yii', 'Delete');\n                        break;\n                    default:\n                        $title = ucfirst($name);\n                }\n                $options = array_merge([\n                    'title' => $title,\n                    'aria-label' => $title,\n                    'data-pjax' => '0',\n                ], $additionalOptions, $this->buttonOptions);\n                $icon = isset($this->icons[$iconName])\n                    ? $this->icons[$iconName]\n                    : Html::tag('span', '', ['class' => \"glyphicon glyphicon-$iconName\"]);\n                return Html::a($icon, $url, $options);\n            };\n        }\n    }\n\n    /**\n     * Creates a URL for the given action and model.\n     * This method is called for each button and each row.\n     * @param string $action the button name (or action ID)\n     * @param \\yii\\db\\ActiveRecordInterface $model the data model\n     * @param mixed $key the key associated with the data model\n     * @param int $index the current row index\n     * @return string the created URL\n     */\n    public function createUrl($action, $model, $key, $index)\n    {\n        if (is_callable($this->urlCreator)) {\n            return call_user_func($this->urlCreator, $action, $model, $key, $index, $this);\n        }\n\n        $params = is_array($key) ? $key : ['id' => (string) $key];\n        $params[0] = $this->controller ? $this->controller . '/' . $action : $action;\n\n        return Url::toRoute($params);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function renderDataCellContent($model, $key, $index)\n    {\n        return preg_replace_callback('/\\\\{([\\w\\-\\/]+)\\\\}/', function ($matches) use ($model, $key, $index) {\n            $name = $matches[1];\n\n            if (isset($this->visibleButtons[$name])) {\n                $isVisible = $this->visibleButtons[$name] instanceof \\Closure\n                    ? call_user_func($this->visibleButtons[$name], $model, $key, $index)\n                    : $this->visibleButtons[$name];\n            } else {\n                $isVisible = true;\n            }\n\n            if ($isVisible && isset($this->buttons[$name])) {\n                $url = $this->createUrl($name, $model, $key, $index);\n                return call_user_func($this->buttons[$name], $url, $model, $key);\n            }\n\n            return '';\n        }, $this->template);\n    }\n}\n"
  },
  {
    "path": "framework/grid/CheckboxColumn.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\grid;\n\nuse Closure;\nuse yii\\base\\InvalidConfigException;\nuse yii\\helpers\\Html;\nuse yii\\helpers\\Json;\n\n/**\n * CheckboxColumn displays a column of checkboxes in a grid view.\n *\n * To add a CheckboxColumn to the [[GridView]], add it to the [[GridView::columns|columns]] configuration as follows:\n *\n * ```\n * 'columns' => [\n *     // ...\n *     [\n *         'class' => 'yii\\grid\\CheckboxColumn',\n *         // you may configure additional properties here\n *     ],\n * ]\n * ```\n *\n * Users may click on the checkboxes to select rows of the grid. The selected rows may be\n * obtained by calling the following JavaScript code:\n *\n * ```\n * var keys = $('#grid').yiiGridView('getSelectedRows');\n * // keys is an array consisting of the keys associated with the selected rows\n * ```\n *\n * For more details and usage information on CheckboxColumn, see the [guide article on data widgets](guide:output-data-widgets).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass CheckboxColumn extends Column\n{\n    /**\n     * @var string the name of the input checkbox input fields. This will be appended with `[]` to ensure it is an array.\n     */\n    public $name = 'selection';\n    /**\n     * @var array|\\Closure the HTML attributes for checkboxes. This can either be an array of\n     * attributes or an anonymous function ([[Closure]]) that returns such an array.\n     * The signature of the function should be the following: `function ($model, $key, $index, $column)`.\n     * Where `$model`, `$key`, and `$index` refer to the model, key and index of the row currently being rendered\n     * and `$column` is a reference to the [[CheckboxColumn]] object.\n     * A function may be used to assign different attributes to different rows based on the data in that row.\n     * Specifically if you want to set a different value for the checkbox\n     * you can use this option in the following way (in this example using the `name` attribute of the model):\n     *\n     * ```\n     * 'checkboxOptions' => function ($model, $key, $index, $column) {\n     *     return ['value' => $model->name];\n     * }\n     * ```\n     *\n     * @see \\yii\\helpers\\Html::renderTagAttributes() for details on how attributes are being rendered.\n     */\n    public $checkboxOptions = [];\n    /**\n     * @var bool whether it is possible to select multiple rows. Defaults to `true`.\n     */\n    public $multiple = true;\n    /**\n     * @var string the css class that will be used to find the checkboxes.\n     * @since 2.0.9\n     */\n    public $cssClass;\n\n\n    /**\n     * {@inheritdoc}\n     * @throws \\yii\\base\\InvalidConfigException if [[name]] is not set.\n     */\n    public function init()\n    {\n        parent::init();\n        if (empty($this->name)) {\n            throw new InvalidConfigException('The \"name\" property must be set.');\n        }\n        if (substr_compare($this->name, '[]', -2, 2)) {\n            $this->name .= '[]';\n        }\n\n        $this->registerClientScript();\n    }\n\n    /**\n     * Renders the header cell content.\n     * The default implementation simply renders [[header]].\n     * This method may be overridden to customize the rendering of the header cell.\n     * @return string the rendering result\n     */\n    protected function renderHeaderCellContent()\n    {\n        if ($this->header !== null || !$this->multiple) {\n            return parent::renderHeaderCellContent();\n        }\n\n        return Html::checkbox($this->getHeaderCheckBoxName(), false, ['class' => 'select-on-check-all']);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function renderDataCellContent($model, $key, $index)\n    {\n        if ($this->content !== null) {\n            return parent::renderDataCellContent($model, $key, $index);\n        }\n\n        if ($this->checkboxOptions instanceof Closure) {\n            $options = call_user_func($this->checkboxOptions, $model, $key, $index, $this);\n        } else {\n            $options = $this->checkboxOptions;\n        }\n\n        if (!isset($options['value'])) {\n            $options['value'] = is_array($key) ? Json::encode($key) : $key;\n        }\n\n        if ($this->cssClass !== null) {\n            Html::addCssClass($options, $this->cssClass);\n        }\n\n        return Html::checkbox($this->name, !empty($options['checked']), $options);\n    }\n\n    /**\n     * Returns header checkbox name.\n     * @return string header checkbox name\n     * @since 2.0.8\n     */\n    protected function getHeaderCheckBoxName()\n    {\n        $name = $this->name;\n        if (substr_compare($name, '[]', -2, 2) === 0) {\n            $name = substr($name, 0, -2);\n        }\n        if (substr_compare($name, ']', -1, 1) === 0) {\n            $name = substr($name, 0, -1) . '_all]';\n        } else {\n            $name .= '_all';\n        }\n\n        return $name;\n    }\n\n    /**\n     * Registers the needed JavaScript.\n     * @since 2.0.8\n     */\n    public function registerClientScript()\n    {\n        $id = $this->grid->options['id'];\n        $options = Json::encode([\n            'name' => $this->name,\n            'class' => $this->cssClass,\n            'multiple' => $this->multiple,\n            'checkAll' => $this->grid->showHeader ? $this->getHeaderCheckBoxName() : null,\n        ]);\n        $this->grid->getView()->registerJs(\"jQuery('#$id').yiiGridView('setSelectionColumn', $options);\");\n    }\n}\n"
  },
  {
    "path": "framework/grid/Column.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\grid;\n\nuse Closure;\nuse yii\\base\\BaseObject;\nuse yii\\helpers\\Html;\n\n/**\n * Column is the base class of all [[GridView]] column classes.\n *\n * For more details and usage information on Column, see the [guide article on data widgets](guide:output-data-widgets).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Column extends BaseObject\n{\n    /**\n     * @var GridView the grid view object that owns this column.\n     */\n    public $grid;\n    /**\n     * @var string|null the header cell content. Note that it will not be HTML-encoded.\n     */\n    public $header;\n    /**\n     * @var string the footer cell content. Note that it will not be HTML-encoded.\n     */\n    public $footer;\n    /**\n     * @var callable This is a callable that will be used to generate the content of each cell.\n     * The signature of the function should be the following: `function ($model, $key, $index, $column)`.\n     * Where `$model`, `$key`, and `$index` refer to the model, key and index of the row currently being rendered\n     * and `$column` is a reference to the [[Column]] object.\n     */\n    public $content;\n    /**\n     * @var bool whether this column is visible. Defaults to true.\n     */\n    public $visible = true;\n    /**\n     * @var array the HTML attributes for the column group tag.\n     * @see \\yii\\helpers\\Html::renderTagAttributes() for details on how attributes are being rendered.\n     */\n    public $options = [];\n    /**\n     * @var array the HTML attributes for the header cell tag.\n     * @see \\yii\\helpers\\Html::renderTagAttributes() for details on how attributes are being rendered.\n     */\n    public $headerOptions = [];\n    /**\n     * @var array|\\Closure the HTML attributes for the data cell tag. This can either be an array of\n     * attributes or an anonymous function ([[Closure]]) that returns such an array.\n     * The signature of the function should be the following: `function ($model, $key, $index, $column)`.\n     * Where `$model`, `$key`, and `$index` refer to the model, key and index of the row currently being rendered\n     * and `$column` is a reference to the [[Column]] object.\n     * A function may be used to assign different attributes to different rows based on the data in that row.\n     *\n     * @see \\yii\\helpers\\Html::renderTagAttributes() for details on how attributes are being rendered.\n     */\n    public $contentOptions = [];\n    /**\n     * @var array the HTML attributes for the footer cell tag.\n     * @see \\yii\\helpers\\Html::renderTagAttributes() for details on how attributes are being rendered.\n     */\n    public $footerOptions = [];\n    /**\n     * @var array the HTML attributes for the filter cell tag.\n     * @see \\yii\\helpers\\Html::renderTagAttributes() for details on how attributes are being rendered.\n     */\n    public $filterOptions = [];\n\n\n    /**\n     * Renders the header cell.\n     */\n    public function renderHeaderCell()\n    {\n        return Html::tag('th', $this->renderHeaderCellContent(), $this->headerOptions);\n    }\n\n    /**\n     * Renders the footer cell.\n     */\n    public function renderFooterCell()\n    {\n        return Html::tag('td', $this->renderFooterCellContent(), $this->footerOptions);\n    }\n\n    /**\n     * Renders a data cell.\n     * @param mixed $model the data model being rendered\n     * @param mixed $key the key associated with the data model\n     * @param int $index the zero-based index of the data item among the item array returned by [[GridView::dataProvider]].\n     * @return string the rendering result\n     */\n    public function renderDataCell($model, $key, $index)\n    {\n        if ($this->contentOptions instanceof Closure) {\n            $options = call_user_func($this->contentOptions, $model, $key, $index, $this);\n        } else {\n            $options = $this->contentOptions;\n        }\n\n        return Html::tag('td', $this->renderDataCellContent($model, $key, $index), $options);\n    }\n\n    /**\n     * Renders the filter cell.\n     */\n    public function renderFilterCell()\n    {\n        return Html::tag('td', $this->renderFilterCellContent(), $this->filterOptions);\n    }\n\n    /**\n     * Renders the header cell content.\n     * The default implementation simply renders [[header]].\n     * This method may be overridden to customize the rendering of the header cell.\n     * @return string the rendering result\n     */\n    protected function renderHeaderCellContent()\n    {\n        return $this->header !== null && trim($this->header) !== '' ? $this->header : $this->getHeaderCellLabel();\n    }\n\n    /**\n     * Returns header cell label.\n     * This method may be overridden to customize the label of the header cell.\n     * @return string label\n     * @since 2.0.8\n     */\n    protected function getHeaderCellLabel()\n    {\n        return $this->grid->emptyCell;\n    }\n\n    /**\n     * Renders the footer cell content.\n     * The default implementation simply renders [[footer]].\n     * This method may be overridden to customize the rendering of the footer cell.\n     * @return string the rendering result\n     */\n    protected function renderFooterCellContent()\n    {\n        return $this->footer !== null && trim($this->footer) !== '' ? $this->footer : $this->grid->emptyCell;\n    }\n\n    /**\n     * Renders the data cell content.\n     * @param mixed $model the data model\n     * @param mixed $key the key associated with the data model\n     * @param int $index the zero-based index of the data model among the models array returned by [[GridView::dataProvider]].\n     * @return string the rendering result\n     */\n    protected function renderDataCellContent($model, $key, $index)\n    {\n        if ($this->content !== null) {\n            return call_user_func($this->content, $model, $key, $index, $this);\n        }\n\n        return $this->grid->emptyCell;\n    }\n\n    /**\n     * Renders the filter cell content.\n     * The default implementation simply renders a space.\n     * This method may be overridden to customize the rendering of the filter cell (if any).\n     * @return string the rendering result\n     */\n    protected function renderFilterCellContent()\n    {\n        return $this->grid->emptyCell;\n    }\n}\n"
  },
  {
    "path": "framework/grid/DataColumn.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\grid;\n\nuse Closure;\nuse yii\\base\\Model;\nuse yii\\data\\ActiveDataProvider;\nuse yii\\data\\ArrayDataProvider;\nuse yii\\db\\ActiveQueryInterface;\nuse yii\\helpers\\ArrayHelper;\nuse yii\\helpers\\Html;\nuse yii\\helpers\\Inflector;\n\n/**\n * DataColumn is the default column type for the [[GridView]] widget.\n *\n * It is used to show data columns and allows [[enableSorting|sorting]] and [[filter|filtering]] them.\n *\n * A simple data column definition refers to an attribute in the data model of the\n * GridView's data provider. The name of the attribute is specified by [[attribute]].\n *\n * By setting [[value]] and [[label]], the header and cell content can be customized.\n *\n * A data column differentiates between the [[getDataCellValue|data cell value]] and the\n * [[renderDataCellContent|data cell content]]. The cell value is an un-formatted value that\n * may be used for calculation, while the actual cell content is a [[format|formatted]] version of that\n * value which may contain HTML markup.\n *\n * For more details and usage information on DataColumn, see the [guide article on data widgets](guide:output-data-widgets).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass DataColumn extends Column\n{\n    /**\n     * @var string the attribute name associated with this column. When neither [[content]] nor [[value]]\n     * is specified, the value of the specified attribute will be retrieved from each data model and displayed.\n     *\n     * Also, if [[label]] is not specified, the label associated with the attribute will be displayed.\n     */\n    public $attribute;\n    /**\n     * @var string|null label to be displayed in the [[header|header cell]] and also to be used as the sorting\n     * link label when sorting is enabled for this column.\n     * If it is not set and the models provided by the GridViews data provider are instances\n     * of [[\\yii\\db\\ActiveRecord]], the label will be determined using [[\\yii\\db\\ActiveRecord::getAttributeLabel()]].\n     * Otherwise [[\\yii\\helpers\\Inflector::camel2words()]] will be used to get a label.\n     */\n    public $label;\n    /**\n     * @var bool whether the header label should be HTML-encoded.\n     * @see label\n     * @since 2.0.1\n     */\n    public $encodeLabel = true;\n    /**\n     * @var string|Closure|null an anonymous function or a string that is used to determine the value to display in the current column.\n     *\n     * If this is an anonymous function, it will be called for each row and the return value will be used as the value to\n     * display for every data model. The signature of this function should be: `function ($model, $key, $index, $column)`.\n     * Where `$model`, `$key`, and `$index` refer to the model, key and index of the row currently being rendered\n     * and `$column` is a reference to the [[DataColumn]] object.\n     *\n     * You may also set this property to a string representing the attribute name to be displayed in this column.\n     * This can be used when the attribute to be displayed is different from the [[attribute]] that is used for\n     * sorting and filtering.\n     *\n     * If this is not set, `$model[$attribute]` will be used to obtain the value, where `$attribute` is the value of [[attribute]].\n     */\n    public $value;\n    /**\n     * @var string|array|Closure in which format should the value of each data model be displayed as (e.g. `\"raw\"`, `\"text\"`, `\"html\"`,\n     * `['date', 'php:Y-m-d']`). Supported formats are determined by the [[GridView::formatter|formatter]] used by\n     * the [[GridView]]. Default format is \"text\" which will format the value as an HTML-encoded plain text when\n     * [[\\yii\\i18n\\Formatter]] is used as the [[GridView::$formatter|formatter]] of the GridView.\n     * @see \\yii\\i18n\\Formatter::format()\n     */\n    public $format = 'text';\n    /**\n     * @var bool whether to allow sorting by this column. If true and [[attribute]] is found in\n     * the sort definition of [[GridView::dataProvider]], then the header cell of this column\n     * will contain a link that may trigger the sorting when being clicked.\n     */\n    public $enableSorting = true;\n    /**\n     * @var array the HTML attributes for the link tag in the header cell\n     * generated by [[\\yii\\data\\Sort::link]] when sorting is enabled for this column.\n     * @see \\yii\\helpers\\Html::renderTagAttributes() for details on how attributes are being rendered.\n     */\n    public $sortLinkOptions = [];\n    /**\n     * @var string|array|null|false the HTML code representing a filter input (e.g. a text field, a dropdown list)\n     * that is used for this data column. This property is effective only when [[GridView::filterModel]] is set.\n     *\n     * - If this property is not set, a text field will be generated as the filter input with attributes defined\n     *   with [[filterInputOptions]]. See [[\\yii\\helpers\\BaseHtml::activeInput]] for details on how an active\n     *   input tag is generated.\n     * - If this property is an array, a dropdown list will be generated that uses this property value as\n     *   the list options.\n     * - If you don't want a filter for this data column, set this value to be false.\n     */\n    public $filter;\n    /**\n     * @var array the HTML attributes for the filter input fields. This property is used in combination with\n     * the [[filter]] property. When [[filter]] is not set or is an array, this property will be used to\n     * render the HTML attributes for the generated filter input fields.\n     *\n     * Empty `id` in the default value ensures that id would not be obtained from the model attribute thus\n     * providing better performance.\n     *\n     * @see \\yii\\helpers\\Html::renderTagAttributes() for details on how attributes are being rendered.\n     */\n    public $filterInputOptions = ['class' => 'form-control', 'id' => null];\n    /**\n     * @var string|null the attribute name of the [[GridView::filterModel]] associated with this column. If not set,\n     * will have the same value as [[attribute]].\n     * @since 2.0.41\n     */\n    public $filterAttribute;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        if ($this->filterAttribute === null) {\n            $this->filterAttribute = $this->attribute;\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function renderHeaderCellContent()\n    {\n        if ($this->header !== null || $this->label === null && $this->attribute === null) {\n            return parent::renderHeaderCellContent();\n        }\n\n        $label = $this->getHeaderCellLabel();\n        if ($this->encodeLabel) {\n            $label = Html::encode($label);\n        }\n\n        if (\n            $this->attribute !== null && $this->enableSorting &&\n            ($sort = $this->grid->dataProvider->getSort()) !== false && $sort->hasAttribute($this->attribute)\n        ) {\n            return $sort->link($this->attribute, array_merge($this->sortLinkOptions, ['label' => $label]));\n        }\n\n        return $label;\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.8\n     */\n    protected function getHeaderCellLabel()\n    {\n        $provider = $this->grid->dataProvider;\n\n        if ($this->label === null) {\n            if ($this->attribute === null) {\n                $label = '';\n            } elseif ($provider instanceof ActiveDataProvider && $provider->query instanceof ActiveQueryInterface) {\n                /** @var Model $modelClass */\n                $modelClass = $provider->query->modelClass;\n                $model = $modelClass::instance();\n                $label = $model->getAttributeLabel($this->attribute);\n            } elseif ($provider instanceof ArrayDataProvider && $provider->modelClass !== null) {\n                /** @var Model $modelClass */\n                $modelClass = $provider->modelClass;\n                $model = $modelClass::instance();\n                $label = $model->getAttributeLabel($this->attribute);\n            } elseif ($this->grid->filterModel !== null && $this->grid->filterModel instanceof Model) {\n                $label = $this->grid->filterModel->getAttributeLabel($this->filterAttribute);\n            } else {\n                $models = $provider->getModels();\n                if (($model = reset($models)) instanceof Model) {\n                    /** @var Model $model */\n                    $label = $model->getAttributeLabel($this->attribute);\n                } else {\n                    $label = Inflector::camel2words($this->attribute);\n                }\n            }\n        } else {\n            $label = $this->label;\n        }\n\n        return $label;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function renderFilterCellContent()\n    {\n        if (is_string($this->filter)) {\n            return $this->filter;\n        }\n\n        $model = $this->grid->filterModel;\n\n        if ($this->filter !== false && $model instanceof Model && $this->filterAttribute !== null && $model->isAttributeActive($this->filterAttribute)) {\n            if ($model->hasErrors($this->filterAttribute)) {\n                Html::addCssClass($this->filterOptions, 'has-error');\n                $error = ' ' . Html::error($model, $this->filterAttribute, $this->grid->filterErrorOptions);\n            } else {\n                $error = '';\n            }\n            if (is_array($this->filter)) {\n                $options = array_merge(['prompt' => '', 'strict' => true], $this->filterInputOptions);\n                return Html::activeDropDownList($model, $this->filterAttribute, $this->filter, $options) . $error;\n            } elseif ($this->format === 'boolean') {\n                $options = array_merge(['prompt' => '', 'strict' => true], $this->filterInputOptions);\n                return Html::activeDropDownList($model, $this->filterAttribute, [\n                    1 => $this->grid->formatter->booleanFormat[1],\n                    0 => $this->grid->formatter->booleanFormat[0],\n                ], $options) . $error;\n            }\n            $options = array_merge(['maxlength' => true], $this->filterInputOptions);\n\n            return Html::activeTextInput($model, $this->filterAttribute, $options) . $error;\n        }\n\n        return parent::renderFilterCellContent();\n    }\n\n    /**\n     * Returns the data cell value.\n     * @param mixed $model the data model\n     * @param mixed $key the key associated with the data model\n     * @param int $index the zero-based index of the data model among the models array returned by [[GridView::dataProvider]].\n     * @return string|null the data cell value\n     */\n    public function getDataCellValue($model, $key, $index)\n    {\n        if ($this->value !== null) {\n            if (is_string($this->value)) {\n                return ArrayHelper::getValue($model, $this->value);\n            }\n\n            return call_user_func($this->value, $model, $key, $index, $this);\n        } elseif ($this->attribute !== null) {\n            return ArrayHelper::getValue($model, $this->attribute);\n        }\n\n        return null;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function renderDataCellContent($model, $key, $index)\n    {\n        if ($this->content === null) {\n            return $this->grid->formatter->format($this->getDataCellValue($model, $key, $index), $this->format);\n        }\n\n        return parent::renderDataCellContent($model, $key, $index);\n    }\n}\n"
  },
  {
    "path": "framework/grid/GridView.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\grid;\n\nuse Closure;\nuse Yii;\nuse yii\\base\\InvalidConfigException;\nuse yii\\base\\Model;\nuse yii\\helpers\\Html;\nuse yii\\helpers\\Json;\nuse yii\\helpers\\Url;\nuse yii\\i18n\\Formatter;\nuse yii\\widgets\\BaseListView;\n\n/**\n * The GridView widget is used to display data in a grid.\n *\n * It provides features like [[sorter|sorting]], [[pager|paging]] and also [[filterModel|filtering]] the data.\n *\n * A basic usage looks like the following:\n *\n * ```\n * <?= GridView::widget([\n *     'dataProvider' => $dataProvider,\n *     'columns' => [\n *         'id',\n *         'name',\n *         'created_at:datetime',\n *         // ...\n *     ],\n * ]) ?>\n * ```\n *\n * The columns of the grid table are configured in terms of [[Column]] classes,\n * which are configured via [[columns]].\n *\n * The look and feel of a grid view can be customized using the large amount of properties.\n *\n * For more details and usage information on GridView, see the [guide article on data widgets](guide:output-data-widgets).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass GridView extends BaseListView\n{\n    public const FILTER_POS_HEADER = 'header';\n    public const FILTER_POS_FOOTER = 'footer';\n    public const FILTER_POS_BODY = 'body';\n    /**\n     * @var string the default data column class if the class name is not explicitly specified when configuring a data column.\n     * Defaults to 'yii\\grid\\DataColumn'.\n     */\n    public $dataColumnClass;\n    /**\n     * @var string the caption of the grid table\n     * @see captionOptions\n     */\n    public $caption;\n    /**\n     * @var array the HTML attributes for the caption element.\n     * @see \\yii\\helpers\\Html::renderTagAttributes() for details on how attributes are being rendered.\n     * @see caption\n     */\n    public $captionOptions = [];\n    /**\n     * @var array the HTML attributes for the grid table element.\n     * @see \\yii\\helpers\\Html::renderTagAttributes() for details on how attributes are being rendered.\n     */\n    public $tableOptions = ['class' => 'table table-striped table-bordered'];\n    /**\n     * @var array the HTML attributes for the container tag of the grid view.\n     * The \"tag\" element specifies the tag name of the container element and defaults to \"div\".\n     * @see \\yii\\helpers\\Html::renderTagAttributes() for details on how attributes are being rendered.\n     */\n    public $options = ['class' => 'grid-view'];\n    /**\n     * @var array the HTML attributes for the table header row.\n     * @see \\yii\\helpers\\Html::renderTagAttributes() for details on how attributes are being rendered.\n     */\n    public $headerRowOptions = [];\n    /**\n     * @var array the HTML attributes for the table footer row.\n     * @see \\yii\\helpers\\Html::renderTagAttributes() for details on how attributes are being rendered.\n     */\n    public $footerRowOptions = [];\n    /**\n     * @var array|Closure the HTML attributes for the table body rows. This can be either an array\n     * specifying the common HTML attributes for all body rows, or an anonymous function that\n     * returns an array of the HTML attributes. The anonymous function will be called once for every\n     * data model returned by [[dataProvider]]. It should have the following signature:\n     *\n     * ```\n     * function ($model, $key, $index, $grid)\n     * ```\n     *\n     * - `$model`: the current data model being rendered\n     * - `$key`: the key value associated with the current data model\n     * - `$index`: the zero-based index of the data model in the model array returned by [[dataProvider]]\n     * - `$grid`: the GridView object\n     *\n     * @see \\yii\\helpers\\Html::renderTagAttributes() for details on how attributes are being rendered.\n     */\n    public $rowOptions = [];\n    /**\n     * @var Closure an anonymous function that is called once BEFORE rendering each data model.\n     * It should have the similar signature as [[rowOptions]]. The return result of the function\n     * will be rendered directly.\n     */\n    public $beforeRow;\n    /**\n     * @var Closure an anonymous function that is called once AFTER rendering each data model.\n     * It should have the similar signature as [[rowOptions]]. The return result of the function\n     * will be rendered directly.\n     */\n    public $afterRow;\n    /**\n     * @var bool whether to show the header section of the grid table.\n     */\n    public $showHeader = true;\n    /**\n     * @var bool whether to show the footer section of the grid table.\n     */\n    public $showFooter = false;\n    /**\n     * @var bool whether to place footer after body in DOM if $showFooter is true\n     * @since 2.0.14\n     */\n    public $placeFooterAfterBody = false;\n    /**\n     * @var bool whether to show the grid view if [[dataProvider]] returns no data.\n     */\n    public $showOnEmpty = true;\n    /**\n     * @var array|Formatter|null the formatter used to format model attribute values into displayable texts.\n     * This can be either an instance of [[Formatter]] or an configuration array for creating the [[Formatter]]\n     * instance. If this property is not set, the \"formatter\" application component will be used.\n     */\n    public $formatter;\n    /**\n     * @var array grid column configuration. Each array element represents the configuration\n     * for one particular grid column. For example,\n     *\n     * ```\n     * [\n     *     ['class' => SerialColumn::class],\n     *     [\n     *         'class' => DataColumn::class, // this line is optional\n     *         'attribute' => 'name',\n     *         'format' => 'text',\n     *         'label' => 'Name',\n     *     ],\n     *     ['class' => CheckboxColumn::class],\n     * ]\n     * ```\n     *\n     * If a column is of class [[DataColumn]], the \"class\" element can be omitted.\n     *\n     * As a shortcut format, a string may be used to specify the configuration of a data column\n     * which only contains [[DataColumn::attribute|attribute]], [[DataColumn::format|format]],\n     * and/or [[DataColumn::label|label]] options: `\"attribute:format:label\"`.\n     * For example, the above \"name\" column can also be specified as: `\"name:text:Name\"`.\n     * Both \"format\" and \"label\" are optional. They will take default values if absent.\n     *\n     * Using the shortcut format the configuration for columns in simple cases would look like this:\n     *\n     * ```\n     * [\n     *     'id',\n     *     'amount:currency:Total Amount',\n     *     'created_at:datetime',\n     * ]\n     * ```\n     *\n     * When using a [[dataProvider]] with active records, you can also display values from related records,\n     * e.g. the `name` attribute of the `author` relation:\n     *\n     * ```\n     * // shortcut syntax\n     * 'author.name',\n     * // full syntax\n     * [\n     *     'attribute' => 'author.name',\n     *     // ...\n     * ]\n     * ```\n     */\n    public $columns = [];\n    /**\n     * @var string the HTML display when the content of a cell is empty.\n     * This property is used to render cells that have no defined content,\n     * e.g. empty footer or filter cells.\n     *\n     * Note that this is not used by the [[DataColumn]] if a data item is `null`. In that case\n     * the [[\\yii\\i18n\\Formatter::nullDisplay|nullDisplay]] property of the [[formatter]] will\n     * be used to indicate an empty data value.\n     */\n    public $emptyCell = '&nbsp;';\n    /**\n     * @var \\yii\\base\\Model|null the model that keeps the user-entered filter data. When this property is set,\n     * the grid view will enable column-based filtering. Each data column by default will display a text field\n     * at the top that users can fill in to filter the data.\n     *\n     * Note that in order to show an input field for filtering, a column must have its [[DataColumn::attribute]]\n     * property set and the attribute should be active in the current scenario of $filterModel or have\n     * [[DataColumn::filter]] set as the HTML code for the input field.\n     *\n     * When this property is not set (null) the filtering feature is disabled.\n     */\n    public $filterModel;\n    /**\n     * @var string|array|null the URL for returning the filtering result. [[Url::to()]] will be called to\n     * normalize the URL. If not set, the current controller action will be used.\n     * When the user makes change to any filter input, the current filtering inputs will be appended\n     * as GET parameters to this URL.\n     */\n    public $filterUrl;\n    /**\n     * @var string|\\Closure(string, string): string jQuery selector for selecting filter input fields.\n     * If this is a Closure, it gets the widget ID and the filter row ID as parameters and is expected to return the selector.\n     *\n     * By default, this selector is added to the default selector '#$id input, #$id select' (where $id is the filter row ID).\n     * If [[overrideFilterSelector]] is set, this selector is used instead of the default selector.\n     */\n    public $filterSelector;\n    /**\n     * @var bool If set, [[filterSelector]] overrides the default selector of '#$id input, #$id select' (where $id is the filter row ID) rather than being added to it.\n     */\n    public $overrideFilterSelector = false;\n    /**\n     * @var string whether the filters should be displayed in the grid view. Valid values include:\n     *\n     * - [[FILTER_POS_HEADER]]: the filters will be displayed on top of each column's header cell.\n     * - [[FILTER_POS_BODY]]: the filters will be displayed right below each column's header cell.\n     * - [[FILTER_POS_FOOTER]]: the filters will be displayed below each column's footer cell.\n     */\n    public $filterPosition = self::FILTER_POS_BODY;\n    /**\n     * @var array the HTML attributes for the filter row element.\n     * @see \\yii\\helpers\\Html::renderTagAttributes() for details on how attributes are being rendered.\n     */\n    public $filterRowOptions = ['class' => 'filters'];\n    /**\n     * @var array the options for rendering the filter error summary.\n     * Please refer to [[Html::errorSummary()]] for more details about how to specify the options.\n     * @see renderErrors()\n     */\n    public $filterErrorSummaryOptions = ['class' => 'error-summary'];\n    /**\n     * @var array the options for rendering every filter error message.\n     * This is mainly used by [[Html::error()]] when rendering an error message next to every filter input field.\n     */\n    public $filterErrorOptions = ['class' => 'help-block'];\n    /**\n     * @var bool whatever to apply filters on losing focus. Leaves an ability to manage filters via yiiGridView JS\n     * @since 2.0.16\n     */\n    public $filterOnFocusOut = true;\n    /**\n     * @var string the layout that determines how different sections of the grid view should be organized.\n     * The following tokens will be replaced with the corresponding section contents:\n     *\n     * - `{summary}`: the summary section. See [[renderSummary()]].\n     * - `{errors}`: the filter model error summary. See [[renderErrors()]].\n     * - `{items}`: the list items. See [[renderItems()]].\n     * - `{sorter}`: the sorter. See [[renderSorter()]].\n     * - `{pager}`: the pager. See [[renderPager()]].\n     */\n    public $layout = \"{summary}\\n{items}\\n{pager}\";\n\n\n    /**\n     * Initializes the grid view.\n     * This method will initialize required property values and instantiate [[columns]] objects.\n     */\n    public function init()\n    {\n        parent::init();\n        if ($this->formatter === null) {\n            $this->formatter = Yii::$app->getFormatter();\n        } elseif (is_array($this->formatter)) {\n            $this->formatter = Yii::createObject($this->formatter);\n        }\n        if (!$this->formatter instanceof Formatter) {\n            throw new InvalidConfigException('The \"formatter\" property must be either a Format object or a configuration array.');\n        }\n        if (!isset($this->filterRowOptions['id'])) {\n            $this->filterRowOptions['id'] = $this->options['id'] . '-filters';\n        }\n\n        $this->initColumns();\n    }\n\n    /**\n     * Runs the widget.\n     */\n    public function run()\n    {\n        $view = $this->getView();\n        GridViewAsset::register($view);\n        $id = $this->options['id'];\n        $options = Json::htmlEncode(array_merge($this->getClientOptions(), ['filterOnFocusOut' => $this->filterOnFocusOut]));\n        $view->registerJs(\"jQuery('#$id').yiiGridView($options);\");\n        parent::run();\n    }\n\n    /**\n     * Renders validator errors of filter model.\n     * @return string the rendering result.\n     */\n    public function renderErrors()\n    {\n        if ($this->filterModel instanceof Model && $this->filterModel->hasErrors()) {\n            return Html::errorSummary($this->filterModel, $this->filterErrorSummaryOptions);\n        }\n\n        return '';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function renderSection($name)\n    {\n        switch ($name) {\n            case '{errors}':\n                return $this->renderErrors();\n            default:\n                return parent::renderSection($name);\n        }\n    }\n\n    /**\n     * Returns the options for the grid view JS widget.\n     * @return array the options\n     */\n    protected function getClientOptions()\n    {\n        $filterUrl = isset($this->filterUrl) ? $this->filterUrl : Yii::$app->request->url;\n        $id = $this->filterRowOptions['id'];\n        $filterSelector = \"#$id input, #$id select\";\n        if (isset($this->filterSelector)) {\n            $additionalFilterSelector = $this->filterSelector;\n            if ($this->filterSelector instanceof \\Closure) {\n                $additionalFilterSelector = ($this->filterSelector)($this->getId(), $id);\n            }\n            if ($this->overrideFilterSelector) {\n                $filterSelector = $additionalFilterSelector;\n            } else {\n                $filterSelector .= ', ' . $additionalFilterSelector;\n            }\n        }\n\n        return [\n            'filterUrl' => Url::to($filterUrl),\n            'filterSelector' => $filterSelector,\n        ];\n    }\n\n    /**\n     * Renders the data models for the grid view.\n     * @return string the HTML code of table\n     */\n    public function renderItems()\n    {\n        $caption = $this->renderCaption();\n        $columnGroup = $this->renderColumnGroup();\n        $tableHeader = $this->showHeader ? $this->renderTableHeader() : false;\n        $tableBody = $this->renderTableBody();\n\n        $tableFooter = false;\n        $tableFooterAfterBody = false;\n\n        if ($this->showFooter) {\n            if ($this->placeFooterAfterBody) {\n                $tableFooterAfterBody = $this->renderTableFooter();\n            } else {\n                $tableFooter = $this->renderTableFooter();\n            }\n        }\n\n        $content = array_filter([\n            $caption,\n            $columnGroup,\n            $tableHeader,\n            $tableFooter,\n            $tableBody,\n            $tableFooterAfterBody,\n        ]);\n\n        return Html::tag('table', implode(\"\\n\", $content), $this->tableOptions);\n    }\n\n    /**\n     * Renders the caption element.\n     * @return bool|string the rendered caption element or `false` if no caption element should be rendered.\n     */\n    public function renderCaption()\n    {\n        if (!empty($this->caption)) {\n            return Html::tag('caption', $this->caption, $this->captionOptions);\n        }\n\n        return false;\n    }\n\n    /**\n     * Renders the column group HTML.\n     * @return bool|string the column group HTML or `false` if no column group should be rendered.\n     */\n    public function renderColumnGroup()\n    {\n        foreach ($this->columns as $column) {\n            /** @var Column $column */\n            if (!empty($column->options)) {\n                $cols = [];\n                foreach ($this->columns as $col) {\n                    $cols[] = Html::tag('col', '', $col->options);\n                }\n\n                return Html::tag('colgroup', implode(\"\\n\", $cols));\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * Renders the table header.\n     * @return string the rendering result.\n     */\n    public function renderTableHeader()\n    {\n        $cells = [];\n        foreach ($this->columns as $column) {\n            /** @var Column $column */\n            $cells[] = $column->renderHeaderCell();\n        }\n        $content = Html::tag('tr', implode('', $cells), $this->headerRowOptions);\n        if ($this->filterPosition === self::FILTER_POS_HEADER) {\n            $content = $this->renderFilters() . $content;\n        } elseif ($this->filterPosition === self::FILTER_POS_BODY) {\n            $content .= $this->renderFilters();\n        }\n\n        return \"<thead>\\n\" . $content . \"\\n</thead>\";\n    }\n\n    /**\n     * Renders the table footer.\n     * @return string the rendering result.\n     */\n    public function renderTableFooter()\n    {\n        $cells = [];\n        foreach ($this->columns as $column) {\n            /** @var Column $column */\n            $cells[] = $column->renderFooterCell();\n        }\n        $content = Html::tag('tr', implode('', $cells), $this->footerRowOptions);\n        if ($this->filterPosition === self::FILTER_POS_FOOTER) {\n            $content .= $this->renderFilters();\n        }\n\n        return \"<tfoot>\\n\" . $content . \"\\n</tfoot>\";\n    }\n\n    /**\n     * Renders the filter.\n     * @return string the rendering result.\n     */\n    public function renderFilters()\n    {\n        if ($this->filterModel !== null) {\n            $cells = [];\n            foreach ($this->columns as $column) {\n                /** @var Column $column */\n                $cells[] = $column->renderFilterCell();\n            }\n\n            return Html::tag('tr', implode('', $cells), $this->filterRowOptions);\n        }\n\n        return '';\n    }\n\n    /**\n     * Renders the table body.\n     * @return string the rendering result.\n     */\n    public function renderTableBody()\n    {\n        $models = array_values($this->dataProvider->getModels());\n        $keys = $this->dataProvider->getKeys();\n        $rows = [];\n        foreach ($models as $index => $model) {\n            $key = $keys[$index];\n            if ($this->beforeRow !== null) {\n                $row = call_user_func($this->beforeRow, $model, $key, $index, $this);\n                if (!empty($row)) {\n                    $rows[] = $row;\n                }\n            }\n\n            $rows[] = $this->renderTableRow($model, $key, $index);\n\n            if ($this->afterRow !== null) {\n                $row = call_user_func($this->afterRow, $model, $key, $index, $this);\n                if (!empty($row)) {\n                    $rows[] = $row;\n                }\n            }\n        }\n\n        if (empty($rows) && $this->emptyText !== false) {\n            $colspan = count($this->columns);\n\n            return \"<tbody>\\n<tr><td colspan=\\\"$colspan\\\">\" . $this->renderEmpty() . \"</td></tr>\\n</tbody>\";\n        }\n\n        return \"<tbody>\\n\" . implode(\"\\n\", $rows) . \"\\n</tbody>\";\n    }\n\n    /**\n     * Renders a table row with the given data model and key.\n     * @param mixed $model the data model to be rendered\n     * @param mixed $key the key associated with the data model\n     * @param int $index the zero-based index of the data model among the model array returned by [[dataProvider]].\n     * @return string the rendering result\n     */\n    public function renderTableRow($model, $key, $index)\n    {\n        $cells = [];\n        /** @var Column $column */\n        foreach ($this->columns as $column) {\n            $cells[] = $column->renderDataCell($model, $key, $index);\n        }\n        if ($this->rowOptions instanceof Closure) {\n            $options = call_user_func($this->rowOptions, $model, $key, $index, $this);\n        } else {\n            $options = $this->rowOptions;\n        }\n        $options['data-key'] = is_array($key) ? json_encode($key) : (string) $key;\n\n        return Html::tag('tr', implode('', $cells), $options);\n    }\n\n    /**\n     * Creates column objects and initializes them.\n     */\n    protected function initColumns()\n    {\n        if (empty($this->columns)) {\n            $this->guessColumns();\n        }\n        foreach ($this->columns as $i => $column) {\n            if (is_string($column)) {\n                $column = $this->createDataColumn($column);\n            } else {\n                $column = Yii::createObject(array_merge([\n                    'class' => $this->dataColumnClass ?: DataColumn::className(),\n                    'grid' => $this,\n                ], $column));\n            }\n            if (!$column->visible) {\n                unset($this->columns[$i]);\n                continue;\n            }\n            $this->columns[$i] = $column;\n        }\n    }\n\n    /**\n     * Creates a [[DataColumn]] object based on a string in the format of \"attribute:format:label\".\n     * @param string $text the column specification string\n     * @return DataColumn the column instance\n     * @throws InvalidConfigException if the column specification is invalid\n     */\n    protected function createDataColumn($text)\n    {\n        if (!preg_match('/^([^:]+)(:(\\w*))?(:(.*))?$/', $text, $matches)) {\n            throw new InvalidConfigException('The column must be specified in the format of \"attribute\", \"attribute:format\" or \"attribute:format:label\"');\n        }\n\n        return Yii::createObject([\n            'class' => $this->dataColumnClass ?: DataColumn::className(),\n            'grid' => $this,\n            'attribute' => $matches[1],\n            'format' => isset($matches[3]) ? $matches[3] : 'text',\n            'label' => isset($matches[5]) ? $matches[5] : null,\n        ]);\n    }\n\n    /**\n     * This function tries to guess the columns to show from the given data\n     * if [[columns]] are not explicitly specified.\n     */\n    protected function guessColumns()\n    {\n        $models = $this->dataProvider->getModels();\n        $model = reset($models);\n        if (is_array($model) || is_object($model)) {\n            foreach ($model as $name => $value) {\n                if ($value === null || is_scalar($value) || is_callable([$value, '__toString'])) {\n                    $this->columns[] = (string) $name;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "framework/grid/GridViewAsset.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\grid;\n\nuse yii\\web\\AssetBundle;\n\n/**\n * This asset bundle provides the javascript files for the [[GridView]] widget.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass GridViewAsset extends AssetBundle\n{\n    public $sourcePath = '@yii/assets';\n    public $js = [\n        'yii.gridView.js',\n    ];\n    public $depends = [\n        'yii\\web\\YiiAsset',\n    ];\n}\n"
  },
  {
    "path": "framework/grid/RadioButtonColumn.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\grid;\n\nuse Closure;\nuse yii\\base\\InvalidConfigException;\nuse yii\\helpers\\Html;\n\n/**\n * RadioButtonColumn displays a column of radio buttons in a grid view.\n *\n * To add a RadioButtonColumn to the [[GridView]], add it to the [[GridView::columns|columns]] configuration as follows:\n *\n * ```\n * 'columns' => [\n *     // ...\n *     [\n *         'class' => 'yii\\grid\\RadioButtonColumn',\n *         'radioOptions' => function ($model) {\n *              return [\n *                  'value' => $model['value'],\n *                  'checked' => $model['value'] == 2\n *              ];\n *          }\n *     ],\n * ]\n * ```\n *\n * @author Kirk Hansen <hanski07@luther.edu>\n * @since 2.0.11\n */\nclass RadioButtonColumn extends Column\n{\n    /**\n     * @var string the name of the input radio button input fields.\n     */\n    public $name = 'radioButtonSelection';\n    /**\n     * @var array|\\Closure the HTML attributes for the radio buttons. This can either be an array of\n     * attributes or an anonymous function ([[Closure]]) returning such an array.\n     *\n     * The signature of the function should be as follows: `function ($model, $key, $index, $column)`\n     * where `$model`, `$key`, and `$index` refer to the model, key and index of the row currently being rendered\n     * and `$column` is a reference to the [[RadioButtonColumn]] object.\n     *\n     * A function may be used to assign different attributes to different rows based on the data in that row.\n     * Specifically if you want to set a different value for the radio button you can use this option\n     * in the following way (in this example using the `name` attribute of the model):\n     *\n     * ```\n     * 'radioOptions' => function ($model, $key, $index, $column) {\n     *     return ['value' => $model->attribute];\n     * }\n     * ```\n     *\n     * @see \\yii\\helpers\\Html::renderTagAttributes() for details on how attributes are being rendered.\n     */\n    public $radioOptions = [];\n\n\n    /**\n     * {@inheritdoc}\n     * @throws \\yii\\base\\InvalidConfigException if [[name]] is not set.\n     */\n    public function init()\n    {\n        parent::init();\n        if (empty($this->name)) {\n            throw new InvalidConfigException('The \"name\" property must be set.');\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function renderDataCellContent($model, $key, $index)\n    {\n        if ($this->content !== null) {\n            return parent::renderDataCellContent($model, $key, $index);\n        }\n\n        if ($this->radioOptions instanceof Closure) {\n            $options = call_user_func($this->radioOptions, $model, $key, $index, $this);\n        } else {\n            $options = $this->radioOptions;\n            if (!isset($options['value'])) {\n                $options['value'] = is_array($key) ? json_encode($key, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) : $key;\n            }\n        }\n        $checked = isset($options['checked']) ? $options['checked'] : false;\n        return Html::radio($this->name, $checked, $options);\n    }\n}\n"
  },
  {
    "path": "framework/grid/SerialColumn.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\grid;\n\n/**\n * SerialColumn displays a column of row numbers (1-based).\n *\n * To add a SerialColumn to the [[GridView]], add it to the [[GridView::columns|columns]] configuration as follows:\n *\n * ```\n * 'columns' => [\n *     // ...\n *     [\n *         'class' => 'yii\\grid\\SerialColumn',\n *         // you may configure additional properties here\n *     ],\n * ]\n * ```\n *\n * For more details and usage information on SerialColumn, see the [guide article on data widgets](guide:output-data-widgets).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass SerialColumn extends Column\n{\n    /**\n     * {@inheritdoc}\n     */\n    public $header = '#';\n\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function renderDataCellContent($model, $key, $index)\n    {\n        $pagination = $this->grid->dataProvider->getPagination();\n        if ($pagination !== false) {\n            return (string) ($pagination->getOffset() + $index + 1);\n        }\n\n        return (string) ($index + 1);\n    }\n}\n"
  },
  {
    "path": "framework/helpers/ArrayHelper.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\helpers;\n\n/**\n * ArrayHelper provides additional array functionality that you can use in your\n * application.\n *\n * For more details and usage information on ArrayHelper, see the [guide article on array helpers](guide:helper-array).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass ArrayHelper extends BaseArrayHelper\n{\n}\n"
  },
  {
    "path": "framework/helpers/BaseArrayHelper.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\helpers;\n\nuse Yii;\nuse ArrayAccess;\nuse Traversable;\nuse yii\\base\\Arrayable;\nuse yii\\base\\InvalidArgumentException;\n\n/**\n * BaseArrayHelper provides concrete implementation for [[ArrayHelper]].\n *\n * Do not use BaseArrayHelper. Use [[ArrayHelper]] instead.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass BaseArrayHelper\n{\n    /**\n     * Converts an object or an array of objects into an array.\n     * @param object|array|string $object the object to be converted into an array\n     * @param array $properties a mapping from object class names to the properties that need to put into the resulting arrays.\n     * The properties specified for each class is an array of the following format:\n     *\n     * ```\n     * [\n     *     'app\\models\\Post' => [\n     *         'id',\n     *         'title',\n     *         // the key name in array result => property name\n     *         'createTime' => 'created_at',\n     *         // the key name in array result => anonymous function\n     *         'length' => function ($post) {\n     *             return strlen($post->content);\n     *         },\n     *     ],\n     * ]\n     * ```\n     *\n     * The result of `ArrayHelper::toArray($post, $properties)` could be like the following:\n     *\n     * ```\n     * [\n     *     'id' => 123,\n     *     'title' => 'test',\n     *     'createTime' => '2013-01-01 12:00AM',\n     *     'length' => 301,\n     * ]\n     * ```\n     *\n     * @param bool $recursive whether to recursively converts properties which are objects into arrays.\n     * @return array the array representation of the object\n     */\n    public static function toArray($object, $properties = [], $recursive = true)\n    {\n        if (is_array($object)) {\n            if ($recursive) {\n                foreach ($object as $key => $value) {\n                    if (is_array($value) || is_object($value)) {\n                        $object[$key] = static::toArray($value, $properties, true);\n                    }\n                }\n            }\n\n            return $object;\n        } elseif ($object instanceof \\DateTimeInterface) {\n            return (array)$object;\n        } elseif (is_object($object)) {\n            if (!empty($properties)) {\n                $className = get_class($object);\n                if (!empty($properties[$className])) {\n                    $result = [];\n                    foreach ($properties[$className] as $key => $name) {\n                        if (is_int($key)) {\n                            $result[$name] = $object->$name;\n                        } else {\n                            $result[$key] = static::getValue($object, $name);\n                        }\n                    }\n\n                    return $recursive ? static::toArray($result, $properties) : $result;\n                }\n            }\n            if ($object instanceof Arrayable) {\n                $result = $object->toArray([], [], $recursive);\n            } else {\n                $result = [];\n                foreach ($object as $key => $value) {\n                    $result[$key] = $value;\n                }\n            }\n\n            return $recursive ? static::toArray($result, $properties) : $result;\n        }\n\n        return [$object];\n    }\n\n    /**\n     * Merges two or more arrays into one recursively.\n     * If each array has an element with the same string key value, the latter\n     * will overwrite the former (different from array_merge_recursive).\n     * Recursive merging will be conducted if both arrays have an element of array\n     * type and are having the same key.\n     * For integer-keyed elements, the elements from the latter array will\n     * be appended to the former array.\n     * You can use [[UnsetArrayValue]] object to unset value from previous array or\n     * [[ReplaceArrayValue]] to force replace former value instead of recursive merging.\n     * @param array<array-key, mixed> $a array to be merged to\n     * @param array<array-key, mixed> ...$b array to be merged from. You can specify additional\n     * arrays via third argument, fourth argument etc.\n     * @return array<array-key, mixed> the merged array (the original arrays are not changed.)\n     */\n    public static function merge($a, ...$b)\n    {\n        $args = func_get_args();\n        $res = array_shift($args);\n        while (!empty($args)) {\n            foreach (array_shift($args) as $k => $v) {\n                if ($v instanceof UnsetArrayValue) {\n                    unset($res[$k]);\n                } elseif ($v instanceof ReplaceArrayValue) {\n                    $res[$k] = $v->value;\n                } elseif (is_int($k)) {\n                    if (array_key_exists($k, $res)) {\n                        $res[] = $v;\n                    } else {\n                        $res[$k] = $v;\n                    }\n                } elseif (is_array($v) && isset($res[$k]) && is_array($res[$k])) {\n                    $res[$k] = static::merge($res[$k], $v);\n                } else {\n                    $res[$k] = $v;\n                }\n            }\n        }\n\n        return $res;\n    }\n\n    /**\n     * Retrieves the value of an array element or object property with the given key or property name.\n     * If the key does not exist in the array, the default value will be returned instead.\n     * Not used when getting value from an object.\n     *\n     * The key may be specified in a dot format to retrieve the value of a sub-array or the property\n     * of an embedded object. In particular, if the key is `x.y.z`, then the returned value would\n     * be `$array['x']['y']['z']` or `$array->x->y->z` (if `$array` is an object). If `$array['x']`\n     * or `$array->x` is neither an array nor an object, the default value will be returned.\n     * Note that if the array already has an element `x.y.z`, then its value will be returned\n     * instead of going through the sub-arrays. So it is better to be done specifying an array of key names\n     * like `['x', 'y', 'z']`.\n     *\n     * Below are some usage examples,\n     *\n     * ```\n     * // working with array\n     * $username = \\yii\\helpers\\ArrayHelper::getValue($_POST, 'username');\n     * // working with object\n     * $username = \\yii\\helpers\\ArrayHelper::getValue($user, 'username');\n     * // working with anonymous function\n     * $fullName = \\yii\\helpers\\ArrayHelper::getValue($user, function ($user, $defaultValue) {\n     *     return $user->firstName . ' ' . $user->lastName;\n     * });\n     * // using dot format to retrieve the property of embedded object\n     * $street = \\yii\\helpers\\ArrayHelper::getValue($users, 'address.street');\n     * // using an array of keys to retrieve the value\n     * $value = \\yii\\helpers\\ArrayHelper::getValue($versions, ['1.0', 'date']);\n     * ```\n     *\n     * @param array|object $array array or object to extract value from\n     * @param string|\\Closure|array $key key name of the array element, an array of keys or property name of the object,\n     * or an anonymous function returning the value. The anonymous function signature should be:\n     * `function($array, $defaultValue)`.\n     * The possibility to pass an array of keys is available since version 2.0.4.\n     * @param mixed $default the default value to be returned if the specified array key does not exist. Not used when\n     * getting value from an object.\n     * @return mixed the value of the element if found, default value otherwise\n     */\n    public static function getValue($array, $key, $default = null)\n    {\n        if ($key instanceof \\Closure) {\n            return $key($array, $default);\n        }\n\n        if (is_array($key)) {\n            $lastKey = array_pop($key);\n            foreach ($key as $keyPart) {\n                $array = static::getValue($array, $keyPart);\n            }\n            $key = $lastKey;\n        }\n\n        if (is_object($array) && property_exists($array, $key)) {\n            return $array->$key;\n        }\n\n        if (static::keyExists($key, $array)) {\n            return $array[$key];\n        }\n\n        if ($key && ($pos = strrpos($key, '.')) !== false) {\n            $array = static::getValue($array, substr($key, 0, $pos), $default);\n            $key = substr($key, $pos + 1);\n        }\n\n        if (static::keyExists($key, $array)) {\n            return $array[$key];\n        }\n        if (is_object($array)) {\n            // this is expected to fail if the property does not exist, or __get() is not implemented\n            // it is not reliably possible to check whether a property is accessible beforehand\n            try {\n                return $array->$key;\n            } catch (\\Exception $e) {\n                if ($array instanceof ArrayAccess) {\n                    return $default;\n                }\n                throw $e;\n            }\n        }\n\n        return $default;\n    }\n\n    /**\n     * Writes a value into an associative array at the key path specified.\n     * If there is no such key path yet, it will be created recursively.\n     * If the key exists, it will be overwritten.\n     *\n     * ```\n     *  $array = [\n     *      'key' => [\n     *          'in' => [\n     *              'val1',\n     *              'key' => 'val'\n     *          ]\n     *      ]\n     *  ];\n     * ```\n     *\n     * The result of `ArrayHelper::setValue($array, 'key.in.0', ['arr' => 'val']);` will be the following:\n     *\n     * ```\n     *  [\n     *      'key' => [\n     *          'in' => [\n     *              ['arr' => 'val'],\n     *              'key' => 'val'\n     *          ]\n     *      ]\n     *  ]\n     *\n     * ```\n     *\n     * The result of\n     * `ArrayHelper::setValue($array, 'key.in', ['arr' => 'val']);` or\n     * `ArrayHelper::setValue($array, ['key', 'in'], ['arr' => 'val']);`\n     * will be the following:\n     *\n     * ```\n     *  [\n     *      'key' => [\n     *          'in' => [\n     *              'arr' => 'val'\n     *          ]\n     *      ]\n     *  ]\n     * ```\n     *\n     * @param array $array the array to write the value to\n     * @param string|array|null $path the path of where do you want to write a value to `$array`\n     * the path can be described by a string when each key should be separated by a dot\n     * you can also describe the path as an array of keys\n     * if the path is null then `$array` will be assigned the `$value`\n     * @param mixed $value the value to be written\n     * @since 2.0.13\n     */\n    public static function setValue(&$array, $path, $value)\n    {\n        if ($path === null) {\n            $array = $value;\n            return;\n        }\n\n        $keys = is_array($path) ? $path : explode('.', $path);\n\n        while (count($keys) > 1) {\n            $key = array_shift($keys);\n            if (!isset($array[$key])) {\n                $array[$key] = [];\n            }\n            if (!is_array($array[$key])) {\n                $array[$key] = [$array[$key]];\n            }\n            $array = &$array[$key];\n        }\n\n        $array[array_shift($keys)] = $value;\n    }\n\n    /**\n     * Removes an item from an array and returns the value. If the key does not exist in the array, the default value\n     * will be returned instead.\n     *\n     * Usage examples,\n     *\n     * ```\n     * // $array = ['type' => 'A', 'options' => [1, 2]];\n     * // working with array\n     * $type = \\yii\\helpers\\ArrayHelper::remove($array, 'type');\n     * // $array content\n     * // $array = ['options' => [1, 2]];\n     * ```\n     *\n     * @param array $array the array to extract value from\n     * @param string $key key name of the array element\n     * @param mixed $default the default value to be returned if the specified key does not exist\n     * @return mixed|null the value of the element if found, default value otherwise\n     */\n    public static function remove(&$array, $key, $default = null)\n    {\n        // ToDo: This check can be removed when the minimum PHP version is >= 8.1 (Yii2.2)\n        if (is_float($key)) {\n            $key = (int)$key;\n        }\n\n        if (is_array($array) && array_key_exists($key, $array)) {\n            $value = $array[$key];\n            unset($array[$key]);\n\n            return $value;\n        }\n\n        return $default;\n    }\n\n    /**\n     * Removes items with matching values from the array and returns the removed items.\n     *\n     * Example,\n     *\n     * ```\n     * $array = ['Bob' => 'Dylan', 'Michael' => 'Jackson', 'Mick' => 'Jagger', 'Janet' => 'Jackson'];\n     * $removed = \\yii\\helpers\\ArrayHelper::removeValue($array, 'Jackson');\n     * // result:\n     * // $array = ['Bob' => 'Dylan', 'Mick' => 'Jagger'];\n     * // $removed = ['Michael' => 'Jackson', 'Janet' => 'Jackson'];\n     * ```\n     *\n     * @param array $array the array where to look the value from\n     * @param mixed $value the value to remove from the array\n     * @return array the items that were removed from the array\n     * @since 2.0.11\n     */\n    public static function removeValue(&$array, $value)\n    {\n        $result = [];\n        if (is_array($array)) {\n            foreach ($array as $key => $val) {\n                if ($val === $value) {\n                    $result[$key] = $val;\n                    unset($array[$key]);\n                }\n            }\n        }\n\n        return $result;\n    }\n\n    /**\n     * Indexes and/or groups the array according to a specified key.\n     * The input should be either multidimensional array or an array of objects.\n     *\n     * The $key can be either a key name of the sub-array, a property name of object, or an anonymous\n     * function that must return the value that will be used as a key.\n     *\n     * $groups is an array of keys, that will be used to group the input array into one or more sub-arrays based\n     * on keys specified.\n     *\n     * If the `$key` is specified as `null` or a value of an element corresponding to the key is `null` in addition\n     * to `$groups` not specified then the element is discarded.\n     *\n     * For example:\n     *\n     * ```\n     * $array = [\n     *     ['id' => '123', 'data' => 'abc', 'device' => 'laptop'],\n     *     ['id' => '345', 'data' => 'def', 'device' => 'tablet'],\n     *     ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone'],\n     * ];\n     * $result = ArrayHelper::index($array, 'id');\n     * ```\n     *\n     * The result will be an associative array, where the key is the value of `id` attribute\n     *\n     * ```\n     * [\n     *     '123' => ['id' => '123', 'data' => 'abc', 'device' => 'laptop'],\n     *     '345' => ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone']\n     *     // The second element of an original array is overwritten by the last element because of the same id\n     * ]\n     * ```\n     *\n     * An anonymous function can be used in the grouping array as well.\n     *\n     * ```\n     * $result = ArrayHelper::index($array, function ($element) {\n     *     return $element['id'];\n     * });\n     * ```\n     *\n     * Passing `id` as a third argument will group `$array` by `id`:\n     *\n     * ```\n     * $result = ArrayHelper::index($array, null, 'id');\n     * ```\n     *\n     * The result will be a multidimensional array grouped by `id`:\n     *\n     * ```\n     * [\n     *     '123' => [\n     *         ['id' => '123', 'data' => 'abc', 'device' => 'laptop']\n     *     ],\n     *     '345' => [ // all elements with this index are present in the result array\n     *         ['id' => '345', 'data' => 'def', 'device' => 'tablet'],\n     *         ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone'],\n     *     ]\n     * ]\n     * ```\n     *\n     * The anonymous function can be used in the array of grouping keys as well:\n     *\n     * ```\n     * $result = ArrayHelper::index($array, 'data', [function ($element) {\n     *     return $element['id'];\n     * }, 'device']);\n     * ```\n     *\n     * The result will be a multidimensional array grouped by `id` on the first level, by the `device` on the second one\n     * and indexed by the `data` on the third level:\n     *\n     * ```\n     * [\n     *     '123' => [\n     *         'laptop' => [\n     *             'abc' => ['id' => '123', 'data' => 'abc', 'device' => 'laptop']\n     *         ]\n     *     ],\n     *     '345' => [\n     *         'tablet' => [\n     *             'def' => ['id' => '345', 'data' => 'def', 'device' => 'tablet']\n     *         ],\n     *         'smartphone' => [\n     *             'hgi' => ['id' => '345', 'data' => 'hgi', 'device' => 'smartphone']\n     *         ]\n     *     ]\n     * ]\n     * ```\n     *\n     * @param array $array the array that needs to be indexed or grouped\n     * @param string|\\Closure|null $key the column name or anonymous function which result will be used to index the array\n     * @param string|string[]|\\Closure[]|null $groups the array of keys, that will be used to group the input array\n     * by one or more keys. If the $key attribute or its value for the particular element is null and $groups is not\n     * defined, the array element will be discarded. Otherwise, if $groups is specified, array element will be added\n     * to the result array without any key. This parameter is available since version 2.0.8.\n     * @return array the indexed and/or grouped array\n     */\n    public static function index($array, $key, $groups = [])\n    {\n        $result = [];\n        $groups = (array) $groups;\n\n        foreach ($array as $element) {\n            $lastArray = &$result;\n\n            foreach ($groups as $group) {\n                $value = static::getValue($element, $group);\n                if (!array_key_exists($value, $lastArray)) {\n                    $lastArray[$value] = [];\n                }\n                $lastArray = &$lastArray[$value];\n            }\n\n            if ($key === null) {\n                if (!empty($groups)) {\n                    $lastArray[] = $element;\n                }\n            } else {\n                $value = static::getValue($element, $key);\n                if ($value !== null) {\n                    if (is_float($value)) {\n                        $value = StringHelper::floatToString($value);\n                    }\n                    $lastArray[$value] = $element;\n                }\n            }\n            unset($lastArray);\n        }\n\n        return $result;\n    }\n\n    /**\n     * Returns the values of a specified column in an array.\n     * The input array should be multidimensional or an array of objects.\n     *\n     * For example,\n     *\n     * ```\n     * $array = [\n     *     ['id' => '123', 'data' => 'abc'],\n     *     ['id' => '345', 'data' => 'def'],\n     * ];\n     * $result = ArrayHelper::getColumn($array, 'id');\n     * // the result is: ['123', '345']\n     *\n     * // using anonymous function\n     * $result = ArrayHelper::getColumn($array, function ($element) {\n     *     return $element['id'];\n     * });\n     * ```\n     *\n     * @param array $array\n     * @param int|string|array|\\Closure $name\n     * @param bool $keepKeys whether to maintain the array keys. If false, the resulting array\n     * will be re-indexed with integers.\n     * @return array the list of column values\n     */\n    public static function getColumn($array, $name, $keepKeys = true)\n    {\n        $result = [];\n        if ($keepKeys) {\n            foreach ($array as $k => $element) {\n                $result[$k] = static::getValue($element, $name);\n            }\n        } else {\n            foreach ($array as $element) {\n                $result[] = static::getValue($element, $name);\n            }\n        }\n\n        return $result;\n    }\n\n    /**\n     * Builds a map (key-value pairs) from a multidimensional array or an array of objects.\n     * The `$from` and `$to` parameters specify the key names or property names to set up the map.\n     * Optionally, one can further group the map according to a grouping field `$group`.\n     *\n     * For example,\n     *\n     * ```\n     * $array = [\n     *     ['id' => '123', 'name' => 'aaa', 'class' => 'x'],\n     *     ['id' => '124', 'name' => 'bbb', 'class' => 'x'],\n     *     ['id' => '345', 'name' => 'ccc', 'class' => 'y'],\n     * ];\n     *\n     * $result = ArrayHelper::map($array, 'id', 'name');\n     * // the result is:\n     * // [\n     * //     '123' => 'aaa',\n     * //     '124' => 'bbb',\n     * //     '345' => 'ccc',\n     * // ]\n     *\n     * $result = ArrayHelper::map($array, 'id', 'name', 'class');\n     * // the result is:\n     * // [\n     * //     'x' => [\n     * //         '123' => 'aaa',\n     * //         '124' => 'bbb',\n     * //     ],\n     * //     'y' => [\n     * //         '345' => 'ccc',\n     * //     ],\n     * // ]\n     * ```\n     *\n     * @param array $array\n     * @param string|\\Closure $from\n     * @param string|\\Closure $to\n     * @param string|\\Closure|null $group\n     * @return array\n     */\n    public static function map($array, $from, $to, $group = null)\n    {\n        if (is_string($from) && is_string($to) && $group === null && strpos($from, '.') === false && strpos($to, '.') === false) {\n            return array_column($array, $to, $from);\n        }\n        $result = [];\n        foreach ($array as $element) {\n            $key = static::getValue($element, $from);\n            $value = static::getValue($element, $to);\n            if ($group !== null) {\n                $result[static::getValue($element, $group)][$key] = $value;\n            } else {\n                $result[$key] = $value;\n            }\n        }\n\n        return $result;\n    }\n\n    /**\n     * Checks if the given array contains the specified key.\n     * This method enhances the `array_key_exists()` function by supporting case-insensitive\n     * key comparison.\n     * @param string|int $key the key to check\n     * @param array<array-key, mixed>|ArrayAccess<array-key, mixed> $array the array with keys to check\n     * @param bool $caseSensitive whether the key comparison should be case-sensitive\n     * @return bool whether the array contains the specified key\n     */\n    public static function keyExists($key, $array, $caseSensitive = true)\n    {\n        // ToDo: This check can be removed when the minimum PHP version is >= 8.1 (Yii2.2)\n        if (is_float($key)) {\n            $key = (int)$key;\n        }\n\n        if ($caseSensitive) {\n            if (is_array($array) && array_key_exists($key, $array)) {\n                return true;\n            }\n            // Cannot use `array_has_key` on Objects for PHP 7.4+, therefore we need to check using [[ArrayAccess::offsetExists()]]\n            return $array instanceof ArrayAccess && $array->offsetExists($key);\n        }\n\n        if ($array instanceof ArrayAccess) {\n            throw new InvalidArgumentException('Second parameter($array) cannot be ArrayAccess in case insensitive mode');\n        }\n\n        foreach (array_keys($array) as $k) {\n            if (strcasecmp($key, $k) === 0) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * Sorts an array of objects or arrays (with the same structure) by one or several keys.\n     * @param array $array the array to be sorted. The array will be modified after calling this method.\n     * @param string|\\Closure|array $key the key(s) to be sorted by. This refers to a key name of the sub-array\n     * elements, a property name of the objects, or an anonymous function returning the values for comparison\n     * purpose. The anonymous function signature should be: `function($item)`.\n     * To sort by multiple keys, provide an array of keys here.\n     * @param int|array $direction the sorting direction. It can be either `SORT_ASC` or `SORT_DESC`.\n     * When sorting by multiple keys with different sorting directions, use an array of sorting directions.\n     * @param int|array $sortFlag the PHP sort flag. Valid values include\n     * `SORT_REGULAR`, `SORT_NUMERIC`, `SORT_STRING`, `SORT_LOCALE_STRING`, `SORT_NATURAL` and `SORT_FLAG_CASE`.\n     * Please refer to [PHP manual](https://www.php.net/manual/en/function.sort.php)\n     * for more details. When sorting by multiple keys with different sort flags, use an array of sort flags.\n     * @throws InvalidArgumentException if the $direction or $sortFlag parameters do not have\n     * correct number of elements as that of $key.\n     */\n    public static function multisort(&$array, $key, $direction = SORT_ASC, $sortFlag = SORT_REGULAR)\n    {\n        $keys = is_array($key) ? $key : [$key];\n        if (empty($keys) || empty($array)) {\n            return;\n        }\n        $n = count($keys);\n        if (is_scalar($direction)) {\n            $direction = array_fill(0, $n, $direction);\n        } elseif (count($direction) !== $n) {\n            throw new InvalidArgumentException('The length of $direction parameter must be the same as that of $keys.');\n        }\n        if (is_scalar($sortFlag)) {\n            $sortFlag = array_fill(0, $n, $sortFlag);\n        } elseif (count($sortFlag) !== $n) {\n            throw new InvalidArgumentException('The length of $sortFlag parameter must be the same as that of $keys.');\n        }\n        $args = [];\n        foreach ($keys as $i => $k) {\n            $flag = $sortFlag[$i];\n            $args[] = static::getColumn($array, $k);\n            $args[] = $direction[$i];\n            $args[] = $flag;\n        }\n\n        // This fix is used for cases when main sorting specified by columns has equal values\n        // Without it it will lead to Fatal Error: Nesting level too deep - recursive dependency?\n        $args[] = range(1, count($array));\n        $args[] = SORT_ASC;\n        $args[] = SORT_NUMERIC;\n\n        $args[] = &$array;\n        call_user_func_array('array_multisort', $args);\n    }\n\n    /**\n     * Encodes special characters in an array of strings into HTML entities.\n     * Only array values will be encoded by default.\n     * If a value is an array, this method will also encode it recursively.\n     * Only string values will be encoded.\n     * @param array $data data to be encoded\n     * @param bool $valuesOnly whether to encode array values only. If false,\n     * both the array keys and array values will be encoded.\n     * @param string|null $charset the charset that the data is using. If not set,\n     * [[\\yii\\base\\Application::charset]] will be used.\n     * @return array the encoded data\n     * @see https://www.php.net/manual/en/function.htmlspecialchars.php\n     */\n    public static function htmlEncode($data, $valuesOnly = true, $charset = null)\n    {\n        if ($charset === null) {\n            $charset = Yii::$app ? Yii::$app->charset : 'UTF-8';\n        }\n        $d = [];\n        foreach ($data as $key => $value) {\n            if (!$valuesOnly && is_string($key)) {\n                $key = htmlspecialchars($key, ENT_QUOTES | ENT_SUBSTITUTE, $charset);\n            }\n            if (is_string($value)) {\n                $d[$key] = htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, $charset);\n            } elseif (is_array($value)) {\n                $d[$key] = static::htmlEncode($value, $valuesOnly, $charset);\n            } else {\n                $d[$key] = $value;\n            }\n        }\n\n        return $d;\n    }\n\n    /**\n     * Decodes HTML entities into the corresponding characters in an array of strings.\n     *\n     * Only array values will be decoded by default.\n     * If a value is an array, this method will also decode it recursively.\n     * Only string values will be decoded.\n     *\n     * @param array $data data to be decoded\n     * @param bool $valuesOnly whether to decode array values only. If `false`,\n     * then both the array keys and array values will be decoded.\n     * @return array the decoded data\n     * @see https://www.php.net/manual/en/function.htmlspecialchars-decode.php\n     */\n    public static function htmlDecode($data, $valuesOnly = true)\n    {\n        $d = [];\n        foreach ($data as $key => $value) {\n            if (!$valuesOnly && is_string($key)) {\n                $key = htmlspecialchars_decode($key, ENT_QUOTES | ENT_SUBSTITUTE);\n            }\n            if (is_string($value)) {\n                $d[$key] = htmlspecialchars_decode($value, ENT_QUOTES | ENT_SUBSTITUTE);\n            } elseif (is_array($value)) {\n                $d[$key] = static::htmlDecode($value, $valuesOnly);\n            } else {\n                $d[$key] = $value;\n            }\n        }\n\n        return $d;\n    }\n\n    /**\n     * Returns a value indicating whether the given array is an associative array.\n     *\n     * An array is associative if all its keys are strings. If `$allStrings` is false,\n     * then an array will be treated as associative if at least one of its keys is a string.\n     *\n     * Note that an empty array will NOT be considered associative.\n     *\n     * @param array $array the array being checked\n     * @param bool $allStrings whether the array keys must be all strings in order for\n     * the array to be treated as associative.\n     * @return bool whether the array is associative\n     */\n    public static function isAssociative($array, $allStrings = true)\n    {\n        if (empty($array) || !is_array($array)) {\n            return false;\n        }\n\n        if ($allStrings) {\n            foreach ($array as $key => $value) {\n                if (!is_string($key)) {\n                    return false;\n                }\n            }\n\n            return true;\n        }\n\n        foreach ($array as $key => $value) {\n            if (is_string($key)) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * Returns a value indicating whether the given array is an indexed array.\n     *\n     * An array is indexed if all its keys are integers. If `$consecutive` is true,\n     * then the array keys must be a consecutive sequence starting from 0.\n     *\n     * Note that an empty array will be considered indexed.\n     *\n     * @param array $array the array being checked\n     * @param bool $consecutive whether the array keys must be a consecutive sequence\n     * in order for the array to be treated as indexed.\n     * @return bool whether the array is indexed\n     */\n    public static function isIndexed($array, $consecutive = false)\n    {\n        if (!is_array($array)) {\n            return false;\n        }\n\n        if (empty($array)) {\n            return true;\n        }\n\n        $keys = array_keys($array);\n\n        if ($consecutive) {\n            return $keys === array_keys($keys);\n        }\n\n        foreach ($keys as $key) {\n            if (!is_int($key)) {\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    /**\n     * Check whether an array or [[Traversable]] contains an element.\n     *\n     * This method does the same as the PHP function [in_array()](https://www.php.net/manual/en/function.in-array.php)\n     * but additionally works for objects that implement the [[Traversable]] interface.\n     *\n     * @param mixed $needle The value to look for.\n     * @param iterable $haystack The set of values to search.\n     * @param bool $strict Whether to enable strict (`===`) comparison.\n     * @return bool `true` if `$needle` was found in `$haystack`, `false` otherwise.\n     * @throws InvalidArgumentException if `$haystack` is neither traversable nor an array.\n     * @see https://www.php.net/manual/en/function.in-array.php\n     * @since 2.0.7\n     */\n    public static function isIn($needle, $haystack, $strict = false)\n    {\n        if (!static::isTraversable($haystack)) {\n            throw new InvalidArgumentException('Argument $haystack must be an array or implement Traversable');\n        }\n\n        if (is_array($haystack)) {\n            return in_array($needle, $haystack, $strict);\n        }\n\n        foreach ($haystack as $value) {\n            if ($strict ? $needle === $value : $needle == $value) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * Checks whether a variable is an array or [[Traversable]].\n     *\n     * This method does the same as the PHP function [is_array()](https://www.php.net/manual/en/function.is-array.php)\n     * but additionally works on objects that implement the [[Traversable]] interface.\n     * @param mixed $var The variable being evaluated.\n     * @return bool whether $var can be traversed via foreach\n     * @see https://www.php.net/manual/en/function.is-array.php\n     * @since 2.0.8\n     */\n    public static function isTraversable($var)\n    {\n        return is_array($var) || $var instanceof Traversable;\n    }\n\n    /**\n     * Checks whether an array or [[Traversable]] is a subset of another array or [[Traversable]].\n     *\n     * This method will return `true`, if all elements of `$needles` are contained in\n     * `$haystack`. If at least one element is missing, `false` will be returned.\n     *\n     * @param iterable $needles The values that must **all** be in `$haystack`.\n     * @param iterable $haystack The set of value to search.\n     * @param bool $strict Whether to enable strict (`===`) comparison.\n     * @return bool `true` if `$needles` is a subset of `$haystack`, `false` otherwise.\n     * @throws InvalidArgumentException if `$haystack` or `$needles` is neither traversable nor an array.\n     * @since 2.0.7\n     */\n    public static function isSubset($needles, $haystack, $strict = false)\n    {\n        if (!static::isTraversable($needles)) {\n            throw new InvalidArgumentException('Argument $needles must be an array or implement Traversable');\n        }\n\n        foreach ($needles as $needle) {\n            if (!static::isIn($needle, $haystack, $strict)) {\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    /**\n     * Filters array according to rules specified.\n     *\n     * For example:\n     *\n     * ```\n     * $array = [\n     *     'A' => [1, 2],\n     *     'B' => [\n     *         'C' => 1,\n     *         'D' => 2,\n     *     ],\n     *     'E' => 1,\n     * ];\n     *\n     * $result = \\yii\\helpers\\ArrayHelper::filter($array, ['A']);\n     * // $result will be:\n     * // [\n     * //     'A' => [1, 2],\n     * // ]\n     *\n     * $result = \\yii\\helpers\\ArrayHelper::filter($array, ['A', 'B.C']);\n     * // $result will be:\n     * // [\n     * //     'A' => [1, 2],\n     * //     'B' => ['C' => 1],\n     * // ]\n     *\n     * $result = \\yii\\helpers\\ArrayHelper::filter($array, ['B', '!B.C']);\n     * // $result will be:\n     * // [\n     * //     'B' => ['D' => 2],\n     * // ]\n     * ```\n     *\n     * @param array $array Source array\n     * @param iterable $filters Rules that define array keys which should be left or removed from results.\n     * Each rule is:\n     * - `var` - `$array['var']` will be left in result.\n     * - `var.key` = only `$array['var']['key'] will be left in result.\n     * - `!var.key` = `$array['var']['key'] will be removed from result.\n     * @return array Filtered array\n     * @since 2.0.9\n     */\n    public static function filter($array, $filters)\n    {\n        $result = [];\n        $excludeFilters = [];\n\n        foreach ($filters as $filter) {\n            if (!is_string($filter) && !is_int($filter)) {\n                continue;\n            }\n\n            if (is_string($filter) && strncmp($filter, '!', 1) === 0) {\n                $excludeFilters[] = substr($filter, 1);\n                continue;\n            }\n\n            $nodeValue = $array; //set $array as root node\n            $keys = explode('.', (string) $filter);\n            foreach ($keys as $key) {\n                if (!array_key_exists($key, $nodeValue)) {\n                    continue 2; //Jump to next filter\n                }\n                $nodeValue = $nodeValue[$key];\n            }\n\n            //We've found a value now let's insert it\n            $resultNode = &$result;\n            foreach ($keys as $key) {\n                if (!array_key_exists($key, $resultNode)) {\n                    $resultNode[$key] = [];\n                }\n                $resultNode = &$resultNode[$key];\n            }\n            $resultNode = $nodeValue;\n        }\n\n        foreach ($excludeFilters as $filter) {\n            $excludeNode = &$result;\n            $keys = explode('.', (string) $filter);\n            $numNestedKeys = count($keys) - 1;\n            foreach ($keys as $i => $key) {\n                if (!array_key_exists($key, $excludeNode)) {\n                    continue 2; //Jump to next filter\n                }\n\n                if ($i < $numNestedKeys) {\n                    $excludeNode = &$excludeNode[$key];\n                } else {\n                    unset($excludeNode[$key]);\n                    break;\n                }\n            }\n        }\n\n        return $result;\n    }\n\n    /**\n     * Sorts array recursively.\n     *\n     * @param array $array An array passing by reference.\n     * @param callable|null $sorter The array sorter. If omitted, sort index array by values, sort assoc array by keys.\n     * @return array\n     */\n    public static function recursiveSort(array &$array, $sorter = null)\n    {\n        foreach ($array as &$value) {\n            if (is_array($value)) {\n                static::recursiveSort($value, $sorter);\n            }\n        }\n        unset($value);\n\n        if ($sorter === null) {\n            $sorter = static::isIndexed($array) ? 'sort' : 'ksort';\n        }\n\n        call_user_func_array($sorter, [&$array]);\n\n        return $array;\n    }\n\n    /**\n     * Flattens a multidimensional array into a one-dimensional array.\n     *\n     * This method recursively traverses the input array and concatenates the keys\n     * in a dot format to form a new key in the resulting array.\n     *\n     * Example:\n     *\n     * ```\n     * $array = [\n     *      'A' => [1, 2],\n     *      'B' => [\n     *          'C' => 1,\n     *          'D' => 2,\n     *      ],\n     *      'E' => 1,\n     *  ];\n     * $result = \\yii\\helpers\\ArrayHelper::flatten($array);\n     * // $result will be:\n     * // [\n     * //     'A.0' => 1\n     * //     'A.1' => 2\n     * //     'B.C' => 1\n     * //     'B.D' => 2\n     * //     'E' => 1\n     * // ]\n     * ```\n     *\n     * @param array $array the input array to be flattened in terms of name-value pairs.\n     * @param string $separator the separator to use between keys. Defaults to '.'.\n     *\n     * @return array the flattened array.\n     * @throws InvalidArgumentException if `$array` is neither traversable nor an array.\n     */\n    public static function flatten($array, $separator = '.'): array\n    {\n        if (!static::isTraversable($array)) {\n            throw new InvalidArgumentException('Argument $array must be an array or implement Traversable');\n        }\n\n        $result = [];\n\n        foreach ($array as $key => $value) {\n            $newKey = $key;\n            if (is_array($value)) {\n                $flattenedArray = self::flatten($value, $separator);\n                foreach ($flattenedArray as $subKey => $subValue) {\n                    $result[$newKey . $separator . $subKey] = $subValue;\n                }\n            } else {\n                $result[$newKey] = $value;\n            }\n        }\n\n        return $result;\n    }\n}\n"
  },
  {
    "path": "framework/helpers/BaseConsole.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\helpers;\n\nuse Yii;\nuse yii\\console\\Markdown as ConsoleMarkdown;\nuse yii\\base\\Model;\n\n/**\n * BaseConsole provides concrete implementation for [[Console]].\n *\n * Do not use BaseConsole. Use [[Console]] instead.\n *\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0\n */\nclass BaseConsole\n{\n    // foreground color control codes\n    public const FG_BLACK = 30;\n    public const FG_RED = 31;\n    public const FG_GREEN = 32;\n    public const FG_YELLOW = 33;\n    public const FG_BLUE = 34;\n    public const FG_PURPLE = 35;\n    public const FG_CYAN = 36;\n    public const FG_GREY = 37;\n    // background color control codes\n    public const BG_BLACK = 40;\n    public const BG_RED = 41;\n    public const BG_GREEN = 42;\n    public const BG_YELLOW = 43;\n    public const BG_BLUE = 44;\n    public const BG_PURPLE = 45;\n    public const BG_CYAN = 46;\n    public const BG_GREY = 47;\n    // fonts style control codes\n    public const RESET = 0;\n    public const NORMAL = 0;\n    public const BOLD = 1;\n    public const ITALIC = 3;\n    public const UNDERLINE = 4;\n    public const BLINK = 5;\n    public const NEGATIVE = 7;\n    public const CONCEALED = 8;\n    public const CROSSED_OUT = 9;\n    public const FRAMED = 51;\n    public const ENCIRCLED = 52;\n    public const OVERLINED = 53;\n    /**\n     * Moves the terminal cursor up by sending ANSI control code CUU to the terminal.\n     * If the cursor is already at the edge of the screen, this has no effect.\n     * @param int $rows number of rows the cursor should be moved up\n     */\n    public static function moveCursorUp($rows = 1)\n    {\n        echo \"\\033[\" . (int) $rows . 'A';\n    }\n\n    /**\n     * Moves the terminal cursor down by sending ANSI control code CUD to the terminal.\n     * If the cursor is already at the edge of the screen, this has no effect.\n     * @param int $rows number of rows the cursor should be moved down\n     */\n    public static function moveCursorDown($rows = 1)\n    {\n        echo \"\\033[\" . (int) $rows . 'B';\n    }\n\n    /**\n     * Moves the terminal cursor forward by sending ANSI control code CUF to the terminal.\n     * If the cursor is already at the edge of the screen, this has no effect.\n     * @param int $steps number of steps the cursor should be moved forward\n     */\n    public static function moveCursorForward($steps = 1)\n    {\n        echo \"\\033[\" . (int) $steps . 'C';\n    }\n\n    /**\n     * Moves the terminal cursor backward by sending ANSI control code CUB to the terminal.\n     * If the cursor is already at the edge of the screen, this has no effect.\n     * @param int $steps number of steps the cursor should be moved backward\n     */\n    public static function moveCursorBackward($steps = 1)\n    {\n        echo \"\\033[\" . (int) $steps . 'D';\n    }\n\n    /**\n     * Moves the terminal cursor to the beginning of the next line by sending ANSI control code CNL to the terminal.\n     * @param int $lines number of lines the cursor should be moved down\n     */\n    public static function moveCursorNextLine($lines = 1)\n    {\n        echo \"\\033[\" . (int) $lines . 'E';\n    }\n\n    /**\n     * Moves the terminal cursor to the beginning of the previous line by sending ANSI control code CPL to the terminal.\n     * @param int $lines number of lines the cursor should be moved up\n     */\n    public static function moveCursorPrevLine($lines = 1)\n    {\n        echo \"\\033[\" . (int) $lines . 'F';\n    }\n\n    /**\n     * Moves the cursor to an absolute position given as column and row by sending ANSI control code CUP or CHA to the terminal.\n     * @param int $column 1-based column number, 1 is the left edge of the screen.\n     * @param int|null $row 1-based row number, 1 is the top edge of the screen. if not set, will move cursor only in current line.\n     */\n    public static function moveCursorTo($column, $row = null)\n    {\n        if ($row === null) {\n            echo \"\\033[\" . (int) $column . 'G';\n        } else {\n            echo \"\\033[\" . (int) $row . ';' . (int) $column . 'H';\n        }\n    }\n\n    /**\n     * Scrolls whole page up by sending ANSI control code SU to the terminal.\n     * New lines are added at the bottom. This is not supported by ANSI.SYS used in windows.\n     * @param int $lines number of lines to scroll up\n     */\n    public static function scrollUp($lines = 1)\n    {\n        echo \"\\033[\" . (int) $lines . 'S';\n    }\n\n    /**\n     * Scrolls whole page down by sending ANSI control code SD to the terminal.\n     * New lines are added at the top. This is not supported by ANSI.SYS used in windows.\n     * @param int $lines number of lines to scroll down\n     */\n    public static function scrollDown($lines = 1)\n    {\n        echo \"\\033[\" . (int) $lines . 'T';\n    }\n\n    /**\n     * Saves the current cursor position by sending ANSI control code SCP to the terminal.\n     * Position can then be restored with [[restoreCursorPosition()]].\n     */\n    public static function saveCursorPosition()\n    {\n        echo \"\\033[s\";\n    }\n\n    /**\n     * Restores the cursor position saved with [[saveCursorPosition()]] by sending ANSI control code RCP to the terminal.\n     */\n    public static function restoreCursorPosition()\n    {\n        echo \"\\033[u\";\n    }\n\n    /**\n     * Hides the cursor by sending ANSI DECTCEM code ?25l to the terminal.\n     * Use [[showCursor()]] to bring it back.\n     * Do not forget to show cursor when your application exits. Cursor might stay hidden in terminal after exit.\n     */\n    public static function hideCursor()\n    {\n        echo \"\\033[?25l\";\n    }\n\n    /**\n     * Will show a cursor again when it has been hidden by [[hideCursor()]]  by sending ANSI DECTCEM code ?25h to the terminal.\n     */\n    public static function showCursor()\n    {\n        echo \"\\033[?25h\";\n    }\n\n    /**\n     * Clears entire screen content by sending ANSI control code ED with argument 2 to the terminal.\n     * Cursor position will not be changed.\n     * **Note:** ANSI.SYS implementation used in windows will reset cursor position to upper left corner of the screen.\n     */\n    public static function clearScreen()\n    {\n        echo \"\\033[2J\";\n    }\n\n    /**\n     * Clears text from cursor to the beginning of the screen by sending ANSI control code ED with argument 1 to the terminal.\n     * Cursor position will not be changed.\n     */\n    public static function clearScreenBeforeCursor()\n    {\n        echo \"\\033[1J\";\n    }\n\n    /**\n     * Clears text from cursor to the end of the screen by sending ANSI control code ED with argument 0 to the terminal.\n     * Cursor position will not be changed.\n     */\n    public static function clearScreenAfterCursor()\n    {\n        echo \"\\033[0J\";\n    }\n\n    /**\n     * Clears the line, the cursor is currently on by sending ANSI control code EL with argument 2 to the terminal.\n     * Cursor position will not be changed.\n     */\n    public static function clearLine()\n    {\n        echo \"\\033[2K\";\n    }\n\n    /**\n     * Clears text from cursor position to the beginning of the line by sending ANSI control code EL with argument 1 to the terminal.\n     * Cursor position will not be changed.\n     */\n    public static function clearLineBeforeCursor()\n    {\n        echo \"\\033[1K\";\n    }\n\n    /**\n     * Clears text from cursor position to the end of the line by sending ANSI control code EL with argument 0 to the terminal.\n     * Cursor position will not be changed.\n     */\n    public static function clearLineAfterCursor()\n    {\n        echo \"\\033[0K\";\n    }\n\n    /**\n     * Returns the ANSI format code.\n     *\n     * @param array $format An array containing formatting values.\n     * You can pass any of the `FG_*`, `BG_*` and `TEXT_*` constants\n     * and also [[xtermFgColor]] and [[xtermBgColor]] to specify a format.\n     * @return string The ANSI format code according to the given formatting constants.\n     */\n    public static function ansiFormatCode($format)\n    {\n        return \"\\033[\" . implode(';', $format) . 'm';\n    }\n\n    /**\n     * Echoes an ANSI format code that affects the formatting of any text that is printed afterwards.\n     *\n     * @param array $format An array containing formatting values.\n     * You can pass any of the `FG_*`, `BG_*` and `TEXT_*` constants\n     * and also [[xtermFgColor]] and [[xtermBgColor]] to specify a format.\n     * @see ansiFormatCode()\n     * @see endAnsiFormat()\n     */\n    public static function beginAnsiFormat($format)\n    {\n        echo \"\\033[\" . implode(';', $format) . 'm';\n    }\n\n    /**\n     * Resets any ANSI format set by previous method [[beginAnsiFormat()]]\n     * Any output after this will have default text format.\n     * This is equal to calling.\n     *\n     * ```\n     * echo Console::ansiFormatCode([Console::RESET])\n     * ```\n     */\n    public static function endAnsiFormat()\n    {\n        echo \"\\033[0m\";\n    }\n\n    /**\n     * Will return a string formatted with the given ANSI style.\n     *\n     * @param string $string the string to be formatted\n     * @param array $format An array containing formatting values.\n     * You can pass any of the `FG_*`, `BG_*` and `TEXT_*` constants\n     * and also [[xtermFgColor]] and [[xtermBgColor]] to specify a format.\n     * @return string\n     */\n    public static function ansiFormat($string, $format = [])\n    {\n        $code = implode(';', $format);\n\n        return \"\\033[0m\" . ($code !== '' ? \"\\033[\" . $code . 'm' : '') . $string . \"\\033[0m\";\n    }\n\n    /**\n     * Returns the ansi format code for xterm foreground color.\n     *\n     * You can pass the return value of this to one of the formatting methods:\n     * [[ansiFormat]], [[ansiFormatCode]], [[beginAnsiFormat]].\n     *\n     * @param int $colorCode xterm color code\n     * @return string\n     * @see https://en.wikipedia.org/wiki/Talk:ANSI_escape_code#xterm-256colors\n     */\n    public static function xtermFgColor($colorCode)\n    {\n        return '38;5;' . $colorCode;\n    }\n\n    /**\n     * Returns the ansi format code for xterm background color.\n     *\n     * You can pass the return value of this to one of the formatting methods:\n     * [[ansiFormat]], [[ansiFormatCode]], [[beginAnsiFormat]].\n     *\n     * @param int $colorCode xterm color code\n     * @return string\n     * @see https://en.wikipedia.org/wiki/Talk:ANSI_escape_code#xterm-256colors\n     */\n    public static function xtermBgColor($colorCode)\n    {\n        return '48;5;' . $colorCode;\n    }\n\n    /**\n     * Strips ANSI control codes from a string.\n     *\n     * @param string $string String to strip\n     * @return string\n     */\n    public static function stripAnsiFormat($string)\n    {\n        return preg_replace(self::ansiCodesPattern(), '', (string)$string);\n    }\n\n    /**\n     * Returns the length of the string without ANSI color codes.\n     * @param string $string the string to measure\n     * @return int the length of the string not counting ANSI format characters\n     */\n    public static function ansiStrlen($string)\n    {\n        return mb_strlen(static::stripAnsiFormat($string));\n    }\n\n    /**\n     * Returns the width of the string without ANSI color codes.\n     * @param string $string the string to measure\n     * @return int the width of the string not counting ANSI format characters\n     * @since 2.0.36\n     */\n    public static function ansiStrwidth($string)\n    {\n        return mb_strwidth(static::stripAnsiFormat($string), Yii::$app->charset);\n    }\n\n    /**\n     * Returns the portion with ANSI color codes of string specified by the start and length parameters.\n     * If string has color codes, then will be return \"TEXT_COLOR + TEXT_STRING + DEFAULT_COLOR\",\n     * else will be simple \"TEXT_STRING\".\n     * @param string $string\n     * @param int $start\n     * @param int $length\n     * @return string\n     */\n    public static function ansiColorizedSubstr($string, $start, $length)\n    {\n        if ($start < 0 || $length <= 0) {\n            return '';\n        }\n\n        $textItems = preg_split(self::ansiCodesPattern(), (string)$string);\n\n        preg_match_all(self::ansiCodesPattern(), (string)$string, $colors);\n        $colors = count($colors) ? $colors[0] : [];\n        array_unshift($colors, '');\n\n        $result = '';\n        $curPos = 0;\n        $inRange = false;\n\n        foreach ($textItems as $k => $textItem) {\n            $color = $colors[$k];\n\n            if ($curPos <= $start && $start < $curPos + Console::ansiStrwidth($textItem)) {\n                $text = mb_substr($textItem, $start - $curPos, null, Yii::$app->charset);\n                $inRange = true;\n            } else {\n                $text = $textItem;\n            }\n\n            if ($inRange) {\n                $result .= $color . $text;\n                $diff = $length - Console::ansiStrwidth($result);\n                if ($diff <= 0) {\n                    if ($diff < 0) {\n                        $result = mb_substr($result, 0, $diff, Yii::$app->charset);\n                    }\n                    $defaultColor = static::renderColoredString('%n');\n                    if ($color && $color != $defaultColor) {\n                        $result .= $defaultColor;\n                    }\n                    break;\n                }\n            }\n\n            $curPos += mb_strlen($textItem, Yii::$app->charset);\n        }\n\n        return $result;\n    }\n\n    private static function ansiCodesPattern()\n    {\n        return /** @lang PhpRegExp */ '/\\033\\[[\\d;?]*\\w/';\n    }\n\n    /**\n     * Converts an ANSI formatted string to HTML.\n     *\n     * Note: xTerm 256 bit colors are currently not supported.\n     *\n     * @param string $string the string to convert.\n     * @param array $styleMap an optional mapping of ANSI control codes such as\n     * FG\\_*COLOR* or [[BOLD]] to a set of css style definitions.\n     * The CSS style definitions are represented as an array where the array keys correspond\n     * to the css style attribute names and the values are the css values.\n     * values may be arrays that will be merged and imploded with `' '` when rendered.\n     * @return string HTML representation of the ANSI formatted string\n     */\n    public static function ansiToHtml($string, $styleMap = [])\n    {\n        $styleMap = [\n            // https://www.w3.org/TR/CSS2/syndata.html#value-def-color\n            self::FG_BLACK => ['color' => 'black'],\n            self::FG_BLUE => ['color' => 'blue'],\n            self::FG_CYAN => ['color' => 'aqua'],\n            self::FG_GREEN => ['color' => 'lime'],\n            self::FG_GREY => ['color' => 'silver'],\n            // https://meyerweb.com/eric/thoughts/2014/06/19/rebeccapurple/\n            // https://drafts.csswg.org/css-color/#valuedef-rebeccapurple\n            self::FG_PURPLE => ['color' => 'rebeccapurple'],\n            self::FG_RED => ['color' => 'red'],\n            self::FG_YELLOW => ['color' => 'yellow'],\n            self::BG_BLACK => ['background-color' => 'black'],\n            self::BG_BLUE => ['background-color' => 'blue'],\n            self::BG_CYAN => ['background-color' => 'aqua'],\n            self::BG_GREEN => ['background-color' => 'lime'],\n            self::BG_GREY => ['background-color' => 'silver'],\n            self::BG_PURPLE => ['background-color' => 'rebeccapurple'],\n            self::BG_RED => ['background-color' => 'red'],\n            self::BG_YELLOW => ['background-color' => 'yellow'],\n            self::BOLD => ['font-weight' => 'bold'],\n            self::ITALIC => ['font-style' => 'italic'],\n            self::UNDERLINE => ['text-decoration' => ['underline']],\n            self::OVERLINED => ['text-decoration' => ['overline']],\n            self::CROSSED_OUT => ['text-decoration' => ['line-through']],\n            self::BLINK => ['text-decoration' => ['blink']],\n            self::CONCEALED => ['visibility' => 'hidden'],\n        ] + $styleMap;\n\n        $tags = 0;\n        $result = preg_replace_callback(\n            '/\\033\\[([\\d;]+)m/',\n            function ($ansi) use (&$tags, $styleMap) {\n                $style = [];\n                $reset = false;\n                $negative = false;\n                foreach (explode(';', $ansi[1]) as $controlCode) {\n                    if ($controlCode == 0) {\n                        $style = [];\n                        $reset = true;\n                    } elseif ($controlCode == self::NEGATIVE) {\n                        $negative = true;\n                    } elseif (isset($styleMap[$controlCode])) {\n                        $style[] = $styleMap[$controlCode];\n                    }\n                }\n\n                $return = '';\n                while ($reset && $tags > 0) {\n                    $return .= '</span>';\n                    $tags--;\n                }\n                if (empty($style)) {\n                    return $return;\n                }\n\n                $currentStyle = [];\n                foreach ($style as $content) {\n                    $currentStyle = ArrayHelper::merge($currentStyle, $content);\n                }\n\n                // if negative is set, invert background and foreground\n                if ($negative) {\n                    if (isset($currentStyle['color'])) {\n                        $fgColor = $currentStyle['color'];\n                        unset($currentStyle['color']);\n                    }\n                    if (isset($currentStyle['background-color'])) {\n                        $bgColor = $currentStyle['background-color'];\n                        unset($currentStyle['background-color']);\n                    }\n                    if (isset($fgColor)) {\n                        $currentStyle['background-color'] = $fgColor;\n                    }\n                    if (isset($bgColor)) {\n                        $currentStyle['color'] = $bgColor;\n                    }\n                }\n\n                $styleString = '';\n                foreach ($currentStyle as $name => $value) {\n                    if (is_array($value)) {\n                        $value = implode(' ', $value);\n                    }\n                    $styleString .= \"$name: $value;\";\n                }\n                $tags++;\n                return \"$return<span style=\\\"$styleString\\\">\";\n            },\n            $string\n        );\n        while ($tags > 0) {\n            $result .= '</span>';\n            $tags--;\n        }\n\n        return $result;\n    }\n\n    /**\n     * Converts Markdown to be better readable in console environments by applying some ANSI format.\n     * @param string $markdown the markdown string.\n     * @return string the parsed result as ANSI formatted string.\n     */\n    public static function markdownToAnsi($markdown)\n    {\n        $parser = new ConsoleMarkdown();\n        return $parser->parse($markdown);\n    }\n\n    /**\n     * Converts a string to ansi formatted by replacing patterns like %y (for yellow) with ansi control codes.\n     *\n     * Uses almost the same syntax as https://github.com/pear/Console_Color2/blob/master/Console/Color2.php\n     * The conversion table is: ('bold' meaning 'light' on some\n     * terminals). It's almost the same conversion table irssi uses.\n     * <pre>\n     *                  text      text            background\n     *      ------------------------------------------------\n     *      %k %K %0    black     dark grey       black\n     *      %r %R %1    red       bold red        red\n     *      %g %G %2    green     bold green      green\n     *      %y %Y %3    yellow    bold yellow     yellow\n     *      %b %B %4    blue      bold blue       blue\n     *      %m %M %5    magenta   bold magenta    magenta\n     *      %p %P       magenta (think: purple)\n     *      %c %C %6    cyan      bold cyan       cyan\n     *      %w %W %7    white     bold white      white\n     *\n     *      %F     Blinking, Flashing\n     *      %U     Underline\n     *      %8     Reverse\n     *      %_,%9  Bold\n     *\n     *      %n     Resets the color\n     *      %%     A single %\n     * </pre>\n     * First param is the string to convert, second is an optional flag if\n     * colors should be used. It defaults to true, if set to false, the\n     * color codes will just be removed (And %% will be transformed into %)\n     *\n     * @param string $string String to convert\n     * @param bool $colored Should the string be colored?\n     * @return string\n     */\n    public static function renderColoredString($string, $colored = true)\n    {\n        // TODO rework/refactor according to https://github.com/yiisoft/yii2/issues/746\n        static $conversions = [\n            '%y' => [self::FG_YELLOW],\n            '%g' => [self::FG_GREEN],\n            '%b' => [self::FG_BLUE],\n            '%r' => [self::FG_RED],\n            '%p' => [self::FG_PURPLE],\n            '%m' => [self::FG_PURPLE],\n            '%c' => [self::FG_CYAN],\n            '%w' => [self::FG_GREY],\n            '%k' => [self::FG_BLACK],\n            '%n' => [0], // reset\n            '%Y' => [self::FG_YELLOW, self::BOLD],\n            '%G' => [self::FG_GREEN, self::BOLD],\n            '%B' => [self::FG_BLUE, self::BOLD],\n            '%R' => [self::FG_RED, self::BOLD],\n            '%P' => [self::FG_PURPLE, self::BOLD],\n            '%M' => [self::FG_PURPLE, self::BOLD],\n            '%C' => [self::FG_CYAN, self::BOLD],\n            '%W' => [self::FG_GREY, self::BOLD],\n            '%K' => [self::FG_BLACK, self::BOLD],\n            '%N' => [0, self::BOLD],\n            '%3' => [self::BG_YELLOW],\n            '%2' => [self::BG_GREEN],\n            '%4' => [self::BG_BLUE],\n            '%1' => [self::BG_RED],\n            '%5' => [self::BG_PURPLE],\n            '%6' => [self::BG_CYAN],\n            '%7' => [self::BG_GREY],\n            '%0' => [self::BG_BLACK],\n            '%F' => [self::BLINK],\n            '%U' => [self::UNDERLINE],\n            '%8' => [self::NEGATIVE],\n            '%9' => [self::BOLD],\n            '%_' => [self::BOLD],\n        ];\n\n        if ($colored) {\n            $string = str_replace('%%', '% ', $string);\n            foreach ($conversions as $key => $value) {\n                $string = str_replace(\n                    $key,\n                    static::ansiFormatCode($value),\n                    $string\n                );\n            }\n            $string = str_replace('% ', '%', $string);\n        } else {\n            $string = preg_replace('/%((%)|.)/', '$2', $string);\n        }\n\n        return $string;\n    }\n\n    /**\n     * Escapes % so they don't get interpreted as color codes when\n     * the string is parsed by [[renderColoredString]].\n     *\n     * @param string $string String to escape\n     *\n     * @return string\n     */\n    public static function escape($string)\n    {\n        // TODO rework/refactor according to https://github.com/yiisoft/yii2/issues/746\n        return str_replace('%', '%%', $string);\n    }\n\n    /**\n     * Returns true if the stream supports colorization. ANSI colors are disabled if not supported by the stream.\n     *\n     * - windows without ansicon\n     * - not tty consoles\n     *\n     * @param mixed $stream\n     * @return bool true if the stream supports ANSI colors, otherwise false.\n     */\n    public static function streamSupportsAnsiColors($stream)\n    {\n        return DIRECTORY_SEPARATOR === '\\\\'\n            ? getenv('ANSICON') !== false || getenv('ConEmuANSI') === 'ON'\n            : function_exists('posix_isatty') && @posix_isatty($stream);\n    }\n\n    /**\n     * Returns true if the console is running on windows.\n     * @return bool\n     */\n    public static function isRunningOnWindows()\n    {\n        return DIRECTORY_SEPARATOR === '\\\\';\n    }\n\n    /**\n     * Returns terminal screen size.\n     *\n     * Usage:\n     *\n     * ```\n     * list($width, $height) = ConsoleHelper::getScreenSize();\n     * ```\n     *\n     * @param bool $refresh whether to force checking and not re-use cached size value.\n     * This is useful to detect changing window size while the application is running but may\n     * not get up to date values on every terminal.\n     * @return array|bool An array of ($width, $height) or false when it was not able to determine size.\n     */\n    public static function getScreenSize($refresh = false)\n    {\n        static $size;\n        static $execDisabled;\n\n        if ($size !== null && ($execDisabled || !$refresh)) {\n            return $size;\n        }\n\n        if ($execDisabled === null) {\n            $execDisabled = !function_exists('ini_get') || preg_match('/(\\bexec\\b)/i', ini_get('disable_functions'));\n            if ($execDisabled) {\n                return $size = false;\n            }\n        }\n\n        if (static::isRunningOnWindows()) {\n            $output = [];\n            exec('mode con', $output);\n            if (isset($output[1]) && strpos($output[1], 'CON') !== false) {\n                return $size = [(int) preg_replace('~\\D~', '', $output[4]), (int) preg_replace('~\\D~', '', $output[3])];\n            }\n        } else {\n            // try stty if available\n            $stty = [];\n            if (exec('stty -a 2>&1', $stty)) {\n                $stty = implode(' ', $stty);\n\n                // Linux stty output\n                if (preg_match('/rows\\s+(\\d+);\\s*columns\\s+(\\d+);/mi', $stty, $matches)) {\n                    return $size = [(int) $matches[2], (int) $matches[1]];\n                }\n\n                // MacOS stty output\n                if (preg_match('/(\\d+)\\s+rows;\\s*(\\d+)\\s+columns;/mi', $stty, $matches)) {\n                    return $size = [(int) $matches[2], (int) $matches[1]];\n                }\n            }\n\n            // fallback to tput, which may not be updated on terminal resize\n            if (($width = (int) exec('tput cols 2>&1')) > 0 && ($height = (int) exec('tput lines 2>&1')) > 0) {\n                return $size = [$width, $height];\n            }\n\n            // fallback to ENV variables, which may not be updated on terminal resize\n            if (($width = (int) getenv('COLUMNS')) > 0 && ($height = (int) getenv('LINES')) > 0) {\n                return $size = [$width, $height];\n            }\n        }\n\n        return $size = false;\n    }\n\n    /**\n     * Word wrap text with indentation to fit the screen size.\n     *\n     * If screen size could not be detected, or the indentation is greater than the screen size, the text will not be wrapped.\n     *\n     * The first line will **not** be indented, so `Console::wrapText(\"Lorem ipsum dolor sit amet.\", 4)` will result in the\n     * following output, given the screen width is 16 characters:\n     *\n     * ```\n     * Lorem ipsum\n     *     dolor sit\n     *     amet.\n     * ```\n     *\n     * @param string $text the text to be wrapped\n     * @param int $indent number of spaces to use for indentation.\n     * @param bool $refresh whether to force refresh of screen size.\n     * This will be passed to [[getScreenSize()]].\n     * @return string the wrapped text.\n     * @since 2.0.4\n     */\n    public static function wrapText($text, $indent = 0, $refresh = false)\n    {\n        $size = static::getScreenSize($refresh);\n        if ($size === false || $size[0] <= $indent) {\n            return $text;\n        }\n        $pad = str_repeat(' ', $indent);\n        $lines = explode(\"\\n\", wordwrap($text, $size[0] - $indent, \"\\n\"));\n        $first = true;\n        foreach ($lines as $i => $line) {\n            if ($first) {\n                $first = false;\n                continue;\n            }\n            $lines[$i] = $pad . $line;\n        }\n\n        return implode(\"\\n\", $lines);\n    }\n\n    /**\n     * Gets input from STDIN and returns a string right-trimmed for EOLs.\n     *\n     * @param bool $raw If set to true, returns the raw string without trimming\n     * @return string the string read from stdin\n     */\n    public static function stdin($raw = false)\n    {\n        return $raw ? fgets(\\STDIN) : rtrim(fgets(\\STDIN), PHP_EOL);\n    }\n\n    /**\n     * Prints a string to STDOUT.\n     *\n     * @param string $string the string to print\n     * @return int|bool Number of bytes printed or false on error\n     */\n    public static function stdout($string)\n    {\n        return fwrite(\\STDOUT, $string);\n    }\n\n    /**\n     * Prints a string to STDERR.\n     *\n     * @param string $string the string to print\n     * @return int|bool Number of bytes printed or false on error\n     */\n    public static function stderr($string)\n    {\n        return fwrite(\\STDERR, $string);\n    }\n\n    /**\n     * Asks the user for input. Ends when the user types a carriage return (PHP_EOL). Optionally, It also provides a\n     * prompt.\n     *\n     * @param string|null $prompt the prompt to display before waiting for input (optional)\n     * @return string the user's input\n     */\n    public static function input($prompt = null)\n    {\n        if (isset($prompt)) {\n            static::stdout($prompt);\n        }\n\n        return static::stdin();\n    }\n\n    /**\n     * Prints text to STDOUT appended with a carriage return (PHP_EOL).\n     *\n     * @param string|null $string the text to print\n     * @return int|bool number of bytes printed or false on error.\n     */\n    public static function output($string = null)\n    {\n        return static::stdout($string . PHP_EOL);\n    }\n\n    /**\n     * Prints text to STDERR appended with a carriage return (PHP_EOL).\n     *\n     * @param string|null $string the text to print\n     * @return int|bool number of bytes printed or false on error.\n     */\n    public static function error($string = null)\n    {\n        return static::stderr($string . PHP_EOL);\n    }\n\n    /**\n     * Prompts the user for input and validates it.\n     *\n     * @param string $text prompt string\n     * @param array $options the options to validate the input:\n     *\n     * - `required`: whether it is required or not\n     * - `default`: default value if no input is inserted by the user\n     * - `pattern`: regular expression pattern to validate user input\n     * - `validator`: a callable function to validate input. The function must accept two parameters:\n     * - `input`: the user input to validate\n     * - `error`: the error value passed by reference if validation failed.\n     *\n     * @return string the user input\n     */\n    public static function prompt($text, $options = [])\n    {\n        $options = ArrayHelper::merge(\n            [\n                'required' => false,\n                'default' => null,\n                'pattern' => null,\n                'validator' => null,\n                'error' => 'Invalid input.',\n            ],\n            $options\n        );\n        $error = null;\n\n        top:\n        $input = $options['default']\n            ? static::input(\"$text [\" . $options['default'] . '] ')\n            : static::input(\"$text \");\n\n        if ($input === '') {\n            if (isset($options['default'])) {\n                $input = $options['default'];\n            } elseif ($options['required']) {\n                static::output($options['error']);\n                goto top;\n            }\n        } elseif ($options['pattern'] && !preg_match($options['pattern'], $input)) {\n            static::output($options['error']);\n            goto top;\n        } elseif ($options['validator'] && !call_user_func_array($options['validator'], [$input, &$error])) {\n            static::output(isset($error) ? $error : $options['error']);\n            goto top;\n        }\n\n        return $input;\n    }\n\n    /**\n     * Asks user to confirm by typing y or n.\n     *\n     * A typical usage looks like the following:\n     *\n     * ```\n     * if (Console::confirm(\"Are you sure?\")) {\n     *     echo \"user typed yes\\n\";\n     * } else {\n     *     echo \"user typed no\\n\";\n     * }\n     * ```\n     *\n     * @param string $message to print out before waiting for user input\n     * @param bool $default this value is returned if no selection is made.\n     * @return bool whether user confirmed\n     */\n    public static function confirm($message, $default = false)\n    {\n        while (true) {\n            static::stdout($message . ' (yes|no) [' . ($default ? 'yes' : 'no') . ']:');\n            $input = trim(static::stdin());\n\n            if (empty($input)) {\n                return $default;\n            }\n\n            if (!strcasecmp($input, 'y') || !strcasecmp($input, 'yes')) {\n                return true;\n            }\n\n            if (!strcasecmp($input, 'n') || !strcasecmp($input, 'no')) {\n                return false;\n            }\n        }\n    }\n\n    /**\n     * Gives the user an option to choose from. Giving '?' as an input will show\n     * a list of options to choose from and their explanations.\n     *\n     * @param string $prompt the prompt message\n     * @param array $options Key-value array of options to choose from. Key is what is inputed and used, value is\n     * what's displayed to end user by help command.\n     * @param string|null $default value to use when the user doesn't provide an option.\n     * If the default is `null`, the user is required to select an option.\n     *\n     * @return string An option character the user chose\n     * @since 2.0.49 Added the $default argument\n     */\n    public static function select($prompt, $options = [], $default = null)\n    {\n        top:\n        static::stdout(\"$prompt (\" . implode(',', array_keys($options)) . ',?)'\n            . ($default !== null ? '[' . $default . ']' : '') . ': ');\n        $input = static::stdin();\n        if ($input === '?') {\n            foreach ($options as $key => $value) {\n                static::output(\" $key - $value\");\n            }\n            static::output(' ? - Show help');\n            goto top;\n        } elseif ($default !== null && $input === '') {\n            return $default;\n        } elseif (!array_key_exists($input, $options)) {\n            goto top;\n        }\n\n        return $input;\n    }\n\n    private static $_progressStart;\n    private static $_progressWidth;\n    private static $_progressPrefix;\n    private static $_progressEta;\n    private static $_progressEtaLastDone = 0;\n    private static $_progressEtaLastUpdate;\n\n    /**\n     * Starts display of a progress bar on screen.\n     *\n     * This bar will be updated by [[updateProgress()]] and may be ended by [[endProgress()]].\n     *\n     * The following example shows a simple usage of a progress bar:\n     *\n     * ```\n     * Console::startProgress(0, 1000);\n     * for ($n = 1; $n <= 1000; $n++) {\n     *     usleep(1000);\n     *     Console::updateProgress($n, 1000);\n     * }\n     * Console::endProgress();\n     * ```\n     *\n     * Git clone like progress (showing only status information):\n     *\n     * ```\n     * Console::startProgress(0, 1000, 'Counting objects: ', false);\n     * for ($n = 1; $n <= 1000; $n++) {\n     *     usleep(1000);\n     *     Console::updateProgress($n, 1000);\n     * }\n     * Console::endProgress(\"done.\" . PHP_EOL);\n     * ```\n     *\n     * @param int $done the number of items that are completed.\n     * @param int $total the total value of items that are to be done.\n     * @param string $prefix an optional string to display before the progress bar.\n     * Default to empty string which results in no prefix to be displayed.\n     * @param int|float|bool|null $width optional width of the progressbar. This can be an integer representing\n     * the number of characters to display for the progress bar or a float between 0 and 1 representing the\n     * percentage of screen with the progress bar may take. It can also be set to false to disable the\n     * bar and only show progress information like percent, number of items and ETA.\n     * If not set, the bar will be as wide as the screen. Screen size will be detected using [[getScreenSize()]].\n     * @see startProgress\n     * @see updateProgress\n     * @see endProgress\n     */\n    public static function startProgress($done, $total, $prefix = '', $width = null)\n    {\n        self::$_progressStart = time();\n        self::$_progressWidth = $width;\n        self::$_progressPrefix = $prefix;\n        self::$_progressEta = null;\n        self::$_progressEtaLastDone = 0;\n        self::$_progressEtaLastUpdate = time();\n\n        static::updateProgress($done, $total);\n    }\n\n    /**\n     * Updates a progress bar that has been started by [[startProgress()]].\n     *\n     * @param int $done the number of items that are completed.\n     * @param int $total the total value of items that are to be done.\n     * @param string|null $prefix an optional string to display before the progress bar.\n     * Defaults to null meaning the prefix specified by [[startProgress()]] will be used.\n     * If prefix is specified it will update the prefix that will be used by later calls.\n     * @see startProgress\n     * @see endProgress\n     */\n    public static function updateProgress($done, $total, $prefix = null)\n    {\n        if ($prefix === null) {\n            $prefix = self::$_progressPrefix;\n        } else {\n            self::$_progressPrefix = $prefix;\n        }\n        $width = static::getProgressbarWidth($prefix);\n        $percent = ($total == 0) ? 1 : $done / $total;\n        $info = sprintf('%d%% (%d/%d)', $percent * 100, $done, $total);\n        self::setETA($done, $total);\n        $info .= self::$_progressEta === null ? ' ETA: n/a' : sprintf(' ETA: %d sec.', self::$_progressEta);\n\n        // Number extra characters outputted. These are opening [, closing ], and space before info\n        // Since Windows uses \\r\\n\\ for line endings, there's one more in the case\n        $extraChars = static::isRunningOnWindows() ? 4 : 3;\n        $width -= $extraChars + static::ansiStrlen($info);\n        // skipping progress bar on very small display or if forced to skip\n        if ($width < 5) {\n            static::stdout(\"\\r$prefix$info   \");\n        } else {\n            if ($percent < 0) {\n                $percent = 0;\n            } elseif ($percent > 1) {\n                $percent = 1;\n            }\n            $bar = floor($percent * $width);\n            $status = str_repeat('=', $bar);\n            if ($bar < $width) {\n                $status .= '>';\n                $status .= str_repeat(' ', $width - $bar - 1);\n            }\n            static::stdout(\"\\r$prefix\" . \"[$status] $info\");\n        }\n        flush();\n    }\n\n    /**\n     * Return width of the progressbar\n     * @param string $prefix an optional string to display before the progress bar.\n     * @see updateProgress\n     * @return int screen width\n     * @since 2.0.14\n     */\n    private static function getProgressbarWidth($prefix)\n    {\n        $width = self::$_progressWidth;\n\n        if ($width === false) {\n            return 0;\n        }\n\n        $screenSize = static::getScreenSize(true);\n        if ($screenSize === false && $width < 1) {\n            return 0;\n        }\n\n        if ($width === null) {\n            $width = $screenSize[0];\n        } elseif ($width > 0 && $width < 1) {\n            $width = floor($screenSize[0] * $width);\n        }\n\n        $width -= static::ansiStrlen($prefix);\n\n        return $width;\n    }\n\n    /**\n     * Calculate $_progressEta, $_progressEtaLastUpdate and $_progressEtaLastDone\n     * @param int $done the number of items that are completed.\n     * @param int $total the total value of items that are to be done.\n     * @see updateProgress\n     * @since 2.0.14\n     */\n    private static function setETA($done, $total)\n    {\n        if ($done > $total || $done == 0) {\n            self::$_progressEta = null;\n            self::$_progressEtaLastUpdate = time();\n            return;\n        }\n\n        if ($done < $total && (time() - self::$_progressEtaLastUpdate > 1 && $done > self::$_progressEtaLastDone)) {\n            $rate = (time() - (self::$_progressEtaLastUpdate ?: self::$_progressStart)) / ($done - self::$_progressEtaLastDone);\n            self::$_progressEta = $rate * ($total - $done);\n            self::$_progressEtaLastUpdate = time();\n            self::$_progressEtaLastDone = $done;\n        }\n    }\n\n    /**\n     * Ends a progress bar that has been started by [[startProgress()]].\n     *\n     * @param string|bool $remove This can be `false` to leave the progress bar on screen and just print a newline.\n     * If set to `true`, the line of the progress bar will be cleared. This may also be a string to be displayed instead\n     * of the progress bar.\n     * @param bool $keepPrefix whether to keep the prefix that has been specified for the progressbar when progressbar\n     * gets removed. Defaults to true.\n     * @see startProgress\n     * @see updateProgress\n     */\n    public static function endProgress($remove = false, $keepPrefix = true)\n    {\n        if ($remove === false) {\n            static::stdout(PHP_EOL);\n        } else {\n            if (static::streamSupportsAnsiColors(STDOUT)) {\n                static::clearLine();\n            }\n            static::stdout(\"\\r\" . ($keepPrefix ? self::$_progressPrefix : '') . (is_string($remove) ? $remove : ''));\n        }\n        flush();\n\n        self::$_progressStart = null;\n        self::$_progressWidth = null;\n        self::$_progressPrefix = '';\n        self::$_progressEta = null;\n        self::$_progressEtaLastDone = 0;\n        self::$_progressEtaLastUpdate = null;\n    }\n\n    /**\n     * Generates a summary of the validation errors.\n     * @param Model|Model[] $models the model(s) whose validation errors are to be displayed.\n     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:\n     *\n     * - showAllErrors: boolean, if set to true every error message for each attribute will be shown otherwise\n     *   only the first error message for each attribute will be shown. Defaults to `false`.\n     *\n     * @return string the generated error summary\n     * @since 2.0.14\n     */\n    public static function errorSummary($models, $options = [])\n    {\n        $showAllErrors = ArrayHelper::remove($options, 'showAllErrors', false);\n        $lines = self::collectErrors($models, $showAllErrors);\n\n        return implode(PHP_EOL, $lines);\n    }\n\n    /**\n     * Return array of the validation errors\n     * @param Model|Model[] $models the model(s) whose validation errors are to be displayed.\n     * @param $showAllErrors boolean, if set to true every error message for each attribute will be shown otherwise\n     * only the first error message for each attribute will be shown.\n     * @return array of the validation errors\n     * @since 2.0.14\n     */\n    private static function collectErrors($models, $showAllErrors)\n    {\n        $lines = [];\n        if (!is_array($models)) {\n            $models = [$models];\n        }\n\n        foreach ($models as $model) {\n            $lines = array_unique(array_merge($lines, $model->getErrorSummary($showAllErrors)));\n        }\n\n        return $lines;\n    }\n}\n"
  },
  {
    "path": "framework/helpers/BaseFileHelper.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\helpers;\n\nuse Yii;\nuse yii\\base\\ErrorException;\nuse yii\\base\\Exception;\nuse yii\\base\\InvalidArgumentException;\nuse yii\\base\\InvalidConfigException;\n\n/**\n * BaseFileHelper provides concrete implementation for [[FileHelper]].\n *\n * Do not use BaseFileHelper. Use [[FileHelper]] instead.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Alex Makarov <sam@rmcreative.ru>\n * @since 2.0\n */\nclass BaseFileHelper\n{\n    public const PATTERN_NODIR = 1;\n    public const PATTERN_ENDSWITH = 4;\n    public const PATTERN_MUSTBEDIR = 8;\n    public const PATTERN_NEGATIVE = 16;\n    public const PATTERN_CASE_INSENSITIVE = 32;\n    /**\n     * @var string the path (or alias) of a PHP file containing MIME type information.\n     */\n    public static $mimeMagicFile = '@yii/helpers/mimeTypes.php';\n    /**\n     * @var string the path (or alias) of a PHP file containing MIME aliases.\n     * @since 2.0.14\n     */\n    public static $mimeAliasesFile = '@yii/helpers/mimeAliases.php';\n    /**\n     * @var string the path (or alias) of a PHP file containing extensions per MIME type.\n     * @since 2.0.48\n     */\n    public static $mimeExtensionsFile = '@yii/helpers/mimeExtensions.php';\n\n\n    /**\n     * Normalizes a file/directory path.\n     *\n     * The normalization does the following work:\n     *\n     * - Convert all directory separators into `DIRECTORY_SEPARATOR` (e.g. \"\\a/b\\c\" becomes \"/a/b/c\")\n     * - Remove trailing directory separators (e.g. \"/a/b/c/\" becomes \"/a/b/c\")\n     * - Turn multiple consecutive slashes into a single one (e.g. \"/a///b/c\" becomes \"/a/b/c\")\n     * - Remove \"..\" and \".\" based on their meanings (e.g. \"/a/./b/../c\" becomes \"/a/c\")\n     *\n     * Note: For registered stream wrappers, the consecutive slashes rule\n     * and \"..\"/\".\" translations are skipped.\n     *\n     * @param string $path the file/directory path to be normalized\n     * @param string $ds the directory separator to be used in the normalized result. Defaults to `DIRECTORY_SEPARATOR`.\n     * @return string the normalized file/directory path\n     */\n    public static function normalizePath($path, $ds = DIRECTORY_SEPARATOR)\n    {\n        $path = rtrim(strtr($path, '/\\\\', $ds . $ds), $ds);\n        if (strpos($ds . $path, \"{$ds}.\") === false && strpos($path, \"{$ds}{$ds}\") === false) {\n            return $path;\n        }\n        // fix #17235 stream wrappers\n        foreach (stream_get_wrappers() as $protocol) {\n            if (strpos($path, \"{$protocol}://\") === 0) {\n                return $path;\n            }\n        }\n        // the path may contain \".\", \"..\" or double slashes, need to clean them up\n        if (strpos($path, \"{$ds}{$ds}\") === 0 && $ds == '\\\\') {\n            $parts = [$ds];\n        } else {\n            $parts = [];\n        }\n        foreach (explode($ds, $path) as $part) {\n            if ($part === '..' && !empty($parts) && end($parts) !== '..') {\n                array_pop($parts);\n            } elseif ($part === '.' || $part === '' && !empty($parts)) {\n                continue;\n            } else {\n                $parts[] = $part;\n            }\n        }\n        $path = implode($ds, $parts);\n        return $path === '' ? '.' : $path;\n    }\n\n    /**\n     * Returns the localized version of a specified file.\n     *\n     * The searching is based on the specified language code. In particular,\n     * a file with the same name will be looked for under the subdirectory\n     * whose name is the same as the language code. For example, given the file \"path/to/view.php\"\n     * and language code \"zh-CN\", the localized file will be looked for as\n     * \"path/to/zh-CN/view.php\". If the file is not found, it will try a fallback with just a language code that is\n     * \"zh\" i.e. \"path/to/zh/view.php\". If it is not found as well the original file will be returned.\n     *\n     * If the target and the source language codes are the same, the original file will be returned.\n     *\n     * @param string $file the original file\n     * @param string|null $language the target language that the file should be localized to.\n     * If not set, the value of [[\\yii\\base\\Application::language]] will be used.\n     * @param string|null $sourceLanguage the language that the original file is in.\n     * If not set, the value of [[\\yii\\base\\Application::sourceLanguage]] will be used.\n     * @return string the matching localized file, or the original file if the localized version is not found.\n     * If the target and the source language codes are the same, the original file will be returned.\n     */\n    public static function localize($file, $language = null, $sourceLanguage = null)\n    {\n        if ($language === null) {\n            $language = Yii::$app->language;\n        }\n        if ($sourceLanguage === null) {\n            $sourceLanguage = Yii::$app->sourceLanguage;\n        }\n        if ($language === $sourceLanguage) {\n            return $file;\n        }\n        $desiredFile = dirname($file) . DIRECTORY_SEPARATOR . $language . DIRECTORY_SEPARATOR . basename($file);\n        if (is_file($desiredFile)) {\n            return $desiredFile;\n        }\n\n        $language = substr($language, 0, 2);\n        if ($language === $sourceLanguage) {\n            return $file;\n        }\n        $desiredFile = dirname($file) . DIRECTORY_SEPARATOR . $language . DIRECTORY_SEPARATOR . basename($file);\n\n        return is_file($desiredFile) ? $desiredFile : $file;\n    }\n\n    /**\n     * Determines the MIME type of the specified file.\n     * This method will first try to determine the MIME type based on\n     * [finfo_open](https://www.php.net/manual/en/function.finfo-open.php). If the `fileinfo` extension is not installed,\n     * it will fall back to [[getMimeTypeByExtension()]] when `$checkExtension` is true.\n     * @param string $file the file name.\n     * @param string|null $magicFile name of the optional magic database file (or alias), usually something like `/path/to/magic.mime`.\n     * This will be passed as the second parameter to [finfo_open()](https://www.php.net/manual/en/function.finfo-open.php)\n     * when the `fileinfo` extension is installed. If the MIME type is being determined based via [[getMimeTypeByExtension()]]\n     * and this is null, it will use the file specified by [[mimeMagicFile]].\n     * @param bool $checkExtension whether to use the file extension to determine the MIME type in case\n     * `finfo_open()` cannot determine it.\n     * @return string|null the MIME type (e.g. `text/plain`). Null is returned if the MIME type cannot be determined.\n     * @throws InvalidConfigException when the `fileinfo` PHP extension is not installed and `$checkExtension` is `false`.\n     */\n    public static function getMimeType($file, $magicFile = null, $checkExtension = true)\n    {\n        if ($magicFile !== null) {\n            $magicFile = Yii::getAlias($magicFile);\n        }\n        if (!extension_loaded('fileinfo')) {\n            if ($checkExtension) {\n                return static::getMimeTypeByExtension($file, $magicFile);\n            }\n\n            throw new InvalidConfigException('The fileinfo PHP extension is not installed.');\n        }\n\n        $info = finfo_open(FILEINFO_MIME_TYPE, $magicFile);\n\n        if ($info) {\n            $result = finfo_file($info, $file);\n            // @link https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_finfo_close\n            // @link https://github.com/php/php-src/commit/ccb716dcadf60d989db48e4d2963e14d7dc63df3\n            if (PHP_VERSION_ID < 80500) {\n                finfo_close($info);\n            }\n\n            if ($result !== false) {\n                return $result;\n            }\n        }\n\n        return $checkExtension ? static::getMimeTypeByExtension($file, $magicFile) : null;\n    }\n\n    /**\n     * Determines the MIME type based on the extension name of the specified file.\n     * This method will use a local map between extension names and MIME types.\n     * @param string $file the file name.\n     * @param string|null $magicFile the path (or alias) of the file that contains all available MIME type information.\n     * If this is not set, the file specified by [[mimeMagicFile]] will be used.\n     * @return string|null the MIME type. Null is returned if the MIME type cannot be determined.\n     */\n    public static function getMimeTypeByExtension($file, $magicFile = null)\n    {\n        $mimeTypes = static::loadMimeTypes($magicFile);\n\n        if (($ext = pathinfo($file, PATHINFO_EXTENSION)) !== '') {\n            $ext = strtolower($ext);\n            if (isset($mimeTypes[$ext])) {\n                return $mimeTypes[$ext];\n            }\n        }\n\n        return null;\n    }\n\n    /**\n     * Determines the extensions by given MIME type.\n     * This method will use a local map between extension names and MIME types.\n     * @param string $mimeType file MIME type.\n     * @param string|null $magicFile the path (or alias) of the file that contains all available MIME type information.\n     * If this is not set, the file specified by [[mimeMagicFile]] will be used.\n     * @return array the extensions corresponding to the specified MIME type\n     */\n    public static function getExtensionsByMimeType($mimeType, $magicFile = null)\n    {\n        $aliases = static::loadMimeAliases(static::$mimeAliasesFile);\n        if (isset($aliases[$mimeType])) {\n            $mimeType = $aliases[$mimeType];\n        }\n\n        // Note: For backwards compatibility the \"MimeTypes\" file is used.\n        $mimeTypes = static::loadMimeTypes($magicFile);\n        return array_keys($mimeTypes, mb_strtolower($mimeType, 'UTF-8'), true);\n    }\n\n    /**\n     * Determines the most common extension by given MIME type.\n     * This method will use a local map between MIME types and extension names.\n     * @param string $mimeType file MIME type.\n     * @param bool $preferShort return an extension with a maximum of 3 characters.\n     * @param string|null $magicFile the path (or alias) of the file that contains all available MIME type information.\n     * If this is not set, the file specified by [[mimeMagicFile]] will be used.\n     * @return string|null the extensions corresponding to the specified MIME type\n     * @since 2.0.48\n     */\n    public static function getExtensionByMimeType($mimeType, $preferShort = false, $magicFile = null)\n    {\n        $aliases = static::loadMimeAliases(static::$mimeAliasesFile);\n        if (isset($aliases[$mimeType])) {\n            $mimeType = $aliases[$mimeType];\n        }\n\n        $mimeExtensions = static::loadMimeExtensions($magicFile);\n\n        if (!array_key_exists($mimeType, $mimeExtensions)) {\n            return null;\n        }\n\n        $extensions = $mimeExtensions[$mimeType];\n        if (is_array($extensions)) {\n            if ($preferShort) {\n                foreach ($extensions as $extension) {\n                    if (mb_strlen($extension, 'UTF-8') <= 3) {\n                        return $extension;\n                    }\n                }\n            }\n            return $extensions[0];\n        } else {\n            return $extensions;\n        }\n    }\n\n    private static $_mimeTypes = [];\n\n    /**\n     * Loads MIME types from the specified file.\n     * @param string|null $magicFile the path (or alias) of the file that contains all available MIME type information.\n     * If this is not set, the file specified by [[mimeMagicFile]] will be used.\n     * @return array the mapping from file extensions to MIME types\n     */\n    protected static function loadMimeTypes($magicFile)\n    {\n        if ($magicFile === null) {\n            $magicFile = static::$mimeMagicFile;\n        }\n        $magicFile = Yii::getAlias($magicFile);\n        if (!isset(self::$_mimeTypes[$magicFile])) {\n            self::$_mimeTypes[$magicFile] = require $magicFile;\n        }\n\n        return self::$_mimeTypes[$magicFile];\n    }\n\n    private static $_mimeAliases = [];\n\n    /**\n     * Loads MIME aliases from the specified file.\n     * @param string|null $aliasesFile the path (or alias) of the file that contains MIME type aliases.\n     * If this is not set, the file specified by [[mimeAliasesFile]] will be used.\n     * @return array the mapping from file extensions to MIME types\n     * @since 2.0.14\n     */\n    protected static function loadMimeAliases($aliasesFile)\n    {\n        if ($aliasesFile === null) {\n            $aliasesFile = static::$mimeAliasesFile;\n        }\n        $aliasesFile = Yii::getAlias($aliasesFile);\n        if (!isset(self::$_mimeAliases[$aliasesFile])) {\n            self::$_mimeAliases[$aliasesFile] = require $aliasesFile;\n        }\n\n        return self::$_mimeAliases[$aliasesFile];\n    }\n\n    private static $_mimeExtensions = [];\n\n    /**\n     * Loads MIME extensions from the specified file.\n     * @param string|null $extensionsFile the path (or alias) of the file that contains MIME type aliases.\n     * If this is not set, the file specified by [[mimeAliasesFile]] will be used.\n     * @return array the mapping from file extensions to MIME types\n     * @since 2.0.48\n     */\n    protected static function loadMimeExtensions($extensionsFile)\n    {\n        if ($extensionsFile === null) {\n            $extensionsFile = static::$mimeExtensionsFile;\n        }\n        $extensionsFile = Yii::getAlias($extensionsFile);\n        if (!isset(self::$_mimeExtensions[$extensionsFile])) {\n            self::$_mimeExtensions[$extensionsFile] = require $extensionsFile;\n        }\n\n        return self::$_mimeExtensions[$extensionsFile];\n    }\n\n    /**\n     * Copies a whole directory as another one.\n     * The files and sub-directories will also be copied over.\n     * @param string $src the source directory\n     * @param string $dst the destination directory\n     * @param array $options options for directory copy. Valid options are:\n     *\n     * - dirMode: integer, the permission to be set for newly copied directories. Defaults to 0775.\n     * - fileMode:  integer, the permission to be set for newly copied files. Defaults to the current environment setting.\n     * - filter: callback, a PHP callback that is called for each directory or file.\n     *   The signature of the callback should be: `function ($path)`, where `$path` refers the full path to be filtered.\n     *   The callback can return one of the following values:\n     *\n     *   * true: the directory or file will be copied (the \"only\" and \"except\" options will be ignored)\n     *   * false: the directory or file will NOT be copied (the \"only\" and \"except\" options will be ignored)\n     *   * null: the \"only\" and \"except\" options will determine whether the directory or file should be copied\n     *\n     * - only: array, list of patterns that the file paths should match if they want to be copied.\n     *   A path matches a pattern if it contains the pattern string at its end.\n     *   For example, '.php' matches all file paths ending with '.php'.\n     *   Note, the '/' characters in a pattern matches both '/' and '\\' in the paths.\n     *   If a file path matches a pattern in both \"only\" and \"except\", it will NOT be copied.\n     * - except: array, list of patterns that the files or directories should match if they want to be excluded from being copied.\n     *   A path matches a pattern if it contains the pattern string at its end.\n     *   Patterns ending with '/' apply to directory paths only, and patterns not ending with '/'\n     *   apply to file paths only. For example, '/a/b' matches all file paths ending with '/a/b';\n     *   and '.svn/' matches directory paths ending with '.svn'. Note, the '/' characters in a pattern matches\n     *   both '/' and '\\' in the paths.\n     * - caseSensitive: boolean, whether patterns specified at \"only\" or \"except\" should be case sensitive. Defaults to true.\n     * - recursive: boolean, whether the files under the subdirectories should also be copied. Defaults to true.\n     * - beforeCopy: callback, a PHP callback that is called before copying each sub-directory or file.\n     *   If the callback returns false, the copy operation for the sub-directory or file will be cancelled.\n     *   The signature of the callback should be: `function ($from, $to)`, where `$from` is the sub-directory or\n     *   file to be copied from, while `$to` is the copy target.\n     * - afterCopy: callback, a PHP callback that is called after each sub-directory or file is successfully copied.\n     *   The signature of the callback should be: `function ($from, $to)`, where `$from` is the sub-directory or\n     *   file copied from, while `$to` is the copy target.\n     * - copyEmptyDirectories: boolean, whether to copy empty directories. Set this to false to avoid creating directories\n     *   that do not contain files. This affects directories that do not contain files initially as well as directories that\n     *   do not contain files at the target destination because files have been filtered via `only` or `except`.\n     *   Defaults to true. This option is available since version 2.0.12. Before 2.0.12 empty directories are always copied.\n     * @throws InvalidArgumentException if unable to open directory\n     */\n    public static function copyDirectory($src, $dst, $options = [])\n    {\n        $src = static::normalizePath($src);\n        $dst = static::normalizePath($dst);\n\n        if ($src === $dst || strpos($dst, $src . DIRECTORY_SEPARATOR) === 0) {\n            throw new InvalidArgumentException('Trying to copy a directory to itself or a subdirectory.');\n        }\n        $dstExists = is_dir($dst);\n        if (!$dstExists && (!isset($options['copyEmptyDirectories']) || $options['copyEmptyDirectories'])) {\n            static::createDirectory($dst, isset($options['dirMode']) ? $options['dirMode'] : 0775, true);\n            $dstExists = true;\n        }\n\n        $handle = opendir($src);\n        if ($handle === false) {\n            throw new InvalidArgumentException(\"Unable to open directory: $src\");\n        }\n        if (!isset($options['basePath'])) {\n            // this should be done only once\n            $options['basePath'] = realpath($src);\n            $options = static::normalizeOptions($options);\n        }\n        while (($file = readdir($handle)) !== false) {\n            if ($file === '.' || $file === '..') {\n                continue;\n            }\n            $from = $src . DIRECTORY_SEPARATOR . $file;\n            $to = $dst . DIRECTORY_SEPARATOR . $file;\n            if (static::filterPath($from, $options)) {\n                if (isset($options['beforeCopy']) && !call_user_func($options['beforeCopy'], $from, $to)) {\n                    continue;\n                }\n                if (is_file($from)) {\n                    if (!$dstExists) {\n                        // delay creation of destination directory until the first file is copied to avoid creating empty directories\n                        static::createDirectory($dst, isset($options['dirMode']) ? $options['dirMode'] : 0775, true);\n                        $dstExists = true;\n                    }\n                    copy($from, $to);\n                    if (isset($options['fileMode'])) {\n                        @chmod($to, $options['fileMode']);\n                    }\n                } else {\n                    // recursive copy, defaults to true\n                    if (!isset($options['recursive']) || $options['recursive']) {\n                        static::copyDirectory($from, $to, $options);\n                    }\n                }\n                if (isset($options['afterCopy'])) {\n                    call_user_func($options['afterCopy'], $from, $to);\n                }\n            }\n        }\n        closedir($handle);\n    }\n\n    /**\n     * Removes a directory (and all its content) recursively.\n     *\n     * @param string $dir the directory to be deleted recursively.\n     * @param array $options options for directory remove. Valid options are:\n     *\n     * - traverseSymlinks: boolean, whether symlinks to the directories should be traversed too.\n     *   Defaults to `false`, meaning the content of the symlinked directory would not be deleted.\n     *   Only symlink would be removed in that default case.\n     *\n     * @throws ErrorException in case of failure\n     */\n    public static function removeDirectory($dir, $options = [])\n    {\n        if (!is_dir($dir)) {\n            return;\n        }\n        if (!empty($options['traverseSymlinks']) || !is_link($dir)) {\n            if (!($handle = opendir($dir))) {\n                return;\n            }\n            while (($file = readdir($handle)) !== false) {\n                if ($file === '.' || $file === '..') {\n                    continue;\n                }\n                $path = $dir . DIRECTORY_SEPARATOR . $file;\n                if (is_dir($path)) {\n                    static::removeDirectory($path, $options);\n                } else {\n                    static::unlink($path);\n                }\n            }\n            closedir($handle);\n        }\n        if (is_link($dir)) {\n            static::unlink($dir);\n        } else {\n            rmdir($dir);\n        }\n    }\n\n    /**\n     * Removes a file or symlink in a cross-platform way\n     *\n     * @param string $path\n     * @return bool\n     *\n     * @since 2.0.14\n     */\n    public static function unlink($path)\n    {\n        $isWindows = DIRECTORY_SEPARATOR === '\\\\';\n\n        if (!$isWindows) {\n            return unlink($path);\n        }\n\n        if (is_link($path) && is_dir($path)) {\n            return rmdir($path);\n        }\n\n        try {\n            return unlink($path);\n        } catch (ErrorException $e) {\n            // last resort measure for Windows\n            if (is_dir($path) && count(static::findFiles($path)) !== 0) {\n                return false;\n            }\n            if (function_exists('exec') && file_exists($path)) {\n                exec('DEL /F/Q ' . escapeshellarg($path));\n\n                return !file_exists($path);\n            }\n\n            return false;\n        }\n    }\n\n    /**\n     * Returns the files found under the specified directory and subdirectories.\n     * @param string $dir the directory under which the files will be looked for.\n     * @param array $options options for file searching. Valid options are:\n     *\n     * - `filter`: callback, a PHP callback that is called for each directory or file.\n     *   The signature of the callback should be: `function ($path)`, where `$path` refers the full path to be filtered.\n     *   The callback can return one of the following values:\n     *\n     *   * `true`: the directory or file will be returned (the `only` and `except` options will be ignored)\n     *   * `false`: the directory or file will NOT be returned (the `only` and `except` options will be ignored)\n     *   * `null`: the `only` and `except` options will determine whether the directory or file should be returned\n     *\n     * - `except`: array, list of patterns excluding from the results matching file or directory paths.\n     *   Patterns ending with slash ('/') apply to directory paths only, and patterns not ending with '/'\n     *   apply to file paths only. For example, '/a/b' matches all file paths ending with '/a/b';\n     *   and `.svn/` matches directory paths ending with `.svn`.\n     *   If the pattern does not contain a slash (`/`), it is treated as a shell glob pattern\n     *   and checked for a match against the pathname relative to `$dir`.\n     *   Otherwise, the pattern is treated as a shell glob suitable for consumption by `fnmatch(3)`\n     *   with the `FNM_PATHNAME` flag: wildcards in the pattern will not match a `/` in the pathname.\n     *   For example, `views/*.php` matches `views/index.php` but not `views/controller/index.php`.\n     *   A leading slash matches the beginning of the pathname. For example, `/*.php` matches `index.php` but not `views/start/index.php`.\n     *   An optional prefix `!` which negates the pattern; any matching file excluded by a previous pattern will become included again.\n     *   If a negated pattern matches, this will override lower precedence patterns sources. Put a backslash (`\\`) in front of the first `!`\n     *   for patterns that begin with a literal `!`, for example, `\\!important!.txt`.\n     *   Note, the '/' characters in a pattern matches both '/' and '\\' in the paths.\n     *   You can find more details about the gitignore pattern format [here](https://git-scm.com/docs/gitignore/en#_pattern_format).\n     * - `only`: array, list of patterns that the file paths should match if they are to be returned. Directory paths\n     *   are not checked against them. Same pattern matching rules as in the `except` option are used.\n     *   If a file path matches a pattern in both `only` and `except`, it will NOT be returned.\n     * - `caseSensitive`: boolean, whether patterns specified at `only` or `except` should be case sensitive. Defaults to `true`.\n     * - `recursive`: boolean, whether the files under the subdirectories should also be looked for. Defaults to `true`.\n     * @return array files found under the directory, in no particular order. Ordering depends on the files system used.\n     * @throws InvalidArgumentException if the dir is invalid.\n     */\n    public static function findFiles($dir, $options = [])\n    {\n        $dir = self::clearDir($dir);\n        $options = self::setBasePath($dir, $options);\n        $list = [];\n        $handle = self::openDir($dir);\n        while (($file = readdir($handle)) !== false) {\n            if ($file === '.' || $file === '..') {\n                continue;\n            }\n            $path = $dir . DIRECTORY_SEPARATOR . $file;\n            if (static::filterPath($path, $options)) {\n                if (is_file($path)) {\n                    $list[] = $path;\n                } elseif (is_dir($path) && (!isset($options['recursive']) || $options['recursive'])) {\n                    $list = array_merge($list, static::findFiles($path, $options));\n                }\n            }\n        }\n        closedir($handle);\n\n        return $list;\n    }\n\n    /**\n     * Returns the directories found under the specified directory and subdirectories.\n     * @param string $dir the directory under which the files will be looked for.\n     * @param array $options options for directory searching. Valid options are:\n     *\n     * - `filter`: callback, a PHP callback that is called for each directory or file.\n     *   The signature of the callback should be: `function (string $path): bool`, where `$path` refers\n     *   the full path to be filtered. The callback can return one of the following values:\n     *\n     *   * `true`: the directory will be returned\n     *   * `false`: the directory will NOT be returned\n     *\n     * - `recursive`: boolean, whether the files under the subdirectories should also be looked for. Defaults to `true`.\n     *   See [[findFiles()]] for more options.\n     * @return array directories found under the directory, in no particular order. Ordering depends on the files system used.\n     * @throws InvalidArgumentException if the dir is invalid.\n     * @since 2.0.14\n     */\n    public static function findDirectories($dir, $options = [])\n    {\n        $dir = self::clearDir($dir);\n        $options = self::setBasePath($dir, $options);\n        $list = [];\n        $handle = self::openDir($dir);\n        while (($file = readdir($handle)) !== false) {\n            if ($file === '.' || $file === '..') {\n                continue;\n            }\n            $path = $dir . DIRECTORY_SEPARATOR . $file;\n            if (is_dir($path) && static::filterPath($path, $options)) {\n                $list[] = $path;\n                if (!isset($options['recursive']) || $options['recursive']) {\n                    $list = array_merge($list, static::findDirectories($path, $options));\n                }\n            }\n        }\n        closedir($handle);\n\n        return $list;\n    }\n\n    /**\n     * @param string $dir\n     * @param array $options\n     * @return array\n     */\n    private static function setBasePath($dir, $options)\n    {\n        if (!isset($options['basePath'])) {\n            // this should be done only once\n            $options['basePath'] = realpath($dir);\n            $options = static::normalizeOptions($options);\n        }\n\n        return $options;\n    }\n\n    /**\n     * @param string $dir\n     * @return resource\n     * @throws InvalidArgumentException if unable to open directory\n     */\n    private static function openDir($dir)\n    {\n        $handle = opendir($dir);\n        if ($handle === false) {\n            throw new InvalidArgumentException(\"Unable to open directory: $dir\");\n        }\n        return $handle;\n    }\n\n    /**\n     * @param string $dir\n     * @return string\n     * @throws InvalidArgumentException if directory not exists\n     */\n    private static function clearDir($dir)\n    {\n        if (!is_dir($dir)) {\n            throw new InvalidArgumentException(\"The dir argument must be a directory: $dir\");\n        }\n        return rtrim($dir, '\\/');\n    }\n\n    /**\n     * Checks if the given file path satisfies the filtering options.\n     * @param string $path the path of the file or directory to be checked\n     * @param array $options the filtering options. See [[findFiles()]] for explanations of\n     * the supported options.\n     * @return bool whether the file or directory satisfies the filtering options.\n     */\n    public static function filterPath($path, $options)\n    {\n        if (isset($options['filter'])) {\n            $result = call_user_func($options['filter'], $path);\n            if (is_bool($result)) {\n                return $result;\n            }\n        }\n\n        if (empty($options['except']) && empty($options['only'])) {\n            return true;\n        }\n\n        $path = str_replace('\\\\', '/', $path);\n\n        if (\n            !empty($options['except'])\n            && ($except = self::lastExcludeMatchingFromList($options['basePath'], $path, $options['except'])) !== null\n        ) {\n            return $except['flags'] & self::PATTERN_NEGATIVE;\n        }\n\n        if (!empty($options['only']) && !is_dir($path)) {\n            return self::lastExcludeMatchingFromList($options['basePath'], $path, $options['only']) !== null;\n        }\n\n        return true;\n    }\n\n    /**\n     * Creates a new directory.\n     *\n     * This method is similar to the PHP `mkdir()` function except that\n     * it uses `chmod()` to set the permission of the created directory\n     * in order to avoid the impact of the `umask` setting.\n     *\n     * @param string $path path of the directory to be created.\n     * @param int $mode the permission to be set for the created directory.\n     * @param bool $recursive whether to create parent directories if they do not exist.\n     * @return bool whether the directory is created successfully\n     * @throws \\yii\\base\\Exception if the directory could not be created (i.e. php error due to parallel changes)\n     */\n    public static function createDirectory($path, $mode = 0775, $recursive = true)\n    {\n        if (is_dir($path)) {\n            return true;\n        }\n        $parentDir = dirname($path);\n        // recurse if parent dir does not exist and we are not at the root of the file system.\n        if ($recursive && !is_dir($parentDir) && $parentDir !== $path) {\n            static::createDirectory($parentDir, $mode, true);\n        }\n        try {\n            if (!mkdir($path, $mode)) {\n                return false;\n            }\n        } catch (\\Exception $e) {\n            if (!is_dir($path)) {// https://github.com/yiisoft/yii2/issues/9288\n                throw new \\yii\\base\\Exception(\"Failed to create directory \\\"$path\\\": \" . $e->getMessage(), $e->getCode(), $e);\n            }\n        }\n        try {\n            return chmod($path, $mode);\n        } catch (\\Exception $e) {\n            throw new \\yii\\base\\Exception(\"Failed to change permissions for directory \\\"$path\\\": \" . $e->getMessage(), $e->getCode(), $e);\n        }\n    }\n\n    /**\n     * Performs a simple comparison of file or directory names.\n     *\n     * Based on match_basename() from dir.c of git 1.8.5.3 sources.\n     *\n     * @param string $baseName file or directory name to compare with the pattern\n     * @param string $pattern the pattern that $baseName will be compared against\n     * @param int|bool $firstWildcard location of first wildcard character in the $pattern\n     * @param int $flags pattern flags\n     * @return bool whether the name matches against pattern\n     */\n    private static function matchBasename($baseName, $pattern, $firstWildcard, $flags)\n    {\n        if ($firstWildcard === false) {\n            if ($pattern === $baseName) {\n                return true;\n            }\n        } elseif ($flags & self::PATTERN_ENDSWITH) {\n            /* \"*literal\" matching against \"fooliteral\" */\n            $n = StringHelper::byteLength($pattern);\n            if (StringHelper::byteSubstr($pattern, 1, $n) === StringHelper::byteSubstr($baseName, -$n, $n)) {\n                return true;\n            }\n        }\n\n        $matchOptions = [];\n        if ($flags & self::PATTERN_CASE_INSENSITIVE) {\n            $matchOptions['caseSensitive'] = false;\n        }\n\n        return StringHelper::matchWildcard($pattern, $baseName, $matchOptions);\n    }\n\n    /**\n     * Compares a path part against a pattern with optional wildcards.\n     *\n     * Based on match_pathname() from dir.c of git 1.8.5.3 sources.\n     *\n     * @param string $path full path to compare\n     * @param string $basePath base of path that will not be compared\n     * @param string $pattern the pattern that path part will be compared against\n     * @param int|bool $firstWildcard location of first wildcard character in the $pattern\n     * @param int $flags pattern flags\n     * @return bool whether the path part matches against pattern\n     */\n    private static function matchPathname($path, $basePath, $pattern, $firstWildcard, $flags)\n    {\n        // match with FNM_PATHNAME; the pattern has base implicitly in front of it.\n        if (strncmp($pattern, '/', 1) === 0) {\n            $pattern = StringHelper::byteSubstr($pattern, 1, StringHelper::byteLength($pattern));\n            if ($firstWildcard !== false && $firstWildcard !== 0) {\n                $firstWildcard--;\n            }\n        }\n\n        $namelen = StringHelper::byteLength($path) - (empty($basePath) ? 0 : StringHelper::byteLength($basePath) + 1);\n        $name = StringHelper::byteSubstr($path, -$namelen, $namelen);\n\n        if ($firstWildcard !== 0) {\n            if ($firstWildcard === false) {\n                $firstWildcard = StringHelper::byteLength($pattern);\n            }\n            // if the non-wildcard part is longer than the remaining pathname, surely it cannot match.\n            if ($firstWildcard > $namelen) {\n                return false;\n            }\n\n            if (strncmp($pattern, $name, $firstWildcard)) {\n                return false;\n            }\n            $pattern = StringHelper::byteSubstr($pattern, $firstWildcard, StringHelper::byteLength($pattern));\n            $name = StringHelper::byteSubstr($name, $firstWildcard, $namelen);\n\n            // If the whole pattern did not have a wildcard, then our prefix match is all we need; we do not need to call fnmatch at all.\n            if (empty($pattern) && empty($name)) {\n                return true;\n            }\n        }\n\n        $matchOptions = [\n            'filePath' => true\n        ];\n        if ($flags & self::PATTERN_CASE_INSENSITIVE) {\n            $matchOptions['caseSensitive'] = false;\n        }\n\n        return StringHelper::matchWildcard($pattern, $name, $matchOptions);\n    }\n\n    /**\n     * Scan the given exclude list in reverse to see whether pathname\n     * should be ignored.  The first match (i.e. the last on the list), if\n     * any, determines the fate.  Returns the element which\n     * matched, or null for undecided.\n     *\n     * Based on last_exclude_matching_from_list() from dir.c of git 1.8.5.3 sources.\n     *\n     * @param string $basePath\n     * @param string $path\n     * @param array $excludes list of patterns to match $path against\n     * @return array|null null or one of $excludes item as an array with keys: 'pattern', 'flags'\n     * @throws InvalidArgumentException if any of the exclude patterns is not a string or an array with keys: pattern, flags, firstWildcard.\n     */\n    private static function lastExcludeMatchingFromList($basePath, $path, $excludes)\n    {\n        foreach (array_reverse($excludes) as $exclude) {\n            if (is_string($exclude)) {\n                $exclude = self::parseExcludePattern($exclude, false);\n            }\n            if (!isset($exclude['pattern']) || !isset($exclude['flags']) || !isset($exclude['firstWildcard'])) {\n                throw new InvalidArgumentException('If exclude/include pattern is an array it must contain the pattern, flags and firstWildcard keys.');\n            }\n            if ($exclude['flags'] & self::PATTERN_MUSTBEDIR && !is_dir($path)) {\n                continue;\n            }\n\n            if ($exclude['flags'] & self::PATTERN_NODIR) {\n                if (self::matchBasename(basename($path), $exclude['pattern'], $exclude['firstWildcard'], $exclude['flags'])) {\n                    return $exclude;\n                }\n                continue;\n            }\n\n            if (self::matchPathname($path, $basePath, $exclude['pattern'], $exclude['firstWildcard'], $exclude['flags'])) {\n                return $exclude;\n            }\n        }\n\n        return null;\n    }\n\n    /**\n     * Processes the pattern, stripping special characters like / and ! from the beginning and settings flags instead.\n     * @param string $pattern\n     * @param bool $caseSensitive\n     * @return array with keys: (string) pattern, (int) flags, (int|bool) firstWildcard\n     * @throws InvalidArgumentException\n     */\n    private static function parseExcludePattern($pattern, $caseSensitive)\n    {\n        if (!is_string($pattern)) {\n            throw new InvalidArgumentException('Exclude/include pattern must be a string.');\n        }\n\n        $result = [\n            'pattern' => $pattern,\n            'flags' => 0,\n            'firstWildcard' => false,\n        ];\n\n        if (!$caseSensitive) {\n            $result['flags'] |= self::PATTERN_CASE_INSENSITIVE;\n        }\n\n        if (empty($pattern)) {\n            return $result;\n        }\n\n        if (strncmp($pattern, '!', 1) === 0) {\n            $result['flags'] |= self::PATTERN_NEGATIVE;\n            $pattern = StringHelper::byteSubstr($pattern, 1, StringHelper::byteLength($pattern));\n        }\n        if (StringHelper::byteLength($pattern) && StringHelper::byteSubstr($pattern, -1, 1) === '/') {\n            $pattern = StringHelper::byteSubstr($pattern, 0, -1);\n            $result['flags'] |= self::PATTERN_MUSTBEDIR;\n        }\n        if (strpos($pattern, '/') === false) {\n            $result['flags'] |= self::PATTERN_NODIR;\n        }\n        $result['firstWildcard'] = self::firstWildcardInPattern($pattern);\n        if (strncmp($pattern, '*', 1) === 0 && self::firstWildcardInPattern(StringHelper::byteSubstr($pattern, 1, StringHelper::byteLength($pattern))) === false) {\n            $result['flags'] |= self::PATTERN_ENDSWITH;\n        }\n        $result['pattern'] = $pattern;\n\n        return $result;\n    }\n\n    /**\n     * Searches for the first wildcard character in the pattern.\n     * @param string $pattern the pattern to search in\n     * @return int|bool position of first wildcard character or false if not found\n     */\n    private static function firstWildcardInPattern($pattern)\n    {\n        $wildcards = ['*', '?', '[', '\\\\'];\n        $wildcardSearch = function ($r, $c) use ($pattern) {\n            $p = strpos($pattern, $c);\n\n            return $r === false ? $p : ($p === false ? $r : min($r, $p));\n        };\n\n        return array_reduce($wildcards, $wildcardSearch, false);\n    }\n\n    /**\n     * @param array $options raw options\n     * @return array normalized options\n     * @since 2.0.12\n     */\n    protected static function normalizeOptions(array $options)\n    {\n        if (!array_key_exists('caseSensitive', $options)) {\n            $options['caseSensitive'] = true;\n        }\n        if (isset($options['except'])) {\n            foreach ($options['except'] as $key => $value) {\n                if (is_string($value)) {\n                    $options['except'][$key] = self::parseExcludePattern($value, $options['caseSensitive']);\n                }\n            }\n        }\n        if (isset($options['only'])) {\n            foreach ($options['only'] as $key => $value) {\n                if (is_string($value)) {\n                    $options['only'][$key] = self::parseExcludePattern($value, $options['caseSensitive']);\n                }\n            }\n        }\n\n        return $options;\n    }\n\n    /**\n     * Changes the Unix user and/or group ownership of a file or directory, and optionally the mode.\n     * Note: This function will not work on remote files as the file to be examined must be accessible\n     * via the server's filesystem.\n     * Note: On Windows, this function fails silently when applied on a regular file.\n     * @param string $path the path to the file or directory.\n     * @param string|array|int|null $ownership the user and/or group ownership for the file or directory.\n     * When $ownership is a string, the format is 'user:group' where both are optional. E.g.\n     * 'user' or 'user:' will only change the user,\n     * ':group' will only change the group,\n     * 'user:group' will change both.\n     * When $owners is an index array the format is [0 => user, 1 => group], e.g. `[$myUser, $myGroup]`.\n     * It is also possible to pass an associative array, e.g. ['user' => $myUser, 'group' => $myGroup].\n     * In case $owners is an integer it will be used as user id.\n     * If `null`, an empty array or an empty string is passed, the ownership will not be changed.\n     * @param int|null $mode the permission to be set for the file or directory.\n     * If `null` is passed, the mode will not be changed.\n     *\n     * @since 2.0.43\n     */\n    public static function changeOwnership($path, $ownership, $mode = null)\n    {\n        if (!file_exists((string)$path)) {\n            throw new InvalidArgumentException('Unable to change ownership, \"' . $path . '\" is not a file or directory.');\n        }\n\n        if (empty($ownership) && $ownership !== 0 && $mode === null) {\n            return;\n        }\n\n        $user = $group = null;\n        if (!empty($ownership) || $ownership === 0 || $ownership === '0') {\n            if (is_int($ownership)) {\n                $user = $ownership;\n            } elseif (is_string($ownership)) {\n                $ownerParts = explode(':', $ownership);\n                $user = $ownerParts[0];\n                if (count($ownerParts) > 1) {\n                    $group = $ownerParts[1];\n                }\n            } elseif (is_array($ownership)) {\n                $ownershipIsIndexed = ArrayHelper::isIndexed($ownership);\n                $user = ArrayHelper::getValue($ownership, $ownershipIsIndexed ? 0 : 'user');\n                $group = ArrayHelper::getValue($ownership, $ownershipIsIndexed ? 1 : 'group');\n            } else {\n                throw new InvalidArgumentException('$ownership must be an integer, string, array, or null.');\n            }\n        }\n\n        if ($mode !== null) {\n            if (!is_int($mode)) {\n                throw new InvalidArgumentException('$mode must be an integer or null.');\n            }\n            if (!chmod($path, $mode)) {\n                throw new Exception('Unable to change mode of \"' . $path . '\" to \"0' . decoct($mode) . '\".');\n            }\n        }\n        if ($user !== null && $user !== '') {\n            if (is_numeric($user)) {\n                $user = (int) $user;\n            } elseif (!is_string($user)) {\n                throw new InvalidArgumentException('The user part of $ownership must be an integer, string, or null.');\n            }\n            if (!chown($path, $user)) {\n                throw new Exception('Unable to change user ownership of \"' . $path . '\" to \"' . $user . '\".');\n            }\n        }\n        if ($group !== null && $group !== '') {\n            if (is_numeric($group)) {\n                $group = (int) $group;\n            } elseif (!is_string($group)) {\n                throw new InvalidArgumentException('The group part of $ownership must be an integer, string or null.');\n            }\n            if (!chgrp($path, $group)) {\n                throw new Exception('Unable to change group ownership of \"' . $path . '\" to \"' . $group . '\".');\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "framework/helpers/BaseFormatConverter.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\helpers;\n\nuse IntlDateFormatter;\nuse Yii;\n\n/**\n * BaseFormatConverter provides concrete implementation for [[FormatConverter]].\n *\n * Do not use BaseFormatConverter. Use [[FormatConverter]] instead.\n *\n * @author Carsten Brandt <mail@cebe.cc>\n * @author Enrica Ruedin <e.ruedin@guggach.com>\n * @since 2.0\n */\nclass BaseFormatConverter\n{\n    /**\n     * @var array the php fallback definition to use for the ICU short patterns `short`, `medium`, `long` and `full`.\n     * This is used as fallback when the `intl` extension is not installed.\n     */\n    public static $phpFallbackDatePatterns = [\n        'short' => [\n            'date' => 'n/j/y',\n            'time' => 'H:i',\n            'datetime' => 'n/j/y H:i',\n        ],\n        'medium' => [\n            'date' => 'M j, Y',\n            'time' => 'g:i:s A',\n            'datetime' => 'M j, Y g:i:s A',\n        ],\n        'long' => [\n            'date' => 'F j, Y',\n            'time' => 'g:i:sA',\n            'datetime' => 'F j, Y g:i:sA',\n        ],\n        'full' => [\n            'date' => 'l, F j, Y',\n            'time' => 'g:i:sA T',\n            'datetime' => 'l, F j, Y g:i:sA T',\n        ],\n    ];\n    /**\n     * @var array the jQuery UI fallback definition to use for the ICU short patterns `short`, `medium`, `long` and `full`.\n     * This is used as fallback when the `intl` extension is not installed.\n     */\n    public static $juiFallbackDatePatterns = [\n        'short' => [\n            'date' => 'd/m/y',\n            'time' => '',\n            'datetime' => 'd/m/y',\n        ],\n        'medium' => [\n            'date' => 'M d, yy',\n            'time' => '',\n            'datetime' => 'M d, yy',\n        ],\n        'long' => [\n            'date' => 'MM d, yy',\n            'time' => '',\n            'datetime' => 'MM d, yy',\n        ],\n        'full' => [\n            'date' => 'DD, MM d, yy',\n            'time' => '',\n            'datetime' => 'DD, MM d, yy',\n        ],\n    ];\n\n    private static $_icuShortFormats = [\n        'short' => 3, // IntlDateFormatter::SHORT,\n        'medium' => 2, // IntlDateFormatter::MEDIUM,\n        'long' => 1, // IntlDateFormatter::LONG,\n        'full' => 0, // IntlDateFormatter::FULL,\n    ];\n\n\n    /**\n     * Converts a date format pattern from [ICU format](https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax)\n     * to [PHP `date()` function format](https://www.php.net/manual/en/function.date).\n     *\n     * The conversion is limited to date patterns that do not use escaped characters.\n     * Patterns like `d 'of' MMMM yyyy` which will result in a date like `1 of December 2014` may not be converted correctly\n     * because of the use of escaped characters.\n     *\n     * Pattern constructs that are not supported by the PHP format will be removed.\n     *\n     * @param string $pattern date format pattern in ICU format.\n     * @param string $type 'date', 'time', or 'datetime'.\n     * @param string|null $locale the locale to use for converting ICU short patterns `short`, `medium`, `long` and `full`.\n     * If not given, `Yii::$app->language` will be used.\n     * @return string The converted date format pattern.\n     * @throws \\Exception\n     */\n    public static function convertDateIcuToPhp($pattern, $type = 'date', $locale = null)\n    {\n        if (isset(self::$_icuShortFormats[$pattern])) {\n            if (extension_loaded('intl')) {\n                $pattern = self::createFormatter($locale, $type, $pattern);\n            } else {\n                return static::$phpFallbackDatePatterns[$pattern][$type];\n            }\n        }\n        // https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax\n        // escaped text\n        $escaped = [];\n        if (preg_match_all('/(?<!\\')\\'(.*?[^\\'])\\'(?!\\')/', $pattern, $matches, PREG_SET_ORDER)) {\n            foreach ($matches as $match) {\n                $match[1] = str_replace('\\'\\'', '\\'', $match[1]);\n                $escaped[$match[0]] = '\\\\' . implode('\\\\', preg_split('//u', $match[1], -1, PREG_SPLIT_NO_EMPTY));\n            }\n        }\n\n        return strtr($pattern, array_merge($escaped, [\n            \"''\" => \"\\\\'\",  // two single quotes produce one\n            'G' => '',      // era designator like (Anno Domini)\n            'Y' => 'o',     // 4digit year of \"Week of Year\"\n            'y' => 'Y',     // 4digit year e.g. 2014\n            'yyyy' => 'Y',  // 4digit year e.g. 2014\n            'yy' => 'y',    // 2digit year number eg. 14\n            'u' => '',      // extended year e.g. 4601\n            'U' => '',      // cyclic year name, as in Chinese lunar calendar\n            'r' => '',      // related Gregorian year e.g. 1996\n            'Q' => '',      // number of quarter\n            'QQ' => '',     // number of quarter '02'\n            'QQQ' => '',    // quarter 'Q2'\n            'QQQQ' => '',   // quarter '2nd quarter'\n            'QQQQQ' => '',  // number of quarter '2'\n            'q' => '',      // number of Stand Alone quarter\n            'qq' => '',     // number of Stand Alone quarter '02'\n            'qqq' => '',    // Stand Alone quarter 'Q2'\n            'qqqq' => '',   // Stand Alone quarter '2nd quarter'\n            'qqqqq' => '',  // number of Stand Alone quarter '2'\n            'M' => 'n',     // Numeric representation of a month, without leading zeros\n            'MM' => 'm',    // Numeric representation of a month, with leading zeros\n            'MMM' => 'M',   // A short textual representation of a month, three letters\n            'MMMM' => 'F',  // A full textual representation of a month, such as January or March\n            'MMMMM' => '',\n            'L' => 'n',     // Stand alone month in year\n            'LL' => 'm',    // Stand alone month in year\n            'LLL' => 'M',   // Stand alone month in year\n            'LLLL' => 'F',  // Stand alone month in year\n            'LLLLL' => '',  // Stand alone month in year\n            'w' => 'W',     // ISO-8601 week number of year\n            'ww' => 'W',    // ISO-8601 week number of year\n            'W' => '',      // week of the current month\n            'd' => 'j',     // day without leading zeros\n            'dd' => 'd',    // day with leading zeros\n            'D' => 'z',     // day of the year 0 to 365\n            'F' => '',      // Day of Week in Month. eg. 2nd Wednesday in July\n            'g' => '',      // Modified Julian day. This is different from the conventional Julian day number in two regards.\n            'E' => 'D',     // day of week written in short form eg. Sun\n            'EE' => 'D',\n            'EEE' => 'D',\n            'EEEE' => 'l',  // day of week fully written eg. Sunday\n            'EEEEE' => '',\n            'EEEEEE' => '',\n            'e' => 'N',     // ISO-8601 numeric representation of the day of the week 1=Mon to 7=Sun\n            'ee' => 'N',    // php 'w' 0=Sun to 6=Sat isn't supported by ICU -> 'w' means week number of year\n            'eee' => 'D',\n            'eeee' => 'l',\n            'eeeee' => '',\n            'eeeeee' => '',\n            'c' => 'N',     // ISO-8601 numeric representation of the day of the week 1=Mon to 7=Sun\n            'cc' => 'N',    // php 'w' 0=Sun to 6=Sat isn't supported by ICU -> 'w' means week number of year\n            'ccc' => 'D',\n            'cccc' => 'l',\n            'ccccc' => '',\n            'cccccc' => '',\n            'a' => 'A',     // AM/PM marker\n            'h' => 'g',     // 12-hour format of an hour without leading zeros 1 to 12h\n            'hh' => 'h',    // 12-hour format of an hour with leading zeros, 01 to 12 h\n            'H' => 'G',     // 24-hour format of an hour without leading zeros 0 to 23h\n            'HH' => 'H',    // 24-hour format of an hour with leading zeros, 00 to 23 h\n            'k' => '',      // hour in day (1~24)\n            'kk' => '',     // hour in day (1~24)\n            'K' => '',      // hour in am/pm (0~11)\n            'KK' => '',     // hour in am/pm (0~11)\n            'm' => 'i',     // Minutes without leading zeros, not supported by php but we fallback\n            'mm' => 'i',    // Minutes with leading zeros\n            's' => 's',     // Seconds, without leading zeros, not supported by php but we fallback\n            'ss' => 's',    // Seconds, with leading zeros\n            'S' => '',      // fractional second\n            'SS' => '',     // fractional second\n            'SSS' => '',    // fractional second\n            'SSSS' => '',   // fractional second\n            'A' => '',      // milliseconds in day\n            'z' => 'T',     // Timezone abbreviation\n            'zz' => 'T',    // Timezone abbreviation\n            'zzz' => 'T',   // Timezone abbreviation\n            'zzzz' => 'T',  // Timezone full name, not supported by php but we fallback\n            'Z' => 'O',     // Difference to Greenwich time (GMT) in hours\n            'ZZ' => 'O',    // Difference to Greenwich time (GMT) in hours\n            'ZZZ' => 'O',   // Difference to Greenwich time (GMT) in hours\n            'ZZZZ' => '\\G\\M\\TP', // Time Zone: long localized GMT (=OOOO) e.g. GMT-08:00\n            'ZZZZZ' => '',  //  TIme Zone: ISO8601 extended hms? (=XXXXX)\n            'O' => '',      // Time Zone: short localized GMT e.g. GMT-8\n            'OOOO' => '\\G\\M\\TP', //  Time Zone: long localized GMT (=ZZZZ) e.g. GMT-08:00\n            'v' => '\\G\\M\\TP', // Time Zone: generic non-location (falls back first to VVVV and then to OOOO) using the ICU defined fallback here\n            'vvvv' => '\\G\\M\\TP', // Time Zone: generic non-location (falls back first to VVVV and then to OOOO) using the ICU defined fallback here\n            'V' => '',      // Time Zone: short time zone ID\n            'VV' => 'e',    // Time Zone: long time zone ID\n            'VVV' => '',    // Time Zone: time zone exemplar city\n            'VVVV' => '\\G\\M\\TP', // Time Zone: generic location (falls back to OOOO) using the ICU defined fallback here\n            'X' => '',      // Time Zone: ISO8601 basic hm?, with Z for 0, e.g. -08, +0530, Z\n            'XX' => 'O, \\Z', // Time Zone: ISO8601 basic hm, with Z, e.g. -0800, Z\n            'XXX' => 'P, \\Z',    // Time Zone: ISO8601 extended hm, with Z, e.g. -08:00, Z\n            'XXXX' => '',   // Time Zone: ISO8601 basic hms?, with Z, e.g. -0800, -075258, Z\n            'XXXXX' => '',  // Time Zone: ISO8601 extended hms?, with Z, e.g. -08:00, -07:52:58, Z\n            'x' => '',      // Time Zone: ISO8601 basic hm?, without Z for 0, e.g. -08, +0530\n            'xx' => 'O',    // Time Zone: ISO8601 basic hm, without Z, e.g. -0800\n            'xxx' => 'P',   // Time Zone: ISO8601 extended hm, without Z, e.g. -08:00\n            'xxxx' => '',   // Time Zone: ISO8601 basic hms?, without Z, e.g. -0800, -075258\n            'xxxxx' => '',  // Time Zone: ISO8601 extended hms?, without Z, e.g. -08:00, -07:52:58\n        ]));\n    }\n\n    /**\n     * Converts a date format pattern from [PHP `date()` function format](https://www.php.net/manual/en/function.date)\n     * to [ICU format](https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax).\n     *\n     * Pattern constructs that are not supported by the ICU format will be removed.\n     *\n     * Since 2.0.13 it handles escaped characters correctly.\n     *\n     * @param string $pattern date format pattern in PHP `date()` function format.\n     * @return string The converted date format pattern.\n     */\n    public static function convertDatePhpToIcu($pattern)\n    {\n        // https://www.php.net/manual/en/function.date\n        $result = strtr($pattern, [\n            \"'\" => \"''''\",  // single `'` should be encoded as `''`, which internally should be encoded as `''''`\n            // Day\n            '\\d' => \"'d'\",\n            'd' => 'dd',    // Day of the month, 2 digits with leading zeros — 01 to 31\n            '\\D' => \"'D'\",\n            'D' => 'eee',   // A textual representation of a day, three letters — Mon through Sun\n            '\\j' => \"'j'\",\n            'j' => 'd',     // Day of the month without leading zeros — 1 to 31\n            '\\l' => \"'l'\",\n            'l' => 'eeee',  // A full textual representation of the day of the week — Sunday through Saturday\n            '\\N' => \"'N'\",\n            'N' => 'e',     // ISO-8601 numeric representation of the day of the week, 1 (for Monday) through 7 (for Sunday)\n            '\\S' => \"'S'\",\n            'S' => '',      // English ordinal suffix for the day of the month, 2 characters — st, nd, rd or th. Works well with j\n            '\\w' => \"'w'\",\n            'w' => '',      // Numeric representation of the day of the week — 0 (for Sunday) through 6 (for Saturday)\n            '\\z' => \"'z'\",\n            'z' => 'D',     // The day of the year (starting from 0) — 0 through 365\n            // Week\n            '\\W' => \"'W'\",\n            'W' => 'w',     // ISO-8601 week number of year, weeks starting on Monday (added in PHP 4.1.0) — Example: 42 (the 42nd week in the year)\n            // Month\n            '\\F' => \"'F'\",\n            'F' => 'MMMM',  // A full textual representation of a month, January through December\n            '\\m' => \"'m'\",\n            'm' => 'MM',    // Numeric representation of a month, with leading zeros — 01 through 12\n            '\\M' => \"'M'\",\n            'M' => 'MMM',   // A short textual representation of a month, three letters — Jan through Dec\n            '\\n' => \"'n'\",\n            'n' => 'M',     // Numeric representation of a month, without leading zeros — 1 through 12, not supported by ICU but we fallback to \"with leading zero\"\n            '\\t' => \"'t'\",\n            't' => '',      // Number of days in the given month — 28 through 31\n            // Year\n            '\\L' => \"'L'\",\n            'L' => '',      // Whether it's a leap year, 1 if it is a leap year, 0 otherwise.\n            '\\o' => \"'o'\",\n            'o' => 'Y',     // ISO-8601 year number. This has the same value as Y, except that if the ISO week number (W) belongs to the previous or next year, that year is used instead.\n            '\\Y' => \"'Y'\",\n            'Y' => 'yyyy',  // A full numeric representation of a year, 4 digits — Examples: 1999 or 2003\n            '\\y' => \"'y'\",\n            'y' => 'yy',    // A two digit representation of a year — Examples: 99 or 03\n            // Time\n            '\\a' => \"'a'\",\n            'a' => 'a',     // Lowercase Ante meridiem and Post meridiem, am or pm\n            '\\A' => \"'A'\",\n            'A' => 'a',     // Uppercase Ante meridiem and Post meridiem, AM or PM, not supported by ICU but we fallback to lowercase\n            '\\B' => \"'B'\",\n            'B' => '',      // Swatch Internet time — 000 through 999\n            '\\g' => \"'g'\",\n            'g' => 'h',     // 12-hour format of an hour without leading zeros — 1 through 12\n            '\\G' => \"'G'\",\n            'G' => 'H',     // 24-hour format of an hour without leading zeros 0 to 23h\n            '\\h' => \"'h'\",\n            'h' => 'hh',    // 12-hour format of an hour with leading zeros, 01 to 12 h\n            '\\H' => \"'H'\",\n            'H' => 'HH',    // 24-hour format of an hour with leading zeros, 00 to 23 h\n            '\\i' => \"'i'\",\n            'i' => 'mm',    // Minutes with leading zeros — 00 to 59\n            '\\s' => \"'s'\",\n            's' => 'ss',    // Seconds, with leading zeros — 00 through 59\n            '\\u' => \"'u'\",\n            'u' => '',      // Microseconds. Example: 654321\n            // Timezone\n            '\\e' => \"'e'\",\n            'e' => 'VV',    // Timezone identifier. Examples: UTC, GMT, Atlantic/Azores\n            '\\I' => \"'I'\",\n            'I' => '',      // Whether or not the date is in daylight saving time, 1 if Daylight Saving Time, 0 otherwise.\n            '\\O' => \"'O'\",\n            'O' => 'xx',    // Difference to Greenwich time (GMT) in hours, Example: +0200\n            '\\P' => \"'P'\",\n            'P' => 'xxx',   // Difference to Greenwich time (GMT) with colon between hours and minutes, Example: +02:00\n            '\\T' => \"'T'\",\n            'T' => 'zzz',   // Timezone abbreviation, Examples: EST, MDT ...\n            '\\Z' => \"'Z'\",\n            'Z' => '',      // Timezone offset in seconds. The offset for timezones west of UTC is always negative, and for those east of UTC is always positive. -43200 through 50400\n            // Full Date/Time\n            '\\c' => \"'c'\",\n            'c' => \"yyyy-MM-dd'T'HH:mm:ssxxx\", // ISO 8601 date, e.g. 2004-02-12T15:19:21+00:00\n            '\\r' => \"'r'\",\n            'r' => 'eee, dd MMM yyyy HH:mm:ss xx', // RFC 2822 formatted date, Example: Thu, 21 Dec 2000 16:01:07 +0200\n            '\\U' => \"'U'\",\n            'U' => '',      // Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)\n            '\\\\\\\\' => '\\\\',\n        ]);\n\n        // remove `''` - they're result of consecutive escaped chars (`\\A\\B` will be `'A''B'`, but should be `'AB'`)\n        // real `'` are encoded as `''''`\n        return strtr($result, [\n            \"''''\" => \"''\",\n            \"''\" => '',\n        ]);\n    }\n\n    /**\n     * Converts a date format pattern from [ICU format](https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax)\n     * to [jQuery UI date format](https://api.jqueryui.com/datepicker/#utility-formatDate).\n     *\n     * Pattern constructs that are not supported by the jQuery UI format will be removed.\n     *\n     * @param string $pattern date format pattern in ICU format.\n     * @param string $type 'date', 'time', or 'datetime'.\n     * @param string|null $locale the locale to use for converting ICU short patterns `short`, `medium`, `long` and `full`.\n     * If not given, `Yii::$app->language` will be used.\n     * @return string The converted date format pattern.\n     * @throws \\Exception\n     */\n    public static function convertDateIcuToJui($pattern, $type = 'date', $locale = null)\n    {\n        if (isset(self::$_icuShortFormats[$pattern])) {\n            if (extension_loaded('intl')) {\n                $pattern = self::createFormatter($locale, $type, $pattern);\n            } else {\n                return static::$juiFallbackDatePatterns[$pattern][$type];\n            }\n        }\n        // https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax\n        // escaped text\n        $escaped = [];\n        if (preg_match_all('/(?<!\\')\\'.*?[^\\']\\'(?!\\')/', $pattern, $matches)) {\n            foreach ($matches[0] as $match) {\n                $escaped[$match] = $match;\n            }\n        }\n\n        return strtr($pattern, array_merge($escaped, [\n            'G' => '',      // era designator like (Anno Domini)\n            'Y' => '',      // 4digit year of \"Week of Year\"\n            'y' => 'yy',    // 4digit year e.g. 2014\n            'yyyy' => 'yy', // 4digit year e.g. 2014\n            'yy' => 'y',    // 2digit year number eg. 14\n            'u' => '',      // extended year e.g. 4601\n            'U' => '',      // cyclic year name, as in Chinese lunar calendar\n            'r' => '',      // related Gregorian year e.g. 1996\n            'Q' => '',      // number of quarter\n            'QQ' => '',     // number of quarter '02'\n            'QQQ' => '',    // quarter 'Q2'\n            'QQQQ' => '',   // quarter '2nd quarter'\n            'QQQQQ' => '',  // number of quarter '2'\n            'q' => '',      // number of Stand Alone quarter\n            'qq' => '',     // number of Stand Alone quarter '02'\n            'qqq' => '',    // Stand Alone quarter 'Q2'\n            'qqqq' => '',   // Stand Alone quarter '2nd quarter'\n            'qqqqq' => '',  // number of Stand Alone quarter '2'\n            'M' => 'm',     // Numeric representation of a month, without leading zeros\n            'MM' => 'mm',   // Numeric representation of a month, with leading zeros\n            'MMM' => 'M',   // A short textual representation of a month, three letters\n            'MMMM' => 'MM', // A full textual representation of a month, such as January or March\n            'MMMMM' => '',\n            'L' => 'm',     // Stand alone month in year\n            'LL' => 'mm',   // Stand alone month in year\n            'LLL' => 'M',   // Stand alone month in year\n            'LLLL' => 'MM', // Stand alone month in year\n            'LLLLL' => '',  // Stand alone month in year\n            'w' => '',      // ISO-8601 week number of year\n            'ww' => '',     // ISO-8601 week number of year\n            'W' => '',      // week of the current month\n            'd' => 'd',     // day without leading zeros\n            'dd' => 'dd',   // day with leading zeros\n            'D' => 'o',     // day of the year 0 to 365\n            'F' => '',      // Day of Week in Month. eg. 2nd Wednesday in July\n            'g' => '',      // Modified Julian day. This is different from the conventional Julian day number in two regards.\n            'E' => 'D',     // day of week written in short form eg. Sun\n            'EE' => 'D',\n            'EEE' => 'D',\n            'EEEE' => 'DD', // day of week fully written eg. Sunday\n            'EEEEE' => '',\n            'EEEEEE' => '',\n            'e' => '',      // ISO-8601 numeric representation of the day of the week 1=Mon to 7=Sun\n            'ee' => '',     // php 'w' 0=Sun to 6=Sat isn't supported by ICU -> 'w' means week number of year\n            'eee' => 'D',\n            'eeee' => '',\n            'eeeee' => '',\n            'eeeeee' => '',\n            'c' => '',      // ISO-8601 numeric representation of the day of the week 1=Mon to 7=Sun\n            'cc' => '',     // php 'w' 0=Sun to 6=Sat isn't supported by ICU -> 'w' means week number of year\n            'ccc' => 'D',\n            'cccc' => 'DD',\n            'ccccc' => '',\n            'cccccc' => '',\n            'a' => '',      // am/pm marker\n            'h' => '',      // 12-hour format of an hour without leading zeros 1 to 12h\n            'hh' => '',     // 12-hour format of an hour with leading zeros, 01 to 12 h\n            'H' => '',      // 24-hour format of an hour without leading zeros 0 to 23h\n            'HH' => '',     // 24-hour format of an hour with leading zeros, 00 to 23 h\n            'k' => '',      // hour in day (1~24)\n            'kk' => '',     // hour in day (1~24)\n            'K' => '',      // hour in am/pm (0~11)\n            'KK' => '',     // hour in am/pm (0~11)\n            'm' => '',      // Minutes without leading zeros, not supported by php but we fallback\n            'mm' => '',     // Minutes with leading zeros\n            's' => '',      // Seconds, without leading zeros, not supported by php but we fallback\n            'ss' => '',     // Seconds, with leading zeros\n            'S' => '',      // fractional second\n            'SS' => '',     // fractional second\n            'SSS' => '',    // fractional second\n            'SSSS' => '',   // fractional second\n            'A' => '',      // milliseconds in day\n            'z' => '',      // Timezone abbreviation\n            'zz' => '',     // Timezone abbreviation\n            'zzz' => '',    // Timezone abbreviation\n            'zzzz' => '',   // Timezone full name, not supported by php but we fallback\n            'Z' => '',      // Difference to Greenwich time (GMT) in hours\n            'ZZ' => '',     // Difference to Greenwich time (GMT) in hours\n            'ZZZ' => '',    // Difference to Greenwich time (GMT) in hours\n            'ZZZZ' => '',   // Time Zone: long localized GMT (=OOOO) e.g. GMT-08:00\n            'ZZZZZ' => '',  // Time Zone: ISO8601 extended hms? (=XXXXX)\n            'O' => '',      // Time Zone: short localized GMT e.g. GMT-8\n            'OOOO' => '',   // Time Zone: long localized GMT (=ZZZZ) e.g. GMT-08:00\n            'v' => '',      // Time Zone: generic non-location (falls back first to VVVV and then to OOOO) using the ICU defined fallback here\n            'vvvv' => '',   // Time Zone: generic non-location (falls back first to VVVV and then to OOOO) using the ICU defined fallback here\n            'V' => '',      // Time Zone: short time zone ID\n            'VV' => '',     // Time Zone: long time zone ID\n            'VVV' => '',    // Time Zone: time zone exemplar city\n            'VVVV' => '',   // Time Zone: generic location (falls back to OOOO) using the ICU defined fallback here\n            'X' => '',      // Time Zone: ISO8601 basic hm?, with Z for 0, e.g. -08, +0530, Z\n            'XX' => '',     // Time Zone: ISO8601 basic hm, with Z, e.g. -0800, Z\n            'XXX' => '',    // Time Zone: ISO8601 extended hm, with Z, e.g. -08:00, Z\n            'XXXX' => '',   // Time Zone: ISO8601 basic hms?, with Z, e.g. -0800, -075258, Z\n            'XXXXX' => '',  // Time Zone: ISO8601 extended hms?, with Z, e.g. -08:00, -07:52:58, Z\n            'x' => '',      // Time Zone: ISO8601 basic hm?, without Z for 0, e.g. -08, +0530\n            'xx' => '',     // Time Zone: ISO8601 basic hm, without Z, e.g. -0800\n            'xxx' => '',    // Time Zone: ISO8601 extended hm, without Z, e.g. -08:00\n            'xxxx' => '',   // Time Zone: ISO8601 basic hms?, without Z, e.g. -0800, -075258\n            'xxxxx' => '',  // Time Zone: ISO8601 extended hms?, without Z, e.g. -08:00, -07:52:58\n        ]));\n    }\n\n    /**\n     * Converts a date format pattern from [PHP `date()` function format](https://www.php.net/manual/en/function.date)\n     * to [jQuery UI date format](https://api.jqueryui.com/datepicker/#utility-formatDate).\n     *\n     * The conversion is limited to date patterns that do not use escaped characters.\n     * Patterns like `jS \\o\\f F Y` which will result in a date like `1st of December 2014` may not be converted correctly\n     * because of the use of escaped characters.\n     *\n     * Pattern constructs that are not supported by the jQuery UI format will be removed.\n     *\n     * @param string $pattern date format pattern in PHP `date()` function format.\n     * @return string The converted date format pattern.\n     */\n    public static function convertDatePhpToJui($pattern)\n    {\n        // https://www.php.net/manual/en/function.date\n        return strtr($pattern, [\n            // Day\n            'd' => 'dd',    // Day of the month, 2 digits with leading zeros — 01 to 31\n            'D' => 'D',     // A textual representation of a day, three letters — Mon through Sun\n            'j' => 'd',     // Day of the month without leading zeros — 1 to 31\n            'l' => 'DD',    // A full textual representation of the day of the week — Sunday through Saturday\n            'N' => '',      // ISO-8601 numeric representation of the day of the week, 1 (for Monday) through 7 (for Sunday)\n            'S' => '',      // English ordinal suffix for the day of the month, 2 characters — st, nd, rd or th. Works well with j\n            'w' => '',      // Numeric representation of the day of the week — 0 (for Sunday) through 6 (for Saturday)\n            'z' => 'o',     // The day of the year (starting from 0) — 0 through 365\n            // Week\n            'W' => '',      // ISO-8601 week number of year, weeks starting on Monday (added in PHP 4.1.0) — Example: 42 (the 42nd week in the year)\n            // Month\n            'F' => 'MM',    // A full textual representation of a month, January through December\n            'm' => 'mm',    // Numeric representation of a month, with leading zeros — 01 through 12\n            'M' => 'M',     // A short textual representation of a month, three letters — Jan through Dec\n            'n' => 'm',     // Numeric representation of a month, without leading zeros — 1 through 12\n            't' => '',      // Number of days in the given month — 28 through 31\n            // Year\n            'L' => '',      // Whether it's a leap year, 1 if it is a leap year, 0 otherwise.\n            'o' => '',      // ISO-8601 year number. This has the same value as Y, except that if the ISO week number (W) belongs to the previous or next year, that year is used instead.\n            'Y' => 'yy',    // A full numeric representation of a year, 4 digits — Examples: 1999 or 2003\n            'y' => 'y',     // A two digit representation of a year — Examples: 99 or 03\n            // Time\n            'a' => '',      // Lowercase Ante meridiem and Post meridiem, am or pm\n            'A' => '',      // Uppercase Ante meridiem and Post meridiem, AM or PM, not supported by ICU but we fallback to lowercase\n            'B' => '',      // Swatch Internet time — 000 through 999\n            'g' => '',      // 12-hour format of an hour without leading zeros — 1 through 12\n            'G' => '',      // 24-hour format of an hour without leading zeros 0 to 23h\n            'h' => '',      // 12-hour format of an hour with leading zeros, 01 to 12 h\n            'H' => '',      // 24-hour format of an hour with leading zeros, 00 to 23 h\n            'i' => '',      // Minutes with leading zeros — 00 to 59\n            's' => '',      // Seconds, with leading zeros — 00 through 59\n            'u' => '',      // Microseconds. Example: 654321\n            // Timezone\n            'e' => '',      // Timezone identifier. Examples: UTC, GMT, Atlantic/Azores\n            'I' => '',      // Whether or not the date is in daylight saving time, 1 if Daylight Saving Time, 0 otherwise.\n            'O' => '',      // Difference to Greenwich time (GMT) in hours, Example: +0200\n            'P' => '',      // Difference to Greenwich time (GMT) with colon between hours and minutes, Example: +02:00\n            'T' => '',      // Timezone abbreviation, Examples: EST, MDT ...\n            'Z' => '',      // Timezone offset in seconds. The offset for timezones west of UTC is always negative, and for those east of UTC is always positive. -43200 through 50400\n            // Full Date/Time\n            'c' => 'yyyy-MM-dd', // ISO 8601 date, e.g. 2004-02-12T15:19:21+00:00, skipping the time here because it is not supported\n            'r' => 'D, d M yy', // RFC 2822 formatted date, Example: Thu, 21 Dec 2000 16:01:07 +0200, skipping the time here because it is not supported\n            'U' => '@',     // Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)\n        ]);\n    }\n\n    /**\n     * Creates a date/time formatter based on the given parameters.\n     *\n     * @param string|null $locale The locale to be used. If null, the application's current language will be used.\n     * @param string $type The type of formatter ('date', 'time', etc.)\n     * @param string $pattern The pattern for the IntlDateFormatter.\n     *\n     * @return string The resulting pattern after formatter creation.\n     *\n     * @throws \\Exception If the 'intl' extension is not loaded.\n     */\n    private static function createFormatter($locale, $type, $pattern)\n    {\n        if ($locale === null) {\n            $locale = Yii::$app->language;\n        }\n\n        if ($type === 'date') {\n            $formatter = new IntlDateFormatter($locale, self::$_icuShortFormats[$pattern], IntlDateFormatter::NONE);\n        } elseif ($type === 'time') {\n            $formatter = new IntlDateFormatter($locale, IntlDateFormatter::NONE, self::$_icuShortFormats[$pattern]);\n        } else {\n            $formatter = new IntlDateFormatter($locale, self::$_icuShortFormats[$pattern], self::$_icuShortFormats[$pattern]);\n        }\n\n        return $formatter->getPattern();\n    }\n}\n"
  },
  {
    "path": "framework/helpers/BaseHtml.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\helpers;\n\nuse Yii;\nuse yii\\base\\InvalidArgumentException;\nuse yii\\base\\Model;\nuse yii\\db\\ActiveRecordInterface;\nuse yii\\validators\\StringValidator;\nuse yii\\web\\Request;\n\n/**\n * BaseHtml provides concrete implementation for [[Html]].\n *\n * Do not use BaseHtml. Use [[Html]] instead.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass BaseHtml\n{\n    /**\n     * @var string Regular expression used for attribute name validation.\n     * @since 2.0.12\n     */\n    public static $attributeRegex = '/(^|.*\\])([\\w\\.\\+]+)(\\[.*|$)/u';\n    /**\n     * @var array list of void elements (element name => 1)\n     * @see https://html.spec.whatwg.org/multipage/syntax.html#void-element\n     */\n    public static $voidElements = [\n        'area' => 1,\n        'base' => 1,\n        'br' => 1,\n        'col' => 1,\n        'command' => 1,\n        'embed' => 1,\n        'hr' => 1,\n        'img' => 1,\n        'input' => 1,\n        'keygen' => 1,\n        'link' => 1,\n        'meta' => 1,\n        'param' => 1,\n        'source' => 1,\n        'track' => 1,\n        'wbr' => 1,\n    ];\n    /**\n     * @var array the preferred order of attributes in a tag. This mainly affects the order of the attributes\n     * that are rendered by [[renderTagAttributes()]].\n     */\n    public static $attributeOrder = [\n        'type',\n        'id',\n        'class',\n        'name',\n        'value',\n\n        'href',\n        'src',\n        'srcset',\n        'form',\n        'action',\n        'method',\n\n        'selected',\n        'checked',\n        'readonly',\n        'disabled',\n        'multiple',\n\n        'size',\n        'maxlength',\n        'width',\n        'height',\n        'rows',\n        'cols',\n\n        'alt',\n        'title',\n        'rel',\n        'media',\n    ];\n    /**\n     * @var array list of tag attributes that should be specially handled when their values are of array type.\n     * In particular, if the value of the `data` attribute is `['name' => 'xyz', 'age' => 13]`, two attributes\n     * will be generated instead of one: `data-name=\"xyz\" data-age=\"13\"`.\n     * @since 2.0.3\n     */\n    public static $dataAttributes = ['aria', 'data', 'data-ng', 'ng'];\n    /**\n     * @var bool whether to removes duplicate class names in tag attribute `class`\n     * @see mergeCssClasses()\n     * @see renderTagAttributes()\n     * @since 2.0.44\n     */\n    public static $normalizeClassAttribute = false;\n\n\n    /**\n     * Encodes special characters into HTML entities.\n     * The [[\\yii\\base\\Application::charset|application charset]] will be used for encoding.\n     * @param string $content the content to be encoded\n     * @param bool $doubleEncode whether to encode HTML entities in `$content`. If false,\n     * HTML entities in `$content` will not be further encoded.\n     * @return string the encoded content\n     * @see decode()\n     * @see https://www.php.net/manual/en/function.htmlspecialchars.php\n     */\n    public static function encode($content, $doubleEncode = true)\n    {\n        return htmlspecialchars((string)$content, ENT_QUOTES | ENT_SUBSTITUTE, Yii::$app ? Yii::$app->charset : 'UTF-8', $doubleEncode);\n    }\n\n    /**\n     * Decodes special HTML entities back to the corresponding characters.\n     * This is the opposite of [[encode()]].\n     * @param string $content the content to be decoded\n     * @return string the decoded content\n     * @see encode()\n     * @see https://www.php.net/manual/en/function.htmlspecialchars-decode.php\n     */\n    public static function decode($content)\n    {\n        return htmlspecialchars_decode($content, ENT_QUOTES);\n    }\n\n    /**\n     * Generates a complete HTML tag.\n     * @param string|bool|null $name the tag name. If $name is `null` or `false`, the corresponding content will be rendered without any tag.\n     * @param string $content the content to be enclosed between the start and end tags. It will not be HTML-encoded.\n     * If this is coming from end users, you should consider [[encode()]] it to prevent XSS attacks.\n     * @param array $options the HTML tag attributes (HTML options) in terms of name-value pairs.\n     * These will be rendered as the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].\n     * If a value is null, the corresponding attribute will not be rendered.\n     *\n     * For example when using `['class' => 'my-class', 'target' => '_blank', 'value' => null]` it will result in the\n     * html attributes rendered like this: `class=\"my-class\" target=\"_blank\"`.\n     *\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     *\n     * @return string the generated HTML tag\n     * @see beginTag()\n     * @see endTag()\n     */\n    public static function tag($name, $content = '', $options = [])\n    {\n        if ($name === null || $name === false) {\n            return $content;\n        }\n        $html = \"<$name\" . static::renderTagAttributes($options) . '>';\n        return isset(static::$voidElements[strtolower($name)]) ? $html : \"$html$content</$name>\";\n    }\n\n    /**\n     * Generates a start tag.\n     * @param string|bool|null $name the tag name. If $name is `null` or `false`, the corresponding content will be rendered without any tag.\n     * @param array $options the tag options in terms of name-value pairs. These will be rendered as\n     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].\n     * If a value is null, the corresponding attribute will not be rendered.\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     * @return string the generated start tag\n     * @see endTag()\n     * @see tag()\n     */\n    public static function beginTag($name, $options = [])\n    {\n        if ($name === null || $name === false) {\n            return '';\n        }\n\n        return \"<$name\" . static::renderTagAttributes($options) . '>';\n    }\n\n    /**\n     * Generates an end tag.\n     * @param string|bool|null $name the tag name. If $name is `null` or `false`, the corresponding content will be rendered without any tag.\n     * @return string the generated end tag\n     * @see beginTag()\n     * @see tag()\n     */\n    public static function endTag($name)\n    {\n        if ($name === null || $name === false) {\n            return '';\n        }\n\n        return \"</$name>\";\n    }\n\n    /**\n     * Generates a style tag.\n     * @param string $content the style content\n     * @param array $options the tag options in terms of name-value pairs. These will be rendered as\n     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].\n     * If a value is null, the corresponding attribute will not be rendered.\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     * @return string the generated style tag\n     */\n    public static function style($content, $options = [])\n    {\n        $view = Yii::$app->getView();\n        if ($view instanceof \\yii\\web\\View && !empty($view->styleOptions)) {\n            $options = array_merge($view->styleOptions, $options);\n        }\n\n        return static::tag('style', $content, $options);\n    }\n\n    /**\n     * Generates a script tag.\n     * @param string $content the script content\n     * @param array $options the tag options in terms of name-value pairs. These will be rendered as\n     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].\n     * If a value is null, the corresponding attribute will not be rendered.\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     * @return string the generated script tag\n     */\n    public static function script($content, $options = [])\n    {\n        $view = Yii::$app->getView();\n        if ($view instanceof \\yii\\web\\View && !empty($view->scriptOptions)) {\n            $options = array_merge($view->scriptOptions, $options);\n        }\n\n        return static::tag('script', $content, $options);\n    }\n\n    /**\n     * Generates a link tag that refers to an external CSS file.\n     * @param array|string $url the URL of the external CSS file. This parameter will be processed by [[Url::to()]].\n     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:\n     *\n     * - condition: specifies the conditional comments for IE, e.g., `lt IE 9`. When this is specified,\n     *   the generated `link` tag will be enclosed within the conditional comments. This is mainly useful\n     *   for supporting old versions of IE browsers.\n     * - noscript: if set to true, `link` tag will be wrapped into `<noscript>` tags.\n     *\n     * The rest of the options will be rendered as the attributes of the resulting link tag. The values will\n     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     * @return string the generated link tag\n     * @see Url::to()\n     */\n    public static function cssFile($url, $options = [])\n    {\n        if (!isset($options['rel'])) {\n            $options['rel'] = 'stylesheet';\n        }\n        $options['href'] = Url::to($url);\n\n        if (isset($options['condition'])) {\n            $condition = $options['condition'];\n            unset($options['condition']);\n            return self::wrapIntoCondition(static::tag('link', '', $options), $condition);\n        } elseif (isset($options['noscript']) && $options['noscript'] === true) {\n            unset($options['noscript']);\n            return '<noscript>' . static::tag('link', '', $options) . '</noscript>';\n        }\n\n        return static::tag('link', '', $options);\n    }\n\n    /**\n     * Generates a script tag that refers to an external JavaScript file.\n     * @param string $url the URL of the external JavaScript file. This parameter will be processed by [[Url::to()]].\n     * @param array $options the tag options in terms of name-value pairs. The following option is specially handled:\n     *\n     * - condition: specifies the conditional comments for IE, e.g., `lt IE 9`. When this is specified,\n     *   the generated `script` tag will be enclosed within the conditional comments. This is mainly useful\n     *   for supporting old versions of IE browsers.\n     *\n     * The rest of the options will be rendered as the attributes of the resulting script tag. The values will\n     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     * @return string the generated script tag\n     * @see Url::to()\n     */\n    public static function jsFile($url, $options = [])\n    {\n        $options['src'] = Url::to($url);\n        if (isset($options['condition'])) {\n            $condition = $options['condition'];\n            unset($options['condition']);\n            return self::wrapIntoCondition(static::tag('script', '', $options), $condition);\n        }\n\n        return static::tag('script', '', $options);\n    }\n\n    /**\n     * Wraps given content into conditional comments for IE, e.g., `lt IE 9`.\n     * @param string $content raw HTML content.\n     * @param string $condition condition string.\n     * @return string generated HTML.\n     */\n    private static function wrapIntoCondition($content, $condition)\n    {\n        if (strpos($condition, '!IE') !== false) {\n            return \"<!--[if $condition]><!-->\\n\" . $content . \"\\n<!--<![endif]-->\";\n        }\n\n        return \"<!--[if $condition]>\\n\" . $content . \"\\n<![endif]-->\";\n    }\n\n    /**\n     * Generates the meta tags containing CSRF token information.\n     * @return string the generated meta tags\n     * @see Request::enableCsrfValidation\n     */\n    public static function csrfMetaTags()\n    {\n        $request = Yii::$app->getRequest();\n        if ($request instanceof Request && $request->enableCsrfValidation) {\n            return static::tag('meta', '', ['name' => 'csrf-param', 'content' => $request->csrfParam]) . \"\\n\"\n                . static::tag('meta', '', ['name' => 'csrf-token', 'content' => $request->getCsrfToken()]) . \"\\n\";\n        }\n\n        return '';\n    }\n\n    /**\n     * Generates a form start tag.\n     * @param array|string $action the form action URL. This parameter will be processed by [[Url::to()]].\n     * @param string $method the form submission method, such as \"post\", \"get\", \"put\", \"delete\" (case-insensitive).\n     * Since most browsers only support \"post\" and \"get\", if other methods are given, they will\n     * be simulated using \"post\", and a hidden input will be added which contains the actual method type.\n     * See [[\\yii\\web\\Request::methodParam]] for more details.\n     * @param array $options the tag options in terms of name-value pairs. These will be rendered as\n     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].\n     * If a value is null, the corresponding attribute will not be rendered.\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     *\n     * Special options:\n     *\n     *  - `csrf`: whether to generate the CSRF hidden input. Defaults to true.\n     *\n     * @return string the generated form start tag.\n     * @see endForm()\n     */\n    public static function beginForm($action = '', $method = 'post', $options = [])\n    {\n        $action = Url::to($action);\n\n        $hiddenInputs = [];\n\n        $request = Yii::$app->getRequest();\n        if ($request instanceof Request) {\n            if (strcasecmp($method, 'get') && strcasecmp($method, 'post')) {\n                // simulate PUT, DELETE, etc. via POST\n                $hiddenInputs[] = static::hiddenInput($request->methodParam, $method);\n                $method = 'post';\n            }\n            $csrf = ArrayHelper::remove($options, 'csrf', true);\n\n            if ($csrf && $request->enableCsrfValidation && strcasecmp($method, 'post') === 0) {\n                $hiddenInputs[] = static::hiddenInput($request->csrfParam, $request->getCsrfToken());\n            }\n        }\n\n        if (!strcasecmp($method, 'get') && ($pos = strpos($action, '?')) !== false) {\n            // query parameters in the action are ignored for GET method\n            // we use hidden fields to add them back\n            foreach (explode('&', substr($action, $pos + 1)) as $pair) {\n                if (($pos1 = strpos($pair, '=')) !== false) {\n                    $hiddenInputs[] = static::hiddenInput(\n                        urldecode(substr($pair, 0, $pos1)),\n                        urldecode(substr($pair, $pos1 + 1))\n                    );\n                } else {\n                    $hiddenInputs[] = static::hiddenInput(urldecode($pair), '');\n                }\n            }\n            $action = substr($action, 0, $pos);\n        }\n\n        $options['action'] = $action;\n        $options['method'] = $method;\n        $form = static::beginTag('form', $options);\n        if (!empty($hiddenInputs)) {\n            $form .= \"\\n\" . implode(\"\\n\", $hiddenInputs);\n        }\n\n        return $form;\n    }\n\n    /**\n     * Generates a form end tag.\n     * @return string the generated tag\n     * @see beginForm()\n     */\n    public static function endForm()\n    {\n        return '</form>';\n    }\n\n    /**\n     * Generates a hyperlink tag.\n     * @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code\n     * such as an image tag. If this is coming from end users, you should consider [[encode()]]\n     * it to prevent XSS attacks.\n     * @param array|string|null $url the URL for the hyperlink tag. This parameter will be processed by [[Url::to()]]\n     * and will be used for the \"href\" attribute of the tag. If this parameter is null, the \"href\" attribute\n     * will not be generated.\n     *\n     * If you want to use an absolute url you can call [[Url::to()]] yourself, before passing the URL to this method,\n     * like this:\n     *\n     * ```\n     * Html::a('link text', Url::to($url, true))\n     * ```\n     *\n     * @param array $options the tag options in terms of name-value pairs. These will be rendered as\n     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].\n     * If a value is null, the corresponding attribute will not be rendered.\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     * @return string the generated hyperlink\n     * @see \\yii\\helpers\\Url::to()\n     */\n    public static function a($text, $url = null, $options = [])\n    {\n        if ($url !== null) {\n            $options['href'] = Url::to($url);\n        }\n\n        return static::tag('a', $text, $options);\n    }\n\n    /**\n     * Generates a mailto hyperlink.\n     * @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code\n     * such as an image tag. If this is coming from end users, you should consider [[encode()]]\n     * it to prevent XSS attacks.\n     * @param string|null $email email address. If this is null, the first parameter (link body) will be treated\n     * as the email address and used.\n     * @param array $options the tag options in terms of name-value pairs. These will be rendered as\n     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].\n     * If a value is null, the corresponding attribute will not be rendered.\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     * @return string the generated mailto link\n     */\n    public static function mailto($text, $email = null, $options = [])\n    {\n        $options['href'] = 'mailto:' . ($email === null ? $text : $email);\n        return static::tag('a', $text, $options);\n    }\n\n    /**\n     * Generates an image tag.\n     * @param array|string $src the image URL. This parameter will be processed by [[Url::to()]].\n     * @param array $options the tag options in terms of name-value pairs. These will be rendered as\n     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].\n     * If a value is null, the corresponding attribute will not be rendered.\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     *\n     * Since version 2.0.12 It is possible to pass the `srcset` option as an array which keys are\n     * descriptors and values are URLs. All URLs will be processed by [[Url::to()]].\n     * @return string the generated image tag.\n     */\n    public static function img($src, $options = [])\n    {\n        $options['src'] = Url::to($src);\n\n        if (isset($options['srcset']) && is_array($options['srcset'])) {\n            $srcset = [];\n            foreach ($options['srcset'] as $descriptor => $url) {\n                $srcset[] = Url::to($url) . ' ' . $descriptor;\n            }\n            $options['srcset'] = implode(',', $srcset);\n        }\n\n        if (!isset($options['alt'])) {\n            $options['alt'] = '';\n        }\n\n        return static::tag('img', '', $options);\n    }\n\n    /**\n     * Generates a label tag.\n     * @param string $content label text. It will NOT be HTML-encoded. Therefore you can pass in HTML code\n     * such as an image tag. If this is is coming from end users, you should [[encode()]]\n     * it to prevent XSS attacks.\n     * @param string|null $for the ID of the HTML element that this label is associated with.\n     * If this is null, the \"for\" attribute will not be generated.\n     * @param array $options the tag options in terms of name-value pairs. These will be rendered as\n     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].\n     * If a value is null, the corresponding attribute will not be rendered.\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     * @return string the generated label tag\n     */\n    public static function label($content, $for = null, $options = [])\n    {\n        $options['for'] = $for;\n        return static::tag('label', $content, $options);\n    }\n\n    /**\n     * Generates a button tag.\n     * @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded.\n     * Therefore you can pass in HTML code such as an image tag. If this is is coming from end users,\n     * you should consider [[encode()]] it to prevent XSS attacks.\n     * @param array $options the tag options in terms of name-value pairs. These will be rendered as\n     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].\n     * If a value is null, the corresponding attribute will not be rendered.\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     * @return string the generated button tag\n     */\n    public static function button($content = 'Button', $options = [])\n    {\n        if (!isset($options['type'])) {\n            $options['type'] = 'button';\n        }\n\n        return static::tag('button', $content, $options);\n    }\n\n    /**\n     * Generates a submit button tag.\n     *\n     * Be careful when naming form elements such as submit buttons. According to the [jQuery documentation](https://api.jquery.com/submit/) there\n     * are some reserved names that can cause conflicts, e.g. `submit`, `length`, or `method`.\n     *\n     * @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded.\n     * Therefore you can pass in HTML code such as an image tag. If this is is coming from end users,\n     * you should consider [[encode()]] it to prevent XSS attacks.\n     * @param array $options the tag options in terms of name-value pairs. These will be rendered as\n     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].\n     * If a value is null, the corresponding attribute will not be rendered.\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     * @return string the generated submit button tag\n     */\n    public static function submitButton($content = 'Submit', $options = [])\n    {\n        $options['type'] = 'submit';\n        return static::button($content, $options);\n    }\n\n    /**\n     * Generates a reset button tag.\n     * @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded.\n     * Therefore you can pass in HTML code such as an image tag. If this is is coming from end users,\n     * you should consider [[encode()]] it to prevent XSS attacks.\n     * @param array $options the tag options in terms of name-value pairs. These will be rendered as\n     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].\n     * If a value is null, the corresponding attribute will not be rendered.\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     * @return string the generated reset button tag\n     */\n    public static function resetButton($content = 'Reset', $options = [])\n    {\n        $options['type'] = 'reset';\n        return static::button($content, $options);\n    }\n\n    /**\n     * Generates an input type of the given type.\n     * @param string $type the type attribute.\n     * @param string|null $name the name attribute. If it is null, the name attribute will not be generated.\n     * @param string|null $value the value attribute. If it is null, the value attribute will not be generated.\n     * @param array $options the tag options in terms of name-value pairs. These will be rendered as\n     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].\n     * If a value is null, the corresponding attribute will not be rendered.\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     * @return string the generated input tag\n     */\n    public static function input($type, $name = null, $value = null, $options = [])\n    {\n        if (!isset($options['type'])) {\n            $options['type'] = $type;\n        }\n        $options['name'] = $name;\n        $options['value'] = $value === null ? null : (string) $value;\n        return static::tag('input', '', $options);\n    }\n\n    /**\n     * Generates an input button.\n     * @param string|null $label the value attribute. If it is null, the value attribute will not be generated.\n     * @param array $options the tag options in terms of name-value pairs. These will be rendered as\n     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].\n     * If a value is null, the corresponding attribute will not be rendered.\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     * @return string the generated button tag\n     */\n    public static function buttonInput($label = 'Button', $options = [])\n    {\n        $options['type'] = 'button';\n        $options['value'] = $label;\n        return static::tag('input', '', $options);\n    }\n\n    /**\n     * Generates a submit input button.\n     *\n     * Be careful when naming form elements such as submit buttons. According to the [jQuery documentation](https://api.jquery.com/submit/) there\n     * are some reserved names that can cause conflicts, e.g. `submit`, `length`, or `method`.\n     *\n     * @param string|null $label the value attribute. If it is null, the value attribute will not be generated.\n     * @param array $options the tag options in terms of name-value pairs. These will be rendered as\n     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].\n     * If a value is null, the corresponding attribute will not be rendered.\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     * @return string the generated button tag\n     */\n    public static function submitInput($label = 'Submit', $options = [])\n    {\n        $options['type'] = 'submit';\n        $options['value'] = $label;\n        return static::tag('input', '', $options);\n    }\n\n    /**\n     * Generates a reset input button.\n     * @param string|null $label the value attribute. If it is null, the value attribute will not be generated.\n     * @param array $options the attributes of the button tag. The values will be HTML-encoded using [[encode()]].\n     * Attributes whose value is null will be ignored and not put in the tag returned.\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     * @return string the generated button tag\n     */\n    public static function resetInput($label = 'Reset', $options = [])\n    {\n        $options['type'] = 'reset';\n        $options['value'] = $label;\n        return static::tag('input', '', $options);\n    }\n\n    /**\n     * Generates a text input field.\n     * @param string $name the name attribute.\n     * @param string|null $value the value attribute. If it is null, the value attribute will not be generated.\n     * @param array $options the tag options in terms of name-value pairs. These will be rendered as\n     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].\n     * If a value is null, the corresponding attribute will not be rendered.\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     * @return string the generated text input tag\n     */\n    public static function textInput($name, $value = null, $options = [])\n    {\n        return static::input('text', $name, $value, $options);\n    }\n\n    /**\n     * Generates a hidden input field.\n     * @param string $name the name attribute.\n     * @param string|null $value the value attribute. If it is null, the value attribute will not be generated.\n     * @param array $options the tag options in terms of name-value pairs. These will be rendered as\n     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].\n     * If a value is null, the corresponding attribute will not be rendered.\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     * @return string the generated hidden input tag\n     */\n    public static function hiddenInput($name, $value = null, $options = [])\n    {\n        return static::input('hidden', $name, $value, $options);\n    }\n\n    /**\n     * Generates a password input field.\n     * @param string $name the name attribute.\n     * @param string|null $value the value attribute. If it is null, the value attribute will not be generated.\n     * @param array $options the tag options in terms of name-value pairs. These will be rendered as\n     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].\n     * If a value is null, the corresponding attribute will not be rendered.\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     * @return string the generated password input tag\n     */\n    public static function passwordInput($name, $value = null, $options = [])\n    {\n        return static::input('password', $name, $value, $options);\n    }\n\n    /**\n     * Generates a file input field.\n     * To use a file input field, you should set the enclosing form's \"enctype\" attribute to\n     * be \"multipart/form-data\". After the form is submitted, the uploaded file information\n     * can be obtained via $_FILES[$name] (see PHP documentation).\n     * @param string $name the name attribute.\n     * @param string|null $value the value attribute. If it is null, the value attribute will not be generated.\n     * @param array $options the tag options in terms of name-value pairs. These will be rendered as\n     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].\n     * If a value is null, the corresponding attribute will not be rendered.\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     * @return string the generated file input tag\n     */\n    public static function fileInput($name, $value = null, $options = [])\n    {\n        return static::input('file', $name, $value, $options);\n    }\n\n    /**\n     * Generates a text area input.\n     * @param string $name the input name\n     * @param string $value the input value. Note that it will be encoded using [[encode()]].\n     * @param array $options the tag options in terms of name-value pairs. These will be rendered as\n     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].\n     * If a value is null, the corresponding attribute will not be rendered.\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     * The following special options are recognized:\n     *\n     * - `doubleEncode`: whether to double encode HTML entities in `$value`. If `false`, HTML entities in `$value` will not\n     *   be further encoded. This option is available since version 2.0.11.\n     *\n     * @return string the generated text area tag\n     */\n    public static function textarea($name, $value = '', $options = [])\n    {\n        $options['name'] = $name;\n        $doubleEncode = ArrayHelper::remove($options, 'doubleEncode', true);\n        return static::tag('textarea', static::encode($value, $doubleEncode), $options);\n    }\n\n    /**\n     * Generates a radio button input.\n     * @param string $name the name attribute.\n     * @param bool $checked whether the radio button should be checked.\n     * @param array $options the tag options in terms of name-value pairs.\n     * See [[booleanInput()]] for details about accepted attributes.\n     *\n     * @return string the generated radio button tag\n     */\n    public static function radio($name, $checked = false, $options = [])\n    {\n        return static::booleanInput('radio', $name, $checked, $options);\n    }\n\n    /**\n     * Generates a checkbox input.\n     * @param string $name the name attribute.\n     * @param bool $checked whether the checkbox should be checked.\n     * @param array $options the tag options in terms of name-value pairs.\n     * See [[booleanInput()]] for details about accepted attributes.\n     *\n     * @return string the generated checkbox tag\n     */\n    public static function checkbox($name, $checked = false, $options = [])\n    {\n        return static::booleanInput('checkbox', $name, $checked, $options);\n    }\n\n    /**\n     * Generates a boolean input.\n     * @param string $type the input type. This can be either `radio` or `checkbox`.\n     * @param string $name the name attribute.\n     * @param bool $checked whether the checkbox should be checked.\n     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:\n     *\n     * - uncheck: string, the value associated with the uncheck state of the checkbox. When this attribute\n     *   is present, a hidden input will be generated so that if the checkbox is not checked and is submitted,\n     *   the value of this attribute will still be submitted to the server via the hidden input.\n     * - label: string, a label displayed next to the checkbox.  It will NOT be HTML-encoded. Therefore you can pass\n     *   in HTML code such as an image tag. If this is is coming from end users, you should [[encode()]] it to prevent XSS attacks.\n     *   When this option is specified, the checkbox will be enclosed by a label tag.\n     * - labelOptions: array, the HTML attributes for the label tag. Do not set this option unless you set the \"label\" option.\n     *\n     * The rest of the options will be rendered as the attributes of the resulting checkbox tag. The values will\n     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     *\n     * @return string the generated checkbox tag\n     * @since 2.0.9\n     */\n    protected static function booleanInput($type, $name, $checked = false, $options = [])\n    {\n        // 'checked' option has priority over $checked argument\n        if (!isset($options['checked'])) {\n            $options['checked'] = (bool) $checked;\n        }\n        $value = array_key_exists('value', $options) ? $options['value'] : '1';\n        if (isset($options['uncheck'])) {\n            // add a hidden field so that if the checkbox is not selected, it still submits a value\n            $hiddenOptions = [];\n            if (isset($options['form'])) {\n                $hiddenOptions['form'] = $options['form'];\n            }\n            // make sure disabled input is not sending any value\n            if (!empty($options['disabled'])) {\n                $hiddenOptions['disabled'] = $options['disabled'];\n            }\n            $hidden = static::hiddenInput($name, $options['uncheck'], $hiddenOptions);\n            unset($options['uncheck']);\n        } else {\n            $hidden = '';\n        }\n        if (isset($options['label'])) {\n            $label = $options['label'];\n            $labelOptions = isset($options['labelOptions']) ? $options['labelOptions'] : [];\n            unset($options['label'], $options['labelOptions']);\n            $content = static::label(static::input($type, $name, $value, $options) . ' ' . $label, null, $labelOptions);\n            return $hidden . $content;\n        }\n\n        return $hidden . static::input($type, $name, $value, $options);\n    }\n\n    /**\n     * Generates a drop-down list.\n     * @param string $name the input name\n     * @param string|bool|array|null $selection the selected value(s). String/boolean for single or array for multiple\n     * selection(s).\n     * @param array $items the option data items. The array keys are option values, and the array values\n     * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).\n     * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.\n     * If you have a list of data models, you may convert them into the format described above using\n     * [[\\yii\\helpers\\ArrayHelper::map()]].\n     *\n     * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in\n     * the labels will also be HTML-encoded.\n     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:\n     *\n     * - prompt: string, a prompt text to be displayed as the first option. Since version 2.0.11 you can use an array\n     *   to override the value and to set other tag attributes:\n     *\n     *   ```\n     *   ['text' => 'Please select', 'options' => ['value' => 'none', 'class' => 'prompt', 'label' => 'Select']],\n     *   ```\n     *\n     * - options: array, the attributes for the select option tags. The array keys must be valid option values,\n     *   and the array values are the extra attributes for the corresponding option tags. For example,\n     *\n     *   ```\n     *   [\n     *       'value1' => ['disabled' => true],\n     *       'value2' => ['label' => 'value 2'],\n     *   ];\n     *   ```\n     *\n     * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',\n     *   except that the array keys represent the optgroup labels specified in $items.\n     * - encodeSpaces: bool, whether to encode spaces in option prompt and option value with `&nbsp;` character.\n     *   Defaults to false.\n     * - encode: bool, whether to encode option prompt and option value characters.\n     *   Defaults to `true`. This option is available since 2.0.3.\n     * - strict: boolean, if `$selection` is an array and this value is true a strict comparison will be performed on `$items` keys. Defaults to false.\n     *   This option is available since 2.0.37.\n     *\n     * The rest of the options will be rendered as the attributes of the resulting tag. The values will\n     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     *\n     * @return string the generated drop-down list tag\n     */\n    public static function dropDownList($name, $selection = null, $items = [], $options = [])\n    {\n        if (!empty($options['multiple'])) {\n            return static::listBox($name, $selection, $items, $options);\n        }\n        $options['name'] = $name;\n        unset($options['unselect']);\n        $selectOptions = static::renderSelectOptions($selection, $items, $options);\n        return static::tag('select', \"\\n\" . $selectOptions . \"\\n\", $options);\n    }\n\n    /**\n     * Generates a list box.\n     * @param string $name the input name\n     * @param string|bool|array|null $selection the selected value(s). String for single or array for multiple\n     * selection(s).\n     * @param array $items the option data items. The array keys are option values, and the array values\n     * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).\n     * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.\n     * If you have a list of data models, you may convert them into the format described above using\n     * [[\\yii\\helpers\\ArrayHelper::map()]].\n     *\n     * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in\n     * the labels will also be HTML-encoded.\n     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:\n     *\n     * - prompt: string, a prompt text to be displayed as the first option. Since version 2.0.11 you can use an array\n     *   to override the value and to set other tag attributes:\n     *\n     *   ```\n     *   ['text' => 'Please select', 'options' => ['value' => 'none', 'class' => 'prompt', 'label' => 'Select']],\n     *   ```\n     *\n     * - options: array, the attributes for the select option tags. The array keys must be valid option values,\n     *   and the array values are the extra attributes for the corresponding option tags. For example,\n     *\n     *   ```\n     *   [\n     *       'value1' => ['disabled' => true],\n     *       'value2' => ['label' => 'value 2'],\n     *   ];\n     *   ```\n     *\n     * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',\n     *   except that the array keys represent the optgroup labels specified in $items.\n     * - unselect: string, the value that will be submitted when no option is selected.\n     *   When this attribute is set, a hidden field will be generated so that if no option is selected in multiple\n     *   mode, we can still obtain the posted unselect value.\n     * - encodeSpaces: bool, whether to encode spaces in option prompt and option value with `&nbsp;` character.\n     *   Defaults to false.\n     * - encode: bool, whether to encode option prompt and option value characters.\n     *   Defaults to `true`. This option is available since 2.0.3.\n     * - strict: boolean, if `$selection` is an array and this value is true a strict comparison will be performed on `$items` keys. Defaults to false.\n     *   This option is available since 2.0.37.\n     *\n     * The rest of the options will be rendered as the attributes of the resulting tag. The values will\n     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     *\n     * @return string the generated list box tag\n     */\n    public static function listBox($name, $selection = null, $items = [], $options = [])\n    {\n        if (!array_key_exists('size', $options)) {\n            $options['size'] = 4;\n        }\n        if (!empty($options['multiple']) && !empty($name) && substr_compare($name, '[]', -2, 2)) {\n            $name .= '[]';\n        }\n        $options['name'] = $name;\n        if (isset($options['unselect'])) {\n            // add a hidden field so that if the list box has no option being selected, it still submits a value\n            if (!empty($name) && substr_compare($name, '[]', -2, 2) === 0) {\n                $name = substr($name, 0, -2);\n            }\n            $hiddenOptions = [];\n            // make sure disabled input is not sending any value\n            if (!empty($options['disabled'])) {\n                $hiddenOptions['disabled'] = $options['disabled'];\n            }\n            $hidden = static::hiddenInput($name, $options['unselect'], $hiddenOptions);\n            unset($options['unselect']);\n        } else {\n            $hidden = '';\n        }\n        $selectOptions = static::renderSelectOptions($selection, $items, $options);\n        return $hidden . static::tag('select', \"\\n\" . $selectOptions . \"\\n\", $options);\n    }\n\n    /**\n     * Generates a list of checkboxes.\n     * A checkbox list allows multiple selection, like [[listBox()]].\n     * As a result, the corresponding submitted value is an array.\n     * @param string $name the name attribute of each checkbox.\n     * @param string|array|null $selection the selected value(s). String for single or array for multiple selection(s).\n     * @param array $items the data item used to generate the checkboxes.\n     * The array keys are the checkbox values, while the array values are the corresponding labels.\n     * @param array $options options (name => config) for the checkbox list container tag.\n     * The following options are specially handled:\n     *\n     * - tag: string|false, the tag name of the container element. False to render checkbox without container.\n     *   See also [[tag()]].\n     * - unselect: string, the value that should be submitted when none of the checkboxes is selected.\n     *   By setting this option, a hidden input will be generated.\n     * - disabled: boolean, whether the generated by unselect option hidden input should be disabled. Defaults to false.\n     *   This option is available since version 2.0.16.\n     * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true.\n     *   This option is ignored if `item` option is set.\n     * - strict: boolean, if `$selection` is an array and this value is true a strict comparison will be performed on `$items` keys. Defaults to false.\n     *   This option is available since 2.0.37.\n     * - separator: string, the HTML code that separates items.\n     * - itemOptions: array, the options for generating the checkbox tag using [[checkbox()]].\n     * - item: callable, a callback that can be used to customize the generation of the HTML code\n     *   corresponding to a single item in $items. The signature of this callback must be:\n     *\n     *   ```\n     *   function ($index, $label, $name, $checked, $value)\n     *   ```\n     *\n     *   where $index is the zero-based index of the checkbox in the whole list; $label\n     *   is the label for the checkbox; and $name, $value and $checked represent the name,\n     *   value and the checked status of the checkbox input, respectively.\n     *\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     *\n     * @return string the generated checkbox list\n     */\n    public static function checkboxList($name, $selection = null, $items = [], $options = [])\n    {\n        if (substr($name, -2) !== '[]') {\n            $name .= '[]';\n        }\n        if (ArrayHelper::isTraversable($selection)) {\n            $selection = array_map('strval', ArrayHelper::toArray($selection));\n        }\n\n        $formatter = ArrayHelper::remove($options, 'item');\n        $itemOptions = ArrayHelper::remove($options, 'itemOptions', []);\n        $encode = ArrayHelper::remove($options, 'encode', true);\n        $separator = ArrayHelper::remove($options, 'separator', \"\\n\");\n        $tag = ArrayHelper::remove($options, 'tag', 'div');\n        $strict = ArrayHelper::remove($options, 'strict', false);\n\n        $lines = [];\n        $index = 0;\n        foreach ($items as $value => $label) {\n            $checked = $selection !== null &&\n                (!ArrayHelper::isTraversable($selection) && !strcmp($value, $selection)\n                    || ArrayHelper::isTraversable($selection) && ArrayHelper::isIn((string)$value, $selection, $strict));\n            if ($formatter !== null) {\n                $lines[] = call_user_func($formatter, $index, $label, $name, $checked, $value);\n            } else {\n                $lines[] = static::checkbox($name, $checked, array_merge([\n                    'value' => $value,\n                    'label' => $encode ? static::encode($label) : $label,\n                ], $itemOptions));\n            }\n            $index++;\n        }\n\n        if (isset($options['unselect'])) {\n            // add a hidden field so that if the list box has no option being selected, it still submits a value\n            $name2 = substr($name, -2) === '[]' ? substr($name, 0, -2) : $name;\n            $hiddenOptions = [];\n            // make sure disabled input is not sending any value\n            if (!empty($options['disabled'])) {\n                $hiddenOptions['disabled'] = $options['disabled'];\n            }\n            $hidden = static::hiddenInput($name2, $options['unselect'], $hiddenOptions);\n            unset($options['unselect'], $options['disabled']);\n        } else {\n            $hidden = '';\n        }\n\n        $visibleContent = implode($separator, $lines);\n\n        if ($tag === false) {\n            return $hidden . $visibleContent;\n        }\n\n        return $hidden . static::tag($tag, $visibleContent, $options);\n    }\n\n    /**\n     * Generates a list of radio buttons.\n     * A radio button list is like a checkbox list, except that it only allows single selection.\n     * @param string $name the name attribute of each radio button.\n     * @param string|array|null $selection the selected value(s). String for single or array for multiple selection(s).\n     * @param array $items the data item used to generate the radio buttons.\n     * The array keys are the radio button values, while the array values are the corresponding labels.\n     * @param array $options options (name => config) for the radio button list container tag.\n     * The following options are specially handled:\n     *\n     * - tag: string|false, the tag name of the container element. False to render radio buttons without container.\n     *   See also [[tag()]].\n     * - unselect: string, the value that should be submitted when none of the radio buttons is selected.\n     *   By setting this option, a hidden input will be generated.\n     * - disabled: boolean, whether the generated by unselect option hidden input should be disabled. Defaults to false.\n     *   This option is available since version 2.0.16.\n     * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true.\n     *   This option is ignored if `item` option is set.\n     * - strict: boolean, if `$selection` is an array and this value is true a strict comparison will be performed on `$items` keys. Defaults to false.\n     *   This option is available since 2.0.37.\n     * - separator: string, the HTML code that separates items.\n     * - itemOptions: array, the options for generating the radio button tag using [[radio()]].\n     * - item: callable, a callback that can be used to customize the generation of the HTML code\n     *   corresponding to a single item in $items. The signature of this callback must be:\n     *\n     *   ```\n     *   function ($index, $label, $name, $checked, $value)\n     *   ```\n     *\n     *   where $index is the zero-based index of the radio button in the whole list; $label\n     *   is the label for the radio button; and $name, $value and $checked represent the name,\n     *   value and the checked status of the radio button input, respectively.\n     *\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     *\n     * @return string the generated radio button list\n     */\n    public static function radioList($name, $selection = null, $items = [], $options = [])\n    {\n        if (ArrayHelper::isTraversable($selection)) {\n            $selection = array_map('strval', ArrayHelper::toArray($selection));\n        }\n\n        $formatter = ArrayHelper::remove($options, 'item');\n        $itemOptions = ArrayHelper::remove($options, 'itemOptions', []);\n        $encode = ArrayHelper::remove($options, 'encode', true);\n        $separator = ArrayHelper::remove($options, 'separator', \"\\n\");\n        $tag = ArrayHelper::remove($options, 'tag', 'div');\n        $strict = ArrayHelper::remove($options, 'strict', false);\n\n        $hidden = '';\n        if (isset($options['unselect'])) {\n            // add a hidden field so that if the list box has no option being selected, it still submits a value\n            $hiddenOptions = [];\n            // make sure disabled input is not sending any value\n            if (!empty($options['disabled'])) {\n                $hiddenOptions['disabled'] = $options['disabled'];\n            }\n            $hidden =  static::hiddenInput($name, $options['unselect'], $hiddenOptions);\n            unset($options['unselect'], $options['disabled']);\n        }\n\n        $lines = [];\n        $index = 0;\n        foreach ($items as $value => $label) {\n            $checked = $selection !== null &&\n                (!ArrayHelper::isTraversable($selection) && !strcmp($value, $selection)\n                    || ArrayHelper::isTraversable($selection) && ArrayHelper::isIn((string)$value, $selection, $strict));\n            if ($formatter !== null) {\n                $lines[] = call_user_func($formatter, $index, $label, $name, $checked, $value);\n            } else {\n                $lines[] = static::radio($name, $checked, array_merge([\n                    'value' => $value,\n                    'label' => $encode ? static::encode($label) : $label,\n                ], $itemOptions));\n            }\n            $index++;\n        }\n        $visibleContent = implode($separator, $lines);\n\n        if ($tag === false) {\n            return $hidden . $visibleContent;\n        }\n\n        return $hidden . static::tag($tag, $visibleContent, $options);\n    }\n\n    /**\n     * Generates an unordered list.\n     * @param array|\\Traversable $items the items for generating the list. Each item generates a single list item.\n     * Note that items will be automatically HTML encoded if `$options['encode']` is not set or true.\n     * @param array $options options (name => config) for the radio button list. The following options are supported:\n     *\n     * - encode: boolean, whether to HTML-encode the items. Defaults to true.\n     *   This option is ignored if the `item` option is specified.\n     * - separator: string, the HTML code that separates items. Defaults to a simple newline (`\"\\n\"`).\n     *   This option is available since version 2.0.7.\n     * - itemOptions: array, the HTML attributes for the `li` tags. This option is ignored if the `item` option is specified.\n     * - item: callable, a callback that is used to generate each individual list item.\n     *   The signature of this callback must be:\n     *\n     *   ```\n     *   function ($item, $index)\n     *   ```\n     *\n     *   where $index is the array key corresponding to `$item` in `$items`. The callback should return\n     *   the whole list item tag.\n     *\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     *\n     * @return string the generated unordered list. An empty list tag will be returned if `$items` is empty.\n     */\n    public static function ul($items, $options = [])\n    {\n        $tag = ArrayHelper::remove($options, 'tag', 'ul');\n        $encode = ArrayHelper::remove($options, 'encode', true);\n        $formatter = ArrayHelper::remove($options, 'item');\n        $separator = ArrayHelper::remove($options, 'separator', \"\\n\");\n        $itemOptions = ArrayHelper::remove($options, 'itemOptions', []);\n\n        if (empty($items)) {\n            return static::tag($tag, '', $options);\n        }\n\n        $results = [];\n        foreach ($items as $index => $item) {\n            if ($formatter !== null) {\n                $results[] = call_user_func($formatter, $item, $index);\n            } else {\n                $results[] = static::tag('li', $encode ? static::encode($item) : $item, $itemOptions);\n            }\n        }\n\n        return static::tag(\n            $tag,\n            $separator . implode($separator, $results) . $separator,\n            $options\n        );\n    }\n\n    /**\n     * Generates an ordered list.\n     * @param array|\\Traversable $items the items for generating the list. Each item generates a single list item.\n     * Note that items will be automatically HTML encoded if `$options['encode']` is not set or true.\n     * @param array $options options (name => config) for the radio button list. The following options are supported:\n     *\n     * - encode: boolean, whether to HTML-encode the items. Defaults to true.\n     *   This option is ignored if the `item` option is specified.\n     * - itemOptions: array, the HTML attributes for the `li` tags. This option is ignored if the `item` option is specified.\n     * - item: callable, a callback that is used to generate each individual list item.\n     *   The signature of this callback must be:\n     *\n     *   ```\n     *   function ($item, $index)\n     *   ```\n     *\n     *   where $index is the array key corresponding to `$item` in `$items`. The callback should return\n     *   the whole list item tag.\n     *\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     *\n     * @return string the generated ordered list. An empty string is returned if `$items` is empty.\n     */\n    public static function ol($items, $options = [])\n    {\n        $options['tag'] = 'ol';\n        return static::ul($items, $options);\n    }\n\n    /**\n     * Generates a label tag for the given model attribute.\n     * The label text is the label associated with the attribute, obtained via [[Model::getAttributeLabel()]].\n     * @param Model $model the model object\n     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format\n     * about attribute expression.\n     * @param array $options the tag options in terms of name-value pairs. These will be rendered as\n     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].\n     * If a value is null, the corresponding attribute will not be rendered.\n     * The following options are specially handled:\n     *\n     * - label: this specifies the label to be displayed. Note that this will NOT be [[encode()|encoded]].\n     *   If this is not set, [[Model::getAttributeLabel()]] will be called to get the label for display\n     *   (after encoding).\n     *\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     *\n     * @return string the generated label tag\n     */\n    public static function activeLabel($model, $attribute, $options = [])\n    {\n        $for = ArrayHelper::remove($options, 'for', static::getInputId($model, $attribute));\n        $attribute = static::getAttributeName($attribute);\n        $label = ArrayHelper::remove($options, 'label', static::encode($model->getAttributeLabel($attribute)));\n        return static::label($label, $for, $options);\n    }\n\n    /**\n     * Generates a hint tag for the given model attribute.\n     * The hint text is the hint associated with the attribute, obtained via [[Model::getAttributeHint()]].\n     * If no hint content can be obtained, method will return an empty string.\n     * @param Model $model the model object\n     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format\n     * about attribute expression.\n     * @param array $options the tag options in terms of name-value pairs. These will be rendered as\n     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].\n     * If a value is null, the corresponding attribute will not be rendered.\n     * The following options are specially handled:\n     *\n     * - hint: this specifies the hint to be displayed. Note that this will NOT be [[encode()|encoded]].\n     *   If this is not set, [[Model::getAttributeHint()]] will be called to get the hint for display\n     *   (without encoding).\n     *\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     *\n     * @return string the generated hint tag\n     * @since 2.0.4\n     */\n    public static function activeHint($model, $attribute, $options = [])\n    {\n        $attribute = static::getAttributeName($attribute);\n        $hint = isset($options['hint']) ? $options['hint'] : $model->getAttributeHint($attribute);\n        if (empty($hint)) {\n            return '';\n        }\n        $tag = ArrayHelper::remove($options, 'tag', 'div');\n        unset($options['hint']);\n        return static::tag($tag, $hint, $options);\n    }\n\n    /**\n     * Generates a summary of the validation errors.\n     * If there is no validation error, an empty error summary markup will still be generated, but it will be hidden.\n     * @param Model|Model[] $models the model(s) whose validation errors are to be displayed.\n     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:\n     *\n     * - header: string, the header HTML for the error summary. If not set, a default prompt string will be used.\n     * - footer: string, the footer HTML for the error summary. Defaults to empty string.\n     * - encode: boolean, if set to false then the error messages won't be encoded. Defaults to `true`.\n     * - showAllErrors: boolean, if set to true every error message for each attribute will be shown otherwise\n     *   only the first error message for each attribute will be shown. Defaults to `false`.\n     *   Option is available since 2.0.10.\n     * - emptyClass: string, the class name that is added to an empty summary.\n     *\n     * The rest of the options will be rendered as the attributes of the container tag.\n     *\n     * @return string the generated error summary\n     */\n    public static function errorSummary($models, $options = [])\n    {\n        $header = isset($options['header']) ? $options['header'] : '<p>' . Yii::t('yii', 'Please fix the following errors:') . '</p>';\n        $footer = ArrayHelper::remove($options, 'footer', '');\n        $encode = ArrayHelper::remove($options, 'encode', true);\n        $showAllErrors = ArrayHelper::remove($options, 'showAllErrors', false);\n        $emptyClass = ArrayHelper::remove($options, 'emptyClass', null);\n        unset($options['header']);\n        $lines = self::collectErrors($models, $encode, $showAllErrors);\n        if (empty($lines)) {\n            // still render the placeholder for client-side validation use\n            $content = '<ul></ul>';\n            if ($emptyClass !== null) {\n                $options['class'] = $emptyClass;\n            } else {\n                $options['style'] = isset($options['style']) ? rtrim($options['style'], ';') . '; display:none' : 'display:none';\n            }\n        } else {\n            $content = '<ul><li>' . implode(\"</li>\\n<li>\", $lines) . '</li></ul>';\n        }\n\n        return Html::tag('div', $header . $content . $footer, $options);\n    }\n\n    /**\n     * Return array of the validation errors\n     * @param Model|Model[] $models the model(s) whose validation errors are to be displayed.\n     * @param $encode boolean, if set to false then the error messages won't be encoded.\n     * @param $showAllErrors boolean, if set to true every error message for each attribute will be shown otherwise\n     * only the first error message for each attribute will be shown.\n     * @return array of the validation errors\n     * @since 2.0.14\n     */\n    private static function collectErrors($models, $encode, $showAllErrors)\n    {\n        $lines = [];\n        if (!is_array($models)) {\n            $models = [$models];\n        }\n\n        foreach ($models as $model) {\n            $lines = array_unique(array_merge($lines, $model->getErrorSummary($showAllErrors)));\n        }\n\n        // If there are the same error messages for different attributes, array_unique will leave gaps\n        // between sequential keys. Applying array_values to reorder array keys.\n        $lines = array_values($lines);\n\n        if ($encode) {\n            foreach ($lines as &$line) {\n                $line = Html::encode($line);\n            }\n        }\n\n        return $lines;\n    }\n\n    /**\n     * Generates a tag that contains the first validation error of the specified model attribute.\n     * Note that even if there is no validation error, this method will still return an empty error tag.\n     * @param Model $model the model object\n     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format\n     * about attribute expression.\n     * @param array $options the tag options in terms of name-value pairs. The values will be HTML-encoded\n     * using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.\n     *\n     * The following options are specially handled:\n     *\n     * - tag: this specifies the tag name. If not set, \"div\" will be used.\n     *   See also [[tag()]].\n     * - encode: boolean, if set to false then the error message won't be encoded.\n     * - errorSource (since 2.0.14): \\Closure|callable, callback that will be called to obtain an error message.\n     *   The signature of the callback must be: `function ($model, $attribute)` and return a string.\n     *   When not set, the `$model->getFirstError()` method will be called.\n     *\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     *\n     * @return string the generated label tag\n     */\n    public static function error($model, $attribute, $options = [])\n    {\n        $attribute = static::getAttributeName($attribute);\n        $errorSource = ArrayHelper::remove($options, 'errorSource');\n        if ($errorSource !== null) {\n            $error = call_user_func($errorSource, $model, $attribute);\n        } else {\n            $error = $model->getFirstError($attribute);\n        }\n        $tag = ArrayHelper::remove($options, 'tag', 'div');\n        $encode = ArrayHelper::remove($options, 'encode', true);\n        return Html::tag($tag, $encode ? Html::encode($error) : $error, $options);\n    }\n\n    /**\n     * Generates an input tag for the given model attribute.\n     * This method will generate the \"name\" and \"value\" tag attributes automatically for the model attribute\n     * unless they are explicitly specified in `$options`.\n     * @param string $type the input type (e.g. 'text', 'password')\n     * @param Model $model the model object\n     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format\n     * about attribute expression.\n     * @param array $options the tag options in terms of name-value pairs. These will be rendered as\n     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     * @return string the generated input tag\n     */\n    public static function activeInput($type, $model, $attribute, $options = [])\n    {\n        $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);\n        $value = isset($options['value']) ? $options['value'] : static::getAttributeValue($model, $attribute);\n        if (!array_key_exists('id', $options)) {\n            $options['id'] = static::getInputId($model, $attribute);\n        }\n\n        static::setActivePlaceholder($model, $attribute, $options);\n        self::normalizeMaxLength($model, $attribute, $options);\n\n        return static::input($type, $name, $value, $options);\n    }\n\n    /**\n     * If `maxlength` option is set true and the model attribute is validated by a string validator,\n     * the `maxlength` option will take the max value of [[\\yii\\validators\\StringValidator::max]] and\n     * [[\\yii\\validators\\StringValidator::length]].\n     * @param Model $model the model object\n     * @param string $attribute the attribute name or expression.\n     * @param array $options the tag options in terms of name-value pairs.\n     */\n    private static function normalizeMaxLength($model, $attribute, &$options)\n    {\n        if (isset($options['maxlength']) && $options['maxlength'] === true) {\n            unset($options['maxlength']);\n            $attrName = static::getAttributeName($attribute);\n            foreach ($model->getActiveValidators($attrName) as $validator) {\n                if ($validator instanceof StringValidator && ($validator->max !== null || $validator->length !== null)) {\n                    $options['maxlength'] = max($validator->max, $validator->length);\n                    break;\n                }\n            }\n        }\n    }\n\n    /**\n     * Generates a text input tag for the given model attribute.\n     * This method will generate the \"name\" and \"value\" tag attributes automatically for the model attribute\n     * unless they are explicitly specified in `$options`.\n     * @param Model $model the model object\n     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format\n     * about attribute expression.\n     * @param array $options the tag options in terms of name-value pairs. These will be rendered as\n     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     * The following special options are recognized:\n     *\n     * - maxlength: integer|boolean, when `maxlength` is set true and the model attribute is validated\n     *   by a string validator, the `maxlength` option will take the max value of [[\\yii\\validators\\StringValidator::max]]\n     *   and [[\\yii\\validators\\StringValidator::length].\n     *   This is available since version 2.0.3 and improved taking `length` into account since version 2.0.42.\n     * - placeholder: string|boolean, when `placeholder` equals `true`, the attribute label from the $model will be used\n     *   as a placeholder (this behavior is available since version 2.0.14).\n     *\n     * @return string the generated input tag\n     */\n    public static function activeTextInput($model, $attribute, $options = [])\n    {\n        return static::activeInput('text', $model, $attribute, $options);\n    }\n\n    /**\n     * Generate placeholder from model attribute label.\n     *\n     * @param Model $model the model object\n     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format\n     * about attribute expression.\n     * @param array $options the tag options in terms of name-value pairs. These will be rendered as\n     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].\n     * @since 2.0.14\n     */\n    protected static function setActivePlaceholder($model, $attribute, &$options = [])\n    {\n        if (isset($options['placeholder']) && $options['placeholder'] === true) {\n            $attribute = static::getAttributeName($attribute);\n            $options['placeholder'] = $model->getAttributeLabel($attribute);\n        }\n    }\n\n    /**\n     * Generates a hidden input tag for the given model attribute.\n     * This method will generate the \"name\" and \"value\" tag attributes automatically for the model attribute\n     * unless they are explicitly specified in `$options`.\n     * @param Model $model the model object\n     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format\n     * about attribute expression.\n     * @param array $options the tag options in terms of name-value pairs. These will be rendered as\n     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     * @return string the generated input tag\n     */\n    public static function activeHiddenInput($model, $attribute, $options = [])\n    {\n        return static::activeInput('hidden', $model, $attribute, $options);\n    }\n\n    /**\n     * Generates a password input tag for the given model attribute.\n     * This method will generate the \"name\" and \"value\" tag attributes automatically for the model attribute\n     * unless they are explicitly specified in `$options`.\n     * @param Model $model the model object\n     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format\n     * about attribute expression.\n     * @param array $options the tag options in terms of name-value pairs. These will be rendered as\n     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     * The following special options are recognized:\n     *\n     * - maxlength: integer|boolean, when `maxlength` is set true and the model attribute is validated\n     *   by a string validator, the `maxlength` option will take the max value of [[\\yii\\validators\\StringValidator::max]]\n     *   and [[\\yii\\validators\\StringValidator::length].\n     *   This is available since version 2.0.6 and improved taking `length` into account since version 2.0.42.\n     * - placeholder: string|boolean, when `placeholder` equals `true`, the attribute label from the $model will be used\n     *   as a placeholder (this behavior is available since version 2.0.14).\n     *\n     * @return string the generated input tag\n     */\n    public static function activePasswordInput($model, $attribute, $options = [])\n    {\n        return static::activeInput('password', $model, $attribute, $options);\n    }\n\n    /**\n     * Generates a file input tag for the given model attribute.\n     * This method will generate the \"name\" and \"value\" tag attributes automatically for the model attribute\n     * unless they are explicitly specified in `$options`.\n     * Additionally, if a separate set of HTML options array is defined inside `$options` with a key named `hiddenOptions`,\n     * it will be passed to the `activeHiddenInput` field as its own `$options` parameter.\n     * @param Model $model the model object\n     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format\n     * about attribute expression.\n     * @param array $options the tag options in terms of name-value pairs. These will be rendered as\n     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     * If `hiddenOptions` parameter which is another set of HTML options array is defined, it will be extracted\n     * from `$options` to be used for the hidden input.\n     * @return string the generated input tag\n     */\n    public static function activeFileInput($model, $attribute, $options = [])\n    {\n        $hiddenOptions = ['id' => null, 'value' => ''];\n        if (isset($options['name'])) {\n            $hiddenOptions['name'] = $options['name'];\n        }\n        // make sure disabled input is not sending any value\n        if (!empty($options['disabled'])) {\n            $hiddenOptions['disabled'] = $options['disabled'];\n        }\n        $hiddenOptions = ArrayHelper::merge($hiddenOptions, ArrayHelper::remove($options, 'hiddenOptions', []));\n        // Add a hidden field so that if a model only has a file field, we can\n        // still use isset($_POST[$modelClass]) to detect if the input is submitted.\n        // The hidden input will be assigned its own set of html options via `$hiddenOptions`.\n        // This provides the possibility to interact with the hidden field via client script.\n        // Note: For file-field-only model with `disabled` option set to `true` input submitting detection won't work.\n\n        return static::activeHiddenInput($model, $attribute, $hiddenOptions)\n            . static::activeInput('file', $model, $attribute, $options);\n    }\n\n    /**\n     * Generates a textarea tag for the given model attribute.\n     * The model attribute value will be used as the content in the textarea.\n     * @param Model $model the model object\n     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format\n     * about attribute expression.\n     * @param array $options the tag options in terms of name-value pairs. These will be rendered as\n     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     * The following special options are recognized:\n     *\n     * - maxlength: integer|boolean, when `maxlength` is set true and the model attribute is validated\n     *   by a string validator, the `maxlength` option will take the max value of [[\\yii\\validators\\StringValidator::max]]\n     *   and [[\\yii\\validators\\StringValidator::length].\n     *   This is available since version 2.0.6 and improved taking `length` into account since version 2.0.42.\n     * - placeholder: string|boolean, when `placeholder` equals `true`, the attribute label from the $model will be used\n     *   as a placeholder (this behavior is available since version 2.0.14).\n     *\n     * @return string the generated textarea tag\n     */\n    public static function activeTextarea($model, $attribute, $options = [])\n    {\n        $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);\n        if (isset($options['value'])) {\n            $value = $options['value'];\n            unset($options['value']);\n        } else {\n            $value = static::getAttributeValue($model, $attribute);\n        }\n        if (!array_key_exists('id', $options)) {\n            $options['id'] = static::getInputId($model, $attribute);\n        }\n        self::normalizeMaxLength($model, $attribute, $options);\n        static::setActivePlaceholder($model, $attribute, $options);\n        return static::textarea($name, $value, $options);\n    }\n\n    /**\n     * Generates a radio button tag together with a label for the given model attribute.\n     * This method will generate the \"checked\" tag attribute according to the model attribute value.\n     * @param Model $model the model object\n     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format\n     * about attribute expression.\n     * @param array $options the tag options in terms of name-value pairs.\n     * See [[booleanInput()]] for details about accepted attributes.\n     *\n     * @return string the generated radio button tag\n     */\n    public static function activeRadio($model, $attribute, $options = [])\n    {\n        return static::activeBooleanInput('radio', $model, $attribute, $options);\n    }\n\n    /**\n     * Generates a checkbox tag together with a label for the given model attribute.\n     * This method will generate the \"checked\" tag attribute according to the model attribute value.\n     * @param Model $model the model object\n     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format\n     * about attribute expression.\n     * @param array $options the tag options in terms of name-value pairs.\n     * See [[booleanInput()]] for details about accepted attributes.\n     *\n     * @return string the generated checkbox tag\n     */\n    public static function activeCheckbox($model, $attribute, $options = [])\n    {\n        return static::activeBooleanInput('checkbox', $model, $attribute, $options);\n    }\n\n    /**\n     * Generates a boolean input\n     * This method is mainly called by [[activeCheckbox()]] and [[activeRadio()]].\n     * @param string $type the input type. This can be either `radio` or `checkbox`.\n     * @param Model $model the model object\n     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format\n     * about attribute expression.\n     * @param array $options the tag options in terms of name-value pairs.\n     * See [[booleanInput()]] for details about accepted attributes.\n     * @return string the generated input element\n     * @since 2.0.9\n     */\n    protected static function activeBooleanInput($type, $model, $attribute, $options = [])\n    {\n        $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);\n        $value = static::getAttributeValue($model, $attribute);\n\n        if (!array_key_exists('value', $options)) {\n            $options['value'] = '1';\n        }\n        if (!array_key_exists('uncheck', $options)) {\n            $options['uncheck'] = '0';\n        } elseif ($options['uncheck'] === false) {\n            unset($options['uncheck']);\n        }\n        if (!array_key_exists('label', $options)) {\n            $options['label'] = static::encode($model->getAttributeLabel(static::getAttributeName($attribute)));\n        } elseif ($options['label'] === false) {\n            unset($options['label']);\n        }\n\n        $checked = \"$value\" === \"{$options['value']}\";\n\n        if (!array_key_exists('id', $options)) {\n            $options['id'] = static::getInputId($model, $attribute);\n        }\n\n        return static::$type($name, $checked, $options);\n    }\n\n    /**\n     * Generates a drop-down list for the given model attribute.\n     * The selection of the drop-down list is taken from the value of the model attribute.\n     * @param Model $model the model object\n     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format\n     * about attribute expression.\n     * @param array $items the option data items. The array keys are option values, and the array values\n     * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).\n     * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.\n     * If you have a list of data models, you may convert them into the format described above using\n     * [[\\yii\\helpers\\ArrayHelper::map()]].\n     *\n     * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in\n     * the labels will also be HTML-encoded.\n     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:\n     *\n     * - prompt: string, a prompt text to be displayed as the first option. Since version 2.0.11 you can use an array\n     *   to override the value and to set other tag attributes:\n     *\n     *   ```\n     *   ['text' => 'Please select', 'options' => ['value' => 'none', 'class' => 'prompt', 'label' => 'Select']],\n     *   ```\n     *\n     * - options: array, the attributes for the select option tags. The array keys must be valid option values,\n     *   and the array values are the extra attributes for the corresponding option tags. For example,\n     *\n     *   ```\n     *   [\n     *       'value1' => ['disabled' => true],\n     *       'value2' => ['label' => 'value 2'],\n     *   ];\n     *   ```\n     *\n     * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',\n     *   except that the array keys represent the optgroup labels specified in $items.\n     * - encodeSpaces: bool, whether to encode spaces in option prompt and option value with `&nbsp;` character.\n     *   Defaults to false.\n     * - encode: bool, whether to encode option prompt and option value characters.\n     *   Defaults to `true`. This option is available since 2.0.3.\n     *\n     * The rest of the options will be rendered as the attributes of the resulting tag. The values will\n     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     *\n     * @return string the generated drop-down list tag\n     */\n    public static function activeDropDownList($model, $attribute, $items, $options = [])\n    {\n        if (empty($options['multiple'])) {\n            return static::activeListInput('dropDownList', $model, $attribute, $items, $options);\n        }\n\n        return static::activeListBox($model, $attribute, $items, $options);\n    }\n\n    /**\n     * Generates a list box.\n     * The selection of the list box is taken from the value of the model attribute.\n     * @param Model $model the model object\n     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format\n     * about attribute expression.\n     * @param array $items the option data items. The array keys are option values, and the array values\n     * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).\n     * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.\n     * If you have a list of data models, you may convert them into the format described above using\n     * [[\\yii\\helpers\\ArrayHelper::map()]].\n     *\n     * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in\n     * the labels will also be HTML-encoded.\n     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:\n     *\n     * - prompt: string, a prompt text to be displayed as the first option. Since version 2.0.11 you can use an array\n     *   to override the value and to set other tag attributes:\n     *\n     *   ```\n     *   ['text' => 'Please select', 'options' => ['value' => 'none', 'class' => 'prompt', 'label' => 'Select']],\n     *   ```\n     *\n     * - options: array, the attributes for the select option tags. The array keys must be valid option values,\n     *   and the array values are the extra attributes for the corresponding option tags. For example,\n     *\n     *   ```\n     *   [\n     *       'value1' => ['disabled' => true],\n     *       'value2' => ['label' => 'value 2'],\n     *   ];\n     *   ```\n     *\n     * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',\n     *   except that the array keys represent the optgroup labels specified in $items.\n     * - unselect: string, the value that will be submitted when no option is selected.\n     *   When this attribute is set, a hidden field will be generated so that if no option is selected in multiple\n     *   mode, we can still obtain the posted unselect value.\n     * - encodeSpaces: bool, whether to encode spaces in option prompt and option value with `&nbsp;` character.\n     *   Defaults to false.\n     * - encode: bool, whether to encode option prompt and option value characters.\n     *   Defaults to `true`. This option is available since 2.0.3.\n     *\n     * The rest of the options will be rendered as the attributes of the resulting tag. The values will\n     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     *\n     * @return string the generated list box tag\n     */\n    public static function activeListBox($model, $attribute, $items, $options = [])\n    {\n        return static::activeListInput('listBox', $model, $attribute, $items, $options);\n    }\n\n    /**\n     * Generates a list of checkboxes.\n     * A checkbox list allows multiple selection, like [[listBox()]].\n     * As a result, the corresponding submitted value is an array.\n     * The selection of the checkbox list is taken from the value of the model attribute.\n     * @param Model $model the model object\n     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format\n     * about attribute expression.\n     * @param array $items the data item used to generate the checkboxes.\n     * The array keys are the checkbox values, and the array values are the corresponding labels.\n     * @param array $options options (name => config) for the checkbox list container tag.\n     * The following options are specially handled:\n     *\n     * - tag: string|false, the tag name of the container element. False to render checkbox without container.\n     *   See also [[tag()]].\n     * - unselect: string, the value that should be submitted when none of the checkboxes is selected.\n     *   You may set this option to be null to prevent default value submission.\n     *   If this option is not set, an empty string will be submitted.\n     * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true.\n     *   This option is ignored if `item` option is set.\n     * - separator: string, the HTML code that separates items.\n     * - itemOptions: array, the options for generating the checkbox tag using [[checkbox()]].\n     * - item: callable, a callback that can be used to customize the generation of the HTML code\n     *   corresponding to a single item in $items. The signature of this callback must be:\n     *\n     *   ```\n     *   function ($index, $label, $name, $checked, $value)\n     *   ```\n     *\n     *   where $index is the zero-based index of the checkbox in the whole list; $label\n     *   is the label for the checkbox; and $name, $value and $checked represent the name,\n     *   value and the checked status of the checkbox input.\n     *\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     *\n     * @return string the generated checkbox list\n     */\n    public static function activeCheckboxList($model, $attribute, $items, $options = [])\n    {\n        return static::activeListInput('checkboxList', $model, $attribute, $items, $options);\n    }\n\n    /**\n     * Generates a list of radio buttons.\n     * A radio button list is like a checkbox list, except that it only allows single selection.\n     * The selection of the radio buttons is taken from the value of the model attribute.\n     * @param Model $model the model object\n     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format\n     * about attribute expression.\n     * @param array $items the data item used to generate the radio buttons.\n     * The array keys are the radio values, and the array values are the corresponding labels.\n     * @param array $options options (name => config) for the radio button list container tag.\n     * The following options are specially handled:\n     *\n     * - tag: string|false, the tag name of the container element. False to render radio button without container.\n     *   See also [[tag()]].\n     * - unselect: string, the value that should be submitted when none of the radio buttons is selected.\n     *   You may set this option to be null to prevent default value submission.\n     *   If this option is not set, an empty string will be submitted.\n     * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true.\n     *   This option is ignored if `item` option is set.\n     * - separator: string, the HTML code that separates items.\n     * - itemOptions: array, the options for generating the radio button tag using [[radio()]].\n     * - item: callable, a callback that can be used to customize the generation of the HTML code\n     *   corresponding to a single item in $items. The signature of this callback must be:\n     *\n     *   ```\n     *   function ($index, $label, $name, $checked, $value)\n     *   ```\n     *\n     *   where $index is the zero-based index of the radio button in the whole list; $label\n     *   is the label for the radio button; and $name, $value and $checked represent the name,\n     *   value and the checked status of the radio button input.\n     *\n     * See [[renderTagAttributes()]] for details on how attributes are being rendered.\n     *\n     * @return string the generated radio button list\n     */\n    public static function activeRadioList($model, $attribute, $items, $options = [])\n    {\n        return static::activeListInput('radioList', $model, $attribute, $items, $options);\n    }\n\n    /**\n     * Generates a list of input fields.\n     * This method is mainly called by [[activeListBox()]], [[activeRadioList()]] and [[activeCheckboxList()]].\n     * @param string $type the input type. This can be 'listBox', 'radioList', or 'checkBoxList'.\n     * @param Model $model the model object\n     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format\n     * about attribute expression.\n     * @param array $items the data item used to generate the input fields.\n     * The array keys are the input values, and the array values are the corresponding labels.\n     * @param array $options options (name => config) for the input list. The supported special options\n     * depend on the input type specified by `$type`.\n     * @return string the generated input list\n     */\n    protected static function activeListInput($type, $model, $attribute, $items, $options = [])\n    {\n        $name = ArrayHelper::remove($options, 'name', static::getInputName($model, $attribute));\n        $selection = ArrayHelper::remove($options, 'value', static::getAttributeValue($model, $attribute));\n        if (!array_key_exists('unselect', $options)) {\n            $options['unselect'] = '';\n        }\n        if (!array_key_exists('id', $options)) {\n            $options['id'] = static::getInputId($model, $attribute);\n        }\n\n        return static::$type($name, $selection, $items, $options);\n    }\n\n    /**\n     * Renders the option tags that can be used by [[dropDownList()]] and [[listBox()]].\n     * @param string|array|bool|null $selection the selected value(s). String/boolean for single or array for multiple\n     * selection(s).\n     * @param array $items the option data items. The array keys are option values, and the array values\n     * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).\n     * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.\n     * If you have a list of data models, you may convert them into the format described above using\n     * [[\\yii\\helpers\\ArrayHelper::map()]].\n     *\n     * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in\n     * the labels will also be HTML-encoded.\n     * @param array $tagOptions the $options parameter that is passed to the [[dropDownList()]] or [[listBox()]] call.\n     * This method will take out these elements, if any: \"prompt\", \"options\" and \"groups\". See more details\n     * in [[dropDownList()]] for the explanation of these elements.\n     *\n     * @return string the generated list options\n     */\n    public static function renderSelectOptions($selection, $items, &$tagOptions = [])\n    {\n        if (ArrayHelper::isTraversable($selection)) {\n            $normalizedSelection = [];\n            foreach (ArrayHelper::toArray($selection) as $selectionItem) {\n                if (is_bool($selectionItem)) {\n                    $normalizedSelection[] = $selectionItem ? '1' : '0';\n                } else {\n                    $normalizedSelection[] = (string)$selectionItem;\n                }\n            }\n            $selection = $normalizedSelection;\n        } elseif (is_bool($selection)) {\n            $selection = $selection ? '1' : '0';\n        }\n\n        $lines = [];\n        $encodeSpaces = ArrayHelper::remove($tagOptions, 'encodeSpaces', false);\n        $encode = ArrayHelper::remove($tagOptions, 'encode', true);\n        $strict = ArrayHelper::remove($tagOptions, 'strict', false);\n        if (isset($tagOptions['prompt'])) {\n            $promptOptions = ['value' => ''];\n            if (is_string($tagOptions['prompt'])) {\n                $promptText = $tagOptions['prompt'];\n            } else {\n                $promptText = $tagOptions['prompt']['text'];\n                $promptOptions = array_merge($promptOptions, $tagOptions['prompt']['options']);\n            }\n            $promptText = $encode ? static::encode($promptText) : $promptText;\n            if ($encodeSpaces) {\n                $promptText = str_replace(' ', '&nbsp;', $promptText);\n            }\n            $lines[] = static::tag('option', $promptText, $promptOptions);\n        }\n\n        $options = isset($tagOptions['options']) ? $tagOptions['options'] : [];\n        $groups = isset($tagOptions['groups']) ? $tagOptions['groups'] : [];\n        unset($tagOptions['prompt'], $tagOptions['options'], $tagOptions['groups']);\n        $options['encodeSpaces'] = ArrayHelper::getValue($options, 'encodeSpaces', $encodeSpaces);\n        $options['encode'] = ArrayHelper::getValue($options, 'encode', $encode);\n\n        foreach ($items as $key => $value) {\n            if (is_array($value)) {\n                $groupAttrs = isset($groups[$key]) ? $groups[$key] : [];\n                if (!isset($groupAttrs['label'])) {\n                    $groupAttrs['label'] = $key;\n                }\n                $attrs = ['options' => $options, 'groups' => $groups, 'encodeSpaces' => $encodeSpaces, 'encode' => $encode, 'strict' => $strict];\n                $content = static::renderSelectOptions($selection, $value, $attrs);\n                $lines[] = static::tag('optgroup', \"\\n\" . $content . \"\\n\", $groupAttrs);\n            } else {\n                $attrs = isset($options[$key]) ? $options[$key] : [];\n                $attrs['value'] = (string) $key;\n                if (!array_key_exists('selected', $attrs)) {\n                    $selected = false;\n                    if ($selection !== null) {\n                        if (ArrayHelper::isTraversable($selection)) {\n                            $selected = ArrayHelper::isIn((string)$key, $selection, $strict);\n                        } elseif ($key === '' || $selection === '') {\n                            $selected = $selection === $key;\n                        } elseif ($strict) {\n                            $selected = !strcmp((string)$key, (string)$selection);\n                        } else {\n                            $selected = $selection == $key;\n                        }\n                    }\n\n                    $attrs['selected'] = $selected;\n                }\n                $text = $encode ? static::encode($value) : $value;\n                if ($encodeSpaces) {\n                    $text = str_replace(' ', '&nbsp;', $text);\n                }\n                $lines[] = static::tag('option', $text, $attrs);\n            }\n        }\n\n        return implode(\"\\n\", $lines);\n    }\n\n    /**\n     * Renders the HTML tag attributes.\n     *\n     * Attributes whose values are of boolean type will be treated as\n     * [boolean attributes](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#boolean-attributes).\n     *\n     * Attributes whose values are null will not be rendered.\n     *\n     * The values of attributes will be HTML-encoded using [[encode()]].\n     *\n     * `aria` and `data` attributes get special handling when they are set to an array value. In these cases,\n     * the array will be \"expanded\" and a list of ARIA/data attributes will be rendered. For example,\n     * `'aria' => ['role' => 'checkbox', 'value' => 'true']` would be rendered as\n     * `aria-role=\"checkbox\" aria-value=\"true\"`.\n     *\n     * If a nested `data` value is set to an array, it will be JSON-encoded. For example,\n     * `'data' => ['params' => ['id' => 1, 'name' => 'yii']]` would be rendered as\n     * `data-params='{\"id\":1,\"name\":\"yii\"}'`.\n     *\n     * @param array $attributes attributes to be rendered. The attribute values will be HTML-encoded using [[encode()]].\n     * @return string the rendering result. If the attributes are not empty, they will be rendered\n     * into a string with a leading white space (so that it can be directly appended to the tag name\n     * in a tag). If there is no attribute, an empty string will be returned.\n     * @see addCssClass()\n     */\n    public static function renderTagAttributes($attributes)\n    {\n        if (count($attributes) > 1) {\n            $sorted = [];\n            foreach (static::$attributeOrder as $name) {\n                if (isset($attributes[$name])) {\n                    $sorted[$name] = $attributes[$name];\n                }\n            }\n            $attributes = array_merge($sorted, $attributes);\n        }\n\n        $html = '';\n        foreach ($attributes as $name => $value) {\n            if (is_bool($value)) {\n                if ($value) {\n                    $html .= \" $name\";\n                }\n            } elseif (is_array($value)) {\n                if (in_array($name, static::$dataAttributes)) {\n                    foreach ($value as $n => $v) {\n                        if (is_array($v)) {\n                            $html .= \" $name-$n='\" . Json::htmlEncode($v) . \"'\";\n                        } elseif (is_bool($v)) {\n                            if ($v) {\n                                $html .= \" $name-$n\";\n                            }\n                        } elseif ($v !== null) {\n                            $html .= \" $name-$n=\\\"\" . static::encode($v) . '\"';\n                        }\n                    }\n                } elseif ($name === 'class') {\n                    if (empty($value)) {\n                        continue;\n                    }\n                    if (static::$normalizeClassAttribute === true && count($value) > 1) {\n                        // removes duplicate classes\n                        $value = explode(' ', implode(' ', $value));\n                        $value = array_unique($value);\n                    }\n                    $html .= \" $name=\\\"\" . static::encode(implode(' ', array_filter($value))) . '\"';\n                } elseif ($name === 'style') {\n                    if (empty($value)) {\n                        continue;\n                    }\n                    $html .= \" $name=\\\"\" . static::encode(static::cssStyleFromArray($value)) . '\"';\n                } else {\n                    $html .= \" $name='\" . Json::htmlEncode($value) . \"'\";\n                }\n            } elseif ($value !== null) {\n                $html .= \" $name=\\\"\" . static::encode($value) . '\"';\n            }\n        }\n\n        return $html;\n    }\n\n    /**\n     * Adds a CSS class (or several classes) to the specified options.\n     *\n     * If the CSS class is already in the options, it will not be added again.\n     * If class specification at given options is an array, and some class placed there with the named (string) key,\n     * overriding of such key will have no effect. For example:\n     *\n     * ```\n     * $options = ['class' => ['persistent' => 'initial']];\n     * Html::addCssClass($options, ['persistent' => 'override']);\n     * var_dump($options['class']); // outputs: array('persistent' => 'initial');\n     * ```\n     *\n     * @param array $options the options to be modified.\n     * @param string|array $class the CSS class(es) to be added\n     * @see removeCssClass()\n     */\n    public static function addCssClass(&$options, $class)\n    {\n        if (isset($options['class'])) {\n            if (is_array($options['class'])) {\n                $options['class'] = self::mergeCssClasses($options['class'], (array) $class);\n            } else {\n                $classes = preg_split('/\\s+/', $options['class'], -1, PREG_SPLIT_NO_EMPTY);\n                $options['class'] = implode(' ', self::mergeCssClasses($classes, (array) $class));\n            }\n        } else {\n            $options['class'] = $class;\n        }\n    }\n\n    /**\n     * Merges already existing CSS classes with new one.\n     * This method provides the priority for named existing classes over additional.\n     * @param array $existingClasses already existing CSS classes.\n     * @param array $additionalClasses CSS classes to be added.\n     * @return array merge result.\n     * @see addCssClass()\n     */\n    private static function mergeCssClasses(array $existingClasses, array $additionalClasses)\n    {\n        foreach ($additionalClasses as $key => $class) {\n            if (is_int($key) && !in_array($class, $existingClasses)) {\n                $existingClasses[] = $class;\n            } elseif (!isset($existingClasses[$key])) {\n                $existingClasses[$key] = $class;\n            }\n        }\n\n        return static::$normalizeClassAttribute ? array_unique($existingClasses) : $existingClasses;\n    }\n\n    /**\n     * Removes a CSS class from the specified options.\n     * @param array $options the options to be modified.\n     * @param string|array $class the CSS class(es) to be removed\n     * @see addCssClass()\n     */\n    public static function removeCssClass(&$options, $class)\n    {\n        if (isset($options['class'])) {\n            if (is_array($options['class'])) {\n                $classes = array_diff($options['class'], (array) $class);\n                if (empty($classes)) {\n                    unset($options['class']);\n                } else {\n                    $options['class'] = $classes;\n                }\n            } else {\n                $classes = preg_split('/\\s+/', $options['class'], -1, PREG_SPLIT_NO_EMPTY);\n                $classes = array_diff($classes, (array) $class);\n                if (empty($classes)) {\n                    unset($options['class']);\n                } else {\n                    $options['class'] = implode(' ', $classes);\n                }\n            }\n        }\n    }\n\n    /**\n     * Adds the specified CSS style to the HTML options.\n     *\n     * If the options already contain a `style` element, the new style will be merged\n     * with the existing one. If a CSS property exists in both the new and the old styles,\n     * the old one may be overwritten if `$overwrite` is true.\n     *\n     * For example,\n     *\n     * ```\n     * Html::addCssStyle($options, 'width: 100px; height: 200px');\n     * ```\n     *\n     * @param array $options the HTML options to be modified.\n     * @param string|array $style the new style string (e.g. `'width: 100px; height: 200px'`) or\n     * array (e.g. `['width' => '100px', 'height' => '200px']`).\n     * @param bool $overwrite whether to overwrite existing CSS properties if the new style\n     * contain them too.\n     * @see removeCssStyle()\n     * @see cssStyleFromArray()\n     * @see cssStyleToArray()\n     */\n    public static function addCssStyle(&$options, $style, $overwrite = true)\n    {\n        if (!empty($options['style'])) {\n            $oldStyle = is_array($options['style']) ? $options['style'] : static::cssStyleToArray($options['style']);\n            $newStyle = is_array($style) ? $style : static::cssStyleToArray($style);\n            if (!$overwrite) {\n                foreach ($newStyle as $property => $value) {\n                    if (isset($oldStyle[$property])) {\n                        unset($newStyle[$property]);\n                    }\n                }\n            }\n            $style = array_merge($oldStyle, $newStyle);\n        }\n        $options['style'] = is_array($style) ? static::cssStyleFromArray($style) : $style;\n    }\n\n    /**\n     * Removes the specified CSS style from the HTML options.\n     *\n     * For example,\n     *\n     * ```\n     * Html::removeCssStyle($options, ['width', 'height']);\n     * ```\n     *\n     * @param array $options the HTML options to be modified.\n     * @param string|array $properties the CSS properties to be removed. You may use a string\n     * if you are removing a single property.\n     * @see addCssStyle()\n     */\n    public static function removeCssStyle(&$options, $properties)\n    {\n        if (!empty($options['style'])) {\n            $style = is_array($options['style']) ? $options['style'] : static::cssStyleToArray($options['style']);\n            foreach ((array) $properties as $property) {\n                unset($style[$property]);\n            }\n            $options['style'] = static::cssStyleFromArray($style);\n        }\n    }\n\n    /**\n     * Converts a CSS style array into a string representation.\n     *\n     * For example,\n     *\n     * ```\n     * print_r(Html::cssStyleFromArray(['width' => '100px', 'height' => '200px']));\n     * // will display: 'width: 100px; height: 200px;'\n     * ```\n     *\n     * @param array $style the CSS style array. The array keys are the CSS property names,\n     * and the array values are the corresponding CSS property values.\n     * @return string the CSS style string. If the CSS style is empty, a null will be returned.\n     */\n    public static function cssStyleFromArray(array $style)\n    {\n        $result = '';\n        foreach ($style as $name => $value) {\n            $result .= \"$name: $value; \";\n        }\n        // return null if empty to avoid rendering the \"style\" attribute\n        return $result === '' ? null : rtrim($result);\n    }\n\n    /**\n     * Converts a CSS style string into an array representation.\n     *\n     * The array keys are the CSS property names, and the array values\n     * are the corresponding CSS property values.\n     *\n     * For example,\n     *\n     * ```\n     * print_r(Html::cssStyleToArray('width: 100px; height: 200px;'));\n     * // will display: ['width' => '100px', 'height' => '200px']\n     * ```\n     *\n     * @param string $style the CSS style string\n     * @return array the array representation of the CSS style\n     */\n    public static function cssStyleToArray($style)\n    {\n        $result = [];\n        foreach (explode(';', $style) as $property) {\n            $property = explode(':', $property);\n            if (count($property) > 1) {\n                $result[trim($property[0])] = trim($property[1]);\n            }\n        }\n\n        return $result;\n    }\n\n    /**\n     * Returns the real attribute name from the given attribute expression.\n     *\n     * An attribute expression is an attribute name prefixed and/or suffixed with array indexes.\n     * It is mainly used in tabular data input and/or input of array type. Below are some examples:\n     *\n     * - `[0]content` is used in tabular data input to represent the \"content\" attribute\n     *   for the first model in tabular input;\n     * - `dates[0]` represents the first array element of the \"dates\" attribute;\n     * - `[0]dates[0]` represents the first array element of the \"dates\" attribute\n     *   for the first model in tabular input.\n     *\n     * If `$attribute` has neither prefix nor suffix, it will be returned back without change.\n     * @param string $attribute the attribute name or expression\n     * @return string the attribute name without prefix and suffix.\n     * @throws InvalidArgumentException if the attribute name contains non-word characters.\n     */\n    public static function getAttributeName($attribute)\n    {\n        if (preg_match(static::$attributeRegex, $attribute, $matches)) {\n            return $matches[2];\n        }\n\n        throw new InvalidArgumentException('Attribute name must contain word characters only.');\n    }\n\n    /**\n     * Returns the value of the specified attribute name or expression.\n     *\n     * For an attribute expression like `[0]dates[0]`, this method will return the value of `$model->dates[0]`.\n     * See [[getAttributeName()]] for more details about attribute expression.\n     *\n     * If an attribute value is an instance of [[ActiveRecordInterface]] or an array of such instances,\n     * the primary value(s) of the AR instance(s) will be returned instead.\n     *\n     * @param Model $model the model object\n     * @param string $attribute the attribute name or expression\n     * @return string|array|null the corresponding attribute value\n     * @throws InvalidArgumentException if the attribute name contains non-word characters.\n     */\n    public static function getAttributeValue($model, $attribute)\n    {\n        if (!preg_match(static::$attributeRegex, $attribute, $matches)) {\n            throw new InvalidArgumentException('Attribute name must contain word characters only.');\n        }\n        $attribute = $matches[2];\n        $value = $model->$attribute;\n        if ($matches[3] !== '') {\n            foreach (explode('][', trim($matches[3], '[]')) as $id) {\n                if ((is_array($value) || $value instanceof \\ArrayAccess) && isset($value[$id])) {\n                    $value = $value[$id];\n                } else {\n                    return null;\n                }\n            }\n        }\n\n        // https://github.com/yiisoft/yii2/issues/1457\n        if (is_array($value)) {\n            foreach ($value as $i => $v) {\n                if ($v instanceof ActiveRecordInterface) {\n                    $v = $v->getPrimaryKey(false);\n                    $value[$i] = is_array($v) ? json_encode($v) : $v;\n                }\n            }\n        } elseif ($value instanceof ActiveRecordInterface) {\n            $value = $value->getPrimaryKey(false);\n\n            return is_array($value) ? json_encode($value) : $value;\n        }\n\n        return $value;\n    }\n\n    /**\n     * Generates an appropriate input name for the specified attribute name or expression.\n     *\n     * This method generates a name that can be used as the input name to collect user input\n     * for the specified attribute. The name is generated according to the [[Model::formName|form name]]\n     * of the model and the given attribute name. For example, if the form name of the `Post` model\n     * is `Post`, then the input name generated for the `content` attribute would be `Post[content]`.\n     *\n     * See [[getAttributeName()]] for explanation of attribute expression.\n     *\n     * @param Model $model the model object\n     * @param string $attribute the attribute name or expression\n     * @return string the generated input name\n     * @throws InvalidArgumentException if the attribute name contains non-word characters.\n     */\n    public static function getInputName($model, $attribute)\n    {\n        $formName = $model->formName();\n        if (!preg_match(static::$attributeRegex, $attribute, $matches)) {\n            throw new InvalidArgumentException('Attribute name must contain word characters only.');\n        }\n        $prefix = $matches[1];\n        $attribute = $matches[2];\n        $suffix = $matches[3];\n        if ($formName === '' && $prefix === '') {\n            return $attribute . $suffix;\n        } elseif ($formName !== '') {\n            return $formName . $prefix . \"[$attribute]\" . $suffix;\n        }\n\n        throw new InvalidArgumentException(get_class($model) . '::formName() cannot be empty for tabular inputs.');\n    }\n\n    /**\n     * Converts input name to ID.\n     *\n     * For example, if `$name` is `Post[content]`, this method will return `post-content`.\n     *\n     * @param string $name the input name\n     * @return string the generated input ID\n     * @since 2.0.43\n     */\n    public static function getInputIdByName($name)\n    {\n        $charset = Yii::$app ? Yii::$app->charset : 'UTF-8';\n        $name = mb_strtolower($name, $charset);\n        return str_replace(['[]', '][', '[', ']', ' ', '.', '--'], ['', '-', '-', '', '-', '-', '-'], $name);\n    }\n\n    /**\n     * Generates an appropriate input ID for the specified attribute name or expression.\n     *\n     * @param Model $model the model object\n     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for explanation of attribute expression.\n     * @return string the generated input ID.\n     * @throws InvalidArgumentException if the attribute name contains non-word characters.\n     */\n    public static function getInputId($model, $attribute)\n    {\n        $name = static::getInputName($model, $attribute);\n        return static::getInputIdByName($name);\n    }\n\n    /**\n     * Escapes regular expression to use in JavaScript.\n     * @param string $regexp the regular expression to be escaped.\n     * @return string the escaped result.\n     * @since 2.0.6\n     */\n    public static function escapeJsRegularExpression($regexp)\n    {\n        $pattern = preg_replace('/\\\\\\\\x\\{?([0-9a-fA-F]+)\\}?/', '\\u$1', $regexp);\n        $deliminator = substr($pattern, 0, 1);\n        $pos = strrpos($pattern, $deliminator, 1);\n        $flag = substr($pattern, $pos + 1);\n        if ($deliminator !== '/') {\n            $pattern = '/' . str_replace('/', '\\\\/', substr($pattern, 1, $pos - 1)) . '/';\n        } else {\n            $pattern = substr($pattern, 0, $pos + 1);\n        }\n        if (!empty($flag)) {\n            $pattern .= preg_replace('/[^igmu]/', '', $flag);\n        }\n\n        return $pattern;\n    }\n}\n"
  },
  {
    "path": "framework/helpers/BaseHtmlPurifier.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\helpers;\n\n/**\n * BaseHtmlPurifier provides concrete implementation for [[HtmlPurifier]].\n *\n * Do not use BaseHtmlPurifier. Use [[HtmlPurifier]] instead.\n *\n * @author Alexander Makarov <sam@rmcreative.ru>\n * @since 2.0\n */\nclass BaseHtmlPurifier\n{\n    /**\n     * Passes markup through HTMLPurifier making it safe to output to end user.\n     *\n     * @param string $content The HTML content to purify\n     * @param array|\\Closure|null $config The config to use for HtmlPurifier.\n     * If not specified or `null` the default config will be used.\n     * You can use an array or an anonymous function to provide configuration options:\n     *\n     * - An array will be passed to the `HTMLPurifier_Config::create()` method.\n     * - An anonymous function will be called after the config was created.\n     *   The signature should be: `function($config)` where `$config` will be an\n     *   instance of `HTMLPurifier_Config`.\n     *\n     *   Here is a usage example of such a function:\n     *\n     *   ```\n     *   // Allow the HTML5 data attribute `data-type` on `img` elements.\n     *   $content = HtmlPurifier::process($content, function ($config) {\n     *     $config->getHTMLDefinition(true)\n     *            ->addAttribute('img', 'data-type', 'Text');\n     *   });\n     * ```\n     *\n     * @return string the purified HTML content.\n     */\n    public static function process($content, $config = null)\n    {\n        $configInstance = \\HTMLPurifier_Config::create($config instanceof \\Closure ? null : $config);\n        $configInstance->autoFinalize = false;\n        $purifier = \\HTMLPurifier::instance($configInstance);\n        $purifier->config->set('Cache.SerializerPath', \\Yii::$app->getRuntimePath());\n        $purifier->config->set('Cache.SerializerPermissions', 0775);\n\n        static::configure($configInstance);\n        if ($config instanceof \\Closure) {\n            call_user_func($config, $configInstance);\n        }\n\n        return $purifier->purify($content);\n    }\n\n    /**\n     * Allow the extended HtmlPurifier class to set some default config options.\n     * @param \\HTMLPurifier_Config $config\n     * @since 2.0.3\n     */\n    protected static function configure($config)\n    {\n    }\n}\n"
  },
  {
    "path": "framework/helpers/BaseInflector.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\helpers;\n\nuse Yii;\n\n/**\n * BaseInflector provides concrete implementation for [[Inflector]].\n *\n * Do not use BaseInflector. Use [[Inflector]] instead.\n *\n * @author Antonio Ramirez <amigo.cobos@gmail.com>\n * @author Alexander Makarov <sam@rmcreative.ru>\n * @since 2.0\n */\nclass BaseInflector\n{\n    /**\n     * @var array the rules for converting a word into its plural form.\n     * The keys are the regular expressions and the values are the corresponding replacements.\n     */\n    public static $plurals = [\n        '/([nrlm]ese|deer|fish|sheep|measles|ois|pox|media)$/i' => '\\1',\n        '/^(sea[- ]bass)$/i' => '\\1',\n        '/(m)ove$/i' => '\\1oves',\n        '/(f)oot$/i' => '\\1eet',\n        '/(h)uman$/i' => '\\1umans',\n        '/(s)tatus$/i' => '\\1tatuses',\n        '/(s)taff$/i' => '\\1taff',\n        '/(t)ooth$/i' => '\\1eeth',\n        '/(quiz)$/i' => '\\1zes',\n        '/^(ox)$/i' => '\\1\\2en',\n        '/([m|l])ouse$/i' => '\\1ice',\n        '/(matr|vert|ind)(ix|ex)$/i' => '\\1ices',\n        '/(x|ch|ss|sh)$/i' => '\\1es',\n        '/([^aeiouy]|qu)y$/i' => '\\1ies',\n        '/(hive)$/i' => '\\1s',\n        '/(?:([^f])fe|([lr])f)$/i' => '\\1\\2ves',\n        '/sis$/i' => 'ses',\n        '/([ti])um$/i' => '\\1a',\n        '/(p)erson$/i' => '\\1eople',\n        '/(m)an$/i' => '\\1en',\n        '/(c)hild$/i' => '\\1hildren',\n        '/(buffal|tomat|potat|ech|her|vet)o$/i' => '\\1oes',\n        '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\\1i',\n        '/us$/i' => 'uses',\n        '/(alias)$/i' => '\\1es',\n        '/(ax|cris|test)is$/i' => '\\1es',\n        '/(currenc)y$/' => '\\1ies',\n        '/s$/' => 's',\n        '/^$/' => '',\n        '/$/' => 's',\n    ];\n    /**\n     * @var array the rules for converting a word into its singular form.\n     * The keys are the regular expressions and the values are the corresponding replacements.\n     */\n    public static $singulars = [\n        '/([nrlm]ese|deer|fish|sheep|measles|ois|pox|media|ss)$/i' => '\\1',\n        '/^(sea[- ]bass)$/i' => '\\1',\n        '/(s)tatuses$/i' => '\\1tatus',\n        '/(f)eet$/i' => '\\1oot',\n        '/(t)eeth$/i' => '\\1ooth',\n        '/^(.*)(menu)s$/i' => '\\1\\2',\n        '/(quiz)zes$/i' => '\\\\1',\n        '/(matr)ices$/i' => '\\1ix',\n        '/(vert|ind)ices$/i' => '\\1ex',\n        '/^(ox)en/i' => '\\1',\n        '/(alias)(es)*$/i' => '\\1',\n        '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\\1us',\n        '/([ftw]ax)es/i' => '\\1',\n        '/(cris|ax|test)es$/i' => '\\1is',\n        '/(shoe|slave)s$/i' => '\\1',\n        '/(o)es$/i' => '\\1',\n        '/ouses$/' => 'ouse',\n        '/([^a])uses$/' => '\\1us',\n        '/([m|l])ice$/i' => '\\1ouse',\n        '/(x|ch|ss|sh)es$/i' => '\\1',\n        '/(m)ovies$/i' => '\\1\\2ovie',\n        '/(s)eries$/i' => '\\1\\2eries',\n        '/([^aeiouy]|qu)ies$/i' => '\\1y',\n        '/([lr])ves$/i' => '\\1f',\n        '/(tive)s$/i' => '\\1',\n        '/(hive)s$/i' => '\\1',\n        '/(drive)s$/i' => '\\1',\n        '/([^fo])ves$/i' => '\\1fe',\n        '/(^analy)ses$/i' => '\\1sis',\n        '/(analy|diagno|^ba|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\\1\\2sis',\n        '/([ti])a$/i' => '\\1um',\n        '/(p)eople$/i' => '\\1\\2erson',\n        '/(m)en$/i' => '\\1an',\n        '/(c)hildren$/i' => '\\1\\2hild',\n        '/(n)ews$/i' => '\\1\\2ews',\n        '/(n)etherlands$/i' => '\\1\\2etherlands',\n        '/eaus$/' => 'eau',\n        '/(currenc)ies$/' => '\\1y',\n        '/^(.*us)$/' => '\\\\1',\n        '/s$/i' => '',\n    ];\n    /**\n     * @var array the special rules for converting a word between its plural form and singular form.\n     * The keys are the special words in singular form, and the values are the corresponding plural form.\n     */\n    public static $specials = [\n        'atlas' => 'atlases',\n        'beef' => 'beefs',\n        'brother' => 'brothers',\n        'cafe' => 'cafes',\n        'child' => 'children',\n        'cookie' => 'cookies',\n        'corpus' => 'corpuses',\n        'cow' => 'cows',\n        'curve' => 'curves',\n        'foe' => 'foes',\n        'ganglion' => 'ganglions',\n        'genie' => 'genies',\n        'genus' => 'genera',\n        'graffito' => 'graffiti',\n        'hoof' => 'hoofs',\n        'loaf' => 'loaves',\n        'man' => 'men',\n        'money' => 'monies',\n        'mongoose' => 'mongooses',\n        'move' => 'moves',\n        'mythos' => 'mythoi',\n        'niche' => 'niches',\n        'numen' => 'numina',\n        'occiput' => 'occiputs',\n        'octopus' => 'octopuses',\n        'opus' => 'opuses',\n        'ox' => 'oxen',\n        'pasta' => 'pasta',\n        'penis' => 'penises',\n        'sex' => 'sexes',\n        'soliloquy' => 'soliloquies',\n        'testis' => 'testes',\n        'trilby' => 'trilbys',\n        'turf' => 'turfs',\n        'wave' => 'waves',\n        'Amoyese' => 'Amoyese',\n        'bison' => 'bison',\n        'Borghese' => 'Borghese',\n        'bream' => 'bream',\n        'breeches' => 'breeches',\n        'britches' => 'britches',\n        'buffalo' => 'buffalo',\n        'cantus' => 'cantus',\n        'carp' => 'carp',\n        'chassis' => 'chassis',\n        'clippers' => 'clippers',\n        'cod' => 'cod',\n        'coitus' => 'coitus',\n        'Congoese' => 'Congoese',\n        'contretemps' => 'contretemps',\n        'corps' => 'corps',\n        'debris' => 'debris',\n        'diabetes' => 'diabetes',\n        'djinn' => 'djinn',\n        'eland' => 'eland',\n        'elk' => 'elk',\n        'equipment' => 'equipment',\n        'Faroese' => 'Faroese',\n        'flounder' => 'flounder',\n        'Foochowese' => 'Foochowese',\n        'gallows' => 'gallows',\n        'Genevese' => 'Genevese',\n        'Genoese' => 'Genoese',\n        'Gilbertese' => 'Gilbertese',\n        'graffiti' => 'graffiti',\n        'headquarters' => 'headquarters',\n        'herpes' => 'herpes',\n        'hijinks' => 'hijinks',\n        'Hottentotese' => 'Hottentotese',\n        'information' => 'information',\n        'innings' => 'innings',\n        'jackanapes' => 'jackanapes',\n        'Kiplingese' => 'Kiplingese',\n        'Kongoese' => 'Kongoese',\n        'Lucchese' => 'Lucchese',\n        'mackerel' => 'mackerel',\n        'Maltese' => 'Maltese',\n        'mews' => 'mews',\n        'moose' => 'moose',\n        'mumps' => 'mumps',\n        'Nankingese' => 'Nankingese',\n        'news' => 'news',\n        'nexus' => 'nexus',\n        'Niasese' => 'Niasese',\n        'Pekingese' => 'Pekingese',\n        'Piedmontese' => 'Piedmontese',\n        'pincers' => 'pincers',\n        'Pistoiese' => 'Pistoiese',\n        'pliers' => 'pliers',\n        'Portuguese' => 'Portuguese',\n        'proceedings' => 'proceedings',\n        'rabies' => 'rabies',\n        'rice' => 'rice',\n        'rhinoceros' => 'rhinoceros',\n        'salmon' => 'salmon',\n        'Sarawakese' => 'Sarawakese',\n        'scissors' => 'scissors',\n        'series' => 'series',\n        'Shavese' => 'Shavese',\n        'shears' => 'shears',\n        'siemens' => 'siemens',\n        'species' => 'species',\n        'swine' => 'swine',\n        'testes' => 'testes',\n        'trousers' => 'trousers',\n        'trout' => 'trout',\n        'tuna' => 'tuna',\n        'Vermontese' => 'Vermontese',\n        'Wenchowese' => 'Wenchowese',\n        'whiting' => 'whiting',\n        'wildebeest' => 'wildebeest',\n        'Yengeese' => 'Yengeese',\n        'software' => 'software',\n        'hardware' => 'hardware',\n    ];\n    /**\n     * @var array fallback map for transliteration used by [[transliterate()]] when intl isn't available.\n     */\n    public static $transliteration = [\n        'À' => 'A', 'Á' => 'A', 'Â' => 'A', 'Ã' => 'A', 'Ä' => 'A', 'Å' => 'A', 'Æ' => 'AE', 'Ç' => 'C',\n        'È' => 'E', 'É' => 'E', 'Ê' => 'E', 'Ë' => 'E', 'Ì' => 'I', 'Í' => 'I', 'Î' => 'I', 'Ï' => 'I',\n        'Ð' => 'D', 'Ñ' => 'N', 'Ò' => 'O', 'Ó' => 'O', 'Ô' => 'O', 'Õ' => 'O', 'Ö' => 'O', 'Ő' => 'O',\n        'Ø' => 'O', 'Ù' => 'U', 'Ú' => 'U', 'Û' => 'U', 'Ü' => 'U', 'Ű' => 'U', 'Ý' => 'Y', 'Þ' => 'TH',\n        'ß' => 'ss',\n        'à' => 'a', 'á' => 'a', 'â' => 'a', 'ã' => 'a', 'ä' => 'a', 'å' => 'a', 'æ' => 'ae', 'ç' => 'c',\n        'è' => 'e', 'é' => 'e', 'ê' => 'e', 'ë' => 'e', 'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i',\n        'ð' => 'd', 'ñ' => 'n', 'ò' => 'o', 'ó' => 'o', 'ô' => 'o', 'õ' => 'o', 'ö' => 'o', 'ő' => 'o',\n        'ø' => 'o', 'ù' => 'u', 'ú' => 'u', 'û' => 'u', 'ü' => 'u', 'ű' => 'u', 'ý' => 'y', 'þ' => 'th',\n        'ÿ' => 'y',\n    ];\n    /**\n     * Shortcut for `Any-Latin; NFKD` transliteration rule.\n     *\n     * The rule is strict, letters will be transliterated with\n     * the closest sound-representation chars. The result may contain any UTF-8 chars. For example:\n     * `获取到 どちら Українська: ґ,є, Српска: ђ, њ, џ! ¿Español?` will be transliterated to\n     * `huò qǔ dào dochira Ukraí̈nsʹka: g̀,ê, Srpska: đ, n̂, d̂! ¿Español?`.\n     *\n     * Used in [[transliterate()]].\n     * For detailed information see [unicode normalization forms](https://unicode.org/reports/tr15/#Normalization_Forms_Table)\n     * @see https://unicode.org/reports/tr15/#Normalization_Forms_Table\n     * @see transliterate()\n     * @since 2.0.7\n     */\n    public const TRANSLITERATE_STRICT = 'Any-Latin; NFKD';\n    /**\n     * Shortcut for `Any-Latin; Latin-ASCII` transliteration rule.\n     *\n     * The rule is medium, letters will be\n     * transliterated to characters of Latin-1 (ISO 8859-1) ASCII table. For example:\n     * `获取到 どちら Українська: ґ,є, Српска: ђ, њ, џ! ¿Español?` will be transliterated to\n     * `huo qu dao dochira Ukrainsʹka: g,e, Srpska: d, n, d! ¿Espanol?`.\n     *\n     * Used in [[transliterate()]].\n     * For detailed information see [unicode normalization forms](https://unicode.org/reports/tr15/#Normalization_Forms_Table)\n     * @see https://unicode.org/reports/tr15/#Normalization_Forms_Table\n     * @see transliterate()\n     * @since 2.0.7\n     */\n    public const TRANSLITERATE_MEDIUM = 'Any-Latin; Latin-ASCII';\n    /**\n     * Shortcut for `Any-Latin; Latin-ASCII; [\\u0080-\\uffff] remove` transliteration rule.\n     *\n     * The rule is loose,\n     * letters will be transliterated with the characters of Basic Latin Unicode Block.\n     * For example:\n     * `获取到 どちら Українська: ґ,є, Српска: ђ, њ, џ! ¿Español?` will be transliterated to\n     * `huo qu dao dochira Ukrainska: g,e, Srpska: d, n, d! Espanol?`.\n     *\n     * Used in [[transliterate()]].\n     * For detailed information see [unicode normalization forms](https://unicode.org/reports/tr15/#Normalization_Forms_Table)\n     * @see https://unicode.org/reports/tr15/#Normalization_Forms_Table\n     * @see transliterate()\n     * @since 2.0.7\n     */\n    public const TRANSLITERATE_LOOSE = 'Any-Latin; Latin-ASCII; [\\u0080-\\uffff] remove';\n    /**\n     * @var mixed Either a [[\\Transliterator]], or a string from which a [[\\Transliterator]] can be built\n     * for transliteration. Used by [[transliterate()]] when intl is available. Defaults to [[TRANSLITERATE_LOOSE]]\n     * @see https://www.php.net/manual/en/transliterator.transliterate.php\n     */\n    public static $transliterator = self::TRANSLITERATE_LOOSE;\n\n\n    /**\n     * Converts a word to its plural form.\n     * Note that this is for English only!\n     * For example, 'apple' will become 'apples', and 'child' will become 'children'.\n     * @param string $word the word to be pluralized\n     * @return string the pluralized word\n     */\n    public static function pluralize($word)\n    {\n        if (empty($word)) {\n            return (string) $word;\n        }\n        if (isset(static::$specials[$word])) {\n            return static::$specials[$word];\n        }\n        foreach (static::$plurals as $rule => $replacement) {\n            if (preg_match($rule, $word)) {\n                return preg_replace($rule, $replacement, $word);\n            }\n        }\n\n        return $word;\n    }\n\n    /**\n     * Returns the singular of the $word.\n     * @param string $word the english word to singularize\n     * @return string Singular noun.\n     */\n    public static function singularize($word)\n    {\n        if (empty($word)) {\n            return (string) $word;\n        }\n        $result = array_search($word, static::$specials, true);\n        if ($result !== false) {\n            return $result;\n        }\n        foreach (static::$singulars as $rule => $replacement) {\n            if (preg_match($rule, $word)) {\n                return preg_replace($rule, $replacement, $word);\n            }\n        }\n\n        return $word;\n    }\n\n    /**\n     * Converts an underscored or CamelCase word into a English\n     * sentence.\n     * @param string $words\n     * @param bool $ucAll whether to set all words to uppercase\n     * @return string\n     */\n    public static function titleize($words, $ucAll = false)\n    {\n        if (empty($words)) {\n            return (string) $words;\n        }\n        $words = static::humanize(static::underscore($words), $ucAll);\n\n        return $ucAll ? StringHelper::mb_ucwords($words, self::encoding()) : StringHelper::mb_ucfirst($words, self::encoding());\n    }\n\n    /**\n     * Returns given word as CamelCased.\n     *\n     * Converts a word like \"send_email\" to \"SendEmail\". It\n     * will remove non alphanumeric character from the word, so\n     * \"who's online\" will be converted to \"WhoSOnline\".\n     * @param string $word the word to CamelCase\n     * @return string\n     * @see variablize()\n     */\n    public static function camelize($word)\n    {\n        if (empty($word)) {\n            return (string) $word;\n        }\n        return str_replace(' ', '', StringHelper::mb_ucwords(preg_replace('/[^\\pL\\pN]+/u', ' ', $word), self::encoding()));\n    }\n\n    /**\n     * Converts a CamelCase name into space-separated words.\n     * For example, 'PostTag' will be converted to 'Post Tag'.\n     * @param string $name the string to be converted\n     * @param bool $ucwords whether to capitalize the first letter in each word\n     * @return string the resulting words\n     */\n    public static function camel2words($name, $ucwords = true)\n    {\n        if (empty($name)) {\n            return (string) $name;\n        }\n        // Add a space before any uppercase letter preceded by a lowercase letter (xY => x Y)\n        // and any uppercase letter preceded by an uppercase letter and followed by a lowercase letter (XYz => X Yz)\n        $label = preg_replace('/(?<=\\p{Ll})\\p{Lu}|(?<=\\p{L})\\p{Lu}(?=\\p{Ll})/u', ' \\0', $name);\n\n        $label = mb_strtolower(trim(str_replace(['-', '_', '.'], ' ', $label)), self::encoding());\n\n        return $ucwords ? StringHelper::mb_ucwords($label, self::encoding()) : $label;\n    }\n\n    /**\n     * Converts a CamelCase name into an ID in lowercase.\n     * Words in the ID may be concatenated using the specified character (defaults to '-').\n     * For example, 'PostTag' will be converted to 'post-tag'.\n     * @param string $name the string to be converted\n     * @param string $separator the character used to concatenate the words in the ID\n     * @param bool|string $strict whether to insert a separator between two consecutive uppercase chars, defaults to false\n     * @return string the resulting ID\n     */\n    public static function camel2id($name, $separator = '-', $strict = false)\n    {\n        if (empty($name)) {\n            return (string) $name;\n        }\n        $regex = $strict ? '/\\p{Lu}/u' : '/(?<!\\p{Lu})\\p{Lu}/u';\n        if ($separator === '_') {\n            return mb_strtolower(trim(preg_replace($regex, '_\\0', $name), '_'), self::encoding());\n        }\n\n        return mb_strtolower(trim(str_replace('_', $separator, preg_replace($regex, $separator . '\\0', $name)), $separator), self::encoding());\n    }\n\n    /**\n     * Converts an ID into a CamelCase name.\n     * Words in the ID separated by `$separator` (defaults to '-') will be concatenated into a CamelCase name.\n     * For example, 'post-tag' is converted to 'PostTag'.\n     * @param string $id the ID to be converted\n     * @param string $separator the character used to separate the words in the ID\n     * @return string the resulting CamelCase name\n     */\n    public static function id2camel($id, $separator = '-')\n    {\n        if (empty($id)) {\n            return (string) $id;\n        }\n        return str_replace(' ', '', StringHelper::mb_ucwords(str_replace($separator, ' ', $id), self::encoding()));\n    }\n\n    /**\n     * Converts any \"CamelCased\" into an \"underscored_word\".\n     * @param string $words the word(s) to underscore\n     * @return string\n     */\n    public static function underscore($words)\n    {\n        if (empty($words)) {\n            return (string) $words;\n        }\n        return mb_strtolower(preg_replace('/(?<=\\\\pL)(\\\\p{Lu})/u', '_\\\\1', $words), self::encoding());\n    }\n\n    /**\n     * Returns a human-readable string from $word.\n     * @param string $word the string to humanize\n     * @param bool $ucAll whether to set all words to uppercase or not\n     * @return string\n     */\n    public static function humanize($word, $ucAll = false)\n    {\n        if (empty($word)) {\n            return (string) $word;\n        }\n        $word = str_replace('_', ' ', preg_replace('/_id$/', '', $word));\n        $encoding = self::encoding();\n\n        return $ucAll ? StringHelper::mb_ucwords($word, $encoding) : StringHelper::mb_ucfirst($word, $encoding);\n    }\n\n    /**\n     * Same as camelize but first char is in lowercase.\n     *\n     * Converts a word like \"send_email\" to \"sendEmail\". It\n     * will remove non alphanumeric character from the word, so\n     * \"who's online\" will be converted to \"whoSOnline\".\n     * @param string $word to lowerCamelCase\n     * @return string\n     */\n    public static function variablize($word)\n    {\n        if (empty($word)) {\n            return (string) $word;\n        }\n        $word = static::camelize($word);\n\n        return mb_strtolower(mb_substr($word, 0, 1, self::encoding())) . mb_substr($word, 1, null, self::encoding());\n    }\n\n    /**\n     * Converts a class name to its table name (pluralized) naming conventions.\n     *\n     * For example, converts \"Person\" to \"people\".\n     * @param string $className the class name for getting related table_name\n     * @return string\n     */\n    public static function tableize($className)\n    {\n        if (empty($className)) {\n            return (string) $className;\n        }\n        return static::pluralize(static::underscore($className));\n    }\n\n    /**\n     * Returns a string with all spaces converted to given replacement,\n     * non word characters removed and the rest of characters transliterated.\n     *\n     * If intl extension isn't available uses fallback that converts latin characters only\n     * and removes the rest. You may customize characters map via $transliteration property\n     * of the helper.\n     *\n     * @param string $string An arbitrary string to convert\n     * @param string $replacement The replacement to use for spaces\n     * @param bool $lowercase whether to return the string in lowercase or not. Defaults to `true`.\n     * @return string The converted string.\n     */\n    public static function slug($string, $replacement = '-', $lowercase = true)\n    {\n        if (empty($string)) {\n            return (string) $string;\n        }\n        if ((string)$replacement !== '') {\n            $parts = explode($replacement, static::transliterate($string));\n        } else {\n            $parts = [static::transliterate($string)];\n        }\n\n        $replaced = array_map(function ($element) use ($replacement) {\n            $element = preg_replace('/[^a-zA-Z0-9=\\s—–-]+/u', '', $element);\n            return preg_replace('/[=\\s—–-]+/u', $replacement, $element);\n        }, $parts);\n\n        $string = trim(implode($replacement, $replaced), $replacement);\n        if ((string)$replacement !== '') {\n            $string = preg_replace('#' . preg_quote($replacement, '#') . '+#', $replacement, $string);\n        }\n\n        return $lowercase ? strtolower($string) : $string;\n    }\n\n    /**\n     * Returns transliterated version of a string.\n     *\n     * If intl extension isn't available uses fallback that converts latin characters only\n     * and removes the rest. You may customize characters map via $transliteration property\n     * of the helper.\n     *\n     * @param string $string input string\n     * @param string|\\Transliterator|null $transliterator either a [[\\Transliterator]] or a string\n     * from which a [[\\Transliterator]] can be built.\n     * @return string\n     * @since 2.0.7 this method is public.\n     */\n    public static function transliterate($string, $transliterator = null)\n    {\n        if (empty($string)) {\n            return (string) $string;\n        }\n        if (static::hasIntl()) {\n            if ($transliterator === null) {\n                $transliterator = static::$transliterator;\n            }\n\n            return transliterator_transliterate($transliterator, $string);\n        }\n\n        return strtr($string, static::$transliteration);\n    }\n\n    /**\n     * @return bool if intl extension is loaded\n     */\n    protected static function hasIntl()\n    {\n        return extension_loaded('intl');\n    }\n\n    /**\n     * Converts a table name to its class name.\n     *\n     * For example, converts \"people\" to \"Person\".\n     * @param string $tableName\n     * @return string\n     */\n    public static function classify($tableName)\n    {\n        if (empty($tableName)) {\n            return (string) $tableName;\n        }\n        return static::camelize(static::singularize($tableName));\n    }\n\n    /**\n     * Converts number to its ordinal English form. For example, converts 13 to 13th, 2 to 2nd ...\n     * @param int $number the number to get its ordinal value\n     * @return string\n     */\n    public static function ordinalize($number)\n    {\n        if (in_array($number % 100, range(11, 13))) {\n            return $number . 'th';\n        }\n        switch ($number % 10) {\n            case 1:\n                return $number . 'st';\n            case 2:\n                return $number . 'nd';\n            case 3:\n                return $number . 'rd';\n            default:\n                return $number . 'th';\n        }\n    }\n\n    /**\n     * Converts a list of words into a sentence.\n     *\n     * Special treatment is done for the last few words. For example,\n     *\n     * ```\n     * $words = ['Spain', 'France'];\n     * echo Inflector::sentence($words);\n     * // output: Spain and France\n     *\n     * $words = ['Spain', 'France', 'Italy'];\n     * echo Inflector::sentence($words);\n     * // output: Spain, France and Italy\n     *\n     * $words = ['Spain', 'France', 'Italy'];\n     * echo Inflector::sentence($words, ' & ');\n     * // output: Spain, France & Italy\n     * ```\n     *\n     * @param array $words the words to be converted into an string\n     * @param string|null $twoWordsConnector the string connecting words when there are only two\n     * @param string|null $lastWordConnector the string connecting the last two words. If this is null, it will\n     * take the value of `$twoWordsConnector`.\n     * @param string $connector the string connecting words other than those connected by\n     * $lastWordConnector and $twoWordsConnector\n     * @return string the generated sentence\n     * @since 2.0.1\n     */\n    public static function sentence(array $words, $twoWordsConnector = null, $lastWordConnector = null, $connector = ', ')\n    {\n        if ($twoWordsConnector === null) {\n            $twoWordsConnector = Yii::t('yii', ' and ');\n        }\n        if ($lastWordConnector === null) {\n            $lastWordConnector = $twoWordsConnector;\n        }\n        switch (count($words)) {\n            case 0:\n                return '';\n            case 1:\n                return reset($words);\n            case 2:\n                return implode($twoWordsConnector, $words);\n            default:\n                return implode($connector, array_slice($words, 0, -1)) . $lastWordConnector . end($words);\n        }\n    }\n\n    /**\n     * @return string\n     */\n    private static function encoding()\n    {\n        return isset(Yii::$app) ? Yii::$app->charset : 'UTF-8';\n    }\n}\n"
  },
  {
    "path": "framework/helpers/BaseIpHelper.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\helpers;\n\nuse yii\\base\\NotSupportedException;\n\n/**\n * Class BaseIpHelper provides concrete implementation for [[IpHelper]]\n *\n * Do not use BaseIpHelper, use [[IpHelper]] instead.\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n */\nclass BaseIpHelper\n{\n    public const IPV4 = 4;\n    public const IPV6 = 6;\n    /**\n     * The length of IPv6 address in bits\n     */\n    public const IPV6_ADDRESS_LENGTH = 128;\n    /**\n     * The length of IPv4 address in bits\n     */\n    public const IPV4_ADDRESS_LENGTH = 32;\n    /**\n     * Gets the IP version. Does not perform IP address validation.\n     *\n     * @param string $ip the valid IPv4 or IPv6 address.\n     * @return int [[IPV4]] or [[IPV6]]\n     */\n    public static function getIpVersion($ip)\n    {\n        return strpos($ip, ':') === false ? self::IPV4 : self::IPV6;\n    }\n\n    /**\n     * Checks whether IP address or subnet $subnet is contained by $subnet.\n     *\n     * For example, the following code checks whether subnet `192.168.1.0/24` is in subnet `192.168.0.0/22`:\n     *\n     * ```\n     * IpHelper::inRange('192.168.1.0/24', '192.168.0.0/22'); // true\n     * ```\n     *\n     * In case you need to check whether a single IP address `192.168.1.21` is in the subnet `192.168.1.0/24`,\n     * you can use any of theses examples:\n     *\n     * ```\n     * IpHelper::inRange('192.168.1.21', '192.168.1.0/24'); // true\n     * IpHelper::inRange('192.168.1.21/32', '192.168.1.0/24'); // true\n     * ```\n     *\n     * @param string $subnet the valid IPv4 or IPv6 address or CIDR range, e.g.: `10.0.0.0/8` or `2001:af::/64`\n     * @param string $range the valid IPv4 or IPv6 CIDR range, e.g. `10.0.0.0/8` or `2001:af::/64`\n     * @return bool whether $subnet is contained by $range\n     *\n     * @throws NotSupportedException\n     * @see https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing\n     */\n    public static function inRange($subnet, $range)\n    {\n        list($ip, $mask) = array_pad(explode('/', $subnet), 2, null);\n        list($net, $netMask) = array_pad(explode('/', $range), 2, null);\n\n        $ipVersion = static::getIpVersion($ip);\n        $netVersion = static::getIpVersion($net);\n        if ($ipVersion !== $netVersion) {\n            return false;\n        }\n\n        $maxMask = $ipVersion === self::IPV4 ? self::IPV4_ADDRESS_LENGTH : self::IPV6_ADDRESS_LENGTH;\n        $mask = isset($mask) ? $mask : $maxMask;\n        $netMask = isset($netMask) ? $netMask : $maxMask;\n\n        $binIp = static::ip2bin($ip);\n        $binNet = static::ip2bin($net);\n        return substr($binIp, 0, $netMask) === substr($binNet, 0, $netMask) && $mask >= $netMask;\n    }\n\n    /**\n     * Expands an IPv6 address to it's full notation.\n     *\n     * For example `2001:db8::1` will be expanded to `2001:0db8:0000:0000:0000:0000:0000:0001`\n     *\n     * @param string $ip the original valid IPv6 address\n     * @return string the expanded IPv6 address\n     */\n    public static function expandIPv6($ip)\n    {\n        $hex = unpack('H*hex', inet_pton($ip));\n        return substr(preg_replace('/([a-f0-9]{4})/i', '$1:', $hex['hex']), 0, -1);\n    }\n\n    /**\n     * Converts IP address to bits representation.\n     *\n     * @param string $ip the valid IPv4 or IPv6 address\n     * @return string bits as a string\n     * @throws NotSupportedException\n     */\n    public static function ip2bin($ip)\n    {\n        $ipBinary = null;\n        if (static::getIpVersion($ip) === self::IPV4) {\n            $ipBinary = pack('N', ip2long($ip));\n        } elseif (@inet_pton('::1') === false) {\n            throw new NotSupportedException('IPv6 is not supported by inet_pton()!');\n        } else {\n            $ipBinary = inet_pton($ip);\n        }\n\n        $result = '';\n        for ($i = 0, $iMax = strlen($ipBinary); $i < $iMax; $i += 4) {\n            $result .= str_pad(decbin(unpack('N', substr($ipBinary, $i, 4))[1]), 32, '0', STR_PAD_LEFT);\n        }\n        return $result;\n    }\n}\n"
  },
  {
    "path": "framework/helpers/BaseJson.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\helpers;\n\nuse yii\\base\\Arrayable;\nuse yii\\base\\InvalidArgumentException;\nuse yii\\base\\Model;\nuse yii\\web\\JsExpression;\nuse yii\\web\\JsonResponseFormatter;\n\n/**\n * BaseJson provides concrete implementation for [[Json]].\n *\n * Do not use BaseJson. Use [[Json]] instead.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass BaseJson\n{\n    /**\n     * @var bool|null Enables human readable output a.k.a. Pretty Print.\n     * This can useful for debugging during development but is not recommended in a production environment!\n     * In case `prettyPrint` is `null` (default) the `options` passed to `encode` functions will not be changed.\n     * @since 2.0.43\n     */\n    public static $prettyPrint;\n    /**\n     * @var bool Avoids objects with zero-indexed keys to be encoded as array\n     * `Json::encode((object)['test'])` will be encoded as an object not as an array. This matches the behaviour of `json_encode()`.\n     * Defaults to false to avoid any backwards compatibility issues.\n     * Enable for single purpose: `Json::$keepObjectType = true;`\n     * @see JsonResponseFormatter documentation to enable for all JSON responses\n     * @since 2.0.44\n     */\n    public static $keepObjectType = false;\n    /**\n     * @var array List of JSON Error messages assigned to constant names for better handling of PHP <= 5.5.\n     * @since 2.0.7\n     */\n    public static $jsonErrorMessages = [\n        'JSON_ERROR_SYNTAX' => 'Syntax error',\n        'JSON_ERROR_UNSUPPORTED_TYPE' => 'Type is not supported',\n        'JSON_ERROR_DEPTH' => 'The maximum stack depth has been exceeded',\n        'JSON_ERROR_STATE_MISMATCH' => 'Invalid or malformed JSON',\n        'JSON_ERROR_CTRL_CHAR' => 'Control character error, possibly incorrectly encoded',\n        'JSON_ERROR_UTF8' => 'Malformed UTF-8 characters, possibly incorrectly encoded',\n    ];\n\n\n    /**\n     * Encodes the given value into a JSON string.\n     *\n     * The method enhances `json_encode()` by supporting JavaScript expressions.\n     * In particular, the method will not encode a JavaScript expression that is\n     * represented in terms of a [[JsExpression]] object.\n     *\n     * Note that data encoded as JSON must be UTF-8 encoded according to the JSON specification.\n     * You must ensure strings passed to this method have proper encoding before passing them.\n     *\n     * @param mixed $value the data to be encoded.\n     * @param int $options the encoding options. For more details please refer to\n     * <https://www.php.net/manual/en/function.json-encode.php>. Default is `JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE`.\n     * @return string the encoding result.\n     * @throws InvalidArgumentException if there is any encoding error.\n     */\n    public static function encode($value, $options = 320)\n    {\n        $expressions = [];\n        $value = static::processData($value, $expressions, uniqid('', true));\n        set_error_handler(function () {\n            static::handleJsonError(JSON_ERROR_SYNTAX);\n        }, E_WARNING);\n\n        if (static::$prettyPrint === true) {\n            $options |= JSON_PRETTY_PRINT;\n        } elseif (static::$prettyPrint === false) {\n            $options &= ~JSON_PRETTY_PRINT;\n        }\n\n        $json = json_encode($value, $options);\n        restore_error_handler();\n        static::handleJsonError(json_last_error());\n\n        return $expressions === [] ? $json : strtr($json, $expressions);\n    }\n\n    /**\n     * Encodes the given value into a JSON string HTML-escaping entities so it is safe to be embedded in HTML code.\n     *\n     * The method enhances `json_encode()` by supporting JavaScript expressions.\n     * In particular, the method will not encode a JavaScript expression that is\n     * represented in terms of a [[JsExpression]] object.\n     *\n     * Note that data encoded as JSON must be UTF-8 encoded according to the JSON specification.\n     * You must ensure strings passed to this method have proper encoding before passing them.\n     *\n     * @param mixed $value the data to be encoded\n     * @return string the encoding result\n     * @since 2.0.4\n     * @throws InvalidArgumentException if there is any encoding error\n     */\n    public static function htmlEncode($value)\n    {\n        return static::encode($value, JSON_UNESCAPED_UNICODE | JSON_HEX_QUOT | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS);\n    }\n\n    /**\n     * Decodes the given JSON string into a PHP data structure.\n     * @param string $json the JSON string to be decoded\n     * @param bool $asArray whether to return objects in terms of associative arrays.\n     * @return mixed the PHP data\n     * @throws InvalidArgumentException if there is any decoding error\n     */\n    public static function decode($json, $asArray = true)\n    {\n        if (is_array($json)) {\n            throw new InvalidArgumentException('Invalid JSON data.');\n        } elseif ($json === null || $json === '') {\n            return null;\n        }\n        $decode = json_decode((string) $json, $asArray);\n        static::handleJsonError(json_last_error());\n\n        return $decode;\n    }\n\n    /**\n     * Handles [[encode()]] and [[decode()]] errors by throwing exceptions with the respective error message.\n     *\n     * @param int $lastError error code from [json_last_error()](https://www.php.net/manual/en/function.json-last-error.php).\n     * @throws InvalidArgumentException if there is any encoding/decoding error.\n     * @since 2.0.6\n     */\n    protected static function handleJsonError($lastError)\n    {\n        if ($lastError === JSON_ERROR_NONE) {\n            return;\n        }\n\n        if (PHP_VERSION_ID >= 50500) {\n            throw new InvalidArgumentException(json_last_error_msg(), $lastError);\n        }\n\n        foreach (static::$jsonErrorMessages as $const => $message) {\n            if (defined($const) && constant($const) === $lastError) {\n                throw new InvalidArgumentException($message, $lastError);\n            }\n        }\n\n        throw new InvalidArgumentException('Unknown JSON encoding/decoding error.');\n    }\n\n    /**\n     * Pre-processes the data before sending it to `json_encode()`.\n     * @param mixed $data the data to be processed\n     * @param array $expressions collection of JavaScript expressions\n     * @param string $expPrefix a prefix internally used to handle JS expressions\n     * @return mixed the processed data\n     */\n    protected static function processData($data, &$expressions, $expPrefix)\n    {\n        $revertToObject = false;\n\n        if (is_object($data)) {\n            if ($data instanceof JsExpression) {\n                $token = \"!{[$expPrefix=\" . count($expressions) . ']}!';\n                $expressions['\"' . $token . '\"'] = $data->expression;\n\n                return $token;\n            }\n\n            if ($data instanceof \\JsonSerializable) {\n                return static::processData($data->jsonSerialize(), $expressions, $expPrefix);\n            }\n\n            if ($data instanceof \\DateTimeInterface) {\n                return static::processData((array)$data, $expressions, $expPrefix);\n            }\n\n            if ($data instanceof Arrayable) {\n                $data = $data->toArray();\n            } elseif ($data instanceof \\Generator) {\n                $_data = [];\n                foreach ($data as $name => $value) {\n                    $_data[$name] = static::processData($value, $expressions, $expPrefix);\n                }\n                $data = $_data;\n            } elseif ($data instanceof \\SimpleXMLElement) {\n                $data = (array) $data;\n\n                // Avoid empty elements to be returned as array.\n                // Not breaking BC because empty array was always cast to stdClass before.\n                $revertToObject = true;\n            } else {\n                /*\n                 * $data type is changed to array here and its elements will be processed further\n                 * We must cast $data back to object later to keep intended dictionary type in JSON.\n                 * Revert is only done when keepObjectType flag is provided to avoid breaking BC\n                 */\n                $revertToObject = static::$keepObjectType;\n\n                $result = [];\n                foreach ($data as $name => $value) {\n                    $result[$name] = $value;\n                }\n                $data = $result;\n\n                // Avoid empty objects to be returned as array (would break BC without keepObjectType flag)\n                if ($data === []) {\n                    $revertToObject = true;\n                }\n            }\n        }\n\n        if (is_array($data)) {\n            foreach ($data as $key => $value) {\n                if (is_array($value) || is_object($value)) {\n                    $data[$key] = static::processData($value, $expressions, $expPrefix);\n                }\n            }\n        }\n\n        return $revertToObject ? (object) $data : $data;\n    }\n\n    /**\n     * Generates a summary of the validation errors.\n     *\n     * @param Model|Model[] $models the model(s) whose validation errors are to be displayed.\n     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:\n     *\n     * - showAllErrors: boolean, if set to true every error message for each attribute will be shown otherwise\n     *   only the first error message for each attribute will be shown. Defaults to `false`.\n     *\n     * @return string the generated error summary\n     * @since 2.0.14\n     */\n    public static function errorSummary($models, $options = [])\n    {\n        $showAllErrors = ArrayHelper::remove($options, 'showAllErrors', false);\n        $lines = self::collectErrors($models, $showAllErrors);\n\n        return static::encode($lines);\n    }\n\n    /**\n     * Return array of the validation errors.\n     *\n     * @param Model|Model[] $models the model(s) whose validation errors are to be displayed.\n     * @param bool $showAllErrors if set to true every error message for each attribute will be shown otherwise\n     * only the first error message for each attribute will be shown.\n     * @return array of the validation errors\n     * @since 2.0.14\n     */\n    private static function collectErrors($models, $showAllErrors)\n    {\n        $lines = [];\n\n        if (!is_array($models)) {\n            $models = [$models];\n        }\n        foreach ($models as $model) {\n            $lines[] = $model->getErrorSummary($showAllErrors);\n        }\n\n        return array_unique(call_user_func_array('array_merge', $lines));\n    }\n}\n"
  },
  {
    "path": "framework/helpers/BaseMarkdown.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\helpers;\n\nuse Yii;\nuse yii\\base\\InvalidArgumentException;\n\n/**\n * BaseMarkdown provides concrete implementation for [[Markdown]].\n *\n * Do not use BaseMarkdown. Use [[Markdown]] instead.\n *\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0\n */\nclass BaseMarkdown\n{\n    /**\n     * @var array a map of markdown flavor names to corresponding parser class configurations.\n     */\n    public static $flavors = [\n        'original' => [\n            'class' => 'cebe\\markdown\\Markdown',\n            'html5' => true,\n        ],\n        'gfm' => [\n            'class' => 'cebe\\markdown\\GithubMarkdown',\n            'html5' => true,\n        ],\n        'gfm-comment' => [\n            'class' => 'cebe\\markdown\\GithubMarkdown',\n            'html5' => true,\n            'enableNewlines' => true,\n        ],\n        'extra' => [\n            'class' => 'cebe\\markdown\\MarkdownExtra',\n            'html5' => true,\n        ],\n    ];\n    /**\n     * @var string the markdown flavor to use when none is specified explicitly.\n     * Defaults to `original`.\n     * @see flavors\n     */\n    public static $defaultFlavor = 'original';\n\n\n    /**\n     * Converts markdown into HTML.\n     *\n     * @param string $markdown the markdown text to parse\n     * @param string|null $flavor the markdown flavor to use. See [[$flavors]] for available values.\n     * Defaults to [[$defaultFlavor]], if not set.\n     * @return string the parsed HTML output\n     * @throws InvalidArgumentException when an undefined flavor is given.\n     */\n    public static function process($markdown, $flavor = null)\n    {\n        $parser = static::getParser($flavor);\n\n        return $parser->parse($markdown);\n    }\n\n    /**\n     * Converts markdown into HTML but only parses inline elements.\n     *\n     * This can be useful for parsing small comments or description lines.\n     *\n     * @param string $markdown the markdown text to parse\n     * @param string|null $flavor the markdown flavor to use. See [[$flavors]] for available values.\n     * Defaults to [[$defaultFlavor]], if not set.\n     * @return string the parsed HTML output\n     * @throws InvalidArgumentException when an undefined flavor is given.\n     */\n    public static function processParagraph($markdown, $flavor = null)\n    {\n        $parser = static::getParser($flavor);\n\n        return $parser->parseParagraph($markdown);\n    }\n\n    /**\n     * @param string|null $flavor the markdown flavor to use. See [[$flavors]] for available values.\n     * Defaults to [[$defaultFlavor]], if not set.\n     * @return \\cebe\\markdown\\Parser\n     * @throws InvalidArgumentException when an undefined flavor is given.\n     */\n    protected static function getParser($flavor)\n    {\n        if ($flavor === null) {\n            $flavor = static::$defaultFlavor;\n        }\n        if (!isset(static::$flavors[$flavor])) {\n            throw new InvalidArgumentException(\"Markdown flavor '$flavor' is not defined.'\");\n        } elseif (!is_object($config = static::$flavors[$flavor])) {\n            static::$flavors[$flavor] = Yii::createObject($config);\n        }\n\n        return static::$flavors[$flavor];\n    }\n}\n"
  },
  {
    "path": "framework/helpers/BaseStringHelper.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\helpers;\n\nuse Yii;\n\n/**\n * BaseStringHelper provides concrete implementation for [[StringHelper]].\n *\n * Do not use BaseStringHelper. Use [[StringHelper]] instead.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Alex Makarov <sam@rmcreative.ru>\n * @since 2.0\n */\nclass BaseStringHelper\n{\n    /**\n     * Returns the number of bytes in the given string.\n     * This method ensures the string is treated as a byte array by using `mb_strlen()`.\n     *\n     * @param string $string the string being measured for length\n     * @return int the number of bytes in the given string.\n     */\n    public static function byteLength($string)\n    {\n        return mb_strlen((string)$string, '8bit');\n    }\n\n    /**\n     * Returns the portion of string specified by the start and length parameters.\n     * This method ensures the string is treated as a byte array by using `mb_substr()`.\n     *\n     * @param string $string the input string. Must be one character or longer.\n     * @param int $start the starting position\n     * @param int|null $length the desired portion length. If not specified or `null`, there will be\n     * no limit on length i.e. the output will be until the end of the string.\n     * @return string the extracted part of string, or FALSE on failure or an empty string.\n     * @see https://www.php.net/manual/en/function.substr.php\n     */\n    public static function byteSubstr($string, $start, $length = null)\n    {\n        if ($length === null) {\n            $length = static::byteLength($string);\n        }\n\n        return mb_substr((string)$string, $start, $length, '8bit');\n    }\n\n    /**\n     * Converts php.ini style size to bytes.\n     *\n     * @param string $string php.ini style size. Examples: `512M`, `1024K`, `1G`, `256`.\n     * @return int the number of bytes equivalent to the specified string.\n     * @since 2.0.54\n     */\n    public static function convertIniSizeToBytes($string)\n    {\n        switch (substr($string, -1)) {\n            case 'M':\n            case 'm':\n                return (int) $string * 1048576;\n            case 'K':\n            case 'k':\n                return (int) $string * 1024;\n            case 'G':\n            case 'g':\n                return (int) $string * 1073741824;\n            default:\n                return (int) $string;\n        }\n    }\n\n    /**\n     * Returns the trailing name component of a path.\n     * This method is similar to the php function `basename()` except that it will\n     * treat both \\ and / as directory separators, independent of the operating system.\n     * This method was mainly created to work on php namespaces. When working with real\n     * file paths, php's `basename()` should work fine for you.\n     * Note: this method is not aware of the actual filesystem, or path components such as \"..\".\n     *\n     * @param string $path A path string.\n     * @param string $suffix If the name component ends in suffix this will also be cut off.\n     * @return string the trailing name component of the given path.\n     * @see https://www.php.net/manual/en/function.basename.php\n     */\n    public static function basename($path, $suffix = '')\n    {\n        $path = (string)$path;\n\n        $len = mb_strlen($suffix);\n        if ($len > 0 && mb_substr($path, -$len) === $suffix) {\n            $path = mb_substr($path, 0, -$len);\n        }\n\n        $path = rtrim(str_replace('\\\\', '/', $path), '/');\n        $pos = mb_strrpos($path, '/');\n        if ($pos !== false) {\n            return mb_substr($path, $pos + 1);\n        }\n\n        return $path;\n    }\n\n    /**\n     * Returns parent directory's path.\n     * This method is similar to `dirname()` except that it will treat\n     * both \\ and / as directory separators, independent of the operating system.\n     *\n     * @param string $path A path string.\n     * @return string the parent directory's path.\n     * @see https://www.php.net/manual/en/function.basename.php\n     */\n    public static function dirname($path)\n    {\n        $normalizedPath = rtrim(\n            str_replace('\\\\', '/', (string)$path),\n            '/'\n        );\n        $separatorPosition = mb_strrpos($normalizedPath, '/');\n\n        if ($separatorPosition !== false) {\n            return mb_substr($path, 0, $separatorPosition);\n        }\n\n        return '';\n    }\n\n    /**\n     * Truncates a string to the number of characters specified.\n     *\n     * In order to truncate for an exact length, the $suffix char length must be counted towards the $length. For example\n     * to have a string which is exactly 255 long with $suffix `...` of 3 chars, then `StringHelper::truncate($string, 252, '...')`\n     * must be used to ensure you have 255 long string afterwards.\n     *\n     * @param string $string The string to truncate.\n     * @param int $length How many characters from original string to include into truncated string.\n     * @param string $suffix String to append to the end of truncated string.\n     * @param string|null $encoding The charset to use, defaults to charset currently used by application.\n     * @param bool $asHtml Whether to treat the string being truncated as HTML and preserve proper HTML tags.\n     * This parameter is available since version 2.0.1.\n     * @return string the truncated string.\n     */\n    public static function truncate($string, $length, $suffix = '...', $encoding = null, $asHtml = false)\n    {\n        $string = (string)$string;\n\n        if ($encoding === null) {\n            $encoding = Yii::$app ? Yii::$app->charset : 'UTF-8';\n        }\n        if ($asHtml) {\n            return static::truncateHtml($string, $length, $suffix, $encoding);\n        }\n\n        if (mb_strlen($string, $encoding) > $length) {\n            return rtrim(mb_substr($string, 0, $length, $encoding)) . $suffix;\n        }\n\n        return $string;\n    }\n\n    /**\n     * Truncates a string to the number of words specified.\n     *\n     * @param string $string The string to truncate.\n     * @param int $count How many words from original string to include into truncated string.\n     * @param string $suffix String to append to the end of truncated string.\n     * @param bool $asHtml Whether to treat the string being truncated as HTML and preserve proper HTML tags.\n     * This parameter is available since version 2.0.1.\n     * @return string the truncated string.\n     */\n    public static function truncateWords($string, $count, $suffix = '...', $asHtml = false)\n    {\n        if ($asHtml) {\n            return static::truncateHtml($string, $count, $suffix);\n        }\n\n        $words = preg_split('/(\\s+)/u', trim($string), 0, PREG_SPLIT_DELIM_CAPTURE);\n        if (count($words) / 2 > $count) {\n            return implode('', array_slice($words, 0, ($count * 2) - 1)) . $suffix;\n        }\n\n        return $string;\n    }\n\n    /**\n     * Truncate a string while preserving the HTML.\n     *\n     * @param string $string The string to truncate\n     * @param int $count The counter\n     * @param string $suffix String to append to the end of the truncated string.\n     * @param string|bool $encoding Encoding flag or charset.\n     * @return string\n     * @since 2.0.1\n     */\n    protected static function truncateHtml($string, $count, $suffix, $encoding = false)\n    {\n        $config = \\HTMLPurifier_Config::create(null);\n        if (Yii::$app !== null) {\n            $config->set('Cache.SerializerPath', Yii::$app->getRuntimePath());\n        }\n        $lexer = \\HTMLPurifier_Lexer::create($config);\n        $tokens = $lexer->tokenizeHTML($string, $config, new \\HTMLPurifier_Context());\n        $openTokens = [];\n        $totalCount = 0;\n        $depth = 0;\n        $truncated = [];\n        foreach ($tokens as $token) {\n            if ($token instanceof \\HTMLPurifier_Token_Start) { //Tag begins\n                $openTokens[$depth] = $token->name;\n                $truncated[] = $token;\n                ++$depth;\n            } elseif ($token instanceof \\HTMLPurifier_Token_Text && $totalCount <= $count) { //Text\n                if (false === $encoding) {\n                    preg_match('/^(\\s*)/um', $token->data, $prefixSpace) ?: $prefixSpace = ['', ''];\n                    $token->data = $prefixSpace[1] . self::truncateWords(ltrim($token->data), $count - $totalCount, '');\n                    $currentCount = self::countWords($token->data);\n                } else {\n                    $token->data = self::truncate($token->data, $count - $totalCount, '', $encoding);\n                    $currentCount = mb_strlen($token->data, $encoding);\n                }\n                $totalCount += $currentCount;\n                $truncated[] = $token;\n            } elseif ($token instanceof \\HTMLPurifier_Token_End) { //Tag ends\n                if ($token->name === $openTokens[$depth - 1]) {\n                    --$depth;\n                    unset($openTokens[$depth]);\n                    $truncated[] = $token;\n                }\n            } elseif ($token instanceof \\HTMLPurifier_Token_Empty) { //Self contained tags, i.e. <img/> etc.\n                $truncated[] = $token;\n            }\n            if ($totalCount >= $count) {\n                if (0 < count($openTokens)) {\n                    krsort($openTokens);\n                    foreach ($openTokens as $name) {\n                        $truncated[] = new \\HTMLPurifier_Token_End($name);\n                    }\n                }\n                break;\n            }\n        }\n        $context = new \\HTMLPurifier_Context();\n        $generator = new \\HTMLPurifier_Generator($config, $context);\n        return $generator->generateFromTokens($truncated) . ($totalCount >= $count ? $suffix : '');\n    }\n\n    /**\n     * Check if given string starts with specified substring. Binary and multibyte safe.\n     *\n     * @param string $string Input string\n     * @param string $with Part to search inside the $string\n     * @param bool $caseSensitive Case sensitive search. Default is true. When case sensitive is enabled, `$with` must\n     * exactly match the starting of the string in order to get a true value.\n     * @return bool Returns true if first input starts with second input, false otherwise\n     */\n    public static function startsWith($string, $with, $caseSensitive = true)\n    {\n        $string = (string)$string;\n        $with = (string)$with;\n\n        if (!$bytes = static::byteLength($with)) {\n            return true;\n        }\n        if ($caseSensitive) {\n            return strncmp($string, $with, $bytes) === 0;\n        }\n\n        $encoding = Yii::$app ? Yii::$app->charset : 'UTF-8';\n        $string = static::byteSubstr($string, 0, $bytes);\n\n        return mb_strtolower($string, $encoding) === mb_strtolower($with, $encoding);\n    }\n\n    /**\n     * Check if given string ends with specified substring. Binary and multibyte safe.\n     *\n     * @param string $string Input string to check\n     * @param string $with Part to search inside of the `$string`.\n     * @param bool $caseSensitive Case sensitive search. Default is true. When case sensitive is enabled, `$with` must\n     * exactly match the ending of the string in order to get a true value.\n     * @return bool Returns true if first input ends with second input, false otherwise\n     */\n    public static function endsWith($string, $with, $caseSensitive = true)\n    {\n        $string = (string)$string;\n        $with = (string)$with;\n\n        if (!$bytes = static::byteLength($with)) {\n            return true;\n        }\n        if ($caseSensitive) {\n            // Warning check, see https://php.net/substr-compare#refsect1-function.substr-compare-returnvalues\n            if (static::byteLength($string) < $bytes) {\n                return false;\n            }\n\n            return substr_compare($string, $with, -$bytes, $bytes) === 0;\n        }\n\n        $encoding = Yii::$app ? Yii::$app->charset : 'UTF-8';\n        $string = static::byteSubstr($string, -$bytes);\n\n        return mb_strtolower($string, $encoding) === mb_strtolower($with, $encoding);\n    }\n\n    /**\n     * Explodes string into array, optionally trims values and skips empty ones.\n     *\n     * @param string $string String to be exploded.\n     * @param string $delimiter Delimiter. Default is ','.\n     * @param mixed $trim Whether to trim each element. Can be:\n     *   - boolean - to trim normally;\n     *   - string - custom characters to trim. Will be passed as a second argument to `trim()` function.\n     *   - callable - will be called for each value instead of trim. Takes the only argument - value.\n     * @param bool $skipEmpty Whether to skip empty strings between delimiters. Default is false.\n     * @return array\n     * @since 2.0.4\n     */\n    public static function explode($string, $delimiter = ',', $trim = true, $skipEmpty = false)\n    {\n        $result = explode($delimiter, $string);\n        if ($trim !== false) {\n            if ($trim === true) {\n                $trim = 'trim';\n            } elseif (!is_callable($trim)) {\n                $trim = function ($v) use ($trim) {\n                    return trim($v, $trim);\n                };\n            }\n            $result = array_map($trim, $result);\n        }\n        if ($skipEmpty) {\n            // Wrapped with array_values to make array keys sequential after empty values removing\n            $result = array_values(\n                array_filter(\n                    $result,\n                    function ($value) {\n                        return $value !== '';\n                    }\n                )\n            );\n        }\n\n        return $result;\n    }\n\n    /**\n     * Counts words in a string.\n     *\n     * @param string $string the text to calculate\n     * @return int\n     * @since 2.0.8\n     */\n    public static function countWords($string)\n    {\n        return count(preg_split('/\\s+/u', $string, 0, PREG_SPLIT_NO_EMPTY));\n    }\n\n    /**\n     * Returns string representation of number value with replaced commas to dots, if decimal point\n     * of current locale is comma.\n     *\n     * @param int|float|string $value the value to normalize.\n     * @return string\n     * @since 2.0.11\n     */\n    public static function normalizeNumber($value)\n    {\n        $value = (string)$value;\n\n        $localeInfo = localeconv();\n        $decimalSeparator = isset($localeInfo['decimal_point']) ? $localeInfo['decimal_point'] : null;\n\n        if ($decimalSeparator !== null && $decimalSeparator !== '.') {\n            $value = str_replace($decimalSeparator, '.', $value);\n        }\n\n        return $value;\n    }\n\n    /**\n     * Encodes string into \"Base 64 Encoding with URL and Filename Safe Alphabet\" (RFC 4648).\n     *\n     * > Note: Base 64 padding `=` may be at the end of the returned string.\n     * > `=` is not transparent to URL encoding.\n     *\n     * @param string $input the string to encode.\n     * @return string encoded string.\n     * @see https://tools.ietf.org/html/rfc4648#page-7\n     * @since 2.0.12\n     */\n    public static function base64UrlEncode($input)\n    {\n        return strtr(base64_encode($input), '+/', '-_');\n    }\n\n    /**\n     * Decodes \"Base 64 Encoding with URL and Filename Safe Alphabet\" (RFC 4648).\n     *\n     * @param string $input encoded string.\n     * @return string decoded string.\n     * @see https://tools.ietf.org/html/rfc4648#page-7\n     * @since 2.0.12\n     */\n    public static function base64UrlDecode($input)\n    {\n        return base64_decode(strtr($input, '-_', '+/'));\n    }\n\n    /**\n     * Safely casts a float to string independent of the current locale.\n     * The decimal separator will always be `.`.\n     *\n     * @param float|int $number a floating point number or integer.\n     * @return string the string representation of the number.\n     * @since 2.0.13\n     */\n    public static function floatToString($number)\n    {\n        // . and , are the only decimal separators known in ICU data,\n        // so its safe to call str_replace here\n        return str_replace(',', '.', (string)$number);\n    }\n\n    /**\n     * Checks if the passed string would match the given shell wildcard pattern.\n     * This function emulates [[fnmatch()]], which may be unavailable at certain environment, using PCRE.\n     *\n     * @param string $pattern the shell wildcard pattern.\n     * @param string $string the tested string.\n     * @param array $options options for matching. Valid options are:\n     *\n     * - caseSensitive: bool, whether pattern should be case sensitive. Defaults to `true`.\n     * - escape: bool, whether backslash escaping is enabled. Defaults to `true`.\n     * - filePath: bool, whether slashes in string only matches slashes in the given pattern. Defaults to `false`.\n     *\n     * @return bool whether the string matches pattern or not.\n     * @since 2.0.14\n     */\n    public static function matchWildcard($pattern, $string, $options = [])\n    {\n        if ($pattern === '*' && empty($options['filePath'])) {\n            return true;\n        }\n\n        $replacements = [\n            '\\\\\\\\\\\\\\\\' => '\\\\\\\\',\n            '\\\\\\\\\\\\*'  => '[*]',\n            '\\\\\\\\\\\\?'  => '[?]',\n            '\\*'       => '.*',\n            '\\?'       => '.',\n            '\\[\\!'     => '[^',\n            '\\['       => '[',\n            '\\]'       => ']',\n            '\\-'       => '-',\n        ];\n\n        if (isset($options['escape']) && !$options['escape']) {\n            unset($replacements['\\\\\\\\\\\\\\\\']);\n            unset($replacements['\\\\\\\\\\\\*']);\n            unset($replacements['\\\\\\\\\\\\?']);\n        }\n\n        if (!empty($options['filePath'])) {\n            $replacements['\\*'] = '[^/\\\\\\\\]*';\n            $replacements['\\?'] = '[^/\\\\\\\\]';\n        }\n\n        $pattern = strtr(preg_quote($pattern, '#'), $replacements);\n        $pattern = '#^' . $pattern . '$#us';\n\n        if (isset($options['caseSensitive']) && !$options['caseSensitive']) {\n            $pattern .= 'i';\n        }\n\n        return preg_match($pattern, (string)$string) === 1;\n    }\n\n    /**\n     * This method provides a unicode-safe implementation of built-in PHP function `ucfirst()`.\n     *\n     * @param string $string the string to be proceeded\n     * @param string $encoding Optional, defaults to \"UTF-8\"\n     * @return string\n     * @see https://www.php.net/manual/en/function.ucfirst.php\n     * @since 2.0.16\n     * @phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps\n     */\n    public static function mb_ucfirst($string, $encoding = 'UTF-8')\n    {\n        $firstChar = mb_substr((string)$string, 0, 1, $encoding);\n        $rest = mb_substr((string)$string, 1, null, $encoding);\n\n        return mb_strtoupper($firstChar, $encoding) . $rest;\n    }\n\n    /**\n     * This method provides a unicode-safe implementation of built-in PHP function `ucwords()`.\n     *\n     * @param string $string the string to be proceeded\n     * @param string $encoding Optional, defaults to \"UTF-8\"\n     * @return string\n     * @see https://www.php.net/manual/en/function.ucwords\n     * @since 2.0.16\n     * @phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps\n     */\n    public static function mb_ucwords($string, $encoding = 'UTF-8')\n    {\n        $string = (string)$string;\n        if (empty($string)) {\n            return $string;\n        }\n\n        $parts = preg_split('/(\\s+\\W+\\s+|^\\W+\\s+|\\s+)/u', $string, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);\n        $ucfirstEven = trim(mb_substr($parts[0], -1, 1, $encoding)) === '';\n        foreach ($parts as $key => $value) {\n            $isEven = (bool)($key % 2);\n            if ($ucfirstEven === $isEven) {\n                $parts[$key] = static::mb_ucfirst($value, $encoding);\n            }\n        }\n\n        return implode('', $parts);\n    }\n\n    /**\n     * Masks a portion of a string with a repeated character.\n     * This method is multibyte-safe.\n     *\n     * @param string $string The input string.\n     * @param int $start The starting position from where to begin masking.\n     * This can be a positive or negative integer.\n     * Positive values count from the beginning,\n     * negative values count from the end of the string.\n     * @param int $length The length of the section to be masked.\n     * The masking will start from the $start position\n     * and continue for $length characters.\n     * @param string $mask The character to use for masking. The default is '*'.\n     * @return string The masked string.\n     */\n    public static function mask($string, $start, $length, $mask = '*')\n    {\n        $strLength = mb_strlen($string, 'UTF-8');\n\n        // Return original string if start position is out of bounds\n        if ($start >= $strLength || $start < -$strLength) {\n            return $string;\n        }\n\n        $masked = mb_substr($string, 0, $start, 'UTF-8');\n        $masked .= str_repeat($mask, abs($length));\n        $masked .= mb_substr($string, $start + abs($length), null, 'UTF-8');\n\n        return $masked;\n    }\n\n    /**\n     * Returns the portion of the string that lies between the first occurrence of the start string\n     * and the last occurrence of the end string after that.\n     *\n     * @param string $string The input string.\n     * @param string $start The string marking the start of the portion to extract.\n     * @param string $end The string marking the end of the portion to extract.\n     * @return string|null The portion of the string between the first occurrence of\n     * start and the last occurrence of end, or null if either start or end cannot be found.\n     */\n    public static function findBetween($string, $start, $end)\n    {\n        $startPos = mb_strpos($string, $start);\n\n        if ($startPos === false) {\n            return null;\n        }\n\n        $startPos += mb_strlen($start);\n        $endPos = mb_strrpos($string, $end, $startPos);\n\n        if ($endPos === false) {\n            return null;\n        }\n\n        return mb_substr($string, $startPos, $endPos - $startPos);\n    }\n}\n"
  },
  {
    "path": "framework/helpers/BaseUrl.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\helpers;\n\nuse Yii;\nuse yii\\base\\InvalidArgumentException;\n\n/**\n * BaseUrl provides concrete implementation for [[Url]].\n *\n * Do not use BaseUrl. Use [[Url]] instead.\n *\n * @author Alexander Makarov <sam@rmcreative.ru>\n * @since 2.0\n */\nclass BaseUrl\n{\n    /**\n     * @var \\yii\\web\\UrlManager URL manager to use for creating URLs\n     * @since 2.0.8\n     */\n    public static $urlManager;\n\n\n    /**\n     * Creates a URL for the given route.\n     *\n     * This method will use [[\\yii\\web\\UrlManager]] to create a URL.\n     *\n     * You may specify the route as a string, e.g., `site/index`. You may also use an array\n     * if you want to specify additional query parameters for the URL being created. The\n     * array format must be:\n     *\n     * ```\n     * // generates: /index.php?r=site/index&param1=value1&param2=value2\n     * ['site/index', 'param1' => 'value1', 'param2' => 'value2']\n     * ```\n     *\n     * If you want to create a URL with an anchor, you can use the array format with a `#` parameter.\n     * For example,\n     *\n     * ```\n     * // generates: /index.php?r=site/index&param1=value1#name\n     * ['site/index', 'param1' => 'value1', '#' => 'name']\n     * ```\n     *\n     * A route may be either absolute or relative. An absolute route has a leading slash (e.g. `/site/index`),\n     * while a relative route has none (e.g. `site/index` or `index`). A relative route will be converted\n     * into an absolute one by the following rules:\n     *\n     * - If the route is an empty string, the current [[\\yii\\web\\Controller::route|route]] will be used;\n     * - If the route contains no slashes at all (e.g. `index`), it is considered to be an action ID\n     *   of the current controller and will be prepended with [[\\yii\\web\\Controller::uniqueId]];\n     * - If the route has no leading slash (e.g. `site/index`), it is considered to be a route relative\n     *   to the current module and will be prepended with the module's [[\\yii\\base\\Module::uniqueId|uniqueId]].\n     *\n     * Starting from version 2.0.2, a route can also be specified as an alias. In this case, the alias\n     * will be converted into the actual route first before conducting the above transformation steps.\n     *\n     * Below are some examples of using this method:\n     *\n     * ```\n     * // /index.php?r=site%2Findex\n     * echo Url::toRoute('site/index');\n     *\n     * // /index.php?r=site%2Findex&src=ref1#name\n     * echo Url::toRoute(['site/index', 'src' => 'ref1', '#' => 'name']);\n     *\n     * // https://www.example.com/index.php?r=site%2Findex\n     * echo Url::toRoute('site/index', true);\n     *\n     * // https://www.example.com/index.php?r=site%2Findex\n     * echo Url::toRoute('site/index', 'https');\n     *\n     * // /index.php?r=post%2Findex     assume the alias \"@posts\" is defined as \"post/index\"\n     * echo Url::toRoute('@posts');\n     * ```\n     *\n     * @param string|array $route use a string to represent a route (e.g. `index`, `site/index`),\n     * or an array to represent a route with query parameters (e.g. `['site/index', 'param1' => 'value1']`).\n     * @param bool|string $scheme the URI scheme to use in the generated URL:\n     *\n     * - `false` (default): generating a relative URL.\n     * - `true`: returning an absolute base URL whose scheme is the same as that in [[\\yii\\web\\UrlManager::$hostInfo]].\n     * - string: generating an absolute URL with the specified scheme (either `http`, `https` or empty string\n     *   for protocol-relative URL).\n     *\n     * @return string the generated URL\n     * @throws InvalidArgumentException a relative route is given while there is no active controller\n     */\n    public static function toRoute($route, $scheme = false)\n    {\n        $route = (array) $route;\n        $route[0] = static::normalizeRoute($route[0]);\n\n        if ($scheme !== false) {\n            return static::getUrlManager()->createAbsoluteUrl($route, is_string($scheme) ? $scheme : null);\n        }\n\n        return static::getUrlManager()->createUrl($route);\n    }\n\n    /**\n     * Normalizes route and makes it suitable for UrlManager. Absolute routes are staying as is\n     * while relative routes are converted to absolute ones.\n     *\n     * A relative route is a route without a leading slash, such as \"view\", \"post/view\".\n     *\n     * - If the route is an empty string, the current [[\\yii\\web\\Controller::route|route]] will be used;\n     * - If the route contains no slashes at all, it is considered to be an action ID\n     *   of the current controller and will be prepended with [[\\yii\\web\\Controller::uniqueId]];\n     * - If the route has no leading slash, it is considered to be a route relative\n     *   to the current module and will be prepended with the module's uniqueId.\n     *\n     * Starting from version 2.0.2, a route can also be specified as an alias. In this case, the alias\n     * will be converted into the actual route first before conducting the above transformation steps.\n     *\n     * @param string $route the route. This can be either an absolute route or a relative route.\n     * @return string normalized route suitable for UrlManager\n     * @throws InvalidArgumentException a relative route is given while there is no active controller\n     */\n    protected static function normalizeRoute($route)\n    {\n        $route = Yii::getAlias((string) $route);\n        if (strncmp($route, '/', 1) === 0) {\n            // absolute route\n            return ltrim($route, '/');\n        }\n\n        // relative route\n        if (Yii::$app->controller === null) {\n            throw new InvalidArgumentException(\"Unable to resolve the relative route: $route. No active controller is available.\");\n        }\n\n        if (strpos($route, '/') === false) {\n            // empty or an action ID\n            return $route === '' ? Yii::$app->controller->getRoute() : Yii::$app->controller->getUniqueId() . '/' . $route;\n        }\n\n        // relative to module\n        return ltrim(Yii::$app->controller->module->getUniqueId() . '/' . $route, '/');\n    }\n\n    /**\n     * Creates a URL based on the given parameters.\n     *\n     * This method is very similar to [[toRoute()]]. The only difference is that this method\n     * requires a route to be specified as an array only. If a string is given, it will be treated as a URL.\n     * In particular, if `$url` is\n     *\n     * - an array: [[toRoute()]] will be called to generate the URL. For example:\n     *   `['site/index']`, `['post/index', 'page' => 2]`. Please refer to [[toRoute()]] for more details\n     *   on how to specify a route.\n     * - a string with a leading `@`: it is treated as an alias, and the corresponding aliased string\n     *   will be returned.\n     * - an empty string: the currently requested URL will be returned;\n     * - a normal string: it will be returned as is.\n     *\n     * When `$scheme` is specified (either a string or `true`), an absolute URL with host info (obtained from\n     * [[\\yii\\web\\UrlManager::$hostInfo]]) will be returned. If `$url` is already an absolute URL, its scheme\n     * will be replaced with the specified one.\n     *\n     * Below are some examples of using this method:\n     *\n     * ```\n     * // /index.php?r=site%2Findex\n     * echo Url::to(['site/index']);\n     *\n     * // /index.php?r=site%2Findex&src=ref1#name\n     * echo Url::to(['site/index', 'src' => 'ref1', '#' => 'name']);\n     *\n     * // /index.php?r=post%2Findex     assume the alias \"@posts\" is defined as \"/post/index\"\n     * echo Url::to(['@posts']);\n     *\n     * // the currently requested URL\n     * echo Url::to();\n     *\n     * // /images/logo.gif\n     * echo Url::to('@web/images/logo.gif');\n     *\n     * // images/logo.gif\n     * echo Url::to('images/logo.gif');\n     *\n     * // https://www.example.com/images/logo.gif\n     * echo Url::to('@web/images/logo.gif', true);\n     *\n     * // https://www.example.com/images/logo.gif\n     * echo Url::to('@web/images/logo.gif', 'https');\n     *\n     * // //www.example.com/images/logo.gif\n     * echo Url::to('@web/images/logo.gif', '');\n     * ```\n     *\n     *\n     * @param array|string $url the parameter to be used to generate a valid URL\n     * @param bool|string $scheme the URI scheme to use in the generated URL:\n     *\n     * - `false` (default): generating a relative URL.\n     * - `true`: returning an absolute base URL whose scheme is the same as that in [[\\yii\\web\\UrlManager::$hostInfo]].\n     * - string: generating an absolute URL with the specified scheme (either `http`, `https` or empty string\n     *   for protocol-relative URL).\n     *\n     * @return string the generated URL\n     * @throws InvalidArgumentException a relative route is given while there is no active controller\n     */\n    public static function to($url = '', $scheme = false)\n    {\n        if (is_array($url)) {\n            return static::toRoute($url, $scheme);\n        }\n\n        $url = Yii::getAlias($url);\n        if ($url === '') {\n            $url = Yii::$app->getRequest()->getUrl();\n        }\n\n        if ($scheme === false) {\n            return $url;\n        }\n\n        if (static::isRelative($url)) {\n            // turn relative URL into absolute\n            $url = static::getUrlManager()->getHostInfo() . '/' . ltrim($url, '/');\n        }\n\n        return static::ensureScheme($url, $scheme);\n    }\n\n    /**\n     * Normalize the URL by ensuring it uses specified scheme.\n     *\n     * If the URL is relative or the scheme is not a string, normalization is skipped.\n     *\n     * @param string $url the URL to process\n     * @param string $scheme the URI scheme used in the URL (e.g. `http` or `https`). Use an empty string to\n     * create protocol-relative URL (e.g. `//example.com/path`)\n     * @return string the processed URL\n     * @since 2.0.11\n     */\n    public static function ensureScheme($url, $scheme)\n    {\n        if (static::isRelative($url) || !is_string($scheme)) {\n            return $url;\n        }\n\n        if (strncmp($url, '//', 2) === 0) {\n            // e.g. //example.com/path/to/resource\n            return $scheme === '' ? $url : \"$scheme:$url\";\n        }\n\n        if (($pos = strpos($url, '://')) !== false) {\n            if ($scheme === '') {\n                $url = substr($url, $pos + 1);\n            } else {\n                $url = $scheme . substr($url, $pos);\n            }\n        }\n\n        return $url;\n    }\n\n    /**\n     * Returns the base URL of the current request.\n     * @param bool|string $scheme the URI scheme to use in the returned base URL:\n     *\n     * - `false` (default): returning the base URL without host info.\n     * - `true`: returning an absolute base URL whose scheme is the same as that in [[\\yii\\web\\UrlManager::$hostInfo]].\n     * - string: returning an absolute base URL with the specified scheme (either `http`, `https` or empty string\n     *   for protocol-relative URL).\n     * @return string\n     */\n    public static function base($scheme = false)\n    {\n        $url = static::getUrlManager()->getBaseUrl();\n        if ($scheme !== false) {\n            $url = static::getUrlManager()->getHostInfo() . $url;\n            $url = static::ensureScheme($url, $scheme);\n        }\n\n        return $url;\n    }\n\n    /**\n     * Remembers the specified URL so that it can be later fetched back by [[previous()]].\n     *\n     * @param string|array $url the URL to remember. Please refer to [[to()]] for acceptable formats.\n     * If this parameter is not specified, the currently requested URL will be used.\n     * @param string|null $name the name associated with the URL to be remembered. This can be used\n     * later by [[previous()]]. If not set, [[\\yii\\web\\User::setReturnUrl()]] will be used with passed URL.\n     * @see previous()\n     * @see \\yii\\web\\User::setReturnUrl()\n     */\n    public static function remember($url = '', $name = null)\n    {\n        $url = static::to($url);\n\n        if ($name === null) {\n            Yii::$app->getUser()->setReturnUrl($url);\n        } else {\n            Yii::$app->getSession()->set($name, $url);\n        }\n    }\n\n    /**\n     * Returns the URL previously [[remember()|remembered]].\n     *\n     * @param string|null $name the named associated with the URL that was remembered previously.\n     * If not set, [[\\yii\\web\\User::getReturnUrl()]] will be used to obtain remembered URL.\n     * @return string|null the URL previously remembered. Null is returned if no URL was remembered with the given name\n     * and `$name` is not specified.\n     * @see remember()\n     * @see \\yii\\web\\User::getReturnUrl()\n     */\n    public static function previous($name = null)\n    {\n        if ($name === null) {\n            return Yii::$app->getUser()->getReturnUrl();\n        }\n\n        return Yii::$app->getSession()->get($name);\n    }\n\n    /**\n     * Returns the canonical URL of the currently requested page.\n     *\n     * The canonical URL is constructed using the current controller's [[\\yii\\web\\Controller::route]] and\n     * [[\\yii\\web\\Controller::actionParams]]. You may use the following code in the layout view to add a link tag\n     * about canonical URL:\n     *\n     * ```\n     * $this->registerLinkTag(['rel' => 'canonical', 'href' => Url::canonical()]);\n     * ```\n     *\n     * @return string the canonical URL of the currently requested page\n     */\n    public static function canonical()\n    {\n        $params = Yii::$app->controller->actionParams;\n        $params[0] = Yii::$app->controller->getRoute();\n\n        return static::getUrlManager()->createAbsoluteUrl($params);\n    }\n\n    /**\n     * Returns the home URL.\n     *\n     * @param bool|string $scheme the URI scheme to use for the returned URL:\n     *\n     * - `false` (default): returning a relative URL.\n     * - `true`: returning an absolute base URL whose scheme is the same as that in [[\\yii\\web\\UrlManager::$hostInfo]].\n     * - string: returning an absolute URL with the specified scheme (either `http`, `https` or empty string\n     *   for protocol-relative URL).\n     *\n     * @return string home URL\n     */\n    public static function home($scheme = false)\n    {\n        $url = Yii::$app->getHomeUrl();\n\n        if ($scheme !== false) {\n            $url = static::getUrlManager()->getHostInfo() . $url;\n            $url = static::ensureScheme($url, $scheme);\n        }\n\n        return $url;\n    }\n\n    /**\n     * Returns a value indicating whether a URL is relative.\n     * A relative URL does not have host info part.\n     * @param string $url the URL to be checked\n     * @return bool whether the URL is relative\n     */\n    public static function isRelative($url)\n    {\n        return preg_match('~^[[:alpha:]][[:alnum:]+-.]*://|^//~', $url) === 0;\n    }\n\n    /**\n     * Creates a URL by using the current route and the GET parameters.\n     *\n     * You may modify or remove some of the GET parameters, or add additional query parameters through\n     * the `$params` parameter. In particular, if you specify a parameter to be null, then this parameter\n     * will be removed from the existing GET parameters; all other parameters specified in `$params` will\n     * be merged with the existing GET parameters. For example,\n     *\n     * ```\n     * // assume $_GET = ['id' => 123, 'src' => 'google'], current route is \"post/view\"\n     *\n     * // /index.php?r=post%2Fview&id=123&src=google\n     * echo Url::current();\n     *\n     * // /index.php?r=post%2Fview&id=123\n     * echo Url::current(['src' => null]);\n     *\n     * // /index.php?r=post%2Fview&id=100&src=google\n     * echo Url::current(['id' => 100]);\n     * ```\n     *\n     * Note that if you're replacing array parameters with `[]` at the end you should specify `$params` as nested arrays.\n     * For a `PostSearchForm` model where parameter names are `PostSearchForm[id]` and `PostSearchForm[src]` the syntax\n     * would be the following:\n     *\n     * ```\n     * // index.php?r=post%2Findex&PostSearchForm%5Bid%5D=100&PostSearchForm%5Bsrc%5D=google\n     * echo Url::current([\n     *     $postSearch->formName() => ['id' => 100, 'src' => 'google'],\n     * ]);\n     * ```\n     *\n     * @param array $params an associative array of parameters that will be merged with the current GET parameters.\n     * If a parameter value is null, the corresponding GET parameter will be removed.\n     * @param bool|string $scheme the URI scheme to use in the generated URL:\n     *\n     * - `false` (default): generating a relative URL.\n     * - `true`: returning an absolute base URL whose scheme is the same as that in [[\\yii\\web\\UrlManager::$hostInfo]].\n     * - string: generating an absolute URL with the specified scheme (either `http`, `https` or empty string\n     *   for protocol-relative URL).\n     *\n     * @return string the generated URL\n     * @since 2.0.3\n     */\n    public static function current(array $params = [], $scheme = false)\n    {\n        $currentParams = Yii::$app->getRequest()->getQueryParams();\n        $currentParams[0] = '/' . Yii::$app->controller->getRoute();\n        $route = array_replace_recursive($currentParams, $params);\n        return static::toRoute($route, $scheme);\n    }\n\n    /**\n     * @return \\yii\\web\\UrlManager URL manager used to create URLs\n     * @since 2.0.8\n     */\n    protected static function getUrlManager()\n    {\n        return static::$urlManager ?: Yii::$app->getUrlManager();\n    }\n}\n"
  },
  {
    "path": "framework/helpers/BaseVarDumper.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\helpers;\n\nuse yii\\base\\Arrayable;\nuse yii\\base\\InvalidValueException;\n\n/**\n * BaseVarDumper provides concrete implementation for [[VarDumper]].\n *\n * Do not use BaseVarDumper. Use [[VarDumper]] instead.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass BaseVarDumper\n{\n    private static $_objects;\n    private static $_output;\n    private static $_depth;\n\n\n    /**\n     * Displays a variable.\n     * This method achieves the similar functionality as var_dump and print_r\n     * but is more robust when handling complex objects such as Yii controllers.\n     * @param mixed $var variable to be dumped\n     * @param int $depth maximum depth that the dumper should go into the variable. Defaults to 10.\n     * @param bool $highlight whether the result should be syntax-highlighted\n     */\n    public static function dump($var, $depth = 10, $highlight = false)\n    {\n        echo static::dumpAsString($var, $depth, $highlight);\n    }\n\n    /**\n     * Dumps a variable in terms of a string.\n     * This method achieves the similar functionality as var_dump and print_r\n     * but is more robust when handling complex objects such as Yii controllers.\n     * @param mixed $var variable to be dumped\n     * @param int $depth maximum depth that the dumper should go into the variable. Defaults to 10.\n     * @param bool $highlight whether the result should be syntax-highlighted\n     * @return string the string representation of the variable\n     */\n    public static function dumpAsString($var, $depth = 10, $highlight = false)\n    {\n        self::$_output = '';\n        self::$_objects = [];\n        self::$_depth = $depth;\n        self::dumpInternal($var, 0);\n        if ($highlight) {\n            $result = highlight_string(\"<?php\\n\" . self::$_output, true);\n            self::$_output = preg_replace('/&lt;\\\\?php<br \\\\/>/', '', $result, 1);\n        }\n\n        return self::$_output;\n    }\n\n    /**\n     * @param mixed $var variable to be dumped\n     * @param int $level depth level\n     */\n    private static function dumpInternal($var, $level)\n    {\n        switch (gettype($var)) {\n            case 'boolean':\n                self::$_output .= $var ? 'true' : 'false';\n                break;\n            case 'integer':\n                self::$_output .= (string)$var;\n                break;\n            case 'double':\n                self::$_output .= (string)$var;\n                break;\n            case 'string':\n                self::$_output .= \"'\" . addslashes($var) . \"'\";\n                break;\n            case 'resource':\n                self::$_output .= '{resource}';\n                break;\n            case 'NULL':\n                self::$_output .= 'null';\n                break;\n            case 'unknown type':\n                self::$_output .= '{unknown}';\n                break;\n            case 'array':\n                if (self::$_depth <= $level) {\n                    self::$_output .= '[...]';\n                } elseif (empty($var)) {\n                    self::$_output .= '[]';\n                } else {\n                    $keys = array_keys($var);\n                    $spaces = str_repeat(' ', $level * 4);\n                    self::$_output .= '[';\n                    foreach ($keys as $key) {\n                        self::$_output .= \"\\n\" . $spaces . '    ';\n                        self::dumpInternal($key, 0);\n                        self::$_output .= ' => ';\n                        self::dumpInternal($var[$key], $level + 1);\n                    }\n                    self::$_output .= \"\\n\" . $spaces . ']';\n                }\n                break;\n            case 'object':\n                if (($id = array_search($var, self::$_objects, true)) !== false) {\n                    self::$_output .= get_class($var) . '#' . ($id + 1) . '(...)';\n                } elseif (self::$_depth <= $level) {\n                    self::$_output .= get_class($var) . '(...)';\n                } else {\n                    $id = array_push(self::$_objects, $var);\n                    $className = get_class($var);\n                    $spaces = str_repeat(' ', $level * 4);\n                    self::$_output .= \"$className#$id\\n\" . $spaces . '(';\n                    if ('__PHP_Incomplete_Class' !== get_class($var) && method_exists($var, '__debugInfo')) {\n                        $dumpValues = $var->__debugInfo();\n                        if (!is_array($dumpValues)) {\n                            throw new InvalidValueException('__debuginfo() must return an array');\n                        }\n                    } else {\n                        $dumpValues = (array) $var;\n                    }\n                    foreach ($dumpValues as $key => $value) {\n                        $keyDisplay = strtr(trim($key), \"\\0\", ':');\n                        self::$_output .= \"\\n\" . $spaces . \"    [$keyDisplay] => \";\n                        self::dumpInternal($value, $level + 1);\n                    }\n                    self::$_output .= \"\\n\" . $spaces . ')';\n                }\n                break;\n        }\n    }\n\n    /**\n     * Exports a variable as a string representation.\n     *\n     * The string is a valid PHP expression that can be evaluated by PHP parser\n     * and the evaluation result will give back the variable value.\n     *\n     * This method is similar to `var_export()`. The main difference is that\n     * it generates more compact string representation using short array syntax.\n     *\n     * It also handles objects by using the PHP functions serialize() and unserialize().\n     *\n     * PHP 5.4 or above is required to parse the exported value.\n     *\n     * @param mixed $var the variable to be exported.\n     * @return string a string representation of the variable\n     */\n    public static function export($var)\n    {\n        self::$_output = '';\n        self::exportInternal($var, 0);\n        return self::$_output;\n    }\n\n    /**\n     * @param mixed $var variable to be exported\n     * @param int $level depth level\n     */\n    private static function exportInternal($var, $level)\n    {\n        switch (gettype($var)) {\n            case 'NULL':\n                self::$_output .= 'null';\n                break;\n            case 'array':\n                if (empty($var)) {\n                    self::$_output .= '[]';\n                } else {\n                    $keys = array_keys($var);\n                    $outputKeys = ($keys !== range(0, count($var) - 1));\n                    $spaces = str_repeat(' ', $level * 4);\n                    self::$_output .= '[';\n                    foreach ($keys as $key) {\n                        self::$_output .= \"\\n\" . $spaces . '    ';\n                        if ($outputKeys) {\n                            self::exportInternal($key, 0);\n                            self::$_output .= ' => ';\n                        }\n                        self::exportInternal($var[$key], $level + 1);\n                        self::$_output .= ',';\n                    }\n                    self::$_output .= \"\\n\" . $spaces . ']';\n                }\n                break;\n            case 'object':\n                if ($var instanceof \\Closure) {\n                    self::$_output .= self::exportClosure($var);\n                } else {\n                    try {\n                        $output = 'unserialize(' . var_export(serialize($var), true) . ')';\n                    } catch (\\Exception $e) {\n                        // serialize may fail, for example: if object contains a `\\Closure` instance\n                        // so we use a fallback\n                        if ($var instanceof Arrayable) {\n                            self::exportInternal($var->toArray(), $level);\n                            return;\n                        } elseif ($var instanceof \\IteratorAggregate) {\n                            $varAsArray = [];\n                            foreach ($var as $key => $value) {\n                                $varAsArray[$key] = $value;\n                            }\n                            self::exportInternal($varAsArray, $level);\n                            return;\n                        } elseif ('__PHP_Incomplete_Class' !== get_class($var) && method_exists($var, '__toString')) {\n                            $output = var_export($var->__toString(), true);\n                        } else {\n                            $outputBackup = self::$_output;\n                            $output = var_export(self::dumpAsString($var), true);\n                            self::$_output = $outputBackup;\n                        }\n                    }\n                    self::$_output .= $output;\n                }\n                break;\n            default:\n                self::$_output .= var_export($var, true);\n        }\n    }\n\n    /**\n     * Exports a [[Closure]] instance.\n     * @param \\Closure $closure closure instance.\n     * @return string\n     */\n    private static function exportClosure(\\Closure $closure)\n    {\n        $reflection = new \\ReflectionFunction($closure);\n\n        $fileName = $reflection->getFileName();\n        $start = $reflection->getStartLine();\n        $end = $reflection->getEndLine();\n\n        if ($fileName === false || $start === false || $end === false) {\n            return 'function() {/* Error: unable to determine Closure source */}';\n        }\n\n        --$start;\n\n        $source = implode(\"\\n\", array_slice(file($fileName), $start, $end - $start));\n        $tokens = token_get_all('<?php ' . $source);\n        array_shift($tokens);\n\n        $closureTokens = [];\n        $pendingParenthesisCount = 0;\n        foreach ($tokens as $token) {\n            if (isset($token[0]) && $token[0] === T_FUNCTION) {\n                $closureTokens[] = $token[1];\n                continue;\n            }\n            if ($closureTokens !== []) {\n                $closureTokens[] = isset($token[1]) ? $token[1] : $token;\n                if ($token === '}') {\n                    $pendingParenthesisCount--;\n                    if ($pendingParenthesisCount === 0) {\n                        break;\n                    }\n                } elseif ($token === '{') {\n                    $pendingParenthesisCount++;\n                }\n            }\n        }\n\n        return implode('', $closureTokens);\n    }\n}\n"
  },
  {
    "path": "framework/helpers/Console.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\helpers;\n\n/**\n * Console helper provides useful methods for command line related tasks such as getting input or formatting and coloring\n * output.\n *\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0\n */\nclass Console extends BaseConsole\n{\n}\n"
  },
  {
    "path": "framework/helpers/FileHelper.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\helpers;\n\n/**\n * File system helper.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Alex Makarov <sam@rmcreative.ru>\n * @since 2.0\n */\nclass FileHelper extends BaseFileHelper\n{\n}\n"
  },
  {
    "path": "framework/helpers/FormatConverter.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\helpers;\n\n/**\n * FormatConverter provides functionality to convert between different formatting pattern formats.\n *\n * It provides functions to convert date format patterns between different conventions.\n *\n * @author Carsten Brandt <mail@cebe.cc>\n * @author Enrica Ruedin <e.ruedin@guggach.com>\n * @since 2.0\n */\nclass FormatConverter extends BaseFormatConverter\n{\n}\n"
  },
  {
    "path": "framework/helpers/Html.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\helpers;\n\n/**\n * Html provides a set of static methods for generating commonly used HTML tags.\n *\n * Nearly all of the methods in this class allow setting additional html attributes for the html\n * tags they generate. You can specify, for example, `class`, `style` or `id` for an html element\n * using the `$options` parameter. See the documentation of the [[tag()]] method for more details.\n *\n * For more details and usage information on Html, see the [guide article on html helpers](guide:helper-html).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Html extends BaseHtml\n{\n}\n"
  },
  {
    "path": "framework/helpers/HtmlPurifier.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\helpers;\n\n/**\n * HtmlPurifier provides an ability to clean up HTML from any harmful code.\n *\n * Basic usage is the following:\n *\n * ```\n * echo HtmlPurifier::process($html);\n * ```\n *\n * If you want to configure it:\n *\n * ```\n * echo HtmlPurifier::process($html, [\n *     'Attr.EnableID' => true,\n * ]);\n * ```\n *\n * For more details please refer to [HTMLPurifier documentation](http://htmlpurifier.org/).\n *\n * @author Alexander Makarov <sam@rmcreative.ru>\n * @since 2.0\n */\nclass HtmlPurifier extends BaseHtmlPurifier\n{\n}\n"
  },
  {
    "path": "framework/helpers/Inflector.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\helpers;\n\n/**\n * Inflector pluralizes and singularizes English nouns. It also contains some other useful methods.\n *\n * @author Antonio Ramirez <amigo.cobos@gmail.com>\n * @since 2.0\n */\nclass Inflector extends BaseInflector\n{\n}\n"
  },
  {
    "path": "framework/helpers/IpHelper.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\helpers;\n\n/**\n * Class IpHelper provides a set of IP-related static methods.\n *\n * Methods expect correct IP addresses.\n * To validate IP addresses use [[\\yii\\validators\\IpValidator|IpValidator]].\n *\n * @author Dmytro Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.14\n */\nclass IpHelper extends BaseIpHelper\n{\n}\n"
  },
  {
    "path": "framework/helpers/Json.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\helpers;\n\n/**\n * Json is a helper class providing JSON data encoding and decoding.\n * It enhances the PHP built-in functions `json_encode()` and `json_decode()`\n * by supporting encoding JavaScript expressions and throwing exceptions when decoding fails.\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Json extends BaseJson\n{\n}\n"
  },
  {
    "path": "framework/helpers/Markdown.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\helpers;\n\n/**\n * Markdown provides an ability to transform markdown into HTML.\n *\n * Basic usage is the following:\n *\n * ```\n * $myHtml = Markdown::process($myText); // use original markdown flavor\n * $myHtml = Markdown::process($myText, 'gfm'); // use github flavored markdown\n * $myHtml = Markdown::process($myText, 'extra'); // use markdown extra\n * ```\n *\n * You can configure multiple flavors using the [[$flavors]] property.\n *\n * For more details please refer to the [Markdown library documentation](https://github.com/cebe/markdown#readme).\n *\n * > Note: The Markdown library works with PHPDoc annotations so if you use it together with\n * > PHP `opcache` make sure [it does not strip comments](https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.save-comments).\n *\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0\n */\nclass Markdown extends BaseMarkdown\n{\n}\n"
  },
  {
    "path": "framework/helpers/ReplaceArrayValue.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\helpers;\n\nuse yii\\base\\InvalidConfigException;\n\n/**\n * Object that represents the replacement of array value while performing [[ArrayHelper::merge()]].\n *\n * Usage example:\n *\n * ```\n * $array1 = [\n *     'ids' => [\n *         1,\n *     ],\n *     'validDomains' => [\n *         'example.com',\n *         'www.example.com',\n *     ],\n * ];\n *\n * $array2 = [\n *     'ids' => [\n *         2,\n *     ],\n *     'validDomains' => new \\yii\\helpers\\ReplaceArrayValue([\n *         'yiiframework.com',\n *         'www.yiiframework.com',\n *     ]),\n * ];\n *\n * $result = \\yii\\helpers\\ArrayHelper::merge($array1, $array2);\n * ```\n *\n * The result will be\n *\n * ```\n * [\n *     'ids' => [\n *         1,\n *         2,\n *     ],\n *     'validDomains' => [\n *         'yiiframework.com',\n *         'www.yiiframework.com',\n *     ],\n * ]\n * ```\n *\n * @author Robert Korulczyk <robert@korulczyk.pl>\n * @since 2.0.10\n */\nclass ReplaceArrayValue\n{\n    /**\n     * @var mixed value used as replacement.\n     */\n    public $value;\n\n\n    /**\n     * Constructor.\n     * @param mixed $value value used as replacement.\n     */\n    public function __construct($value)\n    {\n        $this->value = $value;\n    }\n\n    /**\n     * Restores class state after using `var_export()`.\n     *\n     * @param array $state\n     * @return ReplaceArrayValue\n     * @throws InvalidConfigException when $state property does not contain `value` parameter\n     * @see https://www.php.net/manual/en/function.var-export.php\n     * @since 2.0.16\n     */\n    public static function __set_state($state)\n    {\n        if (!isset($state['value'])) {\n            throw new InvalidConfigException('Failed to instantiate class \"ReplaceArrayValue\". Required parameter \"value\" is missing');\n        }\n\n        return new self($state['value']);\n    }\n}\n"
  },
  {
    "path": "framework/helpers/StringHelper.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\helpers;\n\n/**\n * StringHelper.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Alex Makarov <sam@rmcreative.ru>\n * @since 2.0\n */\nclass StringHelper extends BaseStringHelper\n{\n}\n"
  },
  {
    "path": "framework/helpers/UnsetArrayValue.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\helpers;\n\n/**\n * Object that represents the removal of array value while performing [[ArrayHelper::merge()]].\n *\n * Usage example:\n *\n * ```\n * $array1 = [\n *     'ids' => [\n *         1,\n *     ],\n *     'validDomains' => [\n *         'example.com',\n *         'www.example.com',\n *     ],\n * ];\n *\n * $array2 = [\n *     'ids' => [\n *         2,\n *     ],\n *     'validDomains' => new \\yii\\helpers\\UnsetArrayValue(),\n * ];\n *\n * $result = \\yii\\helpers\\ArrayHelper::merge($array1, $array2);\n * ```\n *\n * The result will be\n *\n * ```\n * [\n *     'ids' => [\n *         1,\n *         2,\n *     ],\n * ]\n * ```\n *\n * @author Robert Korulczyk <robert@korulczyk.pl>\n * @since 2.0.10\n */\nclass UnsetArrayValue\n{\n    /**\n     * Restores class state after using `var_export()`.\n     *\n     * @param array $state\n     * @return UnsetArrayValue\n     * @see https://www.php.net/manual/en/function.var-export.php\n     * @since 2.0.16\n     */\n    public static function __set_state($state)\n    {\n        return new self();\n    }\n}\n"
  },
  {
    "path": "framework/helpers/Url.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\helpers;\n\n/**\n * Url provides a set of static methods for managing URLs.\n *\n * For more details and usage information on Url, see the [guide article on url helpers](guide:helper-url).\n *\n * @author Alexander Makarov <sam@rmcreative.ru>\n * @since 2.0\n */\nclass Url extends BaseUrl\n{\n}\n"
  },
  {
    "path": "framework/helpers/VarDumper.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\helpers;\n\n/**\n * VarDumper is intended to replace the buggy PHP function var_dump and print_r.\n * It can correctly identify the recursively referenced objects in a complex\n * object structure. It also has a recursive depth control to avoid indefinite\n * recursive display of some peculiar variables.\n *\n * VarDumper can be used as follows,\n *\n * ```\n * VarDumper::dump($var);\n * ```\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass VarDumper extends BaseVarDumper\n{\n}\n"
  },
  {
    "path": "framework/helpers/mimeAliases.php",
    "content": "<?php\n\n/**\n * MIME aliases.\n *\n * This file contains aliases for MIME types.\n *\n * All extra changes made to this file must be committed to /build/controllers/MimeTypeController.php\n * otherwise they will be lost on next build.\n */\n\nreturn [\n    'text/rtf' => 'application/rtf',\n    'text/xml' => 'application/xml',\n    'image/svg' => 'image/svg+xml',\n    'image/x-bmp' => 'image/bmp',\n    'image/x-bitmap' => 'image/bmp',\n    'image/x-xbitmap' => 'image/bmp',\n    'image/x-win-bitmap' => 'image/bmp',\n    'image/x-windows-bmp' => 'image/bmp',\n    'image/ms-bmp' => 'image/bmp',\n    'image/x-ms-bmp' => 'image/bmp',\n    'application/bmp' => 'image/bmp',\n    'application/x-bmp' => 'image/bmp',\n    'application/x-win-bitmap' => 'image/bmp',\n];\n"
  },
  {
    "path": "framework/helpers/mimeExtensions.php",
    "content": "<?php\n\n/**\n * MIME type extensions.\n *\n * This file contains most commonly used extensions for MIME types.\n * If there are multiple extensions for a singe MIME type\n * they are ordered from most to least common.\n * Its content is generated from the apache http mime.types file.\n * https://raw.githubusercontent.com/apache/httpd/refs/heads/trunk/docs/conf/mime.types\n * This file has been placed in the public domain for unlimited redistribution.\n *\n * All extra changes made to this file must be committed to /build/controllers/MimeTypeController.php\n * otherwise they will be lost on next build.\n */\n\nreturn [\n    'application/andrew-inset' => 'ez',\n    'application/applixware' => 'aw',\n    'application/atom+xml' => 'atom',\n    'application/atomcat+xml' => 'atomcat',\n    'application/atomsvc+xml' => 'atomsvc',\n    'application/ccxml+xml' => 'ccxml',\n    'application/cdmi-capability' => 'cdmia',\n    'application/cdmi-container' => 'cdmic',\n    'application/cdmi-domain' => 'cdmid',\n    'application/cdmi-object' => 'cdmio',\n    'application/cdmi-queue' => 'cdmiq',\n    'application/cu-seeme' => 'cu',\n    'application/davmount+xml' => 'davmount',\n    'application/docbook+xml' => 'dbk',\n    'application/dssc+der' => 'dssc',\n    'application/dssc+xml' => 'xdssc',\n    'application/ecmascript' => 'ecma',\n    'application/emma+xml' => 'emma',\n    'application/epub+zip' => 'epub',\n    'application/exi' => 'exi',\n    'application/font-tdpfr' => 'pfr',\n    'application/gml+xml' => 'gml',\n    'application/gpx+xml' => 'gpx',\n    'application/gxf' => 'gxf',\n    'application/hyperstudio' => 'stk',\n    'application/inkml+xml' => [\n        'ink',\n        'inkml',\n    ],\n    'application/ipfix' => 'ipfix',\n    'application/java-archive' => 'jar',\n    'application/java-serialized-object' => 'ser',\n    'application/java-vm' => 'class',\n    'application/json' => 'json',\n    'application/jsonml+json' => 'jsonml',\n    'application/lost+xml' => 'lostxml',\n    'application/mac-binhex40' => 'hqx',\n    'application/mac-compactpro' => 'cpt',\n    'application/mads+xml' => 'mads',\n    'application/marc' => 'mrc',\n    'application/marcxml+xml' => 'mrcx',\n    'application/mathematica' => [\n        'ma',\n        'nb',\n        'mb',\n    ],\n    'application/mathml+xml' => 'mathml',\n    'application/mbox' => 'mbox',\n    'application/mediaservercontrol+xml' => 'mscml',\n    'application/metalink+xml' => 'metalink',\n    'application/metalink4+xml' => 'meta4',\n    'application/mets+xml' => 'mets',\n    'application/mods+xml' => 'mods',\n    'application/mp21' => [\n        'm21',\n        'mp21',\n    ],\n    'application/mp4' => 'mp4s',\n    'application/msword' => [\n        'doc',\n        'dot',\n    ],\n    'application/mxf' => 'mxf',\n    'application/octet-stream' => [\n        'bin',\n        'dms',\n        'lrf',\n        'mar',\n        'so',\n        'dist',\n        'distz',\n        'pkg',\n        'bpk',\n        'dump',\n        'elc',\n        'deploy',\n    ],\n    'application/oda' => 'oda',\n    'application/oebps-package+xml' => 'opf',\n    'application/ogg' => 'ogx',\n    'application/omdoc+xml' => 'omdoc',\n    'application/onenote' => [\n        'onetoc',\n        'onetoc2',\n        'onetmp',\n        'onepkg',\n    ],\n    'application/oxps' => 'oxps',\n    'application/patch-ops-error+xml' => 'xer',\n    'application/pdf' => 'pdf',\n    'application/pgp-encrypted' => 'pgp',\n    'application/pgp-signature' => [\n        'asc',\n        'sig',\n    ],\n    'application/pics-rules' => 'prf',\n    'application/pkcs10' => 'p10',\n    'application/pkcs7-mime' => [\n        'p7m',\n        'p7c',\n    ],\n    'application/pkcs7-signature' => 'p7s',\n    'application/pkcs8' => 'p8',\n    'application/pkix-attr-cert' => 'ac',\n    'application/pkix-cert' => 'cer',\n    'application/pkix-crl' => 'crl',\n    'application/pkix-pkipath' => 'pkipath',\n    'application/pkixcmp' => 'pki',\n    'application/pls+xml' => 'pls',\n    'application/postscript' => [\n        'ai',\n        'eps',\n        'ps',\n    ],\n    'application/prs.cww' => 'cww',\n    'application/pskc+xml' => 'pskcxml',\n    'application/rdf+xml' => 'rdf',\n    'application/reginfo+xml' => 'rif',\n    'application/relax-ng-compact-syntax' => 'rnc',\n    'application/resource-lists+xml' => 'rl',\n    'application/resource-lists-diff+xml' => 'rld',\n    'application/rls-services+xml' => 'rs',\n    'application/rpki-ghostbusters' => 'gbr',\n    'application/rpki-manifest' => 'mft',\n    'application/rpki-roa' => 'roa',\n    'application/rsd+xml' => 'rsd',\n    'application/rss+xml' => 'rss',\n    'application/rtf' => 'rtf',\n    'application/sbml+xml' => 'sbml',\n    'application/scvp-cv-request' => 'scq',\n    'application/scvp-cv-response' => 'scs',\n    'application/scvp-vp-request' => 'spq',\n    'application/scvp-vp-response' => 'spp',\n    'application/sdp' => 'sdp',\n    'application/set-payment-initiation' => 'setpay',\n    'application/set-registration-initiation' => 'setreg',\n    'application/shf+xml' => 'shf',\n    'application/smil+xml' => [\n        'smi',\n        'smil',\n    ],\n    'application/sparql-query' => 'rq',\n    'application/sparql-results+xml' => 'srx',\n    'application/srgs' => 'gram',\n    'application/srgs+xml' => 'grxml',\n    'application/sru+xml' => 'sru',\n    'application/ssdl+xml' => 'ssdl',\n    'application/ssml+xml' => 'ssml',\n    'application/tei+xml' => [\n        'tei',\n        'teicorpus',\n    ],\n    'application/thraud+xml' => 'tfi',\n    'application/timestamped-data' => 'tsd',\n    'application/vnd.3gpp.pic-bw-large' => 'plb',\n    'application/vnd.3gpp.pic-bw-small' => 'psb',\n    'application/vnd.3gpp.pic-bw-var' => 'pvb',\n    'application/vnd.3gpp2.tcap' => 'tcap',\n    'application/vnd.3m.post-it-notes' => 'pwn',\n    'application/vnd.accpac.simply.aso' => 'aso',\n    'application/vnd.accpac.simply.imp' => 'imp',\n    'application/vnd.acucobol' => 'acu',\n    'application/vnd.acucorp' => [\n        'atc',\n        'acutc',\n    ],\n    'application/vnd.adobe.air-application-installer-package+zip' => 'air',\n    'application/vnd.adobe.formscentral.fcdt' => 'fcdt',\n    'application/vnd.adobe.fxp' => [\n        'fxp',\n        'fxpl',\n    ],\n    'application/vnd.adobe.xdp+xml' => 'xdp',\n    'application/vnd.adobe.xfdf' => 'xfdf',\n    'application/vnd.ahead.space' => 'ahead',\n    'application/vnd.airzip.filesecure.azf' => 'azf',\n    'application/vnd.airzip.filesecure.azs' => 'azs',\n    'application/vnd.amazon.ebook' => 'azw',\n    'application/vnd.americandynamics.acc' => 'acc',\n    'application/vnd.amiga.ami' => 'ami',\n    'application/vnd.android.package-archive' => 'apk',\n    'application/vnd.anser-web-certificate-issue-initiation' => 'cii',\n    'application/vnd.anser-web-funds-transfer-initiation' => 'fti',\n    'application/vnd.antix.game-component' => 'atx',\n    'application/vnd.apple.installer+xml' => 'mpkg',\n    'application/vnd.apple.mpegurl' => 'm3u8',\n    'application/vnd.aristanetworks.swi' => 'swi',\n    'application/vnd.astraea-software.iota' => 'iota',\n    'application/vnd.audiograph' => 'aep',\n    'application/vnd.blueice.multipass' => 'mpm',\n    'application/vnd.bmi' => 'bmi',\n    'application/vnd.businessobjects' => 'rep',\n    'application/vnd.chemdraw+xml' => 'cdxml',\n    'application/vnd.chipnuts.karaoke-mmd' => 'mmd',\n    'application/vnd.cinderella' => 'cdy',\n    'application/vnd.claymore' => 'cla',\n    'application/vnd.cloanto.rp9' => 'rp9',\n    'application/vnd.clonk.c4group' => [\n        'c4g',\n        'c4d',\n        'c4f',\n        'c4p',\n        'c4u',\n    ],\n    'application/vnd.cluetrust.cartomobile-config' => 'c11amc',\n    'application/vnd.cluetrust.cartomobile-config-pkg' => 'c11amz',\n    'application/vnd.commonspace' => 'csp',\n    'application/vnd.contact.cmsg' => 'cdbcmsg',\n    'application/vnd.cosmocaller' => 'cmc',\n    'application/vnd.crick.clicker' => 'clkx',\n    'application/vnd.crick.clicker.keyboard' => 'clkk',\n    'application/vnd.crick.clicker.palette' => 'clkp',\n    'application/vnd.crick.clicker.template' => 'clkt',\n    'application/vnd.crick.clicker.wordbank' => 'clkw',\n    'application/vnd.criticaltools.wbs+xml' => 'wbs',\n    'application/vnd.ctc-posml' => 'pml',\n    'application/vnd.cups-ppd' => 'ppd',\n    'application/vnd.curl.car' => 'car',\n    'application/vnd.curl.pcurl' => 'pcurl',\n    'application/vnd.dart' => 'dart',\n    'application/vnd.data-vision.rdz' => 'rdz',\n    'application/vnd.dece.data' => [\n        'uvf',\n        'uvvf',\n        'uvd',\n        'uvvd',\n    ],\n    'application/vnd.dece.ttml+xml' => [\n        'uvt',\n        'uvvt',\n    ],\n    'application/vnd.dece.unspecified' => [\n        'uvx',\n        'uvvx',\n    ],\n    'application/vnd.dece.zip' => [\n        'uvz',\n        'uvvz',\n    ],\n    'application/vnd.denovo.fcselayout-link' => 'fe_launch',\n    'application/vnd.dna' => 'dna',\n    'application/vnd.dolby.mlp' => 'mlp',\n    'application/vnd.dpgraph' => 'dpg',\n    'application/vnd.dreamfactory' => 'dfac',\n    'application/vnd.ds-keypoint' => 'kpxx',\n    'application/vnd.dvb.ait' => 'ait',\n    'application/vnd.dvb.service' => 'svc',\n    'application/vnd.dynageo' => 'geo',\n    'application/vnd.ecowin.chart' => 'mag',\n    'application/vnd.enliven' => 'nml',\n    'application/vnd.epson.esf' => 'esf',\n    'application/vnd.epson.msf' => 'msf',\n    'application/vnd.epson.quickanime' => 'qam',\n    'application/vnd.epson.salt' => 'slt',\n    'application/vnd.epson.ssf' => 'ssf',\n    'application/vnd.eszigno3+xml' => [\n        'es3',\n        'et3',\n    ],\n    'application/vnd.ezpix-album' => 'ez2',\n    'application/vnd.ezpix-package' => 'ez3',\n    'application/vnd.fdf' => 'fdf',\n    'application/vnd.fdsn.mseed' => 'mseed',\n    'application/vnd.fdsn.seed' => [\n        'seed',\n        'dataless',\n    ],\n    'application/vnd.flographit' => 'gph',\n    'application/vnd.fluxtime.clip' => 'ftc',\n    'application/vnd.framemaker' => [\n        'fm',\n        'frame',\n        'maker',\n        'book',\n    ],\n    'application/vnd.frogans.fnc' => 'fnc',\n    'application/vnd.frogans.ltf' => 'ltf',\n    'application/vnd.fsc.weblaunch' => 'fsc',\n    'application/vnd.fujitsu.oasys' => 'oas',\n    'application/vnd.fujitsu.oasys2' => 'oa2',\n    'application/vnd.fujitsu.oasys3' => 'oa3',\n    'application/vnd.fujitsu.oasysgp' => 'fg5',\n    'application/vnd.fujitsu.oasysprs' => 'bh2',\n    'application/vnd.fujixerox.ddd' => 'ddd',\n    'application/vnd.fujixerox.docuworks' => 'xdw',\n    'application/vnd.fujixerox.docuworks.binder' => 'xbd',\n    'application/vnd.fuzzysheet' => 'fzs',\n    'application/vnd.genomatix.tuxedo' => 'txd',\n    'application/vnd.geogebra.file' => 'ggb',\n    'application/vnd.geogebra.slides' => 'ggs',\n    'application/vnd.geogebra.tool' => 'ggt',\n    'application/vnd.geometry-explorer' => [\n        'gex',\n        'gre',\n    ],\n    'application/vnd.geonext' => 'gxt',\n    'application/vnd.geoplan' => 'g2w',\n    'application/vnd.geospace' => 'g3w',\n    'application/vnd.gmx' => 'gmx',\n    'application/vnd.google-earth.kml+xml' => 'kml',\n    'application/vnd.google-earth.kmz' => 'kmz',\n    'application/vnd.grafeq' => [\n        'gqf',\n        'gqs',\n    ],\n    'application/vnd.groove-account' => 'gac',\n    'application/vnd.groove-help' => 'ghf',\n    'application/vnd.groove-identity-message' => 'gim',\n    'application/vnd.groove-injector' => 'grv',\n    'application/vnd.groove-tool-message' => 'gtm',\n    'application/vnd.groove-tool-template' => 'tpl',\n    'application/vnd.groove-vcard' => 'vcg',\n    'application/vnd.hal+xml' => 'hal',\n    'application/vnd.handheld-entertainment+xml' => 'zmm',\n    'application/vnd.hbci' => 'hbci',\n    'application/vnd.hhe.lesson-player' => 'les',\n    'application/vnd.hp-hpgl' => 'hpgl',\n    'application/vnd.hp-hpid' => 'hpid',\n    'application/vnd.hp-hps' => 'hps',\n    'application/vnd.hp-jlyt' => 'jlt',\n    'application/vnd.hp-pcl' => 'pcl',\n    'application/vnd.hp-pclxl' => 'pclxl',\n    'application/vnd.hydrostatix.sof-data' => 'sfd-hdstx',\n    'application/vnd.ibm.minipay' => 'mpy',\n    'application/vnd.ibm.modcap' => [\n        'afp',\n        'listafp',\n        'list3820',\n    ],\n    'application/vnd.ibm.rights-management' => 'irm',\n    'application/vnd.ibm.secure-container' => 'sc',\n    'application/vnd.iccprofile' => [\n        'icc',\n        'icm',\n    ],\n    'application/vnd.igloader' => 'igl',\n    'application/vnd.immervision-ivp' => 'ivp',\n    'application/vnd.immervision-ivu' => 'ivu',\n    'application/vnd.insors.igm' => 'igm',\n    'application/vnd.intercon.formnet' => [\n        'xpw',\n        'xpx',\n    ],\n    'application/vnd.intergeo' => 'i2g',\n    'application/vnd.intu.qbo' => 'qbo',\n    'application/vnd.intu.qfx' => 'qfx',\n    'application/vnd.ipunplugged.rcprofile' => 'rcprofile',\n    'application/vnd.irepository.package+xml' => 'irp',\n    'application/vnd.is-xpr' => 'xpr',\n    'application/vnd.isac.fcs' => 'fcs',\n    'application/vnd.jam' => 'jam',\n    'application/vnd.jcp.javame.midlet-rms' => 'rms',\n    'application/vnd.jisp' => 'jisp',\n    'application/vnd.joost.joda-archive' => 'joda',\n    'application/vnd.kahootz' => [\n        'ktz',\n        'ktr',\n    ],\n    'application/vnd.kde.karbon' => 'karbon',\n    'application/vnd.kde.kchart' => 'chrt',\n    'application/vnd.kde.kformula' => 'kfo',\n    'application/vnd.kde.kivio' => 'flw',\n    'application/vnd.kde.kontour' => 'kon',\n    'application/vnd.kde.kpresenter' => [\n        'kpr',\n        'kpt',\n    ],\n    'application/vnd.kde.kspread' => 'ksp',\n    'application/vnd.kde.kword' => [\n        'kwd',\n        'kwt',\n    ],\n    'application/vnd.kenameaapp' => 'htke',\n    'application/vnd.kidspiration' => 'kia',\n    'application/vnd.kinar' => [\n        'kne',\n        'knp',\n    ],\n    'application/vnd.koan' => [\n        'skp',\n        'skd',\n        'skt',\n        'skm',\n    ],\n    'application/vnd.kodak-descriptor' => 'sse',\n    'application/vnd.las.las+xml' => 'lasxml',\n    'application/vnd.llamagraphics.life-balance.desktop' => 'lbd',\n    'application/vnd.llamagraphics.life-balance.exchange+xml' => 'lbe',\n    'application/vnd.lotus-1-2-3' => '123',\n    'application/vnd.lotus-approach' => 'apr',\n    'application/vnd.lotus-freelance' => 'pre',\n    'application/vnd.lotus-notes' => 'nsf',\n    'application/vnd.lotus-organizer' => 'org',\n    'application/vnd.lotus-screencam' => 'scm',\n    'application/vnd.lotus-wordpro' => 'lwp',\n    'application/vnd.macports.portpkg' => 'portpkg',\n    'application/vnd.mcd' => 'mcd',\n    'application/vnd.medcalcdata' => 'mc1',\n    'application/vnd.mediastation.cdkey' => 'cdkey',\n    'application/vnd.mfer' => 'mwf',\n    'application/vnd.mfmp' => 'mfm',\n    'application/vnd.micrografx.flo' => 'flo',\n    'application/vnd.micrografx.igx' => 'igx',\n    'application/vnd.mif' => 'mif',\n    'application/vnd.mobius.daf' => 'daf',\n    'application/vnd.mobius.dis' => 'dis',\n    'application/vnd.mobius.mbk' => 'mbk',\n    'application/vnd.mobius.mqy' => 'mqy',\n    'application/vnd.mobius.msl' => 'msl',\n    'application/vnd.mobius.plc' => 'plc',\n    'application/vnd.mobius.txf' => 'txf',\n    'application/vnd.mophun.application' => 'mpn',\n    'application/vnd.mophun.certificate' => 'mpc',\n    'application/vnd.mozilla.xul+xml' => 'xul',\n    'application/vnd.ms-artgalry' => 'cil',\n    'application/vnd.ms-cab-compressed' => 'cab',\n    'application/vnd.ms-excel' => [\n        'xls',\n        'xlm',\n        'xla',\n        'xlc',\n        'xlt',\n        'xlw',\n    ],\n    'application/vnd.ms-excel.addin.macroenabled.12' => 'xlam',\n    'application/vnd.ms-excel.sheet.binary.macroenabled.12' => 'xlsb',\n    'application/vnd.ms-excel.sheet.macroenabled.12' => 'xlsm',\n    'application/vnd.ms-excel.template.macroenabled.12' => 'xltm',\n    'application/vnd.ms-fontobject' => 'eot',\n    'application/vnd.ms-htmlhelp' => 'chm',\n    'application/vnd.ms-ims' => 'ims',\n    'application/vnd.ms-lrm' => 'lrm',\n    'application/vnd.ms-officetheme' => 'thmx',\n    'application/vnd.ms-pki.seccat' => 'cat',\n    'application/vnd.ms-pki.stl' => 'stl',\n    'application/vnd.ms-powerpoint' => [\n        'ppt',\n        'pps',\n        'pot',\n    ],\n    'application/vnd.ms-powerpoint.addin.macroenabled.12' => 'ppam',\n    'application/vnd.ms-powerpoint.presentation.macroenabled.12' => 'pptm',\n    'application/vnd.ms-powerpoint.slide.macroenabled.12' => 'sldm',\n    'application/vnd.ms-powerpoint.slideshow.macroenabled.12' => 'ppsm',\n    'application/vnd.ms-powerpoint.template.macroenabled.12' => 'potm',\n    'application/vnd.ms-project' => [\n        'mpp',\n        'mpt',\n    ],\n    'application/vnd.ms-word.document.macroenabled.12' => 'docm',\n    'application/vnd.ms-word.template.macroenabled.12' => 'dotm',\n    'application/vnd.ms-works' => [\n        'wps',\n        'wks',\n        'wcm',\n        'wdb',\n    ],\n    'application/vnd.ms-wpl' => 'wpl',\n    'application/vnd.ms-xpsdocument' => 'xps',\n    'application/vnd.mseq' => 'mseq',\n    'application/vnd.musician' => 'mus',\n    'application/vnd.muvee.style' => 'msty',\n    'application/vnd.mynfc' => 'taglet',\n    'application/vnd.neurolanguage.nlu' => 'nlu',\n    'application/vnd.nitf' => [\n        'ntf',\n        'nitf',\n    ],\n    'application/vnd.noblenet-directory' => 'nnd',\n    'application/vnd.noblenet-sealer' => 'nns',\n    'application/vnd.noblenet-web' => 'nnw',\n    'application/vnd.nokia.n-gage.data' => 'ngdat',\n    'application/vnd.nokia.n-gage.symbian.install' => 'n-gage',\n    'application/vnd.nokia.radio-preset' => 'rpst',\n    'application/vnd.nokia.radio-presets' => 'rpss',\n    'application/vnd.novadigm.edm' => 'edm',\n    'application/vnd.novadigm.edx' => 'edx',\n    'application/vnd.novadigm.ext' => 'ext',\n    'application/vnd.oasis.opendocument.chart' => 'odc',\n    'application/vnd.oasis.opendocument.chart-template' => 'otc',\n    'application/vnd.oasis.opendocument.database' => 'odb',\n    'application/vnd.oasis.opendocument.formula' => 'odf',\n    'application/vnd.oasis.opendocument.formula-template' => 'odft',\n    'application/vnd.oasis.opendocument.graphics' => 'odg',\n    'application/vnd.oasis.opendocument.graphics-template' => 'otg',\n    'application/vnd.oasis.opendocument.image' => 'odi',\n    'application/vnd.oasis.opendocument.image-template' => 'oti',\n    'application/vnd.oasis.opendocument.presentation' => 'odp',\n    'application/vnd.oasis.opendocument.presentation-template' => 'otp',\n    'application/vnd.oasis.opendocument.spreadsheet' => 'ods',\n    'application/vnd.oasis.opendocument.spreadsheet-template' => 'ots',\n    'application/vnd.oasis.opendocument.text' => 'odt',\n    'application/vnd.oasis.opendocument.text-master' => 'odm',\n    'application/vnd.oasis.opendocument.text-template' => 'ott',\n    'application/vnd.oasis.opendocument.text-web' => 'oth',\n    'application/vnd.olpc-sugar' => 'xo',\n    'application/vnd.oma.dd2+xml' => 'dd2',\n    'application/vnd.openofficeorg.extension' => 'oxt',\n    'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'pptx',\n    'application/vnd.openxmlformats-officedocument.presentationml.slide' => 'sldx',\n    'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => 'ppsx',\n    'application/vnd.openxmlformats-officedocument.presentationml.template' => 'potx',\n    'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx',\n    'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => 'xltx',\n    'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx',\n    'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => 'dotx',\n    'application/vnd.osgeo.mapguide.package' => 'mgp',\n    'application/vnd.osgi.dp' => 'dp',\n    'application/vnd.osgi.subsystem' => 'esa',\n    'application/vnd.palm' => [\n        'pdb',\n        'pqa',\n        'oprc',\n    ],\n    'application/vnd.pawaafile' => 'paw',\n    'application/vnd.pg.format' => 'str',\n    'application/vnd.pg.osasli' => 'ei6',\n    'application/vnd.picsel' => 'efif',\n    'application/vnd.pmi.widget' => 'wg',\n    'application/vnd.pocketlearn' => 'plf',\n    'application/vnd.powerbuilder6' => 'pbd',\n    'application/vnd.previewsystems.box' => 'box',\n    'application/vnd.proteus.magazine' => 'mgz',\n    'application/vnd.publishare-delta-tree' => 'qps',\n    'application/vnd.pvi.ptid1' => 'ptid',\n    'application/vnd.quark.quarkxpress' => [\n        'qxd',\n        'qxt',\n        'qwd',\n        'qwt',\n        'qxl',\n        'qxb',\n    ],\n    'application/vnd.realvnc.bed' => 'bed',\n    'application/vnd.recordare.musicxml' => 'mxl',\n    'application/vnd.recordare.musicxml+xml' => 'musicxml',\n    'application/vnd.rig.cryptonote' => 'cryptonote',\n    'application/vnd.rim.cod' => 'cod',\n    'application/vnd.rn-realmedia' => 'rm',\n    'application/vnd.rn-realmedia-vbr' => 'rmvb',\n    'application/vnd.route66.link66+xml' => 'link66',\n    'application/vnd.sailingtracker.track' => 'st',\n    'application/vnd.seemail' => 'see',\n    'application/vnd.sema' => 'sema',\n    'application/vnd.semd' => 'semd',\n    'application/vnd.semf' => 'semf',\n    'application/vnd.shana.informed.formdata' => 'ifm',\n    'application/vnd.shana.informed.formtemplate' => 'itp',\n    'application/vnd.shana.informed.interchange' => 'iif',\n    'application/vnd.shana.informed.package' => 'ipk',\n    'application/vnd.simtech-mindmapper' => [\n        'twd',\n        'twds',\n    ],\n    'application/vnd.smaf' => 'mmf',\n    'application/vnd.smart.teacher' => 'teacher',\n    'application/vnd.solent.sdkm+xml' => [\n        'sdkm',\n        'sdkd',\n    ],\n    'application/vnd.spotfire.dxp' => 'dxp',\n    'application/vnd.spotfire.sfs' => 'sfs',\n    'application/vnd.sqlite3' => [\n        'sqlite',\n        'sqlite3',\n    ],\n    'application/vnd.stardivision.calc' => 'sdc',\n    'application/vnd.stardivision.draw' => 'sda',\n    'application/vnd.stardivision.impress' => 'sdd',\n    'application/vnd.stardivision.math' => 'smf',\n    'application/vnd.stardivision.writer' => [\n        'sdw',\n        'vor',\n    ],\n    'application/vnd.stardivision.writer-global' => 'sgl',\n    'application/vnd.stepmania.package' => 'smzip',\n    'application/vnd.stepmania.stepchart' => 'sm',\n    'application/vnd.sun.xml.calc' => 'sxc',\n    'application/vnd.sun.xml.calc.template' => 'stc',\n    'application/vnd.sun.xml.draw' => 'sxd',\n    'application/vnd.sun.xml.draw.template' => 'std',\n    'application/vnd.sun.xml.impress' => 'sxi',\n    'application/vnd.sun.xml.impress.template' => 'sti',\n    'application/vnd.sun.xml.math' => 'sxm',\n    'application/vnd.sun.xml.writer' => 'sxw',\n    'application/vnd.sun.xml.writer.global' => 'sxg',\n    'application/vnd.sun.xml.writer.template' => 'stw',\n    'application/vnd.sus-calendar' => [\n        'sus',\n        'susp',\n    ],\n    'application/vnd.svd' => 'svd',\n    'application/vnd.symbian.install' => [\n        'sis',\n        'sisx',\n    ],\n    'application/vnd.syncml+xml' => 'xsm',\n    'application/vnd.syncml.dm+wbxml' => 'bdm',\n    'application/vnd.syncml.dm+xml' => 'xdm',\n    'application/vnd.tao.intent-module-archive' => 'tao',\n    'application/vnd.tcpdump.pcap' => [\n        'pcap',\n        'cap',\n        'dmp',\n    ],\n    'application/vnd.tmobile-livetv' => 'tmo',\n    'application/vnd.trid.tpt' => 'tpt',\n    'application/vnd.triscape.mxs' => 'mxs',\n    'application/vnd.trueapp' => 'tra',\n    'application/vnd.ufdl' => [\n        'ufd',\n        'ufdl',\n    ],\n    'application/vnd.uiq.theme' => 'utz',\n    'application/vnd.umajin' => 'umj',\n    'application/vnd.unity' => 'unityweb',\n    'application/vnd.uoml+xml' => 'uoml',\n    'application/vnd.vcx' => 'vcx',\n    'application/vnd.visio' => [\n        'vsd',\n        'vst',\n        'vss',\n        'vsw',\n    ],\n    'application/vnd.visionary' => 'vis',\n    'application/vnd.vsf' => 'vsf',\n    'application/vnd.wap.wbxml' => 'wbxml',\n    'application/vnd.wap.wmlc' => 'wmlc',\n    'application/vnd.wap.wmlscriptc' => 'wmlsc',\n    'application/vnd.webturbo' => 'wtb',\n    'application/vnd.wolfram.player' => 'nbp',\n    'application/vnd.wordperfect' => 'wpd',\n    'application/vnd.wqd' => 'wqd',\n    'application/vnd.wt.stf' => 'stf',\n    'application/vnd.xara' => 'xar',\n    'application/vnd.xfdl' => 'xfdl',\n    'application/vnd.yamaha.hv-dic' => 'hvd',\n    'application/vnd.yamaha.hv-script' => 'hvs',\n    'application/vnd.yamaha.hv-voice' => 'hvp',\n    'application/vnd.yamaha.openscoreformat' => 'osf',\n    'application/vnd.yamaha.openscoreformat.osfpvg+xml' => 'osfpvg',\n    'application/vnd.yamaha.smaf-audio' => 'saf',\n    'application/vnd.yamaha.smaf-phrase' => 'spf',\n    'application/vnd.yellowriver-custom-menu' => 'cmp',\n    'application/vnd.zul' => [\n        'zir',\n        'zirz',\n    ],\n    'application/vnd.zzazz.deck+xml' => 'zaz',\n    'application/voicexml+xml' => 'vxml',\n    'application/wasm' => 'wasm',\n    'application/widget' => 'wgt',\n    'application/winhlp' => 'hlp',\n    'application/wsdl+xml' => 'wsdl',\n    'application/wspolicy+xml' => 'wspolicy',\n    'application/x-7z-compressed' => '7z',\n    'application/x-abiword' => 'abw',\n    'application/x-ace-compressed' => 'ace',\n    'application/x-apple-diskimage' => 'dmg',\n    'application/x-authorware-bin' => [\n        'aab',\n        'x32',\n        'u32',\n        'vox',\n    ],\n    'application/x-authorware-map' => 'aam',\n    'application/x-authorware-seg' => 'aas',\n    'application/x-bcpio' => 'bcpio',\n    'application/x-bittorrent' => 'torrent',\n    'application/x-blorb' => [\n        'blb',\n        'blorb',\n    ],\n    'application/x-bzip' => 'bz',\n    'application/x-bzip2' => [\n        'bz2',\n        'boz',\n    ],\n    'application/x-cbr' => [\n        'cbr',\n        'cba',\n        'cbt',\n        'cbz',\n        'cb7',\n    ],\n    'application/x-cdlink' => 'vcd',\n    'application/x-cfs-compressed' => 'cfs',\n    'application/x-chat' => 'chat',\n    'application/x-chess-pgn' => 'pgn',\n    'application/x-conference' => 'nsc',\n    'application/x-cpio' => 'cpio',\n    'application/x-csh' => 'csh',\n    'application/x-debian-package' => [\n        'deb',\n        'udeb',\n    ],\n    'application/x-dgc-compressed' => 'dgc',\n    'application/x-director' => [\n        'dir',\n        'dcr',\n        'dxr',\n        'cst',\n        'cct',\n        'cxt',\n        'w3d',\n        'fgd',\n        'swa',\n    ],\n    'application/x-doom' => 'wad',\n    'application/x-dtbncx+xml' => 'ncx',\n    'application/x-dtbook+xml' => 'dtb',\n    'application/x-dtbresource+xml' => 'res',\n    'application/x-dvi' => 'dvi',\n    'application/x-envoy' => 'evy',\n    'application/x-eva' => 'eva',\n    'application/x-font-bdf' => 'bdf',\n    'application/x-font-ghostscript' => 'gsf',\n    'application/x-font-linux-psf' => 'psf',\n    'application/x-font-pcf' => 'pcf',\n    'application/x-font-snf' => 'snf',\n    'application/x-font-type1' => [\n        'pfa',\n        'pfb',\n        'pfm',\n        'afm',\n    ],\n    'application/x-freearc' => 'arc',\n    'application/x-futuresplash' => 'spl',\n    'application/x-gca-compressed' => 'gca',\n    'application/x-glulx' => 'ulx',\n    'application/x-gnumeric' => 'gnumeric',\n    'application/x-gramps-xml' => 'gramps',\n    'application/x-gtar' => 'gtar',\n    'application/x-hdf' => 'hdf',\n    'application/x-install-instructions' => 'install',\n    'application/x-iso9660-image' => 'iso',\n    'application/x-java-jnlp-file' => 'jnlp',\n    'application/x-latex' => 'latex',\n    'application/x-lzh-compressed' => [\n        'lzh',\n        'lha',\n    ],\n    'application/x-mie' => 'mie',\n    'application/x-mobipocket-ebook' => [\n        'prc',\n        'mobi',\n    ],\n    'application/x-ms-application' => 'application',\n    'application/x-ms-shortcut' => 'lnk',\n    'application/x-ms-wmd' => 'wmd',\n    'application/x-ms-wmz' => 'wmz',\n    'application/x-ms-xbap' => 'xbap',\n    'application/x-msaccess' => 'mdb',\n    'application/x-msbinder' => 'obd',\n    'application/x-mscardfile' => 'crd',\n    'application/x-msclip' => 'clp',\n    'application/x-msdownload' => [\n        'exe',\n        'dll',\n        'com',\n        'bat',\n        'msi',\n    ],\n    'application/x-msmediaview' => [\n        'mvb',\n        'm13',\n        'm14',\n    ],\n    'application/x-msmetafile' => [\n        'wmf',\n        'wmz',\n        'emf',\n        'emz',\n    ],\n    'application/x-msmoney' => 'mny',\n    'application/x-mspublisher' => 'pub',\n    'application/x-msschedule' => 'scd',\n    'application/x-msterminal' => 'trm',\n    'application/x-mswrite' => 'wri',\n    'application/x-netcdf' => [\n        'nc',\n        'cdf',\n    ],\n    'application/x-nzb' => 'nzb',\n    'application/x-pkcs12' => [\n        'p12',\n        'pfx',\n    ],\n    'application/x-pkcs7-certificates' => [\n        'p7b',\n        'spc',\n    ],\n    'application/x-pkcs7-certreqresp' => 'p7r',\n    'application/x-rar-compressed' => 'rar',\n    'application/x-research-info-systems' => 'ris',\n    'application/x-sh' => 'sh',\n    'application/x-shar' => 'shar',\n    'application/x-shockwave-flash' => 'swf',\n    'application/x-silverlight-app' => 'xap',\n    'application/x-sql' => 'sql',\n    'application/x-stuffit' => 'sit',\n    'application/x-stuffitx' => 'sitx',\n    'application/x-subrip' => 'srt',\n    'application/x-sv4cpio' => 'sv4cpio',\n    'application/x-sv4crc' => 'sv4crc',\n    'application/x-t3vm-image' => 't3',\n    'application/x-tads' => 'gam',\n    'application/x-tar' => 'tar',\n    'application/x-tcl' => 'tcl',\n    'application/x-tex' => 'tex',\n    'application/x-tex-tfm' => 'tfm',\n    'application/x-texinfo' => [\n        'texinfo',\n        'texi',\n    ],\n    'application/x-tgif' => 'obj',\n    'application/x-ustar' => 'ustar',\n    'application/x-wais-source' => 'src',\n    'application/x-x509-ca-cert' => [\n        'der',\n        'crt',\n    ],\n    'application/x-xfig' => 'fig',\n    'application/x-xliff+xml' => 'xlf',\n    'application/x-xpinstall' => 'xpi',\n    'application/x-xz' => 'xz',\n    'application/x-zmachine' => [\n        'z1',\n        'z2',\n        'z3',\n        'z4',\n        'z5',\n        'z6',\n        'z7',\n        'z8',\n    ],\n    'application/xaml+xml' => 'xaml',\n    'application/xcap-diff+xml' => 'xdf',\n    'application/xenc+xml' => 'xenc',\n    'application/xhtml+xml' => [\n        'xhtml',\n        'xht',\n    ],\n    'application/xml' => [\n        'xml',\n        'xsl',\n    ],\n    'application/xml-dtd' => 'dtd',\n    'application/xop+xml' => 'xop',\n    'application/xproc+xml' => 'xpl',\n    'application/xslt+xml' => 'xslt',\n    'application/xspf+xml' => 'xspf',\n    'application/xv+xml' => [\n        'mxml',\n        'xhvml',\n        'xvml',\n        'xvm',\n    ],\n    'application/yang' => 'yang',\n    'application/yin+xml' => 'yin',\n    'application/zip' => 'zip',\n    'audio/adpcm' => 'adp',\n    'audio/basic' => [\n        'au',\n        'snd',\n    ],\n    'audio/midi' => [\n        'mid',\n        'midi',\n        'kar',\n        'rmi',\n    ],\n    'audio/mp4' => [\n        'm4a',\n        'mp4a',\n    ],\n    'audio/mpeg' => [\n        'mpga',\n        'mp2',\n        'mp2a',\n        'mp3',\n        'm2a',\n        'm3a',\n    ],\n    'audio/ogg' => [\n        'oga',\n        'ogg',\n        'spx',\n        'opus',\n    ],\n    'audio/s3m' => 's3m',\n    'audio/silk' => 'sil',\n    'audio/vnd.dece.audio' => [\n        'uva',\n        'uvva',\n    ],\n    'audio/vnd.digital-winds' => 'eol',\n    'audio/vnd.dra' => 'dra',\n    'audio/vnd.dts' => 'dts',\n    'audio/vnd.dts.hd' => 'dtshd',\n    'audio/vnd.lucent.voice' => 'lvp',\n    'audio/vnd.ms-playready.media.pya' => 'pya',\n    'audio/vnd.nuera.ecelp4800' => 'ecelp4800',\n    'audio/vnd.nuera.ecelp7470' => 'ecelp7470',\n    'audio/vnd.nuera.ecelp9600' => 'ecelp9600',\n    'audio/vnd.rip' => 'rip',\n    'audio/webm' => 'weba',\n    'audio/x-aac' => 'aac',\n    'audio/x-aiff' => [\n        'aif',\n        'aiff',\n        'aifc',\n    ],\n    'audio/x-caf' => 'caf',\n    'audio/x-flac' => 'flac',\n    'audio/x-matroska' => 'mka',\n    'audio/x-mpegurl' => 'm3u',\n    'audio/x-ms-wax' => 'wax',\n    'audio/x-ms-wma' => 'wma',\n    'audio/x-pn-realaudio' => [\n        'ram',\n        'ra',\n    ],\n    'audio/x-pn-realaudio-plugin' => 'rmp',\n    'audio/x-wav' => 'wav',\n    'audio/xm' => 'xm',\n    'chemical/x-cdx' => 'cdx',\n    'chemical/x-cif' => 'cif',\n    'chemical/x-cmdf' => 'cmdf',\n    'chemical/x-cml' => 'cml',\n    'chemical/x-csml' => 'csml',\n    'chemical/x-xyz' => 'xyz',\n    'font/collection' => 'ttc',\n    'font/otf' => 'otf',\n    'font/ttf' => 'ttf',\n    'font/woff' => 'woff',\n    'font/woff2' => 'woff2',\n    'image/apng' => 'apng',\n    'image/avif' => [\n        'avif',\n        'avif',\n    ],\n    'image/bmp' => 'bmp',\n    'image/cgm' => 'cgm',\n    'image/g3fax' => 'g3',\n    'image/gif' => 'gif',\n    'image/heic' => 'heic',\n    'image/heic-sequence' => 'heics',\n    'image/heif' => 'heif',\n    'image/heif-sequence' => 'heifs',\n    'image/ief' => 'ief',\n    'image/jpeg' => [\n        'jpeg',\n        'jpg',\n        'jpe',\n        'jfif',\n        'pjp',\n        'pjpeg',\n    ],\n    'image/jxl' => 'jxl',\n    'image/ktx' => 'ktx',\n    'image/png' => 'png',\n    'image/prs.btif' => 'btif',\n    'image/sgi' => 'sgi',\n    'image/svg+xml' => [\n        'svg',\n        'svgz',\n    ],\n    'image/tiff' => [\n        'tiff',\n        'tif',\n    ],\n    'image/vnd.adobe.photoshop' => 'psd',\n    'image/vnd.dece.graphic' => [\n        'uvi',\n        'uvvi',\n        'uvg',\n        'uvvg',\n    ],\n    'image/vnd.djvu' => [\n        'djvu',\n        'djv',\n    ],\n    'image/vnd.dvb.subtitle' => 'sub',\n    'image/vnd.dwg' => 'dwg',\n    'image/vnd.dxf' => 'dxf',\n    'image/vnd.fastbidsheet' => 'fbs',\n    'image/vnd.fpx' => 'fpx',\n    'image/vnd.fst' => 'fst',\n    'image/vnd.fujixerox.edmics-mmr' => 'mmr',\n    'image/vnd.fujixerox.edmics-rlc' => 'rlc',\n    'image/vnd.ms-modi' => 'mdi',\n    'image/vnd.ms-photo' => 'wdp',\n    'image/vnd.net-fpx' => 'npx',\n    'image/vnd.wap.wbmp' => 'wbmp',\n    'image/vnd.xiff' => 'xif',\n    'image/webp' => 'webp',\n    'image/x-3ds' => '3ds',\n    'image/x-cmu-raster' => 'ras',\n    'image/x-cmx' => 'cmx',\n    'image/x-freehand' => [\n        'fh',\n        'fhc',\n        'fh4',\n        'fh5',\n        'fh7',\n    ],\n    'image/x-icon' => 'ico',\n    'image/x-mrsid-image' => 'sid',\n    'image/x-pcx' => 'pcx',\n    'image/x-pict' => [\n        'pic',\n        'pct',\n    ],\n    'image/x-portable-anymap' => 'pnm',\n    'image/x-portable-bitmap' => 'pbm',\n    'image/x-portable-graymap' => 'pgm',\n    'image/x-portable-pixmap' => 'ppm',\n    'image/x-rgb' => 'rgb',\n    'image/x-tga' => 'tga',\n    'image/x-xbitmap' => 'xbm',\n    'image/x-xpixmap' => 'xpm',\n    'image/x-xwindowdump' => 'xwd',\n    'message/rfc822' => [\n        'eml',\n        'mime',\n    ],\n    'model/iges' => [\n        'igs',\n        'iges',\n    ],\n    'model/mesh' => [\n        'msh',\n        'mesh',\n        'silo',\n    ],\n    'model/vnd.collada+xml' => 'dae',\n    'model/vnd.dwf' => 'dwf',\n    'model/vnd.gdl' => 'gdl',\n    'model/vnd.gtw' => 'gtw',\n    'model/vnd.vtu' => 'vtu',\n    'model/vrml' => [\n        'wrl',\n        'vrml',\n    ],\n    'model/x3d+binary' => [\n        'x3db',\n        'x3dbz',\n    ],\n    'model/x3d+vrml' => [\n        'x3dv',\n        'x3dvz',\n    ],\n    'model/x3d+xml' => [\n        'x3d',\n        'x3dz',\n    ],\n    'text/cache-manifest' => 'appcache',\n    'text/calendar' => [\n        'ics',\n        'ifb',\n    ],\n    'text/css' => 'css',\n    'text/csv' => 'csv',\n    'text/html' => [\n        'html',\n        'htm',\n    ],\n    'text/javascript' => [\n        'js',\n        'mjs',\n        'mjs',\n    ],\n    'text/n3' => 'n3',\n    'text/plain' => [\n        'txt',\n        'text',\n        'conf',\n        'def',\n        'list',\n        'log',\n        'in',\n    ],\n    'text/prs.lines.tag' => 'dsc',\n    'text/richtext' => 'rtx',\n    'text/sgml' => [\n        'sgml',\n        'sgm',\n    ],\n    'text/tab-separated-values' => 'tsv',\n    'text/troff' => [\n        't',\n        'tr',\n        'roff',\n        'man',\n        'me',\n        'ms',\n    ],\n    'text/turtle' => 'ttl',\n    'text/uri-list' => [\n        'uri',\n        'uris',\n        'urls',\n    ],\n    'text/vcard' => 'vcard',\n    'text/vnd.curl' => 'curl',\n    'text/vnd.curl.dcurl' => 'dcurl',\n    'text/vnd.curl.mcurl' => 'mcurl',\n    'text/vnd.curl.scurl' => 'scurl',\n    'text/vnd.dvb.subtitle' => 'sub',\n    'text/vnd.fly' => 'fly',\n    'text/vnd.fmi.flexstor' => 'flx',\n    'text/vnd.graphviz' => 'gv',\n    'text/vnd.in3d.3dml' => '3dml',\n    'text/vnd.in3d.spot' => 'spot',\n    'text/vnd.sun.j2me.app-descriptor' => 'jad',\n    'text/vnd.wap.wml' => 'wml',\n    'text/vnd.wap.wmlscript' => 'wmls',\n    'text/x-asm' => [\n        's',\n        'asm',\n    ],\n    'text/x-c' => [\n        'c',\n        'cc',\n        'cxx',\n        'cpp',\n        'h',\n        'hh',\n        'dic',\n    ],\n    'text/x-fortran' => [\n        'f',\n        'for',\n        'f77',\n        'f90',\n    ],\n    'text/x-java-source' => 'java',\n    'text/x-nfo' => 'nfo',\n    'text/x-opml' => 'opml',\n    'text/x-pascal' => [\n        'p',\n        'pas',\n    ],\n    'text/x-setext' => 'etx',\n    'text/x-sfv' => 'sfv',\n    'text/x-uuencode' => 'uu',\n    'text/x-vcalendar' => 'vcs',\n    'text/x-vcard' => 'vcf',\n    'video/3gpp' => '3gp',\n    'video/3gpp2' => '3g2',\n    'video/h261' => 'h261',\n    'video/h263' => 'h263',\n    'video/h264' => 'h264',\n    'video/jpeg' => 'jpgv',\n    'video/jpm' => [\n        'jpm',\n        'jpgm',\n    ],\n    'video/mj2' => [\n        'mj2',\n        'mjp2',\n    ],\n    'video/mp2t' => [\n        'ts',\n        'm2t',\n        'm2ts',\n        'mts',\n    ],\n    'video/mp4' => [\n        'mp4',\n        'mp4v',\n        'mpg4',\n    ],\n    'video/mpeg' => [\n        'mpeg',\n        'mpg',\n        'mpe',\n        'm1v',\n        'm2v',\n    ],\n    'video/ogg' => 'ogv',\n    'video/quicktime' => [\n        'qt',\n        'mov',\n    ],\n    'video/vnd.dece.hd' => [\n        'uvh',\n        'uvvh',\n    ],\n    'video/vnd.dece.mobile' => [\n        'uvm',\n        'uvvm',\n    ],\n    'video/vnd.dece.pd' => [\n        'uvp',\n        'uvvp',\n    ],\n    'video/vnd.dece.sd' => [\n        'uvs',\n        'uvvs',\n    ],\n    'video/vnd.dece.video' => [\n        'uvv',\n        'uvvv',\n    ],\n    'video/vnd.dvb.file' => 'dvb',\n    'video/vnd.fvt' => 'fvt',\n    'video/vnd.mpegurl' => [\n        'mxu',\n        'm4u',\n    ],\n    'video/vnd.ms-playready.media.pyv' => 'pyv',\n    'video/vnd.uvvu.mp4' => [\n        'uvu',\n        'uvvu',\n    ],\n    'video/vnd.vivo' => 'viv',\n    'video/webm' => 'webm',\n    'video/x-f4v' => 'f4v',\n    'video/x-fli' => 'fli',\n    'video/x-flv' => 'flv',\n    'video/x-m4v' => 'm4v',\n    'video/x-matroska' => [\n        'mkv',\n        'mk3d',\n        'mks',\n    ],\n    'video/x-mng' => 'mng',\n    'video/x-ms-asf' => [\n        'asf',\n        'asx',\n    ],\n    'video/x-ms-vob' => 'vob',\n    'video/x-ms-wm' => 'wm',\n    'video/x-ms-wmv' => 'wmv',\n    'video/x-ms-wmx' => 'wmx',\n    'video/x-ms-wvx' => 'wvx',\n    'video/x-msvideo' => 'avi',\n    'video/x-sgi-movie' => 'movie',\n    'video/x-smv' => 'smv',\n    'x-conference/x-cooltalk' => 'ice',\n];\n"
  },
  {
    "path": "framework/helpers/mimeTypes.php",
    "content": "<?php\n\n/**\n * MIME types.\n *\n * This file contains most commonly used MIME types\n * according to file extension names.\n * Its content is generated from the apache http mime.types file.\n * https://raw.githubusercontent.com/apache/httpd/refs/heads/trunk/docs/conf/mime.types\n * This file has been placed in the public domain for unlimited redistribution.\n *\n * All extra changes made to this file must be committed to /build/controllers/MimeTypeController.php\n * otherwise they will be lost on next build.\n */\n\n$mimeTypes = [\n    123 => 'application/vnd.lotus-1-2-3',\n    '3dml' => 'text/vnd.in3d.3dml',\n    '3ds' => 'image/x-3ds',\n    '3g2' => 'video/3gpp2',\n    '3gp' => 'video/3gpp',\n    '7z' => 'application/x-7z-compressed',\n    'aab' => 'application/x-authorware-bin',\n    'aac' => 'audio/x-aac',\n    'aam' => 'application/x-authorware-map',\n    'aas' => 'application/x-authorware-seg',\n    'abw' => 'application/x-abiword',\n    'ac' => 'application/pkix-attr-cert',\n    'acc' => 'application/vnd.americandynamics.acc',\n    'ace' => 'application/x-ace-compressed',\n    'acu' => 'application/vnd.acucobol',\n    'acutc' => 'application/vnd.acucorp',\n    'adp' => 'audio/adpcm',\n    'aep' => 'application/vnd.audiograph',\n    'afm' => 'application/x-font-type1',\n    'afp' => 'application/vnd.ibm.modcap',\n    'ahead' => 'application/vnd.ahead.space',\n    'ai' => 'application/postscript',\n    'aif' => 'audio/x-aiff',\n    'aifc' => 'audio/x-aiff',\n    'aiff' => 'audio/x-aiff',\n    'air' => 'application/vnd.adobe.air-application-installer-package+zip',\n    'ait' => 'application/vnd.dvb.ait',\n    'ami' => 'application/vnd.amiga.ami',\n    'apk' => 'application/vnd.android.package-archive',\n    'apng' => 'image/apng',\n    'appcache' => 'text/cache-manifest',\n    'application' => 'application/x-ms-application',\n    'apr' => 'application/vnd.lotus-approach',\n    'arc' => 'application/x-freearc',\n    'asc' => 'application/pgp-signature',\n    'asf' => 'video/x-ms-asf',\n    'asm' => 'text/x-asm',\n    'aso' => 'application/vnd.accpac.simply.aso',\n    'asx' => 'video/x-ms-asf',\n    'atc' => 'application/vnd.acucorp',\n    'atom' => 'application/atom+xml',\n    'atomcat' => 'application/atomcat+xml',\n    'atomsvc' => 'application/atomsvc+xml',\n    'atx' => 'application/vnd.antix.game-component',\n    'au' => 'audio/basic',\n    'avi' => 'video/x-msvideo',\n    'avif' => 'image/avif',\n    'aw' => 'application/applixware',\n    'azf' => 'application/vnd.airzip.filesecure.azf',\n    'azs' => 'application/vnd.airzip.filesecure.azs',\n    'azw' => 'application/vnd.amazon.ebook',\n    'bat' => 'application/x-msdownload',\n    'bcpio' => 'application/x-bcpio',\n    'bdf' => 'application/x-font-bdf',\n    'bdm' => 'application/vnd.syncml.dm+wbxml',\n    'bed' => 'application/vnd.realvnc.bed',\n    'bh2' => 'application/vnd.fujitsu.oasysprs',\n    'bin' => 'application/octet-stream',\n    'blb' => 'application/x-blorb',\n    'blorb' => 'application/x-blorb',\n    'bmi' => 'application/vnd.bmi',\n    'bmp' => 'image/bmp',\n    'book' => 'application/vnd.framemaker',\n    'box' => 'application/vnd.previewsystems.box',\n    'boz' => 'application/x-bzip2',\n    'bpk' => 'application/octet-stream',\n    'btif' => 'image/prs.btif',\n    'bz' => 'application/x-bzip',\n    'bz2' => 'application/x-bzip2',\n    'c' => 'text/x-c',\n    'c11amc' => 'application/vnd.cluetrust.cartomobile-config',\n    'c11amz' => 'application/vnd.cluetrust.cartomobile-config-pkg',\n    'c4d' => 'application/vnd.clonk.c4group',\n    'c4f' => 'application/vnd.clonk.c4group',\n    'c4g' => 'application/vnd.clonk.c4group',\n    'c4p' => 'application/vnd.clonk.c4group',\n    'c4u' => 'application/vnd.clonk.c4group',\n    'cab' => 'application/vnd.ms-cab-compressed',\n    'caf' => 'audio/x-caf',\n    'cap' => 'application/vnd.tcpdump.pcap',\n    'car' => 'application/vnd.curl.car',\n    'cat' => 'application/vnd.ms-pki.seccat',\n    'cb7' => 'application/x-cbr',\n    'cba' => 'application/x-cbr',\n    'cbr' => 'application/x-cbr',\n    'cbt' => 'application/x-cbr',\n    'cbz' => 'application/x-cbr',\n    'cc' => 'text/x-c',\n    'cct' => 'application/x-director',\n    'ccxml' => 'application/ccxml+xml',\n    'cdbcmsg' => 'application/vnd.contact.cmsg',\n    'cdf' => 'application/x-netcdf',\n    'cdkey' => 'application/vnd.mediastation.cdkey',\n    'cdmia' => 'application/cdmi-capability',\n    'cdmic' => 'application/cdmi-container',\n    'cdmid' => 'application/cdmi-domain',\n    'cdmio' => 'application/cdmi-object',\n    'cdmiq' => 'application/cdmi-queue',\n    'cdx' => 'chemical/x-cdx',\n    'cdxml' => 'application/vnd.chemdraw+xml',\n    'cdy' => 'application/vnd.cinderella',\n    'cer' => 'application/pkix-cert',\n    'cfs' => 'application/x-cfs-compressed',\n    'cgm' => 'image/cgm',\n    'chat' => 'application/x-chat',\n    'chm' => 'application/vnd.ms-htmlhelp',\n    'chrt' => 'application/vnd.kde.kchart',\n    'cif' => 'chemical/x-cif',\n    'cii' => 'application/vnd.anser-web-certificate-issue-initiation',\n    'cil' => 'application/vnd.ms-artgalry',\n    'cla' => 'application/vnd.claymore',\n    'class' => 'application/java-vm',\n    'clkk' => 'application/vnd.crick.clicker.keyboard',\n    'clkp' => 'application/vnd.crick.clicker.palette',\n    'clkt' => 'application/vnd.crick.clicker.template',\n    'clkw' => 'application/vnd.crick.clicker.wordbank',\n    'clkx' => 'application/vnd.crick.clicker',\n    'clp' => 'application/x-msclip',\n    'cmc' => 'application/vnd.cosmocaller',\n    'cmdf' => 'chemical/x-cmdf',\n    'cml' => 'chemical/x-cml',\n    'cmp' => 'application/vnd.yellowriver-custom-menu',\n    'cmx' => 'image/x-cmx',\n    'cod' => 'application/vnd.rim.cod',\n    'com' => 'application/x-msdownload',\n    'conf' => 'text/plain',\n    'cpio' => 'application/x-cpio',\n    'cpp' => 'text/x-c',\n    'cpt' => 'application/mac-compactpro',\n    'crd' => 'application/x-mscardfile',\n    'crl' => 'application/pkix-crl',\n    'crt' => 'application/x-x509-ca-cert',\n    'cryptonote' => 'application/vnd.rig.cryptonote',\n    'csh' => 'application/x-csh',\n    'csml' => 'chemical/x-csml',\n    'csp' => 'application/vnd.commonspace',\n    'css' => 'text/css',\n    'cst' => 'application/x-director',\n    'csv' => 'text/csv',\n    'cu' => 'application/cu-seeme',\n    'curl' => 'text/vnd.curl',\n    'cww' => 'application/prs.cww',\n    'cxt' => 'application/x-director',\n    'cxx' => 'text/x-c',\n    'dae' => 'model/vnd.collada+xml',\n    'daf' => 'application/vnd.mobius.daf',\n    'dart' => 'application/vnd.dart',\n    'dataless' => 'application/vnd.fdsn.seed',\n    'davmount' => 'application/davmount+xml',\n    'dbk' => 'application/docbook+xml',\n    'dcr' => 'application/x-director',\n    'dcurl' => 'text/vnd.curl.dcurl',\n    'dd2' => 'application/vnd.oma.dd2+xml',\n    'ddd' => 'application/vnd.fujixerox.ddd',\n    'deb' => 'application/x-debian-package',\n    'def' => 'text/plain',\n    'deploy' => 'application/octet-stream',\n    'der' => 'application/x-x509-ca-cert',\n    'dfac' => 'application/vnd.dreamfactory',\n    'dgc' => 'application/x-dgc-compressed',\n    'dic' => 'text/x-c',\n    'dir' => 'application/x-director',\n    'dis' => 'application/vnd.mobius.dis',\n    'dist' => 'application/octet-stream',\n    'distz' => 'application/octet-stream',\n    'djv' => 'image/vnd.djvu',\n    'djvu' => 'image/vnd.djvu',\n    'dll' => 'application/x-msdownload',\n    'dmg' => 'application/x-apple-diskimage',\n    'dmp' => 'application/vnd.tcpdump.pcap',\n    'dms' => 'application/octet-stream',\n    'dna' => 'application/vnd.dna',\n    'doc' => 'application/msword',\n    'docm' => 'application/vnd.ms-word.document.macroenabled.12',\n    'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n    'dot' => 'application/msword',\n    'dotm' => 'application/vnd.ms-word.template.macroenabled.12',\n    'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',\n    'dp' => 'application/vnd.osgi.dp',\n    'dpg' => 'application/vnd.dpgraph',\n    'dra' => 'audio/vnd.dra',\n    'dsc' => 'text/prs.lines.tag',\n    'dssc' => 'application/dssc+der',\n    'dtb' => 'application/x-dtbook+xml',\n    'dtd' => 'application/xml-dtd',\n    'dts' => 'audio/vnd.dts',\n    'dtshd' => 'audio/vnd.dts.hd',\n    'dump' => 'application/octet-stream',\n    'dvb' => 'video/vnd.dvb.file',\n    'dvi' => 'application/x-dvi',\n    'dwf' => 'model/vnd.dwf',\n    'dwg' => 'image/vnd.dwg',\n    'dxf' => 'image/vnd.dxf',\n    'dxp' => 'application/vnd.spotfire.dxp',\n    'dxr' => 'application/x-director',\n    'ecelp4800' => 'audio/vnd.nuera.ecelp4800',\n    'ecelp7470' => 'audio/vnd.nuera.ecelp7470',\n    'ecelp9600' => 'audio/vnd.nuera.ecelp9600',\n    'ecma' => 'application/ecmascript',\n    'edm' => 'application/vnd.novadigm.edm',\n    'edx' => 'application/vnd.novadigm.edx',\n    'efif' => 'application/vnd.picsel',\n    'ei6' => 'application/vnd.pg.osasli',\n    'elc' => 'application/octet-stream',\n    'emf' => 'application/x-msmetafile',\n    'eml' => 'message/rfc822',\n    'emma' => 'application/emma+xml',\n    'emz' => 'application/x-msmetafile',\n    'eol' => 'audio/vnd.digital-winds',\n    'eot' => 'application/vnd.ms-fontobject',\n    'eps' => 'application/postscript',\n    'epub' => 'application/epub+zip',\n    'es3' => 'application/vnd.eszigno3+xml',\n    'esa' => 'application/vnd.osgi.subsystem',\n    'esf' => 'application/vnd.epson.esf',\n    'et3' => 'application/vnd.eszigno3+xml',\n    'etx' => 'text/x-setext',\n    'eva' => 'application/x-eva',\n    'evy' => 'application/x-envoy',\n    'exe' => 'application/x-msdownload',\n    'exi' => 'application/exi',\n    'ext' => 'application/vnd.novadigm.ext',\n    'ez' => 'application/andrew-inset',\n    'ez2' => 'application/vnd.ezpix-album',\n    'ez3' => 'application/vnd.ezpix-package',\n    'f' => 'text/x-fortran',\n    'f4v' => 'video/x-f4v',\n    'f77' => 'text/x-fortran',\n    'f90' => 'text/x-fortran',\n    'fbs' => 'image/vnd.fastbidsheet',\n    'fcdt' => 'application/vnd.adobe.formscentral.fcdt',\n    'fcs' => 'application/vnd.isac.fcs',\n    'fdf' => 'application/vnd.fdf',\n    'fe_launch' => 'application/vnd.denovo.fcselayout-link',\n    'fg5' => 'application/vnd.fujitsu.oasysgp',\n    'fgd' => 'application/x-director',\n    'fh' => 'image/x-freehand',\n    'fh4' => 'image/x-freehand',\n    'fh5' => 'image/x-freehand',\n    'fh7' => 'image/x-freehand',\n    'fhc' => 'image/x-freehand',\n    'fig' => 'application/x-xfig',\n    'flac' => 'audio/x-flac',\n    'fli' => 'video/x-fli',\n    'flo' => 'application/vnd.micrografx.flo',\n    'flv' => 'video/x-flv',\n    'flw' => 'application/vnd.kde.kivio',\n    'flx' => 'text/vnd.fmi.flexstor',\n    'fly' => 'text/vnd.fly',\n    'fm' => 'application/vnd.framemaker',\n    'fnc' => 'application/vnd.frogans.fnc',\n    'for' => 'text/x-fortran',\n    'fpx' => 'image/vnd.fpx',\n    'frame' => 'application/vnd.framemaker',\n    'fsc' => 'application/vnd.fsc.weblaunch',\n    'fst' => 'image/vnd.fst',\n    'ftc' => 'application/vnd.fluxtime.clip',\n    'fti' => 'application/vnd.anser-web-funds-transfer-initiation',\n    'fvt' => 'video/vnd.fvt',\n    'fxp' => 'application/vnd.adobe.fxp',\n    'fxpl' => 'application/vnd.adobe.fxp',\n    'fzs' => 'application/vnd.fuzzysheet',\n    'g2w' => 'application/vnd.geoplan',\n    'g3' => 'image/g3fax',\n    'g3w' => 'application/vnd.geospace',\n    'gac' => 'application/vnd.groove-account',\n    'gam' => 'application/x-tads',\n    'gbr' => 'application/rpki-ghostbusters',\n    'gca' => 'application/x-gca-compressed',\n    'gdl' => 'model/vnd.gdl',\n    'geo' => 'application/vnd.dynageo',\n    'gex' => 'application/vnd.geometry-explorer',\n    'ggb' => 'application/vnd.geogebra.file',\n    'ggs' => 'application/vnd.geogebra.slides',\n    'ggt' => 'application/vnd.geogebra.tool',\n    'ghf' => 'application/vnd.groove-help',\n    'gif' => 'image/gif',\n    'gim' => 'application/vnd.groove-identity-message',\n    'gml' => 'application/gml+xml',\n    'gmx' => 'application/vnd.gmx',\n    'gnumeric' => 'application/x-gnumeric',\n    'gph' => 'application/vnd.flographit',\n    'gpx' => 'application/gpx+xml',\n    'gqf' => 'application/vnd.grafeq',\n    'gqs' => 'application/vnd.grafeq',\n    'gram' => 'application/srgs',\n    'gramps' => 'application/x-gramps-xml',\n    'gre' => 'application/vnd.geometry-explorer',\n    'grv' => 'application/vnd.groove-injector',\n    'grxml' => 'application/srgs+xml',\n    'gsf' => 'application/x-font-ghostscript',\n    'gtar' => 'application/x-gtar',\n    'gtm' => 'application/vnd.groove-tool-message',\n    'gtw' => 'model/vnd.gtw',\n    'gv' => 'text/vnd.graphviz',\n    'gxf' => 'application/gxf',\n    'gxt' => 'application/vnd.geonext',\n    'h' => 'text/x-c',\n    'h261' => 'video/h261',\n    'h263' => 'video/h263',\n    'h264' => 'video/h264',\n    'hal' => 'application/vnd.hal+xml',\n    'hbci' => 'application/vnd.hbci',\n    'hdf' => 'application/x-hdf',\n    'heic' => 'image/heic',\n    'heics' => 'image/heic-sequence',\n    'heif' => 'image/heif',\n    'heifs' => 'image/heif-sequence',\n    'hh' => 'text/x-c',\n    'hlp' => 'application/winhlp',\n    'hpgl' => 'application/vnd.hp-hpgl',\n    'hpid' => 'application/vnd.hp-hpid',\n    'hps' => 'application/vnd.hp-hps',\n    'hqx' => 'application/mac-binhex40',\n    'htke' => 'application/vnd.kenameaapp',\n    'htm' => 'text/html',\n    'html' => 'text/html',\n    'hvd' => 'application/vnd.yamaha.hv-dic',\n    'hvp' => 'application/vnd.yamaha.hv-voice',\n    'hvs' => 'application/vnd.yamaha.hv-script',\n    'i2g' => 'application/vnd.intergeo',\n    'icc' => 'application/vnd.iccprofile',\n    'ice' => 'x-conference/x-cooltalk',\n    'icm' => 'application/vnd.iccprofile',\n    'ico' => 'image/x-icon',\n    'ics' => 'text/calendar',\n    'ief' => 'image/ief',\n    'ifb' => 'text/calendar',\n    'ifm' => 'application/vnd.shana.informed.formdata',\n    'iges' => 'model/iges',\n    'igl' => 'application/vnd.igloader',\n    'igm' => 'application/vnd.insors.igm',\n    'igs' => 'model/iges',\n    'igx' => 'application/vnd.micrografx.igx',\n    'iif' => 'application/vnd.shana.informed.interchange',\n    'imp' => 'application/vnd.accpac.simply.imp',\n    'ims' => 'application/vnd.ms-ims',\n    'in' => 'text/plain',\n    'ink' => 'application/inkml+xml',\n    'inkml' => 'application/inkml+xml',\n    'install' => 'application/x-install-instructions',\n    'iota' => 'application/vnd.astraea-software.iota',\n    'ipfix' => 'application/ipfix',\n    'ipk' => 'application/vnd.shana.informed.package',\n    'irm' => 'application/vnd.ibm.rights-management',\n    'irp' => 'application/vnd.irepository.package+xml',\n    'iso' => 'application/x-iso9660-image',\n    'itp' => 'application/vnd.shana.informed.formtemplate',\n    'ivp' => 'application/vnd.immervision-ivp',\n    'ivu' => 'application/vnd.immervision-ivu',\n    'jad' => 'text/vnd.sun.j2me.app-descriptor',\n    'jam' => 'application/vnd.jam',\n    'jar' => 'application/java-archive',\n    'java' => 'text/x-java-source',\n    'jfif' => 'image/jpeg',\n    'jisp' => 'application/vnd.jisp',\n    'jlt' => 'application/vnd.hp-jlyt',\n    'jnlp' => 'application/x-java-jnlp-file',\n    'joda' => 'application/vnd.joost.joda-archive',\n    'jpe' => 'image/jpeg',\n    'jpeg' => 'image/jpeg',\n    'jpg' => 'image/jpeg',\n    'jpgm' => 'video/jpm',\n    'jpgv' => 'video/jpeg',\n    'jpm' => 'video/jpm',\n    'js' => 'text/javascript',\n    'json' => 'application/json',\n    'jsonml' => 'application/jsonml+json',\n    'jxl' => 'image/jxl',\n    'kar' => 'audio/midi',\n    'karbon' => 'application/vnd.kde.karbon',\n    'kfo' => 'application/vnd.kde.kformula',\n    'kia' => 'application/vnd.kidspiration',\n    'kml' => 'application/vnd.google-earth.kml+xml',\n    'kmz' => 'application/vnd.google-earth.kmz',\n    'kne' => 'application/vnd.kinar',\n    'knp' => 'application/vnd.kinar',\n    'kon' => 'application/vnd.kde.kontour',\n    'kpr' => 'application/vnd.kde.kpresenter',\n    'kpt' => 'application/vnd.kde.kpresenter',\n    'kpxx' => 'application/vnd.ds-keypoint',\n    'ksp' => 'application/vnd.kde.kspread',\n    'ktr' => 'application/vnd.kahootz',\n    'ktx' => 'image/ktx',\n    'ktz' => 'application/vnd.kahootz',\n    'kwd' => 'application/vnd.kde.kword',\n    'kwt' => 'application/vnd.kde.kword',\n    'lasxml' => 'application/vnd.las.las+xml',\n    'latex' => 'application/x-latex',\n    'lbd' => 'application/vnd.llamagraphics.life-balance.desktop',\n    'lbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml',\n    'les' => 'application/vnd.hhe.lesson-player',\n    'lha' => 'application/x-lzh-compressed',\n    'link66' => 'application/vnd.route66.link66+xml',\n    'list' => 'text/plain',\n    'list3820' => 'application/vnd.ibm.modcap',\n    'listafp' => 'application/vnd.ibm.modcap',\n    'lnk' => 'application/x-ms-shortcut',\n    'log' => 'text/plain',\n    'lostxml' => 'application/lost+xml',\n    'lrf' => 'application/octet-stream',\n    'lrm' => 'application/vnd.ms-lrm',\n    'ltf' => 'application/vnd.frogans.ltf',\n    'lvp' => 'audio/vnd.lucent.voice',\n    'lwp' => 'application/vnd.lotus-wordpro',\n    'lzh' => 'application/x-lzh-compressed',\n    'm13' => 'application/x-msmediaview',\n    'm14' => 'application/x-msmediaview',\n    'm1v' => 'video/mpeg',\n    'm21' => 'application/mp21',\n    'm2a' => 'audio/mpeg',\n    'm2t' => 'video/mp2t',\n    'm2ts' => 'video/mp2t',\n    'm2v' => 'video/mpeg',\n    'm3a' => 'audio/mpeg',\n    'm3u' => 'audio/x-mpegurl',\n    'm3u8' => 'application/vnd.apple.mpegurl',\n    'm4a' => 'audio/mp4',\n    'm4u' => 'video/vnd.mpegurl',\n    'm4v' => 'video/x-m4v',\n    'ma' => 'application/mathematica',\n    'mads' => 'application/mads+xml',\n    'mag' => 'application/vnd.ecowin.chart',\n    'maker' => 'application/vnd.framemaker',\n    'man' => 'text/troff',\n    'mar' => 'application/octet-stream',\n    'mathml' => 'application/mathml+xml',\n    'mb' => 'application/mathematica',\n    'mbk' => 'application/vnd.mobius.mbk',\n    'mbox' => 'application/mbox',\n    'mc1' => 'application/vnd.medcalcdata',\n    'mcd' => 'application/vnd.mcd',\n    'mcurl' => 'text/vnd.curl.mcurl',\n    'mdb' => 'application/x-msaccess',\n    'mdi' => 'image/vnd.ms-modi',\n    'me' => 'text/troff',\n    'mesh' => 'model/mesh',\n    'meta4' => 'application/metalink4+xml',\n    'metalink' => 'application/metalink+xml',\n    'mets' => 'application/mets+xml',\n    'mfm' => 'application/vnd.mfmp',\n    'mft' => 'application/rpki-manifest',\n    'mgp' => 'application/vnd.osgeo.mapguide.package',\n    'mgz' => 'application/vnd.proteus.magazine',\n    'mid' => 'audio/midi',\n    'midi' => 'audio/midi',\n    'mie' => 'application/x-mie',\n    'mif' => 'application/vnd.mif',\n    'mime' => 'message/rfc822',\n    'mj2' => 'video/mj2',\n    'mjp2' => 'video/mj2',\n    'mjs' => 'text/javascript',\n    'mk3d' => 'video/x-matroska',\n    'mka' => 'audio/x-matroska',\n    'mks' => 'video/x-matroska',\n    'mkv' => 'video/x-matroska',\n    'mlp' => 'application/vnd.dolby.mlp',\n    'mmd' => 'application/vnd.chipnuts.karaoke-mmd',\n    'mmf' => 'application/vnd.smaf',\n    'mmr' => 'image/vnd.fujixerox.edmics-mmr',\n    'mng' => 'video/x-mng',\n    'mny' => 'application/x-msmoney',\n    'mobi' => 'application/x-mobipocket-ebook',\n    'mods' => 'application/mods+xml',\n    'mov' => 'video/quicktime',\n    'movie' => 'video/x-sgi-movie',\n    'mp2' => 'audio/mpeg',\n    'mp21' => 'application/mp21',\n    'mp2a' => 'audio/mpeg',\n    'mp3' => 'audio/mpeg',\n    'mp4' => 'video/mp4',\n    'mp4a' => 'audio/mp4',\n    'mp4s' => 'application/mp4',\n    'mp4v' => 'video/mp4',\n    'mpc' => 'application/vnd.mophun.certificate',\n    'mpe' => 'video/mpeg',\n    'mpeg' => 'video/mpeg',\n    'mpg' => 'video/mpeg',\n    'mpg4' => 'video/mp4',\n    'mpga' => 'audio/mpeg',\n    'mpkg' => 'application/vnd.apple.installer+xml',\n    'mpm' => 'application/vnd.blueice.multipass',\n    'mpn' => 'application/vnd.mophun.application',\n    'mpp' => 'application/vnd.ms-project',\n    'mpt' => 'application/vnd.ms-project',\n    'mpy' => 'application/vnd.ibm.minipay',\n    'mqy' => 'application/vnd.mobius.mqy',\n    'mrc' => 'application/marc',\n    'mrcx' => 'application/marcxml+xml',\n    'ms' => 'text/troff',\n    'mscml' => 'application/mediaservercontrol+xml',\n    'mseed' => 'application/vnd.fdsn.mseed',\n    'mseq' => 'application/vnd.mseq',\n    'msf' => 'application/vnd.epson.msf',\n    'msh' => 'model/mesh',\n    'msi' => 'application/x-msdownload',\n    'msl' => 'application/vnd.mobius.msl',\n    'msty' => 'application/vnd.muvee.style',\n    'mts' => 'video/mp2t',\n    'mus' => 'application/vnd.musician',\n    'musicxml' => 'application/vnd.recordare.musicxml+xml',\n    'mvb' => 'application/x-msmediaview',\n    'mwf' => 'application/vnd.mfer',\n    'mxf' => 'application/mxf',\n    'mxl' => 'application/vnd.recordare.musicxml',\n    'mxml' => 'application/xv+xml',\n    'mxs' => 'application/vnd.triscape.mxs',\n    'mxu' => 'video/vnd.mpegurl',\n    'n-gage' => 'application/vnd.nokia.n-gage.symbian.install',\n    'n3' => 'text/n3',\n    'nb' => 'application/mathematica',\n    'nbp' => 'application/vnd.wolfram.player',\n    'nc' => 'application/x-netcdf',\n    'ncx' => 'application/x-dtbncx+xml',\n    'nfo' => 'text/x-nfo',\n    'ngdat' => 'application/vnd.nokia.n-gage.data',\n    'nitf' => 'application/vnd.nitf',\n    'nlu' => 'application/vnd.neurolanguage.nlu',\n    'nml' => 'application/vnd.enliven',\n    'nnd' => 'application/vnd.noblenet-directory',\n    'nns' => 'application/vnd.noblenet-sealer',\n    'nnw' => 'application/vnd.noblenet-web',\n    'npx' => 'image/vnd.net-fpx',\n    'nsc' => 'application/x-conference',\n    'nsf' => 'application/vnd.lotus-notes',\n    'ntf' => 'application/vnd.nitf',\n    'nzb' => 'application/x-nzb',\n    'oa2' => 'application/vnd.fujitsu.oasys2',\n    'oa3' => 'application/vnd.fujitsu.oasys3',\n    'oas' => 'application/vnd.fujitsu.oasys',\n    'obd' => 'application/x-msbinder',\n    'obj' => 'application/x-tgif',\n    'oda' => 'application/oda',\n    'odb' => 'application/vnd.oasis.opendocument.database',\n    'odc' => 'application/vnd.oasis.opendocument.chart',\n    'odf' => 'application/vnd.oasis.opendocument.formula',\n    'odft' => 'application/vnd.oasis.opendocument.formula-template',\n    'odg' => 'application/vnd.oasis.opendocument.graphics',\n    'odi' => 'application/vnd.oasis.opendocument.image',\n    'odm' => 'application/vnd.oasis.opendocument.text-master',\n    'odp' => 'application/vnd.oasis.opendocument.presentation',\n    'ods' => 'application/vnd.oasis.opendocument.spreadsheet',\n    'odt' => 'application/vnd.oasis.opendocument.text',\n    'oga' => 'audio/ogg',\n    'ogg' => 'audio/ogg',\n    'ogv' => 'video/ogg',\n    'ogx' => 'application/ogg',\n    'omdoc' => 'application/omdoc+xml',\n    'onepkg' => 'application/onenote',\n    'onetmp' => 'application/onenote',\n    'onetoc' => 'application/onenote',\n    'onetoc2' => 'application/onenote',\n    'opf' => 'application/oebps-package+xml',\n    'opml' => 'text/x-opml',\n    'oprc' => 'application/vnd.palm',\n    'opus' => 'audio/ogg',\n    'org' => 'application/vnd.lotus-organizer',\n    'osf' => 'application/vnd.yamaha.openscoreformat',\n    'osfpvg' => 'application/vnd.yamaha.openscoreformat.osfpvg+xml',\n    'otc' => 'application/vnd.oasis.opendocument.chart-template',\n    'otf' => 'font/otf',\n    'otg' => 'application/vnd.oasis.opendocument.graphics-template',\n    'oth' => 'application/vnd.oasis.opendocument.text-web',\n    'oti' => 'application/vnd.oasis.opendocument.image-template',\n    'otp' => 'application/vnd.oasis.opendocument.presentation-template',\n    'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template',\n    'ott' => 'application/vnd.oasis.opendocument.text-template',\n    'oxps' => 'application/oxps',\n    'oxt' => 'application/vnd.openofficeorg.extension',\n    'p' => 'text/x-pascal',\n    'p10' => 'application/pkcs10',\n    'p12' => 'application/x-pkcs12',\n    'p7b' => 'application/x-pkcs7-certificates',\n    'p7c' => 'application/pkcs7-mime',\n    'p7m' => 'application/pkcs7-mime',\n    'p7r' => 'application/x-pkcs7-certreqresp',\n    'p7s' => 'application/pkcs7-signature',\n    'p8' => 'application/pkcs8',\n    'pas' => 'text/x-pascal',\n    'paw' => 'application/vnd.pawaafile',\n    'pbd' => 'application/vnd.powerbuilder6',\n    'pbm' => 'image/x-portable-bitmap',\n    'pcap' => 'application/vnd.tcpdump.pcap',\n    'pcf' => 'application/x-font-pcf',\n    'pcl' => 'application/vnd.hp-pcl',\n    'pclxl' => 'application/vnd.hp-pclxl',\n    'pct' => 'image/x-pict',\n    'pcurl' => 'application/vnd.curl.pcurl',\n    'pcx' => 'image/x-pcx',\n    'pdb' => 'application/vnd.palm',\n    'pdf' => 'application/pdf',\n    'pfa' => 'application/x-font-type1',\n    'pfb' => 'application/x-font-type1',\n    'pfm' => 'application/x-font-type1',\n    'pfr' => 'application/font-tdpfr',\n    'pfx' => 'application/x-pkcs12',\n    'pgm' => 'image/x-portable-graymap',\n    'pgn' => 'application/x-chess-pgn',\n    'pgp' => 'application/pgp-encrypted',\n    'pic' => 'image/x-pict',\n    'pjp' => 'image/jpeg',\n    'pjpeg' => 'image/jpeg',\n    'pkg' => 'application/octet-stream',\n    'pki' => 'application/pkixcmp',\n    'pkipath' => 'application/pkix-pkipath',\n    'plb' => 'application/vnd.3gpp.pic-bw-large',\n    'plc' => 'application/vnd.mobius.plc',\n    'plf' => 'application/vnd.pocketlearn',\n    'pls' => 'application/pls+xml',\n    'pml' => 'application/vnd.ctc-posml',\n    'png' => 'image/png',\n    'pnm' => 'image/x-portable-anymap',\n    'portpkg' => 'application/vnd.macports.portpkg',\n    'pot' => 'application/vnd.ms-powerpoint',\n    'potm' => 'application/vnd.ms-powerpoint.template.macroenabled.12',\n    'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',\n    'ppam' => 'application/vnd.ms-powerpoint.addin.macroenabled.12',\n    'ppd' => 'application/vnd.cups-ppd',\n    'ppm' => 'image/x-portable-pixmap',\n    'pps' => 'application/vnd.ms-powerpoint',\n    'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroenabled.12',\n    'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',\n    'ppt' => 'application/vnd.ms-powerpoint',\n    'pptm' => 'application/vnd.ms-powerpoint.presentation.macroenabled.12',\n    'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n    'pqa' => 'application/vnd.palm',\n    'prc' => 'application/x-mobipocket-ebook',\n    'pre' => 'application/vnd.lotus-freelance',\n    'prf' => 'application/pics-rules',\n    'ps' => 'application/postscript',\n    'psb' => 'application/vnd.3gpp.pic-bw-small',\n    'psd' => 'image/vnd.adobe.photoshop',\n    'psf' => 'application/x-font-linux-psf',\n    'pskcxml' => 'application/pskc+xml',\n    'ptid' => 'application/vnd.pvi.ptid1',\n    'pub' => 'application/x-mspublisher',\n    'pvb' => 'application/vnd.3gpp.pic-bw-var',\n    'pwn' => 'application/vnd.3m.post-it-notes',\n    'pya' => 'audio/vnd.ms-playready.media.pya',\n    'pyv' => 'video/vnd.ms-playready.media.pyv',\n    'qam' => 'application/vnd.epson.quickanime',\n    'qbo' => 'application/vnd.intu.qbo',\n    'qfx' => 'application/vnd.intu.qfx',\n    'qps' => 'application/vnd.publishare-delta-tree',\n    'qt' => 'video/quicktime',\n    'qwd' => 'application/vnd.quark.quarkxpress',\n    'qwt' => 'application/vnd.quark.quarkxpress',\n    'qxb' => 'application/vnd.quark.quarkxpress',\n    'qxd' => 'application/vnd.quark.quarkxpress',\n    'qxl' => 'application/vnd.quark.quarkxpress',\n    'qxt' => 'application/vnd.quark.quarkxpress',\n    'ra' => 'audio/x-pn-realaudio',\n    'ram' => 'audio/x-pn-realaudio',\n    'rar' => 'application/x-rar-compressed',\n    'ras' => 'image/x-cmu-raster',\n    'rcprofile' => 'application/vnd.ipunplugged.rcprofile',\n    'rdf' => 'application/rdf+xml',\n    'rdz' => 'application/vnd.data-vision.rdz',\n    'rep' => 'application/vnd.businessobjects',\n    'res' => 'application/x-dtbresource+xml',\n    'rgb' => 'image/x-rgb',\n    'rif' => 'application/reginfo+xml',\n    'rip' => 'audio/vnd.rip',\n    'ris' => 'application/x-research-info-systems',\n    'rl' => 'application/resource-lists+xml',\n    'rlc' => 'image/vnd.fujixerox.edmics-rlc',\n    'rld' => 'application/resource-lists-diff+xml',\n    'rm' => 'application/vnd.rn-realmedia',\n    'rmi' => 'audio/midi',\n    'rmp' => 'audio/x-pn-realaudio-plugin',\n    'rms' => 'application/vnd.jcp.javame.midlet-rms',\n    'rmvb' => 'application/vnd.rn-realmedia-vbr',\n    'rnc' => 'application/relax-ng-compact-syntax',\n    'roa' => 'application/rpki-roa',\n    'roff' => 'text/troff',\n    'rp9' => 'application/vnd.cloanto.rp9',\n    'rpss' => 'application/vnd.nokia.radio-presets',\n    'rpst' => 'application/vnd.nokia.radio-preset',\n    'rq' => 'application/sparql-query',\n    'rs' => 'application/rls-services+xml',\n    'rsd' => 'application/rsd+xml',\n    'rss' => 'application/rss+xml',\n    'rtf' => 'application/rtf',\n    'rtx' => 'text/richtext',\n    's' => 'text/x-asm',\n    's3m' => 'audio/s3m',\n    'saf' => 'application/vnd.yamaha.smaf-audio',\n    'sbml' => 'application/sbml+xml',\n    'sc' => 'application/vnd.ibm.secure-container',\n    'scd' => 'application/x-msschedule',\n    'scm' => 'application/vnd.lotus-screencam',\n    'scq' => 'application/scvp-cv-request',\n    'scs' => 'application/scvp-cv-response',\n    'scurl' => 'text/vnd.curl.scurl',\n    'sda' => 'application/vnd.stardivision.draw',\n    'sdc' => 'application/vnd.stardivision.calc',\n    'sdd' => 'application/vnd.stardivision.impress',\n    'sdkd' => 'application/vnd.solent.sdkm+xml',\n    'sdkm' => 'application/vnd.solent.sdkm+xml',\n    'sdp' => 'application/sdp',\n    'sdw' => 'application/vnd.stardivision.writer',\n    'see' => 'application/vnd.seemail',\n    'seed' => 'application/vnd.fdsn.seed',\n    'sema' => 'application/vnd.sema',\n    'semd' => 'application/vnd.semd',\n    'semf' => 'application/vnd.semf',\n    'ser' => 'application/java-serialized-object',\n    'setpay' => 'application/set-payment-initiation',\n    'setreg' => 'application/set-registration-initiation',\n    'sfd-hdstx' => 'application/vnd.hydrostatix.sof-data',\n    'sfs' => 'application/vnd.spotfire.sfs',\n    'sfv' => 'text/x-sfv',\n    'sgi' => 'image/sgi',\n    'sgl' => 'application/vnd.stardivision.writer-global',\n    'sgm' => 'text/sgml',\n    'sgml' => 'text/sgml',\n    'sh' => 'application/x-sh',\n    'shar' => 'application/x-shar',\n    'shf' => 'application/shf+xml',\n    'sid' => 'image/x-mrsid-image',\n    'sig' => 'application/pgp-signature',\n    'sil' => 'audio/silk',\n    'silo' => 'model/mesh',\n    'sis' => 'application/vnd.symbian.install',\n    'sisx' => 'application/vnd.symbian.install',\n    'sit' => 'application/x-stuffit',\n    'sitx' => 'application/x-stuffitx',\n    'skd' => 'application/vnd.koan',\n    'skm' => 'application/vnd.koan',\n    'skp' => 'application/vnd.koan',\n    'skt' => 'application/vnd.koan',\n    'sldm' => 'application/vnd.ms-powerpoint.slide.macroenabled.12',\n    'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',\n    'slt' => 'application/vnd.epson.salt',\n    'sm' => 'application/vnd.stepmania.stepchart',\n    'smf' => 'application/vnd.stardivision.math',\n    'smi' => 'application/smil+xml',\n    'smil' => 'application/smil+xml',\n    'smv' => 'video/x-smv',\n    'smzip' => 'application/vnd.stepmania.package',\n    'snd' => 'audio/basic',\n    'snf' => 'application/x-font-snf',\n    'so' => 'application/octet-stream',\n    'spc' => 'application/x-pkcs7-certificates',\n    'spf' => 'application/vnd.yamaha.smaf-phrase',\n    'spl' => 'application/x-futuresplash',\n    'spot' => 'text/vnd.in3d.spot',\n    'spp' => 'application/scvp-vp-response',\n    'spq' => 'application/scvp-vp-request',\n    'spx' => 'audio/ogg',\n    'sql' => 'application/x-sql',\n    'sqlite' => 'application/vnd.sqlite3',\n    'sqlite3' => 'application/vnd.sqlite3',\n    'src' => 'application/x-wais-source',\n    'srt' => 'application/x-subrip',\n    'sru' => 'application/sru+xml',\n    'srx' => 'application/sparql-results+xml',\n    'ssdl' => 'application/ssdl+xml',\n    'sse' => 'application/vnd.kodak-descriptor',\n    'ssf' => 'application/vnd.epson.ssf',\n    'ssml' => 'application/ssml+xml',\n    'st' => 'application/vnd.sailingtracker.track',\n    'stc' => 'application/vnd.sun.xml.calc.template',\n    'std' => 'application/vnd.sun.xml.draw.template',\n    'stf' => 'application/vnd.wt.stf',\n    'sti' => 'application/vnd.sun.xml.impress.template',\n    'stk' => 'application/hyperstudio',\n    'stl' => 'application/vnd.ms-pki.stl',\n    'str' => 'application/vnd.pg.format',\n    'stw' => 'application/vnd.sun.xml.writer.template',\n    'sub' => 'text/vnd.dvb.subtitle',\n    'sus' => 'application/vnd.sus-calendar',\n    'susp' => 'application/vnd.sus-calendar',\n    'sv4cpio' => 'application/x-sv4cpio',\n    'sv4crc' => 'application/x-sv4crc',\n    'svc' => 'application/vnd.dvb.service',\n    'svd' => 'application/vnd.svd',\n    'svg' => 'image/svg+xml',\n    'svgz' => 'image/svg+xml',\n    'swa' => 'application/x-director',\n    'swf' => 'application/x-shockwave-flash',\n    'swi' => 'application/vnd.aristanetworks.swi',\n    'sxc' => 'application/vnd.sun.xml.calc',\n    'sxd' => 'application/vnd.sun.xml.draw',\n    'sxg' => 'application/vnd.sun.xml.writer.global',\n    'sxi' => 'application/vnd.sun.xml.impress',\n    'sxm' => 'application/vnd.sun.xml.math',\n    'sxw' => 'application/vnd.sun.xml.writer',\n    't' => 'text/troff',\n    't3' => 'application/x-t3vm-image',\n    'taglet' => 'application/vnd.mynfc',\n    'tao' => 'application/vnd.tao.intent-module-archive',\n    'tar' => 'application/x-tar',\n    'tcap' => 'application/vnd.3gpp2.tcap',\n    'tcl' => 'application/x-tcl',\n    'teacher' => 'application/vnd.smart.teacher',\n    'tei' => 'application/tei+xml',\n    'teicorpus' => 'application/tei+xml',\n    'tex' => 'application/x-tex',\n    'texi' => 'application/x-texinfo',\n    'texinfo' => 'application/x-texinfo',\n    'text' => 'text/plain',\n    'tfi' => 'application/thraud+xml',\n    'tfm' => 'application/x-tex-tfm',\n    'tga' => 'image/x-tga',\n    'thmx' => 'application/vnd.ms-officetheme',\n    'tif' => 'image/tiff',\n    'tiff' => 'image/tiff',\n    'tmo' => 'application/vnd.tmobile-livetv',\n    'torrent' => 'application/x-bittorrent',\n    'tpl' => 'application/vnd.groove-tool-template',\n    'tpt' => 'application/vnd.trid.tpt',\n    'tr' => 'text/troff',\n    'tra' => 'application/vnd.trueapp',\n    'trm' => 'application/x-msterminal',\n    'ts' => 'video/mp2t',\n    'tsd' => 'application/timestamped-data',\n    'tsv' => 'text/tab-separated-values',\n    'ttc' => 'font/collection',\n    'ttf' => 'font/ttf',\n    'ttl' => 'text/turtle',\n    'twd' => 'application/vnd.simtech-mindmapper',\n    'twds' => 'application/vnd.simtech-mindmapper',\n    'txd' => 'application/vnd.genomatix.tuxedo',\n    'txf' => 'application/vnd.mobius.txf',\n    'txt' => 'text/plain',\n    'u32' => 'application/x-authorware-bin',\n    'udeb' => 'application/x-debian-package',\n    'ufd' => 'application/vnd.ufdl',\n    'ufdl' => 'application/vnd.ufdl',\n    'ulx' => 'application/x-glulx',\n    'umj' => 'application/vnd.umajin',\n    'unityweb' => 'application/vnd.unity',\n    'uoml' => 'application/vnd.uoml+xml',\n    'uri' => 'text/uri-list',\n    'uris' => 'text/uri-list',\n    'urls' => 'text/uri-list',\n    'ustar' => 'application/x-ustar',\n    'utz' => 'application/vnd.uiq.theme',\n    'uu' => 'text/x-uuencode',\n    'uva' => 'audio/vnd.dece.audio',\n    'uvd' => 'application/vnd.dece.data',\n    'uvf' => 'application/vnd.dece.data',\n    'uvg' => 'image/vnd.dece.graphic',\n    'uvh' => 'video/vnd.dece.hd',\n    'uvi' => 'image/vnd.dece.graphic',\n    'uvm' => 'video/vnd.dece.mobile',\n    'uvp' => 'video/vnd.dece.pd',\n    'uvs' => 'video/vnd.dece.sd',\n    'uvt' => 'application/vnd.dece.ttml+xml',\n    'uvu' => 'video/vnd.uvvu.mp4',\n    'uvv' => 'video/vnd.dece.video',\n    'uvva' => 'audio/vnd.dece.audio',\n    'uvvd' => 'application/vnd.dece.data',\n    'uvvf' => 'application/vnd.dece.data',\n    'uvvg' => 'image/vnd.dece.graphic',\n    'uvvh' => 'video/vnd.dece.hd',\n    'uvvi' => 'image/vnd.dece.graphic',\n    'uvvm' => 'video/vnd.dece.mobile',\n    'uvvp' => 'video/vnd.dece.pd',\n    'uvvs' => 'video/vnd.dece.sd',\n    'uvvt' => 'application/vnd.dece.ttml+xml',\n    'uvvu' => 'video/vnd.uvvu.mp4',\n    'uvvv' => 'video/vnd.dece.video',\n    'uvvx' => 'application/vnd.dece.unspecified',\n    'uvvz' => 'application/vnd.dece.zip',\n    'uvx' => 'application/vnd.dece.unspecified',\n    'uvz' => 'application/vnd.dece.zip',\n    'vcard' => 'text/vcard',\n    'vcd' => 'application/x-cdlink',\n    'vcf' => 'text/x-vcard',\n    'vcg' => 'application/vnd.groove-vcard',\n    'vcs' => 'text/x-vcalendar',\n    'vcx' => 'application/vnd.vcx',\n    'vis' => 'application/vnd.visionary',\n    'viv' => 'video/vnd.vivo',\n    'vob' => 'video/x-ms-vob',\n    'vor' => 'application/vnd.stardivision.writer',\n    'vox' => 'application/x-authorware-bin',\n    'vrml' => 'model/vrml',\n    'vsd' => 'application/vnd.visio',\n    'vsf' => 'application/vnd.vsf',\n    'vss' => 'application/vnd.visio',\n    'vst' => 'application/vnd.visio',\n    'vsw' => 'application/vnd.visio',\n    'vtu' => 'model/vnd.vtu',\n    'vxml' => 'application/voicexml+xml',\n    'w3d' => 'application/x-director',\n    'wad' => 'application/x-doom',\n    'wasm' => 'application/wasm',\n    'wav' => 'audio/x-wav',\n    'wax' => 'audio/x-ms-wax',\n    'wbmp' => 'image/vnd.wap.wbmp',\n    'wbs' => 'application/vnd.criticaltools.wbs+xml',\n    'wbxml' => 'application/vnd.wap.wbxml',\n    'wcm' => 'application/vnd.ms-works',\n    'wdb' => 'application/vnd.ms-works',\n    'wdp' => 'image/vnd.ms-photo',\n    'weba' => 'audio/webm',\n    'webm' => 'video/webm',\n    'webp' => 'image/webp',\n    'wg' => 'application/vnd.pmi.widget',\n    'wgt' => 'application/widget',\n    'wks' => 'application/vnd.ms-works',\n    'wm' => 'video/x-ms-wm',\n    'wma' => 'audio/x-ms-wma',\n    'wmd' => 'application/x-ms-wmd',\n    'wmf' => 'application/x-msmetafile',\n    'wml' => 'text/vnd.wap.wml',\n    'wmlc' => 'application/vnd.wap.wmlc',\n    'wmls' => 'text/vnd.wap.wmlscript',\n    'wmlsc' => 'application/vnd.wap.wmlscriptc',\n    'wmv' => 'video/x-ms-wmv',\n    'wmx' => 'video/x-ms-wmx',\n    'wmz' => 'application/x-msmetafile',\n    'woff' => 'font/woff',\n    'woff2' => 'font/woff2',\n    'wpd' => 'application/vnd.wordperfect',\n    'wpl' => 'application/vnd.ms-wpl',\n    'wps' => 'application/vnd.ms-works',\n    'wqd' => 'application/vnd.wqd',\n    'wri' => 'application/x-mswrite',\n    'wrl' => 'model/vrml',\n    'wsdl' => 'application/wsdl+xml',\n    'wspolicy' => 'application/wspolicy+xml',\n    'wtb' => 'application/vnd.webturbo',\n    'wvx' => 'video/x-ms-wvx',\n    'x32' => 'application/x-authorware-bin',\n    'x3d' => 'model/x3d+xml',\n    'x3db' => 'model/x3d+binary',\n    'x3dbz' => 'model/x3d+binary',\n    'x3dv' => 'model/x3d+vrml',\n    'x3dvz' => 'model/x3d+vrml',\n    'x3dz' => 'model/x3d+xml',\n    'xaml' => 'application/xaml+xml',\n    'xap' => 'application/x-silverlight-app',\n    'xar' => 'application/vnd.xara',\n    'xbap' => 'application/x-ms-xbap',\n    'xbd' => 'application/vnd.fujixerox.docuworks.binder',\n    'xbm' => 'image/x-xbitmap',\n    'xdf' => 'application/xcap-diff+xml',\n    'xdm' => 'application/vnd.syncml.dm+xml',\n    'xdp' => 'application/vnd.adobe.xdp+xml',\n    'xdssc' => 'application/dssc+xml',\n    'xdw' => 'application/vnd.fujixerox.docuworks',\n    'xenc' => 'application/xenc+xml',\n    'xer' => 'application/patch-ops-error+xml',\n    'xfdf' => 'application/vnd.adobe.xfdf',\n    'xfdl' => 'application/vnd.xfdl',\n    'xht' => 'application/xhtml+xml',\n    'xhtml' => 'application/xhtml+xml',\n    'xhvml' => 'application/xv+xml',\n    'xif' => 'image/vnd.xiff',\n    'xla' => 'application/vnd.ms-excel',\n    'xlam' => 'application/vnd.ms-excel.addin.macroenabled.12',\n    'xlc' => 'application/vnd.ms-excel',\n    'xlf' => 'application/x-xliff+xml',\n    'xlm' => 'application/vnd.ms-excel',\n    'xls' => 'application/vnd.ms-excel',\n    'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroenabled.12',\n    'xlsm' => 'application/vnd.ms-excel.sheet.macroenabled.12',\n    'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n    'xlt' => 'application/vnd.ms-excel',\n    'xltm' => 'application/vnd.ms-excel.template.macroenabled.12',\n    'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',\n    'xlw' => 'application/vnd.ms-excel',\n    'xm' => 'audio/xm',\n    'xml' => 'application/xml',\n    'xo' => 'application/vnd.olpc-sugar',\n    'xop' => 'application/xop+xml',\n    'xpi' => 'application/x-xpinstall',\n    'xpl' => 'application/xproc+xml',\n    'xpm' => 'image/x-xpixmap',\n    'xpr' => 'application/vnd.is-xpr',\n    'xps' => 'application/vnd.ms-xpsdocument',\n    'xpw' => 'application/vnd.intercon.formnet',\n    'xpx' => 'application/vnd.intercon.formnet',\n    'xsl' => 'application/xml',\n    'xslt' => 'application/xslt+xml',\n    'xsm' => 'application/vnd.syncml+xml',\n    'xspf' => 'application/xspf+xml',\n    'xul' => 'application/vnd.mozilla.xul+xml',\n    'xvm' => 'application/xv+xml',\n    'xvml' => 'application/xv+xml',\n    'xwd' => 'image/x-xwindowdump',\n    'xyz' => 'chemical/x-xyz',\n    'xz' => 'application/x-xz',\n    'yang' => 'application/yang',\n    'yin' => 'application/yin+xml',\n    'z1' => 'application/x-zmachine',\n    'z2' => 'application/x-zmachine',\n    'z3' => 'application/x-zmachine',\n    'z4' => 'application/x-zmachine',\n    'z5' => 'application/x-zmachine',\n    'z6' => 'application/x-zmachine',\n    'z7' => 'application/x-zmachine',\n    'z8' => 'application/x-zmachine',\n    'zaz' => 'application/vnd.zzazz.deck+xml',\n    'zip' => 'application/zip',\n    'zir' => 'application/vnd.zul',\n    'zirz' => 'application/vnd.zul',\n    'zmm' => 'application/vnd.handheld-entertainment+xml',\n];\n\n# fix for bundled libmagic bug, see also https://github.com/yiisoft/yii2/issues/19925\nif ((PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80122) || (PHP_VERSION_ID >= 80200 && PHP_VERSION_ID < 80209)) {\n    $mimeTypes = array_replace($mimeTypes, ['xz' => 'application/octet-stream']);\n}\n\nreturn $mimeTypes;\n"
  },
  {
    "path": "framework/i18n/DbMessageSource.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\i18n;\n\nuse yii\\base\\InvalidConfigException;\nuse yii\\caching\\CacheInterface;\nuse yii\\db\\Connection;\nuse yii\\db\\Expression;\nuse yii\\db\\Query;\nuse yii\\di\\Instance;\nuse yii\\helpers\\ArrayHelper;\n\n/**\n * DbMessageSource extends [[MessageSource]] and represents a message source that stores translated\n * messages in database.\n *\n * The database must contain the following two tables: source_message and message.\n *\n * The `source_message` table stores the messages to be translated, and the `message` table stores\n * the translated messages. The name of these two tables can be customized by setting [[sourceMessageTable]]\n * and [[messageTable]], respectively.\n *\n * The database connection is specified by [[db]]. Database schema could be initialized by applying migration:\n *\n * ```\n * yii migrate --migrationPath=@yii/i18n/migrations/\n * ```\n *\n * If you don't want to use migration and need SQL instead, files for all databases are in migrations directory.\n *\n * @author resurtm <resurtm@gmail.com>\n * @since 2.0\n */\nclass DbMessageSource extends MessageSource\n{\n    /**\n     * Prefix which would be used when generating cache key.\n     * @deprecated This constant has never been used and will be removed in 2.1.0.\n     */\n    public const CACHE_KEY_PREFIX = 'DbMessageSource';\n    /**\n     * @var Connection|array|string the DB connection object or the application component ID of the DB connection.\n     *\n     * After the DbMessageSource object is created, if you want to change this property, you should only assign\n     * it with a DB connection object.\n     *\n     * Starting from version 2.0.2, this can also be a configuration array for creating the object.\n     */\n    public $db = 'db';\n    /**\n     * @var CacheInterface|array|string the cache object or the application component ID of the cache object.\n     * The messages data will be cached using this cache object.\n     * Note, that to enable caching you have to set [[enableCaching]] to `true`, otherwise setting this property has no effect.\n     *\n     * After the DbMessageSource object is created, if you want to change this property, you should only assign\n     * it with a cache object.\n     *\n     * Starting from version 2.0.2, this can also be a configuration array for creating the object.\n     * @see cachingDuration\n     * @see enableCaching\n     */\n    public $cache = 'cache';\n    /**\n     * @var string the name of the source message table.\n     */\n    public $sourceMessageTable = '{{%source_message}}';\n    /**\n     * @var string the name of the translated message table.\n     */\n    public $messageTable = '{{%message}}';\n    /**\n     * @var int the time in seconds that the messages can remain valid in cache.\n     * Use 0 to indicate that the cached data will never expire.\n     * @see enableCaching\n     */\n    public $cachingDuration = 0;\n    /**\n     * @var bool whether to enable caching translated messages\n     */\n    public $enableCaching = false;\n\n\n    /**\n     * Initializes the DbMessageSource component.\n     * This method will initialize the [[db]] property to make sure it refers to a valid DB connection.\n     * Configured [[cache]] component would also be initialized.\n     * @throws InvalidConfigException if [[db]] is invalid or [[cache]] is invalid.\n     */\n    public function init()\n    {\n        parent::init();\n        $this->db = Instance::ensure($this->db, Connection::className());\n        if ($this->enableCaching) {\n            $this->cache = Instance::ensure($this->cache, 'yii\\caching\\CacheInterface');\n        }\n    }\n\n    /**\n     * Loads the message translation for the specified language and category.\n     * If translation for specific locale code such as `en-US` isn't found it\n     * tries more generic `en`.\n     *\n     * @param string $category the message category\n     * @param string $language the target language\n     * @return array the loaded messages. The keys are original messages, and the values\n     * are translated messages.\n     */\n    protected function loadMessages($category, $language)\n    {\n        if ($this->enableCaching) {\n            $key = [\n                __CLASS__,\n                $category,\n                $language,\n            ];\n            $messages = $this->cache->get($key);\n            if ($messages === false) {\n                $messages = $this->loadMessagesFromDb($category, $language);\n                $this->cache->set($key, $messages, $this->cachingDuration);\n            }\n\n            return $messages;\n        }\n\n        return $this->loadMessagesFromDb($category, $language);\n    }\n\n    /**\n     * Loads the messages from database.\n     * You may override this method to customize the message storage in the database.\n     * @param string $category the message category.\n     * @param string $language the target language.\n     * @return array the messages loaded from database.\n     */\n    protected function loadMessagesFromDb($category, $language)\n    {\n        $mainQuery = (new Query())->select(['message' => 't1.message', 'translation' => 't2.translation'])\n            ->from(['t1' => $this->sourceMessageTable, 't2' => $this->messageTable])\n            ->where([\n                't1.id' => new Expression('[[t2.id]]'),\n                't1.category' => $category,\n                't2.language' => $language,\n            ]);\n\n        $fallbackLanguage = substr($language, 0, 2);\n        $fallbackSourceLanguage = substr($this->sourceLanguage, 0, 2);\n\n        if ($fallbackLanguage !== $language) {\n            $mainQuery->union($this->createFallbackQuery($category, $language, $fallbackLanguage), true);\n        } elseif ($language === $fallbackSourceLanguage) {\n            $mainQuery->union($this->createFallbackQuery($category, $language, $fallbackSourceLanguage), true);\n        }\n\n        $messages = $mainQuery->createCommand($this->db)->queryAll();\n\n        return ArrayHelper::map($messages, 'message', 'translation');\n    }\n\n    /**\n     * The method builds the [[Query]] object for the fallback language messages search.\n     * Normally is called from [[loadMessagesFromDb]].\n     *\n     * @param string $category the message category\n     * @param string $language the originally requested language\n     * @param string $fallbackLanguage the target fallback language\n     * @return Query\n     * @see loadMessagesFromDb\n     * @since 2.0.7\n     */\n    protected function createFallbackQuery($category, $language, $fallbackLanguage)\n    {\n        return (new Query())->select(['message' => 't1.message', 'translation' => 't2.translation'])\n            ->from(['t1' => $this->sourceMessageTable, 't2' => $this->messageTable])\n            ->where([\n                't1.id' => new Expression('[[t2.id]]'),\n                't1.category' => $category,\n                't2.language' => $fallbackLanguage,\n            ])->andWhere([\n                'NOT IN', 't2.id', (new Query())->select('[[id]]')->from($this->messageTable)->where(['language' => $language]),\n            ]);\n    }\n}\n"
  },
  {
    "path": "framework/i18n/Formatter.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\i18n;\n\nuse Closure;\nuse DateInterval;\nuse DateTime;\nuse DateTimeInterface;\nuse DateTimeZone;\nuse IntlDateFormatter;\nuse NumberFormatter;\nuse Yii;\nuse yii\\base\\Component;\nuse yii\\base\\InvalidArgumentException;\nuse yii\\base\\InvalidConfigException;\nuse yii\\helpers\\ArrayHelper;\nuse yii\\helpers\\FormatConverter;\nuse yii\\helpers\\Html;\nuse yii\\helpers\\HtmlPurifier;\nuse yii\\helpers\\Url;\n\n/**\n * Formatter provides a set of commonly used data formatting methods.\n *\n * The formatting methods provided by Formatter are all named in the form of `asXyz()`.\n * The behavior of some of them may be configured via the properties of Formatter. For example,\n * by configuring [[dateFormat]], one may control how [[asDate()]] formats the value into a date string.\n *\n * Formatter is configured as an application component in [[\\yii\\base\\Application]] by default.\n * You can access that instance via `Yii::$app->formatter`.\n *\n * The Formatter class is designed to format values according to a [[locale]]. For this feature to work\n * the [PHP intl extension](https://www.php.net/manual/en/book.intl.php) has to be installed.\n * Most of the methods however work also if the PHP intl extension is not installed by providing\n * a fallback implementation. Without intl month and day names are in English only.\n * Note that even if the intl extension is installed, formatting date and time values for years >=2038 or <=1901\n * on 32bit systems will fall back to the PHP implementation because intl uses a 32bit UNIX timestamp internally.\n * On a 64bit system the intl formatter is used in all cases if installed.\n *\n * > Note: The Formatter class is meant to be used for formatting values for display to users in different\n * > languages and time zones. If you need to format a date or time in machine readable format, use the\n * > PHP [date()](https://www.php.net/manual/en/function.date.php) function instead.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Enrica Ruedin <e.ruedin@guggach.com>\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0\n */\nclass Formatter extends Component\n{\n    /**\n     * @since 2.0.13\n     */\n    public const UNIT_SYSTEM_METRIC = 'metric';\n    /**\n     * @since 2.0.13\n     */\n    public const UNIT_SYSTEM_IMPERIAL = 'imperial';\n    /**\n     * @since 2.0.13\n     */\n    public const FORMAT_WIDTH_LONG = 'long';\n    /**\n     * @since 2.0.13\n     */\n    public const FORMAT_WIDTH_SHORT = 'short';\n    /**\n     * @since 2.0.13\n     */\n    public const UNIT_LENGTH = 'length';\n    /**\n     * @since 2.0.13\n     */\n    public const UNIT_WEIGHT = 'mass';\n    /**\n     * @var string|null the text to be displayed when formatting a `null` value.\n     * Defaults to `'<span class=\"not-set\">(not set)</span>'`, where `(not set)`\n     * will be translated according to [[locale]].\n     */\n    public $nullDisplay;\n    /**\n     * @var array the text to be displayed when formatting a boolean value. The first element corresponds\n     * to the text displayed for `false`, the second element for `true`.\n     * Defaults to `['No', 'Yes']`, where `Yes` and `No`\n     * will be translated according to [[locale]].\n     */\n    public $booleanFormat;\n    /**\n     * @var string|null the locale ID that is used to localize the date and number formatting.\n     * For number and date formatting this is only effective when the\n     * [PHP intl extension](https://www.php.net/manual/en/book.intl.php) is installed.\n     * If not set, [[\\yii\\base\\Application::language]] will be used.\n     */\n    public $locale;\n    /**\n     * @var string|null the language code (e.g. `en-US`, `en`) that is used to translate internal messages.\n     * If not set, [[locale]] will be used (without the `@calendar` param, if included).\n     *\n     * @since 2.0.28\n     */\n    public $language;\n    /**\n     * @var string|null the time zone to use for formatting time and date values.\n     *\n     * This can be any value that may be passed to [date_default_timezone_set()](https://www.php.net/manual/en/function.date-default-timezone-set.php)\n     * e.g. `UTC`, `Europe/Berlin` or `America/Chicago`.\n     * Refer to the [php manual](https://www.php.net/manual/en/timezones.php) for available time zones.\n     * If this property is not set, [[\\yii\\base\\Application::timeZone]] will be used.\n     *\n     * Note that the default time zone for input data is assumed to be UTC by default if no time zone is included in the input date value.\n     * If you store your data in a different time zone in the database, you have to adjust [[defaultTimeZone]] accordingly.\n     */\n    public $timeZone;\n    /**\n     * @var string the time zone that is assumed for input values if they do not include a time zone explicitly.\n     *\n     * The value must be a valid time zone identifier, e.g. `UTC`, `Europe/Berlin` or `America/Chicago`.\n     * Please refer to the [php manual](https://www.php.net/manual/en/timezones.php) for available time zones.\n     *\n     * It defaults to `UTC` so you only have to adjust this value if you store datetime values in another time zone in your database.\n     *\n     * Note that a UNIX timestamp is always in UTC by its definition. That means that specifying a default time zone different from\n     * UTC has no effect on date values given as UNIX timestamp.\n     *\n     * @since 2.0.1\n     */\n    public $defaultTimeZone = 'UTC';\n    /**\n     * @var string the default format string to be used to format a [[asDate()|date]].\n     * This can be \"short\", \"medium\", \"long\", or \"full\", which represents a preset format of different lengths.\n     *\n     * It can also be a custom format as specified in the [ICU manual](https://unicode-org.github.io/icu/userguide/format_parse/datetime/).\n     * Alternatively this can be a string prefixed with `php:` representing a format that can be recognized by the\n     * PHP [date()](https://www.php.net/manual/en/function.date.php)-function.\n     *\n     * For example:\n     *\n     * ```\n     * 'MM/dd/yyyy' // date in ICU format\n     * 'php:m/d/Y' // the same date in PHP format\n     * ```\n     */\n    public $dateFormat = 'medium';\n    /**\n     * @var string the default format string to be used to format a [[asTime()|time]].\n     * This can be \"short\", \"medium\", \"long\", or \"full\", which represents a preset format of different lengths.\n     *\n     * It can also be a custom format as specified in the [ICU manual](https://unicode-org.github.io/icu/userguide/format_parse/datetime/).\n     * Alternatively this can be a string prefixed with `php:` representing a format that can be recognized by the\n     * PHP [date()](https://www.php.net/manual/en/function.date.php)-function.\n     *\n     * For example:\n     *\n     * ```\n     * 'HH:mm:ss' // time in ICU format\n     * 'php:H:i:s' // the same time in PHP format\n     * ```\n     */\n    public $timeFormat = 'medium';\n    /**\n     * @var string the default format string to be used to format a [[asDatetime()|date and time]].\n     * This can be \"short\", \"medium\", \"long\", or \"full\", which represents a preset format of different lengths.\n     *\n     * It can also be a custom format as specified in the [ICU manual](https://unicode-org.github.io/icu/userguide/format_parse/datetime/).\n     *\n     * Alternatively this can be a string prefixed with `php:` representing a format that can be recognized by the\n     * PHP [date()](https://www.php.net/manual/en/function.date.php) function.\n     *\n     * For example:\n     *\n     * ```\n     * 'MM/dd/yyyy HH:mm:ss' // date and time in ICU format\n     * 'php:m/d/Y H:i:s' // the same date and time in PHP format\n     * ```\n     */\n    public $datetimeFormat = 'medium';\n    /**\n     * @var \\IntlCalendar|int|null the calendar to be used for date formatting. The value of this property will be directly\n     * passed to the [constructor of the `IntlDateFormatter` class](https://www.php.net/manual/en/intldateformatter.create.php).\n     *\n     * Defaults to `null`, which means the Gregorian calendar will be used. You may also explicitly pass the constant\n     * `\\IntlDateFormatter::GREGORIAN` for Gregorian calendar.\n     *\n     * To use an alternative calendar like for example the [Jalali calendar](https://en.wikipedia.org/wiki/Jalali_calendar),\n     * set this property to `\\IntlDateFormatter::TRADITIONAL`.\n     * The calendar must then be specified in the [[locale]], for example for the persian calendar the configuration for the formatter would be:\n     *\n     * ```\n     * 'formatter' => [\n     *     'locale' => 'fa_IR@calendar=persian',\n     *     'calendar' => \\IntlDateFormatter::TRADITIONAL,\n     * ],\n     * ```\n     *\n     * Available calendar names can be found in the [ICU manual](https://unicode-org.github.io/icu/userguide/datetime/calendar/).\n     *\n     * Since PHP 5.5 you may also use an instance of the [[\\IntlCalendar]] class.\n     * Check the [PHP manual](https://www.php.net/manual/en/intldateformatter.create.php) for more details.\n     *\n     * If the [PHP intl extension](https://www.php.net/manual/en/book.intl.php) is not available, setting this property will have no effect.\n     *\n     * @see https://www.php.net/manual/en/intldateformatter.create.php\n     * @see https://www.php.net/manual/en/class.intldateformatter.php#intl.intldateformatter-constants.calendartypes\n     * @see https://www.php.net/manual/en/class.intlcalendar.php\n     * @since 2.0.7\n     */\n    public $calendar;\n    /**\n     * @var string|null the character displayed as the decimal point when formatting a number.\n     * If not set, the decimal separator corresponding to [[locale]] will be used.\n     * If [PHP intl extension](https://www.php.net/manual/en/book.intl.php) is not available, the default value is '.'.\n     */\n    public $decimalSeparator;\n    /**\n     * @var string|null the character displayed as the decimal point when formatting a currency.\n     * If not set, the currency decimal separator corresponding to [[locale]] will be used.\n     * If [PHP intl extension](https://www.php.net/manual/en/book.intl.php) is not available, setting this property will have no effect.\n     * @since 2.0.35\n     */\n    public $currencyDecimalSeparator;\n    /**\n     * @var string|null the character displayed as the thousands separator (also called grouping separator) character when formatting a number.\n     * If not set, the thousand separator corresponding to [[locale]] will be used.\n     * If [PHP intl extension](https://www.php.net/manual/en/book.intl.php) is not available, the default value is ','.\n     */\n    public $thousandSeparator;\n    /**\n     * @var array a list of name value pairs that are passed to the\n     * intl [NumberFormatter::setAttribute()](https://www.php.net/manual/en/numberformatter.setattribute.php) method of all\n     * the number formatter objects created by [[createNumberFormatter()]].\n     * This property takes only effect if the [PHP intl extension](https://www.php.net/manual/en/book.intl.php) is installed.\n     *\n     * Please refer to the [PHP manual](https://www.php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants.unumberformatattribute)\n     * for the possible options.\n     *\n     * For example to adjust the maximum and minimum value of fraction digits you can configure this property like the following:\n     *\n     * ```\n     * [\n     *     NumberFormatter::MIN_FRACTION_DIGITS => 0,\n     *     NumberFormatter::MAX_FRACTION_DIGITS => 2,\n     * ]\n     * ```\n     */\n    public $numberFormatterOptions = [];\n    /**\n     * @var array a list of name value pairs that are passed to the\n     * intl [NumberFormatter::setTextAttribute()](https://www.php.net/manual/en/numberformatter.settextattribute.php) method of all\n     * the number formatter objects created by [[createNumberFormatter()]].\n     * This property takes only effect if the [PHP intl extension](https://www.php.net/manual/en/book.intl.php) is installed.\n     *\n     * Please refer to the [PHP manual](https://www.php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants.unumberformattextattribute)\n     * for the possible options.\n     *\n     * For example to change the minus sign for negative numbers you can configure this property like the following:\n     *\n     * ```\n     * [\n     *     NumberFormatter::NEGATIVE_PREFIX => 'MINUS',\n     * ]\n     * ```\n     */\n    public $numberFormatterTextOptions = [];\n    /**\n     * @var array a list of name value pairs that are passed to the\n     * intl [NumberFormatter::setSymbol()](https://www.php.net/manual/en/numberformatter.setsymbol.php) method of all\n     * the number formatter objects created by [[createNumberFormatter()]].\n     * This property takes only effect if the [PHP intl extension](https://www.php.net/manual/en/book.intl.php) is installed.\n     *\n     * Please refer to the [PHP manual](https://www.php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants.unumberformatsymbol)\n     * for the possible options.\n     *\n     * For example to choose a custom currency symbol, e.g. [U+20BD](https://unicode-table.com/en/20BD/) instead of `руб.` for Russian Ruble:\n     *\n     * ```\n     * [\n     *     NumberFormatter::CURRENCY_SYMBOL => '₽',\n     * ]\n     * ```\n     *\n     * @since 2.0.4\n     */\n    public $numberFormatterSymbols = [];\n    /**\n     * @var string|null the 3-letter ISO 4217 currency code indicating the default currency to use for [[asCurrency]].\n     * If not set, the currency code corresponding to [[locale]] will be used.\n     * Note that in this case the [[locale]] has to be specified with a country code, e.g. `en-US` otherwise it\n     * is not possible to determine the default currency.\n     */\n    public $currencyCode;\n    /**\n     * @var int the base at which a kilobyte is calculated (1000 or 1024 bytes per kilobyte), used by [[asSize]] and [[asShortSize]].\n     * Defaults to 1024.\n     */\n    public $sizeFormatBase = 1024;\n    /**\n     * @var string default system of measure units. Defaults to [[UNIT_SYSTEM_METRIC]].\n     * Possible values:\n     *  - [[UNIT_SYSTEM_METRIC]]\n     *  - [[UNIT_SYSTEM_IMPERIAL]]\n     *\n     * @see asLength\n     * @see asWeight\n     * @since 2.0.13\n     */\n    public $systemOfUnits = self::UNIT_SYSTEM_METRIC;\n    /**\n     * @var array configuration of weight and length measurement units.\n     * This array contains the most usable measurement units, but you can change it\n     * in case you have some special requirements.\n     *\n     * For example, you can add smaller measure unit:\n     *\n     * ```\n     * $this->measureUnits[self::UNIT_LENGTH][self::UNIT_SYSTEM_METRIC] = [\n     *     'nanometer' => 0.000001\n     * ]\n     * ```\n     * @see asLength\n     * @see asWeight\n     * @since 2.0.13\n     */\n    public $measureUnits = [\n        self::UNIT_LENGTH => [\n            self::UNIT_SYSTEM_IMPERIAL => [\n                'inch' => 1,\n                'foot' => 12,\n                'yard' => 36,\n                'chain' => 792,\n                'furlong' => 7920,\n                'mile' => 63360,\n            ],\n            self::UNIT_SYSTEM_METRIC => [\n                'millimeter' => 1,\n                'centimeter' => 10,\n                'meter' => 1000,\n                'kilometer' => 1000000,\n            ],\n        ],\n        self::UNIT_WEIGHT => [\n            self::UNIT_SYSTEM_IMPERIAL => [\n                'grain' => 1,\n                'drachm' => 27.34375,\n                'ounce' => 437.5,\n                'pound' => 7000,\n                'stone' => 98000,\n                'quarter' => 196000,\n                'hundredweight' => 784000,\n                'ton' => 15680000,\n            ],\n            self::UNIT_SYSTEM_METRIC => [\n                'gram' => 1,\n                'kilogram' => 1000,\n                'ton' => 1000000,\n            ],\n        ],\n    ];\n    /**\n     * @var array The base units that are used as multipliers for smallest possible unit from [[measureUnits]].\n     * @since 2.0.13\n     */\n    public $baseUnits = [\n        self::UNIT_LENGTH => [\n            self::UNIT_SYSTEM_IMPERIAL => 12, // 1 feet = 12 inches\n            self::UNIT_SYSTEM_METRIC => 1000, // 1 meter = 1000 millimeters\n        ],\n        self::UNIT_WEIGHT => [\n            self::UNIT_SYSTEM_IMPERIAL => 7000, // 1 pound = 7000 grains\n            self::UNIT_SYSTEM_METRIC => 1000, // 1 kilogram = 1000 grams\n        ],\n    ];\n\n    /**\n     * @var bool whether the [PHP intl extension](https://www.php.net/manual/en/book.intl.php) is loaded.\n     */\n    private $_intlLoaded = false;\n    /**\n     * @var \\ResourceBundle cached ResourceBundle object used to read unit translations\n     */\n    private $_resourceBundle;\n    /**\n     * @var array cached unit translation patterns\n     */\n    private $_unitMessages = [];\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        if ($this->timeZone === null) {\n            $this->timeZone = Yii::$app->timeZone;\n        }\n        if ($this->locale === null) {\n            $this->locale = Yii::$app->language;\n        }\n        if ($this->language === null) {\n            $this->language = strtok($this->locale, '@');\n        }\n        if ($this->booleanFormat === null) {\n            $this->booleanFormat = [Yii::t('yii', 'No', [], $this->language), Yii::t('yii', 'Yes', [], $this->language)];\n        }\n        if ($this->nullDisplay === null) {\n            $this->nullDisplay = '<span class=\"not-set\">' . Yii::t('yii', '(not set)', [], $this->language) . '</span>';\n        }\n        $this->_intlLoaded = extension_loaded('intl');\n        if (!$this->_intlLoaded) {\n            if ($this->decimalSeparator === null) {\n                $this->decimalSeparator = '.';\n            }\n            if ($this->thousandSeparator === null) {\n                $this->thousandSeparator = ',';\n            }\n        }\n    }\n\n    /**\n     * Formats the value based on the given format type.\n     * This method will call one of the \"as\" methods available in this class to do the formatting.\n     * For type \"xyz\", the method \"asXyz\" will be used. For example, if the format is \"html\",\n     * then [[asHtml()]] will be used. Format names are case insensitive.\n     * @param mixed $value the value to be formatted.\n     * @param string|array|Closure $format the format of the value, e.g., \"html\", \"text\" or an anonymous function\n     * returning the formatted value.\n     *\n     * To specify additional parameters of the formatting method, you may use an array.\n     * The first element of the array specifies the format name, while the rest of the elements will be used as the\n     * parameters to the formatting method. For example, a format of `['date', 'Y-m-d']` will cause the invocation\n     * of `asDate($value, 'Y-m-d')`.\n     *\n     * The anonymous function signature should be: `function($value, $formatter)`,\n     * where `$value` is the value that should be formatted and `$formatter` is an instance of the Formatter class,\n     * which can be used to call other formatting functions.\n     * The possibility to use an anonymous function is available since version 2.0.13.\n     * @return string the formatting result.\n     * @throws InvalidArgumentException if the format type is not supported by this class.\n     */\n    public function format($value, $format)\n    {\n        if ($format instanceof Closure) {\n            return $format($value, $this);\n        }\n        if (is_array($format)) {\n            if (!isset($format[0])) {\n                throw new InvalidArgumentException('The $format array must contain at least one element.');\n            }\n            $f = $format[0];\n            $format[0] = $value;\n            $params = $format;\n            $format = $f;\n        } else {\n            $params = [$value];\n        }\n        $method = 'as' . $format;\n        if ($this->hasMethod($method)) {\n            return call_user_func_array([$this, $method], array_values($params));\n        }\n\n        throw new InvalidArgumentException(\"Unknown format type: $format\");\n    }\n\n    // simple formats\n\n    /**\n     * Formats the value as is without any formatting.\n     * This method simply returns back the parameter without any format.\n     * The only exception is a `null` value which will be formatted using [[nullDisplay]].\n     * @param mixed $value the value to be formatted.\n     * @return string the formatted result.\n     */\n    public function asRaw($value)\n    {\n        if ($value === null) {\n            return $this->nullDisplay;\n        }\n\n        return $value;\n    }\n\n    /**\n     * Formats the value as an HTML-encoded plain text.\n     * @param string|null $value the value to be formatted.\n     * @return string the formatted result.\n     */\n    public function asText($value)\n    {\n        if ($value === null) {\n            return $this->nullDisplay;\n        }\n\n        return Html::encode($value);\n    }\n\n    /**\n     * Formats the value as an HTML-encoded plain text with newlines converted into breaks.\n     * @param string|null $value the value to be formatted.\n     * @return string the formatted result.\n     */\n    public function asNtext($value)\n    {\n        if ($value === null) {\n            return $this->nullDisplay;\n        }\n\n        return nl2br(Html::encode($value));\n    }\n\n    /**\n     * Formats the value as HTML-encoded text paragraphs.\n     * Each text paragraph is enclosed within a `<p>` tag.\n     * One or multiple consecutive empty lines divide two paragraphs.\n     * @param string|null $value the value to be formatted.\n     * @return string the formatted result.\n     */\n    public function asParagraphs($value)\n    {\n        if ($value === null) {\n            return $this->nullDisplay;\n        }\n\n        return str_replace('<p></p>', '', '<p>' . preg_replace('/\\R{2,}/u', \"</p>\\n<p>\", Html::encode($value)) . '</p>');\n    }\n\n    /**\n     * Formats the value as HTML text.\n     * The value will be purified using [[HtmlPurifier]] to avoid XSS attacks.\n     * Use [[asRaw()]] if you do not want any purification of the value.\n     * @param string|null $value the value to be formatted.\n     * @param array|null $config the configuration for the HTMLPurifier class.\n     * @return string the formatted result.\n     */\n    public function asHtml($value, $config = null)\n    {\n        if ($value === null) {\n            return $this->nullDisplay;\n        }\n\n        return HtmlPurifier::process($value, $config);\n    }\n\n    /**\n     * Formats the value as a mailto link.\n     * @param string|null $value the value to be formatted.\n     * @param array $options the tag options in terms of name-value pairs. See [[Html::mailto()]].\n     * @return string the formatted result.\n     */\n    public function asEmail($value, $options = [])\n    {\n        if ($value === null) {\n            return $this->nullDisplay;\n        }\n\n        return Html::mailto(Html::encode($value), $value, $options);\n    }\n\n    /**\n     * Formats the value as an image tag.\n     * @param mixed $value the value to be formatted.\n     * @param array $options the tag options in terms of name-value pairs. See [[Html::img()]].\n     * @return string the formatted result.\n     */\n    public function asImage($value, $options = [])\n    {\n        if ($value === null) {\n            return $this->nullDisplay;\n        }\n\n        return Html::img($value, $options);\n    }\n\n    /**\n     * Formats the value as a hyperlink.\n     * @param mixed $value the value to be formatted.\n     * @param array $options the tag options in terms of name-value pairs. See [[Html::a()]]. Since 2.0.43 there is\n     * a special option available `scheme` - if set it won't be passed to [[Html::a()]] but it will control the URL\n     * protocol part of the link by normalizing URL and ensuring that it uses specified scheme. See [[Url::ensureScheme()]].\n     * If `scheme` is not set the original behavior is preserved which is to add \"http://\" prefix when \"://\" string is\n     * not found in the $value.\n     * @return string the formatted result.\n     */\n    public function asUrl($value, $options = [])\n    {\n        if ($value === null) {\n            return $this->nullDisplay;\n        }\n        $url = $value;\n        $scheme = ArrayHelper::remove($options, 'scheme');\n        if ($scheme === null) {\n            if (strpos($url, '://') === false) {\n                $url = 'http://' . $url;\n            }\n        } else {\n            $url = Url::ensureScheme($url, $scheme);\n        }\n\n        return Html::a(Html::encode($value), $url, $options);\n    }\n\n    /**\n     * Formats the value as a boolean.\n     * @param mixed $value the value to be formatted.\n     * @return string the formatted result.\n     * @see booleanFormat\n     */\n    public function asBoolean($value)\n    {\n        if ($value === null) {\n            return $this->nullDisplay;\n        }\n\n        return $value ? $this->booleanFormat[1] : $this->booleanFormat[0];\n    }\n\n    // date and time formats\n\n    /**\n     * Formats the value as a date.\n     * @param int|string|DateTime|DateTimeInterface|null $value the value to be formatted. The following\n     * types of value are supported:\n     *\n     * - an integer representing a UNIX timestamp. A UNIX timestamp is always in UTC by its definition.\n     * - a string that can be [parsed to create a DateTime object](https://www.php.net/manual/en/datetime.formats.php).\n     *   The timestamp is assumed to be in [[defaultTimeZone]] unless a time zone is explicitly given.\n     * - a PHP [DateTime](https://www.php.net/manual/en/class.datetime.php) object. You may set the time zone\n     *   for the DateTime object to specify the source time zone.\n     *\n     * The formatter will convert date values according to [[timeZone]] before formatting it.\n     * If no timezone conversion should be performed, you need to set [[defaultTimeZone]] and [[timeZone]] to the same value.\n     * Also no conversion will be performed on values that have no time information, e.g. `\"2017-06-05\"`.\n     *\n     * @param string|null $format the format used to convert the value into a date string.\n     * If null, [[dateFormat]] will be used.\n     *\n     * This can be \"short\", \"medium\", \"long\", or \"full\", which represents a preset format of different lengths.\n     * It can also be a custom format as specified in the [ICU manual](https://unicode-org.github.io/icu/userguide/format_parse/datetime/).\n     *\n     * Alternatively this can be a string prefixed with `php:` representing a format that can be recognized by the\n     * PHP [date()](https://www.php.net/manual/en/function.date.php)-function.\n     *\n     * @return string the formatted result.\n     * @throws InvalidArgumentException if the input value can not be evaluated as a date value.\n     * @throws InvalidConfigException if the date format is invalid.\n     * @see dateFormat\n     */\n    public function asDate($value, $format = null)\n    {\n        if ($format === null) {\n            $format = $this->dateFormat;\n        }\n\n        return $this->formatDateTimeValue($value, $format, 'date');\n    }\n\n    /**\n     * Formats the value as a time.\n     * @param int|string|DateTime|DateTimeInterface|null $value the value to be formatted. The following\n     * types of value are supported:\n     *\n     * - an integer representing a UNIX timestamp. A UNIX timestamp is always in UTC by its definition.\n     * - a string that can be [parsed to create a DateTime object](https://www.php.net/manual/en/datetime.formats.php).\n     *   The timestamp is assumed to be in [[defaultTimeZone]] unless a time zone is explicitly given.\n     * - a PHP [DateTime](https://www.php.net/manual/en/class.datetime.php) object. You may set the time zone\n     *   for the DateTime object to specify the source time zone.\n     *\n     * The formatter will convert date values according to [[timeZone]] before formatting it.\n     * If no timezone conversion should be performed, you need to set [[defaultTimeZone]] and [[timeZone]] to the same value.\n     *\n     * @param string|null $format the format used to convert the value into a date string.\n     * If null, [[timeFormat]] will be used.\n     *\n     * This can be \"short\", \"medium\", \"long\", or \"full\", which represents a preset format of different lengths.\n     * It can also be a custom format as specified in the [ICU manual](https://unicode-org.github.io/icu/userguide/format_parse/datetime/).\n     *\n     * Alternatively this can be a string prefixed with `php:` representing a format that can be recognized by the\n     * PHP [date()](https://www.php.net/manual/en/function.date.php)-function.\n     *\n     * @return string the formatted result.\n     * @throws InvalidArgumentException if the input value can not be evaluated as a date value.\n     * @throws InvalidConfigException if the date format is invalid.\n     * @see timeFormat\n     */\n    public function asTime($value, $format = null)\n    {\n        if ($format === null) {\n            $format = $this->timeFormat;\n        }\n\n        return $this->formatDateTimeValue($value, $format, 'time');\n    }\n\n    /**\n     * Formats the value as a datetime.\n     * @param int|string|DateTime|DateTimeInterface|null $value the value to be formatted. The following\n     * types of value are supported:\n     *\n     * - an integer representing a UNIX timestamp. A UNIX timestamp is always in UTC by its definition.\n     * - a string that can be [parsed to create a DateTime object](https://www.php.net/manual/en/datetime.formats.php).\n     *   The timestamp is assumed to be in [[defaultTimeZone]] unless a time zone is explicitly given.\n     * - a PHP [DateTime](https://www.php.net/manual/en/class.datetime.php) object. You may set the time zone\n     *   for the DateTime object to specify the source time zone.\n     *\n     * The formatter will convert date values according to [[timeZone]] before formatting it.\n     * If no timezone conversion should be performed, you need to set [[defaultTimeZone]] and [[timeZone]] to the same value.\n     *\n     * @param string|null $format the format used to convert the value into a date string.\n     * If null, [[datetimeFormat]] will be used.\n     *\n     * This can be \"short\", \"medium\", \"long\", or \"full\", which represents a preset format of different lengths.\n     * It can also be a custom format as specified in the [ICU manual](https://unicode-org.github.io/icu/userguide/format_parse/datetime/).\n     *\n     * Alternatively this can be a string prefixed with `php:` representing a format that can be recognized by the\n     * PHP [date()](https://www.php.net/manual/en/function.date.php)-function.\n     *\n     * @return string the formatted result.\n     * @throws InvalidArgumentException if the input value can not be evaluated as a date value.\n     * @throws InvalidConfigException if the date format is invalid.\n     * @see datetimeFormat\n     */\n    public function asDatetime($value, $format = null)\n    {\n        if ($format === null) {\n            $format = $this->datetimeFormat;\n        }\n\n        return $this->formatDateTimeValue($value, $format, 'datetime');\n    }\n\n    /**\n     * @var array map of short format names to IntlDateFormatter constant values.\n     */\n    private $_dateFormats = [\n        'short' => 3, // IntlDateFormatter::SHORT,\n        'medium' => 2, // IntlDateFormatter::MEDIUM,\n        'long' => 1, // IntlDateFormatter::LONG,\n        'full' => 0, // IntlDateFormatter::FULL,\n    ];\n\n    /**\n     * @param int|string|DateTime|DateTimeInterface|null $value the value to be formatted. The following\n     * types of value are supported:\n     *\n     * - an integer representing a UNIX timestamp\n     * - a string that can be [parsed to create a DateTime object](https://www.php.net/manual/en/datetime.formats.php).\n     *   The timestamp is assumed to be in [[defaultTimeZone]] unless a time zone is explicitly given.\n     * - a PHP [DateTime](https://www.php.net/manual/en/class.datetime.php) object\n     *\n     * @param string $format the format used to convert the value into a date string.\n     * @param string $type 'date', 'time', or 'datetime'.\n     * @throws InvalidConfigException if the date format is invalid.\n     * @return string the formatted result.\n     */\n    private function formatDateTimeValue($value, $format, $type)\n    {\n        $timeZone = $this->timeZone;\n        // avoid time zone conversion for date-only and time-only values\n        if ($type === 'date' || $type === 'time') {\n            list($timestamp, $hasTimeInfo, $hasDateInfo) = $this->normalizeDatetimeValue($value, true);\n            if (($type === 'date' && !$hasTimeInfo) || ($type === 'time' && !$hasDateInfo)) {\n                $timeZone = $this->defaultTimeZone;\n            }\n        } else {\n            $timestamp = $this->normalizeDatetimeValue($value);\n        }\n        if ($timestamp === null) {\n            return $this->nullDisplay;\n        }\n\n        // intl does not work with dates >=2038 or <=1901 on 32bit machines, fall back to PHP\n        $year = $timestamp->format('Y');\n        if ($this->_intlLoaded && !(PHP_INT_SIZE === 4 && ($year <= 1901 || $year >= 2038))) {\n            if (strncmp($format, 'php:', 4) === 0) {\n                $format = FormatConverter::convertDatePhpToIcu(substr($format, 4));\n            }\n            if (isset($this->_dateFormats[$format])) {\n                if ($type === 'date') {\n                    $formatter = new IntlDateFormatter(\n                        $this->locale,\n                        $this->_dateFormats[$format],\n                        IntlDateFormatter::NONE,\n                        $timeZone,\n                        $this->calendar\n                    );\n                } elseif ($type === 'time') {\n                    $formatter = new IntlDateFormatter(\n                        $this->locale,\n                        IntlDateFormatter::NONE,\n                        $this->_dateFormats[$format],\n                        $timeZone,\n                        $this->calendar\n                    );\n                } else {\n                    $formatter = new IntlDateFormatter(\n                        $this->locale,\n                        $this->_dateFormats[$format],\n                        $this->_dateFormats[$format],\n                        $timeZone,\n                        $this->calendar\n                    );\n                }\n            } else {\n                $formatter = new IntlDateFormatter(\n                    $this->locale,\n                    IntlDateFormatter::NONE,\n                    IntlDateFormatter::NONE,\n                    $timeZone,\n                    $this->calendar,\n                    $format\n                );\n            }\n\n            // make IntlDateFormatter work with DateTimeImmutable\n            if ($timestamp instanceof \\DateTimeImmutable) {\n                $timestamp = new DateTime($timestamp->format(DateTime::ISO8601), $timestamp->getTimezone());\n            }\n\n            return $formatter->format($timestamp);\n        }\n\n        if (strncmp($format, 'php:', 4) === 0) {\n            $format = substr($format, 4);\n        } else {\n            $format = FormatConverter::convertDateIcuToPhp($format, $type, $this->locale);\n        }\n        if ($timeZone != null) {\n            if ($timestamp instanceof \\DateTimeImmutable) {\n                $timestamp = $timestamp->setTimezone(new DateTimeZone($timeZone));\n            } else {\n                $timestamp->setTimezone(new DateTimeZone($timeZone));\n            }\n        }\n\n        return $timestamp->format($format);\n    }\n\n    /**\n     * Normalizes the given datetime value as a DateTime object that can be taken by various date/time formatting methods.\n     *\n     * @param int|string|DateTime|DateTimeInterface|null $value the datetime value to be normalized. The following\n     * types of value are supported:\n     *\n     * - an integer representing a UNIX timestamp\n     * - a string that can be [parsed to create a DateTime object](https://www.php.net/manual/en/datetime.formats.php).\n     *   The timestamp is assumed to be in [[defaultTimeZone]] unless a time zone is explicitly given.\n     * - a PHP [DateTime](https://www.php.net/manual/en/class.datetime.php) object\n     *\n     * @param bool $checkDateTimeInfo whether to also check if the date/time value has some time and date information attached.\n     * Defaults to `false`. If `true`, the method will then return an array with the first element being the normalized\n     * timestamp, the second a boolean indicating whether the timestamp has time information and third a boolean indicating\n     * whether the timestamp has date information.\n     * This parameter is available since version 2.0.1.\n     * @return DateTime|array the normalized datetime value\n     * Since version 2.0.1 this may also return an array if `$checkDateTimeInfo` is true.\n     * The first element of the array is the normalized timestamp and the second is a boolean indicating whether\n     * the timestamp has time information or it is just a date value.\n     * Since version 2.0.12 the array has third boolean element indicating whether the timestamp has date information\n     * or it is just a time value.\n     * @throws InvalidArgumentException if the input value can not be evaluated as a date value.\n     */\n    protected function normalizeDatetimeValue($value, $checkDateTimeInfo = false)\n    {\n        // checking for DateTime and DateTimeInterface is not redundant, DateTimeInterface is only in PHP>5.5\n        if ($value === null || $value instanceof DateTime || $value instanceof DateTimeInterface) {\n            // skip any processing\n            return $checkDateTimeInfo ? [$value, true, true] : $value;\n        }\n        if (empty($value)) {\n            $value = 0;\n        }\n        try {\n            if (is_numeric($value)) { // process as unix timestamp, which is always in UTC\n                $timestamp = new DateTime('@' . (int) $value, new DateTimeZone('UTC'));\n                return $checkDateTimeInfo ? [$timestamp, true, true] : $timestamp;\n            }\n            if (\n                ($timestamp = DateTime::createFromFormat(\n                    'Y-m-d|',\n                    $value,\n                    new DateTimeZone($this->defaultTimeZone)\n                )\n                ) !== false\n            ) { // try Y-m-d format (support invalid dates like 2012-13-01)\n                return $checkDateTimeInfo ? [$timestamp, false, true] : $timestamp;\n            }\n            if (\n                ($timestamp = DateTime::createFromFormat(\n                    'Y-m-d H:i:s',\n                    $value,\n                    new DateTimeZone($this->defaultTimeZone)\n                )\n                ) !== false\n            ) { // try Y-m-d H:i:s format (support invalid dates like 2012-13-01 12:63:12)\n                return $checkDateTimeInfo ? [$timestamp, true, true] : $timestamp;\n            }\n            // finally try to create a DateTime object with the value\n            if ($checkDateTimeInfo) {\n                $timestamp = new DateTime($value, new DateTimeZone($this->defaultTimeZone));\n                $info = date_parse($value);\n                return [\n                    $timestamp,\n                    !($info['hour'] === false && $info['minute'] === false && $info['second'] === false),\n                    !($info['year'] === false && $info['month'] === false && $info['day'] === false && empty($info['zone'])),\n                ];\n            }\n\n            return new DateTime($value, new DateTimeZone($this->defaultTimeZone));\n        } catch (\\Exception $e) {\n            throw new InvalidArgumentException(\"'$value' is not a valid date time value: \" . $e->getMessage()\n                . \"\\n\" . print_r(DateTime::getLastErrors(), true), $e->getCode(), $e);\n        }\n    }\n\n    /**\n     * Formats a date, time or datetime in a float number as UNIX timestamp (seconds since 01-01-1970).\n     * @param int|string|DateTime|DateTimeInterface|null $value the value to be formatted. The following\n     * types of value are supported:\n     *\n     * - an integer representing a UNIX timestamp\n     * - a string that can be [parsed to create a DateTime object](https://www.php.net/manual/en/datetime.formats.php).\n     *   The timestamp is assumed to be in [[defaultTimeZone]] unless a time zone is explicitly given.\n     * - a PHP [DateTime](https://www.php.net/manual/en/class.datetime.php) object\n     *\n     * @return string the formatted result.\n     */\n    public function asTimestamp($value)\n    {\n        if ($value === null) {\n            return $this->nullDisplay;\n        }\n        $timestamp = $this->normalizeDatetimeValue($value);\n        return number_format($timestamp->format('U'), 0, '.', '');\n    }\n\n    /**\n     * Formats the value as the time interval between a date and now in human readable form.\n     *\n     * This method can be used in three different ways:\n     *\n     * 1. Using a timestamp that is relative to `now`.\n     * 2. Using a timestamp that is relative to the `$referenceTime`.\n     * 3. Using a `DateInterval` object.\n     *\n     * @param int|string|DateTime|DateTimeInterface|DateInterval|null $value the value to be formatted. The following\n     * types of value are supported:\n     *\n     * - an integer representing a UNIX timestamp\n     * - a string that can be [parsed to create a DateTime object](https://www.php.net/manual/en/datetime.formats.php).\n     *   The timestamp is assumed to be in [[defaultTimeZone]] unless a time zone is explicitly given.\n     * - a PHP [DateTime](https://www.php.net/manual/en/class.datetime.php) object\n     * - a PHP DateInterval object (a positive time interval will refer to the past, a negative one to the future)\n     *\n     * @param int|string|DateTime|DateTimeInterface|null $referenceTime if specified the value is used as a reference time instead of `now`\n     * when `$value` is not a `DateInterval` object.\n     * @return string the formatted result.\n     * @throws InvalidArgumentException if the input value can not be evaluated as a date value.\n     */\n    public function asRelativeTime($value, $referenceTime = null)\n    {\n        if ($value === null) {\n            return $this->nullDisplay;\n        }\n\n        if ($value instanceof DateInterval) {\n            $interval = $value;\n        } else {\n            $timestamp = $this->normalizeDatetimeValue($value);\n            $timeZone = new DateTimeZone($this->timeZone);\n\n            if ($referenceTime === null) {\n                $dateNow = new DateTime('now', $timeZone);\n            } else {\n                $dateNow = $this->normalizeDatetimeValue($referenceTime);\n                $dateNow->setTimezone($timeZone);\n            }\n\n            $dateThen = $timestamp->setTimezone($timeZone);\n            $interval = $dateThen->diff($dateNow);\n        }\n\n        if ($interval->invert) {\n            if ($interval->y >= 1) {\n                return Yii::t('yii', 'in {delta, plural, =1{a year} other{# years}}', ['delta' => $interval->y], $this->language);\n            }\n            if ($interval->m >= 1) {\n                return Yii::t('yii', 'in {delta, plural, =1{a month} other{# months}}', ['delta' => $interval->m], $this->language);\n            }\n            if ($interval->d >= 1) {\n                return Yii::t('yii', 'in {delta, plural, =1{a day} other{# days}}', ['delta' => $interval->d], $this->language);\n            }\n            if ($interval->h >= 1) {\n                return Yii::t('yii', 'in {delta, plural, =1{an hour} other{# hours}}', ['delta' => $interval->h], $this->language);\n            }\n            if ($interval->i >= 1) {\n                return Yii::t('yii', 'in {delta, plural, =1{a minute} other{# minutes}}', ['delta' => $interval->i], $this->language);\n            }\n            if ($interval->s == 0) {\n                return Yii::t('yii', 'just now', [], $this->language);\n            }\n\n            return Yii::t('yii', 'in {delta, plural, =1{a second} other{# seconds}}', ['delta' => $interval->s], $this->language);\n        }\n\n        if ($interval->y >= 1) {\n            return Yii::t('yii', '{delta, plural, =1{a year} other{# years}} ago', ['delta' => $interval->y], $this->language);\n        }\n        if ($interval->m >= 1) {\n            return Yii::t('yii', '{delta, plural, =1{a month} other{# months}} ago', ['delta' => $interval->m], $this->language);\n        }\n        if ($interval->d >= 1) {\n            return Yii::t('yii', '{delta, plural, =1{a day} other{# days}} ago', ['delta' => $interval->d], $this->language);\n        }\n        if ($interval->h >= 1) {\n            return Yii::t('yii', '{delta, plural, =1{an hour} other{# hours}} ago', ['delta' => $interval->h], $this->language);\n        }\n        if ($interval->i >= 1) {\n            return Yii::t('yii', '{delta, plural, =1{a minute} other{# minutes}} ago', ['delta' => $interval->i], $this->language);\n        }\n        if ($interval->s == 0) {\n            return Yii::t('yii', 'just now', [], $this->language);\n        }\n\n        return Yii::t('yii', '{delta, plural, =1{a second} other{# seconds}} ago', ['delta' => $interval->s], $this->language);\n    }\n\n    /**\n     * Represents the value as duration in human readable format.\n     *\n     * @param DateInterval|string|int|null $value the value to be formatted. Acceptable formats:\n     *  - [DateInterval object](https://www.php.net/manual/ru/class.dateinterval.php)\n     *  - integer - number of seconds. For example: value `131` represents `2 minutes, 11 seconds`\n     *  - ISO8601 duration format. For example, all of these values represent `1 day, 2 hours, 30 minutes` duration:\n     *    `2015-01-01T13:00:00Z/2015-01-02T13:30:00Z` - between two datetime values\n     *    `2015-01-01T13:00:00Z/P1D2H30M` - time interval after datetime value\n     *    `P1D2H30M/2015-01-02T13:30:00Z` - time interval before datetime value\n     *    `P1D2H30M` - simply a date interval\n     *    `P-1D2H30M` - a negative date interval (`-1 day, 2 hours, 30 minutes`)\n     *\n     * @param string $implodeString will be used to concatenate duration parts. Defaults to `, `.\n     * @param string $negativeSign will be prefixed to the formatted duration, when it is negative. Defaults to `-`.\n     * @return string the formatted duration.\n     * @since 2.0.7\n     */\n    public function asDuration($value, $implodeString = ', ', $negativeSign = '-')\n    {\n        if ($value === null) {\n            return $this->nullDisplay;\n        }\n\n        if ($value instanceof DateInterval) {\n            $isNegative = $value->invert;\n            $interval = $value;\n        } elseif (is_numeric($value)) {\n            $isNegative = $value < 0;\n            $zeroDateTime = (new DateTime())->setTimestamp(0);\n            $valueDateTime = (new DateTime())->setTimestamp(abs((int) $value));\n            $interval = $valueDateTime->diff($zeroDateTime);\n        } elseif (strncmp($value, 'P-', 2) === 0) {\n            $interval = new DateInterval('P' . substr($value, 2));\n            $isNegative = true;\n        } else {\n            $interval = new DateInterval($value);\n            $isNegative = $interval->invert;\n        }\n\n        $parts = [];\n        if ($interval->y > 0) {\n            $parts[] = Yii::t('yii', '{delta, plural, =1{1 year} other{# years}}', ['delta' => $interval->y], $this->language);\n        }\n        if ($interval->m > 0) {\n            $parts[] = Yii::t('yii', '{delta, plural, =1{1 month} other{# months}}', ['delta' => $interval->m], $this->language);\n        }\n        if ($interval->d > 0) {\n            $parts[] = Yii::t('yii', '{delta, plural, =1{1 day} other{# days}}', ['delta' => $interval->d], $this->language);\n        }\n        if ($interval->h > 0) {\n            $parts[] = Yii::t('yii', '{delta, plural, =1{1 hour} other{# hours}}', ['delta' => $interval->h], $this->language);\n        }\n        if ($interval->i > 0) {\n            $parts[] = Yii::t('yii', '{delta, plural, =1{1 minute} other{# minutes}}', ['delta' => $interval->i], $this->language);\n        }\n        if ($interval->s > 0) {\n            $parts[] = Yii::t('yii', '{delta, plural, =1{1 second} other{# seconds}}', ['delta' => $interval->s], $this->language);\n        }\n        if ($interval->s === 0 && empty($parts)) {\n            $parts[] = Yii::t('yii', '{delta, plural, =1{1 second} other{# seconds}}', ['delta' => $interval->s], $this->language);\n            $isNegative = false;\n        }\n\n        return empty($parts) ? $this->nullDisplay : (($isNegative ? $negativeSign : '') . implode($implodeString, $parts));\n    }\n\n\n    // number formats\n\n\n    /**\n     * Formats the value as an integer number by removing any decimal digits without rounding.\n     *\n     * Since 2.0.16 numbers that are mispresented after normalization are formatted as strings using fallback function\n     * without [PHP intl extension](https://www.php.net/manual/en/book.intl.php) support. For very big numbers it's\n     * recommended to pass them as strings and not use scientific notation otherwise the output might be wrong.\n     *\n     * @param mixed $value the value to be formatted.\n     * @param array $options optional configuration for the number formatter. This parameter will be merged with [[numberFormatterOptions]].\n     * @param array $textOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterTextOptions]].\n     * @return string the formatted result.\n     * @throws InvalidArgumentException if the input value is not numeric or the formatting failed.\n     */\n    public function asInteger($value, $options = [], $textOptions = [])\n    {\n        if ($value === null) {\n            return $this->nullDisplay;\n        }\n\n        $normalizedValue = $this->normalizeNumericValue($value);\n\n        if ($this->isNormalizedValueMispresented($value, $normalizedValue)) {\n            return $this->asIntegerStringFallback((string) $value);\n        }\n\n        if ($this->_intlLoaded) {\n            $f = $this->createNumberFormatter(NumberFormatter::DECIMAL, null, $options, $textOptions);\n            $f->setAttribute(NumberFormatter::FRACTION_DIGITS, 0);\n            if (($result = $f->format($normalizedValue, NumberFormatter::TYPE_INT64)) === false) {\n                throw new InvalidArgumentException('Formatting integer value failed: ' . $f->getErrorCode() . ' ' . $f->getErrorMessage());\n            }\n\n            return $result;\n        }\n\n        return number_format((int) $normalizedValue, 0, $this->decimalSeparator, $this->thousandSeparator);\n    }\n\n    /**\n     * Formats the value as a decimal number.\n     *\n     * Property [[decimalSeparator]] will be used to represent the decimal point. The\n     * value is rounded automatically to the defined decimal digits.\n     *\n     * Since 2.0.16 numbers that are mispresented after normalization are formatted as strings using fallback function\n     * without [PHP intl extension](https://www.php.net/manual/en/book.intl.php) support. For very big numbers it's\n     * recommended to pass them as strings and not use scientific notation otherwise the output might be wrong.\n     *\n     * @param mixed $value the value to be formatted.\n     * @param int|null $decimals the number of digits after the decimal point.\n     * If not given, the number of digits depends in the input value and is determined based on\n     * `NumberFormatter::MIN_FRACTION_DIGITS` and `NumberFormatter::MAX_FRACTION_DIGITS`, which can be configured\n     * using [[$numberFormatterOptions]].\n     * If the PHP intl extension is not available, the default value is `2`.\n     * If you want consistent behavior between environments where intl is available and not, you should explicitly\n     * specify a value here.\n     * @param array $options optional configuration for the number formatter. This parameter will be merged with [[numberFormatterOptions]].\n     * @param array $textOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterTextOptions]].\n     * @return string the formatted result.\n     * @throws InvalidArgumentException if the input value is not numeric or the formatting failed.\n     * @see decimalSeparator\n     * @see thousandSeparator\n     */\n    public function asDecimal($value, $decimals = null, $options = [], $textOptions = [])\n    {\n        if ($value === null) {\n            return $this->nullDisplay;\n        }\n\n        $normalizedValue = $this->normalizeNumericValue($value);\n\n        if ($this->isNormalizedValueMispresented($value, $normalizedValue)) {\n            return $this->asDecimalStringFallback((string) $value, $decimals);\n        }\n\n        if ($this->_intlLoaded) {\n            $f = $this->createNumberFormatter(NumberFormatter::DECIMAL, $decimals, $options, $textOptions);\n            if (($result = $f->format($normalizedValue)) === false) {\n                throw new InvalidArgumentException('Formatting decimal value failed: ' . $f->getErrorCode() . ' ' . $f->getErrorMessage());\n            }\n\n            return $result;\n        }\n\n        if ($decimals === null) {\n            $decimals = 2;\n        }\n\n        return number_format($normalizedValue, $decimals, $this->decimalSeparator, $this->thousandSeparator);\n    }\n\n    /**\n     * Formats the value as a percent number with \"%\" sign.\n     *\n     * Since 2.0.16 numbers that are mispresented after normalization are formatted as strings using fallback function\n     * without [PHP intl extension](https://www.php.net/manual/en/book.intl.php) support. For very big numbers it's\n     * recommended to pass them as strings and not use scientific notation otherwise the output might be wrong.\n     *\n     * @param mixed $value the value to be formatted. It must be a factor e.g. `0.75` will result in `75%`.\n     * @param int|null $decimals the number of digits after the decimal point.\n     * If not given, the number of digits depends in the input value and is determined based on\n     * `NumberFormatter::MIN_FRACTION_DIGITS` and `NumberFormatter::MAX_FRACTION_DIGITS`, which can be configured\n     * using [[$numberFormatterOptions]].\n     * If the PHP intl extension is not available, the default value is `0`.\n     * If you want consistent behavior between environments where intl is available and not, you should explicitly\n     * specify a value here.\n     * @param array $options optional configuration for the number formatter. This parameter will be merged with [[numberFormatterOptions]].\n     * @param array $textOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterTextOptions]].\n     * @return string the formatted result.\n     * @throws InvalidArgumentException if the input value is not numeric or the formatting failed.\n     */\n    public function asPercent($value, $decimals = null, $options = [], $textOptions = [])\n    {\n        if ($value === null) {\n            return $this->nullDisplay;\n        }\n\n        $normalizedValue = $this->normalizeNumericValue($value);\n\n        if ($this->isNormalizedValueMispresented($value, $normalizedValue)) {\n            return $this->asPercentStringFallback((string) $value, $decimals);\n        }\n\n        if ($this->_intlLoaded) {\n            $f = $this->createNumberFormatter(NumberFormatter::PERCENT, $decimals, $options, $textOptions);\n            if (($result = $f->format($normalizedValue)) === false) {\n                throw new InvalidArgumentException('Formatting percent value failed: ' . $f->getErrorCode() . ' ' . $f->getErrorMessage());\n            }\n\n            return $result;\n        }\n\n        if ($decimals === null) {\n            $decimals = 0;\n        }\n\n        $normalizedValue *= 100;\n        return number_format($normalizedValue, $decimals, $this->decimalSeparator, $this->thousandSeparator) . '%';\n    }\n\n    /**\n     * Formats the value as a scientific number.\n     *\n     * @param mixed $value the value to be formatted.\n     * @param int|null $decimals the number of digits after the decimal point.\n     * If not given, the number of digits depends in the input value and is determined based on\n     * `NumberFormatter::MIN_FRACTION_DIGITS` and `NumberFormatter::MAX_FRACTION_DIGITS`, which can be configured\n     * using [[$numberFormatterOptions]].\n     * If the [PHP intl extension](https://www.php.net/manual/en/book.intl.php) is not available, the default value\n     * depends on your PHP configuration.\n     * If you want consistent behavior between environments where intl is available and not, you should explicitly\n     * specify a value here.\n     * @param array $options optional configuration for the number formatter. This parameter will be merged with [[numberFormatterOptions]].\n     * @param array $textOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterTextOptions]].\n     * @return string the formatted result.\n     * @throws InvalidArgumentException if the input value is not numeric or the formatting failed.\n     */\n    public function asScientific($value, $decimals = null, $options = [], $textOptions = [])\n    {\n        if ($value === null) {\n            return $this->nullDisplay;\n        }\n\n        $value = $this->normalizeNumericValue($value);\n\n        if ($this->_intlLoaded) {\n            $f = $this->createNumberFormatter(NumberFormatter::SCIENTIFIC, $decimals, $options, $textOptions);\n\n            if (($result = $f->format($value)) === false) {\n                throw new InvalidArgumentException('Formatting scientific number value failed: ' . $f->getErrorCode() . ' ' . $f->getErrorMessage());\n            }\n\n            return $result;\n        }\n\n        if ($decimals !== null) {\n            return sprintf(\"%.{$decimals}E\", $value);\n        }\n\n        // PHP 8.5+ changed sprintf('%.E') behavior: empty precision now defaults to '0' instead of '6'\n        // Specify explicit precision to maintain backward compatibility\n        // @link https://github.com/php/php-src/commit/5ed8b2be5533fbd4db95d9724d268eb9c9741f14\n        $format = PHP_VERSION_ID >= 80500 ? '%.6E' : '%.E';\n\n        return sprintf($format, $value);\n    }\n\n    /**\n     * Formats the value as a currency number.\n     *\n     * This function does not require the [PHP intl extension](https://www.php.net/manual/en/book.intl.php) to be installed\n     * to work, but it is highly recommended to install it to get good formatting results.\n     *\n     * Since 2.0.16 numbers that are mispresented after normalization are formatted as strings using fallback function\n     * without PHP intl extension support. For very big numbers it's recommended to pass them as strings and not use\n     * scientific notation otherwise the output might be wrong.\n     *\n     * @param mixed $value the value to be formatted.\n     * @param string|null $currency the 3-letter ISO 4217 currency code indicating the currency to use.\n     * If null, [[currencyCode]] will be used.\n     * @param array $options optional configuration for the number formatter. This parameter will be merged with [[numberFormatterOptions]].\n     * @param array $textOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterTextOptions]].\n     * @return string the formatted result.\n     * @throws InvalidArgumentException if the input value is not numeric or the formatting failed.\n     * @throws InvalidConfigException if no currency is given and [[currencyCode]] is not defined.\n     */\n    public function asCurrency($value, $currency = null, $options = [], $textOptions = [])\n    {\n        if ($value === null) {\n            return $this->nullDisplay;\n        }\n\n        $normalizedValue = $this->normalizeNumericValue($value);\n\n        if ($this->isNormalizedValueMispresented($value, $normalizedValue)) {\n            return $this->asCurrencyStringFallback((string) $value, $currency);\n        }\n\n        if ($this->_intlLoaded) {\n            $currency = $currency ?: $this->currencyCode;\n            // currency code must be set before fraction digits\n            // https://www.php.net/manual/en/numberformatter.formatcurrency.php#114376\n            if ($currency && !isset($textOptions[NumberFormatter::CURRENCY_CODE])) {\n                $textOptions[NumberFormatter::CURRENCY_CODE] = $currency;\n            }\n            $formatter = $this->createNumberFormatter(NumberFormatter::CURRENCY, null, $options, $textOptions);\n            if ($currency === null) {\n                $result = $formatter->format($normalizedValue);\n            } else {\n                $result = $formatter->formatCurrency($normalizedValue, $currency);\n            }\n            if ($result === false) {\n                throw new InvalidArgumentException('Formatting currency value failed: ' . $formatter->getErrorCode() . ' ' . $formatter->getErrorMessage());\n            }\n\n            return $result;\n        }\n\n        if ($currency === null) {\n            if ($this->currencyCode === null) {\n                throw new InvalidConfigException('The default currency code for the formatter is not defined and the php intl extension is not installed which could take the default currency from the locale.');\n            }\n            $currency = $this->currencyCode;\n        }\n\n        return $currency . ' ' . $this->asDecimal($normalizedValue, 2, $options, $textOptions);\n    }\n\n    /**\n     * Formats the value as a number spellout.\n     *\n     * This function requires the [PHP intl extension](https://www.php.net/manual/en/book.intl.php) to be installed.\n     *\n     * This formatter does not work well with very big numbers.\n     *\n     * @param mixed $value the value to be formatted\n     * @return string the formatted result.\n     * @throws InvalidArgumentException if the input value is not numeric or the formatting failed.\n     * @throws InvalidConfigException when the [PHP intl extension](https://www.php.net/manual/en/book.intl.php) is not available.\n     */\n    public function asSpellout($value)\n    {\n        if ($value === null) {\n            return $this->nullDisplay;\n        }\n        $value = $this->normalizeNumericValue($value);\n        if ($this->_intlLoaded) {\n            $f = $this->createNumberFormatter(NumberFormatter::SPELLOUT);\n            if (($result = $f->format($value)) === false) {\n                throw new InvalidArgumentException('Formatting number as spellout failed: ' . $f->getErrorCode() . ' ' . $f->getErrorMessage());\n            }\n\n            return $result;\n        }\n\n        throw new InvalidConfigException('Format as Spellout is only supported when PHP intl extension is installed.');\n    }\n\n    /**\n     * Formats the value as a ordinal value of a number.\n     *\n     * This function requires the [PHP intl extension](https://www.php.net/manual/en/book.intl.php) to be installed.\n     *\n     * This formatter does not work well with very big numbers.\n     *\n     * @param mixed $value the value to be formatted\n     * @return string the formatted result.\n     * @throws InvalidArgumentException if the input value is not numeric or the formatting failed.\n     * @throws InvalidConfigException when the [PHP intl extension](https://www.php.net/manual/en/book.intl.php) is not available.\n     */\n    public function asOrdinal($value)\n    {\n        if ($value === null) {\n            return $this->nullDisplay;\n        }\n        $value = $this->normalizeNumericValue($value);\n        if ($this->_intlLoaded) {\n            $f = $this->createNumberFormatter(NumberFormatter::ORDINAL);\n            if (($result = $f->format($value)) === false) {\n                throw new InvalidArgumentException('Formatting number as ordinal failed: ' . $f->getErrorCode() . ' ' . $f->getErrorMessage());\n            }\n\n            return $result;\n        }\n\n        throw new InvalidConfigException('Format as Ordinal is only supported when PHP intl extension is installed.');\n    }\n\n    /**\n     * Formats the value in bytes as a size in human readable form for example `12 kB`.\n     *\n     * This is the short form of [[asSize]].\n     *\n     * If [[sizeFormatBase]] is 1024, [binary prefixes](https://en.wikipedia.org/wiki/Binary_prefix) (e.g. kibibyte/KiB, mebibyte/MiB, ...)\n     * are used in the formatting result.\n     *\n     * @param string|int|float|null $value value in bytes to be formatted.\n     * @param int|null $decimals the number of digits after the decimal point.\n     * @param array $options optional configuration for the number formatter. This parameter will be merged with [[numberFormatterOptions]].\n     * @param array $textOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterTextOptions]].\n     * @return string the formatted result.\n     * @throws InvalidArgumentException if the input value is not numeric or the formatting failed.\n     * @see sizeFormatBase\n     * @see asSize\n     */\n    public function asShortSize($value, $decimals = null, $options = [], $textOptions = [])\n    {\n        if ($value === null) {\n            return $this->nullDisplay;\n        }\n\n        list($params, $position) = $this->formatNumber($value, $decimals, 4, $this->sizeFormatBase, $options, $textOptions);\n\n        if ($this->sizeFormatBase == 1024) {\n            switch ($position) {\n                case 0:\n                    return Yii::t('yii', '{nFormatted} B', $params, $this->language);\n                case 1:\n                    return Yii::t('yii', '{nFormatted} KiB', $params, $this->language);\n                case 2:\n                    return Yii::t('yii', '{nFormatted} MiB', $params, $this->language);\n                case 3:\n                    return Yii::t('yii', '{nFormatted} GiB', $params, $this->language);\n                case 4:\n                    return Yii::t('yii', '{nFormatted} TiB', $params, $this->language);\n                default:\n                    return Yii::t('yii', '{nFormatted} PiB', $params, $this->language);\n            }\n        } else {\n            switch ($position) {\n                case 0:\n                    return Yii::t('yii', '{nFormatted} B', $params, $this->language);\n                case 1:\n                    return Yii::t('yii', '{nFormatted} kB', $params, $this->language);\n                case 2:\n                    return Yii::t('yii', '{nFormatted} MB', $params, $this->language);\n                case 3:\n                    return Yii::t('yii', '{nFormatted} GB', $params, $this->language);\n                case 4:\n                    return Yii::t('yii', '{nFormatted} TB', $params, $this->language);\n                default:\n                    return Yii::t('yii', '{nFormatted} PB', $params, $this->language);\n            }\n        }\n    }\n\n    /**\n     * Formats the value in bytes as a size in human readable form, for example `12 kilobytes`.\n     *\n     * If [[sizeFormatBase]] is 1024, [binary prefixes](https://en.wikipedia.org/wiki/Binary_prefix) (e.g. kibibyte/KiB, mebibyte/MiB, ...)\n     * are used in the formatting result.\n     *\n     * @param string|int|float|null $value value in bytes to be formatted.\n     * @param int|null $decimals the number of digits after the decimal point.\n     * @param array $options optional configuration for the number formatter. This parameter will be merged with [[numberFormatterOptions]].\n     * @param array $textOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterTextOptions]].\n     * @return string the formatted result.\n     * @throws InvalidArgumentException if the input value is not numeric or the formatting failed.\n     * @see sizeFormatBase\n     * @see asShortSize\n     */\n    public function asSize($value, $decimals = null, $options = [], $textOptions = [])\n    {\n        if ($value === null) {\n            return $this->nullDisplay;\n        }\n\n        list($params, $position) = $this->formatNumber($value, $decimals, 4, $this->sizeFormatBase, $options, $textOptions);\n\n        if ($this->sizeFormatBase == 1024) {\n            switch ($position) {\n                case 0:\n                    return Yii::t('yii', '{nFormatted} {n, plural, =1{byte} other{bytes}}', $params, $this->language);\n                case 1:\n                    return Yii::t('yii', '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}', $params, $this->language);\n                case 2:\n                    return Yii::t('yii', '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}', $params, $this->language);\n                case 3:\n                    return Yii::t('yii', '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}', $params, $this->language);\n                case 4:\n                    return Yii::t('yii', '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}', $params, $this->language);\n                default:\n                    return Yii::t('yii', '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}', $params, $this->language);\n            }\n        } else {\n            switch ($position) {\n                case 0:\n                    return Yii::t('yii', '{nFormatted} {n, plural, =1{byte} other{bytes}}', $params, $this->language);\n                case 1:\n                    return Yii::t('yii', '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}', $params, $this->language);\n                case 2:\n                    return Yii::t('yii', '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}', $params, $this->language);\n                case 3:\n                    return Yii::t('yii', '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}', $params, $this->language);\n                case 4:\n                    return Yii::t('yii', '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}', $params, $this->language);\n                default:\n                    return Yii::t('yii', '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}', $params, $this->language);\n            }\n        }\n    }\n\n    /**\n     * Formats the value as a length in human readable form for example `12 meters`.\n     * Check properties [[baseUnits]] if you need to change unit of value as the multiplier\n     * of the smallest unit and [[systemOfUnits]] to switch between [[UNIT_SYSTEM_METRIC]] or [[UNIT_SYSTEM_IMPERIAL]].\n     *\n     * @param float|int $value value to be formatted.\n     * @param int|null $decimals the number of digits after the decimal point.\n     * @param array $numberOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterOptions]].\n     * @param array $textOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterTextOptions]].\n     * @return string the formatted result.\n     * @throws InvalidArgumentException if the input value is not numeric or the formatting failed.\n     * @throws InvalidConfigException when INTL is not installed or does not contain required information.\n     * @see asLength\n     * @since 2.0.13\n     * @author John Was <janek.jan@gmail.com>\n     */\n    public function asLength($value, $decimals = null, $numberOptions = [], $textOptions = [])\n    {\n        return $this->formatUnit(self::UNIT_LENGTH, self::FORMAT_WIDTH_LONG, $value, $decimals, $numberOptions, $textOptions);\n    }\n\n    /**\n     * Formats the value as a length in human readable form for example `12 m`.\n     * This is the short form of [[asLength]].\n     *\n     * Check properties [[baseUnits]] if you need to change unit of value as the multiplier\n     * of the smallest unit and [[systemOfUnits]] to switch between [[UNIT_SYSTEM_METRIC]] or [[UNIT_SYSTEM_IMPERIAL]].\n     *\n     * @param float|int $value value to be formatted.\n     * @param int|null $decimals the number of digits after the decimal point.\n     * @param array $options optional configuration for the number formatter. This parameter will be merged with [[numberFormatterOptions]].\n     * @param array $textOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterTextOptions]].\n     * @return string the formatted result.\n     * @throws InvalidArgumentException if the input value is not numeric or the formatting failed.\n     * @throws InvalidConfigException when INTL is not installed or does not contain required information.\n     * @see asLength\n     * @since 2.0.13\n     * @author John Was <janek.jan@gmail.com>\n     */\n    public function asShortLength($value, $decimals = null, $options = [], $textOptions = [])\n    {\n        return $this->formatUnit(self::UNIT_LENGTH, self::FORMAT_WIDTH_SHORT, $value, $decimals, $options, $textOptions);\n    }\n\n    /**\n     * Formats the value as a weight in human readable form for example `12 kilograms`.\n     * Check properties [[baseUnits]] if you need to change unit of value as the multiplier\n     * of the smallest unit and [[systemOfUnits]] to switch between [[UNIT_SYSTEM_METRIC]] or [[UNIT_SYSTEM_IMPERIAL]].\n     *\n     * @param float|int $value value to be formatted.\n     * @param int|null $decimals the number of digits after the decimal point.\n     * @param array $options optional configuration for the number formatter. This parameter will be merged with [[numberFormatterOptions]].\n     * @param array $textOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterTextOptions]].\n     * @return string the formatted result.\n     * @throws InvalidArgumentException if the input value is not numeric or the formatting failed.\n     * @throws InvalidConfigException when INTL is not installed or does not contain required information.\n     * @since 2.0.13\n     * @author John Was <janek.jan@gmail.com>\n     */\n    public function asWeight($value, $decimals = null, $options = [], $textOptions = [])\n    {\n        return $this->formatUnit(self::UNIT_WEIGHT, self::FORMAT_WIDTH_LONG, $value, $decimals, $options, $textOptions);\n    }\n\n    /**\n     * Formats the value as a weight in human readable form for example `12 kg`.\n     * This is the short form of [[asWeight]].\n     *\n     * Check properties [[baseUnits]] if you need to change unit of value as the multiplier\n     * of the smallest unit and [[systemOfUnits]] to switch between [[UNIT_SYSTEM_METRIC]] or [[UNIT_SYSTEM_IMPERIAL]].\n     *\n     * @param float|int $value value to be formatted.\n     * @param int|null $decimals the number of digits after the decimal point.\n     * @param array $options optional configuration for the number formatter. This parameter will be merged with [[numberFormatterOptions]].\n     * @param array $textOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterTextOptions]].\n     * @return string the formatted result.\n     * @throws InvalidArgumentException if the input value is not numeric or the formatting failed.\n     * @throws InvalidConfigException when INTL is not installed or does not contain required information.\n     * @since 2.0.13\n     * @author John Was <janek.jan@gmail.com>\n     */\n    public function asShortWeight($value, $decimals = null, $options = [], $textOptions = [])\n    {\n        return $this->formatUnit(self::UNIT_WEIGHT, self::FORMAT_WIDTH_SHORT, $value, $decimals, $options, $textOptions);\n    }\n\n    /**\n     * @param string $unitType one of [[UNIT_WEIGHT]], [[UNIT_LENGTH]]\n     * @param string $unitFormat one of [[FORMAT_WIDTH_SHORT]], [[FORMAT_WIDTH_LONG]]\n     * @param float|int|null $value to be formatted\n     * @param int|null $decimals the number of digits after the decimal point.\n     * @param array $options optional configuration for the number formatter. This parameter will be merged with [[numberFormatterOptions]].\n     * @param array $textOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterTextOptions]].\n     * @return string\n     * @throws InvalidConfigException when INTL is not installed or does not contain required information\n     */\n    private function formatUnit($unitType, $unitFormat, $value, $decimals, $options, $textOptions)\n    {\n        if ($value === null) {\n            return $this->nullDisplay;\n        }\n\n        $multipliers = array_values($this->measureUnits[$unitType][$this->systemOfUnits]);\n\n        list($params, $position) = $this->formatNumber(\n            $this->normalizeNumericValue($value) * $this->baseUnits[$unitType][$this->systemOfUnits],\n            $decimals,\n            null,\n            $multipliers,\n            $options,\n            $textOptions\n        );\n\n        $message = $this->getUnitMessage($unitType, $unitFormat, $this->systemOfUnits, $position);\n\n        return (new \\MessageFormatter($this->locale, $message))->format([\n            '0' => $params['nFormatted'],\n            'n' => $params['n'],\n        ]);\n    }\n\n    /**\n     * @param string $unitType one of [[UNIT_WEIGHT]], [[UNIT_LENGTH]]\n     * @param string $unitFormat one of [[FORMAT_WIDTH_SHORT]], [[FORMAT_WIDTH_LONG]]\n     * @param string|null $system either [[UNIT_SYSTEM_METRIC]] or [[UNIT_SYSTEM_IMPERIAL]]. When `null`, property [[systemOfUnits]] will be used.\n     * @param int $position internal position of size unit\n     * @return string\n     * @throws InvalidConfigException when INTL is not installed or does not contain required information\n     */\n    private function getUnitMessage($unitType, $unitFormat, $system, $position)\n    {\n        if (isset($this->_unitMessages[$unitType][$unitFormat][$system][$position])) {\n            return $this->_unitMessages[$unitType][$unitFormat][$system][$position];\n        }\n        if (!$this->_intlLoaded) {\n            throw new InvalidConfigException('Format of ' . $unitType . ' is only supported when PHP intl extension is installed.');\n        }\n\n        if ($this->_resourceBundle === null) {\n            try {\n                $this->_resourceBundle = new \\ResourceBundle($this->locale, 'ICUDATA-unit');\n            } catch (\\IntlException $e) {\n                throw new InvalidConfigException('Current ICU data does not contain information about measure units. Check system requirements.');\n            }\n        }\n        $unitNames = array_keys($this->measureUnits[$unitType][$system]);\n        $bundleKey = 'units' . ($unitFormat === self::FORMAT_WIDTH_SHORT ? 'Short' : '');\n\n        $unitBundle = $this->_resourceBundle[$bundleKey][$unitType][$unitNames[$position]];\n        if ($unitBundle === null) {\n            throw new InvalidConfigException(\n                'Current ICU data version does not contain information about unit type \"' . $unitType\n                . '\" and unit measure \"' . $unitNames[$position] . '\". Check system requirements.'\n            );\n        }\n\n        $message = [];\n        foreach ($unitBundle as $key => $value) {\n            if ($key === 'dnam' || $key === 'case') {\n                continue;\n            }\n            $message[] = \"$key{{$value}}\";\n        }\n\n        return $this->_unitMessages[$unitType][$unitFormat][$system][$position] = '{n, plural, ' . implode(' ', $message) . '}';\n    }\n\n    /**\n     * Given the value in bytes formats number part of the human readable form.\n     *\n     * @param string|int|float $value value in bytes to be formatted.\n     * @param int|null $decimals the number of digits after the decimal point\n     * @param int $maxPosition maximum internal position of size unit, ignored if $formatBase is an array\n     * @param array|int $formatBase the base at which each next unit is calculated, either 1000 or 1024, or an array\n     * @param array $options optional configuration for the number formatter. This parameter will be merged with [[numberFormatterOptions]].\n     * @param array $textOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterTextOptions]].\n     * @return array [parameters for Yii::t containing formatted number, internal position of size unit]\n     * @throws InvalidArgumentException if the input value is not numeric or the formatting failed.\n     * @since 2.0.32\n     */\n    protected function formatNumber($value, $decimals, $maxPosition, $formatBase, $options, $textOptions)\n    {\n        $value = $this->normalizeNumericValue($value);\n\n        $position = 0;\n        if (is_array($formatBase)) {\n            $maxPosition = count($formatBase) - 1;\n        }\n        do {\n            if (is_array($formatBase)) {\n                if (!isset($formatBase[$position + 1])) {\n                    break;\n                }\n\n                if (abs($value) < $formatBase[$position + 1]) {\n                    break;\n                }\n            } else {\n                if (abs($value) < $formatBase) {\n                    break;\n                }\n                $value /= $formatBase;\n            }\n            $position++;\n        } while ($position < $maxPosition + 1);\n\n        if (is_array($formatBase) && $position !== 0) {\n            $value /= $formatBase[$position];\n        }\n\n        // no decimals for smallest unit\n        if ($position === 0) {\n            $decimals = 0;\n        } elseif ($decimals !== null) {\n            $value = round($value, $decimals);\n        }\n        // disable grouping for edge cases like 1023 to get 1023 B instead of 1,023 B\n        $oldThousandSeparator = $this->thousandSeparator;\n        $this->thousandSeparator = '';\n        if ($this->_intlLoaded && !isset($options[NumberFormatter::GROUPING_USED])) {\n            $options[NumberFormatter::GROUPING_USED] = 0;\n        }\n        // format the size value\n        $params = [\n            // this is the unformatted number used for the plural rule\n            // abs() to make sure the plural rules work correctly on negative numbers, intl does not cover this\n            // https://english.stackexchange.com/questions/9735/is-1-followed-by-a-singular-or-plural-noun\n            'n' => abs($value),\n            // this is the formatted number used for display\n            'nFormatted' => $this->asDecimal($value, $decimals, $options, $textOptions),\n        ];\n        $this->thousandSeparator = $oldThousandSeparator;\n\n        return [$params, $position];\n    }\n\n    /**\n     * Normalizes a numeric input value.\n     *\n     * - everything [empty](https://www.php.net/manual/en/function.empty.php) will result in `0`\n     * - a [numeric](https://www.php.net/manual/en/function.is-numeric.php) string will be casted to float\n     * - everything else will be returned if it is [numeric](https://www.php.net/manual/en/function.is-numeric.php),\n     *   otherwise an exception is thrown.\n     *\n     * @param mixed $value the input value\n     * @return float|int the normalized number value\n     * @throws InvalidArgumentException if the input value is not numeric.\n     */\n    protected function normalizeNumericValue($value)\n    {\n        if (empty($value)) {\n            return 0;\n        }\n        if (is_string($value) && is_numeric($value)) {\n            $value = (float) $value;\n        }\n        if (!is_numeric($value)) {\n            throw new InvalidArgumentException(\"'$value' is not a numeric value.\");\n        }\n\n        return $value;\n    }\n\n    /**\n     * Creates a number formatter based on the given type and format.\n     *\n     * You may override this method to create a number formatter based on patterns.\n     *\n     * @param int $style the type of the number formatter.\n     * Values: NumberFormatter::DECIMAL, ::CURRENCY, ::PERCENT, ::SCIENTIFIC, ::SPELLOUT, ::ORDINAL\n     * ::DURATION, ::PATTERN_RULEBASED, ::DEFAULT_STYLE, ::IGNORE\n     * @param int|null $decimals the number of digits after the decimal point.\n     * @param array $options optional configuration for the number formatter. This parameter will be merged with [[numberFormatterOptions]].\n     * @param array $textOptions optional configuration for the number formatter. This parameter will be merged with [[numberFormatterTextOptions]].\n     * @return NumberFormatter the created formatter instance\n     */\n    protected function createNumberFormatter($style, $decimals = null, $options = [], $textOptions = [])\n    {\n        $formatter = new NumberFormatter($this->locale, $style);\n\n        // set text attributes\n        foreach ($this->numberFormatterTextOptions as $attribute => $value) {\n            $this->setFormatterTextAttribute($formatter, $attribute, $value, 'numberFormatterTextOptions', 'numberFormatterOptions');\n        }\n        foreach ($textOptions as $attribute => $value) {\n            $this->setFormatterTextAttribute($formatter, $attribute, $value, '$textOptions', '$options');\n        }\n\n        // set attributes\n        foreach ($this->numberFormatterOptions as $attribute => $value) {\n            $this->setFormatterIntAttribute($formatter, $attribute, $value, 'numberFormatterOptions', 'numberFormatterTextOptions');\n        }\n        foreach ($options as $attribute => $value) {\n            $this->setFormatterIntAttribute($formatter, $attribute, $value, '$options', '$textOptions');\n        }\n        if ($decimals !== null) {\n            $formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, $decimals);\n            $formatter->setAttribute(NumberFormatter::MIN_FRACTION_DIGITS, $decimals);\n        }\n\n        // set symbols\n        if ($this->decimalSeparator !== null) {\n            $formatter->setSymbol(NumberFormatter::DECIMAL_SEPARATOR_SYMBOL, $this->decimalSeparator);\n        }\n        if ($this->currencyDecimalSeparator !== null) {\n            $formatter->setSymbol(NumberFormatter::MONETARY_SEPARATOR_SYMBOL, $this->currencyDecimalSeparator);\n        }\n        if ($this->thousandSeparator !== null) {\n            $formatter->setSymbol(NumberFormatter::GROUPING_SEPARATOR_SYMBOL, $this->thousandSeparator);\n            $formatter->setSymbol(NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL, $this->thousandSeparator);\n        }\n        foreach ($this->numberFormatterSymbols as $symbol => $value) {\n            $this->setFormatterSymbol($formatter, $symbol, $value, 'numberFormatterSymbols');\n        }\n\n        return $formatter;\n    }\n\n    /**\n     * @param NumberFormatter $formatter\n     * @param mixed $attribute\n     * @param mixed $value\n     * @param string $source\n     * @param string $alternative\n     */\n    private function setFormatterTextAttribute($formatter, $attribute, $value, $source, $alternative)\n    {\n        if (!is_int($attribute)) {\n            throw new InvalidArgumentException(\n                \"The $source array keys must be integers recognizable by NumberFormatter::setTextAttribute(). \\\"\"\n                . gettype($attribute) . '\" provided instead.'\n            );\n        }\n        if (!is_string($value)) {\n            if (is_int($value)) {\n                throw new InvalidArgumentException(\n                    \"The $source array values must be strings. Did you mean to use $alternative?\"\n                );\n            }\n            throw new InvalidArgumentException(\n                \"The $source array values must be strings. \\\"\" . gettype($value) . '\" provided instead.'\n            );\n        }\n        $formatter->setTextAttribute($attribute, $value);\n    }\n\n    /**\n     * @param NumberFormatter $formatter\n     * @param mixed $symbol\n     * @param mixed $value\n     * @param string $source\n     */\n    private function setFormatterSymbol($formatter, $symbol, $value, $source)\n    {\n        if (!is_int($symbol)) {\n            throw new InvalidArgumentException(\n                \"The $source array keys must be integers recognizable by NumberFormatter::setSymbol(). \\\"\"\n                . gettype($symbol) . '\" provided instead.'\n            );\n        }\n        if (!is_string($value)) {\n            throw new InvalidArgumentException(\n                \"The $source array values must be strings. \\\"\" . gettype($value) . '\" provided instead.'\n            );\n        }\n        $formatter->setSymbol($symbol, $value);\n    }\n\n    /**\n     * @param NumberFormatter $formatter\n     * @param mixed $attribute\n     * @param mixed $value\n     * @param string $source\n     * @param string $alternative\n     */\n    private function setFormatterIntAttribute($formatter, $attribute, $value, $source, $alternative)\n    {\n        if (!is_int($attribute)) {\n            throw new InvalidArgumentException(\n                \"The $source array keys must be integers recognizable by NumberFormatter::setAttribute(). \\\"\"\n                . gettype($attribute) . '\" provided instead.'\n            );\n        }\n        if (!is_int($value)) {\n            if (is_string($value)) {\n                throw new InvalidArgumentException(\n                    \"The $source array values must be integers. Did you mean to use $alternative?\"\n                );\n            }\n            throw new InvalidArgumentException(\n                \"The $source array values must be integers. \\\"\" . gettype($value) . '\" provided instead.'\n            );\n        }\n        $formatter->setAttribute($attribute, $value);\n    }\n\n    /**\n     * Checks if string representations of given value and its normalized version are different.\n     * @param string|float|int $value\n     * @param float|int $normalizedValue\n     * @return bool\n     * @since 2.0.16\n     */\n    protected function isNormalizedValueMispresented($value, $normalizedValue)\n    {\n        if (empty($value)) {\n            $value = 0;\n        }\n\n        return (string) $normalizedValue !== $this->normalizeNumericStringValue((string) $value);\n    }\n\n    /**\n     * Normalizes a numeric string value.\n     * @param string $value\n     * @return string the normalized number value as a string\n     * @since 2.0.16\n     */\n    protected function normalizeNumericStringValue($value)\n    {\n        $powerPosition = strrpos($value, 'E');\n        if ($powerPosition !== false) {\n            $valuePart = substr($value, 0, $powerPosition);\n            $powerPart = substr($value, $powerPosition + 1);\n        } else {\n            $powerPart = null;\n            $valuePart = $value;\n        }\n\n        $separatorPosition = strrpos($valuePart, '.');\n\n        if ($separatorPosition !== false) {\n            $integerPart = substr($valuePart, 0, $separatorPosition);\n            $fractionalPart = substr($valuePart, $separatorPosition + 1);\n        } else {\n            $integerPart = $valuePart;\n            $fractionalPart = null;\n        }\n\n        // truncate insignificant zeros, keep minus\n        $integerPart = preg_replace('/^\\+?(-?)0*(\\d+)$/', '$1$2', $integerPart);\n        // for zeros only leave one zero, keep minus\n        $integerPart = preg_replace('/^\\+?(-?)0*$/', '${1}0', $integerPart);\n\n        if ($fractionalPart !== null) {\n            // truncate insignificant zeros\n            $fractionalPart = rtrim($fractionalPart, '0');\n\n            if (empty($fractionalPart)) {\n                $fractionalPart = $powerPart !== null ? '0' : null;\n            }\n        }\n\n        $normalizedValue = $integerPart;\n        if ($fractionalPart !== null) {\n            $normalizedValue .= '.' . $fractionalPart;\n        } elseif ($normalizedValue === '-0') {\n            $normalizedValue = '0';\n        }\n\n        if ($powerPart !== null) {\n            $normalizedValue .= 'E' . $powerPart;\n        }\n\n        return $normalizedValue;\n    }\n\n    /**\n     * Fallback for formatting value as a decimal number.\n     *\n     * Property [[decimalSeparator]] will be used to represent the decimal point. The value is rounded automatically\n     * to the defined decimal digits.\n     *\n     * @param string|int|float $value the value to be formatted.\n     * @param int|null $decimals the number of digits after the decimal point. The default value is `2`.\n     * @return string the formatted result.\n     * @see decimalSeparator\n     * @see thousandSeparator\n     * @since 2.0.16\n     */\n    protected function asDecimalStringFallback($value, $decimals = 2)\n    {\n        if (empty($value)) {\n            $value = 0;\n        }\n\n        $value = $this->normalizeNumericStringValue((string) $value);\n\n        $separatorPosition = strrpos($value, '.');\n\n        if ($separatorPosition !== false) {\n            $integerPart = substr($value, 0, $separatorPosition);\n            $fractionalPart = substr($value, $separatorPosition + 1);\n        } else {\n            $integerPart = $value;\n            $fractionalPart = null;\n        }\n\n        $decimalOutput = '';\n\n        if ($decimals === null) {\n            $decimals = 2;\n        }\n\n        $carry = 0;\n\n        if ($decimals > 0) {\n            $decimalSeparator = $this->decimalSeparator;\n            if ($this->decimalSeparator === null) {\n                $decimalSeparator = '.';\n            }\n\n            if ($fractionalPart === null) {\n                $fractionalPart = str_repeat('0', $decimals);\n            } elseif (strlen($fractionalPart) > $decimals) {\n                $cursor = $decimals;\n\n                // checking if fractional part must be rounded\n                if ((int) substr($fractionalPart, $cursor, 1) >= 5) {\n                    while (--$cursor >= 0) {\n                        $carry = 0;\n\n                        $oneUp = (int) substr($fractionalPart, $cursor, 1) + 1;\n                        if ($oneUp === 10) {\n                            $oneUp = 0;\n                            $carry = 1;\n                        }\n\n                        $fractionalPart = substr($fractionalPart, 0, $cursor) . $oneUp . substr($fractionalPart, $cursor + 1);\n\n                        if ($carry === 0) {\n                            break;\n                        }\n                    }\n                }\n\n                $fractionalPart = substr($fractionalPart, 0, $decimals);\n            } elseif (strlen($fractionalPart) < $decimals) {\n                $fractionalPart = str_pad($fractionalPart, $decimals, '0');\n            }\n\n            $decimalOutput .= $decimalSeparator . $fractionalPart;\n        }\n\n        // checking if integer part must be rounded\n        if ($carry || ($decimals === 0 && $fractionalPart !== null && (int) substr($fractionalPart, 0, 1) >= 5)) {\n            $integerPartLength = strlen($integerPart);\n            $cursor = 0;\n\n            while (++$cursor <= $integerPartLength) {\n                $carry = 0;\n\n                $oneUp = (int) substr($integerPart, -$cursor, 1) + 1;\n                if ($oneUp === 10) {\n                    $oneUp = 0;\n                    $carry = 1;\n                }\n\n                $integerPart = substr($integerPart, 0, -$cursor) . $oneUp . substr($integerPart, $integerPartLength - $cursor + 1);\n\n                if ($carry === 0) {\n                    break;\n                }\n            }\n            if ($carry === 1) {\n                $integerPart = '1' . $integerPart;\n            }\n        }\n\n        if (strlen($integerPart) > 3) {\n            $thousandSeparator = $this->thousandSeparator;\n            if ($thousandSeparator === null) {\n                $thousandSeparator = ',';\n            }\n\n            $integerPart = strrev(implode(',', str_split(strrev($integerPart), 3)));\n            if ($thousandSeparator !== ',') {\n                $integerPart = str_replace(',', $thousandSeparator, $integerPart);\n            }\n        }\n\n        return $integerPart . $decimalOutput;\n    }\n\n    /**\n     * Fallback for formatting value as an integer number by removing any decimal digits without rounding.\n     *\n     * @param string|int|float $value the value to be formatted.\n     * @return string the formatted result.\n     * @since 2.0.16\n     */\n    protected function asIntegerStringFallback($value)\n    {\n        if (empty($value)) {\n            $value = 0;\n        }\n\n        $value = $this->normalizeNumericStringValue((string) $value);\n        $separatorPosition = strrpos($value, '.');\n\n        if ($separatorPosition !== false) {\n            $integerPart = substr($value, 0, $separatorPosition);\n        } else {\n            $integerPart = $value;\n        }\n\n        return $this->asDecimalStringFallback($integerPart, 0);\n    }\n\n    /**\n     * Fallback for formatting value as a percent number with \"%\" sign.\n     *\n     * Property [[decimalSeparator]] will be used to represent the decimal point. The value is rounded automatically\n     * to the defined decimal digits.\n     *\n     * @param string|int|float $value the value to be formatted.\n     * @param int|null $decimals the number of digits after the decimal point. The default value is `0`.\n     * @return string the formatted result.\n     * @since 2.0.16\n     */\n    protected function asPercentStringFallback($value, $decimals = null)\n    {\n        if (empty($value)) {\n            $value = 0;\n        }\n\n        if ($decimals === null) {\n            $decimals = 0;\n        }\n\n        $value = $this->normalizeNumericStringValue((string) $value);\n        $separatorPosition = strrpos($value, '.');\n\n        if ($separatorPosition !== false) {\n            $integerPart = substr($value, 0, $separatorPosition);\n            $fractionalPart = str_pad(substr($value, $separatorPosition + 1), 2, '0');\n\n            $integerPart .= substr($fractionalPart, 0, 2);\n            $fractionalPart = substr($fractionalPart, 2);\n\n            if ($fractionalPart === '') {\n                $multipliedValue = $integerPart;\n            } else {\n                $multipliedValue = $integerPart . '.' . $fractionalPart;\n            }\n        } else {\n            $multipliedValue = $value . '00';\n        }\n\n        return $this->asDecimalStringFallback($multipliedValue, $decimals) . '%';\n    }\n\n    /**\n     * Fallback for formatting value as a currency number.\n     *\n     * @param string|int|float $value the value to be formatted.\n     * @param string|null $currency the 3-letter ISO 4217 currency code indicating the currency to use.\n     * If null, [[currencyCode]] will be used.\n     * @return string the formatted result.\n     * @throws InvalidConfigException if no currency is given and [[currencyCode]] is not defined.\n     * @since 2.0.16\n     */\n    protected function asCurrencyStringFallback($value, $currency = null)\n    {\n        if ($currency === null) {\n            if ($this->currencyCode === null) {\n                throw new InvalidConfigException('The default currency code for the formatter is not defined.');\n            }\n            $currency = $this->currencyCode;\n        }\n\n        return $currency . ' ' . $this->asDecimalStringFallback($value, 2);\n    }\n}\n"
  },
  {
    "path": "framework/i18n/GettextFile.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\i18n;\n\nuse yii\\base\\Component;\n\n/**\n * GettextFile is the base class for representing a Gettext message file.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nabstract class GettextFile extends Component\n{\n    /**\n     * Loads messages from a file.\n     * @param string $filePath file path\n     * @param string $context message context\n     * @return array message translations. Array keys are source messages and array values are translated messages:\n     * source message => translated message.\n     */\n    abstract public function load($filePath, $context);\n\n    /**\n     * Saves messages to a file.\n     * @param string $filePath file path\n     * @param array $messages message translations. Array keys are source messages and array values are\n     * translated messages: source message => translated message. Note if the message has a context,\n     * the message ID must be prefixed with the context with chr(4) as the separator.\n     */\n    abstract public function save($filePath, $messages);\n}\n"
  },
  {
    "path": "framework/i18n/GettextMessageSource.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\i18n;\n\nuse Yii;\nuse yii\\base\\InvalidArgumentException;\n\n/**\n * GettextMessageSource represents a message source that is based on GNU Gettext.\n *\n * Each GettextMessageSource instance represents the message translations\n * for a single domain. And each message category represents a message context\n * in Gettext. Translated messages are stored as either a MO or PO file,\n * depending on the [[useMoFile]] property value.\n *\n * All translations are saved under the [[basePath]] directory.\n *\n * Translations in one language are kept as MO or PO files under an individual\n * subdirectory whose name is the language ID. The file name is specified via\n * [[catalog]] property, which defaults to 'messages'.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass GettextMessageSource extends MessageSource\n{\n    public const MO_FILE_EXT = '.mo';\n    public const PO_FILE_EXT = '.po';\n    /**\n     * @var string base directory of messages files\n     */\n    public $basePath = '@app/messages';\n    /**\n     * @var string sub-directory of messages files\n     */\n    public $catalog = 'messages';\n    /**\n     * @var bool whether to use generated MO files\n     */\n    public $useMoFile = true;\n    /**\n     * @var bool whether to use big-endian when reading and writing an integer\n     */\n    public $useBigEndian = false;\n\n\n    /**\n     * Loads the message translation for the specified $language and $category.\n     * If translation for specific locale code such as `en-US` isn't found it\n     * tries more generic `en`. When both are present, the `en-US` messages will be merged\n     * over `en`. See [[loadFallbackMessages]] for details.\n     * If the $language is less specific than [[sourceLanguage]], the method will try to\n     * load the messages for [[sourceLanguage]]. For example: [[sourceLanguage]] is `en-GB`,\n     * $language is `en`. The method will load the messages for `en` and merge them over `en-GB`.\n     *\n     * @param string $category the message category\n     * @param string $language the target language\n     * @return array the loaded messages. The keys are original messages, and the values are translated messages.\n     * @see loadFallbackMessages\n     * @see sourceLanguage\n     */\n    protected function loadMessages($category, $language)\n    {\n        $messageFile = $this->getMessageFilePath($language);\n        $messages = $this->loadMessagesFromFile($messageFile, $category);\n\n        $fallbackLanguage = substr($language, 0, 2);\n        $fallbackSourceLanguage = substr($this->sourceLanguage, 0, 2);\n\n        if ($fallbackLanguage !== '' && $fallbackLanguage !== $language) {\n            $messages = $this->loadFallbackMessages($category, $fallbackLanguage, $messages, $messageFile);\n        } elseif ($fallbackSourceLanguage !== '' && $language === $fallbackSourceLanguage) {\n            $messages = $this->loadFallbackMessages($category, $this->sourceLanguage, $messages, $messageFile);\n        } elseif ($messages === null) {\n            Yii::error(\"The message file for category '$category' does not exist: $messageFile\", __METHOD__);\n        }\n\n        return (array) $messages;\n    }\n\n    /**\n     * The method is normally called by [[loadMessages]] to load the fallback messages for the language.\n     * Method tries to load the $category messages for the $fallbackLanguage and adds them to the $messages array.\n     *\n     * @param string $category the message category\n     * @param string $fallbackLanguage the target fallback language\n     * @param array $messages the array of previously loaded translation messages.\n     * The keys are original messages, and the values are the translated messages.\n     * @param string $originalMessageFile the path to the file with messages. Used to log an error message\n     * in case when no translations were found.\n     * @return array the loaded messages. The keys are original messages, and the values are the translated messages.\n     * @since 2.0.7\n     */\n    protected function loadFallbackMessages($category, $fallbackLanguage, $messages, $originalMessageFile)\n    {\n        $fallbackMessageFile = $this->getMessageFilePath($fallbackLanguage);\n        $fallbackMessages = $this->loadMessagesFromFile($fallbackMessageFile, $category);\n\n        if (\n            $messages === null && $fallbackMessages === null\n            && $fallbackLanguage !== $this->sourceLanguage\n            && strpos($this->sourceLanguage, $fallbackLanguage) !== 0\n        ) {\n            Yii::error(\"The message file for category '$category' does not exist: $originalMessageFile \"\n                . \"Fallback file does not exist as well: $fallbackMessageFile\", __METHOD__);\n        } elseif (empty($messages)) {\n            return $fallbackMessages;\n        } elseif (!empty($fallbackMessages)) {\n            foreach ($fallbackMessages as $key => $value) {\n                if (!empty($value) && empty($messages[$key])) {\n                    $messages[$key] = $value;\n                }\n            }\n        }\n\n        return (array) $messages;\n    }\n\n    /**\n     * Returns message file path for the specified language and category.\n     *\n     * @param string $language the target language\n     * @return string path to message file\n     */\n    protected function getMessageFilePath($language)\n    {\n        $language = (string) $language;\n        if ($language !== '' && !preg_match('/^[a-z0-9_-]+$/i', $language)) {\n            throw new InvalidArgumentException(sprintf('Invalid language code: \"%s\".', $language));\n        }\n        $messageFile = Yii::getAlias($this->basePath) . '/' . $language . '/' . $this->catalog;\n        if ($this->useMoFile) {\n            $messageFile .= self::MO_FILE_EXT;\n        } else {\n            $messageFile .= self::PO_FILE_EXT;\n        }\n\n        return $messageFile;\n    }\n\n    /**\n     * Loads the message translation for the specified language and category or returns null if file doesn't exist.\n     *\n     * @param string $messageFile path to message file\n     * @param string $category the message category\n     * @return array|null array of messages or null if file not found\n     */\n    protected function loadMessagesFromFile($messageFile, $category)\n    {\n        if (is_file($messageFile)) {\n            if ($this->useMoFile) {\n                $gettextFile = new GettextMoFile(['useBigEndian' => $this->useBigEndian]);\n            } else {\n                $gettextFile = new GettextPoFile();\n            }\n            $messages = $gettextFile->load($messageFile, $category);\n            if (!is_array($messages)) {\n                $messages = [];\n            }\n\n            return $messages;\n        }\n\n        return null;\n    }\n}\n"
  },
  {
    "path": "framework/i18n/GettextMoFile.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\i18n;\n\nuse yii\\base\\Exception;\n\n/**\n * GettextMoFile represents an MO Gettext message file.\n *\n * This class is written by adapting Michael's Gettext_MO class in PEAR.\n * Please refer to the following license terms.\n *\n * Copyright (c) 2004-2005, Michael Wallner <mike@iworks.at>.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n *     * Redistributions of source code must retain the above copyright notice,\n *       this list of conditions and the following disclaimer.\n *     * Redistributions in binary form must reproduce the above copyright\n *       notice, this list of conditions and the following disclaimer in the\n *       documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass GettextMoFile extends GettextFile\n{\n    /**\n     * @var bool whether to use big-endian when reading and writing an integer.\n     */\n    public $useBigEndian = false;\n\n\n    /**\n     * Loads messages from an MO file.\n     * @param string $filePath file path\n     * @param string $context message context\n     * @return array message translations. Array keys are source messages and array values are translated messages:\n     * source message => translated message.\n     * @throws Exception if unable to read the MO file\n     */\n    public function load($filePath, $context)\n    {\n        if (false === ($fileHandle = @fopen($filePath, 'rb'))) {\n            throw new Exception('Unable to read file \"' . $filePath . '\".');\n        }\n        if (false === @flock($fileHandle, LOCK_SH)) {\n            throw new Exception('Unable to lock file \"' . $filePath . '\" for reading.');\n        }\n\n        // magic\n        $array = unpack('c', $this->readBytes($fileHandle, 4));\n        $magic = current($array);\n        if ($magic == -34) {\n            $this->useBigEndian = false;\n        } elseif ($magic == -107) {\n            $this->useBigEndian = true;\n        } else {\n            throw new Exception('Invalid MO file: ' . $filePath . ' (magic: ' . $magic . ').');\n        }\n\n        // revision\n        $revision = $this->readInteger($fileHandle);\n        if ($revision !== 0) {\n            throw new Exception('Invalid MO file revision: ' . $revision . '.');\n        }\n\n        $count = $this->readInteger($fileHandle);\n        $sourceOffset = $this->readInteger($fileHandle);\n        $targetOffset = $this->readInteger($fileHandle);\n\n        $sourceLengths = [];\n        $sourceOffsets = [];\n        fseek($fileHandle, $sourceOffset);\n        for ($i = 0; $i < $count; ++$i) {\n            $sourceLengths[] = $this->readInteger($fileHandle);\n            $sourceOffsets[] = $this->readInteger($fileHandle);\n        }\n\n        $targetLengths = [];\n        $targetOffsets = [];\n        fseek($fileHandle, $targetOffset);\n        for ($i = 0; $i < $count; ++$i) {\n            $targetLengths[] = $this->readInteger($fileHandle);\n            $targetOffsets[] = $this->readInteger($fileHandle);\n        }\n\n        $messages = [];\n        for ($i = 0; $i < $count; ++$i) {\n            $id = $this->readString($fileHandle, $sourceLengths[$i], $sourceOffsets[$i]);\n            $separatorPosition = strpos((string)$id, chr(4));\n\n\n            if ((!$context && $separatorPosition === false) || ($context && $separatorPosition !== false && strncmp($id, $context, $separatorPosition) === 0)) {\n                if ($separatorPosition !== false) {\n                    $id = substr($id, $separatorPosition + 1);\n                }\n\n                $message = $this->readString($fileHandle, $targetLengths[$i], $targetOffsets[$i]);\n                $messages[$id] = $message;\n            }\n        }\n\n        @flock($fileHandle, LOCK_UN);\n        @fclose($fileHandle);\n\n        return $messages;\n    }\n\n    /**\n     * Saves messages to an MO file.\n     * @param string $filePath file path\n     * @param array $messages message translations. Array keys are source messages and array values are\n     * translated messages: source message => translated message. Note if the message has a context,\n     * the message ID must be prefixed with the context with chr(4) as the separator.\n     * @throws Exception if unable to save the MO file\n     */\n    public function save($filePath, $messages)\n    {\n        if (false === ($fileHandle = @fopen($filePath, 'wb'))) {\n            throw new Exception('Unable to write file \"' . $filePath . '\".');\n        }\n        if (false === @flock($fileHandle, LOCK_EX)) {\n            throw new Exception('Unable to lock file \"' . $filePath . '\" for reading.');\n        }\n\n        // magic\n        if ($this->useBigEndian) {\n            $this->writeBytes($fileHandle, pack('c*', 0x95, 0x04, 0x12, 0xde)); // -107\n        } else {\n            $this->writeBytes($fileHandle, pack('c*', 0xde, 0x12, 0x04, 0x95)); // -34\n        }\n\n        // revision\n        $this->writeInteger($fileHandle, 0);\n\n        // message count\n        $messageCount = count($messages);\n        $this->writeInteger($fileHandle, $messageCount);\n\n        // offset of source message table\n        $offset = 28;\n        $this->writeInteger($fileHandle, $offset);\n        $offset += $messageCount * 8;\n        $this->writeInteger($fileHandle, $offset);\n\n        // hashtable size, omitted\n        $this->writeInteger($fileHandle, 0);\n        $offset += $messageCount * 8;\n        $this->writeInteger($fileHandle, $offset);\n\n        // length and offsets for source messages\n        foreach (array_keys($messages) as $id) {\n            $length = strlen($id);\n            $this->writeInteger($fileHandle, $length);\n            $this->writeInteger($fileHandle, $offset);\n            $offset += $length + 1;\n        }\n\n        // length and offsets for target messages\n        foreach ($messages as $message) {\n            $length = strlen($message);\n            $this->writeInteger($fileHandle, $length);\n            $this->writeInteger($fileHandle, $offset);\n            $offset += $length + 1;\n        }\n\n        // source messages\n        foreach (array_keys($messages) as $id) {\n            $this->writeString($fileHandle, $id);\n        }\n\n        // target messages\n        foreach ($messages as $message) {\n            $this->writeString($fileHandle, $message);\n        }\n\n        @flock($fileHandle, LOCK_UN);\n        @fclose($fileHandle);\n    }\n\n    /**\n     * Reads one or several bytes.\n     * @param resource $fileHandle to read from\n     * @param int $byteCount to be read\n     * @return string|null bytes\n     */\n    protected function readBytes($fileHandle, $byteCount = 1)\n    {\n        if ($byteCount > 0) {\n            return fread($fileHandle, $byteCount);\n        }\n\n        return null;\n    }\n\n    /**\n     * Write bytes.\n     * @param resource $fileHandle to write to\n     * @param string $bytes to be written\n     * @return int how many bytes are written\n     */\n    protected function writeBytes($fileHandle, $bytes)\n    {\n        return fwrite($fileHandle, $bytes);\n    }\n\n    /**\n     * Reads a 4-byte integer.\n     * @param resource $fileHandle to read from\n     * @return int the result\n     */\n    protected function readInteger($fileHandle)\n    {\n        $array = unpack($this->useBigEndian ? 'N' : 'V', $this->readBytes($fileHandle, 4));\n\n        return current($array);\n    }\n\n    /**\n     * Writes a 4-byte integer.\n     * @param resource $fileHandle to write to\n     * @param int $integer to be written\n     * @return int how many bytes are written\n     */\n    protected function writeInteger($fileHandle, $integer)\n    {\n        return $this->writeBytes($fileHandle, pack($this->useBigEndian ? 'N' : 'V', (int) $integer));\n    }\n\n    /**\n     * Reads a string.\n     * @param resource $fileHandle file handle\n     * @param int $length of the string\n     * @param int|null $offset of the string in the file. If null, it reads from the current position.\n     * @return string|null the result\n     */\n    protected function readString($fileHandle, $length, $offset = null)\n    {\n        if ($offset !== null) {\n            fseek($fileHandle, $offset);\n        }\n\n        return $this->readBytes($fileHandle, $length);\n    }\n\n    /**\n     * Writes a string.\n     * @param resource $fileHandle to write to\n     * @param string $string to be written\n     * @return int how many bytes are written\n     */\n    protected function writeString($fileHandle, $string)\n    {\n        return $this->writeBytes($fileHandle, $string . \"\\0\");\n    }\n}\n"
  },
  {
    "path": "framework/i18n/GettextPoFile.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\i18n;\n\nuse Yii;\n\n/**\n * GettextPoFile represents a PO Gettext message file.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass GettextPoFile extends GettextFile\n{\n    /**\n     * Loads messages from a PO file.\n     * @param string $filePath file path\n     * @param string $context message context\n     * @return array message translations. Array keys are source messages and array values are translated messages:\n     * source message => translated message.\n     */\n    public function load($filePath, $context)\n    {\n        $pattern = '/(msgctxt\\s+\"(.*?(?<!\\\\\\\\))\")?\\s+' // context\n            . 'msgid\\s+((?:\".*(?<!\\\\\\\\)\"\\s*)+)\\s+' // message ID, i.e. original string\n            . 'msgstr\\s+((?:\".*(?<!\\\\\\\\)\"\\s*)+)/'; // translated string\n        $content = file_get_contents($filePath);\n        $matches = [];\n        $matchCount = preg_match_all($pattern, $content, $matches);\n\n        $messages = [];\n        for ($i = 0; $i < $matchCount; ++$i) {\n            if ($matches[2][$i] === $context) {\n                $id = $this->decode($matches[3][$i]);\n                $message = $this->decode($matches[4][$i]);\n                $messages[$id] = $message;\n            }\n        }\n\n        return $messages;\n    }\n\n    /**\n     * Saves messages to a PO file.\n     * @param string $filePath file path\n     * @param array $messages message translations. Array keys are source messages and array values are\n     * translated messages: source message => translated message. Note if the message has a context,\n     * the message ID must be prefixed with the context with chr(4) as the separator.\n     */\n    public function save($filePath, $messages)\n    {\n        $language = str_replace('-', '_', basename(dirname($filePath)));\n        $headers = [\n            'msgid \"\"',\n            'msgstr \"\"',\n            '\"Project-Id-Version: \\n\"',\n            '\"POT-Creation-Date: \\n\"',\n            '\"PO-Revision-Date: \\n\"',\n            '\"Last-Translator: \\n\"',\n            '\"Language-Team: \\n\"',\n            '\"Language: ' . $language . '\\n\"',\n            '\"MIME-Version: 1.0\\n\"',\n            '\"Content-Type: text/plain; charset=' . Yii::$app->charset . '\\n\"',\n            '\"Content-Transfer-Encoding: 8bit\\n\"',\n        ];\n        $content = implode(\"\\n\", $headers) . \"\\n\\n\";\n        foreach ($messages as $id => $message) {\n            $separatorPosition = strpos($id, chr(4));\n            if ($separatorPosition !== false) {\n                $content .= 'msgctxt \"' . substr($id, 0, $separatorPosition) . \"\\\"\\n\";\n                $id = substr($id, $separatorPosition + 1);\n            }\n            $content .= 'msgid \"' . $this->encode($id) . \"\\\"\\n\";\n            $content .= 'msgstr \"' . $this->encode($message) . \"\\\"\\n\\n\";\n        }\n        file_put_contents($filePath, $content);\n    }\n\n    /**\n     * Encodes special characters in a message.\n     * @param string $string message to be encoded\n     * @return string the encoded message\n     */\n    protected function encode($string)\n    {\n        return str_replace(\n            ['\"', \"\\n\", \"\\t\", \"\\r\"],\n            ['\\\\\"', '\\\\n', '\\\\t', '\\\\r'],\n            $string\n        );\n    }\n\n    /**\n     * Decodes special characters in a message.\n     * @param string $string message to be decoded\n     * @return string the decoded message\n     */\n    protected function decode($string)\n    {\n        $string = preg_replace(\n            ['/\"\\s+\"/', '/\\\\\\\\n/', '/\\\\\\\\r/', '/\\\\\\\\t/', '/\\\\\\\\\"/'],\n            ['', \"\\n\", \"\\r\", \"\\t\", '\"'],\n            $string\n        );\n\n        return substr(rtrim($string), 1, -1);\n    }\n}\n"
  },
  {
    "path": "framework/i18n/I18N.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\i18n;\n\nuse Yii;\nuse yii\\base\\Component;\nuse yii\\base\\InvalidConfigException;\n\n/**\n * I18N provides features related with internationalization (I18N) and localization (L10N).\n *\n * I18N is configured as an application component in [[\\yii\\base\\Application]] by default.\n * You can access that instance via `Yii::$app->i18n`.\n *\n * @property MessageFormatter $messageFormatter The message formatter to be used to format message via ICU\n * message format. Note that the type of this property differs in getter and setter. See\n * [[getMessageFormatter()]] and [[setMessageFormatter()]] for details.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass I18N extends Component\n{\n    /**\n     * @var array list of [[MessageSource]] configurations or objects. The array keys are message\n     * category patterns, and the array values are the corresponding [[MessageSource]] objects or the configurations\n     * for creating the [[MessageSource]] objects.\n     *\n     * The message category patterns can contain the wildcard `*` at the end to match multiple categories with the same prefix.\n     * For example, `app/*` matches both `app/cat1` and `app/cat2`.\n     *\n     * The `*` category pattern will match all categories that do not match any other category patterns.\n     *\n     * This property may be modified on the fly by extensions who want to have their own message sources\n     * registered under their own namespaces.\n     *\n     * The category `yii` and `app` are always defined. The former refers to the messages used in the Yii core\n     * framework code, while the latter refers to the default message category for custom application code.\n     * By default, both of these categories use [[PhpMessageSource]] and the corresponding message files are\n     * stored under `@yii/messages` and `@app/messages`, respectively.\n     *\n     * You may override the configuration of both categories.\n     */\n    public $translations;\n\n\n    /**\n     * Initializes the component by configuring the default message categories.\n     */\n    public function init()\n    {\n        parent::init();\n        if (!isset($this->translations['yii']) && !isset($this->translations['yii*'])) {\n            $this->translations['yii'] = [\n                'class' => 'yii\\i18n\\PhpMessageSource',\n                'sourceLanguage' => 'en-US',\n                'basePath' => '@yii/messages',\n            ];\n        }\n\n        if (!isset($this->translations['app']) && !isset($this->translations['app*'])) {\n            $this->translations['app'] = [\n                'class' => 'yii\\i18n\\PhpMessageSource',\n                'sourceLanguage' => Yii::$app->sourceLanguage,\n                'basePath' => '@app/messages',\n            ];\n        }\n    }\n\n    /**\n     * Translates a message to the specified language.\n     *\n     * After translation the message will be formatted using [[MessageFormatter]] if it contains\n     * ICU message format and `$params` are not empty.\n     *\n     * @param string $category the message category.\n     * @param string $message the message to be translated.\n     * @param array $params the parameters that will be used to replace the corresponding placeholders in the message.\n     * @param string $language the language code (e.g. `en-US`, `en`).\n     * @return string the translated and formatted message.\n     */\n    public function translate($category, $message, $params, $language)\n    {\n        $messageSource = $this->getMessageSource($category);\n        $translation = $messageSource->translate($category, $message, $language);\n        if ($translation === false) {\n            return $this->format($message, $params, $messageSource->sourceLanguage);\n        }\n\n        return $this->format($translation, $params, $language);\n    }\n\n    /**\n     * Formats a message using [[MessageFormatter]].\n     *\n     * @param string $message the message to be formatted.\n     * @param array $params the parameters that will be used to replace the corresponding placeholders in the message.\n     * @param string $language the language code (e.g. `en-US`, `en`).\n     * @return string the formatted message.\n     */\n    public function format($message, $params, $language)\n    {\n        $params = (array) $params;\n        if ($params === []) {\n            return $message;\n        }\n\n        if (preg_match('~{\\s*[\\w.]+\\s*,~u', $message)) {\n            $formatter = $this->getMessageFormatter();\n            $result = $formatter->format($message, $params, $language);\n            if ($result === false) {\n                $errorMessage = $formatter->getErrorMessage();\n                Yii::warning(\"Formatting message for language '$language' failed with error: $errorMessage. The message being formatted was: $message.\", __METHOD__);\n\n                return $message;\n            }\n\n            return $result;\n        }\n\n        $p = [];\n        foreach ($params as $name => $value) {\n            $p['{' . $name . '}'] = $value;\n        }\n\n        return strtr($message, $p);\n    }\n\n    /**\n     * @var string|array|MessageFormatter\n     */\n    private $_messageFormatter;\n\n    /**\n     * Returns the message formatter instance.\n     * @return MessageFormatter the message formatter to be used to format message via ICU message format.\n     */\n    public function getMessageFormatter()\n    {\n        if ($this->_messageFormatter === null) {\n            $this->_messageFormatter = new MessageFormatter();\n        } elseif (is_array($this->_messageFormatter) || is_string($this->_messageFormatter)) {\n            $this->_messageFormatter = Yii::createObject($this->_messageFormatter);\n        }\n\n        return $this->_messageFormatter;\n    }\n\n    /**\n     * @param string|array|MessageFormatter $value the message formatter to be used to format message via ICU message format.\n     * Can be given as array or string configuration that will be given to [[Yii::createObject]] to create an instance\n     * or a [[MessageFormatter]] instance.\n     */\n    public function setMessageFormatter($value)\n    {\n        $this->_messageFormatter = $value;\n    }\n\n    /**\n     * Returns the message source for the given category.\n     * @param string $category the category name.\n     * @return MessageSource the message source for the given category.\n     * @throws InvalidConfigException if there is no message source available for the specified category.\n     */\n    public function getMessageSource($category)\n    {\n        if (isset($this->translations[$category])) {\n            $source = $this->translations[$category];\n            if ($source instanceof MessageSource) {\n                return $source;\n            }\n\n            return $this->translations[$category] = Yii::createObject($source);\n        }\n        // try wildcard matching\n        foreach ($this->translations as $pattern => $source) {\n            if (strpos($pattern, '*') > 0 && strpos($category, rtrim($pattern, '*')) === 0) {\n                if ($source instanceof MessageSource) {\n                    return $source;\n                }\n\n                return $this->translations[$category] = $this->translations[$pattern] = Yii::createObject($source);\n            }\n        }\n\n        // match '*' in the last\n        if (isset($this->translations['*'])) {\n            $source = $this->translations['*'];\n            if ($source instanceof MessageSource) {\n                return $source;\n            }\n\n            return $this->translations[$category] = $this->translations['*'] = Yii::createObject($source);\n        }\n\n        throw new InvalidConfigException(\"Unable to locate message source for category '$category'.\");\n    }\n}\n"
  },
  {
    "path": "framework/i18n/Locale.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\i18n;\n\nuse Yii;\nuse yii\\base\\Component;\nuse yii\\base\\InvalidConfigException;\n\n/**\n * Locale provides various locale information via convenient methods.\n *\n * The class requires [PHP intl extension](https://www.php.net/manual/en/book.intl.php) to be installed.\n *\n * @property-read string $currencySymbol\n *\n * @since 2.0.14\n */\nclass Locale extends Component\n{\n    /**\n     * @var string|null the locale ID.\n     * If not set, [[\\yii\\base\\Application::language]] will be used.\n     */\n    public $locale;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        if (!extension_loaded('intl')) {\n            throw new InvalidConfigException('Locale component requires PHP intl extension to be installed.');\n        }\n\n        if ($this->locale === null) {\n            $this->locale = Yii::$app->language;\n        }\n    }\n\n    /**\n     * Returns a currency symbol\n     *\n     * @param string|null $currencyCode the 3-letter ISO 4217 currency code to get symbol for. If null,\n     * method will attempt using currency code from [[locale]].\n     * @return string\n     */\n    public function getCurrencySymbol($currencyCode = null)\n    {\n        $locale = $this->locale;\n\n        if ($currencyCode !== null) {\n            $locale .= '@currency=' . $currencyCode;\n        }\n\n        $formatter = new \\NumberFormatter($locale, \\NumberFormatter::CURRENCY);\n        return $formatter->getSymbol(\\NumberFormatter::CURRENCY_SYMBOL);\n    }\n}\n"
  },
  {
    "path": "framework/i18n/MessageFormatter.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\i18n;\n\nuse Yii;\nuse yii\\base\\Component;\nuse yii\\base\\NotSupportedException;\n\n/**\n * MessageFormatter allows formatting messages via [ICU message format](https://unicode-org.github.io/icu/userguide/format_parse/messages/).\n *\n * This class enhances the message formatter class provided by the PHP intl extension.\n *\n * The following enhancements are provided:\n *\n * - It accepts named arguments and mixed numeric and named arguments.\n * - Issues no error when an insufficient number of arguments have been provided. Instead, the placeholders will not be\n *   substituted.\n * - Fixes PHP 5.5 weird placeholder replacement in case no arguments are provided at all (https://bugs.php.net/bug.php?id=65920).\n * - Offers limited support for message formatting in case PHP intl extension is not installed.\n *   However it is highly recommended that you install [PHP intl extension](https://www.php.net/manual/en/book.intl.php) if you want\n *   to use MessageFormatter features.\n *\n *   The fallback implementation only supports the following message formats:\n *   - plural formatting for english ('one' and 'other' selectors)\n *   - select format\n *   - simple parameters\n *   - integer number parameters\n *\n *   The fallback implementation does NOT support the ['apostrophe-friendly' syntax](https://www.php.net/manual/en/messageformatter.formatmessage.php).\n *   Also messages that are working with the fallback implementation are not necessarily compatible with the\n *   PHP intl MessageFormatter so do not rely on the fallback if you are able to install intl extension somehow.\n *\n * @property-read string $errorCode Code of the last error.\n * @property-read string $errorMessage Description of the last error.\n *\n * @author Alexander Makarov <sam@rmcreative.ru>\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0\n */\nclass MessageFormatter extends Component\n{\n    private $_errorCode = 0;\n    private $_errorMessage = '';\n\n\n    /**\n     * Get the error code from the last operation.\n     * @link https://www.php.net/manual/en/messageformatter.geterrorcode.php\n     * @return string Code of the last error.\n     */\n    public function getErrorCode()\n    {\n        return $this->_errorCode;\n    }\n\n    /**\n     * Get the error text from the last operation.\n     * @link https://www.php.net/manual/en/messageformatter.geterrormessage.php\n     * @return string Description of the last error.\n     */\n    public function getErrorMessage()\n    {\n        return $this->_errorMessage;\n    }\n\n    /**\n     * Formats a message via [ICU message format](https://unicode-org.github.io/icu/userguide/format_parse/messages/).\n     *\n     * It uses the PHP intl extension's [MessageFormatter](https://www.php.net/manual/en/class.messageformatter.php)\n     * and works around some issues.\n     * If PHP intl is not installed a fallback will be used that supports a subset of the ICU message format.\n     *\n     * @param string $pattern The pattern string to insert parameters into.\n     * @param array $params The array of name value pairs to insert into the format string.\n     * @param string $language The locale to use for formatting locale-dependent parts\n     * @return string|false The formatted pattern string or `false` if an error occurred\n     */\n    public function format($pattern, $params, $language)\n    {\n        $this->_errorCode = 0;\n        $this->_errorMessage = '';\n\n        if ($params === []) {\n            return $pattern;\n        }\n\n        if (!class_exists('MessageFormatter', false)) {\n            return $this->fallbackFormat($pattern, $params, $language);\n        }\n\n        // replace named arguments (https://github.com/yiisoft/yii2/issues/9678)\n        $newParams = [];\n        $pattern = $this->replaceNamedArguments($pattern, $params, $newParams);\n        $params = $newParams;\n\n        try {\n            $formatter = new \\MessageFormatter($language, $pattern);\n\n            if ($formatter === null) {\n                // formatter may be null in PHP 5.x\n                $this->_errorCode = intl_get_error_code();\n                $this->_errorMessage = 'Message pattern is invalid: ' . intl_get_error_message();\n                return false;\n            }\n        } catch (\\IntlException $e) {\n            // IntlException is thrown since PHP 7\n            $this->_errorCode = $e->getCode();\n            $this->_errorMessage = 'Message pattern is invalid: ' . $e->getMessage();\n            return false;\n        } catch (\\Exception $e) {\n            // Exception is thrown by HHVM\n            $this->_errorCode = $e->getCode();\n            $this->_errorMessage = 'Message pattern is invalid: ' . $e->getMessage();\n            return false;\n        }\n\n        $result = $formatter->format($params);\n\n        if ($result === false) {\n            $this->_errorCode = $formatter->getErrorCode();\n            $this->_errorMessage = $formatter->getErrorMessage();\n            return false;\n        }\n\n        return $result;\n    }\n\n    /**\n     * Parses an input string according to an [ICU message format](https://unicode-org.github.io/icu/userguide/format_parse/messages/) pattern.\n     *\n     * It uses the PHP intl extension's [MessageFormatter::parse()](https://www.php.net/manual/en/messageformatter.parsemessage.php)\n     * and adds support for named arguments.\n     * Usage of this method requires PHP intl extension to be installed.\n     *\n     * @param string $pattern The pattern to use for parsing the message.\n     * @param string $message The message to parse, conforming to the pattern.\n     * @param string $language The locale to use for formatting locale-dependent parts\n     * @return array|bool An array containing items extracted, or `FALSE` on error.\n     * @throws \\yii\\base\\NotSupportedException when PHP intl extension is not installed.\n     */\n    public function parse($pattern, $message, $language)\n    {\n        $this->_errorCode = 0;\n        $this->_errorMessage = '';\n\n        if (!class_exists('MessageFormatter', false)) {\n            throw new NotSupportedException('You have to install PHP intl extension to use this feature.');\n        }\n\n        // replace named arguments\n        if (($tokens = self::tokenizePattern($pattern)) === false) {\n            $this->_errorCode = -1;\n            $this->_errorMessage = 'Message pattern is invalid.';\n\n            return false;\n        }\n        $map = [];\n        foreach ($tokens as $i => $token) {\n            if (is_array($token)) {\n                $param = trim($token[0]);\n                if (!isset($map[$param])) {\n                    $map[$param] = count($map);\n                }\n                $token[0] = $map[$param];\n                $tokens[$i] = '{' . implode(',', $token) . '}';\n            }\n        }\n        $pattern = implode('', $tokens);\n        $map = array_flip($map);\n\n        $formatter = new \\MessageFormatter($language, $pattern);\n        if ($formatter === null) {\n            $this->_errorCode = -1;\n            $this->_errorMessage = 'Message pattern is invalid.';\n\n            return false;\n        }\n        $result = $formatter->parse($message);\n        if ($result === false) {\n            $this->_errorCode = $formatter->getErrorCode();\n            $this->_errorMessage = $formatter->getErrorMessage();\n\n            return false;\n        }\n\n        $values = [];\n        foreach ($result as $key => $value) {\n            $values[$map[$key]] = $value;\n        }\n\n        return $values;\n    }\n\n    /**\n     * Replace named placeholders with numeric placeholders and quote unused.\n     *\n     * @param string $pattern The pattern string to replace things into.\n     * @param array $givenParams The array of values to insert into the format string.\n     * @param array $resultingParams Modified array of parameters.\n     * @param array $map\n     * @return string|false The pattern string with placeholders replaced.\n     */\n    private function replaceNamedArguments($pattern, $givenParams, &$resultingParams = [], &$map = [])\n    {\n        if (($tokens = self::tokenizePattern($pattern)) === false) {\n            return false;\n        }\n        foreach ($tokens as $i => $token) {\n            if (!is_array($token)) {\n                continue;\n            }\n            $param = trim($token[0]);\n            if (array_key_exists($param, $givenParams)) {\n                // if param is given, replace it with a number\n                if (!isset($map[$param])) {\n                    $map[$param] = count($map);\n                    // make sure only used params are passed to format method\n                    $resultingParams[$map[$param]] = $givenParams[$param];\n                }\n                $token[0] = $map[$param];\n                $quote = '';\n            } else {\n                // quote unused token\n                $quote = \"'\";\n            }\n            $type = isset($token[1]) ? trim($token[1]) : 'none';\n            // replace plural and select format recursively\n            if ($type === 'plural' || $type === 'select') {\n                if (!isset($token[2])) {\n                    return false;\n                }\n                if (($subtokens = self::tokenizePattern($token[2])) === false) {\n                    return false;\n                }\n                $c = count($subtokens);\n                for ($k = 0; $k + 1 < $c; $k++) {\n                    if (is_array($subtokens[$k]) || !is_array($subtokens[++$k])) {\n                        return false;\n                    }\n                    $subpattern = $this->replaceNamedArguments(implode(',', $subtokens[$k]), $givenParams, $resultingParams, $map);\n                    $subtokens[$k] = $quote . '{' . $quote . $subpattern . $quote . '}' . $quote;\n                }\n                $token[2] = implode('', $subtokens);\n            }\n            $tokens[$i] = $quote . '{' . $quote . implode(',', $token) . $quote . '}' . $quote;\n        }\n\n        return implode('', $tokens);\n    }\n\n    /**\n     * Fallback implementation for MessageFormatter::formatMessage.\n     * @param string $pattern The pattern string to insert things into.\n     * @param array $args The array of values to insert into the format string\n     * @param string $locale The locale to use for formatting locale-dependent parts\n     * @return false|string The formatted pattern string or `false` if an error occurred\n     */\n    protected function fallbackFormat($pattern, $args, $locale)\n    {\n        if (($tokens = self::tokenizePattern($pattern)) === false) {\n            $this->_errorCode = -1;\n            $this->_errorMessage = 'Message pattern is invalid.';\n\n            return false;\n        }\n        foreach ($tokens as $i => $token) {\n            if (is_array($token)) {\n                if (($tokens[$i] = $this->parseToken($token, $args, $locale)) === false) {\n                    $this->_errorCode = -1;\n                    $this->_errorMessage = 'Message pattern is invalid.';\n\n                    return false;\n                }\n            }\n        }\n\n        return implode('', $tokens);\n    }\n\n    /**\n     * Tokenizes a pattern by separating normal text from replaceable patterns.\n     * @param string $pattern patter to tokenize\n     * @return array|bool array of tokens or false on failure\n     */\n    private static function tokenizePattern($pattern)\n    {\n        $charset = Yii::$app ? Yii::$app->charset : 'UTF-8';\n        $depth = 1;\n        if (($start = $pos = mb_strpos($pattern, '{', 0, $charset)) === false) {\n            return [$pattern];\n        }\n        $tokens = [mb_substr($pattern, 0, $pos, $charset)];\n        while (true) {\n            $open = mb_strpos($pattern, '{', $pos + 1, $charset);\n            $close = mb_strpos($pattern, '}', $pos + 1, $charset);\n            if ($open === false && $close === false) {\n                break;\n            }\n            if ($open === false) {\n                $open = mb_strlen($pattern, $charset);\n            }\n            if ($close > $open) {\n                $depth++;\n                $pos = $open;\n            } else {\n                $depth--;\n                $pos = $close;\n            }\n            if ($depth === 0) {\n                $tokens[] = explode(',', mb_substr($pattern, $start + 1, $pos - $start - 1, $charset), 3);\n                $start = $pos + 1;\n                $tokens[] = mb_substr($pattern, $start, $open - $start, $charset);\n                $start = $open;\n            }\n\n            if ($depth !== 0 && ($open === false || $close === false)) {\n                break;\n            }\n        }\n        if ($depth !== 0) {\n            return false;\n        }\n\n        return $tokens;\n    }\n\n    /**\n     * Parses a token.\n     * @param array $token the token to parse\n     * @param array $args arguments to replace\n     * @param string $locale the locale\n     * @return bool|string parsed token or false on failure\n     * @throws \\yii\\base\\NotSupportedException when unsupported formatting is used.\n     */\n    private function parseToken($token, $args, $locale)\n    {\n        // parsing pattern based on ICU grammar:\n        // https://unicode-org.github.io/icu-docs/#/icu4c/classMessageFormat.html\n        $charset = Yii::$app ? Yii::$app->charset : 'UTF-8';\n        $param = trim($token[0]);\n        if (isset($args[$param])) {\n            $arg = $args[$param];\n        } else {\n            return '{' . implode(',', $token) . '}';\n        }\n        $type = isset($token[1]) ? trim($token[1]) : 'none';\n        switch ($type) {\n            case 'date':\n            case 'time':\n            case 'spellout':\n            case 'ordinal':\n            case 'duration':\n            case 'choice':\n            case 'selectordinal':\n                throw new NotSupportedException(\"Message format '$type' is not supported. You have to install PHP intl extension to use this feature.\");\n            case 'number':\n                $format = isset($token[2]) ? trim($token[2]) : null;\n                if (is_numeric($arg) && ($format === null || $format === 'integer')) {\n                    $number = number_format($arg);\n                    if ($format === null && ($pos = strpos($arg, '.')) !== false) {\n                        // add decimals with unknown length\n                        $number .= '.' . substr($arg, $pos + 1);\n                    }\n\n                    return $number;\n                }\n                throw new NotSupportedException(\"Message format 'number' is only supported for integer values. You have to install PHP intl extension to use this feature.\");\n            case 'none':\n                return $arg;\n            case 'select':\n                /* https://unicode-org.github.io/icu-docs/#/icu4c/classicu_1_1SelectFormat.html\n                selectStyle = (selector '{' message '}')+\n                */\n                if (!isset($token[2])) {\n                    return false;\n                }\n                $select = self::tokenizePattern($token[2]);\n                $c = count($select);\n                $message = false;\n                for ($i = 0; $i + 1 < $c; $i++) {\n                    if (is_array($select[$i]) || !is_array($select[$i + 1])) {\n                        return false;\n                    }\n                    $selector = trim($select[$i++]);\n                    if ($message === false && $selector === 'other' || $selector == $arg) {\n                        $message = implode(',', $select[$i]);\n                    }\n                }\n                if ($message !== false) {\n                    return $this->fallbackFormat($message, $args, $locale);\n                }\n                break;\n            case 'plural':\n                /* https://unicode-org.github.io/icu-docs/#/icu4c/classicu_1_1PluralFormat.html\n                pluralStyle = [offsetValue] (selector '{' message '}')+\n                offsetValue = \"offset:\" number\n                selector = explicitValue | keyword\n                explicitValue = '=' number  // adjacent, no white space in between\n                keyword = [^[[:Pattern_Syntax:][:Pattern_White_Space:]]]+\n                message: see MessageFormat\n                */\n                if (!isset($token[2])) {\n                    return false;\n                }\n                $plural = self::tokenizePattern($token[2]);\n                $c = count($plural);\n                $message = false;\n                $offset = 0;\n                for ($i = 0; $i + 1 < $c; $i++) {\n                    if (is_array($plural[$i]) || !is_array($plural[$i + 1])) {\n                        return false;\n                    }\n                    $selector = trim($plural[$i++]);\n\n                    if ($i == 1 && strncmp($selector, 'offset:', 7) === 0) {\n                        $offset = (int) trim(mb_substr($selector, 7, ($pos = mb_strpos(str_replace([\"\\n\", \"\\r\", \"\\t\"], ' ', $selector), ' ', 7, $charset)) - 7, $charset));\n                        $selector = trim(mb_substr($selector, $pos + 1, mb_strlen($selector, $charset), $charset));\n                    }\n                    if (\n                        $message === false && $selector === 'other' ||\n                        strncmp($selector, '=', 1) === 0 && (int) mb_substr($selector, 1, mb_strlen($selector, $charset), $charset) === $arg ||\n                        $selector === 'one' && $arg - $offset == 1\n                    ) {\n                        $message = implode(',', str_replace('#', $arg - $offset, $plural[$i]));\n                    }\n                }\n                if ($message !== false) {\n                    return $this->fallbackFormat($message, $args, $locale);\n                }\n                break;\n        }\n\n        return false;\n    }\n}\n"
  },
  {
    "path": "framework/i18n/MessageSource.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\i18n;\n\nuse Yii;\nuse yii\\base\\Component;\n\n/**\n * MessageSource is the base class for message translation repository classes.\n *\n * A message source stores message translations in some persistent storage.\n *\n * Child classes should override [[loadMessages()]] to provide translated messages.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass MessageSource extends Component\n{\n    /**\n     * @event MissingTranslationEvent an event that is triggered when a message translation is not found.\n     */\n    public const EVENT_MISSING_TRANSLATION = 'missingTranslation';\n    /**\n     * @var bool whether to force message translation when the source and target languages are the same.\n     * Defaults to false, meaning translation is only performed when source and target languages are different.\n     */\n    public $forceTranslation = false;\n    /**\n     * @var string|null the language that the original messages are in. If not set, it will use the value of\n     * [[\\yii\\base\\Application::sourceLanguage]].\n     */\n    public $sourceLanguage;\n\n    private $_messages = [];\n\n\n    /**\n     * Initializes this component.\n     */\n    public function init()\n    {\n        parent::init();\n        if ($this->sourceLanguage === null) {\n            $this->sourceLanguage = Yii::$app->sourceLanguage;\n        }\n    }\n\n    /**\n     * Loads the message translation for the specified language and category.\n     * If translation for specific locale code such as `en-US` isn't found it\n     * tries more generic `en`.\n     *\n     * @param string $category the message category\n     * @param string $language the target language\n     * @return array the loaded messages. The keys are original messages, and the values\n     * are translated messages.\n     */\n    protected function loadMessages($category, $language)\n    {\n        return [];\n    }\n\n    /**\n     * Translates a message to the specified language.\n     *\n     * Note that unless [[forceTranslation]] is true, if the target language\n     * is the same as the [[sourceLanguage|source language]], the message\n     * will NOT be translated.\n     *\n     * If a translation is not found, a [[EVENT_MISSING_TRANSLATION|missingTranslation]] event will be triggered.\n     *\n     * @param string $category the message category\n     * @param string $message the message to be translated\n     * @param string $language the target language\n     * @return string|bool the translated message or false if translation wasn't found or isn't required\n     */\n    public function translate($category, $message, $language)\n    {\n        if ($this->forceTranslation || $language !== $this->sourceLanguage) {\n            return $this->translateMessage($category, $message, $language);\n        }\n\n        return false;\n    }\n\n    /**\n     * Translates the specified message.\n     * If the message is not found, a [[EVENT_MISSING_TRANSLATION|missingTranslation]] event will be triggered.\n     * If there is an event handler, it may provide a [[MissingTranslationEvent::$translatedMessage|fallback translation]].\n     * If no fallback translation is provided this method will return `false`.\n     * @param string $category the category that the message belongs to.\n     * @param string $message the message to be translated.\n     * @param string $language the target language.\n     * @return string|bool the translated message or false if translation wasn't found.\n     */\n    protected function translateMessage($category, $message, $language)\n    {\n        $key = $language . '/' . $category;\n        if (!isset($this->_messages[$key])) {\n            $this->_messages[$key] = $this->loadMessages($category, $language);\n        }\n        if (isset($this->_messages[$key][$message]) && $this->_messages[$key][$message] !== '') {\n            return $this->_messages[$key][$message];\n        } elseif ($this->hasEventHandlers(self::EVENT_MISSING_TRANSLATION)) {\n            $event = new MissingTranslationEvent([\n                'category' => $category,\n                'message' => $message,\n                'language' => $language,\n            ]);\n            $this->trigger(self::EVENT_MISSING_TRANSLATION, $event);\n            if ($event->translatedMessage !== null) {\n                return $this->_messages[$key][$message] = $event->translatedMessage;\n            }\n        }\n\n        return $this->_messages[$key][$message] = false;\n    }\n}\n"
  },
  {
    "path": "framework/i18n/MissingTranslationEvent.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\i18n;\n\nuse yii\\base\\Event;\n\n/**\n * MissingTranslationEvent represents the parameter for the [[MessageSource::EVENT_MISSING_TRANSLATION]] event.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass MissingTranslationEvent extends Event\n{\n    /**\n     * @var string the message to be translated. An event handler may use this to provide a fallback translation\n     * and set [[translatedMessage]] if possible.\n     */\n    public $message;\n    /**\n     * @var string|null the translated message. An event handler may overwrite this property\n     * with a translated version of [[message]] if possible. If not set (null), it means the message is not translated.\n     */\n    public $translatedMessage;\n    /**\n     * @var string the category that the message belongs to\n     */\n    public $category;\n    /**\n     * @var string the language ID (e.g. en-US) that the message is to be translated to\n     */\n    public $language;\n}\n"
  },
  {
    "path": "framework/i18n/PhpMessageSource.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\i18n;\n\nuse Yii;\nuse yii\\base\\InvalidArgumentException;\n\n/**\n * PhpMessageSource represents a message source that stores translated messages in PHP scripts.\n *\n * PhpMessageSource uses PHP arrays to keep message translations.\n *\n * - Each PHP script contains one array which stores the message translations in one particular\n *   language and for a single message category;\n * - Each PHP script is saved as a file named as \"[[basePath]]/LanguageID/CategoryName.php\";\n * - Within each PHP script, the message translations are returned as an array like the following:\n *\n * ```\n * return [\n *     'original message 1' => 'translated message 1',\n *     'original message 2' => 'translated message 2',\n * ];\n * ```\n *\n * You may use [[fileMap]] to customize the association between category names and the file names.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass PhpMessageSource extends MessageSource\n{\n    /**\n     * @var string the base path for all translated messages. Defaults to '@app/messages'.\n     */\n    public $basePath = '@app/messages';\n    /**\n     * @var array mapping between message categories and the corresponding message file paths.\n     * The file paths are relative to [[basePath]]. For example,\n     *\n     * ```\n     * [\n     *     'core' => 'core.php',\n     *     'ext' => 'extensions.php',\n     * ]\n     * ```\n     */\n    public $fileMap;\n\n\n    /**\n     * Loads the message translation for the specified $language and $category.\n     * If translation for specific locale code such as `en-US` isn't found it\n     * tries more generic `en`. When both are present, the `en-US` messages will be merged\n     * over `en`. See [[loadFallbackMessages]] for details.\n     * If the $language is less specific than [[sourceLanguage]], the method will try to\n     * load the messages for [[sourceLanguage]]. For example: [[sourceLanguage]] is `en-GB`,\n     * $language is `en`. The method will load the messages for `en` and merge them over `en-GB`.\n     *\n     * @param string $category the message category\n     * @param string $language the target language\n     * @return array the loaded messages. The keys are original messages, and the values are the translated messages.\n     * @see loadFallbackMessages\n     * @see sourceLanguage\n     */\n    protected function loadMessages($category, $language)\n    {\n        $messageFile = $this->getMessageFilePath($category, $language);\n        $messages = $this->loadMessagesFromFile($messageFile);\n\n        $fallbackLanguage = substr((string)$language, 0, 2);\n        $fallbackSourceLanguage = substr($this->sourceLanguage, 0, 2);\n\n        if ($fallbackLanguage !== '' && $language !== $fallbackLanguage) {\n            $messages = $this->loadFallbackMessages($category, $fallbackLanguage, $messages, $messageFile);\n        } elseif ($fallbackSourceLanguage !== '' && $language === $fallbackSourceLanguage) {\n            $messages = $this->loadFallbackMessages($category, $this->sourceLanguage, $messages, $messageFile);\n        } elseif ($messages === null) {\n            Yii::warning(\"The message file for category '$category' does not exist: $messageFile\", __METHOD__);\n        }\n\n        return (array) $messages;\n    }\n\n    /**\n     * The method is normally called by [[loadMessages]] to load the fallback messages for the language.\n     * Method tries to load the $category messages for the $fallbackLanguage and adds them to the $messages array.\n     *\n     * @param string $category the message category\n     * @param string $fallbackLanguage the target fallback language\n     * @param array $messages the array of previously loaded translation messages.\n     * The keys are original messages, and the values are the translated messages.\n     * @param string $originalMessageFile the path to the file with messages. Used to log an error message\n     * in case when no translations were found.\n     * @return array the loaded messages. The keys are original messages, and the values are the translated messages.\n     * @since 2.0.7\n     */\n    protected function loadFallbackMessages($category, $fallbackLanguage, $messages, $originalMessageFile)\n    {\n        $fallbackMessageFile = $this->getMessageFilePath($category, $fallbackLanguage);\n        $fallbackMessages = $this->loadMessagesFromFile($fallbackMessageFile);\n\n        if (\n            $messages === null && $fallbackMessages === null\n            && $fallbackLanguage !== $this->sourceLanguage\n            && strpos($this->sourceLanguage, $fallbackLanguage) !== 0\n        ) {\n            Yii::error(\"The message file for category '$category' does not exist: $originalMessageFile \"\n                . \"Fallback file does not exist as well: $fallbackMessageFile\", __METHOD__);\n        } elseif (empty($messages)) {\n            return $fallbackMessages;\n        } elseif (!empty($fallbackMessages)) {\n            foreach ($fallbackMessages as $key => $value) {\n                if (!empty($value) && empty($messages[$key])) {\n                    $messages[$key] = $value;\n                }\n            }\n        }\n\n        return (array) $messages;\n    }\n\n    /**\n     * Returns message file path for the specified language and category.\n     *\n     * @param string $category the message category\n     * @param string $language the target language\n     * @return string path to message file\n     */\n    protected function getMessageFilePath($category, $language)\n    {\n        $language = (string) $language;\n        if ($language !== '' && !preg_match('/^[a-z0-9_-]+$/i', $language)) {\n            throw new InvalidArgumentException(sprintf('Invalid language code: \"%s\".', $language));\n        }\n        $messageFile = Yii::getAlias($this->basePath) . \"/$language/\";\n        if (isset($this->fileMap[$category])) {\n            $messageFile .= $this->fileMap[$category];\n        } else {\n            $messageFile .= str_replace('\\\\', '/', $category) . '.php';\n        }\n\n        return $messageFile;\n    }\n\n    /**\n     * Loads the message translation for the specified language and category or returns null if file doesn't exist.\n     *\n     * @param string $messageFile path to message file\n     * @return array|null array of messages or null if file not found\n     */\n    protected function loadMessagesFromFile($messageFile)\n    {\n        if (is_file($messageFile)) {\n            $messages = include $messageFile;\n            if (!is_array($messages)) {\n                $messages = [];\n            }\n\n            return $messages;\n        }\n\n        return null;\n    }\n}\n"
  },
  {
    "path": "framework/i18n/migrations/m150207_210500_i18n_init.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nuse yii\\db\\Migration;\n\n/**\n * Initializes i18n messages tables.\n *\n *\n *\n * @author Dmitry Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.7\n */\nclass m150207_210500_i18n_init extends Migration\n{\n    public function up()\n    {\n        $tableOptions = null;\n        if ($this->db->driverName === 'mysql') {\n            // https://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci\n            $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';\n        }\n\n        $this->createTable('{{%source_message}}', [\n            'id' => $this->primaryKey(),\n            'category' => $this->string(),\n            'message' => $this->text(),\n        ], $tableOptions);\n\n        $this->createTable('{{%message}}', [\n            'id' => $this->integer()->notNull(),\n            'language' => $this->string(16)->notNull(),\n            'translation' => $this->text(),\n        ], $tableOptions);\n\n        $this->addPrimaryKey('pk_message_id_language', '{{%message}}', ['id', 'language']);\n        $onUpdateConstraint = 'RESTRICT';\n        if ($this->db->driverName === 'sqlsrv') {\n            // 'NO ACTION' is equivalent to 'RESTRICT' in MSSQL\n            $onUpdateConstraint = 'NO ACTION';\n        }\n        $this->addForeignKey('fk_message_source_message', '{{%message}}', 'id', '{{%source_message}}', 'id', 'CASCADE', $onUpdateConstraint);\n        $this->createIndex('idx_source_message_category', '{{%source_message}}', 'category');\n        $this->createIndex('idx_message_language', '{{%message}}', 'language');\n    }\n\n    public function down()\n    {\n        $this->dropForeignKey('fk_message_source_message', '{{%message}}');\n        $this->dropTable('{{%message}}');\n        $this->dropTable('{{%source_message}}');\n    }\n}\n"
  },
  {
    "path": "framework/i18n/migrations/schema-mssql.sql",
    "content": "/**\n * Database schema required by \\yii\\i18n\\DbMessageSource.\n *\n * @author Dmitry Naumenko <d.naumenko.a@gmail.com>\n * @link https://www.yiiframework.com/\n * @copyright 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n * @since 2.0.7\n */\n\nif object_id('[source_message]', 'U') is not null\n    drop table [source_message];\n\nif object_id('[message]', 'U') is not null\n    drop table [message];\n\nCREATE TABLE [source_message]\n(\n   [id]          integer IDENTITY PRIMARY KEY,\n   [category]    varchar(255),\n   [message]     text\n);\n\nCREATE TABLE [message]\n(\n   [id]          integer NOT NULL,\n   [language]    varchar(16) NOT NULL,\n   [translation] text\n);\n\nALTER TABLE [message] ADD CONSTRAINT [pk_message_id_language] PRIMARY KEY ([id], [language]);\nALTER TABLE [message] ADD CONSTRAINT [fk_message_source_message] FOREIGN KEY ([id]) REFERENCES [source_message] ([id]) ON UPDATE NO ACTION ON DELETE CASCADE;\n\nCREATE INDEX [idx_message_language] on [message] ([language]);\nCREATE INDEX [idx_source_message_category] on [source_message] ([category]);\n"
  },
  {
    "path": "framework/i18n/migrations/schema-mysql.sql",
    "content": "/**\n * Database schema required by \\yii\\i18n\\DbMessageSource.\n *\n * @author Dmitry Naumenko <d.naumenko.a@gmail.com>\n * @link https://www.yiiframework.com/\n * @copyright 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n * @since 2.0.7\n */\n\n\ndrop table if exists `source_message`;\ndrop table if exists `message`;\n\nCREATE TABLE `source_message`\n(\n   `id`          integer NOT NULL AUTO_INCREMENT PRIMARY KEY,\n   `category`    varchar(255),\n   `message`     text\n);\n\nCREATE TABLE `message`\n(\n   `id`          integer NOT NULL,\n   `language`    varchar(16) NOT NULL,\n   `translation` text\n);\n\nALTER TABLE `message` ADD CONSTRAINT `pk_message_id_language` PRIMARY KEY (`id`, `language`);\nALTER TABLE `message` ADD CONSTRAINT `fk_message_source_message` FOREIGN KEY (`id`) REFERENCES `source_message` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE;\n\nCREATE INDEX idx_message_language ON message (language);\nCREATE INDEX idx_source_message_category ON source_message (category);\n"
  },
  {
    "path": "framework/i18n/migrations/schema-oci.sql",
    "content": "/**\n * Database schema required by \\yii\\i18n\\DbMessageSource.\n *\n * @author Dmitry Naumenko <d.naumenko.a@gmail.com>\n * @link https://www.yiiframework.com/\n * @copyright 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n * @since 2.0.7\n */\n\n\ndrop table if exists \"source_message\";\ndrop table if exists \"message\";\n\nCREATE TABLE \"source_message\"\n(\n\t\"id\"          integer NOT NULL PRIMARY KEY,\n\t\"category\"    varchar(255),\n\t\"message\"     clob\n);\nCREATE SEQUENCE \"source_message_SEQ\";\n\nCREATE TABLE \"message\"\n(\n\t\"id\"          integer NOT NULL,\n\t\"language\"    varchar(16) NOT NULL,\n\t\"translation\" clob,\n\tprimary key (\"id\", \"language\"),\n\tforeign key (\"id\") references \"source_message\" (\"id\") on delete cascade\n);\n\nCREATE INDEX idx_message_language ON \"message\"(\"language\");\nCREATE INDEX idx_source_message_category ON \"source_message\"(\"category\");\n"
  },
  {
    "path": "framework/i18n/migrations/schema-pgsql.sql",
    "content": "/**\n * Database schema required by \\yii\\i18n\\DbMessageSource.\n *\n * @author Dmitry Naumenko <d.naumenko.a@gmail.com>\n * @link https://www.yiiframework.com/\n * @copyright 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n * @since 2.0.7\n */\n\n\ndrop table if exists \"source_message\";\ndrop table if exists \"message\";\n\nCREATE SEQUENCE source_message_seq;\n\nCREATE TABLE \"source_message\"\n(\n   \"id\"         integer NOT NULL PRIMARY KEY DEFAULT nextval('source_message_seq'),\n   \"category\"   varchar(255),\n   \"message\"    text\n);\n\nCREATE TABLE \"message\"\n(\n   \"id\"          integer NOT NULL,\n   \"language\"    varchar(16) NOT NULL,\n   \"translation\" text\n);\n\nALTER TABLE \"message\" ADD CONSTRAINT \"pk_message_id_language\" PRIMARY KEY (\"id\", \"language\");\nALTER TABLE \"message\" ADD CONSTRAINT \"fk_message_source_message\" FOREIGN KEY (\"id\") REFERENCES \"source_message\" (\"id\") ON UPDATE RESTRICT ON DELETE CASCADE;\n\nCREATE INDEX \"idx_message_language\" ON \"message\" USING btree (language);\nALTER TABLE \"message\" CLUSTER ON \"idx_message_language\";\n\nCREATE INDEX \"idx_source_message_category\" ON \"source_message\" USING btree (category);\nALTER TABLE \"source_message\" CLUSTER ON \"idx_source_message_category\";\n"
  },
  {
    "path": "framework/i18n/migrations/schema-sqlite.sql",
    "content": "/**\n * Database schema required by \\yii\\i18n\\DbMessageSource.\n *\n * @author Dmitry Naumenko <d.naumenko.a@gmail.com>\n * @link https://www.yiiframework.com/\n * @copyright 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n * @since 2.0.7\n */\n\ndrop table if exists `source_message`;\ndrop table if exists `message`;\n\nCREATE TABLE `source_message`\n(\n   `id`          integer PRIMARY KEY AUTOINCREMENT NOT NULL,\n   `category`    varchar(255),\n   `message`     text\n);\n\nCREATE TABLE `message`\n(\n   `id`          integer NOT NULL REFERENCES `source_message` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,\n   `language`    varchar(16) NOT NULL,\n   `translation` text,\n   PRIMARY KEY (`id`, `language`)\n);\n\nCREATE INDEX idx_message_language ON message (language);\nCREATE INDEX idx_source_message_category ON source_message (category);\n"
  },
  {
    "path": "framework/log/DbTarget.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\log;\n\nuse yii\\base\\InvalidConfigException;\nuse yii\\db\\Connection;\nuse yii\\db\\Exception;\nuse yii\\di\\Instance;\nuse yii\\helpers\\VarDumper;\n\n/**\n * DbTarget stores log messages in a database table.\n *\n * The database connection is specified by [[db]]. Database schema could be initialized by applying migration:\n *\n * ```\n * yii migrate --migrationPath=@yii/log/migrations/\n * ```\n *\n * If you don't want to use migration and need SQL instead, files for all databases are in migrations directory.\n *\n * You may change the name of the table used to store the data by setting [[logTable]].\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass DbTarget extends Target\n{\n    /**\n     * @var Connection|array|string the DB connection object or the application component ID of the DB connection.\n     * After the DbTarget object is created, if you want to change this property, you should only assign it\n     * with a DB connection object.\n     * Starting from version 2.0.2, this can also be a configuration array for creating the object.\n     */\n    public $db = 'db';\n    /**\n     * @var string name of the DB table to store cache content. Defaults to \"log\".\n     */\n    public $logTable = '{{%log}}';\n\n\n    /**\n     * Initializes the DbTarget component.\n     * This method will initialize the [[db]] property to make sure it refers to a valid DB connection.\n     * @throws InvalidConfigException if [[db]] is invalid.\n     */\n    public function init()\n    {\n        parent::init();\n        $this->db = Instance::ensure($this->db, Connection::className());\n    }\n\n    /**\n     * Stores log messages to DB.\n     * Starting from version 2.0.14, this method throws LogRuntimeException in case the log can not be exported.\n     * @throws Exception\n     * @throws LogRuntimeException\n     */\n    public function export()\n    {\n        if ($this->db->getTransaction()) {\n            // create new database connection, if there is an open transaction\n            // to ensure insert statement is not affected by a rollback\n            $this->db = clone $this->db;\n        }\n\n        $tableName = $this->db->quoteTableName($this->logTable);\n        $sql = \"INSERT INTO $tableName ([[level]], [[category]], [[log_time]], [[prefix]], [[message]])\n                VALUES (:level, :category, :log_time, :prefix, :message)\";\n        $command = $this->db->createCommand($sql);\n        foreach ($this->messages as $message) {\n            list($text, $level, $category, $timestamp) = $message;\n            if (!is_string($text)) {\n                // exceptions may not be serializable if in the call stack somewhere is a Closure\n                if ($text instanceof \\Exception || $text instanceof \\Throwable) {\n                    $text = (string) $text;\n                } else {\n                    $text = VarDumper::export($text);\n                }\n            }\n            if (\n                $command->bindValues([\n                    ':level' => $level,\n                    ':category' => $category,\n                    ':log_time' => $timestamp,\n                    ':prefix' => $this->getMessagePrefix($message),\n                    ':message' => $text,\n                ])->execute() > 0\n            ) {\n                continue;\n            }\n            throw new LogRuntimeException('Unable to export log through database!');\n        }\n    }\n}\n"
  },
  {
    "path": "framework/log/Dispatcher.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\log;\n\nuse Yii;\nuse yii\\base\\Component;\nuse yii\\base\\ErrorHandler;\n\n/**\n * Dispatcher manages a set of [[Target|log targets]].\n *\n * Dispatcher implements the [[dispatch()]]-method that forwards the log messages from a [[Logger]] to\n * the registered log [[targets]].\n *\n * An instance of Dispatcher is registered as a core application component and can be accessed using `Yii::$app->log`.\n *\n * You may configure the targets in application configuration, like the following:\n *\n * ```\n * [\n *     'components' => [\n *         'log' => [\n *             'targets' => [\n *                 'file' => [\n *                     'class' => 'yii\\log\\FileTarget',\n *                     'levels' => ['trace', 'info'],\n *                     'categories' => ['yii\\*'],\n *                 ],\n *                 'email' => [\n *                     'class' => 'yii\\log\\EmailTarget',\n *                     'levels' => ['error', 'warning'],\n *                     'message' => [\n *                         'to' => 'admin@example.com',\n *                     ],\n *                 ],\n *             ],\n *         ],\n *     ],\n * ]\n * ```\n *\n * Each log target can have a name and can be referenced via the [[targets]] property as follows:\n *\n * ```\n * Yii::$app->log->targets['file']->enabled = false;\n * ```\n *\n * @property int $flushInterval How many messages should be logged before they are sent to targets. See\n * [[getFlushInterval()]] and [[setFlushInterval()]] for details.\n * @property Logger $logger The logger. If not set, [[Yii::getLogger()]] will be used. Note that the type of\n * this property differs in getter and setter. See [[getLogger()]] and [[setLogger()]] for details.\n * @property int $traceLevel How many application call stacks should be logged together with each message.\n * This method returns the value of [[Logger::traceLevel]]. Defaults to 0.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Dispatcher extends Component\n{\n    /**\n     * @var array|Target[] the log targets. Each array element represents a single [[Target|log target]] instance\n     * or the configuration for creating the log target instance.\n     */\n    public $targets = [];\n\n    /**\n     * @var Logger|null the logger.\n     */\n    private $_logger;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function __construct($config = [])\n    {\n        // ensure logger gets set before any other config option\n        if (isset($config['logger'])) {\n            $this->setLogger($config['logger']);\n            unset($config['logger']);\n        }\n        // connect logger and dispatcher\n        $this->getLogger();\n\n        parent::__construct($config);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n\n        foreach ($this->targets as $name => $target) {\n            if (!$target instanceof Target) {\n                $this->targets[$name] = Yii::createObject($target);\n            }\n        }\n    }\n\n    /**\n     * Gets the connected logger.\n     * If not set, [[Yii::getLogger()]] will be used.\n     * @return Logger the logger.\n     */\n    public function getLogger()\n    {\n        if ($this->_logger === null) {\n            $this->setLogger(Yii::getLogger());\n        }\n\n        return $this->_logger;\n    }\n\n    /**\n     * Sets the connected logger.\n     * @param Logger|string|array $value the logger to be used. This can either be a logger instance\n     * or a configuration that will be used to create one using [[Yii::createObject()]].\n     * If you are providing custom logger configuration and would like it to be used for the whole application\n     * and not just for the dispatcher you should use [[Yii::setLogger()]] instead.\n     */\n    public function setLogger($value)\n    {\n        if (is_string($value) || is_array($value)) {\n            $value = Yii::createObject($value);\n        }\n\n        $this->_logger = $value;\n        $this->_logger->dispatcher = $this;\n    }\n\n    /**\n     * @return int how many application call stacks should be logged together with each message.\n     * This method returns the value of [[Logger::traceLevel]]. Defaults to 0.\n     */\n    public function getTraceLevel()\n    {\n        return $this->getLogger()->traceLevel;\n    }\n\n    /**\n     * @param int $value how many application call stacks should be logged together with each message.\n     * This method will set the value of [[Logger::traceLevel]]. If the value is greater than 0,\n     * at most that number of call stacks will be logged. Note that only application call stacks are counted.\n     * Defaults to 0.\n     */\n    public function setTraceLevel($value)\n    {\n        $this->getLogger()->traceLevel = $value;\n    }\n\n    /**\n     * @return int how many messages should be logged before they are sent to targets.\n     * This method returns the value of [[Logger::flushInterval]].\n     */\n    public function getFlushInterval()\n    {\n        return $this->getLogger()->flushInterval;\n    }\n\n    /**\n     * @param int $value how many messages should be logged before they are sent to targets.\n     * This method will set the value of [[Logger::flushInterval]].\n     * Defaults to 1000, meaning the [[Logger::flush()]] method will be invoked once every 1000 messages logged.\n     * Set this property to be 0 if you don't want to flush messages until the application terminates.\n     * This property mainly affects how much memory will be taken by the logged messages.\n     * A smaller value means less memory, but will increase the execution time due to the overhead of [[Logger::flush()]].\n     */\n    public function setFlushInterval($value)\n    {\n        $this->getLogger()->flushInterval = $value;\n    }\n\n    /**\n     * Dispatches the logged messages to [[targets]].\n     * @param array $messages the logged messages\n     * @param bool $final whether this method is called at the end of the current application\n     */\n    public function dispatch($messages, $final)\n    {\n        $targetErrors = [];\n        foreach ($this->targets as $target) {\n            if (!$target->enabled) {\n                continue;\n            }\n            try {\n                $target->collect($messages, $final);\n            } catch (\\Throwable $t) {\n                $target->enabled = false;\n                $targetErrors[] = $this->generateTargetFailErrorMessage($target, $t, __METHOD__);\n            } catch (\\Exception $e) {\n                $target->enabled = false;\n                $targetErrors[] = $this->generateTargetFailErrorMessage($target, $e, __METHOD__);\n            }\n        }\n\n        if (!empty($targetErrors)) {\n            $this->dispatch($targetErrors, true);\n        }\n    }\n\n    /**\n     * Generate target error message\n     *\n     * @param Target $target log target object\n     * @param \\Throwable $throwable catched exception\n     * @param string $method full method path\n     * @return array generated error message data\n     * @since 2.0.32\n     */\n    protected function generateTargetFailErrorMessage($target, $throwable, $method)\n    {\n        return [\n            'Unable to send log via ' . get_class($target) . ': ' . ErrorHandler::convertExceptionToVerboseString($throwable),\n            Logger::LEVEL_WARNING,\n            $method,\n            microtime(true),\n            [],\n        ];\n    }\n}\n"
  },
  {
    "path": "framework/log/EmailTarget.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\log;\n\nuse Yii;\nuse yii\\base\\InvalidConfigException;\nuse yii\\di\\Instance;\nuse yii\\mail\\MailerInterface;\n\n/**\n * EmailTarget sends selected log messages to the specified email addresses.\n *\n * You may configure the email to be sent by setting the [[message]] property, through which\n * you can set the target email addresses, subject, etc.:\n *\n * ```\n * 'components' => [\n *     'log' => [\n *          'targets' => [\n *              [\n *                  'class' => 'yii\\log\\EmailTarget',\n *                  'mailer' => 'mailer',\n *                  'levels' => ['error', 'warning'],\n *                  'message' => [\n *                      'from' => ['log@example.com'],\n *                      'to' => ['developer1@example.com', 'developer2@example.com'],\n *                      'subject' => 'Log message',\n *                  ],\n *              ],\n *          ],\n *     ],\n * ],\n * ```\n *\n * In the above `mailer` is ID of the component that sends email and should be already configured.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass EmailTarget extends Target\n{\n    /**\n     * @var array the configuration array for creating a [[\\yii\\mail\\MessageInterface|message]] object.\n     * Note that the \"to\" option must be set, which specifies the destination email address(es).\n     */\n    public $message = [];\n    /**\n     * @var MailerInterface|array|string the mailer object or the application component ID of the mailer object.\n     * After the EmailTarget object is created, if you want to change this property, you should only assign it\n     * with a mailer object.\n     * Starting from version 2.0.2, this can also be a configuration array for creating the object.\n     */\n    public $mailer = 'mailer';\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        if (empty($this->message['to'])) {\n            throw new InvalidConfigException('The \"to\" option must be set for EmailTarget::message.');\n        }\n        $this->mailer = Instance::ensure($this->mailer, 'yii\\mail\\MailerInterface');\n    }\n\n    /**\n     * Sends log messages to specified email addresses.\n     * Starting from version 2.0.14, this method throws LogRuntimeException in case the log can not be exported.\n     * @throws LogRuntimeException\n     */\n    public function export()\n    {\n        // moved initialization of subject here because of the following issue\n        // https://github.com/yiisoft/yii2/issues/1446\n        if (empty($this->message['subject'])) {\n            $this->message['subject'] = 'Application Log';\n        }\n        $messages = array_map([$this, 'formatMessage'], $this->messages);\n        $body = wordwrap(implode(\"\\n\", $messages), 70);\n        $message = $this->composeMessage($body);\n        if (!$message->send($this->mailer)) {\n            throw new LogRuntimeException('Unable to export log through email!');\n        }\n    }\n\n    /**\n     * Composes a mail message with the given body content.\n     * @param string $body the body content\n     * @return \\yii\\mail\\MessageInterface $message\n     */\n    protected function composeMessage($body)\n    {\n        $message = $this->mailer->compose();\n        Yii::configure($message, $this->message);\n        $message->setTextBody($body);\n\n        return $message;\n    }\n}\n"
  },
  {
    "path": "framework/log/FileTarget.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\log;\n\nuse Yii;\nuse yii\\base\\InvalidConfigException;\nuse yii\\helpers\\FileHelper;\n\n/**\n * FileTarget records log messages in a file.\n *\n * The log file is specified via [[logFile]]. If the size of the log file exceeds\n * [[maxFileSize]] (in kilo-bytes), a rotation will be performed, which renames\n * the current log file by suffixing the file name with '.1'. All existing log\n * files are moved backwards by one place, i.e., '.2' to '.3', '.1' to '.2', and so on.\n * The property [[maxLogFiles]] specifies how many history files to keep.\n *\n * Since 2.0.46 rotation of the files is done only by copy and the\n * `rotateByCopy` property is deprecated.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass FileTarget extends Target\n{\n    /**\n     * @var string|null log file path or [path alias](guide:concept-aliases). If not set, it will use the \"@runtime/logs/app.log\" file.\n     * The directory containing the log files will be automatically created if not existing.\n     */\n    public $logFile;\n    /**\n     * @var bool whether log files should be rotated when they reach a certain [[maxFileSize|maximum size]].\n     * Log rotation is enabled by default. This property allows you to disable it, when you have configured\n     * an external tools for log rotation on your server.\n     * @since 2.0.3\n     */\n    public $enableRotation = true;\n    /**\n     * @var int maximum log file size, in kilo-bytes. Defaults to 10240, meaning 10MB.\n     */\n    public $maxFileSize = 10240; // in KB\n    /**\n     * @var int number of log files used for rotation. Defaults to 5.\n     */\n    public $maxLogFiles = 5;\n    /**\n     * @var int|null the permission to be set for newly created log files.\n     * This value will be used by PHP chmod() function. No umask will be applied.\n     * If not set, the permission will be determined by the current environment.\n     */\n    public $fileMode;\n    /**\n     * @var int the permission to be set for newly created directories.\n     * This value will be used by PHP chmod() function. No umask will be applied.\n     * Defaults to 0775, meaning the directory is read-writable by owner and group,\n     * but read-only for other users.\n     */\n    public $dirMode = 0775;\n    /**\n     * @var bool Whether to rotate log files by copy and truncate in contrast to rotation by\n     * renaming files. Defaults to `true` to be more compatible with log tailers and windows\n     * systems which do not play well with rename on open files. Rotation by renaming however is\n     * a bit faster.\n     *\n     * The problem with windows systems where the [rename()](https://www.php.net/manual/en/function.rename.php)\n     * function does not work with files that are opened by some process is described in a\n     * [comment by Martin Pelletier](https://www.php.net/manual/en/function.rename.php#102274) in\n     * the PHP documentation. By setting rotateByCopy to `true` you can work\n     * around this.\n     * @deprecated since 2.0.46 and setting it to false has no effect anymore\n     * since rotating is now always done by copy.\n     */\n    public $rotateByCopy = true;\n\n\n    /**\n     * Initializes the route.\n     * This method is invoked after the route is created by the route manager.\n     */\n    public function init()\n    {\n        parent::init();\n        if ($this->logFile === null) {\n            $this->logFile = Yii::$app->getRuntimePath() . '/logs/app.log';\n        } else {\n            $this->logFile = Yii::getAlias($this->logFile);\n        }\n        if ($this->maxLogFiles < 1) {\n            $this->maxLogFiles = 1;\n        }\n        if ($this->maxFileSize < 1) {\n            $this->maxFileSize = 1;\n        }\n    }\n\n    /**\n     * Writes log messages to a file.\n     * Starting from version 2.0.14, this method throws LogRuntimeException in case the log can not be exported.\n     * @throws InvalidConfigException if unable to open the log file for writing\n     * @throws LogRuntimeException if unable to write complete log to file\n     */\n    public function export()\n    {\n        $text = implode(\"\\n\", array_map([$this, 'formatMessage'], $this->messages)) . \"\\n\";\n\n        if (trim($text) === '') {\n            return; // No messages to export, so we exit the function early\n        }\n\n        if (strpos($this->logFile, '://') === false || strncmp($this->logFile, 'file://', 7) === 0) {\n            $logPath = dirname($this->logFile);\n            FileHelper::createDirectory($logPath, $this->dirMode, true);\n        }\n\n        if (($fp = @fopen($this->logFile, 'a')) === false) {\n            throw new InvalidConfigException(\"Unable to append to log file: {$this->logFile}\");\n        }\n        @flock($fp, LOCK_EX);\n        if ($this->enableRotation) {\n            // clear stat cache to ensure getting the real current file size and not a cached one\n            // this may result in rotating twice when cached file size is used on subsequent calls\n            clearstatcache();\n        }\n        if ($this->enableRotation && @filesize($this->logFile) > $this->maxFileSize * 1024) {\n            $this->rotateFiles();\n        }\n        $writeResult = @fwrite($fp, $text);\n        if ($writeResult === false) {\n            $message = \"Unable to export log through file ($this->logFile)!\";\n            if ($error = error_get_last()) {\n                $message .= \": {$error['message']}\";\n            }\n            throw new LogRuntimeException($message);\n        }\n        $textSize = strlen($text);\n        if ($writeResult < $textSize) {\n            throw new LogRuntimeException(\"Unable to export whole log through file ({$this->logFile})! Wrote $writeResult out of $textSize bytes.\");\n        }\n        @fflush($fp);\n        @flock($fp, LOCK_UN);\n        @fclose($fp);\n\n        if ($this->fileMode !== null) {\n            @chmod($this->logFile, $this->fileMode);\n        }\n    }\n\n    /**\n     * Rotates log files.\n     */\n    protected function rotateFiles()\n    {\n        $file = $this->logFile;\n        for ($i = $this->maxLogFiles; $i >= 0; --$i) {\n            // $i == 0 is the original log file\n            $rotateFile = $file . ($i === 0 ? '' : '.' . $i);\n            if (is_file($rotateFile)) {\n                // suppress errors because it's possible multiple processes enter into this section\n                if ($i === $this->maxLogFiles) {\n                    @unlink($rotateFile);\n                    continue;\n                }\n                $newFile = $this->logFile . '.' . ($i + 1);\n                $this->rotateByCopy($rotateFile, $newFile);\n                if ($i === 0) {\n                    $this->clearLogFile($rotateFile);\n                }\n            }\n        }\n    }\n\n    /***\n     * Clear log file without closing any other process open handles\n     * @param string $rotateFile\n     */\n    private function clearLogFile($rotateFile)\n    {\n        if ($filePointer = @fopen($rotateFile, 'a')) {\n            @ftruncate($filePointer, 0);\n            @fclose($filePointer);\n        }\n    }\n\n    /***\n     * Copy rotated file into new file\n     * @param string $rotateFile\n     * @param string $newFile\n     */\n    private function rotateByCopy($rotateFile, $newFile)\n    {\n        @copy($rotateFile, $newFile);\n        if ($this->fileMode !== null) {\n            @chmod($newFile, $this->fileMode);\n        }\n    }\n}\n"
  },
  {
    "path": "framework/log/LogRuntimeException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\log;\n\n/**\n * LogRuntimeException represents an exception caused by problems with log delivery.\n *\n * @author Bizley <pawel@positive.codes>\n * @since 2.0.14\n */\nclass LogRuntimeException extends \\yii\\base\\Exception\n{\n    /**\n     * @return string the user-friendly name of this exception\n     */\n    public function getName()\n    {\n        return 'Log Runtime';\n    }\n}\n"
  },
  {
    "path": "framework/log/Logger.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\log;\n\nuse yii\\base\\Component;\n\n/**\n * Logger records logged messages in memory and sends them to different targets if [[dispatcher]] is set.\n *\n * A Logger instance can be accessed via `Yii::getLogger()`. You can call the method [[log()]] to record a single log message.\n * For convenience, a set of shortcut methods are provided for logging messages of various severity levels\n * via the [[Yii]] class:\n *\n * - [[Yii::trace()]]\n * - [[Yii::error()]]\n * - [[Yii::warning()]]\n * - [[Yii::info()]]\n * - [[Yii::beginProfile()]]\n * - [[Yii::endProfile()]]\n *\n * For more details and usage information on Logger, see the [guide article on logging](guide:runtime-logging).\n *\n * When the application ends or [[flushInterval]] is reached, Logger will call [[flush()]]\n * to send logged messages to different log targets, such as [[FileTarget|file]], [[EmailTarget|email]],\n * or [[DbTarget|database]], with the help of the [[dispatcher]].\n *\n * @property-read array $dbProfiling The first element indicates the number of SQL statements executed, and\n * the second element the total time spent in SQL execution.\n * @property-read float $elapsedTime The total elapsed time in seconds for current request.\n * @property-read array $profiling The profiling results. Each element is an array consisting of these\n * elements: `info`, `category`, `timestamp`, `trace`, `level`, `duration`, `memory`, `memoryDiff`. The `memory`\n * and `memoryDiff` values are available since version 2.0.11.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Logger extends Component\n{\n    /**\n     * Error message level. An error message is one that indicates the abnormal termination of the\n     * application and may require developer's handling.\n     */\n    public const LEVEL_ERROR = 0x01;\n    /**\n     * Warning message level. A warning message is one that indicates some abnormal happens but\n     * the application is able to continue to run. Developers should pay attention to this message.\n     */\n    public const LEVEL_WARNING = 0x02;\n    /**\n     * Informational message level. An informational message is one that includes certain information\n     * for developers to review.\n     */\n    public const LEVEL_INFO = 0x04;\n    /**\n     * Tracing message level. A tracing message is one that reveals the code execution flow.\n     */\n    public const LEVEL_TRACE = 0x08;\n    /**\n     * Profiling message level. This indicates the message is for profiling purpose.\n     */\n    public const LEVEL_PROFILE = 0x40;\n    /**\n     * Profiling message level. This indicates the message is for profiling purpose. It marks the beginning\n     * of a profiling block.\n     */\n    public const LEVEL_PROFILE_BEGIN = 0x50;\n    /**\n     * Profiling message level. This indicates the message is for profiling purpose. It marks the end\n     * of a profiling block.\n     */\n    public const LEVEL_PROFILE_END = 0x60;\n    /**\n     * @var array logged messages. This property is managed by [[log()]] and [[flush()]].\n     * Each log message is of the following structure:\n     *\n     * ```\n     * [\n     *   [0] => message (mixed, can be a string or some complex data, such as an exception object)\n     *   [1] => level (integer)\n     *   [2] => category (string)\n     *   [3] => timestamp (float, obtained by microtime(true))\n     *   [4] => traces (array, debug backtrace, contains the application code call stacks)\n     *   [5] => memory usage in bytes (int, obtained by memory_get_usage()), available since version 2.0.11.\n     * ]\n     * ```\n     */\n    public $messages = [];\n    /**\n     * @var int how many messages should be logged before they are flushed from memory and sent to targets.\n     * Defaults to 1000, meaning the [[flush()]] method will be invoked once every 1000 messages logged.\n     * Set this property to be 0 if you don't want to flush messages until the application terminates.\n     * This property mainly affects how much memory will be taken by the logged messages.\n     * A smaller value means less memory, but will increase the execution time due to the overhead of [[flush()]].\n     */\n    public $flushInterval = 1000;\n    /**\n     * @var int how much call stack information (file name and line number) should be logged for each message.\n     * If it is greater than 0, at most that number of call stacks will be logged. Note that only application\n     * call stacks are counted.\n     */\n    public $traceLevel = 0;\n    /**\n     * @var Dispatcher the message dispatcher.\n     */\n    public $dispatcher;\n    /**\n     * @var array of event names used to get statistical results of DB queries.\n     * @since 2.0.41\n     */\n    public $dbEventNames = ['yii\\db\\Command::query', 'yii\\db\\Command::execute'];\n    /**\n     * @var bool whether the profiling-aware mode should be switched on.\n     * If on, [[flush()]] makes sure that profiling blocks are flushed in pairs. In case that any dangling messages are\n     * detected these are kept for the next flush interval to find their pair. To prevent memory leaks, when number of\n     * dangling messages reaches flushInterval value, logger flushes them immediately and triggers a warning.\n     * Keep in mind that profiling-aware mode is more time and memory consuming.\n     * @since 2.0.43\n     */\n    public $profilingAware = false;\n\n\n    /**\n     * Initializes the logger by registering [[flush()]] as a shutdown function.\n     */\n    public function init()\n    {\n        parent::init();\n        register_shutdown_function(function () {\n            // make regular flush before other shutdown functions, which allows session data collection and so on\n            $this->flush();\n            // make sure log entries written by shutdown functions are also flushed\n            // ensure \"flush()\" is called last when there are multiple shutdown functions\n            register_shutdown_function([$this, 'flush'], true);\n        });\n    }\n\n    /**\n     * Logs a message with the given type and category.\n     * If [[traceLevel]] is greater than 0, additional call stack information about\n     * the application code will be logged as well.\n     * @param string|array|\\Throwable $message the message to be logged. This can be a simple string or a more\n     * complex data structure that will be handled by a [[Target|log target]].\n     * @param int $level the level of the message. This must be one of the following:\n     * `Logger::LEVEL_ERROR`, `Logger::LEVEL_WARNING`, `Logger::LEVEL_INFO`, `Logger::LEVEL_TRACE`, `Logger::LEVEL_PROFILE`,\n     * `Logger::LEVEL_PROFILE_BEGIN`, `Logger::LEVEL_PROFILE_END`.\n     * @param string $category the category of the message.\n     */\n    public function log($message, $level, $category = 'application')\n    {\n        $time = microtime(true);\n        $traces = [];\n        if ($this->traceLevel > 0) {\n            $count = 0;\n            $ts = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);\n            array_pop($ts); // remove the last trace since it would be the entry script, not very useful\n            foreach ($ts as $trace) {\n                if (isset($trace['file'], $trace['line']) && strpos($trace['file'], YII2_PATH) !== 0) {\n                    unset($trace['object'], $trace['args']);\n                    $traces[] = $trace;\n                    if (++$count >= $this->traceLevel) {\n                        break;\n                    }\n                }\n            }\n        }\n        $data = [$message, $level, $category, $time, $traces, memory_get_usage()];\n        if ($this->profilingAware && in_array($level, [self::LEVEL_PROFILE_BEGIN, self::LEVEL_PROFILE_END])) {\n            $this->messages[($level == self::LEVEL_PROFILE_BEGIN ? 'begin-' : 'end-') . md5(json_encode($message))] = $data;\n        } else {\n            $this->messages[] = $data;\n        }\n\n        if ($this->flushInterval > 0 && count($this->messages) >= $this->flushInterval) {\n            $this->flush();\n        }\n    }\n\n    /**\n     * Flushes log messages from memory to targets.\n     * @param bool $final whether this is a final call during a request.\n     */\n    public function flush($final = false)\n    {\n        if ($this->profilingAware) {\n            $keep = [];\n            $messages = [];\n            foreach ($this->messages as $index => $message) {\n                if (is_int($index)) {\n                    $messages[] = $message;\n                } else {\n                    if (strncmp($index, 'begin-', 6) === 0) {\n                        $oppositeProfile = 'end-' . substr($index, 6);\n                    } else {\n                        $oppositeProfile = 'begin-' . substr($index, 4);\n                    }\n                    if (array_key_exists($oppositeProfile, $this->messages)) {\n                        $messages[] = $message;\n                    } else {\n                        $keep[$index] = $message;\n                    }\n                }\n            }\n            if ($this->flushInterval > 0 && count($keep) >= $this->flushInterval) {\n                $this->messages = [];\n                $this->log(\n                    'Number of dangling profiling block messages reached flushInterval value and therefore these were flushed. Please consider setting higher flushInterval value or making profiling blocks shorter.',\n                    self::LEVEL_WARNING\n                );\n                $messages = array_merge($messages, array_values($keep));\n            } else {\n                $this->messages = $keep;\n            }\n        } else {\n            $messages = $this->messages;\n            $this->messages = [];\n        }\n\n        if ($this->dispatcher instanceof Dispatcher) {\n            $this->dispatcher->dispatch($messages, $final);\n        }\n    }\n\n    /**\n     * Returns the total elapsed time since the start of the current request.\n     * This method calculates the difference between now and the timestamp\n     * defined by constant `YII_BEGIN_TIME` which is evaluated at the beginning\n     * of [[\\yii\\BaseYii]] class file.\n     * @return float the total elapsed time in seconds for current request.\n     */\n    public function getElapsedTime()\n    {\n        return microtime(true) - YII_BEGIN_TIME;\n    }\n\n    /**\n     * Returns the profiling results.\n     *\n     * By default, all profiling results will be returned. You may provide\n     * `$categories` and `$excludeCategories` as parameters to retrieve the\n     * results that you are interested in.\n     *\n     * @param array $categories list of categories that you are interested in.\n     * You can use an asterisk at the end of a category to do a prefix match.\n     * For example, 'yii\\db\\*' will match categories starting with 'yii\\db\\',\n     * such as 'yii\\db\\Connection'.\n     * @param array $excludeCategories list of categories that you want to exclude\n     * @return array the profiling results. Each element is an array consisting of these elements:\n     * `info`, `category`, `timestamp`, `trace`, `level`, `duration`, `memory`, `memoryDiff`.\n     * The `memory` and `memoryDiff` values are available since version 2.0.11.\n     */\n    public function getProfiling($categories = [], $excludeCategories = [])\n    {\n        $timings = $this->calculateTimings($this->messages);\n        if (empty($categories) && empty($excludeCategories)) {\n            return $timings;\n        }\n\n        foreach ($timings as $outerIndex => $outerTimingItem) {\n            $currentIndex = $outerIndex;\n            $matched = empty($categories);\n            foreach ($categories as $category) {\n                $prefix = rtrim($category, '*');\n                if (\n                    ($outerTimingItem['category'] === $category || $prefix !== $category)\n                    && strpos($outerTimingItem['category'], $prefix) === 0\n                ) {\n                    $matched = true;\n                    break;\n                }\n            }\n\n            if ($matched) {\n                foreach ($excludeCategories as $category) {\n                    $prefix = rtrim($category, '*');\n                    foreach ($timings as $innerIndex => $innerTimingItem) {\n                        $currentIndex = $innerIndex;\n                        if (\n                            ($innerTimingItem['category'] === $category || $prefix !== $category)\n                            && strpos($innerTimingItem['category'], $prefix) === 0\n                        ) {\n                            $matched = false;\n                            break;\n                        }\n                    }\n                }\n            }\n\n            if (!$matched) {\n                unset($timings[$currentIndex]);\n            }\n        }\n\n        return array_values($timings);\n    }\n\n    /**\n     * Returns the statistical results of DB queries.\n     * The results returned include the number of SQL statements executed and\n     * the total time spent.\n     * @return array the first element indicates the number of SQL statements executed,\n     * and the second element the total time spent in SQL execution.\n     */\n    public function getDbProfiling()\n    {\n        $timings = $this->getProfiling($this->dbEventNames);\n        $count = count($timings);\n        $time = 0;\n        foreach ($timings as $timing) {\n            $time += $timing['duration'];\n        }\n\n        return [$count, $time];\n    }\n\n    /**\n     * Calculates the elapsed time for the given log messages.\n     * @param array $messages the log messages obtained from profiling\n     * @return array timings. Each element is an array consisting of these elements:\n     * `info`, `category`, `timestamp`, `trace`, `level`, `duration`, `memory`, `memoryDiff`.\n     * The `memory` and `memoryDiff` values are available since version 2.0.11.\n     */\n    public function calculateTimings($messages)\n    {\n        $timings = [];\n        $stack = [];\n\n        foreach ($messages as $i => $log) {\n            list($token, $level, $category, $timestamp, $traces) = $log;\n            $memory = isset($log[5]) ? $log[5] : 0;\n            $log[6] = $i;\n            $hash = md5(json_encode($token));\n            if ($level == self::LEVEL_PROFILE_BEGIN) {\n                $stack[$hash] = $log;\n            } elseif ($level == self::LEVEL_PROFILE_END) {\n                if (isset($stack[$hash])) {\n                    $timings[$stack[$hash][6]] = [\n                        'info' => $stack[$hash][0],\n                        'category' => $stack[$hash][2],\n                        'timestamp' => $stack[$hash][3],\n                        'trace' => $stack[$hash][4],\n                        'level' => count($stack) - 1,\n                        'duration' => $timestamp - $stack[$hash][3],\n                        'memory' => $memory,\n                        'memoryDiff' => $memory - (isset($stack[$hash][5]) ? $stack[$hash][5] : 0),\n                    ];\n                    unset($stack[$hash]);\n                }\n            }\n        }\n\n        ksort($timings);\n\n        return array_values($timings);\n    }\n\n\n    /**\n     * Returns the text display of the specified level.\n     * @param int $level the message level, e.g. [[LEVEL_ERROR]], [[LEVEL_WARNING]].\n     * @return string the text display of the level\n     */\n    public static function getLevelName($level)\n    {\n        static $levels = [\n            self::LEVEL_ERROR => 'error',\n            self::LEVEL_WARNING => 'warning',\n            self::LEVEL_INFO => 'info',\n            self::LEVEL_TRACE => 'trace',\n            self::LEVEL_PROFILE_BEGIN => 'profile begin',\n            self::LEVEL_PROFILE_END => 'profile end',\n            self::LEVEL_PROFILE => 'profile',\n        ];\n\n        return isset($levels[$level]) ? $levels[$level] : 'unknown';\n    }\n}\n"
  },
  {
    "path": "framework/log/SyslogTarget.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\log;\n\nuse yii\\helpers\\VarDumper;\n\n/**\n * SyslogTarget writes log to syslog.\n *\n * @author miramir <gmiramir@gmail.com>\n * @since 2.0\n */\nclass SyslogTarget extends Target\n{\n    /**\n     * @var string syslog identity\n     */\n    public $identity;\n    /**\n     * @var int syslog facility.\n     */\n    public $facility = LOG_USER;\n    /**\n     * @var int|null openlog options. This is a bitfield passed as the `$option` parameter to [openlog()](https://www.php.net/openlog).\n     * Defaults to `null` which means to use the default options `LOG_ODELAY | LOG_PID`.\n     * @see https://www.php.net/openlog for available options.\n     * @since 2.0.11\n     */\n    public $options;\n\n    /**\n     * @var array syslog levels\n     */\n    private $_syslogLevels = [\n        Logger::LEVEL_TRACE => LOG_DEBUG,\n        Logger::LEVEL_PROFILE_BEGIN => LOG_DEBUG,\n        Logger::LEVEL_PROFILE_END => LOG_DEBUG,\n        Logger::LEVEL_PROFILE => LOG_DEBUG,\n        Logger::LEVEL_INFO => LOG_INFO,\n        Logger::LEVEL_WARNING => LOG_WARNING,\n        Logger::LEVEL_ERROR => LOG_ERR,\n    ];\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        if ($this->options === null) {\n            $this->options = LOG_ODELAY | LOG_PID;\n        }\n    }\n\n    /**\n     * Writes log messages to syslog.\n     * Starting from version 2.0.14, this method throws LogRuntimeException in case the log can not be exported.\n     * @throws LogRuntimeException\n     */\n    public function export()\n    {\n        openlog($this->identity, $this->options, $this->facility);\n        foreach ($this->messages as $message) {\n            if (syslog($this->_syslogLevels[$message[1]], $this->formatMessage($message)) === false) {\n                throw new LogRuntimeException('Unable to export log through system log!');\n            }\n        }\n        closelog();\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function formatMessage($message)\n    {\n        list($text, $level, $category, $timestamp) = $message;\n        $level = Logger::getLevelName($level);\n        if (!is_string($text)) {\n            // exceptions may not be serializable if in the call stack somewhere is a Closure\n            if ($text instanceof \\Exception || $text instanceof \\Throwable) {\n                $text = (string) $text;\n            } else {\n                $text = VarDumper::export($text);\n            }\n        }\n\n        $prefix = $this->getMessagePrefix($message);\n        return \"{$prefix}[$level][$category] $text\";\n    }\n}\n"
  },
  {
    "path": "framework/log/Target.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\log;\n\nuse Yii;\nuse yii\\base\\Component;\nuse yii\\base\\InvalidConfigException;\nuse yii\\helpers\\ArrayHelper;\nuse yii\\helpers\\StringHelper;\nuse yii\\helpers\\VarDumper;\nuse yii\\web\\Request;\nuse yii\\web\\User;\n\n/**\n * Target is the base class for all log target classes.\n *\n * A log target object will filter the messages logged by [[Logger]] according\n * to its [[levels]] and [[categories]] properties. It may also export the filtered\n * messages to specific destination defined by the target, such as emails, files.\n *\n * Level filter and category filter are combinatorial, i.e., only messages\n * satisfying both filter conditions will be handled. Additionally, you\n * may specify [[except]] to exclude messages of certain categories.\n *\n * @property bool $enabled Indicates whether this log target is enabled. Defaults to true. Note that the type\n * of this property differs in getter and setter. See [[getEnabled()]] and [[setEnabled()]] for details.\n * @property int $levels The message levels that this target is interested in. This is a bitmap of level\n * values. Defaults to 0, meaning all available levels. Note that the type of this property differs in getter and\n * setter. See [[getLevels()]] and [[setLevels()]] for details.\n *\n * For more details and usage information on Target, see the [guide article on logging & targets](guide:runtime-logging).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nabstract class Target extends Component\n{\n    /**\n     * @var array list of message categories that this target is interested in. Defaults to empty, meaning all categories.\n     * You can use an asterisk at the end of a category so that the category may be used to\n     * match those categories sharing the same common prefix. For example, 'yii\\db\\*' will match\n     * categories starting with 'yii\\db\\', such as 'yii\\db\\Connection'.\n     */\n    public $categories = [];\n    /**\n     * @var array list of message categories that this target is NOT interested in. Defaults to empty, meaning no uninteresting messages.\n     * If this property is not empty, then any category listed here will be excluded from [[categories]].\n     * You can use an asterisk at the end of a category so that the category can be used to\n     * match those categories sharing the same common prefix. For example, 'yii\\db\\*' will match\n     * categories starting with 'yii\\db\\', such as 'yii\\db\\Connection'.\n     * @see categories\n     */\n    public $except = [];\n    /**\n     * @var array list of the PHP predefined variables that should be logged in a message.\n     * Note that a variable must be accessible via `$GLOBALS`. Otherwise it won't be logged.\n     *\n     * Defaults to `['_GET', '_POST', '_FILES', '_COOKIE', '_SESSION', '_SERVER']`.\n     *\n     * Since version 2.0.9 additional syntax can be used:\n     * Each element could be specified as one of the following:\n     *\n     * - `var` - `var` will be logged.\n     * - `var.key` - only `var[key]` key will be logged.\n     * - `!var.key` - `var[key]` key will be excluded.\n     *\n     * Note that if you need $_SESSION to logged regardless if session was used you have to open it right at\n     * the start of your request.\n     *\n     * @see \\yii\\helpers\\ArrayHelper::filter()\n     */\n    public $logVars = [\n        '_GET',\n        '_POST',\n        '_FILES',\n        '_COOKIE',\n        '_SESSION',\n        '_SERVER',\n    ];\n    /**\n     * @var array list of the PHP predefined variables that should NOT be logged \"as is\" and should always be replaced\n     * with a mask `***` before logging, when exist.\n     *\n     * Defaults to `[ '_SERVER.HTTP_AUTHORIZATION', '_SERVER.PHP_AUTH_USER', '_SERVER.PHP_AUTH_PW']`\n     *\n     * Each element could be specified as one of the following:\n     *\n     * - `var` - `var` will be logged as `***`\n     * - `var.key` - only `var[key]` will be logged as `***`\n     *\n     * In addition, this property accepts (case-insensitive) patterns. For example:\n     * - `_SERVER.*_SECRET` matches all ending with `_SECRET`, such as `$_SERVER['TOKEN_SECRET']` etc.\n     * - `_SERVER.SECRET_*` matches all starting with `SECRET_`, such as `$_SERVER['SECRET_TOKEN']` etc.\n     * - `_SERVER.*SECRET*` matches all containing `SECRET` i.e. both of the above.\n     *\n     * @since 2.0.16\n     */\n    public $maskVars = [\n        '_SERVER.HTTP_AUTHORIZATION',\n        '_SERVER.PHP_AUTH_USER',\n        '_SERVER.PHP_AUTH_PW',\n    ];\n    /**\n     * @var callable|null a PHP callable that returns a string to be prefixed to every exported message.\n     *\n     * If not set, [[getMessagePrefix()]] will be used, which prefixes the message with context information\n     * such as user IP, user ID and session ID.\n     *\n     * The signature of the callable should be `function ($message)`.\n     */\n    public $prefix;\n    /**\n     * @var int how many messages should be accumulated before they are exported.\n     * Defaults to 1000. Note that messages will always be exported when the application terminates.\n     * Set this property to be 0 if you don't want to export messages until the application terminates.\n     */\n    public $exportInterval = 1000;\n    /**\n     * @var array the messages that are retrieved from the logger so far by this log target.\n     * Please refer to [[Logger::messages]] for the details about the message structure.\n     */\n    public $messages = [];\n    /**\n     * @var bool whether to log time with microseconds.\n     * Defaults to false.\n     * @since 2.0.13\n     */\n    public $microtime = false;\n\n    private $_levels = 0;\n    private $_enabled = true;\n\n\n    /**\n     * Exports log [[messages]] to a specific destination.\n     * Child classes must implement this method.\n     */\n    abstract public function export();\n\n    /**\n     * Processes the given log messages.\n     * This method will filter the given messages with [[levels]] and [[categories]].\n     * And if requested, it will also export the filtering result to specific medium (e.g. email).\n     * @param array $messages log messages to be processed. See [[Logger::messages]] for the structure\n     * of each message.\n     * @param bool $final whether this method is called at the end of the current application\n     */\n    public function collect($messages, $final)\n    {\n        $this->messages = array_merge($this->messages, static::filterMessages($messages, $this->getLevels(), $this->categories, $this->except));\n        $count = count($this->messages);\n        if ($count > 0 && ($final || $this->exportInterval > 0 && $count >= $this->exportInterval)) {\n            if (($context = $this->getContextMessage()) !== '') {\n                $this->messages[] = [$context, Logger::LEVEL_INFO, 'application', YII_BEGIN_TIME, [], 0];\n            }\n            // set exportInterval to 0 to avoid triggering export again while exporting\n            $oldExportInterval = $this->exportInterval;\n            $this->exportInterval = 0;\n            $this->export();\n            $this->exportInterval = $oldExportInterval;\n\n            $this->messages = [];\n        }\n    }\n\n    /**\n     * Generates the context information to be logged.\n     * The default implementation will dump user information, system variables, etc.\n     * @return string the context information. If an empty string, it means no context information.\n     */\n    protected function getContextMessage()\n    {\n        $context = ArrayHelper::filter($GLOBALS, $this->logVars);\n        $items = ArrayHelper::flatten($context);\n        foreach ($this->maskVars as $var) {\n            foreach ($items as $key => $value) {\n                if (StringHelper::matchWildcard($var, $key, ['caseSensitive' => false])) {\n                    ArrayHelper::setValue($context, $key, '***');\n                }\n            }\n        }\n        $result = [];\n        foreach ($context as $key => $value) {\n            $result[] = \"\\${$key} = \" . VarDumper::dumpAsString($value);\n        }\n\n        return implode(\"\\n\\n\", $result);\n    }\n\n    /**\n     * @return int the message levels that this target is interested in. This is a bitmap of\n     * level values. Defaults to 0, meaning all available levels.\n     */\n    public function getLevels()\n    {\n        return $this->_levels;\n    }\n\n    /**\n     * Sets the message levels that this target is interested in.\n     *\n     * The parameter can be either an array of interested level names or an integer representing\n     * the bitmap of the interested level values. Valid level names include: 'error',\n     * 'warning', 'info', 'trace' and 'profile'; valid level values include:\n     * [[Logger::LEVEL_ERROR]], [[Logger::LEVEL_WARNING]], [[Logger::LEVEL_INFO]],\n     * [[Logger::LEVEL_TRACE]] and [[Logger::LEVEL_PROFILE]].\n     *\n     * For example,\n     *\n     * ```\n     * ['error', 'warning']\n     * // which is equivalent to:\n     * Logger::LEVEL_ERROR | Logger::LEVEL_WARNING\n     * ```\n     *\n     * @param array|int $levels message levels that this target is interested in.\n     * @throws InvalidConfigException if $levels value is not correct.\n     */\n    public function setLevels($levels)\n    {\n        static $levelMap = [\n            'error' => Logger::LEVEL_ERROR,\n            'warning' => Logger::LEVEL_WARNING,\n            'info' => Logger::LEVEL_INFO,\n            'trace' => Logger::LEVEL_TRACE,\n            'profile' => Logger::LEVEL_PROFILE,\n        ];\n        if (is_array($levels)) {\n            $this->_levels = 0;\n            foreach ($levels as $level) {\n                if (isset($levelMap[$level])) {\n                    $this->_levels |= $levelMap[$level];\n                } else {\n                    throw new InvalidConfigException(\"Unrecognized level: $level\");\n                }\n            }\n        } else {\n            $bitmapValues = array_reduce($levelMap, function ($carry, $item) {\n                return $carry | $item;\n            });\n            if (!($bitmapValues & $levels) && $levels !== 0) {\n                throw new InvalidConfigException(\"Incorrect $levels value\");\n            }\n            $this->_levels = $levels;\n        }\n    }\n\n    /**\n     * Filters the given messages according to their categories and levels.\n     * @param array $messages messages to be filtered.\n     * The message structure follows that in [[Logger::messages]].\n     * @param int $levels the message levels to filter by. This is a bitmap of\n     * level values. Value 0 means allowing all levels.\n     * @param array $categories the message categories to filter by. If empty, it means all categories are allowed.\n     * @param array $except the message categories to exclude. If empty, it means all categories are allowed.\n     * @return array the filtered messages.\n     */\n    public static function filterMessages($messages, $levels = 0, $categories = [], $except = [])\n    {\n        foreach ($messages as $i => $message) {\n            if ($levels && !($levels & $message[1])) {\n                unset($messages[$i]);\n                continue;\n            }\n\n            $matched = empty($categories);\n            foreach ($categories as $category) {\n                if ($message[2] === $category || !empty($category) && substr_compare($category, '*', -1, 1) === 0 && strpos($message[2], rtrim($category, '*')) === 0) {\n                    $matched = true;\n                    break;\n                }\n            }\n\n            if ($matched) {\n                foreach ($except as $category) {\n                    $prefix = rtrim($category, '*');\n                    if (($message[2] === $category || $prefix !== $category) && strpos($message[2], $prefix) === 0) {\n                        $matched = false;\n                        break;\n                    }\n                }\n            }\n\n            if (!$matched) {\n                unset($messages[$i]);\n            }\n        }\n\n        return $messages;\n    }\n\n    /**\n     * Formats a log message for display as a string.\n     * @param array $message the log message to be formatted.\n     * The message structure follows that in [[Logger::messages]].\n     * @return string the formatted message\n     */\n    public function formatMessage($message)\n    {\n        [$text, $level, $category, $timestamp] = $message;\n        $level = Logger::getLevelName($level);\n        if (!is_string($text)) {\n            // exceptions may not be serializable if in the call stack somewhere is a Closure\n            if ($text instanceof \\Exception || $text instanceof \\Throwable) {\n                $text = (string) $text;\n            } else {\n                $text = VarDumper::export($text);\n            }\n        }\n        $traces = [];\n        if (isset($message[4])) {\n            foreach ($message[4] as $trace) {\n                $traces[] = \"in {$trace['file']}:{$trace['line']}\";\n            }\n        }\n\n        $prefix = $this->getMessagePrefix($message);\n        return $this->getTime($timestamp) . \" {$prefix}[$level][$category] $text\"\n            . (empty($traces) ? '' : \"\\n    \" . implode(\"\\n    \", $traces));\n    }\n\n    /**\n     * Returns a string to be prefixed to the given message.\n     * If [[prefix]] is configured it will return the result of the callback.\n     * The default implementation will return user IP, user ID and session ID as a prefix.\n     * @param array $message the message being exported.\n     * The message structure follows that in [[Logger::messages]].\n     * @return string the prefix string\n     */\n    public function getMessagePrefix($message)\n    {\n        if ($this->prefix !== null) {\n            return call_user_func($this->prefix, $message);\n        }\n\n        if (Yii::$app === null) {\n            return '';\n        }\n\n        $request = Yii::$app->getRequest();\n        $ip = $request instanceof Request ? $request->getUserIP() : '-';\n\n        /** @var User $user */\n        $user = Yii::$app->has('user', true) ? Yii::$app->get('user') : null;\n        if ($user && ($identity = $user->getIdentity(false))) {\n            $userID = $identity->getId();\n        } else {\n            $userID = '-';\n        }\n\n        /** @var \\yii\\web\\Session $session */\n        $session = Yii::$app->has('session', true) ? Yii::$app->get('session') : null;\n        $sessionID = $session && $session->getIsActive() ? $session->getId() : '-';\n\n        return \"[$ip][$userID][$sessionID]\";\n    }\n\n    /**\n     * Sets a value indicating whether this log target is enabled.\n     * @param bool|callable $value a boolean value or a callable to obtain the value from.\n     * The callable value is available since version 2.0.13.\n     *\n     * A callable may be used to determine whether the log target should be enabled in a dynamic way.\n     * For example, to only enable a log if the current user is logged in you can configure the target\n     * as follows:\n     *\n     * ```\n     * 'enabled' => function() {\n     *     return !Yii::$app->user->isGuest;\n     * }\n     * ```\n     */\n    public function setEnabled($value)\n    {\n        $this->_enabled = $value;\n    }\n\n    /**\n     * Check whether the log target is enabled.\n     * @return bool A value indicating whether this log target is enabled.\n     */\n    public function getEnabled()\n    {\n        if (is_callable($this->_enabled)) {\n            return call_user_func($this->_enabled, $this);\n        }\n\n        return $this->_enabled;\n    }\n\n    /**\n     * Returns formatted ('Y-m-d H:i:s') timestamp for message.\n     * If [[microtime]] is configured to true it will return format 'Y-m-d H:i:s.u'.\n     * @param float $timestamp\n     * @return string\n     * @since 2.0.13\n     */\n    protected function getTime($timestamp)\n    {\n        $parts = explode('.', sprintf('%F', $timestamp));\n\n        return date('Y-m-d H:i:s', $parts[0]) . ($this->microtime ? ('.' . $parts[1]) : '');\n    }\n}\n"
  },
  {
    "path": "framework/log/migrations/m141106_185632_log_init.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nuse yii\\base\\InvalidConfigException;\nuse yii\\db\\Migration;\nuse yii\\log\\DbTarget;\n\n/**\n * Initializes log table.\n *\n * The indexes declared are not required. They are mainly used to improve the performance\n * of some queries about message levels and categories. Depending on your actual needs, you may\n * want to create additional indexes (e.g. index on `log_time`).\n *\n * @author Alexander Makarov <sam@rmcreative.ru>\n * @since 2.0.1\n */\nclass m141106_185632_log_init extends Migration\n{\n    /**\n     * @var DbTarget[] Targets to create log table for\n     */\n    private $_dbTargets = [];\n\n    /**\n     * @throws InvalidConfigException\n     * @return DbTarget[]\n     */\n    protected function getDbTargets()\n    {\n        if ($this->_dbTargets === []) {\n            $log = Yii::$app->getLog();\n\n            $usedTargets = [];\n            foreach ($log->targets as $target) {\n                if ($target instanceof DbTarget) {\n                    $currentTarget = [\n                        $target->db,\n                        $target->logTable,\n                    ];\n                    if (!in_array($currentTarget, $usedTargets, true)) {\n                        // do not create same table twice\n                        $usedTargets[] = $currentTarget;\n                        $this->_dbTargets[] = $target;\n                    }\n                }\n            }\n\n            if ($this->_dbTargets === []) {\n                throw new InvalidConfigException('You should configure \"log\" component to use one or more database targets before executing this migration.');\n            }\n        }\n\n        return $this->_dbTargets;\n    }\n\n    public function up()\n    {\n        foreach ($this->getDbTargets() as $target) {\n            $this->db = $target->db;\n\n            $tableOptions = null;\n            if ($this->db->driverName === 'mysql') {\n                // https://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci\n                $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';\n            }\n\n            $this->createTable($target->logTable, [\n                'id' => $this->bigPrimaryKey(),\n                'level' => $this->integer(),\n                'category' => $this->string(),\n                'log_time' => $this->double(),\n                'prefix' => $this->text(),\n                'message' => $this->text(),\n            ], $tableOptions);\n\n            $this->createIndex('idx_log_level', $target->logTable, 'level');\n            $this->createIndex('idx_log_category', $target->logTable, 'category');\n        }\n    }\n\n    public function down()\n    {\n        foreach ($this->getDbTargets() as $target) {\n            $this->db = $target->db;\n\n            $this->dropTable($target->logTable);\n        }\n    }\n}\n"
  },
  {
    "path": "framework/log/migrations/schema-mssql.sql",
    "content": "/**\n * Database schema required by \\yii\\log\\DbTarget.\n *\n * The indexes declared are not required. They are mainly used to improve the performance\n * of some queries about message levels and categories. Depending on your actual needs, you may\n * want to create additional indexes (e.g. index on `log_time`).\n *\n * @author Alexander Makarov <sam@rmcreative.ru>\n * @link https://www.yiiframework.com/\n * @copyright 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n * @since 2.0.1\n */\n\nif object_id('[log]', 'U') is not null\n    drop table [log];\n\ncreate table [log]\n(\n   [id]          bigint IDENTITY PRIMARY KEY,\n   [level]       integer,\n   [category]    varchar(255),\n   [log_time]    float,\n   [prefix]      text,\n   [message]     text\n);\n\ncreate index [idx_log_level] on [log] ([level]);\ncreate index [idx_log_category] on [log] ([category]);\n"
  },
  {
    "path": "framework/log/migrations/schema-mysql.sql",
    "content": "/**\n * Database schema required by \\yii\\log\\DbTarget.\n *\n * The indexes declared are not required. They are mainly used to improve the performance\n * of some queries about message levels and categories. Depending on your actual needs, you may\n * want to create additional indexes (e.g. index on `log_time`).\n *\n * @author Alexander Makarov <sam@rmcreative.ru>\n * @link https://www.yiiframework.com/\n * @copyright 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n * @since 2.0.1\n */\n\ndrop table if exists `log`;\n\ncreate table `log`\n(\n   `id`          bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,\n   `level`       integer,\n   `category`    varchar(255),\n   `log_time`    double,\n   `prefix`      text,\n   `message`     text,\n   key `idx_log_level` (`level`),\n   key `idx_log_category` (`category`)\n) engine InnoDB;\n"
  },
  {
    "path": "framework/log/migrations/schema-oci.sql",
    "content": "/**\n * Database schema required by \\yii\\log\\DbTarget.\n *\n * The indexes declared are not required. They are mainly used to improve the performance\n * of some queries about message levels and categories. Depending on your actual needs, you may\n * want to create additional indexes (e.g. index on `log_time`).\n *\n * @author Alexander Makarov <sam@rmcreative.ru>\n * @link https://www.yiiframework.com/\n * @copyright 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n * @since 2.0.1\n */\n\ndrop table if exists \"log\";\n\ncreate table \"log\"\n(\n   \"id\"          number(20) NOT NULL PRIMARY KEY,\n   \"level\"       integer,\n   \"category\"    varchar(255),\n   \"log_time\"    number,\n   \"prefix\"      text,\n   \"message\"     text,\n   key \"idx_log_level\" (\"level\"),\n   key \"idx_log_category\" (\"category\")\n);\n"
  },
  {
    "path": "framework/log/migrations/schema-pgsql.sql",
    "content": "/**\n * Database schema required by \\yii\\log\\DbTarget.\n *\n * The indexes declared are not required. They are mainly used to improve the performance\n * of some queries about message levels and categories. Depending on your actual needs, you may\n * want to create additional indexes (e.g. index on `log_time`).\n *\n * @author Alexander Makarov <sam@rmcreative.ru>\n * @link https://www.yiiframework.com/\n * @copyright 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n * @since 2.0.1\n */\n\ndrop table if exists \"log\";\n\ncreate table \"log\"\n(\n   \"id\"          bigserial NOT NULL PRIMARY KEY,\n   \"level\"       integer,\n   \"category\"    varchar(255),\n   \"log_time\"    double precision,\n   \"prefix\"      text,\n   \"message\"     text\n);\n\ncreate index \"idx_log_level\" on \"log\" (\"level\");\ncreate index \"idx_log_category\" on \"log\" (\"category\");\n"
  },
  {
    "path": "framework/log/migrations/schema-sqlite.sql",
    "content": "/**\n * Database schema required by \\yii\\log\\DbTarget.\n *\n * The indexes declared are not required. They are mainly used to improve the performance\n * of some queries about message levels and categories. Depending on your actual needs, you may\n * want to create additional indexes (e.g. index on `log_time`).\n *\n * @author Alexander Makarov <sam@rmcreative.ru>\n * @link https://www.yiiframework.com/\n * @copyright 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n * @since 2.0.1\n */\n\ndrop table if exists \"log\";\n\ncreate table \"log\"\n(\n   \"id\"          integer PRIMARY KEY AUTOINCREMENT NOT NULL,\n   \"level\"       integer,\n   \"category\"    varchar(255),\n   \"log_time\"    double,\n   \"prefix\"      text,\n   \"message\"     text\n);\n\ncreate index \"idx_log_level\" on \"log\" (\"level\");\ncreate index \"idx_log_category\" on \"log\" (\"category\");\n"
  },
  {
    "path": "framework/mail/BaseMailer.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\mail;\n\nuse Yii;\nuse yii\\base\\Component;\nuse yii\\base\\InvalidConfigException;\nuse yii\\base\\ViewContextInterface;\nuse yii\\web\\View;\n\n/**\n * BaseMailer serves as a base class that implements the basic functions required by [[MailerInterface]].\n *\n * Concrete child classes should may focus on implementing the [[sendMessage()]] method.\n *\n * @see BaseMessage\n *\n * For more details and usage information on BaseMailer, see the [guide article on mailing](guide:tutorial-mailing).\n *\n * @property View $view View instance. Note that the type of this property differs in getter and setter. See\n * [[getView()]] and [[setView()]] for details.\n * @property string $viewPath The directory that contains the view files for composing mail messages Defaults\n * to '@app/mail'.\n *\n * @author Paul Klimov <klimov.paul@gmail.com>\n * @since 2.0\n */\nabstract class BaseMailer extends Component implements MailerInterface, ViewContextInterface\n{\n    /**\n     * @event MailEvent an event raised right before send.\n     * You may set [[MailEvent::isValid]] to be false to cancel the send.\n     */\n    public const EVENT_BEFORE_SEND = 'beforeSend';\n    /**\n     * @event MailEvent an event raised right after send.\n     */\n    public const EVENT_AFTER_SEND = 'afterSend';\n    /**\n     * @var string|bool HTML layout view name. This is the layout used to render HTML mail body.\n     * The property can take the following values:\n     *\n     * - a relative view name: a view file relative to [[viewPath]], e.g., 'layouts/html'.\n     * - a [path alias](guide:concept-aliases): an absolute view file path specified as a path alias, e.g., '@app/mail/html'.\n     * - a boolean false: the layout is disabled.\n     */\n    public $htmlLayout = 'layouts/html';\n    /**\n     * @var string|bool text layout view name. This is the layout used to render TEXT mail body.\n     * Please refer to [[htmlLayout]] for possible values that this property can take.\n     */\n    public $textLayout = 'layouts/text';\n    /**\n     * @var array the configuration that should be applied to any newly created\n     * email message instance by [[createMessage()]] or [[compose()]]. Any valid property defined\n     * by [[MessageInterface]] can be configured, such as `from`, `to`, `subject`, `textBody`, `htmlBody`, etc.\n     *\n     * For example:\n     *\n     * ```\n     * [\n     *     'charset' => 'UTF-8',\n     *     'from' => 'noreply@mydomain.com',\n     *     'bcc' => 'developer@mydomain.com',\n     * ]\n     * ```\n     */\n    public $messageConfig = [];\n    /**\n     * @var string the default class name of the new message instances created by [[createMessage()]]\n     */\n    public $messageClass = 'yii\\mail\\BaseMessage';\n    /**\n     * @var bool whether to save email messages as files under [[fileTransportPath]] instead of sending them\n     * to the actual recipients. This is usually used during development for debugging purpose.\n     * @see fileTransportPath\n     */\n    public $useFileTransport = false;\n    /**\n     * @var string the directory where the email messages are saved when [[useFileTransport]] is true.\n     */\n    public $fileTransportPath = '@runtime/mail';\n    /**\n     * @var callable|null a PHP callback that will be called by [[send()]] when [[useFileTransport]] is true.\n     * The callback should return a file name which will be used to save the email message.\n     * If not set, the file name will be generated based on the current timestamp.\n     *\n     * The signature of the callback is:\n     *\n     * ```\n     * function ($mailer, $message)\n     * ```\n     */\n    public $fileTransportCallback;\n\n    /**\n     * @var View|array view instance or its array configuration.\n     */\n    private $_view = [];\n    /**\n     * @var string the directory containing view files for composing mail messages.\n     */\n    private $_viewPath;\n\n\n    /**\n     * @param array|View $view view instance or its array configuration that will be used to\n     * render message bodies.\n     * @throws InvalidConfigException on invalid argument.\n     */\n    public function setView($view)\n    {\n        if (!is_array($view) && !is_object($view)) {\n            throw new InvalidConfigException('\"' . get_class($this) . '::view\" should be either object or configuration array, \"' . gettype($view) . '\" given.');\n        }\n        $this->_view = $view;\n    }\n\n    /**\n     * @return View view instance.\n     */\n    public function getView()\n    {\n        if (!is_object($this->_view)) {\n            $this->_view = $this->createView($this->_view);\n        }\n\n        return $this->_view;\n    }\n\n    /**\n     * Creates view instance from given configuration.\n     * @param array $config view configuration.\n     * @return View view instance.\n     */\n    protected function createView(array $config)\n    {\n        if (!array_key_exists('class', $config)) {\n            $config['class'] = View::className();\n        }\n\n        return Yii::createObject($config);\n    }\n\n    private $_message;\n\n    /**\n     * Creates a new message instance and optionally composes its body content via view rendering.\n     *\n     * @param string|array|null $view the view to be used for rendering the message body. This can be:\n     *\n     * - a string, which represents the view name or [path alias](guide:concept-aliases) for rendering the HTML body of the email.\n     *   In this case, the text body will be generated by applying `strip_tags()` to the HTML body.\n     * - an array with 'html' and/or 'text' elements. The 'html' element refers to the view name or path alias\n     *   for rendering the HTML body, while 'text' element is for rendering the text body. For example,\n     *   `['html' => 'contact-html', 'text' => 'contact-text']`.\n     * - null, meaning the message instance will be returned without body content.\n     *\n     * The view to be rendered can be specified in one of the following formats:\n     *\n     * - path alias (e.g. \"@app/mail/contact\");\n     * - a relative view name (e.g. \"contact\") located under [[viewPath]].\n     *\n     * @param array $params the parameters (name-value pairs) that will be extracted and made available in the view file.\n     * @return MessageInterface message instance.\n     */\n    public function compose($view = null, array $params = [])\n    {\n        $message = $this->createMessage();\n        if ($view === null) {\n            return $message;\n        }\n\n        if (!array_key_exists('message', $params)) {\n            $params['message'] = $message;\n        }\n\n        $this->_message = $message;\n\n        if (is_array($view)) {\n            if (isset($view['html'])) {\n                $html = $this->render($view['html'], $params, $this->htmlLayout);\n            }\n            if (isset($view['text'])) {\n                $text = $this->render($view['text'], $params, $this->textLayout);\n            }\n        } else {\n            $html = $this->render($view, $params, $this->htmlLayout);\n        }\n\n\n        $this->_message = null;\n\n        if (isset($html)) {\n            $message->setHtmlBody($html);\n        }\n        if (isset($text)) {\n            $message->setTextBody($text);\n        } elseif (isset($html)) {\n            if (preg_match('~<body[^>]*>(.*?)</body>~is', $html, $match)) {\n                $html = $match[1];\n            }\n            // remove style and script\n            $html = preg_replace('~<((style|script))[^>]*>(.*?)</\\1>~is', '', $html);\n            // strip all HTML tags and decoded HTML entities\n            $text = html_entity_decode(strip_tags($html), ENT_QUOTES | ENT_HTML5, Yii::$app ? Yii::$app->charset : 'UTF-8');\n            // improve whitespace\n            $text = preg_replace(\"~^[ \\t]+~m\", '', trim($text));\n            $text = preg_replace('~\\R\\R+~mu', \"\\n\\n\", $text);\n            $message->setTextBody($text);\n        }\n\n        return $message;\n    }\n\n    /**\n     * Creates a new message instance.\n     * The newly created instance will be initialized with the configuration specified by [[messageConfig]].\n     * If the configuration does not specify a 'class', the [[messageClass]] will be used as the class\n     * of the new message instance.\n     * @return MessageInterface message instance.\n     */\n    protected function createMessage()\n    {\n        $config = $this->messageConfig;\n        if (!array_key_exists('class', $config)) {\n            $config['class'] = $this->messageClass;\n        }\n        $config['mailer'] = $this;\n        return Yii::createObject($config);\n    }\n\n    /**\n     * Sends the given email message.\n     * This method will log a message about the email being sent.\n     * If [[useFileTransport]] is true, it will save the email as a file under [[fileTransportPath]].\n     * Otherwise, it will call [[sendMessage()]] to send the email to its recipient(s).\n     * Child classes should implement [[sendMessage()]] with the actual email sending logic.\n     * @param MessageInterface $message email message instance to be sent\n     * @return bool whether the message has been sent successfully\n     */\n    public function send($message)\n    {\n        if (!$this->beforeSend($message)) {\n            return false;\n        }\n\n        $address = $message->getTo();\n        if (is_array($address)) {\n            $address = implode(', ', array_keys($address));\n        }\n        Yii::info('Sending email \"' . $message->getSubject() . '\" to \"' . $address . '\"', __METHOD__);\n\n        if ($this->useFileTransport) {\n            $isSuccessful = $this->saveMessage($message);\n        } else {\n            $isSuccessful = $this->sendMessage($message);\n        }\n        $this->afterSend($message, $isSuccessful);\n\n        return $isSuccessful;\n    }\n\n    /**\n     * Sends multiple messages at once.\n     *\n     * The default implementation simply calls [[send()]] multiple times.\n     * Child classes may override this method to implement more efficient way of\n     * sending multiple messages.\n     *\n     * @param array $messages list of email messages, which should be sent.\n     * @return int number of messages that are successfully sent.\n     */\n    public function sendMultiple(array $messages)\n    {\n        $successCount = 0;\n        foreach ($messages as $message) {\n            if ($this->send($message)) {\n                $successCount++;\n            }\n        }\n\n        return $successCount;\n    }\n\n    /**\n     * Renders the specified view with optional parameters and layout.\n     * The view will be rendered using the [[view]] component.\n     * @param string $view the view name or the [path alias](guide:concept-aliases) of the view file.\n     * @param array $params the parameters (name-value pairs) that will be extracted and made available in the view file.\n     * @param string|bool $layout layout view name or [path alias](guide:concept-aliases). If false, no layout will be applied.\n     * @return string the rendering result.\n     */\n    public function render($view, $params = [], $layout = false)\n    {\n        $output = $this->getView()->render($view, $params, $this);\n        if ($layout !== false) {\n            return $this->getView()->render($layout, ['content' => $output, 'message' => $this->_message], $this);\n        }\n\n        return $output;\n    }\n\n    /**\n     * Sends the specified message.\n     * This method should be implemented by child classes with the actual email sending logic.\n     * @param MessageInterface $message the message to be sent\n     * @return bool whether the message is sent successfully\n     */\n    abstract protected function sendMessage($message);\n\n    /**\n     * Saves the message as a file under [[fileTransportPath]].\n     * @param MessageInterface $message\n     * @return bool whether the message is saved successfully\n     */\n    protected function saveMessage($message)\n    {\n        $path = Yii::getAlias($this->fileTransportPath);\n        if (!is_dir($path)) {\n            mkdir($path, 0777, true);\n        }\n        if ($this->fileTransportCallback !== null) {\n            $file = $path . '/' . call_user_func($this->fileTransportCallback, $this, $message);\n        } else {\n            $file = $path . '/' . $this->generateMessageFileName();\n        }\n        file_put_contents($file, $message->toString());\n\n        return true;\n    }\n\n    /**\n     * @return string the file name for saving the message when [[useFileTransport]] is true.\n     */\n    public function generateMessageFileName()\n    {\n        $time = microtime(true);\n        $timeInt = (int) $time;\n\n        return date('Ymd-His-', $timeInt) . sprintf('%04d', (int) (($time - $timeInt) * 10000)) . '-' . sprintf('%04d', random_int(0, 10000)) . '.eml';\n    }\n\n    /**\n     * @return string the directory that contains the view files for composing mail messages\n     * Defaults to '@app/mail'.\n     */\n    public function getViewPath()\n    {\n        if ($this->_viewPath === null) {\n            $this->setViewPath('@app/mail');\n        }\n\n        return $this->_viewPath;\n    }\n\n    /**\n     * @param string $path the directory that contains the view files for composing mail messages\n     * This can be specified as an absolute path or a [path alias](guide:concept-aliases).\n     */\n    public function setViewPath($path)\n    {\n        $this->_viewPath = Yii::getAlias($path);\n    }\n\n    /**\n     * This method is invoked right before mail send.\n     * You may override this method to do last-minute preparation for the message.\n     * If you override this method, please make sure you call the parent implementation first.\n     * @param MessageInterface $message\n     * @return bool whether to continue sending an email.\n     */\n    public function beforeSend($message)\n    {\n        $event = new MailEvent(['message' => $message]);\n        $this->trigger(self::EVENT_BEFORE_SEND, $event);\n\n        return $event->isValid;\n    }\n\n    /**\n     * This method is invoked right after mail was send.\n     * You may override this method to do some postprocessing or logging based on mail send status.\n     * If you override this method, please make sure you call the parent implementation first.\n     * @param MessageInterface $message\n     * @param bool $isSuccessful\n     */\n    public function afterSend($message, $isSuccessful)\n    {\n        $event = new MailEvent(['message' => $message, 'isSuccessful' => $isSuccessful]);\n        $this->trigger(self::EVENT_AFTER_SEND, $event);\n    }\n}\n"
  },
  {
    "path": "framework/mail/BaseMessage.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\mail;\n\nuse Yii;\nuse yii\\base\\BaseObject;\nuse yii\\base\\ErrorHandler;\n\n/**\n * BaseMessage serves as a base class that implements the [[send()]] method required by [[MessageInterface]].\n *\n * By default, [[send()]] will use the \"mailer\" application component to send the current message.\n * The \"mailer\" application component should be a mailer instance implementing [[MailerInterface]].\n *\n * @see BaseMailer\n *\n * @author Paul Klimov <klimov.paul@gmail.com>\n * @since 2.0\n */\nabstract class BaseMessage extends BaseObject implements MessageInterface\n{\n    /**\n     * @var MailerInterface|null the mailer instance that created this message.\n     * For independently created messages this is `null`.\n     */\n    public $mailer;\n\n\n    /**\n     * Sends this email message.\n     * @param MailerInterface|null $mailer the mailer that should be used to send this message.\n     * If no mailer is given it will first check if [[mailer]] is set and if not,\n     * the \"mailer\" application component will be used instead.\n     * @return bool whether this message is sent successfully.\n     */\n    public function send(?MailerInterface $mailer = null)\n    {\n        if ($mailer === null && $this->mailer === null) {\n            $mailer = Yii::$app->getMailer();\n        } elseif ($mailer === null) {\n            $mailer = $this->mailer;\n        }\n\n        return $mailer->send($this);\n    }\n\n    /**\n     * PHP magic method that returns the string representation of this object.\n     * @return string the string representation of this object.\n     */\n    public function __toString()\n    {\n        // __toString cannot throw exception\n        // use trigger_error to bypass this limitation\n        try {\n            return $this->toString();\n        } catch (\\Throwable $e) {\n            if (PHP_VERSION_ID < 70400) {\n                trigger_error(ErrorHandler::convertExceptionToString($e), E_USER_ERROR);\n\n                return '';\n            }\n\n            throw $e;\n        }\n    }\n}\n"
  },
  {
    "path": "framework/mail/MailEvent.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\mail;\n\nuse yii\\base\\Event;\n\n/**\n * MailEvent represents the event parameter used for events triggered by [[BaseMailer]].\n *\n * By setting the [[isValid]] property, one may control whether to continue running the action.\n *\n * @author Mark Jebri <mark.github@yandex.ru>\n * @since 2.0\n */\nclass MailEvent extends Event\n{\n    /**\n     * @var \\yii\\mail\\MessageInterface the mail message being send.\n     */\n    public $message;\n    /**\n     * @var bool if message was sent successfully.\n     */\n    public $isSuccessful;\n    /**\n     * @var bool whether to continue sending an email. Event handlers of\n     * [[\\yii\\mail\\BaseMailer::EVENT_BEFORE_SEND]] may set this property to decide whether\n     * to continue send or not.\n     */\n    public $isValid = true;\n}\n"
  },
  {
    "path": "framework/mail/MailerInterface.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\mail;\n\n/**\n * MailerInterface is the interface that should be implemented by mailer classes.\n *\n * A mailer should mainly support creating and sending [[MessageInterface|mail messages]]. It should\n * also support composition of the message body through the view rendering mechanism. For example,\n *\n * ```\n * Yii::$app->mailer->compose('contact/html', ['contactForm' => $form])\n *     ->setFrom('from@domain.com')\n *     ->setTo($form->email)\n *     ->setSubject($form->subject)\n *     ->send();\n * ```\n *\n * @see MessageInterface\n *\n * @author Paul Klimov <klimov.paul@gmail.com>\n * @since 2.0\n */\ninterface MailerInterface\n{\n    /**\n     * Creates a new message instance and optionally composes its body content via view rendering.\n     *\n     * @param string|array|null $view the view to be used for rendering the message body. This can be:\n     *\n     * - a string, which represents the view name or [path alias](guide:concept-aliases) for rendering the HTML body of the email.\n     *   In this case, the text body will be generated by applying `strip_tags()` to the HTML body.\n     * - an array with 'html' and/or 'text' elements. The 'html' element refers to the view name or path alias\n     *   for rendering the HTML body, while 'text' element is for rendering the text body. For example,\n     *   `['html' => 'contact-html', 'text' => 'contact-text']`.\n     * - null, meaning the message instance will be returned without body content.\n     *\n     * @param array $params the parameters (name-value pairs) that will be extracted and made available in the view file.\n     * @return MessageInterface message instance.\n     */\n    public function compose($view = null, array $params = []);\n\n    /**\n     * Sends the given email message.\n     * @param MessageInterface $message email message instance to be sent\n     * @return bool whether the message has been sent successfully\n     */\n    public function send($message);\n\n    /**\n     * Sends multiple messages at once.\n     *\n     * This method may be implemented by some mailers which support more efficient way of sending multiple messages in the same batch.\n     *\n     * @param array $messages list of email messages, which should be sent.\n     * @return int number of messages that are successfully sent.\n     */\n    public function sendMultiple(array $messages);\n}\n"
  },
  {
    "path": "framework/mail/MessageInterface.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\mail;\n\n/**\n * MessageInterface is the interface that should be implemented by mail message classes.\n *\n * A message represents the settings and content of an email, such as the sender, recipient,\n * subject, body, etc.\n *\n * Messages are sent by a [[\\yii\\mail\\MailerInterface|mailer]], like the following,\n *\n * ```\n * Yii::$app->mailer->compose()\n *     ->setFrom('from@domain.com')\n *     ->setTo($form->email)\n *     ->setSubject($form->subject)\n *     ->setTextBody('Plain text content')\n *     ->setHtmlBody('<b>HTML content</b>')\n *     ->send();\n * ```\n *\n * @see MailerInterface\n *\n * @author Paul Klimov <klimov.paul@gmail.com>\n * @since 2.0\n */\ninterface MessageInterface\n{\n    /**\n     * Returns the character set of this message.\n     * @return string the character set of this message.\n     */\n    public function getCharset();\n\n    /**\n     * Sets the character set of this message.\n     * @param string $charset character set name.\n     * @return $this self reference.\n     */\n    public function setCharset($charset);\n\n    /**\n     * Returns the message sender.\n     * @return string|array the sender\n     */\n    public function getFrom();\n\n    /**\n     * Sets the message sender.\n     * @param string|array $from sender email address.\n     * You may pass an array of addresses if this message is from multiple people.\n     * You may also specify sender name in addition to email address using format:\n     * `[email => name]`.\n     * @return $this self reference.\n     */\n    public function setFrom($from);\n\n    /**\n     * Returns the message recipient(s).\n     * @return string|array the message recipients\n     */\n    public function getTo();\n\n    /**\n     * Sets the message recipient(s).\n     * @param string|array $to receiver email address.\n     * You may pass an array of addresses if multiple recipients should receive this message.\n     * You may also specify receiver name in addition to email address using format:\n     * `[email => name]`.\n     * @return $this self reference.\n     */\n    public function setTo($to);\n\n    /**\n     * Returns the reply-to address of this message.\n     * @return string|array the reply-to address of this message.\n     */\n    public function getReplyTo();\n\n    /**\n     * Sets the reply-to address of this message.\n     * @param string|array $replyTo the reply-to address.\n     * You may pass an array of addresses if this message should be replied to multiple people.\n     * You may also specify reply-to name in addition to email address using format:\n     * `[email => name]`.\n     * @return $this self reference.\n     */\n    public function setReplyTo($replyTo);\n\n    /**\n     * Returns the Cc (additional copy receiver) addresses of this message.\n     * @return string|array the Cc (additional copy receiver) addresses of this message.\n     */\n    public function getCc();\n\n    /**\n     * Sets the Cc (additional copy receiver) addresses of this message.\n     * @param string|array $cc copy receiver email address.\n     * You may pass an array of addresses if multiple recipients should receive this message.\n     * You may also specify receiver name in addition to email address using format:\n     * `[email => name]`.\n     * @return $this self reference.\n     */\n    public function setCc($cc);\n\n    /**\n     * Returns the Bcc (hidden copy receiver) addresses of this message.\n     * @return string|array the Bcc (hidden copy receiver) addresses of this message.\n     */\n    public function getBcc();\n\n    /**\n     * Sets the Bcc (hidden copy receiver) addresses of this message.\n     * @param string|array $bcc hidden copy receiver email address.\n     * You may pass an array of addresses if multiple recipients should receive this message.\n     * You may also specify receiver name in addition to email address using format:\n     * `[email => name]`.\n     * @return $this self reference.\n     */\n    public function setBcc($bcc);\n\n    /**\n     * Returns the message subject.\n     * @return string the message subject\n     */\n    public function getSubject();\n\n    /**\n     * Sets the message subject.\n     * @param string $subject message subject\n     * @return $this self reference.\n     */\n    public function setSubject($subject);\n\n    /**\n     * Sets message plain text content.\n     * @param string $text message plain text content.\n     * @return $this self reference.\n     */\n    public function setTextBody($text);\n\n    /**\n     * Sets message HTML content.\n     * @param string $html message HTML content.\n     * @return $this self reference.\n     */\n    public function setHtmlBody($html);\n\n    /**\n     * Attaches existing file to the email message.\n     * @param string $fileName full file name\n     * @param array $options options for embed file. Valid options are:\n     *\n     * - fileName: name, which should be used to attach file.\n     * - contentType: attached file MIME type.\n     *\n     * @return $this self reference.\n     */\n    public function attach($fileName, array $options = []);\n\n    /**\n     * Attach specified content as file for the email message.\n     * @param string $content attachment file content.\n     * @param array $options options for embed file. Valid options are:\n     *\n     * - fileName: name, which should be used to attach file.\n     * - contentType: attached file MIME type.\n     *\n     * @return $this self reference.\n     */\n    public function attachContent($content, array $options = []);\n\n    /**\n     * Attach a file and return it's CID source.\n     * This method should be used when embedding images or other data in a message.\n     * @param string $fileName file name.\n     * @param array $options options for embed file. Valid options are:\n     *\n     * - fileName: name, which should be used to attach file.\n     * - contentType: attached file MIME type.\n     *\n     * @return string attachment CID.\n     */\n    public function embed($fileName, array $options = []);\n\n    /**\n     * Attach a content as file and return it's CID source.\n     * This method should be used when embedding images or other data in a message.\n     * @param string $content attachment file content.\n     * @param array $options options for embed file. Valid options are:\n     *\n     * - fileName: name, which should be used to attach file.\n     * - contentType: attached file MIME type.\n     *\n     * @return string attachment CID.\n     */\n    public function embedContent($content, array $options = []);\n\n    /**\n     * Sends this email message.\n     * @param MailerInterface|null $mailer the mailer that should be used to send this message.\n     * If null, the \"mailer\" application component will be used instead.\n     * @return bool whether this message is sent successfully.\n     */\n    public function send(?MailerInterface $mailer = null);\n\n    /**\n     * Returns string representation of this message.\n     * @return string the string representation of this message.\n     */\n    public function toString();\n}\n"
  },
  {
    "path": "framework/messages/af/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => ' en ',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '\"{attribute}\" ondersteun nie operateur \"{operator}\" nie.',\n    '(not set)' => '(nie gestel nie)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => '\\'n Interne bediener fout het plaasgevind.',\n    'Are you sure you want to delete this item?' => 'Is jy seker jy wil hierdie item skrap?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => 'Voorwaarde vir \"{attribute}\" moet óf \\'n waarde, óf \\'n geldige operateurspesifikasie wees.',\n    'Delete' => 'Skrap',\n    'Error' => 'Fout',\n    'File upload failed.' => 'Lêeroplaai het misluk.',\n    'Home' => 'Tuis',\n    'Invalid data received for parameter \"{param}\".' => 'Ongeldige data ontvang vir parameter \"{param}\".',\n    'Login Required' => 'Inteken vereis',\n    'Missing required arguments: {params}' => 'Vereiste argumente ontbreek: {params}',\n    'Missing required parameters: {params}' => 'Vereiste parameters ontbreek: {params}',\n    'No' => 'Nee',\n    'No results found.' => 'Geen resultate gevind nie.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Slegs lêers met hierdie MIME-tipes word toegelaat: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Slegs hierdie soort lêers word toegelaat: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => 'Operateur \"{operator}\" moet gebruik word met \\'n soekkenmerk.',\n    'Operator \"{operator}\" requires multiple operands.' => 'Operateur \"{operator}\" vereis veelvuldige operande.',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Bladsy nie gevind nie.',\n    'Please fix the following errors:' => 'Maak asseblief die volgende foute reg:',\n    'Please upload a file.' => 'Laai asseblief \\'n lêer op.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => '',\n    'The combination {values} of {attributes} has already been taken.' => 'Die kombinasie {values} van {attributes} is reeds geneem.',\n    'The file \"{file}\" is not an image.' => 'Die lêer \"{file}\" is nie \\'n prent nie.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'Die lêer \"{file}\" is te groot. Die grootte daarvan kan nie groter as {formattedLimit} wees nie.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Die lêer \"{file}\" is te klein. Die grootte daarvan kan nie kleiner as {formattedLimit} wees nie.',\n    'The format of {attribute} is invalid.' => 'Die formaat van {attribute} is nie geldig nie.',\n    'The format of {filter} is invalid.' => 'Die formaat van {filter} is nie geldig nie.',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Die prent \"{file}\" is te groot. Die hoogte kan nie groter as {limit, number} {limit, plural, one{spikkel} other{spikkels}} wees nie.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Die prent \"{file}\" is te groot. Die wydte kan nie groter as {limit, number} {limit, plural, one{spikkel} other{spikkels}} wees nie.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Die prent \"{file}\" is te klein. Die hoogte kan nie kleiner as {limit, number} {limit, plural, one{spikkel} other{spikkels}} wees nie.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Die prent \"{file}\" is te klein. Die hoogte kan nie kleiner as {limit, number} {limit, plural, one{spikkel} other{spikkels}} wees nie.',\n    'The requested view \"{name}\" was not found.' => 'Die versoekte aansig \"{name}\" is nie gevind nie.',\n    'The verification code is incorrect.' => 'Die verifikasiekode is verkeerd.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Totaal <b>{count, number}</b> {count, plural, one{item} other{items}}.',\n    'Unable to verify your data submission.' => 'Die data wat voorgelê is, kon nie verifieer word nie.',\n    'Unknown alias: -{name}' => 'Onbekende alias: -{name}',\n    'Unknown filter attribute \"{attribute}\"' => 'Onbekende filterkenmerk \"{attribute}',\n    'Unknown option: --{name}' => 'Onbekende opsie: --{name}',\n    'Update' => 'Opdateer',\n    'View' => 'Beskou',\n    'Yes' => 'Ja',\n    'You are not allowed to perform this action.' => 'Jy mag nie hierdie aksie uitvoer nie.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Jy kan \\'n maksimum van {limit, number} {limit, plural, one{lêer} other{lêers}} oplaai.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => 'Jy moet ten minste {limit, number} {limit, plural, one{lêer} other{lêers}} oplaai.',\n    'in {delta, plural, =1{a day} other{# days}}' => 'oor {delta, plural, =1{\\'n dag} other{# dae}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'oor {delta, plural, =1{\\'n minuut} other{# minute}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'oor {delta, plural, =1{\\'n maand} other{# maande}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'oor {delta, plural, =1{\\'n sekonde} other{# sekondes}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'oor {delta} jaar',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'oor {delta} uur',\n    'just now' => 'netnou',\n    'the input value' => 'die insetwaarde',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" is reeds geneem.',\n    '{attribute} cannot be blank.' => '{attribute} kan nie leeg wees nie.',\n    '{attribute} contains wrong subnet mask.' => '{attribute} bevat verkeerde subnetmasker.',\n    '{attribute} is invalid.' => '{attribute} is nie geldig nie.',\n    '{attribute} is not a valid URL.' => '{attribute} is nie \\'n geldige webadres nie.',\n    '{attribute} is not a valid email address.' => '{attribute} is nie \\'n geldige e-posadres nie.',\n    '{attribute} is not in the allowed range.' => '{attribute} is nie in die toegelate reeks nie.',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} moet \"{requiredValue}\" wees.',\n    '{attribute} must be a number.' => '{attribute} moet \\'n nommer wees.',\n    '{attribute} must be a string.' => '{attribute} moet teks wees.',\n    '{attribute} must be a valid IP address.' => '{attribute} moet \\'n geldige IP-adres wees.',\n    '{attribute} must be an IP address with specified subnet.' => '{attribute} waarde moet \\'n IP-adres met gespesifiseerde subnet wees.',\n    '{attribute} must be an integer.' => '{attribute} moet \\'n heelgetal wees.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} moet óf \"{true}\" óf \"{false}\" wees.',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '{attribute} moet gelyk wees aan \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} moet groter as \"{compareValueOrAttribute}\" wees.',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} moet groter of gelyk aan \"{compareValueOrAttribute}\" wees.',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} moet minder as \"{compareValueOrAttribute}\" wees.',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} moet minder as of gelyk aan \"{compareValueOrAttribute}\" wees.',\n    '{attribute} must be no greater than {max}.' => '{attribute} mag nie groter as {max} wees nie.',\n    '{attribute} must be no less than {min}.' => '{attribute} mag nie kleiner as {min} wees nie.',\n    '{attribute} must not be a subnet.' => '{attribute} mag nie \\'n subnet wees nie.',\n    '{attribute} must not be an IPv4 address.' => '{attribute} mag nie \\'n IPv4-adres wees nie.',\n    '{attribute} must not be an IPv6 address.' => '{attribute} mag nie \\'n IPv6-adres wees nie.',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} moet nie gelyk aan \"{compareValueOrAttribute}\" wees nie.',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} moet ten minste {min, number} {min, plural, one{karakter} other{karakters}} bevat.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} moet hoogstens {max, number} {max, plural, one{karakter} other{karakters}} bevat.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} moet {length, number} {length, plural, one{karakter} other{karakters}} bevat.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, =1{1 dag} other{# dae}}',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta} uur',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, =1{1 minuut} other{# minute}}',\n    '{delta, plural, =1{1 month} other{# months}}' => '{delta, plural, =1{1 maand} other{# maande}}',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta, plural, =1{1 sekonde} other{# sekondes}}',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta} jaar',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{\\'n dag} other{# dae}} gelede',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{\\'n minuut} other{# minute}} gelede',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{\\'n maand} other{# maande}} gelede',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{\\'n sekonde} other{# sekondes}} gelede',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta} jaar gelede',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta} uur gelede',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '{nFormatted} GiB',\n    '{nFormatted} KiB' => '{nFormatted} KiB',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '{nFormatted} MiB',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} KB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, =1{greep} other{grepe}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, =1{gibigreep} other{gibigrepe}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, =1{gigagreep} other{gigagrepe}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, =1{kibigreep} other{kibigrepe}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, =1{kilogreep} other{kilogrepe}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, =1{mebigreep} other{mebigrepe}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, =1{megagreep} other{megagrepe}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, =1{pebigreep} other{pebigrepe}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, =1{petagreep} other{petagrepe}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, =1{tebigreep} other{tebigrepe}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, =1{teragreep} other{teragrepe}}',\n];\n"
  },
  {
    "path": "framework/messages/ar/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => '',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(لم يحدد)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => '.حدث خطأ داخلي في الخادم',\n    'Are you sure you want to delete this item?' => '',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'حذف',\n    'Error' => 'خطأ',\n    'File upload failed.' => '.فشل في تحميل الملف',\n    'Home' => 'الرئيسية',\n    'Invalid data received for parameter \"{param}\".' => 'بيانات غير صالحة قد وردت في \"{param}\".',\n    'Login Required' => 'تسجيل الدخول مطلوب',\n    'Missing required arguments: {params}' => 'البيانات المطلوبة ضرورية: {params}',\n    'Missing required parameters: {params}' => 'البيانات المطلوبة ضرورية: {params}',\n    'No' => 'لا',\n    'No results found.' => 'لم يتم العثور على نتائج',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'فقط الملفات من هذه الأنواع مسموح بها: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'فقط الملفات التي تحمل هذه الصيغ مسموح بها: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'لم يتم العثور على الصفحة',\n    'Please fix the following errors:' => 'الرجاء تصحيح الأخطاء التالية:',\n    'Please upload a file.' => 'الرجاء تحميل ملف.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'عرض <b>{begin, number}-{end, number}</b> من أصل <b>{totalCount, number}</b> {totalCount, plural, one{مُدخل} few{مُدخلات} many{مُدخل} other{مُدخلات}}.',\n    'The combination {values} of {attributes} has already been taken.' => '',\n    'The file \"{file}\" is not an image.' => 'الملف \"{file}\" ليس صورة.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'الملف \"{file}\" كبير الحجم. حجمه لا يجب أن يتخطى {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'الملف \"{file}\"  صغير جداً. حجمه لا يجب أن يكون أصغر من {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'شكل {attribute} غير صالح',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'الصورة \"{file}\" كبيرة جداً. ارتفاعها لا يمكن أن يتخطى {limit, number} {limit, plural, other{بكسل}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'الصورة \"{file}\" كبيرة جداً. عرضها لا يمكن أن يتخطى {limit, number} {limit, plural, other{بكسل}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'الصورة \"{file}\" صغيرة جداً. ارتفاعها لا يمكن أن يقل عن  {limit, number} {limit, plural, other{بكسل}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'الصورة \"{file}\" كبيرة جداً. عرضها لا يمكن أن يقل عن  {limit, number} {limit, plural, other{بكسل}}.',\n    'The requested view \"{name}\" was not found.' => '',\n    'The verification code is incorrect.' => 'رمز التحقق غير صحيح',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'مجموع <b>{count, number}</b> {count, plural, one{مُدخل} few{مُدخلات} many{مُدخل} other{مُدخلات}}.',\n    'Unable to verify your data submission.' => 'لم نستطع التأكد من البيانات المقدمة.',\n    'Unknown alias: -{name}' => '',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'خيار غير معروف: --{name}',\n    'Update' => 'تحديث',\n    'View' => 'عرض',\n    'Yes' => 'نعم',\n    'You are not allowed to perform this action.' => 'غير مصرح لك القيام بهذا العمل',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'تستطيع كأقصى حد تحميل {limit, number} {limit, plural, one{ملف} few{ملفات} many{ملف} other{ملفات}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => '',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => '',\n    'in {delta, plural, =1{a month} other{# months}}' => '',\n    'in {delta, plural, =1{a second} other{# seconds}}' => '',\n    'in {delta, plural, =1{a year} other{# years}}' => '',\n    'in {delta, plural, =1{an hour} other{# hours}}' => '',\n    'just now' => '',\n    'the input value' => 'قيمة المُدخل',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" سبق استعماله',\n    '{attribute} cannot be blank.' => '{attribute} لا يمكن تركه فارغًا.',\n    '{attribute} contains wrong subnet mask.' => '',\n    '{attribute} is invalid.' => '{attribute} غير صالح.',\n    '{attribute} is not a valid URL.' => '{attribute} ليس بعنوان صحيح.',\n    '{attribute} is not a valid email address.' => '{attribute} ليس ببريد إلكتروني صحيح.',\n    '{attribute} is not in the allowed range.' => '',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} يجب أن يكون \"{requiredValue}\".',\n    '{attribute} must be a number.' => '{attribute} يجب أن يكون رقمًا',\n    '{attribute} must be a string.' => '{attribute} يجب أن يكون كلمات',\n    '{attribute} must be a valid IP address.' => '',\n    '{attribute} must be an IP address with specified subnet.' => '',\n    '{attribute} must be an integer.' => '{attribute} يجب أن يكون رقمًا صحيحًا',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} يجب أن يكن إما \"{true}\" أو \"{false}\".',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '{attribute} يجب أن يساوي \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} يجب أن يكون أكبر من \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} يجب أن يكون أكبر من أو يساوي \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} يجب أن يكون أصغر من \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} يجب أن يكون أصغر من أو يساوي \"{compareValueOrAttribute}\".',\n    '{attribute} must be no greater than {max}.' => '{attribute} يجب أن لا يكون أكبر من \"{max}\".',\n    '{attribute} must be no less than {min}.' => '{attribute} يجب أن لا يكون أصغر من \"{min}\".',\n    '{attribute} must not be a subnet.' => '',\n    '{attribute} must not be an IPv4 address.' => '',\n    '{attribute} must not be an IPv6 address.' => '',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} يجب أن لا يساوي \"{compareValueOrAttribute}\".',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} يجب أن يحتوي على أكثر من {min, number} {min, plural, one{حرف} few{حروف} other{حرف}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} يجب أن لا يحتوي على أكثر من {max, number} {max, plural, one{حرف} few{حروف} other{حرف}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute}  يجب أن يحتوي على {length, number} {length, plural, one{حرف} few{حروف} other{حرف}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '',\n    '{delta, plural, =1{1 month} other{# months}}' => '',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '',\n    '{delta, plural, =1{1 year} other{# years}}' => '',\n    '{delta, plural, =1{a day} other{# days}} ago' => '',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '',\n    '{delta, plural, =1{a month} other{# months}} ago' => '',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '',\n    '{delta, plural, =1{a year} other{# years}} ago' => '',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '',\n    '{nFormatted} B' => '{nFormatted} بايت',\n    '{nFormatted} GB' => '{nFormatted} جيجابايت',\n    '{nFormatted} GiB' => '{nFormatted} جيبيبايت',\n    '{nFormatted} KiB' => '{nFormatted} كيبيبايت',\n    '{nFormatted} MB' => '{nFormatted} ميجابايت',\n    '{nFormatted} MiB' => '{nFormatted} ميبيبايت',\n    '{nFormatted} PB' => '{nFormatted} بيتابايت',\n    '{nFormatted} PiB' => '{nFormatted} بيبيبايت',\n    '{nFormatted} TB' => '{nFormatted} تيرابايت',\n    '{nFormatted} TiB' => '{nFormatted} تيبيبايت',\n    '{nFormatted} kB' => '{nFormatted} كيلوبايت',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} بايت',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} جيبيبايت',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} جيجابايت',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} كيبيبايت',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} كيلوبايت',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} ميبيبايت',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} ميجابايت',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} بيبيبايت',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} بيتابايت',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} تيبيبايت',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} تيرابايت',\n];\n"
  },
  {
    "path": "framework/messages/az/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => '',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(məlumat yoxdur)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Daxili server xətası meydana gəldi.',\n    'Are you sure you want to delete this item?' => 'Bu elementi silmək istədiyinizə əminsinizmi?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'Sil',\n    'Error' => 'Xəta',\n    'File upload failed.' => 'Fayl yüklənmədi.',\n    'Home' => 'Ana səhifə',\n    'Invalid data received for parameter \"{param}\".' => '\"{param}\" parametri üçün yalnış məlumat alındı.',\n    'Login Required' => 'İstifadəçi girişi tələb olunur',\n    'Missing required arguments: {params}' => 'Tələb olunan arqumentlər tapılmadı: {params}',\n    'Missing required parameters: {params}' => 'Tələb olunan parametrlər tapılmadı: {params}',\n    'No' => 'Xeyr',\n    'No results found.' => 'Heç bir nəticə tapılmadı',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Ancaq bu MIME tipli fayllara icazə verilib: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Genişlənmələri ancaq bu tipdə olan fayllara icazə verilib: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Səhifə tapılmadı.',\n    'Please fix the following errors:' => 'Xahiş olunur xətaları düzəldin: ',\n    'Please upload a file.' => 'Xahiş olunur bir fayl yükləyin.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => '<b>{totalCount, number}</b> {totalCount, plural, one{elementdən} other{elementdən}} <b>{begin, number}-{end, number}</b> arası göstərilir.',\n    'The combination {values} of {attributes} has already been taken.' => '',\n    'The file \"{file}\" is not an image.' => '\"{file}\" təsvir faylı deyil.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => '\"{file}\" faylı çox böyükdür. Həcmi {formattedLimit} qiymətindən böyük ola bilməz.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => '\"{file}\" faylı çox kiçikdir. Həcmi {formattedLimit} qiymətindən kiçik ola bilməz.',\n    'The format of {attribute} is invalid.' => '{attribute} formatı düzgün deyil.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '\"{file}\" şəkli çox böyükdür. Uzunluq {limit, plural, one{pixel} other{pixels}} qiymətindən böyük ola bilməz.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '\"{file}\" şəkli çox böyükdür. Eni {limit, number} {limit, plural, one{pixel} other{pixel}} qiymətindən böyük ola bilməz.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '\"{file}\" şəkli çox kiçikdir. Eni {limit, number} {limit, plural, one{pixel} other{pixel}} qiymətindən kiçik ola bilməz.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '\"{file}\" şəkli çox kiçikdir. Eni {limit, number} {limit, plural, one{pixel} other{pixels}} qiymətindən kiçik ola bilməz.',\n    'The requested view \"{name}\" was not found.' => '',\n    'The verification code is incorrect.' => 'Təsdiqləmə kodu səhvdir.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Toplam <b>{count, number}</b> {count, plural, one{element} other{element}}.',\n    'Unable to verify your data submission.' => 'Təqdim etdiyiniz məlumat təsdiqlənmədi.',\n    'Unknown alias: -{name}' => '',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'Qeyri-müəyyən seçim: --{name}',\n    'Update' => 'Yenilə',\n    'View' => 'Bax',\n    'Yes' => 'Bəli',\n    'You are not allowed to perform this action.' => 'Bu əməliyyatı yerinə yetirmək üçün icazəniz yoxdur.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Ancaq {limit, number} {limit, plural, one{fayl} other{fayl}} yükləyə bilərsiniz.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => '',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => '',\n    'in {delta, plural, =1{a month} other{# months}}' => '',\n    'in {delta, plural, =1{a second} other{# seconds}}' => '',\n    'in {delta, plural, =1{a year} other{# years}}' => '',\n    'in {delta, plural, =1{an hour} other{# hours}}' => '',\n    'just now' => '',\n    'the input value' => 'daxil olunmuş qiymət',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" artıq istifadə olunub.',\n    '{attribute} cannot be blank.' => '{attribute} boş qoyula bilməz.',\n    '{attribute} contains wrong subnet mask.' => '',\n    '{attribute} is invalid.' => '{attribute} düzgün deyil.',\n    '{attribute} is not a valid URL.' => '{attribute} düzgün URL deyil.',\n    '{attribute} is not a valid email address.' => '{attribute} düzgün e-mail deyil.',\n    '{attribute} is not in the allowed range.' => '',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} {requiredValue} olmalıdır.',\n    '{attribute} must be a number.' => '{attribute} ədəd olmalıdır.',\n    '{attribute} must be a string.' => '{attribute} simvol tipli olmalıdır.',\n    '{attribute} must be a valid IP address.' => '',\n    '{attribute} must be an IP address with specified subnet.' => '',\n    '{attribute} must be an integer.' => '{attribute} tam ədəd olmalıdır.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} ya {true} ya da {false} ola bilər.',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute}, \"{compareValueOrAttribute}\" dən böyük olmalıdır.',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute}, \"{compareValueOrAttribute}\"dən böyük və ya bərabər olmalıdır.',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute}, \"{compareValueOrAttribute}\" dən kiçik olmalıdır.',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute}, \"{compareValueOrAttribute}\"dən kiçik və ya bərabər olmalıdır.',\n    '{attribute} must be no greater than {max}.' => '{attribute} {max} dən böyük olmamalıdır.',\n    '{attribute} must be no less than {min}.' => '{attribute} {min} dən kiçik olmamalıdır.',\n    '{attribute} must not be a subnet.' => '',\n    '{attribute} must not be an IPv4 address.' => '',\n    '{attribute} must not be an IPv6 address.' => '',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute}, \"{compareValueOrAttribute}\" ilə eyni olmamalıdır',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} ən az {min, number} simvol olmalıdır.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} ən çox {max, number} simvol olmalıdır.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} {length, number} simvol olmalıdır.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '',\n    '{delta, plural, =1{1 month} other{# months}}' => '',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '',\n    '{delta, plural, =1{1 year} other{# years}}' => '',\n    '{delta, plural, =1{a day} other{# days}} ago' => '',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '',\n    '{delta, plural, =1{a month} other{# months}} ago' => '',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '',\n    '{delta, plural, =1{a year} other{# years}} ago' => '',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '',\n    '{nFormatted} KiB' => '',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} Bayt',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} Giqabayt',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} Kilobayt',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} Meqabayt',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} Petabayt',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} Terabayt',\n];\n"
  },
  {
    "path": "framework/messages/be/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => ' і ',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '\"{attribute}\" не падтрымлівае аператар \"{operator}\".',\n    '(not set)' => '(не зададзена)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Узнікла ўнутраная памылка сервера.',\n    'Are you sure you want to delete this item?' => 'Вы ўпэўнены, што жадаеце выдаліць гэты элемент?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => 'Умова для \"{attribute}\" павінна быць ці значэннем, ці дакладнай спецыфікацыяй аператара.',\n    'Delete' => 'Выдаліць',\n    'Error' => 'Памылка',\n    'File upload failed.' => 'Не атрымалася загрузіць файл.',\n    'Home' => 'Галоўная',\n    'Invalid data received for parameter \"{param}\".' => 'Несапраўдныя дадзеныя, атрыманыя для параметру \"{param}\".',\n    'Login Required' => 'Патрабуецца ўваход.',\n    'Missing required arguments: {params}' => 'Адсутнічаюць неабходныя аргументы: {params}',\n    'Missing required parameters: {params}' => 'Адсутнічаюць неабходныя параметры: {params}',\n    'No' => 'Не',\n    'No results found.' => 'Нічога не знойдзена.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Дазволена загрузка файлаў толькі з наступнымі MIME-тыпамі: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Дазволена загрузка файлаў толькі з наступнымі пашырэннямі: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => 'Аператар \"{operator}\" павінен выкарыстоўвацца праз атрыбут пошуку.',\n    'Operator \"{operator}\" requires multiple operands.' => 'Аператар \"{operator}\" патрабуе некалькі аперандаў.',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Старонка не знойдзена.',\n    'Please fix the following errors:' => 'Выпраўце наступныя памылкі:',\n    'Please upload a file.' => 'Загрузіце файл.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Паказаны запісы <b>{begin, number}-{end, number}</b> з <b>{totalCount, number}</b>.',\n    'The combination {values} of {attributes} has already been taken.' => 'Камбінацыя {values} параметраў {attributes} ужо існуе.',\n    'The file \"{file}\" is not an image.' => 'Файл «{file}» не зьяўляецца малюнкам.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'Файл «{file}» занадта вялікі. Памер не павінен перавышаць {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Файл «{file}» занадта маленькі. Памер павінен быць больш {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'Няправільны фармат значэння «{attribute}».',\n    'The format of {filter} is invalid.' => 'Фармат {filter} з\\'яўляецца несапраўдным.',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Файл «{file}» занадта вялікі. Вышыня не павінна перавышаць {limit, number} {limit, plural, one {піксель} few {пікселя} many {пікселяў} other {пікселя}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Файл «{file}» занадта вялікі. Шырыня не павінна перавышаць {limit, number} {limit, plural, one {піксель} few {пікселя} many {пікселяў} other {пікселя}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Файл «{file}» занадта маленькі. Вышыня павінна быць больш за {limit, number} {limit, plural, one {піксель} few {пікселя} many {пікселяў} other {пікселя}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Файл «{file}» занадта маленькі. Шырыня павінна быць больш за {limit, number} {limit, plural, one {піксель} few {пікселя} many {пікселяў} other {пікселя}}.',\n    'The requested view \"{name}\" was not found.' => 'Запытаны файл прадстаўлення \"{name}\" не знойдзены.',\n    'The verification code is incorrect.' => 'Праверачны код з\\'яўляецца памылковым.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Усяго <b>{count, number}</b> {count, plural, one{запіс} few{запісы} many{запісаў} other{запісу}}.',\n    'Unable to verify your data submission.' => 'Не атрымалася праверыць перададзеныя дадзеныя.',\n    'Unknown alias: -{name}' => 'Невядомы псеўданім: -{name}',\n    'Unknown filter attribute \"{attribute}\"' => 'Невядомы атрыбут фільтра \"{attribute}\"',\n    'Unknown option: --{name}' => 'Невядомая опцыя: --{name}',\n    'Update' => 'Рэдагаваць',\n    'View' => 'Прагляд',\n    'Yes' => 'Так',\n    'You are not allowed to perform this action.' => 'You are not allowed to perform this action.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Вы не можаце загружаць больш за {limit, number} {limit, plural, one{файл} few{файлы} many{файлаў} other{файла}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => 'Вы павінны загрузіць мінімум {limit, number} {limit, plural, one{файл} few{файлы} many{файлаў} other{файла}}',\n    'in {delta, plural, =1{a day} other{# days}}' => 'праз {delta, plural, =1{дзень} one{# дзень} few{# дні} many{# дзён} other{# дні}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'праз {delta, plural, =1{хвіліну} one{# хвіліну} few{# хвіліны} many{# хвілін} other{# хвіліны}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'праз {delta, plural, =1{месяц} one{# месяц} few{# месяцы} many{# месяцаў} other{# месяца}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'праз {delta, plural, =1{секунду} one{# секунду} few{# секунды} many{# секунд} other{# секунды}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'праз {delta, plural, =1{год} one{# год} few{# гады} many{# гадоў} other{# гады}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'праз {delta, plural, =1{гадзіну} one{# гадзіну} few{# гадзіны} many{# гадзінаў} other{# гадзіны}}',\n    'just now' => 'толькі што',\n    'the input value' => 'уведзенае значэнне',\n    '{attribute} \"{value}\" has already been taken.' => 'Значэнне «{value}» для «{attribute}» ужо занятае.',\n    '{attribute} cannot be blank.' => 'Неабходна запоўніць «{attribute}».',\n    '{attribute} contains wrong subnet mask.' => 'Значэнне «{attribute}» змяшчае няправільную маску падсеткі.',\n    '{attribute} is invalid.' => 'Значэнне «{attribute}» з\\'яўляецца несапраўдным.',\n    '{attribute} is not a valid URL.' => 'Значэнне «{attribute}» не зьяўляецца правільным URL.',\n    '{attribute} is not a valid email address.' => 'Значэнне «{attribute}» не зьяўляецца правільным email адрасам.',\n    '{attribute} is not in the allowed range.' => 'Значэнне «{attribute}» не ўваходзіць у спіс дазволеных дыяпазонаў адрасоў.',\n    '{attribute} must be \"{requiredValue}\".' => 'Значэнне «{attribute}» павінна быць роўна «{requiredValue}».',\n    '{attribute} must be a number.' => 'Значэнне «{attribute}» павінна быць лікам',\n    '{attribute} must be a string.' => 'Значэнне «{attribute}» павінна быць радком.',\n    '{attribute} must be a valid IP address.' => 'Значэнне «{attribute}» павінна быць правільным IP адрасам.',\n    '{attribute} must be an IP address with specified subnet.' => 'Значэнне «{attribute}» павінна быць IP адрасам з падсеткай',\n    '{attribute} must be an integer.' => 'Значэнне «{attribute}» павінна быць цэлым лікам.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => 'Значэнне «{attribute}» павінна быць роўна «{true}» або «{false}».',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => 'Значэнне «{attribute}» павінна быць роўна «{compareValueOrAttribute}».',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => 'Значэнне «{attribute}» павінна быць больш за значэнне «{compareValueOrAttribute}».',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => 'Значэнне «{attribute}» павінна быць больш за або роўна значэнню «{compareValueOrAttribute}».',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => 'Значэнне «{attribute}» павінна быць менш за значэнне «{compareValueOrAttribute}».',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => 'Значэнне «{attribute}» павінна быць менш за або роўна значэнню «{compareValueOrAttribute}».',\n    '{attribute} must be no greater than {max}.' => 'Значэнне «{attribute}» не павінна перавышаць {max}.',\n    '{attribute} must be no less than {min}.' => 'Значэнне «{attribute}» павінна быць не менш за {min}.',\n    '{attribute} must not be a subnet.' => 'Значэнне «{attribute}» не павінна быць падсеткай',\n    '{attribute} must not be an IPv4 address.' => 'Значэнне «{attribute}» не павінна быць IPv4 адрасам.',\n    '{attribute} must not be an IPv6 address.' => 'Значэнне «{attribute}» не павінна быць IPv6 адрасам.',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => 'Значэнне «{attribute}» не павінна быць роўна «{compareValueOrAttribute}».',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => 'Значэнне «{attribute}» павінна ўтрымліваць мінімум {min, number} {min, plural, one{сімвал} few{сімвала} many{сімвалаў} other{сімвала}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => 'Значэнне «{attribute}» павінна ўтрымліваць максімум {max, number} {max, plural, one{сімвал} few{сімвала} many{сімвалаў} other{сімвала}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => 'Значэнне «{attribute}» павінна ўтрымліваць {length, number} {length, plural, one{сімвал} few{сімвала} many{сімвалаў} other{сімвала}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, one{# дзень} few{# дні} many{# дзён} other{# дні}}',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, one{# гадзіна} few{# гадзіны} many{# гадзін} other{# гадзіны}}',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, one{# хвіліна} few{# хвіліны} many{# хвілін} other{# хвіліны}}',\n    '{delta, plural, =1{1 month} other{# months}}' => '{delta, plural, one{# месяц} few{# месяцы} many{# месяцаў} other{# месяца}}',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta, plural, one{# секунда} few{# секунды} many{# секунд} other{# секунды}}',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta, plural, one{# год} few{# гады} many{# год} other{# гады}}',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{дзень} one{# дзень} few{# дні} many{# дзён} other{# дні}} назад',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{хвіліну} one{# хвіліну} few{# хвіліны} many{# хвілін} other{# хвіліны}} таму',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{месяц} one{# месяц} few{# месяцы} many{# месяцаў} other{# месяца}} таму',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{секунду} one{# секунду} few{# секунды} many{# секунд} other{# секунды}} таму',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{год} one{# год} few{# гады} many{# год} other{# гады}} таму',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, =1{гадзіна} one{# гадзіна} few{# гадзіны} many{# гадзін} other{# гадзіны}} таму',\n    '{nFormatted} B' => '{nFormatted} Б',\n    '{nFormatted} GB' => '{nFormatted} ГБ',\n    '{nFormatted} GiB' => '{nFormatted} ГіБ',\n    '{nFormatted} KiB' => '{nFormatted} КіБ',\n    '{nFormatted} MB' => '{nFormatted} МБ',\n    '{nFormatted} MiB' => '{nFormatted} МіБ',\n    '{nFormatted} PB' => '{nFormatted} ПБ',\n    '{nFormatted} PiB' => '{nFormatted} ПіБ',\n    '{nFormatted} TB' => '{nFormatted} ТБ',\n    '{nFormatted} TiB' => '{nFormatted} ЦіБ',\n    '{nFormatted} kB' => '{nFormatted} КБ',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, one{байт} few{байта} many{байтаў} other{байта}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, one{гібібайт} few{гібібайта} many{гібібайтаў} other{гібібайта}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, one{гігабайт} few{гігабайта} many{гігабайтаў} other{гігабайта}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, one{кібібайт} few{кібібайта} many{кібібайтаў} other{кібібайта}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, one{кілабайт} few{кілабайта} many{кілабайтаў} other{кілабайта}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, one{мэбібайт} few{мэбібайта} many{мэбібайтаў} other{мэбібайта}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, one{мегабайт} few{мегабайта} many{мегабайтаў} other{мегабайта}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, one{пэбібайт} few{пэбібайта} many{пэбібайтаў} other{пэбібайта}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, one{петабайт} few{петабайта} many{петабайтаў} other{петабайта}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, one{тэбібайт} few{тэбібайта} many{тэбібайтаў} other{тэбібайта}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, one{тэрабайт} few{тэрабайта} many{тэрабайтаў} other{тэрабайта}}',\n];\n"
  },
  {
    "path": "framework/messages/bg/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => ' и ',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '\"{attribute}\" не поддържа оператор \"{operator}\".',\n    '(not set)' => '(не е попълнено)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Възникна вътрешна грешка в сървъра.',\n    'Are you sure you want to delete this item?' => 'Сигурни ли сте, че искате да изтриете записа?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => 'Условието за \"{attribute}\" трябва да е валидна стойност или оператор.',\n    'Delete' => 'Изтрий',\n    'Error' => 'Грешка',\n    'File upload failed.' => 'Грешка при качване на файл.',\n    'Home' => 'Начало',\n    'Invalid data received for parameter \"{param}\".' => 'Невалидни данни за параметъра \"{param}\".',\n    'Login Required' => 'Трябва да влезете в системата.',\n    'Missing required arguments: {params}' => 'Липсват задължителните аргументи: {params}',\n    'Missing required parameters: {params}' => 'Липсват задължителните параметри: {params}',\n    'No' => 'Не',\n    'No results found.' => 'Няма намерени резултати.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Допускат се файлове са от типове: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Допускат се файлове със следните разширения: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => 'Операторът \"{operator}\" трябва да се използва с атрибут за търсене.',\n    'Operator \"{operator}\" requires multiple operands.' => 'Операторът \"{operator}\" изисква множество елементи.',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Страницата не беше намерена.',\n    'Please fix the following errors:' => 'Моля, коригирайте следните грешки:',\n    'Please upload a file.' => 'Моля, прикачете файл.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Показване на <b>{begin, number}-{end, number}</b> от <b>{totalCount, number}</b> {totalCount, plural, one{запис} other{записа}}.',\n    'The combination {values} of {attributes} has already been taken.' => 'Комбинацията от {values} от {attributes} е вече заета.',\n    'The file \"{file}\" is not an image.' => 'Файлът \"{file}\" не е изображение.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'Фйлът \"{file}\" е твърде голям. Размерът на файла не трябва да превишава {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Файлът \"{file}\" е твърде малък. Размерът на файла трябва да е поне {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'Невалиден формат за \"{attribute}\".',\n    'The format of {filter} is invalid.' => 'Форматът на филтъра {filter} е невалиден.',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Изображението \"{file}\" е твърде голямо. Височината не трябва да е по-голяма от {limit, number} {limit, plural, one{пиксел} other{пиксела}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Изображението \"{file}\" е твърде голямо. Широчината не трябва да е по-голяма от {limit, number} {limit, plural, one{пиксел} other{пиксела}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Изображението \"{file}\" е твърде малко. Височината трябва да е поне {limit, number} {limit, plural, one{пиксел} other{пиксела}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Изображението \"{file}\" е твърде малко. Широчината трябва да е поне {limit, number} {limit, plural, one{пиксел} other{пиксела}}.',\n    'The requested view \"{name}\" was not found.' => 'Завения view файл \"{name}\" не беше открит',\n    'The verification code is incorrect.' => 'Неправилен код за проверка.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => '<b>{count, number}</b> {count, plural, one{запис} other{записа}}.',\n    'Unable to verify your data submission.' => 'Не може да се валидират подадените данни.',\n    'Unknown alias: -{name}' => 'Неизвестен псевдоним: -{name}',\n    'Unknown filter attribute \"{attribute}\"' => 'Неизвестен филтърен атрибут \"{attribute}\"',\n    'Unknown option: --{name}' => 'Несъществуваща опция : --{name}',\n    'Update' => 'Обнови',\n    'View' => 'Виж',\n    'Yes' => 'Да',\n    'You are not allowed to perform this action.' => 'Нямате права да изпълните тази операция.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Може да прикачите най-много {limit, number} {limit, plural, one{файл} other{файла}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => 'Трябва да качите поне {limit, number} {limit, plural, one{файл} other{файлове}}.',\n    'in {delta, plural, =1{a day} other{# days}}' => 'след {delta, plural, =1{ден} other{# дни}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'след {delta, plural, =1{минута} other{# минути}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'след {delta, plural, =1{месец} other{# месеца}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'след {delta, plural, =1{секунда} other{# секунди}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'след {delta, plural, =1{година} other{# години}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'след {delta, plural, =1{час} other{# часа}}',\n    'just now' => 'току що',\n    'the input value' => 'стойността',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" вече е зает.',\n    '{attribute} cannot be blank.' => 'Попълнете полето \"{attribute}\".',\n    '{attribute} contains wrong subnet mask.' => '{attribute} съдържа грешен subnet mask.',\n    '{attribute} is invalid.' => 'Полето \"{attribute}\" е некоректно попълнено.',\n    '{attribute} is not a valid URL.' => 'Полето \"{attribute}\" съдържа невалиден УЕБ адрес.',\n    '{attribute} is not a valid email address.' => 'Полето \"{attribute}\" съдържа невалиден email адрес.',\n    '{attribute} is not in the allowed range.' => '{attribute} не е в позволения интервал.',\n    '{attribute} must be \"{requiredValue}\".' => 'Полето \"{attribute}\" трябва да съдържа \"{requiredValue}\".',\n    '{attribute} must be a number.' => 'Полето \"{attribute}\" съдържа невалиден номер.',\n    '{attribute} must be a string.' => 'Полето \"{attribute}\" трябва да съдържа текст.',\n    '{attribute} must be a valid IP address.' => '{attribute} трябва да е валиден IP адрес.',\n    '{attribute} must be an IP address with specified subnet.' => '{attribute} трябва да бъде IP адрес в позволения subnet интервал.',\n    '{attribute} must be an integer.' => 'Полето \"{attribute}\" трябва да съдържа цяло число.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => 'Полето \"{attribute}\"  трябва да бъде \"{true}\" или \"{false}\".',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '\"{attribute}\" трябва да е равно на \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} трябва да е по-голямо от \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} трябва да е по-голямо или равно на \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} трябва да е по-малко от \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} трябва да е по-малко или равно на \"{compareValueOrAttribute}\".',\n    '{attribute} must be no greater than {max}.' => 'Полето \"{attribute}\" не трябва да е по-голямо от {max}.',\n    '{attribute} must be no less than {min}.' => 'Полето \"{attribute}\" не трябва да е по-малко от {min}.',\n    '{attribute} must not be a subnet.' => '{attribute} не трябва да бъде subnet.',\n    '{attribute} must not be an IPv4 address.' => '{attribute} не трябва да бъде IPv4 адрес.',\n    '{attribute} must not be an IPv6 address.' => '{attribute} не трябва дабъде IPv6 адрес.',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} не трябва да бъде равно на \"{compareValueOrAttribute}\".',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => 'Полето {attribute} трябва да съдържа поне {min, number} {min, plural, one{символ} other{символа}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => 'Полето \"{attribute}\" трябва да съдържа най-много {max, number} {max, plural, one{символ} other{символа}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => 'Полето \"{attribute}\" трябва да съдържа точно {length, number} {length, plural, one{символ} other{символа}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, =1{1 ден} other{# дни}}',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, =1{1 час} other{# часа}}',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, =1{1 минута} other{# минути}}',\n    '{delta, plural, =1{1 month} other{# months}}' => '{delta, plural, =1{1 месец} other{# месеца}}',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta, plural, =1{1 секунда} other{# секунди}}',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta, plural, =1{1 година} other{# години}}',\n    '{delta, plural, =1{a day} other{# days}} ago' => 'преди {delta, plural, =1{ден} other{# дни}}',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => 'преди {delta, plural, =1{минута} other{# минути}}',\n    '{delta, plural, =1{a month} other{# months}} ago' => 'преди {delta, plural, =1{месец} other{# месеца}}',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => 'преди {delta, plural, =1{секунда} other{# секунди}}',\n    '{delta, plural, =1{a year} other{# years}} ago' => 'преди {delta, plural, =1{година} other{# години}}',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => 'преди {delta, plural, =1{час} other{# часа}}',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '{nFormatted} GiB',\n    '{nFormatted} KiB' => '{nFormatted} KiB',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '{nFormatted} MiB',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} KB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, =1{байта} other{байта}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, =1{гибибайт} other{гибибайта}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, =1{гигабайт} other{гигабайта}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, =1{кибибайт} other{кибибайта}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, =1{килобайт} other{килобайта}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, =1{мебибайт} other{мебибайта}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, =1{мегабайт} other{мегабайта}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, =1{пебибайт} other{пебибайта}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, =1{петабайт} other{петабайта}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, =1{тебибайт} other{тебибайта}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, =1{терабайт} other{терабайта}}',\n];\n"
  },
  {
    "path": "framework/messages/bs/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => '',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(bez vrijednosti)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Došlo je do interne greške na serveru.',\n    'Are you sure you want to delete this item?' => 'Jeste li sigurni da želite obrisati ovu stavku?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'Obriši',\n    'Error' => 'Greška',\n    'File upload failed.' => 'Slanje datoteke nije uspjelo.',\n    'Home' => 'Početna',\n    'Invalid data received for parameter \"{param}\".' => 'Neispravan podatak dobijen u parametru \"{param}\"',\n    'Login Required' => 'Prijava je obavezna',\n    'Missing required arguments: {params}' => 'Nedostaju obavezni argumenti: {params}',\n    'Missing required parameters: {params}' => 'Nedostaju obavezni parametri: {params}',\n    'No' => 'Ne',\n    'No results found.' => 'Nema rezultata.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Samo datoteke sa sljedećim MIME tipovima su dozvoljeni: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Samo datoteke sa sljedećim ekstenzijama su dozvoljeni: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Stranica nije pronađena.',\n    'Please fix the following errors:' => 'Molimo ispravite sljedeće greške:',\n    'Please upload a file.' => 'Molimo da pošaljete datoteku.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Prikazano <b>{begin, number}-{end, number}</b> od <b>{totalCount, number}</b> {totalCount, plural, one{stavke} other{stavki}}.',\n    'The combination {values} of {attributes} has already been taken.' => '',\n    'The file \"{file}\" is not an image.' => 'Datoteka \"{file}\" nije slika.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'Datoteka \"{file}\" je prevelika. Veličina ne smije biti veća od {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Datoteka \"{file}\" је premala. Veličina ne smije biti manja od {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'Format atributa \"{attribute}\" je neispravan.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Slika \"{file}\" je prevelika. Visina ne smije biti veća od {limit, number} {limit, plural, one{piksel} other{piksela}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Slika \"{file}\" je prevelika. Širina ne smije biti veća od {limit, number} {limit, plural, one{piksel} other{piksela}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Slika \"{file}\" je premala. Visina ne smije biti manja od {limit, number} {limit, plural, one{piksel} other{piksela}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Slika \"{file}\" je premala. Širina ne smije biti manja od {limit, number} {limit, plural, one{piksel} other{piksela}}.',\n    'The requested view \"{name}\" was not found.' => 'Stranica \"{name} nije pronađena.\"',\n    'The verification code is incorrect.' => 'Potvrdni kod nije ispravan.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Ukupno <b>{count, number}</b> {count, plural, one{stavka} other{stavki}}.',\n    'Unable to verify your data submission.' => 'Nije moguće provjeriti poslane podatke.',\n    'Unknown alias: -{name}' => '',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'Nepoznata opcija: --{name}',\n    'Update' => 'Ažurirati',\n    'View' => 'Pregled',\n    'Yes' => 'Da',\n    'You are not allowed to perform this action.' => 'Nemate prava da izvršite ovu akciju.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Možete poslati najviše {limit, number} {limit, plural, one{datoteku} other{datoteka}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => 'za {delta, plural, =1{dan} one{# dan} few{# dana} many{# dana} other{# dana}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'za {delta, plural, =1{minut} one{# minut} few{# minuta} many{# minuta} other{# minuta}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'za {delta, plural, =1{mjesec} one{# mjesec} few{# mjeseci} many{# mjeseci} other{# mjeseci}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'za {delta, plural, =1{sekundu} one{# sekundu} few{# sekundi} many{# sekundi} other{# sekundi}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'za {delta, plural, =1{godinu} one{# godinu} few{# godini} many{# godina} other{# godina}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'za {delta, plural, =1{sat} one{# sat} few{# sati} many{# sati} other{# sati}}',\n    'just now' => 'upravo sada',\n    'the input value' => 'ulazna vrijednost',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" je već zauzet.',\n    '{attribute} cannot be blank.' => '{attribute} ne smije biti prazan.',\n    '{attribute} contains wrong subnet mask.' => '',\n    '{attribute} is invalid.' => '{attribute} je neispravan.',\n    '{attribute} is not a valid URL.' => '{attribute} ne sadrži ispravan URL.',\n    '{attribute} is not a valid email address.' => '{attribute} ne sadrži ispravnu email adresu.',\n    '{attribute} is not in the allowed range.' => '',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} mora biti \"{requiredValue}\".',\n    '{attribute} must be a number.' => '{attribute} mora biti broj.',\n    '{attribute} must be a string.' => '{attribute} mora biti tekst.',\n    '{attribute} must be a valid IP address.' => '',\n    '{attribute} must be an IP address with specified subnet.' => '',\n    '{attribute} must be an integer.' => '{attribute} mora biti cijeli broj.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} mora biti \"{true}\" ili \"{false}\".',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} mora biti veći od \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} mora biti veći ili jednak od \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} mora biti manji od \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} mora biti manji ili jednak od \"{compareValueOrAttribute}\".',\n    '{attribute} must be no greater than {max}.' => '{attribute} ne smije biti veći od \"{max}\"',\n    '{attribute} must be no less than {min}.' => '{attribute} ne smije biti manji od {min}.',\n    '{attribute} must not be a subnet.' => '',\n    '{attribute} must not be an IPv4 address.' => '',\n    '{attribute} must not be an IPv6 address.' => '',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} ne smije biti jednak\"{compareValueOrAttribute}\".',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} treba sadržavati najmanje {min, number} {min, plural, one{znak} other{znakova}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} treba sadržavati najviše {max, number} {max, plural, one{znak} other{znakova}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} treba sadržavati {length, number} {length, plural, one{znak} other{znakova}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '',\n    '{delta, plural, =1{1 month} other{# months}}' => '',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '',\n    '{delta, plural, =1{1 year} other{# years}}' => '',\n    '{delta, plural, =1{a day} other{# days}} ago' => 'prije {delta, plural, =1{dan} one{# dan} few{# dana} many{# dana} other{# dana}}',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => 'prije {delta, plural, =1{minut} one{# minut} few{# minuta} many{# minuta} other{# minuta}}',\n    '{delta, plural, =1{a month} other{# months}} ago' => 'prije {delta, plural, =1{mjesec} one{# mjesec} few{# mjeseci} many{# mjeseci} other{# mjeseci}}',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => 'prije {delta, plural, =1{sekundu} one{# sekundu} few{# sekundi} many{# sekundi} other{# sekundi}}',\n    '{delta, plural, =1{a year} other{# years}} ago' => 'prije {delta, plural, =1{godinu} one{# godinu} few{# godina} many{# godina} other{# godina}}',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => 'prije {delta, plural, =1{sat} one{# sat} few{# sati} many{# sati} other{# sati}}',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '{nFormatted} GiB',\n    '{nFormatted} KiB' => '{nFormatted} KiB',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '{nFormatted} MiB',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, =1{bajt} other{bajtova}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, =1{gibibajt} other{gibibajta}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, =1{gigabajt} other{gigabajta}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, =1{kibibajt} other{kibibajta}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, =1{kilobajt} other{kilobajta}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, =1{mebibajt} other{mebibajta}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, =1{megabajt} other{megabajta}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, =1{pebibajt} other{pebibajta}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, =1{petabajt} other{petabajta}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, =1{tebibajt} other{tebibajta}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, =1{terabajt} other{terabajta}}',\n];\n"
  },
  {
    "path": "framework/messages/ca/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => '',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(no establert)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'S\\'ha produït un error intern al servidor.',\n    'Are you sure you want to delete this item?' => 'Estas segur que vols eliminar aquest element?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'Eliminar',\n    'Error' => 'Error',\n    'File upload failed.' => 'Ha fallat la pujada del fitxer.',\n    'Home' => 'Inici',\n    'Invalid data received for parameter \"{param}\".' => 'S\\'han rebut dades errònies pel paràmetre \"{param}\"',\n    'Login Required' => 'Login Requerit',\n    'Missing required arguments: {params}' => 'Falten arguments requerits: {params}',\n    'Missing required parameters: {params}' => 'Falten paràmetres requerits: {params}',\n    'No' => 'No',\n    'No results found.' => 'No s\\'han trobat resultats.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Només s\\'accepten arxius amb els següents tipus MIME: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Només s\\'accepten arxius amb les seguents extensions: {extensions}',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'No s\\'ha trobat la pàgina.',\n    'Please fix the following errors:' => 'Si us plau corregeix els següents errors:',\n    'Please upload a file.' => 'Si us plau puja un arxiu.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Mostrant  <b>{begin, number}-{end, number}</b> de <b>{totalCount, number}</b> {totalCount, plural, one{element} other{elements}}.',\n    'The combination {values} of {attributes} has already been taken.' => '',\n    'The file \"{file}\" is not an image.' => 'L\\'arxiu \"{file}\" no és una imatge.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'L\\'arxiu \"{file}\" és massa gran. El seu tamany no pot excedir {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'L\\'arxiu \"{file}\" és massa petit. El seu tamany no pot ser menor que {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'El format de {attribute} és invalid.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'La imatge \"{file}\" és massa gran. L\\'altura no pot ser major que {limit, number} {limit, plural, one{píxel} other{píxels}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'La imatge \"{file}\" és massa gran. L\\'amplada no pot ser major que  {limit, number} {limit, plural, one{píxel} other{píxels}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'La imatge  \"{file}\" és massa petita. L\\'altura no pot ser menor que {limit, number} {limit, plural, one{píxel} other{píxels}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'La imatge \"{file}\" és massa petita. L\\'amplada no pot ser menor que {limit, number} {limit, plural, one{píxel} other{píxels}}.',\n    'The requested view \"{name}\" was not found.' => '',\n    'The verification code is incorrect.' => 'El codi de verificació és incorrecte.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Total <b>{count, number}</b> {count, plural, one{element} other{elements}}.',\n    'Unable to verify your data submission.' => 'No s\\'ha pogut verificar les dades enviades.',\n    'Unknown alias: -{name}' => '',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'Opció desconeguda: --{name}',\n    'Update' => 'Actualitzar',\n    'View' => 'Veure',\n    'Yes' => 'Sí',\n    'You are not allowed to perform this action.' => 'No tems permís per executar aquesta acció.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Pots pujar com a màxim {limit, number} {limit, plural, one{arxiu} other{arxius}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => 'en {delta, plural, =1{un dia} other{# dies}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'en {delta, plural, =1{un minut} other{# minuts}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'en {delta, plural, =1{un mes} other{# mesos}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'en {delta, plural, =1{un segon} other{# segons}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'en {delta, plural, =1{un any} other{# anys}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'en {delta, plural, =1{una hora} other{# hores}}',\n    'just now' => '',\n    'the input value' => 'el valor d\\'entrada',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" ja ha sigut utilitzat.',\n    '{attribute} cannot be blank.' => '{attribute} no pot estar buit.',\n    '{attribute} contains wrong subnet mask.' => '',\n    '{attribute} is invalid.' => '{attribute} és invalid.',\n    '{attribute} is not a valid URL.' => '{attribute} no és una URL valida.',\n    '{attribute} is not a valid email address.' => '{attribute} no es una direcció de correu valida.',\n    '{attribute} is not in the allowed range.' => '',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} ha de ser \"{requiredValue}\".',\n    '{attribute} must be a number.' => '{attribute} ha de  ser un nombre.',\n    '{attribute} must be a string.' => '{attribute} ha de ser una cadena de caràcters.',\n    '{attribute} must be a valid IP address.' => '',\n    '{attribute} must be an IP address with specified subnet.' => '',\n    '{attribute} must be an integer.' => '{attribute} ha de ser un nombre enter.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} ha de ser \"{true}\" o \"{false}\".',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} ha de ser major que \"{compareValueOrAttribute}',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} ha de ser major o igual que \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} ha de ser menor que \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} ha de ser menor o igual que \"{compareValueOrAttribute}\".',\n    '{attribute} must be no greater than {max}.' => '{attribute} no pot ser major que {max}.',\n    '{attribute} must be no less than {min}.' => '{attribute} no pot ser menor que {min}.',\n    '{attribute} must not be a subnet.' => '',\n    '{attribute} must not be an IPv4 address.' => '',\n    '{attribute} must not be an IPv6 address.' => '',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} no pot ser igual que \"{compareValueOrAttribute}\".',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} hauria de contenir com a mínim {min, number} {min, plural, one{lletra} other{lletres}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} hauria de contenir com a màxim {max, number} {max, plural, one{lletra} other{lletres}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} hauria contenir {length, number} {length, plural, one{lletra} other{lletres}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '',\n    '{delta, plural, =1{1 month} other{# months}}' => '',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '',\n    '{delta, plural, =1{1 year} other{# years}}' => '',\n    '{delta, plural, =1{a day} other{# days}} ago' => 'hace {delta, plural, =1{un dia} other{# dies}}',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => 'fa {delta, plural, =1{un minut} other{# minuts}}',\n    '{delta, plural, =1{a month} other{# months}} ago' => 'fa {delta, plural, =1{un mes} other{# mesos}}',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => 'fa {delta, plural, =1{un segon} other{# segons}}',\n    '{delta, plural, =1{a year} other{# years}} ago' => 'fa {delta, plural, =1{un any} other{# anys}}',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => 'fa {delta, plural, =1{una hora} other{# hores}}',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '',\n    '{nFormatted} KiB' => '',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, =1{byte} other{bytes}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}',\n];\n"
  },
  {
    "path": "framework/messages/config.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nreturn [\n    // string, required, root directory of all source files\n    'sourcePath' => __DIR__ . '/..',\n    // string, required, root directory containing message translations.\n    'messagePath' => __DIR__,\n    // array, required, list of language codes that the extracted messages\n    // should be translated to. For example, ['zh-CN', 'de'].\n    'languages' => [\n        'af', 'ar', 'az', 'be', 'bg', 'bs', 'ca', 'cs', 'da', 'de', 'el', 'es', 'et', 'fa', 'fi', 'fr', 'ga', 'he',\n        'hi', 'pt-BR', 'ro', 'hr', 'hu', 'hy', 'id', 'it', 'ja', 'ka', 'kk', 'ko', 'kz', 'lt', 'lv', 'ms', 'mt',\n        'nb-NO', 'nl', 'pl', 'pt', 'ru', 'sk', 'sl', 'sr', 'sr-Latn', 'sv', 'tg', 'th', 'tr', 'uk', 'uz', 'uz-Cy', 'vi',\n        'zh', 'zh-TW'\n    ],\n    // string, the name of the function for translating messages.\n    // Defaults to 'Yii::t'. This is used as a mark to find the messages to be\n    // translated. You may use a string for single function name or an array for\n    // multiple function names.\n    'translator' => ['\\Yii::t', 'Yii::t'],\n    // boolean, whether to sort messages by keys when merging new messages\n    // with the existing ones. Defaults to false, which means the new (untranslated)\n    // messages will be separated from the old (translated) ones.\n    'sort' => true,\n    // boolean, whether the message file should be overwritten with the merged messages\n    'overwrite' => true,\n    // boolean, whether to remove messages that no longer appear in the source code.\n    // Defaults to false, which means each of these messages will be enclosed with a pair of '@@' marks.\n    'removeUnused' => true,\n    // boolean, whether to mark messages that no longer appear in the source code.\n    // Defaults to true, which means each of these messages will be enclosed with a pair of '@@' marks.\n    'markUnused' => true,\n    // array, list of patterns that specify which files/directories should NOT be processed.\n    // If empty or not set, all files/directories will be processed.\n    // A path matches a pattern if it contains the pattern string at its end. For example,\n    // '/a/b' will match all files and directories ending with '/a/b';\n    // the '*.svn' will match all files and directories whose name ends with '.svn'.\n    // and the '.svn' will match all files and directories named exactly '.svn'.\n    // Note, the '/' characters in a pattern matches both '/' and '\\'.\n    // See helpers/FileHelper::findFiles() description for more details on pattern matching rules.\n    'except' => [\n        '.*',\n        '/.*',\n        '/messages',\n        '/tests',\n        '/runtime',\n        '/vendor',\n        '/BaseYii.php',\n    ],\n    // array, list of patterns that specify which files (not directories) should be processed.\n    // If empty or not set, all files will be processed.\n    // Please refer to \"except\" for details about the patterns.\n    // If a file/directory matches both a pattern in \"only\" and \"except\", it will NOT be processed.\n    'only' => ['*.php'],\n    'phpFileHeader' => '/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n',\n    // Generated file format. Can be \"php\", \"db\" or \"po\".\n    'format' => 'php',\n    // Connection component ID for \"db\" format.\n    //'db' => 'db',\n];\n"
  },
  {
    "path": "framework/messages/cs/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => ' a ',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(není zadáno)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Vyskytla se vnitřní chyba serveru.',\n    'Are you sure you want to delete this item?' => 'Opravdu chcete smazat tuto položku?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'Smazat',\n    'Error' => 'Chyba',\n    'File upload failed.' => 'Nepodařilo se nahrát soubor.',\n    'Home' => 'Úvod',\n    'Invalid data received for parameter \"{param}\".' => 'Přijata neplatná data pro parametr \"{param}\".',\n    'Login Required' => 'Je zapotřebí se přihlásit.',\n    'Missing required arguments: {params}' => 'Chybí povinné argumenty: {params}',\n    'Missing required parameters: {params}' => 'Chybí povinné parametry: {params}',\n    'No' => 'Ne',\n    'No results found.' => 'Nenalezeny žádné záznamy.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Povolené jsou pouze soubory následujících MIME typů: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Povolené jsou pouze soubory s následujícími příponami: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Stránka nenalezena.',\n    'Please fix the following errors:' => 'Opravte prosím následující chyby:',\n    'Please upload a file.' => 'Nahrajte prosím soubor.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => '{totalCount, plural, one{Zobrazen} few{Zobrazeny} other{Zobrazeno}} <b>{totalCount, plural, one{{begin, number}} other{{begin, number}-{end, number}}}</b> z <b>{totalCount, number}</b> {totalCount, plural, one{záznamu} other{záznamů}}.',\n    'The combination {values} of {attributes} has already been taken.' => 'Kombinace {values} pro {attributes} je již použitá.',\n    'The file \"{file}\" is not an image.' => 'Soubor \"{file}\" není obrázek.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'Soubor \"{file}\" je příliš velký. Velikost souboru nesmí přesáhnout {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Soubor \"{file}\" je příliš malý. Velikost souboru nesmí být méně než {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'Formát údaje {attribute} je neplatný.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Obrázek \"{file}\" je příliš velký. Výška nesmí přesáhnout {limit, number} {limit, plural, one{pixel} few{pixely} other{pixelů}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Obrázek \"{file}\" je příliš velký. Šířka nesmí přesáhnout {limit, number} {limit, plural, one{pixel} few{pixely} other{pixelů}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Obrázek \"{file}\" je příliš malý. Výška nesmí být méně než {limit, number} {limit, plural, one{pixel} few{pixely} other{pixelů}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Obrázek \"{file}\" je příliš malý. Šířka nesmí být méně než {limit, number} {limit, plural, one{pixel} few{pixely} other{pixelů}}.',\n    'The requested view \"{name}\" was not found.' => 'Nebyl nalezen požadovaný náhled \"{name}\".',\n    'The verification code is incorrect.' => 'Nesprávný ověřovací kód.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Celkem <b>{count, number}</b> {count, plural, one{záznam} few{záznamy} other{záznamů}}.',\n    'Unable to verify your data submission.' => 'Nebylo možné ověřit odeslané údaje.',\n    'Unknown alias: -{name}' => 'Neznámý alias: -{name}',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'Neznámá volba: --{name}',\n    'Update' => 'Upravit',\n    'View' => 'Náhled',\n    'Yes' => 'Ano',\n    'You are not allowed to perform this action.' => 'Nemáte oprávnění pro požadovanou akci.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Nahrát můžete nanejvýš {limit, number} {limit, plural, one{soubor} few{soubory} other{souborů}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => 'za {delta, plural, =1{den} few{# dny} other{# dnů}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'za {delta, plural, =1{minutu} few{# minuty} other{# minut}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'za {delta, plural, =1{měsíc} few{# měsíce} other{# měsíců}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'za {delta, plural, =1{sekundu} few{# sekundy} other{# sekund}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'za {delta, plural, =1{rok} few{# roky} other{# let}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'za {delta, plural, =1{hodinu} few{# hodiny} other{# hodin}}',\n    'just now' => 'právě teď',\n    'the input value' => 'vstupní hodnota',\n    '{attribute} \"{value}\" has already been taken.' => 'Hodnota \"{value}\" pro údaj {attribute} již byla dříve použita.',\n    '{attribute} cannot be blank.' => 'Je zapotřebí vyplnit {attribute}.',\n    '{attribute} contains wrong subnet mask.' => '{attribute} obsahuje neplatnou masku podsítě.',\n    '{attribute} is invalid.' => 'Neplatná hodnota pro {attribute}.',\n    '{attribute} is not a valid URL.' => '{attribute} není platná URL.',\n    '{attribute} is not a valid email address.' => 'Pro {attribute} nebyla použita platná emailová adresa.',\n    '{attribute} is not in the allowed range.' => '{attribute} není v povoleném rozsahu.',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} musí být \"{requiredValue}\".',\n    '{attribute} must be a number.' => '{attribute} musí být číslo.',\n    '{attribute} must be a string.' => '{attribute} musí být řetězec.',\n    '{attribute} must be a valid IP address.' => '{attribute} musí být platná IP adresa.',\n    '{attribute} must be an IP address with specified subnet.' => '{attribute} musí být IP adresa se zadanou podsítí.',\n    '{attribute} must be an integer.' => '{attribute} musí být celé číslo.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} musí být buď \"{true}\" nebo \"{false}\".',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '{attribute} se musí rovnat \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} musí být větší než \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} musí být větší nebo roven \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} musí být menší než \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} musí být menší nebo roven \"{compareValueOrAttribute}\".',\n    '{attribute} must be no greater than {max}.' => '{attribute} nesmí být větší než {max}.',\n    '{attribute} must be no less than {min}.' => '{attribute} nesmí být menší než {min}.',\n    '{attribute} must not be a subnet.' => '{attribute} nesmí být podsíť.',\n    '{attribute} must not be an IPv4 address.' => '{attribute} nesmí být IPv4 adresa.',\n    '{attribute} must not be an IPv6 address.' => '{attribute} nesmí být IPv6 adresa.',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} se nesmí rovnat \"{compareValueOrAttribute}\".',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} musí obsahovat alespoň {min, number} {min, plural, one{znak} few{znaky} other{znaků}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} může obsahovat nanejvýš {max, number} {max, plural, one{znak} few{znaky} other{znaků}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute}  musí obsahovat {length, number} {length, plural, one{znak} few{znaky} other{znaků}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, =1{1 den} few{# dny} other{# dní}}',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, =1{1 hodina} few{# hodiny} other{# hodin}}',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, =1{1 minuta} few{# minuty} other{# minut}}',\n    '{delta, plural, =1{1 month} other{# months}}' => '{delta, plural, =1{1 měsíc} few{# měsíce} other{# měsíců}}',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta, plural, =1{1 sekunda} few{# sekundy} other{# sekund}}',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta, plural, =1{1 rok} few{# roky} other{# let}}',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{včera} other{před # dny}}',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => 'před {delta, plural, =1{minutou} other{# minutami}}',\n    '{delta, plural, =1{a month} other{# months}} ago' => 'před {delta, plural, =1{měsícem} other{# měsíci}}',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => 'před {delta, plural, =1{sekundou} other{# sekundami}}',\n    '{delta, plural, =1{a year} other{# years}} ago' => 'před {delta, plural, =1{rokem} other{# lety}}',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => 'před {delta, plural, =1{hodinou} other{# hodinami}}',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '{nFormatted} GiB',\n    '{nFormatted} KiB' => '{nFormatted} KiB',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '{nFormatted} MiB',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, =1{byte} few{byty} other{bytů}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, =1{gibibyte} few{gibibyty} other{gibibytů}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, =1{gigabyte} few{gigabyty} other{gigabytů}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, =1{kibibyte} few{kibibyty} other{kibibytů}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, =1{kilobyte} few{kilobyty} other{kilobytů}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, =1{mebibyte} few{mebibyty} other{mebibytů}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, =1{megabyte} few{megabyty} other{megabytů}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, =1{pebibyte} few{pebibyty} other{pebibytů}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, =1{petabyte} few{petabyty} other{petabytů}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, =1{tebibyte} few{tebibyty} other{tebibytů}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, =1{terabyte} few{terabyty} other{terabytů}}',\n];\n"
  },
  {
    "path": "framework/messages/da/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => '',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(ikke defineret)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Der opstod en intern server fejl.',\n    'Are you sure you want to delete this item?' => 'Er du sikker på, at du vil slette dette element?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'Slet',\n    'Error' => 'Fejl',\n    'File upload failed.' => 'Upload af fil fejlede.',\n    'Home' => 'Start',\n    'Invalid data received for parameter \"{param}\".' => 'Ugyldig data modtaget for parameteren \"{param}\".',\n    'Login Required' => 'Login Påkrævet',\n    'Missing required arguments: {params}' => 'Påkrævede argumenter mangler: {params}',\n    'Missing required parameters: {params}' => 'Påkrævede parametre mangler: {params}',\n    'No' => 'Nej',\n    'No results found.' => 'Ingen resultater fundet.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Kun filer med følgende MIME-typer er tilladte: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Kun filer med følgende filtyper er tilladte: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Siden blev ikke fundet.',\n    'Please fix the following errors:' => 'Ret venligst følgende fejl:',\n    'Please upload a file.' => 'Venligst upload en fil.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Viser <b>{begin, number}-{end, number}</b> af <b>{totalCount, number}</b> {totalCount, plural, one{element} other{elementer}}.',\n    'The combination {values} of {attributes} has already been taken.' => '',\n    'The file \"{file}\" is not an image.' => 'Filen \"{file}\" er ikke et billede.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'Filen \"{file}\" er for stor. Størrelsen må ikke overstige {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Filen \"{file}\" er for lille. Størrelsen må ikke være mindre end {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'Formatet af {attribute} er ugyldigt.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Billedet \"{file}\" er for stort. Højden må ikke være større end {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Billedet \"{file}\" er for stort. Bredden må ikke være større end {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Billedet \"{file}\" er for lille. Højden må ikke være mindre end {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Billedet \"{file}\" er for lille. Bredden må ikke være mindre end {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The requested view \"{name}\" was not found.' => 'Den ønskede visning \"{name}\" blev ikke fundet.',\n    'The verification code is incorrect.' => 'Verifikationskoden er ikke korrekt.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Total <b>{count, number}</b> {count, plural, one{element} other{elementer}}.',\n    'Unable to verify your data submission.' => 'Kunne ikke verificere din data indsendelse.',\n    'Unknown alias: -{name}' => '',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'Ukendt option: --{name}',\n    'Update' => 'Opdatér',\n    'View' => 'Vis',\n    'Yes' => 'Ja',\n    'You are not allowed to perform this action.' => 'Du har ikke tilladelse til at udføre denne handling.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Du kan højst uploade {limit, number} {limit, plural, one{fil} other{filer}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => 'om {delta, plural, =1{en dag} other{# dage}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'om {delta, plural, =1{et minut} other{# minutter}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'om {delta, plural, =1{en måned} other{# måneder}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'om {delta, plural, =1{et sekund} other{# sekunder}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'om {delta, plural, =1{et år} other{# år}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'om {delta, plural, =1{en time} other{# timer}}',\n    'just now' => '',\n    'the input value' => 'inputværdien',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" er allerede i brug.',\n    '{attribute} cannot be blank.' => '{attribute} må ikke være tom.',\n    '{attribute} contains wrong subnet mask.' => '',\n    '{attribute} is invalid.' => '{attribute} er ugyldig.',\n    '{attribute} is not a valid URL.' => '{attribute} er ikke en gyldig URL.',\n    '{attribute} is not a valid email address.' => '{attribute} er ikke en gyldig emailadresse.',\n    '{attribute} is not in the allowed range.' => '',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} skal være \"{requiredValue}\".',\n    '{attribute} must be a number.' => '{attribute} skal være et nummer.',\n    '{attribute} must be a string.' => '{attribute} skal være en tekst-streng.',\n    '{attribute} must be a valid IP address.' => '',\n    '{attribute} must be an IP address with specified subnet.' => '',\n    '{attribute} must be an integer.' => '{attribute} skal være et heltal.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} skal være enten \"{true}\" eller \"{false}\".',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} skal være større end \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} skal være større end eller lig med \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} skal være mindre end \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} skal være mindre end eller lig med \"{compareValueOrAttribute}\".',\n    '{attribute} must be no greater than {max}.' => '{attribute} må ikke være større end {max}.',\n    '{attribute} must be no less than {min}.' => '{attribute} må ikke være mindre end {min}.',\n    '{attribute} must not be a subnet.' => '',\n    '{attribute} must not be an IPv4 address.' => '',\n    '{attribute} must not be an IPv6 address.' => '',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} må ikke være lig med \"{compareValueOrAttribute}\".',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} skal mindst indeholde {min, number} {min, plural, one{tegn} other{tegn}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} skal højst indeholde {max, number} {max, plural, one{tegn} other{tegn}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} skal indeholde {length, number} {length, plural, one{tegn} other{tegn}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '',\n    '{delta, plural, =1{1 month} other{# months}}' => '',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '',\n    '{delta, plural, =1{1 year} other{# years}}' => '',\n    '{delta, plural, =1{a day} other{# days}} ago' => 'for {delta, plural, =1{en dag} other{# dage}} siden',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => 'for {delta, plural, =1{et minut} other{# minutter}} siden',\n    '{delta, plural, =1{a month} other{# months}} ago' => 'for {delta, plural, =1{en måned} other{# måneder}} siden',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => 'for {delta, plural, =1{et sekund} other{# sekunder}} siden',\n    '{delta, plural, =1{a year} other{# years}} ago' => 'for {delta, plural, =1{et år} other{# år}} siden',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => 'for {delta, plural, =1{en time} other{# timer}} siden',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '',\n    '{nFormatted} KiB' => '',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, =1{byte} other{bytes}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}',\n];\n"
  },
  {
    "path": "framework/messages/de/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => ' und ',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '\"{attribute}\" unterstützt den Operator \"{operator}\" nicht.',\n    '(not set)' => '(nicht gesetzt)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Es ist ein interner Serverfehler aufgetreten.',\n    'Are you sure you want to delete this item?' => 'Wollen Sie diesen Eintrag wirklich löschen?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => 'Die Bedingung für \"{attribute}\" muss entweder ein Wert oder ein gültiger Operator sein.',\n    'Delete' => 'Löschen',\n    'Error' => 'Fehler',\n    'File upload failed.' => 'Das Hochladen der Datei ist fehlgeschlagen.',\n    'Home' => 'Home',\n    'Invalid data received for parameter \"{param}\".' => 'Ungültige Daten erhalten für Parameter \"{param}\".',\n    'Login Required' => 'Anmeldung erforderlich',\n    'Missing required arguments: {params}' => 'Pflichtargumente fehlen: {params}',\n    'Missing required parameters: {params}' => 'Pflichtparameter fehlen: {params}',\n    'No' => 'Nein',\n    'No results found.' => 'Keine Ergebnisse gefunden',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Es sind nur Dateien mit folgenden MIME-Typen erlaubt: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Es sind nur Dateien mit folgenden Dateierweiterungen erlaubt: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => 'Der Operator \"{operator}\" muss zusammen mit einem Such-Attribut verwendet werden.',\n    'Operator \"{operator}\" requires multiple operands.' => 'Der Operator \"{operator}\" erwartet mehrere Operanden.',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Seite nicht gefunden.',\n    'Please fix the following errors:' => 'Bitte korrigieren Sie die folgenden Fehler:',\n    'Please upload a file.' => 'Bitte laden Sie eine Datei hoch.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Zeige <b>{begin, number}-{end, number}</b> von <b>{totalCount, number}</b> {totalCount, plural, one{Eintrag} other{Einträgen}}.',\n    'The combination {values} of {attributes} has already been taken.' => 'Die Kombination {values} für {attributes} wird bereits verwendet.',\n    'The file \"{file}\" is not an image.' => 'Die Datei \"{file}\" ist kein Bild.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'Die Datei \"{file}\" ist zu groß. Es sind maximal {formattedLimit} erlaubt.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Die Datei \"{file}\" ist zu klein. Es sind mindestens {formattedLimit} erforderlich.',\n    'The format of {attribute} is invalid.' => 'Das Format von {attribute} ist ungültig.',\n    'The format of {filter} is invalid.' => 'Das Format von {filter} ist ungültig.',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Das Bild \"{file}\" ist zu groß. Es darf maximal {limit, number} Pixel hoch sein.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Das Bild \"{file}\" ist zu groß. Es darf maximal {limit, number} Pixel breit sein.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Das Bild \"{file}\" ist zu klein. Es muss mindestens {limit, number} Pixel hoch sein.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Das Bild \"{file}\" ist zu klein. Es muss mindestens {limit, number} Pixel breit sein.',\n    'The requested view \"{name}\" was not found.' => 'Die View-Datei \"{name}\" konnte nicht gefunden werden.',\n    'The verification code is incorrect.' => 'Der Prüfcode ist falsch.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Insgesamt <b>{count, number}</b> {count, plural, one{Eintrag} other{Einträge}}.',\n    'Unable to verify your data submission.' => 'Ihre Dateneingabe konnte nicht überprüft werden oder ist ungültig.',\n    'Unknown alias: -{name}' => 'Unbekannter Alias: -{name}',\n    'Unknown filter attribute \"{attribute}\"' => 'Unbekanntes Filter-Attribut \"{attribute}\"',\n    'Unknown option: --{name}' => 'Unbekannte Option: --{name}',\n    'Update' => 'Bearbeiten',\n    'View' => 'Anzeigen',\n    'Yes' => 'Ja',\n    'You are not allowed to perform this action.' => 'Sie dürfen diese Aktion nicht durchführen.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Sie können maximal {limit, plural, one{eine Datei} other{# Dateien}} hochladen.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => 'in {delta, plural, =1{einem Tag} other{# Tagen}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'in {delta, plural, =1{einer Minute} other{# Minuten}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'in {delta, plural, =1{einem Monat} other{# Monaten}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'in {delta, plural, =1{einer Sekunde} other{# Sekunden}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'in {delta, plural, =1{einem Jahr} other{# Jahren}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'in {delta, plural, =1{einer Stunde} other{# Stunden}}',\n    'just now' => 'gerade jetzt',\n    'the input value' => 'der eingegebene Wert',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" wird bereits verwendet.',\n    '{attribute} cannot be blank.' => '{attribute} darf nicht leer sein.',\n    '{attribute} contains wrong subnet mask.' => '{attribute} enthält ungültige Subnetz-Maske.',\n    '{attribute} is invalid.' => '{attribute} ist ungültig.',\n    '{attribute} is not a valid URL.' => '{attribute} ist keine gültige URL.',\n    '{attribute} is not a valid email address.' => '{attribute} ist keine gültige E-Mail-Adresse.',\n    '{attribute} is not in the allowed range.' => '{attribute} ist außerhalb des gültigen Bereichs.',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} muss den Wert {requiredValue} haben.',\n    '{attribute} must be a number.' => '{attribute} muss eine Zahl sein.',\n    '{attribute} must be a string.' => '{attribute} muss eine Zeichenkette sein.',\n    '{attribute} must be a valid IP address.' => '{attribute} muss eine gültige IP-Adresse sein.',\n    '{attribute} must be an IP address with specified subnet.' => '{attribute} muss eine gültige IP-Adresse inklusive Subnetz-Maske sein.',\n    '{attribute} must be an integer.' => '{attribute} muss eine Ganzzahl sein.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} muss entweder \"{true}\" oder \"{false}\" sein.',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '{attribute} muss gleich mit \"{compareValueOrAttribute}\" sein.',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} muss größer als \"{compareValueOrAttribute}\" sein.',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} muss größer oder gleich \"{compareValueOrAttribute}\" sein.',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} muss kleiner als \"{compareValueOrAttribute}\" sein.',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} muss kleiner oder gleich \"{compareValueOrAttribute}\" sein.',\n    '{attribute} must be no greater than {max}.' => '{attribute} darf nicht größer als {max} sein.',\n    '{attribute} must be no less than {min}.' => '{attribute} darf nicht kleiner als {min} sein.',\n    '{attribute} must not be a subnet.' => '{attribute} darf kein Subnetz sein.',\n    '{attribute} must not be an IPv4 address.' => '{attribute} darf keine IPv4-Adresse sein.',\n    '{attribute} must not be an IPv6 address.' => '{attribute} darf keine IPv6-Adresse sein.',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} darf nicht \"{compareValueOrAttribute}\" sein.',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} muss mindestens {min, number} Zeichen enthalten.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} darf maximal {max, number} Zeichen enthalten.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} muss aus genau {length, number} Zeichen bestehen.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, =1{1 Tag} other{# Tage}}',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, =1{1 Stunde} other{# Stunden}}',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, =1{1 Minute} other{# Minuten}}',\n    '{delta, plural, =1{1 month} other{# months}}' => '{delta, plural, =1{1 Monat} other{# Monate}}',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta, plural, =1{1 Sekunde} other{# Sekunden}}',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta, plural, =1{1 Jahr} other{# Jahre}}',\n    '{delta, plural, =1{a day} other{# days}} ago' => 'vor {delta, plural, =1{einem Tag} other{# Tagen}}',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => 'vor {delta, plural, =1{einer Minute} other{# Minuten}}',\n    '{delta, plural, =1{a month} other{# months}} ago' => 'vor {delta, plural, =1{einem Monat} other{# Monaten}}',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => 'vor {delta, plural, =1{einer Sekunde} other{# Sekunden}}',\n    '{delta, plural, =1{a year} other{# years}} ago' => 'vor {delta, plural, =1{einem Jahr} other{# Jahren}}',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => 'vor {delta, plural, =1{einer Stunde} other{# Stunden}}',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '{nFormatted} GiB',\n    '{nFormatted} KiB' => '{nFormatted} KiB',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '{nFormatted} MiB',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} Byte',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} GibiByte',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} Gigabyte',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} KibiByte',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} Kilobyte',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} MebiByte',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} Megabyte',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} PebiByte',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} Petabyte',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} TebiByte',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} Terabyte',\n];\n"
  },
  {
    "path": "framework/messages/el/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => ' και ',\n    '\"{attribute}\" does not support operator \"{operator}\".' => 'Το \"{attribute}\" δεν υποστηρίζει τον τελεστή \"{operator}\".',\n    '(not set)' => '(μη ορισμένο)',\n    'Action not found.' => 'Δε βρέθηκε η ενέργεια.',\n    'Aliases available: {aliases}' => 'Διαθέσιμα ψευδώνυμα: {aliases}',\n    'An internal server error occurred.' => 'Υπήρξε ένα εσωτερικό σφάλμα του διακομιστή.',\n    'Are you sure you want to delete this item?' => 'Είστε σίγουροι για τη διαγραφή του αντικειμένου;',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => 'Η συνθήκη για το \"{attribute}\" πρέπει να είναι είτε τιμή είτε προδιαγραφή έγκυρου τελεστή.',\n    'Delete' => 'Διαγραφή',\n    'Error' => 'Σφάλμα',\n    'File upload failed.' => 'Το ανέβασμα του αρχείου απέτυχε.',\n    'Home' => 'Αρχή',\n    'Invalid data received for parameter \"{param}\".' => 'Λήφθησαν μη έγκυρα δεδομένα για την παράμετρο \"{param}\".',\n    'Login Required' => 'Απαιτείται είσοδος',\n    'Missing required arguments: {params}' => 'Απουσιάζουν απαραίτητα ορίσματα: {params}',\n    'Missing required parameters: {params}' => 'Απουσιάζουν απαραίτητες παράμετροι: {params}',\n    'No' => 'Όχι',\n    'No results found.' => 'Δε βρέθηκαν αποτελέσματα.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Επιτρέπονται μόνο αρχεία με τους ακόλουθους τύπους MIME: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Επιτρέπονται αρχεία μόνο με καταλήξεις: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => 'Ο τελεστής \"{operator}\" πρέπει να χρησιμοποιηθεί με μια ιδιότητα για αναζήτηση.',\n    'Operator \"{operator}\" requires multiple operands.' => 'Ο τελεστής \"{operator}\" απαιτεί πολλούς τελεστέους.',\n    'Options available: {options}' => 'Διαθέσιμες επιλογές: {options}',\n    'Page not found.' => 'Η σελίδα δε βρέθηκε.',\n    'Please fix the following errors:' => 'Παρακαλώ διορθώστε τα παρακάτω σφάλματα:',\n    'Please upload a file.' => 'Παρακαλώ ανεβάστε ένα αρχείο.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Εμφανίζονται <b>{begin, number}-{end, number}</b> από <b>{totalCount, number}</b>.',\n    'The combination {values} of {attributes} has already been taken.' => 'Ο συνδυασμός των τιμών {values} του/των {attributes} έχει ήδη δοθεί.',\n    'The file \"{file}\" is not an image.' => 'Το αρχείο «{file}» δεν είναι εικόνα.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'Το αρχείο «{file}» είναι πολύ μεγάλο. Το μέγεθός του δεν μπορεί να είναι πάνω από {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Το αρχείο «{file}» είναι πολύ μικρό. Το μέγεθός του δεν μπορεί να είναι μικρότερο από {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'Η μορφή του «{attribute}» δεν είναι έγκυρη.',\n    'The format of {filter} is invalid.' => 'Η μορφή του {filter} δεν είναι έγκυρη.',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Η εικόνα «{file}» είναι πολύ μεγάλη. Το ύψος δεν μπορεί να είναι μεγαλύτερο από {limit, number} {limit, plural, one{pixel} few{pixels} many{pixels} other{pixels}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Η εικόνα «{file}» είναι πολύ μεγάλη. Το πλάτος δεν μπορεί να είναι μεγαλύτερο από {limit, number} {limit, plural, one{pixel} few{pixels} many{pixels} other{pixels}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Η εικόνα «{file}» είναι πολύ μικρή. To ύψος δεν μπορεί να είναι μικρότερο από {limit, number} {limit, plural, one{pixel} few{pixels} many{pixels} other{pixels}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Η εικόνα «{file}» είναι πολύ μικρή. Το πλάτος δεν μπορεί να είναι μικρότερο από {limit, number} {limit, plural, one{pixel} few{pixels} many{pixels} other{pixels}}.',\n    'The requested view \"{name}\" was not found.' => 'Δε βρέθηκε η αιτούμενη όψη \"{name}\".',\n    'The verification code is incorrect.' => 'Ο κωδικός επαλήθευσης είναι εσφαλμένος.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Συνολικά <b>{count, number}</b> {count, plural, one{αντικείμενο} few{αντικείμενα} many{αντικείμενα} other{αντικείμενα}}.',\n    'Unable to verify your data submission.' => 'Δεν ήταν δυνατή η επαλήθευση των απεσταλμένων δεδομένων.',\n    'Unknown alias: -{name}' => 'Άγνωστο ψευδώνυμο: -{name}',\n    'Unknown filter attribute \"{attribute}\"' => 'Άγνωστη ιδιότητα φίλτρου \"{attribute}\"',\n    'Unknown option: --{name}' => 'Άγνωστη επιλογή: --{name}',\n    'Update' => 'Ενημέρωση',\n    'View' => 'Προβολή',\n    'Yes' => 'Ναι',\n    'You are not allowed to perform this action.' => 'Δεν επιτρέπεται να εκτελέσετε αυτή την ενέργεια.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Μπορείτε να ανεβάσετε το πολύ {limit, number} {limit, plural, one{αρχείο} few{αρχεία} many{αρχεία} other{αρχεία}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => 'Θα πρέπει να ανεβάσετε τουλάχιστον {limit, number} {limit, plural, one{αρχείο} other{αρχεία}}.',\n    'in {delta, plural, =1{a day} other{# days}}' => 'σε {delta, plural, =1{μία ημέρα} other{# ημέρες}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'σε {delta, plural, =1{ένα λεπτό} other{# λεπτά}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'σε {delta, plural, =1{ένα μήνα} other{# μήνες}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'σε {delta, plural, =1{ένα δευτερόλεπτο} other{# δευτερόλεπτα}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'σε {delta, plural, =1{ένα έτος} other{# έτη}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'σε {delta, plural, =1{μία ώρα} other{# ώρες}}',\n    'just now' => 'μόλις τώρα',\n    'the input value' => 'η τιμή εισόδου',\n    '{attribute} \"{value}\" has already been taken.' => 'Το {attribute} «{value}» έχει ήδη καταχωρηθεί.',\n    '{attribute} cannot be blank.' => 'Το «{attribute}» δεν μπορεί να είναι κενό.',\n    '{attribute} contains wrong subnet mask.' => 'Το {attribute} περιέχει λάθος μάσκα υποδικτύου.',\n    '{attribute} is invalid.' => 'Το «{attribute}» δεν είναι έγκυρο.',\n    '{attribute} is not a valid URL.' => 'Το «{attribute}» δεν είναι έγκυρη διεύθυνση URL.',\n    '{attribute} is not a valid email address.' => 'Το «{attribute}» δεν είναι έγκυρη διεύθυνση e-mail.',\n    '{attribute} is not in the allowed range.' => 'Το {attribute} δεν είναι στο επιτρεπόμενο εύρος.',\n    '{attribute} must be \"{requiredValue}\".' => 'Το «{attribute}» πρέπει να είναι «{requiredValue}».',\n    '{attribute} must be a number.' => 'Το «{attribute}» πρέπει να είναι αριθμός.',\n    '{attribute} must be a string.' => 'Το «{attribute}» πρέπει να είναι συμβολοσειρά.',\n    '{attribute} must be a valid IP address.' => 'Το {attribute} πρέπει να είναι έγκυρη διεύθυνση IP.',\n    '{attribute} must be an IP address with specified subnet.' => 'Το {attribute} πρέπει να είναι διεύθυνση IP με καθορισμένη μάσκα.',\n    '{attribute} must be an integer.' => 'Το «{attribute}» πρέπει να είναι ακέραιος.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => 'Το «{attribute}» πρέπει να είναι «{true}» ή «{false}».',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => 'Το {attribute} πρέπει να είναι ίδιο με το \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => 'Το {attribute} πρέπει να είναι μεγαλύτερο από το \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => 'Το {attribute} πρέπει να είναι μεγαλύτερο ή ίσο από το \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => 'Το {attribute} πρέπει να είναι μικρότερο από το \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => 'Το {attribute} πρέπει να είναι μικρότερο ή ίσο από το \"{compareValueOrAttribute}\".',\n    '{attribute} must be no greater than {max}.' => 'Το «{attribute}» δεν πρέπει να είναι μεγαλύτερο από {max}.',\n    '{attribute} must be no less than {min}.' => 'Το «{attribute}» δεν πρέπει να είναι μικρότερο από {min}.',\n    '{attribute} must not be a subnet.' => 'Το {attribute} δεν πρέπει να είναι υποδίκτυο.',\n    '{attribute} must not be an IPv4 address.' => 'Το {attribute} δεν πρέπει να είναι διεύθυνση IPv4.',\n    '{attribute} must not be an IPv6 address.' => 'Το {attribute} δεν πρέπει να είναι διεύθυνση IPv6.',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => 'Το {attribute} δεν πρέπει να είναι ίδιο με το \"{compareValueOrAttribute}\".',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => 'Το «{attribute}» πρέπει να περιέχει τουλάχιστον {min, number} {min, plural, one{χαρακτήρα} few{χαρακτήρες} many{χαρακτήρες} other{χαρακτήρες}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => 'Το «{attribute}» πρέπει να περιέχει το πολύ {max, number} {max, plural, one{χαρακτήρα} few{χαρακτήρες} many{χαρακτήρες} other{χαρακτήρες}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => 'Το «{attribute}» πρέπει να περιέχει {length, number} {length, plural, one{χαρακτήρα} few{χαρακτήρες} many{χαρακτήρες} other{χαρακτήρες}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, =1{1 ημέρα} other{# ημέρες}}',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, =1{1 ώρα} other{# ώρες}}',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, =1{1 λεπτό} other{# λεπτά}}',\n    '{delta, plural, =1{1 month} other{# months}}' => '{delta, plural, =1{1 μήνας} other{# μήνες}}',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta, plural, =1{1 δευτερόλεπτο} other{# δευτερόλεπτα}}',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta, plural, =1{1 έτος} other{# έτη}}',\n    '{delta, plural, =1{a day} other{# days}} ago' => 'πριν {delta, plural, =1{μία ημέρα} other{# ημέρες}}',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => 'πριν {delta, plural, =1{ένα λεπτό} other{# λεπτά}}',\n    '{delta, plural, =1{a month} other{# months}} ago' => 'πριν {delta, plural, =1{ένα μήνα} other{# μήνες}}',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => 'πριν {delta, plural, =1{ένα δευτερόλεπτο} other{# δευτερόλεπτα}}',\n    '{delta, plural, =1{a year} other{# years}} ago' => 'πριν {delta, plural, =1{ένα έτος} other{# έτη}}',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => 'πριν {delta, plural, =1{μία ώρα} other{# ώρες}}',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '{nFormatted} GiB',\n    '{nFormatted} KiB' => '{nFormatted} KiB',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '{nFormatted} MiB',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, =1{byte} other{bytes}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}',\n];\n"
  },
  {
    "path": "framework/messages/es/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => ' y ',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(no definido)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Hubo un error interno del servidor.',\n    'Are you sure you want to delete this item?' => '¿Está seguro de eliminar este elemento?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'Eliminar',\n    'Error' => 'Error',\n    'File upload failed.' => 'Falló la subida del archivo.',\n    'Home' => 'Inicio',\n    'Invalid data received for parameter \"{param}\".' => 'Se recibieron datos erróneos para el parámetro \"{param}\"',\n    'Login Required' => 'Login Requerido',\n    'Missing required arguments: {params}' => 'Argumentos requeridos ausentes: {params}',\n    'Missing required parameters: {params}' => 'Parámetros requeridos ausentes: {params}',\n    'No' => 'No',\n    'No results found.' => 'No se encontraron resultados.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Sólo se aceptan archivos con los siguientes tipos MIME: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Sólo se aceptan archivos con las siguientes extensiones: {extensions}',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Página no encontrada.',\n    'Please fix the following errors:' => 'Por favor corrija los siguientes errores:',\n    'Please upload a file.' => 'Por favor suba un archivo.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Mostrando <b>{begin, number}-{end, number}</b> de <b>{totalCount, number}</b> {totalCount, plural, one{elemento} other{elementos}}.',\n    'The combination {values} of {attributes} has already been taken.' => 'La combinación de {values} de {attributes} ya ha sido utilizada.',\n    'The file \"{file}\" is not an image.' => 'El archivo \"{file}\" no es una imagen.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'El archivo \"{file}\" es demasiado grande. Su tamaño no puede exceder {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'El archivo \"{file}\" es demasiado pequeño. Su tamaño no puede ser menor a {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'El formato de {attribute} es inválido.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'La imagen \"{file}\" es demasiado grande. La altura no puede ser mayor a {limit, number} {limit, plural, one{píxel} other{píxeles}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'La imagen \"{file}\" es demasiado grande. La anchura no puede ser mayor a {limit, number} {limit, plural, one{píxel} other{píxeles}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'La imagen \"{file}\" es demasiado pequeña. La altura no puede ser menor a {limit, number} {limit, plural, one{píxel} other{píxeles}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'La imagen \"{file}\" es demasiado pequeña. La anchura no puede ser menor a {limit, number} {limit, plural, one{píxel} other{píxeles}}.',\n    'The requested view \"{name}\" was not found.' => 'La vista solicitada \"{name}\" no fue encontrada',\n    'The verification code is incorrect.' => 'El código de verificación es incorrecto.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Total <b>{count, number}</b> {count, plural, one{elemento} other{elementos}}.',\n    'Unable to verify your data submission.' => 'Incapaz de verificar los datos enviados.',\n    'Unknown alias: -{name}' => 'Alias desconocido: -{name}',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'Opción desconocida: --{name}',\n    'Update' => 'Actualizar',\n    'View' => 'Ver',\n    'Yes' => 'Sí',\n    'You are not allowed to perform this action.' => 'No tiene permitido ejecutar esta acción.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Puedes subir como máximo {limit, number} {limit, plural, one{archivo} other{archivos}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => 'en {delta, plural, =1{un día} other{# días}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'en {delta, plural, =1{un minuto} other{# minutos}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'en {delta, plural, =1{un mes} other{# meses}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'en {delta, plural, =1{un segundo} other{# segundos}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'en {delta, plural, =1{un año} other{# años}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'en {delta, plural, =1{una hora} other{# horas}}',\n    'just now' => 'justo ahora',\n    'the input value' => 'el valor de entrada',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" ya ha sido utilizado.',\n    '{attribute} cannot be blank.' => '{attribute} no puede estar vacío.',\n    '{attribute} contains wrong subnet mask.' => '{attribute} contiene la máscara de subred incorrecta',\n    '{attribute} is invalid.' => '{attribute} es inválido.',\n    '{attribute} is not a valid URL.' => '{attribute} no es una URL válida.',\n    '{attribute} is not a valid email address.' => '{attribute} no es una dirección de correo válida.',\n    '{attribute} is not in the allowed range.' => '{attribute} no está en el rango permitido.',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} debe ser \"{requiredValue}\".',\n    '{attribute} must be a number.' => '{attribute} debe ser un número.',\n    '{attribute} must be a string.' => '{attribute} debe ser una cadena de caracteres.',\n    '{attribute} must be a valid IP address.' => '{attribute} debe ser una dirección IP válida ',\n    '{attribute} must be an IP address with specified subnet.' => '{attribute} debe ser una dirección IP con subred especificada.',\n    '{attribute} must be an integer.' => '{attribute} debe ser un número entero.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} debe ser \"{true}\" o \"{false}\".',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '{attribute} debe ser igual a \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} debe ser mayor a \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} debe ser mayor o igual a \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} debe ser menor a \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} debe ser menor  o igual a \"{compareValueOrAttribute}\".',\n    '{attribute} must be no greater than {max}.' => '{attribute} no debe ser mayor a {max}.',\n    '{attribute} must be no less than {min}.' => '{attribute} no debe ser menor a {min}.',\n    '{attribute} must not be a subnet.' => '{attribute} no debe ser una subnet.',\n    '{attribute} must not be an IPv4 address.' => '{attribute} no debe ser una dirección IPv4.',\n    '{attribute} must not be an IPv6 address.' => '{attribute} no debe ser una dirección IPv6.',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} no debe ser igual a \"{compareValueOrAttribute}\".',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} debería contener al menos {min, number} {min, plural, one{letra} other{letras}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} debería contener como máximo {max, number} {max, plural, one{letra} other{letras}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} debería contener {length, number} {length, plural, one{letra} other{letras}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, =1{1 día} other{# días}}',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, =1{1 hora} other{# horas}}',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, =1{1 minuto} other{# minutos}}',\n    '{delta, plural, =1{1 month} other{# months}}' => '{delta, plural, =1{1 mes} other{# meses}}',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta, plural, =1{1 segundo} other{# segundos}}',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta, plural, =1{1 año} other{# años}}',\n    '{delta, plural, =1{a day} other{# days}} ago' => 'hace {delta, plural, =1{un día} other{# días}}',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => 'hace {delta, plural, =1{un minuto} other{# minutos}}',\n    '{delta, plural, =1{a month} other{# months}} ago' => 'hace {delta, plural, =1{un mes} other{# meses}}',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => 'hace {delta, plural, =1{un segundo} other{# segundos}}',\n    '{delta, plural, =1{a year} other{# years}} ago' => 'hace {delta, plural, =1{un año} other{# años}}',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => 'hace {delta, plural, =1{una hora} other{# horas}}',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '{nFormatted} GiB',\n    '{nFormatted} KiB' => '{nFormatted} KiB',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '{nFormatted} MiB',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, =1{byte} other{bytes}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}',\n];\n"
  },
  {
    "path": "framework/messages/et/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => ' ja ',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '\"{attribute}\" ei toeta tehtemärki \"{operator}\".',\n    '(not set)' => '(määramata)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Ilmnes serveri sisemine viga.',\n    'Are you sure you want to delete this item?' => 'Kas olete kindel, et soovite selle üksuse kustutada?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => 'Atribuudi \"{attribute}\" tingimus peaks olema kas väärtus või korrektne tehtemärgi spetsifikatsioon.',\n    'Delete' => 'Kustuta',\n    'Error' => 'Viga',\n    'File upload failed.' => 'Faili üleslaadimine ebaõnnestus.',\n    'Home' => 'Avaleht',\n    'Invalid data received for parameter \"{param}\".' => 'Parameetri \"{param}\" jaoks võeti vastu sobimatud andmed.',\n    'Login Required' => 'Vajalik on sisse logimine',\n    'Missing required arguments: {params}' => 'Puuduvad nõutud argumendid: {params}',\n    'Missing required parameters: {params}' => 'Puuduvad nõutud parameetrid: {params}',\n    'No' => 'Ei',\n    'No results found.' => 'Ei leitud ühtegi tulemust.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Lubatud on ainult nende MIME tüüpidega failid: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Lubatud on ainult nende faililaienditega failid: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => 'Tehtemärki \"{operator}\" peab kasutama koos otsinguatribuudiga.',\n    'Operator \"{operator}\" requires multiple operands.' => 'Tehtemärk \"{operator}\" nõuab mitut operandi.',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Lehekülge ei leitud.',\n    'Please fix the following errors:' => 'Palun parandage järgnevad vead:',\n    'Please upload a file.' => 'Palun laadige fail üles.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Näitan <b>{totalCount, number}</b> {totalCount, plural, one{üksusest} other{üksusest}} <b>{begin, number}-{end, number}</b>.',\n    'The combination {values} of {attributes} has already been taken.' => 'Atribuutide {attributes} väärtuste kombinatsioon {values} on juba võetud.',\n    'The file \"{file}\" is not an image.' => 'See fail \"{file}\" ei ole pilt.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'See fail \"{file}\" on liiga suur. Suurus ei tohi ületada {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'See fail \"{file}\" on liiga väike. Suurus ei tohi olla väiksem kui {formattedLimit}.',\n    'The format of {attribute} is invalid.' => '{attribute} on sobimatus vormingus.',\n    'The format of {filter} is invalid.' => 'Filtri {filter} formaat on sobimatu.',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Pilt \"{file}\" on liiga suur. Kõrgus ei tohi olla suurem kui {limit, number} {limit, plural, one{piksel} other{pikslit}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Pilt \"{file}\" on liiga suur. Laius ei tohi olla suurem kui {limit, number} {limit, plural, one{piksel} other{pikslit}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Pilt \"{file}\" on liiga väike. Kõrgus ei tohi olla väiksem kui {limit, number} {limit, plural, one{piksel} other{pikslit}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Pilt \"{file}\" on liiga väike. Laius ei tohi olla väiksem kui {limit, number} {limit, plural, one{piksel} other{pikslit}}.',\n    'The requested view \"{name}\" was not found.' => 'Soovitud vaadet \"{name}\" ei leitud.',\n    'The verification code is incorrect.' => 'Kontrollkood on vale.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Kokku <b>{count, number}</b> {count, plural, one{üksus} other{üksust}}.',\n    'Unable to verify your data submission.' => 'Ei suuda edastatud andmete õigsuses veenduda.',\n    'Unknown alias: -{name}' => 'Tundmatu alias: -{name}',\n    'Unknown filter attribute \"{attribute}\"' => 'Tundmatu filtri atribuut \"{attribute}\"',\n    'Unknown option: --{name}' => 'Tundmatu valik: --{name}',\n    'Update' => 'Muuda',\n    'View' => 'Vaata',\n    'Yes' => 'Jah',\n    'You are not allowed to perform this action.' => 'Teil pole õigust seda toimingut sooritada.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Saate üles laadida kõige rohkem {limit, number} {limit, plural, one{faili} other{faili}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => 'Peaksid üles laadima vähemalt {limit, number} {limit, plural, one{faili} other{faili}}.',\n    'in {delta, plural, =1{a day} other{# days}}' => '{delta, plural, =1{ühe päeva} other{# päeva}} pärast',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => '{delta, plural, =1{ühe minuti} other{# minuti}} pärast',\n    'in {delta, plural, =1{a month} other{# months}}' => '{delta, plural, =1{ühe kuu} other{# kuu}} pärast',\n    'in {delta, plural, =1{a second} other{# seconds}}' => '{delta, plural, =1{ühe sekundi} other{# sekundi}} pärast',\n    'in {delta, plural, =1{a year} other{# years}}' => '{delta, plural, =1{ühe aasta} other{# aasta}} pärast',\n    'in {delta, plural, =1{an hour} other{# hours}}' => '{delta, plural, =1{ühe tunni} other{# tunni}} pärast',\n    'just now' => 'just nüüd',\n    'the input value' => 'sisendväärtus',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" on juba kasutuses.',\n    '{attribute} cannot be blank.' => '{attribute} ei tohi olla tühi.',\n    '{attribute} contains wrong subnet mask.' => '{attribute} sisaldab valet alamvõrgumaski.',\n    '{attribute} is invalid.' => '{attribute} on sobimatu.',\n    '{attribute} is not a valid URL.' => '{attribute} ei ole korrektne URL.',\n    '{attribute} is not a valid email address.' => '{attribute} ei ole korrektne e-posti aadress.',\n    '{attribute} is not in the allowed range.' => '{attribute} ei ole lubatud vahemikus.',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} peab olema \"{requiredValue}\".',\n    '{attribute} must be a number.' => '{attribute} peab olema number.',\n    '{attribute} must be a string.' => '{attribute} peab olema tekst.',\n    '{attribute} must be a valid IP address.' => '{attribute} peab olema õige IP-aadress',\n    '{attribute} must be an IP address with specified subnet.' => '{attribute} peab olema võrgumaskiga IP-aadress.',\n    '{attribute} must be an integer.' => '{attribute} peab olema täisarv.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} peab olema kas \"{true}\" või \"{false}\".',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '{attribute} peab olema \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} peab olema suurem kui \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} peab olema suurem või võrdne \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} peab olema väiksem kui \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} peab olema väiksem või võrdne \"{compareValueOrAttribute}\".',\n    '{attribute} must be no greater than {max}.' => '{attribute} ei tohi olla suurem kui {max}.',\n    '{attribute} must be no less than {min}.' => '{attribute} ei tohi olla väiksem kui {min}.',\n    '{attribute} must not be a subnet.' => '{attribute} ei tohi olla alamvõrk.',\n    '{attribute} must not be an IPv4 address.' => '{attribute} ei tohi olla IPv4 aadress.',\n    '{attribute} must not be an IPv6 address.' => '{attribute} ei tohi olla IPv6 aadress.',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} ei tohi olla \"{compareValueOrAttribute}\".',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} peab sisaldama vähemalt {min, number} {min, plural, one{tähemärki} other{tähemärki}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} tohib sisaldada maksimaalselt {max, number} {max, plural, one{tähemärki} other{tähemärki}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} peab sisaldama {length, number} {length, plural, one{tähemärki} other{tähemärki}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, =1{1 päev} other{# päeva}}',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, =1{1 tund} other{# tundi}}',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, =1{1 minut} other{# minutit}}',\n    '{delta, plural, =1{1 month} other{# months}}' => '{delta, plural, =1{1 kuu} other{# kuud}}',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta, plural, =1{1 sekund} other{# sekundit}}',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta, plural, =1{1 aasta} other{# aastat}}',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{1 päev} other{# päeva}} tagasi',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{1 minut} other{# minutit}} tagasi',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{kuu aega} other{# kuud}} tagasi',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{1 sekund} other{# sekundit}} tagasi',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{aasta} other{# aastat}} tagasi',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, =1{tund aega} other{# tundi}} tagasi',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '{nFormatted} GiB',\n    '{nFormatted} KiB' => '{nFormatted} KiB',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '{nFormatted} MiB',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, =1{bait} other{baiti}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, =1{gibibait} other{gibibaiti}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, =1{gigabait} other{gigabaiti}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, =1{kibibait} other{kibibaiti}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, =1{kilobait} other{kilobaiti}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, =1{mebibait} other{mebibaiti}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, =1{megabait} other{megabaiti}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, =1{pebibait} other{pebibaiti}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, =1{petabait} other{petabaiti}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, =1{tebibait} other{tebibaiti}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, =1{terabait} other{terabaiti}}',\n];\n"
  },
  {
    "path": "framework/messages/fa/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => ' و ',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '\"{attribute}\" از عملگر \"{operator}\" پشتیبانی نمی‌کند.',\n    '(not set)' => '(تنظیم نشده)',\n    'Action not found.' => 'عمل یافت نشد.',\n    'Aliases available: {aliases}' => 'نام‌های مستعار موجود: {aliases}',\n    'An internal server error occurred.' => 'خطای داخلی سرور رخ داده است.',\n    'Are you sure you want to delete this item?' => 'آیا اطمینان به حذف این مورد دارید؟',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => 'شرط برای \"{attribute}\" باید یک مقدار یا مشخصه‌ی عملگر معتبر باشد.',\n    'Delete' => 'حذف',\n    'Error' => 'خطا',\n    'File upload failed.' => 'آپلود فایل ناموفق بود.',\n    'Home' => 'صفحه‌اصلی',\n    'Invalid data received for parameter \"{param}\".' => 'برای پارامتر \"{param}\" اطلاعات نادرستی دریافت شده است.',\n    'Login Required' => 'ورود اجباری',\n    'Missing required arguments: {params}' => 'فاقد آرگومان‌های مورد نیاز: {params}',\n    'Missing required parameters: {params}' => 'فاقد پارامترهای مورد نیاز: {params}',\n    'No' => 'خیر',\n    'No results found.' => 'نتیجه‌ای یافت نشد.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'فقط این نوع فایل‌ها مجاز می‌باشند: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'فقط فایل‌های با این پسوندها مجاز هستند: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => 'عملگر \"{operator}\" باید با یک ویژگی جستجو استفاده شود.',\n    'Operator \"{operator}\" requires multiple operands.' => 'عملگر \"{operator}\" به چند عملوند نیاز دارد.',\n    'Options available: {options}' => 'گزینه‌های موجود: {options}',\n    'Page not found.' => 'صفحه‌ای یافت نشد.',\n    'Please fix the following errors:' => 'لطفاً خطاهای زیر را رفع نمائید:',\n    'Please upload a file.' => 'لطفاً یک فایل آپلود کنید.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'نمایش <b>{begin, number}</b> تا <b>{end, number}</b> مورد از کل <b>{totalCount, number}</b> مورد.',\n    'The combination {values} of {attributes} has already been taken.' => 'مقدار {values} از {attributes} قبلاً گرفته شده است.',\n    'The file \"{file}\" is not an image.' => 'فایل \"{file}\" یک تصویر نیست.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'حجم فایل \"{file}\" بسیار بیشتر می‌باشد. حجم آن نمی‌تواند از {formattedLimit} بیشتر باشد.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'حجم فایل \"{file}\" بسیار کم می‌باشد. حجم آن نمی‌تواند از {formattedLimit} کمتر باشد.',\n    'The format of {attribute} is invalid.' => 'قالب {attribute} نامعتبر است.',\n    'The format of {filter} is invalid.' => 'قالب {filter} نامعتبر است.',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'تصویر \"{file}\" خیلی بزرگ است. ارتفاع نمی‌تواند بزرگتر از {limit, number} پیکسل باشد.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'تصویر \"{file}\" خیلی بزرگ است. عرض نمی‌تواند بزرگتر از {limit, number} پیکسل باشد.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'تصویر \"{file}\" خیلی کوچک است. ارتفاع نمی‌تواند کوچکتر از {limit, number} پیکسل باشد.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'تصویر \"{file}\" خیلی کوچک است. عرض نمی‌تواند کوچکتر از {limit, number} پیکسل باشد.',\n    'The requested view \"{name}\" was not found.' => 'نمای درخواستی \"{name}\" یافت نشد.',\n    'The verification code is incorrect.' => 'کد تأیید اشتباه است.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'مجموع <b>{count, number}</b> مورد.',\n    'Unable to verify your data submission.' => 'قادر به تأیید اطلاعات ارسالی شما نمی‌باشد.',\n    'Unknown alias: -{name}' => 'نام مستعار ناشناخته: -{name}',\n    'Unknown filter attribute \"{attribute}\"' => 'ویژگی \"{attribute}\" فیلتر ناشناخته',\n    'Unknown option: --{name}' => 'گزینه ناشناخته: --{name}',\n    'Update' => 'بروزرسانی',\n    'View' => 'نما',\n    'Yes' => 'بله',\n    'You are not allowed to perform this action.' => 'شما برای انجام این عملیات، دسترسی ندارید.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'شما حداکثر {limit, number} فایل را می‌توانید آپلود کنید.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => 'شما باید حداقل {limit, number} فایل آپلود کنید.',\n    'in {delta, plural, =1{a day} other{# days}}' => '{delta} روز دیگر',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => '{delta} دقیقه دیگر',\n    'in {delta, plural, =1{a month} other{# months}}' => '{delta} ماه دیگر',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'در {delta} ثانیه',\n    'in {delta, plural, =1{a year} other{# years}}' => '{delta} سال دیگر',\n    'in {delta, plural, =1{an hour} other{# hours}}' => '{delta} ساعت دیگر',\n    'just now' => 'هم اکنون',\n    'the input value' => 'مقدار ورودی',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} در حال حاضر با مقدار \"{value}\" گرفته شده است.',\n    '{attribute} cannot be blank.' => '{attribute} نمی‌تواند خالی باشد.',\n    '{attribute} contains wrong subnet mask.' => '{attribute} شامل فرمت پوشش زیرشبکه (subnet mask) اشتباه است.',\n    '{attribute} is invalid.' => '{attribute} معتبر نیست.',\n    '{attribute} is not a valid URL.' => '{attribute} یک URL معتبر نیست.',\n    '{attribute} is not a valid email address.' => '{attribute} یک آدرس ایمیل معتبر نیست.',\n    '{attribute} is not in the allowed range.' => '{attribute} در محدوده مجاز نمی‌باشد.',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} باید \"{requiredValue}\" باشد.',\n    '{attribute} must be a number.' => '{attribute} باید یک عدد باشد.',\n    '{attribute} must be a string.' => '{attribute} باید یک رشته باشد.',\n    '{attribute} must be a valid IP address.' => '{attribute} باید یک آدرس IP معتبر باشد.',\n    '{attribute} must be an IP address with specified subnet.' => '{attribute} باید یک آدرس IP با زیرشبکه (subnet) مشخص شده باشد.',\n    '{attribute} must be an integer.' => '{attribute} باید یک عدد صحیح باشد.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} باید \"{true}\" و یا \"{false}\" باشد.',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '{attribute} باید با \"{compareValueOrAttribute}\" برابر باشد.',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} باید بیشتر از \"{compareValueOrAttribute}\" باشد.',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} باید بیشتر یا برابر با \"{compareValueOrAttribute}\" باشد.',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} باید کمتر از \"{compareValueOrAttribute}\" باشد.',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} باید کمتر یا برابر با \"{compareValueOrAttribute}\" باشد.',\n    '{attribute} must be no greater than {max}.' => '{attribute} نباید بیشتر از \"{max}\" باشد.',\n    '{attribute} must be no less than {min}.' => '{attribute} نباید کمتر از \"{min}\" باشد.',\n    '{attribute} must not be a subnet.' => '{attribute} نباید یک زیرشبکه (subnet) باشد.',\n    '{attribute} must not be an IPv4 address.' => '{attribute} نباید آدرس IPv4 باشد.',\n    '{attribute} must not be an IPv6 address.' => '{attribute} نباید آدرس IPv6 باشد.',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} نباید برابر با \"{compareValueOrAttribute}\" باشد.',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} حداقل باید شامل {min, number} کارکتر باشد.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} حداکثر باید شامل {max, number} کارکتر باشد.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} باید شامل {length, number} کارکتر باشد.',\n    '{compareAttribute} is invalid.' => '{compareAttribute} نامعتبر است.',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta} روز',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta} ساعت',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta} دقیقه',\n    '{delta, plural, =1{1 month} other{# months}}' => '{delta} ماه',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta} ثانیه',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta} سال',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta} روز قبل',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta} دقیقه قبل',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta} ماه قبل',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta} ثانیه قبل',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta} سال پیش',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta} ساعت قبل',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '{nFormatted} GiB',\n    '{nFormatted} KiB' => '{nFormatted} KiB',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '{nFormatted} MiB',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} بایت',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} گیبی‌بایت',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} گیگابایت',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} کیبی‌بایت',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} کیلوبایت',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} مبی‌بایت',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} مگابایت',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} پبی‌بایت',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} پتابایت',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} تبی‌بایت',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} ترابایت',\n];\n"
  },
  {
    "path": "framework/messages/fi/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => '',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(ei asetettu)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Sisäinen palvelinvirhe.',\n    'Are you sure you want to delete this item?' => 'Haluatko varmasti poistaa tämän?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'Poista',\n    'Error' => 'Virhe',\n    'File upload failed.' => 'Tiedoston lähetys epäonnistui.',\n    'Home' => 'Koti',\n    'Invalid data received for parameter \"{param}\".' => 'Parametri \"{param}\" vastaanotti virheellistä dataa.',\n    'Login Required' => 'Kirjautuminen vaaditaan',\n    'Missing required arguments: {params}' => 'Pakolliset argumentit puuttuu: {params}',\n    'Missing required parameters: {params}' => 'Pakolliset parametrit puuttuu: {params}',\n    'No' => 'Ei',\n    'No results found.' => 'Ei tuloksia.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Sallittuja ovat vain tiedostot, joiden MIME-tyyppi on: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Sallittuja ovat vain tiedostot, joiden tiedostopääte on: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Sivua ei löytynyt.',\n    'Please fix the following errors:' => 'Korjaa seuraavat virheet:',\n    'Please upload a file.' => 'Lähetä tiedosto.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Näytetään <b>{begin, number}-{end, number}</b> kaikkiaan <b>{totalCount, number}</b> {totalCount, plural, one{tuloksesta} other{tuloksesta}}.',\n    'The combination {values} of {attributes} has already been taken.' => '',\n    'The file \"{file}\" is not an image.' => 'Tiedosto \"{file}\" ei ole kuva.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'Tiedosto \"{file}\" on liian iso. Sen koko ei voi olla suurempi kuin {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Tiedosto \"{file}\" on liian pieni. Sen koko ei voi olla pienempi kuin {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'Attribuutin {attribute} formaatti on virheellinen.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Kuva \"{file}\" on liian suuri. Korkeus ei voi olla suurempi kuin {limit, number} {limit, plural, one{pikseli} other{pikseliä}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Kuva \"{file}\" on liian suuri. Leveys ei voi olla suurempi kuin {limit, number} {limit, plural, one{pikseli} other{pikseliä}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Kuva \"{file}\" on liian pieni. Korkeus ei voi olla pienempi kuin {limit, number} {limit, plural, one{pikseli} other{pikseliä}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Kuva \"{file}\" on liian pieni. Leveys ei voi olla pienempi kuin {limit, number} {limit, plural, one{pikseli} other{pikseliä}}.',\n    'The requested view \"{name}\" was not found.' => 'Pyydettyä näkymää \"{name}\" ei löytynyt.',\n    'The verification code is incorrect.' => 'Vahvistuskoodi on virheellinen.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Yhteensä <b>{count, number}</b> {count, plural, one{tulos} other{tulosta}}.',\n    'Unable to verify your data submission.' => 'Tietojen lähetystä ei voida varmistaa.',\n    'Unknown alias: -{name}' => '',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'Tuntematon valinta: --{name}',\n    'Update' => 'Päivitä',\n    'View' => 'Näytä',\n    'Yes' => 'Kyllä',\n    'You are not allowed to perform this action.' => 'Sinulla ei ole tarvittavia oikeuksia toiminnon suorittamiseen.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Voit lähettää enintään {limit, number} {limit, plural, one{tiedoston} other{tiedostoa}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => '{delta, plural, =1{päivässä} other{# päivässä}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => '{delta, plural, =1{minuutissa} other{# minuutissa}}',\n    'in {delta, plural, =1{a month} other{# months}}' => '{delta, plural, =1{kuukaudessa} other{# kuukaudessa}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => '{delta, plural, =1{sekunnissa} other{# sekunnissa}}',\n    'in {delta, plural, =1{a year} other{# years}}' => '{delta, plural, =1{vuodessa} other{# vuodessa}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => '{delta, plural, =1{tunnissa} other{# tunnissa}}',\n    'just now' => 'juuri nyt',\n    'the input value' => 'syötetty arvo',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" on jo käytössä.',\n    '{attribute} cannot be blank.' => '{attribute} ei voi olla tyhjä.',\n    '{attribute} contains wrong subnet mask.' => '{attribute} sisältää väärän aliverkkopeitteen.',\n    '{attribute} is invalid.' => '{attribute} on virheellinen.',\n    '{attribute} is not a valid URL.' => '{attribute} on virheellinen URL.',\n    '{attribute} is not a valid email address.' => '{attribute} on virheellinen sähköpostiosoite.',\n    '{attribute} is not in the allowed range.' => '{attribute} ei ole sallitulla alueella.',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} täytyy olla \"{requiredValue}\".',\n    '{attribute} must be a number.' => '{attribute} täytyy olla luku.',\n    '{attribute} must be a string.' => '{attribute} täytyy olla merkkijono.',\n    '{attribute} must be a valid IP address.' => '{attribute} täytyy olla kelvollinen IP-osoite.',\n    '{attribute} must be an IP address with specified subnet.' => '{attribute} täytyy olla määritetyllä aliverkolla oleva IP-osoite.',\n    '{attribute} must be an integer.' => '{attribute} täytyy olla kokonaisluku.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} täytyy olla joko {true} tai {false}.',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '{attribute} täytyy olla yhtä suuri kuin \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} täytyy olla suurempi kuin \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} täytyy olla suurempi tai yhtä suuri kuin \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} täytyy olla pienempi kuin \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} täytyy olla pienempi tai yhtä suuri kuin \"{compareValueOrAttribute}\".',\n    '{attribute} must be no greater than {max}.' => '{attribute} ei saa olla suurempi kuin \"{max}\".',\n    '{attribute} must be no less than {min}.' => '{attribute} ei saa olla pienempi kuin \"{min}\".',\n    '{attribute} must not be a subnet.' => '{attribute} ei saa olla aliverkko.',\n    '{attribute} must not be an IPv4 address.' => '{attribute} ei saa olla IPv4-osoite.',\n    '{attribute} must not be an IPv6 address.' => '{attribute} ei saa olla IPv6-osoite.',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} ei saa olla yhtä suuri kuin \"{compareValueOrAttribute}\".',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} tulisi sisältää vähintään {min, number} {min, plural, one{merkki} other{merkkiä}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} tulisi sisältää enintään {max, number} {max, plural, one{merkki} other{merkkiä}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} tulisi sisältää {length, number} {length, plural, one{merkki} other{merkkiä}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, =1{1 päivä} other{# päivää}}',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, =1{1 tunti} other{# tuntia}}',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, =1{1 minuutti} other{# minuuttia}}',\n    '{delta, plural, =1{1 month} other{# months}}' => '{delta, plural, =1{1 kuukausi} other{# kuukautta}}',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta, plural, =1{1 sekunti} other{# sekuntia}}',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta, plural, =1{1 vuosi} other{# vuotta}}',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{päivä} other{# päivää}} sitten',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{minuutti} other{# minuuttia}} sitten',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{kuukausi} other{# kuukautta}} sitten',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{sekunti} other{# sekuntia}} sitten',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{vuosi} other{# vuotta}} sitten',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, =1{tunti} other{# tuntia}} sitten',\n    '{nFormatted} B' => '{nFormatted} t',\n    '{nFormatted} GB' => '{nFormatted} Gt',\n    '{nFormatted} GiB' => '{nFormatted} GiB',\n    '{nFormatted} KiB' => '{nFormatted} KiB',\n    '{nFormatted} MB' => '{nFormatted} Mt',\n    '{nFormatted} MiB' => '{nFormatted} MiB',\n    '{nFormatted} PB' => '{nFormatted} Pt',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} Tt',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} kt',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, =1{tavu} other{tavua}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, =1{gibitavu} other{gibitavua}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, =1{gigatavu} other{gigatavua}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, =1{kibitavu} other{kibitavua}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, =1{kilotavu} other{kilotavua}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, =1{mebitavu} other{mebitavua}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, =1{megatavu} other{megatavua}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, =1{pebitavu} other{pebitavua}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, =1{petatavu} other{petatavua}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, =1{tebitavu} other{tebitavua}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, =1{teratavu} other{teratavua}}',\n];\n"
  },
  {
    "path": "framework/messages/fr/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => ' et ',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '\"{attribute}\" ne supporte pas l\\'opérateur \"{operator}\".',\n    '(not set)' => '(non défini)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Une erreur de serveur interne s\\'est produite.',\n    'Are you sure you want to delete this item?' => 'Êtes-vous sûr de vouloir supprimer cet élément ?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => 'La condition pour \"{atttribute}\" doit être soit une valeur, soit une spécification d\\'opérateur valide.',\n    'Delete' => 'Supprimer',\n    'Error' => 'Erreur',\n    'File upload failed.' => 'Le téléchargement du fichier a échoué.',\n    'Home' => 'Accueil',\n    'Invalid data received for parameter \"{param}\".' => 'Données non valides reçues pour le paramètre « {param} ».',\n    'Login Required' => 'Identifiant requis',\n    'Missing required arguments: {params}' => 'Arguments manquants requis : {params}',\n    'Missing required parameters: {params}' => 'Paramètres manquants requis : {params}',\n    'No' => 'Non',\n    'No results found.' => 'Aucun résultat trouvé.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Seulement les fichiers ayant ces types MIME sont autorisés : {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Les extensions de fichiers autorisées sont : {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => 'L\\'opérateur \"{operator}\" doit être utilisé avec un attribut de recherche.',\n    'Operator \"{operator}\" requires multiple operands.' => 'L\\'opérateur \"{operator}\" requière plusieurs opérandes.',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Page non trouvée.',\n    'Please fix the following errors:' => 'Veuillez vérifier les erreurs suivantes :',\n    'Please upload a file.' => 'Veuillez télécharger un fichier.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Affichage de <b>{begin, number}-{end, number}</b> sur <b>{totalCount, number}</b> {totalCount, plural, one{élément} other{éléments}}.',\n    'The combination {values} of {attributes} has already been taken.' => 'La combinaison {values} de {attributes} est déjà utilisée.',\n    'The file \"{file}\" is not an image.' => 'Le fichier « {file} » n\\'est pas une image.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'Le fichier « {file} » est trop gros. Sa taille ne peut dépasser {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Le fichier « {file} » est trop petit. Sa taille ne peut être inférieure à {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'Le format de {attribute} est invalide.',\n    'The format of {filter} is invalid.' => 'Le format de {filter} est invalide.',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'L\\'image « {file} » est trop grande. La hauteur ne peut être supérieure à {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'L\\'image « {file} » est trop large. La largeur ne peut être supérieure à {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'L\\'image « {file} » est trop petite. La hauteur ne peut être inférieure à {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'L\\'image « {file} » est trop petite. La largeur ne peut être inférieure à {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The requested view \"{name}\" was not found.' => 'La vue demandée « {name} » n\\'a pas été trouvée.',\n    'The verification code is incorrect.' => 'Le code de vérification est incorrect.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Total <b>{count, number}</b> {count, plural, one{élément} other{éléments}}.',\n    'Unable to verify your data submission.' => 'Impossible de vérifier votre envoi de données.',\n    'Unknown alias: -{name}' => 'Alias inconnu : -{name}',\n    'Unknown filter attribute \"{attribute}\"' => 'Attribut de filtre inconnu \"{attribute}\"',\n    'Unknown option: --{name}' => 'Option inconnue : --{name}',\n    'Update' => 'Modifier',\n    'View' => 'Voir',\n    'Yes' => 'Oui',\n    'You are not allowed to perform this action.' => 'Vous n\\'êtes pas autorisé à effectuer cette action.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Vous pouvez télécharger au maximum {limit, number} {limit, plural, one{fichier} other{fichiers}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => 'Vous devez télécharger au moins {limit, number} {limit, plural, one{fichier} other{fichiers}}.',\n    'in {delta, plural, =1{a day} other{# days}}' => 'dans {delta, plural, =1{un jour} other{# jours}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'dans {delta, plural, =1{une minute} other{# minutes}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'dans {delta, plural, =1{un mois} other{# mois}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'dans {delta, plural, =1{une seconde} other{# secondes}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'dans {delta, plural, =1{un an} other{# ans}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'dans {delta, plural, =1{une heure} other{# heures}}',\n    'just now' => 'à l\\'instant',\n    'the input value' => 'la valeur d\\'entrée',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} « {value} » a déjà été pris.',\n    '{attribute} cannot be blank.' => '{attribute} ne peut être vide.',\n    '{attribute} contains wrong subnet mask.' => '{attribute} contient un masque de sous-réseau non valide.',\n    '{attribute} is invalid.' => '{attribute} est invalide.',\n    '{attribute} is not a valid URL.' => '{attribute} n\\'est pas une URL valide.',\n    '{attribute} is not a valid email address.' => '{attribute} n\\'est pas une adresse email valide.',\n    '{attribute} is not in the allowed range.' => '{attribute} n\\'est pas dans la plage autorisée.',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} doit êre « {requiredValue} ».',\n    '{attribute} must be a number.' => '{attribute} doit être un nombre.',\n    '{attribute} must be a string.' => '{attribute} doit être au format texte.',\n    '{attribute} must be a valid IP address.' => '{attribute} doit être une adresse IP valide.',\n    '{attribute} must be an IP address with specified subnet.' => '{attribute} doit être une adresse IP avec un sous-réseau.',\n    '{attribute} must be an integer.' => '{attribute} doit être un entier.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} doit être soit « {true} » soit « {false} ».',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '{attribute} doit être égal à \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} doit être supérieur à « {compareValueOrAttribute} ».',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} doit être supérieur ou égal à « {compareValueOrAttribute} ».',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} doit être inférieur à « {compareValueOrAttribute} ».',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} doit être inférieur ou égal à « {compareValueOrAttribute} ».',\n    '{attribute} must be no greater than {max}.' => '{attribute} ne doit pas être supérieur à {max}.',\n    '{attribute} must be no less than {min}.' => '{attribute} ne doit pas être inférieur à {min}.',\n    '{attribute} must not be a subnet.' => '{attribute} ne doit pas être un sous-réseau.',\n    '{attribute} must not be an IPv4 address.' => '{attribute} ne doit pas être une adresse IPv4.',\n    '{attribute} must not be an IPv6 address.' => '{attribute} ne doit pas être une adresse IPv6.',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} ne doit pas être égal à « {compareValueOrAttribute} ».',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} doit comporter au moins {min, number} {min, plural, one{caractère} other{caractères}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} doit comporter au plus {max, number} {max, plural, one{caractère} other{caractères}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} doit comporter {length, number} {length, plural, one{caractère} other{caractères}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, =1{1 jour} other{# jours}}',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, =1{1 heure} other{# heures}}',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, =1{1 minute} other{# minutes}}',\n    '{delta, plural, =1{1 month} other{# months}}' => 'mois',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta, plural, =1{1 seconde} other{# secondes}}',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta, plural, =1{1 année} other{# années}}',\n    '{delta, plural, =1{a day} other{# days}} ago' => 'il y a {delta, plural, =1{un jour} other{# jours}}',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => 'il y a {delta, plural, =1{une minute} other{# minutes}}',\n    '{delta, plural, =1{a month} other{# months}} ago' => 'il y a {delta, plural, =1{un mois} other{# mois}}',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => 'il y a {delta, plural, =1{une seconde} other{# secondes}}',\n    '{delta, plural, =1{a year} other{# years}} ago' => 'il y a {delta, plural, =1{un an} other{# ans}}',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => 'il y a {delta, plural, =1{une heure} other{# heures}}',\n    '{nFormatted} B' => '{nFormatted} o',\n    '{nFormatted} GB' => '{nFormatted} Go',\n    '{nFormatted} GiB' => '{nFormatted} Gio',\n    '{nFormatted} KiB' => '{nFormatted} Kio',\n    '{nFormatted} MB' => '{nFormatted} Mo',\n    '{nFormatted} MiB' => '{nFormatted} Mio',\n    '{nFormatted} PB' => '{nFormatted} Po',\n    '{nFormatted} PiB' => '{nFormatted} Pio',\n    '{nFormatted} TB' => '{nFormatted} To',\n    '{nFormatted} TiB' => '{nFormatted} Tio',\n    '{nFormatted} kB' => '{nFormatted} Ko',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, =1{octet} other{octets}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, =1{# gigaoctet} other{# gigaoctets}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, =1{gibioctet} other{gibioctets}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, =1{kibioctet} other{kibioctets}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, =1{# kilooctet} other{# kilooctets}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, =1{mebioctet} other{mebioctets}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, =1{# megaoctet} other{# megaoctets}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, =1{pebioctet} other{pebioctets}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, =1{# petaoctet} other{# petaoctets}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, =1{# teraoctet} other{# teraoctets}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, =1{# teraoctet} other{# teraoctets}}',\n];\n"
  },
  {
    "path": "framework/messages/ga/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => ' agus ',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '\"{attribute}\" ní thacaíonn oibreoir \"{operator}\".',\n    '(not set)' => '(gan socrú)',\n    'Action not found.' => 'Gníomh gan aimsiú.',\n    'Aliases available: {aliases}' => 'Ailiasanna ar fáil: {aliases}',\n    'An internal server error occurred.' => 'Tharla earráid freastalaí inmheánach.',\n    'Are you sure you want to delete this item?' => 'An bhfuil tú cinnte gur mhaith leat an mhír seo a scriosadh?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => 'Ba cheart go mbeadh an coinníoll le haghaidh \"{attribute}\" ina luach nó ina shonraíocht oibritheora bailí.',\n    'Delete' => 'Scrios',\n    'Error' => 'Earráid',\n    'File upload failed.' => 'Theip ar uaslódáil an chomhaid.',\n    'Home' => 'Baile',\n    'Invalid data received for parameter \"{param}\".' => 'Sonraí neamhbhailí faighte don pharaiméadar \"{param}\".',\n    'Login Required' => 'Logáil Isteach ag Teastáil',\n    'Missing required arguments: {params}' => 'Argóintí riachtanacha in easnamh: {params}',\n    'Missing required parameters: {params}' => 'Paraiméadair riachtanacha in easnamh: {params}',\n    'No' => 'Níl',\n    'No results found.' => 'Níor aimsíodh aon torthaí.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Ní cheadaítear ach comhaid leis na cineálacha MIME seo: {mimeTypes}',\n    'Only files with these extensions are allowed: {extensions}.' => 'Ní cheadaítear ach comhaid leis na síntí seo: {extensions}',\n    'Operator \"{operator}\" must be used with a search attribute.' => 'Oibreoir \"{operator}\" Ní mór é a úsáid le tréith cuardaigh.',\n    'Operator \"{operator}\" requires multiple operands.' => 'Oibreoir \"{operator}\" éilíonn oibríochtaí iolracha.',\n    'Options available: {options}' => 'Roghanna ar fáil: {options}',\n    'Page not found.' => 'Ní bhfuarthas an leathanach.',\n    'Please fix the following errors:' => 'Ceartaigh na hearráidí seo a leanas le do thoil:',\n    'Please upload a file.' => 'Le do thoil uaslódáil comhad.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Ag taispeáint <b>{begin, number}-{end, number}</b> de <b>{totalCount, number}</b> {totalCount, plural, one{mír} other{míreanna}}.',\n    'The combination {values} of {attributes} has already been taken.' => 'An meascán {values} de {attributes} Tá glactha cheana féin.',\n    'The file \"{file}\" is not an image.' => 'An comhad \"{file}\" nach íomhá í.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'An comhad \"{file}\" ró-mhór. Ní féidir a mhéid a shárú  {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'An comhad \"{file}\" ró-bheag. Ní féidir a mhéid a bheith níos lú ná {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'Formáid na {attribute} neamhbhailí.',\n    'The format of {filter} is invalid.' => 'Formáid na {filter} neamhbhailí.',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'An íomhá \"{file}\" ró-mhór. Ní féidir leis an airde a bheith níos mó ná {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'An íomhá \"{file}\" ró-mhór. Ní féidir leis an leithead a bheith níos mó ná {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'An íomhá \"{file}\" ró-bheag. Ní féidir leis an airde a bheith níos lú ná {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'An íomhá \"{file}\" ró-bheag. Ní féidir leis an leithead a bheith níos lú ná {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The requested view \"{name}\" was not found.' => 'An radharc iarrtha \"{name}\" níor aimsíodh',\n    'The verification code is incorrect.' => 'Tá an cód fíoraithe mícheart',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Iomlán <b>{count, number}</b> {count, plural, one{mír} other{míreanna}}.',\n    'Unable to verify your data submission.' => 'Ní féidir d\\'aighneacht sonraí a fhíorú',\n    'Unknown alias: -{name}' => 'Ailias anaithnid: -{name}',\n    'Unknown filter attribute \"{attribute}\"' => 'Aitreabúid scagaire anaithnid \"{attribute}\"',\n    'Unknown option: --{name}' => 'Rogha anaithnid: --{name}',\n    'Update' => 'Nuashonrú',\n    'View' => 'Amharc',\n    'Yes' => 'Tá',\n    'You are not allowed to perform this action.' => 'Níl cead agat an gníomh seo a dhéanamh.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Is féidir leat a uaslódáil ar a mhéad {limit, number} {limit, plural, one{file} other{files}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => 'Ba chóir duit a uaslódáil ar a laghad {limit, number} {limit, plural, one{file} other{files}}.',\n    'in {delta, plural, =1{a day} other{# days}}' => 'isteach {delta, plural, =1{lá} other{# laethanta}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'isteach {delta, plural, =1{nóiméad} other{# nóiméad}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'isteach {delta, plural, =1{mí} other{# mhí}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'isteach {delta, plural, =1{dara} other{# soicind}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'isteach {delta, plural, =1{bliain} other{# blianta}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'isteach {delta, plural, =1{uair an chloig} other{# uair an chloig}}',\n    'just now' => 'díreach anois',\n    'the input value' => 'an luach ionchuir',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" Tá glactha cheana féin',\n    '{attribute} cannot be blank.' => '{attribute} Ní féidir a bheith bán.',\n    '{attribute} contains wrong subnet mask.' => '{attribute} Tá masc subnet mícheart.',\n    '{attribute} is invalid.' => '{attribute} neamhbhailí.',\n    '{attribute} is not a valid URL.' => '{attribute} nach URL bailí é.',\n    '{attribute} is not a valid email address.' => '{attribute} nach seoladh ríomhphoist bailí é.',\n    '{attribute} is not in the allowed range.' => '{attribute} nach bhfuil sa raon ceadaithe.',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} caithfidh go bhfuil \"{requiredValue}\".',\n    '{attribute} must be a number.' => '{attribute} caithfidh gur uimhir é.',\n    '{attribute} must be a string.' => '{attribute} Ní mór a bheith ina teaghrán.',\n    '{attribute} must be a valid IP address.' => '{attribute} caithfidh gur seoladh IP bailí é.',\n    '{attribute} must be an IP address with specified subnet.' => '{attribute} Ní mór seoladh IP a bheith ann le folíon sonraithe.',\n    '{attribute} must be an integer.' => '{attribute} Ní mór a bheith ina slánuimhir.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} Ní mór ceachtar \"{true}\" nó \"{false}\".',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '{attribute} Ní mór a bheith comhionann le \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} ní mór a bheith níos mó ná \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} a bheith níos mó ná nó cothrom le \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} Ní mór a bheith níos lú ná \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} ní mór a bheith níos lú ná nó cothrom le \"{compareValueOrAttribute}\".',\n    '{attribute} must be no greater than {max}.' => '{attribute} Ní mór a bheith níos mó ná {max}.',\n    '{attribute} must be no less than {min}.' => '{attribute} Ní mór a bheith níos lú ná{min}.',\n    '{attribute} must not be a subnet.' => '{attribute} Ní mór a bheith ina subnet.',\n    '{attribute} must not be an IPv4 address.' => '{attribute} níor cheart gur seoladh IPv4 é.',\n    '{attribute} must not be an IPv6 address.' => '{attribute} níor cheart gur seoladh IPv6 é.',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} Ní mór a bheith comhionann le \"{compareValueOrAttribute}\".',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} chóir go bhfuil ar a laghad {min, number} {min, plural, one{carachtar} other{carachtair}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} chóir go bhfuil ar a mhéad {max, number} {max, plural, one{carachtar} other{carachtair}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} chóir go bhfuil {length, number} {length, plural, one{carachtar} other{carachtair}}.',\n    '{compareAttribute} is invalid.' => '{compareAttribute} neamhbhailí.',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, =1{1 lá} eile{# laethanta}}',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, =1{1 uair an chloig} eile{# uair an chloig}}',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, =1{1 nóiméad} eile{# nóiméad}}',\n    '{delta, plural, =1{1 month} other{# months}}' => '{delta, plural, =1{1 mí} eile{# mhí}}',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta, plural, =1{1 dara} eile{# soicind}}',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta, plural, =1{1 bliain} eile{# blianta}}',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{1 lá} eile{# laethanta}} ó shin',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{1 nóiméad} eile{# nóiméad}} ó shin',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{1 mí} eile{# mhí}} ó shin',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{1 dara} eile{# soicind}} ó shin',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{1 bliain} eile{# blianta}} ó shin',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, =1{1 uair an chloig} eile{# uair an chloig}} ó shin',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '{nFormatted} GiB',\n    '{nFormatted} KiB' => '{nFormatted} KiB',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '{nFormatted} MiB',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, =1{beart} eile{bearta}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, =1{gibibheart} eile{gibítí}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, =1{gigabyte} eile{ghigibheart}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, =1{cibeibít} eile{cibítí}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, =1{cilibheart} eile{cilibheart}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, =1{meibít} eile{meibítí}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, =1{meigibheart} eile{megabytes}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, =1{peibít} eile{peibít}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, =1{peitibít} eile{peiteabóid}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, =1{teibít} eile{teibítí}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, =1{teiribíte} eile{teiribít}}',\n];\n"
  },
  {
    "path": "framework/messages/he/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => '',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(לא הוגדר)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'שגיאת שרת פנימית',\n    'Are you sure you want to delete this item?' => 'האם אתה בטוח שברצונך למחוק פריט זה?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'מחק',\n    'Error' => 'שגיאה',\n    'File upload failed.' => 'העלאת קובץ נכשלה',\n    'Home' => 'דף הבית',\n    'Invalid data received for parameter \"{param}\".' => 'התקבל ערך לא חוקי לפרמטר \"{param}\"',\n    'Login Required' => 'נדרשת הרשמה',\n    'Missing required arguments: {params}' => 'חסרים ארגומנטים נדרשים: {params}',\n    'Missing required parameters: {params}' => 'חסרים פרמטרים נדרשים: {params}',\n    'No' => 'לא',\n    'No results found.' => 'לא נמצאו תוצאות',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => '',\n    'Only files with these extensions are allowed: {extensions}.' => 'רק קבצים עם ההרחבות הבאות מותרים: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'דף לא נמצא',\n    'Please fix the following errors:' => 'בבקשה, תקן את השגיאות הבאות: ',\n    'Please upload a file.' => 'נא העלה קובץ.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'מציג <b>{begin, number}-{end, number}</b> מתוך <b>{totalCount, number}</b> {totalCount, plural, one{רשומה} other{רשומות}}.',\n    'The combination {values} of {attributes} has already been taken.' => '',\n    'The file \"{file}\" is not an image.' => 'הקובץ \"{file}\" אינו קובץ תמונה.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'הקובץ \"{file}\" גדול מדי. גודל זה אינו מצליח {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'הקובץ \"{file}\" קטן מדי. הקובץ אינו יכול להיות קטן מ {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'הפורמט של {attribute} אינו חוקי.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'התמונה \"{file}\" גדולה מדי. הגובה לא יכול להיות גדול מ {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'התמונה \"{file}\" גדולה מדי. הרוחב לא יכול להיות גדול מ {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'התמונה \"{file}\" קטנה מדי. הגובה לא יכול להיות קטן מ {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'התמונה \"{file}\" קטנה מדי. הרוחב לא יכול להיות קטן מ {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The requested view \"{name}\" was not found.' => '',\n    'The verification code is incorrect.' => 'קוד האימות אינו תקין.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'סך הכל <b>{count, number}</b> {count, plural, one{אייטם} other{אייטמים}}.',\n    'Unable to verify your data submission.' => 'אין אפשרות לאמת את המידע שהתקבל.',\n    'Unknown alias: -{name}' => '',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'Unknown option: --{name}',\n    'Update' => 'עדכון',\n    'View' => 'תצוגה',\n    'Yes' => 'כן',\n    'You are not allowed to perform this action.' => 'אינך מורשה לבצע את הפעולה הזו.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'אתה יכול להעלות לכל היותר {limit, number} {limit, plural, one{קובץ} other{קבצים}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => '',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => '',\n    'in {delta, plural, =1{a month} other{# months}}' => '',\n    'in {delta, plural, =1{a second} other{# seconds}}' => '',\n    'in {delta, plural, =1{a year} other{# years}}' => '',\n    'in {delta, plural, =1{an hour} other{# hours}}' => '',\n    'just now' => '',\n    'the input value' => 'הערך המוכנס',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" כבר בשימוש',\n    '{attribute} cannot be blank.' => '{attribute} לא יכול להיות ריק.',\n    '{attribute} contains wrong subnet mask.' => '',\n    '{attribute} is invalid.' => '{attribute} לא חוקי.',\n    '{attribute} is not a valid URL.' => '{attribute} איננו כתובת אינטרנט חוקית.',\n    '{attribute} is not a valid email address.' => '{attribute} לא כתובת מייל חוקית.',\n    '{attribute} is not in the allowed range.' => '',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} חייב להיות \"{requiredValue}\".',\n    '{attribute} must be a number.' => '{attribute} חייב להיות מספר',\n    '{attribute} must be a string.' => '{attribute} חייב להיות מחרוזת טקסט',\n    '{attribute} must be a valid IP address.' => '',\n    '{attribute} must be an IP address with specified subnet.' => '',\n    '{attribute} must be an integer.' => '{attribute} חייב להיות מספר שלם',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} חייב להיות \"{true}\" או \"{false}\".',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} חייב להיות גדול מ \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} חייב להיות גדול מ או שווה  \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} חייב להיות פחות מ \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} חייב להיות פחות מ או שווה \"{compareValueOrAttribute}\".',\n    '{attribute} must be no greater than {max}.' => '{attribute} חייב להיות לא יותר מ \"{max}\".',\n    '{attribute} must be no less than {min}.' => '{attribute} חייב להיות לא פחות מ \"{min}\".',\n    '{attribute} must not be a subnet.' => '',\n    '{attribute} must not be an IPv4 address.' => '',\n    '{attribute} must not be an IPv6 address.' => '',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} חייב להיות שווה ל \"{compareValueOrAttribute}\"',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} אמור לכלול לפחות {min, number} {min, plural, one{תו} other{תוים}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} אמור לא לכלול יותר מ{max, number} {max, plural, one{תו} other{תוים}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute}  אמור לכלול {length, number} {length, plural, one{תו} other{תוים}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '',\n    '{delta, plural, =1{1 month} other{# months}}' => '',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '',\n    '{delta, plural, =1{1 year} other{# years}}' => '',\n    '{delta, plural, =1{a day} other{# days}} ago' => '',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '',\n    '{delta, plural, =1{a month} other{# months}} ago' => '',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '',\n    '{delta, plural, =1{a year} other{# years}} ago' => '',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '',\n    '{nFormatted} B' => '',\n    '{nFormatted} GB' => '',\n    '{nFormatted} GiB' => '',\n    '{nFormatted} KiB' => '',\n    '{nFormatted} MB' => '',\n    '{nFormatted} MiB' => '',\n    '{nFormatted} PB' => '',\n    '{nFormatted} PiB' => '',\n    '{nFormatted} TB' => '',\n    '{nFormatted} TiB' => '',\n    '{nFormatted} kB' => '',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '',\n];\n"
  },
  {
    "path": "framework/messages/hi/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => ' और ',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(स्थापित नहीं)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'सर्वर में एक आंतरिक दोष उत्पन्न हुआ है।',\n    'Are you sure you want to delete this item?' => 'क्या आप सुनिश्चित रूप से इस आइटम को मिटाना चाहते हैं?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'मिटाएँ',\n    'Error' => 'खामी',\n    'File upload failed.' => 'फ़ाइल अपलोड असफल रहा।',\n    'Home' => 'घर',\n    'Invalid data received for parameter \"{param}\".' => 'पैरामीटर \"{param}\" के लिए प्राप्त डेटा अमान्य है।',\n    'Login Required' => 'लॉगिन आवश्यक हैं',\n    'Missing required arguments: {params}' => 'आवश्यक तर्क: {params} अनुपस्थित है',\n    'Missing required parameters: {params}' => 'आवश्यक पैरामीटर: {params} अनुपस्थित है',\n    'No' => 'नहीं',\n    'No results found.' => 'कोई परिणाम नहीं मिला।',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'केवल इन MIME प्रकारों वाली फ़ाइलों की अनुमति है: {mimeTypes}।',\n    'Only files with these extensions are allowed: {extensions}.' => 'केवल इन एक्सटेंशन वाली फाइलों की अनुमति है: {extensions}।',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'पृष्ठ नहीं मिला।',\n    'Please fix the following errors:' => 'कृपया निम्नलिखित खामीयां सुधारें:',\n    'Please upload a file.' => 'कृपया एक फ़ाइल अपलोड करें।',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'दिखाया गया है {totalCount, number} {totalCount, plural, one{चीज} other{चीज़े}} में से {begin, number}-{end, number} ।',\n    'The combination {values} of {attributes} has already been taken.' => '{attributes} और {values} का संयोजन पहले से ही लिया जा चुका है।',\n    'The file \"{file}\" is not an image.' => 'यह फ़ाइल \"{file}\" एक चित्र नहीं है।',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'यह फ़ाइल \"{file}\" बहुत बड़ी है। इसका आकार {formattedLimit} से अधिक नहीं हो सकता है।',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'यह फ़ाइल \"{file}\" बहुत छोटी है। इसका आकार {formattedLimit} से छोटा नहीं हो सकता।',\n    'The format of {attribute} is invalid.' => '{attribute} का प्रारूप गलत है।',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'यह चित्र \"{file}\" बहुत बड़ी है। ऊंचाई {limit, number} {limit, plural, one{पिक्सेल} other{पिक्सेल}} से बड़ी नहीं हो सकती।',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'यह चित्र \"{file}\" बहुत बड़ी है। चौड़ाई {limit, number} {limit, plural, one{पिक्सेल} other{पिक्सेल}} से बड़ी नहीं हो सकती।',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'यह चित्र \"{file}\" बहुत छोटी है। ऊंचाई {limit, number} {limit, plural, one{पिक्सेल} other{पिक्सेल}} से छोटी नहीं हो सकती।',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'यह चित्र \"{file}\" बहुत छोटी है। चौड़ाई {limit, number} {limit, plural, one{पिक्सेल} other{पिक्सेल}} से छोटी नहीं हो सकती।',\n    'The requested view \"{name}\" was not found.' => 'अनुरोधित दृश्य \"{name}\" नहीं मिला।',\n    'The verification code is incorrect.' => 'सत्यापन कोड गलत है।',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'कुल <b>{count, number}</b> {count, plural, one{चीज} other{चीज़े}}।',\n    'Unable to verify your data submission.' => 'आपके डेटा सबमिशन को सत्यापित करने में असमर्थ।',\n    'Unknown alias: -{name}' => 'अज्ञात उपनाम: - {name}',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'अज्ञात विकल्प: - {name}',\n    'Update' => 'अपडेट करें',\n    'View' => 'देखें',\n    'Yes' => 'हाँ',\n    'You are not allowed to perform this action.' => 'आपको यह करने की अनुमति नहीं है।',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'आप अधिकतम {limit, number} {limit, plural, one{फ़ाइल} other{फाइलें}} अपलोड कर सकते हैं।',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => '{delta, plural, =1{एक दिन} other{# दिनों}} में',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => '{delta, plural, =1{एक मिनट} other{# मिनटों}} में',\n    'in {delta, plural, =1{a month} other{# months}}' => '{delta, plural, =1{एक महीना} other{# महीनों}} में',\n    'in {delta, plural, =1{a second} other{# seconds}}' => '{delta, plural, =1{एक सेकन्ड} other{# सेकन्डे}} में',\n    'in {delta, plural, =1{a year} other{# years}}' => '{delta, plural, =1{एक वर्ष} other{# वर्षों}} में',\n    'in {delta, plural, =1{an hour} other{# hours}}' => '{delta, plural, =1{एक घंटा} other{# घंटे}} में',\n    'just now' => 'अभी',\n    'the input value' => 'इनपुट मूल्य',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" पहले से ही ले लिया गया है।',\n    '{attribute} cannot be blank.' => '{attribute} खाली नहीं हो सकता।',\n    '{attribute} contains wrong subnet mask.' => '{attribute} में गलत सबनेट मास्क है।',\n    '{attribute} is invalid.' => '{attribute} अमान्य है।',\n    '{attribute} is not a valid URL.' => '{attribute} एक मान्य URL नहीं है।',\n    '{attribute} is not a valid email address.' => '{attribute} एक मान्य ईमेल एड्रेस नहीं है।',\n    '{attribute} is not in the allowed range.' => '{attribute} अनुमत सीमा में नहीं है।',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} \"{requiredValue}\" होना चाहिए।',\n    '{attribute} must be a number.' => '{attribute} एक संख्या होनी चाहिए।',\n    '{attribute} must be a string.' => '{attribute} एक या ज़्यादा अक्षर होने चाहिए।',\n    '{attribute} must be a valid IP address.' => '{attribute} एक वैध IP एड्रेस होना चाहिए।',\n    '{attribute} must be an IP address with specified subnet.' => '{attribute} निर्दिष्ट सबनेट वाला IP एड्रेस होना चाहिए।',\n    '{attribute} must be an integer.' => '{attribute} एक पूर्णांक होनी चाहिए।',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} या तो \"{true}\" या \"{false}\" होनी चाहिए।',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '{attribute} \"{compareValueOrAttribute}\" के बराबर होनी चाहिए।',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} \"{compareValueOrAttribute}\" से अधिक होनी चाहिए।',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '\"{attribute}\" {compareValueOrAttribute} \"से अधिक या बराबर होनी चाहिए।',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} \"{compareValueOrAttribute}\" से कम होनी चाहिए।',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '\"{attribute}\" {compareValueOrAttribute} \"से कम या बराबर होनी चाहिए।',\n    '{attribute} must be no greater than {max}.' => '{attribute} {max} से अधिक नहीं होनी चाहिए।',\n    '{attribute} must be no less than {min}.' => '{attribute} {min} से कम नहीं होनी चाहिए।',\n    '{attribute} must not be a subnet.' => '{attribute} सबनेट नहीं होना चाहिए।',\n    '{attribute} must not be an IPv4 address.' => '{attribute} IPv4 एड्रेस नहीं होना चाहिए।',\n    '{attribute} must not be an IPv6 address.' => '{attribute} IPv6 एड्रेस नहीं होना चाहिए।',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} को \"{compareValueOrAttribute}\" के बराबर नहीं होना चाहिए।',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} में कम से कम {min, number} {min, plural, one{अक्षर} other{अक्षर}} होना चाहिए।',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} में अधिकतम {max, number} {max, plural, one{अक्षर} other{अक्षर}} होना चाहिए।',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} में {length, number} {length, plural, one{अक्षर} other{अक्षर}} शामिल होना चाहिए।',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, =1{1 दिन} other{# दिन}}',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, =1{1 घंटा} other{# घंटे}}',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, =1{1 मिनट} other{# मिनिटे}}',\n    '{delta, plural, =1{1 month} other{# months}}' => '{delta, plural, =1{1 महीना} other{# महीने}}',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta, plural, =1{1 सेकंड} other{# सेकंड}}',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta, plural, =1{1 वर्ष} other{# वर्ष}}',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{एक दिन} other{# दिन}} पहले',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{एक मिनट} other{# मिनट}} पहले',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{एक महीना} other{# महीने}} पहले',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{एक सेकंड} other{# सेकंड}} पहले',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{एक वर्ष} other{# वर्ष}} पहले',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, =1{एक घंटा} other{# घंटे}} पहले',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '{nFormatted} GiB',\n    '{nFormatted} KiB' => '{nFormatted} KiB',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '{nFormatted} MiB',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} KB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, =1{बाइट} other{बाइट्स}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, =1{गिबिबाइट} other{गिबिबाइटस}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, =1{गीगाबाइट} other{गीगाबाइटस}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, =1{किबिबाइट} other{किबिबाइटस}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, =1{किलोबाइट} other{किलोबाइट्स}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, =1{मेबीबाइट} other{मेबीबाइटस}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, =1{मेगाबाइट} other{मेगाबाइट्स}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, =1{पेबिबाइट} other{पेबिबाइटस}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, =1{पेटाबाइट} other{पेटाबाइट्स}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, =1{तबीबाइट} other{तबीबाइटस}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, =1{टेराबाइट} other{टेराबाइट्स}}',\n];\n"
  },
  {
    "path": "framework/messages/hr/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => '',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(nije postavljeno)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Došlo je do interne pogreške servera.',\n    'Are you sure you want to delete this item?' => '',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'Obrisati',\n    'Error' => 'Pogreška',\n    'File upload failed.' => 'Upload datoteke nije uspio.',\n    'Home' => 'Home',\n    'Invalid data received for parameter \"{param}\".' => 'Nevažeći podaci primljeni za parametar \"{param}\"',\n    'Login Required' => 'Prijava potrebna',\n    'Missing required arguments: {params}' => 'Nedostaju potrebni argunenti: {params}',\n    'Missing required parameters: {params}' => 'Nedostaju potrebni parametri: {params}',\n    'No' => 'Ne',\n    'No results found.' => 'Nema rezultata.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Samo datoteke s ovim MIME vrstama su dopuštene: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Samo datoteke s ovim ekstenzijama su dopuštene: {extensions}',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Stranica nije pronađena.',\n    'Please fix the following errors:' => 'Molimo vas ispravite pogreške:',\n    'Please upload a file.' => 'Molimo vas da uploadate datoteku.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Prikazuj  <b>{begin, number}-{end, number}</b> od <b>{totalCount, number}</b> {totalCount, plural, one{stavka} few{stavke} many{stavki} other{stavki}}.',\n    'The combination {values} of {attributes} has already been taken.' => '',\n    'The file \"{file}\" is not an image.' => 'Datoteka \"{file}\" nije slika.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'Datoteka \"{file}\" je prevelika. Ne smije biti veća od {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Datoteka \"{file}\" je premalena. Ne smije biti manja od {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'Format od {attribute} je nevažeći.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Slika \"{file}\" je prevelika. Visina slike ne smije biti veća od {limit, number} {limit, plural, one{piksel} other{piksela}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Slika \"{file}\" je prevelika. Širina slike ne smije biti veća od {limit, number} {limit, plural, one{piksel} other{piksela}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Slika  \"{file}\" je premalena. Visina slike ne smije biti manja od {limit, number} {limit, plural, one{piksel} other{piksela}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Slika \"{file}\" je premalena.  Širina slike ne smije biti manja od {limit, number} {limit, plural, one{piksel} other{piksela}}.',\n    'The requested view \"{name}\" was not found.' => '',\n    'The verification code is incorrect.' => 'Kod za provjeru nije točan.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Ukupno <b>{count, number}</b> {count, plural, =1{stavka} one{# stavka} few{# stavke} many{# stavki} other{# stavki}}.',\n    'Unable to verify your data submission.' => 'Nije moguće provjeriti poslane podatke.',\n    'Unknown alias: -{name}' => '',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'Nepoznata opcija: --{name}',\n    'Update' => 'Uredi',\n    'View' => 'Pregled',\n    'Yes' => 'Da',\n    'You are not allowed to perform this action.' => 'Nije vam dopušteno obavljati tu radnju.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Najviše možete uploadat {limit, number} {limit, plural, one{datoteku} few{datoteke} other{datoteka}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => 'u {delta, plural, =1{dan} one{# dan} few{# dana} many{# dana} other{# dana}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'u {delta, plural, =1{minuta} one{# minuta} few{# minute} many{# minuta} other{# minuta}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'u {delta, plural, =1{mjesec} one{# mjesec} few{# mjeseca} many{# mjeseci} other{# mjeseci}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'u {delta, plural, =1{sekunda} one{# sekunda} few{# sekunde} many{# sekundi} other{# sekundi}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'u {delta, plural, =1{godina} one{# godine} few{# godine} many{# godina} other{# godina}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'u {delta, plural, =1{sat} one{# sat} few{# sata} many{# sati} other{# sati}}',\n    'just now' => '',\n    'the input value' => 'ulazna vrijednost',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" već se koristi.',\n    '{attribute} cannot be blank.' => '{attribute} ne smije biti prazan.',\n    '{attribute} contains wrong subnet mask.' => '',\n    '{attribute} is invalid.' => 'Atribut \"{attribute}\" je neispravan.',\n    '{attribute} is not a valid URL.' => '{attribute} nije valjan URL.',\n    '{attribute} is not a valid email address.' => '{attribute} nije valjana email adresa.',\n    '{attribute} is not in the allowed range.' => '',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} mora biti \"{requiredValue}\".',\n    '{attribute} must be a number.' => '{attribute} mora biti broj.',\n    '{attribute} must be a string.' => '{attribute} mora biti string(riječ,tekst).',\n    '{attribute} must be a valid IP address.' => '',\n    '{attribute} must be an IP address with specified subnet.' => '',\n    '{attribute} must be an integer.' => '{attribute} mora biti cijeli broj.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} mora biti \"{true}\" ili \"{false}\".',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} mora biti veći od \"{compareValueOrAttribute}',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} mora biti veći ili jednak \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} mora biti manji od \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} mora biti jednak \"{compareValueOrAttribute}\".',\n    '{attribute} must be no greater than {max}.' => '{attribute} ne smije biti veći od {max}.',\n    '{attribute} must be no less than {min}.' => '{attribute} ne smije biti manji od {min}.',\n    '{attribute} must not be a subnet.' => '',\n    '{attribute} must not be an IPv4 address.' => '',\n    '{attribute} must not be an IPv6 address.' => '',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} ne smije biti jednak \"{compareValueOrAttribute}\".',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} mora najmanje sadržavati {min, number} {min, plural, =1{znak} one{znak} few{znaka} many{znakova} other{znakova}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} moze sadržavati najviše do {max, number} {max, plural, =1{znak} one{znak} few{znaka} many{znakova} other{znakova}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} mora sadržavati {length, number} {length, plural, =1{znak} one{znak} few{znaka} many{znakova} other{znakova}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '',\n    '{delta, plural, =1{1 month} other{# months}}' => '',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '',\n    '{delta, plural, =1{1 year} other{# years}}' => '',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{dan} one{# dan} few{# dana} many{# dana} other{# dana}}',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{minuta} one{# minuta} few{# minute} many{# minuta} other{# minuta}}',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{mjesec} one{# mjesec} few{# mjeseca} many{# mjeseci} other{# mjeseci}}',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{sekunda} one{# sekunda} few{# sekunde} many{# sekundi} other{# sekundi}}',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{godina} one{# godine} few{# godine} many{# godina} other{# godina}}',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => ' {delta, plural, =1{sat} one{# sat} few{# sata} many{# sati} other{# sati}}',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '',\n    '{nFormatted} KiB' => '',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, =1{bajt} other{bajta}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, =1{gigabajt} other{gigabajta}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, =1{kilobajt} other{kilobajta}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, =1{megabajt} other{megabajta}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, =1{petabajt} other{petabajta}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, =1{terabajt} other{terabajta}}',\n];\n"
  },
  {
    "path": "framework/messages/hu/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => '',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(nincs beállítva)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Egy belső szerver hiba történt.',\n    'Are you sure you want to delete this item?' => 'Biztos benne, hogy törli ezt az elemet?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'Törlés',\n    'Error' => 'Hiba',\n    'File upload failed.' => 'A fájlfeltöltés nem sikerült.',\n    'Home' => 'Főoldal',\n    'Invalid data received for parameter \"{param}\".' => 'Érvénytelen paraméter: {param}.',\n    'Login Required' => 'Bejelentkezés szükséges',\n    'Missing required arguments: {params}' => 'Hiányzó argumentumok: {params}',\n    'Missing required parameters: {params}' => 'Hiányzó paraméterek: {params}',\n    'No' => 'Nem',\n    'No results found.' => 'Nincs találat.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Csak a következő MIME típusú fájlok engedélyezettek: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Csak a következő kiterjesztésű fájlok engedélyezettek: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Az oldal nem található.',\n    'Please fix the following errors:' => 'Kérjük javítsa a következő hibákat:',\n    'Please upload a file.' => 'Kérjük töltsön fel egy fájlt.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => '<b>{begin, number}-{end, number}</b> megjelenítése a(z) <b>{totalCount, number}</b> elemből.',\n    'The combination {values} of {attributes} has already been taken.' => '',\n    'The file \"{file}\" is not an image.' => '\"{file}\" nem egy kép.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => '\"{file}\" túl nagy. A mérete nem lehet nagyobb {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => '\"{file}\" túl kicsi. A mérete nem lehet kisebb {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'A(z) {attribute} formátuma érvénytelen.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'A(z) \"{file}\" kép túl nagy. A magassága nem lehet nagyobb {limit, number} pixelnél.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'A(z) \"{file}\" kép túl nagy. A szélessége nem lehet nagyobb {limit, number} pixelnél.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'A(z) \"{file}\" kép túl kicsi. A magassága nem lehet kisebb {limit, number} pixelnél.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'A(z) \"{file}\" kép túl kicsi. A szélessége nem lehet kisebb {limit, number} pixelnél.',\n    'The requested view \"{name}\" was not found.' => 'A kért \"{name}\" nézet nem található.',\n    'The verification code is incorrect.' => 'A megerősítő kód helytelen.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Összesen <b>{count, number}</b> elem.',\n    'Unable to verify your data submission.' => 'Nem sikerült ellenőrizni az adatokat.',\n    'Unknown alias: -{name}' => '',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'Ismeretlen kapcsoló: --{name}',\n    'Update' => 'Szerkesztés',\n    'View' => 'Megtekintés',\n    'Yes' => 'Igen',\n    'You are not allowed to perform this action.' => 'Nincs jogosultsága a művelet végrehajtásához.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Legfeljebb {limit, number} fájlt tölthet fel.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => '{delta} napon belül',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => '{delta} percen belül',\n    'in {delta, plural, =1{a month} other{# months}}' => '{delta} hónapon belül',\n    'in {delta, plural, =1{a second} other{# seconds}}' => '{delta} másodpercen belül',\n    'in {delta, plural, =1{a year} other{# years}}' => '{delta} éven belül',\n    'in {delta, plural, =1{an hour} other{# hours}}' => '{delta} órán belül',\n    'just now' => 'éppen most',\n    'the input value' => 'a beviteli érték',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" már használatban van.',\n    '{attribute} cannot be blank.' => '{attribute} nem lehet üres.',\n    '{attribute} contains wrong subnet mask.' => '',\n    '{attribute} is invalid.' => '{attribute} érvénytelen.',\n    '{attribute} is not a valid URL.' => '{attribute} nem valódi URL.',\n    '{attribute} is not a valid email address.' => '{attribute} nem valódi e-mail cím.',\n    '{attribute} is not in the allowed range.' => '',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} értéke \"{requiredValue}\" kell, hogy legyen.',\n    '{attribute} must be a number.' => '{attribute} csak szám lehet.',\n    '{attribute} must be a string.' => '{attribute} csak szöveg lehet.',\n    '{attribute} must be a valid IP address.' => '',\n    '{attribute} must be an IP address with specified subnet.' => '',\n    '{attribute} must be an integer.' => '{attribute} csak egész szám lehet.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} csak \"{true}\" vagy \"{false}\" lehet.',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '\"{attribute}\" mezőnek azonosnak kell lennie a \"{compareValueOrAttribute}\" mezőbe írtakkal.',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} nagyobbnak kell lennie, mint \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} nagyobb vagy egyenlő kell legyen, mint \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} kisebbnek kell lennie, mint \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} kisebb vagy egyenlő kell legyen, mint \"{compareValueOrAttribute}\".',\n    '{attribute} must be no greater than {max}.' => '{attribute} nem lehet nagyobb, mint {max}.',\n    '{attribute} must be no less than {min}.' => '{attribute} nem lehet kisebb, mint {min}.',\n    '{attribute} must not be a subnet.' => '',\n    '{attribute} must not be an IPv4 address.' => '',\n    '{attribute} must not be an IPv6 address.' => '',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} nem lehet egyenlő ezzel: \"{compareValueOrAttribute}\".',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} minimum {min, number} karakter kell, hogy legyen.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} maximum {max, number} karakter lehet.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} pontosan {length, number} kell, hogy legyen.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '',\n    '{delta, plural, =1{1 month} other{# months}}' => '',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '',\n    '{delta, plural, =1{1 year} other{# years}}' => '',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta} nappal ezelőtt',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta} perccel ezelőtt',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta} hónappal ezelőtt',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta} másodperccel ezelőtt',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta} évvel ezelőtt',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta} órával ezelőtt',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '',\n    '{nFormatted} KiB' => '',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} bájt',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} gigabájt',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} kilobájt',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} megabájt',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} petabájt',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} terabájt',\n];\n"
  },
  {
    "path": "framework/messages/hy/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => ' և ',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '«{attribute}»-ը չի սպասարկում «{operator}» օպերատորը։',\n    '(not set)' => '(չի նշված)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Սերվերի ներքին սխալ է տեղի ունեցել։',\n    'Are you sure you want to delete this item?' => 'Վստա՞հ եք, որ ցանկանում եք ջնջել այս տարրը:',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '«{attribute}»-ի համար պետք է լինի արժեք կամ գործող օպերատորի հստակեցում:',\n    'Delete' => 'Ջնջել',\n    'Error' => 'Սխալ',\n    'File upload failed.' => 'Ֆայլի վերբեռնումը ձախողվեց։',\n    'Home' => 'Գլխավոր',\n    'Invalid data received for parameter \"{param}\".' => '«{param}» պարամետրի համար ստացվել է անվավեր տվյալ։',\n    'Login Required' => 'Մուտքը պարտադիր է',\n    'Missing required arguments: {params}' => 'Բացակայում են պահանջվող արգումենտները՝ {params}',\n    'Missing required parameters: {params}' => 'Բացակայում են պահանջվող պարամետրերը՝ {params}',\n    'No' => 'Ոչ',\n    'No results found.' => 'Ոչ մի արդյունք չի գտնվել։',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Թույլատրվում են միայն {mimeTypes} MIME տեսակի ֆայլերը։',\n    'Only files with these extensions are allowed: {extensions}.' => 'Թույլատրվում են միայն {extensions} վերջավորությամբ ֆայլերը։',\n    'Operator \"{operator}\" must be used with a search attribute.' => '«{operator}» օպերատորը պետք է օգտագործվի որոնման ատրիբուտի հետ միասին:',\n    'Operator \"{operator}\" requires multiple operands.' => '«{operator}» օպերատորը պահանջում բազմակի օպերանդներ։',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Էջը չի գտնվել։',\n    'Please fix the following errors:' => 'Խնդրում ենք ուղղել հետևյալ սխալները՝',\n    'Please upload a file.' => 'Խնդրում ենք վերբեռնել ֆայլ:',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Ցուցադրված են <b>{begin, number}-ից {end, number}-ը</b> ընդհանուր <b>{totalCount, number}-ից</b>։',\n    'The combination {values} of {attributes} has already been taken.' => '{attributes}-ի {values} արժեքների կոմբինացիան արդեն զբաղված է։',\n    'The file \"{file}\" is not an image.' => '«{file}» ֆայլը վավեր նկար չէ։',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => '«{file}» ֆայլը շատ մեծ է։ Նրա չափը չի կարող գերազանցել {formattedLimit}-ը։',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => '«{file}» ֆայլը շատ փոքր է։ Նրա չափը չի կարող լինել {formattedLimit}-ից փոքր։',\n    'The format of {attribute} is invalid.' => '{attribute}-ի ֆորմատը անվավեր է։',\n    'The format of {filter} is invalid.' => '{filter}-ի ֆորմատը անվավեր է։',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '«{file}» նկարը շատ մեծ է։ Նրա բարձրությունը չի կարող գերազանցել {limit, number} {limit, plural, one{պիքսել} few{պիքսել} many{պիքսել} other{պիքսել}}ը։',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '«{file}» նկարը շատ մեծ է։ Նրա երկարությունը չի կարող գերազանցել {limit, number} {limit, plural, one{պիքսել} few{պիքսել} many{պիքսել} other{պիքսել}}ը։',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '«{file}» նկարը շատ փոքր է։ Նրա բարձրությունը չի կարող լինել {limit, number} {limit, plural, one{պիքսել} few{պիքսել} many{պիքսել} other{պիքսել}}ից փոքր։',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '«{file}» նկարը շատ փոքր է։ Նրա երկարությունը չի կարող լինել {limit, number} {limit, plural, one{պիքսել} few{պիքսել} many{պիքսել} other{պիքսել}}ից փոքր։',\n    'The requested view \"{name}\" was not found.' => 'Պահանջվող «{name}» տեսքի ֆալյը չի գտնվել։',\n    'The verification code is incorrect.' => 'Հաստատման կոդը սխալ է:',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Ընդհանուր <b>{count, number}</b> {count, plural, one{տարր} other{տարր}}։',\n    'Unable to verify your data submission.' => 'Հնարավոր չէ ստուգել ձեր տվյալների ներկայացումը:',\n    'Unknown alias: -{name}' => 'Անհայտ այլանուն՝ «{name}»։',\n    'Unknown filter attribute \"{attribute}\"' => 'Անհայտ ֆիլտրի ատրիբուտ՝ «{attribute}»։',\n    'Unknown option: --{name}' => 'Անհայտ տարբերակ՝ «{name}»։',\n    'Update' => 'Թարմացնել',\n    'View' => 'Դիտել',\n    'Yes' => 'Այո',\n    'You are not allowed to perform this action.' => 'Ձեզ չի թույլատրվում կատարել այս գործողությունը:',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Դուք կարող եք վերբեռնել առավելագույնը {limit, number} {limit, plural, one{ֆայլ} few{ֆայլ} many{ֆայլ} other{ֆայլ}}։',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => '{delta, plural, =1{օր} one{# օր} few{# օր} many{# օր} other{# օր}}ից',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => '{delta, plural, =1{րոպե} one{# րոպե} few{# րոպե} many{# րոպե} other{# րոպե}}ից',\n    'in {delta, plural, =1{a month} other{# months}}' => '{delta, plural, =1{ամս} one{# ամս} few{# ամս} many{# ամս} other{# ամս}}ից',\n    'in {delta, plural, =1{a second} other{# seconds}}' => '{delta, plural, =1{վայրկյան} one{# վայրկյան} few{# վայրկյան} many{# վայրկյան} other{# վայրկյան}}ից',\n    'in {delta, plural, =1{a year} other{# years}}' => '{delta, plural, =1{տար} one{# տար} few{# տար} many{# տար} other{# տար}}ուց',\n    'in {delta, plural, =1{an hour} other{# hours}}' => '{delta, plural, =1{ժամ} one{# ժամ} few{# ժամ} many{# ժամ} other{# ժամ}}ից',\n    'just now' => 'հենց հիմա',\n    'the input value' => 'մուտքային արժեքը',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} «{value}» արդեն զբաղված է։',\n    '{attribute} cannot be blank.' => '{attribute}-ի արժեքը չի կարող դատարկ լինել։',\n    '{attribute} contains wrong subnet mask.' => '{attribute}-ի արժեքը պարունակում է սխալ ենթացանցի ծածկույթ։',\n    '{attribute} is invalid.' => '{attribute}-ի արժեքը անվավեր է։',\n    '{attribute} is not a valid URL.' => '{attribute}-ի արժեքը վավեր URL չէ:',\n    '{attribute} is not a valid email address.' => '{attribute}-ի արժեքը վավեր էլ․փոստի հասցե չէ:',\n    '{attribute} is not in the allowed range.' => '{attribute}-ի արժեքը թույլատրված միջակայքում չէ։',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute}-ի արժեքը պետք է լինի «{requiredValue}»։',\n    '{attribute} must be a number.' => '{attribute}-ի արժեքը պետք է լինի թիվ։',\n    '{attribute} must be a string.' => '{attribute}-ի արժեքը պետք է լինի տող։',\n    '{attribute} must be a valid IP address.' => '{attribute}-ի արժեքը պետք է լինի վավեր IP հասցե։',\n    '{attribute} must be an IP address with specified subnet.' => '{attribute}-ի արժեքը պետք է լինի նշված ենթացանցի IP հասցե։',\n    '{attribute} must be an integer.' => '{attribute}-ի արժեքը պետք է լինի ամբողջ թիվ։',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute}-ի արժեքը պետք է լինի «{true}» կամ «{false}»։',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '{attribute}-ի արժեքը պետք է հավասար լինի «{compareValueOrAttribute}»-ին։',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute}-ի արժեքը պետք է մեծ լինի «{compareValueOrAttribute}»-ից։',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute}-ի արժեքը պետք է մեծ կամ հավասար լինի «{compareValueOrAttribute}»-ին։',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute}-ի արժեքը պետք է փոքր լինի «{compareValueOrAttribute}»-ից։',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute}-ի արժեքը պետք է փոքր կամ հավասար լինի «{compareValueOrAttribute}»-ին։',\n    '{attribute} must be no greater than {max}.' => '{attribute}-ի արժեքը պետք է չգերազանցի {max}-ը։',\n    '{attribute} must be no less than {min}.' => '{attribute}-ի արժեքը պետք է գերազանցի {min}-ը։',\n    '{attribute} must not be a subnet.' => '{attribute}-ի արժեքը պետք է չլինի ենթացանց։',\n    '{attribute} must not be an IPv4 address.' => '{attribute}-ի արժեքը պետք է չլինի IPv4 հասցե։',\n    '{attribute} must not be an IPv6 address.' => '{attribute}-ի արժեքը պետք է չլինի IPv6 հասցե։',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute}-ի արժեքը պետք է հավասար չլինի «{compareValueOrAttribute}»-ին։',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute}-ի արժեքը պետք է պարունակի առնվազն {min, number} {min, plural, one{նիշ} other{նիշ}}։',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute}-ի արժեքը պետք է պարունակի առավելագույնը {max, number} {max, plural, one{նիշ} other{նիշ}}։',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute}-ի արժեքը պետք է պարունակի {length, number} {length, plural, one{նիշ} other{նիշ}}։',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, =1{1 օր} other{# օր}}',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, =1{1 ժամ} other{# ժամ}}',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, =1{1 րոպե} other{# րոպե}}',\n    '{delta, plural, =1{1 month} other{# months}}' => '{delta, plural, =1{1 ամիս} other{# ամիս}}',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta, plural, =1{1 վայրկյան} other{# վայրկյան}}',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta, plural, =1{1 տարի} other{# տարի}}',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{a օր} other{# օր}} առաջ',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{րոպե} other{րոպե}} առաջ',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{ամիս} other{ամիս}} առաջ',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{վայրկյան} other{# վայրկյան}} առաջ',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{տարի} other{# տարի}} առաջ',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, =1{ժամ} other{# ժամ}} առաջ',\n    '{nFormatted} B' => '{nFormatted} Բ',\n    '{nFormatted} GB' => '{nFormatted} ԳԲ',\n    '{nFormatted} GiB' => '{nFormatted} ԳիԲ',\n    '{nFormatted} KiB' => '{nFormatted} ԿիԲ',\n    '{nFormatted} MB' => '{nFormatted} ՄԲ',\n    '{nFormatted} MiB' => '{nFormatted} ՄիԲ',\n    '{nFormatted} PB' => '{nFormatted} ՊԲ',\n    '{nFormatted} PiB' => '{nFormatted} ՊիԲ',\n    '{nFormatted} TB' => '{nFormatted} ՏԲ',\n    '{nFormatted} TiB' => '{nFormatted} ՏիԲ',\n    '{nFormatted} kB' => '{nFormatted} ԿԲ',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, =1{բայթ} other{բայթ}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, =1{գիգաբիթ} other{գիգաբիթ}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, =1{գիգաբայթ} other{գիգաբայթ}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, =1{կիբիբայթ} other{կիբիբայթ}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, =1{կիլոբայթ} other{կիլոբայթ}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, =1{մեբիբայթ} other{մեբիբայթ}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, =1{մեգաբայթ} other{մեգաբայթ}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, =1{պեբիբայթ} other{պեբիբայթ}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, =1{պետաբայթ} other{պետաբայթ}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, =1{տեբիբայթ} other{տեբիբայթ}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, =1{տերաբայթ} other{տերաբայթ}}',\n];\n"
  },
  {
    "path": "framework/messages/id/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => '',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(belum diset)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Terjadi kesalahan internal server.',\n    'Are you sure you want to delete this item?' => 'Apakah Anda yakin ingin menghapus item ini?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'Hapus',\n    'Error' => 'Kesalahan',\n    'File upload failed.' => 'Mengunggah berkas gagal.',\n    'Home' => 'Beranda',\n    'Invalid data received for parameter \"{param}\".' => 'Data yang diterima tidak valid untuk parameter \"{param}\"',\n    'Login Required' => 'Diperlukan login',\n    'Missing required arguments: {params}' => 'Argumen yang diperlukan tidak ada: {params}',\n    'Missing required parameters: {params}' => 'Parameter yang diperlukan tidak ada: {params}',\n    'No' => 'Tidak',\n    'No results found.' => 'Tidak ada data yang ditemukan.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Hanya berkas dengan tipe MIME ini yang diperbolehkan: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Hanya berkas dengan ekstensi ini yang diperbolehkan: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Halaman tidak ditemukan.',\n    'Please fix the following errors:' => 'Silahkan perbaiki kesalahan berikut:',\n    'Please upload a file.' => 'Silahkan mengunggah berkas.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Menampilkan <b>{begin, number}-{end, number}</b> dari <b>{totalCount, number}</b> {totalCount, plural, one{item} other{item}}.',\n    'The combination {values} of {attributes} has already been taken.' => 'Kombinasi {values} dari {attributes} telah dipergunakan.',\n    'The file \"{file}\" is not an image.' => 'File bukan berupa gambar.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'Berkas \"{file}\" terlalu besar. Ukurannya tidak boleh lebih besar dari {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Berkas \"{file}\" terlalu kecil. Ukurannya tidak boleh lebih kecil dari {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'Format dari {attribute} tidak valid.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Gambar \"{file}\" terlalu besar. Tingginya tidak boleh lebih besar dari {limit, number} {limit, plural, one{piksel} other{piksel}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Gambar \"{file}\" terlalu besar. Lebarnya tidak boleh lebih besar dari  {limit, number} {limit, plural, one{piksel} other{piksel}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Gambar  \"{file}\" terlalu kecil. Tingginya tidak boleh lebih kecil dari {limit, number} {limit, plural, one{piksel} other{piksel}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Gambar \"{file}\" terlalu kecil. Lebarnya tidak boleh lebih kecil dari {limit, number} {limit, plural, one{piksel} other{piksel}}.',\n    'The requested view \"{name}\" was not found.' => 'View \"{name}\" yang diminta tidak ditemukan.',\n    'The verification code is incorrect.' => 'Kode verifikasi tidak benar.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Total <b>{count, number}</b> {count, plural, one{item} other{item}}.',\n    'Unable to verify your data submission.' => 'Tidak dapat mem-verifikasi pengiriman data Anda.',\n    'Unknown alias: -{name}' => '',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'Opsi tidak dikenal: --{name}',\n    'Update' => 'Ubah',\n    'View' => 'Lihat',\n    'Yes' => 'Ya',\n    'You are not allowed to perform this action.' => 'Anda tidak diperbolehkan untuk melakukan aksi ini.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Anda dapat mengunggah paling banyak {limit, number} {limit, plural, one{file} other{file}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => 'dalam {delta, plural, =1{satu hari} other{# hari}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'dalam {delta, plural, =1{satu menit} other{# menit}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'dalam {delta, plural, =1{satu bulan} other{# bulan}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'dalam {delta, plural, =1{satu detik} other{# detik}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'dalam {delta, plural, =1{satu tahun} other{# tahun}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'dalam {delta, plural, =1{satu jam} other{# jam}}',\n    'just now' => '',\n    'the input value' => 'nilai input',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" telah dipergunakan.',\n    '{attribute} cannot be blank.' => '{attribute} tidak boleh kosong.',\n    '{attribute} contains wrong subnet mask.' => '',\n    '{attribute} is invalid.' => '{attribute} tidak valid.',\n    '{attribute} is not a valid URL.' => '{attribute} bukan URL yang valid.',\n    '{attribute} is not a valid email address.' => '{attribute} bukan alamat email yang valid.',\n    '{attribute} is not in the allowed range.' => '',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} harus berupa \"{requiredValue}\".',\n    '{attribute} must be a number.' => '{attribute} harus berupa angka.',\n    '{attribute} must be a string.' => '{attribute} harus berupa string.',\n    '{attribute} must be a valid IP address.' => '',\n    '{attribute} must be an IP address with specified subnet.' => '',\n    '{attribute} must be an integer.' => '{attribute} harus berupa integer.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} harus berupa \"{true}\" atau \"{false}\".',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} harus lebih besar dari \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} harus lebih besar dari atau sama dengan \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} harus lebih kecil dari \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} harus lebih kecil dari atau sama dengan \"{compareValueOrAttribute}\".',\n    '{attribute} must be no greater than {max}.' => '{attribute} harus tidak boleh lebih besar dari {max}.',\n    '{attribute} must be no less than {min}.' => '{attribute} harus tidak boleh lebih kecil dari {min}.',\n    '{attribute} must not be a subnet.' => '',\n    '{attribute} must not be an IPv4 address.' => '',\n    '{attribute} must not be an IPv6 address.' => '',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} harus tidak boleh sama dengan \"{compareValueOrAttribute}\".',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} harus memiliki paling sedikit {min, number} {min, plural, one{karakter} other{karakter}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} harus memiliki paling banyak {max, number} {max, plural, one{karakter} other{karakter}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} harus memiliki {length, number} {length, plural, one{karakter} other{karakter}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '',\n    '{delta, plural, =1{1 month} other{# months}}' => '',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '',\n    '{delta, plural, =1{1 year} other{# years}}' => '',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{satu hari} other{# hari}} yang lalu',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{satu menit} other{# menit}} yang lalu',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{satu bulan} other{# bulan}} yang lalu',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{satu detik} other{# detik}} yang lalu',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{satu tahun} other{# tahun}} yang lalu',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, =1{satu jam} other{# jam}} yang lalu',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '',\n    '{nFormatted} KiB' => '',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, =1{bita} other{bita}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, =1{gigabita} other{gigabita}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, =1{kilobita} other{kilobita}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, =1{megabita} other{megabita}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, =1{petabita} other{petabita}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, =1{petabita} other{petabita}}',\n];\n"
  },
  {
    "path": "framework/messages/it/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => '',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(nessun valore)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Si è verificato un errore interno',\n    'Are you sure you want to delete this item?' => 'Sei sicuro di voler eliminare questo elemento?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'Elimina',\n    'Error' => 'Errore',\n    'File upload failed.' => 'Upload file fallito.',\n    'Home' => 'Home',\n    'Invalid data received for parameter \"{param}\".' => 'Dati ricevuti non corretti per il parametro \"{param}\".',\n    'Login Required' => 'Login richiesto',\n    'Missing required arguments: {params}' => 'Il seguente argomento è mancante: {params}',\n    'Missing required parameters: {params}' => 'Il seguente parametro è mancante: {params}',\n    'No' => 'No',\n    'No results found.' => 'Nessun risultato trovato',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Solo i file con questi tipi MIME sono consentiti: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Solo i file con queste estensioni sono permessi: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Pagina non trovata.',\n    'Please fix the following errors:' => 'Per favore correggi i seguenti errori:',\n    'Please upload a file.' => 'Per favore carica un file.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Visualizzo <b>{begin, number}-{end, number}</b> di <b>{totalCount, number}</b> {totalCount, plural, one{elemento} other{elementi}}.',\n    'The combination {values} of {attributes} has already been taken.' => '',\n    'The file \"{file}\" is not an image.' => 'Il file \"{file}\" non è una immagine.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'Il file \"{file}\" è troppo grande. La dimensione non può superare i {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Il file \"{file}\" è troppo piccolo. La dimensione non può essere inferiore a {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'Il formato di {attribute} non è valido.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'L\\'immagine \"{file}\" è troppo grande. La sua altezza non può essere maggiore di {limit, number} {limit, plural, one{pixel} other{pixel}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'L immagine \"{file}\" è troppo grande. La sua larghezza non può essere maggiore di {limit, number} {limit, plural, one{pixel} other{pixel}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'L immagine \"{file}\" è troppo piccola. La sua altezza non può essere minore di {limit, number} {limit, plural, one{pixel} other{pixel}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'L immagine \"{file}\" è troppo piccola. La sua larghezza non può essere minore di {limit, number} {limit, plural, one{pixel} other{pixel}}.',\n    'The requested view \"{name}\" was not found.' => 'La vista \"{name}\" richiesta non è stata trovata.',\n    'The verification code is incorrect.' => 'Il codice di verifica non è corretto.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => '{count, plural, one{Elementi} other{Elementi}} totali <b>{count, number}</b>.',\n    'Unable to verify your data submission.' => 'Impossibile verificare i dati inviati.',\n    'Unknown alias: -{name}' => 'Alias sconosciuto: -{name}',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'Opzione Sconosciuta: --{name}',\n    'Update' => 'Aggiorna',\n    'View' => 'Visualizza',\n    'Yes' => 'Si',\n    'You are not allowed to perform this action.' => 'Non sei autorizzato ad eseguire questa operazione.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Puoi caricare al massimo {limit, number} {limit, plural, one{file} other{file}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => 'in {delta, plural, =1{un giorno} other{# giorni}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'in {delta, plural, =1{un minuto} other{# minuti}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'in {delta, plural, =1{un mese} other{# mesi}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'in {delta, plural, =1{un secondo} other{# secondi}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'in {delta, plural, =1{un anno} other{# anni}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'in {delta, plural, =1{un\\'ora} other{# ore}}',\n    'just now' => 'proprio ora',\n    'the input value' => 'il valore del campo',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" è già presente.',\n    '{attribute} cannot be blank.' => '{attribute} non può essere vuoto.',\n    '{attribute} contains wrong subnet mask.' => '{attribute} contiene una subnet mask errata.',\n    '{attribute} is invalid.' => '{attribute} non è valido.',\n    '{attribute} is not a valid URL.' => '{attribute} non è un URL valido.',\n    '{attribute} is not a valid email address.' => '{attribute} non è un indirizzo email valido.',\n    '{attribute} is not in the allowed range.' => '{attribute} non rientra nell\\'intervallo permesso',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} deve essere \"{requiredValue}\".',\n    '{attribute} must be a number.' => '{attribute} deve essere un numero.',\n    '{attribute} must be a string.' => '{attribute} deve essere una stringa.',\n    '{attribute} must be a valid IP address.' => '{attribute} deve essere un indirizzo IP valido.',\n    '{attribute} must be an IP address with specified subnet.' => '{attribute} deve essere un indirizzo IP valido con subnet specificata.',\n    '{attribute} must be an integer.' => '{attribute} deve essere un numero intero.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} deve essere \"{true}\" oppure \"{false}\".',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '{attribute} deve essere uguale a \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} deve essere maggiore di \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} deve essere maggiore o uguale a \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} deve essere minore di \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} deve essere minore o uguale a \"{compareValueOrAttribute}\".',\n    '{attribute} must be no greater than {max}.' => '{attribute} non deve essere maggiore di {max}.',\n    '{attribute} must be no less than {min}.' => '{attribute} non deve essere minore di {min}.',\n    '{attribute} must not be a subnet.' => '{attribute} non deve essere una subnet.',\n    '{attribute} must not be an IPv4 address.' => '{attribute} non deve essere un indirizzo IPv4.',\n    '{attribute} must not be an IPv6 address.' => '{attribute} non deve essere un indirizzo IPv6.',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} non deve essere uguale a \"{compareValueOrAttribute}\".',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} dovrebbe contenere almeno {min, number} {min, plural, one{carattere} other{caratteri}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} dovrebbe contenere al massimo {max, number} {max, plural, one{carattere} other{caratteri}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} dovrebbe contenere {length, number} {length, plural, one{carattere} other{caratteri}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, =1{1 giorno} other{# giorni}}',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, =1{1 ora} other{# ore}}',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, =1{1 minuto} other{# minuti}}',\n    '{delta, plural, =1{1 month} other{# months}}' => '{delta, plural, =1{1 mese} other{# mesi}}',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta, plural, =1{1 secondo} other{# secondi}}',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta, plural, =1{1 anno} other{# anni}}',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{un giorno} other{# giorni}} fa',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{un minuto} other{# minuti}} fa',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{un mese} other{# mesi}} fa',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{un secondo} other{# secondi}} fa',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{un anno} other{# anni}} fa',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, =1{un\\'ora} other{# ore}} fa',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '{nFormatted} GiB',\n    '{nFormatted} KiB' => '{nFormatted} KiB',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '{nFormatted} MiB',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, =1{byte} other{byte}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, =1{gibibyte} other{gibibyte}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, =1{gigabyte} other{gigabyte}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, =1{kibibyte} other{kibibyte}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, =1{kilobyte} other{kilobyte}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, =1{mebibyte} other{mebibyte}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, =1{megabyte} other{megabyte}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, =1{pebibyte} other{pebibyte}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, =1{petabyte} other{petabyte}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, =1{tebibyte} other{tebibyte}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, =1{terabyte} other{terabyte}}',\n];\n"
  },
  {
    "path": "framework/messages/ja/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => ' および ',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '\"{attribute}\" は演算子 \"{operator}\" をサポートしていません。',\n    '(not set)' => '(未設定)',\n    'Action not found.' => 'アクションがありません。',\n    'Aliases available: {aliases}' => '利用可能なエイリアス: {aliases}',\n    'An internal server error occurred.' => '内部サーバーエラーが発生しました。',\n    'Are you sure you want to delete this item?' => 'このアイテムを削除したいというのは本当ですか?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '\"{attribute}\" のための条件は値であるか有効な演算子の定義でなければなりません。',\n    'Delete' => '削除',\n    'Error' => 'エラー',\n    'File upload failed.' => 'ファイルアップロードに失敗しました。',\n    'Home' => 'ホーム',\n    'Invalid data received for parameter \"{param}\".' => 'パラメータ \"{param}\" に不正なデータを受け取りました。',\n    'Login Required' => 'ログインが必要です',\n    'Missing required arguments: {params}' => '必要な引数がありません: {params}',\n    'Missing required parameters: {params}' => '必要なパラメータがありません: {params}',\n    'No' => 'いいえ',\n    'No results found.' => '結果が得られませんでした。',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => '以下の MIME タイプのファイルだけが許可されています: {mimeTypes}',\n    'Only files with these extensions are allowed: {extensions}.' => '次の拡張子を持つファイルだけが許可されています : {extensions}',\n    'Operator \"{operator}\" must be used with a search attribute.' => '演算子 \"{operator}\" はサーチ属性とともに使用されなければなりません。',\n    'Operator \"{operator}\" requires multiple operands.' => '演算子 \"{operator}\" は複数の被演算子を要求します。',\n    'Options available: {options}' => '利用可能なオプション: {options}',\n    'Page not found.' => 'ページが見つかりません。',\n    'Please fix the following errors:' => '次のエラーを修正してください :',\n    'Please upload a file.' => 'ファイルをアップロードしてください。',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => '<b>{totalCount, number}</b> 件中 <b>{begin, number}</b> から <b>{end, number}</b> までを表示しています。',\n    'The combination {values} of {attributes} has already been taken.' => '{attributes} の {values} という組み合せは既に登録されています。',\n    'The file \"{file}\" is not an image.' => 'ファイル \"{file}\" は画像ではありません。',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'ファイル \"{file}\" は大きすぎます。サイズが {formattedLimit} を超えてはいけません。',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'ファイル \"{file}\" は小さすぎます。サイズが {formattedLimit} より小さくてはいけません。',\n    'The format of {attribute} is invalid.' => '{attribute} の書式が正しくありません。',\n    'The format of {filter} is invalid.' => '{filter} の書式が正しくありません。',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '画像 \"{file}\" が大きすぎます。高さが {limit} ピクセルより大きくてはいけません。',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '画像 \"{file}\" が大きすぎます。幅が {limit} ピクセルより大きくてはいけません。',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '画像 \"{file}\" が小さすぎます。高さが {limit} ピクセルより小さくてはいけません。',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '画像 \"{file}\" が小さすぎます。幅が {limit} ピクセルより小さくてはいけません。',\n    'The requested view \"{name}\" was not found.' => 'リクエストされたビュー \"{name}\" が見つかりませんでした。',\n    'The verification code is incorrect.' => '検証コードが正しくありません。',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => '合計 <b>{count}</b> 件。',\n    'Unable to verify your data submission.' => 'データ送信を検証できませんでした。',\n    'Unknown alias: -{name}' => '不明なエイリアス: -{name}',\n    'Unknown filter attribute \"{attribute}\"' => '不明なフィルタ属性 \"{attribute}\"',\n    'Unknown option: --{name}' => '不明なオプション: --{name}',\n    'Update' => '更新',\n    'View' => '閲覧',\n    'Yes' => 'はい',\n    'You are not allowed to perform this action.' => 'このアクションの実行は許可されていません。',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => '最大で {limit, number} 個のファイルをアップロードできます。',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '少なくとも {limit, number} 個のファイルをアップロードしなければなりません。',\n    'in {delta, plural, =1{a day} other{# days}}' => '{delta} 日後',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => '{delta} 分後',\n    'in {delta, plural, =1{a month} other{# months}}' => '{delta} ヶ月後',\n    'in {delta, plural, =1{a second} other{# seconds}}' => '{delta} 秒後',\n    'in {delta, plural, =1{a year} other{# years}}' => '{delta} 年後',\n    'in {delta, plural, =1{an hour} other{# hours}}' => '{delta} 時間後',\n    'just now' => '現在',\n    'the input value' => '入力値',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} で \"{value}\" は既に使われています。',\n    '{attribute} cannot be blank.' => '{attribute} は空白ではいけません。',\n    '{attribute} contains wrong subnet mask.' => '{attribute} は無効なサブネットマスクを含んでいます。',\n    '{attribute} is invalid.' => '{attribute} は無効です。',\n    '{attribute} is not a valid URL.' => '{attribute} は有効な URL 書式ではありません。',\n    '{attribute} is not a valid email address.' => '{attribute} は有効なメールアドレス書式ではありません。',\n    '{attribute} is not in the allowed range.' => '{attribute} は許容される範囲内にありません。',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} は \"{requiredValue}\" である必要があります。',\n    '{attribute} must be a number.' => '{attribute} は数字でなければいけません。',\n    '{attribute} must be a string.' => '{attribute} は文字列でなければいけません。',\n    '{attribute} must be a valid IP address.' => '{attribute} は有効な IP アドレスでなければいけません。',\n    '{attribute} must be an IP address with specified subnet.' => '{attribute} は指定されたサブネットを持つ IP アドレスでなければいけません。',\n    '{attribute} must be an integer.' => '{attribute} は整数でなければいけません。',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} は \"{true}\" か \"{false}\" のいずれかでなければいけません。',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '{attribute} は \"{compareValueOrAttribute}\" と等しくなければいけません。',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} は \"{compareValueOrAttribute}\" より大きくなければいけません。',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} は \"{compareValueOrAttribute}\" と等しいか、または、大きくなければいけません。',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} は \"{compareValueOrAttribute}\" より小さくなければいけません。',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} は \"{compareValueOrAttribute}\" と等しいか、または、小さくなければいけません。',\n    '{attribute} must be no greater than {max}.' => '{attribute} は {max} より大きくてはいけません。',\n    '{attribute} must be no less than {min}.' => '{attribute} は {min} より小さくてはいけません。',\n    '{attribute} must not be a subnet.' => '{attribute} はサブネットであってはいけません。',\n    '{attribute} must not be an IPv4 address.' => '{attribute} は IPv4 アドレスであってはいけません。',\n    '{attribute} must not be an IPv6 address.' => '{attribute} は IPv6 アドレスであってはいけません。',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} は \"{compareValueOrAttribute}\" と等しくてはいけません。',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} は {min} 文字以上でなければいけません。',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} は {max} 文字以下でなければいけません。',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} は {length} 文字でなければいけません。',\n    '{compareAttribute} is invalid.' => '{compareAttribute} は無効です。',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta} 日間',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta} 時間',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta} 分間',\n    '{delta, plural, =1{1 month} other{# months}}' => '{delta} ヶ月間',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta} 秒間',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta} 年間',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta} 日前',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta} 分前',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta} ヶ月前',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta} 秒前',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta} 年前',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta} 時間前',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '{nFormatted} GiB',\n    '{nFormatted} KiB' => '{nFormatted} KiB',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '{nFormatted} MiB',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} バイト',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} ギビバイト',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} ギガバイト',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} キビバイト',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} キロバイト',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} メビバイト',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} メガバイト',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} ペビバイト',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} ペタバイト',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} テビバイト',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} テラバイト',\n];\n"
  },
  {
    "path": "framework/messages/ka/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => '',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(არ არის მითითებული)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'წარმოიშვა სერვერის შიდა შეცდომა.',\n    'Are you sure you want to delete this item?' => 'დარწმუნებული ხართ, რომ გინდათ ამ ელემენტის წაშლა?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'წაშლა',\n    'Error' => 'შეცდომა',\n    'File upload failed.' => 'ფაილის ჩამოტვირთვა ვერ მოხერხდა.',\n    'Home' => 'მთავარი',\n    'Invalid data received for parameter \"{param}\".' => 'პარამეტრის \"{param}\" არასწორი მნიშვნელობა.',\n    'Login Required' => 'საჭიროა შესვლა.',\n    'Missing required arguments: {params}' => 'არ არსებობენ აუცილებელი პარამეტრები: {params}',\n    'Missing required parameters: {params}' => 'არ არსებობენ აუცილებელი პარამეტრები: {params}',\n    'No' => 'არა',\n    'No results found.' => 'არაფერი მოიძებნა.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'ნებადართულია ფაილების ჩამოტვირთვა მხოლოდ შემდეგი MIME-ტიპებით: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'ნებადართულია ფაილების ჩამოტვირთვა მხოლოდ შემდეგი გაფართოებებით: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'გვერდი ვერ მოიძებნა.',\n    'Please fix the following errors:' => 'შეასწორეთ შემდეგი შეცდომები:',\n    'Please upload a file.' => 'ჩამოტვირთეთ ფაილი.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'ნაჩვენებია ჩანაწერები <b>{begin, number}-{end, number}</b> из <b>{totalCount, number}</b>.',\n    'The combination {values} of {attributes} has already been taken.' => '',\n    'The file \"{file}\" is not an image.' => 'ფაილი «{file}» არ წარმოადგენს გამოსახულებას.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'ფაილი «{file}» ძალიან დიდია. ზომა არ უნდა აღემატებოდეს {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'ფაილი «{file}» ძალიან პატარაა. ზომა უნდა აღემატებოდეს {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'მნიშვნელობის «{attribute}» არასწორი ფორმატი.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'ფაილი «{file}» ძალიან დიდია. სიმაღლე არ უნდა აღემატებოდეს {limit, number} {limit, plural, one{პიქსელს} few{პიქსელს} many{პიქსელს} other{პიქსელს}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'ფაილი «{file}» ძალიან დიდია. სიგანე არ უნდა აღემატებოდეს {limit, number} {limit, plural, one{პიქსელს} few{პიქსელს} many{პიქსელს} other{პიქსელს}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'ფაილი «{file}» ძალიან პატარაა. სიმაღლე უნდა აღემატებოდეს {limit, number} {limit, plural, one{პიქსელს} few{პიქსელს} many{პიქსელს} other{პიქსელს}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'ფაილი «{file}» ძალიან პატარაა. სიგანე უნდა აღემატებოდეს {limit, number} {limit, plural, one{პიქსელს} few{პიქსელს} many{პიქსელს} other{პიქსელს}}.',\n    'The requested view \"{name}\" was not found.' => 'მოთხოვნილი წარმოდგენის \"{name}\" ფაილი ვერ მოიძებნა.',\n    'The verification code is incorrect.' => 'არასწორი შემამოწმებელი კოდი.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'სულ <b>{count, number}</b> {count, plural, one{ჩანაწერი} few{ჩანაწერი} many{ჩანაწერი}} other{ჩანაწერი}}.',\n    'Unable to verify your data submission.' => 'ვერ მოხერხდა გადაცემული მონაცემების შემოწმება.',\n    'Unknown alias: -{name}' => '',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'უცნობი ოფცია: --{name}',\n    'Update' => 'რედაქტირება',\n    'View' => 'ნახვა',\n    'Yes' => 'დიახ',\n    'You are not allowed to perform this action.' => 'თქვენ არ გაქვთ მოცემული ქმედების შესრულების ნებართვა.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'თქვენ არ შეგიძლიათ ჩამოტვირთოთ {limit, number}-ზე მეტი {limit, plural, one{ფაილი} few{ფაილი} many{ფაილი} other{ფაილი}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => '{delta, plural, =1{დღის} one{# დღის} few{# დღის} many{# დღის} other{# დღის}} შემდეგ',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => '{delta, plural, =1{წუთის} one{# წუთის} few{# წუთის} many{# წუთის} other{# წუთის}} შემდეგ',\n    'in {delta, plural, =1{a month} other{# months}}' => '{delta, plural, =1{თვის} one{# თვის} few{# თვის} many{# თვის} other{# თვის}} შემდეგ',\n    'in {delta, plural, =1{a second} other{# seconds}}' => '{delta, plural, =1{წამის} one{# წამის} few{# წამის} many{# წამის} other{# წამის}} შემდეგ',\n    'in {delta, plural, =1{a year} other{# years}}' => '{delta, plural, =1{წლის} one{# წლის} few{# წლის} many{# წლის} other{# წლის}} შემდეგ',\n    'in {delta, plural, =1{an hour} other{# hours}}' => '{delta, plural, =1{საათის} one{# საათის} few{# საათის} many{# საათის} other{# საათის}} შემდეგ',\n    'just now' => 'ახლავე',\n    'the input value' => 'შეყვანილი მნიშვნელობა',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} «{value}» უკვე დაკავებულია.',\n    '{attribute} cannot be blank.' => 'საჭიროა შევსება «{attribute}».',\n    '{attribute} contains wrong subnet mask.' => '',\n    '{attribute} is invalid.' => 'მნიშვნელობა «{attribute}» არასწორია.',\n    '{attribute} is not a valid URL.' => 'მნიშვნელობა «{attribute}» არ წარმოადგენს სწორ URL-ს.',\n    '{attribute} is not a valid email address.' => 'მნიშვნელობა «{attribute}» არ წარმოადგენს სწორ email მისამართს.',\n    '{attribute} is not in the allowed range.' => '',\n    '{attribute} must be \"{requiredValue}\".' => 'მნიშვნელობა «{attribute}» უნდა იყოს «{requiredValue}-ს ტოლი».',\n    '{attribute} must be a number.' => 'მნიშვნელობა «{attribute}» უნდა იყოს რიცხვი.',\n    '{attribute} must be a string.' => 'მნიშვნელობა «{attribute}» უნდა იყოს სტრიქონი.',\n    '{attribute} must be a valid IP address.' => '',\n    '{attribute} must be an IP address with specified subnet.' => '',\n    '{attribute} must be an integer.' => 'მნიშვნელობა «{attribute}» უნდა იყოს მთელი რიცხვი.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => 'მნიშვნელობა «{attribute}» უნდა იყოს «{true}»-ს ან «{false}»-ს ტოლი.',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => 'მნიშვნელობა «{attribute}» უნდა იყოს «{compareValueOrAttribute}» მნიშვნელობაზე მეტი.',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => 'მნიშვნელობა «{attribute}» უნდა იყოს «{compareValueOrAttribute}» მნიშვნელობაზე მეტი ან ტოლი.',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => 'მნიშვნელობა «{attribute}» უნდა იყოს «{compareValueOrAttribute}» მნიშვნელობაზე ნაკლები.',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => 'მნიშვნელობა «{attribute}» უნდა იყოს «{compareValueOrAttribute}» მნიშვნელობაზე ნაკლები ან ტოლი.',\n    '{attribute} must be no greater than {max}.' => 'მნიშვნელობა «{attribute}» არ უნდა აღემატებოდეს {max}.',\n    '{attribute} must be no less than {min}.' => 'მნიშვნელობა «{attribute}» უნდა იყოს არანაკლები {min}.',\n    '{attribute} must not be a subnet.' => '',\n    '{attribute} must not be an IPv4 address.' => '',\n    '{attribute} must not be an IPv6 address.' => '',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => 'მნიშვნელობა «{attribute}» არ უნდა იყოს «{compareValueOrAttribute}»-ს ტოლი.',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => 'მნიშვნელობა «{attribute}» უნდა შეიცავდეს მინიმუმ {min, number} {min, plural, one{სიმბოლს} few{სიმბოლს} many{სიმბოლს} other{სიმბოლს}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => 'მნიშვნელობა «{attribute}» უნდა შეიცავდეს მაქსიმუმ {max, number} {max, plural, one{სიმბოლს} few{სიმბოლს} many{სიმბოლს} other{სიმბოლს}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => 'მნიშვნელობა «{attribute}» უნდა შეიცავდეს {length, number} {length, plural, one{სიმბოლს} few{სიმბოლს} many{სიმბოლს} other{სიმბოლს}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '',\n    '{delta, plural, =1{1 month} other{# months}}' => '',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '',\n    '{delta, plural, =1{1 year} other{# years}}' => '',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{დღის} one{# დღის} few{# დღის} many{# დღის} other{# დღის}} უკან',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{წუთის} one{# წუთის} few{# წუთის} many{# წუთის} other{# წუთის}} უკან',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{თვის} one{# თვის} few{# თვის} many{# თვის} other{# თვის}} უკან',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{წამის} one{# წამის} few{# წამის} many{# წამის} other{# წამის}} უკან',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{წლის} one{# წლის} few{# წლის} many{# წლის} other{# წლის}} უკან',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, =1{საათის} one{# საათის} few{# საათის} many{# საათის} other{# საათის}} უკან',\n    '{nFormatted} B' => '{nFormatted} ბ',\n    '{nFormatted} GB' => '{nFormatted} გბ',\n    '{nFormatted} GiB' => '{nFormatted} გიბ',\n    '{nFormatted} KiB' => '{nFormatted} კიბ',\n    '{nFormatted} MB' => '{nFormatted} მბ',\n    '{nFormatted} MiB' => '{nFormatted} მიბ',\n    '{nFormatted} PB' => '{nFormatted} პბ',\n    '{nFormatted} PiB' => '{nFormatted} პიბ',\n    '{nFormatted} TB' => '{nFormatted} ტბ',\n    '{nFormatted} TiB' => '{nFormatted} ტიბ',\n    '{nFormatted} kB' => '{nFormatted} კბ',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, one{ბაიტი} few{ბაიტს} many{ბაიტი} other{ბაიტს}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, one{გიბიბაიტი} few{გიბიბაიტს} many{გიბიბაიტი} other{გიბიბაიტს}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, one{გიგაბაიტი} few{გიგაბაიტს} many{გიგაბაიტი} other{გიგაბაიტს}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, one{კიბიბაიტი} few{კიბიბაიტს} many{კიბიბაიტი} other{კიბიბაიტს}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, one{კილობაიტი} few{კილობაიტს} many{კილობაიტი} other{კილობაიტს}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, one{მებიბაიტი} few{მებიბაიტს} many{მებიბაიტი} other{მებიბაიტს}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, one{მეგაბაიტი} few{მეგაბაიტს} many{მეგაბაიტი} other{მეგაბაიტს}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, one{პებიბაიტი} few{პებიბაიტს} many{პებიბაიტი} other{პებიბაიტს}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, one{პეტაბაიტი} few{პეტაბაიტს} many{პეტაბაიტი} other{პეტაბაიტს}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, one{ტებიბაიტი} few{ტებიბაიტს} many{ტებიბაიტი} other{ტებიბაიტს}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, one{ტერაბაიტი} few{ტერაბაიტს} many{ტერაბაიტი} other{ტერაბაიტს}}',\n];\n"
  },
  {
    "path": "framework/messages/kk/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => '',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(берілмеген)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Сервердің ішкі қатесі туды.',\n    'Are you sure you want to delete this item?' => 'Бұл элементті жоюға сенімдісіз бе?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'Жою',\n    'Error' => 'Қате',\n    'File upload failed.' => 'Файлды жүктеу сәтті болмады',\n    'Home' => 'Басы',\n    'Invalid data received for parameter \"{param}\".' => '\"{param}\" параметріне дұрыс емес мән берілген.',\n    'Login Required' => 'Кіруді сұрайды.',\n    'Missing required arguments: {params}' => 'Қажетті аргументтер жоқ: {params}',\n    'Missing required parameters: {params}' => 'Қажетті параметрлер жоқ: {params}',\n    'No' => 'Жоқ',\n    'No results found.' => 'Нәтиже табылған жок.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => '',\n    'Only files with these extensions are allowed: {extensions}.' => 'Тек мына кеңейтімдегі файлдарға ғана рұқсат етілген: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Бет табылған жок.',\n    'Please fix the following errors:' => 'Өтінеміз, мына қателерді түзеңіз:',\n    'Please upload a file.' => 'Файлды жүктеңіз.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => '<b>{totalCount, number}</b> жазбадан <b>{begin, number}-{end, number}</b> аралығы көрсетілген.',\n    'The combination {values} of {attributes} has already been taken.' => '',\n    'The file \"{file}\" is not an image.' => '«{file}» файлы сурет емес.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => '«{file}» файлының өлшемі тым үлкен. Көлемі {formattedLimit} аспау керек.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => '«{file}» файлының өлшемі тым кіші. Көлемі {formattedLimit} кем болмауы керек.',\n    'The format of {attribute} is invalid.' => '«{attribute}» аттрибутының форматы дұрыс емес.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '«{file}» суретінің өлшемі тым үлкен. Биіктігі {limit, number} пиксельден аспауы қажет.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '«{file}» суретінің өлшемі тым үлкен. Ені {limit, number} пиксельден аспауы қажет.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '«{file}» суретінің өлшемі тым кіші. Биіктігі {limit, number} пиксельден кем болмауы қажет.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '«{file}» суретінің өлшемі тым кіші. Ені {limit, number} пиксельден кем болмауы қажет.',\n    'The requested view \"{name}\" was not found.' => '',\n    'The verification code is incorrect.' => 'Тексеріс коды қате.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Барлығы <b>{count, number}</b> жазба.',\n    'Unable to verify your data submission.' => 'Жіберілген мәліметерді тексеру мүмкін емес.',\n    'Unknown alias: -{name}' => '',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'Белгісіз опция: --{name}',\n    'Update' => 'Жаңарту',\n    'View' => 'Көру',\n    'Yes' => 'Иә',\n    'You are not allowed to perform this action.' => 'Сізге бұл әрекетті жасауға рұқсат жоқ',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Сіз {limit} файлдан көп жүктей алмайсыз.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => '',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => '',\n    'in {delta, plural, =1{a month} other{# months}}' => '',\n    'in {delta, plural, =1{a second} other{# seconds}}' => '',\n    'in {delta, plural, =1{a year} other{# years}}' => '',\n    'in {delta, plural, =1{an hour} other{# hours}}' => '',\n    'just now' => '',\n    'the input value' => 'енгізілген мән',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} «{value}» Бұл бос емес мән.',\n    '{attribute} cannot be blank.' => '«{attribute}» толтыруды қажет етеді.',\n    '{attribute} contains wrong subnet mask.' => '',\n    '{attribute} is invalid.' => '«{attribute}» мәні жарамсыз.',\n    '{attribute} is not a valid URL.' => '«{attribute}» жарамды URL емес.',\n    '{attribute} is not a valid email address.' => '«{attribute}» жарамды email адрес емес.',\n    '{attribute} is not in the allowed range.' => '',\n    '{attribute} must be \"{requiredValue}\".' => '«{attribute}» мәні «{requiredValue}» болу керек.',\n    '{attribute} must be a number.' => '«{attribute}» мәні сан болуы керек.',\n    '{attribute} must be a string.' => '«{attribute}» мәні мәтін болуы керек.',\n    '{attribute} must be a valid IP address.' => '',\n    '{attribute} must be an IP address with specified subnet.' => '',\n    '{attribute} must be an integer.' => '«{attribute}» мәні бүтін сан болу керек.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '«{attribute}» мәні «{true}» немесе «{false}» болуы керек.',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '«{attribute}» мәні мынадан үлкен болуы керек: «{compareValueOrAttribute}».',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '«{attribute}» мәні мынадан үлкен немесе тең болуы керек: «{compareValueOrAttribute}».',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '«{attribute}» мәні мынадан кіші болуы керек: «{compareValueOrAttribute}».',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '«{attribute}» мәні мынадан кіші немесе тең болуы керек: «{compareValueOrAttribute}».',\n    '{attribute} must be no greater than {max}.' => '«{attribute}» мәні мынадан аспауы керек: {max}.',\n    '{attribute} must be no less than {min}.' => '«{attribute}» мәні мынадан кем болмауы керек: {min}.',\n    '{attribute} must not be a subnet.' => '',\n    '{attribute} must not be an IPv4 address.' => '',\n    '{attribute} must not be an IPv6 address.' => '',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '«{attribute}» мәні мынаған тең болмауы керек: «{compareValueOrAttribute}».',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '«{attribute}» мәні кемінде {min} таңбадан тұруы керек.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '«{attribute}» мәні {max} таңбадан аспауы қажет.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '«{attribute}» {length} таңбадан тұруы керек.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '',\n    '{delta, plural, =1{1 month} other{# months}}' => '',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '',\n    '{delta, plural, =1{1 year} other{# years}}' => '',\n    '{delta, plural, =1{a day} other{# days}} ago' => '',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '',\n    '{delta, plural, =1{a month} other{# months}} ago' => '',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '',\n    '{delta, plural, =1{a year} other{# years}} ago' => '',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '',\n    '{nFormatted} B' => '',\n    '{nFormatted} GB' => '',\n    '{nFormatted} GiB' => '',\n    '{nFormatted} KiB' => '',\n    '{nFormatted} MB' => '',\n    '{nFormatted} MiB' => '',\n    '{nFormatted} PB' => '',\n    '{nFormatted} PiB' => '',\n    '{nFormatted} TB' => '',\n    '{nFormatted} TiB' => '',\n    '{nFormatted} kB' => '',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '',\n];\n"
  },
  {
    "path": "framework/messages/ko/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => '',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(설정되어있지않습니다)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => '서버 오류가 발생하였습니다.',\n    'Are you sure you want to delete this item?' => '',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => '삭제',\n    'Error' => '오류',\n    'File upload failed.' => '파일 업로드 실패하였습니다.',\n    'Home' => '홈',\n    'Invalid data received for parameter \"{param}\".' => '매개변수\"{param}\"를 위한 데이터가 잘못된 데이터입니다.',\n    'Login Required' => '로그인이 필요합니다.',\n    'Missing required arguments: {params}' => '필요한 인수가 없습니다: {params}',\n    'Missing required parameters: {params}' => '필요한 매개변수가 없습니다: {params}',\n    'No' => '아니오',\n    'No results found.' => '결과가 없습니다.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => '',\n    'Only files with these extensions are allowed: {extensions}.' => '다음의 확장명을 가진 파일만 허용됩니다: {extensions}',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => '페이지를 찾을 수 없습니다.',\n    'Please fix the following errors:' => '다음 오류를 수정하십시오:',\n    'Please upload a file.' => '파일을 업로드하십시오.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => '{totalCount} 중 {begin} 에서 {end} 까지 표시하고 있습니다.',\n    'The combination {values} of {attributes} has already been taken.' => '',\n    'The file \"{file}\" is not an image.' => '파일 \"{file}\"은 이미지 파일이 아닙니다.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => '',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => '',\n    'The format of {attribute} is invalid.' => '{attribute}의 형식이 올바르지 않습니다.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '이미지 \"{file}\"가 너무 큽니다. 높이는 {limit} 보다 클 수 없습니다.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '이미지 \"{file}\"가 너무 큽니다. 넓이는 {limit} 보다 클 수 없습니다.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '이미지 \"{file}\"가 너무 작습니다. 높이는 {limit} 보다 작을 수 없습니다.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '이미지 \"{file}\"가 너무 작습니다. 넒이는 {limit} 보다 작을 수 없습니다.',\n    'The requested view \"{name}\" was not found.' => '',\n    'The verification code is incorrect.' => '확인코드가 올바르지않습니다.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => '모두 {count} 개',\n    'Unable to verify your data submission.' => '데이터 전송을 확인하지 못했습니다.',\n    'Unknown alias: -{name}' => '',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => '알 수 없는 옵션: --{name}',\n    'Update' => '갱신',\n    'View' => '보기',\n    'Yes' => '예',\n    'You are not allowed to perform this action.' => '이 작업의 실행을 허가받지못하였습니다.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => '최대 {limit} 개의 파일을 업로드할 수 있습니다.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => '',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => '',\n    'in {delta, plural, =1{a month} other{# months}}' => '',\n    'in {delta, plural, =1{a second} other{# seconds}}' => '',\n    'in {delta, plural, =1{a year} other{# years}}' => '',\n    'in {delta, plural, =1{an hour} other{# hours}}' => '',\n    'just now' => '',\n    'the input value' => '입력 값',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute}에 \"{value}\"이 이미 사용되고 있습니다.',\n    '{attribute} cannot be blank.' => '{attribute}는 공백일 수 없습니다.',\n    '{attribute} contains wrong subnet mask.' => '',\n    '{attribute} is invalid.' => '{attribute}가 잘못되었습니다.',\n    '{attribute} is not a valid URL.' => '{attribute}는 올바른 URL 형식이 아닙니다.',\n    '{attribute} is not a valid email address.' => '{attribute}는 올바른 이메일 주소 형식이 아닙니다.',\n    '{attribute} is not in the allowed range.' => '',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute}는 {value}이어야 합니다.',\n    '{attribute} must be a number.' => '{attribute}는 반드시 숫자이어야 합니다.',\n    '{attribute} must be a string.' => '{attribute}는 반드시 문자이어야 합니다.',\n    '{attribute} must be a valid IP address.' => '',\n    '{attribute} must be an IP address with specified subnet.' => '',\n    '{attribute} must be an integer.' => '{attribute}는 반드시 정수이어야 합니다.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute}는 {true} 또는 {false} 이어야 합니다.',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute}는 \"{compareValueOrAttribute}\" 보다 커야 합니다.',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute}는 \"{compareValueOrAttribute}\" 보다 크거나 같아야 합니다.',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute}는 \"{compareValueOrAttribute}\" 보다 작아야 합니다.',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute}는 \"{compareValueOrAttribute}\" 보다 작거나 같아야 합니다.',\n    '{attribute} must be no greater than {max}.' => '{attribute}는 \"{max}\" 보다 클 수 없습니다.',\n    '{attribute} must be no less than {min}.' => '{attribute}는 \"{min}\" 보다 작을 수 없습니다.',\n    '{attribute} must not be a subnet.' => '',\n    '{attribute} must not be an IPv4 address.' => '',\n    '{attribute} must not be an IPv6 address.' => '',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute}는 \"{compareValueOrAttribute}\"와 같을 수 없습니다.',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute}는 최소 {min}자 이어야합니다.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute}는 최대 {max}자 이어야합니다.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute}는 {length}자 이어야합니다.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '',\n    '{delta, plural, =1{1 month} other{# months}}' => '',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '',\n    '{delta, plural, =1{1 year} other{# years}}' => '',\n    '{delta, plural, =1{a day} other{# days}} ago' => '',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '',\n    '{delta, plural, =1{a month} other{# months}} ago' => '',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '',\n    '{delta, plural, =1{a year} other{# years}} ago' => '',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '',\n    '{nFormatted} B' => '',\n    '{nFormatted} GB' => '',\n    '{nFormatted} GiB' => '',\n    '{nFormatted} KiB' => '',\n    '{nFormatted} MB' => '',\n    '{nFormatted} MiB' => '',\n    '{nFormatted} PB' => '',\n    '{nFormatted} PiB' => '',\n    '{nFormatted} TB' => '',\n    '{nFormatted} TiB' => '',\n    '{nFormatted} kB' => '',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '',\n];\n"
  },
  {
    "path": "framework/messages/kz/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => '',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(көрсетілмеген)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Ішкі сервер қатесі орын алды.',\n    'Are you sure you want to delete this item?' => 'Бұл элементті жою керек пе?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'Жою',\n    'Error' => 'Қате',\n    'File upload failed.' => 'Файл жүктелмеді.',\n    'Home' => 'Үй',\n    'Invalid data received for parameter \"{param}\".' => '\"{param}\" параметрінің мәні дұрыс емес.',\n    'Login Required' => 'Кіру қажет.',\n    'Missing required arguments: {params}' => 'Қажетті дәлелдер жоқ: {params}',\n    'Missing required parameters: {params}' => 'Қажетті параметрлер жоқ: {params}',\n    'No' => 'Жоқ',\n    'No results found.' => 'Еш нәрсе табылмады.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Тек келесі MIME-түріндегі файлдарға рұқсат етіледі: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Тек келесі кеңейтілімдері бар файлдарға рұқсат етіледі: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Бет табылмады.',\n    'Please fix the following errors:' => 'Келесі қателерді түзетіңіз:',\n    'Please upload a file.' => 'Файлды жүктеп алыңыз.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Жазбаларды көрсету <b>{begin, number}-{end, number}</b> из <b>{totalCount, number}</b>.',\n    'The combination {values} of {attributes} has already been taken.' => '',\n    'The file \"{file}\" is not an image.' => '\"{file}\" файлы сурет емес.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => '',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => '',\n    'The format of {attribute} is invalid.' => '{attribute} мәнінің пішімі дұрыс емес.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '«{file}» файлы тым үлкен. Биіктігі аспауы керек {limit, number} {limit, plural, other{пиксел}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '«{file}» файлы тым үлкен. Ені аспауы тиіс {limit, number} {limit, plural, other{пиксел}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '«{file}» файлы тым кішкентай. Биіктігі көп болуы керек {limit, number} {limit, plural, other{пиксел}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '«{file}» файлы тым кішкентай. Ені көп болуы керек {limit, number} {limit, plural, other{пиксел}}.',\n    'The requested view \"{name}\" was not found.' => 'Сұрау салынған \"{name}\" қарау файлы табылмады.',\n    'The verification code is incorrect.' => 'Растау коды дұрыс емес.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Барлығы <b>{count, number}</b> {count, plural, one{кіру} few{жазбалар} many{жазбалар} other{жазбалар}}.',\n    'Unable to verify your data submission.' => 'Жіберілген деректерді тексере алмадық.',\n    'Unknown alias: -{name}' => '',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'Белгісіз опция: --{name}',\n    'Update' => 'Өңдеу',\n    'View' => 'Ішінде қараңыз',\n    'Yes' => 'Ия',\n    'You are not allowed to perform this action.' => 'Сіз бұл әрекетті орындауға рұқсатыңыз жоқ.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Көбірек жүктей алмайсыз {limit, number} {limit, plural, one{файл} few{файлдар} many{файлдар} other{файл}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => 'арқылы {delta, plural, =1{күн} one{# күн} few{# күн} many{# күндер} other{# күн}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'арқылы {delta, plural, =1{минутына} one{# минутына} few{# минуттар} many{# минуттар} other{# минуттар}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'арқылы {delta, plural, =1{ай} one{# ай} few{# айлар} many{# айлар} other{# айлар}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'арқылы {delta, plural, other{# екіншіден}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'арқылы {delta, plural, =1{жыл} one{# жыл} few{# жыл} many{# жастағы} other{# жыл}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'арқылы {delta, plural, other{# сағат}}',\n    'just now' => 'дәл қазір',\n    'the input value' => 'енгізілген мән',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} «{value}» қазірдің өзінде қабылданды.',\n    '{attribute} cannot be blank.' => 'Толтырылуы керек «{attribute}».',\n    '{attribute} contains wrong subnet mask.' => '',\n    '{attribute} is invalid.' => '«{attribute}» мәні жарамсыз.',\n    '{attribute} is not a valid URL.' => '«{attribute}» мәні жарамды URL емес.',\n    '{attribute} is not a valid email address.' => '«{attribute}» мәні жарамды электрондық пошта мекенжайы емес.',\n    '{attribute} is not in the allowed range.' => '',\n    '{attribute} must be \"{requiredValue}\".' => '«{attribute}» мәні «{requiredValue}» дегенге тең болуы керек.',\n    '{attribute} must be a number.' => '«{attribute}» мәні сан болуы керек.',\n    '{attribute} must be a string.' => '«{attribute}» мәні жол болуы керек.',\n    '{attribute} must be a valid IP address.' => '',\n    '{attribute} must be an IP address with specified subnet.' => '',\n    '{attribute} must be an integer.' => '«{attribute}» мәні бүтін сан болуы керек.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '«{attribute}» мәні «{true}» немесе «{false}» мәніне тең болуы керек.',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '«{attribute}» мәні «{compareValueOrAttribute}» мәнінен үлкен болуы керек.',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '«{attribute}» мәні «{compareValueOrAttribute}» мәнінен үлкен немесе тең болуы керек.',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '«{attribute}» мәні «{compareValueOrAttribute}» мәнінен аз болуы керек.',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '«{attribute}» мәні «{compareValueOrAttribute}» мәнінен аз немесе тең болуы керек.',\n    '{attribute} must be no greater than {max}.' => '«{attribute}» мәні {max} аспауы керек.',\n    '{attribute} must be no less than {min}.' => '«{attribute}» мәні кем дегенде {min} болуы керек.',\n    '{attribute} must not be a subnet.' => '',\n    '{attribute} must not be an IPv4 address.' => '',\n    '{attribute} must not be an IPv6 address.' => '',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '«{attribute}» мәні «{compareValueOrAttribute}» тең болмауы керек.',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '«{attribute}» мәні минималды {min, number} болуы керек {min, number} {min, plural, one{таңба} few{таңба} many{таңбалар} other{таңба}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '«{attribute}» мәні ең көп {max, number} {max, plural, one{таңба} few{таңба} many{таңбалар} other{таңба}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '«{attribute}» мәні {length, number} болуы керек {length, plural, one{таңба} few{таңба} many{таңбалар} other{таңба}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '',\n    '{delta, plural, =1{1 month} other{# months}}' => '',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '',\n    '{delta, plural, =1{1 year} other{# years}}' => '',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{күн} one{күн} few{# күн} many{# күндер} other{# күн}} артқа қарай',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{минутына} one{# минутына} few{# минуттар} many{# минуттар} other{# минуттар}} артқа қарай',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{ай} one{# ай} few{# айлар} many{# айлар} other{# айлар}} артқа қарай',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, other{# екіншіден}} артқа қарай',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{жыл} one{# жыл} few{# жыл} many{# жастағы} other{# жыл}} артқа қарай',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, other{# сағат}} артқа қарай',\n    '{nFormatted} B' => '{nFormatted} Б',\n    '{nFormatted} GB' => '{nFormatted} ГБ',\n    '{nFormatted} GiB' => '{nFormatted} ГиБ',\n    '{nFormatted} KiB' => '{nFormatted} КиБ',\n    '{nFormatted} MB' => '{nFormatted} МБ',\n    '{nFormatted} MiB' => '{nFormatted} МиБ',\n    '{nFormatted} PB' => '{nFormatted} ПБ',\n    '{nFormatted} PiB' => '{nFormatted} ПиБ',\n    '{nFormatted} TB' => '{nFormatted} ТБ',\n    '{nFormatted} TiB' => '{nFormatted} ТиБ',\n    '{nFormatted} kB' => '{nFormatted} КБ',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, one{байттар} few{байт} many{байттар} other{байт}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, one{гибибайт} few{гибибайта} many{гибибайтов} other{гибибайта}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, one{гигабайт} few{гигабайта} many{гигабайтов} other{гигабайта}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, one{кибибайт} few{кибибайта} many{кибибайтов} other{кибибайта}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, one{килобайт} few{килобайта} many{килобайтов} other{килобайта}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, one{мебибайт} few{мебибайта} many{мебибайтов} other{мебибайта}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, one{мегабайт} few{мегабайта} many{мегабайтов} other{мегабайта}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, one{пебибайт} few{пебибайта} many{пебибайтов} other{пебибайта}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, one{петабайт} few{петабайта} many{петабайтов} other{петабайта}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, one{тебибайт} few{тебибайта} many{тебибайтов} other{тебибайта}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, one{терабайт} few{терабайта} many{терабайтов} other{терабайта}}',\n];\n"
  },
  {
    "path": "framework/messages/lt/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => '',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(nenustatyta)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Įvyko vidinė serverio klaida',\n    'Are you sure you want to delete this item?' => 'Ar tikrai norite ištrinti šį elementą?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'Ištrinti',\n    'Error' => 'Klaida',\n    'File upload failed.' => 'Nepavyko įkelti failo.',\n    'Home' => 'Pradžia',\n    'Invalid data received for parameter \"{param}\".' => 'Gauti neteisingi \"{param}\" parametro duomenys.',\n    'Login Required' => 'Būtina prisijungti',\n    'Missing required arguments: {params}' => 'Trūksta privalomų argumentų: {params}',\n    'Missing required parameters: {params}' => 'Trūksta privalomų parametrų: {params}',\n    'No' => 'Ne',\n    'No results found.' => 'Nėra užklausą atitinkančių įrašų.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Leidžiama įkelti tik šių MIME tipų failus: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Leidžiama įkelti failus tik su šiais plėtiniais: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Puslapis nerastas.',\n    'Please fix the following errors:' => 'Prašytume ištaisyti nurodytas klaidas:',\n    'Please upload a file.' => 'Prašome įkelti failą.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Rodomi rezultatai <b>{begin, number}-{end, number}</b> iš <b>{totalCount, number}</b>.',\n    'The combination {values} of {attributes} has already been taken.' => '',\n    'The file \"{file}\" is not an image.' => 'Failas „{file}“ nėra paveikslėlis.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'Failas „{file}“ yra per didelis. Dydis negali viršyti {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Failas „{file}“ yra per mažas. Dydis negali būti mažesnis už {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'Atributo „{attribute}“ formatas yra netinkamas.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Paveikslėlis „{file}“ yra per didelis. Aukštis negali viršyti {limit, number} {limit, plural, one{taško} few{taškų} other{taškų}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Paveikslėlis „{file}“ yra per didelis. Plotis negali viršyti {limit, number} {limit, plural, one{taško} few{taškų} other{taškų}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Paveikslėlis „{file}“ yra per mažas. Aukštis turi viršyti {limit, number} {limit, plural, one{tašką} few{taškus} other{taškų}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Paveikslėlis „{file}“ yra per mažas. Plotis turi viršyti {limit, number} {limit, plural, one{tašką} few{taškus} other{taškų}}.',\n    'The requested view \"{name}\" was not found.' => 'Nerastas „{name}“ rodinys',\n    'The verification code is incorrect.' => 'Neteisingas patikrinimo kodas.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Iš viso <b>{count, number}</b> {count, plural, one{elementas} few{elementai} other{elementų}}.',\n    'Unable to verify your data submission.' => 'Neįmanoma patikrinti jūsų siunčiamų duomenų.',\n    'Unknown alias: -{name}' => '',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'Nežinomas pasirinkimas: --{name}',\n    'Update' => 'Atnaujinti',\n    'View' => 'Peržiūrėti',\n    'Yes' => 'Taip',\n    'You are not allowed to perform this action.' => 'Jums neleidžiama atlikti šio veiksmo.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Leidžiama įkelti ne daugiau nei {limit, number} {limit, plural, one{failą} few{failus} other{failų}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => 'po {delta, plural, =1{dienos} one{# dienos} other{# dienų}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'po {delta, plural, =1{minutės} one{# minutės} other{# minučių}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'po {delta, plural, =1{mėnesio} one{# mėnesio} other{# mėnesių}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'po {delta, plural, =1{sekundės} one{# sekundės} other{# sekundžių}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'po {delta, plural, =1{metų} other{# metų}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'po {delta, plural, =1{valandos} one{# valandos} other{# valandų}}',\n    'just now' => 'dabar',\n    'the input value' => 'įvesties reikšmė',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} reikšmė „{value}“ jau naudojama.',\n    '{attribute} cannot be blank.' => '„{attribute}“ negali būti tuščias.',\n    '{attribute} contains wrong subnet mask.' => '',\n    '{attribute} is invalid.' => '„{attribute}“ reikšmė netinkama.',\n    '{attribute} is not a valid URL.' => '„{attribute}“ įvestas netinkamas URL.',\n    '{attribute} is not a valid email address.' => '„{attribute}“ įvestas netinkamas el. pašto adresas.',\n    '{attribute} is not in the allowed range.' => '',\n    '{attribute} must be \"{requiredValue}\".' => '„{attribute}“ privalo būti „{requiredValue}“.',\n    '{attribute} must be a number.' => '„{attribute}“ privalo būti skaičius.',\n    '{attribute} must be a string.' => '„{attribute}“ privalo būti eilutė.',\n    '{attribute} must be a valid IP address.' => '',\n    '{attribute} must be an IP address with specified subnet.' => '',\n    '{attribute} must be an integer.' => '„{attribute}“ privalo būti sveikas skaičius.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '„{attribute}“ privalo būti „{true}“ arba „{false}“.',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => 'Laukelio „{attribute}“ reikšmė privalo būti didesnė nei „{compareValueOrAttribute}“.',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => 'Laukelio „{attribute}“ reikšmė privalo būti didesnė arba lygi „{compareValueOrAttribute}“.',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => 'Laukelio „{attribute}“ reikšmė privalo būti mažesnė nei „{compareValueOrAttribute}“.',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => 'Laukelio „{attribute}“ reikšmė privalo būti mažesnė arba lygi „{compareValueOrAttribute}“.',\n    '{attribute} must be no greater than {max}.' => 'Laukelio „{attribute}“ reikšmė privalo būti ne didesnė nei {max}.',\n    '{attribute} must be no less than {min}.' => 'Laukelio „{attribute}“  reikšmė privalo būti ne mažesnė nei {min}.',\n    '{attribute} must not be a subnet.' => '',\n    '{attribute} must not be an IPv4 address.' => '',\n    '{attribute} must not be an IPv6 address.' => '',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => 'Laukelio „{attribute}“ reikšmė negali būti lygi „{compareValueOrAttribute}“.',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => 'Laukelio „{attribute}“ reikšmę privalo sudaryti mažiausiai {min, number} {min, plural, one{ženklas} few{ženklai} other{ženklų}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => 'Laukelio „{attribute}“ reikšmę privalo sudaryti daugiausiai {max, number} {max, plural, one{ženklas} few{ženklai} other{ženklų}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => 'Laukelio „{attribute}“ reikšmę privalo sudaryti {length, number} {length, plural, one{ženklas} few{ženklai} other{ženklų}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '',\n    '{delta, plural, =1{1 month} other{# months}}' => '',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '',\n    '{delta, plural, =1{1 year} other{# years}}' => '',\n    '{delta, plural, =1{a day} other{# days}} ago' => 'prieš {delta, plural, =1{dieną} one{# dieną} few{# dienas} other{# dienų}}',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => 'prieš {delta, plural, =1{minutę} one{# minutę} few{# minutes} other{# minučių}}',\n    '{delta, plural, =1{a month} other{# months}} ago' => 'prieš {delta, plural, =1{mėnesį} one{# mėnesį} few{# mėnesius} other{# mėnesių}}',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => 'prieš {delta, plural, =1{sekundę} one{# sekundę} few{# sekundes} other{# sekundžių}}',\n    '{delta, plural, =1{a year} other{# years}} ago' => 'prieš {delta, plural, =1{metus} one{# metus} few{# metus} other{# metų}}',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => 'prieš {delta, plural, =1{valandą} one{# valandą} few{# valandas} other{# valandų}}',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '{nFormatted} GiB',\n    '{nFormatted} KiB' => '{nFormatted} KiB',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '{nFormatted} MiB',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, one{baitas} few{baitai} other{baitų}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} gibi{n, plural, one{baitas} few{baitai} other{baitų}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} giga{n, plural, one{baitas} few{baitai} other{baitų}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} kibi{n, plural, one{baitas} few{baitai} other{baitų}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} kilo{n, plural, one{baitas} few{baitai} other{baitų}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} mebi{n, plural, one{baitas} few{baitai} other{baitų}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} mega{n, plural, one{baitas} few{baitai} other{baitų}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} pebi{n, plural, one{baitas} few{baitai} other{baitų}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} peta{n, plural, one{baitas} few{baitai} other{baitų}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} tebi{n, plural, one{baitas} few{baitai} other{baitų}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} tera{n, plural, one{baitas} few{baitai} other{baitų}}',\n];\n"
  },
  {
    "path": "framework/messages/lv/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => ' un ',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '\"{attribute}\" neatbalsta operātoru \"{operator}\".',\n    '(not set)' => '(nav uzstādīts)',\n    'Action not found.' => 'Darbība nav atrasta',\n    'Aliases available: {aliases}' => 'Pieejamie pseidonīmi: {aliases}',\n    'An internal server error occurred.' => 'Notika servera iekšējā kļūda.',\n    'Are you sure you want to delete this item?' => 'Vai jūs esat pārliecināti, ka vēlaties dzēst šo vienumu?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '\"{attribute}\" nosacījumam jābūt vai nu vērtībai, vai derīgai operatora specifikācijai.',\n    'Delete' => 'Dzēst',\n    'Error' => 'Kļūda',\n    'File upload failed.' => 'Neizdevās augšupielādēt datni.',\n    'Home' => 'Sākums',\n    'Invalid data received for parameter \"{param}\".' => 'Tika saņemta nepareiza vērtība parametram \"{param}\".',\n    'Login Required' => 'Nepieciešama autorizācija',\n    'Missing required arguments: {params}' => 'Trūkst nepieciešamie argumenti: {params}',\n    'Missing required parameters: {params}' => 'Trūkst nepieciešamie parametri: {params}',\n    'No' => 'Nē',\n    'No results found.' => 'Nekas netika atrasts.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Ir atļauts augšupielādēt datnes tikai ar šādiem MIME-tipiem: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Ir atļauts augšupielādēt datnes tikai ar šādiem paplašinājumiem: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => 'Operātoru \"{operator}\" jāizmanto meklēšanas atribūtā',\n    'Operator \"{operator}\" requires multiple operands.' => 'Operātoram \"{operator}\" nepieciešami vairāki operandi',\n    'Options available: {options}' => 'Pieejamas opvijas: {options}',\n    'Page not found.' => 'Pieprasītā lapa netika atrasta.',\n    'Please fix the following errors:' => 'Nepieciešams izlabot šādas kļūdas:',\n    'Please upload a file.' => 'Lūdzu, augšupielādēt datni.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Tiek rādīti ieraksti <b>{begin, number}-{end, number}</b> no <b>{totalCount, number}</b>.',\n    'The combination {values} of {attributes} has already been taken.' => 'Kombinācija {values} priekš {attributes} ir jau aizņemta.',\n    'The file \"{file}\" is not an image.' => 'Saņemtā \"{file}\" datne nav attēls.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'Saņemtās \"{file}\" datnes izmērs pārsniedz pieļaujamo ierobežojumu {formattedLimit} apmērā.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Saņemtās \"{file}\" datnes izmērs ir pārāk maza, tai ir jābūt vismaz {formattedLimit} apmērā.',\n    'The format of {attribute} is invalid.' => '{attribute} vērtības formāts ir nepareizs.',\n    'The format of {filter} is invalid.' => '{filter} formāts ir kļūdains',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Attēls \"{file}\" ir pārāk liels. Augstumam ir jābūt mazākam par {limit, number} {limit, plural, one{pikseli} other{pikseļiem}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Attēls \"{file}\" ir pārāk liels. Platumam ir jābūt mazākam par {limit, number} {limit, plural, one{pikseli} other{pikseļiem}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Attēls \"{file}\" ir pārāk mazs. Augstumam ir jābūt lielākam par {limit, number} {limit, plural, one{pikseli} other{pikseļiem}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Attēls \"{file}\" ir pārāk mazs. Platumam ir jābūt lielākam par {limit, number} {limit, plural, one{pikseli} other{pikseļiem}}.',\n    'The requested view \"{name}\" was not found.' => 'Pieprasītā skata datne \"{name}\" netika atrasta.',\n    'The verification code is incorrect.' => 'Cilvēktesta kods bija nepareizs.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Kopā <b>{count, number}</b> {count, plural, zero{ierakstu} one{ieraksts} other{ieraksti}}.',\n    'Unable to verify your data submission.' => 'Neizdevās apstiprināt saņemtos datus.',\n    'Unknown alias: -{name}' => 'Neatpazīts preidonīms {name}',\n    'Unknown filter attribute \"{attribute}\"' => 'Neatpazīts filtra attribūts \"{attribute}\"',\n    'Unknown option: --{name}' => 'Nezināma iespēja: --{name}',\n    'Update' => 'Labot',\n    'View' => 'Apskatīt',\n    'Yes' => 'Jā',\n    'You are not allowed to perform this action.' => 'Jūs neesat autorizēts veikt šo darbību.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Jūs nevarat augšupielādēt vairāk par {limit, number} {limit, plural, one{failu} other{failiem}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => 'Jums jāaugšupielādē vismaz {limit, number} {limit, mulural, one {file} other {files}}.',\n    'in {delta, plural, =1{a day} other{# days}}' => 'pēc {delta, plural, =1{dienas} one{# dienas} other{# dienām}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'pēc {delta, plural, =1{minūtes} one{# minūtes} other{# minūtēm}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'pēc {delta, plural, =1{mēneša} one{# mēneša} other{# mēnešiem}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'pēc {delta, plural, =1{sekundes} one{# sekundes} other{# sekundēm}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'pēc {delta, plural, =1{gada} one{# gada} other{# gadiem}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'pēc {delta, plural, =1{stundas} one{# stundas} other{# stundām}}',\n    'just now' => 'tikko',\n    'the input value' => 'ievadītā vērtība',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" jau ir aizņemts.',\n    '{attribute} cannot be blank.' => 'Ir jāaizpilda {attribute}.',\n    '{attribute} contains wrong subnet mask.' => '{attribute} satur kļūdainu apakštīklu.',\n    '{attribute} is invalid.' => '{attribute} vērtība ir nepareiza.',\n    '{attribute} is not a valid URL.' => '{attribute} vērtība nav pareiza URL formātā.',\n    '{attribute} is not a valid email address.' => '{attribute} vērtība nav pareizas e-pasta adreses formātā.',\n    '{attribute} is not in the allowed range.' => '{attribute} nav atļautajā diapazonā.',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} vērtībai ir jābūt vienādai ar \"{requiredValue}\".',\n    '{attribute} must be a number.' => '{attribute} vērtībai ir jābūt skaitlim.',\n    '{attribute} must be a string.' => '{attribute} vērtībai ir jābūt simbolu virknei.',\n    '{attribute} must be a valid IP address.' => '{attribute} jābūt derīgai IP adresei.',\n    '{attribute} must be an IP address with specified subnet.' => '{attribute} jābūt IP adresei ar norādīto apakštīklu.',\n    '{attribute} must be an integer.' => '{attribute} vērtībai ir jābūt veselam skaitlim.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} vērtībai ir jābūt \"{true}\" vai \"{false}\".',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '{attribute} vērtībai ir jābūt vienādai ar \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} vērtībai ir jābūt lielākai par \"{compareValueOrAttribute}\" vērtību.',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} vērtībai ir jābūt lielākai vai vienādai ar \"{compareValueOrAttribute}\" vērtību.',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} vērtībai ir jābūt mazākai par \"{compareValueOrAttribute}\" vērtību.',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} vērtībai ir jābūt mazākai vai vienādai ar \"{compareValueOrAttribute}\" vērtību.',\n    '{attribute} must be no greater than {max}.' => '{attribute} vērtībai ir jābūt ne lielākai par {max}.',\n    '{attribute} must be no less than {min}.' => '{attribute} vērtībai ir jābūt ne mazākai par {min}.',\n    '{attribute} must not be a subnet.' => '{attribute} nedrīkst būt apakštīkls.',\n    '{attribute} must not be an IPv4 address.' => '{attribute} nedrīkst būt IPv4 adrese.',\n    '{attribute} must not be an IPv6 address.' => '{attribute} nedrīkst būt IPv6 adrese.',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} vērtība nedrīkst būt vienāda ar \"{compareValueOrAttribute}\" vērtību.',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} vērtībai ir jābūt ne īsākai par {min, number} {min, plural, one{simbolu} other{simboliem}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} vērtībai ir jābūt ne garākai par {max, number} {max, plural, one{simbolu} other{simboliem}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} vērtībai ir jāsastāv no {length, number} {length, plural, one{simbola} other{simboliem}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, zero{# dienas} one{# diena} other{# dienas}}',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, zero{# stundas} one{# stunda} other{# stundas}}',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, zero{# minūtes} one{# minūte} other{# minūtes}}',\n    '{delta, plural, =1{1 month} other{# months}}' => '{delta, plural, zero{# mēneši} one{# mēnesis} other{# mēneši}}',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta, plural, zero{# sekundes} one{# sekunde} other{# sekundes}}',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta, plural, zero{# gadi} one{# gads} other{# gadi}}',\n    '{delta, plural, =1{a day} other{# days}} ago' => 'pirms {delta, plural, =1{dienas} one{# dienas} other{# dienām}}',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => 'pirms {delta, plural, =1{minūtes} one{# minūtes} other{# minūtēm}}',\n    '{delta, plural, =1{a month} other{# months}} ago' => 'pirms {delta, plural, =1{mēneša} one{# mēneša} other{# mēnešiem}}',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => 'pirms {delta, plural, =1{sekundes} one{# sekundes} other{# sekundēm}}',\n    '{delta, plural, =1{a year} other{# years}} ago' => 'pirms {delta, plural, =1{gada} one{# gada} other{# gadiem}}',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => 'pirms {delta, plural, =1{stundas} one{# stundas} other{# stundām}}',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} Gb',\n    '{nFormatted} GiB' => '{nFormatted} GiB',\n    '{nFormatted} KiB' => '{nFormatted} KiB',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '{nFormatted} MiB',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, zero{baitu} one{baits} other{baiti}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} gibi{n, plural, zero{baitu} one{baits} other{baiti}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} giga{n, plural, zero{baitu} one{baits} other{baiti}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} kibi{n, plural, zero{baitu} one{baits} other{baiti}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} kilo{n, plural, zero{baitu} one{baits} other{baiti}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} mebi{n, plural, zero{baitu} one{baits} other{baiti}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} mega{n, plural, zero{baitu} one{baits} other{baiti}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} pebi{n, plural, zero{baitu} one{baits} other{baiti}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} peta{n, plural, zero{baitu} one{baits} other{baiti}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} tebi{n, plural, zero{baitu} one{baits} other{baiti}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} tera{n, plural, zero{baitu} one{baits} other{baiti}}',\n];\n"
  },
  {
    "path": "framework/messages/ms/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => '',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(tidak ditetapkan)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Ralat dalaman pelayan web telah berlaku',\n    'Are you sure you want to delete this item?' => 'Adakah anda pasti untuk menghapuskan item ini?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'Padam',\n    'Error' => 'Ralat',\n    'File upload failed.' => 'Gagal memuat naik fail',\n    'Home' => 'Utama',\n    'Invalid data received for parameter \"{param}\".' => 'Data yang tidak sah untuk parameter \"{param}\".',\n    'Login Required' => 'Wajib untuk Log Masuk',\n    'Missing required arguments: {params}' => 'Kehilangan penyataan yang mandatori: {params}',\n    'Missing required parameters: {params}' => 'Kehilangan parameter yang mandatori: {params}',\n    'No' => 'Tidak',\n    'No results found.' => 'Tiada keputusan dijumpai',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Hanya fail yang berjenis MIME dibenarkan: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Hanya fail yang mempunyai sambungan berikut dibenarkan: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Halaman tidak dijumpai.',\n    'Please fix the following errors:' => 'Sila betulkan ralat berikut:',\n    'Please upload a file.' => 'Sila muat naik fail',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Memaparkan <b>{begin, number}-{end, number}</b> daripada <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.',\n    'The combination {values} of {attributes} has already been taken.' => '',\n    'The file \"{file}\" is not an image.' => 'Fail ini \"{file}\" bukan berjenis gambar.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'Fail ini \"{file}\" terlalu besar. Saiz tidak boleh lebih besar daripada {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Fail ini \"{file}\" terlalu kecil. Saiznya tidak boleh lebih kecil daripada {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'Format untuk atribut ini {attribute} tidak sah.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Gambar \"{file}\" terlalu panjang. Panjang gambar tidak boleh lebih besar daripada {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Gambar \"{file}\" terlalu lebar. Gambar tidak boleh lebih lebar daripada {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Gambar \"{file}\" terlalu singkat. Panjang tidak boleh lebih singkat daripada {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Gambar \"{file}\" terlalu kecil. Lebar gambar tidak boleh kurang daripada {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The requested view \"{name}\" was not found.' => 'Paparan yang diminta \"{name}\" tidak dijumpai.',\n    'The verification code is incorrect.' => 'Kod penyesah tidak tepat.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Jumlah <b>{count, number}</b> {count, plural, one{item} other{items}}.',\n    'Unable to verify your data submission.' => 'Tidak bejaya mengesahkan data yang dihantar.',\n    'Unknown alias: -{name}' => '',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'Pilihan lain: --{name}',\n    'Update' => 'Kemaskini',\n    'View' => 'Paparan',\n    'Yes' => 'Ya',\n    'You are not allowed to perform this action.' => 'Anda tidak dibenarkan untuk mengunakan fungsi ini.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Anda boleh memuat naik tidak lebih daripada  {limit, number} {limit, plural, one{file} other{files}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => 'dalam {delta, plural, =1{1 hari} other{# hari}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'dalam {delta, plural, =1{1 minit} other{# minit}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'dalam {delta, plural, =1{1 bulan} other{# bulan}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'dalam {delta, plural, =1{1 saat} other{# saat}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'dalam {delta, plural, =1{1 tahun} other{# tahun}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'dalam {delta, plural, =1{1 jam} other{# jam}}',\n    'just now' => 'baru sahaja',\n    'the input value' => 'nilai input',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" telah digunakan.',\n    '{attribute} cannot be blank.' => '{attribute} tidak boleh dibiarkan kosong.',\n    '{attribute} contains wrong subnet mask.' => '',\n    '{attribute} is invalid.' => '{attribute} tidak sah.',\n    '{attribute} is not a valid URL.' => '{attribute} alamat URL yang tidak sah.',\n    '{attribute} is not a valid email address.' => '{attribute} adalah alamat email yang tidak sah.',\n    '{attribute} is not in the allowed range.' => '',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} mestilah \"{requiredValue}\".',\n    '{attribute} must be a number.' => '{attribute} mestilah nombor.',\n    '{attribute} must be a string.' => '{attribute} mestilah perkataan.',\n    '{attribute} must be a valid IP address.' => '',\n    '{attribute} must be an IP address with specified subnet.' => '',\n    '{attribute} must be an integer.' => '{attribute} mestilah nombor tanpa titik perpuluhan.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} mestilah \"{true}\" atau \"{false}\".',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} mestilah lebih besar daripada \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} mestilah lebih besar atau sama dengan \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} mestilah kurang daripada \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} mestilah kurang daripada atau sama dengan \"{compareValueOrAttribute}\".',\n    '{attribute} must be no greater than {max}.' => '{attribute} tidak boleh lebih besar daripada {max}.',\n    '{attribute} must be no less than {min}.' => '{attribute} tidak boleh kurang daripada {min}.',\n    '{attribute} must not be a subnet.' => '',\n    '{attribute} must not be an IPv4 address.' => '',\n    '{attribute} must not be an IPv6 address.' => '',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} mestilah tidak sama dengan \"{compareValueOrAttribute}\".',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} mesti mengandungi sekurang-kurangnya {min, number} {min, plural, one{character} other{characters}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} mesti mengangungi paling banyak {max, number} {max, plural, one{character} other{characters}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} mesti mengandungi {length, number} {length, plural, one{character} other{characters}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '',\n    '{delta, plural, =1{1 month} other{# months}}' => '',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '',\n    '{delta, plural, =1{1 year} other{# years}}' => '',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{1 hari} other{# hari}} lalu',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{1 minit} other{# minit}} lalu',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{1 bulan} other{# bulan}} lalu',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{1 saat} other{# saat}} lalu',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{1 tahun} other{# tahun}} lalu',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, =1{1 jam} other{# jam}} lalu',\n    '{nFormatted} B' => '',\n    '{nFormatted} GB' => '',\n    '{nFormatted} GiB' => '',\n    '{nFormatted} KiB' => '',\n    '{nFormatted} MB' => '',\n    '{nFormatted} MiB' => '',\n    '{nFormatted} PB' => '',\n    '{nFormatted} PiB' => '',\n    '{nFormatted} TB' => '',\n    '{nFormatted} TiB' => '',\n    '{nFormatted} kB' => '',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '',\n];\n"
  },
  {
    "path": "framework/messages/mt/yii.php",
    "content": "<?php\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\n\nreturn [\n    ' and ' => ' u ',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '\"{attribute}\" ma jappoġġjax l-operatur \"{operator}\".',\n    '(not set)' => '(mhux issettjat)',\n    'Action not found.' => 'Azzjoni ma nstabitx.',\n    'Aliases available: {aliases}' => 'Alias disponibbli: {aliases}',\n    'An internal server error occurred.' => 'Sar żball intern tas-server.',\n    'Are you sure you want to delete this item?' => 'Int żgur li trid tħassar dan l-oġġett?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => 'Kundizzjoni għal \"{attribute}\" għandu jkun jew valur jew speċifikazzjoni valida tal-operatur.',\n    'Delete' => 'Ħassar',\n    'Error' => 'Żball',\n    'File upload failed.' => 'It-tlugħ tal-fajl falla.',\n    'Home' => 'Dar',\n    'Invalid data received for parameter \"{param}\".' => 'Data invalida riċevuta għall-parametru \"{param}\".',\n    'Login Required' => 'Login Meħtieġa',\n    'Missing required arguments: {params}' => 'Argumenti meħtieġa nieqsa: {params}',\n    'Missing required parameters: {params}' => 'Parametri meħtieġa nieqsa: {params}',\n    'No' => 'Nru',\n    'No results found.' => 'Ma nstab l-ebda riżultat.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Fajls b\\'dawn it-tipi MIME biss huma permessi: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Fajls b\\'dawn l-estensjonijiet biss huma permessi: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => 'Operatur \"{operator}\" għandu jintuża ma\\' attribut ta\\' tfittxija.',\n    'Operator \"{operator}\" requires multiple operands.' => 'Operatur \"{operator}\" teħtieġ operandi multipli.',\n    'Options available: {options}' => 'Għażliet disponibbli: {options}',\n    'Page not found.' => 'Paġna mhux misjuba.',\n    'Please fix the following errors:' => 'Jekk jogħġbok waħħal l-iżbalji li ġejjin:',\n    'Please upload a file.' => 'Jekk jogħġbok ittella\\' fajl.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Wiri <b>{begin, number}-{end, number}</b> ta <b>{totalCount, number}</b> {totalCount, plural, one{oġġett} other{oġġetti}}.',\n    'The combination {values} of {attributes} has already been taken.' => 'Il-kombinazzjoni {values} ta {attributes} diġà ttieħdet.',\n    'The file \"{file}\" is not an image.' => 'Il-fajl \"{file}\" mhix immaġni',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'Il-fajl \"{file}\" hija kbira wisq. Id-daqs tiegħu ma jistax jaqbeż {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Il-fajl \"{file}\" huwa żgħir wisq. Id-daqs tiegħu ma jistax ikun iżgħar minn {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'Il-format ta\\'  {attribute} hija invalida.',\n    'The format of {filter} is invalid.' => 'Il-format ta\\'  {filter}  hija invalida.',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'L-immaġni \"{file}\" hija kbira wisq. L-għoli ma jistax ikun akbar minn {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'L-immaġni \"{file}\" hija kbira wisq. Il-wisa \\'ma tistax tkun akbar minn {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'L-immaġni \"{file}\" huwa żgħir wisq. L-għoli ma jistax ikun iżgħar minn {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'L-immaġni \"{file}\" huwa żgħir wisq. Il-wisa \\'ma tistax tkun iżgħar minn {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The requested view \"{name}\" was not found.' => 'Il-veduta mitluba \"{name}\" ma nstabx.',\n    'The verification code is incorrect.' => 'Il-kodiċi ta\\' verifika mhux korrett.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Total <b>{count, number}</b> {count, plural, one{oġġett} other{oġġetti}}.',\n    'Unable to verify your data submission.' => 'Ma tistax tivverifika s-sottomissjoni tad-dejta tiegħek.',\n    'Unknown alias: -{name}' => 'Psewdonimu mhux magħruf: -{name}',\n    'Unknown filter attribute \"{attribute}\"' => 'Attribut tal-filtru mhux magħruf \"{attribute}\"',\n    'Unknown option: --{name}' => 'Għażla mhux magħrufa: --{name}',\n    'Update' => 'Aġġornament',\n    'View' => 'Ara',\n    'Yes' => 'Iva',\n    'You are not allowed to perform this action.' => 'M\\'intix permess li twettaq din l-azzjoni.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Tista \\'ttella\\' l-aktar {limit, number} {limit, plural, one{file} other{files}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => 'Għandek ittella mill-inqas {limit, number} {limit, plural, one{file} other{files}}.',\n    'in {delta, plural, =1{a day} other{# days}}' => 'fi {delta, plural, =1{a jum} other{# jiem}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'fi {delta, plural, =1{minuta} other{# minuti}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'fi {delta, plural, =1{xahar} other{# xhur}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'fi {delta, plural, =1{it-tieni} other{# sekondi}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'fi {delta, plural, =1{sena} other{# snin}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'fi {delta, plural, =1{siegħa} other{# sigħat}}',\n    'just now' => 'biss issa',\n    'the input value' => 'il-valur tad-dħul',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" diġà ttieħdet.',\n    '{attribute} cannot be blank.' => '{attribute} ma jistax ikun vojt.',\n    '{attribute} contains wrong subnet mask.' => '{attribute} fih subnet mask ħażina.',\n    '{attribute} is invalid.' => '{attribute} hija invalida.',\n    '{attribute} is not a valid URL.' => '{attribute} mhuwiex URL validu.',\n    '{attribute} is not a valid email address.' => '{attribute} mhuwiex indirizz elettroniku validu.',\n    '{attribute} is not in the allowed range.' => '{attribute} mhix fil-medda permessa.',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} għandu jkun \"{requiredValue}\".',\n    '{attribute} must be a number.' => '{attribute} irid ikun numru.',\n    '{attribute} must be a string.' => '{attribute} għandu jkun spag.',\n    '{attribute} must be a valid IP address.' => '{attribute} għandu jkun indirizz IP validu.',\n    '{attribute} must be an IP address with specified subnet.' => '{attribute} ',\n    '{attribute} must be an integer.' => '{attribute} għandu jkun numru sħiħ.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} trid tkun jew \"{true}\" jew \"{false}\".',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '{attribute} trid tkun ugwali għal \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} għandu jkun akbar minn \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} għandu jkun akbar minn jew ugwali għal \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} għandu jkun inqas minn \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} għandu jkun inqas minn jew ugwali għal \"{compareValueOrAttribute}\".',\n    '{attribute} must be no greater than {max}.' => '{attribute} m\\'għandux ikun akbar minn {max}.',\n    '{attribute} must be no less than {min}.' => '{attribute} għandu jkun mhux inqas minn {min}.',\n    '{attribute} must not be a subnet.' => '{attribute} m\\'għandux ikun subnet.',\n    '{attribute} must not be an IPv4 address.' => '{attribute} m\\'għandux ikun indirizz IPv4.',\n    '{attribute} must not be an IPv6 address.' => '{attribute} m\\'għandux ikun indirizz IPv6.',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} m\\'għandux ikun ugwali għal \"{compareValueOrAttribute}\".',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} għandu jkun fih mill-inqas {min, number} {min, plural, one{character} other{characters}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} għandu jkun fih l-aktar {max, number} {max, plural, one{character} other{characters}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} għandu jkun fih {length, number} {length, plural, one{character} other{characters}}.',\n    '{compareAttribute} is invalid.' => '{compareAttribute} hija invalida',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, =1{1 jum} other{# jiem}}',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, =1{1 siegħa} other{# sigħat}}',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, =1{1 minuta} other{# minuti}}',\n    '{delta, plural, =1{1 month} other{# months}}' => '{delta, plural, =1{1 xahar} other{# xhur}}',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta, plural, =1{1 it-tieni} other{# sekondi}}',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta, plural, =1{1 sena} other{# snin}}',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{jum} other{# jiem}} ago',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{minuta} other{# minuti}} ago',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{xahar} other{# xhur}} ago',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{it-tieni} other{# sekondi}} ago',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{sena} other{# snin}} ago',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, =1{siegħa} other{# sigħat}} ago',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '{nFormatted} GiB',\n    '{nFormatted} KiB' => '{nFormatted} KiB',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '{nFormatted} MiB',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, =1{byte} other{bytes}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}',\n];\n"
  },
  {
    "path": "framework/messages/nb-NO/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => '',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(ikke angitt)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'En intern serverfeil oppstod.',\n    'Are you sure you want to delete this item?' => 'Er du sikker på at du vil slette dette elementet?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'Slett',\n    'Error' => 'Feil',\n    'File upload failed.' => 'Filopplasting feilet.',\n    'Home' => 'Hjem',\n    'Invalid data received for parameter \"{param}\".' => 'Ugyldig data mottatt for parameter \"{param}\".',\n    'Login Required' => 'Innlogging påkrevet',\n    'Missing required arguments: {params}' => 'Mangler obligatoriske argumenter: {params}',\n    'Missing required parameters: {params}' => 'Mangler obligatoriske parametere: {params}',\n    'No' => 'Nei',\n    'No results found.' => 'Ingen resultater funnet.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Bare filer med disse MIME-typene er tillatt: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Bare filer med disse filendelsene er tillatt: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Siden finnes ikke.',\n    'Please fix the following errors:' => 'Vennligs fiks følgende feil:',\n    'Please upload a file.' => 'Vennligs last opp en fil.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Viser <b>{begin, number}-{end, number}</b> av <b>{totalCount, number}</b> {totalCount, plural, one{element} other{elementer}}.',\n    'The combination {values} of {attributes} has already been taken.' => '',\n    'The file \"{file}\" is not an image.' => 'Filen \"{file}\" er ikke et bilde.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'Filen \"{file}\" er for stor. Størrelsen kan ikke overskride {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Filen \"{file}\" er for liten. Størrelsen kan ikke være mindre enn {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'Formatet til {attribute} er ugyldig.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Bildet \"{file}\" er for stort. Høyden kan ikke overskride {limit, number} {limit, plural, one{piksel} other{piksler}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Bildet \"{file}\" er for stort. Bredden kan ikke overskride {limit, number} {limit, plural, one{piksel} other{piksler}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Bildet \"{file}\" er for lite. Høyden kan ikke være mindre enn {limit, number} {limit, plural, one{piksel} other{piksler}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Bildet \"{file}\" er for lite. Bredden kan ikke være mindre enn {limit, number} {limit, plural, one{piksel} other{piksler}}.',\n    'The requested view \"{name}\" was not found.' => 'Den forespurte visningen \"{name}\" ble ikke funnet.',\n    'The verification code is incorrect.' => 'Verifiseringskoden er feil.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Totalt <b>{count, number}</b> {count, plural, one{element} other{elementer}}.',\n    'Unable to verify your data submission.' => 'Kunne ikke verifisere innsendt data.',\n    'Unknown alias: -{name}' => '',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'Ukjent alternativ: --{name}',\n    'Update' => 'Oppdater',\n    'View' => 'Vis',\n    'Yes' => 'Ja',\n    'You are not allowed to perform this action.' => 'Du har ikke tilatelse til å gjennomføre denne handlingen.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Du kan laste opp maks {limit, number} {limit, plural, one{fil} other{filer}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => 'om {delta, plural, =1{en dag} other{# dager}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'om {delta, plural, =1{ett minutt} other{# minutter}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'om {delta, plural, =1{en måned} other{# måneder}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'om {delta, plural, =1{ett sekund} other{# sekunder}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'om {delta, plural, =1{ett år} other{# år}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'om {delta, plural, =1{en time} other{# timer}}',\n    'just now' => 'akkurat nå',\n    'the input value' => 'inndataverdien',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" er allerede tatt i bruk.',\n    '{attribute} cannot be blank.' => '{attribute} kan ikke være tomt.',\n    '{attribute} contains wrong subnet mask.' => '',\n    '{attribute} is invalid.' => '{attribute} er ugyldig.',\n    '{attribute} is not a valid URL.' => '{attribute} er ikke en gyldig URL.',\n    '{attribute} is not a valid email address.' => '{attribute} er ikke en gyldig e-postadresse.',\n    '{attribute} is not in the allowed range.' => '',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} må være \"{requiredValue}\".',\n    '{attribute} must be a number.' => '{attribute} må være et nummer.',\n    '{attribute} must be a string.' => '{attribute} må være en tekststreng.',\n    '{attribute} must be a valid IP address.' => '',\n    '{attribute} must be an IP address with specified subnet.' => '',\n    '{attribute} must be an integer.' => '{attribute} må være et heltall.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} må være enten \"{true}\" eller \"{false}\".',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} må være større enn \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} må være større enn eller lik \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} må være mindre enn \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} må være mindre enn eller lik \"{compareValueOrAttribute}\".',\n    '{attribute} must be no greater than {max}.' => '{attribute} kan ikke være større enn {max}.',\n    '{attribute} must be no less than {min}.' => '{attribute} kan ikke være mindre enn {min}.',\n    '{attribute} must not be a subnet.' => '',\n    '{attribute} must not be an IPv4 address.' => '',\n    '{attribute} must not be an IPv6 address.' => '',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} kan ikke være lik \"{compareValueOrAttribute}\".',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} må inneholde minst {min, number} {min, plural, one{tegn} other{tegn}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} kan inneholde maks {max, number} {max, plural, one{tegn} other{tegn}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} må inneholde {length, number} {length, plural, one{tegn} other{tegn}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '',\n    '{delta, plural, =1{1 month} other{# months}}' => '',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '',\n    '{delta, plural, =1{1 year} other{# years}}' => '',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{en dag} other{# dager}} siden',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{ett minutt} other{# minutter}} siden',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{en måned} other{# måneder}} siden',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{ett sekund} other{# sekunder}} siden',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{ett år} other{# år}} siden',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, =1{en time} other{# timer}} siden',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '{nFormatted} GiB',\n    '{nFormatted} KiB' => '{nFormatted} KiB',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '{nFormatted} MiB',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, =1{byte} other{byte}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '',\n];\n"
  },
  {
    "path": "framework/messages/nl/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => '',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(niet ingesteld)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Er is een interne serverfout opgetreden.',\n    'Are you sure you want to delete this item?' => 'Weet je zeker dat je dit item wilt verwijderen?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'Verwijderen',\n    'Error' => 'Fout',\n    'File upload failed.' => 'Bestand uploaden mislukt.',\n    'Home' => 'Home',\n    'Invalid data received for parameter \"{param}\".' => 'Ongeldige gegevens ontvangen voor parameter \"{param}\".',\n    'Login Required' => 'Inloggen verplicht',\n    'Missing required arguments: {params}' => 'Ontbrekende vereiste argumenten: {params}',\n    'Missing required parameters: {params}' => 'Ontbrekende vereiste parameters: {params}',\n    'No' => 'Nee',\n    'No results found.' => 'Geen resultaten gevonden',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Alleen bestanden met de volgende MIME types zijn toegestaan: {mimeTypes}',\n    'Only files with these extensions are allowed: {extensions}.' => 'Alleen bestanden met de volgende extensies zijn toegestaan: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Pagina niet gevonden.',\n    'Please fix the following errors:' => 'Corrigeer de volgende fouten:',\n    'Please upload a file.' => 'Upload een bestand.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Resultaat <b>{begin, number}-{end, number}</b> van <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.',\n    'The combination {values} of {attributes} has already been taken.' => '',\n    'The file \"{file}\" is not an image.' => 'Het bestand \"{file}\" is geen afbeelding.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'Het bestand \"{file}\" is te groot. Het kan niet groter zijn dan {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Het bestand \"{file}\" is te klein. Het kan niet kleiner zijn dan {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'Het formaat van {attribute} is ongeldig.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'De afbeelding \"{file}\" is te groot. Het mag maximaal {limit, number} {limit, plural, one{pixel} other{pixels}} hoog zijn.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'De afbeelding \"{file}\" is te groot. Het mag maximaal {limit, number} {limit, plural, one{pixel} other{pixels}} breed zijn.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'De afbeelding \"{file}\" is te klein. Het moet minimaal {limit, number} {limit, plural, one{pixel} other{pixels}} hoog zijn.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'De afbeelding \"{file}\" is te klein. Het moet minimaal {limit, number} {limit, plural, one{pixel} other{pixels}} breed zijn.',\n    'The requested view \"{name}\" was not found.' => 'De gevraagde view \"{name}\" werd niet gevonden.',\n    'The verification code is incorrect.' => 'De verificatiecode is onjuist.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Totaal <b>{count, number}</b> {count, plural, one{item} other{items}}.',\n    'Unable to verify your data submission.' => 'Het is niet mogelijk uw verstrekte gegevens te verifiëren.',\n    'Unknown alias: -{name}' => '',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'Onbekende optie: --{name}',\n    'Update' => 'Bewerk',\n    'View' => 'Bekijk',\n    'Yes' => 'Ja',\n    'You are not allowed to perform this action.' => 'U bent niet gemachtigd om deze actie uit te voeren.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'U kunt maximaal {limit, number} {limit, plural, one{ander bestand} other{andere bestanden}} uploaden.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => 'binnen {delta, plural, =1{een dag} other{# dagen}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'binnen {delta, plural, =1{een minuut} other{# minuten}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'binnen {delta, plural, =1{een maand} other{# maanden}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'binnen {delta, plural, =1{een seconde} other{# seconden}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'binnen {delta, plural, =1{een jaar} other{# jaar}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'binnen {delta, plural, =1{een uur} other{# uur}}',\n    'just now' => 'zojuist',\n    'the input value' => 'de invoerwaarde',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" is reeds in gebruik.',\n    '{attribute} cannot be blank.' => '{attribute} mag niet leeg zijn.',\n    '{attribute} contains wrong subnet mask.' => '',\n    '{attribute} is invalid.' => '{attribute} is ongeldig.',\n    '{attribute} is not a valid URL.' => '{attribute} is geen geldige URL.',\n    '{attribute} is not a valid email address.' => '{attribute} is geen geldig e-mailadres.',\n    '{attribute} is not in the allowed range.' => '',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} moet \"{requiredValue}\" zijn.',\n    '{attribute} must be a number.' => '{attribute} moet een getal zijn.',\n    '{attribute} must be a string.' => '{attribute} moet een string zijn.',\n    '{attribute} must be a valid IP address.' => '',\n    '{attribute} must be an IP address with specified subnet.' => '',\n    '{attribute} must be an integer.' => '{attribute} moet een geheel getal zijn.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} moet \"{true}\" of \"{false}\" zijn.',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} moet groter zijn dan \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} moet groter dan of gelijk aan \"{compareValueOrAttribute}\" zijn.',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} moet minder zijn dan \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} moet minder dan of gelijk aan \"{compareValueOrAttribute}\" zijn.',\n    '{attribute} must be no greater than {max}.' => '{attribute} mag niet groter zijn dan {max}.',\n    '{attribute} must be no less than {min}.' => '{attribute} mag niet kleiner zijn dan {min}.',\n    '{attribute} must not be a subnet.' => '',\n    '{attribute} must not be an IPv4 address.' => '',\n    '{attribute} must not be an IPv6 address.' => '',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} mag niet gelijk zijn aan \"{compareValueOrAttribute}\".',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} moet minstens {min, number} {min, plural, one{karakter} other{karakters}} bevatten.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} mag maximaal {max, number} {max, plural, one{karakter} other{karakters}} bevatten.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} moet precies {min, number} {min, plural, one{karakter} other{karakters}} bevatten.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, one{# dag} other{# dagen}}',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, one{# uur} other{# uur}}',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, one{# minuut} other{# minuten}}',\n    '{delta, plural, =1{1 month} other{# months}}' => '{delta, plural, one{# maand} other{# maanden}}',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta, plural, one{# seconde} other{# seconden}}',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta, plural, one{# jaar} other{# jaar}}',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{een dag} other{# dagen}} geleden',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{een minuut} other{# minuten}} geleden',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{een maand} other{# maanden}} geleden',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{een seconde} other{# seconden}} geleden',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{een jaar} other{# jaar}} geleden',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, =1{een uur} other{# uur}} geleden',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '{nFormatted} GiB',\n    '{nFormatted} KiB' => '{nFormatted} KiB',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '{nFormatted} MiB',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, =1{byte} other{bytes}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}',\n];\n"
  },
  {
    "path": "framework/messages/pl/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => ' i ',\n    '\"{attribute}\" does not support operator \"{operator}\".' => 'Operator \"{operator}\" nie jest dozwolony dla \"{attribute}\".',\n    '(not set)' => '(brak wartości)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Wystąpił wewnętrzny błąd serwera.',\n    'Are you sure you want to delete this item?' => 'Czy na pewno usunąć ten element?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => 'Warunek dla \"{attribute}\" powinien mieć określoną wartość lub być operatorem o prawidłowej konstrukcji.',\n    'Delete' => 'Usuń',\n    'Error' => 'Błąd',\n    'File upload failed.' => 'Wgrywanie pliku nie powiodło się.',\n    'Home' => 'Strona domowa',\n    'Invalid data received for parameter \"{param}\".' => 'Otrzymano nieprawidłowe dane dla parametru \"{param}\".',\n    'Login Required' => 'Wymagane zalogowanie się',\n    'Missing required arguments: {params}' => 'Brak wymaganych argumentów: {params}',\n    'Missing required parameters: {params}' => 'Brak wymaganych parametrów: {params}',\n    'No' => 'Nie',\n    'No results found.' => 'Brak wyników.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Dozwolone są tylko pliki z następującymi typami MIME: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Dozwolone są tylko pliki z następującymi rozszerzeniami: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => 'Operator \"{operator}\" musi być użyty razem z atrybutem wyszukiwania.',\n    'Operator \"{operator}\" requires multiple operands.' => 'Operator \"{operator}\" wymaga więcej niż jednego argumentu.',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Nie odnaleziono strony.',\n    'Please fix the following errors:' => 'Proszę poprawić następujące błędy:',\n    'Please upload a file.' => 'Proszę wgrać plik.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Wyświetlone <b>{begin, number}-{end, number}</b> z <b>{totalCount, number}</b> {totalCount, plural, one{rekordu} other{rekordów}}.',\n    'The combination {values} of {attributes} has already been taken.' => 'Zestawienie {values} dla {attributes} jest już w użyciu.',\n    'The file \"{file}\" is not an image.' => 'Plik \"{file}\" nie jest obrazem.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'Plik \"{file}\" jest zbyt duży. Jego rozmiar nie może przekraczać {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Plik \"{file}\" jest za mały. Jego rozmiar nie może być mniejszy niż {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'Format {attribute} jest nieprawidłowy.',\n    'The format of {filter} is invalid.' => 'Format {filter} jest nieprawidłowy.',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Obraz \"{file}\" jest zbyt duży. Wysokość nie może być większa niż {limit, number} {limit, plural, one{piksela} few{pikseli} many{pikseli} other{piksela}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Obraz \"{file}\" jest zbyt duży. Szerokość nie może być większa niż {limit, number} {limit, plural, one{piksela} few{pikseli} many{pikseli} other{piksela}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Obraz \"{file}\" jest za mały. Wysokość nie może być mniejsza niż {limit, number} {limit, plural, one{piksela} few{pikseli} many{pikseli} other{piksela}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Obraz \"{file}\" jest za mały. Szerokość nie może być mniejsza niż {limit, number} {limit, plural, one{piksela} few{pikseli} many{pikseli} other{piksela}}.',\n    'The requested view \"{name}\" was not found.' => 'Żądany widok \"{name}\" nie został odnaleziony.',\n    'The verification code is incorrect.' => 'Kod weryfikacyjny jest nieprawidłowy.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Razem <b>{count, number}</b> {count, plural, one{rekord} few{rekordy} many{rekordów} other{rekordu}}.',\n    'Unable to verify your data submission.' => 'Nie udało się zweryfikować przesłanych danych.',\n    'Unknown alias: -{name}' => 'Nieznany alias: -{name}',\n    'Unknown filter attribute \"{attribute}\"' => 'Nieznany atrybut filtru \"{attribute}\"',\n    'Unknown option: --{name}' => 'Nieznana opcja: --{name}',\n    'Update' => 'Aktualizuj',\n    'View' => 'Zobacz szczegóły',\n    'Yes' => 'Tak',\n    'You are not allowed to perform this action.' => 'Brak upoważnienia do wykonania tej czynności.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Możliwe wgranie najwyżej {limit, number} {limit, plural, one{pliku} few{plików} many{plików} other{pliku}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => 'Należy wgrać przynajmniej {limit, number} {limit, plural, one{plik} few{pliki} many{plików} other{pliku}}.',\n    'in {delta, plural, =1{a day} other{# days}}' => 'za {delta, plural, =1{jeden dzień} other{# dni}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'za {delta, plural, =1{minutę} few{# minuty} many{# minut} other{# minuty}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'za {delta, plural, =1{miesiąc} few{# miesiące} many{# miesięcy} other{# miesiąca}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'za {delta, plural, =1{sekundę} few{# sekundy} many{# sekund} other{# sekundy}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'za {delta, plural, =1{rok} few{# lata} many{# lat} other{# roku}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'za {delta, plural, =1{godzinę} few{# godziny} many{# godzin} other{# godziny}}',\n    'just now' => 'przed chwilą',\n    'the input value' => 'wartość wejściowa',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" jest już w użyciu.',\n    '{attribute} cannot be blank.' => '{attribute} nie może pozostać bez wartości.',\n    '{attribute} contains wrong subnet mask.' => '{attribute} posiada złą maskę podsieci.',\n    '{attribute} is invalid.' => '{attribute} zawiera nieprawidłową wartość.',\n    '{attribute} is not a valid URL.' => '{attribute} nie zawiera prawidłowego adresu URL.',\n    '{attribute} is not a valid email address.' => '{attribute} nie zawiera prawidłowego adresu email.',\n    '{attribute} is not in the allowed range.' => '{attribute} nie jest w dozwolonym zakresie.',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} musi mieć wartość \"{requiredValue}\".',\n    '{attribute} must be a number.' => '{attribute} musi być liczbą.',\n    '{attribute} must be a string.' => '{attribute} musi być tekstem.',\n    '{attribute} must be a valid IP address.' => '{attribute} musi być poprawnym adresem IP.',\n    '{attribute} must be an IP address with specified subnet.' => '{attribute} musi być adresem IP w określonej podsieci.',\n    '{attribute} must be an integer.' => '{attribute} musi być liczbą całkowitą.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} musi mieć wartość \"{true}\" lub \"{false}\".',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '{attribute} musi mieć tę samą wartość co \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} musi mieć wartość większą od \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} musi mieć wartość większą lub równą \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} musi mieć wartość mniejszą od \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} musi mieć wartość mniejszą lub równą \"{compareValueOrAttribute}\".',\n    '{attribute} must be no greater than {max}.' => '{attribute} musi wynosić nie więcej niż {max}.',\n    '{attribute} must be no less than {min}.' => '{attribute} musi wynosić nie mniej niż {min}.',\n    '{attribute} must not be a subnet.' => '{attribute} nie może być podsiecią.',\n    '{attribute} must not be an IPv4 address.' => '{attribute} nie może być adresem IPv4.',\n    '{attribute} must not be an IPv6 address.' => '{attribute} nie może być adresem IPv6.',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} musi mieć wartość różną od \"{compareValueOrAttribute}\".',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} musi zawierać co najmniej {min, number} {min, plural, one{znak} few{znaki} many{znaków} other{znaku}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} musi zawierać nie więcej niż {max, number} {max, plural, one{znak} few{znaki} many{znaków} other{znaku}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} musi zawierać dokładnie {length, number} {length, plural, one{znak} few{znaki} many{znaków} other{znaku}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, =1{1 dzień} few{# dni} many{# dni} other{# dnia}}',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, =1{1 godzina} few{# godziny} many{# godzin} other{# godziny}}',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, =1{1 minuta} few{# minuty} many{# minut} other{# minuty}}',\n    '{delta, plural, =1{1 month} other{# months}}' => '{delta, plural, =1{1 miesiąc} few{# miesiące} many{# miesięcy} other{# miesiąca}}',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta, plural, =1{1 sekunda} few{# sekundy} many{# sekund} other{# sekundy}}',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta, plural, =1{1 rok} few{# lata} many{# lat} other{# roku}}',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{jeden dzień} few{# dni} many{# dni} other{# dnia}} temu',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{minutę} few{# minuty} many{# minut} other{# minuty}} temu',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{miesiąc} few{# miesiące} many{# miesięcy} other{# miesiąca}} temu',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{sekundę} few{# sekundy} many{# sekund} other{# sekundy}} temu',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{rok} few{# lata} many{# lat} other{# roku}} temu',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, =1{godzinę} few{# godziny} many{# godzin} other{# godziny}} temu',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '{nFormatted} GiB',\n    '{nFormatted} KiB' => '{nFormatted} KiB',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '{nFormatted} MiB',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, =1{bajt} few{bajty} many{bajtów} other{bajta}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, =1{gibibajt} few{gibibajty} many{gibibajtów} other{gibibajta}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, =1{gigabajt} few{gigabajty} many{gigabajtów} other{gigabajta}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, =1{kibibajt} few{kibibajty} many{kibibajtów} other{kibibajta}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, =1{kilobajt} few{kilobajty} many{kilobajtów} other{kilobajta}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, =1{mebibajt} few{mebibajty} many{mebibajtów} other{mebibajta}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, =1{megabajt} few{megabajty} many{megabajtów} other{megabajta}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, =1{pebibajt} few{pebibajty} many{pebibajtów} other{pebibajta}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, =1{petabajt} few{petabajty} many{petabajtów} other{petabajta}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, =1{tebibajt} few{tebibajty} many{tebibajtów} other{tebibajta}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, =1{terabajt} few{terabajty} many{terabajtów} other{terabajta}}',\n];\n"
  },
  {
    "path": "framework/messages/pt/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => ' e ',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '\"{attribute}\" não suporta o operador \"{operator}\".',\n    '(not set)' => '(não definido)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Ocorreu um erro interno do servidor.',\n    'Are you sure you want to delete this item?' => 'Tens a certeza que queres apagar este item?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => 'A condição para \"{attribute}\" tem de ser ou um valor ou uma especificação válida do operador.',\n    'Delete' => 'Apagar',\n    'Error' => 'Erro',\n    'File upload failed.' => 'O upload do ficheiro falhou.',\n    'Home' => 'Página Inicial',\n    'Invalid data received for parameter \"{param}\".' => 'Dados inválidos recebidos para o parâmetro “{param}”.',\n    'Login Required' => 'Login Necessário.',\n    'Missing required arguments: {params}' => 'Argumentos obrigatórios em falta: {params}',\n    'Missing required parameters: {params}' => 'Parâmetros obrigatórios em falta: {params}',\n    'No' => 'Não',\n    'No results found.' => 'Não foram encontrados resultados.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Apenas ficheiros com este tipo de MIME são permitidos: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Só são permitidos ficheiros com as seguintes extensões: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => 'O operador \"{operator}\" tem de ser usado com um atributo de pesquisa.',\n    'Operator \"{operator}\" requires multiple operands.' => 'O operador \"{operator}\" requer vários operandos.',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Página não encontrada.',\n    'Please fix the following errors:' => 'Por favor, corrija os seguintes erros:',\n    'Please upload a file.' => 'Por favor faça upload de um ficheiro.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'A exibir <b>{begin, number}-{end, number}</b> de <b>{totalCount, number}</b> {totalCount, plural, one{item} other{itens}}.',\n    'The combination {values} of {attributes} has already been taken.' => 'A combinação {values} de {attributes} já está a ser utilizada.',\n    'The file \"{file}\" is not an image.' => 'O ficheiro “{file}” não é uma imagem.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'O ficheiro “{file}” é grande demais. O tamanho não pode exceder {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'O ficheiro “{file}” é pequeno demais. O tamanho não pode ser menor do que {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'O formato de “{attribute}” é inválido.',\n    'The format of {filter} is invalid.' => 'O formato de {filter} é inválido.',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'O ficheiro “{file}” é grande demais. A altura não pode ser maior do que {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'O ficheiro “{file}” é grande demais. A largura não pode ser maior do que {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'O ficheiro “{file}” é pequeno demais. A altura não pode ser menor do que {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'O ficheiro “{file}” é pequeno demais. A largura não pode ser menor do que {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The requested view \"{name}\" was not found.' => 'A visualização solicitada \"{name}\" não foi encontrada.',\n    'The verification code is incorrect.' => 'O código de verificação está incorreto.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Total <b>{count, number}</b> {count, plural, one{item} other{itens}}.',\n    'Unable to verify your data submission.' => 'Não foi possível verificar a sua submissão de dados.',\n    'Unknown alias: -{name}' => 'Alias desconhecido: -{name}',\n    'Unknown filter attribute \"{attribute}\"' => 'Atributo de filtro desconhecido \"{attribute}\"',\n    'Unknown option: --{name}' => 'Opção desconhecida : --{name}',\n    'Update' => 'Atualizar',\n    'View' => 'Ver',\n    'Yes' => 'Sim',\n    'You are not allowed to perform this action.' => 'Você não está autorizado a realizar essa ação.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Você pode fazer o upload de no máximo {limit, number} {limit, plural, one{ficheiro} other{ficheiros}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => 'A transferência deve ser pelo menos {limit, number} {limit, plural, one{ficheiro} other{ficheiros}}. ',\n    'in {delta, plural, =1{a day} other{# days}}' => 'em {delta, plural, =1{um dia} other{# dias}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'em {delta, plural, =1{um minuto} other{# minutos}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'em {delta, plural, =1{um mês} other{# meses}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'em {delta, plural, =1{um segundo} other{# segundos}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'em {delta, plural, =1{um ano} other{# anos}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'em {delta, plural, =1{uma hora} other{# horas}}',\n    'just now' => 'agora mesmo',\n    'the input value' => 'o valor de entrada',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} “{value}” já foi atribuido.',\n    '{attribute} cannot be blank.' => '“{attribute}” não pode ficar em branco.',\n    '{attribute} contains wrong subnet mask.' => '{attribute} contém uma máscara de sub-rede errada.',\n    '{attribute} is invalid.' => '“{attribute}” é inválido.',\n    '{attribute} is not a valid URL.' => '“{attribute}” não é uma URL válida.',\n    '{attribute} is not a valid email address.' => '“{attribute}” não é um endereço de e-mail válido.',\n    '{attribute} is not in the allowed range.' => '{attribute} não está no alcance permitido.',\n    '{attribute} must be \"{requiredValue}\".' => '“{attribute}” deve ser “{requiredValue}”.',\n    '{attribute} must be a number.' => '“{attribute}” deve ser um número.',\n    '{attribute} must be a string.' => '“{attribute}” deve ser uma string.',\n    '{attribute} must be a valid IP address.' => '{attribute} tem de ser um endereço IP válido.',\n    '{attribute} must be an IP address with specified subnet.' => '{attribute} tem de ser um endereço IP com a sub-rede especificada.',\n    '{attribute} must be an integer.' => '“{attribute}” deve ser um número inteiro.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '“{attribute}” deve ser “{true}” ou “{false}”.',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '{attribute} tem de ser igual a \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} tem de ser maior que \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} tem de ser maior ou igual a \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} tem de ser menor que \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} tem de ser menor ou igual a \"{compareValueOrAttribute}\".',\n    '{attribute} must be no greater than {max}.' => '“{attribute}” não pode ser maior do que {max}.',\n    '{attribute} must be no less than {min}.' => '“{attribute}” não pode ser menor do que {min}.',\n    '{attribute} must not be a subnet.' => '{attribute} não pode ser uma sub-rede.',\n    '{attribute} must not be an IPv4 address.' => '{attribute} não pode ser um endereço IPv4.',\n    '{attribute} must not be an IPv6 address.' => '{attribute} não pode ser um endereço IPv6.',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} não pode ser igual a \"{compareValueOrAttribute}\".',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '“{attribute}” deve conter pelo menos {min, number} {min, plural, one{caractere} other{caracteres}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '“{attribute}” deve conter no máximo {max, number} {max, plural, one{caractere} other{caracteres}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '“{attribute}” deve conter {length, number} {length, plural, one{caractere} other{caracteres}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, =1{1 dia} other{# dias}}',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, =1{1 hora} other{# horas}}',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, =1{1 minuto} other{# minutos}}',\n    '{delta, plural, =1{1 month} other{# months}}' => '{delta, plural, =1{1 mês} other{# meses}}',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta, plural, =1{1 segundo} other{# segundos}}',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta, plural, =1{1 ano} other{# anos}}',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{um dia} other{# dias}} atrás',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{um minuto} other{# minutos}} atrás',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{um mês} other{# meses}} atrás',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{um segundo} other{# segundos}} atrás',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{um ano} other{# anos}} atrás',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, =1{uma hora} other{# horas}} atrás',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '{nFormatted} GiB',\n    '{nFormatted} KiB' => '{nFormatted} KiB',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '{nFormatted} MiB',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, =1{byte} other{bytes}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}',\n];\n"
  },
  {
    "path": "framework/messages/pt-BR/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => ' e ',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '\"{attribute}\" não suporta o operador \"{operator}\".',\n    '(not set)' => '(não definido)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Ocorreu um erro interno do servidor.',\n    'Are you sure you want to delete this item?' => 'Deseja realmente excluir este item?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => 'A condição para \"{attribute}\" deve ser um valor ou a especificação de um operador válido.',\n    'Delete' => 'Excluir',\n    'Error' => 'Erro',\n    'File upload failed.' => 'O upload do arquivo falhou.',\n    'Home' => 'Página Inicial',\n    'Invalid data received for parameter \"{param}\".' => 'Dados inválidos recebidos para o parâmetro \"{param}\".',\n    'Login Required' => 'Login Necessário.',\n    'Missing required arguments: {params}' => 'Argumentos obrigatórios ausentes: {params}',\n    'Missing required parameters: {params}' => 'Parâmetros obrigatórios ausentes: {params}',\n    'No' => 'Não',\n    'No results found.' => 'Nenhum resultado foi encontrado.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'São permitidos somente arquivos com os seguintes tipos MIME: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'São permitidos somente arquivos com as seguintes extensões: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => 'O operador \"{operator}\" deve ser usado com um atributo de busca.',\n    'Operator \"{operator}\" requires multiple operands.' => 'O operador \"{operator}\" requer múltiplos operandos.',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Página não encontrada.',\n    'Please fix the following errors:' => 'Por favor, corrija os seguintes erros:',\n    'Please upload a file.' => 'Por favor, faça upload de um arquivo.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Exibindo <b>{begin, number}-{end, number}</b> de <b>{totalCount, number}</b> {totalCount, plural, one{item} other{itens}}.',\n    'The combination {values} of {attributes} has already been taken.' => 'A combinação {values} de {attributes} já foi utilizado.',\n    'The file \"{file}\" is not an image.' => 'O arquivo \"{file}\" não é uma imagem.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'O arquivo \"{file}\" é grande demais. Seu tamanho não pode exceder {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'O arquivo \"{file}\" é pequeno demais. Seu tamanho não pode ser menor que {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'O formato de \"{attribute}\" é inválido.',\n    'The format of {filter} is invalid.' => 'O formato de {filter} é inválido.',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'O arquivo \"{file}\" é grande demais. A altura não pode ser maior que {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'O arquivo \"{file}\" é grande demais. A largura não pode ser maior que {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'O arquivo \"{file}\" é pequeno demais. A altura não pode ser menor que {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'O arquivo \"{file}\" é pequeno demais. A largura não pode ser menor que {limit, number} {limit, plural, one{pixel} other{pixels}}.',\n    'The requested view \"{name}\" was not found.' => 'A visão \"{name}\" solicitada não foi encontrada.',\n    'The verification code is incorrect.' => 'O código de verificação está incorreto.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Total <b>{count, number}</b> {count, plural, one{item} other{itens}}.',\n    'Unable to verify your data submission.' => 'Não foi possível verificar o seu envio de dados.',\n    'Unknown alias: -{name}' => 'Alias desconhecido: -{name}',\n    'Unknown filter attribute \"{attribute}\"' => 'Atributo de filtro desconhecido \"{attribute}\"',\n    'Unknown option: --{name}' => 'Opção desconhecida : --{name}',\n    'Update' => 'Alterar',\n    'View' => 'Exibir',\n    'Yes' => 'Sim',\n    'You are not allowed to perform this action.' => 'Você não está autorizado a realizar essa ação.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Você pode fazer o upload de, no máximo, {limit, number} {limit, plural, one{arquivo} other{arquivos}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => 'Você deve enviar ao menos {limit, number} {limit, plural, one{arquivo} other{arquivos}}.',\n    'in {delta, plural, =1{a day} other{# days}}' => 'em {delta, plural, =1{1 dia} other{# dias}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'em {delta, plural, =1{1 minuto} other{# minutos}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'em {delta, plural, =1{1 mês} other{# meses}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'em {delta, plural, =1{1 segundo} other{# segundos}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'em {delta, plural, =1{1 ano} other{# anos}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'em {delta, plural, =1{1 hora} other{# horas}}',\n    'just now' => 'agora mesmo',\n    'the input value' => 'o valor de entrada',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" já foi utilizado.',\n    '{attribute} cannot be blank.' => '\"{attribute}\" não pode ficar em branco.',\n    '{attribute} contains wrong subnet mask.' => '{attribute} contém a máscara de sub-rede errado.',\n    '{attribute} is invalid.' => '\"{attribute}\" é inválido.',\n    '{attribute} is not a valid URL.' => '\"{attribute}\" não é uma URL válida.',\n    '{attribute} is not a valid email address.' => '\"{attribute}\" não é um endereço de e-mail válido.',\n    '{attribute} is not in the allowed range.' => '{attribute} intervalo não permitido.',\n    '{attribute} must be \"{requiredValue}\".' => '\"{attribute}\" deve ser \"{requiredValue}\".',\n    '{attribute} must be a number.' => '\"{attribute}\" deve ser um número.',\n    '{attribute} must be a string.' => '\"{attribute}\" deve ser um texto.',\n    '{attribute} must be a valid IP address.' => '{attribute} deve ser um endereço IP válido.',\n    '{attribute} must be an IP address with specified subnet.' => '{attribute} deve ser um endereço IP com sub-rede especificada.',\n    '{attribute} must be an integer.' => '\"{attribute}\" deve ser um número inteiro.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '\"{attribute}\" deve ser \"{true}\" ou \"{false}\".',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '{attribute} deve ser igual a \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '\"{attribute}\" deve ser maior que \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '\"{attribute}\" deve ser maior ou igual a \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '\"{attribute}\" deve ser menor que \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '\"{attribute}\" deve ser menor ou igual a \"{compareValueOrAttribute}\".',\n    '{attribute} must be no greater than {max}.' => '\"{attribute}\" não pode ser maior que {max}.',\n    '{attribute} must be no less than {min}.' => '\"{attribute}\" não pode ser menor que {min}.',\n    '{attribute} must not be a subnet.' => '{attribute} não deve ser uma sub-rede.',\n    '{attribute} must not be an IPv4 address.' => '{attribute} não deve ser um endereço IPv4.',\n    '{attribute} must not be an IPv6 address.' => '{attribute} não deve ser um endereço IPv6.',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '\"{attribute}\" não deve ser igual a \"{compareValueOrAttribute}\".',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '\"{attribute}\" deve conter pelo menos {min, number} {min, plural, one{caractere} other{caracteres}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '\"{attribute}\" deve conter no máximo {max, number} {max, plural, one{caractere} other{caracteres}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '\"{attribute}\" deve conter {length, number} {length, plural, one{caractere} other{caracteres}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, =1{1 dia} other{# dias}}',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, =1{1 hora} other{# horas}}',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, =1{1 minuto} other{# minutos}}',\n    '{delta, plural, =1{1 month} other{# months}}' => '{delta, plural, =1{1 mês} other{# meses}}',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta, plural, =1{1 segundo} other{# segundos}}',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta, plural, =1{1 ano} other{# anos}}',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{há 1 dia} other{há # dias}}',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{há 1 minuto} other{há # minutos}}',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{há 1 mês} other{há # meses}}',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{há 1 segundo} other{há # segundos}}',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{há 1 ano} other{há # anos}}',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, =1{há 1 hora} other{há # horas}}',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '{nFormatted} GiB',\n    '{nFormatted} KiB' => '{nFormatted} KiB',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '{nFormatted} MiB',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, =1{byte} other{bytes}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}',\n];\n"
  },
  {
    "path": "framework/messages/ro/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => '',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(nu este setat)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'A aparut o eroare internă de server.',\n    'Are you sure you want to delete this item?' => '',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'Șterge',\n    'Error' => 'Eroare',\n    'File upload failed.' => 'Încărcarea fișierului a eșuat.',\n    'Home' => 'Acasă',\n    'Invalid data received for parameter \"{param}\".' => 'Date incorecte primite pentru parametrul \"{param}\".',\n    'Login Required' => 'Autentificare obligatorie.',\n    'Missing required arguments: {params}' => 'Lipsesc argumente obligatorii: {params}',\n    'Missing required parameters: {params}' => 'Lipsesc parametrii obligatori: {params}',\n    'No' => 'Nu',\n    'No results found.' => 'Nu a fost găsit niciun rezultat.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => '',\n    'Only files with these extensions are allowed: {extensions}.' => 'Se acceptă numai fișiere cu următoarele extensii: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Pagina nu a fost găsită.',\n    'Please fix the following errors:' => 'Vă rugăm sa corectați următoarele erori:',\n    'Please upload a file.' => 'Vă rugăm sa încărcați un fișier.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Sunt afișați itemii <b>{begin, number}-{end, number}</b> din <b>{totalCount, number}</b>.',\n    'The combination {values} of {attributes} has already been taken.' => '',\n    'The file \"{file}\" is not an image.' => 'Fișierul «{file}» nu este o imagine.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'Fișierul «{file}» este prea mare. Dimensiunea acestuia nu trebuie să fie mai mare de {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Fișierul «{file}» este prea mic. Dimensiunea acestuia nu trebuie sa fie mai mică de {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'Formatul «{attribute}» nu este valid.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Imaginea «{file}» este prea mare. Înălțimea nu trebuie să fie mai mare de {limit, number} {limit, plural, one{pixel} other{pixeli}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Imaginea «{file}» este prea mare. Lățimea nu trebuie să fie mai mare de {limit, number} {limit, plural, one{pixel} other{pixeli}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Imaginea «{file}» este prea mică. Înălțimea nu trebuie să fie mai mică de {limit, number} {limit, plural, one{pixel} other{pixeli}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Imaginea «{file}» este prea mică. Lățimea nu trebuie sa fie mai mică de {limit, number} {limit, plural, one{pixel} other{pixeli}}.',\n    'The requested view \"{name}\" was not found.' => '',\n    'The verification code is incorrect.' => 'Codul de verificare este incorect.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Total <b>{count, number}</b> {count, plural, one{item} other{itemi}}.',\n    'Unable to verify your data submission.' => 'Datele trimise nu au putut fi verificate.',\n    'Unknown alias: -{name}' => '',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'Opțiune necunoscută : --{name}',\n    'Update' => 'Redactare',\n    'View' => 'Vizualizare',\n    'Yes' => 'Da',\n    'You are not allowed to perform this action.' => 'Nu aveți dreptul să efectuați această acțiunea.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Puteți încărca maxim {limit, number} {limit, plural, one{fișier} other{fișiere}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => '',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => '',\n    'in {delta, plural, =1{a month} other{# months}}' => '',\n    'in {delta, plural, =1{a second} other{# seconds}}' => '',\n    'in {delta, plural, =1{a year} other{# years}}' => '',\n    'in {delta, plural, =1{an hour} other{# hours}}' => '',\n    'just now' => '',\n    'the input value' => 'valoarea introdusă',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} «{value}» este deja ocupat.',\n    '{attribute} cannot be blank.' => '«{attribute}» nu poate fi gol.',\n    '{attribute} contains wrong subnet mask.' => '',\n    '{attribute} is invalid.' => '«{attribute}» este incorect.',\n    '{attribute} is not a valid URL.' => '«{attribute}» nu este un URL valid.',\n    '{attribute} is not a valid email address.' => '«{attribute}» nu este o adresă de email validă.',\n    '{attribute} is not in the allowed range.' => '',\n    '{attribute} must be \"{requiredValue}\".' => '«{attribute}» trebuie să fie «{requiredValue}».',\n    '{attribute} must be a number.' => '«{attribute}» trebuie să fie un număr.',\n    '{attribute} must be a string.' => '«{attribute}» trebuie să fie un șir de caractere.',\n    '{attribute} must be a valid IP address.' => '',\n    '{attribute} must be an IP address with specified subnet.' => '',\n    '{attribute} must be an integer.' => '«{attribute}» trebuie să fie un număr întreg.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '«{attribute}» trebuie să fie «{true}» sau «{false}».',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '«{attribute}» trebuie să fie mai mare ca «{compareValueOrAttribute}».',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '«{attribute}» trebuie să fie mai mare sau egal cu «{compareValueOrAttribute}».',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '«{attribute}» trebuie să fie mai mic ca «{compareValueOrAttribute}».',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '«{attribute}» trebuie să fie mai mic sau egal cu «{compareValueOrAttribute}».',\n    '{attribute} must be no greater than {max}.' => '«{attribute}» nu trebuie să fie mai mare ca {max}.',\n    '{attribute} must be no less than {min}.' => '«{attribute}» nu trebuie să fie mai mic ca {min}.',\n    '{attribute} must not be a subnet.' => '',\n    '{attribute} must not be an IPv4 address.' => '',\n    '{attribute} must not be an IPv6 address.' => '',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '«{attribute}» nu trebuie să fie egală cu «{compareValueOrAttribute}».',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '«{attribute}» trebuie să conțină minim {min, number} {min, plural, one{caracter} other{caractere}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '«{attribute}» trebuie să conțină maxim {max, number} {max, plural, one{caracter} other{caractere}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '«{attribute}» trebuie să conțină {length, number} {length, plural, one{caracter} other{caractere}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '',\n    '{delta, plural, =1{1 month} other{# months}}' => '',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '',\n    '{delta, plural, =1{1 year} other{# years}}' => '',\n    '{delta, plural, =1{a day} other{# days}} ago' => '',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '',\n    '{delta, plural, =1{a month} other{# months}} ago' => '',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '',\n    '{delta, plural, =1{a year} other{# years}} ago' => '',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '',\n    '{nFormatted} B' => '',\n    '{nFormatted} GB' => '',\n    '{nFormatted} GiB' => '',\n    '{nFormatted} KiB' => '',\n    '{nFormatted} MB' => '',\n    '{nFormatted} MiB' => '',\n    '{nFormatted} PB' => '',\n    '{nFormatted} PiB' => '',\n    '{nFormatted} TB' => '',\n    '{nFormatted} TiB' => '',\n    '{nFormatted} kB' => '',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '',\n];\n"
  },
  {
    "path": "framework/messages/ru/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => ' и ',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '\"{attribute}\" не поддерживает оператор \"{operator}\".',\n    '(not set)' => '(не задано)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Возникла внутренняя ошибка сервера.',\n    'Are you sure you want to delete this item?' => 'Вы уверены, что хотите удалить этот элемент?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => 'Условие для \"{attribute}\" должно быть или значением или верной спецификацией оператора.',\n    'Delete' => 'Удалить',\n    'Error' => 'Ошибка',\n    'File upload failed.' => 'Загрузка файла не удалась.',\n    'Home' => 'Главная',\n    'Invalid data received for parameter \"{param}\".' => 'Неправильное значение параметра \"{param}\".',\n    'Login Required' => 'Требуется вход.',\n    'Missing required arguments: {params}' => 'Отсутствуют обязательные аргументы: {params}',\n    'Missing required parameters: {params}' => 'Отсутствуют обязательные параметры: {params}',\n    'No' => 'Нет',\n    'No results found.' => 'Ничего не найдено.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Разрешена загрузка файлов только со следующими MIME-типами: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Разрешена загрузка файлов только со следующими расширениями: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => 'Оператор \"{operator}\" должен использоваться через атрибут поиска.',\n    'Operator \"{operator}\" requires multiple operands.' => 'Оператор \"{operator}\" требует несколько операндов.',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Страница не найдена.',\n    'Please fix the following errors:' => 'Исправьте следующие ошибки:',\n    'Please upload a file.' => 'Загрузите файл.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Показаны записи <b>{begin, number}-{end, number}</b> из <b>{totalCount, number}</b>.',\n    'The combination {values} of {attributes} has already been taken.' => 'Комбинация {values} параметров {attributes} уже существует.',\n    'The file \"{file}\" is not an image.' => 'Файл «{file}» не является изображением.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'Файл «{file}» слишком большой. Размер не должен превышать {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Файл «{file}» слишком маленький. Размер должен быть более {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'Неверный формат значения «{attribute}».',\n    'The format of {filter} is invalid.' => 'Формат фильтра {filter} не верен.',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Файл «{file}» слишком большой. Высота не должна превышать {limit, number} {limit, plural, one{пиксель} few{пикселя} many{пикселей} other{пикселя}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Файл «{file}» слишком большой. Ширина не должна превышать {limit, number} {limit, plural, one{пиксель} few{пикселя} many{пикселей} other{пикселя}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Файл «{file}» слишком маленький. Высота должна быть более {limit, number} {limit, plural, one{пиксель} few{пикселя} many{пикселей} other{пикселя}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Файл «{file}» слишком маленький. Ширина должна быть более {limit, number} {limit, plural, one{пиксель} few{пикселя} many{пикселей} other{пикселя}}.',\n    'The requested view \"{name}\" was not found.' => 'Запрашиваемый файл представления \"{name}\" не найден.',\n    'The verification code is incorrect.' => 'Неправильный проверочный код.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Всего <b>{count, number}</b> {count, plural, one{запись} few{записи} many{записей} other{записи}}.',\n    'Unable to verify your data submission.' => 'Не удалось проверить переданные данные.',\n    'Unknown alias: -{name}' => 'Неизвестный псевдоним: -{name}',\n    'Unknown filter attribute \"{attribute}\"' => 'Неизвестный атрибут фильтра \"{attribute}\"',\n    'Unknown option: --{name}' => 'Неизвестная опция: --{name}',\n    'Update' => 'Редактировать',\n    'View' => 'Просмотр',\n    'Yes' => 'Да',\n    'You are not allowed to perform this action.' => 'Вам не разрешено производить данное действие.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Вы не можете загружать более {limit, number} {limit, plural, one{файла} few{файлов} many{файлов} other{файла}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => 'Вы должны загрузить как минимум {limit, number} {limit, plural, one{файл} few{файла} many{файлов} other{файла}}.',\n    'in {delta, plural, =1{a day} other{# days}}' => 'через {delta, plural, =1{день} one{# день} few{# дня} many{# дней} other{# дня}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'через {delta, plural, =1{минуту} one{# минуту} few{# минуты} many{# минут} other{# минуты}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'через {delta, plural, =1{месяц} one{# месяц} few{# месяца} many{# месяцев} other{# месяца}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'через {delta, plural, =1{секунду} one{# секунду} few{# секунды} many{# секунд} other{# секунды}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'через {delta, plural, =1{год} one{# год} few{# года} many{# лет} other{# года}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'через {delta, plural, =1{час} one{# час} few{# часа} many{# часов} other{# часа}}',\n    'just now' => 'прямо сейчас',\n    'the input value' => 'введённое значение',\n    '{attribute} \"{value}\" has already been taken.' => 'Значение «{value}» для «{attribute}» уже занято.',\n    '{attribute} cannot be blank.' => 'Необходимо заполнить «{attribute}».',\n    '{attribute} contains wrong subnet mask.' => 'Значение «{attribute}» содержит неверную маску подсети.',\n    '{attribute} is invalid.' => 'Значение «{attribute}» неверно.',\n    '{attribute} is not a valid URL.' => 'Значение «{attribute}» не является правильным URL.',\n    '{attribute} is not a valid email address.' => 'Значение «{attribute}» не является правильным email адресом.',\n    '{attribute} is not in the allowed range.' => 'Значение «{attribute}» не входит в список разрешенных диапазонов адресов.',\n    '{attribute} must be \"{requiredValue}\".' => 'Значение «{attribute}» должно быть равно «{requiredValue}».',\n    '{attribute} must be a number.' => 'Значение «{attribute}» должно быть числом.',\n    '{attribute} must be a string.' => 'Значение «{attribute}» должно быть строкой.',\n    '{attribute} must be a valid IP address.' => 'Значение «{attribute}» должно быть правильным IP адресом.',\n    '{attribute} must be an IP address with specified subnet.' => 'Значение «{attribute}» должно быть IP адресом с подсетью.',\n    '{attribute} must be an integer.' => 'Значение «{attribute}» должно быть целым числом.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => 'Значение «{attribute}» должно быть равно «{true}» или «{false}».',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => 'Значение «{attribute}» должно быть равно «{compareValueOrAttribute}».',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => 'Значение «{attribute}» должно быть больше значения «{compareValueOrAttribute}».',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => 'Значение «{attribute}» должно быть больше или равно значения «{compareValueOrAttribute}».',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => 'Значение «{attribute}» должно быть меньше значения «{compareValueOrAttribute}».',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => 'Значение «{attribute}» должно быть меньше или равно значения «{compareValueOrAttribute}».',\n    '{attribute} must be no greater than {max}.' => 'Значение «{attribute}» не должно превышать {max}.',\n    '{attribute} must be no less than {min}.' => 'Значение «{attribute}» должно быть не меньше {min}.',\n    '{attribute} must not be a subnet.' => 'Значение «{attribute}» не должно быть подсетью.',\n    '{attribute} must not be an IPv4 address.' => 'Значение «{attribute}» не должно быть IPv4 адресом.',\n    '{attribute} must not be an IPv6 address.' => 'Значение «{attribute}» не должно быть IPv6 адресом.',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => 'Значение «{attribute}» не должно быть равно «{compareValueOrAttribute}».',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => 'Значение «{attribute}» должно содержать минимум {min, number} {min, plural, one{символ} few{символа} many{символов} other{символа}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => 'Значение «{attribute}» должно содержать максимум {max, number} {max, plural, one{символ} few{символа} many{символов} other{символа}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => 'Значение «{attribute}» должно содержать {length, number} {length, plural, one{символ} few{символа} many{символов} other{символа}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, one{# день} few{# дня} many{# дней} other{# дня}}',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, one{# час} few{# часа} many{# часов} other{# часа}}',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, one{# минута} few{# минуты} many{# минут} other{# минуты}}',\n    '{delta, plural, =1{1 month} other{# months}}' => '{delta, plural, one{# месяц} few{# месяца} many{# месяцев} other{# месяца}}',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta, plural, one{# секунда} few{# секунды} many{# секунд} other{# секунды}}',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta, plural, one{# год} few{# года} many{# лет} other{# года}}',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{день} one{# день} few{# дня} many{# дней} other{# дня}} назад',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{минуту} one{# минуту} few{# минуты} many{# минут} other{# минуты}} назад',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{месяц} one{# месяц} few{# месяца} many{# месяцев} other{# месяца}} назад',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{секунду} one{# секунду} few{# секунды} many{# секунд} other{# секунды}} назад',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{год} one{# год} few{# года} many{# лет} other{# года}} назад',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, =1{час} one{# час} few{# часа} many{# часов} other{# часа}} назад',\n    '{nFormatted} B' => '{nFormatted} Б',\n    '{nFormatted} GB' => '{nFormatted} ГБ',\n    '{nFormatted} GiB' => '{nFormatted} ГиБ',\n    '{nFormatted} KiB' => '{nFormatted} КиБ',\n    '{nFormatted} MB' => '{nFormatted} МБ',\n    '{nFormatted} MiB' => '{nFormatted} МиБ',\n    '{nFormatted} PB' => '{nFormatted} ПБ',\n    '{nFormatted} PiB' => '{nFormatted} ПиБ',\n    '{nFormatted} TB' => '{nFormatted} ТБ',\n    '{nFormatted} TiB' => '{nFormatted} ТиБ',\n    '{nFormatted} kB' => '{nFormatted} КБ',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, one{байт} few{байта} many{байтов} other{байта}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, one{гибибайт} few{гибибайта} many{гибибайтов} other{гибибайта}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, one{гигабайт} few{гигабайта} many{гигабайтов} other{гигабайта}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, one{кибибайт} few{кибибайта} many{кибибайтов} other{кибибайта}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, one{килобайт} few{килобайта} many{килобайтов} other{килобайта}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, one{мебибайт} few{мебибайта} many{мебибайтов} other{мебибайта}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, one{мегабайт} few{мегабайта} many{мегабайтов} other{мегабайта}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, one{пебибайт} few{пебибайта} many{пебибайтов} other{пебибайта}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, one{петабайт} few{петабайта} many{петабайтов} other{петабайта}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, one{тебибайт} few{тебибайта} many{тебибайтов} other{тебибайта}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, one{терабайт} few{терабайта} many{терабайтов} other{терабайта}}',\n];\n"
  },
  {
    "path": "framework/messages/sk/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => ' a ',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '\"{attribute}\" nepodporuje operátor \"{operator}\".',\n    '(not set)' => '(nie je nastavené)',\n    'Action not found.' => 'Akcia nebola nájdená.',\n    'Aliases available: {aliases}' => 'Dostupné aliasy: {aliases}',\n    'An internal server error occurred.' => 'Vyskytla sa interná chyba servera.',\n    'Are you sure you want to delete this item?' => 'Skutočne chcete odstrániť tento záznam?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => 'Podmienkou pre \"{attribute}\" by mala byť hodnota alebo platná špecifikácia operátora.',\n    'Delete' => 'Zmazať',\n    'Error' => 'Chyba',\n    'File upload failed.' => 'Súbor sa nepodarilo nahrať.',\n    'Home' => 'Úvod',\n    'Invalid data received for parameter \"{param}\".' => 'Neplatné údaje pre parameter \"{param}\".',\n    'Login Required' => 'Je potrebné sa prihlásiť',\n    'Missing required arguments: {params}' => 'Chýbajú povinné argumenty: {params}',\n    'Missing required parameters: {params}' => 'Chýbajú povinné parametre: {params}',\n    'No' => 'Nie',\n    'No results found.' => 'Neboli nájdené žiadne záznamy.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Povolené sú len súbory nasledovných MIME typov: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Povolené sú len súbory s nasledovnými príponami: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => 'Operátor \"{operator}\" musí byť použitý s atribútom vyhľadávania.',\n    'Operator \"{operator}\" requires multiple operands.' => 'Operátor \"{operator}\" vyžaduje viac operandov.',\n    'Options available: {options}' => 'Dostupné možnosti: {options}',\n    'Page not found.' => 'Stránka nebola nájdená.',\n    'Please fix the following errors:' => 'Opravte prosím nasledujúce chyby:',\n    'Please upload a file.' => 'Nahrajte prosím súbor.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Zobrazujem <b>{begin, number}-{end, number}</b> z <b>{totalCount, number}</b> záznamov.',\n    'The combination {values} of {attributes} has already been taken.' => 'Kombinácia {values} pre {attributes} je už použitá.',\n    'The file \"{file}\" is not an image.' => 'Súbor \"{file}\" nie je obrázok.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'Súbor \"{file}\" je príliš veľký. Veľkosť súboru nesmie byť viac ako {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Súbor \"{file}\" je príliš malý. Veľkosť súboru nesmie byť menej ako {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'Formát atribútu {attribute} je neplatný.',\n    'The format of {filter} is invalid.' => 'Format {filter} je neplatný.',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Obrázok \"{file}\" je príliš veľký. Výška nesmie presiahnuť {limit, number} {limit, plural, =1{pixel} =2{pixle} =3{pixle} =4{pixle} other{pixlov}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Obrázok \"{file}\" je príliš veľký. Šírka nesmie presiahnuť {limit, number} {limit, plural, =1{pixel} =2{pixle} =3{pixle} =4{pixle} other{pixlov}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Obrázok \"{file}\" je príliš malý. Výška nesmie byť menšia ako {limit, number} {limit, plural, =1{pixel} =2{pixle} =3{pixle} =4{pixle} other{pixlov}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Obrázok \"{file}\" je príliš malý. Šírka nesmie byť menšia ako {limit, number} {limit, plural, =1{pixel} =2{pixle} =3{pixle} =4{pixle} other{pixlov}}.',\n    'The requested view \"{name}\" was not found.' => 'Požadovaná stránka \"{name}\" nebola nájdená.',\n    'The verification code is incorrect.' => 'Kód pre overenie je neplatný.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Celkovo <b>{count, number}</b> {count, plural, =1{záznam} =2{záznamy} =3{záznamy} =4{záznamy} other{záznamov}}.',\n    'Unable to verify your data submission.' => 'Nebolo možné preveriť odoslané údaje.',\n    'Unknown alias: -{name}' => 'Neznámy alias: -{name}',\n    'Unknown filter attribute \"{attribute}\"' => 'Neznámy atribút filtra \"{attribute}\"',\n    'Unknown option: --{name}' => 'Neznáme nastavenie: --{name}',\n    'Update' => 'Upraviť',\n    'View' => 'Náhľad',\n    'Yes' => 'Áno',\n    'You are not allowed to perform this action.' => 'Nemáte oprávnenie pre požadovanú akciu.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Nahrať môžete najviac {limit, number} {limit, plural, =1{súbor} =2{súbory} =3{súbory} =4{súbory} other{súborov}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => 'Je potrebné nahrať aspoň {limit, number} {limit, plural, =1{súbor} =2{súbory} =3{súbory} =4{súbory} other{súborov}}.',\n    'in {delta, plural, =1{a day} other{# days}}' => 'o {delta, plural, =1{deň} =2{dni} =3{dni} =4{dni} other{# dní}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'o {delta, plural, =1{minútu} =2{minúty} =3{minúty} =4{minúty} other{# minút}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'o {delta, plural, =1{mesiac} =2{mesiace} =3{mesiace} =4{mesiace} other{# mesiacov}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'o {delta, plural, =1{sekundu} =2{sekundy} =3{sekundy} =4{sekundy} other{# sekúnd}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'o {delta, plural, =1{rok} =2{roky} =3{roky} =4{roky} other{# rokov}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'o {delta, plural, =1{hodinu} =2{hodiny} =3{hodiny} =4{hodiny} other{# hodín}}',\n    'just now' => 'práve teraz',\n    'the input value' => 'vstupná hodnota',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" je už použité.',\n    '{attribute} cannot be blank.' => 'Pole {attribute} nesmie byť prázdne.',\n    '{attribute} contains wrong subnet mask.' => '{attribute} obsahuje neplatnú masku podsiete.',\n    '{attribute} is invalid.' => 'Pole {attribute} je neplatné.',\n    '{attribute} is not a valid URL.' => '{attribute} nie je platná URL.',\n    '{attribute} is not a valid email address.' => '{attribute} nie je platná emailová adresa.',\n    '{attribute} is not in the allowed range.' => '{attribute} je mimo povoleného rozsahu.',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} musí byť \"{requiredValue}\".',\n    '{attribute} must be a number.' => '{attribute} musí byť číslo.',\n    '{attribute} must be a string.' => '{attribute} musí byť reťazec.',\n    '{attribute} must be a valid IP address.' => '{attribute} musí byť platná IP adresa.',\n    '{attribute} must be an IP address with specified subnet.' => '{attribute} musí byť IP adresa so špecifikovanou podsieťou.',\n    '{attribute} must be an integer.' => '{attribute} musí byť celé číslo.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} musí byť \"{true}\" alebo \"{false}\".',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '{attribute} musí byť \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} musí byť väčšie ako \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} musí byť väčšie alebo rovné \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} musí byť menšie ako \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} musí byť menšie alebo rovné \"{compareValueOrAttribute}\".',\n    '{attribute} must be no greater than {max}.' => '{attribute} nesmie byť vyšší ako {max}.',\n    '{attribute} must be no less than {min}.' => '{attribute} nesmie byť nižší ako {min}.',\n    '{attribute} must not be a subnet.' => '{attribute} nesmie byť podsieť.',\n    '{attribute} must not be an IPv4 address.' => '{attribute} nesmie byť IPv4 adresa.',\n    '{attribute} must not be an IPv6 address.' => '{attribute} nesmie byť IPv6 adresa.',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} sa nesmie rovnať \"{compareValueOrAttribute}\".',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} musí obsahovať aspoň {min, number} {min, plural, =1{znak} =2{znaky} =3{znaky} =4{znaky} other{znakov}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} môže obsahovať najviac {max, number} {max, plural, =1{znak} =2{znaky} =3{znaky} =4{znaky} other{znakov}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} musí obsahovať {length, number} {length, plural, =1{znak} =2{znaky} =3{znaky} =4{znaky} other{znakov}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, =1{1 deň} =2{2 dni} =3{3 dni} =4{4 dni} other{# dní}}',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, =1{1 hodina} =2{2 hodiny} =3{3 hodiny} =4{4 hodiny} other{# hodín}}',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, =1{1 minúta} =2{2 minúty} =3{3 minúty} =4{4 minúty} other{# minút}}',\n    '{delta, plural, =1{1 month} other{# months}}' => '{delta, plural, =1{1 mesiac} =2{2 mesiace} =3{3 mesiace} =4{4 mesiace} other{# mesiacov}}',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta, plural, =1{1 sekunda} =2{2 sekundy} =3{3 sekundy} =4{4 sekundy} other{# sekúnd}}',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta, plural, =1{1 rok} =2{2 roky} =3{3 roky} =4{4 roky} other{# rokov}}',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{včera} other{pred # dňami}}',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => 'pred {delta, plural, =1{minútou} other{# minútami}}',\n    '{delta, plural, =1{a month} other{# months}} ago' => 'pred {delta, plural, =1{mesiacom} other{# mesiacmi}}',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => 'pred {delta, plural, =1{sekundou} other{# sekundami}}',\n    '{delta, plural, =1{a year} other{# years}} ago' => 'pred {delta, plural, =1{rokom} other{# rokmi}}',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => 'pred {delta, plural, =1{hodinou} other{# hodinami}}',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '{nFormatted} GiB',\n    '{nFormatted} KiB' => '{nFormatted} KiB',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '{nFormatted} MiB',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, =1{bajt} =2{bajty} =3{bajty} =4{bajty} other{bajtov}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, =1{gibibajt} =2{gibibajty} =3{gibibajty} =4{gibibajty} other{gibibajtov}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, =1{gigabajt} =2{gigabajty} =3{gigabajty} =4{gigabajty} other{gigabajtov}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, =1{kibibajt} =2{kibibajty} =3{kibibajty} =4{kibibajty} other{kibibajtov}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, =1{kilobajt} =2{kilobajty} =3{kilobajty} =4{kilobajty} other{kilobajtov}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, =1{mebibajt} =2{mebibajty} =3{mebibajty} =4{mebibajty} other{mebibajtov}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, =1{megabajt} =2{megabajty} =3{megabajty} =4{megabajty} other{megabajtov}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, =1{pebibajt} =2{pebibajty} =3{pebibajty} =4{pebibajty} other{pebibajtov}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, =1{petabajt} =2{petabajty} =3{petabajty} =4{petabajty} other{petabajtov}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, =1{tebibajt} =2{tebibajty} =3{tebibajty} =4{tebibajty} other{tebibajtov}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, =1{terabajt} =2{terabajty} =3{terabajty} =4{terabajty} other{terabajtov}}',\n];\n"
  },
  {
    "path": "framework/messages/sl/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => '',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(ni nastavljeno)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Prišlo je do notranje napake na strežniku.',\n    'Are you sure you want to delete this item?' => 'Ste prepričani, da želite izbrisati ta element?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'Izbrišite',\n    'Error' => 'Napaka',\n    'File upload failed.' => 'Nalaganje datoteke ni bilo uspešno.',\n    'Home' => 'Domov',\n    'Invalid data received for parameter \"{param}\".' => 'Neveljavni podatki so bili poslani za parameter \"{param}\".',\n    'Login Required' => 'Zahtevana je prijava',\n    'Missing required arguments: {params}' => 'Manjkajo zahtevani argumenti: {params}',\n    'Missing required parameters: {params}' => 'Manjkajo zahtevani parametri: {params}',\n    'No' => 'Ne',\n    'No results found.' => 'Rezultatov ni bilo mogoče najti.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Dovoljene so samo datoteke s temi MIME tipi: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Dovoljene so samo datoteke s temi končnicami: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Strani ni mogoče najti.',\n    'Please fix the following errors:' => 'Prosimo, popravite sledeče napake:',\n    'Please upload a file.' => 'Prosimo, naložite datoteko.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Prikaz <b>{begin, number}-{end, number}</b> od <b>{totalCount, number}</b> {totalCount, plural, one{Element} two{Elementa} few{Elementi} other{Elementov}}.',\n    'The combination {values} of {attributes} has already been taken.' => '',\n    'The file \"{file}\" is not an image.' => 'Datoteka \"{file}\" ni slika.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'Datoteka \"{file}\" je prevelika. Njena velikost {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Datoteka \"{file}\" je premajhna. Njena velikost ne sme biti manjša od {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'Format {attribute} ni veljaven.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Slika \"{file}\" je prevelika. Višina ne sme biti večja od {limit, number} {limit, plural, one{piksla} other{pikslov}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Slika \"{file}\" je prevelika. Širina ne sme biti večja od {limit, number} {limit, plural, one{piksla} other{pikslov}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Slika \"{file}\" je premajhna. Višina ne sme biti manjša od {limit, number} {limit, plural, one{piksla} other{pikslov}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Slika \"{file}\" je premajhna. Širina ne sme biti manjša od {limit, number} {limit, plural, one{piksla} other{pikslov}}.',\n    'The requested view \"{name}\" was not found.' => 'Zahtevani pogled \"{name}\" ni bil najden.',\n    'The verification code is incorrect.' => 'Koda za preverjanje je napačna.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Skupaj <b>{count, number}</b> {count, plural, one{element} two{elementa} few{elementi} other{elementov}}.',\n    'Unable to verify your data submission.' => 'Preverjanje vaših poslanih podatkov ni uspelo.',\n    'Unknown alias: -{name}' => '',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'Neznana opcija: --{name}',\n    'Update' => 'Posodobitev',\n    'View' => 'Pogled',\n    'Yes' => 'Da',\n    'You are not allowed to perform this action.' => 'Ta akcija ni dovoljena za izvajanje.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Naložite lahko največ {limit, number} {limit, plural, one{datoteko} two{datoteki} few{datoteke} other{datotek}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => 'v {delta, plural, one{# dnevu} other{# dneh}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'v {delta, plural, one{# minuti} other{# minutah}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'v {delta, plural, one{# mesecu} other{# mesecih}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'v {delta, plural, one{# sekundi} other{# sekundah}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'v {delta, plural, one{# letu} other{# letih}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'v {delta, plural, one{# uri} other{# urah}}',\n    'just now' => 'ravno zdaj',\n    'the input value' => 'vhodna vrednost',\n    '{attribute} \"{value}\" has already been taken.' => 'Atribut {attribute} \"{value}\" je že nastavljen.',\n    '{attribute} cannot be blank.' => 'Atribut {attribute} ne more biti prazen',\n    '{attribute} contains wrong subnet mask.' => '',\n    '{attribute} is invalid.' => 'Atribut {attribute} je neveljaven.',\n    '{attribute} is not a valid URL.' => 'Atribut {attribute} ni veljaven URL.',\n    '{attribute} is not a valid email address.' => 'Atribut {attribute} ni veljaven e-poštni naslov.',\n    '{attribute} is not in the allowed range.' => '',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} mora biti \"{requiredValue}\".',\n    '{attribute} must be a number.' => '{attribute} mora biti število.',\n    '{attribute} must be a string.' => '{attribute} mora biti niz.',\n    '{attribute} must be a valid IP address.' => '',\n    '{attribute} must be an IP address with specified subnet.' => '',\n    '{attribute} must be an integer.' => '{attribute} mora biti celo število.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} mora biti ali \"{true}\" ali \"{false}\".',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => 'Atribut {attribute} mora biti večji od \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => 'Atribut {attribute} mora biti večji ali enak \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => 'Atribut {attribute} mora biti manjši od \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => 'Atribut {attribute} mora biti manjši ali enak \"{compareValueOrAttribute}\".',\n    '{attribute} must be no greater than {max}.' => 'Atribut {attribute} ne sme biti večji od {max}',\n    '{attribute} must be no less than {min}.' => 'Atribut {attribute} ne sme biti manjši od {min}.',\n    '{attribute} must not be a subnet.' => '',\n    '{attribute} must not be an IPv4 address.' => '',\n    '{attribute} must not be an IPv6 address.' => '',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => 'Atribut {attribute} ne sme biti enak \"{compareValueOrAttribute}\".',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => 'Atribut {attribute} mora vsebovati vsaj {min, number} {min, plural, one{znak} two{znaka} few{znake} other{znakov}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => 'Atribut {attribute} mora vsebovati največ {max, number} {max, plural, one{znak} two{znaka} few{znake} other{znakov}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => 'Atribut {attribute} mora vsebovati {length, number} {length, plural, one{znak} two{znaka} few{znake} other{znakov}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '',\n    '{delta, plural, =1{1 month} other{# months}}' => '',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '',\n    '{delta, plural, =1{1 year} other{# years}}' => '',\n    '{delta, plural, =1{a day} other{# days}} ago' => 'pred {delta, plural, one{# dnevom} two{# dnevoma} other{# dnevi}}',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => 'pred {delta, plural, one{# minuto} two{# minutama} other{# minutami}}',\n    '{delta, plural, =1{a month} other{# months}} ago' => 'pred {delta, plural, one{# mesecem} two{# mesecema} other{# meseci}}',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => 'pred {delta, plural, one{# sekundo} two{# sekundama} other{# sekundami}}',\n    '{delta, plural, =1{a year} other{# years}} ago' => 'pred {delta, plural, one{# letom} two{# letoma} other{# leti}}',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => 'pred {delta, plural, one{#uro} two{# urama} other{# urami}}',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '{nFormatted} GiB',\n    '{nFormatted} KiB' => '{nFormatted} KiB',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '{nFormatted} MiB',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, one{bajt} two{bajta} few{bajti} other{bajtov}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, one{gibibajt} two{gibibajta} few{gibibajti} other{gibibajtov}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, one{gigabajt} two{gigabajta} few{gigabajti} other{gigabajtov}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, one{kibibajt} two{kibibajta} few{kibibajti} other{kibibajtov}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, one{kilobajt} two{kilobajta} few{kilobajti} other{kilobajtov}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, one{mebibajt} two{mebibajta} few{mebibajti} other{mebibajtov}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, one{megabajt} two{megabajta} few{megabajti} other{megabajtov}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, one{pebibajt} two{pebibajta} few{pebibajti} other{pebibajtov}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, one{petabajt} two{petabajta} few{petabajti} other{petabajtov}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, one{tebibajt} two{tebibajta} few{tebibajti} other{tebibajtov}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, one{terabajt} two{terabajta} few{terabajti} other{terabajtov}}',\n];\n"
  },
  {
    "path": "framework/messages/sr/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => '',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(без вредности)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Дошло је до интерне грешке на серверу.',\n    'Are you sure you want to delete this item?' => 'Да ли сте сигурни да желите да обришете ову ставку?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'Обриши',\n    'Error' => 'Грешка',\n    'File upload failed.' => 'Постављање фајла није успело.',\n    'Home' => 'Почетна',\n    'Invalid data received for parameter \"{param}\".' => 'Неисправан податак добијен за параметар \"{param}.\"',\n    'Login Required' => 'Пријава на систем је обавезна',\n    'Missing required arguments: {params}' => 'Недостају обавезни аргументи: {params}',\n    'Missing required parameters: {params}' => 'Недостају обавезни параметри: {params}',\n    'No' => 'Не',\n    'No results found.' => 'Нема резултата.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Само следећи MIME типови су дозвољени: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Само фајлови са следећим екстензијама су дозвољени: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Страница није пронађена.',\n    'Please fix the following errors:' => 'Молимо вас исправите следеће грешке:',\n    'Please upload a file.' => 'Молимо вас поставите фајл.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Приказано <b>{begin, number}-{end, number}</b> од <b>{totalCount, number}</b> {totalCount, plural, =1{ставке} one{ставкe} few{ставке} many{ставки} other{ставки}}.',\n    'The combination {values} of {attributes} has already been taken.' => '',\n    'The file \"{file}\" is not an image.' => 'Фајл \"{file}\" није слика.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'Фајл \"{file}\" је превелик. Величина не може бити већа од {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Фајл \"{file}\" је премали. Величина не може бити мања од {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'Формат атрибута \"{attribute}\" је неисправан.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Слика \"{file}\" је превелика. Висина не сме бити већа од {limit, number} {limit, plural, one{пиксел} other{пиксела}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Слика \"{file}\" је превелика. Ширина не сме бити већа од {limit, number} {limit, plural, one{пиксел} other{пиксела}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Слика \"{file}\" је премала. Висина не сме бити мања од {limit, number} {limit, plural, one{пиксел} other{пиксела}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Слика \"{file}\" је премала. Ширина не сме бити мања од {limit, number} {limit, plural, one{пиксел} other{пиксела}}.',\n    'The requested view \"{name}\" was not found.' => 'Тражени приказ \"{name}\" није пронађен.',\n    'The verification code is incorrect.' => 'Код за потврду није исправан.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Укупно <b>{count, number}</b> {count, plural, one{ставка} few{ставке} other{ставки}}.',\n    'Unable to verify your data submission.' => 'Није могуће верификовати ваше послате податке.',\n    'Unknown alias: -{name}' => '',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'Непозната опција: --{name}',\n    'Update' => 'Исправи',\n    'View' => 'Приказ',\n    'Yes' => 'Да',\n    'You are not allowed to perform this action.' => 'Немате права да извршите ову акцију.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Можете поставити највише {limit, number} {limit, plural, one{фајл} few{фајлa} other{фајлова}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => 'за {delta, plural, =1{дан} one{# дан} few{# дана} many{# дана} other{# дана}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'за {delta, plural, =1{минут} one{# минут} few{# минута} many{# минута} other{# минута}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'за {delta, plural, =1{месец} one{# месец} few{# месеца} many{# месеци} other{# месеци}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'за {delta, plural, =1{секунду} one{# секунду} few{# секундe} many{# секунди} other{# секунди}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'за {delta, plural, =1{годину} one{# годину} few{# године} many{# година} other{# година}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'за {delta, plural, =1{сат} one{# сат} few{# сата} many{# сати} other{# сати}}',\n    'just now' => 'управо сада',\n    'the input value' => 'улазна вредност',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" је већ заузет.',\n    '{attribute} cannot be blank.' => '{attribute} не сме бити празан.',\n    '{attribute} contains wrong subnet mask.' => '',\n    '{attribute} is invalid.' => '{attribute} је неисправан.',\n    '{attribute} is not a valid URL.' => '{attribute} није исправан URL.',\n    '{attribute} is not a valid email address.' => '{attribute} није исправна email адреса.',\n    '{attribute} is not in the allowed range.' => '',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} мора бити \"{requiredValue}\".',\n    '{attribute} must be a number.' => '{attribute} мора бити број.',\n    '{attribute} must be a string.' => '{attribute} мора бити текст.',\n    '{attribute} must be a valid IP address.' => '',\n    '{attribute} must be an IP address with specified subnet.' => '',\n    '{attribute} must be an integer.' => '{attribute} мора бити цели број.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} мора бити \"{true}\" или \"{false}\".',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} мора бити већи од \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} мора бити већи или једнак \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} мора бити мањи од \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} мора бити мањи или једнак \"{compareValueOrAttribute}\".',\n    '{attribute} must be no greater than {max}.' => '{attribute} не сме бити већи од \"{max}\".',\n    '{attribute} must be no less than {min}.' => '{attribute} не сме бити мањи од {min}.',\n    '{attribute} must not be a subnet.' => '',\n    '{attribute} must not be an IPv4 address.' => '',\n    '{attribute} must not be an IPv6 address.' => '',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} не сме бити једнак \"{compareValueOrAttribute}\".',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} треба да садржи барем {min, number} {min, plural, one{карактер} other{карактера}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} треба да садржи највише {max, number} {max, plural, one{карактер} other{карактера}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} треба да садржи {length, number} {length, plural, one{карактер} other{карактера}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '',\n    '{delta, plural, =1{1 month} other{# months}}' => '',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '',\n    '{delta, plural, =1{1 year} other{# years}}' => '',\n    '{delta, plural, =1{a day} other{# days}} ago' => 'пре {delta, plural, =1{дан} one{дан} few{# дана} many{# дана} other{# дана}}',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => 'пре {delta, plural, =1{минут} one{# минут} few{# минута} many{# минута} other{# минута}}',\n    '{delta, plural, =1{a month} other{# months}} ago' => 'пре {delta, plural, =1{месец} one{# месец} few{# месеца} many{# месеци} other{# месеци}}',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => 'пре {delta, plural, =1{секунде} one{# секунде} few{# секунде} many{# секунди} other{# секунди}}',\n    '{delta, plural, =1{a year} other{# years}} ago' => 'пре {delta, plural, =1{године} one{# године} few{# године} many{# година} other{# година}}',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => 'пре {delta, plural, =1{сат} one{# сат} few{# сата} many{# сати} other{# сати}}',\n    '{nFormatted} B' => '{nFormatted} Б',\n    '{nFormatted} GB' => '{nFormatted} ГБ',\n    '{nFormatted} GiB' => '',\n    '{nFormatted} KiB' => '',\n    '{nFormatted} MB' => '{nFormatted} МБ',\n    '{nFormatted} MiB' => '',\n    '{nFormatted} PB' => '{nFormatted} ПБ',\n    '{nFormatted} PiB' => '',\n    '{nFormatted} TB' => '{nFormatted} ТБ',\n    '{nFormatted} TiB' => '',\n    '{nFormatted} kB' => '{nFormatted} кБ',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, one{бајт} few{бајта} many{бајтова} other{бајта}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, one{гигабајт} few{гигабајта} many{гигабајта} other{гигабајта}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, one{килобајт} few{килобајта} many{килобајта} other{килобајта}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, one{мегабајт} few{мегабајта} many{мегабајта} other{мегабајта}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, one{петабајт} few{петабајта} many{петабајта} other{петабајта}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, one{терабајт} few{терабајта} many{терабајта} other{терабајта}}',\n];\n"
  },
  {
    "path": "framework/messages/sr-Latn/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => '',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(bez vrednosti)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Došlo je do interne greške na serveru.',\n    'Are you sure you want to delete this item?' => 'Da li ste sigurni da želite da obrišete ovu stavku?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'Obriši',\n    'Error' => 'Greška',\n    'File upload failed.' => 'Postavljanje fajla nije uspelo.',\n    'Home' => 'Početna',\n    'Invalid data received for parameter \"{param}\".' => 'Neispravan podatak dobijen za parametar \"{param}\".',\n    'Login Required' => 'Prijava na sistem je obavezna',\n    'Missing required arguments: {params}' => 'Nedostaju obavezni argumenti: {params}',\n    'Missing required parameters: {params}' => 'Nedostaju obavezni parametri: {params}',\n    'No' => 'Ne',\n    'No results found.' => 'Nema rezultata.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Samo sledeći MIME tipovi su dozvoljeni: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Samo fajlovi sa sledećim ekstenzijama su dozvoljeni: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Stranica nije pronađena.',\n    'Please fix the following errors:' => 'Molimo vas ispravite sledeće greške:',\n    'Please upload a file.' => 'Molimo vas postavite fajl.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Prikazano <b>{begin, number}-{end, number}</b> od <b>{totalCount, number}</b> {totalCount, plural, =1{stavke} one{stavke} few{stavke} many{stavki} other{stavki}}.',\n    'The combination {values} of {attributes} has already been taken.' => '',\n    'The file \"{file}\" is not an image.' => 'Fajl \"{file}\" nije slika.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'Fajl \"{file}\" je prevelik. Veličina ne može biti veća od {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Fajl \"{file}\" je premali. Veličina ne može biti manja od {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'Format atributa \"{attribute}\" je neispravan.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Slika \"{file}\" je prevelika. Visina ne sme biti veća od {limit, number} {limit, plural, one{piksel} other{piksela}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Slika \"{file}\" je prevelika. Širina ne sme biti veća od {limit, number} {limit, plural, one{piksel} other{piksela}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Slika \"{file}\" je premala. Visina ne sme biti manja od {limit, number} {limit, plural, one{piksel} other{piksela}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Slika \"{file}\" je premala. Širina ne sme biti manja od {limit, number} {limit, plural, one{piksel} other{piksela}}.',\n    'The requested view \"{name}\" was not found.' => 'Traženi prikaz \"{name}\" nije pronađen.',\n    'The verification code is incorrect.' => 'Kod za potvrdu nije ispravan.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Ukupno <b>{count, number}</b> {count, plural, one{stavka} few{stavke} other{stavki}}.',\n    'Unable to verify your data submission.' => 'Nije moguće verifikovati vaše poslate podatke.',\n    'Unknown alias: -{name}' => '',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'Nepoznata opcija: --{name}',\n    'Update' => 'Ispravi',\n    'View' => 'Prikaz',\n    'Yes' => 'Da',\n    'You are not allowed to perform this action.' => 'Nemate prava da izvršite ovu akciju.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Možete postaviti najviše {limit, number} {limit, plural, one{fajl} few{fajla} other{fajlova}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => 'za {delta, plural, =1{dan} one{# dan} few{# dana} many{# dana} other{# dana}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'za {delta, plural, =1{minut} one{# minut} few{# minuta} many{# minuta} other{# minuta}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'za {delta, plural, =1{mesec} one{# mesec} few{# meseca} many{# meseci} other{# meseci}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'za {delta, plural, =1{sekundu} one{# sekundu} few{# sekunde} many{# sekundi} other{# sekundi}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'za {delta, plural, =1{godinu} one{# godinu} few{# godine} many{# godina} other{# godina}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'za {delta, plural, =1{sat} one{# sat} few{# sata} many{# sati} other{# sati}}',\n    'just now' => 'upravo sada',\n    'the input value' => 'ulazna vrednost',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" je već zauzet.',\n    '{attribute} cannot be blank.' => '{attribute} ne sme biti prazan.',\n    '{attribute} contains wrong subnet mask.' => '',\n    '{attribute} is invalid.' => '{attribute} je neispravan.',\n    '{attribute} is not a valid URL.' => '{attribute} nije ispravan URL.',\n    '{attribute} is not a valid email address.' => '{attribute} nije ispravna email adresa.',\n    '{attribute} is not in the allowed range.' => '',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} mora biti \"{requiredValue}\".',\n    '{attribute} must be a number.' => '{attribute} mora biti broj.',\n    '{attribute} must be a string.' => '{attribute} mora biti tekst.',\n    '{attribute} must be a valid IP address.' => '',\n    '{attribute} must be an IP address with specified subnet.' => '',\n    '{attribute} must be an integer.' => '{attribute} mora biti celi broj.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} mora biti \"{true}\" ili \"{false}\".',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} mora biti veći od \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} mora biti veći ili jednak \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} mora biti manji od \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} mora biti manji ili jednak \"{compareValueOrAttribute}\".',\n    '{attribute} must be no greater than {max}.' => '{attribute} ne sme biti veći od \"{max}\".',\n    '{attribute} must be no less than {min}.' => '{attribute} ne sme biti manji od {min}.',\n    '{attribute} must not be a subnet.' => '',\n    '{attribute} must not be an IPv4 address.' => '',\n    '{attribute} must not be an IPv6 address.' => '',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} ne sme biti jednak \"{compareValueOrAttribute}\".',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} treba da sadrži bar {min, number} {min, plural, one{karakter} other{karaktera}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} treba da sadrži najviše {max, number} {max, plural, one{karakter} other{karaktera}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} treba da sadrži {length, number} {length, plural, one{karakter} other{karaktera}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '',\n    '{delta, plural, =1{1 month} other{# months}}' => '',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '',\n    '{delta, plural, =1{1 year} other{# years}}' => '',\n    '{delta, plural, =1{a day} other{# days}} ago' => 'pre {delta, plural, =1{dan} one{# dan} few{# dana} many{# dana} other{# dana}}',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => 'pre {delta, plural, =1{minut} one{# minut} few{# minuta} many{# minuta} other{# minuta}}',\n    '{delta, plural, =1{a month} other{# months}} ago' => 'pre {delta, plural, =1{mesec} one{# meseca} few{# meseca} many{# meseci} other{# meseci}}',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => 'pre {delta, plural, =1{sekundu} one{# sekunde} few{# sekunde} many{# sekundi} other{# sekundi}}',\n    '{delta, plural, =1{a year} other{# years}} ago' => 'pre {delta, plural, =1{godinu} one{# godine} few{# godine} many{# godina} other{# godina}}',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => 'pre {delta, plural, =1{sat} one{# sat} few{# sata} many{# sati} other{# sati}}',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '',\n    '{nFormatted} KiB' => '',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, one{bajt} few{bajta} many{bajtova} other{bajta}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, one{gigabajt} few{gigabajta} many{gigabajta} other{gigabajta}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, one{kilobajt} few{kilobajta} many{kilobajta} other{kilobajta}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, one{megabajt} few{megabajta} many{megabajta} other{megabajta}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, one{petabajt} few{petabajta} many{petabajta} other{petabajta}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, one{terabajt} few{terabajta} many{terabajta} other{terabajta}}',\n];\n"
  },
  {
    "path": "framework/messages/sv/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => '',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(ej satt)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Ett internt serverfel har inträffat.',\n    'Are you sure you want to delete this item?' => 'Är du säker på att du vill radera objektet?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'Radera',\n    'Error' => 'Error',\n    'File upload failed.' => 'Uppladdningen misslyckades.',\n    'Home' => 'Hem',\n    'Invalid data received for parameter \"{param}\".' => 'Ogiltig data har mottagits för parameter \"{param}\".',\n    'Login Required' => 'Inloggning krävs',\n    'Missing required arguments: {params}' => 'Följande begärda variabler saknas: {params}',\n    'Missing required parameters: {params}' => 'Följande begärda parametrar saknas: {params}',\n    'No' => 'Nej',\n    'No results found.' => 'Inga resultat hittades.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Endast filer med följande MIME-typer är tillåtna: {mimeTypes}',\n    'Only files with these extensions are allowed: {extensions}.' => 'Endast filer med följande filnamnstillägg är tillåtna: {extensions}',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Sidan hittades inte.',\n    'Please fix the following errors:' => 'Var god fixa följande fel:',\n    'Please upload a file.' => 'Var god ladda upp en fil.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Visar <b>{begin, number}-{end, number}</b> av <b>{totalCount, number}</b> objekt.',\n    'The combination {values} of {attributes} has already been taken.' => '',\n    'The file \"{file}\" is not an image.' => 'Filen \"{file}\" är inte en bild.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'Filen \"{file}\" är för stor. Filstorleken får inte överskrida {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Filen \"{file}\" är för liten. Filstorleken måste vara minst {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'Formatet för \"{attribute}\" är ogiltigt.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Bilden \"{file}\" är för stor. Höjden får inte överskrida {limit, number} {limit, plural, =1{pixel} other{pixlar}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Bilden \"{file}\" är för stor. Bredden får inte överskrida {limit, number} {limit, plural, =1{pixel} other{pixlar}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Bilden \"{file}\" är för liten. Bilden måste vara minst {limit, number} {limit, plural, =1{pixel} other{pixlar}} hög.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Bilden \"{file}\" är för liten. Bilden måste vara minst {limit, number} {limit, plural, =1{pixel} other{pixlar}} bred.',\n    'The requested view \"{name}\" was not found.' => 'Den begärda vyn \"{name}\" kunde inte hittas.',\n    'The verification code is incorrect.' => 'Verifieringskoden är felaktig.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Totalt <b>{count, number}</b> objekt.',\n    'Unable to verify your data submission.' => 'Det gick inte att verifiera skickad data.',\n    'Unknown alias: -{name}' => '',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'Okänt alternativ: --{name}',\n    'Update' => 'Uppdatera',\n    'View' => 'Visa',\n    'Yes' => 'Ja',\n    'You are not allowed to perform this action.' => 'Du har inte behörighet att utföra den här åtgärden.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Du får inte ladda upp mer än {limit, number} {limit, plural, =1{fil} other{filer}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => 'under {delta, plural, =1{en dag} other{# dagar}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'under {delta, plural, =1{en minut} other{# minuter}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'under {delta, plural, =1{en månad} other{# månader}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'under {delta, plural, =1{en sekund} other{# sekunder}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'under {delta, plural, =1{ett år} other{# år}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'under {delta, plural, =1{en timme} other{# timmar}}',\n    'just now' => 'just nu',\n    'the input value' => 'inmatningsvärdet',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" används redan.',\n    '{attribute} cannot be blank.' => 'Värdet för {attribute} får inte vara tomt.',\n    '{attribute} contains wrong subnet mask.' => '',\n    '{attribute} is invalid.' => 'Värdet för {attribute} är ogiltigt.',\n    '{attribute} is not a valid URL.' => '{attribute} är inte en giltig URL.',\n    '{attribute} is not a valid email address.' => '{attribute} är inte en giltig emailadress.',\n    '{attribute} is not in the allowed range.' => '',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} måste vara satt till \"{requiredValue}\".',\n    '{attribute} must be a number.' => '{attribute} måste vara ett nummer.',\n    '{attribute} must be a string.' => '{attribute} måste vara en sträng.',\n    '{attribute} must be a valid IP address.' => '',\n    '{attribute} must be an IP address with specified subnet.' => '',\n    '{attribute} must be an integer.' => '{attribute} måste vara ett heltal.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} måste vara satt till antingen \"{true}\" eller \"{false}\".',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} måste vara större än \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} måste vara större än eller lika med \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} måste vara mindre än \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} måste vara mindre än eller lika med \"{compareValueOrAttribute}\".',\n    '{attribute} must be no greater than {max}.' => '{attribute} får inte överskrida {max}.',\n    '{attribute} must be no less than {min}.' => '{attribute} får som minst vara {min}.',\n    '{attribute} must not be a subnet.' => '',\n    '{attribute} must not be an IPv4 address.' => '',\n    '{attribute} must not be an IPv6 address.' => '',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} får inte vara satt till \"{compareValueOrAttribute}\".',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} bör innehålla minst {min, number} tecken.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} bör innehålla max {max, number} tecken.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} bör innehålla {length, number} tecken.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '',\n    '{delta, plural, =1{1 month} other{# months}}' => '',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '',\n    '{delta, plural, =1{1 year} other{# years}}' => '',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{en dag} other{# dagar}} sedan',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{en minut} other{# minuter}} sedan',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{en månad} other{# månader}} sedan',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{en sekund} other{# sekunder}} sedan',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{ett år} other{# år}} sedan',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, =1{en timme} other{# timmar}} sedan',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '{nFormatted} GiB',\n    '{nFormatted} KiB' => '{nFormatted} KiB',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '{nFormatted} MiB',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, =1{byte} other{byte}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, =1{gibibyte} other{gibibyte}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, =1{gigabyte} other{gigabyte}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, =1{kibibyte} other{kibibyte}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, =1{kilobyte} other{kilobyte}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, =1{mebibyte} other{mebibyte}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, =1{megabyte} other{megabyte}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, =1{pebibyte} other{pebibyte}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, =1{petabyte} other{petabyte}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, =1{tebibyte} other{tebibyte}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, =1{terabyte} other{terabyte}}',\n];\n"
  },
  {
    "path": "framework/messages/tg/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => ' ва ',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(супориш дода нашуд)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Хатои дохилии сервер рух дод.',\n    'Are you sure you want to delete this item?' => 'Шумо боварманд ҳастед, ки ҳамин элементро нест кардан мехоҳед?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'Нест кардан',\n    'Error' => 'Иштибоҳ',\n    'File upload failed.' => 'Фарокашии файл муяссар гашт.',\n    'Home' => 'Саҳифаи асосӣ',\n    'Invalid data received for parameter \"{param}\".' => 'Маънои нодурусти параметри \"{param}\".',\n    'Login Required' => 'Вуруд талаб карда мешавад.',\n    'Missing required arguments: {params}' => 'Далелҳои лозимӣ вуҷуд надоранд: {params}',\n    'Missing required parameters: {params}' => 'Параметрҳои лозимӣ вуҷуд надоранд: {params}',\n    'No' => 'Не',\n    'No results found.' => 'Ҳеҷ чиз ёфт нашуд.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Барои фарокашии файлҳо танҳо бо намудҳои зерини MIME иҷозат аст: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Барои фарокашии файлҳо танҳо тавассути зиёдкуни зерин иҷозат аст: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Саҳифа ёфт нашуд.',\n    'Please fix the following errors:' => 'Лутфан, хатогиҳои зеринро ислоҳ намоед:',\n    'Please upload a file.' => 'Лутфан, файлро бор кунед.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Қайдҳо нишон дода шудаанд <b>{begin, number}-{end, number}</b> аз <b>{totalCount, number}</b>.',\n    'The combination {values} of {attributes} has already been taken.' => 'Комбинатсияи {values} параметрҳо {attributes} аллакай вуҷуд дорад.',\n    'The file \"{file}\" is not an image.' => 'Файли \"{file}\" тасвир нест.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'Ҳаҷми файли \"{file}\" азҳад зиёд калон аст. Андозаи он набояд аз {formattedLimit} зиёдтар бошад.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Ҳаҷми файли \"{file}\" аз ҳад зиёд хурд аст. Он бояд аз {formattedLimit} калонтар бошад.',\n    'The format of {attribute} is invalid.' => 'Формати нодурусти маънӣ {attribute}.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Ҳаҷми файли \"{file}\" аз ҳад зиёд калон аст. Баландияш набояд аз {limit, number} {limit, plural, one{пиксел} few{пиксел} many{пиксел} other{пиксел}} зиёд бошад.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Ҳаҷми файл \"{file}\" аз ҳад зиёд калон аст. Дарозияш набояд аз {limit, number} {limit, plural, one{пиксел} few{пиксел} many{пиксел} other{пиксел}} зиёд бошад.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Ҳаҷми файл \"{file}\" аз ҳад зиёд хурд аст. Баландияш бояд аз {limit, number} {limit, plural, one{пиксел} few{пиксел} many{пиксел} other{пиксел}} зиёд бошад.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Ҳаҷми файл \"{file}\" аз ҳад зиёд хурд аст. Дарозияш бояд аз {limit, number} {limit, plural, one{пиксел} few{пиксел} many{пиксел} other{пиксел}} зиёд бошад.',\n    'The requested view \"{name}\" was not found.' => 'Файл дархостшудаи ҷадвали \"{name}\" ёфт нашуд.',\n    'The verification code is incorrect.' => 'Рамзи нодурусти санҷишӣ.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Ҳамаги <b>{count, number}</b> {count, plural, one{қайд} few{қайд} many{қайдҳо} other{қайд}}.',\n    'Unable to verify your data submission.' => 'Санҷидани маълумоти фиристодаи Шумо муяссар нагардид.',\n    'Unknown alias: -{name}' => 'Тахаллуси номаълум: -{name}',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'Гузинаи номаълум: --{name}',\n    'Update' => 'Таҳрир намудан',\n    'View' => 'Аз назар гузарондан',\n    'Yes' => 'Ҳа',\n    'You are not allowed to perform this action.' => 'Шумо барои анҷом додани амали мазкур иҷозат надоред.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Шумо наметавонед зиёда аз {limit, number} {limit, plural,one{файлро} few{файлҳоро} many{файлро} other{файлро}} фаро бикашед.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => 'баъд аз {delta, plural, =1{рӯз} one{# рӯз} few{# рӯз} many{# рӯз} other{# рӯз}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'баъд аз {delta, plural, =1{дақиқа} one{# дақиқа} few{# дақиқа} many{# дақиқа} other{# дақиқа}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'баъд аз {delta, plural, =1{моҳ} one{# моҳ} few{# моҳ} many{# моҳ} other{# моҳ}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'баъд аз {delta, plural, =1{сония} one{# сония} few{# сония} many{# сония} other{# сония}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'баъд аз {delta, plural, =1{сол} one{# сол} few{# сол} many{# сол} other{# сол}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'баъд аз {delta, plural, =1{соат} one{# соат} few{# соат} many{# соат} other{# соат}}',\n    'just now' => 'ҳоло',\n    'the input value' => 'ҷадвали воридшуда',\n    '{attribute} \"{value}\" has already been taken.' => 'Ҷадвали «{value}» барои «{attribute}» аллакай банд аст.',\n    '{attribute} cannot be blank.' => 'Ҳошияи «{attribute}» набояд холӣ бошад.',\n    '{attribute} contains wrong subnet mask.' => 'Маънои \"{attribute}\" дорои нодурусти ниқоби зершабака мебошад.',\n    '{attribute} is invalid.' => 'Ҷадвали «{attribute}» ғалат аст.',\n    '{attribute} is not a valid URL.' => 'Ҷадвали «{attribute}» URL-и нодуруст мебошад.',\n    '{attribute} is not a valid email address.' => 'Ҷадвали {attribute} суроғаи дурусти паёмнигор нест.',\n    '{attribute} is not in the allowed range.' => 'Ҷадвали «{attribute}» ба рӯйхати суроғаҳои диапазонҳои иҷозат додашуда дохил намешавад.',\n    '{attribute} must be \"{requiredValue}\".' => 'Ҷадвали «{attribute}» бояд ба «{requiredValue}» баробар бошад.',\n    '{attribute} must be a number.' => 'Ҷадвали «{attribute}» бояд адад бошад.',\n    '{attribute} must be a string.' => 'Ҷадвали «{attribute}» бояд сатр бошад.',\n    '{attribute} must be a valid IP address.' => 'Ҷадвали «{attribute}» бояд суроғаи дурусти IP бошад.',\n    '{attribute} must be an IP address with specified subnet.' => 'Ҷадвали «{attribute}» бояд суроғаи IP бо зершабака бошад.',\n    '{attribute} must be an integer.' => 'Ҷадвали «{attribute}» бояд адади бутун бошад.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => 'Маънои «{attribute}» бояд ба «{true}» ё «{false}» баробар бошад.',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => 'Маънои «{attribute}» бояд ба «{compareValueOrAttribute}» баробар бошад.',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => 'Маънои «{attribute}» бояд аз маънии «{compareValueOrAttribute}» бузургтар бошад.',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => 'Маънои «{attribute}» бояд аз маънии «{compareValueOrAttribute}» бузургтар ё ба он баробар бошад.',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => 'Маънои «{attribute}» бояд аз маънии «{compareValueOrAttribute}» хурдтар бошад.',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => 'Маънои «{attribute}» бояд аз маънии «{compareValueOrAttribute}» хурдтар ё ба он баробар бошад.',\n    '{attribute} must be no greater than {max}.' => '«{attribute}» бояд аз {max} зиёд набошад.',\n    '{attribute} must be no less than {min}.' => '«{attribute}» бояд аз {min} кам набошад.',\n    '{attribute} must not be a subnet.' => 'Маънии «{attribute}» набояд зершабака бошад.',\n    '{attribute} must not be an IPv4 address.' => 'Ҷадвали «{attribute}» набояд суроғаи IPv4 бошад.',\n    '{attribute} must not be an IPv6 address.' => 'Ҷадвали «{attribute}» набояд суроғаи IPv6 бошад.',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => 'Ҷадвали «{attribute}» набояд ба «{compareValueOrAttribute}» баробар бошад.',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => 'Ҷадвали «{attribute}» бояд хади ақал {min, number} {min, plural, one{аломат} few{аломат} many{аломат} other{аломат}} дошта бошад.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => 'Ҷадвали «{attribute}» бояд ҳади аксар {min, number} {min, plural, one{аломат} few{аломат} many{аломат} other{аломат}} дошта бошад.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => 'Ҷадвали «{attribute}» бояд {length, number} {length, plural, one{аломат} few{аломат} many{аломат} other{аломат}} дошта бошад.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, one{# рӯз} few{# рӯз} many{# рӯз} other{# рӯз}}',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, one{# соат} few{# соат} many{# соат} other{# соат}}',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, one{# дақиқа} few{# дақиқа} many{# дақиқа} other{# дақиқа}}',\n    '{delta, plural, =1{1 month} other{# months}}' => '{delta, plural, one{# моҳ} few{# моҳ} many{# моҳ} other{# моҳ}}',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta, plural, one{# сония} few{# сония} many{# сония} other{# сония}}',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta, plural, one{# сол} few{# сол} many{# сол} other{# сол}}',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{рӯз} one{# рӯз} few{# рӯз} many{# рӯз} other{# рӯз}} пеш',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => ' {delta, plural, =1{дақиқа} one{# дақиқа} few{# дақиқа} many{# дақиқа} other{# дақиқа}} пеш',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{моҳ} one{# моҳ} few{# моҳ} many{# моҳ} other{# моҳ}} пеш',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{сония} one{# сония} few{# сония} many{# сония} other{# сония}} пеш',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{сол} one{# сол} few{# сол} many{# сол} other{# сол}} пеш',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, =1{соат} one{# соат} few{# соат} many{# соат} other{# соат}} пеш',\n    '{nFormatted} B' => '{nFormatted} Б',\n    '{nFormatted} GB' => '{nFormatted} ГБ',\n    '{nFormatted} GiB' => '{nFormatted} ГиБ',\n    '{nFormatted} KiB' => '{nFormatted} КиБ',\n    '{nFormatted} MB' => '{nFormatted} МБ',\n    '{nFormatted} MiB' => '{nFormatted} МиБ',\n    '{nFormatted} PB' => '{nFormatted} ПБ',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} КБ',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, one{байт} few{байт} many{байт} other{байт}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, one{гибибайт} few{гибибайт} many{гибибайт} other{гибибайт}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, one{гигабайт} few{гигабайт} many{гигабайт} other{гигабайт}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, one{кибибайт} few{кибибайт} many{кибибайт} other{кибибайт}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, one{килобайт} few{килобайт} many{килобайт} other{килобайт}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, one{мебибайт} few{мебибайт} many{мебибайт} other{мебибайт}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, one{мегабайт} few{мегабайт} many{мегабайт} other{мегабайт}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, one{пебибайт} few{пебибайт} many{пебибайт} other{пебибайт}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, one{петабайт} few{мегабайт} many{петабайт} other{петабайт}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, one{тебибайт} few{тебибайт} many{тебибайт} other{тебибайт}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, one{терабайт} few{терабайт} many{терабайт} other{терабайт}}',\n];\n"
  },
  {
    "path": "framework/messages/th/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => ' และ ',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '\"{attribute}\" ไม่สนับสนุนตัวดำเนินการ \"{operator}\"',\n    '(not set)' => '(ไม่ได้ตั้ง)',\n    'Action not found.' => 'ไม่พบแอคชั่น',\n    'Aliases available: {aliases}' => 'Alias ที่ใช้ได้: {aliases}',\n    'An internal server error occurred.' => 'เกิดข้อผิดพลาดภายในเซิร์ฟเวอร์',\n    'Are you sure you want to delete this item?' => 'คุณแน่ใจหรือไม่ที่จะลบรายการนี้?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => 'เงื่อนไขสำหรับ \"{attribute}\" ควรเป็นค่าหรือข้อกำหนดเฉพาะของตัวดำเนินการที่ถูกต้อง',\n    'Delete' => 'ลบ',\n    'Error' => 'ผิดพลาด',\n    'File upload failed.' => 'อัพโหลดไฟล์ไม่สำเร็จ',\n    'Home' => 'หน้าหลัก',\n    'Invalid data received for parameter \"{param}\".' => 'ข้อมูลสำหรับค่าพารามิเตอร์ \"{param}\" ที่ได้รับไม่ถูกต้อง',\n    'Login Required' => 'ต้องเข้าสู่ระบบก่อน',\n    'Missing required arguments: {params}' => 'ขาดอาร์กิวเมนต์ที่จำเป็น: {params}',\n    'Missing required parameters: {params}' => 'ขาดพารามิเตอร์ที่จำเป็น: {params}',\n    'No' => 'ไม่',\n    'No results found.' => 'ไม่พบผลลัพธ์',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'อนุญาตเฉพาะไฟล์ที่มีชนิด MIME ต่อไปนี้: {mimeTypes}',\n    'Only files with these extensions are allowed: {extensions}.' => 'อนุญาตเฉพาะไฟล์ที่มีนามสกุลต่อไปนี้: {extensions}',\n    'Operator \"{operator}\" must be used with a search attribute.' => 'ตัวดำเนินการ \"{operator}\" ต้องใช้กับแอตทริบิวต์สำหรับค้นหา',\n    'Operator \"{operator}\" requires multiple operands.' => 'ตัวดำเนินการ \"{operator}\" ต้องมีตัวถูกดำเนินการหลายตัว',\n    'Options available: {options}' => 'ตัวเลือกที่ใช้ได้: {options}',\n    'Page not found.' => 'ไม่พบหน้าที่ต้องการ',\n    'Please fix the following errors:' => 'โปรดแก้ไขข้อผิดพลาดต่อไปนี้:',\n    'Please upload a file.' => 'กรุณาอัพโหลดไฟล์',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'แสดง <b>{begin, number} ถึง {end, number}</b> จาก <b>{totalCount, number}</b> รายการ',\n    'The combination {values} of {attributes} has already been taken.' => 'กลุ่ม {values} ของ {attributes} ถูกใช้ไปแล้ว',\n    'The file \"{file}\" is not an image.' => 'ไฟล์ \"{file}\" ไม่ใช่ไฟล์รูปภาพ',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'ไฟล์ \"{file}\" มีขนาดใหญ่เกินไป ไฟล์ต้องมีขนาดไม่เกิน {formattedLimit}',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'ไฟล์ \"{file}\" มีขนาดเล็กเกินไป ไฟล์ต้องมีขนาดมากกว่า {formattedLimit}',\n    'The format of {attribute} is invalid.' => 'รูปแบบ {attribute} ไม่ถูกต้อง',\n    'The format of {filter} is invalid.' => 'รูปแบบ {filter} ไม่ถูกต้อง',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'ไฟล์รูปภาพ \"{file}\" มีขนาดใหญ่เกินไป ความสูงของรูปภาพต้องไม่เกิน {limit, number} พิกเซล',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'ไฟล์รูปภาพ \"{file}\" มีขนาดใหญ่เกินไป ความกว้างของรูปภาพต้องไม่เกิน {limit, number} พิกเซล',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'ไฟล์รูปภาพ \"{file}\" มีขนาดเล็กเกินไป ความสูงของรูปภาพต้องมากว่า {limit, number} พิกเซล',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'ไฟล์รูปภาพ \"{file}\" มีขนาดเล็กเกินไป ความกว้างของรูปภาพต้องมากกว่า {limit, number} พิกเซล',\n    'The requested view \"{name}\" was not found.' => 'ไม่พบวิว \"{name}\" ที่เรียกใช้',\n    'The verification code is incorrect.' => 'รหัสยืนยันไม่ถูกต้อง',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'ทั้งหมด <b>{count, number}</b> รายการ',\n    'Unable to verify your data submission.' => 'ไม่สามารถตรวจสอบยืนยันข้อมูลที่คุณส่งได้',\n    'Unknown alias: -{name}' => 'Alias ที่ไม่รู้จัก: -{name}',\n    'Unknown filter attribute \"{attribute}\"' => 'แอตทริบิวต์ฟิลเตอร์ที่ไม่รู้จัก: \"{attribute}\"',\n    'Unknown option: --{name}' => 'ตัวเลือกที่ไม่รู้จัก: --{name}',\n    'Update' => 'ปรับปรุง',\n    'View' => 'ดู',\n    'Yes' => 'ใช่',\n    'You are not allowed to perform this action.' => 'คุณไม่ได้รับอนุญาตให้ดำเนินการนี้',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'คุณสามารถอัพโหลดไฟล์ได้ไม่เกิน {limit, number} ไฟล์',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => 'คุณควรอัพโหลดไฟล์อย่างน้อย {limit, number} ไฟล์',\n    'in {delta, plural, =1{a day} other{# days}}' => 'ใน {delta} วัน',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'ใน {delta} นาที',\n    'in {delta, plural, =1{a month} other{# months}}' => 'ใน {delta} เดือน',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'ใน {delta} วินาที',\n    'in {delta, plural, =1{a year} other{# years}}' => 'ใน {delta} ปี',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'ใน {delta} ชั่วโมง',\n    'just now' => 'เมื่อสักครู่',\n    'the input value' => 'ค่าที่ป้อน',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" ถูกใช้ไปแล้ว',\n    '{attribute} cannot be blank.' => '{attribute}ต้องไม่ว่างเปล่า',\n    '{attribute} contains wrong subnet mask.' => '{attribute}ไม่ใช่ซับเน็ตที่ถูกต้อง',\n    '{attribute} is invalid.' => '{attribute}ไม่ถูกต้อง',\n    '{attribute} is not a valid URL.' => '{attribute}ไม่ใช่รูปแบบ URL ที่ถูกต้อง',\n    '{attribute} is not a valid email address.' => '{attribute}ไม่ใช่รูปแบบอีเมลที่ถูกต้อง',\n    '{attribute} is not in the allowed range.' => '{attribute}ไม่ได้อยู่ในช่วงที่ได้รับอนุญาต',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute}ต้องการ \"{requiredValue}\"',\n    '{attribute} must be a number.' => '{attribute}ต้องเป็นตัวเลขเท่านั้น',\n    '{attribute} must be a string.' => '{attribute}ต้องเป็นตัวอักขระเท่านั้น',\n    '{attribute} must be a valid IP address.' => '{attribute}ต้องเป็นที่อยู่ IP ที่ถูกต้อง',\n    '{attribute} must be an IP address with specified subnet.' => '{attribute}ต้องเป็นที่อยู่ IP ตามซับเน็ตที่ระบุ',\n    '{attribute} must be an integer.' => '{attribute}ต้องเป็นจำนวนเต็มเท่านั้น',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute}ต้องเป็น \"{true}\" หรือ \"{false}\"',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '{attribute}ต้องเหมือนกับ \"{compareValueOrAttribute}\"',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute}ต้องมากกว่า \"{compareValueOrAttribute}\"',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute}ต้องมากกว่าหรือเท่ากับ \"{compareValueOrAttribute}\"',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute}ต้องน้อยกว่า \"{compareValueOrAttribute}\"',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute}ต้องน้อยกว่าหรือเท่ากับ \"{compareValueOrAttribute}\"',\n    '{attribute} must be no greater than {max}.' => '{attribute}ต้องไม่มากกว่า {max}.',\n    '{attribute} must be no less than {min}.' => '{attribute}ต้องไม่น้อยกว่า {min}',\n    '{attribute} must not be a subnet.' => '{attribute}ต้องไม่เป็นซับเน็ต',\n    '{attribute} must not be an IPv4 address.' => '{attribute}ต้องไม่เป็นที่อยู่ IPv4',\n    '{attribute} must not be an IPv6 address.' => '{attribute}ต้องไม่เป็นที่อยู่ IPv6',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute}ต้องมีค่าไม่เหมือนกับ \"{compareValueOrAttribute}\"',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute}ควรประกอบด้วยอักขระอย่างน้อย {min, number} ตัว',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute}ควรประกอบด้วยอักขระไม่เกิน {max, number} ตัว',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute}ควรประกอบด้วยอักขระ {length, number} ตัว',\n    '{compareAttribute} is invalid.' => '{compareAttribute} ไม่ถูกต้อง',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta} วัน',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta} ชั่วโมง',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta} นาที',\n    '{delta, plural, =1{1 month} other{# months}}' => '{delta} เดือน',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta} วินาที',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta} ปี',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta} วันที่ผ่านมา',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta} นาทีที่ผ่านมา',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta} เดือนที่ผ่านมา',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta} วินาทีที่ผ่านมา',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta} ปีที่ผ่านมา',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta} ชั่วโมงที่ผ่านมา',\n    '{nFormatted} B' => '',\n    '{nFormatted} GB' => '',\n    '{nFormatted} GiB' => '',\n    '{nFormatted} KiB' => '',\n    '{nFormatted} MB' => '',\n    '{nFormatted} MiB' => '',\n    '{nFormatted} PB' => '',\n    '{nFormatted} PiB' => '',\n    '{nFormatted} TB' => '',\n    '{nFormatted} TiB' => '',\n    '{nFormatted} kB' => '',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} ไบต์',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} จิบิไบต์',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} จิกะไบต์',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} กิบิไบต์',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} กิโลไบต์',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} เมบิไบต์',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} เมกะไบต์',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} เพบิไบต์',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} เพตะไบต์',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} เทบิไบต์',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} เทระไบต์',\n];\n"
  },
  {
    "path": "framework/messages/tr/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => '',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(Veri Yok)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Bir sunucu hatası oluştu.',\n    'Are you sure you want to delete this item?' => 'Bu veriyi silmek istediğinizden emin misiniz?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'Sil',\n    'Error' => 'Hata',\n    'File upload failed.' => 'Dosya yükleme başarısız.',\n    'Home' => 'Anasayfa',\n    'Invalid data received for parameter \"{param}\".' => 'Bu \"{param}\" parametre için yanlış veri alındı.',\n    'Login Required' => 'Üye Girişi Gerekli',\n    'Missing required arguments: {params}' => 'Gerekli argüman eksik: {params}',\n    'Missing required parameters: {params}' => 'Gerekli parametre eksik: {params}',\n    'No' => 'Hayır',\n    'No results found.' => 'Sonuç bulunamadı',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Sadece bu tip MIME türleri geçerlidir: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Sadece bu tip uzantıları olan dosyalar geçerlidir: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Sayfa bulunamadı.',\n    'Please fix the following errors:' => 'Lütfen hataları düzeltin:',\n    'Please upload a file.' => 'Lütfen bir dosya yükleyin.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => '<b>{totalCount, number}</b> {totalCount, plural, one{öğenin} other{öğenin}} <b>{begin, number}-{end, number} arası gösteriliyor.</b>',\n    'The combination {values} of {attributes} has already been taken.' => '',\n    'The file \"{file}\" is not an image.' => '\"{file}\" bir resim dosyası değil.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => '\"{file}\" dosyası çok büyük. Boyutu {formattedLimit} değerinden büyük olamaz.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => '\"{file}\" dosyası çok küçük. Boyutu {formattedLimit} değerinden küçük olamaz.',\n    'The format of {attribute} is invalid.' => '{attribute} formatı geçersiz.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '\"{file}\" çok büyük. Yükseklik {limit, plural, one{pixel} other{pixels}} değerinden büyük olamaz.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '\"{file}\" çok büyük. Genişlik {limit, number} {limit, plural, one{pixel} other{pixels}} değerinden büyük olamaz.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '\"{file}\" çok küçük. Genişlik {limit, number} {limit, plural, one{pixel} other{pixels}} değerinden küçük olamaz.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '\"{file}\" çok küçük. Genişlik {limit, number} {limit, plural, one{pixel} other{pixels}} değerinden küçük olamaz.',\n    'The requested view \"{name}\" was not found.' => '',\n    'The verification code is incorrect.' => 'Doğrulama kodu yanlış.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Toplam <b>{count, number}</b> {count, plural, one{öğe} other{öğe}}.',\n    'Unable to verify your data submission.' => 'İlettiğiniz veri doğrulanamadı.',\n    'Unknown alias: -{name}' => 'Bilinmeyen rumuz: -{name}',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'Bilinmeyen opsiyon: --{name}',\n    'Update' => 'Güncelle',\n    'View' => 'Görüntüle',\n    'Yes' => 'Evet',\n    'You are not allowed to perform this action.' => 'Bu işlemi yapmaya yetkiniz yok.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Sadece {limit, number} {limit, plural, one{dosya} other{# dosya}} yükleyebilirsiniz.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => '{delta, plural, =1{bir gün} other{# gün}} içerisinde',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => '{delta, plural, =1{bir dakika} other{# dakika}} içerisinde',\n    'in {delta, plural, =1{a month} other{# months}}' => '{delta, plural, =1{bir ay} other{# ay}} içerisinde',\n    'in {delta, plural, =1{a second} other{# seconds}}' => '{delta, plural, =1{bir saniye} other{# saniye}} içerisinde',\n    'in {delta, plural, =1{a year} other{# years}}' => '{delta, plural, =1{bir yıl} other{# yıl}} içerisinde',\n    'in {delta, plural, =1{an hour} other{# hours}}' => '{delta, plural, =1{bir saat} other{# saat}} içerisinde',\n    'just now' => 'henüz',\n    'the input value' => 'veri giriş değeri',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" daha önce alınmış.',\n    '{attribute} cannot be blank.' => '{attribute} boş bırakılamaz.',\n    '{attribute} contains wrong subnet mask.' => '{attribute} yanlış alt ağ maskesi içeriyor.',\n    '{attribute} is invalid.' => '{attribute} geçersiz.',\n    '{attribute} is not a valid URL.' => '{attribute} geçerli bir URL değil.',\n    '{attribute} is not a valid email address.' => '{attribute} geçerli bir mail adresi değil.',\n    '{attribute} is not in the allowed range.' => '{attribute} izin verilen aralıkta değil.',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} {requiredValue} olmalı.',\n    '{attribute} must be a number.' => '{attribute} sayı olmalı.',\n    '{attribute} must be a string.' => '{attribute} harf olmalı.',\n    '{attribute} must be a valid IP address.' => '{attribute} geçerli bir IP adresi değil.',\n    '{attribute} must be an IP address with specified subnet.' => '{attribute} IP adresi belirtilen alt ağ ile birlikte olmalı.',\n    '{attribute} must be an integer.' => '{attribute} rakam olmalı.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} \"{true}\" ya da \"{false}\" olmalı.',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '{attribute} \"{compareValueOrAttribute}\" değerine eşit olmalı.',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} \"{compareValueOrAttribute}\" değerinden büyük olmalı.',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} \"{compareValueOrAttribute}\" değerinden büyük veya eşit olmalı.',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} \"{compareValueOrAttribute}\" değerinden küçük olmalı.',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} \"{compareValueOrAttribute}\" değerinden küçük veya eşit olmalı.',\n    '{attribute} must be no greater than {max}.' => '{attribute} {max} değerinden büyük olamaz.',\n    '{attribute} must be no less than {min}.' => '{attribute} {min} değerinden küçük olamaz.',\n    '{attribute} must not be a subnet.' => '{attribute} alt ağ olamaz.',\n    '{attribute} must not be an IPv4 address.' => '{attribute} IPv4 olamaz.',\n    '{attribute} must not be an IPv6 address.' => '{attribute} IPv6 olamaz.',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} \"{compareValueOrAttribute}\" değerine eşit olamaz.',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} en az {min, number} karakter içermeli.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} en fazla {max, number} karakter içermeli.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} {length, number} karakter içermeli.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, =1{1 gün} other{# gün}}',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, =1{1 saat} other{# saat}}',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, =1{1 dakika} other{# dakika}}',\n    '{delta, plural, =1{1 month} other{# months}}' => '{delta, plural, =1{1 ay} other{# ay}}',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta, plural, =1{1 saniye} other{# saniye}}',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta, plural, =1{1 yıl} other{# yıl}}',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{bir gün} other{# gün}} önce',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{bir dakika} other{# dakika}} önce',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{bir ay} other{# ay}} önce',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{bir saniye} other{# saniye}} önce',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{bir yıl} other{# yıl}} önce',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, =1{bir saat} other{# saat}} önce',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '{nFormatted} GiB',\n    '{nFormatted} KiB' => '{nFormatted} KiB',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '{nFormatted} MiB',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, =1{bayt} other{bayt}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, =1{gibibayt} other{gibibayt}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, =1{gigabayt} other{gigabayt}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, =1{kibibayt} other{kibibayt}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, =1{kilobayt} other{kilobayt}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, =1{mebibayt} other{mebibayt}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, =1{megabayt} other{megabayt}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, =1{pebibayt} other{pebibayt}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, =1{petabayt} other{petabayt}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, =1{tebibayt} other{tebibayt}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, =1{terabayt} other{terabayt}}',\n];\n"
  },
  {
    "path": "framework/messages/uk/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => '',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(не задано)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Виникла внутрішня помилка сервера.',\n    'Are you sure you want to delete this item?' => 'Ви впевнені, що хочете видалити цей елемент?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'Видалити',\n    'Error' => 'Помилка',\n    'File upload failed.' => 'Завантаження файлу не вдалося.',\n    'Home' => 'Головна',\n    'Invalid data received for parameter \"{param}\".' => 'Отримано невірне значення для параметра \"{param}\".',\n    'Login Required' => 'Необхідно увійти',\n    'Missing required arguments: {params}' => 'Відсутні обовʼязкові аргументи: {params}',\n    'Missing required parameters: {params}' => 'Відсутні обовʼязкові параметри: {params}',\n    'No' => 'Ні',\n    'No results found.' => 'Нічого не знайдено.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Дозволені файли лише з наступними MIME-типами: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Дозволені файли лише з наступними розширеннями: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Сторінка не знайдена.',\n    'Please fix the following errors:' => 'Будь ласка, виправте наступні помилки:',\n    'Please upload a file.' => 'Будь ласка, завантажте файл.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Показані <b>{begin, number}-{end, number}</b> із <b>{totalCount, number}</b> {totalCount, plural, one{запису} other{записів}}.',\n    'The combination {values} of {attributes} has already been taken.' => '',\n    'The file \"{file}\" is not an image.' => 'Файл \"{file}\" не є зображенням.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'Файл \"{file}\" занадто великий. Розмір не повинен перевищувати {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'Файл \"{file}\" занадто малий. Розмір повинен бути більше, ніж {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'Невірний формат значення \"{attribute}\".',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Зображення \"{file}\" занадто велике. Висота не повинна перевищувати {limit, number} {limit, plural, one{піксель} few{пікселя} many{пікселів} other{пікселя}}.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Зображення \"{file}\" занадто велике. Ширина не повинна перевищувати {limit, number} {limit, plural, one{піксель} few{пікселя} many{пікселів} other{пікселя}}.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Зображення \"{file}\" занадто мале. Висота повинна бути більше, ніж {limit, number} {limit, plural, one{піксель} few{пікселя} many{пікселів} other{пікселя}}.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Зображення \"{file}\" занадто мале. Ширина повинна бути більше, ніж {limit, number} {limit, plural, one{піксель} few{пікселя} many{пікселів} other{пікселя}}.',\n    'The requested view \"{name}\" was not found.' => 'Представлення \"{name}\" не знайдено.',\n    'The verification code is incorrect.' => 'Невірний код перевірки.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Всього <b>{count, number}</b> {count, plural, one{запис} few{записи} many{записів} other{записи}}.',\n    'Unable to verify your data submission.' => 'Не вдалося перевірити передані дані.',\n    'Unknown alias: -{name}' => '',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'Невідома опція : --{name}',\n    'Update' => 'Оновити',\n    'View' => 'Переглянути',\n    'Yes' => 'Так',\n    'You are not allowed to perform this action.' => 'Вам не дозволено виконувати дану дію.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Ви не можете завантажувати більше {limit, number} {limit, plural, one{файла} few{файлів} many{файлів} other{файла}}.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => 'через {delta, plural, =1{день} one{# день} few{# дні} many{# днів} other{# дні}}',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'через {delta, plural, =1{хвилину} one{# хвилину} few{# хвилини} many{# хвилин} other{# хвилини}}',\n    'in {delta, plural, =1{a month} other{# months}}' => 'через {delta, plural, =1{місяць} one{# місяць} few{# місяці} many{# місяців} other{# місяці}}',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'через {delta, plural, =1{секунду} one{# секунду} few{# секунди} many{# секунд} other{# секунди}}',\n    'in {delta, plural, =1{a year} other{# years}}' => 'через {delta, plural, =1{рік} one{# рік} few{# роки} many{# років} other{# роки}}',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'через {delta, plural, =1{годину} one{# годину} few{# години} many{# годин} other{# години}}',\n    'just now' => 'саме зараз',\n    'the input value' => 'введене значення',\n    '{attribute} \"{value}\" has already been taken.' => 'Значення «{value}» для «{attribute}» вже зайнято.',\n    '{attribute} cannot be blank.' => 'Необхідно заповнити \"{attribute}\".',\n    '{attribute} contains wrong subnet mask.' => 'Значення «{attribute}» містить неправильну маску підмережі.',\n    '{attribute} is invalid.' => 'Значення \"{attribute}\" не вірне.',\n    '{attribute} is not a valid URL.' => 'Значення \"{attribute}\" не є правильним URL.',\n    '{attribute} is not a valid email address.' => 'Значення \"{attribute}\" не є правильною email адресою.',\n    '{attribute} is not in the allowed range.' => 'Значення «{attribute}» не входить в список дозволених діапазонів адрес.',\n    '{attribute} must be \"{requiredValue}\".' => 'Значення \"{attribute}\" має бути рівним \"{requiredValue}\".',\n    '{attribute} must be a number.' => 'Значення \"{attribute}\" має бути числом.',\n    '{attribute} must be a string.' => 'Значення \"{attribute}\" має бути текстовим рядком.',\n    '{attribute} must be a valid IP address.' => 'Значення «{attribute}» повинно бути правильною IP адресою.',\n    '{attribute} must be an IP address with specified subnet.' => 'Значення «{attribute}» повинно бути IP адресою з підмережею.',\n    '{attribute} must be an integer.' => 'Значення \"{attribute}\" має бути цілим числом.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => 'Значення \"{attribute}\" має дорівнювати \"{true}\" або \"{false}\".',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => 'Значення \"{attribute}\" повинно бути рівним \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => 'Значення \"{attribute}\" повинно бути більшим значення \"{compareValueOrAttribute}\".',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => 'Значення \"{attribute}\" повинно бути більшим або дорівнювати значенню \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => 'Значення \"{attribute}\" повинно бути меншим значення \"{compareValueOrAttribute}\".',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => 'Значення \"{attribute}\" повинно бути меншим або дорівнювати значенню \"{compareValueOrAttribute}\".',\n    '{attribute} must be no greater than {max}.' => 'Значення \"{attribute}\" не повинно перевищувати {max}.',\n    '{attribute} must be no less than {min}.' => 'Значення \"{attribute}\" має бути більшим {min}.',\n    '{attribute} must not be a subnet.' => 'Значення «{attribute}» не повинно бути підмережею.',\n    '{attribute} must not be an IPv4 address.' => 'Значення «{attribute}» не повинно бути IPv4 адресою.',\n    '{attribute} must not be an IPv6 address.' => 'Значення «{attribute}» не повинно бути IPv6 адресою.',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => 'Значення \"{attribute}\" не повинно бути рівним \"{compareValueOrAttribute}\".',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => 'Значення \"{attribute}\" повинно містити мінімум {min, number} {min, plural, one{символ} few{символа} many{символів} other{символа}}.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => 'Значення \"{attribute}\" повинно містити максимум {max, number} {max, plural, one{символ} few{символа} many{символів} other{символа}}.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => 'Значення \"{attribute}\" повинно містити {length, number} {length, plural, one{символ} few{символа} many{символів} other{символа}}.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta, plural, one{# день} few{# дні} many{# днів} other{# днів}}',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta, plural, one{# година} few{# години} many{# годин} other{# годин}}',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta, plural, one{# хвилина} few{# хвилини} many{# хвилин} other{# хвилин}}',\n    '{delta, plural, =1{1 month} other{# months}}' => '{delta, plural, one{# місяць} few{# місяця} many{# місяців} other{# місяців}}',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta, plural, one{# секунда} few{# секунди} many{# секунд} other{# секунд}}',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta, plural, one{# рік} few{# роки} many{# років} other{# років}}',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{день} one{# день} few{# дні} many{# днів} other{# дні}} тому',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{хвилину} one{# хвилину} few{# хвилини} many{# хвилин} other{# хвилини}} тому',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{місяць} one{# місяць} few{# місяці} many{# місяців} other{# місяці}} тому',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{секунду} one{# секунду} few{# секунди} many{# секунд} other{# секунди}} тому',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{рік} one{# рік} few{# роки} many{# років} other{# роки}} тому',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, =1{година} one{# година} few{# години} many{# годин} other{# години}} тому',\n    '{nFormatted} B' => '{nFormatted} Б',\n    '{nFormatted} GB' => '{nFormatted} Гб',\n    '{nFormatted} GiB' => '{nFormatted} ГіБ',\n    '{nFormatted} KiB' => '{nFormatted} КіБ',\n    '{nFormatted} MB' => '{nFormatted} Мб',\n    '{nFormatted} MiB' => '{nFormatted} МіБ',\n    '{nFormatted} PB' => '{nFormatted} Пб',\n    '{nFormatted} PiB' => '{nFormatted} ПіБ',\n    '{nFormatted} TB' => '{nFormatted} Тб',\n    '{nFormatted} TiB' => '{nFormatted} ТіБ',\n    '{nFormatted} kB' => '{nFormatted} Кб',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, one{байт} few{байта} many{байтів} other{байта}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, one{гібібайт} few{гібібайта} many{гібібайтів} other{гібібайта}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, one{гігабайт} few{гігабайта} many{гігабайтів} other{гігабайта}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, one{кібібайт} few{кібібайта} many{кібібайтів} other{кібібайта}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, one{кілобайт} few{кілобайта} many{кілобайтів} other{кілобайта}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, one{мебібайт} few{мебібайта} many{мебібайтів} other{мебібайта}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, one{мегабайт} few{мегабайта} many{мегабайтів} other{мегабайта}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, one{пебібайт} few{пебібайта} many{пебібайтів} other{пебібайта}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, one{петабайт} few{петабайта} many{петабайтів} other{петабайта}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, one{тебібайт} few{тебібайта} many{тебібайтів} other{тебібайта}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, one{терабайт} few{терабайта} many{терабайтів} other{терабайта}}',\n];\n"
  },
  {
    "path": "framework/messages/uz/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => '',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(qiymatlanmagan)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Serverning ichki xatoligi yuz berdi.',\n    'Are you sure you want to delete this item?' => 'Siz rostdan ham ushbu elementni o`chirmoqchimisiz?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'O`chirish',\n    'Error' => 'Xato',\n    'File upload failed.' => 'Faylni yuklab bo`lmadi.',\n    'Home' => 'Asosiy',\n    'Invalid data received for parameter \"{param}\".' => '\"{param}\" parametr qiymati noto`g`ri.',\n    'Login Required' => 'Kirish talab qilinadi.',\n    'Missing required arguments: {params}' => 'Quyidagi zarur argumentlar mavjud emas: {params}',\n    'Missing required parameters: {params}' => 'Quyidagi zarur parametrlar mavjud emas: {params}',\n    'No' => 'Yo`q',\n    'No results found.' => 'Hech nima topilmadi.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Faqat quyidagi MIME-turidagi fayllarni yuklashga ruhsat berilgan: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Faqat quyidagi kengaytmali fayllarni yuklashga ruhsat berilgan: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Sahifa topilmadi.',\n    'Please fix the following errors:' => 'Navbatdagi xatoliklarni to`g`rilang:',\n    'Please upload a file.' => 'Faylni yuklang.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Namoyish etilayabdi <b>{begin, number}-{end, number}</b> ta yozuv <b>{totalCount, number}</b> tadan.',\n    'The combination {values} of {attributes} has already been taken.' => '',\n    'The file \"{file}\" is not an image.' => '«{file}» fayl rasm emas.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => '«{file}» fayl juda katta. O`lcham {formattedLimit} oshishi kerak emas.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => '«{file}» fayl juda kichkina. O`lcham {formattedLimit} kam bo`lmasligi kerak.',\n    'The format of {attribute} is invalid.' => '«{attribute}» qiymatning formati noto`g`ri.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '«{file}» fayl juda katta. Balandlik {limit, number} {limit, plural, one{pikseldan} few{pikseldan} many{pikseldan} other{pikseldan}} oshmasligi kerak.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '«{file}» fayl juda katta. Eni {limit, number} {limit, plural, one{pikseldan} few{pikseldan} many{pikseldan} other{pikseldan}} oshmasligi kerak.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '«{file}» fayl juda kichkina. Balandlik {limit, number} {limit, plural, one{pikseldan} few{pikseldan} many{pikseldan} other{pikseldan}} kam bo`lmasligi kerak.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '«{file}» juda kichkina. Eni {limit, number} {limit, plural, one{pikseldan} few{pikseldan} many{pikseldan} other{pikseldan}} kam bo`lmasligi kerak.',\n    'The requested view \"{name}\" was not found.' => 'So`ralayotgan \"{name}\" namoyish fayli topilmadi.',\n    'The verification code is incorrect.' => 'Tekshiruv kodi noto`g`ri.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Jami <b>{count, number}</b> {count, plural, one{yozuv} few{yozuv} many{yozuv} other{yozuv}}.',\n    'Unable to verify your data submission.' => 'Yuborilgan ma`lumotlarni tekshirib bo`lmadi.',\n    'Unknown alias: -{name}' => '',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'Noaniq opsiya: --{name}',\n    'Update' => 'Tahrirlash',\n    'View' => 'Ko`rish',\n    'Yes' => 'Ha',\n    'You are not allowed to perform this action.' => 'Sizga ushbu amalni bajarishga ruhsat berilmagan.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Siz {limit, number} {limit, plural, one{fayldan} few{fayllardan} many{fayllardan} other{fayllardan}} ko`pini yuklab ola olmaysiz.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => '{delta, plural, =1{kundan} one{# kundan} few{# kundan} many{# kunlardan} other{# kundan}} keyin',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => '{delta, plural, =1{minutdan} one{# minutdan} few{# minutlardan} many{# minutdan} other{# minutlardan}} keyin',\n    'in {delta, plural, =1{a month} other{# months}}' => '{delta, plural, =1{oydan} one{# oydan} few{# oydan} many{# oylardan} other{# oylardan}} keyin',\n    'in {delta, plural, =1{a second} other{# seconds}}' => '{delta, plural, =1{sekunddan} one{# sekunddan} few{# sekundlardan} many{# sekunddan} other{# sekundlardan}} keyin',\n    'in {delta, plural, =1{a year} other{# years}}' => '{delta, plural, =1{yildan} one{# yildan} few{# yillardan} many{# yillardan} other{# yillardan}} keyin',\n    'in {delta, plural, =1{an hour} other{# hours}}' => '{delta, plural, =1{soatdan} one{# soatdan} few{# soatlardan} many{# soatlardan} other{# soatlardan}} keyin',\n    'just now' => 'xoziroq',\n    'the input value' => 'kiritilgan qiymat',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} «{value}» avvalroq band qilingan.',\n    '{attribute} cannot be blank.' => '«{attribute}» to`ldirish shart.',\n    '{attribute} contains wrong subnet mask.' => '',\n    '{attribute} is invalid.' => '«{attribute}» qiymati noto`g`ri.',\n    '{attribute} is not a valid URL.' => '«{attribute}» qiymati to`g`ri URL emas.',\n    '{attribute} is not a valid email address.' => '«{attribute}» qiymati to`g`ri email manzil emas.',\n    '{attribute} is not in the allowed range.' => '',\n    '{attribute} must be \"{requiredValue}\".' => '«{attribute}» qiymati «{requiredValue}» ga teng bo`lishi kerak.',\n    '{attribute} must be a number.' => '«{attribute}» qiymati son bo`lishi kerak.',\n    '{attribute} must be a string.' => '«{attribute}» qiymati satr bo`lishi kerak.',\n    '{attribute} must be a valid IP address.' => '',\n    '{attribute} must be an IP address with specified subnet.' => '',\n    '{attribute} must be an integer.' => '«{attribute}» qiymati butun son bo`lishi kerak.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '«{attribute}» qiymati «{true}» yoki «{false}» bo`lishi kerak.',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '«{attribute}» qiymati «{compareValueOrAttribute}» dan katta bo`lishi kerak.',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '«{attribute}» qiymati «{compareValueOrAttribute}» dan katta yoki teng bo`lishi kerak.',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '«{attribute}» qiymati «{compareValueOrAttribute}» dan kichkina bo`lishi kerak.',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '«{attribute}» qiymati «{compareValueOrAttribute}» dan kichik yoki teng bo`lishi kerak.',\n    '{attribute} must be no greater than {max}.' => '«{attribute}» qiymati {max} dan oshmasligi kerak.',\n    '{attribute} must be no less than {min}.' => '«{attribute}» qiymati {min} dan kichkina bo`lmasligi kerak.',\n    '{attribute} must not be a subnet.' => '',\n    '{attribute} must not be an IPv4 address.' => '',\n    '{attribute} must not be an IPv6 address.' => '',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '«{attribute}» qiymati «{compareValueOrAttribute}» qiymatiga teng bo`lmasligi lozim.',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '«{attribute}» qiymati minimum {min, number} {min, plural, one{belgidan} few{belgidan} many{belgidan} other{belgidan}} tashkil topishi kerak.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '«{attribute}» qiymati maksimum {max, number} {max, plural, one{belgidan} few{belgidan} many{belgidan} other{belgidan}} oshmasligi kerak.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '«{attribute}» qiymati {length, number} {length, plural, one{belgidan} few{belgidan} many{belgidan} other{belgidan}} tashkil topishi kerak.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '',\n    '{delta, plural, =1{1 month} other{# months}}' => '',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '',\n    '{delta, plural, =1{1 year} other{# years}}' => '',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{kun} one{kun} few{# kun} many{# kun} other{# kun}} avval',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{daqiqa} one{# daqiqa} few{# daqiqa} many{# daqiqa} other{# daqiqa}} avval',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{oy} one{# oy} few{# oy} many{# oy} other{# oy}} avval',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{soniya} one{# soniya} few{# soniya} many{# soniya} other{# soniya}} avval',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{yil} one{# yil} few{# yil} many{# yil} other{# yil}} avval',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, =1{soat} one{# soat} few{# soat} many{# soat} other{# soat}} avval',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '{nFormatted} GiB',\n    '{nFormatted} KiB' => '{nFormatted} KiB',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '{nFormatted} MiB',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, one{bayt} few{bayt} many{baytlar} other{bayt}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, one{gibibayt} few{gibibayt} many{gibibayt} other{gibibayt}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, one{gigabayt} few{gigabayt} many{gigabayt} other{gigabayt}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, one{kibibayt} few{kibibayt} many{kibibayt} other{kibibayt}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, one{kilobayt} few{kilobayt} many{kilobayt} other{kilobayt}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, one{mebibayt} few{mebibayt} many{mebibayt} other{mebibayt}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, one{megabayt} few{megabayt} many{megabayt} other{megabayt}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, one{pebibayt} few{pebibayt} many{pebibayt} other{pebibayt}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, one{petabayt} few{petabayt} many{petabayt} other{petabayt}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, one{tebibayt} few{tebibayt} many{tebibayt} other{tebibayt}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, one{terabayt} few{terabayt} many{terabayt} other{terabayt}}',\n];\n"
  },
  {
    "path": "framework/messages/uz-Cy/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => '',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(қийматланмаган)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Сервернинг ички хатолиги юз берди.',\n    'Are you sure you want to delete this item?' => 'Сиз ростдан ҳам ушбу элементни ўчирмоқчимисиз?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'Ўчириш',\n    'Error' => 'Хато',\n    'File upload failed.' => 'Файлни юклаб бўлмади.',\n    'Home' => 'Асосий',\n    'Invalid data received for parameter \"{param}\".' => '\"{param}\" параметр қиймати нотўғри.',\n    'Login Required' => 'Кириш талаб қилинади.',\n    'Missing required arguments: {params}' => 'Қуйидаги зарур аргументлар мавжуд эмас: {params}',\n    'Missing required parameters: {params}' => 'Қуйидаги зарур параметрлар мавжуд эмас: {params}',\n    'No' => 'Йўқ',\n    'No results found.' => 'Хеч нима топилмади.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Фақат MIME-туридаги файлларни юклашга рухсат берилган: {mimeTypes}.',\n    'Only files with these extensions are allowed: {extensions}.' => 'Фақат қуйидаги кенгайтмали файлларни юклашга рухсат берилган: {extensions}.',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Саҳифа топилмади.',\n    'Please fix the following errors:' => 'Қуйидаги хатоликларни тўғриланг:',\n    'Please upload a file.' => 'Файлни юкланг.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Намойиш этиляпти <b>{begin, number}-{end, number}</b> маълумот, жами: <b>{totalCount, number}</b> та',\n    'The combination {values} of {attributes} has already been taken.' => '',\n    'The file \"{file}\" is not an image.' => '«{file}» расм файл эмас.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => '«{file}» файл жуда катта. Ўлчам {formattedLimit} ошиши керак эмас.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => '«{file}» файл жуда кичкина. Ўлчам {formattedLimit} кам бўлмаслиги керак.',\n    'The format of {attribute} is invalid.' => '«{attribute}» қийматнинг формати нотўғри.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '«{file}» файл жуда катта. Баландлик {limit, number} {limit, plural, one{пикселдан} few{пикселдан} many{пикселдан} other{пикселдан}} ошмаслиги керак.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '«{file}» файл жуда катта. Эни {limit, number} {limit, plural, one{пикселдан} few{пикселдан} many{пикселдан} other{пикселдан}} ошмаслиги керак.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '«{file}» файл жуда кичкина. Баландлик {limit, number} {limit, plural, one{пикселдан} few{пикселдан} many{пикселдан} other{пикселдан}} кам бўлмаслиги керак.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '«{file}» файл жуда кичкина. Эни {limit, number} {limit, plural, one{пикселдан} few{пикселдан} many{пикселдан} other{пикселдан}} кам бўлмаслиги керак.',\n    'The requested view \"{name}\" was not found.' => 'Сўралаётган \"{name}\" файли топилмади.',\n    'The verification code is incorrect.' => 'Текширув коди нотўғри.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Жами <b>{count, number}</b> {count, plural, one{ёзув} few{ёзув} many{ёзув} other{ёзув}}.',\n    'Unable to verify your data submission.' => 'Юборилган маълумотларни текшириб бўлмади.',\n    'Unknown alias: -{name}' => '',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'Ноаниқ танлов: --{name}',\n    'Update' => 'Таҳрирлаш',\n    'View' => 'Кўриш',\n    'Yes' => 'Ҳа',\n    'You are not allowed to perform this action.' => 'Сизга ушбу амални бажаришга руҳсат берилмаган.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Сиз {limit, number} {limit, plural, one{файлдан} few{файллардан} many{файллардан} other{файллардан}} кўпини юклаб ололмайсиз.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => '{delta, plural, =1{кундан} one{# кундан} few{# кундан} many{# кунлардан} other{# кундан}} кейин',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => '{delta, plural, =1{минутдан} one{# минутдан} few{# минутлардан} many{# минутдан} other{# минутлардан}} кейин',\n    'in {delta, plural, =1{a month} other{# months}}' => '{delta, plural, =1{ойдан} one{# ойдан} few{# ойдан} many{# ойлардан} other{# ойлардан}} кейин',\n    'in {delta, plural, =1{a second} other{# seconds}}' => '{delta, plural, =1{секунддан} one{# секунддан} few{# секундлардан} many{# секунддан} other{# секундлардан}} кейин',\n    'in {delta, plural, =1{a year} other{# years}}' => '{delta, plural, =1{йилдан} one{# йилдан} few{# йиллардан} many{# йиллардан} other{# йиллардан}} кейин',\n    'in {delta, plural, =1{an hour} other{# hours}}' => '{delta, plural, =1{соатдан} one{# соатдан} few{# соатлардан} many{# соатлардан} other{# соатлардан}} кейин',\n    'just now' => 'ҳозироқ',\n    'the input value' => 'киритилган қиймат',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} «{value}» аввалроқ банд қилинган.',\n    '{attribute} cannot be blank.' => '«{attribute}» тўлдириш шарт.',\n    '{attribute} contains wrong subnet mask.' => '',\n    '{attribute} is invalid.' => '«{attribute}» қиймати нотўғри',\n    '{attribute} is not a valid URL.' => '«{attribute}» тўғри URL эмас.',\n    '{attribute} is not a valid email address.' => '«{attribute}» тўғри электрон манзил эмас.',\n    '{attribute} is not in the allowed range.' => '',\n    '{attribute} must be \"{requiredValue}\".' => '«{attribute}» қиймати «{requiredValue}» га тенг бўлиши керак.',\n    '{attribute} must be a number.' => '«{attribute}» қиймати сон бўлиши керак.',\n    '{attribute} must be a string.' => '«{attribute}» қиймати матн бўлиши керак.',\n    '{attribute} must be a valid IP address.' => '',\n    '{attribute} must be an IP address with specified subnet.' => '',\n    '{attribute} must be an integer.' => '«{attribute}» қиймати бутун сон бўлиши керак.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '«{attribute}» қиймати «{true}» ёки «{false}» бўлиши керак.',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '«{attribute}» қиймати «{compareValueOrAttribute}» дан катта бўлиши керак.',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '«{attribute}» қиймати «{compareValueOrAttribute}» дан катта ёки тенг бўлиши керак.',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '«{attribute}» қиймати «{compareValueOrAttribute}» дан кичкина бўлиши керак.',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '«{attribute}» қиймати «{compareValueOrAttribute}» дан кичик ёки тенг бўлиши керак.',\n    '{attribute} must be no greater than {max}.' => '«{attribute}» қиймати {max} дан ошмаслиги керак.',\n    '{attribute} must be no less than {min}.' => '«{attribute}» қиймати {min} дан катта бўлиши керак.',\n    '{attribute} must not be a subnet.' => '',\n    '{attribute} must not be an IPv4 address.' => '',\n    '{attribute} must not be an IPv6 address.' => '',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '«{attribute}» қиймати «{compareValueOrAttribute}» қийматига тенг бўлмаслиги лозим.',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '«{attribute}» қиймати минимум {min, number} {min, plural, one{белгидан} few{белгидан} many{белгидан} other{белгидан}} ташкил топиши керак.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '«{attribute}» қиймати максимум {max, number} {max, plural, one{белгидан} few{белгидан} many{белгидан} other{белгидан}} ошмаслиги керак.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '«{attribute}» қиймати {length, number} {length, plural, one{белгидан} few{белгидан} many{белгидан} other{белгидан}} ташкил топиши керак.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '',\n    '{delta, plural, =1{1 month} other{# months}}' => '',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '',\n    '{delta, plural, =1{1 year} other{# years}}' => '',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{кун} one{кун} few{# кун} many{# кун} other{# кун}} аввал',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{дақиқа} one{# дақиқа} few{# дақиқа} many{# дақиқа} other{# дақиқа}} аввал',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{ой} one{# ой} few{# ой} many{# ой} other{# ой}} аввал',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{сония} one{# сония} few{# сония} many{# сония} other{# сония}} аввал',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{йил} one{# йил} few{# йил} many{# йил} other{# йил}} аввал',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, =1{соат} one{# соат} few{# соат} many{# соат} other{# соат}} аввал',\n    '{nFormatted} B' => '{nFormatted} Б',\n    '{nFormatted} GB' => '{nFormatted} ГБ',\n    '{nFormatted} GiB' => '{nFormatted} ГиБ',\n    '{nFormatted} KiB' => '{nFormatted} КиБ',\n    '{nFormatted} MB' => '{nFormatted} МБ',\n    '{nFormatted} MiB' => '{nFormatted} МиБ',\n    '{nFormatted} PB' => '{nFormatted} ПБ',\n    '{nFormatted} PiB' => '{nFormatted} ПиБ',\n    '{nFormatted} TB' => '{nFormatted} ТБ',\n    '{nFormatted} TiB' => '{nFormatted} ТиБ',\n    '{nFormatted} kB' => '',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, one{байт} few{байт} many{байтлар} other{байт}}',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, one{гибибайт} few{гибибайт} many{гибибайт} other{гибибайт}}',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, one{гигабайт} few{гигабайт} many{гигабайт} other{гигабайт}}',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, one{кибибайт} few{кибибайт} many{кибибайт} other{кибибайт}}',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, one{килобайт} few{килобайт} many{килобайт} other{килобайт}}',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, one{мебибайт} few{мебибайт} many{мебибайт} other{мебибайт}}',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, one{мегабайт} few{мегабайт} many{мегабайт} other{мегабайт}}',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, one{пебибайт} few{пебибайт} many{пебибайт} other{пебибайт}}',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, one{петабайт} few{петабайт} many{петабайт} other{петабайт}}',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, one{тебибайт} few{тебибайт} many{тебибайт} other{тебибайт}}',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, one{терабайт} few{терабайт} many{терабайт} other{терабайт}}',\n];\n"
  },
  {
    "path": "framework/messages/vi/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => ' và ',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(không có)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => 'Máy chủ đã gặp sự cố nội bộ.',\n    'Are you sure you want to delete this item?' => 'Bạn có chắc là sẽ xóa mục này không?',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => 'Xóa',\n    'Error' => 'Lỗi',\n    'File upload failed.' => 'Không tải được file lên.',\n    'Home' => 'Trang chủ',\n    'Invalid data received for parameter \"{param}\".' => 'Dữ liệu của tham số \"{param}\" không hợp lệ.',\n    'Login Required' => 'Yêu cầu đăng nhập',\n    'Missing required arguments: {params}' => 'Thiếu đối số: {params}',\n    'Missing required parameters: {params}' => 'Thiếu tham số: {params}',\n    'No' => 'Không',\n    'No results found.' => 'Không tìm thấy kết quả nào.',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => 'Chỉ các file với kiểu MIME sau đây được phép tải lên: {mimeTypes}',\n    'Only files with these extensions are allowed: {extensions}.' => 'Chỉ các file với phần mở rộng sau đây được phép tải lên: {extensions}',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => 'Không tìm thấy trang.',\n    'Please fix the following errors:' => 'Vui lòng sửa các lỗi sau đây:',\n    'Please upload a file.' => 'Hãy tải file lên.',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Trình bày <b>{begin, number}-{end, number}</b> trong số <b>{totalCount, number}</b> mục.',\n    'The combination {values} of {attributes} has already been taken.' => '',\n    'The file \"{file}\" is not an image.' => 'File \"{file}\" phải là một ảnh.',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => 'File \"{file}\" quá lớn. Kích cỡ tối đa được phép tải lên là {formattedLimit}.',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => 'File \"{file}\" quá nhỏ. Kích cỡ tối thiểu được phép tải lên là {formattedLimit}.',\n    'The format of {attribute} is invalid.' => 'Định dạng của {attribute} không hợp lệ.',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'File \"{file}\" có kích thước quá lớn. Chiều cao tối đa được phép là {limit, number} pixel.',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Ảnh \"{file}\" có kích thước quá lớn. Chiều rộng tối đa được phép là {limit, number} pixel.',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Ảnh \"{file}\" quá nhỏ. Chiều cao của ảnh không được phép nhỏ hơn {limit, number} pixel.',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Ảnh \"{file}\" quá nhỏ. Chiều rộng của ảnh không được phép nhỏ hơn {limit, number} pixel.',\n    'The requested view \"{name}\" was not found.' => 'Không tìm thấy file view \"{name}\"',\n    'The verification code is incorrect.' => 'Mã xác thực không đúng.',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Tổng số <b>{count, number}</b> mục.',\n    'Unable to verify your data submission.' => 'Không kiểm tra được dữ liệu đã gửi lên.',\n    'Unknown alias: -{name}' => '',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => 'Tùy chọn không biết: --{name}',\n    'Update' => 'Sửa',\n    'View' => 'Xem',\n    'Yes' => 'Có',\n    'You are not allowed to perform this action.' => 'Bạn không được phép truy cập chức năng này.',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Chỉ có thể tải lên tối đa {limit, number} file.',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => 'trong {delta} ngày',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => 'trong {delta} phút',\n    'in {delta, plural, =1{a month} other{# months}}' => 'trong {delta} tháng',\n    'in {delta, plural, =1{a second} other{# seconds}}' => 'trong {delta} giây',\n    'in {delta, plural, =1{a year} other{# years}}' => 'trong {delta} năm',\n    'in {delta, plural, =1{an hour} other{# hours}}' => 'trong {delta} giờ',\n    'just now' => 'ngay bây giờ',\n    'the input value' => 'giá trị đã nhập',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} \"{value}\" đã bị sử dụng.',\n    '{attribute} cannot be blank.' => '{attribute} không được để trống.',\n    '{attribute} contains wrong subnet mask.' => '',\n    '{attribute} is invalid.' => '{attribute} không hợp lệ.',\n    '{attribute} is not a valid URL.' => '{attribute} không phải là URL hợp lệ.',\n    '{attribute} is not a valid email address.' => '{attribute} không phải là địa chỉ email hợp lệ.',\n    '{attribute} is not in the allowed range.' => '',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute} phải là \"{requiredValue}\"',\n    '{attribute} must be a number.' => '{attribute} phải là số.',\n    '{attribute} must be a string.' => '{attribute} phải là chuỗi.',\n    '{attribute} must be a valid IP address.' => '',\n    '{attribute} must be an IP address with specified subnet.' => '',\n    '{attribute} must be an integer.' => '{attribute} phải là số nguyên.',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} phải là \"{true}\" hoặc \"{false}\".',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '{attribute} phải bằng \"{compareValueOrAttribute}\"',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} phải lớn hơn \"{compareValueOrAttribute}\"',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} phải lớn hơn hoặc bằng \"{compareValueOrAttribute}\"',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} phải nhỏ hơn \"{compareValueOrAttribute}\"',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} phải nhỏ hơn hoặc bằng \"{compareValueOrAttribute}\"',\n    '{attribute} must be no greater than {max}.' => '{attribute} không được lớn hơn {max}.',\n    '{attribute} must be no less than {min}.' => '{attribute} không được nhỏ hơn {min}',\n    '{attribute} must not be a subnet.' => '',\n    '{attribute} must not be an IPv4 address.' => '',\n    '{attribute} must not be an IPv6 address.' => '',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} không được phép bằng \"{compareValueOrAttribute}\"',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} phải chứa ít nhất {min, number} ký tự.',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} phải chứa nhiều nhất {max, number} ký tự.',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} phải bao gồm {length, number} ký tự.',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '',\n    '{delta, plural, =1{1 month} other{# months}}' => '',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '',\n    '{delta, plural, =1{1 year} other{# years}}' => '',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta} ngày trước',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta} phút trước',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta} tháng trước',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta} giây trước',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta} năm trước',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta} giờ trước',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '',\n    '{nFormatted} KiB' => '',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{n} byte',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} gigabyte',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} kilobyte',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} megabyte',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} petabyte',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} terabyte',\n];\n"
  },
  {
    "path": "framework/messages/zh/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => ' 与 ',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '\"{attribute}\" 不支持操作 \"{operator}\"',\n    '(not set)' => '(未设置)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => '服务器内部错误。',\n    'Are you sure you want to delete this item?' => '您确定要删除此项吗？',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '\"{attribute}\" 的条件应为一个值或有效的操作规约。',\n    'Delete' => '删除',\n    'Error' => '错误',\n    'File upload failed.' => '文件上传失败。',\n    'Home' => '首页',\n    'Invalid data received for parameter \"{param}\".' => '\"{param}\"参数接收到无效的数据。',\n    'Login Required' => '需要登录',\n    'Missing required arguments: {params}' => '函数缺少参数：{params}',\n    'Missing required parameters: {params}' => '缺少参数：{params}',\n    'No' => '否',\n    'No results found.' => '没有找到数据。',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => '只允许这些MIME类型的文件: {mimeTypes}。',\n    'Only files with these extensions are allowed: {extensions}.' => '只允许使用以下文件扩展名的文件：{extensions}。',\n    'Operator \"{operator}\" must be used with a search attribute.' => '操作 \"{operator}\" 必须与一个搜索属性一起使用。',\n    'Operator \"{operator}\" requires multiple operands.' => '操作 \"{operator}\" 需要多个操作数。',\n    'Options available: {options}' => '',\n    'Page not found.' => '页面未找到。',\n    'Please fix the following errors:' => '请修复以下错误',\n    'Please upload a file.' => '请上传一个文件。',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => '第<b>{begin, number}-{end, number}</b>条，共<b>{totalCount, number}</b>条数据.',\n    'The combination {values} of {attributes} has already been taken.' => '{attributes} 的值 \"{values}\" 已经被占用了。',\n    'The file \"{file}\" is not an image.' => '文件 \"{file}\" 不是一个图像文件。',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => '文件\"{file}\"太大了。它的大小不能超过{formattedLimit}。',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => '文件\"{file}\"太小了。它的大小不能小于{formattedLimit}。',\n    'The format of {attribute} is invalid.' => '属性 {attribute} 的格式无效。',\n    'The format of {filter} is invalid.' => '{filter}的格式无效。',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '图像\"{file}\"太大。他的高度不得超过{limit, number}像素。',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '图像\"{file}\"太大。他的宽度不得超过{limit, number}像素。',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '图像\"{file}\"太小。他的高度不得小于{limit, number}像素。',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '图像\"{file}\"太小。他的宽度不得小于{limit, number}像素。',\n    'The requested view \"{name}\" was not found.' => '所请求的视图不存在\"{name}\"。',\n    'The verification code is incorrect.' => '验证码不正确。',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => '总计<b>{count, number}</b>条数据。',\n    'Unable to verify your data submission.' => '您提交的数据无法被验证。',\n    'Unknown alias: -{name}' => '未知的别名: -{name}',\n    'Unknown filter attribute \"{attribute}\"' => '未知的过滤器属性 \"{attribute}\"',\n    'Unknown option: --{name}' => '未知的选项：--{name}',\n    'Update' => '更新',\n    'View' => '查看',\n    'Yes' => '是',\n    'You are not allowed to perform this action.' => '您没有执行此操作的权限。',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => '您最多上传{limit, number}个文件。',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '需要至少 {limit, number} 个文件。',\n    'in {delta, plural, =1{a day} other{# days}}' => '{delta}天后',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => '{delta}分钟后',\n    'in {delta, plural, =1{a month} other{# months}}' => '{delta}个月后',\n    'in {delta, plural, =1{a second} other{# seconds}}' => '{delta}秒后',\n    'in {delta, plural, =1{a year} other{# years}}' => '{delta}年后',\n    'in {delta, plural, =1{an hour} other{# hours}}' => '{delta}小时后',\n    'just now' => '刚刚',\n    'the input value' => '该输入',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute}的值\"{value}\"已经被占用了。',\n    '{attribute} cannot be blank.' => '{attribute}不能为空。',\n    '{attribute} contains wrong subnet mask.' => '{attribute} 属性包含错误的子网掩码。',\n    '{attribute} is invalid.' => '{attribute}是无效的。',\n    '{attribute} is not a valid URL.' => '{attribute}不是一条有效的URL。',\n    '{attribute} is not a valid email address.' => '{attribute}不是有效的邮箱地址。',\n    '{attribute} is not in the allowed range.' => '{attribute} 不在允许的范围内。',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute}必须为\"{requiredValue}\"。',\n    '{attribute} must be a number.' => '{attribute}必须是一个数字。',\n    '{attribute} must be a string.' => '{attribute}必须是一条字符串。',\n    '{attribute} must be a valid IP address.' => '{attribute} 必须是一个有效的IP地址。',\n    '{attribute} must be an IP address with specified subnet.' => '{attribute} 必须指定一个IP地址和子网。',\n    '{attribute} must be an integer.' => '{attribute}必须是整数。',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute}的值必须要么为\"{true}\"，要么为\"{false}\"。',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '{attribute}的值必须等于\"{compareValueOrAttribute}\"。',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute}的值必须大于\"{compareValueOrAttribute}\"。',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute}的值必须大于或等于\"{compareValueOrAttribute}\"。',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute}的值必须小于\"{compareValueOrAttribute}\"。',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute}的值必须小于或等于\"{compareValueOrAttribute}\"。',\n    '{attribute} must be no greater than {max}.' => '{attribute}的值必须不大于{max}。',\n    '{attribute} must be no less than {min}.' => '{attribute}的值必须不小于{min}。',\n    '{attribute} must not be a subnet.' => '{attribute} 必须不是一个子网。',\n    '{attribute} must not be an IPv4 address.' => '{attribute} 必须不是一个IPv4地址。',\n    '{attribute} must not be an IPv6 address.' => '{attribute} 必须不是一个IPv6地址。',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute}的值不得等于\"{compareValueOrAttribute}\"。',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute}应该包含至少{min, number}个字符。',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute}只能包含至多{max, number}个字符。',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute}应该包含{length, number}个字符。',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta} 天',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta} 小时',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta} 分钟',\n    '{delta, plural, =1{1 month} other{# months}}' => '{delta} 月',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta} 秒',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta} 年',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta}天前',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta}分钟前',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta}个月前',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta}秒前',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta}年前',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta}小时前',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '{nFormatted} GiB',\n    '{nFormatted} KiB' => '{nFormatted} KiB',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '{nFormatted} MiB',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} 字节',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} 千兆二进制字节',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} 千兆字节',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} 千位二进制字节',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} 千字节',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} 兆二进制字节',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} 兆字节',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} 拍二进制字节',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} 拍字节',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} 太二进制字节',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} 太字节',\n];\n"
  },
  {
    "path": "framework/messages/zh-TW/yii.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\n/**\n * Message translations.\n *\n * This file is automatically generated by 'yii message/extract' command.\n * It contains the localizable messages extracted from source code.\n * You may modify this file by translating the extracted messages.\n *\n * Each array element represents the translation (value) of a message (key).\n * If the value is empty, the message is considered as not translated.\n * Messages that no longer need translation will have their translations\n * enclosed between a pair of '@@' marks.\n *\n * Message string can be used with plural forms format. Check i18n section\n * of the guide for details.\n *\n * NOTE: this file must be saved in UTF-8 encoding.\n */\nreturn [\n    ' and ' => ' 與 ',\n    '\"{attribute}\" does not support operator \"{operator}\".' => '',\n    '(not set)' => '(未設定)',\n    'Action not found.' => '',\n    'Aliases available: {aliases}' => '',\n    'An internal server error occurred.' => '內部系統錯誤。',\n    'Are you sure you want to delete this item?' => '您確定要刪除此項嗎？',\n    'Condition for \"{attribute}\" should be either a value or valid operator specification.' => '',\n    'Delete' => '刪除',\n    'Error' => '錯誤',\n    'File upload failed.' => '未能上傳檔案。',\n    'Home' => '首頁',\n    'Invalid data received for parameter \"{param}\".' => '\"{param}\" 參數資料不正確。',\n    'Login Required' => '需要登入',\n    'Missing required arguments: {params}' => '參數不齊全：{params}',\n    'Missing required parameters: {params}' => '參數不齊全：{params}',\n    'No' => '否',\n    'No results found.' => '沒有資料。',\n    'Only files with these MIME types are allowed: {mimeTypes}.' => '只允許這些MIME類型的文件: {mimeTypes}。',\n    'Only files with these extensions are allowed: {extensions}.' => '只可以使用以下擴充名的檔案：{extensions}。',\n    'Operator \"{operator}\" must be used with a search attribute.' => '',\n    'Operator \"{operator}\" requires multiple operands.' => '',\n    'Options available: {options}' => '',\n    'Page not found.' => '找不到頁面。',\n    'Please fix the following errors:' => '請修正以下錯誤：',\n    'Please upload a file.' => '請上傳一個檔案。',\n    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => '第 <b>{begin, number}-{end, number}</b> 項，共 <b>{totalCount, number}</b> 項資料.',\n    'The combination {values} of {attributes} has already been taken.' => '{attribute} 的值 \"{value}\" 已經被佔用了。',\n    'The file \"{file}\" is not an image.' => '檔案 \"{file}\" 不是一個圖片檔案。',\n    'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.' => '檔案\"{file}\"太大了。它的大小不可以超過{formattedLimit}。',\n    'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.' => '文件\"{file}\"太小了。它的大小不可以小於{formattedLimit}。',\n    'The format of {attribute} is invalid.' => '屬性 {attribute} 的格式不正確。',\n    'The format of {filter} is invalid.' => '',\n    'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '圖像 \"{file}\" 太大。它的高度不可以超過 {limit, number} 像素。',\n    'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '圖像 \"{file}\" 太大。它的寬度不可以超過 {limit, number} 像素。',\n    'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '圖像 \"{file}\" 太小。它的高度不可以小於 {limit, number} 像素。',\n    'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => '圖像 \"{file}\" 太小。它的寬度不可以小於 {limit, number} 像素。',\n    'The requested view \"{name}\" was not found.' => '所請求的視圖不存在\"{name}\"。',\n    'The verification code is incorrect.' => '驗證碼不正確。',\n    'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => '總計 <b>{count, number}</b> 項資料。',\n    'Unable to verify your data submission.' => '您提交的資料無法被驗證。',\n    'Unknown alias: -{name}' => '未知的別名: -{name}',\n    'Unknown filter attribute \"{attribute}\"' => '',\n    'Unknown option: --{name}' => '未知的選項：--{name}',\n    'Update' => '更新',\n    'View' => '查看',\n    'Yes' => '是',\n    'You are not allowed to perform this action.' => '您沒有執行此操作的權限。',\n    'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => '您最多可以上載 {limit, number} 個檔案。',\n    'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.' => '',\n    'in {delta, plural, =1{a day} other{# days}}' => '{delta}天後',\n    'in {delta, plural, =1{a minute} other{# minutes}}' => '{delta}分鐘後',\n    'in {delta, plural, =1{a month} other{# months}}' => '{delta}個月後',\n    'in {delta, plural, =1{a second} other{# seconds}}' => '{delta}秒後',\n    'in {delta, plural, =1{a year} other{# years}}' => '{delta}年後',\n    'in {delta, plural, =1{an hour} other{# hours}}' => '{delta}小時後',\n    'just now' => '剛剛',\n    'the input value' => '該輸入',\n    '{attribute} \"{value}\" has already been taken.' => '{attribute} 的值 \"{value}\" 已經被佔用了。',\n    '{attribute} cannot be blank.' => '{attribute} 不能為空白。',\n    '{attribute} contains wrong subnet mask.' => '{attribute} 屬性包含錯誤的子網掩碼。',\n    '{attribute} is invalid.' => '{attribute} 不正確。',\n    '{attribute} is not a valid URL.' => '{attribute} 不是一個正確的網址。',\n    '{attribute} is not a valid email address.' => '{attribute} 不是一個正確的電郵地址。',\n    '{attribute} is not in the allowed range.' => '{attribute} 不在允許的範圍內。',\n    '{attribute} must be \"{requiredValue}\".' => '{attribute}必須為 \"{requiredValue}\"。',\n    '{attribute} must be a number.' => '{attribute} 必須為數字。',\n    '{attribute} must be a string.' => '{attribute} 必須為字串。',\n    '{attribute} must be a valid IP address.' => '{attribute} 必須是一個有效的IP地址。',\n    '{attribute} must be an IP address with specified subnet.' => '{attribute} 必須指定一個IP地址和子網。',\n    '{attribute} must be an integer.' => '{attribute} 必須為整數。',\n    '{attribute} must be either \"{true}\" or \"{false}\".' => '{attribute} 必須為 \"{true}\" 或 \"{false}\"。',\n    '{attribute} must be equal to \"{compareValueOrAttribute}\".' => '{attribute}必須等於\"{compareValueOrAttribute}\"。',\n    '{attribute} must be greater than \"{compareValueOrAttribute}\".' => '{attribute} 必須大於 \"{compareValueOrAttribute}\"。',\n    '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".' => '{attribute} 必須大或等於 \"{compareValueOrAttribute}\"。',\n    '{attribute} must be less than \"{compareValueOrAttribute}\".' => '{attribute} 必須小於 \"{compareValueOrAttribute}\"。',\n    '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".' => '{attribute} 必須少或等於 \"{compareValueOrAttribute}\"。',\n    '{attribute} must be no greater than {max}.' => '{attribute} 不可以大於 {max}。',\n    '{attribute} must be no less than {min}.' => '{attribute} 不可以少於 {min}。',\n    '{attribute} must not be a subnet.' => '{attribute} 必須不是一個子網。',\n    '{attribute} must not be an IPv4 address.' => '{attribute} 必須不是一個IPv4地址。',\n    '{attribute} must not be an IPv6 address.' => '{attribute} 必須不是一個IPv6地址。',\n    '{attribute} must not be equal to \"{compareValueOrAttribute}\".' => '{attribute} 不可以等於 \"{compareValueOrAttribute}\"。',\n    '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} 應該包含至少 {min, number} 個字符。',\n    '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} 只能包含最多 {max, number} 個字符。',\n    '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} 應該包含 {length, number} 個字符。',\n    '{compareAttribute} is invalid.' => '',\n    '{delta, plural, =1{1 day} other{# days}}' => '{delta} 天',\n    '{delta, plural, =1{1 hour} other{# hours}}' => '{delta} 小時',\n    '{delta, plural, =1{1 minute} other{# minutes}}' => '{delta} 分鐘',\n    '{delta, plural, =1{1 month} other{# months}}' => '{delta} 月',\n    '{delta, plural, =1{1 second} other{# seconds}}' => '{delta} 秒',\n    '{delta, plural, =1{1 year} other{# years}}' => '{delta} 年',\n    '{delta, plural, =1{a day} other{# days}} ago' => '{delta}天前',\n    '{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta}分鐘前',\n    '{delta, plural, =1{a month} other{# months}} ago' => '{delta}個月前',\n    '{delta, plural, =1{a second} other{# seconds}} ago' => '{delta}秒前',\n    '{delta, plural, =1{a year} other{# years}} ago' => '{delta}年前',\n    '{delta, plural, =1{an hour} other{# hours}} ago' => '{delta}小時前',\n    '{nFormatted} B' => '{nFormatted} B',\n    '{nFormatted} GB' => '{nFormatted} GB',\n    '{nFormatted} GiB' => '{nFormatted} GiB',\n    '{nFormatted} KiB' => '{nFormatted} KiB',\n    '{nFormatted} MB' => '{nFormatted} MB',\n    '{nFormatted} MiB' => '{nFormatted} MiB',\n    '{nFormatted} PB' => '{nFormatted} PB',\n    '{nFormatted} PiB' => '{nFormatted} PiB',\n    '{nFormatted} TB' => '{nFormatted} TB',\n    '{nFormatted} TiB' => '{nFormatted} TiB',\n    '{nFormatted} kB' => '{nFormatted} kB',\n    '{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} 字節',\n    '{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} 千兆位二進制字節',\n    '{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} 千兆字節',\n    '{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} 千位二進制字節',\n    '{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} 千字節',\n    '{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} 兆位二進制字節',\n    '{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} 兆字節',\n    '{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} 拍位二進制字節',\n    '{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} 拍字節',\n    '{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} 太位二進制字節',\n    '{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} 太字節',\n];\n"
  },
  {
    "path": "framework/mutex/DbMutex.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\mutex;\n\nuse yii\\base\\InvalidConfigException;\nuse yii\\db\\Connection;\nuse yii\\di\\Instance;\n\n/**\n * DbMutex is the base class for classes, which relies on database while implementing mutex \"lock\" mechanism.\n *\n * @see Mutex\n *\n * @author resurtm <resurtm@gmail.com>\n * @since 2.0\n */\nabstract class DbMutex extends Mutex\n{\n    /**\n     * @var Connection|array|string the DB connection object or the application component ID of the DB connection.\n     * After the Mutex object is created, if you want to change this property, you should only assign\n     * it with a DB connection object.\n     * Starting from version 2.0.2, this can also be a configuration array for creating the object.\n     */\n    public $db = 'db';\n\n\n    /**\n     * Initializes generic database table based mutex implementation.\n     * @throws InvalidConfigException if [[db]] is invalid.\n     */\n    public function init()\n    {\n        parent::init();\n        $this->db = Instance::ensure($this->db, Connection::className());\n    }\n}\n"
  },
  {
    "path": "framework/mutex/FileMutex.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\mutex;\n\nuse Yii;\nuse yii\\base\\InvalidConfigException;\nuse yii\\helpers\\FileHelper;\n\n/**\n * FileMutex implements mutex \"lock\" mechanism via local file system files.\n *\n * This component relies on PHP `flock()` function.\n *\n * Application configuration example:\n *\n * ```\n * [\n *     'components' => [\n *         'mutex' => [\n *             'class' => 'yii\\mutex\\FileMutex'\n *         ],\n *     ],\n * ]\n * ```\n *\n * > Note: this component can maintain the locks only for the single web server,\n * > it probably will not suffice in case you are using cloud server solution.\n *\n * > Warning: due to `flock()` function nature this component is unreliable when\n * > using a multithreaded server API like ISAPI.\n *\n * @see Mutex\n *\n * @author resurtm <resurtm@gmail.com>\n * @since 2.0\n */\nclass FileMutex extends Mutex\n{\n    use RetryAcquireTrait;\n\n    /**\n     * @var string the directory to store mutex files. You may use [path alias](guide:concept-aliases) here.\n     * Defaults to the \"mutex\" subdirectory under the application runtime path.\n     */\n    public $mutexPath = '@runtime/mutex';\n    /**\n     * @var int|null the permission to be set for newly created mutex files.\n     * This value will be used by PHP chmod() function. No umask will be applied.\n     * If not set, the permission will be determined by the current environment.\n     */\n    public $fileMode;\n    /**\n     * @var int the permission to be set for newly created directories.\n     * This value will be used by PHP chmod() function. No umask will be applied.\n     * Defaults to 0775, meaning the directory is read-writable by owner and group,\n     * but read-only for other users.\n     */\n    public $dirMode = 0775;\n    /**\n     * @var bool|null whether file handling should assume a Windows file system.\n     * This value will determine how [[releaseLock()]] goes about deleting the lock file.\n     * If not set, it will be determined by checking the DIRECTORY_SEPARATOR constant.\n     * @since 2.0.16\n     */\n    public $isWindows;\n\n    /**\n     * @var resource[] stores all opened lock files. Keys are lock names and values are file handles.\n     */\n    private $_files = [];\n\n\n    /**\n     * Initializes mutex component implementation dedicated for UNIX, GNU/Linux, Mac OS X, and other UNIX-like\n     * operating systems.\n     * @throws InvalidConfigException\n     */\n    public function init()\n    {\n        parent::init();\n        $this->mutexPath = Yii::getAlias($this->mutexPath);\n        if (!is_dir($this->mutexPath)) {\n            FileHelper::createDirectory($this->mutexPath, $this->dirMode, true);\n        }\n        if ($this->isWindows === null) {\n            $this->isWindows = DIRECTORY_SEPARATOR === '\\\\';\n        }\n    }\n\n    /**\n     * Acquires lock by given name.\n     * @param string $name of the lock to be acquired.\n     * @param int $timeout time (in seconds) to wait for lock to become released.\n     * @return bool acquiring result.\n     */\n    protected function acquireLock($name, $timeout = 0)\n    {\n        $filePath = $this->getLockFilePath($name);\n        return $this->retryAcquire($timeout, function () use ($filePath, $name) {\n            $file = fopen($filePath, 'w+');\n            if ($file === false) {\n                return false;\n            }\n\n            if ($this->fileMode !== null) {\n                @chmod($filePath, $this->fileMode);\n            }\n\n            if (!flock($file, LOCK_EX | LOCK_NB)) {\n                fclose($file);\n                return false;\n            }\n\n            // Under unix we delete the lock file before releasing the related handle. Thus it's possible that we've acquired a lock on\n            // a non-existing file here (race condition). We must compare the inode of the lock file handle with the inode of the actual lock file.\n            // If they do not match we simply continue the loop since we can assume the inodes will be equal on the next try.\n            // Example of race condition without inode-comparison:\n            // Script A: locks file\n            // Script B: opens file\n            // Script A: unlinks and unlocks file\n            // Script B: locks handle of *unlinked* file\n            // Script C: opens and locks *new* file\n            // In this case we would have acquired two locks for the same file path.\n            if (DIRECTORY_SEPARATOR !== '\\\\' && fstat($file)['ino'] !== @fileinode($filePath)) {\n                clearstatcache(true, $filePath);\n                flock($file, LOCK_UN);\n                fclose($file);\n                return false;\n            }\n\n            $this->_files[$name] = $file;\n            return true;\n        });\n    }\n\n    /**\n     * Releases lock by given name.\n     * @param string $name of the lock to be released.\n     * @return bool release result.\n     */\n    protected function releaseLock($name)\n    {\n        if (!isset($this->_files[$name])) {\n            return false;\n        }\n\n        if ($this->isWindows) {\n            // Under windows it's not possible to delete a file opened via fopen (either by own or other process).\n            // That's why we must first unlock and close the handle and then *try* to delete the lock file.\n            flock($this->_files[$name], LOCK_UN);\n            fclose($this->_files[$name]);\n            @unlink($this->getLockFilePath($name));\n        } else {\n            // Under unix it's possible to delete a file opened via fopen (either by own or other process).\n            // That's why we must unlink (the currently locked) lock file first and then unlock and close the handle.\n            unlink($this->getLockFilePath($name));\n            flock($this->_files[$name], LOCK_UN);\n            fclose($this->_files[$name]);\n        }\n\n        unset($this->_files[$name]);\n        return true;\n    }\n\n    /**\n     * Generate path for lock file.\n     * @param string $name\n     * @return string\n     * @since 2.0.10\n     */\n    protected function getLockFilePath($name)\n    {\n        return $this->mutexPath . DIRECTORY_SEPARATOR . md5($name) . '.lock';\n    }\n}\n"
  },
  {
    "path": "framework/mutex/Mutex.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\mutex;\n\nuse yii\\base\\Component;\n\n/**\n * The Mutex component allows mutual execution of concurrent processes in order to prevent \"race conditions\".\n *\n * This is achieved by using a \"lock\" mechanism. Each possibly concurrent thread cooperates by acquiring\n * a lock before accessing the corresponding data.\n *\n * Usage example:\n *\n * ```\n * if ($mutex->acquire($mutexName)) {\n *     // business logic execution\n * } else {\n *     // execution is blocked!\n * }\n * ```\n *\n * This is a base class, which should be extended in order to implement the actual lock mechanism.\n *\n * @author resurtm <resurtm@gmail.com>\n * @since 2.0\n */\nabstract class Mutex extends Component\n{\n    /**\n     * @var bool whether all locks acquired in this process (i.e. local locks) must be released automatically\n     * before finishing script execution. Defaults to true. Setting this property to true means that all locks\n     * acquired in this process must be released (regardless of errors or exceptions).\n     */\n    public $autoRelease = true;\n\n    /**\n     * @var string[] names of the locks acquired by the current PHP process.\n     */\n    private $_locks = [];\n\n\n    /**\n     * Initializes the Mutex component.\n     */\n    public function init()\n    {\n        if ($this->autoRelease) {\n            $locks = &$this->_locks;\n            register_shutdown_function(function () use (&$locks) {\n                foreach ($locks as $lock) {\n                    $this->release($lock);\n                }\n            });\n        }\n    }\n\n    /**\n     * Acquires a lock by name.\n     * @param string $name of the lock to be acquired. Must be unique.\n     * @param int $timeout time (in seconds) to wait for lock to be released. Defaults to zero meaning that method will return\n     * false immediately in case lock was already acquired.\n     * @return bool lock acquiring result.\n     */\n    public function acquire($name, $timeout = 0)\n    {\n        if (!in_array($name, $this->_locks, true) && $this->acquireLock($name, $timeout)) {\n            $this->_locks[] = $name;\n\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * Releases acquired lock. This method will return false in case the lock was not found.\n     * @param string $name of the lock to be released. This lock must already exist.\n     * @return bool lock release result: false in case named lock was not found..\n     */\n    public function release($name)\n    {\n        if ($this->releaseLock($name)) {\n            $index = array_search($name, $this->_locks);\n            if ($index !== false) {\n                unset($this->_locks[$index]);\n            }\n\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * Checks if a lock is acquired by the current process.\n     * Note that it returns false if the mutex is acquired in another process.\n     *\n     * @param string $name of the lock to check.\n     * @return bool Returns true if currently acquired.\n     * @since 2.0.36\n     */\n    public function isAcquired($name)\n    {\n        return in_array($name, $this->_locks, true);\n    }\n\n    /**\n     * This method should be extended by a concrete Mutex implementations. Acquires lock by name.\n     * @param string $name of the lock to be acquired.\n     * @param int $timeout time (in seconds) to wait for the lock to be released.\n     * @return bool acquiring result.\n     */\n    abstract protected function acquireLock($name, $timeout = 0);\n\n    /**\n     * This method should be extended by a concrete Mutex implementations. Releases lock by given name.\n     * @param string $name of the lock to be released.\n     * @return bool release result.\n     */\n    abstract protected function releaseLock($name);\n}\n"
  },
  {
    "path": "framework/mutex/MysqlMutex.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\mutex;\n\nuse yii\\base\\InvalidConfigException;\nuse yii\\db\\Expression;\n\n/**\n * MysqlMutex implements mutex \"lock\" mechanism via MySQL locks.\n *\n * Application configuration example:\n *\n * ```\n * [\n *     'components' => [\n *         'db' => [\n *             'class' => 'yii\\db\\Connection',\n *             'dsn' => 'mysql:host=127.0.0.1;dbname=demo',\n *         ]\n *         'mutex' => [\n *             'class' => 'yii\\mutex\\MysqlMutex',\n *         ],\n *     ],\n * ]\n * ```\n *\n * @see Mutex\n *\n * @author resurtm <resurtm@gmail.com>\n * @since 2.0\n */\nclass MysqlMutex extends DbMutex\n{\n    /**\n     * @var Expression|string|null prefix value. If null (by default) then connection's current database name is used.\n     * @since 2.0.47\n     */\n    public $keyPrefix = null;\n\n\n    /**\n     * Initializes MySQL specific mutex component implementation.\n     * @throws InvalidConfigException if [[db]] is not MySQL connection.\n     */\n    public function init()\n    {\n        parent::init();\n        if ($this->db->driverName !== 'mysql') {\n            throw new InvalidConfigException('In order to use MysqlMutex connection must be configured to use MySQL database.');\n        }\n        if ($this->keyPrefix === null) {\n            $this->keyPrefix = new Expression('DATABASE()');\n        }\n    }\n\n    /**\n     * Acquires lock by given name.\n     * @param string $name of the lock to be acquired.\n     * @param int $timeout time (in seconds) to wait for lock to become released.\n     * @return bool acquiring result.\n     * @see https://dev.mysql.com/doc/refman/8.0/en/miscellaneous-functions.html#function_get-lock\n     */\n    protected function acquireLock($name, $timeout = 0)\n    {\n        return $this->db->useMaster(function ($db) use ($name, $timeout) {\n            /** @var \\yii\\db\\Connection $db */\n            $nameData = $this->prepareName();\n            return (bool)$db->createCommand(\n                'SELECT GET_LOCK(' . $nameData[0] . ', :timeout), :prefix',\n                array_merge(\n                    [':name' => $this->hashLockName($name), ':timeout' => $timeout, ':prefix' => $this->keyPrefix],\n                    $nameData[1]\n                )\n            )->queryScalar();\n        });\n    }\n\n    /**\n     * Releases lock by given name.\n     * @param string $name of the lock to be released.\n     * @return bool release result.\n     * @see https://dev.mysql.com/doc/refman/8.0/en/miscellaneous-functions.html#function_release-lock\n     */\n    protected function releaseLock($name)\n    {\n        return $this->db->useMaster(function ($db) use ($name) {\n            /** @var \\yii\\db\\Connection $db */\n            $nameData = $this->prepareName();\n            return (bool)$db->createCommand(\n                'SELECT RELEASE_LOCK(' . $nameData[0] . '), :prefix',\n                array_merge(\n                    [':name' => $this->hashLockName($name), ':prefix' => $this->keyPrefix],\n                    $nameData[1]\n                )\n            )->queryScalar();\n        });\n    }\n\n    /**\n     * Prepare lock name\n     * @return array expression and params\n     * @since 2.0.48\n     */\n    protected function prepareName()\n    {\n        $params = [];\n        $expression = 'SUBSTRING(CONCAT(:prefix, :name), 1, 64)';\n        if ($this->keyPrefix instanceof Expression) {\n            $expression = strtr($expression, [':prefix' => $this->keyPrefix->expression]);\n            $params = $this->keyPrefix->params;\n        }\n        return [$expression, $params];\n    }\n\n    /**\n     * Generate hash for lock name to avoid exceeding lock name length limit.\n     *\n     * @param string $name\n     * @return string\n     * @since 2.0.16\n     * @see https://github.com/yiisoft/yii2/pull/16836\n     */\n    protected function hashLockName($name)\n    {\n        return sha1($name);\n    }\n}\n"
  },
  {
    "path": "framework/mutex/OracleMutex.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\mutex;\n\nuse PDO;\nuse yii\\base\\InvalidConfigException;\n\n/**\n * OracleMutex implements mutex \"lock\" mechanism via Oracle locks.\n *\n * Application configuration example:\n *\n * ```\n * [\n *     'components' => [\n *         'db' => [\n *             'class' => 'yii\\db\\Connection',\n *             'dsn' => 'oci:dbname=LOCAL_XE',\n *              ...\n *         ]\n *         'mutex' => [\n *             'class' => 'yii\\mutex\\OracleMutex',\n *             'lockMode' => 'NL_MODE',\n *             'releaseOnCommit' => true,\n *              ...\n *         ],\n *     ],\n * ]\n * ```\n *\n * @see https://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_lock.htm#ARPLS021\n * @see Mutex\n *\n * @author Alexander Zlakomanov <zlakomanoff@gmail.com>\n * @since 2.0.10\n */\nclass OracleMutex extends DbMutex\n{\n    /** available lock modes */\n    public const MODE_X = 'X_MODE';\n    public const MODE_NL = 'NL_MODE';\n    public const MODE_S = 'S_MODE';\n    public const MODE_SX = 'SX_MODE';\n    public const MODE_SS = 'SS_MODE';\n    public const MODE_SSX = 'SSX_MODE';\n    /**\n     * @var string lock mode to be used.\n     * @see https://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_lock.htm#ARPLS021#CHDBCFDI\n     */\n    public $lockMode = self::MODE_X;\n    /**\n     * @var bool whether to release lock on commit.\n     */\n    public $releaseOnCommit = false;\n\n\n    /**\n     * Initializes Oracle specific mutex component implementation.\n     * @throws InvalidConfigException if [[db]] is not Oracle connection.\n     */\n    public function init()\n    {\n        parent::init();\n        if (strncmp($this->db->driverName, 'oci', 3) !== 0 && strncmp($this->db->driverName, 'odbc', 4) !== 0) {\n            throw new InvalidConfigException('In order to use OracleMutex connection must be configured to use Oracle database.');\n        }\n    }\n\n    /**\n     * Acquires lock by given name.\n     * @see https://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_lock.htm#ARPLS021\n     * @param string $name of the lock to be acquired.\n     * @param int $timeout time (in seconds) to wait for lock to become released.\n     * @return bool acquiring result.\n     */\n    protected function acquireLock($name, $timeout = 0)\n    {\n        $lockStatus = null;\n\n        // clean vars before using\n        $releaseOnCommit = $this->releaseOnCommit ? 'TRUE' : 'FALSE';\n        $timeout = abs((int) $timeout);\n\n        // inside pl/sql scopes pdo binding not working correctly :(\n        $this->db->useMaster(function ($db) use ($name, $timeout, $releaseOnCommit, &$lockStatus) {\n            /** @var \\yii\\db\\Connection $db */\n            $db->createCommand(\n                'DECLARE\n    handle VARCHAR2(128);\nBEGIN\n    DBMS_LOCK.ALLOCATE_UNIQUE(:name, handle);\n    :lockStatus := DBMS_LOCK.REQUEST(handle, DBMS_LOCK.' . $this->lockMode . ', ' . $timeout . ', ' . $releaseOnCommit . ');\nEND;',\n                [':name' => $name]\n            )\n            ->bindParam(':lockStatus', $lockStatus, PDO::PARAM_INT, 1)\n            ->execute();\n        });\n\n        return $lockStatus === 0 || $lockStatus === '0';\n    }\n\n    /**\n     * Releases lock by given name.\n     * @param string $name of the lock to be released.\n     * @return bool release result.\n     * @see https://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_lock.htm#ARPLS021\n     */\n    protected function releaseLock($name)\n    {\n        $releaseStatus = null;\n        $this->db->useMaster(function ($db) use ($name, &$releaseStatus) {\n            /** @var \\yii\\db\\Connection $db */\n            $db->createCommand(\n                'DECLARE\n    handle VARCHAR2(128);\nBEGIN\n    DBMS_LOCK.ALLOCATE_UNIQUE(:name, handle);\n    :result := DBMS_LOCK.RELEASE(handle);\nEND;',\n                [':name' => $name]\n            )\n            ->bindParam(':result', $releaseStatus, PDO::PARAM_INT, 1)\n            ->execute();\n        });\n\n        return $releaseStatus === 0 || $releaseStatus === '0';\n    }\n}\n"
  },
  {
    "path": "framework/mutex/PgsqlMutex.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\mutex;\n\nuse yii\\base\\InvalidConfigException;\n\n/**\n * PgsqlMutex implements mutex \"lock\" mechanism via PgSQL locks.\n *\n * Application configuration example:\n *\n * ```\n * [\n *     'components' => [\n *         'db' => [\n *             'class' => 'yii\\db\\Connection',\n *             'dsn' => 'pgsql:host=127.0.0.1;dbname=demo',\n *         ]\n *         'mutex' => [\n *             'class' => 'yii\\mutex\\PgsqlMutex',\n *         ],\n *     ],\n * ]\n * ```\n *\n * @see Mutex\n *\n * @author nineinchnick <janek.jan@gmail.com>\n * @since 2.0.8\n */\nclass PgsqlMutex extends DbMutex\n{\n    use RetryAcquireTrait;\n\n\n    /**\n     * Initializes PgSQL specific mutex component implementation.\n     * @throws InvalidConfigException if [[db]] is not PgSQL connection.\n     */\n    public function init()\n    {\n        parent::init();\n        if ($this->db->driverName !== 'pgsql') {\n            throw new InvalidConfigException('In order to use PgsqlMutex connection must be configured to use PgSQL database.');\n        }\n    }\n\n    /**\n     * Converts a string into two 16 bit integer keys using the SHA1 hash function.\n     * @param string $name\n     * @return array contains two 16 bit integer keys\n     */\n    private function getKeysFromName($name)\n    {\n        return array_values(unpack('n2', sha1($name, true)));\n    }\n\n    /**\n     * Acquires lock by given name.\n     * @param string $name of the lock to be acquired.\n     * @param int $timeout time (in seconds) to wait for lock to become released.\n     * @return bool acquiring result.\n     * @see https://www.postgresql.org/docs/9.0/functions-admin.html\n     */\n    protected function acquireLock($name, $timeout = 0)\n    {\n        list($key1, $key2) = $this->getKeysFromName($name);\n\n        return $this->retryAcquire($timeout, function () use ($key1, $key2) {\n            return $this->db->useMaster(function ($db) use ($key1, $key2) {\n                /** @var \\yii\\db\\Connection $db */\n                return (bool) $db->createCommand(\n                    'SELECT pg_try_advisory_lock(:key1, :key2)',\n                    [':key1' => $key1, ':key2' => $key2]\n                )->queryScalar();\n            });\n        });\n    }\n\n    /**\n     * Releases lock by given name.\n     * @param string $name of the lock to be released.\n     * @return bool release result.\n     * @see https://www.postgresql.org/docs/9.0/functions-admin.html\n     */\n    protected function releaseLock($name)\n    {\n        list($key1, $key2) = $this->getKeysFromName($name);\n        return $this->db->useMaster(function ($db) use ($key1, $key2) {\n            /** @var \\yii\\db\\Connection $db */\n            return (bool) $db->createCommand(\n                'SELECT pg_advisory_unlock(:key1, :key2)',\n                [':key1' => $key1, ':key2' => $key2]\n            )->queryScalar();\n        });\n    }\n}\n"
  },
  {
    "path": "framework/mutex/RetryAcquireTrait.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\mutex;\n\nuse Closure;\n\n/**\n * Trait RetryAcquireTrait.\n *\n * @author Robert Korulczyk <robert@korulczyk.pl>\n * @internal\n * @since 2.0.16\n */\ntrait RetryAcquireTrait\n{\n    /**\n     * @var int Number of milliseconds between each try in [[acquire()]] until specified timeout times out.\n     * By default it is 50 milliseconds - it means that [[acquire()]] may try acquire lock up to 20 times per second.\n     * @since 2.0.16\n     */\n    public $retryDelay = 50;\n\n\n    private function retryAcquire($timeout, Closure $callback)\n    {\n        $start = microtime(true);\n        do {\n            if ($callback()) {\n                return true;\n            }\n            usleep($this->retryDelay * 1000);\n        } while (microtime(true) - $start < $timeout);\n\n        return false;\n    }\n}\n"
  },
  {
    "path": "framework/rbac/Assignment.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\rbac;\n\nuse yii\\base\\BaseObject;\n\n/**\n * Assignment represents an assignment of a role to a user.\n *\n * For more details and usage information on Assignment, see the [guide article on security authorization](guide:security-authorization).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Alexander Kochetov <creocoder@gmail.com>\n * @since 2.0\n */\nclass Assignment extends BaseObject\n{\n    /**\n     * @var string|int user ID (see [[\\yii\\web\\User::id]])\n     */\n    public $userId;\n    /**\n     * @var string the role name\n     */\n    public $roleName;\n    /**\n     * @var int UNIX timestamp representing the assignment creation time\n     */\n    public $createdAt;\n}\n"
  },
  {
    "path": "framework/rbac/BaseManager.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\rbac;\n\nuse yii\\base\\Component;\nuse yii\\base\\InvalidArgumentException;\nuse yii\\base\\InvalidConfigException;\nuse yii\\base\\InvalidValueException;\n\n/**\n * BaseManager is a base class implementing [[ManagerInterface]] for RBAC management.\n *\n * For more details and usage information on DbManager, see the [guide article on security authorization](guide:security-authorization).\n *\n * @property-read Role[] $defaultRoleInstances Default roles. The array is indexed by the role names.\n * @property string[] $defaultRoles Default roles. Note that the type of this property differs in getter and\n * setter. See [[getDefaultRoles()]] and [[setDefaultRoles()]] for details.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nabstract class BaseManager extends Component implements ManagerInterface\n{\n    /**\n     * @var array a list of role names that are assigned to every user automatically without calling [[assign()]].\n     * Note that these roles are applied to users, regardless of their state of authentication.\n     */\n    protected $defaultRoles = [];\n\n\n    /**\n     * Returns the named auth item.\n     * @param string $name the auth item name.\n     * @return Item|null the auth item corresponding to the specified name. Null is returned if no such item.\n     */\n    abstract protected function getItem($name);\n\n    /**\n     * Returns the items of the specified type.\n     * @param Item::TYPE_ROLE|Item::TYPE_PERMISSION $type the auth item type (either [[Item::TYPE_ROLE]] or [[Item::TYPE_PERMISSION]]\n     * @return ($type is Item::TYPE_ROLE ? Role[] : Permission[]) the auth items of the specified type.\n     */\n    abstract protected function getItems($type);\n\n    /**\n     * Adds an auth item to the RBAC system.\n     * @param Item $item the item to add\n     * @return bool whether the auth item is successfully added to the system\n     * @throws \\Exception if data validation or saving fails (such as the name of the role or permission is not unique)\n     */\n    abstract protected function addItem($item);\n\n    /**\n     * Adds a rule to the RBAC system.\n     * @param Rule $rule the rule to add\n     * @return bool whether the rule is successfully added to the system\n     * @throws \\Exception if data validation or saving fails (such as the name of the rule is not unique)\n     */\n    abstract protected function addRule($rule);\n\n    /**\n     * Removes an auth item from the RBAC system.\n     * @param Item $item the item to remove\n     * @return bool whether the role or permission is successfully removed\n     * @throws \\Exception if data validation or saving fails (such as the name of the role or permission is not unique)\n     */\n    abstract protected function removeItem($item);\n\n    /**\n     * Removes a rule from the RBAC system.\n     * @param Rule $rule the rule to remove\n     * @return bool whether the rule is successfully removed\n     * @throws \\Exception if data validation or saving fails (such as the name of the rule is not unique)\n     */\n    abstract protected function removeRule($rule);\n\n    /**\n     * Updates an auth item in the RBAC system.\n     * @param string $name the name of the item being updated\n     * @param Item $item the updated item\n     * @return bool whether the auth item is successfully updated\n     * @throws \\Exception if data validation or saving fails (such as the name of the role or permission is not unique)\n     */\n    abstract protected function updateItem($name, $item);\n\n    /**\n     * Updates a rule to the RBAC system.\n     * @param string $name the name of the rule being updated\n     * @param Rule $rule the updated rule\n     * @return bool whether the rule is successfully updated\n     * @throws \\Exception if data validation or saving fails (such as the name of the rule is not unique)\n     */\n    abstract protected function updateRule($name, $rule);\n\n    /**\n     * {@inheritdoc}\n     */\n    public function createRole($name)\n    {\n        $role = new Role();\n        $role->name = $name;\n        return $role;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function createPermission($name)\n    {\n        $permission = new Permission();\n        $permission->name = $name;\n        return $permission;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function add($object)\n    {\n        if ($object instanceof Item) {\n            if ($object->ruleName && $this->getRule($object->ruleName) === null) {\n                $rule = \\Yii::createObject($object->ruleName);\n                $rule->name = $object->ruleName;\n                $this->addRule($rule);\n            }\n\n            return $this->addItem($object);\n        } elseif ($object instanceof Rule) {\n            return $this->addRule($object);\n        }\n\n        throw new InvalidArgumentException('Adding unsupported object type.');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function remove($object)\n    {\n        if ($object instanceof Item) {\n            return $this->removeItem($object);\n        } elseif ($object instanceof Rule) {\n            return $this->removeRule($object);\n        }\n\n        throw new InvalidArgumentException('Removing unsupported object type.');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function update($name, $object)\n    {\n        if ($object instanceof Item) {\n            if ($object->ruleName && $this->getRule($object->ruleName) === null) {\n                $rule = \\Yii::createObject($object->ruleName);\n                $rule->name = $object->ruleName;\n                $this->addRule($rule);\n            }\n\n            return $this->updateItem($name, $object);\n        } elseif ($object instanceof Rule) {\n            return $this->updateRule($name, $object);\n        }\n\n        throw new InvalidArgumentException('Updating unsupported object type.');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getRole($name)\n    {\n        $item = $this->getItem($name);\n        if ($item instanceof Item && $item->type == Item::TYPE_ROLE) {\n            /** @var Role $item */\n            return $item;\n        }\n\n        return null;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getPermission($name)\n    {\n        $item = $this->getItem($name);\n        if ($item instanceof Item && $item->type == Item::TYPE_PERMISSION) {\n            /** @var Permission $item */\n            return $item;\n        }\n\n        return null;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getRoles()\n    {\n        return $this->getItems(Item::TYPE_ROLE);\n    }\n\n    /**\n     * Set default roles\n     * @param string[]|\\Closure $roles either array of roles or a callable returning it\n     * @throws InvalidArgumentException when $roles is neither array nor Closure\n     * @throws InvalidValueException when Closure return is not an array\n     * @since 2.0.14\n     */\n    public function setDefaultRoles($roles)\n    {\n        if (is_array($roles)) {\n            $this->defaultRoles = $roles;\n        } elseif ($roles instanceof \\Closure) {\n            $roles = call_user_func($roles);\n            if (!is_array($roles)) {\n                throw new InvalidValueException('Default roles closure must return an array');\n            }\n            $this->defaultRoles = $roles;\n        } else {\n            throw new InvalidArgumentException('Default roles must be either an array or a callable');\n        }\n    }\n\n    /**\n     * Get default roles\n     * @return string[] default roles\n     * @since 2.0.14\n     */\n    public function getDefaultRoles()\n    {\n        return $this->defaultRoles;\n    }\n\n    /**\n     * Returns defaultRoles as array of Role objects.\n     * @since 2.0.12\n     * @return Role[] default roles. The array is indexed by the role names\n     */\n    public function getDefaultRoleInstances()\n    {\n        $result = [];\n        foreach ($this->defaultRoles as $roleName) {\n            $result[$roleName] = $this->createRole($roleName);\n        }\n\n        return $result;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getPermissions()\n    {\n        return $this->getItems(Item::TYPE_PERMISSION);\n    }\n\n    /**\n     * Executes the rule associated with the specified auth item.\n     *\n     * If the item does not specify a rule, this method will return true. Otherwise, it will\n     * return the value of [[Rule::execute()]].\n     *\n     * @param string|int $user the user ID. This should be either an integer or a string representing\n     * the unique identifier of a user. See [[\\yii\\web\\User::id]].\n     * @param Item $item the auth item that needs to execute its rule\n     * @param array $params parameters passed to [[CheckAccessInterface::checkAccess()]] and will be passed to the rule\n     * @return bool the return value of [[Rule::execute()]]. If the auth item does not specify a rule, true will be returned.\n     * @throws InvalidConfigException if the auth item has an invalid rule.\n     */\n    protected function executeRule($user, $item, $params)\n    {\n        if ($item->ruleName === null) {\n            return true;\n        }\n        $rule = $this->getRule($item->ruleName);\n        if ($rule instanceof Rule) {\n            return $rule->execute($user, $item, $params);\n        }\n\n        throw new InvalidConfigException(\"Rule not found: {$item->ruleName}\");\n    }\n\n    /**\n     * Checks whether array of $assignments is empty and [[defaultRoles]] property is empty as well.\n     *\n     * @param Assignment[] $assignments array of user's assignments\n     * @return bool whether array of $assignments is empty and [[defaultRoles]] property is empty as well\n     * @since 2.0.11\n     */\n    protected function hasNoAssignments(array $assignments)\n    {\n        return empty($assignments) && empty($this->defaultRoles);\n    }\n}\n"
  },
  {
    "path": "framework/rbac/CheckAccessInterface.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\rbac;\n\n/**\n * For more details and usage information on CheckAccessInterface, see the [guide article on security authorization](guide:security-authorization).\n *\n * @author Sam Mousa <sam@mousa.nl>\n * @since 2.0.9\n */\ninterface CheckAccessInterface\n{\n    /**\n     * Checks if the user has the specified permission.\n     * @param string|int $userId the user ID. This should be either an integer or a string representing\n     * the unique identifier of a user. See [[\\yii\\web\\User::id]].\n     * @param string $permissionName the name of the permission to be checked against\n     * @param array $params name-value pairs that will be passed to the rules associated\n     * with the roles and permissions assigned to the user.\n     * @return bool whether the user has the specified permission.\n     * @throws \\yii\\base\\InvalidParamException if $permissionName does not refer to an existing permission\n     */\n    public function checkAccess($userId, $permissionName, $params = []);\n}\n"
  },
  {
    "path": "framework/rbac/DbManager.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\rbac;\n\nuse Yii;\nuse yii\\base\\InvalidArgumentException;\nuse yii\\base\\InvalidCallException;\nuse yii\\caching\\CacheInterface;\nuse yii\\db\\Connection;\nuse yii\\db\\Expression;\nuse yii\\db\\Query;\nuse yii\\di\\Instance;\n\n/**\n * DbManager represents an authorization manager that stores authorization information in database.\n *\n * The database connection is specified by [[db]]. The database schema could be initialized by applying migration:\n *\n * ```\n * yii migrate --migrationPath=@yii/rbac/migrations/\n * ```\n *\n * If you don't want to use migration and need SQL instead, files for all databases are in migrations directory.\n *\n * You may change the names of the tables used to store the authorization and rule data by setting [[itemTable]],\n * [[itemChildTable]], [[assignmentTable]] and [[ruleTable]].\n *\n * For more details and usage information on DbManager, see the [guide article on security authorization](guide:security-authorization).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Alexander Kochetov <creocoder@gmail.com>\n * @since 2.0\n */\nclass DbManager extends BaseManager\n{\n    /**\n     * @var Connection|array|string the DB connection object or the application component ID of the DB connection.\n     * After the DbManager object is created, if you want to change this property, you should only assign it\n     * with a DB connection object.\n     * Starting from version 2.0.2, this can also be a configuration array for creating the object.\n     */\n    public $db = 'db';\n    /**\n     * @var string the name of the table storing authorization items. Defaults to \"auth_item\".\n     */\n    public $itemTable = '{{%auth_item}}';\n    /**\n     * @var string the name of the table storing authorization item hierarchy. Defaults to \"auth_item_child\".\n     */\n    public $itemChildTable = '{{%auth_item_child}}';\n    /**\n     * @var string the name of the table storing authorization item assignments. Defaults to \"auth_assignment\".\n     */\n    public $assignmentTable = '{{%auth_assignment}}';\n    /**\n     * @var string the name of the table storing rules. Defaults to \"auth_rule\".\n     */\n    public $ruleTable = '{{%auth_rule}}';\n    /**\n     * @var CacheInterface|array|string|null the cache used to improve RBAC performance. This can be one of the following:\n     *\n     * - an application component ID (e.g. `cache`)\n     * - a configuration array\n     * - a [[\\yii\\caching\\Cache]] object\n     *\n     * When this is not set, it means caching is not enabled.\n     *\n     * Note that by enabling RBAC cache, all auth items, rules and auth item parent-child relationships will\n     * be cached and loaded into memory. This will improve the performance of RBAC permission check. However,\n     * it does require extra memory and as a result may not be appropriate if your RBAC system contains too many\n     * auth items. You should seek other RBAC implementations (e.g. RBAC based on Redis storage) in this case.\n     *\n     * Also note that if you modify RBAC items, rules or parent-child relationships from outside of this component,\n     * you have to manually call [[invalidateCache()]] to ensure data consistency.\n     *\n     * @since 2.0.3\n     */\n    public $cache;\n    /**\n     * @var string the key used to store RBAC data in cache\n     * @see cache\n     * @since 2.0.3\n     */\n    public $cacheKey = 'rbac';\n    /**\n     * @var string the key used to store user RBAC roles in cache\n     * @since 2.0.48\n     */\n    public $rolesCacheSuffix = 'roles';\n\n    /**\n     * @var Item[]|null all auth items (name => Item)\n     */\n    protected $items;\n    /**\n     * @var Rule[]|null all auth rules (name => Rule)\n     */\n    protected $rules;\n    /**\n     * @var array|null auth item parent-child relationships (childName => list of parents)\n     */\n    protected $parents;\n    /**\n     * @var array user assignments (user id => Assignment[])\n     * @since `protected` since 2.0.38\n     */\n    protected $checkAccessAssignments = [];\n\n\n    /**\n     * Initializes the application component.\n     * This method overrides the parent implementation by establishing the database connection.\n     */\n    public function init()\n    {\n        parent::init();\n        $this->db = Instance::ensure($this->db, Connection::className());\n        if ($this->cache !== null) {\n            $this->cache = Instance::ensure($this->cache, 'yii\\caching\\CacheInterface');\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function checkAccess($userId, $permissionName, $params = [])\n    {\n        if (isset($this->checkAccessAssignments[(string) $userId])) {\n            $assignments = $this->checkAccessAssignments[(string) $userId];\n        } else {\n            $assignments = $this->getAssignments($userId);\n            $this->checkAccessAssignments[(string) $userId] = $assignments;\n        }\n\n        if ($this->hasNoAssignments($assignments)) {\n            return false;\n        }\n\n        $this->loadFromCache();\n        if ($this->items !== null) {\n            return $this->checkAccessFromCache($userId, $permissionName, $params, $assignments);\n        }\n\n        return $this->checkAccessRecursive($userId, $permissionName, $params, $assignments);\n    }\n\n    /**\n     * Performs access check for the specified user based on the data loaded from cache.\n     * This method is internally called by [[checkAccess()]] when [[cache]] is enabled.\n     * @param string|int $user the user ID. This should can be either an integer or a string representing\n     * the unique identifier of a user. See [[\\yii\\web\\User::id]].\n     * @param string $itemName the name of the operation that need access check\n     * @param array $params name-value pairs that would be passed to rules associated\n     * with the tasks and roles assigned to the user. A param with name 'user' is added to this array,\n     * which holds the value of `$userId`.\n     * @param Assignment[] $assignments the assignments to the specified user\n     * @return bool whether the operations can be performed by the user.\n     * @since 2.0.3\n     */\n    protected function checkAccessFromCache($user, $itemName, $params, $assignments)\n    {\n        if (!isset($this->items[$itemName])) {\n            return false;\n        }\n\n        $item = $this->items[$itemName];\n\n        Yii::debug($item instanceof Role ? \"Checking role: $itemName\" : \"Checking permission: $itemName\", __METHOD__);\n\n        if (!$this->executeRule($user, $item, $params)) {\n            return false;\n        }\n\n        if (isset($assignments[$itemName]) || in_array($itemName, $this->defaultRoles)) {\n            return true;\n        }\n\n        if (!empty($this->parents[$itemName])) {\n            foreach ($this->parents[$itemName] as $parent) {\n                if ($this->checkAccessFromCache($user, $parent, $params, $assignments)) {\n                    return true;\n                }\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * Performs access check for the specified user.\n     * This method is internally called by [[checkAccess()]].\n     * @param string|int $user the user ID. This should can be either an integer or a string representing\n     * the unique identifier of a user. See [[\\yii\\web\\User::id]].\n     * @param string $itemName the name of the operation that need access check\n     * @param array $params name-value pairs that would be passed to rules associated\n     * with the tasks and roles assigned to the user. A param with name 'user' is added to this array,\n     * which holds the value of `$userId`.\n     * @param Assignment[] $assignments the assignments to the specified user\n     * @return bool whether the operations can be performed by the user.\n     */\n    protected function checkAccessRecursive($user, $itemName, $params, $assignments)\n    {\n        if (($item = $this->getItem($itemName)) === null) {\n            return false;\n        }\n\n        Yii::debug($item instanceof Role ? \"Checking role: $itemName\" : \"Checking permission: $itemName\", __METHOD__);\n\n        if (!$this->executeRule($user, $item, $params)) {\n            return false;\n        }\n\n        if (isset($assignments[$itemName]) || in_array($itemName, $this->defaultRoles)) {\n            return true;\n        }\n\n        $query = new Query();\n        $parents = $query->select(['parent'])\n            ->from($this->itemChildTable)\n            ->where(['child' => $itemName])\n            ->column($this->db);\n        foreach ($parents as $parent) {\n            if ($this->checkAccessRecursive($user, $parent, $params, $assignments)) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function getItem($name)\n    {\n        if (empty($name)) {\n            return null;\n        }\n\n        if (!empty($this->items[$name])) {\n            return $this->items[$name];\n        }\n\n        $row = (new Query())->from($this->itemTable)\n            ->where(['name' => $name])\n            ->one($this->db);\n\n        if ($row === false) {\n            return null;\n        }\n\n        return $this->populateItem($row);\n    }\n\n    /**\n     * Returns a value indicating whether the database supports cascading update and delete.\n     * The default implementation will return false for SQLite database and true for all other databases.\n     * @return bool whether the database supports cascading update and delete.\n     */\n    protected function supportsCascadeUpdate()\n    {\n        return strncmp($this->db->getDriverName(), 'sqlite', 6) !== 0;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function addItem($item)\n    {\n        $time = time();\n        if ($item->createdAt === null) {\n            $item->createdAt = $time;\n        }\n        if ($item->updatedAt === null) {\n            $item->updatedAt = $time;\n        }\n        $this->db->createCommand()\n            ->insert($this->itemTable, [\n                'name' => $item->name,\n                'type' => $item->type,\n                'description' => $item->description,\n                'rule_name' => $item->ruleName,\n                'data' => $item->data === null ? null : serialize($item->data),\n                'created_at' => $item->createdAt,\n                'updated_at' => $item->updatedAt,\n            ])->execute();\n\n        $this->invalidateCache();\n\n        return true;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function removeItem($item)\n    {\n        if (!$this->supportsCascadeUpdate()) {\n            $this->db->createCommand()\n                ->delete($this->itemChildTable, ['or', '[[parent]]=:parent', '[[child]]=:child'], [':parent' => $item->name, ':child' => $item->name])\n                ->execute();\n            $this->db->createCommand()\n                ->delete($this->assignmentTable, ['item_name' => $item->name])\n                ->execute();\n        }\n\n        $this->db->createCommand()\n            ->delete($this->itemTable, ['name' => $item->name])\n            ->execute();\n\n        $this->invalidateCache();\n\n        return true;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function updateItem($name, $item)\n    {\n        if ($item->name !== $name && !$this->supportsCascadeUpdate()) {\n            $this->db->createCommand()\n                ->update($this->itemChildTable, ['parent' => $item->name], ['parent' => $name])\n                ->execute();\n            $this->db->createCommand()\n                ->update($this->itemChildTable, ['child' => $item->name], ['child' => $name])\n                ->execute();\n            $this->db->createCommand()\n                ->update($this->assignmentTable, ['item_name' => $item->name], ['item_name' => $name])\n                ->execute();\n        }\n\n        $item->updatedAt = time();\n\n        $this->db->createCommand()\n            ->update($this->itemTable, [\n                'name' => $item->name,\n                'description' => $item->description,\n                'rule_name' => $item->ruleName,\n                'data' => $item->data === null ? null : serialize($item->data),\n                'updated_at' => $item->updatedAt,\n            ], [\n                'name' => $name,\n            ])->execute();\n\n        $this->invalidateCache();\n\n        return true;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function addRule($rule)\n    {\n        $time = time();\n        if ($rule->createdAt === null) {\n            $rule->createdAt = $time;\n        }\n        if ($rule->updatedAt === null) {\n            $rule->updatedAt = $time;\n        }\n        $this->db->createCommand()\n            ->insert($this->ruleTable, [\n                'name' => $rule->name,\n                'data' => serialize($rule),\n                'created_at' => $rule->createdAt,\n                'updated_at' => $rule->updatedAt,\n            ])->execute();\n\n        $this->invalidateCache();\n\n        return true;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function updateRule($name, $rule)\n    {\n        if ($rule->name !== $name && !$this->supportsCascadeUpdate()) {\n            $this->db->createCommand()\n                ->update($this->itemTable, ['rule_name' => $rule->name], ['rule_name' => $name])\n                ->execute();\n        }\n\n        $rule->updatedAt = time();\n\n        $this->db->createCommand()\n            ->update($this->ruleTable, [\n                'name' => $rule->name,\n                'data' => serialize($rule),\n                'updated_at' => $rule->updatedAt,\n            ], [\n                'name' => $name,\n            ])->execute();\n\n        $this->invalidateCache();\n\n        return true;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function removeRule($rule)\n    {\n        if (!$this->supportsCascadeUpdate()) {\n            $this->db->createCommand()\n                ->update($this->itemTable, ['rule_name' => null], ['rule_name' => $rule->name])\n                ->execute();\n        }\n\n        $this->db->createCommand()\n            ->delete($this->ruleTable, ['name' => $rule->name])\n            ->execute();\n\n        $this->invalidateCache();\n\n        return true;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function getItems($type)\n    {\n        $query = (new Query())\n            ->from($this->itemTable)\n            ->where(['type' => $type]);\n\n        $items = [];\n        foreach ($query->all($this->db) as $row) {\n            /** @var Role|Permission $item */\n            $item = $this->populateItem($row);\n            $items[$row['name']] = $item;\n        }\n\n        return $items;\n    }\n\n    /**\n     * Populates an auth item with the data fetched from database.\n     * @param array $row the data from the auth item table\n     * @return Item the populated auth item instance (either Role or Permission)\n     */\n    protected function populateItem($row)\n    {\n        $class = $row['type'] == Item::TYPE_PERMISSION ? Permission::className() : Role::className();\n\n        if (!isset($row['data']) || ($data = @unserialize(is_resource($row['data']) ? stream_get_contents($row['data']) : $row['data'])) === false) {\n            $data = null;\n        }\n\n        return new $class([\n            'name' => $row['name'],\n            'type' => $row['type'],\n            'description' => $row['description'],\n            'ruleName' => $row['rule_name'] ?: null,\n            'data' => $data,\n            'createdAt' => $row['created_at'],\n            'updatedAt' => $row['updated_at'],\n        ]);\n    }\n\n    /**\n     * {@inheritdoc}\n     * The roles returned by this method include the roles assigned via [[$defaultRoles]].\n     */\n    public function getRolesByUser($userId)\n    {\n        if ($this->isEmptyUserId($userId)) {\n            return [];\n        }\n\n        if ($this->cache !== null) {\n            $data = $this->cache->get($this->getUserRolesCacheKey($userId));\n\n            if ($data !== false) {\n                return $data;\n            }\n        }\n\n        $query = (new Query())->select('b.*')\n            ->from(['a' => $this->assignmentTable, 'b' => $this->itemTable])\n            ->where('{{a}}.[[item_name]]={{b}}.[[name]]')\n            ->andWhere(['a.user_id' => (string) $userId])\n            ->andWhere(['b.type' => Item::TYPE_ROLE]);\n\n        $roles = $this->getDefaultRoleInstances();\n        foreach ($query->all($this->db) as $row) {\n            /** @var Role $role */\n            $role = $this->populateItem($row);\n            $roles[$row['name']] = $role;\n        }\n\n        if ($this->cache !== null) {\n            $this->cacheUserRolesData($userId, $roles);\n        }\n\n        return $roles;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getChildRoles($roleName)\n    {\n        $role = $this->getRole($roleName);\n\n        if ($role === null) {\n            throw new InvalidArgumentException(\"Role \\\"$roleName\\\" not found.\");\n        }\n\n        $result = [];\n        $this->getChildrenRecursive($roleName, $this->getChildrenList(), $result);\n\n        $roles = [$roleName => $role];\n\n        $roles += array_filter($this->getRoles(), function (Role $roleItem) use ($result) {\n            return array_key_exists($roleItem->name, $result);\n        });\n\n        return $roles;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getPermissionsByRole($roleName)\n    {\n        $childrenList = $this->getChildrenList();\n        $result = [];\n        $this->getChildrenRecursive($roleName, $childrenList, $result);\n        if (empty($result)) {\n            return [];\n        }\n        $query = (new Query())->from($this->itemTable)->where([\n            'type' => Item::TYPE_PERMISSION,\n            'name' => array_keys($result),\n        ]);\n        $permissions = [];\n        foreach ($query->all($this->db) as $row) {\n            /** @var Permission $permission */\n            $permission = $this->populateItem($row);\n            $permissions[$row['name']] = $permission;\n        }\n\n        return $permissions;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getPermissionsByUser($userId)\n    {\n        if ($this->isEmptyUserId($userId)) {\n            return [];\n        }\n\n        $directPermission = $this->getDirectPermissionsByUser($userId);\n        $inheritedPermission = $this->getInheritedPermissionsByUser($userId);\n\n        return array_merge($directPermission, $inheritedPermission);\n    }\n\n    /**\n     * Returns all permissions that are directly assigned to user.\n     * @param string|int $userId the user ID (see [[\\yii\\web\\User::id]])\n     * @return Permission[] all direct permissions that the user has. The array is indexed by the permission names.\n     * @since 2.0.7\n     */\n    protected function getDirectPermissionsByUser($userId)\n    {\n        $query = (new Query())->select('b.*')\n            ->from(['a' => $this->assignmentTable, 'b' => $this->itemTable])\n            ->where('{{a}}.[[item_name]]={{b}}.[[name]]')\n            ->andWhere(['a.user_id' => (string) $userId])\n            ->andWhere(['b.type' => Item::TYPE_PERMISSION]);\n\n        $permissions = [];\n        foreach ($query->all($this->db) as $row) {\n            /** @var Permission $permission */\n            $permission = $this->populateItem($row);\n            $permissions[$row['name']] = $permission;\n        }\n\n        return $permissions;\n    }\n\n    /**\n     * Returns all permissions that the user inherits from the roles assigned to him.\n     * @param string|int $userId the user ID (see [[\\yii\\web\\User::id]])\n     * @return Permission[] all inherited permissions that the user has. The array is indexed by the permission names.\n     * @since 2.0.7\n     */\n    protected function getInheritedPermissionsByUser($userId)\n    {\n        $query = (new Query())->select('item_name')\n            ->from($this->assignmentTable)\n            ->where(['user_id' => (string) $userId]);\n\n        $childrenList = $this->getChildrenList();\n        $result = [];\n        foreach ($query->column($this->db) as $roleName) {\n            $this->getChildrenRecursive($roleName, $childrenList, $result);\n        }\n\n        if (empty($result)) {\n            return [];\n        }\n\n        $query = (new Query())->from($this->itemTable)->where([\n            'type' => Item::TYPE_PERMISSION,\n            'name' => array_keys($result),\n        ]);\n        $permissions = [];\n        foreach ($query->all($this->db) as $row) {\n            /** @var Permission $permission */\n            $permission = $this->populateItem($row);\n            $permissions[$row['name']] = $permission;\n        }\n\n        return $permissions;\n    }\n\n    /**\n     * Returns the children for every parent.\n     * @return array the children list. Each array key is a parent item name,\n     * and the corresponding array value is a list of child item names.\n     */\n    protected function getChildrenList()\n    {\n        $query = (new Query())->from($this->itemChildTable);\n        $parents = [];\n        foreach ($query->all($this->db) as $row) {\n            $parents[$row['parent']][] = $row['child'];\n        }\n\n        return $parents;\n    }\n\n    /**\n     * Recursively finds all children and grand children of the specified item.\n     * @param string $name the name of the item whose children are to be looked for.\n     * @param array $childrenList the child list built via [[getChildrenList()]]\n     * @param array $result the children and grand children (in array keys)\n     */\n    protected function getChildrenRecursive($name, $childrenList, &$result)\n    {\n        if (isset($childrenList[$name])) {\n            foreach ($childrenList[$name] as $child) {\n                $result[$child] = true;\n                $this->getChildrenRecursive($child, $childrenList, $result);\n            }\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getRule($name)\n    {\n        if ($this->rules !== null) {\n            return isset($this->rules[$name]) ? $this->rules[$name] : null;\n        }\n\n        $row = (new Query())->select(['data'])\n            ->from($this->ruleTable)\n            ->where(['name' => $name])\n            ->one($this->db);\n        if ($row === false) {\n            return null;\n        }\n        $data = $row['data'];\n        if (is_resource($data)) {\n            $data = stream_get_contents($data);\n        }\n        if (!$data) {\n            return null;\n        }\n        return unserialize($data);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getRules()\n    {\n        if ($this->rules !== null) {\n            return $this->rules;\n        }\n\n        $query = (new Query())->from($this->ruleTable);\n\n        $rules = [];\n        foreach ($query->all($this->db) as $row) {\n            $data = $row['data'];\n            if (is_resource($data)) {\n                $data = stream_get_contents($data);\n            }\n            if ($data) {\n                $rules[$row['name']] = unserialize($data);\n            }\n        }\n\n        return $rules;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getAssignment($roleName, $userId)\n    {\n        if ($this->isEmptyUserId($userId)) {\n            return null;\n        }\n\n        $row = (new Query())->from($this->assignmentTable)\n            ->where(['user_id' => (string) $userId, 'item_name' => $roleName])\n            ->one($this->db);\n\n        if ($row === false) {\n            return null;\n        }\n\n        return new Assignment([\n            'userId' => $row['user_id'],\n            'roleName' => $row['item_name'],\n            'createdAt' => $row['created_at'],\n        ]);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getAssignments($userId)\n    {\n        if ($this->isEmptyUserId($userId)) {\n            return [];\n        }\n\n        $query = (new Query())\n            ->from($this->assignmentTable)\n            ->where(['user_id' => (string) $userId]);\n\n        $assignments = [];\n        foreach ($query->all($this->db) as $row) {\n            $assignments[$row['item_name']] = new Assignment([\n                'userId' => $row['user_id'],\n                'roleName' => $row['item_name'],\n                'createdAt' => $row['created_at'],\n            ]);\n        }\n\n        return $assignments;\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.8\n     */\n    public function canAddChild($parent, $child)\n    {\n        return !$this->detectLoop($parent, $child);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function addChild($parent, $child)\n    {\n        if ($parent->name === $child->name) {\n            throw new InvalidArgumentException(\"Cannot add '{$parent->name}' as a child of itself.\");\n        }\n\n        if ($parent instanceof Permission && $child instanceof Role) {\n            throw new InvalidArgumentException('Cannot add a role as a child of a permission.');\n        }\n\n        if ($this->detectLoop($parent, $child)) {\n            throw new InvalidCallException(\"Cannot add '{$child->name}' as a child of '{$parent->name}'. A loop has been detected.\");\n        }\n\n        $this->db->createCommand()\n            ->insert($this->itemChildTable, ['parent' => $parent->name, 'child' => $child->name])\n            ->execute();\n\n        $this->invalidateCache();\n\n        return true;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function removeChild($parent, $child)\n    {\n        $result = $this->db->createCommand()\n            ->delete($this->itemChildTable, ['parent' => $parent->name, 'child' => $child->name])\n            ->execute() > 0;\n\n        $this->invalidateCache();\n\n        return $result;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function removeChildren($parent)\n    {\n        $result = $this->db->createCommand()\n            ->delete($this->itemChildTable, ['parent' => $parent->name])\n            ->execute() > 0;\n\n        $this->invalidateCache();\n\n        return $result;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function hasChild($parent, $child)\n    {\n        return (new Query())\n            ->from($this->itemChildTable)\n            ->where(['parent' => $parent->name, 'child' => $child->name])\n            ->one($this->db) !== false;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getChildren($name)\n    {\n        $query = (new Query())\n            ->select(['name', 'type', 'description', 'rule_name', 'data', 'created_at', 'updated_at'])\n            ->from([$this->itemTable, $this->itemChildTable])\n            ->where(['parent' => $name, 'name' => new Expression('[[child]]')]);\n\n        $children = [];\n        foreach ($query->all($this->db) as $row) {\n            $children[$row['name']] = $this->populateItem($row);\n        }\n\n        return $children;\n    }\n\n    /**\n     * Checks whether there is a loop in the authorization item hierarchy.\n     * @param Item $parent the parent item\n     * @param Item $child the child item to be added to the hierarchy\n     * @return bool whether a loop exists\n     */\n    protected function detectLoop($parent, $child)\n    {\n        if ($child->name === $parent->name) {\n            return true;\n        }\n        foreach ($this->getChildren($child->name) as $grandchild) {\n            if ($this->detectLoop($parent, $grandchild)) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function assign($role, $userId)\n    {\n        $assignment = new Assignment([\n            'userId' => $userId,\n            'roleName' => $role->name,\n            'createdAt' => time(),\n        ]);\n\n        $this->db->createCommand()\n            ->insert($this->assignmentTable, [\n                'user_id' => $assignment->userId,\n                'item_name' => $assignment->roleName,\n                'created_at' => $assignment->createdAt,\n            ])->execute();\n\n        unset($this->checkAccessAssignments[(string) $userId]);\n\n        $this->invalidateCache();\n\n        return $assignment;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function revoke($role, $userId)\n    {\n        if ($this->isEmptyUserId($userId)) {\n            return false;\n        }\n\n        unset($this->checkAccessAssignments[(string) $userId]);\n        $result = $this->db->createCommand()\n            ->delete($this->assignmentTable, ['user_id' => (string) $userId, 'item_name' => $role->name])\n            ->execute() > 0;\n\n        $this->invalidateCache();\n\n        return $result;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function revokeAll($userId)\n    {\n        if ($this->isEmptyUserId($userId)) {\n            return false;\n        }\n\n        unset($this->checkAccessAssignments[(string) $userId]);\n        $result = $this->db->createCommand()\n            ->delete($this->assignmentTable, ['user_id' => (string) $userId])\n            ->execute() > 0;\n\n        $this->invalidateCache();\n\n        return $result;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function removeAll()\n    {\n        $this->removeAllAssignments();\n        $this->db->createCommand()->delete($this->itemChildTable)->execute();\n        $this->db->createCommand()->delete($this->itemTable)->execute();\n        $this->db->createCommand()->delete($this->ruleTable)->execute();\n        $this->invalidateCache();\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function removeAllPermissions()\n    {\n        $this->removeAllItems(Item::TYPE_PERMISSION);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function removeAllRoles()\n    {\n        $this->removeAllItems(Item::TYPE_ROLE);\n    }\n\n    /**\n     * Removes all auth items of the specified type.\n     * @param int $type the auth item type (either Item::TYPE_PERMISSION or Item::TYPE_ROLE)\n     */\n    protected function removeAllItems($type)\n    {\n        if (!$this->supportsCascadeUpdate()) {\n            $names = (new Query())\n                ->select(['name'])\n                ->from($this->itemTable)\n                ->where(['type' => $type])\n                ->column($this->db);\n            if (empty($names)) {\n                return;\n            }\n            $key = $type == Item::TYPE_PERMISSION ? 'child' : 'parent';\n            $this->db->createCommand()\n                ->delete($this->itemChildTable, [$key => $names])\n                ->execute();\n            $this->db->createCommand()\n                ->delete($this->assignmentTable, ['item_name' => $names])\n                ->execute();\n        }\n        $this->db->createCommand()\n            ->delete($this->itemTable, ['type' => $type])\n            ->execute();\n\n        $this->invalidateCache();\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function removeAllRules()\n    {\n        if (!$this->supportsCascadeUpdate()) {\n            $this->db->createCommand()\n                ->update($this->itemTable, ['rule_name' => null])\n                ->execute();\n        }\n\n        $this->db->createCommand()->delete($this->ruleTable)->execute();\n\n        $this->invalidateCache();\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function removeAllAssignments()\n    {\n        $this->checkAccessAssignments = [];\n        $this->db->createCommand()->delete($this->assignmentTable)->execute();\n    }\n\n    public function invalidateCache()\n    {\n        if ($this->cache !== null) {\n            $this->cache->delete($this->cacheKey);\n            $this->items = null;\n            $this->rules = null;\n            $this->parents = null;\n\n            $cachedUserIds = $this->cache->get($this->getUserRolesCachedSetKey());\n\n            if ($cachedUserIds !== false) {\n                foreach ($cachedUserIds as $userId) {\n                    $this->cache->delete($this->getUserRolesCacheKey($userId));\n                }\n\n                $this->cache->delete($this->getUserRolesCachedSetKey());\n            }\n        }\n        $this->checkAccessAssignments = [];\n    }\n\n    public function loadFromCache()\n    {\n        if ($this->items !== null || !$this->cache instanceof CacheInterface) {\n            return;\n        }\n\n        $data = $this->cache->get($this->cacheKey);\n        if (is_array($data) && isset($data[0], $data[1], $data[2])) {\n            list($this->items, $this->rules, $this->parents) = $data;\n            return;\n        }\n\n        $query = (new Query())->from($this->itemTable);\n        $this->items = [];\n        foreach ($query->all($this->db) as $row) {\n            $this->items[$row['name']] = $this->populateItem($row);\n        }\n\n        $query = (new Query())->from($this->ruleTable);\n        $this->rules = [];\n        foreach ($query->all($this->db) as $row) {\n            $data = $row['data'];\n            if (is_resource($data)) {\n                $data = stream_get_contents($data);\n            }\n            if ($data) {\n                $this->rules[$row['name']] = unserialize($data);\n            }\n        }\n\n        $query = (new Query())->from($this->itemChildTable);\n        $this->parents = [];\n        foreach ($query->all($this->db) as $row) {\n            if (isset($this->items[$row['child']])) {\n                $this->parents[$row['child']][] = $row['parent'];\n            }\n        }\n\n        $this->cache->set($this->cacheKey, [$this->items, $this->rules, $this->parents]);\n    }\n\n    /**\n     * Returns all role assignment information for the specified role.\n     * @param string $roleName\n     * @return string[] the ids. An empty array will be\n     * returned if role is not assigned to any user.\n     * @since 2.0.7\n     */\n    public function getUserIdsByRole($roleName)\n    {\n        if (empty($roleName)) {\n            return [];\n        }\n\n        return (new Query())->select('[[user_id]]')\n            ->from($this->assignmentTable)\n            ->where(['item_name' => $roleName])->column($this->db);\n    }\n\n    /**\n     * Check whether $userId is empty.\n     * @param mixed $userId\n     * @return bool\n     * @since 2.0.26\n     */\n    protected function isEmptyUserId($userId)\n    {\n        return !isset($userId) || $userId === '';\n    }\n\n    private function getUserRolesCacheKey($userId)\n    {\n        return $this->cacheKey . $this->rolesCacheSuffix . $userId;\n    }\n\n    private function getUserRolesCachedSetKey()\n    {\n        return $this->cacheKey . $this->rolesCacheSuffix;\n    }\n\n    private function cacheUserRolesData($userId, $roles)\n    {\n        $cachedUserIds = $this->cache->get($this->getUserRolesCachedSetKey());\n\n        if ($cachedUserIds === false) {\n            $cachedUserIds = [];\n        }\n\n        $cachedUserIds[] = $userId;\n\n        $this->cache->set($this->getUserRolesCacheKey($userId), $roles);\n        $this->cache->set($this->getUserRolesCachedSetKey(), $cachedUserIds);\n    }\n}\n"
  },
  {
    "path": "framework/rbac/Item.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\rbac;\n\nuse yii\\base\\BaseObject;\n\n/**\n * For more details and usage information on Item, see the [guide article on security authorization](guide:security-authorization).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Item extends BaseObject\n{\n    public const TYPE_ROLE = 1;\n    public const TYPE_PERMISSION = 2;\n    /**\n     * @var int the type of the item. This should be either [[TYPE_ROLE]] or [[TYPE_PERMISSION]].\n     */\n    public $type;\n    /**\n     * @var string the name of the item. This must be globally unique.\n     */\n    public $name;\n    /**\n     * @var string the item description\n     */\n    public $description;\n    /**\n     * @var string|null name of the rule associated with this item\n     */\n    public $ruleName;\n    /**\n     * @var mixed the additional data associated with this item\n     */\n    public $data;\n    /**\n     * @var int UNIX timestamp representing the item creation time\n     */\n    public $createdAt;\n    /**\n     * @var int UNIX timestamp representing the item updating time\n     */\n    public $updatedAt;\n}\n"
  },
  {
    "path": "framework/rbac/ManagerInterface.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\rbac;\n\n/**\n * For more details and usage information on ManagerInterface, see the [guide article on security authorization](guide:security-authorization).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\ninterface ManagerInterface extends CheckAccessInterface\n{\n    /**\n     * Creates a new Role object.\n     * Note that the newly created role is not added to the RBAC system yet.\n     * You must fill in the needed data and call [[add()]] to add it to the system.\n     * @param string $name the role name\n     * @return Role the new Role object\n     */\n    public function createRole($name);\n\n    /**\n     * Creates a new Permission object.\n     * Note that the newly created permission is not added to the RBAC system yet.\n     * You must fill in the needed data and call [[add()]] to add it to the system.\n     * @param string $name the permission name\n     * @return Permission the new Permission object\n     */\n    public function createPermission($name);\n\n    /**\n     * Adds a role, permission or rule to the RBAC system.\n     * @param Role|Permission|Rule $object\n     * @return bool whether the role, permission or rule is successfully added to the system\n     * @throws \\Exception if data validation or saving fails (such as the name of the role or permission is not unique)\n     */\n    public function add($object);\n\n    /**\n     * Removes a role, permission or rule from the RBAC system.\n     * @param Role|Permission|Rule $object\n     * @return bool whether the role, permission or rule is successfully removed\n     */\n    public function remove($object);\n\n    /**\n     * Updates the specified role, permission or rule in the system.\n     * @param string $name the old name of the role, permission or rule\n     * @param Role|Permission|Rule $object\n     * @return bool whether the update is successful\n     * @throws \\Exception if data validation or saving fails (such as the name of the role or permission is not unique)\n     */\n    public function update($name, $object);\n\n    /**\n     * Returns the named role.\n     * @param string $name the role name.\n     * @return Role|null the role corresponding to the specified name. Null is returned if no such role.\n     */\n    public function getRole($name);\n\n    /**\n     * Returns all roles in the system.\n     * @return Role[] all roles in the system. The array is indexed by the role names.\n     */\n    public function getRoles();\n\n    /**\n     * Returns the roles that are assigned to the user via [[assign()]].\n     * Note that child roles that are not assigned directly to the user will not be returned.\n     * @param string|int $userId the user ID (see [[\\yii\\web\\User::id]])\n     * @return Role[] all roles directly assigned to the user. The array is indexed by the role names.\n     */\n    public function getRolesByUser($userId);\n\n    /**\n     * Returns child roles of the role specified. Depth isn't limited.\n     * @param string $roleName name of the role to file child roles for\n     * @return Role[] Child roles. The array is indexed by the role names.\n     * First element is an instance of the parent Role itself.\n     * @throws \\yii\\base\\InvalidParamException if Role was not found that are getting by $roleName\n     * @since 2.0.10\n     */\n    public function getChildRoles($roleName);\n\n    /**\n     * Returns the named permission.\n     * @param string $name the permission name.\n     * @return Permission|null the permission corresponding to the specified name. Null is returned if no such permission.\n     */\n    public function getPermission($name);\n\n    /**\n     * Returns all permissions in the system.\n     * @return Permission[] all permissions in the system. The array is indexed by the permission names.\n     */\n    public function getPermissions();\n\n    /**\n     * Returns all permissions that the specified role represents.\n     * @param string $roleName the role name\n     * @return Permission[] all permissions that the role represents. The array is indexed by the permission names.\n     */\n    public function getPermissionsByRole($roleName);\n\n    /**\n     * Returns all permissions that the user has.\n     * @param string|int $userId the user ID (see [[\\yii\\web\\User::id]])\n     * @return Permission[] all permissions that the user has. The array is indexed by the permission names.\n     */\n    public function getPermissionsByUser($userId);\n\n    /**\n     * Returns the rule of the specified name.\n     * @param string $name the rule name\n     * @return Rule|null the rule object, or null if the specified name does not correspond to a rule.\n     */\n    public function getRule($name);\n\n    /**\n     * Returns all rules available in the system.\n     * @return Rule[] the rules indexed by the rule names\n     */\n    public function getRules();\n\n    /**\n     * Checks the possibility of adding a child to parent.\n     * @param Item $parent the parent item\n     * @param Item $child the child item to be added to the hierarchy\n     * @return bool possibility of adding\n     *\n     * @since 2.0.8\n     */\n    public function canAddChild($parent, $child);\n\n    /**\n     * Adds an item as a child of another item.\n     * @param Item $parent\n     * @param Item $child\n     * @return bool whether the child successfully added\n     * @throws \\yii\\base\\Exception if the parent-child relationship already exists or if a loop has been detected.\n     */\n    public function addChild($parent, $child);\n\n    /**\n     * Removes a child from its parent.\n     * Note, the child item is not deleted. Only the parent-child relationship is removed.\n     * @param Item $parent\n     * @param Item $child\n     * @return bool whether the removal is successful\n     */\n    public function removeChild($parent, $child);\n\n    /**\n     * Removed all children form their parent.\n     * Note, the children items are not deleted. Only the parent-child relationships are removed.\n     * @param Item $parent\n     * @return bool whether the removal is successful\n     */\n    public function removeChildren($parent);\n\n    /**\n     * Returns a value indicating whether the child already exists for the parent.\n     * @param Item $parent\n     * @param Item $child\n     * @return bool whether `$child` is already a child of `$parent`\n     */\n    public function hasChild($parent, $child);\n\n    /**\n     * Returns the child permissions and/or roles.\n     * @param string $name the parent name\n     * @return Item[] the child permissions and/or roles\n     */\n    public function getChildren($name);\n\n    /**\n     * Assigns a role to a user.\n     *\n     * @param Role|Permission $role\n     * @param string|int $userId the user ID (see [[\\yii\\web\\User::id]])\n     * @return Assignment the role assignment information.\n     * @throws \\Exception if the role has already been assigned to the user\n     */\n    public function assign($role, $userId);\n\n    /**\n     * Revokes a role from a user.\n     * @param Role|Permission $role\n     * @param string|int $userId the user ID (see [[\\yii\\web\\User::id]])\n     * @return bool whether the revoking is successful\n     */\n    public function revoke($role, $userId);\n\n    /**\n     * Revokes all roles from a user.\n     * @param mixed $userId the user ID (see [[\\yii\\web\\User::id]])\n     * @return bool whether the revoking is successful\n     */\n    public function revokeAll($userId);\n\n    /**\n     * Returns the assignment information regarding a role and a user.\n     * @param string $roleName the role name\n     * @param string|int $userId the user ID (see [[\\yii\\web\\User::id]])\n     * @return Assignment|null the assignment information. Null is returned if\n     * the role is not assigned to the user.\n     */\n    public function getAssignment($roleName, $userId);\n\n    /**\n     * Returns all role assignment information for the specified user.\n     * @param string|int $userId the user ID (see [[\\yii\\web\\User::id]])\n     * @return Assignment[] the assignments indexed by role names. An empty array will be\n     * returned if there is no role assigned to the user.\n     */\n    public function getAssignments($userId);\n\n    /**\n     * Returns all user IDs assigned to the role specified.\n     * @param string $roleName\n     * @return array array of user ID strings\n     * @since 2.0.7\n     */\n    public function getUserIdsByRole($roleName);\n\n    /**\n     * Removes all authorization data, including roles, permissions, rules, and assignments.\n     */\n    public function removeAll();\n\n    /**\n     * Removes all permissions.\n     * All parent child relations will be adjusted accordingly.\n     */\n    public function removeAllPermissions();\n\n    /**\n     * Removes all roles.\n     * All parent child relations will be adjusted accordingly.\n     */\n    public function removeAllRoles();\n\n    /**\n     * Removes all rules.\n     * All roles and permissions which have rules will be adjusted accordingly.\n     */\n    public function removeAllRules();\n\n    /**\n     * Removes all role assignments.\n     */\n    public function removeAllAssignments();\n}\n"
  },
  {
    "path": "framework/rbac/Permission.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\rbac;\n\n/**\n * For more details and usage information on Permission, see the [guide article on security authorization](guide:security-authorization).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Permission extends Item\n{\n    /**\n     * {@inheritdoc}\n     */\n    public $type = self::TYPE_PERMISSION;\n}\n"
  },
  {
    "path": "framework/rbac/PhpManager.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\rbac;\n\nuse Yii;\nuse yii\\base\\InvalidArgumentException;\nuse yii\\base\\InvalidCallException;\nuse yii\\helpers\\VarDumper;\n\n/**\n * PhpManager represents an authorization manager that stores authorization\n * information in terms of a PHP script file.\n *\n * The authorization data will be saved to and loaded from three files\n * specified by [[itemFile]], [[assignmentFile]] and [[ruleFile]].\n *\n * PhpManager is mainly suitable for authorization data that is not too big\n * (for example, the authorization data for a personal blog system).\n * Use [[DbManager]] for more complex authorization data.\n *\n * Note that PhpManager is not compatible with facebooks [HHVM](https://hhvm.com/) because\n * it relies on writing php files and including them afterwards which is not supported by HHVM.\n *\n * For more details and usage information on PhpManager, see the [guide article on security authorization](guide:security-authorization).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Alexander Kochetov <creocoder@gmail.com>\n * @author Christophe Boulain <christophe.boulain@gmail.com>\n * @author Alexander Makarov <sam@rmcreative.ru>\n * @since 2.0\n */\nclass PhpManager extends BaseManager\n{\n    /**\n     * @var string the path of the PHP script that contains the authorization items.\n     * This can be either a file path or a [path alias](guide:concept-aliases) to the file.\n     * Make sure this file is writable by the Web server process if the authorization needs to be changed online.\n     * @see loadFromFile()\n     * @see saveToFile()\n     */\n    public $itemFile = '@app/rbac/items.php';\n    /**\n     * @var string the path of the PHP script that contains the authorization assignments.\n     * This can be either a file path or a [path alias](guide:concept-aliases) to the file.\n     * Make sure this file is writable by the Web server process if the authorization needs to be changed online.\n     * @see loadFromFile()\n     * @see saveToFile()\n     */\n    public $assignmentFile = '@app/rbac/assignments.php';\n    /**\n     * @var string the path of the PHP script that contains the authorization rules.\n     * This can be either a file path or a [path alias](guide:concept-aliases) to the file.\n     * Make sure this file is writable by the Web server process if the authorization needs to be changed online.\n     * @see loadFromFile()\n     * @see saveToFile()\n     */\n    public $ruleFile = '@app/rbac/rules.php';\n\n    /**\n     * @var Item[]\n     */\n    protected $items = []; // itemName => item\n    /**\n     * @var array\n     */\n    protected $children = []; // itemName, childName => child\n    /**\n     * @var array\n     */\n    protected $assignments = []; // userId, itemName => assignment\n    /**\n     * @var Rule[]\n     */\n    protected $rules = []; // ruleName => rule\n\n\n    /**\n     * Initializes the application component.\n     * This method overrides parent implementation by loading the authorization data\n     * from PHP script.\n     */\n    public function init()\n    {\n        parent::init();\n        $this->itemFile = Yii::getAlias($this->itemFile);\n        $this->assignmentFile = Yii::getAlias($this->assignmentFile);\n        $this->ruleFile = Yii::getAlias($this->ruleFile);\n        $this->load();\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function checkAccess($userId, $permissionName, $params = [])\n    {\n        $assignments = $this->getAssignments($userId);\n\n        if ($this->hasNoAssignments($assignments)) {\n            return false;\n        }\n\n        return $this->checkAccessRecursive($userId, $permissionName, $params, $assignments);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getAssignments($userId)\n    {\n        // using null as an array offset is deprecated in PHP `8.5`\n        if ($userId !== null && isset($this->assignments[$userId])) {\n            return $this->assignments[$userId];\n        }\n\n        return [];\n    }\n\n    /**\n     * Performs access check for the specified user.\n     * This method is internally called by [[checkAccess()]].\n     *\n     * @param string|int $user the user ID. This should can be either an integer or a string representing\n     * the unique identifier of a user. See [[\\yii\\web\\User::id]].\n     * @param string $itemName the name of the operation that need access check\n     * @param array $params name-value pairs that would be passed to rules associated\n     * with the tasks and roles assigned to the user. A param with name 'user' is added to this array,\n     * which holds the value of `$userId`.\n     * @param Assignment[] $assignments the assignments to the specified user\n     * @return bool whether the operations can be performed by the user.\n     */\n    protected function checkAccessRecursive($user, $itemName, $params, $assignments)\n    {\n        if (!isset($this->items[$itemName])) {\n            return false;\n        }\n\n        /** @var Item $item */\n        $item = $this->items[$itemName];\n        Yii::debug($item instanceof Role ? \"Checking role: $itemName\" : \"Checking permission : $itemName\", __METHOD__);\n\n        if (!$this->executeRule($user, $item, $params)) {\n            return false;\n        }\n\n        if (isset($assignments[$itemName]) || in_array($itemName, $this->defaultRoles)) {\n            return true;\n        }\n\n        foreach ($this->children as $parentName => $children) {\n            if (isset($children[$itemName]) && $this->checkAccessRecursive($user, $parentName, $params, $assignments)) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.8\n     */\n    public function canAddChild($parent, $child)\n    {\n        return !$this->detectLoop($parent, $child);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function addChild($parent, $child)\n    {\n        if (!isset($this->items[$parent->name], $this->items[$child->name])) {\n            throw new InvalidArgumentException(\"Either '{$parent->name}' or '{$child->name}' does not exist.\");\n        }\n\n        if ($parent->name === $child->name) {\n            throw new InvalidArgumentException(\"Cannot add '{$parent->name} ' as a child of itself.\");\n        }\n        if ($parent instanceof Permission && $child instanceof Role) {\n            throw new InvalidArgumentException('Cannot add a role as a child of a permission.');\n        }\n\n        if ($this->detectLoop($parent, $child)) {\n            throw new InvalidCallException(\"Cannot add '{$child->name}' as a child of '{$parent->name}'. A loop has been detected.\");\n        }\n        if (isset($this->children[$parent->name][$child->name])) {\n            throw new InvalidCallException(\"The item '{$parent->name}' already has a child '{$child->name}'.\");\n        }\n        $this->children[$parent->name][$child->name] = $this->items[$child->name];\n        $this->saveItems();\n\n        return true;\n    }\n\n    /**\n     * Checks whether there is a loop in the authorization item hierarchy.\n     *\n     * @param Item $parent parent item\n     * @param Item $child the child item that is to be added to the hierarchy\n     * @return bool whether a loop exists\n     */\n    protected function detectLoop($parent, $child)\n    {\n        if ($child->name === $parent->name) {\n            return true;\n        }\n        if (!isset($this->children[$child->name], $this->items[$parent->name])) {\n            return false;\n        }\n        foreach ($this->children[$child->name] as $grandchild) {\n            /** @var Item $grandchild */\n            if ($this->detectLoop($parent, $grandchild)) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function removeChild($parent, $child)\n    {\n        if (isset($this->children[$parent->name][$child->name])) {\n            unset($this->children[$parent->name][$child->name]);\n            $this->saveItems();\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function removeChildren($parent)\n    {\n        if (isset($this->children[$parent->name])) {\n            unset($this->children[$parent->name]);\n            $this->saveItems();\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function hasChild($parent, $child)\n    {\n        return isset($this->children[$parent->name][$child->name]);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function assign($role, $userId)\n    {\n        if (!isset($this->items[$role->name])) {\n            throw new InvalidArgumentException(\"Unknown role '{$role->name}'.\");\n        } elseif (isset($this->assignments[$userId][$role->name])) {\n            throw new InvalidArgumentException(\"Authorization item '{$role->name}' has already been assigned to user '$userId'.\");\n        }\n\n        $this->assignments[$userId][$role->name] = new Assignment([\n            'userId' => $userId,\n            'roleName' => $role->name,\n            'createdAt' => time(),\n        ]);\n        $this->saveAssignments();\n\n        return $this->assignments[$userId][$role->name];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function revoke($role, $userId)\n    {\n        if (isset($this->assignments[$userId][$role->name])) {\n            unset($this->assignments[$userId][$role->name]);\n            $this->saveAssignments();\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function revokeAll($userId)\n    {\n        if (isset($this->assignments[$userId]) && is_array($this->assignments[$userId])) {\n            foreach ($this->assignments[$userId] as $itemName => $value) {\n                unset($this->assignments[$userId][$itemName]);\n            }\n            $this->saveAssignments();\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getAssignment($roleName, $userId)\n    {\n        return isset($this->assignments[$userId][$roleName]) ? $this->assignments[$userId][$roleName] : null;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getItems($type)\n    {\n        $items = [];\n\n        foreach ($this->items as $name => $item) {\n            /** @var Role|Permission $item */\n            if ($item->type == $type) {\n                $items[$name] = $item;\n            }\n        }\n\n        return $items;\n    }\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function removeItem($item)\n    {\n        if (isset($this->items[$item->name])) {\n            foreach ($this->children as &$children) {\n                unset($children[$item->name]);\n            }\n            foreach ($this->assignments as &$assignments) {\n                unset($assignments[$item->name]);\n            }\n            unset($this->items[$item->name]);\n            $this->saveItems();\n            $this->saveAssignments();\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getItem($name)\n    {\n        return isset($this->items[$name]) ? $this->items[$name] : null;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function updateRule($name, $rule)\n    {\n        if ($rule->name !== $name) {\n            unset($this->rules[$name]);\n        }\n        $this->rules[$rule->name] = $rule;\n        $this->saveRules();\n        return true;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getRule($name)\n    {\n        return isset($this->rules[$name]) ? $this->rules[$name] : null;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getRules()\n    {\n        return $this->rules;\n    }\n\n    /**\n     * {@inheritdoc}\n     * The roles returned by this method include the roles assigned via [[$defaultRoles]].\n     */\n    public function getRolesByUser($userId)\n    {\n        $roles = $this->getDefaultRoleInstances();\n        foreach ($this->getAssignments($userId) as $name => $assignment) {\n            $item = $this->items[$assignment->roleName];\n            if ($item->type === Item::TYPE_ROLE) {\n                /** @var Role $item */\n                $roles[$name] = $item;\n            }\n        }\n\n        return $roles;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getChildRoles($roleName)\n    {\n        $role = $this->getRole($roleName);\n\n        if ($role === null) {\n            throw new InvalidArgumentException(\"Role \\\"$roleName\\\" not found.\");\n        }\n\n        $result = [];\n        $this->getChildrenRecursive($roleName, $result);\n\n        $roles = [$roleName => $role];\n\n        $roles += array_filter($this->getRoles(), function (Role $roleItem) use ($result) {\n            return array_key_exists($roleItem->name, $result);\n        });\n\n        return $roles;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getPermissionsByRole($roleName)\n    {\n        $result = [];\n        $this->getChildrenRecursive($roleName, $result);\n        if (empty($result)) {\n            return [];\n        }\n        $permissions = [];\n        foreach (array_keys($result) as $itemName) {\n            if (isset($this->items[$itemName]) && $this->items[$itemName] instanceof Permission) {\n                $permissions[$itemName] = $this->items[$itemName];\n            }\n        }\n\n        return $permissions;\n    }\n\n    /**\n     * Recursively finds all children and grand children of the specified item.\n     *\n     * @param string $name the name of the item whose children are to be looked for.\n     * @param array $result the children and grand children (in array keys)\n     */\n    protected function getChildrenRecursive($name, &$result)\n    {\n        if (isset($this->children[$name])) {\n            foreach ($this->children[$name] as $child) {\n                $result[$child->name] = true;\n                $this->getChildrenRecursive($child->name, $result);\n            }\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getPermissionsByUser($userId)\n    {\n        $directPermission = $this->getDirectPermissionsByUser($userId);\n        $inheritedPermission = $this->getInheritedPermissionsByUser($userId);\n\n        return array_merge($directPermission, $inheritedPermission);\n    }\n\n    /**\n     * Returns all permissions that are directly assigned to user.\n     * @param string|int $userId the user ID (see [[\\yii\\web\\User::id]])\n     * @return Permission[] all direct permissions that the user has. The array is indexed by the permission names.\n     * @since 2.0.7\n     */\n    protected function getDirectPermissionsByUser($userId)\n    {\n        $permissions = [];\n        foreach ($this->getAssignments($userId) as $name => $assignment) {\n            $item = $this->items[$assignment->roleName];\n            if ($item->type === Item::TYPE_PERMISSION) {\n                /** @var Permission $item */\n                $permissions[$name] = $item;\n            }\n        }\n\n        return $permissions;\n    }\n\n    /**\n     * Returns all permissions that the user inherits from the roles assigned to him.\n     * @param string|int $userId the user ID (see [[\\yii\\web\\User::id]])\n     * @return Permission[] all inherited permissions that the user has. The array is indexed by the permission names.\n     * @since 2.0.7\n     */\n    protected function getInheritedPermissionsByUser($userId)\n    {\n        $assignments = $this->getAssignments($userId);\n        $result = [];\n        foreach (array_keys($assignments) as $roleName) {\n            $this->getChildrenRecursive($roleName, $result);\n        }\n\n        if (empty($result)) {\n            return [];\n        }\n\n        $permissions = [];\n        foreach (array_keys($result) as $itemName) {\n            if (isset($this->items[$itemName]) && $this->items[$itemName] instanceof Permission) {\n                $permissions[$itemName] = $this->items[$itemName];\n            }\n        }\n\n        return $permissions;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getChildren($name)\n    {\n        return isset($this->children[$name]) ? $this->children[$name] : [];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function removeAll()\n    {\n        $this->children = [];\n        $this->items = [];\n        $this->assignments = [];\n        $this->rules = [];\n        $this->save();\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function removeAllPermissions()\n    {\n        $this->removeAllItems(Item::TYPE_PERMISSION);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function removeAllRoles()\n    {\n        $this->removeAllItems(Item::TYPE_ROLE);\n    }\n\n    /**\n     * Removes all auth items of the specified type.\n     * @param int $type the auth item type (either Item::TYPE_PERMISSION or Item::TYPE_ROLE)\n     */\n    protected function removeAllItems($type)\n    {\n        $names = [];\n        foreach ($this->items as $name => $item) {\n            if ($item->type == $type) {\n                unset($this->items[$name]);\n                $names[$name] = true;\n            }\n        }\n        if (empty($names)) {\n            return;\n        }\n\n        foreach ($this->assignments as $i => $assignments) {\n            foreach ($assignments as $n => $assignment) {\n                if (isset($names[$assignment->roleName])) {\n                    unset($this->assignments[$i][$n]);\n                }\n            }\n        }\n        foreach ($this->children as $name => $children) {\n            if (isset($names[$name])) {\n                unset($this->children[$name]);\n            } else {\n                foreach ($children as $childName => $item) {\n                    if (isset($names[$childName])) {\n                        unset($children[$childName]);\n                    }\n                }\n                $this->children[$name] = $children;\n            }\n        }\n\n        $this->saveItems();\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function removeAllRules()\n    {\n        foreach ($this->items as $item) {\n            $item->ruleName = null;\n        }\n        $this->rules = [];\n        $this->saveRules();\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function removeAllAssignments()\n    {\n        $this->assignments = [];\n        $this->saveAssignments();\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function removeRule($rule)\n    {\n        if (isset($this->rules[$rule->name])) {\n            unset($this->rules[$rule->name]);\n            foreach ($this->items as $item) {\n                if ($item->ruleName === $rule->name) {\n                    $item->ruleName = null;\n                }\n            }\n            $this->saveRules();\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function addRule($rule)\n    {\n        $this->rules[$rule->name] = $rule;\n        $this->saveRules();\n        return true;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function updateItem($name, $item)\n    {\n        if ($name !== $item->name) {\n            if (isset($this->items[$item->name])) {\n                throw new InvalidArgumentException(\"Unable to change the item name. The name '{$item->name}' is already used by another item.\");\n            }\n\n            // Remove old item in case of renaming\n            unset($this->items[$name]);\n\n            if (isset($this->children[$name])) {\n                $this->children[$item->name] = $this->children[$name];\n                unset($this->children[$name]);\n            }\n            foreach ($this->children as &$children) {\n                if (isset($children[$name])) {\n                    $children[$item->name] = $children[$name];\n                    unset($children[$name]);\n                }\n            }\n            foreach ($this->assignments as &$assignments) {\n                if (isset($assignments[$name])) {\n                    $assignments[$item->name] = $assignments[$name];\n                    $assignments[$item->name]->roleName = $item->name;\n                    unset($assignments[$name]);\n                }\n            }\n            $this->saveAssignments();\n        }\n\n        $this->items[$item->name] = $item;\n\n        $this->saveItems();\n        return true;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function addItem($item)\n    {\n        $time = time();\n        if ($item->createdAt === null) {\n            $item->createdAt = $time;\n        }\n        if ($item->updatedAt === null) {\n            $item->updatedAt = $time;\n        }\n\n        $this->items[$item->name] = $item;\n\n        $this->saveItems();\n\n        return true;\n    }\n\n    /**\n     * Loads authorization data from persistent storage.\n     */\n    protected function load()\n    {\n        $this->children = [];\n        $this->rules = [];\n        $this->assignments = [];\n        $this->items = [];\n\n        $items = $this->loadFromFile($this->itemFile);\n        $itemsMtime = @filemtime($this->itemFile);\n        $assignments = $this->loadFromFile($this->assignmentFile);\n        $assignmentsMtime = @filemtime($this->assignmentFile);\n        $rules = $this->loadFromFile($this->ruleFile);\n\n        foreach ($items as $name => $item) {\n            $class = $item['type'] == Item::TYPE_PERMISSION ? Permission::className() : Role::className();\n\n            $this->items[$name] = new $class([\n                'name' => $name,\n                'description' => isset($item['description']) ? $item['description'] : null,\n                'ruleName' => isset($item['ruleName']) ? $item['ruleName'] : null,\n                'data' => isset($item['data']) ? $item['data'] : null,\n                'createdAt' => $itemsMtime,\n                'updatedAt' => $itemsMtime,\n            ]);\n        }\n\n        foreach ($items as $name => $item) {\n            if (isset($item['children'])) {\n                foreach ($item['children'] as $childName) {\n                    if (isset($this->items[$childName])) {\n                        $this->children[$name][$childName] = $this->items[$childName];\n                    }\n                }\n            }\n        }\n\n        foreach ($assignments as $userId => $roles) {\n            foreach ($roles as $role) {\n                $this->assignments[$userId][$role] = new Assignment([\n                    'userId' => $userId,\n                    'roleName' => $role,\n                    'createdAt' => $assignmentsMtime,\n                ]);\n            }\n        }\n\n        foreach ($rules as $name => $ruleData) {\n            $this->rules[$name] = unserialize($ruleData);\n        }\n    }\n\n    /**\n     * Saves authorization data into persistent storage.\n     */\n    protected function save()\n    {\n        $this->saveItems();\n        $this->saveAssignments();\n        $this->saveRules();\n    }\n\n    /**\n     * Loads the authorization data from a PHP script file.\n     *\n     * @param string $file the file path.\n     * @return array the authorization data\n     * @see saveToFile()\n     */\n    protected function loadFromFile($file)\n    {\n        if (is_file($file)) {\n            return require $file;\n        }\n\n        return [];\n    }\n\n    /**\n     * Saves the authorization data to a PHP script file.\n     *\n     * @param array $data the authorization data\n     * @param string $file the file path.\n     * @see loadFromFile()\n     */\n    protected function saveToFile($data, $file)\n    {\n        file_put_contents($file, \"<?php\\n\\nreturn \" . VarDumper::export($data) . \";\\n\", LOCK_EX);\n        $this->invalidateScriptCache($file);\n    }\n\n    /**\n     * Invalidates precompiled script cache (such as OPCache or APC) for the given file.\n     * @param string $file the file path.\n     * @since 2.0.9\n     */\n    protected function invalidateScriptCache($file)\n    {\n        if (function_exists('opcache_invalidate')) {\n            opcache_invalidate($file, true);\n        }\n        if (function_exists('apc_delete_file')) {\n            @apc_delete_file($file);\n        }\n    }\n\n    /**\n     * Saves items data into persistent storage.\n     */\n    protected function saveItems()\n    {\n        $items = [];\n        foreach ($this->items as $name => $item) {\n            /** @var Item $item */\n            $items[$name] = array_filter(\n                [\n                    'type' => $item->type,\n                    'description' => $item->description,\n                    'ruleName' => $item->ruleName,\n                    'data' => $item->data,\n                ]\n            );\n            if (isset($this->children[$name])) {\n                foreach ($this->children[$name] as $child) {\n                    /** @var Item $child */\n                    $items[$name]['children'][] = $child->name;\n                }\n            }\n        }\n        $this->saveToFile($items, $this->itemFile);\n    }\n\n    /**\n     * Saves assignments data into persistent storage.\n     */\n    protected function saveAssignments()\n    {\n        $assignmentData = [];\n        foreach ($this->assignments as $userId => $assignments) {\n            foreach ($assignments as $name => $assignment) {\n                /** @var Assignment $assignment */\n                $assignmentData[$userId][] = $assignment->roleName;\n            }\n        }\n        $this->saveToFile($assignmentData, $this->assignmentFile);\n    }\n\n    /**\n     * Saves rules data into persistent storage.\n     */\n    protected function saveRules()\n    {\n        $rules = [];\n        foreach ($this->rules as $name => $rule) {\n            $rules[$name] = serialize($rule);\n        }\n        $this->saveToFile($rules, $this->ruleFile);\n    }\n\n    /**\n     * {@inheritdoc}\n     * @since 2.0.7\n     */\n    public function getUserIdsByRole($roleName)\n    {\n        $result = [];\n        foreach ($this->assignments as $userID => $assignments) {\n            foreach ($assignments as $userAssignment) {\n                if ($userAssignment->roleName === $roleName && $userAssignment->userId == $userID) {\n                    $result[] = (string) $userID;\n                }\n            }\n        }\n\n        return $result;\n    }\n}\n"
  },
  {
    "path": "framework/rbac/Role.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\rbac;\n\n/**\n * For more details and usage information on Role, see the [guide article on security authorization](guide:security-authorization).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Role extends Item\n{\n    /**\n     * {@inheritdoc}\n     */\n    public $type = self::TYPE_ROLE;\n}\n"
  },
  {
    "path": "framework/rbac/Rule.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\rbac;\n\nuse yii\\base\\BaseObject;\n\n/**\n * Rule represents a business constraint that may be associated with a role, permission or assignment.\n *\n * For more details and usage information on Rule, see the [guide article on security authorization](guide:security-authorization).\n *\n * @author Alexander Makarov <sam@rmcreative.ru>\n * @since 2.0\n */\nabstract class Rule extends BaseObject\n{\n    /**\n     * @var string name of the rule\n     */\n    public $name;\n    /**\n     * @var int UNIX timestamp representing the rule creation time\n     */\n    public $createdAt;\n    /**\n     * @var int UNIX timestamp representing the rule updating time\n     */\n    public $updatedAt;\n\n\n    /**\n     * Executes the rule.\n     *\n     * @param string|int $user the user ID. This should be either an integer or a string representing\n     * the unique identifier of a user. See [[\\yii\\web\\User::id]].\n     * @param Item $item the role or permission that this rule is associated with\n     * @param array $params parameters passed to [[CheckAccessInterface::checkAccess()]].\n     * @return bool a value indicating whether the rule permits the auth item it is associated with.\n     */\n    abstract public function execute($user, $item, $params);\n}\n"
  },
  {
    "path": "framework/rbac/migrations/m140506_102106_rbac_init.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nuse yii\\base\\InvalidConfigException;\nuse yii\\rbac\\DbManager;\n\n/**\n * Initializes RBAC tables.\n *\n * @author Alexander Kochetov <creocoder@gmail.com>\n * @since 2.0\n */\nclass m140506_102106_rbac_init extends \\yii\\db\\Migration\n{\n    /**\n     * @throws yii\\base\\InvalidConfigException\n     * @return DbManager\n     */\n    protected function getAuthManager()\n    {\n        $authManager = Yii::$app->getAuthManager();\n        if (!$authManager instanceof DbManager) {\n            throw new InvalidConfigException('You should configure \"authManager\" component to use database before executing this migration.');\n        }\n\n        return $authManager;\n    }\n\n    /**\n     * @return bool\n     */\n    protected function isMSSQL()\n    {\n        return $this->db->driverName === 'mssql' || $this->db->driverName === 'sqlsrv' || $this->db->driverName === 'dblib';\n    }\n\n    protected function isOracle()\n    {\n        return $this->db->driverName === 'oci' || $this->db->driverName === 'oci8';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $authManager = $this->getAuthManager();\n        $this->db = $authManager->db;\n        $schema = $this->db->getSchema()->defaultSchema;\n\n        $tableOptions = null;\n        if ($this->db->driverName === 'mysql') {\n            // https://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci\n            $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';\n        }\n\n        $this->createTable($authManager->ruleTable, [\n            'name' => $this->string(64)->notNull(),\n            'data' => $this->binary(),\n            'created_at' => $this->integer(),\n            'updated_at' => $this->integer(),\n            'PRIMARY KEY ([[name]])',\n        ], $tableOptions);\n\n        $this->createTable($authManager->itemTable, [\n            'name' => $this->string(64)->notNull(),\n            'type' => $this->smallInteger()->notNull(),\n            'description' => $this->text(),\n            'rule_name' => $this->string(64),\n            'data' => $this->binary(),\n            'created_at' => $this->integer(),\n            'updated_at' => $this->integer(),\n            'PRIMARY KEY ([[name]])',\n            'FOREIGN KEY ([[rule_name]]) REFERENCES ' . $authManager->ruleTable . ' ([[name]])' .\n                $this->buildFkClause('ON DELETE SET NULL', 'ON UPDATE CASCADE'),\n        ], $tableOptions);\n        $this->createIndex('idx-auth_item-type', $authManager->itemTable, 'type');\n\n        $this->createTable($authManager->itemChildTable, [\n            'parent' => $this->string(64)->notNull(),\n            'child' => $this->string(64)->notNull(),\n            'PRIMARY KEY ([[parent]], [[child]])',\n            'FOREIGN KEY ([[parent]]) REFERENCES ' . $authManager->itemTable . ' ([[name]])' .\n                $this->buildFkClause('ON DELETE CASCADE', 'ON UPDATE CASCADE'),\n            'FOREIGN KEY ([[child]]) REFERENCES ' . $authManager->itemTable . ' ([[name]])' .\n                $this->buildFkClause('ON DELETE CASCADE', 'ON UPDATE CASCADE'),\n        ], $tableOptions);\n\n        $this->createTable($authManager->assignmentTable, [\n            'item_name' => $this->string(64)->notNull(),\n            'user_id' => $this->string(64)->notNull(),\n            'created_at' => $this->integer(),\n            'PRIMARY KEY ([[item_name]], [[user_id]])',\n            'FOREIGN KEY ([[item_name]]) REFERENCES ' . $authManager->itemTable . ' ([[name]])' .\n                $this->buildFkClause('ON DELETE CASCADE', 'ON UPDATE CASCADE'),\n        ], $tableOptions);\n\n        if ($this->isMSSQL()) {\n            $this->execute(\"CREATE TRIGGER {$schema}.trigger_auth_item_child\n            ON {$schema}.{$authManager->itemTable}\n            INSTEAD OF DELETE, UPDATE\n            AS\n            DECLARE @old_name VARCHAR (64) = (SELECT name FROM deleted)\n            DECLARE @new_name VARCHAR (64) = (SELECT name FROM inserted)\n            BEGIN\n            IF COLUMNS_UPDATED() > 0\n                BEGIN\n                    IF @old_name <> @new_name\n                    BEGIN\n                        ALTER TABLE {$authManager->itemChildTable} NOCHECK CONSTRAINT FK__auth_item__child;\n                        UPDATE {$authManager->itemChildTable} SET child = @new_name WHERE child = @old_name;\n                    END\n                UPDATE {$authManager->itemTable}\n                SET name = (SELECT name FROM inserted),\n                type = (SELECT type FROM inserted),\n                description = (SELECT description FROM inserted),\n                rule_name = (SELECT rule_name FROM inserted),\n                data = (SELECT data FROM inserted),\n                created_at = (SELECT created_at FROM inserted),\n                updated_at = (SELECT updated_at FROM inserted)\n                WHERE name IN (SELECT name FROM deleted)\n                IF @old_name <> @new_name\n                    BEGIN\n                        ALTER TABLE {$authManager->itemChildTable} CHECK CONSTRAINT FK__auth_item__child;\n                    END\n                END\n                ELSE\n                    BEGIN\n                        DELETE FROM {$schema}.{$authManager->itemChildTable} WHERE parent IN (SELECT name FROM deleted) OR child IN (SELECT name FROM deleted);\n                        DELETE FROM {$schema}.{$authManager->itemTable} WHERE name IN (SELECT name FROM deleted);\n                    END\n            END;\");\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        $authManager = $this->getAuthManager();\n        $this->db = $authManager->db;\n        $schema = $this->db->getSchema()->defaultSchema;\n\n        if ($this->isMSSQL()) {\n            $this->execute(\"DROP TRIGGER {$schema}.trigger_auth_item_child;\");\n        }\n\n        $this->dropTable($authManager->assignmentTable);\n        $this->dropTable($authManager->itemChildTable);\n        $this->dropTable($authManager->itemTable);\n        $this->dropTable($authManager->ruleTable);\n    }\n\n    protected function buildFkClause($delete = '', $update = '')\n    {\n        if ($this->isMSSQL()) {\n            return '';\n        }\n\n        if ($this->isOracle()) {\n            return ' ' . $delete;\n        }\n\n        return implode(' ', ['', $delete, $update]);\n    }\n}\n"
  },
  {
    "path": "framework/rbac/migrations/m170907_052038_rbac_add_index_on_auth_assignment_user_id.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nuse yii\\base\\InvalidConfigException;\nuse yii\\db\\Migration;\nuse yii\\rbac\\DbManager;\n\n/**\n * Adds index on `user_id` column in `auth_assignment` table for performance reasons.\n *\n * @see https://github.com/yiisoft/yii2/pull/14765\n *\n * @author Ivan Buttinoni <ivan.buttinoni@cibi.it>\n * @since 2.0.13\n */\nclass m170907_052038_rbac_add_index_on_auth_assignment_user_id extends Migration\n{\n    public $column = 'user_id';\n    public $index = 'auth_assignment_user_id_idx';\n\n    /**\n     * @throws yii\\base\\InvalidConfigException\n     * @return DbManager\n     */\n    protected function getAuthManager()\n    {\n        $authManager = Yii::$app->getAuthManager();\n        if (!$authManager instanceof DbManager) {\n            throw new InvalidConfigException('You should configure \"authManager\" component to use database before executing this migration.');\n        }\n\n        return $authManager;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $authManager = $this->getAuthManager();\n        $this->db = $authManager->db;\n\n        $this->createIndex($this->index, $authManager->assignmentTable, $this->column);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        $authManager = $this->getAuthManager();\n        $this->db = $authManager->db;\n\n        $this->dropIndex($this->index, $authManager->assignmentTable);\n    }\n}\n"
  },
  {
    "path": "framework/rbac/migrations/m180523_151638_rbac_updates_indexes_without_prefix.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nuse yii\\base\\InvalidConfigException;\nuse yii\\db\\Migration;\nuse yii\\rbac\\DbManager;\n\n/**\n * Updates indexes without a prefix.\n *\n * @see https://github.com/yiisoft/yii2/pull/15548\n *\n * @author Sergey Gonimar <sergey.gonimar@gmail.com>\n * @since 2.0.16\n */\nclass m180523_151638_rbac_updates_indexes_without_prefix extends Migration\n{\n    /**\n     * @throws yii\\base\\InvalidConfigException\n     * @return DbManager\n     */\n    protected function getAuthManager()\n    {\n        $authManager = Yii::$app->getAuthManager();\n        if (!$authManager instanceof DbManager) {\n            throw new InvalidConfigException('You should configure \"authManager\" component to use database before executing this migration.');\n        }\n\n        return $authManager;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        $authManager = $this->getAuthManager();\n        $this->db = $authManager->db;\n\n        $this->dropIndex('auth_assignment_user_id_idx', $authManager->assignmentTable);\n        $this->createIndex('{{%idx-auth_assignment-user_id}}', $authManager->assignmentTable, 'user_id');\n\n        $this->dropIndex('idx-auth_item-type', $authManager->itemTable);\n        $this->createIndex('{{%idx-auth_item-type}}', $authManager->itemTable, 'type');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        $authManager = $this->getAuthManager();\n        $this->db = $authManager->db;\n\n        $this->dropIndex('{{%idx-auth_assignment-user_id}}', $authManager->assignmentTable);\n        $this->createIndex('auth_assignment_user_id_idx', $authManager->assignmentTable, 'user_id');\n\n\n        $this->dropIndex('{{%idx-auth_item-type}}', $authManager->itemTable);\n        $this->createIndex('idx-auth_item-type', $authManager->itemTable, 'type');\n    }\n}\n"
  },
  {
    "path": "framework/rbac/migrations/m200409_110543_rbac_update_mssql_trigger.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nuse yii\\base\\InvalidConfigException;\nuse yii\\db\\Migration;\nuse yii\\db\\Query;\nuse yii\\rbac\\DbManager;\n\n/**\n * Fix MSSQL trigger.\n *\n * @see https://github.com/yiisoft/yii2/pull/17966\n *\n * @author Aurelien Chretien <chretien.aurelien@gmail.com>\n * @since 2.0.35\n */\nclass m200409_110543_rbac_update_mssql_trigger extends Migration\n{\n    /**\n     * @throws yii\\base\\InvalidConfigException\n     * @return DbManager\n     */\n    protected function getAuthManager()\n    {\n        $authManager = Yii::$app->getAuthManager();\n        if (!$authManager instanceof DbManager) {\n            throw new InvalidConfigException('You should configure \"authManager\" component to use database before executing this migration.');\n        }\n\n        return $authManager;\n    }\n\n    protected function findForeignKeyName($table, $column, $referenceTable, $referenceColumn)\n    {\n        return (new Query())\n            ->select(['OBJECT_NAME(fkc.constraint_object_id)'])\n            ->from(['fkc' => 'sys.foreign_key_columns'])\n            ->innerJoin(['c' => 'sys.columns'], 'fkc.parent_object_id = c.object_id AND fkc.parent_column_id = c.column_id')\n            ->innerJoin(['r' => 'sys.columns'], 'fkc.referenced_object_id = r.object_id AND fkc.referenced_column_id = r.column_id')\n            ->andWhere('fkc.parent_object_id=OBJECT_ID(:fkc_parent_object_id)', [':fkc_parent_object_id' => $this->db->schema->getRawTableName($table)])\n            ->andWhere('fkc.referenced_object_id=OBJECT_ID(:fkc_referenced_object_id)', [':fkc_referenced_object_id' => $this->db->schema->getRawTableName($referenceTable)])\n            ->andWhere(['c.name' => $column])\n            ->andWhere(['r.name' => $referenceColumn])\n            ->scalar($this->db);\n    }\n\n    /**\n     * @return bool\n     */\n    protected function isMSSQL()\n    {\n        return $this->db->driverName === 'mssql' || $this->db->driverName === 'sqlsrv' || $this->db->driverName === 'dblib';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function up()\n    {\n        if ($this->isMSSQL()) {\n            $authManager = $this->getAuthManager();\n            $this->db = $authManager->db;\n            $schema = $this->db->getSchema()->defaultSchema;\n            $triggerSuffix = $this->db->schema->getRawTableName($authManager->itemChildTable);\n\n            $this->execute(\"IF (OBJECT_ID(N'{$schema}.trigger_{$triggerSuffix}') IS NOT NULL) DROP TRIGGER {$schema}.trigger_{$triggerSuffix};\");\n            $this->execute(\"IF (OBJECT_ID(N'{$schema}.trigger_auth_item_child') IS NOT NULL) DROP TRIGGER {$schema}.trigger_auth_item_child;\");\n\n            $this->execute(\"CREATE TRIGGER {$schema}.trigger_delete_{$triggerSuffix}\n            ON {$schema}.{$authManager->itemTable}\n            INSTEAD OF DELETE\n            AS\n            BEGIN\n                  DELETE FROM {$schema}.{$authManager->itemChildTable} WHERE parent IN (SELECT name FROM deleted) OR child IN (SELECT name FROM deleted);\n                  DELETE FROM {$schema}.{$authManager->itemTable} WHERE name IN (SELECT name FROM deleted);\n            END;\");\n\n            $foreignKey = $this->findForeignKeyName($authManager->itemChildTable, 'child', $authManager->itemTable, 'name');\n            $this->execute(\"CREATE TRIGGER {$schema}.trigger_update_{$triggerSuffix}\n            ON {$schema}.{$authManager->itemTable}\n            INSTEAD OF UPDATE\n            AS\n                DECLARE @old_name NVARCHAR(64) = (SELECT name FROM deleted)\n                DECLARE @new_name NVARCHAR(64) = (SELECT name FROM inserted)\n            BEGIN\n                IF @old_name <> @new_name\n                BEGIN\n                    ALTER TABLE {$authManager->itemChildTable} NOCHECK CONSTRAINT {$foreignKey};\n                    UPDATE {$authManager->itemChildTable} SET child = @new_name WHERE child = @old_name;\n                END\n            UPDATE {$authManager->itemTable}\n            SET name = (SELECT name FROM inserted),\n            type = (SELECT type FROM inserted),\n            description = (SELECT description FROM inserted),\n            rule_name = (SELECT rule_name FROM inserted),\n            data = (SELECT data FROM inserted),\n            created_at = (SELECT created_at FROM inserted),\n            updated_at = (SELECT updated_at FROM inserted)\n            WHERE name IN (SELECT name FROM deleted)\n            IF @old_name <> @new_name\n                BEGIN\n                    ALTER TABLE {$authManager->itemChildTable} CHECK CONSTRAINT {$foreignKey};\n                END\n            END;\");\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function down()\n    {\n        if ($this->isMSSQL()) {\n            $authManager = $this->getAuthManager();\n            $this->db = $authManager->db;\n            $schema = $this->db->getSchema()->defaultSchema;\n            $triggerSuffix = $this->db->schema->getRawTableName($authManager->itemChildTable);\n\n            $this->execute(\"DROP TRIGGER {$schema}.trigger_update_{$triggerSuffix};\");\n            $this->execute(\"DROP TRIGGER {$schema}.trigger_delete_{$triggerSuffix};\");\n\n            $this->execute(\"CREATE TRIGGER {$schema}.trigger_auth_item_child\n            ON {$schema}.{$authManager->itemTable}\n            INSTEAD OF DELETE, UPDATE\n            AS\n            DECLARE @old_name VARCHAR (64) = (SELECT name FROM deleted)\n            DECLARE @new_name VARCHAR (64) = (SELECT name FROM inserted)\n            BEGIN\n            IF COLUMNS_UPDATED() > 0\n                BEGIN\n                    IF @old_name <> @new_name\n                    BEGIN\n                        ALTER TABLE {$authManager->itemChildTable} NOCHECK CONSTRAINT FK__auth_item__child;\n                        UPDATE {$authManager->itemChildTable} SET child = @new_name WHERE child = @old_name;\n                    END\n                UPDATE {$authManager->itemTable}\n                SET name = (SELECT name FROM inserted),\n                type = (SELECT type FROM inserted),\n                description = (SELECT description FROM inserted),\n                rule_name = (SELECT rule_name FROM inserted),\n                data = (SELECT data FROM inserted),\n                created_at = (SELECT created_at FROM inserted),\n                updated_at = (SELECT updated_at FROM inserted)\n                WHERE name IN (SELECT name FROM deleted)\n                IF @old_name <> @new_name\n                    BEGIN\n                        ALTER TABLE {$authManager->itemChildTable} CHECK CONSTRAINT FK__auth_item__child;\n                    END\n                END\n                ELSE\n                    BEGIN\n                        DELETE FROM {$schema}.{$authManager->itemChildTable} WHERE parent IN (SELECT name FROM deleted) OR child IN (SELECT name FROM deleted);\n                        DELETE FROM {$schema}.{$authManager->itemTable} WHERE name IN (SELECT name FROM deleted);\n                    END\n            END;\");\n        }\n    }\n}\n"
  },
  {
    "path": "framework/rbac/migrations/schema-mssql.sql",
    "content": "/**\n * Database schema required by \\yii\\rbac\\DbManager.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Alexander Kochetov <creocoder@gmail.com>\n * @link https://www.yiiframework.com/\n * @copyright 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n * @since 2.0\n */\n\nif object_id('[auth_assignment]', 'U') is not null\n    drop table [auth_assignment];\n\nif object_id('[auth_item_child]', 'U') is not null\n    drop table [auth_item_child];\n\nif object_id('[auth_item]', 'U') is not null\n    drop table [auth_item];\n\nif object_id('[auth_rule]', 'U') is not null\n    drop table [auth_rule];\n\ncreate table [auth_rule]\n(\n    [name]  varchar(64) not null,\n    [data]  blob,\n    [created_at]           integer,\n    [updated_at]           integer,\n    primary key ([name])\n);\n\ncreate table [auth_item]\n(\n   [name]                 varchar(64) not null,\n   [type]                 smallint not null,\n   [description]          text,\n   [rule_name]            varchar(64),\n   [data]                 blob,\n   [created_at]           integer,\n   [updated_at]           integer,\n   primary key ([name]),\n   foreign key ([rule_name]) references [auth_rule] ([name])\n);\n\ncreate index [idx-auth_item-type] on [auth_item] ([type]);\n\ncreate table [auth_item_child]\n(\n   [parent]               varchar(64) not null,\n   [child]                varchar(64) not null,\n   primary key ([parent],[child]),\n   foreign key ([parent]) references [auth_item] ([name]),\n   foreign key ([child]) references [auth_item] ([name])\n);\n\ncreate table [auth_assignment]\n(\n   [item_name]            varchar(64) not null,\n   [user_id]              varchar(64) not null,\n   [created_at]           integer,\n   primary key ([item_name], [user_id]),\n   foreign key ([item_name]) references [auth_item] ([name]) on delete cascade on update cascade\n);\n\ncreate index [auth_assignment_user_id_idx] on [auth_assignment] ([user_id]);\n\nCREATE TRIGGER dbo.trigger_auth_item_child\n    ON dbo.[auth_item]\n    INSTEAD OF DELETE, UPDATE\n    AS\n    DECLARE @old_name VARCHAR (64) = (SELECT name FROM deleted)\n    DECLARE @new_name VARCHAR (64) = (SELECT name FROM inserted)\n    BEGIN\n        IF COLUMNS_UPDATED() > 0\n        BEGIN\n            IF @old_name <> @new_name\n                BEGIN\n                    ALTER TABLE auth_item_child NOCHECK CONSTRAINT FK__auth_item__child;\n                    UPDATE auth_item_child SET child = @new_name WHERE child = @old_name;\n                END\n                UPDATE auth_item\n                SET name = (SELECT name FROM inserted),\n                type = (SELECT type FROM inserted),\n                description = (SELECT description FROM inserted),\n                rule_name = (SELECT rule_name FROM inserted),\n                data = (SELECT data FROM inserted),\n                created_at = (SELECT created_at FROM inserted),\n                updated_at = (SELECT updated_at FROM inserted)\n                WHERE name IN (SELECT name FROM deleted)\n                IF @old_name <> @new_name\n                    BEGIN\n                        ALTER TABLE auth_item_child CHECK CONSTRAINT FK__auth_item__child;\n                    END\n                END\n                ELSE\n                    BEGIN\n                        DELETE FROM dbo.[auth_item_child] WHERE parent IN (SELECT name FROM deleted) OR child IN (SELECT name FROM deleted);\n                        DELETE FROM dbo.[auth_item] WHERE name IN (SELECT name FROM deleted);\n                    END\n        END;\n"
  },
  {
    "path": "framework/rbac/migrations/schema-mysql.sql",
    "content": "/**\n * Database schema required by \\yii\\rbac\\DbManager.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Alexander Kochetov <creocoder@gmail.com>\n * @link https://www.yiiframework.com/\n * @copyright 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n * @since 2.0\n */\n\ndrop table if exists `auth_assignment`;\ndrop table if exists `auth_item_child`;\ndrop table if exists `auth_item`;\ndrop table if exists `auth_rule`;\n\ncreate table `auth_rule`\n(\n   `name`                 varchar(64) not null,\n   `data`                 blob,\n   `created_at`           integer,\n   `updated_at`           integer,\n    primary key (`name`)\n) engine InnoDB;\n\ncreate table `auth_item`\n(\n   `name`                 varchar(64) not null,\n   `type`                 smallint not null,\n   `description`          text,\n   `rule_name`            varchar(64),\n   `data`                 blob,\n   `created_at`           integer,\n   `updated_at`           integer,\n   primary key (`name`),\n   foreign key (`rule_name`) references `auth_rule` (`name`) on delete set null on update cascade,\n   key `type` (`type`)\n) engine InnoDB;\n\ncreate table `auth_item_child`\n(\n   `parent`               varchar(64) not null,\n   `child`                varchar(64) not null,\n   primary key (`parent`, `child`),\n   foreign key (`parent`) references `auth_item` (`name`) on delete cascade on update cascade,\n   foreign key (`child`) references `auth_item` (`name`) on delete cascade on update cascade\n) engine InnoDB;\n\ncreate table `auth_assignment`\n(\n   `item_name`            varchar(64) not null,\n   `user_id`              varchar(64) not null,\n   `created_at`           integer,\n   primary key (`item_name`, `user_id`),\n   foreign key (`item_name`) references `auth_item` (`name`) on delete cascade on update cascade,\n   key `auth_assignment_user_id_idx` (`user_id`)\n) engine InnoDB;\n"
  },
  {
    "path": "framework/rbac/migrations/schema-oci.sql",
    "content": "/**\n * Database schema required by \\yii\\rbac\\DbManager.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Alexander Kochetov <creocoder@gmail.com>\n * @link https://www.yiiframework.com/\n * @copyright 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n * @since 2.0\n */\n\ndrop table \"auth_assignment\";\ndrop table \"auth_item_child\";\ndrop table \"auth_item\";\ndrop table \"auth_rule\";\n\n-- create new auth_rule table\ncreate table \"auth_rule\"\n(\n   \"name\"  varchar(64) not null,\n   \"data\"  BYTEA,\n   \"created_at\"           integer,\n   \"updated_at\"           integer,\n    primary key (\"name\")\n);\n\n-- create auth_item table\ncreate table \"auth_item\"\n(\n   \"name\"                 varchar(64) not null,\n   \"type\"                 smallint not null,\n   \"description\"          varchar(1000),\n   \"rule_name\"            varchar(64),\n   \"data\"                 BYTEA,\n   \"updated_at\"           integer,\n        foreign key (\"rule_name\") references \"auth_rule\"(\"name\") on delete set null,\n        primary key (\"name\")\n);\n-- adds oracle specific index to auth_item\nCREATE INDEX auth_type_index ON \"auth_item\"(\"type\");\n\ncreate table \"auth_item_child\"\n(\n   \"parent\"               varchar(64) not null,\n   \"child\"                varchar(64) not null,\n   primary key (\"parent\",\"child\"),\n   foreign key (\"parent\") references \"auth_item\"(\"name\") on delete cascade,\n   foreign key (\"child\") references \"auth_item\"(\"name\") on delete cascade\n);\n\ncreate table \"auth_assignment\"\n(\n   \"item_name\"            varchar(64) not null,\n   \"user_id\"              varchar(64) not null,\n   \"created_at\"           integer,\n   primary key (\"item_name\",\"user_id\"),\n   foreign key (\"item_name\") references \"auth_item\" (\"name\") on delete cascade\n);\n\nCREATE INDEX auth_assignment_user_id_idx ON \"auth_assignment\" (\"user_id\");\n"
  },
  {
    "path": "framework/rbac/migrations/schema-pgsql.sql",
    "content": "/**\n * Database schema required by \\yii\\rbac\\DbManager.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Alexander Kochetov <creocoder@gmail.com>\n * @link https://www.yiiframework.com/\n * @copyright 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n * @since 2.0\n */\n\ndrop table if exists \"auth_assignment\";\ndrop table if exists \"auth_item_child\";\ndrop table if exists \"auth_item\";\ndrop table if exists \"auth_rule\";\n\ncreate table \"auth_rule\"\n(\n    \"name\"  varchar(64) not null,\n    \"data\"  bytea,\n    \"created_at\"           integer,\n    \"updated_at\"           integer,\n    primary key (\"name\")\n);\n\ncreate table \"auth_item\"\n(\n   \"name\"                 varchar(64) not null,\n   \"type\"                 smallint not null,\n   \"description\"          text,\n   \"rule_name\"            varchar(64),\n   \"data\"                 bytea,\n   \"created_at\"           integer,\n   \"updated_at\"           integer,\n   primary key (\"name\"),\n   foreign key (\"rule_name\") references \"auth_rule\" (\"name\") on delete set null on update cascade\n);\n\ncreate index auth_item_type_idx on \"auth_item\" (\"type\");\n\ncreate table \"auth_item_child\"\n(\n   \"parent\"               varchar(64) not null,\n   \"child\"                varchar(64) not null,\n   primary key (\"parent\",\"child\"),\n   foreign key (\"parent\") references \"auth_item\" (\"name\") on delete cascade on update cascade,\n   foreign key (\"child\") references \"auth_item\" (\"name\") on delete cascade on update cascade\n);\n\ncreate table \"auth_assignment\"\n(\n   \"item_name\"            varchar(64) not null,\n   \"user_id\"              varchar(64) not null,\n   \"created_at\"           integer,\n   primary key (\"item_name\",\"user_id\"),\n   foreign key (\"item_name\") references \"auth_item\" (\"name\") on delete cascade on update cascade\n);\n\ncreate index auth_assignment_user_id_idx on \"auth_assignment\" (\"user_id\");\n"
  },
  {
    "path": "framework/rbac/migrations/schema-sqlite.sql",
    "content": "/**\n * Database schema required by \\yii\\rbac\\DbManager.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Alexander Kochetov <creocoder@gmail.com>\n * @link https://www.yiiframework.com/\n * @copyright 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n * @since 2.0\n */\n\ndrop table if exists \"auth_assignment\";\ndrop table if exists \"auth_item_child\";\ndrop table if exists \"auth_item\";\ndrop table if exists \"auth_rule\";\n\ncreate table \"auth_rule\"\n(\n    \"name\"  varchar(64) not null,\n    \"data\"  blob,\n    \"created_at\"           integer,\n    \"updated_at\"           integer,\n    primary key (\"name\")\n);\n\ncreate table \"auth_item\"\n(\n   \"name\"                 varchar(64) not null,\n   \"type\"                 smallint not null,\n   \"description\"          text,\n   \"rule_name\"            varchar(64),\n   \"data\"                 blob,\n   \"created_at\"           integer,\n   \"updated_at\"           integer,\n   primary key (\"name\"),\n   foreign key (\"rule_name\") references \"auth_rule\" (\"name\") on delete set null on update cascade\n);\n\ncreate index \"auth_item_type_idx\" on \"auth_item\" (\"type\");\n\ncreate table \"auth_item_child\"\n(\n   \"parent\"               varchar(64) not null,\n   \"child\"                varchar(64) not null,\n   primary key (\"parent\",\"child\"),\n   foreign key (\"parent\") references \"auth_item\" (\"name\") on delete cascade on update cascade,\n   foreign key (\"child\") references \"auth_item\" (\"name\") on delete cascade on update cascade\n);\n\ncreate table \"auth_assignment\"\n(\n   \"item_name\"            varchar(64) not null,\n   \"user_id\"              varchar(64) not null,\n   \"created_at\"           integer,\n   primary key (\"item_name\",\"user_id\"),\n   foreign key (\"item_name\") references \"auth_item\" (\"name\") on delete cascade on update cascade\n);\n\ncreate index \"auth_assignment_user_id_idx\" on \"auth_assignment\" (\"user_id\");\n"
  },
  {
    "path": "framework/requirements/YiiRequirementChecker.php",
    "content": "<?php\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nif (version_compare(PHP_VERSION, '4.3', '<')) {\n    echo 'At least PHP 4.3 is required to run this script!';\n    exit(1);\n}\n\n/**\n * YiiRequirementChecker allows checking, if current system meets the requirements for running the Yii application.\n * This class allows rendering of the check report for the web and console application interface.\n *\n * Example:\n *\n * ```\n * require_once 'path/to/YiiRequirementChecker.php';\n * $requirementsChecker = new YiiRequirementChecker();\n * $requirements = array(\n *     array(\n *         'name' => 'PHP Some Extension',\n *         'mandatory' => true,\n *         'condition' => extension_loaded('some_extension'),\n *         'by' => 'Some application feature',\n *         'memo' => 'PHP extension \"some_extension\" required',\n *     ),\n * );\n * $requirementsChecker->checkYii()->check($requirements)->render();\n * ```\n *\n * If you wish to render the report with your own representation, use [[getResult()]] instead of [[render()]]\n *\n * Requirement condition could be in format \"eval:PHP expression\".\n * In this case specified PHP expression will be evaluated in the context of this class instance.\n * For example:\n *\n * ```\n * $requirements = array(\n *     array(\n *         'name' => 'Upload max file size',\n *         'condition' => 'eval:$this->checkUploadMaxFileSize(\"5M\")',\n *     ),\n * );\n * ```\n *\n * Note: this class definition does not match ordinary Yii style, because it should match PHP 4.3\n * and should not use features from newer PHP versions!\n *\n * @author Paul Klimov <klimov.paul@gmail.com>\n * @since 2.0\n */\nclass YiiRequirementChecker\n{\n    /**\n     * @var array|null the check results, this property is for internal usage only.\n     */\n    public $result;\n\n    /**\n     * Check the given requirements, collecting results into internal field.\n     * This method can be invoked several times checking different requirement sets.\n     * Use [[getResult()]] or [[render()]] to get the results.\n     * @param array|string $requirements requirements to be checked.\n     * If an array, it is treated as the set of requirements;\n     * If a string, it is treated as the path of the file, which contains the requirements;\n     * @return $this self instance.\n     */\n    public function check($requirements)\n    {\n        if (is_string($requirements)) {\n            $requirements = require $requirements;\n        }\n        if (!is_array($requirements)) {\n            $this->usageError('Requirements must be an array, \"' . gettype($requirements) . '\" has been given!');\n        }\n        if (!isset($this->result) || !is_array($this->result)) {\n            $this->result = array(\n                'summary' => array(\n                    'total' => 0,\n                    'errors' => 0,\n                    'warnings' => 0,\n                ),\n                'requirements' => array(),\n            );\n        }\n        foreach ($requirements as $key => $rawRequirement) {\n            $requirement = $this->normalizeRequirement($rawRequirement, $key);\n            $this->result['summary']['total']++;\n            if (!$requirement['condition']) {\n                if ($requirement['mandatory']) {\n                    $requirement['error'] = true;\n                    $requirement['warning'] = true;\n                    $this->result['summary']['errors']++;\n                } else {\n                    $requirement['error'] = false;\n                    $requirement['warning'] = true;\n                    $this->result['summary']['warnings']++;\n                }\n            } else {\n                $requirement['error'] = false;\n                $requirement['warning'] = false;\n            }\n            $this->result['requirements'][] = $requirement;\n        }\n\n        return $this;\n    }\n\n    /**\n     * Performs the check for the Yii core requirements.\n     * @return YiiRequirementChecker self instance.\n     */\n    public function checkYii()\n    {\n        return $this->check(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'requirements.php');\n    }\n\n    /**\n     * Return the check results.\n     * @return array|null check results in format:\n     *\n     * ```\n     * array(\n     *     'summary' => array(\n     *         'total' => total number of checks,\n     *         'errors' => number of errors,\n     *         'warnings' => number of warnings,\n     *     ),\n     *     'requirements' => array(\n     *         array(\n     *             ...\n     *             'error' => is there an error,\n     *             'warning' => is there a warning,\n     *         ),\n     *         ...\n     *     ),\n     * )\n     * ```\n     */\n    public function getResult()\n    {\n        if (isset($this->result)) {\n            return $this->result;\n        } else {\n            return null;\n        }\n    }\n\n    /**\n     * Renders the requirements check result.\n     * The output will vary depending is a script running from web or from console.\n     */\n    public function render()\n    {\n        if (!isset($this->result)) {\n            $this->usageError('Nothing to render!');\n        }\n        $baseViewFilePath = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'views';\n        if (!empty($_SERVER['argv'])) {\n            $viewFileName = $baseViewFilePath . DIRECTORY_SEPARATOR . 'console' . DIRECTORY_SEPARATOR . 'index.php';\n        } else {\n            $viewFileName = $baseViewFilePath . DIRECTORY_SEPARATOR . 'web' . DIRECTORY_SEPARATOR . 'index.php';\n        }\n        $this->renderViewFile($viewFileName, $this->result);\n    }\n\n    /**\n     * Checks if the given PHP extension is available and its version matches the given one.\n     * @param string $extensionName PHP extension name.\n     * @param string $version required PHP extension version.\n     * @param string $compare comparison operator, by default '>='\n     * @return bool if PHP extension version matches.\n     */\n    public function checkPhpExtensionVersion($extensionName, $version, $compare = '>=')\n    {\n        if (!extension_loaded($extensionName)) {\n            return false;\n        }\n        $extensionVersion = phpversion($extensionName);\n        if (empty($extensionVersion)) {\n            return false;\n        }\n        if (strncasecmp($extensionVersion, 'PECL-', 5) === 0) {\n            $extensionVersion = substr($extensionVersion, 5);\n        }\n\n        return version_compare($extensionVersion, $version, $compare);\n    }\n\n    /**\n     * Checks if PHP configuration option (from php.ini) is on.\n     * @param string $name configuration option name.\n     * @return bool option is on.\n     */\n    public function checkPhpIniOn($name)\n    {\n        $value = ini_get($name);\n        if (empty($value)) {\n            return false;\n        }\n\n        return ((int) $value === 1 || strtolower($value) === 'on');\n    }\n\n    /**\n     * Checks if PHP configuration option (from php.ini) is off.\n     * @param string $name configuration option name.\n     * @return bool option is off.\n     */\n    public function checkPhpIniOff($name)\n    {\n        $value = ini_get($name);\n        if (empty($value)) {\n            return true;\n        }\n\n        return (strtolower($value) === 'off');\n    }\n\n    /**\n     * Compare byte sizes of values given in the verbose representation,\n     * like '5M', '15K' etc.\n     * @param string $a first value.\n     * @param string $b second value.\n     * @param string $compare comparison operator, by default '>='.\n     * @return bool comparison result.\n     */\n    public function compareByteSize($a, $b, $compare = '>=')\n    {\n        $compareExpression = '(' . $this->getByteSize($a) . $compare . $this->getByteSize($b) . ')';\n\n        return $this->evaluateExpression($compareExpression);\n    }\n\n    /**\n     * Gets the size in bytes from verbose size representation.\n     * For example: '5K' => 5*1024\n     * @param string $verboseSize verbose size representation.\n     * @return int actual size in bytes.\n     */\n    public function getByteSize($verboseSize)\n    {\n        if (empty($verboseSize)) {\n            return 0;\n        }\n        if (is_numeric($verboseSize)) {\n            return (int) $verboseSize;\n        }\n        $sizeUnit = trim($verboseSize, '0123456789');\n        $size = trim(str_replace($sizeUnit, '', $verboseSize));\n        if (!is_numeric($size)) {\n            return 0;\n        }\n        switch (strtolower($sizeUnit)) {\n            case 'kb':\n            case 'k':\n                return $size * 1024;\n            case 'mb':\n            case 'm':\n                return $size * 1024 * 1024;\n            case 'gb':\n            case 'g':\n                return $size * 1024 * 1024 * 1024;\n            default:\n                return 0;\n        }\n    }\n\n    /**\n     * Checks if upload max file size matches the given range.\n     * @param string|null $min verbose file size minimum required value, pass null to skip minimum check.\n     * @param string|null $max verbose file size maximum required value, pass null to skip maximum check.\n     * @return bool success.\n     */\n    public function checkUploadMaxFileSize($min = null, $max = null)\n    {\n        $postMaxSize = ini_get('post_max_size');\n        $uploadMaxFileSize = ini_get('upload_max_filesize');\n        if ($min !== null) {\n            $minCheckResult = $this->compareByteSize($postMaxSize, $min, '>=') && $this->compareByteSize($uploadMaxFileSize, $min, '>=');\n        } else {\n            $minCheckResult = true;\n        }\n        if ($max !== null) {\n            $maxCheckResult = $this->compareByteSize($postMaxSize, $max, '<=') && $this->compareByteSize($uploadMaxFileSize, $max, '<=');\n        } else {\n            $maxCheckResult = true;\n        }\n\n        return ($minCheckResult && $maxCheckResult);\n    }\n\n    /**\n     * Renders a view file.\n     * This method includes the view file as a PHP script\n     * and captures the display result if required.\n     * @param string $_viewFile_ view file\n     * @param array|null $_data_ data to be extracted and made available to the view file\n     * @param bool $_return_ whether the rendering result should be returned as a string\n     * @return string|null the rendering result. Null if the rendering result is not required.\n     */\n    public function renderViewFile($_viewFile_, $_data_ = null, $_return_ = false)\n    {\n        // we use special variable names here to avoid conflict when extracting data\n        if (is_array($_data_)) {\n            extract($_data_, EXTR_PREFIX_SAME, 'data');\n        } else {\n            $data = $_data_;\n        }\n        if ($_return_) {\n            ob_start();\n            ob_implicit_flush(false);\n            require $_viewFile_;\n\n            return ob_get_clean();\n        } else {\n            require $_viewFile_;\n        }\n\n        return null;\n    }\n\n    /**\n     * Normalizes requirement ensuring it has correct format.\n     * @param array $requirement raw requirement.\n     * @param int $requirementKey requirement key in the list.\n     * @return array normalized requirement.\n     */\n    public function normalizeRequirement($requirement, $requirementKey = 0)\n    {\n        if (!is_array($requirement)) {\n            $this->usageError('Requirement must be an array!');\n        }\n        if (!array_key_exists('condition', $requirement)) {\n            $this->usageError(\"Requirement '{$requirementKey}' has no condition!\");\n        } else {\n            $evalPrefix = 'eval:';\n            if (is_string($requirement['condition']) && strpos($requirement['condition'], $evalPrefix) === 0) {\n                $expression = substr($requirement['condition'], strlen($evalPrefix));\n                $requirement['condition'] = $this->evaluateExpression($expression);\n            }\n        }\n        if (!array_key_exists('name', $requirement)) {\n            $requirement['name'] = is_numeric($requirementKey) ? 'Requirement #' . $requirementKey : $requirementKey;\n        }\n        if (!array_key_exists('mandatory', $requirement)) {\n            if (array_key_exists('required', $requirement)) {\n                $requirement['mandatory'] = $requirement['required'];\n            } else {\n                $requirement['mandatory'] = false;\n            }\n        }\n        if (!array_key_exists('by', $requirement)) {\n            $requirement['by'] = 'Unknown';\n        }\n        if (!array_key_exists('memo', $requirement)) {\n            $requirement['memo'] = '';\n        }\n\n        return $requirement;\n    }\n\n    /**\n     * Displays a usage error.\n     * This method will then terminate the execution of the current application.\n     * @param string $message the error message\n     */\n    public function usageError($message)\n    {\n        echo \"Error: $message\\n\\n\";\n        exit(1);\n    }\n\n    /**\n     * Evaluates a PHP expression under the context of this class.\n     * @param string $expression a PHP expression to be evaluated.\n     * @return mixed the expression result.\n     */\n    public function evaluateExpression($expression)\n    {\n        return eval('return ' . $expression . ';');\n    }\n\n    /**\n     * Returns the server information.\n     * @return string server information.\n     */\n    public function getServerInfo()\n    {\n        return isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : '';\n    }\n\n    /**\n     * Returns the now date if possible in string representation.\n     * @return string now date.\n     */\n    public function getNowDate()\n    {\n        return date('Y-m-d H:i');\n    }\n}\n"
  },
  {
    "path": "framework/requirements/requirements.php",
    "content": "<?php\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n *\n * @var YiiRequirementChecker $this\n *\n * These are the Yii core requirements for the [[YiiRequirementChecker]] instance.\n * These requirements are mandatory for any Yii application.\n */\nreturn array(\n    array(\n        'name' => 'PHP version',\n        'mandatory' => true,\n        'condition' => version_compare(PHP_VERSION, '7.3.0', '>='),\n        'by' => '<a href=\"https://www.yiiframework.com\">Yii Framework</a>',\n        'memo' => 'PHP 7.3.0 or higher is required.',\n    ),\n    array(\n        'name' => 'Reflection extension',\n        'mandatory' => true,\n        'condition' => class_exists('Reflection', false),\n        'by' => '<a href=\"https://www.yiiframework.com\">Yii Framework</a>',\n    ),\n    array(\n        'name' => 'PCRE extension',\n        'mandatory' => true,\n        'condition' => extension_loaded('pcre'),\n        'by' => '<a href=\"https://www.yiiframework.com\">Yii Framework</a>',\n    ),\n    array(\n        'name' => 'SPL extension',\n        'mandatory' => true,\n        'condition' => extension_loaded('SPL'),\n        'by' => '<a href=\"https://www.yiiframework.com\">Yii Framework</a>',\n    ),\n    array(\n        'name' => 'Ctype extension',\n        'mandatory' => true,\n        'condition' => extension_loaded('ctype'),\n        'by' => '<a href=\"https://www.yiiframework.com\">Yii Framework</a>'\n    ),\n    array(\n        'name' => 'MBString extension',\n        'mandatory' => true,\n        'condition' => extension_loaded('mbstring'),\n        'by' => '<a href=\"https://www.php.net/manual/en/book.mbstring.php\">Multibyte string</a> processing',\n        'memo' => 'Required for multibyte encoding string processing.'\n    ),\n    array(\n        'name' => 'OpenSSL extension',\n        'mandatory' => false,\n        'condition' => extension_loaded('openssl'),\n        'by' => '<a href=\"https://www.yiiframework.com/doc-2.0/yii-base-security.html\">Security Component</a>',\n        'memo' => 'Required by encrypt and decrypt methods.'\n    ),\n    array(\n        'name' => 'Intl extension',\n        'mandatory' => false,\n        'condition' => $this->checkPhpExtensionVersion('intl', '1.0.2', '>='),\n        'by' => '<a href=\"https://www.php.net/manual/en/book.intl.php\">Internationalization</a> support',\n        'memo' => 'PHP Intl extension 1.0.2 or higher is required when you want to use advanced parameters formatting\n        in <code>Yii::t()</code>, non-latin languages with <code>Inflector::slug()</code>,\n        <abbr title=\"Internationalized domain names\">IDN</abbr>-feature of\n        <code>EmailValidator</code> or <code>UrlValidator</code> or the <code>yii\\i18n\\Formatter</code> class.'\n    ),\n    array(\n        'name' => 'ICU version',\n        'mandatory' => false,\n        'condition' => defined('INTL_ICU_VERSION') && version_compare(INTL_ICU_VERSION, '49', '>='),\n        'by' => '<a href=\"https://www.php.net/manual/en/book.intl.php\">Internationalization</a> support',\n        'memo' => 'ICU 49.0 or higher is required when you want to use <code>#</code> placeholder in plural rules\n        (for example, plural in\n        <a href=\\\"https://www.yiiframework.com/doc-2.0/yii-i18n-formatter.html#asRelativeTime%28%29-detail\\\">\n        Formatter::asRelativeTime()</a>) in the <code>yii\\i18n\\Formatter</code> class. Your current ICU version is ' .\n        (defined('INTL_ICU_VERSION') ? INTL_ICU_VERSION : '(ICU is missing)') . '.'\n    ),\n    array(\n        'name' => 'ICU Data version',\n        'mandatory' => false,\n        'condition' => defined('INTL_ICU_DATA_VERSION') && version_compare(INTL_ICU_DATA_VERSION, '49.1', '>='),\n        'by' => '<a href=\"https://www.php.net/manual/en/book.intl.php\">Internationalization</a> support',\n        'memo' => 'ICU Data 49.1 or higher is required when you want to use <code>#</code> placeholder in plural rules\n        (for example, plural in\n        <a href=\\\"https://www.yiiframework.com/doc-2.0/yii-i18n-formatter.html#asRelativeTime%28%29-detail\\\">\n        Formatter::asRelativeTime()</a>) in the <code>yii\\i18n\\Formatter</code> class. Your current ICU Data version is ' .\n        (defined('INTL_ICU_DATA_VERSION') ? INTL_ICU_DATA_VERSION : '(ICU Data is missing)') . '.'\n    ),\n    array(\n        'name' => 'Fileinfo extension',\n        'mandatory' => false,\n        'condition' => extension_loaded('fileinfo'),\n        'by' => '<a href=\"https://www.php.net/manual/en/book.fileinfo.php\">File Information</a>',\n        'memo' => 'Required for files upload to detect correct file mime-types.'\n    ),\n    array(\n        'name' => 'DOM extension',\n        'mandatory' => false,\n        'condition' => extension_loaded('dom'),\n        'by' => '<a href=\"https://www.php.net/manual/en/book.dom.php\">Document Object Model</a>',\n        'memo' => 'Required for REST API to send XML responses via <code>yii\\web\\XmlResponseFormatter</code>.'\n    ),\n    array(\n        'name' => 'IPv6 support',\n        'mandatory' => false,\n        'condition' => strlen(@inet_pton('2001:db8::1')) === 16,\n        'by' => 'IPv6 expansion in <a href=\"https://www.yiiframework.com/doc-2.0/yii-validators-ipvalidator.html\">IpValidator</a>',\n        'memo' => 'When <a href=\"https://www.yiiframework.com/doc-2.0/yii-validators-ipvalidator.html#$expandIPv6-detail\">IpValidator::expandIPv6</a>\n        property is set to <code>true</code>, PHP must support IPv6 protocol stack. Currently PHP constant <code>AF_INET6</code> is not defined\n        and IPv6 is probably unsupported.'\n    ),\n    array(\n        'name' => 'pcntl',\n        'mandatory' => false,\n        'condition' => extension_loaded('pcntl'),\n        'by' => '<a href=\"https://www.php.net/manual/de/book.pcntl.php\">Process Control</a>',\n        'memo' => 'Recommended for yii2-queue CLI operations'\n    )\n);\n"
  },
  {
    "path": "framework/requirements/views/console/index.php",
    "content": "<?php\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n *\n * @var YiiRequirementChecker $this\n * @var array $summary\n * @var array[] $requirements\n */\n\necho \"\\nYii Application Requirement Checker\\n\\n\";\n\necho \"This script checks if your server configuration meets the requirements\\n\";\necho \"for running Yii application.\\n\";\necho \"It checks if the server is running the right version of PHP,\\n\";\necho \"if appropriate PHP extensions have been loaded, and if php.ini file settings are correct.\\n\";\n\n$header = 'Check conclusion:';\necho \"\\n{$header}\\n\";\necho str_pad('', strlen($header), '-') . \"\\n\\n\";\n\nforeach ($requirements as $key => $requirement) {\n    if ($requirement['condition']) {\n        echo $requirement['name'] . \": OK\\n\";\n    } else {\n        echo $requirement['name'] . ': ' . ($requirement['mandatory'] ? 'FAILED!!!' : 'WARNING!!!') . \"\\n\";\n        echo 'Required by: ' . strip_tags($requirement['by']) . \"\\n\";\n        $memo = strip_tags($requirement['memo']);\n        if (!empty($memo)) {\n            echo 'Memo: ' . strip_tags($requirement['memo']) . \"\\n\";\n        }\n    }\n    echo \"\\n\";\n}\n\n$summaryString = 'Errors: ' . $summary['errors'] . '   Warnings: ' . $summary['warnings'] . '   Total checks: ' . $summary['total'];\necho str_pad('', strlen($summaryString), '-') . \"\\n\";\necho $summaryString;\n\necho \"\\n\\n\";\n"
  },
  {
    "path": "framework/requirements/views/web/css.php",
    "content": "<style type=\"text/css\">\n*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n:before,:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\nhtml{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}\nbody{margin:0;font-family:\"Helvetica Neue\",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}\nheader,main,footer{display:block}\na{background-color:transparent;color:#337ab7;text-decoration:none}\na:hover,a:focus{color:#23527c;text-decoration:underline}\na:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}\na:active,a:hover{outline:0}\nabbr[title]{border-bottom:1px dotted}\nabbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #777}\nstrong{font-weight:700}\nh1{font-size:36px;margin:.67em 0}\nh3{font-size:24px}\nh1,h3{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}\nh1,h3{margin-top:20px;margin-bottom:10px}\nhr{box-sizing:content-box;height:0;margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}\ncode{font-family:Menlo,Monaco,Consolas,\"Courier New\",monospace;padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}\ntable{border-collapse:collapse;border-spacing:0}\ntd,th{padding:0}\np{margin:0 0 10px}\n.container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}\ntable{background-color:transparent}\nth{text-align:left}\n.table{width:100%;max-width:100%;margin-bottom:20px}\n.table>tbody>tr>th,.table>tbody>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}\n.table>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}\n.table>tr:first-child>th,.table>tr:first-child>td{border-top:0}\n.table>tbody+tbody{border-top:2px solid #ddd}\n.table .table{background-color:#fff}\n.table-bordered{border:1px solid #ddd}\n.table-bordered>tbody>tr>th,.table-bordered>tbody>tr>td{border:1px solid #ddd}\n.table-bordered>tr>th,.table-bordered>tr>td{border-bottom-width:2px}\ntable td[class*=col-],table th[class*=col-]{position:static;float:none;display:table-cell}\n.table tr>td.active,.table tr>th.active,.table tr.active>td,.table tr.active>th{background-color:#f5f5f5;border-color:#e8e8e8}\n.table tr>td.success,.table tr>th.success,.table tr.success>td,.table tr.success>th{background-color:#dff0d8;border-color:#d6e9c6}\n.table tr>td.info,.table tr>th.info,.table tr.info>td,.table tr.info>th{background-color:#d9edf7;border-color:#c4ebf3}\n.table tr>td.warning,.table tr>th.warning,.table tr.warning>td,.table tr.warning>th{background-color:#fcf8e3;border-color:#faebcc}\n.table tr>td.danger,.table tr>th.danger,.table tr.danger>td,.table tr.danger>th{background-color:#f2dede;border-color:#ebccd1}\n.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}\n.alert>p{margin-bottom:0}.alert>p+p{margin-top:5px}\n.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#3c763d}\n.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#31708f}\n.alert-warning{background-color:#fcf8e3;border-color:#faebcc;color:#8a6d3b}\n.alert-danger{background-color:#f2dede;border-color:#ebccd1;color:#a94442}\n@media (min-width:768px){.container{width:750px}}\n@media (min-width:992px){.container{width:970px}}\n@media (min-width:1200px){.container{width:1170px}}\n@media print{\n    *,:before,:after{background:transparent!important;color:#000!important;box-shadow:none!important;text-shadow:none!important}\n    a,a:visited{text-decoration:underline}\n    a[href]:after{content:\" (\" attr(href) \")\"}\n    abbr[title]:after{content:\" (\" attr(title) \")\"}\n    a[href^=\"#\"]:after,a[href^=\"javascript:\"]:after{content:\"\"}\n    tr{page-break-inside:avoid}\n    p,h3{orphans:3;widows:3}\n    h3{page-break-after:avoid}\n    .table{border-collapse:collapse!important}\n    .table td,.table th{background-color:#fff!important}\n    .table-bordered th,.table-bordered td{border:1px solid #ddd!important}\n}\n</style>\n"
  },
  {
    "path": "framework/requirements/views/web/index.php",
    "content": "<?php\n/**\n * @var YiiRequirementChecker $this\n * @var array $summary\n * @var array[] $requirements\n */\n?><!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"utf-8\"/>\n    <title>Yii Application Requirement Checker</title>\n    <?php $this->renderViewFile(dirname(__FILE__) . '/css.php'); ?>\n</head>\n<body>\n<div class=\"container\">\n    <header>\n        <h1>Yii Application Requirement Checker</h1>\n    </header>\n    <hr>\n    <main>\n        <h3>Description</h3>\n        <p>\n        This script checks if your server configuration meets the requirements\n        for running Yii application.\n        It checks if the server is running the right version of PHP,\n        if appropriate PHP extensions have been loaded, and if php.ini file settings are correct.\n        </p>\n        <p>\n        There are two kinds of requirements being checked. Mandatory requirements are those that have to be met\n        to allow Yii to work as expected. There are also some optional requirements being checked which will\n        show you a warning when they do not meet. You can use Yii framework without them but some specific\n        functionality may be not available in this case.\n        </p>\n\n        <h3>Conclusion</h3>\n        <?php if ($summary['errors'] > 0): ?>\n            <div class=\"alert alert-danger\">\n                <strong>Unfortunately your server configuration does not satisfy the requirements by this application.<br>Please refer to the table below for detailed explanation.</strong>\n            </div>\n        <?php elseif ($summary['warnings'] > 0): ?>\n            <div class=\"alert alert-info\">\n                <strong>Your server configuration satisfies the minimum requirements by this application.<br>Please pay attention to the warnings listed below and check if your application will use the corresponding features.</strong>\n            </div>\n        <?php else: ?>\n            <div class=\"alert alert-success\">\n                <strong>Congratulations! Your server configuration satisfies all requirements.</strong>\n            </div>\n        <?php endif; ?>\n\n        <h3>Details</h3>\n\n        <table class=\"table table-bordered\">\n            <tr><th>Name</th><th>Result</th><th>Required By</th><th>Memo</th></tr>\n            <?php foreach ($requirements as $requirement): ?>\n            <tr class=\"<?php echo $requirement['condition'] ? 'success' : ($requirement['mandatory'] ? 'danger' : 'warning') ?>\">\n                <td>\n                <?php echo $requirement['name'] ?>\n                </td>\n                <td>\n                <span class=\"result\"><?php echo $requirement['condition'] ? 'Passed' : ($requirement['mandatory'] ? 'Failed' : 'Warning') ?></span>\n                </td>\n                <td>\n                <?php echo $requirement['by'] ?>\n                </td>\n                <td>\n                <?php echo $requirement['memo'] ?>\n                </td>\n            </tr>\n            <?php endforeach; ?>\n        </table>\n    </main>\n    <hr>\n    <footer>\n        <p>Server: <?php echo $this->getServerInfo() . ' ' . $this->getNowDate() ?></p>\n        <p>Powered by <a href=\"https://www.yiiframework.com/\" rel=\"external\">Yii Framework</a></p>\n    </footer>\n</div>\n</body>\n</html>\n"
  },
  {
    "path": "framework/rest/Action.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\rest;\n\nuse yii\\base\\InvalidConfigException;\nuse yii\\db\\ActiveRecordInterface;\nuse yii\\web\\NotFoundHttpException;\nuse yii\\base\\Action as BaseAction;\n\n/**\n * Action is the base class for action classes that implement RESTful API.\n *\n * For more details and usage information on Action, see the [guide article on rest controllers](guide:rest-controllers).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Controller = Controller\n * @extends BaseAction<T>\n */\nclass Action extends BaseAction\n{\n    /**\n     * @var string class name of the model which will be handled by this action.\n     * The model class must implement [[ActiveRecordInterface]].\n     * This property must be set.\n     */\n    public $modelClass;\n    /**\n     * @var callable|null a PHP callable that will be called to return the model corresponding\n     * to the specified primary key value. If not set, [[findModel()]] will be used instead.\n     * The signature of the callable should be:\n     *\n     * ```\n     * function ($id, $action) {\n     *     // $id is the primary key value. If composite primary key, the key values\n     *     // will be separated by comma.\n     *     // $action is the action object currently running\n     * }\n     * ```\n     *\n     * The callable should return the model found, or throw an exception if not found.\n     */\n    public $findModel;\n    /**\n     * @var callable|null a PHP callable that will be called when running an action to determine\n     * if the current user has the permission to execute the action. If not set, the access\n     * check will not be performed. The signature of the callable should be as follows,\n     *\n     * ```\n     * function ($action, $model = null) {\n     *     // $model is the requested model instance.\n     *     // If null, it means no specific model (e.g. IndexAction)\n     * }\n     * ```\n     */\n    public $checkAccess;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        if ($this->modelClass === null) {\n            throw new InvalidConfigException(get_class($this) . '::$modelClass must be set.');\n        }\n    }\n\n    /**\n     * Returns the data model based on the primary key given.\n     * If the data model is not found, a 404 HTTP exception will be raised.\n     * @param string $id the ID of the model to be loaded. If the model has a composite primary key,\n     * the ID must be a string of the primary key values separated by commas.\n     * The order of the primary key values should follow that returned by the `primaryKey()` method\n     * of the model.\n     * @return ActiveRecordInterface the model found\n     * @throws NotFoundHttpException if the model cannot be found\n     */\n    public function findModel($id)\n    {\n        if ($this->findModel !== null) {\n            return call_user_func($this->findModel, $id, $this);\n        }\n\n        /** @var ActiveRecordInterface $modelClass */\n        $modelClass = $this->modelClass;\n        $keys = $modelClass::primaryKey();\n        if (count($keys) > 1) {\n            $values = explode(',', $id);\n            if (count($keys) === count($values)) {\n                $model = $modelClass::findOne(array_combine($keys, $values));\n            }\n        } elseif ($id !== null) {\n            $model = $modelClass::findOne($id);\n        }\n\n        if (isset($model)) {\n            return $model;\n        }\n\n        throw new NotFoundHttpException(\"Object not found: $id\");\n    }\n}\n"
  },
  {
    "path": "framework/rest/ActiveController.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\rest;\n\nuse yii\\base\\InvalidConfigException;\nuse yii\\base\\Model;\nuse yii\\base\\Module;\nuse yii\\web\\ForbiddenHttpException;\n\n/**\n * ActiveController implements a common set of actions for supporting RESTful access to ActiveRecord.\n *\n * The class of the ActiveRecord should be specified via [[modelClass]], which must implement [[\\yii\\db\\ActiveRecordInterface]].\n * By default, the following actions are supported:\n *\n * - `index`: list of models\n * - `view`: return the details of a model\n * - `create`: create a new model\n * - `update`: update an existing model\n * - `delete`: delete an existing model\n * - `options`: return the allowed HTTP methods\n *\n * You may disable some of these actions by overriding [[actions()]] and unsetting the corresponding actions.\n *\n * To add a new action, either override [[actions()]] by appending a new action class or write a new action method.\n * Make sure you also override [[verbs()]] to properly declare what HTTP methods are allowed by the new action.\n *\n * You should usually override [[checkAccess()]] to check whether the current user has the privilege to perform\n * the specified action against the specified model.\n *\n * For more details and usage information on ActiveController, see the [guide article on rest controllers](guide:rest-controllers).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Module = Module\n * @extends Controller<T>\n */\nclass ActiveController extends Controller\n{\n    /**\n     * @var string the model class name. This property must be set.\n     */\n    public $modelClass;\n    /**\n     * @var string the scenario used for updating a model.\n     * @see \\yii\\base\\Model::scenarios()\n     */\n    public $updateScenario = Model::SCENARIO_DEFAULT;\n    /**\n     * @var string the scenario used for creating a model.\n     * @see \\yii\\base\\Model::scenarios()\n     */\n    public $createScenario = Model::SCENARIO_DEFAULT;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        if ($this->modelClass === null) {\n            throw new InvalidConfigException('The \"modelClass\" property must be set.');\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function actions()\n    {\n        return [\n            'index' => [\n                'class' => 'yii\\rest\\IndexAction',\n                'modelClass' => $this->modelClass,\n                'checkAccess' => [$this, 'checkAccess'],\n            ],\n            'view' => [\n                'class' => 'yii\\rest\\ViewAction',\n                'modelClass' => $this->modelClass,\n                'checkAccess' => [$this, 'checkAccess'],\n            ],\n            'create' => [\n                'class' => 'yii\\rest\\CreateAction',\n                'modelClass' => $this->modelClass,\n                'checkAccess' => [$this, 'checkAccess'],\n                'scenario' => $this->createScenario,\n            ],\n            'update' => [\n                'class' => 'yii\\rest\\UpdateAction',\n                'modelClass' => $this->modelClass,\n                'checkAccess' => [$this, 'checkAccess'],\n                'scenario' => $this->updateScenario,\n            ],\n            'delete' => [\n                'class' => 'yii\\rest\\DeleteAction',\n                'modelClass' => $this->modelClass,\n                'checkAccess' => [$this, 'checkAccess'],\n            ],\n            'options' => [\n                'class' => 'yii\\rest\\OptionsAction',\n            ],\n        ];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function verbs()\n    {\n        return [\n            'index' => ['GET', 'HEAD'],\n            'view' => ['GET', 'HEAD'],\n            'create' => ['POST'],\n            'update' => ['PUT', 'PATCH'],\n            'delete' => ['DELETE'],\n        ];\n    }\n\n    /**\n     * Checks the privilege of the current user.\n     *\n     * This method should be overridden to check whether the current user has the privilege\n     * to run the specified action against the specified data model.\n     * If the user does not have access, a [[ForbiddenHttpException]] should be thrown.\n     *\n     * @param string $action the ID of the action to be executed\n     * @param object|null $model the model to be accessed. If null, it means no specific model is being accessed.\n     * @param array $params additional parameters\n     * @throws ForbiddenHttpException if the user does not have access\n     */\n    public function checkAccess($action, $model = null, $params = [])\n    {\n    }\n}\n"
  },
  {
    "path": "framework/rest/Controller.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\rest;\n\nuse Yii;\nuse yii\\filters\\auth\\CompositeAuth;\nuse yii\\filters\\ContentNegotiator;\nuse yii\\filters\\RateLimiter;\nuse yii\\filters\\VerbFilter;\nuse yii\\web\\Response;\nuse yii\\web\\Controller as WebController;\nuse yii\\base\\Module;\n\n/**\n * Controller is the base class for RESTful API controller classes.\n *\n * Controller implements the following steps in a RESTful API request handling cycle:\n *\n * 1. Resolving response format (see [[ContentNegotiator]]);\n * 2. Validating request method (see [[verbs()]]).\n * 3. Authenticating user (see [[\\yii\\filters\\auth\\AuthInterface]]);\n * 4. Rate limiting (see [[RateLimiter]]);\n * 5. Formatting response data (see [[serializeData()]]).\n *\n * For more details and usage information on Controller, see the [guide article on rest controllers](guide:rest-controllers).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Module = Module\n * @extends WebController<T>\n */\nclass Controller extends WebController\n{\n    /**\n     * @var string|array the configuration for creating the serializer that formats the response data.\n     */\n    public $serializer = 'yii\\rest\\Serializer';\n    /**\n     * {@inheritdoc}\n     */\n    public $enableCsrfValidation = false;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function behaviors()\n    {\n        return [\n            'contentNegotiator' => [\n                'class' => ContentNegotiator::className(),\n                'formats' => [\n                    'application/json' => Response::FORMAT_JSON,\n                    'application/xml' => Response::FORMAT_XML,\n                ],\n            ],\n            'verbFilter' => [\n                'class' => VerbFilter::className(),\n                'actions' => $this->verbs(),\n            ],\n            'authenticator' => [\n                'class' => CompositeAuth::className(),\n            ],\n            'rateLimiter' => [\n                'class' => RateLimiter::className(),\n            ],\n        ];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function afterAction($action, $result)\n    {\n        $result = parent::afterAction($action, $result);\n        return $this->serializeData($result);\n    }\n\n    /**\n     * Declares the allowed HTTP verbs.\n     * Please refer to [[VerbFilter::actions]] on how to declare the allowed verbs.\n     * @return array the allowed HTTP verbs.\n     */\n    protected function verbs()\n    {\n        return [];\n    }\n\n    /**\n     * Serializes the specified data.\n     * The default implementation will create a serializer based on the configuration given by [[serializer]].\n     * It then uses the serializer to serialize the given data.\n     * @param mixed $data the data to be serialized\n     * @return mixed the serialized data.\n     */\n    protected function serializeData($data)\n    {\n        return Yii::createObject($this->serializer)->serialize($data);\n    }\n}\n"
  },
  {
    "path": "framework/rest/CreateAction.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\rest;\n\nuse Yii;\nuse yii\\base\\Model;\nuse yii\\helpers\\Url;\nuse yii\\web\\ServerErrorHttpException;\n\n/**\n * CreateAction implements the API endpoint for creating a new model from the given data.\n *\n * For more details and usage information on CreateAction, see the [guide article on rest controllers](guide:rest-controllers).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Controller = Controller\n * @extends Action<T>\n */\nclass CreateAction extends Action\n{\n    /**\n     * @var string the scenario to be assigned to the new model before it is validated and saved.\n     */\n    public $scenario = Model::SCENARIO_DEFAULT;\n    /**\n     * @var string the name of the view action. This property is needed to create the URL when the model is successfully created.\n     */\n    public $viewAction = 'view';\n\n\n    /**\n     * Creates a new model.\n     * @return \\yii\\db\\ActiveRecordInterface the model newly created\n     * @throws ServerErrorHttpException if there is any error when creating the model\n     */\n    public function run()\n    {\n        if ($this->checkAccess) {\n            call_user_func($this->checkAccess, $this->id);\n        }\n\n        /** @var \\yii\\db\\ActiveRecord $model */\n        $model = new $this->modelClass([\n            'scenario' => $this->scenario,\n        ]);\n\n        $model->load(Yii::$app->getRequest()->getBodyParams(), '');\n        if ($model->save()) {\n            $response = Yii::$app->getResponse();\n            $response->setStatusCode(201);\n            $id = implode(',', $model->getPrimaryKey(true));\n            $response->getHeaders()->set('Location', Url::toRoute([$this->viewAction, 'id' => $id], true));\n        } elseif (!$model->hasErrors()) {\n            throw new ServerErrorHttpException('Failed to create the object for unknown reason.');\n        }\n\n        return $model;\n    }\n}\n"
  },
  {
    "path": "framework/rest/DeleteAction.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\rest;\n\nuse Yii;\nuse yii\\web\\ServerErrorHttpException;\n\n/**\n * DeleteAction implements the API endpoint for deleting a model.\n *\n * For more details and usage information on DeleteAction, see the [guide article on rest controllers](guide:rest-controllers).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Controller = Controller\n * @extends Action<T>\n */\nclass DeleteAction extends Action\n{\n    /**\n     * Deletes a model.\n     * @param mixed $id id of the model to be deleted.\n     * @throws ServerErrorHttpException on failure.\n     */\n    public function run($id)\n    {\n        $model = $this->findModel($id);\n\n        if ($this->checkAccess) {\n            call_user_func($this->checkAccess, $this->id, $model);\n        }\n\n        if ($model->delete() === false) {\n            throw new ServerErrorHttpException('Failed to delete the object for unknown reason.');\n        }\n\n        Yii::$app->getResponse()->setStatusCode(204);\n    }\n}\n"
  },
  {
    "path": "framework/rest/IndexAction.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\rest;\n\nuse Yii;\nuse yii\\data\\ActiveDataProvider;\nuse yii\\data\\DataFilter;\nuse yii\\data\\Pagination;\nuse yii\\data\\Sort;\nuse yii\\helpers\\ArrayHelper;\n\n/**\n * IndexAction implements the API endpoint for listing multiple models.\n *\n * For more details and usage information on IndexAction, see the [guide article on rest controllers](guide:rest-controllers).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Controller = Controller\n * @extends Action<T>\n */\nclass IndexAction extends Action\n{\n    /**\n     * @var callable|null a PHP callable that will be called to prepare a data provider that\n     * should return a collection of the models. If not set, [[prepareDataProvider()]] will be used instead.\n     * The signature of the callable should be:\n     *\n     * ```\n     * function (IndexAction $action) {\n     *     // $action is the action object currently running\n     * }\n     * ```\n     *\n     * The callable should return an instance of [[ActiveDataProvider]].\n     *\n     * If [[dataFilter]] is set the result of [[DataFilter::build()]] will be passed to the callable as a second parameter.\n     * In this case the signature of the callable should be the following:\n     *\n     * ```\n     * function (IndexAction $action, mixed $filter) {\n     *     // $action is the action object currently running\n     *     // $filter the built filter condition\n     * }\n     * ```\n     */\n    public $prepareDataProvider;\n    /**\n     * @var callable a PHP callable that will be called to prepare query in prepareDataProvider.\n     * Should return $query.\n     * For example:\n     *\n     * ```\n     * function ($query, $requestParams) {\n     *     $query->andFilterWhere(['id' => 1]);\n     *     ...\n     *     return $query;\n     * }\n     * ```\n     *\n     * @since 2.0.42\n     */\n    public $prepareSearchQuery;\n    /**\n     * @var DataFilter|null data filter to be used for the search filter composition.\n     * You must set up this field explicitly in order to enable filter processing.\n     * For example:\n     *\n     * ```\n     * [\n     *     'class' => 'yii\\data\\ActiveDataFilter',\n     *     'searchModel' => function () {\n     *         return (new \\yii\\base\\DynamicModel(['id' => null, 'name' => null, 'price' => null]))\n     *             ->addRule('id', 'integer')\n     *             ->addRule('name', 'trim')\n     *             ->addRule('name', 'string')\n     *             ->addRule('price', 'number');\n     *     },\n     * ]\n     * ```\n     *\n     * @see DataFilter\n     *\n     * @since 2.0.13\n     */\n    public $dataFilter;\n    /**\n     * @var array|Pagination|false The pagination to be used by [[prepareDataProvider()]].\n     * If this is `false`, it means pagination is disabled.\n     * Note: if a Pagination object is passed, it's `params` will be set to the request parameters.\n     * @see Pagination\n     * @since 2.0.45\n     */\n    public $pagination = [];\n    /**\n     * @var array|Sort|false The sorting to be used by [[prepareDataProvider()]].\n     * If this is `false`, it means sorting is disabled.\n     * Note: if a Sort object is passed, it's `params` will be set to the request parameters.\n     * @see Sort\n     * @since 2.0.45\n     */\n    public $sort = [];\n\n\n    /**\n     * @return ActiveDataProvider\n     */\n    public function run()\n    {\n        if ($this->checkAccess) {\n            call_user_func($this->checkAccess, $this->id);\n        }\n\n        return $this->prepareDataProvider();\n    }\n\n    /**\n     * Prepares the data provider that should return the requested collection of the models.\n     * @return ActiveDataProvider\n     */\n    protected function prepareDataProvider()\n    {\n        $requestParams = Yii::$app->getRequest()->getBodyParams();\n        if (empty($requestParams)) {\n            $requestParams = Yii::$app->getRequest()->getQueryParams();\n        }\n\n        $filter = null;\n        if ($this->dataFilter !== null) {\n            $this->dataFilter = Yii::createObject($this->dataFilter);\n            if ($this->dataFilter->load($requestParams)) {\n                $filter = $this->dataFilter->build();\n                if ($filter === false) {\n                    return $this->dataFilter;\n                }\n            }\n        }\n\n        if ($this->prepareDataProvider !== null) {\n            return call_user_func($this->prepareDataProvider, $this, $filter);\n        }\n\n        /** @var \\yii\\db\\BaseActiveRecord $modelClass */\n        $modelClass = $this->modelClass;\n\n        $query = $modelClass::find();\n        if (!empty($filter)) {\n            $query->andWhere($filter);\n        }\n        if (is_callable($this->prepareSearchQuery)) {\n            $query = call_user_func($this->prepareSearchQuery, $query, $requestParams);\n        }\n\n        if (is_array($this->pagination)) {\n            $pagination = ArrayHelper::merge(\n                [\n                    'params' => $requestParams,\n                ],\n                $this->pagination\n            );\n        } else {\n            $pagination = $this->pagination;\n            if ($this->pagination instanceof Pagination) {\n                $pagination->params = $requestParams;\n            }\n        }\n\n        if (is_array($this->sort)) {\n            $sort = ArrayHelper::merge(\n                [\n                    'params' => $requestParams,\n                ],\n                $this->sort\n            );\n        } else {\n            $sort = $this->sort;\n            if ($this->sort instanceof Sort) {\n                $sort->params = $requestParams;\n            }\n        }\n\n        return Yii::createObject([\n            'class' => ActiveDataProvider::className(),\n            'query' => $query,\n            'pagination' => $pagination,\n            'sort' => $sort,\n        ]);\n    }\n}\n"
  },
  {
    "path": "framework/rest/OptionsAction.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\rest;\n\nuse yii\\base\\Action as BaseAction;\nuse Yii;\n\n/**\n * OptionsAction responds to the OPTIONS request by sending back an `Allow` header.\n *\n * For more details and usage information on OptionsAction, see the [guide article on rest controllers](guide:rest-controllers).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Controller = Controller\n * @extends BaseAction<T>\n */\nclass OptionsAction extends BaseAction\n{\n    /**\n     * @var array the HTTP verbs that are supported by the collection URL\n     */\n    public $collectionOptions = ['GET', 'POST', 'HEAD', 'OPTIONS'];\n    /**\n     * @var array the HTTP verbs that are supported by the resource URL\n     */\n    public $resourceOptions = ['GET', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'];\n\n\n    /**\n     * Responds to the OPTIONS request.\n     * @param string|null $id\n     */\n    public function run($id = null)\n    {\n        if (Yii::$app->getRequest()->getMethod() !== 'OPTIONS') {\n            Yii::$app->getResponse()->setStatusCode(405);\n        }\n        $options = $id === null ? $this->collectionOptions : $this->resourceOptions;\n        $headers = Yii::$app->getResponse()->getHeaders();\n        $headers->set('Allow', implode(', ', $options));\n        $headers->set('Access-Control-Allow-Methods', implode(', ', $options));\n    }\n}\n"
  },
  {
    "path": "framework/rest/Serializer.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\rest;\n\nuse Yii;\nuse yii\\base\\Arrayable;\nuse yii\\base\\Component;\nuse yii\\base\\Model;\nuse yii\\data\\DataProviderInterface;\nuse yii\\data\\Pagination;\nuse yii\\helpers\\ArrayHelper;\nuse yii\\web\\Link;\nuse yii\\web\\Request;\nuse yii\\web\\Response;\n\n/**\n * Serializer converts resource objects and collections into array representation.\n *\n * Serializer is mainly used by REST controllers to convert different objects into array representation\n * so that they can be further turned into different formats, such as JSON, XML, by response formatters.\n *\n * The default implementation handles resources as [[Model]] objects and collections as objects\n * implementing [[DataProviderInterface]]. You may override [[serialize()]] to handle more types.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Serializer extends Component\n{\n    /**\n     * @var string the name of the query parameter containing the information about which fields should be returned\n     * for a [[Model]] object. If the parameter is not provided or empty, the default set of fields as defined\n     * by [[Model::fields()]] will be returned.\n     */\n    public $fieldsParam = 'fields';\n    /**\n     * @var string the name of the query parameter containing the information about which fields should be returned\n     * in addition to those listed in [[fieldsParam]] for a resource object.\n     */\n    public $expandParam = 'expand';\n    /**\n     * @var string the name of the HTTP header containing the information about total number of data items.\n     * This is used when serving a resource collection with pagination.\n     */\n    public $totalCountHeader = 'X-Pagination-Total-Count';\n    /**\n     * @var string the name of the HTTP header containing the information about total number of pages of data.\n     * This is used when serving a resource collection with pagination.\n     */\n    public $pageCountHeader = 'X-Pagination-Page-Count';\n    /**\n     * @var string the name of the HTTP header containing the information about the current page number (1-based).\n     * This is used when serving a resource collection with pagination.\n     */\n    public $currentPageHeader = 'X-Pagination-Current-Page';\n    /**\n     * @var string the name of the HTTP header containing the information about the number of data items in each page.\n     * This is used when serving a resource collection with pagination.\n     */\n    public $perPageHeader = 'X-Pagination-Per-Page';\n    /**\n     * @var string|null the name of the envelope (e.g. `items`) for returning the resource objects in a collection.\n     * This is used when serving a resource collection. When this is set and pagination is enabled, the serializer\n     * will return a collection in the following format:\n     *\n     * ```\n     * [\n     *     'items' => [...],  // assuming collectionEnvelope is \"items\"\n     *     '_links' => {  // pagination links as returned by Pagination::getLinks()\n     *         'self' => '...',\n     *         'next' => '...',\n     *         'last' => '...',\n     *     },\n     *     '_meta' => {  // meta information as returned by Pagination::toArray()\n     *         'totalCount' => 100,\n     *         'pageCount' => 5,\n     *         'currentPage' => 1,\n     *         'perPage' => 20,\n     *     },\n     * ]\n     * ```\n     *\n     * If this property is not set, the resource arrays will be directly returned without using envelope.\n     * The pagination information as shown in `_links` and `_meta` can be accessed from the response HTTP headers.\n     */\n    public $collectionEnvelope;\n    /**\n     * @var string the name of the envelope (e.g. `_links`) for returning the links objects.\n     * It takes effect only, if `collectionEnvelope` is set.\n     * @since 2.0.4\n     */\n    public $linksEnvelope = '_links';\n    /**\n     * @var string the name of the envelope (e.g. `_meta`) for returning the pagination object.\n     * It takes effect only, if `collectionEnvelope` is set.\n     * @since 2.0.4\n     */\n    public $metaEnvelope = '_meta';\n    /**\n     * @var Request|null the current request. If not set, the `request` application component will be used.\n     */\n    public $request;\n    /**\n     * @var Response|null the response to be sent. If not set, the `response` application component will be used.\n     */\n    public $response;\n    /**\n     * @var bool whether to preserve array keys when serializing collection data.\n     * Set this to `true` to allow serialization of a collection as a JSON object where array keys are\n     * used to index the model objects. The default is to serialize all collections as array, regardless\n     * of how the array is indexed.\n     * @see serializeDataProvider()\n     * @since 2.0.10\n     */\n    public $preserveKeys = false;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        if ($this->request === null) {\n            $this->request = Yii::$app->getRequest();\n        }\n        if ($this->response === null) {\n            $this->response = Yii::$app->getResponse();\n        }\n    }\n\n    /**\n     * Serializes the given data into a format that can be easily turned into other formats.\n     * This method mainly converts the objects of recognized types into array representation.\n     * It will not do conversion for unknown object types or non-object data.\n     * The default implementation will handle [[Model]], [[DataProviderInterface]] and [\\JsonSerializable](https://www.php.net/manual/en/class.jsonserializable.php).\n     * You may override this method to support more object types.\n     * @param mixed $data the data to be serialized.\n     * @return mixed the converted data.\n     */\n    public function serialize($data)\n    {\n        if ($data instanceof Model && $data->hasErrors()) {\n            return $this->serializeModelErrors($data);\n        } elseif ($data instanceof Arrayable) {\n            return $this->serializeModel($data);\n        } elseif ($data instanceof \\JsonSerializable) {\n            return $data->jsonSerialize();\n        } elseif ($data instanceof DataProviderInterface) {\n            return $this->serializeDataProvider($data);\n        } elseif (is_array($data)) {\n            $serializedArray = [];\n            foreach ($data as $key => $value) {\n                $serializedArray[$key] = $this->serialize($value);\n            }\n            return $serializedArray;\n        }\n\n        return $data;\n    }\n\n    /**\n     * @return array the names of the requested fields. The first element is an array\n     * representing the list of default fields requested, while the second element is\n     * an array of the extra fields requested in addition to the default fields.\n     * @see Model::fields()\n     * @see Model::extraFields()\n     */\n    protected function getRequestedFields()\n    {\n        $fields = $this->request->get($this->fieldsParam);\n        $expand = $this->request->get($this->expandParam);\n\n        return [\n            is_string($fields) ? preg_split('/\\s*,\\s*/', $fields, -1, PREG_SPLIT_NO_EMPTY) : [],\n            is_string($expand) ? preg_split('/\\s*,\\s*/', $expand, -1, PREG_SPLIT_NO_EMPTY) : [],\n        ];\n    }\n\n    /**\n     * Serializes a data provider.\n     * @param DataProviderInterface $dataProvider\n     * @return array|null the array representation of the data provider.\n     */\n    protected function serializeDataProvider($dataProvider)\n    {\n        if ($this->preserveKeys) {\n            $models = $dataProvider->getModels();\n        } else {\n            $models = array_values($dataProvider->getModels());\n        }\n        $models = $this->serializeModels($models);\n\n        if (($pagination = $dataProvider->getPagination()) !== false) {\n            $this->addPaginationHeaders($pagination);\n        }\n\n        if ($this->request->getIsHead()) {\n            return null;\n        } elseif ($this->collectionEnvelope === null) {\n            return $models;\n        }\n\n        $result = [\n            $this->collectionEnvelope => $models,\n        ];\n        if ($pagination !== false) {\n            return array_merge($result, $this->serializePagination($pagination));\n        }\n\n        return $result;\n    }\n\n    /**\n     * Serializes a pagination into an array.\n     * @param Pagination $pagination\n     * @return array the array representation of the pagination\n     * @see addPaginationHeaders()\n     */\n    protected function serializePagination($pagination)\n    {\n        return [\n            $this->linksEnvelope => Link::serialize($pagination->getLinks(true)),\n            $this->metaEnvelope => [\n                'totalCount' => $pagination->totalCount,\n                'pageCount' => $pagination->getPageCount(),\n                'currentPage' => $pagination->getPage() + 1,\n                'perPage' => $pagination->getPageSize(),\n            ],\n        ];\n    }\n\n    /**\n     * Adds HTTP headers about the pagination to the response.\n     * @param Pagination $pagination\n     */\n    protected function addPaginationHeaders($pagination)\n    {\n        $links = [];\n        foreach ($pagination->getLinks(true) as $rel => $url) {\n            $links[] = \"<$url>; rel=$rel\";\n        }\n\n        $this->response->getHeaders()\n            ->set($this->totalCountHeader, $pagination->totalCount)\n            ->set($this->pageCountHeader, $pagination->getPageCount())\n            ->set($this->currentPageHeader, $pagination->getPage() + 1)\n            ->set($this->perPageHeader, $pagination->pageSize)\n            ->set('Link', implode(', ', $links));\n    }\n\n    /**\n     * Serializes a model object.\n     * @param Arrayable $model\n     * @return array|null the array representation of the model\n     */\n    protected function serializeModel($model)\n    {\n        if ($this->request->getIsHead()) {\n            return null;\n        }\n\n        list($fields, $expand) = $this->getRequestedFields();\n        return $model->toArray($fields, $expand);\n    }\n\n    /**\n     * Serializes the validation errors in a model.\n     * @param Model $model\n     * @return array the array representation of the errors\n     */\n    protected function serializeModelErrors($model)\n    {\n        $this->response->setStatusCode(422, 'Data Validation Failed.');\n        $result = [];\n        foreach ($model->getFirstErrors() as $name => $message) {\n            $result[] = [\n                'field' => $name,\n                'message' => $message,\n            ];\n        }\n\n        return $result;\n    }\n\n    /**\n     * Serializes a set of models.\n     * @param array $models\n     * @return array the array representation of the models\n     */\n    protected function serializeModels(array $models)\n    {\n        foreach ($models as $i => $model) {\n            if ($model instanceof Arrayable) {\n                $models[$i] = $this->serializeModel($model);\n            } elseif (is_array($model)) {\n                $models[$i] = ArrayHelper::toArray($model);\n            }\n        }\n\n        return $models;\n    }\n}\n"
  },
  {
    "path": "framework/rest/UpdateAction.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\rest;\n\nuse Yii;\nuse yii\\base\\Model;\nuse yii\\db\\ActiveRecord;\nuse yii\\web\\ServerErrorHttpException;\n\n/**\n * UpdateAction implements the API endpoint for updating a model.\n *\n * For more details and usage information on UpdateAction, see the [guide article on rest controllers](guide:rest-controllers).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Controller = Controller\n * @extends Action<T>\n */\nclass UpdateAction extends Action\n{\n    /**\n     * @var string the scenario to be assigned to the model before it is validated and updated.\n     */\n    public $scenario = Model::SCENARIO_DEFAULT;\n\n\n    /**\n     * Updates an existing model.\n     * @param string $id the primary key of the model.\n     * @return \\yii\\db\\ActiveRecordInterface the model being updated\n     * @throws ServerErrorHttpException if there is any error when updating the model\n     */\n    public function run($id)\n    {\n        /** @var ActiveRecord $model */\n        $model = $this->findModel($id);\n\n        if ($this->checkAccess) {\n            call_user_func($this->checkAccess, $this->id, $model);\n        }\n\n        $model->scenario = $this->scenario;\n        $model->load(Yii::$app->getRequest()->getBodyParams(), '');\n        if ($model->save() === false && !$model->hasErrors()) {\n            throw new ServerErrorHttpException('Failed to update the object for unknown reason.');\n        }\n\n        return $model;\n    }\n}\n"
  },
  {
    "path": "framework/rest/UrlRule.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\rest;\n\nuse Yii;\nuse yii\\base\\InvalidConfigException;\nuse yii\\helpers\\Inflector;\nuse yii\\web\\CompositeUrlRule;\nuse yii\\web\\UrlRule as WebUrlRule;\nuse yii\\web\\UrlRuleInterface;\n\n/**\n * UrlRule is provided to simplify the creation of URL rules for RESTful API support.\n *\n * The simplest usage of UrlRule is to declare a rule like the following in the application configuration,\n *\n * ```\n * [\n *     'class' => 'yii\\rest\\UrlRule',\n *     'controller' => 'user',\n * ]\n * ```\n *\n * The above code will create a whole set of URL rules supporting the following RESTful API endpoints:\n *\n * - `'PUT,PATCH users/<id>' => 'user/update'`: update a user\n * - `'DELETE users/<id>' => 'user/delete'`: delete a user\n * - `'GET,HEAD users/<id>' => 'user/view'`: return the details/overview/options of a user\n * - `'POST users' => 'user/create'`: create a new user\n * - `'GET,HEAD users' => 'user/index'`: return a list/overview/options of users\n * - `'users/<id>' => 'user/options'`: process all unhandled verbs of a user\n * - `'users' => 'user/options'`: process all unhandled verbs of user collection\n *\n * You may configure [[only]] and/or [[except]] to disable some of the above rules.\n * You may configure [[patterns]] to completely redefine your own list of rules.\n * You may configure [[controller]] with multiple controller IDs to generate rules for all these controllers.\n * For example, the following code will disable the `delete` rule and generate rules for both `user` and `post` controllers:\n *\n * ```\n * [\n *     'class' => 'yii\\rest\\UrlRule',\n *     'controller' => ['user', 'post'],\n *     'except' => ['delete'],\n * ]\n * ```\n *\n * The property [[controller]] is required and should represent one or multiple controller IDs.\n * Each controller ID should be prefixed with the module ID if the controller is within a module.\n * The controller ID used in the pattern will be automatically pluralized (e.g. `user` becomes `users`\n * as shown in the above examples).\n *\n * For more details and usage information on UrlRule, see the [guide article on rest routing](guide:rest-routing).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass UrlRule extends CompositeUrlRule\n{\n    /**\n     * @var string|null the common prefix string shared by all patterns.\n     */\n    public $prefix;\n    /**\n     * @var string the suffix that will be assigned to [[\\yii\\web\\UrlRule::suffix]] for every generated rule.\n     */\n    public $suffix;\n    /**\n     * @var string|array the controller ID (e.g. `user`, `post-comment`) that the rules in this composite rule\n     * are dealing with. It should be prefixed with the module ID if the controller is within a module (e.g. `admin/user`).\n     *\n     * By default, the controller ID will be pluralized automatically when it is put in the patterns of the\n     * generated rules. If you want to explicitly specify how the controller ID should appear in the patterns,\n     * you may use an array with the array key being as the controller ID in the pattern, and the array value\n     * the actual controller ID. For example, `['u' => 'user']`.\n     *\n     * You may also pass multiple controller IDs as an array. If this is the case, this composite rule will\n     * generate applicable URL rules for EVERY specified controller. For example, `['user', 'post']`.\n     */\n    public $controller;\n    /**\n     * @var array list of acceptable actions. If not empty, only the actions within this array\n     * will have the corresponding URL rules created.\n     * @see patterns\n     */\n    public $only = [];\n    /**\n     * @var array list of actions that should be excluded. Any action found in this array\n     * will NOT have its URL rules created.\n     * @see patterns\n     */\n    public $except = [];\n    /**\n     * @var array patterns for supporting extra actions in addition to those listed in [[patterns]].\n     * The keys are the patterns and the values are the corresponding action IDs.\n     * These extra patterns will take precedence over [[patterns]].\n     */\n    public $extraPatterns = [];\n    /**\n     * @var array list of tokens that should be replaced for each pattern. The keys are the token names,\n     * and the values are the corresponding replacements.\n     * @see patterns\n     */\n    public $tokens = [\n        '{id}' => '<id:\\\\d[\\\\d,]*>',\n    ];\n    /**\n     * @var array list of possible patterns and the corresponding actions for creating the URL rules.\n     * The keys are the patterns and the values are the corresponding actions.\n     * The format of patterns is `Verbs Pattern`, where `Verbs` stands for a list of HTTP verbs separated\n     * by comma (without space). If `Verbs` is not specified, it means all verbs are allowed.\n     * `Pattern` is optional. It will be prefixed with [[prefix]]/[[controller]]/,\n     * and tokens in it will be replaced by [[tokens]].\n     */\n    public $patterns = [\n        'PUT,PATCH {id}' => 'update',\n        'DELETE {id}' => 'delete',\n        'GET,HEAD {id}' => 'view',\n        'POST' => 'create',\n        'GET,HEAD' => 'index',\n        '{id}' => 'options',\n        '' => 'options',\n    ];\n    /**\n     * @var array the default configuration for creating each URL rule contained by this rule.\n     */\n    public $ruleConfig = [\n        'class' => 'yii\\web\\UrlRule',\n    ];\n    /**\n     * @var bool whether to automatically pluralize the URL names for controllers.\n     * If true, a controller ID will appear in plural form in URLs. For example, `user` controller\n     * will appear as `users` in URLs.\n     * @see controller\n     */\n    public $pluralize = true;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        if (empty($this->controller)) {\n            throw new InvalidConfigException('\"controller\" must be set.');\n        }\n\n        $controllers = [];\n        foreach ((array) $this->controller as $urlName => $controller) {\n            if (is_int($urlName)) {\n                $urlName = $this->pluralize ? Inflector::pluralize($controller) : $controller;\n            }\n            $controllers[$urlName] = $controller;\n        }\n        $this->controller = $controllers;\n\n        $this->prefix = trim((string)$this->prefix, '/');\n\n        parent::init();\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function createRules()\n    {\n        $only = array_flip($this->only);\n        $except = array_flip($this->except);\n        $patterns = $this->extraPatterns + $this->patterns;\n        $rules = [];\n        foreach ($this->controller as $urlName => $controller) {\n            $prefix = trim($this->prefix . '/' . $urlName, '/');\n            foreach ($patterns as $pattern => $action) {\n                if (!isset($except[$action]) && (empty($only) || isset($only[$action]))) {\n                    $rules[$urlName][] = $this->createRule($pattern, $prefix, $controller . '/' . $action);\n                }\n            }\n        }\n\n        return $rules;\n    }\n\n    /**\n     * Creates a URL rule using the given pattern and action.\n     * @param string $pattern\n     * @param string $prefix\n     * @param string $action\n     * @return UrlRuleInterface\n     */\n    protected function createRule($pattern, $prefix, $action)\n    {\n        $verbs = 'GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS';\n        if (preg_match(\"/^((?:($verbs),)*($verbs))(?:\\\\s+(.*))?$/\", $pattern, $matches)) {\n            $verbs = explode(',', $matches[1]);\n            $pattern = isset($matches[4]) ? $matches[4] : '';\n        } else {\n            $verbs = [];\n        }\n\n        $config = $this->ruleConfig;\n        $config['verb'] = $verbs;\n        $config['pattern'] = rtrim($prefix . '/' . strtr($pattern, $this->tokens), '/');\n        $config['route'] = $action;\n        $config['suffix'] = $this->suffix;\n\n        return Yii::createObject($config);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function parseRequest($manager, $request)\n    {\n        $pathInfo = $request->getPathInfo();\n        if (\n            $this->prefix !== ''\n            && strpos($this->prefix, '<') === false\n            && strpos($pathInfo . '/', $this->prefix . '/') !== 0\n        ) {\n            return false;\n        }\n\n        foreach ($this->rules as $urlName => $rules) {\n            if (strpos($pathInfo, $urlName) !== false) {\n                foreach ($rules as $rule) {\n                    /** @var WebUrlRule $rule */\n                    $result = $rule->parseRequest($manager, $request);\n                    if (YII_DEBUG) {\n                        Yii::debug([\n                            'rule' => method_exists($rule, '__toString') ? $rule->__toString() : get_class($rule),\n                            'match' => $result !== false,\n                            'parent' => self::className(),\n                        ], __METHOD__);\n                    }\n                    if ($result !== false) {\n                        return $result;\n                    }\n                }\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function createUrl($manager, $route, $params)\n    {\n        $this->createStatus = WebUrlRule::CREATE_STATUS_SUCCESS;\n        foreach ($this->controller as $urlName => $controller) {\n            if (strpos($route, $controller) !== false) {\n                /** @var UrlRuleInterface[] $rules */\n                $rules = $this->rules[$urlName];\n                $url = $this->iterateRules($rules, $manager, $route, $params);\n                if ($url !== false) {\n                    return $url;\n                }\n            } else {\n                $this->createStatus |= WebUrlRule::CREATE_STATUS_ROUTE_MISMATCH;\n            }\n        }\n\n        if ($this->createStatus === WebUrlRule::CREATE_STATUS_SUCCESS) {\n            // create status was not changed - there is no rules configured\n            $this->createStatus = WebUrlRule::CREATE_STATUS_PARSING_ONLY;\n        }\n\n        return false;\n    }\n}\n"
  },
  {
    "path": "framework/rest/ViewAction.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\rest;\n\n/**\n * ViewAction implements the API endpoint for returning the detailed information about a model.\n *\n * For more details and usage information on ViewAction, see the [guide article on rest controllers](guide:rest-controllers).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Controller = Controller\n * @extends Action<T>\n */\nclass ViewAction extends Action\n{\n    /**\n     * Displays a model.\n     * @param string $id the primary key of the model.\n     * @return \\yii\\db\\ActiveRecordInterface the model being displayed\n     */\n    public function run($id)\n    {\n        $model = $this->findModel($id);\n        if ($this->checkAccess) {\n            call_user_func($this->checkAccess, $this->id, $model);\n        }\n\n        return $model;\n    }\n}\n"
  },
  {
    "path": "framework/test/ActiveFixture.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\test;\n\nuse yii\\base\\InvalidConfigException;\nuse yii\\db\\ActiveRecord;\nuse yii\\db\\TableSchema;\n\n/**\n * ActiveFixture represents a fixture backed up by a [[modelClass|ActiveRecord class]] or a [[tableName|database table]].\n *\n * Either [[modelClass]] or [[tableName]] must be set. You should also provide fixture data in the file\n * specified by [[dataFile]] or overriding [[getData()]] if you want to use code to generate the fixture data.\n *\n * When the fixture is being loaded, it will first call [[resetTable()]] to remove any existing data in the table.\n * It will then populate the table with the data returned by [[getData()]].\n *\n * After the fixture is loaded, you can access the loaded data via the [[data]] property. If you set [[modelClass]],\n * you will also be able to retrieve an instance of [[modelClass]] with the populated data via [[getModel()]].\n *\n * For more details and usage information on ActiveFixture, see the [guide article on fixtures](guide:test-fixtures).\n *\n * @property-read TableSchema $tableSchema The schema information of the database table associated with this\n * fixture.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass ActiveFixture extends BaseActiveFixture\n{\n    /**\n     * @var string|null the name of the database table that this fixture is about. If this property is not set,\n     * the table name will be determined via [[modelClass]].\n     * @see modelClass\n     */\n    public $tableName;\n    /**\n     * @var string|bool|null the file path or [path alias](guide:concept-aliases) of the data file that contains the fixture data\n     * to be returned by [[getData()]]. If this is not set, it will default to `FixturePath/data/TableName.php`,\n     * where `FixturePath` stands for the directory containing this fixture class, and `TableName` stands for the\n     * name of the table associated with this fixture. You can set this property to be false to prevent loading any data.\n     */\n    public $dataFile;\n\n    /**\n     * @var TableSchema the table schema for the table associated with this fixture\n     */\n    private $_table;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        if ($this->tableName === null) {\n            if ($this->modelClass === null) {\n                throw new InvalidConfigException('Either \"modelClass\" or \"tableName\" must be set.');\n            }\n            /** @var ActiveRecord $modelClass */\n            $modelClass = $this->modelClass;\n            $this->db = $modelClass::getDb();\n        }\n    }\n\n    /**\n     * Loads the fixture.\n     *\n     * It populate the table with the data returned by [[getData()]].\n     *\n     * If you override this method, you should consider calling the parent implementation\n     * so that the data returned by [[getData()]] can be populated into the table.\n     */\n    public function load()\n    {\n        $this->data = [];\n        $table = $this->getTableSchema();\n        foreach ($this->getData() as $alias => $row) {\n            $primaryKeys = $this->db->schema->insert($table->fullName, $row);\n            $this->data[$alias] = array_merge($row, $primaryKeys);\n        }\n        if ($table->sequenceName !== null) {\n            $this->db->createCommand()->executeResetSequence($table->fullName);\n        }\n    }\n\n    /**\n     * Returns the fixture data.\n     *\n     * The default implementation will try to return the fixture data by including the external file specified by [[dataFile]].\n     * The file should return an array of data rows (column name => column value), each corresponding to a row in the table.\n     *\n     * If the data file does not exist, an empty array will be returned.\n     *\n     * @return array the data rows to be inserted into the database table.\n     */\n    protected function getData()\n    {\n        if ($this->dataFile === null) {\n            if ($this->dataDirectory !== null) {\n                $dataFile = $this->getTableSchema()->fullName . '.php';\n            } else {\n                $class = new \\ReflectionClass($this);\n                $dataFile = dirname($class->getFileName()) . '/data/' . $this->getTableSchema()->fullName . '.php';\n            }\n\n            return $this->loadData($dataFile, false);\n        }\n        return parent::getData();\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function unload()\n    {\n        $this->resetTable();\n        parent::unload();\n    }\n\n    /**\n     * Removes all existing data from the specified table and resets sequence number to 1 (if any).\n     * This method is called before populating fixture data into the table associated with this fixture.\n     */\n    protected function resetTable()\n    {\n        $table = $this->getTableSchema();\n        $this->db->createCommand()->delete($table->fullName)->execute();\n        if ($table->sequenceName !== null) {\n            $this->db->createCommand()->executeResetSequence($table->fullName, 1);\n        }\n    }\n\n    /**\n     * @return TableSchema the schema information of the database table associated with this fixture.\n     * @throws \\yii\\base\\InvalidConfigException if the table does not exist\n     */\n    public function getTableSchema()\n    {\n        if ($this->_table !== null) {\n            return $this->_table;\n        }\n\n        $db = $this->db;\n        $tableName = $this->tableName;\n        if ($tableName === null) {\n            /** @var \\yii\\db\\ActiveRecord $modelClass */\n            $modelClass = $this->modelClass;\n            $tableName = $modelClass::tableName();\n        }\n\n        $this->_table = $db->getSchema()->getTableSchema($tableName);\n        if ($this->_table === null) {\n            throw new InvalidConfigException(\"Table does not exist: {$tableName}\");\n        }\n\n        return $this->_table;\n    }\n}\n"
  },
  {
    "path": "framework/test/ArrayFixture.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\test;\n\nuse yii\\base\\ArrayAccessTrait;\nuse yii\\base\\InvalidConfigException;\n\n/**\n * ArrayFixture represents arbitrary fixture that can be loaded from PHP files.\n *\n * For more details and usage information on ArrayFixture, see the [guide article on fixtures](guide:test-fixtures).\n *\n * @author Mark Jebri <mark.github@yandex.ru>\n * @since 2.0\n *\n * @implements \\IteratorAggregate<string, array<string, mixed>>\n * @implements \\ArrayAccess<string, array<string, mixed>|null>\n */\nclass ArrayFixture extends Fixture implements \\IteratorAggregate, \\ArrayAccess, \\Countable\n{\n    use ArrayAccessTrait;\n    use FileFixtureTrait;\n\n    /**\n     * @var array<string, array<string, mixed>> the data rows. Each array element represents one row of data (column name => column value).\n     */\n    public $data = [];\n\n\n    /**\n     * Loads the fixture.\n     *\n     * The default implementation simply stores the data returned by [[getData()]] in [[data]].\n     * You should usually override this method by putting the data into the underlying database.\n     */\n    public function load()\n    {\n        $this->data = $this->getData();\n    }\n\n    /**\n     * Returns the fixture data.\n     *\n     * The default implementation will try to return the fixture data by including the external file specified by [[dataFile]].\n     * The file should return the data array that will be stored in [[data]] after inserting into the database.\n     *\n     * @return array<string, array<string, mixed>> the data to be put into the database\n     * @throws InvalidConfigException if the specified data file does not exist.\n     */\n    protected function getData()\n    {\n        return $this->loadData($this->dataFile);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function unload()\n    {\n        parent::unload();\n        $this->data = [];\n    }\n}\n"
  },
  {
    "path": "framework/test/BaseActiveFixture.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\test;\n\nuse yii\\base\\ArrayAccessTrait;\nuse yii\\base\\InvalidConfigException;\n\n/**\n * BaseActiveFixture is the base class for fixture classes that support accessing fixture data as ActiveRecord objects.\n *\n * For more details and usage information on BaseActiveFixture, see the [guide article on fixtures](guide:test-fixtures).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @implements \\IteratorAggregate<string, array<string, mixed>>\n * @implements \\ArrayAccess<string, array<string, mixed>|null>\n */\nabstract class BaseActiveFixture extends DbFixture implements \\IteratorAggregate, \\ArrayAccess, \\Countable\n{\n    use ArrayAccessTrait;\n    use FileFixtureTrait;\n\n    /**\n     * @var string the AR model class associated with this fixture.\n     */\n    public $modelClass;\n    /**\n     * @var array<string, array<string, mixed>> the data rows. Each array element represents one row of data (column name => column value).\n     */\n    public $data = [];\n\n    /**\n     * @var \\yii\\db\\ActiveRecord[] the loaded AR models\n     */\n    private $_models = [];\n\n\n    /**\n     * Returns the AR model by the specified model name.\n     * A model name is the key of the corresponding data row in [[data]].\n     * @param string $name the model name.\n     * @return \\yii\\db\\ActiveRecord|null the AR model, or null if the model cannot be found in the database\n     * @throws \\yii\\base\\InvalidConfigException if [[modelClass]] is not set.\n     */\n    public function getModel($name)\n    {\n        if (!isset($this->data[$name])) {\n            return null;\n        }\n        if (array_key_exists($name, $this->_models)) {\n            return $this->_models[$name];\n        }\n\n        if ($this->modelClass === null) {\n            throw new InvalidConfigException('The \"modelClass\" property must be set.');\n        }\n        $row = $this->data[$name];\n        /** @var \\yii\\db\\ActiveRecord $modelClass */\n        $modelClass = $this->modelClass;\n        $keys = [];\n        foreach ($modelClass::primaryKey() as $key) {\n            $keys[$key] = isset($row[$key]) ? $row[$key] : null;\n        }\n\n        return $this->_models[$name] = $modelClass::findOne($keys);\n    }\n\n    /**\n     * Loads the fixture.\n     *\n     * The default implementation simply stores the data returned by [[getData()]] in [[data]].\n     * You should usually override this method by putting the data into the underlying database.\n     */\n    public function load()\n    {\n        $this->data = $this->getData();\n    }\n\n    /**\n     * Returns the fixture data.\n     *\n     * @return array<string, array<string, mixed>> the data to be put into the database\n     * @throws InvalidConfigException if the specified data file does not exist.\n     * @see loadData()\n     */\n    protected function getData()\n    {\n        return $this->loadData($this->dataFile);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function unload()\n    {\n        parent::unload();\n        $this->data = [];\n        $this->_models = [];\n    }\n}\n"
  },
  {
    "path": "framework/test/DbFixture.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\test;\n\nuse yii\\base\\BaseObject;\nuse yii\\db\\Connection;\nuse yii\\di\\Instance;\n\n/**\n * DbFixture is the base class for DB-related fixtures.\n *\n * DbFixture provides the [[db]] connection to be used by DB fixtures.\n *\n * For more details and usage information on DbFixture, see the [guide article on fixtures](guide:test-fixtures).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nabstract class DbFixture extends Fixture\n{\n    /**\n     * @var Connection|array|string the DB connection object or the application component ID of the DB connection.\n     * After the DbFixture object is created, if you want to change this property, you should only assign it\n     * with a DB connection object.\n     * Starting from version 2.0.2, this can also be a configuration array for creating the object.\n     */\n    public $db = 'db';\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        $this->db = Instance::ensure($this->db, BaseObject::className());\n    }\n}\n"
  },
  {
    "path": "framework/test/FileFixtureTrait.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\test;\n\nuse Yii;\nuse yii\\base\\InvalidConfigException;\n\n/**\n * FileFixtureTrait provides functionalities for loading data fixture from file.\n *\n * @author Leandro Guindani Gehlen <leandrogehlen@gmail.com>\n * @since 2.0.14\n */\ntrait FileFixtureTrait\n{\n    /**\n     * @var string the directory path or [path alias](guide:concept-aliases) that contains the fixture data\n     */\n    public $dataDirectory;\n    /**\n     * @var string|bool|null the file path or [path alias](guide:concept-aliases) of the data file that contains the fixture data\n     * to be returned by [[getData()]]. You can set this property to be false to prevent loading any data.\n     */\n    public $dataFile;\n\n    /**\n     * Returns the fixture data.\n     *\n     * The default implementation will try to return the fixture data by including the external file specified by [[dataFile]].\n     * The file should return the data array that will be stored in [[data]] after inserting into the database.\n     *\n     * @param string $file the data file path\n     * @param bool $throwException whether to throw exception if fixture data file does not exist.\n     * @return array the data to be put into the database\n     * @throws InvalidConfigException if the specified data file does not exist.\n     */\n    protected function loadData($file, $throwException = true)\n    {\n        if ($file === null || $file === false) {\n            return [];\n        }\n\n        if (basename($file) === $file && $this->dataDirectory !== null) {\n            $file = $this->dataDirectory . '/' . $file;\n        }\n\n        $file = Yii::getAlias($file);\n        if (is_file($file)) {\n            return require $file;\n        }\n\n        if ($throwException) {\n            throw new InvalidConfigException(\"Fixture data file does not exist: {$file}\");\n        }\n\n        return [];\n    }\n}\n"
  },
  {
    "path": "framework/test/Fixture.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\test;\n\nuse yii\\base\\Component;\n\n/**\n * Fixture represents a fixed state of a test environment.\n *\n * Each fixture instance represents a particular aspect of a test environment. For example,\n * you may use `UserFixture` to initialize the user database table with a set of known data. You may\n * load the fixture when running every test method so that the user table always contains the fixed data\n * and thus allows your test predictable and repeatable.\n *\n * A fixture may depend on other fixtures, specified via the [[depends]] property. When a fixture is being loaded,\n * its dependent fixtures will be automatically loaded BEFORE the fixture; and when the fixture is being unloaded,\n * its dependent fixtures will be unloaded AFTER the fixture.\n *\n * You should normally override [[load()]] to specify how to set up a fixture; and override [[unload()]]\n * for clearing up a fixture.\n *\n * For more details and usage information on Fixture, see the [guide article on fixtures](guide:test-fixtures).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Fixture extends Component\n{\n    /**\n     * @var array the fixtures that this fixture depends on. This must be a list of the dependent\n     * fixture class names.\n     */\n    public $depends = [];\n\n\n    /**\n     * Loads the fixture.\n     * This method is called before performing every test method.\n     * You should override this method with concrete implementation about how to set up the fixture.\n     */\n    public function load()\n    {\n    }\n\n    /**\n     * This method is called BEFORE any fixture data is loaded for the current test.\n     */\n    public function beforeLoad()\n    {\n    }\n\n    /**\n     * This method is called AFTER all fixture data have been loaded for the current test.\n     */\n    public function afterLoad()\n    {\n    }\n\n    /**\n     * Unloads the fixture.\n     * This method is called after every test method finishes.\n     * You may override this method to perform necessary cleanup work for the fixture.\n     */\n    public function unload()\n    {\n    }\n\n    /**\n     * This method is called BEFORE any fixture data is unloaded for the current test.\n     */\n    public function beforeUnload()\n    {\n    }\n\n    /**\n     * This method is called AFTER all fixture data have been unloaded for the current test.\n     */\n    public function afterUnload()\n    {\n    }\n}\n"
  },
  {
    "path": "framework/test/FixtureTrait.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\test;\n\nuse Yii;\nuse yii\\base\\InvalidConfigException;\n\n/**\n * FixtureTrait provides functionalities for loading, unloading and accessing fixtures for a test case.\n *\n * By using FixtureTrait, a test class will be able to specify which fixtures to load by overriding\n * the [[fixtures()]] method. It can then load and unload the fixtures using [[loadFixtures()]] and [[unloadFixtures()]].\n * Once a fixture is loaded, it can be accessed like an object property, thanks to the PHP `__get()` magic method.\n * Also, if the fixture is an instance of [[ActiveFixture]], you will be able to access AR models\n * through the syntax `$this->fixtureName('model name')`.\n *\n * For more details and usage information on FixtureTrait, see the [guide article on fixtures](guide:test-fixtures).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\ntrait FixtureTrait\n{\n    /**\n     * @var array the list of fixture objects available for the current test.\n     * The array keys are the corresponding fixture class names.\n     * The fixtures are listed in their dependency order. That is, fixture A is listed before B\n     * if B depends on A.\n     */\n    private $_fixtures;\n\n\n    /**\n     * Declares the fixtures that are needed by the current test case.\n     *\n     * The return value of this method must be an array of fixture configurations. For example,\n     *\n     * ```\n     * [\n     *     // anonymous fixture\n     *     PostFixture::class,\n     *     // \"users\" fixture\n     *     'users' => UserFixture::class,\n     *     // \"cache\" fixture with configuration\n     *     'cache' => [\n     *          'class' => CacheFixture::class,\n     *          'host' => 'xxx',\n     *     ],\n     * ]\n     * ```\n     *\n     * Note that the actual fixtures used for a test case will include both [[globalFixtures()]]\n     * and [[fixtures()]].\n     *\n     * @return array the fixtures needed by the current test case\n     */\n    public function fixtures()\n    {\n        return [];\n    }\n\n    /**\n     * Declares the fixtures shared required by different test cases.\n     * The return value should be similar to that of [[fixtures()]].\n     * You should usually override this method in a base class.\n     * @return array the fixtures shared and required by different test cases.\n     * @see fixtures()\n     */\n    public function globalFixtures()\n    {\n        return [];\n    }\n\n    /**\n     * Loads the specified fixtures.\n     * This method will call [[Fixture::load()]] for every fixture object.\n     * @param Fixture[]|null $fixtures the fixtures to be loaded. If this parameter is not specified,\n     * the return value of [[getFixtures()]] will be used.\n     */\n    public function loadFixtures($fixtures = null)\n    {\n        if ($fixtures === null) {\n            $fixtures = $this->getFixtures();\n        }\n\n        foreach ($fixtures as $fixture) {\n            $fixture->beforeLoad();\n        }\n        foreach ($fixtures as $fixture) {\n            $fixture->load();\n        }\n        foreach (array_reverse($fixtures) as $fixture) {\n            $fixture->afterLoad();\n        }\n    }\n\n    /**\n     * Unloads the specified fixtures.\n     * This method will call [[Fixture::unload()]] for every fixture object.\n     * @param Fixture[]|null $fixtures the fixtures to be loaded. If this parameter is not specified,\n     * the return value of [[getFixtures()]] will be used.\n     */\n    public function unloadFixtures($fixtures = null)\n    {\n        if ($fixtures === null) {\n            $fixtures = $this->getFixtures();\n        }\n\n        foreach ($fixtures as $fixture) {\n            $fixture->beforeUnload();\n        }\n        $fixtures = array_reverse($fixtures);\n        foreach ($fixtures as $fixture) {\n            $fixture->unload();\n        }\n        foreach ($fixtures as $fixture) {\n            $fixture->afterUnload();\n        }\n    }\n\n    /**\n     * Initialize the fixtures.\n     * @since 2.0.12\n     */\n    public function initFixtures()\n    {\n        $this->unloadFixtures();\n        $this->loadFixtures();\n    }\n\n    /**\n     * Returns the fixture objects as specified in [[globalFixtures()]] and [[fixtures()]].\n     * @return Fixture[] the loaded fixtures for the current test case\n     */\n    public function getFixtures()\n    {\n        if ($this->_fixtures === null) {\n            $this->_fixtures = $this->createFixtures(array_merge($this->globalFixtures(), $this->fixtures()));\n        }\n\n        return $this->_fixtures;\n    }\n\n    /**\n     * Returns the named fixture.\n     * @param string $name the fixture name. This can be either the fixture alias name, or the class name if the alias is not used.\n     * @return Fixture|null the fixture object, or null if the named fixture does not exist.\n     */\n    public function getFixture($name)\n    {\n        if ($this->_fixtures === null) {\n            $this->_fixtures = $this->createFixtures(array_merge($this->globalFixtures(), $this->fixtures()));\n        }\n        $name = ltrim($name, '\\\\');\n\n        return isset($this->_fixtures[$name]) ? $this->_fixtures[$name] : null;\n    }\n\n    /**\n     * Creates the specified fixture instances.\n     * All dependent fixtures will also be created. Duplicate fixtures and circular dependencies will only be created once.\n     * @param array $fixtures the fixtures to be created. You may provide fixture names or fixture configurations.\n     * If this parameter is not provided, the fixtures specified in [[globalFixtures()]] and [[fixtures()]] will be created.\n     * @return Fixture[] the created fixture instances\n     * @throws InvalidConfigException if fixtures are not properly configured\n     */\n    protected function createFixtures(array $fixtures)\n    {\n        // normalize fixture configurations\n        $config = [];  // configuration provided in test case\n        $aliases = [];  // class name => alias or class name\n        foreach ($fixtures as $name => $fixture) {\n            if (!is_array($fixture)) {\n                $class = ltrim($fixture, '\\\\');\n                $fixtures[$name] = ['class' => $class];\n                $aliases[$class] = is_int($name) ? $class : $name;\n            } elseif (isset($fixture['class'])) {\n                $class = ltrim($fixture['class'], '\\\\');\n                $config[$class] = $fixture;\n                $aliases[$class] = $name;\n            } else {\n                throw new InvalidConfigException(\"You must specify 'class' for the fixture '$name'.\");\n            }\n        }\n\n        // create fixture instances\n        $instances = [];\n        $stack = array_reverse($fixtures);\n        while (($fixture = array_pop($stack)) !== null) {\n            if ($fixture instanceof Fixture) {\n                $class = get_class($fixture);\n                $name = isset($aliases[$class]) ? $aliases[$class] : $class;\n                unset($instances[$name]);  // unset so that the fixture is added to the last in the next line\n                $instances[$name] = $fixture;\n            } else {\n                $class = ltrim($fixture['class'], '\\\\');\n                $name = isset($aliases[$class]) ? $aliases[$class] : $class;\n                if (!isset($instances[$name])) {\n                    $instances[$name] = false;\n                    $stack[] = $fixture = Yii::createObject($fixture);\n                    foreach ($fixture->depends as $dep) {\n                        // need to use the configuration provided in test case\n                        $stack[] = isset($config[$dep]) ? $config[$dep] : ['class' => $dep];\n                    }\n                }\n                // if the fixture is already loaded (ie. a circular dependency or if two fixtures depend on the same fixture) just skip it.\n            }\n        }\n\n        return $instances;\n    }\n}\n"
  },
  {
    "path": "framework/test/InitDbFixture.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\test;\n\nuse Yii;\n\n/**\n * InitDbFixture represents the initial state needed for DB-related tests.\n *\n * Its main task is to toggle integrity check of the database during data loading.\n * This is needed by other DB-related fixtures (e.g. [[ActiveFixture]]) so that they can populate\n * data into the database without triggering integrity check errors.\n *\n * Besides, DbFixture also attempts to load an [[initScript|initialization script]] if it exists.\n *\n * You should normally use InitDbFixture to prepare a skeleton test database.\n * Other DB fixtures will then add specific tables and data to this database.\n *\n * For more details and usage information on InitDbFixture, see the [guide article on fixtures](guide:test-fixtures).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass InitDbFixture extends DbFixture\n{\n    /**\n     * @var string the init script file that should be executed when loading this fixture.\n     * This should be either a file path or [path alias](guide:concept-aliases). Note that if the file does not exist,\n     * no error will be raised.\n     */\n    public $initScript = '@app/tests/fixtures/initdb.php';\n    /**\n     * @var array list of database schemas that the test tables may reside in. Defaults to\n     * `['']`, meaning using the default schema (an empty string refers to the\n     * default schema). This property is mainly used when turning on and off integrity checks\n     * so that fixture data can be populated into the database without causing problem.\n     */\n    public $schemas = [''];\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function beforeLoad()\n    {\n        $this->checkIntegrity(false);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function afterLoad()\n    {\n        $this->checkIntegrity(true);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function load()\n    {\n        $file = Yii::getAlias($this->initScript);\n        if (is_file($file)) {\n            require $file;\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function beforeUnload()\n    {\n        $this->checkIntegrity(false);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function afterUnload()\n    {\n        $this->checkIntegrity(true);\n    }\n\n    /**\n     * Toggles the DB integrity check.\n     * @param bool $check whether to turn on or off the integrity check.\n     */\n    public function checkIntegrity($check)\n    {\n        if (!$this->db instanceof \\yii\\db\\Connection) {\n            return;\n        }\n\n        if ($this->db->getDriverName() === 'oci') {\n            return;\n        }\n\n        foreach ($this->schemas as $schema) {\n            $this->db->createCommand()->checkIntegrity($check, $schema)->execute();\n        }\n    }\n}\n"
  },
  {
    "path": "framework/validators/BooleanValidator.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\validators;\n\nuse Yii;\nuse yii\\helpers\\Json;\n\n/**\n * BooleanValidator checks if the attribute value is a boolean value.\n *\n * Possible boolean values can be configured via the [[trueValue]] and [[falseValue]] properties.\n * And the comparison can be either [[strict]] or not.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass BooleanValidator extends Validator\n{\n    /**\n     * @var mixed the value representing true status. Defaults to '1'.\n     */\n    public $trueValue = '1';\n    /**\n     * @var mixed the value representing false status. Defaults to '0'.\n     */\n    public $falseValue = '0';\n    /**\n     * @var bool whether the comparison to [[trueValue]] and [[falseValue]] is strict.\n     * When this is true, the attribute value and type must both match those of [[trueValue]] or [[falseValue]].\n     * Defaults to false, meaning only the value needs to be matched.\n     */\n    public $strict = false;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        if ($this->message === null) {\n            $this->message = Yii::t('yii', '{attribute} must be either \"{true}\" or \"{false}\".');\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function validateValue($value)\n    {\n        if ($this->strict) {\n            $valid = $value === $this->trueValue || $value === $this->falseValue;\n        } else {\n            $valid = $value == $this->trueValue || $value == $this->falseValue;\n        }\n\n        if (!$valid) {\n            return [$this->message, [\n                'true' => $this->trueValue === true ? 'true' : $this->trueValue,\n                'false' => $this->falseValue === false ? 'false' : $this->falseValue,\n            ]];\n        }\n\n        return null;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function clientValidateAttribute($model, $attribute, $view)\n    {\n        ValidationAsset::register($view);\n        $options = $this->getClientOptions($model, $attribute);\n\n        return 'yii.validation.boolean(value, messages, ' . Json::htmlEncode($options) . ');';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getClientOptions($model, $attribute)\n    {\n        $options = [\n            'trueValue' => $this->trueValue,\n            'falseValue' => $this->falseValue,\n            'message' => $this->formatMessage($this->message, [\n                'attribute' => $model->getAttributeLabel($attribute),\n                'true' => $this->trueValue === true ? 'true' : $this->trueValue,\n                'false' => $this->falseValue === false ? 'false' : $this->falseValue,\n            ]),\n        ];\n        if ($this->skipOnEmpty) {\n            $options['skipOnEmpty'] = 1;\n        }\n        if ($this->strict) {\n            $options['strict'] = 1;\n        }\n\n        return $options;\n    }\n}\n"
  },
  {
    "path": "framework/validators/CompareValidator.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\validators;\n\nuse Yii;\nuse yii\\base\\InvalidConfigException;\nuse yii\\helpers\\Html;\nuse yii\\helpers\\Json;\n\n/**\n * CompareValidator compares the specified attribute value with another value.\n *\n * The value being compared with can be another attribute value\n * (specified via [[compareAttribute]]) or a constant (specified via\n * [[compareValue]]). When both are specified, the latter takes\n * precedence. If neither is specified, the attribute will be compared\n * with another attribute whose name is by appending \"_repeat\" to the source\n * attribute name.\n *\n * CompareValidator supports different comparison operators, specified\n * via the [[operator]] property.\n *\n * The default comparison function is based on string values, which means the values\n * are compared byte by byte. When comparing numbers, make sure to set the [[$type]]\n * to [[TYPE_NUMBER]] to enable numeric comparison.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass CompareValidator extends Validator\n{\n    /**\n     * Constant for specifying the comparison [[type]] by numeric values.\n     * @since 2.0.11\n     * @see type\n     */\n    public const TYPE_STRING = 'string';\n    /**\n     * Constant for specifying the comparison [[type]] by numeric values.\n     * @since 2.0.11\n     * @see type\n     */\n    public const TYPE_NUMBER = 'number';\n    /**\n     * @var string the name of the attribute to be compared with. When both this property\n     * and [[compareValue]] are set, the latter takes precedence. If neither is set,\n     * it assumes the comparison is against another attribute whose name is formed by\n     * appending '_repeat' to the attribute being validated. For example, if 'password' is\n     * being validated, then the attribute to be compared would be 'password_repeat'.\n     * @see compareValue\n     */\n    public $compareAttribute;\n    /**\n     * @var mixed the constant value to be compared with or an anonymous function\n     * that returns the constant value. When both this property and\n     * [[compareAttribute]] are set, this property takes precedence.\n     * The signature of the anonymous function should be as follows,\n     *\n     * ```\n     * function($model, $attribute) {\n     *     // compute value to compare with\n     *     return $value;\n     * }\n     * ```\n     * @see compareAttribute\n     */\n    public $compareValue;\n    /**\n     * @var string the type of the values being compared. The follow types are supported:\n     *\n     * - [[TYPE_STRING|string]]: the values are being compared as strings. No conversion will be done before comparison.\n     * - [[TYPE_NUMBER|number]]: the values are being compared as numbers. String values will be converted into numbers before comparison.\n     */\n    public $type = self::TYPE_STRING;\n    /**\n     * @var string the operator for comparison. The following operators are supported:\n     *\n     * - `==`: check if two values are equal. The comparison is done is non-strict mode.\n     * - `===`: check if two values are equal. The comparison is done is strict mode.\n     * - `!=`: check if two values are NOT equal. The comparison is done is non-strict mode.\n     * - `!==`: check if two values are NOT equal. The comparison is done is strict mode.\n     * - `>`: check if value being validated is greater than the value being compared with.\n     * - `>=`: check if value being validated is greater than or equal to the value being compared with.\n     * - `<`: check if value being validated is less than the value being compared with.\n     * - `<=`: check if value being validated is less than or equal to the value being compared with.\n     *\n     * When you want to compare numbers, make sure to also set [[type]] to `number`.\n     */\n    public $operator = '==';\n    /**\n     * @var string the user-defined error message. It may contain the following placeholders which\n     * will be replaced accordingly by the validator:\n     *\n     * - `{attribute}`: the label of the attribute being validated\n     * - `{value}`: the value of the attribute being validated\n     * - `{compareValue}`: the value or the attribute label to be compared with\n     * - `{compareAttribute}`: the label of the attribute to be compared with\n     * - `{compareValueOrAttribute}`: the value or the attribute label to be compared with\n     */\n    public $message;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        if ($this->message === null) {\n            switch ($this->operator) {\n                case '==':\n                case '===':\n                    $this->message = Yii::t('yii', '{attribute} must be equal to \"{compareValueOrAttribute}\".');\n                    break;\n                case '!=':\n                case '!==':\n                    $this->message = Yii::t('yii', '{attribute} must not be equal to \"{compareValueOrAttribute}\".');\n                    break;\n                case '>':\n                    $this->message = Yii::t('yii', '{attribute} must be greater than \"{compareValueOrAttribute}\".');\n                    break;\n                case '>=':\n                    $this->message = Yii::t('yii', '{attribute} must be greater than or equal to \"{compareValueOrAttribute}\".');\n                    break;\n                case '<':\n                    $this->message = Yii::t('yii', '{attribute} must be less than \"{compareValueOrAttribute}\".');\n                    break;\n                case '<=':\n                    $this->message = Yii::t('yii', '{attribute} must be less than or equal to \"{compareValueOrAttribute}\".');\n                    break;\n                default:\n                    throw new InvalidConfigException(\"Unknown operator: {$this->operator}\");\n            }\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function validateAttribute($model, $attribute)\n    {\n        $value = $model->$attribute;\n        if (is_array($value)) {\n            $this->addError($model, $attribute, Yii::t('yii', '{attribute} is invalid.'));\n\n            return;\n        }\n        if ($this->compareValue !== null) {\n            if ($this->compareValue instanceof \\Closure) {\n                $this->compareValue = call_user_func($this->compareValue, $model, $attribute);\n            }\n            $compareLabel = $compareValue = $compareValueOrAttribute = $this->compareValue;\n        } else {\n            $compareAttribute = $this->compareAttribute === null ? $attribute . '_repeat' : $this->compareAttribute;\n            $compareValue = $model->$compareAttribute;\n            $compareLabel = $compareValueOrAttribute = $model->getAttributeLabel($compareAttribute);\n\n            if (!$this->skipOnError && $model->hasErrors($compareAttribute)) {\n                $this->addError(\n                    $model,\n                    $attribute,\n                    Yii::t('yii', '{compareAttribute} is invalid.'),\n                    ['compareAttribute' => $compareLabel]\n                );\n\n                return;\n            }\n        }\n\n        if (!$this->compareValues($this->operator, $this->type, $value, $compareValue)) {\n            $this->addError($model, $attribute, $this->message, [\n                'compareAttribute' => $compareLabel,\n                'compareValue' => $compareValue,\n                'compareValueOrAttribute' => $compareValueOrAttribute,\n            ]);\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function validateValue($value)\n    {\n        if ($this->compareValue === null) {\n            throw new InvalidConfigException('CompareValidator::compareValue must be set.');\n        }\n        if ($this->compareValue instanceof \\Closure) {\n            $this->compareValue = call_user_func($this->compareValue);\n        }\n        if (!$this->compareValues($this->operator, $this->type, $value, $this->compareValue)) {\n            return [$this->message, [\n                'compareAttribute' => $this->compareValue,\n                'compareValue' => $this->compareValue,\n                'compareValueOrAttribute' => $this->compareValue,\n            ]];\n        }\n\n        return null;\n    }\n\n    /**\n     * Compares two values with the specified operator.\n     * @param string $operator the comparison operator\n     * @param string $type the type of the values being compared\n     * @param mixed $value the value being compared\n     * @param mixed $compareValue another value being compared\n     * @return bool whether the comparison using the specified operator is true.\n     */\n    protected function compareValues($operator, $type, $value, $compareValue)\n    {\n        if ($type === self::TYPE_NUMBER) {\n            $value = (float) $value;\n            $compareValue = (float) $compareValue;\n        } else {\n            $value = (string) $value;\n            $compareValue = (string) $compareValue;\n        }\n        switch ($operator) {\n            case '==':\n                return $value == $compareValue;\n            case '===':\n                return $value === $compareValue;\n            case '!=':\n                return $value != $compareValue;\n            case '!==':\n                return $value !== $compareValue;\n            case '>':\n                return $value > $compareValue;\n            case '>=':\n                return $value >= $compareValue;\n            case '<':\n                return $value < $compareValue;\n            case '<=':\n                return $value <= $compareValue;\n            default:\n                return false;\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function clientValidateAttribute($model, $attribute, $view)\n    {\n        if ($this->compareValue != null && $this->compareValue instanceof \\Closure) {\n            $this->compareValue = call_user_func($this->compareValue);\n        }\n\n        ValidationAsset::register($view);\n        $options = $this->getClientOptions($model, $attribute);\n\n        return 'yii.validation.compare(value, messages, ' . Json::htmlEncode($options) . ', $form);';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getClientOptions($model, $attribute)\n    {\n        $options = [\n            'operator' => $this->operator,\n            'type' => $this->type,\n        ];\n\n        if ($this->compareValue !== null) {\n            $options['compareValue'] = $this->compareValue;\n            $compareLabel = $compareValue = $compareValueOrAttribute = $this->compareValue;\n        } else {\n            $compareAttribute = $this->compareAttribute === null ? $attribute . '_repeat' : $this->compareAttribute;\n            $compareValue = $model->getAttributeLabel($compareAttribute);\n            $options['compareAttribute'] = Html::getInputId($model, $compareAttribute);\n            $options['compareAttributeName'] = Html::getInputName($model, $compareAttribute);\n            $compareLabel = $compareValueOrAttribute = $model->getAttributeLabel($compareAttribute);\n        }\n\n        if ($this->skipOnEmpty) {\n            $options['skipOnEmpty'] = 1;\n        }\n\n        $options['message'] = $this->formatMessage($this->message, [\n            'attribute' => $model->getAttributeLabel($attribute),\n            'compareAttribute' => $compareLabel,\n            'compareValue' => $compareValue,\n            'compareValueOrAttribute' => $compareValueOrAttribute,\n        ]);\n\n        return $options;\n    }\n}\n"
  },
  {
    "path": "framework/validators/DateValidator.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\validators;\n\nuse DateTime;\nuse DateTimeZone;\nuse Exception;\nuse IntlDateFormatter;\nuse Yii;\nuse yii\\base\\InvalidConfigException;\nuse yii\\helpers\\FormatConverter;\n\n/**\n * DateValidator verifies if the attribute represents a date, time or datetime in a proper [[format]].\n *\n * It can also parse internationalized dates in a specific [[locale]] like e.g. `12 мая 2014` when [[format]]\n * is configured to use a time pattern in ICU format.\n *\n * It is further possible to limit the date within a certain range using [[min]] and [[max]].\n *\n * Additional to validating the date it can also export the parsed timestamp as a machine readable format\n * which can be configured using [[timestampAttribute]]. For values that include time information (not date-only values)\n * also the time zone will be adjusted. The time zone of the input value is assumed to be the one specified by the [[timeZone]]\n * property and the target timeZone will be UTC when [[timestampAttributeFormat]] is `null` (exporting as UNIX timestamp)\n * or [[timestampAttributeTimeZone]] otherwise. If you want to avoid the time zone conversion, make sure that [[timeZone]] and\n * [[timestampAttributeTimeZone]] are the same.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0\n */\nclass DateValidator extends Validator\n{\n    /**\n     * Constant for specifying the validation [[type]] as a date value, used for validation with intl short format.\n     * @since 2.0.8\n     * @see type\n     */\n    public const TYPE_DATE = 'date';\n    /**\n     * Constant for specifying the validation [[type]] as a datetime value, used for validation with intl short format.\n     * @since 2.0.8\n     * @see type\n     */\n    public const TYPE_DATETIME = 'datetime';\n    /**\n     * Constant for specifying the validation [[type]] as a time value, used for validation with intl short format.\n     * @since 2.0.8\n     * @see type\n     */\n    public const TYPE_TIME = 'time';\n    /**\n     * @var string the type of the validator. Indicates, whether a date, time or datetime value should be validated.\n     * This property influences the default value of [[format]] and also sets the correct behavior when [[format]] is one of the intl\n     * short formats, `short`, `medium`, `long`, or `full`.\n     *\n     * This is only effective when the [PHP intl extension](https://www.php.net/manual/en/book.intl.php) is installed.\n     *\n     * This property can be set to the following values:\n     *\n     * - [[TYPE_DATE]] - (default) for validating date values only, that means only values that do not include a time range are valid.\n     * - [[TYPE_DATETIME]] - for validating datetime values, that contain a date part as well as a time part.\n     * - [[TYPE_TIME]] - for validating time values, that contain no date information.\n     *\n     * @since 2.0.8\n     */\n    public $type = self::TYPE_DATE;\n    /**\n     * @var string|null the date format that the value being validated should follow.\n     * This can be a date time pattern as described in the [ICU manual](https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax).\n     *\n     * Alternatively this can be a string prefixed with `php:` representing a format that can be recognized by the PHP Datetime class.\n     * Please refer to <https://www.php.net/manual/en/datetime.createfromformat.php> on supported formats.\n     *\n     * If this property is not set, the default value will be obtained from `Yii::$app->formatter->dateFormat`, see [[\\yii\\i18n\\Formatter::dateFormat]] for details.\n     * Since version 2.0.8 the default value will be determined from different formats of the formatter class,\n     * dependent on the value of [[type]]:\n     *\n     * - if type is [[TYPE_DATE]], the default value will be taken from [[\\yii\\i18n\\Formatter::dateFormat]],\n     * - if type is [[TYPE_DATETIME]], it will be taken from [[\\yii\\i18n\\Formatter::datetimeFormat]],\n     * - and if type is [[TYPE_TIME]], it will be [[\\yii\\i18n\\Formatter::timeFormat]].\n     *\n     * Here are some example values:\n     *\n     * ```\n     * 'MM/dd/yyyy' // date in ICU format\n     * 'php:m/d/Y' // the same date in PHP format\n     * 'MM/dd/yyyy HH:mm' // not only dates but also times can be validated\n     * ```\n     *\n     * **Note:** the underlying date parsers being used vary dependent on the format. If you use the ICU format and\n     * the [PHP intl extension](https://www.php.net/manual/en/book.intl.php) is installed, the [IntlDateFormatter](https://www.php.net/manual/en/intldateformatter.parse.php)\n     * is used to parse the input value. In all other cases the PHP [DateTime](https://www.php.net/manual/en/datetime.createfromformat.php) class\n     * is used. The IntlDateFormatter has the advantage that it can parse international dates like `12. Mai 2015` or `12 мая 2014`, while the\n     * PHP parser is limited to English only. The PHP parser however is more strict about the input format as it will not accept\n     * `12.05.05` for the format `php:d.m.Y`, but the IntlDateFormatter will accept it for the format `dd.MM.yyyy`.\n     * If you need to use the IntlDateFormatter you can avoid this problem by specifying a [[min|minimum date]].\n     */\n    public $format;\n    /**\n     * @var string|null the locale ID that is used to localize the date parsing.\n     * This is only effective when the [PHP intl extension](https://www.php.net/manual/en/book.intl.php) is installed.\n     * If not set, the locale of the [[\\yii\\base\\Application::formatter|formatter]] will be used.\n     * See also [[\\yii\\i18n\\Formatter::locale]].\n     */\n    public $locale;\n    /**\n     * @var string|null the timezone to use for parsing date and time values.\n     * This can be any value that may be passed to [date_default_timezone_set()](https://www.php.net/manual/en/function.date-default-timezone-set.php)\n     * e.g. `UTC`, `Europe/Berlin` or `America/Chicago`.\n     * Refer to the [php manual](https://www.php.net/manual/en/timezones.php) for available timezones.\n     * If this property is not set, [[\\yii\\base\\Application::timeZone]] will be used.\n     */\n    public $timeZone;\n    /**\n     * @var string|null the name of the attribute to receive the parsing result.\n     * When this property is not null and the validation is successful, the named attribute will\n     * receive the parsing result.\n     *\n     * This can be the same attribute as the one being validated. If this is the case,\n     * the original value will be overwritten with the timestamp value after successful validation.\n     *\n     * Note, that when using this property, the input value will be converted to a unix timestamp, which by definition\n     * is in [[$defaultTimeZone|default UTC time zone]], so a conversion from the [[$timeZone|input time zone]] to\n     * the default one will be performed. If you want to change the default time zone, set the [[$defaultTimeZone]] property.\n     * When defining [[$timestampAttributeFormat]] you can further control the conversion by setting\n     * [[$timestampAttributeTimeZone]] to a different value than `'UTC'`.\n     *\n     * @see timestampAttributeFormat\n     * @see timestampAttributeTimeZone\n     */\n    public $timestampAttribute;\n    /**\n     * @var string|null the format to use when populating the [[timestampAttribute]].\n     * The format can be specified in the same way as for [[format]].\n     *\n     * If not set, [[timestampAttribute]] will receive a UNIX timestamp.\n     * If [[timestampAttribute]] is not set, this property will be ignored.\n     * @see format\n     * @see timestampAttribute\n     * @since 2.0.4\n     */\n    public $timestampAttributeFormat;\n    /**\n     * @var string the timezone to use when populating the [[timestampAttribute]] with [[timestampAttributeFormat]]. Defaults to `UTC`.\n     *\n     * This can be any value that may be passed to [date_default_timezone_set()](https://www.php.net/manual/en/function.date-default-timezone-set.php)\n     * e.g. `UTC`, `Europe/Berlin` or `America/Chicago`.\n     * Refer to the [php manual](https://www.php.net/manual/en/timezones.php) for available timezones.\n     *\n     * If [[timestampAttributeFormat]] is not set, this property will be ignored.\n     * @see timestampAttributeFormat\n     * @since 2.0.4\n     */\n    public $timestampAttributeTimeZone = 'UTC';\n    /**\n     * @var int|string|null upper limit of the date. Defaults to null, meaning no upper limit.\n     * This can be a unix timestamp or a string representing a date time value.\n     * If this property is a string, [[format]] will be used to parse it.\n     * @see tooBig for the customized message used when the date is too big.\n     * @since 2.0.4\n     */\n    public $max;\n    /**\n     * @var int|string|null lower limit of the date. Defaults to null, meaning no lower limit.\n     * This can be a unix timestamp or a string representing a date time value.\n     * If this property is a string, [[format]] will be used to parse it.\n     * @see tooSmall for the customized message used when the date is too small.\n     * @since 2.0.4\n     */\n    public $min;\n    /**\n     * @var string user-defined error message used when the value is bigger than [[max]].\n     * @since 2.0.4\n     */\n    public $tooBig;\n    /**\n     * @var string user-defined error message used when the value is smaller than [[min]].\n     * @since 2.0.4\n     */\n    public $tooSmall;\n    /**\n     * @var string|null user friendly value of upper limit to display in the error message.\n     * If this property is null, the value of [[max]] will be used (before parsing).\n     * @since 2.0.4\n     */\n    public $maxString;\n    /**\n     * @var string|null user friendly value of lower limit to display in the error message.\n     * If this property is null, the value of [[min]] will be used (before parsing).\n     * @since 2.0.4\n     */\n    public $minString;\n    /**\n     * @var bool set this parameter to true if you need strict date format validation (e.g. only such dates pass\n     * validation for the following format 'yyyy-MM-dd': '0011-03-25', '2019-04-30' etc. and not '18-05-15',\n     * '2017-Mar-14' etc. which pass validation if this parameter is set to false)\n     * @since 2.0.22\n     */\n    public $strictDateFormat = false;\n    /**\n     * @var string the default timezone used for parsing when no time parts are provided in the format.\n     * See [[timestampAttributeTimeZone]] for more description.\n     * @since 2.0.39\n     */\n    public $defaultTimeZone = 'UTC';\n\n    /**\n     * @var array map of short format names to IntlDateFormatter constant values.\n     */\n    private $_dateFormats = [\n        'short' => 3, // IntlDateFormatter::SHORT,\n        'medium' => 2, // IntlDateFormatter::MEDIUM,\n        'long' => 1, // IntlDateFormatter::LONG,\n        'full' => 0, // IntlDateFormatter::FULL,\n    ];\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        if ($this->message === null) {\n            $this->message = Yii::t('yii', 'The format of {attribute} is invalid.');\n        }\n        if ($this->format === null) {\n            if ($this->type === self::TYPE_DATE) {\n                $this->format = Yii::$app->formatter->dateFormat;\n            } elseif ($this->type === self::TYPE_DATETIME) {\n                $this->format = Yii::$app->formatter->datetimeFormat;\n            } elseif ($this->type === self::TYPE_TIME) {\n                $this->format = Yii::$app->formatter->timeFormat;\n            } else {\n                throw new InvalidConfigException('Unknown validation type set for DateValidator::$type: ' . $this->type);\n            }\n        }\n        if ($this->locale === null) {\n            $this->locale = Yii::$app->language;\n        }\n        if ($this->timeZone === null) {\n            $this->timeZone = Yii::$app->timeZone;\n        }\n        if ($this->min !== null && $this->tooSmall === null) {\n            $this->tooSmall = Yii::t('yii', '{attribute} must be no less than {min}.');\n        }\n        if ($this->max !== null && $this->tooBig === null) {\n            $this->tooBig = Yii::t('yii', '{attribute} must be no greater than {max}.');\n        }\n        if ($this->maxString === null) {\n            $this->maxString = (string)$this->max;\n        }\n        if ($this->minString === null) {\n            $this->minString = (string)$this->min;\n        }\n        if ($this->max !== null && is_string($this->max)) {\n            $timestamp = $this->parseDateValue($this->max);\n            if ($timestamp === false) {\n                throw new InvalidConfigException(\"Invalid max date value: {$this->max}\");\n            }\n            $this->max = $timestamp;\n        }\n        if ($this->min !== null && is_string($this->min)) {\n            $timestamp = $this->parseDateValue($this->min);\n            if ($timestamp === false) {\n                throw new InvalidConfigException(\"Invalid min date value: {$this->min}\");\n            }\n            $this->min = $timestamp;\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function validateAttribute($model, $attribute)\n    {\n        $value = $model->$attribute;\n        if ($this->isEmpty($value)) {\n            if ($this->timestampAttribute !== null) {\n                $model->{$this->timestampAttribute} = null;\n            }\n            return;\n        }\n\n        $timestamp = $this->parseDateValue($value);\n        if ($timestamp === false) {\n            if ($this->timestampAttribute === $attribute) {\n                if ($this->timestampAttributeFormat === null) {\n                    if (is_int($value)) {\n                        return;\n                    }\n                } elseif ($this->parseDateValueFormat($value, $this->timestampAttributeFormat) !== false) {\n                    return;\n                }\n            }\n            $this->addError($model, $attribute, $this->message, []);\n        } elseif ($this->min !== null && $timestamp < $this->min) {\n            $this->addError($model, $attribute, $this->tooSmall, ['min' => $this->minString]);\n        } elseif ($this->max !== null && $timestamp > $this->max) {\n            $this->addError($model, $attribute, $this->tooBig, ['max' => $this->maxString]);\n        } elseif ($this->timestampAttribute !== null) {\n            if ($this->timestampAttributeFormat === null) {\n                $model->{$this->timestampAttribute} = $timestamp;\n            } else {\n                $model->{$this->timestampAttribute} = $this->formatTimestamp($timestamp, $this->timestampAttributeFormat);\n            }\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function validateValue($value)\n    {\n        $timestamp = $this->parseDateValue($value);\n        if ($timestamp === false) {\n            return [$this->message, []];\n        } elseif ($this->min !== null && $timestamp < $this->min) {\n            return [$this->tooSmall, ['min' => $this->minString]];\n        } elseif ($this->max !== null && $timestamp > $this->max) {\n            return [$this->tooBig, ['max' => $this->maxString]];\n        }\n\n        return null;\n    }\n\n    /**\n     * Parses date string into UNIX timestamp.\n     *\n     * @param mixed $value string representing date\n     * @return int|false a UNIX timestamp or `false` on failure.\n     */\n    protected function parseDateValue($value)\n    {\n        // TODO consider merging these methods into single one at 2.1\n        return $this->parseDateValueFormat($value, $this->format);\n    }\n\n    /**\n     * Parses date string into UNIX timestamp.\n     *\n     * @param mixed $value string representing date\n     * @param string $format expected date format\n     * @return int|false a UNIX timestamp or `false` on failure.\n     * @throws InvalidConfigException\n     */\n    private function parseDateValueFormat($value, $format)\n    {\n        if (is_array($value)) {\n            return false;\n        }\n        if (strncmp($format, 'php:', 4) === 0) {\n            $format = substr($format, 4);\n        } else {\n            if (extension_loaded('intl')) {\n                return $this->parseDateValueIntl($value, $format);\n            }\n\n            // fallback to PHP if intl is not installed\n            $format = FormatConverter::convertDateIcuToPhp($format, 'date');\n        }\n\n        return $this->parseDateValuePHP($value, $format);\n    }\n\n    /**\n     * Parses a date value using the IntlDateFormatter::parse().\n     * @param string $value string representing date\n     * @param string $format the expected date format\n     * @return int|bool a UNIX timestamp or `false` on failure.\n     * @throws InvalidConfigException\n     */\n    private function parseDateValueIntl($value, $format)\n    {\n        $formatter = $this->getIntlDateFormatter($format);\n        // enable strict parsing to avoid getting invalid date values\n        $formatter->setLenient(false);\n\n        // There should not be a warning thrown by parse() but this seems to be the case on windows so we suppress it here\n        // See https://github.com/yiisoft/yii2/issues/5962 and https://bugs.php.net/bug.php?id=68528\n        $parsePos = 0;\n        $parsedDate = @$formatter->parse($value, $parsePos);\n        $valueLength = mb_strlen($value, Yii::$app ? Yii::$app->charset : 'UTF-8');\n        if ($parsedDate === false || $parsePos !== $valueLength || ($this->strictDateFormat && $formatter->format($parsedDate) !== $value)) {\n            return false;\n        }\n\n        return $parsedDate;\n    }\n\n    /**\n     * Creates IntlDateFormatter\n     *\n     * @param $format string date format\n     * @return IntlDateFormatter\n     * @throws InvalidConfigException\n     */\n    private function getIntlDateFormatter($format)\n    {\n        if (!isset($this->_dateFormats[$format])) {\n            // if no time was provided in the format string set timezone to default one to match yii\\i18n\\Formatter::formatDateTimeValue()\n            $timezone = strpbrk($format, 'ahHkKmsSA') !== false ? $this->timeZone : $this->defaultTimeZone;\n            return new IntlDateFormatter($this->locale, IntlDateFormatter::NONE, IntlDateFormatter::NONE, $timezone, null, $format);\n        }\n\n        if ($this->type === self::TYPE_DATE) {\n            $dateType = $this->_dateFormats[$format];\n            $timeType = IntlDateFormatter::NONE;\n            $timeZone = $this->defaultTimeZone;\n        } elseif ($this->type === self::TYPE_DATETIME) {\n            $dateType = $this->_dateFormats[$format];\n            $timeType = $this->_dateFormats[$format];\n            $timeZone = $this->timeZone;\n        } elseif ($this->type === self::TYPE_TIME) {\n            $dateType = IntlDateFormatter::NONE;\n            $timeType = $this->_dateFormats[$format];\n            $timeZone = $this->timeZone;\n        } else {\n            throw new InvalidConfigException('Unknown validation type set for DateValidator::$type: ' . $this->type);\n        }\n\n        return new IntlDateFormatter($this->locale, $dateType, $timeType, $timeZone);\n    }\n\n    /**\n     * Parses a date value using the DateTime::createFromFormat().\n     * @param string $value string representing date\n     * @param string $format the expected date format\n     * @return int|bool a UNIX timestamp or `false` on failure.\n     */\n    private function parseDateValuePHP($value, $format)\n    {\n        $hasTimeInfo = strpbrk($format, 'HhGgisU') !== false;\n        // if no time was provided in the format string set timezone to default one to match yii\\i18n\\Formatter::formatDateTimeValue()\n        $timezone = $hasTimeInfo ? $this->timeZone : $this->defaultTimeZone;\n        $date = DateTime::createFromFormat($format, $value, new DateTimeZone($timezone));\n        $errors = DateTime::getLastErrors(); // Before PHP 8.2 may return array instead of false (see https://github.com/php/php-src/issues/9431).\n        if ($date === false || ($errors !== false && ($errors['error_count'] || $errors['warning_count'])) || ($this->strictDateFormat && $date->format($format) !== $value)) {\n            return false;\n        }\n\n        if (!$hasTimeInfo) {\n            // if no time was provided in the format string set time to 0 to get a simple date timestamp\n            $date->setTime(0, 0, 0);\n        }\n\n        return $date->getTimestamp();\n    }\n\n    /**\n     * Formats a timestamp using the specified format.\n     * @param int $timestamp\n     * @param string $format\n     * @return string\n     * @throws Exception\n     */\n    private function formatTimestamp($timestamp, $format)\n    {\n        if (strncmp($format, 'php:', 4) === 0) {\n            $format = substr($format, 4);\n        } else {\n            $format = FormatConverter::convertDateIcuToPhp($format, 'date');\n        }\n\n        $date = new DateTime();\n        $date->setTimestamp($timestamp);\n        $date->setTimezone(new DateTimeZone($this->timestampAttributeTimeZone));\n\n        return $date->format($format);\n    }\n}\n"
  },
  {
    "path": "framework/validators/DefaultValueValidator.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\validators;\n\n/**\n * DefaultValueValidator sets the attribute to be the specified default value.\n *\n * DefaultValueValidator is not really a validator. It is provided mainly to allow\n * specifying attribute default values when they are empty.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass DefaultValueValidator extends Validator\n{\n    /**\n     * @var mixed the default value or an anonymous function that returns the default value which will\n     * be assigned to the attributes being validated if they are empty. The signature of the anonymous function\n     * should be as follows,\n     *\n     * ```\n     * function($model, $attribute) {\n     *     // compute value\n     *     return $value;\n     * }\n     * ```\n     */\n    public $value;\n    /**\n     * @var bool this property is overwritten to be false so that this validator will\n     * be applied when the value being validated is empty.\n     */\n    public $skipOnEmpty = false;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function validateAttribute($model, $attribute)\n    {\n        if ($this->isEmpty($model->$attribute)) {\n            if ($this->value instanceof \\Closure) {\n                $model->$attribute = call_user_func($this->value, $model, $attribute);\n            } else {\n                $model->$attribute = $this->value;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "framework/validators/EachValidator.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\validators;\n\nuse Yii;\nuse yii\\base\\DynamicModel;\nuse yii\\base\\InvalidConfigException;\nuse yii\\base\\Model;\n\n/**\n * EachValidator validates an array by checking each of its elements against an embedded validation rule.\n *\n * ```\n * class MyModel extends Model\n * {\n *     public $categoryIDs = [];\n *\n *     public function rules()\n *     {\n *         return [\n *             // checks if every category ID is an integer\n *             ['categoryIDs', 'each', 'rule' => ['integer']],\n *         ]\n *     }\n * }\n * ```\n *\n * > Note: This validator will not work with inline validation rules in case of usage outside the model scope,\n *   e.g. via [[validate()]] method.\n *\n * > Note: EachValidator is meant to be used only in basic cases, you should consider usage of tabular input,\n *   using several models for the more complex case.\n *\n * @author Paul Klimov <klimov.paul@gmail.com>\n * @since 2.0.4\n */\nclass EachValidator extends Validator\n{\n    /**\n     * @var array|Validator definition of the validation rule, which should be used on array values.\n     * It should be specified in the same format as at [[\\yii\\base\\Model::rules()]], except it should not\n     * contain attribute list as the first element.\n     * For example:\n     *\n     * ```\n     * ['integer']\n     * ['match', 'pattern' => '/[a-z]/is']\n     * ```\n     *\n     * Please refer to [[\\yii\\base\\Model::rules()]] for more details.\n     */\n    public $rule;\n    /**\n     * @var bool whether to use error message composed by validator declared via [[rule]] if its validation fails.\n     * If enabled, error message specified for this validator itself will appear only if attribute value is not an array.\n     * If disabled, own error message value will be used always.\n     */\n    public $allowMessageFromRule = true;\n    /**\n     * @var bool whether to stop validation once first error among attribute value elements is detected.\n     * When enabled validation will produce single error message on attribute, when disabled - multiple\n     * error messages mya appear: one per each invalid value.\n     * Note that this option will affect only [[validateAttribute()]] value, while [[validateValue()]] will\n     * not be affected.\n     * @since 2.0.11\n     */\n    public $stopOnFirstError = true;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        if ($this->message === null) {\n            $this->message = Yii::t('yii', '{attribute} is invalid.');\n        }\n    }\n\n    /**\n     * Creates validator object based on the validation rule specified in [[rule]].\n     * @param Model|null $model model in which context validator should be created.\n     * @param mixed|null $current value being currently validated.\n     * @throws \\yii\\base\\InvalidConfigException\n     * @return Validator validator instance\n     */\n    private function createEmbeddedValidator($model = null, $current = null)\n    {\n        $rule = $this->rule;\n        if ($rule instanceof Validator) {\n            return $rule;\n        }\n\n        if (is_array($rule) && isset($rule[0])) { // validator type\n            if (!is_object($model)) {\n                $model = new Model(); // mock up context model\n            }\n\n            $params = array_slice($rule, 1);\n            $params['current'] = $current;\n            return Validator::createValidator($rule[0], $model, $this->attributes, $params);\n        }\n\n        throw new InvalidConfigException('Invalid validation rule: a rule must be an array specifying validator type.');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function validateAttribute($model, $attribute)\n    {\n        $arrayOfValues = $model->$attribute;\n        if (!is_array($arrayOfValues) && !$arrayOfValues instanceof \\ArrayAccess) {\n            $this->addError($model, $attribute, $this->message, []);\n            return;\n        }\n\n        foreach ($arrayOfValues as $k => $v) {\n            $dynamicModel = new DynamicModel($model->getAttributes());\n            $dynamicModel->setAttributeLabels($model->attributeLabels());\n            $dynamicModel->addRule($attribute, $this->createEmbeddedValidator($model, $v));\n            $dynamicModel->defineAttribute($attribute, $v);\n            $dynamicModel->validate();\n\n            $arrayOfValues[$k] = $dynamicModel->$attribute; // filtered values like 'trim'\n\n            if (!$dynamicModel->hasErrors($attribute)) {\n                continue;\n            }\n\n            if ($this->allowMessageFromRule) {\n                $validationErrors = $dynamicModel->getErrors($attribute);\n                $model->addErrors([$attribute => $validationErrors]);\n            } else {\n                $this->addError($model, $attribute, $this->message, ['value' => $v]);\n            }\n\n            if ($this->stopOnFirstError) {\n                break;\n            }\n        }\n\n        $model->$attribute = $arrayOfValues;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function validateValue($value)\n    {\n        if (!is_array($value) && !$value instanceof \\ArrayAccess) {\n            return [$this->message, []];\n        }\n\n        $validator = $this->createEmbeddedValidator();\n        foreach ($value as $v) {\n            if ($validator->skipOnEmpty && $validator->isEmpty($v)) {\n                continue;\n            }\n            $result = $validator->validateValue($v);\n            if ($result !== null) {\n                if ($this->allowMessageFromRule) {\n                    $result[1]['value'] = $v;\n                    return $result;\n                }\n\n                return [$this->message, ['value' => $v]];\n            }\n        }\n\n        return null;\n    }\n}\n"
  },
  {
    "path": "framework/validators/EmailValidator.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\validators;\n\nuse Yii;\nuse yii\\base\\ErrorException;\nuse yii\\base\\InvalidConfigException;\nuse yii\\helpers\\Json;\nuse yii\\web\\JsExpression;\n\n/**\n * EmailValidator validates that the attribute value is a valid email address.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass EmailValidator extends Validator\n{\n    /**\n     * @var string the regular expression used to validate the attribute value.\n     * @see https://www.regular-expressions.info/email.html\n     */\n    public $pattern = '/^[a-zA-Z0-9!#$%&\\'*+\\\\/=?^_`{|}~-]+(?:\\.[a-zA-Z0-9!#$%&\\'*+\\\\/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$/';\n    /**\n     * @var string the regular expression used to validate email addresses with the name part.\n     * This property is used only when [[allowName]] is true.\n     * @see allowName\n     */\n    public $fullPattern = '/^[^@]*<[a-zA-Z0-9!#$%&\\'*+\\\\/=?^_`{|}~-]+(?:\\.[a-zA-Z0-9!#$%&\\'*+\\\\/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?>$/';\n    /**\n     * @var string the regular expression used to validate the part before the @ symbol, used if ASCII conversion fails to validate the address.\n     * @see https://www.regular-expressions.info/email.html\n     * @since 2.0.42\n     */\n    public $patternASCII = '/^[a-zA-Z0-9!#$%&\\'*+\\\\/=?^_`{|}~-]+(?:\\.[a-zA-Z0-9!#$%&\\'*+\\\\/=?^_`{|}~-]+)*$/';\n    /**\n     * @var string the regular expression used to validate email addresses with the name part before the @ symbol, used if ASCII conversion fails to validate the address.\n     * This property is used only when [[allowName]] is true.\n     * @see allowName\n     * @since 2.0.42\n     */\n    public $fullPatternASCII = '/^[^@]*<[a-zA-Z0-9!#$%&\\'*+\\\\/=?^_`{|}~-]+(?:\\.[a-zA-Z0-9!#$%&\\'*+\\\\/=?^_`{|}~-]+)*$/';\n    /**\n     * @var bool whether to allow name in the email address (e.g. \"John Smith <john.smith@example.com>\"). Defaults to false.\n     * @see fullPattern\n     */\n    public $allowName = false;\n    /**\n     * @var bool whether to check whether the email's domain exists and has either an A or MX record.\n     * Be aware that this check can fail due to temporary DNS problems even if the email address is\n     * valid and an email would be deliverable. Defaults to false.\n     */\n    public $checkDNS = false;\n    /**\n     * @var bool whether validation process should take into account IDN (internationalized domain\n     * names). Defaults to false meaning that validation of emails containing IDN will always fail.\n     * Note that in order to use IDN validation you have to install and enable `intl` PHP extension,\n     * otherwise an exception would be thrown.\n     */\n    public $enableIDN = false;\n    /**\n     * @var bool whether [[enableIDN]] should apply to the local part of the email (left side\n     * of the `@`). Only applies if [[enableIDN]] is `true`.\n     * @since 2.0.43\n     */\n    public $enableLocalIDN = true;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        if ($this->enableIDN && !function_exists('idn_to_ascii')) {\n            throw new InvalidConfigException('In order to use IDN validation intl extension must be installed and enabled.');\n        }\n        if ($this->message === null) {\n            $this->message = Yii::t('yii', '{attribute} is not a valid email address.');\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function validateValue($value)\n    {\n        if (!is_string($value)) {\n            $valid = false;\n        } elseif (!preg_match('/^(?P<name>(?:\"?([^\"]*)\"?\\s)?)(?:\\s+)?(?:(?P<open><?)((?P<local>.+)@(?P<domain>[^>]+))(?P<close>>?))$/i', $value, $matches)) {\n            $valid = false;\n        } else {\n            if ($this->enableIDN) {\n                if ($this->enableLocalIDN) {\n                    $matches['local'] = $this->idnToAsciiWithFallback($matches['local']);\n                }\n                $matches['domain'] = $this->idnToAscii($matches['domain']);\n                $value = $matches['name'] . $matches['open'] . $matches['local'] . '@' . $matches['domain'] . $matches['close'];\n            }\n\n            if (strlen($matches['local']) > 64) {\n                // The maximum total length of a user name or other local-part is 64 octets. RFC 5322 section 4.5.3.1.1\n                // https://datatracker.ietf.org/doc/html/rfc5321#section-4.5.3.1.1\n                $valid = false;\n            } elseif (strlen($matches['local'] . '@' . $matches['domain']) > 254) {\n                // There is a restriction in RFC 2821 on the length of an address in MAIL and RCPT commands\n                // of 254 characters. Since addresses that do not fit in those fields are not normally useful, the\n                // upper limit on address lengths should normally be considered to be 254.\n                //\n                // Dominic Sayers, RFC 3696 erratum 1690\n                // https://www.rfc-editor.org/errata_search.php?eid=1690\n                $valid = false;\n            } else {\n                $valid = preg_match($this->pattern, $value) || ($this->allowName && preg_match($this->fullPattern, $value));\n                if ($valid && $this->checkDNS) {\n                    $valid = $this->isDNSValid($matches['domain']);\n                }\n            }\n        }\n\n        return $valid ? null : [$this->message, []];\n    }\n\n    /**\n     * @param string $domain\n     * @return bool if DNS records for domain are valid\n     * @see https://github.com/yiisoft/yii2/issues/17083\n     */\n    protected function isDNSValid($domain)\n    {\n        return $this->hasDNSRecord($domain, true) || $this->hasDNSRecord($domain, false);\n    }\n\n    private function hasDNSRecord($domain, $isMX)\n    {\n        $normalizedDomain = $domain . '.';\n        if (!checkdnsrr($normalizedDomain, ($isMX ? 'MX' : 'A'))) {\n            return false;\n        }\n\n        try {\n            // dns_get_record can return false and emit Warning that may or may not be converted to ErrorException\n            $records = dns_get_record($normalizedDomain, ($isMX ? DNS_MX : DNS_A));\n        } catch (ErrorException $exception) {\n            return false;\n        }\n\n        return !empty($records);\n    }\n\n    private function idnToAscii($idn)\n    {\n        return idn_to_ascii($idn, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function clientValidateAttribute($model, $attribute, $view)\n    {\n        ValidationAsset::register($view);\n        if ($this->enableIDN) {\n            PunycodeAsset::register($view);\n        }\n        $options = $this->getClientOptions($model, $attribute);\n\n        return 'yii.validation.email(value, messages, ' . Json::htmlEncode($options) . ');';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getClientOptions($model, $attribute)\n    {\n        $options = [\n            'pattern' => new JsExpression($this->pattern),\n            'fullPattern' => new JsExpression($this->fullPattern),\n            'allowName' => $this->allowName,\n            'message' => $this->formatMessage($this->message, [\n                'attribute' => $model->getAttributeLabel($attribute),\n            ]),\n            'enableIDN' => (bool) $this->enableIDN,\n        ];\n        if ($this->skipOnEmpty) {\n            $options['skipOnEmpty'] = 1;\n        }\n\n        return $options;\n    }\n\n    /**\n     * @param string $value\n     * @return string|bool returns string if it is valid and/or can be converted, bool false if it can't be converted and/or is invalid\n     * @see https://github.com/yiisoft/yii2/issues/18585\n     */\n    private function idnToAsciiWithFallback($value)\n    {\n        $ascii = $this->idnToAscii($value);\n        if ($ascii === false) {\n            if (preg_match($this->patternASCII, $value) || ($this->allowName && preg_match($this->fullPatternASCII, $value))) {\n                return $value;\n            }\n        }\n\n        return $ascii;\n    }\n}\n"
  },
  {
    "path": "framework/validators/ExistValidator.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\validators;\n\nuse Yii;\nuse yii\\base\\InvalidConfigException;\nuse yii\\base\\Model;\nuse yii\\db\\ActiveQuery;\nuse yii\\db\\ActiveRecord;\nuse yii\\db\\QueryInterface;\n\n/**\n * ExistValidator validates that the attribute value exists in a table.\n *\n * ExistValidator checks if the value being validated can be found in the table column specified by\n * the ActiveRecord class [[targetClass]] and the attribute [[targetAttribute]].\n * Since version 2.0.14 you can use more convenient attribute [[targetRelation]]\n *\n * This validator is often used to verify that a foreign key contains a value\n * that can be found in the foreign table.\n *\n * The following are examples of validation rules using this validator:\n *\n * ```\n * // a1 needs to exist\n * ['a1', 'exist']\n * // a1 needs to exist, but its value will use a2 to check for the existence\n * ['a1', 'exist', 'targetAttribute' => 'a2']\n * // a1 and a2 need to exist together, and they both will receive error message\n * [['a1', 'a2'], 'exist', 'targetAttribute' => ['a1', 'a2']]\n * // a1 and a2 need to exist together, only a1 will receive error message\n * ['a1', 'exist', 'targetAttribute' => ['a1', 'a2']]\n * // a1 needs to exist by checking the existence of both a2 and a3 (using a1 value)\n * ['a1', 'exist', 'targetAttribute' => ['a2', 'a1' => 'a3']]\n * // type_id needs to exist in the column \"id\" in the table defined in ProductType class\n * ['type_id', 'exist', 'targetClass' => ProductType::class, 'targetAttribute' => ['type_id' => 'id']],\n * // the same as the previous, but using already defined relation \"type\"\n * ['type_id', 'exist', 'targetRelation' => 'type'],\n * ```\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass ExistValidator extends Validator\n{\n    /**\n     * @var string|null the name of the ActiveRecord class that should be used to validate the existence\n     * of the current attribute value. If not set, it will use the ActiveRecord class of the attribute being validated.\n     * @see targetAttribute\n     */\n    public $targetClass;\n    /**\n     * @var string|array|null the name of the ActiveRecord attribute that should be used to\n     * validate the existence of the current attribute value. If not set, it will use the name\n     * of the attribute currently being validated. You may use an array to validate the existence\n     * of multiple columns at the same time. The array key is the name of the attribute with the value to validate,\n     * the array value is the name of the database field to search.\n     */\n    public $targetAttribute;\n    /**\n     * @var string the name of the relation that should be used to validate the existence of the current attribute value\n     * This param overwrites $targetClass and $targetAttribute\n     * @since 2.0.14\n     */\n    public $targetRelation;\n    /**\n     * @var string|array|\\Closure additional filter to be applied to the DB query used to check the existence of the attribute value.\n     * This can be a string or an array representing the additional query condition (refer to [[\\yii\\db\\Query::where()]]\n     * on the format of query condition), or an anonymous function with the signature `function ($query)`, where `$query`\n     * is the [[\\yii\\db\\Query|Query]] object that you can modify in the function.\n     */\n    public $filter;\n    /**\n     * @var bool whether to allow array type attribute.\n     */\n    public $allowArray = false;\n    /**\n     * @var string and|or define how target attributes are related\n     * @since 2.0.11\n     */\n    public $targetAttributeJunction = 'and';\n    /**\n     * @var bool whether this validator is forced to always use master DB\n     * @since 2.0.14\n     */\n    public $forceMasterDb = true;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        if ($this->message === null) {\n            $this->message = Yii::t('yii', '{attribute} is invalid.');\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function validateAttribute($model, $attribute)\n    {\n        if (!empty($this->targetRelation)) {\n            $this->checkTargetRelationExistence($model, $attribute);\n        } else {\n            $this->checkTargetAttributeExistence($model, $attribute);\n        }\n    }\n\n    /**\n     * Validates existence of the current attribute based on relation name\n     * @param ActiveRecord $model the data model to be validated\n     * @param string $attribute the name of the attribute to be validated.\n     */\n    private function checkTargetRelationExistence($model, $attribute)\n    {\n        $exists = false;\n\n        /** @var ActiveQuery<ActiveRecord> $relationQuery */\n        $relationQuery = $model->{'get' . ucfirst($this->targetRelation)}();\n\n        if ($this->filter instanceof \\Closure) {\n            call_user_func($this->filter, $relationQuery);\n        } elseif ($this->filter !== null) {\n            $relationQuery->andWhere($this->filter);\n        }\n\n        $connection = $model::getDb();\n        if ($this->forceMasterDb && method_exists($connection, 'useMaster')) {\n            $exists = $connection->useMaster(function () use ($relationQuery) {\n                return $relationQuery->exists();\n            });\n        } else {\n            $exists = $relationQuery->exists();\n        }\n\n        if (!$exists) {\n            $this->addError($model, $attribute, $this->message);\n        }\n    }\n\n    /**\n     * Validates existence of the current attribute based on targetAttribute\n     * @param \\yii\\base\\Model $model the data model to be validated\n     * @param string $attribute the name of the attribute to be validated.\n     */\n    private function checkTargetAttributeExistence($model, $attribute)\n    {\n        $targetAttribute = $this->targetAttribute === null ? $attribute : $this->targetAttribute;\n        if ($this->skipOnError) {\n            foreach ((array)$targetAttribute as $k => $v) {\n                if ($model->hasErrors(is_int($k) ? $v : $k)) {\n                    return;\n                }\n            }\n        }\n\n        $params = $this->prepareConditions($targetAttribute, $model, $attribute);\n        $conditions = [$this->targetAttributeJunction === 'or' ? 'or' : 'and'];\n\n        if (!$this->allowArray) {\n            foreach ($params as $key => $value) {\n                if (is_array($value)) {\n                    $this->addError($model, $attribute, Yii::t('yii', '{attribute} is invalid.'));\n\n                    return;\n                }\n                $conditions[] = [$key => $value];\n            }\n        } else {\n            $conditions[] = $params;\n        }\n\n        $targetClass = $this->getTargetClass($model);\n        $query = $this->createQuery($targetClass, $conditions);\n\n        if (!$this->valueExists($targetClass, $query, $model->$attribute)) {\n            $this->addError($model, $attribute, $this->message);\n        }\n    }\n\n    /**\n     * Processes attributes' relations described in $targetAttribute parameter into conditions, compatible with\n     * [[\\yii\\db\\Query::where()|Query::where()]] key-value format.\n     *\n     * @param $targetAttribute array|string|null $attribute the name of the ActiveRecord attribute that should be used to\n     * validate the existence of the current attribute value. If not set, it will use the name\n     * of the attribute currently being validated. You may use an array to validate the existence\n     * of multiple columns at the same time. The array key is the name of the attribute with the value to validate,\n     * the array value is the name of the database field to search.\n     * If the key and the value are the same, you can just specify the value.\n     * @param \\yii\\base\\Model $model the data model to be validated\n     * @param string $attribute the name of the attribute to be validated in the $model\n     * @return array conditions, compatible with [[\\yii\\db\\Query::where()|Query::where()]] key-value format.\n     * @throws InvalidConfigException\n     */\n    private function prepareConditions($targetAttribute, $model, $attribute)\n    {\n        if (is_array($targetAttribute)) {\n            if ($this->allowArray) {\n                throw new InvalidConfigException('The \"targetAttribute\" property must be configured as a string.');\n            }\n            $conditions = [];\n            foreach ($targetAttribute as $k => $v) {\n                $conditions[$v] = is_int($k) ? $model->$v : $model->$k;\n            }\n        } else {\n            $conditions = [$targetAttribute => $model->$attribute];\n        }\n\n        $targetModelClass = $this->getTargetClass($model);\n        if (!is_subclass_of($targetModelClass, 'yii\\db\\ActiveRecord')) {\n            return $conditions;\n        }\n\n        /** @var ActiveRecord $targetModelClass */\n        return $this->applyTableAlias($targetModelClass::find(), $conditions);\n    }\n\n    /**\n     * @param Model $model the data model to be validated\n     * @return string Target class name\n     */\n    private function getTargetClass($model)\n    {\n        return $this->targetClass === null ? get_class($model) : $this->targetClass;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function validateValue($value)\n    {\n        if ($this->targetClass === null) {\n            throw new InvalidConfigException('The \"targetClass\" property must be set.');\n        }\n        if (!is_string($this->targetAttribute)) {\n            throw new InvalidConfigException('The \"targetAttribute\" property must be configured as a string.');\n        }\n\n        if (is_array($value) && !$this->allowArray) {\n            return [$this->message, []];\n        }\n\n        $query = $this->createQuery($this->targetClass, [$this->targetAttribute => $value]);\n\n        return $this->valueExists($this->targetClass, $query, $value) ? null : [$this->message, []];\n    }\n\n    /**\n     * Check whether value exists in target table.\n     *\n     * @param string $targetClass the model\n     * @param QueryInterface $query\n     * @param mixed $value the value want to be checked\n     * @return bool\n     */\n    private function valueExists($targetClass, $query, $value)\n    {\n        $db = $targetClass::getDb();\n\n        if ($this->forceMasterDb && method_exists($db, 'useMaster')) {\n            return $db->useMaster(function () use ($query, $value) {\n                return $this->queryValueExists($query, $value);\n            });\n        }\n\n        return $this->queryValueExists($query, $value);\n    }\n\n\n    /**\n     * Run query to check if value exists.\n     *\n     * @param QueryInterface $query\n     * @param mixed $value the value to be checked\n     * @return bool\n     */\n    private function queryValueExists($query, $value)\n    {\n        if (is_array($value)) {\n            return $query->count(\"DISTINCT [[$this->targetAttribute]]\") == count(array_unique($value));\n        }\n\n        return $query->exists();\n    }\n\n    /**\n     * Creates a query instance with the given condition.\n     * @param string $targetClass the target AR class\n     * @param mixed $condition query condition\n     * @return \\yii\\db\\ActiveQueryInterface the query instance\n     */\n    protected function createQuery($targetClass, $condition)\n    {\n        /** @var \\yii\\db\\ActiveRecordInterface $targetClass */\n        $query = $targetClass::find()->andWhere($condition);\n        if ($this->filter instanceof \\Closure) {\n            call_user_func($this->filter, $query);\n        } elseif ($this->filter !== null) {\n            $query->andWhere($this->filter);\n        }\n\n        return $query;\n    }\n\n    /**\n     * Returns conditions with alias.\n     * @param ActiveQuery<ActiveRecord> $query\n     * @param array $conditions array of condition, keys to be modified\n     * @param string|null $alias set empty string for no apply alias. Set null for apply primary table alias\n     * @return array\n     */\n    private function applyTableAlias($query, $conditions, $alias = null)\n    {\n        if ($alias === null) {\n            $alias = array_keys($query->getTablesUsedInFrom())[0];\n        }\n        $prefixedConditions = [];\n        foreach ($conditions as $columnName => $columnValue) {\n            if (strpos($columnName, '(') === false) {\n                $prefixedColumn = \"{$alias}.[[\" . preg_replace(\n                    '/^' . preg_quote($alias, '/') . '\\.(.*)$/',\n                    '$1',\n                    $columnName\n                ) . ']]';\n            } else {\n                // there is an expression, can't prefix it reliably\n                $prefixedColumn = $columnName;\n            }\n\n            $prefixedConditions[$prefixedColumn] = $columnValue;\n        }\n\n        return $prefixedConditions;\n    }\n}\n"
  },
  {
    "path": "framework/validators/FileValidator.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\validators;\n\nuse Yii;\nuse yii\\helpers\\FileHelper;\nuse yii\\helpers\\Html;\nuse yii\\helpers\\Json;\nuse yii\\helpers\\StringHelper;\nuse yii\\web\\JsExpression;\nuse yii\\web\\UploadedFile;\n\n/**\n * FileValidator verifies if an attribute is receiving a valid uploaded file.\n *\n * Note that you should enable `fileinfo` PHP extension.\n *\n * @property-read int $sizeLimit The size limit for uploaded files.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass FileValidator extends Validator\n{\n    /**\n     * @var array|string|null a list of file name extensions that are allowed to be uploaded.\n     * This can be either an array or a string consisting of file extension names\n     * separated by space or comma (e.g. \"gif, jpg\").\n     * Extension names are case-insensitive. Defaults to null, meaning all file name\n     * extensions are allowed.\n     * @see wrongExtension for the customized message for wrong file type.\n     */\n    public $extensions;\n    /**\n     * @var bool whether to check file type (extension) with mime-type. If extension produced by\n     * file mime-type check differs from uploaded file extension, the file will be considered as invalid.\n     */\n    public $checkExtensionByMimeType = true;\n    /**\n     * @var array|string|null a list of file MIME types that are allowed to be uploaded.\n     * This can be either an array or a string consisting of file MIME types\n     * separated by space or comma (e.g. \"text/plain, image/png\").\n     * The mask with the special character `*` can be used to match groups of mime types.\n     * For example `image/*` will pass all mime types, that begin with `image/` (e.g. `image/jpeg`, `image/png`).\n     * Mime type names are case-insensitive. Defaults to null, meaning all MIME types are allowed.\n     * @see wrongMimeType for the customized message for wrong MIME type.\n     */\n    public $mimeTypes;\n    /**\n     * @var int|null the minimum number of bytes required for the uploaded file.\n     * Defaults to null, meaning no limit.\n     * @see tooSmall for the customized message for a file that is too small.\n     */\n    public $minSize;\n    /**\n     * @var int|null the maximum number of bytes required for the uploaded file.\n     * Defaults to null, meaning no limit.\n     * Note, the size limit is also affected by `upload_max_filesize` and `post_max_size` INI setting\n     * and the 'MAX_FILE_SIZE' hidden field value. See [[getSizeLimit()]] for details.\n     * @see https://www.php.net/manual/en/ini.core.php#ini.upload-max-filesize\n     * @see https://www.php.net/post-max-size\n     * @see getSizeLimit\n     * @see tooBig for the customized message for a file that is too big.\n     */\n    public $maxSize;\n    /**\n     * @var int the maximum file count the given attribute can hold.\n     * Defaults to 1, meaning single file upload. By defining a higher number,\n     * multiple uploads become possible. Setting it to `0` means there is no limit on\n     * the number of files that can be uploaded simultaneously.\n     *\n     * > Note: The maximum number of files allowed to be uploaded simultaneously is\n     * also limited with PHP directive `max_file_uploads`, which defaults to 20.\n     *\n     * @see https://www.php.net/manual/en/ini.core.php#ini.max-file-uploads\n     * @see tooMany for the customized message when too many files are uploaded.\n     */\n    public $maxFiles = 1;\n    /**\n     * @var int the minimum file count the given attribute can hold.\n     * Defaults to 0. Higher value means at least that number of files should be uploaded.\n     *\n     * @see tooFew for the customized message when too few files are uploaded.\n     * @since 2.0.14\n     */\n    public $minFiles = 0;\n    /**\n     * @var string the error message used when a file is not uploaded correctly.\n     */\n    public $message;\n    /**\n     * @var string the error message used when no file is uploaded.\n     * Note that this is the text of the validation error message. To make uploading files required,\n     * you have to set [[skipOnEmpty]] to `false`.\n     */\n    public $uploadRequired;\n    /**\n     * @var string the error message used when the uploaded file is too large.\n     * You may use the following tokens in the message:\n     *\n     * - {attribute}: the attribute name\n     * - {file}: the uploaded file name\n     * - {limit}: the maximum size allowed (see [[getSizeLimit()]])\n     * - {formattedLimit}: the maximum size formatted\n     *   with [[\\yii\\i18n\\Formatter::asShortSize()|Formatter::asShortSize()]]\n     */\n    public $tooBig;\n    /**\n     * @var string the error message used when the uploaded file is too small.\n     * You may use the following tokens in the message:\n     *\n     * - {attribute}: the attribute name\n     * - {file}: the uploaded file name\n     * - {limit}: the value of [[minSize]]\n     * - {formattedLimit}: the value of [[minSize]] formatted\n     *   with [[\\yii\\i18n\\Formatter::asShortSize()|Formatter::asShortSize()]\n     */\n    public $tooSmall;\n    /**\n     * @var string the error message used if the count of multiple uploads exceeds limit.\n     * You may use the following tokens in the message:\n     *\n     * - {attribute}: the attribute name\n     * - {limit}: the value of [[maxFiles]]\n     */\n    public $tooMany;\n    /**\n     * @var string the error message used if the count of multiple uploads less that minFiles.\n     * You may use the following tokens in the message:\n     *\n     * - {attribute}: the attribute name\n     * - {limit}: the value of [[minFiles]]\n     *\n     * @since 2.0.14\n     */\n    public $tooFew;\n    /**\n     * @var string the error message used when the uploaded file has an extension name\n     * that is not listed in [[extensions]]. You may use the following tokens in the message:\n     *\n     * - {attribute}: the attribute name\n     * - {file}: the uploaded file name\n     * - {extensions}: the list of the allowed extensions.\n     */\n    public $wrongExtension;\n    /**\n     * @var string the error message used when the file has an mime type\n     * that is not allowed by [[mimeTypes]] property.\n     * You may use the following tokens in the message:\n     *\n     * - {attribute}: the attribute name\n     * - {file}: the uploaded file name\n     * - {mimeTypes}: the value of [[mimeTypes]]\n     */\n    public $wrongMimeType;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        if ($this->message === null) {\n            $this->message = Yii::t('yii', 'File upload failed.');\n        }\n        if ($this->uploadRequired === null) {\n            $this->uploadRequired = Yii::t('yii', 'Please upload a file.');\n        }\n        if ($this->tooMany === null) {\n            $this->tooMany = Yii::t('yii', 'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.');\n        }\n        if ($this->tooFew === null) {\n            $this->tooFew = Yii::t('yii', 'You should upload at least {limit, number} {limit, plural, one{file} other{files}}.');\n        }\n        if ($this->wrongExtension === null) {\n            $this->wrongExtension = Yii::t('yii', 'Only files with these extensions are allowed: {extensions}.');\n        }\n        if ($this->tooBig === null) {\n            $this->tooBig = Yii::t('yii', 'The file \"{file}\" is too big. Its size cannot exceed {formattedLimit}.');\n        }\n        if ($this->tooSmall === null) {\n            $this->tooSmall = Yii::t('yii', 'The file \"{file}\" is too small. Its size cannot be smaller than {formattedLimit}.');\n        }\n        if (!is_array($this->extensions)) {\n            $this->extensions = preg_split('/[\\s,]+/', strtolower((string)$this->extensions), -1, PREG_SPLIT_NO_EMPTY);\n        } else {\n            $this->extensions = array_map('strtolower', $this->extensions);\n        }\n        if ($this->wrongMimeType === null) {\n            $this->wrongMimeType = Yii::t('yii', 'Only files with these MIME types are allowed: {mimeTypes}.');\n        }\n        if (!is_array($this->mimeTypes)) {\n            $this->mimeTypes = preg_split('/[\\s,]+/', strtolower((string)$this->mimeTypes), -1, PREG_SPLIT_NO_EMPTY);\n        } else {\n            $this->mimeTypes = array_map('strtolower', $this->mimeTypes);\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function validateAttribute($model, $attribute)\n    {\n        $files = $this->filterFiles(is_array($model->$attribute) ? $model->$attribute : [$model->$attribute]);\n        $filesCount = count($files);\n        if ($filesCount === 0) {\n            $this->addError($model, $attribute, $this->uploadRequired);\n\n            return;\n        }\n\n        if ($this->maxFiles > 0 && $filesCount > $this->maxFiles) {\n            $this->addError($model, $attribute, $this->tooMany, ['limit' => $this->maxFiles]);\n        }\n        if ($this->minFiles > 0 && $this->minFiles > $filesCount) {\n            $this->addError($model, $attribute, $this->tooFew, ['limit' => $this->minFiles]);\n        }\n\n        foreach ($files as $file) {\n            $result = $this->validateValue($file);\n            if (!empty($result)) {\n                $this->addError($model, $attribute, $result[0], $result[1]);\n            }\n        }\n    }\n\n    /**\n     * Files filter.\n     * @param array $files\n     * @return UploadedFile[]\n     */\n    private function filterFiles(array $files)\n    {\n        $result = [];\n\n        foreach ($files as $fileName => $file) {\n            if ($file instanceof UploadedFile && $file->error !== UPLOAD_ERR_NO_FILE) {\n                $result[$fileName] = $file;\n            }\n        }\n\n        return $result;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function validateValue($value)\n    {\n        if (!$value instanceof UploadedFile || $value->error == UPLOAD_ERR_NO_FILE) {\n            return [$this->uploadRequired, []];\n        }\n\n        switch ($value->error) {\n            case UPLOAD_ERR_OK:\n                if ($this->maxSize !== null && $value->size > $this->getSizeLimit()) {\n                    return [\n                        $this->tooBig,\n                        [\n                            'file' => $value->name,\n                            'limit' => $this->getSizeLimit(),\n                            'formattedLimit' => Yii::$app->formatter->asShortSize($this->getSizeLimit()),\n                        ],\n                    ];\n                } elseif ($this->minSize !== null && $value->size < $this->minSize) {\n                    return [\n                        $this->tooSmall,\n                        [\n                            'file' => $value->name,\n                            'limit' => $this->minSize,\n                            'formattedLimit' => Yii::$app->formatter->asShortSize($this->minSize),\n                        ],\n                    ];\n                } elseif (!empty($this->extensions) && !$this->validateExtension($value)) {\n                    return [$this->wrongExtension, ['file' => $value->name, 'extensions' => implode(', ', $this->extensions)]];\n                } elseif (!empty($this->mimeTypes) && !$this->validateMimeType($value)) {\n                    return [$this->wrongMimeType, ['file' => $value->name, 'mimeTypes' => implode(', ', $this->mimeTypes)]];\n                }\n\n                return null;\n            case UPLOAD_ERR_INI_SIZE:\n            case UPLOAD_ERR_FORM_SIZE:\n                return [$this->tooBig, [\n                    'file' => $value->name,\n                    'limit' => $this->getSizeLimit(),\n                    'formattedLimit' => Yii::$app->formatter->asShortSize($this->getSizeLimit()),\n                ]];\n            case UPLOAD_ERR_PARTIAL:\n                Yii::warning('File was only partially uploaded: ' . $value->name, __METHOD__);\n                break;\n            case UPLOAD_ERR_NO_TMP_DIR:\n                Yii::warning('Missing the temporary folder to store the uploaded file: ' . $value->name, __METHOD__);\n                break;\n            case UPLOAD_ERR_CANT_WRITE:\n                Yii::warning('Failed to write the uploaded file to disk: ' . $value->name, __METHOD__);\n                break;\n            case UPLOAD_ERR_EXTENSION:\n                Yii::warning('File upload was stopped by some PHP extension: ' . $value->name, __METHOD__);\n                break;\n            default:\n                break;\n        }\n\n        return [$this->message, []];\n    }\n\n    /**\n     * Returns the maximum size allowed for uploaded files.\n     *\n     * This is determined based on four factors:\n     *\n     * - 'upload_max_filesize' in php.ini\n     * - 'post_max_size' in php.ini\n     * - 'MAX_FILE_SIZE' hidden field\n     * - [[maxSize]]\n     *\n     * @return int the size limit for uploaded files.\n     */\n    public function getSizeLimit()\n    {\n        // Get the lowest between post_max_size and upload_max_filesize, log a warning if the first is < than the latter\n        $limit = StringHelper::convertIniSizeToBytes(ini_get('upload_max_filesize'));\n        $postLimit = StringHelper::convertIniSizeToBytes(ini_get('post_max_size'));\n        if ($postLimit > 0 && $postLimit < $limit) {\n            Yii::warning('PHP.ini\\'s \\'post_max_size\\' is less than \\'upload_max_filesize\\'.', __METHOD__);\n            $limit = $postLimit;\n        }\n        if ($this->maxSize !== null && $limit > 0 && $this->maxSize < $limit) {\n            $limit = $this->maxSize;\n        }\n        if (isset($_POST['MAX_FILE_SIZE']) && $_POST['MAX_FILE_SIZE'] > 0 && $_POST['MAX_FILE_SIZE'] < $limit) {\n            $limit = (int) $_POST['MAX_FILE_SIZE'];\n        }\n\n        return $limit;\n    }\n\n    /**\n     * {@inheritdoc}\n     * @param bool $trim\n     */\n    public function isEmpty($value, $trim = false)\n    {\n        $value = is_array($value) ? reset($value) : $value;\n        return !($value instanceof UploadedFile) || $value->error == UPLOAD_ERR_NO_FILE;\n    }\n\n    /**\n     * Checks if given uploaded file have correct type (extension) according current validator settings.\n     * @param UploadedFile $file\n     * @return bool\n     */\n    protected function validateExtension($file)\n    {\n        $extension = mb_strtolower($file->extension, 'UTF-8');\n\n        if ($this->checkExtensionByMimeType) {\n            $mimeType = FileHelper::getMimeType($file->tempName, null, false);\n            if ($mimeType === null) {\n                return false;\n            }\n\n            $extensionsByMimeType = FileHelper::getExtensionsByMimeType($mimeType);\n\n            if (!in_array($extension, $extensionsByMimeType, true)) {\n                return false;\n            }\n        }\n\n        if (!empty($this->extensions)) {\n            foreach ((array) $this->extensions as $ext) {\n                if ($extension === $ext || StringHelper::endsWith($file->name, \".$ext\", false)) {\n                    return true;\n                }\n            }\n            return false;\n        }\n\n        return true;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function clientValidateAttribute($model, $attribute, $view)\n    {\n        ValidationAsset::register($view);\n        $options = $this->getClientOptions($model, $attribute);\n        return 'yii.validation.file(attribute, messages, ' . Json::htmlEncode($options) . ');';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getClientOptions($model, $attribute)\n    {\n        $label = $model->getAttributeLabel($attribute);\n\n        $options = [];\n        if ($this->message !== null) {\n            $options['message'] = $this->formatMessage($this->message, [\n                'attribute' => $label,\n            ]);\n        }\n\n        $options['skipOnEmpty'] = $this->skipOnEmpty;\n\n        if (!$this->skipOnEmpty) {\n            $options['uploadRequired'] = $this->formatMessage($this->uploadRequired, [\n                'attribute' => $label,\n            ]);\n        }\n\n        if ($this->mimeTypes !== null) {\n            $mimeTypes = [];\n            foreach ($this->mimeTypes as $mimeType) {\n                $mimeTypes[] = new JsExpression(Html::escapeJsRegularExpression($this->buildMimeTypeRegexp($mimeType)));\n            }\n            $options['mimeTypes'] = $mimeTypes;\n            $options['wrongMimeType'] = $this->formatMessage($this->wrongMimeType, [\n                'attribute' => $label,\n                'mimeTypes' => implode(', ', $this->mimeTypes),\n            ]);\n        }\n\n        if ($this->extensions !== null) {\n            $options['extensions'] = $this->extensions;\n            $options['wrongExtension'] = $this->formatMessage($this->wrongExtension, [\n                'attribute' => $label,\n                'extensions' => implode(', ', $this->extensions),\n            ]);\n        }\n\n        if ($this->minSize !== null) {\n            $options['minSize'] = $this->minSize;\n            $options['tooSmall'] = $this->formatMessage($this->tooSmall, [\n                'attribute' => $label,\n                'limit' => $this->minSize,\n                'formattedLimit' => Yii::$app->formatter->asShortSize($this->minSize),\n            ]);\n        }\n\n        if ($this->maxSize !== null) {\n            $options['maxSize'] = $this->maxSize;\n            $options['tooBig'] = $this->formatMessage($this->tooBig, [\n                'attribute' => $label,\n                'limit' => $this->getSizeLimit(),\n                'formattedLimit' => Yii::$app->formatter->asShortSize($this->getSizeLimit()),\n            ]);\n        }\n\n        if ($this->maxFiles !== null) {\n            $options['maxFiles'] = $this->maxFiles;\n            $options['tooMany'] = $this->formatMessage($this->tooMany, [\n                'attribute' => $label,\n                'limit' => $this->maxFiles,\n            ]);\n        }\n\n        return $options;\n    }\n\n    /**\n     * Builds the RegExp from the $mask.\n     *\n     * @param string $mask\n     * @return string the regular expression\n     * @see mimeTypes\n     */\n    private function buildMimeTypeRegexp($mask)\n    {\n        return '/^' . str_replace('\\*', '.*', preg_quote($mask, '/')) . '$/i';\n    }\n\n    /**\n     * Checks the mimeType of the $file against the list in the [[mimeTypes]] property.\n     *\n     * @param UploadedFile $file\n     * @return bool whether the $file mimeType is allowed\n     * @throws \\yii\\base\\InvalidConfigException\n     * @see mimeTypes\n     * @since 2.0.8\n     */\n    protected function validateMimeType($file)\n    {\n        $fileMimeType = $this->getMimeTypeByFile($file->tempName);\n        if ($fileMimeType === null) {\n            return false;\n        }\n\n        foreach ($this->mimeTypes as $mimeType) {\n            if (strcasecmp($mimeType, $fileMimeType) === 0) {\n                return true;\n            }\n\n            if (strpos($mimeType, '*') !== false && preg_match($this->buildMimeTypeRegexp($mimeType), $fileMimeType)) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * Get MIME type by file path\n     *\n     * @param string $filePath\n     * @return string|null\n     * @throws \\yii\\base\\InvalidConfigException\n     * @since 2.0.26\n     */\n    protected function getMimeTypeByFile($filePath)\n    {\n        return FileHelper::getMimeType($filePath);\n    }\n}\n"
  },
  {
    "path": "framework/validators/FilterValidator.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\validators;\n\nuse yii\\base\\InvalidConfigException;\nuse yii\\helpers\\Json;\n\n/**\n * FilterValidator converts the attribute value according to a filter.\n *\n * FilterValidator is actually not a validator but a data processor.\n * It invokes the specified filter callback to process the attribute value\n * and save the processed value back to the attribute. The filter must be\n * a valid PHP callback with the following signature:\n *\n * ```\n * function foo($value) {\n *     // compute $newValue here\n *     return $newValue;\n * }\n * ```\n *\n * Many PHP functions qualify this signature (e.g. `trim()`).\n * If the callback function requires non-null argument (important since PHP 8.1)\n * remember to set [[skipOnEmpty]] to `true` otherwise you may trigger an error.\n *\n * To specify the filter, set [[filter]] property to be the callback.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass FilterValidator extends Validator\n{\n    /**\n     * @var callable the filter. This can be a global function name, anonymous function, etc.\n     * The function signature must be as follows,\n     *\n     * ```\n     * function foo($value) {\n     *     // compute $newValue here\n     *     return $newValue;\n     * }\n     * ```\n     */\n    public $filter;\n    /**\n     * @var bool whether the filter should be skipped if an array input is given.\n     * If true and an array input is given, the filter will not be applied.\n     */\n    public $skipOnArray = false;\n    /**\n     * @var bool this property is overwritten to be false so that this validator will\n     * be applied when the value being validated is empty.\n     */\n    public $skipOnEmpty = false;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        if ($this->filter === null) {\n            throw new InvalidConfigException('The \"filter\" property must be set.');\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function validateAttribute($model, $attribute)\n    {\n        $value = $model->$attribute;\n        if (!$this->skipOnArray || !is_array($value)) {\n            $model->$attribute = call_user_func($this->filter, $value);\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function clientValidateAttribute($model, $attribute, $view)\n    {\n        if ($this->filter !== 'trim') {\n            return null;\n        }\n\n        ValidationAsset::register($view);\n        $options = $this->getClientOptions($model, $attribute);\n\n        return 'value = yii.validation.trim($form, attribute, ' . Json::htmlEncode($options) . ', value);';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getClientOptions($model, $attribute)\n    {\n        $options = [];\n        if ($this->skipOnEmpty) {\n            $options['skipOnEmpty'] = 1;\n        }\n\n        return $options;\n    }\n}\n"
  },
  {
    "path": "framework/validators/ImageValidator.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\validators;\n\nuse Yii;\nuse yii\\helpers\\Json;\nuse yii\\web\\UploadedFile;\n\n/**\n * ImageValidator verifies if an attribute is receiving a valid image.\n *\n * @author Taras Gudz <gudz.taras@gmail.com>\n * @since 2.0\n */\nclass ImageValidator extends FileValidator\n{\n    /**\n     * @var string the error message used when the uploaded file is not an image.\n     * You may use the following tokens in the message:\n     *\n     * - {attribute}: the attribute name\n     * - {file}: the uploaded file name\n     */\n    public $notImage;\n    /**\n     * @var int|null the minimum width in pixels.\n     * Defaults to null, meaning no limit.\n     * @see underWidth for the customized message used when image width is too small.\n     */\n    public $minWidth;\n    /**\n     * @var int|null the maximum width in pixels.\n     * Defaults to null, meaning no limit.\n     * @see overWidth for the customized message used when image width is too big.\n     */\n    public $maxWidth;\n    /**\n     * @var int|null the minimum height in pixels.\n     * Defaults to null, meaning no limit.\n     * @see underHeight for the customized message used when image height is too small.\n     */\n    public $minHeight;\n    /**\n     * @var int|null the maximum width in pixels.\n     * Defaults to null, meaning no limit.\n     * @see overHeight for the customized message used when image height is too big.\n     */\n    public $maxHeight;\n    /**\n     * @var string the error message used when the image is under [[minWidth]].\n     * You may use the following tokens in the message:\n     *\n     * - {attribute}: the attribute name\n     * - {file}: the uploaded file name\n     * - {limit}: the value of [[minWidth]]\n     */\n    public $underWidth;\n    /**\n     * @var string the error message used when the image is over [[maxWidth]].\n     * You may use the following tokens in the message:\n     *\n     * - {attribute}: the attribute name\n     * - {file}: the uploaded file name\n     * - {limit}: the value of [[maxWidth]]\n     */\n    public $overWidth;\n    /**\n     * @var string the error message used when the image is under [[minHeight]].\n     * You may use the following tokens in the message:\n     *\n     * - {attribute}: the attribute name\n     * - {file}: the uploaded file name\n     * - {limit}: the value of [[minHeight]]\n     */\n    public $underHeight;\n    /**\n     * @var string the error message used when the image is over [[maxHeight]].\n     * You may use the following tokens in the message:\n     *\n     * - {attribute}: the attribute name\n     * - {file}: the uploaded file name\n     * - {limit}: the value of [[maxHeight]]\n     */\n    public $overHeight;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n\n        if ($this->notImage === null) {\n            $this->notImage = Yii::t('yii', 'The file \"{file}\" is not an image.');\n        }\n        if ($this->underWidth === null) {\n            $this->underWidth = Yii::t('yii', 'The image \"{file}\" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.');\n        }\n        if ($this->underHeight === null) {\n            $this->underHeight = Yii::t('yii', 'The image \"{file}\" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.');\n        }\n        if ($this->overWidth === null) {\n            $this->overWidth = Yii::t('yii', 'The image \"{file}\" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.');\n        }\n        if ($this->overHeight === null) {\n            $this->overHeight = Yii::t('yii', 'The image \"{file}\" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.');\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function validateValue($value)\n    {\n        $result = parent::validateValue($value);\n\n        return empty($result) ? $this->validateImage($value) : $result;\n    }\n\n    /**\n     * Validates an image file.\n     * @param UploadedFile $image uploaded file passed to check against a set of rules\n     * @return array|null the error message and the parameters to be inserted into the error message.\n     * Null should be returned if the data is valid.\n     */\n    protected function validateImage($image)\n    {\n        if (false === ($imageInfo = getimagesize($image->tempName))) {\n            return [$this->notImage, ['file' => $image->name]];\n        }\n\n        list($width, $height) = $imageInfo;\n\n        if ($width == 0 || $height == 0) {\n            return [$this->notImage, ['file' => $image->name]];\n        }\n\n        if ($this->minWidth !== null && $width < $this->minWidth) {\n            return [$this->underWidth, ['file' => $image->name, 'limit' => $this->minWidth]];\n        }\n\n        if ($this->minHeight !== null && $height < $this->minHeight) {\n            return [$this->underHeight, ['file' => $image->name, 'limit' => $this->minHeight]];\n        }\n\n        if ($this->maxWidth !== null && $width > $this->maxWidth) {\n            return [$this->overWidth, ['file' => $image->name, 'limit' => $this->maxWidth]];\n        }\n\n        if ($this->maxHeight !== null && $height > $this->maxHeight) {\n            return [$this->overHeight, ['file' => $image->name, 'limit' => $this->maxHeight]];\n        }\n\n        return null;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function clientValidateAttribute($model, $attribute, $view)\n    {\n        ValidationAsset::register($view);\n        $options = $this->getClientOptions($model, $attribute);\n        return 'yii.validation.image(attribute, messages, ' . Json::htmlEncode($options) . ', deferred);';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getClientOptions($model, $attribute)\n    {\n        $options = parent::getClientOptions($model, $attribute);\n\n        $label = $model->getAttributeLabel($attribute);\n\n        if ($this->notImage !== null) {\n            $options['notImage'] = $this->formatMessage($this->notImage, [\n                'attribute' => $label,\n            ]);\n        }\n\n        if ($this->minWidth !== null) {\n            $options['minWidth'] = $this->minWidth;\n            $options['underWidth'] = $this->formatMessage($this->underWidth, [\n                'attribute' => $label,\n                'limit' => $this->minWidth,\n            ]);\n        }\n\n        if ($this->maxWidth !== null) {\n            $options['maxWidth'] = $this->maxWidth;\n            $options['overWidth'] = $this->formatMessage($this->overWidth, [\n                'attribute' => $label,\n                'limit' => $this->maxWidth,\n            ]);\n        }\n\n        if ($this->minHeight !== null) {\n            $options['minHeight'] = $this->minHeight;\n            $options['underHeight'] = $this->formatMessage($this->underHeight, [\n                'attribute' => $label,\n                'limit' => $this->minHeight,\n            ]);\n        }\n\n        if ($this->maxHeight !== null) {\n            $options['maxHeight'] = $this->maxHeight;\n            $options['overHeight'] = $this->formatMessage($this->overHeight, [\n                'attribute' => $label,\n                'limit' => $this->maxHeight,\n            ]);\n        }\n\n        return $options;\n    }\n}\n"
  },
  {
    "path": "framework/validators/InlineValidator.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\validators;\n\n/**\n * InlineValidator represents a validator which is defined as a method in the object being validated.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass InlineValidator extends Validator\n{\n    /**\n     * @var string|callable an anonymous function or the name of a model class method that will be\n     * called to perform the actual validation. The signature of the method should be like the following:\n     *\n     * ```\n     * function (string $attribute, mixed $params, InlineValidator $validator, mixed $current): bool {\n     * }\n     * ```\n     *\n     * - `$attribute` is the name of the attribute to be validated\n     * - `$params` contains the value of [[params]] that you specify when declaring the inline validation rule\n     * - `$validator` is a reference to related [[InlineValidator]] object. This parameter is available since version 2.0.11\n     * - `$current` is the attribute value. This parameter is available since version 2.0.36\n     */\n    public $method;\n    /**\n     * @var mixed additional parameters that are passed to the validation method\n     */\n    public $params;\n    /**\n     * @var string|\\Closure an anonymous function or the name of a model class method that returns the client validation code.\n     * The signature of the method should be like the following:\n     *\n     * ```\n     * function (string $attribute, mixed $params, InlineValidator $validator, mixed $current, View $view): string\n     * {\n     *     // $view->registerJs('JS validation function');\n     *     // or \\app\\assets\\ValidationAsset::register($view);\n     *     return \"calling JS validation function\";\n     * }\n     * ```\n     *\n     * Please refer to [[clientValidateAttribute()]] and [guide](guide:input-validation#client-side-validation) for details on how\n     * to return client validation code.\n     */\n    public $clientValidate;\n    /**\n     * @var mixed the value of attribute being currently validated.\n     * @since 2.0.36\n     */\n    public $current;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function validateAttribute($model, $attribute)\n    {\n        $method = $this->method;\n        if (is_string($method)) {\n            $method = [$model, $method];\n        } elseif ($method instanceof \\Closure) {\n            $method = $method->bindTo($model);\n        }\n\n        $current = $this->current;\n        if ($current === null) {\n            $current = $model->$attribute;\n        }\n        $method($attribute, $this->params, $this, $current);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function clientValidateAttribute($model, $attribute, $view)\n    {\n        if ($this->clientValidate !== null) {\n            $method = $this->clientValidate;\n            if (is_string($method)) {\n                $method = [$model, $method];\n            } elseif ($method instanceof \\Closure) {\n                $method = $method->bindTo($model);\n            }\n            $current = $this->current;\n            if ($current === null) {\n                $current = $model->$attribute;\n            }\n            return $method($attribute, $this->params, $this, $current, $view);\n        }\n\n        return null;\n    }\n}\n"
  },
  {
    "path": "framework/validators/IpValidator.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\validators;\n\nuse Yii;\nuse yii\\base\\InvalidConfigException;\nuse yii\\helpers\\Html;\nuse yii\\helpers\\IpHelper;\nuse yii\\helpers\\Json;\nuse yii\\web\\JsExpression;\n\n/**\n * The validator checks if the attribute value is a valid IPv4/IPv6 address or subnet.\n *\n * It also may change attribute's value if normalization of IPv6 expansion is enabled.\n *\n * The following are examples of validation rules using this validator:\n *\n * ```\n * ['ip_address', 'ip'], // IPv4 or IPv6 address\n * ['ip_address', 'ip', 'ipv6' => false], // IPv4 address (IPv6 is disabled)\n * ['ip_address', 'ip', 'subnet' => true], // requires a CIDR prefix (like 10.0.0.1/24) for the IP address\n * ['ip_address', 'ip', 'subnet' => null], // CIDR prefix is optional\n * ['ip_address', 'ip', 'subnet' => null, 'normalize' => true], // CIDR prefix is optional and will be added when missing\n * ['ip_address', 'ip', 'ranges' => ['192.168.0.0/24']], // only IP addresses from the specified subnet are allowed\n * ['ip_address', 'ip', 'ranges' => ['!192.168.0.0/24', 'any']], // any IP is allowed except IP in the specified subnet\n * ['ip_address', 'ip', 'expandIPv6' => true], // expands IPv6 address to a full notation format\n * ```\n *\n * @property array $ranges The IPv4 or IPv6 ranges that are allowed or forbidden. Note that the type of this\n * property differs in getter and setter. See [[getRanges()]] and [[setRanges()]] for details.\n *\n * @author Dmitry Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0.7\n */\nclass IpValidator extends Validator\n{\n    /**\n     * Negation char.\n     *\n     * Used to negate [[ranges]] or [[networks]] or to negate validating value when [[negation]] is set to `true`.\n     * @see negation\n     * @see networks\n     * @see ranges\n     */\n    public const NEGATION_CHAR = '!';\n    /**\n     * @var array The network aliases, that can be used in [[ranges]].\n     *  - key - alias name\n     *  - value - array of strings. String can be an IP range, IP address or another alias. String can be\n     *    negated with [[NEGATION_CHAR]] (independent of `negation` option).\n     *\n     * The following aliases are defined by default:\n     *  - `*`: `any`\n     *  - `any`: `0.0.0.0/0, ::/0`\n     *  - `private`: `10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fd00::/8`\n     *  - `multicast`: `224.0.0.0/4, ff00::/8`\n     *  - `linklocal`: `169.254.0.0/16, fe80::/10`\n     *  - `localhost`: `127.0.0.0/8', ::1`\n     *  - `documentation`: `192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24, 2001:db8::/32`\n     *  - `system`: `multicast, linklocal, localhost, documentation`\n     */\n    public $networks = [\n        '*' => ['any'],\n        'any' => ['0.0.0.0/0', '::/0'],\n        'private' => ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', 'fd00::/8'],\n        'multicast' => ['224.0.0.0/4', 'ff00::/8'],\n        'linklocal' => ['169.254.0.0/16', 'fe80::/10'],\n        'localhost' => ['127.0.0.0/8', '::1'],\n        'documentation' => ['192.0.2.0/24', '198.51.100.0/24', '203.0.113.0/24', '2001:db8::/32'],\n        'system' => ['multicast', 'linklocal', 'localhost', 'documentation'],\n    ];\n    /**\n     * @var bool whether the validating value can be an IPv6 address. Defaults to `true`.\n     */\n    public $ipv6 = true;\n    /**\n     * @var bool whether the validating value can be an IPv4 address. Defaults to `true`.\n     */\n    public $ipv4 = true;\n    /**\n     * @var bool|null whether the address can be an IP with CIDR subnet, like `192.168.10.0/24`.\n     * The following values are possible:\n     *\n     * - `false` - the address must not have a subnet (default).\n     * - `true` - specifying a subnet is required.\n     * - `null` - specifying a subnet is optional.\n     */\n    public $subnet = false;\n    /**\n     * @var bool whether to add the CIDR prefix with the smallest length (32 for IPv4 and 128 for IPv6) to an\n     * address without it. Works only when `subnet` is not `false`. For example:\n     *  - `10.0.1.5` will normalized to `10.0.1.5/32`\n     *  - `2008:db0::1` will be normalized to `2008:db0::1/128`\n     *    Defaults to `false`.\n     * @see subnet\n     */\n    public $normalize = false;\n    /**\n     * @var bool whether address may have a [[NEGATION_CHAR]] character at the beginning.\n     * Defaults to `false`.\n     */\n    public $negation = false;\n    /**\n     * @var bool whether to expand an IPv6 address to the full notation format.\n     * Defaults to `false`.\n     */\n    public $expandIPv6 = false;\n    /**\n     * @var string Regexp-pattern to validate IPv4 address\n     */\n    public $ipv4Pattern = '/^(?:(?:2(?:[0-4]\\d|5[0-5])|[0-1]?\\d?\\d)\\.){3}(?:(?:2([0-4]\\d|5[0-5])|[0-1]?\\d?\\d))$/';\n    /**\n     * @var string Regexp-pattern to validate IPv6 address\n     */\n    public $ipv6Pattern = '/^(([\\da-fA-F]{1,4}:){7}[\\da-fA-F]{1,4}|([\\da-fA-F]{1,4}:){1,7}:|([\\da-fA-F]{1,4}:){1,6}:[\\da-fA-F]{1,4}|([\\da-fA-F]{1,4}:){1,5}(:[\\da-fA-F]{1,4}){1,2}|([\\da-fA-F]{1,4}:){1,4}(:[\\da-fA-F]{1,4}){1,3}|([\\da-fA-F]{1,4}:){1,3}(:[\\da-fA-F]{1,4}){1,4}|([\\da-fA-F]{1,4}:){1,2}(:[\\da-fA-F]{1,4}){1,5}|[\\da-fA-F]{1,4}:((:[\\da-fA-F]{1,4}){1,6})|:((:[\\da-fA-F]{1,4}){1,7}|:)|fe80:(:[\\da-fA-F]{0,4}){0,4}%[\\da-zA-Z]+|::(ffff(:0{1,4})?:)?((25[0-5]|(2[0-4]|1?\\d)?\\d)\\.){3}(25[0-5]|(2[0-4]|1?\\d)?\\d)|([\\da-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1?[\\d])?\\d)\\.){3}(25[0-5]|(2[0-4]|1?\\d)?\\d))$/';\n    /**\n     * @var string user-defined error message is used when validation fails due to the wrong IP address format.\n     *\n     * You may use the following placeholders in the message:\n     *\n     * - `{attribute}`: the label of the attribute being validated\n     * - `{value}`: the value of the attribute being validated\n     */\n    public $message;\n    /**\n     * @var string user-defined error message is used when validation fails due to the disabled IPv6 validation.\n     *\n     * You may use the following placeholders in the message:\n     *\n     * - `{attribute}`: the label of the attribute being validated\n     * - `{value}`: the value of the attribute being validated\n     *\n     * @see ipv6\n     */\n    public $ipv6NotAllowed;\n    /**\n     * @var string user-defined error message is used when validation fails due to the disabled IPv4 validation.\n     *\n     * You may use the following placeholders in the message:\n     *\n     * - `{attribute}`: the label of the attribute being validated\n     * - `{value}`: the value of the attribute being validated\n     *\n     * @see ipv4\n     */\n    public $ipv4NotAllowed;\n    /**\n     * @var string user-defined error message is used when validation fails due to the wrong CIDR.\n     *\n     * You may use the following placeholders in the message:\n     *\n     * - `{attribute}`: the label of the attribute being validated\n     * - `{value}`: the value of the attribute being validated\n     * @see subnet\n     */\n    public $wrongCidr;\n    /**\n     * @var string|null user-defined error message is used when validation fails due to subnet [[subnet]] set to 'only',\n     * but the CIDR prefix is not set.\n     *\n     * You may use the following placeholders in the message:\n     *\n     * - `{attribute}`: the label of the attribute being validated\n     * - `{value}`: the value of the attribute being validated\n     *\n     * @see subnet\n     */\n    public $noSubnet;\n    /**\n     * @var string user-defined error message is used when validation fails\n     * due to [[subnet]] is false, but CIDR prefix is present.\n     *\n     * You may use the following placeholders in the message:\n     *\n     * - `{attribute}`: the label of the attribute being validated\n     * - `{value}`: the value of the attribute being validated\n     *\n     * @see subnet\n     */\n    public $hasSubnet;\n    /**\n     * @var string user-defined error message is used when validation fails due to IP address\n     * is not not allowed by [[ranges]] check.\n     *\n     * You may use the following placeholders in the message:\n     *\n     * - `{attribute}`: the label of the attribute being validated\n     * - `{value}`: the value of the attribute being validated\n     *\n     * @see ranges\n     */\n    public $notInRange;\n\n    /**\n     * @var array\n     */\n    private $_ranges = [];\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n\n        if (!$this->ipv4 && !$this->ipv6) {\n            throw new InvalidConfigException('Both IPv4 and IPv6 checks can not be disabled at the same time');\n        }\n        if ($this->message === null) {\n            $this->message = Yii::t('yii', '{attribute} must be a valid IP address.');\n        }\n        if ($this->ipv6NotAllowed === null) {\n            $this->ipv6NotAllowed = Yii::t('yii', '{attribute} must not be an IPv6 address.');\n        }\n        if ($this->ipv4NotAllowed === null) {\n            $this->ipv4NotAllowed = Yii::t('yii', '{attribute} must not be an IPv4 address.');\n        }\n        if ($this->wrongCidr === null) {\n            $this->wrongCidr = Yii::t('yii', '{attribute} contains wrong subnet mask.');\n        }\n        if ($this->noSubnet === null) {\n            $this->noSubnet = Yii::t('yii', '{attribute} must be an IP address with specified subnet.');\n        }\n        if ($this->hasSubnet === null) {\n            $this->hasSubnet = Yii::t('yii', '{attribute} must not be a subnet.');\n        }\n        if ($this->notInRange === null) {\n            $this->notInRange = Yii::t('yii', '{attribute} is not in the allowed range.');\n        }\n    }\n\n    /**\n     * Set the IPv4 or IPv6 ranges that are allowed or forbidden.\n     *\n     * The following preparation tasks are performed:\n     *\n     * - Recursively substitutes aliases (described in [[networks]]) with their values.\n     * - Removes duplicates\n     *\n     * @param array|string|null $ranges the IPv4 or IPv6 ranges that are allowed or forbidden.\n     *\n     * When the array is empty, or the option not set, all IP addresses are allowed.\n     *\n     * Otherwise, the rules are checked sequentially until the first match is found.\n     * An IP address is forbidden, when it has not matched any of the rules.\n     *\n     * Example:\n     *\n     * ```\n     * [\n     *      'ranges' => [\n     *          '192.168.10.128'\n     *          '!192.168.10.0/24',\n     *          'any' // allows any other IP addresses\n     *      ]\n     * ]\n     * ```\n     *\n     * In this example, access is allowed for all the IPv4 and IPv6 addresses excluding the `192.168.10.0/24` subnet.\n     * IPv4 address `192.168.10.128` is also allowed, because it is listed before the restriction.\n     */\n    public function setRanges($ranges)\n    {\n        $this->_ranges = $this->prepareRanges((array) $ranges);\n    }\n\n    /**\n     * @return array The IPv4 or IPv6 ranges that are allowed or forbidden.\n     */\n    public function getRanges()\n    {\n        return $this->_ranges;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function validateValue($value)\n    {\n        $result = $this->validateSubnet($value);\n        if (is_array($result)) {\n            $result[1] = array_merge(['ip' => is_array($value) ? 'array()' : $value], $result[1]);\n            return $result;\n        }\n\n        return null;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function validateAttribute($model, $attribute)\n    {\n        $value = $model->$attribute;\n\n        $result = $this->validateSubnet($value);\n        if (is_array($result)) {\n            $result[1] = array_merge(['ip' => is_array($value) ? 'array()' : $value], $result[1]);\n            $this->addError($model, $attribute, $result[0], $result[1]);\n        } else {\n            $model->$attribute = $result;\n        }\n    }\n\n    /**\n     * Validates an IPv4/IPv6 address or subnet.\n     *\n     * @param $ip string\n     * @return string|array\n     * string - the validation was successful;\n     * array  - an error occurred during the validation.\n     * Array[0] contains the text of an error, array[1] contains values for the placeholders in the error message\n     */\n    private function validateSubnet($ip)\n    {\n        if (!is_string($ip)) {\n            return [$this->message, []];\n        }\n\n        $negation = null;\n        $cidr = null;\n        $isCidrDefault = false;\n\n        if (preg_match($this->getIpParsePattern(), $ip, $matches)) {\n            $negation = ($matches[1] !== '') ? $matches[1] : null;\n            $ip = $matches[2];\n            $cidr = isset($matches[4]) ? $matches[4] : null;\n        }\n\n        if ($this->subnet === true && $cidr === null) {\n            return [$this->noSubnet, []];\n        }\n        if ($this->subnet === false && $cidr !== null) {\n            return [$this->hasSubnet, []];\n        }\n        if ($this->negation === false && $negation !== null) {\n            return [$this->message, []];\n        }\n\n        if ($this->getIpVersion($ip) === IpHelper::IPV6) {\n            if ($cidr !== null) {\n                if ($cidr > IpHelper::IPV6_ADDRESS_LENGTH || $cidr < 0) {\n                    return [$this->wrongCidr, []];\n                }\n            } else {\n                $isCidrDefault = true;\n                $cidr = IpHelper::IPV6_ADDRESS_LENGTH;\n            }\n\n            if (!$this->validateIPv6($ip)) {\n                return [$this->message, []];\n            }\n            if (!$this->ipv6) {\n                return [$this->ipv6NotAllowed, []];\n            }\n\n            if ($this->expandIPv6) {\n                $ip = $this->expandIPv6($ip);\n            }\n        } else {\n            if ($cidr !== null) {\n                if ($cidr > IpHelper::IPV4_ADDRESS_LENGTH || $cidr < 0) {\n                    return [$this->wrongCidr, []];\n                }\n            } else {\n                $isCidrDefault = true;\n                $cidr = IpHelper::IPV4_ADDRESS_LENGTH;\n            }\n            if (!$this->validateIPv4($ip)) {\n                return [$this->message, []];\n            }\n            if (!$this->ipv4) {\n                return [$this->ipv4NotAllowed, []];\n            }\n        }\n\n        if (!$this->isAllowed($ip, $cidr)) {\n            return [$this->notInRange, []];\n        }\n\n        $result = $negation . $ip;\n\n        if ($this->subnet !== false && (!$isCidrDefault || $isCidrDefault && $this->normalize)) {\n            $result .= \"/$cidr\";\n        }\n\n        return $result;\n    }\n\n    /**\n     * Expands an IPv6 address to it's full notation.\n     *\n     * For example `2001:db8::1` will be expanded to `2001:0db8:0000:0000:0000:0000:0000:0001`.\n     *\n     * @param string $ip the original IPv6\n     * @return string the expanded IPv6\n     */\n    private function expandIPv6($ip)\n    {\n        return IpHelper::expandIPv6($ip);\n    }\n\n    /**\n     * The method checks whether the IP address with specified CIDR is allowed according to the [[ranges]] list.\n     *\n     * @param string $ip\n     * @param int $cidr\n     * @return bool\n     * @see ranges\n     */\n    private function isAllowed($ip, $cidr)\n    {\n        if (empty($this->ranges)) {\n            return true;\n        }\n\n        foreach ($this->ranges as $string) {\n            list($isNegated, $range) = $this->parseNegatedRange($string);\n            if ($this->inRange($ip, $cidr, $range)) {\n                return !$isNegated;\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * Parses IP address/range for the negation with [[NEGATION_CHAR]].\n     *\n     * @param $string\n     * @return array `[0 => bool, 1 => string]`\n     *  - boolean: whether the string is negated\n     *  - string: the string without negation (when the negation were present)\n     */\n    private function parseNegatedRange($string)\n    {\n        $isNegated = strpos($string, static::NEGATION_CHAR) === 0;\n        return [$isNegated, $isNegated ? substr($string, strlen(static::NEGATION_CHAR)) : $string];\n    }\n\n    /**\n     * Prepares array to fill in [[ranges]].\n     *\n     *  - Recursively substitutes aliases, described in [[networks]] with their values,\n     *  - Removes duplicates.\n     *\n     * @param $ranges\n     * @return array\n     * @see networks\n     */\n    private function prepareRanges($ranges)\n    {\n        $result = [];\n        foreach ($ranges as $string) {\n            list($isRangeNegated, $range) = $this->parseNegatedRange($string);\n            if (isset($this->networks[$range])) {\n                $replacements = $this->prepareRanges($this->networks[$range]);\n                foreach ($replacements as &$replacement) {\n                    list($isReplacementNegated, $replacement) = $this->parseNegatedRange($replacement);\n                    $result[] = ($isRangeNegated && !$isReplacementNegated ? static::NEGATION_CHAR : '') . $replacement;\n                }\n            } else {\n                $result[] = $string;\n            }\n        }\n\n        return array_unique($result);\n    }\n\n    /**\n     * Validates IPv4 address.\n     *\n     * @param string $value\n     * @return bool\n     */\n    protected function validateIPv4($value)\n    {\n        return preg_match($this->ipv4Pattern, $value) !== 0;\n    }\n\n    /**\n     * Validates IPv6 address.\n     *\n     * @param string $value\n     * @return bool\n     */\n    protected function validateIPv6($value)\n    {\n        return preg_match($this->ipv6Pattern, $value) !== 0;\n    }\n\n    /**\n     * Gets the IP version.\n     *\n     * @param string $ip\n     * @return int\n     */\n    private function getIpVersion($ip)\n    {\n        return IpHelper::getIpVersion($ip);\n    }\n\n    /**\n     * Used to get the Regexp pattern for initial IP address parsing.\n     * @return string\n     */\n    private function getIpParsePattern()\n    {\n        return '/^(' . preg_quote(static::NEGATION_CHAR, '/') . '?)(.+?)(\\/(\\d+))?$/';\n    }\n\n    /**\n     * Checks whether the IP is in subnet range.\n     *\n     * @param string $ip an IPv4 or IPv6 address\n     * @param int $cidr\n     * @param string $range subnet in CIDR format e.g. `10.0.0.0/8` or `2001:af::/64`\n     * @return bool\n     */\n    private function inRange($ip, $cidr, $range)\n    {\n        return IpHelper::inRange($ip . '/' . $cidr, $range);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function clientValidateAttribute($model, $attribute, $view)\n    {\n        ValidationAsset::register($view);\n        $options = $this->getClientOptions($model, $attribute);\n\n        return 'yii.validation.ip(value, messages, ' . Json::htmlEncode($options) . ');';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getClientOptions($model, $attribute)\n    {\n        $messages = [\n            'ipv6NotAllowed' => $this->ipv6NotAllowed,\n            'ipv4NotAllowed' => $this->ipv4NotAllowed,\n            'message' => $this->message,\n            'noSubnet' => $this->noSubnet,\n            'hasSubnet' => $this->hasSubnet,\n        ];\n        foreach ($messages as &$message) {\n            $message = $this->formatMessage($message, [\n                'attribute' => $model->getAttributeLabel($attribute),\n            ]);\n        }\n\n        $options = [\n            'ipv4Pattern' => new JsExpression(Html::escapeJsRegularExpression($this->ipv4Pattern)),\n            'ipv6Pattern' => new JsExpression(Html::escapeJsRegularExpression($this->ipv6Pattern)),\n            'messages' => $messages,\n            'ipv4' => (bool) $this->ipv4,\n            'ipv6' => (bool) $this->ipv6,\n            'ipParsePattern' => new JsExpression(Html::escapeJsRegularExpression($this->getIpParsePattern())),\n            'negation' => $this->negation,\n            'subnet' => $this->subnet,\n        ];\n        if ($this->skipOnEmpty) {\n            $options['skipOnEmpty'] = 1;\n        }\n\n        return $options;\n    }\n}\n"
  },
  {
    "path": "framework/validators/NumberValidator.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\validators;\n\nuse Yii;\nuse yii\\helpers\\Json;\nuse yii\\helpers\\StringHelper;\nuse yii\\web\\JsExpression;\n\n/**\n * NumberValidator validates that the attribute value is a number.\n *\n * The format of the number must match the regular expression specified in [[integerPattern]] or [[numberPattern]].\n * Optionally, you may configure the [[max]] and [[min]] properties to ensure the number\n * is within certain range.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass NumberValidator extends Validator\n{\n    /**\n     * @var bool whether to allow array type attribute. Defaults to false.\n     * @since 2.0.42\n     */\n    public $allowArray = false;\n    /**\n     * @var bool whether the attribute value can only be an integer. Defaults to false.\n     */\n    public $integerOnly = false;\n    /**\n     * @var int|float|null upper limit of the number. Defaults to null, meaning no upper limit.\n     * @see tooBig for the customized message used when the number is too big.\n     */\n    public $max;\n    /**\n     * @var int|float|null lower limit of the number. Defaults to null, meaning no lower limit.\n     * @see tooSmall for the customized message used when the number is too small.\n     */\n    public $min;\n    /**\n     * @var string user-defined error message used when the value is bigger than [[max]].\n     */\n    public $tooBig;\n    /**\n     * @var string user-defined error message used when the value is smaller than [[min]].\n     */\n    public $tooSmall;\n    /**\n     * @var string the regular expression for matching integers.\n     */\n    public $integerPattern = '/^[+-]?\\d+$/';\n    /**\n     * @var string the regular expression for matching numbers. It defaults to a pattern\n     * that matches floating numbers with optional exponential part (e.g. -1.23e-10).\n     */\n    public $numberPattern = '/^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$/';\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        if ($this->message === null) {\n            $this->message = $this->integerOnly ? Yii::t('yii', '{attribute} must be an integer.')\n                : Yii::t('yii', '{attribute} must be a number.');\n        }\n        if ($this->min !== null && $this->tooSmall === null) {\n            $this->tooSmall = Yii::t('yii', '{attribute} must be no less than {min}.');\n        }\n        if ($this->max !== null && $this->tooBig === null) {\n            $this->tooBig = Yii::t('yii', '{attribute} must be no greater than {max}.');\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function validateAttribute($model, $attribute)\n    {\n        $value = $model->$attribute;\n        if (is_array($value) && !$this->allowArray) {\n            $this->addError($model, $attribute, $this->message);\n            return;\n        }\n        $values = !is_array($value) ? [$value] : $value;\n        foreach ($values as $value) {\n            if ($this->isNotNumber($value)) {\n                $this->addError($model, $attribute, $this->message);\n                return;\n            }\n            $pattern = $this->integerOnly ? $this->integerPattern : $this->numberPattern;\n\n            if (!preg_match($pattern, StringHelper::normalizeNumber($value))) {\n                $this->addError($model, $attribute, $this->message);\n            }\n            if ($this->min !== null && $value < $this->min) {\n                $this->addError($model, $attribute, $this->tooSmall, ['min' => $this->min]);\n            }\n            if ($this->max !== null && $value > $this->max) {\n                $this->addError($model, $attribute, $this->tooBig, ['max' => $this->max]);\n            }\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function validateValue($value)\n    {\n        if (is_array($value) && !$this->allowArray) {\n            return [$this->message, []];\n        }\n        $values = !is_array($value) ? [$value] : $value;\n        foreach ($values as $sample) {\n            if ($this->isNotNumber($sample)) {\n                return [$this->message, []];\n            }\n            $pattern = $this->integerOnly ? $this->integerPattern : $this->numberPattern;\n            if (!preg_match($pattern, StringHelper::normalizeNumber($sample))) {\n                return [$this->message, []];\n            } elseif ($this->min !== null && $sample < $this->min) {\n                return [$this->tooSmall, ['min' => $this->min]];\n            } elseif ($this->max !== null && $sample > $this->max) {\n                return [$this->tooBig, ['max' => $this->max]];\n            }\n        }\n\n        return null;\n    }\n\n    /**\n     * @param mixed $value the data value to be checked.\n     */\n    private function isNotNumber($value)\n    {\n        return is_array($value)\n            || is_bool($value)\n            || (is_object($value) && !method_exists($value, '__toString'))\n            || (!is_object($value) && !is_scalar($value) && $value !== null);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function clientValidateAttribute($model, $attribute, $view)\n    {\n        ValidationAsset::register($view);\n        $options = $this->getClientOptions($model, $attribute);\n\n        return 'yii.validation.number(value, messages, ' . Json::htmlEncode($options) . ');';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getClientOptions($model, $attribute)\n    {\n        $label = $model->getAttributeLabel($attribute);\n\n        $options = [\n            'pattern' => new JsExpression($this->integerOnly ? $this->integerPattern : $this->numberPattern),\n            'message' => $this->formatMessage($this->message, [\n                'attribute' => $label,\n            ]),\n        ];\n\n        if ($this->min !== null) {\n            // ensure numeric value to make javascript comparison equal to PHP comparison\n            // https://github.com/yiisoft/yii2/issues/3118\n            $options['min'] = is_string($this->min) ? (float) $this->min : $this->min;\n            $options['tooSmall'] = $this->formatMessage($this->tooSmall, [\n                'attribute' => $label,\n                'min' => $this->min,\n            ]);\n        }\n        if ($this->max !== null) {\n            // ensure numeric value to make javascript comparison equal to PHP comparison\n            // https://github.com/yiisoft/yii2/issues/3118\n            $options['max'] = is_string($this->max) ? (float) $this->max : $this->max;\n            $options['tooBig'] = $this->formatMessage($this->tooBig, [\n                'attribute' => $label,\n                'max' => $this->max,\n            ]);\n        }\n        if ($this->skipOnEmpty) {\n            $options['skipOnEmpty'] = 1;\n        }\n\n        return $options;\n    }\n}\n"
  },
  {
    "path": "framework/validators/PunycodeAsset.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\validators;\n\nuse yii\\web\\AssetBundle;\n\n/**\n * This asset bundle provides the javascript files needed for the [[EmailValidator]]s client validation.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass PunycodeAsset extends AssetBundle\n{\n    public $sourcePath = '@bower/punycode';\n    public $js = [\n        'punycode.js',\n    ];\n}\n"
  },
  {
    "path": "framework/validators/RangeValidator.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\validators;\n\nuse Yii;\nuse yii\\base\\InvalidConfigException;\nuse yii\\helpers\\ArrayHelper;\nuse yii\\helpers\\Json;\n\n/**\n * RangeValidator validates that the attribute value is among a list of values.\n *\n * The range can be specified via the [[range]] property.\n * If the [[not]] property is set true, the validator will ensure the attribute value\n * is NOT among the specified range.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass RangeValidator extends Validator\n{\n    /**\n     * @var array|\\Traversable|\\Closure a list of valid values that the attribute value should be among or an anonymous function that returns\n     * such a list. The signature of the anonymous function should be as follows,\n     *\n     * ```\n     * function($model, $attribute) {\n     *     // compute range\n     *     return $range;\n     * }\n     * ```\n     */\n    public $range;\n    /**\n     * @var bool whether the comparison is strict (both type and value must be the same)\n     */\n    public $strict = false;\n    /**\n     * @var bool whether to invert the validation logic. Defaults to false. If set to true,\n     * the attribute value should NOT be among the list of values defined via [[range]].\n     */\n    public $not = false;\n    /**\n     * @var bool whether to allow array type attribute.\n     */\n    public $allowArray = false;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        if (\n            !is_array($this->range)\n            && !($this->range instanceof \\Closure)\n            && !($this->range instanceof \\Traversable)\n        ) {\n            throw new InvalidConfigException('The \"range\" property must be set.');\n        }\n        if ($this->message === null) {\n            $this->message = Yii::t('yii', '{attribute} is invalid.');\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function validateValue($value)\n    {\n        $in = false;\n\n        if (\n            $this->allowArray\n            && ($value instanceof \\Traversable || is_array($value))\n            && ArrayHelper::isSubset($value, $this->range, $this->strict)\n        ) {\n            $in = true;\n        }\n\n        if (!$in && ArrayHelper::isIn($value, $this->range, $this->strict)) {\n            $in = true;\n        }\n\n        return $this->not !== $in ? null : [$this->message, []];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function validateAttribute($model, $attribute)\n    {\n        if ($this->range instanceof \\Closure) {\n            $this->range = call_user_func($this->range, $model, $attribute);\n        }\n        parent::validateAttribute($model, $attribute);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function clientValidateAttribute($model, $attribute, $view)\n    {\n        if ($this->range instanceof \\Closure) {\n            $this->range = call_user_func($this->range, $model, $attribute);\n        }\n\n        ValidationAsset::register($view);\n        $options = $this->getClientOptions($model, $attribute);\n\n        return 'yii.validation.range(value, messages, ' . Json::htmlEncode($options) . ');';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getClientOptions($model, $attribute)\n    {\n        $range = [];\n        foreach ($this->range as $value) {\n            $range[] = (string) $value;\n        }\n        $options = [\n            'range' => $range,\n            'not' => $this->not,\n            'message' => $this->formatMessage($this->message, [\n                'attribute' => $model->getAttributeLabel($attribute),\n            ]),\n        ];\n        if ($this->skipOnEmpty) {\n            $options['skipOnEmpty'] = 1;\n        }\n        if ($this->allowArray) {\n            $options['allowArray'] = 1;\n        }\n\n        return $options;\n    }\n}\n"
  },
  {
    "path": "framework/validators/RegularExpressionValidator.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\validators;\n\nuse Yii;\nuse yii\\base\\InvalidConfigException;\nuse yii\\helpers\\Html;\nuse yii\\helpers\\Json;\nuse yii\\web\\JsExpression;\n\n/**\n * RegularExpressionValidator validates that the attribute value matches the specified [[pattern]].\n *\n * If the [[not]] property is set true, the validator will ensure the attribute value do NOT match the [[pattern]].\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass RegularExpressionValidator extends Validator\n{\n    /**\n     * @var string the regular expression to be matched with\n     */\n    public $pattern;\n    /**\n     * @var bool whether to invert the validation logic. Defaults to false. If set to true,\n     * the regular expression defined via [[pattern]] should NOT match the attribute value.\n     */\n    public $not = false;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        if ($this->pattern === null) {\n            throw new InvalidConfigException('The \"pattern\" property must be set.');\n        }\n        if ($this->message === null) {\n            $this->message = Yii::t('yii', '{attribute} is invalid.');\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function validateValue($value)\n    {\n        $valid = !is_array($value) &&\n            (!$this->not && preg_match($this->pattern, $value)\n            || $this->not && !preg_match($this->pattern, $value));\n\n        return $valid ? null : [$this->message, []];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function clientValidateAttribute($model, $attribute, $view)\n    {\n        ValidationAsset::register($view);\n        $options = $this->getClientOptions($model, $attribute);\n\n        return 'yii.validation.regularExpression(value, messages, ' . Json::htmlEncode($options) . ');';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getClientOptions($model, $attribute)\n    {\n        $pattern = Html::escapeJsRegularExpression($this->pattern);\n\n        $options = [\n            'pattern' => new JsExpression($pattern),\n            'not' => $this->not,\n            'message' => $this->formatMessage($this->message, [\n                'attribute' => $model->getAttributeLabel($attribute),\n            ]),\n        ];\n        if ($this->skipOnEmpty) {\n            $options['skipOnEmpty'] = 1;\n        }\n\n        return $options;\n    }\n}\n"
  },
  {
    "path": "framework/validators/RequiredValidator.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\validators;\n\nuse Yii;\nuse yii\\helpers\\Json;\n\n/**\n * RequiredValidator validates that the specified attribute does not have null or empty value.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass RequiredValidator extends Validator\n{\n    /**\n     * @var bool whether to skip this validator if the value being validated is empty.\n     */\n    public $skipOnEmpty = false;\n    /**\n     * @var mixed the desired value that the attribute must have.\n     * If this is null, the validator will validate that the specified attribute is not empty.\n     * If this is set as a value that is not null, the validator will validate that\n     * the attribute has a value that is the same as this property value.\n     * Defaults to null.\n     * @see strict\n     */\n    public $requiredValue;\n    /**\n     * @var bool whether the comparison between the attribute value and [[requiredValue]] is strict.\n     * When this is true, both the values and types must match.\n     * Defaults to false, meaning only the values need to match.\n     *\n     * Note that behavior for when [[requiredValue]] is null is the following:\n     *\n     * - In strict mode, the validator will check if the attribute value is null\n     * - In non-strict mode validation will fail\n     */\n    public $strict = false;\n    /**\n     * @var string the user-defined error message. It may contain the following placeholders which\n     * will be replaced accordingly by the validator:\n     *\n     * - `{attribute}`: the label of the attribute being validated\n     * - `{value}`: the value of the attribute being validated\n     * - `{requiredValue}`: the value of [[requiredValue]]\n     */\n    public $message;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        if ($this->message === null) {\n            $this->message = $this->requiredValue === null ? Yii::t('yii', '{attribute} cannot be blank.')\n                : Yii::t('yii', '{attribute} must be \"{requiredValue}\".');\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function validateValue($value)\n    {\n        if ($this->requiredValue === null) {\n            if ($this->strict && $value !== null || !$this->strict && !$this->isEmpty(is_string($value) ? trim($value) : $value)) {\n                return null;\n            }\n        } elseif (!$this->strict && $value == $this->requiredValue || $this->strict && $value === $this->requiredValue) {\n            return null;\n        }\n        if ($this->requiredValue === null) {\n            return [$this->message, []];\n        }\n\n        return [$this->message, [\n            'requiredValue' => $this->requiredValue,\n        ]];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function clientValidateAttribute($model, $attribute, $view)\n    {\n        ValidationAsset::register($view);\n        $options = $this->getClientOptions($model, $attribute);\n\n        return 'yii.validation.required(value, messages, ' . Json::htmlEncode($options) . ');';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getClientOptions($model, $attribute)\n    {\n        $options = [];\n        if ($this->requiredValue !== null) {\n            $options['message'] = $this->formatMessage($this->message, [\n                'requiredValue' => $this->requiredValue,\n            ]);\n            $options['requiredValue'] = $this->requiredValue;\n        } else {\n            $options['message'] = $this->message;\n        }\n        if ($this->strict) {\n            $options['strict'] = 1;\n        }\n\n        $options['message'] = $this->formatMessage($options['message'], [\n            'attribute' => $model->getAttributeLabel($attribute),\n        ]);\n\n        return $options;\n    }\n}\n"
  },
  {
    "path": "framework/validators/SafeValidator.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\validators;\n\n/**\n * SafeValidator serves as a dummy validator whose main purpose is to mark the attributes to be safe for massive assignment.\n *\n * This class is required because of the way in which Yii determines whether a property is safe for massive assignment, that is,\n * when a user submits form data to be loaded into a model directly from the POST data, is it ok for a property to be copied.\n * In many cases, this is required but because sometimes properties are internal and you do not want the POST data to be able to\n * override these internal values (especially things like database row ids), Yii assumes all values are unsafe for massive assignment\n * unless a validation rule exists for the property, which in most cases it will. Sometimes, however, an item is safe for massive assignment but\n * does not have a validation rule associated with it - for instance, due to no validation being performed, in which case, you use this class\n * as a validation rule for that property. Although it has no functionality, it allows Yii to determine that the property is safe to copy.\n *\n * > Note: [[when]] property is not supported by SafeValidator.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass SafeValidator extends Validator\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function validateAttributes($model, $attributes = null)\n    {\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function validateAttribute($model, $attribute)\n    {\n    }\n}\n"
  },
  {
    "path": "framework/validators/StringValidator.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\validators;\n\nuse Yii;\nuse yii\\helpers\\Json;\n\n/**\n * StringValidator validates that the attribute value is of certain length.\n *\n * Note, this validator should only be used with string-typed attributes.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass StringValidator extends Validator\n{\n    /**\n     * @var int|array|null specifies the length limit of the value to be validated.\n     * This can be specified in one of the following forms:\n     *\n     * - an integer: the exact length that the value should be of;\n     * - an array of one element: the minimum length that the value should be of. For example, `[8]`.\n     *   This will overwrite [[min]].\n     * - an array of two elements: the minimum and maximum lengths that the value should be of.\n     *   For example, `[8, 128]`. This will overwrite both [[min]] and [[max]].\n     * @see tooShort for the customized message for a too short string.\n     * @see tooLong for the customized message for a too long string.\n     * @see notEqual for the customized message for a string that does not match desired length.\n     */\n    public $length;\n    /**\n     * @var int|null maximum length. If not set, it means no maximum length limit.\n     * @see tooLong for the customized message for a too long string.\n     */\n    public $max;\n    /**\n     * @var int|null minimum length. If not set, it means no minimum length limit.\n     * @see tooShort for the customized message for a too short string.\n     */\n    public $min;\n    /**\n     * @var string user-defined error message used when the value is not a string.\n     */\n    public $message;\n    /**\n     * @var string user-defined error message used when the length of the value is smaller than [[min]].\n     */\n    public $tooShort;\n    /**\n     * @var string user-defined error message used when the length of the value is greater than [[max]].\n     */\n    public $tooLong;\n    /**\n     * @var string user-defined error message used when the length of the value is not equal to [[length]].\n     */\n    public $notEqual;\n    /**\n     * @var string|null the encoding of the string value to be validated (e.g. 'UTF-8').\n     * If this property is not set, [[\\yii\\base\\Application::charset]] will be used.\n     */\n    public $encoding;\n    /**\n     * @var boolean whether to require the value to be a string data type.\n     * If false any scalar value will be treated as it's string equivalent.\n     * @since 2.0.33\n     */\n    public $strict = true;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        if (is_array($this->length)) {\n            if (isset($this->length[0])) {\n                $this->min = $this->length[0];\n            }\n            if (isset($this->length[1])) {\n                $this->max = $this->length[1];\n            }\n            $this->length = null;\n        }\n        if ($this->encoding === null) {\n            $this->encoding = Yii::$app ? Yii::$app->charset : 'UTF-8';\n        }\n        if ($this->message === null) {\n            $this->message = Yii::t('yii', '{attribute} must be a string.');\n        }\n        if ($this->min !== null && $this->tooShort === null) {\n            $this->tooShort = Yii::t('yii', '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.');\n        }\n        if ($this->max !== null && $this->tooLong === null) {\n            $this->tooLong = Yii::t('yii', '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.');\n        }\n        if ($this->length !== null && $this->notEqual === null) {\n            $this->notEqual = Yii::t('yii', '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.');\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function validateAttribute($model, $attribute)\n    {\n        $value = $model->$attribute;\n        if (!$this->strict && is_scalar($value) && !is_string($value)) {\n            $value = (string)$value;\n        }\n        if (!is_string($value)) {\n            $this->addError($model, $attribute, $this->message);\n\n            return;\n        }\n\n        $length = mb_strlen($value, $this->encoding);\n\n        if ($this->min !== null && $length < $this->min) {\n            $this->addError($model, $attribute, $this->tooShort, ['min' => $this->min]);\n        }\n        if ($this->max !== null && $length > $this->max) {\n            $this->addError($model, $attribute, $this->tooLong, ['max' => $this->max]);\n        }\n        if ($this->length !== null && $length !== $this->length) {\n            $this->addError($model, $attribute, $this->notEqual, ['length' => $this->length]);\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function validateValue($value)\n    {\n        if (!$this->strict && is_scalar($value) && !is_string($value)) {\n            $value = (string)$value;\n        }\n\n        if (!is_string($value)) {\n            return [$this->message, []];\n        }\n\n        $length = mb_strlen($value, $this->encoding);\n\n        if ($this->min !== null && $length < $this->min) {\n            return [$this->tooShort, ['min' => $this->min]];\n        }\n        if ($this->max !== null && $length > $this->max) {\n            return [$this->tooLong, ['max' => $this->max]];\n        }\n        if ($this->length !== null && $length !== $this->length) {\n            return [$this->notEqual, ['length' => $this->length]];\n        }\n\n        return null;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function clientValidateAttribute($model, $attribute, $view)\n    {\n        ValidationAsset::register($view);\n        $options = $this->getClientOptions($model, $attribute);\n\n        return 'yii.validation.string(value, messages, ' . Json::htmlEncode($options) . ');';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getClientOptions($model, $attribute)\n    {\n        $label = $model->getAttributeLabel($attribute);\n\n        $options = [\n            'message' => $this->formatMessage($this->message, [\n                'attribute' => $label,\n            ]),\n        ];\n\n        if ($this->min !== null) {\n            $options['min'] = $this->min;\n            $options['tooShort'] = $this->formatMessage($this->tooShort, [\n                'attribute' => $label,\n                'min' => $this->min,\n            ]);\n        }\n        if ($this->max !== null) {\n            $options['max'] = $this->max;\n            $options['tooLong'] = $this->formatMessage($this->tooLong, [\n                'attribute' => $label,\n                'max' => $this->max,\n            ]);\n        }\n        if ($this->length !== null) {\n            $options['is'] = $this->length;\n            $options['notEqual'] = $this->formatMessage($this->notEqual, [\n                'attribute' => $label,\n                'length' => $this->length,\n            ]);\n        }\n        if ($this->skipOnEmpty) {\n            $options['skipOnEmpty'] = 1;\n        }\n\n        return $options;\n    }\n}\n"
  },
  {
    "path": "framework/validators/TrimValidator.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\validators;\n\nuse yii\\helpers\\Json;\n\n/**\n * This class converts the attribute value(s) to string(s) and strip characters.\n *\n * @since 2.0.46\n */\nclass TrimValidator extends Validator\n{\n    /**\n     * @var string The list of characters to strip, with `..` can specify a range of characters.\n     * For example, set '\\/ ' to normalize path or namespace.\n     */\n    public $chars;\n    /**\n     * @var bool Whether the filter should be skipped if an array input is given.\n     * If true and an array input is given, the filter will not be applied.\n     */\n    public $skipOnArray = false;\n    /**\n     * @inheritDoc\n     */\n    public $skipOnEmpty = false;\n\n\n    /**\n     * @inheritDoc\n     */\n    public function validateAttribute($model, $attribute)\n    {\n        $value = $model->$attribute;\n        if (!$this->skipOnArray || !is_array($value)) {\n            $model->$attribute = is_array($value)\n                ? array_map([$this, 'trimValue'], $value)\n                : $this->trimValue($value);\n        }\n    }\n\n    /**\n     * Converts given value to string and strips declared characters.\n     *\n     * @param mixed $value the value to strip\n     * @return string\n     */\n    protected function trimValue($value)\n    {\n        return $this->isEmpty($value) ? '' : trim((string) $value, $this->chars ?: \" \\n\\r\\t\\v\\x00\");\n    }\n\n    /**\n     * @inheritDoc\n     */\n    public function clientValidateAttribute($model, $attribute, $view)\n    {\n        if ($this->skipOnArray && is_array($model->$attribute)) {\n            return null;\n        }\n\n        ValidationAsset::register($view);\n        $options = $this->getClientOptions($model, $attribute);\n\n        return 'value = yii.validation.trim($form, attribute, ' . Json::htmlEncode($options) . ', value);';\n    }\n\n    /**\n     * @inheritDoc\n     */\n    public function getClientOptions($model, $attribute)\n    {\n        return [\n            'skipOnArray' => (bool) $this->skipOnArray,\n            'skipOnEmpty' => (bool) $this->skipOnEmpty,\n            'chars' => $this->chars ?: false,\n        ];\n    }\n}\n"
  },
  {
    "path": "framework/validators/UniqueValidator.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\validators;\n\nuse Yii;\nuse yii\\base\\Model;\nuse yii\\db\\ActiveQuery;\nuse yii\\db\\ActiveQueryInterface;\nuse yii\\db\\ActiveRecord;\nuse yii\\db\\ActiveRecordInterface;\nuse yii\\helpers\\Inflector;\n\n/**\n * UniqueValidator validates that the attribute value is unique in the specified database table.\n *\n * UniqueValidator checks if the value being validated is unique in the table column specified by\n * the ActiveRecord class [[targetClass]] and the attribute [[targetAttribute]].\n *\n * The following are examples of validation rules using this validator:\n *\n * ```\n * // a1 needs to be unique\n * ['a1', 'unique']\n * // a1 needs to be unique, but column a2 will be used to check the uniqueness of the a1 value\n * ['a1', 'unique', 'targetAttribute' => 'a2']\n * // a1 and a2 need to be unique together, and they both will receive error message\n * [['a1', 'a2'], 'unique', 'targetAttribute' => ['a1', 'a2']]\n * // a1 and a2 need to be unique together, only a1 will receive error message\n * ['a1', 'unique', 'targetAttribute' => ['a1', 'a2']]\n * // a1 needs to be unique by checking the uniqueness of both a2 and a3 (using a1 value)\n * ['a1', 'unique', 'targetAttribute' => ['a2', 'a1' => 'a3']]\n * ```\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass UniqueValidator extends Validator\n{\n    /**\n     * @var string|null the name of the ActiveRecord class that should be used to validate the uniqueness\n     * of the current attribute value. If not set, it will use the ActiveRecord class of the attribute being validated.\n     * @see targetAttribute\n     */\n    public $targetClass;\n    /**\n     * @var string|array|null the name of the [[\\yii\\db\\ActiveRecord|ActiveRecord]] attribute that should be used to\n     * validate the uniqueness of the current attribute value. If not set, it will use the name\n     * of the attribute currently being validated. You may use an array to validate the uniqueness\n     * of multiple columns at the same time. The array values are the attributes that will be\n     * used to validate the uniqueness, while the array keys are the attributes whose values are to be validated.\n     */\n    public $targetAttribute;\n    /**\n     * @var string|array|\\Closure additional filter to be applied to the DB query used to check the uniqueness of the attribute value.\n     * This can be a string or an array representing the additional query condition (refer to [[\\yii\\db\\Query::where()]]\n     * on the format of query condition), or an anonymous function with the signature `function ($query)`, where `$query`\n     * is the [[\\yii\\db\\Query|Query]] object that you can modify in the function.\n     */\n    public $filter;\n    /**\n     * @var string the user-defined error message.\n     *\n     * When validating single attribute, it may contain\n     * the following placeholders which will be replaced accordingly by the validator:\n     *\n     * - `{attribute}`: the label of the attribute being validated\n     * - `{value}`: the value of the attribute being validated\n     *\n     * When validating mutliple attributes, it may contain the following placeholders:\n     *\n     * - `{attributes}`: the labels of the attributes being validated.\n     * - `{values}`: the values of the attributes being validated.\n     */\n    public $message;\n    /**\n     * @var string\n     * @since 2.0.9\n     * @deprecated since version 2.0.10, to be removed in 2.1. Use [[message]] property\n     * to setup custom message for multiple target attributes.\n     */\n    public $comboNotUnique;\n    /**\n     * @var string and|or define how target attributes are related\n     * @since 2.0.11\n     */\n    public $targetAttributeJunction = 'and';\n    /**\n     * @var bool whether this validator is forced to always use master DB\n     * @since 2.0.14\n     */\n    public $forceMasterDb =  true;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        if ($this->message !== null) {\n            return;\n        }\n        if (is_array($this->targetAttribute) && count($this->targetAttribute) > 1) {\n            // fallback for deprecated `comboNotUnique` property - use it as message if is set\n            if ($this->comboNotUnique === null) {\n                $this->message = Yii::t('yii', 'The combination {values} of {attributes} has already been taken.');\n            } else {\n                $this->message = $this->comboNotUnique;\n            }\n        } else {\n            $this->message = Yii::t('yii', '{attribute} \"{value}\" has already been taken.');\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function validateAttribute($model, $attribute)\n    {\n        $targetAttribute = $this->targetAttribute === null ? $attribute : $this->targetAttribute;\n        if ($this->skipOnError) {\n            foreach ((array)$targetAttribute as $k => $v) {\n                if ($model->hasErrors(is_int($k) ? $v : $k)) {\n                    return;\n                }\n            }\n        }\n\n        $rawConditions = $this->prepareConditions($targetAttribute, $model, $attribute);\n        $conditions = [$this->targetAttributeJunction === 'or' ? 'or' : 'and'];\n\n        foreach ($rawConditions as $key => $value) {\n            if (is_array($value)) {\n                $this->addError($model, $attribute, Yii::t('yii', '{attribute} is invalid.'));\n                return;\n            }\n            $conditions[] = [$key => $value];\n        }\n\n        /** @var ActiveRecordInterface $targetClass */\n        $targetClass = $this->getTargetClass($model);\n        $db = $targetClass::getDb();\n\n        $modelExists = false;\n\n        if ($this->forceMasterDb && method_exists($db, 'useMaster')) {\n            $db->useMaster(function () use ($targetClass, $conditions, $model, &$modelExists) {\n                $modelExists = $this->modelExists($targetClass, $conditions, $model);\n            });\n        } else {\n            $modelExists = $this->modelExists($targetClass, $conditions, $model);\n        }\n\n        if ($modelExists) {\n            if (is_array($targetAttribute) && count($targetAttribute) > 1) {\n                $this->addComboNotUniqueError($model, $attribute);\n            } else {\n                $this->addError($model, $attribute, $this->message);\n            }\n        }\n    }\n\n    /**\n     * @param Model $model the data model to be validated\n     * @return string Target class name\n     */\n    private function getTargetClass($model)\n    {\n        return $this->targetClass === null ? get_class($model) : $this->targetClass;\n    }\n\n    /**\n     * Checks whether the $model exists in the database.\n     *\n     * @param string $targetClass the name of the ActiveRecord class that should be used to validate the uniqueness\n     * of the current attribute value.\n     * @param array $conditions conditions, compatible with [[\\yii\\db\\Query::where()|Query::where()]] key-value format.\n     * @param Model $model the data model to be validated\n     *\n     * @return bool whether the model already exists\n     */\n    private function modelExists($targetClass, $conditions, $model)\n    {\n        /** @var ActiveRecordInterface|\\yii\\base\\BaseObject $targetClass $query */\n        $query = $this->prepareQuery($targetClass, $conditions);\n\n        if (!$model instanceof ActiveRecordInterface || $model->getIsNewRecord() || $model::className() !== $targetClass::className()) {\n            // if current $model isn't in the database yet, then it's OK just to call exists()\n            // also there's no need to run check based on primary keys, when $targetClass is not the same as $model's class\n            $exists = $query->exists();\n        } else {\n            // if current $model is in the database already we can't use exists()\n            if ($query instanceof \\yii\\db\\ActiveQuery) {\n                // only select primary key to optimize query\n                $columnsCondition = array_flip($targetClass::primaryKey());\n                $query->select(array_flip($this->applyTableAlias($query, $columnsCondition)));\n\n                // any with relation can't be loaded because related fields are not selected\n                $query->with = null;\n\n                if (is_array($query->joinWith)) {\n                    // any joinWiths need to have eagerLoading turned off to prevent related fields being loaded\n                    foreach ($query->joinWith as &$joinWith) {\n                        // \\yii\\db\\ActiveQuery::joinWith adds eagerLoading at key 1\n                        $joinWith[1] = false;\n                    }\n                    unset($joinWith);\n                }\n            }\n            $models = $query->limit(2)->asArray()->all();\n            $n = count($models);\n            if ($n === 1) {\n                // if there is one record, check if it is the currently validated model\n                $dbModel = reset($models);\n                $pks = $targetClass::primaryKey();\n                $pk = [];\n                foreach ($pks as $pkAttribute) {\n                    $pk[$pkAttribute] = $dbModel[$pkAttribute];\n                }\n                $exists = ($pk != $model->getOldPrimaryKey(true));\n            } else {\n                // if there is more than one record, the value is not unique\n                $exists = $n > 1;\n            }\n        }\n\n        return $exists;\n    }\n\n    /**\n     * Prepares a query by applying filtering conditions defined in $conditions method property\n     * and [[filter]] class property.\n     *\n     * @param ActiveRecordInterface $targetClass the name of the ActiveRecord class that should be used to validate\n     * the uniqueness of the current attribute value.\n     * @param array $conditions conditions, compatible with [[\\yii\\db\\Query::where()|Query::where()]] key-value format\n     * @return ActiveQueryInterface|ActiveQuery<ActiveRecord>\n     */\n    private function prepareQuery($targetClass, $conditions)\n    {\n        $query = $targetClass::find();\n        $query->andWhere($conditions);\n        if ($this->filter instanceof \\Closure) {\n            call_user_func($this->filter, $query);\n        } elseif ($this->filter !== null) {\n            $query->andWhere($this->filter);\n        }\n\n        return $query;\n    }\n\n    /**\n     * Processes attributes' relations described in $targetAttribute parameter into conditions, compatible with\n     * [[\\yii\\db\\Query::where()|Query::where()]] key-value format.\n     *\n     * @param string|array $targetAttribute the name of the [[\\yii\\db\\ActiveRecord|ActiveRecord]] attribute that\n     * should be used to validate the uniqueness of the current attribute value. You may use an array to validate\n     * the uniqueness of multiple columns at the same time. The array values are the attributes that will be\n     * used to validate the uniqueness, while the array keys are the attributes whose values are to be validated.\n     * If the key and the value are the same, you can just specify the value.\n     * @param Model $model the data model to be validated\n     * @param string $attribute the name of the attribute to be validated in the $model\n     *\n     * @return array conditions, compatible with [[\\yii\\db\\Query::where()|Query::where()]] key-value format.\n     */\n    private function prepareConditions($targetAttribute, $model, $attribute)\n    {\n        if (is_array($targetAttribute)) {\n            $conditions = [];\n            foreach ($targetAttribute as $k => $v) {\n                $conditions[$v] = is_int($k) ? $model->$v : $model->$k;\n            }\n        } else {\n            $conditions = [$targetAttribute => $model->$attribute];\n        }\n\n        $targetModelClass = $this->getTargetClass($model);\n        if (!is_subclass_of($targetModelClass, 'yii\\db\\ActiveRecord')) {\n            return $conditions;\n        }\n\n        /** @var ActiveRecord $targetModelClass */\n        return $this->applyTableAlias($targetModelClass::find(), $conditions);\n    }\n\n    /**\n     * Builds and adds [[comboNotUnique]] error message to the specified model attribute.\n     * @param \\yii\\base\\Model $model the data model.\n     * @param string $attribute the name of the attribute.\n     */\n    private function addComboNotUniqueError($model, $attribute)\n    {\n        $attributeCombo = [];\n        $valueCombo = [];\n        foreach ($this->targetAttribute as $key => $value) {\n            if (is_int($key)) {\n                $attributeCombo[] = $model->getAttributeLabel($value);\n                $valueCombo[] = '\"' . $model->$value . '\"';\n            } else {\n                $attributeCombo[] = $model->getAttributeLabel($key);\n                $valueCombo[] = '\"' . $model->$key . '\"';\n            }\n        }\n        $this->addError($model, $attribute, $this->message, [\n            'attributes' => Inflector::sentence($attributeCombo),\n            'values' => implode('-', $valueCombo),\n        ]);\n    }\n\n    /**\n     * Returns conditions with alias.\n     * @param ActiveQuery<ActiveRecord> $query\n     * @param array $conditions array of condition, keys to be modified\n     * @param string|null $alias set empty string for no apply alias. Set null for apply primary table alias\n     * @return array\n     */\n    private function applyTableAlias($query, $conditions, $alias = null)\n    {\n        if ($alias === null) {\n            $alias = array_keys($query->getTablesUsedInFrom())[0];\n        }\n        $prefixedConditions = [];\n        foreach ($conditions as $columnName => $columnValue) {\n            if (strpos($columnName, '(') === false) {\n                $columnName = preg_replace('/^' . preg_quote($alias, '/') . '\\.(.*)$/', '$1', $columnName);\n                if (strncmp($columnName, '[[', 2) === 0) {\n                    $prefixedColumn = \"{$alias}.{$columnName}\";\n                } else {\n                    $prefixedColumn = \"{$alias}.[[{$columnName}]]\";\n                }\n            } else {\n                // there is an expression, can't prefix it reliably\n                $prefixedColumn = $columnName;\n            }\n\n            $prefixedConditions[$prefixedColumn] = $columnValue;\n        }\n\n        return $prefixedConditions;\n    }\n}\n"
  },
  {
    "path": "framework/validators/UrlValidator.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\validators;\n\nuse Yii;\nuse yii\\base\\InvalidConfigException;\nuse yii\\helpers\\Json;\nuse yii\\web\\JsExpression;\n\n/**\n * UrlValidator validates that the attribute value is a valid http or https URL.\n *\n * Note that this validator only checks if the URL scheme and host part are correct.\n * It does not check the remaining parts of a URL.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass UrlValidator extends Validator\n{\n    /**\n     * @var string the regular expression used to validate the attribute value.\n     * The pattern may contain a `{schemes}` token that will be replaced\n     * by a regular expression which represents the [[validSchemes]].\n     */\n    public $pattern = '/^{schemes}:\\/\\/(([A-Z0-9][A-Z0-9_-]*)(\\.[A-Z0-9][A-Z0-9_-]*)+)(?::\\d{1,5})?(?:$|[?\\/#])/i';\n    /**\n     * @var array list of URI schemes which should be considered valid. By default, http and https\n     * are considered to be valid schemes.\n     */\n    public $validSchemes = ['http', 'https'];\n    /**\n     * @var string|null the default URI scheme. If the input doesn't contain the scheme part, the default\n     * scheme will be prepended to it (thus changing the input). Defaults to null, meaning a URL must\n     * contain the scheme part.\n     */\n    public $defaultScheme;\n    /**\n     * @var bool whether validation process should take into account IDN (internationalized\n     * domain names). Defaults to false meaning that validation of URLs containing IDN will always\n     * fail. Note that in order to use IDN validation you have to install and enable `intl` PHP\n     * extension, otherwise an exception would be thrown.\n     */\n    public $enableIDN = false;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        if ($this->enableIDN && !function_exists('idn_to_ascii')) {\n            throw new InvalidConfigException('In order to use IDN validation intl extension must be installed and enabled.');\n        }\n        if ($this->message === null) {\n            $this->message = Yii::t('yii', '{attribute} is not a valid URL.');\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function validateAttribute($model, $attribute)\n    {\n        $value = $model->$attribute;\n        $result = $this->validateValue($value);\n        if (!empty($result)) {\n            $this->addError($model, $attribute, $result[0], $result[1]);\n        } elseif ($this->defaultScheme !== null && strpos($value, '://') === false) {\n            $model->$attribute = $this->defaultScheme . '://' . $value;\n        }\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function validateValue($value)\n    {\n        // make sure the length is limited to avoid DOS attacks\n        if (is_string($value) && strlen($value) < 2000) {\n            if ($this->defaultScheme !== null && strpos($value, '://') === false) {\n                $value = $this->defaultScheme . '://' . $value;\n            }\n\n            if (strpos($this->pattern, '{schemes}') !== false) {\n                $pattern = str_replace('{schemes}', '(' . implode('|', $this->validSchemes) . ')', $this->pattern);\n            } else {\n                $pattern = $this->pattern;\n            }\n\n            if ($this->enableIDN) {\n                $value = preg_replace_callback('/:\\/\\/([^\\/]+)/', function ($matches) {\n                    return '://' . $this->idnToAscii($matches[1]);\n                }, $value);\n            }\n\n            if (preg_match($pattern, $value)) {\n                return null;\n            }\n        }\n\n        return [$this->message, []];\n    }\n\n    private function idnToAscii($idn)\n    {\n        return idn_to_ascii($idn, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function clientValidateAttribute($model, $attribute, $view)\n    {\n        ValidationAsset::register($view);\n        if ($this->enableIDN) {\n            PunycodeAsset::register($view);\n        }\n        $options = $this->getClientOptions($model, $attribute);\n\n        return 'yii.validation.url(value, messages, ' . Json::htmlEncode($options) . ');';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getClientOptions($model, $attribute)\n    {\n        if (strpos($this->pattern, '{schemes}') !== false) {\n            $pattern = str_replace('{schemes}', '(' . implode('|', $this->validSchemes) . ')', $this->pattern);\n        } else {\n            $pattern = $this->pattern;\n        }\n\n        $options = [\n            'pattern' => new JsExpression($pattern),\n            'message' => $this->formatMessage($this->message, [\n                'attribute' => $model->getAttributeLabel($attribute),\n            ]),\n            'enableIDN' => (bool) $this->enableIDN,\n        ];\n        if ($this->skipOnEmpty) {\n            $options['skipOnEmpty'] = 1;\n        }\n        if ($this->defaultScheme !== null) {\n            $options['defaultScheme'] = $this->defaultScheme;\n        }\n\n        return $options;\n    }\n}\n"
  },
  {
    "path": "framework/validators/ValidationAsset.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\validators;\n\nuse yii\\web\\AssetBundle;\n\n/**\n * This asset bundle provides the javascript files for client validation.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass ValidationAsset extends AssetBundle\n{\n    public $sourcePath = '@yii/assets';\n    public $js = [\n        'yii.validation.js',\n    ];\n    public $depends = [\n        'yii\\web\\YiiAsset',\n    ];\n}\n"
  },
  {
    "path": "framework/validators/Validator.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\validators;\n\nuse Yii;\nuse yii\\base\\Component;\nuse yii\\base\\NotSupportedException;\n\n/**\n * Validator is the base class for all validators.\n *\n * Child classes should override the [[validateValue()]] and/or [[validateAttribute()]] methods to provide the actual\n * logic of performing data validation. Child classes may also override [[clientValidateAttribute()]]\n * to provide client-side validation support.\n *\n * Validator declares a set of [[builtInValidators|built-in validators]] which can\n * be referenced using short names. They are listed as follows:\n *\n * - `boolean`: [[BooleanValidator]]\n * - `captcha`: [[\\yii\\captcha\\CaptchaValidator]]\n * - `compare`: [[CompareValidator]]\n * - `date`: [[DateValidator]]\n * - `datetime`: [[DateValidator]]\n * - `time`: [[DateValidator]]\n * - `default`: [[DefaultValueValidator]]\n * - `double`: [[NumberValidator]]\n * - `each`: [[EachValidator]]\n * - `email`: [[EmailValidator]]\n * - `exist`: [[ExistValidator]]\n * - `file`: [[FileValidator]]\n * - `filter`: [[FilterValidator]]\n * - `image`: [[ImageValidator]]\n * - `in`: [[RangeValidator]]\n * - `integer`: [[NumberValidator]]\n * - `match`: [[RegularExpressionValidator]]\n * - `required`: [[RequiredValidator]]\n * - `safe`: [[SafeValidator]]\n * - `string`: [[StringValidator]]\n * - `trim`: [[TrimValidator]]\n * - `unique`: [[UniqueValidator]]\n * - `url`: [[UrlValidator]]\n * - `ip`: [[IpValidator]]\n *\n * For more details and usage information on Validator, see the [guide article on validators](guide:input-validation).\n *\n * @property-read array $attributeNames Attribute names.\n * @property-read array|null $validationAttributes List of attribute names.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Validator extends Component\n{\n    /**\n     * @var array list of built-in validators (name => class or configuration)\n     */\n    public static $builtInValidators = [\n        'boolean' => 'yii\\validators\\BooleanValidator',\n        'captcha' => 'yii\\captcha\\CaptchaValidator',\n        'compare' => 'yii\\validators\\CompareValidator',\n        'date' => 'yii\\validators\\DateValidator',\n        'datetime' => [\n            'class' => 'yii\\validators\\DateValidator',\n            'type' => DateValidator::TYPE_DATETIME,\n        ],\n        'time' => [\n            'class' => 'yii\\validators\\DateValidator',\n            'type' => DateValidator::TYPE_TIME,\n        ],\n        'default' => 'yii\\validators\\DefaultValueValidator',\n        'double' => 'yii\\validators\\NumberValidator',\n        'each' => 'yii\\validators\\EachValidator',\n        'email' => 'yii\\validators\\EmailValidator',\n        'exist' => 'yii\\validators\\ExistValidator',\n        'file' => 'yii\\validators\\FileValidator',\n        'filter' => 'yii\\validators\\FilterValidator',\n        'image' => 'yii\\validators\\ImageValidator',\n        'in' => 'yii\\validators\\RangeValidator',\n        'integer' => [\n            'class' => 'yii\\validators\\NumberValidator',\n            'integerOnly' => true,\n        ],\n        'match' => 'yii\\validators\\RegularExpressionValidator',\n        'number' => 'yii\\validators\\NumberValidator',\n        'required' => 'yii\\validators\\RequiredValidator',\n        'safe' => 'yii\\validators\\SafeValidator',\n        'string' => 'yii\\validators\\StringValidator',\n        'trim' => [\n            'class' => 'yii\\validators\\TrimValidator',\n            'skipOnArray' => true,\n        ],\n        'unique' => 'yii\\validators\\UniqueValidator',\n        'url' => 'yii\\validators\\UrlValidator',\n        'ip' => 'yii\\validators\\IpValidator',\n    ];\n    /**\n     * @var array|string attributes to be validated by this validator. For multiple attributes,\n     * please specify them as an array; for single attribute, you may use either a string or an array.\n     */\n    public $attributes = [];\n    /**\n     * @var string|null the user-defined error message. It may contain the following placeholders which\n     * will be replaced accordingly by the validator:\n     *\n     * - `{attribute}`: the label of the attribute being validated\n     * - `{value}`: the value of the attribute being validated\n     *\n     * Note that some validators may introduce other properties for error messages used when specific\n     * validation conditions are not met. Please refer to individual class API documentation for details\n     * about these properties. By convention, this property represents the primary error message\n     * used when the most important validation condition is not met.\n     */\n    public $message;\n    /**\n     * @var array|string scenarios that the validator can be applied to. For multiple scenarios,\n     * please specify them as an array; for single scenario, you may use either a string or an array.\n     */\n    public $on = [];\n    /**\n     * @var array|string scenarios that the validator should not be applied to. For multiple scenarios,\n     * please specify them as an array; for single scenario, you may use either a string or an array.\n     */\n    public $except = [];\n    /**\n     * @var bool whether this validation rule should be skipped if the attribute being validated\n     * already has some validation error according to some previous rules. Defaults to true.\n     */\n    public $skipOnError = true;\n    /**\n     * @var bool whether this validation rule should be skipped if the attribute value\n     * is null or an empty string. This property is used only when validating [[yii\\base\\Model]].\n     */\n    public $skipOnEmpty = true;\n    /**\n     * @var bool whether to enable client-side validation for this validator.\n     * The actual client-side validation is done via the JavaScript code returned\n     * by [[clientValidateAttribute()]]. If that method returns null, even if this property\n     * is true, no client-side validation will be done by this validator.\n     */\n    public $enableClientValidation = true;\n    /**\n     * @var callable|null a PHP callable that replaces the default implementation of [[isEmpty()]].\n     * If not set, [[isEmpty()]] will be used to check if a value is empty. The signature\n     * of the callable should be `function ($value)` which returns a boolean indicating\n     * whether the value is empty.\n     */\n    public $isEmpty;\n    /**\n     * @var callable|null a PHP callable whose return value determines whether this validator should be applied.\n     * The signature of the callable should be `function ($model, $attribute)`, where `$model` and `$attribute`\n     * refer to the model and the attribute currently being validated. The callable should return a boolean value.\n     *\n     * This property is mainly provided to support conditional validation on the server-side.\n     * If this property is not set, this validator will be always applied on the server-side.\n     *\n     * The following example will enable the validator only when the country currently selected is USA:\n     *\n     * ```\n     * function ($model) {\n     *     return $model->country == Country::USA;\n     * }\n     * ```\n     *\n     * @see whenClient\n     */\n    public $when;\n    /**\n     * @var string|null a JavaScript function name whose return value determines whether this validator should be applied\n     * on the client-side. The signature of the function should be `function (attribute, value)`, where\n     * `attribute` is an object describing the attribute being validated (see [[clientValidateAttribute()]])\n     * and `value` the current value of the attribute.\n     *\n     * This property is mainly provided to support conditional validation on the client-side.\n     * If this property is not set, this validator will be always applied on the client-side.\n     *\n     * The following example will enable the validator only when the country currently selected is USA:\n     *\n     * ```\n     * function (attribute, value) {\n     *     return $('#country').val() === 'USA';\n     * }\n     * ```\n     *\n     * @see when\n     */\n    public $whenClient;\n\n\n    /**\n     * Creates a validator object.\n     * @param string|\\Closure $type the validator type. This can be either:\n     *  * a built-in validator name listed in [[builtInValidators]];\n     *  * a method name of the model class;\n     *  * an anonymous function;\n     *  * a validator class name.\n     * @param \\yii\\base\\Model $model the data model to be validated.\n     * @param array|string $attributes list of attributes to be validated. This can be either an array of\n     * the attribute names or a string of comma-separated attribute names.\n     * @param array $params initial values to be applied to the validator properties.\n     * @return Validator the validator\n     */\n    public static function createValidator($type, $model, $attributes, $params = [])\n    {\n        $params['attributes'] = $attributes;\n\n        if ($type instanceof \\Closure) {\n            $params['class'] = __NAMESPACE__ . '\\InlineValidator';\n            $params['method'] = $type;\n        } elseif (!isset(static::$builtInValidators[$type]) && $model->hasMethod($type)) {\n            // method-based validator\n            $params['class'] = __NAMESPACE__ . '\\InlineValidator';\n            $params['method'] = [$model, $type];\n        } else {\n            unset($params['current']);\n            if (isset(static::$builtInValidators[$type])) {\n                $type = static::$builtInValidators[$type];\n            }\n            if (is_array($type)) {\n                $params = array_merge($type, $params);\n            } else {\n                $params['class'] = $type;\n            }\n        }\n\n        return Yii::createObject($params);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        $this->attributes = (array) $this->attributes;\n        $this->on = (array) $this->on;\n        $this->except = (array) $this->except;\n    }\n\n    /**\n     * Validates the specified object.\n     * @param \\yii\\base\\Model $model the data model being validated\n     * @param array|string|null $attributes the list of attributes to be validated.\n     * Note that if an attribute is not associated with the validator - it will be\n     * ignored. If this parameter is null, every attribute listed in [[attributes]] will be validated.\n     */\n    public function validateAttributes($model, $attributes = null)\n    {\n        $attributes = $this->getValidationAttributes($attributes);\n\n        foreach ($attributes as $attribute) {\n            $skip = $this->skipOnError && $model->hasErrors($attribute)\n                || $this->skipOnEmpty && $this->isEmpty($model->$attribute);\n            if (!$skip) {\n                if ($this->when === null || call_user_func($this->when, $model, $attribute)) {\n                    $this->validateAttribute($model, $attribute);\n                }\n            }\n        }\n    }\n\n    /**\n     * Returns a list of attributes this validator applies to.\n     * @param array|string|null $attributes the list of attributes to be validated.\n     *\n     * - If this is `null`, the result will be equal to [[getAttributeNames()]].\n     * - If this is a string or an array, the intersection of [[getAttributeNames()]]\n     *   and the specified attributes will be returned.\n     *\n     * @return array|null list of attribute names.\n     * @since 2.0.16\n     */\n    public function getValidationAttributes($attributes = null)\n    {\n        if ($attributes === null) {\n            return $this->getAttributeNames();\n        }\n\n        if (is_scalar($attributes)) {\n            $attributes = [$attributes];\n        }\n\n        $newAttributes = [];\n        $attributeNames = $this->getAttributeNames();\n        foreach ($attributes as $attribute) {\n            // do not strict compare, otherwise int attributes would fail due to to string conversion in getAttributeNames() using ltrim().\n            if (in_array($attribute, $attributeNames, false)) {\n                $newAttributes[] = $attribute;\n            }\n        }\n        return $newAttributes;\n    }\n\n    /**\n     * Validates a single attribute.\n     * Child classes must implement this method to provide the actual validation logic.\n     * @param \\yii\\base\\Model $model the data model to be validated\n     * @param string $attribute the name of the attribute to be validated.\n     */\n    public function validateAttribute($model, $attribute)\n    {\n        $result = $this->validateValue($model->$attribute);\n        if (!empty($result)) {\n            $this->addError($model, $attribute, $result[0], $result[1]);\n        }\n    }\n\n    /**\n     * Validates a given value.\n     * You may use this method to validate a value out of the context of a data model.\n     * @param mixed $value the data value to be validated.\n     * @param string|null $error the error message to be returned, if the validation fails.\n     * @return bool whether the data is valid.\n     */\n    public function validate($value, &$error = null)\n    {\n        $result = $this->validateValue($value);\n        if (empty($result)) {\n            return true;\n        }\n\n        list($message, $params) = $result;\n        $params['attribute'] = Yii::t('yii', 'the input value');\n        if (is_array($value)) {\n            $params['value'] = 'array()';\n        } elseif (is_object($value)) {\n            $params['value'] = 'object';\n        } else {\n            $params['value'] = $value;\n        }\n        $error = $this->formatMessage($message, $params);\n\n        return false;\n    }\n\n    /**\n     * Validates a value.\n     * A validator class can implement this method to support data validation out of the context of a data model.\n     * @param mixed $value the data value to be validated.\n     * @return array|null the error message and the array of parameters to be inserted into the error message.\n     * ```\n     * if (!$valid) {\n     *     return [$this->message, [\n     *         'param1' => $this->param1,\n     *         'formattedLimit' => Yii::$app->formatter->asShortSize($this->getSizeLimit()),\n     *         'mimeTypes' => implode(', ', $this->mimeTypes),\n     *         'param4' => 'etc...',\n     *     ]];\n     * }\n     *\n     * return null;\n     * ```\n     * for this example `message` template can contain `{param1}`, `{formattedLimit}`, `{mimeTypes}`, `{param4}`\n     *\n     * Null should be returned if the data is valid.\n     * @throws NotSupportedException if the validator does not supporting data validation without a model\n     */\n    protected function validateValue($value)\n    {\n        throw new NotSupportedException(get_class($this) . ' does not support validateValue().');\n    }\n\n    /**\n     * Returns the JavaScript needed for performing client-side validation.\n     *\n     * Calls [[getClientOptions()]] to generate options array for client-side validation.\n     *\n     * You may override this method to return the JavaScript validation code if\n     * the validator can support client-side validation.\n     *\n     * The following JavaScript variables are predefined and can be used in the validation code:\n     *\n     * - `attribute`: an object describing the the attribute being validated.\n     * - `value`: the value being validated.\n     * - `messages`: an array used to hold the validation error messages for the attribute.\n     * - `deferred`: an array used to hold deferred objects for asynchronous validation\n     * - `$form`: a jQuery object containing the form element\n     *\n     * The `attribute` object contains the following properties:\n     * - `id`: a unique ID identifying the attribute (e.g. \"loginform-username\") in the form\n     * - `name`: attribute name or expression (e.g. \"[0]content\" for tabular input)\n     * - `container`: the jQuery selector of the container of the input field\n     * - `input`: the jQuery selector of the input field under the context of the form\n     * - `error`: the jQuery selector of the error tag under the context of the container\n     * - `status`: status of the input field, 0: empty, not entered before, 1: validated, 2: pending validation, 3: validating\n     *\n     * @param \\yii\\base\\Model $model the data model being validated\n     * @param string $attribute the name of the attribute to be validated.\n     * @param \\yii\\web\\View $view the view object that is going to be used to render views or view files\n     * containing a model form with this validator applied.\n     * @return string|null the client-side validation script. Null if the validator does not support\n     * client-side validation.\n     * @see getClientOptions()\n     * @see \\yii\\widgets\\ActiveForm::enableClientValidation\n     */\n    public function clientValidateAttribute($model, $attribute, $view)\n    {\n        return null;\n    }\n\n    /**\n     * Returns the client-side validation options.\n     * This method is usually called from [[clientValidateAttribute()]]. You may override this method to modify options\n     * that will be passed to the client-side validation.\n     * @param \\yii\\base\\Model $model the model being validated\n     * @param string $attribute the attribute name being validated\n     * @return array the client-side validation options\n     * @since 2.0.11\n     */\n    public function getClientOptions($model, $attribute)\n    {\n        return [];\n    }\n\n    /**\n     * Returns a value indicating whether the validator is active for the given scenario and attribute.\n     *\n     * A validator is active if\n     *\n     * - the validator's `on` property is empty, or\n     * - the validator's `on` property contains the specified scenario\n     *\n     * @param string $scenario scenario name\n     * @return bool whether the validator applies to the specified scenario.\n     */\n    public function isActive($scenario)\n    {\n        return !in_array($scenario, $this->except, true) && (empty($this->on) || in_array($scenario, $this->on, true));\n    }\n\n    /**\n     * Adds an error about the specified attribute to the model object.\n     * This is a helper method that performs message selection and internationalization.\n     * @param \\yii\\base\\Model $model the data model being validated\n     * @param string $attribute the attribute being validated\n     * @param string $message the error message\n     * @param array $params values for the placeholders in the error message\n     */\n    public function addError($model, $attribute, $message, $params = [])\n    {\n        $params['attribute'] = $model->getAttributeLabel($attribute);\n        if (!isset($params['value'])) {\n            $value = $model->$attribute;\n            if (is_array($value)) {\n                $params['value'] = 'array()';\n            } elseif (is_object($value) && !method_exists($value, '__toString')) {\n                $params['value'] = '(object)';\n            } else {\n                $params['value'] = $value;\n            }\n        }\n        $model->addError($attribute, $this->formatMessage($message, $params));\n    }\n\n    /**\n     * Checks if the given value is empty.\n     * A value is considered empty if it is null, an empty array, or an empty string.\n     * Note that this method is different from PHP empty(). It will return false when the value is 0.\n     * @param mixed $value the value to be checked\n     * @return bool whether the value is empty\n     */\n    public function isEmpty($value)\n    {\n        if ($this->isEmpty !== null) {\n            return call_user_func($this->isEmpty, $value);\n        }\n\n        return $value === null || $value === [] || $value === '';\n    }\n\n    /**\n     * Formats a mesage using the I18N, or simple strtr if `\\Yii::$app` is not available.\n     * @param string $message\n     * @param array $params\n     * @since 2.0.12\n     * @return string\n     */\n    protected function formatMessage($message, $params)\n    {\n        if (Yii::$app !== null) {\n            return \\Yii::$app->getI18n()->format($message, $params, Yii::$app->language);\n        }\n\n        $placeholders = [];\n        foreach ((array) $params as $name => $value) {\n            $placeholders['{' . $name . '}'] = $value;\n        }\n\n        return ($placeholders === []) ? $message : strtr($message, $placeholders);\n    }\n\n    /**\n     * Returns cleaned attribute names without the `!` character at the beginning.\n     * @return array attribute names.\n     * @since 2.0.12\n     */\n    public function getAttributeNames()\n    {\n        return array_map(function ($attribute) {\n            return ltrim($attribute, '!');\n        }, $this->attributes);\n    }\n}\n"
  },
  {
    "path": "framework/views/_addColumns.php",
    "content": "<?php\n\n/**\n * @var \\yii\\web\\View $this\n * @var array $fields\n * @var string $table\n * @var array $foreignKeys\n */\n\n?>\n<?php foreach ($fields as $field): ?>\n        $this->addColumn('<?=\n            $table\n        ?>', '<?=\n            $field['property']\n        ?>', $this-><?=\n            $field['decorators']\n        ?>);\n<?php endforeach;\n\necho $this->render('_addForeignKeys', [\n    'table' => $table,\n    'foreignKeys' => $foreignKeys,\n]);\n"
  },
  {
    "path": "framework/views/_addComments.php",
    "content": "<?php\n/**\n * Creates a call for the method `yii\\db\\Migration::createTable()`\n *\n * @var string $table the name table\n * @var string $tableComment the comment table\n */\n?>        $this->addCommentOnTable('<?= $table ?>', '<?= $tableComment ?>');\n"
  },
  {
    "path": "framework/views/_addForeignKeys.php",
    "content": "<?php\n\n/**\n * @var array $foreignKeys\n * @var string $table\n */\n\n?>\n<?php foreach ($foreignKeys as $column => $fkData): ?>\n\n        // creates index for column `<?= $column ?>`\n        $this->createIndex(\n            '<?= $fkData['idx']  ?>',\n            '<?= $table ?>',\n            '<?= $column ?>'\n        );\n\n        // add foreign key for table `<?= $fkData['relatedTable'] ?>`\n        $this->addForeignKey(\n            '<?= $fkData['fk'] ?>',\n            '<?= $table ?>',\n            '<?= $column ?>',\n            '<?= $fkData['relatedTable'] ?>',\n            '<?= $fkData['relatedColumn'] ?>',\n            'CASCADE'\n        );\n<?php endforeach;\n"
  },
  {
    "path": "framework/views/_createTable.php",
    "content": "<?php\n\n/**\n * Creates a call for the method `yii\\db\\Migration::createTable()`.\n *\n * @var \\yii\\web\\View $this\n * @var string $table the name table\n * @var array $fields the fields\n * @var array $foreignKeys the foreign keys\n */\n\n?>        $this->createTable('<?= $table ?>', [\n<?php foreach ($fields as $field):\n    if (empty($field['decorators'])): ?>\n            '<?= $field['property'] ?>',\n<?php else: ?>\n            <?= \"'{$field['property']}' => \\$this->{$field['decorators']}\" ?>,\n<?php endif;\nendforeach; ?>\n        ]);\n<?= $this->render('_addForeignKeys', [\n    'table' => $table,\n    'foreignKeys' => $foreignKeys,\n]);\n"
  },
  {
    "path": "framework/views/_dropColumns.php",
    "content": "<?php\n\n/**\n * @var \\yii\\web\\View $this\n * @var string $table\n * @var array $foreignKeys\n * @var array $fields\n */\n\necho  $this->render('_dropForeignKeys', [\n    'table' => $table,\n    'foreignKeys' => $foreignKeys,\n]);\n\nforeach ($fields as $field): ?>\n        $this->dropColumn('<?= $table ?>', '<?= $field['property'] ?>');\n<?php endforeach;\n"
  },
  {
    "path": "framework/views/_dropForeignKeys.php",
    "content": "<?php\n\n/**\n * @var array $foreignKeys\n * @var string $table\n */\n\n?>\n<?php foreach ($foreignKeys as $column => $fkData): ?>\n        // drops foreign key for table `<?= $fkData['relatedTable'] ?>`\n        $this->dropForeignKey(\n            '<?= $fkData['fk'] ?>',\n            '<?= $table ?>'\n        );\n\n        // drops index for column `<?= $column ?>`\n        $this->dropIndex(\n            '<?= $fkData['idx'] ?>',\n            '<?= $table ?>'\n        );\n\n<?php endforeach;\n"
  },
  {
    "path": "framework/views/_dropTable.php",
    "content": "<?php\n\n/**\n * Creates a call for the method `yii\\db\\Migration::dropTable()`.\n *\n * @var \\yii\\web\\View $this\n * @var string $table the name table\n * @var array $foreignKeys the foreign keys\n */\n\necho $this->render('_dropForeignKeys', [\n    'table' => $table,\n    'foreignKeys' => $foreignKeys,\n]) ?>\n        $this->dropTable('<?= $table ?>');\n"
  },
  {
    "path": "framework/views/_foreignTables.php",
    "content": "<?php\n\n/**\n * Creates a call for the method `yii\\db\\Migration::createTable()`.\n *\n * @var array $foreignKeys the foreign keys\n */\n\nif (!empty($foreignKeys)):?>\n * Has foreign keys to the tables:\n *\n<?php foreach ($foreignKeys as $fkData): ?>\n * - `<?= $fkData['relatedTable'] ?>`\n<?php endforeach;\nendif;\n"
  },
  {
    "path": "framework/views/addColumnMigration.php",
    "content": "<?php\n\n/**\n * This view is used by console/controllers/MigrateController.php.\n *\n * The following variables are available in this view:\n *\n * @var \\yii\\web\\View $this\n * @var string $className the new migration class name without namespace\n * @var string $namespace the new migration class namespace\n * @var string $table the name table\n * @var array $fields the fields\n * @var array $foreignKeys\n */\n\necho \"<?php\\n\";\nif (!empty($namespace)) {\n    echo \"\\nnamespace {$namespace};\\n\";\n}\n?>\n\nuse yii\\db\\Migration;\n\n/**\n * Handles adding columns to table `<?= $table ?>`.\n<?= $this->render('_foreignTables', [\n     'foreignKeys' => $foreignKeys,\n ]) ?>\n */\nclass <?= $className ?> extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function safeUp()\n    {\n<?= $this->render('_addColumns', [\n    'table' => $table,\n    'fields' => $fields,\n    'foreignKeys' => $foreignKeys,\n])\n?>\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function safeDown()\n    {\n<?= $this->render('_dropColumns', [\n    'table' => $table,\n    'fields' => $fields,\n    'foreignKeys' => $foreignKeys,\n])\n?>\n    }\n}\n"
  },
  {
    "path": "framework/views/createJunctionMigration.php",
    "content": "<?php\n\n/**\n * This view is used by console/controllers/MigrateController.php.\n *\n * The following variables are available in this view:\n * @since 2.0.7\n * @deprecated since 2.0.8\n *\n * @var string $className the new migration class name without namespace\n * @var string $namespace the new migration class namespace\n * @var string $table the name table\n * @var string $field_first the name field first\n * @var string $field_second the name field second\n */\n\necho \"<?php\\n\";\nif (!empty($namespace)) {\n    echo \"\\nnamespace {$namespace};\\n\";\n}\n?>\n\nuse yii\\db\\Migration;\n\n/**\n * Handles the creation of table `<?= $table ?>` which is a junction between\n * table `<?= $field_first ?>` and table `<?= $field_second ?>`.\n */\nclass <?= $className ?> extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function safeUp()\n    {\n        $this->createTable('<?= $table ?>', [\n            '<?= $field_first ?>_id' => $this->integer(),\n            '<?= $field_second ?>_id' => $this->integer(),\n            'PRIMARY KEY(<?= $field_first ?>_id, <?= $field_second ?>_id)',\n        ]);\n\n        $this->createIndex(\n            'idx-<?= $table . '-' . $field_first ?>_id',\n            '<?= $table ?>',\n            '<?= $field_first ?>_id'\n        );\n\n        $this->createIndex(\n            'idx-<?= $table . '-' . $field_second ?>_id',\n            '<?= $table ?>',\n            '<?= $field_second ?>_id'\n        );\n\n        $this->addForeignKey(\n            'fk-<?= $table . '-' . $field_first ?>_id',\n            '<?= $table ?>',\n            '<?= $field_first ?>_id',\n            '<?= $field_first ?>',\n            'id',\n            'CASCADE'\n        );\n\n        $this->addForeignKey(\n            'fk-<?= $table . '-' . $field_second ?>_id',\n            '<?= $table ?>',\n            '<?= $field_second ?>_id',\n            '<?= $field_second ?>',\n            'id',\n            'CASCADE'\n        );\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function safeDown()\n    {\n        $this->dropTable('<?= $table ?>');\n    }\n}\n"
  },
  {
    "path": "framework/views/createTableMigration.php",
    "content": "<?php\n\n/**\n * This view is used by console/controllers/MigrateController.php.\n *\n * The following variables are available in this view:\n *\n * @var \\yii\\web\\View $this\n * @var string $className the new migration class name without namespace\n * @var string $namespace the new migration class namespace\n * @var string $table the name table\n * @var string $tableComment the comment table\n * @var array $fields the fields\n * @var array $foreignKeys the foreign keys\n */\n\necho \"<?php\\n\";\nif (!empty($namespace)) {\n    echo \"\\nnamespace {$namespace};\\n\";\n}\n?>\n\nuse yii\\db\\Migration;\n\n/**\n * Handles the creation of table `<?= $table ?>`.\n<?= $this->render('_foreignTables', [\n    'foreignKeys' => $foreignKeys,\n]) ?>\n */\nclass <?= $className ?> extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function safeUp()\n    {\n<?= $this->render('_createTable', [\n    'table' => $table,\n    'fields' => $fields,\n    'foreignKeys' => $foreignKeys,\n])\n?>\n<?php if (!empty($tableComment)) {\n    echo $this->render('_addComments', [\n        'table' => $table,\n        'tableComment' => $tableComment,\n    ]);\n}\n?>\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function safeDown()\n    {\n<?= $this->render('_dropTable', [\n    'table' => $table,\n    'foreignKeys' => $foreignKeys,\n])\n?>\n    }\n}\n"
  },
  {
    "path": "framework/views/dropColumnMigration.php",
    "content": "<?php\n\n/**\n * This view is used by console/controllers/MigrateController.php.\n *\n * The following variables are available in this view:\n *\n * @var \\yii\\web\\View $this\n * @var string $className the new migration class name without namespace\n * @var string $namespace the new migration class namespace\n * @var string $table the name table\n * @var array $fields the fields\n * @var array $foreignKeys\n */\n\necho \"<?php\\n\";\nif (!empty($namespace)) {\n    echo \"\\nnamespace {$namespace};\\n\";\n}\n?>\n\nuse yii\\db\\Migration;\n\n/**\n * Handles dropping columns from table `<?= $table ?>`.\n<?= $this->render('_foreignTables', [\n    'foreignKeys' => $foreignKeys,\n]) ?>\n */\nclass <?= $className ?> extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function safeUp()\n    {\n<?= $this->render('_dropColumns', [\n    'table' => $table,\n    'fields' => $fields,\n    'foreignKeys' => $foreignKeys,\n])\n?>\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function safeDown()\n    {\n<?= $this->render('_addColumns', [\n    'table' => $table,\n    'fields' => $fields,\n    'foreignKeys' => $foreignKeys,\n])\n?>\n    }\n}\n"
  },
  {
    "path": "framework/views/dropTableMigration.php",
    "content": "<?php\n\n/**\n * This view is used by console/controllers/MigrateController.php.\n *\n * The following variables are available in this view:\n *\n * @var \\yii\\web\\View $this\n * @var string $className the new migration class name without namespace\n * @var string $namespace the new migration class namespace\n * @var string $table the name table\n * @var array $fields the fields\n * @var array $foreignKeys\n */\n\necho \"<?php\\n\";\nif (!empty($namespace)) {\n    echo \"\\nnamespace {$namespace};\\n\";\n}\n?>\n\nuse yii\\db\\Migration;\n\n/**\n * Handles the dropping of table `<?= $table ?>`.\n<?= $this->render('_foreignTables', [\n    'foreignKeys' => $foreignKeys,\n]) ?>\n */\nclass <?= $className ?> extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function safeUp()\n    {\n<?= $this->render('_dropTable', [\n    'table' => $table,\n    'foreignKeys' => $foreignKeys,\n])\n?>\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function safeDown()\n    {\n<?= $this->render('_createTable', [\n    'table' => $table,\n    'fields' => $fields,\n    'foreignKeys' => $foreignKeys,\n])\n?>\n    }\n}\n"
  },
  {
    "path": "framework/views/errorHandler/callStackItem.php",
    "content": "<?php\n/**\n * @var string|null $file\n * @var int|null $line\n * @var string|null $class\n * @var string|null $method\n * @var int $index\n * @var string[] $lines\n * @var int $begin\n * @var int $end\n * @var array $args\n * @var \\yii\\web\\ErrorHandler $handler\n */\n$html = <<<HTML\nIDE\n<svg class=\"icon icon--new-window\" focusable=\"false\" aria-hidden=\"true\" width=\"16\" height=\"16\">\n    <use href=\"#new-window\"></use>\n</svg>\nHTML;\n?>\n<li class=\"<?= ($index === 1 || !$handler->isCoreFile($file)) ? 'application' : '' ?> call-stack-item\"\n    data-line=\"<?= (int) ($line - $begin) ?>\">\n    <div class=\"element-wrap\">\n        <div class=\"element\">\n            <span class=\"item-number\"><?= (int) $index ?>.</span>\n            <span class=\"text\"><?= $file !== null ? 'in ' . $handler->htmlEncode($file) : '' ?></span>\n            <?php if ($handler->traceLine !== '{html}'): ?>\n                <span> &ndash; </span>\n                <?= strtr($handler->traceLine, ['{file}' => $file, '{line}' => $line + 1, '{html}' => $html]) ?>\n            <?php endif; ?>\n            <span class=\"at\">\n                <?= $line !== null ? 'at line' : '' ?>\n                <span class=\"line\"><?= $line !== null ? $line + 1 : '' ?></span>\n            </span>\n            <?php if ($method !== null): ?>\n                <span class=\"call\">\n                    <?= $file !== null ? '&ndash;' : '' ?>\n                    <?= ($class !== null ? $handler->addTypeLinks(\"$class::$method\") : $handler->htmlEncode($method)) . '(' . $handler->argumentsToString($args) . ')' ?>\n                </span>\n            <?php endif; ?>\n        </div>\n    </div>\n    <?php if (!empty($lines)): ?>\n        <div class=\"code-wrap\">\n            <div class=\"error-line\"></div>\n            <?php for ($i = $begin; $i <= $end; ++$i): ?><div class=\"hover-line\"></div><?php endfor; ?>\n            <div class=\"code\">\n                <?php for ($i = $begin; $i <= $end; ++$i): ?><span class=\"lines-item\"><?= (int) ($i + 1) ?></span><?php endfor; ?>\n                <pre><?php\n                    // fill empty lines with a whitespace to avoid rendering problems in opera\n                    for ($i = $begin; $i <= $end; ++$i) {\n                        echo (trim($lines[$i]) === '') ? \" \\n\" : $handler->htmlEncode($lines[$i]);\n                    }\n                    ?></pre>\n            </div>\n        </div>\n    <?php endif; ?>\n</li>\n"
  },
  {
    "path": "framework/views/errorHandler/error.php",
    "content": "<?php\n/**\n * @var \\yii\\web\\View $this\n * @var \\Throwable $exception\n * @var \\yii\\web\\ErrorHandler $handler\n */\nif ($exception instanceof \\yii\\web\\HttpException) {\n    $code = $exception->statusCode;\n} else {\n    $code = $exception->getCode();\n}\n$name = $handler->getExceptionName($exception);\nif ($name === null) {\n    $name = 'Error';\n}\nif ($code) {\n    $name .= \" (#$code)\";\n}\n\nif ($exception instanceof \\yii\\base\\UserException) {\n    $message = $exception->getMessage();\n} else {\n    $message = 'An internal server error occurred.';\n}\n\nif (method_exists($this, 'beginPage')) {\n    $this->beginPage();\n}\n?>\n<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"utf-8\" />\n    <title><?= $handler->htmlEncode($name) ?></title>\n\n    <style>\n        body {\n            font: normal 9pt \"Verdana\";\n            color: #000;\n            background: #fff;\n        }\n\n        h1 {\n            font: normal 18pt \"Verdana\";\n            color: #f00;\n            margin-bottom: .5em;\n        }\n\n        h2 {\n            font: normal 14pt \"Verdana\";\n            color: #800000;\n            margin-bottom: .5em;\n        }\n\n        h3 {\n            font: bold 11pt \"Verdana\";\n        }\n\n        p {\n            font: normal 9pt \"Verdana\";\n            color: #000;\n        }\n\n        .version {\n            color: gray;\n            font-size: 8pt;\n            border-top: 1px solid #aaa;\n            padding-top: 1em;\n            margin-bottom: 1em;\n        }\n    </style>\n</head>\n\n<body>\n    <h1><?= $handler->htmlEncode($name) ?></h1>\n    <h2><?= nl2br($handler->htmlEncode($message)) ?></h2>\n    <p>\n        The above error occurred while the Web server was processing your request.\n    </p>\n    <p>\n        Please contact us if you think this is a server error. Thank you.\n    </p>\n    <div class=\"version\">\n        <?= date('Y-m-d H:i:s') ?>\n    </div>\n    <?php if (method_exists($this, 'endBody')): ?>\n        <?php $this->endBody() // to allow injecting code into body (mostly by Yii Debug Toolbar)?>\n    <?php endif ?>\n</body>\n</html>\n<?php if (method_exists($this, 'endPage')): ?>\n    <?php $this->endPage() ?>\n<?php endif ?>\n"
  },
  {
    "path": "framework/views/errorHandler/exception.php",
    "content": "<?php\n/**\n * @var \\yii\\web\\View $this\n * @var \\Throwable $exception\n * @var \\yii\\web\\ErrorHandler $handler\n */\n?>\n<?php if (method_exists($this, 'beginPage')): ?>\n    <?php $this->beginPage() ?>\n<?php endif ?>\n<!doctype html>\n<html lang=\"en\">\n\n<head>\n    <meta charset=\"utf-8\"/>\n\n    <title><?php\n        $name = $handler->getExceptionName($exception);\n        if ($exception instanceof \\yii\\web\\HttpException) {\n            echo (int) $exception->statusCode . ' ' . $handler->htmlEncode($name);\n        } else {\n            if ($name !== null) {\n                echo $handler->htmlEncode($name . ' – ' . get_class($exception));\n            } else {\n                echo $handler->htmlEncode(get_class($exception));\n            }\n        }\n    ?></title>\n\n    <style type=\"text/css\">\n/* reset */\nhtml,body,div,span,h1,h2,h3,h4,h5,h6,p,pre,a,code,em,img,strong,b,i,ul,li{\n    margin: 0;\n    padding: 0;\n    border: 0;\n    font-size: 100%;\n    font: inherit;\n    vertical-align: baseline;\n}\nbody{\n    line-height: 1;\n}\nul{\n    list-style: none;\n}\n\n/* base */\na{\n    text-decoration: none;\n}\na:hover{\n    text-decoration: underline;\n}\nh1,h2,h3,p,img,ul li{\n    font-family: Arial,sans-serif;\n    color: #505050;\n}\n/*corresponds to min-width of 860px for some elements (.header .footer .element ...)*/\n@media screen and (min-width: 960px) {\n    html,body{\n        overflow-x: hidden;\n    }\n}\n\n/* header */\n.header{\n    min-width: 860px; /* 960px - 50px * 2 */\n    margin: 0 auto;\n    background: #f3f3f3;\n    padding: 40px 50px 30px 50px;\n    border-bottom: #ccc 1px solid;\n}\n.header h1{\n    font-size: 30px;\n    color: #e57373;\n    margin-bottom: 30px;\n}\n.header h1 span, .header h1 span a{\n    color: #e51717;\n}\n.header h1 a{\n    color: #e57373;\n}\n.header h1 a:hover{\n    color: #e51717;\n}\n.header img.erroricon{\n    float: right;\n    margin-top: -15px;\n    margin-left: 50px;\n}\n.header .tools{\n    float: right;\n}\n.header .tools a {\n    border-radius: 5px;\n    width: 25px;\n    text-align: center;\n    margin-right: 7px;\n}\n.header .tools a,\n.header .tools span{\n    display: block;\n    float: left;\n    height: 25px;\n    padding: 5px;\n}\n.header .tools span{\n    display: none;\n}\n.header .tools a:hover{\n    background: #fff;\n    text-decoration: none;\n}\n.header .tools a:active img{\n    position: relative;\n    left: 2px;\n    top: 2px;\n}\n.header .tools textarea{\n    position: absolute;\n    top: -500px;\n    right: 300px;\n    width: 750px;\n    height: 150px;\n}\n.header h2{\n    font-size: 20px;\n    line-height: 1.25;\n}\n.header pre{\n    margin: 10px 0;\n    overflow-y: scroll;\n    font-family: Courier, monospace;\n    font-size: 14px;\n}\n\n/* previous exceptions */\n.header .previous{\n    margin: 20px 0;\n    padding-left: 30px;\n}\n.header .previous div{\n    margin: 20px 0;\n}\n.header .previous .arrow{\n    -moz-transform: scale(-1, 1);\n    -webkit-transform: scale(-1, 1);\n    -o-transform: scale(-1, 1);\n    transform: scale(-1, 1);\n    filter: progid:DXImageTransform.Microsoft.BasicImage(mirror=1);\n    font-size: 26px;\n    position: absolute;\n    margin-top: -3px;\n    margin-left: -30px;\n    color: #e51717;\n}\n.header .previous h2{\n    font-size: 20px;\n    color: #e57373;\n    margin-bottom: 10px;\n}\n.header .previous h2 span{\n    color: #e51717;\n}\n.header .previous h2 a{\n    color: #e57373;\n}\n.header .previous h2 a:hover{\n    color: #e51717;\n}\n.header .previous h3{\n    font-size: 14px;\n    margin: 10px 0;\n}\n.header .previous p{\n    font-size: 14px;\n    color: #aaa;\n}\n.header .previous pre{\n    font-family: Courier, monospace;\n    font-size: 14px;\n    margin: 10px 0;\n}\n\n/* call stack */\n.call-stack{\n    margin-top: 30px;\n    margin-bottom: 40px;\n}\n.call-stack ul li{\n    margin: 1px 0;\n}\n.call-stack ul li .element-wrap{\n    cursor: pointer;\n    padding: 15px 0;\n    background-color: #fdfdfd;\n}\n.call-stack ul li.application .element-wrap{\n    background-color: #fafafa;\n}\n.call-stack ul li .element-wrap:hover{\n    background-color: #edf9ff;\n}\n.call-stack ul li .element{\n    min-width: 860px; /* 960px - 50px * 2 */\n    margin: 0 auto;\n    padding: 0 50px;\n    position: relative;\n}\n.call-stack ul li a{\n    color: #505050;\n}\n.call-stack ul li a:hover{\n    color: #000;\n}\n.call-stack ul li .item-number{\n    width: 45px;\n    display: inline-block;\n}\n.call-stack ul li .text{\n    color: #aaa;\n}\n.call-stack ul li.application .text{\n    color: #505050;\n}\n.call-stack ul li .at{\n    float: right;\n    display: inline-block;\n    width: 7em;\n    padding-left: 1em;\n    text-align: left;\n    color: #aaa;\n}\n.call-stack ul li.application .at{\n    color: #505050;\n}\n.call-stack ul li .line{\n    display: inline-block;\n    width: 3em;\n    text-align: right;\n}\n.call-stack ul li .code-wrap{\n    display: none;\n    position: relative;\n}\n.call-stack ul li.application .code-wrap{\n    display: block;\n}\n.call-stack ul li .error-line,\n.call-stack ul li .hover-line{\n    background-color: #ffebeb;\n    position: absolute;\n    width: 100%;\n    z-index: 100;\n    margin-top: 0;\n}\n.call-stack ul li .hover-line{\n    background: none;\n}\n.call-stack ul li .hover-line.hover,\n.call-stack ul li .hover-line:hover{\n    background: #edf9ff !important;\n}\n.call-stack ul li .code{\n    min-width: 860px; /* 960px - 50px * 2 */\n    margin: 15px auto;\n    padding: 0 50px;\n    position: relative;\n}\n.call-stack ul li .code .lines-item{\n    position: absolute;\n    z-index: 200;\n    display: block;\n    width: 25px;\n    text-align: right;\n    color: #aaa;\n    line-height: 20px;\n    font-size: 12px;\n    margin-top: 1px;\n    font-family: Consolas, monospace;\n}\n.call-stack ul li .code pre{\n    position: relative;\n    z-index: 200;\n    left: 50px;\n    line-height: 20px;\n    font-size: 12px;\n    font-family: Consolas, monospace;\n    display: inline;\n}\n@-moz-document url-prefix() {\n    .call-stack ul li .code pre{\n        line-height: 20px;\n    }\n}\n\n/* request */\n.request{\n    background-color: #fafafa;\n    padding-top: 40px;\n    padding-bottom: 40px;\n    margin-top: 40px;\n    margin-bottom: 1px;\n}\n.request .code{\n    min-width: 860px; /* 960px - 50px * 2 */\n    margin: 0 auto;\n    padding: 15px 50px;\n}\n.request .code pre{\n    font-size: 14px;\n    line-height: 18px;\n    font-family: Consolas, monospace;\n    display: inline;\n    word-wrap: break-word;\n}\n\n/* footer */\n.footer{\n    position: relative;\n    height: 222px;\n    min-width: 860px; /* 960px - 50px * 2 */\n    padding: 0 50px;\n    margin: 1px auto 0 auto;\n}\n.footer p{\n    font-size: 16px;\n    padding-bottom: 10px;\n}\n.footer p a{\n    color: #505050;\n}\n.footer p a:hover{\n    color: #000;\n}\n.footer .timestamp{\n    font-size: 14px;\n    padding-top: 67px;\n    margin-bottom: 28px;\n}\n.footer img{\n    position: absolute;\n    right: -50px;\n}\n\n/* highlight.js */\n.comment{\n    color: #808080;\n    font-style: italic;\n}\n.keyword{\n    color: #000080;\n}\n.number{\n    color: #00a;\n}\n.number{\n    font-weight: normal;\n}\n.string, .value{\n    color: #0a0;\n}\n.symbol, .char {\n    color: #505050;\n    background: #d0eded;\n    font-style: italic;\n}\n.phpdoc{\n    text-decoration: underline;\n}\n.variable{\n    color: #a00;\n}\n\nbody pre {\n    pointer-events: none;\n}\nbody.mousedown pre {\n    pointer-events: auto;\n}\n    </style>\n</head>\n\n<body>\n    <svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" display=\"none\">\n        <symbol id=\"new-window\" viewBox=\"0 0 24 24\">\n            <g transform=\"scale(0.0234375 0.0234375)\">\n                <path d=\"M598 128h298v298h-86v-152l-418 418-60-60 418-418h-152v-86zM810 810v-298h86v298c0 46-40 86-86 86h-596c-48 0-86-40-86-86v-596c0-46 38-86 86-86h298v86h-298v596h596z\"></path>\n            </g>\n        </symbol>\n    </svg>\n\n    <div class=\"header\">\n        <div class=\"tools\">\n            <textarea id=\"clipboard\"><?= $handler->htmlEncode($exception) ?></textarea>\n            <span id=\"copied\">Copied!</span>\n            <!-- Icon Credit: Font Awesome by Dave Gandy - https://fontawesome.com/ ; fa-clipboard, fa-stackoverflow, fa-google-->\n            <a href=\"#\" id=\"copy-stacktrace\" title=\"Copy the stacktrace for use in a bug report or pastebin\">\n            <img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAZCAYAAAArK+5dAAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAR1JREFUSMftlTFKxEAYhb8n2c7CarGR9RAWHkFsvMIqCy4iVpZ6AS9gIXqDbbdYLEUstLPwBt5AXQw+mxTDkkyymWwh+CAQMsP/zZt/XkbAEbAPiOVlYGr7rmqCgCcgo71y27tVg9lC8Wvbt3UVJV0CB0ENYoBQG5K2Gqx6vak9Ac8kyvZO1dgaK1YnAEnDpj2I6azk2wXQB04l9WzftAbYfixZ+Wfx+gocS2IR0lUPToCXAjLqHGD7o9jCB2AcQjo7RbbnwDlwH0Ky1LpFLyYluzKW9J7qYAbkwCB4wj/BYeMkx9JakY0JMPgbSf4HJAPeVgnIgBGwHXFzBWy2Btj+irmQNE91UKfvAtTmas1luy6Re8AQ6C1Z/AeY/gK6sUu5CuQ0NQAAAABJRU5ErkJggg==\" alt=\"Copy Stacktrace\"/>\n            </a>\n            <a href=\"https://stackoverflow.com/search?<?= http_build_query(['q' => $exception->getMessage()]) ?>\" title=\"Search error on Stackoverflow\" target=\"_blank\">\n                <img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAAAZCAYAAADaILXQAAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAc9JREFUSMe11MuLzlEcx/GXuTQpCzQYl3EL0UxNNDRNsWAht40UC7OzkT9BsrCQkI2ysJeSknssRsl9lFyGGrkUjRqDlEth2HzU0+SZeeZ5PN86nfP7ds7n+z3v7/f8qMw6MFEVbDt6cKTYhtoKxOuxAXPxFQ8rFa/D76z7s27PuIWBSlDsxmGMz3cNjgXPWUwoV3gh7kboBJrib8SV+A+Ui+UDHmEVpmMtHuAV+rAO8/ERveUwf4Nr6Ezm6/E2vnosxQpcx2Ap4u059BJD+IRLaEUzVifJ49k7E8txrhTxfdiGjRF/ji+4HN6LsSxIDgXXTdyoLaH15qWYk4JjMxrC+WoCdUS8DTtxEb9GEx/CbZzOQ1mQIO3YmvUZ3MPK3Kb77+FxYyxoAzahK2zhZ0TP437BIxuxW6ZhP2bn0XwOgl6cSgvOwlQswkm8H860mLWGcWeBrx9P8DjzjnBux7PhAoVYmjO/w49kvAYtCdRYpCYv0iU9mBEaA/heKN6TeUuuPNymJEhLxpKCf0kXnoZ7E3bhTt0YijmQTuguuPWcBOor1sfF7Gj6txS78C9njSraSJnvTV9XRXywGpnvwbcy9SaPJt5WDeYH/2MtX8Mf2fhjE3QWPKAAAAAASUVORK5CYII=\" alt=\"Search Stackoverflow\"/>\n            </a>\n            <a href=\"https://www.google.com/search?<?= http_build_query(['q' => $exception->getMessage()]) ?>\" title=\"Search error on Google\" target=\"_blank\">\n                <img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAZCAYAAAAxFw7TAAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAZZJREFUOMvl1T9IlVEYBvCf37301+iPFgYtBhUYQUIQiGaEgVCDELg11l5jo2MNgTg5NYiTNN2GIKJIA0MwGlIMFQoirAjRoUiq5XxwOHz33rrq1APf8pznPO/7nu897+G/Q1ONtYPoxVkcxk58wxJe4g1+/41hK25gAKUaAecxjFcxmW44h1F0IqtTXSuuBN1MTpYjQRfu18mqCF+KSj6GMTQn4qd4iHdYx1FcxHXswxAqRVHuhbTz7zku1MjqCLqr/eWTGE/4W3jRSNtk6Eu4x42a5YbnE25iM41dRlvCva2irRRoU6xk2B8R6/ixiQQPZViNiOZwxRrFRhmf0BKRHZgtEE+E3ouxG4Nxk5cxjdMRea2K4YMC7lJiuJDhSSLqr9a0CXbgZsJNlfAVJ9AeLfRgEe9rmN0Noy3HGobyQTCHq0GYb+jH8SD8jo1wly+HO3wmCTKC2aYtmDYwidv4FW/+gNeh3F3/YPYMd/CzaMB+xCPswak6Q/ZzqGg4HEfdN+VAeFM6w5uyN5znchj703lW24o/8/BJk5VnLk0AAAAASUVORK5CYII=\" alt=\"Search Google\"/>\n            </a>\n        <?php if ($exception instanceof \\yii\\base\\ErrorException): ?>\n            <img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEwAAABACAMAAACHi2FiAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDE0IDc5LjE1MTQ4MSwgMjAxMy8wMy8xMy0xMjowOToxNSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6Mjk0MTdEMEI1QjhGMTFFM0I3QzE5ODkzMUQwNUQyMzYiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6Mjk0MTdEMEE1QjhGMTFFM0I3QzE5ODkzMUQwNUQyMzYiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NThCMjFBODBDNTUwMTFFMkE0QzFFREYxQTMyMDUzRTEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NThCMjFBODFDNTUwMTFFMkE0QzFFREYxQTMyMDUzRTEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz6hNq0vAAACylBMVEXz8/Plc3P////menry8vLtvLzz8fHjqqrkq6vcjIzos7P7+/vlra39/f3+/v7nsLDy7u7qtrbrubn39/fw6urturrlvLzv7+/pzs7mrq7eiIjnhYXmv7/lycnjsbHv0tLgiIjtv7/x8PD4+Pjy7e3twcHtxMTpsbHjs7Py5+fv4eHvycnr0tLvzs7p1dXu1tbptrbptLTwzs7w0NDnj4/t5eXlj4/bkpLv5eXokpLv2Njv2trx4eHv7e3nycnt3Nzsycnci4vehobg4ODx2dnira3mkJDls7Pw7u7w7e3o09Pw5eXy5eXr2dnvzMzz6enkjIznwcHjjY3vy8v19fXt09Pny8vu6urrzs7lwcHpubnq3d3n0dHl5eXv0NDlzMzsz8/e3t7bmZnqvLzkx8fiiors4uLy4+Pqs7PolpbrxMTntrbvvb3ktrbbj4/lzs7jp6fdq6vlv7/rtLTp4eHt6urjxsby3t7p3Nzp6enqycnd3d3p0NDfwcHnzs7uv7/68vLw5+fnxMTx4+Pq4+Pq3t7grq7jkJDw0tLtzMzppKTuwsLm5ubkxMToxMTYqqrjurrei4vjqKjpwsLejo7XoKDt3t7w1NT56+vlpaXv3NznxsbckJDY1tbpxsblsbHp3d3blZXx3t748PDrzMzowcHrurrq1tba2trs7Ozu3t7vxMTjrq7xw8PusrLcoaHd1NTgpaXZuLj17e3bqan9+PjgkJDa0tL78PDq1dXutLTt1dXms7PspaXuwcHfmZncqKjj4+PalpbjwsLozMzktLTv4+PYzc3jl5ft5+fv5+fq6urjxcXfjIzt2dngk5ProqLdwMDq0NDkurrllZXdlZXooqLkubnmtLTkrq7t0dHoy8vVtrbr6enx29vipKT89/fz6+vi3NzYnp7Y09Pu6en01NTe1tbXy8vnl5fUrq7o5ubn29v8+vq7vBJbAAAEwklEQVR42syYVWMbRxCAj09sWYql2AI7ZohjxxRz7LimxIEGG26aNNRAG2qo3KTMzMzMzMzMzPwfuijtrU53jpSHzIt2duc+rWZnZ+YkCIeCNMh5ak3DQYLlikDkLCGHNaIPTYawHCUrVrksl8PPaSKCZcVSwYYQrYeBVc3RMmGVIYRc9nxAxLBOMDkjV1QzgY3xiEaR1RG02bJMaGNFc5FrM4ApN5jDPHdnsrWPTVn6hIyO03UQWXX0+SlH/fLyFKocbvXIiIrFMHkSmMibSFHXrejb8+e1BOaygrWTI/KnXEYin/WFg8G5dz14ETEstYAdSZ6Zxp6jzMBOULySJLm7jiN6nQVsPbGpZuZKGVbRP24JSsm7NMxAOjremA8S6iJi80xiaQ252ViOLvYhmHsqG7esUwoDgRnc+d9K9HFyoFNYfF5lby+BdWCY93wWxqSjQuBgeRwe55D19Ujzq3B8NfQb3Um9F+/sKgaWb2QBUf2C1higvnapNwvCGpwh1p4GrIYdeGFWI9yar2YWA3PxLFHMLQ8YLnDeMrqydQTYPUuUlg0/+q58uolo/atisfmTE7Ae0Vby24FdfUJtGmhKfCN/mwbtWHIUnYTZ0iAfWs0OG1gcJlpTlvhUSqBOt6Z5umAxMV9zjT6JInE0A4v3PWk8MCc1iVrBcJKJwmHltgt1bnViFQ973bBeUFDAqjnorihxUY+BQN+OXeLUt+xwotHtPGwlQ5r07a93vnFBL0NbhqvTic3MFdmrkM0O+jmWlkzIRY+u2FUshJc+cRPzSwzGGNYKNxsS9VAxh/KflWS9eK7g9knekvAflyad3KFpXCXwoAkt2s1y1DxZZg/98aVBfI+94Y+K2EOTQdpiYMeYZMJS/uT2K5gFssI7H/BRgJ/Z/Vql03GEWcnnYa+UEJbkEx7iYYmkZd5MpcCWuClMCh7LL9o0ZCmwaxjYxdnCvmRgbx4ojL++nyZ9VjjbHKao6jk1prShbTs3r4pvStjP3kpP85a/khRnKLbkxtPDAgquKpiTLTa4M7nBlg2YdvLZTNr4DZt9j4pOnV1P8FXyyYEPf39MevK+dczPuw0bXbEWNItC1UrSz848M003O97gm7a2NkM+a8VGX+ObgDvQnxoC6brZYqt8tojevfzR9Z/VlqWJ9jl38OnctGfRci1rQIBrkGjPONb8dcG6OEVw56BV4OT592U4EUfNXxdkm1IXqRXUSCSCxg/seanv4aK0PWOtbFvRI+0VZPTez0rJ3F2o0ZM7TWA9tIo5NzkNCOckqldUUNgLYa/kC+4gzcg9M1NoUbw1x1Rl8cZk3tE37lNOxYVNj7218F4M/iEIE2c3NepI3VsIHfRqONxOi/sXYahOgKr+Khx2fQKnz3gEwhIRV20SGoAWX2Bs9q7H6mpPIgSmo/nP/5N8DS3IC07dc4pZoIUW0pJFG6fxRF8QouH0Nl5Yd/9zA2gwX7F9F76EwC5PaSHquSMetn+p2Z3GG5p6gImXTSBjuHl/ymvYPHvYZI/uBK7N96csOMT+f5f3M11D6WheUqGYzO/tBrPfQMzy7zaDAHT5s/1/BHrBMQTaQ0d8KOs/W+bRRmNYyZoltG6pNG00DmH5X4ABADmwra2S/uwNAAAAAElFTkSuQmCC\" class=\"erroricon\" alt=\"Error\"/>\n            </div>\n            <h1>\n                <span><?= $handler->htmlEncode($exception->getName()) ?></span>\n                &ndash; <?= $handler->addTypeLinks(get_class($exception)) ?>\n            </h1>\n        <?php else: ?>\n            <img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAA6CAMAAAA3Dq9LAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDE0IDc5LjE1MTQ4MSwgMjAxMy8wMy8xMy0xMjowOToxNSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QUFDNzhCNUM1QjhDMTFFM0I3QzE5ODkzMUQwNUQyMzYiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QUFDNzhCNUI1QjhDMTFFM0I3QzE5ODkzMUQwNUQyMzYiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NzBFREE5RDFDNDdEMTFFMkJGNUU4MkNCQUY4MUM3RUEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NzBFREE5RDJDNDdEMTFFMkJGNUU4MkNCQUY4MUM3RUEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz5g4xOFAAADAFBMVEUAAAD////lc3PahobplZXhl5felpbdlpbqoKDln5/moqLgn5/ppqbopaXmpqborKznrKziqKjlq6vnrq7psLDnr6/irKzpsrLqtLTps7Pst7ferKzqtrbntLTturrtvLzqvLzourrhtbXrwMDov7/sw8PrwsLowMDhvb3tysrqycntz8/qzMzqzs7u1NTr09Ps19fo1NTv29vo1dXx4ODx4uLw4uLx5OTq39/x5+fx6Ojw5+fo39/x6enw6Ojp4eHz7Ozy6+vt5+fq5OTz7u7y7e3s5+fz8PDy7+/x7u7z8fHy8PDt6+vz8vLz8/Py8vLw8PDv7+/t7e0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACxvAGHAAAB9klEQVR42qTXfVvBUBgG8Od6qGwx75WoVCKJSlR6pZJSQt//w8S2w86cPXaO+z9n8xu7bs8Z+KXSDgWDoTZ5ClAHm2GcJNxUBfoRNBPpKwInaKesBnQ1BmjvSkAeZ8mrAC1tDmgtBSCFjmTkgQZyuZUGDB6IygJ1dKUuCRhuwJADKriQmgzQ0xcBXQYooiBl/0BXEwHiQgNV4oAd++WxX4CV+PRvZObnyP4IHZ8AK/E1W7giCg1EiS/ZygVRaCA6VGUrVaLQQJR4EcCGD8CgAGM54CjxDDgnCg1Eic/YYokoNBAlLrHFgmOxQgMdZ4kLIkDvkUAelwFYpICWJgT2uN9UhwC4SYyHbDmN3hMaiEmcFgP8JgHEIJwBMX495QXUXBMk5gFwhQZiEjNgECMmNBCTOPZtHXhCYpMAahLv7E+zu43EJgH0JPZKZRHoaDLAvNAzICM4zTWVhYUGYYmtxB/H06E8fokLNomuC0gJrpJgV0kIDuZ5oCH6okkGJEX7VIsDDGmAFRqEJfYD4I0D0FWA6ByooApgFRo8HidM4NN+ZvYADAZ4ljh3YCbndbxmAXIldj/1gGsSy6U8BQaaOqAPJsADrpD7CfC1tgLwPL0HW+rv3zRv4uu66vs37qwefGQDSsm+sSoPR2OFjIZL/zf6yb8AAwCmB2Y7BrVl9wAAAABJRU5ErkJggg==\" class=\"erroricon\" alt=\"Exception\"/>\n            </div>\n            <h1><?php\n                if ($exception instanceof \\yii\\web\\HttpException) {\n                    echo '<span>' . $handler->createHttpStatusLink($exception->statusCode, $handler->htmlEncode($exception->getName())) . '</span>';\n                    echo ' &ndash; ' . $handler->addTypeLinks(get_class($exception));\n                } else {\n                    $name = $handler->getExceptionName($exception);\n                    if ($name !== null) {\n                        echo '<span>' . $handler->htmlEncode($name) . '</span>';\n                        echo ' &ndash; ' . $handler->addTypeLinks(get_class($exception));\n                    } else {\n                        echo '<span>' . $handler->htmlEncode(get_class($exception)) . '</span>';\n                    }\n                }\n            ?></h1>\n        <?php endif; ?>\n        <h2><?= nl2br($handler->htmlEncode($exception->getMessage())) ?></h2>\n\n        <?php if ($exception instanceof \\yii\\db\\Exception && !empty($exception->errorInfo)): ?>\n            <pre>Error Info: <?= $handler->htmlEncode(print_r($exception->errorInfo, true)) ?></pre>\n        <?php endif ?>\n\n        <?= $handler->renderPreviousExceptions($exception) ?>\n    </div>\n\n    <div class=\"call-stack\">\n        <?= $handler->renderCallStack($exception) ?>\n    </div>\n\n    <div class=\"request\">\n        <div class=\"code\">\n            <?= $handler->renderRequest() ?>\n        </div>\n    </div>\n\n    <div class=\"footer\">\n        <img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAS4AAADeCAIAAAAMx3q5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkE5NkEyOTAzQzQ3RDExRTI4NjIzOEE2RkU5QTc4RTU0IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkE5NkEyOTA0QzQ3RDExRTI4NjIzOEE2RkU5QTc4RTU0Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QTk2QTI5MDFDNDdEMTFFMjg2MjM4QTZGRTlBNzhFNTQiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QTk2QTI5MDJDNDdEMTFFMjg2MjM4QTZGRTlBNzhFNTQiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4DYCAjAAAx4UlEQVR42ux9aZvjuJEmgqTOvCrrrmr3jO3xjD37/3/FftiP3me965nxbl/VVdWVl1IXyVhSJ0EEgABJSZQEtLoyUwJBisSLN24AIgohlv+W2vLN6f/47/kfkP0nlr9s/6V/gfJA8hsAxJvEgUQH5Q/QdgD+UeQ75BexXk/2I+oGly/h+g0EkWA0sJ6l9c0wee6m/xjPv9hHaOpKTMMhsq8Dqwyf/9OLrt5d/EX3ZM3POjLfSjGbCd+cZkM8S+5+gcfPwdUbDiCX9/l4AWlaxJP7cfw1X9HQAkAor4VYDaVFtigfiNtbjLpR1z0AyVPDaiDy2hbvTpPHBGchdJc3ofRY1XeKLbDc6Nk0O3b5EuRrex2Lv1Vmgy3HSOOU+AeAPIp6B1bPDrT3E/hHgYafDdgAamT5I0yT5P6X5Me/pnc/YxpXm9BHjUMUyd3kv2zAAWF4kPrnw4QlVB7TMsVM7W7yY7WbGZzSzGhdS5M0A+RPf03v7YDERTsZYfVu8o9UJAToOC8FpcVlvAIglSFhQw1MQAK1gpDXM5p/wfUXVx+o4REHDsSvuxNg1LjMfQAsKiKTD4ufgMKM0AQfqsOWnhVoZtQGkI+/WsF2RIA0XGcmmk6Su7NdgZ9mXyocFXgC3B9Dfvsx+fmv6fM9B5BHLZo+TH+AClxIiq5QXEm3XASyxsOnRygvpFB8GY6kTlS8nm2Hx9mnCg83sHc1kIkkQQiTlqiyEJgUS4bGqOdDIZsDgKcrWPlQpxmSCipxyOIVz9Ov/5l+/jvOxma8HZ28urna0fzXBKfnvOrG6XSaPLquqgF3eLNqXV1Brteg4mcEDpsYhjtrp0/Jp/+Fdz9hmlgBeVyiaYKzp9nPlR+mhTAJniSp0vLQCIYsTGXtCEUuJrRHab6MjDIqeQMtUITBkJ6Oxe9h4EPZviwqA0dPoTqxwY0PWVfF40OD4i93SJ8+J7/8T3z+dgL0uLnCyjjcwVp8iHHWgz3Hd67rqY0Vw5ASR2t/T3DssMNnyceh8dqqXXCuQP6/9MvfMZ4dCz0armSWPo3j3ypriE6EKUxUCWbfGT2yk4mVWO2l4xHj0fyrm9mm4eUE7JqXXYIAmwpnZemafAgWHKq2dp34Y5sDS3l1lP76N3z8ZCbA1tLjQSgRGu/Y5PqfjzSef3Na0aJ937D6fNiYTALO38HOh8C6VOXUudL48AnGD8Ht99jpG2IyzBEbB6TEefo0T56g4hTB1f+CDmUxPxjUDrn9uxiBg9arKYywJkbqmy/XZU1ozsZyw3xqQZNIO8gMoS26VS/MHKOx64k+Hyef/4ajL1Z6bCEljue/NbpyO0iwoNfTTQul+8rtdHyKyTh2cK7aoQiX18R0N7jmtZdsM24RrggwG4QAbBhy8luYjS6ksQw0diRdcJ9O3i4ckt7/lH79D5HM2yasGs6Y4mycfBUVnImEw5DUDFlAADsAy2qkaSZSegiw9MZVm8aPhrtXeic42HLKUaaO0RbXSJs+pZ//JiYPLbflFHyJn9sxaZx40+VkZjRqJtE4/tYoK4YhtVZo74+uV1n6MH1pOx/S4swu+LD0KHXeLsPD5gYPFO9eHlGefvsHPvxs9j3uDY3mE03irzuFX+Xo0/orMDier9g/TmcJzpqD4tK16NtBaGf0Jf36d6uwelhKnMS/oUigjhOD1w+EKeCXRYfUSmqNC5fZxejnUEKeJwUZ1fzs3KNtrCI2Jweq0mpGRXszAuisfAh2PtQMa8ul0V0bkHpv3ohD5pP0y/8W04cDqo7mwafJvV80tTcnfmDeRrszAy6u/A09cEuT9Ld/wNU7cflW6POM9+nn2MyqFGcLKDqfVz6ACi7XLdyojeBCxY9Bng43C2WhL5ivRu/noBOR136OokvD3BhQDCNt9FqBKukuVu+82dYMBh4FniAPPOcEcSZjyJ7u7ODkCwHdXaL64dMnEY/h5nsUQRvQ6CmR05J0tpDeQ68rnpbqOHlIf/sPEU/2qTqaB5ylT03ZZsjkKHOii8EdYlAjNQlTW9XAoD1KfaU0K+2ZJjyXBktXhG6vrCwBn0A0zGDNCa4f3aa7n6CpPcWKYtednXp8lPJs0QxNNoe16rh3NOrGz9b7aexZ0dLmyTOnGw+KnZ7pU/cAsr00l8uqH+ENTr3q3Ys0Tb/8HzG+M1hr9mNWXUa61Xk5EaaWJ4U9YMDMkKSlrZJllThgnjYIxdxyA25z0bzes6cy7aVkRnu7n5UVDQQVgQhWfVJLhoQNP334AUe/GlDXCBr3Ip3ufLndI01AZVaMMjHJ+sxydbFOZGazQeF1SJgZkQgufMaKGXCz0HBx8vSrSOZw/Z3OWrMLK05xtmSsWHMWy8UJyV/NByFlGi29tVrMC+HmtIkVS2dAajDN1RQsq3l8RrFnzPPyB5TAqXBFf0gTBxiyhJi3H3TTFoxGR8vZgO27ZMU4QhUSVTsBMC+KVk2pDjj+hg8/7pobdS3G8aHpDpiPTujz7rVjATfThlxxi51LWRrkM+JVsO728hziJGHdL2j0IYC7VsqjKfpDNz50+foAO5mKCzSKeAK3f9A5OXbk4ZivpFPzyFj5S4NytJUqgaxlXEimkinO4oSU3Y9IkCd5CEg/Vto92mvhcqNtgv5wV6vfAeOwYRfQcP+OdarnLisgx2P89p8C02a50ZxMEKdjMKZTwD4fEFPZ3+LU8YYAOChPCjfO03FjUCzH3ICmtKk1JttM//zOTrYiXb22ynFthog21Z7HqmSukUgtctn2PBhP8K55NBqBmjKQAeRrx+s2cCxwsPuFoXCvkp1BsdrlQY3VsXL9m8bMrU3PnaYL0ON8r2ic1zCfOuATKoWBK6548u4BcQL9nS7gl5vruP4xYxhR2QLqxTVh7CAyfdlmSWOuLwjHbEYnPrSjwp0PVWoE4ZbQbA8u16ZpHQqNTS9c1Qlzp9pD40Kztvgic/epwMeFH0Pbm6Q6341TkV+u22ZdLtOZIVVqqXEvrD5gMZQX9AI+N06TJ6ta7pDFD8Mr0zLCKiJe5E89H1TIgXIXTbUFxU1xbdaEZlv9eKvvnvFgdVNzO3kzNN7/V000Om294lv95gDF4PrW36+jafMxPvxfsUt/IzT3ctIqDddhHFNT1QbIyDUONwpHbmwOitAfQhhqItGqxrhVsJpalTdhiTU31S91ivMm+JCvMdjmjpoCQKqWOmtr9sH8GZ9+2hEaN/uW+cZscTplQZGpLsLVbXOmwEoSpvNR0Gi3pi6VGf5W+6omdztCY5JONH4Kw4tZKcONJ4W7vE+zGS8EqzI3pjhvjBXz3lcv6mqJ5uh4p5hve8Q58AQG/W038q3Eh1YZzn46E2Wro9FMqCZeTe7E5DdxoMi4ystVK/fC2O3p3KqD1yuuYcu7b5ZYBLOaziH4EOoO6nROfPoFIBS9G37UOAef1aogoul4qc420B8TJFnogOr1ATH29hPYFiYv9JYix4n4ODkyTpQuu9ra5saKEEZwfbsrLdHwmdN+lrtfGCuF0dhwaFMLNXKGvtJxgUjx6UeRTBvkxii42BVvHCIQEhroAjW/QWBQDmnv4s0rEw4bID1w/OIu3aDRiB9wl7mg+jShJVL2pefuDR4ady2y6jVDIIxR2nBCrRrpmO5DaS3a/fvMeqMJjWgjS+fq4MHVrQgid9IDwdeM7DgE13lviWG1xu7U2nXDVgHEVhhSQ7FMBXXdDdPchLMIHDWg8SDOQzBpxs5LMVTJGbJ47bnlF6A6N1Yp1E8bb5wojF84nDkF1aLqfEmoctrEToUrqL8YKZceT5YG1WYuEMJdAJJWTvSb/doYkrsqltgNKDuZnRvZooq65FWC4st34swb1OTJ3ShFnHjK+aMYf2lECo1gsAchlq2CawHZ4MpYgRsrsiLXu9gfQv/CQYQDHu/ZnfXOkxEMqgCnkLluzeQalqzhb1oXm76ivOEu2dx2y/V4/FnMn+qjMYDuYRY9fciNExphj0LMrgTU/LDbt8K3o225mJrOa6IxgA64u/mhajmwsgWHWpX08od8aipWTiumsvV3VXx2+r4VoQiZuhiEDIONtjwImdZlUyl5tAmOBVeLD0szTkGpZ/tVAPTXpteJDL57gxXSHMCiXltuwvnBbMKxthD6flFrsAVWiZSWUcNI8mr4dnQtnornT7V0xWBQLfpbpkhntlRWofKq52bLMQXKSYUS+JFx5DreC692woqS8YZZUMO1ggsnxs0a813b/ShHt3GMt0CrJwb+V6dCSaazqIVWvxsQdsiMGmf3dZTGTFfMZFS/pu2KFTX2FIoYOz1PjEevNI5qKY0RXNawxZTNU+uVx02xlAhKqu2t32TQzc8BuloaumxjYld4iKpAkV+oL3j9UU+JYHHrO+VyuRitHHOgRHnvR372E6kVW/RD6+PX68O2cejrA2PRqkxdHP24+tV9s/FO1fC3c2udYGBFWVDnBDkxXr/eybXvLRCx0XLdDXSGpu4VN34Qk4mYfOGgkZph140mDYNa+amoUjIVSEM8QA1uFOVQmm16FIIS1FZSN4JqrMg33iyI8YNdSxR71RLZJwIBNQiZcwFm8ybpNuQQb2n2GQQ/+7RdIDCDYjKtgEaAMAwGos0NWnEVHcZdCup+01xjfO18O2D3984aAQui7Q32N8NwtPJtuKKxG7yo+RVtScPblcWJIcvB5Q1xoy512Oxp5IRDuBTU0BHjqw8cxUW4WjXtc1SfmAzu05auQMWjKX62l3DhQyEcUts5xc8MKmUab8RUJzR2g9u9rT8lCDU4KtROTjXHxEVBDSjyjTc5MTKCb2BvzwqaOi3U6gXVNThTQY0Kdi5gZSvg9JuIn4to5KRNZTJqJ7gBTatWtp/DkMJWeNcQpa2hO4eIV6NeBRQUh9Wh6EiMH6XgG992Ef9tgFND8ZQ4/kUIqfw+J22qG75whBj3chlrCAijvOqUMwHcWHM3bgxEGDCyWIIKwCN6hlGYoZGzbPP3zLBEZoN9gmq9C2BxYNhtRVYc0hF/hj1uLZYew7QGlm3GZN3ZqFTpvCimsm0S14HouquCh926aKfnRvn+sLZ+Cpo6eXD7DnpD4ZuLjFGbD6uVjSfG3P6aiamJ866JUXDFNMsocUQsnjQuKZi9DLYcg1pOiqnA2kZTSWvcfoClzp3wsgEo8r0aWQvf/97BpVFZ56FKHFSPF4BqYNCY2PRGN7bfwrq/pm2rKUPgWzEuRQ+CePq1JKZaWy+sGHcFnFoLlo+hTsWcPdTa6fD8PY2xYn7NvWHw8qNnu6Nn63SWzB/dZCLohtUyiQmqBKZ4TS1QPL1R4UaLh8MSE1e6rPLFc0JtsnfsUHQixuDFW+h0tct98Zs2YhUUNbRE+jZar83Mh0R8dkU+BP5+HuTUJRRC0GRuSde2uLxkfo+2Erpl403w0q9ihKgIvTDo7ZAVdWjM7Tcf/sU/gKNuwcLXn0x/czXe1N8ho7QmAHDscg7cCC7caFc0LEpJ/rPDjkYK6gBPL6Z+qGVpcNk9qn5UeXMGOah6kIt+yNUMLfKBZH0o3/DcAJim0zR2EFMXDka/wZGyQoWXTULRFZ/hq4/QHdaY04dD0/677f6CwfFDWDsS49m9k/1mYUe18yLI1Zetpe1WmVMADJNUmRvBWnHIFE/ATGiWizvIo3UbhyI4Rk6HH/4oghAYG7FUYZ7mtMTtYGD1e+lygrVhayYt0RYpzuJDodUMTWqhuaC4hMY0mX1zklF9MrGsKHY5TsUluOolSRmeZacXvv19FX6A3XcQJA4bH7T+YPp1Z8eUu8n6SeIR2nYjk4gRrlxukxtPummPtnzT8rKtVQWZ1lRidJIStSFrNYFnsqZevoCXtkhx6z6N+9ASnePFLQo7J9rbPr4Gh7a9yIweANuVbJl1Gy+SzO74t7IbvmpoLdiHdM7vB+xIPfluOFQ5aNKvSBD0y49w+cK+xnOpqSorQINj7VFtbcr9DJzN6kufb6G4sN+MuPMJumHQ122pCKzVyJLAo3ggmWualUidrak6ati0XvhiV1B0Jcb8BG/+WWwC4qDO1Kyc5gv2aC/rw2Sa05nVIik2q8SH8uykp0eFMgXlt+JcY+Tab8wORpdgP+amDTyMG5kNak9Q9ftFwSBw2csgcJ/YbmhceBr/TbvjTeNcAtxHvxMtkRO7ZZMNnPmw8u6R+tC5AEvAS/nxNxFc0zxn2SdKi0be7iG2XXVZu7SbZHhebOq2DcKXHKRs3gn2AZAwDD/+6+lnUUHLTtro9STzO8SYt1iHCzRaUeZg2oGqmg00ekOcNEYn6bQiFCuIqdAbVkGja9nvhqeji2PdSmaG6Da300GR0U3bgPO3YSXKrqo5ipDM75k3Lk8mVqtZGLW09pc3cWqdYBAG3Z1DsTIac73Rt6Ml+DR3bLACU6MgY8WwSCZyaVO6jDLY7OXV/Bw67z+jmy2LSm826ofOEbnBPh9wcPFii0apcDjwNzAGvhm7Qn4wML2CNhcC021Qhw+ZwQZCY8RkOPoJMXXGDUzVZjCKusYTaMGqZG2kdGrOrGg48M0alBNcvfLceLwtTadMj38neGnb6ZviScW0w5DyWdwo91tmGxtWSOD7NtRPO+Glq3QqdhFtw0Ljuz+y9UarEqgPmK+5AIJj2Ui+lgiu37seH1YiT9DsHc/0+IfQB9E9zwVrEFYJi69dB7UaGi9ehO//1Vemare6iDpiTOMnlowKl2XPBZj33SF2QTRvYmHjRt2iBNb6VGY2JK9zNbch7LpLpzvUFa1ozG2qORoj5ryQngrP9avnJ6tqyivqR0anE3HnhsACcKjXVoHiBEMzBGMRX5IY5w9cGfX8Wje8cfLsNwlFbRoxB42/+2+CTKeq4y6DSnmDVbtBzVM3fT1u/cB9sLwiY8wJhVvs+tYT5BYYpo3OBWgq8ZDrjZP7UpH60WhNLXfn3OUKttMmWbE6GsMofP8nGN7s0EbWlNOq4UFcsp90RlrOfo/GbbAsmDARI8vHGMFNxeW0sZsO+1z6OsGAmY3R8E5STqgzoDF484fg5n3LNac9QZcZHgJ7zUvQEKNdYwzhyrxCSiRJhz/oFyMjN8pbLdb44i5L+SB6U3mKNVrxrZIJZ9kHXrwP3v5BNuRU2R2qGQ61Rvxraar0iF0iSuwbZjpOGgDeV6RqWzHOx9EYcxlV9A8gfZTvQe1IYw5mc4PNTSugWBeNg5vw419Ed+D2xPYUNAXMZ+4wGjOMG2rNQYfv5XgiJjFGwQ17DVH2V6zNjcJhCyKWxih3kOyo/fCWNNgwaybuKdqGi8aoG374c9uF1f0Kxtx0gJrqTiXAc4gxZOf1ExsP1I8nZ65LtiwqlnTaeVvn8GBvqGMqkzkgb96H7/6UwZISumy3HMDEneBegrX5iuPAKuKqdXtpN4rT3oQyzyjqGeh71ybGADqZjNrkzj2tjBzvhy9D6LYLimY0MulR9C6D9/8GV6/FCbRdi9C7GTxlmlKTZ56M6lLlTRtyzfFwuOviLndZd1KdwcZhA+/9S6RcYTWMgtvvwrc5PVbcIxUagQ2Rqq8tXQPWR8bjOb43H2xCgWSfMWcVWOuHS51x8UqTaZpM2DJqG9T65ls3uIxqb4S+Q12xJhrX9HgRfPj3TGT1UXKtbUn8YJdRoVdaHhhbJOuC4Az2FO2izYiwWqwtBmDrr3UQva8zz/dhtmkEjXm7fhe8/7MYvmxS8CPSlBqrM6Y9H1NLZJY5MmeZlb8bUGogEfuZK4HGuL/SnM2I0ZrgH8LNqa5EGSXyK7uZKiTuXFqojcZVz7ATvPw+ePOnTI30RLTrho7rkNWUmsmoyg5RwKmXoYYLlQyths0PHRV3EJqC4ubQn0YoUeypto3eWuOExpW8+uZfIANkZLNWlTjAMUdWq8JZhZ5abn3dOxUvXtL8GHXHgQhxLwuSSEl+SfxsJsZMRs0e3umtWZ2GKFHsM4u/pllVklczQL7/C9x+L8IzzYjbPSuWi6JuXnqN8dkqoxKkKPOYpSAPsAN02cqEozu2TMXD6F1T9zza5wPOQISIrh/pOsPwNnuJ2QgfPuFsVFejg6pvNfDY9T4za50R/XYdesGKiPUpxYctHoVL6CZkUHyKOtdGKA5jPKkFqxve6DaKctqVdN+s2KDqKHFp9wJe/zF4/UcYvKhtWWng++kx08jqwO9Rq9T60jCjYUIQKmMuzTfGzKkgz+vvlCVxym3ILPFTJfiiqh2VvJSLqMk9toMDzFYjGqsD8vb74N2f4eotOEutzWY5EI/XvHU0V7EkLBo8IY5puSiYUhCrBMgltiTGEC73/Dh2tyz3wpe6AjbVspQicYi2vNZGhFVptLALV+/E1TuYPOD4G04euA+kpVU4wQGHrAE0fyvpCymH0xXEpzhPk2kQ9vTEeCXwrmj9WRVdLddezZ4oMQOW3xnLPA1b3RbV60P9fUHDgqSeCFYH5D8BwmHno+sUbSMUOaqjAavmb5sf1b+G7JUmOL4T428iHrsBr2KhAGgSdpbAmkpXzzsq5ZxJo94mybMJimKQbwKQb78BtOSL9B+Mm4c1b77L8dAPX+uqZlRO3D0kFK0EWAGQ0lFBCBevRPZKZjh9zDE5n+xM0Gl0A8a9ES6Vzpfa4bf9W342kCTjCG8AAt2jCeAywYcSNVG/ceFR7EaSXV2YEtze6bukCDPBGR1+Ttjw5iqvEiSZCa7DV2KYY1LMnnH6IGYjgYk47QYVF4AUay0VGTFG0aWeGIeJeOB/A6zbDZbyb4NovIg+Nk6JrYDi7uiRxKQYdFe21vkoVybnzxRV1lPogP8nsEawysZAD0wqlgY+XN2oDC8g116UO6PxWpN4ZIBiJqjMcTdS5l5aJ7hwStXngzNqz5fk0GNlQJYxmd/UC+hcLObOTEwfcT7K/j1FMnRryE6P0o6AidF4EwbQQ5yiHY3ajXTWP1FrfGFo+uhOltkBF53v66Ou7VBkiqM1AUlgMqPK4SsQi52rl5icPYpkXn7ue4UFVBoGGHRpORoxWM1PFyZUZNSx0XhzmYhpLYnzQJOzH711yg92AmfroMgEW31AEpjMWu8KeldCvF9olU95EM/8JKiSr+nRIqvTwrA03lyTxps8ETWDIn5dU57ix9jYb9DJEgMbngQkLxg5yxjq+TaAbj98syNKbCkU9wzI0n3cUuXgJQwWaVmzjCqfF1Q5q0uEjRc1BiP3maLb6MCgJGPFQgc2CBWNMZlE0VDTtbfY8u3IzGbD6INT2W9XcEYt//4c8ylBbs1SZfcKulfi4l0utWYkmWFy/ixOsS0T8xuRtBd21KFWRoVBik+1HpMg7EpYdxht6wY3BmtNhYjT44OiE/U1RZJ6quyI8KXov8wdIUuejJ9FvvvnXioWg42U9N4Lq9V02RZujGbKlqTpPMVERyMBXNaE4n5nYDjs/G53ounRQLEaIBvEJDFmNsMynuwuSrbEkxyQ86ccnLuoxl0x5LvK6pAsYpIrC6Vld388CjS5GoEYymwma2osO2oTZNcO0fTIoFiB93aKye2wUT9/LakyHheokpGcsdOi1W5BqrBQFEOsBWbpwCSZdjq6HhFAB3He/inXCS57+h1pmqLE44NiNUF0F5ikqbJzmb/yaThZwPJeJNMd3ofGSHERzL3NDnIwPRkoNHcwpvMg6OiIMYF7E3nZ6XBrNbWSoGuU6fLcgVE0bRacRwnFygDbEyZzrbKfv3q3C63yaSHBjhq2GTplMTO2jshtp444tLY4fu52NZtJwUDg/X4kzMpj9sN3Bkdig5R43FCsY63ZNSZlrfJGLKdjxpPzTHx9EmncKA7BHSMUbDB0quOvpdCiaySdkncJ80iCYcvnVTe86UevnXB4CjGoByFJGjy7u55okL/E21yTzAA5f4JkXDygcSnV1ANU6XQXCSNpkkzCsE+ri6KDoqXqIojwItqTaHpqUKzPePujykx96t5mL8wtPSPIJdin2sJgtQGgQIl8JjTzoZzBmM5IKK7URXHfzlk0jL4DvdW0WdH0ZKHYICb3Yum5xqXFP0NjBsvsX0x3gTlzWyuKzbeMFYXGpdHacoy98JWTQ1+cduBbGzC5V0tPdJm9ULzLAJnBEuKCmQfq+SFtR8cYrKVTcKNBYU62Wv1MkmlIRYcHueWGvqQDZkuF0B+E7/Z/3rOAYrOYFDsNHsifyUX2yv9YYjIZ5arcLhslnTZKjCkNxQUrBtpvdyA4DqPfuYqmjcir5wXFprhuHwE9RUwmC8E1w+RadnXM3DJ1y3eDyvEA7kxI/EEem6Ra20wA/RRbFNA7CD+47gl1doFvu8Zke6iSin29wDDPcs4ZMlcpm8zbmpt8GG641mHY4OsHkc37ylB0SB3m9OoE1wbvxY6sNR6Kx0SVmwFzQGav7pucIeNHSMdVYCPtGCVJp1w+RFDHMR8YJ5MORKoHOIBhgl/b8PQD6A6N3ovdiaYeijunysZ5cm13DTC6EtEV4hySZ4jvRdVgzsSJEhkhO3p1cdahbgiIrkpt6K4yYm01cxj900FURA/Fo7T0KJjsYHSTvSCdiPgB0meXMnawkE4jefoCSy102zlkyYRp9lLz+kGEC0f/7LBPeRB9qL9tsIfiOVJlaTQM+qLbz+d6OlqQJGtmJyJIuXWO61fZyO2oUTggJcMEnaGIzfTNP+qGt73QWUVsXG/0UDwYVTaIya3gGl5lL5FOIXmA5NFmsIkYWKqTxyVXOk5jEZKd+kIcLI04hMEg/HBwHHooHpIqGxRfy/AOehi8wegVJPeQPgG1CWlGiYsIm2peew5Ky06OOJ12xRXFisNtNIByJuTSHMr/MO9baA5w8wKqp8qGSDK6RXELyROkj7k+WaZEt6XdJasYdMYbdfclEF0dDmuLpjZTTfi70Kgi7o0SPRRPE5OKC+Qye4l0HCQL084i4jQpRtjoi+K4ll00U2iSzikohvoCcBY1rw4u++GbTnjdEhx6KB6B+FqnGrp0bDBIMwbAOEi+xclhLJYp0omageilhKMfLbxYFYfZXVl489+1B4fiIFudni0sXTdyLR5Yfy1YvxWN4fWX+O0U+yuuWu7vsqr1BsVy4LJEarDrQKkDripHFV6Lf9I0Idkvjwun8MbEmlWexbKppp+piG2bIR6Kx4HJOkguvfM4wyQNn5Kru/jlNO3v+evHVAWDhWuxuADQOETNm/wOSyRaTTX7p0QvoB6fSlmtos+m8zTB6RoLicgBOU6Hg+C5F0zZmqGjZVWOlctdGkFPucJOCUb274YVTTkX0T+1x1TjWfF0eNIVwPcT3BLPYgInuGTI21na3cOXTagYvbzODQuHuNqWGyn7DdpJdBB+FwUXLcShZ8Wj50mnElvPc5ylSNLaApDXEc4HwTgCQ0Qrz7gKxT2Epc+1EXC7L4vaCW674W1rn75nxVPgSU7PDBr3q00NcWFEzF9F5sh+ztPOQ3w9Si5Tur6Gy2bp+ksiiTFTF7dXxdYS0dihuF6EcGE11RyQEj0UTweT1j6PU4xT5GhV07T3kLyYpMM1sEC1oGLpIyiVkIPiTxmmkFCWm2CXdW5CGAyj79uMQy+gnpTsath1KwPh4yxdQggKO5mufsUy56UI43SQYXIYPnUgZptHgGNqSan0kYWAaqC1skPfGOcmGWAhN5l+H0DUZhx6VjxWnnQ96ts4TVFrCCkZctaYz4NyMgXyKbnE8l6LGiakvY9lXs2gqLLzjlhxUc7094Fxq+A9Q85D8TRJkvP+JMZxjGUVC8tBn0gSH2YKZPc+fpExJCmprt5BYajdhkrAgEqMC12RVAUJqykW3kQjVw+i78JKiYj7x6eH4qmhUW1fnxNV9lMAKaMRZSAhPCcXT8lV6jZhQL/7YqwKqMxBke1FHITfdYLrlquIHorn0u4naZyuBE77JDcyTIzRY3w9x44ULkeYdQBXL3U0WO8eR8fc0CqiTfkkWzd4ZXVdtAeHHoon3jIQ3k2SLQViGZBY8GwUVEfCz7H8IEUYJZej+BKx1nxNUtpyQ5tqtn9Lrg7Uq76d4HYQfTgiHHoonnj78hzzKIT4XKuDZdojdjJ6zEhy67jfMiGgzWyzBFWq7EewtNzoTKZ8Qozgoh9+bJsq6KF4vu1hmk7miAq9rRvBkIotZ92tPERGj8FTcjlJqkeTI2G5CQ2uC7QsEis+DGAwiL43I83J6OWh6Fu1+Y1b0XSc7Pp0k7Q/ii+U0BwwBAZs8qcSFYorAZWVE0x+vnDl/7PZhdhanvQu/qNHHdk+j+IEl0aVwtyWKgkvtUcQhRi1jcIomWDWb5RjAxZ/ZsJqguEweg7BDfkEFPPZqPIh6dcnm92V30IV0bPiCeJw82Yumsbp3i4mY8WMG+erxA7QkBhgKTVZMaKCqFPuye7KbzMOPRRPsGUY/PYcr93fskpIesuLCqEq/hX8j6vRkNDiMpg9J4NxMqB1OBLAyoISQF9rLzV583MNMcOh1ZXfZhx6AfUEKTETTVPKoV/ObgJjH3nS08nCxT0zFr1naTfDZD+cBKs3NLMcVv+nmITQwAwc2rKBRStNpp4VTxmH95N0PE/LnIYrTpPYTqXLlXFVYyRRD1VIKnt3nnSe42FqqoWzhWiq7KwYwBA3fLj5zcSH2LdlA4u2mkw9FE+2zRL8OoppsVDjGKfzAJFV0IIOIl9kIa/RKKNv9YKCkllXoc1wWC2kpoU86aF4IpSYUeGvT7HxYCoym65DQWzMhFyILlI60nA0v0gkJ8fWZrO5BhWKkE9Ia5w3bvjwZHDooXg6oum353i6TA1WAtYEZYMxcmPBlkPCVLLl0NyYR5DHwwRNE0z1Z+SWGzYfdoIXJ4NDD8UTweFoli5iTWm4seLAUSusImO7Q1TIdEF6SzSG2dyXCXnFkKghVRsf5iGmGQ6rIaq19hsPxaNvcZqJpvOy4V/+VRVKN34O1dVhVCRVeVVGmXxEHj4eD5I00BNjLAuo9lSpDIf98KMVUS13XXgoniAl/vwwS1I2AXIYsmpfpBRNXKAxTcOCxigbfyQBtWMgbjxdHHooHj0Ov4wyFZHwg0ukJ3OjTGZKuLco+Q9MEeQk4W4JczNEjsb+Go0yK4qUgffVeN089em7k8Shh+Lxq4jPyZF8kRyNqhWHX/F1icPjVQU9FE+2ZWT46XGOatiaZINRUoEp4+qmBz9KDpWsYqQsqxInIzzPy2gseflDuBAyfxf5sDIOjwKfHopHSYkpigyHSYrH9o1gMu8VEz8QTfvNYBN8eCw86aF4lKLpr0/zQu7FmpaKpKZEjaFKj0i6+1HVMzVRcubyHEiW58hY8XnWx0IhRqFRP5djd84Dhx6KR4nDu3HyMEmO99tlaJzErK1yMj4cngcOPRSPr43n6a+P8y1ZKblNhJ1Tdhiq+1JgqQgj6TAkdrTYGlephYOqELV+c55EGzTGMjEGK3XxjPjQQ/H4KDFO8ce7GbmjWVnRovzzxK6ghl0NjTEvKvA4ptAi4GdJJwOkrmcIg0H4/nxwKHy+4nGZan74NksK2IMiGkGulLH6tTAn1wcCKIDCbaWNctZiYVeNUnkOUPazQQQQcsUOsanGUb42QBjPe0GQ7/Em5D2AQ+hfRL83bwx8Yjj0rHhMKuKnx/nUUCZDH0SqpSbujtk2iuMcq+HY51k/kd+PguFF9Idzw6GH4tG0+0lyv9z4QhvvqfME6uLTJBRzIsiRqpTKUVZ1oy0TOIpiagakfvjuDHHooXgclJh78x/mZSShYnHRDUxyY9nqg6gMr6Y1opq0qElStpUhl4w7TkA6SRx6KB6HivjT/SzF6rtH6CRV0ximLXxR1Iw7p6B75jj0ZpsjUBF/eZhN5qlYm0mKIiSgUth0nQW4nuDLf9afFGs/gWTyWb6Ba6sNyB9tjyju/FYYCFE93Qb8ki1ne63u2DlhHHpWbHt7nGYqos2bz2Uo5NNURdLjOT/UjRzr7HB+Gjj0rNhqSpwn+NPCiwiKhLlmSJTICgq/CoL0Sn4OUXRIrMcqdYPSwQUyI/wcy3Ekqhb2MuQM3J88Dj0rtlo0zVTEBP1NOgscelZsb/ttFD9NE7HeU1RinjUjydrjWhdcMk1Rx2PvmSH5K0BRSoWkPcrEuz01yA6UYkRB+XSLdzuhx6FnxRaLpnmg6Xm0QA+q88GhZ8WWiqY/3k0TVF0HWyosaY9m42qBKwvdCtbOzRbCkqlTsqCWSY8kXtWyKijjKhauAJqzo3pW9K1hcH4dxaNZej5fvB85UN+p4tCzYosocdly0fRhVlQFhcRSK4UQ5SlJ9S+GjUt2SoXnZNotD70lQFgz9PYvIL9dkSiBOl1ZezxzPvSs2EbR9Ge21dS814UhGs4eDm4NpdvYd+yns+Rd9aNzRJ2HYtvbw0RKz0fU5wEqBTRIfBZKZJjKehMh3RRk6SBvHf55pwvYkDt5cHoBtS2UuIg1ncrehK1wB9QMhxUorcLqeozSDt7KhopyxiMVr4ZFj8jyL81wgvJzKMIq6ck4Q0r0rNgicH5+ms/i0uYzUjkKvaxJSJREfURz4BtqhV6SeQ0iLvKqASypOgpqRYF7VvSteWvNl6cZhZFy+nw5omxlHyEMLuRoIJRAOGOUnCbmTbXlFPwc5MUSoeh5p24AfmJ4VmyRtSZ3JKZGm4wwqXtaXRHtxh6yR1M5UNZxuoonQ11PzkRe9ax4eHCOFukXK699gXm2vxZCrjl+DqEGbUuh2ChkX0NZ/6QcGJu3wMSNaxUXldSt4sWsv47Oo+hZ0bcDiKZikZHoPmAtsqr3XXjnYXTrhh6KnhVbA87fnuPHaSEjscA8hTjvLTeuuQtkY2YRA1utTIogF8XIOTmbWOij5Mjk5CIzU8F0q88LUQVEQAFgx0PRs2KLKPF+erZ3ZtCpnjHsWdG3RilxsTtiKXxabJKJSpQi2Se3Md+oGCaFVDSqXNGCSrCiI8iFqTxH+SJV7XGrpVIR5B0A1ZNxnh5Fz4qHp8SfH86XEvsdL516VmwNJc7icgqUUJx1AkvaXVEBW5kiFQokdDs6glxITCuHjWvLc6hlyEFxdxb9oVveLhBvP/I04Fnx6CiRjPM0lg+2DUO/g7wDzcE9ghpQPeSi61nRs2ILwPl1NJ/OUU0ykotfIBTZTo5lAUGbQdHIjUJTngMILZEwrhZpExnlOWR63Kq43QgC8FD0rNgCSvzpfkYWOSyRHsoSpZnBikfYQ0M19GXl1Ur3onz2yy4x8c42zsZD8ZCUOMs3oqFL6AstGjXCKrmLBmqj56jEKELItMfZsffMUIE58DYbD8U2UOKP9zNUpnNpY1GK3VDI+3LrJjoWvf064l1lPFrCVkvbd2suitj5Bin9cvleFEDP22w8FA8OzsdpsqBESyq9kGFpkDbRKlI2VUy1oWi7i66fdd5s0wZKvJtuzCRrw8i2DhqA7LVf/q4EXm92qiiOo40gF1Y/x1o1U4iRKkMOJQOPoK+wsN2qkGIArnpeUfSseGhwzhJ8XJTMQJtBBSvthYHVaIwXwW33nqDBnrRqvchLp54VW0CJP9xNUJTDwCSywkLyEpDbn21pCDaFRxVvBJQ8GJsIciEFkRdVPlhxqak8ByuIvBT8Lfs5rnqhnySeFQ8MziQV357jM78bXjr1rHj49vlpFifFeDOJHose81UsNUI5n1jRzaSNnwrx2avRlAhyUQgiJ7RHLIRxK2XIy/tA6cpzlATZwkVedMPQV9DwrHgQ6bT4zs/uKcLnQIm+eVbcaxvN0nGcgiiTFcgZT7IWKfFLUWajIsiFUCo+SYFz28FN5TlKgW/FKDnJsrpVZe3lOZbn6wRwSSmK4CPgPCvu02BzzvlQy/ZiwDXYnCc4PSvuA5xJil9G83Sh3IEcV13KOdrSImwMpBuFrUxhUODBwvaJUmEoKs67yI3LkSlbLtFZomVATaUNNYI8rwIurvvedupZ8dDty3M8T1dQQlLILLxpcfJpYtk2P5h5T+RgTu5N5FW7Wo5w3aMNNl469VDcq8Emo8RVfOk67FP3Wk/f4j6E2g1nUIPG0ttIRpCSG1wYy5Ajrww56em/HXLlr7MFp4fizluc4ufnFRQX6RiYLn8pxXzLcdVYyHLA8kTHZRNKBDkVKU5tR0MCjUKXgl57TKu0z9Ti500/9MXdPBTbIJ3ORYn6CikRGlbcorHEdjKZaTtgOZtq/Q5qubEEQD0gCTQiqty7HffVRcQkwHOWV73ZZg/SaSw0Fo615aRsLYF1fW3Vz1E0h6wiysjpW4g0lw0o1F4YcoS5beMNws9RRHypDPnNwFOiZ8V2SKeZoijKdFd4IWszDIOJBVGbua+PYTWcD5u9A68vOl4n9KzYAuk0xyGRQYTbGDepHE2gEFcZHnL58KKfA9bbVdBoXEepITWO2NAjyNu/qcVOhZSTRfg5Cj6X20HEp8QzB6dnxR1Lp89zYWQmkvo2ihzqeIpKWOJkCTN3khJGQrZosGtABgCvLyOPOs+K7WFFRXvb8k3e0gKbQJEbFzS1VQuB1gKLKmShMqliESlXaqPLkENZmbRtYoWF0eQy5LdDh+BvD07PirvEYYkSz6llcumby46fA54VWyGd3o1jk/yHUsnsJTcGi8PTJROumUINIhfKnhkbjpW0x0J5DiXwrcBmmzLk8gaP29GUCHJhK8/x9qrLJ0BPiZ4V9yadnle76IY+4tSzYlvaJE4nccqwkEDRwppKNLhSvgLqIIJjNRHkIDsmVW6UsqnW+cqq43GzwaO5DHkI8OGm4ynRs2KbpVMDILd/lLyOZPJVybKKawsLGsoQK28Wo234EeSidIRiPH05jLph4G0znhXb0u4m/DI25Q1+ixiDFT2uZnGwpqmSirglt0KZC1GyrIK85ZOQTiNVglS26BDlTaxkPXPdtx8Fb68crDUenJ4Vdw/FcaWKUkqdbVzzLRmtqnamq4yvO5GpIeo4vDLkxL4A373oetR5VjwuRZELTLBVFhVk2KjKaVadFYlOkua5+V0z+JvLTr/jsLh7cHootkdR1OCL3ou76MAvSosCCxuOlsLWiln8qp+jDHssRZAL069yLPlF1ySaetRZ2/8XYADH/GSQm72TNQAAAABJRU5ErkJggg==\" alt=\"Yii Framework\"/>\n        <p class=\"timestamp\"><?= date('Y-m-d, H:i:s') ?></p>\n        <p><?= $handler->createServerInformationLink() ?></p>\n        <p><a href=\"https://yiiframework.com/\">Yii Framework</a>/<?= $handler->createFrameworkVersionLink() ?></p>\n    </div>\n\n    <script>\nvar hljs=new function(){function l(o){return o.replace(/&/gm,\"&amp;\").replace(/</gm,\"&lt;\").replace(/>/gm,\"&gt;\")}function b(p){for(var o=p.firstChild;o;o=o.nextSibling){if(o.nodeName==\"CODE\"){return o}if(!(o.nodeType==3&&o.nodeValue.match(/\\s+/))){break}}}function h(p,o){return Array.prototype.map.call(p.childNodes,function(q){if(q.nodeType==3){return o?q.nodeValue.replace(/\\n/g,\"\"):q.nodeValue}if(q.nodeName==\"BR\"){return\"\\n\"}return h(q,o)}).join(\"\")}function a(q){var p=(q.className+\" \"+q.parentNode.className).split(/\\s+/);p=p.map(function(r){return r.replace(/^language-/,\"\")});for(var o=0;o<p.length;o++){if(e[p[o]]||p[o]==\"no-highlight\"){return p[o]}}}function c(q){var o=[];(function p(r,s){for(var t=r.firstChild;t;t=t.nextSibling){if(t.nodeType==3){s+=t.nodeValue.length}else{if(t.nodeName==\"BR\"){s+=1}else{if(t.nodeType==1){o.push({event:\"start\",offset:s,node:t});s=p(t,s);o.push({event:\"stop\",offset:s,node:t})}}}}return s})(q,0);return o}function j(x,v,w){var p=0;var y=\"\";var r=[];function t(){if(x.length&&v.length){if(x[0].offset!=v[0].offset){return(x[0].offset<v[0].offset)?x:v}else{return v[0].event==\"start\"?x:v}}else{return x.length?x:v}}function s(A){function z(B){return\" \"+B.nodeName+'=\"'+l(B.value)+'\"'}return\"<\"+A.nodeName+Array.prototype.map.call(A.attributes,z).join(\"\")+\">\"}while(x.length||v.length){var u=t().splice(0,1)[0];y+=l(w.substr(p,u.offset-p));p=u.offset;if(u.event==\"start\"){y+=s(u.node);r.push(u.node)}else{if(u.event==\"stop\"){var o,q=r.length;do{q--;o=r[q];y+=(\"</\"+o.nodeName.toLowerCase()+\">\")}while(o!=u.node);r.splice(q,1);while(q<r.length){y+=s(r[q]);q++}}}}return y+l(w.substr(p))}function f(q){function o(s,r){return RegExp(s,\"m\"+(q.cI?\"i\":\"\")+(r?\"g\":\"\"))}function p(y,w){if(y.compiled){return}y.compiled=true;var s=[];if(y.k){var r={};function z(A,t){t.split(\" \").forEach(function(B){var C=B.split(\"|\");r[C[0]]=[A,C[1]?Number(C[1]):1];s.push(C[0])})}y.lR=o(y.l||hljs.IR,true);if(typeof y.k==\"string\"){z(\"keyword\",y.k)}else{for(var x in y.k){if(!y.k.hasOwnProperty(x)){continue}z(x,y.k[x])}}y.k=r}if(w){if(y.bWK){y.b=\"\\\\b(\"+s.join(\"|\")+\")\\\\s\"}y.bR=o(y.b?y.b:\"\\\\B|\\\\b\");if(!y.e&&!y.eW){y.e=\"\\\\B|\\\\b\"}if(y.e){y.eR=o(y.e)}y.tE=y.e||\"\";if(y.eW&&w.tE){y.tE+=(y.e?\"|\":\"\")+w.tE}}if(y.i){y.iR=o(y.i)}if(y.r===undefined){y.r=1}if(!y.c){y.c=[]}for(var v=0;v<y.c.length;v++){if(y.c[v]==\"self\"){y.c[v]=y}p(y.c[v],y)}if(y.starts){p(y.starts,w)}var u=[];for(var v=0;v<y.c.length;v++){u.push(y.c[v].b)}if(y.tE){u.push(y.tE)}if(y.i){u.push(y.i)}y.t=u.length?o(u.join(\"|\"),true):{exec:function(t){return null}}}p(q)}function d(D,E){function o(r,M){for(var L=0;L<M.c.length;L++){var K=M.c[L].bR.exec(r);if(K&&K.index==0){return M.c[L]}}}function s(K,r){if(K.e&&K.eR.test(r)){return K}if(K.eW){return s(K.parent,r)}}function t(r,K){return K.i&&K.iR.test(r)}function y(L,r){var K=F.cI?r[0].toLowerCase():r[0];return L.k.hasOwnProperty(K)&&L.k[K]}function G(){var K=l(w);if(!A.k){return K}var r=\"\";var N=0;A.lR.lastIndex=0;var L=A.lR.exec(K);while(L){r+=K.substr(N,L.index-N);var M=y(A,L);if(M){v+=M[1];r+='<span class=\"'+M[0]+'\">'+L[0]+\"</span>\"}else{r+=L[0]}N=A.lR.lastIndex;L=A.lR.exec(K)}return r+K.substr(N)}function z(){if(A.sL&&!e[A.sL]){return l(w)}var r=A.sL?d(A.sL,w):g(w);if(A.r>0){v+=r.keyword_count;B+=r.r}return'<span class=\"'+r.language+'\">'+r.value+\"</span>\"}function J(){return A.sL!==undefined?z():G()}function I(L,r){var K=L.cN?'<span class=\"'+L.cN+'\">':\"\";if(L.rB){x+=K;w=\"\"}else{if(L.eB){x+=l(r)+K;w=\"\"}else{x+=K;w=r}}A=Object.create(L,{parent:{value:A}});B+=L.r}function C(K,r){w+=K;if(r===undefined){x+=J();return 0}var L=o(r,A);if(L){x+=J();I(L,r);return L.rB?0:r.length}var M=s(A,r);if(M){if(!(M.rE||M.eE)){w+=r}x+=J();do{if(A.cN){x+=\"</span>\"}A=A.parent}while(A!=M.parent);if(M.eE){x+=l(r)}w=\"\";if(M.starts){I(M.starts,\"\")}return M.rE?0:r.length}if(t(r,A)){throw\"Illegal\"}w+=r;return r.length||1}var F=e[D];f(F);var A=F;var w=\"\";var B=0;var v=0;var x=\"\";try{var u,q,p=0;while(true){A.t.lastIndex=p;u=A.t.exec(E);if(!u){break}q=C(E.substr(p,u.index-p),u[0]);p=u.index+q}C(E.substr(p));return{r:B,keyword_count:v,value:x,language:D}}catch(H){if(H==\"Illegal\"){return{r:0,keyword_count:0,value:l(E)}}else{throw H}}}function g(s){var o={keyword_count:0,r:0,value:l(s)};var q=o;for(var p in e){if(!e.hasOwnProperty(p)){continue}var r=d(p,s);r.language=p;if(r.keyword_count+r.r>q.keyword_count+q.r){q=r}if(r.keyword_count+r.r>o.keyword_count+o.r){q=o;o=r}}if(q.language){o.second_best=q}return o}function i(q,p,o){if(p){q=q.replace(/^((<[^>]+>|\\t)+)/gm,function(r,v,u,t){return v.replace(/\\t/g,p)})}if(o){q=q.replace(/\\n/g,\"<br>\")}return q}function m(r,u,p){var v=h(r,p);var t=a(r);if(t==\"no-highlight\"){return}var w=t?d(t,v):g(v);t=w.language;var o=c(r);if(o.length){var q=document.createElement(\"pre\");q.innerHTML=w.value;w.value=j(o,c(q),v)}w.value=i(w.value,u,p);var s=r.className;if(!s.match(\"(\\\\s|^)(language-)?\"+t+\"(\\\\s|$)\")){s=s?(s+\" \"+t):t}r.innerHTML=w.value;r.className=s;r.result={language:t,kw:w.keyword_count,re:w.r};if(w.second_best){r.second_best={language:w.second_best.language,kw:w.second_best.keyword_count,re:w.second_best.r}}}function n(){if(n.called){return}n.called=true;Array.prototype.map.call(document.getElementsByTagName(\"pre\"),b).filter(Boolean).forEach(function(o){m(o,hljs.tabReplace)})}function k(){window.addEventListener(\"DOMContentLoaded\",n,false);window.addEventListener(\"load\",n,false)}var e={};this.LANGUAGES=e;this.highlight=d;this.highlightAuto=g;this.fixMarkup=i;this.highlightBlock=m;this.initHighlighting=n;this.initHighlightingOnLoad=k;this.IR=\"[a-zA-Z][a-zA-Z0-9_]*\";this.UIR=\"[a-zA-Z_][a-zA-Z0-9_]*\";this.NR=\"\\\\b\\\\d+(\\\\.\\\\d+)?\";this.CNR=\"(\\\\b0[xX][a-fA-F0-9]+|(\\\\b\\\\d+(\\\\.\\\\d*)?|\\\\.\\\\d+)([eE][-+]?\\\\d+)?)\";this.BNR=\"\\\\b(0b[01]+)\";this.RSR=\"!|!=|!==|%|%=|&|&&|&=|\\\\*|\\\\*=|\\\\+|\\\\+=|,|\\\\.|-|-=|/|/=|:|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\\\?|\\\\[|\\\\{|\\\\(|\\\\^|\\\\^=|\\\\||\\\\|=|\\\\|\\\\||~\";this.BE={b:\"\\\\\\\\[\\\\s\\\\S]\",r:0};this.ASM={cN:\"string\",b:\"'\",e:\"'\",i:\"\\\\n\",c:[this.BE],r:0};this.QSM={cN:\"string\",b:'\"',e:'\"',i:\"\\\\n\",c:[this.BE],r:0};this.CLCM={cN:\"comment\",b:\"//\",e:\"$\"};this.CBLCLM={cN:\"comment\",b:\"/\\\\*\",e:\"\\\\*/\"};this.HCM={cN:\"comment\",b:\"#\",e:\"$\"};this.NM={cN:\"number\",b:this.NR,r:0};this.CNM={cN:\"number\",b:this.CNR,r:0};this.BNM={cN:\"number\",b:this.BNR,r:0};this.inherit=function(q,r){var o={};for(var p in q){o[p]=q[p]}if(r){for(var p in r){o[p]=r[p]}}return o}}();hljs.LANGUAGES.php=function(a){var e={cN:\"variable\",b:\"\\\\$+[a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*\"};var b=[a.inherit(a.ASM,{i:null}),a.inherit(a.QSM,{i:null}),{cN:\"string\",b:'b\"',e:'\"',c:[a.BE]},{cN:\"string\",b:\"b'\",e:\"'\",c:[a.BE]}];var c=[a.BNM,a.CNM];var d={cN:\"title\",b:a.UIR};return{cI:true,k:\"and include_once list abstract global private echo interface as static endswitch array null if endwhile or const for endforeach self var while isset public protected exit foreach throw elseif include __FILE__ empty require_once do xor return implements parent clone use __CLASS__ __LINE__ else break print eval new catch __METHOD__ case exception php_user_filter default die require __FUNCTION__ enddeclare final try this switch continue endfor endif declare unset true false namespace trait goto instanceof insteadof __DIR__ __NAMESPACE__ __halt_compiler\",c:[a.CLCM,a.HCM,{cN:\"comment\",b:\"/\\\\*\",e:\"\\\\*/\",c:[{cN:\"phpdoc\",b:\"\\\\s@[A-Za-z]+\"}]},{cN:\"comment\",eB:true,b:\"__halt_compiler.+?;\",eW:true},{cN:\"string\",b:\"<<<['\\\"]?\\\\w+['\\\"]?$\",e:\"^\\\\w+;\",c:[a.BE]},{cN:\"preprocessor\",b:\"<\\\\?php\",r:10},{cN:\"preprocessor\",b:\"\\\\?>\"},e,{cN:\"function\",bWK:true,e:\"{\",k:\"function\",i:\"\\\\$|\\\\[|%\",c:[d,{cN:\"params\",b:\"\\\\(\",e:\"\\\\)\",c:[\"self\",e,a.CBLCLM].concat(b).concat(c)}]},{cN:\"class\",bWK:true,e:\"{\",k:\"class\",i:\"[:\\\\(\\\\$]\",c:[{bWK:true,eW:true,k:\"extends\",c:[d]},d]},{b:\"=>\"}].concat(b).concat(c)}}(hljs);\n    </script>\n\n    <script>\nwindow.onload = function() {\n    var codeBlocks = document.getElementsByTagName('pre'),\n        callStackItems = document.getElementsByClassName('call-stack-item');\n\n    // highlight code blocks\n    for (var i = 0, imax = codeBlocks.length; i < imax; ++i) {\n        hljs.highlightBlock(codeBlocks[i], '    ');\n    }\n\n    var refreshCallStackItemCode = function(callStackItem) {\n        if (!callStackItem.getElementsByTagName('pre')[0]) {\n            return;\n        }\n        var top = callStackItem.getElementsByClassName('code-wrap')[0].offsetTop - window.pageYOffset + 3,\n            lines = callStackItem.getElementsByTagName('pre')[0].getClientRects(),\n            lineNumbers = callStackItem.getElementsByClassName('lines-item'),\n            errorLine = callStackItem.getElementsByClassName('error-line')[0],\n            hoverLines = callStackItem.getElementsByClassName('hover-line');\n        for (var i = 0, imax = lines.length; i < imax; ++i) {\n            if (!lineNumbers[i]) {\n                continue;\n            }\n            lineNumbers[i].style.top = parseInt(lines[i].top - top) + 'px';\n            hoverLines[i].style.top = parseInt(lines[i].top - top) + 'px';\n            hoverLines[i].style.height = parseInt(lines[i].bottom - lines[i].top + 6) + 'px';\n            if (parseInt(callStackItem.getAttribute('data-line')) == i) {\n                errorLine.style.top = parseInt(lines[i].top - top) + 'px';\n                errorLine.style.height = parseInt(lines[i].bottom - lines[i].top + 6) + 'px';\n            }\n        }\n    };\n\n    for (var i = 0, imax = callStackItems.length; i < imax; ++i) {\n        refreshCallStackItemCode(callStackItems[i]);\n\n        // toggle code block visibility\n        callStackItems[i].getElementsByClassName('element-wrap')[0].addEventListener('click', function(event) {\n            if (event.target.nodeName.toLowerCase() === 'a') {\n                return;\n            }\n\n            var callStackItem = this.parentNode,\n                code = callStackItem.getElementsByClassName('code-wrap')[0];\n\n            if (typeof code !== 'undefined') {\n                code.style.display = window.getComputedStyle(code).display == 'block' ? 'none' : 'block';\n                refreshCallStackItemCode(callStackItem);\n            }\n        });\n    }\n\n    // handle copy stacktrace action on clipboard button\n    document.getElementById('copy-stacktrace').onclick = function(e) {\n        e.preventDefault();\n        var textarea = document.getElementById('clipboard');\n        textarea.focus();\n        textarea.select();\n        try {\n            succeeded = document.execCommand('copy');\n        } catch (err) {\n            succeeded = false;\n        }\n        if (succeeded) {\n            document.getElementById('copied').style.display = 'block';\n        } else {\n            // fallback: show textarea if browser does not support copying directly\n            textarea.style.top = 0;\n        }\n    }\n};\n\n    // Highlight lines that have text in them but still support text selection:\n    document.onmousedown = function() { document.getElementsByTagName('body')[0].classList.add('mousedown'); };\n    document.onmouseup = function() { document.getElementsByTagName('body')[0].classList.remove('mousedown'); };\n    </script>\n    <?php if (method_exists($this, 'endBody')): ?>\n        <?php $this->endBody() // to allow injecting code into body (mostly by Yii Debug Toolbar)?>\n    <?php endif ?>\n</body>\n\n</html>\n<?php if (method_exists($this, 'endPage')): ?>\n    <?php $this->endPage() ?>\n<?php endif ?>\n"
  },
  {
    "path": "framework/views/errorHandler/previousException.php",
    "content": "<?php\n/**\n * @var \\yii\\base\\Exception $exception\n * @var \\yii\\web\\ErrorHandler $handler\n */\n?>\n<div class=\"previous\">\n    <span class=\"arrow\">&crarr;</span>\n    <h2>\n        <span>Caused by:</span>\n        <?php $name = $handler->getExceptionName($exception) ?>\n        <?php if ($name !== null): ?>\n            <span><?= $handler->htmlEncode($name) ?></span> &ndash;\n            <?= $handler->addTypeLinks(get_class($exception)) ?>\n        <?php else: ?>\n            <span><?= $handler->htmlEncode(get_class($exception)) ?></span>\n        <?php endif; ?>\n    </h2>\n    <h3><?= nl2br($handler->htmlEncode($exception->getMessage())) ?></h3>\n    <p>in <span class=\"file\"><?= $exception->getFile() ?></span> at line <span class=\"line\"><?= $exception->getLine() ?></span></p>\n    <?php if ($exception instanceof \\yii\\db\\Exception && !empty($exception->errorInfo)): ?>\n        <pre>Error Info: <?= $handler->htmlEncode(print_r($exception->errorInfo, true)) ?></pre>\n    <?php endif ?>\n    <?= $handler->renderPreviousExceptions($exception) ?>\n</div>\n"
  },
  {
    "path": "framework/views/messageConfig.php",
    "content": "<?php\n\nreturn [\n    // string, required, root directory of all source files\n    'sourcePath' => __DIR__ . DIRECTORY_SEPARATOR . '..',\n    // array, required, list of language codes that the extracted messages\n    // should be translated to. For example, ['zh-CN', 'de'].\n    'languages' => [\n        'af', 'ar', 'az', 'be', 'bg', 'bs', 'ca', 'cs', 'da', 'de', 'el', 'es', 'et', 'fa', 'fi', 'fr', 'he', 'hi',\n        'pt-BR', 'ro', 'hr', 'hu', 'hy', 'id', 'it', 'ja', 'ka', 'kk', 'ko', 'kz', 'lt', 'lv', 'ms', 'nb-NO', 'nl',\n        'pl', 'pt', 'ru', 'sk', 'sl', 'sr', 'sr-Latn', 'sv', 'tg', 'th', 'tr', 'uk', 'uz', 'uz-Cy', 'vi', 'zh-CN',\n        'zh-TW'\n    ],\n    // string, the name of the function for translating messages.\n    // Defaults to 'Yii::t'. This is used as a mark to find the messages to be\n    // translated. You may use a string for single function name or an array for\n    // multiple function names.\n    'translator' => ['\\Yii::t', 'Yii::t'],\n    // boolean, whether to sort messages by keys when merging new messages\n    // with the existing ones. Defaults to false, which means the new (untranslated)\n    // messages will be separated from the old (translated) ones.\n    'sort' => false,\n    // boolean, whether to remove messages that no longer appear in the source code.\n    // Defaults to false, which means these messages will NOT be removed.\n    'removeUnused' => false,\n    // boolean, whether to mark messages that no longer appear in the source code.\n    // Defaults to true, which means each of these messages will be enclosed with a pair of '@@' marks.\n    'markUnused' => true,\n    // array, list of patterns that specify which files (not directories) should be processed.\n    // If empty or not set, all files will be processed.\n    // See helpers/FileHelper::findFiles() for pattern matching rules.\n    // If a file/directory matches both a pattern in \"only\" and \"except\", it will NOT be processed.\n    'only' => ['*.php'],\n    // array, list of patterns that specify which files/directories should NOT be processed.\n    // If empty or not set, all files/directories will be processed.\n    // See helpers/FileHelper::findFiles() for pattern matching rules.\n    // If a file/directory matches both a pattern in \"only\" and \"except\", it will NOT be processed.\n    'except' => [\n        '.*',\n        '/.*',\n        '/messages',\n        '/tests',\n        '/runtime',\n        '/vendor',\n        '/BaseYii.php',\n    ],\n    // 'php' output format is for saving messages to php files.\n    'format' => 'php',\n    // Root directory containing message translations.\n    'messagePath' => __DIR__,\n    // boolean, whether the message file should be overwritten with the merged messages\n    'overwrite' => true,\n    /*\n    // File header used in generated messages files\n    'phpFileHeader' => '',\n    // PHPDoc used for array of messages with generated messages files\n    'phpDocBlock' => null,\n    */\n\n    /*\n    // Message categories to ignore\n    'ignoreCategories' => [\n        'yii',\n    ],\n     */\n\n    /*\n    // 'db' output format is for saving messages to database.\n    'format' => 'db',\n    // Connection component to use. Optional.\n    'db' => 'db',\n    // Custom source message table. Optional.\n    // 'sourceMessageTable' => '{{%source_message}}',\n    // Custom name for translation message table. Optional.\n    // 'messageTable' => '{{%message}}',\n    */\n\n    /*\n    // 'po' output format is for saving messages to gettext po files.\n    'format' => 'po',\n    // Root directory containing message translations.\n    'messagePath' => __DIR__ . DIRECTORY_SEPARATOR . 'messages',\n    // Name of the file that will be used for translations.\n    'catalog' => 'messages',\n    // boolean, whether the message file should be overwritten with the merged messages\n    'overwrite' => true,\n    */\n];\n"
  },
  {
    "path": "framework/views/migration.php",
    "content": "<?php\n\n/**\n * This view is used by console/controllers/MigrateController.php.\n *\n * The following variables are available in this view:\n *\n * @var string $className the new migration class name without namespace\n * @var string $namespace the new migration class namespace\n */\n\necho \"<?php\\n\";\nif (!empty($namespace)) {\n    echo \"\\nnamespace {$namespace};\\n\";\n}\n?>\n\nuse yii\\db\\Migration;\n\nclass <?= $className ?> extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function safeUp()\n    {\n\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function safeDown()\n    {\n        echo \"<?= $className ?> cannot be reverted.\\n\";\n\n        return false;\n    }\n\n    /*\n    // Use up()/down() to run migration code without a transaction.\n    public function up()\n    {\n\n    }\n\n    public function down()\n    {\n        echo \"<?= $className ?> cannot be reverted.\\n\";\n\n        return false;\n    }\n    */\n}\n"
  },
  {
    "path": "framework/web/Application.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\nuse Yii;\nuse yii\\base\\InvalidRouteException;\nuse yii\\helpers\\Url;\n\n/**\n * Application is the base class for all web application classes.\n *\n * For more details and usage information on Application, see the [guide article on applications](guide:structure-applications).\n *\n * @template TUserIdentity of IdentityInterface = IdentityInterface\n *\n * @property-read ErrorHandler $errorHandler The error handler application component.\n * @property string $homeUrl The homepage URL.\n * @property-read Request $request The request component.\n * @property-read Response $response The response component.\n * @property-read Session $session The session component.\n * @property-read User<TUserIdentity> $user The user component.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Application extends \\yii\\base\\Application\n{\n    /**\n     * @var string the default route of this application. Defaults to 'site'.\n     */\n    public $defaultRoute = 'site';\n    /**\n     * @var array|null the configuration specifying a controller action which should handle\n     * all user requests. This is mainly used when the application is in maintenance mode\n     * and needs to handle all incoming requests via a single action.\n     * The configuration is an array whose first element specifies the route of the action.\n     * The rest of the array elements (key-value pairs) specify the parameters to be bound\n     * to the action. For example,\n     *\n     * ```\n     * [\n     *     'offline/notice',\n     *     'param1' => 'value1',\n     *     'param2' => 'value2',\n     * ]\n     * ```\n     *\n     * Defaults to null, meaning catch-all is not used.\n     */\n    public $catchAll;\n    /**\n     * @var Controller|null the currently active controller instance\n     */\n    public $controller;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function bootstrap()\n    {\n        $request = $this->getRequest();\n        Yii::setAlias('@webroot', dirname($request->getScriptFile()));\n        Yii::setAlias('@web', $request->getBaseUrl());\n\n        parent::bootstrap();\n    }\n\n    /**\n     * Handles the specified request.\n     * @param Request $request the request to be handled\n     * @return Response the resulting response\n     * @throws NotFoundHttpException if the requested route is invalid\n     */\n    public function handleRequest($request)\n    {\n        if (empty($this->catchAll)) {\n            try {\n                list($route, $params) = $request->resolve();\n            } catch (UrlNormalizerRedirectException $e) {\n                $url = $e->url;\n                if (is_array($url)) {\n                    if (isset($url[0])) {\n                        // ensure the route is absolute\n                        $url[0] = '/' . ltrim($url[0], '/');\n                    }\n                    $url += $request->getQueryParams();\n                }\n\n                return $this->getResponse()->redirect(Url::to($url, $e->scheme), $e->statusCode);\n            }\n        } else {\n            $route = $this->catchAll[0];\n            $params = $this->catchAll;\n            unset($params[0]);\n        }\n        try {\n            Yii::debug(\"Route requested: '$route'\", __METHOD__);\n            $this->requestedRoute = $route;\n            $result = $this->runAction($route, $params);\n            if ($result instanceof Response) {\n                return $result;\n            }\n\n            $response = $this->getResponse();\n            if ($result !== null) {\n                $response->data = $result;\n            }\n\n            return $response;\n        } catch (InvalidRouteException $e) {\n            throw new NotFoundHttpException(Yii::t('yii', 'Page not found.'), $e->getCode(), $e);\n        }\n    }\n\n    private $_homeUrl;\n\n    /**\n     * @return string the homepage URL\n     */\n    public function getHomeUrl()\n    {\n        if ($this->_homeUrl === null) {\n            if ($this->getUrlManager()->showScriptName) {\n                return $this->getRequest()->getScriptUrl();\n            }\n\n            return $this->getRequest()->getBaseUrl() . '/';\n        }\n\n        return $this->_homeUrl;\n    }\n\n    /**\n     * @param string $value the homepage URL\n     */\n    public function setHomeUrl($value)\n    {\n        $this->_homeUrl = $value;\n    }\n\n    /**\n     * Returns the error handler component.\n     * @return ErrorHandler the error handler application component.\n     */\n    public function getErrorHandler()\n    {\n        return $this->get('errorHandler');\n    }\n\n    /**\n     * Returns the request component.\n     * @return Request the request component.\n     */\n    public function getRequest()\n    {\n        return $this->get('request');\n    }\n\n    /**\n     * Returns the response component.\n     * @return Response the response component.\n     */\n    public function getResponse()\n    {\n        return $this->get('response');\n    }\n\n    /**\n     * Returns the session component.\n     * @return Session the session component.\n     */\n    public function getSession()\n    {\n        return $this->get('session');\n    }\n\n    /**\n     * Returns the user component.\n     * @return User<TUserIdentity> the user component.\n     */\n    public function getUser()\n    {\n        return $this->get('user');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function coreComponents()\n    {\n        return array_merge(parent::coreComponents(), [\n            'request' => ['class' => 'yii\\web\\Request'],\n            'response' => ['class' => 'yii\\web\\Response'],\n            'session' => ['class' => 'yii\\web\\Session'],\n            'user' => ['class' => 'yii\\web\\User'],\n            'errorHandler' => ['class' => 'yii\\web\\ErrorHandler'],\n        ]);\n    }\n}\n"
  },
  {
    "path": "framework/web/AssetBundle.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\nuse Yii;\nuse yii\\base\\BaseObject;\nuse yii\\helpers\\ArrayHelper;\nuse yii\\helpers\\Url;\n\n/**\n * AssetBundle represents a collection of asset files, such as CSS, JS, images.\n *\n * Each asset bundle has a unique name that globally identifies it among all asset bundles used in an application.\n * The name is the [fully qualified class name](https://www.php.net/manual/en/language.namespaces.rules.php)\n * of the class representing it.\n *\n * An asset bundle can depend on other asset bundles. When registering an asset bundle\n * with a view, all its dependent asset bundles will be automatically registered.\n *\n * For more details and usage information on AssetBundle, see the [guide article on assets](guide:structure-assets).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @phpstan-import-type RegisterJsFileOptions from View\n * @phpstan-import-type RegisterCssFileOptions from View\n * @phpstan-import-type PublishOptions from AssetManager\n */\nclass AssetBundle extends BaseObject\n{\n    /**\n     * @var string|null the directory that contains the source asset files for this asset bundle.\n     * A source asset file is a file that is part of your source code repository of your Web application.\n     *\n     * You must set this property if the directory containing the source asset files is not Web accessible.\n     * By setting this property, [[AssetManager]] will publish the source asset files\n     * to a Web-accessible directory automatically when the asset bundle is registered on a page.\n     *\n     * If you do not set this property, it means the source asset files are located under [[basePath]].\n     *\n     * You can use either a directory or an alias of the directory.\n     * @see publishOptions\n     */\n    public $sourcePath;\n    /**\n     * @var string|null the Web-accessible directory that contains the asset files in this bundle.\n     *\n     * If [[sourcePath]] is set, this property will be *overwritten* by [[AssetManager]]\n     * when it publishes the asset files from [[sourcePath]].\n     *\n     * You can use either a directory or an alias of the directory.\n     */\n    public $basePath;\n    /**\n     * @var string|null the base URL for the relative asset files listed in [[js]] and [[css]].\n     *\n     * If [[sourcePath]] is set, this property will be *overwritten* by [[AssetManager]]\n     * when it publishes the asset files from [[sourcePath]].\n     *\n     * You can use either a URL or an alias of the URL.\n     */\n    public $baseUrl;\n    /**\n     * @var class-string[] list of bundle class names that this bundle depends on.\n     *\n     * For example:\n     *\n     * ```\n     * public $depends = [\n     *    'yii\\web\\YiiAsset',\n     *    'yii\\bootstrap\\BootstrapAsset',\n     * ];\n     * ```\n     */\n    public $depends = [];\n    /**\n     * @var (string|array<array-key, mixed>)[] list of JavaScript files that this bundle contains. Each JavaScript file can be\n     * specified in one of the following formats:\n     *\n     * - an absolute URL representing an external asset. For example,\n     *   `https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js` or\n     *   `//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js`.\n     * - a relative path representing a local asset (e.g. `js/main.js`). The actual file path of a local\n     *   asset can be determined by prefixing [[basePath]] to the relative path, and the actual URL\n     *   of the asset can be determined by prefixing [[baseUrl]] to the relative path.\n     * - an array, with the first entry being the URL or relative path as described before, and a list of key => value pairs\n     *   that will be used to overwrite [[jsOptions]] settings for this entry.\n     *   This functionality is available since version 2.0.7.\n     *\n     * Note that only a forward slash \"/\" should be used as directory separator.\n     */\n    public $js = [];\n    /**\n     * @var (string|array<array-key, mixed>)[] list of CSS files that this bundle contains. Each CSS file can be specified\n     * in one of the three formats as explained in [[js]].\n     *\n     * Note that only a forward slash \"/\" should be used as directory separator.\n     */\n    public $css = [];\n    /**\n     * @var RegisterJsFileOptions the options that will be passed to [[View::registerJsFile()]]\n     * when registering the JS files in this bundle.\n     */\n    public $jsOptions = [];\n    /**\n     * @var RegisterCssFileOptions the options that will be passed to [[View::registerCssFile()]]\n     * when registering the CSS files in this bundle.\n     */\n    public $cssOptions = [];\n    /**\n     * @var PublishOptions the options to be passed to [[AssetManager::publish()]] when the asset bundle\n     * is being published. This property is used only when [[sourcePath]] is set.\n     */\n    public $publishOptions = [];\n\n\n    /**\n     * Registers this asset bundle with a view.\n     * @param View $view the view to be registered with\n     * @return static the registered asset bundle instance\n     */\n    public static function register($view)\n    {\n        /** @var static $result */\n        $result = $view->registerAssetBundle(get_called_class());\n\n        return $result;\n    }\n\n    /**\n     * Initializes the bundle.\n     * If you override this method, make sure you call the parent implementation in the last.\n     */\n    public function init()\n    {\n        if ($this->sourcePath !== null) {\n            $this->sourcePath = rtrim(Yii::getAlias($this->sourcePath), '/\\\\');\n        }\n        if ($this->basePath !== null) {\n            $this->basePath = rtrim(Yii::getAlias($this->basePath), '/\\\\');\n        }\n        if ($this->baseUrl !== null) {\n            $this->baseUrl = rtrim(Yii::getAlias($this->baseUrl), '/');\n        }\n    }\n\n    /**\n     * Registers the CSS and JS files with the given view.\n     * @param \\yii\\web\\View $view the view that the asset files are to be registered with.\n     */\n    public function registerAssetFiles($view)\n    {\n        $manager = $view->getAssetManager();\n        foreach ($this->js as $js) {\n            if (is_array($js)) {\n                $file = array_shift($js);\n                $options = ArrayHelper::merge($this->jsOptions, $js);\n                $view->registerJsFile($manager->getAssetUrl($this, $file, ArrayHelper::getValue($options, 'appendTimestamp')), $options);\n            } elseif ($js !== null) {\n                $view->registerJsFile($manager->getAssetUrl($this, $js), $this->jsOptions);\n            }\n        }\n        foreach ($this->css as $css) {\n            if (is_array($css)) {\n                $file = array_shift($css);\n                $options = ArrayHelper::merge($this->cssOptions, $css);\n                $view->registerCssFile($manager->getAssetUrl($this, $file, ArrayHelper::getValue($options, 'appendTimestamp')), $options);\n            } elseif ($css !== null) {\n                $view->registerCssFile($manager->getAssetUrl($this, $css), $this->cssOptions);\n            }\n        }\n    }\n\n    /**\n     * Publishes the asset bundle if its source code is not under Web-accessible directory.\n     * It will also try to convert non-CSS or JS files (e.g. LESS, Sass) into the corresponding\n     * CSS or JS files using [[AssetManager::converter|asset converter]].\n     * @param AssetManager $am the asset manager to perform the asset publishing\n     */\n    public function publish($am)\n    {\n        if ($this->sourcePath !== null && !isset($this->basePath, $this->baseUrl)) {\n            list($this->basePath, $this->baseUrl) = $am->publish($this->sourcePath, $this->publishOptions);\n        }\n\n        if (isset($this->basePath, $this->baseUrl) && ($converter = $am->getConverter()) !== null) {\n            foreach ($this->js as $i => $js) {\n                if (is_array($js)) {\n                    $file = array_shift($js);\n                    if (Url::isRelative($file)) {\n                        $js = ArrayHelper::merge($this->jsOptions, $js);\n                        array_unshift($js, $converter->convert($file, $this->basePath));\n                        $this->js[$i] = $js;\n                    }\n                } elseif (Url::isRelative($js)) {\n                    $this->js[$i] = $converter->convert($js, $this->basePath);\n                }\n            }\n            foreach ($this->css as $i => $css) {\n                if (is_array($css)) {\n                    $file = array_shift($css);\n                    if (Url::isRelative($file)) {\n                        $css = ArrayHelper::merge($this->cssOptions, $css);\n                        array_unshift($css, $converter->convert($file, $this->basePath));\n                        $this->css[$i] = $css;\n                    }\n                } elseif (Url::isRelative($css)) {\n                    $this->css[$i] = $converter->convert($css, $this->basePath);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "framework/web/AssetConverter.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\nuse Yii;\nuse yii\\base\\Component;\nuse yii\\base\\Exception;\n\n/**\n * AssetConverter supports conversion of several popular formats into JS or CSS files.\n *\n * It is used by [[AssetManager]] to convert files after they have been published.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass AssetConverter extends Component implements AssetConverterInterface\n{\n    /**\n     * @var array the commands that are used to perform the asset conversion.\n     * The keys are the asset file extension names, and the values are the corresponding\n     * target script types (either \"css\" or \"js\") and the commands used for the conversion.\n     *\n     * The command placeholders: `{from}` - source file, `{to}` - converted file.\n     *\n     * You may also use a [path alias](guide:concept-aliases) to specify the location of the command:\n     *\n     * ```\n     * [\n     *     'styl' => ['css', '@app/node_modules/bin/stylus < {from} > {to}'],\n     * ]\n     * ```\n     *\n     * Note: `Yii::getAlias()` can replace alias at the begin of the command only.\n     *\n     * @see https://sass-lang.com/documentation/cli/ SASS/SCSS\n     */\n    public $commands = [\n        'less' => ['css', 'lessc {from} {to} --no-color --source-map'],\n        'scss' => ['css', 'sass --style=compressed {from} {to}'],\n        'sass' => ['css', 'sass --style=compressed {from} {to}'],\n        'styl' => ['css', 'stylus < {from} > {to}'],\n        'coffee' => ['js', 'coffee -p {from} > {to}'],\n        'ts' => ['js', 'tsc --out {to} {from}'],\n    ];\n    /**\n     * @var bool whether the source asset file should be converted even if its result already exists.\n     * You may want to set this to be `true` during the development stage to make sure the converted\n     * assets are always up-to-date. Do not set this to true on production servers as it will\n     * significantly degrade the performance.\n     */\n    public $forceConvert = false;\n\n\n    /**\n     * Converts a given asset file into a CSS or JS file.\n     * @param string $asset the asset file path, relative to $basePath\n     * @param string $basePath the directory the $asset is relative to.\n     * @return string the converted asset file path, relative to $basePath.\n     */\n    public function convert($asset, $basePath)\n    {\n        $pos = strrpos($asset, '.');\n        if ($pos !== false) {\n            $ext = substr($asset, $pos + 1);\n            if (isset($this->commands[$ext])) {\n                list($ext, $command) = $this->commands[$ext];\n                $result = substr($asset, 0, $pos + 1) . $ext;\n                if ($this->forceConvert || @filemtime(\"$basePath/$result\") < @filemtime(\"$basePath/$asset\")) {\n                    $this->runCommand($command, $basePath, $asset, $result);\n                }\n\n                return $result;\n            }\n        }\n\n        return $asset;\n    }\n\n    /**\n     * Runs a command to convert asset files.\n     * @param string $command the command to run. If prefixed with an `@` it will be treated as a [path alias](guide:concept-aliases).\n     * @param string $basePath asset base path and command working directory\n     * @param string $asset the name of the asset file\n     * @param string $result the name of the file to be generated by the converter command\n     * @return bool true on success, false on failure. Failures will be logged.\n     * @throws \\yii\\base\\Exception when the command fails and YII_DEBUG is true.\n     * In production mode the error will be logged.\n     */\n    protected function runCommand($command, $basePath, $asset, $result)\n    {\n        $command = Yii::getAlias($command);\n\n        $command = strtr($command, [\n            '{from}' => escapeshellarg(\"$basePath/$asset\"),\n            '{to}' => escapeshellarg(\"$basePath/$result\"),\n        ]);\n        $descriptor = [\n            1 => ['pipe', 'w'],\n            2 => ['pipe', 'w'],\n        ];\n        $pipes = [];\n        $proc = proc_open($command, $descriptor, $pipes, $basePath);\n        $stdout = stream_get_contents($pipes[1]);\n        $stderr = stream_get_contents($pipes[2]);\n        foreach ($pipes as $pipe) {\n            fclose($pipe);\n        }\n        $status = proc_close($proc);\n\n        if ($status === 0) {\n            Yii::debug(\"Converted $asset into $result:\\nSTDOUT:\\n$stdout\\nSTDERR:\\n$stderr\", __METHOD__);\n        } elseif (YII_DEBUG) {\n            throw new Exception(\"AssetConverter command '$command' failed with exit code $status:\\nSTDOUT:\\n$stdout\\nSTDERR:\\n$stderr\");\n        } else {\n            Yii::error(\"AssetConverter command '$command' failed with exit code $status:\\nSTDOUT:\\n$stdout\\nSTDERR:\\n$stderr\", __METHOD__);\n        }\n\n        return $status === 0;\n    }\n}\n"
  },
  {
    "path": "framework/web/AssetConverterInterface.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\n/**\n * The AssetConverterInterface must be implemented by asset converter classes.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\ninterface AssetConverterInterface\n{\n    /**\n     * Converts a given asset file into a CSS or JS file.\n     * @param string $asset the asset file path, relative to $basePath\n     * @param string $basePath the directory the $asset is relative to.\n     * @return string the converted asset file path, relative to $basePath.\n     */\n    public function convert($asset, $basePath);\n}\n"
  },
  {
    "path": "framework/web/AssetManager.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\nuse Yii;\nuse yii\\base\\Component;\nuse yii\\base\\InvalidArgumentException;\nuse yii\\base\\InvalidConfigException;\nuse yii\\helpers\\FileHelper;\nuse yii\\helpers\\Url;\n\n/**\n * AssetManager manages asset bundle configuration and loading.\n *\n * AssetManager is configured as an application component in [[\\yii\\web\\Application]] by default.\n * You can access that instance via `Yii::$app->assetManager`.\n *\n * You can modify its configuration by adding an array to your application config under `components`\n * as shown in the following example:\n *\n * ```\n * 'assetManager' => [\n *     'bundles' => [\n *         // you can override AssetBundle configs here\n *     ],\n * ]\n * ```\n *\n * For more details and usage information on AssetManager, see the [guide article on assets](guide:structure-assets).\n *\n * @property AssetConverterInterface $converter The asset converter. Note that the type of this property\n * differs in getter and setter. See [[getConverter()]] and [[setConverter()]] for details.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @phpstan-type PublishOptions array{\n *     only?: string[],\n *     except?: string[],\n *     caseSensitive?: bool,\n *     beforeCopy?: callable,\n *     afterCopy?: callable,\n *     forceCopy?: bool,\n * }\n */\nclass AssetManager extends Component\n{\n    /**\n     * @var array|false list of asset bundle configurations. This property is provided to customize asset bundles.\n     * When a bundle is being loaded by [[getBundle()]], if it has a corresponding configuration specified here,\n     * the configuration will be applied to the bundle.\n     *\n     * The array keys are the asset bundle names, which typically are asset bundle class names without leading backslash.\n     * The array values are the corresponding configurations. If a value is false, it means the corresponding asset\n     * bundle is disabled and [[getBundle()]] should return null.\n     *\n     * If this property is false, it means the whole asset bundle feature is disabled and [[getBundle()]]\n     * will always return null.\n     *\n     * The following example shows how to disable the bootstrap css file used by Bootstrap widgets\n     * (because you want to use your own styles):\n     *\n     * ```\n     * [\n     *     'yii\\bootstrap\\BootstrapAsset' => [\n     *         'css' => [],\n     *     ],\n     * ]\n     * ```\n     */\n    public $bundles = [];\n    /**\n     * @var string the root directory storing the published asset files.\n     */\n    public $basePath = '@webroot/assets';\n    /**\n     * @var string the base URL through which the published asset files can be accessed.\n     */\n    public $baseUrl = '@web/assets';\n    /**\n     * @var string[] mapping from source asset files (keys) to target asset files (values).\n     *\n     * This property is provided to support fixing incorrect asset file paths in some asset bundles.\n     * When an asset bundle is registered with a view, each relative asset file in its [[AssetBundle::css|css]]\n     * and [[AssetBundle::js|js]] arrays will be examined against this map. If any of the keys is found\n     * to be the last part of an asset file (which is prefixed with [[AssetBundle::sourcePath]] if available),\n     * the corresponding value will replace the asset and be registered with the view.\n     * For example, an asset file `my/path/to/jquery.js` matches a key `jquery.js`.\n     *\n     * Note that the target asset files should be absolute URLs, domain relative URLs (starting from '/') or paths\n     * relative to [[baseUrl]] and [[basePath]].\n     *\n     * In the following example, any assets ending with `jquery.min.js` will be replaced with `jquery/dist/jquery.js`\n     * which is relative to [[baseUrl]] and [[basePath]].\n     *\n     * ```\n     * [\n     *     'jquery.min.js' => 'jquery/dist/jquery.js',\n     * ]\n     * ```\n     *\n     * You may also use aliases while specifying map value, for example:\n     *\n     * ```\n     * [\n     *     'jquery.min.js' => '@web/js/jquery/jquery.js',\n     * ]\n     * ```\n     */\n    public $assetMap = [];\n    /**\n     * @var bool whether to use symbolic link to publish asset files. Defaults to false, meaning\n     * asset files are copied to [[basePath]]. Using symbolic links has the benefit that the published\n     * assets will always be consistent with the source assets and there is no copy operation required.\n     * This is especially useful during development.\n     *\n     * However, there are special requirements for hosting environments in order to use symbolic links.\n     * In particular, symbolic links are supported only on Linux/Unix, and Windows Vista/2008 or greater.\n     *\n     * Moreover, some Web servers need to be properly configured so that the linked assets are accessible\n     * to Web users. For example, for Apache Web server, the following configuration directive should be added\n     * for the Web folder:\n     *\n     * ```\n     * Options FollowSymLinks\n     * ```\n     */\n    public $linkAssets = false;\n    /**\n     * @var int|null the permission to be set for newly published asset files.\n     * This value will be used by PHP chmod() function. No umask will be applied.\n     * If not set, the permission will be determined by the current environment.\n     */\n    public $fileMode;\n    /**\n     * @var int the permission to be set for newly generated asset directories.\n     * This value will be used by PHP chmod() function. No umask will be applied.\n     * Defaults to 0775, meaning the directory is read-writable by owner and group,\n     * but read-only for other users.\n     */\n    public $dirMode = 0775;\n    /**\n     * @var callable|null a PHP callback that is called before copying each sub-directory or file.\n     * This option is used only when publishing a directory. If the callback returns false, the copy\n     * operation for the sub-directory or file will be cancelled.\n     *\n     * The signature of the callback should be: `function ($from, $to)`, where `$from` is the sub-directory or\n     * file to be copied from, while `$to` is the copy target.\n     *\n     * This is passed as a parameter `beforeCopy` to [[\\yii\\helpers\\FileHelper::copyDirectory()]].\n     */\n    public $beforeCopy;\n    /**\n     * @var callable|null a PHP callback that is called after a sub-directory or file is successfully copied.\n     * This option is used only when publishing a directory. The signature of the callback is the same as\n     * for [[beforeCopy]].\n     * This is passed as a parameter `afterCopy` to [[\\yii\\helpers\\FileHelper::copyDirectory()]].\n     */\n    public $afterCopy;\n    /**\n     * @var bool whether the directory being published should be copied even if\n     * it is found in the target directory. This option is used only when publishing a directory.\n     * You may want to set this to be `true` during the development stage to make sure the published\n     * directory is always up-to-date. Do not set this to true on production servers as it will\n     * significantly degrade the performance.\n     */\n    public $forceCopy = false;\n    /**\n     * @var bool whether to append a timestamp to the URL of every published asset. When this is true,\n     * the URL of a published asset may look like `/path/to/asset?v=timestamp`, where `timestamp` is the\n     * last modification time of the published asset file.\n     * You normally would want to set this property to true when you have enabled HTTP caching for assets,\n     * because it allows you to bust caching when the assets are updated.\n     * @since 2.0.3\n     */\n    public $appendTimestamp = false;\n    /**\n     * @var callable|null a callback that will be called to produce hash for asset directory generation.\n     * The signature of the callback should be as follows:\n     *\n     * ```\n     * function ($path)\n     * ```\n     *\n     * where `$path` is the asset path. Note that the `$path` can be either directory where the asset\n     * files reside or a single file. For a CSS file that uses relative path in `url()`, the hash\n     * implementation should use the directory path of the file instead of the file path to include\n     * the relative asset files in the copying.\n     *\n     * If this is not set, the asset manager will use the default CRC32 and filemtime in the `hash`\n     * method.\n     *\n     * Example of an implementation using MD4 hash:\n     *\n     * ```\n     * function ($path) {\n     *     return hash('md4', $path);\n     * }\n     * ```\n     *\n     * @since 2.0.6\n     */\n    public $hashCallback;\n\n    /**\n     * @var array\n     */\n    private $_dummyBundles = [];\n\n\n    /**\n     * Initializes the component.\n     * @throws InvalidConfigException if [[basePath]] does not exist.\n     */\n    public function init()\n    {\n        parent::init();\n        $this->basePath = Yii::getAlias($this->basePath);\n\n        $this->basePath = realpath($this->basePath);\n        $this->baseUrl = rtrim(Yii::getAlias($this->baseUrl), '/');\n    }\n\n    /**\n     * @var bool|null\n     */\n    private $_isBasePathPermissionChecked;\n\n    /**\n     * Check whether the basePath exists and is writeable.\n     *\n     * @since 2.0.40\n     */\n    public function checkBasePathPermission()\n    {\n        // if the check is been done already, skip further checks\n        if ($this->_isBasePathPermissionChecked) {\n            return;\n        }\n\n        if (!is_dir($this->basePath)) {\n            throw new InvalidConfigException(\"The directory does not exist: {$this->basePath}\");\n        }\n\n        if (!is_writable($this->basePath)) {\n            throw new InvalidConfigException(\"The directory is not writable by the Web process: {$this->basePath}\");\n        }\n\n        $this->_isBasePathPermissionChecked = true;\n    }\n\n    /**\n     * Returns the named asset bundle.\n     *\n     * This method will first look for the bundle in [[bundles]]. If not found,\n     * it will treat `$name` as the class of the asset bundle and create a new instance of it.\n     *\n     * @param string $name the class name of the asset bundle (without the leading backslash)\n     * @param bool $publish whether to publish the asset files in the asset bundle before it is returned.\n     * If you set this false, you must manually call `AssetBundle::publish()` to publish the asset files.\n     * @return AssetBundle the asset bundle instance\n     * @throws InvalidConfigException if $name does not refer to a valid asset bundle\n     */\n    public function getBundle($name, $publish = true)\n    {\n        if ($this->bundles === false) {\n            return $this->loadDummyBundle($name);\n        } elseif (!isset($this->bundles[$name])) {\n            return $this->bundles[$name] = $this->loadBundle($name, [], $publish);\n        } elseif ($this->bundles[$name] instanceof AssetBundle) {\n            return $this->bundles[$name];\n        } elseif (is_array($this->bundles[$name])) {\n            return $this->bundles[$name] = $this->loadBundle($name, $this->bundles[$name], $publish);\n        } elseif ($this->bundles[$name] === false) {\n            return $this->loadDummyBundle($name);\n        }\n\n        throw new InvalidConfigException(\"Invalid asset bundle configuration: $name\");\n    }\n\n    /**\n     * Loads asset bundle class by name.\n     *\n     * @param string $name bundle name\n     * @param array $config bundle object configuration\n     * @param bool $publish if bundle should be published\n     * @return AssetBundle\n     * @throws InvalidConfigException if configuration isn't valid\n     */\n    protected function loadBundle($name, $config = [], $publish = true)\n    {\n        if (!isset($config['class'])) {\n            $config['class'] = $name;\n        }\n        /** @var AssetBundle $bundle */\n        $bundle = Yii::createObject($config);\n        if ($publish) {\n            $bundle->publish($this);\n        }\n\n        return $bundle;\n    }\n\n    /**\n     * Loads dummy bundle by name.\n     *\n     * @param string $name\n     * @return AssetBundle\n     */\n    protected function loadDummyBundle($name)\n    {\n        if (!isset($this->_dummyBundles[$name])) {\n            $bundle = Yii::createObject(['class' => $name]);\n            $bundle->sourcePath = null;\n            $bundle->js = [];\n            $bundle->css = [];\n\n            $this->_dummyBundles[$name] = $bundle;\n        }\n\n        return $this->_dummyBundles[$name];\n    }\n\n    /**\n     * Returns the actual URL for the specified asset.\n     * The actual URL is obtained by prepending either [[AssetBundle::$baseUrl]] or [[AssetManager::$baseUrl]] to the given asset path.\n     * @param AssetBundle $bundle the asset bundle which the asset file belongs to\n     * @param string $asset the asset path. This should be one of the assets listed in [[AssetBundle::$js]] or [[AssetBundle::$css]].\n     * @param bool|null $appendTimestamp Whether to append timestamp to the URL.\n     * @return string the actual URL for the specified asset.\n     */\n    public function getAssetUrl($bundle, $asset, $appendTimestamp = null)\n    {\n        $assetUrl = $this->getActualAssetUrl($bundle, $asset);\n        $assetPath = $this->getAssetPath($bundle, $asset);\n\n        $withTimestamp = $this->appendTimestamp;\n        if ($appendTimestamp !== null) {\n            $withTimestamp = $appendTimestamp;\n        }\n\n        if ($withTimestamp && $assetPath && ($timestamp = @filemtime($assetPath)) > 0) {\n            return \"$assetUrl?v=$timestamp\";\n        }\n\n        return $assetUrl;\n    }\n\n    /**\n     * Returns the actual file path for the specified asset.\n     * @param AssetBundle $bundle the asset bundle which the asset file belongs to\n     * @param string $asset the asset path. This should be one of the assets listed in [[AssetBundle::$js]] or [[AssetBundle::$css]].\n     * @return string|false the actual file path, or `false` if the asset is specified as an absolute URL\n     */\n    public function getAssetPath($bundle, $asset)\n    {\n        if (($actualAsset = $this->resolveAsset($bundle, $asset)) !== false) {\n            return Url::isRelative($actualAsset) ? $this->basePath . '/' . $actualAsset : false;\n        }\n\n        return Url::isRelative($asset) ? $bundle->basePath . '/' . $asset : false;\n    }\n\n    /**\n     * @param AssetBundle $bundle\n     * @param string $asset\n     * @return string|false\n     */\n    protected function resolveAsset($bundle, $asset)\n    {\n        if (isset($this->assetMap[$asset])) {\n            return $this->assetMap[$asset];\n        }\n        if ($bundle->sourcePath !== null && Url::isRelative($asset)) {\n            $asset = $bundle->sourcePath . '/' . $asset;\n        }\n\n        $n = mb_strlen($asset, Yii::$app->charset);\n        foreach ($this->assetMap as $from => $to) {\n            $n2 = mb_strlen($from, Yii::$app->charset);\n            if ($n2 <= $n && substr_compare($asset, $from, $n - $n2, $n2) === 0) {\n                return $to;\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * @var array|string|AssetConverterInterface|null\n     */\n    private $_converter;\n\n    /**\n     * Returns the asset converter.\n     * @return AssetConverterInterface the asset converter.\n     */\n    public function getConverter()\n    {\n        if ($this->_converter === null) {\n            $this->_converter = Yii::createObject(AssetConverter::className());\n        } elseif (is_array($this->_converter) || is_string($this->_converter)) {\n            if (is_array($this->_converter) && !isset($this->_converter['class'])) {\n                $this->_converter['class'] = AssetConverter::className();\n            }\n            $this->_converter = Yii::createObject($this->_converter);\n        }\n\n        return $this->_converter;\n    }\n\n    /**\n     * Sets the asset converter.\n     * @param array|string|AssetConverterInterface $value the asset converter. This can be either\n     * an object implementing the [[AssetConverterInterface]], or a configuration\n     * array that can be used to create the asset converter object, or a class name.\n     */\n    public function setConverter($value)\n    {\n        $this->_converter = $value;\n    }\n\n    /**\n     * @var array published assets\n     */\n    private $_published = [];\n\n    /**\n     * Publishes a file or a directory.\n     *\n     * This method will copy the specified file or directory to [[basePath]] so that\n     * it can be accessed via the Web server.\n     *\n     * If the asset is a file, its file modification time will be checked to avoid\n     * unnecessary file copying.\n     *\n     * If the asset is a directory, all files and subdirectories under it will be published recursively.\n     * Note, in case $forceCopy is false the method only checks the existence of the target\n     * directory to avoid repetitive copying (which is very expensive).\n     *\n     * By default, when publishing a directory, subdirectories and files whose name starts with a dot \".\"\n     * will NOT be published. If you want to change this behavior, you may specify the \"beforeCopy\" option\n     * as explained in the `$options` parameter.\n     *\n     * Note: On rare scenario, a race condition can develop that will lead to a\n     * one-time-manifestation of a non-critical problem in the creation of the directory\n     * that holds the published assets. This problem can be avoided altogether by 'requesting'\n     * in advance all the resources that are supposed to trigger a 'publish()' call, and doing\n     * that in the application deployment phase, before system goes live. See more in the following\n     * discussion: https://code.google.com/archive/p/yii/issues/2579\n     *\n     * @param string $path the asset (file or directory) to be published\n     * @param PublishOptions $options the options to be applied when publishing a directory.\n     * The following options are supported:\n     *\n     * - only: array, list of patterns that the file paths should match if they want to be copied.\n     * - except: array, list of patterns that the files or directories should match if they want to be excluded from being copied.\n     * - caseSensitive: boolean, whether patterns specified at \"only\" or \"except\" should be case sensitive. Defaults to true.\n     * - beforeCopy: callback, a PHP callback that is called before copying each sub-directory or file.\n     *   This overrides [[beforeCopy]] if set.\n     * - afterCopy: callback, a PHP callback that is called after a sub-directory or file is successfully copied.\n     *   This overrides [[afterCopy]] if set.\n     * - forceCopy: boolean, whether the directory being published should be copied even if\n     *   it is found in the target directory. This option is used only when publishing a directory.\n     *   This overrides [[forceCopy]] if set.\n     *\n     * @return array the path (directory or file path) and the URL that the asset is published as.\n     * @throws InvalidArgumentException if the asset to be published does not exist.\n     * @throws InvalidConfigException if the target directory [[basePath]] is not writeable.\n     */\n    public function publish($path, $options = [])\n    {\n        $path = Yii::getAlias($path);\n\n        if (isset($this->_published[$path])) {\n            return $this->_published[$path];\n        }\n\n        if (!is_string($path) || ($src = realpath($path)) === false) {\n            throw new InvalidArgumentException(\"The file or directory to be published does not exist: $path\");\n        }\n\n        if (!is_readable($path)) {\n            throw new InvalidArgumentException(\"The file or directory to be published is not readable: $path\");\n        }\n\n        if (is_file($src)) {\n            return $this->_published[$path] = $this->publishFile($src);\n        }\n\n        return $this->_published[$path] = $this->publishDirectory($src, $options);\n    }\n\n    /**\n     * Publishes a file.\n     * @param string $src the asset file to be published\n     * @return string[] the path and the URL that the asset is published as.\n     * @throws InvalidArgumentException if the asset to be published does not exist.\n     */\n    protected function publishFile($src)\n    {\n        $this->checkBasePathPermission();\n\n        $dir = $this->hash($src);\n        $fileName = basename($src);\n        $dstDir = $this->basePath . DIRECTORY_SEPARATOR . $dir;\n        $dstFile = $dstDir . DIRECTORY_SEPARATOR . $fileName;\n\n        if (!is_dir($dstDir)) {\n            FileHelper::createDirectory($dstDir, $this->dirMode, true);\n        }\n\n        if ($this->linkAssets) {\n            if (!is_file($dstFile)) {\n                try { // fix #6226 symlinking multi threaded\n                    symlink($src, $dstFile);\n                } catch (\\Exception $e) {\n                    if (!is_file($dstFile)) {\n                        throw $e;\n                    }\n                }\n            }\n        } elseif (@filemtime($dstFile) < @filemtime($src)) {\n            copy($src, $dstFile);\n            if ($this->fileMode !== null) {\n                @chmod($dstFile, $this->fileMode);\n            }\n        }\n\n        if ($this->appendTimestamp && ($timestamp = @filemtime($dstFile)) > 0) {\n            $fileName = $fileName . \"?v=$timestamp\";\n        }\n\n        return [$dstFile, $this->baseUrl . \"/$dir/$fileName\"];\n    }\n\n    /**\n     * Publishes a directory.\n     * @param string $src the asset directory to be published\n     * @param array $options the options to be applied when publishing a directory.\n     * The following options are supported:\n     *\n     * - only: array, list of patterns that the file paths should match if they want to be copied.\n     * - except: array, list of patterns that the files or directories should match if they want to be excluded from being copied.\n     * - caseSensitive: boolean, whether patterns specified at \"only\" or \"except\" should be case sensitive. Defaults to true.\n     * - beforeCopy: callback, a PHP callback that is called before copying each sub-directory or file.\n     *   This overrides [[beforeCopy]] if set.\n     * - afterCopy: callback, a PHP callback that is called after a sub-directory or file is successfully copied.\n     *   This overrides [[afterCopy]] if set.\n     * - forceCopy: boolean, whether the directory being published should be copied even if\n     *   it is found in the target directory. This option is used only when publishing a directory.\n     *   This overrides [[forceCopy]] if set.\n     *\n     * @return string[] the path directory and the URL that the asset is published as.\n     * @throws InvalidArgumentException if the asset to be published does not exist.\n     */\n    protected function publishDirectory($src, $options)\n    {\n        $this->checkBasePathPermission();\n\n        $dir = $this->hash($src);\n        $dstDir = $this->basePath . DIRECTORY_SEPARATOR . $dir;\n        if ($this->linkAssets) {\n            if (!is_dir($dstDir)) {\n                FileHelper::createDirectory(dirname($dstDir), $this->dirMode, true);\n                try { // fix #6226 symlinking multi threaded\n                    symlink($src, $dstDir);\n                } catch (\\Exception $e) {\n                    if (!is_dir($dstDir)) {\n                        throw $e;\n                    }\n                }\n            }\n        } elseif (!empty($options['forceCopy']) || ($this->forceCopy && !isset($options['forceCopy'])) || !is_dir($dstDir)) {\n            $opts = array_merge(\n                $options,\n                [\n                    'dirMode' => $this->dirMode,\n                    'fileMode' => $this->fileMode,\n                    'copyEmptyDirectories' => false,\n                ]\n            );\n            if (!isset($opts['beforeCopy'])) {\n                if ($this->beforeCopy !== null) {\n                    $opts['beforeCopy'] = $this->beforeCopy;\n                } else {\n                    $opts['beforeCopy'] = function ($from, $to) {\n                        return strncmp(basename($from), '.', 1) !== 0;\n                    };\n                }\n            }\n            if (!isset($opts['afterCopy']) && $this->afterCopy !== null) {\n                $opts['afterCopy'] = $this->afterCopy;\n            }\n            FileHelper::copyDirectory($src, $dstDir, $opts);\n        }\n\n        return [$dstDir, $this->baseUrl . '/' . $dir];\n    }\n\n    /**\n     * Returns the published path of a file path.\n     * This method does not perform any publishing. It merely tells you\n     * if the file or directory is published, where it will go.\n     * @param string $path directory or file path being published\n     * @return string|false string the published file path. False if the file or directory does not exist\n     */\n    public function getPublishedPath($path)\n    {\n        $path = Yii::getAlias($path);\n\n        if (isset($this->_published[$path])) {\n            return $this->_published[$path][0];\n        }\n        if (is_string($path) && ($path = realpath($path)) !== false) {\n            return $this->basePath . DIRECTORY_SEPARATOR . $this->hash($path) . (is_file($path) ? DIRECTORY_SEPARATOR . basename($path) : '');\n        }\n\n        return false;\n    }\n\n    /**\n     * Returns the URL of a published file path.\n     * This method does not perform any publishing. It merely tells you\n     * if the file path is published, what the URL will be to access it.\n     * @param string $path directory or file path being published\n     * @return string|false string the published URL for the file or directory. False if the file or directory does not exist.\n     */\n    public function getPublishedUrl($path)\n    {\n        $path = Yii::getAlias($path);\n\n        if (isset($this->_published[$path])) {\n            return $this->_published[$path][1];\n        }\n        if (is_string($path) && ($path = realpath($path)) !== false) {\n            return $this->baseUrl . '/' . $this->hash($path) . (is_file($path) ? '/' . basename($path) : '');\n        }\n\n        return false;\n    }\n\n    /**\n     * Generate a CRC32 hash for the directory path. Collisions are higher\n     * than MD5 but generates a much smaller hash string.\n     * @param string $path string to be hashed.\n     * @return string hashed string.\n     */\n    protected function hash($path)\n    {\n        if (is_callable($this->hashCallback)) {\n            return call_user_func($this->hashCallback, $path);\n        }\n        $path = (is_file($path) ? dirname($path) : $path) . filemtime($path);\n        return sprintf('%x', crc32($path . Yii::getVersion() . '|' . $this->linkAssets));\n    }\n\n    /**\n     * Returns the actual URL for the specified asset. Without parameters.\n     * The actual URL is obtained by prepending either [[AssetBundle::$baseUrl]] or [[AssetManager::$baseUrl]] to the given asset path.\n     * @param AssetBundle $bundle the asset bundle which the asset file belongs to\n     * @param string $asset the asset path. This should be one of the assets listed in [[AssetBundle::$js]] or [[AssetBundle::$css]].\n     * @return string the actual URL for the specified asset.\n     * @since 2.0.39\n     */\n    public function getActualAssetUrl($bundle, $asset)\n    {\n        if (($actualAsset = $this->resolveAsset($bundle, $asset)) !== false) {\n            if (strncmp($actualAsset, '@web/', 5) === 0) {\n                $asset = substr($actualAsset, 5);\n                $baseUrl = Yii::getAlias('@web');\n            } else {\n                $asset = Yii::getAlias($actualAsset);\n                $baseUrl = $this->baseUrl;\n            }\n        } else {\n            $baseUrl = $bundle->baseUrl;\n        }\n\n        if (!Url::isRelative($asset) || strncmp($asset, '/', 1) === 0) {\n            return $asset;\n        }\n\n        return \"$baseUrl/$asset\";\n    }\n}\n"
  },
  {
    "path": "framework/web/BadRequestHttpException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\n/**\n * BadRequestHttpException represents a \"Bad Request\" HTTP exception with status code 400.\n *\n * Use this exception to represent a generic client error. In many cases, there\n * may be an HTTP exception that more precisely describes the error. In that\n * case, consider using the more precise exception to provide the user with\n * additional information.\n *\n * @see https://tools.ietf.org/html/rfc7231#section-6.5.1\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass BadRequestHttpException extends HttpException\n{\n    /**\n     * Constructor.\n     * @param string|null $message error message\n     * @param int $code error code\n     * @param \\Throwable|null $previous The previous exception used for the exception chaining.\n     */\n    public function __construct($message = null, $code = 0, $previous = null)\n    {\n        parent::__construct(400, $message, $code, $previous);\n    }\n}\n"
  },
  {
    "path": "framework/web/CacheSession.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\nuse yii\\caching\\CacheInterface;\nuse yii\\di\\Instance;\n\n/**\n * CacheSession implements a session component using cache as storage medium.\n *\n * The cache being used can be any cache application component.\n * The ID of the cache application component is specified via [[cache]], which defaults to 'cache'.\n *\n * Beware, by definition cache storage are volatile, which means the data stored on them\n * may be swapped out and get lost. Therefore, you must make sure the cache used by this component\n * is NOT volatile. If you want to use database as storage medium, [[DbSession]] is a better choice.\n *\n * The following example shows how you can configure the application to use CacheSession:\n * Add the following to your application config under `components`:\n *\n * ```\n * 'session' => [\n *     'class' => 'yii\\web\\CacheSession',\n *     // 'cache' => 'mycache',\n * ]\n * ```\n *\n * @property-read bool $useCustomStorage Whether to use custom storage.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass CacheSession extends Session\n{\n    /**\n     * @var CacheInterface|array|string the cache object or the application component ID of the cache object.\n     * The session data will be stored using this cache object.\n     *\n     * After the CacheSession object is created, if you want to change this property,\n     * you should only assign it with a cache object.\n     *\n     * Starting from version 2.0.2, this can also be a configuration array for creating the object.\n     */\n    public $cache = 'cache';\n\n\n    /**\n     * Initializes the application component.\n     */\n    public function init()\n    {\n        parent::init();\n        $this->cache = Instance::ensure($this->cache, 'yii\\caching\\CacheInterface');\n    }\n\n    /**\n     * Returns a value indicating whether to use custom session storage.\n     * This method overrides the parent implementation and always returns true.\n     * @return bool whether to use custom storage.\n     */\n    public function getUseCustomStorage()\n    {\n        return true;\n    }\n\n    /**\n     * Session open handler.\n     * @internal Do not call this method directly.\n     * @param string $savePath session save path\n     * @param string $sessionName session name\n     * @return bool whether session is opened successfully\n     */\n    public function openSession($savePath, $sessionName)\n    {\n        if ($this->getUseStrictMode()) {\n            $id = $this->getId();\n            if (!$this->cache->exists($this->calculateKey($id))) {\n                //This session id does not exist, mark it for forced regeneration\n                $this->_forceRegenerateId = $id;\n            }\n        }\n\n        return parent::openSession($savePath, $sessionName);\n    }\n\n    /**\n     * Session read handler.\n     * @internal Do not call this method directly.\n     * @param string $id session ID\n     * @return string|false the session data, or false on failure\n     */\n    public function readSession($id)\n    {\n        $data = $this->cache->get($this->calculateKey($id));\n\n        return $data === false ? '' : $data;\n    }\n\n    /**\n     * Session write handler.\n     * @internal Do not call this method directly.\n     * @param string $id session ID\n     * @param string $data session data\n     * @return bool whether session write is successful\n     */\n    public function writeSession($id, $data)\n    {\n        if ($this->getUseStrictMode() && $id === $this->_forceRegenerateId) {\n            //Ignore write when forceRegenerate is active for this id\n            return true;\n        }\n\n        return $this->cache->set($this->calculateKey($id), $data, $this->getTimeout());\n    }\n\n    /**\n     * Session destroy handler.\n     * @internal Do not call this method directly.\n     * @param string $id session ID\n     * @return bool whether session is destroyed successfully\n     */\n    public function destroySession($id)\n    {\n        $cacheId = $this->calculateKey($id);\n        if ($this->cache->exists($cacheId) === false) {\n            return true;\n        }\n\n        return $this->cache->delete($cacheId);\n    }\n\n    /**\n     * Generates a unique key used for storing session data in cache.\n     * @param string $id session variable name\n     * @return mixed a safe cache key associated with the session variable name\n     */\n    protected function calculateKey($id)\n    {\n        return [__CLASS__, $id];\n    }\n}\n"
  },
  {
    "path": "framework/web/CompositeUrlRule.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\nuse Yii;\nuse yii\\base\\BaseObject;\n\n/**\n * CompositeUrlRule is the base class for URL rule classes that consist of multiple simpler rules.\n *\n * @property-read int|null $createUrlStatus Status of the URL creation after the last [[createUrl()]] call.\n * `null` if rule does not provide info about create status.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nabstract class CompositeUrlRule extends BaseObject implements UrlRuleInterface\n{\n    /**\n     * @var UrlRuleInterface[]|UrlRuleInterface[][]|array[]|string[] the URL rules contained in this composite rule.\n     * This property is set in [[init()]] by the return value of [[createRules()]].\n     */\n    protected $rules = [];\n    /**\n     * @var int|null status of the URL creation after the last [[createUrl()]] call.\n     * @since 2.0.12\n     */\n    protected $createStatus;\n\n\n    /**\n     * Creates the URL rules that should be contained within this composite rule.\n     * @return UrlRuleInterface[]|UrlRuleInterface[][] the URL rules\n     */\n    abstract protected function createRules();\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        parent::init();\n        $this->rules = $this->createRules();\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function parseRequest($manager, $request)\n    {\n        foreach ($this->rules as $rule) {\n            /** @var UrlRule $rule */\n            $result = $rule->parseRequest($manager, $request);\n            if (YII_DEBUG) {\n                Yii::debug([\n                    'rule' => method_exists($rule, '__toString') ? $rule->__toString() : get_class($rule),\n                    'match' => $result !== false,\n                    'parent' => self::className(),\n                ], __METHOD__);\n            }\n            if ($result !== false) {\n                return $result;\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function createUrl($manager, $route, $params)\n    {\n        $this->createStatus = UrlRule::CREATE_STATUS_SUCCESS;\n        $url = $this->iterateRules($this->rules, $manager, $route, $params);\n        if ($url !== false) {\n            return $url;\n        }\n\n        if ($this->createStatus === UrlRule::CREATE_STATUS_SUCCESS) {\n            // create status was not changed - there is no rules configured\n            $this->createStatus = UrlRule::CREATE_STATUS_PARSING_ONLY;\n        }\n\n        return false;\n    }\n\n    /**\n     * Iterates through specified rules and calls [[createUrl()]] for each of them.\n     *\n     * @param UrlRuleInterface[] $rules rules to iterate.\n     * @param UrlManager $manager the URL manager\n     * @param string $route the route. It should not have slashes at the beginning or the end.\n     * @param array $params the parameters\n     * @return bool|string the created URL, or `false` if none of specified rules cannot be used for creating this URL.\n     * @see createUrl()\n     * @since 2.0.12\n     */\n    protected function iterateRules($rules, $manager, $route, $params)\n    {\n        /** @var UrlRule $rule */\n        foreach ($rules as $rule) {\n            $url = $rule->createUrl($manager, $route, $params);\n            if ($url !== false) {\n                $this->createStatus = UrlRule::CREATE_STATUS_SUCCESS;\n                return $url;\n            }\n            if (\n                $this->createStatus === null\n                || !method_exists($rule, 'getCreateUrlStatus')\n                || $rule->getCreateUrlStatus() === null\n            ) {\n                $this->createStatus = null;\n            } else {\n                $this->createStatus |= $rule->getCreateUrlStatus();\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * Returns status of the URL creation after the last [[createUrl()]] call.\n     *\n     * For multiple rules statuses will be combined by bitwise `or` operator\n     * (e.g. `UrlRule::CREATE_STATUS_PARSING_ONLY | UrlRule::CREATE_STATUS_PARAMS_MISMATCH`).\n     *\n     * @return int|null Status of the URL creation after the last [[createUrl()]] call. `null` if rule does not provide\n     * info about create status.\n     * @see createStatus\n     * @see https://www.php.net/manual/en/language.operators.bitwise.php\n     * @since 2.0.12\n     */\n    public function getCreateUrlStatus()\n    {\n        return $this->createStatus;\n    }\n}\n"
  },
  {
    "path": "framework/web/ConflictHttpException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\n/**\n * ConflictHttpException represents a \"Conflict\" HTTP exception with status code 409.\n *\n * @see https://tools.ietf.org/html/rfc7231#section-6.5.8\n * @author Dan Schmidt <danschmidt5189@gmail.com>\n * @since 2.0\n */\nclass ConflictHttpException extends HttpException\n{\n    /**\n     * Constructor.\n     * @param string|null $message error message\n     * @param int $code error code\n     * @param \\Throwable|null $previous The previous exception used for the exception chaining.\n     */\n    public function __construct($message = null, $code = 0, $previous = null)\n    {\n        parent::__construct(409, $message, $code, $previous);\n    }\n}\n"
  },
  {
    "path": "framework/web/Controller.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\nuse Yii;\nuse yii\\base\\Exception;\nuse yii\\base\\InlineAction;\nuse yii\\helpers\\Url;\nuse yii\\base\\Action;\nuse yii\\base\\Controller as BaseController;\nuse yii\\base\\Module;\n\n/**\n * Controller is the base class of web controllers.\n *\n * For more details and usage information on Controller, see the [guide article on controllers](guide:structure-controllers).\n *\n * @property Request $request The request object.\n * @property Response $response The response object.\n * @property View $view The view object that can be used to render views or view files.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @template T of Module = Module\n * @extends BaseController<T>\n */\nclass Controller extends BaseController\n{\n    /**\n     * @var bool whether to enable CSRF validation for the actions in this controller.\n     * CSRF validation is enabled only when both this property and [[\\yii\\web\\Request::enableCsrfValidation]] are true.\n     */\n    public $enableCsrfValidation = true;\n    /**\n     * @var array the parameters bound to the current action.\n     */\n    public $actionParams = [];\n\n\n    /**\n     * Renders a view in response to an AJAX request.\n     *\n     * This method is similar to [[renderPartial()]] except that it will inject into\n     * the rendering result with JS/CSS scripts and files which are registered with the view.\n     * For this reason, you should use this method instead of [[renderPartial()]] to render\n     * a view to respond to an AJAX request.\n     *\n     * @param string $view the view name. Please refer to [[render()]] on how to specify a view name.\n     * @param array $params the parameters (name-value pairs) that should be made available in the view.\n     * @return string the rendering result.\n     */\n    public function renderAjax($view, $params = [])\n    {\n        /** @var View $viewComponent */\n        $viewComponent = $this->getView();\n\n        return $viewComponent->renderAjax($view, $params, $this);\n    }\n\n    /**\n     * Send data formatted as JSON.\n     *\n     * This method is a shortcut for sending data formatted as JSON. It will return\n     * the [[Application::getResponse()|response]] application component after configuring\n     * the [[Response::$format|format]] and setting the [[Response::$data|data]] that should\n     * be formatted. A common usage will be:\n     *\n     * ```\n     * return $this->asJson($data);\n     * ```\n     *\n     * @param mixed $data the data that should be formatted.\n     * @return Response a response that is configured to send `$data` formatted as JSON.\n     * @since 2.0.11\n     * @see Response::$format\n     * @see Response::FORMAT_JSON\n     * @see JsonResponseFormatter\n     */\n    public function asJson($data)\n    {\n        $this->response->format = Response::FORMAT_JSON;\n        $this->response->data = $data;\n        return $this->response;\n    }\n\n    /**\n     * Send data formatted as XML.\n     *\n     * This method is a shortcut for sending data formatted as XML. It will return\n     * the [[Application::getResponse()|response]] application component after configuring\n     * the [[Response::$format|format]] and setting the [[Response::$data|data]] that should\n     * be formatted. A common usage will be:\n     *\n     * ```\n     * return $this->asXml($data);\n     * ```\n     *\n     * @param mixed $data the data that should be formatted.\n     * @return Response a response that is configured to send `$data` formatted as XML.\n     * @since 2.0.11\n     * @see Response::$format\n     * @see Response::FORMAT_XML\n     * @see XmlResponseFormatter\n     */\n    public function asXml($data)\n    {\n        $this->response->format = Response::FORMAT_XML;\n        $this->response->data = $data;\n        return $this->response;\n    }\n\n    /**\n     * Binds the parameters to the action.\n     * This method is invoked by [[Action]] when it begins to run with the given parameters.\n     * This method will check the parameter names that the action requires and return\n     * the provided parameters according to the requirement. If there is any missing parameter,\n     * an exception will be thrown.\n     * @param Action<static> $action the action to be bound with parameters\n     * @param array<array-key, mixed> $params the parameters to be bound to the action\n     * @return mixed[] the valid parameters that the action can run with.\n     * @throws BadRequestHttpException if there are missing or invalid parameters.\n     *\n     * @phpstan-param Action<static> $action\n     * @psalm-param Action<self> $action\n     */\n    public function bindActionParams($action, $params)\n    {\n        if ($action instanceof InlineAction) {\n            $method = new \\ReflectionMethod($this, $action->actionMethod);\n        } else {\n            $method = new \\ReflectionMethod($action, 'run');\n        }\n\n        $args = [];\n        $missing = [];\n        $actionParams = [];\n        $requestedParams = [];\n        foreach ($method->getParameters() as $param) {\n            $name = $param->getName();\n            if (array_key_exists($name, $params)) {\n                $isValid = true;\n                $type = $param->getType();\n                if ($type instanceof \\ReflectionNamedType) {\n                    [$result, $isValid] = $this->filterSingleTypeActionParam($params[$name], $type);\n                    $params[$name] = $result;\n                } elseif ($type instanceof \\ReflectionUnionType) {\n                    [$result, $isValid] = $this->filterUnionTypeActionParam($params[$name], $type);\n                    $params[$name] = $result;\n                }\n\n                if (!$isValid) {\n                    throw new BadRequestHttpException(\n                        Yii::t('yii', 'Invalid data received for parameter \"{param}\".', ['param' => $name])\n                    );\n                }\n                $args[] = $actionParams[$name] = $params[$name];\n                unset($params[$name]);\n            } elseif (\n                PHP_VERSION_ID >= 70100\n                && ($type = $param->getType()) !== null\n                && $type instanceof \\ReflectionNamedType\n                && !$type->isBuiltin()\n            ) {\n                try {\n                    $this->bindInjectedParams($type, $name, $args, $requestedParams);\n                } catch (HttpException $e) {\n                    throw $e;\n                } catch (Exception $e) {\n                    throw new ServerErrorHttpException($e->getMessage(), 0, $e);\n                }\n            } elseif ($param->isDefaultValueAvailable()) {\n                $args[] = $actionParams[$name] = $param->getDefaultValue();\n            } else {\n                $missing[] = $name;\n            }\n        }\n\n        if (!empty($missing)) {\n            throw new BadRequestHttpException(\n                Yii::t('yii', 'Missing required parameters: {params}', ['params' => implode(', ', $missing)])\n            );\n        }\n\n        $this->actionParams = $actionParams;\n\n        // We use a different array here, specifically one that doesn't contain service instances but descriptions instead.\n        if (Yii::$app->requestedParams === null) {\n            Yii::$app->requestedParams = array_merge($actionParams, $requestedParams);\n        }\n\n        return $args;\n    }\n\n    /**\n     * The logic for [[bindActionParam]] to validate whether a given parameter matches the action's typing\n     * if the function parameter has a single named type.\n     * @param mixed $param The parameter value.\n     * @param \\ReflectionNamedType $type\n     * @return array{mixed, bool} The resulting parameter value and a boolean indicating whether the value is valid.\n     */\n    private function filterSingleTypeActionParam($param, $type)\n    {\n        $isArray = $type->getName() === 'array';\n        if ($isArray) {\n            return [(array)$param, true];\n        }\n        $isMixed = $type->getName() === 'mixed';\n        if ($isMixed) {\n            return [$param, true];\n        }\n\n        if (is_array($param)) {\n            return [$param, false];\n        }\n\n        if (\n            PHP_VERSION_ID >= 70000\n            && method_exists($type, 'isBuiltin')\n            && $type->isBuiltin()\n            && ($param !== null || !$type->allowsNull())\n        ) {\n            $typeName = PHP_VERSION_ID >= 70100 ? $type->getName() : (string)$type;\n            if ($param === '' && $type->allowsNull()) {\n                if ($typeName !== 'string') { // for old string behavior compatibility\n                    return [null, true];\n                }\n                return ['', true];\n            }\n\n            if ($typeName === 'string') {\n                return [$param, true];\n            }\n            $filterResult = $this->filterParamByType($param, $typeName);\n            return [$filterResult, $filterResult !== null];\n        }\n        return [$param, true];\n    }\n\n    /**\n     * The logic for [[bindActionParam]] to validate whether a given parameter matches the action's typing\n     * if the function parameter has a union type.\n     * @param mixed $param The parameter value.\n     * @param \\ReflectionUnionType $type\n     * @return array{mixed, bool} The resulting parameter value and a boolean indicating whether the value is valid.\n     */\n    private function filterUnionTypeActionParam($param, $type)\n    {\n        $types = $type->getTypes();\n        if ($param === '' && $type->allowsNull()) {\n            // check if type can be string for old string behavior compatibility\n            foreach ($types as $partialType) {\n                if (\n                    $partialType === null\n                    || !method_exists($partialType, 'isBuiltin')\n                    || !$partialType->isBuiltin()\n                ) {\n                    continue;\n                }\n                $typeName = PHP_VERSION_ID >= 70100 ? $partialType->getName() : (string)$partialType;\n                if ($typeName === 'string') {\n                    return ['', true];\n                }\n            }\n            return [null, true];\n        }\n        // if we found a built-in type but didn't return out, its validation failed\n        $foundBuiltinType = false;\n        // we save returning out an array or string for later because other types should take precedence\n        $canBeArray = false;\n        $canBeString = false;\n        foreach ($types as $partialType) {\n            if (\n                $partialType === null\n                || !method_exists($partialType, 'isBuiltin')\n                || !$partialType->isBuiltin()\n            ) {\n                continue;\n            }\n            $foundBuiltinType = true;\n            $typeName = PHP_VERSION_ID >= 70100 ? $partialType->getName() : (string)$partialType;\n            $canBeArray |= $typeName === 'array';\n            $canBeString |= $typeName === 'string';\n            if (is_array($param)) {\n                if ($canBeArray) {\n                    break;\n                }\n                continue;\n            }\n\n            $filterResult = $this->filterParamByType($param, $typeName);\n            if ($filterResult !== null) {\n                return [$filterResult, true];\n            }\n        }\n        if (!is_array($param) && $canBeString) {\n            return [$param, true];\n        }\n        if ($canBeArray) {\n            return [(array)$param, true];\n        }\n        return [$param, $canBeString || !$foundBuiltinType];\n    }\n\n    /**\n     * Run the according filter_var logic for teh given type.\n     * @param string $param The value to filter.\n     * @param string $typeName The type name.\n     * @return mixed|null The resulting value, or null if validation failed or the type can't be validated.\n     */\n    private function filterParamByType(string $param, string $typeName)\n    {\n        switch ($typeName) {\n            case 'int':\n                return filter_var($param, FILTER_VALIDATE_INT, FILTER_NULL_ON_FAILURE);\n            case 'float':\n                return filter_var($param, FILTER_VALIDATE_FLOAT, FILTER_NULL_ON_FAILURE);\n            case 'bool':\n                return filter_var($param, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);\n        }\n        return null;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function beforeAction($action)\n    {\n        if (parent::beforeAction($action)) {\n            if ($this->enableCsrfValidation && Yii::$app->getErrorHandler()->exception === null && !$this->request->validateCsrfToken()) {\n                throw new BadRequestHttpException(Yii::t('yii', 'Unable to verify your data submission.'));\n            }\n\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * Redirects the browser to the specified URL.\n     * This method is a shortcut to [[Response::redirect()]].\n     *\n     * You can use it in an action by returning the [[Response]] directly:\n     *\n     * ```\n     * // stop executing this action and redirect to login page\n     * return $this->redirect(['login']);\n     * ```\n     *\n     * @param string|array $url the URL to be redirected to. This can be in one of the following formats:\n     *\n     * - a string representing a URL (e.g. \"https://example.com\")\n     * - a string representing a URL alias (e.g. \"@example.com\")\n     * - an array in the format of `[$route, ...name-value pairs...]` (e.g. `['site/index', 'ref' => 1]`)\n     *   [[Url::to()]] will be used to convert the array into a URL.\n     *\n     * Any relative URL that starts with a single forward slash \"/\" will be converted\n     * into an absolute one by prepending it with the host info of the current request.\n     *\n     * @param int $statusCode the HTTP status code. Defaults to 302.\n     * See <https://tools.ietf.org/html/rfc2616#section-10>\n     * for details about HTTP status code\n     * @return Response the current response object\n     */\n    public function redirect($url, $statusCode = 302)\n    {\n        // calling Url::to() here because Response::redirect() modifies route before calling Url::to()\n        return $this->response->redirect(Url::to($url), $statusCode);\n    }\n\n    /**\n     * Redirects the browser to the home page.\n     *\n     * You can use this method in an action by returning the [[Response]] directly:\n     *\n     * ```\n     * // stop executing this action and redirect to home page\n     * return $this->goHome();\n     * ```\n     *\n     * @return Response the current response object\n     */\n    public function goHome()\n    {\n        return $this->response->redirect(Yii::$app->getHomeUrl());\n    }\n\n    /**\n     * Redirects the browser to the last visited page.\n     *\n     * You can use this method in an action by returning the [[Response]] directly:\n     *\n     * ```\n     * // stop executing this action and redirect to last visited page\n     * return $this->goBack();\n     * ```\n     *\n     * For this function to work you have to [[User::setReturnUrl()|set the return URL]] in appropriate places before.\n     *\n     * @param string|array|null $defaultUrl the default return URL in case it was not set previously.\n     * If this is null and the return URL was not set previously, [[Application::homeUrl]] will be redirected to.\n     * Please refer to [[User::setReturnUrl()]] on accepted format of the URL.\n     * @return Response the current response object\n     * @see User::getReturnUrl()\n     */\n    public function goBack($defaultUrl = null)\n    {\n        return $this->response->redirect(Yii::$app->getUser()->getReturnUrl($defaultUrl));\n    }\n\n    /**\n     * Refreshes the current page.\n     * This method is a shortcut to [[Response::refresh()]].\n     *\n     * You can use it in an action by returning the [[Response]] directly:\n     *\n     * ```\n     * // stop executing this action and refresh the current page\n     * return $this->refresh();\n     * ```\n     *\n     * @param string $anchor the anchor that should be appended to the redirection URL.\n     * Defaults to empty. Make sure the anchor starts with '#' if you want to specify it.\n     * @return Response the response object itself\n     */\n    public function refresh($anchor = '')\n    {\n        return $this->response->redirect($this->request->getUrl() . $anchor);\n    }\n}\n"
  },
  {
    "path": "framework/web/Cookie.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\n/**\n * Cookie represents information related with a cookie, such as [[name]], [[value]], [[domain]], etc.\n *\n * For more details and usage information on Cookie, see the [guide article on handling cookies](guide:runtime-sessions-cookies).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Cookie extends \\yii\\base\\BaseObject\n{\n    /**\n     * SameSite policy Lax will prevent the cookie from being sent by the browser in all cross-site browsing context\n     * during CSRF-prone request methods (e.g. POST, PUT, PATCH etc).\n     * E.g. a POST request from https://otherdomain.com to https://yourdomain.com will not include the cookie, however a GET request will.\n     * When a user follows a link from https://otherdomain.com to https://yourdomain.com it will include the cookie\n     * @see sameSite\n     */\n    public const SAME_SITE_LAX = 'Lax';\n    /**\n     * SameSite policy Strict will prevent the cookie from being sent by the browser in all cross-site browsing context\n     * regardless of the request method and even when following a regular link.\n     * E.g. a GET request from https://otherdomain.com to https://yourdomain.com or a user following a link from\n     * https://otherdomain.com to https://yourdomain.com will not include the cookie.\n     * @see sameSite\n     */\n    public const SAME_SITE_STRICT = 'Strict';\n    /**\n     * SameSite policy None disables the SameSite policy so cookies will be sent in all contexts,\n     * i.e in responses to both first-party and cross-origin requests.\n     * E.g. a POST request from https://otherdomain.com to https://yourdomain.com will include the cookie.\n     * Note: If `sameSite` is set to None, the `secure` attribute must be set to `true` (otherwise the cookie will be blocked by the browser).\n     * @see sameSite\n     * @see secure\n     * @since 2.0.43\n     */\n    public const SAME_SITE_NONE = 'None';\n    /**\n     * @var string name of the cookie\n     */\n    public $name;\n    /**\n     * @var string value of the cookie\n     */\n    public $value = '';\n    /**\n     * @var string domain of the cookie\n     */\n    public $domain = '';\n    /**\n     * @var int|string|\\DateTimeInterface|null the timestamp or date at which the cookie expires. This is the server timestamp.\n     * Defaults to 0, meaning \"until the browser is closed\" (the same applies to `null`).\n     */\n    public $expire = 0;\n    /**\n     * @var string the path on the server in which the cookie will be available on. The default is '/'.\n     */\n    public $path = '/';\n    /**\n     * @var bool whether cookie should be sent via secure connection\n     */\n    public $secure = false;\n    /**\n     * @var bool whether the cookie should be accessible only through the HTTP protocol.\n     * By setting this property to true, the cookie will not be accessible by scripting languages,\n     * such as JavaScript, which can effectively help to reduce identity theft through XSS attacks.\n     */\n    public $httpOnly = true;\n    /**\n     * @var string SameSite prevents the browser from sending this cookie along with cross-site requests.\n     *\n     * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite for more information about sameSite.\n     *\n     * @since 2.0.21\n     */\n    public $sameSite = self::SAME_SITE_LAX;\n\n\n    /**\n     * Magic method to turn a cookie object into a string without having to explicitly access [[value]].\n     *\n     * ```\n     * if (isset($request->cookies['name'])) {\n     *     $value = (string) $request->cookies['name'];\n     * }\n     * ```\n     *\n     * @return string The value of the cookie. If the value property is null, an empty string will be returned.\n     */\n    public function __toString()\n    {\n        return (string) $this->value;\n    }\n}\n"
  },
  {
    "path": "framework/web/CookieCollection.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\nuse ArrayIterator;\nuse Yii;\nuse yii\\base\\BaseObject;\nuse yii\\base\\InvalidCallException;\n\n/**\n * CookieCollection maintains the cookies available in the current request.\n *\n * For more details and usage information on CookieCollection, see the [guide article on handling cookies](guide:runtime-sessions-cookies).\n *\n * @property-read int $count The number of cookies in the collection.\n * @property-read ArrayIterator<string, Cookie> $iterator An iterator for traversing the cookies in the\n * collection.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @implements \\IteratorAggregate<string, Cookie>\n * @implements \\ArrayAccess<string, Cookie|null>\n */\nclass CookieCollection extends BaseObject implements \\IteratorAggregate, \\ArrayAccess, \\Countable\n{\n    /**\n     * @var bool whether this collection is read only.\n     */\n    public $readOnly = false;\n\n    /**\n     * @var array<string, Cookie> the cookies in this collection (indexed by the cookie names)\n     */\n    private $_cookies;\n\n\n    /**\n     * Constructor.\n     * @param array $cookies the cookies that this collection initially contains. This should be\n     * an array of name-value pairs.\n     * @param array $config name-value pairs that will be used to initialize the object properties\n     */\n    public function __construct($cookies = [], $config = [])\n    {\n        $this->_cookies = $cookies;\n        parent::__construct($config);\n    }\n\n    /**\n     * Returns an iterator for traversing the cookies in the collection.\n     * This method is required by the SPL interface [[\\IteratorAggregate]].\n     * It will be implicitly called when you use `foreach` to traverse the collection.\n     * @return ArrayIterator<string, Cookie> an iterator for traversing the cookies in the collection.\n     */\n    #[\\ReturnTypeWillChange]\n    public function getIterator()\n    {\n        return new ArrayIterator($this->_cookies);\n    }\n\n    /**\n     * Returns the number of cookies in the collection.\n     * This method is required by the SPL `Countable` interface.\n     * It will be implicitly called when you use `count($collection)`.\n     * @return int the number of cookies in the collection.\n     */\n    #[\\ReturnTypeWillChange]\n    public function count()\n    {\n        return $this->getCount();\n    }\n\n    /**\n     * Returns the number of cookies in the collection.\n     * @return int the number of cookies in the collection.\n     */\n    public function getCount()\n    {\n        return count($this->_cookies);\n    }\n\n    /**\n     * Returns the cookie with the specified name.\n     * @param string $name the cookie name\n     * @return Cookie|null the cookie with the specified name. Null if the named cookie does not exist.\n     * @see getValue()\n     */\n    public function get($name)\n    {\n        return isset($this->_cookies[$name]) ? $this->_cookies[$name] : null;\n    }\n\n    /**\n     * Returns the value of the named cookie.\n     * @param string $name the cookie name\n     * @param mixed $defaultValue the value that should be returned when the named cookie does not exist.\n     * @return mixed the value of the named cookie.\n     * @see get()\n     */\n    public function getValue($name, $defaultValue = null)\n    {\n        return isset($this->_cookies[$name]) ? $this->_cookies[$name]->value : $defaultValue;\n    }\n\n    /**\n     * Returns whether there is a cookie with the specified name.\n     * Note that if a cookie is marked for deletion from browser or its value is an empty string, this method will return false.\n     * @param string $name the cookie name\n     * @return bool whether the named cookie exists\n     * @see remove()\n     */\n    public function has($name)\n    {\n        if (!isset($this->_cookies[$name]) || $this->_cookies[$name]->value === '') {\n            return false;\n        }\n\n        $expire = $this->_cookies[$name]->expire;\n\n        if ($expire === null || $expire === 0) {\n            return true;\n        }\n\n        $currentTime = time();\n\n        if (is_numeric($expire)) {\n            return (int) $expire >= $currentTime;\n        }\n\n        if (is_string($expire)) {\n            return strtotime($expire) >= $currentTime;\n        }\n\n        return $expire->getTimestamp() >= $currentTime;\n    }\n\n    /**\n     * Adds a cookie to the collection.\n     * If there is already a cookie with the same name in the collection, it will be removed first.\n     * @param Cookie $cookie the cookie to be added\n     * @throws InvalidCallException if the cookie collection is read only\n     */\n    public function add($cookie)\n    {\n        if ($this->readOnly) {\n            throw new InvalidCallException('The cookie collection is read only.');\n        }\n        $this->_cookies[$cookie->name] = $cookie;\n    }\n\n    /**\n     * Removes a cookie.\n     * If `$removeFromBrowser` is true, the cookie will be removed from the browser.\n     * In this case, a cookie with outdated expiry will be added to the collection.\n     * @param Cookie|string $cookie the cookie object or the name of the cookie to be removed.\n     * @param bool $removeFromBrowser whether to remove the cookie from browser\n     * @throws InvalidCallException if the cookie collection is read only\n     */\n    public function remove($cookie, $removeFromBrowser = true)\n    {\n        if ($this->readOnly) {\n            throw new InvalidCallException('The cookie collection is read only.');\n        }\n        if ($cookie instanceof Cookie) {\n            $cookie->expire = 1;\n            $cookie->value = '';\n        } else {\n            $cookie = Yii::createObject([\n                'class' => 'yii\\web\\Cookie',\n                'name' => $cookie,\n                'expire' => 1,\n            ]);\n        }\n        if ($removeFromBrowser) {\n            $this->_cookies[$cookie->name] = $cookie;\n        } else {\n            unset($this->_cookies[$cookie->name]);\n        }\n    }\n\n    /**\n     * Removes all cookies.\n     * @throws InvalidCallException if the cookie collection is read only\n     */\n    public function removeAll()\n    {\n        if ($this->readOnly) {\n            throw new InvalidCallException('The cookie collection is read only.');\n        }\n        $this->_cookies = [];\n    }\n\n    /**\n     * Returns the collection as a PHP array.\n     * @return Cookie[] the array representation of the collection.\n     * The array keys are cookie names, and the array values are the corresponding cookie objects.\n     */\n    public function toArray()\n    {\n        return $this->_cookies;\n    }\n\n    /**\n     * Populates the cookie collection from an array.\n     * @param array $array the cookies to populate from\n     * @since 2.0.3\n     */\n    public function fromArray(array $array)\n    {\n        $this->_cookies = $array;\n    }\n\n    /**\n     * Returns whether there is a cookie with the specified name.\n     * This method is required by the SPL interface [[\\ArrayAccess]].\n     * It is implicitly called when you use something like `isset($collection[$name])`.\n     * @param string $name the cookie name\n     * @return bool whether the named cookie exists\n     */\n    #[\\ReturnTypeWillChange]\n    public function offsetExists($name)\n    {\n        return $this->has($name);\n    }\n\n    /**\n     * Returns the cookie with the specified name.\n     * This method is required by the SPL interface [[\\ArrayAccess]].\n     * It is implicitly called when you use something like `$cookie = $collection[$name];`.\n     * This is equivalent to [[get()]].\n     * @param string $name the cookie name\n     * @return Cookie|null the cookie with the specified name, null if the named cookie does not exist.\n     */\n    #[\\ReturnTypeWillChange]\n    public function offsetGet($name)\n    {\n        return $this->get($name);\n    }\n\n    /**\n     * Adds the cookie to the collection.\n     * This method is required by the SPL interface [[\\ArrayAccess]].\n     * It is implicitly called when you use something like `$collection[$name] = $cookie;`.\n     * This is equivalent to [[add()]].\n     * @param string $name the cookie name\n     * @param Cookie $cookie the cookie to be added\n     */\n    #[\\ReturnTypeWillChange]\n    public function offsetSet($name, $cookie)\n    {\n        $this->add($cookie);\n    }\n\n    /**\n     * Removes the named cookie.\n     * This method is required by the SPL interface [[\\ArrayAccess]].\n     * It is implicitly called when you use something like `unset($collection[$name])`.\n     * This is equivalent to [[remove()]].\n     * @param string $name the cookie name\n     */\n    #[\\ReturnTypeWillChange]\n    public function offsetUnset($name)\n    {\n        $this->remove($name);\n    }\n}\n"
  },
  {
    "path": "framework/web/DbSession.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\nuse Yii;\nuse yii\\base\\InvalidConfigException;\nuse yii\\db\\Connection;\nuse yii\\db\\PdoValue;\nuse yii\\db\\Query;\nuse yii\\di\\Instance;\n\n/**\n * DbSession extends [[Session]] by using database as session data storage.\n *\n * By default, DbSession stores session data in a DB table named 'session'. This table\n * must be pre-created. The table name can be changed by setting [[sessionTable]].\n *\n * The following example shows how you can configure the application to use DbSession:\n * Add the following to your application config under `components`:\n *\n * ```\n * 'session' => [\n *     'class' => 'yii\\web\\DbSession',\n *     // 'db' => 'mydb',\n *     // 'sessionTable' => 'my_session',\n * ]\n * ```\n *\n * DbSession extends [[MultiFieldSession]], thus it allows saving extra fields into the [[sessionTable]].\n * Refer to [[MultiFieldSession]] for more details.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass DbSession extends MultiFieldSession\n{\n    /**\n     * @var Connection|array|string the DB connection object or the application component ID of the DB connection.\n     * After the DbSession object is created, if you want to change this property, you should only assign it\n     * with a DB connection object.\n     * Starting from version 2.0.2, this can also be a configuration array for creating the object.\n     */\n    public $db = 'db';\n    /**\n     * @var string the name of the DB table that stores the session data.\n     * The table should be pre-created as follows:\n     *\n     * ```\n     * CREATE TABLE session\n     * (\n     *     id CHAR(40) NOT NULL PRIMARY KEY,\n     *     expire INTEGER,\n     *     data BLOB\n     * )\n     * ```\n     *\n     * where 'BLOB' refers to the BLOB-type of your preferred DBMS. Below are the BLOB type\n     * that can be used for some popular DBMS:\n     *\n     * - MySQL: LONGBLOB\n     * - PostgreSQL: BYTEA\n     * - MSSQL: BLOB\n     *\n     * When using DbSession in a production server, we recommend you create a DB index for the 'expire'\n     * column in the session table to improve the performance.\n     *\n     * Note that according to the php.ini setting of `session.hash_function`, you may need to adjust\n     * the length of the `id` column. For example, if `session.hash_function=sha256`, you should use\n     * length 64 instead of 40.\n     */\n    public $sessionTable = '{{%session}}';\n\n    /**\n     * @var array Session fields to be written into session table columns\n     * @since 2.0.17\n     */\n    protected $fields = [];\n\n\n    /**\n     * Initializes the DbSession component.\n     * This method will initialize the [[db]] property to make sure it refers to a valid DB connection.\n     * @throws InvalidConfigException if [[db]] is invalid.\n     */\n    public function init()\n    {\n        parent::init();\n        $this->db = Instance::ensure($this->db, Connection::className());\n    }\n\n    /**\n     * Session open handler.\n     * @internal Do not call this method directly.\n     * @param string $savePath session save path\n     * @param string $sessionName session name\n     * @return bool whether session is opened successfully\n     */\n    public function openSession($savePath, $sessionName)\n    {\n        if ($this->getUseStrictMode()) {\n            $id = $this->getId();\n            if (!$this->getReadQuery($id)->exists($this->db)) {\n                //This session id does not exist, mark it for forced regeneration\n                $this->_forceRegenerateId = $id;\n            }\n        }\n\n        return parent::openSession($savePath, $sessionName);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function regenerateID($deleteOldSession = false)\n    {\n        $oldID = session_id();\n\n        // if no session is started, there is nothing to regenerate\n        if (empty($oldID)) {\n            return;\n        }\n\n        parent::regenerateID(false);\n        $newID = session_id();\n        // if session id regeneration failed, no need to create/update it.\n        if (empty($newID)) {\n            Yii::warning('Failed to generate new session ID', __METHOD__);\n            return;\n        }\n\n        $row = $this->db->useMaster(function () use ($oldID) {\n            return (new Query())->from($this->sessionTable)\n               ->where(['id' => $oldID])\n               ->createCommand($this->db)\n               ->queryOne();\n        });\n\n        if ($row !== false && $this->getIsActive()) {\n            if ($deleteOldSession) {\n                $this->db->createCommand()\n                    ->update($this->sessionTable, ['id' => $newID], ['id' => $oldID])\n                    ->execute();\n            } else {\n                $row['id'] = $newID;\n                $this->db->createCommand()\n                    ->insert($this->sessionTable, $row)\n                    ->execute();\n            }\n        }\n    }\n\n    /**\n     * Ends the current session and store session data.\n     * @since 2.0.17\n     */\n    public function close()\n    {\n        if ($this->getIsActive()) {\n            // prepare writeCallback fields before session closes\n            $this->fields = $this->composeFields();\n            YII_DEBUG ? session_write_close() : @session_write_close();\n        }\n    }\n\n    /**\n     * Session read handler.\n     * @internal Do not call this method directly.\n     * @param string $id session ID\n     * @return string|false the session data, or false on failure\n     */\n    public function readSession($id)\n    {\n        $query = $this->getReadQuery($id);\n\n        if ($this->readCallback !== null) {\n            $fields = $query->one($this->db);\n            return $fields === false ? '' : $this->extractData($fields);\n        }\n\n        $data = $query->select(['data'])->scalar($this->db);\n        return $data === false ? '' : $data;\n    }\n\n    /**\n     * Session write handler.\n     * @internal Do not call this method directly.\n     * @param string $id session ID\n     * @param string $data session data\n     * @return bool whether session write is successful\n     */\n    public function writeSession($id, $data)\n    {\n        if ($this->getUseStrictMode() && $id === $this->_forceRegenerateId) {\n            //Ignore write when forceRegenerate is active for this id\n            return true;\n        }\n\n        // exception must be caught in session write handler\n        // https://www.php.net/manual/en/function.session-set-save-handler.php#refsect1-function.session-set-save-handler-notes\n        try {\n            // ensure backwards compatability (fixed #9438)\n            if ($this->writeCallback && !$this->fields) {\n                $this->fields = $this->composeFields();\n            }\n            // ensure data consistency\n            if (!isset($this->fields['data'])) {\n                $this->fields['data'] = $data;\n            } else {\n                $_SESSION = $this->fields['data'];\n            }\n            // ensure 'id' and 'expire' are never affected by [[writeCallback]]\n            $this->fields = array_merge($this->fields, [\n                'id' => $id,\n                'expire' => time() + $this->getTimeout(),\n            ]);\n            $this->fields = $this->typecastFields($this->fields);\n            $this->db->createCommand()->upsert($this->sessionTable, $this->fields)->execute();\n            $this->fields = [];\n        } catch (\\Exception $e) {\n            Yii::$app->errorHandler->handleException($e);\n            return false;\n        }\n        return true;\n    }\n\n    /**\n     * Session destroy handler.\n     * @internal Do not call this method directly.\n     * @param string $id session ID\n     * @return bool whether session is destroyed successfully\n     */\n    public function destroySession($id)\n    {\n        $this->db->createCommand()\n            ->delete($this->sessionTable, ['id' => $id])\n            ->execute();\n\n        return true;\n    }\n\n    /**\n     * Session GC (garbage collection) handler.\n     * @internal Do not call this method directly.\n     * @param int $maxLifetime the number of seconds after which data will be seen as 'garbage' and cleaned up.\n     * @return int|false the number of deleted sessions on success, or false on failure\n     */\n    public function gcSession($maxLifetime)\n    {\n        return $this->db->createCommand()\n            ->delete($this->sessionTable, '[[expire]]<:expire', [':expire' => time()])\n            ->execute();\n    }\n\n    /**\n     * Generates a query to get the session from db\n     * @param string $id The id of the session\n     * @return Query\n     */\n    protected function getReadQuery($id)\n    {\n        return (new Query())\n            ->from($this->sessionTable)\n            ->where('[[expire]]>:expire AND [[id]]=:id', [':expire' => time(), ':id' => $id]);\n    }\n\n    /**\n     * Method typecasts $fields before passing them to PDO.\n     * Default implementation casts field `data` to `\\PDO::PARAM_LOB`.\n     * You can override this method in case you need special type casting.\n     *\n     * @param array $fields Fields, that will be passed to PDO. Key - name, Value - value\n     * @return array\n     * @since 2.0.13\n     */\n    protected function typecastFields($fields)\n    {\n        if (isset($fields['data']) && !is_array($fields['data']) && !is_object($fields['data'])) {\n            $fields['data'] = new PdoValue($fields['data'], \\PDO::PARAM_LOB);\n        }\n\n        return $fields;\n    }\n}\n"
  },
  {
    "path": "framework/web/ErrorAction.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\nuse Yii;\nuse yii\\base\\Action;\nuse yii\\base\\Exception;\nuse yii\\base\\UserException;\n\n/**\n * ErrorAction displays application errors using a specified view.\n *\n * To use ErrorAction, you need to do the following steps:\n *\n * First, declare an action of ErrorAction type in the `actions()` method of your `SiteController`\n * class (or whatever controller you prefer), like the following:\n *\n * ```\n * public function actions()\n * {\n *     return [\n *         'error' => ['class' => 'yii\\web\\ErrorAction'],\n *     ];\n * }\n * ```\n *\n * Then, create a view file for this action. If the route of your error action is `site/error`, then\n * the view file should be `views/site/error.php`. In this view file, the following variables are available:\n *\n * - `$name`: the error name\n * - `$message`: the error message\n * - `$exception`: the exception being handled\n *\n * Finally, configure the \"errorHandler\" application component as follows,\n *\n * ```\n * 'errorHandler' => [\n *     'errorAction' => 'site/error',\n * ]\n * ```\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Dmitry Naumenko <d.naumenko.a@gmail.com>\n * @since 2.0\n *\n * @template T of Controller = Controller\n * @extends Action<T>\n */\nclass ErrorAction extends Action\n{\n    /**\n     * @var string|null the view file to be rendered. If not set, it will take the value of [[id]].\n     * That means, if you name the action as \"error\" in \"SiteController\", then the view name\n     * would be \"error\", and the corresponding view file would be \"views/site/error.php\".\n     */\n    public $view;\n    /**\n     * @var string the name of the error when the exception name cannot be determined.\n     * Defaults to \"Error\".\n     */\n    public $defaultName;\n    /**\n     * @var string the message to be displayed when the exception message contains sensitive information.\n     * Defaults to \"An internal server error occurred.\".\n     */\n    public $defaultMessage;\n    /**\n     * @var string|null|false the name of the layout to be applied to this error action view.\n     * If not set, the layout configured in the controller will be used.\n     * @see \\yii\\base\\Controller::$layout\n     * @since 2.0.14\n     */\n    public $layout;\n\n    /**\n     * @var \\Throwable the exception object, normally is filled on [[init()]] method call.\n     * @see findException() to know default way of obtaining exception.\n     * @since 2.0.11\n     */\n    protected $exception;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        $this->exception = $this->findException();\n\n        if ($this->defaultMessage === null) {\n            $this->defaultMessage = Yii::t('yii', 'An internal server error occurred.');\n        }\n\n        if ($this->defaultName === null) {\n            $this->defaultName = Yii::t('yii', 'Error');\n        }\n    }\n\n    /**\n     * Runs the action.\n     *\n     * @return string result content\n     */\n    public function run()\n    {\n        if ($this->layout !== null) {\n            $this->controller->layout = $this->layout;\n        }\n\n        Yii::$app->getResponse()->setStatusCodeByException($this->exception);\n\n        if (Yii::$app->getRequest()->getIsAjax()) {\n            return $this->renderAjaxResponse();\n        }\n\n        return $this->renderHtmlResponse();\n    }\n\n    /**\n     * Builds string that represents the exception.\n     * Normally used to generate a response to AJAX request.\n     * @return string\n     * @since 2.0.11\n     */\n    protected function renderAjaxResponse()\n    {\n        return $this->getExceptionName() . ': ' . $this->getExceptionMessage();\n    }\n\n    /**\n     * Renders a view that represents the exception.\n     * @return string\n     * @since 2.0.11\n     */\n    protected function renderHtmlResponse()\n    {\n        return $this->controller->render($this->view ?: $this->id, $this->getViewRenderParams());\n    }\n\n    /**\n     * Builds array of parameters that will be passed to the view.\n     * @return array\n     * @since 2.0.11\n     */\n    protected function getViewRenderParams()\n    {\n        return [\n            'name' => $this->getExceptionName(),\n            'message' => $this->getExceptionMessage(),\n            'exception' => $this->exception,\n        ];\n    }\n\n    /**\n     * Gets exception from the [[yii\\web\\ErrorHandler|ErrorHandler]] component.\n     * In case there is no exception in the component, treat as the action has been invoked\n     * not from error handler, but by direct route, so '404 Not Found' error will be displayed.\n     * @return \\Throwable\n     * @since 2.0.11\n     */\n    protected function findException()\n    {\n        if (($exception = Yii::$app->getErrorHandler()->exception) === null) {\n            $exception = new NotFoundHttpException(Yii::t('yii', 'Page not found.'));\n        }\n\n        return $exception;\n    }\n\n    /**\n     * Gets the code from the [[exception]].\n     * @return mixed\n     * @since 2.0.11\n     */\n    protected function getExceptionCode()\n    {\n        if ($this->exception instanceof HttpException) {\n            return $this->exception->statusCode;\n        }\n\n        return $this->exception->getCode();\n    }\n\n    /**\n     * Returns the exception name, followed by the code (if present).\n     *\n     * @return string\n     * @since 2.0.11\n     */\n    protected function getExceptionName()\n    {\n        if ($this->exception instanceof Exception) {\n            $name = $this->exception->getName();\n        } else {\n            $name = $this->defaultName;\n        }\n\n        if ($code = $this->getExceptionCode()) {\n            $name .= \" (#$code)\";\n        }\n\n        return $name;\n    }\n\n    /**\n     * Returns the [[exception]] message for [[yii\\base\\UserException]] only.\n     * For other cases [[defaultMessage]] will be returned.\n     * @return string\n     * @since 2.0.11\n     */\n    protected function getExceptionMessage()\n    {\n        if ($this->exception instanceof UserException) {\n            return $this->exception->getMessage();\n        }\n\n        return $this->defaultMessage;\n    }\n}\n"
  },
  {
    "path": "framework/web/ErrorHandler.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\nuse Yii;\nuse yii\\base\\ErrorException;\nuse yii\\base\\Exception;\nuse yii\\base\\UserException;\nuse yii\\helpers\\VarDumper;\n\n/**\n * ErrorHandler handles uncaught PHP errors and exceptions.\n *\n * ErrorHandler displays these errors using appropriate views based on the\n * nature of the errors and the mode the application runs at.\n *\n * ErrorHandler is configured as an application component in [[\\yii\\base\\Application]] by default.\n * You can access that instance via `Yii::$app->errorHandler`.\n *\n * For more details and usage information on ErrorHandler, see the [guide article on handling errors](guide:runtime-handling-errors).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Timur Ruziev <resurtm@gmail.com>\n * @since 2.0\n */\nclass ErrorHandler extends \\yii\\base\\ErrorHandler\n{\n    /**\n     * @var int maximum number of source code lines to be displayed. Defaults to 19.\n     */\n    public $maxSourceLines = 19;\n    /**\n     * @var int maximum number of trace source code lines to be displayed. Defaults to 13.\n     */\n    public $maxTraceSourceLines = 13;\n    /**\n     * @var string|null the route (e.g. `site/error`) to the controller action that will be used\n     * to display external errors. Inside the action, it can retrieve the error information\n     * using `Yii::$app->errorHandler->exception`. This property defaults to null, meaning ErrorHandler\n     * will handle the error display.\n     */\n    public $errorAction;\n    /**\n     * @var string the path of the view file for rendering exceptions without call stack information.\n     */\n    public $errorView = '@yii/views/errorHandler/error.php';\n    /**\n     * @var string the path of the view file for rendering exceptions.\n     */\n    public $exceptionView = '@yii/views/errorHandler/exception.php';\n    /**\n     * @var string the path of the view file for rendering exceptions and errors call stack element.\n     */\n    public $callStackItemView = '@yii/views/errorHandler/callStackItem.php';\n    /**\n     * @var string the path of the view file for rendering previous exceptions.\n     */\n    public $previousExceptionView = '@yii/views/errorHandler/previousException.php';\n    /**\n     * @var array list of the PHP predefined variables that should be displayed on the error page.\n     * Note that a variable must be accessible via `$GLOBALS`. Otherwise it won't be displayed.\n     * Defaults to `['_GET', '_POST', '_FILES', '_COOKIE', '_SESSION']`.\n     * @see renderRequest()\n     * @since 2.0.7\n     */\n    public $displayVars = ['_GET', '_POST', '_FILES', '_COOKIE', '_SESSION'];\n    /**\n     * @var string trace line with placeholders to be be substituted.\n     * The placeholders are {file}, {line} and {text} and the string should be as follows.\n     *\n     * `File: {file} - Line: {line} - Text: {text}`\n     *\n     * @example <a href=\"ide://open?file={file}&line={line}\">{html}</a>\n     * @see https://github.com/yiisoft/yii2-debug#open-files-in-ide\n     * @since 2.0.14\n     */\n    public $traceLine = '{html}';\n\n\n    /**\n     * Renders the exception.\n     * @param \\Throwable $exception the exception to be rendered.\n     */\n    protected function renderException($exception)\n    {\n        if (Yii::$app->has('response')) {\n            $response = Yii::$app->getResponse();\n            // reset parameters of response to avoid interference with partially created response data\n            // in case the error occurred while sending the response.\n            $response->isSent = false;\n            $response->stream = null;\n            $response->data = null;\n            $response->content = null;\n        } else {\n            $response = new Response();\n        }\n\n        $response->setStatusCodeByException($exception);\n\n        $useErrorView = $response->format === Response::FORMAT_HTML && (!YII_DEBUG || $exception instanceof UserException);\n\n        if ($useErrorView && $this->errorAction !== null) {\n            /** @var View $view */\n            $view = Yii::$app->view;\n            $view->clear();\n            $result = Yii::$app->runAction($this->errorAction);\n            if ($result instanceof Response) {\n                $response = $result;\n            } else {\n                $response->data = $result;\n            }\n        } elseif ($response->format === Response::FORMAT_HTML) {\n            if ($this->shouldRenderSimpleHtml()) {\n                // AJAX request\n                $response->data = '<pre>' . $this->htmlEncode(static::convertExceptionToString($exception)) . '</pre>';\n            } else {\n                // if there is an error during error rendering it's useful to\n                // display PHP error in debug mode instead of a blank screen\n                if (YII_DEBUG) {\n                    ini_set('display_errors', 1);\n                }\n                $file = $useErrorView ? $this->errorView : $this->exceptionView;\n                $response->data = $this->renderFile($file, [\n                    'exception' => $exception,\n                ]);\n            }\n        } elseif ($response->format === Response::FORMAT_RAW) {\n            $response->data = static::convertExceptionToString($exception);\n        } else {\n            $response->data = $this->convertExceptionToArray($exception);\n        }\n\n        $response->send();\n    }\n\n    /**\n     * Converts an exception into an array.\n     * @param \\Throwable $exception the exception being converted\n     * @return array the array representation of the exception.\n     */\n    protected function convertExceptionToArray($exception)\n    {\n        if (!YII_DEBUG && !$exception instanceof UserException && !$exception instanceof HttpException) {\n            $exception = new HttpException(500, Yii::t('yii', 'An internal server error occurred.'));\n        }\n\n        $array = [\n            'name' => ($exception instanceof Exception || $exception instanceof ErrorException) ? $exception->getName() : 'Exception',\n            'message' => $exception->getMessage(),\n            'code' => $exception->getCode(),\n        ];\n        if ($exception instanceof HttpException) {\n            $array['status'] = $exception->statusCode;\n        }\n        if (YII_DEBUG) {\n            $array['type'] = get_class($exception);\n            if (!$exception instanceof UserException) {\n                $array['file'] = $exception->getFile();\n                $array['line'] = $exception->getLine();\n                $array['stack-trace'] = explode(\"\\n\", $exception->getTraceAsString());\n                if ($exception instanceof \\yii\\db\\Exception) {\n                    $array['error-info'] = $exception->errorInfo;\n                }\n            }\n        }\n        if (($prev = $exception->getPrevious()) !== null) {\n            $array['previous'] = $this->convertExceptionToArray($prev);\n        }\n\n        return $array;\n    }\n\n    /**\n     * Converts special characters to HTML entities.\n     * @param string $text to encode.\n     * @return string encoded original text.\n     */\n    public function htmlEncode($text)\n    {\n        return htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5, 'UTF-8');\n    }\n\n    /**\n     * Adds informational links to the given PHP type/class.\n     * @param string $code type/class name to be linkified.\n     * @return string linkified with HTML type/class name.\n     */\n    public function addTypeLinks($code)\n    {\n        if (preg_match('/(.*?)::([^(]+)/', $code, $matches)) {\n            $class = $matches[1];\n            $method = $matches[2];\n            $text = $this->htmlEncode($class) . '::' . $this->htmlEncode($method);\n        } else {\n            $class = $code;\n            $method = null;\n            $text = $this->htmlEncode($class);\n        }\n\n        $url = null;\n\n        $shouldGenerateLink = true;\n        if ($method !== null && strpos($method, '{closure') === false) {\n            $reflection = new \\ReflectionClass($class);\n            if ($reflection->hasMethod($method)) {\n                $reflectionMethod = $reflection->getMethod($method);\n                $shouldGenerateLink = $reflectionMethod->isPublic() || $reflectionMethod->isProtected();\n            } else {\n                $shouldGenerateLink = false;\n            }\n        }\n\n        if ($shouldGenerateLink) {\n            $url = $this->getTypeUrl($class, $method);\n        }\n\n        if ($url === null) {\n            return $text;\n        }\n\n        return '<a href=\"' . $url . '\" target=\"_blank\">' . $text . '</a>';\n    }\n\n    /**\n     * Returns the informational link URL for a given PHP type/class.\n     * @param string $class the type or class name.\n     * @param string|null $method the method name.\n     * @return string|null the informational link URL.\n     * @see addTypeLinks()\n     */\n    protected function getTypeUrl($class, $method)\n    {\n        if (strncmp($class, 'yii\\\\', 4) !== 0) {\n            return null;\n        }\n\n        $page = $this->htmlEncode(strtolower(str_replace('\\\\', '-', $class)));\n        $url = \"https://www.yiiframework.com/doc-2.0/$page.html\";\n        if ($method) {\n            $url .= \"#$method()-detail\";\n        }\n\n        return $url;\n    }\n\n    /**\n     * Renders a view file as a PHP script.\n     * @param string $_file_ the view file.\n     * @param array $_params_ the parameters (name-value pairs) that will be extracted and made available in the view file.\n     * @return string the rendering result\n     */\n    public function renderFile($_file_, $_params_)\n    {\n        $_params_['handler'] = $this;\n        if ($this->exception instanceof ErrorException || !Yii::$app->has('view')) {\n            ob_start();\n            ob_implicit_flush(false);\n            extract($_params_, EXTR_OVERWRITE);\n            require Yii::getAlias($_file_);\n\n            return ob_get_clean();\n        }\n\n        /** @var View $view */\n        $view = Yii::$app->getView();\n        $view->clear();\n\n        return $view->renderFile($_file_, $_params_, $this);\n    }\n\n    /**\n     * Renders the previous exception stack for a given Exception.\n     * @param \\Throwable $exception the exception whose precursors should be rendered.\n     * @return string HTML content of the rendered previous exceptions.\n     * Empty string if there are none.\n     */\n    public function renderPreviousExceptions($exception)\n    {\n        if (($previous = $exception->getPrevious()) !== null) {\n            return $this->renderFile($this->previousExceptionView, ['exception' => $previous]);\n        }\n\n        return '';\n    }\n\n    /**\n     * Renders a single call stack element.\n     * @param string|null $file name where call has happened.\n     * @param int|null $line number on which call has happened.\n     * @param string|null $class called class name.\n     * @param string|null $method called function/method name.\n     * @param array $args array of method arguments.\n     * @param int $index number of the call stack element.\n     * @return string HTML content of the rendered call stack element.\n     */\n    public function renderCallStackItem($file, $line, $class, $method, $args, $index)\n    {\n        $lines = [];\n        $begin = $end = 0;\n        if ($file !== null && $line !== null) {\n            $line--; // adjust line number from one-based to zero-based\n            $lines = @file($file);\n            if ($line < 0 || $lines === false || ($lineCount = count($lines)) < $line) {\n                return '';\n            }\n\n            $half = (int) (($index === 1 ? $this->maxSourceLines : $this->maxTraceSourceLines) / 2);\n            $begin = $line - $half > 0 ? $line - $half : 0;\n            $end = $line + $half < $lineCount ? $line + $half : $lineCount - 1;\n        }\n\n        return $this->renderFile($this->callStackItemView, [\n            'file' => $file,\n            'line' => $line,\n            'class' => $class,\n            'method' => $method,\n            'index' => $index,\n            'lines' => $lines,\n            'begin' => $begin,\n            'end' => $end,\n            'args' => $args,\n        ]);\n    }\n\n    /**\n     * Renders call stack.\n     * @param \\Throwable $exception exception to get call stack from\n     * @return string HTML content of the rendered call stack.\n     * @since 2.0.12\n     */\n    public function renderCallStack($exception)\n    {\n        $out = '<ul>';\n        $out .= $this->renderCallStackItem($exception->getFile(), $exception->getLine(), null, null, [], 1);\n        for ($i = 0, $trace = $exception->getTrace(), $length = count($trace); $i < $length; ++$i) {\n            $file = !empty($trace[$i]['file']) ? $trace[$i]['file'] : null;\n            $line = !empty($trace[$i]['line']) ? $trace[$i]['line'] : null;\n            $class = !empty($trace[$i]['class']) ? $trace[$i]['class'] : null;\n            $function = null;\n            if (!empty($trace[$i]['function']) && $trace[$i]['function'] !== 'unknown') {\n                $function = $trace[$i]['function'];\n            }\n            $args = !empty($trace[$i]['args']) ? $trace[$i]['args'] : [];\n            $out .= $this->renderCallStackItem($file, $line, $class, $function, $args, $i + 2);\n        }\n        $out .= '</ul>';\n        return $out;\n    }\n\n    /**\n     * Renders the global variables of the request.\n     * List of global variables is defined in [[displayVars]].\n     * @return string the rendering result\n     * @see displayVars\n     */\n    public function renderRequest()\n    {\n        $request = '';\n        foreach ($this->displayVars as $name) {\n            if (!empty($GLOBALS[$name])) {\n                $request .= '$' . $name . ' = ' . VarDumper::export($GLOBALS[$name]) . \";\\n\\n\";\n            }\n        }\n\n        return '<pre>' . $this->htmlEncode(rtrim($request, \"\\n\")) . '</pre>';\n    }\n\n    /**\n     * Determines whether given name of the file belongs to the framework.\n     * @param string $file name to be checked.\n     * @return bool whether given name of the file belongs to the framework.\n     */\n    public function isCoreFile($file)\n    {\n        return $file === null || strpos(realpath($file), YII2_PATH . DIRECTORY_SEPARATOR) === 0;\n    }\n\n    /**\n     * Creates HTML containing link to the page with the information on given HTTP status code.\n     * @param int $statusCode to be used to generate information link.\n     * @param string $statusDescription Description to display after the the status code.\n     * @return string generated HTML with HTTP status code information.\n     */\n    public function createHttpStatusLink($statusCode, $statusDescription)\n    {\n        return '<a href=\"https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#' . (int) $statusCode . '\" target=\"_blank\">HTTP ' . (int) $statusCode . ' &ndash; ' . $statusDescription . '</a>';\n    }\n\n    /**\n     * Creates string containing HTML link which refers to the home page of determined web-server software\n     * and its full name.\n     * @return string server software information hyperlink.\n     */\n    public function createServerInformationLink()\n    {\n        $serverUrls = [\n            'https://httpd.apache.org/' => ['apache'],\n            'https://nginx.org/' => ['nginx'],\n            'https://www.lighttpd.net/' => ['lighttpd'],\n            'http://gwan.com/' => ['g-wan', 'gwan'],\n            'https://www.iis.net/' => ['iis', 'services'],\n            'https://www.php.net/manual/en/features.commandline.webserver.php' => ['development'],\n        ];\n        if (isset($_SERVER['SERVER_SOFTWARE'])) {\n            foreach ($serverUrls as $url => $keywords) {\n                foreach ($keywords as $keyword) {\n                    if (stripos($_SERVER['SERVER_SOFTWARE'], $keyword) !== false) {\n                        return '<a href=\"' . $url . '\" target=\"_blank\">' . $this->htmlEncode($_SERVER['SERVER_SOFTWARE']) . '</a>';\n                    }\n                }\n            }\n        }\n\n        return '';\n    }\n\n    /**\n     * Creates string containing HTML link which refers to the page with the current version\n     * of the framework and version number text.\n     * @return string framework version information hyperlink.\n     */\n    public function createFrameworkVersionLink()\n    {\n        return '<a href=\"https://github.com/yiisoft/yii2/\" target=\"_blank\">' . $this->htmlEncode(Yii::getVersion()) . '</a>';\n    }\n\n    /**\n     * Converts arguments array to its string representation.\n     *\n     * @param array $args arguments array to be converted\n     * @return string string representation of the arguments array\n     */\n    public function argumentsToString($args)\n    {\n        $count = 0;\n        $isAssoc = $args !== array_values($args);\n\n        foreach ($args as $key => $value) {\n            $count++;\n            if ($count >= 5) {\n                if ($count > 5) {\n                    unset($args[$key]);\n                } else {\n                    $args[$key] = '...';\n                }\n                continue;\n            }\n\n            if (is_object($value)) {\n                $args[$key] = '<span class=\"title\">' . $this->htmlEncode(get_class($value)) . '</span>';\n            } elseif (is_bool($value)) {\n                $args[$key] = '<span class=\"keyword\">' . ($value ? 'true' : 'false') . '</span>';\n            } elseif (is_string($value)) {\n                $fullValue = $this->htmlEncode($value);\n                if (mb_strlen($value, 'UTF-8') > 32) {\n                    $displayValue = $this->htmlEncode(mb_substr($value, 0, 32, 'UTF-8')) . '...';\n                    $args[$key] = \"<span class=\\\"string\\\" title=\\\"$fullValue\\\">'$displayValue'</span>\";\n                } else {\n                    $args[$key] = \"<span class=\\\"string\\\">'$fullValue'</span>\";\n                }\n            } elseif (is_array($value)) {\n                $args[$key] = '[' . $this->argumentsToString($value) . ']';\n            } elseif ($value === null) {\n                $args[$key] = '<span class=\"keyword\">null</span>';\n            } elseif (is_resource($value)) {\n                $args[$key] = '<span class=\"keyword\">resource</span>';\n            } else {\n                $args[$key] = '<span class=\"number\">' . $value . '</span>';\n            }\n\n            if (is_string($key)) {\n                $args[$key] = '<span class=\"string\">\\'' . $this->htmlEncode($key) . \"'</span> => $args[$key]\";\n            } elseif ($isAssoc) {\n                $args[$key] = \"<span class=\\\"number\\\">$key</span> => $args[$key]\";\n            }\n        }\n\n        return implode(', ', $args);\n    }\n\n    /**\n     * Returns human-readable exception name.\n     * @param \\Throwable $exception\n     * @return string|null human-readable exception name or null if it cannot be determined\n     */\n    public function getExceptionName($exception)\n    {\n        if ($exception instanceof \\yii\\base\\Exception || $exception instanceof \\yii\\base\\InvalidCallException || $exception instanceof \\yii\\base\\InvalidParamException || $exception instanceof \\yii\\base\\UnknownMethodException) {\n            return $exception->getName();\n        }\n\n        return null;\n    }\n\n    /**\n     * @return bool if simple HTML should be rendered\n     * @since 2.0.12\n     */\n    protected function shouldRenderSimpleHtml()\n    {\n        return YII_ENV_TEST || Yii::$app->request->getIsAjax();\n    }\n}\n"
  },
  {
    "path": "framework/web/ForbiddenHttpException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\n/**\n * ForbiddenHttpException represents a \"Forbidden\" HTTP exception with status code 403.\n *\n * Use this exception when a user is not allowed to perform the requested action.\n * Using different credentials might or might not allow performing the requested action.\n * If you do not want to expose authorization information to the user, it is valid\n * to respond with a 404 [[NotFoundHttpException]].\n *\n * @see https://tools.ietf.org/html/rfc7231#section-6.5.3\n * @author Dan Schmidt <danschmidt5189@gmail.com>\n * @since 2.0\n */\nclass ForbiddenHttpException extends HttpException\n{\n    /**\n     * Constructor.\n     * @param string|null $message error message\n     * @param int $code error code\n     * @param \\Throwable|null $previous The previous exception used for the exception chaining.\n     */\n    public function __construct($message = null, $code = 0, $previous = null)\n    {\n        parent::__construct(403, $message, $code, $previous);\n    }\n}\n"
  },
  {
    "path": "framework/web/GoneHttpException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\n/**\n * GoneHttpException represents a \"Gone\" HTTP exception with status code 410.\n *\n * Throw a GoneHttpException when a user requests a resource that no longer exists\n * at the requested url. For example, after a record is deleted, future requests\n * for that record should return a 410 GoneHttpException instead of a 404\n * [[NotFoundHttpException]].\n *\n * @see https://tools.ietf.org/html/rfc7231#section-6.5.9\n * @author Dan Schmidt <danschmidt5189@gmail.com>\n * @since 2.0\n */\nclass GoneHttpException extends HttpException\n{\n    /**\n     * Constructor.\n     * @param string|null $message error message\n     * @param int $code error code\n     * @param \\Throwable|null $previous The previous exception used for the exception chaining.\n     */\n    public function __construct($message = null, $code = 0, $previous = null)\n    {\n        parent::__construct(410, $message, $code, $previous);\n    }\n}\n"
  },
  {
    "path": "framework/web/GroupUrlRule.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\nuse Yii;\nuse yii\\base\\InvalidConfigException;\n\n/**\n * GroupUrlRule represents a collection of URL rules sharing the same prefix in their patterns and routes.\n *\n * GroupUrlRule is best used by a module which often uses module ID as the prefix for the URL rules.\n * For example, the following code creates a rule for the `admin` module:\n *\n * ```\n * new GroupUrlRule([\n *     'prefix' => 'admin',\n *     'rules' => [\n *         'login' => 'user/login',\n *         'logout' => 'user/logout',\n *         'dashboard' => 'default/dashboard',\n *     ],\n * ]);\n *\n * // the above rule is equivalent to the following three rules:\n *\n * [\n *     'admin/login' => 'admin/user/login',\n *     'admin/logout' => 'admin/user/logout',\n *     'admin/dashboard' => 'admin/default/dashboard',\n * ]\n * ```\n *\n * The above example assumes the prefix for patterns and routes are the same. They can be made different\n * by configuring [[prefix]] and [[routePrefix]] separately.\n *\n * Using a GroupUrlRule is more efficient than directly declaring the individual rules it contains.\n * This is because GroupUrlRule can quickly determine if it should process a URL parsing or creation request\n * by simply checking if the prefix matches.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass GroupUrlRule extends CompositeUrlRule\n{\n    /**\n     * @var UrlRuleInterface[]|array[]|string[] the rules contained within this composite rule. Please refer to [[UrlManager::rules]]\n     * for the format of this property.\n     * @see prefix\n     * @see routePrefix\n     */\n    public $rules = [];\n    /**\n     * @var string the prefix for the pattern part of every rule declared in [[rules]].\n     * The prefix and the pattern will be separated with a slash.\n     */\n    public $prefix;\n    /**\n     * @var string|null the prefix for the route part of every rule declared in [[rules]].\n     * The prefix and the route will be separated with a slash.\n     * If this property is not set, it will take the value of [[prefix]].\n     */\n    public $routePrefix;\n    /**\n     * @var array the default configuration of URL rules. Individual rule configurations\n     * specified via [[rules]] will take precedence when the same property of the rule is configured.\n     */\n    public $ruleConfig = ['class' => 'yii\\web\\UrlRule'];\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function init()\n    {\n        $this->prefix = trim((string)$this->prefix, '/');\n        $this->routePrefix = $this->routePrefix === null ? $this->prefix : trim($this->routePrefix, '/');\n        parent::init();\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function createRules()\n    {\n        $rules = [];\n        foreach ($this->rules as $key => $rule) {\n            if (!is_array($rule)) {\n                $verbs = 'GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS';\n                $verb = null;\n                if (preg_match(\"/^((?:(?:$verbs),)*(?:$verbs))\\\\s+(.*)$/\", $key, $matches)) {\n                    $verb = explode(',', $matches[1]);\n                    $key = $matches[2];\n                }\n                $rule = [\n                    'pattern' => ltrim($this->prefix . '/' . $key, '/'),\n                    'route' => ltrim($this->routePrefix . '/' . $rule, '/'),\n                    'verb' => $verb\n                ];\n            } elseif (isset($rule['pattern'], $rule['route'])) {\n                $rule['pattern'] = ltrim($this->prefix . '/' . $rule['pattern'], '/');\n                $rule['route'] = ltrim($this->routePrefix . '/' . $rule['route'], '/');\n            }\n\n            $rule = Yii::createObject(array_merge($this->ruleConfig, $rule));\n            if (!$rule instanceof UrlRuleInterface) {\n                throw new InvalidConfigException('URL rule class must implement UrlRuleInterface.');\n            }\n            $rules[] = $rule;\n        }\n\n        return $rules;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function parseRequest($manager, $request)\n    {\n        $pathInfo = $request->getPathInfo();\n        if ($this->prefix === '' || strpos($pathInfo . '/', $this->prefix . '/') === 0) {\n            return parent::parseRequest($manager, $request);\n        }\n\n        return false;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function createUrl($manager, $route, $params)\n    {\n        if ($this->routePrefix === '' || strpos($route, $this->routePrefix . '/') === 0) {\n            return parent::createUrl($manager, $route, $params);\n        }\n\n        $this->createStatus = UrlRule::CREATE_STATUS_ROUTE_MISMATCH;\n        return false;\n    }\n}\n"
  },
  {
    "path": "framework/web/HeaderCollection.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\nuse yii\\base\\BaseObject;\n\n/**\n * HeaderCollection is used by [[Response]] to maintain the currently registered HTTP headers.\n *\n * @property-read int $count The number of headers in the collection.\n * @property-read \\ArrayIterator<string, string[]> $iterator An iterator for traversing the headers in the\n * collection.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @implements \\IteratorAggregate<string, string[]>\n * @implements \\ArrayAccess<string, string|null>\n */\nclass HeaderCollection extends BaseObject implements \\IteratorAggregate, \\ArrayAccess, \\Countable\n{\n    /**\n     * @var array<string, string[]> the headers in this collection (indexed by the normalized header names)\n     */\n    private $_headers = [];\n    /**\n     * @var array<string, string> the original names of the headers (indexed by the normalized header names)\n     */\n    private $_originalHeaderNames = [];\n\n\n    /**\n     * Returns an iterator for traversing the headers in the collection.\n     * This method is required by the SPL interface [[\\IteratorAggregate]].\n     * It will be implicitly called when you use `foreach` to traverse the collection.\n     * @return \\ArrayIterator<string, string[]> an iterator for traversing the headers in the collection.\n     */\n    #[\\ReturnTypeWillChange]\n    public function getIterator()\n    {\n        return new \\ArrayIterator($this->_headers);\n    }\n\n    /**\n     * Returns the number of headers in the collection.\n     * This method is required by the SPL `Countable` interface.\n     * It will be implicitly called when you use `count($collection)`.\n     * @return int the number of headers in the collection.\n     */\n    #[\\ReturnTypeWillChange]\n    public function count()\n    {\n        return $this->getCount();\n    }\n\n    /**\n     * Returns the number of headers in the collection.\n     * @return int the number of headers in the collection.\n     */\n    #[\\ReturnTypeWillChange]\n    public function getCount()\n    {\n        return count($this->_headers);\n    }\n\n    /**\n     * Returns the named header(s).\n     * @param string $name the name of the header to return\n     * @param ($first is true ? string|null : string[]|null) $default the value to return in case the named header does not exist\n     * @param bool $first whether to only return the first header of the specified name.\n     * If false, all headers of the specified name will be returned.\n     * @return ($first is true ? string|null : string[]|null) the named header(s). If `$first` is true, a string will be returned;\n     * If `$first` is false, an array will be returned.\n     */\n    public function get($name, $default = null, $first = true)\n    {\n        $normalizedName = strtolower($name);\n        if (isset($this->_headers[$normalizedName])) {\n            return $first ? reset($this->_headers[$normalizedName]) : $this->_headers[$normalizedName];\n        }\n\n        return $default;\n    }\n\n    /**\n     * Adds a new header.\n     * If there is already a header with the same name, it will be replaced.\n     * @param string $name the name of the header\n     * @param string $value the value of the header\n     * @return $this the collection object itself\n     */\n    public function set($name, $value = '')\n    {\n        $normalizedName = strtolower($name);\n        $this->_headers[$normalizedName] = (array) $value;\n        $this->_originalHeaderNames[$normalizedName] = $name;\n\n        return $this;\n    }\n\n    /**\n     * Adds a new header.\n     * If there is already a header with the same name, the new one will\n     * be appended to it instead of replacing it.\n     * @param string $name the name of the header\n     * @param string $value the value of the header\n     * @return $this the collection object itself\n     */\n    public function add($name, $value)\n    {\n        $normalizedName = strtolower($name);\n        $this->_headers[$normalizedName][] = $value;\n        if (!\\array_key_exists($normalizedName, $this->_originalHeaderNames)) {\n            $this->_originalHeaderNames[$normalizedName] = $name;\n        }\n\n        return $this;\n    }\n\n    /**\n     * Sets a new header only if it does not exist yet.\n     * If there is already a header with the same name, the new one will be ignored.\n     * @param string $name the name of the header\n     * @param string $value the value of the header\n     * @return $this the collection object itself\n     */\n    public function setDefault($name, $value)\n    {\n        $normalizedName = strtolower($name);\n        if (empty($this->_headers[$normalizedName])) {\n            $this->_headers[$normalizedName][] = $value;\n            $this->_originalHeaderNames[$normalizedName] = $name;\n        }\n\n        return $this;\n    }\n\n    /**\n     * Returns a value indicating whether the named header exists.\n     * @param string $name the name of the header\n     * @return bool whether the named header exists\n     */\n    public function has($name)\n    {\n        return isset($this->_headers[strtolower($name)]);\n    }\n\n    /**\n     * Removes a header.\n     * @param string $name the name of the header to be removed.\n     * @return array|null the value of the removed header. Null is returned if the header does not exist.\n     */\n    public function remove($name)\n    {\n        $normalizedName = strtolower($name);\n        if (isset($this->_headers[$normalizedName])) {\n            $value = $this->_headers[$normalizedName];\n            unset($this->_headers[$normalizedName], $this->_originalHeaderNames[$normalizedName]);\n            return $value;\n        }\n\n        return null;\n    }\n\n    /**\n     * Removes all headers.\n     */\n    public function removeAll()\n    {\n        $this->_headers = [];\n        $this->_originalHeaderNames = [];\n    }\n\n    /**\n     * Returns the collection as a PHP array.\n     * @return array the array representation of the collection.\n     * The array keys are header names, and the array values are the corresponding header values.\n     */\n    public function toArray()\n    {\n        return $this->_headers;\n    }\n\n    /**\n     * Returns the collection as a PHP array but instead of using normalized header names as keys (like [[toArray()]])\n     * it uses original header names (case-sensitive).\n     * @return array the array representation of the collection.\n     * @since 2.0.45\n     */\n    public function toOriginalArray()\n    {\n        return \\array_map(function ($normalizedName) {\n            return $this->_headers[$normalizedName];\n        }, \\array_flip($this->_originalHeaderNames));\n    }\n\n    /**\n     * Populates the header collection from an array.\n     * @param array $array the headers to populate from\n     * @since 2.0.3\n     */\n    public function fromArray(array $array)\n    {\n        foreach ($array as $name => $value) {\n            $this->set($name, $value);\n        }\n    }\n\n    /**\n     * Returns whether there is a header with the specified name.\n     * This method is required by the SPL interface [[\\ArrayAccess]].\n     * It is implicitly called when you use something like `isset($collection[$name])`.\n     * @param string $name the header name\n     * @return bool whether the named header exists\n     */\n    #[\\ReturnTypeWillChange]\n    public function offsetExists($name)\n    {\n        return $this->has($name);\n    }\n\n    /**\n     * Returns the header with the specified name.\n     * This method is required by the SPL interface [[\\ArrayAccess]].\n     * It is implicitly called when you use something like `$header = $collection[$name];`.\n     * This is equivalent to [[get()]].\n     * @param string $name the header name\n     * @return string|null the header value with the specified name, null if the named header does not exist.\n     */\n    #[\\ReturnTypeWillChange]\n    public function offsetGet($name)\n    {\n        return $this->get($name);\n    }\n\n    /**\n     * Adds the header to the collection.\n     * This method is required by the SPL interface [[\\ArrayAccess]].\n     * It is implicitly called when you use something like `$collection[$name] = $header;`.\n     * This is equivalent to [[add()]].\n     * @param string $name the header name\n     * @param string $value the header value to be added\n     */\n    #[\\ReturnTypeWillChange]\n    public function offsetSet($name, $value)\n    {\n        $this->set($name, $value);\n    }\n\n    /**\n     * Removes the named header.\n     * This method is required by the SPL interface [[\\ArrayAccess]].\n     * It is implicitly called when you use something like `unset($collection[$name])`.\n     * This is equivalent to [[remove()]].\n     * @param string $name the header name\n     */\n    #[\\ReturnTypeWillChange]\n    public function offsetUnset($name)\n    {\n        $this->remove($name);\n    }\n}\n"
  },
  {
    "path": "framework/web/HeadersAlreadySentException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\nuse yii\\base\\Exception;\n\n/**\n * HeadersAlreadySentException represents an exception caused by\n * any headers that were already sent before web response was sent.\n *\n * @author Dmitry Dorogin <dmirogin@ya.ru>\n * @since 2.0.14\n */\nclass HeadersAlreadySentException extends Exception\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function __construct($file, $line)\n    {\n        $message = YII_DEBUG ? \"Headers already sent in {$file} on line {$line}.\" : 'Headers already sent.';\n        parent::__construct($message);\n    }\n}\n"
  },
  {
    "path": "framework/web/HtmlResponseFormatter.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\nuse yii\\base\\Component;\n\n/**\n * HtmlResponseFormatter formats the given data into an HTML response content.\n *\n * It is used by [[Response]] to format response data.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass HtmlResponseFormatter extends Component implements ResponseFormatterInterface\n{\n    /**\n     * @var string the Content-Type header for the response\n     */\n    public $contentType = 'text/html';\n\n\n    /**\n     * Formats the specified response.\n     * @param Response $response the response to be formatted.\n     */\n    public function format($response)\n    {\n        if (stripos($this->contentType, 'charset') === false) {\n            $this->contentType .= '; charset=' . $response->charset;\n        }\n        $response->getHeaders()->set('Content-Type', $this->contentType);\n        if ($response->data !== null) {\n            $response->content = $response->data;\n        }\n    }\n}\n"
  },
  {
    "path": "framework/web/HttpException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\nuse yii\\base\\UserException;\n\n/**\n * HttpException represents an exception caused by an improper request of the end-user.\n *\n * HttpException can be differentiated via its [[statusCode]] property value which\n * keeps a standard HTTP status code (e.g. 404, 500). Error handlers may use this status code\n * to decide how to format the error page.\n *\n * Throwing an HttpException like in the following example will result in the 404 page to be displayed.\n *\n * ```\n * if ($item === null) { // item does not exist\n *     throw new \\yii\\web\\HttpException(404, 'The requested Item could not be found.');\n * }\n * ```\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass HttpException extends UserException\n{\n    /**\n     * @var int HTTP status code, such as 403, 404, 500, etc.\n     */\n    public $statusCode;\n\n\n    /**\n     * Constructor.\n     * @param int $status HTTP status code, such as 404, 500, etc.\n     * @param string|null $message error message\n     * @param int $code error code\n     * @param \\Throwable|null $previous The previous exception used for the exception chaining.\n     */\n    public function __construct($status, $message = null, $code = 0, $previous = null)\n    {\n        $this->statusCode = $status;\n        parent::__construct((string)$message, $code, $previous);\n    }\n\n    /**\n     * @return string the user-friendly name of this exception\n     */\n    public function getName()\n    {\n        if (isset(Response::$httpStatuses[$this->statusCode])) {\n            return Response::$httpStatuses[$this->statusCode];\n        }\n\n        return 'Error';\n    }\n}\n"
  },
  {
    "path": "framework/web/IdentityInterface.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\n/**\n * IdentityInterface is the interface that should be implemented by a class providing identity information.\n *\n * This interface can typically be implemented by a user model class. For example, the following\n * code shows how to implement this interface by a User ActiveRecord class:\n *\n * ```\n * class User extends ActiveRecord implements IdentityInterface\n * {\n *     public static function findIdentity($id)\n *     {\n *         return static::findOne($id);\n *     }\n *\n *     public static function findIdentityByAccessToken($token, $type = null)\n *     {\n *         return static::findOne(['access_token' => $token]);\n *     }\n *\n *     public function getId()\n *     {\n *         return $this->id;\n *     }\n *\n *     public function getAuthKey()\n *     {\n *         return $this->authKey;\n *     }\n *\n *     public function validateAuthKey($authKey)\n *     {\n *         return $this->authKey === $authKey;\n *     }\n * }\n * ```\n *\n * In some situations not all of these methods are required to be implemented.\n * For example, if your application is a pure stateless RESTful application,\n * you would only need to implement [[yii\\web\\IdentityInterface::findIdentityByAccessToken()|findIdentityByAccessToken()]]\n * and [[yii\\web\\IdentityInterface::getId()|getId()]] while leaving all other methods with an empty body.\n * Or if your application uses session only authentication, you would need to implement all the methods\n * except [[yii\\web\\IdentityInterface::findIdentityByAccessToken()|findIdentityByAccessToken()]].\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\ninterface IdentityInterface\n{\n    /**\n     * Finds an identity by the given ID.\n     * @param string|int $id the ID to be looked for\n     * @return IdentityInterface|null the identity object that matches the given ID.\n     * Null should be returned if such an identity cannot be found\n     * or the identity is not in an active state (disabled, deleted, etc.)\n     */\n    public static function findIdentity($id);\n\n    /**\n     * Finds an identity by the given token.\n     * @param mixed $token the token to be looked for\n     * @param mixed $type the type of the token. The value of this parameter depends on the implementation.\n     * For example, [[\\yii\\filters\\auth\\HttpBearerAuth]] will set this parameter to be `yii\\filters\\auth\\HttpBearerAuth`.\n     * @return IdentityInterface|null the identity object that matches the given token.\n     * Null should be returned if such an identity cannot be found\n     * or the identity is not in an active state (disabled, deleted, etc.)\n     */\n    public static function findIdentityByAccessToken($token, $type = null);\n\n    /**\n     * Returns an ID that can uniquely identify a user identity.\n     * @return string|int an ID that uniquely identifies a user identity.\n     */\n    public function getId();\n\n    /**\n     * Returns a key that can be used to check the validity of a given identity ID.\n     *\n     * The key should be unique for each individual user, and should be persistent\n     * so that it can be used to check the validity of the user identity.\n     *\n     * The space of such keys should be big enough to defeat potential identity attacks.\n     *\n     * The returned key is used to validate session and auto-login (if [[User::enableAutoLogin]] is enabled).\n     *\n     * Make sure to invalidate earlier issued authKeys when you implement force user logout, password change and\n     * other scenarios, that require forceful access revocation for old sessions.\n     *\n     * @return string|null a key that is used to check the validity of a given identity ID.\n     * @see validateAuthKey()\n     */\n    public function getAuthKey();\n\n    /**\n     * Validates the given auth key.\n     *\n     * @param string $authKey the given auth key\n     * @return bool|null whether the given auth key is valid.\n     * @see getAuthKey()\n     */\n    public function validateAuthKey($authKey);\n}\n"
  },
  {
    "path": "framework/web/JqueryAsset.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\n/**\n * This asset bundle provides the [jQuery](https://jquery.com/) JavaScript library.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass JqueryAsset extends AssetBundle\n{\n    public $sourcePath = '@bower/jquery/dist';\n    public $js = [\n        'jquery.js',\n    ];\n}\n"
  },
  {
    "path": "framework/web/JsExpression.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\nuse yii\\base\\BaseObject;\n\n/**\n * `JsExpression` marks a string as a JavaScript expression.\n *\n * When using [[\\yii\\helpers\\Json::encode()]] or [[\\yii\\helpers\\Json::htmlEncode()]] to encode a value, `JsExpression` objects\n * will be specially handled and encoded as a JavaScript expression instead of a string.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass JsExpression extends BaseObject\n{\n    /**\n     * @var string the JavaScript expression represented by this object\n     */\n    public $expression;\n\n\n    /**\n     * Constructor.\n     * @param string $expression the JavaScript expression represented by this object\n     * @param array $config additional configurations for this object\n     */\n    public function __construct($expression, $config = [])\n    {\n        $this->expression = $expression;\n        parent::__construct($config);\n    }\n\n    /**\n     * The PHP magic function converting an object into a string.\n     * @return string the JavaScript expression.\n     */\n    public function __toString()\n    {\n        return (string) $this->expression;\n    }\n}\n"
  },
  {
    "path": "framework/web/JsonParser.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\nuse yii\\base\\InvalidArgumentException;\nuse yii\\helpers\\Json;\n\n/**\n * Parses a raw HTTP request using [[\\yii\\helpers\\Json::decode()]].\n *\n * To enable parsing for JSON requests you can configure [[Request::parsers]] using this class:\n *\n * ```\n * 'request' => [\n *     'parsers' => [\n *         'application/json' => 'yii\\web\\JsonParser',\n *     ]\n * ]\n * ```\n *\n * @author Dan Schmidt <danschmidt5189@gmail.com>\n * @since 2.0\n */\nclass JsonParser implements RequestParserInterface\n{\n    /**\n     * @var bool whether to return objects in terms of associative arrays.\n     */\n    public $asArray = true;\n    /**\n     * @var bool whether to throw a [[BadRequestHttpException]] if the body is invalid JSON\n     */\n    public $throwException = true;\n\n\n    /**\n     * Parses a HTTP request body.\n     * @param string $rawBody the raw HTTP request body.\n     * @param string $contentType the content type specified for the request body.\n     * @return array|\\stdClass parameters parsed from the request body\n     * @throws BadRequestHttpException if the body contains invalid json and [[throwException]] is `true`.\n     */\n    public function parse($rawBody, $contentType)\n    {\n        // converts JSONP to JSON\n        if (strpos($contentType, 'application/javascript') !== false) {\n            $rawBody = preg_filter('/(^[^{]+|[^}]+$)/', '', $rawBody);\n        }\n\n        try {\n            $parameters = Json::decode($rawBody, $this->asArray);\n            return $parameters === null ? [] : $parameters;\n        } catch (InvalidArgumentException $e) {\n            if ($this->throwException) {\n                throw new BadRequestHttpException('Invalid JSON data in request body: ' . $e->getMessage());\n            }\n\n            return [];\n        }\n    }\n}\n"
  },
  {
    "path": "framework/web/JsonResponseFormatter.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\nuse Yii;\nuse yii\\base\\Component;\nuse yii\\helpers\\Json;\n\n/**\n * JsonResponseFormatter formats the given data into a JSON or JSONP response content.\n *\n * It is used by [[Response]] to format response data.\n *\n * To configure properties like [[encodeOptions]] or [[prettyPrint]], you can configure the `response`\n * application component like the following:\n *\n * ```\n * 'response' => [\n *     // ...\n *     'formatters' => [\n *         \\yii\\web\\Response::FORMAT_JSON => [\n *              'class' => 'yii\\web\\JsonResponseFormatter',\n *              'prettyPrint' => YII_DEBUG, // use \"pretty\" output in debug mode\n *              'keepObjectType' => false, // keep object type for zero-indexed objects\n *              // ...\n *         ],\n *     ],\n * ],\n * ```\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass JsonResponseFormatter extends Component implements ResponseFormatterInterface\n{\n    /**\n     * JSON Content Type\n     * @since 2.0.14\n     */\n    public const CONTENT_TYPE_JSONP = 'application/javascript; charset=UTF-8';\n    /**\n     * JSONP Content Type\n     * @since 2.0.14\n     */\n    public const CONTENT_TYPE_JSON = 'application/json; charset=UTF-8';\n    /**\n     * HAL JSON Content Type\n     * @since 2.0.14\n     */\n    public const CONTENT_TYPE_HAL_JSON = 'application/hal+json; charset=UTF-8';\n    /**\n     * @var string|null custom value of the `Content-Type` header of the response.\n     * When equals `null` default content type will be used based on the `useJsonp` property.\n     * @since 2.0.14\n     */\n    public $contentType;\n    /**\n     * @var bool whether to use JSONP response format. When this is true, the [[Response::data|response data]]\n     * must be an array consisting of `data` and `callback` members. The latter should be a JavaScript\n     * function name while the former will be passed to this function as a parameter.\n     */\n    public $useJsonp = false;\n    /**\n     * @var int the encoding options passed to [[Json::encode()]]. For more details please refer to\n     * <https://www.php.net/manual/en/function.json-encode.php>.\n     * Default is `JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE`.\n     * This property has no effect, when [[useJsonp]] is `true`.\n     * @since 2.0.7\n     */\n    public $encodeOptions = 320;\n    /**\n     * @var bool whether to format the output in a readable \"pretty\" format. This can be useful for debugging purpose.\n     * If this is true, `JSON_PRETTY_PRINT` will be added to [[encodeOptions]].\n     * Defaults to `false`.\n     * This property has no effect, when [[useJsonp]] is `true`.\n     * @since 2.0.7\n     */\n    public $prettyPrint = false;\n    /**\n     * @var bool Avoids objects with zero-indexed keys to be encoded as array\n     * Json::encode((object)['test']) will be encoded as an object not array. This matches the behaviour of json_encode().\n     * Defaults to Json::$keepObjectType value\n     * @since 2.0.44\n     */\n    public $keepObjectType;\n\n\n    /**\n     * Formats the specified response.\n     * @param Response $response the response to be formatted.\n     */\n    public function format($response)\n    {\n        if ($this->contentType === null) {\n            $this->contentType = $this->useJsonp\n                ? self::CONTENT_TYPE_JSONP\n                : self::CONTENT_TYPE_JSON;\n        } elseif (strpos($this->contentType, 'charset') === false) {\n            $this->contentType .= '; charset=UTF-8';\n        }\n        $response->getHeaders()->set('Content-Type', $this->contentType);\n\n        if ($this->useJsonp) {\n            $this->formatJsonp($response);\n        } else {\n            $this->formatJson($response);\n        }\n    }\n\n    /**\n     * Formats response data in JSON format.\n     * @param Response $response\n     */\n    protected function formatJson($response)\n    {\n        if ($response->data !== null) {\n            $options = $this->encodeOptions;\n            if ($this->prettyPrint) {\n                $options |= JSON_PRETTY_PRINT;\n            }\n\n            $default = Json::$keepObjectType;\n            if ($this->keepObjectType !== null) {\n                Json::$keepObjectType = $this->keepObjectType;\n            }\n\n            $response->content = Json::encode($response->data, $options);\n\n            // Restore default value to avoid any unexpected behaviour\n            Json::$keepObjectType = $default;\n        } elseif ($response->content === null) {\n            $response->content = 'null';\n        }\n    }\n\n    /**\n     * Formats response data in JSONP format.\n     * @param Response $response\n     */\n    protected function formatJsonp($response)\n    {\n        if (\n            is_array($response->data)\n            && isset($response->data['data'], $response->data['callback'])\n        ) {\n            $response->content = sprintf(\n                '%s(%s);',\n                $response->data['callback'],\n                Json::htmlEncode($response->data['data'])\n            );\n        } elseif ($response->data !== null) {\n            $response->content = '';\n            Yii::warning(\n                \"The 'jsonp' response requires that the data be an array consisting of both 'data' and 'callback' elements.\",\n                __METHOD__\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "framework/web/Link.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\nuse yii\\base\\BaseObject;\n\n/**\n * Link represents a link object as defined in [JSON Hypermedia API Language](https://tools.ietf.org/html/draft-kelly-json-hal-03).\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass Link extends BaseObject\n{\n    /**\n     * The self link.\n     */\n    public const REL_SELF = 'self';\n    /**\n     * @var string a URI [RFC3986](https://tools.ietf.org/html/rfc3986) or\n     * URI template [RFC6570](https://tools.ietf.org/html/rfc6570). This property is required.\n     */\n    public $href;\n    /**\n     * @var string a secondary key for selecting Link Objects which share the same relation type\n     */\n    public $name;\n    /**\n     * @var string a hint to indicate the media type expected when dereferencing the target resource\n     */\n    public $type;\n    /**\n     * @var bool a value indicating whether [[href]] refers to a URI or URI template.\n     */\n    public $templated = false;\n    /**\n     * @var string a URI that hints about the profile of the target resource.\n     */\n    public $profile;\n    /**\n     * @var string a label describing the link\n     */\n    public $title;\n    /**\n     * @var string the language of the target resource\n     */\n    public $hreflang;\n\n\n    /**\n     * Serializes a list of links into proper array format.\n     * @param array $links the links to be serialized\n     * @return array the proper array representation of the links.\n     */\n    public static function serialize(array $links)\n    {\n        foreach ($links as $rel => $link) {\n            if (is_array($link)) {\n                $links[$rel] = self::serialize($link);\n            } elseif ($link instanceof self) {\n                $links[$rel] = array_filter((array)$link);\n            } else {\n                $links[$rel] = ['href' => $link];\n            }\n        }\n\n        return $links;\n    }\n}\n"
  },
  {
    "path": "framework/web/Linkable.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\n/**\n * Linkable is the interface that should be implemented by classes that typically represent locatable resources.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\ninterface Linkable\n{\n    /**\n     * Returns a list of links.\n     *\n     * Each link is either a URI or a [[Link]] object. The return value of this method should\n     * be an array whose keys are the relation names and values the corresponding links.\n     *\n     * If a relation name corresponds to multiple links, use an array to represent them.\n     *\n     * For example,\n     *\n     * ```\n     * [\n     *     'self' => 'https://example.com/users/1',\n     *     'friends' => [\n     *         'https://example.com/users/2',\n     *         'https://example.com/users/3',\n     *     ],\n     *     'manager' => $managerLink, // $managerLink is a Link object\n     * ]\n     * ```\n     *\n     * @return array the links\n     */\n    public function getLinks();\n}\n"
  },
  {
    "path": "framework/web/MultiFieldSession.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\n/**\n * MultiFieldSession is the base class for session storage implementations with multi-field data storage support.\n *\n * With multi-field data storage, session data can be split between several fields in the storage record.\n * Using such a storage allows saving particular session data into separated field, which then can be used\n * to manipulate sessions in the way plain PHP does not allow.\n *\n * For example the ID of the authenticated user can be saved as separated column in the MySQL 'session' table,\n * which allows to query all active sessions for a particular user or terminate them at will.\n *\n * Customizing of the session writing is performed via [[writeCallback]], reading via [[readCallback]].\n *\n * While extending this class you should use [[composeFields()]] method - while writing the session data into the storage and\n * [[extractData()]] - while reading session data from the storage.\n *\n * @property-read bool $useCustomStorage Whether to use custom storage.\n *\n * @author Paul Klimov <klimov.paul@gmail.com>\n * @since 2.0.6\n */\nabstract class MultiFieldSession extends Session\n{\n    /**\n     * @var callable a callback that will be called during session data reading.\n     * The signature of the callback should be as follows:\n     *\n     * ```\n     * function ($fields)\n     * ```\n     *\n     * where `$fields` is the storage field set for read session and `$session` is this session instance.\n     * If callback returns an array, it will be merged into the session data.\n     *\n     * For example:\n     *\n     * ```\n     * function ($fields) {\n     *     return [\n     *         'expireDate' => Yii::$app->formatter->asDate($fields['expire']),\n     *     ];\n     * }\n     * ```\n     */\n    public $readCallback;\n    /**\n     * @var callable|null a callback that will be called during session data writing.\n     * The signature of the callback should be as follows:\n     *\n     * ```\n     * function ($session)\n     * ```\n     *\n     * where `$session` is this session instance, this variable can be used to retrieve session data.\n     * Callback should return the actual fields set, which should be saved into the session storage.\n     *\n     * For example:\n     *\n     * ```\n     * function ($session) {\n     *     return [\n     *         'user_id' => Yii::$app->user->id,\n     *         'ip' => $_SERVER['REMOTE_ADDR'],\n     *         'is_trusted' => $session->get('is_trusted', false),\n     *     ];\n     * }\n     * ```\n     */\n    public $writeCallback;\n\n\n    /**\n     * Returns a value indicating whether to use custom session storage.\n     * This method overrides the parent implementation and always returns true.\n     * @return bool whether to use custom storage.\n     */\n    public function getUseCustomStorage()\n    {\n        return true;\n    }\n\n    /**\n     * Composes storage field set for session writing.\n     * @param string|null $id Optional session id\n     * @param string|null $data Optional session data\n     * @return array storage fields\n     */\n    protected function composeFields($id = null, $data = null)\n    {\n        $fields = $this->writeCallback ? call_user_func($this->writeCallback, $this) : [];\n        if ($id !== null) {\n            $fields['id'] = $id;\n        }\n        if ($data !== null) {\n            $fields['data'] = $data;\n        }\n        return $fields;\n    }\n\n    /**\n     * Extracts session data from storage field set.\n     * @param array $fields storage fields.\n     * @return string session data.\n     */\n    protected function extractData($fields)\n    {\n        if ($this->readCallback !== null) {\n            if (!isset($fields['data'])) {\n                $fields['data'] = '';\n            }\n            $extraData = call_user_func($this->readCallback, $fields);\n            if (!empty($extraData)) {\n                session_decode($fields['data']);\n                $_SESSION = array_merge((array) $_SESSION, (array) $extraData);\n                return session_encode();\n            }\n\n            return $fields['data'];\n        }\n\n        return isset($fields['data']) ? $fields['data'] : '';\n    }\n}\n"
  },
  {
    "path": "framework/web/MultipartFormDataParser.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\nuse yii\\base\\BaseObject;\nuse yii\\helpers\\ArrayHelper;\nuse yii\\helpers\\StringHelper;\n\n/**\n * MultipartFormDataParser parses content encoded as 'multipart/form-data'.\n * This parser provides the fallback for the 'multipart/form-data' processing on non POST requests,\n * for example: the one with 'PUT' request method.\n *\n * In order to enable this parser you should configure [[Request::parsers]] in the following way:\n *\n * ```\n * return [\n *     'components' => [\n *         'request' => [\n *             'parsers' => [\n *                 'multipart/form-data' => 'yii\\web\\MultipartFormDataParser'\n *             ],\n *         ],\n *         // ...\n *     ],\n *     // ...\n * ];\n * ```\n *\n * Method [[parse()]] of this parser automatically populates `$_FILES` with the files parsed from raw body.\n *\n * > Note: since this is a request parser, it will initialize `$_FILES` values on [[Request::getBodyParams()]].\n * Until this method is invoked, `$_FILES` array will remain empty even if there are submitted files in the\n * request body. Make sure you have requested body params before any attempt to get uploaded file in case\n * you are using this parser.\n *\n * Usage example:\n *\n * ```\n * use yii\\web\\UploadedFile;\n *\n * $restRequestData = Yii::$app->request->getBodyParams();\n * $uploadedFile = UploadedFile::getInstancesByName('photo');\n *\n * $model = new Item();\n * $model->populate($restRequestData);\n * copy($uploadedFile->tempName, '/path/to/file/storage/photo.jpg');\n * ```\n *\n * > Note: although this parser fully emulates regular structure of the `$_FILES`, related temporary\n * files, which are available via `tmp_name` key, will not be recognized by PHP as uploaded ones.\n * Thus functions like `is_uploaded_file()` and `move_uploaded_file()` will fail on them.\n *\n * @property int $uploadFileMaxCount Maximum upload files count.\n * @property int $uploadFileMaxSize Upload file max size in bytes.\n *\n * @author Paul Klimov <klimov.paul@gmail.com>\n * @since 2.0.10\n */\nclass MultipartFormDataParser extends BaseObject implements RequestParserInterface\n{\n    /**\n     * @var bool whether to parse raw body even for 'POST' request and `$_FILES` already populated.\n     * By default this option is disabled saving performance for 'POST' requests, which are already\n     * processed by PHP automatically.\n     * > Note: if this option is enabled, value of `$_FILES` will be reset on each parse.\n     * @since 2.0.13\n     */\n    public $force = false;\n\n    /**\n     * @var int upload file max size in bytes.\n     */\n    private $_uploadFileMaxSize;\n    /**\n     * @var int maximum upload files count.\n     */\n    private $_uploadFileMaxCount;\n\n\n    /**\n     * @return int upload file max size in bytes.\n     */\n    public function getUploadFileMaxSize()\n    {\n        if ($this->_uploadFileMaxSize === null) {\n            $this->_uploadFileMaxSize = $this->getByteSize(ini_get('upload_max_filesize'));\n        }\n\n        return $this->_uploadFileMaxSize;\n    }\n\n    /**\n     * @param int $uploadFileMaxSize upload file max size in bytes.\n     */\n    public function setUploadFileMaxSize($uploadFileMaxSize)\n    {\n        $this->_uploadFileMaxSize = $uploadFileMaxSize;\n    }\n\n    /**\n     * @return int maximum upload files count.\n     */\n    public function getUploadFileMaxCount()\n    {\n        if ($this->_uploadFileMaxCount === null) {\n            $this->_uploadFileMaxCount = (int)ini_get('max_file_uploads');\n        }\n\n        return $this->_uploadFileMaxCount;\n    }\n\n    /**\n     * @param int $uploadFileMaxCount maximum upload files count.\n     */\n    public function setUploadFileMaxCount($uploadFileMaxCount)\n    {\n        $this->_uploadFileMaxCount = $uploadFileMaxCount;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function parse($rawBody, $contentType)\n    {\n        if (!$this->force) {\n            if (!empty($_POST) || !empty($_FILES)) {\n                // normal POST request is parsed by PHP automatically\n                return $_POST;\n            }\n        } else {\n            $_FILES = [];\n        }\n\n        if (empty($rawBody)) {\n            return [];\n        }\n\n        if (!preg_match('/boundary=\"?(.*)\"?$/is', $contentType, $matches)) {\n            return [];\n        }\n\n        $boundary = trim($matches[1], '\"');\n\n        $bodyParts = preg_split('/\\\\R?-+' . preg_quote($boundary, '/') . '/s', $rawBody);\n        array_pop($bodyParts); // last block always has no data, contains boundary ending like `--`\n\n        $bodyParams = [];\n        $filesCount = 0;\n        foreach ($bodyParts as $bodyPart) {\n            if (empty($bodyPart)) {\n                continue;\n            }\n            list($headers, $value) = preg_split('/\\\\R\\\\R/', $bodyPart, 2);\n            $headers = $this->parseHeaders($headers);\n\n            if (!isset($headers['content-disposition']['name'])) {\n                continue;\n            }\n\n            if (isset($headers['content-disposition']['filename'])) {\n                // file upload:\n                if ($filesCount >= $this->getUploadFileMaxCount()) {\n                    continue;\n                }\n\n                $fileInfo = [\n                    'name' => $headers['content-disposition']['filename'],\n                    'type' => ArrayHelper::getValue($headers, 'content-type', 'application/octet-stream'),\n                    'size' => StringHelper::byteLength($value),\n                    'error' => UPLOAD_ERR_OK,\n                    'tmp_name' => null,\n                ];\n\n                if ($fileInfo['size'] > $this->getUploadFileMaxSize()) {\n                    $fileInfo['error'] = UPLOAD_ERR_INI_SIZE;\n                } else {\n                    $tmpResource = tmpfile();\n                    if ($tmpResource === false) {\n                        $fileInfo['error'] = UPLOAD_ERR_CANT_WRITE;\n                    } else {\n                        $tmpResourceMetaData = stream_get_meta_data($tmpResource);\n                        $tmpFileName = $tmpResourceMetaData['uri'];\n                        if (empty($tmpFileName)) {\n                            $fileInfo['error'] = UPLOAD_ERR_CANT_WRITE;\n                            @fclose($tmpResource);\n                        } else {\n                            fwrite($tmpResource, $value);\n                            rewind($tmpResource);\n                            $fileInfo['tmp_name'] = $tmpFileName;\n                            $fileInfo['tmp_resource'] = $tmpResource; // save file resource, otherwise it will be deleted\n                        }\n                    }\n                }\n\n                $this->addFile($_FILES, $headers['content-disposition']['name'], $fileInfo);\n\n                $filesCount++;\n            } else {\n                // regular parameter:\n                $this->addValue($bodyParams, $headers['content-disposition']['name'], $value);\n            }\n        }\n\n        return $bodyParams;\n    }\n\n    /**\n     * Parses content part headers.\n     * @param string $headerContent headers source content\n     * @return array parsed headers.\n     */\n    private function parseHeaders($headerContent)\n    {\n        $headers = [];\n        $headerParts = preg_split('/\\\\R/su', $headerContent, -1, PREG_SPLIT_NO_EMPTY);\n        foreach ($headerParts as $headerPart) {\n            if (strpos($headerPart, ':') === false) {\n                continue;\n            }\n\n            list($headerName, $headerValue) = explode(':', $headerPart, 2);\n            $headerName = strtolower(trim($headerName));\n            $headerValue = trim($headerValue);\n\n            if (strpos($headerValue, ';') === false) {\n                $headers[$headerName] = $headerValue;\n            } else {\n                $headers[$headerName] = [];\n                foreach (explode(';', $headerValue) as $part) {\n                    $part = trim($part);\n                    if (strpos($part, '=') === false) {\n                        $headers[$headerName][] = $part;\n                    } else {\n                        list($name, $value) = explode('=', $part, 2);\n                        $name = strtolower(trim($name));\n                        $value = trim(trim($value), '\"');\n                        $headers[$headerName][$name] = $value;\n                    }\n                }\n            }\n        }\n\n        return $headers;\n    }\n\n    /**\n     * Adds value to the array by input name, e.g. `Item[name]`.\n     * @param array $array array which should store value.\n     * @param string $name input name specification.\n     * @param mixed $value value to be added.\n     */\n    private function addValue(&$array, $name, $value)\n    {\n        $nameParts = preg_split('/\\\\]\\\\[|\\\\[/s', $name);\n        $current = &$array;\n        foreach ($nameParts as $namePart) {\n            $namePart = trim($namePart, ']');\n            if ($namePart === '') {\n                $current[] = [];\n                $keys = array_keys($current);\n                $lastKey = array_pop($keys);\n                $current = &$current[$lastKey];\n            } else {\n                if (!isset($current[$namePart])) {\n                    $current[$namePart] = [];\n                }\n                $current = &$current[$namePart];\n            }\n        }\n        $current = $value;\n    }\n\n    /**\n     * Adds file info to the uploaded files array by input name, e.g. `Item[file]`.\n     * @param array $files array containing uploaded files\n     * @param string $name input name specification.\n     * @param array $info file info.\n     */\n    private function addFile(&$files, $name, $info)\n    {\n        if (strpos($name, '[') === false) {\n            $files[$name] = $info;\n            return;\n        }\n\n        $fileInfoAttributes = [\n            'name',\n            'type',\n            'size',\n            'error',\n            'tmp_name',\n            'tmp_resource',\n        ];\n\n        $nameParts = preg_split('/\\\\]\\\\[|\\\\[/s', $name);\n        $baseName = array_shift($nameParts);\n        if (!isset($files[$baseName])) {\n            $files[$baseName] = [];\n            foreach ($fileInfoAttributes as $attribute) {\n                $files[$baseName][$attribute] = [];\n            }\n        } else {\n            foreach ($fileInfoAttributes as $attribute) {\n                $files[$baseName][$attribute] = (array) $files[$baseName][$attribute];\n            }\n        }\n\n        foreach ($fileInfoAttributes as $attribute) {\n            if (!isset($info[$attribute])) {\n                continue;\n            }\n\n            $current = &$files[$baseName][$attribute];\n            foreach ($nameParts as $namePart) {\n                $namePart = trim($namePart, ']');\n                if ($namePart === '') {\n                    $current[] = [];\n                    $keys = array_keys($current);\n                    $lastKey = array_pop($keys);\n                    $current = &$current[$lastKey];\n                } else {\n                    if (!isset($current[$namePart])) {\n                        $current[$namePart] = [];\n                    }\n                    $current = &$current[$namePart];\n                }\n            }\n            $current = $info[$attribute];\n        }\n    }\n\n    /**\n     * Gets the size in bytes from verbose size representation.\n     *\n     * For example: '5K' => 5*1024.\n     * @param string $verboseSize verbose size representation.\n     * @return int actual size in bytes.\n     */\n    private function getByteSize($verboseSize)\n    {\n        if (empty($verboseSize)) {\n            return 0;\n        }\n        if (is_numeric($verboseSize)) {\n            return (int) $verboseSize;\n        }\n        $sizeUnit = trim($verboseSize, '0123456789');\n        $size = trim(str_replace($sizeUnit, '', $verboseSize));\n        if (!is_numeric($size)) {\n            return 0;\n        }\n        switch (strtolower($sizeUnit)) {\n            case 'kb':\n            case 'k':\n                return $size * 1024;\n            case 'mb':\n            case 'm':\n                return $size * 1024 * 1024;\n            case 'gb':\n            case 'g':\n                return $size * 1024 * 1024 * 1024;\n            default:\n                return 0;\n        }\n    }\n}\n"
  },
  {
    "path": "framework/web/NotAcceptableHttpException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\n/**\n * NotAcceptableHttpException represents a \"Not Acceptable\" HTTP exception with status code 406.\n *\n * Use this exception when the client requests a Content-Type that your\n * application cannot return. Note that, according to the HTTP 1.1 specification,\n * you are not required to respond with this status code in this situation.\n *\n * @see https://tools.ietf.org/html/rfc7231#section-6.5.6\n * @author Dan Schmidt <danschmidt5189@gmail.com>\n * @since 2.0\n */\nclass NotAcceptableHttpException extends HttpException\n{\n    /**\n     * Constructor.\n     * @param string|null $message error message\n     * @param int $code error code\n     * @param \\Throwable|null $previous The previous exception used for the exception chaining.\n     */\n    public function __construct($message = null, $code = 0, $previous = null)\n    {\n        parent::__construct(406, $message, $code, $previous);\n    }\n}\n"
  },
  {
    "path": "framework/web/NotFoundHttpException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\n/**\n * NotFoundHttpException represents a \"Not Found\" HTTP exception with status code 404.\n *\n * @see https://tools.ietf.org/html/rfc7231#section-6.5.4\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass NotFoundHttpException extends HttpException\n{\n    /**\n     * Constructor.\n     * @param string|null $message error message\n     * @param int $code error code\n     * @param \\Throwable|null $previous The previous exception used for the exception chaining.\n     */\n    public function __construct($message = null, $code = 0, $previous = null)\n    {\n        parent::__construct(404, $message, $code, $previous);\n    }\n}\n"
  },
  {
    "path": "framework/web/RangeNotSatisfiableHttpException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\n/**\n * RangeNotSatisfiableHttpException represents an exception caused by an improper request of the end-user.\n * This exception thrown when the requested range is not satisfiable: the client asked for a portion of\n * the file (byte serving), but the server cannot supply that portion. For example, if the client asked for\n * a part of the file that lies beyond the end of the file.\n *\n * Throwing an RangeNotSatisfiableHttpException like in the following example will result in the error page\n * with error 416 to be displayed.\n *\n * @author Zalatov Alexander <CaHbKa.Z@gmail.com>\n *\n * @since 2.0.11\n */\nclass RangeNotSatisfiableHttpException extends HttpException\n{\n    /**\n     * Constructor.\n     * @param string|null $message error message\n     * @param int $code error code\n     * @param \\Throwable|null $previous The previous exception used for the exception chaining.\n     */\n    public function __construct($message = null, $code = 0, $previous = null)\n    {\n        parent::__construct(416, $message, $code, $previous);\n    }\n}\n"
  },
  {
    "path": "framework/web/RequestParserInterface.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\n/**\n * Interface for classes that parse the raw request body into a parameters array.\n *\n * @author Dan Schmidt <danschmidt5189@gmail.com>\n * @since 2.0\n */\ninterface RequestParserInterface\n{\n    /**\n     * Parses a HTTP request body.\n     * @param string $rawBody the raw HTTP request body.\n     * @param string $contentType the content type specified for the request body.\n     * @return array|object parameters parsed from the request body\n     */\n    public function parse($rawBody, $contentType);\n}\n"
  },
  {
    "path": "framework/web/Response.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\nuse Yii;\nuse yii\\base\\InvalidArgumentException;\nuse yii\\base\\InvalidConfigException;\nuse yii\\base\\InvalidRouteException;\nuse yii\\helpers\\FileHelper;\nuse yii\\helpers\\Inflector;\nuse yii\\helpers\\StringHelper;\nuse yii\\helpers\\Url;\n\n/**\n * The web Response class represents an HTTP response.\n *\n * It holds the [[headers]], [[cookies]] and [[content]] that is to be sent to the client.\n * It also controls the HTTP [[statusCode|status code]].\n *\n * Response is configured as an application component in [[\\yii\\web\\Application]] by default.\n * You can access that instance via `Yii::$app->response`.\n *\n * You can modify its configuration by adding an array to your application config under `components`\n * as it is shown in the following example:\n *\n * ```\n * 'response' => [\n *     'format' => yii\\web\\Response::FORMAT_JSON,\n *     'charset' => 'UTF-8',\n *     // ...\n * ]\n * ```\n *\n * For more details and usage information on Response, see the [guide article on responses](guide:runtime-responses).\n *\n * @property-read CookieCollection $cookies The cookie collection.\n * @property-write string $downloadHeaders The attachment file name.\n * @property-read HeaderCollection $headers The header collection.\n * @property-read bool $isClientError Whether this response indicates a client error.\n * @property-read bool $isEmpty Whether this response is empty.\n * @property-read bool $isForbidden Whether this response indicates the current request is forbidden.\n * @property-read bool $isInformational Whether this response is informational.\n * @property-read bool $isInvalid Whether this response has a valid [[statusCode]].\n * @property-read bool $isNotFound Whether this response indicates the currently requested resource is not\n * found.\n * @property-read bool $isOk Whether this response is OK.\n * @property-read bool $isRedirection Whether this response is a redirection.\n * @property-read bool $isServerError Whether this response indicates a server error.\n * @property-read bool $isSuccessful Whether this response is successful.\n * @property int $statusCode The HTTP status code to send with the response.\n * @property-write \\Throwable $statusCodeByException The exception object.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @author Carsten Brandt <mail@cebe.cc>\n * @since 2.0\n */\nclass Response extends \\yii\\base\\Response\n{\n    /**\n     * @event \\yii\\base\\Event an event that is triggered at the beginning of [[send()]].\n     */\n    public const EVENT_BEFORE_SEND = 'beforeSend';\n    /**\n     * @event \\yii\\base\\Event an event that is triggered at the end of [[send()]].\n     */\n    public const EVENT_AFTER_SEND = 'afterSend';\n    /**\n     * @event \\yii\\base\\Event an event that is triggered right after [[prepare()]] is called in [[send()]].\n     * You may respond to this event to filter the response content before it is sent to the client.\n     */\n    public const EVENT_AFTER_PREPARE = 'afterPrepare';\n    public const FORMAT_RAW = 'raw';\n    public const FORMAT_HTML = 'html';\n    public const FORMAT_JSON = 'json';\n    public const FORMAT_JSONP = 'jsonp';\n    public const FORMAT_XML = 'xml';\n    /**\n     * @var string the response format. This determines how to convert [[data]] into [[content]]\n     * when the latter is not set. The value of this property must be one of the keys declared in the [[formatters]] array.\n     * By default, the following formats are supported:\n     *\n     * - [[FORMAT_RAW]]: the data will be treated as the response content without any conversion.\n     *   No extra HTTP header will be added.\n     * - [[FORMAT_HTML]]: the data will be treated as the response content without any conversion.\n     *   The \"Content-Type\" header will set as \"text/html\".\n     * - [[FORMAT_JSON]]: the data will be converted into JSON format, and the \"Content-Type\"\n     *   header will be set as \"application/json\".\n     * - [[FORMAT_JSONP]]: the data will be converted into JSONP format, and the \"Content-Type\"\n     *   header will be set as \"text/javascript\". Note that in this case `$data` must be an array\n     *   with \"data\" and \"callback\" elements. The former refers to the actual data to be sent,\n     *   while the latter refers to the name of the JavaScript callback.\n     * - [[FORMAT_XML]]: the data will be converted into XML format. Please refer to [[XmlResponseFormatter]]\n     *   for more details.\n     *\n     * You may customize the formatting process or support additional formats by configuring [[formatters]].\n     * @see formatters\n     */\n    public $format = self::FORMAT_HTML;\n    /**\n     * @var string|null the MIME type (e.g. `application/json`) from the request ACCEPT header chosen for this response.\n     * This property is mainly set by [[\\yii\\filters\\ContentNegotiator]].\n     */\n    public $acceptMimeType;\n    /**\n     * @var array the parameters (e.g. `['q' => 1, 'version' => '1.0']`) associated with the [[acceptMimeType|chosen MIME type]].\n     * This is a list of name-value pairs associated with [[acceptMimeType]] from the ACCEPT HTTP header.\n     * This property is mainly set by [[\\yii\\filters\\ContentNegotiator]].\n     */\n    public $acceptParams = [];\n    /**\n     * @var array the formatters for converting data into the response content of the specified [[format]].\n     * The array keys are the format names, and the array values are the corresponding configurations\n     * for creating the formatter objects.\n     * @see format\n     * @see defaultFormatters\n     */\n    public $formatters = [];\n    /**\n     * @var mixed the original response data. When this is not null, it will be converted into [[content]]\n     * according to [[format]] when the response is being sent out.\n     * @see content\n     */\n    public $data;\n    /**\n     * @var string|null the response content. When [[data]] is not null, it will be converted into [[content]]\n     * according to [[format]] when the response is being sent out.\n     * @see data\n     */\n    public $content;\n    /**\n     * @var resource|array|callable|null the stream to be sent. This can be a stream handle or an array of stream handle,\n     * the begin position and the end position. Alternatively it can be set to a callable, which returns\n     * (or [yields](https://www.php.net/manual/en/language.generators.syntax.php)) an array of strings that should\n     * be echoed and flushed out one by one.\n     *\n     * Note that when this property is set, the [[data]] and [[content]] properties will be ignored by [[send()]].\n     */\n    public $stream;\n    /**\n     * @var string|null the charset of the text response. If not set, it will use\n     * the value of [[Application::charset]].\n     */\n    public $charset;\n    /**\n     * @var string the HTTP status description that comes together with the status code.\n     * @see httpStatuses\n     */\n    public $statusText = 'OK';\n    /**\n     * @var string|null the version of the HTTP protocol to use. If not set, it will be determined via `$_SERVER['SERVER_PROTOCOL']`,\n     * or '1.1' if that is not available.\n     */\n    public $version;\n    /**\n     * @var bool whether the response has been sent. If this is true, calling [[send()]] will do nothing.\n     */\n    public $isSent = false;\n    /**\n     * @var array list of HTTP status codes and the corresponding texts\n     */\n    public static $httpStatuses = [\n        100 => 'Continue',\n        101 => 'Switching Protocols',\n        102 => 'Processing',\n        118 => 'Connection timed out',\n        200 => 'OK',\n        201 => 'Created',\n        202 => 'Accepted',\n        203 => 'Non-Authoritative',\n        204 => 'No Content',\n        205 => 'Reset Content',\n        206 => 'Partial Content',\n        207 => 'Multi-Status',\n        208 => 'Already Reported',\n        210 => 'Content Different',\n        226 => 'IM Used',\n        300 => 'Multiple Choices',\n        301 => 'Moved Permanently',\n        302 => 'Found',\n        303 => 'See Other',\n        304 => 'Not Modified',\n        305 => 'Use Proxy',\n        306 => 'Reserved',\n        307 => 'Temporary Redirect',\n        308 => 'Permanent Redirect',\n        310 => 'Too many Redirect',\n        400 => 'Bad Request',\n        401 => 'Unauthorized',\n        402 => 'Payment Required',\n        403 => 'Forbidden',\n        404 => 'Not Found',\n        405 => 'Method Not Allowed',\n        406 => 'Not Acceptable',\n        407 => 'Proxy Authentication Required',\n        408 => 'Request Time-out',\n        409 => 'Conflict',\n        410 => 'Gone',\n        411 => 'Length Required',\n        412 => 'Precondition Failed',\n        413 => 'Request Entity Too Large',\n        414 => 'Request-URI Too Long',\n        415 => 'Unsupported Media Type',\n        416 => 'Requested range unsatisfiable',\n        417 => 'Expectation failed',\n        418 => 'I\\'m a teapot',\n        421 => 'Misdirected Request',\n        422 => 'Unprocessable entity',\n        423 => 'Locked',\n        424 => 'Method failure',\n        425 => 'Unordered Collection',\n        426 => 'Upgrade Required',\n        428 => 'Precondition Required',\n        429 => 'Too Many Requests',\n        431 => 'Request Header Fields Too Large',\n        449 => 'Retry With',\n        450 => 'Blocked by Windows Parental Controls',\n        451 => 'Unavailable For Legal Reasons',\n        500 => 'Internal Server Error',\n        501 => 'Not Implemented',\n        502 => 'Bad Gateway or Proxy Error',\n        503 => 'Service Unavailable',\n        504 => 'Gateway Time-out',\n        505 => 'HTTP Version not supported',\n        507 => 'Insufficient storage',\n        508 => 'Loop Detected',\n        509 => 'Bandwidth Limit Exceeded',\n        510 => 'Not Extended',\n        511 => 'Network Authentication Required',\n    ];\n\n    /**\n     * @var int the HTTP status code to send with the response.\n     */\n    private $_statusCode = 200;\n    /**\n     * @var HeaderCollection|null\n     */\n    private $_headers;\n\n\n    /**\n     * Initializes this component.\n     */\n    public function init()\n    {\n        if ($this->version === null) {\n            if (isset($_SERVER['SERVER_PROTOCOL']) && $_SERVER['SERVER_PROTOCOL'] === 'HTTP/1.0') {\n                $this->version = '1.0';\n            } else {\n                $this->version = '1.1';\n            }\n        }\n        if ($this->charset === null) {\n            $this->charset = Yii::$app->charset;\n        }\n        $this->formatters = array_merge($this->defaultFormatters(), $this->formatters);\n    }\n\n    /**\n     * @return int the HTTP status code to send with the response.\n     */\n    public function getStatusCode()\n    {\n        return $this->_statusCode;\n    }\n\n    /**\n     * Sets the response status code.\n     * This method will set the corresponding status text if `$text` is null.\n     * @param int $value the status code\n     * @param string|null $text the status text. If not set, it will be set automatically based on the status code.\n     * @throws InvalidArgumentException if the status code is invalid.\n     * @return $this the response object itself\n     */\n    public function setStatusCode($value, $text = null)\n    {\n        if ($value === null) {\n            $value = 200;\n        }\n        $this->_statusCode = (int) $value;\n        if ($this->getIsInvalid()) {\n            throw new InvalidArgumentException(\"The HTTP status code is invalid: $value\");\n        }\n        if ($text === null) {\n            $this->statusText = isset(static::$httpStatuses[$this->_statusCode]) ? static::$httpStatuses[$this->_statusCode] : '';\n        } else {\n            $this->statusText = $text;\n        }\n\n        return $this;\n    }\n\n    /**\n     * Sets the response status code based on the exception.\n     * @param \\Throwable $e the exception object.\n     * @throws InvalidArgumentException if the status code is invalid.\n     * @return $this the response object itself\n     * @since 2.0.12\n     */\n    public function setStatusCodeByException($e)\n    {\n        if ($e instanceof HttpException) {\n            $this->setStatusCode($e->statusCode);\n        } else {\n            $this->setStatusCode(500);\n        }\n\n        return $this;\n    }\n\n    /**\n     * Returns the header collection.\n     * The header collection contains the currently registered HTTP headers.\n     * @return HeaderCollection the header collection\n     */\n    public function getHeaders()\n    {\n        if ($this->_headers === null) {\n            $this->_headers = new HeaderCollection();\n        }\n\n        return $this->_headers;\n    }\n\n    /**\n     * Sends the response to the client.\n     */\n    public function send()\n    {\n        if ($this->isSent) {\n            return;\n        }\n        $this->trigger(self::EVENT_BEFORE_SEND);\n        $this->prepare();\n        $this->trigger(self::EVENT_AFTER_PREPARE);\n        $this->sendHeaders();\n        $this->sendContent();\n        $this->trigger(self::EVENT_AFTER_SEND);\n        $this->isSent = true;\n    }\n\n    /**\n     * Clears the headers, cookies, content, status code of the response.\n     */\n    public function clear()\n    {\n        $this->_headers = null;\n        $this->_cookies = null;\n        $this->_statusCode = 200;\n        $this->statusText = 'OK';\n        $this->data = null;\n        $this->stream = null;\n        $this->content = null;\n        $this->isSent = false;\n    }\n\n    /**\n     * Sends the response headers to the client.\n     */\n    protected function sendHeaders()\n    {\n        if (headers_sent($file, $line)) {\n            throw new HeadersAlreadySentException($file, $line);\n        }\n        if ($this->_headers) {\n            foreach ($this->getHeaders() as $name => $values) {\n                $name = str_replace(' ', '-', ucwords(str_replace('-', ' ', $name)));\n                // set replace for first occurrence of header but false afterwards to allow multiple\n                $replace = true;\n                foreach ($values as $value) {\n                    header(\"$name: $value\", $replace);\n                    $replace = false;\n                }\n            }\n        }\n        $statusCode = $this->getStatusCode();\n        header(\"HTTP/{$this->version} {$statusCode} {$this->statusText}\");\n        $this->sendCookies();\n    }\n\n    /**\n     * Sends the cookies to the client.\n     */\n    protected function sendCookies()\n    {\n        if ($this->_cookies === null) {\n            return;\n        }\n        $request = Yii::$app->getRequest();\n        if ($request->enableCookieValidation) {\n            if ($request->cookieValidationKey == '') {\n                throw new InvalidConfigException(get_class($request) . '::cookieValidationKey must be configured with a secret key.');\n            }\n            $validationKey = $request->cookieValidationKey;\n        }\n        foreach ($this->getCookies() as $cookie) {\n            $value = $cookie->value;\n            $expire = $cookie->expire;\n            if (is_string($expire)) {\n                $expire = strtotime($expire);\n            } elseif (interface_exists('\\\\DateTimeInterface') && $expire instanceof \\DateTimeInterface) {\n                $expire = $expire->getTimestamp();\n            }\n            if ($expire === null || $expire === false) {\n                $expire = 0;\n            }\n            if ($expire != 1 && isset($validationKey)) {\n                $value = Yii::$app->getSecurity()->hashData(serialize([$cookie->name, $value]), $validationKey);\n            }\n            if (PHP_VERSION_ID >= 70300) {\n                setcookie($cookie->name, $value, [\n                    'expires' => $expire,\n                    'path' => $cookie->path,\n                    'domain' => $cookie->domain,\n                    'secure' => $cookie->secure,\n                    'httpOnly' => $cookie->httpOnly,\n                    'sameSite' => !empty($cookie->sameSite) ? $cookie->sameSite : null,\n                ]);\n            } else {\n                // Work around for setting sameSite cookie prior PHP 7.3\n                // https://stackoverflow.com/questions/39750906/php-setcookie-samesite-strict/46971326#46971326\n                $cookiePath = $cookie->path;\n                if (!is_null($cookie->sameSite)) {\n                    $cookiePath .= '; samesite=' . $cookie->sameSite;\n                }\n                setcookie($cookie->name, $value, $expire, $cookiePath, $cookie->domain, $cookie->secure, $cookie->httpOnly);\n            }\n        }\n    }\n\n    /**\n     * Sends the response content to the client.\n     */\n    protected function sendContent()\n    {\n        if ($this->stream === null) {\n            echo $this->content;\n\n            return;\n        }\n\n        // Try to reset time limit for big files\n        $setTimeLimitFailed = !function_exists('set_time_limit') || !@set_time_limit(0);\n\n        if (is_callable($this->stream)) {\n            $data = call_user_func($this->stream);\n            foreach ($data as $datum) {\n                echo $datum;\n                flush();\n            }\n            return;\n        }\n\n        $chunkSize = 8 * 1024 * 1024; // 8MB per chunk\n\n        $iterationCount = 0;\n        if (is_array($this->stream)) {\n            list($handle, $begin, $end) = $this->stream;\n\n            // only seek if stream is seekable\n            if ($this->isSeekable($handle)) {\n                fseek($handle, $begin);\n            }\n\n            while (!feof($handle) && ($pos = ftell($handle)) <= $end) {\n                $iterationCount++;\n                if ($setTimeLimitFailed && $iterationCount === 2) {\n                    Yii::warning('set_time_limit() is not available', __METHOD__);\n                }\n                if ($pos + $chunkSize > $end) {\n                    $chunkSize = $end - $pos + 1;\n                }\n                echo fread($handle, $chunkSize);\n                flush(); // Free up memory. Otherwise large files will trigger PHP's memory limit.\n            }\n            fclose($handle);\n        } else {\n            while (!feof($this->stream)) {\n                $iterationCount++;\n                if ($setTimeLimitFailed && $iterationCount === 2) {\n                    Yii::warning('set_time_limit() is not available', __METHOD__);\n                }\n                echo fread($this->stream, $chunkSize);\n                flush();\n            }\n            fclose($this->stream);\n        }\n    }\n\n    /**\n     * Sends a file to the browser.\n     *\n     * Note that this method only prepares the response for file sending. The file is not sent\n     * until [[send()]] is called explicitly or implicitly. The latter is done after you return from a controller action.\n     *\n     * The following is an example implementation of a controller action that allows requesting files from a directory\n     * that is not accessible from web:\n     *\n     * ```\n     * public function actionFile($filename)\n     * {\n     *     $storagePath = Yii::getAlias('@app/files');\n     *\n     *     // check filename for allowed chars (do not allow ../ to avoid security issue: downloading arbitrary files)\n     *     if (!preg_match('/^[a-z0-9]+\\.[a-z0-9]+$/i', $filename) || !is_file(\"$storagePath/$filename\")) {\n     *         throw new \\yii\\web\\NotFoundHttpException('The file does not exists.');\n     *     }\n     *     return Yii::$app->response->sendFile(\"$storagePath/$filename\", $filename);\n     * }\n     * ```\n     *\n     * @param string $filePath the path of the file to be sent.\n     * @param string|null $attachmentName the file name shown to the user. If null, it will be determined from `$filePath`.\n     * @param array $options additional options for sending the file. The following options are supported:\n     *\n     *  - `mimeType`: the MIME type of the content. If not set, it will be guessed based on `$filePath`\n     *  - `inline`: boolean, whether the browser should open the file within the browser window. Defaults to false,\n     *    meaning a download dialog will pop up.\n     *\n     * @return $this the response object itself\n     * @see sendContentAsFile()\n     * @see sendStreamAsFile()\n     * @see xSendFile()\n     */\n    public function sendFile($filePath, $attachmentName = null, $options = [])\n    {\n        if (!isset($options['mimeType'])) {\n            $options['mimeType'] = FileHelper::getMimeTypeByExtension($filePath);\n        }\n        if ($attachmentName === null) {\n            $attachmentName = basename($filePath);\n        }\n        $handle = fopen($filePath, 'rb');\n        $this->sendStreamAsFile($handle, $attachmentName, $options);\n\n        return $this;\n    }\n\n    /**\n     * Sends the specified content as a file to the browser.\n     *\n     * Note that this method only prepares the response for file sending. The file is not sent\n     * until [[send()]] is called explicitly or implicitly. The latter is done after you return from a controller action.\n     *\n     * @param string $content the content to be sent. The existing [[content]] will be discarded.\n     * @param string $attachmentName the file name shown to the user.\n     * @param array $options additional options for sending the file. The following options are supported:\n     *\n     *  - `mimeType`: the MIME type of the content. Defaults to 'application/octet-stream'.\n     *  - `inline`: boolean, whether the browser should open the file within the browser window. Defaults to false,\n     *    meaning a download dialog will pop up.\n     *\n     * @return $this the response object itself\n     * @throws RangeNotSatisfiableHttpException if the requested range is not satisfiable\n     * @see sendFile() for an example implementation.\n     */\n    public function sendContentAsFile($content, $attachmentName, $options = [])\n    {\n        $headers = $this->getHeaders();\n\n        $contentLength = StringHelper::byteLength($content);\n        $range = $this->getHttpRange($contentLength);\n\n        if ($range === false) {\n            $headers->set('Content-Range', \"bytes */$contentLength\");\n            throw new RangeNotSatisfiableHttpException();\n        }\n\n        list($begin, $end) = $range;\n        if ($begin != 0 || $end != $contentLength - 1) {\n            $this->setStatusCode(206);\n            $headers->set('Content-Range', \"bytes $begin-$end/$contentLength\");\n            $this->content = StringHelper::byteSubstr($content, $begin, $end - $begin + 1);\n        } else {\n            $this->setStatusCode(200);\n            $this->content = $content;\n        }\n\n        $mimeType = isset($options['mimeType']) ? $options['mimeType'] : 'application/octet-stream';\n        $this->setDownloadHeaders($attachmentName, $mimeType, !empty($options['inline']), $end - $begin + 1);\n\n        $this->format = self::FORMAT_RAW;\n\n        return $this;\n    }\n\n    /**\n     * Sends the specified stream as a file to the browser.\n     *\n     * Note that this method only prepares the response for file sending. The file is not sent\n     * until [[send()]] is called explicitly or implicitly. The latter is done after you return from a controller action.\n     *\n     * @param resource $handle the handle of the stream to be sent.\n     * @param string $attachmentName the file name shown to the user.\n     * @param array $options additional options for sending the file. The following options are supported:\n     *\n     *  - `mimeType`: the MIME type of the content. Defaults to 'application/octet-stream'.\n     *  - `inline`: boolean, whether the browser should open the file within the browser window. Defaults to false,\n     *    meaning a download dialog will pop up.\n     *  - `fileSize`: the size of the content to stream this is useful when size of the content is known\n     *    and the content is not seekable. Defaults to content size using `ftell()`.\n     *    This option is available since version 2.0.4.\n     *\n     * @return $this the response object itself\n     * @throws RangeNotSatisfiableHttpException if the requested range is not satisfiable\n     * @see sendFile() for an example implementation.\n     */\n    public function sendStreamAsFile($handle, $attachmentName, $options = [])\n    {\n        $headers = $this->getHeaders();\n        if (isset($options['fileSize'])) {\n            $fileSize = $options['fileSize'];\n        } else {\n            if ($this->isSeekable($handle)) {\n                fseek($handle, 0, SEEK_END);\n                $fileSize = ftell($handle);\n            } else {\n                $fileSize = 0;\n            }\n        }\n\n        $range = $this->getHttpRange($fileSize);\n        if ($range === false) {\n            $headers->set('Content-Range', \"bytes */$fileSize\");\n            throw new RangeNotSatisfiableHttpException();\n        }\n\n        list($begin, $end) = $range;\n        if ($begin != 0 || $end != $fileSize - 1) {\n            $this->setStatusCode(206);\n            $headers->set('Content-Range', \"bytes $begin-$end/$fileSize\");\n        } else {\n            $this->setStatusCode(200);\n        }\n\n        $mimeType = isset($options['mimeType']) ? $options['mimeType'] : 'application/octet-stream';\n        $this->setDownloadHeaders($attachmentName, $mimeType, !empty($options['inline']), $end - $begin + 1);\n\n        $this->format = self::FORMAT_RAW;\n        $this->stream = [$handle, $begin, $end];\n\n        return $this;\n    }\n\n    /**\n     * Sets a default set of HTTP headers for file downloading purpose.\n     * @param string $attachmentName the attachment file name\n     * @param string|null $mimeType the MIME type for the response. If null, `Content-Type` header will NOT be set.\n     * @param bool $inline whether the browser should open the file within the browser window. Defaults to false,\n     * meaning a download dialog will pop up.\n     * @param int|null $contentLength the byte length of the file being downloaded. If null, `Content-Length` header will NOT be set.\n     * @return $this the response object itself\n     */\n    public function setDownloadHeaders($attachmentName, $mimeType = null, $inline = false, $contentLength = null)\n    {\n        $headers = $this->getHeaders();\n\n        $disposition = $inline ? 'inline' : 'attachment';\n        $headers->setDefault('Pragma', 'public')\n            ->setDefault('Accept-Ranges', 'bytes')\n            ->setDefault('Expires', '0')\n            ->setDefault('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')\n            ->setDefault('Content-Disposition', $this->getDispositionHeaderValue($disposition, $attachmentName));\n\n        if ($mimeType !== null) {\n            $headers->setDefault('Content-Type', $mimeType);\n        }\n\n        if ($contentLength !== null) {\n            $headers->setDefault('Content-Length', $contentLength);\n        }\n\n        return $this;\n    }\n\n    /**\n     * Determines the HTTP range given in the request.\n     * @param int $fileSize the size of the file that will be used to validate the requested HTTP range.\n     * @return array|bool the range (begin, end), or false if the range request is invalid.\n     */\n    protected function getHttpRange($fileSize)\n    {\n        $rangeHeader = Yii::$app->getRequest()->getHeaders()->get('Range', '-');\n        if ($rangeHeader === '-') {\n            return [0, $fileSize - 1];\n        }\n        if (!preg_match('/^bytes=(\\d*)-(\\d*)$/', $rangeHeader, $matches)) {\n            return false;\n        }\n        if ($matches[1] === '') {\n            $start = $fileSize - $matches[2];\n            $end = $fileSize - 1;\n        } elseif ($matches[2] !== '') {\n            $start = $matches[1];\n            $end = $matches[2];\n            if ($end >= $fileSize) {\n                $end = $fileSize - 1;\n            }\n        } else {\n            $start = $matches[1];\n            $end = $fileSize - 1;\n        }\n        if ($start < 0 || $start > $end) {\n            return false;\n        }\n\n        return [$start, $end];\n    }\n\n    /**\n     * Sends existing file to a browser as a download using x-sendfile.\n     *\n     * X-Sendfile is a feature allowing a web application to redirect the request for a file to the webserver\n     * that in turn processes the request, this way eliminating the need to perform tasks like reading the file\n     * and sending it to the user. When dealing with a lot of files (or very big files) this can lead to a great\n     * increase in performance as the web application is allowed to terminate earlier while the webserver is\n     * handling the request.\n     *\n     * The request is sent to the server through a special non-standard HTTP-header.\n     * When the web server encounters the presence of such header it will discard all output and send the file\n     * specified by that header using web server internals including all optimizations like caching-headers.\n     *\n     * As this header directive is non-standard different directives exists for different web servers applications:\n     *\n     * - Apache: [X-Sendfile](https://tn123.org/mod_xsendfile/)\n     * - Lighttpd v1.4: [X-LIGHTTPD-send-file](https://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file)\n     * - Lighttpd v1.5: [X-Sendfile](https://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file)\n     * - Nginx: [X-Accel-Redirect](https://www.nginx.com/resources/wiki/XSendfile)\n     * - Cherokee: [X-Sendfile and X-Accel-Redirect](https://cherokee-project.com/doc/other_goodies.html#x-sendfile)\n     *\n     * So for this method to work the X-SENDFILE option/module should be enabled by the web server and\n     * a proper xHeader should be sent.\n     *\n     * **Note**\n     *\n     * This option allows to download files that are not under web folders, and even files that are otherwise protected\n     * (deny from all) like `.htaccess`.\n     *\n     * **Side effects**\n     *\n     * If this option is disabled by the web server, when this method is called a download configuration dialog\n     * will open but the downloaded file will have 0 bytes.\n     *\n     * **Known issues**\n     *\n     * There is a Bug with Internet Explorer 6, 7 and 8 when X-SENDFILE is used over an SSL connection, it will show\n     * an error message like this: \"Internet Explorer was not able to open this Internet site. The requested site\n     * is either unavailable or cannot be found.\". You can work around this problem by removing the `Pragma`-header.\n     *\n     * **Example**\n     *\n     * ```\n     * Yii::$app->response->xSendFile('/home/user/Pictures/picture1.jpg');\n     * ```\n     *\n     * @param string $filePath file name with full path\n     * @param string|null $attachmentName file name shown to the user. If null, it will be determined from `$filePath`.\n     * @param array $options additional options for sending the file. The following options are supported:\n     *\n     *  - `mimeType`: the MIME type of the content. If not set, it will be guessed based on `$filePath`\n     *  - `inline`: boolean, whether the browser should open the file within the browser window. Defaults to false,\n     *    meaning a download dialog will pop up.\n     *  - xHeader: string, the name of the x-sendfile header. Defaults to \"X-Sendfile\".\n     *\n     * @return $this the response object itself\n     * @see sendFile()\n     */\n    public function xSendFile($filePath, $attachmentName = null, $options = [])\n    {\n        if ($attachmentName === null) {\n            $attachmentName = basename($filePath);\n        }\n        if (isset($options['mimeType'])) {\n            $mimeType = $options['mimeType'];\n        } elseif (($mimeType = FileHelper::getMimeTypeByExtension($filePath)) === null) {\n            $mimeType = 'application/octet-stream';\n        }\n        if (isset($options['xHeader'])) {\n            $xHeader = $options['xHeader'];\n        } else {\n            $xHeader = 'X-Sendfile';\n        }\n\n        $disposition = empty($options['inline']) ? 'attachment' : 'inline';\n        $this->getHeaders()\n            ->setDefault($xHeader, $filePath)\n            ->setDefault('Content-Type', $mimeType)\n            ->setDefault('Content-Disposition', $this->getDispositionHeaderValue($disposition, $attachmentName));\n\n        $this->format = self::FORMAT_RAW;\n\n        return $this;\n    }\n\n    /**\n     * Returns Content-Disposition header value that is safe to use with both old and new browsers.\n     *\n     * Fallback name:\n     *\n     * - Causes issues if contains non-ASCII characters with codes less than 32 or more than 126.\n     * - Causes issues if contains urlencoded characters (starting with `%`) or `%` character. Some browsers interpret\n     *   `filename=\"X\"` as urlencoded name, some don't.\n     * - Causes issues if contains path separator characters such as `\\` or `/`.\n     * - Since value is wrapped with `\"`, it should be escaped as `\\\"`.\n     * - Since input could contain non-ASCII characters, fallback is obtained by transliteration.\n     *\n     * UTF name:\n     *\n     * - Causes issues if contains path separator characters such as `\\` or `/`.\n     * - Should be urlencoded since headers are ASCII-only.\n     * - Could be omitted if it exactly matches fallback name.\n     *\n     * @param string $disposition\n     * @param string $attachmentName\n     * @return string\n     *\n     * @since 2.0.10\n     */\n    protected function getDispositionHeaderValue($disposition, $attachmentName)\n    {\n        $fallbackName = str_replace(\n            ['%', '/', '\\\\', '\"', \"\\x7F\"],\n            ['_', '_', '_', '\\\\\"', '_'],\n            Inflector::transliterate($attachmentName, Inflector::TRANSLITERATE_LOOSE)\n        );\n        $utfName = rawurlencode(str_replace(['%', '/', '\\\\'], '', $attachmentName));\n\n        $dispositionHeader = \"{$disposition}; filename=\\\"{$fallbackName}\\\"\";\n        if ($utfName !== $fallbackName) {\n            $dispositionHeader .= \"; filename*=utf-8''{$utfName}\";\n        }\n\n        return $dispositionHeader;\n    }\n\n    /**\n     * Redirects the browser to the specified URL.\n     *\n     * This method adds a \"Location\" header to the current response. Note that it does not send out\n     * the header until [[send()]] is called. In a controller action you may use this method as follows:\n     *\n     * ```\n     * return Yii::$app->getResponse()->redirect($url);\n     * ```\n     *\n     * In other places, if you want to send out the \"Location\" header immediately, you should use\n     * the following code:\n     *\n     * ```\n     * Yii::$app->getResponse()->redirect($url)->send();\n     * return;\n     * ```\n     *\n     * In AJAX mode, this normally will not work as expected unless there are some\n     * client-side JavaScript code handling the redirection. To help achieve this goal,\n     * this method will send out a \"X-Redirect\" header instead of \"Location\".\n     *\n     * If you use the \"yii\" JavaScript module, it will handle the AJAX redirection as\n     * described above. Otherwise, you should write the following JavaScript code to\n     * handle the redirection:\n     *\n     * ```\n     * $document.ajaxComplete(function (event, xhr, settings) {\n     *     var url = xhr && xhr.getResponseHeader('X-Redirect');\n     *     if (url) {\n     *         window.location = url;\n     *     }\n     * });\n     * ```\n     *\n     * @param string|array $url the URL to be redirected to. This can be in one of the following formats:\n     *\n     * - a string representing a URL (e.g. \"https://example.com\")\n     * - a string representing a URL alias (e.g. \"@example.com\")\n     * - an array in the format of `[$route, ...name-value pairs...]` (e.g. `['site/index', 'ref' => 1]`).\n     *   Note that the route is with respect to the whole application, instead of relative to a controller or module.\n     *   [[Url::to()]] will be used to convert the array into a URL.\n     *\n     * Any relative URL that starts with a single forward slash \"/\" will be converted\n     * into an absolute one by prepending it with the host info of the current request.\n     *\n     * @param int $statusCode the HTTP status code. Defaults to 302.\n     * See <https://tools.ietf.org/html/rfc2616#section-10>\n     * for details about HTTP status code\n     * @param bool $checkAjax whether to specially handle AJAX (and PJAX) requests. Defaults to true,\n     * meaning if the current request is an AJAX or PJAX request, then calling this method will cause the browser\n     * to redirect to the given URL. If this is false, a `Location` header will be sent, which when received as\n     * an AJAX/PJAX response, may NOT cause browser redirection.\n     * Takes effect only when request header `X-Ie-Redirect-Compatibility` is absent.\n     * @return $this the response object itself\n     */\n    public function redirect($url, $statusCode = 302, $checkAjax = true)\n    {\n        if (is_array($url) && isset($url[0])) {\n            // ensure the route is absolute\n            $url[0] = '/' . ltrim($url[0], '/');\n        }\n        $request = Yii::$app->getRequest();\n        $normalizedUrl = Url::to($url);\n        if ($normalizedUrl !== null) {\n            if (preg_match('/\\n/', $normalizedUrl)) {\n                throw new InvalidRouteException('Route with new line character detected \"' . $normalizedUrl . '\".');\n            }\n            if (strncmp($normalizedUrl, '/', 1) === 0 && strncmp($normalizedUrl, '//', 2) !== 0) {\n                $normalizedUrl = $request->getHostInfo() . $normalizedUrl;\n            }\n        }\n\n        if ($checkAjax && $request->getIsAjax()) {\n            if (\n                in_array($statusCode, [301, 302])\n                && preg_match('/Trident\\/|MSIE /', (string)$request->userAgent)\n            ) {\n                $statusCode = 200;\n            }\n            if ($request->getIsPjax()) {\n                $this->getHeaders()->set('X-Pjax-Url', $normalizedUrl);\n            } else {\n                $this->getHeaders()->set('X-Redirect', $normalizedUrl);\n            }\n        } else {\n            $this->getHeaders()->set('Location', $normalizedUrl);\n        }\n\n        $this->setStatusCode($statusCode);\n\n        return $this;\n    }\n\n    /**\n     * Refreshes the current page.\n     * The effect of this method call is the same as the user pressing the refresh button of his browser\n     * (without re-posting data).\n     *\n     * In a controller action you may use this method like this:\n     *\n     * ```\n     * return Yii::$app->getResponse()->refresh();\n     * ```\n     *\n     * @param string $anchor the anchor that should be appended to the redirection URL.\n     * Defaults to empty. Make sure the anchor starts with '#' if you want to specify it.\n     * @return Response the response object itself\n     */\n    public function refresh($anchor = '')\n    {\n        return $this->redirect(Yii::$app->getRequest()->getUrl() . $anchor);\n    }\n\n    /**\n     * @var CookieCollection|null\n     */\n    private $_cookies;\n\n    /**\n     * Returns the cookie collection.\n     *\n     * Through the returned cookie collection, you add or remove cookies as follows,\n     *\n     * ```\n     * // add a cookie\n     * $response->cookies->add(new Cookie([\n     *     'name' => $name,\n     *     'value' => $value,\n     * ]);\n     *\n     * // remove a cookie\n     * $response->cookies->remove('name');\n     * // alternatively\n     * unset($response->cookies['name']);\n     * ```\n     *\n     * @return CookieCollection the cookie collection.\n     */\n    public function getCookies()\n    {\n        if ($this->_cookies === null) {\n            $this->_cookies = new CookieCollection();\n        }\n\n        return $this->_cookies;\n    }\n\n    /**\n     * @return bool whether this response has a valid [[statusCode]].\n     */\n    public function getIsInvalid()\n    {\n        return $this->getStatusCode() < 100 || $this->getStatusCode() >= 600;\n    }\n\n    /**\n     * @return bool whether this response is informational\n     */\n    public function getIsInformational()\n    {\n        return $this->getStatusCode() >= 100 && $this->getStatusCode() < 200;\n    }\n\n    /**\n     * @return bool whether this response is successful\n     */\n    public function getIsSuccessful()\n    {\n        return $this->getStatusCode() >= 200 && $this->getStatusCode() < 300;\n    }\n\n    /**\n     * @return bool whether this response is a redirection\n     */\n    public function getIsRedirection()\n    {\n        return $this->getStatusCode() >= 300 && $this->getStatusCode() < 400;\n    }\n\n    /**\n     * @return bool whether this response indicates a client error\n     */\n    public function getIsClientError()\n    {\n        return $this->getStatusCode() >= 400 && $this->getStatusCode() < 500;\n    }\n\n    /**\n     * @return bool whether this response indicates a server error\n     */\n    public function getIsServerError()\n    {\n        return $this->getStatusCode() >= 500 && $this->getStatusCode() < 600;\n    }\n\n    /**\n     * @return bool whether this response is OK\n     */\n    public function getIsOk()\n    {\n        return $this->getStatusCode() == 200;\n    }\n\n    /**\n     * @return bool whether this response indicates the current request is forbidden\n     */\n    public function getIsForbidden()\n    {\n        return $this->getStatusCode() == 403;\n    }\n\n    /**\n     * @return bool whether this response indicates the currently requested resource is not found\n     */\n    public function getIsNotFound()\n    {\n        return $this->getStatusCode() == 404;\n    }\n\n    /**\n     * @return bool whether this response is empty\n     */\n    public function getIsEmpty()\n    {\n        return in_array($this->getStatusCode(), [201, 204, 304]);\n    }\n\n    /**\n     * @return array the formatters that are supported by default\n     */\n    protected function defaultFormatters()\n    {\n        return [\n            self::FORMAT_HTML => [\n                'class' => 'yii\\web\\HtmlResponseFormatter',\n            ],\n            self::FORMAT_XML => [\n                'class' => 'yii\\web\\XmlResponseFormatter',\n            ],\n            self::FORMAT_JSON => [\n                'class' => 'yii\\web\\JsonResponseFormatter',\n            ],\n            self::FORMAT_JSONP => [\n                'class' => 'yii\\web\\JsonResponseFormatter',\n                'useJsonp' => true,\n            ],\n        ];\n    }\n\n    /**\n     * Prepares for sending the response.\n     * The default implementation will convert [[data]] into [[content]] and set headers accordingly.\n     * @throws InvalidConfigException if the formatter for the specified format is invalid or [[format]] is not supported\n     *\n     * @see https://tools.ietf.org/html/rfc7231#page-53\n     * @see https://tools.ietf.org/html/rfc7232#page-18\n     */\n    protected function prepare()\n    {\n        if (in_array($this->getStatusCode(), [204, 304])) {\n            // A 204/304 response cannot contain a message body according to rfc7231/rfc7232\n            $this->content = '';\n            $this->stream = null;\n            return;\n        }\n\n        if ($this->stream !== null) {\n            return;\n        }\n\n        if (isset($this->formatters[$this->format])) {\n            $formatter = $this->formatters[$this->format];\n            if (!is_object($formatter)) {\n                $this->formatters[$this->format] = $formatter = Yii::createObject($formatter);\n            }\n            if ($formatter instanceof ResponseFormatterInterface) {\n                $formatter->format($this);\n            } else {\n                throw new InvalidConfigException(\"The '{$this->format}' response formatter is invalid. It must implement the ResponseFormatterInterface.\");\n            }\n        } elseif ($this->format === self::FORMAT_RAW) {\n            if ($this->data !== null) {\n                $this->content = $this->data;\n            }\n        } else {\n            throw new InvalidConfigException(\"Unsupported response format: {$this->format}\");\n        }\n\n        if (is_array($this->content)) {\n            throw new InvalidArgumentException('Response content must not be an array.');\n        } elseif (is_object($this->content)) {\n            if (method_exists($this->content, '__toString')) {\n                $this->content = $this->content->__toString();\n            } else {\n                throw new InvalidArgumentException('Response content must be a string or an object implementing __toString().');\n            }\n        }\n    }\n\n    /**\n     * Checks if a stream is seekable\n     *\n     * @param $handle\n     * @return bool\n     */\n    private function isSeekable($handle)\n    {\n        if (!is_resource($handle)) {\n            return true;\n        }\n\n        $metaData = stream_get_meta_data($handle);\n        return isset($metaData['seekable']) && $metaData['seekable'] === true;\n    }\n}\n"
  },
  {
    "path": "framework/web/ResponseFormatterInterface.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\n/**\n * ResponseFormatterInterface specifies the interface needed to format a response before it is sent out.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\ninterface ResponseFormatterInterface\n{\n    /**\n     * Formats the specified response.\n     * @param Response $response the response to be formatted.\n     */\n    public function format($response);\n}\n"
  },
  {
    "path": "framework/web/ServerErrorHttpException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\n/**\n * ServerErrorHttpException represents an \"Internal Server Error\" HTTP exception with status code 500.\n *\n * @see https://tools.ietf.org/html/rfc7231#section-6.6.1\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass ServerErrorHttpException extends HttpException\n{\n    /**\n     * Constructor.\n     * @param string|null $message error message\n     * @param int $code error code\n     * @param \\Throwable|null $previous The previous exception used for the exception chaining.\n     */\n    public function __construct($message = null, $code = 0, $previous = null)\n    {\n        parent::__construct(500, $message, $code, $previous);\n    }\n}\n"
  },
  {
    "path": "framework/web/SessionHandler.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\nuse SessionHandlerInterface;\n\n/**\n * SessionHandler implements an [[\\SessionHandlerInterface]] for handling [[Session]] with custom session storage.\n *\n * @author Viktor Khokhryakov <viktor.khokhryakov@gmail.com>\n * @since 2.0.52\n */\nclass SessionHandler implements SessionHandlerInterface\n{\n    /**\n     * @var Session\n     */\n    private $_session;\n\n\n    public function __construct(Session $session)\n    {\n        $this->_session = $session;\n    }\n\n    /**\n     * @inheritDoc\n     */\n    public function close(): bool\n    {\n        return $this->_session->closeSession();\n    }\n\n    /**\n     * @inheritDoc\n     */\n    public function destroy($id): bool\n    {\n        return $this->_session->destroySession($id);\n    }\n\n    /**\n     * @inheritDoc\n     */\n    #[\\ReturnTypeWillChange]\n    public function gc($max_lifetime)\n    {\n        return $this->_session->gcSession($max_lifetime);\n    }\n\n    /**\n     * @inheritDoc\n     */\n    public function open($path, $name): bool\n    {\n        return $this->_session->openSession($path, $name);\n    }\n\n    /**\n     * @inheritDoc\n     */\n    #[\\ReturnTypeWillChange]\n    public function read($id)\n    {\n        return $this->_session->readSession($id);\n    }\n\n    /**\n     * @inheritDoc\n     */\n    public function write($id, $data): bool\n    {\n        return $this->_session->writeSession($id, $data);\n    }\n}\n"
  },
  {
    "path": "framework/web/SessionIterator.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\n/**\n * SessionIterator implements an [[\\Iterator|iterator]] for traversing session variables managed by [[Session]].\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @implements \\Iterator<array-key, mixed>\n */\nclass SessionIterator implements \\Iterator\n{\n    /**\n     * @var array list of keys in the map\n     */\n    private $_keys;\n    /**\n     * @var string|int|false current key\n     */\n    private $_key;\n\n\n    /**\n     * Constructor.\n     */\n    public function __construct()\n    {\n        $this->_keys = array_keys(isset($_SESSION) ? $_SESSION : []);\n        $this->rewind();\n    }\n\n    /**\n     * Rewinds internal array pointer.\n     * This method is required by the interface [[\\Iterator]].\n     */\n    #[\\ReturnTypeWillChange]\n    public function rewind()\n    {\n        $this->_key = reset($this->_keys);\n    }\n\n    /**\n     * Returns the key of the current array element.\n     * This method is required by the interface [[\\Iterator]].\n     * @return string|int|null the key of the current array element\n     */\n    #[\\ReturnTypeWillChange]\n    public function key()\n    {\n        return $this->_key === false ? null : $this->_key;\n    }\n\n    /**\n     * Returns the current array element.\n     * This method is required by the interface [[\\Iterator]].\n     * @return mixed the current array element\n     */\n    #[\\ReturnTypeWillChange]\n    public function current()\n    {\n        return $this->_key !== false && isset($_SESSION[$this->_key]) ? $_SESSION[$this->_key] : null;\n    }\n\n    /**\n     * Moves the internal pointer to the next array element.\n     * This method is required by the interface [[\\Iterator]].\n     */\n    #[\\ReturnTypeWillChange]\n    public function next()\n    {\n        do {\n            $this->_key = next($this->_keys);\n        } while ($this->_key !== false && !isset($_SESSION[$this->_key]));\n    }\n\n    /**\n     * Returns whether there is an element at current position.\n     * This method is required by the interface [[\\Iterator]].\n     * @return bool\n     */\n    #[\\ReturnTypeWillChange]\n    public function valid()\n    {\n        return $this->_key !== false;\n    }\n}\n"
  },
  {
    "path": "framework/web/TooManyRequestsHttpException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\n/**\n * TooManyRequestsHttpException represents a \"Too Many Requests\" HTTP exception with status code 429.\n *\n * Use this exception to indicate that a client has made too many requests in a\n * given period of time. For example, you would throw this exception when\n * 'throttling' an API user.\n *\n * @see https://tools.ietf.org/html/rfc6585#section-4\n * @author Dan Schmidt <danschmidt5189@gmail.com>\n * @since 2.0\n */\nclass TooManyRequestsHttpException extends HttpException\n{\n    /**\n     * Constructor.\n     * @param string|null $message error message\n     * @param int $code error code\n     * @param \\Throwable|null $previous The previous exception used for the exception chaining.\n     */\n    public function __construct($message = null, $code = 0, $previous = null)\n    {\n        parent::__construct(429, $message, $code, $previous);\n    }\n}\n"
  },
  {
    "path": "framework/web/UnauthorizedHttpException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\n/**\n * UnauthorizedHttpException represents an \"Unauthorized\" HTTP exception with status code 401.\n *\n * Use this exception to indicate that a client needs to authenticate via WWW-Authenticate header\n * to perform the requested action.\n *\n * If the client is already authenticated and is simply not allowed to\n * perform the action, consider using a 403 [[ForbiddenHttpException]]\n * or 404 [[NotFoundHttpException]] instead.\n *\n * @link https://tools.ietf.org/html/rfc7235#section-3.1\n * @author Dan Schmidt <danschmidt5189@gmail.com>\n * @since 2.0\n */\nclass UnauthorizedHttpException extends HttpException\n{\n    /**\n     * Constructor.\n     * @param string|null $message error message\n     * @param int $code error code\n     * @param \\Throwable|null $previous The previous exception used for the exception chaining.\n     */\n    public function __construct($message = null, $code = 0, $previous = null)\n    {\n        parent::__construct(401, $message, $code, $previous);\n    }\n}\n"
  },
  {
    "path": "framework/web/UnsupportedMediaTypeHttpException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\n/**\n * UnsupportedMediaTypeHttpException represents an \"Unsupported Media Type\" HTTP exception with status code 415.\n *\n * Use this exception when the client sends data in a format that your\n * application does not understand. For example, you would throw this exception\n * if the client POSTs XML data to an action or controller that only accepts\n * JSON.\n *\n * @see https://tools.ietf.org/html/rfc7231#section-6.5.13\n * @author Dan Schmidt <danschmidt5189@gmail.com>\n * @since 2.0\n */\nclass UnsupportedMediaTypeHttpException extends HttpException\n{\n    /**\n     * Constructor.\n     * @param string|null $message error message\n     * @param int $code error code\n     * @param \\Throwable|null $previous The previous exception used for the exception chaining.\n     */\n    public function __construct($message = null, $code = 0, $previous = null)\n    {\n        parent::__construct(415, $message, $code, $previous);\n    }\n}\n"
  },
  {
    "path": "framework/web/UploadedFile.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\nuse Yii;\nuse yii\\base\\BaseObject;\nuse yii\\helpers\\ArrayHelper;\nuse yii\\helpers\\Html;\n\n/**\n * UploadedFile represents the information for an uploaded file.\n *\n * You can call [[getInstance()]] to retrieve the instance of an uploaded file,\n * and then use [[saveAs()]] to save it on the server.\n * You may also query other information about the file, including [[name]],\n * [[tempName]], [[type]], [[size]], [[error]] and [[fullPath]].\n *\n * For more details and usage information on UploadedFile, see the [guide article on handling uploads](guide:input-file-upload).\n *\n * @property-read string $baseName Original file base name.\n * @property-read string $extension File extension.\n * @property-read bool $hasError Whether there is an error with the uploaded file. Check [[error]] for\n * detailed error code information.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass UploadedFile extends BaseObject\n{\n    /**\n     * @var string the original name of the file being uploaded\n     */\n    public $name;\n    /**\n     * @var string the path of the uploaded file on the server.\n     * Note, this is a temporary file which will be automatically deleted by PHP\n     * after the current request is processed.\n     */\n    public $tempName;\n    /**\n     * @var string the MIME-type of the uploaded file (such as \"image/gif\").\n     * Since this MIME type is not checked on the server-side, do not take this value for granted.\n     * Instead, use [[\\yii\\helpers\\FileHelper::getMimeType()]] to determine the exact MIME type.\n     */\n    public $type;\n    /**\n     * @var int the actual size of the uploaded file in bytes\n     */\n    public $size;\n    /**\n     * @var int an error code describing the status of this file uploading.\n     * @see https://www.php.net/manual/en/features.file-upload.errors.php\n     */\n    public $error;\n    /**\n     * @var string|null The full path as submitted by the browser. Note this value does not always\n     * contain a real directory structure, and cannot be trusted. Available as of PHP 8.1.\n     * @since 2.0.46\n     */\n    public $fullPath;\n\n    /**\n     * @var resource|null a temporary uploaded stream resource used within PUT and PATCH request.\n     */\n    private $_tempResource;\n    /**\n     * @var array[]|null\n     */\n    private static $_files;\n\n\n    /**\n     * UploadedFile constructor.\n     *\n     * @param array $config name-value pairs that will be used to initialize the object properties\n     */\n    public function __construct($config = [])\n    {\n        $this->_tempResource = ArrayHelper::remove($config, 'tempResource');\n        parent::__construct($config);\n    }\n\n    /**\n     * String output.\n     * This is PHP magic method that returns string representation of an object.\n     * The implementation here returns the uploaded file's name.\n     * @return string the string representation of the object\n     */\n    public function __toString()\n    {\n        return $this->name;\n    }\n\n    /**\n     * Returns an uploaded file for the given model attribute.\n     * The file should be uploaded using [[\\yii\\widgets\\ActiveField::fileInput()]].\n     * @param \\yii\\base\\Model $model the data model\n     * @param string $attribute the attribute name. The attribute name may contain array indexes.\n     * For example, '[1]file' for tabular file uploading; and 'file[1]' for an element in a file array.\n     * @return UploadedFile|null the instance of the uploaded file.\n     * Null is returned if no file is uploaded for the specified model attribute.\n     * @see getInstanceByName()\n     */\n    public static function getInstance($model, $attribute)\n    {\n        $name = Html::getInputName($model, $attribute);\n        return static::getInstanceByName($name);\n    }\n\n    /**\n     * Returns all uploaded files for the given model attribute.\n     * @param \\yii\\base\\Model $model the data model\n     * @param string $attribute the attribute name. The attribute name may contain array indexes\n     * for tabular file uploading, e.g. '[1]file'.\n     * @return UploadedFile[] array of UploadedFile objects.\n     * Empty array is returned if no available file was found for the given attribute.\n     */\n    public static function getInstances($model, $attribute)\n    {\n        $name = Html::getInputName($model, $attribute);\n        return static::getInstancesByName($name);\n    }\n\n    /**\n     * Returns an uploaded file according to the given file input name.\n     * The name can be a plain string or a string like an array element (e.g. 'Post[imageFile]', or 'Post[0][imageFile]').\n     * @param string $name the name of the file input field.\n     * @return UploadedFile|null the instance of the uploaded file.\n     * Null is returned if no file is uploaded for the specified name.\n     */\n    public static function getInstanceByName($name)\n    {\n        $files = self::loadFiles();\n        return isset($files[$name]) ? new static($files[$name]) : null;\n    }\n\n    /**\n     * Returns an array of uploaded files corresponding to the specified file input name.\n     * This is mainly used when multiple files were uploaded and saved as 'files[0]', 'files[1]',\n     * 'files[n]'..., and you can retrieve them all by passing 'files' as the name.\n     * @param string $name the name of the array of files\n     * @return UploadedFile[] the array of UploadedFile objects. Empty array is returned\n     * if no adequate upload was found. Please note that this array will contain\n     * all files from all sub-arrays regardless how deeply nested they are.\n     */\n    public static function getInstancesByName($name)\n    {\n        $files = self::loadFiles();\n        if (isset($files[$name])) {\n            return [new static($files[$name])];\n        }\n        $results = [];\n        foreach ($files as $key => $file) {\n            if (strpos($key, \"{$name}[\") === 0) {\n                $results[] = new static($file);\n            }\n        }\n\n        return $results;\n    }\n\n    /**\n     * Cleans up the loaded UploadedFile instances.\n     * This method is mainly used by test scripts to set up a fixture.\n     */\n    public static function reset()\n    {\n        self::$_files = null;\n    }\n\n    /**\n     * Saves the uploaded file.\n     * If the target file `$file` already exists, it will be overwritten.\n     * @param string $file the file path or a path alias used to save the uploaded file.\n     * @param bool $deleteTempFile whether to delete the temporary file after saving.\n     * If true, you will not be able to save the uploaded file again in the current request.\n     * @return bool true whether the file is saved successfully\n     * @see error\n     */\n    public function saveAs($file, $deleteTempFile = true)\n    {\n        if ($this->hasError) {\n            return false;\n        }\n\n        $targetFile = Yii::getAlias($file);\n        if (is_resource($this->_tempResource)) {\n            $result = $this->copyTempFile($targetFile);\n            return $deleteTempFile ? @fclose($this->_tempResource) : (bool) $result;\n        }\n\n        return $deleteTempFile ? move_uploaded_file($this->tempName, $targetFile) : copy($this->tempName, $targetFile);\n    }\n\n    /**\n     * Copy temporary file into file specified\n     *\n     * @param string $targetFile path of the file to copy to\n     * @return int|false the total count of bytes copied, or false on failure\n     * @since 2.0.32\n     */\n    protected function copyTempFile($targetFile)\n    {\n        $target = fopen($targetFile, 'wb');\n        if ($target === false) {\n            return false;\n        }\n\n        $result = stream_copy_to_stream($this->_tempResource, $target);\n        @fclose($target);\n\n        return $result;\n    }\n\n    /**\n     * @return string original file base name\n     */\n    public function getBaseName()\n    {\n        // https://github.com/yiisoft/yii2/issues/11012\n        $pathInfo = pathinfo('_' . $this->name, PATHINFO_FILENAME);\n        return mb_substr($pathInfo, 1, mb_strlen($pathInfo, '8bit'), '8bit');\n    }\n\n    /**\n     * @return string file extension\n     */\n    public function getExtension()\n    {\n        return strtolower(pathinfo($this->name, PATHINFO_EXTENSION));\n    }\n\n    /**\n     * @return bool whether there is an error with the uploaded file.\n     * Check [[error]] for detailed error code information.\n     */\n    public function getHasError()\n    {\n        return $this->error != UPLOAD_ERR_OK;\n    }\n\n    /**\n     * Returns reformated data of uplodaded files.\n     *\n     * @return array[]\n     */\n    private static function loadFiles()\n    {\n        if (self::$_files === null) {\n            self::$_files = [];\n            if (is_array($_FILES)) {\n                foreach ($_FILES as $key => $info) {\n                    self::loadFilesRecursive(\n                        $key,\n                        $info['name'],\n                        $info['tmp_name'],\n                        $info['type'],\n                        $info['size'],\n                        $info['error'],\n                        isset($info['full_path']) ? $info['full_path'] : [],\n                        isset($info['tmp_resource']) ? $info['tmp_resource'] : []\n                    );\n                }\n            }\n        }\n\n        return self::$_files;\n    }\n\n    /**\n     * Recursive reformats data of uplodaded file(s).\n     *\n     * @param string $key key for identifying uploaded file(sub-array index)\n     * @param string[]|string $names file name(s) provided by PHP\n     * @param string[]|string $tempNames temporary file name(s) provided by PHP\n     * @param string[]|string $types file type(s) provided by PHP\n     * @param int[]|int $sizes file size(s) provided by PHP\n     * @param int[]|int $errors uploading issue(s) provided by PHP\n     * @param array|string|null $fullPaths the full path(s) as submitted by the browser/PHP\n     * @param array|resource|null $tempResources the resource(s)\n     */\n    private static function loadFilesRecursive($key, $names, $tempNames, $types, $sizes, $errors, $fullPaths, $tempResources)\n    {\n        if (is_array($names)) {\n            foreach ($names as $i => $name) {\n                self::loadFilesRecursive(\n                    $key . '[' . $i . ']',\n                    $name,\n                    $tempNames[$i],\n                    $types[$i],\n                    $sizes[$i],\n                    $errors[$i],\n                    isset($fullPaths[$i]) ? $fullPaths[$i] : null,\n                    isset($tempResources[$i]) ? $tempResources[$i] : null\n                );\n            }\n\n            return;\n        }\n\n        /** @var int $errors */\n        if ($errors != UPLOAD_ERR_NO_FILE) {\n            self::$_files[$key] = [\n                'name' => $names,\n                'tempName' => $tempNames,\n                'tempResource' => is_resource($tempResources) ? $tempResources : null,\n                'type' => $types,\n                'size' => $sizes,\n                'error' => $errors,\n                'fullPath' => is_string($fullPaths) ? $fullPaths : null,\n            ];\n        }\n    }\n}\n"
  },
  {
    "path": "framework/web/UrlManager.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\nuse Yii;\nuse yii\\base\\Component;\nuse yii\\base\\InvalidConfigException;\nuse yii\\caching\\CacheInterface;\nuse yii\\di\\Instance;\nuse yii\\helpers\\Url;\n\n/**\n * UrlManager handles HTTP request parsing and creation of URLs based on a set of rules.\n *\n * UrlManager is configured as an application component in [[\\yii\\base\\Application]] by default.\n * You can access that instance via `Yii::$app->urlManager`.\n *\n * You can modify its configuration by adding an array to your application config under `components`\n * as it is shown in the following example:\n *\n * ```\n * 'urlManager' => [\n *     'enablePrettyUrl' => true,\n *     'rules' => [\n *         // your rules go here\n *     ],\n *     // ...\n * ]\n * ```\n *\n * Rules are classes implementing the [[UrlRuleInterface]], by default that is [[UrlRule]].\n * For nesting rules, there is also a [[GroupUrlRule]] class.\n *\n * For more details and usage information on UrlManager, see the [guide article on routing](guide:runtime-routing).\n *\n * @property string $baseUrl The base URL that is used by [[createUrl()]] to prepend to created URLs.\n * @property string $hostInfo The host info (e.g. `https://www.example.com`) that is used by\n * [[createAbsoluteUrl()]] to prepend to created URLs.\n * @property string $scriptUrl The entry script URL that is used by [[createUrl()]] to prepend to created\n * URLs.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\nclass UrlManager extends Component\n{\n    /**\n     * @var bool whether to enable pretty URLs. Instead of putting all parameters in the query\n     * string part of a URL, pretty URLs allow using path info to represent some of the parameters\n     * and can thus produce more user-friendly URLs, such as \"/news/Yii-is-released\", instead of\n     * \"/index.php?r=news%2Fview&id=100\".\n     */\n    public $enablePrettyUrl = false;\n    /**\n     * @var bool whether to enable strict parsing. If strict parsing is enabled, the incoming\n     * requested URL must match at least one of the [[rules]] in order to be treated as a valid request.\n     * Otherwise, the path info part of the request will be treated as the requested route.\n     * This property is used only when [[enablePrettyUrl]] is `true`.\n     */\n    public $enableStrictParsing = false;\n    /**\n     * @var array the rules for creating and parsing URLs when [[enablePrettyUrl]] is `true`.\n     * This property is used only if [[enablePrettyUrl]] is `true`. Each element in the array\n     * is the configuration array for creating a single URL rule. The configuration will\n     * be merged with [[ruleConfig]] first before it is used for creating the rule object.\n     *\n     * A special shortcut format can be used if a rule only specifies [[UrlRule::pattern|pattern]]\n     * and [[UrlRule::route|route]]: `'pattern' => 'route'`. That is, instead of using a configuration\n     * array, one can use the key to represent the pattern and the value the corresponding route.\n     * For example, `'post/<id:\\d+>' => 'post/view'`.\n     *\n     * For RESTful routing the mentioned shortcut format also allows you to specify the\n     * [[UrlRule::verb|HTTP verb]] that the rule should apply for.\n     * You can do that  by prepending it to the pattern, separated by space.\n     * For example, `'PUT post/<id:\\d+>' => 'post/update'`.\n     * You may specify multiple verbs by separating them with comma\n     * like this: `'POST,PUT post/index' => 'post/create'`.\n     * The supported verbs in the shortcut format are: GET, HEAD, POST, PUT, PATCH and DELETE.\n     * Note that [[UrlRule::mode|mode]] will be set to PARSING_ONLY when specifying verb in this way\n     * so you normally would not specify a verb for normal GET request.\n     *\n     * Here is an example configuration for RESTful CRUD controller:\n     *\n     * ```\n     * [\n     *     'dashboard' => 'site/index',\n     *\n     *     'POST <controller:[\\w-]+>' => '<controller>/create',\n     *     '<controller:[\\w-]+>s' => '<controller>/index',\n     *\n     *     'PUT <controller:[\\w-]+>/<id:\\d+>'    => '<controller>/update',\n     *     'DELETE <controller:[\\w-]+>/<id:\\d+>' => '<controller>/delete',\n     *     '<controller:[\\w-]+>/<id:\\d+>'        => '<controller>/view',\n     * ];\n     * ```\n     *\n     * Note that if you modify this property after the UrlManager object is created, make sure\n     * you populate the array with rule objects instead of rule configurations.\n     */\n    public $rules = [];\n    /**\n     * @var string the URL suffix used when [[enablePrettyUrl]] is `true`.\n     * For example, \".html\" can be used so that the URL looks like pointing to a static HTML page.\n     * This property is used only if [[enablePrettyUrl]] is `true`.\n     */\n    public $suffix;\n    /**\n     * @var bool whether to show entry script name in the constructed URL. Defaults to `true`.\n     * This property is used only if [[enablePrettyUrl]] is `true`.\n     */\n    public $showScriptName = true;\n    /**\n     * @var string the GET parameter name for route. This property is used only if [[enablePrettyUrl]] is `false`.\n     */\n    public $routeParam = 'r';\n    /**\n     * @var CacheInterface|array|string|bool|null the cache object or the application component ID of the cache object.\n     * This can also be an array that is used to create a [[CacheInterface]] instance in case you do not want to use\n     * an application component.\n     * Compiled URL rules will be cached through this cache object, if it is available.\n     *\n     * After the UrlManager object is created, if you want to change this property,\n     * you should only assign it with a cache object.\n     * Set this property to `false` or `null` if you do not want to cache the URL rules.\n     *\n     * Cache entries are stored for the time set by [[\\yii\\caching\\Cache::$defaultDuration|$defaultDuration]] in\n     * the cache configuration, which is unlimited by default. You may want to tune this value if your [[rules]]\n     * change frequently.\n     */\n    public $cache = 'cache';\n    /**\n     * @var array the default configuration of URL rules. Individual rule configurations\n     * specified via [[rules]] will take precedence when the same property of the rule is configured.\n     */\n    public $ruleConfig = ['class' => 'yii\\web\\UrlRule'];\n    /**\n     * @var UrlNormalizer|array|string|false the configuration for [[UrlNormalizer]] used by this UrlManager.\n     * The default value is `false`, which means normalization will be skipped.\n     * If you wish to enable URL normalization, you should configure this property manually.\n     * For example:\n     *\n     * ```\n     * [\n     *     'class' => 'yii\\web\\UrlNormalizer',\n     *     'collapseSlashes' => true,\n     *     'normalizeTrailingSlash' => true,\n     * ]\n     * ```\n     *\n     * @since 2.0.10\n     */\n    public $normalizer = false;\n\n    /**\n     * @var string the cache key for cached rules\n     * @since 2.0.8\n     */\n    protected $cacheKey = __CLASS__;\n\n    private $_baseUrl;\n    private $_scriptUrl;\n    private $_hostInfo;\n    private $_ruleCache;\n\n\n    /**\n     * Initializes UrlManager.\n     */\n    public function init()\n    {\n        parent::init();\n\n        if ($this->normalizer !== false) {\n            $this->normalizer = Yii::createObject($this->normalizer);\n            if (!$this->normalizer instanceof UrlNormalizer) {\n                throw new InvalidConfigException('`' . get_class($this) . '::normalizer` should be an instance of `' . UrlNormalizer::className() . '` or its DI compatible configuration.');\n            }\n        }\n\n        if (!$this->enablePrettyUrl) {\n            return;\n        }\n\n        if (!empty($this->rules)) {\n            $this->rules = $this->buildRules($this->rules);\n        }\n    }\n\n    /**\n     * Adds additional URL rules.\n     *\n     * This method will call [[buildRules()]] to parse the given rule declarations and then append or insert\n     * them to the existing [[rules]].\n     *\n     * Note that if [[enablePrettyUrl]] is `false`, this method will do nothing.\n     *\n     * @param array $rules the new rules to be added. Each array element represents a single rule declaration.\n     * Please refer to [[rules]] for the acceptable rule format.\n     * @param bool $append whether to add the new rules by appending them to the end of the existing rules.\n     */\n    public function addRules($rules, $append = true)\n    {\n        if (!$this->enablePrettyUrl) {\n            return;\n        }\n        $rules = $this->buildRules($rules);\n        if ($append) {\n            $this->rules = array_merge($this->rules, $rules);\n        } else {\n            $this->rules = array_merge($rules, $this->rules);\n        }\n    }\n\n    /**\n     * Builds URL rule objects from the given rule declarations.\n     *\n     * @param array $ruleDeclarations the rule declarations. Each array element represents a single rule declaration.\n     * Please refer to [[rules]] for the acceptable rule formats.\n     * @return UrlRuleInterface[] the rule objects built from the given rule declarations\n     * @throws InvalidConfigException if a rule declaration is invalid\n     */\n    protected function buildRules($ruleDeclarations)\n    {\n        $builtRules = $this->getBuiltRulesFromCache($ruleDeclarations);\n        if ($builtRules !== false) {\n            return $builtRules;\n        }\n\n        $builtRules = [];\n        $verbs = 'GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS';\n        foreach ($ruleDeclarations as $key => $rule) {\n            if (is_string($rule)) {\n                $rule = ['route' => $rule];\n                if (preg_match(\"/^((?:($verbs),)*($verbs))\\\\s+(.*)$/\", $key, $matches)) {\n                    $rule['verb'] = explode(',', $matches[1]);\n                    $key = $matches[4];\n                }\n                $rule['pattern'] = $key;\n            }\n            if (is_array($rule)) {\n                $rule = Yii::createObject(array_merge($this->ruleConfig, $rule));\n            }\n            if (!$rule instanceof UrlRuleInterface) {\n                throw new InvalidConfigException('URL rule class must implement UrlRuleInterface.');\n            }\n            $builtRules[] = $rule;\n        }\n\n        $this->setBuiltRulesCache($ruleDeclarations, $builtRules);\n\n        return $builtRules;\n    }\n\n    /**\n     * @return CacheInterface|null|bool\n     */\n    private function ensureCache()\n    {\n        if (!$this->cache instanceof CacheInterface && $this->cache !== false && $this->cache !== null) {\n            try {\n                $this->cache = Instance::ensure($this->cache, 'yii\\caching\\CacheInterface');\n            } catch (InvalidConfigException $e) {\n                Yii::warning('Unable to use cache for URL manager: ' . $e->getMessage());\n                $this->cache = null;\n            }\n        }\n\n        return $this->cache;\n    }\n\n    /**\n     * Stores $builtRules to cache, using $rulesDeclaration as a part of cache key.\n     *\n     * @param array $ruleDeclarations the rule declarations. Each array element represents a single rule declaration.\n     * Please refer to [[rules]] for the acceptable rule formats.\n     * @param UrlRuleInterface[] $builtRules the rule objects built from the given rule declarations.\n     * @return bool whether the value is successfully stored into cache\n     * @since 2.0.14\n     */\n    protected function setBuiltRulesCache($ruleDeclarations, $builtRules)\n    {\n        $cache = $this->ensureCache();\n        if (!$cache) {\n            return false;\n        }\n\n        return $cache->set([$this->cacheKey, $this->ruleConfig, $ruleDeclarations], $builtRules);\n    }\n\n    /**\n     * Provides the built URL rules that are associated with the $ruleDeclarations from cache.\n     *\n     * @param array $ruleDeclarations the rule declarations. Each array element represents a single rule declaration.\n     * Please refer to [[rules]] for the acceptable rule formats.\n     * @return UrlRuleInterface[]|false the rule objects built from the given rule declarations or boolean `false` when\n     * there are no cache items for this definition exists.\n     * @since 2.0.14\n     */\n    protected function getBuiltRulesFromCache($ruleDeclarations)\n    {\n        $cache = $this->ensureCache();\n        if (!$cache) {\n            return false;\n        }\n\n        return $cache->get([$this->cacheKey, $this->ruleConfig, $ruleDeclarations]);\n    }\n\n    /**\n     * Parses the user request.\n     * @param Request $request the request component\n     * @return array|bool the route and the associated parameters. The latter is always empty\n     * if [[enablePrettyUrl]] is `false`. `false` is returned if the current request cannot be successfully parsed.\n     */\n    public function parseRequest($request)\n    {\n        if ($this->enablePrettyUrl) {\n            /** @var UrlRule $rule */\n            foreach ($this->rules as $rule) {\n                $result = $rule->parseRequest($this, $request);\n                if (YII_DEBUG) {\n                    Yii::debug([\n                        'rule' => method_exists($rule, '__toString') ? $rule->__toString() : get_class($rule),\n                        'match' => $result !== false,\n                        'parent' => null,\n                    ], __METHOD__);\n                }\n                if ($result !== false) {\n                    return $result;\n                }\n            }\n\n            if ($this->enableStrictParsing) {\n                return false;\n            }\n\n            Yii::debug('No matching URL rules. Using default URL parsing logic.', __METHOD__);\n\n            $suffix = (string) $this->suffix;\n            $pathInfo = $request->getPathInfo();\n            $normalized = false;\n            if ($this->normalizer !== false) {\n                $pathInfo = $this->normalizer->normalizePathInfo($pathInfo, $suffix, $normalized);\n            }\n            if ($suffix !== '' && $pathInfo !== '') {\n                $n = strlen($this->suffix);\n                if (substr_compare($pathInfo, $this->suffix, -$n, $n) === 0) {\n                    $pathInfo = substr($pathInfo, 0, -$n);\n                    if ($pathInfo === '') {\n                        // suffix alone is not allowed\n                        return false;\n                    }\n                } else {\n                    // suffix doesn't match\n                    return false;\n                }\n            }\n\n            if ($normalized) {\n                // pathInfo was changed by normalizer - we need also normalize route\n                return $this->normalizer->normalizeRoute([$pathInfo, []]);\n            }\n\n            return [$pathInfo, []];\n        }\n\n        Yii::debug('Pretty URL not enabled. Using default URL parsing logic.', __METHOD__);\n        $route = $request->getQueryParam($this->routeParam, '');\n        if (is_array($route)) {\n            $route = '';\n        }\n\n        return [(string) $route, []];\n    }\n\n    /**\n     * Creates a URL using the given route and query parameters.\n     *\n     * You may specify the route as a string, e.g., `site/index`. You may also use an array\n     * if you want to specify additional query parameters for the URL being created. The\n     * array format must be:\n     *\n     * ```\n     * // generates: /index.php?r=site%2Findex&param1=value1&param2=value2\n     * ['site/index', 'param1' => 'value1', 'param2' => 'value2']\n     * ```\n     *\n     * If you want to create a URL with an anchor, you can use the array format with a `#` parameter.\n     * For example,\n     *\n     * ```\n     * // generates: /index.php?r=site%2Findex&param1=value1#name\n     * ['site/index', 'param1' => 'value1', '#' => 'name']\n     * ```\n     *\n     * The URL created is a relative one. Use [[createAbsoluteUrl()]] to create an absolute URL.\n     *\n     * Note that unlike [[\\yii\\helpers\\Url::toRoute()]], this method always treats the given route\n     * as an absolute route.\n     *\n     * @param string|array $params use a string to represent a route (e.g. `site/index`),\n     * or an array to represent a route with query parameters (e.g. `['site/index', 'param1' => 'value1']`).\n     * @return string the created URL\n     */\n    public function createUrl($params)\n    {\n        $params = (array) $params;\n        $anchor = isset($params['#']) ? '#' . $params['#'] : '';\n        unset($params['#'], $params[$this->routeParam]);\n\n        $route = trim(isset($params[0]) ? $params[0] : '', '/');\n        unset($params[0]);\n\n        $baseUrl = $this->showScriptName || !$this->enablePrettyUrl ? $this->getScriptUrl() : $this->getBaseUrl();\n\n        if ($this->enablePrettyUrl) {\n            $cacheKey = $route . '?';\n            foreach ($params as $key => $value) {\n                if ($value !== null) {\n                    $cacheKey .= $key . '&';\n                }\n            }\n\n            $url = $this->getUrlFromCache($cacheKey, $route, $params);\n            if ($url === false) {\n                /** @var UrlRule $rule */\n                foreach ($this->rules as $rule) {\n                    if (in_array($rule, $this->_ruleCache[$cacheKey], true)) {\n                        // avoid redundant calls of `UrlRule::createUrl()` for rules checked in `getUrlFromCache()`\n                        // @see https://github.com/yiisoft/yii2/issues/14094\n                        continue;\n                    }\n                    $url = $rule->createUrl($this, $route, $params);\n                    if ($this->canBeCached($rule)) {\n                        $this->setRuleToCache($cacheKey, $rule);\n                    }\n                    if ($url !== false) {\n                        break;\n                    }\n                }\n            }\n\n            if ($url !== false) {\n                if (strpos($url, '://') !== false) {\n                    if ($baseUrl !== '' && ($pos = strpos($url, '/', 8)) !== false) {\n                        return substr($url, 0, $pos) . $baseUrl . substr($url, $pos) . $anchor;\n                    }\n\n                    return $url . $baseUrl . $anchor;\n                } elseif (strncmp($url, '//', 2) === 0) {\n                    if ($baseUrl !== '' && ($pos = strpos($url, '/', 2)) !== false) {\n                        return substr($url, 0, $pos) . $baseUrl . substr($url, $pos) . $anchor;\n                    }\n\n                    return $url . $baseUrl . $anchor;\n                }\n\n                $url = ltrim($url, '/');\n                return \"$baseUrl/{$url}{$anchor}\";\n            }\n\n            if ($this->suffix !== null) {\n                $route .= $this->suffix;\n            }\n            if (!empty($params) && ($query = http_build_query($params)) !== '') {\n                $route .= '?' . $query;\n            }\n\n            $route = ltrim($route, '/');\n            return \"$baseUrl/{$route}{$anchor}\";\n        }\n\n        $url = \"$baseUrl?{$this->routeParam}=\" . urlencode($route);\n        if (!empty($params) && ($query = http_build_query($params)) !== '') {\n            $url .= '&' . $query;\n        }\n\n        return $url . $anchor;\n    }\n\n    /**\n     * Returns the value indicating whether result of [[createUrl()]] of rule should be cached in internal cache.\n     *\n     * @param UrlRuleInterface $rule\n     * @return bool `true` if result should be cached, `false` if not.\n     * @since 2.0.12\n     * @see getUrlFromCache()\n     * @see setRuleToCache()\n     * @see UrlRule::getCreateUrlStatus()\n     */\n    protected function canBeCached(UrlRuleInterface $rule)\n    {\n        return\n            // if rule does not provide info about create status, we cache it every time to prevent bugs like #13350\n            // @see https://github.com/yiisoft/yii2/pull/13350#discussion_r114873476\n            !method_exists($rule, 'getCreateUrlStatus') || ($status = $rule->getCreateUrlStatus()) === null\n            || $status === UrlRule::CREATE_STATUS_SUCCESS\n            || $status & UrlRule::CREATE_STATUS_PARAMS_MISMATCH;\n    }\n\n    /**\n     * Get URL from internal cache if exists.\n     * @param string $cacheKey generated cache key to store data.\n     * @param string $route the route (e.g. `site/index`).\n     * @param array $params rule params.\n     * @return bool|string the created URL\n     * @see createUrl()\n     * @since 2.0.8\n     */\n    protected function getUrlFromCache($cacheKey, $route, $params)\n    {\n        if (!empty($this->_ruleCache[$cacheKey])) {\n            foreach ($this->_ruleCache[$cacheKey] as $rule) {\n                /** @var UrlRule $rule */\n                if (($url = $rule->createUrl($this, $route, $params)) !== false) {\n                    return $url;\n                }\n            }\n        } else {\n            $this->_ruleCache[$cacheKey] = [];\n        }\n\n        return false;\n    }\n\n    /**\n     * Store rule (e.g. [[UrlRule]]) to internal cache.\n     * @param $cacheKey\n     * @param UrlRuleInterface $rule\n     * @since 2.0.8\n     */\n    protected function setRuleToCache($cacheKey, UrlRuleInterface $rule)\n    {\n        $this->_ruleCache[$cacheKey][] = $rule;\n    }\n\n    /**\n     * Creates an absolute URL using the given route and query parameters.\n     *\n     * This method prepends the URL created by [[createUrl()]] with the [[hostInfo]].\n     *\n     * Note that unlike [[\\yii\\helpers\\Url::toRoute()]], this method always treats the given route\n     * as an absolute route.\n     *\n     * @param string|array $params use a string to represent a route (e.g. `site/index`),\n     * or an array to represent a route with query parameters (e.g. `['site/index', 'param1' => 'value1']`).\n     * @param string|null $scheme the scheme to use for the URL (either `http`, `https` or empty string\n     * for protocol-relative URL).\n     * If not specified the scheme of the current request will be used.\n     * @return string the created URL\n     * @see createUrl()\n     */\n    public function createAbsoluteUrl($params, $scheme = null)\n    {\n        $params = (array) $params;\n        $url = $this->createUrl($params);\n        if (strpos($url, '://') === false) {\n            $hostInfo = $this->getHostInfo();\n            if (strncmp($url, '//', 2) === 0) {\n                $url = substr($hostInfo, 0, strpos($hostInfo, '://')) . ':' . $url;\n            } else {\n                $url = $hostInfo . $url;\n            }\n        }\n\n        return Url::ensureScheme($url, $scheme);\n    }\n\n    /**\n     * Returns the base URL that is used by [[createUrl()]] to prepend to created URLs.\n     * It defaults to [[Request::baseUrl]].\n     * This is mainly used when [[enablePrettyUrl]] is `true` and [[showScriptName]] is `false`.\n     * @return string the base URL that is used by [[createUrl()]] to prepend to created URLs.\n     * @throws InvalidConfigException if running in console application and [[baseUrl]] is not configured.\n     */\n    public function getBaseUrl()\n    {\n        if ($this->_baseUrl === null) {\n            $request = Yii::$app->getRequest();\n            if ($request instanceof Request) {\n                $this->_baseUrl = $request->getBaseUrl();\n            } else {\n                throw new InvalidConfigException('Please configure UrlManager::baseUrl correctly as you are running a console application.');\n            }\n        }\n\n        return $this->_baseUrl;\n    }\n\n    /**\n     * Sets the base URL that is used by [[createUrl()]] to prepend to created URLs.\n     * This is mainly used when [[enablePrettyUrl]] is `true` and [[showScriptName]] is `false`.\n     * @param string $value the base URL that is used by [[createUrl()]] to prepend to created URLs.\n     */\n    public function setBaseUrl($value)\n    {\n        $this->_baseUrl = $value === null ? null : rtrim(Yii::getAlias($value), '/');\n    }\n\n    /**\n     * Returns the entry script URL that is used by [[createUrl()]] to prepend to created URLs.\n     * It defaults to [[Request::scriptUrl]].\n     * This is mainly used when [[enablePrettyUrl]] is `false` or [[showScriptName]] is `true`.\n     * @return string the entry script URL that is used by [[createUrl()]] to prepend to created URLs.\n     * @throws InvalidConfigException if running in console application and [[scriptUrl]] is not configured.\n     */\n    public function getScriptUrl()\n    {\n        if ($this->_scriptUrl === null) {\n            $request = Yii::$app->getRequest();\n            if ($request instanceof Request) {\n                $this->_scriptUrl = $request->getScriptUrl();\n            } else {\n                throw new InvalidConfigException('Please configure UrlManager::scriptUrl correctly as you are running a console application.');\n            }\n        }\n\n        return $this->_scriptUrl;\n    }\n\n    /**\n     * Sets the entry script URL that is used by [[createUrl()]] to prepend to created URLs.\n     * This is mainly used when [[enablePrettyUrl]] is `false` or [[showScriptName]] is `true`.\n     * @param string $value the entry script URL that is used by [[createUrl()]] to prepend to created URLs.\n     */\n    public function setScriptUrl($value)\n    {\n        $this->_scriptUrl = $value;\n    }\n\n    /**\n     * Returns the host info that is used by [[createAbsoluteUrl()]] to prepend to created URLs.\n     * @return string the host info (e.g. `https://www.example.com`) that is used by [[createAbsoluteUrl()]] to prepend to created URLs.\n     * @throws InvalidConfigException if running in console application and [[hostInfo]] is not configured.\n     */\n    public function getHostInfo()\n    {\n        if ($this->_hostInfo === null) {\n            $request = Yii::$app->getRequest();\n            if ($request instanceof \\yii\\web\\Request) {\n                $this->_hostInfo = $request->getHostInfo();\n            } else {\n                throw new InvalidConfigException('Please configure UrlManager::hostInfo correctly as you are running a console application.');\n            }\n        }\n\n        return $this->_hostInfo;\n    }\n\n    /**\n     * Sets the host info that is used by [[createAbsoluteUrl()]] to prepend to created URLs.\n     * @param string $value the host info (e.g. \"https://www.example.com\") that is used by [[createAbsoluteUrl()]] to prepend to created URLs.\n     */\n    public function setHostInfo($value)\n    {\n        $this->_hostInfo = $value === null ? null : rtrim($value, '/');\n    }\n}\n"
  },
  {
    "path": "framework/web/UrlNormalizerRedirectException.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\n/**\n * UrlNormalizerRedirectException represents an information for redirection which should be\n * performed during the URL normalization.\n *\n * @author Robert Korulczyk <robert@korulczyk.pl>\n * @since 2.0.10\n */\nclass UrlNormalizerRedirectException extends \\yii\\base\\Exception\n{\n    /**\n     * @var array|string the parameter to be used to generate a valid URL for redirection\n     * @see \\yii\\helpers\\Url::to()\n     */\n    public $url;\n    /**\n     * @var bool|string the URI scheme to use in the generated URL for redirection\n     * @see \\yii\\helpers\\Url::to()\n     */\n    public $scheme;\n    /**\n     * @var int the HTTP status code\n     */\n    public $statusCode;\n\n\n    /**\n     * @param array|string $url the parameter to be used to generate a valid URL for redirection.\n     * This will be used as first parameter for [[\\yii\\helpers\\Url::to()]]\n     * @param int $statusCode HTTP status code used for redirection\n     * @param bool|string $scheme the URI scheme to use in the generated URL for redirection.\n     * This will be used as second parameter for [[\\yii\\helpers\\Url::to()]]\n     * @param string|null $message the error message\n     * @param int $code the error code\n     * @param \\Throwable|null $previous the previous exception used for the exception chaining\n     */\n    public function __construct($url, $statusCode = 302, $scheme = false, $message = null, $code = 0, $previous = null)\n    {\n        $this->url = $url;\n        $this->scheme = $scheme;\n        $this->statusCode = $statusCode;\n        parent::__construct((string)$message, $code, $previous);\n    }\n}\n"
  },
  {
    "path": "framework/web/UrlRuleInterface.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\n/**\n * UrlRuleInterface is the interface that should be implemented by URL rule classes.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n */\ninterface UrlRuleInterface\n{\n    /**\n     * Parses the given request and returns the corresponding route and parameters.\n     * @param UrlManager $manager the URL manager\n     * @param Request $request the request component\n     * @return array|bool the parsing result. The route and the parameters are returned as an array.\n     * If false, it means this rule cannot be used to parse this path info.\n     */\n    public function parseRequest($manager, $request);\n\n    /**\n     * Creates a URL according to the given route and parameters.\n     * @param UrlManager $manager the URL manager\n     * @param string $route the route. It should not have slashes at the beginning or the end.\n     * @param array $params the parameters\n     * @return string|bool the created URL, or false if this rule cannot be used for creating this URL.\n     */\n    public function createUrl($manager, $route, $params);\n}\n"
  },
  {
    "path": "framework/web/View.php",
    "content": "<?php\n\n/**\n * @link https://www.yiiframework.com/\n * @copyright Copyright (c) 2008 Yii Software LLC\n * @license https://www.yiiframework.com/license/\n */\n\nnamespace yii\\web;\n\nuse Yii;\nuse yii\\base\\InvalidConfigException;\nuse yii\\helpers\\ArrayHelper;\nuse yii\\helpers\\Html;\nuse yii\\helpers\\Url;\n\n/**\n * View represents a view object in the MVC pattern.\n *\n * View provides a set of methods (e.g. [[render()]]) for rendering purpose.\n *\n * View is configured as an application component in [[\\yii\\base\\Application]] by default.\n * You can access that instance via `Yii::$app->view`.\n *\n * You can modify its configuration by adding an array to your application config under `components`\n * as it is shown in the following example:\n *\n * ```\n * 'view' => [\n *     'theme' => 'app\\themes\\MyTheme',\n *     'renderers' => [\n *         // you may add Smarty or Twig renderer here\n *     ]\n *     // ...\n * ]\n * ```\n *\n * For more details and usage information on View, see the [guide article on views](guide:structure-views).\n *\n * @property \\yii\\web\\AssetManager $assetManager The asset manager. Defaults to the \"assetManager\" application\n * component.\n *\n * @author Qiang Xue <qiang.xue@gmail.com>\n * @since 2.0\n *\n * @phpstan-type RegisterJsFileOptions array{\n *     depends?: class-string[],\n *     position?: int,\n *     appendTimestamp?: bool,\n *     ...\n * }\n *\n * @phpstan-type RegisterCssFileOptions array{\n *     depends?: class-string[],\n *     appendTimestamp?: bool,\n *     ...\n * }\n */\nclass View extends \\yii\\base\\View\n{\n    /**\n     * @event Event an event that is triggered by [[beginBody()]].\n     */\n    public const EVENT_BEGIN_BODY = 'beginBody';\n    /**\n     * @event Event an event that is triggered by [[endBody()]].\n     */\n    public const EVENT_END_BODY = 'endBody';\n    /**\n     * The location of registered JavaScript code block or files.\n     * This means the location is in the head section.\n     */\n    public const POS_HEAD = 1;\n    /**\n     * The location of registered JavaScript code block or files.\n     * This means the location is at the beginning of the body section.\n     */\n    public const POS_BEGIN = 2;\n    /**\n     * The location of registered JavaScript code block or files.\n     * This means the location is at the end of the body section.\n     */\n    public const POS_END = 3;\n    /**\n     * The location of registered JavaScript code block.\n     * This means the JavaScript code block will be enclosed within `jQuery(document).ready()`.\n     */\n    public const POS_READY = 4;\n    /**\n     * The location of registered JavaScript code block.\n     * This means the JavaScript code block will be enclosed within `jQuery(window).load()`.\n     */\n    public const POS_LOAD = 5;\n    /**\n     * This is internally used as the placeholder for receiving the content registered for the head section.\n     */\n    public const PH_HEAD = '<![CDATA[YII-BLOCK-HEAD]]>';\n    /**\n     * This is internally used as the placeholder for receiving the content registered for the beginning of the body section.\n     */\n    public const PH_BODY_BEGIN = '<![CDATA[YII-BLOCK-BODY-BEGIN]]>';\n    /**\n     * This is internally used as the placeholder for receiving the content registered for the end of the body section.\n     */\n    public const PH_BODY_END = '<![CDATA[YII-BLOCK-BODY-END]]>';\n    /**\n     * @var AssetBundle[] list of the registered asset bundles. The keys are the bundle names, and the values\n     * are the registered [[AssetBundle]] objects.\n     * @see registerAssetBundle()\n     */\n    public $assetBundles = [];\n    /**\n     * @var string the page title\n     */\n    public $title;\n    /**\n     * @var array the registered meta tags.\n     * @see registerMetaTag()\n     */\n    public $metaTags = [];\n    /**\n     * @var array the registered link tags.\n     * @see registerLinkTag()\n     */\n    public $linkTags = [];\n    /**\n     * @var array the registered CSS code blocks.\n     * @see registerCss()\n     */\n    public $css = [];\n    /**\n     * @var array the registered CSS files.\n     * @see registerCssFile()\n     */\n    public $cssFiles = [];\n    /**\n     * @since 2.0.53\n     * @var array the style tag options.\n     */\n    public $styleOptions = [];\n    /**\n     * @var array the registered JS code blocks\n     * @see registerJs()\n     */\n    public $js = [];\n    /**\n     * @var array the registered JS files.\n     * @see registerJsFile()\n     */\n    public $jsFiles = [];\n    /**\n     * @since 2.0.50\n     * @var array the script tag options.\n     */\n    public $scriptOptions = [];\n\n    private $_assetManager;\n\n\n    /**\n     * Whether [[endPage()]] has been called and all files have been registered\n     * @var bool\n     * @since 2.0.44\n     */\n    protected $isPageEnded = false;\n\n    /**\n     * Marks the position of an HTML head section.\n     */\n    public function head()\n    {\n        echo self::PH_HEAD;\n    }\n\n    /**\n     * Marks the beginning of an HTML body section.\n     */\n    public function beginBody()\n    {\n        echo self::PH_BODY_BEGIN;\n        $this->trigger(self::EVENT_BEGIN_BODY);\n    }\n\n    /**\n     * Marks the ending of an HTML body section.\n     */\n    public function endBody()\n    {\n        $this->trigger(self::EVENT_END_BODY);\n        echo self::PH_BODY_END;\n\n        foreach (array_keys($this->assetBundles) as $bundle) {\n            $this->registerAssetFiles($bundle);\n        }\n    }\n\n    /**\n     * Marks the ending of an HTML page.\n     * @param bool $ajaxMode whether the view is rendering in AJAX mode.\n     * If true, the JS scripts registered at [[POS_READY]] and [[POS_LOAD]] positions\n     * will be rendered at the end of the view like normal scripts.\n     */\n    public function endPage($ajaxMode = false)\n    {\n        $this->trigger(self::EVENT_END_PAGE);\n\n        $this->isPageEnded = true;\n\n        $content = ob_get_clean();\n\n        echo strtr($content, [\n            self::PH_HEAD => $this->renderHeadHtml(),\n            self::PH_BODY_BEGIN => $this->renderBodyBeginHtml(),\n            self::PH_BODY_END => $this->renderBodyEndHtml($ajaxMode),\n        ]);\n\n        $this->clear();\n    }\n\n    /**\n     * Renders a view in response to an AJAX request.\n     *\n     * This method is similar to [[render()]] except that it will surround the view being rendered\n     * with the calls of [[beginPage()]], [[head()]], [[beginBody()]], [[endBody()]] and [[endPage()]].\n     * By doing so, the method is able to inject into the rendering result with JS/CSS scripts and files\n     * that are registered with the view.\n     *\n     * @param string $view the view name. Please refer to [[render()]] on how to specify this parameter.\n     * @param array $params the parameters (name-value pairs) that will be extracted and made available in the view file.\n     * @param object|null $context the context that the view should use for rendering the view. If null,\n     * existing [[context]] will be used.\n     * @return string the rendering result\n     * @see render()\n     */\n    public function renderAjax($view, $params = [], $context = null)\n    {\n        $viewFile = $this->findViewFile($view, $context);\n\n        ob_start();\n        ob_implicit_flush(false);\n\n        $this->beginPage();\n        $this->head();\n        $this->beginBody();\n        echo $this->renderFile($viewFile, $params, $context);\n        $this->endBody();\n        $this->endPage(true);\n\n        return ob_get_clean();\n    }\n\n    /**\n     * Registers the asset manager being used by this view object.\n     * @return \\yii\\web\\AssetManager the asset manager. Defaults to the \"assetManager\" application component.\n     */\n    public function getAssetManager()\n    {\n        return $this->_assetManager ?: Yii::$app->getAssetManager();\n    }\n\n    /**\n     * Sets the asset manager.\n     * @param \\yii\\web\\AssetManager $value the asset manager\n     */\n    public function setAssetManager($value)\n    {\n        $this->_assetManager = $value;\n    }\n\n    /**\n     * Clears up the registered meta tags, link tags, css/js scripts and files.\n     */\n    public function clear()\n    {\n        $this->metaTags = [];\n        $this->linkTags = [];\n        $this->css = [];\n        $this->cssFiles = [];\n        $this->js = [];\n        $this->jsFiles = [];\n        $this->assetBundles = [];\n    }\n\n    /**\n     * Registers all files provided by an asset bundle including depending bundles files.\n     * Removes a bundle from [[assetBundles]] once files are registered.\n     * @param string $name name of the bundle to register\n     */\n    protected function registerAssetFiles($name)\n    {\n        if (!isset($this->assetBundles[$name])) {\n            return;\n        }\n        $bundle = $this->assetBundles[$name];\n        if ($bundle) {\n            foreach ($bundle->depends as $dep) {\n                $this->registerAssetFiles($dep);\n            }\n            $bundle->registerAssetFiles($this);\n        }\n        unset($this->assetBundles[$name]);\n    }\n\n    /**\n     * Registers the named asset bundle.\n     * All dependent asset bundles will be registered.\n     * @param string $name the class name of the asset bundle (without the leading backslash)\n     * @param int|null $position if set, this forces a minimum position for javascript files.\n     * This will adjust depending assets javascript file position or fail if requirement can not be met.\n     * If this is null, asset bundles position settings will not be changed.\n     * See [[registerJsFile]] for more details on javascript position.\n     * @return AssetBundle the registered asset bundle instance\n     * @throws InvalidConfigException if the asset bundle does not exist or a circular dependency is detected\n     */\n    public function registerAssetBundle($name, $position = null)\n    {\n        if (!isset($this->assetBundles[$name])) {\n            $am = $this->getAssetManager();\n            $bundle = $am->getBundle($name);\n            $this->assetBundles[$name] = false;\n            // register dependencies\n            $pos = isset($bundle->jsOptions['position']) ? $bundle->jsOptions['position'] : null;\n            foreach ($bundle->depends as $dep) {\n                $this->registerAssetBundle($dep, $pos);\n            }\n            $this->assetBundles[$name] = $bundle;\n        } elseif ($this->assetBundles[$name] === false) {\n            throw new InvalidConfigException(\"A circular dependency is detected for bundle '$name'.\");\n        } else {\n            $bundle = $this->assetBundles[$name];\n        }\n\n        if ($position !== null) {\n            $pos = isset($bundle->jsOptions['position']) ? $bundle->jsOptions['position'] : null;\n            if ($pos === null) {\n                $bundle->jsOptions['position'] = $pos = $position;\n            } elseif ($pos > $position) {\n                throw new InvalidConfigException(\"An asset bundle that depends on '$name' has a higher javascript file position configured than '$name'.\");\n            }\n            // update position for all dependencies\n            foreach ($bundle->depends as $dep) {\n                $this->registerAssetBundle($dep, $pos);\n            }\n        }\n\n        return $bundle;\n    }\n\n    /**\n     * Registers a meta tag.\n     *\n     * For example, a description meta tag can be added like the following:\n     *\n     * ```\n     * $view->registerMetaTag([\n     *     'name' => 'description',\n     *     'content' => 'This website is about funny raccoons.'\n     * ]);\n     * ```\n     *\n     * will result in the meta tag `<meta name=\"description\" content=\"This website is about funny raccoons.\">`.\n     *\n     * @param array $options the HTML attributes for the meta tag.\n     * @param string|null $key the key that identifies the meta tag. If two meta tags are registered\n     * with the same key, the latter will overwrite the former. If this is null, the new meta tag\n     * will be appended to the existing ones.\n     */\n    public function registerMetaTag($options, $key = null)\n    {\n        if ($key === null) {\n            $this->metaTags[] = Html::tag('meta', '', $options);\n        } else {\n            $this->metaTags[$key] = Html::tag('meta', '', $options);\n        }\n    }\n\n    /**\n     * Registers CSRF meta tags.\n     * They are rendered dynamically to retrieve a new CSRF token for each request.\n     *\n     * ```\n     * $view->registerCsrfMetaTags();\n     * ```\n     *\n     * The above code will result in `<meta name=\"csrf-param\" content=\"[yii\\web\\Request::$csrfParam]\">`\n     * and `<meta name=\"csrf-token\" content=\"tTNpWKpdy-bx8ZmIq9R72...K1y8IP3XGkzZA==\">` added to the page.\n     *\n     * Note: Hidden CSRF input of ActiveForm will be automatically refreshed by calling `window.yii.refreshCsrfToken()`\n     * from `yii.js`.\n     *\n     * @since 2.0.13\n     */\n    public function registerCsrfMetaTags()\n    {\n        $this->metaTags['csrf_meta_tags'] = $this->renderDynamic('return yii\\helpers\\Html::csrfMetaTags();');\n    }\n\n    /**\n     * Registers a link tag.\n     *\n     * For example, a link tag for a custom [favicon](https://www.w3.org/2005/10/howto-favicon)\n     * can be added like the following:\n     *\n     * ```\n     * $view->registerLinkTag(['rel' => 'icon', 'type' => 'image/png', 'href' => '/myicon.png']);\n     * ```\n     *\n     * which will result in the following HTML: `<link rel=\"icon\" type=\"image/png\" href=\"/myicon.png\">`.\n     *\n     * **Note:** To register link tags for CSS stylesheets, use [[registerCssFile()]] instead, which\n     * has more options for this kind of link tag.\n     *\n     * @param array $options the HTML attributes for the link tag.\n     * @param string|null $key the key that identifies the link tag. If two link tags are registered\n     * with the same key, the latter will overwrite the former. If this is null, the new link tag\n     * will be appended to the existing ones.\n     */\n    public function registerLinkTag($options, $key = null)\n    {\n        if ($key === null) {\n            $this->linkTags[] = Html::tag('link', '', $options);\n        } else {\n            $this->linkTags[$key] = Html::tag('link', '', $options);\n        }\n    }\n\n    /**\n     * Registers a CSS code block.\n     * @param string $css the content of the CSS code block to be registered\n     * @param array $options the HTML attributes for the `<style>`-tag.\n     * @param string|null $key the key that identifies the CSS code block. If null, it will use\n     * $css as the key. If two CSS code blocks are registered with the same key, the latter\n     * will overwrite the former.\n     */\n    public function registerCss($css, $options = [], $key = null)\n    {\n        $key = $key ?: md5($css);\n        $this->css[$key] = Html::style($css, $options);\n    }\n\n    /**\n     * Registers a CSS file.\n     *\n     * This method should be used for simple registration of CSS files. If you want to use features of\n     * [[AssetManager]] like appending timestamps to the URL and file publishing options, use [[AssetBundle]]\n     * and [[registerAssetBundle()]] instead.\n     *\n     * @param string $url the CSS file to be registered.\n     * @param RegisterCssFileOptions $options the HTML attributes for the link tag. Please refer to [[Html::cssFile()]] for\n     * the supported options. The following options are specially handled and are not treated as HTML attributes:\n     *\n     * - `depends`: array, specifies the names of the asset bundles that this CSS file depends on.\n     * - `appendTimestamp`: bool whether to append a timestamp to the URL.\n     *\n     * @param string|null $key the key that identifies the CSS script file. If null, it will use\n     * $url as the key. If two CSS files are registered with the same key, the latter\n     * will overwrite the former.\n     * @throws InvalidConfigException\n     */\n    public function registerCssFile($url, $options = [], $key = null)\n    {\n        $this->registerFile('css', $url, $options, $key);\n    }\n\n    /**\n     * Registers a JS code block.\n     * @param string $js the JS code block to be registered\n     * @param int $position the position at which the JS script tag should be inserted\n     * in a page. The possible values are:\n     *\n     * - [[POS_HEAD]]: in the head section\n     * - [[POS_BEGIN]]: at the beginning of the body section\n     * - [[POS_END]]: at the end of the body section\n     * - [[POS_LOAD]]: enclosed within jQuery(window).load().\n     *   Note that by using this position, the method will automatically register the jQuery js file.\n     * - [[POS_READY]]: enclosed within jQuery(document).ready(). This is the default value.\n     *   Note that by using this position, the method will automatically register the jQuery js file.\n     *\n     * @param string|null $key the key that identifies the JS code block. If null, it will use\n     * $js as the key. If two JS code blocks are registered with the same key, the latter\n     * will overwrite the former.\n     */\n    public function registerJs($js, $position = self::POS_READY, $key = null)\n    {\n        $key = $key ?: md5($js);\n        $this->js[$position][$key] = $js;\n        if ($position === self::POS_READY || $position === self::POS_LOAD) {\n            JqueryAsset::register($this);\n        }\n    }\n\n    /**\n     * Registers a JS or CSS file.\n     *\n     * @param string $url the JS file to be registered.\n     * @param string $type type (js or css) of the file.\n     * @param array $options the HTML attributes for the script tag. The following options are specially handled\n     * and are not treated as HTML attributes:\n     *\n     * - `depends`: array, specifies the names of the asset bundles that this CSS file depends on.\n     * - `appendTimestamp`: bool whether to append a timestamp to the URL.\n     *\n     * @param string|null $key the key that identifies the JS script file. If null, it will use\n     * $url as the key. If two JS files are registered with the same key at the same position, the latter\n     * will overwrite the former. Note that position option takes precedence, thus files registered with the same key,\n     * but different position option will not override each other.\n     * @throws InvalidConfigException\n     */\n    private function registerFile($type, $url, $options = [], $key = null)\n    {\n        $url = Yii::getAlias($url);\n        $key = $key ?: $url;\n        $depends = ArrayHelper::remove($options, 'depends', []);\n        $originalOptions = $options;\n        $position = ArrayHelper::remove($options, 'position', self::POS_END);\n\n        try {\n            $assetManagerAppendTimestamp = $this->getAssetManager()->appendTimestamp;\n        } catch (InvalidConfigException $e) {\n            $depends = null; // the AssetManager is not available\n            $assetManagerAppendTimestamp = false;\n        }\n        $appendTimestamp = ArrayHelper::remove($options, 'appendTimestamp', $assetManagerAppendTimestamp);\n\n        if ($this->isPageEnded) {\n            Yii::warning('You\\'re trying to register a file after View::endPage() has been called.');\n        }\n\n        if (empty($depends)) {\n            // register directly without AssetManager\n            if ($appendTimestamp && Url::isRelative($url)) {\n                $prefix = Yii::getAlias('@web');\n                $prefixLength = strlen($prefix);\n                $trimmedUrl = ltrim((substr($url, 0, $prefixLength) === $prefix) ? substr($url, $prefixLength) : $url, '/');\n                $timestamp = @filemtime(Yii::getAlias('@webroot/' . $trimmedUrl, false));\n                if ($timestamp > 0) {\n                    $url = \"$url?v=$timestamp\";\n                }\n            }\n            if ($type === 'js') {\n                $this->jsFiles[$position][$key] = Html::jsFile($url, $options);\n            } else {\n                $this->cssFiles[$key] = Html::cssFile($url, $options);\n            }\n        } else {\n            $this->getAssetManager()->bundles[$key] = Yii::createObject([\n                'class' => AssetBundle::className(),\n                'baseUrl' => '',\n                'basePath' => '@webroot',\n                (string)$type => [ArrayHelper::merge([!Url::isRelative($url) ? $url : ltrim($url, '/')], $originalOptions)],\n                \"{$type}Options\" => $options,\n                'depends' => (array)$depends,\n            ]);\n            $this->registerAssetBundle($key);\n        }\n    }\n\n    /**\n     * Registers a JS file.\n     *\n     * This method should be used for simple registration of JS files. If you want to use features of\n     * [[AssetManager]] like appending timestamps to the URL and file publishing options, use [[AssetBundle]]\n     * and [[registerAssetBundle()]] instead.\n     *\n     * @param string $url the JS file to be registered.\n     * @param RegisterJsFileOptions $options the HTML attributes for the script tag. The following options are specially handled\n     * and are not treated as HTML attributes:\n     *\n     * - `depends`: array, specifies the names of the asset bundles that this JS file depends on.\n     * - `position`: specifies where the JS script tag should be inserted in a page. The possible values are:\n     *     * [[POS_HEAD]]: in the head section\n     *     * [[POS_BEGIN]]: at the beginning of the body section\n     *     * [[POS_END]]: at the end of the body section. This is the default value.\n     * - `appendTimestamp`: bool whether to append a timestamp to the URL.\n     *\n     * Please refer to [[Html::jsFile()]] for other supported options.\n     *\n     * @param string|null $key the key that identifies the JS script file. If null, it will use\n     * $url as the key. If two JS files are registered with the same key at the same position, the latter\n     * will overwrite the former. Note that position option takes precedence, thus files registered with the same key,\n     * but different position option will not override each other.\n     * @throws InvalidConfigException\n     */\n    public function registerJsFile($url, $options = [], $key = null)\n    {\n        $this->registerFile('js', $url, $options, $key);\n    }\n\n    /**\n     * Registers a JS code block defining a variable. The name of variable will be\n     * used as key, preventing duplicated variable names.\n     *\n     * @param string $name Name of the variable\n     * @param array|string $value Value of the variable\n     * @param int $position the position in a page at which the JavaScript variable should be inserted.\n     * The possible values are:\n     *\n     * - [[POS_HEAD]]: in the head section. This is the default value.\n     * - [[POS_BEGIN]]: at the beginning of the body section.\n     * - [[POS_END]]: at the end of the body section.\n     * - [[POS_LOAD]]: enclosed within jQuery(window).load().\n     *   Note that by using this position, the method will automatically register the jQuery js file.\n     * - [[POS_READY]]: enclosed within jQuery(document).ready().\n     *   Note that by using this position, the method will automatically register the jQuery js file.\n     *\n     * @since 2.0.14\n     */\n    public function registerJsVar($name, $value, $position = self::POS_HEAD)\n    {\n        $js = sprintf('var %s = %s;', $name, \\yii\\helpers\\Json::htmlEncode($value));\n        $this->registerJs($js, $position, $name);\n    }\n\n    /**\n     * Renders the content to be inserted in the head section.\n     * The content is rendered using the registered meta tags, link tags, CSS/JS code blocks and files.\n     * @return string the rendered content\n     */\n    protected function renderHeadHtml()\n    {\n        $lines = [];\n        if (!empty($this->metaTags)) {\n            $lines[] = implode(\"\\n\", $this->metaTags);\n        }\n\n        if (!empty($this->linkTags)) {\n            $lines[] = implode(\"\\n\", $this->linkTags);\n        }\n        if (!empty($this->cssFiles)) {\n            $lines[] = implode(\"\\n\", $this->cssFiles);\n        }\n        if (!empty($this->css)) {\n            $lines[] = implode(\"\\n\", $this->css);\n        }\n        if (!empty($this->jsFiles[self::POS_HEAD])) {\n            $lines[] = implode(\"\\n\", $this->jsFiles[self::POS_HEAD]);\n        }\n        if (!empty($this->js[self::POS_HEAD])) {\n            $lines[] = Html::script(implode(\"\\n\", $this->js[self::POS_HEAD]));\n        }\n\n        return empty($lines) ? '' : implode(\"\\n\", $lines);\n    }\n\n    /**\n     * Renders the content to be inserted at the beginning of the body section.\n     * The content is rendered using the registered JS code blocks and files.\n     * @return string the rendered content\n     */\n    protected function renderBodyBeginHtml()\n    {\n        $lines = [];\n        if (!empty($this->jsFiles[self::POS_BEGIN])) {\n            $lines[] = implode(\"\\n\", $this->jsFiles[self::POS_BEGIN]);\n        }\n        if (!empty($this->js[self::POS_BEGIN])) {\n            $lines[] = Html::script(implode(\"\\n\", $this->js[self::POS_BEGIN]));\n        }\n\n        return empty($lines) ? '' : implode(\"\\n\", $lines);\n    }\n\n    /**\n     * Renders the content to be inserted at the end of the body section.\n     * The content is rendered using the registered JS code blocks and files.\n     * @param bool $ajaxMode whether the view is rendering in AJAX mode.\n     * If true, the JS scripts registered at [[POS_READY]] and [[POS_LOAD]] positions\n     * will be rendered at the end of the view like normal scripts.\n     * @return string the rendered content\n     */\n    protected function renderBodyEndHtml($ajaxMode)\n    {\n        $lines = [];\n\n        if (!empty($this->jsFiles[self::POS_END])) {\n            $lines[] = implode(\"\\n\", $this->jsFiles[self::POS_END]);\n        }\n\n        if ($ajaxMode) {\n            $scripts = [];\n            if (!empty($this->js[self::POS_END])) {\n                $scripts[] = implode(\"\\n\", $this->js[self::POS_END]);\n            }\n            if (!empty($this->js[self::POS_READY])) {\n                $scripts[] = implode(\"\\n\", $this->js[self::POS_READY]);\n            }\n            if (!empty($this->js[self::POS_LOAD])) {\n                $scripts[] = implode(\"\\n\", $this->js[self::POS_LOAD]);\n            }\n            if (!empty($scripts)) {\n                $lines[] = Html::script(implode(\"\\n\", $scripts));\n            }\n        } else {\n            if (!empty($this->js[self::POS_END])) {\n                $lines[] = Html::script(implode(\"\\n\", $this->js[self::POS_END]));\n            }\n            if (!empty($this->js[self::POS_READY])) {\n                $js = \"jQuery(function ($) {\\n\" . implode(\"\\n\", $this->js[self::POS_READY]) . \"\\n});\";\n                $lines[] = Html::script($js);\n            }\n            if (!empty($this->js[self::POS_LOAD])) {\n                $js = \"jQuery(window).on('load', function () {\\n\" . implode(\"\\n\", $this->js[self::POS_LOAD]) . \"\\n});\";\n                $lines[] = Html::script($js);\n            }\n        }\n\n        return empty($lines) ? '' : implode(\"\\n\", $lines);\n    }\n}\n"
  },
  {
    "path": "tests/.hhconfig",
    "content": ""
  }
]